mirror of https://github.com/vishvananda/netlink
Two new functions: LinkSetBondSlave and VethPeerIndex
This commit is contained in:
parent
f67b75edbf
commit
54ad9e3a4c
|
@ -0,0 +1,98 @@
|
|||
package netlink
|
||||
|
||||
import (
|
||||
"syscall"
|
||||
"unsafe"
|
||||
|
||||
"golang.org/x/sys/unix"
|
||||
)
|
||||
|
||||
// ioctl for statistics.
|
||||
const (
|
||||
// ETHTOOL_GSSET_INFO gets string set info
|
||||
ETHTOOL_GSSET_INFO = 0x00000037
|
||||
// SIOCETHTOOL is Ethtool interface
|
||||
SIOCETHTOOL = 0x8946
|
||||
// ETHTOOL_GSTRINGS gets specified string set
|
||||
ETHTOOL_GSTRINGS = 0x0000001b
|
||||
// ETHTOOL_GSTATS gets NIC-specific statistics
|
||||
ETHTOOL_GSTATS = 0x0000001d
|
||||
)
|
||||
|
||||
// string set id.
|
||||
const (
|
||||
// ETH_SS_TEST is self-test result names, for use with %ETHTOOL_TEST
|
||||
ETH_SS_TEST = iota
|
||||
// ETH_SS_STATS statistic names, for use with %ETHTOOL_GSTATS
|
||||
ETH_SS_STATS
|
||||
// ETH_SS_PRIV_FLAGS are driver private flag names
|
||||
ETH_SS_PRIV_FLAGS
|
||||
// _ETH_SS_NTUPLE_FILTERS is deprecated
|
||||
_ETH_SS_NTUPLE_FILTERS
|
||||
// ETH_SS_FEATURES are device feature names
|
||||
ETH_SS_FEATURES
|
||||
// ETH_SS_RSS_HASH_FUNCS is RSS hush function names
|
||||
ETH_SS_RSS_HASH_FUNCS
|
||||
)
|
||||
|
||||
// IfreqSlave is a struct for ioctl bond manipulation syscalls.
|
||||
// It is used to assign slave to bond interface with Name.
|
||||
type IfreqSlave struct {
|
||||
Name [unix.IFNAMSIZ]byte
|
||||
Slave [unix.IFNAMSIZ]byte
|
||||
}
|
||||
|
||||
// Ifreq is a struct for ioctl ethernet manipulation syscalls.
|
||||
type Ifreq struct {
|
||||
Name [unix.IFNAMSIZ]byte
|
||||
Data uintptr
|
||||
}
|
||||
|
||||
// ethtoolSset is a string set information
|
||||
type ethtoolSset struct {
|
||||
cmd uint32
|
||||
reserved uint32
|
||||
mask uint64
|
||||
data [1]uint32
|
||||
}
|
||||
|
||||
// ethtoolGstrings is string set for data tagging
|
||||
type ethtoolGstrings struct {
|
||||
cmd uint32
|
||||
stringSet uint32
|
||||
length uint32
|
||||
data [32]byte
|
||||
}
|
||||
|
||||
type ethtoolStats struct {
|
||||
cmd uint32
|
||||
nStats uint32
|
||||
data [1]uint64
|
||||
}
|
||||
|
||||
// newIocltSlaveReq returns filled IfreqSlave with proper interface names
|
||||
// It is used by ioctl to assign slave to bond master
|
||||
func newIocltSlaveReq(slave, master string) *IfreqSlave {
|
||||
ifreq := &IfreqSlave{}
|
||||
copy(ifreq.Name[:unix.IFNAMSIZ-1], master)
|
||||
copy(ifreq.Slave[:unix.IFNAMSIZ-1], slave)
|
||||
return ifreq
|
||||
}
|
||||
|
||||
// newIocltStringSetReq creates request to get interface string set
|
||||
func newIocltStringSetReq(linkName string) (*Ifreq, *ethtoolSset) {
|
||||
e := ðtoolSset{
|
||||
cmd: ETHTOOL_GSSET_INFO,
|
||||
mask: 1 << ETH_SS_STATS,
|
||||
}
|
||||
|
||||
ifreq := &Ifreq{Data: uintptr(unsafe.Pointer(e))}
|
||||
copy(ifreq.Name[:unix.IFNAMSIZ-1], linkName)
|
||||
return ifreq, e
|
||||
}
|
||||
|
||||
// getSocketUDP returns file descriptor to new UDP socket
|
||||
// It is used for communication with ioctl interface.
|
||||
func getSocketUDP() (int, error) {
|
||||
return syscall.Socket(unix.AF_INET, unix.SOCK_DGRAM, 0)
|
||||
}
|
|
@ -2256,3 +2256,57 @@ func parseGTPData(link Link, data []syscall.NetlinkRouteAttr) {
|
|||
}
|
||||
}
|
||||
}
|
||||
|
||||
// LinkSetBondSlave add slave to bond link via ioctl interface.
|
||||
func LinkSetBondSlave(link Link, master *Bond) error {
|
||||
fd, err := getSocketUDP()
|
||||
if err != nil {
|
||||
return err
|
||||
}
|
||||
defer syscall.Close(fd)
|
||||
|
||||
ifreq := newIocltSlaveReq(link.Attrs().Name, master.Attrs().Name)
|
||||
|
||||
_, _, errno := syscall.Syscall(syscall.SYS_IOCTL, uintptr(fd), unix.SIOCBONDENSLAVE, uintptr(unsafe.Pointer(ifreq)))
|
||||
if errno != 0 {
|
||||
return fmt.Errorf("Failed to enslave %q to %q, errno=%v", link.Attrs().Name, master.Attrs().Name, errno)
|
||||
}
|
||||
return nil
|
||||
}
|
||||
|
||||
// VethPeerIndex get veth peer index.
|
||||
func VethPeerIndex(link *Veth) (int, error) {
|
||||
fd, err := getSocketUDP()
|
||||
if err != nil {
|
||||
return -1, err
|
||||
}
|
||||
defer syscall.Close(fd)
|
||||
|
||||
ifreq, sSet := newIocltStringSetReq(link.Name)
|
||||
_, _, errno := syscall.Syscall(syscall.SYS_IOCTL, uintptr(fd), SIOCETHTOOL, uintptr(unsafe.Pointer(ifreq)))
|
||||
if errno != 0 {
|
||||
return -1, fmt.Errorf("SIOCETHTOOL request for %q failed, errno=%v", link.Attrs().Name, errno)
|
||||
}
|
||||
|
||||
gstrings := ðtoolGstrings{
|
||||
cmd: ETHTOOL_GSTRINGS,
|
||||
stringSet: ETH_SS_STATS,
|
||||
length: sSet.data[0],
|
||||
}
|
||||
ifreq.Data = uintptr(unsafe.Pointer(gstrings))
|
||||
_, _, errno = syscall.Syscall(syscall.SYS_IOCTL, uintptr(fd), SIOCETHTOOL, uintptr(unsafe.Pointer(ifreq)))
|
||||
if errno != 0 {
|
||||
return -1, fmt.Errorf("SIOCETHTOOL request for %q failed, errno=%v", link.Attrs().Name, errno)
|
||||
}
|
||||
|
||||
stats := ðtoolStats{
|
||||
cmd: ETHTOOL_GSTATS,
|
||||
nStats: gstrings.length,
|
||||
}
|
||||
ifreq.Data = uintptr(unsafe.Pointer(stats))
|
||||
_, _, errno = syscall.Syscall(syscall.SYS_IOCTL, uintptr(fd), SIOCETHTOOL, uintptr(unsafe.Pointer(ifreq)))
|
||||
if errno != 0 {
|
||||
return -1, fmt.Errorf("SIOCETHTOOL request for %q failed, errno=%v", link.Attrs().Name, errno)
|
||||
}
|
||||
return int(stats.data[0]), nil
|
||||
}
|
||||
|
|
127
link_test.go
127
link_test.go
|
@ -1544,3 +1544,130 @@ func TestLinkAddDelTuntapMq(t *testing.T) {
|
|||
Queues: 4,
|
||||
Flags: TUNTAP_MULTI_QUEUE_DEFAULTS | TUNTAP_VNET_HDR})
|
||||
}
|
||||
|
||||
func TestVethPeerIndex(t *testing.T) {
|
||||
tearDown := setUpNetlinkTest(t)
|
||||
defer tearDown()
|
||||
|
||||
const (
|
||||
vethPeer1 = "vethOne"
|
||||
vethPeer2 = "vethTwo"
|
||||
)
|
||||
|
||||
link := &Veth{
|
||||
LinkAttrs: LinkAttrs{
|
||||
Name: vethPeer1,
|
||||
MTU: 1500,
|
||||
Flags: net.FlagUp,
|
||||
},
|
||||
PeerName: vethPeer2,
|
||||
}
|
||||
|
||||
if err := LinkAdd(link); err != nil {
|
||||
t.Fatal(err)
|
||||
}
|
||||
|
||||
linkOne, err := LinkByName("vethOne")
|
||||
if err != nil {
|
||||
t.Fatal(err)
|
||||
}
|
||||
|
||||
linkTwo, err := LinkByName("vethTwo")
|
||||
if err != nil {
|
||||
t.Fatal(err)
|
||||
}
|
||||
|
||||
peerIndexOne, err := VethPeerIndex(&Veth{LinkAttrs: *linkOne.Attrs()})
|
||||
if err != nil {
|
||||
t.Fatal(err)
|
||||
}
|
||||
|
||||
peerIndexTwo, err := VethPeerIndex(&Veth{LinkAttrs: *linkTwo.Attrs()})
|
||||
if err != nil {
|
||||
t.Fatal(err)
|
||||
}
|
||||
|
||||
if peerIndexOne != linkTwo.Attrs().Index {
|
||||
t.Errorf("VethPeerIndex(%s) mismatch %d != %d", linkOne.Attrs().Name, peerIndexOne, linkTwo.Attrs().Index)
|
||||
}
|
||||
|
||||
if peerIndexTwo != linkOne.Attrs().Index {
|
||||
t.Errorf("VethPeerIndex(%s) mismatch %d != %d", linkTwo.Attrs().Name, peerIndexTwo, linkOne.Attrs().Index)
|
||||
}
|
||||
}
|
||||
|
||||
func TestLinkSetBondSlave(t *testing.T) {
|
||||
minKernelRequired(t, 3, 13)
|
||||
|
||||
tearDown := setUpNetlinkTest(t)
|
||||
defer tearDown()
|
||||
|
||||
const (
|
||||
bondName = "foo"
|
||||
slaveOneName = "fooFoo"
|
||||
slaveTwoName = "fooBar"
|
||||
)
|
||||
|
||||
bond := NewLinkBond(LinkAttrs{Name: bondName})
|
||||
bond.Mode = StringToBondModeMap["802.3ad"]
|
||||
bond.AdSelect = BondAdSelect(BOND_AD_SELECT_BANDWIDTH)
|
||||
bond.AdActorSysPrio = 1
|
||||
bond.AdUserPortKey = 1
|
||||
bond.AdActorSystem, _ = net.ParseMAC("06:aa:bb:cc:dd:ee")
|
||||
|
||||
if err := LinkAdd(bond); err != nil {
|
||||
t.Fatal(err)
|
||||
}
|
||||
|
||||
bondLink, err := LinkByName(bondName)
|
||||
if err != nil {
|
||||
t.Fatal(err)
|
||||
}
|
||||
defer LinkDel(bondLink)
|
||||
|
||||
if err := LinkAdd(&Dummy{LinkAttrs{Name: slaveOneName}}); err != nil {
|
||||
t.Fatal(err)
|
||||
}
|
||||
|
||||
slaveOneLink, err := LinkByName(slaveOneName)
|
||||
if err != nil {
|
||||
t.Fatal(err)
|
||||
}
|
||||
defer LinkDel(slaveOneLink)
|
||||
|
||||
if err := LinkAdd(&Dummy{LinkAttrs{Name: slaveTwoName}}); err != nil {
|
||||
t.Fatal(err)
|
||||
}
|
||||
slaveTwoLink, err := LinkByName(slaveTwoName)
|
||||
if err != nil {
|
||||
t.Fatal(err)
|
||||
}
|
||||
defer LinkDel(slaveTwoLink)
|
||||
|
||||
if err := LinkSetBondSlave(slaveOneLink, &Bond{LinkAttrs: *bondLink.Attrs()}); err != nil {
|
||||
t.Fatal(err)
|
||||
}
|
||||
|
||||
if err := LinkSetBondSlave(slaveTwoLink, &Bond{LinkAttrs: *bondLink.Attrs()}); err != nil {
|
||||
t.Fatal(err)
|
||||
}
|
||||
|
||||
// Update info about interfaces
|
||||
slaveOneLink, err = LinkByName(slaveOneName)
|
||||
if err != nil {
|
||||
t.Fatal(err)
|
||||
}
|
||||
|
||||
slaveTwoLink, err = LinkByName(slaveTwoName)
|
||||
if err != nil {
|
||||
t.Fatal(err)
|
||||
}
|
||||
|
||||
if slaveOneLink.Attrs().MasterIndex != bondLink.Attrs().Index {
|
||||
t.Errorf("For %s expected %s to be master", slaveOneLink.Attrs().Name, bondLink.Attrs().Name)
|
||||
}
|
||||
|
||||
if slaveTwoLink.Attrs().MasterIndex != bondLink.Attrs().Index {
|
||||
t.Errorf("For %s expected %s to be master", slaveTwoLink.Attrs().Name, bondLink.Attrs().Name)
|
||||
}
|
||||
}
|
||||
|
|
Loading…
Reference in New Issue