~ [ source navigation ] ~ [ diff markup ] ~ [ identifier search ] ~

TOMOYO Linux Cross Reference
Linux/tools/testing/selftests/bpf/progs/test_seg6_loop.c

Version: ~ [ linux-5.19-rc3 ] ~ [ linux-5.18.5 ] ~ [ linux-5.17.15 ] ~ [ linux-5.16.20 ] ~ [ linux-5.15.48 ] ~ [ linux-5.14.21 ] ~ [ linux-5.13.19 ] ~ [ linux-5.12.19 ] ~ [ linux-5.11.22 ] ~ [ linux-5.10.123 ] ~ [ linux-5.9.16 ] ~ [ linux-5.8.18 ] ~ [ linux-5.7.19 ] ~ [ linux-5.6.19 ] ~ [ linux-5.5.19 ] ~ [ linux-5.4.199 ] ~ [ linux-5.3.18 ] ~ [ linux-5.2.21 ] ~ [ linux-5.1.21 ] ~ [ linux-5.0.21 ] ~ [ linux-4.20.17 ] ~ [ linux-4.19.248 ] ~ [ linux-4.18.20 ] ~ [ linux-4.17.19 ] ~ [ linux-4.16.18 ] ~ [ linux-4.15.18 ] ~ [ linux-4.14.284 ] ~ [ linux-4.13.16 ] ~ [ linux-4.12.14 ] ~ [ linux-4.11.12 ] ~ [ linux-4.10.17 ] ~ [ linux-4.9.319 ] ~ [ linux-4.8.17 ] ~ [ linux-4.7.10 ] ~ [ linux-4.6.7 ] ~ [ linux-4.5.7 ] ~ [ linux-4.4.302 ] ~ [ linux-4.3.6 ] ~ [ linux-4.2.8 ] ~ [ linux-4.1.52 ] ~ [ linux-4.0.9 ] ~ [ linux-3.10.108 ] ~ [ linux-2.6.32.71 ] ~ [ linux-2.6.0 ] ~ [ linux-2.4.37.11 ] ~ [ unix-v6-master ] ~ [ ccs-tools-1.8.9 ] ~ [ policy-sample ] ~
Architecture: ~ [ i386 ] ~ [ alpha ] ~ [ m68k ] ~ [ mips ] ~ [ ppc ] ~ [ sparc ] ~ [ sparc64 ] ~

  1 #include <stddef.h>
  2 #include <inttypes.h>
  3 #include <errno.h>
  4 #include <linux/seg6_local.h>
  5 #include <linux/bpf.h>
  6 #include <bpf/bpf_helpers.h>
  7 #include <bpf/bpf_endian.h>
  8 
  9 /* Packet parsing state machine helpers. */
 10 #define cursor_advance(_cursor, _len) \
 11         ({ void *_tmp = _cursor; _cursor += _len; _tmp; })
 12 
 13 #define SR6_FLAG_ALERT (1 << 4)
 14 
 15 #define BPF_PACKET_HEADER __attribute__((packed))
 16 
 17 struct ip6_t {
 18         unsigned int ver:4;
 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;
 28 } BPF_PACKET_HEADER;
 29 
 30 struct ip6_addr_t {
 31         unsigned long long hi;
 32         unsigned long long lo;
 33 } BPF_PACKET_HEADER;
 34 
 35 struct ip6_srh_t {
 36         unsigned char nexthdr;
 37         unsigned char hdrlen;
 38         unsigned char type;
 39         unsigned char segments_left;
 40         unsigned char first_segment;
 41         unsigned char flags;
 42         unsigned short tag;
 43 
 44         struct ip6_addr_t segments[0];
 45 } BPF_PACKET_HEADER;
 46 
 47 struct sr6_tlv_t {
 48         unsigned char type;
 49         unsigned char len;
 50         unsigned char value[0];
 51 } BPF_PACKET_HEADER;
 52 
 53 static __always_inline struct ip6_srh_t *get_srh(struct __sk_buff *skb)
 54 {
 55         void *cursor, *data_end;
 56         struct ip6_srh_t *srh;
 57         struct ip6_t *ip;
 58         uint8_t *ipver;
 59 
 60         data_end = (void *)(long)skb->data_end;
 61         cursor = (void *)(long)skb->data;
 62         ipver = (uint8_t *)cursor;
 63 
 64         if ((void *)ipver + sizeof(*ipver) > data_end)
 65                 return NULL;
 66 
 67         if ((*ipver >> 4) != 6)
 68                 return NULL;
 69 
 70         ip = cursor_advance(cursor, sizeof(*ip));
 71         if ((void *)ip + sizeof(*ip) > data_end)
 72                 return NULL;
 73 
 74         if (ip->next_header != 43)
 75                 return NULL;
 76 
 77         srh = cursor_advance(cursor, sizeof(*srh));
 78         if ((void *)srh + sizeof(*srh) > data_end)
 79                 return NULL;
 80 
 81         if (srh->type != 4)
 82                 return NULL;
 83 
 84         return srh;
 85 }
 86 
 87 static __always_inline int update_tlv_pad(struct __sk_buff *skb,
 88                                           uint32_t new_pad, uint32_t old_pad,
 89                                           uint32_t pad_off)
 90 {
 91         int err;
 92 
 93         if (new_pad != old_pad) {
 94                 err = bpf_lwt_seg6_adjust_srh(skb, pad_off,
 95                                           (int) new_pad - (int) old_pad);
 96                 if (err)
 97                         return err;
 98         }
 99 
100         if (new_pad > 0) {
101                 char pad_tlv_buf[16] = {0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0,
102                                         0, 0, 0};
103                 struct sr6_tlv_t *pad_tlv = (struct sr6_tlv_t *) pad_tlv_buf;
104 
105                 pad_tlv->type = SR6_TLV_PADDING;
106                 pad_tlv->len = new_pad - 2;
107 
108                 err = bpf_lwt_seg6_store_bytes(skb, pad_off,
109                                                (void *)pad_tlv_buf, new_pad);
110                 if (err)
111                         return err;
112         }
113 
114         return 0;
115 }
116 
117 static __always_inline int is_valid_tlv_boundary(struct __sk_buff *skb,
118                                                  struct ip6_srh_t *srh,
119                                                  uint32_t *tlv_off,
120                                                  uint32_t *pad_size,
121                                                  uint32_t *pad_off)
122 {
123         uint32_t srh_off, cur_off;
124         int offset_valid = 0;
125         int err;
126 
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);
131 
132         *pad_off = 0;
133 
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;
140 
141                 if (cur_off == *tlv_off)
142                         offset_valid = 1;
143 
144                 if (cur_off >= srh_off + ((srh->hdrlen + 1) << 3))
145                         break;
146 
147                 err = bpf_skb_load_bytes(skb, cur_off, &tlv, sizeof(tlv));
148                 if (err)
149                         return err;
150 
151                 if (tlv.type == SR6_TLV_PADDING) {
152                         *pad_size = tlv.len + sizeof(tlv);
153                         *pad_off = cur_off;
154 
155                         if (*tlv_off == srh_off) {
156                                 *tlv_off = cur_off;
157                                 offset_valid = 1;
158                         }
159                         break;
160 
161                 } else if (tlv.type == SR6_TLV_HMAC) {
162                         break;
163                 }
164 
165                 cur_off += sizeof(tlv) + tlv.len;
166         } // we reached the padding or HMAC TLVs, or the end of the SRH
167 
168         if (*pad_off == 0)
169                 *pad_off = cur_off;
170 
171         if (*tlv_off == -1)
172                 *tlv_off = cur_off;
173         else if (!offset_valid)
174                 return -EINVAL;
175 
176         return 0;
177 }
178 
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)
182 {
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;
188         int err;
189 
190         if (tlv_off != -1)
191                 tlv_off += srh_off;
192 
193         if (itlv->type == SR6_TLV_PADDING || itlv->type == SR6_TLV_HMAC)
194                 return -EINVAL;
195 
196         err = is_valid_tlv_boundary(skb, srh, &tlv_off, &pad_size, &pad_off);
197         if (err)
198                 return err;
199 
200         err = bpf_lwt_seg6_adjust_srh(skb, tlv_off, sizeof(*itlv) + itlv->len);
201         if (err)
202                 return err;
203 
204         err = bpf_lwt_seg6_store_bytes(skb, tlv_off, (void *)itlv, tlv_size);
205         if (err)
206                 return err;
207 
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;
214 
215         if (new_pad == 1) // cannot pad for 1 byte only
216                 new_pad = 9;
217         else if (new_pad == 8)
218                 new_pad = 0;
219 
220         return update_tlv_pad(skb, new_pad, pad_size, pad_off);
221 }
222 
223 // Add an Egress TLV fc00::4, add the flag A,
224 // and apply End.X action to fc42::1
225 SEC("lwt_seg6local")
226 int __add_egr_x(struct __sk_buff *skb)
227 {
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;
233         int err, offset;
234 
235         if (srh == NULL)
236                 return BPF_DROP;
237 
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};
240 
241         err = add_tlv(skb, srh, (srh->hdrlen+1) << 3,
242                       (struct sr6_tlv_t *)&tlv, 20);
243         if (err)
244                 return BPF_DROP;
245 
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));
249         if (err)
250                 return BPF_DROP;
251 
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));
256         if (err)
257                 return BPF_DROP;
258         return BPF_REDIRECT;
259 }
260 char __license[] SEC("license") = "GPL";
261 

~ [ source navigation ] ~ [ diff markup ] ~ [ identifier search ] ~

kernel.org | git.kernel.org | LWN.net | Project Home | Wiki (Japanese) | Wiki (English) | SVN repository | Mail admin

Linux® is a registered trademark of Linus Torvalds in the United States and other countries.
TOMOYO® is a registered trademark of NTT DATA CORPORATION.

osdn.jp