in plugins/vpc-shared-eni/network/bridge_linux.go [108:240]
func (nb *BridgeBuilder) FindOrCreateEndpoint(nw *Network, ep *Endpoint) error {
// Derive endpoint names.
cid := ep.ContainerID
if len(cid) > 8 {
cid = cid[:8]
}
vethLinkName := fmt.Sprintf(vethLinkNameFormat, cid)
vethPeerName := vethLinkName + "-2"
// Find the target network namespace.
log.Infof("Searching for netns %s.", ep.NetNSName)
targetNetNS, err := netns.GetNetNS(ep.NetNSName)
if err != nil {
log.Errorf("Failed to find netns %s: %v.", ep.NetNSName, err)
return err
}
// Connect the bridge to the target network namespace with a veth pair.
err = nb.createVethPair(nw.BridgeIndex, targetNetNS, vethLinkName, vethPeerName)
if err != nil {
log.Errorf("Failed to create veth pair %s: %v.", vethLinkName, err)
return err
}
var epIPAddresses []net.IPNet
var gatewayIPv4Address net.IP
var gatewayIPv6Address net.IP
var gatewayIPAddresses []net.IP
var gatewayMACAddress net.HardwareAddr
if nw.BridgeType == config.BridgeTypeL3 {
// Configure the endpoint to relay the default gateway traffic to the on-link bridge.
bridgeLink, err := netlink.LinkByIndex(nw.BridgeIndex)
if err == nil {
gatewayMACAddress = bridgeLink.Attrs().HardwareAddr
}
for _, ipAddr := range ep.IPAddresses {
// Route ingress traffic for this IP address to the bridge.
dst := ipAddr
_, maskSize := dst.Mask.Size()
dst.Mask = net.CIDRMask(maskSize, maskSize)
route := &netlink.Route{
LinkIndex: nw.BridgeIndex,
Scope: netlink.SCOPE_LINK,
Dst: &dst,
}
log.Infof("Adding IP route %+v to bridge.", route)
err = netlink.RouteAdd(route)
if err != nil && !os.IsExist(err) {
log.Errorf("Failed to add IP route %+v: %v.", route, err)
return err
}
// The endpoint IP addresses and default gateways are set differently based on the
// address family.
if ipAddr.IP.To4() != nil {
// Assign the endpoint IPv4 address as-is with the actual subnet prefix length.
epIPAddresses = append(epIPAddresses, ipAddr)
// For IPv4, the actual VPC subnet default gateway is used as the default gateway.
// If a gateway address was not specified, derive it from the endpoint's IP address.
if gatewayIPv4Address == nil {
if nw.GatewayIPAddress == nil {
subnet, _ := vpc.NewSubnet(vpc.GetSubnetPrefix(&ipAddr))
gatewayIPv4Address = subnet.Gateways[0]
} else {
gatewayIPv4Address = nw.GatewayIPAddress
}
gatewayIPAddresses = append(gatewayIPAddresses, gatewayIPv4Address)
}
} else {
// Set the endpoint IPv6 address prefix length to address size. This essentially
// disables neighbor discovery and forces the endpoint to send all egress traffic
// to the default gateway.
addr := ipAddr
_, maskSize := addr.Mask.Size()
addr.Mask = net.CIDRMask(maskSize, maskSize)
epIPAddresses = append(epIPAddresses, addr)
// For IPv6, the link-local address of the bridge is used as the default gateway.
if gatewayIPv6Address == nil {
addrs, _ := netlink.AddrList(bridgeLink, netlink.FAMILY_V6)
for _, addr := range addrs {
if netlink.Scope(addr.Scope) == netlink.SCOPE_LINK {
gatewayIPv6Address = addr.IP
}
}
gatewayIPAddresses = append(gatewayIPAddresses, gatewayIPv6Address)
}
}
}
}
// Setup the target network namespace.
err = targetNetNS.Run(func() error {
ep.MACAddress, err = nb.setupTargetNetNS(
vethPeerName, ep.IfType, ep.TapUserID, ep.IfName, epIPAddresses,
gatewayIPAddresses, gatewayMACAddress)
return err
})
if err != nil {
log.Errorf("Failed to setup target netns: %v.", err)
return err
}
if nw.BridgeType == config.BridgeTypeL2 {
// Set MAC DNAT rule for translating ingress IP datagrams arriving on the shared ENI
// sent to the endpoint IP address to endpoint MAC address.
err = ebtables.NAT.Append(
ebtables.PreRouting,
&ebtables.Rule{
Protocol: "IPv4",
In: nw.SharedENI.GetLinkName(),
Match: &ebtables.IPv4Match{
Dst: ep.IPAddresses[0].IP,
},
Target: &ebtables.DNATTarget{
ToDst: ep.MACAddress,
Target: ebtables.Accept,
},
},
)
if err != nil {
log.Errorf("Failed to append DNAT rule for veth link %s: %v.", vethLinkName, err)
}
}
return nil
}