diff --git a/Godeps/Godeps.json b/Godeps/Godeps.json index 62bf3d94..a59db899 100644 --- a/Godeps/Godeps.json +++ b/Godeps/Godeps.json @@ -176,11 +176,11 @@ }, { "ImportPath": "github.com/vishvananda/netlink", - "Rev": "fe3b5664d23a11b52ba59bece4ff29c52772a56b" + "Rev": "b71e0bb214aebd980216cb2516e8bd7bca9e9672" }, { "ImportPath": "github.com/vishvananda/netlink/nl", - "Rev": "fe3b5664d23a11b52ba59bece4ff29c52772a56b" + "Rev": "b71e0bb214aebd980216cb2516e8bd7bca9e9672" }, { "ImportPath": "github.com/vishvananda/netns", diff --git a/vendor/github.com/vishvananda/netlink/.travis.yml b/vendor/github.com/vishvananda/netlink/.travis.yml index 73a0374c..f5c0b3eb 100644 --- a/vendor/github.com/vishvananda/netlink/.travis.yml +++ b/vendor/github.com/vishvananda/netlink/.travis.yml @@ -4,5 +4,10 @@ before_script: - sudo sed -i -e 's/^Defaults\tsecure_path.*$//' /etc/sudoers # modprobe ip_gre or else the first gre device can't be deleted - sudo modprobe ip_gre + # modprobe nf_conntrack for the conntrack testing + - sudo modprobe nf_conntrack + - sudo modprobe nf_conntrack_netlink + - sudo modprobe nf_conntrack_ipv4 + - sudo modprobe nf_conntrack_ipv6 install: - go get github.com/vishvananda/netns diff --git a/vendor/github.com/vishvananda/netlink/addr.go b/vendor/github.com/vishvananda/netlink/addr.go index fe3e3d36..f08c9569 100644 --- a/vendor/github.com/vishvananda/netlink/addr.go +++ b/vendor/github.com/vishvananda/netlink/addr.go @@ -10,11 +10,13 @@ import ( // include a mask, so it stores the address as a net.IPNet. type Addr struct { *net.IPNet - Label string - Flags int - Scope int - Peer *net.IPNet - Broadcast net.IP + Label string + Flags int + Scope int + Peer *net.IPNet + Broadcast net.IP + PreferedLft int + ValidLft int } // String returns $ip/$netmask $label diff --git a/vendor/github.com/vishvananda/netlink/addr_linux.go b/vendor/github.com/vishvananda/netlink/addr_linux.go index 5348e403..43daa447 100644 --- a/vendor/github.com/vishvananda/netlink/addr_linux.go +++ b/vendor/github.com/vishvananda/netlink/addr_linux.go @@ -27,6 +27,19 @@ func (h *Handle) AddrAdd(link Link, addr *Addr) error { return h.addrHandle(link, addr, req) } +// AddrReplace will replace (or, if not present, add) an IP address on a link device. +// Equivalent to: `ip addr replace $addr dev $link` +func AddrReplace(link Link, addr *Addr) error { + return pkgHandle.AddrReplace(link, addr) +} + +// AddrReplace will replace (or, if not present, add) an IP address on a link device. +// Equivalent to: `ip addr replace $addr dev $link` +func (h *Handle) AddrReplace(link Link, addr *Addr) error { + req := h.newNetlinkRequest(syscall.RTM_NEWADDR, syscall.NLM_F_CREATE|syscall.NLM_F_REPLACE|syscall.NLM_F_ACK) + return h.addrHandle(link, addr, req) +} + // AddrDel will delete an IP address from a link device. // Equivalent to: `ip addr del $addr dev $link` func AddrDel(link Link, addr *Addr) error { @@ -186,6 +199,10 @@ func parseAddr(m []byte) (addr Addr, family, index int, err error) { addr.Label = string(attr.Value[:len(attr.Value)-1]) case IFA_FLAGS: addr.Flags = int(native.Uint32(attr.Value[0:4])) + case nl.IFA_CACHEINFO: + ci := nl.DeserializeIfaCacheInfo(attr.Value) + addr.PreferedLft = int(ci.IfaPrefered) + addr.ValidLft = int(ci.IfaValid) } } @@ -203,6 +220,10 @@ func parseAddr(m []byte) (addr Addr, family, index int, err error) { type AddrUpdate struct { LinkAddress net.IPNet LinkIndex int + Flags int + Scope int + PreferedLft int + ValidLft int NewAddr bool // true=added false=deleted } @@ -250,7 +271,13 @@ func addrSubscribe(newNs, curNs netns.NsHandle, ch chan<- AddrUpdate, done <-cha continue } - ch <- AddrUpdate{LinkAddress: *addr.IPNet, LinkIndex: ifindex, NewAddr: msgType == syscall.RTM_NEWADDR} + ch <- AddrUpdate{LinkAddress: *addr.IPNet, + LinkIndex: ifindex, + NewAddr: msgType == syscall.RTM_NEWADDR, + Flags: addr.Flags, + Scope: addr.Scope, + PreferedLft: addr.PreferedLft, + ValidLft: addr.ValidLft} } } }() diff --git a/vendor/github.com/vishvananda/netlink/addr_test.go b/vendor/github.com/vishvananda/netlink/addr_test.go new file mode 100644 index 00000000..cd7fed4d --- /dev/null +++ b/vendor/github.com/vishvananda/netlink/addr_test.go @@ -0,0 +1,193 @@ +// +build linux + +package netlink + +import ( + "net" + "os" + "syscall" + "testing" +) + +func TestAddrAdd(t *testing.T) { + DoTestAddr(t, AddrAdd) +} + +func TestAddrReplace(t *testing.T) { + DoTestAddr(t, AddrReplace) +} + +func DoTestAddr(t *testing.T, FunctionUndertest func(Link, *Addr) error) { + if os.Getenv("TRAVIS_BUILD_DIR") != "" { + t.Skipf("Fails in travis with: addr_test.go:68: Address flags not set properly, got=0, expected=128") + } + // TODO: IFA_F_PERMANENT does not seem to be set by default on older kernels? + var address = &net.IPNet{IP: net.IPv4(127, 0, 0, 2), Mask: net.CIDRMask(24, 32)} + var peer = &net.IPNet{IP: net.IPv4(127, 0, 0, 3), Mask: net.CIDRMask(24, 32)} + var addrTests = []struct { + addr *Addr + expected *Addr + }{ + { + &Addr{IPNet: address}, + &Addr{IPNet: address, Label: "lo", Scope: syscall.RT_SCOPE_UNIVERSE, Flags: syscall.IFA_F_PERMANENT}, + }, + { + &Addr{IPNet: address, Label: "local"}, + &Addr{IPNet: address, Label: "local", Scope: syscall.RT_SCOPE_UNIVERSE, Flags: syscall.IFA_F_PERMANENT}, + }, + { + &Addr{IPNet: address, Flags: syscall.IFA_F_OPTIMISTIC}, + &Addr{IPNet: address, Label: "lo", Flags: syscall.IFA_F_OPTIMISTIC | syscall.IFA_F_PERMANENT, Scope: syscall.RT_SCOPE_UNIVERSE}, + }, + { + &Addr{IPNet: address, Flags: syscall.IFA_F_OPTIMISTIC | syscall.IFA_F_DADFAILED}, + &Addr{IPNet: address, Label: "lo", Flags: syscall.IFA_F_OPTIMISTIC | syscall.IFA_F_DADFAILED | syscall.IFA_F_PERMANENT, Scope: syscall.RT_SCOPE_UNIVERSE}, + }, + { + &Addr{IPNet: address, Scope: syscall.RT_SCOPE_NOWHERE}, + &Addr{IPNet: address, Label: "lo", Flags: syscall.IFA_F_PERMANENT, Scope: syscall.RT_SCOPE_NOWHERE}, + }, + { + &Addr{IPNet: address, Peer: peer}, + &Addr{IPNet: address, Peer: peer, Label: "lo", Scope: syscall.RT_SCOPE_UNIVERSE, Flags: syscall.IFA_F_PERMANENT}, + }, + } + + tearDown := setUpNetlinkTest(t) + defer tearDown() + + link, err := LinkByName("lo") + if err != nil { + t.Fatal(err) + } + + for _, tt := range addrTests { + if err = FunctionUndertest(link, tt.addr); err != nil { + t.Fatal(err) + } + + addrs, err := AddrList(link, FAMILY_ALL) + if err != nil { + t.Fatal(err) + } + + if len(addrs) != 1 { + t.Fatal("Address not added properly") + } + + if !addrs[0].Equal(*tt.expected) { + t.Fatalf("Address ip no set properly, got=%s, expected=%s", addrs[0], tt.expected) + } + + if addrs[0].Label != tt.expected.Label { + t.Fatalf("Address label not set properly, got=%s, expected=%s", addrs[0].Label, tt.expected.Label) + } + + if addrs[0].Flags != tt.expected.Flags { + t.Fatalf("Address flags not set properly, got=%d, expected=%d", addrs[0].Flags, tt.expected.Flags) + } + + if addrs[0].Scope != tt.expected.Scope { + t.Fatalf("Address scope not set properly, got=%d, expected=%d", addrs[0].Scope, tt.expected.Scope) + } + + if tt.expected.Peer != nil { + if !addrs[0].PeerEqual(*tt.expected) { + t.Fatalf("Peer Address ip no set properly, got=%s, expected=%s", addrs[0].Peer, tt.expected.Peer) + } + } + + // Pass FAMILY_V4, we should get the same results as FAMILY_ALL + addrs, err = AddrList(link, FAMILY_V4) + if err != nil { + t.Fatal(err) + } + if len(addrs) != 1 { + t.Fatal("Address not added properly") + } + + // Pass a wrong family number, we should get nil list + addrs, err = AddrList(link, 0x8) + if err != nil { + t.Fatal(err) + } + + if len(addrs) != 0 { + t.Fatal("Address not expected") + } + + if err = AddrDel(link, tt.addr); err != nil { + t.Fatal(err) + } + + addrs, err = AddrList(link, FAMILY_ALL) + if err != nil { + t.Fatal(err) + } + + if len(addrs) != 0 { + t.Fatal("Address not removed properly") + } + } + +} + +func TestAddrAddReplace(t *testing.T) { + tearDown := setUpNetlinkTest(t) + defer tearDown() + + var address = &net.IPNet{IP: net.IPv4(127, 0, 0, 2), Mask: net.CIDRMask(24, 32)} + var addr = &Addr{IPNet: address} + + link, err := LinkByName("lo") + if err != nil { + t.Fatal(err) + } + + err = AddrAdd(link, addr) + if err != nil { + t.Fatal(err) + } + + addrs, err := AddrList(link, FAMILY_ALL) + if err != nil { + t.Fatal(err) + } + + if len(addrs) != 1 { + t.Fatal("Address not added properly") + } + + err = AddrAdd(link, addr) + if err == nil { + t.Fatal("Re-adding address should fail (but succeeded unexpectedly).") + } + + err = AddrReplace(link, addr) + if err != nil { + t.Fatal("Replacing address failed.") + } + + addrs, err = AddrList(link, FAMILY_ALL) + if err != nil { + t.Fatal(err) + } + + if len(addrs) != 1 { + t.Fatal("Address not added properly") + } + + if err = AddrDel(link, addr); err != nil { + t.Fatal(err) + } + + addrs, err = AddrList(link, FAMILY_ALL) + if err != nil { + t.Fatal(err) + } + + if len(addrs) != 0 { + t.Fatal("Address not removed properly") + } +} diff --git a/vendor/github.com/vishvananda/netlink/class_test.go b/vendor/github.com/vishvananda/netlink/class_test.go new file mode 100644 index 00000000..1d0c27b5 --- /dev/null +++ b/vendor/github.com/vishvananda/netlink/class_test.go @@ -0,0 +1,408 @@ +// +build linux + +package netlink + +import ( + "testing" +) + +func TestClassAddDel(t *testing.T) { + tearDown := setUpNetlinkTest(t) + defer tearDown() + if err := LinkAdd(&Ifb{LinkAttrs{Name: "foo"}}); err != nil { + t.Fatal(err) + } + if err := LinkAdd(&Ifb{LinkAttrs{Name: "bar"}}); err != nil { + t.Fatal(err) + } + link, err := LinkByName("foo") + if err != nil { + t.Fatal(err) + } + if err := LinkSetUp(link); err != nil { + t.Fatal(err) + } + attrs := QdiscAttrs{ + LinkIndex: link.Attrs().Index, + Handle: MakeHandle(0xffff, 0), + Parent: HANDLE_ROOT, + } + qdisc := NewHtb(attrs) + if err := QdiscAdd(qdisc); err != nil { + t.Fatal(err) + } + qdiscs, err := QdiscList(link) + if err != nil { + t.Fatal(err) + } + if len(qdiscs) != 1 { + t.Fatal("Failed to add qdisc") + } + _, ok := qdiscs[0].(*Htb) + if !ok { + t.Fatal("Qdisc is the wrong type") + } + + classattrs := ClassAttrs{ + LinkIndex: link.Attrs().Index, + Parent: MakeHandle(0xffff, 0), + Handle: MakeHandle(0xffff, 2), + } + + htbclassattrs := HtbClassAttrs{ + Rate: 1234000, + Cbuffer: 1690, + } + class := NewHtbClass(classattrs, htbclassattrs) + if err := ClassAdd(class); err != nil { + t.Fatal(err) + } + classes, err := ClassList(link, MakeHandle(0xffff, 0)) + if err != nil { + t.Fatal(err) + } + if len(classes) != 1 { + t.Fatal("Failed to add class") + } + + htb, ok := classes[0].(*HtbClass) + if !ok { + t.Fatal("Class is the wrong type") + } + if htb.Rate != class.Rate { + t.Fatal("Rate doesn't match") + } + if htb.Ceil != class.Ceil { + t.Fatal("Ceil doesn't match") + } + if htb.Buffer != class.Buffer { + t.Fatal("Buffer doesn't match") + } + if htb.Cbuffer != class.Cbuffer { + t.Fatal("Cbuffer doesn't match") + } + + qattrs := QdiscAttrs{ + LinkIndex: link.Attrs().Index, + Handle: MakeHandle(0x2, 0), + Parent: MakeHandle(0xffff, 2), + } + nattrs := NetemQdiscAttrs{ + Latency: 20000, + Loss: 23.4, + Duplicate: 14.3, + LossCorr: 8.34, + Jitter: 1000, + DelayCorr: 12.3, + ReorderProb: 23.4, + CorruptProb: 10.0, + CorruptCorr: 10, + } + qdiscnetem := NewNetem(qattrs, nattrs) + if err := QdiscAdd(qdiscnetem); err != nil { + t.Fatal(err) + } + + qdiscs, err = QdiscList(link) + if err != nil { + t.Fatal(err) + } + if len(qdiscs) != 2 { + t.Fatal("Failed to add qdisc") + } + _, ok = qdiscs[0].(*Htb) + if !ok { + t.Fatal("Qdisc is the wrong type") + } + + netem, ok := qdiscs[1].(*Netem) + if !ok { + t.Fatal("Qdisc is the wrong type") + } + // Compare the record we got from the list with the one we created + if netem.Loss != qdiscnetem.Loss { + t.Fatal("Loss does not match") + } + if netem.Latency != qdiscnetem.Latency { + t.Fatal("Latency does not match") + } + if netem.CorruptProb != qdiscnetem.CorruptProb { + t.Fatal("CorruptProb does not match") + } + if netem.Jitter != qdiscnetem.Jitter { + t.Fatal("Jitter does not match") + } + if netem.LossCorr != qdiscnetem.LossCorr { + t.Fatal("Loss does not match") + } + if netem.DuplicateCorr != qdiscnetem.DuplicateCorr { + t.Fatal("DuplicateCorr does not match") + } + + // Deletion + if err := ClassDel(class); err != nil { + t.Fatal(err) + } + classes, err = ClassList(link, MakeHandle(0xffff, 0)) + if err != nil { + t.Fatal(err) + } + if len(classes) != 0 { + t.Fatal("Failed to remove class") + } + if err := QdiscDel(qdisc); err != nil { + t.Fatal(err) + } + qdiscs, err = QdiscList(link) + if err != nil { + t.Fatal(err) + } + if len(qdiscs) != 0 { + t.Fatal("Failed to remove qdisc") + } +} + +func TestHtbClassAddHtbClassChangeDel(t *testing.T) { + /** + This test first set up a interface ans set up a Htb qdisc + A HTB class is attach to it and a Netem qdisc is attached to that class + Next, we test changing the HTB class in place and confirming the Netem is + still attached. We also check that invoting ClassChange with a non-existing + class will fail. + Finally, we test ClassReplace. We confirm it correctly behave like + ClassChange when the parent/handle pair exists and that it will create a + new class if the handle is modified. + */ + tearDown := setUpNetlinkTest(t) + defer tearDown() + if err := LinkAdd(&Ifb{LinkAttrs{Name: "foo"}}); err != nil { + t.Fatal(err) + } + link, err := LinkByName("foo") + if err != nil { + t.Fatal(err) + } + if err := LinkSetUp(link); err != nil { + t.Fatal(err) + } + attrs := QdiscAttrs{ + LinkIndex: link.Attrs().Index, + Handle: MakeHandle(0xffff, 0), + Parent: HANDLE_ROOT, + } + qdisc := NewHtb(attrs) + if err := QdiscAdd(qdisc); err != nil { + t.Fatal(err) + } + qdiscs, err := QdiscList(link) + if err != nil { + t.Fatal(err) + } + if len(qdiscs) != 1 { + t.Fatal("Failed to add qdisc") + } + _, ok := qdiscs[0].(*Htb) + if !ok { + t.Fatal("Qdisc is the wrong type") + } + + classattrs := ClassAttrs{ + LinkIndex: link.Attrs().Index, + Parent: MakeHandle(0xffff, 0), + Handle: MakeHandle(0xffff, 2), + } + + htbclassattrs := HtbClassAttrs{ + Rate: 1234000, + Cbuffer: 1690, + } + class := NewHtbClass(classattrs, htbclassattrs) + if err := ClassAdd(class); err != nil { + t.Fatal(err) + } + classes, err := ClassList(link, 0) + if err != nil { + t.Fatal(err) + } + if len(classes) != 1 { + t.Fatal("Failed to add class") + } + + htb, ok := classes[0].(*HtbClass) + if !ok { + t.Fatal("Class is the wrong type") + } + + qattrs := QdiscAttrs{ + LinkIndex: link.Attrs().Index, + Handle: MakeHandle(0x2, 0), + Parent: MakeHandle(0xffff, 2), + } + nattrs := NetemQdiscAttrs{ + Latency: 20000, + Loss: 23.4, + Duplicate: 14.3, + LossCorr: 8.34, + Jitter: 1000, + DelayCorr: 12.3, + ReorderProb: 23.4, + CorruptProb: 10.0, + CorruptCorr: 10, + } + qdiscnetem := NewNetem(qattrs, nattrs) + if err := QdiscAdd(qdiscnetem); err != nil { + t.Fatal(err) + } + + qdiscs, err = QdiscList(link) + if err != nil { + t.Fatal(err) + } + if len(qdiscs) != 2 { + t.Fatal("Failed to add qdisc") + } + + _, ok = qdiscs[1].(*Netem) + if !ok { + t.Fatal("Qdisc is the wrong type") + } + + // Change + // For change to work, the handle and parent cannot be changed. + + // First, test it fails if we change the Handle. + oldHandle := classattrs.Handle + classattrs.Handle = MakeHandle(0xffff, 3) + class = NewHtbClass(classattrs, htbclassattrs) + if err := ClassChange(class); err == nil { + t.Fatal("ClassChange should not work when using a different handle.") + } + // It should work with the same handle + classattrs.Handle = oldHandle + htbclassattrs.Rate = 4321000 + class = NewHtbClass(classattrs, htbclassattrs) + if err := ClassChange(class); err != nil { + t.Fatal(err) + } + + classes, err = ClassList(link, MakeHandle(0xffff, 0)) + if err != nil { + t.Fatal(err) + } + if len(classes) != 1 { + t.Fatalf( + "1 class expected, %d found", + len(classes), + ) + } + + htb, ok = classes[0].(*HtbClass) + if !ok { + t.Fatal("Class is the wrong type") + } + // Verify that the rate value has changed. + if htb.Rate != class.Rate { + t.Fatal("Rate did not get changed while changing the class.") + } + + // Check that we still have the netem child qdisc + qdiscs, err = QdiscList(link) + if err != nil { + t.Fatal(err) + } + + if len(qdiscs) != 2 { + t.Fatalf("2 qdisc expected, %d found", len(qdiscs)) + } + _, ok = qdiscs[0].(*Htb) + if !ok { + t.Fatal("Qdisc is the wrong type") + } + + _, ok = qdiscs[1].(*Netem) + if !ok { + t.Fatal("Qdisc is the wrong type") + } + + // Replace + // First replace by keeping the same handle, class will be changed. + // Then, replace by providing a new handle, n new class will be created. + + // Replace acting as Change + class = NewHtbClass(classattrs, htbclassattrs) + if err := ClassReplace(class); err != nil { + t.Fatal("Failed to replace class that is existing.") + } + + classes, err = ClassList(link, MakeHandle(0xffff, 0)) + if err != nil { + t.Fatal(err) + } + if len(classes) != 1 { + t.Fatalf( + "1 class expected, %d found", + len(classes), + ) + } + + htb, ok = classes[0].(*HtbClass) + if !ok { + t.Fatal("Class is the wrong type") + } + // Verify that the rate value has changed. + if htb.Rate != class.Rate { + t.Fatal("Rate did not get changed while changing the class.") + } + + // It should work with the same handle + classattrs.Handle = MakeHandle(0xffff, 3) + class = NewHtbClass(classattrs, htbclassattrs) + if err := ClassReplace(class); err != nil { + t.Fatal(err) + } + + classes, err = ClassList(link, MakeHandle(0xffff, 0)) + if err != nil { + t.Fatal(err) + } + if len(classes) != 2 { + t.Fatalf( + "2 classes expected, %d found", + len(classes), + ) + } + + htb, ok = classes[1].(*HtbClass) + if !ok { + t.Fatal("Class is the wrong type") + } + // Verify that the rate value has changed. + if htb.Rate != class.Rate { + t.Fatal("Rate did not get changed while changing the class.") + } + + // Deletion + for _, class := range classes { + if err := ClassDel(class); err != nil { + t.Fatal(err) + } + } + + classes, err = ClassList(link, MakeHandle(0xffff, 0)) + if err != nil { + t.Fatal(err) + } + if len(classes) != 0 { + t.Fatal("Failed to remove class") + } + if err := QdiscDel(qdisc); err != nil { + t.Fatal(err) + } + qdiscs, err = QdiscList(link) + if err != nil { + t.Fatal(err) + } + if len(qdiscs) != 0 { + t.Fatal("Failed to remove qdisc") + } +} diff --git a/vendor/github.com/vishvananda/netlink/conntrack_linux.go b/vendor/github.com/vishvananda/netlink/conntrack_linux.go new file mode 100644 index 00000000..20df9030 --- /dev/null +++ b/vendor/github.com/vishvananda/netlink/conntrack_linux.go @@ -0,0 +1,344 @@ +package netlink + +import ( + "bytes" + "encoding/binary" + "errors" + "fmt" + "net" + "syscall" + + "github.com/vishvananda/netlink/nl" +) + +// ConntrackTableType Conntrack table for the netlink operation +type ConntrackTableType uint8 + +const ( + // ConntrackTable Conntrack table + // https://github.com/torvalds/linux/blob/master/include/uapi/linux/netfilter/nfnetlink.h -> #define NFNL_SUBSYS_CTNETLINK 1 + ConntrackTable = 1 + // ConntrackExpectTable Conntrack expect table + // https://github.com/torvalds/linux/blob/master/include/uapi/linux/netfilter/nfnetlink.h -> #define NFNL_SUBSYS_CTNETLINK_EXP 2 + ConntrackExpectTable = 2 +) + +const ( + // backward compatibility with golang 1.6 which does not have io.SeekCurrent + seekCurrent = 1 +) + +// InetFamily Family type +type InetFamily uint8 + +// -L [table] [options] List conntrack or expectation table +// -G [table] parameters Get conntrack or expectation + +// -I [table] parameters Create a conntrack or expectation +// -U [table] parameters Update a conntrack +// -E [table] [options] Show events + +// -C [table] Show counter +// -S Show statistics + +// ConntrackTableList returns the flow list of a table of a specific family +// conntrack -L [table] [options] List conntrack or expectation table +func ConntrackTableList(table ConntrackTableType, family InetFamily) ([]*ConntrackFlow, error) { + return pkgHandle.ConntrackTableList(table, family) +} + +// ConntrackTableFlush flushes all the flows of a specified table +// conntrack -F [table] Flush table +// The flush operation applies to all the family types +func ConntrackTableFlush(table ConntrackTableType) error { + return pkgHandle.ConntrackTableFlush(table) +} + +// ConntrackDeleteFilter deletes entries on the specified table on the base of the filter +// conntrack -D [table] parameters Delete conntrack or expectation +func ConntrackDeleteFilter(table ConntrackTableType, family InetFamily, filter *ConntrackFilter) (uint, error) { + return pkgHandle.ConntrackDeleteFilter(table, family, filter) +} + +// ConntrackTableList returns the flow list of a table of a specific family using the netlink handle passed +// conntrack -L [table] [options] List conntrack or expectation table +func (h *Handle) ConntrackTableList(table ConntrackTableType, family InetFamily) ([]*ConntrackFlow, error) { + res, err := h.dumpConntrackTable(table, family) + if err != nil { + return nil, err + } + + // Deserialize all the flows + var result []*ConntrackFlow + for _, dataRaw := range res { + result = append(result, parseRawData(dataRaw)) + } + + return result, nil +} + +// ConntrackTableFlush flushes all the flows of a specified table using the netlink handle passed +// conntrack -F [table] Flush table +// The flush operation applies to all the family types +func (h *Handle) ConntrackTableFlush(table ConntrackTableType) error { + req := h.newConntrackRequest(table, syscall.AF_INET, nl.IPCTNL_MSG_CT_DELETE, syscall.NLM_F_ACK) + _, err := req.Execute(syscall.NETLINK_NETFILTER, 0) + return err +} + +// ConntrackDeleteFilter deletes entries on the specified table on the base of the filter using the netlink handle passed +// conntrack -D [table] parameters Delete conntrack or expectation +func (h *Handle) ConntrackDeleteFilter(table ConntrackTableType, family InetFamily, filter *ConntrackFilter) (uint, error) { + res, err := h.dumpConntrackTable(table, family) + if err != nil { + return 0, err + } + + var matched uint + for _, dataRaw := range res { + flow := parseRawData(dataRaw) + if match := filter.MatchConntrackFlow(flow); match { + req2 := h.newConntrackRequest(table, family, nl.IPCTNL_MSG_CT_DELETE, syscall.NLM_F_ACK) + // skip the first 4 byte that are the netfilter header, the newConntrackRequest is adding it already + req2.AddRawData(dataRaw[4:]) + req2.Execute(syscall.NETLINK_NETFILTER, 0) + matched++ + } + } + + return matched, nil +} + +func (h *Handle) newConntrackRequest(table ConntrackTableType, family InetFamily, operation, flags int) *nl.NetlinkRequest { + // Create the Netlink request object + req := h.newNetlinkRequest((int(table)<<8)|operation, flags) + // Add the netfilter header + msg := &nl.Nfgenmsg{ + NfgenFamily: uint8(family), + Version: nl.NFNETLINK_V0, + ResId: 0, + } + req.AddData(msg) + return req +} + +func (h *Handle) dumpConntrackTable(table ConntrackTableType, family InetFamily) ([][]byte, error) { + req := h.newConntrackRequest(table, family, nl.IPCTNL_MSG_CT_GET, syscall.NLM_F_DUMP) + return req.Execute(syscall.NETLINK_NETFILTER, 0) +} + +// The full conntrack flow structure is very complicated and can be found in the file: +// http://git.netfilter.org/libnetfilter_conntrack/tree/include/internal/object.h +// For the time being, the structure below allows to parse and extract the base information of a flow +type ipTuple struct { + SrcIP net.IP + DstIP net.IP + Protocol uint8 + SrcPort uint16 + DstPort uint16 +} + +type ConntrackFlow struct { + FamilyType uint8 + Forward ipTuple + Reverse ipTuple +} + +func (s *ConntrackFlow) String() string { + // conntrack cmd output: + // udp 17 src=127.0.0.1 dst=127.0.0.1 sport=4001 dport=1234 [UNREPLIED] src=127.0.0.1 dst=127.0.0.1 sport=1234 dport=4001 + return fmt.Sprintf("%s\t%d src=%s dst=%s sport=%d dport=%d\tsrc=%s dst=%s sport=%d dport=%d", + nl.L4ProtoMap[s.Forward.Protocol], s.Forward.Protocol, + s.Forward.SrcIP.String(), s.Forward.DstIP.String(), s.Forward.SrcPort, s.Forward.DstPort, + s.Reverse.SrcIP.String(), s.Reverse.DstIP.String(), s.Reverse.SrcPort, s.Reverse.DstPort) +} + +// This method parse the ip tuple structure +// The message structure is the following: +// +// +// +// +// +func parseIpTuple(reader *bytes.Reader, tpl *ipTuple) { + for i := 0; i < 2; i++ { + _, t, _, v := parseNfAttrTLV(reader) + switch t { + case nl.CTA_IP_V4_SRC, nl.CTA_IP_V6_SRC: + tpl.SrcIP = v + case nl.CTA_IP_V4_DST, nl.CTA_IP_V6_DST: + tpl.DstIP = v + } + } + // Skip the next 4 bytes nl.NLA_F_NESTED|nl.CTA_TUPLE_PROTO + reader.Seek(4, seekCurrent) + _, t, _, v := parseNfAttrTLV(reader) + if t == nl.CTA_PROTO_NUM { + tpl.Protocol = uint8(v[0]) + } + // Skip some padding 3 bytes + reader.Seek(3, seekCurrent) + for i := 0; i < 2; i++ { + _, t, _ := parseNfAttrTL(reader) + switch t { + case nl.CTA_PROTO_SRC_PORT: + parseBERaw16(reader, &tpl.SrcPort) + case nl.CTA_PROTO_DST_PORT: + parseBERaw16(reader, &tpl.DstPort) + } + // Skip some padding 2 byte + reader.Seek(2, seekCurrent) + } +} + +func parseNfAttrTLV(r *bytes.Reader) (isNested bool, attrType, len uint16, value []byte) { + isNested, attrType, len = parseNfAttrTL(r) + + value = make([]byte, len) + binary.Read(r, binary.BigEndian, &value) + return isNested, attrType, len, value +} + +func parseNfAttrTL(r *bytes.Reader) (isNested bool, attrType, len uint16) { + binary.Read(r, nl.NativeEndian(), &len) + len -= nl.SizeofNfattr + + binary.Read(r, nl.NativeEndian(), &attrType) + isNested = (attrType & nl.NLA_F_NESTED) == nl.NLA_F_NESTED + attrType = attrType & (nl.NLA_F_NESTED - 1) + + return isNested, attrType, len +} + +func parseBERaw16(r *bytes.Reader, v *uint16) { + binary.Read(r, binary.BigEndian, v) +} + +func parseRawData(data []byte) *ConntrackFlow { + s := &ConntrackFlow{} + // First there is the Nfgenmsg header + // consume only the family field + reader := bytes.NewReader(data) + binary.Read(reader, nl.NativeEndian(), &s.FamilyType) + + // skip rest of the Netfilter header + reader.Seek(3, seekCurrent) + // The message structure is the following: + // 4 bytes + // 4 bytes + // flow information of the forward flow + // 4 bytes + // 4 bytes + // flow information of the reverse flow + for reader.Len() > 0 { + nested, t, l := parseNfAttrTL(reader) + if nested && t == nl.CTA_TUPLE_ORIG { + if nested, t, _ = parseNfAttrTL(reader); nested && t == nl.CTA_TUPLE_IP { + parseIpTuple(reader, &s.Forward) + } + } else if nested && t == nl.CTA_TUPLE_REPLY { + if nested, t, _ = parseNfAttrTL(reader); nested && t == nl.CTA_TUPLE_IP { + parseIpTuple(reader, &s.Reverse) + + // Got all the useful information stop parsing + break + } else { + // Header not recognized skip it + reader.Seek(int64(l), seekCurrent) + } + } + } + + return s +} + +// Conntrack parameters and options: +// -n, --src-nat ip source NAT ip +// -g, --dst-nat ip destination NAT ip +// -j, --any-nat ip source or destination NAT ip +// -m, --mark mark Set mark +// -c, --secmark secmark Set selinux secmark +// -e, --event-mask eventmask Event mask, eg. NEW,DESTROY +// -z, --zero Zero counters while listing +// -o, --output type[,...] Output format, eg. xml +// -l, --label label[,...] conntrack labels + +// Common parameters and options: +// -s, --src, --orig-src ip Source address from original direction +// -d, --dst, --orig-dst ip Destination address from original direction +// -r, --reply-src ip Source addres from reply direction +// -q, --reply-dst ip Destination address from reply direction +// -p, --protonum proto Layer 4 Protocol, eg. 'tcp' +// -f, --family proto Layer 3 Protocol, eg. 'ipv6' +// -t, --timeout timeout Set timeout +// -u, --status status Set status, eg. ASSURED +// -w, --zone value Set conntrack zone +// --orig-zone value Set zone for original direction +// --reply-zone value Set zone for reply direction +// -b, --buffer-size Netlink socket buffer size +// --mask-src ip Source mask address +// --mask-dst ip Destination mask address + +// Filter types +type ConntrackFilterType uint8 + +const ( + ConntrackOrigSrcIP = iota // -orig-src ip Source address from original direction + ConntrackOrigDstIP // -orig-dst ip Destination address from original direction + ConntrackNatSrcIP // -src-nat ip Source NAT ip + ConntrackNatDstIP // -dst-nat ip Destination NAT ip + ConntrackNatAnyIP // -any-nat ip Source or destination NAT ip +) + +type ConntrackFilter struct { + ipFilter map[ConntrackFilterType]net.IP +} + +// AddIP adds an IP to the conntrack filter +func (f *ConntrackFilter) AddIP(tp ConntrackFilterType, ip net.IP) error { + if f.ipFilter == nil { + f.ipFilter = make(map[ConntrackFilterType]net.IP) + } + if _, ok := f.ipFilter[tp]; ok { + return errors.New("Filter attribute already present") + } + f.ipFilter[tp] = ip + return nil +} + +// MatchConntrackFlow applies the filter to the flow and returns true if the flow matches the filter +// false otherwise +func (f *ConntrackFilter) MatchConntrackFlow(flow *ConntrackFlow) bool { + if len(f.ipFilter) == 0 { + // empty filter always not match + return false + } + + match := true + // -orig-src ip Source address from original direction + if elem, found := f.ipFilter[ConntrackOrigSrcIP]; found { + match = match && elem.Equal(flow.Forward.SrcIP) + } + + // -orig-dst ip Destination address from original direction + if elem, found := f.ipFilter[ConntrackOrigDstIP]; match && found { + match = match && elem.Equal(flow.Forward.DstIP) + } + + // -src-nat ip Source NAT ip + if elem, found := f.ipFilter[ConntrackNatSrcIP]; match && found { + match = match && elem.Equal(flow.Reverse.SrcIP) + } + + // -dst-nat ip Destination NAT ip + if elem, found := f.ipFilter[ConntrackNatDstIP]; match && found { + match = match && elem.Equal(flow.Reverse.DstIP) + } + + // -any-nat ip Source or destination NAT ip + if elem, found := f.ipFilter[ConntrackNatAnyIP]; match && found { + match = match && (elem.Equal(flow.Reverse.SrcIP) || elem.Equal(flow.Reverse.DstIP)) + } + + return match +} diff --git a/vendor/github.com/vishvananda/netlink/conntrack_test.go b/vendor/github.com/vishvananda/netlink/conntrack_test.go new file mode 100644 index 00000000..00b80606 --- /dev/null +++ b/vendor/github.com/vishvananda/netlink/conntrack_test.go @@ -0,0 +1,387 @@ +package netlink + +import ( + "fmt" + "net" + "runtime" + "syscall" + "testing" + + "github.com/vishvananda/netns" +) + +func CheckErrorFail(t *testing.T, err error) { + if err != nil { + t.Fatalf("Fatal Error: %s", err) + } +} +func CheckError(t *testing.T, err error) { + if err != nil { + t.Errorf("Error: %s", err) + } +} + +func udpFlowCreateProg(t *testing.T, flows, srcPort int, dstIP string, dstPort int) { + for i := 0; i < flows; i++ { + ServerAddr, err := net.ResolveUDPAddr("udp", fmt.Sprintf("%s:%d", dstIP, dstPort)) + CheckError(t, err) + + LocalAddr, err := net.ResolveUDPAddr("udp", fmt.Sprintf("127.0.0.1:%d", srcPort+i)) + CheckError(t, err) + + Conn, err := net.DialUDP("udp", LocalAddr, ServerAddr) + CheckError(t, err) + + Conn.Write([]byte("Hello World")) + Conn.Close() + } +} + +func nsCreateAndEnter(t *testing.T) (*netns.NsHandle, *netns.NsHandle, *Handle) { + // Lock the OS Thread so we don't accidentally switch namespaces + runtime.LockOSThread() + + // Save the current network namespace + origns, _ := netns.Get() + + ns, err := netns.New() + CheckErrorFail(t, err) + + h, err := NewHandleAt(ns) + CheckErrorFail(t, err) + + // Enter the new namespace + netns.Set(ns) + + // Bing up loopback + link, _ := h.LinkByName("lo") + h.LinkSetUp(link) + + return &origns, &ns, h +} + +func applyFilter(flowList []ConntrackFlow, ipv4Filter *ConntrackFilter, ipv6Filter *ConntrackFilter) (ipv4Match, ipv6Match uint) { + for _, flow := range flowList { + if ipv4Filter.MatchConntrackFlow(&flow) == true { + ipv4Match++ + } + if ipv6Filter.MatchConntrackFlow(&flow) == true { + ipv6Match++ + } + } + return ipv4Match, ipv6Match +} + +// TestConntrackSocket test the opening of a NETFILTER family socket +func TestConntrackSocket(t *testing.T) { + skipUnlessRoot(t) + + h, err := NewHandle(syscall.NETLINK_NETFILTER) + CheckErrorFail(t, err) + + if h.SupportsNetlinkFamily(syscall.NETLINK_NETFILTER) != true { + t.Fatal("ERROR not supporting the NETFILTER family") + } +} + +// TestConntrackTableList test the conntrack table list +// Creates some flows and checks that they are correctly fetched from the conntrack table +func TestConntrackTableList(t *testing.T) { + skipUnlessRoot(t) + + // Creates a new namespace and bring up the loopback interface + origns, ns, h := nsCreateAndEnter(t) + defer netns.Set(*origns) + defer origns.Close() + defer ns.Close() + defer runtime.UnlockOSThread() + + // Flush the table to start fresh + err := h.ConntrackTableFlush(ConntrackTable) + CheckErrorFail(t, err) + + // Create 5 udp + udpFlowCreateProg(t, 5, 2000, "127.0.0.10", 3000) + + // Fetch the conntrack table + flows, err := h.ConntrackTableList(ConntrackTable, syscall.AF_INET) + CheckErrorFail(t, err) + + // Check that it is able to find the 5 flows created + var found int + for _, flow := range flows { + if flow.Forward.Protocol == 17 && + flow.Forward.DstIP.Equal(net.ParseIP("127.0.0.10")) && + flow.Forward.DstPort == 3000 && + (flow.Forward.SrcPort >= 2000 && flow.Forward.SrcPort <= 2005) { + found++ + } + } + if found != 5 { + t.Fatalf("Found only %d flows over 5", found) + } + + // Give a try also to the IPv6 version + _, err = h.ConntrackTableList(ConntrackTable, syscall.AF_INET6) + CheckErrorFail(t, err) + + // Switch back to the original namespace + netns.Set(*origns) +} + +// TestConntrackTableFlush test the conntrack table flushing +// Creates some flows and then call the table flush +func TestConntrackTableFlush(t *testing.T) { + skipUnlessRoot(t) + + // Creates a new namespace and bring up the loopback interface + origns, ns, h := nsCreateAndEnter(t) + defer netns.Set(*origns) + defer origns.Close() + defer ns.Close() + defer runtime.UnlockOSThread() + + // Create 5 udp flows using netcat + udpFlowCreateProg(t, 5, 3000, "127.0.0.10", 4000) + + // Fetch the conntrack table + flows, err := h.ConntrackTableList(ConntrackTable, syscall.AF_INET) + CheckErrorFail(t, err) + + // Check that it is able to find the 5 flows created + var found int + for _, flow := range flows { + if flow.Forward.Protocol == 17 && + flow.Forward.DstIP.Equal(net.ParseIP("127.0.0.10")) && + flow.Forward.DstPort == 4000 && + (flow.Forward.SrcPort >= 3000 && flow.Forward.SrcPort <= 3005) { + found++ + } + } + if found != 5 { + t.Fatalf("Found only %d flows over 5", found) + } + + // Flush the table + err = h.ConntrackTableFlush(ConntrackTable) + CheckErrorFail(t, err) + + // Fetch again the flows to validate the flush + flows, err = h.ConntrackTableList(ConntrackTable, syscall.AF_INET) + CheckErrorFail(t, err) + + // Check if it is still able to find the 5 flows created + found = 0 + for _, flow := range flows { + if flow.Forward.Protocol == 17 && + flow.Forward.DstIP.Equal(net.ParseIP("127.0.0.10")) && + flow.Forward.DstPort == 4000 && + (flow.Forward.SrcPort >= 3000 && flow.Forward.SrcPort <= 3005) { + found++ + } + } + if found > 0 { + t.Fatalf("Found %d flows, they should had been flushed", found) + } + + // Switch back to the original namespace + netns.Set(*origns) +} + +// TestConntrackTableDelete tests the deletion with filter +// Creates 2 group of flows then deletes only one group and validates the result +func TestConntrackTableDelete(t *testing.T) { + skipUnlessRoot(t) + + // Creates a new namespace and bring up the loopback interface + origns, ns, h := nsCreateAndEnter(t) + defer netns.Set(*origns) + defer origns.Close() + defer ns.Close() + defer runtime.UnlockOSThread() + + // Create 10 udp flows + udpFlowCreateProg(t, 5, 5000, "127.0.0.10", 6000) + udpFlowCreateProg(t, 5, 7000, "127.0.0.20", 8000) + + // Fetch the conntrack table + flows, err := h.ConntrackTableList(ConntrackTable, syscall.AF_INET) + CheckErrorFail(t, err) + + // Check that it is able to find the 5 flows created for each group + var groupA int + var groupB int + for _, flow := range flows { + if flow.Forward.Protocol == 17 && + flow.Forward.DstIP.Equal(net.ParseIP("127.0.0.10")) && + flow.Forward.DstPort == 6000 && + (flow.Forward.SrcPort >= 5000 && flow.Forward.SrcPort <= 5005) { + groupA++ + } + if flow.Forward.Protocol == 17 && + flow.Forward.DstIP.Equal(net.ParseIP("127.0.0.20")) && + flow.Forward.DstPort == 8000 && + (flow.Forward.SrcPort >= 7000 && flow.Forward.SrcPort <= 7005) { + groupB++ + } + } + if groupA != 5 || groupB != 5 { + t.Fatalf("Flow creation issue groupA:%d, groupB:%d", groupA, groupB) + } + + // Create a filter to erase groupB flows + filter := &ConntrackFilter{} + filter.AddIP(ConntrackOrigDstIP, net.ParseIP("127.0.0.20")) + + // Flush entries of groupB + var deleted uint + if deleted, err = h.ConntrackDeleteFilter(ConntrackTable, syscall.AF_INET, filter); err != nil { + t.Fatalf("Error during the erase: %s", err) + } + if deleted != 5 { + t.Fatalf("Error deleted a wrong number of flows:%d instead of 5", deleted) + } + + // Check again the table to verify that are gone + flows, err = h.ConntrackTableList(ConntrackTable, syscall.AF_INET) + CheckErrorFail(t, err) + + // Check if it is able to find the 5 flows of groupA but none of groupB + groupA = 0 + groupB = 0 + for _, flow := range flows { + if flow.Forward.Protocol == 17 && + flow.Forward.DstIP.Equal(net.ParseIP("127.0.0.10")) && + flow.Forward.DstPort == 6000 && + (flow.Forward.SrcPort >= 5000 && flow.Forward.SrcPort <= 5005) { + groupA++ + } + if flow.Forward.Protocol == 17 && + flow.Forward.DstIP.Equal(net.ParseIP("127.0.0.20")) && + flow.Forward.DstPort == 8000 && + (flow.Forward.SrcPort >= 7000 && flow.Forward.SrcPort <= 7005) { + groupB++ + } + } + if groupA != 5 || groupB > 0 { + t.Fatalf("Error during the erase groupA:%d, groupB:%d", groupA, groupB) + } + + // Switch back to the original namespace + netns.Set(*origns) +} + +func TestConntrackFilter(t *testing.T) { + var flowList []ConntrackFlow + flowList = append(flowList, ConntrackFlow{ + FamilyType: syscall.AF_INET, + Forward: ipTuple{ + SrcIP: net.ParseIP("10.0.0.1"), + DstIP: net.ParseIP("20.0.0.1"), + SrcPort: 1000, + DstPort: 2000, + }, + Reverse: ipTuple{ + SrcIP: net.ParseIP("20.0.0.1"), + DstIP: net.ParseIP("192.168.1.1"), + SrcPort: 2000, + DstPort: 1000, + }, + }, + ConntrackFlow{ + FamilyType: syscall.AF_INET, + Forward: ipTuple{ + SrcIP: net.ParseIP("10.0.0.2"), + DstIP: net.ParseIP("20.0.0.2"), + SrcPort: 5000, + DstPort: 6000, + }, + Reverse: ipTuple{ + SrcIP: net.ParseIP("20.0.0.2"), + DstIP: net.ParseIP("192.168.1.1"), + SrcPort: 6000, + DstPort: 5000, + }, + }, + ConntrackFlow{ + FamilyType: syscall.AF_INET6, + Forward: ipTuple{ + SrcIP: net.ParseIP("eeee:eeee:eeee:eeee:eeee:eeee:eeee:eeee"), + DstIP: net.ParseIP("dddd:dddd:dddd:dddd:dddd:dddd:dddd:dddd"), + SrcPort: 1000, + DstPort: 2000, + }, + Reverse: ipTuple{ + SrcIP: net.ParseIP("dddd:dddd:dddd:dddd:dddd:dddd:dddd:dddd"), + DstIP: net.ParseIP("eeee:eeee:eeee:eeee:eeee:eeee:eeee:eeee"), + SrcPort: 2000, + DstPort: 1000, + }, + }) + + // Empty filter + v4Match, v6Match := applyFilter(flowList, &ConntrackFilter{}, &ConntrackFilter{}) + if v4Match > 0 || v6Match > 0 { + t.Fatalf("Error, empty filter cannot match, v4:%d, v6:%d", v4Match, v6Match) + } + + // SrcIP filter + filterV4 := &ConntrackFilter{} + filterV4.AddIP(ConntrackOrigSrcIP, net.ParseIP("10.0.0.1")) + + filterV6 := &ConntrackFilter{} + filterV6.AddIP(ConntrackOrigSrcIP, net.ParseIP("eeee:eeee:eeee:eeee:eeee:eeee:eeee:eeee")) + + v4Match, v6Match = applyFilter(flowList, filterV4, filterV6) + if v4Match != 1 || v6Match != 1 { + t.Fatalf("Error, there should be only 1 match, v4:%d, v6:%d", v4Match, v6Match) + } + + // DstIp filter + filterV4 = &ConntrackFilter{} + filterV4.AddIP(ConntrackOrigDstIP, net.ParseIP("20.0.0.1")) + + filterV6 = &ConntrackFilter{} + filterV6.AddIP(ConntrackOrigDstIP, net.ParseIP("dddd:dddd:dddd:dddd:dddd:dddd:dddd:dddd")) + + v4Match, v6Match = applyFilter(flowList, filterV4, filterV6) + if v4Match != 1 || v6Match != 1 { + t.Fatalf("Error, there should be only 1 match, v4:%d, v6:%d", v4Match, v6Match) + } + + // SrcIP for NAT + filterV4 = &ConntrackFilter{} + filterV4.AddIP(ConntrackNatSrcIP, net.ParseIP("20.0.0.1")) + + filterV6 = &ConntrackFilter{} + filterV6.AddIP(ConntrackNatSrcIP, net.ParseIP("dddd:dddd:dddd:dddd:dddd:dddd:dddd:dddd")) + + v4Match, v6Match = applyFilter(flowList, filterV4, filterV6) + if v4Match != 1 || v6Match != 1 { + t.Fatalf("Error, there should be only 1 match, v4:%d, v6:%d", v4Match, v6Match) + } + + // DstIP for NAT + filterV4 = &ConntrackFilter{} + filterV4.AddIP(ConntrackNatDstIP, net.ParseIP("192.168.1.1")) + + filterV6 = &ConntrackFilter{} + filterV6.AddIP(ConntrackNatDstIP, net.ParseIP("dddd:dddd:dddd:dddd:dddd:dddd:dddd:dddd")) + + v4Match, v6Match = applyFilter(flowList, filterV4, filterV6) + if v4Match != 2 || v6Match != 0 { + t.Fatalf("Error, there should be an exact match, v4:%d, v6:%d", v4Match, v6Match) + } + + // AnyIp for Nat + filterV4 = &ConntrackFilter{} + filterV4.AddIP(ConntrackNatAnyIP, net.ParseIP("192.168.1.1")) + + filterV6 = &ConntrackFilter{} + filterV6.AddIP(ConntrackNatAnyIP, net.ParseIP("eeee:eeee:eeee:eeee:eeee:eeee:eeee:eeee")) + + v4Match, v6Match = applyFilter(flowList, filterV4, filterV6) + if v4Match != 2 || v6Match != 1 { + t.Fatalf("Error, there should be an exact match, v4:%d, v6:%d", v4Match, v6Match) + } +} diff --git a/vendor/github.com/vishvananda/netlink/conntrack_unspecified.go b/vendor/github.com/vishvananda/netlink/conntrack_unspecified.go new file mode 100644 index 00000000..af7af799 --- /dev/null +++ b/vendor/github.com/vishvananda/netlink/conntrack_unspecified.go @@ -0,0 +1,53 @@ +// +build !linux + +package netlink + +// ConntrackTableType Conntrack table for the netlink operation +type ConntrackTableType uint8 + +// InetFamily Family type +type InetFamily uint8 + +// ConntrackFlow placeholder +type ConntrackFlow struct{} + +// ConntrackFilter placeholder +type ConntrackFilter struct{} + +// ConntrackTableList returns the flow list of a table of a specific family +// conntrack -L [table] [options] List conntrack or expectation table +func ConntrackTableList(table ConntrackTableType, family InetFamily) ([]*ConntrackFlow, error) { + return nil, ErrNotImplemented +} + +// ConntrackTableFlush flushes all the flows of a specified table +// conntrack -F [table] Flush table +// The flush operation applies to all the family types +func ConntrackTableFlush(table ConntrackTableType) error { + return ErrNotImplemented +} + +// ConntrackDeleteFilter deletes entries on the specified table on the base of the filter +// conntrack -D [table] parameters Delete conntrack or expectation +func ConntrackDeleteFilter(table ConntrackTableType, family InetFamily, filter *ConntrackFilter) (uint, error) { + return 0, ErrNotImplemented +} + +// ConntrackTableList returns the flow list of a table of a specific family using the netlink handle passed +// conntrack -L [table] [options] List conntrack or expectation table +func (h *Handle) ConntrackTableList(table ConntrackTableType, family InetFamily) ([]*ConntrackFlow, error) { + return nil, ErrNotImplemented +} + +// ConntrackTableFlush flushes all the flows of a specified table using the netlink handle passed +// conntrack -F [table] Flush table +// The flush operation applies to all the family types +func (h *Handle) ConntrackTableFlush(table ConntrackTableType) error { + return ErrNotImplemented +} + +// ConntrackDeleteFilter deletes entries on the specified table on the base of the filter using the netlink handle passed +// conntrack -D [table] parameters Delete conntrack or expectation +func (h *Handle) ConntrackDeleteFilter(table ConntrackTableType, family InetFamily, filter *ConntrackFilter) (uint, error) { + return 0, ErrNotImplemented +} diff --git a/vendor/github.com/vishvananda/netlink/filter.go b/vendor/github.com/vishvananda/netlink/filter.go index bc8a1e96..938b28b0 100644 --- a/vendor/github.com/vishvananda/netlink/filter.go +++ b/vendor/github.com/vishvananda/netlink/filter.go @@ -1,6 +1,10 @@ package netlink -import "fmt" +import ( + "fmt" + + "github.com/vishvananda/netlink/nl" +) type Filter interface { Attrs() *FilterAttrs @@ -180,11 +184,46 @@ func NewMirredAction(redirIndex int) *MirredAction { } } +// Constants used in TcU32Sel.Flags. +const ( + TC_U32_TERMINAL = nl.TC_U32_TERMINAL + TC_U32_OFFSET = nl.TC_U32_OFFSET + TC_U32_VAROFFSET = nl.TC_U32_VAROFFSET + TC_U32_EAT = nl.TC_U32_EAT +) + +// Sel of the U32 filters that contains multiple TcU32Key. This is the copy +// and the frontend representation of nl.TcU32Sel. It is serialized into canonical +// nl.TcU32Sel with the appropriate endianness. +type TcU32Sel struct { + Flags uint8 + Offshift uint8 + Nkeys uint8 + Pad uint8 + Offmask uint16 + Off uint16 + Offoff int16 + Hoff int16 + Hmask uint32 + Keys []TcU32Key +} + +// TcU32Key contained of Sel in the U32 filters. This is the copy and the frontend +// representation of nl.TcU32Key. It is serialized into chanonical nl.TcU32Sel +// with the appropriate endianness. +type TcU32Key struct { + Mask uint32 + Val uint32 + Off int32 + OffMask int32 +} + // U32 filters on many packet related properties type U32 struct { FilterAttrs ClassId uint32 RedirIndex int + Sel *TcU32Sel Actions []Action } diff --git a/vendor/github.com/vishvananda/netlink/filter_linux.go b/vendor/github.com/vishvananda/netlink/filter_linux.go index d9aedca7..a0e000ca 100644 --- a/vendor/github.com/vishvananda/netlink/filter_linux.go +++ b/vendor/github.com/vishvananda/netlink/filter_linux.go @@ -6,6 +6,7 @@ import ( "errors" "fmt" "syscall" + "unsafe" "github.com/vishvananda/netlink/nl" ) @@ -128,12 +129,34 @@ func (h *Handle) FilterAdd(filter Filter) error { options := nl.NewRtAttr(nl.TCA_OPTIONS, nil) if u32, ok := filter.(*U32); ok { - // match all - sel := nl.TcU32Sel{ - Nkeys: 1, - Flags: nl.TC_U32_TERMINAL, + // Convert TcU32Sel into nl.TcU32Sel as it is without copy. + sel := (*nl.TcU32Sel)(unsafe.Pointer(u32.Sel)) + if sel == nil { + // match all + sel = &nl.TcU32Sel{ + Nkeys: 1, + Flags: nl.TC_U32_TERMINAL, + } + sel.Keys = append(sel.Keys, nl.TcU32Key{}) } - sel.Keys = append(sel.Keys, nl.TcU32Key{}) + + if native != networkOrder { + // Copy Tcu32Sel. + cSel := sel + keys := make([]nl.TcU32Key, cap(sel.Keys)) + copy(keys, sel.Keys) + cSel.Keys = keys + sel = cSel + + // Handle the endianness of attributes + sel.Offmask = native.Uint16(htons(sel.Offmask)) + sel.Hmask = native.Uint32(htonl(sel.Hmask)) + for _, key := range sel.Keys { + key.Mask = native.Uint32(htonl(key.Mask)) + key.Val = native.Uint32(htonl(key.Val)) + } + } + sel.Nkeys = uint8(len(sel.Keys)) nl.NewRtAttrChild(options, nl.TCA_U32_SEL, sel.Serialize()) if u32.ClassId != 0 { nl.NewRtAttrChild(options, nl.TCA_U32_CLASSID, nl.Uint32Attr(u32.ClassId)) @@ -425,9 +448,15 @@ func parseU32Data(filter Filter, data []syscall.NetlinkRouteAttr) (bool, error) case nl.TCA_U32_SEL: detailed = true sel := nl.DeserializeTcU32Sel(datum.Value) - // only parse if we have a very basic redirect - if sel.Flags&nl.TC_U32_TERMINAL == 0 || sel.Nkeys != 1 { - return detailed, nil + u32.Sel = (*TcU32Sel)(unsafe.Pointer(sel)) + if native != networkOrder { + // Handle the endianness of attributes + u32.Sel.Offmask = native.Uint16(htons(sel.Offmask)) + u32.Sel.Hmask = native.Uint32(htonl(sel.Hmask)) + for _, key := range u32.Sel.Keys { + key.Mask = native.Uint32(htonl(key.Mask)) + key.Val = native.Uint32(htonl(key.Val)) + } } case nl.TCA_U32_ACT: tables, err := nl.ParseRouteAttr(datum.Value) @@ -443,6 +472,8 @@ func parseU32Data(filter Filter, data []syscall.NetlinkRouteAttr) (bool, error) u32.RedirIndex = int(action.Ifindex) } } + case nl.TCA_U32_CLASSID: + u32.ClassId = native.Uint32(datum.Value) } } return detailed, nil diff --git a/vendor/github.com/vishvananda/netlink/filter_test.go b/vendor/github.com/vishvananda/netlink/filter_test.go new file mode 100644 index 00000000..64ab757f --- /dev/null +++ b/vendor/github.com/vishvananda/netlink/filter_test.go @@ -0,0 +1,644 @@ +// +build linux + +package netlink + +import ( + "syscall" + "testing" +) + +func TestFilterAddDel(t *testing.T) { + tearDown := setUpNetlinkTest(t) + defer tearDown() + if err := LinkAdd(&Ifb{LinkAttrs{Name: "foo"}}); err != nil { + t.Fatal(err) + } + if err := LinkAdd(&Ifb{LinkAttrs{Name: "bar"}}); err != nil { + t.Fatal(err) + } + link, err := LinkByName("foo") + if err != nil { + t.Fatal(err) + } + if err := LinkSetUp(link); err != nil { + t.Fatal(err) + } + redir, err := LinkByName("bar") + if err != nil { + t.Fatal(err) + } + if err := LinkSetUp(redir); err != nil { + t.Fatal(err) + } + qdisc := &Ingress{ + QdiscAttrs: QdiscAttrs{ + LinkIndex: link.Attrs().Index, + Handle: MakeHandle(0xffff, 0), + Parent: HANDLE_INGRESS, + }, + } + if err := QdiscAdd(qdisc); err != nil { + t.Fatal(err) + } + qdiscs, err := QdiscList(link) + if err != nil { + t.Fatal(err) + } + if len(qdiscs) != 1 { + t.Fatal("Failed to add qdisc") + } + _, ok := qdiscs[0].(*Ingress) + if !ok { + t.Fatal("Qdisc is the wrong type") + } + classId := MakeHandle(1, 1) + filter := &U32{ + FilterAttrs: FilterAttrs{ + LinkIndex: link.Attrs().Index, + Parent: MakeHandle(0xffff, 0), + Priority: 1, + Protocol: syscall.ETH_P_IP, + }, + RedirIndex: redir.Attrs().Index, + ClassId: classId, + } + if err := FilterAdd(filter); err != nil { + t.Fatal(err) + } + filters, err := FilterList(link, MakeHandle(0xffff, 0)) + if err != nil { + t.Fatal(err) + } + if len(filters) != 1 { + t.Fatal("Failed to add filter") + } + u32, ok := filters[0].(*U32) + if !ok { + t.Fatal("Filter is the wrong type") + } + if u32.ClassId != classId { + t.Fatalf("ClassId of the filter is the wrong value") + } + if err := FilterDel(filter); err != nil { + t.Fatal(err) + } + filters, err = FilterList(link, MakeHandle(0xffff, 0)) + if err != nil { + t.Fatal(err) + } + if len(filters) != 0 { + t.Fatal("Failed to remove filter") + } + if err := QdiscDel(qdisc); err != nil { + t.Fatal(err) + } + qdiscs, err = QdiscList(link) + if err != nil { + t.Fatal(err) + } + if len(qdiscs) != 0 { + t.Fatal("Failed to remove qdisc") + } +} + +func TestAdvancedFilterAddDel(t *testing.T) { + tearDown := setUpNetlinkTest(t) + defer tearDown() + if err := LinkAdd(&Ifb{LinkAttrs{Name: "baz"}}); err != nil { + t.Fatal(err) + } + link, err := LinkByName("baz") + if err != nil { + t.Fatal(err) + } + if err := LinkSetUp(link); err != nil { + t.Fatal(err) + } + index := link.Attrs().Index + + qdiscHandle := MakeHandle(0x1, 0x0) + qdiscAttrs := QdiscAttrs{ + LinkIndex: index, + Handle: qdiscHandle, + Parent: HANDLE_ROOT, + } + + qdisc := NewHtb(qdiscAttrs) + if err := QdiscAdd(qdisc); err != nil { + t.Fatal(err) + } + qdiscs, err := QdiscList(link) + if err != nil { + t.Fatal(err) + } + if len(qdiscs) != 1 { + t.Fatal("Failed to add qdisc") + } + _, ok := qdiscs[0].(*Htb) + if !ok { + t.Fatal("Qdisc is the wrong type") + } + + classId := MakeHandle(0x1, 0x46cb) + classAttrs := ClassAttrs{ + LinkIndex: index, + Parent: qdiscHandle, + Handle: classId, + } + htbClassAttrs := HtbClassAttrs{ + Rate: 512 * 1024, + Buffer: 32 * 1024, + } + htbClass := NewHtbClass(classAttrs, htbClassAttrs) + if err = ClassReplace(htbClass); err != nil { + t.Fatalf("Failed to add a HTB class: %v", err) + } + classes, err := ClassList(link, qdiscHandle) + if err != nil { + t.Fatal(err) + } + if len(classes) != 1 { + t.Fatal("Failed to add class") + } + _, ok = classes[0].(*HtbClass) + if !ok { + t.Fatal("Class is the wrong type") + } + + u32SelKeys := []TcU32Key{ + TcU32Key{ + Mask: 0xff, + Val: 80, + Off: 20, + OffMask: 0, + }, + TcU32Key{ + Mask: 0xffff, + Val: 0x146ca, + Off: 32, + OffMask: 0, + }, + } + filter := &U32{ + FilterAttrs: FilterAttrs{ + LinkIndex: index, + Parent: qdiscHandle, + Priority: 1, + Protocol: syscall.ETH_P_ALL, + }, + Sel: &TcU32Sel{ + Keys: u32SelKeys, + Flags: TC_U32_TERMINAL, + }, + ClassId: classId, + Actions: []Action{}, + } + if err := FilterAdd(filter); err != nil { + t.Fatal(err) + } + filters, err := FilterList(link, qdiscHandle) + if err != nil { + t.Fatal(err) + } + if len(filters) != 1 { + t.Fatal("Failed to add filter") + } + + u32, ok := filters[0].(*U32) + if !ok { + t.Fatal("Filter is the wrong type") + } + // Endianness checks + if u32.Sel.Offmask != filter.Sel.Offmask { + t.Fatal("The endianness of TcU32Key.Sel.Offmask is wrong") + } + if u32.Sel.Hmask != filter.Sel.Hmask { + t.Fatal("The endianness of TcU32Key.Sel.Hmask is wrong") + } + for i, key := range u32.Sel.Keys { + if key.Mask != filter.Sel.Keys[i].Mask { + t.Fatal("The endianness of TcU32Key.Mask is wrong") + } + if key.Val != filter.Sel.Keys[i].Val { + t.Fatal("The endianness of TcU32Key.Val is wrong") + } + } + + if err := FilterDel(filter); err != nil { + t.Fatal(err) + } + filters, err = FilterList(link, qdiscHandle) + if err != nil { + t.Fatal(err) + } + if len(filters) != 0 { + t.Fatal("Failed to remove filter") + } + + if err = ClassDel(htbClass); err != nil { + t.Fatalf("Failed to delete a HTP class: %v", err) + } + classes, err = ClassList(link, qdiscHandle) + if err != nil { + t.Fatal(err) + } + if len(classes) != 0 { + t.Fatal("Failed to remove class") + } + + if err := QdiscDel(qdisc); err != nil { + t.Fatal(err) + } + qdiscs, err = QdiscList(link) + if err != nil { + t.Fatal(err) + } + if len(qdiscs) != 0 { + t.Fatal("Failed to remove qdisc") + } +} + +func TestFilterFwAddDel(t *testing.T) { + tearDown := setUpNetlinkTest(t) + defer tearDown() + if err := LinkAdd(&Ifb{LinkAttrs{Name: "foo"}}); err != nil { + t.Fatal(err) + } + if err := LinkAdd(&Ifb{LinkAttrs{Name: "bar"}}); err != nil { + t.Fatal(err) + } + link, err := LinkByName("foo") + if err != nil { + t.Fatal(err) + } + if err := LinkSetUp(link); err != nil { + t.Fatal(err) + } + redir, err := LinkByName("bar") + if err != nil { + t.Fatal(err) + } + if err := LinkSetUp(redir); err != nil { + t.Fatal(err) + } + attrs := QdiscAttrs{ + LinkIndex: link.Attrs().Index, + Handle: MakeHandle(0xffff, 0), + Parent: HANDLE_ROOT, + } + qdisc := NewHtb(attrs) + if err := QdiscAdd(qdisc); err != nil { + t.Fatal(err) + } + qdiscs, err := QdiscList(link) + if err != nil { + t.Fatal(err) + } + if len(qdiscs) != 1 { + t.Fatal("Failed to add qdisc") + } + _, ok := qdiscs[0].(*Htb) + if !ok { + t.Fatal("Qdisc is the wrong type") + } + + classattrs := ClassAttrs{ + LinkIndex: link.Attrs().Index, + Parent: MakeHandle(0xffff, 0), + Handle: MakeHandle(0xffff, 2), + } + + htbclassattrs := HtbClassAttrs{ + Rate: 1234000, + Cbuffer: 1690, + } + class := NewHtbClass(classattrs, htbclassattrs) + if err := ClassAdd(class); err != nil { + t.Fatal(err) + } + classes, err := ClassList(link, MakeHandle(0xffff, 2)) + if err != nil { + t.Fatal(err) + } + if len(classes) != 1 { + t.Fatal("Failed to add class") + } + + filterattrs := FilterAttrs{ + LinkIndex: link.Attrs().Index, + Parent: MakeHandle(0xffff, 0), + Handle: MakeHandle(0, 0x6), + Priority: 1, + Protocol: syscall.ETH_P_IP, + } + fwattrs := FilterFwAttrs{ + Buffer: 12345, + Rate: 1234, + PeakRate: 2345, + Action: TC_POLICE_SHOT, + ClassId: MakeHandle(0xffff, 2), + } + + filter, err := NewFw(filterattrs, fwattrs) + if err != nil { + t.Fatal(err) + } + + if err := FilterAdd(filter); err != nil { + t.Fatal(err) + } + + filters, err := FilterList(link, MakeHandle(0xffff, 0)) + if err != nil { + t.Fatal(err) + } + if len(filters) != 1 { + t.Fatal("Failed to add filter") + } + fw, ok := filters[0].(*Fw) + if !ok { + t.Fatal("Filter is the wrong type") + } + if fw.Police.Rate.Rate != filter.Police.Rate.Rate { + t.Fatal("Police Rate doesn't match") + } + for i := range fw.Rtab { + if fw.Rtab[i] != filter.Rtab[i] { + t.Fatal("Rtab doesn't match") + } + if fw.Ptab[i] != filter.Ptab[i] { + t.Fatal("Ptab doesn't match") + } + } + if fw.ClassId != filter.ClassId { + t.Fatal("ClassId doesn't match") + } + if fw.InDev != filter.InDev { + t.Fatal("InDev doesn't match") + } + if fw.AvRate != filter.AvRate { + t.Fatal("AvRate doesn't match") + } + + if err := FilterDel(filter); err != nil { + t.Fatal(err) + } + filters, err = FilterList(link, MakeHandle(0xffff, 0)) + if err != nil { + t.Fatal(err) + } + if len(filters) != 0 { + t.Fatal("Failed to remove filter") + } + if err := ClassDel(class); err != nil { + t.Fatal(err) + } + classes, err = ClassList(link, MakeHandle(0xffff, 0)) + if err != nil { + t.Fatal(err) + } + if len(classes) != 0 { + t.Fatal("Failed to remove class") + } + + if err := QdiscDel(qdisc); err != nil { + t.Fatal(err) + } + qdiscs, err = QdiscList(link) + if err != nil { + t.Fatal(err) + } + if len(qdiscs) != 0 { + t.Fatal("Failed to remove qdisc") + } +} + +func TestFilterU32BpfAddDel(t *testing.T) { + tearDown := setUpNetlinkTest(t) + defer tearDown() + if err := LinkAdd(&Ifb{LinkAttrs{Name: "foo"}}); err != nil { + t.Fatal(err) + } + if err := LinkAdd(&Ifb{LinkAttrs{Name: "bar"}}); err != nil { + t.Fatal(err) + } + link, err := LinkByName("foo") + if err != nil { + t.Fatal(err) + } + if err := LinkSetUp(link); err != nil { + t.Fatal(err) + } + redir, err := LinkByName("bar") + if err != nil { + t.Fatal(err) + } + if err := LinkSetUp(redir); err != nil { + t.Fatal(err) + } + qdisc := &Ingress{ + QdiscAttrs: QdiscAttrs{ + LinkIndex: link.Attrs().Index, + Handle: MakeHandle(0xffff, 0), + Parent: HANDLE_INGRESS, + }, + } + if err := QdiscAdd(qdisc); err != nil { + t.Fatal(err) + } + qdiscs, err := QdiscList(link) + if err != nil { + t.Fatal(err) + } + if len(qdiscs) != 1 { + t.Fatal("Failed to add qdisc") + } + _, ok := qdiscs[0].(*Ingress) + if !ok { + t.Fatal("Qdisc is the wrong type") + } + + fd, err := loadSimpleBpf(BPF_PROG_TYPE_SCHED_ACT, 1) + if err != nil { + t.Skipf("Loading bpf program failed: %s", err) + } + classId := MakeHandle(1, 1) + filter := &U32{ + FilterAttrs: FilterAttrs{ + LinkIndex: link.Attrs().Index, + Parent: MakeHandle(0xffff, 0), + Priority: 1, + Protocol: syscall.ETH_P_ALL, + }, + ClassId: classId, + Actions: []Action{ + &BpfAction{Fd: fd, Name: "simple"}, + &MirredAction{ + ActionAttrs: ActionAttrs{ + Action: TC_ACT_STOLEN, + }, + MirredAction: TCA_EGRESS_REDIR, + Ifindex: redir.Attrs().Index, + }, + }, + } + + if err := FilterAdd(filter); err != nil { + t.Fatal(err) + } + + filters, err := FilterList(link, MakeHandle(0xffff, 0)) + if err != nil { + t.Fatal(err) + } + if len(filters) != 1 { + t.Fatal("Failed to add filter") + } + u32, ok := filters[0].(*U32) + if !ok { + t.Fatal("Filter is the wrong type") + } + + if len(u32.Actions) != 2 { + t.Fatalf("Too few Actions in filter") + } + if u32.ClassId != classId { + t.Fatalf("ClassId of the filter is the wrong value") + } + bpfAction, ok := u32.Actions[0].(*BpfAction) + if !ok { + t.Fatal("Action[0] is the wrong type") + } + if bpfAction.Fd != fd { + t.Fatal("Action Fd does not match") + } + if _, ok := u32.Actions[1].(*MirredAction); !ok { + t.Fatal("Action[1] is the wrong type") + } + + if err := FilterDel(filter); err != nil { + t.Fatal(err) + } + filters, err = FilterList(link, MakeHandle(0xffff, 0)) + if err != nil { + t.Fatal(err) + } + if len(filters) != 0 { + t.Fatal("Failed to remove filter") + } + + if err := QdiscDel(qdisc); err != nil { + t.Fatal(err) + } + qdiscs, err = QdiscList(link) + if err != nil { + t.Fatal(err) + } + if len(qdiscs) != 0 { + t.Fatal("Failed to remove qdisc") + } +} + +func TestFilterClsActBpfAddDel(t *testing.T) { + tearDown := setUpNetlinkTest(t) + defer tearDown() + if err := LinkAdd(&Ifb{LinkAttrs{Name: "foo"}}); err != nil { + t.Fatal(err) + } + link, err := LinkByName("foo") + if err != nil { + t.Fatal(err) + } + if err := LinkSetUp(link); err != nil { + t.Fatal(err) + } + attrs := QdiscAttrs{ + LinkIndex: link.Attrs().Index, + Handle: MakeHandle(0xffff, 0), + Parent: HANDLE_CLSACT, + } + qdisc := &GenericQdisc{ + QdiscAttrs: attrs, + QdiscType: "clsact", + } + // This feature was added in kernel 4.5 + if err := QdiscAdd(qdisc); err != nil { + t.Skipf("Failed adding clsact qdisc, unsupported kernel") + } + qdiscs, err := QdiscList(link) + if err != nil { + t.Fatal(err) + } + if len(qdiscs) != 1 { + t.Fatal("Failed to add qdisc") + } + if q, ok := qdiscs[0].(*GenericQdisc); !ok || q.Type() != "clsact" { + t.Fatal("qdisc is the wrong type") + } + + filterattrs := FilterAttrs{ + LinkIndex: link.Attrs().Index, + Parent: HANDLE_MIN_EGRESS, + Handle: MakeHandle(0, 1), + Protocol: syscall.ETH_P_ALL, + Priority: 1, + } + fd, err := loadSimpleBpf(BPF_PROG_TYPE_SCHED_CLS, 1) + if err != nil { + t.Skipf("Loading bpf program failed: %s", err) + } + filter := &BpfFilter{ + FilterAttrs: filterattrs, + Fd: fd, + Name: "simple", + DirectAction: true, + } + if filter.Fd < 0 { + t.Skipf("Failed to load bpf program") + } + + if err := FilterAdd(filter); err != nil { + t.Fatal(err) + } + + filters, err := FilterList(link, HANDLE_MIN_EGRESS) + if err != nil { + t.Fatal(err) + } + if len(filters) != 1 { + t.Fatal("Failed to add filter") + } + bpf, ok := filters[0].(*BpfFilter) + if !ok { + t.Fatal("Filter is the wrong type") + } + + if bpf.Fd != filter.Fd { + t.Fatal("Filter Fd does not match") + } + if bpf.DirectAction != filter.DirectAction { + t.Fatal("Filter DirectAction does not match") + } + + if err := FilterDel(filter); err != nil { + t.Fatal(err) + } + filters, err = FilterList(link, HANDLE_MIN_EGRESS) + if err != nil { + t.Fatal(err) + } + if len(filters) != 0 { + t.Fatal("Failed to remove filter") + } + + if err := QdiscDel(qdisc); err != nil { + t.Fatal(err) + } + qdiscs, err = QdiscList(link) + if err != nil { + t.Fatal(err) + } + if len(qdiscs) != 0 { + t.Fatal("Failed to remove qdisc") + } +} diff --git a/vendor/github.com/vishvananda/netlink/handle_test.go b/vendor/github.com/vishvananda/netlink/handle_test.go new file mode 100644 index 00000000..e7a7f86f --- /dev/null +++ b/vendor/github.com/vishvananda/netlink/handle_test.go @@ -0,0 +1,343 @@ +// +build linux + +package netlink + +import ( + "crypto/rand" + "encoding/hex" + "fmt" + "io" + "net" + "sync" + "sync/atomic" + "syscall" + "testing" + "time" + "unsafe" + + "github.com/vishvananda/netlink/nl" + "github.com/vishvananda/netns" +) + +func TestHandleCreateDelete(t *testing.T) { + h, err := NewHandle() + if err != nil { + t.Fatal(err) + } + for _, f := range nl.SupportedNlFamilies { + sh, ok := h.sockets[f] + if !ok { + t.Fatalf("Handle socket(s) for family %d was not created", f) + } + if sh.Socket == nil { + t.Fatalf("Socket for family %d was not created", f) + } + } + + h.Delete() + if h.sockets != nil { + t.Fatalf("Handle socket(s) were not destroyed") + } +} + +func TestHandleCreateNetns(t *testing.T) { + skipUnlessRoot(t) + + id := make([]byte, 4) + if _, err := io.ReadFull(rand.Reader, id); err != nil { + t.Fatal(err) + } + ifName := "dummy-" + hex.EncodeToString(id) + + // Create an handle on the current netns + curNs, err := netns.Get() + if err != nil { + t.Fatal(err) + } + defer curNs.Close() + + ch, err := NewHandleAt(curNs) + if err != nil { + t.Fatal(err) + } + defer ch.Delete() + + // Create an handle on a custom netns + newNs, err := netns.New() + if err != nil { + t.Fatal(err) + } + defer newNs.Close() + + nh, err := NewHandleAt(newNs) + if err != nil { + t.Fatal(err) + } + defer nh.Delete() + + // Create an interface using the current handle + err = ch.LinkAdd(&Dummy{LinkAttrs{Name: ifName}}) + if err != nil { + t.Fatal(err) + } + l, err := ch.LinkByName(ifName) + if err != nil { + t.Fatal(err) + } + if l.Type() != "dummy" { + t.Fatalf("Unexpected link type: %s", l.Type()) + } + + // Verify the new handle cannot find the interface + ll, err := nh.LinkByName(ifName) + if err == nil { + t.Fatalf("Unexpected link found on netns %s: %v", newNs, ll) + } + + // Move the interface to the new netns + err = ch.LinkSetNsFd(l, int(newNs)) + if err != nil { + t.Fatal(err) + } + + // Verify new netns handle can find the interface while current cannot + ll, err = nh.LinkByName(ifName) + if err != nil { + t.Fatal(err) + } + if ll.Type() != "dummy" { + t.Fatalf("Unexpected link type: %s", ll.Type()) + } + ll, err = ch.LinkByName(ifName) + if err == nil { + t.Fatalf("Unexpected link found on netns %s: %v", curNs, ll) + } +} + +func TestHandleTimeout(t *testing.T) { + h, err := NewHandle() + if err != nil { + t.Fatal(err) + } + defer h.Delete() + + for _, sh := range h.sockets { + verifySockTimeVal(t, sh.Socket.GetFd(), syscall.Timeval{Sec: 0, Usec: 0}) + } + + h.SetSocketTimeout(2*time.Second + 8*time.Millisecond) + + for _, sh := range h.sockets { + verifySockTimeVal(t, sh.Socket.GetFd(), syscall.Timeval{Sec: 2, Usec: 8000}) + } +} + +func verifySockTimeVal(t *testing.T, fd int, tv syscall.Timeval) { + var ( + tr syscall.Timeval + v = uint32(0x10) + ) + _, _, errno := syscall.Syscall6(syscall.SYS_GETSOCKOPT, uintptr(fd), syscall.SOL_SOCKET, syscall.SO_SNDTIMEO, uintptr(unsafe.Pointer(&tr)), uintptr(unsafe.Pointer(&v)), 0) + if errno != 0 { + t.Fatal(errno) + } + + if tr.Sec != tv.Sec || tr.Usec != tv.Usec { + t.Fatalf("Unexpected timeout value read: %v. Expected: %v", tr, tv) + } + + _, _, errno = syscall.Syscall6(syscall.SYS_GETSOCKOPT, uintptr(fd), syscall.SOL_SOCKET, syscall.SO_RCVTIMEO, uintptr(unsafe.Pointer(&tr)), uintptr(unsafe.Pointer(&v)), 0) + if errno != 0 { + t.Fatal(errno) + } + + if tr.Sec != tv.Sec || tr.Usec != tv.Usec { + t.Fatalf("Unexpected timeout value read: %v. Expected: %v", tr, tv) + } +} + +var ( + iter = 10 + numThread = uint32(4) + prefix = "iface" + handle1 *Handle + handle2 *Handle + ns1 netns.NsHandle + ns2 netns.NsHandle + done uint32 + initError error + once sync.Once +) + +func getXfrmState(thread int) *XfrmState { + return &XfrmState{ + Src: net.IPv4(byte(192), byte(168), 1, byte(1+thread)), + Dst: net.IPv4(byte(192), byte(168), 2, byte(1+thread)), + Proto: XFRM_PROTO_AH, + Mode: XFRM_MODE_TUNNEL, + Spi: thread, + Auth: &XfrmStateAlgo{ + Name: "hmac(sha256)", + Key: []byte("abcdefghijklmnopqrstuvwzyzABCDEF"), + }, + } +} + +func getXfrmPolicy(thread int) *XfrmPolicy { + return &XfrmPolicy{ + Src: &net.IPNet{IP: net.IPv4(byte(10), byte(10), byte(thread), 0), Mask: []byte{255, 255, 255, 0}}, + Dst: &net.IPNet{IP: net.IPv4(byte(10), byte(10), byte(thread), 0), Mask: []byte{255, 255, 255, 0}}, + Proto: 17, + DstPort: 1234, + SrcPort: 5678, + Dir: XFRM_DIR_OUT, + Tmpls: []XfrmPolicyTmpl{ + { + Src: net.IPv4(byte(192), byte(168), 1, byte(thread)), + Dst: net.IPv4(byte(192), byte(168), 2, byte(thread)), + Proto: XFRM_PROTO_ESP, + Mode: XFRM_MODE_TUNNEL, + }, + }, + } +} +func initParallel() { + ns1, initError = netns.New() + if initError != nil { + return + } + handle1, initError = NewHandleAt(ns1) + if initError != nil { + return + } + ns2, initError = netns.New() + if initError != nil { + return + } + handle2, initError = NewHandleAt(ns2) + if initError != nil { + return + } +} + +func parallelDone() { + atomic.AddUint32(&done, 1) + if done == numThread { + if ns1.IsOpen() { + ns1.Close() + } + if ns2.IsOpen() { + ns2.Close() + } + if handle1 != nil { + handle1.Delete() + } + if handle2 != nil { + handle2.Delete() + } + } +} + +// Do few route and xfrm operation on the two handles in parallel +func runParallelTests(t *testing.T, thread int) { + skipUnlessRoot(t) + defer parallelDone() + + t.Parallel() + + once.Do(initParallel) + if initError != nil { + t.Fatal(initError) + } + + state := getXfrmState(thread) + policy := getXfrmPolicy(thread) + for i := 0; i < iter; i++ { + ifName := fmt.Sprintf("%s_%d_%d", prefix, thread, i) + link := &Dummy{LinkAttrs{Name: ifName}} + err := handle1.LinkAdd(link) + if err != nil { + t.Fatal(err) + } + l, err := handle1.LinkByName(ifName) + if err != nil { + t.Fatal(err) + } + err = handle1.LinkSetUp(l) + if err != nil { + t.Fatal(err) + } + handle1.LinkSetNsFd(l, int(ns2)) + if err != nil { + t.Fatal(err) + } + err = handle1.XfrmStateAdd(state) + if err != nil { + t.Fatal(err) + } + err = handle1.XfrmPolicyAdd(policy) + if err != nil { + t.Fatal(err) + } + err = handle2.LinkSetDown(l) + if err != nil { + t.Fatal(err) + } + err = handle2.XfrmStateAdd(state) + if err != nil { + t.Fatal(err) + } + err = handle2.XfrmPolicyAdd(policy) + if err != nil { + t.Fatal(err) + } + _, err = handle2.LinkByName(ifName) + if err != nil { + t.Fatal(err) + } + handle2.LinkSetNsFd(l, int(ns1)) + if err != nil { + t.Fatal(err) + } + err = handle1.LinkSetUp(l) + if err != nil { + t.Fatal(err) + } + l, err = handle1.LinkByName(ifName) + if err != nil { + t.Fatal(err) + } + err = handle1.XfrmPolicyDel(policy) + if err != nil { + t.Fatal(err) + } + err = handle2.XfrmPolicyDel(policy) + if err != nil { + t.Fatal(err) + } + err = handle1.XfrmStateDel(state) + if err != nil { + t.Fatal(err) + } + err = handle2.XfrmStateDel(state) + if err != nil { + t.Fatal(err) + } + } +} + +func TestHandleParallel1(t *testing.T) { + runParallelTests(t, 1) +} + +func TestHandleParallel2(t *testing.T) { + runParallelTests(t, 2) +} + +func TestHandleParallel3(t *testing.T) { + runParallelTests(t, 3) +} + +func TestHandleParallel4(t *testing.T) { + runParallelTests(t, 4) +} diff --git a/vendor/github.com/vishvananda/netlink/link_linux.go b/vendor/github.com/vishvananda/netlink/link_linux.go index 56409eb2..fb2013a1 100644 --- a/vendor/github.com/vishvananda/netlink/link_linux.go +++ b/vendor/github.com/vishvananda/netlink/link_linux.go @@ -58,6 +58,44 @@ func (h *Handle) ensureIndex(link *LinkAttrs) { } } +func (h *Handle) LinkSetARPOff(link Link) error { + base := link.Attrs() + h.ensureIndex(base) + req := h.newNetlinkRequest(syscall.RTM_SETLINK, syscall.NLM_F_ACK) + + msg := nl.NewIfInfomsg(syscall.AF_UNSPEC) + msg.Change |= syscall.IFF_NOARP + msg.Flags |= syscall.IFF_NOARP + msg.Index = int32(base.Index) + req.AddData(msg) + + _, err := req.Execute(syscall.NETLINK_ROUTE, 0) + return err +} + +func LinkSetARPOff(link Link) error { + return pkgHandle.LinkSetARPOff(link) +} + +func (h *Handle) LinkSetARPOn(link Link) error { + base := link.Attrs() + h.ensureIndex(base) + req := h.newNetlinkRequest(syscall.RTM_SETLINK, syscall.NLM_F_ACK) + + msg := nl.NewIfInfomsg(syscall.AF_UNSPEC) + msg.Change |= syscall.IFF_NOARP + msg.Flags &= ^uint32(syscall.IFF_NOARP) + msg.Index = int32(base.Index) + req.AddData(msg) + + _, err := req.Execute(syscall.NETLINK_ROUTE, 0) + return err +} + +func LinkSetARPOn(link Link) error { + return pkgHandle.LinkSetARPOn(link) +} + func (h *Handle) SetPromiscOn(link Link) error { base := link.Attrs() h.ensureIndex(base) @@ -65,7 +103,7 @@ func (h *Handle) SetPromiscOn(link Link) error { msg := nl.NewIfInfomsg(syscall.AF_UNSPEC) msg.Change = syscall.IFF_PROMISC - msg.Flags = syscall.IFF_UP + msg.Flags = syscall.IFF_PROMISC msg.Index = int32(base.Index) req.AddData(msg) @@ -84,7 +122,7 @@ func (h *Handle) SetPromiscOff(link Link) error { msg := nl.NewIfInfomsg(syscall.AF_UNSPEC) msg.Change = syscall.IFF_PROMISC - msg.Flags = 0 & ^syscall.IFF_UP + msg.Flags = 0 & ^syscall.IFF_PROMISC msg.Index = int32(base.Index) req.AddData(msg) @@ -1250,6 +1288,22 @@ func (h *Handle) LinkSetFlood(link Link, mode bool) error { return h.setProtinfoAttr(link, mode, nl.IFLA_BRPORT_UNICAST_FLOOD) } +func LinkSetBrProxyArp(link Link, mode bool) error { + return pkgHandle.LinkSetBrProxyArp(link, mode) +} + +func (h *Handle) LinkSetBrProxyArp(link Link, mode bool) error { + return h.setProtinfoAttr(link, mode, nl.IFLA_BRPORT_PROXYARP) +} + +func LinkSetBrProxyArpWiFi(link Link, mode bool) error { + return pkgHandle.LinkSetBrProxyArpWiFi(link, mode) +} + +func (h *Handle) LinkSetBrProxyArpWiFi(link Link, mode bool) error { + return h.setProtinfoAttr(link, mode, nl.IFLA_BRPORT_PROXYARP_WIFI) +} + func (h *Handle) setProtinfoAttr(link Link, mode bool, attr int) error { base := link.Attrs() h.ensureIndex(base) @@ -1332,7 +1386,7 @@ func parseVxlanData(link Link, data []syscall.NetlinkRouteAttr) { } func parseBondData(link Link, data []syscall.NetlinkRouteAttr) { - bond := NewLinkBond(NewLinkAttrs()) + bond := link.(*Bond) for i := range data { switch data[i].Attr.Type { case nl.IFLA_BOND_MODE: diff --git a/vendor/github.com/vishvananda/netlink/link_test.go b/vendor/github.com/vishvananda/netlink/link_test.go new file mode 100644 index 00000000..9d771473 --- /dev/null +++ b/vendor/github.com/vishvananda/netlink/link_test.go @@ -0,0 +1,1119 @@ +// +build linux + +package netlink + +import ( + "bytes" + "net" + "os" + "syscall" + "testing" + "time" + + "github.com/vishvananda/netns" +) + +const ( + testTxQLen int = 100 + defaultTxQLen int = 1000 +) + +func testLinkAddDel(t *testing.T, link Link) { + links, err := LinkList() + if err != nil { + t.Fatal(err) + } + + if err := LinkAdd(link); err != nil { + t.Fatal(err) + } + + base := link.Attrs() + + result, err := LinkByName(base.Name) + if err != nil { + t.Fatal(err) + } + + rBase := result.Attrs() + + if vlan, ok := link.(*Vlan); ok { + other, ok := result.(*Vlan) + if !ok { + t.Fatal("Result of create is not a vlan") + } + if vlan.VlanId != other.VlanId { + t.Fatal("Link.VlanId id doesn't match") + } + } + + if veth, ok := result.(*Veth); ok { + if rBase.TxQLen != base.TxQLen { + t.Fatalf("qlen is %d, should be %d", rBase.TxQLen, base.TxQLen) + } + if rBase.MTU != base.MTU { + t.Fatalf("MTU is %d, should be %d", rBase.MTU, base.MTU) + } + + if original, ok := link.(*Veth); ok { + if original.PeerName != "" { + var peer *Veth + other, err := LinkByName(original.PeerName) + if err != nil { + t.Fatalf("Peer %s not created", veth.PeerName) + } + if peer, ok = other.(*Veth); !ok { + t.Fatalf("Peer %s is incorrect type", veth.PeerName) + } + if peer.TxQLen != testTxQLen { + t.Fatalf("TxQLen of peer is %d, should be %d", peer.TxQLen, testTxQLen) + } + } + } + } else { + // recent kernels set the parent index for veths in the response + if rBase.ParentIndex == 0 && base.ParentIndex != 0 { + t.Fatalf("Created link doesn't have parent %d but it should", base.ParentIndex) + } else if rBase.ParentIndex != 0 && base.ParentIndex == 0 { + t.Fatalf("Created link has parent %d but it shouldn't", rBase.ParentIndex) + } else if rBase.ParentIndex != 0 && base.ParentIndex != 0 { + if rBase.ParentIndex != base.ParentIndex { + t.Fatalf("Link.ParentIndex doesn't match %d != %d", rBase.ParentIndex, base.ParentIndex) + } + } + } + + if vxlan, ok := link.(*Vxlan); ok { + other, ok := result.(*Vxlan) + if !ok { + t.Fatal("Result of create is not a vxlan") + } + compareVxlan(t, vxlan, other) + } + + if ipv, ok := link.(*IPVlan); ok { + other, ok := result.(*IPVlan) + if !ok { + t.Fatal("Result of create is not a ipvlan") + } + if ipv.Mode != other.Mode { + t.Fatalf("Got unexpected mode: %d, expected: %d", other.Mode, ipv.Mode) + } + } + + if macv, ok := link.(*Macvlan); ok { + other, ok := result.(*Macvlan) + if !ok { + t.Fatal("Result of create is not a macvlan") + } + if macv.Mode != other.Mode { + t.Fatalf("Got unexpected mode: %d, expected: %d", other.Mode, macv.Mode) + } + } + + if macv, ok := link.(*Macvtap); ok { + other, ok := result.(*Macvtap) + if !ok { + t.Fatal("Result of create is not a macvtap") + } + if macv.Mode != other.Mode { + t.Fatalf("Got unexpected mode: %d, expected: %d", other.Mode, macv.Mode) + } + } + + if _, ok := link.(*Vti); ok { + _, ok := result.(*Vti) + if !ok { + t.Fatal("Result of create is not a vti") + } + } + + if bond, ok := link.(*Bond); ok { + other, ok := result.(*Bond) + if !ok { + t.Fatal("Result of create is not a bond") + } + if bond.Mode != other.Mode { + t.Fatalf("Got unexpected mode: %d, expected: %d", other.Mode, bond.Mode) + } + } + + if _, ok := link.(*Iptun); ok { + _, ok := result.(*Iptun) + if !ok { + t.Fatal("Result of create is not a iptun") + } + } + + if err = LinkDel(link); err != nil { + t.Fatal(err) + } + + links, err = LinkList() + if err != nil { + t.Fatal(err) + } + + for _, l := range links { + if l.Attrs().Name == link.Attrs().Name { + t.Fatal("Link not removed properly") + } + } +} + +func compareVxlan(t *testing.T, expected, actual *Vxlan) { + + if actual.VxlanId != expected.VxlanId { + t.Fatal("Vxlan.VxlanId doesn't match") + } + if expected.SrcAddr != nil && !actual.SrcAddr.Equal(expected.SrcAddr) { + t.Fatal("Vxlan.SrcAddr doesn't match") + } + if expected.Group != nil && !actual.Group.Equal(expected.Group) { + t.Fatal("Vxlan.Group doesn't match") + } + if expected.TTL != -1 && actual.TTL != expected.TTL { + t.Fatal("Vxlan.TTL doesn't match") + } + if expected.TOS != -1 && actual.TOS != expected.TOS { + t.Fatal("Vxlan.TOS doesn't match") + } + if actual.Learning != expected.Learning { + t.Fatal("Vxlan.Learning doesn't match") + } + if actual.Proxy != expected.Proxy { + t.Fatal("Vxlan.Proxy doesn't match") + } + if actual.RSC != expected.RSC { + t.Fatal("Vxlan.RSC doesn't match") + } + if actual.L2miss != expected.L2miss { + t.Fatal("Vxlan.L2miss doesn't match") + } + if actual.L3miss != expected.L3miss { + t.Fatal("Vxlan.L3miss doesn't match") + } + if actual.GBP != expected.GBP { + t.Fatal("Vxlan.GBP doesn't match") + } + if expected.NoAge { + if !actual.NoAge { + t.Fatal("Vxlan.NoAge doesn't match") + } + } else if expected.Age > 0 && actual.Age != expected.Age { + t.Fatal("Vxlan.Age doesn't match") + } + if expected.Limit > 0 && actual.Limit != expected.Limit { + t.Fatal("Vxlan.Limit doesn't match") + } + if expected.Port > 0 && actual.Port != expected.Port { + t.Fatal("Vxlan.Port doesn't match") + } + if expected.PortLow > 0 || expected.PortHigh > 0 { + if actual.PortLow != expected.PortLow { + t.Fatal("Vxlan.PortLow doesn't match") + } + if actual.PortHigh != expected.PortHigh { + t.Fatal("Vxlan.PortHigh doesn't match") + } + } +} + +func TestLinkAddDelDummy(t *testing.T) { + tearDown := setUpNetlinkTest(t) + defer tearDown() + + testLinkAddDel(t, &Dummy{LinkAttrs{Name: "foo"}}) +} + +func TestLinkAddDelIfb(t *testing.T) { + tearDown := setUpNetlinkTest(t) + defer tearDown() + + testLinkAddDel(t, &Ifb{LinkAttrs{Name: "foo"}}) +} + +func TestLinkAddDelBridge(t *testing.T) { + tearDown := setUpNetlinkTest(t) + defer tearDown() + + testLinkAddDel(t, &Bridge{LinkAttrs{Name: "foo", MTU: 1400}}) +} + +func TestLinkAddDelGretap(t *testing.T) { + tearDown := setUpNetlinkTest(t) + defer tearDown() + + testLinkAddDel(t, &Gretap{ + LinkAttrs: LinkAttrs{Name: "foo"}, + IKey: 0x101, + OKey: 0x101, + PMtuDisc: 1, + Local: net.IPv4(127, 0, 0, 1), + Remote: net.IPv4(127, 0, 0, 1)}) +} + +func TestLinkAddDelVlan(t *testing.T) { + tearDown := setUpNetlinkTest(t) + defer tearDown() + + parent := &Dummy{LinkAttrs{Name: "foo"}} + if err := LinkAdd(parent); err != nil { + t.Fatal(err) + } + + testLinkAddDel(t, &Vlan{LinkAttrs{Name: "bar", ParentIndex: parent.Attrs().Index}, 900}) + + if err := LinkDel(parent); err != nil { + t.Fatal(err) + } +} + +func TestLinkAddDelMacvlan(t *testing.T) { + tearDown := setUpNetlinkTest(t) + defer tearDown() + + parent := &Dummy{LinkAttrs{Name: "foo"}} + if err := LinkAdd(parent); err != nil { + t.Fatal(err) + } + + testLinkAddDel(t, &Macvlan{ + LinkAttrs: LinkAttrs{Name: "bar", ParentIndex: parent.Attrs().Index}, + Mode: MACVLAN_MODE_PRIVATE, + }) + + testLinkAddDel(t, &Macvlan{ + LinkAttrs: LinkAttrs{Name: "bar", ParentIndex: parent.Attrs().Index}, + Mode: MACVLAN_MODE_BRIDGE, + }) + + testLinkAddDel(t, &Macvlan{ + LinkAttrs: LinkAttrs{Name: "bar", ParentIndex: parent.Attrs().Index}, + Mode: MACVLAN_MODE_VEPA, + }) + + if err := LinkDel(parent); err != nil { + t.Fatal(err) + } +} + +func TestLinkAddDelMacvtap(t *testing.T) { + tearDown := setUpNetlinkTest(t) + defer tearDown() + + parent := &Dummy{LinkAttrs{Name: "foo"}} + if err := LinkAdd(parent); err != nil { + t.Fatal(err) + } + + testLinkAddDel(t, &Macvtap{ + Macvlan: Macvlan{ + LinkAttrs: LinkAttrs{Name: "bar", ParentIndex: parent.Attrs().Index}, + Mode: MACVLAN_MODE_PRIVATE, + }, + }) + + testLinkAddDel(t, &Macvtap{ + Macvlan: Macvlan{ + LinkAttrs: LinkAttrs{Name: "bar", ParentIndex: parent.Attrs().Index}, + Mode: MACVLAN_MODE_BRIDGE, + }, + }) + + testLinkAddDel(t, &Macvtap{ + Macvlan: Macvlan{ + LinkAttrs: LinkAttrs{Name: "bar", ParentIndex: parent.Attrs().Index}, + Mode: MACVLAN_MODE_VEPA, + }, + }) + + if err := LinkDel(parent); err != nil { + t.Fatal(err) + } +} + +func TestLinkAddDelVeth(t *testing.T) { + tearDown := setUpNetlinkTest(t) + defer tearDown() + + veth := &Veth{LinkAttrs: LinkAttrs{Name: "foo", TxQLen: testTxQLen, MTU: 1400}, PeerName: "bar"} + testLinkAddDel(t, veth) +} + +func TestLinkAddDelBond(t *testing.T) { + tearDown := setUpNetlinkTest(t) + defer tearDown() + + bond := NewLinkBond(LinkAttrs{Name: "foo"}) + bond.Mode = StringToBondModeMap["802.3ad"] + testLinkAddDel(t, bond) +} + +func TestLinkAddVethWithDefaultTxQLen(t *testing.T) { + tearDown := setUpNetlinkTest(t) + defer tearDown() + la := NewLinkAttrs() + la.Name = "foo" + + veth := &Veth{LinkAttrs: la, PeerName: "bar"} + if err := LinkAdd(veth); err != nil { + t.Fatal(err) + } + link, err := LinkByName("foo") + if err != nil { + t.Fatal(err) + } + if veth, ok := link.(*Veth); !ok { + t.Fatalf("unexpected link type: %T", link) + } else { + if veth.TxQLen != defaultTxQLen { + t.Fatalf("TxQLen is %d, should be %d", veth.TxQLen, defaultTxQLen) + } + } + peer, err := LinkByName("bar") + if err != nil { + t.Fatal(err) + } + if veth, ok := peer.(*Veth); !ok { + t.Fatalf("unexpected link type: %T", link) + } else { + if veth.TxQLen != defaultTxQLen { + t.Fatalf("TxQLen is %d, should be %d", veth.TxQLen, defaultTxQLen) + } + } +} + +func TestLinkAddVethWithZeroTxQLen(t *testing.T) { + tearDown := setUpNetlinkTest(t) + defer tearDown() + la := NewLinkAttrs() + la.Name = "foo" + la.TxQLen = 0 + + veth := &Veth{LinkAttrs: la, PeerName: "bar"} + if err := LinkAdd(veth); err != nil { + t.Fatal(err) + } + link, err := LinkByName("foo") + if err != nil { + t.Fatal(err) + } + if veth, ok := link.(*Veth); !ok { + t.Fatalf("unexpected link type: %T", link) + } else { + if veth.TxQLen != 0 { + t.Fatalf("TxQLen is %d, should be %d", veth.TxQLen, 0) + } + } + peer, err := LinkByName("bar") + if err != nil { + t.Fatal(err) + } + if veth, ok := peer.(*Veth); !ok { + t.Fatalf("unexpected link type: %T", link) + } else { + if veth.TxQLen != 0 { + t.Fatalf("TxQLen is %d, should be %d", veth.TxQLen, 0) + } + } +} + +func TestLinkAddDummyWithTxQLen(t *testing.T) { + tearDown := setUpNetlinkTest(t) + defer tearDown() + la := NewLinkAttrs() + la.Name = "foo" + la.TxQLen = 1500 + + dummy := &Dummy{LinkAttrs: la} + if err := LinkAdd(dummy); err != nil { + t.Fatal(err) + } + link, err := LinkByName("foo") + if err != nil { + t.Fatal(err) + } + if dummy, ok := link.(*Dummy); !ok { + t.Fatalf("unexpected link type: %T", link) + } else { + if dummy.TxQLen != 1500 { + t.Fatalf("TxQLen is %d, should be %d", dummy.TxQLen, 1500) + } + } +} + +func TestLinkAddDelBridgeMaster(t *testing.T) { + tearDown := setUpNetlinkTest(t) + defer tearDown() + + master := &Bridge{LinkAttrs{Name: "foo"}} + if err := LinkAdd(master); err != nil { + t.Fatal(err) + } + testLinkAddDel(t, &Dummy{LinkAttrs{Name: "bar", MasterIndex: master.Attrs().Index}}) + + if err := LinkDel(master); err != nil { + t.Fatal(err) + } +} + +func TestLinkSetUnsetResetMaster(t *testing.T) { + tearDown := setUpNetlinkTest(t) + defer tearDown() + + master := &Bridge{LinkAttrs{Name: "foo"}} + if err := LinkAdd(master); err != nil { + t.Fatal(err) + } + + newmaster := &Bridge{LinkAttrs{Name: "bar"}} + if err := LinkAdd(newmaster); err != nil { + t.Fatal(err) + } + + slave := &Dummy{LinkAttrs{Name: "baz"}} + if err := LinkAdd(slave); err != nil { + t.Fatal(err) + } + + nonexistsmaster := &Bridge{LinkAttrs{Name: "foobar"}} + + if err := LinkSetMaster(slave, nonexistsmaster); err == nil { + t.Fatal("error expected") + } + + if err := LinkSetMaster(slave, master); err != nil { + t.Fatal(err) + } + + link, err := LinkByName("baz") + if err != nil { + t.Fatal(err) + } + + if link.Attrs().MasterIndex != master.Attrs().Index { + t.Fatal("Master not set properly") + } + + if err := LinkSetMaster(slave, newmaster); err != nil { + t.Fatal(err) + } + + link, err = LinkByName("baz") + if err != nil { + t.Fatal(err) + } + + if link.Attrs().MasterIndex != newmaster.Attrs().Index { + t.Fatal("Master not reset properly") + } + + if err := LinkSetNoMaster(slave); err != nil { + t.Fatal(err) + } + + link, err = LinkByName("baz") + if err != nil { + t.Fatal(err) + } + + if link.Attrs().MasterIndex != 0 { + t.Fatal("Master not unset properly") + } + if err := LinkDel(slave); err != nil { + t.Fatal(err) + } + + if err := LinkDel(newmaster); err != nil { + t.Fatal(err) + } + + if err := LinkDel(master); err != nil { + t.Fatal(err) + } +} + +func TestLinkSetNs(t *testing.T) { + tearDown := setUpNetlinkTest(t) + defer tearDown() + + basens, err := netns.Get() + if err != nil { + t.Fatal("Failed to get basens") + } + defer basens.Close() + + newns, err := netns.New() + if err != nil { + t.Fatal("Failed to create newns") + } + defer newns.Close() + + link := &Veth{LinkAttrs{Name: "foo"}, "bar"} + if err := LinkAdd(link); err != nil { + t.Fatal(err) + } + + peer, err := LinkByName("bar") + if err != nil { + t.Fatal(err) + } + + LinkSetNsFd(peer, int(basens)) + if err != nil { + t.Fatal("Failed to set newns for link") + } + + _, err = LinkByName("bar") + if err == nil { + t.Fatal("Link bar is still in newns") + } + + err = netns.Set(basens) + if err != nil { + t.Fatal("Failed to set basens") + } + + peer, err = LinkByName("bar") + if err != nil { + t.Fatal("Link is not in basens") + } + + if err := LinkDel(peer); err != nil { + t.Fatal(err) + } + + err = netns.Set(newns) + if err != nil { + t.Fatal("Failed to set newns") + } + + _, err = LinkByName("foo") + if err == nil { + t.Fatal("Other half of veth pair not deleted") + } + +} + +func TestLinkAddDelVxlan(t *testing.T) { + tearDown := setUpNetlinkTest(t) + defer tearDown() + + parent := &Dummy{ + LinkAttrs{Name: "foo"}, + } + if err := LinkAdd(parent); err != nil { + t.Fatal(err) + } + + vxlan := Vxlan{ + LinkAttrs: LinkAttrs{ + Name: "bar", + }, + VxlanId: 10, + VtepDevIndex: parent.Index, + Learning: true, + L2miss: true, + L3miss: true, + } + + testLinkAddDel(t, &vxlan) + if err := LinkDel(parent); err != nil { + t.Fatal(err) + } +} + +func TestLinkAddDelVxlanGbp(t *testing.T) { + if os.Getenv("TRAVIS_BUILD_DIR") != "" { + t.Skipf("Kernel in travis is too old for this test") + } + + tearDown := setUpNetlinkTest(t) + defer tearDown() + + parent := &Dummy{ + LinkAttrs{Name: "foo"}, + } + if err := LinkAdd(parent); err != nil { + t.Fatal(err) + } + + vxlan := Vxlan{ + LinkAttrs: LinkAttrs{ + Name: "bar", + }, + VxlanId: 10, + VtepDevIndex: parent.Index, + Learning: true, + L2miss: true, + L3miss: true, + GBP: true, + } + + testLinkAddDel(t, &vxlan) + if err := LinkDel(parent); err != nil { + t.Fatal(err) + } +} + +func TestLinkAddDelIPVlanL2(t *testing.T) { + if os.Getenv("TRAVIS_BUILD_DIR") != "" { + t.Skipf("Kernel in travis is too old for this test") + } + tearDown := setUpNetlinkTest(t) + defer tearDown() + parent := &Dummy{LinkAttrs{Name: "foo"}} + if err := LinkAdd(parent); err != nil { + t.Fatal(err) + } + + ipv := IPVlan{ + LinkAttrs: LinkAttrs{ + Name: "bar", + ParentIndex: parent.Index, + }, + Mode: IPVLAN_MODE_L2, + } + + testLinkAddDel(t, &ipv) +} + +func TestLinkAddDelIPVlanL3(t *testing.T) { + if os.Getenv("TRAVIS_BUILD_DIR") != "" { + t.Skipf("Kernel in travis is too old for this test") + } + tearDown := setUpNetlinkTest(t) + defer tearDown() + parent := &Dummy{LinkAttrs{Name: "foo"}} + if err := LinkAdd(parent); err != nil { + t.Fatal(err) + } + + ipv := IPVlan{ + LinkAttrs: LinkAttrs{ + Name: "bar", + ParentIndex: parent.Index, + }, + Mode: IPVLAN_MODE_L3, + } + + testLinkAddDel(t, &ipv) +} + +func TestLinkAddDelIPVlanNoParent(t *testing.T) { + tearDown := setUpNetlinkTest(t) + defer tearDown() + + ipv := IPVlan{ + LinkAttrs: LinkAttrs{ + Name: "bar", + }, + Mode: IPVLAN_MODE_L3, + } + err := LinkAdd(&ipv) + if err == nil { + t.Fatal("Add should fail if ipvlan creating without ParentIndex") + } + if err.Error() != "Can't create ipvlan link without ParentIndex" { + t.Fatalf("Error should be about missing ParentIndex, got %q", err) + } +} + +func TestLinkByIndex(t *testing.T) { + tearDown := setUpNetlinkTest(t) + defer tearDown() + + dummy := &Dummy{LinkAttrs{Name: "dummy"}} + if err := LinkAdd(dummy); err != nil { + t.Fatal(err) + } + + found, err := LinkByIndex(dummy.Index) + if err != nil { + t.Fatal(err) + } + + if found.Attrs().Index != dummy.Attrs().Index { + t.Fatalf("Indices don't match: %v != %v", found.Attrs().Index, dummy.Attrs().Index) + } + + LinkDel(dummy) + + // test not found + _, err = LinkByIndex(dummy.Attrs().Index) + if err == nil { + t.Fatalf("LinkByIndex(%v) found deleted link", err) + } +} + +func TestLinkSet(t *testing.T) { + tearDown := setUpNetlinkTest(t) + defer tearDown() + + iface := &Dummy{LinkAttrs{Name: "foo"}} + if err := LinkAdd(iface); err != nil { + t.Fatal(err) + } + + link, err := LinkByName("foo") + if err != nil { + t.Fatal(err) + } + + err = LinkSetName(link, "bar") + if err != nil { + t.Fatalf("Could not change interface name: %v", err) + } + + link, err = LinkByName("bar") + if err != nil { + t.Fatalf("Interface name not changed: %v", err) + } + + err = LinkSetMTU(link, 1400) + if err != nil { + t.Fatalf("Could not set MTU: %v", err) + } + + link, err = LinkByName("bar") + if err != nil { + t.Fatal(err) + } + + if link.Attrs().MTU != 1400 { + t.Fatal("MTU not changed!") + } + + addr, err := net.ParseMAC("00:12:34:56:78:AB") + if err != nil { + t.Fatal(err) + } + + err = LinkSetHardwareAddr(link, addr) + if err != nil { + t.Fatal(err) + } + + link, err = LinkByName("bar") + if err != nil { + t.Fatal(err) + } + + if !bytes.Equal(link.Attrs().HardwareAddr, addr) { + t.Fatalf("hardware address not changed!") + } + + err = LinkSetAlias(link, "barAlias") + if err != nil { + t.Fatalf("Could not set alias: %v", err) + } + + link, err = LinkByName("bar") + if err != nil { + t.Fatal(err) + } + + if link.Attrs().Alias != "barAlias" { + t.Fatalf("alias not changed!") + } + + link, err = LinkByAlias("barAlias") + if err != nil { + t.Fatal(err) + } +} + +func TestLinkSetARP(t *testing.T) { + tearDown := setUpNetlinkTest(t) + defer tearDown() + + iface := &Veth{LinkAttrs: LinkAttrs{Name: "foo", TxQLen: testTxQLen, MTU: 1500}, PeerName: "banana"} + if err := LinkAdd(iface); err != nil { + t.Fatal(err) + } + + link, err := LinkByName("foo") + if err != nil { + t.Fatal(err) + } + + err = LinkSetARPOff(link) + if err != nil { + t.Fatal(err) + } + + link, err = LinkByName("foo") + if err != nil { + t.Fatal(err) + } + + if link.Attrs().RawFlags&syscall.IFF_NOARP != uint32(syscall.IFF_NOARP) { + t.Fatalf("NOARP was not set!") + } + + err = LinkSetARPOn(link) + if err != nil { + t.Fatal(err) + } + + link, err = LinkByName("foo") + if err != nil { + t.Fatal(err) + } + + if link.Attrs().RawFlags&syscall.IFF_NOARP != 0 { + t.Fatalf("NOARP is still set!") + } +} + +func expectLinkUpdate(ch <-chan LinkUpdate, ifaceName string, up bool) bool { + for { + timeout := time.After(time.Minute) + select { + case update := <-ch: + if ifaceName == update.Link.Attrs().Name && (update.IfInfomsg.Flags&syscall.IFF_UP != 0) == up { + return true + } + case <-timeout: + return false + } + } +} + +func TestLinkSubscribe(t *testing.T) { + tearDown := setUpNetlinkTest(t) + defer tearDown() + + ch := make(chan LinkUpdate) + done := make(chan struct{}) + defer close(done) + if err := LinkSubscribe(ch, done); err != nil { + t.Fatal(err) + } + + link := &Veth{LinkAttrs{Name: "foo", TxQLen: testTxQLen, MTU: 1400}, "bar"} + if err := LinkAdd(link); err != nil { + t.Fatal(err) + } + + if !expectLinkUpdate(ch, "foo", false) { + t.Fatal("Add update not received as expected") + } + + if err := LinkSetUp(link); err != nil { + t.Fatal(err) + } + + if !expectLinkUpdate(ch, "foo", true) { + t.Fatal("Link Up update not received as expected") + } + + if err := LinkDel(link); err != nil { + t.Fatal(err) + } + + if !expectLinkUpdate(ch, "foo", false) { + t.Fatal("Del update not received as expected") + } +} + +func TestLinkSubscribeAt(t *testing.T) { + skipUnlessRoot(t) + + // Create an handle on a custom netns + newNs, err := netns.New() + if err != nil { + t.Fatal(err) + } + defer newNs.Close() + + nh, err := NewHandleAt(newNs) + if err != nil { + t.Fatal(err) + } + defer nh.Delete() + + // Subscribe for Link events on the custom netns + ch := make(chan LinkUpdate) + done := make(chan struct{}) + defer close(done) + if err := LinkSubscribeAt(newNs, ch, done); err != nil { + t.Fatal(err) + } + + link := &Veth{LinkAttrs{Name: "test", TxQLen: testTxQLen, MTU: 1400}, "bar"} + if err := nh.LinkAdd(link); err != nil { + t.Fatal(err) + } + + if !expectLinkUpdate(ch, "test", false) { + t.Fatal("Add update not received as expected") + } + + if err := nh.LinkSetUp(link); err != nil { + t.Fatal(err) + } + + if !expectLinkUpdate(ch, "test", true) { + t.Fatal("Link Up update not received as expected") + } + + if err := nh.LinkDel(link); err != nil { + t.Fatal(err) + } + + if !expectLinkUpdate(ch, "test", false) { + t.Fatal("Del update not received as expected") + } +} + +func TestLinkStats(t *testing.T) { + defer setUpNetlinkTest(t)() + + // Create a veth pair and verify the cross-stats once both + // ends are brought up and some ICMPv6 packets are exchanged + v0 := "v0" + v1 := "v1" + + vethLink := &Veth{LinkAttrs: LinkAttrs{Name: v0}, PeerName: v1} + if err := LinkAdd(vethLink); err != nil { + t.Fatal(err) + } + + veth0, err := LinkByName(v0) + if err != nil { + t.Fatal(err) + } + if err := LinkSetUp(veth0); err != nil { + t.Fatal(err) + } + + veth1, err := LinkByName(v1) + if err != nil { + t.Fatal(err) + } + if err := LinkSetUp(veth1); err != nil { + t.Fatal(err) + } + + time.Sleep(2 * time.Second) + + // verify statistics + veth0, err = LinkByName(v0) + if err != nil { + t.Fatal(err) + } + veth1, err = LinkByName(v1) + if err != nil { + t.Fatal(err) + } + v0Stats := veth0.Attrs().Statistics + v1Stats := veth1.Attrs().Statistics + if v0Stats.RxPackets != v1Stats.TxPackets || v0Stats.TxPackets != v1Stats.RxPackets || + v0Stats.RxBytes != v1Stats.TxBytes || v0Stats.TxBytes != v1Stats.RxBytes { + t.Fatalf("veth ends counters differ:\n%v\n%v", v0Stats, v1Stats) + } +} + +func TestLinkXdp(t *testing.T) { + links, err := LinkList() + if err != nil { + t.Fatal(err) + } + var testXdpLink Link + for _, link := range links { + if link.Attrs().Xdp != nil && !link.Attrs().Xdp.Attached { + testXdpLink = link + break + } + } + if testXdpLink == nil { + t.Skipf("No link supporting XDP found") + } + fd, err := loadSimpleBpf(BPF_PROG_TYPE_XDP, 2 /*XDP_PASS*/) + if err != nil { + t.Skipf("Loading bpf program failed: %s", err) + } + if err := LinkSetXdpFd(testXdpLink, fd); err != nil { + t.Fatal(err) + } + if err := LinkSetXdpFd(testXdpLink, -1); err != nil { + t.Fatal(err) + } +} + +func TestLinkAddDelIptun(t *testing.T) { + tearDown := setUpNetlinkTest(t) + defer tearDown() + + testLinkAddDel(t, &Iptun{ + LinkAttrs: LinkAttrs{Name: "iptunfoo"}, + PMtuDisc: 1, + Local: net.IPv4(127, 0, 0, 1), + Remote: net.IPv4(127, 0, 0, 1)}) +} + +func TestLinkAddDelVti(t *testing.T) { + tearDown := setUpNetlinkTest(t) + defer tearDown() + + testLinkAddDel(t, &Vti{ + LinkAttrs: LinkAttrs{Name: "vtifoo"}, + IKey: 0x101, + OKey: 0x101, + Local: net.IPv4(127, 0, 0, 1), + Remote: net.IPv4(127, 0, 0, 1)}) +} + +func TestLinkSubscribeWithProtinfo(t *testing.T) { + tearDown := setUpNetlinkTest(t) + defer tearDown() + + master := &Bridge{LinkAttrs{Name: "foo"}} + if err := LinkAdd(master); err != nil { + t.Fatal(err) + } + + slave := &Veth{ + LinkAttrs: LinkAttrs{ + Name: "bar", + TxQLen: testTxQLen, + MTU: 1400, + MasterIndex: master.Attrs().Index, + }, + PeerName: "bar-peer", + } + if err := LinkAdd(slave); err != nil { + t.Fatal(err) + } + + ch := make(chan LinkUpdate) + done := make(chan struct{}) + defer close(done) + if err := LinkSubscribe(ch, done); err != nil { + t.Fatal(err) + } + + if err := LinkSetHairpin(slave, true); err != nil { + t.Fatal(err) + } + + select { + case update := <-ch: + if !(update.Attrs().Name == "bar" && update.Attrs().Protinfo != nil && + update.Attrs().Protinfo.Hairpin) { + t.Fatal("Hairpin update not received as expected") + } + case <-time.After(time.Minute): + t.Fatal("Hairpin update timed out") + } + + if err := LinkDel(slave); err != nil { + t.Fatal(err) + } + + if err := LinkDel(master); err != nil { + t.Fatal(err) + } +} diff --git a/vendor/github.com/vishvananda/netlink/neigh_test.go b/vendor/github.com/vishvananda/netlink/neigh_test.go new file mode 100644 index 00000000..8ad8b32c --- /dev/null +++ b/vendor/github.com/vishvananda/netlink/neigh_test.go @@ -0,0 +1,194 @@ +// +build linux + +package netlink + +import ( + "net" + "testing" +) + +type arpEntry struct { + ip net.IP + mac net.HardwareAddr +} + +type proxyEntry struct { + ip net.IP + dev int +} + +func parseMAC(s string) net.HardwareAddr { + m, err := net.ParseMAC(s) + if err != nil { + panic(err) + } + return m +} + +func dumpContains(dump []Neigh, e arpEntry) bool { + for _, n := range dump { + if n.IP.Equal(e.ip) && (n.State&NUD_INCOMPLETE) == 0 { + return true + } + } + return false +} + +func dumpContainsProxy(dump []Neigh, p proxyEntry) bool { + for _, n := range dump { + if n.IP.Equal(p.ip) && (n.LinkIndex == p.dev) && (n.Flags&NTF_PROXY) == NTF_PROXY { + return true + } + } + return false +} + +func TestNeighAddDel(t *testing.T) { + tearDown := setUpNetlinkTest(t) + defer tearDown() + + dummy := Dummy{LinkAttrs{Name: "neigh0"}} + if err := LinkAdd(&dummy); err != nil { + t.Fatal(err) + } + + ensureIndex(dummy.Attrs()) + + arpTable := []arpEntry{ + {net.ParseIP("10.99.0.1"), parseMAC("aa:bb:cc:dd:00:01")}, + {net.ParseIP("10.99.0.2"), parseMAC("aa:bb:cc:dd:00:02")}, + {net.ParseIP("10.99.0.3"), parseMAC("aa:bb:cc:dd:00:03")}, + {net.ParseIP("10.99.0.4"), parseMAC("aa:bb:cc:dd:00:04")}, + {net.ParseIP("10.99.0.5"), parseMAC("aa:bb:cc:dd:00:05")}, + } + + // Add the arpTable + for _, entry := range arpTable { + err := NeighAdd(&Neigh{ + LinkIndex: dummy.Index, + State: NUD_REACHABLE, + IP: entry.ip, + HardwareAddr: entry.mac, + }) + + if err != nil { + t.Errorf("Failed to NeighAdd: %v", err) + } + } + + // Dump and see that all added entries are there + dump, err := NeighList(dummy.Index, 0) + if err != nil { + t.Errorf("Failed to NeighList: %v", err) + } + + for _, entry := range arpTable { + if !dumpContains(dump, entry) { + t.Errorf("Dump does not contain: %v", entry) + } + } + + // Delete the arpTable + for _, entry := range arpTable { + err := NeighDel(&Neigh{ + LinkIndex: dummy.Index, + IP: entry.ip, + HardwareAddr: entry.mac, + }) + + if err != nil { + t.Errorf("Failed to NeighDel: %v", err) + } + } + + // TODO: seems not working because of cache + //// Dump and see that none of deleted entries are there + //dump, err = NeighList(dummy.Index, 0) + //if err != nil { + //t.Errorf("Failed to NeighList: %v", err) + //} + + //for _, entry := range arpTable { + //if dumpContains(dump, entry) { + //t.Errorf("Dump contains: %v", entry) + //} + //} + + if err := LinkDel(&dummy); err != nil { + t.Fatal(err) + } +} + +func TestNeighAddDelProxy(t *testing.T) { + tearDown := setUpNetlinkTest(t) + defer tearDown() + + dummy := Dummy{LinkAttrs{Name: "neigh0"}} + if err := LinkAdd(&dummy); err != nil { + t.Fatal(err) + } + + ensureIndex(dummy.Attrs()) + + proxyTable := []proxyEntry{ + {net.ParseIP("10.99.0.1"), dummy.Index}, + {net.ParseIP("10.99.0.2"), dummy.Index}, + {net.ParseIP("10.99.0.3"), dummy.Index}, + {net.ParseIP("10.99.0.4"), dummy.Index}, + {net.ParseIP("10.99.0.5"), dummy.Index}, + } + + // Add the proxyTable + for _, entry := range proxyTable { + err := NeighAdd(&Neigh{ + LinkIndex: dummy.Index, + Flags: NTF_PROXY, + IP: entry.ip, + }) + + if err != nil { + t.Errorf("Failed to NeighAdd: %v", err) + } + } + + // Dump and see that all added entries are there + dump, err := NeighProxyList(dummy.Index, 0) + if err != nil { + t.Errorf("Failed to NeighList: %v", err) + } + + for _, entry := range proxyTable { + if !dumpContainsProxy(dump, entry) { + t.Errorf("Dump does not contain: %v", entry) + } + } + + // Delete the proxyTable + for _, entry := range proxyTable { + err := NeighDel(&Neigh{ + LinkIndex: dummy.Index, + Flags: NTF_PROXY, + IP: entry.ip, + }) + + if err != nil { + t.Errorf("Failed to NeighDel: %v", err) + } + } + + // Dump and see that none of deleted entries are there + dump, err = NeighProxyList(dummy.Index, 0) + if err != nil { + t.Errorf("Failed to NeighList: %v", err) + } + + for _, entry := range proxyTable { + if dumpContainsProxy(dump, entry) { + t.Errorf("Dump contains: %v", entry) + } + } + + if err := LinkDel(&dummy); err != nil { + t.Fatal(err) + } +} diff --git a/vendor/github.com/vishvananda/netlink/netlink_test.go b/vendor/github.com/vishvananda/netlink/netlink_test.go new file mode 100644 index 00000000..5037b7f5 --- /dev/null +++ b/vendor/github.com/vishvananda/netlink/netlink_test.go @@ -0,0 +1,58 @@ +package netlink + +import ( + "log" + "os" + "runtime" + "testing" + + "github.com/vishvananda/netns" +) + +type tearDownNetlinkTest func() + +func skipUnlessRoot(t *testing.T) { + if os.Getuid() != 0 { + msg := "Skipped test because it requires root privileges." + log.Printf(msg) + t.Skip(msg) + } +} + +func setUpNetlinkTest(t *testing.T) tearDownNetlinkTest { + skipUnlessRoot(t) + + // new temporary namespace so we don't pollute the host + // lock thread since the namespace is thread local + runtime.LockOSThread() + var err error + ns, err := netns.New() + if err != nil { + t.Fatal("Failed to create newns", ns) + } + + return func() { + ns.Close() + runtime.UnlockOSThread() + } +} + +func setUpMPLSNetlinkTest(t *testing.T) tearDownNetlinkTest { + if _, err := os.Stat("/proc/sys/net/mpls/platform_labels"); err != nil { + msg := "Skipped test because it requires MPLS support." + log.Printf(msg) + t.Skip(msg) + } + f := setUpNetlinkTest(t) + setUpF := func(path, value string) { + file, err := os.Create(path) + defer file.Close() + if err != nil { + t.Fatalf("Failed to open %s: %s", path, err) + } + file.WriteString(value) + } + setUpF("/proc/sys/net/mpls/platform_labels", "1024") + setUpF("/proc/sys/net/mpls/conf/lo/input", "1") + return f +} diff --git a/vendor/github.com/vishvananda/netlink/netlink_unspecified.go b/vendor/github.com/vishvananda/netlink/netlink_unspecified.go index fa421e4e..2d57c16d 100644 --- a/vendor/github.com/vishvananda/netlink/netlink_unspecified.go +++ b/vendor/github.com/vishvananda/netlink/netlink_unspecified.go @@ -16,7 +16,7 @@ func LinkSetMTU(link Link, mtu int) error { return ErrNotImplemented } -func LinkSetMaster(link Link, master *Link) error { +func LinkSetMaster(link Link, master *Bridge) error { return ErrNotImplemented } @@ -64,6 +64,14 @@ func LinkSetXdpFd(link Link, fd int) error { return ErrNotImplemented } +func LinkSetARPOff(link Link) error { + return ErrNotImplemented +} + +func LinkSetARPOn(link Link) error { + return ErrNotImplemented +} + func LinkByName(name string) (Link, error) { return nil, ErrNotImplemented } diff --git a/vendor/github.com/vishvananda/netlink/nl/addr_linux.go b/vendor/github.com/vishvananda/netlink/nl/addr_linux.go index 17088fa0..fe362e9f 100644 --- a/vendor/github.com/vishvananda/netlink/nl/addr_linux.go +++ b/vendor/github.com/vishvananda/netlink/nl/addr_linux.go @@ -45,3 +45,32 @@ func (msg *IfAddrmsg) Serialize() []byte { func (msg *IfAddrmsg) Len() int { return syscall.SizeofIfAddrmsg } + +// struct ifa_cacheinfo { +// __u32 ifa_prefered; +// __u32 ifa_valid; +// __u32 cstamp; /* created timestamp, hundredths of seconds */ +// __u32 tstamp; /* updated timestamp, hundredths of seconds */ +// }; + +const IFA_CACHEINFO = 6 +const SizeofIfaCacheInfo = 0x10 + +type IfaCacheInfo struct { + IfaPrefered uint32 + IfaValid uint32 + Cstamp uint32 + Tstamp uint32 +} + +func (msg *IfaCacheInfo) Len() int { + return SizeofIfaCacheInfo +} + +func DeserializeIfaCacheInfo(b []byte) *IfaCacheInfo { + return (*IfaCacheInfo)(unsafe.Pointer(&b[0:SizeofIfaCacheInfo][0])) +} + +func (msg *IfaCacheInfo) Serialize() []byte { + return (*(*[SizeofIfaCacheInfo]byte)(unsafe.Pointer(msg)))[:] +} diff --git a/vendor/github.com/vishvananda/netlink/nl/addr_linux_test.go b/vendor/github.com/vishvananda/netlink/nl/addr_linux_test.go new file mode 100644 index 00000000..07b02014 --- /dev/null +++ b/vendor/github.com/vishvananda/netlink/nl/addr_linux_test.go @@ -0,0 +1,68 @@ +package nl + +import ( + "bytes" + "crypto/rand" + "encoding/binary" + "syscall" + "testing" +) + +func (msg *IfAddrmsg) write(b []byte) { + native := NativeEndian() + b[0] = msg.Family + b[1] = msg.Prefixlen + b[2] = msg.Flags + b[3] = msg.Scope + native.PutUint32(b[4:8], msg.Index) +} + +func (msg *IfAddrmsg) serializeSafe() []byte { + len := syscall.SizeofIfAddrmsg + b := make([]byte, len) + msg.write(b) + return b +} + +func deserializeIfAddrmsgSafe(b []byte) *IfAddrmsg { + var msg = IfAddrmsg{} + binary.Read(bytes.NewReader(b[0:syscall.SizeofIfAddrmsg]), NativeEndian(), &msg) + return &msg +} + +func TestIfAddrmsgDeserializeSerialize(t *testing.T) { + var orig = make([]byte, syscall.SizeofIfAddrmsg) + rand.Read(orig) + safemsg := deserializeIfAddrmsgSafe(orig) + msg := DeserializeIfAddrmsg(orig) + testDeserializeSerialize(t, orig, safemsg, msg) +} + +func (msg *IfaCacheInfo) write(b []byte) { + native := NativeEndian() + native.PutUint32(b[0:4], uint32(msg.IfaPrefered)) + native.PutUint32(b[4:8], uint32(msg.IfaValid)) + native.PutUint32(b[8:12], uint32(msg.Cstamp)) + native.PutUint32(b[12:16], uint32(msg.Tstamp)) +} + +func (msg *IfaCacheInfo) serializeSafe() []byte { + length := SizeofIfaCacheInfo + b := make([]byte, length) + msg.write(b) + return b +} + +func deserializeIfaCacheInfoSafe(b []byte) *IfaCacheInfo { + var msg = IfaCacheInfo{} + binary.Read(bytes.NewReader(b[0:SizeofIfaCacheInfo]), NativeEndian(), &msg) + return &msg +} + +func TestIfaCacheInfoDeserializeSerialize(t *testing.T) { + var orig = make([]byte, SizeofIfaCacheInfo) + rand.Read(orig) + safemsg := deserializeIfaCacheInfoSafe(orig) + msg := DeserializeIfaCacheInfo(orig) + testDeserializeSerialize(t, orig, safemsg, msg) +} diff --git a/vendor/github.com/vishvananda/netlink/nl/conntrack_linux.go b/vendor/github.com/vishvananda/netlink/nl/conntrack_linux.go new file mode 100644 index 00000000..6692b53e --- /dev/null +++ b/vendor/github.com/vishvananda/netlink/nl/conntrack_linux.go @@ -0,0 +1,189 @@ +package nl + +import "unsafe" + +// Track the message sizes for the correct serialization/deserialization +const ( + SizeofNfgenmsg = 4 + SizeofNfattr = 4 + SizeofNfConntrack = 376 + SizeofNfctTupleHead = 52 +) + +var L4ProtoMap = map[uint8]string{ + 6: "tcp", + 17: "udp", +} + +// All the following constants are coming from: +// https://github.com/torvalds/linux/blob/master/include/uapi/linux/netfilter/nfnetlink_conntrack.h + +// enum cntl_msg_types { +// IPCTNL_MSG_CT_NEW, +// IPCTNL_MSG_CT_GET, +// IPCTNL_MSG_CT_DELETE, +// IPCTNL_MSG_CT_GET_CTRZERO, +// IPCTNL_MSG_CT_GET_STATS_CPU, +// IPCTNL_MSG_CT_GET_STATS, +// IPCTNL_MSG_CT_GET_DYING, +// IPCTNL_MSG_CT_GET_UNCONFIRMED, +// +// IPCTNL_MSG_MAX +// }; +const ( + IPCTNL_MSG_CT_GET = 1 + IPCTNL_MSG_CT_DELETE = 2 +) + +// #define NFNETLINK_V0 0 +const ( + NFNETLINK_V0 = 0 +) + +// #define NLA_F_NESTED (1 << 15) +const ( + NLA_F_NESTED = (1 << 15) +) + +// enum ctattr_type { +// CTA_UNSPEC, +// CTA_TUPLE_ORIG, +// CTA_TUPLE_REPLY, +// CTA_STATUS, +// CTA_PROTOINFO, +// CTA_HELP, +// CTA_NAT_SRC, +// #define CTA_NAT CTA_NAT_SRC /* backwards compatibility */ +// CTA_TIMEOUT, +// CTA_MARK, +// CTA_COUNTERS_ORIG, +// CTA_COUNTERS_REPLY, +// CTA_USE, +// CTA_ID, +// CTA_NAT_DST, +// CTA_TUPLE_MASTER, +// CTA_SEQ_ADJ_ORIG, +// CTA_NAT_SEQ_ADJ_ORIG = CTA_SEQ_ADJ_ORIG, +// CTA_SEQ_ADJ_REPLY, +// CTA_NAT_SEQ_ADJ_REPLY = CTA_SEQ_ADJ_REPLY, +// CTA_SECMARK, /* obsolete */ +// CTA_ZONE, +// CTA_SECCTX, +// CTA_TIMESTAMP, +// CTA_MARK_MASK, +// CTA_LABELS, +// CTA_LABELS_MASK, +// __CTA_MAX +// }; +const ( + CTA_TUPLE_ORIG = 1 + CTA_TUPLE_REPLY = 2 + CTA_STATUS = 3 + CTA_TIMEOUT = 8 + CTA_MARK = 9 + CTA_PROTOINFO = 4 +) + +// enum ctattr_tuple { +// CTA_TUPLE_UNSPEC, +// CTA_TUPLE_IP, +// CTA_TUPLE_PROTO, +// CTA_TUPLE_ZONE, +// __CTA_TUPLE_MAX +// }; +// #define CTA_TUPLE_MAX (__CTA_TUPLE_MAX - 1) +const ( + CTA_TUPLE_IP = 1 + CTA_TUPLE_PROTO = 2 +) + +// enum ctattr_ip { +// CTA_IP_UNSPEC, +// CTA_IP_V4_SRC, +// CTA_IP_V4_DST, +// CTA_IP_V6_SRC, +// CTA_IP_V6_DST, +// __CTA_IP_MAX +// }; +// #define CTA_IP_MAX (__CTA_IP_MAX - 1) +const ( + CTA_IP_V4_SRC = 1 + CTA_IP_V4_DST = 2 + CTA_IP_V6_SRC = 3 + CTA_IP_V6_DST = 4 +) + +// enum ctattr_l4proto { +// CTA_PROTO_UNSPEC, +// CTA_PROTO_NUM, +// CTA_PROTO_SRC_PORT, +// CTA_PROTO_DST_PORT, +// CTA_PROTO_ICMP_ID, +// CTA_PROTO_ICMP_TYPE, +// CTA_PROTO_ICMP_CODE, +// CTA_PROTO_ICMPV6_ID, +// CTA_PROTO_ICMPV6_TYPE, +// CTA_PROTO_ICMPV6_CODE, +// __CTA_PROTO_MAX +// }; +// #define CTA_PROTO_MAX (__CTA_PROTO_MAX - 1) +const ( + CTA_PROTO_NUM = 1 + CTA_PROTO_SRC_PORT = 2 + CTA_PROTO_DST_PORT = 3 +) + +// enum ctattr_protoinfo { +// CTA_PROTOINFO_UNSPEC, +// CTA_PROTOINFO_TCP, +// CTA_PROTOINFO_DCCP, +// CTA_PROTOINFO_SCTP, +// __CTA_PROTOINFO_MAX +// }; +// #define CTA_PROTOINFO_MAX (__CTA_PROTOINFO_MAX - 1) +const ( + CTA_PROTOINFO_TCP = 1 +) + +// enum ctattr_protoinfo_tcp { +// CTA_PROTOINFO_TCP_UNSPEC, +// CTA_PROTOINFO_TCP_STATE, +// CTA_PROTOINFO_TCP_WSCALE_ORIGINAL, +// CTA_PROTOINFO_TCP_WSCALE_REPLY, +// CTA_PROTOINFO_TCP_FLAGS_ORIGINAL, +// CTA_PROTOINFO_TCP_FLAGS_REPLY, +// __CTA_PROTOINFO_TCP_MAX +// }; +// #define CTA_PROTOINFO_TCP_MAX (__CTA_PROTOINFO_TCP_MAX - 1) +const ( + CTA_PROTOINFO_TCP_STATE = 1 + CTA_PROTOINFO_TCP_WSCALE_ORIGINAL = 2 + CTA_PROTOINFO_TCP_WSCALE_REPLY = 3 + CTA_PROTOINFO_TCP_FLAGS_ORIGINAL = 4 + CTA_PROTOINFO_TCP_FLAGS_REPLY = 5 +) + +// /* General form of address family dependent message. +// */ +// struct nfgenmsg { +// __u8 nfgen_family; /* AF_xxx */ +// __u8 version; /* nfnetlink version */ +// __be16 res_id; /* resource id */ +// }; +type Nfgenmsg struct { + NfgenFamily uint8 + Version uint8 + ResId uint16 // big endian +} + +func (msg *Nfgenmsg) Len() int { + return SizeofNfgenmsg +} + +func DeserializeNfgenmsg(b []byte) *Nfgenmsg { + return (*Nfgenmsg)(unsafe.Pointer(&b[0:SizeofNfgenmsg][0])) +} + +func (msg *Nfgenmsg) Serialize() []byte { + return (*(*[SizeofNfgenmsg]byte)(unsafe.Pointer(msg)))[:] +} diff --git a/vendor/github.com/vishvananda/netlink/nl/link_linux.go b/vendor/github.com/vishvananda/netlink/nl/link_linux.go index 6d9af569..dd038529 100644 --- a/vendor/github.com/vishvananda/netlink/nl/link_linux.go +++ b/vendor/github.com/vishvananda/netlink/nl/link_linux.go @@ -102,7 +102,10 @@ const ( IFLA_BRPORT_FAST_LEAVE IFLA_BRPORT_LEARNING IFLA_BRPORT_UNICAST_FLOOD - IFLA_BRPORT_MAX = IFLA_BRPORT_UNICAST_FLOOD + IFLA_BRPORT_PROXYARP + IFLA_BRPORT_LEARNING_SYNC + IFLA_BRPORT_PROXYARP_WIFI + IFLA_BRPORT_MAX = IFLA_BRPORT_PROXYARP_WIFI ) const ( diff --git a/vendor/github.com/vishvananda/netlink/nl/link_linux_test.go b/vendor/github.com/vishvananda/netlink/nl/link_linux_test.go new file mode 100644 index 00000000..ec8dff0c --- /dev/null +++ b/vendor/github.com/vishvananda/netlink/nl/link_linux_test.go @@ -0,0 +1,199 @@ +package nl + +import ( + "bytes" + "crypto/rand" + "encoding/binary" + "testing" +) + +func (msg *VfMac) write(b []byte) { + native := NativeEndian() + native.PutUint32(b[0:4], uint32(msg.Vf)) + copy(b[4:36], msg.Mac[:]) +} + +func (msg *VfMac) serializeSafe() []byte { + length := SizeofVfMac + b := make([]byte, length) + msg.write(b) + return b +} + +func deserializeVfMacSafe(b []byte) *VfMac { + var msg = VfMac{} + binary.Read(bytes.NewReader(b[0:SizeofVfMac]), NativeEndian(), &msg) + return &msg +} + +func TestVfMacDeserializeSerialize(t *testing.T) { + var orig = make([]byte, SizeofVfMac) + rand.Read(orig) + safemsg := deserializeVfMacSafe(orig) + msg := DeserializeVfMac(orig) + testDeserializeSerialize(t, orig, safemsg, msg) +} + +func (msg *VfVlan) write(b []byte) { + native := NativeEndian() + native.PutUint32(b[0:4], uint32(msg.Vf)) + native.PutUint32(b[4:8], uint32(msg.Vlan)) + native.PutUint32(b[8:12], uint32(msg.Qos)) +} + +func (msg *VfVlan) serializeSafe() []byte { + length := SizeofVfVlan + b := make([]byte, length) + msg.write(b) + return b +} + +func deserializeVfVlanSafe(b []byte) *VfVlan { + var msg = VfVlan{} + binary.Read(bytes.NewReader(b[0:SizeofVfVlan]), NativeEndian(), &msg) + return &msg +} + +func TestVfVlanDeserializeSerialize(t *testing.T) { + var orig = make([]byte, SizeofVfVlan) + rand.Read(orig) + safemsg := deserializeVfVlanSafe(orig) + msg := DeserializeVfVlan(orig) + testDeserializeSerialize(t, orig, safemsg, msg) +} + +func (msg *VfTxRate) write(b []byte) { + native := NativeEndian() + native.PutUint32(b[0:4], uint32(msg.Vf)) + native.PutUint32(b[4:8], uint32(msg.Rate)) +} + +func (msg *VfTxRate) serializeSafe() []byte { + length := SizeofVfTxRate + b := make([]byte, length) + msg.write(b) + return b +} + +func deserializeVfTxRateSafe(b []byte) *VfTxRate { + var msg = VfTxRate{} + binary.Read(bytes.NewReader(b[0:SizeofVfTxRate]), NativeEndian(), &msg) + return &msg +} + +func TestVfTxRateDeserializeSerialize(t *testing.T) { + var orig = make([]byte, SizeofVfTxRate) + rand.Read(orig) + safemsg := deserializeVfTxRateSafe(orig) + msg := DeserializeVfTxRate(orig) + testDeserializeSerialize(t, orig, safemsg, msg) +} + +func (msg *VfRate) write(b []byte) { + native := NativeEndian() + native.PutUint32(b[0:4], uint32(msg.Vf)) + native.PutUint32(b[4:8], uint32(msg.MinTxRate)) + native.PutUint32(b[8:12], uint32(msg.MaxTxRate)) +} + +func (msg *VfRate) serializeSafe() []byte { + length := SizeofVfRate + b := make([]byte, length) + msg.write(b) + return b +} + +func deserializeVfRateSafe(b []byte) *VfRate { + var msg = VfRate{} + binary.Read(bytes.NewReader(b[0:SizeofVfRate]), NativeEndian(), &msg) + return &msg +} + +func TestVfRateDeserializeSerialize(t *testing.T) { + var orig = make([]byte, SizeofVfRate) + rand.Read(orig) + safemsg := deserializeVfRateSafe(orig) + msg := DeserializeVfRate(orig) + testDeserializeSerialize(t, orig, safemsg, msg) +} + +func (msg *VfSpoofchk) write(b []byte) { + native := NativeEndian() + native.PutUint32(b[0:4], uint32(msg.Vf)) + native.PutUint32(b[4:8], uint32(msg.Setting)) +} + +func (msg *VfSpoofchk) serializeSafe() []byte { + length := SizeofVfSpoofchk + b := make([]byte, length) + msg.write(b) + return b +} + +func deserializeVfSpoofchkSafe(b []byte) *VfSpoofchk { + var msg = VfSpoofchk{} + binary.Read(bytes.NewReader(b[0:SizeofVfSpoofchk]), NativeEndian(), &msg) + return &msg +} + +func TestVfSpoofchkDeserializeSerialize(t *testing.T) { + var orig = make([]byte, SizeofVfSpoofchk) + rand.Read(orig) + safemsg := deserializeVfSpoofchkSafe(orig) + msg := DeserializeVfSpoofchk(orig) + testDeserializeSerialize(t, orig, safemsg, msg) +} + +func (msg *VfLinkState) write(b []byte) { + native := NativeEndian() + native.PutUint32(b[0:4], uint32(msg.Vf)) + native.PutUint32(b[4:8], uint32(msg.LinkState)) +} + +func (msg *VfLinkState) serializeSafe() []byte { + length := SizeofVfLinkState + b := make([]byte, length) + msg.write(b) + return b +} + +func deserializeVfLinkStateSafe(b []byte) *VfLinkState { + var msg = VfLinkState{} + binary.Read(bytes.NewReader(b[0:SizeofVfLinkState]), NativeEndian(), &msg) + return &msg +} + +func TestVfLinkStateDeserializeSerialize(t *testing.T) { + var orig = make([]byte, SizeofVfLinkState) + rand.Read(orig) + safemsg := deserializeVfLinkStateSafe(orig) + msg := DeserializeVfLinkState(orig) + testDeserializeSerialize(t, orig, safemsg, msg) +} + +func (msg *VfRssQueryEn) write(b []byte) { + native := NativeEndian() + native.PutUint32(b[0:4], uint32(msg.Vf)) + native.PutUint32(b[4:8], uint32(msg.Setting)) +} + +func (msg *VfRssQueryEn) serializeSafe() []byte { + length := SizeofVfRssQueryEn + b := make([]byte, length) + msg.write(b) + return b +} + +func deserializeVfRssQueryEnSafe(b []byte) *VfRssQueryEn { + var msg = VfRssQueryEn{} + binary.Read(bytes.NewReader(b[0:SizeofVfRssQueryEn]), NativeEndian(), &msg) + return &msg +} + +func TestVfRssQueryEnDeserializeSerialize(t *testing.T) { + var orig = make([]byte, SizeofVfRssQueryEn) + rand.Read(orig) + safemsg := deserializeVfRssQueryEnSafe(orig) + msg := DeserializeVfRssQueryEn(orig) + testDeserializeSerialize(t, orig, safemsg, msg) +} diff --git a/vendor/github.com/vishvananda/netlink/nl/nl_linux.go b/vendor/github.com/vishvananda/netlink/nl/nl_linux.go index fb9031e3..5820e842 100644 --- a/vendor/github.com/vishvananda/netlink/nl/nl_linux.go +++ b/vendor/github.com/vishvananda/netlink/nl/nl_linux.go @@ -24,7 +24,7 @@ const ( ) // SupportedNlFamilies contains the list of netlink families this netlink package supports -var SupportedNlFamilies = []int{syscall.NETLINK_ROUTE, syscall.NETLINK_XFRM} +var SupportedNlFamilies = []int{syscall.NETLINK_ROUTE, syscall.NETLINK_XFRM, syscall.NETLINK_NETFILTER} var nextSeqNr uint32 @@ -321,6 +321,7 @@ func (a *RtAttr) Serialize() []byte { type NetlinkRequest struct { syscall.NlMsghdr Data []NetlinkRequestData + RawData []byte Sockets map[int]*SocketHandle } @@ -332,6 +333,8 @@ func (req *NetlinkRequest) Serialize() []byte { dataBytes[i] = data.Serialize() length = length + len(dataBytes[i]) } + length += len(req.RawData) + req.Len = uint32(length) b := make([]byte, length) hdr := (*(*[syscall.SizeofNlMsghdr]byte)(unsafe.Pointer(req)))[:] @@ -343,6 +346,10 @@ func (req *NetlinkRequest) Serialize() []byte { next = next + 1 } } + // Add the raw data if any + if len(req.RawData) > 0 { + copy(b[next:length], req.RawData) + } return b } @@ -352,6 +359,13 @@ func (req *NetlinkRequest) AddData(data NetlinkRequestData) { } } +// AddRawData adds raw bytes to the end of the NetlinkRequest object during serialization +func (req *NetlinkRequest) AddRawData(data []byte) { + if data != nil { + req.RawData = append(req.RawData, data...) + } +} + // Execute the request against a the given sockType. // Returns a list of netlink messages in serialized format, optionally filtered // by resType. @@ -451,7 +465,7 @@ type NetlinkSocket struct { } func getNetlinkSocket(protocol int) (*NetlinkSocket, error) { - fd, err := syscall.Socket(syscall.AF_NETLINK, syscall.SOCK_RAW, protocol) + fd, err := syscall.Socket(syscall.AF_NETLINK, syscall.SOCK_RAW|syscall.SOCK_CLOEXEC, protocol) if err != nil { return nil, err } diff --git a/vendor/github.com/vishvananda/netlink/nl/nl_linux_test.go b/vendor/github.com/vishvananda/netlink/nl/nl_linux_test.go new file mode 100644 index 00000000..521a7ef3 --- /dev/null +++ b/vendor/github.com/vishvananda/netlink/nl/nl_linux_test.go @@ -0,0 +1,62 @@ +package nl + +import ( + "bytes" + "crypto/rand" + "encoding/binary" + "reflect" + "syscall" + "testing" +) + +type testSerializer interface { + serializeSafe() []byte + Serialize() []byte +} + +func testDeserializeSerialize(t *testing.T, orig []byte, safemsg testSerializer, msg testSerializer) { + if !reflect.DeepEqual(safemsg, msg) { + t.Fatal("Deserialization failed.\n", safemsg, "\n", msg) + } + safe := msg.serializeSafe() + if !bytes.Equal(safe, orig) { + t.Fatal("Safe serialization failed.\n", safe, "\n", orig) + } + b := msg.Serialize() + if !bytes.Equal(b, safe) { + t.Fatal("Serialization failed.\n", b, "\n", safe) + } +} + +func (msg *IfInfomsg) write(b []byte) { + native := NativeEndian() + b[0] = msg.Family + // pad byte is skipped because it is not exported on linux/s390x + native.PutUint16(b[2:4], msg.Type) + native.PutUint32(b[4:8], uint32(msg.Index)) + native.PutUint32(b[8:12], msg.Flags) + native.PutUint32(b[12:16], msg.Change) +} + +func (msg *IfInfomsg) serializeSafe() []byte { + length := syscall.SizeofIfInfomsg + b := make([]byte, length) + msg.write(b) + return b +} + +func deserializeIfInfomsgSafe(b []byte) *IfInfomsg { + var msg = IfInfomsg{} + binary.Read(bytes.NewReader(b[0:syscall.SizeofIfInfomsg]), NativeEndian(), &msg) + return &msg +} + +func TestIfInfomsgDeserializeSerialize(t *testing.T) { + var orig = make([]byte, syscall.SizeofIfInfomsg) + rand.Read(orig) + // zero out the pad byte + orig[1] = 0 + safemsg := deserializeIfInfomsgSafe(orig) + msg := DeserializeIfInfomsg(orig) + testDeserializeSerialize(t, orig, safemsg, msg) +} diff --git a/vendor/github.com/vishvananda/netlink/nl/route_linux_test.go b/vendor/github.com/vishvananda/netlink/nl/route_linux_test.go new file mode 100644 index 00000000..ba9c410e --- /dev/null +++ b/vendor/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) +} diff --git a/vendor/github.com/vishvananda/netlink/nl/tc_linux_test.go b/vendor/github.com/vishvananda/netlink/nl/tc_linux_test.go new file mode 100644 index 00000000..148b2b02 --- /dev/null +++ b/vendor/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) +} diff --git a/vendor/github.com/vishvananda/netlink/nl/xfrm_linux_test.go b/vendor/github.com/vishvananda/netlink/nl/xfrm_linux_test.go new file mode 100644 index 00000000..04404d75 --- /dev/null +++ b/vendor/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) +} diff --git a/vendor/github.com/vishvananda/netlink/nl/xfrm_monitor_linux_test.go b/vendor/github.com/vishvananda/netlink/nl/xfrm_monitor_linux_test.go new file mode 100644 index 00000000..3602caf7 --- /dev/null +++ b/vendor/github.com/vishvananda/netlink/nl/xfrm_monitor_linux_test.go @@ -0,0 +1,34 @@ +package nl + +import ( + "bytes" + "crypto/rand" + "encoding/binary" + "testing" +) + +func (msg *XfrmUserExpire) write(b []byte) { + msg.XfrmUsersaInfo.write(b[0:SizeofXfrmUsersaInfo]) + b[SizeofXfrmUsersaInfo] = msg.Hard + copy(b[SizeofXfrmUsersaInfo+1:SizeofXfrmUserExpire], msg.Pad[:]) +} + +func (msg *XfrmUserExpire) serializeSafe() []byte { + b := make([]byte, SizeofXfrmUserExpire) + msg.write(b) + return b +} + +func deserializeXfrmUserExpireSafe(b []byte) *XfrmUserExpire { + var msg = XfrmUserExpire{} + binary.Read(bytes.NewReader(b[0:SizeofXfrmUserExpire]), NativeEndian(), &msg) + return &msg +} + +func TestXfrmUserExpireDeserializeSerialize(t *testing.T) { + var orig = make([]byte, SizeofXfrmUserExpire) + rand.Read(orig) + safemsg := deserializeXfrmUserExpireSafe(orig) + msg := DeserializeXfrmUserExpire(orig) + testDeserializeSerialize(t, orig, safemsg, msg) +} diff --git a/vendor/github.com/vishvananda/netlink/nl/xfrm_policy_linux_test.go b/vendor/github.com/vishvananda/netlink/nl/xfrm_policy_linux_test.go new file mode 100644 index 00000000..08a604b9 --- /dev/null +++ b/vendor/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) +} diff --git a/vendor/github.com/vishvananda/netlink/nl/xfrm_state_linux_test.go b/vendor/github.com/vishvananda/netlink/nl/xfrm_state_linux_test.go new file mode 100644 index 00000000..5ede308c --- /dev/null +++ b/vendor/github.com/vishvananda/netlink/nl/xfrm_state_linux_test.go @@ -0,0 +1,304 @@ +package nl + +import ( + "bytes" + "crypto/rand" + "encoding/binary" + "testing" +) + +func (msg *XfrmUsersaId) write(b []byte) { + native := NativeEndian() + msg.Daddr.write(b[0:SizeofXfrmAddress]) + native.PutUint32(b[SizeofXfrmAddress:SizeofXfrmAddress+4], msg.Spi) + native.PutUint16(b[SizeofXfrmAddress+4:SizeofXfrmAddress+6], msg.Family) + b[SizeofXfrmAddress+6] = msg.Proto + b[SizeofXfrmAddress+7] = msg.Pad +} + +func (msg *XfrmUsersaId) serializeSafe() []byte { + b := make([]byte, SizeofXfrmUsersaId) + msg.write(b) + return b +} + +func deserializeXfrmUsersaIdSafe(b []byte) *XfrmUsersaId { + var msg = XfrmUsersaId{} + binary.Read(bytes.NewReader(b[0:SizeofXfrmUsersaId]), NativeEndian(), &msg) + return &msg +} + +func TestXfrmUsersaIdDeserializeSerialize(t *testing.T) { + var orig = make([]byte, SizeofXfrmUsersaId) + rand.Read(orig) + safemsg := deserializeXfrmUsersaIdSafe(orig) + msg := DeserializeXfrmUsersaId(orig) + testDeserializeSerialize(t, orig, safemsg, msg) +} + +func (msg *XfrmStats) write(b []byte) { + native := NativeEndian() + native.PutUint32(b[0:4], msg.ReplayWindow) + native.PutUint32(b[4:8], msg.Replay) + native.PutUint32(b[8:12], msg.IntegrityFailed) +} + +func (msg *XfrmStats) serializeSafe() []byte { + b := make([]byte, SizeofXfrmStats) + msg.write(b) + return b +} + +func deserializeXfrmStatsSafe(b []byte) *XfrmStats { + var msg = XfrmStats{} + binary.Read(bytes.NewReader(b[0:SizeofXfrmStats]), NativeEndian(), &msg) + return &msg +} + +func TestXfrmStatsDeserializeSerialize(t *testing.T) { + var orig = make([]byte, SizeofXfrmStats) + rand.Read(orig) + safemsg := deserializeXfrmStatsSafe(orig) + msg := DeserializeXfrmStats(orig) + testDeserializeSerialize(t, orig, safemsg, msg) +} + +func (msg *XfrmUsersaInfo) write(b []byte) { + const IdEnd = SizeofXfrmSelector + SizeofXfrmId + const AddressEnd = IdEnd + SizeofXfrmAddress + const CfgEnd = AddressEnd + SizeofXfrmLifetimeCfg + const CurEnd = CfgEnd + SizeofXfrmLifetimeCur + const StatsEnd = CurEnd + SizeofXfrmStats + native := NativeEndian() + msg.Sel.write(b[0:SizeofXfrmSelector]) + msg.Id.write(b[SizeofXfrmSelector:IdEnd]) + msg.Saddr.write(b[IdEnd:AddressEnd]) + msg.Lft.write(b[AddressEnd:CfgEnd]) + msg.Curlft.write(b[CfgEnd:CurEnd]) + msg.Stats.write(b[CurEnd:StatsEnd]) + native.PutUint32(b[StatsEnd:StatsEnd+4], msg.Seq) + native.PutUint32(b[StatsEnd+4:StatsEnd+8], msg.Reqid) + native.PutUint16(b[StatsEnd+8:StatsEnd+10], msg.Family) + b[StatsEnd+10] = msg.Mode + b[StatsEnd+11] = msg.ReplayWindow + b[StatsEnd+12] = msg.Flags + copy(b[StatsEnd+13:StatsEnd+20], msg.Pad[:]) +} + +func (msg *XfrmUsersaInfo) serializeSafe() []byte { + b := make([]byte, SizeofXfrmUsersaInfo) + msg.write(b) + return b +} + +func deserializeXfrmUsersaInfoSafe(b []byte) *XfrmUsersaInfo { + var msg = XfrmUsersaInfo{} + binary.Read(bytes.NewReader(b[0:SizeofXfrmUsersaInfo]), NativeEndian(), &msg) + return &msg +} + +func TestXfrmUsersaInfoDeserializeSerialize(t *testing.T) { + var orig = make([]byte, SizeofXfrmUsersaInfo) + rand.Read(orig) + safemsg := deserializeXfrmUsersaInfoSafe(orig) + msg := DeserializeXfrmUsersaInfo(orig) + testDeserializeSerialize(t, orig, safemsg, msg) +} + +func (msg *XfrmAlgo) write(b []byte) { + native := NativeEndian() + copy(b[0:64], msg.AlgName[:]) + native.PutUint32(b[64:68], msg.AlgKeyLen) + copy(b[68:msg.Len()], msg.AlgKey[:]) +} + +func (msg *XfrmAlgo) serializeSafe() []byte { + b := make([]byte, msg.Len()) + msg.write(b) + return b +} + +func (msg *XfrmUserSpiInfo) write(b []byte) { + native := NativeEndian() + msg.XfrmUsersaInfo.write(b[0:SizeofXfrmUsersaInfo]) + native.PutUint32(b[SizeofXfrmUsersaInfo:SizeofXfrmUsersaInfo+4], msg.Min) + native.PutUint32(b[SizeofXfrmUsersaInfo+4:SizeofXfrmUsersaInfo+8], msg.Max) +} + +func (msg *XfrmUserSpiInfo) serializeSafe() []byte { + b := make([]byte, SizeofXfrmUserSpiInfo) + msg.write(b) + return b +} + +func deserializeXfrmUserSpiInfoSafe(b []byte) *XfrmUserSpiInfo { + var msg = XfrmUserSpiInfo{} + binary.Read(bytes.NewReader(b[0:SizeofXfrmUserSpiInfo]), NativeEndian(), &msg) + return &msg +} + +func TestXfrmUserSpiInfoDeserializeSerialize(t *testing.T) { + var orig = make([]byte, SizeofXfrmUserSpiInfo) + rand.Read(orig) + safemsg := deserializeXfrmUserSpiInfoSafe(orig) + msg := DeserializeXfrmUserSpiInfo(orig) + testDeserializeSerialize(t, orig, safemsg, msg) +} + +func deserializeXfrmAlgoSafe(b []byte) *XfrmAlgo { + var msg = XfrmAlgo{} + copy(msg.AlgName[:], b[0:64]) + binary.Read(bytes.NewReader(b[64:68]), NativeEndian(), &msg.AlgKeyLen) + msg.AlgKey = b[68:msg.Len()] + return &msg +} + +func TestXfrmAlgoDeserializeSerialize(t *testing.T) { + native := NativeEndian() + // use a 32 byte key len + var orig = make([]byte, SizeofXfrmAlgo+32) + rand.Read(orig) + // set the key len to 256 bits + var KeyLen uint32 = 0x00000100 + // Little Endian Big Endian + // orig[64] = 0 orig[64] = 0 + // orig[65] = 1 orig[65] = 0 + // orig[66] = 0 orig[66] = 1 + // orig[67] = 0 orig[67] = 0 + native.PutUint32(orig[64:68], KeyLen) + safemsg := deserializeXfrmAlgoSafe(orig) + msg := DeserializeXfrmAlgo(orig) + testDeserializeSerialize(t, orig, safemsg, msg) +} + +func (msg *XfrmAlgoAuth) write(b []byte) { + native := NativeEndian() + copy(b[0:64], msg.AlgName[:]) + native.PutUint32(b[64:68], msg.AlgKeyLen) + native.PutUint32(b[68:72], msg.AlgTruncLen) + copy(b[72:msg.Len()], msg.AlgKey[:]) +} + +func (msg *XfrmAlgoAuth) serializeSafe() []byte { + b := make([]byte, msg.Len()) + msg.write(b) + return b +} + +func deserializeXfrmAlgoAuthSafe(b []byte) *XfrmAlgoAuth { + var msg = XfrmAlgoAuth{} + copy(msg.AlgName[:], b[0:64]) + binary.Read(bytes.NewReader(b[64:68]), NativeEndian(), &msg.AlgKeyLen) + binary.Read(bytes.NewReader(b[68:72]), NativeEndian(), &msg.AlgTruncLen) + msg.AlgKey = b[72:msg.Len()] + return &msg +} + +func TestXfrmAlgoAuthDeserializeSerialize(t *testing.T) { + native := NativeEndian() + // use a 32 byte key len + var orig = make([]byte, SizeofXfrmAlgoAuth+32) + rand.Read(orig) + // set the key len to 256 bits + var KeyLen uint32 = 0x00000100 + // Little Endian Big Endian + // orig[64] = 0 orig[64] = 0 + // orig[65] = 1 orig[65] = 0 + // orig[66] = 0 orig[66] = 1 + // orig[67] = 0 orig[67] = 0 + native.PutUint32(orig[64:68], KeyLen) + safemsg := deserializeXfrmAlgoAuthSafe(orig) + msg := DeserializeXfrmAlgoAuth(orig) + testDeserializeSerialize(t, orig, safemsg, msg) +} + +func (msg *XfrmEncapTmpl) write(b []byte) { + native := NativeEndian() + native.PutUint16(b[0:2], msg.EncapType) + native.PutUint16(b[2:4], msg.EncapSport) + native.PutUint16(b[4:6], msg.EncapDport) + copy(b[6:8], msg.Pad[:]) + msg.EncapOa.write(b[8:SizeofXfrmAddress]) +} + +func (msg *XfrmEncapTmpl) serializeSafe() []byte { + b := make([]byte, SizeofXfrmEncapTmpl) + msg.write(b) + return b +} + +func deserializeXfrmEncapTmplSafe(b []byte) *XfrmEncapTmpl { + var msg = XfrmEncapTmpl{} + binary.Read(bytes.NewReader(b[0:SizeofXfrmEncapTmpl]), NativeEndian(), &msg) + return &msg +} + +func TestXfrmEncapTmplDeserializeSerialize(t *testing.T) { + var orig = make([]byte, SizeofXfrmEncapTmpl) + rand.Read(orig) + safemsg := deserializeXfrmEncapTmplSafe(orig) + msg := DeserializeXfrmEncapTmpl(orig) + testDeserializeSerialize(t, orig, safemsg, msg) +} + +func (msg *XfrmMark) write(b []byte) { + native := NativeEndian() + native.PutUint32(b[0:4], msg.Value) + native.PutUint32(b[4:8], msg.Mask) +} + +func (msg *XfrmMark) serializeSafe() []byte { + b := make([]byte, SizeofXfrmMark) + msg.write(b) + return b +} + +func deserializeXfrmMarkSafe(b []byte) *XfrmMark { + var msg = XfrmMark{} + binary.Read(bytes.NewReader(b[0:SizeofXfrmMark]), NativeEndian(), &msg) + return &msg +} + +func TestXfrmMarkDeserializeSerialize(t *testing.T) { + var orig = make([]byte, SizeofXfrmMark) + rand.Read(orig) + safemsg := deserializeXfrmMarkSafe(orig) + msg := DeserializeXfrmMark(orig) + testDeserializeSerialize(t, orig, safemsg, msg) +} + +func (msg *XfrmAlgoAEAD) write(b []byte) { + native := NativeEndian() + copy(b[0:64], msg.AlgName[:]) + native.PutUint32(b[64:68], msg.AlgKeyLen) + native.PutUint32(b[68:72], msg.AlgICVLen) + copy(b[72:msg.Len()], msg.AlgKey[:]) +} + +func (msg *XfrmAlgoAEAD) serializeSafe() []byte { + b := make([]byte, msg.Len()) + msg.write(b) + return b +} + +func deserializeXfrmAlgoAEADSafe(b []byte) *XfrmAlgoAEAD { + var msg = XfrmAlgoAEAD{} + copy(msg.AlgName[:], b[0:64]) + binary.Read(bytes.NewReader(b[64:68]), NativeEndian(), &msg.AlgKeyLen) + binary.Read(bytes.NewReader(b[68:72]), NativeEndian(), &msg.AlgICVLen) + msg.AlgKey = b[72:msg.Len()] + return &msg +} + +func TestXfrmXfrmAlgoAeadDeserializeSerialize(t *testing.T) { + native := NativeEndian() + // use a 32 byte key len + var orig = make([]byte, SizeofXfrmAlgoAEAD+36) + rand.Read(orig) + // set the key len to (256 + 32) bits + var KeyLen uint32 = 0x00000120 + native.PutUint32(orig[64:68], KeyLen) + safemsg := deserializeXfrmAlgoAEADSafe(orig) + msg := DeserializeXfrmAlgoAEAD(orig) + testDeserializeSerialize(t, orig, safemsg, msg) +} diff --git a/vendor/github.com/vishvananda/netlink/protinfo.go b/vendor/github.com/vishvananda/netlink/protinfo.go index ead3f2f1..0087c443 100644 --- a/vendor/github.com/vishvananda/netlink/protinfo.go +++ b/vendor/github.com/vishvananda/netlink/protinfo.go @@ -6,12 +6,14 @@ import ( // Protinfo represents bridge flags from netlink. type Protinfo struct { - Hairpin bool - Guard bool - FastLeave bool - RootBlock bool - Learning bool - Flood bool + Hairpin bool + Guard bool + FastLeave bool + RootBlock bool + Learning bool + Flood bool + ProxyArp bool + ProxyArpWiFi bool } // String returns a list of enabled flags @@ -35,6 +37,12 @@ func (prot *Protinfo) String() string { if prot.Flood { boolStrings = append(boolStrings, "Flood") } + if prot.ProxyArp { + boolStrings = append(boolStrings, "ProxyArp") + } + if prot.ProxyArpWiFi { + boolStrings = append(boolStrings, "ProxyArpWiFi") + } return strings.Join(boolStrings, " ") } diff --git a/vendor/github.com/vishvananda/netlink/protinfo_linux.go b/vendor/github.com/vishvananda/netlink/protinfo_linux.go index ea726953..10dd0d53 100644 --- a/vendor/github.com/vishvananda/netlink/protinfo_linux.go +++ b/vendor/github.com/vishvananda/netlink/protinfo_linux.go @@ -64,6 +64,10 @@ func parseProtinfo(infos []syscall.NetlinkRouteAttr) *Protinfo { pi.Learning = byteToBool(info.Value[0]) case nl.IFLA_BRPORT_UNICAST_FLOOD: pi.Flood = byteToBool(info.Value[0]) + case nl.IFLA_BRPORT_PROXYARP: + pi.ProxyArp = byteToBool(info.Value[0]) + case nl.IFLA_BRPORT_PROXYARP_WIFI: + pi.ProxyArpWiFi = byteToBool(info.Value[0]) } } return &pi diff --git a/vendor/github.com/vishvananda/netlink/protinfo_test.go b/vendor/github.com/vishvananda/netlink/protinfo_test.go new file mode 100644 index 00000000..680f461d --- /dev/null +++ b/vendor/github.com/vishvananda/netlink/protinfo_test.go @@ -0,0 +1,163 @@ +// +build linux + +package netlink + +import ( + "os" + "testing" +) + +func TestProtinfo(t *testing.T) { + tearDown := setUpNetlinkTest(t) + defer tearDown() + master := &Bridge{LinkAttrs{Name: "foo"}} + if err := LinkAdd(master); err != nil { + t.Fatal(err) + } + iface1 := &Dummy{LinkAttrs{Name: "bar1", MasterIndex: master.Index}} + iface2 := &Dummy{LinkAttrs{Name: "bar2", MasterIndex: master.Index}} + iface3 := &Dummy{LinkAttrs{Name: "bar3"}} + iface4 := &Dummy{LinkAttrs{Name: "bar4", MasterIndex: master.Index}} + + if err := LinkAdd(iface1); err != nil { + t.Fatal(err) + } + if err := LinkAdd(iface2); err != nil { + t.Fatal(err) + } + if err := LinkAdd(iface3); err != nil { + t.Fatal(err) + } + if err := LinkAdd(iface4); err != nil { + t.Fatal(err) + } + + oldpi1, err := LinkGetProtinfo(iface1) + if err != nil { + t.Fatal(err) + } + oldpi2, err := LinkGetProtinfo(iface2) + if err != nil { + t.Fatal(err) + } + oldpi4, err := LinkGetProtinfo(iface4) + if err != nil { + t.Fatal(err) + } + + if err := LinkSetHairpin(iface1, true); err != nil { + t.Fatal(err) + } + + if err := LinkSetRootBlock(iface1, true); err != nil { + t.Fatal(err) + } + + pi1, err := LinkGetProtinfo(iface1) + if err != nil { + t.Fatal(err) + } + if !pi1.Hairpin { + t.Fatalf("Hairpin mode is not enabled for %s, but should", iface1.Name) + } + if !pi1.RootBlock { + t.Fatalf("RootBlock is not enabled for %s, but should", iface1.Name) + } + if pi1.ProxyArp != oldpi1.ProxyArp { + t.Fatalf("ProxyArp field was changed for %s but shouldn't", iface1.Name) + } + if pi1.ProxyArpWiFi != oldpi1.ProxyArp { + t.Fatalf("ProxyArpWiFi ProxyArp field was changed for %s but shouldn't", iface1.Name) + } + if pi1.Guard != oldpi1.Guard { + t.Fatalf("Guard field was changed for %s but shouldn't", iface1.Name) + } + if pi1.FastLeave != oldpi1.FastLeave { + t.Fatalf("FastLeave field was changed for %s but shouldn't", iface1.Name) + } + if pi1.Learning != oldpi1.Learning { + t.Fatalf("Learning field was changed for %s but shouldn't", iface1.Name) + } + if pi1.Flood != oldpi1.Flood { + t.Fatalf("Flood field was changed for %s but shouldn't", iface1.Name) + } + + if err := LinkSetGuard(iface2, true); err != nil { + t.Fatal(err) + } + if err := LinkSetLearning(iface2, false); err != nil { + t.Fatal(err) + } + pi2, err := LinkGetProtinfo(iface2) + if err != nil { + t.Fatal(err) + } + if pi2.Hairpin { + t.Fatalf("Hairpin mode is enabled for %s, but shouldn't", iface2.Name) + } + if !pi2.Guard { + t.Fatalf("Guard is not enabled for %s, but should", iface2.Name) + } + if pi2.ProxyArp != oldpi2.ProxyArp { + t.Fatalf("ProxyArp field was changed for %s but shouldn't", iface2.Name) + } + if pi2.ProxyArpWiFi != oldpi2.ProxyArpWiFi { + t.Fatalf("ProxyArpWiFi field was changed for %s but shouldn't", iface2.Name) + } + if pi2.Learning { + t.Fatalf("Learning is enabled for %s, but shouldn't", iface2.Name) + } + if pi2.RootBlock != oldpi2.RootBlock { + t.Fatalf("RootBlock field was changed for %s but shouldn't", iface2.Name) + } + if pi2.FastLeave != oldpi2.FastLeave { + t.Fatalf("FastLeave field was changed for %s but shouldn't", iface2.Name) + } + if pi2.Flood != oldpi2.Flood { + t.Fatalf("Flood field was changed for %s but shouldn't", iface2.Name) + } + + if err := LinkSetHairpin(iface3, true); err == nil || err.Error() != "operation not supported" { + t.Fatalf("Set protinfo attrs for link without master is not supported, but err: %s", err) + } + + if os.Getenv("TRAVIS_BUILD_DIR") != "" { + t.Skipf("Skipped some tests because travis kernel is to old to support BR_PROXYARP.") + } + + if err := LinkSetBrProxyArp(iface4, true); err != nil { + t.Fatal(err) + } + + if err := LinkSetBrProxyArpWiFi(iface4, true); err != nil { + t.Fatal(err) + } + pi4, err := LinkGetProtinfo(iface4) + if err != nil { + t.Fatal(err) + } + if pi4.Hairpin != oldpi4.Hairpin { + t.Fatalf("Hairpin field was changed for %s but shouldn't", iface4.Name) + } + if pi4.Guard != oldpi4.Guard { + t.Fatalf("Guard field was changed for %s but shouldn't", iface4.Name) + } + if pi4.Learning != oldpi4.Learning { + t.Fatalf("Learning field was changed for %s but shouldn't", iface4.Name) + } + if !pi4.ProxyArp { + t.Fatalf("ProxyArp is not enabled for %s, but should", iface4.Name) + } + if !pi4.ProxyArpWiFi { + t.Fatalf("ProxyArpWiFi is not enabled for %s, but should", iface4.Name) + } + if pi4.RootBlock != oldpi4.RootBlock { + t.Fatalf("RootBlock field was changed for %s but shouldn't", iface4.Name) + } + if pi4.FastLeave != oldpi4.FastLeave { + t.Fatalf("FastLeave field was changed for %s but shouldn't", iface4.Name) + } + if pi4.Flood != oldpi4.Flood { + t.Fatalf("Flood field was changed for %s but shouldn't", iface4.Name) + } +} diff --git a/vendor/github.com/vishvananda/netlink/qdisc_test.go b/vendor/github.com/vishvananda/netlink/qdisc_test.go new file mode 100644 index 00000000..dff58f9d --- /dev/null +++ b/vendor/github.com/vishvananda/netlink/qdisc_test.go @@ -0,0 +1,347 @@ +// +build linux + +package netlink + +import ( + "testing" +) + +func TestTbfAddDel(t *testing.T) { + tearDown := setUpNetlinkTest(t) + defer tearDown() + if err := LinkAdd(&Ifb{LinkAttrs{Name: "foo"}}); err != nil { + t.Fatal(err) + } + link, err := LinkByName("foo") + if err != nil { + t.Fatal(err) + } + if err := LinkSetUp(link); err != nil { + t.Fatal(err) + } + qdisc := &Tbf{ + QdiscAttrs: QdiscAttrs{ + LinkIndex: link.Attrs().Index, + Handle: MakeHandle(1, 0), + Parent: HANDLE_ROOT, + }, + Rate: 131072, + Limit: 1220703, + Buffer: 16793, + } + if err := QdiscAdd(qdisc); err != nil { + t.Fatal(err) + } + qdiscs, err := QdiscList(link) + if err != nil { + t.Fatal(err) + } + if len(qdiscs) != 1 { + t.Fatal("Failed to add qdisc") + } + tbf, ok := qdiscs[0].(*Tbf) + if !ok { + t.Fatal("Qdisc is the wrong type") + } + if tbf.Rate != qdisc.Rate { + t.Fatal("Rate doesn't match") + } + if tbf.Limit != qdisc.Limit { + t.Fatal("Limit doesn't match") + } + if tbf.Buffer != qdisc.Buffer { + t.Fatal("Buffer doesn't match") + } + if err := QdiscDel(qdisc); err != nil { + t.Fatal(err) + } + qdiscs, err = QdiscList(link) + if err != nil { + t.Fatal(err) + } + if len(qdiscs) != 0 { + t.Fatal("Failed to remove qdisc") + } +} + +func TestHtbAddDel(t *testing.T) { + tearDown := setUpNetlinkTest(t) + defer tearDown() + if err := LinkAdd(&Ifb{LinkAttrs{Name: "foo"}}); err != nil { + t.Fatal(err) + } + link, err := LinkByName("foo") + if err != nil { + t.Fatal(err) + } + if err := LinkSetUp(link); err != nil { + t.Fatal(err) + } + + attrs := QdiscAttrs{ + LinkIndex: link.Attrs().Index, + Handle: MakeHandle(1, 0), + Parent: HANDLE_ROOT, + } + + qdisc := NewHtb(attrs) + qdisc.Rate2Quantum = 5 + if err := QdiscAdd(qdisc); err != nil { + t.Fatal(err) + } + + qdiscs, err := QdiscList(link) + if err != nil { + t.Fatal(err) + } + if len(qdiscs) != 1 { + t.Fatal("Failed to add qdisc") + } + htb, ok := qdiscs[0].(*Htb) + if !ok { + t.Fatal("Qdisc is the wrong type") + } + if htb.Defcls != qdisc.Defcls { + t.Fatal("Defcls doesn't match") + } + if htb.Rate2Quantum != qdisc.Rate2Quantum { + t.Fatal("Rate2Quantum doesn't match") + } + if htb.Debug != qdisc.Debug { + t.Fatal("Debug doesn't match") + } + if err := QdiscDel(qdisc); err != nil { + t.Fatal(err) + } + qdiscs, err = QdiscList(link) + if err != nil { + t.Fatal(err) + } + if len(qdiscs) != 0 { + t.Fatal("Failed to remove qdisc") + } +} + +func TestPrioAddDel(t *testing.T) { + tearDown := setUpNetlinkTest(t) + defer tearDown() + if err := LinkAdd(&Ifb{LinkAttrs{Name: "foo"}}); err != nil { + t.Fatal(err) + } + link, err := LinkByName("foo") + if err != nil { + t.Fatal(err) + } + if err := LinkSetUp(link); err != nil { + t.Fatal(err) + } + qdisc := NewPrio(QdiscAttrs{ + LinkIndex: link.Attrs().Index, + Handle: MakeHandle(1, 0), + Parent: HANDLE_ROOT, + }) + if err := QdiscAdd(qdisc); err != nil { + t.Fatal(err) + } + qdiscs, err := QdiscList(link) + if err != nil { + t.Fatal(err) + } + if len(qdiscs) != 1 { + t.Fatal("Failed to add qdisc") + } + _, ok := qdiscs[0].(*Prio) + if !ok { + t.Fatal("Qdisc is the wrong type") + } + if err := QdiscDel(qdisc); err != nil { + t.Fatal(err) + } + qdiscs, err = QdiscList(link) + if err != nil { + t.Fatal(err) + } + if len(qdiscs) != 0 { + t.Fatal("Failed to remove qdisc") + } +} + +func TestTbfAddHtbReplaceDel(t *testing.T) { + tearDown := setUpNetlinkTest(t) + defer tearDown() + if err := LinkAdd(&Ifb{LinkAttrs{Name: "foo"}}); err != nil { + t.Fatal(err) + } + link, err := LinkByName("foo") + if err != nil { + t.Fatal(err) + } + if err := LinkSetUp(link); err != nil { + t.Fatal(err) + } + + // Add + attrs := QdiscAttrs{ + LinkIndex: link.Attrs().Index, + Handle: MakeHandle(1, 0), + Parent: HANDLE_ROOT, + } + qdisc := &Tbf{ + QdiscAttrs: attrs, + Rate: 131072, + Limit: 1220703, + Buffer: 16793, + } + if err := QdiscAdd(qdisc); err != nil { + t.Fatal(err) + } + qdiscs, err := QdiscList(link) + if err != nil { + t.Fatal(err) + } + if len(qdiscs) != 1 { + t.Fatal("Failed to add qdisc") + } + tbf, ok := qdiscs[0].(*Tbf) + if !ok { + t.Fatal("Qdisc is the wrong type") + } + if tbf.Rate != qdisc.Rate { + t.Fatal("Rate doesn't match") + } + if tbf.Limit != qdisc.Limit { + t.Fatal("Limit doesn't match") + } + if tbf.Buffer != qdisc.Buffer { + t.Fatal("Buffer doesn't match") + } + // Replace + // For replace to work, the handle MUST be different that the running one + attrs.Handle = MakeHandle(2, 0) + qdisc2 := NewHtb(attrs) + qdisc2.Rate2Quantum = 5 + if err := QdiscReplace(qdisc2); err != nil { + t.Fatal(err) + } + + qdiscs, err = QdiscList(link) + if err != nil { + t.Fatal(err) + } + if len(qdiscs) != 1 { + t.Fatal("Failed to add qdisc") + } + htb, ok := qdiscs[0].(*Htb) + if !ok { + t.Fatal("Qdisc is the wrong type") + } + if htb.Defcls != qdisc2.Defcls { + t.Fatal("Defcls doesn't match") + } + if htb.Rate2Quantum != qdisc2.Rate2Quantum { + t.Fatal("Rate2Quantum doesn't match") + } + if htb.Debug != qdisc2.Debug { + t.Fatal("Debug doesn't match") + } + + if err := QdiscDel(qdisc2); err != nil { + t.Fatal(err) + } + qdiscs, err = QdiscList(link) + if err != nil { + t.Fatal(err) + } + if len(qdiscs) != 0 { + t.Fatal("Failed to remove qdisc") + } +} + +func TestTbfAddTbfChangeDel(t *testing.T) { + tearDown := setUpNetlinkTest(t) + defer tearDown() + if err := LinkAdd(&Ifb{LinkAttrs{Name: "foo"}}); err != nil { + t.Fatal(err) + } + link, err := LinkByName("foo") + if err != nil { + t.Fatal(err) + } + if err := LinkSetUp(link); err != nil { + t.Fatal(err) + } + + // Add + attrs := QdiscAttrs{ + LinkIndex: link.Attrs().Index, + Handle: MakeHandle(1, 0), + Parent: HANDLE_ROOT, + } + qdisc := &Tbf{ + QdiscAttrs: attrs, + Rate: 131072, + Limit: 1220703, + Buffer: 16793, + } + if err := QdiscAdd(qdisc); err != nil { + t.Fatal(err) + } + qdiscs, err := QdiscList(link) + if err != nil { + t.Fatal(err) + } + if len(qdiscs) != 1 { + t.Fatal("Failed to add qdisc") + } + tbf, ok := qdiscs[0].(*Tbf) + if !ok { + t.Fatal("Qdisc is the wrong type") + } + if tbf.Rate != qdisc.Rate { + t.Fatal("Rate doesn't match") + } + if tbf.Limit != qdisc.Limit { + t.Fatal("Limit doesn't match") + } + if tbf.Buffer != qdisc.Buffer { + t.Fatal("Buffer doesn't match") + } + // Change + // For change to work, the handle MUST not change + qdisc.Rate = 23456 + if err := QdiscChange(qdisc); err != nil { + t.Fatal(err) + } + + qdiscs, err = QdiscList(link) + if err != nil { + t.Fatal(err) + } + if len(qdiscs) != 1 { + t.Fatal("Failed to add qdisc") + } + tbf, ok = qdiscs[0].(*Tbf) + if !ok { + t.Fatal("Qdisc is the wrong type") + } + if tbf.Rate != qdisc.Rate { + t.Fatal("Rate doesn't match") + } + if tbf.Limit != qdisc.Limit { + t.Fatal("Limit doesn't match") + } + if tbf.Buffer != qdisc.Buffer { + t.Fatal("Buffer doesn't match") + } + + if err := QdiscDel(qdisc); err != nil { + t.Fatal(err) + } + qdiscs, err = QdiscList(link) + if err != nil { + t.Fatal(err) + } + if len(qdiscs) != 0 { + t.Fatal("Failed to remove qdisc") + } +} diff --git a/vendor/github.com/vishvananda/netlink/route_test.go b/vendor/github.com/vishvananda/netlink/route_test.go new file mode 100644 index 00000000..2435001c --- /dev/null +++ b/vendor/github.com/vishvananda/netlink/route_test.go @@ -0,0 +1,500 @@ +// +build linux + +package netlink + +import ( + "net" + "syscall" + "testing" + "time" + + "github.com/vishvananda/netns" +) + +func TestRouteAddDel(t *testing.T) { + tearDown := setUpNetlinkTest(t) + defer tearDown() + + // get loopback interface + link, err := LinkByName("lo") + if err != nil { + t.Fatal(err) + } + + // bring the interface up + if err := LinkSetUp(link); err != nil { + t.Fatal(err) + } + + // add a gateway route + dst := &net.IPNet{ + IP: net.IPv4(192, 168, 0, 0), + Mask: net.CIDRMask(24, 32), + } + + ip := net.IPv4(127, 1, 1, 1) + route := Route{LinkIndex: link.Attrs().Index, Dst: dst, Src: ip} + if err := RouteAdd(&route); err != nil { + t.Fatal(err) + } + routes, err := RouteList(link, FAMILY_V4) + if err != nil { + t.Fatal(err) + } + if len(routes) != 1 { + t.Fatal("Route not added properly") + } + + dstIP := net.IPv4(192, 168, 0, 42) + routeToDstIP, err := RouteGet(dstIP) + if err != nil { + t.Fatal(err) + } + + if len(routeToDstIP) == 0 { + t.Fatal("Default route not present") + } + if err := RouteDel(&route); err != nil { + t.Fatal(err) + } + routes, err = RouteList(link, FAMILY_V4) + if err != nil { + t.Fatal(err) + } + if len(routes) != 0 { + t.Fatal("Route not removed properly") + } + +} + +func TestRouteReplace(t *testing.T) { + tearDown := setUpNetlinkTest(t) + defer tearDown() + + // get loopback interface + link, err := LinkByName("lo") + if err != nil { + t.Fatal(err) + } + + // bring the interface up + if err := LinkSetUp(link); err != nil { + t.Fatal(err) + } + + // add a gateway route + dst := &net.IPNet{ + IP: net.IPv4(192, 168, 0, 0), + Mask: net.CIDRMask(24, 32), + } + + ip := net.IPv4(127, 1, 1, 1) + route := Route{LinkIndex: link.Attrs().Index, Dst: dst, Src: ip} + if err := RouteAdd(&route); err != nil { + t.Fatal(err) + } + routes, err := RouteList(link, FAMILY_V4) + if err != nil { + t.Fatal(err) + } + if len(routes) != 1 { + t.Fatal("Route not added properly") + } + + ip = net.IPv4(127, 1, 1, 2) + route = Route{LinkIndex: link.Attrs().Index, Dst: dst, Src: ip} + if err := RouteReplace(&route); err != nil { + t.Fatal(err) + } + + routes, err = RouteList(link, FAMILY_V4) + if err != nil { + t.Fatal(err) + } + + if len(routes) != 1 || !routes[0].Src.Equal(ip) { + t.Fatal("Route not replaced properly") + } + + if err := RouteDel(&route); err != nil { + t.Fatal(err) + } + routes, err = RouteList(link, FAMILY_V4) + if err != nil { + t.Fatal(err) + } + if len(routes) != 0 { + t.Fatal("Route not removed properly") + } + +} + +func TestRouteAddIncomplete(t *testing.T) { + tearDown := setUpNetlinkTest(t) + defer tearDown() + + // get loopback interface + link, err := LinkByName("lo") + if err != nil { + t.Fatal(err) + } + + // bring the interface up + if err = LinkSetUp(link); err != nil { + t.Fatal(err) + } + + route := Route{LinkIndex: link.Attrs().Index} + if err := RouteAdd(&route); err == nil { + t.Fatal("Adding incomplete route should fail") + } +} + +func expectRouteUpdate(ch <-chan RouteUpdate, t uint16, dst net.IP) bool { + for { + timeout := time.After(time.Minute) + select { + case update := <-ch: + if update.Type == t && update.Route.Dst.IP.Equal(dst) { + return true + } + case <-timeout: + return false + } + } +} + +func TestRouteSubscribe(t *testing.T) { + tearDown := setUpNetlinkTest(t) + defer tearDown() + + ch := make(chan RouteUpdate) + done := make(chan struct{}) + defer close(done) + if err := RouteSubscribe(ch, done); err != nil { + t.Fatal(err) + } + + // get loopback interface + link, err := LinkByName("lo") + if err != nil { + t.Fatal(err) + } + + // bring the interface up + if err = LinkSetUp(link); err != nil { + t.Fatal(err) + } + + // add a gateway route + dst := &net.IPNet{ + IP: net.IPv4(192, 168, 0, 0), + Mask: net.CIDRMask(24, 32), + } + + ip := net.IPv4(127, 1, 1, 1) + route := Route{LinkIndex: link.Attrs().Index, Dst: dst, Src: ip} + if err := RouteAdd(&route); err != nil { + t.Fatal(err) + } + + if !expectRouteUpdate(ch, syscall.RTM_NEWROUTE, dst.IP) { + t.Fatal("Add update not received as expected") + } + if err := RouteDel(&route); err != nil { + t.Fatal(err) + } + if !expectRouteUpdate(ch, syscall.RTM_DELROUTE, dst.IP) { + t.Fatal("Del update not received as expected") + } +} + +func TestRouteSubscribeAt(t *testing.T) { + skipUnlessRoot(t) + + // Create an handle on a custom netns + newNs, err := netns.New() + if err != nil { + t.Fatal(err) + } + defer newNs.Close() + + nh, err := NewHandleAt(newNs) + if err != nil { + t.Fatal(err) + } + defer nh.Delete() + + // Subscribe for Route events on the custom netns + ch := make(chan RouteUpdate) + done := make(chan struct{}) + defer close(done) + if err := RouteSubscribeAt(newNs, ch, done); err != nil { + t.Fatal(err) + } + + // get loopback interface + link, err := nh.LinkByName("lo") + if err != nil { + t.Fatal(err) + } + + // bring the interface up + if err = nh.LinkSetUp(link); err != nil { + t.Fatal(err) + } + + // add a gateway route + dst := &net.IPNet{ + IP: net.IPv4(192, 169, 0, 0), + Mask: net.CIDRMask(24, 32), + } + + ip := net.IPv4(127, 100, 1, 1) + route := Route{LinkIndex: link.Attrs().Index, Dst: dst, Src: ip} + if err := nh.RouteAdd(&route); err != nil { + t.Fatal(err) + } + + if !expectRouteUpdate(ch, syscall.RTM_NEWROUTE, dst.IP) { + t.Fatal("Add update not received as expected") + } + if err := nh.RouteDel(&route); err != nil { + t.Fatal(err) + } + if !expectRouteUpdate(ch, syscall.RTM_DELROUTE, dst.IP) { + t.Fatal("Del update not received as expected") + } +} + +func TestRouteExtraFields(t *testing.T) { + tearDown := setUpNetlinkTest(t) + defer tearDown() + + // get loopback interface + link, err := LinkByName("lo") + if err != nil { + t.Fatal(err) + } + // bring the interface up + if err = LinkSetUp(link); err != nil { + t.Fatal(err) + } + + // add a gateway route + dst := &net.IPNet{ + IP: net.IPv4(1, 1, 1, 1), + Mask: net.CIDRMask(32, 32), + } + + src := net.IPv4(127, 3, 3, 3) + route := Route{ + LinkIndex: link.Attrs().Index, + Dst: dst, + Src: src, + Scope: syscall.RT_SCOPE_LINK, + Priority: 13, + Table: syscall.RT_TABLE_MAIN, + Type: syscall.RTN_UNICAST, + Tos: 14, + } + if err := RouteAdd(&route); err != nil { + t.Fatal(err) + } + routes, err := RouteListFiltered(FAMILY_V4, &Route{ + Dst: dst, + Src: src, + Scope: syscall.RT_SCOPE_LINK, + Table: syscall.RT_TABLE_MAIN, + Type: syscall.RTN_UNICAST, + Tos: 14, + }, RT_FILTER_DST|RT_FILTER_SRC|RT_FILTER_SCOPE|RT_FILTER_TABLE|RT_FILTER_TYPE|RT_FILTER_TOS) + if err != nil { + t.Fatal(err) + } + if len(routes) != 1 { + t.Fatal("Route not added properly") + } + + if routes[0].Scope != syscall.RT_SCOPE_LINK { + t.Fatal("Invalid Scope. Route not added properly") + } + if routes[0].Priority != 13 { + t.Fatal("Invalid Priority. Route not added properly") + } + if routes[0].Table != syscall.RT_TABLE_MAIN { + t.Fatal("Invalid Scope. Route not added properly") + } + if routes[0].Type != syscall.RTN_UNICAST { + t.Fatal("Invalid Type. Route not added properly") + } + if routes[0].Tos != 14 { + t.Fatal("Invalid Tos. Route not added properly") + } +} + +func TestRouteMultiPath(t *testing.T) { + tearDown := setUpNetlinkTest(t) + defer tearDown() + + // get loopback interface + link, err := LinkByName("lo") + if err != nil { + t.Fatal(err) + } + // bring the interface up + if err = LinkSetUp(link); err != nil { + t.Fatal(err) + } + + // add a gateway route + dst := &net.IPNet{ + IP: net.IPv4(192, 168, 0, 0), + Mask: net.CIDRMask(24, 32), + } + + idx := link.Attrs().Index + route := Route{Dst: dst, MultiPath: []*NexthopInfo{&NexthopInfo{LinkIndex: idx}, &NexthopInfo{LinkIndex: idx}}} + if err := RouteAdd(&route); err != nil { + t.Fatal(err) + } + routes, err := RouteList(nil, FAMILY_V4) + if err != nil { + t.Fatal(err) + } + if len(routes) != 1 { + t.Fatal("MultiPath Route not added properly") + } + if len(routes[0].MultiPath) != 2 { + t.Fatal("MultiPath Route not added properly") + } +} + +func TestFilterDefaultRoute(t *testing.T) { + tearDown := setUpNetlinkTest(t) + defer tearDown() + + // get loopback interface + link, err := LinkByName("lo") + if err != nil { + t.Fatal(err) + } + // bring the interface up + if err = LinkSetUp(link); err != nil { + t.Fatal(err) + } + + address := &Addr{ + IPNet: &net.IPNet{ + IP: net.IPv4(127, 0, 0, 2), + Mask: net.CIDRMask(24, 32), + }, + } + if err = AddrAdd(link, address); err != nil { + t.Fatal(err) + } + + // Add default route + gw := net.IPv4(127, 0, 0, 2) + + defaultRoute := Route{ + Dst: nil, + Gw: gw, + } + + if err := RouteAdd(&defaultRoute); err != nil { + t.Fatal(err) + } + + // add an extra route + dst := &net.IPNet{ + IP: net.IPv4(192, 168, 0, 0), + Mask: net.CIDRMask(24, 32), + } + + extraRoute := Route{ + Dst: dst, + Gw: gw, + } + + if err := RouteAdd(&extraRoute); err != nil { + t.Fatal(err) + } + var filterTests = []struct { + filter *Route + mask uint64 + expected net.IP + }{ + { + &Route{Dst: nil}, + RT_FILTER_DST, + gw, + }, + { + &Route{Dst: dst}, + RT_FILTER_DST, + gw, + }, + } + + for _, f := range filterTests { + routes, err := RouteListFiltered(FAMILY_V4, f.filter, f.mask) + if err != nil { + t.Fatal(err) + } + if len(routes) != 1 { + t.Fatal("Route not filtered properly") + } + if !routes[0].Gw.Equal(gw) { + t.Fatal("Unexpected Gateway") + } + } + +} + +func TestMPLSRouteAddDel(t *testing.T) { + tearDown := setUpMPLSNetlinkTest(t) + defer tearDown() + + // get loopback interface + link, err := LinkByName("lo") + if err != nil { + t.Fatal(err) + } + + // bring the interface up + if err := LinkSetUp(link); err != nil { + t.Fatal(err) + } + + mplsDst := 100 + route := Route{ + LinkIndex: link.Attrs().Index, + MPLSDst: &mplsDst, + NewDst: &MPLSDestination{ + Labels: []int{200, 300}, + }, + } + if err := RouteAdd(&route); err != nil { + t.Fatal(err) + } + routes, err := RouteList(link, FAMILY_MPLS) + if err != nil { + t.Fatal(err) + } + if len(routes) != 1 { + t.Fatal("Route not added properly") + } + + if err := RouteDel(&route); err != nil { + t.Fatal(err) + } + routes, err = RouteList(link, FAMILY_MPLS) + if err != nil { + t.Fatal(err) + } + if len(routes) != 0 { + t.Fatal("Route not removed properly") + } + +} diff --git a/vendor/github.com/vishvananda/netlink/rule_test.go b/vendor/github.com/vishvananda/netlink/rule_test.go new file mode 100644 index 00000000..78448776 --- /dev/null +++ b/vendor/github.com/vishvananda/netlink/rule_test.go @@ -0,0 +1,70 @@ +// +build linux + +package netlink + +import ( + "net" + "syscall" + "testing" +) + +func TestRuleAddDel(t *testing.T) { + skipUnlessRoot(t) + + srcNet := &net.IPNet{IP: net.IPv4(172, 16, 0, 1), Mask: net.CIDRMask(16, 32)} + dstNet := &net.IPNet{IP: net.IPv4(172, 16, 1, 1), Mask: net.CIDRMask(24, 32)} + + rulesBegin, err := RuleList(syscall.AF_INET) + if err != nil { + t.Fatal(err) + } + + rule := NewRule() + rule.Table = syscall.RT_TABLE_MAIN + rule.Src = srcNet + rule.Dst = dstNet + rule.Priority = 5 + rule.OifName = "lo" + rule.IifName = "lo" + if err := RuleAdd(rule); err != nil { + t.Fatal(err) + } + + rules, err := RuleList(syscall.AF_INET) + if err != nil { + t.Fatal(err) + } + + if len(rules) != len(rulesBegin)+1 { + t.Fatal("Rule not added properly") + } + + // find this rule + var found bool + for i := range rules { + if rules[i].Table == rule.Table && + rules[i].Src != nil && rules[i].Src.String() == srcNet.String() && + rules[i].Dst != nil && rules[i].Dst.String() == dstNet.String() && + rules[i].OifName == rule.OifName && + rules[i].Priority == rule.Priority && + rules[i].IifName == rule.IifName { + found = true + } + } + if !found { + t.Fatal("Rule has diffrent options than one added") + } + + if err := RuleDel(rule); err != nil { + t.Fatal(err) + } + + rulesEnd, err := RuleList(syscall.AF_INET) + if err != nil { + t.Fatal(err) + } + + if len(rulesEnd) != len(rulesBegin) { + t.Fatal("Rule not removed properly") + } +} diff --git a/vendor/github.com/vishvananda/netlink/socket_test.go b/vendor/github.com/vishvananda/netlink/socket_test.go new file mode 100644 index 00000000..4a24b89f --- /dev/null +++ b/vendor/github.com/vishvananda/netlink/socket_test.go @@ -0,0 +1,60 @@ +// +build linux + +package netlink + +import ( + "log" + "net" + "os" + "os/user" + "strconv" + "testing" +) + +func TestSocketGet(t *testing.T) { + if os.Getenv("TRAVIS_BUILD_DIR") != "" { + t.Skipf("Goroutines + network namespaces == inconsistent results") + } + addr, err := net.ResolveTCPAddr("tcp", "localhost:0") + if err != nil { + log.Fatal(err) + } + l, err := net.ListenTCP("tcp", addr) + if err != nil { + log.Fatal(err) + } + defer l.Close() + + conn, err := net.Dial(l.Addr().Network(), l.Addr().String()) + if err != nil { + t.Fatal(err) + } + defer conn.Close() + + localAddr := conn.LocalAddr().(*net.TCPAddr) + remoteAddr := conn.RemoteAddr().(*net.TCPAddr) + socket, err := SocketGet(localAddr, remoteAddr) + if err != nil { + t.Fatal(err) + } + + if got, want := socket.ID.Source, localAddr.IP; !got.Equal(want) { + t.Fatalf("local ip = %v, want %v", got, want) + } + if got, want := socket.ID.Destination, remoteAddr.IP; !got.Equal(want) { + t.Fatalf("remote ip = %v, want %v", got, want) + } + if got, want := int(socket.ID.SourcePort), localAddr.Port; got != want { + t.Fatalf("local port = %d, want %d", got, want) + } + if got, want := int(socket.ID.DestinationPort), remoteAddr.Port; got != want { + t.Fatalf("remote port = %d, want %d", got, want) + } + u, err := user.Current() + if err != nil { + t.Fatal(err) + } + if got, want := strconv.Itoa(int(socket.UID)), u.Uid; got != want { + t.Fatalf("UID = %s, want %s", got, want) + } +} diff --git a/vendor/github.com/vishvananda/netlink/xfrm_monitor_test.go b/vendor/github.com/vishvananda/netlink/xfrm_monitor_test.go new file mode 100644 index 00000000..74d3d813 --- /dev/null +++ b/vendor/github.com/vishvananda/netlink/xfrm_monitor_test.go @@ -0,0 +1,39 @@ +// +build linux + +package netlink + +import ( + "testing" + + "github.com/vishvananda/netlink/nl" +) + +func TestXfrmMonitorExpire(t *testing.T) { + setUpNetlinkTest(t)() + + ch := make(chan XfrmMsg) + done := make(chan struct{}) + defer close(done) + errChan := make(chan error) + if err := XfrmMonitor(ch, nil, errChan, nl.XFRM_MSG_EXPIRE); err != nil { + t.Fatal(err) + } + + // Program state with limits + state := getBaseState() + state.Limits.TimeHard = 2 + state.Limits.TimeSoft = 1 + if err := XfrmStateAdd(state); err != nil { + t.Fatal(err) + } + + msg := (<-ch).(*XfrmMsgExpire) + if msg.XfrmState.Spi != state.Spi || msg.Hard { + t.Fatal("Received unexpected msg") + } + + msg = (<-ch).(*XfrmMsgExpire) + if msg.XfrmState.Spi != state.Spi || !msg.Hard { + t.Fatal("Received unexpected msg") + } +} diff --git a/vendor/github.com/vishvananda/netlink/xfrm_policy_test.go b/vendor/github.com/vishvananda/netlink/xfrm_policy_test.go new file mode 100644 index 00000000..5b884218 --- /dev/null +++ b/vendor/github.com/vishvananda/netlink/xfrm_policy_test.go @@ -0,0 +1,199 @@ +// +build linux + +package netlink + +import ( + "bytes" + "net" + "testing" +) + +const zeroCIDR = "0.0.0.0/0" + +func TestXfrmPolicyAddUpdateDel(t *testing.T) { + tearDown := setUpNetlinkTest(t) + defer tearDown() + + policy := getPolicy() + if err := XfrmPolicyAdd(policy); err != nil { + t.Fatal(err) + } + policies, err := XfrmPolicyList(FAMILY_ALL) + if err != nil { + t.Fatal(err) + } + + if len(policies) != 1 { + t.Fatal("Policy not added properly") + } + + if !comparePolicies(policy, &policies[0]) { + t.Fatalf("unexpected policy returned.\nExpected: %v.\nGot %v", policy, policies[0]) + } + + // Look for a specific policy + sp, err := XfrmPolicyGet(policy) + if err != nil { + t.Fatal(err) + } + + if !comparePolicies(policy, sp) { + t.Fatalf("unexpected policy returned") + } + + // Modify the policy + policy.Priority = 100 + if err := XfrmPolicyUpdate(policy); err != nil { + t.Fatal(err) + } + sp, err = XfrmPolicyGet(policy) + if err != nil { + t.Fatal(err) + } + if sp.Priority != 100 { + t.Fatalf("failed to modify the policy") + } + + if err = XfrmPolicyDel(policy); err != nil { + t.Fatal(err) + } + + policies, err = XfrmPolicyList(FAMILY_ALL) + if err != nil { + t.Fatal(err) + } + if len(policies) != 0 { + t.Fatal("Policy not removed properly") + } + + // Src and dst are not mandatory field. Creation should succeed + policy.Src = nil + policy.Dst = nil + if err = XfrmPolicyAdd(policy); err != nil { + t.Fatal(err) + } + + sp, err = XfrmPolicyGet(policy) + if err != nil { + t.Fatal(err) + } + + if !comparePolicies(policy, sp) { + t.Fatalf("unexpected policy returned") + } + + if err = XfrmPolicyDel(policy); err != nil { + t.Fatal(err) + } + + if _, err := XfrmPolicyGet(policy); err == nil { + t.Fatalf("Unexpected success") + } +} + +func TestXfrmPolicyFlush(t *testing.T) { + setUpNetlinkTest(t)() + + p1 := getPolicy() + if err := XfrmPolicyAdd(p1); err != nil { + t.Fatal(err) + } + + p1.Dir = XFRM_DIR_IN + s := p1.Src + p1.Src = p1.Dst + p1.Dst = s + if err := XfrmPolicyAdd(p1); err != nil { + t.Fatal(err) + } + + policies, err := XfrmPolicyList(FAMILY_ALL) + if err != nil { + t.Fatal(err) + } + if len(policies) != 2 { + t.Fatalf("unexpected number of policies: %d", len(policies)) + } + + if err := XfrmPolicyFlush(); err != nil { + t.Fatal(err) + } + + policies, err = XfrmPolicyList(FAMILY_ALL) + if err != nil { + t.Fatal(err) + } + if len(policies) != 0 { + t.Fatalf("unexpected number of policies: %d", len(policies)) + } + +} + +func comparePolicies(a, b *XfrmPolicy) bool { + if a == b { + return true + } + if a == nil || b == nil { + return false + } + // Do not check Index which is assigned by kernel + return a.Dir == b.Dir && a.Priority == b.Priority && + compareIPNet(a.Src, b.Src) && compareIPNet(a.Dst, b.Dst) && + a.Mark.Value == b.Mark.Value && a.Mark.Mask == b.Mark.Mask && + compareTemplates(a.Tmpls, b.Tmpls) +} + +func compareTemplates(a, b []XfrmPolicyTmpl) bool { + if len(a) != len(b) { + return false + } + for i, ta := range a { + tb := b[i] + if !ta.Dst.Equal(tb.Dst) || !ta.Src.Equal(tb.Src) || ta.Spi != tb.Spi || + ta.Mode != tb.Mode || ta.Reqid != tb.Reqid || ta.Proto != tb.Proto { + return false + } + } + return true +} + +func compareIPNet(a, b *net.IPNet) bool { + if a == b { + return true + } + // For unspecified src/dst parseXfrmPolicy would set the zero address cidr + if (a == nil && b.String() == zeroCIDR) || (b == nil && a.String() == zeroCIDR) { + return true + } + if a == nil || b == nil { + return false + } + return a.IP.Equal(b.IP) && bytes.Equal(a.Mask, b.Mask) +} + +func getPolicy() *XfrmPolicy { + src, _ := ParseIPNet("127.1.1.1/32") + dst, _ := ParseIPNet("127.1.1.2/32") + policy := &XfrmPolicy{ + Src: src, + Dst: dst, + Proto: 17, + DstPort: 1234, + SrcPort: 5678, + Dir: XFRM_DIR_OUT, + Mark: &XfrmMark{ + Value: 0xabff22, + Mask: 0xffffffff, + }, + Priority: 10, + } + tmpl := XfrmPolicyTmpl{ + Src: net.ParseIP("127.0.0.1"), + Dst: net.ParseIP("127.0.0.2"), + Proto: XFRM_PROTO_ESP, + Mode: XFRM_MODE_TUNNEL, + Spi: 0xabcdef99, + } + policy.Tmpls = append(policy.Tmpls, tmpl) + return policy +} diff --git a/vendor/github.com/vishvananda/netlink/xfrm_state_test.go b/vendor/github.com/vishvananda/netlink/xfrm_state_test.go new file mode 100644 index 00000000..ec5dfb62 --- /dev/null +++ b/vendor/github.com/vishvananda/netlink/xfrm_state_test.go @@ -0,0 +1,270 @@ +// +build linux + +package netlink + +import ( + "bytes" + "encoding/hex" + "net" + "testing" +) + +func TestXfrmStateAddGetDel(t *testing.T) { + for _, s := range []*XfrmState{getBaseState(), getAeadState()} { + testXfrmStateAddGetDel(t, s) + } +} + +func testXfrmStateAddGetDel(t *testing.T, state *XfrmState) { + tearDown := setUpNetlinkTest(t) + defer tearDown() + if err := XfrmStateAdd(state); err != nil { + t.Fatal(err) + } + states, err := XfrmStateList(FAMILY_ALL) + if err != nil { + t.Fatal(err) + } + + if len(states) != 1 { + t.Fatal("State not added properly") + } + + if !compareStates(state, &states[0]) { + t.Fatalf("unexpected states returned") + } + + // Get specific state + sa, err := XfrmStateGet(state) + if err != nil { + t.Fatal(err) + } + + if !compareStates(state, sa) { + t.Fatalf("unexpected state returned") + } + + if err = XfrmStateDel(state); err != nil { + t.Fatal(err) + } + + states, err = XfrmStateList(FAMILY_ALL) + if err != nil { + t.Fatal(err) + } + if len(states) != 0 { + t.Fatal("State not removed properly") + } + + if _, err := XfrmStateGet(state); err == nil { + t.Fatalf("Unexpected success") + } +} + +func TestXfrmStateAllocSpi(t *testing.T) { + setUpNetlinkTest(t)() + + state := getBaseState() + state.Spi = 0 + state.Auth = nil + state.Crypt = nil + rstate, err := XfrmStateAllocSpi(state) + if err != nil { + t.Fatal(err) + } + if rstate.Spi == 0 { + t.Fatalf("SPI is not allocated") + } + rstate.Spi = 0 + if !compareStates(state, rstate) { + t.Fatalf("State not properly allocated") + } +} + +func TestXfrmStateFlush(t *testing.T) { + setUpNetlinkTest(t)() + + state1 := getBaseState() + state2 := getBaseState() + state2.Src = net.ParseIP("127.1.0.1") + state2.Dst = net.ParseIP("127.1.0.2") + state2.Proto = XFRM_PROTO_AH + state2.Mode = XFRM_MODE_TUNNEL + state2.Spi = 20 + state2.Mark = nil + state2.Crypt = nil + + if err := XfrmStateAdd(state1); err != nil { + t.Fatal(err) + } + if err := XfrmStateAdd(state2); err != nil { + t.Fatal(err) + } + + // flushing proto for which no state is present should return silently + if err := XfrmStateFlush(XFRM_PROTO_COMP); err != nil { + t.Fatal(err) + } + + if err := XfrmStateFlush(XFRM_PROTO_AH); err != nil { + t.Fatal(err) + } + + if _, err := XfrmStateGet(state2); err == nil { + t.Fatalf("Unexpected success") + } + + if err := XfrmStateAdd(state2); err != nil { + t.Fatal(err) + } + + if err := XfrmStateFlush(0); err != nil { + t.Fatal(err) + } + + states, err := XfrmStateList(FAMILY_ALL) + if err != nil { + t.Fatal(err) + } + if len(states) != 0 { + t.Fatal("State not flushed properly") + } + +} + +func TestXfrmStateUpdateLimits(t *testing.T) { + setUpNetlinkTest(t)() + + // Program state with limits + state := getBaseState() + state.Limits.TimeHard = 3600 + state.Limits.TimeSoft = 60 + state.Limits.PacketHard = 1000 + state.Limits.PacketSoft = 50 + state.Limits.ByteHard = 1000000 + state.Limits.ByteSoft = 50000 + state.Limits.TimeUseHard = 3000 + state.Limits.TimeUseSoft = 1500 + if err := XfrmStateAdd(state); err != nil { + t.Fatal(err) + } + // Verify limits + s, err := XfrmStateGet(state) + if err != nil { + t.Fatal(err) + } + if !compareLimits(state, s) { + t.Fatalf("Incorrect time hard/soft retrieved: %s", s.Print(true)) + } + + // Update limits + state.Limits.TimeHard = 1800 + state.Limits.TimeSoft = 30 + state.Limits.PacketHard = 500 + state.Limits.PacketSoft = 25 + state.Limits.ByteHard = 500000 + state.Limits.ByteSoft = 25000 + state.Limits.TimeUseHard = 2000 + state.Limits.TimeUseSoft = 1000 + if err := XfrmStateUpdate(state); err != nil { + t.Fatal(err) + } + + // Verify new limits + s, err = XfrmStateGet(state) + if err != nil { + t.Fatal(err) + } + if s.Limits.TimeHard != 1800 || s.Limits.TimeSoft != 30 { + t.Fatalf("Incorrect time hard retrieved: (%d, %d)", s.Limits.TimeHard, s.Limits.TimeSoft) + } +} + +func getBaseState() *XfrmState { + return &XfrmState{ + // Force 4 byte notation for the IPv4 addresses + Src: net.ParseIP("127.0.0.1").To4(), + Dst: net.ParseIP("127.0.0.2").To4(), + Proto: XFRM_PROTO_ESP, + Mode: XFRM_MODE_TUNNEL, + Spi: 1, + Auth: &XfrmStateAlgo{ + Name: "hmac(sha256)", + Key: []byte("abcdefghijklmnopqrstuvwzyzABCDEF"), + }, + Crypt: &XfrmStateAlgo{ + Name: "cbc(aes)", + Key: []byte("abcdefghijklmnopqrstuvwzyzABCDEF"), + }, + Mark: &XfrmMark{ + Value: 0x12340000, + Mask: 0xffff0000, + }, + } +} + +func getAeadState() *XfrmState { + // 128 key bits + 32 salt bits + k, _ := hex.DecodeString("d0562776bf0e75830ba3f7f8eb6c09b555aa1177") + return &XfrmState{ + // Leave IPv4 addresses in Ipv4 in IPv6 notation + Src: net.ParseIP("192.168.1.1"), + Dst: net.ParseIP("192.168.2.2"), + Proto: XFRM_PROTO_ESP, + Mode: XFRM_MODE_TUNNEL, + Spi: 2, + Aead: &XfrmStateAlgo{ + Name: "rfc4106(gcm(aes))", + Key: k, + ICVLen: 64, + }, + } +} + +func compareStates(a, b *XfrmState) bool { + if a == b { + return true + } + if a == nil || b == nil { + return false + } + return a.Src.Equal(b.Src) && a.Dst.Equal(b.Dst) && + a.Mode == b.Mode && a.Spi == b.Spi && a.Proto == b.Proto && + compareAlgo(a.Auth, b.Auth) && + compareAlgo(a.Crypt, b.Crypt) && + compareAlgo(a.Aead, b.Aead) && + compareMarks(a.Mark, b.Mark) +} + +func compareLimits(a, b *XfrmState) bool { + return a.Limits.TimeHard == b.Limits.TimeHard && + a.Limits.TimeSoft == b.Limits.TimeSoft && + a.Limits.PacketHard == b.Limits.PacketHard && + a.Limits.PacketSoft == b.Limits.PacketSoft && + a.Limits.ByteHard == b.Limits.ByteHard && + a.Limits.ByteSoft == b.Limits.ByteSoft && + a.Limits.TimeUseHard == b.Limits.TimeUseHard && + a.Limits.TimeUseSoft == b.Limits.TimeUseSoft +} + +func compareAlgo(a, b *XfrmStateAlgo) bool { + if a == b { + return true + } + if a == nil || b == nil { + return false + } + return a.Name == b.Name && bytes.Equal(a.Key, b.Key) && + (a.TruncateLen == 0 || a.TruncateLen == b.TruncateLen) && + (a.ICVLen == 0 || a.ICVLen == b.ICVLen) +} + +func compareMarks(a, b *XfrmMark) bool { + if a == b { + return true + } + if a == nil || b == nil { + return false + } + return a.Value == b.Value && a.Mask == b.Mask +}