diff --git a/addr_linux.go b/addr_linux.go index 0333d25..4d771d9 100644 --- a/addr_linux.go +++ b/addr_linux.go @@ -5,13 +5,13 @@ import ( "net" "strings" "syscall" - + "github.com/vishvananda/netlink/nl" ) // AddrAdd will add an IP address to a link device. // Equivalent to: `ip addr del $addr dev $link` -func AddrAdd(link *Link, addr *Addr) error { +func AddrAdd(link Link, addr *Addr) error { req := nl.NewNetlinkRequest(syscall.RTM_NEWADDR, syscall.NLM_F_CREATE|syscall.NLM_F_EXCL|syscall.NLM_F_ACK) return addrHandle(link, addr, req) @@ -19,21 +19,22 @@ func AddrAdd(link *Link, addr *Addr) error { // 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 { +func AddrDel(link Link, addr *Addr) error { req := nl.NewNetlinkRequest(syscall.RTM_DELADDR, syscall.NLM_F_ACK) return addrHandle(link, addr, req) } -func addrHandle(link *Link, addr *Addr, req *nl.NetlinkRequest) error { - if addr.Label != "" && !strings.HasPrefix(addr.Label, link.Name) { +func addrHandle(link Link, addr *Addr, req *nl.NetlinkRequest) error { + base := link.Attrs() + if addr.Label != "" && !strings.HasPrefix(addr.Label, base.Name) { return fmt.Errorf("label must begin with interface name") } - ensureIndex(link) + ensureIndex(base) family := nl.GetIPFamily(addr.IP) msg := nl.NewIfAddrmsg(family) - msg.Index = uint32(link.Index) + msg.Index = uint32(base.Index) prefixlen, _ := addr.Mask.Size() msg.Prefixlen = uint8(prefixlen) req.AddData(msg) @@ -63,7 +64,7 @@ func addrHandle(link *Link, addr *Addr, req *nl.NetlinkRequest) error { // AddrList gets a list of IP addresses in the system. // Equivalent to: `ip addr show`. // The list can be filtered by link and ip family. -func AddrList(link *Link, family int) ([]Addr, error) { +func AddrList(link Link, family int) ([]Addr, error) { req := nl.NewNetlinkRequest(syscall.RTM_GETADDR, syscall.NLM_F_DUMP) msg := nl.NewIfInfomsg(family) req.AddData(msg) @@ -73,13 +74,18 @@ func AddrList(link *Link, family int) ([]Addr, error) { return nil, err } - ensureIndex(link) + index := 0 + if link != nil { + base := link.Attrs() + ensureIndex(base) + index = base.Index + } res := make([]Addr, 0) for _, m := range msgs { msg := nl.DeserializeIfAddrmsg(m) - if link != nil && msg.Index != uint32(link.Index) { + if link != nil && msg.Index != uint32(index) { // Ignore messages from other interfaces continue } diff --git a/link.go b/link.go index e13fa8e..dceba80 100644 --- a/link.go +++ b/link.go @@ -4,21 +4,119 @@ import ( "net" ) -// Link represents a link device from netlink. The Type is a string -// representing the type of device. Currently supported types include: -// "dummy", "bridge", "vlan", "macvlan", and "veth". Some of the -// members of Link only apply to some types of link devices. -type Link struct { - Type string +// Link represents a link device from netlink. Shared link attributes +// like name may be retrieved using the Attrs() method. Unique data +// can be retrieved by casting the object to the proper type. +type Link interface { + Attrs() *LinkAttrs + Type() string +} + +// LinkAttrs represents data shared by most link types +type LinkAttrs struct { Index int MTU int Name string HardwareAddr net.HardwareAddr Flags net.Flags - Parent *Link // vlan and macvlan - Master *Link // bridge only - VlanId int // vlan only - PeerName string // veth on create only + ParentIndex int // index of the parent link device + MasterIndex int // must be the index of a bridge +} + +// Device links cannot be created via netlink. These links +// are links created by udev like 'lo' and 'etho0' +type Device struct { + LinkAttrs +} + +func (device *Device) Attrs() *LinkAttrs { + return &device.LinkAttrs +} + +func (device *Device) Type() string { + return "device" +} + +// Dummy links are dummy ethernet devices +type Dummy struct { + LinkAttrs +} + +func (dummy *Dummy) Attrs() *LinkAttrs { + return &dummy.LinkAttrs +} + +func (dummy *Dummy) Type() string { + return "dummy" +} + +// Bridge links are simple linux bridges +type Bridge struct { + LinkAttrs +} + +func (bridge *Bridge) Attrs() *LinkAttrs { + return &bridge.LinkAttrs +} + +func (bridge *Bridge) Type() string { + return "bridge" +} + +// Vlan links have ParentIndex set in their Attrs() +type Vlan struct { + LinkAttrs + VlanId int +} + +func (vlan *Vlan) Attrs() *LinkAttrs { + return &vlan.LinkAttrs +} + +func (vlan *Vlan) Type() string { + return "vlan" +} + +// Macvlan links have ParentIndex set in their Attrs() +type Macvlan struct { + LinkAttrs +} + +func (macvlan *Macvlan) Attrs() *LinkAttrs { + return &macvlan.LinkAttrs +} + +func (macvlan *Macvlan) Type() string { + return "macvlan" +} + +// Veth devices must specify PeerName on create +type Veth struct { + LinkAttrs + PeerName string // veth on create only +} + +func (veth *Veth) Attrs() *LinkAttrs { + return &veth.LinkAttrs +} + +func (veth *Veth) Type() string { + return "veth" +} + +// Generic links represent types that are not currently understood +// by this netlink library. +type Generic struct { + LinkAttrs + LinkType string +} + +func (generic *Generic) Attrs() *LinkAttrs { + return &generic.LinkAttrs +} + +func (generic *Generic) Type() string { + return generic.LinkType } // iproute2 supported devices; diff --git a/link_linux.go b/link_linux.go index 30859b6..c09e150 100644 --- a/link_linux.go +++ b/link_linux.go @@ -9,25 +9,26 @@ import ( "github.com/vishvananda/netlink/nl" ) -func ensureIndex(link *Link) { +func ensureIndex(link *LinkAttrs) { if link != nil && link.Index == 0 { newlink, _ := LinkByName(link.Name) if newlink != nil { - link.Index = newlink.Index + link.Index = newlink.Attrs().Index } } } // LinkSetUp enables the link device. // Equivalent to: `ip link set $link up` -func LinkSetUp(link *Link) error { - ensureIndex(link) +func LinkSetUp(link Link) error { + base := link.Attrs() + ensureIndex(base) req := nl.NewNetlinkRequest(syscall.RTM_NEWLINK, syscall.NLM_F_ACK) msg := nl.NewIfInfomsg(syscall.AF_UNSPEC) msg.Change = syscall.IFF_UP msg.Flags = syscall.IFF_UP - msg.Index = int32(link.Index) + msg.Index = int32(base.Index) req.AddData(msg) _, err := req.Execute(syscall.NETLINK_ROUTE, 0) @@ -36,14 +37,15 @@ func LinkSetUp(link *Link) error { // LinkSetUp disables link device. // Equivalent to: `ip link set $link down` -func LinkSetDown(link *Link) error { - ensureIndex(link) +func LinkSetDown(link Link) error { + base := link.Attrs() + ensureIndex(base) req := nl.NewNetlinkRequest(syscall.RTM_NEWLINK, syscall.NLM_F_ACK) msg := nl.NewIfInfomsg(syscall.AF_UNSPEC) msg.Change = syscall.IFF_UP msg.Flags = 0 & ^syscall.IFF_UP - msg.Index = int32(link.Index) + msg.Index = int32(base.Index) req.AddData(msg) _, err := req.Execute(syscall.NETLINK_ROUTE, 0) @@ -52,14 +54,15 @@ func LinkSetDown(link *Link) error { // LinkSetMTU sets the mtu of the link device. // Equivalent to: `ip link set $link mtu $mtu` -func LinkSetMTU(link *Link, mtu int) error { - ensureIndex(link) +func LinkSetMTU(link Link, mtu int) error { + base := link.Attrs() + ensureIndex(base) req := nl.NewNetlinkRequest(syscall.RTM_SETLINK, syscall.NLM_F_ACK) msg := nl.NewIfInfomsg(syscall.AF_UNSPEC) msg.Type = syscall.RTM_SETLINK msg.Flags = syscall.NLM_F_REQUEST - msg.Index = int32(link.Index) + msg.Index = int32(base.Index) msg.Change = nl.DEFAULT_CHANGE req.AddData(msg) @@ -76,32 +79,38 @@ func LinkSetMTU(link *Link, mtu int) error { return err } -// LinkSetMTU sets the master of the link device. This only works -// for bridges. +// LinkSetMaster sets the master of the link device. // Equivalent to: `ip link set $link master $master` -func LinkSetMaster(link *Link, master *Link) error { - ensureIndex(link) - ensureIndex(master) +func LinkSetMaster(link Link, master *Bridge) error { + index := 0 + if master != nil { + masterBase := master.Attrs() + ensureIndex(masterBase) + index = masterBase.Index + } + return LinkSetMasterByIndex(link, index) +} + +// LinkSetMasterByIndex sets the master of the link device. +// Equivalent to: `ip link set $link master $master` +func LinkSetMasterByIndex(link Link, masterIndex int) error { + base := link.Attrs() + ensureIndex(base) req := nl.NewNetlinkRequest(syscall.RTM_SETLINK, syscall.NLM_F_ACK) msg := nl.NewIfInfomsg(syscall.AF_UNSPEC) msg.Type = syscall.RTM_SETLINK msg.Flags = syscall.NLM_F_REQUEST - msg.Index = int32(link.Index) + msg.Index = int32(base.Index) msg.Change = nl.DEFAULT_CHANGE req.AddData(msg) var ( b = make([]byte, 4) native = nl.NativeEndian() - index = 0 ) - if master != nil { - index = master.Index - } - - native.PutUint32(b, uint32(index)) + native.PutUint32(b, uint32(masterIndex)) data := nl.NewRtAttr(syscall.IFLA_MASTER, b) req.AddData(data) @@ -113,13 +122,15 @@ func LinkSetMaster(link *Link, master *Link) error { // LinkSetNsPid puts the device into a new network namespace. The // pid must be a pid of a running process. // Equivalent to: `ip link set $link netns $pid` -func LinkSetNsPid(link *Link, nspid int) error { +func LinkSetNsPid(link Link, nspid int) error { + base := link.Attrs() + ensureIndex(base) req := nl.NewNetlinkRequest(syscall.RTM_SETLINK, syscall.NLM_F_ACK) msg := nl.NewIfInfomsg(syscall.AF_UNSPEC) msg.Type = syscall.RTM_SETLINK msg.Flags = syscall.NLM_F_REQUEST - msg.Index = int32(link.Index) + msg.Index = int32(base.Index) msg.Change = nl.DEFAULT_CHANGE req.AddData(msg) @@ -139,13 +150,15 @@ func LinkSetNsPid(link *Link, nspid int) error { // LinkSetNsPid puts the device into a new network namespace. The // fd must be an open file descriptor to a network namespace. // Similar to: `ip link set $link netns $ns` -func LinkSetNsFd(link *Link, fd int) error { +func LinkSetNsFd(link Link, fd int) error { + base := link.Attrs() + ensureIndex(base) req := nl.NewNetlinkRequest(syscall.RTM_SETLINK, syscall.NLM_F_ACK) msg := nl.NewIfInfomsg(syscall.AF_UNSPEC) msg.Type = syscall.RTM_SETLINK msg.Flags = syscall.NLM_F_REQUEST - msg.Index = int32(link.Index) + msg.Index = int32(base.Index) msg.Change = nl.DEFAULT_CHANGE req.AddData(msg) @@ -165,12 +178,13 @@ func LinkSetNsFd(link *Link, fd int) error { // 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` -func LinkAdd(link *Link) error { +func LinkAdd(link Link) error { // TODO: set mtu and hardware address // TODO: support extra data for macvlan + base := link.Attrs() - if link.Type == "" || link.Name == "" { - return fmt.Errorf("Neither link.Name nor link.Type can be empty!") + if base.Name == "" { + return fmt.Errorf("LinkAttrs.Name cannot be empty!") } req := nl.NewNetlinkRequest(syscall.RTM_NEWLINK, syscall.NLM_F_CREATE|syscall.NLM_F_EXCL|syscall.NLM_F_ACK) @@ -180,30 +194,29 @@ func LinkAdd(link *Link) error { native := nl.NativeEndian() - if link.Parent != nil { - ensureIndex(link.Parent) + if base.ParentIndex != 0 { b := make([]byte, 4) - native.PutUint32(b, uint32(link.Parent.Index)) + native.PutUint32(b, uint32(base.ParentIndex)) data := nl.NewRtAttr(syscall.IFLA_LINK, b) req.AddData(data) } - nameData := nl.NewRtAttr(syscall.IFLA_IFNAME, nl.ZeroTerminated(link.Name)) + nameData := nl.NewRtAttr(syscall.IFLA_IFNAME, nl.ZeroTerminated(base.Name)) req.AddData(nameData) linkInfo := nl.NewRtAttr(syscall.IFLA_LINKINFO, nil) - nl.NewRtAttrChild(linkInfo, nl.IFLA_INFO_KIND, nl.NonZeroTerminated(link.Type)) + nl.NewRtAttrChild(linkInfo, nl.IFLA_INFO_KIND, nl.NonZeroTerminated(link.Type())) - if link.Type == "vlan" { + if vlan, ok := link.(*Vlan); ok { b := make([]byte, 2) - native.PutUint16(b, uint16(link.VlanId)) + native.PutUint16(b, uint16(vlan.VlanId)) data := nl.NewRtAttrChild(linkInfo, nl.IFLA_INFO_DATA, nil) nl.NewRtAttrChild(data, nl.IFLA_VLAN_ID, b) - } else if link.Type == "veth" { + } else if veth, ok := link.(*Veth); ok { data := nl.NewRtAttrChild(linkInfo, nl.IFLA_INFO_DATA, nil) peer := nl.NewRtAttrChild(data, nl.VETH_INFO_PEER, nil) nl.NewIfInfomsgChild(peer, syscall.AF_UNSPEC) - nl.NewRtAttrChild(peer, syscall.IFLA_IFNAME, nl.ZeroTerminated(link.PeerName)) + nl.NewRtAttrChild(peer, syscall.IFLA_IFNAME, nl.ZeroTerminated(veth.PeerName)) } req.AddData(linkInfo) @@ -213,9 +226,12 @@ func LinkAdd(link *Link) error { return err } + ensureIndex(base) + // can't set master during create, so set it afterwards - if link.Master != nil { - return LinkSetMaster(link, link.Master) + if base.MasterIndex != 0 { + // TODO: verify MasterIndex is actually a bridge? + return LinkSetMasterByIndex(link, base.MasterIndex) } return nil } @@ -223,13 +239,15 @@ func LinkAdd(link *Link) error { // LinkAdd adds a new link device. Either Index or Name must be set in // the link object for it to be deleted. The other values are ignored. // Equivalent to: `ip link del $link` -func LinkDel(link *Link) error { - ensureIndex(link) +func LinkDel(link Link) error { + base := link.Attrs() + + ensureIndex(base) req := nl.NewNetlinkRequest(syscall.RTM_DELLINK, syscall.NLM_F_ACK) msg := nl.NewIfInfomsg(syscall.AF_UNSPEC) - msg.Index = int32(link.Index) + msg.Index = int32(base.Index) req.AddData(msg) _, err := req.Execute(syscall.NETLINK_ROUTE, 0) @@ -237,28 +255,30 @@ func LinkDel(link *Link) error { } // LikByName finds a link by name and returns a pointer to the object. -func LinkByName(name string) (*Link, error) { +func LinkByName(name string) (Link, error) { links, err := LinkList() if err != nil { return nil, err } for _, link := range links { - if link.Name == name { - return &link, nil + base := link.Attrs() + if base.Name == name { + return link, nil } } return nil, fmt.Errorf("Link %s not found", name) } // LikByName finds a link by index and returns a pointer to the object. -func LinkByIndex(index int) (*Link, error) { +func LinkByIndex(index int) (Link, error) { links, err := LinkList() if err != nil { return nil, err } for _, link := range links { - if link.Index == index { - return &link, nil + base := link.Attrs() + if base.Index == index { + return link, nil } } return nil, fmt.Errorf("Link with index %d not found", index) @@ -290,7 +310,9 @@ func LinkList() ([]Link, error) { return nil, err } - link := Link{Index: int(msg.Index), Flags: linkFlags(msg.Flags)} + base := LinkAttrs{Index: int(msg.Index), Flags: linkFlags(msg.Flags)} + var link Link + linkType := "" for _, attr := range attrs { switch attr.Attr.Type { case syscall.IFLA_LINKINFO: @@ -301,15 +323,27 @@ func LinkList() ([]Link, error) { for _, info := range infos { switch info.Attr.Type { case nl.IFLA_INFO_KIND: - link.Type = string(info.Value[:len(info.Value)-1]) + linkType = string(info.Value[:len(info.Value)-1]) + switch linkType { + case "dummy": + link = &Dummy{} + case "bridge": + link = &Bridge{} + case "vlan": + link = &Vlan{} + case "veth": + link = &Veth{} + default: + link = &Generic{LinkType: linkType} + } case nl.IFLA_INFO_DATA: data, err := nl.ParseRouteAttr(info.Value) if err != nil { return nil, err } - switch link.Type { + switch linkType { case "vlan": - parseVlanData(&link, data, native) + parseVlanData(link, data, native) } } } @@ -321,29 +355,35 @@ func LinkList() ([]Link, error) { } } if nonzero { - link.HardwareAddr = attr.Value[:] + base.HardwareAddr = attr.Value[:] } case syscall.IFLA_IFNAME: - link.Name = string(attr.Value[:len(attr.Value)-1]) + base.Name = string(attr.Value[:len(attr.Value)-1]) case syscall.IFLA_MTU: - link.MTU = int(native.Uint32(attr.Value[0:4])) + base.MTU = int(native.Uint32(attr.Value[0:4])) case syscall.IFLA_LINK: - link.Parent = &Link{Index: int(native.Uint32(attr.Value[0:4]))} + base.ParentIndex = int(native.Uint32(attr.Value[0:4])) case syscall.IFLA_MASTER: - link.Master = &Link{Index: int(native.Uint32(attr.Value[0:4]))} + base.MasterIndex = int(native.Uint32(attr.Value[0:4])) } } + // Links that don't have IFLA_INFO_KIND are hardware devices + if link == nil { + link = &Device{} + } + *link.Attrs() = base res = append(res, link) } return res, nil } -func parseVlanData(link *Link, data []syscall.NetlinkRouteAttr, native binary.ByteOrder) { +func parseVlanData(link Link, data []syscall.NetlinkRouteAttr, native binary.ByteOrder) { + vlan, _ := link.(*Vlan) for _, datum := range data { switch datum.Attr.Type { case nl.IFLA_VLAN_ID: - link.VlanId = int(native.Uint16(datum.Value[0:2])) + vlan.VlanId = int(native.Uint16(datum.Value[0:2])) } } } diff --git a/link_test.go b/link_test.go index 837ab16..28ad9f8 100644 --- a/link_test.go +++ b/link_test.go @@ -5,7 +5,7 @@ import ( "testing" ) -func testLinkAddDel(t *testing.T, link *Link) { +func testLinkAddDel(t *testing.T, link Link) { links, err := LinkList() if err != nil { t.Fatal(err) @@ -16,34 +16,44 @@ func testLinkAddDel(t *testing.T, link *Link) { t.Fatal(err) } - l, err := LinkByName(link.Name) + base := link.Attrs() + result, err := LinkByName(base.Name) if err != nil { t.Fatal(err) } - if l.Type != link.Type { - t.Fatal("Link.Type doesn't match") - } + rBase := result.Attrs() - if l.VlanId != link.VlanId { - t.Fatal("Link.VlanId id doesn't match") - } - - if l.Parent == nil && link.Parent != nil { - t.Fatal("Created link doesn't have a Parent but it should") - } else if l.Parent != nil && link.Parent == nil { - t.Fatal("Created link has a Parent but it shouldn't") - } else if l.Parent != nil && link.Parent != nil { - if l.Parent.Index != link.Parent.Index { - t.Fatal("Link.Parent.Index doesn't match") + 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 link.PeerName != "" { - _, err := LinkByName(link.PeerName) - if err != nil { - t.Fatal("Peer %s not created", link.PeerName) + if rBase.ParentIndex == 0 && base.ParentIndex != 0 { + t.Fatal("Created link doesn't have a Parent but it should") + } else if rBase.ParentIndex != 0 && base.ParentIndex == 0 { + t.Fatal("Created link has a Parent but it shouldn't") + } else if rBase.ParentIndex != 0 && base.ParentIndex != 0 { + if rBase.ParentIndex != base.ParentIndex { + t.Fatal("Link.ParentIndex doesn't match") + } + } + + if veth, ok := link.(*Veth); ok { + if veth.PeerName != "" { + other, err := LinkByName(veth.PeerName) + if err != nil { + t.Fatal("Peer %s not created", veth.PeerName) + } + if _, ok = other.(*Veth); !ok { + t.Fatal("Peer %s is incorrect type", veth.PeerName) + } } } @@ -65,25 +75,26 @@ func TestLinkAddDelDummy(t *testing.T) { tearDown := setUpNetlinkTest(t) defer tearDown() - testLinkAddDel(t, &Link{Name: "foo", Type: "dummy"}) + testLinkAddDel(t, &Dummy{LinkAttrs{Name: "foo"}}) } func TestLinkAddDelBridge(t *testing.T) { tearDown := setUpNetlinkTest(t) defer tearDown() - testLinkAddDel(t, &Link{Name: "foo", Type: "bridge"}) + testLinkAddDel(t, &Bridge{LinkAttrs{Name: "foo"}}) } func TestLinkAddDelVlan(t *testing.T) { tearDown := setUpNetlinkTest(t) defer tearDown() - parent := &Link{Name: "foo", Type: "dummy"} + parent := &Dummy{LinkAttrs{Name: "foo"}} if err := LinkAdd(parent); err != nil { t.Fatal(err) } - testLinkAddDel(t, &Link{Name: "bar", Type: "vlan", Parent: parent, VlanId: 900}) + + testLinkAddDel(t, &Vlan{LinkAttrs{Name: "bar", ParentIndex: parent.Attrs().Index}, 900}) if err := LinkDel(parent); err != nil { t.Fatal(err) @@ -94,11 +105,12 @@ func TestLinkAddDelMacvlan(t *testing.T) { tearDown := setUpNetlinkTest(t) defer tearDown() - parent := &Link{Name: "foo", Type: "dummy"} + parent := &Dummy{LinkAttrs{Name: "foo"}} if err := LinkAdd(parent); err != nil { t.Fatal(err) } - testLinkAddDel(t, &Link{Name: "bar", Type: "macvlan", Parent: parent}) + + testLinkAddDel(t, &Macvlan{LinkAttrs{Name: "bar", ParentIndex: parent.Attrs().Index}}) if err := LinkDel(parent); err != nil { t.Fatal(err) @@ -109,18 +121,18 @@ func TestLinkAddDelVeth(t *testing.T) { tearDown := setUpNetlinkTest(t) defer tearDown() - testLinkAddDel(t, &Link{Name: "foo", Type: "veth", PeerName: "bar"}) + testLinkAddDel(t, &Veth{LinkAttrs{Name: "foo"}, "bar"}) } func TestLinkAddDelBridgeMaster(t *testing.T) { tearDown := setUpNetlinkTest(t) defer tearDown() - master := &Link{Name: "foo", Type: "bridge"} + master := &Bridge{LinkAttrs{Name: "foo"}} if err := LinkAdd(master); err != nil { t.Fatal(err) } - testLinkAddDel(t, &Link{Name: "bar", Type: "dummy", Master: master}) + testLinkAddDel(t, &Dummy{LinkAttrs{Name: "bar", MasterIndex: master.Attrs().Index}}) if err := LinkDel(master); err != nil { t.Fatal(err) @@ -131,17 +143,17 @@ func TestLinkSetUnsetResetMaster(t *testing.T) { tearDown := setUpNetlinkTest(t) defer tearDown() - master := &Link{Name: "foo", Type: "bridge"} + master := &Bridge{LinkAttrs{Name: "foo"}} if err := LinkAdd(master); err != nil { t.Fatal(err) } - newmaster := &Link{Name: "bar", Type: "bridge"} + newmaster := &Bridge{LinkAttrs{Name: "bar"}} if err := LinkAdd(newmaster); err != nil { t.Fatal(err) } - slave := &Link{Name: "baz", Type: "dummy"} + slave := &Dummy{LinkAttrs{Name: "baz"}} if err := LinkAdd(slave); err != nil { t.Fatal(err) } @@ -155,7 +167,7 @@ func TestLinkSetUnsetResetMaster(t *testing.T) { t.Fatal(err) } - if link.Master == nil || link.Master.Index != master.Index { + if link.Attrs().MasterIndex != master.Attrs().Index { t.Fatal("Master not set properly") } @@ -168,7 +180,7 @@ func TestLinkSetUnsetResetMaster(t *testing.T) { t.Fatal(err) } - if link.Master == nil || link.Master.Index != newmaster.Index { + if link.Attrs().MasterIndex != newmaster.Attrs().Index { t.Fatal("Master not reset properly") } @@ -181,7 +193,7 @@ func TestLinkSetUnsetResetMaster(t *testing.T) { t.Fatal(err) } - if link.Master != nil { + if link.Attrs().MasterIndex != 0 { t.Fatal("Master not unset properly") } if err := LinkDel(slave); err != nil { @@ -213,7 +225,7 @@ func TestLinkSetNs(t *testing.T) { } defer newns.Close() - link := &Link{Name: "foo", Type: "veth", PeerName: "bar"} + link := &Veth{LinkAttrs{Name: "foo"}, "bar"} if err := LinkAdd(link); err != nil { t.Fatal(err) } diff --git a/route.go b/route.go index 4d40ed6..57bf0e9 100644 --- a/route.go +++ b/route.go @@ -22,7 +22,7 @@ const ( // gateway. Advanced route parameters and non-main routing tables are // currently not supported. type Route struct { - Link *Link + Link Link Scope Scope Dst *net.IPNet Src net.IP @@ -30,6 +30,7 @@ type Route struct { } func (r Route) String() string { - return fmt.Sprintf("{%s Dst: %s Src: %s Gw: %s}", r.Link.Name, r.Dst.String(), + base := r.Link.Attrs() + return fmt.Sprintf("{%s Dst: %s Src: %s Gw: %s}", base.Name, r.Dst.String(), r.Src, r.Gw) } diff --git a/route_linux.go b/route_linux.go index 5af6dd6..b499d60 100644 --- a/route_linux.go +++ b/route_linux.go @@ -90,7 +90,8 @@ func routeHandle(route *Route, req *nl.NetlinkRequest) error { b = make([]byte, 4) native = nl.NativeEndian() ) - native.PutUint32(b, uint32(route.Link.Index)) + base := route.Link.Attrs() + native.PutUint32(b, uint32(base.Index)) req.AddData(nl.NewRtAttr(syscall.RTA_OIF, b)) @@ -101,7 +102,7 @@ func routeHandle(route *Route, req *nl.NetlinkRequest) error { // RouteList gets a list of routes in the system. // Equivalent to: `ip route show`. // The list can be filtered by link and ip family. -func RouteList(link *Link, family int) ([]Route, error) { +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) @@ -111,6 +112,13 @@ func RouteList(link *Link, family int) ([]Route, error) { return nil, err } + index := 0 + if link != nil { + base := link.Attrs() + ensureIndex(base) + index = base.Index + } + native := nl.NativeEndian() res := make([]Route, 0) for _, m := range msgs { @@ -144,12 +152,12 @@ func RouteList(link *Link, family int) ([]Route, error) { Mask: net.CIDRMask(int(msg.Dst_len), 8*len(attr.Value)), } case syscall.RTA_OIF: - index := int(native.Uint32(attr.Value[0:4])) - if link != nil && index != link.Index { + routeIndex := int(native.Uint32(attr.Value[0:4])) + if link != nil && routeIndex != index { // Ignore routes from other interfaces continue } - resLink, _ := LinkByIndex(index) + resLink, _ := LinkByIndex(routeIndex) route.Link = resLink } }