]> git.proxmox.com Git - mirror_iproute2.git/blob - ip/link_gre.c
gre: add support for ERSPAN tunnel
[mirror_iproute2.git] / ip / link_gre.c
1 /*
2 * link_gre.c gre driver module
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: Herbert Xu <herbert@gondor.apana.org.au>
10 *
11 */
12
13 #include <string.h>
14 #include <net/if.h>
15 #include <sys/types.h>
16 #include <sys/socket.h>
17 #include <arpa/inet.h>
18
19 #include <linux/ip.h>
20 #include <linux/if_tunnel.h>
21 #include "rt_names.h"
22 #include "utils.h"
23 #include "ip_common.h"
24 #include "tunnel.h"
25
26 static void print_usage(FILE *f)
27 {
28 fprintf(f,
29 "Usage: ... { gre | gretap | erspan } [ remote ADDR ]\n"
30 " [ local ADDR ]\n"
31 " [ [i|o]seq ]\n"
32 " [ [i|o]key KEY ]\n"
33 " [ [i|o]csum ]\n"
34 " [ ttl TTL ]\n"
35 " [ tos TOS ]\n"
36 " [ [no]pmtudisc ]\n"
37 " [ [no]ignore-df ]\n"
38 " [ dev PHYS_DEV ]\n"
39 " [ noencap ]\n"
40 " [ encap { fou | gue | none } ]\n"
41 " [ encap-sport PORT ]\n"
42 " [ encap-dport PORT ]\n"
43 " [ [no]encap-csum ]\n"
44 " [ [no]encap-csum6 ]\n"
45 " [ [no]encap-remcsum ]\n"
46 " [ fwmark MARK ]\n"
47 " [ erspan IDX ]\n"
48 "\n"
49 "Where: ADDR := { IP_ADDRESS | any }\n"
50 " TOS := { NUMBER | inherit }\n"
51 " TTL := { 1..255 | inherit }\n"
52 " KEY := { DOTTED_QUAD | NUMBER }\n"
53 " MARK := { 0x0..0xffffffff }\n"
54 );
55 }
56
57 static void usage(void) __attribute__((noreturn));
58 static void usage(void)
59 {
60 print_usage(stderr);
61 exit(-1);
62 }
63
64 static int gre_parse_opt(struct link_util *lu, int argc, char **argv,
65 struct nlmsghdr *n)
66 {
67 struct ifinfomsg *ifi = (struct ifinfomsg *)(n + 1);
68 struct {
69 struct nlmsghdr n;
70 struct ifinfomsg i;
71 char buf[16384];
72 } req = {
73 .n.nlmsg_len = NLMSG_LENGTH(sizeof(*ifi)),
74 .n.nlmsg_flags = NLM_F_REQUEST,
75 .n.nlmsg_type = RTM_GETLINK,
76 .i.ifi_family = preferred_family,
77 .i.ifi_index = ifi->ifi_index,
78 };
79 struct rtattr *tb[IFLA_MAX + 1];
80 struct rtattr *linkinfo[IFLA_INFO_MAX+1];
81 struct rtattr *greinfo[IFLA_GRE_MAX + 1];
82 __u16 iflags = 0;
83 __u16 oflags = 0;
84 unsigned int ikey = 0;
85 unsigned int okey = 0;
86 unsigned int saddr = 0;
87 unsigned int daddr = 0;
88 unsigned int link = 0;
89 __u8 pmtudisc = 1;
90 __u8 ttl = 0;
91 __u8 tos = 0;
92 int len;
93 __u16 encaptype = 0;
94 __u16 encapflags = 0;
95 __u16 encapsport = 0;
96 __u16 encapdport = 0;
97 __u8 metadata = 0;
98 __u8 ignore_df = 0;
99 __u32 fwmark = 0;
100 __u32 erspan_idx = 0;
101
102 if (!(n->nlmsg_flags & NLM_F_CREATE)) {
103 if (rtnl_talk(&rth, &req.n, &req.n, sizeof(req)) < 0) {
104 get_failed:
105 fprintf(stderr,
106 "Failed to get existing tunnel info.\n");
107 return -1;
108 }
109
110 len = req.n.nlmsg_len;
111 len -= NLMSG_LENGTH(sizeof(*ifi));
112 if (len < 0)
113 goto get_failed;
114
115 parse_rtattr(tb, IFLA_MAX, IFLA_RTA(&req.i), len);
116
117 if (!tb[IFLA_LINKINFO])
118 goto get_failed;
119
120 parse_rtattr_nested(linkinfo, IFLA_INFO_MAX, tb[IFLA_LINKINFO]);
121
122 if (!linkinfo[IFLA_INFO_DATA])
123 goto get_failed;
124
125 parse_rtattr_nested(greinfo, IFLA_GRE_MAX,
126 linkinfo[IFLA_INFO_DATA]);
127
128 if (greinfo[IFLA_GRE_IKEY])
129 ikey = rta_getattr_u32(greinfo[IFLA_GRE_IKEY]);
130
131 if (greinfo[IFLA_GRE_OKEY])
132 okey = rta_getattr_u32(greinfo[IFLA_GRE_OKEY]);
133
134 if (greinfo[IFLA_GRE_IFLAGS])
135 iflags = rta_getattr_u16(greinfo[IFLA_GRE_IFLAGS]);
136
137 if (greinfo[IFLA_GRE_OFLAGS])
138 oflags = rta_getattr_u16(greinfo[IFLA_GRE_OFLAGS]);
139
140 if (greinfo[IFLA_GRE_LOCAL])
141 saddr = rta_getattr_u32(greinfo[IFLA_GRE_LOCAL]);
142
143 if (greinfo[IFLA_GRE_REMOTE])
144 daddr = rta_getattr_u32(greinfo[IFLA_GRE_REMOTE]);
145
146 if (greinfo[IFLA_GRE_PMTUDISC])
147 pmtudisc = rta_getattr_u8(
148 greinfo[IFLA_GRE_PMTUDISC]);
149
150 if (greinfo[IFLA_GRE_TTL])
151 ttl = rta_getattr_u8(greinfo[IFLA_GRE_TTL]);
152
153 if (greinfo[IFLA_GRE_TOS])
154 tos = rta_getattr_u8(greinfo[IFLA_GRE_TOS]);
155
156 if (greinfo[IFLA_GRE_LINK])
157 link = rta_getattr_u8(greinfo[IFLA_GRE_LINK]);
158
159 if (greinfo[IFLA_GRE_ENCAP_TYPE])
160 encaptype = rta_getattr_u16(greinfo[IFLA_GRE_ENCAP_TYPE]);
161 if (greinfo[IFLA_GRE_ENCAP_FLAGS])
162 encapflags = rta_getattr_u16(greinfo[IFLA_GRE_ENCAP_FLAGS]);
163 if (greinfo[IFLA_GRE_ENCAP_SPORT])
164 encapsport = rta_getattr_u16(greinfo[IFLA_GRE_ENCAP_SPORT]);
165 if (greinfo[IFLA_GRE_ENCAP_DPORT])
166 encapdport = rta_getattr_u16(greinfo[IFLA_GRE_ENCAP_DPORT]);
167
168 if (greinfo[IFLA_GRE_COLLECT_METADATA])
169 metadata = 1;
170
171 if (greinfo[IFLA_GRE_IGNORE_DF])
172 ignore_df =
173 !!rta_getattr_u8(greinfo[IFLA_GRE_IGNORE_DF]);
174
175 if (greinfo[IFLA_GRE_FWMARK])
176 fwmark = rta_getattr_u32(greinfo[IFLA_GRE_FWMARK]);
177
178 if (greinfo[IFLA_GRE_ERSPAN_INDEX])
179 erspan_idx = rta_getattr_u32(greinfo[IFLA_GRE_ERSPAN_INDEX]);
180 }
181
182 while (argc > 0) {
183 if (!matches(*argv, "key")) {
184 unsigned int uval;
185
186 NEXT_ARG();
187 iflags |= GRE_KEY;
188 oflags |= GRE_KEY;
189 if (strchr(*argv, '.'))
190 uval = get_addr32(*argv);
191 else {
192 if (get_unsigned(&uval, *argv, 0) < 0) {
193 fprintf(stderr,
194 "Invalid value for \"key\": \"%s\"; it should be an unsigned integer\n", *argv);
195 exit(-1);
196 }
197 uval = htonl(uval);
198 }
199
200 ikey = okey = uval;
201 } else if (!matches(*argv, "ikey")) {
202 unsigned int uval;
203
204 NEXT_ARG();
205 iflags |= GRE_KEY;
206 if (strchr(*argv, '.'))
207 uval = get_addr32(*argv);
208 else {
209 if (get_unsigned(&uval, *argv, 0) < 0) {
210 fprintf(stderr, "invalid value for \"ikey\": \"%s\"; it should be an unsigned integer\n", *argv);
211 exit(-1);
212 }
213 uval = htonl(uval);
214 }
215 ikey = uval;
216 } else if (!matches(*argv, "okey")) {
217 unsigned int uval;
218
219 NEXT_ARG();
220 oflags |= GRE_KEY;
221 if (strchr(*argv, '.'))
222 uval = get_addr32(*argv);
223 else {
224 if (get_unsigned(&uval, *argv, 0) < 0) {
225 fprintf(stderr, "invalid value for \"okey\": \"%s\"; it should be an unsigned integer\n", *argv);
226 exit(-1);
227 }
228 uval = htonl(uval);
229 }
230 okey = uval;
231 } else if (!matches(*argv, "seq")) {
232 iflags |= GRE_SEQ;
233 oflags |= GRE_SEQ;
234 } else if (!matches(*argv, "iseq")) {
235 iflags |= GRE_SEQ;
236 } else if (!matches(*argv, "oseq")) {
237 oflags |= GRE_SEQ;
238 } else if (!matches(*argv, "csum")) {
239 iflags |= GRE_CSUM;
240 oflags |= GRE_CSUM;
241 } else if (!matches(*argv, "icsum")) {
242 iflags |= GRE_CSUM;
243 } else if (!matches(*argv, "ocsum")) {
244 oflags |= GRE_CSUM;
245 } else if (!matches(*argv, "nopmtudisc")) {
246 pmtudisc = 0;
247 } else if (!matches(*argv, "pmtudisc")) {
248 pmtudisc = 1;
249 } else if (!matches(*argv, "remote")) {
250 NEXT_ARG();
251 if (strcmp(*argv, "any"))
252 daddr = get_addr32(*argv);
253 } else if (!matches(*argv, "local")) {
254 NEXT_ARG();
255 if (strcmp(*argv, "any"))
256 saddr = get_addr32(*argv);
257 } else if (!matches(*argv, "dev")) {
258 NEXT_ARG();
259 link = if_nametoindex(*argv);
260 if (link == 0) {
261 fprintf(stderr, "Cannot find device \"%s\"\n",
262 *argv);
263 exit(-1);
264 }
265 } else if (!matches(*argv, "ttl") ||
266 !matches(*argv, "hoplimit")) {
267 unsigned int uval;
268
269 NEXT_ARG();
270 if (strcmp(*argv, "inherit") != 0) {
271 if (get_unsigned(&uval, *argv, 0))
272 invarg("invalid TTL\n", *argv);
273 if (uval > 255)
274 invarg("TTL must be <= 255\n", *argv);
275 ttl = uval;
276 }
277 } else if (!matches(*argv, "tos") ||
278 !matches(*argv, "tclass") ||
279 !matches(*argv, "dsfield")) {
280 __u32 uval;
281
282 NEXT_ARG();
283 if (strcmp(*argv, "inherit") != 0) {
284 if (rtnl_dsfield_a2n(&uval, *argv))
285 invarg("bad TOS value", *argv);
286 tos = uval;
287 } else
288 tos = 1;
289 } else if (strcmp(*argv, "noencap") == 0) {
290 encaptype = TUNNEL_ENCAP_NONE;
291 } else if (strcmp(*argv, "encap") == 0) {
292 NEXT_ARG();
293 if (strcmp(*argv, "fou") == 0)
294 encaptype = TUNNEL_ENCAP_FOU;
295 else if (strcmp(*argv, "gue") == 0)
296 encaptype = TUNNEL_ENCAP_GUE;
297 else if (strcmp(*argv, "none") == 0)
298 encaptype = TUNNEL_ENCAP_NONE;
299 else
300 invarg("Invalid encap type.", *argv);
301 } else if (strcmp(*argv, "encap-sport") == 0) {
302 NEXT_ARG();
303 if (strcmp(*argv, "auto") == 0)
304 encapsport = 0;
305 else if (get_u16(&encapsport, *argv, 0))
306 invarg("Invalid source port.", *argv);
307 } else if (strcmp(*argv, "encap-dport") == 0) {
308 NEXT_ARG();
309 if (get_u16(&encapdport, *argv, 0))
310 invarg("Invalid destination port.", *argv);
311 } else if (strcmp(*argv, "encap-csum") == 0) {
312 encapflags |= TUNNEL_ENCAP_FLAG_CSUM;
313 } else if (strcmp(*argv, "noencap-csum") == 0) {
314 encapflags &= ~TUNNEL_ENCAP_FLAG_CSUM;
315 } else if (strcmp(*argv, "encap-udp6-csum") == 0) {
316 encapflags |= TUNNEL_ENCAP_FLAG_CSUM6;
317 } else if (strcmp(*argv, "noencap-udp6-csum") == 0) {
318 encapflags |= ~TUNNEL_ENCAP_FLAG_CSUM6;
319 } else if (strcmp(*argv, "encap-remcsum") == 0) {
320 encapflags |= TUNNEL_ENCAP_FLAG_REMCSUM;
321 } else if (strcmp(*argv, "noencap-remcsum") == 0) {
322 encapflags |= ~TUNNEL_ENCAP_FLAG_REMCSUM;
323 } else if (strcmp(*argv, "external") == 0) {
324 metadata = 1;
325 } else if (strcmp(*argv, "ignore-df") == 0) {
326 ignore_df = 1;
327 } else if (strcmp(*argv, "noignore-df") == 0) {
328 /*
329 *only the lsb is significant, use 2 for presence
330 */
331 ignore_df = 2;
332 } else if (strcmp(*argv, "fwmark") == 0) {
333 NEXT_ARG();
334 if (get_u32(&fwmark, *argv, 0))
335 invarg("invalid fwmark\n", *argv);
336 } else if (strcmp(*argv, "erspan") == 0) {
337 NEXT_ARG();
338 if (get_u32(&erspan_idx, *argv, 0))
339 invarg("invalid erspan index\n", *argv);
340 if (erspan_idx & ~((1<<20) - 1) || erspan_idx == 0)
341 invarg("erspan index must be > 0 and <= 20-bit\n", *argv);
342 } else
343 usage();
344 argc--; argv++;
345 }
346
347 if (!ikey && IN_MULTICAST(ntohl(daddr))) {
348 ikey = daddr;
349 iflags |= GRE_KEY;
350 }
351 if (!okey && IN_MULTICAST(ntohl(daddr))) {
352 okey = daddr;
353 oflags |= GRE_KEY;
354 }
355 if (IN_MULTICAST(ntohl(daddr)) && !saddr) {
356 fprintf(stderr, "A broadcast tunnel requires a source address.\n");
357 return -1;
358 }
359
360 if (!metadata) {
361 addattr32(n, 1024, IFLA_GRE_IKEY, ikey);
362 addattr32(n, 1024, IFLA_GRE_OKEY, okey);
363 addattr_l(n, 1024, IFLA_GRE_IFLAGS, &iflags, 2);
364 addattr_l(n, 1024, IFLA_GRE_OFLAGS, &oflags, 2);
365 addattr_l(n, 1024, IFLA_GRE_LOCAL, &saddr, 4);
366 addattr_l(n, 1024, IFLA_GRE_REMOTE, &daddr, 4);
367 addattr_l(n, 1024, IFLA_GRE_PMTUDISC, &pmtudisc, 1);
368 if (link)
369 addattr32(n, 1024, IFLA_GRE_LINK, link);
370 addattr_l(n, 1024, IFLA_GRE_TTL, &ttl, 1);
371 addattr_l(n, 1024, IFLA_GRE_TOS, &tos, 1);
372 addattr32(n, 1024, IFLA_GRE_FWMARK, fwmark);
373 if (erspan_idx != 0)
374 addattr32(n, 1024, IFLA_GRE_ERSPAN_INDEX, erspan_idx);
375 } else {
376 addattr_l(n, 1024, IFLA_GRE_COLLECT_METADATA, NULL, 0);
377 }
378
379 addattr16(n, 1024, IFLA_GRE_ENCAP_TYPE, encaptype);
380 addattr16(n, 1024, IFLA_GRE_ENCAP_FLAGS, encapflags);
381 addattr16(n, 1024, IFLA_GRE_ENCAP_SPORT, htons(encapsport));
382 addattr16(n, 1024, IFLA_GRE_ENCAP_DPORT, htons(encapdport));
383
384 if (ignore_df)
385 addattr8(n, 1024, IFLA_GRE_IGNORE_DF, ignore_df & 1);
386
387 return 0;
388 }
389
390 static void gre_print_direct_opt(FILE *f, struct rtattr *tb[])
391 {
392 char s2[64];
393 const char *local = "any";
394 const char *remote = "any";
395 unsigned int iflags = 0;
396 unsigned int oflags = 0;
397
398 if (tb[IFLA_GRE_REMOTE]) {
399 unsigned int addr = rta_getattr_u32(tb[IFLA_GRE_REMOTE]);
400
401 if (addr)
402 remote = format_host(AF_INET, 4, &addr);
403 }
404
405 print_string(PRINT_ANY, "remote", "remote %s ", remote);
406
407 if (tb[IFLA_GRE_LOCAL]) {
408 unsigned int addr = rta_getattr_u32(tb[IFLA_GRE_LOCAL]);
409
410 if (addr)
411 local = format_host(AF_INET, 4, &addr);
412 }
413
414 print_string(PRINT_ANY, "local", "local %s ", local);
415
416 if (tb[IFLA_GRE_LINK] && rta_getattr_u32(tb[IFLA_GRE_LINK])) {
417 unsigned int link = rta_getattr_u32(tb[IFLA_GRE_LINK]);
418 const char *n = if_indextoname(link, s2);
419
420 if (n)
421 print_string(PRINT_ANY, "link", "dev %s ", n);
422 else
423 print_uint(PRINT_ANY, "link_index", "dev %u ", link);
424 }
425
426 if (tb[IFLA_GRE_TTL]) {
427 __u8 ttl = rta_getattr_u8(tb[IFLA_GRE_TTL]);
428
429 if (ttl)
430 print_int(PRINT_ANY, "ttl", "ttl %d ", ttl);
431 else
432 print_int(PRINT_JSON, "ttl", NULL, ttl);
433 } else {
434 print_string(PRINT_FP, NULL, "ttl %s ", "inherit");
435 }
436
437 if (tb[IFLA_GRE_TOS] && rta_getattr_u8(tb[IFLA_GRE_TOS])) {
438 int tos = rta_getattr_u8(tb[IFLA_GRE_TOS]);
439
440 if (is_json_context()) {
441 SPRINT_BUF(b1);
442
443 snprintf(b1, sizeof(b1), "0x%x", tos);
444 print_string(PRINT_JSON, "tos", NULL, b1);
445 } else {
446 fputs("tos ", f);
447 if (tos == 1)
448 fputs("inherit ", f);
449 else
450 fprintf(f, "0x%x ", tos);
451 }
452 }
453
454 if (tb[IFLA_GRE_PMTUDISC]) {
455 if (!rta_getattr_u8(tb[IFLA_GRE_PMTUDISC]))
456 print_bool(PRINT_ANY, "pmtudisc", "nopmtudisc ", false);
457 else
458 print_bool(PRINT_JSON, "pmtudisc", NULL, true);
459 }
460
461 if (tb[IFLA_GRE_IFLAGS])
462 iflags = rta_getattr_u16(tb[IFLA_GRE_IFLAGS]);
463
464 if (tb[IFLA_GRE_OFLAGS])
465 oflags = rta_getattr_u16(tb[IFLA_GRE_OFLAGS]);
466
467 if ((iflags & GRE_KEY) && tb[IFLA_GRE_IKEY]) {
468 inet_ntop(AF_INET, RTA_DATA(tb[IFLA_GRE_IKEY]), s2, sizeof(s2));
469 print_string(PRINT_ANY, "ikey", "ikey %s ", s2);
470 }
471
472 if ((oflags & GRE_KEY) && tb[IFLA_GRE_OKEY]) {
473 inet_ntop(AF_INET, RTA_DATA(tb[IFLA_GRE_OKEY]), s2, sizeof(s2));
474 print_string(PRINT_ANY, "okey", "okey %s ", s2);
475 }
476
477 if (iflags & GRE_SEQ)
478 print_bool(PRINT_ANY, "iseq", "iseq ", true);
479 if (oflags & GRE_SEQ)
480 print_bool(PRINT_ANY, "oseq", "oseq ", true);
481 if (iflags & GRE_CSUM)
482 print_bool(PRINT_ANY, "icsum", "icsum ", true);
483 if (oflags & GRE_CSUM)
484 print_bool(PRINT_ANY, "ocsum", "ocsum ", true);
485
486 if (tb[IFLA_GRE_FWMARK]) {
487 __u32 fwmark = rta_getattr_u32(tb[IFLA_GRE_FWMARK]);
488
489 if (fwmark) {
490 snprintf(s2, sizeof(s2), "0x%x", fwmark);
491
492 print_string(PRINT_ANY, "fwmark", "fwmark %s ", s2);
493 }
494 }
495 }
496
497 static void gre_print_opt(struct link_util *lu, FILE *f, struct rtattr *tb[])
498 {
499 if (!tb)
500 return;
501
502 if (!tb[IFLA_GRE_COLLECT_METADATA])
503 gre_print_direct_opt(f, tb);
504 else
505 print_bool(PRINT_ANY, "external", "external ", true);
506
507 if (tb[IFLA_GRE_IGNORE_DF] && rta_getattr_u8(tb[IFLA_GRE_IGNORE_DF]))
508 print_bool(PRINT_ANY, "ignore_df", "ignore-df ", true);
509
510 if (tb[IFLA_GRE_ERSPAN_INDEX]) {
511 __u32 erspan_idx = rta_getattr_u32(tb[IFLA_GRE_ERSPAN_INDEX]);
512
513 fprintf(f, "erspan_index %u ", erspan_idx);
514 }
515
516 if (tb[IFLA_GRE_ENCAP_TYPE] &&
517 rta_getattr_u16(tb[IFLA_GRE_ENCAP_TYPE]) != TUNNEL_ENCAP_NONE) {
518 __u16 type = rta_getattr_u16(tb[IFLA_GRE_ENCAP_TYPE]);
519 __u16 flags = rta_getattr_u16(tb[IFLA_GRE_ENCAP_FLAGS]);
520 __u16 sport = rta_getattr_u16(tb[IFLA_GRE_ENCAP_SPORT]);
521 __u16 dport = rta_getattr_u16(tb[IFLA_GRE_ENCAP_DPORT]);
522
523
524 open_json_object("encap");
525 print_string(PRINT_FP, NULL, "encap ", NULL);
526
527 switch (type) {
528 case TUNNEL_ENCAP_FOU:
529 print_string(PRINT_ANY, "type", "%s ", "fou");
530 break;
531 case TUNNEL_ENCAP_GUE:
532 print_string(PRINT_ANY, "type", "%s ", "gue");
533 break;
534 default:
535 print_null(PRINT_ANY, "type", "%s ", "unknown");
536 break;
537 }
538
539 if (is_json_context()) {
540 print_uint(PRINT_JSON,
541 "sport",
542 NULL,
543 sport ? ntohs(sport) : 0);
544 print_uint(PRINT_JSON, "dport", NULL, ntohs(dport));
545
546 print_bool(PRINT_JSON,
547 "csum",
548 NULL,
549 flags & TUNNEL_ENCAP_FLAG_CSUM);
550
551 print_bool(PRINT_JSON,
552 "csum6",
553 NULL,
554 flags & TUNNEL_ENCAP_FLAG_CSUM6);
555
556 print_bool(PRINT_JSON,
557 "remcsum",
558 NULL,
559 flags & TUNNEL_ENCAP_FLAG_REMCSUM);
560
561 close_json_object();
562 } else {
563 if (sport == 0)
564 fputs("encap-sport auto ", f);
565 else
566 fprintf(f, "encap-sport %u", ntohs(sport));
567
568 fprintf(f, "encap-dport %u ", ntohs(dport));
569
570 if (flags & TUNNEL_ENCAP_FLAG_CSUM)
571 fputs("encap-csum ", f);
572 else
573 fputs("noencap-csum ", f);
574
575 if (flags & TUNNEL_ENCAP_FLAG_CSUM6)
576 fputs("encap-csum6 ", f);
577 else
578 fputs("noencap-csum6 ", f);
579
580 if (flags & TUNNEL_ENCAP_FLAG_REMCSUM)
581 fputs("encap-remcsum ", f);
582 else
583 fputs("noencap-remcsum ", f);
584 }
585 }
586 }
587
588 static void gre_print_help(struct link_util *lu, int argc, char **argv,
589 FILE *f)
590 {
591 print_usage(f);
592 }
593
594 struct link_util gre_link_util = {
595 .id = "gre",
596 .maxattr = IFLA_GRE_MAX,
597 .parse_opt = gre_parse_opt,
598 .print_opt = gre_print_opt,
599 .print_help = gre_print_help,
600 };
601
602 struct link_util gretap_link_util = {
603 .id = "gretap",
604 .maxattr = IFLA_GRE_MAX,
605 .parse_opt = gre_parse_opt,
606 .print_opt = gre_print_opt,
607 .print_help = gre_print_help,
608 };
609
610 struct link_util erspan_link_util = {
611 .id = "erspan",
612 .maxattr = IFLA_GRE_MAX,
613 .parse_opt = gre_parse_opt,
614 .print_opt = gre_print_opt,
615 .print_help = gre_print_help,
616 };