Merge pull request #120 from rosenhouse/get-peer

Adds GetVethPeerIfindex library function
This commit is contained in:
Gabe Rosenhouse 2018-02-21 08:13:15 -08:00 committed by GitHub
commit c82fb1bbe2
No known key found for this signature in database
GPG Key ID: 4AEE18F83AFDEB23
4 changed files with 325 additions and 0 deletions

4
Godeps/Godeps.json generated
View File

@ -226,6 +226,10 @@
"Comment": "v1.0-71-g2152b45",
"Rev": "2152b45fa28a361beba9aab0885972323a444e28"
},
{
"ImportPath": "github.com/safchain/ethtool",
"Rev": "7ff1ba29eca231991280817541cb3903f6be15d1"
},
{
"ImportPath": "github.com/vishvananda/netlink",
"Rev": "6e453822d85ef5721799774b654d4d02fed62afb"

View File

@ -23,6 +23,7 @@ import (
"github.com/containernetworking/plugins/pkg/ns"
"github.com/containernetworking/plugins/pkg/utils/hwaddr"
"github.com/safchain/ethtool"
"github.com/vishvananda/netlink"
)
@ -226,3 +227,43 @@ func SetHWAddrByIP(ifName string, ip4 net.IP, ip6 net.IP) error {
return nil
}
// GetVethPeerIfindex returns the veth link object, the peer ifindex of the
// veth, or an error. This peer ifindex will only be valid in the peer's
// network namespace.
func GetVethPeerIfindex(ifName string) (netlink.Link, int, error) {
link, err := netlink.LinkByName(ifName)
if err != nil {
return nil, -1, fmt.Errorf("could not look up %q: %v", ifName, err)
}
if _, ok := link.(*netlink.Veth); !ok {
return nil, -1, fmt.Errorf("interface %q was not a veth interface", ifName)
}
// veth supports IFLA_LINK (what vishvananda/netlink calls ParentIndex)
// on 4.1 and higher kernels
peerIndex := link.Attrs().ParentIndex
if peerIndex <= 0 {
// Fall back to ethtool for 4.0 and earlier kernels
e, err := ethtool.NewEthtool()
if err != nil {
return nil, -1, fmt.Errorf("failed to initialize ethtool: %v", err)
}
defer e.Close()
stats, err := e.Stats(link.Attrs().Name)
if err != nil {
return nil, -1, fmt.Errorf("failed to request ethtool stats: %v", err)
}
n, ok := stats["peer_ifindex"]
if !ok {
return nil, -1, fmt.Errorf("failed to find 'peer_ifindex' in ethtool stats")
}
if n > 32767 || n == 0 {
return nil, -1, fmt.Errorf("invalid 'peer_ifindex' %d", n)
}
peerIndex = int(n)
}
return link, peerIndex, nil
}

View File

@ -91,6 +91,46 @@ var _ = Describe("Link", func() {
rand.Reader = originalRandReader
})
Describe("GetVethPeerIfindex", func() {
It("returns the link and peer index of the named interface", func() {
By("looking up the container veth index using the host veth name")
_ = hostNetNS.Do(func(ns.NetNS) error {
defer GinkgoRecover()
gotHostLink, gotContainerIndex, err := ip.GetVethPeerIfindex(hostVethName)
Expect(err).NotTo(HaveOccurred())
By("checking we got back the host link")
attrs := gotHostLink.Attrs()
Expect(attrs.Index).To(Equal(hostVeth.Index))
Expect(attrs.Name).To(Equal(hostVeth.Name))
By("checking we got back the container veth index")
Expect(gotContainerIndex).To(Equal(containerVeth.Index))
return nil
})
By("looking up the host veth index using the container veth name")
_ = containerNetNS.Do(func(ns.NetNS) error {
defer GinkgoRecover()
gotContainerLink, gotHostIndex, err := ip.GetVethPeerIfindex(containerVethName)
Expect(err).NotTo(HaveOccurred())
By("checking we got back the container link")
attrs := gotContainerLink.Attrs()
Expect(attrs.Index).To(Equal(containerVeth.Index))
Expect(attrs.Name).To(Equal(containerVeth.Name))
By("checking we got back the host veth index")
Expect(gotHostIndex).To(Equal(hostVeth.Index))
return nil
})
})
})
It("SetupVeth must put the veth endpoints into the separate namespaces", func() {
_ = containerNetNS.Do(func(ns.NetNS) error {
defer GinkgoRecover()

240
vendor/github.com/safchain/ethtool/ethtool.go generated vendored Normal file
View File

@ -0,0 +1,240 @@
/*
*
* Licensed to the Apache Software Foundation (ASF) under one
* or more contributor license agreements. See the NOTICE file
* distributed with this work for additional information
* regarding copyright ownership. The ASF licenses this file
* to you under the Apache License, Version 2.0 (the
* "License"); you may not use this file except in compliance
* with the License. You may obtain a copy of the License at
*
* http://www.apache.org/licenses/LICENSE-2.0
*
* Unless required by applicable law or agreed to in writing,
* software distributed under the License is distributed on an
* "AS IS" BASIS, WITHOUT WARRANTIES OR CONDITIONS OF ANY
* KIND, either express or implied. See the License for the
* specific language governing permissions and limitations
* under the License.
*
*/
// Package ethtool aims to provide a library giving a simple access to the
// Linux SIOCETHTOOL ioctl operations. It can be used to retrieve informations
// from a network device like statistics, driver related informations or
// even the peer of a VETH interface.
package ethtool
import (
"bytes"
"fmt"
"syscall"
"unsafe"
)
// Maximum size of an interface name
const (
IFNAMSIZ = 16
)
// ioctl ethtool request
const (
SIOCETHTOOL = 0x8946
)
// ethtool stats related constants.
const (
ETH_GSTRING_LEN = 32
ETH_SS_STATS = 1
ETHTOOL_GDRVINFO = 0x00000003
ETHTOOL_GSTRINGS = 0x0000001b
ETHTOOL_GSTATS = 0x0000001d
// other CMDs from ethtool-copy.h of ethtool-3.5 package
ETHTOOL_GSET = 0x00000001 /* Get settings. */
ETHTOOL_SSET = 0x00000002 /* Set settings. */
ETHTOOL_GMSGLVL = 0x00000007 /* Get driver message level */
ETHTOOL_SMSGLVL = 0x00000008 /* Set driver msg level. */
)
// MAX_GSTRINGS maximum number of stats entries that ethtool can
// retrieve currently.
const (
MAX_GSTRINGS = 1000
)
type ifreq struct {
ifr_name [IFNAMSIZ]byte
ifr_data uintptr
}
type ethtoolDrvInfo struct {
cmd uint32
driver [32]byte
version [32]byte
fw_version [32]byte
bus_info [32]byte
erom_version [32]byte
reserved2 [12]byte
n_priv_flags uint32
n_stats uint32
testinfo_len uint32
eedump_len uint32
regdump_len uint32
}
type ethtoolGStrings struct {
cmd uint32
string_set uint32
len uint32
data [MAX_GSTRINGS * ETH_GSTRING_LEN]byte
}
type ethtoolStats struct {
cmd uint32
n_stats uint32
data [MAX_GSTRINGS]uint64
}
type Ethtool struct {
fd int
}
// DriverName returns the driver name of the given interface.
func (e *Ethtool) DriverName(intf string) (string, error) {
info, err := e.getDriverInfo(intf)
if err != nil {
return "", err
}
return string(bytes.Trim(info.driver[:], "\x00")), nil
}
// BusInfo returns the bus info of the given interface.
func (e *Ethtool) BusInfo(intf string) (string, error) {
info, err := e.getDriverInfo(intf)
if err != nil {
return "", err
}
return string(bytes.Trim(info.bus_info[:], "\x00")), nil
}
func (e *Ethtool) getDriverInfo(intf string) (ethtoolDrvInfo, error) {
drvinfo := ethtoolDrvInfo{
cmd: ETHTOOL_GDRVINFO,
}
var name [IFNAMSIZ]byte
copy(name[:], []byte(intf))
ifr := ifreq{
ifr_name: name,
ifr_data: uintptr(unsafe.Pointer(&drvinfo)),
}
_, _, ep := syscall.Syscall(syscall.SYS_IOCTL, uintptr(e.fd), SIOCETHTOOL, uintptr(unsafe.Pointer(&ifr)))
if ep != 0 {
return ethtoolDrvInfo{}, syscall.Errno(ep)
}
return drvinfo, nil
}
// Stats retrieves stats of the given interface name.
func (e *Ethtool) Stats(intf string) (map[string]uint64, error) {
drvinfo := ethtoolDrvInfo{
cmd: ETHTOOL_GDRVINFO,
}
var name [IFNAMSIZ]byte
copy(name[:], []byte(intf))
ifr := ifreq{
ifr_name: name,
ifr_data: uintptr(unsafe.Pointer(&drvinfo)),
}
_, _, ep := syscall.Syscall(syscall.SYS_IOCTL, uintptr(e.fd), SIOCETHTOOL, uintptr(unsafe.Pointer(&ifr)))
if ep != 0 {
return nil, syscall.Errno(ep)
}
if drvinfo.n_stats*ETH_GSTRING_LEN > MAX_GSTRINGS*ETH_GSTRING_LEN {
return nil, fmt.Errorf("ethtool currently doesn't support more than %d entries, received %d", MAX_GSTRINGS, drvinfo.n_stats)
}
gstrings := ethtoolGStrings{
cmd: ETHTOOL_GSTRINGS,
string_set: ETH_SS_STATS,
len: drvinfo.n_stats,
data: [MAX_GSTRINGS * ETH_GSTRING_LEN]byte{},
}
ifr.ifr_data = uintptr(unsafe.Pointer(&gstrings))
_, _, ep = syscall.Syscall(syscall.SYS_IOCTL, uintptr(e.fd), SIOCETHTOOL, uintptr(unsafe.Pointer(&ifr)))
if ep != 0 {
return nil, syscall.Errno(ep)
}
stats := ethtoolStats{
cmd: ETHTOOL_GSTATS,
n_stats: drvinfo.n_stats,
data: [MAX_GSTRINGS]uint64{},
}
ifr.ifr_data = uintptr(unsafe.Pointer(&stats))
_, _, ep = syscall.Syscall(syscall.SYS_IOCTL, uintptr(e.fd), SIOCETHTOOL, uintptr(unsafe.Pointer(&ifr)))
if ep != 0 {
return nil, syscall.Errno(ep)
}
var result = make(map[string]uint64)
for i := 0; i != int(drvinfo.n_stats); i++ {
b := gstrings.data[i*ETH_GSTRING_LEN : i*ETH_GSTRING_LEN+ETH_GSTRING_LEN]
key := string(bytes.Trim(b, "\x00"))
result[key] = stats.data[i]
}
return result, nil
}
func (e *Ethtool) Close() {
syscall.Close(e.fd)
}
func NewEthtool() (*Ethtool, error) {
fd, _, err := syscall.RawSyscall(syscall.SYS_SOCKET, syscall.AF_INET, syscall.SOCK_DGRAM, syscall.IPPROTO_IP)
if err != 0 {
return nil, syscall.Errno(err)
}
return &Ethtool{
fd: int(fd),
}, nil
}
func BusInfo(intf string) (string, error) {
e, err := NewEthtool()
if err != nil {
return "", err
}
defer e.Close()
return e.BusInfo(intf)
}
func DriverName(intf string) (string, error) {
e, err := NewEthtool()
if err != nil {
return "", err
}
defer e.Close()
return e.DriverName(intf)
}
func Stats(intf string) (map[string]uint64, error) {
e, err := NewEthtool()
if err != nil {
return nil, err
}
defer e.Close()
return e.Stats(intf)
}