Tomofumi Hayashi 3a7f254a63 Introduce iplink(MTU MAC and promiscas) feature into tuning
This change adds 'ip link' command related feature into tuning
meta cni plugin. Currently MTU, MAC and promiscas mode are
supported.
2018-07-31 12:54:15 +09:00

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")
}