github.com/cilium/cilium@v1.16.2/bpf/lib/static_data.h (about)

     1  /* SPDX-License-Identifier: (GPL-2.0-only OR BSD-2-Clause) */
     2  /* Copyright Authors of Cilium */
     3  
     4  #pragma once
     5  
     6  #include <bpf/ctx/ctx.h>
     7  #include <bpf/api.h>
     8  
     9  #include "endian.h"
    10  
    11  /* Declare a global configuration variable that can be modified at runtime,
    12   * without needing to recompile the datapath. Access the variable using the
    13   * CONFIG() macro.
    14   */
    15  #define DECLARE_CONFIG(type, name, description) \
    16  	/* Emit the variable to the .rodata.config section. The compiler will emit a
    17  	 * BTF Datasec referring to all variables in this section, making them
    18  	 * convenient to iterate through for generating config scaffolding in Go.
    19  	 * ebpf-go will expose this section as a MapSpec when loading a
    20  	 * CollectionSpec.
    21  	 */ \
    22  	__section(".rodata.config") \
    23  	/* Assign the config variable a BTF decl tag containing its description. This
    24  	 * allows including doc comments in code generated from BTF.
    25  	 */ \
    26  	__attribute__((btf_decl_tag(description))) \
    27  	/* Declare a global variable of the given name and type. */ \
    28  	static const type __config_##name;
    29  
    30  /* Hardcode config values at compile time, e.g. from per-endpoint headers.
    31   * Can be used only once per config variable within a single compilation unit.
    32   */
    33  #define ASSIGN_CONFIG(type, name, value) \
    34  	static const type __config_##name = value;
    35  
    36  /* Access a global configuration variable declared using DECLARE_CONFIG(). All
    37   * access must be done through this macro to ensure the loader can correctly
    38   * find and update all instructions that refer to the variable.
    39   */
    40  #define CONFIG(name) ({ \
    41  	/* Variable used as output operand for the asm snippet. Type needs to match
    42  	 * the width of the instruction in the snippet or some compilers will
    43  	 * complain (notably arm64).
    44  	 */ \
    45  	__u64 out; \
    46  	/* In BPF, referring to a global variable directly from C code will generally
    47  	 * result in 2 instructions: 1) loading an array map pointer into a register,
    48  	 * and 2) dereferencing the map pointer at the offset where the variable is
    49  	 * located. The first instruction carries a relocation entry against the map,
    50  	 * so the loader can update the instruction to carry the map's file
    51  	 * descriptor after the map has been created, before loading the program.
    52  	 *
    53  	 * For security and efficiency purposes, we want to use global constants,
    54  	 * populated by the agent before loading the program, remaining immutable
    55  	 * thereafter. Native 'static const' are implemented by the compiler using the
    56  	 * mechanism previously described, with variables emitted to the .rodata map,
    57  	 * which gets frozen after being populated. This makes the verifier treat its
    58  	 * values as constant, enabling dead code elimination and JIT optimizations.
    59  	 * Unfortunately, this is only supported on kernels 5.2 and later, so we need
    60  	 * a user space implementation in the meantime.
    61  	 *
    62  	 * This asm snippet emits a single dword load instruction with a symbol
    63  	 * reference to .rodata.config, with the offset of the variable within the
    64  	 * datasec stored in its instruction constant. This is no different from a
    65  	 * regular static var access in bpf, with one difference: with a regular var,
    66  	 * the compiler still takes the liberty of taking out a map pointer and using
    67  	 * it multiple times, and/or pushing the register holding the variable to the
    68  	 * stack, making it nearly impossible to correctly track and modify.
    69  	 *
    70  	 * To emulate the readonly map behaviour on older kernels, the ELF loader then
    71  	 * rewrites all instructions referring to the map to simple ldimm64 with a
    72  	 * constant provided by the agent at runtime.
    73  	 */ \
    74  	asm volatile("%[out] = __config_" #name " ll" : [out]"=r"(out)); \
    75  	(typeof(__config_##name))out; \
    76  })
    77  
    78  /* Deprecated, use CONFIG instead. */
    79  #define fetch_u16(x) CONFIG(x)
    80  #define fetch_u32(x) CONFIG(x)
    81  #define fetch_ipv6(x) CONFIG(x ## _1), CONFIG(x ## _2)
    82  #define fetch_mac(x) { { CONFIG(x ## _1), (__u16)CONFIG(x ## _2) } }
    83  
    84  /* Deprecated, use DECLARE_CONFIG instead. */
    85  #define DEFINE_U16(name, value) \
    86  	DECLARE_CONFIG(__u16, name, "Constant " #name " declared using DEFINE_U16") \
    87  	ASSIGN_CONFIG(__u16, name, value)
    88  #define DEFINE_U32(name, value) \
    89  	DECLARE_CONFIG(__u32, name, "Constant " #name " declared using DEFINE_U32") \
    90  	ASSIGN_CONFIG(__u32, name, value)
    91  
    92  /* DEFINE_IPV6 and DEFINE_MAC are used to assign values to global constants from
    93   * C headers generated at runtime before the datapath is compiled. This data
    94   * ends up in .rodata.config in the ELF and is also inlined by the Go loader,
    95   * even though it's not handled by ELF variable substitution.
    96   *
    97   * Variables relying on this are THIS_INTERFACE_MAC, LXC_IP, IPV6_MASQUERADE, ROUTER_IP
    98   * and HOST_IP.
    99   */
   100  #define DEFINE_IPV6(name, a1, a2, a3, a4, a5, a6, a7, a8, a9, a10, a11, a12, a13, a14, a15, a16) \
   101  	DECLARE_CONFIG(__u64, name##_1, "First half of ipv6 address " #name) \
   102  	DECLARE_CONFIG(__u64, name##_2, "Second half of ipv6 address " #name) \
   103  	ASSIGN_CONFIG(__u64, name##_1, bpf_cpu_to_be64( \
   104  			(__u64)(__u8)(a1) << 56 | (__u64)(__u8)(a2) << 48 | \
   105  			(__u64)(__u8)(a3) << 40 | (__u64)(__u8)(a4) << 32 | \
   106  			(__u64)(__u8)(a5) << 24 | (__u64)(__u8)(a6) << 16 | \
   107  			(__u64)(__u8)(a7) << 8  | (__u64)(__u8)(a8))); \
   108  	ASSIGN_CONFIG(__u64, name##_2, bpf_cpu_to_be64( \
   109  			(__u64)(__u8)(a9) << 56  | (__u64)(__u8)(a10) << 48 | \
   110  			(__u64)(__u8)(a11) << 40 | (__u64)(__u8)(a12) << 32 | \
   111  			(__u64)(__u8)(a13) << 24 | (__u64)(__u8)(a14) << 16 | \
   112  			(__u64)(__u8)(a15) << 8  | (__u64)(__u8)(a16)));
   113  
   114  #define DEFINE_MAC(name, a1, a2, a3, a4, a5, a6) \
   115  	DECLARE_CONFIG(__u32, name##_1, "First 32 bits of mac address " #name) \
   116  	DECLARE_CONFIG(__u32, name##_2, "Remaining 16 bits of mac address " #name) \
   117  	ASSIGN_CONFIG(__u32, name##_1, \
   118  			(__u32)(__u8)(a1) << 24 | (__u32)(__u8)(a2) << 16 | \
   119  			(__u32)(__u8)(a3) << 8  | (__u32)(__u8)(a4)) \
   120  	ASSIGN_CONFIG(__u32, name##_2, (__u32)(__u8)(a5) << 8  | (__u32)(__u8)(a6))