diff --git a/link.go b/link.go index e1695916..887c87da 100644 --- a/link.go +++ b/link.go @@ -33,6 +33,7 @@ type LinkAttrs struct { MasterIndex int // must be the index of a bridge Namespace interface{} // nil | NsPid | NsFd Alias string + AltNames []string Statistics *LinkStatistics Promisc int Allmulti int diff --git a/link_linux.go b/link_linux.go index 96468c4f..528c50f1 100644 --- a/link_linux.go +++ b/link_linux.go @@ -497,6 +497,58 @@ func (h *Handle) LinkSetAlias(link Link, name string) error { return err } +// LinkAddAltName adds a new alternative name for the link device. +// Equivalent to: `ip link property add $link altname $name` +func LinkAddAltName(link Link, name string) error { + return pkgHandle.LinkAddAltName(link, name) +} + +// LinkAddAltName adds a new alternative name for the link device. +// Equivalent to: `ip link property add $link altname $name` +func (h *Handle) LinkAddAltName(link Link, name string) error { + base := link.Attrs() + h.ensureIndex(base) + req := h.newNetlinkRequest(unix.RTM_NEWLINKPROP, unix.NLM_F_ACK) + + msg := nl.NewIfInfomsg(unix.AF_UNSPEC) + msg.Index = int32(base.Index) + req.AddData(msg) + + data := nl.NewRtAttr(unix.IFLA_PROP_LIST|unix.NLA_F_NESTED, nil) + data.AddRtAttr(unix.IFLA_ALT_IFNAME, []byte(name)) + + req.AddData(data) + + _, err := req.Execute(unix.NETLINK_ROUTE, 0) + return err +} + +// LinkDelAltName delete an alternative name for the link device. +// Equivalent to: `ip link property del $link altname $name` +func LinkDelAltName(link Link, name string) error { + return pkgHandle.LinkDelAltName(link, name) +} + +// LinkDelAltName delete an alternative name for the link device. +// Equivalent to: `ip link property del $link altname $name` +func (h *Handle) LinkDelAltName(link Link, name string) error { + base := link.Attrs() + h.ensureIndex(base) + req := h.newNetlinkRequest(unix.RTM_DELLINKPROP, unix.NLM_F_ACK) + + msg := nl.NewIfInfomsg(unix.AF_UNSPEC) + msg.Index = int32(base.Index) + req.AddData(msg) + + data := nl.NewRtAttr(unix.IFLA_PROP_LIST|unix.NLA_F_NESTED, nil) + data.AddRtAttr(unix.IFLA_ALT_IFNAME, []byte(name)) + + req.AddData(data) + + _, err := req.Execute(unix.NETLINK_ROUTE, 0) + return err +} + // LinkSetHardwareAddr sets the hardware address of the link device. // Equivalent to: `ip link set $link address $hwaddr` func LinkSetHardwareAddr(link Link, hwaddr net.HardwareAddr) error { @@ -996,28 +1048,28 @@ func LinkSetXdpFdWithFlags(link Link, fd, flags int) error { // LinkSetGSOMaxSegs sets the GSO maximum segment count of the link device. // Equivalent to: `ip link set $link gso_max_segs $maxSegs` func LinkSetGSOMaxSegs(link Link, maxSegs int) error { - return pkgHandle.LinkSetGSOMaxSegs(link, maxSegs) + return pkgHandle.LinkSetGSOMaxSegs(link, maxSegs) } // LinkSetGSOMaxSegs sets the GSO maximum segment count of the link device. // Equivalent to: `ip link set $link gso_max_segs $maxSegs` func (h *Handle) LinkSetGSOMaxSegs(link Link, maxSize int) error { - base := link.Attrs() - h.ensureIndex(base) - req := h.newNetlinkRequest(unix.RTM_SETLINK, unix.NLM_F_ACK) + base := link.Attrs() + h.ensureIndex(base) + req := h.newNetlinkRequest(unix.RTM_SETLINK, unix.NLM_F_ACK) - msg := nl.NewIfInfomsg(unix.AF_UNSPEC) - msg.Index = int32(base.Index) - req.AddData(msg) + msg := nl.NewIfInfomsg(unix.AF_UNSPEC) + msg.Index = int32(base.Index) + req.AddData(msg) - b := make([]byte, 4) - native.PutUint32(b, uint32(maxSize)) + b := make([]byte, 4) + native.PutUint32(b, uint32(maxSize)) - data := nl.NewRtAttr(unix.IFLA_GSO_MAX_SEGS, b) - req.AddData(data) + data := nl.NewRtAttr(unix.IFLA_GSO_MAX_SEGS, b) + req.AddData(data) - _, err := req.Execute(unix.NETLINK_ROUTE, 0) - return err + _, err := req.Execute(unix.NETLINK_ROUTE, 0) + return err } // LinkSetGSOMaxSize sets the IPv6 GSO maximum size of the link device. @@ -1770,6 +1822,13 @@ func (h *Handle) linkByNameDump(name string) (Link, error) { if link.Attrs().Name == name { return link, nil } + + // support finding interfaces also via altnames + for _, altName := range link.Attrs().AltNames { + if altName == name { + return link, nil + } + } } return nil, LinkNotFoundError{fmt.Errorf("Link %s not found", name)} } @@ -2136,6 +2195,18 @@ func LinkDeserialize(hdr *unix.NlMsghdr, m []byte) (Link, error) { protinfo := parseProtinfo(attrs) base.Protinfo = &protinfo } + case unix.IFLA_PROP_LIST | unix.NLA_F_NESTED: + attrs, err := nl.ParseRouteAttr(attr.Value[:]) + if err != nil { + return nil, err + } + + base.AltNames = []string{} + for _, attr := range attrs { + if attr.Attr.Type == unix.IFLA_ALT_IFNAME { + base.AltNames = append(base.AltNames, nl.BytesToString(attr.Value)) + } + } case unix.IFLA_OPERSTATE: base.OperState = LinkOperState(uint8(attr.Value[0])) case unix.IFLA_PHYS_SWITCH_ID: diff --git a/link_test.go b/link_test.go index 37b0cea6..7841f529 100644 --- a/link_test.go +++ b/link_test.go @@ -1885,6 +1885,80 @@ func TestLinkSet(t *testing.T) { } } +func TestLinkAltName(t *testing.T) { + tearDown := setUpNetlinkTest(t) + defer tearDown() + + iface := &Dummy{LinkAttrs{Name: "bar"}} + if err := LinkAdd(iface); err != nil { + t.Fatal(err) + } + + link, err := LinkByName("bar") + if err != nil { + t.Fatal(err) + } + + err = LinkAddAltName(link, "altname") + if err != nil { + t.Fatalf("Could not add altname: %v", err) + } + + err = LinkAddAltName(link, "altname2") + if err != nil { + t.Fatalf("Could not add altname: %v", err) + } + + link, err = LinkByName("bar") + if err != nil { + t.Fatal(err) + } + + altNameExist := false + altName2Exist := false + for _, altName := range link.Attrs().AltNames { + if altName == "altname" { + altNameExist = true + } else if altName == "altname2" { + altName2Exist = true + } + + } + if !altNameExist { + t.Fatal("Could not find altname") + } + + if !altName2Exist { + t.Fatal("Could not find altname2") + } + + link, err = LinkByName("altname") + if err != nil { + t.Fatal(err) + } + + err = LinkDelAltName(link, "altname") + if err != nil { + t.Fatalf("Could not delete altname: %v", err) + } + + link, err = LinkByName("bar") + if err != nil { + t.Fatal(err) + } + + altNameExist = false + for _, altName := range link.Attrs().AltNames { + if altName == "altname" { + altNameExist = true + break + } + } + if altNameExist { + t.Fatal("altname still exist") + } +} + func TestLinkSetARP(t *testing.T) { tearDown := setUpNetlinkTest(t) defer tearDown()