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 */