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 }