github.com/cilium/cilium@v1.16.2/bpf/lib/ipv6.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 <linux/ipv6.h>
     7  
     8  #include "dbg.h"
     9  
    10  /* Number of extension headers that can be skipped */
    11  #define IPV6_MAX_HEADERS 4
    12  
    13  #define NEXTHDR_HOP             0       /* Hop-by-hop option header. */
    14  #define NEXTHDR_TCP             6       /* TCP segment. */
    15  #define NEXTHDR_UDP             17      /* UDP message. */
    16  #define NEXTHDR_IPV6            41      /* IPv6 in IPv6 */
    17  #define NEXTHDR_ROUTING         43      /* Routing header. */
    18  #define NEXTHDR_FRAGMENT        44      /* Fragmentation/reassembly header. */
    19  #define NEXTHDR_GRE             47      /* GRE header. */
    20  #define NEXTHDR_ESP             50      /* Encapsulating security payload. */
    21  #define NEXTHDR_AUTH            51      /* Authentication header. */
    22  #define NEXTHDR_ICMP            58      /* ICMP for IPv6. */
    23  #define NEXTHDR_NONE            59      /* No next header */
    24  #define NEXTHDR_DEST            60      /* Destination options header. */
    25  #define NEXTHDR_SCTP            132     /* SCTP message. */
    26  #define NEXTHDR_MOBILITY        135     /* Mobility header. */
    27  
    28  #define NEXTHDR_MAX             255
    29  
    30  #define IPV6_SADDR_OFF		offsetof(struct ipv6hdr, saddr)
    31  #define IPV6_DADDR_OFF		offsetof(struct ipv6hdr, daddr)
    32  
    33  static __always_inline int ipv6_optlen(const struct ipv6_opt_hdr *opthdr)
    34  {
    35  	return (opthdr->hdrlen + 1) << 3;
    36  }
    37  
    38  static __always_inline int ipv6_authlen(const struct ipv6_opt_hdr *opthdr)
    39  {
    40  	return (opthdr->hdrlen + 2) << 2;
    41  }
    42  
    43  static __always_inline int ipv6_hdrlen_offset(struct __ctx_buff *ctx, __u8 *nexthdr, int l3_off)
    44  {
    45  	int i, len = sizeof(struct ipv6hdr);
    46  	struct ipv6_opt_hdr opthdr __align_stack_8;
    47  	__u8 nh = *nexthdr;
    48  
    49  #pragma unroll
    50  	for (i = 0; i < IPV6_MAX_HEADERS; i++) {
    51  		switch (nh) {
    52  		case NEXTHDR_NONE:
    53  			return DROP_INVALID_EXTHDR;
    54  
    55  		case NEXTHDR_FRAGMENT:
    56  			return DROP_FRAG_NOSUPPORT;
    57  
    58  		case NEXTHDR_HOP:
    59  		case NEXTHDR_ROUTING:
    60  		case NEXTHDR_AUTH:
    61  		case NEXTHDR_DEST:
    62  			if (ctx_load_bytes(ctx, l3_off + len, &opthdr, sizeof(opthdr)) < 0)
    63  				return DROP_INVALID;
    64  
    65  			if (nh == NEXTHDR_AUTH)
    66  				len += ipv6_authlen(&opthdr);
    67  			else
    68  				len += ipv6_optlen(&opthdr);
    69  
    70  			nh = opthdr.nexthdr;
    71  			break;
    72  
    73  		default:
    74  			*nexthdr = nh;
    75  			return len;
    76  		}
    77  	}
    78  
    79  	/* Reached limit of supported extension headers */
    80  	return DROP_INVALID_EXTHDR;
    81  }
    82  
    83  static __always_inline int ipv6_hdrlen(struct __ctx_buff *ctx, __u8 *nexthdr)
    84  {
    85  	return ipv6_hdrlen_offset(ctx, nexthdr, ETH_HLEN);
    86  }
    87  
    88  static __always_inline void ipv6_addr_copy(union v6addr *dst,
    89  					   const union v6addr *src)
    90  {
    91  	memcpy(dst, src, sizeof(*dst));
    92  }
    93  
    94  static __always_inline void ipv6_addr_copy_unaligned(union v6addr *dst,
    95  						     const union v6addr *src)
    96  {
    97  	dst->d1 = src->d1;
    98  	dst->d2 = src->d2;
    99  }
   100  
   101  static __always_inline bool ipv6_addr_equals(const union v6addr *a,
   102  					     const union v6addr *b)
   103  {
   104  	if (a->d1 != b->d1)
   105  		return false;
   106  	return a->d2 == b->d2;
   107  }
   108  
   109  /* Only works with contiguous masks. */
   110  static __always_inline int ipv6_addr_in_net(const union v6addr *addr,
   111  					    const union v6addr *net,
   112  					    const union v6addr *mask)
   113  {
   114  	return ((addr->p1 & mask->p1) == net->p1)
   115  		&& (!mask->p2
   116  		    || (((addr->p2 & mask->p2) == net->p2)
   117  			&& (!mask->p3
   118  			    || (((addr->p3 & mask->p3) == net->p3)
   119  				&& (!mask->p4 || ((addr->p4 & mask->p4) == net->p4))))));
   120  }
   121  
   122  #define GET_PREFIX(PREFIX)						\
   123  	bpf_htonl(PREFIX <= 0 ? 0 : PREFIX < 32 ? ((1<<PREFIX) - 1) << (32-PREFIX)	\
   124  			      : 0xFFFFFFFF)
   125  
   126  static __always_inline void ipv6_addr_clear_suffix(union v6addr *addr,
   127  						   int prefix)
   128  {
   129  	addr->p1 &= GET_PREFIX(prefix);
   130  	prefix -= 32;
   131  	addr->p2 &= GET_PREFIX(prefix);
   132  	prefix -= 32;
   133  	addr->p3 &= GET_PREFIX(prefix);
   134  	prefix -= 32;
   135  	addr->p4 &= GET_PREFIX(prefix);
   136  }
   137  
   138  static __always_inline int ipv6_dec_hoplimit(struct __ctx_buff *ctx, int off)
   139  {
   140  	__u8 hl;
   141  
   142  	if (ctx_load_bytes(ctx, off + offsetof(struct ipv6hdr, hop_limit),
   143  			   &hl, sizeof(hl)) < 0)
   144  		return DROP_INVALID;
   145  
   146  	if (hl <= 1)
   147  		return DROP_TTL_EXCEEDED;
   148  	hl--;
   149  	if (ctx_store_bytes(ctx, off + offsetof(struct ipv6hdr, hop_limit),
   150  			    &hl, sizeof(hl), BPF_F_RECOMPUTE_CSUM) < 0)
   151  		return DROP_WRITE_ERROR;
   152  	return 0;
   153  }
   154  
   155  static __always_inline int ipv6_load_saddr(struct __ctx_buff *ctx, int off,
   156  					   union v6addr *dst)
   157  {
   158  	return ctx_load_bytes(ctx, off + offsetof(struct ipv6hdr, saddr), dst->addr,
   159  			      sizeof(((struct ipv6hdr *)NULL)->saddr));
   160  }
   161  
   162  /* Assumes that caller fixes checksum csum_diff() and l4_csum_replace() */
   163  static __always_inline int ipv6_store_saddr(struct __ctx_buff *ctx, __u8 *addr,
   164  					    int off)
   165  {
   166  	return ctx_store_bytes(ctx, off + offsetof(struct ipv6hdr, saddr), addr, 16, 0);
   167  }
   168  
   169  static __always_inline int ipv6_load_daddr(struct __ctx_buff *ctx, int off,
   170  					   union v6addr *dst)
   171  {
   172  	return ctx_load_bytes(ctx, off + offsetof(struct ipv6hdr, daddr), dst->addr,
   173  			      sizeof(((struct ipv6hdr *)NULL)->daddr));
   174  }
   175  
   176  /* Assumes that caller fixes checksum csum_diff() and l4_csum_replace() */
   177  static __always_inline int
   178  ipv6_store_daddr(struct __ctx_buff *ctx, const __u8 *addr, int off)
   179  {
   180  	return ctx_store_bytes(ctx, off + offsetof(struct ipv6hdr, daddr), addr, 16, 0);
   181  }
   182  
   183  static __always_inline int ipv6_load_nexthdr(struct __ctx_buff *ctx, int off,
   184  					     __u8 *nexthdr)
   185  {
   186  	return ctx_load_bytes(ctx, off + offsetof(struct ipv6hdr, nexthdr), nexthdr,
   187  			      sizeof(__u8));
   188  }
   189  
   190  /* Assumes that caller fixes checksum csum_diff() and l4_csum_replace() */
   191  static __always_inline int ipv6_store_nexthdr(struct __ctx_buff *ctx, __u8 *nexthdr,
   192  					      int off)
   193  {
   194  	return ctx_store_bytes(ctx, off + offsetof(struct ipv6hdr, nexthdr), nexthdr,
   195  			      sizeof(__u8), 0);
   196  }
   197  
   198  static __always_inline int ipv6_load_paylen(struct __ctx_buff *ctx, int off,
   199  					    __be16 *len)
   200  {
   201  	return ctx_load_bytes(ctx, off + offsetof(struct ipv6hdr, payload_len),
   202  			      len, sizeof(*len));
   203  }
   204  
   205  /* Assumes that caller fixes checksum csum_diff() and l4_csum_replace() */
   206  static __always_inline int ipv6_store_paylen(struct __ctx_buff *ctx, int off,
   207  					     __be16 *len)
   208  {
   209  	return ctx_store_bytes(ctx, off + offsetof(struct ipv6hdr, payload_len),
   210  			       len, sizeof(*len), 0);
   211  }
   212  
   213  static __always_inline __be32 ipv6_pseudohdr_checksum(struct ipv6hdr *hdr,
   214  						      __u8 next_hdr,
   215  						      __u16 payload_len, __be32 sum)
   216  {
   217  	__be32 len = bpf_htonl((__u32)payload_len);
   218  	__be32 nexthdr = bpf_htonl((__u32)next_hdr);
   219  
   220  	sum = csum_diff(NULL, 0, &hdr->saddr, sizeof(struct in6_addr), sum);
   221  	sum = csum_diff(NULL, 0, &hdr->daddr, sizeof(struct in6_addr), sum);
   222  	sum = csum_diff(NULL, 0, &len, sizeof(len), sum);
   223  	sum = csum_diff(NULL, 0, &nexthdr, sizeof(nexthdr), sum);
   224  
   225  	return sum;
   226  }
   227  
   228  /*
   229   * Ipv4 mapped address - 0:0:0:0:0:FFFF::/96
   230   */
   231  static __always_inline int ipv6_addr_is_mapped(const union v6addr *addr)
   232  {
   233  	return addr->p1 == 0 && addr->p2 == 0 && addr->p3 == 0xFFFF0000;
   234  }