vendor: Update vishvanana/netlink dependency.
authorDaniel Nardo <dnardo@google.com>
Fri, 30 Jun 2017 22:18:01 +0000 (15:18 -0700)
committerDaniel Nardo <dnardo@google.com>
Fri, 30 Jun 2017 22:18:01 +0000 (15:18 -0700)
This updates the netlink dependency to include recent updates, including a fix when setting prosmic mode on a bridge and additions for creating qdisc/classes/filters. This is necessary for some upcoming additions to CNI

28 files changed:
Godeps/Godeps.json
vendor/github.com/vishvananda/netlink/.travis.yml
vendor/github.com/vishvananda/netlink/addr.go
vendor/github.com/vishvananda/netlink/addr_linux.go
vendor/github.com/vishvananda/netlink/bridge_linux.go [new file with mode: 0644]
vendor/github.com/vishvananda/netlink/conntrack_linux.go [new file with mode: 0644]
vendor/github.com/vishvananda/netlink/conntrack_unspecified.go [new file with mode: 0644]
vendor/github.com/vishvananda/netlink/filter.go
vendor/github.com/vishvananda/netlink/filter_linux.go
vendor/github.com/vishvananda/netlink/genetlink_linux.go [new file with mode: 0644]
vendor/github.com/vishvananda/netlink/genetlink_unspecified.go [new file with mode: 0644]
vendor/github.com/vishvananda/netlink/gtp_linux.go [new file with mode: 0644]
vendor/github.com/vishvananda/netlink/link.go
vendor/github.com/vishvananda/netlink/link_linux.go
vendor/github.com/vishvananda/netlink/netlink_unspecified.go
vendor/github.com/vishvananda/netlink/nl/addr_linux.go
vendor/github.com/vishvananda/netlink/nl/bridge_linux.go [new file with mode: 0644]
vendor/github.com/vishvananda/netlink/nl/conntrack_linux.go [new file with mode: 0644]
vendor/github.com/vishvananda/netlink/nl/genetlink_linux.go [new file with mode: 0644]
vendor/github.com/vishvananda/netlink/nl/link_linux.go
vendor/github.com/vishvananda/netlink/nl/nl_linux.go
vendor/github.com/vishvananda/netlink/protinfo.go
vendor/github.com/vishvananda/netlink/protinfo_linux.go
vendor/github.com/vishvananda/netlink/route_linux.go
vendor/github.com/vishvananda/netns/README.md
vendor/github.com/vishvananda/netns/netns.go
vendor/github.com/vishvananda/netns/netns_linux.go
vendor/github.com/vishvananda/netns/netns_unspecified.go

index 51e0c24..1eeeb2a 100644 (file)
                },
                {
                        "ImportPath": "github.com/vishvananda/netlink",
-                       "Rev": "fe3b5664d23a11b52ba59bece4ff29c52772a56b"
+                       "Rev": "6e453822d85ef5721799774b654d4d02fed62afb"
                },
                {
                        "ImportPath": "github.com/vishvananda/netlink/nl",
-                       "Rev": "fe3b5664d23a11b52ba59bece4ff29c52772a56b"
+                       "Rev": "6e453822d85ef5721799774b654d4d02fed62afb"
                },
                {
                        "ImportPath": "github.com/vishvananda/netns",
-                       "Rev": "8ba1072b58e0c2a240eb5f6120165c7776c3e7b8"
+                       "Rev": "54f0e4339ce73702a0607f49922aaa1e749b418d"
                },
                {
                        "ImportPath": "golang.org/x/sys/unix",
index 73a0374..f5c0b3e 100644 (file)
@@ -4,5 +4,10 @@ before_script:
   - sudo sed -i -e 's/^Defaults\tsecure_path.*$//' /etc/sudoers
   # modprobe ip_gre or else the first gre device can't be deleted
   - sudo modprobe ip_gre
+  # modprobe nf_conntrack for the conntrack testing
+  - sudo modprobe nf_conntrack
+  - sudo modprobe nf_conntrack_netlink
+  - sudo modprobe nf_conntrack_ipv4
+  - sudo modprobe nf_conntrack_ipv6
 install:
   - go get github.com/vishvananda/netns
index fe3e3d3..f08c956 100644 (file)
@@ -10,11 +10,13 @@ import (
 // include a mask, so it stores the address as a net.IPNet.
 type Addr struct {
        *net.IPNet
-       Label     string
-       Flags     int
-       Scope     int
-       Peer      *net.IPNet
-       Broadcast net.IP
+       Label       string
+       Flags       int
+       Scope       int
+       Peer        *net.IPNet
+       Broadcast   net.IP
+       PreferedLft int
+       ValidLft    int
 }
 
 // String returns $ip/$netmask $label
index 5348e40..f33242a 100644 (file)
@@ -27,6 +27,19 @@ func (h *Handle) AddrAdd(link Link, addr *Addr) error {
        return h.addrHandle(link, addr, req)
 }
 
+// AddrReplace will replace (or, if not present, add) an IP address on a link device.
+// Equivalent to: `ip addr replace $addr dev $link`
+func AddrReplace(link Link, addr *Addr) error {
+       return pkgHandle.AddrReplace(link, addr)
+}
+
+// AddrReplace will replace (or, if not present, add) an IP address on a link device.
+// Equivalent to: `ip addr replace $addr dev $link`
+func (h *Handle) AddrReplace(link Link, addr *Addr) error {
+       req := h.newNetlinkRequest(syscall.RTM_NEWADDR, syscall.NLM_F_CREATE|syscall.NLM_F_REPLACE|syscall.NLM_F_ACK)
+       return h.addrHandle(link, addr, req)
+}
+
 // AddrDel will delete an IP address from a link device.
 // Equivalent to: `ip addr del $addr dev $link`
 func AddrDel(link Link, addr *Addr) error {
@@ -182,10 +195,16 @@ func parseAddr(m []byte) (addr Addr, family, index int, err error) {
                                Mask: net.CIDRMask(int(msg.Prefixlen), 8*len(attr.Value)),
                        }
                        addr.IPNet = local
+               case syscall.IFA_BROADCAST:
+                       addr.Broadcast = attr.Value
                case syscall.IFA_LABEL:
                        addr.Label = string(attr.Value[:len(attr.Value)-1])
                case IFA_FLAGS:
                        addr.Flags = int(native.Uint32(attr.Value[0:4]))
+               case nl.IFA_CACHEINFO:
+                       ci := nl.DeserializeIfaCacheInfo(attr.Value)
+                       addr.PreferedLft = int(ci.IfaPrefered)
+                       addr.ValidLft = int(ci.IfaValid)
                }
        }
 
@@ -203,6 +222,10 @@ func parseAddr(m []byte) (addr Addr, family, index int, err error) {
 type AddrUpdate struct {
        LinkAddress net.IPNet
        LinkIndex   int
+       Flags       int
+       Scope       int
+       PreferedLft int
+       ValidLft    int
        NewAddr     bool // true=added false=deleted
 }
 
@@ -250,7 +273,13 @@ func addrSubscribe(newNs, curNs netns.NsHandle, ch chan<- AddrUpdate, done <-cha
                                        continue
                                }
 
-                               ch <- AddrUpdate{LinkAddress: *addr.IPNet, LinkIndex: ifindex, NewAddr: msgType == syscall.RTM_NEWADDR}
+                               ch <- AddrUpdate{LinkAddress: *addr.IPNet,
+                                       LinkIndex:   ifindex,
+                                       NewAddr:     msgType == syscall.RTM_NEWADDR,
+                                       Flags:       addr.Flags,
+                                       Scope:       addr.Scope,
+                                       PreferedLft: addr.PreferedLft,
+                                       ValidLft:    addr.ValidLft}
                        }
                }
        }()
diff --git a/vendor/github.com/vishvananda/netlink/bridge_linux.go b/vendor/github.com/vishvananda/netlink/bridge_linux.go
new file mode 100644 (file)
index 0000000..a65d6a1
--- /dev/null
@@ -0,0 +1,115 @@
+package netlink
+
+import (
+       "fmt"
+       "syscall"
+
+       "github.com/vishvananda/netlink/nl"
+)
+
+// BridgeVlanList gets a map of device id to bridge vlan infos.
+// Equivalent to: `bridge vlan show`
+func BridgeVlanList() (map[int32][]*nl.BridgeVlanInfo, error) {
+       return pkgHandle.BridgeVlanList()
+}
+
+// BridgeVlanList gets a map of device id to bridge vlan infos.
+// Equivalent to: `bridge vlan show`
+func (h *Handle) BridgeVlanList() (map[int32][]*nl.BridgeVlanInfo, error) {
+       req := h.newNetlinkRequest(syscall.RTM_GETLINK, syscall.NLM_F_DUMP)
+       msg := nl.NewIfInfomsg(syscall.AF_BRIDGE)
+       req.AddData(msg)
+       req.AddData(nl.NewRtAttr(nl.IFLA_EXT_MASK, nl.Uint32Attr(uint32(nl.RTEXT_FILTER_BRVLAN))))
+
+       msgs, err := req.Execute(syscall.NETLINK_ROUTE, syscall.RTM_NEWLINK)
+       if err != nil {
+               return nil, err
+       }
+       ret := make(map[int32][]*nl.BridgeVlanInfo)
+       for _, m := range msgs {
+               msg := nl.DeserializeIfInfomsg(m)
+
+               attrs, err := nl.ParseRouteAttr(m[msg.Len():])
+               if err != nil {
+                       return nil, err
+               }
+               for _, attr := range attrs {
+                       switch attr.Attr.Type {
+                       case nl.IFLA_AF_SPEC:
+                               //nested attr
+                               nestAttrs, err := nl.ParseRouteAttr(attr.Value)
+                               if err != nil {
+                                       return nil, fmt.Errorf("failed to parse nested attr %v", err)
+                               }
+                               for _, nestAttr := range nestAttrs {
+                                       switch nestAttr.Attr.Type {
+                                       case nl.IFLA_BRIDGE_VLAN_INFO:
+                                               vlanInfo := nl.DeserializeBridgeVlanInfo(nestAttr.Value)
+                                               ret[msg.Index] = append(ret[msg.Index], vlanInfo)
+                                       }
+                               }
+                       }
+               }
+       }
+       return ret, nil
+}
+
+// BridgeVlanAdd adds a new vlan filter entry
+// Equivalent to: `bridge vlan add dev DEV vid VID [ pvid ] [ untagged ] [ self ] [ master ]`
+func BridgeVlanAdd(link Link, vid uint16, pvid, untagged, self, master bool) error {
+       return pkgHandle.BridgeVlanAdd(link, vid, pvid, untagged, self, master)
+}
+
+// BridgeVlanAdd adds a new vlan filter entry
+// Equivalent to: `bridge vlan add dev DEV vid VID [ pvid ] [ untagged ] [ self ] [ master ]`
+func (h *Handle) BridgeVlanAdd(link Link, vid uint16, pvid, untagged, self, master bool) error {
+       return h.bridgeVlanModify(syscall.RTM_SETLINK, link, vid, pvid, untagged, self, master)
+}
+
+// BridgeVlanDel adds a new vlan filter entry
+// Equivalent to: `bridge vlan del dev DEV vid VID [ pvid ] [ untagged ] [ self ] [ master ]`
+func BridgeVlanDel(link Link, vid uint16, pvid, untagged, self, master bool) error {
+       return pkgHandle.BridgeVlanDel(link, vid, pvid, untagged, self, master)
+}
+
+// BridgeVlanDel adds a new vlan filter entry
+// Equivalent to: `bridge vlan del dev DEV vid VID [ pvid ] [ untagged ] [ self ] [ master ]`
+func (h *Handle) BridgeVlanDel(link Link, vid uint16, pvid, untagged, self, master bool) error {
+       return h.bridgeVlanModify(syscall.RTM_DELLINK, link, vid, pvid, untagged, self, master)
+}
+
+func (h *Handle) bridgeVlanModify(cmd int, link Link, vid uint16, pvid, untagged, self, master bool) error {
+       base := link.Attrs()
+       h.ensureIndex(base)
+       req := h.newNetlinkRequest(cmd, syscall.NLM_F_ACK)
+
+       msg := nl.NewIfInfomsg(syscall.AF_BRIDGE)
+       msg.Index = int32(base.Index)
+       req.AddData(msg)
+
+       br := nl.NewRtAttr(nl.IFLA_AF_SPEC, nil)
+       var flags uint16
+       if self {
+               flags |= nl.BRIDGE_FLAGS_SELF
+       }
+       if master {
+               flags |= nl.BRIDGE_FLAGS_MASTER
+       }
+       if flags > 0 {
+               nl.NewRtAttrChild(br, nl.IFLA_BRIDGE_FLAGS, nl.Uint16Attr(flags))
+       }
+       vlanInfo := &nl.BridgeVlanInfo{Vid: vid}
+       if pvid {
+               vlanInfo.Flags |= nl.BRIDGE_VLAN_INFO_PVID
+       }
+       if untagged {
+               vlanInfo.Flags |= nl.BRIDGE_VLAN_INFO_UNTAGGED
+       }
+       nl.NewRtAttrChild(br, nl.IFLA_BRIDGE_VLAN_INFO, vlanInfo.Serialize())
+       req.AddData(br)
+       _, err := req.Execute(syscall.NETLINK_ROUTE, 0)
+       if err != nil {
+               return err
+       }
+       return nil
+}
diff --git a/vendor/github.com/vishvananda/netlink/conntrack_linux.go b/vendor/github.com/vishvananda/netlink/conntrack_linux.go
new file mode 100644 (file)
index 0000000..bcdd692
--- /dev/null
@@ -0,0 +1,352 @@
+package netlink
+
+import (
+       "bytes"
+       "encoding/binary"
+       "errors"
+       "fmt"
+       "net"
+       "syscall"
+
+       "github.com/vishvananda/netlink/nl"
+)
+
+// ConntrackTableType Conntrack table for the netlink operation
+type ConntrackTableType uint8
+
+const (
+       // ConntrackTable Conntrack table
+       // https://github.com/torvalds/linux/blob/master/include/uapi/linux/netfilter/nfnetlink.h -> #define NFNL_SUBSYS_CTNETLINK               1
+       ConntrackTable = 1
+       // ConntrackExpectTable Conntrack expect table
+       // https://github.com/torvalds/linux/blob/master/include/uapi/linux/netfilter/nfnetlink.h -> #define NFNL_SUBSYS_CTNETLINK_EXP 2
+       ConntrackExpectTable = 2
+)
+
+const (
+       // backward compatibility with golang 1.6 which does not have io.SeekCurrent
+       seekCurrent = 1
+)
+
+// InetFamily Family type
+type InetFamily uint8
+
+//  -L [table] [options]          List conntrack or expectation table
+//  -G [table] parameters         Get conntrack or expectation
+
+//  -I [table] parameters         Create a conntrack or expectation
+//  -U [table] parameters         Update a conntrack
+//  -E [table] [options]          Show events
+
+//  -C [table]                    Show counter
+//  -S                            Show statistics
+
+// ConntrackTableList returns the flow list of a table of a specific family
+// conntrack -L [table] [options]          List conntrack or expectation table
+func ConntrackTableList(table ConntrackTableType, family InetFamily) ([]*ConntrackFlow, error) {
+       return pkgHandle.ConntrackTableList(table, family)
+}
+
+// ConntrackTableFlush flushes all the flows of a specified table
+// conntrack -F [table]            Flush table
+// The flush operation applies to all the family types
+func ConntrackTableFlush(table ConntrackTableType) error {
+       return pkgHandle.ConntrackTableFlush(table)
+}
+
+// ConntrackDeleteFilter deletes entries on the specified table on the base of the filter
+// conntrack -D [table] parameters         Delete conntrack or expectation
+func ConntrackDeleteFilter(table ConntrackTableType, family InetFamily, filter CustomConntrackFilter) (uint, error) {
+       return pkgHandle.ConntrackDeleteFilter(table, family, filter)
+}
+
+// ConntrackTableList returns the flow list of a table of a specific family using the netlink handle passed
+// conntrack -L [table] [options]          List conntrack or expectation table
+func (h *Handle) ConntrackTableList(table ConntrackTableType, family InetFamily) ([]*ConntrackFlow, error) {
+       res, err := h.dumpConntrackTable(table, family)
+       if err != nil {
+               return nil, err
+       }
+
+       // Deserialize all the flows
+       var result []*ConntrackFlow
+       for _, dataRaw := range res {
+               result = append(result, parseRawData(dataRaw))
+       }
+
+       return result, nil
+}
+
+// ConntrackTableFlush flushes all the flows of a specified table using the netlink handle passed
+// conntrack -F [table]            Flush table
+// The flush operation applies to all the family types
+func (h *Handle) ConntrackTableFlush(table ConntrackTableType) error {
+       req := h.newConntrackRequest(table, syscall.AF_INET, nl.IPCTNL_MSG_CT_DELETE, syscall.NLM_F_ACK)
+       _, err := req.Execute(syscall.NETLINK_NETFILTER, 0)
+       return err
+}
+
+// ConntrackDeleteFilter deletes entries on the specified table on the base of the filter using the netlink handle passed
+// conntrack -D [table] parameters         Delete conntrack or expectation
+func (h *Handle) ConntrackDeleteFilter(table ConntrackTableType, family InetFamily, filter CustomConntrackFilter) (uint, error) {
+       res, err := h.dumpConntrackTable(table, family)
+       if err != nil {
+               return 0, err
+       }
+
+       var matched uint
+       for _, dataRaw := range res {
+               flow := parseRawData(dataRaw)
+               if match := filter.MatchConntrackFlow(flow); match {
+                       req2 := h.newConntrackRequest(table, family, nl.IPCTNL_MSG_CT_DELETE, syscall.NLM_F_ACK)
+                       // skip the first 4 byte that are the netfilter header, the newConntrackRequest is adding it already
+                       req2.AddRawData(dataRaw[4:])
+                       req2.Execute(syscall.NETLINK_NETFILTER, 0)
+                       matched++
+               }
+       }
+
+       return matched, nil
+}
+
+func (h *Handle) newConntrackRequest(table ConntrackTableType, family InetFamily, operation, flags int) *nl.NetlinkRequest {
+       // Create the Netlink request object
+       req := h.newNetlinkRequest((int(table)<<8)|operation, flags)
+       // Add the netfilter header
+       msg := &nl.Nfgenmsg{
+               NfgenFamily: uint8(family),
+               Version:     nl.NFNETLINK_V0,
+               ResId:       0,
+       }
+       req.AddData(msg)
+       return req
+}
+
+func (h *Handle) dumpConntrackTable(table ConntrackTableType, family InetFamily) ([][]byte, error) {
+       req := h.newConntrackRequest(table, family, nl.IPCTNL_MSG_CT_GET, syscall.NLM_F_DUMP)
+       return req.Execute(syscall.NETLINK_NETFILTER, 0)
+}
+
+// The full conntrack flow structure is very complicated and can be found in the file:
+// http://git.netfilter.org/libnetfilter_conntrack/tree/include/internal/object.h
+// For the time being, the structure below allows to parse and extract the base information of a flow
+type ipTuple struct {
+       SrcIP    net.IP
+       DstIP    net.IP
+       Protocol uint8
+       SrcPort  uint16
+       DstPort  uint16
+}
+
+type ConntrackFlow struct {
+       FamilyType uint8
+       Forward    ipTuple
+       Reverse    ipTuple
+}
+
+func (s *ConntrackFlow) String() string {
+       // conntrack cmd output:
+       // udp      17 src=127.0.0.1 dst=127.0.0.1 sport=4001 dport=1234 [UNREPLIED] src=127.0.0.1 dst=127.0.0.1 sport=1234 dport=4001
+       return fmt.Sprintf("%s\t%d src=%s dst=%s sport=%d dport=%d\tsrc=%s dst=%s sport=%d dport=%d",
+               nl.L4ProtoMap[s.Forward.Protocol], s.Forward.Protocol,
+               s.Forward.SrcIP.String(), s.Forward.DstIP.String(), s.Forward.SrcPort, s.Forward.DstPort,
+               s.Reverse.SrcIP.String(), s.Reverse.DstIP.String(), s.Reverse.SrcPort, s.Reverse.DstPort)
+}
+
+// This method parse the ip tuple structure
+// The message structure is the following:
+// <len, [CTA_IP_V4_SRC|CTA_IP_V6_SRC], 16 bytes for the IP>
+// <len, [CTA_IP_V4_DST|CTA_IP_V6_DST], 16 bytes for the IP>
+// <len, NLA_F_NESTED|nl.CTA_TUPLE_PROTO, 1 byte for the protocol, 3 bytes of padding>
+// <len, CTA_PROTO_SRC_PORT, 2 bytes for the source port, 2 bytes of padding>
+// <len, CTA_PROTO_DST_PORT, 2 bytes for the source port, 2 bytes of padding>
+func parseIpTuple(reader *bytes.Reader, tpl *ipTuple) {
+       for i := 0; i < 2; i++ {
+               _, t, _, v := parseNfAttrTLV(reader)
+               switch t {
+               case nl.CTA_IP_V4_SRC, nl.CTA_IP_V6_SRC:
+                       tpl.SrcIP = v
+               case nl.CTA_IP_V4_DST, nl.CTA_IP_V6_DST:
+                       tpl.DstIP = v
+               }
+       }
+       // Skip the next 4 bytes  nl.NLA_F_NESTED|nl.CTA_TUPLE_PROTO
+       reader.Seek(4, seekCurrent)
+       _, t, _, v := parseNfAttrTLV(reader)
+       if t == nl.CTA_PROTO_NUM {
+               tpl.Protocol = uint8(v[0])
+       }
+       // Skip some padding 3 bytes
+       reader.Seek(3, seekCurrent)
+       for i := 0; i < 2; i++ {
+               _, t, _ := parseNfAttrTL(reader)
+               switch t {
+               case nl.CTA_PROTO_SRC_PORT:
+                       parseBERaw16(reader, &tpl.SrcPort)
+               case nl.CTA_PROTO_DST_PORT:
+                       parseBERaw16(reader, &tpl.DstPort)
+               }
+               // Skip some padding 2 byte
+               reader.Seek(2, seekCurrent)
+       }
+}
+
+func parseNfAttrTLV(r *bytes.Reader) (isNested bool, attrType, len uint16, value []byte) {
+       isNested, attrType, len = parseNfAttrTL(r)
+
+       value = make([]byte, len)
+       binary.Read(r, binary.BigEndian, &value)
+       return isNested, attrType, len, value
+}
+
+func parseNfAttrTL(r *bytes.Reader) (isNested bool, attrType, len uint16) {
+       binary.Read(r, nl.NativeEndian(), &len)
+       len -= nl.SizeofNfattr
+
+       binary.Read(r, nl.NativeEndian(), &attrType)
+       isNested = (attrType & nl.NLA_F_NESTED) == nl.NLA_F_NESTED
+       attrType = attrType & (nl.NLA_F_NESTED - 1)
+
+       return isNested, attrType, len
+}
+
+func parseBERaw16(r *bytes.Reader, v *uint16) {
+       binary.Read(r, binary.BigEndian, v)
+}
+
+func parseRawData(data []byte) *ConntrackFlow {
+       s := &ConntrackFlow{}
+       // First there is the Nfgenmsg header
+       // consume only the family field
+       reader := bytes.NewReader(data)
+       binary.Read(reader, nl.NativeEndian(), &s.FamilyType)
+
+       // skip rest of the Netfilter header
+       reader.Seek(3, seekCurrent)
+       // The message structure is the following:
+       // <len, NLA_F_NESTED|CTA_TUPLE_ORIG> 4 bytes
+       // <len, NLA_F_NESTED|CTA_TUPLE_IP> 4 bytes
+       // flow information of the forward flow
+       // <len, NLA_F_NESTED|CTA_TUPLE_REPLY> 4 bytes
+       // <len, NLA_F_NESTED|CTA_TUPLE_IP> 4 bytes
+       // flow information of the reverse flow
+       for reader.Len() > 0 {
+               nested, t, l := parseNfAttrTL(reader)
+               if nested && t == nl.CTA_TUPLE_ORIG {
+                       if nested, t, _ = parseNfAttrTL(reader); nested && t == nl.CTA_TUPLE_IP {
+                               parseIpTuple(reader, &s.Forward)
+                       }
+               } else if nested && t == nl.CTA_TUPLE_REPLY {
+                       if nested, t, _ = parseNfAttrTL(reader); nested && t == nl.CTA_TUPLE_IP {
+                               parseIpTuple(reader, &s.Reverse)
+
+                               // Got all the useful information stop parsing
+                               break
+                       } else {
+                               // Header not recognized skip it
+                               reader.Seek(int64(l), seekCurrent)
+                       }
+               }
+       }
+
+       return s
+}
+
+// Conntrack parameters and options:
+//   -n, --src-nat ip                      source NAT ip
+//   -g, --dst-nat ip                      destination NAT ip
+//   -j, --any-nat ip                      source or destination NAT ip
+//   -m, --mark mark                       Set mark
+//   -c, --secmark secmark                 Set selinux secmark
+//   -e, --event-mask eventmask            Event mask, eg. NEW,DESTROY
+//   -z, --zero                            Zero counters while listing
+//   -o, --output type[,...]               Output format, eg. xml
+//   -l, --label label[,...]               conntrack labels
+
+// Common parameters and options:
+//   -s, --src, --orig-src ip              Source address from original direction
+//   -d, --dst, --orig-dst ip              Destination address from original direction
+//   -r, --reply-src ip            Source addres from reply direction
+//   -q, --reply-dst ip            Destination address from reply direction
+//   -p, --protonum proto          Layer 4 Protocol, eg. 'tcp'
+//   -f, --family proto            Layer 3 Protocol, eg. 'ipv6'
+//   -t, --timeout timeout         Set timeout
+//   -u, --status status           Set status, eg. ASSURED
+//   -w, --zone value              Set conntrack zone
+//   --orig-zone value             Set zone for original direction
+//   --reply-zone value            Set zone for reply direction
+//   -b, --buffer-size             Netlink socket buffer size
+//   --mask-src ip                 Source mask address
+//   --mask-dst ip                 Destination mask address
+
+// Filter types
+type ConntrackFilterType uint8
+
+const (
+       ConntrackOrigSrcIP = iota // -orig-src ip   Source address from original direction
+       ConntrackOrigDstIP        // -orig-dst ip   Destination address from original direction
+       ConntrackNatSrcIP         // -src-nat ip    Source NAT ip
+       ConntrackNatDstIP         // -dst-nat ip    Destination NAT ip
+       ConntrackNatAnyIP         // -any-nat ip    Source or destination NAT ip
+)
+
+type CustomConntrackFilter interface {
+       // MatchConntrackFlow applies the filter to the flow and returns true if the flow matches
+       // the filter or false otherwise
+       MatchConntrackFlow(flow *ConntrackFlow) bool
+}
+
+type ConntrackFilter struct {
+       ipFilter map[ConntrackFilterType]net.IP
+}
+
+// AddIP adds an IP to the conntrack filter
+func (f *ConntrackFilter) AddIP(tp ConntrackFilterType, ip net.IP) error {
+       if f.ipFilter == nil {
+               f.ipFilter = make(map[ConntrackFilterType]net.IP)
+       }
+       if _, ok := f.ipFilter[tp]; ok {
+               return errors.New("Filter attribute already present")
+       }
+       f.ipFilter[tp] = ip
+       return nil
+}
+
+// MatchConntrackFlow applies the filter to the flow and returns true if the flow matches the filter
+// false otherwise
+func (f *ConntrackFilter) MatchConntrackFlow(flow *ConntrackFlow) bool {
+       if len(f.ipFilter) == 0 {
+               // empty filter always not match
+               return false
+       }
+
+       match := true
+       // -orig-src ip   Source address from original direction
+       if elem, found := f.ipFilter[ConntrackOrigSrcIP]; found {
+               match = match && elem.Equal(flow.Forward.SrcIP)
+       }
+
+       // -orig-dst ip   Destination address from original direction
+       if elem, found := f.ipFilter[ConntrackOrigDstIP]; match && found {
+               match = match && elem.Equal(flow.Forward.DstIP)
+       }
+
+       // -src-nat ip    Source NAT ip
+       if elem, found := f.ipFilter[ConntrackNatSrcIP]; match && found {
+               match = match && elem.Equal(flow.Reverse.SrcIP)
+       }
+
+       // -dst-nat ip    Destination NAT ip
+       if elem, found := f.ipFilter[ConntrackNatDstIP]; match && found {
+               match = match && elem.Equal(flow.Reverse.DstIP)
+       }
+
+       // -any-nat ip    Source or destination NAT ip
+       if elem, found := f.ipFilter[ConntrackNatAnyIP]; match && found {
+               match = match && (elem.Equal(flow.Reverse.SrcIP) || elem.Equal(flow.Reverse.DstIP))
+       }
+
+       return match
+}
+
+var _ CustomConntrackFilter = (*ConntrackFilter)(nil)
diff --git a/vendor/github.com/vishvananda/netlink/conntrack_unspecified.go b/vendor/github.com/vishvananda/netlink/conntrack_unspecified.go
new file mode 100644 (file)
index 0000000..af7af79
--- /dev/null
@@ -0,0 +1,53 @@
+// +build !linux
+
+package netlink
+
+// ConntrackTableType Conntrack table for the netlink operation
+type ConntrackTableType uint8
+
+// InetFamily Family type
+type InetFamily uint8
+
+// ConntrackFlow placeholder
+type ConntrackFlow struct{}
+
+// ConntrackFilter placeholder
+type ConntrackFilter struct{}
+
+// ConntrackTableList returns the flow list of a table of a specific family
+// conntrack -L [table] [options]          List conntrack or expectation table
+func ConntrackTableList(table ConntrackTableType, family InetFamily) ([]*ConntrackFlow, error) {
+       return nil, ErrNotImplemented
+}
+
+// ConntrackTableFlush flushes all the flows of a specified table
+// conntrack -F [table]            Flush table
+// The flush operation applies to all the family types
+func ConntrackTableFlush(table ConntrackTableType) error {
+       return ErrNotImplemented
+}
+
+// ConntrackDeleteFilter deletes entries on the specified table on the base of the filter
+// conntrack -D [table] parameters         Delete conntrack or expectation
+func ConntrackDeleteFilter(table ConntrackTableType, family InetFamily, filter *ConntrackFilter) (uint, error) {
+       return 0, ErrNotImplemented
+}
+
+// ConntrackTableList returns the flow list of a table of a specific family using the netlink handle passed
+// conntrack -L [table] [options]          List conntrack or expectation table
+func (h *Handle) ConntrackTableList(table ConntrackTableType, family InetFamily) ([]*ConntrackFlow, error) {
+       return nil, ErrNotImplemented
+}
+
+// ConntrackTableFlush flushes all the flows of a specified table using the netlink handle passed
+// conntrack -F [table]            Flush table
+// The flush operation applies to all the family types
+func (h *Handle) ConntrackTableFlush(table ConntrackTableType) error {
+       return ErrNotImplemented
+}
+
+// ConntrackDeleteFilter deletes entries on the specified table on the base of the filter using the netlink handle passed
+// conntrack -D [table] parameters         Delete conntrack or expectation
+func (h *Handle) ConntrackDeleteFilter(table ConntrackTableType, family InetFamily, filter *ConntrackFilter) (uint, error) {
+       return 0, ErrNotImplemented
+}
index bc8a1e9..938b28b 100644 (file)
@@ -1,6 +1,10 @@
 package netlink
 
-import "fmt"
+import (
+       "fmt"
+
+       "github.com/vishvananda/netlink/nl"
+)
 
 type Filter interface {
        Attrs() *FilterAttrs
@@ -180,11 +184,46 @@ func NewMirredAction(redirIndex int) *MirredAction {
        }
 }
 
+// Constants used in TcU32Sel.Flags.
+const (
+       TC_U32_TERMINAL  = nl.TC_U32_TERMINAL
+       TC_U32_OFFSET    = nl.TC_U32_OFFSET
+       TC_U32_VAROFFSET = nl.TC_U32_VAROFFSET
+       TC_U32_EAT       = nl.TC_U32_EAT
+)
+
+// Sel of the U32 filters that contains multiple TcU32Key. This is the copy
+// and the frontend representation of nl.TcU32Sel. It is serialized into canonical
+// nl.TcU32Sel with the appropriate endianness.
+type TcU32Sel struct {
+       Flags    uint8
+       Offshift uint8
+       Nkeys    uint8
+       Pad      uint8
+       Offmask  uint16
+       Off      uint16
+       Offoff   int16
+       Hoff     int16
+       Hmask    uint32
+       Keys     []TcU32Key
+}
+
+// TcU32Key contained of Sel in the U32 filters. This is the copy and the frontend
+// representation of nl.TcU32Key. It is serialized into chanonical nl.TcU32Sel
+// with the appropriate endianness.
+type TcU32Key struct {
+       Mask    uint32
+       Val     uint32
+       Off     int32
+       OffMask int32
+}
+
 // U32 filters on many packet related properties
 type U32 struct {
        FilterAttrs
        ClassId    uint32
        RedirIndex int
+       Sel        *TcU32Sel
        Actions    []Action
 }
 
index d9aedca..dc0f90a 100644 (file)
@@ -6,6 +6,7 @@ import (
        "errors"
        "fmt"
        "syscall"
+       "unsafe"
 
        "github.com/vishvananda/netlink/nl"
 )
@@ -128,12 +129,34 @@ func (h *Handle) FilterAdd(filter Filter) error {
 
        options := nl.NewRtAttr(nl.TCA_OPTIONS, nil)
        if u32, ok := filter.(*U32); ok {
-               // match all
-               sel := nl.TcU32Sel{
-                       Nkeys: 1,
-                       Flags: nl.TC_U32_TERMINAL,
+               // Convert TcU32Sel into nl.TcU32Sel as it is without copy.
+               sel := (*nl.TcU32Sel)(unsafe.Pointer(u32.Sel))
+               if sel == nil {
+                       // match all
+                       sel = &nl.TcU32Sel{
+                               Nkeys: 1,
+                               Flags: nl.TC_U32_TERMINAL,
+                       }
+                       sel.Keys = append(sel.Keys, nl.TcU32Key{})
                }
-               sel.Keys = append(sel.Keys, nl.TcU32Key{})
+
+               if native != networkOrder {
+                       // Copy TcU32Sel.
+                       cSel := *sel
+                       keys := make([]nl.TcU32Key, cap(sel.Keys))
+                       copy(keys, sel.Keys)
+                       cSel.Keys = keys
+                       sel = &cSel
+
+                       // Handle the endianness of attributes
+                       sel.Offmask = native.Uint16(htons(sel.Offmask))
+                       sel.Hmask = native.Uint32(htonl(sel.Hmask))
+                       for i, key := range sel.Keys {
+                               sel.Keys[i].Mask = native.Uint32(htonl(key.Mask))
+                               sel.Keys[i].Val = native.Uint32(htonl(key.Val))
+                       }
+               }
+               sel.Nkeys = uint8(len(sel.Keys))
                nl.NewRtAttrChild(options, nl.TCA_U32_SEL, sel.Serialize())
                if u32.ClassId != 0 {
                        nl.NewRtAttrChild(options, nl.TCA_U32_CLASSID, nl.Uint32Attr(u32.ClassId))
@@ -425,9 +448,15 @@ func parseU32Data(filter Filter, data []syscall.NetlinkRouteAttr) (bool, error)
                case nl.TCA_U32_SEL:
                        detailed = true
                        sel := nl.DeserializeTcU32Sel(datum.Value)
-                       // only parse if we have a very basic redirect
-                       if sel.Flags&nl.TC_U32_TERMINAL == 0 || sel.Nkeys != 1 {
-                               return detailed, nil
+                       u32.Sel = (*TcU32Sel)(unsafe.Pointer(sel))
+                       if native != networkOrder {
+                               // Handle the endianness of attributes
+                               u32.Sel.Offmask = native.Uint16(htons(sel.Offmask))
+                               u32.Sel.Hmask = native.Uint32(htonl(sel.Hmask))
+                               for i, key := range u32.Sel.Keys {
+                                       u32.Sel.Keys[i].Mask = native.Uint32(htonl(key.Mask))
+                                       u32.Sel.Keys[i].Val = native.Uint32(htonl(key.Val))
+                               }
                        }
                case nl.TCA_U32_ACT:
                        tables, err := nl.ParseRouteAttr(datum.Value)
@@ -443,6 +472,8 @@ func parseU32Data(filter Filter, data []syscall.NetlinkRouteAttr) (bool, error)
                                        u32.RedirIndex = int(action.Ifindex)
                                }
                        }
+               case nl.TCA_U32_CLASSID:
+                       u32.ClassId = native.Uint32(datum.Value)
                }
        }
        return detailed, nil
diff --git a/vendor/github.com/vishvananda/netlink/genetlink_linux.go b/vendor/github.com/vishvananda/netlink/genetlink_linux.go
new file mode 100644 (file)
index 0000000..a388a87
--- /dev/null
@@ -0,0 +1,167 @@
+package netlink
+
+import (
+       "fmt"
+       "syscall"
+
+       "github.com/vishvananda/netlink/nl"
+)
+
+type GenlOp struct {
+       ID    uint32
+       Flags uint32
+}
+
+type GenlMulticastGroup struct {
+       ID   uint32
+       Name string
+}
+
+type GenlFamily struct {
+       ID      uint16
+       HdrSize uint32
+       Name    string
+       Version uint32
+       MaxAttr uint32
+       Ops     []GenlOp
+       Groups  []GenlMulticastGroup
+}
+
+func parseOps(b []byte) ([]GenlOp, error) {
+       attrs, err := nl.ParseRouteAttr(b)
+       if err != nil {
+               return nil, err
+       }
+       ops := make([]GenlOp, 0, len(attrs))
+       for _, a := range attrs {
+               nattrs, err := nl.ParseRouteAttr(a.Value)
+               if err != nil {
+                       return nil, err
+               }
+               var op GenlOp
+               for _, na := range nattrs {
+                       switch na.Attr.Type {
+                       case nl.GENL_CTRL_ATTR_OP_ID:
+                               op.ID = native.Uint32(na.Value)
+                       case nl.GENL_CTRL_ATTR_OP_FLAGS:
+                               op.Flags = native.Uint32(na.Value)
+                       }
+               }
+               ops = append(ops, op)
+       }
+       return ops, nil
+}
+
+func parseMulticastGroups(b []byte) ([]GenlMulticastGroup, error) {
+       attrs, err := nl.ParseRouteAttr(b)
+       if err != nil {
+               return nil, err
+       }
+       groups := make([]GenlMulticastGroup, 0, len(attrs))
+       for _, a := range attrs {
+               nattrs, err := nl.ParseRouteAttr(a.Value)
+               if err != nil {
+                       return nil, err
+               }
+               var g GenlMulticastGroup
+               for _, na := range nattrs {
+                       switch na.Attr.Type {
+                       case nl.GENL_CTRL_ATTR_MCAST_GRP_NAME:
+                               g.Name = nl.BytesToString(na.Value)
+                       case nl.GENL_CTRL_ATTR_MCAST_GRP_ID:
+                               g.ID = native.Uint32(na.Value)
+                       }
+               }
+               groups = append(groups, g)
+       }
+       return groups, nil
+}
+
+func (f *GenlFamily) parseAttributes(attrs []syscall.NetlinkRouteAttr) error {
+       for _, a := range attrs {
+               switch a.Attr.Type {
+               case nl.GENL_CTRL_ATTR_FAMILY_NAME:
+                       f.Name = nl.BytesToString(a.Value)
+               case nl.GENL_CTRL_ATTR_FAMILY_ID:
+                       f.ID = native.Uint16(a.Value)
+               case nl.GENL_CTRL_ATTR_VERSION:
+                       f.Version = native.Uint32(a.Value)
+               case nl.GENL_CTRL_ATTR_HDRSIZE:
+                       f.HdrSize = native.Uint32(a.Value)
+               case nl.GENL_CTRL_ATTR_MAXATTR:
+                       f.MaxAttr = native.Uint32(a.Value)
+               case nl.GENL_CTRL_ATTR_OPS:
+                       ops, err := parseOps(a.Value)
+                       if err != nil {
+                               return err
+                       }
+                       f.Ops = ops
+               case nl.GENL_CTRL_ATTR_MCAST_GROUPS:
+                       groups, err := parseMulticastGroups(a.Value)
+                       if err != nil {
+                               return err
+                       }
+                       f.Groups = groups
+               }
+       }
+
+       return nil
+}
+
+func parseFamilies(msgs [][]byte) ([]*GenlFamily, error) {
+       families := make([]*GenlFamily, 0, len(msgs))
+       for _, m := range msgs {
+               attrs, err := nl.ParseRouteAttr(m[nl.SizeofGenlmsg:])
+               if err != nil {
+                       return nil, err
+               }
+               family := &GenlFamily{}
+               if err := family.parseAttributes(attrs); err != nil {
+                       return nil, err
+               }
+
+               families = append(families, family)
+       }
+       return families, nil
+}
+
+func (h *Handle) GenlFamilyList() ([]*GenlFamily, error) {
+       msg := &nl.Genlmsg{
+               Command: nl.GENL_CTRL_CMD_GETFAMILY,
+               Version: nl.GENL_CTRL_VERSION,
+       }
+       req := h.newNetlinkRequest(nl.GENL_ID_CTRL, syscall.NLM_F_DUMP)
+       req.AddData(msg)
+       msgs, err := req.Execute(syscall.NETLINK_GENERIC, 0)
+       if err != nil {
+               return nil, err
+       }
+       return parseFamilies(msgs)
+}
+
+func GenlFamilyList() ([]*GenlFamily, error) {
+       return pkgHandle.GenlFamilyList()
+}
+
+func (h *Handle) GenlFamilyGet(name string) (*GenlFamily, error) {
+       msg := &nl.Genlmsg{
+               Command: nl.GENL_CTRL_CMD_GETFAMILY,
+               Version: nl.GENL_CTRL_VERSION,
+       }
+       req := h.newNetlinkRequest(nl.GENL_ID_CTRL, 0)
+       req.AddData(msg)
+       req.AddData(nl.NewRtAttr(nl.GENL_CTRL_ATTR_FAMILY_NAME, nl.ZeroTerminated(name)))
+       msgs, err := req.Execute(syscall.NETLINK_GENERIC, 0)
+       if err != nil {
+               return nil, err
+       }
+       families, err := parseFamilies(msgs)
+       if len(families) != 1 {
+               return nil, fmt.Errorf("invalid response for GENL_CTRL_CMD_GETFAMILY")
+       }
+       return families[0], nil
+}
+
+func GenlFamilyGet(name string) (*GenlFamily, error) {
+       return pkgHandle.GenlFamilyGet(name)
+}
diff --git a/vendor/github.com/vishvananda/netlink/genetlink_unspecified.go b/vendor/github.com/vishvananda/netlink/genetlink_unspecified.go
new file mode 100644 (file)
index 0000000..0192b99
--- /dev/null
@@ -0,0 +1,25 @@
+// +build !linux
+
+package netlink
+
+type GenlOp struct{}
+
+type GenlMulticastGroup struct{}
+
+type GenlFamily struct{}
+
+func (h *Handle) GenlFamilyList() ([]*GenlFamily, error) {
+       return nil, ErrNotImplemented
+}
+
+func GenlFamilyList() ([]*GenlFamily, error) {
+       return nil, ErrNotImplemented
+}
+
+func (h *Handle) GenlFamilyGet(name string) (*GenlFamily, error) {
+       return nil, ErrNotImplemented
+}
+
+func GenlFamilyGet(name string) (*GenlFamily, error) {
+       return nil, ErrNotImplemented
+}
diff --git a/vendor/github.com/vishvananda/netlink/gtp_linux.go b/vendor/github.com/vishvananda/netlink/gtp_linux.go
new file mode 100644 (file)
index 0000000..7331303
--- /dev/null
@@ -0,0 +1,238 @@
+package netlink
+
+import (
+       "fmt"
+       "net"
+       "strings"
+       "syscall"
+
+       "github.com/vishvananda/netlink/nl"
+)
+
+type PDP struct {
+       Version     uint32
+       TID         uint64
+       PeerAddress net.IP
+       MSAddress   net.IP
+       Flow        uint16
+       NetNSFD     uint32
+       ITEI        uint32
+       OTEI        uint32
+}
+
+func (pdp *PDP) String() string {
+       elems := []string{}
+       elems = append(elems, fmt.Sprintf("Version: %d", pdp.Version))
+       if pdp.Version == 0 {
+               elems = append(elems, fmt.Sprintf("TID: %d", pdp.TID))
+       } else if pdp.Version == 1 {
+               elems = append(elems, fmt.Sprintf("TEI: %d/%d", pdp.ITEI, pdp.OTEI))
+       }
+       elems = append(elems, fmt.Sprintf("MS-Address: %s", pdp.MSAddress))
+       elems = append(elems, fmt.Sprintf("Peer-Address: %s", pdp.PeerAddress))
+       return fmt.Sprintf("{%s}", strings.Join(elems, " "))
+}
+
+func (p *PDP) parseAttributes(attrs []syscall.NetlinkRouteAttr) error {
+       for _, a := range attrs {
+               switch a.Attr.Type {
+               case nl.GENL_GTP_ATTR_VERSION:
+                       p.Version = native.Uint32(a.Value)
+               case nl.GENL_GTP_ATTR_TID:
+                       p.TID = native.Uint64(a.Value)
+               case nl.GENL_GTP_ATTR_PEER_ADDRESS:
+                       p.PeerAddress = net.IP(a.Value)
+               case nl.GENL_GTP_ATTR_MS_ADDRESS:
+                       p.MSAddress = net.IP(a.Value)
+               case nl.GENL_GTP_ATTR_FLOW:
+                       p.Flow = native.Uint16(a.Value)
+               case nl.GENL_GTP_ATTR_NET_NS_FD:
+                       p.NetNSFD = native.Uint32(a.Value)
+               case nl.GENL_GTP_ATTR_I_TEI:
+                       p.ITEI = native.Uint32(a.Value)
+               case nl.GENL_GTP_ATTR_O_TEI:
+                       p.OTEI = native.Uint32(a.Value)
+               }
+       }
+       return nil
+}
+
+func parsePDP(msgs [][]byte) ([]*PDP, error) {
+       pdps := make([]*PDP, 0, len(msgs))
+       for _, m := range msgs {
+               attrs, err := nl.ParseRouteAttr(m[nl.SizeofGenlmsg:])
+               if err != nil {
+                       return nil, err
+               }
+               pdp := &PDP{}
+               if err := pdp.parseAttributes(attrs); err != nil {
+                       return nil, err
+               }
+               pdps = append(pdps, pdp)
+       }
+       return pdps, nil
+}
+
+func (h *Handle) GTPPDPList() ([]*PDP, error) {
+       f, err := h.GenlFamilyGet(nl.GENL_GTP_NAME)
+       if err != nil {
+               return nil, err
+       }
+       msg := &nl.Genlmsg{
+               Command: nl.GENL_GTP_CMD_GETPDP,
+               Version: nl.GENL_GTP_VERSION,
+       }
+       req := h.newNetlinkRequest(int(f.ID), syscall.NLM_F_DUMP)
+       req.AddData(msg)
+       msgs, err := req.Execute(syscall.NETLINK_GENERIC, 0)
+       if err != nil {
+               return nil, err
+       }
+       return parsePDP(msgs)
+}
+
+func GTPPDPList() ([]*PDP, error) {
+       return pkgHandle.GTPPDPList()
+}
+
+func gtpPDPGet(req *nl.NetlinkRequest) (*PDP, error) {
+       msgs, err := req.Execute(syscall.NETLINK_GENERIC, 0)
+       if err != nil {
+               return nil, err
+       }
+       pdps, err := parsePDP(msgs)
+       if err != nil {
+               return nil, err
+       }
+       if len(pdps) != 1 {
+               return nil, fmt.Errorf("invalid reqponse for GENL_GTP_CMD_GETPDP")
+       }
+       return pdps[0], nil
+}
+
+func (h *Handle) GTPPDPByTID(link Link, tid int) (*PDP, error) {
+       f, err := h.GenlFamilyGet(nl.GENL_GTP_NAME)
+       if err != nil {
+               return nil, err
+       }
+       msg := &nl.Genlmsg{
+               Command: nl.GENL_GTP_CMD_GETPDP,
+               Version: nl.GENL_GTP_VERSION,
+       }
+       req := h.newNetlinkRequest(int(f.ID), 0)
+       req.AddData(msg)
+       req.AddData(nl.NewRtAttr(nl.GENL_GTP_ATTR_VERSION, nl.Uint32Attr(0)))
+       req.AddData(nl.NewRtAttr(nl.GENL_GTP_ATTR_LINK, nl.Uint32Attr(uint32(link.Attrs().Index))))
+       req.AddData(nl.NewRtAttr(nl.GENL_GTP_ATTR_TID, nl.Uint64Attr(uint64(tid))))
+       return gtpPDPGet(req)
+}
+
+func GTPPDPByTID(link Link, tid int) (*PDP, error) {
+       return pkgHandle.GTPPDPByTID(link, tid)
+}
+
+func (h *Handle) GTPPDPByITEI(link Link, itei int) (*PDP, error) {
+       f, err := h.GenlFamilyGet(nl.GENL_GTP_NAME)
+       if err != nil {
+               return nil, err
+       }
+       msg := &nl.Genlmsg{
+               Command: nl.GENL_GTP_CMD_GETPDP,
+               Version: nl.GENL_GTP_VERSION,
+       }
+       req := h.newNetlinkRequest(int(f.ID), 0)
+       req.AddData(msg)
+       req.AddData(nl.NewRtAttr(nl.GENL_GTP_ATTR_VERSION, nl.Uint32Attr(1)))
+       req.AddData(nl.NewRtAttr(nl.GENL_GTP_ATTR_LINK, nl.Uint32Attr(uint32(link.Attrs().Index))))
+       req.AddData(nl.NewRtAttr(nl.GENL_GTP_ATTR_I_TEI, nl.Uint32Attr(uint32(itei))))
+       return gtpPDPGet(req)
+}
+
+func GTPPDPByITEI(link Link, itei int) (*PDP, error) {
+       return pkgHandle.GTPPDPByITEI(link, itei)
+}
+
+func (h *Handle) GTPPDPByMSAddress(link Link, addr net.IP) (*PDP, error) {
+       f, err := h.GenlFamilyGet(nl.GENL_GTP_NAME)
+       if err != nil {
+               return nil, err
+       }
+       msg := &nl.Genlmsg{
+               Command: nl.GENL_GTP_CMD_GETPDP,
+               Version: nl.GENL_GTP_VERSION,
+       }
+       req := h.newNetlinkRequest(int(f.ID), 0)
+       req.AddData(msg)
+       req.AddData(nl.NewRtAttr(nl.GENL_GTP_ATTR_VERSION, nl.Uint32Attr(0)))
+       req.AddData(nl.NewRtAttr(nl.GENL_GTP_ATTR_LINK, nl.Uint32Attr(uint32(link.Attrs().Index))))
+       req.AddData(nl.NewRtAttr(nl.GENL_GTP_ATTR_MS_ADDRESS, []byte(addr.To4())))
+       return gtpPDPGet(req)
+}
+
+func GTPPDPByMSAddress(link Link, addr net.IP) (*PDP, error) {
+       return pkgHandle.GTPPDPByMSAddress(link, addr)
+}
+
+func (h *Handle) GTPPDPAdd(link Link, pdp *PDP) error {
+       f, err := h.GenlFamilyGet(nl.GENL_GTP_NAME)
+       if err != nil {
+               return err
+       }
+       msg := &nl.Genlmsg{
+               Command: nl.GENL_GTP_CMD_NEWPDP,
+               Version: nl.GENL_GTP_VERSION,
+       }
+       req := h.newNetlinkRequest(int(f.ID), syscall.NLM_F_EXCL|syscall.NLM_F_ACK)
+       req.AddData(msg)
+       req.AddData(nl.NewRtAttr(nl.GENL_GTP_ATTR_VERSION, nl.Uint32Attr(pdp.Version)))
+       req.AddData(nl.NewRtAttr(nl.GENL_GTP_ATTR_LINK, nl.Uint32Attr(uint32(link.Attrs().Index))))
+       req.AddData(nl.NewRtAttr(nl.GENL_GTP_ATTR_PEER_ADDRESS, []byte(pdp.PeerAddress.To4())))
+       req.AddData(nl.NewRtAttr(nl.GENL_GTP_ATTR_MS_ADDRESS, []byte(pdp.MSAddress.To4())))
+
+       switch pdp.Version {
+       case 0:
+               req.AddData(nl.NewRtAttr(nl.GENL_GTP_ATTR_TID, nl.Uint64Attr(pdp.TID)))
+               req.AddData(nl.NewRtAttr(nl.GENL_GTP_ATTR_FLOW, nl.Uint16Attr(pdp.Flow)))
+       case 1:
+               req.AddData(nl.NewRtAttr(nl.GENL_GTP_ATTR_I_TEI, nl.Uint32Attr(pdp.ITEI)))
+               req.AddData(nl.NewRtAttr(nl.GENL_GTP_ATTR_O_TEI, nl.Uint32Attr(pdp.OTEI)))
+       default:
+               return fmt.Errorf("unsupported GTP version: %d", pdp.Version)
+       }
+       _, err = req.Execute(syscall.NETLINK_GENERIC, 0)
+       return err
+}
+
+func GTPPDPAdd(link Link, pdp *PDP) error {
+       return pkgHandle.GTPPDPAdd(link, pdp)
+}
+
+func (h *Handle) GTPPDPDel(link Link, pdp *PDP) error {
+       f, err := h.GenlFamilyGet(nl.GENL_GTP_NAME)
+       if err != nil {
+               return err
+       }
+       msg := &nl.Genlmsg{
+               Command: nl.GENL_GTP_CMD_DELPDP,
+               Version: nl.GENL_GTP_VERSION,
+       }
+       req := h.newNetlinkRequest(int(f.ID), syscall.NLM_F_EXCL|syscall.NLM_F_ACK)
+       req.AddData(msg)
+       req.AddData(nl.NewRtAttr(nl.GENL_GTP_ATTR_VERSION, nl.Uint32Attr(pdp.Version)))
+       req.AddData(nl.NewRtAttr(nl.GENL_GTP_ATTR_LINK, nl.Uint32Attr(uint32(link.Attrs().Index))))
+
+       switch pdp.Version {
+       case 0:
+               req.AddData(nl.NewRtAttr(nl.GENL_GTP_ATTR_TID, nl.Uint64Attr(pdp.TID)))
+       case 1:
+               req.AddData(nl.NewRtAttr(nl.GENL_GTP_ATTR_I_TEI, nl.Uint32Attr(pdp.ITEI)))
+       default:
+               return fmt.Errorf("unsupported GTP version: %d", pdp.Version)
+       }
+       _, err = req.Execute(syscall.NETLINK_GENERIC, 0)
+       return err
+}
+
+func GTPPDPDel(link Link, pdp *PDP) error {
+       return pkgHandle.GTPPDPDel(link, pdp)
+}
index 924211c..9ec4ce2 100644 (file)
@@ -170,6 +170,7 @@ type LinkStatistics64 struct {
 type LinkXdp struct {
        Fd       int
        Attached bool
+       Flags    uint32
 }
 
 // Device links cannot be created via netlink. These links
@@ -215,6 +216,8 @@ func (ifb *Ifb) Type() string {
 // Bridge links are simple linux bridges
 type Bridge struct {
        LinkAttrs
+       MulticastSnooping *bool
+       HelloTime         *uint32
 }
 
 func (bridge *Bridge) Attrs() *LinkAttrs {
@@ -336,6 +339,7 @@ type Vxlan struct {
        UDPCSum      bool
        NoAge        bool
        GBP          bool
+       FlowBased    bool
        Age          int
        Limit        int
        Port         int
@@ -590,7 +594,11 @@ type Bond struct {
        LacpRate        BondLacpRate
        AdSelect        BondAdSelect
        // looking at iproute tool AdInfo can only be retrived. It can't be set.
-       AdInfo *BondAdInfo
+       AdInfo         *BondAdInfo
+       AdActorSysPrio int
+       AdUserPortKey  int
+       AdActorSystem  net.HardwareAddr
+       TlbDynamicLb   int
 }
 
 func NewLinkBond(atr LinkAttrs) *Bond {
@@ -618,6 +626,10 @@ func NewLinkBond(atr LinkAttrs) *Bond {
                PackersPerSlave: -1,
                LacpRate:        -1,
                AdSelect:        -1,
+               AdActorSysPrio:  -1,
+               AdUserPortKey:   -1,
+               AdActorSystem:   nil,
+               TlbDynamicLb:    -1,
        }
 }
 
@@ -673,6 +685,7 @@ type Gretap struct {
        EncapType  uint16
        EncapFlags uint16
        Link       uint32
+       FlowBased  bool
 }
 
 func (gretap *Gretap) Attrs() *LinkAttrs {
@@ -731,8 +744,32 @@ func (vrf *Vrf) Type() string {
        return "vrf"
 }
 
+type GTP struct {
+       LinkAttrs
+       FD0         int
+       FD1         int
+       Role        int
+       PDPHashsize int
+}
+
+func (gtp *GTP) Attrs() *LinkAttrs {
+       return &gtp.LinkAttrs
+}
+
+func (gtp *GTP) Type() string {
+       return "gtp"
+}
+
 // iproute2 supported devices;
 // vlan | veth | vcan | dummy | ifb | macvlan | macvtap |
 // bridge | bond | ipoib | ip6tnl | ipip | sit | vxlan |
 // gre | gretap | ip6gre | ip6gretap | vti | nlmon |
 // bond_slave | ipvlan
+
+// LinkNotFoundError wraps the various not found errors when
+// getting/reading links. This is intended for better error
+// handling by dependent code so that "not found error" can
+// be distinguished from other errors
+type LinkNotFoundError struct {
+       error
+}
index 56409eb..f143144 100644 (file)
@@ -58,6 +58,44 @@ func (h *Handle) ensureIndex(link *LinkAttrs) {
        }
 }
 
+func (h *Handle) LinkSetARPOff(link Link) error {
+       base := link.Attrs()
+       h.ensureIndex(base)
+       req := h.newNetlinkRequest(syscall.RTM_SETLINK, syscall.NLM_F_ACK)
+
+       msg := nl.NewIfInfomsg(syscall.AF_UNSPEC)
+       msg.Change |= syscall.IFF_NOARP
+       msg.Flags |= syscall.IFF_NOARP
+       msg.Index = int32(base.Index)
+       req.AddData(msg)
+
+       _, err := req.Execute(syscall.NETLINK_ROUTE, 0)
+       return err
+}
+
+func LinkSetARPOff(link Link) error {
+       return pkgHandle.LinkSetARPOff(link)
+}
+
+func (h *Handle) LinkSetARPOn(link Link) error {
+       base := link.Attrs()
+       h.ensureIndex(base)
+       req := h.newNetlinkRequest(syscall.RTM_SETLINK, syscall.NLM_F_ACK)
+
+       msg := nl.NewIfInfomsg(syscall.AF_UNSPEC)
+       msg.Change |= syscall.IFF_NOARP
+       msg.Flags &= ^uint32(syscall.IFF_NOARP)
+       msg.Index = int32(base.Index)
+       req.AddData(msg)
+
+       _, err := req.Execute(syscall.NETLINK_ROUTE, 0)
+       return err
+}
+
+func LinkSetARPOn(link Link) error {
+       return pkgHandle.LinkSetARPOn(link)
+}
+
 func (h *Handle) SetPromiscOn(link Link) error {
        base := link.Attrs()
        h.ensureIndex(base)
@@ -65,7 +103,7 @@ func (h *Handle) SetPromiscOn(link Link) error {
 
        msg := nl.NewIfInfomsg(syscall.AF_UNSPEC)
        msg.Change = syscall.IFF_PROMISC
-       msg.Flags = syscall.IFF_UP
+       msg.Flags = syscall.IFF_PROMISC
        msg.Index = int32(base.Index)
        req.AddData(msg)
 
@@ -73,6 +111,16 @@ func (h *Handle) SetPromiscOn(link Link) error {
        return err
 }
 
+func BridgeSetMcastSnoop(link Link, on bool) error {
+       return pkgHandle.BridgeSetMcastSnoop(link, on)
+}
+
+func (h *Handle) BridgeSetMcastSnoop(link Link, on bool) error {
+       bridge := link.(*Bridge)
+       bridge.MulticastSnooping = &on
+       return h.linkModify(bridge, syscall.NLM_F_ACK)
+}
+
 func SetPromiscOn(link Link) error {
        return pkgHandle.SetPromiscOn(link)
 }
@@ -84,7 +132,7 @@ func (h *Handle) SetPromiscOff(link Link) error {
 
        msg := nl.NewIfInfomsg(syscall.AF_UNSPEC)
        msg.Change = syscall.IFF_PROMISC
-       msg.Flags = 0 & ^syscall.IFF_UP
+       msg.Flags = 0 & ^syscall.IFF_PROMISC
        msg.Index = int32(base.Index)
        req.AddData(msg)
 
@@ -480,7 +528,13 @@ type vxlanPortRange struct {
 
 func addVxlanAttrs(vxlan *Vxlan, linkInfo *nl.RtAttr) {
        data := nl.NewRtAttrChild(linkInfo, nl.IFLA_INFO_DATA, nil)
+
+       if vxlan.FlowBased {
+               vxlan.VxlanId = 0
+       }
+
        nl.NewRtAttrChild(data, nl.IFLA_VXLAN_ID, nl.Uint32Attr(uint32(vxlan.VxlanId)))
+
        if vxlan.VtepDevIndex != 0 {
                nl.NewRtAttrChild(data, nl.IFLA_VXLAN_LINK, nl.Uint32Attr(uint32(vxlan.VtepDevIndex)))
        }
@@ -521,6 +575,9 @@ func addVxlanAttrs(vxlan *Vxlan, linkInfo *nl.RtAttr) {
        if vxlan.GBP {
                nl.NewRtAttrChild(data, nl.IFLA_VXLAN_GBP, []byte{})
        }
+       if vxlan.FlowBased {
+               nl.NewRtAttrChild(data, nl.IFLA_VXLAN_FLOWBASED, boolAttr(vxlan.FlowBased))
+       }
        if vxlan.NoAge {
                nl.NewRtAttrChild(data, nl.IFLA_VXLAN_AGEING, nl.Uint32Attr(0))
        } else if vxlan.Age > 0 {
@@ -621,6 +678,18 @@ func addBondAttrs(bond *Bond, linkInfo *nl.RtAttr) {
        if bond.AdSelect >= 0 {
                nl.NewRtAttrChild(data, nl.IFLA_BOND_AD_SELECT, nl.Uint8Attr(uint8(bond.AdSelect)))
        }
+       if bond.AdActorSysPrio >= 0 {
+               nl.NewRtAttrChild(data, nl.IFLA_BOND_AD_ACTOR_SYS_PRIO, nl.Uint16Attr(uint16(bond.AdActorSysPrio)))
+       }
+       if bond.AdUserPortKey >= 0 {
+               nl.NewRtAttrChild(data, nl.IFLA_BOND_AD_USER_PORT_KEY, nl.Uint16Attr(uint16(bond.AdUserPortKey)))
+       }
+       if bond.AdActorSystem != nil {
+               nl.NewRtAttrChild(data, nl.IFLA_BOND_AD_ACTOR_SYSTEM, []byte(bond.AdActorSystem))
+       }
+       if bond.TlbDynamicLb >= 0 {
+               nl.NewRtAttrChild(data, nl.IFLA_BOND_TLB_DYNAMIC_LB, nl.Uint8Attr(uint8(bond.TlbDynamicLb)))
+       }
 }
 
 // LinkAdd adds a new link device. The type and features of the device
@@ -634,7 +703,10 @@ func LinkAdd(link Link) error {
 // are taken fromt the parameters in the link object.
 // Equivalent to: `ip link add $link`
 func (h *Handle) LinkAdd(link Link) error {
-       // TODO: set mtu and hardware address
+       return h.linkModify(link, syscall.NLM_F_CREATE|syscall.NLM_F_EXCL|syscall.NLM_F_ACK)
+}
+
+func (h *Handle) linkModify(link Link, flags int) error {
        // TODO: support extra data for macvlan
        base := link.Attrs()
 
@@ -681,7 +753,7 @@ func (h *Handle) LinkAdd(link Link) error {
                return nil
        }
 
-       req := h.newNetlinkRequest(syscall.RTM_NEWLINK, syscall.NLM_F_CREATE|syscall.NLM_F_EXCL|syscall.NLM_F_ACK)
+       req := h.newNetlinkRequest(syscall.RTM_NEWLINK, flags)
 
        msg := nl.NewIfInfomsg(syscall.AF_UNSPEC)
        // TODO: make it shorter
@@ -729,6 +801,11 @@ func (h *Handle) LinkAdd(link Link) error {
                req.AddData(qlen)
        }
 
+       if base.HardwareAddr != nil {
+               hwaddr := nl.NewRtAttr(syscall.IFLA_ADDRESS, []byte(base.HardwareAddr))
+               req.AddData(hwaddr)
+       }
+
        if base.Namespace != nil {
                var attr *nl.RtAttr
                switch base.Namespace.(type) {
@@ -792,6 +869,10 @@ func (h *Handle) LinkAdd(link Link) error {
                addVtiAttrs(vti, linkInfo)
        } else if vrf, ok := link.(*Vrf); ok {
                addVrfAttrs(vrf, linkInfo)
+       } else if bridge, ok := link.(*Bridge); ok {
+               addBridgeAttrs(bridge, linkInfo)
+       } else if gtp, ok := link.(*GTP); ok {
+               addGTPAttrs(gtp, linkInfo)
        }
 
        req.AddData(linkInfo)
@@ -847,7 +928,7 @@ func (h *Handle) linkByNameDump(name string) (Link, error) {
                        return link, nil
                }
        }
-       return nil, fmt.Errorf("Link %s not found", name)
+       return nil, LinkNotFoundError{fmt.Errorf("Link %s not found", name)}
 }
 
 func (h *Handle) linkByAliasDump(alias string) (Link, error) {
@@ -861,7 +942,7 @@ func (h *Handle) linkByAliasDump(alias string) (Link, error) {
                        return link, nil
                }
        }
-       return nil, fmt.Errorf("Link alias %s not found", alias)
+       return nil, LinkNotFoundError{fmt.Errorf("Link alias %s not found", alias)}
 }
 
 // LinkByName finds a link by name and returns a pointer to the object.
@@ -947,7 +1028,7 @@ func execGetLink(req *nl.NetlinkRequest) (Link, error) {
        if err != nil {
                if errno, ok := err.(syscall.Errno); ok {
                        if errno == syscall.ENODEV {
-                               return nil, fmt.Errorf("Link not found")
+                               return nil, LinkNotFoundError{fmt.Errorf("Link not found")}
                        }
                }
                return nil, err
@@ -955,7 +1036,7 @@ func execGetLink(req *nl.NetlinkRequest) (Link, error) {
 
        switch {
        case len(msgs) == 0:
-               return nil, fmt.Errorf("Link not found")
+               return nil, LinkNotFoundError{fmt.Errorf("Link not found")}
 
        case len(msgs) == 1:
                return LinkDeserialize(nil, msgs[0])
@@ -1025,6 +1106,8 @@ func LinkDeserialize(hdr *syscall.NlMsghdr, m []byte) (Link, error) {
                                                link = &Vti{}
                                        case "vrf":
                                                link = &Vrf{}
+                                       case "gtp":
+                                               link = &GTP{}
                                        default:
                                                link = &GenericLink{LinkType: linkType}
                                        }
@@ -1054,6 +1137,10 @@ func LinkDeserialize(hdr *syscall.NlMsghdr, m []byte) (Link, error) {
                                                parseVtiData(link, data)
                                        case "vrf":
                                                parseVrfData(link, data)
+                                       case "bridge":
+                                               parseBridgeData(link, data)
+                                       case "gtp":
+                                               parseGTPData(link, data)
                                        }
                                }
                        }
@@ -1250,6 +1337,22 @@ func (h *Handle) LinkSetFlood(link Link, mode bool) error {
        return h.setProtinfoAttr(link, mode, nl.IFLA_BRPORT_UNICAST_FLOOD)
 }
 
+func LinkSetBrProxyArp(link Link, mode bool) error {
+       return pkgHandle.LinkSetBrProxyArp(link, mode)
+}
+
+func (h *Handle) LinkSetBrProxyArp(link Link, mode bool) error {
+       return h.setProtinfoAttr(link, mode, nl.IFLA_BRPORT_PROXYARP)
+}
+
+func LinkSetBrProxyArpWiFi(link Link, mode bool) error {
+       return pkgHandle.LinkSetBrProxyArpWiFi(link, mode)
+}
+
+func (h *Handle) LinkSetBrProxyArpWiFi(link Link, mode bool) error {
+       return h.setProtinfoAttr(link, mode, nl.IFLA_BRPORT_PROXYARP_WIFI)
+}
+
 func (h *Handle) setProtinfoAttr(link Link, mode bool, attr int) error {
        base := link.Attrs()
        h.ensureIndex(base)
@@ -1313,6 +1416,8 @@ func parseVxlanData(link Link, data []syscall.NetlinkRouteAttr) {
                        vxlan.UDPCSum = int8(datum.Value[0]) != 0
                case nl.IFLA_VXLAN_GBP:
                        vxlan.GBP = true
+               case nl.IFLA_VXLAN_FLOWBASED:
+                       vxlan.FlowBased = int8(datum.Value[0]) != 0
                case nl.IFLA_VXLAN_AGEING:
                        vxlan.Age = int(native.Uint32(datum.Value[0:4]))
                        vxlan.NoAge = vxlan.Age == 0
@@ -1332,7 +1437,7 @@ func parseVxlanData(link Link, data []syscall.NetlinkRouteAttr) {
 }
 
 func parseBondData(link Link, data []syscall.NetlinkRouteAttr) {
-       bond := NewLinkBond(NewLinkAttrs())
+       bond := link.(*Bond)
        for i := range data {
                switch data[i].Attr.Type {
                case nl.IFLA_BOND_MODE:
@@ -1381,6 +1486,14 @@ func parseBondData(link Link, data []syscall.NetlinkRouteAttr) {
                        bond.AdSelect = BondAdSelect(data[i].Value[0])
                case nl.IFLA_BOND_AD_INFO:
                        // TODO: implement
+               case nl.IFLA_BOND_AD_ACTOR_SYS_PRIO:
+                       bond.AdActorSysPrio = int(native.Uint16(data[i].Value[0:2]))
+               case nl.IFLA_BOND_AD_USER_PORT_KEY:
+                       bond.AdUserPortKey = int(native.Uint16(data[i].Value[0:2]))
+               case nl.IFLA_BOND_AD_ACTOR_SYSTEM:
+                       bond.AdActorSystem = net.HardwareAddr(data[i].Value[0:6])
+               case nl.IFLA_BOND_TLB_DYNAMIC_LB:
+                       bond.TlbDynamicLb = int(data[i].Value[0])
                }
        }
 }
@@ -1445,6 +1558,12 @@ func linkFlags(rawFlags uint32) net.Flags {
 func addGretapAttrs(gretap *Gretap, linkInfo *nl.RtAttr) {
        data := nl.NewRtAttrChild(linkInfo, nl.IFLA_INFO_DATA, nil)
 
+       if gretap.FlowBased {
+               // In flow based mode, no other attributes need to be configured
+               nl.NewRtAttrChild(data, nl.IFLA_GRE_COLLECT_METADATA, boolAttr(gretap.FlowBased))
+               return
+       }
+
        ip := gretap.Local.To4()
        if ip != nil {
                nl.NewRtAttrChild(data, nl.IFLA_GRE_LOCAL, []byte(ip))
@@ -1511,6 +1630,8 @@ func parseGretapData(link Link, data []syscall.NetlinkRouteAttr) {
                        gre.EncapType = native.Uint16(datum.Value[0:2])
                case nl.IFLA_GRE_ENCAP_FLAGS:
                        gre.EncapFlags = native.Uint16(datum.Value[0:2])
+               case nl.IFLA_GRE_COLLECT_METADATA:
+                       gre.FlowBased = int8(datum.Value[0]) != 0
                }
        }
 }
@@ -1528,6 +1649,8 @@ func addXdpAttrs(xdp *LinkXdp, req *nl.NetlinkRequest) {
        b := make([]byte, 4)
        native.PutUint32(b, uint32(xdp.Fd))
        nl.NewRtAttrChild(attrs, nl.IFLA_XDP_FD, b)
+       native.PutUint32(b, xdp.Flags)
+       nl.NewRtAttrChild(attrs, nl.IFLA_XDP_FLAGS, b)
        req.AddData(attrs)
 }
 
@@ -1543,6 +1666,8 @@ func parseLinkXdp(data []byte) (*LinkXdp, error) {
                        xdp.Fd = int(native.Uint32(attr.Value[0:4]))
                case nl.IFLA_XDP_ATTACHED:
                        xdp.Attached = attr.Value[0] != 0
+               case nl.IFLA_XDP_FLAGS:
+                       xdp.Flags = native.Uint32(attr.Value[0:4])
                }
        }
        return xdp, nil
@@ -1640,3 +1765,53 @@ func parseVrfData(link Link, data []syscall.NetlinkRouteAttr) {
                }
        }
 }
+
+func addBridgeAttrs(bridge *Bridge, linkInfo *nl.RtAttr) {
+       data := nl.NewRtAttrChild(linkInfo, nl.IFLA_INFO_DATA, nil)
+       if bridge.MulticastSnooping != nil {
+               nl.NewRtAttrChild(data, nl.IFLA_BR_MCAST_SNOOPING, boolToByte(*bridge.MulticastSnooping))
+       }
+       if bridge.HelloTime != nil {
+               nl.NewRtAttrChild(data, nl.IFLA_BR_HELLO_TIME, nl.Uint32Attr(*bridge.HelloTime))
+       }
+}
+
+func parseBridgeData(bridge Link, data []syscall.NetlinkRouteAttr) {
+       br := bridge.(*Bridge)
+       for _, datum := range data {
+               switch datum.Attr.Type {
+               case nl.IFLA_BR_HELLO_TIME:
+                       helloTime := native.Uint32(datum.Value[0:4])
+                       br.HelloTime = &helloTime
+               case nl.IFLA_BR_MCAST_SNOOPING:
+                       mcastSnooping := datum.Value[0] == 1
+                       br.MulticastSnooping = &mcastSnooping
+               }
+       }
+}
+
+func addGTPAttrs(gtp *GTP, linkInfo *nl.RtAttr) {
+       data := nl.NewRtAttrChild(linkInfo, nl.IFLA_INFO_DATA, nil)
+       nl.NewRtAttrChild(data, nl.IFLA_GTP_FD0, nl.Uint32Attr(uint32(gtp.FD0)))
+       nl.NewRtAttrChild(data, nl.IFLA_GTP_FD1, nl.Uint32Attr(uint32(gtp.FD1)))
+       nl.NewRtAttrChild(data, nl.IFLA_GTP_PDP_HASHSIZE, nl.Uint32Attr(131072))
+       if gtp.Role != nl.GTP_ROLE_GGSN {
+               nl.NewRtAttrChild(data, nl.IFLA_GTP_ROLE, nl.Uint32Attr(uint32(gtp.Role)))
+       }
+}
+
+func parseGTPData(link Link, data []syscall.NetlinkRouteAttr) {
+       gtp := link.(*GTP)
+       for _, datum := range data {
+               switch datum.Attr.Type {
+               case nl.IFLA_GTP_FD0:
+                       gtp.FD0 = int(native.Uint32(datum.Value))
+               case nl.IFLA_GTP_FD1:
+                       gtp.FD1 = int(native.Uint32(datum.Value))
+               case nl.IFLA_GTP_PDP_HASHSIZE:
+                       gtp.PDPHashsize = int(native.Uint32(datum.Value))
+               case nl.IFLA_GTP_ROLE:
+                       gtp.Role = int(native.Uint32(datum.Value))
+               }
+       }
+}
index fa421e4..2d57c16 100644 (file)
@@ -16,7 +16,7 @@ func LinkSetMTU(link Link, mtu int) error {
        return ErrNotImplemented
 }
 
-func LinkSetMaster(link Link, master *Link) error {
+func LinkSetMaster(link Link, master *Bridge) error {
        return ErrNotImplemented
 }
 
@@ -64,6 +64,14 @@ func LinkSetXdpFd(link Link, fd int) error {
        return ErrNotImplemented
 }
 
+func LinkSetARPOff(link Link) error {
+       return ErrNotImplemented
+}
+
+func LinkSetARPOn(link Link) error {
+       return ErrNotImplemented
+}
+
 func LinkByName(name string) (Link, error) {
        return nil, ErrNotImplemented
 }
index 17088fa..fe362e9 100644 (file)
@@ -45,3 +45,32 @@ func (msg *IfAddrmsg) Serialize() []byte {
 func (msg *IfAddrmsg) Len() int {
        return syscall.SizeofIfAddrmsg
 }
+
+// struct ifa_cacheinfo {
+//     __u32   ifa_prefered;
+//     __u32   ifa_valid;
+//     __u32   cstamp; /* created timestamp, hundredths of seconds */
+//     __u32   tstamp; /* updated timestamp, hundredths of seconds */
+// };
+
+const IFA_CACHEINFO = 6
+const SizeofIfaCacheInfo = 0x10
+
+type IfaCacheInfo struct {
+       IfaPrefered uint32
+       IfaValid    uint32
+       Cstamp      uint32
+       Tstamp      uint32
+}
+
+func (msg *IfaCacheInfo) Len() int {
+       return SizeofIfaCacheInfo
+}
+
+func DeserializeIfaCacheInfo(b []byte) *IfaCacheInfo {
+       return (*IfaCacheInfo)(unsafe.Pointer(&b[0:SizeofIfaCacheInfo][0]))
+}
+
+func (msg *IfaCacheInfo) Serialize() []byte {
+       return (*(*[SizeofIfaCacheInfo]byte)(unsafe.Pointer(msg)))[:]
+}
diff --git a/vendor/github.com/vishvananda/netlink/nl/bridge_linux.go b/vendor/github.com/vishvananda/netlink/nl/bridge_linux.go
new file mode 100644 (file)
index 0000000..6c0d333
--- /dev/null
@@ -0,0 +1,74 @@
+package nl
+
+import (
+       "fmt"
+       "unsafe"
+)
+
+const (
+       SizeofBridgeVlanInfo = 0x04
+)
+
+/* Bridge Flags */
+const (
+       BRIDGE_FLAGS_MASTER = iota /* Bridge command to/from master */
+       BRIDGE_FLAGS_SELF          /* Bridge command to/from lowerdev */
+)
+
+/* Bridge management nested attributes
+ * [IFLA_AF_SPEC] = {
+ *     [IFLA_BRIDGE_FLAGS]
+ *     [IFLA_BRIDGE_MODE]
+ *     [IFLA_BRIDGE_VLAN_INFO]
+ * }
+ */
+const (
+       IFLA_BRIDGE_FLAGS = iota
+       IFLA_BRIDGE_MODE
+       IFLA_BRIDGE_VLAN_INFO
+)
+
+const (
+       BRIDGE_VLAN_INFO_MASTER = 1 << iota
+       BRIDGE_VLAN_INFO_PVID
+       BRIDGE_VLAN_INFO_UNTAGGED
+       BRIDGE_VLAN_INFO_RANGE_BEGIN
+       BRIDGE_VLAN_INFO_RANGE_END
+)
+
+// struct bridge_vlan_info {
+//   __u16 flags;
+//   __u16 vid;
+// };
+
+type BridgeVlanInfo struct {
+       Flags uint16
+       Vid   uint16
+}
+
+func (b *BridgeVlanInfo) Serialize() []byte {
+       return (*(*[SizeofBridgeVlanInfo]byte)(unsafe.Pointer(b)))[:]
+}
+
+func DeserializeBridgeVlanInfo(b []byte) *BridgeVlanInfo {
+       return (*BridgeVlanInfo)(unsafe.Pointer(&b[0:SizeofBridgeVlanInfo][0]))
+}
+
+func (b *BridgeVlanInfo) PortVID() bool {
+       return b.Flags&BRIDGE_VLAN_INFO_PVID > 0
+}
+
+func (b *BridgeVlanInfo) EngressUntag() bool {
+       return b.Flags&BRIDGE_VLAN_INFO_UNTAGGED > 0
+}
+
+func (b *BridgeVlanInfo) String() string {
+       return fmt.Sprintf("%+v", *b)
+}
+
+/* New extended info filters for IFLA_EXT_MASK */
+const (
+       RTEXT_FILTER_VF = 1 << iota
+       RTEXT_FILTER_BRVLAN
+       RTEXT_FILTER_BRVLAN_COMPRESSED
+)
diff --git a/vendor/github.com/vishvananda/netlink/nl/conntrack_linux.go b/vendor/github.com/vishvananda/netlink/nl/conntrack_linux.go
new file mode 100644 (file)
index 0000000..6692b53
--- /dev/null
@@ -0,0 +1,189 @@
+package nl
+
+import "unsafe"
+
+// Track the message sizes for the correct serialization/deserialization
+const (
+       SizeofNfgenmsg      = 4
+       SizeofNfattr        = 4
+       SizeofNfConntrack   = 376
+       SizeofNfctTupleHead = 52
+)
+
+var L4ProtoMap = map[uint8]string{
+       6:  "tcp",
+       17: "udp",
+}
+
+// All the following constants are coming from:
+// https://github.com/torvalds/linux/blob/master/include/uapi/linux/netfilter/nfnetlink_conntrack.h
+
+// enum cntl_msg_types {
+//     IPCTNL_MSG_CT_NEW,
+//     IPCTNL_MSG_CT_GET,
+//     IPCTNL_MSG_CT_DELETE,
+//     IPCTNL_MSG_CT_GET_CTRZERO,
+//     IPCTNL_MSG_CT_GET_STATS_CPU,
+//     IPCTNL_MSG_CT_GET_STATS,
+//     IPCTNL_MSG_CT_GET_DYING,
+//     IPCTNL_MSG_CT_GET_UNCONFIRMED,
+//
+//     IPCTNL_MSG_MAX
+// };
+const (
+       IPCTNL_MSG_CT_GET    = 1
+       IPCTNL_MSG_CT_DELETE = 2
+)
+
+// #define NFNETLINK_V0        0
+const (
+       NFNETLINK_V0 = 0
+)
+
+// #define NLA_F_NESTED (1 << 15)
+const (
+       NLA_F_NESTED = (1 << 15)
+)
+
+// enum ctattr_type {
+//     CTA_UNSPEC,
+//     CTA_TUPLE_ORIG,
+//     CTA_TUPLE_REPLY,
+//     CTA_STATUS,
+//     CTA_PROTOINFO,
+//     CTA_HELP,
+//     CTA_NAT_SRC,
+// #define CTA_NAT     CTA_NAT_SRC     /* backwards compatibility */
+//     CTA_TIMEOUT,
+//     CTA_MARK,
+//     CTA_COUNTERS_ORIG,
+//     CTA_COUNTERS_REPLY,
+//     CTA_USE,
+//     CTA_ID,
+//     CTA_NAT_DST,
+//     CTA_TUPLE_MASTER,
+//     CTA_SEQ_ADJ_ORIG,
+//     CTA_NAT_SEQ_ADJ_ORIG    = CTA_SEQ_ADJ_ORIG,
+//     CTA_SEQ_ADJ_REPLY,
+//     CTA_NAT_SEQ_ADJ_REPLY   = CTA_SEQ_ADJ_REPLY,
+//     CTA_SECMARK,            /* obsolete */
+//     CTA_ZONE,
+//     CTA_SECCTX,
+//     CTA_TIMESTAMP,
+//     CTA_MARK_MASK,
+//     CTA_LABELS,
+//     CTA_LABELS_MASK,
+//     __CTA_MAX
+// };
+const (
+       CTA_TUPLE_ORIG  = 1
+       CTA_TUPLE_REPLY = 2
+       CTA_STATUS      = 3
+       CTA_TIMEOUT     = 8
+       CTA_MARK        = 9
+       CTA_PROTOINFO   = 4
+)
+
+// enum ctattr_tuple {
+//     CTA_TUPLE_UNSPEC,
+//     CTA_TUPLE_IP,
+//     CTA_TUPLE_PROTO,
+//     CTA_TUPLE_ZONE,
+//     __CTA_TUPLE_MAX
+// };
+// #define CTA_TUPLE_MAX (__CTA_TUPLE_MAX - 1)
+const (
+       CTA_TUPLE_IP    = 1
+       CTA_TUPLE_PROTO = 2
+)
+
+// enum ctattr_ip {
+//     CTA_IP_UNSPEC,
+//     CTA_IP_V4_SRC,
+//     CTA_IP_V4_DST,
+//     CTA_IP_V6_SRC,
+//     CTA_IP_V6_DST,
+//     __CTA_IP_MAX
+// };
+// #define CTA_IP_MAX (__CTA_IP_MAX - 1)
+const (
+       CTA_IP_V4_SRC = 1
+       CTA_IP_V4_DST = 2
+       CTA_IP_V6_SRC = 3
+       CTA_IP_V6_DST = 4
+)
+
+// enum ctattr_l4proto {
+//     CTA_PROTO_UNSPEC,
+//     CTA_PROTO_NUM,
+//     CTA_PROTO_SRC_PORT,
+//     CTA_PROTO_DST_PORT,
+//     CTA_PROTO_ICMP_ID,
+//     CTA_PROTO_ICMP_TYPE,
+//     CTA_PROTO_ICMP_CODE,
+//     CTA_PROTO_ICMPV6_ID,
+//     CTA_PROTO_ICMPV6_TYPE,
+//     CTA_PROTO_ICMPV6_CODE,
+//     __CTA_PROTO_MAX
+// };
+// #define CTA_PROTO_MAX (__CTA_PROTO_MAX - 1)
+const (
+       CTA_PROTO_NUM      = 1
+       CTA_PROTO_SRC_PORT = 2
+       CTA_PROTO_DST_PORT = 3
+)
+
+// enum ctattr_protoinfo {
+//     CTA_PROTOINFO_UNSPEC,
+//     CTA_PROTOINFO_TCP,
+//     CTA_PROTOINFO_DCCP,
+//     CTA_PROTOINFO_SCTP,
+//     __CTA_PROTOINFO_MAX
+// };
+// #define CTA_PROTOINFO_MAX (__CTA_PROTOINFO_MAX - 1)
+const (
+       CTA_PROTOINFO_TCP = 1
+)
+
+// enum ctattr_protoinfo_tcp {
+//     CTA_PROTOINFO_TCP_UNSPEC,
+//     CTA_PROTOINFO_TCP_STATE,
+//     CTA_PROTOINFO_TCP_WSCALE_ORIGINAL,
+//     CTA_PROTOINFO_TCP_WSCALE_REPLY,
+//     CTA_PROTOINFO_TCP_FLAGS_ORIGINAL,
+//     CTA_PROTOINFO_TCP_FLAGS_REPLY,
+//     __CTA_PROTOINFO_TCP_MAX
+// };
+// #define CTA_PROTOINFO_TCP_MAX (__CTA_PROTOINFO_TCP_MAX - 1)
+const (
+       CTA_PROTOINFO_TCP_STATE           = 1
+       CTA_PROTOINFO_TCP_WSCALE_ORIGINAL = 2
+       CTA_PROTOINFO_TCP_WSCALE_REPLY    = 3
+       CTA_PROTOINFO_TCP_FLAGS_ORIGINAL  = 4
+       CTA_PROTOINFO_TCP_FLAGS_REPLY     = 5
+)
+
+// /* General form of address family dependent message.
+//  */
+// struct nfgenmsg {
+//     __u8  nfgen_family;             /* AF_xxx */
+//     __u8  version;          /* nfnetlink version */
+//     __be16    res_id;               /* resource id */
+// };
+type Nfgenmsg struct {
+       NfgenFamily uint8
+       Version     uint8
+       ResId       uint16 // big endian
+}
+
+func (msg *Nfgenmsg) Len() int {
+       return SizeofNfgenmsg
+}
+
+func DeserializeNfgenmsg(b []byte) *Nfgenmsg {
+       return (*Nfgenmsg)(unsafe.Pointer(&b[0:SizeofNfgenmsg][0]))
+}
+
+func (msg *Nfgenmsg) Serialize() []byte {
+       return (*(*[SizeofNfgenmsg]byte)(unsafe.Pointer(msg)))[:]
+}
diff --git a/vendor/github.com/vishvananda/netlink/nl/genetlink_linux.go b/vendor/github.com/vishvananda/netlink/nl/genetlink_linux.go
new file mode 100644 (file)
index 0000000..81b46f2
--- /dev/null
@@ -0,0 +1,89 @@
+package nl
+
+import (
+       "unsafe"
+)
+
+const SizeofGenlmsg = 4
+
+const (
+       GENL_ID_CTRL      = 0x10
+       GENL_CTRL_VERSION = 2
+       GENL_CTRL_NAME    = "nlctrl"
+)
+
+const (
+       GENL_CTRL_CMD_GETFAMILY = 3
+)
+
+const (
+       GENL_CTRL_ATTR_UNSPEC = iota
+       GENL_CTRL_ATTR_FAMILY_ID
+       GENL_CTRL_ATTR_FAMILY_NAME
+       GENL_CTRL_ATTR_VERSION
+       GENL_CTRL_ATTR_HDRSIZE
+       GENL_CTRL_ATTR_MAXATTR
+       GENL_CTRL_ATTR_OPS
+       GENL_CTRL_ATTR_MCAST_GROUPS
+)
+
+const (
+       GENL_CTRL_ATTR_OP_UNSPEC = iota
+       GENL_CTRL_ATTR_OP_ID
+       GENL_CTRL_ATTR_OP_FLAGS
+)
+
+const (
+       GENL_ADMIN_PERM = 1 << iota
+       GENL_CMD_CAP_DO
+       GENL_CMD_CAP_DUMP
+       GENL_CMD_CAP_HASPOL
+)
+
+const (
+       GENL_CTRL_ATTR_MCAST_GRP_UNSPEC = iota
+       GENL_CTRL_ATTR_MCAST_GRP_NAME
+       GENL_CTRL_ATTR_MCAST_GRP_ID
+)
+
+const (
+       GENL_GTP_VERSION = 0
+       GENL_GTP_NAME    = "gtp"
+)
+
+const (
+       GENL_GTP_CMD_NEWPDP = iota
+       GENL_GTP_CMD_DELPDP
+       GENL_GTP_CMD_GETPDP
+)
+
+const (
+       GENL_GTP_ATTR_UNSPEC = iota
+       GENL_GTP_ATTR_LINK
+       GENL_GTP_ATTR_VERSION
+       GENL_GTP_ATTR_TID
+       GENL_GTP_ATTR_PEER_ADDRESS
+       GENL_GTP_ATTR_MS_ADDRESS
+       GENL_GTP_ATTR_FLOW
+       GENL_GTP_ATTR_NET_NS_FD
+       GENL_GTP_ATTR_I_TEI
+       GENL_GTP_ATTR_O_TEI
+       GENL_GTP_ATTR_PAD
+)
+
+type Genlmsg struct {
+       Command uint8
+       Version uint8
+}
+
+func (msg *Genlmsg) Len() int {
+       return SizeofGenlmsg
+}
+
+func DeserializeGenlmsg(b []byte) *Genlmsg {
+       return (*Genlmsg)(unsafe.Pointer(&b[0:SizeofGenlmsg][0]))
+}
+
+func (msg *Genlmsg) Serialize() []byte {
+       return (*(*[SizeofGenlmsg]byte)(unsafe.Pointer(msg)))[:]
+}
index 6d9af56..f7b9575 100644 (file)
@@ -102,7 +102,10 @@ const (
        IFLA_BRPORT_FAST_LEAVE
        IFLA_BRPORT_LEARNING
        IFLA_BRPORT_UNICAST_FLOOD
-       IFLA_BRPORT_MAX = IFLA_BRPORT_UNICAST_FLOOD
+       IFLA_BRPORT_PROXYARP
+       IFLA_BRPORT_LEARNING_SYNC
+       IFLA_BRPORT_PROXYARP_WIFI
+       IFLA_BRPORT_MAX = IFLA_BRPORT_PROXYARP_WIFI
 )
 
 const (
@@ -151,6 +154,10 @@ const (
        IFLA_BOND_AD_LACP_RATE
        IFLA_BOND_AD_SELECT
        IFLA_BOND_AD_INFO
+       IFLA_BOND_AD_ACTOR_SYS_PRIO
+       IFLA_BOND_AD_USER_PORT_KEY
+       IFLA_BOND_AD_ACTOR_SYSTEM
+       IFLA_BOND_TLB_DYNAMIC_LB
 )
 
 const (
@@ -416,7 +423,8 @@ const (
        IFLA_XDP_UNSPEC   = iota
        IFLA_XDP_FD       /* fd of xdp program to attach, or -1 to remove */
        IFLA_XDP_ATTACHED /* read-only bool indicating if prog is attached */
-       IFLA_XDP_MAX      = IFLA_XDP_ATTACHED
+       IFLA_XDP_FLAGS    /* xdp prog related flags */
+       IFLA_XDP_MAX      = IFLA_XDP_FLAGS
 )
 
 const (
@@ -452,3 +460,65 @@ const (
        IFLA_VRF_UNSPEC = iota
        IFLA_VRF_TABLE
 )
+
+const (
+       IFLA_BR_UNSPEC = iota
+       IFLA_BR_FORWARD_DELAY
+       IFLA_BR_HELLO_TIME
+       IFLA_BR_MAX_AGE
+       IFLA_BR_AGEING_TIME
+       IFLA_BR_STP_STATE
+       IFLA_BR_PRIORITY
+       IFLA_BR_VLAN_FILTERING
+       IFLA_BR_VLAN_PROTOCOL
+       IFLA_BR_GROUP_FWD_MASK
+       IFLA_BR_ROOT_ID
+       IFLA_BR_BRIDGE_ID
+       IFLA_BR_ROOT_PORT
+       IFLA_BR_ROOT_PATH_COST
+       IFLA_BR_TOPOLOGY_CHANGE
+       IFLA_BR_TOPOLOGY_CHANGE_DETECTED
+       IFLA_BR_HELLO_TIMER
+       IFLA_BR_TCN_TIMER
+       IFLA_BR_TOPOLOGY_CHANGE_TIMER
+       IFLA_BR_GC_TIMER
+       IFLA_BR_GROUP_ADDR
+       IFLA_BR_FDB_FLUSH
+       IFLA_BR_MCAST_ROUTER
+       IFLA_BR_MCAST_SNOOPING
+       IFLA_BR_MCAST_QUERY_USE_IFADDR
+       IFLA_BR_MCAST_QUERIER
+       IFLA_BR_MCAST_HASH_ELASTICITY
+       IFLA_BR_MCAST_HASH_MAX
+       IFLA_BR_MCAST_LAST_MEMBER_CNT
+       IFLA_BR_MCAST_STARTUP_QUERY_CNT
+       IFLA_BR_MCAST_LAST_MEMBER_INTVL
+       IFLA_BR_MCAST_MEMBERSHIP_INTVL
+       IFLA_BR_MCAST_QUERIER_INTVL
+       IFLA_BR_MCAST_QUERY_INTVL
+       IFLA_BR_MCAST_QUERY_RESPONSE_INTVL
+       IFLA_BR_MCAST_STARTUP_QUERY_INTVL
+       IFLA_BR_NF_CALL_IPTABLES
+       IFLA_BR_NF_CALL_IP6TABLES
+       IFLA_BR_NF_CALL_ARPTABLES
+       IFLA_BR_VLAN_DEFAULT_PVID
+       IFLA_BR_PAD
+       IFLA_BR_VLAN_STATS_ENABLED
+       IFLA_BR_MCAST_STATS_ENABLED
+       IFLA_BR_MCAST_IGMP_VERSION
+       IFLA_BR_MCAST_MLD_VERSION
+       IFLA_BR_MAX = IFLA_BR_MCAST_MLD_VERSION
+)
+
+const (
+       IFLA_GTP_UNSPEC = iota
+       IFLA_GTP_FD0
+       IFLA_GTP_FD1
+       IFLA_GTP_PDP_HASHSIZE
+       IFLA_GTP_ROLE
+)
+
+const (
+       GTP_ROLE_GGSN = iota
+       GTP_ROLE_SGSN
+)
index fb9031e..1329acd 100644 (file)
@@ -24,7 +24,7 @@ const (
 )
 
 // SupportedNlFamilies contains the list of netlink families this netlink package supports
-var SupportedNlFamilies = []int{syscall.NETLINK_ROUTE, syscall.NETLINK_XFRM}
+var SupportedNlFamilies = []int{syscall.NETLINK_ROUTE, syscall.NETLINK_XFRM, syscall.NETLINK_NETFILTER}
 
 var nextSeqNr uint32
 
@@ -321,6 +321,7 @@ func (a *RtAttr) Serialize() []byte {
 type NetlinkRequest struct {
        syscall.NlMsghdr
        Data    []NetlinkRequestData
+       RawData []byte
        Sockets map[int]*SocketHandle
 }
 
@@ -332,6 +333,8 @@ func (req *NetlinkRequest) Serialize() []byte {
                dataBytes[i] = data.Serialize()
                length = length + len(dataBytes[i])
        }
+       length += len(req.RawData)
+
        req.Len = uint32(length)
        b := make([]byte, length)
        hdr := (*(*[syscall.SizeofNlMsghdr]byte)(unsafe.Pointer(req)))[:]
@@ -343,6 +346,10 @@ func (req *NetlinkRequest) Serialize() []byte {
                        next = next + 1
                }
        }
+       // Add the raw data if any
+       if len(req.RawData) > 0 {
+               copy(b[next:length], req.RawData)
+       }
        return b
 }
 
@@ -352,6 +359,13 @@ func (req *NetlinkRequest) AddData(data NetlinkRequestData) {
        }
 }
 
+// AddRawData adds raw bytes to the end of the NetlinkRequest object during serialization
+func (req *NetlinkRequest) AddRawData(data []byte) {
+       if data != nil {
+               req.RawData = append(req.RawData, data...)
+       }
+}
+
 // Execute the request against a the given sockType.
 // Returns a list of netlink messages in serialized format, optionally filtered
 // by resType.
@@ -445,18 +459,18 @@ func NewNetlinkRequest(proto, flags int) *NetlinkRequest {
 }
 
 type NetlinkSocket struct {
-       fd  int
+       fd  int32
        lsa syscall.SockaddrNetlink
        sync.Mutex
 }
 
 func getNetlinkSocket(protocol int) (*NetlinkSocket, error) {
-       fd, err := syscall.Socket(syscall.AF_NETLINK, syscall.SOCK_RAW, protocol)
+       fd, err := syscall.Socket(syscall.AF_NETLINK, syscall.SOCK_RAW|syscall.SOCK_CLOEXEC, protocol)
        if err != nil {
                return nil, err
        }
        s := &NetlinkSocket{
-               fd: fd,
+               fd: int32(fd),
        }
        s.lsa.Family = syscall.AF_NETLINK
        if err := syscall.Bind(fd, &s.lsa); err != nil {
@@ -542,7 +556,7 @@ func Subscribe(protocol int, groups ...uint) (*NetlinkSocket, error) {
                return nil, err
        }
        s := &NetlinkSocket{
-               fd: fd,
+               fd: int32(fd),
        }
        s.lsa.Family = syscall.AF_NETLINK
 
@@ -571,30 +585,32 @@ func SubscribeAt(newNs, curNs netns.NsHandle, protocol int, groups ...uint) (*Ne
 }
 
 func (s *NetlinkSocket) Close() {
-       syscall.Close(s.fd)
-       s.fd = -1
+       fd := int(atomic.SwapInt32(&s.fd, -1))
+       syscall.Close(fd)
 }
 
 func (s *NetlinkSocket) GetFd() int {
-       return s.fd
+       return int(atomic.LoadInt32(&s.fd))
 }
 
 func (s *NetlinkSocket) Send(request *NetlinkRequest) error {
-       if s.fd < 0 {
+       fd := int(atomic.LoadInt32(&s.fd))
+       if fd < 0 {
                return fmt.Errorf("Send called on a closed socket")
        }
-       if err := syscall.Sendto(s.fd, request.Serialize(), 0, &s.lsa); err != nil {
+       if err := syscall.Sendto(fd, request.Serialize(), 0, &s.lsa); err != nil {
                return err
        }
        return nil
 }
 
 func (s *NetlinkSocket) Receive() ([]syscall.NetlinkMessage, error) {
-       if s.fd < 0 {
+       fd := int(atomic.LoadInt32(&s.fd))
+       if fd < 0 {
                return nil, fmt.Errorf("Receive called on a closed socket")
        }
        rb := make([]byte, syscall.Getpagesize())
-       nr, _, err := syscall.Recvfrom(s.fd, rb, 0)
+       nr, _, err := syscall.Recvfrom(fd, rb, 0)
        if err != nil {
                return nil, err
        }
@@ -606,7 +622,8 @@ func (s *NetlinkSocket) Receive() ([]syscall.NetlinkMessage, error) {
 }
 
 func (s *NetlinkSocket) GetPid() (uint32, error) {
-       lsa, err := syscall.Getsockname(s.fd)
+       fd := int(atomic.LoadInt32(&s.fd))
+       lsa, err := syscall.Getsockname(fd)
        if err != nil {
                return 0, err
        }
index ead3f2f..0087c44 100644 (file)
@@ -6,12 +6,14 @@ import (
 
 // Protinfo represents bridge flags from netlink.
 type Protinfo struct {
-       Hairpin   bool
-       Guard     bool
-       FastLeave bool
-       RootBlock bool
-       Learning  bool
-       Flood     bool
+       Hairpin      bool
+       Guard        bool
+       FastLeave    bool
+       RootBlock    bool
+       Learning     bool
+       Flood        bool
+       ProxyArp     bool
+       ProxyArpWiFi bool
 }
 
 // String returns a list of enabled flags
@@ -35,6 +37,12 @@ func (prot *Protinfo) String() string {
        if prot.Flood {
                boolStrings = append(boolStrings, "Flood")
        }
+       if prot.ProxyArp {
+               boolStrings = append(boolStrings, "ProxyArp")
+       }
+       if prot.ProxyArpWiFi {
+               boolStrings = append(boolStrings, "ProxyArpWiFi")
+       }
        return strings.Join(boolStrings, " ")
 }
 
index ea72695..10dd0d5 100644 (file)
@@ -64,6 +64,10 @@ func parseProtinfo(infos []syscall.NetlinkRouteAttr) *Protinfo {
                        pi.Learning = byteToBool(info.Value[0])
                case nl.IFLA_BRPORT_UNICAST_FLOOD:
                        pi.Flood = byteToBool(info.Value[0])
+               case nl.IFLA_BRPORT_PROXYARP:
+                       pi.ProxyArp = byteToBool(info.Value[0])
+               case nl.IFLA_BRPORT_PROXYARP_WIFI:
+                       pi.ProxyArpWiFi = byteToBool(info.Value[0])
                }
        }
        return &pi
index 9e0f1f9..cd739e7 100644 (file)
@@ -401,7 +401,7 @@ func (h *Handle) RouteListFiltered(family int, filter *Route, filterMask uint64)
                }
                if filter != nil {
                        switch {
-                       case filterMask&RT_FILTER_TABLE != 0 && route.Table != filter.Table:
+                       case filterMask&RT_FILTER_TABLE != 0 && filter.Table != syscall.RT_TABLE_UNSPEC && route.Table != filter.Table:
                                continue
                        case filterMask&RT_FILTER_PROTOCOL != 0 && route.Protocol != filter.Protocol:
                                continue
index 6b45cfb..66a5f72 100644 (file)
@@ -37,6 +37,7 @@ func main() {
 
     // Create a new network namespace
     newns, _ := netns.New()
+    netns.Set(newns)
     defer newns.Close()
 
     // Do something with the network namespace
index 1ba6b26..dd2f215 100644 (file)
@@ -19,7 +19,7 @@ type NsHandle int
 
 // Equal determines if two network handles refer to the same network
 // namespace. This is done by comparing the device and inode that the
-// file descripors point to.
+// file descriptors point to.
 func (ns NsHandle) Equal(other NsHandle) bool {
        if ns == other {
                return true
index 0f038d1..7162b86 100644 (file)
@@ -19,6 +19,8 @@ var SYS_SETNS = map[string]uintptr{
        "amd64":   308,
        "arm64":   268,
        "arm":     375,
+       "mips":    4344,
+       "mipsle":  4344,
        "ppc64":   350,
        "ppc64le": 350,
        "s390x":   339,
index b2edc56..d06af62 100644 (file)
@@ -22,6 +22,10 @@ func Get() (NsHandle, error) {
        return -1, ErrNotImplemented
 }
 
+func GetFromPath(path string) (NsHandle, error) {
+       return -1, ErrNotImplemented
+}
+
 func GetFromName(name string) (NsHandle, error) {
        return -1, ErrNotImplemented
 }
@@ -30,6 +34,10 @@ func GetFromPid(pid int) (NsHandle, error) {
        return -1, ErrNotImplemented
 }
 
+func GetFromThread(pid, tid int) (NsHandle, error) {
+       return -1, ErrNotImplemented
+}
+
 func GetFromDocker(id string) (NsHandle, error) {
        return -1, ErrNotImplemented
 }