in cmd/egress-cni-plugin/egressContext.go [103:197]
func (ec *egressContext) setupContainerVethV4() (*current.Interface, *current.Interface, error) {
// The IPAM result will be something like IP=192.168.3.5/24, GW=192.168.3.1.
// What we want is really a point-to-point link but veth does not support IFF_POINTTOPOINT.
// Next best thing would be to let it ARP but set interface to 192.168.3.5/32 and
// add a route like "192.168.3.0/24 via 192.168.3.1 dev $ifName".
// Unfortunately that won't work as the GW will be outside the interface's subnet.
// Our solution is to configure the interface with 192.168.3.5/24, then delete the
// "192.168.3.0/24 dev $ifName" route that was automatically added. Then we add
// "192.168.3.1/32 dev $ifName" and "192.168.3.0/24 via 192.168.3.1 dev $ifName".
// In other words we force all traffic to ARP via the gateway except for GW itself.
hostInterface := ¤t.Interface{}
containerInterface := ¤t.Interface{}
err := ec.Ns.WithNetNSPath(ec.NsPath, func(hostNS ns.NetNS) error {
// Empty veth MAC is passed
hostVeth, contVeth0, err := ec.Veth.Setup(ec.NetConf.IfName, ec.Mtu, "", hostNS)
if err != nil {
return err
}
hostInterface.Name = hostVeth.Name
hostInterface.Mac = hostVeth.HardwareAddr.String()
containerInterface.Name = contVeth0.Name
containerInterface.Mac = contVeth0.HardwareAddr.String()
containerInterface.Sandbox = ec.NsPath
for _, ipc := range ec.TmpResult.IPs {
// All addresses apply to the container veth interface
ipc.Interface = current.Int(1)
}
ec.TmpResult.Interfaces = []*current.Interface{hostInterface, containerInterface}
if err = ec.Ipam.ConfigureIface(ec.NetConf.IfName, ec.TmpResult); err != nil {
return err
}
contVeth, err := ec.Link.LinkByName(ec.NetConf.IfName)
if err != nil {
return fmt.Errorf("failed to look up %q: %v", ec.NetConf.IfName, err)
}
for _, ipc := range ec.TmpResult.IPs {
// Delete the route that was automatically added
route := netlink.Route{
LinkIndex: contVeth.Attrs().Index,
Dst: &net.IPNet{
IP: ipc.Address.IP.Mask(ipc.Address.Mask),
Mask: ipc.Address.Mask,
},
Scope: netlink.SCOPE_NOWHERE,
}
if err := ec.Link.RouteDel(&route); err != nil {
return fmt.Errorf("failed to delete route %v: %v", route, err)
}
addrBits := 128
if ipc.Address.IP.To4() != nil {
addrBits = 32
}
for _, r := range []netlink.Route{
{
LinkIndex: contVeth.Attrs().Index,
Dst: &net.IPNet{
IP: ipc.Gateway,
Mask: net.CIDRMask(addrBits, addrBits),
},
Scope: netlink.SCOPE_LINK,
Src: ipc.Address.IP,
},
{
LinkIndex: contVeth.Attrs().Index,
Dst: &net.IPNet{
IP: ipc.Address.IP.Mask(ipc.Address.Mask),
Mask: ipc.Address.Mask,
},
Scope: netlink.SCOPE_UNIVERSE,
Gw: ipc.Gateway,
Src: ipc.Address.IP,
},
} {
if err := ec.Link.RouteAdd(&r); err != nil {
return fmt.Errorf("failed to add route %v: %v", r, err)
}
}
}
return nil
})
if err != nil {
return nil, nil, err
}
return hostInterface, containerInterface, nil
}