github.com/vishvananda/netlink@v1.3.1/ipset_linux.go (about)

     1  package netlink
     2  
     3  import (
     4  	"encoding/binary"
     5  	"log"
     6  	"net"
     7  	"syscall"
     8  
     9  	"github.com/vishvananda/netlink/nl"
    10  	"golang.org/x/sys/unix"
    11  )
    12  
    13  // IPSetEntry is used for adding, updating, retreiving and deleting entries
    14  type IPSetEntry struct {
    15  	Comment  string
    16  	MAC      net.HardwareAddr
    17  	IP       net.IP
    18  	CIDR     uint8
    19  	Timeout  *uint32
    20  	Packets  *uint64
    21  	Bytes    *uint64
    22  	Protocol *uint8
    23  	Port     *uint16
    24  	IP2      net.IP
    25  	CIDR2    uint8
    26  	IFace    string
    27  	Mark     *uint32
    28  
    29  	Replace bool // replace existing entry
    30  }
    31  
    32  // IPSetResult is the result of a dump request for a set
    33  type IPSetResult struct {
    34  	Nfgenmsg           *nl.Nfgenmsg
    35  	Protocol           uint8
    36  	ProtocolMinVersion uint8
    37  	Revision           uint8
    38  	Family             uint8
    39  	Flags              uint8
    40  	SetName            string
    41  	TypeName           string
    42  	Comment            string
    43  	MarkMask           uint32
    44  
    45  	IPFrom   net.IP
    46  	IPTo     net.IP
    47  	PortFrom uint16
    48  	PortTo   uint16
    49  
    50  	HashSize     uint32
    51  	NumEntries   uint32
    52  	MaxElements  uint32
    53  	References   uint32
    54  	SizeInMemory uint32
    55  	CadtFlags    uint32
    56  	Timeout      *uint32
    57  	LineNo       uint32
    58  
    59  	Entries []IPSetEntry
    60  }
    61  
    62  // IpsetCreateOptions is the options struct for creating a new ipset
    63  type IpsetCreateOptions struct {
    64  	Replace  bool // replace existing ipset
    65  	Timeout  *uint32
    66  	Counters bool
    67  	Comments bool
    68  	Skbinfo  bool
    69  
    70  	Family      uint8
    71  	Revision    uint8
    72  	IPFrom      net.IP
    73  	IPTo        net.IP
    74  	PortFrom    uint16
    75  	PortTo      uint16
    76  	MaxElements uint32
    77  }
    78  
    79  // IpsetProtocol returns the ipset protocol version from the kernel
    80  func IpsetProtocol() (uint8, uint8, error) {
    81  	return pkgHandle.IpsetProtocol()
    82  }
    83  
    84  // IpsetCreate creates a new ipset
    85  func IpsetCreate(setname, typename string, options IpsetCreateOptions) error {
    86  	return pkgHandle.IpsetCreate(setname, typename, options)
    87  }
    88  
    89  // IpsetDestroy destroys an existing ipset
    90  func IpsetDestroy(setname string) error {
    91  	return pkgHandle.IpsetDestroy(setname)
    92  }
    93  
    94  // IpsetFlush flushes an existing ipset
    95  func IpsetFlush(setname string) error {
    96  	return pkgHandle.IpsetFlush(setname)
    97  }
    98  
    99  // IpsetSwap swaps two ipsets.
   100  func IpsetSwap(setname, othersetname string) error {
   101  	return pkgHandle.IpsetSwap(setname, othersetname)
   102  }
   103  
   104  // IpsetList dumps an specific ipset.
   105  func IpsetList(setname string) (*IPSetResult, error) {
   106  	return pkgHandle.IpsetList(setname)
   107  }
   108  
   109  // IpsetListAll dumps all ipsets.
   110  func IpsetListAll() ([]IPSetResult, error) {
   111  	return pkgHandle.IpsetListAll()
   112  }
   113  
   114  // IpsetAdd adds an entry to an existing ipset.
   115  func IpsetAdd(setname string, entry *IPSetEntry) error {
   116  	return pkgHandle.IpsetAdd(setname, entry)
   117  }
   118  
   119  // IpsetDel deletes an entry from an existing ipset.
   120  func IpsetDel(setname string, entry *IPSetEntry) error {
   121  	return pkgHandle.IpsetDel(setname, entry)
   122  }
   123  
   124  // IpsetTest tests whether an entry is in a set or not.
   125  func IpsetTest(setname string, entry *IPSetEntry) (bool, error) {
   126  	return pkgHandle.IpsetTest(setname, entry)
   127  }
   128  
   129  func (h *Handle) IpsetProtocol() (protocol uint8, minVersion uint8, err error) {
   130  	req := h.newIpsetRequest(nl.IPSET_CMD_PROTOCOL)
   131  	msgs, err := req.Execute(unix.NETLINK_NETFILTER, 0)
   132  
   133  	if err != nil {
   134  		return 0, 0, err
   135  	}
   136  	response := ipsetUnserialize(msgs)
   137  	return response.Protocol, response.ProtocolMinVersion, nil
   138  }
   139  
   140  func (h *Handle) IpsetCreate(setname, typename string, options IpsetCreateOptions) error {
   141  	req := h.newIpsetRequest(nl.IPSET_CMD_CREATE)
   142  
   143  	if !options.Replace {
   144  		req.Flags |= unix.NLM_F_EXCL
   145  	}
   146  
   147  	req.AddData(nl.NewRtAttr(nl.IPSET_ATTR_SETNAME, nl.ZeroTerminated(setname)))
   148  	req.AddData(nl.NewRtAttr(nl.IPSET_ATTR_TYPENAME, nl.ZeroTerminated(typename)))
   149  
   150  	cadtFlags := optionsToBitflag(options)
   151  
   152  	revision := options.Revision
   153  	if revision == 0 {
   154  		revision = getIpsetDefaultRevision(typename, cadtFlags)
   155  	}
   156  	req.AddData(nl.NewRtAttr(nl.IPSET_ATTR_REVISION, nl.Uint8Attr(revision)))
   157  
   158  	data := nl.NewRtAttr(nl.IPSET_ATTR_DATA|int(nl.NLA_F_NESTED), nil)
   159  
   160  	var family uint8
   161  	switch typename {
   162  	case "hash:mac":
   163  	case "bitmap:port":
   164  		buf := make([]byte, 4)
   165  		binary.BigEndian.PutUint16(buf, options.PortFrom)
   166  		binary.BigEndian.PutUint16(buf[2:], options.PortTo)
   167  		data.AddChild(nl.NewRtAttr(nl.IPSET_ATTR_PORT_FROM|int(nl.NLA_F_NET_BYTEORDER), buf[:2]))
   168  		data.AddChild(nl.NewRtAttr(nl.IPSET_ATTR_PORT_TO|int(nl.NLA_F_NET_BYTEORDER), buf[2:]))
   169  	default:
   170  		family = options.Family
   171  		if family == 0 {
   172  			family = unix.AF_INET
   173  		}
   174  	}
   175  
   176  	req.AddData(nl.NewRtAttr(nl.IPSET_ATTR_FAMILY, nl.Uint8Attr(family)))
   177  
   178  	if options.MaxElements != 0 {
   179  		data.AddChild(&nl.Uint32Attribute{Type: nl.IPSET_ATTR_MAXELEM | nl.NLA_F_NET_BYTEORDER, Value: options.MaxElements})
   180  	}
   181  
   182  	if timeout := options.Timeout; timeout != nil {
   183  		data.AddChild(&nl.Uint32Attribute{Type: nl.IPSET_ATTR_TIMEOUT | nl.NLA_F_NET_BYTEORDER, Value: *timeout})
   184  	}
   185  
   186  	if cadtFlags != 0 {
   187  		data.AddChild(&nl.Uint32Attribute{Type: nl.IPSET_ATTR_CADT_FLAGS | nl.NLA_F_NET_BYTEORDER, Value: cadtFlags})
   188  	}
   189  
   190  	req.AddData(data)
   191  	_, err := ipsetExecute(req)
   192  	return err
   193  }
   194  
   195  func (h *Handle) IpsetDestroy(setname string) error {
   196  	req := h.newIpsetRequest(nl.IPSET_CMD_DESTROY)
   197  	req.AddData(nl.NewRtAttr(nl.IPSET_ATTR_SETNAME, nl.ZeroTerminated(setname)))
   198  	_, err := ipsetExecute(req)
   199  	return err
   200  }
   201  
   202  func (h *Handle) IpsetFlush(setname string) error {
   203  	req := h.newIpsetRequest(nl.IPSET_CMD_FLUSH)
   204  	req.AddData(nl.NewRtAttr(nl.IPSET_ATTR_SETNAME, nl.ZeroTerminated(setname)))
   205  	_, err := ipsetExecute(req)
   206  	return err
   207  }
   208  
   209  func (h *Handle) IpsetSwap(setname, othersetname string) error {
   210  	req := h.newIpsetRequest(nl.IPSET_CMD_SWAP)
   211  	req.AddData(nl.NewRtAttr(nl.IPSET_ATTR_SETNAME, nl.ZeroTerminated(setname)))
   212  	req.AddData(nl.NewRtAttr(nl.IPSET_ATTR_TYPENAME, nl.ZeroTerminated(othersetname)))
   213  	_, err := ipsetExecute(req)
   214  	return err
   215  }
   216  
   217  func (h *Handle) IpsetList(name string) (*IPSetResult, error) {
   218  	req := h.newIpsetRequest(nl.IPSET_CMD_LIST)
   219  	req.AddData(nl.NewRtAttr(nl.IPSET_ATTR_SETNAME, nl.ZeroTerminated(name)))
   220  
   221  	msgs, err := ipsetExecute(req)
   222  	if err != nil {
   223  		return nil, err
   224  	}
   225  
   226  	result := ipsetUnserialize(msgs)
   227  	return &result, nil
   228  }
   229  
   230  func (h *Handle) IpsetListAll() ([]IPSetResult, error) {
   231  	req := h.newIpsetRequest(nl.IPSET_CMD_LIST)
   232  
   233  	msgs, err := ipsetExecute(req)
   234  	if err != nil {
   235  		return nil, err
   236  	}
   237  
   238  	result := make([]IPSetResult, len(msgs))
   239  	for i, msg := range msgs {
   240  		result[i].unserialize(msg)
   241  	}
   242  
   243  	return result, nil
   244  }
   245  
   246  // IpsetAdd adds an entry to an existing ipset.
   247  func (h *Handle) IpsetAdd(setname string, entry *IPSetEntry) error {
   248  	return h.ipsetAddDel(nl.IPSET_CMD_ADD, setname, entry)
   249  }
   250  
   251  // IpsetDel deletes an entry from an existing ipset.
   252  func (h *Handle) IpsetDel(setname string, entry *IPSetEntry) error {
   253  	return h.ipsetAddDel(nl.IPSET_CMD_DEL, setname, entry)
   254  }
   255  
   256  func encodeIP(ip net.IP) (*nl.RtAttr, error) {
   257  	typ := int(nl.NLA_F_NET_BYTEORDER)
   258  	if ip4 := ip.To4(); ip4 != nil {
   259  		typ |= nl.IPSET_ATTR_IPADDR_IPV4
   260  		ip = ip4
   261  	} else {
   262  		typ |= nl.IPSET_ATTR_IPADDR_IPV6
   263  	}
   264  
   265  	return nl.NewRtAttr(typ, ip), nil
   266  }
   267  
   268  func buildEntryData(entry *IPSetEntry) (*nl.RtAttr, error) {
   269  	data := nl.NewRtAttr(nl.IPSET_ATTR_DATA|int(nl.NLA_F_NESTED), nil)
   270  
   271  	if entry.Comment != "" {
   272  		data.AddChild(nl.NewRtAttr(nl.IPSET_ATTR_COMMENT, nl.ZeroTerminated(entry.Comment)))
   273  	}
   274  
   275  	if entry.Timeout != nil {
   276  		data.AddChild(&nl.Uint32Attribute{Type: nl.IPSET_ATTR_TIMEOUT | nl.NLA_F_NET_BYTEORDER, Value: *entry.Timeout})
   277  	}
   278  
   279  	if entry.IP != nil {
   280  		nestedData, err := encodeIP(entry.IP)
   281  		if err != nil {
   282  			return nil, err
   283  		}
   284  		data.AddChild(nl.NewRtAttr(nl.IPSET_ATTR_IP|int(nl.NLA_F_NESTED), nestedData.Serialize()))
   285  	}
   286  
   287  	if entry.MAC != nil {
   288  		data.AddChild(nl.NewRtAttr(nl.IPSET_ATTR_ETHER, entry.MAC))
   289  	}
   290  
   291  	if entry.CIDR != 0 {
   292  		data.AddChild(nl.NewRtAttr(nl.IPSET_ATTR_CIDR, nl.Uint8Attr(entry.CIDR)))
   293  	}
   294  
   295  	if entry.IP2 != nil {
   296  		nestedData, err := encodeIP(entry.IP2)
   297  		if err != nil {
   298  			return nil, err
   299  		}
   300  		data.AddChild(nl.NewRtAttr(nl.IPSET_ATTR_IP2|int(nl.NLA_F_NESTED), nestedData.Serialize()))
   301  	}
   302  
   303  	if entry.CIDR2 != 0 {
   304  		data.AddChild(nl.NewRtAttr(nl.IPSET_ATTR_CIDR2, nl.Uint8Attr(entry.CIDR2)))
   305  	}
   306  
   307  	if entry.Port != nil {
   308  		if entry.Protocol == nil {
   309  			// use tcp protocol as default
   310  			val := uint8(unix.IPPROTO_TCP)
   311  			entry.Protocol = &val
   312  		}
   313  		data.AddChild(nl.NewRtAttr(nl.IPSET_ATTR_PROTO, nl.Uint8Attr(*entry.Protocol)))
   314  		buf := make([]byte, 2)
   315  		binary.BigEndian.PutUint16(buf, *entry.Port)
   316  		data.AddChild(nl.NewRtAttr(int(nl.IPSET_ATTR_PORT|nl.NLA_F_NET_BYTEORDER), buf))
   317  	}
   318  
   319  	if entry.IFace != "" {
   320  		data.AddChild(nl.NewRtAttr(nl.IPSET_ATTR_IFACE, nl.ZeroTerminated(entry.IFace)))
   321  	}
   322  
   323  	if entry.Mark != nil {
   324  		data.AddChild(&nl.Uint32Attribute{Type: nl.IPSET_ATTR_MARK | nl.NLA_F_NET_BYTEORDER, Value: *entry.Mark})
   325  	}
   326  	return data, nil
   327  }
   328  
   329  func (h *Handle) ipsetAddDel(nlCmd int, setname string, entry *IPSetEntry) error {
   330  	req := h.newIpsetRequest(nlCmd)
   331  	req.AddData(nl.NewRtAttr(nl.IPSET_ATTR_SETNAME, nl.ZeroTerminated(setname)))
   332  
   333  	if !entry.Replace {
   334  		req.Flags |= unix.NLM_F_EXCL
   335  	}
   336  
   337  	data, err := buildEntryData(entry)
   338  	if err != nil {
   339  		return err
   340  	}
   341  	data.AddChild(&nl.Uint32Attribute{Type: nl.IPSET_ATTR_LINENO | nl.NLA_F_NET_BYTEORDER, Value: 0})
   342  	req.AddData(data)
   343  
   344  	_, err = ipsetExecute(req)
   345  	return err
   346  }
   347  
   348  func (h *Handle) IpsetTest(setname string, entry *IPSetEntry) (bool, error) {
   349  	req := h.newIpsetRequest(nl.IPSET_CMD_TEST)
   350  	req.AddData(nl.NewRtAttr(nl.IPSET_ATTR_SETNAME, nl.ZeroTerminated(setname)))
   351  
   352  	if !entry.Replace {
   353  		req.Flags |= unix.NLM_F_EXCL
   354  	}
   355  
   356  	data, err := buildEntryData(entry)
   357  	if err != nil {
   358  		return false, err
   359  	}
   360  	req.AddData(data)
   361  
   362  	_, err = ipsetExecute(req)
   363  	if err != nil {
   364  		if err == nl.IPSetError(nl.IPSET_ERR_EXIST) {
   365  			// not exist
   366  			return false, nil
   367  		}
   368  		return false, err
   369  	}
   370  	return true, nil
   371  }
   372  
   373  func (h *Handle) newIpsetRequest(cmd int) *nl.NetlinkRequest {
   374  	req := h.newNetlinkRequest(cmd|(unix.NFNL_SUBSYS_IPSET<<8), nl.GetIpsetFlags(cmd))
   375  
   376  	// Add the netfilter header
   377  	msg := &nl.Nfgenmsg{
   378  		NfgenFamily: uint8(unix.AF_NETLINK),
   379  		Version:     nl.NFNETLINK_V0,
   380  		ResId:       0,
   381  	}
   382  	req.AddData(msg)
   383  	req.AddData(nl.NewRtAttr(nl.IPSET_ATTR_PROTOCOL, nl.Uint8Attr(nl.IPSET_PROTOCOL)))
   384  
   385  	return req
   386  }
   387  
   388  // NOTE: This can't just take typename into account, it also has to take desired
   389  // feature support into account, on a per-set-type basis, to return the correct revision, see e.g.
   390  // https://github.com/Olipro/ipset/blob/9f145b49100104d6570fe5c31a5236816ebb4f8f/kernel/net/netfilter/ipset/ip_set_hash_ipport.c#L30
   391  //
   392  // This means that whenever a new "type" of ipset is added, returning the "correct" default revision
   393  // requires adding a new case here for that type, and consulting the ipset C code to figure out the correct
   394  // combination of type name, feature bit flags, and revision ranges.
   395  //
   396  // Care should be taken as some types share the same revision ranges for the same features, and others do not.
   397  // When in doubt, mimic the C code.
   398  func getIpsetDefaultRevision(typename string, featureFlags uint32) uint8 {
   399  	switch typename {
   400  	case "hash:ip,port",
   401  		"hash:ip,port,ip":
   402  		// Taken from
   403  		// - ipset/kernel/net/netfilter/ipset/ip_set_hash_ipport.c
   404  		// - ipset/kernel/net/netfilter/ipset/ip_set_hash_ipportip.c
   405  		if (featureFlags & nl.IPSET_FLAG_WITH_SKBINFO) != 0 {
   406  			return 5
   407  		}
   408  
   409  		if (featureFlags & nl.IPSET_FLAG_WITH_FORCEADD) != 0 {
   410  			return 4
   411  		}
   412  
   413  		if (featureFlags & nl.IPSET_FLAG_WITH_COMMENT) != 0 {
   414  			return 3
   415  		}
   416  
   417  		if (featureFlags & nl.IPSET_FLAG_WITH_COUNTERS) != 0 {
   418  			return 2
   419  		}
   420  
   421  		// the min revision this library supports for this type
   422  		return 1
   423  
   424  	case "hash:ip,port,net",
   425  		"hash:net,port":
   426  		// Taken from
   427  		// - ipset/kernel/net/netfilter/ipset/ip_set_hash_ipportnet.c
   428  		// - ipset/kernel/net/netfilter/ipset/ip_set_hash_netport.c
   429  		if (featureFlags & nl.IPSET_FLAG_WITH_SKBINFO) != 0 {
   430  			return 7
   431  		}
   432  
   433  		if (featureFlags & nl.IPSET_FLAG_WITH_FORCEADD) != 0 {
   434  			return 6
   435  		}
   436  
   437  		if (featureFlags & nl.IPSET_FLAG_WITH_COMMENT) != 0 {
   438  			return 5
   439  		}
   440  
   441  		if (featureFlags & nl.IPSET_FLAG_WITH_COUNTERS) != 0 {
   442  			return 4
   443  		}
   444  
   445  		if (featureFlags & nl.IPSET_FLAG_NOMATCH) != 0 {
   446  			return 3
   447  		}
   448  		// the min revision this library supports for this type
   449  		return 2
   450  
   451  	case "hash:ip":
   452  		// Taken from
   453  		// - ipset/kernel/net/netfilter/ipset/ip_set_hash_ip.c
   454  		if (featureFlags & nl.IPSET_FLAG_WITH_SKBINFO) != 0 {
   455  			return 4
   456  		}
   457  
   458  		if (featureFlags & nl.IPSET_FLAG_WITH_FORCEADD) != 0 {
   459  			return 3
   460  		}
   461  
   462  		if (featureFlags & nl.IPSET_FLAG_WITH_COMMENT) != 0 {
   463  			return 2
   464  		}
   465  
   466  		// the min revision this library supports for this type
   467  		return 1
   468  	}
   469  
   470  	// can't map the correct revision for this type.
   471  	return 0
   472  }
   473  
   474  func ipsetExecute(req *nl.NetlinkRequest) (msgs [][]byte, err error) {
   475  	msgs, err = req.Execute(unix.NETLINK_NETFILTER, 0)
   476  
   477  	if err != nil {
   478  		if errno := int(err.(syscall.Errno)); errno >= nl.IPSET_ERR_PRIVATE {
   479  			err = nl.IPSetError(uintptr(errno))
   480  		}
   481  	}
   482  	return
   483  }
   484  
   485  func ipsetUnserialize(msgs [][]byte) (result IPSetResult) {
   486  	for _, msg := range msgs {
   487  		result.unserialize(msg)
   488  	}
   489  	return result
   490  }
   491  
   492  func (result *IPSetResult) unserialize(msg []byte) {
   493  	result.Nfgenmsg = nl.DeserializeNfgenmsg(msg)
   494  
   495  	for attr := range nl.ParseAttributes(msg[4:]) {
   496  		switch attr.Type {
   497  		case nl.IPSET_ATTR_PROTOCOL:
   498  			result.Protocol = attr.Value[0]
   499  		case nl.IPSET_ATTR_SETNAME:
   500  			result.SetName = nl.BytesToString(attr.Value)
   501  		case nl.IPSET_ATTR_COMMENT:
   502  			result.Comment = nl.BytesToString(attr.Value)
   503  		case nl.IPSET_ATTR_TYPENAME:
   504  			result.TypeName = nl.BytesToString(attr.Value)
   505  		case nl.IPSET_ATTR_REVISION:
   506  			result.Revision = attr.Value[0]
   507  		case nl.IPSET_ATTR_FAMILY:
   508  			result.Family = attr.Value[0]
   509  		case nl.IPSET_ATTR_FLAGS:
   510  			result.Flags = attr.Value[0]
   511  		case nl.IPSET_ATTR_DATA | nl.NLA_F_NESTED:
   512  			result.parseAttrData(attr.Value)
   513  		case nl.IPSET_ATTR_ADT | nl.NLA_F_NESTED:
   514  			result.parseAttrADT(attr.Value)
   515  		case nl.IPSET_ATTR_PROTOCOL_MIN:
   516  			result.ProtocolMinVersion = attr.Value[0]
   517  		case nl.IPSET_ATTR_MARKMASK:
   518  			result.MarkMask = attr.Uint32()
   519  		default:
   520  			log.Printf("unknown ipset attribute from kernel: %+v %v", attr, attr.Type&nl.NLA_TYPE_MASK)
   521  		}
   522  	}
   523  }
   524  
   525  func (result *IPSetResult) parseAttrData(data []byte) {
   526  	for attr := range nl.ParseAttributes(data) {
   527  		switch attr.Type {
   528  		case nl.IPSET_ATTR_HASHSIZE | nl.NLA_F_NET_BYTEORDER:
   529  			result.HashSize = attr.Uint32()
   530  		case nl.IPSET_ATTR_MAXELEM | nl.NLA_F_NET_BYTEORDER:
   531  			result.MaxElements = attr.Uint32()
   532  		case nl.IPSET_ATTR_TIMEOUT | nl.NLA_F_NET_BYTEORDER:
   533  			val := attr.Uint32()
   534  			result.Timeout = &val
   535  		case nl.IPSET_ATTR_ELEMENTS | nl.NLA_F_NET_BYTEORDER:
   536  			result.NumEntries = attr.Uint32()
   537  		case nl.IPSET_ATTR_REFERENCES | nl.NLA_F_NET_BYTEORDER:
   538  			result.References = attr.Uint32()
   539  		case nl.IPSET_ATTR_MEMSIZE | nl.NLA_F_NET_BYTEORDER:
   540  			result.SizeInMemory = attr.Uint32()
   541  		case nl.IPSET_ATTR_CADT_FLAGS | nl.NLA_F_NET_BYTEORDER:
   542  			result.CadtFlags = attr.Uint32()
   543  		case nl.IPSET_ATTR_IP | nl.NLA_F_NESTED:
   544  			for nested := range nl.ParseAttributes(attr.Value) {
   545  				switch nested.Type {
   546  				case nl.IPSET_ATTR_IP | nl.NLA_F_NET_BYTEORDER:
   547  					result.Entries = append(result.Entries, IPSetEntry{IP: nested.Value})
   548  				case nl.IPSET_ATTR_IP:
   549  					result.IPFrom = nested.Value
   550  				default:
   551  					log.Printf("unknown nested ipset data attribute from kernel: %+v %v", nested, nested.Type&nl.NLA_TYPE_MASK)
   552  				}
   553  			}
   554  		case nl.IPSET_ATTR_IP_TO | nl.NLA_F_NESTED:
   555  			for nested := range nl.ParseAttributes(attr.Value) {
   556  				switch nested.Type {
   557  				case nl.IPSET_ATTR_IP:
   558  					result.IPTo = nested.Value
   559  				default:
   560  					log.Printf("unknown nested ipset data attribute from kernel: %+v %v", nested, nested.Type&nl.NLA_TYPE_MASK)
   561  				}
   562  			}
   563  		case nl.IPSET_ATTR_PORT_FROM | nl.NLA_F_NET_BYTEORDER:
   564  			result.PortFrom = networkOrder.Uint16(attr.Value)
   565  		case nl.IPSET_ATTR_PORT_TO | nl.NLA_F_NET_BYTEORDER:
   566  			result.PortTo = networkOrder.Uint16(attr.Value)
   567  		case nl.IPSET_ATTR_CADT_LINENO | nl.NLA_F_NET_BYTEORDER:
   568  			result.LineNo = attr.Uint32()
   569  		case nl.IPSET_ATTR_COMMENT:
   570  			result.Comment = nl.BytesToString(attr.Value)
   571  		case nl.IPSET_ATTR_MARKMASK:
   572  			result.MarkMask = attr.Uint32()
   573  		default:
   574  			log.Printf("unknown ipset data attribute from kernel: %+v %v", attr, attr.Type&nl.NLA_TYPE_MASK)
   575  		}
   576  	}
   577  }
   578  
   579  func (result *IPSetResult) parseAttrADT(data []byte) {
   580  	for attr := range nl.ParseAttributes(data) {
   581  		switch attr.Type {
   582  		case nl.IPSET_ATTR_DATA | nl.NLA_F_NESTED:
   583  			result.Entries = append(result.Entries, parseIPSetEntry(attr.Value))
   584  		default:
   585  			log.Printf("unknown ADT attribute from kernel: %+v %v", attr, attr.Type&nl.NLA_TYPE_MASK)
   586  		}
   587  	}
   588  }
   589  
   590  func parseIPSetEntry(data []byte) (entry IPSetEntry) {
   591  	for attr := range nl.ParseAttributes(data) {
   592  		switch attr.Type {
   593  		case nl.IPSET_ATTR_TIMEOUT | nl.NLA_F_NET_BYTEORDER:
   594  			val := attr.Uint32()
   595  			entry.Timeout = &val
   596  		case nl.IPSET_ATTR_BYTES | nl.NLA_F_NET_BYTEORDER:
   597  			val := attr.Uint64()
   598  			entry.Bytes = &val
   599  		case nl.IPSET_ATTR_PACKETS | nl.NLA_F_NET_BYTEORDER:
   600  			val := attr.Uint64()
   601  			entry.Packets = &val
   602  		case nl.IPSET_ATTR_ETHER:
   603  			entry.MAC = net.HardwareAddr(attr.Value)
   604  		case nl.IPSET_ATTR_IP:
   605  			entry.IP = net.IP(attr.Value)
   606  		case nl.IPSET_ATTR_COMMENT:
   607  			entry.Comment = nl.BytesToString(attr.Value)
   608  		case nl.IPSET_ATTR_IP | nl.NLA_F_NESTED:
   609  			for attr := range nl.ParseAttributes(attr.Value) {
   610  				switch attr.Type {
   611  				case nl.IPSET_ATTR_IPADDR_IPV4, nl.IPSET_ATTR_IPADDR_IPV6:
   612  					entry.IP = net.IP(attr.Value)
   613  				default:
   614  					log.Printf("unknown nested ADT attribute from kernel: %+v", attr)
   615  				}
   616  			}
   617  		case nl.IPSET_ATTR_IP2 | nl.NLA_F_NESTED:
   618  			for attr := range nl.ParseAttributes(attr.Value) {
   619  				switch attr.Type {
   620  				case nl.IPSET_ATTR_IPADDR_IPV4, nl.IPSET_ATTR_IPADDR_IPV6:
   621  					entry.IP2 = net.IP(attr.Value)
   622  				default:
   623  					log.Printf("unknown nested ADT attribute from kernel: %+v", attr)
   624  				}
   625  			}
   626  		case nl.IPSET_ATTR_CIDR:
   627  			entry.CIDR = attr.Value[0]
   628  		case nl.IPSET_ATTR_CIDR2:
   629  			entry.CIDR2 = attr.Value[0]
   630  		case nl.IPSET_ATTR_PORT | nl.NLA_F_NET_BYTEORDER:
   631  			val := networkOrder.Uint16(attr.Value)
   632  			entry.Port = &val
   633  		case nl.IPSET_ATTR_PROTO:
   634  			val := attr.Value[0]
   635  			entry.Protocol = &val
   636  		case nl.IPSET_ATTR_IFACE:
   637  			entry.IFace = nl.BytesToString(attr.Value)
   638  		case nl.IPSET_ATTR_MARK | nl.NLA_F_NET_BYTEORDER:
   639  			val := attr.Uint32()
   640  			entry.Mark = &val
   641  		default:
   642  			log.Printf("unknown ADT attribute from kernel: %+v", attr)
   643  		}
   644  	}
   645  	return
   646  }
   647  
   648  func optionsToBitflag(options IpsetCreateOptions) uint32 {
   649  	var cadtFlags uint32
   650  
   651  	if options.Comments {
   652  		cadtFlags |= nl.IPSET_FLAG_WITH_COMMENT
   653  	}
   654  	if options.Counters {
   655  		cadtFlags |= nl.IPSET_FLAG_WITH_COUNTERS
   656  	}
   657  	if options.Skbinfo {
   658  		cadtFlags |= nl.IPSET_FLAG_WITH_SKBINFO
   659  	}
   660  
   661  	return cadtFlags
   662  }