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