
This change adds 'ip link' command related feature into tuning meta cni plugin. Currently MTU, MAC and promiscas mode are supported.
212 lines
5.7 KiB
Go
212 lines
5.7 KiB
Go
// Copyright 2016 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.
|
|
|
|
// This is a "meta-plugin". It reads in its own netconf, it does not create
|
|
// any network interface but just changes the network sysctl.
|
|
|
|
package main
|
|
|
|
import (
|
|
"encoding/json"
|
|
"fmt"
|
|
"io/ioutil"
|
|
"net"
|
|
"path/filepath"
|
|
"strings"
|
|
|
|
"github.com/containernetworking/cni/pkg/skel"
|
|
"github.com/containernetworking/cni/pkg/types"
|
|
"github.com/containernetworking/cni/pkg/types/current"
|
|
"github.com/containernetworking/cni/pkg/version"
|
|
"github.com/containernetworking/plugins/pkg/ns"
|
|
"github.com/vishvananda/netlink"
|
|
)
|
|
|
|
// TuningConf represents the network tuning configuration.
|
|
type TuningConf struct {
|
|
types.NetConf
|
|
SysCtl map[string]string `json:"sysctl"`
|
|
RawPrevResult map[string]interface{} `json:"prevResult,omitempty"`
|
|
PrevResult *current.Result `json:"-"`
|
|
Mac string `json:"mac,omitempty"`
|
|
Promisc bool `json:"promisc,omitempty"`
|
|
Mtu int `json:"mtu,omitempty"`
|
|
}
|
|
|
|
type MACEnvArgs struct {
|
|
types.CommonArgs
|
|
MAC types.UnmarshallableString `json:"mac,omitempty"`
|
|
}
|
|
|
|
func parseConf(data []byte, envArgs string) (*TuningConf, error) {
|
|
conf := TuningConf{Promisc: false, Mtu: -1}
|
|
if err := json.Unmarshal(data, &conf); err != nil {
|
|
return nil, fmt.Errorf("failed to load netconf: %v", err)
|
|
}
|
|
|
|
// Parse custom MAC from both env args
|
|
if envArgs != "" {
|
|
e := MACEnvArgs{}
|
|
err := types.LoadArgs(envArgs, &e)
|
|
if err != nil {
|
|
return nil, err
|
|
}
|
|
|
|
if e.MAC != "" {
|
|
conf.Mac = string(e.MAC)
|
|
}
|
|
}
|
|
|
|
// Parse previous result.
|
|
if conf.RawPrevResult != nil {
|
|
resultBytes, err := json.Marshal(conf.RawPrevResult)
|
|
if err != nil {
|
|
return nil, fmt.Errorf("could not serialize prevResult: %v", err)
|
|
}
|
|
res, err := version.NewResult(conf.CNIVersion, resultBytes)
|
|
if err != nil {
|
|
return nil, fmt.Errorf("could not parse prevResult: %v", err)
|
|
}
|
|
conf.RawPrevResult = nil
|
|
conf.PrevResult, err = current.NewResultFromResult(res)
|
|
if err != nil {
|
|
return nil, fmt.Errorf("could not convert result to current version: %v", err)
|
|
}
|
|
}
|
|
|
|
return &conf, nil
|
|
}
|
|
|
|
func changeMacAddr(ifName string, newMacAddr string) error {
|
|
addr, err := net.ParseMAC(newMacAddr)
|
|
if err != nil {
|
|
return fmt.Errorf("invalid args %v for MAC addr: %v", newMacAddr, err)
|
|
}
|
|
|
|
link, err := netlink.LinkByName(ifName)
|
|
if err != nil {
|
|
return fmt.Errorf("failed to get %q: %v", ifName, err)
|
|
}
|
|
|
|
err = netlink.LinkSetDown(link)
|
|
if err != nil {
|
|
return fmt.Errorf("failed to set %q down: %v", ifName, err)
|
|
}
|
|
err = netlink.LinkSetHardwareAddr(link, addr)
|
|
if err != nil {
|
|
return fmt.Errorf("failed to set %q address to %q: %v", ifName, newMacAddr, err)
|
|
}
|
|
return netlink.LinkSetUp(link)
|
|
}
|
|
|
|
func updateResultsMacAddr(config TuningConf, ifName string, newMacAddr string) error {
|
|
for _, i := range config.PrevResult.Interfaces {
|
|
if i.Name == ifName {
|
|
i.Mac = newMacAddr
|
|
}
|
|
}
|
|
return nil
|
|
}
|
|
|
|
func changePromisc(ifName string, val bool) error {
|
|
link, err := netlink.LinkByName(ifName)
|
|
if err != nil {
|
|
return fmt.Errorf("failed to get %q: %v", ifName, err)
|
|
}
|
|
|
|
if val {
|
|
return netlink.SetPromiscOn(link)
|
|
}
|
|
return netlink.SetPromiscOff(link)
|
|
}
|
|
|
|
func changeMtu(ifName string, mtu int) error {
|
|
link, err := netlink.LinkByName(ifName)
|
|
if err != nil {
|
|
return fmt.Errorf("failed to get %q: %v", ifName, err)
|
|
}
|
|
return netlink.LinkSetMTU(link, mtu)
|
|
}
|
|
|
|
func cmdAdd(args *skel.CmdArgs) error {
|
|
tuningConf, err := parseConf(args.StdinData, args.Args)
|
|
if err != nil {
|
|
return err
|
|
}
|
|
|
|
// The directory /proc/sys/net is per network namespace. Enter in the
|
|
// network namespace before writing on it.
|
|
|
|
err = ns.WithNetNSPath(args.Netns, func(_ ns.NetNS) error {
|
|
for key, value := range tuningConf.SysCtl {
|
|
fileName := filepath.Join("/proc/sys", strings.Replace(key, ".", "/", -1))
|
|
fileName = filepath.Clean(fileName)
|
|
|
|
// Refuse to modify sysctl parameters that don't belong
|
|
// to the network subsystem.
|
|
if !strings.HasPrefix(fileName, "/proc/sys/net/") {
|
|
return fmt.Errorf("invalid net sysctl key: %q", key)
|
|
}
|
|
content := []byte(value)
|
|
err := ioutil.WriteFile(fileName, content, 0644)
|
|
if err != nil {
|
|
return err
|
|
}
|
|
}
|
|
|
|
var err error
|
|
|
|
if tuningConf.Mac != "" {
|
|
if err = changeMacAddr(args.IfName, tuningConf.Mac); err == nil {
|
|
err = updateResultsMacAddr(*tuningConf, args.IfName, tuningConf.Mac)
|
|
}
|
|
}
|
|
|
|
if tuningConf.Promisc != false {
|
|
if err = changePromisc(args.IfName, true); err != nil {
|
|
return err
|
|
}
|
|
}
|
|
|
|
if tuningConf.Mtu != -1 {
|
|
if err = changeMtu(args.IfName, tuningConf.Mtu); err != nil {
|
|
return err
|
|
}
|
|
}
|
|
return nil
|
|
})
|
|
if err != nil {
|
|
return err
|
|
}
|
|
|
|
return types.PrintResult(tuningConf.PrevResult, tuningConf.CNIVersion)
|
|
}
|
|
|
|
func cmdDel(args *skel.CmdArgs) error {
|
|
// TODO: the settings are not reverted to the previous values. Reverting the
|
|
// settings is not useful when the whole container goes away but it could be
|
|
// useful in scenarios where plugins are added and removed at runtime.
|
|
return nil
|
|
}
|
|
|
|
func main() {
|
|
// TODO: implement plugin version
|
|
skel.PluginMain(cmdAdd, cmdGet, cmdDel, version.All, "TODO")
|
|
}
|
|
|
|
func cmdGet(args *skel.CmdArgs) error {
|
|
// TODO: implement
|
|
return fmt.Errorf("not implemented")
|
|
}
|