]>
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 SH |
117 | print_0xhex(PRINT_ANY, "hmac", |
118 | "hmac 0x%X ", 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; | |
497 | char segbuf[1024]; | |
498 | int argc = *argcp; | |
499 | int encap = -1; | |
500 | __u32 hmac = 0; | |
e8493916 | 501 | int srhlen; |
e8493916 DL |
502 | |
503 | while (argc > 0) { | |
504 | if (strcmp(*argv, "mode") == 0) { | |
505 | NEXT_ARG(); | |
506 | if (mode_ok++) | |
507 | duparg2("mode", *argv); | |
cf87da41 DL |
508 | encap = read_seg6mode_type(*argv); |
509 | if (encap < 0) | |
e8493916 DL |
510 | invarg("\"mode\" value is invalid\n", *argv); |
511 | } else if (strcmp(*argv, "segs") == 0) { | |
512 | NEXT_ARG(); | |
513 | if (segs_ok++) | |
514 | duparg2("segs", *argv); | |
515 | if (encap == -1) | |
516 | invarg("\"segs\" provided before \"mode\"\n", | |
517 | *argv); | |
518 | ||
18f156bf | 519 | strlcpy(segbuf, *argv, 1024); |
e8493916 DL |
520 | } else if (strcmp(*argv, "hmac") == 0) { |
521 | NEXT_ARG(); | |
522 | if (hmac_ok++) | |
523 | duparg2("hmac", *argv); | |
524 | get_u32(&hmac, *argv, 0); | |
525 | } else { | |
526 | break; | |
527 | } | |
528 | argc--; argv++; | |
529 | } | |
530 | ||
00e76d4d DL |
531 | srh = parse_srh(segbuf, hmac, encap); |
532 | srhlen = (srh->hdrlen + 1) << 3; | |
e8493916 DL |
533 | |
534 | tuninfo = malloc(sizeof(*tuninfo) + srhlen); | |
535 | memset(tuninfo, 0, sizeof(*tuninfo) + srhlen); | |
536 | ||
cf87da41 | 537 | tuninfo->mode = encap; |
e8493916 | 538 | |
00e76d4d | 539 | memcpy(tuninfo->srh, srh, srhlen); |
e8493916 DL |
540 | |
541 | rta_addattr_l(rta, len, SEG6_IPTUNNEL_SRH, tuninfo, | |
542 | sizeof(*tuninfo) + srhlen); | |
00e76d4d | 543 | |
e8493916 | 544 | free(tuninfo); |
00e76d4d | 545 | free(srh); |
e8493916 DL |
546 | |
547 | *argcp = argc + 1; | |
548 | *argvp = argv - 1; | |
549 | ||
550 | return 0; | |
1e529305 RP |
551 | } |
552 | ||
04cb3c0d MX |
553 | struct lwt_x { |
554 | struct rtattr *rta; | |
555 | size_t len; | |
556 | }; | |
557 | ||
558 | static void bpf_lwt_cb(void *lwt_ptr, int fd, const char *annotation) | |
559 | { | |
560 | struct lwt_x *x = lwt_ptr; | |
561 | ||
562 | rta_addattr32(x->rta, x->len, LWT_BPF_PROG_FD, fd); | |
563 | rta_addattr_l(x->rta, x->len, LWT_BPF_PROG_NAME, annotation, | |
564 | strlen(annotation) + 1); | |
565 | } | |
566 | ||
567 | static const struct bpf_cfg_ops bpf_cb_ops = { | |
568 | .ebpf_cb = bpf_lwt_cb, | |
569 | }; | |
570 | ||
571 | static int lwt_parse_bpf(struct rtattr *rta, size_t len, | |
572 | int *argcp, char ***argvp, | |
573 | int attr, const enum bpf_prog_type bpf_type) | |
574 | { | |
575 | struct bpf_cfg_in cfg = { | |
576 | .type = bpf_type, | |
577 | .argc = *argcp, | |
578 | .argv = *argvp, | |
579 | }; | |
580 | struct lwt_x x = { | |
581 | .rta = rta, | |
582 | .len = len, | |
583 | }; | |
584 | struct rtattr *nest; | |
585 | int err; | |
586 | ||
587 | nest = rta_nest(rta, len, attr); | |
588 | err = bpf_parse_and_load_common(&cfg, &bpf_cb_ops, &x); | |
589 | if (err < 0) { | |
590 | fprintf(stderr, "Failed to parse eBPF program: %s\n", | |
591 | strerror(-err)); | |
592 | return -1; | |
593 | } | |
594 | rta_nest_end(rta, nest); | |
595 | ||
596 | *argcp = cfg.argc; | |
597 | *argvp = cfg.argv; | |
598 | ||
599 | return 0; | |
600 | } | |
601 | ||
8db158b9 DL |
602 | static int parse_encap_seg6local(struct rtattr *rta, size_t len, int *argcp, |
603 | char ***argvp) | |
604 | { | |
605 | int segs_ok = 0, hmac_ok = 0, table_ok = 0, nh4_ok = 0, nh6_ok = 0; | |
04cb3c0d | 606 | int iif_ok = 0, oif_ok = 0, action_ok = 0, srh_ok = 0, bpf_ok = 0; |
8db158b9 DL |
607 | __u32 action = 0, table, iif, oif; |
608 | struct ipv6_sr_hdr *srh; | |
609 | char **argv = *argvp; | |
610 | int argc = *argcp; | |
611 | char segbuf[1024]; | |
612 | inet_prefix addr; | |
613 | __u32 hmac = 0; | |
614 | ||
615 | while (argc > 0) { | |
616 | if (strcmp(*argv, "action") == 0) { | |
617 | NEXT_ARG(); | |
618 | if (action_ok++) | |
619 | duparg2("action", *argv); | |
620 | action = read_action_type(*argv); | |
621 | if (!action) | |
622 | invarg("\"action\" value is invalid\n", *argv); | |
623 | rta_addattr32(rta, len, SEG6_LOCAL_ACTION, action); | |
624 | } else if (strcmp(*argv, "table") == 0) { | |
625 | NEXT_ARG(); | |
626 | if (table_ok++) | |
627 | duparg2("table", *argv); | |
628 | get_u32(&table, *argv, 0); | |
629 | rta_addattr32(rta, len, SEG6_LOCAL_TABLE, table); | |
630 | } else if (strcmp(*argv, "nh4") == 0) { | |
631 | NEXT_ARG(); | |
632 | if (nh4_ok++) | |
633 | duparg2("nh4", *argv); | |
634 | get_addr(&addr, *argv, AF_INET); | |
635 | rta_addattr_l(rta, len, SEG6_LOCAL_NH4, &addr.data, | |
636 | addr.bytelen); | |
637 | } else if (strcmp(*argv, "nh6") == 0) { | |
638 | NEXT_ARG(); | |
639 | if (nh6_ok++) | |
640 | duparg2("nh6", *argv); | |
641 | get_addr(&addr, *argv, AF_INET6); | |
642 | rta_addattr_l(rta, len, SEG6_LOCAL_NH6, &addr.data, | |
643 | addr.bytelen); | |
644 | } else if (strcmp(*argv, "iif") == 0) { | |
645 | NEXT_ARG(); | |
646 | if (iif_ok++) | |
647 | duparg2("iif", *argv); | |
7a14358b | 648 | iif = ll_name_to_index(*argv); |
8db158b9 | 649 | if (!iif) |
fe99adbc | 650 | exit(nodev(*argv)); |
8db158b9 DL |
651 | rta_addattr32(rta, len, SEG6_LOCAL_IIF, iif); |
652 | } else if (strcmp(*argv, "oif") == 0) { | |
653 | NEXT_ARG(); | |
654 | if (oif_ok++) | |
655 | duparg2("oif", *argv); | |
7a14358b | 656 | oif = ll_name_to_index(*argv); |
8db158b9 | 657 | if (!oif) |
fe99adbc | 658 | exit(nodev(*argv)); |
8db158b9 DL |
659 | rta_addattr32(rta, len, SEG6_LOCAL_OIF, oif); |
660 | } else if (strcmp(*argv, "srh") == 0) { | |
661 | NEXT_ARG(); | |
662 | if (srh_ok++) | |
663 | duparg2("srh", *argv); | |
664 | if (strcmp(*argv, "segs") != 0) | |
665 | invarg("missing \"segs\" attribute for srh\n", | |
666 | *argv); | |
667 | NEXT_ARG(); | |
668 | if (segs_ok++) | |
669 | duparg2("segs", *argv); | |
670 | strncpy(segbuf, *argv, 1024); | |
671 | segbuf[1023] = 0; | |
672 | if (!NEXT_ARG_OK()) | |
673 | break; | |
674 | NEXT_ARG(); | |
675 | if (strcmp(*argv, "hmac") == 0) { | |
676 | NEXT_ARG(); | |
677 | if (hmac_ok++) | |
678 | duparg2("hmac", *argv); | |
679 | get_u32(&hmac, *argv, 0); | |
680 | } else { | |
681 | continue; | |
682 | } | |
04cb3c0d MX |
683 | } else if (strcmp(*argv, "endpoint") == 0) { |
684 | NEXT_ARG(); | |
685 | if (bpf_ok++) | |
686 | duparg2("endpoint", *argv); | |
687 | ||
688 | if (lwt_parse_bpf(rta, len, &argc, &argv, SEG6_LOCAL_BPF, | |
689 | BPF_PROG_TYPE_LWT_SEG6LOCAL) < 0) | |
690 | exit(-1); | |
8db158b9 DL |
691 | } else { |
692 | break; | |
693 | } | |
694 | argc--; argv++; | |
695 | } | |
696 | ||
697 | if (!action) { | |
698 | fprintf(stderr, "Missing action type\n"); | |
699 | exit(-1); | |
700 | } | |
701 | ||
702 | if (srh_ok) { | |
703 | int srhlen; | |
704 | ||
705 | srh = parse_srh(segbuf, hmac, | |
706 | action == SEG6_LOCAL_ACTION_END_B6_ENCAP); | |
707 | srhlen = (srh->hdrlen + 1) << 3; | |
708 | rta_addattr_l(rta, len, SEG6_LOCAL_SRH, srh, srhlen); | |
709 | free(srh); | |
710 | } | |
711 | ||
712 | *argcp = argc + 1; | |
713 | *argvp = argv - 1; | |
714 | ||
715 | return 0; | |
716 | } | |
717 | ||
b723368c SH |
718 | static int parse_encap_mpls(struct rtattr *rta, size_t len, |
719 | int *argcp, char ***argvp) | |
1e529305 RP |
720 | { |
721 | inet_prefix addr; | |
722 | int argc = *argcp; | |
723 | char **argv = *argvp; | |
9688cf3b | 724 | int ttl_ok = 0; |
1e529305 RP |
725 | |
726 | if (get_addr(&addr, *argv, AF_MPLS)) { | |
b723368c SH |
727 | fprintf(stderr, |
728 | "Error: an inet address is expected rather than \"%s\".\n", | |
729 | *argv); | |
1e529305 RP |
730 | exit(1); |
731 | } | |
732 | ||
733 | rta_addattr_l(rta, len, MPLS_IPTUNNEL_DST, &addr.data, | |
734 | addr.bytelen); | |
735 | ||
9688cf3b RS |
736 | argc--; |
737 | argv++; | |
738 | ||
739 | while (argc > 0) { | |
740 | if (strcmp(*argv, "ttl") == 0) { | |
741 | __u8 ttl; | |
742 | ||
743 | NEXT_ARG(); | |
744 | if (ttl_ok++) | |
745 | duparg2("ttl", *argv); | |
746 | if (get_u8(&ttl, *argv, 0)) | |
747 | invarg("\"ttl\" value is invalid\n", *argv); | |
748 | rta_addattr8(rta, len, MPLS_IPTUNNEL_TTL, ttl); | |
749 | } else { | |
750 | break; | |
751 | } | |
752 | argc--; argv++; | |
753 | } | |
754 | ||
755 | /* argv is currently the first unparsed argument, | |
756 | * but the lwt_parse_encap() caller will move to the next, | |
757 | * so step back | |
758 | */ | |
759 | *argcp = argc + 1; | |
760 | *argvp = argv - 1; | |
1e529305 RP |
761 | |
762 | return 0; | |
763 | } | |
764 | ||
b723368c SH |
765 | static int parse_encap_ip(struct rtattr *rta, size_t len, |
766 | int *argcp, char ***argvp) | |
1e529305 RP |
767 | { |
768 | int id_ok = 0, dst_ok = 0, tos_ok = 0, ttl_ok = 0; | |
769 | char **argv = *argvp; | |
770 | int argc = *argcp; | |
771 | ||
772 | while (argc > 0) { | |
773 | if (strcmp(*argv, "id") == 0) { | |
774 | __u64 id; | |
56f5daac | 775 | |
1e529305 RP |
776 | NEXT_ARG(); |
777 | if (id_ok++) | |
778 | duparg2("id", *argv); | |
9f7401fa | 779 | if (get_be64(&id, *argv, 0)) |
1e529305 | 780 | invarg("\"id\" value is invalid\n", *argv); |
9f7401fa | 781 | rta_addattr64(rta, len, LWTUNNEL_IP_ID, id); |
1e529305 RP |
782 | } else if (strcmp(*argv, "dst") == 0) { |
783 | inet_prefix addr; | |
56f5daac | 784 | |
1e529305 RP |
785 | NEXT_ARG(); |
786 | if (dst_ok++) | |
787 | duparg2("dst", *argv); | |
788 | get_addr(&addr, *argv, AF_INET); | |
b723368c SH |
789 | rta_addattr_l(rta, len, LWTUNNEL_IP_DST, |
790 | &addr.data, addr.bytelen); | |
1e529305 RP |
791 | } else if (strcmp(*argv, "tos") == 0) { |
792 | __u32 tos; | |
56f5daac | 793 | |
1e529305 RP |
794 | NEXT_ARG(); |
795 | if (tos_ok++) | |
796 | duparg2("tos", *argv); | |
797 | if (rtnl_dsfield_a2n(&tos, *argv)) | |
798 | invarg("\"tos\" value is invalid\n", *argv); | |
799 | rta_addattr8(rta, len, LWTUNNEL_IP_TOS, tos); | |
800 | } else if (strcmp(*argv, "ttl") == 0) { | |
801 | __u8 ttl; | |
56f5daac | 802 | |
1e529305 RP |
803 | NEXT_ARG(); |
804 | if (ttl_ok++) | |
805 | duparg2("ttl", *argv); | |
806 | if (get_u8(&ttl, *argv, 0)) | |
807 | invarg("\"ttl\" value is invalid\n", *argv); | |
808 | rta_addattr8(rta, len, LWTUNNEL_IP_TTL, ttl); | |
809 | } else { | |
810 | break; | |
811 | } | |
f0df4081 | 812 | argc--; argv++; |
1e529305 RP |
813 | } |
814 | ||
f0df4081 PA |
815 | /* argv is currently the first unparsed argument, |
816 | * but the lwt_parse_encap() caller will move to the next, | |
b723368c SH |
817 | * so step back |
818 | */ | |
f0df4081 PA |
819 | *argcp = argc + 1; |
820 | *argvp = argv - 1; | |
1e529305 RP |
821 | |
822 | return 0; | |
823 | } | |
824 | ||
5866bddd TH |
825 | static int parse_encap_ila(struct rtattr *rta, size_t len, |
826 | int *argcp, char ***argvp) | |
827 | { | |
828 | __u64 locator; | |
829 | int argc = *argcp; | |
830 | char **argv = *argvp; | |
831 | ||
832 | if (get_addr64(&locator, *argv) < 0) { | |
833 | fprintf(stderr, "Bad locator: %s\n", *argv); | |
834 | exit(1); | |
835 | } | |
836 | ||
ed67f838 TH |
837 | argc--; argv++; |
838 | ||
5866bddd TH |
839 | rta_addattr64(rta, 1024, ILA_ATTR_LOCATOR, locator); |
840 | ||
ed67f838 TH |
841 | while (argc > 0) { |
842 | if (strcmp(*argv, "csum-mode") == 0) { | |
08806fb0 | 843 | int csum_mode; |
ed67f838 TH |
844 | |
845 | NEXT_ARG(); | |
846 | ||
847 | csum_mode = ila_csum_name2mode(*argv); | |
848 | if (csum_mode < 0) | |
b723368c SH |
849 | invarg("\"csum-mode\" value is invalid\n", |
850 | *argv); | |
ed67f838 | 851 | |
08806fb0 PS |
852 | rta_addattr8(rta, 1024, ILA_ATTR_CSUM_MODE, |
853 | (__u8)csum_mode); | |
ed67f838 | 854 | |
86905c8f TH |
855 | argc--; argv++; |
856 | } else if (strcmp(*argv, "ident-type") == 0) { | |
857 | int ident_type; | |
858 | ||
859 | NEXT_ARG(); | |
860 | ||
861 | ident_type = ila_ident_name2type(*argv); | |
862 | if (ident_type < 0) | |
863 | invarg("\"ident-type\" value is invalid\n", | |
864 | *argv); | |
865 | ||
866 | rta_addattr8(rta, 1024, ILA_ATTR_IDENT_TYPE, | |
867 | (__u8)ident_type); | |
868 | ||
869 | argc--; argv++; | |
870 | } else if (strcmp(*argv, "hook-type") == 0) { | |
871 | int hook_type; | |
872 | ||
873 | NEXT_ARG(); | |
874 | ||
875 | hook_type = ila_hook_name2type(*argv); | |
876 | if (hook_type < 0) | |
877 | invarg("\"hook-type\" value is invalid\n", | |
878 | *argv); | |
879 | ||
880 | rta_addattr8(rta, 1024, ILA_ATTR_HOOK_TYPE, | |
881 | (__u8)hook_type); | |
882 | ||
ed67f838 TH |
883 | argc--; argv++; |
884 | } else { | |
885 | break; | |
886 | } | |
887 | } | |
888 | ||
889 | /* argv is currently the first unparsed argument, | |
890 | * but the lwt_parse_encap() caller will move to the next, | |
891 | * so step back | |
892 | */ | |
893 | *argcp = argc + 1; | |
894 | *argvp = argv - 1; | |
5866bddd TH |
895 | |
896 | return 0; | |
897 | } | |
1e529305 | 898 | |
b723368c SH |
899 | static int parse_encap_ip6(struct rtattr *rta, size_t len, |
900 | int *argcp, char ***argvp) | |
d95cdcf5 PA |
901 | { |
902 | int id_ok = 0, dst_ok = 0, tos_ok = 0, ttl_ok = 0; | |
903 | char **argv = *argvp; | |
904 | int argc = *argcp; | |
905 | ||
906 | while (argc > 0) { | |
907 | if (strcmp(*argv, "id") == 0) { | |
908 | __u64 id; | |
56f5daac | 909 | |
d95cdcf5 PA |
910 | NEXT_ARG(); |
911 | if (id_ok++) | |
912 | duparg2("id", *argv); | |
9f7401fa | 913 | if (get_be64(&id, *argv, 0)) |
d95cdcf5 | 914 | invarg("\"id\" value is invalid\n", *argv); |
9f7401fa | 915 | rta_addattr64(rta, len, LWTUNNEL_IP6_ID, id); |
d95cdcf5 PA |
916 | } else if (strcmp(*argv, "dst") == 0) { |
917 | inet_prefix addr; | |
56f5daac | 918 | |
d95cdcf5 PA |
919 | NEXT_ARG(); |
920 | if (dst_ok++) | |
921 | duparg2("dst", *argv); | |
922 | get_addr(&addr, *argv, AF_INET6); | |
b723368c SH |
923 | rta_addattr_l(rta, len, LWTUNNEL_IP6_DST, |
924 | &addr.data, addr.bytelen); | |
d95cdcf5 PA |
925 | } else if (strcmp(*argv, "tc") == 0) { |
926 | __u32 tc; | |
56f5daac | 927 | |
d95cdcf5 PA |
928 | NEXT_ARG(); |
929 | if (tos_ok++) | |
930 | duparg2("tc", *argv); | |
931 | if (rtnl_dsfield_a2n(&tc, *argv)) | |
932 | invarg("\"tc\" value is invalid\n", *argv); | |
933 | rta_addattr8(rta, len, LWTUNNEL_IP6_TC, tc); | |
934 | } else if (strcmp(*argv, "hoplimit") == 0) { | |
935 | __u8 hoplimit; | |
56f5daac | 936 | |
d95cdcf5 PA |
937 | NEXT_ARG(); |
938 | if (ttl_ok++) | |
939 | duparg2("hoplimit", *argv); | |
940 | if (get_u8(&hoplimit, *argv, 0)) | |
b723368c SH |
941 | invarg("\"hoplimit\" value is invalid\n", |
942 | *argv); | |
d95cdcf5 PA |
943 | rta_addattr8(rta, len, LWTUNNEL_IP6_HOPLIMIT, hoplimit); |
944 | } else { | |
945 | break; | |
946 | } | |
947 | argc--; argv++; | |
948 | } | |
949 | ||
950 | /* argv is currently the first unparsed argument, | |
951 | * but the lwt_parse_encap() caller will move to the next, | |
b723368c SH |
952 | * so step back |
953 | */ | |
d95cdcf5 PA |
954 | *argcp = argc + 1; |
955 | *argvp = argv - 1; | |
956 | ||
957 | return 0; | |
958 | } | |
959 | ||
b15f440e TG |
960 | static void lwt_bpf_usage(void) |
961 | { | |
962 | fprintf(stderr, "Usage: ip route ... encap bpf [ in BPF ] [ out BPF ] [ xmit BPF ] [...]\n"); | |
963 | fprintf(stderr, "BPF := obj FILE [ section NAME ] [ verbose ]\n"); | |
964 | exit(-1); | |
965 | } | |
966 | ||
967 | static int parse_encap_bpf(struct rtattr *rta, size_t len, int *argcp, | |
968 | char ***argvp) | |
969 | { | |
970 | char **argv = *argvp; | |
971 | int argc = *argcp; | |
972 | int headroom_set = 0; | |
973 | ||
974 | while (argc > 0) { | |
975 | if (strcmp(*argv, "in") == 0) { | |
976 | NEXT_ARG(); | |
977 | if (lwt_parse_bpf(rta, len, &argc, &argv, LWT_BPF_IN, | |
978 | BPF_PROG_TYPE_LWT_IN) < 0) | |
979 | return -1; | |
980 | } else if (strcmp(*argv, "out") == 0) { | |
981 | NEXT_ARG(); | |
982 | if (lwt_parse_bpf(rta, len, &argc, &argv, LWT_BPF_OUT, | |
983 | BPF_PROG_TYPE_LWT_OUT) < 0) | |
984 | return -1; | |
985 | } else if (strcmp(*argv, "xmit") == 0) { | |
986 | NEXT_ARG(); | |
987 | if (lwt_parse_bpf(rta, len, &argc, &argv, LWT_BPF_XMIT, | |
988 | BPF_PROG_TYPE_LWT_XMIT) < 0) | |
989 | return -1; | |
990 | } else if (strcmp(*argv, "headroom") == 0) { | |
991 | unsigned int headroom; | |
992 | ||
993 | NEXT_ARG(); | |
994 | if (get_unsigned(&headroom, *argv, 0) || headroom == 0) | |
995 | invarg("headroom is invalid\n", *argv); | |
996 | if (!headroom_set) | |
997 | rta_addattr32(rta, 1024, LWT_BPF_XMIT_HEADROOM, | |
998 | headroom); | |
999 | headroom_set = 1; | |
1000 | } else if (strcmp(*argv, "help") == 0) { | |
1001 | lwt_bpf_usage(); | |
1002 | } else { | |
1003 | break; | |
1004 | } | |
1005 | NEXT_ARG_FWD(); | |
1006 | } | |
1007 | ||
1008 | /* argv is currently the first unparsed argument, | |
1009 | * but the lwt_parse_encap() caller will move to the next, | |
b723368c SH |
1010 | * so step back |
1011 | */ | |
b15f440e TG |
1012 | *argcp = argc + 1; |
1013 | *argvp = argv - 1; | |
1014 | ||
1015 | return 0; | |
1016 | } | |
1017 | ||
1e529305 RP |
1018 | int lwt_parse_encap(struct rtattr *rta, size_t len, int *argcp, char ***argvp) |
1019 | { | |
1020 | struct rtattr *nest; | |
1021 | int argc = *argcp; | |
1022 | char **argv = *argvp; | |
1023 | __u16 type; | |
1024 | ||
1025 | NEXT_ARG(); | |
1026 | type = read_encap_type(*argv); | |
1027 | if (!type) | |
1028 | invarg("\"encap type\" value is invalid\n", *argv); | |
1029 | ||
1030 | NEXT_ARG(); | |
1031 | if (argc <= 1) { | |
b723368c SH |
1032 | fprintf(stderr, |
1033 | "Error: unexpected end of line after \"encap\"\n"); | |
1e529305 RP |
1034 | exit(-1); |
1035 | } | |
1036 | ||
1037 | nest = rta_nest(rta, 1024, RTA_ENCAP); | |
1038 | switch (type) { | |
1039 | case LWTUNNEL_ENCAP_MPLS: | |
1040 | parse_encap_mpls(rta, len, &argc, &argv); | |
1041 | break; | |
1042 | case LWTUNNEL_ENCAP_IP: | |
1043 | parse_encap_ip(rta, len, &argc, &argv); | |
1044 | break; | |
5866bddd TH |
1045 | case LWTUNNEL_ENCAP_ILA: |
1046 | parse_encap_ila(rta, len, &argc, &argv); | |
1047 | break; | |
d95cdcf5 PA |
1048 | case LWTUNNEL_ENCAP_IP6: |
1049 | parse_encap_ip6(rta, len, &argc, &argv); | |
1050 | break; | |
b15f440e TG |
1051 | case LWTUNNEL_ENCAP_BPF: |
1052 | if (parse_encap_bpf(rta, len, &argc, &argv) < 0) | |
1053 | exit(-1); | |
1054 | break; | |
e8493916 DL |
1055 | case LWTUNNEL_ENCAP_SEG6: |
1056 | parse_encap_seg6(rta, len, &argc, &argv); | |
1057 | break; | |
8db158b9 DL |
1058 | case LWTUNNEL_ENCAP_SEG6_LOCAL: |
1059 | parse_encap_seg6local(rta, len, &argc, &argv); | |
1060 | break; | |
1e529305 RP |
1061 | default: |
1062 | fprintf(stderr, "Error: unsupported encap type\n"); | |
1063 | break; | |
1064 | } | |
1065 | rta_nest_end(rta, nest); | |
1066 | ||
1067 | rta_addattr16(rta, 1024, RTA_ENCAP_TYPE, type); | |
1068 | ||
1069 | *argcp = argc; | |
1070 | *argvp = argv; | |
1071 | ||
1072 | return 0; | |
1073 | } |