1/* 2 * Copyright (C)2006 USAGI/WIDE Project 3 * 4 * This program is free software; you can redistribute it and/or modify 5 * it under the terms of the GNU General Public License as published by 6 * the Free Software Foundation; either version 2 of the License, or 7 * (at your option) any later version. 8 * 9 * This program is distributed in the hope that it will be useful, 10 * but WITHOUT ANY WARRANTY; without even the implied warranty of 11 * MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the 12 * GNU General Public License for more details. 13 * 14 * You should have received a copy of the GNU General Public License 15 * along with this program; if not, write to the Free Software 16 * Foundation, Inc., 59 Temple Place, Suite 330, Boston, MA 02111-1307 USA 17 */ 18/* 19 * Author: 20 * Masahide NAKAMURA @USAGI 21 */ 22 23#include <stdio.h> 24#include <string.h> 25#include <stdlib.h> 26#include <unistd.h> 27#include <sys/types.h> 28#include <sys/socket.h> 29#include <arpa/inet.h> 30#include <sys/ioctl.h> 31#include <linux/ip.h> 32#include <linux/if.h> 33#include <linux/if_arp.h> 34#include <linux/if_tunnel.h> 35#include <linux/ip6_tunnel.h> 36 37#include "utils.h" 38#include "tunnel.h" 39#include "ip_common.h" 40 41#define IP6_FLOWINFO_TCLASS htonl(0x0FF00000) 42#define IP6_FLOWINFO_FLOWLABEL htonl(0x000FFFFF) 43 44#define DEFAULT_TNL_HOP_LIMIT (64) 45 46static void usage(void) __attribute__((noreturn)); 47 48static void usage(void) 49{ 50 fprintf(stderr, "Usage: ip -f inet6 tunnel { add | change | del | show } [ NAME ]\n"); 51 fprintf(stderr, " [ mode { ip6ip6 | ipip6 | any } ]\n"); 52 fprintf(stderr, " [ remote ADDR local ADDR ] [ dev PHYS_DEV ]\n"); 53 fprintf(stderr, " [ encaplimit ELIM ]\n"); 54 fprintf(stderr ," [ hoplimit TTL ] [ tclass TCLASS ] [ flowlabel FLOWLABEL ]\n"); 55 fprintf(stderr, " [ dscp inherit ]\n"); 56 fprintf(stderr, "\n"); 57 fprintf(stderr, "Where: NAME := STRING\n"); 58 fprintf(stderr, " ADDR := IPV6_ADDRESS\n"); 59 fprintf(stderr, " ELIM := { none | 0..255 }(default=%d)\n", 60 IPV6_DEFAULT_TNL_ENCAP_LIMIT); 61 fprintf(stderr, " TTL := 0..255 (default=%d)\n", 62 DEFAULT_TNL_HOP_LIMIT); 63 fprintf(stderr, " TOS := { 0x0..0xff | inherit }\n"); 64 fprintf(stderr, " FLOWLABEL := { 0x0..0xfffff | inherit }\n"); 65 exit(-1); 66} 67 68static void print_tunnel(struct ip6_tnl_parm *p) 69{ 70 char remote[64]; 71 char local[64]; 72 73 inet_ntop(AF_INET6, &p->raddr, remote, sizeof(remote)); 74 inet_ntop(AF_INET6, &p->laddr, local, sizeof(local)); 75 76 printf("%s: %s/ipv6 remote %s local %s", 77 p->name, tnl_strproto(p->proto), remote, local); 78 if (p->link) { 79 const char *n = ll_index_to_name(p->link); 80 if (n) 81 printf(" dev %s", n); 82 } 83 84 if (p->flags & IP6_TNL_F_IGN_ENCAP_LIMIT) 85 printf(" encaplimit none"); 86 else 87 printf(" encaplimit %u", p->encap_limit); 88 89 printf(" hoplimit %u", p->hop_limit); 90 91 if (p->flags & IP6_TNL_F_USE_ORIG_TCLASS) 92 printf(" tclass inherit"); 93 else { 94 __u32 val = ntohl(p->flowinfo & IP6_FLOWINFO_TCLASS); 95 printf(" tclass 0x%02x", (__u8)(val >> 20)); 96 } 97 98 if (p->flags & IP6_TNL_F_USE_ORIG_FLOWLABEL) 99 printf(" flowlabel inherit"); 100 else 101 printf(" flowlabel 0x%05x", ntohl(p->flowinfo & IP6_FLOWINFO_FLOWLABEL)); 102 103 printf(" (flowinfo 0x%08x)", ntohl(p->flowinfo)); 104 105 if (p->flags & IP6_TNL_F_RCV_DSCP_COPY) 106 printf(" dscp inherit"); 107} 108 109static int parse_args(int argc, char **argv, int cmd, struct ip6_tnl_parm *p) 110{ 111 int count = 0; 112 char medium[IFNAMSIZ]; 113 114 memset(medium, 0, sizeof(medium)); 115 116 while (argc > 0) { 117 if (strcmp(*argv, "mode") == 0) { 118 NEXT_ARG(); 119 if (strcmp(*argv, "ipv6/ipv6") == 0 || 120 strcmp(*argv, "ip6ip6") == 0) 121 p->proto = IPPROTO_IPV6; 122 else if (strcmp(*argv, "ip/ipv6") == 0 || 123 strcmp(*argv, "ipv4/ipv6") == 0 || 124 strcmp(*argv, "ipip6") == 0 || 125 strcmp(*argv, "ip4ip6") == 0) 126 p->proto = IPPROTO_IPIP; 127 else if (strcmp(*argv, "any/ipv6") == 0 || 128 strcmp(*argv, "any") == 0) 129 p->proto = 0; 130 else { 131 fprintf(stderr,"Cannot guess tunnel mode.\n"); 132 exit(-1); 133 } 134 } else if (strcmp(*argv, "remote") == 0) { 135 inet_prefix raddr; 136 NEXT_ARG(); 137 get_prefix(&raddr, *argv, preferred_family); 138 if (raddr.family == AF_UNSPEC) 139 invarg("\"remote\" address family is AF_UNSPEC", *argv); 140 memcpy(&p->raddr, &raddr.data, sizeof(p->raddr)); 141 } else if (strcmp(*argv, "local") == 0) { 142 inet_prefix laddr; 143 NEXT_ARG(); 144 get_prefix(&laddr, *argv, preferred_family); 145 if (laddr.family == AF_UNSPEC) 146 invarg("\"local\" address family is AF_UNSPEC", *argv); 147 memcpy(&p->laddr, &laddr.data, sizeof(p->laddr)); 148 } else if (strcmp(*argv, "dev") == 0) { 149 NEXT_ARG(); 150 strncpy(medium, *argv, IFNAMSIZ - 1); 151 } else if (strcmp(*argv, "encaplimit") == 0) { 152 NEXT_ARG(); 153 if (strcmp(*argv, "none") == 0) { 154 p->flags |= IP6_TNL_F_IGN_ENCAP_LIMIT; 155 } else { 156 __u8 uval; 157 if (get_u8(&uval, *argv, 0) < -1) 158 invarg("invalid ELIM", *argv); 159 p->encap_limit = uval; 160 } 161 } else if (strcmp(*argv, "hoplimit") == 0 || 162 strcmp(*argv, "ttl") == 0 || 163 strcmp(*argv, "hlim") == 0) { 164 __u8 uval; 165 NEXT_ARG(); 166 if (get_u8(&uval, *argv, 0)) 167 invarg("invalid TTL", *argv); 168 p->hop_limit = uval; 169 } else if (strcmp(*argv, "tclass") == 0 || 170 strcmp(*argv, "tc") == 0 || 171 strcmp(*argv, "tos") == 0 || 172 matches(*argv, "dsfield") == 0) { 173 __u8 uval; 174 NEXT_ARG(); 175 if (strcmp(*argv, "inherit") == 0) 176 p->flags |= IP6_TNL_F_USE_ORIG_TCLASS; 177 else { 178 if (get_u8(&uval, *argv, 16)) 179 invarg("invalid TClass", *argv); 180 p->flowinfo |= htonl((__u32)uval << 20) & IP6_FLOWINFO_TCLASS; 181 p->flags &= ~IP6_TNL_F_USE_ORIG_TCLASS; 182 } 183 } else if (strcmp(*argv, "flowlabel") == 0 || 184 strcmp(*argv, "fl") == 0) { 185 __u32 uval; 186 NEXT_ARG(); 187 if (strcmp(*argv, "inherit") == 0) 188 p->flags |= IP6_TNL_F_USE_ORIG_FLOWLABEL; 189 else { 190 if (get_u32(&uval, *argv, 16)) 191 invarg("invalid Flowlabel", *argv); 192 if (uval > 0xFFFFF) 193 invarg("invalid Flowlabel", *argv); 194 p->flowinfo |= htonl(uval) & IP6_FLOWINFO_FLOWLABEL; 195 p->flags &= ~IP6_TNL_F_USE_ORIG_FLOWLABEL; 196 } 197 } else if (strcmp(*argv, "dscp") == 0) { 198 NEXT_ARG(); 199 if (strcmp(*argv, "inherit") != 0) 200 invarg("not inherit", *argv); 201 p->flags |= IP6_TNL_F_RCV_DSCP_COPY; 202 } else { 203 if (strcmp(*argv, "name") == 0) { 204 NEXT_ARG(); 205 } 206 if (matches(*argv, "help") == 0) 207 usage(); 208 if (p->name[0]) 209 duparg2("name", *argv); 210 strncpy(p->name, *argv, IFNAMSIZ - 1); 211 if (cmd == SIOCCHGTUNNEL && count == 0) { 212 struct ip6_tnl_parm old_p; 213 memset(&old_p, 0, sizeof(old_p)); 214 if (tnl_get_ioctl(*argv, &old_p)) 215 return -1; 216 *p = old_p; 217 } 218 } 219 count++; 220 argc--; argv++; 221 } 222 if (medium[0]) { 223 p->link = ll_name_to_index(medium); 224 if (p->link == 0) 225 return -1; 226 } 227 return 0; 228} 229 230static void ip6_tnl_parm_init(struct ip6_tnl_parm *p, int apply_default) 231{ 232 memset(p, 0, sizeof(*p)); 233 p->proto = IPPROTO_IPV6; 234 if (apply_default) { 235 p->hop_limit = DEFAULT_TNL_HOP_LIMIT; 236 p->encap_limit = IPV6_DEFAULT_TNL_ENCAP_LIMIT; 237 } 238} 239 240/* 241 * @p1: user specified parameter 242 * @p2: database entry 243 */ 244static int ip6_tnl_parm_match(const struct ip6_tnl_parm *p1, 245 const struct ip6_tnl_parm *p2) 246{ 247 return ((!p1->link || p1->link == p2->link) && 248 (!p1->name[0] || strcmp(p1->name, p2->name) == 0) && 249 (memcmp(&p1->laddr, &in6addr_any, sizeof(p1->laddr)) == 0 || 250 memcmp(&p1->laddr, &p2->laddr, sizeof(p1->laddr)) == 0) && 251 (memcmp(&p1->raddr, &in6addr_any, sizeof(p1->raddr)) == 0 || 252 memcmp(&p1->raddr, &p2->raddr, sizeof(p1->raddr)) == 0) && 253 (!p1->proto || !p2->proto || p1->proto == p2->proto) && 254 (!p1->encap_limit || p1->encap_limit == p2->encap_limit) && 255 (!p1->hop_limit || p1->hop_limit == p2->hop_limit) && 256 (!(p1->flowinfo & IP6_FLOWINFO_TCLASS) || 257 !((p1->flowinfo ^ p2->flowinfo) & IP6_FLOWINFO_TCLASS)) && 258 (!(p1->flowinfo & IP6_FLOWINFO_FLOWLABEL) || 259 !((p1->flowinfo ^ p2->flowinfo) & IP6_FLOWINFO_FLOWLABEL)) && 260 (!p1->flags || (p1->flags & p2->flags))); 261} 262 263static int do_tunnels_list(struct ip6_tnl_parm *p) 264{ 265 char buf[512]; 266 int err = -1; 267 FILE *fp = fopen("/proc/net/dev", "r"); 268 if (fp == NULL) { 269 perror("fopen"); 270 goto end; 271 } 272 273 /* skip two lines at the begenning of the file */ 274 if (!fgets(buf, sizeof(buf), fp) || 275 !fgets(buf, sizeof(buf), fp)) { 276 fprintf(stderr, "/proc/net/dev read error\n"); 277 return -1; 278 } 279 280 while (fgets(buf, sizeof(buf), fp) != NULL) { 281 char name[IFNAMSIZ]; 282 int index, type; 283 unsigned long rx_bytes, rx_packets, rx_errs, rx_drops, 284 rx_fifo, rx_frame, 285 tx_bytes, tx_packets, tx_errs, tx_drops, 286 tx_fifo, tx_colls, tx_carrier, rx_multi; 287 struct ip6_tnl_parm p1; 288 char *ptr; 289 290 buf[sizeof(buf) - 1] = '\0'; 291 if ((ptr = strchr(buf, ':')) == NULL || 292 (*ptr++ = 0, sscanf(buf, "%s", name) != 1)) { 293 fprintf(stderr, "Wrong format of /proc/net/dev. Sorry.\n"); 294 goto end; 295 } 296 if (sscanf(ptr, "%ld%ld%ld%ld%ld%ld%ld%*d%ld%ld%ld%ld%ld%ld%ld", 297 &rx_bytes, &rx_packets, &rx_errs, &rx_drops, 298 &rx_fifo, &rx_frame, &rx_multi, 299 &tx_bytes, &tx_packets, &tx_errs, &tx_drops, 300 &tx_fifo, &tx_colls, &tx_carrier) != 14) 301 continue; 302 if (p->name[0] && strcmp(p->name, name)) 303 continue; 304 index = ll_name_to_index(name); 305 if (index == 0) 306 continue; 307 type = ll_index_to_type(index); 308 if (type == -1) { 309 fprintf(stderr, "Failed to get type of [%s]\n", name); 310 continue; 311 } 312 if (type != ARPHRD_TUNNEL6) 313 continue; 314 memset(&p1, 0, sizeof(p1)); 315 ip6_tnl_parm_init(&p1, 0); 316 strcpy(p1.name, name); 317 p1.link = ll_name_to_index(p1.name); 318 if (p1.link == 0) 319 continue; 320 if (tnl_get_ioctl(p1.name, &p1)) 321 continue; 322 if (!ip6_tnl_parm_match(p, &p1)) 323 continue; 324 print_tunnel(&p1); 325 if (show_stats) { 326 printf("%s", _SL_); 327 printf("RX: Packets Bytes Errors CsumErrs OutOfSeq Mcasts%s", _SL_); 328 printf(" %-10ld %-12ld %-6ld %-8ld %-8ld %-8ld%s", 329 rx_packets, rx_bytes, rx_errs, rx_frame, rx_fifo, rx_multi, _SL_); 330 printf("TX: Packets Bytes Errors DeadLoop NoRoute NoBufs%s", _SL_); 331 printf(" %-10ld %-12ld %-6ld %-8ld %-8ld %-6ld", 332 tx_packets, tx_bytes, tx_errs, tx_colls, tx_carrier, tx_drops); 333 } 334 printf("\n"); 335 } 336 err = 0; 337 338 end: 339 if (fp) 340 fclose(fp); 341 return err; 342} 343 344static int do_show(int argc, char **argv) 345{ 346 struct ip6_tnl_parm p; 347 348 ll_init_map(&rth); 349 ip6_tnl_parm_init(&p, 0); 350 p.proto = 0; /* default to any */ 351 352 if (parse_args(argc, argv, SIOCGETTUNNEL, &p) < 0) 353 return -1; 354 355 if (!p.name[0] || show_stats) 356 do_tunnels_list(&p); 357 else { 358 if (tnl_get_ioctl(p.name, &p)) 359 return -1; 360 print_tunnel(&p); 361 printf("\n"); 362 } 363 364 return 0; 365} 366 367static int do_add(int cmd, int argc, char **argv) 368{ 369 struct ip6_tnl_parm p; 370 371 ip6_tnl_parm_init(&p, 1); 372 373 if (parse_args(argc, argv, cmd, &p) < 0) 374 return -1; 375 376 return tnl_add_ioctl(cmd, 377 cmd == SIOCCHGTUNNEL && p.name[0] ? 378 p.name : "ip6tnl0", p.name, &p); 379} 380 381static int do_del(int argc, char **argv) 382{ 383 struct ip6_tnl_parm p; 384 385 ip6_tnl_parm_init(&p, 1); 386 387 if (parse_args(argc, argv, SIOCDELTUNNEL, &p) < 0) 388 return -1; 389 390 return tnl_del_ioctl(p.name[0] ? p.name : "ip6tnl0", p.name, &p); 391} 392 393int do_ip6tunnel(int argc, char **argv) 394{ 395 switch (preferred_family) { 396 case AF_UNSPEC: 397 preferred_family = AF_INET6; 398 break; 399 case AF_INET6: 400 break; 401 default: 402 fprintf(stderr, "Unsupported family:%d\n", preferred_family); 403 exit(-1); 404 } 405 406 if (argc > 0) { 407 if (matches(*argv, "add") == 0) 408 return do_add(SIOCADDTUNNEL, argc - 1, argv + 1); 409 if (matches(*argv, "change") == 0) 410 return do_add(SIOCCHGTUNNEL, argc - 1, argv + 1); 411 if (matches(*argv, "del") == 0) 412 return do_del(argc - 1, argv + 1); 413 if (matches(*argv, "show") == 0 || 414 matches(*argv, "lst") == 0 || 415 matches(*argv, "list") == 0) 416 return do_show(argc - 1, argv + 1); 417 if (matches(*argv, "help") == 0) 418 usage(); 419 } else 420 return do_show(0, NULL); 421 422 fprintf(stderr, "Command \"%s\" is unknown, try \"ip -f inet6 tunnel help\".\n", *argv); 423 exit(-1); 424} 425