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