4 #include <linux/seg6_local.h>
6 #include "bpf_helpers.h"
7 #include "bpf_endian.h"
9 #define bpf_printk(fmt, ...) \
11 char ____fmt[] = fmt; \
12 bpf_trace_printk(____fmt, sizeof(____fmt), \
16 /* Packet parsing state machine helpers. */
17 #define cursor_advance(_cursor, _len) \
18 ({ void *_tmp = _cursor; _cursor += _len; _tmp; })
20 #define SR6_FLAG_ALERT (1 << 4)
22 #define htonll(x) ((bpf_htonl(1)) == 1 ? (x) : ((uint64_t)bpf_htonl((x) & \
23 0xFFFFFFFF) << 32) | bpf_htonl((x) >> 32))
24 #define ntohll(x) ((bpf_ntohl(1)) == 1 ? (x) : ((uint64_t)bpf_ntohl((x) & \
25 0xFFFFFFFF) << 32) | bpf_ntohl((x) >> 32))
26 #define BPF_PACKET_HEADER __attribute__((packed))
30 unsigned int priority
:8;
31 unsigned int flow_label
:20;
32 unsigned short payload_len
;
33 unsigned char next_header
;
34 unsigned char hop_limit
;
35 unsigned long long src_hi
;
36 unsigned long long src_lo
;
37 unsigned long long dst_hi
;
38 unsigned long long dst_lo
;
42 unsigned long long hi
;
43 unsigned long long lo
;
47 unsigned char nexthdr
;
50 unsigned char segments_left
;
51 unsigned char first_segment
;
55 struct ip6_addr_t segments
[0];
61 unsigned char value
[0];
64 static __always_inline
struct ip6_srh_t
*get_srh(struct __sk_buff
*skb
)
66 void *cursor
, *data_end
;
67 struct ip6_srh_t
*srh
;
71 data_end
= (void *)(long)skb
->data_end
;
72 cursor
= (void *)(long)skb
->data
;
73 ipver
= (uint8_t *)cursor
;
75 if ((void *)ipver
+ sizeof(*ipver
) > data_end
)
78 if ((*ipver
>> 4) != 6)
81 ip
= cursor_advance(cursor
, sizeof(*ip
));
82 if ((void *)ip
+ sizeof(*ip
) > data_end
)
85 if (ip
->next_header
!= 43)
88 srh
= cursor_advance(cursor
, sizeof(*srh
));
89 if ((void *)srh
+ sizeof(*srh
) > data_end
)
98 static __always_inline
99 int update_tlv_pad(struct __sk_buff
*skb
, uint32_t new_pad
,
100 uint32_t old_pad
, uint32_t pad_off
)
104 if (new_pad
!= old_pad
) {
105 err
= bpf_lwt_seg6_adjust_srh(skb
, pad_off
,
106 (int) new_pad
- (int) old_pad
);
112 char pad_tlv_buf
[16] = {0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0,
114 struct sr6_tlv_t
*pad_tlv
= (struct sr6_tlv_t
*) pad_tlv_buf
;
116 pad_tlv
->type
= SR6_TLV_PADDING
;
117 pad_tlv
->len
= new_pad
- 2;
119 err
= bpf_lwt_seg6_store_bytes(skb
, pad_off
,
120 (void *)pad_tlv_buf
, new_pad
);
128 static __always_inline
129 int is_valid_tlv_boundary(struct __sk_buff
*skb
, struct ip6_srh_t
*srh
,
130 uint32_t *tlv_off
, uint32_t *pad_size
,
133 uint32_t srh_off
, cur_off
;
134 int offset_valid
= 0;
137 srh_off
= (char *)srh
- (char *)(long)skb
->data
;
138 // cur_off = end of segments, start of possible TLVs
139 cur_off
= srh_off
+ sizeof(*srh
) +
140 sizeof(struct ip6_addr_t
) * (srh
->first_segment
+ 1);
144 // we can only go as far as ~10 TLVs due to the BPF max stack size
145 #pragma clang loop unroll(full)
146 for (int i
= 0; i
< 10; i
++) {
147 struct sr6_tlv_t tlv
;
149 if (cur_off
== *tlv_off
)
152 if (cur_off
>= srh_off
+ ((srh
->hdrlen
+ 1) << 3))
155 err
= bpf_skb_load_bytes(skb
, cur_off
, &tlv
, sizeof(tlv
));
159 if (tlv
.type
== SR6_TLV_PADDING
) {
160 *pad_size
= tlv
.len
+ sizeof(tlv
);
163 if (*tlv_off
== srh_off
) {
169 } else if (tlv
.type
== SR6_TLV_HMAC
) {
173 cur_off
+= sizeof(tlv
) + tlv
.len
;
174 } // we reached the padding or HMAC TLVs, or the end of the SRH
181 else if (!offset_valid
)
187 static __always_inline
188 int add_tlv(struct __sk_buff
*skb
, struct ip6_srh_t
*srh
, uint32_t tlv_off
,
189 struct sr6_tlv_t
*itlv
, uint8_t tlv_size
)
191 uint32_t srh_off
= (char *)srh
- (char *)(long)skb
->data
;
192 uint8_t len_remaining
, new_pad
;
193 uint32_t pad_off
= 0;
194 uint32_t pad_size
= 0;
195 uint32_t partial_srh_len
;
201 if (itlv
->type
== SR6_TLV_PADDING
|| itlv
->type
== SR6_TLV_HMAC
)
204 err
= is_valid_tlv_boundary(skb
, srh
, &tlv_off
, &pad_size
, &pad_off
);
208 err
= bpf_lwt_seg6_adjust_srh(skb
, tlv_off
, sizeof(*itlv
) + itlv
->len
);
212 err
= bpf_lwt_seg6_store_bytes(skb
, tlv_off
, (void *)itlv
, tlv_size
);
216 // the following can't be moved inside update_tlv_pad because the
217 // bpf verifier has some issues with it
218 pad_off
+= sizeof(*itlv
) + itlv
->len
;
219 partial_srh_len
= pad_off
- srh_off
;
220 len_remaining
= partial_srh_len
% 8;
221 new_pad
= 8 - len_remaining
;
223 if (new_pad
== 1) // cannot pad for 1 byte only
225 else if (new_pad
== 8)
228 return update_tlv_pad(skb
, new_pad
, pad_size
, pad_off
);
231 static __always_inline
232 int delete_tlv(struct __sk_buff
*skb
, struct ip6_srh_t
*srh
,
235 uint32_t srh_off
= (char *)srh
- (char *)(long)skb
->data
;
236 uint8_t len_remaining
, new_pad
;
237 uint32_t partial_srh_len
;
238 uint32_t pad_off
= 0;
239 uint32_t pad_size
= 0;
240 struct sr6_tlv_t tlv
;
245 err
= is_valid_tlv_boundary(skb
, srh
, &tlv_off
, &pad_size
, &pad_off
);
249 err
= bpf_skb_load_bytes(skb
, tlv_off
, &tlv
, sizeof(tlv
));
253 err
= bpf_lwt_seg6_adjust_srh(skb
, tlv_off
, -(sizeof(tlv
) + tlv
.len
));
257 pad_off
-= sizeof(tlv
) + tlv
.len
;
258 partial_srh_len
= pad_off
- srh_off
;
259 len_remaining
= partial_srh_len
% 8;
260 new_pad
= 8 - len_remaining
;
261 if (new_pad
== 1) // cannot pad for 1 byte only
263 else if (new_pad
== 8)
266 return update_tlv_pad(skb
, new_pad
, pad_size
, pad_off
);
269 static __always_inline
270 int has_egr_tlv(struct __sk_buff
*skb
, struct ip6_srh_t
*srh
)
272 int tlv_offset
= sizeof(struct ip6_t
) + sizeof(struct ip6_srh_t
) +
273 ((srh
->first_segment
+ 1) << 4);
274 struct sr6_tlv_t tlv
;
276 if (bpf_skb_load_bytes(skb
, tlv_offset
, &tlv
, sizeof(struct sr6_tlv_t
)))
279 if (tlv
.type
== SR6_TLV_EGRESS
&& tlv
.len
== 18) {
280 struct ip6_addr_t egr_addr
;
282 if (bpf_skb_load_bytes(skb
, tlv_offset
+ 4, &egr_addr
, 16))
285 // check if egress TLV value is correct
286 if (ntohll(egr_addr
.hi
) == 0xfd00000000000000 &&
287 ntohll(egr_addr
.lo
) == 0x4)
294 // This function will push a SRH with segments fd00::1, fd00::2, fd00::3,
297 int __encap_srh(struct __sk_buff
*skb
)
299 unsigned long long hi
= 0xfd00000000000000;
300 struct ip6_addr_t
*seg
;
301 struct ip6_srh_t
*srh
;
302 char srh_buf
[72]; // room for 4 segments
305 srh
= (struct ip6_srh_t
*)srh_buf
;
309 srh
->segments_left
= 3;
310 srh
->first_segment
= 3;
314 seg
= (struct ip6_addr_t
*)((char *)srh
+ sizeof(*srh
));
316 #pragma clang loop unroll(full)
317 for (unsigned long long lo
= 0; lo
< 4; lo
++) {
318 seg
->lo
= htonll(4 - lo
);
319 seg
->hi
= htonll(hi
);
320 seg
= (struct ip6_addr_t
*)((char *)seg
+ sizeof(*seg
));
323 err
= bpf_lwt_push_encap(skb
, 0, (void *)srh
, sizeof(srh_buf
));
330 // Add an Egress TLV fc00::4, add the flag A,
331 // and apply End.X action to fc42::1
333 int __add_egr_x(struct __sk_buff
*skb
)
335 unsigned long long hi
= 0xfc42000000000000;
336 unsigned long long lo
= 0x1;
337 struct ip6_srh_t
*srh
= get_srh(skb
);
338 uint8_t new_flags
= SR6_FLAG_ALERT
;
339 struct ip6_addr_t addr
;
345 uint8_t tlv
[20] = {2, 18, 0, 0, 0xfd, 0x0, 0x0, 0x0, 0x0, 0x0, 0x0, 0x0,
346 0x0, 0x0, 0x0, 0x0, 0x0, 0x0, 0x0, 0x4};
348 err
= add_tlv(skb
, srh
, (srh
->hdrlen
+1) << 3,
349 (struct sr6_tlv_t
*)&tlv
, 20);
353 offset
= sizeof(struct ip6_t
) + offsetof(struct ip6_srh_t
, flags
);
354 err
= bpf_lwt_seg6_store_bytes(skb
, offset
,
355 (void *)&new_flags
, sizeof(new_flags
));
359 addr
.lo
= htonll(lo
);
360 addr
.hi
= htonll(hi
);
361 err
= bpf_lwt_seg6_action(skb
, SEG6_LOCAL_ACTION_END_X
,
362 (void *)&addr
, sizeof(addr
));
368 // Pop the Egress TLV, reset the flags, change the tag 2442 and finally do a
371 int __pop_egr(struct __sk_buff
*skb
)
373 struct ip6_srh_t
*srh
= get_srh(skb
);
374 uint16_t new_tag
= bpf_htons(2442);
375 uint8_t new_flags
= 0;
381 if (srh
->flags
!= SR6_FLAG_ALERT
)
384 if (srh
->hdrlen
!= 11) // 4 segments + Egress TLV + Padding TLV
387 if (!has_egr_tlv(skb
, srh
))
390 err
= delete_tlv(skb
, srh
, 8 + (srh
->first_segment
+ 1) * 16);
394 offset
= sizeof(struct ip6_t
) + offsetof(struct ip6_srh_t
, flags
);
395 if (bpf_lwt_seg6_store_bytes(skb
, offset
, (void *)&new_flags
,
399 offset
= sizeof(struct ip6_t
) + offsetof(struct ip6_srh_t
, tag
);
400 if (bpf_lwt_seg6_store_bytes(skb
, offset
, (void *)&new_tag
,
407 // Inspect if the Egress TLV and flag have been removed, if the tag is correct,
408 // then apply a End.T action to reach the last segment
410 int __inspect_t(struct __sk_buff
*skb
)
412 struct ip6_srh_t
*srh
= get_srh(skb
);
422 if (srh
->tag
!= bpf_htons(2442))
425 if (srh
->hdrlen
!= 8) // 4 segments
428 err
= bpf_lwt_seg6_action(skb
, SEG6_LOCAL_ACTION_END_T
,
429 (void *)&table
, sizeof(table
));
437 char __license
[] SEC("license") = "GPL";