github.com/cilium/cilium@v1.16.2/pkg/datapath/loader/template.go (about) 1 // SPDX-License-Identifier: Apache-2.0 2 // Copyright Authors of Cilium 3 4 package loader 5 6 import ( 7 "fmt" 8 "maps" 9 "math" 10 "net" 11 "net/netip" 12 13 "github.com/cilium/ebpf" 14 15 "github.com/cilium/cilium/pkg/bpf" 16 "github.com/cilium/cilium/pkg/byteorder" 17 datapath "github.com/cilium/cilium/pkg/datapath/types" 18 "github.com/cilium/cilium/pkg/identity" 19 "github.com/cilium/cilium/pkg/mac" 20 "github.com/cilium/cilium/pkg/maps/callsmap" 21 "github.com/cilium/cilium/pkg/maps/ctmap" 22 "github.com/cilium/cilium/pkg/maps/policymap" 23 "github.com/cilium/cilium/pkg/option" 24 ) 25 26 const ( 27 templateSecurityID = identity.ReservedIdentityWorld 28 templateLxcID = uint16(65535) 29 templatePolicyVerdictFilter = uint32(0xffff) 30 templateIfIndex = math.MaxUint32 31 ) 32 33 var ( 34 templateIPv4 = [4]byte{192, 0, 2, 3} 35 templateIPv6 = [16]byte{0x20, 0x01, 0xdb, 0x8, 0x0b, 0xad, 0xca, 0xfe, 0x60, 0x0d, 0xbe, 0xe2, 0x0b, 0xad, 0xca, 0xfe} 36 37 templateMAC = mac.MAC([]byte{0x02, 0x00, 0x60, 0x0D, 0xF0, 0x0D}) 38 39 elfMapPrefixes = []string{ 40 policymap.MapName, 41 callsmap.MapName, 42 callsmap.CustomCallsMapName, 43 } 44 elfCtMapPrefixes = []string{ 45 ctmap.MapNameTCP4, 46 ctmap.MapNameAny4, 47 ctmap.MapNameTCP6, 48 ctmap.MapNameAny6, 49 } 50 ) 51 52 // templateCfg wraps a real configuration from an endpoint to pass through its 53 // configuration of conditional branches in the datapath, but to mock out dummy 54 // values for static data. 55 // 56 // Note that the static data dummy values must be non-zero in every 32-bit 57 // section of the data to ensure that during compilation, the compiler reserves 58 // space in the .data section of the ELF for the value of the data, rather than 59 // generating a reference to the .bss section (which is what it will typically 60 // do if a static integer is initialized to zero). 61 // 62 // Ideally we also statically configure the values used in the template in such 63 // a way that if ever someone managed to inadvertently attach the template 64 // program directly to a device, that there are no unintended consequences such 65 // as allowing traffic to leak out with routable addresses. 66 type templateCfg struct { 67 // CompileTimeConfiguration passes through directly to the underlying 68 // endpoint configuration, while the rest of the EndpointConfiguration 69 // interface is implemented directly here through receiver functions. 70 datapath.CompileTimeConfiguration 71 } 72 73 // GetID returns a uint64, but in practice on the datapath side it is 74 // guaranteed to be 16-bit; it is used to generate map names, so we need to 75 // ensure that the template generates map names that are as long as the longest 76 // possible name, which would be guaranteed with a 5-digit output. 77 // 78 // In practice, attempts to load an endpoint with the ID 65535 will fail which 79 // means that the calling code must approprately substitute the ID in the ELF 80 // prior to load, or it will fail with a relatively obvious error. 81 func (t *templateCfg) GetID() uint64 { 82 return uint64(templateLxcID) 83 } 84 85 // StringID returns the string form of the ID returned by GetID(). 86 func (t *templateCfg) StringID() string { 87 return fmt.Sprintf("%d", t.GetID()) 88 } 89 90 // GetIdentity treats the template program as part of the world, so if there's 91 // ever some weird bug that causes the template value here to be used, it will 92 // be in the least privileged security context. 93 func (t *templateCfg) GetIdentity() identity.NumericIdentity { 94 return templateSecurityID 95 } 96 97 // GetIdentityLocked is identical to GetIdentity(). This is a temporary 98 // function until WriteEndpointConfig() no longer assumes that the endpoint is 99 // locked. 100 func (t *templateCfg) GetIdentityLocked() identity.NumericIdentity { 101 return templateSecurityID 102 } 103 104 // GetNodeMAC returns a well-known dummy MAC address which may be later 105 // substituted in the ELF. 106 func (t *templateCfg) GetNodeMAC() mac.MAC { 107 return templateMAC 108 } 109 110 func (t *templateCfg) GetIfIndex() int { 111 return templateIfIndex 112 } 113 114 // IPv4Address always returns an IP in the documentation prefix (RFC5737) as 115 // a nonsense address that should typically not be routable. 116 func (t *templateCfg) IPv4Address() netip.Addr { 117 return netip.AddrFrom4(templateIPv4) 118 } 119 120 // IPv6Address returns an IP in the documentation prefix (RFC3849) to ensure 121 // that each 32-bit segment of the address is non-zero as per the requirements 122 // described in the structure definition. This can't be guaranteed while using 123 // a more appropriate prefix such as the discard prefix (RFC6666). 124 func (t *templateCfg) IPv6Address() netip.Addr { 125 return netip.AddrFrom16(templateIPv6) 126 } 127 128 // GetPolicyVerdictLogFilter returns an uint32 filter to ensure 129 // that the filter is non-zero as per the requirements 130 // described in the structure definition. 131 func (t *templateCfg) GetPolicyVerdictLogFilter() uint32 { 132 return templatePolicyVerdictFilter 133 } 134 135 // wrap takes an endpoint configuration and optional stats tracker and wraps 136 // it inside a templateCfg which hides static data from callers that wish to 137 // generate header files based on the configuration, substituting it for 138 // template data. 139 func wrap(cfg datapath.CompileTimeConfiguration) *templateCfg { 140 return &templateCfg{ 141 CompileTimeConfiguration: cfg, 142 } 143 } 144 145 // ELFMapSubstitutions returns the set of map substitutions that must occur in 146 // an ELF template object file to update map references for the specified 147 // endpoint. 148 func ELFMapSubstitutions(ep datapath.Endpoint) map[string]string { 149 result := make(map[string]string) 150 epID := uint16(ep.GetID()) 151 152 for _, name := range elfMapPrefixes { 153 if ep.IsHost() && name == callsmap.MapName { 154 name = callsmap.HostMapName 155 } 156 // Custom calls for hosts are not supported yet. 157 if name == callsmap.CustomCallsMapName && 158 (!option.Config.EnableCustomCalls || ep.IsHost()) { 159 continue 160 } 161 templateStr := bpf.LocalMapName(name, templateLxcID) 162 desiredStr := bpf.LocalMapName(name, epID) 163 result[templateStr] = desiredStr 164 } 165 if ep.ConntrackLocalLocked() { 166 for _, name := range elfCtMapPrefixes { 167 templateStr := bpf.LocalMapName(name, templateLxcID) 168 desiredStr := bpf.LocalMapName(name, epID) 169 result[templateStr] = desiredStr 170 } 171 } 172 173 return result 174 } 175 176 // sliceToU16 converts the input slice of two bytes to a uint16. 177 func sliceToU16(input []byte) uint16 { 178 result := uint16(input[0]) << 8 179 result |= uint16(input[1]) 180 return result 181 } 182 183 // sliceToBe16 converts the input slice of two bytes to a big-endian uint16. 184 func sliceToBe16(input []byte) uint16 { 185 return byteorder.HostToNetwork16(sliceToU16(input)) 186 } 187 188 // sliceToU32 converts the input slice of four bytes to a uint32. 189 func sliceToU32(input []byte) uint32 { 190 result := uint32(input[0]) << 24 191 result |= uint32(input[1]) << 16 192 result |= uint32(input[2]) << 8 193 result |= uint32(input[3]) 194 return result 195 } 196 197 // sliceToBe32 converts the input slice of four bytes to a big-endian uint32. 198 func sliceToBe32(input []byte) uint32 { 199 return byteorder.HostToNetwork32(sliceToU32(input)) 200 } 201 202 // sliceToU64 converts the input slice of eight bytes to a uint64. 203 func sliceToU64(input []byte) uint64 { 204 result := uint64(input[0]) << 56 205 result |= uint64(input[1]) << 48 206 result |= uint64(input[2]) << 40 207 result |= uint64(input[3]) << 32 208 result |= uint64(input[4]) << 24 209 result |= uint64(input[5]) << 16 210 result |= uint64(input[6]) << 8 211 result |= uint64(input[7]) 212 return result 213 } 214 215 // sliceToBe64 converts the input slice of eight bytes to a big-endian uint64. 216 func sliceToBe64(input []byte) uint64 { 217 return byteorder.HostToNetwork64(sliceToU64(input)) 218 } 219 220 // ELFVariableSubstitutions returns the set of data substitutions that must 221 // occur in an ELF template object file to update static data for the specified 222 // endpoint. 223 func ELFVariableSubstitutions(ep datapath.Endpoint) map[string]uint64 { 224 result := make(map[string]uint64) 225 226 if ipv6 := ep.IPv6Address().AsSlice(); ipv6 != nil { 227 // Corresponds to DEFINE_IPV6() in bpf/lib/utils.h 228 result["LXC_IP_1"] = sliceToBe64(ipv6[0:8]) 229 result["LXC_IP_2"] = sliceToBe64(ipv6[8:16]) 230 } 231 if ipv4 := ep.IPv4Address().AsSlice(); ipv4 != nil { 232 result["LXC_IPV4"] = uint64(byteorder.NetIPv4ToHost32(net.IP(ipv4))) 233 } 234 235 mac := ep.GetNodeMAC() 236 // For L3/NOARP devices node mac is not populated. 237 if len(mac) != 0 { 238 result["THIS_INTERFACE_MAC_1"] = uint64(sliceToBe32(mac[0:4])) 239 result["THIS_INTERFACE_MAC_2"] = uint64(sliceToBe16(mac[4:6])) 240 } 241 242 if ep.IsHost() { 243 result["NATIVE_DEV_IFINDEX"] = 0 244 if option.Config.EnableIPv4Masquerade && option.Config.EnableBPFMasquerade { 245 if option.Config.EnableIPv4 { 246 result["IPV4_MASQUERADE"] = 0 247 } 248 } 249 result["SECCTX_FROM_IPCACHE"] = uint64(secctxFromIpcacheDisabled) 250 } else { 251 result["LXC_ID"] = uint64(ep.GetID()) 252 result["THIS_INTERFACE_IFINDEX"] = uint64(ep.GetIfIndex()) 253 } 254 255 // Contrary to IPV4_MASQUERADE, we cannot use a simple #define and 256 // avoid introducing a symbol in stubs.h for IPV6_MASQUERADE. So the 257 // symbol is present in the template object as long as IPv6 BPF 258 // masquerade is enabled, even though it is not used for host 259 // endpoints. 260 if option.Config.EnableIPv6Masquerade && option.Config.EnableBPFMasquerade { 261 result["IPV6_MASQUERADE_1"] = 0 262 result["IPV6_MASQUERADE_2"] = 0 263 } 264 265 identity := ep.GetIdentity().Uint32() 266 result["SECLABEL"] = uint64(identity) 267 result["SECLABEL_IPV4"] = uint64(identity) 268 result["SECLABEL_IPV6"] = uint64(identity) 269 result["POLICY_VERDICT_LOG_FILTER"] = uint64(ep.GetPolicyVerdictLogFilter()) 270 return result 271 } 272 273 func renameMaps(coll *ebpf.CollectionSpec, renames map[string]string) (*ebpf.CollectionSpec, error) { 274 // Shallow copy to avoid expensive copy of coll.Programs. 275 coll = &ebpf.CollectionSpec{ 276 Maps: maps.Clone(coll.Maps), 277 Programs: coll.Programs, 278 Types: coll.Types, 279 ByteOrder: coll.ByteOrder, 280 } 281 282 for name, rename := range renames { 283 mapSpec := coll.Maps[name] 284 if mapSpec == nil { 285 return nil, fmt.Errorf("unknown map %q: can't rename to %q", name, rename) 286 } 287 288 mapSpec = mapSpec.Copy() 289 // NB: We don't change maps[name] since that is referenced 290 // by instructions. 291 mapSpec.Name = rename 292 coll.Maps[name] = mapSpec 293 } 294 295 return coll, nil 296 }