• Home
  • Line#
  • Scopes#
  • Navigate#
  • Raw
  • Download
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 
print_usage(FILE * f)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));
usage(void)58 static void usage(void)
59 {
60 	print_usage(stderr);
61 	exit(-1);
62 }
63 
gre_parse_opt(struct link_util * lu,int argc,char ** argv,struct nlmsghdr * n)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 
gre_print_direct_opt(FILE * f,struct rtattr * tb[])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 
gre_print_opt(struct link_util * lu,FILE * f,struct rtattr * tb[])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 
gre_print_help(struct link_util * lu,int argc,char ** argv,FILE * f)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 };
617