• Home
  • Line#
  • Scopes#
  • Navigate#
  • Raw
  • Download
1 /*
2  * link_ip6tnl.c	ip6tnl 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:	Nicolas Dichtel <nicolas.dichtel@6wind.com>
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 #include "rt_names.h"
23 #include "utils.h"
24 #include "ip_common.h"
25 #include "tunnel.h"
26 
27 #define IP6_FLOWINFO_TCLASS	htonl(0x0FF00000)
28 #define IP6_FLOWINFO_FLOWLABEL	htonl(0x000FFFFF)
29 
30 #define DEFAULT_TNL_HOP_LIMIT	(64)
31 
print_usage(FILE * f)32 static void print_usage(FILE *f)
33 {
34 	fprintf(f,
35 		"Usage: ... ip6tnl [ mode { ip6ip6 | ipip6 | any } ]\n"
36 		"                  [ remote ADDR ]\n"
37 		"                  [ local ADDR ]\n"
38 		"                  [ dev PHYS_DEV ]\n"
39 		"                  [ encaplimit ELIM ]\n"
40 		"                  [ hoplimit HLIM ]\n"
41 		"                  [ tclass TCLASS ]\n"
42 		"                  [ flowlabel FLOWLABEL ]\n"
43 		"                  [ dscp inherit ]\n"
44 		"                  [ fwmark MARK ]\n"
45 		"                  [ noencap ]\n"
46 		"                  [ encap { fou | gue | none } ]\n"
47 		"                  [ encap-sport PORT ]\n"
48 		"                  [ encap-dport PORT ]\n"
49 		"                  [ [no]encap-csum ]\n"
50 		"                  [ [no]encap-csum6 ]\n"
51 		"                  [ [no]encap-remcsum ]\n"
52 		"                  [ external ]\n"
53 		"\n"
54 		"Where: ADDR      := IPV6_ADDRESS\n"
55 		"       ELIM      := { none | 0..255 }(default=%d)\n"
56 		"       HLIM      := 0..255 (default=%d)\n"
57 		"       TCLASS    := { 0x0..0xff | inherit }\n"
58 		"       FLOWLABEL := { 0x0..0xfffff | inherit }\n"
59 		"       MARK      := { 0x0..0xffffffff | inherit }\n",
60 		IPV6_DEFAULT_TNL_ENCAP_LIMIT, DEFAULT_TNL_HOP_LIMIT
61 	);
62 }
63 
64 static void usage(void) __attribute__((noreturn));
usage(void)65 static void usage(void)
66 {
67 	print_usage(stderr);
68 	exit(-1);
69 }
70 
ip6tunnel_parse_opt(struct link_util * lu,int argc,char ** argv,struct nlmsghdr * n)71 static int ip6tunnel_parse_opt(struct link_util *lu, int argc, char **argv,
72 			       struct nlmsghdr *n)
73 {
74 	struct ifinfomsg *ifi = (struct ifinfomsg *)(n + 1);
75 	struct {
76 		struct nlmsghdr n;
77 		struct ifinfomsg i;
78 		char buf[2048];
79 	} req = {
80 		.n.nlmsg_len = NLMSG_LENGTH(sizeof(*ifi)),
81 		.n.nlmsg_flags = NLM_F_REQUEST,
82 		.n.nlmsg_type = RTM_GETLINK,
83 		.i.ifi_family = preferred_family,
84 		.i.ifi_index = ifi->ifi_index,
85 	};
86 	struct rtattr *tb[IFLA_MAX + 1];
87 	struct rtattr *linkinfo[IFLA_INFO_MAX+1];
88 	struct rtattr *iptuninfo[IFLA_IPTUN_MAX + 1];
89 	int len;
90 	struct in6_addr laddr = {};
91 	struct in6_addr raddr = {};
92 	__u8 hop_limit = DEFAULT_TNL_HOP_LIMIT;
93 	__u8 encap_limit = IPV6_DEFAULT_TNL_ENCAP_LIMIT;
94 	__u32 flowinfo = 0;
95 	__u32 flags = 0;
96 	__u32 link = 0;
97 	__u8 proto = 0;
98 	__u16 encaptype = 0;
99 	__u16 encapflags = TUNNEL_ENCAP_FLAG_CSUM6;
100 	__u16 encapsport = 0;
101 	__u16 encapdport = 0;
102 	__u8 metadata = 0;
103 	__u32 fwmark = 0;
104 
105 	if (!(n->nlmsg_flags & NLM_F_CREATE)) {
106 		if (rtnl_talk(&rth, &req.n, &req.n, sizeof(req)) < 0) {
107 get_failed:
108 			fprintf(stderr,
109 				"Failed to get existing tunnel info.\n");
110 			return -1;
111 		}
112 
113 		len = req.n.nlmsg_len;
114 		len -= NLMSG_LENGTH(sizeof(*ifi));
115 		if (len < 0)
116 			goto get_failed;
117 
118 		parse_rtattr(tb, IFLA_MAX, IFLA_RTA(&req.i), len);
119 
120 		if (!tb[IFLA_LINKINFO])
121 			goto get_failed;
122 
123 		parse_rtattr_nested(linkinfo, IFLA_INFO_MAX, tb[IFLA_LINKINFO]);
124 
125 		if (!linkinfo[IFLA_INFO_DATA])
126 			goto get_failed;
127 
128 		parse_rtattr_nested(iptuninfo, IFLA_IPTUN_MAX,
129 				    linkinfo[IFLA_INFO_DATA]);
130 
131 		if (iptuninfo[IFLA_IPTUN_LOCAL])
132 			memcpy(&laddr, RTA_DATA(iptuninfo[IFLA_IPTUN_LOCAL]),
133 			       sizeof(laddr));
134 
135 		if (iptuninfo[IFLA_IPTUN_REMOTE])
136 			memcpy(&raddr, RTA_DATA(iptuninfo[IFLA_IPTUN_REMOTE]),
137 			       sizeof(raddr));
138 
139 		if (iptuninfo[IFLA_IPTUN_TTL])
140 			hop_limit = rta_getattr_u8(iptuninfo[IFLA_IPTUN_TTL]);
141 
142 		if (iptuninfo[IFLA_IPTUN_ENCAP_LIMIT])
143 			encap_limit = rta_getattr_u8(iptuninfo[IFLA_IPTUN_ENCAP_LIMIT]);
144 
145 		if (iptuninfo[IFLA_IPTUN_FLOWINFO])
146 			flowinfo = rta_getattr_u32(iptuninfo[IFLA_IPTUN_FLOWINFO]);
147 
148 		if (iptuninfo[IFLA_IPTUN_FLAGS])
149 			flags = rta_getattr_u32(iptuninfo[IFLA_IPTUN_FLAGS]);
150 
151 		if (iptuninfo[IFLA_IPTUN_LINK])
152 			link = rta_getattr_u32(iptuninfo[IFLA_IPTUN_LINK]);
153 
154 		if (iptuninfo[IFLA_IPTUN_PROTO])
155 			proto = rta_getattr_u8(iptuninfo[IFLA_IPTUN_PROTO]);
156 		if (iptuninfo[IFLA_IPTUN_COLLECT_METADATA])
157 			metadata = 1;
158 
159 		if (iptuninfo[IFLA_IPTUN_FWMARK])
160 			fwmark = rta_getattr_u32(iptuninfo[IFLA_IPTUN_FWMARK]);
161 	}
162 
163 	while (argc > 0) {
164 		if (matches(*argv, "mode") == 0) {
165 			NEXT_ARG();
166 			if (strcmp(*argv, "ipv6/ipv6") == 0 ||
167 			    strcmp(*argv, "ip6ip6") == 0)
168 				proto = IPPROTO_IPV6;
169 			else if (strcmp(*argv, "ip/ipv6") == 0 ||
170 				 strcmp(*argv, "ipv4/ipv6") == 0 ||
171 				 strcmp(*argv, "ipip6") == 0 ||
172 				 strcmp(*argv, "ip4ip6") == 0)
173 				proto = IPPROTO_IPIP;
174 			else if (strcmp(*argv, "any/ipv6") == 0 ||
175 				 strcmp(*argv, "any") == 0)
176 				proto = 0;
177 			else
178 				invarg("Cannot guess tunnel mode.", *argv);
179 		} else if (strcmp(*argv, "remote") == 0) {
180 			inet_prefix addr;
181 
182 			NEXT_ARG();
183 			get_prefix(&addr, *argv, preferred_family);
184 			if (addr.family == AF_UNSPEC)
185 				invarg("\"remote\" address family is AF_UNSPEC", *argv);
186 			memcpy(&raddr, addr.data, addr.bytelen);
187 		} else if (strcmp(*argv, "local") == 0) {
188 			inet_prefix addr;
189 
190 			NEXT_ARG();
191 			get_prefix(&addr, *argv, preferred_family);
192 			if (addr.family == AF_UNSPEC)
193 				invarg("\"local\" address family is AF_UNSPEC", *argv);
194 			memcpy(&laddr, addr.data, addr.bytelen);
195 		} else if (matches(*argv, "dev") == 0) {
196 			NEXT_ARG();
197 			link = if_nametoindex(*argv);
198 			if (link == 0)
199 				invarg("\"dev\" is invalid", *argv);
200 		} else if (strcmp(*argv, "hoplimit") == 0 ||
201 			   strcmp(*argv, "ttl") == 0 ||
202 			   strcmp(*argv, "hlim") == 0) {
203 			__u8 uval;
204 
205 			NEXT_ARG();
206 			if (get_u8(&uval, *argv, 0))
207 				invarg("invalid HLIM", *argv);
208 			hop_limit = uval;
209 		} else if (strcmp(*argv, "encaplimit") == 0) {
210 			NEXT_ARG();
211 			if (strcmp(*argv, "none") == 0) {
212 				flags |= IP6_TNL_F_IGN_ENCAP_LIMIT;
213 			} else {
214 				__u8 uval;
215 
216 				if (get_u8(&uval, *argv, 0) < -1)
217 					invarg("invalid ELIM", *argv);
218 				encap_limit = uval;
219 				flags &= ~IP6_TNL_F_IGN_ENCAP_LIMIT;
220 			}
221 		} else if (strcmp(*argv, "tclass") == 0 ||
222 			   strcmp(*argv, "tc") == 0 ||
223 			   strcmp(*argv, "tos") == 0 ||
224 			   matches(*argv, "dsfield") == 0) {
225 			__u8 uval;
226 
227 			NEXT_ARG();
228 			flowinfo &= ~IP6_FLOWINFO_TCLASS;
229 			if (strcmp(*argv, "inherit") == 0)
230 				flags |= IP6_TNL_F_USE_ORIG_TCLASS;
231 			else {
232 				if (get_u8(&uval, *argv, 16))
233 					invarg("invalid TClass", *argv);
234 				flowinfo |= htonl((__u32)uval << 20) & IP6_FLOWINFO_TCLASS;
235 				flags &= ~IP6_TNL_F_USE_ORIG_TCLASS;
236 			}
237 		} else if (strcmp(*argv, "flowlabel") == 0 ||
238 			   strcmp(*argv, "fl") == 0) {
239 			__u32 uval;
240 
241 			NEXT_ARG();
242 			flowinfo &= ~IP6_FLOWINFO_FLOWLABEL;
243 			if (strcmp(*argv, "inherit") == 0)
244 				flags |= IP6_TNL_F_USE_ORIG_FLOWLABEL;
245 			else {
246 				if (get_u32(&uval, *argv, 16))
247 					invarg("invalid Flowlabel", *argv);
248 				if (uval > 0xFFFFF)
249 					invarg("invalid Flowlabel", *argv);
250 				flowinfo |= htonl(uval) & IP6_FLOWINFO_FLOWLABEL;
251 				flags &= ~IP6_TNL_F_USE_ORIG_FLOWLABEL;
252 			}
253 		} else if (strcmp(*argv, "dscp") == 0) {
254 			NEXT_ARG();
255 			if (strcmp(*argv, "inherit") != 0)
256 				invarg("not inherit", *argv);
257 			flags |= IP6_TNL_F_RCV_DSCP_COPY;
258 		} else if (strcmp(*argv, "fwmark") == 0) {
259 			NEXT_ARG();
260 			if (strcmp(*argv, "inherit") == 0) {
261 				flags |= IP6_TNL_F_USE_ORIG_FWMARK;
262 				fwmark = 0;
263 			} else {
264 				if (get_u32(&fwmark, *argv, 0))
265 					invarg("invalid fwmark\n", *argv);
266 				flags &= ~IP6_TNL_F_USE_ORIG_FWMARK;
267 			}
268 		} else if (strcmp(*argv, "noencap") == 0) {
269 			encaptype = TUNNEL_ENCAP_NONE;
270 		} else if (strcmp(*argv, "encap") == 0) {
271 			NEXT_ARG();
272 			if (strcmp(*argv, "fou") == 0)
273 				encaptype = TUNNEL_ENCAP_FOU;
274 			else if (strcmp(*argv, "gue") == 0)
275 				encaptype = TUNNEL_ENCAP_GUE;
276 			else if (strcmp(*argv, "none") == 0)
277 				encaptype = TUNNEL_ENCAP_NONE;
278 			else
279 				invarg("Invalid encap type.", *argv);
280 		} else if (strcmp(*argv, "encap-sport") == 0) {
281 			NEXT_ARG();
282 			if (strcmp(*argv, "auto") == 0)
283 				encapsport = 0;
284 			else if (get_u16(&encapsport, *argv, 0))
285 				invarg("Invalid source port.", *argv);
286 		} else if (strcmp(*argv, "encap-dport") == 0) {
287 			NEXT_ARG();
288 			if (get_u16(&encapdport, *argv, 0))
289 				invarg("Invalid destination port.", *argv);
290 		} else if (strcmp(*argv, "encap-csum") == 0) {
291 			encapflags |= TUNNEL_ENCAP_FLAG_CSUM;
292 		} else if (strcmp(*argv, "noencap-csum") == 0) {
293 			encapflags &= ~TUNNEL_ENCAP_FLAG_CSUM;
294 		} else if (strcmp(*argv, "encap-udp6-csum") == 0) {
295 			encapflags |= TUNNEL_ENCAP_FLAG_CSUM6;
296 		} else if (strcmp(*argv, "noencap-udp6-csum") == 0) {
297 			encapflags &= ~TUNNEL_ENCAP_FLAG_CSUM6;
298 		} else if (strcmp(*argv, "encap-remcsum") == 0) {
299 			encapflags |= TUNNEL_ENCAP_FLAG_REMCSUM;
300 		} else if (strcmp(*argv, "noencap-remcsum") == 0) {
301 			encapflags |= ~TUNNEL_ENCAP_FLAG_REMCSUM;
302 		} else if (strcmp(*argv, "external") == 0) {
303 			metadata = 1;
304 		} else
305 			usage();
306 		argc--, argv++;
307 	}
308 
309 	addattr8(n, 1024, IFLA_IPTUN_PROTO, proto);
310 	if (metadata) {
311 		addattr_l(n, 1024, IFLA_IPTUN_COLLECT_METADATA, NULL, 0);
312 		return 0;
313 	}
314 	addattr_l(n, 1024, IFLA_IPTUN_LOCAL, &laddr, sizeof(laddr));
315 	addattr_l(n, 1024, IFLA_IPTUN_REMOTE, &raddr, sizeof(raddr));
316 	addattr8(n, 1024, IFLA_IPTUN_TTL, hop_limit);
317 	addattr8(n, 1024, IFLA_IPTUN_ENCAP_LIMIT, encap_limit);
318 	addattr32(n, 1024, IFLA_IPTUN_FLOWINFO, flowinfo);
319 	addattr32(n, 1024, IFLA_IPTUN_FLAGS, flags);
320 	addattr32(n, 1024, IFLA_IPTUN_LINK, link);
321 	addattr32(n, 1024, IFLA_IPTUN_FWMARK, fwmark);
322 
323 	addattr16(n, 1024, IFLA_IPTUN_ENCAP_TYPE, encaptype);
324 	addattr16(n, 1024, IFLA_IPTUN_ENCAP_FLAGS, encapflags);
325 	addattr16(n, 1024, IFLA_IPTUN_ENCAP_SPORT, htons(encapsport));
326 	addattr16(n, 1024, IFLA_IPTUN_ENCAP_DPORT, htons(encapdport));
327 
328 	return 0;
329 }
330 
ip6tunnel_print_opt(struct link_util * lu,FILE * f,struct rtattr * tb[])331 static void ip6tunnel_print_opt(struct link_util *lu, FILE *f, struct rtattr *tb[])
332 {
333 	char s2[64];
334 	int flags = 0;
335 	__u32 flowinfo = 0;
336 
337 	if (!tb)
338 		return;
339 
340 	if (tb[IFLA_IPTUN_FLAGS])
341 		flags = rta_getattr_u32(tb[IFLA_IPTUN_FLAGS]);
342 
343 	if (tb[IFLA_IPTUN_FLOWINFO])
344 		flowinfo = rta_getattr_u32(tb[IFLA_IPTUN_FLOWINFO]);
345 
346 	if (tb[IFLA_IPTUN_PROTO]) {
347 		switch (rta_getattr_u8(tb[IFLA_IPTUN_PROTO])) {
348 		case IPPROTO_IPIP:
349 			print_string(PRINT_ANY, "proto", "%s ", "ipip6");
350 			break;
351 		case IPPROTO_IPV6:
352 			print_string(PRINT_ANY, "proto", "%s ", "ip6ip6");
353 			break;
354 		case 0:
355 			print_string(PRINT_ANY, "proto", "%s ", "any");
356 			break;
357 		}
358 	}
359 
360 	if (tb[IFLA_IPTUN_REMOTE]) {
361 		print_string(PRINT_ANY,
362 			     "remote",
363 			     "remote %s ",
364 			     rt_addr_n2a_rta(AF_INET6, tb[IFLA_IPTUN_REMOTE]));
365 	}
366 
367 	if (tb[IFLA_IPTUN_LOCAL]) {
368 		print_string(PRINT_ANY,
369 			     "local",
370 			     "local %s ",
371 			     rt_addr_n2a_rta(AF_INET6, tb[IFLA_IPTUN_LOCAL]));
372 	}
373 
374 	if (tb[IFLA_IPTUN_LINK] && rta_getattr_u32(tb[IFLA_IPTUN_LINK])) {
375 		unsigned int link = rta_getattr_u32(tb[IFLA_IPTUN_LINK]);
376 		const char *n = if_indextoname(link, s2);
377 
378 		if (n)
379 			print_string(PRINT_ANY, "link", "dev %s ", n);
380 		else
381 			print_uint(PRINT_ANY, "link_index", "dev %u ", link);
382 	}
383 
384 	if (flags & IP6_TNL_F_IGN_ENCAP_LIMIT)
385 		print_bool(PRINT_ANY,
386 			   "ip6_tnl_f_ign_encap_limit",
387 			   "encaplimit none ",
388 			   true);
389 	else if (tb[IFLA_IPTUN_ENCAP_LIMIT])
390 		print_uint(PRINT_ANY,
391 			   "encap_limit",
392 			   "encaplimit %u ",
393 			   rta_getattr_u8(tb[IFLA_IPTUN_ENCAP_LIMIT]));
394 
395 	if (tb[IFLA_IPTUN_TTL])
396 		print_uint(PRINT_ANY,
397 			   "ttl",
398 			   "hoplimit %u ",
399 			   rta_getattr_u8(tb[IFLA_IPTUN_TTL]));
400 
401 	if (flags & IP6_TNL_F_USE_ORIG_TCLASS)
402 		print_bool(PRINT_ANY,
403 			   "ip6_tnl_f_use_orig_tclass",
404 			   "tclass inherit ",
405 			   true);
406 	else if (tb[IFLA_IPTUN_FLOWINFO]) {
407 		__u32 val = ntohl(flowinfo & IP6_FLOWINFO_TCLASS);
408 
409 		if (is_json_context()) {
410 			SPRINT_BUF(b1);
411 
412 			snprintf(b1, sizeof(b1), "0x%02x", (__u8)(val >> 20));
413 			print_string(PRINT_JSON, "flowinfo_tclass", NULL, b1);
414 		} else {
415 			printf("tclass 0x%02x ", (__u8)(val >> 20));
416 		}
417 	}
418 
419 	if (flags & IP6_TNL_F_USE_ORIG_FLOWLABEL) {
420 		print_bool(PRINT_ANY,
421 			   "ip6_tnl_f_use_orig_flowlabel",
422 			   "flowlabel inherit ",
423 			   true);
424 	} else {
425 		if (is_json_context()) {
426 			SPRINT_BUF(b1);
427 
428 			snprintf(b1, sizeof(b1), "0x%05x",
429 				 ntohl(flowinfo & IP6_FLOWINFO_FLOWLABEL));
430 			print_string(PRINT_JSON, "flowlabel", NULL, b1);
431 		} else {
432 			printf("flowlabel 0x%05x ",
433 			       ntohl(flowinfo & IP6_FLOWINFO_FLOWLABEL));
434 		}
435 	}
436 
437 	if (is_json_context()) {
438 		SPRINT_BUF(flwinfo);
439 
440 		snprintf(flwinfo, sizeof(flwinfo), "0x%08x", ntohl(flowinfo));
441 		print_string(PRINT_JSON, "flowinfo", NULL, flwinfo);
442 	} else {
443 		printf("(flowinfo 0x%08x) ", ntohl(flowinfo));
444 
445 	}
446 
447 	if (flags & IP6_TNL_F_RCV_DSCP_COPY)
448 		print_bool(PRINT_ANY,
449 			   "ip6_tnl_f_rcv_dscp_copy",
450 			   "dscp inherit ",
451 			   true);
452 
453 	if (flags & IP6_TNL_F_MIP6_DEV)
454 		print_bool(PRINT_ANY, "ip6_tnl_f_mip6_dev", "mip6 ", true);
455 
456 	if (flags & IP6_TNL_F_USE_ORIG_FWMARK) {
457 		print_bool(PRINT_ANY,
458 			   "ip6_tnl_f_use_orig_fwmark",
459 			   "fwmark inherit ",
460 			   true);
461 	} else if (tb[IFLA_IPTUN_FWMARK]) {
462 		__u32 fwmark = rta_getattr_u32(tb[IFLA_IPTUN_FWMARK]);
463 
464 		if (fwmark) {
465 			SPRINT_BUF(b1);
466 
467 			snprintf(b1, sizeof(b1), "0x%x", fwmark);
468 			print_string(PRINT_ANY, "fwmark", "fwmark %s ", b1);
469 		}
470 	}
471 
472 	if (tb[IFLA_IPTUN_ENCAP_TYPE] &&
473 	    rta_getattr_u16(tb[IFLA_IPTUN_ENCAP_TYPE]) != TUNNEL_ENCAP_NONE) {
474 		__u16 type = rta_getattr_u16(tb[IFLA_IPTUN_ENCAP_TYPE]);
475 		__u16 flags = rta_getattr_u16(tb[IFLA_IPTUN_ENCAP_FLAGS]);
476 		__u16 sport = rta_getattr_u16(tb[IFLA_IPTUN_ENCAP_SPORT]);
477 		__u16 dport = rta_getattr_u16(tb[IFLA_IPTUN_ENCAP_DPORT]);
478 
479 		open_json_object("encap");
480 		print_string(PRINT_FP, NULL, "encap ", NULL);
481 		switch (type) {
482 		case TUNNEL_ENCAP_FOU:
483 			print_string(PRINT_ANY, "type", "%s ", "fou");
484 			break;
485 		case TUNNEL_ENCAP_GUE:
486 			print_string(PRINT_ANY, "type", "%s ", "gue");
487 			break;
488 		default:
489 			print_null(PRINT_ANY, "type", "unknown ", NULL);
490 			break;
491 		}
492 
493 		if (is_json_context()) {
494 			print_uint(PRINT_JSON,
495 				   "sport",
496 				   NULL,
497 				   sport ? ntohs(sport) : 0);
498 			print_uint(PRINT_JSON, "dport", NULL, ntohs(dport));
499 			print_bool(PRINT_JSON, "csum", NULL,
500 				   flags & TUNNEL_ENCAP_FLAG_CSUM);
501 			print_bool(PRINT_JSON, "csum6", NULL,
502 				   flags & TUNNEL_ENCAP_FLAG_CSUM6);
503 			print_bool(PRINT_JSON, "remcsum", NULL,
504 				   flags & TUNNEL_ENCAP_FLAG_REMCSUM);
505 			close_json_object();
506 		} else {
507 			if (sport == 0)
508 				fputs("encap-sport auto ", f);
509 			else
510 				fprintf(f, "encap-sport %u", ntohs(sport));
511 
512 			fprintf(f, "encap-dport %u ", ntohs(dport));
513 
514 			if (flags & TUNNEL_ENCAP_FLAG_CSUM)
515 				fputs("encap-csum ", f);
516 			else
517 				fputs("noencap-csum ", f);
518 
519 			if (flags & TUNNEL_ENCAP_FLAG_CSUM6)
520 				fputs("encap-csum6 ", f);
521 			else
522 				fputs("noencap-csum6 ", f);
523 
524 			if (flags & TUNNEL_ENCAP_FLAG_REMCSUM)
525 				fputs("encap-remcsum ", f);
526 			else
527 				fputs("noencap-remcsum ", f);
528 		}
529 	}
530 }
531 
ip6tunnel_print_help(struct link_util * lu,int argc,char ** argv,FILE * f)532 static void ip6tunnel_print_help(struct link_util *lu, int argc, char **argv,
533 				 FILE *f)
534 {
535 	print_usage(f);
536 }
537 
538 struct link_util ip6tnl_link_util = {
539 	.id = "ip6tnl",
540 	.maxattr = IFLA_IPTUN_MAX,
541 	.parse_opt = ip6tunnel_parse_opt,
542 	.print_opt = ip6tunnel_print_opt,
543 	.print_help = ip6tunnel_print_help,
544 };
545