github.com/datadog/cilium@v1.6.12/bpf/lib/ipv6.h (about)

     1  /*
     2   *  Copyright (C) 2016-2017 Authors of Cilium
     3   *
     4   *  This program is free software; you can redistribute it and/or modify
     5   *  it under the terms of the GNU General Public License as published by
     6   *  the Free Software Foundation; either version 2 of the License, or
     7   *  (at your option) any later version.
     8   *
     9   *  This program is distributed in the hope that it will be useful,
    10   *  but WITHOUT ANY WARRANTY; without even the implied warranty of
    11   *  MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE.  See the
    12   *  GNU General Public License for more details.
    13   *
    14   *  You should have received a copy of the GNU General Public License
    15   *  along with this program; if not, write to the Free Software
    16   *  Foundation, Inc., 51 Franklin St, Fifth Floor, Boston, MA  02110-1301  USA
    17   */
    18  #ifndef __LIB_IPV6__
    19  #define __LIB_IPV6__
    20  
    21  #include <linux/ipv6.h>
    22  
    23  #include "dbg.h"
    24  #define IPV6_FLOWINFO_MASK              bpf_htonl(0x0FFFFFFF)
    25  #define IPV6_FLOWLABEL_MASK             bpf_htonl(0x000FFFFF)
    26  #define IPV6_FLOWLABEL_STATELESS_FLAG   bpf_htonl(0x00080000)
    27  
    28  #define IPV6_TCLASS_MASK (IPV6_FLOWINFO_MASK & ~IPV6_FLOWLABEL_MASK)
    29  #define IPV6_TCLASS_SHIFT       20
    30  
    31  /* Number of extension headers that can be skipped */
    32  #define IPV6_MAX_HEADERS 4
    33  
    34  #define NEXTHDR_HOP             0       /* Hop-by-hop option header. */
    35  #define NEXTHDR_TCP             6       /* TCP segment. */
    36  #define NEXTHDR_UDP             17      /* UDP message. */
    37  #define NEXTHDR_IPV6            41      /* IPv6 in IPv6 */
    38  #define NEXTHDR_ROUTING         43      /* Routing header. */
    39  #define NEXTHDR_FRAGMENT        44      /* Fragmentation/reassembly header. */
    40  #define NEXTHDR_GRE             47      /* GRE header. */
    41  #define NEXTHDR_ESP             50      /* Encapsulating security payload. */
    42  #define NEXTHDR_AUTH            51      /* Authentication header. */
    43  #define NEXTHDR_ICMP            58      /* ICMP for IPv6. */
    44  #define NEXTHDR_NONE            59      /* No next header */
    45  #define NEXTHDR_DEST            60      /* Destination options header. */
    46  #define NEXTHDR_SCTP            132     /* SCTP message. */
    47  #define NEXTHDR_MOBILITY        135     /* Mobility header. */
    48  
    49  #define NEXTHDR_MAX             255
    50  
    51  static inline int ipv6_optlen(struct ipv6_opt_hdr *opthdr)
    52  {
    53  	return (opthdr->hdrlen + 1) << 3;
    54  }
    55  
    56  static inline int ipv6_authlen(struct ipv6_opt_hdr *opthdr)
    57  {
    58  	return (opthdr->hdrlen + 2) << 2;
    59  }
    60  
    61  static inline int __inline__ ipv6_hdrlen(struct __sk_buff *skb, int l3_off, __u8 *nexthdr)
    62  {
    63  	int i, len = sizeof(struct ipv6hdr);
    64  	struct ipv6_opt_hdr opthdr;
    65  	__u8 nh = *nexthdr;
    66  
    67  #pragma unroll
    68  	for (i = 0; i < IPV6_MAX_HEADERS; i++) {
    69  		switch (nh) {
    70  		case NEXTHDR_NONE:
    71  			return DROP_INVALID_EXTHDR;
    72  
    73  		case NEXTHDR_FRAGMENT:
    74  			return DROP_FRAG_NOSUPPORT;
    75  
    76  		case NEXTHDR_HOP:
    77  		case NEXTHDR_ROUTING:
    78  		case NEXTHDR_AUTH:
    79  		case NEXTHDR_DEST:
    80  			if (skb_load_bytes(skb, l3_off + len, &opthdr, sizeof(opthdr)) < 0)
    81  				return DROP_INVALID;
    82  
    83  			nh = opthdr.nexthdr;
    84  			if (nh == NEXTHDR_AUTH)
    85  				len += ipv6_authlen(&opthdr);
    86  			else
    87  				len += ipv6_optlen(&opthdr);
    88  			break;
    89  
    90  		default:
    91  			*nexthdr = nh;
    92  			return len;
    93  		}
    94  	}
    95  
    96  	/* Reached limit of supported extension headers */
    97  	return DROP_INVALID_EXTHDR;
    98  }
    99  
   100  static inline void ipv6_addr_copy(union v6addr *dst, union v6addr *src)
   101  {
   102  	dst->d1 = src->d1;
   103  	dst->d2 = src->d2;
   104  }
   105  
   106  static inline __u64 ipv6_addrcmp(union v6addr *a, union v6addr *b)
   107  {
   108  	__u64 tmp;
   109  
   110  	tmp = a->d1 - b->d1;
   111  	if (!tmp)
   112  		tmp = a->d2 - b->d2;
   113  	return tmp;
   114  }
   115  
   116  // Only works with contiguous masks.
   117  static inline int ipv6_addr_in_net(union v6addr *addr, union v6addr *net, union v6addr *mask)
   118  {
   119  	return ((addr->p1 & mask->p1) == net->p1)
   120  		&& (!mask->p2
   121  		    || (((addr->p2 & mask->p2) == net->p2)
   122  			&& (!mask->p3
   123  			    || (((addr->p3 & mask->p3) == net->p3)
   124  				&& (!mask->p4 || ((addr->p4 & mask->p4) == net->p4))))));
   125  }
   126  
   127  #define GET_PREFIX(PREFIX)						\
   128  	bpf_htonl(prefix <= 0 ? 0 : prefix < 32 ? ((1<<prefix) - 1) << (32-prefix)	\
   129  			      : 0xFFFFFFFF)
   130  
   131  static inline void ipv6_addr_clear_suffix(union v6addr *addr, int prefix)
   132  {
   133  	addr->p1 &= GET_PREFIX(prefix);
   134  	prefix -= 32;
   135  	addr->p2 &= GET_PREFIX(prefix);
   136  	prefix -= 32;
   137  	addr->p3 &= GET_PREFIX(prefix);
   138  	prefix -= 32;
   139  	addr->p4 &= GET_PREFIX(prefix);
   140  	prefix -= 32;
   141  }
   142  
   143  static inline int ipv6_match_prefix_64(const union v6addr *addr, const union v6addr *prefix)
   144  {
   145  	int tmp;
   146  
   147  	tmp = addr->p1 - prefix->p1;
   148  	if (!tmp)
   149  		tmp = addr->p2 - prefix->p2;
   150  
   151  	return !tmp;
   152  }
   153  
   154  static inline int ipv6_dec_hoplimit(struct __sk_buff *skb, int off)
   155  {
   156  	__u8 hl;
   157  
   158  	skb_load_bytes(skb, off + offsetof(struct ipv6hdr, hop_limit),
   159  		       &hl, sizeof(hl));
   160  	if (hl <= 1)
   161  		return 1;
   162  	hl--;
   163  	if (skb_store_bytes(skb, off + offsetof(struct ipv6hdr, hop_limit),
   164  			    &hl, sizeof(hl), BPF_F_RECOMPUTE_CSUM) < 0)
   165  		return DROP_WRITE_ERROR;
   166  	return 0;
   167  }
   168  
   169  static inline int ipv6_load_saddr(struct __sk_buff *skb, int off, union v6addr *dst)
   170  {
   171  	return skb_load_bytes(skb, off + offsetof(struct ipv6hdr, saddr), dst->addr,
   172  			      sizeof(((struct ipv6hdr *)NULL)->saddr));
   173  }
   174  
   175  /* Assumes that caller fixes checksum csum_diff() and l4_csum_replace() */
   176  static inline int ipv6_store_saddr(struct __sk_buff *skb, __u8 *addr, int off)
   177  {
   178  	return skb_store_bytes(skb, off + offsetof(struct ipv6hdr, saddr), addr, 16, 0);
   179  }
   180  
   181  static inline int ipv6_load_daddr(struct __sk_buff *skb, int off, union v6addr *dst)
   182  {
   183  	return skb_load_bytes(skb, off + offsetof(struct ipv6hdr, daddr), dst->addr,
   184  			      sizeof(((struct ipv6hdr *)NULL)->daddr));
   185  }
   186  
   187  /* Assumes that caller fixes checksum csum_diff() and l4_csum_replace() */
   188  static inline int ipv6_store_daddr(struct __sk_buff *skb, __u8 *addr, int off)
   189  {
   190  	return skb_store_bytes(skb, off + offsetof(struct ipv6hdr, daddr), addr, 16, 0);
   191  }
   192  
   193  static inline int ipv6_load_nexthdr(struct __sk_buff *skb, int off, __u8 *nexthdr)
   194  {
   195  	return skb_load_bytes(skb, off + offsetof(struct ipv6hdr, nexthdr), nexthdr,
   196  			      sizeof(__u8));
   197  }
   198  
   199  /* Assumes that caller fixes checksum csum_diff() and l4_csum_replace() */
   200  static inline int ipv6_store_nexthdr(struct __sk_buff *skb, __u8 *nexthdr, int off)
   201  {
   202  	return skb_store_bytes(skb, off + offsetof(struct ipv6hdr, nexthdr), nexthdr,
   203  			      sizeof(__u8), 0);
   204  }
   205  
   206  static inline int ipv6_load_paylen(struct __sk_buff *skb, int off, __be16 *len)
   207  {
   208  	return skb_load_bytes(skb, off + offsetof(struct ipv6hdr, payload_len),
   209  			      len, sizeof(*len));
   210  }
   211  
   212  /* Assumes that caller fixes checksum csum_diff() and l4_csum_replace() */
   213  static inline int ipv6_store_paylen(struct __sk_buff *skb, int off, __be16 *len)
   214  {
   215  	return skb_store_bytes(skb, off + offsetof(struct ipv6hdr, payload_len),
   216  			       len, sizeof(*len), 0);
   217  }
   218  
   219  static inline int ipv6_store_flowlabel(struct __sk_buff *skb, int off, __be32 label)
   220  {
   221  	__be32 old;
   222  
   223  	/* use traffic class from packet */
   224  	if (skb_load_bytes(skb, off, &old, 4) < 0)
   225  		return DROP_INVALID;
   226  
   227  	old &= IPV6_TCLASS_MASK;
   228  	old = bpf_htonl(0x60000000) | label | old;
   229  
   230  	if (skb_store_bytes(skb, off, &old, 4, BPF_F_RECOMPUTE_CSUM) < 0)
   231  		return DROP_WRITE_ERROR;
   232  
   233  	return 0;
   234  }
   235  
   236  static inline __be32 ipv6_pseudohdr_checksum(struct ipv6hdr *hdr,
   237                                               __u8 next_hdr,
   238  					     __u16 payload_len, __be32 sum)
   239  {
   240  	__be32 len = bpf_htonl((__u32)payload_len);
   241  	__be32 nexthdr = bpf_htonl((__u32)next_hdr);
   242  	sum = csum_diff(NULL, 0, &hdr->saddr, sizeof(struct in6_addr), sum);
   243  	sum = csum_diff(NULL, 0, &hdr->daddr, sizeof(struct in6_addr), sum);
   244  	sum = csum_diff(NULL, 0, &len, sizeof(len), sum);
   245  	sum = csum_diff(NULL, 0, &nexthdr, sizeof(nexthdr), sum);
   246  
   247  	return sum;
   248  }
   249  
   250  /*
   251   * Ipv4 mapped address - 0:0:0:0:0:FFFF::/96
   252   */
   253  static inline int ipv6_addr_is_mapped(union v6addr *addr)
   254  {
   255  	return addr->p1 == 0 && addr->p2 == 0 && addr->p3 == 0xFFFF0000;
   256  }
   257  #endif /* __LIB_IPV6__ */