From 205a160d2eb0fadbfcf7c39d7223a7eee20e1b97 Mon Sep 17 00:00:00 2001 From: Takushi Fujiwara Date: Mon, 16 Sep 2019 12:08:06 +0000 Subject: [PATCH] Add bond slave information This PR refers to PR@lebauce and add some changes. - Added some tests to retrieve bond slave information. - Link.BondSlave is changed to LinkSlave interface. - BondSlaveState.String() returns UPPER case. (same as iproute2) - BondSlaveMiiStatus.String() returns UPPER case. (same as iproute2) --- link.go | 68 ++++++++++++++++++++++++++++++++++++ link_linux.go | 90 +++++++++++++++++++++++++++++++++++++++++++++--- link_test.go | 75 ++++++++++++++++++++++++++++++++++++++++ nl/link_linux.go | 6 +++- 4 files changed, 234 insertions(+), 5 deletions(-) diff --git a/link.go b/link.go index 3d62790..4c0f605 100644 --- a/link.go +++ b/link.go @@ -4,6 +4,7 @@ import ( "fmt" "net" "os" + "strconv" ) // Link represents a link device from netlink. Shared link attributes @@ -45,6 +46,12 @@ type LinkAttrs struct { GSOMaxSegs uint32 Vfs []VfInfo // virtual functions available on link Group uint32 + Slave LinkSlave +} + +// LinkSlave represents a slave device. +type LinkSlave interface { + SlaveType() string } // VfInfo represents configuration of virtual function @@ -749,6 +756,67 @@ func (bond *Bond) Type() string { return "bond" } +// BondSlaveState represents the values of the IFLA_BOND_SLAVE_STATE bond slave +// attribute, which contains the state of the bond slave. +type BondSlaveState uint8 + +const ( + BondStateActive = iota // Link is active. + BondStateBackup // Link is backup. +) + +func (s BondSlaveState) String() string { + switch s { + case BondStateActive: + return "ACTIVE" + case BondStateBackup: + return "BACKUP" + default: + return strconv.Itoa(int(s)) + } +} + +// BondSlaveState represents the values of the IFLA_BOND_SLAVE_MII_STATUS bond slave +// attribute, which contains the status of MII link monitoring +type BondSlaveMiiStatus uint8 + +const ( + BondLinkUp = iota // link is up and running. + BondLinkFail // link has just gone down. + BondLinkDown // link has been down for too long time. + BondLinkBack // link is going back. +) + +func (s BondSlaveMiiStatus) String() string { + switch s { + case BondLinkUp: + return "UP" + case BondLinkFail: + return "GOING_DOWN" + case BondLinkDown: + return "DOWN" + case BondLinkBack: + return "GOING_BACK" + default: + return strconv.Itoa(int(s)) + } +} + +type BondSlave struct { + State BondSlaveState + MiiStatus BondSlaveMiiStatus + LinkFailureCount uint32 + PermHardwareAddr net.HardwareAddr + QueueId uint16 + AggregatorId uint16 + AdActorOperPortState uint8 + AdPartnerOperPortState uint16 +} + +func (b *BondSlave) SlaveType() string { + return "bond" +} + // Gretap devices must specify LocalIP and RemoteIP on create type Gretap struct { LinkAttrs diff --git a/link_linux.go b/link_linux.go index 64ce3e8..f9e50b8 100644 --- a/link_linux.go +++ b/link_linux.go @@ -1476,10 +1476,12 @@ func LinkDeserialize(hdr *unix.NlMsghdr, m []byte) (Link, error) { base.Promisc = 1 } var ( - link Link - stats32 []byte - stats64 []byte - linkType string + link Link + stats32 []byte + stats64 []byte + linkType string + linkSlave LinkSlave + slaveType string ) for _, attr := range attrs { switch attr.Attr.Type { @@ -1585,6 +1587,21 @@ func LinkDeserialize(hdr *unix.NlMsghdr, m []byte) (Link, error) { case "ipoib": parseIPoIBData(link, data) } + case nl.IFLA_INFO_SLAVE_KIND: + slaveType = string(info.Value[:len(info.Value)-1]) + switch slaveType { + case "bond": + linkSlave = &BondSlave{} + } + case nl.IFLA_INFO_SLAVE_DATA: + switch slaveType { + case "bond": + data, err := nl.ParseRouteAttr(info.Value) + if err != nil { + return nil, err + } + parseBondSlaveData(linkSlave, data) + } } } case unix.IFLA_ADDRESS: @@ -1667,6 +1684,7 @@ func LinkDeserialize(hdr *unix.NlMsghdr, m []byte) (Link, error) { link = &Device{} } *link.Attrs() = base + link.Attrs().Slave = linkSlave // If the tuntap attributes are not updated by netlink due to // an older driver, use sysfs @@ -2131,6 +2149,46 @@ func parseBondData(link Link, data []syscall.NetlinkRouteAttr) { } } +func addBondSlaveAttrs(bondSlave *BondSlave, linkInfo *nl.RtAttr) { + data := linkInfo.AddRtAttr(nl.IFLA_INFO_SLAVE_DATA, nil) + + data.AddRtAttr(nl.IFLA_BOND_SLAVE_STATE, nl.Uint8Attr(uint8(bondSlave.State))) + data.AddRtAttr(nl.IFLA_BOND_SLAVE_MII_STATUS, nl.Uint8Attr(uint8(bondSlave.MiiStatus))) + data.AddRtAttr(nl.IFLA_BOND_SLAVE_LINK_FAILURE_COUNT, nl.Uint32Attr(bondSlave.LinkFailureCount)) + data.AddRtAttr(nl.IFLA_BOND_SLAVE_QUEUE_ID, nl.Uint16Attr(bondSlave.QueueId)) + data.AddRtAttr(nl.IFLA_BOND_SLAVE_AD_AGGREGATOR_ID, nl.Uint16Attr(bondSlave.AggregatorId)) + data.AddRtAttr(nl.IFLA_BOND_SLAVE_AD_ACTOR_OPER_PORT_STATE, nl.Uint8Attr(bondSlave.AdActorOperPortState)) + data.AddRtAttr(nl.IFLA_BOND_SLAVE_AD_PARTNER_OPER_PORT_STATE, nl.Uint16Attr(bondSlave.AdPartnerOperPortState)) + + if mac := bondSlave.PermHardwareAddr; mac != nil { + data.AddRtAttr(nl.IFLA_BOND_SLAVE_PERM_HWADDR, []byte(mac)) + } +} + +func parseBondSlaveData(slave LinkSlave, data []syscall.NetlinkRouteAttr) { + bondSlave := slave.(*BondSlave) + for i := range data { + switch data[i].Attr.Type { + case nl.IFLA_BOND_SLAVE_STATE: + bondSlave.State = BondSlaveState(data[i].Value[0]) + case nl.IFLA_BOND_SLAVE_MII_STATUS: + bondSlave.MiiStatus = BondSlaveMiiStatus(data[i].Value[0]) + case nl.IFLA_BOND_SLAVE_LINK_FAILURE_COUNT: + bondSlave.LinkFailureCount = native.Uint32(data[i].Value[0:4]) + case nl.IFLA_BOND_SLAVE_PERM_HWADDR: + bondSlave.PermHardwareAddr = net.HardwareAddr(data[i].Value[0:6]) + case nl.IFLA_BOND_SLAVE_QUEUE_ID: + bondSlave.QueueId = native.Uint16(data[i].Value[0:2]) + case nl.IFLA_BOND_SLAVE_AD_AGGREGATOR_ID: + bondSlave.AggregatorId = native.Uint16(data[i].Value[0:2]) + case nl.IFLA_BOND_SLAVE_AD_ACTOR_OPER_PORT_STATE: + bondSlave.AdActorOperPortState = uint8(data[i].Value[0]) + case nl.IFLA_BOND_SLAVE_AD_PARTNER_OPER_PORT_STATE: + bondSlave.AdPartnerOperPortState = native.Uint16(data[i].Value[0:2]) + } + } +} + func parseIPVlanData(link Link, data []syscall.NetlinkRouteAttr) { ipv := link.(*IPVlan) for _, datum := range data { @@ -2725,6 +2783,30 @@ func LinkSetBondSlave(link Link, master *Bond) error { return nil } +// LinkSetBondSlaveQueueId modify bond slave queue-id. +func (h *Handle) LinkSetBondSlaveQueueId(link Link, queueId uint16) error { + 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) + + linkInfo := nl.NewRtAttr(unix.IFLA_LINKINFO, nil) + data := linkInfo.AddRtAttr(nl.IFLA_INFO_SLAVE_DATA, nil) + data.AddRtAttr(nl.IFLA_BOND_SLAVE_QUEUE_ID, nl.Uint16Attr(queueId)) + + req.AddData(linkInfo) + _, err := req.Execute(unix.NETLINK_ROUTE, 0) + return err +} + +// LinkSetBondSlaveQueueId modify bond slave queue-id. +func LinkSetBondSlaveQueueId(link Link, queueId uint16) error { + return pkgHandle.LinkSetBondSlaveQueueId(link, queueId) +} + // VethPeerIndex get veth peer index. func VethPeerIndex(link *Veth) (int, error) { fd, err := getSocketUDP() diff --git a/link_test.go b/link_test.go index 740be8a..b925b26 100644 --- a/link_test.go +++ b/link_test.go @@ -2035,6 +2035,81 @@ func TestVethPeerIndex(t *testing.T) { } } +func TestLinkSlaveBond(t *testing.T) { + minKernelRequired(t, 3, 13) + + tearDown := setUpNetlinkTest(t) + defer tearDown() + + const ( + bondName = "foo" + slaveName = "fooFoo" + ) + + bond := NewLinkBond(LinkAttrs{Name: bondName}) + bond.Mode = BOND_MODE_BALANCE_RR + if err := LinkAdd(bond); err != nil { + t.Fatal(err) + } + defer LinkDel(bond) + + slaveDummy := &Dummy{LinkAttrs{Name: slaveName}} + if err := LinkAdd(slaveDummy); err != nil { + t.Fatal(err) + } + defer LinkDel(slaveDummy) + + if err := LinkSetBondSlave(slaveDummy, bond); err != nil { + t.Fatal(err) + } + + slaveLink, err := LinkByName(slaveName) + if err != nil { + t.Fatal(err) + } + + slave := slaveLink.Attrs().Slave + if slave == nil { + t.Errorf("for %s expected slave is not nil.", slaveName) + } + + if slaveType := slave.SlaveType(); slaveType != "bond" { + t.Errorf("for %s expected slave type is 'bond', but '%s'", slaveName, slaveType) + } +} + +func TestLinkSetBondSlaveQueueId(t *testing.T) { + minKernelRequired(t, 3, 13) + + tearDown := setUpNetlinkTest(t) + defer tearDown() + + const ( + bondName = "foo" + slave1Name = "fooFoo" + ) + + bond := NewLinkBond(LinkAttrs{Name: bondName}) + if err := LinkAdd(bond); err != nil { + t.Fatal(err) + } + defer LinkDel(bond) + + slave := &Dummy{LinkAttrs{Name: slave1Name}} + if err := LinkAdd(slave); err != nil { + t.Fatal(err) + } + defer LinkDel(slave) + + if err := LinkSetBondSlave(slave, bond); err != nil { + t.Fatal(err) + } + + if err := pkgHandle.LinkSetBondSlaveQueueId(slave, 1); err != nil { + t.Fatal(err) + } +} + func TestLinkSetBondSlave(t *testing.T) { minKernelRequired(t, 3, 13) diff --git a/nl/link_linux.go b/nl/link_linux.go index 56e1cee..afb16a9 100644 --- a/nl/link_linux.go +++ b/nl/link_linux.go @@ -13,7 +13,9 @@ const ( IFLA_INFO_KIND IFLA_INFO_DATA IFLA_INFO_XSTATS - IFLA_INFO_MAX = IFLA_INFO_XSTATS + IFLA_INFO_SLAVE_KIND + IFLA_INFO_SLAVE_DATA + IFLA_INFO_MAX = IFLA_INFO_SLAVE_DATA ) const ( @@ -165,6 +167,8 @@ const ( IFLA_BOND_SLAVE_PERM_HWADDR IFLA_BOND_SLAVE_QUEUE_ID IFLA_BOND_SLAVE_AD_AGGREGATOR_ID + IFLA_BOND_SLAVE_AD_ACTOR_OPER_PORT_STATE + IFLA_BOND_SLAVE_AD_PARTNER_OPER_PORT_STATE ) const (