Skip to content

Commit

Permalink
Add Linux support to vpc-eni plugin (#101)
Browse files Browse the repository at this point in the history
  • Loading branch information
ofiliz committed Jul 25, 2023
1 parent 0425e5b commit 58a0e2b
Show file tree
Hide file tree
Showing 6 changed files with 159 additions and 3 deletions.
9 changes: 8 additions & 1 deletion network/eni/eni_linux.go
Original file line number Diff line number Diff line change
Expand Up @@ -63,11 +63,18 @@ func (eni *ENI) SetOpState(up bool) error {
}

// SetNetNS sets the network namespace of the ENI.
// If ns argument is nil, the ENI is reset to the host network namespace.
func (eni *ENI) SetNetNS(ns netns.NetNS) error {
la := netlink.NewLinkAttrs()
la.Name = eni.linkName
link := &netlink.Dummy{LinkAttrs: la}
return netlink.LinkSetNsFd(link, int(ns.GetFd()))
if ns != nil {
// Move the ENI to the given network namespace.
return netlink.LinkSetNsFd(link, int(ns.GetFd()))
} else {
// PID 1 init is running in the host network namespace.
return netlink.LinkSetNsPid(link, 1)
}
}

// SetMACAddress sets the MAC address of the ENI.
Expand Down
3 changes: 3 additions & 0 deletions plugins/vpc-eni/config/netconfig.go
Original file line number Diff line number Diff line change
Expand Up @@ -32,6 +32,7 @@ type NetConfig struct {
ENIMACAddress net.HardwareAddr
ENIIPAddresses []net.IPNet
GatewayIPAddresses []net.IP
OpState bool
UseExistingNetwork bool
BlockIMDS bool
}
Expand All @@ -43,6 +44,7 @@ type netConfigJSON struct {
ENIMACAddress string `json:"eniMACAddress"`
ENIIPAddresses []string `json:"eniIPAddresses"`
GatewayIPAddresses []string `json:"gatewayIPAddresses"`
OpStateDown bool `json:"opStateDown"`
UseExistingNetwork bool `json:"useExistingNetwork"`
BlockIMDS bool `json:"blockInstanceMetadata"`
}
Expand Down Expand Up @@ -79,6 +81,7 @@ func New(args *cniSkel.CmdArgs) (*NetConfig, error) {
netConfig := NetConfig{
NetConf: config.NetConf,
ENIName: config.ENIName,
OpState: !config.OpStateDown,
UseExistingNetwork: config.UseExistingNetwork,
BlockIMDS: config.BlockIMDS,
}
Expand Down
2 changes: 2 additions & 0 deletions plugins/vpc-eni/network/network.go
Original file line number Diff line number Diff line change
Expand Up @@ -42,7 +42,9 @@ type Network struct {
type Endpoint struct {
ContainerID string
NetNSName string
ENIName string
MACAddress net.HardwareAddr
IPAddresses []net.IPNet
OpState bool
BlockIMDS bool
}
140 changes: 140 additions & 0 deletions plugins/vpc-eni/network/network_linux.go
Original file line number Diff line number Diff line change
Expand Up @@ -13,25 +13,165 @@

package network

import (
"fmt"

"github.com/aws/amazon-vpc-cni-plugins/network/imds"
"github.com/aws/amazon-vpc-cni-plugins/network/netns"

log "github.com/cihub/seelog"
"github.com/vishvananda/netlink"
)

// NetBuilder implements the Builder interface for Linux.
type NetBuilder struct{}

// FindOrCreateNetwork creates a new network.
func (nb *NetBuilder) FindOrCreateNetwork(nw *Network) error {
// Vpc-eni does not need any network-level setup on Linux.
return nil
}

// DeleteNetwork deletes an existing network.
func (nb *NetBuilder) DeleteNetwork(nw *Network) error {
// Vpc-eni does not need any network-level cleanup on Linux.
return nil
}

// FindOrCreateEndpoint creates a new endpoint in the network.
func (nb *NetBuilder) FindOrCreateEndpoint(nw *Network, ep *Endpoint) error {
// Find the network namespace.
log.Infof("Searching for netns %s.", ep.NetNSName)
ns, err := netns.GetNetNS(ep.NetNSName)
if err != nil {
log.Errorf("Failed to find netns %s: %v.", ep.NetNSName, err)
return err
}

eni := nw.ENI
err = eni.AttachToLink()
if err != nil {
log.Errorf("Failed to find ENI %s: %v", eni, err)
return err
}

log.Infof("Moving ENI link %s to netns %s.", eni, ep.NetNSName)
err = eni.SetNetNS(ns)
if err != nil {
log.Errorf("Failed to move eni: %v.", err)
return err
}

// If operational state is down, there is no need to configure anything else.
if !ep.OpState {
return nil
}

// Complete the remaining setup in target network namespace.
err = ns.Run(func() error {
// Rename the ENI link to the requested interface name.
if eni.GetLinkName() != ep.ENIName {
log.Infof("Renaming ENI link %v to %s.", eni, ep.ENIName)
err := eni.SetLinkName(ep.ENIName)
if err != nil {
log.Errorf("Failed to rename ENI link %v: %v.", eni, err)
return err
}
}

// Add a blackhole route for IMDS endpoint if required.
if ep.BlockIMDS {
err = imds.BlockInstanceMetadataEndpoint()
if err != nil {
return err
}
}

// Set ENI IP addresses if specified.
for _, ipAddress := range ep.IPAddresses {
// Assign the IP address.
err = eni.AddIPAddress(&ipAddress)
if err != nil {
log.Errorf("Failed to assign IP address to eni %v: %v.", eni, err)
return err
}
}

log.Infof("Setting ENI link state up.")
err = eni.SetOpState(true)
if err != nil {
log.Errorf("Failed to set link %v state: %v.", eni, err)
return err
}

// Set default gateways if specified.
for _, gatewayIPAddress := range nw.GatewayIPAddresses {
// Add default route via ENI link.
route := &netlink.Route{
Gw: gatewayIPAddress,
LinkIndex: eni.GetLinkIndex(),
}
log.Infof("Adding default IP route %+v.", route)
err = netlink.RouteAdd(route)
if err != nil {
log.Errorf("Failed to add IP route %+v via ENI %v: %v.", route, eni, err)
return err
}
}

return err
})

return nil
}

// DeleteEndpoint deletes an existing endpoint.
func (nb *NetBuilder) DeleteEndpoint(nw *Network, ep *Endpoint) error {
// Search for the target network namespace.
netns, err := netns.GetNetNS(ep.NetNSName)
if err != nil {
// Log and ignore the failure. DEL can be called multiple times and thus must be idempotent.
log.Errorf("Failed to find netns %s, ignoring: %v.", ep.NetNSName, err)
return nil
}

// In target network namespace...
err = netns.Run(func() error {
eni := nw.ENI
err = eni.AttachToLink()
if err != nil {
log.Errorf("Failed to find ENI %s: %v", eni, err)
return err
}

log.Infof("Setting ENI link state down.")
err = eni.SetOpState(false)
if err != nil {
log.Errorf("Failed to set link %v state: %v.", eni, err)
return err
}

// Rename the ENI link to its MAC address to avoid naming conflicts in host netns.
eniName := fmt.Sprintf("ecs%x%x%x", ep.MACAddress[0], ep.MACAddress[1], ep.MACAddress[2])
log.Infof("Renaming ENI link %v to %s.", eni, eniName)
err := eni.SetLinkName(eniName)
if err != nil {
log.Errorf("Failed to rename ENI link %v: %v.", eni, err)
return err
}

log.Infof("Moving ENI link %s to host netns.", eni)
err = eni.SetNetNS(nil)
if err != nil {
log.Errorf("Failed to move eni: %v.", err)
return err
}
return nil
})

if err != nil {
log.Errorf("Failed to set netns to host, ignoring: %v.", err)
}

return nil
}
2 changes: 2 additions & 0 deletions plugins/vpc-eni/plugin/commands.go
Original file line number Diff line number Diff line change
Expand Up @@ -71,8 +71,10 @@ func (plugin *Plugin) Add(args *cniSkel.CmdArgs) error {
ep := network.Endpoint{
ContainerID: args.ContainerID,
NetNSName: args.Netns,
ENIName: args.IfName,
MACAddress: netConfig.ENIMACAddress,
IPAddresses: netConfig.ENIIPAddresses,
OpState: netConfig.OpState,
BlockIMDS: netConfig.BlockIMDS,
}

Expand Down
6 changes: 4 additions & 2 deletions plugins/vpc-eni/vpc-eni.conf
Original file line number Diff line number Diff line change
Expand Up @@ -2,9 +2,11 @@
"cniVersion": "0.3.1",
"name": "vpc",
"type": "vpc-eni",
"eniName": "Ethernet 4",
"eniName": "eth1",
"eniMACAddress": "12:34:56:78:9a:bc",
"eniIPAddresses": ["192.168.1.42/24"],
"gatewayIPAddresses": ["192.168.1.1"],
"useExistingNetwork": "false"
"opStateDown": "false",
"useExistingNetwork": "false",
"blockInstanceMetadata": "false"
}

0 comments on commit 58a0e2b

Please sign in to comment.