Matthieu MOREL 177e0bf2d9
enable staticcheck linter
Signed-off-by: Matthieu MOREL <matthieu.morel35@gmail.com>
2023-03-02 11:06:22 +01:00

211 lines
5.1 KiB
Go

// Copyright 2020 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"
"fmt"
"github.com/vishvananda/netlink"
"github.com/containernetworking/cni/pkg/skel"
"github.com/containernetworking/cni/pkg/types"
current "github.com/containernetworking/cni/pkg/types/100"
"github.com/containernetworking/cni/pkg/version"
"github.com/containernetworking/plugins/pkg/ns"
bv "github.com/containernetworking/plugins/pkg/utils/buildversion"
)
// VRFNetConf represents the vrf configuration.
type VRFNetConf struct {
types.NetConf
// VRFName is the name of the vrf to add the interface to.
VRFName string `json:"vrfname"`
// Table is the optional name of the routing table set for the vrf
Table uint32 `json:"table"`
}
func main() {
skel.PluginMain(cmdAdd, cmdCheck, cmdDel, version.VersionsStartingFrom("0.3.1"), bv.BuildString("vrf"))
}
func cmdAdd(args *skel.CmdArgs) error {
conf, result, err := parseConf(args.StdinData)
if err != nil {
return err
}
if conf.PrevResult == nil {
return fmt.Errorf("missing prevResult from earlier plugin")
}
err = ns.WithNetNSPath(args.Netns, func(_ ns.NetNS) error {
vrf, err := findVRF(conf.VRFName)
// If the user set a tableid and the vrf is already in the namespace
// we check if the tableid is the same one already assigned to the vrf.
if err == nil && conf.Table != 0 && vrf.Table != conf.Table {
return fmt.Errorf("VRF %s already exist with different routing table %d", conf.VRFName, vrf.Table)
}
if _, ok := err.(netlink.LinkNotFoundError); ok {
vrf, err = createVRF(conf.VRFName, conf.Table)
}
if err != nil {
return err
}
err = addInterface(vrf, args.IfName)
if err != nil {
return err
}
return nil
})
if err != nil {
return fmt.Errorf("cmdAdd failed: %v", err)
}
if result == nil {
result = &current.Result{}
}
return types.PrintResult(result, conf.CNIVersion)
}
func cmdDel(args *skel.CmdArgs) error {
conf, _, err := parseConf(args.StdinData)
if err != nil {
return err
}
err = ns.WithNetNSPath(args.Netns, func(_ ns.NetNS) error {
vrf, err := findVRF(conf.VRFName)
if _, ok := err.(netlink.LinkNotFoundError); ok {
return nil
}
if err != nil {
return err
}
err = resetMaster(args.IfName)
if err != nil {
return err
}
interfaces, err := assignedInterfaces(vrf)
if err != nil {
return err
}
// Meaning, we are deleting the last interface assigned to the VRF
if len(interfaces) == 0 {
err = netlink.LinkDel(vrf)
if err != nil {
return err
}
}
return nil
})
if err != nil {
// if NetNs is passed down by the Cloud Orchestration Engine, or if it called multiple times
// so don't return an error if the device is already removed.
// https://github.com/kubernetes/kubernetes/issues/43014#issuecomment-287164444
_, ok := err.(ns.NSPathNotExistErr)
if ok {
return nil
}
return err
}
if err != nil {
return fmt.Errorf("cmdDel failed: %v", err)
}
return nil
}
func cmdCheck(args *skel.CmdArgs) error {
conf, _, err := parseConf(args.StdinData)
if err != nil {
return err
}
// Ensure we have previous result.
if conf.PrevResult == nil {
return fmt.Errorf("missing prevResult from earlier plugin")
}
err = ns.WithNetNSPath(args.Netns, func(_ ns.NetNS) error {
vrf, err := findVRF(conf.VRFName)
if err != nil {
return err
}
vrfInterfaces, err := assignedInterfaces(vrf)
if err != nil {
return err
}
found := false
for _, intf := range vrfInterfaces {
if intf.Attrs().Name == args.IfName {
found = true
break
}
}
if !found {
return fmt.Errorf("failed to find %s associated to vrf %s", args.IfName, conf.VRFName)
}
return nil
})
if err != nil {
return err
}
return nil
}
func parseConf(data []byte) (*VRFNetConf, *current.Result, error) {
conf := VRFNetConf{}
if err := json.Unmarshal(data, &conf); err != nil {
return nil, nil, fmt.Errorf("failed to load netconf: %v", err)
}
if conf.VRFName == "" {
return nil, nil, fmt.Errorf("configuration is expected to have a valid vrf name")
}
if conf.RawPrevResult == nil {
// return early if there was no previous result, which is allowed for DEL calls
return &conf, &current.Result{}, nil
}
// Parse previous result.
var result *current.Result
var err error
if err = version.ParsePrevResult(&conf.NetConf); err != nil {
return nil, nil, fmt.Errorf("could not parse prevResult: %v", err)
}
result, err = current.NewResultFromResult(conf.PrevResult)
if err != nil {
return nil, nil, fmt.Errorf("could not convert result to current version: %v", err)
}
return &conf, result, nil
}