github.com/cilium/cilium@v1.16.2/bpf/lib/srv6.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 "lib/common.h"
     7  #include "lib/fib.h"
     8  #include "lib/identity.h"
     9  
    10  #include "maps.h"
    11  
    12  #ifdef ENABLE_SRV6
    13  struct srv6_srh {
    14  	struct ipv6_rt_hdr rthdr;
    15  	__u8 first_segment;
    16  	__u8 flags;
    17  	__u16 reserved;
    18  	struct in6_addr segments[0];
    19  };
    20  
    21  # ifdef ENABLE_IPV4
    22  
    23  /* SRV6_VRF_STATIC_PREFIX4 gets sizeof non-IP, non-prefix part of
    24   * srv6_vrf_key4.
    25   */
    26  #  define SRV6_VRF_STATIC_PREFIX4						\
    27  	(8 * (sizeof(struct srv6_vrf_key4) - sizeof(struct bpf_lpm_trie_key)\
    28  	      - 4))
    29  #  define SRV6_VRF_PREFIX4_LEN(PREFIX) (SRV6_VRF_STATIC_PREFIX4 + (PREFIX))
    30  #  define SRV6_VRF_IPV4_PREFIX SRV6_VRF_PREFIX4_LEN(32)
    31  static __always_inline __u32*
    32  srv6_lookup_vrf4(__be32 sip, __be32 dip)
    33  {
    34  	struct srv6_vrf_key4 key = {
    35  		.lpm = { SRV6_VRF_IPV4_PREFIX, {} },
    36  		.src_ip = sip,
    37  		.dst_cidr = dip,
    38  	};
    39  	return map_lookup_elem(&SRV6_VRF_MAP4, &key);
    40  }
    41  
    42  /* SRV6_POLICY_STATIC_PREFIX4 gets sizeof non-IP, non-prefix part of
    43   * srv6_policy_key4.
    44   */
    45  #  define SRV6_POLICY_STATIC_PREFIX4						\
    46  	(8 * (sizeof(struct srv6_policy_key4) - sizeof(struct bpf_lpm_trie_key)	\
    47  	      - 4))
    48  #  define SRV6_POLICY_PREFIX4_LEN(PREFIX) (SRV6_POLICY_STATIC_PREFIX4 + (PREFIX))
    49  #  define SRV6_POLICY_IPV4_PREFIX SRV6_POLICY_PREFIX4_LEN(32)
    50  static __always_inline union v6addr *
    51  srv6_lookup_policy4(__u32 vrf_id, __be32 dip)
    52  {
    53  	struct srv6_policy_key4 key = {
    54  		.lpm = { SRV6_POLICY_IPV4_PREFIX, {} },
    55  		.vrf_id = vrf_id,
    56  		.dst_cidr = dip,
    57  	};
    58  	return map_lookup_elem(&SRV6_POLICY_MAP4, &key);
    59  }
    60  # endif /* ENABLE_IPV4 */
    61  
    62  /* SRV6_VRF_STATIC_PREFIX6 gets sizeof non-IP, non-prefix part of
    63   * srv6_vrf_key6.
    64   */
    65  #  define SRV6_VRF_STATIC_PREFIX6						\
    66  	(8 * (sizeof(struct srv6_vrf_key6) - sizeof(struct bpf_lpm_trie_key)\
    67  	      - 4))
    68  #  define SRV6_VRF_PREFIX6_LEN(PREFIX) (SRV6_VRF_STATIC_PREFIX6 + (PREFIX))
    69  #  define SRV6_VRF_IPV6_PREFIX SRV6_VRF_PREFIX6_LEN(32)
    70  static __always_inline __u32*
    71  srv6_lookup_vrf6(const struct in6_addr *sip, const struct in6_addr *dip)
    72  {
    73  	struct srv6_vrf_key6 key = {
    74  		.lpm = { SRV6_VRF_IPV6_PREFIX, {} },
    75  		.src_ip = *(union v6addr *)sip,
    76  		.dst_cidr = *(union v6addr *)dip,
    77  	};
    78  	return map_lookup_elem(&SRV6_VRF_MAP6, &key);
    79  }
    80  
    81  /* SRV6_POLICY_STATIC_PREFIX6 gets sizeof non-IP, non-prefix part of
    82   * srv6_policy_key6.
    83   */
    84  # define SRV6_POLICY_STATIC_PREFIX6						\
    85  	(8 * (sizeof(struct srv6_policy_key6) - sizeof(struct bpf_lpm_trie_key)	\
    86  	      - 4))
    87  # define SRV6_POLICY_PREFIX6_LEN(PREFIX) (SRV6_POLICY_STATIC_PREFIX6 + (PREFIX))
    88  # define SRV6_POLICY_IPV6_PREFIX SRV6_POLICY_PREFIX6_LEN(128)
    89  
    90  static __always_inline union v6addr *
    91  srv6_lookup_policy6(__u32 vrf_id, const struct in6_addr *dip)
    92  {
    93  	struct srv6_policy_key6 key = {
    94  		.lpm = { SRV6_POLICY_IPV6_PREFIX, {} },
    95  		.vrf_id = vrf_id,
    96  		.dst_cidr = *(union v6addr *)dip,
    97  	};
    98  	return map_lookup_elem(&SRV6_POLICY_MAP6, &key);
    99  }
   100  
   101  static __always_inline __u32
   102  srv6_lookup_sid(const struct in6_addr *sid)
   103  {
   104  	__u32 *vrf_id;
   105  
   106  	vrf_id = map_lookup_elem(&SRV6_SID_MAP, sid);
   107  	if (vrf_id)
   108  		return *vrf_id;
   109  	return 0;
   110  }
   111  
   112  static __always_inline bool
   113  is_srv6_packet(const struct ipv6hdr *ip6)
   114  {
   115  #ifdef ENABLE_SRV6_SRH_ENCAP
   116  	if (ip6->nexthdr == NEXTHDR_ROUTING)
   117  		return true;
   118  #endif /* ENABLE_SRV6_SRH_ENCAP */
   119  	return ip6->nexthdr == IPPROTO_IPIP ||
   120  	       ip6->nexthdr == IPPROTO_IPV6;
   121  }
   122  
   123  # ifndef SKIP_SRV6_HANDLING
   124  static __always_inline int
   125  srv6_encapsulation(struct __ctx_buff *ctx, int growth, __u16 new_payload_len,
   126  		   __u8 nexthdr, union v6addr *saddr, struct in6_addr *sid)
   127  {
   128  	__u32 len = sizeof(struct ipv6hdr) - 2 * sizeof(struct in6_addr);
   129  	struct ipv6hdr new_ip6 = {
   130  		.version     = 0x6,
   131  		.payload_len = bpf_htons(new_payload_len),
   132  		.nexthdr     = nexthdr,
   133  		.hop_limit   = IPDEFTTL,
   134  	};
   135  
   136  #ifdef ENABLE_SRV6_SRH_ENCAP
   137  	/* If reduced encapsulation is disabled, the next header will be the
   138  	 * segment routing header.
   139  	 */
   140  	new_ip6.nexthdr = NEXTHDR_ROUTING;
   141  #endif /* ENABLE_SRV6_SRH_ENCAP */
   142  
   143  	/* Add room between Ethernet and network headers. */
   144  	if (ctx_adjust_hroom(ctx, growth, BPF_ADJ_ROOM_MAC,
   145  			     BPF_F_ADJ_ROOM_ENCAP_L3_IPV6))
   146  		return DROP_INVALID;
   147  	if (ctx_store_bytes(ctx, ETH_HLEN, &new_ip6, len, 0) < 0)
   148  		return DROP_WRITE_ERROR;
   149  	if (ctx_store_bytes(ctx, ETH_HLEN + offsetof(struct ipv6hdr, saddr),
   150  			    saddr, sizeof(union v6addr), 0) < 0)
   151  		return DROP_WRITE_ERROR;
   152  	if (ctx_store_bytes(ctx, ETH_HLEN + offsetof(struct ipv6hdr, daddr),
   153  			    sid, sizeof(struct in6_addr), 0) < 0)
   154  		return DROP_WRITE_ERROR;
   155  
   156  #ifdef ENABLE_SRV6_SRH_ENCAP
   157  	{
   158  	/* If reduced encapsulation mode is disabled, we need to add a segment
   159  	 * routing header.
   160  	 */
   161  	struct srv6_srh srh = {
   162  		.rthdr.nexthdr       = nexthdr,
   163  		.rthdr.hdrlen        = sizeof(struct in6_addr) / 8,
   164  		.rthdr.type          = IPV6_SRCRT_TYPE_4,
   165  		.rthdr.segments_left = 0,
   166  		.first_segment       = 0,
   167  		.flags               = 0,
   168  		.reserved            = 0,
   169  	};
   170  	int segment_list_offset = ETH_HLEN + sizeof(struct ipv6hdr) +
   171  				  offsetof(struct srv6_srh, segments);
   172  
   173  	if (ctx_store_bytes(ctx, ETH_HLEN + sizeof(struct ipv6hdr),
   174  			    &srh, sizeof(struct srv6_srh), 0) < 0)
   175  		return DROP_WRITE_ERROR;
   176  	if (ctx_store_bytes(ctx, segment_list_offset, sid,
   177  			    sizeof(struct in6_addr), 0) < 0)
   178  		return DROP_WRITE_ERROR;
   179  	}
   180  #endif /* ENABLE_SRV6_SRH_ENCAP */
   181  
   182  	return 0;
   183  }
   184  
   185  static __always_inline int
   186  srv6_decapsulation(struct __ctx_buff *ctx)
   187  {
   188  	__u16 new_proto = bpf_htons(ETH_P_IP);
   189  	void *data, *data_end;
   190  	struct ipv6hdr *ip6;
   191  	int shrink = 0;
   192  
   193  	if (!revalidate_data(ctx, &data, &data_end, &ip6))
   194  		return DROP_INVALID;
   195  
   196  	switch (ip6->nexthdr) {
   197  #ifdef ENABLE_SRV6_SRH_ENCAP
   198  	case NEXTHDR_ROUTING: {
   199  		struct srv6_srh *srh = (struct srv6_srh *)(ip6 + 1);
   200  
   201  		if ((void *)srh + sizeof(struct srv6_srh) + sizeof(struct in6_addr) > data_end)
   202  			return DROP_INVALID;
   203  
   204  		/* We only support the SRH extension header for now. */
   205  		if (srh->rthdr.type != IPV6_SRCRT_TYPE_4)
   206  			return DROP_INVALID;
   207  
   208  		shrink = sizeof(struct srv6_srh) + sizeof(struct in6_addr);
   209  
   210  		switch (srh->rthdr.nexthdr) {
   211  		case IPPROTO_IPIP:
   212  			goto parse_outer_ipv4;
   213  		case IPPROTO_IPV6:
   214  			goto parse_outer_ipv6;
   215  		default:
   216  			return DROP_INVALID;
   217  		}
   218  	}
   219  #endif /* ENABLE_SRV6_SRH_ENCAP */
   220  	case IPPROTO_IPIP:
   221  parse_outer_ipv4: __maybe_unused;
   222  		if (ctx_change_proto(ctx, new_proto, 0) < 0)
   223  			return DROP_WRITE_ERROR;
   224  		if (ctx_store_bytes(ctx, offsetof(struct ethhdr, h_proto),
   225  				    &new_proto, sizeof(new_proto), 0) < 0)
   226  			return DROP_WRITE_ERROR;
   227  		/* ctx_change_proto above shrinks the packet from IPv6 header
   228  		 * length to IPv4 header length. It removes that space from the
   229  		 * same header we will later delete.
   230  		 * Thus, deduce this space from the next packet shrinking.
   231  		 */
   232  		shrink += sizeof(struct iphdr);
   233  		break;
   234  	case IPPROTO_IPV6:
   235  parse_outer_ipv6: __maybe_unused;
   236  		shrink += sizeof(struct ipv6hdr);
   237  		break;
   238  	default:
   239  		return DROP_INVALID;
   240  	}
   241  
   242  	/* Remove the outer IPv6 header. */
   243  	if (ctx_adjust_hroom(ctx, -shrink, BPF_ADJ_ROOM_MAC,
   244  			     ctx_adjust_hroom_flags()))
   245  		return DROP_INVALID;
   246  	return 0;
   247  }
   248  
   249  static __always_inline int
   250  srv6_handling4(struct __ctx_buff *ctx, union v6addr *src_sid,
   251  	       struct in6_addr *dst_sid)
   252  {
   253  	__u16 new_payload_len, outer_proto = bpf_htons(ETH_P_IPV6);
   254  	void *data, *data_end;
   255  	struct iphdr *ip4;
   256  	__u8 nexthdr;
   257  	int growth = 0;
   258  
   259  	/* Inner packet is IPv4. */
   260  	if (!revalidate_data(ctx, &data, &data_end, &ip4))
   261  		return DROP_INVALID;
   262  	nexthdr = IPPROTO_IPIP;
   263  	/* IPv4's tot_len fields has the size of the entire packet
   264  	 * including headers while IPv6's payload_len field has only
   265  	 * the size of the IPv6 payload. Therefore, without IPv6
   266  	 * extension headers (none here), the outer IPv6 payload_len
   267  	 * is equal to the inner IPv4 tot_len.
   268  	 */
   269  	new_payload_len = bpf_ntohs(ip4->tot_len) - (__u16)(ip4->ihl << 2) + sizeof(struct iphdr);
   270  
   271  	if (ctx_store_bytes(ctx, offsetof(struct ethhdr, h_proto),
   272  			    &outer_proto, sizeof(outer_proto), 0) < 0)
   273  		return DROP_WRITE_ERROR;
   274  
   275  #ifdef ENABLE_SRV6_SRH_ENCAP
   276  	growth += sizeof(struct ipv6hdr) + sizeof(struct srv6_srh) + sizeof(struct in6_addr);
   277  	new_payload_len += sizeof(struct srv6_srh) + sizeof(struct in6_addr);
   278  #else
   279  	growth += sizeof(struct ipv6hdr);
   280  #endif /* ENABLE_SRV6_SRH_ENCAP */
   281  
   282  	return srv6_encapsulation(ctx, growth, new_payload_len, nexthdr,
   283  				  src_sid, dst_sid);
   284  }
   285  
   286  static __always_inline int
   287  srv6_handling6(struct __ctx_buff *ctx, union v6addr *src_sid,
   288  	       struct in6_addr *dst_sid)
   289  {
   290  	__u16 new_payload_len;
   291  	void *data, *data_end;
   292  	struct ipv6hdr *ip6;
   293  	__u8 nexthdr;
   294  	int growth;
   295  
   296  	/* Inner packet is IPv6. */
   297  	if (!revalidate_data(ctx, &data, &data_end, &ip6))
   298  		return DROP_INVALID;
   299  	nexthdr = IPPROTO_IPV6;
   300  	new_payload_len = bpf_ntohs(ip6->payload_len) + sizeof(struct ipv6hdr);
   301  	growth = sizeof(struct ipv6hdr);
   302  
   303  #ifdef ENABLE_SRV6_SRH_ENCAP
   304  	growth += sizeof(struct srv6_srh) + sizeof(struct in6_addr);
   305  	new_payload_len += sizeof(struct srv6_srh) + sizeof(struct in6_addr);
   306  #endif /* ENABLE_SRV6_SRH_ENCAP */
   307  
   308  	return srv6_encapsulation(ctx, growth, new_payload_len, nexthdr,
   309  				  src_sid, dst_sid);
   310  }
   311  
   312  static __always_inline int
   313  srv6_handling(struct __ctx_buff *ctx, struct in6_addr *dst_sid)
   314  {
   315  	void *data, *data_end;
   316  	__u16 inner_proto;
   317  	union v6addr router_ip;
   318  
   319  	BPF_V6(router_ip, ROUTER_IP);
   320  
   321  	if (!validate_ethertype(ctx, &inner_proto))
   322  		return DROP_UNSUPPORTED_L2;
   323  
   324  	switch (inner_proto) {
   325  #  ifdef ENABLE_IPV6
   326  	case bpf_htons(ETH_P_IPV6): {
   327  		struct ipv6hdr *ip6;
   328  
   329  		if (!revalidate_data(ctx, &data, &data_end, &ip6))
   330  			return DROP_INVALID;
   331  
   332  		return srv6_handling6(ctx, &router_ip, dst_sid);
   333  	}
   334  #  endif /* ENABLE_IPV6 */
   335  #  ifdef ENABLE_IPV4
   336  	case bpf_htons(ETH_P_IP): {
   337  		struct iphdr *ip4;
   338  
   339  		if (!revalidate_data(ctx, &data, &data_end, &ip4))
   340  			return DROP_INVALID;
   341  
   342  		return srv6_handling4(ctx, &router_ip, dst_sid);
   343  	}
   344  #  endif /* ENABLE_IPV4 */
   345  	default:
   346  		return DROP_INVALID;
   347  	}
   348  }
   349  
   350  static __always_inline void
   351  srv6_load_meta_sid(struct __ctx_buff *ctx, struct in6_addr *sid)
   352  {
   353  	sid->s6_addr32[0] = ctx_load_meta(ctx, CB_SRV6_SID_1);
   354  	sid->s6_addr32[1] = ctx_load_meta(ctx, CB_SRV6_SID_2);
   355  	sid->s6_addr32[2] = ctx_load_meta(ctx, CB_SRV6_SID_3);
   356  	sid->s6_addr32[3] = ctx_load_meta(ctx, CB_SRV6_SID_4);
   357  }
   358  
   359  static __always_inline void
   360  srv6_store_meta_sid(struct __ctx_buff *ctx, const union v6addr *sid)
   361  {
   362  	ctx_store_meta(ctx, CB_SRV6_SID_1, sid->p1);
   363  	ctx_store_meta(ctx, CB_SRV6_SID_2, sid->p2);
   364  	ctx_store_meta(ctx, CB_SRV6_SID_3, sid->p3);
   365  	ctx_store_meta(ctx, CB_SRV6_SID_4, sid->p4);
   366  }
   367  
   368  __section_tail(CILIUM_MAP_CALLS, CILIUM_CALL_SRV6_ENCAP)
   369  int tail_srv6_encap(struct __ctx_buff *ctx)
   370  {
   371  	struct in6_addr dst_sid;
   372  	int ret = 0;
   373  	int __maybe_unused ext_err = 0;
   374  
   375  	srv6_load_meta_sid(ctx, &dst_sid);
   376  	ret = srv6_handling(ctx, &dst_sid);
   377  	if (ret < 0)
   378  		return send_drop_notify_error(ctx, SECLABEL_IPV6, ret, CTX_ACT_DROP,
   379  					      METRIC_EGRESS);
   380  
   381  	send_trace_notify(ctx, TRACE_TO_STACK, SECLABEL_IPV6, UNKNOWN_ID,
   382  			  TRACE_EP_ID_UNKNOWN,
   383  			  TRACE_IFINDEX_UNKNOWN, TRACE_REASON_SRV6_ENCAP, 0);
   384  
   385  	return ret;
   386  }
   387  
   388  __section_tail(CILIUM_MAP_CALLS, CILIUM_CALL_SRV6_DECAP)
   389  int tail_srv6_decap(struct __ctx_buff *ctx)
   390  {
   391  	int ret = 0;
   392  
   393  	ret = srv6_decapsulation(ctx);
   394  	if (ret < 0)
   395  		goto error_drop;
   396  
   397  	send_trace_notify(ctx, TRACE_TO_STACK, SECLABEL_IPV6, UNKNOWN_ID,
   398  			  TRACE_EP_ID_UNKNOWN,
   399  			  TRACE_IFINDEX_UNKNOWN, TRACE_REASON_SRV6_DECAP, 0);
   400  	return CTX_ACT_OK;
   401  error_drop:
   402  		return send_drop_notify_error(ctx, SECLABEL_IPV6, ret, CTX_ACT_DROP,
   403  					      METRIC_EGRESS);
   404  }
   405  # endif /* SKIP_SRV6_HANDLING */
   406  #endif /* ENABLE_SRV6 */