1/* Modified by Broadcom Corp. Portions Copyright (c) Broadcom Corp, 2012. */ 2/* vi: set sw=4 ts=4: */ 3/* 4 * iptunnel.c "ip tunnel" 5 * 6 * Licensed under the GPL v2 or later, see the file LICENSE in this tarball. 7 * 8 * Authors: Alexey Kuznetsov, <kuznet@ms2.inr.ac.ru> 9 * 10 * 11 * Changes: 12 * 13 * Rani Assaf <rani@magic.metawire.com> 980929: resolve addresses 14 * Rani Assaf <rani@magic.metawire.com> 980930: do not allow key for ipip/sit 15 * Phil Karn <karn@ka9q.ampr.org> 990408: "pmtudisc" flag 16 */ 17 18#include <netinet/ip.h> 19#include <net/if.h> 20#include <net/if_arp.h> 21#include <asm/types.h> 22#ifndef __constant_htons 23#define __constant_htons htons 24#endif 25#include <linux/if_tunnel.h> 26 27#include "ip_common.h" /* #include "libbb.h" is inside */ 28#include "rt_names.h" 29#include "utils.h" 30 31 32/* Dies on error */ 33static int do_ioctl_get_ifindex(char *dev) 34{ 35 struct ifreq ifr; 36 int fd; 37 38 strncpy(ifr.ifr_name, dev, sizeof(ifr.ifr_name)); 39 fd = xsocket(AF_INET, SOCK_DGRAM, 0); 40 xioctl(fd, SIOCGIFINDEX, &ifr); 41 close(fd); 42 return ifr.ifr_ifindex; 43} 44 45static int do_ioctl_get_iftype(char *dev) 46{ 47 struct ifreq ifr; 48 int fd; 49 int err; 50 51 strncpy(ifr.ifr_name, dev, sizeof(ifr.ifr_name)); 52 fd = xsocket(AF_INET, SOCK_DGRAM, 0); 53 err = ioctl_or_warn(fd, SIOCGIFHWADDR, &ifr); 54 close(fd); 55 return err ? -1 : ifr.ifr_addr.sa_family; 56} 57 58static char *do_ioctl_get_ifname(int idx) 59{ 60 struct ifreq ifr; 61 int fd; 62 int err; 63 64 ifr.ifr_ifindex = idx; 65 fd = xsocket(AF_INET, SOCK_DGRAM, 0); 66 err = ioctl_or_warn(fd, SIOCGIFNAME, &ifr); 67 close(fd); 68 return err ? NULL : xstrndup(ifr.ifr_name, sizeof(ifr.ifr_name)); 69} 70 71static int do_get_ioctl(const char *basedev, struct ip_tunnel_parm *p) 72{ 73 struct ifreq ifr; 74 int fd; 75 int err; 76 77 strncpy(ifr.ifr_name, basedev, sizeof(ifr.ifr_name)); 78 ifr.ifr_ifru.ifru_data = (void*)p; 79 fd = xsocket(AF_INET, SOCK_DGRAM, 0); 80 err = ioctl_or_warn(fd, SIOCGETTUNNEL, &ifr); 81 close(fd); 82 return err; 83} 84 85/* Dies on error, otherwise returns 0 */ 86static int do_add_ioctl(int cmd, const char *basedev, struct ip_tunnel_parm *p) 87{ 88 struct ifreq ifr; 89 int fd; 90 91 if (cmd == SIOCCHGTUNNEL && p->name[0]) { 92 strncpy(ifr.ifr_name, p->name, sizeof(ifr.ifr_name)); 93 } else { 94 strncpy(ifr.ifr_name, basedev, sizeof(ifr.ifr_name)); 95 } 96 ifr.ifr_ifru.ifru_data = (void*)p; 97 fd = xsocket(AF_INET, SOCK_DGRAM, 0); 98#if ENABLE_IOCTL_HEX2STR_ERROR 99 /* #define magic will turn ioctl# into string */ 100 if (cmd == SIOCCHGTUNNEL) 101 xioctl(fd, SIOCCHGTUNNEL, &ifr); 102 else 103 xioctl(fd, SIOCADDTUNNEL, &ifr); 104#else 105 xioctl(fd, cmd, &ifr); 106#endif 107 close(fd); 108 return 0; 109} 110 111/* Dies on error, otherwise returns 0 */ 112static int do_del_ioctl(const char *basedev, struct ip_tunnel_parm *p) 113{ 114 struct ifreq ifr; 115 int fd; 116 117 if (p->name[0]) { 118 strncpy(ifr.ifr_name, p->name, sizeof(ifr.ifr_name)); 119 } else { 120 strncpy(ifr.ifr_name, basedev, sizeof(ifr.ifr_name)); 121 } 122 ifr.ifr_ifru.ifru_data = (void*)p; 123 fd = xsocket(AF_INET, SOCK_DGRAM, 0); 124 xioctl(fd, SIOCDELTUNNEL, &ifr); 125 close(fd); 126 return 0; 127} 128 129/* Dies on error */ 130static void parse_args(int argc, char **argv, int cmd, struct ip_tunnel_parm *p) 131{ 132 static const char keywords[] ALIGN1 = 133 "mode\0""ipip\0""ip/ip\0""gre\0""gre/ip\0""sit\0""ipv6/ip\0" 134 "key\0""ikey\0""okey\0""seq\0""iseq\0""oseq\0" 135 "csum\0""icsum\0""ocsum\0""nopmtudisc\0""pmtudisc\0" 136 "remote\0""any\0""local\0""dev\0" 137 "ttl\0""inherit\0""tos\0""dsfield\0" 138 "name\0"; 139 enum { 140 ARG_mode, ARG_ipip, ARG_ip_ip, ARG_gre, ARG_gre_ip, ARG_sit, ARG_ip6_ip, 141 ARG_key, ARG_ikey, ARG_okey, ARG_seq, ARG_iseq, ARG_oseq, 142 ARG_csum, ARG_icsum, ARG_ocsum, ARG_nopmtudisc, ARG_pmtudisc, 143 ARG_remote, ARG_any, ARG_local, ARG_dev, 144 ARG_ttl, ARG_inherit, ARG_tos, ARG_dsfield, 145 ARG_name 146 }; 147 int count = 0; 148 char medium[IFNAMSIZ]; 149 int key; 150 151 memset(p, 0, sizeof(*p)); 152 memset(&medium, 0, sizeof(medium)); 153 154 p->iph.version = 4; 155 p->iph.ihl = 5; 156#ifndef IP_DF 157#define IP_DF 0x4000 /* Flag: "Don't Fragment" */ 158#endif 159 p->iph.frag_off = htons(IP_DF); 160 161 while (argc > 0) { 162 key = index_in_strings(keywords, *argv); 163 if (key == ARG_mode) { 164 NEXT_ARG(); 165 key = index_in_strings(keywords, *argv); 166 if (key == ARG_ipip || 167 key == ARG_ip_ip) { 168 if (p->iph.protocol && p->iph.protocol != IPPROTO_IPIP) { 169 bb_error_msg_and_die("you managed to ask for more than one tunnel mode"); 170 } 171 p->iph.protocol = IPPROTO_IPIP; 172 } else if (key == ARG_gre || 173 key == ARG_gre_ip) { 174 if (p->iph.protocol && p->iph.protocol != IPPROTO_GRE) { 175 bb_error_msg_and_die("you managed to ask for more than one tunnel mode"); 176 } 177 p->iph.protocol = IPPROTO_GRE; 178 } else if (key == ARG_sit || 179 key == ARG_ip6_ip) { 180 if (p->iph.protocol && p->iph.protocol != IPPROTO_IPV6) { 181 bb_error_msg_and_die("you managed to ask for more than one tunnel mode"); 182 } 183 p->iph.protocol = IPPROTO_IPV6; 184 } else { 185 bb_error_msg_and_die("cannot guess tunnel mode"); 186 } 187 } else if (key == ARG_key) { 188 unsigned uval; 189 NEXT_ARG(); 190 p->i_flags |= GRE_KEY; 191 p->o_flags |= GRE_KEY; 192 if (strchr(*argv, '.')) 193 p->i_key = p->o_key = get_addr32(*argv); 194 else { 195 if (get_unsigned(&uval, *argv, 0)<0) { 196 bb_error_msg_and_die("invalid value of \"key\""); 197 } 198 p->i_key = p->o_key = htonl(uval); 199 } 200 } else if (key == ARG_ikey) { 201 unsigned uval; 202 NEXT_ARG(); 203 p->i_flags |= GRE_KEY; 204 if (strchr(*argv, '.')) 205 p->o_key = get_addr32(*argv); 206 else { 207 if (get_unsigned(&uval, *argv, 0)<0) { 208 bb_error_msg_and_die("invalid value of \"ikey\""); 209 } 210 p->i_key = htonl(uval); 211 } 212 } else if (key == ARG_okey) { 213 unsigned uval; 214 NEXT_ARG(); 215 p->o_flags |= GRE_KEY; 216 if (strchr(*argv, '.')) 217 p->o_key = get_addr32(*argv); 218 else { 219 if (get_unsigned(&uval, *argv, 0)<0) { 220 bb_error_msg_and_die("invalid value of \"okey\""); 221 } 222 p->o_key = htonl(uval); 223 } 224 } else if (key == ARG_seq) { 225 p->i_flags |= GRE_SEQ; 226 p->o_flags |= GRE_SEQ; 227 } else if (key == ARG_iseq) { 228 p->i_flags |= GRE_SEQ; 229 } else if (key == ARG_oseq) { 230 p->o_flags |= GRE_SEQ; 231 } else if (key == ARG_csum) { 232 p->i_flags |= GRE_CSUM; 233 p->o_flags |= GRE_CSUM; 234 } else if (key == ARG_icsum) { 235 p->i_flags |= GRE_CSUM; 236 } else if (key == ARG_ocsum) { 237 p->o_flags |= GRE_CSUM; 238 } else if (key == ARG_nopmtudisc) { 239 p->iph.frag_off = 0; 240 } else if (key == ARG_pmtudisc) { 241 p->iph.frag_off = htons(IP_DF); 242 } else if (key == ARG_remote) { 243 NEXT_ARG(); 244 key = index_in_strings(keywords, *argv); 245 if (key != ARG_any) 246 p->iph.daddr = get_addr32(*argv); 247 } else if (key == ARG_local) { 248 NEXT_ARG(); 249 key = index_in_strings(keywords, *argv); 250 if (key != ARG_any) 251 p->iph.saddr = get_addr32(*argv); 252 } else if (key == ARG_dev) { 253 NEXT_ARG(); 254 strncpy(medium, *argv, IFNAMSIZ-1); 255 } else if (key == ARG_ttl) { 256 unsigned uval; 257 NEXT_ARG(); 258 key = index_in_strings(keywords, *argv); 259 if (key != ARG_inherit) { 260 if (get_unsigned(&uval, *argv, 0)) 261 invarg(*argv, "TTL"); 262 if (uval > 255) 263 invarg(*argv, "TTL must be <=255"); 264 p->iph.ttl = uval; 265 } 266 } else if (key == ARG_tos || 267 key == ARG_dsfield) { 268 uint32_t uval; 269 NEXT_ARG(); 270 key = index_in_strings(keywords, *argv); 271 if (key != ARG_inherit) { 272 if (rtnl_dsfield_a2n(&uval, *argv)) 273 invarg(*argv, "TOS"); 274 p->iph.tos = uval; 275 } else 276 p->iph.tos = 1; 277 } else { 278 if (key == ARG_name) { 279 NEXT_ARG(); 280 } 281 if (p->name[0]) 282 duparg2("name", *argv); 283 strncpy(p->name, *argv, IFNAMSIZ); 284 if (cmd == SIOCCHGTUNNEL && count == 0) { 285 struct ip_tunnel_parm old_p; 286 memset(&old_p, 0, sizeof(old_p)); 287 if (do_get_ioctl(*argv, &old_p)) 288 exit(1); 289 *p = old_p; 290 } 291 } 292 count++; 293 argc--; 294 argv++; 295 } 296 297 if (p->iph.protocol == 0) { 298 if (memcmp(p->name, "gre", 3) == 0) 299 p->iph.protocol = IPPROTO_GRE; 300 else if (memcmp(p->name, "ipip", 4) == 0) 301 p->iph.protocol = IPPROTO_IPIP; 302 else if (memcmp(p->name, "sit", 3) == 0) 303 p->iph.protocol = IPPROTO_IPV6; 304 } 305 306 if (p->iph.protocol == IPPROTO_IPIP || p->iph.protocol == IPPROTO_IPV6) { 307 if ((p->i_flags & GRE_KEY) || (p->o_flags & GRE_KEY)) { 308 bb_error_msg_and_die("keys are not allowed with ipip and sit"); 309 } 310 } 311 312 if (medium[0]) { 313 p->link = do_ioctl_get_ifindex(medium); 314 } 315 316 if (p->i_key == 0 && IN_MULTICAST(ntohl(p->iph.daddr))) { 317 p->i_key = p->iph.daddr; 318 p->i_flags |= GRE_KEY; 319 } 320 if (p->o_key == 0 && IN_MULTICAST(ntohl(p->iph.daddr))) { 321 p->o_key = p->iph.daddr; 322 p->o_flags |= GRE_KEY; 323 } 324 if (IN_MULTICAST(ntohl(p->iph.daddr)) && !p->iph.saddr) { 325 bb_error_msg_and_die("broadcast tunnel requires a source address"); 326 } 327} 328 329 330/* Return value becomes exitcode. It's okay to not return at all */ 331static int do_add(int cmd, int argc, char **argv) 332{ 333 struct ip_tunnel_parm p; 334 335 parse_args(argc, argv, cmd, &p); 336 337 if (p.iph.ttl && p.iph.frag_off == 0) { 338 bb_error_msg_and_die("ttl != 0 and noptmudisc are incompatible"); 339 } 340 341 switch (p.iph.protocol) { 342 case IPPROTO_IPIP: 343 return do_add_ioctl(cmd, "tunl0", &p); 344 case IPPROTO_GRE: 345 return do_add_ioctl(cmd, "gre0", &p); 346 case IPPROTO_IPV6: 347 return do_add_ioctl(cmd, "sit0", &p); 348 default: 349 bb_error_msg_and_die("cannot determine tunnel mode (ipip, gre or sit)"); 350 } 351} 352 353/* Return value becomes exitcode. It's okay to not return at all */ 354static int do_del(int argc, char **argv) 355{ 356 struct ip_tunnel_parm p; 357 358 parse_args(argc, argv, SIOCDELTUNNEL, &p); 359 360 switch (p.iph.protocol) { 361 case IPPROTO_IPIP: 362 return do_del_ioctl("tunl0", &p); 363 case IPPROTO_GRE: 364 return do_del_ioctl("gre0", &p); 365 case IPPROTO_IPV6: 366 return do_del_ioctl("sit0", &p); 367 default: 368 return do_del_ioctl(p.name, &p); 369 } 370} 371 372static void print_tunnel(struct ip_tunnel_parm *p) 373{ 374 char s1[256]; 375 char s2[256]; 376 char s3[64]; 377 char s4[64]; 378 379 format_host(AF_INET, 4, &p->iph.daddr, s1, sizeof(s1)); 380 format_host(AF_INET, 4, &p->iph.saddr, s2, sizeof(s2)); 381 inet_ntop(AF_INET, &p->i_key, s3, sizeof(s3)); 382 inet_ntop(AF_INET, &p->o_key, s4, sizeof(s4)); 383 384 printf("%s: %s/ip remote %s local %s ", 385 p->name, 386 p->iph.protocol == IPPROTO_IPIP ? "ip" : 387 (p->iph.protocol == IPPROTO_GRE ? "gre" : 388 (p->iph.protocol == IPPROTO_IPV6 ? "ipv6" : "unknown")), 389 p->iph.daddr ? s1 : "any", p->iph.saddr ? s2 : "any"); 390 if (p->link) { 391 char *n = do_ioctl_get_ifname(p->link); 392 if (n) { 393 printf(" dev %s ", n); 394 free(n); 395 } 396 } 397 if (p->iph.ttl) 398 printf(" ttl %d ", p->iph.ttl); 399 else 400 printf(" ttl inherit "); 401 if (p->iph.tos) { 402 SPRINT_BUF(b1); 403 printf(" tos"); 404 if (p->iph.tos & 1) 405 printf(" inherit"); 406 if (p->iph.tos & ~1) 407 printf("%c%s ", p->iph.tos & 1 ? '/' : ' ', 408 rtnl_dsfield_n2a(p->iph.tos & ~1, b1, sizeof(b1))); 409 } 410 if (!(p->iph.frag_off & htons(IP_DF))) 411 printf(" nopmtudisc"); 412 413 if ((p->i_flags & GRE_KEY) && (p->o_flags & GRE_KEY) && p->o_key == p->i_key) 414 printf(" key %s", s3); 415 else if ((p->i_flags | p->o_flags) & GRE_KEY) { 416 if (p->i_flags & GRE_KEY) 417 printf(" ikey %s ", s3); 418 if (p->o_flags & GRE_KEY) 419 printf(" okey %s ", s4); 420 } 421 422 if (p->i_flags & GRE_SEQ) 423 printf("%c Drop packets out of sequence.\n", _SL_); 424 if (p->i_flags & GRE_CSUM) 425 printf("%c Checksum in received packet is required.", _SL_); 426 if (p->o_flags & GRE_SEQ) 427 printf("%c Sequence packets on output.", _SL_); 428 if (p->o_flags & GRE_CSUM) 429 printf("%c Checksum output packets.", _SL_); 430} 431 432static void do_tunnels_list(struct ip_tunnel_parm *p) 433{ 434 char name[IFNAMSIZ]; 435 unsigned long rx_bytes, rx_packets, rx_errs, rx_drops, 436 rx_fifo, rx_frame, 437 tx_bytes, tx_packets, tx_errs, tx_drops, 438 tx_fifo, tx_colls, tx_carrier, rx_multi; 439 int type; 440 struct ip_tunnel_parm p1; 441 char buf[512]; 442 FILE *fp = fopen_or_warn("/proc/net/dev", "r"); 443 444 if (fp == NULL) { 445 return; 446 } 447 448 fgets(buf, sizeof(buf), fp); 449 fgets(buf, sizeof(buf), fp); 450 451 while (fgets(buf, sizeof(buf), fp) != NULL) { 452 char *ptr; 453 454 /*buf[sizeof(buf) - 1] = 0; - fgets is safe anyway */ 455 ptr = strchr(buf, ':'); 456 if (ptr == NULL || 457 (*ptr++ = 0, sscanf(buf, "%s", name) != 1)) { 458 bb_error_msg("wrong format of /proc/net/dev"); 459 return; 460 } 461 if (sscanf(ptr, "%lu%lu%lu%lu%lu%lu%lu%*d%lu%lu%lu%lu%lu%lu%lu", 462 &rx_bytes, &rx_packets, &rx_errs, &rx_drops, 463 &rx_fifo, &rx_frame, &rx_multi, 464 &tx_bytes, &tx_packets, &tx_errs, &tx_drops, 465 &tx_fifo, &tx_colls, &tx_carrier) != 14) 466 continue; 467 if (p->name[0] && strcmp(p->name, name)) 468 continue; 469 type = do_ioctl_get_iftype(name); 470 if (type == -1) { 471 bb_error_msg("cannot get type of [%s]", name); 472 continue; 473 } 474 if (type != ARPHRD_TUNNEL && type != ARPHRD_IPGRE && type != ARPHRD_SIT) 475 continue; 476 memset(&p1, 0, sizeof(p1)); 477 if (do_get_ioctl(name, &p1)) 478 continue; 479 if ((p->link && p1.link != p->link) || 480 (p->name[0] && strcmp(p1.name, p->name)) || 481 (p->iph.daddr && p1.iph.daddr != p->iph.daddr) || 482 (p->iph.saddr && p1.iph.saddr != p->iph.saddr) || 483 (p->i_key && p1.i_key != p->i_key)) 484 continue; 485 print_tunnel(&p1); 486 puts(""); 487 } 488} 489 490/* Return value becomes exitcode. It's okay to not return at all */ 491static int do_show(int argc, char **argv) 492{ 493 int err; 494 struct ip_tunnel_parm p; 495 496 parse_args(argc, argv, SIOCGETTUNNEL, &p); 497 498 switch (p.iph.protocol) { 499 case IPPROTO_IPIP: 500 err = do_get_ioctl(p.name[0] ? p.name : "tunl0", &p); 501 break; 502 case IPPROTO_GRE: 503 err = do_get_ioctl(p.name[0] ? p.name : "gre0", &p); 504 break; 505 case IPPROTO_IPV6: 506 err = do_get_ioctl(p.name[0] ? p.name : "sit0", &p); 507 break; 508 default: 509 do_tunnels_list(&p); 510 return 0; 511 } 512 if (err) 513 return -1; 514 515 print_tunnel(&p); 516 puts(""); 517 return 0; 518} 519 520/* Return value becomes exitcode. It's okay to not return at all */ 521int do_iptunnel(int argc, char **argv) 522{ 523 static const char keywords[] ALIGN1 = 524 "add\0""change\0""delete\0""show\0""list\0""lst\0"; 525 enum { ARG_add = 0, ARG_change, ARG_del, ARG_show, ARG_list, ARG_lst }; 526 int key; 527 528 if (argc) { 529 key = index_in_substrings(keywords, *argv); 530 if (key < 0) 531 bb_error_msg_and_die(bb_msg_invalid_arg, *argv, applet_name); 532 --argc; 533 ++argv; 534 if (key == ARG_add) 535 return do_add(SIOCADDTUNNEL, argc, argv); 536 if (key == ARG_change) 537 return do_add(SIOCCHGTUNNEL, argc, argv); 538 if (key == ARG_del) 539 return do_del(argc, argv); 540 } 541 return do_show(argc, argv); 542} 543