Compare commits
12 Commits
Author | SHA1 | Date | |
---|---|---|---|
fe60fcddb8 | |||
e308211d34 | |||
29a431f1fc | |||
7d75ab66a4 | |||
d5c8b4b206 | |||
5a4085f1fa | |||
37d2ee1d5d | |||
7f9b1844b8 | |||
2753b9af8f | |||
d35c96dda6 | |||
344d343431 | |||
79b1c402c4 |
@ -3,14 +3,15 @@ sudo: required
|
||||
dist: trusty
|
||||
|
||||
go:
|
||||
- 1.10.x
|
||||
- 1.11.x
|
||||
- 1.12.x
|
||||
|
||||
env:
|
||||
global:
|
||||
- PATH=$GOROOT/bin:$GOPATH/bin:$PATH
|
||||
- CGO_ENABLED=0
|
||||
matrix:
|
||||
- TARGET=386
|
||||
- TARGET=amd64
|
||||
- TARGET=arm
|
||||
- TARGET=arm64
|
||||
@ -22,10 +23,10 @@ matrix:
|
||||
include:
|
||||
- os: windows
|
||||
env: TARGET=amd64
|
||||
go: 1.10.x
|
||||
go: 1.11.x
|
||||
- os: windows
|
||||
env: TARGET=amd64
|
||||
go: 1.11.x
|
||||
go: 1.12.x
|
||||
|
||||
install:
|
||||
- go get github.com/onsi/ginkgo/ginkgo
|
||||
|
2
Godeps/Godeps.json
generated
2
Godeps/Godeps.json
generated
@ -314,7 +314,7 @@
|
||||
},
|
||||
{
|
||||
"ImportPath": "github.com/safchain/ethtool",
|
||||
"Rev": "7ff1ba29eca231991280817541cb3903f6be15d1"
|
||||
"Rev": "42ed695e3de80b9d695f280295fd7994639f209d"
|
||||
},
|
||||
{
|
||||
"ImportPath": "github.com/sirupsen/logrus",
|
||||
|
@ -517,7 +517,7 @@ func cmdAdd(args *skel.CmdArgs) error {
|
||||
chain := utils.FormatChainName(n.Name, args.ContainerID)
|
||||
comment := utils.FormatComment(n.Name, args.ContainerID)
|
||||
for _, ipc := range result.IPs {
|
||||
if err = ip.SetupIPMasq(ip.Network(&ipc.Address), chain, comment); err != nil {
|
||||
if err = ip.SetupIPMasq(&ipc.Address, chain, comment); err != nil {
|
||||
return err
|
||||
}
|
||||
}
|
||||
|
@ -17,12 +17,14 @@ package main
|
||||
import (
|
||||
"encoding/json"
|
||||
"fmt"
|
||||
"github.com/vishvananda/netlink/nl"
|
||||
"io/ioutil"
|
||||
"net"
|
||||
"os"
|
||||
"strings"
|
||||
|
||||
"github.com/coreos/go-iptables/iptables"
|
||||
"github.com/vishvananda/netlink/nl"
|
||||
|
||||
"github.com/containernetworking/cni/pkg/skel"
|
||||
"github.com/containernetworking/cni/pkg/types"
|
||||
"github.com/containernetworking/cni/pkg/types/020"
|
||||
@ -70,6 +72,7 @@ type testCase struct {
|
||||
isLayer2 bool
|
||||
expGWCIDRs []string // Expected gateway addresses in CIDR form
|
||||
vlan int
|
||||
ipMasq bool
|
||||
}
|
||||
|
||||
// Range definition for each entry in the ranges list
|
||||
@ -105,8 +108,7 @@ const (
|
||||
"vlan": %d`
|
||||
|
||||
netDefault = `,
|
||||
"isDefaultGateway": true,
|
||||
"ipMasq": false`
|
||||
"isDefaultGateway": true`
|
||||
|
||||
ipamStartStr = `,
|
||||
"ipam": {
|
||||
@ -115,6 +117,9 @@ const (
|
||||
ipamDataDirStr = `,
|
||||
"dataDir": "%s"`
|
||||
|
||||
ipMasqConfStr = `,
|
||||
"ipMasq": %t`
|
||||
|
||||
// Single subnet configuration (legacy)
|
||||
subnetConfStr = `,
|
||||
"subnet": "%s"`
|
||||
@ -147,6 +152,9 @@ func (tc testCase) netConfJSON(dataDir string) string {
|
||||
if tc.vlan != 0 {
|
||||
conf += fmt.Sprintf(vlan, tc.vlan)
|
||||
}
|
||||
if tc.ipMasq {
|
||||
conf += tc.ipMasqConfig()
|
||||
}
|
||||
|
||||
if !tc.isLayer2 {
|
||||
conf += netDefault
|
||||
@ -178,6 +186,11 @@ func (tc testCase) subnetConfig() string {
|
||||
return conf
|
||||
}
|
||||
|
||||
func (tc testCase) ipMasqConfig() string {
|
||||
conf := fmt.Sprintf(ipMasqConfStr, tc.ipMasq)
|
||||
return conf
|
||||
}
|
||||
|
||||
func (tc testCase) rangesConfig() string {
|
||||
conf := rangesStartStr
|
||||
for i, tcRange := range tc.ranges {
|
||||
@ -1595,4 +1608,40 @@ var _ = Describe("bridge Operations", func() {
|
||||
cmdAddDelCheckTest(originalNS, tc, dataDir)
|
||||
}
|
||||
})
|
||||
|
||||
It("configures a bridge and ipMasq rules for 0.4.0 config", func() {
|
||||
err := originalNS.Do(func(ns.NetNS) error {
|
||||
defer GinkgoRecover()
|
||||
tc := testCase{
|
||||
ranges: []rangeInfo{{
|
||||
subnet: "10.1.2.0/24",
|
||||
}},
|
||||
ipMasq: true,
|
||||
cniVersion: "0.4.0",
|
||||
}
|
||||
|
||||
args := tc.createCmdArgs(originalNS, dataDir)
|
||||
r, _, err := testutils.CmdAddWithArgs(args, func() error {
|
||||
return cmdAdd(args)
|
||||
})
|
||||
Expect(err).NotTo(HaveOccurred())
|
||||
result, err := current.GetResult(r)
|
||||
Expect(err).NotTo(HaveOccurred())
|
||||
Expect(result.IPs).Should(HaveLen(1))
|
||||
|
||||
ipt, err := iptables.NewWithProtocol(iptables.ProtocolIPv4)
|
||||
Expect(err).NotTo(HaveOccurred())
|
||||
|
||||
rules, err := ipt.List("nat", "POSTROUTING")
|
||||
Expect(err).NotTo(HaveOccurred())
|
||||
Expect(rules).Should(ContainElement(ContainSubstring(result.IPs[0].Address.IP.String())))
|
||||
|
||||
err = testutils.CmdDelWithArgs(args, func() error {
|
||||
return cmdDel(args)
|
||||
})
|
||||
Expect(err).NotTo(HaveOccurred())
|
||||
return nil
|
||||
})
|
||||
Expect(err).NotTo(HaveOccurred())
|
||||
})
|
||||
})
|
||||
|
@ -1,17 +1,25 @@
|
||||
# host-device
|
||||
|
||||
Move an already-existing device into a container.
|
||||
|
||||
This simple plugin will move the requested device from the host's network namespace
|
||||
to the container's. Nothing else will be done - no IPAM, no addresses.
|
||||
## Overview
|
||||
|
||||
The device can be specified with any one of three properties:
|
||||
This simple plugin will move the requested device from the host's network namespace
|
||||
to the container's. IPAM configuration can be used for this plugin.
|
||||
|
||||
## Network configuration reference
|
||||
|
||||
The device can be specified with any one of four properties:
|
||||
* `device`: The device name, e.g. `eth0`, `can0`
|
||||
* `hwaddr`: A MAC address
|
||||
* `kernelpath`: The kernel device kobj, e.g. `/sys/devices/pci0000:00/0000:00:1f.6`
|
||||
* `pciBusID`: A PCI address of network device, e.g `0000:00:1f.6`
|
||||
|
||||
For this plugin, `CNI_IFNAME` will be ignored. Upon DEL, the device will be moved back.
|
||||
|
||||
A sample configuration might look like:
|
||||
## Example configuration
|
||||
|
||||
A sample configuration with `device` property looks like:
|
||||
|
||||
```json
|
||||
{
|
||||
@ -20,3 +28,13 @@ A sample configuration might look like:
|
||||
"device": "enp0s1"
|
||||
}
|
||||
```
|
||||
|
||||
A sample configuration with `pciBusID` property looks like:
|
||||
|
||||
```json
|
||||
{
|
||||
"cniVersion": "0.3.1",
|
||||
"type": "host-device",
|
||||
"pciBusID": "0000:3d:00.1"
|
||||
}
|
||||
```
|
||||
|
@ -21,6 +21,7 @@ import (
|
||||
"fmt"
|
||||
"io/ioutil"
|
||||
"net"
|
||||
"os"
|
||||
"path/filepath"
|
||||
"runtime"
|
||||
"strings"
|
||||
@ -38,12 +39,17 @@ import (
|
||||
bv "github.com/containernetworking/plugins/pkg/utils/buildversion"
|
||||
)
|
||||
|
||||
const (
|
||||
sysBusPCI = "/sys/bus/pci/devices"
|
||||
)
|
||||
|
||||
//NetConf for host-device config, look the README to learn how to use those parameters
|
||||
type NetConf struct {
|
||||
types.NetConf
|
||||
Device string `json:"device"` // Device-Name, something like eth0 or can0 etc.
|
||||
HWAddr string `json:"hwaddr"` // MAC Address of target network interface
|
||||
KernelPath string `json:"kernelpath"` // Kernelpath of the device
|
||||
PCIAddr string `json:"pciBusID"` // PCI Address of target network device
|
||||
}
|
||||
|
||||
func init() {
|
||||
@ -58,8 +64,8 @@ func loadConf(bytes []byte) (*NetConf, error) {
|
||||
if err := json.Unmarshal(bytes, n); err != nil {
|
||||
return nil, fmt.Errorf("failed to load netconf: %v", err)
|
||||
}
|
||||
if n.Device == "" && n.HWAddr == "" && n.KernelPath == "" {
|
||||
return nil, fmt.Errorf(`specify either "device", "hwaddr" or "kernelpath"`)
|
||||
if n.Device == "" && n.HWAddr == "" && n.KernelPath == "" && n.PCIAddr == "" {
|
||||
return nil, fmt.Errorf(`specify either "device", "hwaddr", "kernelpath" or "pciBusID"`)
|
||||
}
|
||||
return n, nil
|
||||
}
|
||||
@ -75,7 +81,7 @@ func cmdAdd(args *skel.CmdArgs) error {
|
||||
}
|
||||
defer containerNs.Close()
|
||||
|
||||
hostDev, err := getLink(cfg.Device, cfg.HWAddr, cfg.KernelPath)
|
||||
hostDev, err := getLink(cfg.Device, cfg.HWAddr, cfg.KernelPath, cfg.PCIAddr)
|
||||
if err != nil {
|
||||
return fmt.Errorf("failed to find host device: %v", err)
|
||||
}
|
||||
@ -240,7 +246,7 @@ func printLink(dev netlink.Link, cniVersion string, containerNs ns.NetNS) error
|
||||
return types.PrintResult(&result, cniVersion)
|
||||
}
|
||||
|
||||
func getLink(devname, hwaddr, kernelpath string) (netlink.Link, error) {
|
||||
func getLink(devname, hwaddr, kernelpath, pciaddr string) (netlink.Link, error) {
|
||||
links, err := netlink.LinkList()
|
||||
if err != nil {
|
||||
return nil, fmt.Errorf("failed to list node links: %v", err)
|
||||
@ -278,6 +284,19 @@ func getLink(devname, hwaddr, kernelpath string) (netlink.Link, error) {
|
||||
}
|
||||
}
|
||||
}
|
||||
} else if len(pciaddr) > 0 {
|
||||
netDir := filepath.Join(sysBusPCI, pciaddr, "net")
|
||||
if _, err := os.Lstat(netDir); err != nil {
|
||||
return nil, fmt.Errorf("no net directory under pci device %s: %q", pciaddr, err)
|
||||
}
|
||||
fInfo, err := ioutil.ReadDir(netDir)
|
||||
if err != nil {
|
||||
return nil, fmt.Errorf("failed to read net directory %s: %q", netDir, err)
|
||||
}
|
||||
if len(fInfo) > 0 {
|
||||
return netlink.LinkByName(fInfo[0].Name())
|
||||
}
|
||||
return nil, fmt.Errorf("failed to find device name for pci address %s", pciaddr)
|
||||
}
|
||||
|
||||
return nil, fmt.Errorf("failed to find physical interface")
|
||||
|
@ -40,6 +40,7 @@ type Net struct {
|
||||
Device string `json:"device"` // Device-Name, something like eth0 or can0 etc.
|
||||
HWAddr string `json:"hwaddr"` // MAC Address of target network interface
|
||||
KernelPath string `json:"kernelpath"` // Kernelpath of the device
|
||||
PCIAddr string `json:"pciBusID"` // PCI Address of target network device
|
||||
IPAM *IPAMConfig `json:"ipam,omitempty"`
|
||||
DNS types.DNS `json:"dns"`
|
||||
RawPrevResult map[string]interface{} `json:"prevResult,omitempty"`
|
||||
@ -449,7 +450,7 @@ var _ = Describe("base functionality", func() {
|
||||
StdinData: []byte(conf),
|
||||
}
|
||||
_, _, err := testutils.CmdAddWithArgs(args, func() error { return cmdAdd(args) })
|
||||
Expect(err).To(MatchError(`specify either "device", "hwaddr" or "kernelpath"`))
|
||||
Expect(err).To(MatchError(`specify either "device", "hwaddr", "kernelpath" or "pciBusID"`))
|
||||
|
||||
})
|
||||
|
||||
|
@ -621,6 +621,257 @@ var _ = Describe("bandwidth test", func() {
|
||||
})
|
||||
})
|
||||
|
||||
Describe("Getting the host interface which plugin should work on from veth peer of container interface", func() {
|
||||
It("Should work with multiple host veth interfaces", func() {
|
||||
conf := `{
|
||||
"cniVersion": "0.4.0",
|
||||
"name": "cni-plugin-bandwidth-test",
|
||||
"type": "bandwidth",
|
||||
"ingressRate": 8,
|
||||
"ingressBurst": 8,
|
||||
"egressRate": 16,
|
||||
"egressBurst": 8,
|
||||
"prevResult": {
|
||||
"interfaces": [
|
||||
{
|
||||
"name": "%s",
|
||||
"sandbox": ""
|
||||
},
|
||||
{
|
||||
"name": "%s",
|
||||
"sandbox": ""
|
||||
},
|
||||
{
|
||||
"name": "%s",
|
||||
"sandbox": ""
|
||||
},
|
||||
{
|
||||
"name": "%s",
|
||||
"sandbox": "%s"
|
||||
}
|
||||
],
|
||||
"ips": [
|
||||
{
|
||||
"version": "4",
|
||||
"address": "%s/24",
|
||||
"gateway": "10.0.0.1",
|
||||
"interface": 1
|
||||
}
|
||||
],
|
||||
"routes": []
|
||||
}
|
||||
}`
|
||||
|
||||
// create veth peer in host ns
|
||||
vethName, peerName := "host-veth-peer1", "host-veth-peer2"
|
||||
createVethInOneNs(hostNs.Path(), vethName, peerName)
|
||||
|
||||
conf = fmt.Sprintf(conf, vethName, peerName, hostIfname, containerIfname, containerNs.Path(), containerIP.String())
|
||||
args := &skel.CmdArgs{
|
||||
ContainerID: "dummy",
|
||||
Netns: containerNs.Path(),
|
||||
IfName: containerIfname,
|
||||
StdinData: []byte(conf),
|
||||
}
|
||||
|
||||
Expect(hostNs.Do(func(netNS ns.NetNS) error {
|
||||
defer GinkgoRecover()
|
||||
r, out, err := testutils.CmdAdd(containerNs.Path(), args.ContainerID, "", []byte(conf), func() error { return cmdAdd(args) })
|
||||
Expect(err).NotTo(HaveOccurred(), string(out))
|
||||
result, err := current.GetResult(r)
|
||||
Expect(err).NotTo(HaveOccurred())
|
||||
|
||||
Expect(result.Interfaces).To(HaveLen(5))
|
||||
Expect(result.Interfaces[4].Name).To(Equal(ifbDeviceName))
|
||||
Expect(result.Interfaces[4].Sandbox).To(Equal(""))
|
||||
|
||||
ifbLink, err := netlink.LinkByName(ifbDeviceName)
|
||||
Expect(err).NotTo(HaveOccurred())
|
||||
Expect(ifbLink.Attrs().MTU).To(Equal(hostIfaceMTU))
|
||||
|
||||
qdiscs, err := netlink.QdiscList(ifbLink)
|
||||
Expect(err).NotTo(HaveOccurred())
|
||||
|
||||
Expect(qdiscs).To(HaveLen(1))
|
||||
Expect(qdiscs[0].Attrs().LinkIndex).To(Equal(ifbLink.Attrs().Index))
|
||||
|
||||
Expect(qdiscs[0]).To(BeAssignableToTypeOf(&netlink.Tbf{}))
|
||||
Expect(qdiscs[0].(*netlink.Tbf).Rate).To(Equal(uint64(2)))
|
||||
Expect(qdiscs[0].(*netlink.Tbf).Limit).To(Equal(uint32(1)))
|
||||
|
||||
hostVethLink, err := netlink.LinkByName(hostIfname)
|
||||
Expect(err).NotTo(HaveOccurred())
|
||||
|
||||
qdiscFilters, err := netlink.FilterList(hostVethLink, netlink.MakeHandle(0xffff, 0))
|
||||
Expect(err).NotTo(HaveOccurred())
|
||||
|
||||
Expect(qdiscFilters).To(HaveLen(1))
|
||||
Expect(qdiscFilters[0].(*netlink.U32).Actions[0].(*netlink.MirredAction).Ifindex).To(Equal(ifbLink.Attrs().Index))
|
||||
|
||||
return nil
|
||||
})).To(Succeed())
|
||||
|
||||
Expect(hostNs.Do(func(n ns.NetNS) error {
|
||||
defer GinkgoRecover()
|
||||
|
||||
ifbLink, err := netlink.LinkByName(hostIfname)
|
||||
Expect(err).NotTo(HaveOccurred())
|
||||
|
||||
qdiscs, err := netlink.QdiscList(ifbLink)
|
||||
Expect(err).NotTo(HaveOccurred())
|
||||
|
||||
Expect(qdiscs).To(HaveLen(2))
|
||||
Expect(qdiscs[0].Attrs().LinkIndex).To(Equal(ifbLink.Attrs().Index))
|
||||
|
||||
Expect(qdiscs[0]).To(BeAssignableToTypeOf(&netlink.Tbf{}))
|
||||
Expect(qdiscs[0].(*netlink.Tbf).Rate).To(Equal(uint64(1)))
|
||||
Expect(qdiscs[0].(*netlink.Tbf).Limit).To(Equal(uint32(1)))
|
||||
return nil
|
||||
})).To(Succeed())
|
||||
|
||||
})
|
||||
|
||||
It("Should fail when container interface has no veth peer", func() {
|
||||
conf := `{
|
||||
"cniVersion": "0.4.0",
|
||||
"name": "cni-plugin-bandwidth-test",
|
||||
"type": "bandwidth",
|
||||
"ingressRate": 8,
|
||||
"ingressBurst": 8,
|
||||
"egressRate": 16,
|
||||
"egressBurst": 8,
|
||||
"prevResult": {
|
||||
"interfaces": [
|
||||
{
|
||||
"name": "%s",
|
||||
"sandbox": ""
|
||||
},
|
||||
{
|
||||
"name": "%s",
|
||||
"sandbox": "%s"
|
||||
}
|
||||
],
|
||||
"ips": [
|
||||
{
|
||||
"version": "4",
|
||||
"address": "%s/24",
|
||||
"gateway": "10.0.0.1",
|
||||
"interface": 1
|
||||
}
|
||||
],
|
||||
"routes": []
|
||||
}
|
||||
}`
|
||||
|
||||
// create a macvlan device to be container interface
|
||||
macvlanContainerIfname := "container-macv"
|
||||
createMacvlan(containerNs.Path(), containerIfname, macvlanContainerIfname)
|
||||
|
||||
conf = fmt.Sprintf(conf, hostIfname, macvlanContainerIfname, containerNs.Path(), containerIP.String())
|
||||
args := &skel.CmdArgs{
|
||||
ContainerID: "dummy",
|
||||
Netns: containerNs.Path(),
|
||||
IfName: macvlanContainerIfname,
|
||||
StdinData: []byte(conf),
|
||||
}
|
||||
|
||||
Expect(hostNs.Do(func(netNS ns.NetNS) error {
|
||||
defer GinkgoRecover()
|
||||
|
||||
_, _, err := testutils.CmdAdd(containerNs.Path(), args.ContainerID, "", []byte(conf), func() error { return cmdAdd(args) })
|
||||
Expect(err).To(HaveOccurred())
|
||||
|
||||
return nil
|
||||
})).To(Succeed())
|
||||
})
|
||||
|
||||
It("Should fail when preResult has no interfaces", func() {
|
||||
conf := `{
|
||||
"cniVersion": "0.4.0",
|
||||
"name": "cni-plugin-bandwidth-test",
|
||||
"type": "bandwidth",
|
||||
"ingressRate": 8,
|
||||
"ingressBurst": 8,
|
||||
"egressRate": 16,
|
||||
"egressBurst": 8,
|
||||
"prevResult": {
|
||||
"interfaces": [],
|
||||
"ips": [],
|
||||
"routes": []
|
||||
}
|
||||
}`
|
||||
|
||||
args := &skel.CmdArgs{
|
||||
ContainerID: "dummy",
|
||||
Netns: containerNs.Path(),
|
||||
IfName: "eth0",
|
||||
StdinData: []byte(conf),
|
||||
}
|
||||
|
||||
Expect(hostNs.Do(func(netNS ns.NetNS) error {
|
||||
defer GinkgoRecover()
|
||||
|
||||
_, _, err := testutils.CmdAdd(containerNs.Path(), args.ContainerID, "", []byte(conf), func() error { return cmdAdd(args) })
|
||||
Expect(err).To(HaveOccurred())
|
||||
|
||||
return nil
|
||||
})).To(Succeed())
|
||||
})
|
||||
|
||||
It("Should fail when veth peer of container interface does not match any of host interfaces in preResult", func() {
|
||||
conf := `{
|
||||
"cniVersion": "0.4.0",
|
||||
"name": "cni-plugin-bandwidth-test",
|
||||
"type": "bandwidth",
|
||||
"ingressRate": 8,
|
||||
"ingressBurst": 8,
|
||||
"egressRate": 16,
|
||||
"egressBurst": 8,
|
||||
"prevResult": {
|
||||
"interfaces": [
|
||||
{
|
||||
"name": "%s",
|
||||
"sandbox": ""
|
||||
},
|
||||
{
|
||||
"name": "%s",
|
||||
"sandbox": "%s"
|
||||
}
|
||||
],
|
||||
"ips": [
|
||||
{
|
||||
"version": "4",
|
||||
"address": "%s/24",
|
||||
"gateway": "10.0.0.1",
|
||||
"interface": 1
|
||||
}
|
||||
],
|
||||
"routes": []
|
||||
}
|
||||
}`
|
||||
|
||||
// fake a non-exist host interface name
|
||||
fakeHostIfname := fmt.Sprintf("%s-fake", hostIfname)
|
||||
|
||||
conf = fmt.Sprintf(conf, fakeHostIfname, containerIfname, containerNs.Path(), containerIP.String())
|
||||
args := &skel.CmdArgs{
|
||||
ContainerID: "dummy",
|
||||
Netns: containerNs.Path(),
|
||||
IfName: containerIfname,
|
||||
StdinData: []byte(conf),
|
||||
}
|
||||
|
||||
Expect(hostNs.Do(func(netNS ns.NetNS) error {
|
||||
defer GinkgoRecover()
|
||||
|
||||
_, _, err := testutils.CmdAdd(containerNs.Path(), args.ContainerID, "", []byte(conf), func() error { return cmdAdd(args) })
|
||||
Expect(err).To(HaveOccurred())
|
||||
|
||||
return nil
|
||||
})).To(Succeed())
|
||||
})
|
||||
})
|
||||
|
||||
Context("when chaining bandwidth plugin with PTP using 0.3.0 config", func() {
|
||||
var ptpConf string
|
||||
var rateInBits int
|
||||
|
@ -199,3 +199,61 @@ func createVeth(hostNamespace string, hostVethIfName string, containerNamespace
|
||||
|
||||
Expect(err).NotTo(HaveOccurred())
|
||||
}
|
||||
|
||||
func createVethInOneNs(namespace, vethName, peerName string) {
|
||||
vethDeviceRequest := &netlink.Veth{
|
||||
LinkAttrs: netlink.LinkAttrs{
|
||||
Name: vethName,
|
||||
Flags: net.FlagUp,
|
||||
},
|
||||
PeerName: peerName,
|
||||
}
|
||||
|
||||
netNS, err := ns.GetNS(namespace)
|
||||
Expect(err).NotTo(HaveOccurred())
|
||||
|
||||
err = netNS.Do(func(_ ns.NetNS) error {
|
||||
if err := netlink.LinkAdd(vethDeviceRequest); err != nil {
|
||||
return fmt.Errorf("failed to create veth pair: %v", err)
|
||||
}
|
||||
|
||||
_, err := netlink.LinkByName(peerName)
|
||||
if err != nil {
|
||||
return fmt.Errorf("failed to find newly-created veth device %q: %v", peerName, err)
|
||||
}
|
||||
return nil
|
||||
})
|
||||
Expect(err).NotTo(HaveOccurred())
|
||||
}
|
||||
|
||||
func createMacvlan(namespace, master, macvlanName string) {
|
||||
netNS, err := ns.GetNS(namespace)
|
||||
Expect(err).NotTo(HaveOccurred())
|
||||
|
||||
err = netNS.Do(func(_ ns.NetNS) error {
|
||||
m, err := netlink.LinkByName(master)
|
||||
if err != nil {
|
||||
return fmt.Errorf("failed to lookup master %q: %v", master, err)
|
||||
}
|
||||
|
||||
macvlanDeviceRequest := &netlink.Macvlan{
|
||||
LinkAttrs: netlink.LinkAttrs{
|
||||
MTU: m.Attrs().MTU,
|
||||
Name: macvlanName,
|
||||
ParentIndex: m.Attrs().Index,
|
||||
},
|
||||
Mode: netlink.MACVLAN_MODE_BRIDGE,
|
||||
}
|
||||
|
||||
if err = netlink.LinkAdd(macvlanDeviceRequest); err != nil {
|
||||
return fmt.Errorf("failed to create macvlan device: %s", err)
|
||||
}
|
||||
|
||||
_, err = netlink.LinkByName(macvlanName)
|
||||
if err != nil {
|
||||
return fmt.Errorf("failed to find newly-created macvlan device %q: %v", macvlanName, err)
|
||||
}
|
||||
return nil
|
||||
})
|
||||
Expect(err).NotTo(HaveOccurred())
|
||||
}
|
||||
|
@ -17,7 +17,6 @@ package main
|
||||
import (
|
||||
"crypto/sha1"
|
||||
"encoding/json"
|
||||
"errors"
|
||||
"fmt"
|
||||
|
||||
"github.com/vishvananda/netlink"
|
||||
@ -28,6 +27,7 @@ import (
|
||||
"github.com/containernetworking/cni/pkg/version"
|
||||
|
||||
"github.com/containernetworking/plugins/pkg/ip"
|
||||
"github.com/containernetworking/plugins/pkg/ns"
|
||||
bv "github.com/containernetworking/plugins/pkg/utils/buildversion"
|
||||
)
|
||||
|
||||
@ -130,21 +130,35 @@ func getMTU(deviceName string) (int, error) {
|
||||
return link.Attrs().MTU, nil
|
||||
}
|
||||
|
||||
func getHostInterface(interfaces []*current.Interface) (*current.Interface, error) {
|
||||
// get the veth peer of container interface in host namespace
|
||||
func getHostInterface(interfaces []*current.Interface, containerIfName string, netns ns.NetNS) (*current.Interface, error) {
|
||||
if len(interfaces) == 0 {
|
||||
return nil, errors.New("no interfaces provided")
|
||||
return nil, fmt.Errorf("no interfaces provided")
|
||||
}
|
||||
|
||||
// get veth peer index of container interface
|
||||
var peerIndex int
|
||||
var err error
|
||||
_ = netns.Do(func(_ ns.NetNS) error {
|
||||
_, peerIndex, err = ip.GetVethPeerIfindex(containerIfName)
|
||||
return nil
|
||||
})
|
||||
if peerIndex <= 0 {
|
||||
return nil, fmt.Errorf("container interface %s has no veth peer: %v", containerIfName, err)
|
||||
}
|
||||
|
||||
// find host interface by index
|
||||
link, err := netlink.LinkByIndex(peerIndex)
|
||||
if err != nil {
|
||||
return nil, fmt.Errorf("veth peer with index %d is not in host ns", peerIndex)
|
||||
}
|
||||
for _, iface := range interfaces {
|
||||
if iface.Sandbox == "" { // host interface
|
||||
_, _, err = ip.GetVethPeerIfindex(iface.Name)
|
||||
if err == nil {
|
||||
return iface, err
|
||||
}
|
||||
if iface.Sandbox == "" && iface.Name == link.Attrs().Name {
|
||||
return iface, nil
|
||||
}
|
||||
}
|
||||
return nil, errors.New(fmt.Sprintf("no host interface found. last error: %s", err))
|
||||
|
||||
return nil, fmt.Errorf("no veth peer of container interface found in host ns")
|
||||
}
|
||||
|
||||
func cmdAdd(args *skel.CmdArgs) error {
|
||||
@ -166,7 +180,14 @@ func cmdAdd(args *skel.CmdArgs) error {
|
||||
if err != nil {
|
||||
return fmt.Errorf("could not convert result to current version: %v", err)
|
||||
}
|
||||
hostInterface, err := getHostInterface(result.Interfaces)
|
||||
|
||||
netns, err := ns.GetNS(args.Netns)
|
||||
if err != nil {
|
||||
return fmt.Errorf("failed to open netns %q: %v", netns, err)
|
||||
}
|
||||
defer netns.Close()
|
||||
|
||||
hostInterface, err := getHostInterface(result.Interfaces, args.IfName, netns)
|
||||
if err != nil {
|
||||
return err
|
||||
}
|
||||
@ -266,7 +287,13 @@ func cmdCheck(args *skel.CmdArgs) error {
|
||||
return fmt.Errorf("could not convert result to current version: %v", err)
|
||||
}
|
||||
|
||||
hostInterface, err := getHostInterface(result.Interfaces)
|
||||
netns, err := ns.GetNS(args.Netns)
|
||||
if err != nil {
|
||||
return fmt.Errorf("failed to open netns %q: %v", netns, err)
|
||||
}
|
||||
defer netns.Close()
|
||||
|
||||
hostInterface, err := getHostInterface(result.Interfaces, args.IfName, netns)
|
||||
if err != nil {
|
||||
return err
|
||||
}
|
||||
|
@ -16,7 +16,7 @@ rm -Rf ${SRC_DIR}/${RELEASE_DIR}
|
||||
mkdir -p ${SRC_DIR}/${RELEASE_DIR}
|
||||
mkdir -p ${OUTPUT_DIR}
|
||||
|
||||
$DOCKER run -v ${SRC_DIR}:/go/src/github.com/containernetworking/plugins --rm golang:1.10-alpine \
|
||||
$DOCKER run -ti -v ${SRC_DIR}:/go/src/github.com/containernetworking/plugins --rm golang:1.12-alpine \
|
||||
/bin/sh -xe -c "\
|
||||
apk --no-cache add bash tar;
|
||||
cd /go/src/github.com/containernetworking/plugins; umask 0022;
|
||||
|
3
vendor/github.com/safchain/ethtool/.gitignore
generated
vendored
3
vendor/github.com/safchain/ethtool/.gitignore
generated
vendored
@ -22,3 +22,6 @@ _testmain.go
|
||||
*.exe
|
||||
*.test
|
||||
*.prof
|
||||
|
||||
# Skip compiled example binary file
|
||||
/example/example
|
||||
|
4
vendor/github.com/safchain/ethtool/Makefile
generated
vendored
Normal file
4
vendor/github.com/safchain/ethtool/Makefile
generated
vendored
Normal file
@ -0,0 +1,4 @@
|
||||
all: build
|
||||
|
||||
build:
|
||||
CGO_ENABLED=0 GOOS=linux GOARCH=amd64 go build
|
1
vendor/github.com/safchain/ethtool/README.md
generated
vendored
1
vendor/github.com/safchain/ethtool/README.md
generated
vendored
@ -33,6 +33,7 @@ func main() {
|
||||
if err != nil {
|
||||
panic(err.Error())
|
||||
}
|
||||
defer ethHandle.Close()
|
||||
|
||||
// Retrieve tx from eth0
|
||||
stats, err := ethHandle.Stats("eth0")
|
||||
|
375
vendor/github.com/safchain/ethtool/ethtool.go
generated
vendored
375
vendor/github.com/safchain/ethtool/ethtool.go
generated
vendored
@ -27,7 +27,9 @@ package ethtool
|
||||
|
||||
import (
|
||||
"bytes"
|
||||
"encoding/hex"
|
||||
"fmt"
|
||||
"strings"
|
||||
"syscall"
|
||||
"unsafe"
|
||||
)
|
||||
@ -46,20 +48,34 @@ const (
|
||||
const (
|
||||
ETH_GSTRING_LEN = 32
|
||||
ETH_SS_STATS = 1
|
||||
ETH_SS_FEATURES = 4
|
||||
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. */
|
||||
ETHTOOL_GSET = 0x00000001 /* Get settings. */
|
||||
ETHTOOL_SSET = 0x00000002 /* Set settings. */
|
||||
ETHTOOL_GMSGLVL = 0x00000007 /* Get driver message level */
|
||||
ETHTOOL_SMSGLVL = 0x00000008 /* Set driver msg level. */
|
||||
/* Get link status for host, i.e. whether the interface *and* the
|
||||
* physical port (if there is one) are up (ethtool_value). */
|
||||
ETHTOOL_GLINK = 0x0000000a
|
||||
ETHTOOL_GMODULEINFO = 0x00000042 /* Get plug-in module information */
|
||||
ETHTOOL_GMODULEEEPROM = 0x00000043 /* Get plug-in module eeprom */
|
||||
ETHTOOL_GPERMADDR = 0x00000020
|
||||
ETHTOOL_GFEATURES = 0x0000003a /* Get device offload settings */
|
||||
ETHTOOL_SFEATURES = 0x0000003b /* Change device offload settings */
|
||||
ETHTOOL_GFLAGS = 0x00000025 /* Get flags bitmap(ethtool_value) */
|
||||
ETHTOOL_GSSET_INFO = 0x00000037 /* Get string set info */
|
||||
)
|
||||
|
||||
// MAX_GSTRINGS maximum number of stats entries that ethtool can
|
||||
// retrieve currently.
|
||||
const (
|
||||
MAX_GSTRINGS = 1000
|
||||
MAX_GSTRINGS = 1000
|
||||
MAX_FEATURE_BLOCKS = (MAX_GSTRINGS + 32 - 1) / 32
|
||||
EEPROM_LEN = 640
|
||||
PERMADDR_LEN = 32
|
||||
)
|
||||
|
||||
type ifreq struct {
|
||||
@ -67,6 +83,38 @@ type ifreq struct {
|
||||
ifr_data uintptr
|
||||
}
|
||||
|
||||
// following structures comes from uapi/linux/ethtool.h
|
||||
type ethtoolSsetInfo struct {
|
||||
cmd uint32
|
||||
reserved uint32
|
||||
sset_mask uint32
|
||||
data uintptr
|
||||
}
|
||||
|
||||
type ethtoolGetFeaturesBlock struct {
|
||||
available uint32
|
||||
requested uint32
|
||||
active uint32
|
||||
never_changed uint32
|
||||
}
|
||||
|
||||
type ethtoolGfeatures struct {
|
||||
cmd uint32
|
||||
size uint32
|
||||
blocks [MAX_FEATURE_BLOCKS]ethtoolGetFeaturesBlock
|
||||
}
|
||||
|
||||
type ethtoolSetFeaturesBlock struct {
|
||||
valid uint32
|
||||
requested uint32
|
||||
}
|
||||
|
||||
type ethtoolSfeatures struct {
|
||||
cmd uint32
|
||||
size uint32
|
||||
blocks [MAX_FEATURE_BLOCKS]ethtoolSetFeaturesBlock
|
||||
}
|
||||
|
||||
type ethtoolDrvInfo struct {
|
||||
cmd uint32
|
||||
driver [32]byte
|
||||
@ -95,11 +143,37 @@ type ethtoolStats struct {
|
||||
data [MAX_GSTRINGS]uint64
|
||||
}
|
||||
|
||||
type ethtoolEeprom struct {
|
||||
cmd uint32
|
||||
magic uint32
|
||||
offset uint32
|
||||
len uint32
|
||||
data [EEPROM_LEN]byte
|
||||
}
|
||||
|
||||
type ethtoolModInfo struct {
|
||||
cmd uint32
|
||||
tpe uint32
|
||||
eeprom_len uint32
|
||||
reserved [8]uint32
|
||||
}
|
||||
|
||||
type ethtoolLink struct {
|
||||
cmd uint32
|
||||
data uint32
|
||||
}
|
||||
|
||||
type ethtoolPermAddr struct {
|
||||
cmd uint32
|
||||
size uint32
|
||||
data [PERMADDR_LEN]byte
|
||||
}
|
||||
|
||||
type Ethtool struct {
|
||||
fd int
|
||||
}
|
||||
|
||||
// DriverName returns the driver name of the given interface.
|
||||
// DriverName returns the driver name of the given interface name.
|
||||
func (e *Ethtool) DriverName(intf string) (string, error) {
|
||||
info, err := e.getDriverInfo(intf)
|
||||
if err != nil {
|
||||
@ -108,7 +182,7 @@ func (e *Ethtool) DriverName(intf string) (string, error) {
|
||||
return string(bytes.Trim(info.driver[:], "\x00")), nil
|
||||
}
|
||||
|
||||
// BusInfo returns the bus info of the given interface.
|
||||
// BusInfo returns the bus information of the given interface name.
|
||||
func (e *Ethtool) BusInfo(intf string) (string, error) {
|
||||
info, err := e.getDriverInfo(intf)
|
||||
if err != nil {
|
||||
@ -117,44 +191,259 @@ func (e *Ethtool) BusInfo(intf string) (string, error) {
|
||||
return string(bytes.Trim(info.bus_info[:], "\x00")), nil
|
||||
}
|
||||
|
||||
func (e *Ethtool) getDriverInfo(intf string) (ethtoolDrvInfo, error) {
|
||||
drvinfo := ethtoolDrvInfo{
|
||||
cmd: ETHTOOL_GDRVINFO,
|
||||
// ModuleEeprom returns Eeprom information of the given interface name.
|
||||
func (e *Ethtool) ModuleEeprom(intf string) ([]byte, error) {
|
||||
eeprom, _, err := e.getModuleEeprom(intf)
|
||||
if err != nil {
|
||||
return nil, err
|
||||
}
|
||||
|
||||
return eeprom.data[:eeprom.len], nil
|
||||
}
|
||||
|
||||
// ModuleEeprom returns Eeprom information of the given interface name.
|
||||
func (e *Ethtool) ModuleEepromHex(intf string) (string, error) {
|
||||
eeprom, _, err := e.getModuleEeprom(intf)
|
||||
if err != nil {
|
||||
return "", err
|
||||
}
|
||||
|
||||
return hex.EncodeToString(eeprom.data[:eeprom.len]), nil
|
||||
}
|
||||
|
||||
// DriverInfo returns driver information of the given interface name.
|
||||
func (e *Ethtool) DriverInfo(intf string) (ethtoolDrvInfo, error) {
|
||||
drvInfo, err := e.getDriverInfo(intf)
|
||||
if err != nil {
|
||||
return ethtoolDrvInfo{}, err
|
||||
}
|
||||
|
||||
return drvInfo, nil
|
||||
}
|
||||
|
||||
// PermAddr returns permanent address of the given interface name.
|
||||
func (e *Ethtool) PermAddr(intf string) (string, error) {
|
||||
permAddr, err := e.getPermAddr(intf)
|
||||
if err != nil {
|
||||
return "", err
|
||||
}
|
||||
|
||||
if permAddr.data[0] == 0 && permAddr.data[1] == 0 &&
|
||||
permAddr.data[2] == 0 && permAddr.data[3] == 0 &&
|
||||
permAddr.data[4] == 0 && permAddr.data[5] == 0 {
|
||||
return "", nil
|
||||
}
|
||||
|
||||
return fmt.Sprintf("%x:%x:%x:%x:%x:%x",
|
||||
permAddr.data[0:1],
|
||||
permAddr.data[1:2],
|
||||
permAddr.data[2:3],
|
||||
permAddr.data[3:4],
|
||||
permAddr.data[4:5],
|
||||
permAddr.data[5:6],
|
||||
), nil
|
||||
}
|
||||
|
||||
func (e *Ethtool) ioctl(intf string, data uintptr) error {
|
||||
var name [IFNAMSIZ]byte
|
||||
copy(name[:], []byte(intf))
|
||||
|
||||
ifr := ifreq{
|
||||
ifr_name: name,
|
||||
ifr_data: uintptr(unsafe.Pointer(&drvinfo)),
|
||||
ifr_data: data,
|
||||
}
|
||||
|
||||
_, _, ep := syscall.Syscall(syscall.SYS_IOCTL, uintptr(e.fd), SIOCETHTOOL, uintptr(unsafe.Pointer(&ifr)))
|
||||
if ep != 0 {
|
||||
return ethtoolDrvInfo{}, syscall.Errno(ep)
|
||||
return syscall.Errno(ep)
|
||||
}
|
||||
|
||||
return nil
|
||||
}
|
||||
|
||||
func (e *Ethtool) getDriverInfo(intf string) (ethtoolDrvInfo, error) {
|
||||
drvinfo := ethtoolDrvInfo{
|
||||
cmd: ETHTOOL_GDRVINFO,
|
||||
}
|
||||
|
||||
if err := e.ioctl(intf, uintptr(unsafe.Pointer(&drvinfo))); err != nil {
|
||||
return ethtoolDrvInfo{}, err
|
||||
}
|
||||
|
||||
return drvinfo, nil
|
||||
}
|
||||
|
||||
func (e *Ethtool) getPermAddr(intf string) (ethtoolPermAddr, error) {
|
||||
permAddr := ethtoolPermAddr{
|
||||
cmd: ETHTOOL_GPERMADDR,
|
||||
size: PERMADDR_LEN,
|
||||
}
|
||||
|
||||
if err := e.ioctl(intf, uintptr(unsafe.Pointer(&permAddr))); err != nil {
|
||||
return ethtoolPermAddr{}, err
|
||||
}
|
||||
|
||||
return permAddr, nil
|
||||
}
|
||||
|
||||
func (e *Ethtool) getModuleEeprom(intf string) (ethtoolEeprom, ethtoolModInfo, error) {
|
||||
modInfo := ethtoolModInfo{
|
||||
cmd: ETHTOOL_GMODULEINFO,
|
||||
}
|
||||
|
||||
if err := e.ioctl(intf, uintptr(unsafe.Pointer(&modInfo))); err != nil {
|
||||
return ethtoolEeprom{}, ethtoolModInfo{}, err
|
||||
}
|
||||
|
||||
eeprom := ethtoolEeprom{
|
||||
cmd: ETHTOOL_GMODULEEEPROM,
|
||||
len: modInfo.eeprom_len,
|
||||
offset: 0,
|
||||
}
|
||||
|
||||
if modInfo.eeprom_len > EEPROM_LEN {
|
||||
return ethtoolEeprom{}, ethtoolModInfo{}, fmt.Errorf("eeprom size: %d is larger than buffer size: %d", modInfo.eeprom_len, EEPROM_LEN)
|
||||
}
|
||||
|
||||
if err := e.ioctl(intf, uintptr(unsafe.Pointer(&eeprom))); err != nil {
|
||||
return ethtoolEeprom{}, ethtoolModInfo{}, err
|
||||
}
|
||||
|
||||
return eeprom, modInfo, nil
|
||||
}
|
||||
|
||||
func isFeatureBitSet(blocks [MAX_FEATURE_BLOCKS]ethtoolGetFeaturesBlock, index uint) bool {
|
||||
return (blocks)[index/32].active&(1<<(index%32)) != 0
|
||||
}
|
||||
|
||||
func setFeatureBit(blocks *[MAX_FEATURE_BLOCKS]ethtoolSetFeaturesBlock, index uint, value bool) {
|
||||
blockIndex, bitIndex := index/32, index%32
|
||||
|
||||
blocks[blockIndex].valid |= 1 << bitIndex
|
||||
|
||||
if value {
|
||||
blocks[blockIndex].requested |= 1 << bitIndex
|
||||
} else {
|
||||
blocks[blockIndex].requested &= ^(1 << bitIndex)
|
||||
}
|
||||
}
|
||||
|
||||
// FeatureNames shows supported features by their name.
|
||||
func (e *Ethtool) FeatureNames(intf string) (map[string]uint, error) {
|
||||
ssetInfo := ethtoolSsetInfo{
|
||||
cmd: ETHTOOL_GSSET_INFO,
|
||||
sset_mask: 1 << ETH_SS_FEATURES,
|
||||
}
|
||||
|
||||
if err := e.ioctl(intf, uintptr(unsafe.Pointer(&ssetInfo))); err != nil {
|
||||
return nil, err
|
||||
}
|
||||
|
||||
length := uint32(ssetInfo.data)
|
||||
if length == 0 {
|
||||
return map[string]uint{}, nil
|
||||
} else if length > MAX_GSTRINGS {
|
||||
return nil, fmt.Errorf("ethtool currently doesn't support more than %d entries, received %d", MAX_GSTRINGS, length)
|
||||
}
|
||||
|
||||
gstrings := ethtoolGStrings{
|
||||
cmd: ETHTOOL_GSTRINGS,
|
||||
string_set: ETH_SS_FEATURES,
|
||||
len: length,
|
||||
data: [MAX_GSTRINGS * ETH_GSTRING_LEN]byte{},
|
||||
}
|
||||
|
||||
if err := e.ioctl(intf, uintptr(unsafe.Pointer(&gstrings))); err != nil {
|
||||
return nil, err
|
||||
}
|
||||
|
||||
var result = make(map[string]uint)
|
||||
for i := 0; i != int(length); i++ {
|
||||
b := gstrings.data[i*ETH_GSTRING_LEN : i*ETH_GSTRING_LEN+ETH_GSTRING_LEN]
|
||||
key := string(bytes.Trim(b, "\x00"))
|
||||
if key != "" {
|
||||
result[key] = uint(i)
|
||||
}
|
||||
}
|
||||
|
||||
return result, nil
|
||||
}
|
||||
|
||||
// Features retrieves features of the given interface name.
|
||||
func (e *Ethtool) Features(intf string) (map[string]bool, error) {
|
||||
names, err := e.FeatureNames(intf)
|
||||
if err != nil {
|
||||
return nil, err
|
||||
}
|
||||
|
||||
length := uint32(len(names))
|
||||
if length == 0 {
|
||||
return map[string]bool{}, nil
|
||||
}
|
||||
|
||||
features := ethtoolGfeatures{
|
||||
cmd: ETHTOOL_GFEATURES,
|
||||
size: (length + 32 - 1) / 32,
|
||||
}
|
||||
|
||||
if err := e.ioctl(intf, uintptr(unsafe.Pointer(&features))); err != nil {
|
||||
return nil, err
|
||||
}
|
||||
|
||||
var result = make(map[string]bool, length)
|
||||
for key, index := range names {
|
||||
result[key] = isFeatureBitSet(features.blocks, index)
|
||||
}
|
||||
|
||||
return result, nil
|
||||
}
|
||||
|
||||
// Change requests a change in the given device's features.
|
||||
func (e *Ethtool) Change(intf string, config map[string]bool) error {
|
||||
names, err := e.FeatureNames(intf)
|
||||
if err != nil {
|
||||
return err
|
||||
}
|
||||
|
||||
length := uint32(len(names))
|
||||
|
||||
features := ethtoolSfeatures{
|
||||
cmd: ETHTOOL_SFEATURES,
|
||||
size: (length + 32 - 1) / 32,
|
||||
}
|
||||
|
||||
for key, value := range config {
|
||||
if index, ok := names[key]; ok {
|
||||
setFeatureBit(&features.blocks, index, value)
|
||||
} else {
|
||||
return fmt.Errorf("unsupported feature %q", key)
|
||||
}
|
||||
}
|
||||
|
||||
return e.ioctl(intf, uintptr(unsafe.Pointer(&features)))
|
||||
}
|
||||
|
||||
// Get state of a link.
|
||||
func (e *Ethtool) LinkState(intf string) (uint32, error) {
|
||||
x := ethtoolLink{
|
||||
cmd: ETHTOOL_GLINK,
|
||||
}
|
||||
|
||||
if err := e.ioctl(intf, uintptr(unsafe.Pointer(&x))); err != nil {
|
||||
return 0, err
|
||||
}
|
||||
|
||||
return x.data, 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 err := e.ioctl(intf, uintptr(unsafe.Pointer(&drvinfo))); err != nil {
|
||||
return nil, err
|
||||
}
|
||||
|
||||
if drvinfo.n_stats*ETH_GSTRING_LEN > MAX_GSTRINGS*ETH_GSTRING_LEN {
|
||||
@ -167,11 +456,9 @@ func (e *Ethtool) Stats(intf string) (map[string]uint64, error) {
|
||||
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)
|
||||
if err := e.ioctl(intf, uintptr(unsafe.Pointer(&gstrings))); err != nil {
|
||||
return nil, err
|
||||
}
|
||||
|
||||
stats := ethtoolStats{
|
||||
@ -180,31 +467,32 @@ func (e *Ethtool) Stats(intf string) (map[string]uint64, error) {
|
||||
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)
|
||||
if err := e.ioctl(intf, uintptr(unsafe.Pointer(&stats))); err != nil {
|
||||
return nil, err
|
||||
}
|
||||
|
||||
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]
|
||||
key := string(b[:strings.Index(string(b), "\x00")])
|
||||
if len(key) != 0 {
|
||||
result[key] = stats.data[i]
|
||||
}
|
||||
}
|
||||
|
||||
return result, nil
|
||||
}
|
||||
|
||||
// Close closes the ethool handler
|
||||
func (e *Ethtool) Close() {
|
||||
syscall.Close(e.fd)
|
||||
}
|
||||
|
||||
// NewEthtool returns a new ethtool handler
|
||||
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)
|
||||
fd, err := syscall.Socket(syscall.AF_INET, syscall.SOCK_DGRAM, syscall.IPPROTO_IP)
|
||||
if err != nil {
|
||||
return nil, err
|
||||
}
|
||||
|
||||
return &Ethtool{
|
||||
@ -212,6 +500,7 @@ func NewEthtool() (*Ethtool, error) {
|
||||
}, nil
|
||||
}
|
||||
|
||||
// BusInfo returns bus information of the given interface name.
|
||||
func BusInfo(intf string) (string, error) {
|
||||
e, err := NewEthtool()
|
||||
if err != nil {
|
||||
@ -221,6 +510,7 @@ func BusInfo(intf string) (string, error) {
|
||||
return e.BusInfo(intf)
|
||||
}
|
||||
|
||||
// DriverName returns the driver name of the given interface name.
|
||||
func DriverName(intf string) (string, error) {
|
||||
e, err := NewEthtool()
|
||||
if err != nil {
|
||||
@ -230,6 +520,7 @@ func DriverName(intf string) (string, error) {
|
||||
return e.DriverName(intf)
|
||||
}
|
||||
|
||||
// Stats retrieves stats of the given interface name.
|
||||
func Stats(intf string) (map[string]uint64, error) {
|
||||
e, err := NewEthtool()
|
||||
if err != nil {
|
||||
@ -238,3 +529,13 @@ func Stats(intf string) (map[string]uint64, error) {
|
||||
defer e.Close()
|
||||
return e.Stats(intf)
|
||||
}
|
||||
|
||||
// PermAddr returns permanent address of the given interface name.
|
||||
func PermAddr(intf string) (string, error) {
|
||||
e, err := NewEthtool()
|
||||
if err != nil {
|
||||
return "", err
|
||||
}
|
||||
defer e.Close()
|
||||
return e.PermAddr(intf)
|
||||
}
|
||||
|
7
vendor/github.com/safchain/ethtool/ethtool_cmd.go
generated
vendored
7
vendor/github.com/safchain/ethtool/ethtool_cmd.go
generated
vendored
@ -26,6 +26,7 @@
|
||||
package ethtool
|
||||
|
||||
import (
|
||||
"math"
|
||||
"reflect"
|
||||
"syscall"
|
||||
"unsafe"
|
||||
@ -128,6 +129,9 @@ func (e *Ethtool) CmdGet(ecmd *EthtoolCmd, intf string) (uint32, error) {
|
||||
|
||||
var speedval uint32 = (uint32(ecmd.Speed_hi) << 16) |
|
||||
(uint32(ecmd.Speed) & 0xffff)
|
||||
if speedval == math.MaxUint16 {
|
||||
speedval = math.MaxUint32
|
||||
}
|
||||
|
||||
return speedval, nil
|
||||
}
|
||||
@ -153,6 +157,9 @@ func (e *Ethtool) CmdSet(ecmd *EthtoolCmd, intf string) (uint32, error) {
|
||||
|
||||
var speedval uint32 = (uint32(ecmd.Speed_hi) << 16) |
|
||||
(uint32(ecmd.Speed) & 0xffff)
|
||||
if speedval == math.MaxUint16 {
|
||||
speedval = math.MaxUint32
|
||||
}
|
||||
|
||||
return speedval, nil
|
||||
}
|
||||
|
Reference in New Issue
Block a user