Home | History | Annotate | Download | only in ip
      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 (at) 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 
     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));
     68 static void usage(void)
     69 {
     70 	print_usage(stderr);
     71 	exit(-1);
     72 }
     73 
     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 
    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 
    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