func()

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
}