]>
Commit | Line | Data |
---|---|---|
1e529305 RP |
1 | /* |
2 | * iproute_lwtunnel.c | |
3 | * | |
4 | * This program is free software; you can redistribute it and/or | |
5 | * modify it under the terms of the GNU General Public License | |
6 | * as published by the Free Software Foundation; either version | |
7 | * 2 of the License, or (at your option) any later version. | |
8 | * | |
9 | * Authors: Roopa Prabhu, <roopa@cumulusnetworks.com> | |
56f5daac | 10 | * Thomas Graf <tgraf@suug.ch> |
1e529305 RP |
11 | * |
12 | */ | |
13 | ||
14 | #include <stdio.h> | |
15 | #include <stdlib.h> | |
16 | #include <unistd.h> | |
17 | #include <fcntl.h> | |
18 | #include <string.h> | |
5866bddd | 19 | #include <linux/ila.h> |
1e529305 RP |
20 | #include <linux/lwtunnel.h> |
21 | #include <linux/mpls_iptunnel.h> | |
22 | #include <errno.h> | |
23 | ||
24 | #include "rt_names.h" | |
b15f440e | 25 | #include "bpf_util.h" |
f7af0dc5 SP |
26 | #include "utils.h" |
27 | #include "ip_common.h" | |
010260a7 | 28 | #include "ila_common.h" |
1e529305 | 29 | |
e4319590 DL |
30 | #include <linux/seg6.h> |
31 | #include <linux/seg6_iptunnel.h> | |
32 | #include <linux/seg6_hmac.h> | |
8db158b9 DL |
33 | #include <linux/seg6_local.h> |
34 | #include <net/if.h> | |
e4319590 | 35 | |
1e529305 RP |
36 | static const char *format_encap_type(int type) |
37 | { | |
38 | switch (type) { | |
39 | case LWTUNNEL_ENCAP_MPLS: | |
40 | return "mpls"; | |
41 | case LWTUNNEL_ENCAP_IP: | |
42 | return "ip"; | |
43 | case LWTUNNEL_ENCAP_IP6: | |
44 | return "ip6"; | |
5866bddd TH |
45 | case LWTUNNEL_ENCAP_ILA: |
46 | return "ila"; | |
b15f440e TG |
47 | case LWTUNNEL_ENCAP_BPF: |
48 | return "bpf"; | |
e8493916 DL |
49 | case LWTUNNEL_ENCAP_SEG6: |
50 | return "seg6"; | |
8db158b9 DL |
51 | case LWTUNNEL_ENCAP_SEG6_LOCAL: |
52 | return "seg6local"; | |
1e529305 RP |
53 | default: |
54 | return "unknown"; | |
55 | } | |
56 | } | |
57 | ||
b15f440e TG |
58 | static void encap_type_usage(void) |
59 | { | |
60 | int i; | |
61 | ||
62 | fprintf(stderr, "Usage: ip route ... encap TYPE [ OPTIONS ] [...]\n"); | |
63 | ||
64 | for (i = 1; i <= LWTUNNEL_ENCAP_MAX; i++) | |
65 | fprintf(stderr, "%s %s\n", format_encap_type(i), | |
66 | i == 1 ? "TYPE := " : " "); | |
67 | ||
68 | exit(-1); | |
69 | } | |
70 | ||
71 | static int read_encap_type(const char *name) | |
72 | { | |
73 | if (strcmp(name, "mpls") == 0) | |
74 | return LWTUNNEL_ENCAP_MPLS; | |
75 | else if (strcmp(name, "ip") == 0) | |
76 | return LWTUNNEL_ENCAP_IP; | |
77 | else if (strcmp(name, "ip6") == 0) | |
78 | return LWTUNNEL_ENCAP_IP6; | |
79 | else if (strcmp(name, "ila") == 0) | |
80 | return LWTUNNEL_ENCAP_ILA; | |
81 | else if (strcmp(name, "bpf") == 0) | |
82 | return LWTUNNEL_ENCAP_BPF; | |
e8493916 DL |
83 | else if (strcmp(name, "seg6") == 0) |
84 | return LWTUNNEL_ENCAP_SEG6; | |
8db158b9 DL |
85 | else if (strcmp(name, "seg6local") == 0) |
86 | return LWTUNNEL_ENCAP_SEG6_LOCAL; | |
b15f440e TG |
87 | else if (strcmp(name, "help") == 0) |
88 | encap_type_usage(); | |
89 | ||
90 | return LWTUNNEL_ENCAP_NONE; | |
91 | } | |
92 | ||
00e76d4d | 93 | static void print_srh(FILE *fp, struct ipv6_sr_hdr *srh) |
e8493916 | 94 | { |
e8493916 DL |
95 | int i; |
96 | ||
663c3cb2 SH |
97 | if (is_json_context()) |
98 | open_json_array(PRINT_JSON, "segs"); | |
99 | else | |
100 | fprintf(fp, "segs %d [ ", srh->first_segment + 1); | |
e8493916 DL |
101 | |
102 | for (i = srh->first_segment; i >= 0; i--) | |
663c3cb2 SH |
103 | print_color_string(PRINT_ANY, COLOR_INET6, |
104 | NULL, "%s ", | |
105 | rt_addr_n2a(AF_INET6, 16, &srh->segments[i])); | |
e8493916 | 106 | |
663c3cb2 SH |
107 | if (is_json_context()) |
108 | close_json_array(PRINT_JSON, NULL); | |
109 | else | |
110 | fprintf(fp, "] "); | |
e8493916 DL |
111 | |
112 | if (sr_has_hmac(srh)) { | |
113 | unsigned int offset = ((srh->hdrlen + 1) << 3) - 40; | |
114 | struct sr6_tlv_hmac *tlv; | |
115 | ||
116 | tlv = (struct sr6_tlv_hmac *)((char *)srh + offset); | |
663c3cb2 | 117 | print_0xhex(PRINT_ANY, "hmac", |
90c5c969 | 118 | "hmac %llX ", ntohl(tlv->hmackeyid)); |
e8493916 DL |
119 | } |
120 | } | |
121 | ||
cf87da41 DL |
122 | static const char *seg6_mode_types[] = { |
123 | [SEG6_IPTUN_MODE_INLINE] = "inline", | |
124 | [SEG6_IPTUN_MODE_ENCAP] = "encap", | |
125 | [SEG6_IPTUN_MODE_L2ENCAP] = "l2encap", | |
126 | }; | |
127 | ||
128 | static const char *format_seg6mode_type(int mode) | |
129 | { | |
130 | if (mode < 0 || mode > ARRAY_SIZE(seg6_mode_types)) | |
131 | return "<unknown>"; | |
132 | ||
133 | return seg6_mode_types[mode]; | |
134 | } | |
135 | ||
136 | static int read_seg6mode_type(const char *mode) | |
137 | { | |
138 | int i; | |
139 | ||
140 | for (i = 0; i < ARRAY_SIZE(seg6_mode_types); i++) { | |
141 | if (strcmp(mode, seg6_mode_types[i]) == 0) | |
142 | return i; | |
143 | } | |
144 | ||
145 | return -1; | |
146 | } | |
147 | ||
00e76d4d DL |
148 | static void print_encap_seg6(FILE *fp, struct rtattr *encap) |
149 | { | |
150 | struct rtattr *tb[SEG6_IPTUNNEL_MAX+1]; | |
151 | struct seg6_iptunnel_encap *tuninfo; | |
152 | ||
153 | parse_rtattr_nested(tb, SEG6_IPTUNNEL_MAX, encap); | |
154 | ||
155 | if (!tb[SEG6_IPTUNNEL_SRH]) | |
156 | return; | |
157 | ||
158 | tuninfo = RTA_DATA(tb[SEG6_IPTUNNEL_SRH]); | |
663c3cb2 SH |
159 | print_string(PRINT_ANY, "mode", |
160 | "mode %s ", format_seg6mode_type(tuninfo->mode)); | |
00e76d4d DL |
161 | |
162 | print_srh(fp, tuninfo->srh); | |
163 | } | |
164 | ||
8db158b9 DL |
165 | static const char *seg6_action_names[SEG6_LOCAL_ACTION_MAX + 1] = { |
166 | [SEG6_LOCAL_ACTION_END] = "End", | |
167 | [SEG6_LOCAL_ACTION_END_X] = "End.X", | |
168 | [SEG6_LOCAL_ACTION_END_T] = "End.T", | |
169 | [SEG6_LOCAL_ACTION_END_DX2] = "End.DX2", | |
170 | [SEG6_LOCAL_ACTION_END_DX6] = "End.DX6", | |
171 | [SEG6_LOCAL_ACTION_END_DX4] = "End.DX4", | |
172 | [SEG6_LOCAL_ACTION_END_DT6] = "End.DT6", | |
173 | [SEG6_LOCAL_ACTION_END_DT4] = "End.DT4", | |
174 | [SEG6_LOCAL_ACTION_END_B6] = "End.B6", | |
175 | [SEG6_LOCAL_ACTION_END_B6_ENCAP] = "End.B6.Encaps", | |
176 | [SEG6_LOCAL_ACTION_END_BM] = "End.BM", | |
177 | [SEG6_LOCAL_ACTION_END_S] = "End.S", | |
178 | [SEG6_LOCAL_ACTION_END_AS] = "End.AS", | |
179 | [SEG6_LOCAL_ACTION_END_AM] = "End.AM", | |
04cb3c0d | 180 | [SEG6_LOCAL_ACTION_END_BPF] = "End.BPF", |
8db158b9 DL |
181 | }; |
182 | ||
183 | static const char *format_action_type(int action) | |
184 | { | |
185 | if (action < 0 || action > SEG6_LOCAL_ACTION_MAX) | |
186 | return "<invalid>"; | |
187 | ||
188 | return seg6_action_names[action] ?: "<unknown>"; | |
189 | } | |
190 | ||
191 | static int read_action_type(const char *name) | |
192 | { | |
193 | int i; | |
194 | ||
195 | for (i = 0; i < SEG6_LOCAL_ACTION_MAX + 1; i++) { | |
196 | if (!seg6_action_names[i]) | |
197 | continue; | |
198 | ||
199 | if (strcmp(seg6_action_names[i], name) == 0) | |
200 | return i; | |
201 | } | |
202 | ||
203 | return SEG6_LOCAL_ACTION_UNSPEC; | |
204 | } | |
205 | ||
04cb3c0d MX |
206 | static void print_encap_bpf_prog(FILE *fp, struct rtattr *encap, |
207 | const char *str) | |
208 | { | |
209 | struct rtattr *tb[LWT_BPF_PROG_MAX+1]; | |
210 | const char *progname = NULL; | |
211 | ||
212 | parse_rtattr_nested(tb, LWT_BPF_PROG_MAX, encap); | |
213 | ||
214 | if (tb[LWT_BPF_PROG_NAME]) | |
215 | progname = rta_getattr_str(tb[LWT_BPF_PROG_NAME]); | |
216 | ||
217 | if (is_json_context()) | |
218 | print_string(PRINT_JSON, str, NULL, | |
219 | progname ? : "<unknown>"); | |
220 | else { | |
221 | fprintf(fp, "%s ", str); | |
222 | if (progname) | |
223 | fprintf(fp, "%s ", progname); | |
224 | } | |
225 | } | |
226 | ||
8db158b9 DL |
227 | static void print_encap_seg6local(FILE *fp, struct rtattr *encap) |
228 | { | |
229 | struct rtattr *tb[SEG6_LOCAL_MAX + 1]; | |
8db158b9 DL |
230 | int action; |
231 | ||
232 | parse_rtattr_nested(tb, SEG6_LOCAL_MAX, encap); | |
233 | ||
234 | if (!tb[SEG6_LOCAL_ACTION]) | |
235 | return; | |
236 | ||
237 | action = rta_getattr_u32(tb[SEG6_LOCAL_ACTION]); | |
238 | ||
663c3cb2 SH |
239 | print_string(PRINT_ANY, "action", |
240 | "action %s ", format_action_type(action)); | |
8db158b9 DL |
241 | |
242 | if (tb[SEG6_LOCAL_SRH]) { | |
663c3cb2 | 243 | open_json_object("srh"); |
8db158b9 | 244 | print_srh(fp, RTA_DATA(tb[SEG6_LOCAL_SRH])); |
663c3cb2 | 245 | close_json_object(); |
8db158b9 DL |
246 | } |
247 | ||
248 | if (tb[SEG6_LOCAL_TABLE]) | |
663c3cb2 SH |
249 | print_uint(PRINT_ANY, "table", |
250 | "table %u ", rta_getattr_u32(tb[SEG6_LOCAL_TABLE])); | |
8db158b9 DL |
251 | |
252 | if (tb[SEG6_LOCAL_NH4]) { | |
663c3cb2 SH |
253 | print_string(PRINT_ANY, "nh4", |
254 | "nh4 %s ", rt_addr_n2a_rta(AF_INET, tb[SEG6_LOCAL_NH4])); | |
8db158b9 DL |
255 | } |
256 | ||
257 | if (tb[SEG6_LOCAL_NH6]) { | |
663c3cb2 SH |
258 | print_string(PRINT_ANY, "nh6", |
259 | "nh6 %s ", rt_addr_n2a_rta(AF_INET6, tb[SEG6_LOCAL_NH6])); | |
8db158b9 DL |
260 | } |
261 | ||
262 | if (tb[SEG6_LOCAL_IIF]) { | |
263 | int iif = rta_getattr_u32(tb[SEG6_LOCAL_IIF]); | |
264 | ||
663c3cb2 SH |
265 | print_string(PRINT_ANY, "iif", |
266 | "iif %s ", ll_index_to_name(iif)); | |
8db158b9 DL |
267 | } |
268 | ||
269 | if (tb[SEG6_LOCAL_OIF]) { | |
270 | int oif = rta_getattr_u32(tb[SEG6_LOCAL_OIF]); | |
271 | ||
663c3cb2 SH |
272 | print_string(PRINT_ANY, "oif", |
273 | "oif %s ", ll_index_to_name(oif)); | |
8db158b9 | 274 | } |
04cb3c0d MX |
275 | |
276 | if (tb[SEG6_LOCAL_BPF]) | |
277 | print_encap_bpf_prog(fp, tb[SEG6_LOCAL_BPF], "endpoint"); | |
8db158b9 DL |
278 | } |
279 | ||
1e529305 RP |
280 | static void print_encap_mpls(FILE *fp, struct rtattr *encap) |
281 | { | |
282 | struct rtattr *tb[MPLS_IPTUNNEL_MAX+1]; | |
1e529305 RP |
283 | |
284 | parse_rtattr_nested(tb, MPLS_IPTUNNEL_MAX, encap); | |
285 | ||
286 | if (tb[MPLS_IPTUNNEL_DST]) | |
663c3cb2 | 287 | print_string(PRINT_ANY, "dst", " %s ", |
b723368c | 288 | format_host_rta(AF_MPLS, tb[MPLS_IPTUNNEL_DST])); |
9688cf3b | 289 | if (tb[MPLS_IPTUNNEL_TTL]) |
663c3cb2 | 290 | print_uint(PRINT_ANY, "ttl", "ttl %u ", |
9688cf3b | 291 | rta_getattr_u8(tb[MPLS_IPTUNNEL_TTL])); |
1e529305 RP |
292 | } |
293 | ||
294 | static void print_encap_ip(FILE *fp, struct rtattr *encap) | |
295 | { | |
296 | struct rtattr *tb[LWTUNNEL_IP_MAX+1]; | |
1e529305 RP |
297 | |
298 | parse_rtattr_nested(tb, LWTUNNEL_IP_MAX, encap); | |
299 | ||
300 | if (tb[LWTUNNEL_IP_ID]) | |
4db2ff0d | 301 | print_u64(PRINT_ANY, "id", "id %llu ", |
663c3cb2 | 302 | ntohll(rta_getattr_u64(tb[LWTUNNEL_IP_ID]))); |
1e529305 RP |
303 | |
304 | if (tb[LWTUNNEL_IP_SRC]) | |
663c3cb2 SH |
305 | print_color_string(PRINT_ANY, COLOR_INET, |
306 | "src", "src %s ", | |
307 | rt_addr_n2a_rta(AF_INET, tb[LWTUNNEL_IP_SRC])); | |
1e529305 RP |
308 | |
309 | if (tb[LWTUNNEL_IP_DST]) | |
663c3cb2 SH |
310 | print_color_string(PRINT_ANY, COLOR_INET, |
311 | "dst", "dst %s ", | |
312 | rt_addr_n2a_rta(AF_INET, tb[LWTUNNEL_IP_DST])); | |
1e529305 RP |
313 | |
314 | if (tb[LWTUNNEL_IP_TTL]) | |
663c3cb2 SH |
315 | print_uint(PRINT_ANY, "ttl", |
316 | "ttl %u ", rta_getattr_u8(tb[LWTUNNEL_IP_TTL])); | |
1e529305 RP |
317 | |
318 | if (tb[LWTUNNEL_IP_TOS]) | |
663c3cb2 SH |
319 | print_uint(PRINT_ANY, "tos", |
320 | "tos %d ", rta_getattr_u8(tb[LWTUNNEL_IP_TOS])); | |
1e529305 RP |
321 | } |
322 | ||
5866bddd TH |
323 | static void print_encap_ila(FILE *fp, struct rtattr *encap) |
324 | { | |
325 | struct rtattr *tb[ILA_ATTR_MAX+1]; | |
326 | ||
327 | parse_rtattr_nested(tb, ILA_ATTR_MAX, encap); | |
328 | ||
329 | if (tb[ILA_ATTR_LOCATOR]) { | |
330 | char abuf[ADDR64_BUF_SIZE]; | |
331 | ||
9f1370c0 | 332 | addr64_n2a(rta_getattr_u64(tb[ILA_ATTR_LOCATOR]), |
5866bddd | 333 | abuf, sizeof(abuf)); |
663c3cb2 SH |
334 | print_string(PRINT_ANY, "locator", |
335 | " %s ", abuf); | |
5866bddd | 336 | } |
ed67f838 TH |
337 | |
338 | if (tb[ILA_ATTR_CSUM_MODE]) | |
663c3cb2 SH |
339 | print_string(PRINT_ANY, "csum_mode", |
340 | " csum-mode %s ", | |
341 | ila_csum_mode2name(rta_getattr_u8(tb[ILA_ATTR_CSUM_MODE]))); | |
86905c8f TH |
342 | |
343 | if (tb[ILA_ATTR_IDENT_TYPE]) | |
663c3cb2 SH |
344 | print_string(PRINT_ANY, "ident_type", |
345 | " ident-type %s ", | |
346 | ila_ident_type2name(rta_getattr_u8(tb[ILA_ATTR_IDENT_TYPE]))); | |
86905c8f TH |
347 | |
348 | if (tb[ILA_ATTR_HOOK_TYPE]) | |
663c3cb2 SH |
349 | print_string(PRINT_ANY, "hook_type", |
350 | " hook-type %s ", | |
351 | ila_hook_type2name(rta_getattr_u8(tb[ILA_ATTR_HOOK_TYPE]))); | |
5866bddd TH |
352 | } |
353 | ||
d95cdcf5 PA |
354 | static void print_encap_ip6(FILE *fp, struct rtattr *encap) |
355 | { | |
356 | struct rtattr *tb[LWTUNNEL_IP6_MAX+1]; | |
d95cdcf5 PA |
357 | |
358 | parse_rtattr_nested(tb, LWTUNNEL_IP6_MAX, encap); | |
359 | ||
360 | if (tb[LWTUNNEL_IP6_ID]) | |
4db2ff0d | 361 | print_u64(PRINT_ANY, "id", "id %llu ", |
663c3cb2 | 362 | ntohll(rta_getattr_u64(tb[LWTUNNEL_IP6_ID]))); |
d95cdcf5 PA |
363 | |
364 | if (tb[LWTUNNEL_IP6_SRC]) | |
663c3cb2 SH |
365 | print_color_string(PRINT_ANY, COLOR_INET6, |
366 | "src", "src %s ", | |
367 | rt_addr_n2a_rta(AF_INET6, tb[LWTUNNEL_IP6_SRC])); | |
d95cdcf5 PA |
368 | |
369 | if (tb[LWTUNNEL_IP6_DST]) | |
663c3cb2 SH |
370 | print_color_string(PRINT_ANY, COLOR_INET6, |
371 | "dst", "dst %s ", | |
372 | rt_addr_n2a_rta(AF_INET6, tb[LWTUNNEL_IP6_DST])); | |
d95cdcf5 PA |
373 | |
374 | if (tb[LWTUNNEL_IP6_HOPLIMIT]) | |
4db2ff0d | 375 | print_u64(PRINT_ANY, "hoplimit", |
663c3cb2 SH |
376 | "hoplimit %u ", |
377 | rta_getattr_u8(tb[LWTUNNEL_IP6_HOPLIMIT])); | |
d95cdcf5 PA |
378 | |
379 | if (tb[LWTUNNEL_IP6_TC]) | |
663c3cb2 SH |
380 | print_uint(PRINT_ANY, "tc", |
381 | "tc %u ", rta_getattr_u8(tb[LWTUNNEL_IP6_TC])); | |
d95cdcf5 PA |
382 | } |
383 | ||
b15f440e TG |
384 | static void print_encap_bpf(FILE *fp, struct rtattr *encap) |
385 | { | |
386 | struct rtattr *tb[LWT_BPF_MAX+1]; | |
387 | ||
388 | parse_rtattr_nested(tb, LWT_BPF_MAX, encap); | |
389 | ||
390 | if (tb[LWT_BPF_IN]) | |
391 | print_encap_bpf_prog(fp, tb[LWT_BPF_IN], "in"); | |
392 | if (tb[LWT_BPF_OUT]) | |
393 | print_encap_bpf_prog(fp, tb[LWT_BPF_OUT], "out"); | |
394 | if (tb[LWT_BPF_XMIT]) | |
395 | print_encap_bpf_prog(fp, tb[LWT_BPF_XMIT], "xmit"); | |
396 | if (tb[LWT_BPF_XMIT_HEADROOM]) | |
663c3cb2 SH |
397 | print_uint(PRINT_ANY, "headroom", |
398 | " %u ", rta_getattr_u32(tb[LWT_BPF_XMIT_HEADROOM])); | |
b15f440e TG |
399 | } |
400 | ||
1e529305 RP |
401 | void lwt_print_encap(FILE *fp, struct rtattr *encap_type, |
402 | struct rtattr *encap) | |
403 | { | |
404 | int et; | |
405 | ||
406 | if (!encap_type) | |
407 | return; | |
408 | ||
409 | et = rta_getattr_u16(encap_type); | |
410 | ||
663c3cb2 | 411 | print_string(PRINT_ANY, "encap", " encap %s ", format_encap_type(et)); |
1e529305 RP |
412 | |
413 | switch (et) { | |
414 | case LWTUNNEL_ENCAP_MPLS: | |
415 | print_encap_mpls(fp, encap); | |
416 | break; | |
417 | case LWTUNNEL_ENCAP_IP: | |
418 | print_encap_ip(fp, encap); | |
419 | break; | |
5866bddd TH |
420 | case LWTUNNEL_ENCAP_ILA: |
421 | print_encap_ila(fp, encap); | |
422 | break; | |
d95cdcf5 PA |
423 | case LWTUNNEL_ENCAP_IP6: |
424 | print_encap_ip6(fp, encap); | |
425 | break; | |
b15f440e TG |
426 | case LWTUNNEL_ENCAP_BPF: |
427 | print_encap_bpf(fp, encap); | |
428 | break; | |
e8493916 DL |
429 | case LWTUNNEL_ENCAP_SEG6: |
430 | print_encap_seg6(fp, encap); | |
431 | break; | |
8db158b9 DL |
432 | case LWTUNNEL_ENCAP_SEG6_LOCAL: |
433 | print_encap_seg6local(fp, encap); | |
434 | break; | |
e8493916 DL |
435 | } |
436 | } | |
437 | ||
00e76d4d DL |
438 | static struct ipv6_sr_hdr *parse_srh(char *segbuf, int hmac, bool encap) |
439 | { | |
440 | struct ipv6_sr_hdr *srh; | |
441 | int nsegs = 0; | |
442 | int srhlen; | |
443 | char *s; | |
444 | int i; | |
445 | ||
446 | s = segbuf; | |
447 | for (i = 0; *s; *s++ == ',' ? i++ : *s); | |
448 | nsegs = i + 1; | |
449 | ||
450 | if (!encap) | |
451 | nsegs++; | |
452 | ||
453 | srhlen = 8 + 16*nsegs; | |
454 | ||
455 | if (hmac) | |
456 | srhlen += 40; | |
457 | ||
458 | srh = malloc(srhlen); | |
459 | memset(srh, 0, srhlen); | |
460 | ||
461 | srh->hdrlen = (srhlen >> 3) - 1; | |
462 | srh->type = 4; | |
463 | srh->segments_left = nsegs - 1; | |
464 | srh->first_segment = nsegs - 1; | |
465 | ||
466 | if (hmac) | |
467 | srh->flags |= SR6_FLAG1_HMAC; | |
468 | ||
469 | i = srh->first_segment; | |
470 | for (s = strtok(segbuf, ","); s; s = strtok(NULL, ",")) { | |
6caad8f5 SP |
471 | inet_prefix addr; |
472 | ||
473 | get_addr(&addr, s, AF_INET6); | |
474 | memcpy(&srh->segments[i], addr.data, sizeof(struct in6_addr)); | |
00e76d4d DL |
475 | i--; |
476 | } | |
477 | ||
478 | if (hmac) { | |
479 | struct sr6_tlv_hmac *tlv; | |
480 | ||
481 | tlv = (struct sr6_tlv_hmac *)((char *)srh + srhlen - 40); | |
482 | tlv->tlvhdr.type = SR6_TLV_HMAC; | |
483 | tlv->tlvhdr.len = 38; | |
484 | tlv->hmackeyid = htonl(hmac); | |
485 | } | |
486 | ||
487 | return srh; | |
488 | } | |
489 | ||
e8493916 DL |
490 | static int parse_encap_seg6(struct rtattr *rta, size_t len, int *argcp, |
491 | char ***argvp) | |
492 | { | |
493 | int mode_ok = 0, segs_ok = 0, hmac_ok = 0; | |
494 | struct seg6_iptunnel_encap *tuninfo; | |
495 | struct ipv6_sr_hdr *srh; | |
496 | char **argv = *argvp; | |
3b5c5ef0 | 497 | char segbuf[1024] = ""; |
e8493916 DL |
498 | int argc = *argcp; |
499 | int encap = -1; | |
500 | __u32 hmac = 0; | |
e5da392f | 501 | int ret = 0; |
e8493916 | 502 | int srhlen; |
e8493916 DL |
503 | |
504 | while (argc > 0) { | |
505 | if (strcmp(*argv, "mode") == 0) { | |
506 | NEXT_ARG(); | |
507 | if (mode_ok++) | |
508 | duparg2("mode", *argv); | |
cf87da41 DL |
509 | encap = read_seg6mode_type(*argv); |
510 | if (encap < 0) | |
e8493916 DL |
511 | invarg("\"mode\" value is invalid\n", *argv); |
512 | } else if (strcmp(*argv, "segs") == 0) { | |
513 | NEXT_ARG(); | |
514 | if (segs_ok++) | |
515 | duparg2("segs", *argv); | |
516 | if (encap == -1) | |
517 | invarg("\"segs\" provided before \"mode\"\n", | |
518 | *argv); | |
519 | ||
18f156bf | 520 | strlcpy(segbuf, *argv, 1024); |
e8493916 DL |
521 | } else if (strcmp(*argv, "hmac") == 0) { |
522 | NEXT_ARG(); | |
523 | if (hmac_ok++) | |
524 | duparg2("hmac", *argv); | |
525 | get_u32(&hmac, *argv, 0); | |
526 | } else { | |
527 | break; | |
528 | } | |
529 | argc--; argv++; | |
530 | } | |
531 | ||
00e76d4d DL |
532 | srh = parse_srh(segbuf, hmac, encap); |
533 | srhlen = (srh->hdrlen + 1) << 3; | |
e8493916 DL |
534 | |
535 | tuninfo = malloc(sizeof(*tuninfo) + srhlen); | |
536 | memset(tuninfo, 0, sizeof(*tuninfo) + srhlen); | |
537 | ||
cf87da41 | 538 | tuninfo->mode = encap; |
e8493916 | 539 | |
00e76d4d | 540 | memcpy(tuninfo->srh, srh, srhlen); |
e8493916 | 541 | |
bd59e5b1 | 542 | if (rta_addattr_l(rta, len, SEG6_IPTUNNEL_SRH, tuninfo, |
e5da392f PS |
543 | sizeof(*tuninfo) + srhlen)) { |
544 | ret = -1; | |
545 | goto out; | |
546 | } | |
e8493916 DL |
547 | |
548 | *argcp = argc + 1; | |
549 | *argvp = argv - 1; | |
550 | ||
e5da392f PS |
551 | out: |
552 | free(tuninfo); | |
553 | free(srh); | |
554 | ||
555 | return ret; | |
1e529305 RP |
556 | } |
557 | ||
04cb3c0d MX |
558 | struct lwt_x { |
559 | struct rtattr *rta; | |
560 | size_t len; | |
561 | }; | |
562 | ||
563 | static void bpf_lwt_cb(void *lwt_ptr, int fd, const char *annotation) | |
564 | { | |
565 | struct lwt_x *x = lwt_ptr; | |
566 | ||
567 | rta_addattr32(x->rta, x->len, LWT_BPF_PROG_FD, fd); | |
568 | rta_addattr_l(x->rta, x->len, LWT_BPF_PROG_NAME, annotation, | |
569 | strlen(annotation) + 1); | |
570 | } | |
571 | ||
572 | static const struct bpf_cfg_ops bpf_cb_ops = { | |
573 | .ebpf_cb = bpf_lwt_cb, | |
574 | }; | |
575 | ||
576 | static int lwt_parse_bpf(struct rtattr *rta, size_t len, | |
577 | int *argcp, char ***argvp, | |
578 | int attr, const enum bpf_prog_type bpf_type) | |
579 | { | |
580 | struct bpf_cfg_in cfg = { | |
581 | .type = bpf_type, | |
582 | .argc = *argcp, | |
583 | .argv = *argvp, | |
584 | }; | |
585 | struct lwt_x x = { | |
586 | .rta = rta, | |
587 | .len = len, | |
588 | }; | |
589 | struct rtattr *nest; | |
590 | int err; | |
591 | ||
592 | nest = rta_nest(rta, len, attr); | |
593 | err = bpf_parse_and_load_common(&cfg, &bpf_cb_ops, &x); | |
594 | if (err < 0) { | |
595 | fprintf(stderr, "Failed to parse eBPF program: %s\n", | |
596 | strerror(-err)); | |
597 | return -1; | |
598 | } | |
599 | rta_nest_end(rta, nest); | |
600 | ||
601 | *argcp = cfg.argc; | |
602 | *argvp = cfg.argv; | |
603 | ||
604 | return 0; | |
605 | } | |
606 | ||
8db158b9 DL |
607 | static int parse_encap_seg6local(struct rtattr *rta, size_t len, int *argcp, |
608 | char ***argvp) | |
609 | { | |
610 | int segs_ok = 0, hmac_ok = 0, table_ok = 0, nh4_ok = 0, nh6_ok = 0; | |
04cb3c0d | 611 | int iif_ok = 0, oif_ok = 0, action_ok = 0, srh_ok = 0, bpf_ok = 0; |
8db158b9 DL |
612 | __u32 action = 0, table, iif, oif; |
613 | struct ipv6_sr_hdr *srh; | |
614 | char **argv = *argvp; | |
615 | int argc = *argcp; | |
616 | char segbuf[1024]; | |
617 | inet_prefix addr; | |
618 | __u32 hmac = 0; | |
bd59e5b1 | 619 | int ret = 0; |
8db158b9 DL |
620 | |
621 | while (argc > 0) { | |
622 | if (strcmp(*argv, "action") == 0) { | |
623 | NEXT_ARG(); | |
624 | if (action_ok++) | |
625 | duparg2("action", *argv); | |
626 | action = read_action_type(*argv); | |
627 | if (!action) | |
628 | invarg("\"action\" value is invalid\n", *argv); | |
bd59e5b1 PS |
629 | ret = rta_addattr32(rta, len, SEG6_LOCAL_ACTION, |
630 | action); | |
8db158b9 DL |
631 | } else if (strcmp(*argv, "table") == 0) { |
632 | NEXT_ARG(); | |
633 | if (table_ok++) | |
634 | duparg2("table", *argv); | |
635 | get_u32(&table, *argv, 0); | |
bd59e5b1 | 636 | ret = rta_addattr32(rta, len, SEG6_LOCAL_TABLE, table); |
8db158b9 DL |
637 | } else if (strcmp(*argv, "nh4") == 0) { |
638 | NEXT_ARG(); | |
639 | if (nh4_ok++) | |
640 | duparg2("nh4", *argv); | |
641 | get_addr(&addr, *argv, AF_INET); | |
bd59e5b1 PS |
642 | ret = rta_addattr_l(rta, len, SEG6_LOCAL_NH4, |
643 | &addr.data, addr.bytelen); | |
8db158b9 DL |
644 | } else if (strcmp(*argv, "nh6") == 0) { |
645 | NEXT_ARG(); | |
646 | if (nh6_ok++) | |
647 | duparg2("nh6", *argv); | |
648 | get_addr(&addr, *argv, AF_INET6); | |
bd59e5b1 PS |
649 | ret = rta_addattr_l(rta, len, SEG6_LOCAL_NH6, |
650 | &addr.data, addr.bytelen); | |
8db158b9 DL |
651 | } else if (strcmp(*argv, "iif") == 0) { |
652 | NEXT_ARG(); | |
653 | if (iif_ok++) | |
654 | duparg2("iif", *argv); | |
7a14358b | 655 | iif = ll_name_to_index(*argv); |
8db158b9 | 656 | if (!iif) |
fe99adbc | 657 | exit(nodev(*argv)); |
bd59e5b1 | 658 | ret = rta_addattr32(rta, len, SEG6_LOCAL_IIF, iif); |
8db158b9 DL |
659 | } else if (strcmp(*argv, "oif") == 0) { |
660 | NEXT_ARG(); | |
661 | if (oif_ok++) | |
662 | duparg2("oif", *argv); | |
7a14358b | 663 | oif = ll_name_to_index(*argv); |
8db158b9 | 664 | if (!oif) |
fe99adbc | 665 | exit(nodev(*argv)); |
bd59e5b1 | 666 | ret = rta_addattr32(rta, len, SEG6_LOCAL_OIF, oif); |
8db158b9 DL |
667 | } else if (strcmp(*argv, "srh") == 0) { |
668 | NEXT_ARG(); | |
669 | if (srh_ok++) | |
670 | duparg2("srh", *argv); | |
671 | if (strcmp(*argv, "segs") != 0) | |
672 | invarg("missing \"segs\" attribute for srh\n", | |
673 | *argv); | |
674 | NEXT_ARG(); | |
675 | if (segs_ok++) | |
676 | duparg2("segs", *argv); | |
677 | strncpy(segbuf, *argv, 1024); | |
678 | segbuf[1023] = 0; | |
679 | if (!NEXT_ARG_OK()) | |
680 | break; | |
681 | NEXT_ARG(); | |
682 | if (strcmp(*argv, "hmac") == 0) { | |
683 | NEXT_ARG(); | |
684 | if (hmac_ok++) | |
685 | duparg2("hmac", *argv); | |
686 | get_u32(&hmac, *argv, 0); | |
687 | } else { | |
688 | continue; | |
689 | } | |
04cb3c0d MX |
690 | } else if (strcmp(*argv, "endpoint") == 0) { |
691 | NEXT_ARG(); | |
692 | if (bpf_ok++) | |
693 | duparg2("endpoint", *argv); | |
694 | ||
695 | if (lwt_parse_bpf(rta, len, &argc, &argv, SEG6_LOCAL_BPF, | |
696 | BPF_PROG_TYPE_LWT_SEG6LOCAL) < 0) | |
697 | exit(-1); | |
8db158b9 DL |
698 | } else { |
699 | break; | |
700 | } | |
bd59e5b1 PS |
701 | if (ret) |
702 | return ret; | |
8db158b9 DL |
703 | argc--; argv++; |
704 | } | |
705 | ||
706 | if (!action) { | |
707 | fprintf(stderr, "Missing action type\n"); | |
708 | exit(-1); | |
709 | } | |
710 | ||
711 | if (srh_ok) { | |
712 | int srhlen; | |
713 | ||
714 | srh = parse_srh(segbuf, hmac, | |
715 | action == SEG6_LOCAL_ACTION_END_B6_ENCAP); | |
716 | srhlen = (srh->hdrlen + 1) << 3; | |
bd59e5b1 | 717 | ret = rta_addattr_l(rta, len, SEG6_LOCAL_SRH, srh, srhlen); |
8db158b9 DL |
718 | free(srh); |
719 | } | |
720 | ||
721 | *argcp = argc + 1; | |
722 | *argvp = argv - 1; | |
723 | ||
bd59e5b1 | 724 | return ret; |
8db158b9 DL |
725 | } |
726 | ||
b723368c SH |
727 | static int parse_encap_mpls(struct rtattr *rta, size_t len, |
728 | int *argcp, char ***argvp) | |
1e529305 RP |
729 | { |
730 | inet_prefix addr; | |
731 | int argc = *argcp; | |
732 | char **argv = *argvp; | |
9688cf3b | 733 | int ttl_ok = 0; |
1e529305 RP |
734 | |
735 | if (get_addr(&addr, *argv, AF_MPLS)) { | |
b723368c SH |
736 | fprintf(stderr, |
737 | "Error: an inet address is expected rather than \"%s\".\n", | |
738 | *argv); | |
1e529305 RP |
739 | exit(1); |
740 | } | |
741 | ||
bd59e5b1 PS |
742 | if (rta_addattr_l(rta, len, MPLS_IPTUNNEL_DST, |
743 | &addr.data, addr.bytelen)) | |
744 | return -1; | |
1e529305 | 745 | |
9688cf3b RS |
746 | argc--; |
747 | argv++; | |
748 | ||
749 | while (argc > 0) { | |
750 | if (strcmp(*argv, "ttl") == 0) { | |
751 | __u8 ttl; | |
752 | ||
753 | NEXT_ARG(); | |
754 | if (ttl_ok++) | |
755 | duparg2("ttl", *argv); | |
756 | if (get_u8(&ttl, *argv, 0)) | |
757 | invarg("\"ttl\" value is invalid\n", *argv); | |
bd59e5b1 PS |
758 | if (rta_addattr8(rta, len, MPLS_IPTUNNEL_TTL, ttl)) |
759 | return -1; | |
9688cf3b RS |
760 | } else { |
761 | break; | |
762 | } | |
763 | argc--; argv++; | |
764 | } | |
765 | ||
766 | /* argv is currently the first unparsed argument, | |
767 | * but the lwt_parse_encap() caller will move to the next, | |
768 | * so step back | |
769 | */ | |
770 | *argcp = argc + 1; | |
771 | *argvp = argv - 1; | |
1e529305 RP |
772 | |
773 | return 0; | |
774 | } | |
775 | ||
b723368c SH |
776 | static int parse_encap_ip(struct rtattr *rta, size_t len, |
777 | int *argcp, char ***argvp) | |
1e529305 | 778 | { |
94a8722f | 779 | int id_ok = 0, dst_ok = 0, src_ok = 0, tos_ok = 0, ttl_ok = 0; |
1e529305 RP |
780 | char **argv = *argvp; |
781 | int argc = *argcp; | |
bd59e5b1 | 782 | int ret = 0; |
1e529305 RP |
783 | |
784 | while (argc > 0) { | |
785 | if (strcmp(*argv, "id") == 0) { | |
786 | __u64 id; | |
56f5daac | 787 | |
1e529305 RP |
788 | NEXT_ARG(); |
789 | if (id_ok++) | |
790 | duparg2("id", *argv); | |
9f7401fa | 791 | if (get_be64(&id, *argv, 0)) |
1e529305 | 792 | invarg("\"id\" value is invalid\n", *argv); |
bd59e5b1 | 793 | ret = rta_addattr64(rta, len, LWTUNNEL_IP_ID, id); |
1e529305 RP |
794 | } else if (strcmp(*argv, "dst") == 0) { |
795 | inet_prefix addr; | |
56f5daac | 796 | |
1e529305 RP |
797 | NEXT_ARG(); |
798 | if (dst_ok++) | |
799 | duparg2("dst", *argv); | |
800 | get_addr(&addr, *argv, AF_INET); | |
bd59e5b1 PS |
801 | ret = rta_addattr_l(rta, len, LWTUNNEL_IP_DST, |
802 | &addr.data, addr.bytelen); | |
94a8722f SL |
803 | } else if (strcmp(*argv, "src") == 0) { |
804 | inet_prefix addr; | |
805 | ||
806 | NEXT_ARG(); | |
807 | if (src_ok++) | |
808 | duparg2("src", *argv); | |
809 | get_addr(&addr, *argv, AF_INET); | |
34212c73 DA |
810 | ret = rta_addattr_l(rta, len, LWTUNNEL_IP_SRC, |
811 | &addr.data, addr.bytelen); | |
1e529305 RP |
812 | } else if (strcmp(*argv, "tos") == 0) { |
813 | __u32 tos; | |
56f5daac | 814 | |
1e529305 RP |
815 | NEXT_ARG(); |
816 | if (tos_ok++) | |
817 | duparg2("tos", *argv); | |
818 | if (rtnl_dsfield_a2n(&tos, *argv)) | |
819 | invarg("\"tos\" value is invalid\n", *argv); | |
bd59e5b1 | 820 | ret = rta_addattr8(rta, len, LWTUNNEL_IP_TOS, tos); |
1e529305 RP |
821 | } else if (strcmp(*argv, "ttl") == 0) { |
822 | __u8 ttl; | |
56f5daac | 823 | |
1e529305 RP |
824 | NEXT_ARG(); |
825 | if (ttl_ok++) | |
826 | duparg2("ttl", *argv); | |
827 | if (get_u8(&ttl, *argv, 0)) | |
828 | invarg("\"ttl\" value is invalid\n", *argv); | |
bd59e5b1 | 829 | ret = rta_addattr8(rta, len, LWTUNNEL_IP_TTL, ttl); |
1e529305 RP |
830 | } else { |
831 | break; | |
832 | } | |
bd59e5b1 PS |
833 | if (ret) |
834 | break; | |
f0df4081 | 835 | argc--; argv++; |
1e529305 RP |
836 | } |
837 | ||
f0df4081 PA |
838 | /* argv is currently the first unparsed argument, |
839 | * but the lwt_parse_encap() caller will move to the next, | |
b723368c SH |
840 | * so step back |
841 | */ | |
f0df4081 PA |
842 | *argcp = argc + 1; |
843 | *argvp = argv - 1; | |
1e529305 | 844 | |
bd59e5b1 | 845 | return ret; |
1e529305 RP |
846 | } |
847 | ||
5866bddd TH |
848 | static int parse_encap_ila(struct rtattr *rta, size_t len, |
849 | int *argcp, char ***argvp) | |
850 | { | |
851 | __u64 locator; | |
852 | int argc = *argcp; | |
853 | char **argv = *argvp; | |
bd59e5b1 | 854 | int ret = 0; |
5866bddd TH |
855 | |
856 | if (get_addr64(&locator, *argv) < 0) { | |
857 | fprintf(stderr, "Bad locator: %s\n", *argv); | |
858 | exit(1); | |
859 | } | |
860 | ||
ed67f838 TH |
861 | argc--; argv++; |
862 | ||
05d978e0 | 863 | if (rta_addattr64(rta, len, ILA_ATTR_LOCATOR, locator)) |
bd59e5b1 | 864 | return -1; |
5866bddd | 865 | |
ed67f838 TH |
866 | while (argc > 0) { |
867 | if (strcmp(*argv, "csum-mode") == 0) { | |
08806fb0 | 868 | int csum_mode; |
ed67f838 TH |
869 | |
870 | NEXT_ARG(); | |
871 | ||
872 | csum_mode = ila_csum_name2mode(*argv); | |
873 | if (csum_mode < 0) | |
b723368c SH |
874 | invarg("\"csum-mode\" value is invalid\n", |
875 | *argv); | |
ed67f838 | 876 | |
05d978e0 | 877 | ret = rta_addattr8(rta, len, ILA_ATTR_CSUM_MODE, |
bd59e5b1 | 878 | (__u8)csum_mode); |
ed67f838 | 879 | |
86905c8f TH |
880 | argc--; argv++; |
881 | } else if (strcmp(*argv, "ident-type") == 0) { | |
882 | int ident_type; | |
883 | ||
884 | NEXT_ARG(); | |
885 | ||
886 | ident_type = ila_ident_name2type(*argv); | |
887 | if (ident_type < 0) | |
888 | invarg("\"ident-type\" value is invalid\n", | |
889 | *argv); | |
890 | ||
05d978e0 | 891 | ret = rta_addattr8(rta, len, ILA_ATTR_IDENT_TYPE, |
bd59e5b1 | 892 | (__u8)ident_type); |
86905c8f TH |
893 | |
894 | argc--; argv++; | |
895 | } else if (strcmp(*argv, "hook-type") == 0) { | |
896 | int hook_type; | |
897 | ||
898 | NEXT_ARG(); | |
899 | ||
900 | hook_type = ila_hook_name2type(*argv); | |
901 | if (hook_type < 0) | |
902 | invarg("\"hook-type\" value is invalid\n", | |
903 | *argv); | |
904 | ||
05d978e0 | 905 | ret = rta_addattr8(rta, len, ILA_ATTR_HOOK_TYPE, |
bd59e5b1 | 906 | (__u8)hook_type); |
86905c8f | 907 | |
ed67f838 TH |
908 | argc--; argv++; |
909 | } else { | |
910 | break; | |
911 | } | |
bd59e5b1 PS |
912 | if (ret) |
913 | break; | |
ed67f838 TH |
914 | } |
915 | ||
916 | /* argv is currently the first unparsed argument, | |
917 | * but the lwt_parse_encap() caller will move to the next, | |
918 | * so step back | |
919 | */ | |
920 | *argcp = argc + 1; | |
921 | *argvp = argv - 1; | |
5866bddd | 922 | |
bd59e5b1 | 923 | return ret; |
5866bddd | 924 | } |
1e529305 | 925 | |
b723368c SH |
926 | static int parse_encap_ip6(struct rtattr *rta, size_t len, |
927 | int *argcp, char ***argvp) | |
d95cdcf5 | 928 | { |
94a8722f | 929 | int id_ok = 0, dst_ok = 0, src_ok = 0, tos_ok = 0, ttl_ok = 0; |
d95cdcf5 PA |
930 | char **argv = *argvp; |
931 | int argc = *argcp; | |
bd59e5b1 | 932 | int ret = 0; |
d95cdcf5 PA |
933 | |
934 | while (argc > 0) { | |
935 | if (strcmp(*argv, "id") == 0) { | |
936 | __u64 id; | |
56f5daac | 937 | |
d95cdcf5 PA |
938 | NEXT_ARG(); |
939 | if (id_ok++) | |
940 | duparg2("id", *argv); | |
9f7401fa | 941 | if (get_be64(&id, *argv, 0)) |
d95cdcf5 | 942 | invarg("\"id\" value is invalid\n", *argv); |
bd59e5b1 | 943 | ret = rta_addattr64(rta, len, LWTUNNEL_IP6_ID, id); |
d95cdcf5 PA |
944 | } else if (strcmp(*argv, "dst") == 0) { |
945 | inet_prefix addr; | |
56f5daac | 946 | |
d95cdcf5 PA |
947 | NEXT_ARG(); |
948 | if (dst_ok++) | |
949 | duparg2("dst", *argv); | |
950 | get_addr(&addr, *argv, AF_INET6); | |
bd59e5b1 PS |
951 | ret = rta_addattr_l(rta, len, LWTUNNEL_IP6_DST, |
952 | &addr.data, addr.bytelen); | |
94a8722f SL |
953 | } else if (strcmp(*argv, "src") == 0) { |
954 | inet_prefix addr; | |
955 | ||
956 | NEXT_ARG(); | |
957 | if (src_ok++) | |
958 | duparg2("src", *argv); | |
959 | get_addr(&addr, *argv, AF_INET6); | |
34212c73 DA |
960 | ret = rta_addattr_l(rta, len, LWTUNNEL_IP6_SRC, |
961 | &addr.data, addr.bytelen); | |
d95cdcf5 PA |
962 | } else if (strcmp(*argv, "tc") == 0) { |
963 | __u32 tc; | |
56f5daac | 964 | |
d95cdcf5 PA |
965 | NEXT_ARG(); |
966 | if (tos_ok++) | |
967 | duparg2("tc", *argv); | |
968 | if (rtnl_dsfield_a2n(&tc, *argv)) | |
969 | invarg("\"tc\" value is invalid\n", *argv); | |
bd59e5b1 | 970 | ret = rta_addattr8(rta, len, LWTUNNEL_IP6_TC, tc); |
d95cdcf5 PA |
971 | } else if (strcmp(*argv, "hoplimit") == 0) { |
972 | __u8 hoplimit; | |
56f5daac | 973 | |
d95cdcf5 PA |
974 | NEXT_ARG(); |
975 | if (ttl_ok++) | |
976 | duparg2("hoplimit", *argv); | |
977 | if (get_u8(&hoplimit, *argv, 0)) | |
b723368c SH |
978 | invarg("\"hoplimit\" value is invalid\n", |
979 | *argv); | |
bd59e5b1 PS |
980 | ret = rta_addattr8(rta, len, LWTUNNEL_IP6_HOPLIMIT, |
981 | hoplimit); | |
d95cdcf5 PA |
982 | } else { |
983 | break; | |
984 | } | |
bd59e5b1 PS |
985 | if (ret) |
986 | break; | |
d95cdcf5 PA |
987 | argc--; argv++; |
988 | } | |
989 | ||
990 | /* argv is currently the first unparsed argument, | |
991 | * but the lwt_parse_encap() caller will move to the next, | |
b723368c SH |
992 | * so step back |
993 | */ | |
d95cdcf5 PA |
994 | *argcp = argc + 1; |
995 | *argvp = argv - 1; | |
996 | ||
bd59e5b1 | 997 | return ret; |
d95cdcf5 PA |
998 | } |
999 | ||
b15f440e TG |
1000 | static void lwt_bpf_usage(void) |
1001 | { | |
1002 | fprintf(stderr, "Usage: ip route ... encap bpf [ in BPF ] [ out BPF ] [ xmit BPF ] [...]\n"); | |
1003 | fprintf(stderr, "BPF := obj FILE [ section NAME ] [ verbose ]\n"); | |
1004 | exit(-1); | |
1005 | } | |
1006 | ||
1007 | static int parse_encap_bpf(struct rtattr *rta, size_t len, int *argcp, | |
1008 | char ***argvp) | |
1009 | { | |
1010 | char **argv = *argvp; | |
1011 | int argc = *argcp; | |
1012 | int headroom_set = 0; | |
1013 | ||
1014 | while (argc > 0) { | |
1015 | if (strcmp(*argv, "in") == 0) { | |
1016 | NEXT_ARG(); | |
1017 | if (lwt_parse_bpf(rta, len, &argc, &argv, LWT_BPF_IN, | |
1018 | BPF_PROG_TYPE_LWT_IN) < 0) | |
1019 | return -1; | |
1020 | } else if (strcmp(*argv, "out") == 0) { | |
1021 | NEXT_ARG(); | |
1022 | if (lwt_parse_bpf(rta, len, &argc, &argv, LWT_BPF_OUT, | |
1023 | BPF_PROG_TYPE_LWT_OUT) < 0) | |
1024 | return -1; | |
1025 | } else if (strcmp(*argv, "xmit") == 0) { | |
1026 | NEXT_ARG(); | |
1027 | if (lwt_parse_bpf(rta, len, &argc, &argv, LWT_BPF_XMIT, | |
1028 | BPF_PROG_TYPE_LWT_XMIT) < 0) | |
1029 | return -1; | |
1030 | } else if (strcmp(*argv, "headroom") == 0) { | |
1031 | unsigned int headroom; | |
1032 | ||
1033 | NEXT_ARG(); | |
1034 | if (get_unsigned(&headroom, *argv, 0) || headroom == 0) | |
1035 | invarg("headroom is invalid\n", *argv); | |
1036 | if (!headroom_set) | |
05d978e0 | 1037 | rta_addattr32(rta, len, LWT_BPF_XMIT_HEADROOM, |
b15f440e TG |
1038 | headroom); |
1039 | headroom_set = 1; | |
1040 | } else if (strcmp(*argv, "help") == 0) { | |
1041 | lwt_bpf_usage(); | |
1042 | } else { | |
1043 | break; | |
1044 | } | |
1045 | NEXT_ARG_FWD(); | |
1046 | } | |
1047 | ||
1048 | /* argv is currently the first unparsed argument, | |
1049 | * but the lwt_parse_encap() caller will move to the next, | |
b723368c SH |
1050 | * so step back |
1051 | */ | |
b15f440e TG |
1052 | *argcp = argc + 1; |
1053 | *argvp = argv - 1; | |
1054 | ||
1055 | return 0; | |
1056 | } | |
1057 | ||
1e529305 RP |
1058 | int lwt_parse_encap(struct rtattr *rta, size_t len, int *argcp, char ***argvp) |
1059 | { | |
1060 | struct rtattr *nest; | |
1061 | int argc = *argcp; | |
1062 | char **argv = *argvp; | |
1063 | __u16 type; | |
bd59e5b1 | 1064 | int ret = 0; |
1e529305 RP |
1065 | |
1066 | NEXT_ARG(); | |
1067 | type = read_encap_type(*argv); | |
1068 | if (!type) | |
1069 | invarg("\"encap type\" value is invalid\n", *argv); | |
1070 | ||
1071 | NEXT_ARG(); | |
1072 | if (argc <= 1) { | |
b723368c SH |
1073 | fprintf(stderr, |
1074 | "Error: unexpected end of line after \"encap\"\n"); | |
1e529305 RP |
1075 | exit(-1); |
1076 | } | |
1077 | ||
05d978e0 | 1078 | nest = rta_nest(rta, len, RTA_ENCAP); |
1e529305 RP |
1079 | switch (type) { |
1080 | case LWTUNNEL_ENCAP_MPLS: | |
bd59e5b1 | 1081 | ret = parse_encap_mpls(rta, len, &argc, &argv); |
1e529305 RP |
1082 | break; |
1083 | case LWTUNNEL_ENCAP_IP: | |
bd59e5b1 | 1084 | ret = parse_encap_ip(rta, len, &argc, &argv); |
1e529305 | 1085 | break; |
5866bddd | 1086 | case LWTUNNEL_ENCAP_ILA: |
bd59e5b1 | 1087 | ret = parse_encap_ila(rta, len, &argc, &argv); |
5866bddd | 1088 | break; |
d95cdcf5 | 1089 | case LWTUNNEL_ENCAP_IP6: |
bd59e5b1 | 1090 | ret = parse_encap_ip6(rta, len, &argc, &argv); |
d95cdcf5 | 1091 | break; |
b15f440e TG |
1092 | case LWTUNNEL_ENCAP_BPF: |
1093 | if (parse_encap_bpf(rta, len, &argc, &argv) < 0) | |
1094 | exit(-1); | |
1095 | break; | |
e8493916 | 1096 | case LWTUNNEL_ENCAP_SEG6: |
bd59e5b1 | 1097 | ret = parse_encap_seg6(rta, len, &argc, &argv); |
e8493916 | 1098 | break; |
8db158b9 | 1099 | case LWTUNNEL_ENCAP_SEG6_LOCAL: |
bd59e5b1 | 1100 | ret = parse_encap_seg6local(rta, len, &argc, &argv); |
8db158b9 | 1101 | break; |
1e529305 RP |
1102 | default: |
1103 | fprintf(stderr, "Error: unsupported encap type\n"); | |
1104 | break; | |
1105 | } | |
bd59e5b1 PS |
1106 | if (ret) |
1107 | return ret; | |
1108 | ||
1e529305 RP |
1109 | rta_nest_end(rta, nest); |
1110 | ||
05d978e0 | 1111 | ret = rta_addattr16(rta, len, RTA_ENCAP_TYPE, type); |
1e529305 RP |
1112 | |
1113 | *argcp = argc; | |
1114 | *argvp = argv; | |
1115 | ||
bd59e5b1 | 1116 | return ret; |
1e529305 | 1117 | } |