github.com/vishvananda/netlink@v1.3.0/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  	revision := options.Revision
   151  	if revision == 0 {
   152  		revision = getIpsetDefaultWithTypeName(typename)
   153  	}
   154  	req.AddData(nl.NewRtAttr(nl.IPSET_ATTR_REVISION, nl.Uint8Attr(revision)))
   155  
   156  	data := nl.NewRtAttr(nl.IPSET_ATTR_DATA|int(nl.NLA_F_NESTED), nil)
   157  
   158  	var family uint8
   159  	switch typename {
   160  	case "hash:mac":
   161  	case "bitmap:port":
   162  		buf := make([]byte, 4)
   163  		binary.BigEndian.PutUint16(buf, options.PortFrom)
   164  		binary.BigEndian.PutUint16(buf[2:], options.PortTo)
   165  		data.AddChild(nl.NewRtAttr(nl.IPSET_ATTR_PORT_FROM|int(nl.NLA_F_NET_BYTEORDER), buf[:2]))
   166  		data.AddChild(nl.NewRtAttr(nl.IPSET_ATTR_PORT_TO|int(nl.NLA_F_NET_BYTEORDER), buf[2:]))
   167  	default:
   168  		family = options.Family
   169  		if family == 0 {
   170  			family = unix.AF_INET
   171  		}
   172  	}
   173  
   174  	req.AddData(nl.NewRtAttr(nl.IPSET_ATTR_FAMILY, nl.Uint8Attr(family)))
   175  
   176  	if options.MaxElements != 0 {
   177  		data.AddChild(&nl.Uint32Attribute{Type: nl.IPSET_ATTR_MAXELEM | nl.NLA_F_NET_BYTEORDER, Value: options.MaxElements})
   178  	}
   179  
   180  	if timeout := options.Timeout; timeout != nil {
   181  		data.AddChild(&nl.Uint32Attribute{Type: nl.IPSET_ATTR_TIMEOUT | nl.NLA_F_NET_BYTEORDER, Value: *timeout})
   182  	}
   183  
   184  	var cadtFlags uint32
   185  
   186  	if options.Comments {
   187  		cadtFlags |= nl.IPSET_FLAG_WITH_COMMENT
   188  	}
   189  	if options.Counters {
   190  		cadtFlags |= nl.IPSET_FLAG_WITH_COUNTERS
   191  	}
   192  	if options.Skbinfo {
   193  		cadtFlags |= nl.IPSET_FLAG_WITH_SKBINFO
   194  	}
   195  
   196  	if cadtFlags != 0 {
   197  		data.AddChild(&nl.Uint32Attribute{Type: nl.IPSET_ATTR_CADT_FLAGS | nl.NLA_F_NET_BYTEORDER, Value: cadtFlags})
   198  	}
   199  
   200  	req.AddData(data)
   201  	_, err := ipsetExecute(req)
   202  	return err
   203  }
   204  
   205  func (h *Handle) IpsetDestroy(setname string) error {
   206  	req := h.newIpsetRequest(nl.IPSET_CMD_DESTROY)
   207  	req.AddData(nl.NewRtAttr(nl.IPSET_ATTR_SETNAME, nl.ZeroTerminated(setname)))
   208  	_, err := ipsetExecute(req)
   209  	return err
   210  }
   211  
   212  func (h *Handle) IpsetFlush(setname string) error {
   213  	req := h.newIpsetRequest(nl.IPSET_CMD_FLUSH)
   214  	req.AddData(nl.NewRtAttr(nl.IPSET_ATTR_SETNAME, nl.ZeroTerminated(setname)))
   215  	_, err := ipsetExecute(req)
   216  	return err
   217  }
   218  
   219  func (h *Handle) IpsetSwap(setname, othersetname string) error {
   220  	req := h.newIpsetRequest(nl.IPSET_CMD_SWAP)
   221  	req.AddData(nl.NewRtAttr(nl.IPSET_ATTR_SETNAME, nl.ZeroTerminated(setname)))
   222  	req.AddData(nl.NewRtAttr(nl.IPSET_ATTR_TYPENAME, nl.ZeroTerminated(othersetname)))
   223  	_, err := ipsetExecute(req)
   224  	return err
   225  }
   226  
   227  func (h *Handle) IpsetList(name string) (*IPSetResult, error) {
   228  	req := h.newIpsetRequest(nl.IPSET_CMD_LIST)
   229  	req.AddData(nl.NewRtAttr(nl.IPSET_ATTR_SETNAME, nl.ZeroTerminated(name)))
   230  
   231  	msgs, err := ipsetExecute(req)
   232  	if err != nil {
   233  		return nil, err
   234  	}
   235  
   236  	result := ipsetUnserialize(msgs)
   237  	return &result, nil
   238  }
   239  
   240  func (h *Handle) IpsetListAll() ([]IPSetResult, error) {
   241  	req := h.newIpsetRequest(nl.IPSET_CMD_LIST)
   242  
   243  	msgs, err := ipsetExecute(req)
   244  	if err != nil {
   245  		return nil, err
   246  	}
   247  
   248  	result := make([]IPSetResult, len(msgs))
   249  	for i, msg := range msgs {
   250  		result[i].unserialize(msg)
   251  	}
   252  
   253  	return result, nil
   254  }
   255  
   256  // IpsetAdd adds an entry to an existing ipset.
   257  func (h *Handle) IpsetAdd(setname string, entry *IPSetEntry) error {
   258  	return h.ipsetAddDel(nl.IPSET_CMD_ADD, setname, entry)
   259  }
   260  
   261  // IpsetDel deletes an entry from an existing ipset.
   262  func (h *Handle) IpsetDel(setname string, entry *IPSetEntry) error {
   263  	return h.ipsetAddDel(nl.IPSET_CMD_DEL, setname, entry)
   264  }
   265  
   266  func encodeIP(ip net.IP) (*nl.RtAttr, error) {
   267  	typ := int(nl.NLA_F_NET_BYTEORDER)
   268  	if ip4 := ip.To4(); ip4 != nil {
   269  		typ |= nl.IPSET_ATTR_IPADDR_IPV4
   270  		ip = ip4
   271  	} else {
   272  		typ |= nl.IPSET_ATTR_IPADDR_IPV6
   273  	}
   274  
   275  	return nl.NewRtAttr(typ, ip), nil
   276  }
   277  
   278  func buildEntryData(entry *IPSetEntry) (*nl.RtAttr, error) {
   279  	data := nl.NewRtAttr(nl.IPSET_ATTR_DATA|int(nl.NLA_F_NESTED), nil)
   280  
   281  	if entry.Comment != "" {
   282  		data.AddChild(nl.NewRtAttr(nl.IPSET_ATTR_COMMENT, nl.ZeroTerminated(entry.Comment)))
   283  	}
   284  
   285  	if entry.Timeout != nil {
   286  		data.AddChild(&nl.Uint32Attribute{Type: nl.IPSET_ATTR_TIMEOUT | nl.NLA_F_NET_BYTEORDER, Value: *entry.Timeout})
   287  	}
   288  
   289  	if entry.IP != nil {
   290  		nestedData, err := encodeIP(entry.IP)
   291  		if err != nil {
   292  			return nil, err
   293  		}
   294  		data.AddChild(nl.NewRtAttr(nl.IPSET_ATTR_IP|int(nl.NLA_F_NESTED), nestedData.Serialize()))
   295  	}
   296  
   297  	if entry.MAC != nil {
   298  		data.AddChild(nl.NewRtAttr(nl.IPSET_ATTR_ETHER, entry.MAC))
   299  	}
   300  
   301  	if entry.CIDR != 0 {
   302  		data.AddChild(nl.NewRtAttr(nl.IPSET_ATTR_CIDR, nl.Uint8Attr(entry.CIDR)))
   303  	}
   304  
   305  	if entry.IP2 != nil {
   306  		nestedData, err := encodeIP(entry.IP2)
   307  		if err != nil {
   308  			return nil, err
   309  		}
   310  		data.AddChild(nl.NewRtAttr(nl.IPSET_ATTR_IP2|int(nl.NLA_F_NESTED), nestedData.Serialize()))
   311  	}
   312  
   313  	if entry.CIDR2 != 0 {
   314  		data.AddChild(nl.NewRtAttr(nl.IPSET_ATTR_CIDR2, nl.Uint8Attr(entry.CIDR2)))
   315  	}
   316  
   317  	if entry.Port != nil {
   318  		if entry.Protocol == nil {
   319  			// use tcp protocol as default
   320  			val := uint8(unix.IPPROTO_TCP)
   321  			entry.Protocol = &val
   322  		}
   323  		data.AddChild(nl.NewRtAttr(nl.IPSET_ATTR_PROTO, nl.Uint8Attr(*entry.Protocol)))
   324  		buf := make([]byte, 2)
   325  		binary.BigEndian.PutUint16(buf, *entry.Port)
   326  		data.AddChild(nl.NewRtAttr(int(nl.IPSET_ATTR_PORT|nl.NLA_F_NET_BYTEORDER), buf))
   327  	}
   328  
   329  	if entry.IFace != "" {
   330  		data.AddChild(nl.NewRtAttr(nl.IPSET_ATTR_IFACE, nl.ZeroTerminated(entry.IFace)))
   331  	}
   332  
   333  	if entry.Mark != nil {
   334  		data.AddChild(&nl.Uint32Attribute{Type: nl.IPSET_ATTR_MARK | nl.NLA_F_NET_BYTEORDER, Value: *entry.Mark})
   335  	}
   336  	return data, nil
   337  }
   338  
   339  func (h *Handle) ipsetAddDel(nlCmd int, setname string, entry *IPSetEntry) error {
   340  	req := h.newIpsetRequest(nlCmd)
   341  	req.AddData(nl.NewRtAttr(nl.IPSET_ATTR_SETNAME, nl.ZeroTerminated(setname)))
   342  
   343  	if !entry.Replace {
   344  		req.Flags |= unix.NLM_F_EXCL
   345  	}
   346  
   347  	data, err := buildEntryData(entry)
   348  	if err != nil {
   349  		return err
   350  	}
   351  	data.AddChild(&nl.Uint32Attribute{Type: nl.IPSET_ATTR_LINENO | nl.NLA_F_NET_BYTEORDER, Value: 0})
   352  	req.AddData(data)
   353  
   354  	_, err = ipsetExecute(req)
   355  	return err
   356  }
   357  
   358  func (h *Handle) IpsetTest(setname string, entry *IPSetEntry) (bool, error) {
   359  	req := h.newIpsetRequest(nl.IPSET_CMD_TEST)
   360  	req.AddData(nl.NewRtAttr(nl.IPSET_ATTR_SETNAME, nl.ZeroTerminated(setname)))
   361  
   362  	if !entry.Replace {
   363  		req.Flags |= unix.NLM_F_EXCL
   364  	}
   365  
   366  	data, err := buildEntryData(entry)
   367  	if err != nil {
   368  		return false, err
   369  	}
   370  	req.AddData(data)
   371  
   372  	_, err = ipsetExecute(req)
   373  	if err != nil {
   374  		if err == nl.IPSetError(nl.IPSET_ERR_EXIST) {
   375  			// not exist
   376  			return false, nil
   377  		}
   378  		return false, err
   379  	}
   380  	return true, nil
   381  }
   382  
   383  func (h *Handle) newIpsetRequest(cmd int) *nl.NetlinkRequest {
   384  	req := h.newNetlinkRequest(cmd|(unix.NFNL_SUBSYS_IPSET<<8), nl.GetIpsetFlags(cmd))
   385  
   386  	// Add the netfilter header
   387  	msg := &nl.Nfgenmsg{
   388  		NfgenFamily: uint8(unix.AF_NETLINK),
   389  		Version:     nl.NFNETLINK_V0,
   390  		ResId:       0,
   391  	}
   392  	req.AddData(msg)
   393  	req.AddData(nl.NewRtAttr(nl.IPSET_ATTR_PROTOCOL, nl.Uint8Attr(nl.IPSET_PROTOCOL)))
   394  
   395  	return req
   396  }
   397  
   398  func getIpsetDefaultWithTypeName(typename string) uint8 {
   399  	switch typename {
   400  	case "hash:ip,port",
   401  		"hash:ip,port,ip",
   402  		"hash:ip,port,net",
   403  		"hash:net,port":
   404  		return 1
   405  	}
   406  	return 0
   407  }
   408  
   409  func ipsetExecute(req *nl.NetlinkRequest) (msgs [][]byte, err error) {
   410  	msgs, err = req.Execute(unix.NETLINK_NETFILTER, 0)
   411  
   412  	if err != nil {
   413  		if errno := int(err.(syscall.Errno)); errno >= nl.IPSET_ERR_PRIVATE {
   414  			err = nl.IPSetError(uintptr(errno))
   415  		}
   416  	}
   417  	return
   418  }
   419  
   420  func ipsetUnserialize(msgs [][]byte) (result IPSetResult) {
   421  	for _, msg := range msgs {
   422  		result.unserialize(msg)
   423  	}
   424  	return result
   425  }
   426  
   427  func (result *IPSetResult) unserialize(msg []byte) {
   428  	result.Nfgenmsg = nl.DeserializeNfgenmsg(msg)
   429  
   430  	for attr := range nl.ParseAttributes(msg[4:]) {
   431  		switch attr.Type {
   432  		case nl.IPSET_ATTR_PROTOCOL:
   433  			result.Protocol = attr.Value[0]
   434  		case nl.IPSET_ATTR_SETNAME:
   435  			result.SetName = nl.BytesToString(attr.Value)
   436  		case nl.IPSET_ATTR_COMMENT:
   437  			result.Comment = nl.BytesToString(attr.Value)
   438  		case nl.IPSET_ATTR_TYPENAME:
   439  			result.TypeName = nl.BytesToString(attr.Value)
   440  		case nl.IPSET_ATTR_REVISION:
   441  			result.Revision = attr.Value[0]
   442  		case nl.IPSET_ATTR_FAMILY:
   443  			result.Family = attr.Value[0]
   444  		case nl.IPSET_ATTR_FLAGS:
   445  			result.Flags = attr.Value[0]
   446  		case nl.IPSET_ATTR_DATA | nl.NLA_F_NESTED:
   447  			result.parseAttrData(attr.Value)
   448  		case nl.IPSET_ATTR_ADT | nl.NLA_F_NESTED:
   449  			result.parseAttrADT(attr.Value)
   450  		case nl.IPSET_ATTR_PROTOCOL_MIN:
   451  			result.ProtocolMinVersion = attr.Value[0]
   452  		case nl.IPSET_ATTR_MARKMASK:
   453  			result.MarkMask = attr.Uint32()
   454  		default:
   455  			log.Printf("unknown ipset attribute from kernel: %+v %v", attr, attr.Type&nl.NLA_TYPE_MASK)
   456  		}
   457  	}
   458  }
   459  
   460  func (result *IPSetResult) parseAttrData(data []byte) {
   461  	for attr := range nl.ParseAttributes(data) {
   462  		switch attr.Type {
   463  		case nl.IPSET_ATTR_HASHSIZE | nl.NLA_F_NET_BYTEORDER:
   464  			result.HashSize = attr.Uint32()
   465  		case nl.IPSET_ATTR_MAXELEM | nl.NLA_F_NET_BYTEORDER:
   466  			result.MaxElements = attr.Uint32()
   467  		case nl.IPSET_ATTR_TIMEOUT | nl.NLA_F_NET_BYTEORDER:
   468  			val := attr.Uint32()
   469  			result.Timeout = &val
   470  		case nl.IPSET_ATTR_ELEMENTS | nl.NLA_F_NET_BYTEORDER:
   471  			result.NumEntries = attr.Uint32()
   472  		case nl.IPSET_ATTR_REFERENCES | nl.NLA_F_NET_BYTEORDER:
   473  			result.References = attr.Uint32()
   474  		case nl.IPSET_ATTR_MEMSIZE | nl.NLA_F_NET_BYTEORDER:
   475  			result.SizeInMemory = attr.Uint32()
   476  		case nl.IPSET_ATTR_CADT_FLAGS | nl.NLA_F_NET_BYTEORDER:
   477  			result.CadtFlags = attr.Uint32()
   478  		case nl.IPSET_ATTR_IP | nl.NLA_F_NESTED:
   479  			for nested := range nl.ParseAttributes(attr.Value) {
   480  				switch nested.Type {
   481  				case nl.IPSET_ATTR_IP | nl.NLA_F_NET_BYTEORDER:
   482  					result.Entries = append(result.Entries, IPSetEntry{IP: nested.Value})
   483  				case nl.IPSET_ATTR_IP:
   484  					result.IPFrom = nested.Value
   485  				default:
   486  					log.Printf("unknown nested ipset data attribute from kernel: %+v %v", nested, nested.Type&nl.NLA_TYPE_MASK)
   487  				}
   488  			}
   489  		case nl.IPSET_ATTR_IP_TO | nl.NLA_F_NESTED:
   490  			for nested := range nl.ParseAttributes(attr.Value) {
   491  				switch nested.Type {
   492  				case nl.IPSET_ATTR_IP:
   493  					result.IPTo = nested.Value
   494  				default:
   495  					log.Printf("unknown nested ipset data attribute from kernel: %+v %v", nested, nested.Type&nl.NLA_TYPE_MASK)
   496  				}
   497  			}
   498  		case nl.IPSET_ATTR_PORT_FROM | nl.NLA_F_NET_BYTEORDER:
   499  			result.PortFrom = networkOrder.Uint16(attr.Value)
   500  		case nl.IPSET_ATTR_PORT_TO | nl.NLA_F_NET_BYTEORDER:
   501  			result.PortTo = networkOrder.Uint16(attr.Value)
   502  		case nl.IPSET_ATTR_CADT_LINENO | nl.NLA_F_NET_BYTEORDER:
   503  			result.LineNo = attr.Uint32()
   504  		case nl.IPSET_ATTR_COMMENT:
   505  			result.Comment = nl.BytesToString(attr.Value)
   506  		case nl.IPSET_ATTR_MARKMASK:
   507  			result.MarkMask = attr.Uint32()
   508  		default:
   509  			log.Printf("unknown ipset data attribute from kernel: %+v %v", attr, attr.Type&nl.NLA_TYPE_MASK)
   510  		}
   511  	}
   512  }
   513  
   514  func (result *IPSetResult) parseAttrADT(data []byte) {
   515  	for attr := range nl.ParseAttributes(data) {
   516  		switch attr.Type {
   517  		case nl.IPSET_ATTR_DATA | nl.NLA_F_NESTED:
   518  			result.Entries = append(result.Entries, parseIPSetEntry(attr.Value))
   519  		default:
   520  			log.Printf("unknown ADT attribute from kernel: %+v %v", attr, attr.Type&nl.NLA_TYPE_MASK)
   521  		}
   522  	}
   523  }
   524  
   525  func parseIPSetEntry(data []byte) (entry IPSetEntry) {
   526  	for attr := range nl.ParseAttributes(data) {
   527  		switch attr.Type {
   528  		case nl.IPSET_ATTR_TIMEOUT | nl.NLA_F_NET_BYTEORDER:
   529  			val := attr.Uint32()
   530  			entry.Timeout = &val
   531  		case nl.IPSET_ATTR_BYTES | nl.NLA_F_NET_BYTEORDER:
   532  			val := attr.Uint64()
   533  			entry.Bytes = &val
   534  		case nl.IPSET_ATTR_PACKETS | nl.NLA_F_NET_BYTEORDER:
   535  			val := attr.Uint64()
   536  			entry.Packets = &val
   537  		case nl.IPSET_ATTR_ETHER:
   538  			entry.MAC = net.HardwareAddr(attr.Value)
   539  		case nl.IPSET_ATTR_IP:
   540  			entry.IP = net.IP(attr.Value)
   541  		case nl.IPSET_ATTR_COMMENT:
   542  			entry.Comment = nl.BytesToString(attr.Value)
   543  		case nl.IPSET_ATTR_IP | nl.NLA_F_NESTED:
   544  			for attr := range nl.ParseAttributes(attr.Value) {
   545  				switch attr.Type {
   546  				case nl.IPSET_ATTR_IPADDR_IPV4, nl.IPSET_ATTR_IPADDR_IPV6:
   547  					entry.IP = net.IP(attr.Value)
   548  				default:
   549  					log.Printf("unknown nested ADT attribute from kernel: %+v", attr)
   550  				}
   551  			}
   552  		case nl.IPSET_ATTR_IP2 | nl.NLA_F_NESTED:
   553  			for attr := range nl.ParseAttributes(attr.Value) {
   554  				switch attr.Type {
   555  				case nl.IPSET_ATTR_IPADDR_IPV4, nl.IPSET_ATTR_IPADDR_IPV6:
   556  					entry.IP2 = net.IP(attr.Value)
   557  				default:
   558  					log.Printf("unknown nested ADT attribute from kernel: %+v", attr)
   559  				}
   560  			}
   561  		case nl.IPSET_ATTR_CIDR:
   562  			entry.CIDR = attr.Value[0]
   563  		case nl.IPSET_ATTR_CIDR2:
   564  			entry.CIDR2 = attr.Value[0]
   565  		case nl.IPSET_ATTR_PORT | nl.NLA_F_NET_BYTEORDER:
   566  			val := networkOrder.Uint16(attr.Value)
   567  			entry.Port = &val
   568  		case nl.IPSET_ATTR_PROTO:
   569  			val := attr.Value[0]
   570  			entry.Protocol = &val
   571  		case nl.IPSET_ATTR_IFACE:
   572  			entry.IFace = nl.BytesToString(attr.Value)
   573  		case nl.IPSET_ATTR_MARK | nl.NLA_F_NET_BYTEORDER:
   574  			val := attr.Uint32()
   575  			entry.Mark = &val
   576  		default:
   577  			log.Printf("unknown ADT attribute from kernel: %+v", attr)
   578  		}
   579  	}
   580  	return
   581  }