4 #include <linux/seg6_local.h>
6 #include <bpf/bpf_helpers.h>
7 #include <bpf/bpf_endian.h>
9 /* Packet parsing state machine helpers. */
10 #define cursor_advance(_cursor, _len) \
11 ({ void *_tmp = _cursor; _cursor += _len; _tmp; })
13 #define SR6_FLAG_ALERT (1 << 4)
15 #define BPF_PACKET_HEADER __attribute__((packed))
19 unsigned int priority
:8;
20 unsigned int flow_label
:20;
21 unsigned short payload_len
;
22 unsigned char next_header
;
23 unsigned char hop_limit
;
24 unsigned long long src_hi
;
25 unsigned long long src_lo
;
26 unsigned long long dst_hi
;
27 unsigned long long dst_lo
;
31 unsigned long long hi
;
32 unsigned long long lo
;
36 unsigned char nexthdr
;
39 unsigned char segments_left
;
40 unsigned char first_segment
;
44 struct ip6_addr_t segments
[0];
50 unsigned char value
[0];
53 static __always_inline
struct ip6_srh_t
*get_srh(struct __sk_buff
*skb
)
55 void *cursor
, *data_end
;
56 struct ip6_srh_t
*srh
;
60 data_end
= (void *)(long)skb
->data_end
;
61 cursor
= (void *)(long)skb
->data
;
62 ipver
= (uint8_t *)cursor
;
64 if ((void *)ipver
+ sizeof(*ipver
) > data_end
)
67 if ((*ipver
>> 4) != 6)
70 ip
= cursor_advance(cursor
, sizeof(*ip
));
71 if ((void *)ip
+ sizeof(*ip
) > data_end
)
74 if (ip
->next_header
!= 43)
77 srh
= cursor_advance(cursor
, sizeof(*srh
));
78 if ((void *)srh
+ sizeof(*srh
) > data_end
)
87 static __always_inline
int update_tlv_pad(struct __sk_buff
*skb
,
88 uint32_t new_pad
, uint32_t old_pad
,
93 if (new_pad
!= old_pad
) {
94 err
= bpf_lwt_seg6_adjust_srh(skb
, pad_off
,
95 (int) new_pad
- (int) old_pad
);
101 char pad_tlv_buf
[16] = {0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0,
103 struct sr6_tlv_t
*pad_tlv
= (struct sr6_tlv_t
*) pad_tlv_buf
;
105 pad_tlv
->type
= SR6_TLV_PADDING
;
106 pad_tlv
->len
= new_pad
- 2;
108 err
= bpf_lwt_seg6_store_bytes(skb
, pad_off
,
109 (void *)pad_tlv_buf
, new_pad
);
117 static __always_inline
int is_valid_tlv_boundary(struct __sk_buff
*skb
,
118 struct ip6_srh_t
*srh
,
123 uint32_t srh_off
, cur_off
;
124 int offset_valid
= 0;
127 srh_off
= (char *)srh
- (char *)(long)skb
->data
;
128 // cur_off = end of segments, start of possible TLVs
129 cur_off
= srh_off
+ sizeof(*srh
) +
130 sizeof(struct ip6_addr_t
) * (srh
->first_segment
+ 1);
134 // we can only go as far as ~10 TLVs due to the BPF max stack size
135 // workaround: define induction variable "i" as "long" instead
136 // of "int" to prevent alu32 sub-register spilling.
137 #pragma clang loop unroll(disable)
138 for (long i
= 0; i
< 100; i
++) {
139 struct sr6_tlv_t tlv
;
141 if (cur_off
== *tlv_off
)
144 if (cur_off
>= srh_off
+ ((srh
->hdrlen
+ 1) << 3))
147 err
= bpf_skb_load_bytes(skb
, cur_off
, &tlv
, sizeof(tlv
));
151 if (tlv
.type
== SR6_TLV_PADDING
) {
152 *pad_size
= tlv
.len
+ sizeof(tlv
);
155 if (*tlv_off
== srh_off
) {
161 } else if (tlv
.type
== SR6_TLV_HMAC
) {
165 cur_off
+= sizeof(tlv
) + tlv
.len
;
166 } // we reached the padding or HMAC TLVs, or the end of the SRH
173 else if (!offset_valid
)
179 static __always_inline
int add_tlv(struct __sk_buff
*skb
,
180 struct ip6_srh_t
*srh
, uint32_t tlv_off
,
181 struct sr6_tlv_t
*itlv
, uint8_t tlv_size
)
183 uint32_t srh_off
= (char *)srh
- (char *)(long)skb
->data
;
184 uint8_t len_remaining
, new_pad
;
185 uint32_t pad_off
= 0;
186 uint32_t pad_size
= 0;
187 uint32_t partial_srh_len
;
193 if (itlv
->type
== SR6_TLV_PADDING
|| itlv
->type
== SR6_TLV_HMAC
)
196 err
= is_valid_tlv_boundary(skb
, srh
, &tlv_off
, &pad_size
, &pad_off
);
200 err
= bpf_lwt_seg6_adjust_srh(skb
, tlv_off
, sizeof(*itlv
) + itlv
->len
);
204 err
= bpf_lwt_seg6_store_bytes(skb
, tlv_off
, (void *)itlv
, tlv_size
);
208 // the following can't be moved inside update_tlv_pad because the
209 // bpf verifier has some issues with it
210 pad_off
+= sizeof(*itlv
) + itlv
->len
;
211 partial_srh_len
= pad_off
- srh_off
;
212 len_remaining
= partial_srh_len
% 8;
213 new_pad
= 8 - len_remaining
;
215 if (new_pad
== 1) // cannot pad for 1 byte only
217 else if (new_pad
== 8)
220 return update_tlv_pad(skb
, new_pad
, pad_size
, pad_off
);
223 // Add an Egress TLV fc00::4, add the flag A,
224 // and apply End.X action to fc42::1
226 int __add_egr_x(struct __sk_buff
*skb
)
228 unsigned long long hi
= 0xfc42000000000000;
229 unsigned long long lo
= 0x1;
230 struct ip6_srh_t
*srh
= get_srh(skb
);
231 uint8_t new_flags
= SR6_FLAG_ALERT
;
232 struct ip6_addr_t addr
;
238 uint8_t tlv
[20] = {2, 18, 0, 0, 0xfd, 0x0, 0x0, 0x0, 0x0, 0x0, 0x0, 0x0,
239 0x0, 0x0, 0x0, 0x0, 0x0, 0x0, 0x0, 0x4};
241 err
= add_tlv(skb
, srh
, (srh
->hdrlen
+1) << 3,
242 (struct sr6_tlv_t
*)&tlv
, 20);
246 offset
= sizeof(struct ip6_t
) + offsetof(struct ip6_srh_t
, flags
);
247 err
= bpf_lwt_seg6_store_bytes(skb
, offset
,
248 (void *)&new_flags
, sizeof(new_flags
));
252 addr
.lo
= bpf_cpu_to_be64(lo
);
253 addr
.hi
= bpf_cpu_to_be64(hi
);
254 err
= bpf_lwt_seg6_action(skb
, SEG6_LOCAL_ACTION_END_X
,
255 (void *)&addr
, sizeof(addr
));
260 char __license
[] SEC("license") = "GPL";