diff --git a/pkg/encapsulation/cilium.go b/pkg/encapsulation/cilium.go index 93b133633..a3d5be4d0 100644 --- a/pkg/encapsulation/cilium.go +++ b/pkg/encapsulation/cilium.go @@ -18,15 +18,24 @@ import ( "fmt" "net" + "github.com/vishvananda/netlink" + "github.com/cozystack/kilo/pkg/iproute" "github.com/cozystack/kilo/pkg/iptables" ) -const ciliumHostIface = "cilium_host" +const ( + ciliumHostIface = "cilium_host" + // ciliumTunlIface is the kernel's default IPIP tunnel (tunl0) renamed + // by Cilium when enable-ipip-termination is active. Unlike cilium_ipip4, + // which is receive-only (for DSR), cilium_tunl supports both TX and RX. + ciliumTunlIface = "cilium_tunl" +) type cilium struct { - iface int - strategy Strategy + iface int + strategy Strategy + ownsTunnel bool } // NewCilium returns an encapsulator that uses IPIP tunnels @@ -36,7 +45,11 @@ func NewCilium(strategy Strategy) Encapsulator { } // CleanUp will remove any created IPIP devices. +// If the tunnel is owned by Cilium, skip removal. func (c *cilium) CleanUp() error { + if !c.ownsTunnel { + return nil + } if err := iproute.DeleteAddresses(c.iface); err != nil { return err } @@ -79,8 +92,17 @@ func (c *cilium) Index() int { } // Init initializes the IPIP tunnel interface. +// When Cilium's enable-ipip-termination is active, it renames the kernel's +// tunl0 to cilium_tunl and creates a receive-only cilium_ipip4 device. +// We use cilium_tunl because it supports both sending and receiving IPIP +// traffic, whereas cilium_ipip4 only handles incoming packets (DSR). func (c *cilium) Init(base int) error { - iface, err := iproute.NewIPIP(base) + if link, err := netlink.LinkByName(ciliumTunlIface); err == nil { + c.iface = link.Attrs().Index + c.ownsTunnel = false + return nil + } + iface, err := iproute.NewIPIPWithName(base, ciliumTunlIface) if err != nil { return fmt.Errorf("failed to create tunnel interface: %v", err) } @@ -88,6 +110,7 @@ func (c *cilium) Init(base int) error { return fmt.Errorf("failed to set tunnel interface up: %v", err) } c.iface = iface + c.ownsTunnel = true return nil } diff --git a/pkg/iproute/ipip.go b/pkg/iproute/ipip.go index 7f0761601..9c51f8161 100644 --- a/pkg/iproute/ipip.go +++ b/pkg/iproute/ipip.go @@ -24,17 +24,23 @@ import ( ) const ( - ipipHeaderSize = 20 - tunnelName = "tunl0" + ipipHeaderSize = 20 + DefaultTunnelName = "tunl0" ) -// NewIPIP creates an IPIP interface using the base interface +// NewIPIP creates an IPIP interface named tunl0 using the base interface // to derive the tunnel's MTU. func NewIPIP(baseIndex int) (int, error) { - link, err := netlink.LinkByName(tunnelName) + return NewIPIPWithName(baseIndex, DefaultTunnelName) +} + +// NewIPIPWithName creates a named IPIP interface using the base interface +// to derive the tunnel's MTU. +func NewIPIPWithName(baseIndex int, name string) (int, error) { + link, err := netlink.LinkByName(name) if err != nil { // If we failed to find the tunnel, then it probably simply does not exist. - cmd := exec.Command("ip", "tunnel", "add", tunnelName, "mode", "ipip") + cmd := exec.Command("ip", "tunnel", "add", name, "mode", "ipip") var stderr bytes.Buffer cmd.Stderr = &stderr // Sometimes creating a tunnel returns the error "File exists," @@ -42,7 +48,7 @@ func NewIPIP(baseIndex int) (int, error) { if err := cmd.Run(); err != nil && !strings.Contains(stderr.String(), "File exists") { return 0, fmt.Errorf("failed to create IPIP tunnel: %s", stderr.String()) } - link, err = netlink.LinkByName(tunnelName) + link, err = netlink.LinkByName(name) if err != nil { return 0, fmt.Errorf("failed to get tunnel device: %v", err) }