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
|
dist: trusty
|
||||||
|
|
||||||
go:
|
go:
|
||||||
- 1.10.x
|
|
||||||
- 1.11.x
|
- 1.11.x
|
||||||
|
- 1.12.x
|
||||||
|
|
||||||
env:
|
env:
|
||||||
global:
|
global:
|
||||||
- PATH=$GOROOT/bin:$GOPATH/bin:$PATH
|
- PATH=$GOROOT/bin:$GOPATH/bin:$PATH
|
||||||
- CGO_ENABLED=0
|
- CGO_ENABLED=0
|
||||||
matrix:
|
matrix:
|
||||||
|
- TARGET=386
|
||||||
- TARGET=amd64
|
- TARGET=amd64
|
||||||
- TARGET=arm
|
- TARGET=arm
|
||||||
- TARGET=arm64
|
- TARGET=arm64
|
||||||
@ -22,10 +23,10 @@ matrix:
|
|||||||
include:
|
include:
|
||||||
- os: windows
|
- os: windows
|
||||||
env: TARGET=amd64
|
env: TARGET=amd64
|
||||||
go: 1.10.x
|
go: 1.11.x
|
||||||
- os: windows
|
- os: windows
|
||||||
env: TARGET=amd64
|
env: TARGET=amd64
|
||||||
go: 1.11.x
|
go: 1.12.x
|
||||||
|
|
||||||
install:
|
install:
|
||||||
- go get github.com/onsi/ginkgo/ginkgo
|
- 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",
|
"ImportPath": "github.com/safchain/ethtool",
|
||||||
"Rev": "7ff1ba29eca231991280817541cb3903f6be15d1"
|
"Rev": "42ed695e3de80b9d695f280295fd7994639f209d"
|
||||||
},
|
},
|
||||||
{
|
{
|
||||||
"ImportPath": "github.com/sirupsen/logrus",
|
"ImportPath": "github.com/sirupsen/logrus",
|
||||||
|
@ -517,7 +517,7 @@ func cmdAdd(args *skel.CmdArgs) error {
|
|||||||
chain := utils.FormatChainName(n.Name, args.ContainerID)
|
chain := utils.FormatChainName(n.Name, args.ContainerID)
|
||||||
comment := utils.FormatComment(n.Name, args.ContainerID)
|
comment := utils.FormatComment(n.Name, args.ContainerID)
|
||||||
for _, ipc := range result.IPs {
|
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
|
return err
|
||||||
}
|
}
|
||||||
}
|
}
|
||||||
|
@ -17,12 +17,14 @@ package main
|
|||||||
import (
|
import (
|
||||||
"encoding/json"
|
"encoding/json"
|
||||||
"fmt"
|
"fmt"
|
||||||
"github.com/vishvananda/netlink/nl"
|
|
||||||
"io/ioutil"
|
"io/ioutil"
|
||||||
"net"
|
"net"
|
||||||
"os"
|
"os"
|
||||||
"strings"
|
"strings"
|
||||||
|
|
||||||
|
"github.com/coreos/go-iptables/iptables"
|
||||||
|
"github.com/vishvananda/netlink/nl"
|
||||||
|
|
||||||
"github.com/containernetworking/cni/pkg/skel"
|
"github.com/containernetworking/cni/pkg/skel"
|
||||||
"github.com/containernetworking/cni/pkg/types"
|
"github.com/containernetworking/cni/pkg/types"
|
||||||
"github.com/containernetworking/cni/pkg/types/020"
|
"github.com/containernetworking/cni/pkg/types/020"
|
||||||
@ -70,6 +72,7 @@ type testCase struct {
|
|||||||
isLayer2 bool
|
isLayer2 bool
|
||||||
expGWCIDRs []string // Expected gateway addresses in CIDR form
|
expGWCIDRs []string // Expected gateway addresses in CIDR form
|
||||||
vlan int
|
vlan int
|
||||||
|
ipMasq bool
|
||||||
}
|
}
|
||||||
|
|
||||||
// Range definition for each entry in the ranges list
|
// Range definition for each entry in the ranges list
|
||||||
@ -105,8 +108,7 @@ const (
|
|||||||
"vlan": %d`
|
"vlan": %d`
|
||||||
|
|
||||||
netDefault = `,
|
netDefault = `,
|
||||||
"isDefaultGateway": true,
|
"isDefaultGateway": true`
|
||||||
"ipMasq": false`
|
|
||||||
|
|
||||||
ipamStartStr = `,
|
ipamStartStr = `,
|
||||||
"ipam": {
|
"ipam": {
|
||||||
@ -115,6 +117,9 @@ const (
|
|||||||
ipamDataDirStr = `,
|
ipamDataDirStr = `,
|
||||||
"dataDir": "%s"`
|
"dataDir": "%s"`
|
||||||
|
|
||||||
|
ipMasqConfStr = `,
|
||||||
|
"ipMasq": %t`
|
||||||
|
|
||||||
// Single subnet configuration (legacy)
|
// Single subnet configuration (legacy)
|
||||||
subnetConfStr = `,
|
subnetConfStr = `,
|
||||||
"subnet": "%s"`
|
"subnet": "%s"`
|
||||||
@ -147,6 +152,9 @@ func (tc testCase) netConfJSON(dataDir string) string {
|
|||||||
if tc.vlan != 0 {
|
if tc.vlan != 0 {
|
||||||
conf += fmt.Sprintf(vlan, tc.vlan)
|
conf += fmt.Sprintf(vlan, tc.vlan)
|
||||||
}
|
}
|
||||||
|
if tc.ipMasq {
|
||||||
|
conf += tc.ipMasqConfig()
|
||||||
|
}
|
||||||
|
|
||||||
if !tc.isLayer2 {
|
if !tc.isLayer2 {
|
||||||
conf += netDefault
|
conf += netDefault
|
||||||
@ -178,6 +186,11 @@ func (tc testCase) subnetConfig() string {
|
|||||||
return conf
|
return conf
|
||||||
}
|
}
|
||||||
|
|
||||||
|
func (tc testCase) ipMasqConfig() string {
|
||||||
|
conf := fmt.Sprintf(ipMasqConfStr, tc.ipMasq)
|
||||||
|
return conf
|
||||||
|
}
|
||||||
|
|
||||||
func (tc testCase) rangesConfig() string {
|
func (tc testCase) rangesConfig() string {
|
||||||
conf := rangesStartStr
|
conf := rangesStartStr
|
||||||
for i, tcRange := range tc.ranges {
|
for i, tcRange := range tc.ranges {
|
||||||
@ -1595,4 +1608,40 @@ var _ = Describe("bridge Operations", func() {
|
|||||||
cmdAddDelCheckTest(originalNS, tc, dataDir)
|
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
|
# host-device
|
||||||
|
|
||||||
Move an already-existing device into a container.
|
Move an already-existing device into a container.
|
||||||
|
|
||||||
This simple plugin will move the requested device from the host's network namespace
|
## Overview
|
||||||
to the container's. Nothing else will be done - no IPAM, no addresses.
|
|
||||||
|
|
||||||
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`
|
* `device`: The device name, e.g. `eth0`, `can0`
|
||||||
* `hwaddr`: A MAC address
|
* `hwaddr`: A MAC address
|
||||||
* `kernelpath`: The kernel device kobj, e.g. `/sys/devices/pci0000:00/0000:00:1f.6`
|
* `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.
|
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
|
```json
|
||||||
{
|
{
|
||||||
@ -20,3 +28,13 @@ A sample configuration might look like:
|
|||||||
"device": "enp0s1"
|
"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"
|
"fmt"
|
||||||
"io/ioutil"
|
"io/ioutil"
|
||||||
"net"
|
"net"
|
||||||
|
"os"
|
||||||
"path/filepath"
|
"path/filepath"
|
||||||
"runtime"
|
"runtime"
|
||||||
"strings"
|
"strings"
|
||||||
@ -38,12 +39,17 @@ import (
|
|||||||
bv "github.com/containernetworking/plugins/pkg/utils/buildversion"
|
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
|
//NetConf for host-device config, look the README to learn how to use those parameters
|
||||||
type NetConf struct {
|
type NetConf struct {
|
||||||
types.NetConf
|
types.NetConf
|
||||||
Device string `json:"device"` // Device-Name, something like eth0 or can0 etc.
|
Device string `json:"device"` // Device-Name, something like eth0 or can0 etc.
|
||||||
HWAddr string `json:"hwaddr"` // MAC Address of target network interface
|
HWAddr string `json:"hwaddr"` // MAC Address of target network interface
|
||||||
KernelPath string `json:"kernelpath"` // Kernelpath of the device
|
KernelPath string `json:"kernelpath"` // Kernelpath of the device
|
||||||
|
PCIAddr string `json:"pciBusID"` // PCI Address of target network device
|
||||||
}
|
}
|
||||||
|
|
||||||
func init() {
|
func init() {
|
||||||
@ -58,8 +64,8 @@ func loadConf(bytes []byte) (*NetConf, error) {
|
|||||||
if err := json.Unmarshal(bytes, n); err != nil {
|
if err := json.Unmarshal(bytes, n); err != nil {
|
||||||
return nil, fmt.Errorf("failed to load netconf: %v", err)
|
return nil, fmt.Errorf("failed to load netconf: %v", err)
|
||||||
}
|
}
|
||||||
if n.Device == "" && n.HWAddr == "" && n.KernelPath == "" {
|
if n.Device == "" && n.HWAddr == "" && n.KernelPath == "" && n.PCIAddr == "" {
|
||||||
return nil, fmt.Errorf(`specify either "device", "hwaddr" or "kernelpath"`)
|
return nil, fmt.Errorf(`specify either "device", "hwaddr", "kernelpath" or "pciBusID"`)
|
||||||
}
|
}
|
||||||
return n, nil
|
return n, nil
|
||||||
}
|
}
|
||||||
@ -75,7 +81,7 @@ func cmdAdd(args *skel.CmdArgs) error {
|
|||||||
}
|
}
|
||||||
defer containerNs.Close()
|
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 {
|
if err != nil {
|
||||||
return fmt.Errorf("failed to find host device: %v", err)
|
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)
|
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()
|
links, err := netlink.LinkList()
|
||||||
if err != nil {
|
if err != nil {
|
||||||
return nil, fmt.Errorf("failed to list node links: %v", err)
|
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")
|
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.
|
Device string `json:"device"` // Device-Name, something like eth0 or can0 etc.
|
||||||
HWAddr string `json:"hwaddr"` // MAC Address of target network interface
|
HWAddr string `json:"hwaddr"` // MAC Address of target network interface
|
||||||
KernelPath string `json:"kernelpath"` // Kernelpath of the device
|
KernelPath string `json:"kernelpath"` // Kernelpath of the device
|
||||||
|
PCIAddr string `json:"pciBusID"` // PCI Address of target network device
|
||||||
IPAM *IPAMConfig `json:"ipam,omitempty"`
|
IPAM *IPAMConfig `json:"ipam,omitempty"`
|
||||||
DNS types.DNS `json:"dns"`
|
DNS types.DNS `json:"dns"`
|
||||||
RawPrevResult map[string]interface{} `json:"prevResult,omitempty"`
|
RawPrevResult map[string]interface{} `json:"prevResult,omitempty"`
|
||||||
@ -449,7 +450,7 @@ var _ = Describe("base functionality", func() {
|
|||||||
StdinData: []byte(conf),
|
StdinData: []byte(conf),
|
||||||
}
|
}
|
||||||
_, _, err := testutils.CmdAddWithArgs(args, func() error { return cmdAdd(args) })
|
_, _, 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() {
|
Context("when chaining bandwidth plugin with PTP using 0.3.0 config", func() {
|
||||||
var ptpConf string
|
var ptpConf string
|
||||||
var rateInBits int
|
var rateInBits int
|
||||||
|
@ -199,3 +199,61 @@ func createVeth(hostNamespace string, hostVethIfName string, containerNamespace
|
|||||||
|
|
||||||
Expect(err).NotTo(HaveOccurred())
|
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 (
|
import (
|
||||||
"crypto/sha1"
|
"crypto/sha1"
|
||||||
"encoding/json"
|
"encoding/json"
|
||||||
"errors"
|
|
||||||
"fmt"
|
"fmt"
|
||||||
|
|
||||||
"github.com/vishvananda/netlink"
|
"github.com/vishvananda/netlink"
|
||||||
@ -28,6 +27,7 @@ import (
|
|||||||
"github.com/containernetworking/cni/pkg/version"
|
"github.com/containernetworking/cni/pkg/version"
|
||||||
|
|
||||||
"github.com/containernetworking/plugins/pkg/ip"
|
"github.com/containernetworking/plugins/pkg/ip"
|
||||||
|
"github.com/containernetworking/plugins/pkg/ns"
|
||||||
bv "github.com/containernetworking/plugins/pkg/utils/buildversion"
|
bv "github.com/containernetworking/plugins/pkg/utils/buildversion"
|
||||||
)
|
)
|
||||||
|
|
||||||
@ -130,21 +130,35 @@ func getMTU(deviceName string) (int, error) {
|
|||||||
return link.Attrs().MTU, nil
|
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 {
|
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
|
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 {
|
for _, iface := range interfaces {
|
||||||
if iface.Sandbox == "" { // host interface
|
if iface.Sandbox == "" && iface.Name == link.Attrs().Name {
|
||||||
_, _, err = ip.GetVethPeerIfindex(iface.Name)
|
return iface, nil
|
||||||
if err == nil {
|
|
||||||
return iface, err
|
|
||||||
}
|
}
|
||||||
}
|
}
|
||||||
}
|
|
||||||
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 {
|
func cmdAdd(args *skel.CmdArgs) error {
|
||||||
@ -166,7 +180,14 @@ func cmdAdd(args *skel.CmdArgs) error {
|
|||||||
if err != nil {
|
if err != nil {
|
||||||
return fmt.Errorf("could not convert result to current version: %v", err)
|
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 {
|
if err != nil {
|
||||||
return err
|
return err
|
||||||
}
|
}
|
||||||
@ -266,7 +287,13 @@ func cmdCheck(args *skel.CmdArgs) error {
|
|||||||
return fmt.Errorf("could not convert result to current version: %v", err)
|
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 {
|
if err != nil {
|
||||||
return err
|
return err
|
||||||
}
|
}
|
||||||
|
@ -16,7 +16,7 @@ rm -Rf ${SRC_DIR}/${RELEASE_DIR}
|
|||||||
mkdir -p ${SRC_DIR}/${RELEASE_DIR}
|
mkdir -p ${SRC_DIR}/${RELEASE_DIR}
|
||||||
mkdir -p ${OUTPUT_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 "\
|
/bin/sh -xe -c "\
|
||||||
apk --no-cache add bash tar;
|
apk --no-cache add bash tar;
|
||||||
cd /go/src/github.com/containernetworking/plugins; umask 0022;
|
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
|
*.exe
|
||||||
*.test
|
*.test
|
||||||
*.prof
|
*.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 {
|
if err != nil {
|
||||||
panic(err.Error())
|
panic(err.Error())
|
||||||
}
|
}
|
||||||
|
defer ethHandle.Close()
|
||||||
|
|
||||||
// Retrieve tx from eth0
|
// Retrieve tx from eth0
|
||||||
stats, err := ethHandle.Stats("eth0")
|
stats, err := ethHandle.Stats("eth0")
|
||||||
|
363
vendor/github.com/safchain/ethtool/ethtool.go
generated
vendored
363
vendor/github.com/safchain/ethtool/ethtool.go
generated
vendored
@ -27,7 +27,9 @@ package ethtool
|
|||||||
|
|
||||||
import (
|
import (
|
||||||
"bytes"
|
"bytes"
|
||||||
|
"encoding/hex"
|
||||||
"fmt"
|
"fmt"
|
||||||
|
"strings"
|
||||||
"syscall"
|
"syscall"
|
||||||
"unsafe"
|
"unsafe"
|
||||||
)
|
)
|
||||||
@ -46,6 +48,7 @@ const (
|
|||||||
const (
|
const (
|
||||||
ETH_GSTRING_LEN = 32
|
ETH_GSTRING_LEN = 32
|
||||||
ETH_SS_STATS = 1
|
ETH_SS_STATS = 1
|
||||||
|
ETH_SS_FEATURES = 4
|
||||||
ETHTOOL_GDRVINFO = 0x00000003
|
ETHTOOL_GDRVINFO = 0x00000003
|
||||||
ETHTOOL_GSTRINGS = 0x0000001b
|
ETHTOOL_GSTRINGS = 0x0000001b
|
||||||
ETHTOOL_GSTATS = 0x0000001d
|
ETHTOOL_GSTATS = 0x0000001d
|
||||||
@ -54,12 +57,25 @@ const (
|
|||||||
ETHTOOL_SSET = 0x00000002 /* Set settings. */
|
ETHTOOL_SSET = 0x00000002 /* Set settings. */
|
||||||
ETHTOOL_GMSGLVL = 0x00000007 /* Get driver message level */
|
ETHTOOL_GMSGLVL = 0x00000007 /* Get driver message level */
|
||||||
ETHTOOL_SMSGLVL = 0x00000008 /* Set driver msg 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
|
// MAX_GSTRINGS maximum number of stats entries that ethtool can
|
||||||
// retrieve currently.
|
// retrieve currently.
|
||||||
const (
|
const (
|
||||||
MAX_GSTRINGS = 1000
|
MAX_GSTRINGS = 1000
|
||||||
|
MAX_FEATURE_BLOCKS = (MAX_GSTRINGS + 32 - 1) / 32
|
||||||
|
EEPROM_LEN = 640
|
||||||
|
PERMADDR_LEN = 32
|
||||||
)
|
)
|
||||||
|
|
||||||
type ifreq struct {
|
type ifreq struct {
|
||||||
@ -67,6 +83,38 @@ type ifreq struct {
|
|||||||
ifr_data uintptr
|
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 {
|
type ethtoolDrvInfo struct {
|
||||||
cmd uint32
|
cmd uint32
|
||||||
driver [32]byte
|
driver [32]byte
|
||||||
@ -95,11 +143,37 @@ type ethtoolStats struct {
|
|||||||
data [MAX_GSTRINGS]uint64
|
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 {
|
type Ethtool struct {
|
||||||
fd int
|
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) {
|
func (e *Ethtool) DriverName(intf string) (string, error) {
|
||||||
info, err := e.getDriverInfo(intf)
|
info, err := e.getDriverInfo(intf)
|
||||||
if err != nil {
|
if err != nil {
|
||||||
@ -108,7 +182,7 @@ func (e *Ethtool) DriverName(intf string) (string, error) {
|
|||||||
return string(bytes.Trim(info.driver[:], "\x00")), nil
|
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) {
|
func (e *Ethtool) BusInfo(intf string) (string, error) {
|
||||||
info, err := e.getDriverInfo(intf)
|
info, err := e.getDriverInfo(intf)
|
||||||
if err != nil {
|
if err != nil {
|
||||||
@ -117,44 +191,259 @@ func (e *Ethtool) BusInfo(intf string) (string, error) {
|
|||||||
return string(bytes.Trim(info.bus_info[:], "\x00")), nil
|
return string(bytes.Trim(info.bus_info[:], "\x00")), nil
|
||||||
}
|
}
|
||||||
|
|
||||||
func (e *Ethtool) getDriverInfo(intf string) (ethtoolDrvInfo, error) {
|
// ModuleEeprom returns Eeprom information of the given interface name.
|
||||||
drvinfo := ethtoolDrvInfo{
|
func (e *Ethtool) ModuleEeprom(intf string) ([]byte, error) {
|
||||||
cmd: ETHTOOL_GDRVINFO,
|
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
|
var name [IFNAMSIZ]byte
|
||||||
copy(name[:], []byte(intf))
|
copy(name[:], []byte(intf))
|
||||||
|
|
||||||
ifr := ifreq{
|
ifr := ifreq{
|
||||||
ifr_name: name,
|
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)))
|
_, _, ep := syscall.Syscall(syscall.SYS_IOCTL, uintptr(e.fd), SIOCETHTOOL, uintptr(unsafe.Pointer(&ifr)))
|
||||||
if ep != 0 {
|
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
|
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.
|
// Stats retrieves stats of the given interface name.
|
||||||
func (e *Ethtool) Stats(intf string) (map[string]uint64, error) {
|
func (e *Ethtool) Stats(intf string) (map[string]uint64, error) {
|
||||||
drvinfo := ethtoolDrvInfo{
|
drvinfo := ethtoolDrvInfo{
|
||||||
cmd: ETHTOOL_GDRVINFO,
|
cmd: ETHTOOL_GDRVINFO,
|
||||||
}
|
}
|
||||||
|
|
||||||
var name [IFNAMSIZ]byte
|
if err := e.ioctl(intf, uintptr(unsafe.Pointer(&drvinfo))); err != nil {
|
||||||
copy(name[:], []byte(intf))
|
return nil, err
|
||||||
|
|
||||||
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 {
|
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,
|
len: drvinfo.n_stats,
|
||||||
data: [MAX_GSTRINGS * ETH_GSTRING_LEN]byte{},
|
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 err := e.ioctl(intf, uintptr(unsafe.Pointer(&gstrings))); err != nil {
|
||||||
if ep != 0 {
|
return nil, err
|
||||||
return nil, syscall.Errno(ep)
|
|
||||||
}
|
}
|
||||||
|
|
||||||
stats := ethtoolStats{
|
stats := ethtoolStats{
|
||||||
@ -180,31 +467,32 @@ func (e *Ethtool) Stats(intf string) (map[string]uint64, error) {
|
|||||||
data: [MAX_GSTRINGS]uint64{},
|
data: [MAX_GSTRINGS]uint64{},
|
||||||
}
|
}
|
||||||
|
|
||||||
ifr.ifr_data = uintptr(unsafe.Pointer(&stats))
|
if err := e.ioctl(intf, uintptr(unsafe.Pointer(&stats))); err != nil {
|
||||||
|
return nil, err
|
||||||
_, _, 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)
|
var result = make(map[string]uint64)
|
||||||
for i := 0; i != int(drvinfo.n_stats); i++ {
|
for i := 0; i != int(drvinfo.n_stats); i++ {
|
||||||
b := gstrings.data[i*ETH_GSTRING_LEN : i*ETH_GSTRING_LEN+ETH_GSTRING_LEN]
|
b := gstrings.data[i*ETH_GSTRING_LEN : i*ETH_GSTRING_LEN+ETH_GSTRING_LEN]
|
||||||
key := string(bytes.Trim(b, "\x00"))
|
key := string(b[:strings.Index(string(b), "\x00")])
|
||||||
|
if len(key) != 0 {
|
||||||
result[key] = stats.data[i]
|
result[key] = stats.data[i]
|
||||||
}
|
}
|
||||||
|
}
|
||||||
|
|
||||||
return result, nil
|
return result, nil
|
||||||
}
|
}
|
||||||
|
|
||||||
|
// Close closes the ethool handler
|
||||||
func (e *Ethtool) Close() {
|
func (e *Ethtool) Close() {
|
||||||
syscall.Close(e.fd)
|
syscall.Close(e.fd)
|
||||||
}
|
}
|
||||||
|
|
||||||
|
// NewEthtool returns a new ethtool handler
|
||||||
func NewEthtool() (*Ethtool, error) {
|
func NewEthtool() (*Ethtool, error) {
|
||||||
fd, _, err := syscall.RawSyscall(syscall.SYS_SOCKET, syscall.AF_INET, syscall.SOCK_DGRAM, syscall.IPPROTO_IP)
|
fd, err := syscall.Socket(syscall.AF_INET, syscall.SOCK_DGRAM, syscall.IPPROTO_IP)
|
||||||
if err != 0 {
|
if err != nil {
|
||||||
return nil, syscall.Errno(err)
|
return nil, err
|
||||||
}
|
}
|
||||||
|
|
||||||
return &Ethtool{
|
return &Ethtool{
|
||||||
@ -212,6 +500,7 @@ func NewEthtool() (*Ethtool, error) {
|
|||||||
}, nil
|
}, nil
|
||||||
}
|
}
|
||||||
|
|
||||||
|
// BusInfo returns bus information of the given interface name.
|
||||||
func BusInfo(intf string) (string, error) {
|
func BusInfo(intf string) (string, error) {
|
||||||
e, err := NewEthtool()
|
e, err := NewEthtool()
|
||||||
if err != nil {
|
if err != nil {
|
||||||
@ -221,6 +510,7 @@ func BusInfo(intf string) (string, error) {
|
|||||||
return e.BusInfo(intf)
|
return e.BusInfo(intf)
|
||||||
}
|
}
|
||||||
|
|
||||||
|
// DriverName returns the driver name of the given interface name.
|
||||||
func DriverName(intf string) (string, error) {
|
func DriverName(intf string) (string, error) {
|
||||||
e, err := NewEthtool()
|
e, err := NewEthtool()
|
||||||
if err != nil {
|
if err != nil {
|
||||||
@ -230,6 +520,7 @@ func DriverName(intf string) (string, error) {
|
|||||||
return e.DriverName(intf)
|
return e.DriverName(intf)
|
||||||
}
|
}
|
||||||
|
|
||||||
|
// Stats retrieves stats of the given interface name.
|
||||||
func Stats(intf string) (map[string]uint64, error) {
|
func Stats(intf string) (map[string]uint64, error) {
|
||||||
e, err := NewEthtool()
|
e, err := NewEthtool()
|
||||||
if err != nil {
|
if err != nil {
|
||||||
@ -238,3 +529,13 @@ func Stats(intf string) (map[string]uint64, error) {
|
|||||||
defer e.Close()
|
defer e.Close()
|
||||||
return e.Stats(intf)
|
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
|
package ethtool
|
||||||
|
|
||||||
import (
|
import (
|
||||||
|
"math"
|
||||||
"reflect"
|
"reflect"
|
||||||
"syscall"
|
"syscall"
|
||||||
"unsafe"
|
"unsafe"
|
||||||
@ -128,6 +129,9 @@ func (e *Ethtool) CmdGet(ecmd *EthtoolCmd, intf string) (uint32, error) {
|
|||||||
|
|
||||||
var speedval uint32 = (uint32(ecmd.Speed_hi) << 16) |
|
var speedval uint32 = (uint32(ecmd.Speed_hi) << 16) |
|
||||||
(uint32(ecmd.Speed) & 0xffff)
|
(uint32(ecmd.Speed) & 0xffff)
|
||||||
|
if speedval == math.MaxUint16 {
|
||||||
|
speedval = math.MaxUint32
|
||||||
|
}
|
||||||
|
|
||||||
return speedval, nil
|
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) |
|
var speedval uint32 = (uint32(ecmd.Speed_hi) << 16) |
|
||||||
(uint32(ecmd.Speed) & 0xffff)
|
(uint32(ecmd.Speed) & 0xffff)
|
||||||
|
if speedval == math.MaxUint16 {
|
||||||
|
speedval = math.MaxUint32
|
||||||
|
}
|
||||||
|
|
||||||
return speedval, nil
|
return speedval, nil
|
||||||
}
|
}
|
||||||
|
Reference in New Issue
Block a user