in plugins/vpc-shared-eni/network/bridge_linux.go [318:577]
func (nb *BridgeBuilder) createBridge(
bridgeName string,
bridgeType string,
sharedENI *eni.ENI,
ipAddresses []net.IPNet) (int, error) {
// Check if the bridge already exists.
bridge, err := net.InterfaceByName(bridgeName)
if err == nil {
log.Infof("Found existing bridge %s.", bridgeName)
return bridge.Index, nil
}
// Create the bridge link.
la := netlink.NewLinkAttrs()
la.Name = bridgeName
la.MTU = vpc.JumboFrameMTU
bridgeLink := &netlink.Bridge{LinkAttrs: la}
log.Infof("Creating bridge link %+v.", bridgeLink)
err = netlink.LinkAdd(bridgeLink)
if err != nil {
log.Errorf("Failed to create bridge link: %v.", err)
return 0, err
}
// If anything fails during setup, clean up the bridge so that the next call starts clean.
defer func() {
if err != nil {
log.Infof("Cleaning up bridge on error: %v.", err)
cleanupErr := nb.deleteBridge(bridgeName, bridgeType, sharedENI)
if cleanupErr != nil {
log.Errorf("Failed to cleanup bridge: %v.", cleanupErr)
}
}
}()
// Connect a dummy link to the bridge.
// Bridge inherits the smallest MTU of links connected to its ports.
dummyName := fmt.Sprintf(dummyNameFormat, bridgeName)
la = netlink.NewLinkAttrs()
la.Name = dummyName
la.MTU = vpc.JumboFrameMTU
la.MasterIndex = bridgeLink.Attrs().Index
dummyLink := &netlink.Dummy{LinkAttrs: la}
log.Infof("Creating dummy link %+v.", dummyLink)
err = netlink.LinkAdd(dummyLink)
if err != nil {
log.Errorf("Failed to create dummy link: %v.", err)
return 0, err
}
// Set dummy link operational state up.
err = netlink.LinkSetUp(dummyLink)
if err != nil {
log.Errorf("Failed to set dummy link state up: %v.", err)
return 0, err
}
// Set bridge MAC address to dummy's MAC address.
// Bridge by default inherits the smallest of the MAC addresses of interfaces (veth in this case) connected
// to its ports. Explicitly setting a static address prevents the bridge from dynamically changing its address
// as interfaces join and leave the bridge.
link, err := netlink.LinkByName(dummyName)
if err != nil {
log.Errorf("Failed to query dummy link: %v.", err)
return 0, err
}
err = netlink.LinkSetHardwareAddr(bridgeLink, link.Attrs().HardwareAddr)
if err != nil {
log.Errorf("Failed to set bridge link MAC address: %v.", err)
return 0, err
}
// Setup bridge layer2 configuration.
if bridgeType == config.BridgeTypeL2 {
// Remove IP address from shared ENI link.
ipAddress := &ipAddresses[0]
log.Infof("Removing IP address %v from ENI link %s.", ipAddress, sharedENI)
la = netlink.NewLinkAttrs()
la.Name = sharedENI.GetLinkName()
eniLink := &netlink.Dummy{LinkAttrs: la}
address := &netlink.Addr{IPNet: ipAddress}
err = netlink.AddrDel(eniLink, address)
if err != nil {
log.Errorf("Failed to remove IP address from ENI link %v: %v.", eniLink, err)
return 0, err
}
// Append a MAC DNAT rule to broadcast ARP replies.
broadcastMACAddr, _ := net.ParseMAC("ff:ff:ff:ff:ff:ff")
err = ebtables.NAT.Append(
ebtables.PreRouting,
&ebtables.Rule{
Protocol: "ARP",
In: sharedENI.GetLinkName(),
Match: &ebtables.ARPMatch{
Op: "Reply",
},
Target: &ebtables.DNATTarget{
ToDst: broadcastMACAddr,
Target: ebtables.Accept,
},
},
)
if err != nil {
log.Errorf("Failed to append DNAT rule for ENI link %s: %v.", sharedENI, err)
return 0, err
}
// Append a MAC SNAT rule for unicast frames egress shared ENI to shared ENI's MAC address.
err = ebtables.NAT.Append(
ebtables.PostRouting,
&ebtables.Rule{
Out: sharedENI.GetLinkName(),
SrcType: "unicast",
Target: &ebtables.SNATTarget{
ToSrc: sharedENI.GetMACAddress(),
ARP: true,
Target: ebtables.Accept,
},
},
)
if err != nil {
log.Errorf("Failed to append SNAT rule for ENI link %s: %v.", sharedENI, err)
return 0, err
}
// Set ENI link operational state down.
err = sharedENI.SetOpState(false)
if err != nil {
log.Errorf("Failed to set ENI link %s state: %v.", sharedENI, err)
return 0, err
}
// Set the ENI link MTU.
// This is necessary in case the ENI was not configured by DHCP.
log.Infof("Setting ENI link %s MTU to %d octets.", sharedENI, vpc.JumboFrameMTU)
err = sharedENI.SetLinkMTU(vpc.JumboFrameMTU)
if err != nil {
log.Errorf("Failed to set ENI link MTU: %v.", err)
return 0, err
}
// Connect ENI link to the bridge.
log.Infof("Setting ENI link %s master to %s.", sharedENI, bridgeName)
la = netlink.NewLinkAttrs()
la.Name = sharedENI.GetLinkName()
eniLink = &netlink.Dummy{LinkAttrs: la}
err = netlink.LinkSetMaster(eniLink, bridgeLink)
if err != nil {
log.Errorf("Failed to set ENI link master: %v", err)
return 0, err
}
// Set ENI link operational state up.
err = sharedENI.SetOpState(true)
if err != nil {
log.Errorf("Failed to set ENI link %s state: %v.", sharedENI, err)
return 0, err
}
}
// Set bridge link operational state up.
err = netlink.LinkSetUp(bridgeLink)
if err != nil {
log.Errorf("Failed to set bridge link state up: %v.", err)
return 0, err
}
if bridgeType == config.BridgeTypeL2 {
// In layer2 configuration, the bridge inherits shared ENI's IP address and default route.
// Frames are switched between veth pairs and the shared ENI.
// Assign IP address to bridge.
ipAddress := &ipAddresses[0]
log.Infof("Assigning IP address %v to bridge link %s.", ipAddress, bridgeName)
address := &netlink.Addr{IPNet: ipAddress}
err = netlink.AddrAdd(bridgeLink, address)
if err != nil {
log.Errorf("Failed to assign IP address to bridge link %v: %v.", bridgeName, err)
return 0, err
}
// Add default route to subnet gateway via bridge.
subnet, err := vpc.NewSubnet(vpc.GetSubnetPrefix(ipAddress))
if err != nil {
log.Errorf("Failed to parse VPC subnet for %s: %v.", ipAddress, err)
return 0, err
}
route := &netlink.Route{
Gw: subnet.Gateways[0],
LinkIndex: bridgeLink.Attrs().Index,
}
log.Infof("Adding default IP route %+v.", route)
err = netlink.RouteAdd(route)
if err != nil {
log.Errorf("Failed to add IP route %+v: %v.", route, err)
return 0, err
}
} else {
// In layer3 configuration, the IP address and default route remain on the shared ENI.
// IP datagrams are routed between the bridge and the shared ENI.
if vpc.ListContainsIPv4Address(ipAddresses) {
// Bridge proxies ARP requests originating from veth pairs to the VPC.
log.Infof("Enabling IPv4 proxy ARP on %s.", bridgeName)
err = ipcfg.SetIPv4ProxyARP(bridgeName, 1)
if err != nil {
log.Errorf("Failed to enable IPv4 proxy ARP on %s: %v.", bridgeName, err)
return 0, err
}
// Enable IPv4 forwarding on the bridge and shared ENI, so that IP datagrams can be
// routed between them.
log.Infof("Enabling IPv4 forwarding on %s.", bridgeName)
err = ipcfg.SetIPv4Forwarding(bridgeName, 1)
if err != nil {
log.Errorf("Failed to enable IPv4 forwarding on %s: %v.", bridgeName, err)
return 0, err
}
log.Infof("Enabling IPv4 forwarding on %s.", sharedENI.GetLinkName())
err = ipcfg.SetIPv4Forwarding(sharedENI.GetLinkName(), 1)
if err != nil {
log.Errorf("Failed to enable IPv4 forwarding on %s: %v.", sharedENI.GetLinkName(), err)
return 0, err
}
}
if vpc.ListContainsIPv6Address(ipAddresses) {
// Eanble IPv6 forwarding on all interfaces.
log.Infof("Enabling IPv6 forwarding on all.")
err = ipcfg.SetIPv6Forwarding("all", 1)
if err != nil {
log.Errorf("Failed to enable IPv6 forwarding on all: %v.", err)
return 0, err
}
log.Infof("Enabling IPv6 accept RA on %s.", bridgeName)
err = ipcfg.SetIPv6AcceptRA(bridgeName, 2)
if err != nil {
log.Errorf("Failed to enable IPv6 accept RA on %s: %v.", bridgeName, err)
return 0, err
}
log.Infof("Enabling IPv6 accept RA on %s.", sharedENI.GetLinkName())
err = ipcfg.SetIPv6AcceptRA(sharedENI.GetLinkName(), 2)
if err != nil {
log.Errorf("Failed to enable IPv6 accept RA on %s: %v.", sharedENI.GetLinkName(), err)
return 0, err
}
}
}
return bridgeLink.Attrs().Index, nil
}