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 }