Browse Source

Merge pull request #848 from aboch/nlvnd

Disable DAD for sandbox IPv6 addresses
Madhu Venugopal 9 years ago
parent
commit
3184188851
43 changed files with 4893 additions and 92 deletions
  1. 1 1
      libnetwork/Godeps/Godeps.json
  2. 6 1
      libnetwork/Godeps/_workspace/src/github.com/vishvananda/netlink/.travis.yml
  3. 1 1
      libnetwork/Godeps/_workspace/src/github.com/vishvananda/netlink/Makefile
  4. 3 1
      libnetwork/Godeps/_workspace/src/github.com/vishvananda/netlink/addr.go
  5. 14 0
      libnetwork/Godeps/_workspace/src/github.com/vishvananda/netlink/addr_linux.go
  6. 93 0
      libnetwork/Godeps/_workspace/src/github.com/vishvananda/netlink/addr_test.go
  7. 39 15
      libnetwork/Godeps/_workspace/src/github.com/vishvananda/netlink/class_linux.go
  8. 406 0
      libnetwork/Godeps/_workspace/src/github.com/vishvananda/netlink/class_test.go
  9. 248 0
      libnetwork/Godeps/_workspace/src/github.com/vishvananda/netlink/filter_test.go
  10. 312 0
      libnetwork/Godeps/_workspace/src/github.com/vishvananda/netlink/link.go
  11. 330 0
      libnetwork/Godeps/_workspace/src/github.com/vishvananda/netlink/link_linux.go
  12. 781 0
      libnetwork/Godeps/_workspace/src/github.com/vishvananda/netlink/link_test.go
  13. 3 2
      libnetwork/Godeps/_workspace/src/github.com/vishvananda/netlink/neigh_linux.go
  14. 104 0
      libnetwork/Godeps/_workspace/src/github.com/vishvananda/netlink/neigh_test.go
  15. 5 2
      libnetwork/Godeps/_workspace/src/github.com/vishvananda/netlink/netlink.go
  16. 34 0
      libnetwork/Godeps/_workspace/src/github.com/vishvananda/netlink/netlink_test.go
  17. 39 0
      libnetwork/Godeps/_workspace/src/github.com/vishvananda/netlink/nl/addr_linux_test.go
  18. 80 0
      libnetwork/Godeps/_workspace/src/github.com/vishvananda/netlink/nl/link_linux.go
  19. 9 3
      libnetwork/Godeps/_workspace/src/github.com/vishvananda/netlink/nl/nl_linux.go
  20. 60 0
      libnetwork/Godeps/_workspace/src/github.com/vishvananda/netlink/nl/nl_linux_test.go
  21. 43 0
      libnetwork/Godeps/_workspace/src/github.com/vishvananda/netlink/nl/route_linux_test.go
  22. 37 0
      libnetwork/Godeps/_workspace/src/github.com/vishvananda/netlink/nl/syscall.go
  23. 130 11
      libnetwork/Godeps/_workspace/src/github.com/vishvananda/netlink/nl/tc_linux.go
  24. 173 0
      libnetwork/Godeps/_workspace/src/github.com/vishvananda/netlink/nl/tc_linux_test.go
  25. 161 0
      libnetwork/Godeps/_workspace/src/github.com/vishvananda/netlink/nl/xfrm_linux_test.go
  26. 109 0
      libnetwork/Godeps/_workspace/src/github.com/vishvananda/netlink/nl/xfrm_policy_linux_test.go
  27. 207 0
      libnetwork/Godeps/_workspace/src/github.com/vishvananda/netlink/nl/xfrm_state_linux_test.go
  28. 98 0
      libnetwork/Godeps/_workspace/src/github.com/vishvananda/netlink/protinfo_test.go
  29. 123 0
      libnetwork/Godeps/_workspace/src/github.com/vishvananda/netlink/qdisc.go
  30. 114 15
      libnetwork/Godeps/_workspace/src/github.com/vishvananda/netlink/qdisc_linux.go
  31. 345 0
      libnetwork/Godeps/_workspace/src/github.com/vishvananda/netlink/qdisc_test.go
  32. 13 10
      libnetwork/Godeps/_workspace/src/github.com/vishvananda/netlink/route.go
  33. 104 28
      libnetwork/Godeps/_workspace/src/github.com/vishvananda/netlink/route_linux.go
  34. 210 0
      libnetwork/Godeps/_workspace/src/github.com/vishvananda/netlink/route_test.go
  35. 43 0
      libnetwork/Godeps/_workspace/src/github.com/vishvananda/netlink/rule.go
  36. 198 0
      libnetwork/Godeps/_workspace/src/github.com/vishvananda/netlink/rule_linux.go
  37. 66 0
      libnetwork/Godeps/_workspace/src/github.com/vishvananda/netlink/rule_test.go
  38. 49 0
      libnetwork/Godeps/_workspace/src/github.com/vishvananda/netlink/xfrm_policy_test.go
  39. 50 0
      libnetwork/Godeps/_workspace/src/github.com/vishvananda/netlink/xfrm_state_test.go
  40. 1 1
      libnetwork/Makefile
  41. 2 1
      libnetwork/osl/interface_linux.go
  42. 43 0
      libnetwork/osl/sandbox_linux_test.go
  43. 6 0
      libnetwork/testutils/context.go

+ 1 - 1
libnetwork/Godeps/Godeps.json

@@ -243,7 +243,7 @@
 		},
 		{
 			"ImportPath": "github.com/vishvananda/netlink",
-			"Rev": "8e810149a2e531fed9b837c0c7d8a8922d2bedf7"
+			"Rev": "bfd70f556483c008636b920dda142fdaa0d59ef9"
 		},
 		{
 			"ImportPath": "github.com/vishvananda/netns",

+ 6 - 1
libnetwork/Godeps/_workspace/src/github.com/vishvananda/netlink/.travis.yml

@@ -1,3 +1,8 @@
 language: go
+before_script:
+  # make sure we keep path in tact when we sudo
+  - 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
 install:
-      - go get github.com/vishvananda/netns
+  - go get github.com/vishvananda/netns

+ 1 - 1
libnetwork/Godeps/_workspace/src/github.com/vishvananda/netlink/Makefile

@@ -11,7 +11,7 @@ goroot = $(addprefix ../../../,$(1))
 unroot = $(subst ../../../,,$(1))
 fmt = $(addprefix fmt-,$(1))
 
-all: fmt
+all: fmt test
 
 $(call goroot,$(DEPS)):
 	go get $(call unroot,$@)

+ 3 - 1
libnetwork/Godeps/_workspace/src/github.com/vishvananda/netlink/addr.go

@@ -11,11 +11,13 @@ import (
 type Addr struct {
 	*net.IPNet
 	Label string
+	Flags int
+	Scope int
 }
 
 // String returns $ip/$netmask $label
 func (a Addr) String() string {
-	return fmt.Sprintf("%s %s", a.IPNet, a.Label)
+	return strings.TrimSpace(fmt.Sprintf("%s %s", a.IPNet, a.Label))
 }
 
 // ParseAddr parses the string representation of an address in the

+ 14 - 0
libnetwork/Godeps/_workspace/src/github.com/vishvananda/netlink/addr_linux.go

@@ -9,6 +9,9 @@ import (
 	"github.com/vishvananda/netlink/nl"
 )
 
+// IFA_FLAGS is a u32 attribute.
+const IFA_FLAGS = 0x8
+
 // AddrAdd will add an IP address to a link device.
 // Equivalent to: `ip addr add $addr dev $link`
 func AddrAdd(link Link, addr *Addr) error {
@@ -35,6 +38,7 @@ func addrHandle(link Link, addr *Addr, req *nl.NetlinkRequest) error {
 
 	msg := nl.NewIfAddrmsg(family)
 	msg.Index = uint32(base.Index)
+	msg.Scope = uint8(addr.Scope)
 	prefixlen, _ := addr.Mask.Size()
 	msg.Prefixlen = uint8(prefixlen)
 	req.AddData(msg)
@@ -52,6 +56,13 @@ func addrHandle(link Link, addr *Addr, req *nl.NetlinkRequest) error {
 	addressData := nl.NewRtAttr(syscall.IFA_ADDRESS, addrData)
 	req.AddData(addressData)
 
+	if addr.Flags != 0 {
+		b := make([]byte, 4)
+		native.PutUint32(b, uint32(addr.Flags))
+		flagsData := nl.NewRtAttr(IFA_FLAGS, b)
+		req.AddData(flagsData)
+	}
+
 	if addr.Label != "" {
 		labelData := nl.NewRtAttr(syscall.IFA_LABEL, nl.ZeroTerminated(addr.Label))
 		req.AddData(labelData)
@@ -111,6 +122,8 @@ func AddrList(link Link, family int) ([]Addr, error) {
 				}
 			case syscall.IFA_LABEL:
 				addr.Label = string(attr.Value[:len(attr.Value)-1])
+			case IFA_FLAGS:
+				addr.Flags = int(native.Uint32(attr.Value[0:4]))
 			}
 		}
 
@@ -120,6 +133,7 @@ func AddrList(link Link, family int) ([]Addr, error) {
 		} else {
 			addr.IPNet = dst
 		}
+		addr.Scope = int(msg.Scope)
 
 		res = append(res, addr)
 	}

+ 93 - 0
libnetwork/Godeps/_workspace/src/github.com/vishvananda/netlink/addr_test.go

@@ -0,0 +1,93 @@
+package netlink
+
+import (
+	"net"
+	"os"
+	"syscall"
+	"testing"
+)
+
+func TestAddr(t *testing.T) {
+	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{net.IPv4(127, 0, 0, 2), 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},
+		},
+	}
+
+	tearDown := setUpNetlinkTest(t)
+	defer tearDown()
+
+	link, err := LinkByName("lo")
+	if err != nil {
+		t.Fatal(err)
+	}
+
+	for _, tt := range addrTests {
+		if err = AddrAdd(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 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")
+		}
+	}
+}

+ 39 - 15
libnetwork/Godeps/_workspace/src/github.com/vishvananda/netlink/class_linux.go

@@ -9,24 +9,38 @@ import (
 // ClassDel will delete a class from the system.
 // Equivalent to: `tc class del $class`
 func ClassDel(class Class) error {
-	req := nl.NewNetlinkRequest(syscall.RTM_DELTCLASS, syscall.NLM_F_ACK)
-	base := class.Attrs()
-	msg := &nl.TcMsg{
-		Family:  nl.FAMILY_ALL,
-		Ifindex: int32(base.LinkIndex),
-		Handle:  base.Handle,
-		Parent:  base.Parent,
-	}
-	req.AddData(msg)
+	return classModify(syscall.RTM_DELTCLASS, 0, class)
+}
 
-	_, err := req.Execute(syscall.NETLINK_ROUTE, 0)
-	return err
+// ClassChange will change a class in place
+// Equivalent to: `tc class change $class`
+// The parent and handle MUST NOT be changed.
+
+func ClassChange(class Class) error {
+	return classModify(syscall.RTM_NEWTCLASS, 0, class)
+}
+
+// ClassReplace will replace a class to the system.
+// quivalent to: `tc class replace $class`
+// The handle MAY be changed.
+// If a class already exist with this parent/handle pair, the class is changed.
+// If a class does not already exist with this parent/handle, a new class is created.
+func ClassReplace(class Class) error {
+	return classModify(syscall.RTM_NEWTCLASS, syscall.NLM_F_CREATE, class)
 }
 
 // ClassAdd will add a class to the system.
 // Equivalent to: `tc class add $class`
 func ClassAdd(class Class) error {
-	req := nl.NewNetlinkRequest(syscall.RTM_NEWTCLASS, syscall.NLM_F_CREATE|syscall.NLM_F_EXCL|syscall.NLM_F_ACK)
+	return classModify(
+		syscall.RTM_NEWTCLASS,
+		syscall.NLM_F_CREATE|syscall.NLM_F_EXCL,
+		class,
+	)
+}
+
+func classModify(cmd, flags int, class Class) error {
+	req := nl.NewNetlinkRequest(cmd, flags|syscall.NLM_F_ACK)
 	base := class.Attrs()
 	msg := &nl.TcMsg{
 		Family:  nl.FAMILY_ALL,
@@ -35,6 +49,17 @@ func ClassAdd(class Class) error {
 		Parent:  base.Parent,
 	}
 	req.AddData(msg)
+
+	if cmd != syscall.RTM_DELTCLASS {
+		if err := classPayload(req, class); err != nil {
+			return err
+		}
+	}
+	_, err := req.Execute(syscall.NETLINK_ROUTE, 0)
+	return err
+}
+
+func classPayload(req *nl.NetlinkRequest, class Class) error {
 	req.AddData(nl.NewRtAttr(nl.TCA_KIND, nl.ZeroTerminated(class.Type())))
 
 	options := nl.NewRtAttr(nl.TCA_OPTIONS, nil)
@@ -51,13 +76,12 @@ func ClassAdd(class Class) error {
 		nl.NewRtAttrChild(options, nl.TCA_HTB_PARMS, opt.Serialize())
 	}
 	req.AddData(options)
-	_, err := req.Execute(syscall.NETLINK_ROUTE, 0)
-	return err
+	return nil
 }
 
 // ClassList gets a list of classes in the system.
 // Equivalent to: `tc class show`.
-// Generally retunrs nothing if link and parent are not specified.
+// Generally returns nothing if link and parent are not specified.
 func ClassList(link Link, parent uint32) ([]Class, error) {
 	req := nl.NewNetlinkRequest(syscall.RTM_GETTCLASS, syscall.NLM_F_DUMP)
 	msg := &nl.TcMsg{

+ 406 - 0
libnetwork/Godeps/_workspace/src/github.com/vishvananda/netlink/class_test.go

@@ -0,0 +1,406 @@
+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.
+	old_handle := 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 = old_handle
+	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")
+	}
+}

+ 248 - 0
libnetwork/Godeps/_workspace/src/github.com/vishvananda/netlink/filter_test.go

@@ -0,0 +1,248 @@
+package netlink
+
+import (
+	"syscall"
+	"testing"
+
+	"github.com/vishvananda/netlink/nl"
+)
+
+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")
+	}
+	filter := &U32{
+		FilterAttrs: FilterAttrs{
+			LinkIndex: link.Attrs().Index,
+			Parent:    MakeHandle(0xffff, 0),
+			Priority:  1,
+			Protocol:  syscall.ETH_P_IP,
+		},
+		RedirIndex: 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")
+	}
+	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 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:   nl.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")
+	}
+}

+ 312 - 0
libnetwork/Godeps/_workspace/src/github.com/vishvananda/netlink/link.go

@@ -1,6 +1,7 @@
 package netlink
 
 import (
+	"fmt"
 	"net"
 	"syscall"
 )
@@ -29,6 +30,7 @@ type LinkAttrs struct {
 	ParentIndex  int         // index of the parent link device
 	MasterIndex  int         // must be the index of a bridge
 	Namespace    interface{} // nil | NsPid | NsFd
+	Alias        string
 }
 
 // NewLinkAttrs returns LinkAttrs structure filled with default values
@@ -240,6 +242,316 @@ func (ipvlan *IPVlan) Type() string {
 	return "ipvlan"
 }
 
+// BondMode type
+type BondMode int
+
+func (b BondMode) String() string {
+	s, ok := bondModeToString[b]
+	if !ok {
+		return fmt.Sprintf("BondMode(%d)", b)
+	}
+	return s
+}
+
+// StringToBondMode returns bond mode, or uknonw is the s is invalid.
+func StringToBondMode(s string) BondMode {
+	mode, ok := StringToBondModeMap[s]
+	if !ok {
+		return BOND_MODE_UNKNOWN
+	}
+	return mode
+}
+
+// Possible BondMode
+const (
+	BOND_MODE_802_3AD BondMode = iota
+	BOND_MODE_BALANCE_RR
+	BOND_MODE_ACTIVE_BACKUP
+	BOND_MODE_BALANCE_XOR
+	BOND_MODE_BROADCAST
+	BOND_MODE_BALANCE_TLB
+	BOND_MODE_BALANCE_ALB
+	BOND_MODE_UNKNOWN
+)
+
+var bondModeToString = map[BondMode]string{
+	BOND_MODE_802_3AD:       "802.3ad",
+	BOND_MODE_BALANCE_RR:    "balance-rr",
+	BOND_MODE_ACTIVE_BACKUP: "active-backup",
+	BOND_MODE_BALANCE_XOR:   "balance-xor",
+	BOND_MODE_BROADCAST:     "broadcast",
+	BOND_MODE_BALANCE_TLB:   "balance-tlb",
+	BOND_MODE_BALANCE_ALB:   "balance-alb",
+}
+var StringToBondModeMap = map[string]BondMode{
+	"802.3ad":       BOND_MODE_802_3AD,
+	"balance-rr":    BOND_MODE_BALANCE_RR,
+	"active-backup": BOND_MODE_ACTIVE_BACKUP,
+	"balance-xor":   BOND_MODE_BALANCE_XOR,
+	"broadcast":     BOND_MODE_BROADCAST,
+	"balance-tlb":   BOND_MODE_BALANCE_TLB,
+	"balance-alb":   BOND_MODE_BALANCE_ALB,
+}
+
+// BondArpValidate type
+type BondArpValidate int
+
+// Possible BondArpValidate value
+const (
+	BOND_ARP_VALIDATE_NONE BondArpValidate = iota
+	BOND_ARP_VALIDATE_ACTIVE
+	BOND_ARP_VALIDATE_BACKUP
+	BOND_ARP_VALIDATE_ALL
+)
+
+// BondPrimaryReselect type
+type BondPrimaryReselect int
+
+// Possible BondPrimaryReselect value
+const (
+	BOND_PRIMARY_RESELECT_ALWAYS BondPrimaryReselect = iota
+	BOND_PRIMARY_RESELECT_BETTER
+	BOND_PRIMARY_RESELECT_FAILURE
+)
+
+// BondArpAllTargets type
+type BondArpAllTargets int
+
+// Possible BondArpAllTargets value
+const (
+	BOND_ARP_ALL_TARGETS_ANY BondArpAllTargets = iota
+	BOND_ARP_ALL_TARGETS_ALL
+)
+
+// BondFailOverMac type
+type BondFailOverMac int
+
+// Possible BondFailOverMac value
+const (
+	BOND_FAIL_OVER_MAC_NONE BondFailOverMac = iota
+	BOND_FAIL_OVER_MAC_ACTIVE
+	BOND_FAIL_OVER_MAC_FOLLOW
+)
+
+// BondXmitHashPolicy type
+type BondXmitHashPolicy int
+
+func (b BondXmitHashPolicy) String() string {
+	s, ok := bondXmitHashPolicyToString[b]
+	if !ok {
+		return fmt.Sprintf("XmitHashPolicy(%d)", b)
+	}
+	return s
+}
+
+// StringToBondXmitHashPolicy returns bond lacp arte, or uknonw is the s is invalid.
+func StringToBondXmitHashPolicy(s string) BondXmitHashPolicy {
+	lacp, ok := StringToBondXmitHashPolicyMap[s]
+	if !ok {
+		return BOND_XMIT_HASH_POLICY_UNKNOWN
+	}
+	return lacp
+}
+
+// Possible BondXmitHashPolicy value
+const (
+	BOND_XMIT_HASH_POLICY_LAYER2 BondXmitHashPolicy = iota
+	BOND_XMIT_HASH_POLICY_LAYER3_4
+	BOND_XMIT_HASH_POLICY_LAYER2_3
+	BOND_XMIT_HASH_POLICY_ENCAP2_3
+	BOND_XMIT_HASH_POLICY_ENCAP3_4
+	BOND_XMIT_HASH_POLICY_UNKNOWN
+)
+
+var bondXmitHashPolicyToString = map[BondXmitHashPolicy]string{
+	BOND_XMIT_HASH_POLICY_LAYER2:   "layer2",
+	BOND_XMIT_HASH_POLICY_LAYER3_4: "layer3+4",
+	BOND_XMIT_HASH_POLICY_LAYER2_3: "layer2+3",
+	BOND_XMIT_HASH_POLICY_ENCAP2_3: "encap2+3",
+	BOND_XMIT_HASH_POLICY_ENCAP3_4: "encap3+4",
+}
+var StringToBondXmitHashPolicyMap = map[string]BondXmitHashPolicy{
+	"layer2":   BOND_XMIT_HASH_POLICY_LAYER2,
+	"layer3+4": BOND_XMIT_HASH_POLICY_LAYER3_4,
+	"layer2+3": BOND_XMIT_HASH_POLICY_LAYER2_3,
+	"encap2+3": BOND_XMIT_HASH_POLICY_ENCAP2_3,
+	"encap3+4": BOND_XMIT_HASH_POLICY_ENCAP3_4,
+}
+
+// BondLacpRate type
+type BondLacpRate int
+
+func (b BondLacpRate) String() string {
+	s, ok := bondLacpRateToString[b]
+	if !ok {
+		return fmt.Sprintf("LacpRate(%d)", b)
+	}
+	return s
+}
+
+// StringToBondLacpRate returns bond lacp arte, or uknonw is the s is invalid.
+func StringToBondLacpRate(s string) BondLacpRate {
+	lacp, ok := StringToBondLacpRateMap[s]
+	if !ok {
+		return BOND_LACP_RATE_UNKNOWN
+	}
+	return lacp
+}
+
+// Possible BondLacpRate value
+const (
+	BOND_LACP_RATE_SLOW BondLacpRate = iota
+	BOND_LACP_RATE_FAST
+	BOND_LACP_RATE_UNKNOWN
+)
+
+var bondLacpRateToString = map[BondLacpRate]string{
+	BOND_LACP_RATE_SLOW: "slow",
+	BOND_LACP_RATE_FAST: "fast",
+}
+var StringToBondLacpRateMap = map[string]BondLacpRate{
+	"slow": BOND_LACP_RATE_SLOW,
+	"fast": BOND_LACP_RATE_FAST,
+}
+
+// BondAdSelect type
+type BondAdSelect int
+
+// Possible BondAdSelect value
+const (
+	BOND_AD_SELECT_STABLE BondAdSelect = iota
+	BOND_AD_SELECT_BANDWIDTH
+	BOND_AD_SELECT_COUNT
+)
+
+// BondAdInfo
+type BondAdInfo struct {
+	AggregatorId int
+	NumPorts     int
+	ActorKey     int
+	PartnerKey   int
+	PartnerMac   net.HardwareAddr
+}
+
+// Bond representation
+type Bond struct {
+	LinkAttrs
+	Mode            BondMode
+	ActiveSlave     int
+	Miimon          int
+	UpDelay         int
+	DownDelay       int
+	UseCarrier      int
+	ArpInterval     int
+	ArpIpTargets    []net.IP
+	ArpValidate     BondArpValidate
+	ArpAllTargets   BondArpAllTargets
+	Primary         int
+	PrimaryReselect BondPrimaryReselect
+	FailOverMac     BondFailOverMac
+	XmitHashPolicy  BondXmitHashPolicy
+	ResendIgmp      int
+	NumPeerNotif    int
+	AllSlavesActive int
+	MinLinks        int
+	LpInterval      int
+	PackersPerSlave int
+	LacpRate        BondLacpRate
+	AdSelect        BondAdSelect
+	// looking at iproute tool AdInfo can only be retrived. It can't be set.
+	AdInfo *BondAdInfo
+}
+
+func NewLinkBond(atr LinkAttrs) *Bond {
+	return &Bond{
+		LinkAttrs:       atr,
+		Mode:            -1,
+		ActiveSlave:     -1,
+		Miimon:          -1,
+		UpDelay:         -1,
+		DownDelay:       -1,
+		UseCarrier:      -1,
+		ArpInterval:     -1,
+		ArpIpTargets:    nil,
+		ArpValidate:     -1,
+		ArpAllTargets:   -1,
+		Primary:         -1,
+		PrimaryReselect: -1,
+		FailOverMac:     -1,
+		XmitHashPolicy:  -1,
+		ResendIgmp:      -1,
+		NumPeerNotif:    -1,
+		AllSlavesActive: -1,
+		MinLinks:        -1,
+		LpInterval:      -1,
+		PackersPerSlave: -1,
+		LacpRate:        -1,
+		AdSelect:        -1,
+	}
+}
+
+// Flag mask for bond options. Bond.Flagmask must be set to on for option to work.
+const (
+	BOND_MODE_MASK uint64 = 1 << (1 + iota)
+	BOND_ACTIVE_SLAVE_MASK
+	BOND_MIIMON_MASK
+	BOND_UPDELAY_MASK
+	BOND_DOWNDELAY_MASK
+	BOND_USE_CARRIER_MASK
+	BOND_ARP_INTERVAL_MASK
+	BOND_ARP_VALIDATE_MASK
+	BOND_ARP_ALL_TARGETS_MASK
+	BOND_PRIMARY_MASK
+	BOND_PRIMARY_RESELECT_MASK
+	BOND_FAIL_OVER_MAC_MASK
+	BOND_XMIT_HASH_POLICY_MASK
+	BOND_RESEND_IGMP_MASK
+	BOND_NUM_PEER_NOTIF_MASK
+	BOND_ALL_SLAVES_ACTIVE_MASK
+	BOND_MIN_LINKS_MASK
+	BOND_LP_INTERVAL_MASK
+	BOND_PACKETS_PER_SLAVE_MASK
+	BOND_LACP_RATE_MASK
+	BOND_AD_SELECT_MASK
+)
+
+// Attrs implementation.
+func (bond *Bond) Attrs() *LinkAttrs {
+	return &bond.LinkAttrs
+}
+
+// Type implementation fro Vxlan.
+func (bond *Bond) Type() string {
+	return "bond"
+}
+
+// GreTap devices must specify LocalIP and RemoteIP on create
+type Gretap struct {
+	LinkAttrs
+	IKey       uint32
+	OKey       uint32
+	EncapSport uint16
+	EncapDport uint16
+	Local      net.IP
+	Remote     net.IP
+	IFlags     uint16
+	OFlags     uint16
+	PMtuDisc   uint8
+	Ttl        uint8
+	Tos        uint8
+	EncapType  uint16
+	EncapFlags uint16
+	Link       uint32
+}
+
+func (gretap *Gretap) Attrs() *LinkAttrs {
+	return &gretap.LinkAttrs
+}
+
+func (gretap *Gretap) Type() string {
+	return "gretap"
+}
+
 // iproute2 supported devices;
 // vlan | veth | vcan | dummy | ifb | macvlan | macvtap |
 // bridge | bond | ipoib | ip6tnl | ipip | sit | vxlan |

+ 330 - 0
libnetwork/Godeps/_workspace/src/github.com/vishvananda/netlink/link_linux.go

@@ -106,6 +106,24 @@ func LinkSetName(link Link, name string) error {
 	return err
 }
 
+// LinkSetAlias sets the alias of the link device.
+// Equivalent to: `ip link set dev $link alias $name`
+func LinkSetAlias(link Link, name string) error {
+	base := link.Attrs()
+	ensureIndex(base)
+	req := nl.NewNetlinkRequest(syscall.RTM_SETLINK, syscall.NLM_F_ACK)
+
+	msg := nl.NewIfInfomsg(syscall.AF_UNSPEC)
+	msg.Index = int32(base.Index)
+	req.AddData(msg)
+
+	data := nl.NewRtAttr(syscall.IFLA_IFALIAS, []byte(name))
+	req.AddData(data)
+
+	_, err := req.Execute(syscall.NETLINK_ROUTE, 0)
+	return err
+}
+
 // LinkSetHardwareAddr sets the hardware address of the link device.
 // Equivalent to: `ip link set $link address $hwaddr`
 func LinkSetHardwareAddr(link Link, hwaddr net.HardwareAddr) error {
@@ -133,9 +151,18 @@ func LinkSetMaster(link Link, master *Bridge) error {
 		ensureIndex(masterBase)
 		index = masterBase.Index
 	}
+	if index <= 0 {
+		return fmt.Errorf("Device does not exist")
+	}
 	return LinkSetMasterByIndex(link, index)
 }
 
+// LinkSetNoMaster removes the master of the link device.
+// Equivalent to: `ip link set $link nomaster`
+func LinkSetNoMaster(link Link) error {
+	return LinkSetMasterByIndex(link, 0)
+}
+
 // LinkSetMasterByIndex sets the master of the link device.
 // Equivalent to: `ip link set $link master $master`
 func LinkSetMasterByIndex(link Link, masterIndex int) error {
@@ -275,6 +302,87 @@ func addVxlanAttrs(vxlan *Vxlan, linkInfo *nl.RtAttr) {
 	}
 }
 
+func addBondAttrs(bond *Bond, linkInfo *nl.RtAttr) {
+	data := nl.NewRtAttrChild(linkInfo, nl.IFLA_INFO_DATA, nil)
+	if bond.Mode >= 0 {
+		nl.NewRtAttrChild(data, nl.IFLA_BOND_MODE, nl.Uint8Attr(uint8(bond.Mode)))
+	}
+	if bond.ActiveSlave >= 0 {
+		nl.NewRtAttrChild(data, nl.IFLA_BOND_ACTIVE_SLAVE, nl.Uint32Attr(uint32(bond.ActiveSlave)))
+	}
+	if bond.Miimon >= 0 {
+		nl.NewRtAttrChild(data, nl.IFLA_BOND_MIIMON, nl.Uint32Attr(uint32(bond.Miimon)))
+	}
+	if bond.UpDelay >= 0 {
+		nl.NewRtAttrChild(data, nl.IFLA_BOND_UPDELAY, nl.Uint32Attr(uint32(bond.UpDelay)))
+	}
+	if bond.DownDelay >= 0 {
+		nl.NewRtAttrChild(data, nl.IFLA_BOND_DOWNDELAY, nl.Uint32Attr(uint32(bond.DownDelay)))
+	}
+	if bond.UseCarrier >= 0 {
+		nl.NewRtAttrChild(data, nl.IFLA_BOND_USE_CARRIER, nl.Uint8Attr(uint8(bond.UseCarrier)))
+	}
+	if bond.ArpInterval >= 0 {
+		nl.NewRtAttrChild(data, nl.IFLA_BOND_ARP_INTERVAL, nl.Uint32Attr(uint32(bond.ArpInterval)))
+	}
+	if bond.ArpIpTargets != nil {
+		msg := nl.NewRtAttrChild(data, nl.IFLA_BOND_ARP_IP_TARGET, nil)
+		for i := range bond.ArpIpTargets {
+			ip := bond.ArpIpTargets[i].To4()
+			if ip != nil {
+				nl.NewRtAttrChild(msg, i, []byte(ip))
+				continue
+			}
+			ip = bond.ArpIpTargets[i].To16()
+			if ip != nil {
+				nl.NewRtAttrChild(msg, i, []byte(ip))
+			}
+		}
+	}
+	if bond.ArpValidate >= 0 {
+		nl.NewRtAttrChild(data, nl.IFLA_BOND_ARP_VALIDATE, nl.Uint32Attr(uint32(bond.ArpValidate)))
+	}
+	if bond.ArpAllTargets >= 0 {
+		nl.NewRtAttrChild(data, nl.IFLA_BOND_ARP_ALL_TARGETS, nl.Uint32Attr(uint32(bond.ArpAllTargets)))
+	}
+	if bond.Primary >= 0 {
+		nl.NewRtAttrChild(data, nl.IFLA_BOND_PRIMARY, nl.Uint32Attr(uint32(bond.Primary)))
+	}
+	if bond.PrimaryReselect >= 0 {
+		nl.NewRtAttrChild(data, nl.IFLA_BOND_PRIMARY_RESELECT, nl.Uint8Attr(uint8(bond.PrimaryReselect)))
+	}
+	if bond.FailOverMac >= 0 {
+		nl.NewRtAttrChild(data, nl.IFLA_BOND_FAIL_OVER_MAC, nl.Uint8Attr(uint8(bond.FailOverMac)))
+	}
+	if bond.XmitHashPolicy >= 0 {
+		nl.NewRtAttrChild(data, nl.IFLA_BOND_XMIT_HASH_POLICY, nl.Uint8Attr(uint8(bond.XmitHashPolicy)))
+	}
+	if bond.ResendIgmp >= 0 {
+		nl.NewRtAttrChild(data, nl.IFLA_BOND_RESEND_IGMP, nl.Uint32Attr(uint32(bond.ResendIgmp)))
+	}
+	if bond.NumPeerNotif >= 0 {
+		nl.NewRtAttrChild(data, nl.IFLA_BOND_NUM_PEER_NOTIF, nl.Uint8Attr(uint8(bond.NumPeerNotif)))
+	}
+	if bond.AllSlavesActive >= 0 {
+		nl.NewRtAttrChild(data, nl.IFLA_BOND_ALL_SLAVES_ACTIVE, nl.Uint8Attr(uint8(bond.AllSlavesActive)))
+	}
+	if bond.MinLinks >= 0 {
+		nl.NewRtAttrChild(data, nl.IFLA_BOND_MIN_LINKS, nl.Uint32Attr(uint32(bond.MinLinks)))
+	}
+	if bond.LpInterval >= 0 {
+		nl.NewRtAttrChild(data, nl.IFLA_BOND_LP_INTERVAL, nl.Uint32Attr(uint32(bond.LpInterval)))
+	}
+	if bond.PackersPerSlave >= 0 {
+		nl.NewRtAttrChild(data, nl.IFLA_BOND_PACKETS_PER_SLAVE, nl.Uint32Attr(uint32(bond.PackersPerSlave)))
+	}
+	if bond.LacpRate >= 0 {
+		nl.NewRtAttrChild(data, nl.IFLA_BOND_AD_LACP_RATE, nl.Uint8Attr(uint8(bond.LacpRate)))
+	}
+	if bond.AdSelect >= 0 {
+		nl.NewRtAttrChild(data, nl.IFLA_BOND_AD_SELECT, nl.Uint8Attr(uint8(bond.AdSelect)))
+	}
+}
+
 // LinkAdd adds a new link device. The type and features of the device
 // are taken fromt the parameters in the link object.
 // Equivalent to: `ip link add $link`
@@ -328,6 +436,27 @@ func LinkAdd(link Link) error {
 	req := nl.NewNetlinkRequest(syscall.RTM_NEWLINK, syscall.NLM_F_CREATE|syscall.NLM_F_EXCL|syscall.NLM_F_ACK)
 
 	msg := nl.NewIfInfomsg(syscall.AF_UNSPEC)
+	// TODO: make it shorter
+	if base.Flags&net.FlagUp != 0 {
+		msg.Change = syscall.IFF_UP
+		msg.Flags = syscall.IFF_UP
+	}
+	if base.Flags&net.FlagBroadcast != 0 {
+		msg.Change |= syscall.IFF_BROADCAST
+		msg.Flags |= syscall.IFF_BROADCAST
+	}
+	if base.Flags&net.FlagLoopback != 0 {
+		msg.Change |= syscall.IFF_LOOPBACK
+		msg.Flags |= syscall.IFF_LOOPBACK
+	}
+	if base.Flags&net.FlagPointToPoint != 0 {
+		msg.Change |= syscall.IFF_POINTOPOINT
+		msg.Flags |= syscall.IFF_POINTOPOINT
+	}
+	if base.Flags&net.FlagMulticast != 0 {
+		msg.Change |= syscall.IFF_MULTICAST
+		msg.Flags |= syscall.IFF_MULTICAST
+	}
 	req.AddData(msg)
 
 	if base.ParentIndex != 0 {
@@ -388,6 +517,8 @@ func LinkAdd(link Link) error {
 
 	} else if vxlan, ok := link.(*Vxlan); ok {
 		addVxlanAttrs(vxlan, linkInfo)
+	} else if bond, ok := link.(*Bond); ok {
+		addBondAttrs(bond, linkInfo)
 	} else if ipv, ok := link.(*IPVlan); ok {
 		data := nl.NewRtAttrChild(linkInfo, nl.IFLA_INFO_DATA, nil)
 		nl.NewRtAttrChild(data, nl.IFLA_IPVLAN_MODE, nl.Uint16Attr(uint16(ipv.Mode)))
@@ -396,6 +527,8 @@ func LinkAdd(link Link) error {
 			data := nl.NewRtAttrChild(linkInfo, nl.IFLA_INFO_DATA, nil)
 			nl.NewRtAttrChild(data, nl.IFLA_MACVLAN_MODE, nl.Uint32Attr(macvlanModes[macv.Mode]))
 		}
+	} else if gretap, ok := link.(*Gretap); ok {
+		addGretapAttrs(gretap, linkInfo)
 	}
 
 	req.AddData(linkInfo)
@@ -447,6 +580,20 @@ func linkByNameDump(name string) (Link, error) {
 	return nil, fmt.Errorf("Link %s not found", name)
 }
 
+func linkByAliasDump(alias string) (Link, error) {
+	links, err := LinkList()
+	if err != nil {
+		return nil, err
+	}
+
+	for _, link := range links {
+		if link.Attrs().Alias == alias {
+			return link, nil
+		}
+	}
+	return nil, fmt.Errorf("Link alias %s not found", alias)
+}
+
 // LinkByName finds a link by name and returns a pointer to the object.
 func LinkByName(name string) (Link, error) {
 	if lookupByDump {
@@ -472,6 +619,32 @@ func LinkByName(name string) (Link, error) {
 	return link, err
 }
 
+// LinkByAlias finds a link by its alias and returns a pointer to the object.
+// If there are multiple links with the alias it returns the first one
+func LinkByAlias(alias string) (Link, error) {
+	if lookupByDump {
+		return linkByAliasDump(alias)
+	}
+
+	req := nl.NewNetlinkRequest(syscall.RTM_GETLINK, syscall.NLM_F_ACK)
+
+	msg := nl.NewIfInfomsg(syscall.AF_UNSPEC)
+	req.AddData(msg)
+
+	nameData := nl.NewRtAttr(syscall.IFLA_IFALIAS, nl.ZeroTerminated(alias))
+	req.AddData(nameData)
+
+	link, err := execGetLink(req)
+	if err == syscall.EINVAL {
+		// older kernels don't support looking up via IFLA_IFALIAS
+		// so fall back to dumping all links
+		lookupByDump = true
+		return linkByAliasDump(alias)
+	}
+
+	return link, err
+}
+
 // LinkByIndex finds a link by index and returns a pointer to the object.
 func LinkByIndex(index int) (Link, error) {
 	req := nl.NewNetlinkRequest(syscall.RTM_GETLINK, syscall.NLM_F_ACK)
@@ -543,12 +716,16 @@ func linkDeserialize(m []byte) (Link, error) {
 						link = &Veth{}
 					case "vxlan":
 						link = &Vxlan{}
+					case "bond":
+						link = &Bond{}
 					case "ipvlan":
 						link = &IPVlan{}
 					case "macvlan":
 						link = &Macvlan{}
 					case "macvtap":
 						link = &Macvtap{}
+					case "gretap":
+						link = &Gretap{}
 					default:
 						link = &GenericLink{LinkType: linkType}
 					}
@@ -562,12 +739,16 @@ func linkDeserialize(m []byte) (Link, error) {
 						parseVlanData(link, data)
 					case "vxlan":
 						parseVxlanData(link, data)
+					case "bond":
+						parseBondData(link, data)
 					case "ipvlan":
 						parseIPVlanData(link, data)
 					case "macvlan":
 						parseMacvlanData(link, data)
 					case "macvtap":
 						parseMacvtapData(link, data)
+					case "gretap":
+						parseGretapData(link, data)
 					}
 				}
 			}
@@ -591,6 +772,8 @@ func linkDeserialize(m []byte) (Link, error) {
 			base.MasterIndex = int(native.Uint32(attr.Value[0:4]))
 		case syscall.IFLA_TXQLEN:
 			base.TxQLen = int(native.Uint32(attr.Value[0:4]))
+		case syscall.IFLA_IFALIAS:
+			base.Alias = string(attr.Value[:len(attr.Value)-1])
 		}
 	}
 	// Links that don't have IFLA_INFO_KIND are hardware devices
@@ -772,6 +955,60 @@ func parseVxlanData(link Link, data []syscall.NetlinkRouteAttr) {
 	}
 }
 
+func parseBondData(link Link, data []syscall.NetlinkRouteAttr) {
+	bond := NewLinkBond(NewLinkAttrs())
+	for i := range data {
+		switch data[i].Attr.Type {
+		case nl.IFLA_BOND_MODE:
+			bond.Mode = BondMode(data[i].Value[0])
+		case nl.IFLA_BOND_ACTIVE_SLAVE:
+			bond.ActiveSlave = int(native.Uint32(data[i].Value[0:4]))
+		case nl.IFLA_BOND_MIIMON:
+			bond.Miimon = int(native.Uint32(data[i].Value[0:4]))
+		case nl.IFLA_BOND_UPDELAY:
+			bond.UpDelay = int(native.Uint32(data[i].Value[0:4]))
+		case nl.IFLA_BOND_DOWNDELAY:
+			bond.DownDelay = int(native.Uint32(data[i].Value[0:4]))
+		case nl.IFLA_BOND_USE_CARRIER:
+			bond.UseCarrier = int(data[i].Value[0])
+		case nl.IFLA_BOND_ARP_INTERVAL:
+			bond.ArpInterval = int(native.Uint32(data[i].Value[0:4]))
+		case nl.IFLA_BOND_ARP_IP_TARGET:
+			// TODO: implement
+		case nl.IFLA_BOND_ARP_VALIDATE:
+			bond.ArpValidate = BondArpValidate(native.Uint32(data[i].Value[0:4]))
+		case nl.IFLA_BOND_ARP_ALL_TARGETS:
+			bond.ArpAllTargets = BondArpAllTargets(native.Uint32(data[i].Value[0:4]))
+		case nl.IFLA_BOND_PRIMARY:
+			bond.Primary = int(native.Uint32(data[i].Value[0:4]))
+		case nl.IFLA_BOND_PRIMARY_RESELECT:
+			bond.PrimaryReselect = BondPrimaryReselect(data[i].Value[0])
+		case nl.IFLA_BOND_FAIL_OVER_MAC:
+			bond.FailOverMac = BondFailOverMac(data[i].Value[0])
+		case nl.IFLA_BOND_XMIT_HASH_POLICY:
+			bond.XmitHashPolicy = BondXmitHashPolicy(data[i].Value[0])
+		case nl.IFLA_BOND_RESEND_IGMP:
+			bond.ResendIgmp = int(native.Uint32(data[i].Value[0:4]))
+		case nl.IFLA_BOND_NUM_PEER_NOTIF:
+			bond.NumPeerNotif = int(data[i].Value[0])
+		case nl.IFLA_BOND_ALL_SLAVES_ACTIVE:
+			bond.AllSlavesActive = int(data[i].Value[0])
+		case nl.IFLA_BOND_MIN_LINKS:
+			bond.MinLinks = int(native.Uint32(data[i].Value[0:4]))
+		case nl.IFLA_BOND_LP_INTERVAL:
+			bond.LpInterval = int(native.Uint32(data[i].Value[0:4]))
+		case nl.IFLA_BOND_PACKETS_PER_SLAVE:
+			bond.PackersPerSlave = int(native.Uint32(data[i].Value[0:4]))
+		case nl.IFLA_BOND_AD_LACP_RATE:
+			bond.LacpRate = BondLacpRate(data[i].Value[0])
+		case nl.IFLA_BOND_AD_SELECT:
+			bond.AdSelect = BondAdSelect(data[i].Value[0])
+		case nl.IFLA_BOND_AD_INFO:
+			// TODO: implement
+		}
+	}
+}
+
 func parseIPVlanData(link Link, data []syscall.NetlinkRouteAttr) {
 	ipv := link.(*IPVlan)
 	for _, datum := range data {
@@ -828,3 +1065,96 @@ func linkFlags(rawFlags uint32) net.Flags {
 	}
 	return f
 }
+
+func htonl(val uint32) []byte {
+	bytes := make([]byte, 4)
+	binary.BigEndian.PutUint32(bytes, val)
+	return bytes
+}
+
+func htons(val uint16) []byte {
+	bytes := make([]byte, 2)
+	binary.BigEndian.PutUint16(bytes, val)
+	return bytes
+}
+
+func ntohl(buf []byte) uint32 {
+	return binary.BigEndian.Uint32(buf)
+}
+
+func ntohs(buf []byte) uint16 {
+	return binary.BigEndian.Uint16(buf)
+}
+
+func addGretapAttrs(gretap *Gretap, linkInfo *nl.RtAttr) {
+	data := nl.NewRtAttrChild(linkInfo, nl.IFLA_INFO_DATA, nil)
+
+	ip := gretap.Local.To4()
+	if ip != nil {
+		nl.NewRtAttrChild(data, nl.IFLA_GRE_LOCAL, []byte(ip))
+	}
+	ip = gretap.Remote.To4()
+	if ip != nil {
+		nl.NewRtAttrChild(data, nl.IFLA_GRE_REMOTE, []byte(ip))
+	}
+
+	if gretap.IKey != 0 {
+		nl.NewRtAttrChild(data, nl.IFLA_GRE_IKEY, htonl(gretap.IKey))
+		gretap.IFlags |= uint16(nl.GRE_KEY)
+	}
+
+	if gretap.OKey != 0 {
+		nl.NewRtAttrChild(data, nl.IFLA_GRE_OKEY, htonl(gretap.OKey))
+		gretap.OFlags |= uint16(nl.GRE_KEY)
+	}
+
+	nl.NewRtAttrChild(data, nl.IFLA_GRE_IFLAGS, htons(gretap.IFlags))
+	nl.NewRtAttrChild(data, nl.IFLA_GRE_OFLAGS, htons(gretap.OFlags))
+
+	if gretap.Link != 0 {
+		nl.NewRtAttrChild(data, nl.IFLA_GRE_LINK, nl.Uint32Attr(gretap.Link))
+	}
+
+	nl.NewRtAttrChild(data, nl.IFLA_GRE_PMTUDISC, nl.Uint8Attr(gretap.PMtuDisc))
+	nl.NewRtAttrChild(data, nl.IFLA_GRE_TTL, nl.Uint8Attr(gretap.Ttl))
+	nl.NewRtAttrChild(data, nl.IFLA_GRE_TOS, nl.Uint8Attr(gretap.Tos))
+	nl.NewRtAttrChild(data, nl.IFLA_GRE_ENCAP_TYPE, nl.Uint16Attr(gretap.EncapType))
+	nl.NewRtAttrChild(data, nl.IFLA_GRE_ENCAP_FLAGS, nl.Uint16Attr(gretap.EncapFlags))
+	nl.NewRtAttrChild(data, nl.IFLA_GRE_ENCAP_SPORT, htons(gretap.EncapSport))
+	nl.NewRtAttrChild(data, nl.IFLA_GRE_ENCAP_DPORT, htons(gretap.EncapDport))
+}
+
+func parseGretapData(link Link, data []syscall.NetlinkRouteAttr) {
+	gre := link.(*Gretap)
+	for _, datum := range data {
+		switch datum.Attr.Type {
+		case nl.IFLA_GRE_OKEY:
+			gre.IKey = ntohl(datum.Value[0:4])
+		case nl.IFLA_GRE_IKEY:
+			gre.OKey = ntohl(datum.Value[0:4])
+		case nl.IFLA_GRE_LOCAL:
+			gre.Local = net.IP(datum.Value[0:4])
+		case nl.IFLA_GRE_REMOTE:
+			gre.Remote = net.IP(datum.Value[0:4])
+		case nl.IFLA_GRE_ENCAP_SPORT:
+			gre.EncapSport = ntohs(datum.Value[0:2])
+		case nl.IFLA_GRE_ENCAP_DPORT:
+			gre.EncapDport = ntohs(datum.Value[0:2])
+		case nl.IFLA_GRE_IFLAGS:
+			gre.IFlags = ntohs(datum.Value[0:2])
+		case nl.IFLA_GRE_OFLAGS:
+			gre.OFlags = ntohs(datum.Value[0:2])
+
+		case nl.IFLA_GRE_TTL:
+			gre.Ttl = uint8(datum.Value[0])
+		case nl.IFLA_GRE_TOS:
+			gre.Tos = uint8(datum.Value[0])
+		case nl.IFLA_GRE_PMTUDISC:
+			gre.PMtuDisc = uint8(datum.Value[0])
+		case nl.IFLA_GRE_ENCAP_TYPE:
+			gre.EncapType = native.Uint16(datum.Value[0:2])
+		case nl.IFLA_GRE_ENCAP_FLAGS:
+			gre.EncapFlags = native.Uint16(datum.Value[0:2])
+		}
+	}
+}

+ 781 - 0
libnetwork/Godeps/_workspace/src/github.com/vishvananda/netlink/link_test.go

@@ -0,0 +1,781 @@
+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)
+	}
+	num := len(links)
+
+	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.Fatal("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 err = LinkDel(link); err != nil {
+		t.Fatal(err)
+	}
+
+	links, err = LinkList()
+	if err != nil {
+		t.Fatal(err)
+	}
+
+	if len(links) != num {
+		t.Fatal("Link not removed properly")
+		return
+	}
+}
+
+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,
+	})
+
+	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,
+		},
+	})
+
+	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()
+
+	testLinkAddDel(t, NewLinkBond(LinkAttrs{Name: "foo"}))
+}
+
+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 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 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")
+	}
+}

+ 3 - 2
libnetwork/Godeps/_workspace/src/github.com/vishvananda/netlink/neigh_linux.go

@@ -70,10 +70,10 @@ func NeighAdd(neigh *Neigh) error {
 	return neighAdd(neigh, syscall.NLM_F_CREATE|syscall.NLM_F_EXCL)
 }
 
-// NeighAdd will add or replace an IP to MAC mapping to the ARP table
+// NeighSet will add or replace an IP to MAC mapping to the ARP table
 // Equivalent to: `ip neigh replace....`
 func NeighSet(neigh *Neigh) error {
-	return neighAdd(neigh, syscall.NLM_F_CREATE)
+	return neighAdd(neigh, syscall.NLM_F_CREATE|syscall.NLM_F_REPLACE)
 }
 
 // NeighAppend will append an entry to FDB
@@ -133,6 +133,7 @@ func NeighList(linkIndex, family int) ([]Neigh, error) {
 	req := nl.NewNetlinkRequest(syscall.RTM_GETNEIGH, syscall.NLM_F_DUMP)
 	msg := Ndmsg{
 		Family: uint8(family),
+		Index:  uint32(linkIndex),
 	}
 	req.AddData(&msg)
 

+ 104 - 0
libnetwork/Godeps/_workspace/src/github.com/vishvananda/netlink/neigh_test.go

@@ -0,0 +1,104 @@
+package netlink
+
+import (
+	"net"
+	"testing"
+)
+
+type arpEntry struct {
+	ip  net.IP
+	mac net.HardwareAddr
+}
+
+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 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)
+	}
+}

+ 5 - 2
libnetwork/Godeps/_workspace/src/github.com/vishvananda/netlink/netlink.go

@@ -33,7 +33,10 @@ func ParseIPNet(s string) (*net.IPNet, error) {
 	return &net.IPNet{IP: ip, Mask: ipNet.Mask}, nil
 }
 
-// NewIPNet generates an IPNet from an ip address using a netmask of 32.
+// NewIPNet generates an IPNet from an ip address using a netmask of 32 or 128.
 func NewIPNet(ip net.IP) *net.IPNet {
-	return &net.IPNet{IP: ip, Mask: net.CIDRMask(32, 32)}
+	if ip.To4() != nil {
+		return &net.IPNet{IP: ip, Mask: net.CIDRMask(32, 32)}
+	}
+	return &net.IPNet{IP: ip, Mask: net.CIDRMask(128, 128)}
 }

+ 34 - 0
libnetwork/Godeps/_workspace/src/github.com/vishvananda/netlink/netlink_test.go

@@ -0,0 +1,34 @@
+package netlink
+
+import (
+	"log"
+	"os"
+	"runtime"
+	"testing"
+
+	"github.com/vishvananda/netns"
+)
+
+type tearDownNetlinkTest func()
+
+func setUpNetlinkTest(t *testing.T) tearDownNetlinkTest {
+	if os.Getuid() != 0 {
+		msg := "Skipped test because it requires root privileges."
+		log.Printf(msg)
+		t.Skip(msg)
+	}
+
+	// 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()
+	}
+}

+ 39 - 0
libnetwork/Godeps/_workspace/src/github.com/vishvananda/netlink/nl/addr_linux_test.go

@@ -0,0 +1,39 @@
+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)
+}

+ 80 - 0
libnetwork/Godeps/_workspace/src/github.com/vishvananda/netlink/nl/link_linux.go

@@ -102,3 +102,83 @@ const (
 	MACVLAN_MODE_PASSTHRU = 8
 	MACVLAN_MODE_SOURCE   = 16
 )
+
+const (
+	IFLA_BOND_UNSPEC = iota
+	IFLA_BOND_MODE
+	IFLA_BOND_ACTIVE_SLAVE
+	IFLA_BOND_MIIMON
+	IFLA_BOND_UPDELAY
+	IFLA_BOND_DOWNDELAY
+	IFLA_BOND_USE_CARRIER
+	IFLA_BOND_ARP_INTERVAL
+	IFLA_BOND_ARP_IP_TARGET
+	IFLA_BOND_ARP_VALIDATE
+	IFLA_BOND_ARP_ALL_TARGETS
+	IFLA_BOND_PRIMARY
+	IFLA_BOND_PRIMARY_RESELECT
+	IFLA_BOND_FAIL_OVER_MAC
+	IFLA_BOND_XMIT_HASH_POLICY
+	IFLA_BOND_RESEND_IGMP
+	IFLA_BOND_NUM_PEER_NOTIF
+	IFLA_BOND_ALL_SLAVES_ACTIVE
+	IFLA_BOND_MIN_LINKS
+	IFLA_BOND_LP_INTERVAL
+	IFLA_BOND_PACKETS_PER_SLAVE
+	IFLA_BOND_AD_LACP_RATE
+	IFLA_BOND_AD_SELECT
+	IFLA_BOND_AD_INFO
+)
+
+const (
+	IFLA_BOND_AD_INFO_UNSPEC = iota
+	IFLA_BOND_AD_INFO_AGGREGATOR
+	IFLA_BOND_AD_INFO_NUM_PORTS
+	IFLA_BOND_AD_INFO_ACTOR_KEY
+	IFLA_BOND_AD_INFO_PARTNER_KEY
+	IFLA_BOND_AD_INFO_PARTNER_MAC
+)
+
+const (
+	IFLA_BOND_SLAVE_UNSPEC = iota
+	IFLA_BOND_SLAVE_STATE
+	IFLA_BOND_SLAVE_MII_STATUS
+	IFLA_BOND_SLAVE_LINK_FAILURE_COUNT
+	IFLA_BOND_SLAVE_PERM_HWADDR
+	IFLA_BOND_SLAVE_QUEUE_ID
+	IFLA_BOND_SLAVE_AD_AGGREGATOR_ID
+)
+
+const (
+	IFLA_GRE_UNSPEC = iota
+	IFLA_GRE_LINK
+	IFLA_GRE_IFLAGS
+	IFLA_GRE_OFLAGS
+	IFLA_GRE_IKEY
+	IFLA_GRE_OKEY
+	IFLA_GRE_LOCAL
+	IFLA_GRE_REMOTE
+	IFLA_GRE_TTL
+	IFLA_GRE_TOS
+	IFLA_GRE_PMTUDISC
+	IFLA_GRE_ENCAP_LIMIT
+	IFLA_GRE_FLOWINFO
+	IFLA_GRE_FLAGS
+	IFLA_GRE_ENCAP_TYPE
+	IFLA_GRE_ENCAP_FLAGS
+	IFLA_GRE_ENCAP_SPORT
+	IFLA_GRE_ENCAP_DPORT
+	IFLA_GRE_COLLECT_METADATA
+	IFLA_GRE_MAX = IFLA_GRE_COLLECT_METADATA
+)
+
+const (
+	GRE_CSUM    = 0x8000
+	GRE_ROUTING = 0x4000
+	GRE_KEY     = 0x2000
+	GRE_SEQ     = 0x1000
+	GRE_STRICT  = 0x0800
+	GRE_REC     = 0x0700
+	GRE_FLAGS   = 0x00F8
+	GRE_VERSION = 0x0007
+)

+ 9 - 3
libnetwork/Godeps/_workspace/src/github.com/vishvananda/netlink/nl/nl_linux.go

@@ -149,10 +149,12 @@ func (a *RtAttr) Serialize() []byte {
 	length := a.Len()
 	buf := make([]byte, rtaAlignOf(length))
 
+	next := 4
 	if a.Data != nil {
-		copy(buf[4:], a.Data)
-	} else {
-		next := 4
+		copy(buf[next:], a.Data)
+		next += rtaAlignOf(len(a.Data))
+	}
+	if len(a.children) > 0 {
 		for _, child := range a.children {
 			childBuf := child.Serialize()
 			copy(buf[next:], childBuf)
@@ -323,6 +325,10 @@ func (s *NetlinkSocket) Close() {
 	syscall.Close(s.fd)
 }
 
+func (s *NetlinkSocket) GetFd() int {
+	return s.fd
+}
+
 func (s *NetlinkSocket) Send(request *NetlinkRequest) error {
 	if err := syscall.Sendto(s.fd, request.Serialize(), 0, &s.lsa); err != nil {
 		return err

+ 60 - 0
libnetwork/Godeps/_workspace/src/github.com/vishvananda/netlink/nl/nl_linux_test.go

@@ -0,0 +1,60 @@
+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
+	b[1] = msg.X__ifi_pad
+	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)
+	safemsg := deserializeIfInfomsgSafe(orig)
+	msg := DeserializeIfInfomsg(orig)
+	testDeserializeSerialize(t, orig, safemsg, msg)
+}

+ 43 - 0
libnetwork/Godeps/_workspace/src/github.com/vishvananda/netlink/nl/route_linux_test.go

@@ -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)
+}

+ 37 - 0
libnetwork/Godeps/_workspace/src/github.com/vishvananda/netlink/nl/syscall.go

@@ -0,0 +1,37 @@
+package nl
+
+// syscall package lack of rule atributes type.
+// Thus there are defined below
+const (
+	FRA_UNSPEC  = iota
+	FRA_DST     /* destination address */
+	FRA_SRC     /* source address */
+	FRA_IIFNAME /* interface name */
+	FRA_GOTO    /* target to jump to (FR_ACT_GOTO) */
+	FRA_UNUSED2
+	FRA_PRIORITY /* priority/preference */
+	FRA_UNUSED3
+	FRA_UNUSED4
+	FRA_UNUSED5
+	FRA_FWMARK /* mark */
+	FRA_FLOW   /* flow/class id */
+	FRA_TUN_ID
+	FRA_SUPPRESS_IFGROUP
+	FRA_SUPPRESS_PREFIXLEN
+	FRA_TABLE  /* Extended table id */
+	FRA_FWMASK /* mask for netfilter mark */
+	FRA_OIFNAME
+)
+
+// ip rule netlink request types
+const (
+	FR_ACT_UNSPEC = iota
+	FR_ACT_TO_TBL /* Pass to fixed table */
+	FR_ACT_GOTO   /* Jump to another rule */
+	FR_ACT_NOP    /* No operation */
+	FR_ACT_RES3
+	FR_ACT_RES4
+	FR_ACT_BLACKHOLE   /* Drop without notification */
+	FR_ACT_UNREACHABLE /* Drop with ENETUNREACH */
+	FR_ACT_PROHIBIT    /* Drop with EACCES */
+)

+ 130 - 11
libnetwork/Godeps/_workspace/src/github.com/vishvananda/netlink/nl/tc_linux.go

@@ -56,17 +56,21 @@ const (
 )
 
 const (
-	SizeofTcMsg       = 0x14
-	SizeofTcActionMsg = 0x04
-	SizeofTcPrioMap   = 0x14
-	SizeofTcRateSpec  = 0x0c
-	SizeofTcTbfQopt   = 2*SizeofTcRateSpec + 0x0c
-	SizeofTcHtbCopt   = 2*SizeofTcRateSpec + 0x14
-	SizeofTcHtbGlob   = 0x14
-	SizeofTcU32Key    = 0x10
-	SizeofTcU32Sel    = 0x10 // without keys
-	SizeofTcMirred    = 0x1c
-	SizeofTcPolice    = 2*SizeofTcRateSpec + 0x20
+	SizeofTcMsg          = 0x14
+	SizeofTcActionMsg    = 0x04
+	SizeofTcPrioMap      = 0x14
+	SizeofTcRateSpec     = 0x0c
+	SizeofTcNetemQopt    = 0x18
+	SizeofTcNetemCorr    = 0x0c
+	SizeofTcNetemReorder = 0x08
+	SizeofTcNetemCorrupt = 0x08
+	SizeofTcTbfQopt      = 2*SizeofTcRateSpec + 0x0c
+	SizeofTcHtbCopt      = 2*SizeofTcRateSpec + 0x14
+	SizeofTcHtbGlob      = 0x14
+	SizeofTcU32Key       = 0x10
+	SizeofTcU32Sel       = 0x10 // without keys
+	SizeofTcMirred       = 0x1c
+	SizeofTcPolice       = 2*SizeofTcRateSpec + 0x20
 )
 
 // struct tcmsg {
@@ -191,6 +195,121 @@ func (x *TcRateSpec) Serialize() []byte {
 	return (*(*[SizeofTcRateSpec]byte)(unsafe.Pointer(x)))[:]
 }
 
+/**
+* NETEM
+ */
+
+const (
+	TCA_NETEM_UNSPEC = iota
+	TCA_NETEM_CORR
+	TCA_NETEM_DELAY_DIST
+	TCA_NETEM_REORDER
+	TCA_NETEM_CORRUPT
+	TCA_NETEM_LOSS
+	TCA_NETEM_RATE
+	TCA_NETEM_ECN
+	TCA_NETEM_RATE64
+	TCA_NETEM_MAX = TCA_NETEM_RATE64
+)
+
+// struct tc_netem_qopt {
+//	__u32	latency;	/* added delay (us) */
+//	__u32   limit;		/* fifo limit (packets) */
+//	__u32	loss;		/* random packet loss (0=none ~0=100%) */
+//	__u32	gap;		/* re-ordering gap (0 for none) */
+//	__u32   duplicate;	/* random packet dup  (0=none ~0=100%) */
+// 	__u32	jitter;		/* random jitter in latency (us) */
+// };
+
+type TcNetemQopt struct {
+	Latency   uint32
+	Limit     uint32
+	Loss      uint32
+	Gap       uint32
+	Duplicate uint32
+	Jitter    uint32
+}
+
+func (msg *TcNetemQopt) Len() int {
+	return SizeofTcNetemQopt
+}
+
+func DeserializeTcNetemQopt(b []byte) *TcNetemQopt {
+	return (*TcNetemQopt)(unsafe.Pointer(&b[0:SizeofTcNetemQopt][0]))
+}
+
+func (x *TcNetemQopt) Serialize() []byte {
+	return (*(*[SizeofTcNetemQopt]byte)(unsafe.Pointer(x)))[:]
+}
+
+// struct tc_netem_corr {
+//  __u32   delay_corr; /* delay correlation */
+//  __u32   loss_corr;  /* packet loss correlation */
+//  __u32   dup_corr;   /* duplicate correlation  */
+// };
+
+type TcNetemCorr struct {
+	DelayCorr uint32
+	LossCorr  uint32
+	DupCorr   uint32
+}
+
+func (msg *TcNetemCorr) Len() int {
+	return SizeofTcNetemCorr
+}
+
+func DeserializeTcNetemCorr(b []byte) *TcNetemCorr {
+	return (*TcNetemCorr)(unsafe.Pointer(&b[0:SizeofTcNetemCorr][0]))
+}
+
+func (x *TcNetemCorr) Serialize() []byte {
+	return (*(*[SizeofTcNetemCorr]byte)(unsafe.Pointer(x)))[:]
+}
+
+// struct tc_netem_reorder {
+//  __u32   probability;
+//  __u32   correlation;
+// };
+
+type TcNetemReorder struct {
+	Probability uint32
+	Correlation uint32
+}
+
+func (msg *TcNetemReorder) Len() int {
+	return SizeofTcNetemReorder
+}
+
+func DeserializeTcNetemReorder(b []byte) *TcNetemReorder {
+	return (*TcNetemReorder)(unsafe.Pointer(&b[0:SizeofTcNetemReorder][0]))
+}
+
+func (x *TcNetemReorder) Serialize() []byte {
+	return (*(*[SizeofTcNetemReorder]byte)(unsafe.Pointer(x)))[:]
+}
+
+// struct tc_netem_corrupt {
+//  __u32   probability;
+//  __u32   correlation;
+// };
+
+type TcNetemCorrupt struct {
+	Probability uint32
+	Correlation uint32
+}
+
+func (msg *TcNetemCorrupt) Len() int {
+	return SizeofTcNetemCorrupt
+}
+
+func DeserializeTcNetemCorrupt(b []byte) *TcNetemCorrupt {
+	return (*TcNetemCorrupt)(unsafe.Pointer(&b[0:SizeofTcNetemCorrupt][0]))
+}
+
+func (x *TcNetemCorrupt) Serialize() []byte {
+	return (*(*[SizeofTcNetemCorrupt]byte)(unsafe.Pointer(x)))[:]
+}
+
 // struct tc_tbf_qopt {
 //   struct tc_ratespec rate;
 //   struct tc_ratespec peakrate;

+ 173 - 0
libnetwork/Godeps/_workspace/src/github.com/vishvananda/netlink/nl/tc_linux_test.go

@@ -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)
+}

+ 161 - 0
libnetwork/Godeps/_workspace/src/github.com/vishvananda/netlink/nl/xfrm_linux_test.go

@@ -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)
+}

+ 109 - 0
libnetwork/Godeps/_workspace/src/github.com/vishvananda/netlink/nl/xfrm_policy_linux_test.go

@@ -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)
+}

+ 207 - 0
libnetwork/Godeps/_workspace/src/github.com/vishvananda/netlink/nl/xfrm_state_linux_test.go

@@ -0,0 +1,207 @@
+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 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) {
+	// use a 32 byte key len
+	var orig = make([]byte, SizeofXfrmAlgo+32)
+	rand.Read(orig)
+	// set the key len to 256 bits
+	orig[64] = 0
+	orig[65] = 1
+	orig[66] = 0
+	orig[67] = 0
+	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) {
+	// use a 32 byte key len
+	var orig = make([]byte, SizeofXfrmAlgoAuth+32)
+	rand.Read(orig)
+	// set the key len to 256 bits
+	orig[64] = 0
+	orig[65] = 1
+	orig[66] = 0
+	orig[67] = 0
+	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)
+}

+ 98 - 0
libnetwork/Godeps/_workspace/src/github.com/vishvananda/netlink/protinfo_test.go

@@ -0,0 +1,98 @@
+package netlink
+
+import "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"}}
+
+	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)
+	}
+
+	oldpi1, err := LinkGetProtinfo(iface1)
+	if err != nil {
+		t.Fatal(err)
+	}
+	oldpi2, err := LinkGetProtinfo(iface2)
+	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.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.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)
+	}
+}

+ 123 - 0
libnetwork/Godeps/_workspace/src/github.com/vishvananda/netlink/qdisc.go

@@ -2,6 +2,7 @@ package netlink
 
 import (
 	"fmt"
+	"math"
 )
 
 const (
@@ -52,6 +53,14 @@ func HandleStr(handle uint32) string {
 	}
 }
 
+func Percentage2u32(percentage float32) uint32 {
+	// FIXME this is most likely not the best way to convert from % to uint32
+	if percentage == 100 {
+		return math.MaxUint32
+	}
+	return uint32(math.MaxUint32 * (percentage / 100))
+}
+
 // PfifoFast is the default qdisc created by the kernel if one has not
 // been defined for the interface
 type PfifoFast struct {
@@ -120,6 +129,120 @@ func (qdisc *Htb) Type() string {
 	return "htb"
 }
 
+// Netem is a classless qdisc that rate limits based on tokens
+
+type NetemQdiscAttrs struct {
+	Latency       uint32  // in us
+	DelayCorr     float32 // in %
+	Limit         uint32
+	Loss          float32 // in %
+	LossCorr      float32 // in %
+	Gap           uint32
+	Duplicate     float32 // in %
+	DuplicateCorr float32 // in %
+	Jitter        uint32  // in us
+	ReorderProb   float32 // in %
+	ReorderCorr   float32 // in %
+	CorruptProb   float32 // in %
+	CorruptCorr   float32 // in %
+}
+
+func (q NetemQdiscAttrs) String() string {
+	return fmt.Sprintf(
+		"{Latency: %d, Limit: %d, Loss: %d, Gap: %d, Duplicate: %d, Jitter: %d}",
+		q.Latency, q.Limit, q.Loss, q.Gap, q.Duplicate, q.Jitter,
+	)
+}
+
+type Netem struct {
+	QdiscAttrs
+	Latency       uint32
+	DelayCorr     uint32
+	Limit         uint32
+	Loss          uint32
+	LossCorr      uint32
+	Gap           uint32
+	Duplicate     uint32
+	DuplicateCorr uint32
+	Jitter        uint32
+	ReorderProb   uint32
+	ReorderCorr   uint32
+	CorruptProb   uint32
+	CorruptCorr   uint32
+}
+
+func NewNetem(attrs QdiscAttrs, nattrs NetemQdiscAttrs) *Netem {
+	var limit uint32 = 1000
+	var loss_corr, delay_corr, duplicate_corr uint32
+	var reorder_prob, reorder_corr uint32
+	var corrupt_prob, corrupt_corr uint32
+
+	latency := nattrs.Latency
+	loss := Percentage2u32(nattrs.Loss)
+	gap := nattrs.Gap
+	duplicate := Percentage2u32(nattrs.Duplicate)
+	jitter := nattrs.Jitter
+
+	// Correlation
+	if latency > 0 && jitter > 0 {
+		delay_corr = Percentage2u32(nattrs.DelayCorr)
+	}
+	if loss > 0 {
+		loss_corr = Percentage2u32(nattrs.LossCorr)
+	}
+	if duplicate > 0 {
+		duplicate_corr = Percentage2u32(nattrs.DuplicateCorr)
+	}
+	// FIXME should validate values(like loss/duplicate are percentages...)
+	latency = time2Tick(latency)
+
+	if nattrs.Limit != 0 {
+		limit = nattrs.Limit
+	}
+	// Jitter is only value if latency is > 0
+	if latency > 0 {
+		jitter = time2Tick(jitter)
+	}
+
+	reorder_prob = Percentage2u32(nattrs.ReorderProb)
+	reorder_corr = Percentage2u32(nattrs.ReorderCorr)
+
+	if reorder_prob > 0 {
+		// ERROR if lantency == 0
+		if gap == 0 {
+			gap = 1
+		}
+	}
+
+	corrupt_prob = Percentage2u32(nattrs.CorruptProb)
+	corrupt_corr = Percentage2u32(nattrs.CorruptCorr)
+
+	return &Netem{
+		QdiscAttrs:    attrs,
+		Latency:       latency,
+		DelayCorr:     delay_corr,
+		Limit:         limit,
+		Loss:          loss,
+		LossCorr:      loss_corr,
+		Gap:           gap,
+		Duplicate:     duplicate,
+		DuplicateCorr: duplicate_corr,
+		Jitter:        jitter,
+		ReorderProb:   reorder_prob,
+		ReorderCorr:   reorder_corr,
+		CorruptProb:   corrupt_prob,
+		CorruptCorr:   corrupt_corr,
+	}
+}
+
+func (qdisc *Netem) Attrs() *QdiscAttrs {
+	return &qdisc.QdiscAttrs
+}
+
+func (qdisc *Netem) Type() string {
+	return "netem"
+}
+
 // Tbf is a classless qdisc that rate limits based on tokens
 type Tbf struct {
 	QdiscAttrs

+ 114 - 15
libnetwork/Godeps/_workspace/src/github.com/vishvananda/netlink/qdisc_linux.go

@@ -13,24 +13,37 @@ import (
 // QdiscDel will delete a qdisc from the system.
 // Equivalent to: `tc qdisc del $qdisc`
 func QdiscDel(qdisc Qdisc) error {
-	req := nl.NewNetlinkRequest(syscall.RTM_DELQDISC, syscall.NLM_F_ACK)
-	base := qdisc.Attrs()
-	msg := &nl.TcMsg{
-		Family:  nl.FAMILY_ALL,
-		Ifindex: int32(base.LinkIndex),
-		Handle:  base.Handle,
-		Parent:  base.Parent,
-	}
-	req.AddData(msg)
+	return qdiscModify(syscall.RTM_DELQDISC, 0, qdisc)
+}
 
-	_, err := req.Execute(syscall.NETLINK_ROUTE, 0)
-	return err
+// QdiscChange will change a qdisc in place
+// Equivalent to: `tc qdisc change $qdisc`
+// The parent and handle MUST NOT be changed.
+func QdiscChange(qdisc Qdisc) error {
+	return qdiscModify(syscall.RTM_NEWQDISC, 0, qdisc)
+}
+
+// QdiscReplace will replace a qdisc to the system.
+// Equivalent to: `tc qdisc replace $qdisc`
+// The handle MUST change.
+func QdiscReplace(qdisc Qdisc) error {
+	return qdiscModify(
+		syscall.RTM_NEWQDISC,
+		syscall.NLM_F_CREATE|syscall.NLM_F_REPLACE,
+		qdisc)
 }
 
 // QdiscAdd will add a qdisc to the system.
 // Equivalent to: `tc qdisc add $qdisc`
 func QdiscAdd(qdisc Qdisc) error {
-	req := nl.NewNetlinkRequest(syscall.RTM_NEWQDISC, syscall.NLM_F_CREATE|syscall.NLM_F_EXCL|syscall.NLM_F_ACK)
+	return qdiscModify(
+		syscall.RTM_NEWQDISC,
+		syscall.NLM_F_CREATE|syscall.NLM_F_EXCL,
+		qdisc)
+}
+
+func qdiscModify(cmd, flags int, qdisc Qdisc) error {
+	req := nl.NewNetlinkRequest(cmd, flags|syscall.NLM_F_ACK)
 	base := qdisc.Attrs()
 	msg := &nl.TcMsg{
 		Family:  nl.FAMILY_ALL,
@@ -39,6 +52,20 @@ func QdiscAdd(qdisc Qdisc) error {
 		Parent:  base.Parent,
 	}
 	req.AddData(msg)
+
+	// When deleting don't bother building the rest of the netlink payload
+	if cmd != syscall.RTM_DELQDISC {
+		if err := qdiscPayload(req, qdisc); err != nil {
+			return err
+		}
+	}
+
+	_, err := req.Execute(syscall.NETLINK_ROUTE, 0)
+	return err
+}
+
+func qdiscPayload(req *nl.NetlinkRequest, qdisc Qdisc) error {
+
 	req.AddData(nl.NewRtAttr(nl.TCA_KIND, nl.ZeroTerminated(qdisc.Type())))
 
 	options := nl.NewRtAttr(nl.TCA_OPTIONS, nil)
@@ -65,15 +92,47 @@ func QdiscAdd(qdisc Qdisc) error {
 		opt.DirectPkts = htb.DirectPkts
 		nl.NewRtAttrChild(options, nl.TCA_HTB_INIT, opt.Serialize())
 		// nl.NewRtAttrChild(options, nl.TCA_HTB_DIRECT_QLEN, opt.Serialize())
+	} else if netem, ok := qdisc.(*Netem); ok {
+		opt := nl.TcNetemQopt{}
+		opt.Latency = netem.Latency
+		opt.Limit = netem.Limit
+		opt.Loss = netem.Loss
+		opt.Gap = netem.Gap
+		opt.Duplicate = netem.Duplicate
+		opt.Jitter = netem.Jitter
+		options = nl.NewRtAttr(nl.TCA_OPTIONS, opt.Serialize())
+		// Correlation
+		corr := nl.TcNetemCorr{}
+		corr.DelayCorr = netem.DelayCorr
+		corr.LossCorr = netem.LossCorr
+		corr.DupCorr = netem.DuplicateCorr
+
+		if corr.DelayCorr > 0 || corr.LossCorr > 0 || corr.DupCorr > 0 {
+			nl.NewRtAttrChild(options, nl.TCA_NETEM_CORR, corr.Serialize())
+		}
+		// Corruption
+		corruption := nl.TcNetemCorrupt{}
+		corruption.Probability = netem.CorruptProb
+		corruption.Correlation = netem.CorruptCorr
+		if corruption.Probability > 0 {
+			nl.NewRtAttrChild(options, nl.TCA_NETEM_CORRUPT, corruption.Serialize())
+		}
+		// Reorder
+		reorder := nl.TcNetemReorder{}
+		reorder.Probability = netem.ReorderProb
+		reorder.Correlation = netem.ReorderCorr
+		if reorder.Probability > 0 {
+			nl.NewRtAttrChild(options, nl.TCA_NETEM_REORDER, reorder.Serialize())
+		}
 	} else if _, ok := qdisc.(*Ingress); ok {
 		// ingress filters must use the proper handle
-		if msg.Parent != HANDLE_INGRESS {
+		if qdisc.Attrs().Parent != HANDLE_INGRESS {
 			return fmt.Errorf("Ingress filters must set Parent to HANDLE_INGRESS")
 		}
 	}
+
 	req.AddData(options)
-	_, err := req.Execute(syscall.NETLINK_ROUTE, 0)
-	return err
+	return nil
 }
 
 // QdiscList gets a list of qdiscs in the system.
@@ -135,6 +194,8 @@ func QdiscList(link Link) ([]Qdisc, error) {
 					qdisc = &Ingress{}
 				case "htb":
 					qdisc = &Htb{}
+				case "netem":
+					qdisc = &Netem{}
 				default:
 					qdisc = &GenericQdisc{QdiscType: qdiscType}
 				}
@@ -166,6 +227,10 @@ func QdiscList(link Link) ([]Qdisc, error) {
 					if err := parseHtbData(qdisc, data); err != nil {
 						return nil, err
 					}
+				case "netem":
+					if err := parseNetemData(qdisc, attr.Value); err != nil {
+						return nil, err
+					}
 
 					// no options for ingress
 				}
@@ -213,6 +278,40 @@ func parseHtbData(qdisc Qdisc, data []syscall.NetlinkRouteAttr) error {
 	}
 	return nil
 }
+
+func parseNetemData(qdisc Qdisc, value []byte) error {
+	netem := qdisc.(*Netem)
+	opt := nl.DeserializeTcNetemQopt(value)
+	netem.Latency = opt.Latency
+	netem.Limit = opt.Limit
+	netem.Loss = opt.Loss
+	netem.Gap = opt.Gap
+	netem.Duplicate = opt.Duplicate
+	netem.Jitter = opt.Jitter
+	data, err := nl.ParseRouteAttr(value[nl.SizeofTcNetemQopt:])
+	if err != nil {
+		return err
+	}
+	for _, datum := range data {
+		switch datum.Attr.Type {
+		case nl.TCA_NETEM_CORR:
+			opt := nl.DeserializeTcNetemCorr(datum.Value)
+			netem.DelayCorr = opt.DelayCorr
+			netem.LossCorr = opt.LossCorr
+			netem.DuplicateCorr = opt.DupCorr
+		case nl.TCA_NETEM_CORRUPT:
+			opt := nl.DeserializeTcNetemCorrupt(datum.Value)
+			netem.CorruptProb = opt.Probability
+			netem.CorruptCorr = opt.Correlation
+		case nl.TCA_NETEM_REORDER:
+			opt := nl.DeserializeTcNetemReorder(datum.Value)
+			netem.ReorderProb = opt.Probability
+			netem.ReorderCorr = opt.Correlation
+		}
+	}
+	return nil
+}
+
 func parseTbfData(qdisc Qdisc, data []syscall.NetlinkRouteAttr) error {
 	native = nl.NativeEndian()
 	tbf := qdisc.(*Tbf)

+ 345 - 0
libnetwork/Godeps/_workspace/src/github.com/vishvananda/netlink/qdisc_test.go

@@ -0,0 +1,345 @@
+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")
+	}
+}

+ 13 - 10
libnetwork/Godeps/_workspace/src/github.com/vishvananda/netlink/route.go

@@ -24,17 +24,20 @@ const (
 	FLAG_PERVASIVE NextHopFlag = syscall.RTNH_F_PERVASIVE
 )
 
-// Route represents a netlink route. A route is associated with a link,
-// has a destination network, an optional source ip, and optional
-// gateway. Advanced route parameters and non-main routing tables are
-// currently not supported.
+// Route represents a netlink route.
 type Route struct {
-	LinkIndex int
-	Scope     Scope
-	Dst       *net.IPNet
-	Src       net.IP
-	Gw        net.IP
-	Flags     int
+	LinkIndex  int
+	ILinkIndex int
+	Scope      Scope
+	Dst        *net.IPNet
+	Src        net.IP
+	Gw         net.IP
+	Protocol   int
+	Priority   int
+	Table      int
+	Type       int
+	Tos        int
+	Flags      int
 }
 
 func (r Route) String() string {

+ 104 - 28
libnetwork/Godeps/_workspace/src/github.com/vishvananda/netlink/route_linux.go

@@ -10,6 +10,19 @@ import (
 
 // RtAttr is shared so it is in netlink_linux.go
 
+const (
+	RT_FILTER_PROTOCOL uint64 = 1 << (1 + iota)
+	RT_FILTER_SCOPE
+	RT_FILTER_TYPE
+	RT_FILTER_TOS
+	RT_FILTER_IIF
+	RT_FILTER_OIF
+	RT_FILTER_DST
+	RT_FILTER_SRC
+	RT_FILTER_GW
+	RT_FILTER_TABLE
+)
+
 // RouteAdd will add a route to the system.
 // Equivalent to: `ip route add $route`
 func RouteAdd(route *Route) error {
@@ -29,8 +42,6 @@ func routeHandle(route *Route, req *nl.NetlinkRequest, msg *nl.RtMsg) error {
 		return fmt.Errorf("one of Dst.IP, Src, or Gw must not be nil")
 	}
 
-	msg.Scope = uint8(route.Scope)
-	msg.Flags = uint32(route.Flags)
 	family := -1
 	var rtAttrs []*nl.RtAttr
 
@@ -79,8 +90,34 @@ func routeHandle(route *Route, req *nl.NetlinkRequest, msg *nl.RtMsg) error {
 		rtAttrs = append(rtAttrs, nl.NewRtAttr(syscall.RTA_GATEWAY, gwData))
 	}
 
-	msg.Family = uint8(family)
+	if route.Table > 0 {
+		if route.Table >= 256 {
+			msg.Table = syscall.RT_TABLE_UNSPEC
+			b := make([]byte, 4)
+			native.PutUint32(b, uint32(route.Table))
+			rtAttrs = append(rtAttrs, nl.NewRtAttr(syscall.RTA_TABLE, b))
+		} else {
+			msg.Table = uint8(route.Table)
+		}
+	}
+
+	if route.Priority > 0 {
+		b := make([]byte, 4)
+		native.PutUint32(b, uint32(route.Priority))
+		rtAttrs = append(rtAttrs, nl.NewRtAttr(syscall.RTA_PRIORITY, b))
+	}
+	if route.Tos > 0 {
+		msg.Tos = uint8(route.Tos)
+	}
+	if route.Protocol > 0 {
+		msg.Protocol = uint8(route.Protocol)
+	}
+	if route.Type > 0 {
+		msg.Type = uint8(route.Type)
+	}
 
+	msg.Scope = uint8(route.Scope)
+	msg.Family = uint8(family)
 	req.AddData(msg)
 	for _, attr := range rtAttrs {
 		req.AddData(attr)
@@ -102,61 +139,95 @@ func routeHandle(route *Route, req *nl.NetlinkRequest, msg *nl.RtMsg) error {
 // Equivalent to: `ip route show`.
 // The list can be filtered by link and ip family.
 func RouteList(link Link, family int) ([]Route, error) {
+	var routeFilter *Route
+	if link != nil {
+		routeFilter = &Route{
+			LinkIndex: link.Attrs().Index,
+		}
+	}
+	return RouteListFiltered(family, routeFilter, RT_FILTER_OIF)
+}
+
+// RouteListFiltered gets a list of routes in the system filtered with specified rules.
+// All rules must be defined in RouteFilter struct
+func RouteListFiltered(family int, filter *Route, filterMask uint64) ([]Route, error) {
 	req := nl.NewNetlinkRequest(syscall.RTM_GETROUTE, syscall.NLM_F_DUMP)
-	msg := nl.NewIfInfomsg(family)
-	req.AddData(msg)
+	infmsg := nl.NewIfInfomsg(family)
+	req.AddData(infmsg)
 
 	msgs, err := req.Execute(syscall.NETLINK_ROUTE, syscall.RTM_NEWROUTE)
 	if err != nil {
 		return nil, err
 	}
 
-	index := 0
-	if link != nil {
-		base := link.Attrs()
-		ensureIndex(base)
-		index = base.Index
-	}
-
 	var res []Route
 	for _, m := range msgs {
 		msg := nl.DeserializeRtMsg(m)
-
 		if msg.Flags&syscall.RTM_F_CLONED != 0 {
 			// Ignore cloned routes
 			continue
 		}
-
 		if msg.Table != syscall.RT_TABLE_MAIN {
-			// Ignore non-main tables
-			continue
+			if filter == nil || filter != nil && filterMask&RT_FILTER_TABLE == 0 {
+				// Ignore non-main tables
+				continue
+			}
 		}
-
 		route, err := deserializeRoute(m)
 		if err != nil {
 			return nil, err
 		}
-
-		if link != nil && route.LinkIndex != index {
-			// Ignore routes from other interfaces
-			continue
+		if filter != nil {
+			switch {
+			case filterMask&RT_FILTER_TABLE != 0 && route.Table != filter.Table:
+				continue
+			case filterMask&RT_FILTER_PROTOCOL != 0 && route.Protocol != filter.Protocol:
+				continue
+			case filterMask&RT_FILTER_SCOPE != 0 && route.Scope != filter.Scope:
+				continue
+			case filterMask&RT_FILTER_TYPE != 0 && route.Type != filter.Type:
+				continue
+			case filterMask&RT_FILTER_TOS != 0 && route.Tos != filter.Tos:
+				continue
+			case filterMask&RT_FILTER_OIF != 0 && route.LinkIndex != filter.LinkIndex:
+				continue
+			case filterMask&RT_FILTER_IIF != 0 && route.ILinkIndex != filter.ILinkIndex:
+				continue
+			case filterMask&RT_FILTER_GW != 0 && !route.Gw.Equal(filter.Gw):
+				continue
+			case filterMask&RT_FILTER_SRC != 0 && !route.Src.Equal(filter.Src):
+				continue
+			case filterMask&RT_FILTER_DST != 0 && filter.Dst != nil:
+				if route.Dst == nil {
+					continue
+				}
+				aMaskLen, aMaskBits := route.Dst.Mask.Size()
+				bMaskLen, bMaskBits := filter.Dst.Mask.Size()
+				if !(route.Dst.IP.Equal(filter.Dst.IP) && aMaskLen == bMaskLen && aMaskBits == bMaskBits) {
+					continue
+				}
+			}
 		}
 		res = append(res, route)
 	}
-
 	return res, nil
 }
 
 // deserializeRoute decodes a binary netlink message into a Route struct
 func deserializeRoute(m []byte) (Route, error) {
-	route := Route{}
 	msg := nl.DeserializeRtMsg(m)
 	attrs, err := nl.ParseRouteAttr(m[msg.Len():])
 	if err != nil {
-		return route, err
+		return Route{}, err
+	}
+	route := Route{
+		Scope:    Scope(msg.Scope),
+		Protocol: int(msg.Protocol),
+		Table:    int(msg.Table),
+		Type:     int(msg.Type),
+		Tos:      int(msg.Tos),
+		Flags:    int(msg.Flags),
 	}
-	route.Scope = Scope(msg.Scope)
-	route.Flags = int(msg.Flags)
 
 	native := nl.NativeEndian()
 	for _, attr := range attrs {
@@ -171,8 +242,13 @@ func deserializeRoute(m []byte) (Route, error) {
 				Mask: net.CIDRMask(int(msg.Dst_len), 8*len(attr.Value)),
 			}
 		case syscall.RTA_OIF:
-			routeIndex := int(native.Uint32(attr.Value[0:4]))
-			route.LinkIndex = routeIndex
+			route.LinkIndex = int(native.Uint32(attr.Value[0:4]))
+		case syscall.RTA_IIF:
+			route.ILinkIndex = int(native.Uint32(attr.Value[0:4]))
+		case syscall.RTA_PRIORITY:
+			route.Priority = int(native.Uint32(attr.Value[0:4]))
+		case syscall.RTA_TABLE:
+			route.Table = int(native.Uint32(attr.Value[0:4]))
 		}
 	}
 	return route, nil

+ 210 - 0
libnetwork/Godeps/_workspace/src/github.com/vishvananda/netlink/route_test.go

@@ -0,0 +1,210 @@
+package netlink
+
+import (
+	"net"
+	"syscall"
+	"testing"
+	"time"
+)
+
+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 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 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")
+	}
+}

+ 43 - 0
libnetwork/Godeps/_workspace/src/github.com/vishvananda/netlink/rule.go

@@ -0,0 +1,43 @@
+package netlink
+
+import (
+	"fmt"
+	"net"
+
+	"github.com/vishvananda/netlink/nl"
+)
+
+// Rule represents a netlink rule.
+type Rule struct {
+	*nl.RtMsg
+	Priority          int
+	Table             int
+	Mark              int
+	Mask              int
+	TunID             uint
+	Goto              int
+	Src               *net.IPNet
+	Dst               *net.IPNet
+	Flow              int
+	IifName           string
+	OifName           string
+	SuppressIfgroup   int
+	SuppressPrefixlen int
+}
+
+func (r Rule) String() string {
+	return fmt.Sprintf("ip rule %d: from %s table %d", r.Priority, r.Src, r.Table)
+}
+
+// NewRule return empty rules.
+func NewRule() *Rule {
+	return &Rule{
+		SuppressIfgroup:   -1,
+		SuppressPrefixlen: -1,
+		Priority:          -1,
+		Mark:              -1,
+		Mask:              -1,
+		Goto:              -1,
+		Flow:              -1,
+	}
+}

+ 198 - 0
libnetwork/Godeps/_workspace/src/github.com/vishvananda/netlink/rule_linux.go

@@ -0,0 +1,198 @@
+package netlink
+
+import (
+	"fmt"
+	"net"
+	"syscall"
+
+	"github.com/vishvananda/netlink/nl"
+)
+
+// RuleAdd adds a rule to the system.
+// Equivalent to: ip rule add
+func RuleAdd(rule *Rule) error {
+	req := nl.NewNetlinkRequest(syscall.RTM_NEWRULE, syscall.NLM_F_CREATE|syscall.NLM_F_EXCL|syscall.NLM_F_ACK)
+	return ruleHandle(rule, req)
+}
+
+// RuleDel deletes a rule from the system.
+// Equivalent to: ip rule del
+func RuleDel(rule *Rule) error {
+	req := nl.NewNetlinkRequest(syscall.RTM_DELRULE, syscall.NLM_F_CREATE|syscall.NLM_F_EXCL|syscall.NLM_F_ACK)
+	return ruleHandle(rule, req)
+}
+
+func ruleHandle(rule *Rule, req *nl.NetlinkRequest) error {
+	msg := nl.NewRtMsg()
+	msg.Family = syscall.AF_INET
+	var dstFamily uint8
+
+	var rtAttrs []*nl.RtAttr
+	if rule.Dst != nil && rule.Dst.IP != nil {
+		dstLen, _ := rule.Dst.Mask.Size()
+		msg.Dst_len = uint8(dstLen)
+		msg.Family = uint8(nl.GetIPFamily(rule.Dst.IP))
+		dstFamily = msg.Family
+		var dstData []byte
+		if msg.Family == syscall.AF_INET {
+			dstData = rule.Dst.IP.To4()
+		} else {
+			dstData = rule.Dst.IP.To16()
+		}
+		rtAttrs = append(rtAttrs, nl.NewRtAttr(syscall.RTA_DST, dstData))
+	}
+
+	if rule.Src != nil && rule.Src.IP != nil {
+		msg.Family = uint8(nl.GetIPFamily(rule.Src.IP))
+		if dstFamily != 0 && dstFamily != msg.Family {
+			return fmt.Errorf("source and destination ip are not the same IP family")
+		}
+		srcLen, _ := rule.Src.Mask.Size()
+		msg.Src_len = uint8(srcLen)
+		var srcData []byte
+		if msg.Family == syscall.AF_INET {
+			srcData = rule.Src.IP.To4()
+		} else {
+			srcData = rule.Src.IP.To16()
+		}
+		rtAttrs = append(rtAttrs, nl.NewRtAttr(syscall.RTA_SRC, srcData))
+	}
+
+	if rule.Table >= 0 {
+		msg.Table = uint8(rule.Table)
+		if rule.Table >= 256 {
+			msg.Table = syscall.RT_TABLE_UNSPEC
+		}
+	}
+
+	req.AddData(msg)
+	for i := range rtAttrs {
+		req.AddData(rtAttrs[i])
+	}
+
+	var (
+		b      = make([]byte, 4)
+		native = nl.NativeEndian()
+	)
+
+	if rule.Priority >= 0 {
+		native.PutUint32(b, uint32(rule.Priority))
+		req.AddData(nl.NewRtAttr(nl.FRA_PRIORITY, b))
+	}
+	if rule.Mark >= 0 {
+		native.PutUint32(b, uint32(rule.Mark))
+		req.AddData(nl.NewRtAttr(nl.FRA_FWMARK, b))
+	}
+	if rule.Mask >= 0 {
+		native.PutUint32(b, uint32(rule.Mask))
+		req.AddData(nl.NewRtAttr(nl.FRA_FWMASK, b))
+	}
+	if rule.Flow >= 0 {
+		native.PutUint32(b, uint32(rule.Flow))
+		req.AddData(nl.NewRtAttr(nl.FRA_FLOW, b))
+	}
+	if rule.TunID > 0 {
+		native.PutUint32(b, uint32(rule.TunID))
+		req.AddData(nl.NewRtAttr(nl.FRA_TUN_ID, b))
+	}
+	if rule.Table >= 256 {
+		native.PutUint32(b, uint32(rule.Table))
+		req.AddData(nl.NewRtAttr(nl.FRA_TABLE, b))
+	}
+	if msg.Table > 0 {
+		if rule.SuppressPrefixlen >= 0 {
+			native.PutUint32(b, uint32(rule.SuppressPrefixlen))
+			req.AddData(nl.NewRtAttr(nl.FRA_SUPPRESS_PREFIXLEN, b))
+		}
+		if rule.SuppressIfgroup >= 0 {
+			native.PutUint32(b, uint32(rule.SuppressIfgroup))
+			req.AddData(nl.NewRtAttr(nl.FRA_SUPPRESS_IFGROUP, b))
+		}
+	}
+	if rule.IifName != "" {
+		req.AddData(nl.NewRtAttr(nl.FRA_IIFNAME, []byte(rule.IifName)))
+	}
+	if rule.OifName != "" {
+		req.AddData(nl.NewRtAttr(nl.FRA_OIFNAME, []byte(rule.OifName)))
+	}
+	if rule.Goto >= 0 {
+		msg.Type = nl.FR_ACT_NOP
+		native.PutUint32(b, uint32(rule.Goto))
+		req.AddData(nl.NewRtAttr(nl.FRA_GOTO, b))
+	}
+
+	_, err := req.Execute(syscall.NETLINK_ROUTE, 0)
+	return err
+}
+
+// RuleList lists rules in the system.
+// Equivalent to: ip rule list
+func RuleList(family int) ([]Rule, error) {
+	req := nl.NewNetlinkRequest(syscall.RTM_GETRULE, syscall.NLM_F_DUMP|syscall.NLM_F_REQUEST)
+	msg := nl.NewIfInfomsg(family)
+	req.AddData(msg)
+
+	msgs, err := req.Execute(syscall.NETLINK_ROUTE, syscall.RTM_NEWRULE)
+	if err != nil {
+		return nil, err
+	}
+
+	native := nl.NativeEndian()
+	var res = make([]Rule, 0)
+	for i := range msgs {
+		msg := nl.DeserializeRtMsg(msgs[i])
+		attrs, err := nl.ParseRouteAttr(msgs[i][msg.Len():])
+		if err != nil {
+			return nil, err
+		}
+
+		rule := NewRule()
+		rule.RtMsg = msg
+
+		for j := range attrs {
+			switch attrs[j].Attr.Type {
+			case syscall.RTA_TABLE:
+				rule.Table = int(native.Uint32(attrs[j].Value[0:4]))
+			case nl.FRA_SRC:
+				rule.Src = &net.IPNet{
+					IP:   attrs[j].Value,
+					Mask: net.CIDRMask(int(msg.Src_len), 8*len(attrs[j].Value)),
+				}
+			case nl.FRA_DST:
+				rule.Dst = &net.IPNet{
+					IP:   attrs[j].Value,
+					Mask: net.CIDRMask(int(msg.Dst_len), 8*len(attrs[j].Value)),
+				}
+			case nl.FRA_FWMARK:
+				rule.Mark = int(native.Uint32(attrs[j].Value[0:4]))
+			case nl.FRA_FWMASK:
+				rule.Mask = int(native.Uint32(attrs[j].Value[0:4]))
+			case nl.FRA_TUN_ID:
+				rule.TunID = uint(native.Uint64(attrs[j].Value[0:4]))
+			case nl.FRA_IIFNAME:
+				rule.IifName = string(attrs[j].Value[:len(attrs[j].Value)-1])
+			case nl.FRA_OIFNAME:
+				rule.OifName = string(attrs[j].Value[:len(attrs[j].Value)-1])
+			case nl.FRA_SUPPRESS_PREFIXLEN:
+				i := native.Uint32(attrs[j].Value[0:4])
+				if i != 0xffffffff {
+					rule.SuppressPrefixlen = int(i)
+				}
+			case nl.FRA_SUPPRESS_IFGROUP:
+				i := native.Uint32(attrs[j].Value[0:4])
+				if i != 0xffffffff {
+					rule.SuppressIfgroup = int(i)
+				}
+			case nl.FRA_FLOW:
+				rule.Flow = int(native.Uint32(attrs[j].Value[0:4]))
+			case nl.FRA_GOTO:
+				rule.Goto = int(native.Uint32(attrs[j].Value[0:4]))
+			case nl.FRA_PRIORITY:
+				rule.Priority = int(native.Uint32(attrs[j].Value[0:4]))
+			}
+		}
+		res = append(res, *rule)
+	}
+
+	return res, nil
+}

+ 66 - 0
libnetwork/Godeps/_workspace/src/github.com/vishvananda/netlink/rule_test.go

@@ -0,0 +1,66 @@
+package netlink
+
+import (
+	"net"
+	"syscall"
+	"testing"
+)
+
+func TestRuleAddDel(t *testing.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)}
+
+	rules_begin, 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(rules_begin)+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)
+	}
+
+	rules_end, err := RuleList(syscall.AF_INET)
+	if err != nil {
+		t.Fatal(err)
+	}
+
+	if len(rules_end) != len(rules_begin) {
+		t.Fatal("Rule not removed properly")
+	}
+}

+ 49 - 0
libnetwork/Godeps/_workspace/src/github.com/vishvananda/netlink/xfrm_policy_test.go

@@ -0,0 +1,49 @@
+package netlink
+
+import (
+	"net"
+	"testing"
+)
+
+func TestXfrmPolicyAddDel(t *testing.T) {
+	tearDown := setUpNetlinkTest(t)
+	defer tearDown()
+
+	src, _ := ParseIPNet("127.1.1.1/32")
+	dst, _ := ParseIPNet("127.1.1.2/32")
+	policy := XfrmPolicy{
+		Src: src,
+		Dst: dst,
+		Dir: XFRM_DIR_OUT,
+	}
+	tmpl := XfrmPolicyTmpl{
+		Src:   net.ParseIP("127.0.0.1"),
+		Dst:   net.ParseIP("127.0.0.2"),
+		Proto: XFRM_PROTO_ESP,
+		Mode:  XFRM_MODE_TUNNEL,
+	}
+	policy.Tmpls = append(policy.Tmpls, tmpl)
+	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 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")
+	}
+}

+ 50 - 0
libnetwork/Godeps/_workspace/src/github.com/vishvananda/netlink/xfrm_state_test.go

@@ -0,0 +1,50 @@
+package netlink
+
+import (
+	"net"
+	"testing"
+)
+
+func TestXfrmStateAddDel(t *testing.T) {
+	tearDown := setUpNetlinkTest(t)
+	defer tearDown()
+
+	state := XfrmState{
+		Src:   net.ParseIP("127.0.0.1"),
+		Dst:   net.ParseIP("127.0.0.2"),
+		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"),
+		},
+	}
+	if err := XfrmStateAdd(&state); err != nil {
+		t.Fatal(err)
+	}
+	policies, err := XfrmStateList(FAMILY_ALL)
+	if err != nil {
+		t.Fatal(err)
+	}
+
+	if len(policies) != 1 {
+		t.Fatal("State not added properly")
+	}
+
+	if err = XfrmStateDel(&state); err != nil {
+		t.Fatal(err)
+	}
+
+	policies, err = XfrmStateList(FAMILY_ALL)
+	if err != nil {
+		t.Fatal(err)
+	}
+	if len(policies) != 0 {
+		t.Fatal("State not removed properly")
+	}
+}

+ 1 - 1
libnetwork/Makefile

@@ -4,7 +4,7 @@ build_image=libnetworkbuild
 dockerargs = --privileged -v $(shell pwd):/go/src/github.com/docker/libnetwork -w /go/src/github.com/docker/libnetwork
 container_env = -e "INSIDECONTAINER=-incontainer=true"
 docker = docker run --rm -it ${dockerargs} $$EXTRA_ARGS ${container_env} ${build_image}
-ciargs = -e "COVERALLS_TOKEN=$$COVERALLS_TOKEN" -e "INSIDECONTAINER=-incontainer=true"
+ciargs = -e CIRCLECI -e "COVERALLS_TOKEN=$$COVERALLS_TOKEN" -e "INSIDECONTAINER=-incontainer=true"
 cidocker = docker run ${dockerargs} ${ciargs} ${container_env} ${build_image}
 CROSS_PLATFORMS = linux/amd64 linux/386 linux/arm windows/amd64 windows/386
 

+ 2 - 1
libnetwork/osl/interface_linux.go

@@ -6,6 +6,7 @@ import (
 	"os/exec"
 	"regexp"
 	"sync"
+	"syscall"
 
 	"github.com/docker/libnetwork/types"
 	"github.com/vishvananda/netlink"
@@ -337,7 +338,7 @@ func setInterfaceIPv6(iface netlink.Link, i *nwIface) error {
 	if i.AddressIPv6() == nil {
 		return nil
 	}
-	ipAddr := &netlink.Addr{IPNet: i.AddressIPv6(), Label: ""}
+	ipAddr := &netlink.Addr{IPNet: i.AddressIPv6(), Label: "", Flags: syscall.IFA_F_NODAD}
 	return netlink.AddrAdd(iface, ipAddr)
 }
 

+ 43 - 0
libnetwork/osl/sandbox_linux_test.go

@@ -5,12 +5,15 @@ import (
 	"os"
 	"path/filepath"
 	"runtime"
+	"syscall"
 	"testing"
 	"time"
 
 	"github.com/docker/libnetwork/netutils"
+	"github.com/docker/libnetwork/testutils"
 	"github.com/docker/libnetwork/types"
 	"github.com/vishvananda/netlink"
+	"github.com/vishvananda/netlink/nl"
 	"github.com/vishvananda/netns"
 )
 
@@ -179,3 +182,43 @@ func TestScanStatistics(t *testing.T) {
 		t.Fatalf("Error scanning the statistics")
 	}
 }
+
+func TestDisableIPv6DAD(t *testing.T) {
+	if testutils.RunningOnCircleCI() {
+		t.Skipf("Skipping as not supported on CIRCLE CI kernel")
+	}
+
+	defer testutils.SetupTestOSContext(t)()
+
+	ipv6, _ := types.ParseCIDR("2001:db8::44/64")
+	iface := &nwIface{addressIPv6: ipv6}
+
+	veth := &netlink.Veth{
+		LinkAttrs: netlink.LinkAttrs{Name: "sideA"},
+		PeerName:  "sideB",
+	}
+
+	err := netlink.LinkAdd(veth)
+	if err != nil {
+		t.Fatal(err)
+	}
+
+	link, err := netlink.LinkByName("sideA")
+	if err != nil {
+		t.Fatal(err)
+	}
+
+	err = setInterfaceIPv6(link, iface)
+	if err != nil {
+		t.Fatal(err)
+	}
+
+	addrList, err := netlink.AddrList(link, nl.FAMILY_V6)
+	if err != nil {
+		t.Fatal(err)
+	}
+
+	if addrList[0].Flags&syscall.IFA_F_NODAD == 0 {
+		t.Fatalf("Unexpected interface flags: 0x%x. Expected to contain 0x%x", addrList[0].Flags, syscall.IFA_F_NODAD)
+	}
+}

+ 6 - 0
libnetwork/testutils/context.go

@@ -1,6 +1,7 @@
 package testutils
 
 import (
+	"os"
 	"runtime"
 	"syscall"
 	"testing"
@@ -37,3 +38,8 @@ func SetupTestOSContext(t *testing.T) func() {
 		runtime.UnlockOSThread()
 	}
 }
+
+// RunningOnCircleCI returns true if being executed on libnetwork Circle CI setup
+func RunningOnCircleCI() bool {
+	return os.Getenv("CIRCLECI") != ""
+}