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  }