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 (at) 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 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)); 65 static void usage(void) 66 { 67 print_usage(stderr); 68 exit(-1); 69 } 70 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 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 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