33564a0c03
The forwarding database (fdb) of Linux VXLAN links are restricted to entries with destination VXLAN tunnel endpoint (VTEP) address of a single address family. Which address family is permitted is set when the link is created and cannot be modified. The overlay network driver creates VXLAN links such that the kernel only allows fdb entries to be created with IPv4 destination VTEP addresses. If the Swarm is configured with IPv6 advertise addresses, creating fdb entries for remote peers fails with EAFNOSUPPORT (address family not supported by protocol). Make overlay networks functional over IPv6 transport by configuring the VXLAN links for IPv6 VTEPs if the local node's advertise address is an IPv6 address. Make encrypted overlay networks secure over IPv6 transport by applying the iptables rules to the ip6tables when appropriate. Signed-off-by: Cory Snider <csnider@mirantis.com>
140 lines
3.8 KiB
Go
140 lines
3.8 KiB
Go
//go:build linux
|
|
|
|
package overlay
|
|
|
|
import (
|
|
"context"
|
|
"fmt"
|
|
"net"
|
|
"syscall"
|
|
|
|
"github.com/containerd/log"
|
|
"github.com/docker/docker/libnetwork/drivers/overlay/overlayutils"
|
|
"github.com/docker/docker/libnetwork/netutils"
|
|
"github.com/docker/docker/libnetwork/ns"
|
|
"github.com/vishvananda/netlink"
|
|
"github.com/vishvananda/netns"
|
|
)
|
|
|
|
var soTimeout = ns.NetlinkSocketsTimeout
|
|
|
|
func validateID(nid, eid string) error {
|
|
if nid == "" {
|
|
return fmt.Errorf("invalid network id")
|
|
}
|
|
|
|
if eid == "" {
|
|
return fmt.Errorf("invalid endpoint id")
|
|
}
|
|
|
|
return nil
|
|
}
|
|
|
|
func createVethPair() (string, string, error) {
|
|
nlh := ns.NlHandle()
|
|
|
|
// Generate a name for what will be the host side pipe interface
|
|
name1, err := netutils.GenerateIfaceName(nlh, vethPrefix, vethLen)
|
|
if err != nil {
|
|
return "", "", fmt.Errorf("error generating veth name1: %v", err)
|
|
}
|
|
|
|
// Generate a name for what will be the sandbox side pipe interface
|
|
name2, err := netutils.GenerateIfaceName(nlh, vethPrefix, vethLen)
|
|
if err != nil {
|
|
return "", "", fmt.Errorf("error generating veth name2: %v", err)
|
|
}
|
|
|
|
// Generate and add the interface pipe host <-> sandbox
|
|
veth := &netlink.Veth{
|
|
LinkAttrs: netlink.LinkAttrs{Name: name1, TxQLen: 0},
|
|
PeerName: name2,
|
|
}
|
|
if err := nlh.LinkAdd(veth); err != nil {
|
|
return "", "", fmt.Errorf("error creating veth pair: %v", err)
|
|
}
|
|
|
|
return name1, name2, nil
|
|
}
|
|
|
|
func createVxlan(name string, vni uint32, mtu int, vtepIPv6 bool) error {
|
|
vxlan := &netlink.Vxlan{
|
|
LinkAttrs: netlink.LinkAttrs{Name: name, MTU: mtu},
|
|
VxlanId: int(vni),
|
|
Learning: true,
|
|
Port: int(overlayutils.VXLANUDPPort()),
|
|
Proxy: true,
|
|
L3miss: true,
|
|
L2miss: true,
|
|
}
|
|
|
|
// The kernel restricts the destination VTEP (virtual tunnel endpoint) in
|
|
// VXLAN forwarding database entries to a single address family, defaulting
|
|
// to IPv4 unless either an IPv6 group or default remote destination address
|
|
// is configured when the VXLAN link is created.
|
|
//
|
|
// Set up the VXLAN link for IPv6 destination addresses by setting the VXLAN
|
|
// group address to the IPv6 unspecified address, like iproute2.
|
|
// https://github.com/iproute2/iproute2/commit/97d564b90ccb1e4a3c756d9caae161f55b2b63a2
|
|
// https://patchwork.ozlabs.org/project/netdev/patch/20180917171325.GA2660@localhost.localdomain/
|
|
if vtepIPv6 {
|
|
vxlan.Group = net.IPv6unspecified
|
|
}
|
|
|
|
if err := ns.NlHandle().LinkAdd(vxlan); err != nil {
|
|
return fmt.Errorf("error creating vxlan interface: %v", err)
|
|
}
|
|
|
|
return nil
|
|
}
|
|
|
|
func deleteInterface(name string) error {
|
|
link, err := ns.NlHandle().LinkByName(name)
|
|
if err != nil {
|
|
return fmt.Errorf("failed to find interface with name %s: %v", name, err)
|
|
}
|
|
|
|
if err := ns.NlHandle().LinkDel(link); err != nil {
|
|
return fmt.Errorf("error deleting interface with name %s: %v", name, err)
|
|
}
|
|
|
|
return nil
|
|
}
|
|
|
|
func deleteVxlanByVNI(path string, vni uint32) error {
|
|
nlh := ns.NlHandle()
|
|
if path != "" {
|
|
ns, err := netns.GetFromPath(path)
|
|
if err != nil {
|
|
return fmt.Errorf("failed to get ns handle for %s: %v", path, err)
|
|
}
|
|
defer ns.Close()
|
|
|
|
nlh, err = netlink.NewHandleAt(ns, syscall.NETLINK_ROUTE)
|
|
if err != nil {
|
|
return fmt.Errorf("failed to get netlink handle for ns %s: %v", path, err)
|
|
}
|
|
defer nlh.Close()
|
|
err = nlh.SetSocketTimeout(soTimeout)
|
|
if err != nil {
|
|
log.G(context.TODO()).Warnf("Failed to set the timeout on the netlink handle sockets for vxlan deletion: %v", err)
|
|
}
|
|
}
|
|
|
|
links, err := nlh.LinkList()
|
|
if err != nil {
|
|
return fmt.Errorf("failed to list interfaces while deleting vxlan interface by vni: %v", err)
|
|
}
|
|
|
|
for _, l := range links {
|
|
if l.Type() == "vxlan" && (vni == 0 || l.(*netlink.Vxlan).VxlanId == int(vni)) {
|
|
err = nlh.LinkDel(l)
|
|
if err != nil {
|
|
return fmt.Errorf("error deleting vxlan interface with id %d: %v", vni, err)
|
|
}
|
|
return nil
|
|
}
|
|
}
|
|
|
|
return fmt.Errorf("could not find a vxlan interface to delete with id %d", vni)
|
|
}
|