-
Notifications
You must be signed in to change notification settings - Fork 1.1k
Commit
This commit does not belong to any branch on this repository, and may belong to a fork outside of the repository.
- Loading branch information
Showing
2 changed files
with
240 additions
and
0 deletions.
There are no files selected for viewing
This file contains bidirectional Unicode text that may be interpreted or compiled differently than what appears below. To review, open the file in an editor that reveals hidden Unicode characters.
Learn more about bidirectional Unicode characters
Original file line number | Diff line number | Diff line change |
---|---|---|
@@ -0,0 +1,54 @@ | ||
# SR-IOV CNI plugin | ||
|
||
## Enable SR-IOV | ||
|
||
Given Intel ixgbe NIC on CentOS, Fedora or RHEL: | ||
|
||
``` | ||
# vi /etc/modprobe.conf | ||
options ixgbe max_vfs=8,8 | ||
``` | ||
|
||
## Usage | ||
|
||
Given the following network configuration: | ||
|
||
``` | ||
# cat > /etc/cni/net.d/10-mynet.conf <<EOF | ||
{ | ||
"name": "mynet", | ||
"type": "sriov", | ||
"master": "eth1", | ||
"vf": 1, | ||
"mac": "66:d8:02:77:aa:aa", | ||
"ipam": { | ||
"type": "host-local", | ||
"subnet": "10.55.206.0/26", | ||
"routes": [ | ||
{ "dst": "0.0.0.0/0" } | ||
], | ||
"gateway": "10.55.206.1" | ||
} | ||
} | ||
EOF | ||
``` | ||
|
||
``` | ||
# CNI_PATH=$CNI_PATH CNI_ARGS="IP=10.55.206.46" ./priv-net-run.sh ifconfig | ||
eth0 Link encap:Ethernet HWaddr A6:9A:3B:00:63:16 | ||
inet addr:10.55.206.46 Bcast:0.0.0.0 Mask:255.255.255.192 | ||
inet6 addr: fe80::a49a:3bff:fe00:6316/64 Scope:Link | ||
UP BROADCAST RUNNING MULTICAST MTU:1500 Metric:1 | ||
RX packets:0 errors:0 dropped:0 overruns:0 frame:0 | ||
TX packets:1 errors:0 dropped:0 overruns:0 carrier:0 | ||
collisions:0 txqueuelen:1000 | ||
RX bytes:0 (0.0 b) TX bytes:90 (90.0 b) | ||
lo Link encap:Local Loopback | ||
inet addr:127.0.0.1 Mask:255.0.0.0 | ||
inet6 addr: ::1/128 Scope:Host | ||
UP LOOPBACK RUNNING MTU:65536 Metric:1 | ||
RX packets:0 errors:0 dropped:0 overruns:0 frame:0 | ||
TX packets:0 errors:0 dropped:0 overruns:0 carrier:0 | ||
collisions:0 txqueuelen:0 | ||
RX bytes:0 (0.0 b) TX bytes:0 (0.0 b) | ||
``` |
This file contains bidirectional Unicode text that may be interpreted or compiled differently than what appears below. To review, open the file in an editor that reveals hidden Unicode characters.
Learn more about bidirectional Unicode characters
Original file line number | Diff line number | Diff line change |
---|---|---|
@@ -0,0 +1,186 @@ | ||
// Copyright 2015 CNI authors | ||
// | ||
// Licensed under the Apache License, Version 2.0 (the "License"); | ||
// you may not use this file except in compliance with the License. | ||
// You may obtain a copy of the License at | ||
// | ||
// http://www.apache.org/licenses/LICENSE-2.0 | ||
// | ||
// Unless required by applicable law or agreed to in writing, software | ||
// distributed under the License is distributed on an "AS IS" BASIS, | ||
// WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied. | ||
// See the License for the specific language governing permissions and | ||
// limitations under the License. | ||
|
||
package main | ||
|
||
import ( | ||
"encoding/json" | ||
"errors" | ||
"fmt" | ||
"io/ioutil" | ||
"net" | ||
"os" | ||
"runtime" | ||
|
||
"github.com/containernetworking/cni/pkg/ipam" | ||
"github.com/containernetworking/cni/pkg/ns" | ||
"github.com/containernetworking/cni/pkg/skel" | ||
"github.com/containernetworking/cni/pkg/types" | ||
"github.com/vishvananda/netlink" | ||
) | ||
|
||
type NetConf struct { | ||
types.NetConf | ||
Master string `json:"master"` | ||
MAC string `json:"mac"` | ||
VF int `json:"vf"` | ||
Vlan int `json:"vlan"` | ||
} | ||
|
||
func init() { | ||
// this ensures that main runs only on main thread (thread group leader). | ||
// since namespace ops (unshare, setns) are done for a single thread, we | ||
// must ensure that the goroutine does not jump from OS thread to thread | ||
runtime.LockOSThread() | ||
} | ||
|
||
func loadConf(bytes []byte) (*NetConf, error) { | ||
n := &NetConf{} | ||
if err := json.Unmarshal(bytes, n); err != nil { | ||
return nil, fmt.Errorf("failed to load netconf: %v", err) | ||
} | ||
if n.Master == "" { | ||
return nil, fmt.Errorf(`"master" field is required. It specifies the host interface name to virtualize`) | ||
} | ||
return n, nil | ||
} | ||
|
||
func setupVF(conf *NetConf, ifName string, netns ns.NetNS) error { | ||
|
||
masterName := conf.Master | ||
vfIdx := conf.VF | ||
|
||
m, err := netlink.LinkByName(masterName) | ||
if err != nil { | ||
return fmt.Errorf("failed to lookup master %q: %v", conf.Master, err) | ||
} | ||
|
||
vfDir := fmt.Sprintf("/sys/class/net/%s/device/virtfn%d/net", masterName, vfIdx) | ||
if _, err := os.Lstat(vfDir); err != nil { | ||
return err | ||
} | ||
|
||
infos, err := ioutil.ReadDir(vfDir) | ||
if err != nil { | ||
return err | ||
} | ||
|
||
if len(infos) != 1 { | ||
return fmt.Errorf("Mutiple network devices in directory %s", vfDir) | ||
} | ||
|
||
// VF NIC name | ||
vfDevName := infos[0].Name() | ||
vfDev, err := netlink.LinkByName(vfDevName) | ||
if err != nil { | ||
return fmt.Errorf("failed to lookup vf device %q: %v", vfDevName, err) | ||
} | ||
|
||
// set hardware address | ||
if conf.MAC != "" { | ||
macAddr, err := net.ParseMAC(conf.MAC) | ||
if err != nil { | ||
return err | ||
} | ||
if err = netlink.LinkSetVfHardwareAddr(m, conf.VF, macAddr); err != nil { | ||
return fmt.Errorf("failed to set vf %d macaddress: %v", conf.VF, err) | ||
} | ||
} | ||
|
||
if conf.Vlan != 0 { | ||
if err = netlink.LinkSetVfVlan(m, conf.VF, conf.Vlan); err != nil { | ||
return fmt.Errorf("failed to set vf %d vlan: %v", conf.VF, err) | ||
} | ||
} | ||
|
||
if err = netlink.LinkSetUp(vfDev); err != nil { | ||
return fmt.Errorf("failed to setup vf device: %v", err) | ||
} | ||
|
||
// move vf device to ns | ||
if err = netlink.LinkSetNsFd(vfDev, int(netns.Fd())); err != nil { | ||
return fmt.Errorf("failed to move vf %d to netnamespace: %v", conf.VF, err) | ||
} | ||
|
||
return netns.Do(func(_ ns.NetNS) error { | ||
err := renameLink(vfDevName, ifName) | ||
if err != nil { | ||
return fmt.Errorf("failed to rename vf device %q to %q: %v", vfDevName, ifName, err) | ||
} | ||
return nil | ||
}) | ||
} | ||
|
||
func cmdAdd(args *skel.CmdArgs) error { | ||
n, err := loadConf(args.StdinData) | ||
if err != nil { | ||
return err | ||
} | ||
|
||
netns, err := ns.GetNS(args.Netns) | ||
if err != nil { | ||
return fmt.Errorf("failed to open netns %q: %v", netns, err) | ||
} | ||
defer netns.Close() | ||
|
||
if err = setupVF(n, args.IfName, netns); err != nil { | ||
return err | ||
} | ||
|
||
// run the IPAM plugin and get back the config to apply | ||
result, err := ipam.ExecAdd(n.IPAM.Type, args.StdinData) | ||
if err != nil { | ||
return err | ||
} | ||
if result.IP4 == nil { | ||
return errors.New("IPAM plugin returned missing IPv4 config") | ||
} | ||
|
||
err = netns.Do(func(_ ns.NetNS) error { | ||
return ipam.ConfigureIface(args.IfName, result) | ||
}) | ||
if err != nil { | ||
return err | ||
} | ||
|
||
result.DNS = n.DNS | ||
return result.Print() | ||
} | ||
|
||
func cmdDel(args *skel.CmdArgs) error { | ||
n, err := loadConf(args.StdinData) | ||
if err != nil { | ||
return err | ||
} | ||
|
||
err = ipam.ExecDel(n.IPAM.Type, args.StdinData) | ||
if err != nil { | ||
return err | ||
} | ||
|
||
return nil | ||
} | ||
|
||
func renameLink(curName, newName string) error { | ||
link, err := netlink.LinkByName(curName) | ||
if err != nil { | ||
return err | ||
} | ||
|
||
return netlink.LinkSetName(link, newName) | ||
} | ||
|
||
func main() { | ||
skel.PluginMain(cmdAdd, cmdDel) | ||
} |