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