From 89945b09c00733e82cbd69f81585ee1459fddda1 Mon Sep 17 00:00:00 2001 From: Marek Polewski Date: Thu, 26 Nov 2015 11:50:07 +0100 Subject: [PATCH 01/15] Add support for rules --- rule.go | 48 +++++++++++ rule_linux.go | 216 ++++++++++++++++++++++++++++++++++++++++++++++++++ rule_test.go | 71 +++++++++++++++++ 3 files changed, 335 insertions(+) create mode 100644 rule.go create mode 100644 rule_linux.go create mode 100644 rule_test.go diff --git a/rule.go b/rule.go new file mode 100644 index 0000000..4048077 --- /dev/null +++ b/rule.go @@ -0,0 +1,48 @@ +package netlink + +import ( + "fmt" + "net" +) + +// Flag mask for rule options. Rule.FlagMask must be set to on for option to work. +const ( + RULE_PRIORITY_MASK = 1 << (1 + iota) + RULE_FWMARK_MASK + RULE_FWMASK_MASK + RULE_FLOW_MASK + RULE_TABLE_MASK + RULE_SUPPRESS_PREFIXLEN_MASK + RULE_SUPPRESS_IFGROUP_MASK + RULE_IIFNAME_MASK + RULE_OIFNAME_MASK + RULE_GOTO_MASK +) + +// Rule represents a netlink rule. +type Rule struct { + Family int + Tos int + Scope int + Table int + Protocol int + Type int + Priority int + Mark int + Mask int + Goto int + Src *net.IPNet + Dst *net.IPNet + Flow int // IPv4 only + Flags int + IifName string + OifName string + SuppressIfgroup int + SuppressPrefixlen int + + FlagMask uint64 +} + +func (r Rule) String() string { + return fmt.Sprintf("ip rule %d: from %s table %d", r.Priority, r.Src, r.Table) +} diff --git a/rule_linux.go b/rule_linux.go new file mode 100644 index 0000000..1d6d7db --- /dev/null +++ b/rule_linux.go @@ -0,0 +1,216 @@ +package netlink + +import ( + "fmt" + "net" + "syscall" + + "github.com/vishvananda/netlink/nl" +) + +// RuleAdd adds a rule to the system. +// Equivalent to: ip rule add +func RuleAdd(rule *Rule) error { + req := nl.NewNetlinkRequest(syscall.RTM_NEWRULE, syscall.NLM_F_CREATE|syscall.NLM_F_EXCL|syscall.NLM_F_ACK) + return ruleHandle(rule, req) +} + +// RuleDel deletes a rule from the system. +// Equivalent to: ip rule del +func RuleDel(rule *Rule) error { + req := nl.NewNetlinkRequest(syscall.RTM_DELRULE, syscall.NLM_F_CREATE|syscall.NLM_F_EXCL|syscall.NLM_F_ACK) + return ruleHandle(rule, req) +} + +func ruleHandle(rule *Rule, req *nl.NetlinkRequest) error { + msg := nl.NewRtMsg() + family := syscall.AF_INET + + var rtAttrs []*nl.RtAttr + if rule.Dst != nil && rule.Dst.IP != nil { + dstLen, _ := rule.Dst.Mask.Size() + msg.Dst_len = uint8(dstLen) + family = nl.GetIPFamily(rule.Dst.IP) + var dstData []byte + if family == syscall.AF_INET { + dstData = rule.Dst.IP.To4() + } else { + dstData = rule.Dst.IP.To16() + } + rtAttrs = append(rtAttrs, nl.NewRtAttr(syscall.RTA_DST, dstData)) + } + + if rule.Src != nil && rule.Src.IP != nil { + srcFamily := nl.GetIPFamily(rule.Src.IP) + if family != -1 && family != srcFamily { + return fmt.Errorf("source and destination ip are not the same IP family") + } + srcLen, _ := rule.Src.Mask.Size() + msg.Src_len = uint8(srcLen) + family = srcFamily + var srcData []byte + if srcFamily == syscall.AF_INET { + srcData = rule.Src.IP.To4() + } else { + srcData = rule.Src.IP.To16() + } + rtAttrs = append(rtAttrs, nl.NewRtAttr(syscall.RTA_SRC, srcData)) + } + + msg.Family = uint8(family) + + if rule.Type != 0 { + msg.Type = uint8(rule.Type) + } + if rule.FlagMask&RULE_GOTO_MASK != 0 { + msg.Type = nl.FR_ACT_NOP + } + if rule.FlagMask&RULE_TABLE_MASK != 0 { + if rule.Table < 256 { + msg.Table = uint8(rule.Table) + } else { + msg.Table = syscall.RT_TABLE_UNSPEC + } + } + + req.AddData(msg) + for i := range rtAttrs { + req.AddData(rtAttrs[i]) + } + + var ( + b = make([]byte, 4) + native = nl.NativeEndian() + ) + + if rule.FlagMask&RULE_PRIORITY_MASK != 0 { + native.PutUint32(b, uint32(rule.Priority)) + req.AddData(nl.NewRtAttr(nl.FRA_PRIORITY, b)) + } + if rule.FlagMask&RULE_FWMARK_MASK != 0 { + native.PutUint32(b, uint32(rule.Mark)) + req.AddData(nl.NewRtAttr(nl.FRA_FWMARK, b)) + } + if rule.FlagMask&RULE_FWMASK_MASK != 0 { + native.PutUint32(b, uint32(rule.Mask)) + req.AddData(nl.NewRtAttr(nl.FRA_FWMASK, b)) + } + if rule.FlagMask&RULE_FLOW_MASK != 0 { + native.PutUint32(b, uint32(rule.Flow)) + req.AddData(nl.NewRtAttr(nl.FRA_FLOW, b)) + } + if rule.FlagMask&RULE_TABLE_MASK != 0 && rule.Table >= 256 { + native.PutUint32(b, uint32(rule.Table)) + req.AddData(nl.NewRtAttr(nl.FRA_TABLE, b)) + } + if msg.Table != 0 { + if rule.FlagMask&RULE_SUPPRESS_PREFIXLEN_MASK != 0 { + native.PutUint32(b, uint32(rule.SuppressPrefixlen)) + req.AddData(nl.NewRtAttr(nl.FRA_SUPPRESS_PREFIXLEN, b)) + } + if rule.FlagMask&RULE_SUPPRESS_IFGROUP_MASK != 0 { + native.PutUint32(b, uint32(rule.SuppressIfgroup)) + req.AddData(nl.NewRtAttr(nl.FRA_SUPPRESS_IFGROUP, b)) + } + } + if rule.IifName != "" { + req.AddData(nl.NewRtAttr(nl.FRA_IIFNAME, []byte(rule.IifName))) + } + if rule.OifName != "" { + req.AddData(nl.NewRtAttr(nl.FRA_OIFNAME, []byte(rule.OifName))) + } + if rule.FlagMask&RULE_GOTO_MASK != 0 { + native.PutUint32(b, uint32(rule.Goto)) + req.AddData(nl.NewRtAttr(nl.FRA_GOTO, b)) + } + + _, err := req.Execute(syscall.NETLINK_ROUTE, 0) + return err +} + +// RuleList lists rules in the system. +// Equivalent to: ip rule list +func RuleList(family int) ([]Rule, error) { + req := nl.NewNetlinkRequest(syscall.RTM_GETRULE, syscall.NLM_F_DUMP|syscall.NLM_F_REQUEST) + msg := nl.NewIfInfomsg(family) + req.AddData(msg) + + msgs, err := req.Execute(syscall.NETLINK_ROUTE, syscall.RTM_NEWRULE) + if err != nil { + return nil, err + } + + native := nl.NativeEndian() + var res = make([]Rule, 0) + for i := range msgs { + msg := nl.DeserializeRtMsg(msgs[i]) + attrs, err := nl.ParseRouteAttr(msgs[i][msg.Len():]) + if err != nil { + return nil, err + } + + rule := Rule{ + Table: int(msg.Table), + Protocol: int(msg.Protocol), + Type: int(msg.Type), + Scope: int(msg.Scope), + Tos: int(msg.Tos), + Family: int(msg.Family), + Flags: int(msg.Flags), + SuppressPrefixlen: -1, + SuppressIfgroup: -1, + } + + for j := range attrs { + switch attrs[j].Attr.Type { + case syscall.RTA_TABLE: + rule.Table = int(native.Uint32(attrs[j].Value[0:4])) + rule.FlagMask |= RULE_TABLE_MASK + case nl.FRA_SRC: + rule.Src = &net.IPNet{ + IP: attrs[j].Value, + Mask: net.CIDRMask(int(msg.Src_len), 8*len(attrs[j].Value)), + } + case nl.FRA_DST: + rule.Dst = &net.IPNet{ + IP: attrs[j].Value, + Mask: net.CIDRMask(int(msg.Dst_len), 8*len(attrs[j].Value)), + } + case nl.FRA_FWMARK: + rule.Mark = int(native.Uint32(attrs[j].Value[0:4])) + rule.FlagMask |= RULE_FWMARK_MASK + case nl.FRA_FWMASK: + rule.Mask = int(native.Uint32(attrs[j].Value[0:4])) + rule.FlagMask |= RULE_FWMASK_MASK + case nl.FRA_IIFNAME: + rule.IifName = string(attrs[j].Value[:len(attrs[j].Value)-1]) + case nl.FRA_OIFNAME: + rule.OifName = string(attrs[j].Value[:len(attrs[j].Value)-1]) + case nl.FRA_SUPPRESS_PREFIXLEN: + i := native.Uint32(attrs[j].Value[0:4]) + if i != 0xffffffff { + rule.SuppressPrefixlen = int(i) + rule.FlagMask |= RULE_SUPPRESS_PREFIXLEN_MASK + } + case nl.FRA_SUPPRESS_IFGROUP: + i := native.Uint32(attrs[j].Value[0:4]) + if i != 0xffffffff { + rule.SuppressIfgroup = int(i) + rule.FlagMask |= RULE_SUPPRESS_IFGROUP_MASK + } + case nl.FRA_FLOW: + rule.Flow = int(native.Uint32(attrs[j].Value[0:4])) + rule.FlagMask |= RULE_FLOW_MASK + case nl.FRA_GOTO: + rule.Goto = int(native.Uint32(attrs[j].Value[0:4])) + rule.FlagMask |= RULE_GOTO_MASK + case nl.FRA_PRIORITY: + rule.Priority = int(native.Uint32(attrs[j].Value[0:4])) + rule.FlagMask |= RULE_PRIORITY_MASK + } + } + res = append(res, rule) + } + + return res, nil +} diff --git a/rule_test.go b/rule_test.go new file mode 100644 index 0000000..20daa9e --- /dev/null +++ b/rule_test.go @@ -0,0 +1,71 @@ +package netlink + +import ( + "net" + "syscall" + "testing" +) + +func TestRuleAddDel(t *testing.T) { + srcNet := &net.IPNet{IP: net.IPv4(172, 16, 0, 1), Mask: net.CIDRMask(16, 32)} + dstNet := &net.IPNet{IP: net.IPv4(172, 16, 1, 1), Mask: net.CIDRMask(24, 32)} + + rules_begin, err := RuleList(syscall.AF_INET) + if err != nil { + t.Fatal(err) + } + + rule := &Rule{ + Table: syscall.RT_TABLE_MAIN, + Src: srcNet, + Dst: dstNet, + Priority: 5, + OifName: "lo", + IifName: "lo", + FlagMask: RULE_TABLE_MASK | + RULE_IIFNAME_MASK | + RULE_PRIORITY_MASK | + RULE_OIFNAME_MASK, + } + if err := RuleAdd(rule); err != nil { + t.Fatal(err) + } + + rules, err := RuleList(syscall.AF_INET) + if err != nil { + t.Fatal(err) + } + + if len(rules) != len(rules_begin)+1 { + t.Fatal("Rule not added properly") + } + + // find this rule + var found bool + for i := range rules { + if rules[i].Table == rule.Table && + rules[i].Src != nil && rules[i].Src.String() == srcNet.String() && + rules[i].Dst != nil && rules[i].Dst.String() == dstNet.String() && + rules[i].OifName == rule.OifName && + rules[i].Priority == rule.Priority && + rules[i].IifName == rule.IifName { + found = true + } + } + if !found { + t.Fatal("Rule has diffrent options than one added") + } + + if err := RuleDel(rule); err != nil { + t.Fatal(err) + } + + rules_end, err := RuleList(syscall.AF_INET) + if err != nil { + t.Fatal(err) + } + + if len(rules_end) != len(rules_begin) { + t.Fatal("Rule not removed properly") + } +} From 7d55ffec7db6bd3daf61cfb6e98fd8ff37029741 Mon Sep 17 00:00:00 2001 From: Marek Polewski Date: Thu, 26 Nov 2015 11:58:26 +0100 Subject: [PATCH 02/15] Add missing rule related syscalls const --- nl/syscall.go | 37 +++++++++++++++++++++++++++++++++++++ 1 file changed, 37 insertions(+) create mode 100644 nl/syscall.go diff --git a/nl/syscall.go b/nl/syscall.go new file mode 100644 index 0000000..e33dcf6 --- /dev/null +++ b/nl/syscall.go @@ -0,0 +1,37 @@ +package nl + +// syscall package lack of rule atributes type. +// Thus there are defined below +const ( + FRA_UNSPEC = iota + FRA_DST /* destination address */ + FRA_SRC /* source address */ + FRA_IIFNAME /* interface name */ + FRA_GOTO /* target to jump to (FR_ACT_GOTO) */ + FRA_UNUSED2 + FRA_PRIORITY /* priority/preference */ + FRA_UNUSED3 + FRA_UNUSED4 + FRA_UNUSED5 + FRA_FWMARK /* mark */ + FRA_FLOW /* flow/class id */ + FRA_UNUSED6 + FRA_SUPPRESS_IFGROUP + FRA_SUPPRESS_PREFIXLEN + FRA_TABLE /* Extended table id */ + FRA_FWMASK /* mask for netfilter mark */ + FRA_OIFNAME +) + +// ip rule netlink request types +const ( + FR_ACT_UNSPEC = iota + FR_ACT_TO_TBL /* Pass to fixed table */ + FR_ACT_GOTO /* Jump to another rule */ + FR_ACT_NOP /* No operation */ + FR_ACT_RES3 + FR_ACT_RES4 + FR_ACT_BLACKHOLE /* Drop without notification */ + FR_ACT_UNREACHABLE /* Drop with ENETUNREACH */ + FR_ACT_PROHIBIT /* Drop with EACCES */ +) From 87df994490b40c9bd1aa5e5b90fd4b14cf435dc4 Mon Sep 17 00:00:00 2001 From: Hubert Krauze Date: Thu, 26 Nov 2015 12:47:18 +0100 Subject: [PATCH 03/15] Add scope and flags support for netlink address --- addr.go | 5 ++- addr_linux.go | 14 ++++++++ addr_test.go | 95 +++++++++++++++++++++++++++++++++++++++++++++++++++ 3 files changed, 113 insertions(+), 1 deletion(-) diff --git a/addr.go b/addr.go index ed6769a..c739e9f 100644 --- a/addr.go +++ b/addr.go @@ -10,7 +10,10 @@ import ( // include a mask, so it stores the address as a net.IPNet. type Addr struct { *net.IPNet - Label string + Label string + Flags int + Scope int + FlagsMask int } // String returns $ip/$netmask $label diff --git a/addr_linux.go b/addr_linux.go index 19aac0f..562da59 100644 --- a/addr_linux.go +++ b/addr_linux.go @@ -9,6 +9,9 @@ import ( "github.com/vishvananda/netlink/nl" ) +// IFA_FLAGS is a u32 attribute. +const IFA_FLAGS = 0x8 + // AddrAdd will add an IP address to a link device. // Equivalent to: `ip addr add $addr dev $link` func AddrAdd(link Link, addr *Addr) error { @@ -35,6 +38,7 @@ func addrHandle(link Link, addr *Addr, req *nl.NetlinkRequest) error { msg := nl.NewIfAddrmsg(family) msg.Index = uint32(base.Index) + msg.Scope = uint8(addr.Scope) prefixlen, _ := addr.Mask.Size() msg.Prefixlen = uint8(prefixlen) req.AddData(msg) @@ -52,6 +56,13 @@ func addrHandle(link Link, addr *Addr, req *nl.NetlinkRequest) error { addressData := nl.NewRtAttr(syscall.IFA_ADDRESS, addrData) req.AddData(addressData) + if addr.FlagsMask != 0 { + b := make([]byte, 4) + native.PutUint32(b, uint32(addr.Flags)) + flagsData := nl.NewRtAttr(IFA_FLAGS, b) + req.AddData(flagsData) + } + if addr.Label != "" { labelData := nl.NewRtAttr(syscall.IFA_LABEL, nl.ZeroTerminated(addr.Label)) req.AddData(labelData) @@ -111,6 +122,8 @@ func AddrList(link Link, family int) ([]Addr, error) { } case syscall.IFA_LABEL: addr.Label = string(attr.Value[:len(attr.Value)-1]) + case IFA_FLAGS: + addr.Flags = int(native.Uint32(attr.Value[0:4])) } } @@ -120,6 +133,7 @@ func AddrList(link Link, family int) ([]Addr, error) { } else { addr.IPNet = dst } + addr.Scope = int(msg.Scope) res = append(res, addr) } diff --git a/addr_test.go b/addr_test.go index 45e22c0..9348aac 100644 --- a/addr_test.go +++ b/addr_test.go @@ -1,6 +1,8 @@ package netlink import ( + "net" + "syscall" "testing" ) @@ -43,3 +45,96 @@ func TestAddrAddDel(t *testing.T) { t.Fatal("Address not removed properly") } } + +func TestAddrAddDelScope(t *testing.T) { + tearDown := setUpNetlinkTest(t) + defer tearDown() + + link, err := LinkByName("lo") + if err != nil { + t.Fatal(err) + } + + addr := &Addr{ + IPNet: &net.IPNet{ + IP: net.IPv4(127, 1, 1, 1), + Mask: net.CIDRMask(24, 32), + }, + Scope: syscall.RT_SCOPE_LINK, + } + if err = AddrAdd(link, addr); err != nil { + t.Fatal(err) + } + + addrs, err := AddrList(link, FAMILY_ALL) + if err != nil { + t.Fatal(err) + } + + if len(addrs) != 1 || !addr.Equal(addrs[0]) { + t.Fatal("Address not added properly") + } + + if addrs[0].Scope != addr.Scope { + t.Fatal("Address scope 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") + } +} + +func TestAddrAddDelFlags(t *testing.T) { + tearDown := setUpNetlinkTest(t) + defer tearDown() + + link, err := LinkByName("lo") + if err != nil { + t.Fatal(err) + } + + addr := &Addr{ + IPNet: &net.IPNet{ + IP: net.IPv4(127, 1, 1, 1), + Mask: net.CIDRMask(24, 32), + }, + Flags: syscall.IFA_F_PERMANENT, + FlagsMask: syscall.IFA_F_PERMANENT, + } + if err = AddrAdd(link, addr); err != nil { + t.Fatal(err) + } + + addrs, err := AddrList(link, FAMILY_ALL) + if err != nil { + t.Fatal(err) + } + + if len(addrs) != 1 || !addr.Equal(addrs[0]) { + t.Fatal("Address not added properly") + } + + if addrs[0].Flags != addr.Flags { + t.Fatal("Address flags not set 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") + } +} From dfdad4733611afa20923a0fd9c082edec2f5c413 Mon Sep 17 00:00:00 2001 From: marek-polewski Date: Thu, 26 Nov 2015 16:09:33 +0100 Subject: [PATCH 04/15] add protocol, priority, table, type, tos to route --- route.go | 11 +++--- route_linux.go | 56 +++++++++++++++++++++++++------ route_test.go | 91 +++++++++++++++++++++++++++++++++++++++----------- 3 files changed, 124 insertions(+), 34 deletions(-) diff --git a/route.go b/route.go index 789d39f..d31662a 100644 --- a/route.go +++ b/route.go @@ -24,16 +24,19 @@ const ( FLAG_PERVASIVE NextHopFlag = syscall.RTNH_F_PERVASIVE ) -// Route represents a netlink route. A route is associated with a link, -// has a destination network, an optional source ip, and optional -// gateway. Advanced route parameters and non-main routing tables are -// currently not supported. +// Route represents a netlink route. type Route struct { LinkIndex int + Iif int Scope Scope Dst *net.IPNet Src net.IP Gw net.IP + Protocol int + Priority int + Table int + Type int + Tos int Flags int } diff --git a/route_linux.go b/route_linux.go index c8910e2..0c3e355 100644 --- a/route_linux.go +++ b/route_linux.go @@ -29,8 +29,6 @@ func routeHandle(route *Route, req *nl.NetlinkRequest, msg *nl.RtMsg) error { return fmt.Errorf("one of Dst.IP, Src, or Gw must not be nil") } - msg.Scope = uint8(route.Scope) - msg.Flags = uint32(route.Flags) family := -1 var rtAttrs []*nl.RtAttr @@ -79,8 +77,34 @@ func routeHandle(route *Route, req *nl.NetlinkRequest, msg *nl.RtMsg) error { rtAttrs = append(rtAttrs, nl.NewRtAttr(syscall.RTA_GATEWAY, gwData)) } - msg.Family = uint8(family) + if route.Table > 0 { + if route.Table >= 256 { + msg.Table = syscall.RT_TABLE_UNSPEC + b := make([]byte, 4) + native.PutUint32(b, uint32(route.Table)) + rtAttrs = append(rtAttrs, nl.NewRtAttr(syscall.RTA_TABLE, b)) + } else { + msg.Table = uint8(route.Table) + } + } + if route.Priority > 0 { + b := make([]byte, 4) + native.PutUint32(b, uint32(route.Priority)) + rtAttrs = append(rtAttrs, nl.NewRtAttr(syscall.RTA_PRIORITY, b)) + } + if route.Tos > 0 { + msg.Tos = uint8(route.Tos) + } + if route.Protocol > 0 { + msg.Protocol = uint8(route.Protocol) + } + if route.Type > 0 { + msg.Type = uint8(route.Type) + } + + msg.Scope = uint8(route.Scope) + msg.Family = uint8(family) req.AddData(msg) for _, attr := range rtAttrs { req.AddData(attr) @@ -103,8 +127,8 @@ func routeHandle(route *Route, req *nl.NetlinkRequest, msg *nl.RtMsg) error { // The list can be filtered by link and ip family. func RouteList(link Link, family int) ([]Route, error) { req := nl.NewNetlinkRequest(syscall.RTM_GETROUTE, syscall.NLM_F_DUMP) - msg := nl.NewIfInfomsg(family) - req.AddData(msg) + infmsg := nl.NewIfInfomsg(family) + req.AddData(infmsg) msgs, err := req.Execute(syscall.NETLINK_ROUTE, syscall.RTM_NEWROUTE) if err != nil { @@ -149,14 +173,19 @@ func RouteList(link Link, family int) ([]Route, error) { // deserializeRoute decodes a binary netlink message into a Route struct func deserializeRoute(m []byte) (Route, error) { - route := Route{} msg := nl.DeserializeRtMsg(m) attrs, err := nl.ParseRouteAttr(m[msg.Len():]) if err != nil { - return route, err + return Route{}, err + } + route := Route{ + Scope: Scope(msg.Scope), + Protocol: int(msg.Protocol), + Table: int(msg.Table), + Type: int(msg.Type), + Tos: int(msg.Tos), + Flags: int(msg.Flags), } - route.Scope = Scope(msg.Scope) - route.Flags = int(msg.Flags) native := nl.NativeEndian() for _, attr := range attrs { @@ -171,8 +200,13 @@ func deserializeRoute(m []byte) (Route, error) { Mask: net.CIDRMask(int(msg.Dst_len), 8*len(attr.Value)), } case syscall.RTA_OIF: - routeIndex := int(native.Uint32(attr.Value[0:4])) - route.LinkIndex = routeIndex + route.LinkIndex = int(native.Uint32(attr.Value[0:4])) + case syscall.RTA_IIF: + route.Iif = int(native.Uint32(attr.Value[0:4])) + case syscall.RTA_PRIORITY: + route.Priority = int(native.Uint32(attr.Value[0:4])) + case syscall.RTA_TABLE: + route.Table = int(native.Uint32(attr.Value[0:4])) } } return route, nil diff --git a/route_test.go b/route_test.go index 3d170cc..6f8cdab 100644 --- a/route_test.go +++ b/route_test.go @@ -18,17 +18,19 @@ func TestRouteAddDel(t *testing.T) { } // bring the interface up - if err = LinkSetUp(link); err != nil { + if err := LinkSetUp(link); err != nil { t.Fatal(err) } // add a gateway route - _, dst, err := net.ParseCIDR("192.168.0.0/24") + dst := &net.IPNet{ + IP: net.IPv4(192, 168, 0, 0), + Mask: net.CIDRMask(24, 32), + } - ip := net.ParseIP("127.1.1.1") + ip := net.IPv4(127, 1, 1, 1) route := Route{LinkIndex: link.Attrs().Index, Dst: dst, Src: ip} - err = RouteAdd(&route) - if err != nil { + if err := RouteAdd(&route); err != nil { t.Fatal(err) } routes, err := RouteList(link, FAMILY_V4) @@ -36,10 +38,10 @@ func TestRouteAddDel(t *testing.T) { t.Fatal(err) } if len(routes) != 1 { - t.Fatal("Link not added properly") + t.Fatal("Route not added properly") } - dstIP := net.ParseIP("192.168.0.42") + dstIP := net.IPv4(192, 168, 0, 42) routeToDstIP, err := RouteGet(dstIP) if err != nil { t.Fatal(err) @@ -48,12 +50,9 @@ func TestRouteAddDel(t *testing.T) { if len(routeToDstIP) == 0 { t.Fatal("Default route not present") } - - err = RouteDel(&route) - if err != nil { + if err := RouteDel(&route); err != nil { t.Fatal(err) } - routes, err = RouteList(link, FAMILY_V4) if err != nil { t.Fatal(err) @@ -122,25 +121,79 @@ func TestRouteSubscribe(t *testing.T) { } // add a gateway route - _, dst, err := net.ParseCIDR("192.168.0.0/24") + dst := &net.IPNet{ + IP: net.IPv4(192, 168, 0, 0), + Mask: net.CIDRMask(24, 32), + } - ip := net.ParseIP("127.1.1.1") + ip := net.IPv4(127, 1, 1, 1) route := Route{LinkIndex: link.Attrs().Index, Dst: dst, Src: ip} - err = RouteAdd(&route) - if err != nil { + 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") } - - err = RouteDel(&route) - if err != nil { + if err := RouteDel(&route); err != nil { t.Fatal(err) } - if !expectRouteUpdate(ch, syscall.RTM_DELROUTE, dst.IP) { t.Fatal("Del update not received as expected") } } + +func TestRouteExtraFields(t *testing.T) { + tearDown := setUpNetlinkTest(t) + defer tearDown() + + // get loopback interface + link, err := LinkByName("lo") + if err != nil { + t.Fatal(err) + } + // bring the interface up + if err = LinkSetUp(link); err != nil { + t.Fatal(err) + } + + // add a gateway route + dst := &net.IPNet{ + IP: net.IPv4(1, 1, 1, 1), + Mask: net.CIDRMask(32, 32), + } + + src := net.IPv4(127, 3, 3, 3) + route := Route{ + LinkIndex: link.Attrs().Index, + Dst: dst, + Src: src, + Scope: syscall.RT_SCOPE_LINK, + Priority: 13, + Table: syscall.RT_TABLE_MAIN, + Type: syscall.RTN_UNICAST, + } + 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") + } + + 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") + } +} From b5b45b160b3781bdff3ca5c9b01a2303aeafcbe6 Mon Sep 17 00:00:00 2001 From: marek-polewski Date: Thu, 26 Nov 2015 16:31:53 +0100 Subject: [PATCH 05/15] add route filtration structure --- route_linux.go | 92 +++++++++++++++++++++++++++++++++++++++++++------- route_test.go | 14 +++++++- 2 files changed, 93 insertions(+), 13 deletions(-) diff --git a/route_linux.go b/route_linux.go index 0c3e355..cbd525f 100644 --- a/route_linux.go +++ b/route_linux.go @@ -10,6 +10,37 @@ import ( // RtAttr is shared so it is in netlink_linux.go +// RouteFilter represents filter that can be apply to RouteList function. +type RouteFilter struct { + Table int + Protocol int + Scope Scope + Type int + Tos int + Iif int + Oif int + Dst *net.IPNet + Src net.IP + Gw net.IP + + FlagMask uint64 +} + +// Flag mask for router filters. RouterFilter.FlagMask must be set to on +// for filter to work. +const ( + RT_FILTER_PROTOCOL uint64 = 1 << (1 + iota) + RT_FILTER_SCOPE + RT_FILTER_TYPE + RT_FILTER_TOS + RT_FILTER_IIF + RT_FILTER_OIF + RT_FILTER_DST + RT_FILTER_SRC + RT_FILTER_GW + RT_FILTER_TABLE +) + // RouteAdd will add a route to the system. // Equivalent to: `ip route add $route` func RouteAdd(route *Route) error { @@ -126,6 +157,19 @@ func routeHandle(route *Route, req *nl.NetlinkRequest, msg *nl.RtMsg) error { // Equivalent to: `ip route show`. // The list can be filtered by link and ip family. func RouteList(link Link, family int) ([]Route, error) { + var rf *RouteFilter + if link != nil { + rf = &RouteFilter{ + Oif: link.Attrs().Index, + FlagMask: RT_FILTER_OIF, + } + } + return RouteListFiltered(family, rf) +} + +// RouteListFiltered gets a list of routes in the system filtered with specified rules. +// All rules must be defined in RouteFilter struct +func RouteListFiltered(family int, filter *RouteFilter) ([]Route, error) { req := nl.NewNetlinkRequest(syscall.RTM_GETROUTE, syscall.NLM_F_DUMP) infmsg := nl.NewIfInfomsg(family) req.AddData(infmsg) @@ -135,13 +179,6 @@ func RouteList(link Link, family int) ([]Route, error) { return nil, err } - index := 0 - if link != nil { - base := link.Attrs() - ensureIndex(base) - index = base.Index - } - var res []Route for _, m := range msgs { msg := nl.DeserializeRtMsg(m) @@ -152,8 +189,10 @@ func RouteList(link Link, family int) ([]Route, error) { } if msg.Table != syscall.RT_TABLE_MAIN { - // Ignore non-main tables - continue + if filter == nil || filter != nil && filter.FlagMask&RT_FILTER_TABLE == 0 { + // Ignore non-main tables + continue + } } route, err := deserializeRoute(m) @@ -161,10 +200,39 @@ func RouteList(link Link, family int) ([]Route, error) { return nil, err } - if link != nil && route.LinkIndex != index { - // Ignore routes from other interfaces - continue + if filter != nil { + f := filter.FlagMask + switch { + case f&RT_FILTER_TABLE != 0 && filter.Table != route.Table: + continue + case f&RT_FILTER_PROTOCOL != 0 && route.Protocol != filter.Protocol: + continue + case f&RT_FILTER_SCOPE != 0 && route.Scope != filter.Scope: + continue + case f&RT_FILTER_TYPE != 0 && route.Type != filter.Type: + continue + case f&RT_FILTER_TOS != 0 && route.Tos != filter.Tos: + continue + case f&RT_FILTER_OIF != 0 && filter.Oif != route.LinkIndex: + continue + case f&RT_FILTER_IIF != 0 && filter.Iif != route.Iif: + continue + case f&RT_FILTER_GW != 0 && !route.Gw.Equal(filter.Gw): + continue + case f&RT_FILTER_SRC != 0 && !route.Src.Equal(filter.Src): + continue + case f&RT_FILTER_DST != 0 && filter.Dst != nil: + if route.Dst == nil { + continue + } + aMaskLen, aMaskBits := route.Dst.Mask.Size() + bMaskLen, bMaskBits := filter.Dst.Mask.Size() + if !(route.Dst.IP.Equal(filter.Dst.IP) && aMaskLen == bMaskLen && aMaskBits == bMaskBits) { + continue + } + } } + res = append(res, route) } diff --git a/route_test.go b/route_test.go index 6f8cdab..345c09e 100644 --- a/route_test.go +++ b/route_test.go @@ -172,11 +172,20 @@ func TestRouteExtraFields(t *testing.T) { Priority: 13, Table: syscall.RT_TABLE_MAIN, Type: syscall.RTN_UNICAST, + Tos: 14, } if err := RouteAdd(&route); err != nil { t.Fatal(err) } - routes, err := RouteList(link, FAMILY_V4) + routes, err := RouteListFiltered(FAMILY_V4, &RouteFilter{ + Dst: dst, + Src: src, + Scope: syscall.RT_SCOPE_LINK, + Table: syscall.RT_TABLE_MAIN, + Type: syscall.RTN_UNICAST, + Tos: 14, + FlagMask: RT_FILTER_DST | RT_FILTER_SRC | RT_FILTER_SCOPE | RT_FILTER_TABLE | RT_FILTER_TYPE | RT_FILTER_TOS, + }) if err != nil { t.Fatal(err) } @@ -196,4 +205,7 @@ func TestRouteExtraFields(t *testing.T) { 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") + } } From 889a43b83da4988c708f7e325c35535d46503419 Mon Sep 17 00:00:00 2001 From: marek-polewski Date: Tue, 8 Dec 2015 23:35:55 +0100 Subject: [PATCH 06/15] remove routeFilter struct and use route and mask instead rename iif to iLinkIndex --- route.go | 24 ++++++++++---------- route_linux.go | 60 ++++++++++++++------------------------------------ route_test.go | 17 +++++++------- 3 files changed, 37 insertions(+), 64 deletions(-) diff --git a/route.go b/route.go index d31662a..a7303d4 100644 --- a/route.go +++ b/route.go @@ -26,18 +26,18 @@ const ( // Route represents a netlink route. type Route struct { - LinkIndex int - Iif int - Scope Scope - Dst *net.IPNet - Src net.IP - Gw net.IP - Protocol int - Priority int - Table int - Type int - Tos int - Flags int + LinkIndex int + ILinkIndex int + Scope Scope + Dst *net.IPNet + Src net.IP + Gw net.IP + Protocol int + Priority int + Table int + Type int + Tos int + Flags int } func (r Route) String() string { diff --git a/route_linux.go b/route_linux.go index cbd525f..d8026a7 100644 --- a/route_linux.go +++ b/route_linux.go @@ -10,24 +10,6 @@ import ( // RtAttr is shared so it is in netlink_linux.go -// RouteFilter represents filter that can be apply to RouteList function. -type RouteFilter struct { - Table int - Protocol int - Scope Scope - Type int - Tos int - Iif int - Oif int - Dst *net.IPNet - Src net.IP - Gw net.IP - - FlagMask uint64 -} - -// Flag mask for router filters. RouterFilter.FlagMask must be set to on -// for filter to work. const ( RT_FILTER_PROTOCOL uint64 = 1 << (1 + iota) RT_FILTER_SCOPE @@ -157,19 +139,18 @@ func routeHandle(route *Route, req *nl.NetlinkRequest, msg *nl.RtMsg) error { // Equivalent to: `ip route show`. // The list can be filtered by link and ip family. func RouteList(link Link, family int) ([]Route, error) { - var rf *RouteFilter + var routeFilter *Route if link != nil { - rf = &RouteFilter{ - Oif: link.Attrs().Index, - FlagMask: RT_FILTER_OIF, + routeFilter = &Route{ + LinkIndex: link.Attrs().Index, } } - return RouteListFiltered(family, rf) + return RouteListFiltered(family, routeFilter, RT_FILTER_OIF) } // RouteListFiltered gets a list of routes in the system filtered with specified rules. // All rules must be defined in RouteFilter struct -func RouteListFiltered(family int, filter *RouteFilter) ([]Route, error) { +func RouteListFiltered(family int, filter *Route, filterMask uint64) ([]Route, error) { req := nl.NewNetlinkRequest(syscall.RTM_GETROUTE, syscall.NLM_F_DUMP) infmsg := nl.NewIfInfomsg(family) req.AddData(infmsg) @@ -182,46 +163,41 @@ func RouteListFiltered(family int, filter *RouteFilter) ([]Route, error) { var res []Route for _, m := range msgs { msg := nl.DeserializeRtMsg(m) - if msg.Flags&syscall.RTM_F_CLONED != 0 { // Ignore cloned routes continue } - if msg.Table != syscall.RT_TABLE_MAIN { - if filter == nil || filter != nil && filter.FlagMask&RT_FILTER_TABLE == 0 { + if filter == nil || filter != nil && filterMask&RT_FILTER_TABLE == 0 { // Ignore non-main tables continue } } - route, err := deserializeRoute(m) if err != nil { return nil, err } - if filter != nil { - f := filter.FlagMask switch { - case f&RT_FILTER_TABLE != 0 && filter.Table != route.Table: + case filterMask&RT_FILTER_TABLE != 0 && route.Table != filter.Table: continue - case f&RT_FILTER_PROTOCOL != 0 && route.Protocol != filter.Protocol: + case filterMask&RT_FILTER_PROTOCOL != 0 && route.Protocol != filter.Protocol: continue - case f&RT_FILTER_SCOPE != 0 && route.Scope != filter.Scope: + case filterMask&RT_FILTER_SCOPE != 0 && route.Scope != filter.Scope: continue - case f&RT_FILTER_TYPE != 0 && route.Type != filter.Type: + case filterMask&RT_FILTER_TYPE != 0 && route.Type != filter.Type: continue - case f&RT_FILTER_TOS != 0 && route.Tos != filter.Tos: + case filterMask&RT_FILTER_TOS != 0 && route.Tos != filter.Tos: continue - case f&RT_FILTER_OIF != 0 && filter.Oif != route.LinkIndex: + case filterMask&RT_FILTER_OIF != 0 && route.LinkIndex != filter.LinkIndex: continue - case f&RT_FILTER_IIF != 0 && filter.Iif != route.Iif: + case filterMask&RT_FILTER_IIF != 0 && route.ILinkIndex != filter.ILinkIndex: continue - case f&RT_FILTER_GW != 0 && !route.Gw.Equal(filter.Gw): + case filterMask&RT_FILTER_GW != 0 && !route.Gw.Equal(filter.Gw): continue - case f&RT_FILTER_SRC != 0 && !route.Src.Equal(filter.Src): + case filterMask&RT_FILTER_SRC != 0 && !route.Src.Equal(filter.Src): continue - case f&RT_FILTER_DST != 0 && filter.Dst != nil: + case filterMask&RT_FILTER_DST != 0 && filter.Dst != nil: if route.Dst == nil { continue } @@ -232,10 +208,8 @@ func RouteListFiltered(family int, filter *RouteFilter) ([]Route, error) { } } } - res = append(res, route) } - return res, nil } @@ -270,7 +244,7 @@ func deserializeRoute(m []byte) (Route, error) { case syscall.RTA_OIF: route.LinkIndex = int(native.Uint32(attr.Value[0:4])) case syscall.RTA_IIF: - route.Iif = int(native.Uint32(attr.Value[0:4])) + route.ILinkIndex = int(native.Uint32(attr.Value[0:4])) case syscall.RTA_PRIORITY: route.Priority = int(native.Uint32(attr.Value[0:4])) case syscall.RTA_TABLE: diff --git a/route_test.go b/route_test.go index 345c09e..fa0b579 100644 --- a/route_test.go +++ b/route_test.go @@ -177,15 +177,14 @@ func TestRouteExtraFields(t *testing.T) { if err := RouteAdd(&route); err != nil { t.Fatal(err) } - routes, err := RouteListFiltered(FAMILY_V4, &RouteFilter{ - Dst: dst, - Src: src, - Scope: syscall.RT_SCOPE_LINK, - Table: syscall.RT_TABLE_MAIN, - Type: syscall.RTN_UNICAST, - Tos: 14, - FlagMask: RT_FILTER_DST | RT_FILTER_SRC | RT_FILTER_SCOPE | RT_FILTER_TABLE | RT_FILTER_TYPE | RT_FILTER_TOS, - }) + 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) } From f351153998273cc176a2998ab1a83992c3591644 Mon Sep 17 00:00:00 2001 From: Hubert Krauze Date: Wed, 9 Dec 2015 11:30:21 +0100 Subject: [PATCH 07/15] Remove FlagsMask from Addr struct --- addr.go | 7 +++---- addr_linux.go | 2 +- addr_test.go | 3 +-- 3 files changed, 5 insertions(+), 7 deletions(-) diff --git a/addr.go b/addr.go index c739e9f..079fff3 100644 --- a/addr.go +++ b/addr.go @@ -10,10 +10,9 @@ import ( // include a mask, so it stores the address as a net.IPNet. type Addr struct { *net.IPNet - Label string - Flags int - Scope int - FlagsMask int + Label string + Flags int + Scope int } // String returns $ip/$netmask $label diff --git a/addr_linux.go b/addr_linux.go index 562da59..9373e9c 100644 --- a/addr_linux.go +++ b/addr_linux.go @@ -56,7 +56,7 @@ func addrHandle(link Link, addr *Addr, req *nl.NetlinkRequest) error { addressData := nl.NewRtAttr(syscall.IFA_ADDRESS, addrData) req.AddData(addressData) - if addr.FlagsMask != 0 { + if addr.Flags != 0 { b := make([]byte, 4) native.PutUint32(b, uint32(addr.Flags)) flagsData := nl.NewRtAttr(IFA_FLAGS, b) diff --git a/addr_test.go b/addr_test.go index 9348aac..413523c 100644 --- a/addr_test.go +++ b/addr_test.go @@ -106,8 +106,7 @@ func TestAddrAddDelFlags(t *testing.T) { IP: net.IPv4(127, 1, 1, 1), Mask: net.CIDRMask(24, 32), }, - Flags: syscall.IFA_F_PERMANENT, - FlagsMask: syscall.IFA_F_PERMANENT, + Flags: syscall.IFA_F_PERMANENT, } if err = AddrAdd(link, addr); err != nil { t.Fatal(err) From 124b1f444943f8e3e05297c270c7b619a90c1c3d Mon Sep 17 00:00:00 2001 From: Hubert Krauze Date: Wed, 9 Dec 2015 11:53:28 +0100 Subject: [PATCH 08/15] Table driven test for Addr --- addr_test.go | 175 ++++++++++++++++++--------------------------------- 1 file changed, 62 insertions(+), 113 deletions(-) diff --git a/addr_test.go b/addr_test.go index 413523c..1735b83 100644 --- a/addr_test.go +++ b/addr_test.go @@ -6,93 +6,34 @@ import ( "testing" ) -func TestAddrAddDel(t *testing.T) { - tearDown := setUpNetlinkTest(t) - defer tearDown() - - link, err := LinkByName("lo") - if err != nil { - t.Fatal(err) - } - - addr, err := ParseAddr("127.1.1.1/24 local") - if err != nil { - t.Fatal(err) - } - - if err = AddrAdd(link, addr); err != nil { - t.Fatal(err) - } - - addrs, err := AddrList(link, FAMILY_ALL) - if err != nil { - t.Fatal(err) - } - - if len(addrs) != 1 || !addr.Equal(addrs[0]) || addrs[0].Label != addr.Label { - 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") - } -} - -func TestAddrAddDelScope(t *testing.T) { - tearDown := setUpNetlinkTest(t) - defer tearDown() - - link, err := LinkByName("lo") - if err != nil { - t.Fatal(err) - } - - addr := &Addr{ - IPNet: &net.IPNet{ - IP: net.IPv4(127, 1, 1, 1), - Mask: net.CIDRMask(24, 32), +func TestAddr(t *testing.T) { + var address = &net.IPNet{net.IPv4(127, 0, 0, 2), net.CIDRMask(24, 32)} + var addrTests = []struct { + addr *Addr + expected *Addr + }{ + { + &Addr{IPNet: address}, + &Addr{IPNet: address, Label: "lo", Scope: syscall.RT_SCOPE_UNIVERSE, Flags: syscall.IFA_F_PERMANENT}, + }, + { + &Addr{IPNet: address, Label: "local"}, + &Addr{IPNet: address, Label: "local", Scope: syscall.RT_SCOPE_UNIVERSE, Flags: syscall.IFA_F_PERMANENT}, + }, + { + &Addr{IPNet: address, Flags: syscall.IFA_F_OPTIMISTIC}, + &Addr{IPNet: address, Label: "lo", Flags: syscall.IFA_F_OPTIMISTIC | syscall.IFA_F_PERMANENT, Scope: syscall.RT_SCOPE_UNIVERSE}, + }, + { + &Addr{IPNet: address, Flags: syscall.IFA_F_OPTIMISTIC | syscall.IFA_F_DADFAILED}, + &Addr{IPNet: address, Label: "lo", Flags: syscall.IFA_F_OPTIMISTIC | syscall.IFA_F_DADFAILED | syscall.IFA_F_PERMANENT, Scope: syscall.RT_SCOPE_UNIVERSE}, + }, + { + &Addr{IPNet: address, Scope: syscall.RT_SCOPE_NOWHERE}, + &Addr{IPNet: address, Label: "lo", Flags: syscall.IFA_F_PERMANENT, Scope: syscall.RT_SCOPE_NOWHERE}, }, - Scope: syscall.RT_SCOPE_LINK, - } - if err = AddrAdd(link, addr); err != nil { - t.Fatal(err) } - addrs, err := AddrList(link, FAMILY_ALL) - if err != nil { - t.Fatal(err) - } - - if len(addrs) != 1 || !addr.Equal(addrs[0]) { - t.Fatal("Address not added properly") - } - - if addrs[0].Scope != addr.Scope { - t.Fatal("Address scope 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") - } -} - -func TestAddrAddDelFlags(t *testing.T) { tearDown := setUpNetlinkTest(t) defer tearDown() @@ -101,39 +42,47 @@ func TestAddrAddDelFlags(t *testing.T) { t.Fatal(err) } - addr := &Addr{ - IPNet: &net.IPNet{ - IP: net.IPv4(127, 1, 1, 1), - Mask: net.CIDRMask(24, 32), - }, - Flags: syscall.IFA_F_PERMANENT, - } - if err = AddrAdd(link, addr); err != nil { - t.Fatal(err) - } + for _, tt := range addrTests { + if err = AddrAdd(link, tt.addr); err != nil { + t.Fatal(err) + } - addrs, err := AddrList(link, FAMILY_ALL) - if err != nil { - t.Fatal(err) - } + addrs, err := AddrList(link, FAMILY_ALL) + if err != nil { + t.Fatal(err) + } - if len(addrs) != 1 || !addr.Equal(addrs[0]) { - t.Fatal("Address not added properly") - } + if len(addrs) != 1 { + t.Fatal("Address not added properly") + } - if addrs[0].Flags != addr.Flags { - t.Fatal("Address flags not set properly") - } + if !addrs[0].Equal(*tt.expected) { + t.Fatalf("Address ip no set properly, got=%s, expected=%s", addrs[0], tt.expected) + } - if err = AddrDel(link, addr); err != nil { - t.Fatal(err) - } - addrs, err = AddrList(link, FAMILY_ALL) - if err != nil { - t.Fatal(err) - } + 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 len(addrs) != 0 { - t.Fatal("Address not removed properly") + if addrs[0].Flags != tt.expected.Flags { + t.Fatalf("Address flags not set properly, got=%d, expected=%d", addrs[0].Flags, tt.expected.Flags) + } + + if addrs[0].Scope != tt.expected.Scope { + t.Fatalf("Address scope not set properly, got=%d, expected=%d", addrs[0].Scope, tt.expected.Scope) + } + + if err = AddrDel(link, tt.addr); err != nil { + t.Fatal(err) + } + + addrs, err = AddrList(link, FAMILY_ALL) + if err != nil { + t.Fatal(err) + } + + if len(addrs) != 0 { + t.Fatal("Address not removed properly") + } } } From 2f58676854b981d56b5a90b8651ff82c9f3b96a2 Mon Sep 17 00:00:00 2001 From: Hubert Krauze Date: Wed, 9 Dec 2015 12:25:42 +0100 Subject: [PATCH 09/15] Create LinkSetNoMaster function --- link_linux.go | 6 ++++++ 1 file changed, 6 insertions(+) diff --git a/link_linux.go b/link_linux.go index 0826285..60213bd 100644 --- a/link_linux.go +++ b/link_linux.go @@ -136,6 +136,12 @@ func LinkSetMaster(link Link, master *Bridge) error { return LinkSetMasterByIndex(link, index) } +// LinkSetNoMaster removes the master of the link device. +// Equivalent to: `ip link set $link nomaster` +func LinkSetNoMaster(link Link) error { + return LinkSetMasterByIndex(link, 0) +} + // LinkSetMasterByIndex sets the master of the link device. // Equivalent to: `ip link set $link master $master` func LinkSetMasterByIndex(link Link, masterIndex int) error { From a88e39d8067e7a203be8c7f724b069e3928a2419 Mon Sep 17 00:00:00 2001 From: Hubert Krauze Date: Wed, 9 Dec 2015 12:25:52 +0100 Subject: [PATCH 10/15] Check if master Bridge exists when LinkSetMaster --- link_linux.go | 3 +++ 1 file changed, 3 insertions(+) diff --git a/link_linux.go b/link_linux.go index 60213bd..810fc4f 100644 --- a/link_linux.go +++ b/link_linux.go @@ -133,6 +133,9 @@ func LinkSetMaster(link Link, master *Bridge) error { ensureIndex(masterBase) index = masterBase.Index } + if index <= 0 { + return fmt.Errorf("Device does not exist") + } return LinkSetMasterByIndex(link, index) } From ec755809580eb3c976444655e942f0a352d95dcb Mon Sep 17 00:00:00 2001 From: Hubert Krauze Date: Wed, 9 Dec 2015 12:26:37 +0100 Subject: [PATCH 11/15] Add test for adding non existing master, use new api - LinkSetNoMaster --- link_test.go | 8 +++++++- 1 file changed, 7 insertions(+), 1 deletion(-) diff --git a/link_test.go b/link_test.go index 3b640f1..a23bfb4 100644 --- a/link_test.go +++ b/link_test.go @@ -389,6 +389,12 @@ func TestLinkSetUnsetResetMaster(t *testing.T) { 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) } @@ -415,7 +421,7 @@ func TestLinkSetUnsetResetMaster(t *testing.T) { t.Fatal("Master not reset properly") } - if err := LinkSetMaster(slave, nil); err != nil { + if err := LinkSetNoMaster(slave); err != nil { t.Fatal(err) } From 9f8d5e7e60735540ef6c19a0236605abf4caeff6 Mon Sep 17 00:00:00 2001 From: Hubert Krauze Date: Wed, 9 Dec 2015 12:47:37 +0100 Subject: [PATCH 12/15] Add link.Attrs().Flags during creating link --- link_linux.go | 21 +++++++++++++++++++++ 1 file changed, 21 insertions(+) diff --git a/link_linux.go b/link_linux.go index 0826285..738131b 100644 --- a/link_linux.go +++ b/link_linux.go @@ -328,6 +328,27 @@ func LinkAdd(link Link) error { req := nl.NewNetlinkRequest(syscall.RTM_NEWLINK, syscall.NLM_F_CREATE|syscall.NLM_F_EXCL|syscall.NLM_F_ACK) msg := nl.NewIfInfomsg(syscall.AF_UNSPEC) + // TODO: make it shorter + if base.Flags&net.FlagUp != 0 { + msg.Change = syscall.IFF_UP + msg.Flags = syscall.IFF_UP + } + if base.Flags&net.FlagBroadcast != 0 { + msg.Change |= syscall.IFF_BROADCAST + msg.Flags |= syscall.IFF_BROADCAST + } + if base.Flags&net.FlagLoopback != 0 { + msg.Change |= syscall.IFF_LOOPBACK + msg.Flags |= syscall.IFF_LOOPBACK + } + if base.Flags&net.FlagPointToPoint != 0 { + msg.Change |= syscall.IFF_POINTOPOINT + msg.Flags |= syscall.IFF_POINTOPOINT + } + if base.Flags&net.FlagMulticast != 0 { + msg.Change |= syscall.IFF_MULTICAST + msg.Flags |= syscall.IFF_MULTICAST + } req.AddData(msg) if base.ParentIndex != 0 { From 656c395788262b9b790bc2aee94874cfb84245d1 Mon Sep 17 00:00:00 2001 From: marek-polewski Date: Wed, 9 Dec 2015 13:33:45 +0100 Subject: [PATCH 13/15] add bond interface support --- link.go | 284 +++++++++++++++++++++++++++++++++++++++++++++++ link_linux.go | 141 +++++++++++++++++++++++ link_test.go | 7 ++ nl/link_linux.go | 46 ++++++++ 4 files changed, 478 insertions(+) diff --git a/link.go b/link.go index 5472a25..29bd5df 100644 --- a/link.go +++ b/link.go @@ -1,6 +1,7 @@ package netlink import ( + "fmt" "net" "syscall" ) @@ -240,6 +241,289 @@ func (ipvlan *IPVlan) Type() string { return "ipvlan" } +// BondMode type +type BondMode int + +func (b BondMode) String() string { + s, ok := bondModeToString[b] + if !ok { + return fmt.Sprintf("BondMode(%d)", b) + } + return s +} + +// StringToBondMode returns bond mode, or uknonw is the s is invalid. +func StringToBondMode(s string) BondMode { + mode, ok := StringToBondModeMap[s] + if !ok { + return BOND_MODE_UNKNOWN + } + return mode +} + +// Possible BondMode +const ( + BOND_MODE_802_3AD BondMode = iota + BOND_MODE_BALANCE_RR + BOND_MODE_ACTIVE_BACKUP + BOND_MODE_BALANCE_XOR + BOND_MODE_BROADCAST + BOND_MODE_BALANCE_TLB + BOND_MODE_BALANCE_ALB + BOND_MODE_UNKNOWN +) + +var bondModeToString = map[BondMode]string{ + BOND_MODE_802_3AD: "802.3ad", + BOND_MODE_BALANCE_RR: "balance-rr", + BOND_MODE_ACTIVE_BACKUP: "active-backup", + BOND_MODE_BALANCE_XOR: "balance-xor", + BOND_MODE_BROADCAST: "broadcast", + BOND_MODE_BALANCE_TLB: "balance-tlb", + BOND_MODE_BALANCE_ALB: "balance-alb", +} +var StringToBondModeMap = map[string]BondMode{ + "802.3ad": BOND_MODE_802_3AD, + "balance-rr": BOND_MODE_BALANCE_RR, + "active-backup": BOND_MODE_ACTIVE_BACKUP, + "balance-xor": BOND_MODE_BALANCE_XOR, + "broadcast": BOND_MODE_BROADCAST, + "balance-tlb": BOND_MODE_BALANCE_TLB, + "balance-alb": BOND_MODE_BALANCE_ALB, +} + +// BondArpValidate type +type BondArpValidate int + +// Possible BondArpValidate value +const ( + BOND_ARP_VALIDATE_NONE BondArpValidate = iota + BOND_ARP_VALIDATE_ACTIVE + BOND_ARP_VALIDATE_BACKUP + BOND_ARP_VALIDATE_ALL +) + +// BondPrimaryReselect type +type BondPrimaryReselect int + +// Possible BondPrimaryReselect value +const ( + BOND_PRIMARY_RESELECT_ALWAYS BondPrimaryReselect = iota + BOND_PRIMARY_RESELECT_BETTER + BOND_PRIMARY_RESELECT_FAILURE +) + +// BondArpAllTargets type +type BondArpAllTargets int + +// Possible BondArpAllTargets value +const ( + BOND_ARP_ALL_TARGETS_ANY BondArpAllTargets = iota + BOND_ARP_ALL_TARGETS_ALL +) + +// BondFailOverMac type +type BondFailOverMac int + +// Possible BondFailOverMac value +const ( + BOND_FAIL_OVER_MAC_NONE BondFailOverMac = iota + BOND_FAIL_OVER_MAC_ACTIVE + BOND_FAIL_OVER_MAC_FOLLOW +) + +// BondXmitHashPolicy type +type BondXmitHashPolicy int + +func (b BondXmitHashPolicy) String() string { + s, ok := bondXmitHashPolicyToString[b] + if !ok { + return fmt.Sprintf("XmitHashPolicy(%d)", b) + } + return s +} + +// StringToBondXmitHashPolicy returns bond lacp arte, or uknonw is the s is invalid. +func StringToBondXmitHashPolicy(s string) BondXmitHashPolicy { + lacp, ok := StringToBondXmitHashPolicyMap[s] + if !ok { + return BOND_XMIT_HASH_POLICY_UNKNOWN + } + return lacp +} + +// Possible BondXmitHashPolicy value +const ( + BOND_XMIT_HASH_POLICY_LAYER2 BondXmitHashPolicy = iota + BOND_XMIT_HASH_POLICY_LAYER3_4 + BOND_XMIT_HASH_POLICY_LAYER2_3 + BOND_XMIT_HASH_POLICY_ENCAP2_3 + BOND_XMIT_HASH_POLICY_ENCAP3_4 + BOND_XMIT_HASH_POLICY_UNKNOWN +) + +var bondXmitHashPolicyToString = map[BondXmitHashPolicy]string{ + BOND_XMIT_HASH_POLICY_LAYER2: "layer2", + BOND_XMIT_HASH_POLICY_LAYER3_4: "layer3+4", + BOND_XMIT_HASH_POLICY_LAYER2_3: "layer2+3", + BOND_XMIT_HASH_POLICY_ENCAP2_3: "encap2+3", + BOND_XMIT_HASH_POLICY_ENCAP3_4: "encap3+4", +} +var StringToBondXmitHashPolicyMap = map[string]BondXmitHashPolicy{ + "layer2": BOND_XMIT_HASH_POLICY_LAYER2, + "layer3+4": BOND_XMIT_HASH_POLICY_LAYER3_4, + "layer2+3": BOND_XMIT_HASH_POLICY_LAYER2_3, + "encap2+3": BOND_XMIT_HASH_POLICY_ENCAP2_3, + "encap3+4": BOND_XMIT_HASH_POLICY_ENCAP3_4, +} + +// BondLacpRate type +type BondLacpRate int + +func (b BondLacpRate) String() string { + s, ok := bondLacpRateToString[b] + if !ok { + return fmt.Sprintf("LacpRate(%d)", b) + } + return s +} + +// StringToBondLacpRate returns bond lacp arte, or uknonw is the s is invalid. +func StringToBondLacpRate(s string) BondLacpRate { + lacp, ok := StringToBondLacpRateMap[s] + if !ok { + return BOND_LACP_RATE_UNKNOWN + } + return lacp +} + +// Possible BondLacpRate value +const ( + BOND_LACP_RATE_SLOW BondLacpRate = iota + BOND_LACP_RATE_FAST + BOND_LACP_RATE_UNKNOWN +) + +var bondLacpRateToString = map[BondLacpRate]string{ + BOND_LACP_RATE_SLOW: "slow", + BOND_LACP_RATE_FAST: "fast", +} +var StringToBondLacpRateMap = map[string]BondLacpRate{ + "slow": BOND_LACP_RATE_SLOW, + "fast": BOND_LACP_RATE_FAST, +} + +// BondAdSelect type +type BondAdSelect int + +// Possible BondAdSelect value +const ( + BOND_AD_SELECT_STABLE BondAdSelect = iota + BOND_AD_SELECT_BANDWIDTH + BOND_AD_SELECT_COUNT +) + +// BondAdInfo +type BondAdInfo struct { + AggregatorId int + NumPorts int + ActorKey int + PartnerKey int + PartnerMac net.HardwareAddr +} + +// Bond representation +type Bond struct { + LinkAttrs + Mode BondMode + ActiveSlave int + Miimon int + UpDelay int + DownDelay int + UseCarrier int + ArpInterval int + ArpIpTargets []net.IP + ArpValidate BondArpValidate + ArpAllTargets BondArpAllTargets + Primary int + PrimaryReselect BondPrimaryReselect + FailOverMac BondFailOverMac + XmitHashPolicy BondXmitHashPolicy + ResendIgmp int + NumPeerNotif int + AllSlavesActive int + MinLinks int + LpInterval int + PackersPerSlave int + LacpRate BondLacpRate + AdSelect BondAdSelect + // looking at iproute tool AdInfo can only be retrived. It can't be set. + AdInfo *BondAdInfo +} + +func NewLinkBond(atr LinkAttrs) *Bond { + return &Bond{ + LinkAttrs: atr, + Mode: -1, + ActiveSlave: -1, + Miimon: -1, + UpDelay: -1, + DownDelay: -1, + UseCarrier: -1, + ArpInterval: -1, + ArpIpTargets: nil, + ArpValidate: -1, + ArpAllTargets: -1, + Primary: -1, + PrimaryReselect: -1, + FailOverMac: -1, + XmitHashPolicy: -1, + ResendIgmp: -1, + NumPeerNotif: -1, + AllSlavesActive: -1, + MinLinks: -1, + LpInterval: -1, + PackersPerSlave: -1, + LacpRate: -1, + AdSelect: -1, + } +} + +// Flag mask for bond options. Bond.Flagmask must be set to on for option to work. +const ( + BOND_MODE_MASK uint64 = 1 << (1 + iota) + BOND_ACTIVE_SLAVE_MASK + BOND_MIIMON_MASK + BOND_UPDELAY_MASK + BOND_DOWNDELAY_MASK + BOND_USE_CARRIER_MASK + BOND_ARP_INTERVAL_MASK + BOND_ARP_VALIDATE_MASK + BOND_ARP_ALL_TARGETS_MASK + BOND_PRIMARY_MASK + BOND_PRIMARY_RESELECT_MASK + BOND_FAIL_OVER_MAC_MASK + BOND_XMIT_HASH_POLICY_MASK + BOND_RESEND_IGMP_MASK + BOND_NUM_PEER_NOTIF_MASK + BOND_ALL_SLAVES_ACTIVE_MASK + BOND_MIN_LINKS_MASK + BOND_LP_INTERVAL_MASK + BOND_PACKETS_PER_SLAVE_MASK + BOND_LACP_RATE_MASK + BOND_AD_SELECT_MASK +) + +// Attrs implementation. +func (bond *Bond) Attrs() *LinkAttrs { + return &bond.LinkAttrs +} + +// Type implementation fro Vxlan. +func (bond *Bond) Type() string { + return "bond" +} + // iproute2 supported devices; // vlan | veth | vcan | dummy | ifb | macvlan | macvtap | // bridge | bond | ipoib | ip6tnl | ipip | sit | vxlan | diff --git a/link_linux.go b/link_linux.go index 0826285..1f7ea50 100644 --- a/link_linux.go +++ b/link_linux.go @@ -275,6 +275,87 @@ func addVxlanAttrs(vxlan *Vxlan, linkInfo *nl.RtAttr) { } } +func addBondAttrs(bond *Bond, linkInfo *nl.RtAttr) { + data := nl.NewRtAttrChild(linkInfo, nl.IFLA_INFO_DATA, nil) + if bond.Mode >= 0 { + nl.NewRtAttrChild(data, nl.IFLA_BOND_MODE, nl.Uint8Attr(uint8(bond.Mode))) + } + if bond.ActiveSlave >= 0 { + nl.NewRtAttrChild(data, nl.IFLA_BOND_ACTIVE_SLAVE, nl.Uint32Attr(uint32(bond.ActiveSlave))) + } + if bond.Miimon >= 0 { + nl.NewRtAttrChild(data, nl.IFLA_BOND_MIIMON, nl.Uint32Attr(uint32(bond.Miimon))) + } + if bond.UpDelay >= 0 { + nl.NewRtAttrChild(data, nl.IFLA_BOND_UPDELAY, nl.Uint32Attr(uint32(bond.UpDelay))) + } + if bond.DownDelay >= 0 { + nl.NewRtAttrChild(data, nl.IFLA_BOND_DOWNDELAY, nl.Uint32Attr(uint32(bond.DownDelay))) + } + if bond.UseCarrier >= 0 { + nl.NewRtAttrChild(data, nl.IFLA_BOND_USE_CARRIER, nl.Uint8Attr(uint8(bond.UseCarrier))) + } + if bond.ArpInterval >= 0 { + nl.NewRtAttrChild(data, nl.IFLA_BOND_ARP_INTERVAL, nl.Uint32Attr(uint32(bond.ArpInterval))) + } + if bond.ArpIpTargets != nil { + msg := nl.NewRtAttrChild(data, nl.IFLA_BOND_ARP_IP_TARGET, nil) + for i := range bond.ArpIpTargets { + ip := bond.ArpIpTargets[i].To4() + if ip != nil { + nl.NewRtAttrChild(msg, i, []byte(ip)) + continue + } + ip = bond.ArpIpTargets[i].To16() + if ip != nil { + nl.NewRtAttrChild(msg, i, []byte(ip)) + } + } + } + if bond.ArpValidate >= 0 { + nl.NewRtAttrChild(data, nl.IFLA_BOND_ARP_VALIDATE, nl.Uint32Attr(uint32(bond.ArpValidate))) + } + if bond.ArpAllTargets >= 0 { + nl.NewRtAttrChild(data, nl.IFLA_BOND_ARP_ALL_TARGETS, nl.Uint32Attr(uint32(bond.ArpAllTargets))) + } + if bond.Primary >= 0 { + nl.NewRtAttrChild(data, nl.IFLA_BOND_PRIMARY, nl.Uint32Attr(uint32(bond.Primary))) + } + if bond.PrimaryReselect >= 0 { + nl.NewRtAttrChild(data, nl.IFLA_BOND_PRIMARY_RESELECT, nl.Uint8Attr(uint8(bond.PrimaryReselect))) + } + if bond.FailOverMac >= 0 { + nl.NewRtAttrChild(data, nl.IFLA_BOND_FAIL_OVER_MAC, nl.Uint8Attr(uint8(bond.FailOverMac))) + } + if bond.XmitHashPolicy >= 0 { + nl.NewRtAttrChild(data, nl.IFLA_BOND_XMIT_HASH_POLICY, nl.Uint8Attr(uint8(bond.XmitHashPolicy))) + } + if bond.ResendIgmp >= 0 { + nl.NewRtAttrChild(data, nl.IFLA_BOND_RESEND_IGMP, nl.Uint32Attr(uint32(bond.ResendIgmp))) + } + if bond.NumPeerNotif >= 0 { + nl.NewRtAttrChild(data, nl.IFLA_BOND_NUM_PEER_NOTIF, nl.Uint8Attr(uint8(bond.NumPeerNotif))) + } + if bond.AllSlavesActive >= 0 { + nl.NewRtAttrChild(data, nl.IFLA_BOND_ALL_SLAVES_ACTIVE, nl.Uint8Attr(uint8(bond.AllSlavesActive))) + } + if bond.MinLinks >= 0 { + nl.NewRtAttrChild(data, nl.IFLA_BOND_MIN_LINKS, nl.Uint32Attr(uint32(bond.MinLinks))) + } + if bond.LpInterval >= 0 { + nl.NewRtAttrChild(data, nl.IFLA_BOND_LP_INTERVAL, nl.Uint32Attr(uint32(bond.LpInterval))) + } + if bond.PackersPerSlave >= 0 { + nl.NewRtAttrChild(data, nl.IFLA_BOND_PACKETS_PER_SLAVE, nl.Uint32Attr(uint32(bond.PackersPerSlave))) + } + if bond.LacpRate >= 0 { + nl.NewRtAttrChild(data, nl.IFLA_BOND_AD_LACP_RATE, nl.Uint8Attr(uint8(bond.LacpRate))) + } + if bond.AdSelect >= 0 { + nl.NewRtAttrChild(data, nl.IFLA_BOND_AD_SELECT, nl.Uint8Attr(uint8(bond.AdSelect))) + } +} + // LinkAdd adds a new link device. The type and features of the device // are taken fromt the parameters in the link object. // Equivalent to: `ip link add $link` @@ -388,6 +469,8 @@ func LinkAdd(link Link) error { } else if vxlan, ok := link.(*Vxlan); ok { addVxlanAttrs(vxlan, linkInfo) + } else if bond, ok := link.(*Bond); ok { + addBondAttrs(bond, linkInfo) } else if ipv, ok := link.(*IPVlan); ok { data := nl.NewRtAttrChild(linkInfo, nl.IFLA_INFO_DATA, nil) nl.NewRtAttrChild(data, nl.IFLA_IPVLAN_MODE, nl.Uint16Attr(uint16(ipv.Mode))) @@ -543,6 +626,8 @@ func linkDeserialize(m []byte) (Link, error) { link = &Veth{} case "vxlan": link = &Vxlan{} + case "bond": + link = &Bond{} case "ipvlan": link = &IPVlan{} case "macvlan": @@ -562,6 +647,8 @@ func linkDeserialize(m []byte) (Link, error) { parseVlanData(link, data) case "vxlan": parseVxlanData(link, data) + case "bond": + parseBondData(link, data) case "ipvlan": parseIPVlanData(link, data) case "macvlan": @@ -772,6 +859,60 @@ func parseVxlanData(link Link, data []syscall.NetlinkRouteAttr) { } } +func parseBondData(link Link, data []syscall.NetlinkRouteAttr) { + bond := NewBond(LinkAttrs{}) + for i := range data { + switch data[i].Attr.Type { + case nl.IFLA_BOND_MODE: + bond.Mode = BondMode(data[i].Value[0]) + case nl.IFLA_BOND_ACTIVE_SLAVE: + bond.ActiveSlave = int(native.Uint32(data[i].Value[0:4])) + case nl.IFLA_BOND_MIIMON: + bond.Miimon = int(native.Uint32(data[i].Value[0:4])) + case nl.IFLA_BOND_UPDELAY: + bond.UpDelay = int(native.Uint32(data[i].Value[0:4])) + case nl.IFLA_BOND_DOWNDELAY: + bond.DownDelay = int(native.Uint32(data[i].Value[0:4])) + case nl.IFLA_BOND_USE_CARRIER: + bond.UseCarrier = int(data[i].Value[0]) + case nl.IFLA_BOND_ARP_INTERVAL: + bond.ArpInterval = int(native.Uint32(data[i].Value[0:4])) + case nl.IFLA_BOND_ARP_IP_TARGET: + // TODO: implement + case nl.IFLA_BOND_ARP_VALIDATE: + bond.ArpValidate = BondArpValidate(native.Uint32(data[i].Value[0:4])) + case nl.IFLA_BOND_ARP_ALL_TARGETS: + bond.ArpAllTargets = BondArpAllTargets(native.Uint32(data[i].Value[0:4])) + case nl.IFLA_BOND_PRIMARY: + bond.Primary = int(native.Uint32(data[i].Value[0:4])) + case nl.IFLA_BOND_PRIMARY_RESELECT: + bond.PrimaryReselect = BondPrimaryReselect(data[i].Value[0]) + case nl.IFLA_BOND_FAIL_OVER_MAC: + bond.FailOverMac = BondFailOverMac(data[i].Value[0]) + case nl.IFLA_BOND_XMIT_HASH_POLICY: + bond.XmitHashPolicy = BondXmitHashPolicy(data[i].Value[0]) + case nl.IFLA_BOND_RESEND_IGMP: + bond.ResendIgmp = int(native.Uint32(data[i].Value[0:4])) + case nl.IFLA_BOND_NUM_PEER_NOTIF: + bond.NumPeerNotif = int(data[i].Value[0]) + case nl.IFLA_BOND_ALL_SLAVES_ACTIVE: + bond.AllSlavesActive = int(data[i].Value[0]) + case nl.IFLA_BOND_MIN_LINKS: + bond.MinLinks = int(native.Uint32(data[i].Value[0:4])) + case nl.IFLA_BOND_LP_INTERVAL: + bond.LpInterval = int(native.Uint32(data[i].Value[0:4])) + case nl.IFLA_BOND_PACKETS_PER_SLAVE: + bond.PackersPerSlave = int(native.Uint32(data[i].Value[0:4])) + case nl.IFLA_BOND_AD_LACP_RATE: + bond.LacpRate = BondLacpRate(data[i].Value[0]) + case nl.IFLA_BOND_AD_SELECT: + bond.AdSelect = BondAdSelect(data[i].Value[0]) + case nl.IFLA_BOND_AD_INFO: + // TODO: implement + } + } +} + func parseIPVlanData(link Link, data []syscall.NetlinkRouteAttr) { ipv := link.(*IPVlan) for _, datum := range data { diff --git a/link_test.go b/link_test.go index 3b640f1..74b0718 100644 --- a/link_test.go +++ b/link_test.go @@ -262,6 +262,13 @@ func TestLinkAddDelVeth(t *testing.T) { testLinkAddDel(t, &Veth{LinkAttrs{Name: "foo", TxQLen: testTxQLen, MTU: 1400}, "bar"}) } +func TestLinkAddDelBond(t *testing.T) { + tearDown := setUpNetlinkTest(t) + defer tearDown() + + testLinkAddDel(t, NewBond(LinkAttrs{Name: "foo"})) +} + func TestLinkAddVethWithDefaultTxQLen(t *testing.T) { tearDown := setUpNetlinkTest(t) defer tearDown() diff --git a/nl/link_linux.go b/nl/link_linux.go index 1f9ab08..a2ff65f 100644 --- a/nl/link_linux.go +++ b/nl/link_linux.go @@ -102,3 +102,49 @@ const ( MACVLAN_MODE_PASSTHRU = 8 MACVLAN_MODE_SOURCE = 16 ) + +const ( + IFLA_BOND_UNSPEC = iota + IFLA_BOND_MODE + IFLA_BOND_ACTIVE_SLAVE + IFLA_BOND_MIIMON + IFLA_BOND_UPDELAY + IFLA_BOND_DOWNDELAY + IFLA_BOND_USE_CARRIER + IFLA_BOND_ARP_INTERVAL + IFLA_BOND_ARP_IP_TARGET + IFLA_BOND_ARP_VALIDATE + IFLA_BOND_ARP_ALL_TARGETS + IFLA_BOND_PRIMARY + IFLA_BOND_PRIMARY_RESELECT + IFLA_BOND_FAIL_OVER_MAC + IFLA_BOND_XMIT_HASH_POLICY + IFLA_BOND_RESEND_IGMP + IFLA_BOND_NUM_PEER_NOTIF + IFLA_BOND_ALL_SLAVES_ACTIVE + IFLA_BOND_MIN_LINKS + IFLA_BOND_LP_INTERVAL + IFLA_BOND_PACKETS_PER_SLAVE + IFLA_BOND_AD_LACP_RATE + IFLA_BOND_AD_SELECT + IFLA_BOND_AD_INFO +) + +const ( + IFLA_BOND_AD_INFO_UNSPEC = iota + IFLA_BOND_AD_INFO_AGGREGATOR + IFLA_BOND_AD_INFO_NUM_PORTS + IFLA_BOND_AD_INFO_ACTOR_KEY + IFLA_BOND_AD_INFO_PARTNER_KEY + IFLA_BOND_AD_INFO_PARTNER_MAC +) + +const ( + IFLA_BOND_SLAVE_UNSPEC = iota + IFLA_BOND_SLAVE_STATE + IFLA_BOND_SLAVE_MII_STATUS + IFLA_BOND_SLAVE_LINK_FAILURE_COUNT + IFLA_BOND_SLAVE_PERM_HWADDR + IFLA_BOND_SLAVE_QUEUE_ID + IFLA_BOND_SLAVE_AD_AGGREGATOR_ID +) From 97758f316b211978ff25f36032ac8aa09be37fb3 Mon Sep 17 00:00:00 2001 From: Hubert Krauze Date: Wed, 9 Dec 2015 14:07:11 +0100 Subject: [PATCH 14/15] Remove FlagMask from Rule --- rule.go | 41 +++++++++++++---------------- rule_linux.go | 72 +++++++++++++++++---------------------------------- rule_test.go | 19 +++++--------- 3 files changed, 49 insertions(+), 83 deletions(-) diff --git a/rule.go b/rule.go index 4048077..bd699a7 100644 --- a/rule.go +++ b/rule.go @@ -3,46 +3,41 @@ package netlink import ( "fmt" "net" -) -// Flag mask for rule options. Rule.FlagMask must be set to on for option to work. -const ( - RULE_PRIORITY_MASK = 1 << (1 + iota) - RULE_FWMARK_MASK - RULE_FWMASK_MASK - RULE_FLOW_MASK - RULE_TABLE_MASK - RULE_SUPPRESS_PREFIXLEN_MASK - RULE_SUPPRESS_IFGROUP_MASK - RULE_IIFNAME_MASK - RULE_OIFNAME_MASK - RULE_GOTO_MASK + "github.com/vishvananda/netlink/nl" ) // Rule represents a netlink rule. type Rule struct { - Family int - Tos int - Scope int - Table int - Protocol int - Type int + *nl.RtMsg Priority int + Table int Mark int Mask int + TunID uint Goto int Src *net.IPNet Dst *net.IPNet - Flow int // IPv4 only - Flags int + Flow int IifName string OifName string SuppressIfgroup int SuppressPrefixlen int - - FlagMask uint64 } func (r Rule) String() string { return fmt.Sprintf("ip rule %d: from %s table %d", r.Priority, r.Src, r.Table) } + +// NewRule return empty rules. +func NewRule() *Rule { + return &Rule{ + SuppressIfgroup: -1, + SuppressPrefixlen: -1, + Priority: -1, + Mark: -1, + Mask: -1, + Goto: -1, + Flow: -1, + } +} diff --git a/rule_linux.go b/rule_linux.go index 1d6d7db..027758a 100644 --- a/rule_linux.go +++ b/rule_linux.go @@ -24,15 +24,17 @@ func RuleDel(rule *Rule) error { func ruleHandle(rule *Rule, req *nl.NetlinkRequest) error { msg := nl.NewRtMsg() - family := syscall.AF_INET + msg.Family = syscall.AF_INET + var dstFamily uint8 var rtAttrs []*nl.RtAttr if rule.Dst != nil && rule.Dst.IP != nil { dstLen, _ := rule.Dst.Mask.Size() msg.Dst_len = uint8(dstLen) - family = nl.GetIPFamily(rule.Dst.IP) + msg.Family = uint8(nl.GetIPFamily(rule.Dst.IP)) + dstFamily = msg.Family var dstData []byte - if family == syscall.AF_INET { + if msg.Family == syscall.AF_INET { dstData = rule.Dst.IP.To4() } else { dstData = rule.Dst.IP.To16() @@ -41,15 +43,14 @@ func ruleHandle(rule *Rule, req *nl.NetlinkRequest) error { } if rule.Src != nil && rule.Src.IP != nil { - srcFamily := nl.GetIPFamily(rule.Src.IP) - if family != -1 && family != srcFamily { + msg.Family = uint8(nl.GetIPFamily(rule.Src.IP)) + if dstFamily != 0 && dstFamily != msg.Family { return fmt.Errorf("source and destination ip are not the same IP family") } srcLen, _ := rule.Src.Mask.Size() msg.Src_len = uint8(srcLen) - family = srcFamily var srcData []byte - if srcFamily == syscall.AF_INET { + if msg.Family == syscall.AF_INET { srcData = rule.Src.IP.To4() } else { srcData = rule.Src.IP.To16() @@ -57,18 +58,9 @@ func ruleHandle(rule *Rule, req *nl.NetlinkRequest) error { rtAttrs = append(rtAttrs, nl.NewRtAttr(syscall.RTA_SRC, srcData)) } - msg.Family = uint8(family) - - if rule.Type != 0 { - msg.Type = uint8(rule.Type) - } - if rule.FlagMask&RULE_GOTO_MASK != 0 { - msg.Type = nl.FR_ACT_NOP - } - if rule.FlagMask&RULE_TABLE_MASK != 0 { - if rule.Table < 256 { - msg.Table = uint8(rule.Table) - } else { + if rule.Table >= 0 { + msg.Table = uint8(rule.Table) + if rule.Table >= 256 { msg.Table = syscall.RT_TABLE_UNSPEC } } @@ -83,32 +75,32 @@ func ruleHandle(rule *Rule, req *nl.NetlinkRequest) error { native = nl.NativeEndian() ) - if rule.FlagMask&RULE_PRIORITY_MASK != 0 { + if rule.Priority >= 0 { native.PutUint32(b, uint32(rule.Priority)) req.AddData(nl.NewRtAttr(nl.FRA_PRIORITY, b)) } - if rule.FlagMask&RULE_FWMARK_MASK != 0 { + if rule.Mark >= 0 { native.PutUint32(b, uint32(rule.Mark)) req.AddData(nl.NewRtAttr(nl.FRA_FWMARK, b)) } - if rule.FlagMask&RULE_FWMASK_MASK != 0 { + if rule.Mask >= 0 { native.PutUint32(b, uint32(rule.Mask)) req.AddData(nl.NewRtAttr(nl.FRA_FWMASK, b)) } - if rule.FlagMask&RULE_FLOW_MASK != 0 { + if rule.Flow >= 0 { native.PutUint32(b, uint32(rule.Flow)) req.AddData(nl.NewRtAttr(nl.FRA_FLOW, b)) } - if rule.FlagMask&RULE_TABLE_MASK != 0 && rule.Table >= 256 { + if rule.Table >= 256 { native.PutUint32(b, uint32(rule.Table)) req.AddData(nl.NewRtAttr(nl.FRA_TABLE, b)) } - if msg.Table != 0 { - if rule.FlagMask&RULE_SUPPRESS_PREFIXLEN_MASK != 0 { + if msg.Table > 0 { + if rule.SuppressPrefixlen >= 0 { native.PutUint32(b, uint32(rule.SuppressPrefixlen)) req.AddData(nl.NewRtAttr(nl.FRA_SUPPRESS_PREFIXLEN, b)) } - if rule.FlagMask&RULE_SUPPRESS_IFGROUP_MASK != 0 { + if rule.SuppressIfgroup >= 0 { native.PutUint32(b, uint32(rule.SuppressIfgroup)) req.AddData(nl.NewRtAttr(nl.FRA_SUPPRESS_IFGROUP, b)) } @@ -119,7 +111,8 @@ func ruleHandle(rule *Rule, req *nl.NetlinkRequest) error { if rule.OifName != "" { req.AddData(nl.NewRtAttr(nl.FRA_OIFNAME, []byte(rule.OifName))) } - if rule.FlagMask&RULE_GOTO_MASK != 0 { + if rule.Goto >= 0 { + msg.Type = nl.FR_ACT_NOP native.PutUint32(b, uint32(rule.Goto)) req.AddData(nl.NewRtAttr(nl.FRA_GOTO, b)) } @@ -149,23 +142,13 @@ func RuleList(family int) ([]Rule, error) { return nil, err } - rule := Rule{ - Table: int(msg.Table), - Protocol: int(msg.Protocol), - Type: int(msg.Type), - Scope: int(msg.Scope), - Tos: int(msg.Tos), - Family: int(msg.Family), - Flags: int(msg.Flags), - SuppressPrefixlen: -1, - SuppressIfgroup: -1, - } + rule := NewRule() + rule.RtMsg = msg for j := range attrs { switch attrs[j].Attr.Type { case syscall.RTA_TABLE: rule.Table = int(native.Uint32(attrs[j].Value[0:4])) - rule.FlagMask |= RULE_TABLE_MASK case nl.FRA_SRC: rule.Src = &net.IPNet{ IP: attrs[j].Value, @@ -178,10 +161,8 @@ func RuleList(family int) ([]Rule, error) { } case nl.FRA_FWMARK: rule.Mark = int(native.Uint32(attrs[j].Value[0:4])) - rule.FlagMask |= RULE_FWMARK_MASK case nl.FRA_FWMASK: rule.Mask = int(native.Uint32(attrs[j].Value[0:4])) - rule.FlagMask |= RULE_FWMASK_MASK case nl.FRA_IIFNAME: rule.IifName = string(attrs[j].Value[:len(attrs[j].Value)-1]) case nl.FRA_OIFNAME: @@ -190,26 +171,21 @@ func RuleList(family int) ([]Rule, error) { i := native.Uint32(attrs[j].Value[0:4]) if i != 0xffffffff { rule.SuppressPrefixlen = int(i) - rule.FlagMask |= RULE_SUPPRESS_PREFIXLEN_MASK } case nl.FRA_SUPPRESS_IFGROUP: i := native.Uint32(attrs[j].Value[0:4]) if i != 0xffffffff { rule.SuppressIfgroup = int(i) - rule.FlagMask |= RULE_SUPPRESS_IFGROUP_MASK } case nl.FRA_FLOW: rule.Flow = int(native.Uint32(attrs[j].Value[0:4])) - rule.FlagMask |= RULE_FLOW_MASK case nl.FRA_GOTO: rule.Goto = int(native.Uint32(attrs[j].Value[0:4])) - rule.FlagMask |= RULE_GOTO_MASK case nl.FRA_PRIORITY: rule.Priority = int(native.Uint32(attrs[j].Value[0:4])) - rule.FlagMask |= RULE_PRIORITY_MASK } } - res = append(res, rule) + res = append(res, *rule) } return res, nil diff --git a/rule_test.go b/rule_test.go index 20daa9e..63f995c 100644 --- a/rule_test.go +++ b/rule_test.go @@ -15,18 +15,13 @@ func TestRuleAddDel(t *testing.T) { t.Fatal(err) } - rule := &Rule{ - Table: syscall.RT_TABLE_MAIN, - Src: srcNet, - Dst: dstNet, - Priority: 5, - OifName: "lo", - IifName: "lo", - FlagMask: RULE_TABLE_MASK | - RULE_IIFNAME_MASK | - RULE_PRIORITY_MASK | - RULE_OIFNAME_MASK, - } + 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) } From 487b33a08344df14c05faeb8e8421db99bfeaafe Mon Sep 17 00:00:00 2001 From: Hubert Krauze Date: Wed, 9 Dec 2015 14:14:24 +0100 Subject: [PATCH 15/15] Support Rule tun id --- nl/syscall.go | 2 +- rule_linux.go | 6 ++++++ 2 files changed, 7 insertions(+), 1 deletion(-) diff --git a/nl/syscall.go b/nl/syscall.go index e33dcf6..47aa632 100644 --- a/nl/syscall.go +++ b/nl/syscall.go @@ -15,7 +15,7 @@ const ( FRA_UNUSED5 FRA_FWMARK /* mark */ FRA_FLOW /* flow/class id */ - FRA_UNUSED6 + FRA_TUN_ID FRA_SUPPRESS_IFGROUP FRA_SUPPRESS_PREFIXLEN FRA_TABLE /* Extended table id */ diff --git a/rule_linux.go b/rule_linux.go index 027758a..ba84be0 100644 --- a/rule_linux.go +++ b/rule_linux.go @@ -91,6 +91,10 @@ func ruleHandle(rule *Rule, req *nl.NetlinkRequest) error { native.PutUint32(b, uint32(rule.Flow)) req.AddData(nl.NewRtAttr(nl.FRA_FLOW, b)) } + if rule.TunID > 0 { + native.PutUint32(b, uint32(rule.TunID)) + req.AddData(nl.NewRtAttr(nl.FRA_TUN_ID, b)) + } if rule.Table >= 256 { native.PutUint32(b, uint32(rule.Table)) req.AddData(nl.NewRtAttr(nl.FRA_TABLE, b)) @@ -163,6 +167,8 @@ func RuleList(family int) ([]Rule, error) { rule.Mark = int(native.Uint32(attrs[j].Value[0:4])) case nl.FRA_FWMASK: rule.Mask = int(native.Uint32(attrs[j].Value[0:4])) + case nl.FRA_TUN_ID: + rule.TunID = uint(native.Uint64(attrs[j].Value[0:4])) case nl.FRA_IIFNAME: rule.IifName = string(attrs[j].Value[:len(attrs[j].Value)-1]) case nl.FRA_OIFNAME: