123456789101112131415161718192021222324252627282930313233343536373839404142434445464748495051525354555657585960616263646566676869707172737475767778798081828384858687888990919293949596979899100101102103104105106107108109110111112113114115116117118119120121122123124125126127128129130131132133134135136137138139140141142143144145146147148149150151152153154155156157158159160161162163164165166167168169170171172173174175176177178179180181182183184185186187188189190191192193194195196197198199200201202203204205206207208209210211212213214215216217218219220221222223224225226227228229230231232233234235236237238239240241242243244245246247248249250251252253254255256257258259260261262263264265266267268269270271272273274275276277278279280281282283284285286287288289290291292293294295296297298299300301302303304305306307308309310311312313314315316317318319320321322323324325326327328329330331332333334335336337338339340341342343344345346347348349350351352353354355356357358359360361362363364365366367368369370371372373374375376377378379380381382383384385386387388389390391392393394395396397398399400401402403404405406407408409410411412413414415416417418419420421422423424425426427428429430431432433434435436437438439440441442443444445446447448449450451452453454455456457458459460461462463464465466467468469470471472473474475476477478479480481482483484485486487488489490491492493494495496497498499500501502503504 |
- package netlink
- import (
- "encoding/binary"
- "log"
- "net"
- "syscall"
- "github.com/vishvananda/netlink/nl"
- "golang.org/x/sys/unix"
- )
- // IPSetEntry is used for adding, updating, retreiving and deleting entries
- type IPSetEntry struct {
- Comment string
- MAC net.HardwareAddr
- IP net.IP
- CIDR uint8
- Timeout *uint32
- Packets *uint64
- Bytes *uint64
- Protocol *uint8
- Port *uint16
- IP2 net.IP
- CIDR2 uint8
- IFace string
- Mark *uint32
- Replace bool // replace existing entry
- }
- // IPSetResult is the result of a dump request for a set
- type IPSetResult struct {
- Nfgenmsg *nl.Nfgenmsg
- Protocol uint8
- ProtocolMinVersion uint8
- Revision uint8
- Family uint8
- Flags uint8
- SetName string
- TypeName string
- Comment string
- MarkMask uint32
- IPFrom net.IP
- IPTo net.IP
- PortFrom uint16
- PortTo uint16
- HashSize uint32
- NumEntries uint32
- MaxElements uint32
- References uint32
- SizeInMemory uint32
- CadtFlags uint32
- Timeout *uint32
- LineNo uint32
- Entries []IPSetEntry
- }
- // IpsetCreateOptions is the options struct for creating a new ipset
- type IpsetCreateOptions struct {
- Replace bool // replace existing ipset
- Timeout *uint32
- Counters bool
- Comments bool
- Skbinfo bool
- Revision uint8
- IPFrom net.IP
- IPTo net.IP
- PortFrom uint16
- PortTo uint16
- }
- // IpsetProtocol returns the ipset protocol version from the kernel
- func IpsetProtocol() (uint8, uint8, error) {
- return pkgHandle.IpsetProtocol()
- }
- // IpsetCreate creates a new ipset
- func IpsetCreate(setname, typename string, options IpsetCreateOptions) error {
- return pkgHandle.IpsetCreate(setname, typename, options)
- }
- // IpsetDestroy destroys an existing ipset
- func IpsetDestroy(setname string) error {
- return pkgHandle.IpsetDestroy(setname)
- }
- // IpsetFlush flushes an existing ipset
- func IpsetFlush(setname string) error {
- return pkgHandle.IpsetFlush(setname)
- }
- // IpsetList dumps an specific ipset.
- func IpsetList(setname string) (*IPSetResult, error) {
- return pkgHandle.IpsetList(setname)
- }
- // IpsetListAll dumps all ipsets.
- func IpsetListAll() ([]IPSetResult, error) {
- return pkgHandle.IpsetListAll()
- }
- // IpsetAdd adds an entry to an existing ipset.
- func IpsetAdd(setname string, entry *IPSetEntry) error {
- return pkgHandle.IpsetAdd(setname, entry)
- }
- // IpsetDel deletes an entry from an existing ipset.
- func IpsetDel(setname string, entry *IPSetEntry) error {
- return pkgHandle.IpsetDel(setname, entry)
- }
- func (h *Handle) IpsetProtocol() (protocol uint8, minVersion uint8, err error) {
- req := h.newIpsetRequest(nl.IPSET_CMD_PROTOCOL)
- msgs, err := req.Execute(unix.NETLINK_NETFILTER, 0)
- if err != nil {
- return 0, 0, err
- }
- response := ipsetUnserialize(msgs)
- return response.Protocol, response.ProtocolMinVersion, nil
- }
- func (h *Handle) IpsetCreate(setname, typename string, options IpsetCreateOptions) error {
- req := h.newIpsetRequest(nl.IPSET_CMD_CREATE)
- if !options.Replace {
- req.Flags |= unix.NLM_F_EXCL
- }
- req.AddData(nl.NewRtAttr(nl.IPSET_ATTR_SETNAME, nl.ZeroTerminated(setname)))
- req.AddData(nl.NewRtAttr(nl.IPSET_ATTR_TYPENAME, nl.ZeroTerminated(typename)))
- revision := options.Revision
- if revision == 0 {
- revision = getIpsetDefaultWithTypeName(typename)
- }
- req.AddData(nl.NewRtAttr(nl.IPSET_ATTR_REVISION, nl.Uint8Attr(revision)))
- data := nl.NewRtAttr(nl.IPSET_ATTR_DATA|int(nl.NLA_F_NESTED), nil)
- var family uint8
- switch typename {
- case "hash:mac":
- case "bitmap:port":
- buf := make([]byte, 4)
- binary.BigEndian.PutUint16(buf, options.PortFrom)
- binary.BigEndian.PutUint16(buf[2:], options.PortTo)
- data.AddChild(nl.NewRtAttr(nl.IPSET_ATTR_PORT_FROM|int(nl.NLA_F_NET_BYTEORDER), buf[:2]))
- data.AddChild(nl.NewRtAttr(nl.IPSET_ATTR_PORT_TO|int(nl.NLA_F_NET_BYTEORDER), buf[2:]))
- default:
- family = unix.AF_INET
- }
- req.AddData(nl.NewRtAttr(nl.IPSET_ATTR_FAMILY, nl.Uint8Attr(family)))
- if timeout := options.Timeout; timeout != nil {
- data.AddChild(&nl.Uint32Attribute{Type: nl.IPSET_ATTR_TIMEOUT | nl.NLA_F_NET_BYTEORDER, Value: *timeout})
- }
- var cadtFlags uint32
- if options.Comments {
- cadtFlags |= nl.IPSET_FLAG_WITH_COMMENT
- }
- if options.Counters {
- cadtFlags |= nl.IPSET_FLAG_WITH_COUNTERS
- }
- if options.Skbinfo {
- cadtFlags |= nl.IPSET_FLAG_WITH_SKBINFO
- }
- if cadtFlags != 0 {
- data.AddChild(&nl.Uint32Attribute{Type: nl.IPSET_ATTR_CADT_FLAGS | nl.NLA_F_NET_BYTEORDER, Value: cadtFlags})
- }
- req.AddData(data)
- _, err := ipsetExecute(req)
- return err
- }
- func (h *Handle) IpsetDestroy(setname string) error {
- req := h.newIpsetRequest(nl.IPSET_CMD_DESTROY)
- req.AddData(nl.NewRtAttr(nl.IPSET_ATTR_SETNAME, nl.ZeroTerminated(setname)))
- _, err := ipsetExecute(req)
- return err
- }
- func (h *Handle) IpsetFlush(setname string) error {
- req := h.newIpsetRequest(nl.IPSET_CMD_FLUSH)
- req.AddData(nl.NewRtAttr(nl.IPSET_ATTR_SETNAME, nl.ZeroTerminated(setname)))
- _, err := ipsetExecute(req)
- return err
- }
- func (h *Handle) IpsetList(name string) (*IPSetResult, error) {
- req := h.newIpsetRequest(nl.IPSET_CMD_LIST)
- req.AddData(nl.NewRtAttr(nl.IPSET_ATTR_SETNAME, nl.ZeroTerminated(name)))
- msgs, err := ipsetExecute(req)
- if err != nil {
- return nil, err
- }
- result := ipsetUnserialize(msgs)
- return &result, nil
- }
- func (h *Handle) IpsetListAll() ([]IPSetResult, error) {
- req := h.newIpsetRequest(nl.IPSET_CMD_LIST)
- msgs, err := ipsetExecute(req)
- if err != nil {
- return nil, err
- }
- result := make([]IPSetResult, len(msgs))
- for i, msg := range msgs {
- result[i].unserialize(msg)
- }
- return result, nil
- }
- // IpsetAdd adds an entry to an existing ipset.
- func (h *Handle) IpsetAdd(setname string, entry *IPSetEntry) error {
- return h.ipsetAddDel(nl.IPSET_CMD_ADD, setname, entry)
- }
- // IpsetDel deletes an entry from an existing ipset.
- func (h *Handle) IpsetDel(setname string, entry *IPSetEntry) error {
- return h.ipsetAddDel(nl.IPSET_CMD_DEL, setname, entry)
- }
- func (h *Handle) ipsetAddDel(nlCmd int, setname string, entry *IPSetEntry) error {
- req := h.newIpsetRequest(nlCmd)
- req.AddData(nl.NewRtAttr(nl.IPSET_ATTR_SETNAME, nl.ZeroTerminated(setname)))
- if entry.Comment != "" {
- req.AddData(nl.NewRtAttr(nl.IPSET_ATTR_COMMENT, nl.ZeroTerminated(entry.Comment)))
- }
- data := nl.NewRtAttr(nl.IPSET_ATTR_DATA|int(nl.NLA_F_NESTED), nil)
- if !entry.Replace {
- req.Flags |= unix.NLM_F_EXCL
- }
- if entry.Timeout != nil {
- data.AddChild(&nl.Uint32Attribute{Type: nl.IPSET_ATTR_TIMEOUT | nl.NLA_F_NET_BYTEORDER, Value: *entry.Timeout})
- }
- if entry.IP != nil {
- nestedData := nl.NewRtAttr(nl.IPSET_ATTR_IP|int(nl.NLA_F_NET_BYTEORDER), entry.IP)
- data.AddChild(nl.NewRtAttr(nl.IPSET_ATTR_IP|int(nl.NLA_F_NESTED), nestedData.Serialize()))
- }
- if entry.MAC != nil {
- data.AddChild(nl.NewRtAttr(nl.IPSET_ATTR_ETHER, entry.MAC))
- }
- if entry.CIDR != 0 {
- data.AddChild(nl.NewRtAttr(nl.IPSET_ATTR_CIDR, nl.Uint8Attr(entry.CIDR)))
- }
- if entry.IP2 != nil {
- nestedData := nl.NewRtAttr(nl.IPSET_ATTR_IP|int(nl.NLA_F_NET_BYTEORDER), entry.IP2)
- data.AddChild(nl.NewRtAttr(nl.IPSET_ATTR_IP2|int(nl.NLA_F_NESTED), nestedData.Serialize()))
- }
- if entry.CIDR2 != 0 {
- data.AddChild(nl.NewRtAttr(nl.IPSET_ATTR_CIDR2, nl.Uint8Attr(entry.CIDR2)))
- }
- if entry.Port != nil {
- if entry.Protocol == nil {
- // use tcp protocol as default
- val := uint8(unix.IPPROTO_TCP)
- entry.Protocol = &val
- }
- data.AddChild(nl.NewRtAttr(nl.IPSET_ATTR_PROTO, nl.Uint8Attr(*entry.Protocol)))
- buf := make([]byte, 2)
- binary.BigEndian.PutUint16(buf, *entry.Port)
- data.AddChild(nl.NewRtAttr(int(nl.IPSET_ATTR_PORT|nl.NLA_F_NET_BYTEORDER), buf))
- }
- if entry.IFace != "" {
- data.AddChild(nl.NewRtAttr(nl.IPSET_ATTR_IFACE, nl.ZeroTerminated(entry.IFace)))
- }
- if entry.Mark != nil {
- data.AddChild(&nl.Uint32Attribute{Type: nl.IPSET_ATTR_MARK | nl.NLA_F_NET_BYTEORDER, Value: *entry.Mark})
- }
- data.AddChild(&nl.Uint32Attribute{Type: nl.IPSET_ATTR_LINENO | nl.NLA_F_NET_BYTEORDER, Value: 0})
- req.AddData(data)
- _, err := ipsetExecute(req)
- return err
- }
- func (h *Handle) newIpsetRequest(cmd int) *nl.NetlinkRequest {
- req := h.newNetlinkRequest(cmd|(unix.NFNL_SUBSYS_IPSET<<8), nl.GetIpsetFlags(cmd))
- // Add the netfilter header
- msg := &nl.Nfgenmsg{
- NfgenFamily: uint8(unix.AF_NETLINK),
- Version: nl.NFNETLINK_V0,
- ResId: 0,
- }
- req.AddData(msg)
- req.AddData(nl.NewRtAttr(nl.IPSET_ATTR_PROTOCOL, nl.Uint8Attr(nl.IPSET_PROTOCOL)))
- return req
- }
- func getIpsetDefaultWithTypeName(typename string) uint8 {
- switch typename {
- case "hash:ip,port",
- "hash:ip,port,ip",
- "hash:ip,port,net",
- "hash:net,port":
- return 1
- }
- return 0
- }
- func ipsetExecute(req *nl.NetlinkRequest) (msgs [][]byte, err error) {
- msgs, err = req.Execute(unix.NETLINK_NETFILTER, 0)
- if err != nil {
- if errno := int(err.(syscall.Errno)); errno >= nl.IPSET_ERR_PRIVATE {
- err = nl.IPSetError(uintptr(errno))
- }
- }
- return
- }
- func ipsetUnserialize(msgs [][]byte) (result IPSetResult) {
- for _, msg := range msgs {
- result.unserialize(msg)
- }
- return result
- }
- func (result *IPSetResult) unserialize(msg []byte) {
- result.Nfgenmsg = nl.DeserializeNfgenmsg(msg)
- for attr := range nl.ParseAttributes(msg[4:]) {
- switch attr.Type {
- case nl.IPSET_ATTR_PROTOCOL:
- result.Protocol = attr.Value[0]
- case nl.IPSET_ATTR_SETNAME:
- result.SetName = nl.BytesToString(attr.Value)
- case nl.IPSET_ATTR_COMMENT:
- result.Comment = nl.BytesToString(attr.Value)
- case nl.IPSET_ATTR_TYPENAME:
- result.TypeName = nl.BytesToString(attr.Value)
- case nl.IPSET_ATTR_REVISION:
- result.Revision = attr.Value[0]
- case nl.IPSET_ATTR_FAMILY:
- result.Family = attr.Value[0]
- case nl.IPSET_ATTR_FLAGS:
- result.Flags = attr.Value[0]
- case nl.IPSET_ATTR_DATA | nl.NLA_F_NESTED:
- result.parseAttrData(attr.Value)
- case nl.IPSET_ATTR_ADT | nl.NLA_F_NESTED:
- result.parseAttrADT(attr.Value)
- case nl.IPSET_ATTR_PROTOCOL_MIN:
- result.ProtocolMinVersion = attr.Value[0]
- case nl.IPSET_ATTR_MARKMASK:
- result.MarkMask = attr.Uint32()
- default:
- log.Printf("unknown ipset attribute from kernel: %+v %v", attr, attr.Type&nl.NLA_TYPE_MASK)
- }
- }
- }
- func (result *IPSetResult) parseAttrData(data []byte) {
- for attr := range nl.ParseAttributes(data) {
- switch attr.Type {
- case nl.IPSET_ATTR_HASHSIZE | nl.NLA_F_NET_BYTEORDER:
- result.HashSize = attr.Uint32()
- case nl.IPSET_ATTR_MAXELEM | nl.NLA_F_NET_BYTEORDER:
- result.MaxElements = attr.Uint32()
- case nl.IPSET_ATTR_TIMEOUT | nl.NLA_F_NET_BYTEORDER:
- val := attr.Uint32()
- result.Timeout = &val
- case nl.IPSET_ATTR_ELEMENTS | nl.NLA_F_NET_BYTEORDER:
- result.NumEntries = attr.Uint32()
- case nl.IPSET_ATTR_REFERENCES | nl.NLA_F_NET_BYTEORDER:
- result.References = attr.Uint32()
- case nl.IPSET_ATTR_MEMSIZE | nl.NLA_F_NET_BYTEORDER:
- result.SizeInMemory = attr.Uint32()
- case nl.IPSET_ATTR_CADT_FLAGS | nl.NLA_F_NET_BYTEORDER:
- result.CadtFlags = attr.Uint32()
- case nl.IPSET_ATTR_IP | nl.NLA_F_NESTED:
- for nested := range nl.ParseAttributes(attr.Value) {
- switch nested.Type {
- case nl.IPSET_ATTR_IP | nl.NLA_F_NET_BYTEORDER:
- result.Entries = append(result.Entries, IPSetEntry{IP: nested.Value})
- case nl.IPSET_ATTR_IP:
- result.IPFrom = nested.Value
- default:
- log.Printf("unknown nested ipset data attribute from kernel: %+v %v", nested, nested.Type&nl.NLA_TYPE_MASK)
- }
- }
- case nl.IPSET_ATTR_IP_TO | nl.NLA_F_NESTED:
- for nested := range nl.ParseAttributes(attr.Value) {
- switch nested.Type {
- case nl.IPSET_ATTR_IP:
- result.IPTo = nested.Value
- default:
- log.Printf("unknown nested ipset data attribute from kernel: %+v %v", nested, nested.Type&nl.NLA_TYPE_MASK)
- }
- }
- case nl.IPSET_ATTR_PORT_FROM | nl.NLA_F_NET_BYTEORDER:
- result.PortFrom = networkOrder.Uint16(attr.Value)
- case nl.IPSET_ATTR_PORT_TO | nl.NLA_F_NET_BYTEORDER:
- result.PortTo = networkOrder.Uint16(attr.Value)
- case nl.IPSET_ATTR_CADT_LINENO | nl.NLA_F_NET_BYTEORDER:
- result.LineNo = attr.Uint32()
- case nl.IPSET_ATTR_COMMENT:
- result.Comment = nl.BytesToString(attr.Value)
- case nl.IPSET_ATTR_MARKMASK:
- result.MarkMask = attr.Uint32()
- default:
- log.Printf("unknown ipset data attribute from kernel: %+v %v", attr, attr.Type&nl.NLA_TYPE_MASK)
- }
- }
- }
- func (result *IPSetResult) parseAttrADT(data []byte) {
- for attr := range nl.ParseAttributes(data) {
- switch attr.Type {
- case nl.IPSET_ATTR_DATA | nl.NLA_F_NESTED:
- result.Entries = append(result.Entries, parseIPSetEntry(attr.Value))
- default:
- log.Printf("unknown ADT attribute from kernel: %+v %v", attr, attr.Type&nl.NLA_TYPE_MASK)
- }
- }
- }
- func parseIPSetEntry(data []byte) (entry IPSetEntry) {
- for attr := range nl.ParseAttributes(data) {
- switch attr.Type {
- case nl.IPSET_ATTR_TIMEOUT | nl.NLA_F_NET_BYTEORDER:
- val := attr.Uint32()
- entry.Timeout = &val
- case nl.IPSET_ATTR_BYTES | nl.NLA_F_NET_BYTEORDER:
- val := attr.Uint64()
- entry.Bytes = &val
- case nl.IPSET_ATTR_PACKETS | nl.NLA_F_NET_BYTEORDER:
- val := attr.Uint64()
- entry.Packets = &val
- case nl.IPSET_ATTR_ETHER:
- entry.MAC = net.HardwareAddr(attr.Value)
- case nl.IPSET_ATTR_IP:
- entry.IP = net.IP(attr.Value)
- case nl.IPSET_ATTR_COMMENT:
- entry.Comment = nl.BytesToString(attr.Value)
- case nl.IPSET_ATTR_IP | nl.NLA_F_NESTED:
- for attr := range nl.ParseAttributes(attr.Value) {
- switch attr.Type {
- case nl.IPSET_ATTR_IP:
- entry.IP = net.IP(attr.Value)
- default:
- log.Printf("unknown nested ADT attribute from kernel: %+v", attr)
- }
- }
- case nl.IPSET_ATTR_IP2 | nl.NLA_F_NESTED:
- for attr := range nl.ParseAttributes(attr.Value) {
- switch attr.Type {
- case nl.IPSET_ATTR_IP:
- entry.IP2 = net.IP(attr.Value)
- default:
- log.Printf("unknown nested ADT attribute from kernel: %+v", attr)
- }
- }
- case nl.IPSET_ATTR_CIDR:
- entry.CIDR = attr.Value[0]
- case nl.IPSET_ATTR_CIDR2:
- entry.CIDR2 = attr.Value[0]
- case nl.IPSET_ATTR_PORT | nl.NLA_F_NET_BYTEORDER:
- val := networkOrder.Uint16(attr.Value)
- entry.Port = &val
- case nl.IPSET_ATTR_PROTO:
- val := attr.Value[0]
- entry.Protocol = &val
- case nl.IPSET_ATTR_IFACE:
- entry.IFace = nl.BytesToString(attr.Value)
- case nl.IPSET_ATTR_MARK | nl.NLA_F_NET_BYTEORDER:
- val := attr.Uint32()
- entry.Mark = &val
- default:
- log.Printf("unknown ADT attribute from kernel: %+v", attr)
- }
- }
- return
- }
|