Update netlink dependency.
authorDaniel Nardo <dnardo@google.com>
Mon, 24 Apr 2017 17:31:24 +0000 (10:31 -0700)
committerDaniel Nardo <dnardo@google.com>
Mon, 24 Apr 2017 17:31:24 +0000 (10:31 -0700)
41 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/addr_test.go [new file with mode: 0644]
vendor/github.com/vishvananda/netlink/class_test.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_test.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/filter_test.go [new file with mode: 0644]
vendor/github.com/vishvananda/netlink/handle_test.go [new file with mode: 0644]
vendor/github.com/vishvananda/netlink/link_linux.go
vendor/github.com/vishvananda/netlink/link_test.go [new file with mode: 0644]
vendor/github.com/vishvananda/netlink/neigh_test.go [new file with mode: 0644]
vendor/github.com/vishvananda/netlink/netlink_test.go [new file with mode: 0644]
vendor/github.com/vishvananda/netlink/netlink_unspecified.go
vendor/github.com/vishvananda/netlink/nl/addr_linux.go
vendor/github.com/vishvananda/netlink/nl/addr_linux_test.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/link_linux.go
vendor/github.com/vishvananda/netlink/nl/link_linux_test.go [new file with mode: 0644]
vendor/github.com/vishvananda/netlink/nl/nl_linux.go
vendor/github.com/vishvananda/netlink/nl/nl_linux_test.go [new file with mode: 0644]
vendor/github.com/vishvananda/netlink/nl/route_linux_test.go [new file with mode: 0644]
vendor/github.com/vishvananda/netlink/nl/tc_linux_test.go [new file with mode: 0644]
vendor/github.com/vishvananda/netlink/nl/xfrm_linux_test.go [new file with mode: 0644]
vendor/github.com/vishvananda/netlink/nl/xfrm_monitor_linux_test.go [new file with mode: 0644]
vendor/github.com/vishvananda/netlink/nl/xfrm_policy_linux_test.go [new file with mode: 0644]
vendor/github.com/vishvananda/netlink/nl/xfrm_state_linux_test.go [new file with mode: 0644]
vendor/github.com/vishvananda/netlink/protinfo.go
vendor/github.com/vishvananda/netlink/protinfo_linux.go
vendor/github.com/vishvananda/netlink/protinfo_test.go [new file with mode: 0644]
vendor/github.com/vishvananda/netlink/qdisc_test.go [new file with mode: 0644]
vendor/github.com/vishvananda/netlink/route_test.go [new file with mode: 0644]
vendor/github.com/vishvananda/netlink/rule_test.go [new file with mode: 0644]
vendor/github.com/vishvananda/netlink/socket_test.go [new file with mode: 0644]
vendor/github.com/vishvananda/netlink/xfrm_monitor_test.go [new file with mode: 0644]
vendor/github.com/vishvananda/netlink/xfrm_policy_test.go [new file with mode: 0644]
vendor/github.com/vishvananda/netlink/xfrm_state_test.go [new file with mode: 0644]

index 62bf3d9..a59db89 100644 (file)
                },
                {
                        "ImportPath": "github.com/vishvananda/netlink",
-                       "Rev": "fe3b5664d23a11b52ba59bece4ff29c52772a56b"
+                       "Rev": "b71e0bb214aebd980216cb2516e8bd7bca9e9672"
                },
                {
                        "ImportPath": "github.com/vishvananda/netlink/nl",
-                       "Rev": "fe3b5664d23a11b52ba59bece4ff29c52772a56b"
+                       "Rev": "b71e0bb214aebd980216cb2516e8bd7bca9e9672"
                },
                {
                        "ImportPath": "github.com/vishvananda/netns",
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..43daa44 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 {
@@ -186,6 +199,10 @@ func parseAddr(m []byte) (addr Addr, family, index int, err error) {
                        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 +220,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 +271,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/addr_test.go b/vendor/github.com/vishvananda/netlink/addr_test.go
new file mode 100644 (file)
index 0000000..cd7fed4
--- /dev/null
@@ -0,0 +1,193 @@
+// +build linux
+
+package netlink
+
+import (
+       "net"
+       "os"
+       "syscall"
+       "testing"
+)
+
+func TestAddrAdd(t *testing.T) {
+       DoTestAddr(t, AddrAdd)
+}
+
+func TestAddrReplace(t *testing.T) {
+       DoTestAddr(t, AddrReplace)
+}
+
+func DoTestAddr(t *testing.T, FunctionUndertest func(Link, *Addr) error) {
+       if os.Getenv("TRAVIS_BUILD_DIR") != "" {
+               t.Skipf("Fails in travis with: addr_test.go:68: Address flags not set properly, got=0, expected=128")
+       }
+       // TODO: IFA_F_PERMANENT does not seem to be set by default on older kernels?
+       var address = &net.IPNet{IP: net.IPv4(127, 0, 0, 2), Mask: net.CIDRMask(24, 32)}
+       var peer = &net.IPNet{IP: net.IPv4(127, 0, 0, 3), Mask: net.CIDRMask(24, 32)}
+       var addrTests = []struct {
+               addr     *Addr
+               expected *Addr
+       }{
+               {
+                       &Addr{IPNet: address},
+                       &Addr{IPNet: address, Label: "lo", Scope: syscall.RT_SCOPE_UNIVERSE, Flags: syscall.IFA_F_PERMANENT},
+               },
+               {
+                       &Addr{IPNet: address, Label: "local"},
+                       &Addr{IPNet: address, Label: "local", Scope: syscall.RT_SCOPE_UNIVERSE, Flags: syscall.IFA_F_PERMANENT},
+               },
+               {
+                       &Addr{IPNet: address, Flags: syscall.IFA_F_OPTIMISTIC},
+                       &Addr{IPNet: address, Label: "lo", Flags: syscall.IFA_F_OPTIMISTIC | syscall.IFA_F_PERMANENT, Scope: syscall.RT_SCOPE_UNIVERSE},
+               },
+               {
+                       &Addr{IPNet: address, Flags: syscall.IFA_F_OPTIMISTIC | syscall.IFA_F_DADFAILED},
+                       &Addr{IPNet: address, Label: "lo", Flags: syscall.IFA_F_OPTIMISTIC | syscall.IFA_F_DADFAILED | syscall.IFA_F_PERMANENT, Scope: syscall.RT_SCOPE_UNIVERSE},
+               },
+               {
+                       &Addr{IPNet: address, Scope: syscall.RT_SCOPE_NOWHERE},
+                       &Addr{IPNet: address, Label: "lo", Flags: syscall.IFA_F_PERMANENT, Scope: syscall.RT_SCOPE_NOWHERE},
+               },
+               {
+                       &Addr{IPNet: address, Peer: peer},
+                       &Addr{IPNet: address, Peer: peer, Label: "lo", Scope: syscall.RT_SCOPE_UNIVERSE, Flags: syscall.IFA_F_PERMANENT},
+               },
+       }
+
+       tearDown := setUpNetlinkTest(t)
+       defer tearDown()
+
+       link, err := LinkByName("lo")
+       if err != nil {
+               t.Fatal(err)
+       }
+
+       for _, tt := range addrTests {
+               if err = FunctionUndertest(link, tt.addr); err != nil {
+                       t.Fatal(err)
+               }
+
+               addrs, err := AddrList(link, FAMILY_ALL)
+               if err != nil {
+                       t.Fatal(err)
+               }
+
+               if len(addrs) != 1 {
+                       t.Fatal("Address not added properly")
+               }
+
+               if !addrs[0].Equal(*tt.expected) {
+                       t.Fatalf("Address ip no set properly, got=%s, expected=%s", addrs[0], tt.expected)
+               }
+
+               if addrs[0].Label != tt.expected.Label {
+                       t.Fatalf("Address label not set properly, got=%s, expected=%s", addrs[0].Label, tt.expected.Label)
+               }
+
+               if addrs[0].Flags != tt.expected.Flags {
+                       t.Fatalf("Address flags not set properly, got=%d, expected=%d", addrs[0].Flags, tt.expected.Flags)
+               }
+
+               if addrs[0].Scope != tt.expected.Scope {
+                       t.Fatalf("Address scope not set properly, got=%d, expected=%d", addrs[0].Scope, tt.expected.Scope)
+               }
+
+               if tt.expected.Peer != nil {
+                       if !addrs[0].PeerEqual(*tt.expected) {
+                               t.Fatalf("Peer Address ip no set properly, got=%s, expected=%s", addrs[0].Peer, tt.expected.Peer)
+                       }
+               }
+
+               // Pass FAMILY_V4, we should get the same results as FAMILY_ALL
+               addrs, err = AddrList(link, FAMILY_V4)
+               if err != nil {
+                       t.Fatal(err)
+               }
+               if len(addrs) != 1 {
+                       t.Fatal("Address not added properly")
+               }
+
+               // Pass a wrong family number, we should get nil list
+               addrs, err = AddrList(link, 0x8)
+               if err != nil {
+                       t.Fatal(err)
+               }
+
+               if len(addrs) != 0 {
+                       t.Fatal("Address not expected")
+               }
+
+               if err = AddrDel(link, tt.addr); err != nil {
+                       t.Fatal(err)
+               }
+
+               addrs, err = AddrList(link, FAMILY_ALL)
+               if err != nil {
+                       t.Fatal(err)
+               }
+
+               if len(addrs) != 0 {
+                       t.Fatal("Address not removed properly")
+               }
+       }
+
+}
+
+func TestAddrAddReplace(t *testing.T) {
+       tearDown := setUpNetlinkTest(t)
+       defer tearDown()
+
+       var address = &net.IPNet{IP: net.IPv4(127, 0, 0, 2), Mask: net.CIDRMask(24, 32)}
+       var addr = &Addr{IPNet: address}
+
+       link, err := LinkByName("lo")
+       if err != nil {
+               t.Fatal(err)
+       }
+
+       err = AddrAdd(link, addr)
+       if err != nil {
+               t.Fatal(err)
+       }
+
+       addrs, err := AddrList(link, FAMILY_ALL)
+       if err != nil {
+               t.Fatal(err)
+       }
+
+       if len(addrs) != 1 {
+               t.Fatal("Address not added properly")
+       }
+
+       err = AddrAdd(link, addr)
+       if err == nil {
+               t.Fatal("Re-adding address should fail (but succeeded unexpectedly).")
+       }
+
+       err = AddrReplace(link, addr)
+       if err != nil {
+               t.Fatal("Replacing address failed.")
+       }
+
+       addrs, err = AddrList(link, FAMILY_ALL)
+       if err != nil {
+               t.Fatal(err)
+       }
+
+       if len(addrs) != 1 {
+               t.Fatal("Address not added properly")
+       }
+
+       if err = AddrDel(link, addr); err != nil {
+               t.Fatal(err)
+       }
+
+       addrs, err = AddrList(link, FAMILY_ALL)
+       if err != nil {
+               t.Fatal(err)
+       }
+
+       if len(addrs) != 0 {
+               t.Fatal("Address not removed properly")
+       }
+}
diff --git a/vendor/github.com/vishvananda/netlink/class_test.go b/vendor/github.com/vishvananda/netlink/class_test.go
new file mode 100644 (file)
index 0000000..1d0c27b
--- /dev/null
@@ -0,0 +1,408 @@
+// +build linux
+
+package netlink
+
+import (
+       "testing"
+)
+
+func TestClassAddDel(t *testing.T) {
+       tearDown := setUpNetlinkTest(t)
+       defer tearDown()
+       if err := LinkAdd(&Ifb{LinkAttrs{Name: "foo"}}); err != nil {
+               t.Fatal(err)
+       }
+       if err := LinkAdd(&Ifb{LinkAttrs{Name: "bar"}}); err != nil {
+               t.Fatal(err)
+       }
+       link, err := LinkByName("foo")
+       if err != nil {
+               t.Fatal(err)
+       }
+       if err := LinkSetUp(link); err != nil {
+               t.Fatal(err)
+       }
+       attrs := QdiscAttrs{
+               LinkIndex: link.Attrs().Index,
+               Handle:    MakeHandle(0xffff, 0),
+               Parent:    HANDLE_ROOT,
+       }
+       qdisc := NewHtb(attrs)
+       if err := QdiscAdd(qdisc); err != nil {
+               t.Fatal(err)
+       }
+       qdiscs, err := QdiscList(link)
+       if err != nil {
+               t.Fatal(err)
+       }
+       if len(qdiscs) != 1 {
+               t.Fatal("Failed to add qdisc")
+       }
+       _, ok := qdiscs[0].(*Htb)
+       if !ok {
+               t.Fatal("Qdisc is the wrong type")
+       }
+
+       classattrs := ClassAttrs{
+               LinkIndex: link.Attrs().Index,
+               Parent:    MakeHandle(0xffff, 0),
+               Handle:    MakeHandle(0xffff, 2),
+       }
+
+       htbclassattrs := HtbClassAttrs{
+               Rate:    1234000,
+               Cbuffer: 1690,
+       }
+       class := NewHtbClass(classattrs, htbclassattrs)
+       if err := ClassAdd(class); err != nil {
+               t.Fatal(err)
+       }
+       classes, err := ClassList(link, MakeHandle(0xffff, 0))
+       if err != nil {
+               t.Fatal(err)
+       }
+       if len(classes) != 1 {
+               t.Fatal("Failed to add class")
+       }
+
+       htb, ok := classes[0].(*HtbClass)
+       if !ok {
+               t.Fatal("Class is the wrong type")
+       }
+       if htb.Rate != class.Rate {
+               t.Fatal("Rate doesn't match")
+       }
+       if htb.Ceil != class.Ceil {
+               t.Fatal("Ceil doesn't match")
+       }
+       if htb.Buffer != class.Buffer {
+               t.Fatal("Buffer doesn't match")
+       }
+       if htb.Cbuffer != class.Cbuffer {
+               t.Fatal("Cbuffer doesn't match")
+       }
+
+       qattrs := QdiscAttrs{
+               LinkIndex: link.Attrs().Index,
+               Handle:    MakeHandle(0x2, 0),
+               Parent:    MakeHandle(0xffff, 2),
+       }
+       nattrs := NetemQdiscAttrs{
+               Latency:     20000,
+               Loss:        23.4,
+               Duplicate:   14.3,
+               LossCorr:    8.34,
+               Jitter:      1000,
+               DelayCorr:   12.3,
+               ReorderProb: 23.4,
+               CorruptProb: 10.0,
+               CorruptCorr: 10,
+       }
+       qdiscnetem := NewNetem(qattrs, nattrs)
+       if err := QdiscAdd(qdiscnetem); err != nil {
+               t.Fatal(err)
+       }
+
+       qdiscs, err = QdiscList(link)
+       if err != nil {
+               t.Fatal(err)
+       }
+       if len(qdiscs) != 2 {
+               t.Fatal("Failed to add qdisc")
+       }
+       _, ok = qdiscs[0].(*Htb)
+       if !ok {
+               t.Fatal("Qdisc is the wrong type")
+       }
+
+       netem, ok := qdiscs[1].(*Netem)
+       if !ok {
+               t.Fatal("Qdisc is the wrong type")
+       }
+       // Compare the record we got from the list with the one we created
+       if netem.Loss != qdiscnetem.Loss {
+               t.Fatal("Loss does not match")
+       }
+       if netem.Latency != qdiscnetem.Latency {
+               t.Fatal("Latency does not match")
+       }
+       if netem.CorruptProb != qdiscnetem.CorruptProb {
+               t.Fatal("CorruptProb does not match")
+       }
+       if netem.Jitter != qdiscnetem.Jitter {
+               t.Fatal("Jitter does not match")
+       }
+       if netem.LossCorr != qdiscnetem.LossCorr {
+               t.Fatal("Loss does not match")
+       }
+       if netem.DuplicateCorr != qdiscnetem.DuplicateCorr {
+               t.Fatal("DuplicateCorr does not match")
+       }
+
+       // Deletion
+       if err := ClassDel(class); err != nil {
+               t.Fatal(err)
+       }
+       classes, err = ClassList(link, MakeHandle(0xffff, 0))
+       if err != nil {
+               t.Fatal(err)
+       }
+       if len(classes) != 0 {
+               t.Fatal("Failed to remove class")
+       }
+       if err := QdiscDel(qdisc); err != nil {
+               t.Fatal(err)
+       }
+       qdiscs, err = QdiscList(link)
+       if err != nil {
+               t.Fatal(err)
+       }
+       if len(qdiscs) != 0 {
+               t.Fatal("Failed to remove qdisc")
+       }
+}
+
+func TestHtbClassAddHtbClassChangeDel(t *testing.T) {
+       /**
+       This test first set up a interface ans set up a Htb qdisc
+       A HTB class is attach to it and a Netem qdisc is attached to that class
+       Next, we test changing the HTB class in place and confirming the Netem is
+       still attached. We also check that invoting ClassChange with a non-existing
+       class will fail.
+       Finally, we test ClassReplace. We confirm it correctly behave like
+       ClassChange when the parent/handle pair exists and that it will create a
+       new class if the handle is modified.
+       */
+       tearDown := setUpNetlinkTest(t)
+       defer tearDown()
+       if err := LinkAdd(&Ifb{LinkAttrs{Name: "foo"}}); err != nil {
+               t.Fatal(err)
+       }
+       link, err := LinkByName("foo")
+       if err != nil {
+               t.Fatal(err)
+       }
+       if err := LinkSetUp(link); err != nil {
+               t.Fatal(err)
+       }
+       attrs := QdiscAttrs{
+               LinkIndex: link.Attrs().Index,
+               Handle:    MakeHandle(0xffff, 0),
+               Parent:    HANDLE_ROOT,
+       }
+       qdisc := NewHtb(attrs)
+       if err := QdiscAdd(qdisc); err != nil {
+               t.Fatal(err)
+       }
+       qdiscs, err := QdiscList(link)
+       if err != nil {
+               t.Fatal(err)
+       }
+       if len(qdiscs) != 1 {
+               t.Fatal("Failed to add qdisc")
+       }
+       _, ok := qdiscs[0].(*Htb)
+       if !ok {
+               t.Fatal("Qdisc is the wrong type")
+       }
+
+       classattrs := ClassAttrs{
+               LinkIndex: link.Attrs().Index,
+               Parent:    MakeHandle(0xffff, 0),
+               Handle:    MakeHandle(0xffff, 2),
+       }
+
+       htbclassattrs := HtbClassAttrs{
+               Rate:    1234000,
+               Cbuffer: 1690,
+       }
+       class := NewHtbClass(classattrs, htbclassattrs)
+       if err := ClassAdd(class); err != nil {
+               t.Fatal(err)
+       }
+       classes, err := ClassList(link, 0)
+       if err != nil {
+               t.Fatal(err)
+       }
+       if len(classes) != 1 {
+               t.Fatal("Failed to add class")
+       }
+
+       htb, ok := classes[0].(*HtbClass)
+       if !ok {
+               t.Fatal("Class is the wrong type")
+       }
+
+       qattrs := QdiscAttrs{
+               LinkIndex: link.Attrs().Index,
+               Handle:    MakeHandle(0x2, 0),
+               Parent:    MakeHandle(0xffff, 2),
+       }
+       nattrs := NetemQdiscAttrs{
+               Latency:     20000,
+               Loss:        23.4,
+               Duplicate:   14.3,
+               LossCorr:    8.34,
+               Jitter:      1000,
+               DelayCorr:   12.3,
+               ReorderProb: 23.4,
+               CorruptProb: 10.0,
+               CorruptCorr: 10,
+       }
+       qdiscnetem := NewNetem(qattrs, nattrs)
+       if err := QdiscAdd(qdiscnetem); err != nil {
+               t.Fatal(err)
+       }
+
+       qdiscs, err = QdiscList(link)
+       if err != nil {
+               t.Fatal(err)
+       }
+       if len(qdiscs) != 2 {
+               t.Fatal("Failed to add qdisc")
+       }
+
+       _, ok = qdiscs[1].(*Netem)
+       if !ok {
+               t.Fatal("Qdisc is the wrong type")
+       }
+
+       // Change
+       // For change to work, the handle and parent cannot be changed.
+
+       // First, test it fails if we change the Handle.
+       oldHandle := classattrs.Handle
+       classattrs.Handle = MakeHandle(0xffff, 3)
+       class = NewHtbClass(classattrs, htbclassattrs)
+       if err := ClassChange(class); err == nil {
+               t.Fatal("ClassChange should not work when using a different handle.")
+       }
+       // It should work with the same handle
+       classattrs.Handle = oldHandle
+       htbclassattrs.Rate = 4321000
+       class = NewHtbClass(classattrs, htbclassattrs)
+       if err := ClassChange(class); err != nil {
+               t.Fatal(err)
+       }
+
+       classes, err = ClassList(link, MakeHandle(0xffff, 0))
+       if err != nil {
+               t.Fatal(err)
+       }
+       if len(classes) != 1 {
+               t.Fatalf(
+                       "1 class expected, %d found",
+                       len(classes),
+               )
+       }
+
+       htb, ok = classes[0].(*HtbClass)
+       if !ok {
+               t.Fatal("Class is the wrong type")
+       }
+       // Verify that the rate value has changed.
+       if htb.Rate != class.Rate {
+               t.Fatal("Rate did not get changed while changing the class.")
+       }
+
+       // Check that we still have the netem child qdisc
+       qdiscs, err = QdiscList(link)
+       if err != nil {
+               t.Fatal(err)
+       }
+
+       if len(qdiscs) != 2 {
+               t.Fatalf("2 qdisc expected, %d found", len(qdiscs))
+       }
+       _, ok = qdiscs[0].(*Htb)
+       if !ok {
+               t.Fatal("Qdisc is the wrong type")
+       }
+
+       _, ok = qdiscs[1].(*Netem)
+       if !ok {
+               t.Fatal("Qdisc is the wrong type")
+       }
+
+       // Replace
+       // First replace by keeping the same handle, class will be changed.
+       // Then, replace by providing a new handle, n new class will be created.
+
+       // Replace acting as Change
+       class = NewHtbClass(classattrs, htbclassattrs)
+       if err := ClassReplace(class); err != nil {
+               t.Fatal("Failed to replace class that is existing.")
+       }
+
+       classes, err = ClassList(link, MakeHandle(0xffff, 0))
+       if err != nil {
+               t.Fatal(err)
+       }
+       if len(classes) != 1 {
+               t.Fatalf(
+                       "1 class expected, %d found",
+                       len(classes),
+               )
+       }
+
+       htb, ok = classes[0].(*HtbClass)
+       if !ok {
+               t.Fatal("Class is the wrong type")
+       }
+       // Verify that the rate value has changed.
+       if htb.Rate != class.Rate {
+               t.Fatal("Rate did not get changed while changing the class.")
+       }
+
+       // It should work with the same handle
+       classattrs.Handle = MakeHandle(0xffff, 3)
+       class = NewHtbClass(classattrs, htbclassattrs)
+       if err := ClassReplace(class); err != nil {
+               t.Fatal(err)
+       }
+
+       classes, err = ClassList(link, MakeHandle(0xffff, 0))
+       if err != nil {
+               t.Fatal(err)
+       }
+       if len(classes) != 2 {
+               t.Fatalf(
+                       "2 classes expected, %d found",
+                       len(classes),
+               )
+       }
+
+       htb, ok = classes[1].(*HtbClass)
+       if !ok {
+               t.Fatal("Class is the wrong type")
+       }
+       // Verify that the rate value has changed.
+       if htb.Rate != class.Rate {
+               t.Fatal("Rate did not get changed while changing the class.")
+       }
+
+       // Deletion
+       for _, class := range classes {
+               if err := ClassDel(class); err != nil {
+                       t.Fatal(err)
+               }
+       }
+
+       classes, err = ClassList(link, MakeHandle(0xffff, 0))
+       if err != nil {
+               t.Fatal(err)
+       }
+       if len(classes) != 0 {
+               t.Fatal("Failed to remove class")
+       }
+       if err := QdiscDel(qdisc); err != nil {
+               t.Fatal(err)
+       }
+       qdiscs, err = QdiscList(link)
+       if err != nil {
+               t.Fatal(err)
+       }
+       if len(qdiscs) != 0 {
+               t.Fatal("Failed to remove qdisc")
+       }
+}
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..20df903
--- /dev/null
@@ -0,0 +1,344 @@
+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 *ConntrackFilter) (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 *ConntrackFilter) (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 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
+}
diff --git a/vendor/github.com/vishvananda/netlink/conntrack_test.go b/vendor/github.com/vishvananda/netlink/conntrack_test.go
new file mode 100644 (file)
index 0000000..00b8060
--- /dev/null
@@ -0,0 +1,387 @@
+package netlink
+
+import (
+       "fmt"
+       "net"
+       "runtime"
+       "syscall"
+       "testing"
+
+       "github.com/vishvananda/netns"
+)
+
+func CheckErrorFail(t *testing.T, err error) {
+       if err != nil {
+               t.Fatalf("Fatal Error: %s", err)
+       }
+}
+func CheckError(t *testing.T, err error) {
+       if err != nil {
+               t.Errorf("Error: %s", err)
+       }
+}
+
+func udpFlowCreateProg(t *testing.T, flows, srcPort int, dstIP string, dstPort int) {
+       for i := 0; i < flows; i++ {
+               ServerAddr, err := net.ResolveUDPAddr("udp", fmt.Sprintf("%s:%d", dstIP, dstPort))
+               CheckError(t, err)
+
+               LocalAddr, err := net.ResolveUDPAddr("udp", fmt.Sprintf("127.0.0.1:%d", srcPort+i))
+               CheckError(t, err)
+
+               Conn, err := net.DialUDP("udp", LocalAddr, ServerAddr)
+               CheckError(t, err)
+
+               Conn.Write([]byte("Hello World"))
+               Conn.Close()
+       }
+}
+
+func nsCreateAndEnter(t *testing.T) (*netns.NsHandle, *netns.NsHandle, *Handle) {
+       // Lock the OS Thread so we don't accidentally switch namespaces
+       runtime.LockOSThread()
+
+       // Save the current network namespace
+       origns, _ := netns.Get()
+
+       ns, err := netns.New()
+       CheckErrorFail(t, err)
+
+       h, err := NewHandleAt(ns)
+       CheckErrorFail(t, err)
+
+       // Enter the new namespace
+       netns.Set(ns)
+
+       // Bing up loopback
+       link, _ := h.LinkByName("lo")
+       h.LinkSetUp(link)
+
+       return &origns, &ns, h
+}
+
+func applyFilter(flowList []ConntrackFlow, ipv4Filter *ConntrackFilter, ipv6Filter *ConntrackFilter) (ipv4Match, ipv6Match uint) {
+       for _, flow := range flowList {
+               if ipv4Filter.MatchConntrackFlow(&flow) == true {
+                       ipv4Match++
+               }
+               if ipv6Filter.MatchConntrackFlow(&flow) == true {
+                       ipv6Match++
+               }
+       }
+       return ipv4Match, ipv6Match
+}
+
+// TestConntrackSocket test the opening of a NETFILTER family socket
+func TestConntrackSocket(t *testing.T) {
+       skipUnlessRoot(t)
+
+       h, err := NewHandle(syscall.NETLINK_NETFILTER)
+       CheckErrorFail(t, err)
+
+       if h.SupportsNetlinkFamily(syscall.NETLINK_NETFILTER) != true {
+               t.Fatal("ERROR not supporting the NETFILTER family")
+       }
+}
+
+// TestConntrackTableList test the conntrack table list
+// Creates some flows and checks that they are correctly fetched from the conntrack table
+func TestConntrackTableList(t *testing.T) {
+       skipUnlessRoot(t)
+
+       // Creates a new namespace and bring up the loopback interface
+       origns, ns, h := nsCreateAndEnter(t)
+       defer netns.Set(*origns)
+       defer origns.Close()
+       defer ns.Close()
+       defer runtime.UnlockOSThread()
+
+       // Flush the table to start fresh
+       err := h.ConntrackTableFlush(ConntrackTable)
+       CheckErrorFail(t, err)
+
+       // Create 5 udp
+       udpFlowCreateProg(t, 5, 2000, "127.0.0.10", 3000)
+
+       // Fetch the conntrack table
+       flows, err := h.ConntrackTableList(ConntrackTable, syscall.AF_INET)
+       CheckErrorFail(t, err)
+
+       // Check that it is able to find the 5 flows created
+       var found int
+       for _, flow := range flows {
+               if flow.Forward.Protocol == 17 &&
+                       flow.Forward.DstIP.Equal(net.ParseIP("127.0.0.10")) &&
+                       flow.Forward.DstPort == 3000 &&
+                       (flow.Forward.SrcPort >= 2000 && flow.Forward.SrcPort <= 2005) {
+                       found++
+               }
+       }
+       if found != 5 {
+               t.Fatalf("Found only %d flows over 5", found)
+       }
+
+       // Give a try also to the IPv6 version
+       _, err = h.ConntrackTableList(ConntrackTable, syscall.AF_INET6)
+       CheckErrorFail(t, err)
+
+       // Switch back to the original namespace
+       netns.Set(*origns)
+}
+
+// TestConntrackTableFlush test the conntrack table flushing
+// Creates some flows and then call the table flush
+func TestConntrackTableFlush(t *testing.T) {
+       skipUnlessRoot(t)
+
+       // Creates a new namespace and bring up the loopback interface
+       origns, ns, h := nsCreateAndEnter(t)
+       defer netns.Set(*origns)
+       defer origns.Close()
+       defer ns.Close()
+       defer runtime.UnlockOSThread()
+
+       // Create 5 udp flows using netcat
+       udpFlowCreateProg(t, 5, 3000, "127.0.0.10", 4000)
+
+       // Fetch the conntrack table
+       flows, err := h.ConntrackTableList(ConntrackTable, syscall.AF_INET)
+       CheckErrorFail(t, err)
+
+       // Check that it is able to find the 5 flows created
+       var found int
+       for _, flow := range flows {
+               if flow.Forward.Protocol == 17 &&
+                       flow.Forward.DstIP.Equal(net.ParseIP("127.0.0.10")) &&
+                       flow.Forward.DstPort == 4000 &&
+                       (flow.Forward.SrcPort >= 3000 && flow.Forward.SrcPort <= 3005) {
+                       found++
+               }
+       }
+       if found != 5 {
+               t.Fatalf("Found only %d flows over 5", found)
+       }
+
+       // Flush the table
+       err = h.ConntrackTableFlush(ConntrackTable)
+       CheckErrorFail(t, err)
+
+       // Fetch again the flows to validate the flush
+       flows, err = h.ConntrackTableList(ConntrackTable, syscall.AF_INET)
+       CheckErrorFail(t, err)
+
+       // Check if it is still able to find the 5 flows created
+       found = 0
+       for _, flow := range flows {
+               if flow.Forward.Protocol == 17 &&
+                       flow.Forward.DstIP.Equal(net.ParseIP("127.0.0.10")) &&
+                       flow.Forward.DstPort == 4000 &&
+                       (flow.Forward.SrcPort >= 3000 && flow.Forward.SrcPort <= 3005) {
+                       found++
+               }
+       }
+       if found > 0 {
+               t.Fatalf("Found %d flows, they should had been flushed", found)
+       }
+
+       // Switch back to the original namespace
+       netns.Set(*origns)
+}
+
+// TestConntrackTableDelete tests the deletion with filter
+// Creates 2 group of flows then deletes only one group and validates the result
+func TestConntrackTableDelete(t *testing.T) {
+       skipUnlessRoot(t)
+
+       // Creates a new namespace and bring up the loopback interface
+       origns, ns, h := nsCreateAndEnter(t)
+       defer netns.Set(*origns)
+       defer origns.Close()
+       defer ns.Close()
+       defer runtime.UnlockOSThread()
+
+       // Create 10 udp flows
+       udpFlowCreateProg(t, 5, 5000, "127.0.0.10", 6000)
+       udpFlowCreateProg(t, 5, 7000, "127.0.0.20", 8000)
+
+       // Fetch the conntrack table
+       flows, err := h.ConntrackTableList(ConntrackTable, syscall.AF_INET)
+       CheckErrorFail(t, err)
+
+       // Check that it is able to find the 5 flows created for each group
+       var groupA int
+       var groupB int
+       for _, flow := range flows {
+               if flow.Forward.Protocol == 17 &&
+                       flow.Forward.DstIP.Equal(net.ParseIP("127.0.0.10")) &&
+                       flow.Forward.DstPort == 6000 &&
+                       (flow.Forward.SrcPort >= 5000 && flow.Forward.SrcPort <= 5005) {
+                       groupA++
+               }
+               if flow.Forward.Protocol == 17 &&
+                       flow.Forward.DstIP.Equal(net.ParseIP("127.0.0.20")) &&
+                       flow.Forward.DstPort == 8000 &&
+                       (flow.Forward.SrcPort >= 7000 && flow.Forward.SrcPort <= 7005) {
+                       groupB++
+               }
+       }
+       if groupA != 5 || groupB != 5 {
+               t.Fatalf("Flow creation issue groupA:%d, groupB:%d", groupA, groupB)
+       }
+
+       // Create a filter to erase groupB flows
+       filter := &ConntrackFilter{}
+       filter.AddIP(ConntrackOrigDstIP, net.ParseIP("127.0.0.20"))
+
+       // Flush entries of groupB
+       var deleted uint
+       if deleted, err = h.ConntrackDeleteFilter(ConntrackTable, syscall.AF_INET, filter); err != nil {
+               t.Fatalf("Error during the erase: %s", err)
+       }
+       if deleted != 5 {
+               t.Fatalf("Error deleted a wrong number of flows:%d instead of 5", deleted)
+       }
+
+       // Check again the table to verify that are gone
+       flows, err = h.ConntrackTableList(ConntrackTable, syscall.AF_INET)
+       CheckErrorFail(t, err)
+
+       // Check if it is able to find the 5 flows of groupA but none of groupB
+       groupA = 0
+       groupB = 0
+       for _, flow := range flows {
+               if flow.Forward.Protocol == 17 &&
+                       flow.Forward.DstIP.Equal(net.ParseIP("127.0.0.10")) &&
+                       flow.Forward.DstPort == 6000 &&
+                       (flow.Forward.SrcPort >= 5000 && flow.Forward.SrcPort <= 5005) {
+                       groupA++
+               }
+               if flow.Forward.Protocol == 17 &&
+                       flow.Forward.DstIP.Equal(net.ParseIP("127.0.0.20")) &&
+                       flow.Forward.DstPort == 8000 &&
+                       (flow.Forward.SrcPort >= 7000 && flow.Forward.SrcPort <= 7005) {
+                       groupB++
+               }
+       }
+       if groupA != 5 || groupB > 0 {
+               t.Fatalf("Error during the erase groupA:%d, groupB:%d", groupA, groupB)
+       }
+
+       // Switch back to the original namespace
+       netns.Set(*origns)
+}
+
+func TestConntrackFilter(t *testing.T) {
+       var flowList []ConntrackFlow
+       flowList = append(flowList, ConntrackFlow{
+               FamilyType: syscall.AF_INET,
+               Forward: ipTuple{
+                       SrcIP:   net.ParseIP("10.0.0.1"),
+                       DstIP:   net.ParseIP("20.0.0.1"),
+                       SrcPort: 1000,
+                       DstPort: 2000,
+               },
+               Reverse: ipTuple{
+                       SrcIP:   net.ParseIP("20.0.0.1"),
+                       DstIP:   net.ParseIP("192.168.1.1"),
+                       SrcPort: 2000,
+                       DstPort: 1000,
+               },
+       },
+               ConntrackFlow{
+                       FamilyType: syscall.AF_INET,
+                       Forward: ipTuple{
+                               SrcIP:   net.ParseIP("10.0.0.2"),
+                               DstIP:   net.ParseIP("20.0.0.2"),
+                               SrcPort: 5000,
+                               DstPort: 6000,
+                       },
+                       Reverse: ipTuple{
+                               SrcIP:   net.ParseIP("20.0.0.2"),
+                               DstIP:   net.ParseIP("192.168.1.1"),
+                               SrcPort: 6000,
+                               DstPort: 5000,
+                       },
+               },
+               ConntrackFlow{
+                       FamilyType: syscall.AF_INET6,
+                       Forward: ipTuple{
+                               SrcIP:   net.ParseIP("eeee:eeee:eeee:eeee:eeee:eeee:eeee:eeee"),
+                               DstIP:   net.ParseIP("dddd:dddd:dddd:dddd:dddd:dddd:dddd:dddd"),
+                               SrcPort: 1000,
+                               DstPort: 2000,
+                       },
+                       Reverse: ipTuple{
+                               SrcIP:   net.ParseIP("dddd:dddd:dddd:dddd:dddd:dddd:dddd:dddd"),
+                               DstIP:   net.ParseIP("eeee:eeee:eeee:eeee:eeee:eeee:eeee:eeee"),
+                               SrcPort: 2000,
+                               DstPort: 1000,
+                       },
+               })
+
+       // Empty filter
+       v4Match, v6Match := applyFilter(flowList, &ConntrackFilter{}, &ConntrackFilter{})
+       if v4Match > 0 || v6Match > 0 {
+               t.Fatalf("Error, empty filter cannot match, v4:%d, v6:%d", v4Match, v6Match)
+       }
+
+       // SrcIP filter
+       filterV4 := &ConntrackFilter{}
+       filterV4.AddIP(ConntrackOrigSrcIP, net.ParseIP("10.0.0.1"))
+
+       filterV6 := &ConntrackFilter{}
+       filterV6.AddIP(ConntrackOrigSrcIP, net.ParseIP("eeee:eeee:eeee:eeee:eeee:eeee:eeee:eeee"))
+
+       v4Match, v6Match = applyFilter(flowList, filterV4, filterV6)
+       if v4Match != 1 || v6Match != 1 {
+               t.Fatalf("Error, there should be only 1 match, v4:%d, v6:%d", v4Match, v6Match)
+       }
+
+       // DstIp filter
+       filterV4 = &ConntrackFilter{}
+       filterV4.AddIP(ConntrackOrigDstIP, net.ParseIP("20.0.0.1"))
+
+       filterV6 = &ConntrackFilter{}
+       filterV6.AddIP(ConntrackOrigDstIP, net.ParseIP("dddd:dddd:dddd:dddd:dddd:dddd:dddd:dddd"))
+
+       v4Match, v6Match = applyFilter(flowList, filterV4, filterV6)
+       if v4Match != 1 || v6Match != 1 {
+               t.Fatalf("Error, there should be only 1 match, v4:%d, v6:%d", v4Match, v6Match)
+       }
+
+       // SrcIP for NAT
+       filterV4 = &ConntrackFilter{}
+       filterV4.AddIP(ConntrackNatSrcIP, net.ParseIP("20.0.0.1"))
+
+       filterV6 = &ConntrackFilter{}
+       filterV6.AddIP(ConntrackNatSrcIP, net.ParseIP("dddd:dddd:dddd:dddd:dddd:dddd:dddd:dddd"))
+
+       v4Match, v6Match = applyFilter(flowList, filterV4, filterV6)
+       if v4Match != 1 || v6Match != 1 {
+               t.Fatalf("Error, there should be only 1 match, v4:%d, v6:%d", v4Match, v6Match)
+       }
+
+       // DstIP for NAT
+       filterV4 = &ConntrackFilter{}
+       filterV4.AddIP(ConntrackNatDstIP, net.ParseIP("192.168.1.1"))
+
+       filterV6 = &ConntrackFilter{}
+       filterV6.AddIP(ConntrackNatDstIP, net.ParseIP("dddd:dddd:dddd:dddd:dddd:dddd:dddd:dddd"))
+
+       v4Match, v6Match = applyFilter(flowList, filterV4, filterV6)
+       if v4Match != 2 || v6Match != 0 {
+               t.Fatalf("Error, there should be an exact match, v4:%d, v6:%d", v4Match, v6Match)
+       }
+
+       // AnyIp for Nat
+       filterV4 = &ConntrackFilter{}
+       filterV4.AddIP(ConntrackNatAnyIP, net.ParseIP("192.168.1.1"))
+
+       filterV6 = &ConntrackFilter{}
+       filterV6.AddIP(ConntrackNatAnyIP, net.ParseIP("eeee:eeee:eeee:eeee:eeee:eeee:eeee:eeee"))
+
+       v4Match, v6Match = applyFilter(flowList, filterV4, filterV6)
+       if v4Match != 2 || v6Match != 1 {
+               t.Fatalf("Error, there should be an exact match, v4:%d, v6:%d", v4Match, v6Match)
+       }
+}
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..a0e000c 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 _, key := range sel.Keys {
+                               key.Mask = native.Uint32(htonl(key.Mask))
+                               key.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 _, key := range u32.Sel.Keys {
+                                       key.Mask = native.Uint32(htonl(key.Mask))
+                                       key.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/filter_test.go b/vendor/github.com/vishvananda/netlink/filter_test.go
new file mode 100644 (file)
index 0000000..64ab757
--- /dev/null
@@ -0,0 +1,644 @@
+// +build linux
+
+package netlink
+
+import (
+       "syscall"
+       "testing"
+)
+
+func TestFilterAddDel(t *testing.T) {
+       tearDown := setUpNetlinkTest(t)
+       defer tearDown()
+       if err := LinkAdd(&Ifb{LinkAttrs{Name: "foo"}}); err != nil {
+               t.Fatal(err)
+       }
+       if err := LinkAdd(&Ifb{LinkAttrs{Name: "bar"}}); err != nil {
+               t.Fatal(err)
+       }
+       link, err := LinkByName("foo")
+       if err != nil {
+               t.Fatal(err)
+       }
+       if err := LinkSetUp(link); err != nil {
+               t.Fatal(err)
+       }
+       redir, err := LinkByName("bar")
+       if err != nil {
+               t.Fatal(err)
+       }
+       if err := LinkSetUp(redir); err != nil {
+               t.Fatal(err)
+       }
+       qdisc := &Ingress{
+               QdiscAttrs: QdiscAttrs{
+                       LinkIndex: link.Attrs().Index,
+                       Handle:    MakeHandle(0xffff, 0),
+                       Parent:    HANDLE_INGRESS,
+               },
+       }
+       if err := QdiscAdd(qdisc); err != nil {
+               t.Fatal(err)
+       }
+       qdiscs, err := QdiscList(link)
+       if err != nil {
+               t.Fatal(err)
+       }
+       if len(qdiscs) != 1 {
+               t.Fatal("Failed to add qdisc")
+       }
+       _, ok := qdiscs[0].(*Ingress)
+       if !ok {
+               t.Fatal("Qdisc is the wrong type")
+       }
+       classId := MakeHandle(1, 1)
+       filter := &U32{
+               FilterAttrs: FilterAttrs{
+                       LinkIndex: link.Attrs().Index,
+                       Parent:    MakeHandle(0xffff, 0),
+                       Priority:  1,
+                       Protocol:  syscall.ETH_P_IP,
+               },
+               RedirIndex: redir.Attrs().Index,
+               ClassId:    classId,
+       }
+       if err := FilterAdd(filter); err != nil {
+               t.Fatal(err)
+       }
+       filters, err := FilterList(link, MakeHandle(0xffff, 0))
+       if err != nil {
+               t.Fatal(err)
+       }
+       if len(filters) != 1 {
+               t.Fatal("Failed to add filter")
+       }
+       u32, ok := filters[0].(*U32)
+       if !ok {
+               t.Fatal("Filter is the wrong type")
+       }
+       if u32.ClassId != classId {
+               t.Fatalf("ClassId of the filter is the wrong value")
+       }
+       if err := FilterDel(filter); err != nil {
+               t.Fatal(err)
+       }
+       filters, err = FilterList(link, MakeHandle(0xffff, 0))
+       if err != nil {
+               t.Fatal(err)
+       }
+       if len(filters) != 0 {
+               t.Fatal("Failed to remove filter")
+       }
+       if err := QdiscDel(qdisc); err != nil {
+               t.Fatal(err)
+       }
+       qdiscs, err = QdiscList(link)
+       if err != nil {
+               t.Fatal(err)
+       }
+       if len(qdiscs) != 0 {
+               t.Fatal("Failed to remove qdisc")
+       }
+}
+
+func TestAdvancedFilterAddDel(t *testing.T) {
+       tearDown := setUpNetlinkTest(t)
+       defer tearDown()
+       if err := LinkAdd(&Ifb{LinkAttrs{Name: "baz"}}); err != nil {
+               t.Fatal(err)
+       }
+       link, err := LinkByName("baz")
+       if err != nil {
+               t.Fatal(err)
+       }
+       if err := LinkSetUp(link); err != nil {
+               t.Fatal(err)
+       }
+       index := link.Attrs().Index
+
+       qdiscHandle := MakeHandle(0x1, 0x0)
+       qdiscAttrs := QdiscAttrs{
+               LinkIndex: index,
+               Handle:    qdiscHandle,
+               Parent:    HANDLE_ROOT,
+       }
+
+       qdisc := NewHtb(qdiscAttrs)
+       if err := QdiscAdd(qdisc); err != nil {
+               t.Fatal(err)
+       }
+       qdiscs, err := QdiscList(link)
+       if err != nil {
+               t.Fatal(err)
+       }
+       if len(qdiscs) != 1 {
+               t.Fatal("Failed to add qdisc")
+       }
+       _, ok := qdiscs[0].(*Htb)
+       if !ok {
+               t.Fatal("Qdisc is the wrong type")
+       }
+
+       classId := MakeHandle(0x1, 0x46cb)
+       classAttrs := ClassAttrs{
+               LinkIndex: index,
+               Parent:    qdiscHandle,
+               Handle:    classId,
+       }
+       htbClassAttrs := HtbClassAttrs{
+               Rate:   512 * 1024,
+               Buffer: 32 * 1024,
+       }
+       htbClass := NewHtbClass(classAttrs, htbClassAttrs)
+       if err = ClassReplace(htbClass); err != nil {
+               t.Fatalf("Failed to add a HTB class: %v", err)
+       }
+       classes, err := ClassList(link, qdiscHandle)
+       if err != nil {
+               t.Fatal(err)
+       }
+       if len(classes) != 1 {
+               t.Fatal("Failed to add class")
+       }
+       _, ok = classes[0].(*HtbClass)
+       if !ok {
+               t.Fatal("Class is the wrong type")
+       }
+
+       u32SelKeys := []TcU32Key{
+               TcU32Key{
+                       Mask:    0xff,
+                       Val:     80,
+                       Off:     20,
+                       OffMask: 0,
+               },
+               TcU32Key{
+                       Mask:    0xffff,
+                       Val:     0x146ca,
+                       Off:     32,
+                       OffMask: 0,
+               },
+       }
+       filter := &U32{
+               FilterAttrs: FilterAttrs{
+                       LinkIndex: index,
+                       Parent:    qdiscHandle,
+                       Priority:  1,
+                       Protocol:  syscall.ETH_P_ALL,
+               },
+               Sel: &TcU32Sel{
+                       Keys:  u32SelKeys,
+                       Flags: TC_U32_TERMINAL,
+               },
+               ClassId: classId,
+               Actions: []Action{},
+       }
+       if err := FilterAdd(filter); err != nil {
+               t.Fatal(err)
+       }
+       filters, err := FilterList(link, qdiscHandle)
+       if err != nil {
+               t.Fatal(err)
+       }
+       if len(filters) != 1 {
+               t.Fatal("Failed to add filter")
+       }
+
+       u32, ok := filters[0].(*U32)
+       if !ok {
+               t.Fatal("Filter is the wrong type")
+       }
+       // Endianness checks
+       if u32.Sel.Offmask != filter.Sel.Offmask {
+               t.Fatal("The endianness of TcU32Key.Sel.Offmask is wrong")
+       }
+       if u32.Sel.Hmask != filter.Sel.Hmask {
+               t.Fatal("The endianness of TcU32Key.Sel.Hmask is wrong")
+       }
+       for i, key := range u32.Sel.Keys {
+               if key.Mask != filter.Sel.Keys[i].Mask {
+                       t.Fatal("The endianness of TcU32Key.Mask is wrong")
+               }
+               if key.Val != filter.Sel.Keys[i].Val {
+                       t.Fatal("The endianness of TcU32Key.Val is wrong")
+               }
+       }
+
+       if err := FilterDel(filter); err != nil {
+               t.Fatal(err)
+       }
+       filters, err = FilterList(link, qdiscHandle)
+       if err != nil {
+               t.Fatal(err)
+       }
+       if len(filters) != 0 {
+               t.Fatal("Failed to remove filter")
+       }
+
+       if err = ClassDel(htbClass); err != nil {
+               t.Fatalf("Failed to delete a HTP class: %v", err)
+       }
+       classes, err = ClassList(link, qdiscHandle)
+       if err != nil {
+               t.Fatal(err)
+       }
+       if len(classes) != 0 {
+               t.Fatal("Failed to remove class")
+       }
+
+       if err := QdiscDel(qdisc); err != nil {
+               t.Fatal(err)
+       }
+       qdiscs, err = QdiscList(link)
+       if err != nil {
+               t.Fatal(err)
+       }
+       if len(qdiscs) != 0 {
+               t.Fatal("Failed to remove qdisc")
+       }
+}
+
+func TestFilterFwAddDel(t *testing.T) {
+       tearDown := setUpNetlinkTest(t)
+       defer tearDown()
+       if err := LinkAdd(&Ifb{LinkAttrs{Name: "foo"}}); err != nil {
+               t.Fatal(err)
+       }
+       if err := LinkAdd(&Ifb{LinkAttrs{Name: "bar"}}); err != nil {
+               t.Fatal(err)
+       }
+       link, err := LinkByName("foo")
+       if err != nil {
+               t.Fatal(err)
+       }
+       if err := LinkSetUp(link); err != nil {
+               t.Fatal(err)
+       }
+       redir, err := LinkByName("bar")
+       if err != nil {
+               t.Fatal(err)
+       }
+       if err := LinkSetUp(redir); err != nil {
+               t.Fatal(err)
+       }
+       attrs := QdiscAttrs{
+               LinkIndex: link.Attrs().Index,
+               Handle:    MakeHandle(0xffff, 0),
+               Parent:    HANDLE_ROOT,
+       }
+       qdisc := NewHtb(attrs)
+       if err := QdiscAdd(qdisc); err != nil {
+               t.Fatal(err)
+       }
+       qdiscs, err := QdiscList(link)
+       if err != nil {
+               t.Fatal(err)
+       }
+       if len(qdiscs) != 1 {
+               t.Fatal("Failed to add qdisc")
+       }
+       _, ok := qdiscs[0].(*Htb)
+       if !ok {
+               t.Fatal("Qdisc is the wrong type")
+       }
+
+       classattrs := ClassAttrs{
+               LinkIndex: link.Attrs().Index,
+               Parent:    MakeHandle(0xffff, 0),
+               Handle:    MakeHandle(0xffff, 2),
+       }
+
+       htbclassattrs := HtbClassAttrs{
+               Rate:    1234000,
+               Cbuffer: 1690,
+       }
+       class := NewHtbClass(classattrs, htbclassattrs)
+       if err := ClassAdd(class); err != nil {
+               t.Fatal(err)
+       }
+       classes, err := ClassList(link, MakeHandle(0xffff, 2))
+       if err != nil {
+               t.Fatal(err)
+       }
+       if len(classes) != 1 {
+               t.Fatal("Failed to add class")
+       }
+
+       filterattrs := FilterAttrs{
+               LinkIndex: link.Attrs().Index,
+               Parent:    MakeHandle(0xffff, 0),
+               Handle:    MakeHandle(0, 0x6),
+               Priority:  1,
+               Protocol:  syscall.ETH_P_IP,
+       }
+       fwattrs := FilterFwAttrs{
+               Buffer:   12345,
+               Rate:     1234,
+               PeakRate: 2345,
+               Action:   TC_POLICE_SHOT,
+               ClassId:  MakeHandle(0xffff, 2),
+       }
+
+       filter, err := NewFw(filterattrs, fwattrs)
+       if err != nil {
+               t.Fatal(err)
+       }
+
+       if err := FilterAdd(filter); err != nil {
+               t.Fatal(err)
+       }
+
+       filters, err := FilterList(link, MakeHandle(0xffff, 0))
+       if err != nil {
+               t.Fatal(err)
+       }
+       if len(filters) != 1 {
+               t.Fatal("Failed to add filter")
+       }
+       fw, ok := filters[0].(*Fw)
+       if !ok {
+               t.Fatal("Filter is the wrong type")
+       }
+       if fw.Police.Rate.Rate != filter.Police.Rate.Rate {
+               t.Fatal("Police Rate doesn't match")
+       }
+       for i := range fw.Rtab {
+               if fw.Rtab[i] != filter.Rtab[i] {
+                       t.Fatal("Rtab doesn't match")
+               }
+               if fw.Ptab[i] != filter.Ptab[i] {
+                       t.Fatal("Ptab doesn't match")
+               }
+       }
+       if fw.ClassId != filter.ClassId {
+               t.Fatal("ClassId doesn't match")
+       }
+       if fw.InDev != filter.InDev {
+               t.Fatal("InDev doesn't match")
+       }
+       if fw.AvRate != filter.AvRate {
+               t.Fatal("AvRate doesn't match")
+       }
+
+       if err := FilterDel(filter); err != nil {
+               t.Fatal(err)
+       }
+       filters, err = FilterList(link, MakeHandle(0xffff, 0))
+       if err != nil {
+               t.Fatal(err)
+       }
+       if len(filters) != 0 {
+               t.Fatal("Failed to remove filter")
+       }
+       if err := ClassDel(class); err != nil {
+               t.Fatal(err)
+       }
+       classes, err = ClassList(link, MakeHandle(0xffff, 0))
+       if err != nil {
+               t.Fatal(err)
+       }
+       if len(classes) != 0 {
+               t.Fatal("Failed to remove class")
+       }
+
+       if err := QdiscDel(qdisc); err != nil {
+               t.Fatal(err)
+       }
+       qdiscs, err = QdiscList(link)
+       if err != nil {
+               t.Fatal(err)
+       }
+       if len(qdiscs) != 0 {
+               t.Fatal("Failed to remove qdisc")
+       }
+}
+
+func TestFilterU32BpfAddDel(t *testing.T) {
+       tearDown := setUpNetlinkTest(t)
+       defer tearDown()
+       if err := LinkAdd(&Ifb{LinkAttrs{Name: "foo"}}); err != nil {
+               t.Fatal(err)
+       }
+       if err := LinkAdd(&Ifb{LinkAttrs{Name: "bar"}}); err != nil {
+               t.Fatal(err)
+       }
+       link, err := LinkByName("foo")
+       if err != nil {
+               t.Fatal(err)
+       }
+       if err := LinkSetUp(link); err != nil {
+               t.Fatal(err)
+       }
+       redir, err := LinkByName("bar")
+       if err != nil {
+               t.Fatal(err)
+       }
+       if err := LinkSetUp(redir); err != nil {
+               t.Fatal(err)
+       }
+       qdisc := &Ingress{
+               QdiscAttrs: QdiscAttrs{
+                       LinkIndex: link.Attrs().Index,
+                       Handle:    MakeHandle(0xffff, 0),
+                       Parent:    HANDLE_INGRESS,
+               },
+       }
+       if err := QdiscAdd(qdisc); err != nil {
+               t.Fatal(err)
+       }
+       qdiscs, err := QdiscList(link)
+       if err != nil {
+               t.Fatal(err)
+       }
+       if len(qdiscs) != 1 {
+               t.Fatal("Failed to add qdisc")
+       }
+       _, ok := qdiscs[0].(*Ingress)
+       if !ok {
+               t.Fatal("Qdisc is the wrong type")
+       }
+
+       fd, err := loadSimpleBpf(BPF_PROG_TYPE_SCHED_ACT, 1)
+       if err != nil {
+               t.Skipf("Loading bpf program failed: %s", err)
+       }
+       classId := MakeHandle(1, 1)
+       filter := &U32{
+               FilterAttrs: FilterAttrs{
+                       LinkIndex: link.Attrs().Index,
+                       Parent:    MakeHandle(0xffff, 0),
+                       Priority:  1,
+                       Protocol:  syscall.ETH_P_ALL,
+               },
+               ClassId: classId,
+               Actions: []Action{
+                       &BpfAction{Fd: fd, Name: "simple"},
+                       &MirredAction{
+                               ActionAttrs: ActionAttrs{
+                                       Action: TC_ACT_STOLEN,
+                               },
+                               MirredAction: TCA_EGRESS_REDIR,
+                               Ifindex:      redir.Attrs().Index,
+                       },
+               },
+       }
+
+       if err := FilterAdd(filter); err != nil {
+               t.Fatal(err)
+       }
+
+       filters, err := FilterList(link, MakeHandle(0xffff, 0))
+       if err != nil {
+               t.Fatal(err)
+       }
+       if len(filters) != 1 {
+               t.Fatal("Failed to add filter")
+       }
+       u32, ok := filters[0].(*U32)
+       if !ok {
+               t.Fatal("Filter is the wrong type")
+       }
+
+       if len(u32.Actions) != 2 {
+               t.Fatalf("Too few Actions in filter")
+       }
+       if u32.ClassId != classId {
+               t.Fatalf("ClassId of the filter is the wrong value")
+       }
+       bpfAction, ok := u32.Actions[0].(*BpfAction)
+       if !ok {
+               t.Fatal("Action[0] is the wrong type")
+       }
+       if bpfAction.Fd != fd {
+               t.Fatal("Action Fd does not match")
+       }
+       if _, ok := u32.Actions[1].(*MirredAction); !ok {
+               t.Fatal("Action[1] is the wrong type")
+       }
+
+       if err := FilterDel(filter); err != nil {
+               t.Fatal(err)
+       }
+       filters, err = FilterList(link, MakeHandle(0xffff, 0))
+       if err != nil {
+               t.Fatal(err)
+       }
+       if len(filters) != 0 {
+               t.Fatal("Failed to remove filter")
+       }
+
+       if err := QdiscDel(qdisc); err != nil {
+               t.Fatal(err)
+       }
+       qdiscs, err = QdiscList(link)
+       if err != nil {
+               t.Fatal(err)
+       }
+       if len(qdiscs) != 0 {
+               t.Fatal("Failed to remove qdisc")
+       }
+}
+
+func TestFilterClsActBpfAddDel(t *testing.T) {
+       tearDown := setUpNetlinkTest(t)
+       defer tearDown()
+       if err := LinkAdd(&Ifb{LinkAttrs{Name: "foo"}}); err != nil {
+               t.Fatal(err)
+       }
+       link, err := LinkByName("foo")
+       if err != nil {
+               t.Fatal(err)
+       }
+       if err := LinkSetUp(link); err != nil {
+               t.Fatal(err)
+       }
+       attrs := QdiscAttrs{
+               LinkIndex: link.Attrs().Index,
+               Handle:    MakeHandle(0xffff, 0),
+               Parent:    HANDLE_CLSACT,
+       }
+       qdisc := &GenericQdisc{
+               QdiscAttrs: attrs,
+               QdiscType:  "clsact",
+       }
+       // This feature was added in kernel 4.5
+       if err := QdiscAdd(qdisc); err != nil {
+               t.Skipf("Failed adding clsact qdisc, unsupported kernel")
+       }
+       qdiscs, err := QdiscList(link)
+       if err != nil {
+               t.Fatal(err)
+       }
+       if len(qdiscs) != 1 {
+               t.Fatal("Failed to add qdisc")
+       }
+       if q, ok := qdiscs[0].(*GenericQdisc); !ok || q.Type() != "clsact" {
+               t.Fatal("qdisc is the wrong type")
+       }
+
+       filterattrs := FilterAttrs{
+               LinkIndex: link.Attrs().Index,
+               Parent:    HANDLE_MIN_EGRESS,
+               Handle:    MakeHandle(0, 1),
+               Protocol:  syscall.ETH_P_ALL,
+               Priority:  1,
+       }
+       fd, err := loadSimpleBpf(BPF_PROG_TYPE_SCHED_CLS, 1)
+       if err != nil {
+               t.Skipf("Loading bpf program failed: %s", err)
+       }
+       filter := &BpfFilter{
+               FilterAttrs:  filterattrs,
+               Fd:           fd,
+               Name:         "simple",
+               DirectAction: true,
+       }
+       if filter.Fd < 0 {
+               t.Skipf("Failed to load bpf program")
+       }
+
+       if err := FilterAdd(filter); err != nil {
+               t.Fatal(err)
+       }
+
+       filters, err := FilterList(link, HANDLE_MIN_EGRESS)
+       if err != nil {
+               t.Fatal(err)
+       }
+       if len(filters) != 1 {
+               t.Fatal("Failed to add filter")
+       }
+       bpf, ok := filters[0].(*BpfFilter)
+       if !ok {
+               t.Fatal("Filter is the wrong type")
+       }
+
+       if bpf.Fd != filter.Fd {
+               t.Fatal("Filter Fd does not match")
+       }
+       if bpf.DirectAction != filter.DirectAction {
+               t.Fatal("Filter DirectAction does not match")
+       }
+
+       if err := FilterDel(filter); err != nil {
+               t.Fatal(err)
+       }
+       filters, err = FilterList(link, HANDLE_MIN_EGRESS)
+       if err != nil {
+               t.Fatal(err)
+       }
+       if len(filters) != 0 {
+               t.Fatal("Failed to remove filter")
+       }
+
+       if err := QdiscDel(qdisc); err != nil {
+               t.Fatal(err)
+       }
+       qdiscs, err = QdiscList(link)
+       if err != nil {
+               t.Fatal(err)
+       }
+       if len(qdiscs) != 0 {
+               t.Fatal("Failed to remove qdisc")
+       }
+}
diff --git a/vendor/github.com/vishvananda/netlink/handle_test.go b/vendor/github.com/vishvananda/netlink/handle_test.go
new file mode 100644 (file)
index 0000000..e7a7f86
--- /dev/null
@@ -0,0 +1,343 @@
+// +build linux
+
+package netlink
+
+import (
+       "crypto/rand"
+       "encoding/hex"
+       "fmt"
+       "io"
+       "net"
+       "sync"
+       "sync/atomic"
+       "syscall"
+       "testing"
+       "time"
+       "unsafe"
+
+       "github.com/vishvananda/netlink/nl"
+       "github.com/vishvananda/netns"
+)
+
+func TestHandleCreateDelete(t *testing.T) {
+       h, err := NewHandle()
+       if err != nil {
+               t.Fatal(err)
+       }
+       for _, f := range nl.SupportedNlFamilies {
+               sh, ok := h.sockets[f]
+               if !ok {
+                       t.Fatalf("Handle socket(s) for family %d was not created", f)
+               }
+               if sh.Socket == nil {
+                       t.Fatalf("Socket for family %d was not created", f)
+               }
+       }
+
+       h.Delete()
+       if h.sockets != nil {
+               t.Fatalf("Handle socket(s) were not destroyed")
+       }
+}
+
+func TestHandleCreateNetns(t *testing.T) {
+       skipUnlessRoot(t)
+
+       id := make([]byte, 4)
+       if _, err := io.ReadFull(rand.Reader, id); err != nil {
+               t.Fatal(err)
+       }
+       ifName := "dummy-" + hex.EncodeToString(id)
+
+       // Create an handle on the current netns
+       curNs, err := netns.Get()
+       if err != nil {
+               t.Fatal(err)
+       }
+       defer curNs.Close()
+
+       ch, err := NewHandleAt(curNs)
+       if err != nil {
+               t.Fatal(err)
+       }
+       defer ch.Delete()
+
+       // Create an handle on a custom netns
+       newNs, err := netns.New()
+       if err != nil {
+               t.Fatal(err)
+       }
+       defer newNs.Close()
+
+       nh, err := NewHandleAt(newNs)
+       if err != nil {
+               t.Fatal(err)
+       }
+       defer nh.Delete()
+
+       // Create an interface using the current handle
+       err = ch.LinkAdd(&Dummy{LinkAttrs{Name: ifName}})
+       if err != nil {
+               t.Fatal(err)
+       }
+       l, err := ch.LinkByName(ifName)
+       if err != nil {
+               t.Fatal(err)
+       }
+       if l.Type() != "dummy" {
+               t.Fatalf("Unexpected link type: %s", l.Type())
+       }
+
+       // Verify the new handle cannot find the interface
+       ll, err := nh.LinkByName(ifName)
+       if err == nil {
+               t.Fatalf("Unexpected link found on netns %s: %v", newNs, ll)
+       }
+
+       // Move the interface to the new netns
+       err = ch.LinkSetNsFd(l, int(newNs))
+       if err != nil {
+               t.Fatal(err)
+       }
+
+       // Verify new netns handle can find the interface while current cannot
+       ll, err = nh.LinkByName(ifName)
+       if err != nil {
+               t.Fatal(err)
+       }
+       if ll.Type() != "dummy" {
+               t.Fatalf("Unexpected link type: %s", ll.Type())
+       }
+       ll, err = ch.LinkByName(ifName)
+       if err == nil {
+               t.Fatalf("Unexpected link found on netns %s: %v", curNs, ll)
+       }
+}
+
+func TestHandleTimeout(t *testing.T) {
+       h, err := NewHandle()
+       if err != nil {
+               t.Fatal(err)
+       }
+       defer h.Delete()
+
+       for _, sh := range h.sockets {
+               verifySockTimeVal(t, sh.Socket.GetFd(), syscall.Timeval{Sec: 0, Usec: 0})
+       }
+
+       h.SetSocketTimeout(2*time.Second + 8*time.Millisecond)
+
+       for _, sh := range h.sockets {
+               verifySockTimeVal(t, sh.Socket.GetFd(), syscall.Timeval{Sec: 2, Usec: 8000})
+       }
+}
+
+func verifySockTimeVal(t *testing.T, fd int, tv syscall.Timeval) {
+       var (
+               tr syscall.Timeval
+               v  = uint32(0x10)
+       )
+       _, _, errno := syscall.Syscall6(syscall.SYS_GETSOCKOPT, uintptr(fd), syscall.SOL_SOCKET, syscall.SO_SNDTIMEO, uintptr(unsafe.Pointer(&tr)), uintptr(unsafe.Pointer(&v)), 0)
+       if errno != 0 {
+               t.Fatal(errno)
+       }
+
+       if tr.Sec != tv.Sec || tr.Usec != tv.Usec {
+               t.Fatalf("Unexpected timeout value read: %v. Expected: %v", tr, tv)
+       }
+
+       _, _, errno = syscall.Syscall6(syscall.SYS_GETSOCKOPT, uintptr(fd), syscall.SOL_SOCKET, syscall.SO_RCVTIMEO, uintptr(unsafe.Pointer(&tr)), uintptr(unsafe.Pointer(&v)), 0)
+       if errno != 0 {
+               t.Fatal(errno)
+       }
+
+       if tr.Sec != tv.Sec || tr.Usec != tv.Usec {
+               t.Fatalf("Unexpected timeout value read: %v. Expected: %v", tr, tv)
+       }
+}
+
+var (
+       iter      = 10
+       numThread = uint32(4)
+       prefix    = "iface"
+       handle1   *Handle
+       handle2   *Handle
+       ns1       netns.NsHandle
+       ns2       netns.NsHandle
+       done      uint32
+       initError error
+       once      sync.Once
+)
+
+func getXfrmState(thread int) *XfrmState {
+       return &XfrmState{
+               Src:   net.IPv4(byte(192), byte(168), 1, byte(1+thread)),
+               Dst:   net.IPv4(byte(192), byte(168), 2, byte(1+thread)),
+               Proto: XFRM_PROTO_AH,
+               Mode:  XFRM_MODE_TUNNEL,
+               Spi:   thread,
+               Auth: &XfrmStateAlgo{
+                       Name: "hmac(sha256)",
+                       Key:  []byte("abcdefghijklmnopqrstuvwzyzABCDEF"),
+               },
+       }
+}
+
+func getXfrmPolicy(thread int) *XfrmPolicy {
+       return &XfrmPolicy{
+               Src:     &net.IPNet{IP: net.IPv4(byte(10), byte(10), byte(thread), 0), Mask: []byte{255, 255, 255, 0}},
+               Dst:     &net.IPNet{IP: net.IPv4(byte(10), byte(10), byte(thread), 0), Mask: []byte{255, 255, 255, 0}},
+               Proto:   17,
+               DstPort: 1234,
+               SrcPort: 5678,
+               Dir:     XFRM_DIR_OUT,
+               Tmpls: []XfrmPolicyTmpl{
+                       {
+                               Src:   net.IPv4(byte(192), byte(168), 1, byte(thread)),
+                               Dst:   net.IPv4(byte(192), byte(168), 2, byte(thread)),
+                               Proto: XFRM_PROTO_ESP,
+                               Mode:  XFRM_MODE_TUNNEL,
+                       },
+               },
+       }
+}
+func initParallel() {
+       ns1, initError = netns.New()
+       if initError != nil {
+               return
+       }
+       handle1, initError = NewHandleAt(ns1)
+       if initError != nil {
+               return
+       }
+       ns2, initError = netns.New()
+       if initError != nil {
+               return
+       }
+       handle2, initError = NewHandleAt(ns2)
+       if initError != nil {
+               return
+       }
+}
+
+func parallelDone() {
+       atomic.AddUint32(&done, 1)
+       if done == numThread {
+               if ns1.IsOpen() {
+                       ns1.Close()
+               }
+               if ns2.IsOpen() {
+                       ns2.Close()
+               }
+               if handle1 != nil {
+                       handle1.Delete()
+               }
+               if handle2 != nil {
+                       handle2.Delete()
+               }
+       }
+}
+
+// Do few route and xfrm operation on the two handles in parallel
+func runParallelTests(t *testing.T, thread int) {
+       skipUnlessRoot(t)
+       defer parallelDone()
+
+       t.Parallel()
+
+       once.Do(initParallel)
+       if initError != nil {
+               t.Fatal(initError)
+       }
+
+       state := getXfrmState(thread)
+       policy := getXfrmPolicy(thread)
+       for i := 0; i < iter; i++ {
+               ifName := fmt.Sprintf("%s_%d_%d", prefix, thread, i)
+               link := &Dummy{LinkAttrs{Name: ifName}}
+               err := handle1.LinkAdd(link)
+               if err != nil {
+                       t.Fatal(err)
+               }
+               l, err := handle1.LinkByName(ifName)
+               if err != nil {
+                       t.Fatal(err)
+               }
+               err = handle1.LinkSetUp(l)
+               if err != nil {
+                       t.Fatal(err)
+               }
+               handle1.LinkSetNsFd(l, int(ns2))
+               if err != nil {
+                       t.Fatal(err)
+               }
+               err = handle1.XfrmStateAdd(state)
+               if err != nil {
+                       t.Fatal(err)
+               }
+               err = handle1.XfrmPolicyAdd(policy)
+               if err != nil {
+                       t.Fatal(err)
+               }
+               err = handle2.LinkSetDown(l)
+               if err != nil {
+                       t.Fatal(err)
+               }
+               err = handle2.XfrmStateAdd(state)
+               if err != nil {
+                       t.Fatal(err)
+               }
+               err = handle2.XfrmPolicyAdd(policy)
+               if err != nil {
+                       t.Fatal(err)
+               }
+               _, err = handle2.LinkByName(ifName)
+               if err != nil {
+                       t.Fatal(err)
+               }
+               handle2.LinkSetNsFd(l, int(ns1))
+               if err != nil {
+                       t.Fatal(err)
+               }
+               err = handle1.LinkSetUp(l)
+               if err != nil {
+                       t.Fatal(err)
+               }
+               l, err = handle1.LinkByName(ifName)
+               if err != nil {
+                       t.Fatal(err)
+               }
+               err = handle1.XfrmPolicyDel(policy)
+               if err != nil {
+                       t.Fatal(err)
+               }
+               err = handle2.XfrmPolicyDel(policy)
+               if err != nil {
+                       t.Fatal(err)
+               }
+               err = handle1.XfrmStateDel(state)
+               if err != nil {
+                       t.Fatal(err)
+               }
+               err = handle2.XfrmStateDel(state)
+               if err != nil {
+                       t.Fatal(err)
+               }
+       }
+}
+
+func TestHandleParallel1(t *testing.T) {
+       runParallelTests(t, 1)
+}
+
+func TestHandleParallel2(t *testing.T) {
+       runParallelTests(t, 2)
+}
+
+func TestHandleParallel3(t *testing.T) {
+       runParallelTests(t, 3)
+}
+
+func TestHandleParallel4(t *testing.T) {
+       runParallelTests(t, 4)
+}
index 56409eb..fb2013a 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)
 
@@ -84,7 +122,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)
 
@@ -1250,6 +1288,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)
@@ -1332,7 +1386,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:
diff --git a/vendor/github.com/vishvananda/netlink/link_test.go b/vendor/github.com/vishvananda/netlink/link_test.go
new file mode 100644 (file)
index 0000000..9d77147
--- /dev/null
@@ -0,0 +1,1119 @@
+// +build linux
+
+package netlink
+
+import (
+       "bytes"
+       "net"
+       "os"
+       "syscall"
+       "testing"
+       "time"
+
+       "github.com/vishvananda/netns"
+)
+
+const (
+       testTxQLen    int = 100
+       defaultTxQLen int = 1000
+)
+
+func testLinkAddDel(t *testing.T, link Link) {
+       links, err := LinkList()
+       if err != nil {
+               t.Fatal(err)
+       }
+
+       if err := LinkAdd(link); err != nil {
+               t.Fatal(err)
+       }
+
+       base := link.Attrs()
+
+       result, err := LinkByName(base.Name)
+       if err != nil {
+               t.Fatal(err)
+       }
+
+       rBase := result.Attrs()
+
+       if vlan, ok := link.(*Vlan); ok {
+               other, ok := result.(*Vlan)
+               if !ok {
+                       t.Fatal("Result of create is not a vlan")
+               }
+               if vlan.VlanId != other.VlanId {
+                       t.Fatal("Link.VlanId id doesn't match")
+               }
+       }
+
+       if veth, ok := result.(*Veth); ok {
+               if rBase.TxQLen != base.TxQLen {
+                       t.Fatalf("qlen is %d, should be %d", rBase.TxQLen, base.TxQLen)
+               }
+               if rBase.MTU != base.MTU {
+                       t.Fatalf("MTU is %d, should be %d", rBase.MTU, base.MTU)
+               }
+
+               if original, ok := link.(*Veth); ok {
+                       if original.PeerName != "" {
+                               var peer *Veth
+                               other, err := LinkByName(original.PeerName)
+                               if err != nil {
+                                       t.Fatalf("Peer %s not created", veth.PeerName)
+                               }
+                               if peer, ok = other.(*Veth); !ok {
+                                       t.Fatalf("Peer %s is incorrect type", veth.PeerName)
+                               }
+                               if peer.TxQLen != testTxQLen {
+                                       t.Fatalf("TxQLen of peer is %d, should be %d", peer.TxQLen, testTxQLen)
+                               }
+                       }
+               }
+       } else {
+               // recent kernels set the parent index for veths in the response
+               if rBase.ParentIndex == 0 && base.ParentIndex != 0 {
+                       t.Fatalf("Created link doesn't have parent %d but it should", base.ParentIndex)
+               } else if rBase.ParentIndex != 0 && base.ParentIndex == 0 {
+                       t.Fatalf("Created link has parent %d but it shouldn't", rBase.ParentIndex)
+               } else if rBase.ParentIndex != 0 && base.ParentIndex != 0 {
+                       if rBase.ParentIndex != base.ParentIndex {
+                               t.Fatalf("Link.ParentIndex doesn't match %d != %d", rBase.ParentIndex, base.ParentIndex)
+                       }
+               }
+       }
+
+       if vxlan, ok := link.(*Vxlan); ok {
+               other, ok := result.(*Vxlan)
+               if !ok {
+                       t.Fatal("Result of create is not a vxlan")
+               }
+               compareVxlan(t, vxlan, other)
+       }
+
+       if ipv, ok := link.(*IPVlan); ok {
+               other, ok := result.(*IPVlan)
+               if !ok {
+                       t.Fatal("Result of create is not a ipvlan")
+               }
+               if ipv.Mode != other.Mode {
+                       t.Fatalf("Got unexpected mode: %d, expected: %d", other.Mode, ipv.Mode)
+               }
+       }
+
+       if macv, ok := link.(*Macvlan); ok {
+               other, ok := result.(*Macvlan)
+               if !ok {
+                       t.Fatal("Result of create is not a macvlan")
+               }
+               if macv.Mode != other.Mode {
+                       t.Fatalf("Got unexpected mode: %d, expected: %d", other.Mode, macv.Mode)
+               }
+       }
+
+       if macv, ok := link.(*Macvtap); ok {
+               other, ok := result.(*Macvtap)
+               if !ok {
+                       t.Fatal("Result of create is not a macvtap")
+               }
+               if macv.Mode != other.Mode {
+                       t.Fatalf("Got unexpected mode: %d, expected: %d", other.Mode, macv.Mode)
+               }
+       }
+
+       if _, ok := link.(*Vti); ok {
+               _, ok := result.(*Vti)
+               if !ok {
+                       t.Fatal("Result of create is not a vti")
+               }
+       }
+
+       if bond, ok := link.(*Bond); ok {
+               other, ok := result.(*Bond)
+               if !ok {
+                       t.Fatal("Result of create is not a bond")
+               }
+               if bond.Mode != other.Mode {
+                       t.Fatalf("Got unexpected mode: %d, expected: %d", other.Mode, bond.Mode)
+               }
+       }
+
+       if _, ok := link.(*Iptun); ok {
+               _, ok := result.(*Iptun)
+               if !ok {
+                       t.Fatal("Result of create is not a iptun")
+               }
+       }
+
+       if err = LinkDel(link); err != nil {
+               t.Fatal(err)
+       }
+
+       links, err = LinkList()
+       if err != nil {
+               t.Fatal(err)
+       }
+
+       for _, l := range links {
+               if l.Attrs().Name == link.Attrs().Name {
+                       t.Fatal("Link not removed properly")
+               }
+       }
+}
+
+func compareVxlan(t *testing.T, expected, actual *Vxlan) {
+
+       if actual.VxlanId != expected.VxlanId {
+               t.Fatal("Vxlan.VxlanId doesn't match")
+       }
+       if expected.SrcAddr != nil && !actual.SrcAddr.Equal(expected.SrcAddr) {
+               t.Fatal("Vxlan.SrcAddr doesn't match")
+       }
+       if expected.Group != nil && !actual.Group.Equal(expected.Group) {
+               t.Fatal("Vxlan.Group doesn't match")
+       }
+       if expected.TTL != -1 && actual.TTL != expected.TTL {
+               t.Fatal("Vxlan.TTL doesn't match")
+       }
+       if expected.TOS != -1 && actual.TOS != expected.TOS {
+               t.Fatal("Vxlan.TOS doesn't match")
+       }
+       if actual.Learning != expected.Learning {
+               t.Fatal("Vxlan.Learning doesn't match")
+       }
+       if actual.Proxy != expected.Proxy {
+               t.Fatal("Vxlan.Proxy doesn't match")
+       }
+       if actual.RSC != expected.RSC {
+               t.Fatal("Vxlan.RSC doesn't match")
+       }
+       if actual.L2miss != expected.L2miss {
+               t.Fatal("Vxlan.L2miss doesn't match")
+       }
+       if actual.L3miss != expected.L3miss {
+               t.Fatal("Vxlan.L3miss doesn't match")
+       }
+       if actual.GBP != expected.GBP {
+               t.Fatal("Vxlan.GBP doesn't match")
+       }
+       if expected.NoAge {
+               if !actual.NoAge {
+                       t.Fatal("Vxlan.NoAge doesn't match")
+               }
+       } else if expected.Age > 0 && actual.Age != expected.Age {
+               t.Fatal("Vxlan.Age doesn't match")
+       }
+       if expected.Limit > 0 && actual.Limit != expected.Limit {
+               t.Fatal("Vxlan.Limit doesn't match")
+       }
+       if expected.Port > 0 && actual.Port != expected.Port {
+               t.Fatal("Vxlan.Port doesn't match")
+       }
+       if expected.PortLow > 0 || expected.PortHigh > 0 {
+               if actual.PortLow != expected.PortLow {
+                       t.Fatal("Vxlan.PortLow doesn't match")
+               }
+               if actual.PortHigh != expected.PortHigh {
+                       t.Fatal("Vxlan.PortHigh doesn't match")
+               }
+       }
+}
+
+func TestLinkAddDelDummy(t *testing.T) {
+       tearDown := setUpNetlinkTest(t)
+       defer tearDown()
+
+       testLinkAddDel(t, &Dummy{LinkAttrs{Name: "foo"}})
+}
+
+func TestLinkAddDelIfb(t *testing.T) {
+       tearDown := setUpNetlinkTest(t)
+       defer tearDown()
+
+       testLinkAddDel(t, &Ifb{LinkAttrs{Name: "foo"}})
+}
+
+func TestLinkAddDelBridge(t *testing.T) {
+       tearDown := setUpNetlinkTest(t)
+       defer tearDown()
+
+       testLinkAddDel(t, &Bridge{LinkAttrs{Name: "foo", MTU: 1400}})
+}
+
+func TestLinkAddDelGretap(t *testing.T) {
+       tearDown := setUpNetlinkTest(t)
+       defer tearDown()
+
+       testLinkAddDel(t, &Gretap{
+               LinkAttrs: LinkAttrs{Name: "foo"},
+               IKey:      0x101,
+               OKey:      0x101,
+               PMtuDisc:  1,
+               Local:     net.IPv4(127, 0, 0, 1),
+               Remote:    net.IPv4(127, 0, 0, 1)})
+}
+
+func TestLinkAddDelVlan(t *testing.T) {
+       tearDown := setUpNetlinkTest(t)
+       defer tearDown()
+
+       parent := &Dummy{LinkAttrs{Name: "foo"}}
+       if err := LinkAdd(parent); err != nil {
+               t.Fatal(err)
+       }
+
+       testLinkAddDel(t, &Vlan{LinkAttrs{Name: "bar", ParentIndex: parent.Attrs().Index}, 900})
+
+       if err := LinkDel(parent); err != nil {
+               t.Fatal(err)
+       }
+}
+
+func TestLinkAddDelMacvlan(t *testing.T) {
+       tearDown := setUpNetlinkTest(t)
+       defer tearDown()
+
+       parent := &Dummy{LinkAttrs{Name: "foo"}}
+       if err := LinkAdd(parent); err != nil {
+               t.Fatal(err)
+       }
+
+       testLinkAddDel(t, &Macvlan{
+               LinkAttrs: LinkAttrs{Name: "bar", ParentIndex: parent.Attrs().Index},
+               Mode:      MACVLAN_MODE_PRIVATE,
+       })
+
+       testLinkAddDel(t, &Macvlan{
+               LinkAttrs: LinkAttrs{Name: "bar", ParentIndex: parent.Attrs().Index},
+               Mode:      MACVLAN_MODE_BRIDGE,
+       })
+
+       testLinkAddDel(t, &Macvlan{
+               LinkAttrs: LinkAttrs{Name: "bar", ParentIndex: parent.Attrs().Index},
+               Mode:      MACVLAN_MODE_VEPA,
+       })
+
+       if err := LinkDel(parent); err != nil {
+               t.Fatal(err)
+       }
+}
+
+func TestLinkAddDelMacvtap(t *testing.T) {
+       tearDown := setUpNetlinkTest(t)
+       defer tearDown()
+
+       parent := &Dummy{LinkAttrs{Name: "foo"}}
+       if err := LinkAdd(parent); err != nil {
+               t.Fatal(err)
+       }
+
+       testLinkAddDel(t, &Macvtap{
+               Macvlan: Macvlan{
+                       LinkAttrs: LinkAttrs{Name: "bar", ParentIndex: parent.Attrs().Index},
+                       Mode:      MACVLAN_MODE_PRIVATE,
+               },
+       })
+
+       testLinkAddDel(t, &Macvtap{
+               Macvlan: Macvlan{
+                       LinkAttrs: LinkAttrs{Name: "bar", ParentIndex: parent.Attrs().Index},
+                       Mode:      MACVLAN_MODE_BRIDGE,
+               },
+       })
+
+       testLinkAddDel(t, &Macvtap{
+               Macvlan: Macvlan{
+                       LinkAttrs: LinkAttrs{Name: "bar", ParentIndex: parent.Attrs().Index},
+                       Mode:      MACVLAN_MODE_VEPA,
+               },
+       })
+
+       if err := LinkDel(parent); err != nil {
+               t.Fatal(err)
+       }
+}
+
+func TestLinkAddDelVeth(t *testing.T) {
+       tearDown := setUpNetlinkTest(t)
+       defer tearDown()
+
+       veth := &Veth{LinkAttrs: LinkAttrs{Name: "foo", TxQLen: testTxQLen, MTU: 1400}, PeerName: "bar"}
+       testLinkAddDel(t, veth)
+}
+
+func TestLinkAddDelBond(t *testing.T) {
+       tearDown := setUpNetlinkTest(t)
+       defer tearDown()
+
+       bond := NewLinkBond(LinkAttrs{Name: "foo"})
+       bond.Mode = StringToBondModeMap["802.3ad"]
+       testLinkAddDel(t, bond)
+}
+
+func TestLinkAddVethWithDefaultTxQLen(t *testing.T) {
+       tearDown := setUpNetlinkTest(t)
+       defer tearDown()
+       la := NewLinkAttrs()
+       la.Name = "foo"
+
+       veth := &Veth{LinkAttrs: la, PeerName: "bar"}
+       if err := LinkAdd(veth); err != nil {
+               t.Fatal(err)
+       }
+       link, err := LinkByName("foo")
+       if err != nil {
+               t.Fatal(err)
+       }
+       if veth, ok := link.(*Veth); !ok {
+               t.Fatalf("unexpected link type: %T", link)
+       } else {
+               if veth.TxQLen != defaultTxQLen {
+                       t.Fatalf("TxQLen is %d, should be %d", veth.TxQLen, defaultTxQLen)
+               }
+       }
+       peer, err := LinkByName("bar")
+       if err != nil {
+               t.Fatal(err)
+       }
+       if veth, ok := peer.(*Veth); !ok {
+               t.Fatalf("unexpected link type: %T", link)
+       } else {
+               if veth.TxQLen != defaultTxQLen {
+                       t.Fatalf("TxQLen is %d, should be %d", veth.TxQLen, defaultTxQLen)
+               }
+       }
+}
+
+func TestLinkAddVethWithZeroTxQLen(t *testing.T) {
+       tearDown := setUpNetlinkTest(t)
+       defer tearDown()
+       la := NewLinkAttrs()
+       la.Name = "foo"
+       la.TxQLen = 0
+
+       veth := &Veth{LinkAttrs: la, PeerName: "bar"}
+       if err := LinkAdd(veth); err != nil {
+               t.Fatal(err)
+       }
+       link, err := LinkByName("foo")
+       if err != nil {
+               t.Fatal(err)
+       }
+       if veth, ok := link.(*Veth); !ok {
+               t.Fatalf("unexpected link type: %T", link)
+       } else {
+               if veth.TxQLen != 0 {
+                       t.Fatalf("TxQLen is %d, should be %d", veth.TxQLen, 0)
+               }
+       }
+       peer, err := LinkByName("bar")
+       if err != nil {
+               t.Fatal(err)
+       }
+       if veth, ok := peer.(*Veth); !ok {
+               t.Fatalf("unexpected link type: %T", link)
+       } else {
+               if veth.TxQLen != 0 {
+                       t.Fatalf("TxQLen is %d, should be %d", veth.TxQLen, 0)
+               }
+       }
+}
+
+func TestLinkAddDummyWithTxQLen(t *testing.T) {
+       tearDown := setUpNetlinkTest(t)
+       defer tearDown()
+       la := NewLinkAttrs()
+       la.Name = "foo"
+       la.TxQLen = 1500
+
+       dummy := &Dummy{LinkAttrs: la}
+       if err := LinkAdd(dummy); err != nil {
+               t.Fatal(err)
+       }
+       link, err := LinkByName("foo")
+       if err != nil {
+               t.Fatal(err)
+       }
+       if dummy, ok := link.(*Dummy); !ok {
+               t.Fatalf("unexpected link type: %T", link)
+       } else {
+               if dummy.TxQLen != 1500 {
+                       t.Fatalf("TxQLen is %d, should be %d", dummy.TxQLen, 1500)
+               }
+       }
+}
+
+func TestLinkAddDelBridgeMaster(t *testing.T) {
+       tearDown := setUpNetlinkTest(t)
+       defer tearDown()
+
+       master := &Bridge{LinkAttrs{Name: "foo"}}
+       if err := LinkAdd(master); err != nil {
+               t.Fatal(err)
+       }
+       testLinkAddDel(t, &Dummy{LinkAttrs{Name: "bar", MasterIndex: master.Attrs().Index}})
+
+       if err := LinkDel(master); err != nil {
+               t.Fatal(err)
+       }
+}
+
+func TestLinkSetUnsetResetMaster(t *testing.T) {
+       tearDown := setUpNetlinkTest(t)
+       defer tearDown()
+
+       master := &Bridge{LinkAttrs{Name: "foo"}}
+       if err := LinkAdd(master); err != nil {
+               t.Fatal(err)
+       }
+
+       newmaster := &Bridge{LinkAttrs{Name: "bar"}}
+       if err := LinkAdd(newmaster); err != nil {
+               t.Fatal(err)
+       }
+
+       slave := &Dummy{LinkAttrs{Name: "baz"}}
+       if err := LinkAdd(slave); err != nil {
+               t.Fatal(err)
+       }
+
+       nonexistsmaster := &Bridge{LinkAttrs{Name: "foobar"}}
+
+       if err := LinkSetMaster(slave, nonexistsmaster); err == nil {
+               t.Fatal("error expected")
+       }
+
+       if err := LinkSetMaster(slave, master); err != nil {
+               t.Fatal(err)
+       }
+
+       link, err := LinkByName("baz")
+       if err != nil {
+               t.Fatal(err)
+       }
+
+       if link.Attrs().MasterIndex != master.Attrs().Index {
+               t.Fatal("Master not set properly")
+       }
+
+       if err := LinkSetMaster(slave, newmaster); err != nil {
+               t.Fatal(err)
+       }
+
+       link, err = LinkByName("baz")
+       if err != nil {
+               t.Fatal(err)
+       }
+
+       if link.Attrs().MasterIndex != newmaster.Attrs().Index {
+               t.Fatal("Master not reset properly")
+       }
+
+       if err := LinkSetNoMaster(slave); err != nil {
+               t.Fatal(err)
+       }
+
+       link, err = LinkByName("baz")
+       if err != nil {
+               t.Fatal(err)
+       }
+
+       if link.Attrs().MasterIndex != 0 {
+               t.Fatal("Master not unset properly")
+       }
+       if err := LinkDel(slave); err != nil {
+               t.Fatal(err)
+       }
+
+       if err := LinkDel(newmaster); err != nil {
+               t.Fatal(err)
+       }
+
+       if err := LinkDel(master); err != nil {
+               t.Fatal(err)
+       }
+}
+
+func TestLinkSetNs(t *testing.T) {
+       tearDown := setUpNetlinkTest(t)
+       defer tearDown()
+
+       basens, err := netns.Get()
+       if err != nil {
+               t.Fatal("Failed to get basens")
+       }
+       defer basens.Close()
+
+       newns, err := netns.New()
+       if err != nil {
+               t.Fatal("Failed to create newns")
+       }
+       defer newns.Close()
+
+       link := &Veth{LinkAttrs{Name: "foo"}, "bar"}
+       if err := LinkAdd(link); err != nil {
+               t.Fatal(err)
+       }
+
+       peer, err := LinkByName("bar")
+       if err != nil {
+               t.Fatal(err)
+       }
+
+       LinkSetNsFd(peer, int(basens))
+       if err != nil {
+               t.Fatal("Failed to set newns for link")
+       }
+
+       _, err = LinkByName("bar")
+       if err == nil {
+               t.Fatal("Link bar is still in newns")
+       }
+
+       err = netns.Set(basens)
+       if err != nil {
+               t.Fatal("Failed to set basens")
+       }
+
+       peer, err = LinkByName("bar")
+       if err != nil {
+               t.Fatal("Link is not in basens")
+       }
+
+       if err := LinkDel(peer); err != nil {
+               t.Fatal(err)
+       }
+
+       err = netns.Set(newns)
+       if err != nil {
+               t.Fatal("Failed to set newns")
+       }
+
+       _, err = LinkByName("foo")
+       if err == nil {
+               t.Fatal("Other half of veth pair not deleted")
+       }
+
+}
+
+func TestLinkAddDelVxlan(t *testing.T) {
+       tearDown := setUpNetlinkTest(t)
+       defer tearDown()
+
+       parent := &Dummy{
+               LinkAttrs{Name: "foo"},
+       }
+       if err := LinkAdd(parent); err != nil {
+               t.Fatal(err)
+       }
+
+       vxlan := Vxlan{
+               LinkAttrs: LinkAttrs{
+                       Name: "bar",
+               },
+               VxlanId:      10,
+               VtepDevIndex: parent.Index,
+               Learning:     true,
+               L2miss:       true,
+               L3miss:       true,
+       }
+
+       testLinkAddDel(t, &vxlan)
+       if err := LinkDel(parent); err != nil {
+               t.Fatal(err)
+       }
+}
+
+func TestLinkAddDelVxlanGbp(t *testing.T) {
+       if os.Getenv("TRAVIS_BUILD_DIR") != "" {
+               t.Skipf("Kernel in travis is too old for this test")
+       }
+
+       tearDown := setUpNetlinkTest(t)
+       defer tearDown()
+
+       parent := &Dummy{
+               LinkAttrs{Name: "foo"},
+       }
+       if err := LinkAdd(parent); err != nil {
+               t.Fatal(err)
+       }
+
+       vxlan := Vxlan{
+               LinkAttrs: LinkAttrs{
+                       Name: "bar",
+               },
+               VxlanId:      10,
+               VtepDevIndex: parent.Index,
+               Learning:     true,
+               L2miss:       true,
+               L3miss:       true,
+               GBP:          true,
+       }
+
+       testLinkAddDel(t, &vxlan)
+       if err := LinkDel(parent); err != nil {
+               t.Fatal(err)
+       }
+}
+
+func TestLinkAddDelIPVlanL2(t *testing.T) {
+       if os.Getenv("TRAVIS_BUILD_DIR") != "" {
+               t.Skipf("Kernel in travis is too old for this test")
+       }
+       tearDown := setUpNetlinkTest(t)
+       defer tearDown()
+       parent := &Dummy{LinkAttrs{Name: "foo"}}
+       if err := LinkAdd(parent); err != nil {
+               t.Fatal(err)
+       }
+
+       ipv := IPVlan{
+               LinkAttrs: LinkAttrs{
+                       Name:        "bar",
+                       ParentIndex: parent.Index,
+               },
+               Mode: IPVLAN_MODE_L2,
+       }
+
+       testLinkAddDel(t, &ipv)
+}
+
+func TestLinkAddDelIPVlanL3(t *testing.T) {
+       if os.Getenv("TRAVIS_BUILD_DIR") != "" {
+               t.Skipf("Kernel in travis is too old for this test")
+       }
+       tearDown := setUpNetlinkTest(t)
+       defer tearDown()
+       parent := &Dummy{LinkAttrs{Name: "foo"}}
+       if err := LinkAdd(parent); err != nil {
+               t.Fatal(err)
+       }
+
+       ipv := IPVlan{
+               LinkAttrs: LinkAttrs{
+                       Name:        "bar",
+                       ParentIndex: parent.Index,
+               },
+               Mode: IPVLAN_MODE_L3,
+       }
+
+       testLinkAddDel(t, &ipv)
+}
+
+func TestLinkAddDelIPVlanNoParent(t *testing.T) {
+       tearDown := setUpNetlinkTest(t)
+       defer tearDown()
+
+       ipv := IPVlan{
+               LinkAttrs: LinkAttrs{
+                       Name: "bar",
+               },
+               Mode: IPVLAN_MODE_L3,
+       }
+       err := LinkAdd(&ipv)
+       if err == nil {
+               t.Fatal("Add should fail if ipvlan creating without ParentIndex")
+       }
+       if err.Error() != "Can't create ipvlan link without ParentIndex" {
+               t.Fatalf("Error should be about missing ParentIndex, got %q", err)
+       }
+}
+
+func TestLinkByIndex(t *testing.T) {
+       tearDown := setUpNetlinkTest(t)
+       defer tearDown()
+
+       dummy := &Dummy{LinkAttrs{Name: "dummy"}}
+       if err := LinkAdd(dummy); err != nil {
+               t.Fatal(err)
+       }
+
+       found, err := LinkByIndex(dummy.Index)
+       if err != nil {
+               t.Fatal(err)
+       }
+
+       if found.Attrs().Index != dummy.Attrs().Index {
+               t.Fatalf("Indices don't match: %v != %v", found.Attrs().Index, dummy.Attrs().Index)
+       }
+
+       LinkDel(dummy)
+
+       // test not found
+       _, err = LinkByIndex(dummy.Attrs().Index)
+       if err == nil {
+               t.Fatalf("LinkByIndex(%v) found deleted link", err)
+       }
+}
+
+func TestLinkSet(t *testing.T) {
+       tearDown := setUpNetlinkTest(t)
+       defer tearDown()
+
+       iface := &Dummy{LinkAttrs{Name: "foo"}}
+       if err := LinkAdd(iface); err != nil {
+               t.Fatal(err)
+       }
+
+       link, err := LinkByName("foo")
+       if err != nil {
+               t.Fatal(err)
+       }
+
+       err = LinkSetName(link, "bar")
+       if err != nil {
+               t.Fatalf("Could not change interface name: %v", err)
+       }
+
+       link, err = LinkByName("bar")
+       if err != nil {
+               t.Fatalf("Interface name not changed: %v", err)
+       }
+
+       err = LinkSetMTU(link, 1400)
+       if err != nil {
+               t.Fatalf("Could not set MTU: %v", err)
+       }
+
+       link, err = LinkByName("bar")
+       if err != nil {
+               t.Fatal(err)
+       }
+
+       if link.Attrs().MTU != 1400 {
+               t.Fatal("MTU not changed!")
+       }
+
+       addr, err := net.ParseMAC("00:12:34:56:78:AB")
+       if err != nil {
+               t.Fatal(err)
+       }
+
+       err = LinkSetHardwareAddr(link, addr)
+       if err != nil {
+               t.Fatal(err)
+       }
+
+       link, err = LinkByName("bar")
+       if err != nil {
+               t.Fatal(err)
+       }
+
+       if !bytes.Equal(link.Attrs().HardwareAddr, addr) {
+               t.Fatalf("hardware address not changed!")
+       }
+
+       err = LinkSetAlias(link, "barAlias")
+       if err != nil {
+               t.Fatalf("Could not set alias: %v", err)
+       }
+
+       link, err = LinkByName("bar")
+       if err != nil {
+               t.Fatal(err)
+       }
+
+       if link.Attrs().Alias != "barAlias" {
+               t.Fatalf("alias not changed!")
+       }
+
+       link, err = LinkByAlias("barAlias")
+       if err != nil {
+               t.Fatal(err)
+       }
+}
+
+func TestLinkSetARP(t *testing.T) {
+       tearDown := setUpNetlinkTest(t)
+       defer tearDown()
+
+       iface := &Veth{LinkAttrs: LinkAttrs{Name: "foo", TxQLen: testTxQLen, MTU: 1500}, PeerName: "banana"}
+       if err := LinkAdd(iface); err != nil {
+               t.Fatal(err)
+       }
+
+       link, err := LinkByName("foo")
+       if err != nil {
+               t.Fatal(err)
+       }
+
+       err = LinkSetARPOff(link)
+       if err != nil {
+               t.Fatal(err)
+       }
+
+       link, err = LinkByName("foo")
+       if err != nil {
+               t.Fatal(err)
+       }
+
+       if link.Attrs().RawFlags&syscall.IFF_NOARP != uint32(syscall.IFF_NOARP) {
+               t.Fatalf("NOARP was not set!")
+       }
+
+       err = LinkSetARPOn(link)
+       if err != nil {
+               t.Fatal(err)
+       }
+
+       link, err = LinkByName("foo")
+       if err != nil {
+               t.Fatal(err)
+       }
+
+       if link.Attrs().RawFlags&syscall.IFF_NOARP != 0 {
+               t.Fatalf("NOARP is still set!")
+       }
+}
+
+func expectLinkUpdate(ch <-chan LinkUpdate, ifaceName string, up bool) bool {
+       for {
+               timeout := time.After(time.Minute)
+               select {
+               case update := <-ch:
+                       if ifaceName == update.Link.Attrs().Name && (update.IfInfomsg.Flags&syscall.IFF_UP != 0) == up {
+                               return true
+                       }
+               case <-timeout:
+                       return false
+               }
+       }
+}
+
+func TestLinkSubscribe(t *testing.T) {
+       tearDown := setUpNetlinkTest(t)
+       defer tearDown()
+
+       ch := make(chan LinkUpdate)
+       done := make(chan struct{})
+       defer close(done)
+       if err := LinkSubscribe(ch, done); err != nil {
+               t.Fatal(err)
+       }
+
+       link := &Veth{LinkAttrs{Name: "foo", TxQLen: testTxQLen, MTU: 1400}, "bar"}
+       if err := LinkAdd(link); err != nil {
+               t.Fatal(err)
+       }
+
+       if !expectLinkUpdate(ch, "foo", false) {
+               t.Fatal("Add update not received as expected")
+       }
+
+       if err := LinkSetUp(link); err != nil {
+               t.Fatal(err)
+       }
+
+       if !expectLinkUpdate(ch, "foo", true) {
+               t.Fatal("Link Up update not received as expected")
+       }
+
+       if err := LinkDel(link); err != nil {
+               t.Fatal(err)
+       }
+
+       if !expectLinkUpdate(ch, "foo", false) {
+               t.Fatal("Del update not received as expected")
+       }
+}
+
+func TestLinkSubscribeAt(t *testing.T) {
+       skipUnlessRoot(t)
+
+       // Create an handle on a custom netns
+       newNs, err := netns.New()
+       if err != nil {
+               t.Fatal(err)
+       }
+       defer newNs.Close()
+
+       nh, err := NewHandleAt(newNs)
+       if err != nil {
+               t.Fatal(err)
+       }
+       defer nh.Delete()
+
+       // Subscribe for Link events on the custom netns
+       ch := make(chan LinkUpdate)
+       done := make(chan struct{})
+       defer close(done)
+       if err := LinkSubscribeAt(newNs, ch, done); err != nil {
+               t.Fatal(err)
+       }
+
+       link := &Veth{LinkAttrs{Name: "test", TxQLen: testTxQLen, MTU: 1400}, "bar"}
+       if err := nh.LinkAdd(link); err != nil {
+               t.Fatal(err)
+       }
+
+       if !expectLinkUpdate(ch, "test", false) {
+               t.Fatal("Add update not received as expected")
+       }
+
+       if err := nh.LinkSetUp(link); err != nil {
+               t.Fatal(err)
+       }
+
+       if !expectLinkUpdate(ch, "test", true) {
+               t.Fatal("Link Up update not received as expected")
+       }
+
+       if err := nh.LinkDel(link); err != nil {
+               t.Fatal(err)
+       }
+
+       if !expectLinkUpdate(ch, "test", false) {
+               t.Fatal("Del update not received as expected")
+       }
+}
+
+func TestLinkStats(t *testing.T) {
+       defer setUpNetlinkTest(t)()
+
+       // Create a veth pair and verify the cross-stats once both
+       // ends are brought up and some ICMPv6 packets are exchanged
+       v0 := "v0"
+       v1 := "v1"
+
+       vethLink := &Veth{LinkAttrs: LinkAttrs{Name: v0}, PeerName: v1}
+       if err := LinkAdd(vethLink); err != nil {
+               t.Fatal(err)
+       }
+
+       veth0, err := LinkByName(v0)
+       if err != nil {
+               t.Fatal(err)
+       }
+       if err := LinkSetUp(veth0); err != nil {
+               t.Fatal(err)
+       }
+
+       veth1, err := LinkByName(v1)
+       if err != nil {
+               t.Fatal(err)
+       }
+       if err := LinkSetUp(veth1); err != nil {
+               t.Fatal(err)
+       }
+
+       time.Sleep(2 * time.Second)
+
+       // verify statistics
+       veth0, err = LinkByName(v0)
+       if err != nil {
+               t.Fatal(err)
+       }
+       veth1, err = LinkByName(v1)
+       if err != nil {
+               t.Fatal(err)
+       }
+       v0Stats := veth0.Attrs().Statistics
+       v1Stats := veth1.Attrs().Statistics
+       if v0Stats.RxPackets != v1Stats.TxPackets || v0Stats.TxPackets != v1Stats.RxPackets ||
+               v0Stats.RxBytes != v1Stats.TxBytes || v0Stats.TxBytes != v1Stats.RxBytes {
+               t.Fatalf("veth ends counters differ:\n%v\n%v", v0Stats, v1Stats)
+       }
+}
+
+func TestLinkXdp(t *testing.T) {
+       links, err := LinkList()
+       if err != nil {
+               t.Fatal(err)
+       }
+       var testXdpLink Link
+       for _, link := range links {
+               if link.Attrs().Xdp != nil && !link.Attrs().Xdp.Attached {
+                       testXdpLink = link
+                       break
+               }
+       }
+       if testXdpLink == nil {
+               t.Skipf("No link supporting XDP found")
+       }
+       fd, err := loadSimpleBpf(BPF_PROG_TYPE_XDP, 2 /*XDP_PASS*/)
+       if err != nil {
+               t.Skipf("Loading bpf program failed: %s", err)
+       }
+       if err := LinkSetXdpFd(testXdpLink, fd); err != nil {
+               t.Fatal(err)
+       }
+       if err := LinkSetXdpFd(testXdpLink, -1); err != nil {
+               t.Fatal(err)
+       }
+}
+
+func TestLinkAddDelIptun(t *testing.T) {
+       tearDown := setUpNetlinkTest(t)
+       defer tearDown()
+
+       testLinkAddDel(t, &Iptun{
+               LinkAttrs: LinkAttrs{Name: "iptunfoo"},
+               PMtuDisc:  1,
+               Local:     net.IPv4(127, 0, 0, 1),
+               Remote:    net.IPv4(127, 0, 0, 1)})
+}
+
+func TestLinkAddDelVti(t *testing.T) {
+       tearDown := setUpNetlinkTest(t)
+       defer tearDown()
+
+       testLinkAddDel(t, &Vti{
+               LinkAttrs: LinkAttrs{Name: "vtifoo"},
+               IKey:      0x101,
+               OKey:      0x101,
+               Local:     net.IPv4(127, 0, 0, 1),
+               Remote:    net.IPv4(127, 0, 0, 1)})
+}
+
+func TestLinkSubscribeWithProtinfo(t *testing.T) {
+       tearDown := setUpNetlinkTest(t)
+       defer tearDown()
+
+       master := &Bridge{LinkAttrs{Name: "foo"}}
+       if err := LinkAdd(master); err != nil {
+               t.Fatal(err)
+       }
+
+       slave := &Veth{
+               LinkAttrs: LinkAttrs{
+                       Name:        "bar",
+                       TxQLen:      testTxQLen,
+                       MTU:         1400,
+                       MasterIndex: master.Attrs().Index,
+               },
+               PeerName: "bar-peer",
+       }
+       if err := LinkAdd(slave); err != nil {
+               t.Fatal(err)
+       }
+
+       ch := make(chan LinkUpdate)
+       done := make(chan struct{})
+       defer close(done)
+       if err := LinkSubscribe(ch, done); err != nil {
+               t.Fatal(err)
+       }
+
+       if err := LinkSetHairpin(slave, true); err != nil {
+               t.Fatal(err)
+       }
+
+       select {
+       case update := <-ch:
+               if !(update.Attrs().Name == "bar" && update.Attrs().Protinfo != nil &&
+                       update.Attrs().Protinfo.Hairpin) {
+                       t.Fatal("Hairpin update not received as expected")
+               }
+       case <-time.After(time.Minute):
+               t.Fatal("Hairpin update timed out")
+       }
+
+       if err := LinkDel(slave); err != nil {
+               t.Fatal(err)
+       }
+
+       if err := LinkDel(master); err != nil {
+               t.Fatal(err)
+       }
+}
diff --git a/vendor/github.com/vishvananda/netlink/neigh_test.go b/vendor/github.com/vishvananda/netlink/neigh_test.go
new file mode 100644 (file)
index 0000000..8ad8b32
--- /dev/null
@@ -0,0 +1,194 @@
+// +build linux
+
+package netlink
+
+import (
+       "net"
+       "testing"
+)
+
+type arpEntry struct {
+       ip  net.IP
+       mac net.HardwareAddr
+}
+
+type proxyEntry struct {
+       ip  net.IP
+       dev int
+}
+
+func parseMAC(s string) net.HardwareAddr {
+       m, err := net.ParseMAC(s)
+       if err != nil {
+               panic(err)
+       }
+       return m
+}
+
+func dumpContains(dump []Neigh, e arpEntry) bool {
+       for _, n := range dump {
+               if n.IP.Equal(e.ip) && (n.State&NUD_INCOMPLETE) == 0 {
+                       return true
+               }
+       }
+       return false
+}
+
+func dumpContainsProxy(dump []Neigh, p proxyEntry) bool {
+       for _, n := range dump {
+               if n.IP.Equal(p.ip) && (n.LinkIndex == p.dev) && (n.Flags&NTF_PROXY) == NTF_PROXY {
+                       return true
+               }
+       }
+       return false
+}
+
+func TestNeighAddDel(t *testing.T) {
+       tearDown := setUpNetlinkTest(t)
+       defer tearDown()
+
+       dummy := Dummy{LinkAttrs{Name: "neigh0"}}
+       if err := LinkAdd(&dummy); err != nil {
+               t.Fatal(err)
+       }
+
+       ensureIndex(dummy.Attrs())
+
+       arpTable := []arpEntry{
+               {net.ParseIP("10.99.0.1"), parseMAC("aa:bb:cc:dd:00:01")},
+               {net.ParseIP("10.99.0.2"), parseMAC("aa:bb:cc:dd:00:02")},
+               {net.ParseIP("10.99.0.3"), parseMAC("aa:bb:cc:dd:00:03")},
+               {net.ParseIP("10.99.0.4"), parseMAC("aa:bb:cc:dd:00:04")},
+               {net.ParseIP("10.99.0.5"), parseMAC("aa:bb:cc:dd:00:05")},
+       }
+
+       // Add the arpTable
+       for _, entry := range arpTable {
+               err := NeighAdd(&Neigh{
+                       LinkIndex:    dummy.Index,
+                       State:        NUD_REACHABLE,
+                       IP:           entry.ip,
+                       HardwareAddr: entry.mac,
+               })
+
+               if err != nil {
+                       t.Errorf("Failed to NeighAdd: %v", err)
+               }
+       }
+
+       // Dump and see that all added entries are there
+       dump, err := NeighList(dummy.Index, 0)
+       if err != nil {
+               t.Errorf("Failed to NeighList: %v", err)
+       }
+
+       for _, entry := range arpTable {
+               if !dumpContains(dump, entry) {
+                       t.Errorf("Dump does not contain: %v", entry)
+               }
+       }
+
+       // Delete the arpTable
+       for _, entry := range arpTable {
+               err := NeighDel(&Neigh{
+                       LinkIndex:    dummy.Index,
+                       IP:           entry.ip,
+                       HardwareAddr: entry.mac,
+               })
+
+               if err != nil {
+                       t.Errorf("Failed to NeighDel: %v", err)
+               }
+       }
+
+       // TODO: seems not working because of cache
+       //// Dump and see that none of deleted entries are there
+       //dump, err = NeighList(dummy.Index, 0)
+       //if err != nil {
+       //t.Errorf("Failed to NeighList: %v", err)
+       //}
+
+       //for _, entry := range arpTable {
+       //if dumpContains(dump, entry) {
+       //t.Errorf("Dump contains: %v", entry)
+       //}
+       //}
+
+       if err := LinkDel(&dummy); err != nil {
+               t.Fatal(err)
+       }
+}
+
+func TestNeighAddDelProxy(t *testing.T) {
+       tearDown := setUpNetlinkTest(t)
+       defer tearDown()
+
+       dummy := Dummy{LinkAttrs{Name: "neigh0"}}
+       if err := LinkAdd(&dummy); err != nil {
+               t.Fatal(err)
+       }
+
+       ensureIndex(dummy.Attrs())
+
+       proxyTable := []proxyEntry{
+               {net.ParseIP("10.99.0.1"), dummy.Index},
+               {net.ParseIP("10.99.0.2"), dummy.Index},
+               {net.ParseIP("10.99.0.3"), dummy.Index},
+               {net.ParseIP("10.99.0.4"), dummy.Index},
+               {net.ParseIP("10.99.0.5"), dummy.Index},
+       }
+
+       // Add the proxyTable
+       for _, entry := range proxyTable {
+               err := NeighAdd(&Neigh{
+                       LinkIndex: dummy.Index,
+                       Flags:     NTF_PROXY,
+                       IP:        entry.ip,
+               })
+
+               if err != nil {
+                       t.Errorf("Failed to NeighAdd: %v", err)
+               }
+       }
+
+       // Dump and see that all added entries are there
+       dump, err := NeighProxyList(dummy.Index, 0)
+       if err != nil {
+               t.Errorf("Failed to NeighList: %v", err)
+       }
+
+       for _, entry := range proxyTable {
+               if !dumpContainsProxy(dump, entry) {
+                       t.Errorf("Dump does not contain: %v", entry)
+               }
+       }
+
+       // Delete the proxyTable
+       for _, entry := range proxyTable {
+               err := NeighDel(&Neigh{
+                       LinkIndex: dummy.Index,
+                       Flags:     NTF_PROXY,
+                       IP:        entry.ip,
+               })
+
+               if err != nil {
+                       t.Errorf("Failed to NeighDel: %v", err)
+               }
+       }
+
+       // Dump and see that none of deleted entries are there
+       dump, err = NeighProxyList(dummy.Index, 0)
+       if err != nil {
+               t.Errorf("Failed to NeighList: %v", err)
+       }
+
+       for _, entry := range proxyTable {
+               if dumpContainsProxy(dump, entry) {
+                       t.Errorf("Dump contains: %v", entry)
+               }
+       }
+
+       if err := LinkDel(&dummy); err != nil {
+               t.Fatal(err)
+       }
+}
diff --git a/vendor/github.com/vishvananda/netlink/netlink_test.go b/vendor/github.com/vishvananda/netlink/netlink_test.go
new file mode 100644 (file)
index 0000000..5037b7f
--- /dev/null
@@ -0,0 +1,58 @@
+package netlink
+
+import (
+       "log"
+       "os"
+       "runtime"
+       "testing"
+
+       "github.com/vishvananda/netns"
+)
+
+type tearDownNetlinkTest func()
+
+func skipUnlessRoot(t *testing.T) {
+       if os.Getuid() != 0 {
+               msg := "Skipped test because it requires root privileges."
+               log.Printf(msg)
+               t.Skip(msg)
+       }
+}
+
+func setUpNetlinkTest(t *testing.T) tearDownNetlinkTest {
+       skipUnlessRoot(t)
+
+       // new temporary namespace so we don't pollute the host
+       // lock thread since the namespace is thread local
+       runtime.LockOSThread()
+       var err error
+       ns, err := netns.New()
+       if err != nil {
+               t.Fatal("Failed to create newns", ns)
+       }
+
+       return func() {
+               ns.Close()
+               runtime.UnlockOSThread()
+       }
+}
+
+func setUpMPLSNetlinkTest(t *testing.T) tearDownNetlinkTest {
+       if _, err := os.Stat("/proc/sys/net/mpls/platform_labels"); err != nil {
+               msg := "Skipped test because it requires MPLS support."
+               log.Printf(msg)
+               t.Skip(msg)
+       }
+       f := setUpNetlinkTest(t)
+       setUpF := func(path, value string) {
+               file, err := os.Create(path)
+               defer file.Close()
+               if err != nil {
+                       t.Fatalf("Failed to open %s: %s", path, err)
+               }
+               file.WriteString(value)
+       }
+       setUpF("/proc/sys/net/mpls/platform_labels", "1024")
+       setUpF("/proc/sys/net/mpls/conf/lo/input", "1")
+       return f
+}
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/addr_linux_test.go b/vendor/github.com/vishvananda/netlink/nl/addr_linux_test.go
new file mode 100644 (file)
index 0000000..07b0201
--- /dev/null
@@ -0,0 +1,68 @@
+package nl
+
+import (
+       "bytes"
+       "crypto/rand"
+       "encoding/binary"
+       "syscall"
+       "testing"
+)
+
+func (msg *IfAddrmsg) write(b []byte) {
+       native := NativeEndian()
+       b[0] = msg.Family
+       b[1] = msg.Prefixlen
+       b[2] = msg.Flags
+       b[3] = msg.Scope
+       native.PutUint32(b[4:8], msg.Index)
+}
+
+func (msg *IfAddrmsg) serializeSafe() []byte {
+       len := syscall.SizeofIfAddrmsg
+       b := make([]byte, len)
+       msg.write(b)
+       return b
+}
+
+func deserializeIfAddrmsgSafe(b []byte) *IfAddrmsg {
+       var msg = IfAddrmsg{}
+       binary.Read(bytes.NewReader(b[0:syscall.SizeofIfAddrmsg]), NativeEndian(), &msg)
+       return &msg
+}
+
+func TestIfAddrmsgDeserializeSerialize(t *testing.T) {
+       var orig = make([]byte, syscall.SizeofIfAddrmsg)
+       rand.Read(orig)
+       safemsg := deserializeIfAddrmsgSafe(orig)
+       msg := DeserializeIfAddrmsg(orig)
+       testDeserializeSerialize(t, orig, safemsg, msg)
+}
+
+func (msg *IfaCacheInfo) write(b []byte) {
+       native := NativeEndian()
+       native.PutUint32(b[0:4], uint32(msg.IfaPrefered))
+       native.PutUint32(b[4:8], uint32(msg.IfaValid))
+       native.PutUint32(b[8:12], uint32(msg.Cstamp))
+       native.PutUint32(b[12:16], uint32(msg.Tstamp))
+}
+
+func (msg *IfaCacheInfo) serializeSafe() []byte {
+       length := SizeofIfaCacheInfo
+       b := make([]byte, length)
+       msg.write(b)
+       return b
+}
+
+func deserializeIfaCacheInfoSafe(b []byte) *IfaCacheInfo {
+       var msg = IfaCacheInfo{}
+       binary.Read(bytes.NewReader(b[0:SizeofIfaCacheInfo]), NativeEndian(), &msg)
+       return &msg
+}
+
+func TestIfaCacheInfoDeserializeSerialize(t *testing.T) {
+       var orig = make([]byte, SizeofIfaCacheInfo)
+       rand.Read(orig)
+       safemsg := deserializeIfaCacheInfoSafe(orig)
+       msg := DeserializeIfaCacheInfo(orig)
+       testDeserializeSerialize(t, orig, safemsg, msg)
+}
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)))[:]
+}
index 6d9af56..dd03852 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 (
diff --git a/vendor/github.com/vishvananda/netlink/nl/link_linux_test.go b/vendor/github.com/vishvananda/netlink/nl/link_linux_test.go
new file mode 100644 (file)
index 0000000..ec8dff0
--- /dev/null
@@ -0,0 +1,199 @@
+package nl
+
+import (
+       "bytes"
+       "crypto/rand"
+       "encoding/binary"
+       "testing"
+)
+
+func (msg *VfMac) write(b []byte) {
+       native := NativeEndian()
+       native.PutUint32(b[0:4], uint32(msg.Vf))
+       copy(b[4:36], msg.Mac[:])
+}
+
+func (msg *VfMac) serializeSafe() []byte {
+       length := SizeofVfMac
+       b := make([]byte, length)
+       msg.write(b)
+       return b
+}
+
+func deserializeVfMacSafe(b []byte) *VfMac {
+       var msg = VfMac{}
+       binary.Read(bytes.NewReader(b[0:SizeofVfMac]), NativeEndian(), &msg)
+       return &msg
+}
+
+func TestVfMacDeserializeSerialize(t *testing.T) {
+       var orig = make([]byte, SizeofVfMac)
+       rand.Read(orig)
+       safemsg := deserializeVfMacSafe(orig)
+       msg := DeserializeVfMac(orig)
+       testDeserializeSerialize(t, orig, safemsg, msg)
+}
+
+func (msg *VfVlan) write(b []byte) {
+       native := NativeEndian()
+       native.PutUint32(b[0:4], uint32(msg.Vf))
+       native.PutUint32(b[4:8], uint32(msg.Vlan))
+       native.PutUint32(b[8:12], uint32(msg.Qos))
+}
+
+func (msg *VfVlan) serializeSafe() []byte {
+       length := SizeofVfVlan
+       b := make([]byte, length)
+       msg.write(b)
+       return b
+}
+
+func deserializeVfVlanSafe(b []byte) *VfVlan {
+       var msg = VfVlan{}
+       binary.Read(bytes.NewReader(b[0:SizeofVfVlan]), NativeEndian(), &msg)
+       return &msg
+}
+
+func TestVfVlanDeserializeSerialize(t *testing.T) {
+       var orig = make([]byte, SizeofVfVlan)
+       rand.Read(orig)
+       safemsg := deserializeVfVlanSafe(orig)
+       msg := DeserializeVfVlan(orig)
+       testDeserializeSerialize(t, orig, safemsg, msg)
+}
+
+func (msg *VfTxRate) write(b []byte) {
+       native := NativeEndian()
+       native.PutUint32(b[0:4], uint32(msg.Vf))
+       native.PutUint32(b[4:8], uint32(msg.Rate))
+}
+
+func (msg *VfTxRate) serializeSafe() []byte {
+       length := SizeofVfTxRate
+       b := make([]byte, length)
+       msg.write(b)
+       return b
+}
+
+func deserializeVfTxRateSafe(b []byte) *VfTxRate {
+       var msg = VfTxRate{}
+       binary.Read(bytes.NewReader(b[0:SizeofVfTxRate]), NativeEndian(), &msg)
+       return &msg
+}
+
+func TestVfTxRateDeserializeSerialize(t *testing.T) {
+       var orig = make([]byte, SizeofVfTxRate)
+       rand.Read(orig)
+       safemsg := deserializeVfTxRateSafe(orig)
+       msg := DeserializeVfTxRate(orig)
+       testDeserializeSerialize(t, orig, safemsg, msg)
+}
+
+func (msg *VfRate) write(b []byte) {
+       native := NativeEndian()
+       native.PutUint32(b[0:4], uint32(msg.Vf))
+       native.PutUint32(b[4:8], uint32(msg.MinTxRate))
+       native.PutUint32(b[8:12], uint32(msg.MaxTxRate))
+}
+
+func (msg *VfRate) serializeSafe() []byte {
+       length := SizeofVfRate
+       b := make([]byte, length)
+       msg.write(b)
+       return b
+}
+
+func deserializeVfRateSafe(b []byte) *VfRate {
+       var msg = VfRate{}
+       binary.Read(bytes.NewReader(b[0:SizeofVfRate]), NativeEndian(), &msg)
+       return &msg
+}
+
+func TestVfRateDeserializeSerialize(t *testing.T) {
+       var orig = make([]byte, SizeofVfRate)
+       rand.Read(orig)
+       safemsg := deserializeVfRateSafe(orig)
+       msg := DeserializeVfRate(orig)
+       testDeserializeSerialize(t, orig, safemsg, msg)
+}
+
+func (msg *VfSpoofchk) write(b []byte) {
+       native := NativeEndian()
+       native.PutUint32(b[0:4], uint32(msg.Vf))
+       native.PutUint32(b[4:8], uint32(msg.Setting))
+}
+
+func (msg *VfSpoofchk) serializeSafe() []byte {
+       length := SizeofVfSpoofchk
+       b := make([]byte, length)
+       msg.write(b)
+       return b
+}
+
+func deserializeVfSpoofchkSafe(b []byte) *VfSpoofchk {
+       var msg = VfSpoofchk{}
+       binary.Read(bytes.NewReader(b[0:SizeofVfSpoofchk]), NativeEndian(), &msg)
+       return &msg
+}
+
+func TestVfSpoofchkDeserializeSerialize(t *testing.T) {
+       var orig = make([]byte, SizeofVfSpoofchk)
+       rand.Read(orig)
+       safemsg := deserializeVfSpoofchkSafe(orig)
+       msg := DeserializeVfSpoofchk(orig)
+       testDeserializeSerialize(t, orig, safemsg, msg)
+}
+
+func (msg *VfLinkState) write(b []byte) {
+       native := NativeEndian()
+       native.PutUint32(b[0:4], uint32(msg.Vf))
+       native.PutUint32(b[4:8], uint32(msg.LinkState))
+}
+
+func (msg *VfLinkState) serializeSafe() []byte {
+       length := SizeofVfLinkState
+       b := make([]byte, length)
+       msg.write(b)
+       return b
+}
+
+func deserializeVfLinkStateSafe(b []byte) *VfLinkState {
+       var msg = VfLinkState{}
+       binary.Read(bytes.NewReader(b[0:SizeofVfLinkState]), NativeEndian(), &msg)
+       return &msg
+}
+
+func TestVfLinkStateDeserializeSerialize(t *testing.T) {
+       var orig = make([]byte, SizeofVfLinkState)
+       rand.Read(orig)
+       safemsg := deserializeVfLinkStateSafe(orig)
+       msg := DeserializeVfLinkState(orig)
+       testDeserializeSerialize(t, orig, safemsg, msg)
+}
+
+func (msg *VfRssQueryEn) write(b []byte) {
+       native := NativeEndian()
+       native.PutUint32(b[0:4], uint32(msg.Vf))
+       native.PutUint32(b[4:8], uint32(msg.Setting))
+}
+
+func (msg *VfRssQueryEn) serializeSafe() []byte {
+       length := SizeofVfRssQueryEn
+       b := make([]byte, length)
+       msg.write(b)
+       return b
+}
+
+func deserializeVfRssQueryEnSafe(b []byte) *VfRssQueryEn {
+       var msg = VfRssQueryEn{}
+       binary.Read(bytes.NewReader(b[0:SizeofVfRssQueryEn]), NativeEndian(), &msg)
+       return &msg
+}
+
+func TestVfRssQueryEnDeserializeSerialize(t *testing.T) {
+       var orig = make([]byte, SizeofVfRssQueryEn)
+       rand.Read(orig)
+       safemsg := deserializeVfRssQueryEnSafe(orig)
+       msg := DeserializeVfRssQueryEn(orig)
+       testDeserializeSerialize(t, orig, safemsg, msg)
+}
index fb9031e..5820e84 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.
@@ -451,7 +465,7 @@ type NetlinkSocket struct {
 }
 
 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
        }
diff --git a/vendor/github.com/vishvananda/netlink/nl/nl_linux_test.go b/vendor/github.com/vishvananda/netlink/nl/nl_linux_test.go
new file mode 100644 (file)
index 0000000..521a7ef
--- /dev/null
@@ -0,0 +1,62 @@
+package nl
+
+import (
+       "bytes"
+       "crypto/rand"
+       "encoding/binary"
+       "reflect"
+       "syscall"
+       "testing"
+)
+
+type testSerializer interface {
+       serializeSafe() []byte
+       Serialize() []byte
+}
+
+func testDeserializeSerialize(t *testing.T, orig []byte, safemsg testSerializer, msg testSerializer) {
+       if !reflect.DeepEqual(safemsg, msg) {
+               t.Fatal("Deserialization failed.\n", safemsg, "\n", msg)
+       }
+       safe := msg.serializeSafe()
+       if !bytes.Equal(safe, orig) {
+               t.Fatal("Safe serialization failed.\n", safe, "\n", orig)
+       }
+       b := msg.Serialize()
+       if !bytes.Equal(b, safe) {
+               t.Fatal("Serialization failed.\n", b, "\n", safe)
+       }
+}
+
+func (msg *IfInfomsg) write(b []byte) {
+       native := NativeEndian()
+       b[0] = msg.Family
+       // pad byte is skipped because it is not exported on linux/s390x
+       native.PutUint16(b[2:4], msg.Type)
+       native.PutUint32(b[4:8], uint32(msg.Index))
+       native.PutUint32(b[8:12], msg.Flags)
+       native.PutUint32(b[12:16], msg.Change)
+}
+
+func (msg *IfInfomsg) serializeSafe() []byte {
+       length := syscall.SizeofIfInfomsg
+       b := make([]byte, length)
+       msg.write(b)
+       return b
+}
+
+func deserializeIfInfomsgSafe(b []byte) *IfInfomsg {
+       var msg = IfInfomsg{}
+       binary.Read(bytes.NewReader(b[0:syscall.SizeofIfInfomsg]), NativeEndian(), &msg)
+       return &msg
+}
+
+func TestIfInfomsgDeserializeSerialize(t *testing.T) {
+       var orig = make([]byte, syscall.SizeofIfInfomsg)
+       rand.Read(orig)
+       // zero out the pad byte
+       orig[1] = 0
+       safemsg := deserializeIfInfomsgSafe(orig)
+       msg := DeserializeIfInfomsg(orig)
+       testDeserializeSerialize(t, orig, safemsg, msg)
+}
diff --git a/vendor/github.com/vishvananda/netlink/nl/route_linux_test.go b/vendor/github.com/vishvananda/netlink/nl/route_linux_test.go
new file mode 100644 (file)
index 0000000..ba9c410
--- /dev/null
@@ -0,0 +1,43 @@
+package nl
+
+import (
+       "bytes"
+       "crypto/rand"
+       "encoding/binary"
+       "syscall"
+       "testing"
+)
+
+func (msg *RtMsg) write(b []byte) {
+       native := NativeEndian()
+       b[0] = msg.Family
+       b[1] = msg.Dst_len
+       b[2] = msg.Src_len
+       b[3] = msg.Tos
+       b[4] = msg.Table
+       b[5] = msg.Protocol
+       b[6] = msg.Scope
+       b[7] = msg.Type
+       native.PutUint32(b[8:12], msg.Flags)
+}
+
+func (msg *RtMsg) serializeSafe() []byte {
+       len := syscall.SizeofRtMsg
+       b := make([]byte, len)
+       msg.write(b)
+       return b
+}
+
+func deserializeRtMsgSafe(b []byte) *RtMsg {
+       var msg = RtMsg{}
+       binary.Read(bytes.NewReader(b[0:syscall.SizeofRtMsg]), NativeEndian(), &msg)
+       return &msg
+}
+
+func TestRtMsgDeserializeSerialize(t *testing.T) {
+       var orig = make([]byte, syscall.SizeofRtMsg)
+       rand.Read(orig)
+       safemsg := deserializeRtMsgSafe(orig)
+       msg := DeserializeRtMsg(orig)
+       testDeserializeSerialize(t, orig, safemsg, msg)
+}
diff --git a/vendor/github.com/vishvananda/netlink/nl/tc_linux_test.go b/vendor/github.com/vishvananda/netlink/nl/tc_linux_test.go
new file mode 100644 (file)
index 0000000..148b2b0
--- /dev/null
@@ -0,0 +1,173 @@
+package nl
+
+import (
+       "bytes"
+       "crypto/rand"
+       "encoding/binary"
+       "testing"
+)
+
+/* TcMsg */
+func (msg *TcMsg) write(b []byte) {
+       native := NativeEndian()
+       b[0] = msg.Family
+       copy(b[1:4], msg.Pad[:])
+       native.PutUint32(b[4:8], uint32(msg.Ifindex))
+       native.PutUint32(b[8:12], msg.Handle)
+       native.PutUint32(b[12:16], msg.Parent)
+       native.PutUint32(b[16:20], msg.Info)
+}
+
+func (msg *TcMsg) serializeSafe() []byte {
+       length := SizeofTcMsg
+       b := make([]byte, length)
+       msg.write(b)
+       return b
+}
+
+func deserializeTcMsgSafe(b []byte) *TcMsg {
+       var msg = TcMsg{}
+       binary.Read(bytes.NewReader(b[0:SizeofTcMsg]), NativeEndian(), &msg)
+       return &msg
+}
+
+func TestTcMsgDeserializeSerialize(t *testing.T) {
+       var orig = make([]byte, SizeofTcMsg)
+       rand.Read(orig)
+       safemsg := deserializeTcMsgSafe(orig)
+       msg := DeserializeTcMsg(orig)
+       testDeserializeSerialize(t, orig, safemsg, msg)
+}
+
+/* TcActionMsg */
+func (msg *TcActionMsg) write(b []byte) {
+       b[0] = msg.Family
+       copy(b[1:4], msg.Pad[:])
+}
+
+func (msg *TcActionMsg) serializeSafe() []byte {
+       length := SizeofTcActionMsg
+       b := make([]byte, length)
+       msg.write(b)
+       return b
+}
+
+func deserializeTcActionMsgSafe(b []byte) *TcActionMsg {
+       var msg = TcActionMsg{}
+       binary.Read(bytes.NewReader(b[0:SizeofTcActionMsg]), NativeEndian(), &msg)
+       return &msg
+}
+
+func TestTcActionMsgDeserializeSerialize(t *testing.T) {
+       var orig = make([]byte, SizeofTcActionMsg)
+       rand.Read(orig)
+       safemsg := deserializeTcActionMsgSafe(orig)
+       msg := DeserializeTcActionMsg(orig)
+       testDeserializeSerialize(t, orig, safemsg, msg)
+}
+
+/* TcRateSpec */
+func (msg *TcRateSpec) write(b []byte) {
+       native := NativeEndian()
+       b[0] = msg.CellLog
+       b[1] = msg.Linklayer
+       native.PutUint16(b[2:4], msg.Overhead)
+       native.PutUint16(b[4:6], uint16(msg.CellAlign))
+       native.PutUint16(b[6:8], msg.Mpu)
+       native.PutUint32(b[8:12], msg.Rate)
+}
+
+func (msg *TcRateSpec) serializeSafe() []byte {
+       length := SizeofTcRateSpec
+       b := make([]byte, length)
+       msg.write(b)
+       return b
+}
+
+func deserializeTcRateSpecSafe(b []byte) *TcRateSpec {
+       var msg = TcRateSpec{}
+       binary.Read(bytes.NewReader(b[0:SizeofTcRateSpec]), NativeEndian(), &msg)
+       return &msg
+}
+
+func TestTcRateSpecDeserializeSerialize(t *testing.T) {
+       var orig = make([]byte, SizeofTcRateSpec)
+       rand.Read(orig)
+       safemsg := deserializeTcRateSpecSafe(orig)
+       msg := DeserializeTcRateSpec(orig)
+       testDeserializeSerialize(t, orig, safemsg, msg)
+}
+
+/* TcTbfQopt */
+func (msg *TcTbfQopt) write(b []byte) {
+       native := NativeEndian()
+       msg.Rate.write(b[0:SizeofTcRateSpec])
+       start := SizeofTcRateSpec
+       msg.Peakrate.write(b[start : start+SizeofTcRateSpec])
+       start += SizeofTcRateSpec
+       native.PutUint32(b[start:start+4], msg.Limit)
+       start += 4
+       native.PutUint32(b[start:start+4], msg.Buffer)
+       start += 4
+       native.PutUint32(b[start:start+4], msg.Mtu)
+}
+
+func (msg *TcTbfQopt) serializeSafe() []byte {
+       length := SizeofTcTbfQopt
+       b := make([]byte, length)
+       msg.write(b)
+       return b
+}
+
+func deserializeTcTbfQoptSafe(b []byte) *TcTbfQopt {
+       var msg = TcTbfQopt{}
+       binary.Read(bytes.NewReader(b[0:SizeofTcTbfQopt]), NativeEndian(), &msg)
+       return &msg
+}
+
+func TestTcTbfQoptDeserializeSerialize(t *testing.T) {
+       var orig = make([]byte, SizeofTcTbfQopt)
+       rand.Read(orig)
+       safemsg := deserializeTcTbfQoptSafe(orig)
+       msg := DeserializeTcTbfQopt(orig)
+       testDeserializeSerialize(t, orig, safemsg, msg)
+}
+
+/* TcHtbCopt */
+func (msg *TcHtbCopt) write(b []byte) {
+       native := NativeEndian()
+       msg.Rate.write(b[0:SizeofTcRateSpec])
+       start := SizeofTcRateSpec
+       msg.Ceil.write(b[start : start+SizeofTcRateSpec])
+       start += SizeofTcRateSpec
+       native.PutUint32(b[start:start+4], msg.Buffer)
+       start += 4
+       native.PutUint32(b[start:start+4], msg.Cbuffer)
+       start += 4
+       native.PutUint32(b[start:start+4], msg.Quantum)
+       start += 4
+       native.PutUint32(b[start:start+4], msg.Level)
+       start += 4
+       native.PutUint32(b[start:start+4], msg.Prio)
+}
+
+func (msg *TcHtbCopt) serializeSafe() []byte {
+       length := SizeofTcHtbCopt
+       b := make([]byte, length)
+       msg.write(b)
+       return b
+}
+
+func deserializeTcHtbCoptSafe(b []byte) *TcHtbCopt {
+       var msg = TcHtbCopt{}
+       binary.Read(bytes.NewReader(b[0:SizeofTcHtbCopt]), NativeEndian(), &msg)
+       return &msg
+}
+
+func TestTcHtbCoptDeserializeSerialize(t *testing.T) {
+       var orig = make([]byte, SizeofTcHtbCopt)
+       rand.Read(orig)
+       safemsg := deserializeTcHtbCoptSafe(orig)
+       msg := DeserializeTcHtbCopt(orig)
+       testDeserializeSerialize(t, orig, safemsg, msg)
+}
diff --git a/vendor/github.com/vishvananda/netlink/nl/xfrm_linux_test.go b/vendor/github.com/vishvananda/netlink/nl/xfrm_linux_test.go
new file mode 100644 (file)
index 0000000..04404d7
--- /dev/null
@@ -0,0 +1,161 @@
+package nl
+
+import (
+       "bytes"
+       "crypto/rand"
+       "encoding/binary"
+       "testing"
+)
+
+func (msg *XfrmAddress) write(b []byte) {
+       copy(b[0:SizeofXfrmAddress], msg[:])
+}
+
+func (msg *XfrmAddress) serializeSafe() []byte {
+       b := make([]byte, SizeofXfrmAddress)
+       msg.write(b)
+       return b
+}
+
+func deserializeXfrmAddressSafe(b []byte) *XfrmAddress {
+       var msg = XfrmAddress{}
+       binary.Read(bytes.NewReader(b[0:SizeofXfrmAddress]), NativeEndian(), &msg)
+       return &msg
+}
+
+func TestXfrmAddressDeserializeSerialize(t *testing.T) {
+       var orig = make([]byte, SizeofXfrmAddress)
+       rand.Read(orig)
+       safemsg := deserializeXfrmAddressSafe(orig)
+       msg := DeserializeXfrmAddress(orig)
+       testDeserializeSerialize(t, orig, safemsg, msg)
+}
+
+func (msg *XfrmSelector) write(b []byte) {
+       const AddrEnd = SizeofXfrmAddress * 2
+       native := NativeEndian()
+       msg.Daddr.write(b[0:SizeofXfrmAddress])
+       msg.Saddr.write(b[SizeofXfrmAddress:AddrEnd])
+       native.PutUint16(b[AddrEnd:AddrEnd+2], msg.Dport)
+       native.PutUint16(b[AddrEnd+2:AddrEnd+4], msg.DportMask)
+       native.PutUint16(b[AddrEnd+4:AddrEnd+6], msg.Sport)
+       native.PutUint16(b[AddrEnd+6:AddrEnd+8], msg.SportMask)
+       native.PutUint16(b[AddrEnd+8:AddrEnd+10], msg.Family)
+       b[AddrEnd+10] = msg.PrefixlenD
+       b[AddrEnd+11] = msg.PrefixlenS
+       b[AddrEnd+12] = msg.Proto
+       copy(b[AddrEnd+13:AddrEnd+16], msg.Pad[:])
+       native.PutUint32(b[AddrEnd+16:AddrEnd+20], uint32(msg.Ifindex))
+       native.PutUint32(b[AddrEnd+20:AddrEnd+24], msg.User)
+}
+
+func (msg *XfrmSelector) serializeSafe() []byte {
+       length := SizeofXfrmSelector
+       b := make([]byte, length)
+       msg.write(b)
+       return b
+}
+
+func deserializeXfrmSelectorSafe(b []byte) *XfrmSelector {
+       var msg = XfrmSelector{}
+       binary.Read(bytes.NewReader(b[0:SizeofXfrmSelector]), NativeEndian(), &msg)
+       return &msg
+}
+
+func TestXfrmSelectorDeserializeSerialize(t *testing.T) {
+       var orig = make([]byte, SizeofXfrmSelector)
+       rand.Read(orig)
+       safemsg := deserializeXfrmSelectorSafe(orig)
+       msg := DeserializeXfrmSelector(orig)
+       testDeserializeSerialize(t, orig, safemsg, msg)
+}
+
+func (msg *XfrmLifetimeCfg) write(b []byte) {
+       native := NativeEndian()
+       native.PutUint64(b[0:8], msg.SoftByteLimit)
+       native.PutUint64(b[8:16], msg.HardByteLimit)
+       native.PutUint64(b[16:24], msg.SoftPacketLimit)
+       native.PutUint64(b[24:32], msg.HardPacketLimit)
+       native.PutUint64(b[32:40], msg.SoftAddExpiresSeconds)
+       native.PutUint64(b[40:48], msg.HardAddExpiresSeconds)
+       native.PutUint64(b[48:56], msg.SoftUseExpiresSeconds)
+       native.PutUint64(b[56:64], msg.HardUseExpiresSeconds)
+}
+
+func (msg *XfrmLifetimeCfg) serializeSafe() []byte {
+       length := SizeofXfrmLifetimeCfg
+       b := make([]byte, length)
+       msg.write(b)
+       return b
+}
+
+func deserializeXfrmLifetimeCfgSafe(b []byte) *XfrmLifetimeCfg {
+       var msg = XfrmLifetimeCfg{}
+       binary.Read(bytes.NewReader(b[0:SizeofXfrmLifetimeCfg]), NativeEndian(), &msg)
+       return &msg
+}
+
+func TestXfrmLifetimeCfgDeserializeSerialize(t *testing.T) {
+       var orig = make([]byte, SizeofXfrmLifetimeCfg)
+       rand.Read(orig)
+       safemsg := deserializeXfrmLifetimeCfgSafe(orig)
+       msg := DeserializeXfrmLifetimeCfg(orig)
+       testDeserializeSerialize(t, orig, safemsg, msg)
+}
+
+func (msg *XfrmLifetimeCur) write(b []byte) {
+       native := NativeEndian()
+       native.PutUint64(b[0:8], msg.Bytes)
+       native.PutUint64(b[8:16], msg.Packets)
+       native.PutUint64(b[16:24], msg.AddTime)
+       native.PutUint64(b[24:32], msg.UseTime)
+}
+
+func (msg *XfrmLifetimeCur) serializeSafe() []byte {
+       length := SizeofXfrmLifetimeCur
+       b := make([]byte, length)
+       msg.write(b)
+       return b
+}
+
+func deserializeXfrmLifetimeCurSafe(b []byte) *XfrmLifetimeCur {
+       var msg = XfrmLifetimeCur{}
+       binary.Read(bytes.NewReader(b[0:SizeofXfrmLifetimeCur]), NativeEndian(), &msg)
+       return &msg
+}
+
+func TestXfrmLifetimeCurDeserializeSerialize(t *testing.T) {
+       var orig = make([]byte, SizeofXfrmLifetimeCur)
+       rand.Read(orig)
+       safemsg := deserializeXfrmLifetimeCurSafe(orig)
+       msg := DeserializeXfrmLifetimeCur(orig)
+       testDeserializeSerialize(t, orig, safemsg, msg)
+}
+
+func (msg *XfrmId) write(b []byte) {
+       native := NativeEndian()
+       msg.Daddr.write(b[0:SizeofXfrmAddress])
+       native.PutUint32(b[SizeofXfrmAddress:SizeofXfrmAddress+4], msg.Spi)
+       b[SizeofXfrmAddress+4] = msg.Proto
+       copy(b[SizeofXfrmAddress+5:SizeofXfrmAddress+8], msg.Pad[:])
+}
+
+func (msg *XfrmId) serializeSafe() []byte {
+       b := make([]byte, SizeofXfrmId)
+       msg.write(b)
+       return b
+}
+
+func deserializeXfrmIdSafe(b []byte) *XfrmId {
+       var msg = XfrmId{}
+       binary.Read(bytes.NewReader(b[0:SizeofXfrmId]), NativeEndian(), &msg)
+       return &msg
+}
+
+func TestXfrmIdDeserializeSerialize(t *testing.T) {
+       var orig = make([]byte, SizeofXfrmId)
+       rand.Read(orig)
+       safemsg := deserializeXfrmIdSafe(orig)
+       msg := DeserializeXfrmId(orig)
+       testDeserializeSerialize(t, orig, safemsg, msg)
+}
diff --git a/vendor/github.com/vishvananda/netlink/nl/xfrm_monitor_linux_test.go b/vendor/github.com/vishvananda/netlink/nl/xfrm_monitor_linux_test.go
new file mode 100644 (file)
index 0000000..3602caf
--- /dev/null
@@ -0,0 +1,34 @@
+package nl
+
+import (
+       "bytes"
+       "crypto/rand"
+       "encoding/binary"
+       "testing"
+)
+
+func (msg *XfrmUserExpire) write(b []byte) {
+       msg.XfrmUsersaInfo.write(b[0:SizeofXfrmUsersaInfo])
+       b[SizeofXfrmUsersaInfo] = msg.Hard
+       copy(b[SizeofXfrmUsersaInfo+1:SizeofXfrmUserExpire], msg.Pad[:])
+}
+
+func (msg *XfrmUserExpire) serializeSafe() []byte {
+       b := make([]byte, SizeofXfrmUserExpire)
+       msg.write(b)
+       return b
+}
+
+func deserializeXfrmUserExpireSafe(b []byte) *XfrmUserExpire {
+       var msg = XfrmUserExpire{}
+       binary.Read(bytes.NewReader(b[0:SizeofXfrmUserExpire]), NativeEndian(), &msg)
+       return &msg
+}
+
+func TestXfrmUserExpireDeserializeSerialize(t *testing.T) {
+       var orig = make([]byte, SizeofXfrmUserExpire)
+       rand.Read(orig)
+       safemsg := deserializeXfrmUserExpireSafe(orig)
+       msg := DeserializeXfrmUserExpire(orig)
+       testDeserializeSerialize(t, orig, safemsg, msg)
+}
diff --git a/vendor/github.com/vishvananda/netlink/nl/xfrm_policy_linux_test.go b/vendor/github.com/vishvananda/netlink/nl/xfrm_policy_linux_test.go
new file mode 100644 (file)
index 0000000..08a604b
--- /dev/null
@@ -0,0 +1,109 @@
+package nl
+
+import (
+       "bytes"
+       "crypto/rand"
+       "encoding/binary"
+       "testing"
+)
+
+func (msg *XfrmUserpolicyId) write(b []byte) {
+       native := NativeEndian()
+       msg.Sel.write(b[0:SizeofXfrmSelector])
+       native.PutUint32(b[SizeofXfrmSelector:SizeofXfrmSelector+4], msg.Index)
+       b[SizeofXfrmSelector+4] = msg.Dir
+       copy(b[SizeofXfrmSelector+5:SizeofXfrmSelector+8], msg.Pad[:])
+}
+
+func (msg *XfrmUserpolicyId) serializeSafe() []byte {
+       b := make([]byte, SizeofXfrmUserpolicyId)
+       msg.write(b)
+       return b
+}
+
+func deserializeXfrmUserpolicyIdSafe(b []byte) *XfrmUserpolicyId {
+       var msg = XfrmUserpolicyId{}
+       binary.Read(bytes.NewReader(b[0:SizeofXfrmUserpolicyId]), NativeEndian(), &msg)
+       return &msg
+}
+
+func TestXfrmUserpolicyIdDeserializeSerialize(t *testing.T) {
+       var orig = make([]byte, SizeofXfrmUserpolicyId)
+       rand.Read(orig)
+       safemsg := deserializeXfrmUserpolicyIdSafe(orig)
+       msg := DeserializeXfrmUserpolicyId(orig)
+       testDeserializeSerialize(t, orig, safemsg, msg)
+}
+
+func (msg *XfrmUserpolicyInfo) write(b []byte) {
+       const CfgEnd = SizeofXfrmSelector + SizeofXfrmLifetimeCfg
+       const CurEnd = CfgEnd + SizeofXfrmLifetimeCur
+       native := NativeEndian()
+       msg.Sel.write(b[0:SizeofXfrmSelector])
+       msg.Lft.write(b[SizeofXfrmSelector:CfgEnd])
+       msg.Curlft.write(b[CfgEnd:CurEnd])
+       native.PutUint32(b[CurEnd:CurEnd+4], msg.Priority)
+       native.PutUint32(b[CurEnd+4:CurEnd+8], msg.Index)
+       b[CurEnd+8] = msg.Dir
+       b[CurEnd+9] = msg.Action
+       b[CurEnd+10] = msg.Flags
+       b[CurEnd+11] = msg.Share
+       copy(b[CurEnd+12:CurEnd+16], msg.Pad[:])
+}
+
+func (msg *XfrmUserpolicyInfo) serializeSafe() []byte {
+       b := make([]byte, SizeofXfrmUserpolicyInfo)
+       msg.write(b)
+       return b
+}
+
+func deserializeXfrmUserpolicyInfoSafe(b []byte) *XfrmUserpolicyInfo {
+       var msg = XfrmUserpolicyInfo{}
+       binary.Read(bytes.NewReader(b[0:SizeofXfrmUserpolicyInfo]), NativeEndian(), &msg)
+       return &msg
+}
+
+func TestXfrmUserpolicyInfoDeserializeSerialize(t *testing.T) {
+       var orig = make([]byte, SizeofXfrmUserpolicyInfo)
+       rand.Read(orig)
+       safemsg := deserializeXfrmUserpolicyInfoSafe(orig)
+       msg := DeserializeXfrmUserpolicyInfo(orig)
+       testDeserializeSerialize(t, orig, safemsg, msg)
+}
+
+func (msg *XfrmUserTmpl) write(b []byte) {
+       const AddrEnd = SizeofXfrmId + 4 + SizeofXfrmAddress
+       native := NativeEndian()
+       msg.XfrmId.write(b[0:SizeofXfrmId])
+       native.PutUint16(b[SizeofXfrmId:SizeofXfrmId+2], msg.Family)
+       copy(b[SizeofXfrmId+2:SizeofXfrmId+4], msg.Pad1[:])
+       msg.Saddr.write(b[SizeofXfrmId+4 : AddrEnd])
+       native.PutUint32(b[AddrEnd:AddrEnd+4], msg.Reqid)
+       b[AddrEnd+4] = msg.Mode
+       b[AddrEnd+5] = msg.Share
+       b[AddrEnd+6] = msg.Optional
+       b[AddrEnd+7] = msg.Pad2
+       native.PutUint32(b[AddrEnd+8:AddrEnd+12], msg.Aalgos)
+       native.PutUint32(b[AddrEnd+12:AddrEnd+16], msg.Ealgos)
+       native.PutUint32(b[AddrEnd+16:AddrEnd+20], msg.Calgos)
+}
+
+func (msg *XfrmUserTmpl) serializeSafe() []byte {
+       b := make([]byte, SizeofXfrmUserTmpl)
+       msg.write(b)
+       return b
+}
+
+func deserializeXfrmUserTmplSafe(b []byte) *XfrmUserTmpl {
+       var msg = XfrmUserTmpl{}
+       binary.Read(bytes.NewReader(b[0:SizeofXfrmUserTmpl]), NativeEndian(), &msg)
+       return &msg
+}
+
+func TestXfrmUserTmplDeserializeSerialize(t *testing.T) {
+       var orig = make([]byte, SizeofXfrmUserTmpl)
+       rand.Read(orig)
+       safemsg := deserializeXfrmUserTmplSafe(orig)
+       msg := DeserializeXfrmUserTmpl(orig)
+       testDeserializeSerialize(t, orig, safemsg, msg)
+}
diff --git a/vendor/github.com/vishvananda/netlink/nl/xfrm_state_linux_test.go b/vendor/github.com/vishvananda/netlink/nl/xfrm_state_linux_test.go
new file mode 100644 (file)
index 0000000..5ede308
--- /dev/null
@@ -0,0 +1,304 @@
+package nl
+
+import (
+       "bytes"
+       "crypto/rand"
+       "encoding/binary"
+       "testing"
+)
+
+func (msg *XfrmUsersaId) write(b []byte) {
+       native := NativeEndian()
+       msg.Daddr.write(b[0:SizeofXfrmAddress])
+       native.PutUint32(b[SizeofXfrmAddress:SizeofXfrmAddress+4], msg.Spi)
+       native.PutUint16(b[SizeofXfrmAddress+4:SizeofXfrmAddress+6], msg.Family)
+       b[SizeofXfrmAddress+6] = msg.Proto
+       b[SizeofXfrmAddress+7] = msg.Pad
+}
+
+func (msg *XfrmUsersaId) serializeSafe() []byte {
+       b := make([]byte, SizeofXfrmUsersaId)
+       msg.write(b)
+       return b
+}
+
+func deserializeXfrmUsersaIdSafe(b []byte) *XfrmUsersaId {
+       var msg = XfrmUsersaId{}
+       binary.Read(bytes.NewReader(b[0:SizeofXfrmUsersaId]), NativeEndian(), &msg)
+       return &msg
+}
+
+func TestXfrmUsersaIdDeserializeSerialize(t *testing.T) {
+       var orig = make([]byte, SizeofXfrmUsersaId)
+       rand.Read(orig)
+       safemsg := deserializeXfrmUsersaIdSafe(orig)
+       msg := DeserializeXfrmUsersaId(orig)
+       testDeserializeSerialize(t, orig, safemsg, msg)
+}
+
+func (msg *XfrmStats) write(b []byte) {
+       native := NativeEndian()
+       native.PutUint32(b[0:4], msg.ReplayWindow)
+       native.PutUint32(b[4:8], msg.Replay)
+       native.PutUint32(b[8:12], msg.IntegrityFailed)
+}
+
+func (msg *XfrmStats) serializeSafe() []byte {
+       b := make([]byte, SizeofXfrmStats)
+       msg.write(b)
+       return b
+}
+
+func deserializeXfrmStatsSafe(b []byte) *XfrmStats {
+       var msg = XfrmStats{}
+       binary.Read(bytes.NewReader(b[0:SizeofXfrmStats]), NativeEndian(), &msg)
+       return &msg
+}
+
+func TestXfrmStatsDeserializeSerialize(t *testing.T) {
+       var orig = make([]byte, SizeofXfrmStats)
+       rand.Read(orig)
+       safemsg := deserializeXfrmStatsSafe(orig)
+       msg := DeserializeXfrmStats(orig)
+       testDeserializeSerialize(t, orig, safemsg, msg)
+}
+
+func (msg *XfrmUsersaInfo) write(b []byte) {
+       const IdEnd = SizeofXfrmSelector + SizeofXfrmId
+       const AddressEnd = IdEnd + SizeofXfrmAddress
+       const CfgEnd = AddressEnd + SizeofXfrmLifetimeCfg
+       const CurEnd = CfgEnd + SizeofXfrmLifetimeCur
+       const StatsEnd = CurEnd + SizeofXfrmStats
+       native := NativeEndian()
+       msg.Sel.write(b[0:SizeofXfrmSelector])
+       msg.Id.write(b[SizeofXfrmSelector:IdEnd])
+       msg.Saddr.write(b[IdEnd:AddressEnd])
+       msg.Lft.write(b[AddressEnd:CfgEnd])
+       msg.Curlft.write(b[CfgEnd:CurEnd])
+       msg.Stats.write(b[CurEnd:StatsEnd])
+       native.PutUint32(b[StatsEnd:StatsEnd+4], msg.Seq)
+       native.PutUint32(b[StatsEnd+4:StatsEnd+8], msg.Reqid)
+       native.PutUint16(b[StatsEnd+8:StatsEnd+10], msg.Family)
+       b[StatsEnd+10] = msg.Mode
+       b[StatsEnd+11] = msg.ReplayWindow
+       b[StatsEnd+12] = msg.Flags
+       copy(b[StatsEnd+13:StatsEnd+20], msg.Pad[:])
+}
+
+func (msg *XfrmUsersaInfo) serializeSafe() []byte {
+       b := make([]byte, SizeofXfrmUsersaInfo)
+       msg.write(b)
+       return b
+}
+
+func deserializeXfrmUsersaInfoSafe(b []byte) *XfrmUsersaInfo {
+       var msg = XfrmUsersaInfo{}
+       binary.Read(bytes.NewReader(b[0:SizeofXfrmUsersaInfo]), NativeEndian(), &msg)
+       return &msg
+}
+
+func TestXfrmUsersaInfoDeserializeSerialize(t *testing.T) {
+       var orig = make([]byte, SizeofXfrmUsersaInfo)
+       rand.Read(orig)
+       safemsg := deserializeXfrmUsersaInfoSafe(orig)
+       msg := DeserializeXfrmUsersaInfo(orig)
+       testDeserializeSerialize(t, orig, safemsg, msg)
+}
+
+func (msg *XfrmAlgo) write(b []byte) {
+       native := NativeEndian()
+       copy(b[0:64], msg.AlgName[:])
+       native.PutUint32(b[64:68], msg.AlgKeyLen)
+       copy(b[68:msg.Len()], msg.AlgKey[:])
+}
+
+func (msg *XfrmAlgo) serializeSafe() []byte {
+       b := make([]byte, msg.Len())
+       msg.write(b)
+       return b
+}
+
+func (msg *XfrmUserSpiInfo) write(b []byte) {
+       native := NativeEndian()
+       msg.XfrmUsersaInfo.write(b[0:SizeofXfrmUsersaInfo])
+       native.PutUint32(b[SizeofXfrmUsersaInfo:SizeofXfrmUsersaInfo+4], msg.Min)
+       native.PutUint32(b[SizeofXfrmUsersaInfo+4:SizeofXfrmUsersaInfo+8], msg.Max)
+}
+
+func (msg *XfrmUserSpiInfo) serializeSafe() []byte {
+       b := make([]byte, SizeofXfrmUserSpiInfo)
+       msg.write(b)
+       return b
+}
+
+func deserializeXfrmUserSpiInfoSafe(b []byte) *XfrmUserSpiInfo {
+       var msg = XfrmUserSpiInfo{}
+       binary.Read(bytes.NewReader(b[0:SizeofXfrmUserSpiInfo]), NativeEndian(), &msg)
+       return &msg
+}
+
+func TestXfrmUserSpiInfoDeserializeSerialize(t *testing.T) {
+       var orig = make([]byte, SizeofXfrmUserSpiInfo)
+       rand.Read(orig)
+       safemsg := deserializeXfrmUserSpiInfoSafe(orig)
+       msg := DeserializeXfrmUserSpiInfo(orig)
+       testDeserializeSerialize(t, orig, safemsg, msg)
+}
+
+func deserializeXfrmAlgoSafe(b []byte) *XfrmAlgo {
+       var msg = XfrmAlgo{}
+       copy(msg.AlgName[:], b[0:64])
+       binary.Read(bytes.NewReader(b[64:68]), NativeEndian(), &msg.AlgKeyLen)
+       msg.AlgKey = b[68:msg.Len()]
+       return &msg
+}
+
+func TestXfrmAlgoDeserializeSerialize(t *testing.T) {
+       native := NativeEndian()
+       // use a 32 byte key len
+       var orig = make([]byte, SizeofXfrmAlgo+32)
+       rand.Read(orig)
+       // set the key len to 256 bits
+       var KeyLen uint32 = 0x00000100
+       // Little Endian    Big Endian
+       // orig[64] = 0     orig[64] = 0
+       // orig[65] = 1     orig[65] = 0
+       // orig[66] = 0     orig[66] = 1
+       // orig[67] = 0     orig[67] = 0
+       native.PutUint32(orig[64:68], KeyLen)
+       safemsg := deserializeXfrmAlgoSafe(orig)
+       msg := DeserializeXfrmAlgo(orig)
+       testDeserializeSerialize(t, orig, safemsg, msg)
+}
+
+func (msg *XfrmAlgoAuth) write(b []byte) {
+       native := NativeEndian()
+       copy(b[0:64], msg.AlgName[:])
+       native.PutUint32(b[64:68], msg.AlgKeyLen)
+       native.PutUint32(b[68:72], msg.AlgTruncLen)
+       copy(b[72:msg.Len()], msg.AlgKey[:])
+}
+
+func (msg *XfrmAlgoAuth) serializeSafe() []byte {
+       b := make([]byte, msg.Len())
+       msg.write(b)
+       return b
+}
+
+func deserializeXfrmAlgoAuthSafe(b []byte) *XfrmAlgoAuth {
+       var msg = XfrmAlgoAuth{}
+       copy(msg.AlgName[:], b[0:64])
+       binary.Read(bytes.NewReader(b[64:68]), NativeEndian(), &msg.AlgKeyLen)
+       binary.Read(bytes.NewReader(b[68:72]), NativeEndian(), &msg.AlgTruncLen)
+       msg.AlgKey = b[72:msg.Len()]
+       return &msg
+}
+
+func TestXfrmAlgoAuthDeserializeSerialize(t *testing.T) {
+       native := NativeEndian()
+       // use a 32 byte key len
+       var orig = make([]byte, SizeofXfrmAlgoAuth+32)
+       rand.Read(orig)
+       // set the key len to 256 bits
+       var KeyLen uint32 = 0x00000100
+       // Little Endian    Big Endian
+       // orig[64] = 0     orig[64] = 0
+       // orig[65] = 1     orig[65] = 0
+       // orig[66] = 0     orig[66] = 1
+       // orig[67] = 0     orig[67] = 0
+       native.PutUint32(orig[64:68], KeyLen)
+       safemsg := deserializeXfrmAlgoAuthSafe(orig)
+       msg := DeserializeXfrmAlgoAuth(orig)
+       testDeserializeSerialize(t, orig, safemsg, msg)
+}
+
+func (msg *XfrmEncapTmpl) write(b []byte) {
+       native := NativeEndian()
+       native.PutUint16(b[0:2], msg.EncapType)
+       native.PutUint16(b[2:4], msg.EncapSport)
+       native.PutUint16(b[4:6], msg.EncapDport)
+       copy(b[6:8], msg.Pad[:])
+       msg.EncapOa.write(b[8:SizeofXfrmAddress])
+}
+
+func (msg *XfrmEncapTmpl) serializeSafe() []byte {
+       b := make([]byte, SizeofXfrmEncapTmpl)
+       msg.write(b)
+       return b
+}
+
+func deserializeXfrmEncapTmplSafe(b []byte) *XfrmEncapTmpl {
+       var msg = XfrmEncapTmpl{}
+       binary.Read(bytes.NewReader(b[0:SizeofXfrmEncapTmpl]), NativeEndian(), &msg)
+       return &msg
+}
+
+func TestXfrmEncapTmplDeserializeSerialize(t *testing.T) {
+       var orig = make([]byte, SizeofXfrmEncapTmpl)
+       rand.Read(orig)
+       safemsg := deserializeXfrmEncapTmplSafe(orig)
+       msg := DeserializeXfrmEncapTmpl(orig)
+       testDeserializeSerialize(t, orig, safemsg, msg)
+}
+
+func (msg *XfrmMark) write(b []byte) {
+       native := NativeEndian()
+       native.PutUint32(b[0:4], msg.Value)
+       native.PutUint32(b[4:8], msg.Mask)
+}
+
+func (msg *XfrmMark) serializeSafe() []byte {
+       b := make([]byte, SizeofXfrmMark)
+       msg.write(b)
+       return b
+}
+
+func deserializeXfrmMarkSafe(b []byte) *XfrmMark {
+       var msg = XfrmMark{}
+       binary.Read(bytes.NewReader(b[0:SizeofXfrmMark]), NativeEndian(), &msg)
+       return &msg
+}
+
+func TestXfrmMarkDeserializeSerialize(t *testing.T) {
+       var orig = make([]byte, SizeofXfrmMark)
+       rand.Read(orig)
+       safemsg := deserializeXfrmMarkSafe(orig)
+       msg := DeserializeXfrmMark(orig)
+       testDeserializeSerialize(t, orig, safemsg, msg)
+}
+
+func (msg *XfrmAlgoAEAD) write(b []byte) {
+       native := NativeEndian()
+       copy(b[0:64], msg.AlgName[:])
+       native.PutUint32(b[64:68], msg.AlgKeyLen)
+       native.PutUint32(b[68:72], msg.AlgICVLen)
+       copy(b[72:msg.Len()], msg.AlgKey[:])
+}
+
+func (msg *XfrmAlgoAEAD) serializeSafe() []byte {
+       b := make([]byte, msg.Len())
+       msg.write(b)
+       return b
+}
+
+func deserializeXfrmAlgoAEADSafe(b []byte) *XfrmAlgoAEAD {
+       var msg = XfrmAlgoAEAD{}
+       copy(msg.AlgName[:], b[0:64])
+       binary.Read(bytes.NewReader(b[64:68]), NativeEndian(), &msg.AlgKeyLen)
+       binary.Read(bytes.NewReader(b[68:72]), NativeEndian(), &msg.AlgICVLen)
+       msg.AlgKey = b[72:msg.Len()]
+       return &msg
+}
+
+func TestXfrmXfrmAlgoAeadDeserializeSerialize(t *testing.T) {
+       native := NativeEndian()
+       // use a 32 byte key len
+       var orig = make([]byte, SizeofXfrmAlgoAEAD+36)
+       rand.Read(orig)
+       // set the key len to (256 + 32) bits
+       var KeyLen uint32 = 0x00000120
+       native.PutUint32(orig[64:68], KeyLen)
+       safemsg := deserializeXfrmAlgoAEADSafe(orig)
+       msg := DeserializeXfrmAlgoAEAD(orig)
+       testDeserializeSerialize(t, orig, safemsg, msg)
+}
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
diff --git a/vendor/github.com/vishvananda/netlink/protinfo_test.go b/vendor/github.com/vishvananda/netlink/protinfo_test.go
new file mode 100644 (file)
index 0000000..680f461
--- /dev/null
@@ -0,0 +1,163 @@
+// +build linux
+
+package netlink
+
+import (
+       "os"
+       "testing"
+)
+
+func TestProtinfo(t *testing.T) {
+       tearDown := setUpNetlinkTest(t)
+       defer tearDown()
+       master := &Bridge{LinkAttrs{Name: "foo"}}
+       if err := LinkAdd(master); err != nil {
+               t.Fatal(err)
+       }
+       iface1 := &Dummy{LinkAttrs{Name: "bar1", MasterIndex: master.Index}}
+       iface2 := &Dummy{LinkAttrs{Name: "bar2", MasterIndex: master.Index}}
+       iface3 := &Dummy{LinkAttrs{Name: "bar3"}}
+       iface4 := &Dummy{LinkAttrs{Name: "bar4", MasterIndex: master.Index}}
+
+       if err := LinkAdd(iface1); err != nil {
+               t.Fatal(err)
+       }
+       if err := LinkAdd(iface2); err != nil {
+               t.Fatal(err)
+       }
+       if err := LinkAdd(iface3); err != nil {
+               t.Fatal(err)
+       }
+       if err := LinkAdd(iface4); err != nil {
+               t.Fatal(err)
+       }
+
+       oldpi1, err := LinkGetProtinfo(iface1)
+       if err != nil {
+               t.Fatal(err)
+       }
+       oldpi2, err := LinkGetProtinfo(iface2)
+       if err != nil {
+               t.Fatal(err)
+       }
+       oldpi4, err := LinkGetProtinfo(iface4)
+       if err != nil {
+               t.Fatal(err)
+       }
+
+       if err := LinkSetHairpin(iface1, true); err != nil {
+               t.Fatal(err)
+       }
+
+       if err := LinkSetRootBlock(iface1, true); err != nil {
+               t.Fatal(err)
+       }
+
+       pi1, err := LinkGetProtinfo(iface1)
+       if err != nil {
+               t.Fatal(err)
+       }
+       if !pi1.Hairpin {
+               t.Fatalf("Hairpin mode is not enabled for %s, but should", iface1.Name)
+       }
+       if !pi1.RootBlock {
+               t.Fatalf("RootBlock is not enabled for %s, but should", iface1.Name)
+       }
+       if pi1.ProxyArp != oldpi1.ProxyArp {
+               t.Fatalf("ProxyArp field was changed for %s but shouldn't", iface1.Name)
+       }
+       if pi1.ProxyArpWiFi != oldpi1.ProxyArp {
+               t.Fatalf("ProxyArpWiFi ProxyArp field was changed for %s but shouldn't", iface1.Name)
+       }
+       if pi1.Guard != oldpi1.Guard {
+               t.Fatalf("Guard field was changed for %s but shouldn't", iface1.Name)
+       }
+       if pi1.FastLeave != oldpi1.FastLeave {
+               t.Fatalf("FastLeave field was changed for %s but shouldn't", iface1.Name)
+       }
+       if pi1.Learning != oldpi1.Learning {
+               t.Fatalf("Learning field was changed for %s but shouldn't", iface1.Name)
+       }
+       if pi1.Flood != oldpi1.Flood {
+               t.Fatalf("Flood field was changed for %s but shouldn't", iface1.Name)
+       }
+
+       if err := LinkSetGuard(iface2, true); err != nil {
+               t.Fatal(err)
+       }
+       if err := LinkSetLearning(iface2, false); err != nil {
+               t.Fatal(err)
+       }
+       pi2, err := LinkGetProtinfo(iface2)
+       if err != nil {
+               t.Fatal(err)
+       }
+       if pi2.Hairpin {
+               t.Fatalf("Hairpin mode is enabled for %s, but shouldn't", iface2.Name)
+       }
+       if !pi2.Guard {
+               t.Fatalf("Guard is not enabled for %s, but should", iface2.Name)
+       }
+       if pi2.ProxyArp != oldpi2.ProxyArp {
+               t.Fatalf("ProxyArp field was changed for %s but shouldn't", iface2.Name)
+       }
+       if pi2.ProxyArpWiFi != oldpi2.ProxyArpWiFi {
+               t.Fatalf("ProxyArpWiFi field was changed for %s but shouldn't", iface2.Name)
+       }
+       if pi2.Learning {
+               t.Fatalf("Learning is enabled for %s, but shouldn't", iface2.Name)
+       }
+       if pi2.RootBlock != oldpi2.RootBlock {
+               t.Fatalf("RootBlock field was changed for %s but shouldn't", iface2.Name)
+       }
+       if pi2.FastLeave != oldpi2.FastLeave {
+               t.Fatalf("FastLeave field was changed for %s but shouldn't", iface2.Name)
+       }
+       if pi2.Flood != oldpi2.Flood {
+               t.Fatalf("Flood field was changed for %s but shouldn't", iface2.Name)
+       }
+
+       if err := LinkSetHairpin(iface3, true); err == nil || err.Error() != "operation not supported" {
+               t.Fatalf("Set protinfo attrs for link without master is not supported, but err: %s", err)
+       }
+
+       if os.Getenv("TRAVIS_BUILD_DIR") != "" {
+               t.Skipf("Skipped some tests because travis kernel is to old to support BR_PROXYARP.")
+       }
+
+       if err := LinkSetBrProxyArp(iface4, true); err != nil {
+               t.Fatal(err)
+       }
+
+       if err := LinkSetBrProxyArpWiFi(iface4, true); err != nil {
+               t.Fatal(err)
+       }
+       pi4, err := LinkGetProtinfo(iface4)
+       if err != nil {
+               t.Fatal(err)
+       }
+       if pi4.Hairpin != oldpi4.Hairpin {
+               t.Fatalf("Hairpin field was changed for %s but shouldn't", iface4.Name)
+       }
+       if pi4.Guard != oldpi4.Guard {
+               t.Fatalf("Guard field was changed for %s but shouldn't", iface4.Name)
+       }
+       if pi4.Learning != oldpi4.Learning {
+               t.Fatalf("Learning field was changed for %s but shouldn't", iface4.Name)
+       }
+       if !pi4.ProxyArp {
+               t.Fatalf("ProxyArp is not enabled for %s, but should", iface4.Name)
+       }
+       if !pi4.ProxyArpWiFi {
+               t.Fatalf("ProxyArpWiFi is not enabled for %s, but should", iface4.Name)
+       }
+       if pi4.RootBlock != oldpi4.RootBlock {
+               t.Fatalf("RootBlock field was changed for %s but shouldn't", iface4.Name)
+       }
+       if pi4.FastLeave != oldpi4.FastLeave {
+               t.Fatalf("FastLeave field was changed for %s but shouldn't", iface4.Name)
+       }
+       if pi4.Flood != oldpi4.Flood {
+               t.Fatalf("Flood field was changed for %s but shouldn't", iface4.Name)
+       }
+}
diff --git a/vendor/github.com/vishvananda/netlink/qdisc_test.go b/vendor/github.com/vishvananda/netlink/qdisc_test.go
new file mode 100644 (file)
index 0000000..dff58f9
--- /dev/null
@@ -0,0 +1,347 @@
+// +build linux
+
+package netlink
+
+import (
+       "testing"
+)
+
+func TestTbfAddDel(t *testing.T) {
+       tearDown := setUpNetlinkTest(t)
+       defer tearDown()
+       if err := LinkAdd(&Ifb{LinkAttrs{Name: "foo"}}); err != nil {
+               t.Fatal(err)
+       }
+       link, err := LinkByName("foo")
+       if err != nil {
+               t.Fatal(err)
+       }
+       if err := LinkSetUp(link); err != nil {
+               t.Fatal(err)
+       }
+       qdisc := &Tbf{
+               QdiscAttrs: QdiscAttrs{
+                       LinkIndex: link.Attrs().Index,
+                       Handle:    MakeHandle(1, 0),
+                       Parent:    HANDLE_ROOT,
+               },
+               Rate:   131072,
+               Limit:  1220703,
+               Buffer: 16793,
+       }
+       if err := QdiscAdd(qdisc); err != nil {
+               t.Fatal(err)
+       }
+       qdiscs, err := QdiscList(link)
+       if err != nil {
+               t.Fatal(err)
+       }
+       if len(qdiscs) != 1 {
+               t.Fatal("Failed to add qdisc")
+       }
+       tbf, ok := qdiscs[0].(*Tbf)
+       if !ok {
+               t.Fatal("Qdisc is the wrong type")
+       }
+       if tbf.Rate != qdisc.Rate {
+               t.Fatal("Rate doesn't match")
+       }
+       if tbf.Limit != qdisc.Limit {
+               t.Fatal("Limit doesn't match")
+       }
+       if tbf.Buffer != qdisc.Buffer {
+               t.Fatal("Buffer doesn't match")
+       }
+       if err := QdiscDel(qdisc); err != nil {
+               t.Fatal(err)
+       }
+       qdiscs, err = QdiscList(link)
+       if err != nil {
+               t.Fatal(err)
+       }
+       if len(qdiscs) != 0 {
+               t.Fatal("Failed to remove qdisc")
+       }
+}
+
+func TestHtbAddDel(t *testing.T) {
+       tearDown := setUpNetlinkTest(t)
+       defer tearDown()
+       if err := LinkAdd(&Ifb{LinkAttrs{Name: "foo"}}); err != nil {
+               t.Fatal(err)
+       }
+       link, err := LinkByName("foo")
+       if err != nil {
+               t.Fatal(err)
+       }
+       if err := LinkSetUp(link); err != nil {
+               t.Fatal(err)
+       }
+
+       attrs := QdiscAttrs{
+               LinkIndex: link.Attrs().Index,
+               Handle:    MakeHandle(1, 0),
+               Parent:    HANDLE_ROOT,
+       }
+
+       qdisc := NewHtb(attrs)
+       qdisc.Rate2Quantum = 5
+       if err := QdiscAdd(qdisc); err != nil {
+               t.Fatal(err)
+       }
+
+       qdiscs, err := QdiscList(link)
+       if err != nil {
+               t.Fatal(err)
+       }
+       if len(qdiscs) != 1 {
+               t.Fatal("Failed to add qdisc")
+       }
+       htb, ok := qdiscs[0].(*Htb)
+       if !ok {
+               t.Fatal("Qdisc is the wrong type")
+       }
+       if htb.Defcls != qdisc.Defcls {
+               t.Fatal("Defcls doesn't match")
+       }
+       if htb.Rate2Quantum != qdisc.Rate2Quantum {
+               t.Fatal("Rate2Quantum doesn't match")
+       }
+       if htb.Debug != qdisc.Debug {
+               t.Fatal("Debug doesn't match")
+       }
+       if err := QdiscDel(qdisc); err != nil {
+               t.Fatal(err)
+       }
+       qdiscs, err = QdiscList(link)
+       if err != nil {
+               t.Fatal(err)
+       }
+       if len(qdiscs) != 0 {
+               t.Fatal("Failed to remove qdisc")
+       }
+}
+
+func TestPrioAddDel(t *testing.T) {
+       tearDown := setUpNetlinkTest(t)
+       defer tearDown()
+       if err := LinkAdd(&Ifb{LinkAttrs{Name: "foo"}}); err != nil {
+               t.Fatal(err)
+       }
+       link, err := LinkByName("foo")
+       if err != nil {
+               t.Fatal(err)
+       }
+       if err := LinkSetUp(link); err != nil {
+               t.Fatal(err)
+       }
+       qdisc := NewPrio(QdiscAttrs{
+               LinkIndex: link.Attrs().Index,
+               Handle:    MakeHandle(1, 0),
+               Parent:    HANDLE_ROOT,
+       })
+       if err := QdiscAdd(qdisc); err != nil {
+               t.Fatal(err)
+       }
+       qdiscs, err := QdiscList(link)
+       if err != nil {
+               t.Fatal(err)
+       }
+       if len(qdiscs) != 1 {
+               t.Fatal("Failed to add qdisc")
+       }
+       _, ok := qdiscs[0].(*Prio)
+       if !ok {
+               t.Fatal("Qdisc is the wrong type")
+       }
+       if err := QdiscDel(qdisc); err != nil {
+               t.Fatal(err)
+       }
+       qdiscs, err = QdiscList(link)
+       if err != nil {
+               t.Fatal(err)
+       }
+       if len(qdiscs) != 0 {
+               t.Fatal("Failed to remove qdisc")
+       }
+}
+
+func TestTbfAddHtbReplaceDel(t *testing.T) {
+       tearDown := setUpNetlinkTest(t)
+       defer tearDown()
+       if err := LinkAdd(&Ifb{LinkAttrs{Name: "foo"}}); err != nil {
+               t.Fatal(err)
+       }
+       link, err := LinkByName("foo")
+       if err != nil {
+               t.Fatal(err)
+       }
+       if err := LinkSetUp(link); err != nil {
+               t.Fatal(err)
+       }
+
+       // Add
+       attrs := QdiscAttrs{
+               LinkIndex: link.Attrs().Index,
+               Handle:    MakeHandle(1, 0),
+               Parent:    HANDLE_ROOT,
+       }
+       qdisc := &Tbf{
+               QdiscAttrs: attrs,
+               Rate:       131072,
+               Limit:      1220703,
+               Buffer:     16793,
+       }
+       if err := QdiscAdd(qdisc); err != nil {
+               t.Fatal(err)
+       }
+       qdiscs, err := QdiscList(link)
+       if err != nil {
+               t.Fatal(err)
+       }
+       if len(qdiscs) != 1 {
+               t.Fatal("Failed to add qdisc")
+       }
+       tbf, ok := qdiscs[0].(*Tbf)
+       if !ok {
+               t.Fatal("Qdisc is the wrong type")
+       }
+       if tbf.Rate != qdisc.Rate {
+               t.Fatal("Rate doesn't match")
+       }
+       if tbf.Limit != qdisc.Limit {
+               t.Fatal("Limit doesn't match")
+       }
+       if tbf.Buffer != qdisc.Buffer {
+               t.Fatal("Buffer doesn't match")
+       }
+       // Replace
+       // For replace to work, the handle MUST be different that the running one
+       attrs.Handle = MakeHandle(2, 0)
+       qdisc2 := NewHtb(attrs)
+       qdisc2.Rate2Quantum = 5
+       if err := QdiscReplace(qdisc2); err != nil {
+               t.Fatal(err)
+       }
+
+       qdiscs, err = QdiscList(link)
+       if err != nil {
+               t.Fatal(err)
+       }
+       if len(qdiscs) != 1 {
+               t.Fatal("Failed to add qdisc")
+       }
+       htb, ok := qdiscs[0].(*Htb)
+       if !ok {
+               t.Fatal("Qdisc is the wrong type")
+       }
+       if htb.Defcls != qdisc2.Defcls {
+               t.Fatal("Defcls doesn't match")
+       }
+       if htb.Rate2Quantum != qdisc2.Rate2Quantum {
+               t.Fatal("Rate2Quantum doesn't match")
+       }
+       if htb.Debug != qdisc2.Debug {
+               t.Fatal("Debug doesn't match")
+       }
+
+       if err := QdiscDel(qdisc2); err != nil {
+               t.Fatal(err)
+       }
+       qdiscs, err = QdiscList(link)
+       if err != nil {
+               t.Fatal(err)
+       }
+       if len(qdiscs) != 0 {
+               t.Fatal("Failed to remove qdisc")
+       }
+}
+
+func TestTbfAddTbfChangeDel(t *testing.T) {
+       tearDown := setUpNetlinkTest(t)
+       defer tearDown()
+       if err := LinkAdd(&Ifb{LinkAttrs{Name: "foo"}}); err != nil {
+               t.Fatal(err)
+       }
+       link, err := LinkByName("foo")
+       if err != nil {
+               t.Fatal(err)
+       }
+       if err := LinkSetUp(link); err != nil {
+               t.Fatal(err)
+       }
+
+       // Add
+       attrs := QdiscAttrs{
+               LinkIndex: link.Attrs().Index,
+               Handle:    MakeHandle(1, 0),
+               Parent:    HANDLE_ROOT,
+       }
+       qdisc := &Tbf{
+               QdiscAttrs: attrs,
+               Rate:       131072,
+               Limit:      1220703,
+               Buffer:     16793,
+       }
+       if err := QdiscAdd(qdisc); err != nil {
+               t.Fatal(err)
+       }
+       qdiscs, err := QdiscList(link)
+       if err != nil {
+               t.Fatal(err)
+       }
+       if len(qdiscs) != 1 {
+               t.Fatal("Failed to add qdisc")
+       }
+       tbf, ok := qdiscs[0].(*Tbf)
+       if !ok {
+               t.Fatal("Qdisc is the wrong type")
+       }
+       if tbf.Rate != qdisc.Rate {
+               t.Fatal("Rate doesn't match")
+       }
+       if tbf.Limit != qdisc.Limit {
+               t.Fatal("Limit doesn't match")
+       }
+       if tbf.Buffer != qdisc.Buffer {
+               t.Fatal("Buffer doesn't match")
+       }
+       // Change
+       // For change to work, the handle MUST not change
+       qdisc.Rate = 23456
+       if err := QdiscChange(qdisc); err != nil {
+               t.Fatal(err)
+       }
+
+       qdiscs, err = QdiscList(link)
+       if err != nil {
+               t.Fatal(err)
+       }
+       if len(qdiscs) != 1 {
+               t.Fatal("Failed to add qdisc")
+       }
+       tbf, ok = qdiscs[0].(*Tbf)
+       if !ok {
+               t.Fatal("Qdisc is the wrong type")
+       }
+       if tbf.Rate != qdisc.Rate {
+               t.Fatal("Rate doesn't match")
+       }
+       if tbf.Limit != qdisc.Limit {
+               t.Fatal("Limit doesn't match")
+       }
+       if tbf.Buffer != qdisc.Buffer {
+               t.Fatal("Buffer doesn't match")
+       }
+
+       if err := QdiscDel(qdisc); err != nil {
+               t.Fatal(err)
+       }
+       qdiscs, err = QdiscList(link)
+       if err != nil {
+               t.Fatal(err)
+       }
+       if len(qdiscs) != 0 {
+               t.Fatal("Failed to remove qdisc")
+       }
+}
diff --git a/vendor/github.com/vishvananda/netlink/route_test.go b/vendor/github.com/vishvananda/netlink/route_test.go
new file mode 100644 (file)
index 0000000..2435001
--- /dev/null
@@ -0,0 +1,500 @@
+// +build linux
+
+package netlink
+
+import (
+       "net"
+       "syscall"
+       "testing"
+       "time"
+
+       "github.com/vishvananda/netns"
+)
+
+func TestRouteAddDel(t *testing.T) {
+       tearDown := setUpNetlinkTest(t)
+       defer tearDown()
+
+       // get loopback interface
+       link, err := LinkByName("lo")
+       if err != nil {
+               t.Fatal(err)
+       }
+
+       // bring the interface up
+       if err := LinkSetUp(link); err != nil {
+               t.Fatal(err)
+       }
+
+       // add a gateway route
+       dst := &net.IPNet{
+               IP:   net.IPv4(192, 168, 0, 0),
+               Mask: net.CIDRMask(24, 32),
+       }
+
+       ip := net.IPv4(127, 1, 1, 1)
+       route := Route{LinkIndex: link.Attrs().Index, Dst: dst, Src: ip}
+       if err := RouteAdd(&route); err != nil {
+               t.Fatal(err)
+       }
+       routes, err := RouteList(link, FAMILY_V4)
+       if err != nil {
+               t.Fatal(err)
+       }
+       if len(routes) != 1 {
+               t.Fatal("Route not added properly")
+       }
+
+       dstIP := net.IPv4(192, 168, 0, 42)
+       routeToDstIP, err := RouteGet(dstIP)
+       if err != nil {
+               t.Fatal(err)
+       }
+
+       if len(routeToDstIP) == 0 {
+               t.Fatal("Default route not present")
+       }
+       if err := RouteDel(&route); err != nil {
+               t.Fatal(err)
+       }
+       routes, err = RouteList(link, FAMILY_V4)
+       if err != nil {
+               t.Fatal(err)
+       }
+       if len(routes) != 0 {
+               t.Fatal("Route not removed properly")
+       }
+
+}
+
+func TestRouteReplace(t *testing.T) {
+       tearDown := setUpNetlinkTest(t)
+       defer tearDown()
+
+       // get loopback interface
+       link, err := LinkByName("lo")
+       if err != nil {
+               t.Fatal(err)
+       }
+
+       // bring the interface up
+       if err := LinkSetUp(link); err != nil {
+               t.Fatal(err)
+       }
+
+       // add a gateway route
+       dst := &net.IPNet{
+               IP:   net.IPv4(192, 168, 0, 0),
+               Mask: net.CIDRMask(24, 32),
+       }
+
+       ip := net.IPv4(127, 1, 1, 1)
+       route := Route{LinkIndex: link.Attrs().Index, Dst: dst, Src: ip}
+       if err := RouteAdd(&route); err != nil {
+               t.Fatal(err)
+       }
+       routes, err := RouteList(link, FAMILY_V4)
+       if err != nil {
+               t.Fatal(err)
+       }
+       if len(routes) != 1 {
+               t.Fatal("Route not added properly")
+       }
+
+       ip = net.IPv4(127, 1, 1, 2)
+       route = Route{LinkIndex: link.Attrs().Index, Dst: dst, Src: ip}
+       if err := RouteReplace(&route); err != nil {
+               t.Fatal(err)
+       }
+
+       routes, err = RouteList(link, FAMILY_V4)
+       if err != nil {
+               t.Fatal(err)
+       }
+
+       if len(routes) != 1 || !routes[0].Src.Equal(ip) {
+               t.Fatal("Route not replaced properly")
+       }
+
+       if err := RouteDel(&route); err != nil {
+               t.Fatal(err)
+       }
+       routes, err = RouteList(link, FAMILY_V4)
+       if err != nil {
+               t.Fatal(err)
+       }
+       if len(routes) != 0 {
+               t.Fatal("Route not removed properly")
+       }
+
+}
+
+func TestRouteAddIncomplete(t *testing.T) {
+       tearDown := setUpNetlinkTest(t)
+       defer tearDown()
+
+       // get loopback interface
+       link, err := LinkByName("lo")
+       if err != nil {
+               t.Fatal(err)
+       }
+
+       // bring the interface up
+       if err = LinkSetUp(link); err != nil {
+               t.Fatal(err)
+       }
+
+       route := Route{LinkIndex: link.Attrs().Index}
+       if err := RouteAdd(&route); err == nil {
+               t.Fatal("Adding incomplete route should fail")
+       }
+}
+
+func expectRouteUpdate(ch <-chan RouteUpdate, t uint16, dst net.IP) bool {
+       for {
+               timeout := time.After(time.Minute)
+               select {
+               case update := <-ch:
+                       if update.Type == t && update.Route.Dst.IP.Equal(dst) {
+                               return true
+                       }
+               case <-timeout:
+                       return false
+               }
+       }
+}
+
+func TestRouteSubscribe(t *testing.T) {
+       tearDown := setUpNetlinkTest(t)
+       defer tearDown()
+
+       ch := make(chan RouteUpdate)
+       done := make(chan struct{})
+       defer close(done)
+       if err := RouteSubscribe(ch, done); err != nil {
+               t.Fatal(err)
+       }
+
+       // get loopback interface
+       link, err := LinkByName("lo")
+       if err != nil {
+               t.Fatal(err)
+       }
+
+       // bring the interface up
+       if err = LinkSetUp(link); err != nil {
+               t.Fatal(err)
+       }
+
+       // add a gateway route
+       dst := &net.IPNet{
+               IP:   net.IPv4(192, 168, 0, 0),
+               Mask: net.CIDRMask(24, 32),
+       }
+
+       ip := net.IPv4(127, 1, 1, 1)
+       route := Route{LinkIndex: link.Attrs().Index, Dst: dst, Src: ip}
+       if err := RouteAdd(&route); err != nil {
+               t.Fatal(err)
+       }
+
+       if !expectRouteUpdate(ch, syscall.RTM_NEWROUTE, dst.IP) {
+               t.Fatal("Add update not received as expected")
+       }
+       if err := RouteDel(&route); err != nil {
+               t.Fatal(err)
+       }
+       if !expectRouteUpdate(ch, syscall.RTM_DELROUTE, dst.IP) {
+               t.Fatal("Del update not received as expected")
+       }
+}
+
+func TestRouteSubscribeAt(t *testing.T) {
+       skipUnlessRoot(t)
+
+       // Create an handle on a custom netns
+       newNs, err := netns.New()
+       if err != nil {
+               t.Fatal(err)
+       }
+       defer newNs.Close()
+
+       nh, err := NewHandleAt(newNs)
+       if err != nil {
+               t.Fatal(err)
+       }
+       defer nh.Delete()
+
+       // Subscribe for Route events on the custom netns
+       ch := make(chan RouteUpdate)
+       done := make(chan struct{})
+       defer close(done)
+       if err := RouteSubscribeAt(newNs, ch, done); err != nil {
+               t.Fatal(err)
+       }
+
+       // get loopback interface
+       link, err := nh.LinkByName("lo")
+       if err != nil {
+               t.Fatal(err)
+       }
+
+       // bring the interface up
+       if err = nh.LinkSetUp(link); err != nil {
+               t.Fatal(err)
+       }
+
+       // add a gateway route
+       dst := &net.IPNet{
+               IP:   net.IPv4(192, 169, 0, 0),
+               Mask: net.CIDRMask(24, 32),
+       }
+
+       ip := net.IPv4(127, 100, 1, 1)
+       route := Route{LinkIndex: link.Attrs().Index, Dst: dst, Src: ip}
+       if err := nh.RouteAdd(&route); err != nil {
+               t.Fatal(err)
+       }
+
+       if !expectRouteUpdate(ch, syscall.RTM_NEWROUTE, dst.IP) {
+               t.Fatal("Add update not received as expected")
+       }
+       if err := nh.RouteDel(&route); err != nil {
+               t.Fatal(err)
+       }
+       if !expectRouteUpdate(ch, syscall.RTM_DELROUTE, dst.IP) {
+               t.Fatal("Del update not received as expected")
+       }
+}
+
+func TestRouteExtraFields(t *testing.T) {
+       tearDown := setUpNetlinkTest(t)
+       defer tearDown()
+
+       // get loopback interface
+       link, err := LinkByName("lo")
+       if err != nil {
+               t.Fatal(err)
+       }
+       // bring the interface up
+       if err = LinkSetUp(link); err != nil {
+               t.Fatal(err)
+       }
+
+       // add a gateway route
+       dst := &net.IPNet{
+               IP:   net.IPv4(1, 1, 1, 1),
+               Mask: net.CIDRMask(32, 32),
+       }
+
+       src := net.IPv4(127, 3, 3, 3)
+       route := Route{
+               LinkIndex: link.Attrs().Index,
+               Dst:       dst,
+               Src:       src,
+               Scope:     syscall.RT_SCOPE_LINK,
+               Priority:  13,
+               Table:     syscall.RT_TABLE_MAIN,
+               Type:      syscall.RTN_UNICAST,
+               Tos:       14,
+       }
+       if err := RouteAdd(&route); err != nil {
+               t.Fatal(err)
+       }
+       routes, err := RouteListFiltered(FAMILY_V4, &Route{
+               Dst:   dst,
+               Src:   src,
+               Scope: syscall.RT_SCOPE_LINK,
+               Table: syscall.RT_TABLE_MAIN,
+               Type:  syscall.RTN_UNICAST,
+               Tos:   14,
+       }, RT_FILTER_DST|RT_FILTER_SRC|RT_FILTER_SCOPE|RT_FILTER_TABLE|RT_FILTER_TYPE|RT_FILTER_TOS)
+       if err != nil {
+               t.Fatal(err)
+       }
+       if len(routes) != 1 {
+               t.Fatal("Route not added properly")
+       }
+
+       if routes[0].Scope != syscall.RT_SCOPE_LINK {
+               t.Fatal("Invalid Scope. Route not added properly")
+       }
+       if routes[0].Priority != 13 {
+               t.Fatal("Invalid Priority. Route not added properly")
+       }
+       if routes[0].Table != syscall.RT_TABLE_MAIN {
+               t.Fatal("Invalid Scope. Route not added properly")
+       }
+       if routes[0].Type != syscall.RTN_UNICAST {
+               t.Fatal("Invalid Type. Route not added properly")
+       }
+       if routes[0].Tos != 14 {
+               t.Fatal("Invalid Tos. Route not added properly")
+       }
+}
+
+func TestRouteMultiPath(t *testing.T) {
+       tearDown := setUpNetlinkTest(t)
+       defer tearDown()
+
+       // get loopback interface
+       link, err := LinkByName("lo")
+       if err != nil {
+               t.Fatal(err)
+       }
+       // bring the interface up
+       if err = LinkSetUp(link); err != nil {
+               t.Fatal(err)
+       }
+
+       // add a gateway route
+       dst := &net.IPNet{
+               IP:   net.IPv4(192, 168, 0, 0),
+               Mask: net.CIDRMask(24, 32),
+       }
+
+       idx := link.Attrs().Index
+       route := Route{Dst: dst, MultiPath: []*NexthopInfo{&NexthopInfo{LinkIndex: idx}, &NexthopInfo{LinkIndex: idx}}}
+       if err := RouteAdd(&route); err != nil {
+               t.Fatal(err)
+       }
+       routes, err := RouteList(nil, FAMILY_V4)
+       if err != nil {
+               t.Fatal(err)
+       }
+       if len(routes) != 1 {
+               t.Fatal("MultiPath Route not added properly")
+       }
+       if len(routes[0].MultiPath) != 2 {
+               t.Fatal("MultiPath Route not added properly")
+       }
+}
+
+func TestFilterDefaultRoute(t *testing.T) {
+       tearDown := setUpNetlinkTest(t)
+       defer tearDown()
+
+       // get loopback interface
+       link, err := LinkByName("lo")
+       if err != nil {
+               t.Fatal(err)
+       }
+       // bring the interface up
+       if err = LinkSetUp(link); err != nil {
+               t.Fatal(err)
+       }
+
+       address := &Addr{
+               IPNet: &net.IPNet{
+                       IP:   net.IPv4(127, 0, 0, 2),
+                       Mask: net.CIDRMask(24, 32),
+               },
+       }
+       if err = AddrAdd(link, address); err != nil {
+               t.Fatal(err)
+       }
+
+       // Add default route
+       gw := net.IPv4(127, 0, 0, 2)
+
+       defaultRoute := Route{
+               Dst: nil,
+               Gw:  gw,
+       }
+
+       if err := RouteAdd(&defaultRoute); err != nil {
+               t.Fatal(err)
+       }
+
+       // add an extra route
+       dst := &net.IPNet{
+               IP:   net.IPv4(192, 168, 0, 0),
+               Mask: net.CIDRMask(24, 32),
+       }
+
+       extraRoute := Route{
+               Dst: dst,
+               Gw:  gw,
+       }
+
+       if err := RouteAdd(&extraRoute); err != nil {
+               t.Fatal(err)
+       }
+       var filterTests = []struct {
+               filter   *Route
+               mask     uint64
+               expected net.IP
+       }{
+               {
+                       &Route{Dst: nil},
+                       RT_FILTER_DST,
+                       gw,
+               },
+               {
+                       &Route{Dst: dst},
+                       RT_FILTER_DST,
+                       gw,
+               },
+       }
+
+       for _, f := range filterTests {
+               routes, err := RouteListFiltered(FAMILY_V4, f.filter, f.mask)
+               if err != nil {
+                       t.Fatal(err)
+               }
+               if len(routes) != 1 {
+                       t.Fatal("Route not filtered properly")
+               }
+               if !routes[0].Gw.Equal(gw) {
+                       t.Fatal("Unexpected Gateway")
+               }
+       }
+
+}
+
+func TestMPLSRouteAddDel(t *testing.T) {
+       tearDown := setUpMPLSNetlinkTest(t)
+       defer tearDown()
+
+       // get loopback interface
+       link, err := LinkByName("lo")
+       if err != nil {
+               t.Fatal(err)
+       }
+
+       // bring the interface up
+       if err := LinkSetUp(link); err != nil {
+               t.Fatal(err)
+       }
+
+       mplsDst := 100
+       route := Route{
+               LinkIndex: link.Attrs().Index,
+               MPLSDst:   &mplsDst,
+               NewDst: &MPLSDestination{
+                       Labels: []int{200, 300},
+               },
+       }
+       if err := RouteAdd(&route); err != nil {
+               t.Fatal(err)
+       }
+       routes, err := RouteList(link, FAMILY_MPLS)
+       if err != nil {
+               t.Fatal(err)
+       }
+       if len(routes) != 1 {
+               t.Fatal("Route not added properly")
+       }
+
+       if err := RouteDel(&route); err != nil {
+               t.Fatal(err)
+       }
+       routes, err = RouteList(link, FAMILY_MPLS)
+       if err != nil {
+               t.Fatal(err)
+       }
+       if len(routes) != 0 {
+               t.Fatal("Route not removed properly")
+       }
+
+}
diff --git a/vendor/github.com/vishvananda/netlink/rule_test.go b/vendor/github.com/vishvananda/netlink/rule_test.go
new file mode 100644 (file)
index 0000000..7844877
--- /dev/null
@@ -0,0 +1,70 @@
+// +build linux
+
+package netlink
+
+import (
+       "net"
+       "syscall"
+       "testing"
+)
+
+func TestRuleAddDel(t *testing.T) {
+       skipUnlessRoot(t)
+
+       srcNet := &net.IPNet{IP: net.IPv4(172, 16, 0, 1), Mask: net.CIDRMask(16, 32)}
+       dstNet := &net.IPNet{IP: net.IPv4(172, 16, 1, 1), Mask: net.CIDRMask(24, 32)}
+
+       rulesBegin, err := RuleList(syscall.AF_INET)
+       if err != nil {
+               t.Fatal(err)
+       }
+
+       rule := NewRule()
+       rule.Table = syscall.RT_TABLE_MAIN
+       rule.Src = srcNet
+       rule.Dst = dstNet
+       rule.Priority = 5
+       rule.OifName = "lo"
+       rule.IifName = "lo"
+       if err := RuleAdd(rule); err != nil {
+               t.Fatal(err)
+       }
+
+       rules, err := RuleList(syscall.AF_INET)
+       if err != nil {
+               t.Fatal(err)
+       }
+
+       if len(rules) != len(rulesBegin)+1 {
+               t.Fatal("Rule not added properly")
+       }
+
+       // find this rule
+       var found bool
+       for i := range rules {
+               if rules[i].Table == rule.Table &&
+                       rules[i].Src != nil && rules[i].Src.String() == srcNet.String() &&
+                       rules[i].Dst != nil && rules[i].Dst.String() == dstNet.String() &&
+                       rules[i].OifName == rule.OifName &&
+                       rules[i].Priority == rule.Priority &&
+                       rules[i].IifName == rule.IifName {
+                       found = true
+               }
+       }
+       if !found {
+               t.Fatal("Rule has diffrent options than one added")
+       }
+
+       if err := RuleDel(rule); err != nil {
+               t.Fatal(err)
+       }
+
+       rulesEnd, err := RuleList(syscall.AF_INET)
+       if err != nil {
+               t.Fatal(err)
+       }
+
+       if len(rulesEnd) != len(rulesBegin) {
+               t.Fatal("Rule not removed properly")
+       }
+}
diff --git a/vendor/github.com/vishvananda/netlink/socket_test.go b/vendor/github.com/vishvananda/netlink/socket_test.go
new file mode 100644 (file)
index 0000000..4a24b89
--- /dev/null
@@ -0,0 +1,60 @@
+// +build linux
+
+package netlink
+
+import (
+       "log"
+       "net"
+       "os"
+       "os/user"
+       "strconv"
+       "testing"
+)
+
+func TestSocketGet(t *testing.T) {
+       if os.Getenv("TRAVIS_BUILD_DIR") != "" {
+               t.Skipf("Goroutines + network namespaces == inconsistent results")
+       }
+       addr, err := net.ResolveTCPAddr("tcp", "localhost:0")
+       if err != nil {
+               log.Fatal(err)
+       }
+       l, err := net.ListenTCP("tcp", addr)
+       if err != nil {
+               log.Fatal(err)
+       }
+       defer l.Close()
+
+       conn, err := net.Dial(l.Addr().Network(), l.Addr().String())
+       if err != nil {
+               t.Fatal(err)
+       }
+       defer conn.Close()
+
+       localAddr := conn.LocalAddr().(*net.TCPAddr)
+       remoteAddr := conn.RemoteAddr().(*net.TCPAddr)
+       socket, err := SocketGet(localAddr, remoteAddr)
+       if err != nil {
+               t.Fatal(err)
+       }
+
+       if got, want := socket.ID.Source, localAddr.IP; !got.Equal(want) {
+               t.Fatalf("local ip = %v, want %v", got, want)
+       }
+       if got, want := socket.ID.Destination, remoteAddr.IP; !got.Equal(want) {
+               t.Fatalf("remote ip = %v, want %v", got, want)
+       }
+       if got, want := int(socket.ID.SourcePort), localAddr.Port; got != want {
+               t.Fatalf("local port = %d, want %d", got, want)
+       }
+       if got, want := int(socket.ID.DestinationPort), remoteAddr.Port; got != want {
+               t.Fatalf("remote port = %d, want %d", got, want)
+       }
+       u, err := user.Current()
+       if err != nil {
+               t.Fatal(err)
+       }
+       if got, want := strconv.Itoa(int(socket.UID)), u.Uid; got != want {
+               t.Fatalf("UID = %s, want %s", got, want)
+       }
+}
diff --git a/vendor/github.com/vishvananda/netlink/xfrm_monitor_test.go b/vendor/github.com/vishvananda/netlink/xfrm_monitor_test.go
new file mode 100644 (file)
index 0000000..74d3d81
--- /dev/null
@@ -0,0 +1,39 @@
+// +build linux
+
+package netlink
+
+import (
+       "testing"
+
+       "github.com/vishvananda/netlink/nl"
+)
+
+func TestXfrmMonitorExpire(t *testing.T) {
+       setUpNetlinkTest(t)()
+
+       ch := make(chan XfrmMsg)
+       done := make(chan struct{})
+       defer close(done)
+       errChan := make(chan error)
+       if err := XfrmMonitor(ch, nil, errChan, nl.XFRM_MSG_EXPIRE); err != nil {
+               t.Fatal(err)
+       }
+
+       // Program state with limits
+       state := getBaseState()
+       state.Limits.TimeHard = 2
+       state.Limits.TimeSoft = 1
+       if err := XfrmStateAdd(state); err != nil {
+               t.Fatal(err)
+       }
+
+       msg := (<-ch).(*XfrmMsgExpire)
+       if msg.XfrmState.Spi != state.Spi || msg.Hard {
+               t.Fatal("Received unexpected msg")
+       }
+
+       msg = (<-ch).(*XfrmMsgExpire)
+       if msg.XfrmState.Spi != state.Spi || !msg.Hard {
+               t.Fatal("Received unexpected msg")
+       }
+}
diff --git a/vendor/github.com/vishvananda/netlink/xfrm_policy_test.go b/vendor/github.com/vishvananda/netlink/xfrm_policy_test.go
new file mode 100644 (file)
index 0000000..5b88421
--- /dev/null
@@ -0,0 +1,199 @@
+// +build linux
+
+package netlink
+
+import (
+       "bytes"
+       "net"
+       "testing"
+)
+
+const zeroCIDR = "0.0.0.0/0"
+
+func TestXfrmPolicyAddUpdateDel(t *testing.T) {
+       tearDown := setUpNetlinkTest(t)
+       defer tearDown()
+
+       policy := getPolicy()
+       if err := XfrmPolicyAdd(policy); err != nil {
+               t.Fatal(err)
+       }
+       policies, err := XfrmPolicyList(FAMILY_ALL)
+       if err != nil {
+               t.Fatal(err)
+       }
+
+       if len(policies) != 1 {
+               t.Fatal("Policy not added properly")
+       }
+
+       if !comparePolicies(policy, &policies[0]) {
+               t.Fatalf("unexpected policy returned.\nExpected: %v.\nGot %v", policy, policies[0])
+       }
+
+       // Look for a specific policy
+       sp, err := XfrmPolicyGet(policy)
+       if err != nil {
+               t.Fatal(err)
+       }
+
+       if !comparePolicies(policy, sp) {
+               t.Fatalf("unexpected policy returned")
+       }
+
+       // Modify the policy
+       policy.Priority = 100
+       if err := XfrmPolicyUpdate(policy); err != nil {
+               t.Fatal(err)
+       }
+       sp, err = XfrmPolicyGet(policy)
+       if err != nil {
+               t.Fatal(err)
+       }
+       if sp.Priority != 100 {
+               t.Fatalf("failed to modify the policy")
+       }
+
+       if err = XfrmPolicyDel(policy); err != nil {
+               t.Fatal(err)
+       }
+
+       policies, err = XfrmPolicyList(FAMILY_ALL)
+       if err != nil {
+               t.Fatal(err)
+       }
+       if len(policies) != 0 {
+               t.Fatal("Policy not removed properly")
+       }
+
+       // Src and dst are not mandatory field. Creation should succeed
+       policy.Src = nil
+       policy.Dst = nil
+       if err = XfrmPolicyAdd(policy); err != nil {
+               t.Fatal(err)
+       }
+
+       sp, err = XfrmPolicyGet(policy)
+       if err != nil {
+               t.Fatal(err)
+       }
+
+       if !comparePolicies(policy, sp) {
+               t.Fatalf("unexpected policy returned")
+       }
+
+       if err = XfrmPolicyDel(policy); err != nil {
+               t.Fatal(err)
+       }
+
+       if _, err := XfrmPolicyGet(policy); err == nil {
+               t.Fatalf("Unexpected success")
+       }
+}
+
+func TestXfrmPolicyFlush(t *testing.T) {
+       setUpNetlinkTest(t)()
+
+       p1 := getPolicy()
+       if err := XfrmPolicyAdd(p1); err != nil {
+               t.Fatal(err)
+       }
+
+       p1.Dir = XFRM_DIR_IN
+       s := p1.Src
+       p1.Src = p1.Dst
+       p1.Dst = s
+       if err := XfrmPolicyAdd(p1); err != nil {
+               t.Fatal(err)
+       }
+
+       policies, err := XfrmPolicyList(FAMILY_ALL)
+       if err != nil {
+               t.Fatal(err)
+       }
+       if len(policies) != 2 {
+               t.Fatalf("unexpected number of policies: %d", len(policies))
+       }
+
+       if err := XfrmPolicyFlush(); err != nil {
+               t.Fatal(err)
+       }
+
+       policies, err = XfrmPolicyList(FAMILY_ALL)
+       if err != nil {
+               t.Fatal(err)
+       }
+       if len(policies) != 0 {
+               t.Fatalf("unexpected number of policies: %d", len(policies))
+       }
+
+}
+
+func comparePolicies(a, b *XfrmPolicy) bool {
+       if a == b {
+               return true
+       }
+       if a == nil || b == nil {
+               return false
+       }
+       // Do not check Index which is assigned by kernel
+       return a.Dir == b.Dir && a.Priority == b.Priority &&
+               compareIPNet(a.Src, b.Src) && compareIPNet(a.Dst, b.Dst) &&
+               a.Mark.Value == b.Mark.Value && a.Mark.Mask == b.Mark.Mask &&
+               compareTemplates(a.Tmpls, b.Tmpls)
+}
+
+func compareTemplates(a, b []XfrmPolicyTmpl) bool {
+       if len(a) != len(b) {
+               return false
+       }
+       for i, ta := range a {
+               tb := b[i]
+               if !ta.Dst.Equal(tb.Dst) || !ta.Src.Equal(tb.Src) || ta.Spi != tb.Spi ||
+                       ta.Mode != tb.Mode || ta.Reqid != tb.Reqid || ta.Proto != tb.Proto {
+                       return false
+               }
+       }
+       return true
+}
+
+func compareIPNet(a, b *net.IPNet) bool {
+       if a == b {
+               return true
+       }
+       // For unspecified src/dst parseXfrmPolicy would set the zero address cidr
+       if (a == nil && b.String() == zeroCIDR) || (b == nil && a.String() == zeroCIDR) {
+               return true
+       }
+       if a == nil || b == nil {
+               return false
+       }
+       return a.IP.Equal(b.IP) && bytes.Equal(a.Mask, b.Mask)
+}
+
+func getPolicy() *XfrmPolicy {
+       src, _ := ParseIPNet("127.1.1.1/32")
+       dst, _ := ParseIPNet("127.1.1.2/32")
+       policy := &XfrmPolicy{
+               Src:     src,
+               Dst:     dst,
+               Proto:   17,
+               DstPort: 1234,
+               SrcPort: 5678,
+               Dir:     XFRM_DIR_OUT,
+               Mark: &XfrmMark{
+                       Value: 0xabff22,
+                       Mask:  0xffffffff,
+               },
+               Priority: 10,
+       }
+       tmpl := XfrmPolicyTmpl{
+               Src:   net.ParseIP("127.0.0.1"),
+               Dst:   net.ParseIP("127.0.0.2"),
+               Proto: XFRM_PROTO_ESP,
+               Mode:  XFRM_MODE_TUNNEL,
+               Spi:   0xabcdef99,
+       }
+       policy.Tmpls = append(policy.Tmpls, tmpl)
+       return policy
+}
diff --git a/vendor/github.com/vishvananda/netlink/xfrm_state_test.go b/vendor/github.com/vishvananda/netlink/xfrm_state_test.go
new file mode 100644 (file)
index 0000000..ec5dfb6
--- /dev/null
@@ -0,0 +1,270 @@
+// +build linux
+
+package netlink
+
+import (
+       "bytes"
+       "encoding/hex"
+       "net"
+       "testing"
+)
+
+func TestXfrmStateAddGetDel(t *testing.T) {
+       for _, s := range []*XfrmState{getBaseState(), getAeadState()} {
+               testXfrmStateAddGetDel(t, s)
+       }
+}
+
+func testXfrmStateAddGetDel(t *testing.T, state *XfrmState) {
+       tearDown := setUpNetlinkTest(t)
+       defer tearDown()
+       if err := XfrmStateAdd(state); err != nil {
+               t.Fatal(err)
+       }
+       states, err := XfrmStateList(FAMILY_ALL)
+       if err != nil {
+               t.Fatal(err)
+       }
+
+       if len(states) != 1 {
+               t.Fatal("State not added properly")
+       }
+
+       if !compareStates(state, &states[0]) {
+               t.Fatalf("unexpected states returned")
+       }
+
+       // Get specific state
+       sa, err := XfrmStateGet(state)
+       if err != nil {
+               t.Fatal(err)
+       }
+
+       if !compareStates(state, sa) {
+               t.Fatalf("unexpected state returned")
+       }
+
+       if err = XfrmStateDel(state); err != nil {
+               t.Fatal(err)
+       }
+
+       states, err = XfrmStateList(FAMILY_ALL)
+       if err != nil {
+               t.Fatal(err)
+       }
+       if len(states) != 0 {
+               t.Fatal("State not removed properly")
+       }
+
+       if _, err := XfrmStateGet(state); err == nil {
+               t.Fatalf("Unexpected success")
+       }
+}
+
+func TestXfrmStateAllocSpi(t *testing.T) {
+       setUpNetlinkTest(t)()
+
+       state := getBaseState()
+       state.Spi = 0
+       state.Auth = nil
+       state.Crypt = nil
+       rstate, err := XfrmStateAllocSpi(state)
+       if err != nil {
+               t.Fatal(err)
+       }
+       if rstate.Spi == 0 {
+               t.Fatalf("SPI is not allocated")
+       }
+       rstate.Spi = 0
+       if !compareStates(state, rstate) {
+               t.Fatalf("State not properly allocated")
+       }
+}
+
+func TestXfrmStateFlush(t *testing.T) {
+       setUpNetlinkTest(t)()
+
+       state1 := getBaseState()
+       state2 := getBaseState()
+       state2.Src = net.ParseIP("127.1.0.1")
+       state2.Dst = net.ParseIP("127.1.0.2")
+       state2.Proto = XFRM_PROTO_AH
+       state2.Mode = XFRM_MODE_TUNNEL
+       state2.Spi = 20
+       state2.Mark = nil
+       state2.Crypt = nil
+
+       if err := XfrmStateAdd(state1); err != nil {
+               t.Fatal(err)
+       }
+       if err := XfrmStateAdd(state2); err != nil {
+               t.Fatal(err)
+       }
+
+       // flushing proto for which no state is present should return silently
+       if err := XfrmStateFlush(XFRM_PROTO_COMP); err != nil {
+               t.Fatal(err)
+       }
+
+       if err := XfrmStateFlush(XFRM_PROTO_AH); err != nil {
+               t.Fatal(err)
+       }
+
+       if _, err := XfrmStateGet(state2); err == nil {
+               t.Fatalf("Unexpected success")
+       }
+
+       if err := XfrmStateAdd(state2); err != nil {
+               t.Fatal(err)
+       }
+
+       if err := XfrmStateFlush(0); err != nil {
+               t.Fatal(err)
+       }
+
+       states, err := XfrmStateList(FAMILY_ALL)
+       if err != nil {
+               t.Fatal(err)
+       }
+       if len(states) != 0 {
+               t.Fatal("State not flushed properly")
+       }
+
+}
+
+func TestXfrmStateUpdateLimits(t *testing.T) {
+       setUpNetlinkTest(t)()
+
+       // Program state with limits
+       state := getBaseState()
+       state.Limits.TimeHard = 3600
+       state.Limits.TimeSoft = 60
+       state.Limits.PacketHard = 1000
+       state.Limits.PacketSoft = 50
+       state.Limits.ByteHard = 1000000
+       state.Limits.ByteSoft = 50000
+       state.Limits.TimeUseHard = 3000
+       state.Limits.TimeUseSoft = 1500
+       if err := XfrmStateAdd(state); err != nil {
+               t.Fatal(err)
+       }
+       // Verify limits
+       s, err := XfrmStateGet(state)
+       if err != nil {
+               t.Fatal(err)
+       }
+       if !compareLimits(state, s) {
+               t.Fatalf("Incorrect time hard/soft retrieved: %s", s.Print(true))
+       }
+
+       // Update limits
+       state.Limits.TimeHard = 1800
+       state.Limits.TimeSoft = 30
+       state.Limits.PacketHard = 500
+       state.Limits.PacketSoft = 25
+       state.Limits.ByteHard = 500000
+       state.Limits.ByteSoft = 25000
+       state.Limits.TimeUseHard = 2000
+       state.Limits.TimeUseSoft = 1000
+       if err := XfrmStateUpdate(state); err != nil {
+               t.Fatal(err)
+       }
+
+       // Verify new limits
+       s, err = XfrmStateGet(state)
+       if err != nil {
+               t.Fatal(err)
+       }
+       if s.Limits.TimeHard != 1800 || s.Limits.TimeSoft != 30 {
+               t.Fatalf("Incorrect time hard retrieved: (%d, %d)", s.Limits.TimeHard, s.Limits.TimeSoft)
+       }
+}
+
+func getBaseState() *XfrmState {
+       return &XfrmState{
+               // Force 4 byte notation for the IPv4 addresses
+               Src:   net.ParseIP("127.0.0.1").To4(),
+               Dst:   net.ParseIP("127.0.0.2").To4(),
+               Proto: XFRM_PROTO_ESP,
+               Mode:  XFRM_MODE_TUNNEL,
+               Spi:   1,
+               Auth: &XfrmStateAlgo{
+                       Name: "hmac(sha256)",
+                       Key:  []byte("abcdefghijklmnopqrstuvwzyzABCDEF"),
+               },
+               Crypt: &XfrmStateAlgo{
+                       Name: "cbc(aes)",
+                       Key:  []byte("abcdefghijklmnopqrstuvwzyzABCDEF"),
+               },
+               Mark: &XfrmMark{
+                       Value: 0x12340000,
+                       Mask:  0xffff0000,
+               },
+       }
+}
+
+func getAeadState() *XfrmState {
+       // 128 key bits + 32 salt bits
+       k, _ := hex.DecodeString("d0562776bf0e75830ba3f7f8eb6c09b555aa1177")
+       return &XfrmState{
+               // Leave IPv4 addresses in Ipv4 in IPv6 notation
+               Src:   net.ParseIP("192.168.1.1"),
+               Dst:   net.ParseIP("192.168.2.2"),
+               Proto: XFRM_PROTO_ESP,
+               Mode:  XFRM_MODE_TUNNEL,
+               Spi:   2,
+               Aead: &XfrmStateAlgo{
+                       Name:   "rfc4106(gcm(aes))",
+                       Key:    k,
+                       ICVLen: 64,
+               },
+       }
+}
+
+func compareStates(a, b *XfrmState) bool {
+       if a == b {
+               return true
+       }
+       if a == nil || b == nil {
+               return false
+       }
+       return a.Src.Equal(b.Src) && a.Dst.Equal(b.Dst) &&
+               a.Mode == b.Mode && a.Spi == b.Spi && a.Proto == b.Proto &&
+               compareAlgo(a.Auth, b.Auth) &&
+               compareAlgo(a.Crypt, b.Crypt) &&
+               compareAlgo(a.Aead, b.Aead) &&
+               compareMarks(a.Mark, b.Mark)
+}
+
+func compareLimits(a, b *XfrmState) bool {
+       return a.Limits.TimeHard == b.Limits.TimeHard &&
+               a.Limits.TimeSoft == b.Limits.TimeSoft &&
+               a.Limits.PacketHard == b.Limits.PacketHard &&
+               a.Limits.PacketSoft == b.Limits.PacketSoft &&
+               a.Limits.ByteHard == b.Limits.ByteHard &&
+               a.Limits.ByteSoft == b.Limits.ByteSoft &&
+               a.Limits.TimeUseHard == b.Limits.TimeUseHard &&
+               a.Limits.TimeUseSoft == b.Limits.TimeUseSoft
+}
+
+func compareAlgo(a, b *XfrmStateAlgo) bool {
+       if a == b {
+               return true
+       }
+       if a == nil || b == nil {
+               return false
+       }
+       return a.Name == b.Name && bytes.Equal(a.Key, b.Key) &&
+               (a.TruncateLen == 0 || a.TruncateLen == b.TruncateLen) &&
+               (a.ICVLen == 0 || a.ICVLen == b.ICVLen)
+}
+
+func compareMarks(a, b *XfrmMark) bool {
+       if a == b {
+               return true
+       }
+       if a == nil || b == nil {
+               return false
+       }
+       return a.Value == b.Value && a.Mask == b.Mask
+}