// Copyright 2018 Amazon.com, Inc. or its affiliates. All Rights Reserved. // // Licensed under the Apache License, Version 2.0 (the "License"). You may // not use this file except in compliance with the License. A copy of the // License is located at // // http://aws.amazon.com/apache2.0/ // // or in the "license" file accompanying this file. This file 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 plugin import ( "github.com/aws/amazon-vpc-cni-plugins/network/eni" "github.com/aws/amazon-vpc-cni-plugins/plugins/vpc-bridge/config" "github.com/aws/amazon-vpc-cni-plugins/plugins/vpc-bridge/network" log "github.com/cihub/seelog" cniSkel "github.com/containernetworking/cni/pkg/skel" cniTypes "github.com/containernetworking/cni/pkg/types" cniTypesCurrent "github.com/containernetworking/cni/pkg/types/100" ) // Add is the CNI ADD command handler. func (plugin *Plugin) Add(args *cniSkel.CmdArgs) error { // Parse network configuration. netConfig, err := config.New(args, true) if err != nil { log.Errorf("Failed to parse netconfig from args: %v.", err) return err } log.Infof("Executing ADD with netconfig: %+v ContainerID:%v Netns:%v IfName:%v Args:%v.", netConfig, args.ContainerID, args.Netns, args.IfName, args.Args) // Find the ENI. sharedENI, err := eni.NewENI(netConfig.ENIName, netConfig.ENIMACAddress) if err != nil { log.Errorf("Failed to find ENI %s: %v.", netConfig.ENIName, err) return err } // Find the ENI link. err = sharedENI.AttachToLink() if err != nil { log.Errorf("Failed to find ENI link: %v.", err) return err } // Call the operating system specific network builder. nb := plugin.nb // Find or create the container network for the shared ENI. nw := network.Network{ Name: netConfig.Name, BridgeType: netConfig.BridgeType, BridgeNetNSPath: netConfig.BridgeNetNSPath, SharedENI: sharedENI, ENIIPAddresses: netConfig.ENIIPAddresses, GatewayIPAddress: netConfig.GatewayIPAddress, VPCCIDRs: netConfig.VPCCIDRs, DNSServers: netConfig.DNS.Nameservers, DNSSuffixSearchList: netConfig.DNS.Search, } if netConfig.Kubernetes != nil { nw.ServiceCIDR = netConfig.Kubernetes.ServiceCIDR } // Below creates a bridge but doesn't require attaching any IPs given it is L3 bridge not L2 err = nb.FindOrCreateNetwork(&nw) if err != nil { log.Errorf("Failed to create network: %v.", err) return err } // Find or create the container endpoint on the network. ep := network.Endpoint{ ContainerID: args.ContainerID, NetNSName: args.Netns, IfName: args.IfName, IfType: netConfig.InterfaceType, TapUserID: netConfig.TapUserID, IPAddresses: netConfig.IPAddresses, PortMappings: netConfig.PortMappings, } err = nb.FindOrCreateEndpoint(&nw, &ep) if err != nil { log.Errorf("Failed to create endpoint: %v.", err) return err } // Generate CNI result. result := &cniTypesCurrent.Result{ Interfaces: []*cniTypesCurrent.Interface{ { Name: args.IfName, Mac: ep.MACAddress.String(), Sandbox: args.Netns, }, }, } // Populate an IPConfig entry for each IP address. for _, ipAddr := range netConfig.IPAddresses { ipCfg := &cniTypesCurrent.IPConfig{ Interface: cniTypesCurrent.Int(0), Address: ipAddr, } if ipAddr.IP.To4() != nil { ipCfg.Gateway = netConfig.GatewayIPAddress } else { // Kubernetes doesn't implement dual-stack behavior properly. It defaults to IPv4 if // both an IPv4 and IPv6 address are present. Work around that by reporting only the // first IPv6 address in dual-stack setups. if netConfig.Kubernetes != nil { result.IPs = []*cniTypesCurrent.IPConfig{ipCfg} break } } result.IPs = append(result.IPs, ipCfg) } // Output CNI result. log.Infof("Writing CNI result to stdout: %+v", result) err = cniTypes.PrintResult(result, netConfig.CNIVersion) if err != nil { log.Errorf("Failed to print result for CNI ADD command: %v", err) } return err } // Del is the CNI DEL command handler. func (plugin *Plugin) Del(args *cniSkel.CmdArgs) error { // Parse network configuration. netConfig, err := config.New(args, false) if err != nil { log.Errorf("Failed to parse netconfig from args: %v.", err) return err } log.Infof("Executing DEL with netconfig: %+v ContainerID:%v Netns:%v IfName:%v Args:%v.", netConfig, args.ContainerID, args.Netns, args.IfName, args.Args) // Find the ENI. sharedENI, err := eni.NewENI(netConfig.ENIName, netConfig.ENIMACAddress) if err != nil { log.Errorf("Failed to find ENI %s: %v.", netConfig.ENIName, err) return err } // Find the ENI link. err = sharedENI.AttachToLink() if err != nil { log.Errorf("Failed to find ENI link: %v.", err) return err } // Call operating system specific handler. nb := plugin.nb nw := network.Network{ Name: netConfig.Name, BridgeType: netConfig.BridgeType, BridgeNetNSPath: netConfig.BridgeNetNSPath, SharedENI: sharedENI, } ep := network.Endpoint{ ContainerID: args.ContainerID, NetNSName: args.Netns, IfName: args.IfName, IfType: netConfig.InterfaceType, TapUserID: netConfig.TapUserID, IPAddresses: netConfig.IPAddresses, } err = nb.DeleteEndpoint(&nw, &ep) if err != nil { // DEL is best-effort. Log and ignore the failure. log.Errorf("Failed to delete endpoint, ignoring: %v", err) } return nil }