github.com/cilium/cilium@v1.16.2/pkg/datapath/tunnel/tunnel.go (about)

     1  // SPDX-License-Identifier: Apache-2.0
     2  // Copyright Authors of Cilium
     3  
     4  package tunnel
     5  
     6  import (
     7  	"fmt"
     8  	"strings"
     9  
    10  	"github.com/cilium/hive/cell"
    11  	"github.com/spf13/pflag"
    12  	"github.com/vishvananda/netlink"
    13  
    14  	dpcfgdef "github.com/cilium/cilium/pkg/datapath/linux/config/defines"
    15  	"github.com/cilium/cilium/pkg/defaults"
    16  )
    17  
    18  // Protocol represents the valid types of encapsulation protocols.
    19  type Protocol string
    20  
    21  const (
    22  	// VXLAN specifies VXLAN encapsulation
    23  	VXLAN Protocol = "vxlan"
    24  
    25  	// Geneve specifies Geneve encapsulation
    26  	Geneve Protocol = "geneve"
    27  
    28  	// Disabled specifies to disable encapsulation
    29  	Disabled Protocol = ""
    30  )
    31  
    32  func (tp Protocol) String() string { return string(tp) }
    33  
    34  func (tp Protocol) toDpID() string {
    35  	switch tp {
    36  	case VXLAN:
    37  		return "1"
    38  	case Geneve:
    39  		return "2"
    40  	default:
    41  		return ""
    42  	}
    43  }
    44  
    45  // Config represents the materialized tunneling configuration to be used,
    46  // depending on the user configuration and optional overrides required by
    47  // additional features.
    48  type Config struct {
    49  	protocol       Protocol
    50  	port           uint16
    51  	deviceName     string
    52  	shouldAdaptMTU bool
    53  }
    54  
    55  type newConfigIn struct {
    56  	cell.In
    57  
    58  	Cfg      userCfg
    59  	Enablers []enabler `group:"request-enable-tunneling"`
    60  }
    61  
    62  func newConfig(in newConfigIn) (Config, error) {
    63  	switch Protocol(in.Cfg.TunnelProtocol) {
    64  	case VXLAN, Geneve:
    65  	default:
    66  		return Config{}, fmt.Errorf("invalid tunnel protocol %q", in.Cfg.TunnelProtocol)
    67  	}
    68  
    69  	cfg := Config{
    70  		protocol: Protocol(in.Cfg.TunnelProtocol),
    71  		port:     in.Cfg.TunnelPort,
    72  	}
    73  
    74  	var enabled bool
    75  	for _, e := range in.Enablers {
    76  		if e.enable {
    77  			enabled = true
    78  			cfg.shouldAdaptMTU = cfg.shouldAdaptMTU || e.needsMTUAdaptation
    79  
    80  			for _, validator := range e.validators {
    81  				if err := validator(cfg.protocol); err != nil {
    82  					return Config{}, err
    83  				}
    84  			}
    85  		}
    86  	}
    87  
    88  	if !enabled {
    89  		return Config{protocol: Disabled}, nil
    90  	}
    91  
    92  	switch cfg.protocol {
    93  	case VXLAN:
    94  		cfg.deviceName = defaults.VxlanDevice
    95  
    96  		if cfg.port == 0 {
    97  			cfg.port = defaults.TunnelPortVXLAN
    98  		}
    99  	case Geneve:
   100  		cfg.deviceName = defaults.GeneveDevice
   101  
   102  		if cfg.port == 0 {
   103  			cfg.port = defaults.TunnelPortGeneve
   104  		}
   105  	}
   106  
   107  	return cfg, nil
   108  }
   109  
   110  // NewTestConfig returns a new TunnelConfig for testing purposes.
   111  func NewTestConfig(proto Protocol) Config {
   112  	cfg := Config{protocol: proto}
   113  
   114  	switch proto {
   115  	case VXLAN:
   116  		cfg.port = defaults.TunnelPortVXLAN
   117  		cfg.deviceName = defaults.VxlanDevice
   118  	case Geneve:
   119  		cfg.port = defaults.TunnelPortGeneve
   120  		cfg.deviceName = defaults.GeneveDevice
   121  	}
   122  
   123  	return cfg
   124  }
   125  
   126  // Protocol returns the enabled tunnel protocol. The tunnel protocol may be
   127  // set to either VXLAN or Geneve even when the primary mode is native routing, in
   128  // case an additional feature (e.g., egress gateway) may request some traffic to
   129  // be routed through a tunnel.
   130  func (cfg Config) Protocol() Protocol { return cfg.protocol }
   131  
   132  // Port returns the port used by the tunnel (0 if disabled).
   133  func (cfg Config) Port() uint16 { return cfg.port }
   134  
   135  // DeviceName returns the name of the tunnel device (empty if disabled).
   136  func (cfg Config) DeviceName() string { return cfg.deviceName }
   137  
   138  // ShouldAdaptMTU returns whether we should adapt the MTU calculation to
   139  // account for encapsulation.
   140  func (cfg Config) ShouldAdaptMTU() bool { return cfg.shouldAdaptMTU }
   141  
   142  func (cfg Config) datapathConfigProvider() (dpcfgdef.NodeOut, dpcfgdef.NodeFnOut) {
   143  	defines := make(dpcfgdef.Map)
   144  	definesFn := func() (dpcfgdef.Map, error) { return nil, nil }
   145  
   146  	if cfg.Protocol() != Disabled {
   147  		defines[fmt.Sprintf("TUNNEL_PROTOCOL_%s", strings.ToUpper(VXLAN.String()))] = VXLAN.toDpID()
   148  		defines[fmt.Sprintf("TUNNEL_PROTOCOL_%s", strings.ToUpper(Geneve.String()))] = Geneve.toDpID()
   149  		defines["TUNNEL_PROTOCOL"] = cfg.Protocol().toDpID()
   150  		defines["TUNNEL_PORT"] = fmt.Sprintf("%d", cfg.Port())
   151  
   152  		definesFn = func() (dpcfgdef.Map, error) {
   153  			tunnelDev, err := netlink.LinkByName(cfg.DeviceName())
   154  			if err != nil {
   155  				return nil, fmt.Errorf("failed to retrieve device info for %q: %w", cfg.DeviceName(), err)
   156  			}
   157  
   158  			return dpcfgdef.Map{
   159  				"ENCAP_IFINDEX": fmt.Sprintf("%d", tunnelDev.Attrs().Index),
   160  			}, nil
   161  		}
   162  	}
   163  
   164  	return dpcfgdef.NodeOut{NodeDefines: defines}, dpcfgdef.NewNodeFnOut(definesFn)
   165  }
   166  
   167  // EnablerOut allows requesting to enable tunneling functionalities.
   168  type EnablerOut struct {
   169  	cell.Out
   170  	Enabler enabler `group:"request-enable-tunneling"`
   171  }
   172  
   173  // NewEnabler returns an object to be injected through hive to request to
   174  // enable tunneling functionalities. Extra options are meaningful only when
   175  // enable is set to true, and are ignored otherwise.
   176  func NewEnabler(enable bool, opts ...enablerOpt) EnablerOut {
   177  	enabler := enabler{enable: enable, needsMTUAdaptation: enable}
   178  
   179  	for _, opt := range opts {
   180  		opt(&enabler)
   181  	}
   182  
   183  	return EnablerOut{Enabler: enabler}
   184  }
   185  
   186  // WithValidator allows to register extra validation functions
   187  // to assert that the configured tunnel protocol matches the one expected by
   188  // the given feature.
   189  func WithValidator(validator func(Protocol) error) enablerOpt {
   190  	return func(te *enabler) {
   191  		te.validators = append(te.validators, validator)
   192  	}
   193  }
   194  
   195  // WithoutMTUAdaptation conveys that the given feature request
   196  // to enable tunneling, but the MTU adaptation is already handled externally.
   197  func WithoutMTUAdaptation() enablerOpt {
   198  	return func(te *enabler) {
   199  		te.needsMTUAdaptation = false
   200  	}
   201  }
   202  
   203  type enabler struct {
   204  	enable             bool
   205  	needsMTUAdaptation bool
   206  	validators         []func(Protocol) error
   207  }
   208  
   209  type enablerOpt func(*enabler)
   210  
   211  // userCfg wraps the tunnel-related user configurations.
   212  type userCfg struct {
   213  	TunnelProtocol string
   214  	TunnelPort     uint16
   215  }
   216  
   217  // Flags implements the cell.Flagger interface, to register the given flags.
   218  func (def userCfg) Flags(flags *pflag.FlagSet) {
   219  	flags.String("tunnel-protocol", def.TunnelProtocol, "Encapsulation protocol to use for the overlay (\"vxlan\" or \"geneve\")")
   220  	flags.Uint16("tunnel-port", def.TunnelPort, fmt.Sprintf("Tunnel port (default %d for \"vxlan\" and %d for \"geneve\")", defaults.TunnelPortVXLAN, defaults.TunnelPortGeneve))
   221  }