Masahide NAKAMURA | 9447a0d | 2006-11-24 12:27:06 +0900 | [diff] [blame^] | 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 | * based on: |
| 20 | * $Id: s.ipv6tunnel.c 1.7 02/12/11 11:21:51+02:00 antti@traci.mipl.mediapoli.com $ |
| 21 | * |
| 22 | */ |
| 23 | /* |
| 24 | * Author: |
| 25 | * Masahide NAKAMURA @USAGI |
| 26 | */ |
| 27 | |
| 28 | #include <stdio.h> |
| 29 | #include <string.h> |
| 30 | #include <stdlib.h> |
| 31 | #include <errno.h> |
| 32 | |
| 33 | #include <sys/ioctl.h> |
| 34 | #include <sys/socket.h> |
| 35 | #include <net/if.h> |
| 36 | #include <netinet/in.h> |
| 37 | #include <netdb.h> |
| 38 | #include <unistd.h> |
| 39 | #include <arpa/inet.h> |
| 40 | |
| 41 | #include <asm/types.h> |
| 42 | #include <linux/sockios.h> |
| 43 | #include <linux/ip.h> |
| 44 | #include <linux/if_tunnel.h> |
| 45 | |
| 46 | #include "ipv6_tunnel.h" |
| 47 | |
| 48 | |
| 49 | int tunnel_local_packets = 0; |
| 50 | |
| 51 | static void usage(void) |
| 52 | { |
| 53 | fprintf(stderr, "Usage: ipv6tunnel { add | change | del | show } [ NAME ]\n"); |
| 54 | fprintf(stderr, " [ --allow-local-packets ]\n"); |
| 55 | fprintf(stderr, " [ --use-original-traffic-class ]\n"); |
| 56 | fprintf(stderr, " [ remote ADDR local ADDR ]\n"); |
| 57 | fprintf(stderr, " [ dev PHYS_DEV ]\n"); |
| 58 | fprintf(stderr, " [ encaplimit TEL ]\n"); |
| 59 | fprintf(stderr, " [ hoplimit HOPLIMIT ]\n"); |
| 60 | fprintf(stderr, " [ flowlabel FL ]\n"); |
| 61 | fprintf(stderr, "Where: NAME := STRING\n"); |
| 62 | fprintf(stderr, " ADDR := IPV6_ADDRESS\n"); |
| 63 | fprintf(stderr, " HOPLIMIT := 0..255\n"); |
| 64 | fprintf(stderr, " TEL := { none | 0..255 }\n"); |
| 65 | fprintf(stderr, " FL := 0x0..0xfffff\n"); |
| 66 | fprintf(stderr, " PHYS_DEV := STRING\n"); |
| 67 | exit(-1); |
| 68 | } |
| 69 | |
| 70 | static int do_ioctl_get_ifindex(char *dev) |
| 71 | { |
| 72 | struct ifreq ifr; |
| 73 | int fd; |
| 74 | int err; |
| 75 | |
| 76 | // fprintf(stderr, "do_ioctl_get_ifindex\n"); |
| 77 | |
| 78 | strcpy(ifr.ifr_name, dev); |
| 79 | fd = socket(AF_INET6, SOCK_DGRAM, 0); |
| 80 | err = ioctl(fd, SIOCGIFINDEX, &ifr); |
| 81 | if (err) { |
| 82 | perror("ioctl"); |
| 83 | return 0; |
| 84 | } |
| 85 | close(fd); |
| 86 | return ifr.ifr_ifindex; |
| 87 | } |
| 88 | |
| 89 | static char *do_ioctl_get_ifname(int idx) |
| 90 | { |
| 91 | static struct ifreq ifr; |
| 92 | int fd; |
| 93 | int err; |
| 94 | |
| 95 | // fprintf(stderr, "do_ioctl_get_ifname\n"); |
| 96 | |
| 97 | ifr.ifr_ifindex = idx; |
| 98 | fd = socket(AF_INET6, SOCK_DGRAM, 0); |
| 99 | err = ioctl(fd, SIOCGIFNAME, &ifr); |
| 100 | if (err) { |
| 101 | perror("ioctl"); |
| 102 | return NULL; |
| 103 | } |
| 104 | close(fd); |
| 105 | return ifr.ifr_name; |
| 106 | } |
| 107 | |
| 108 | static int do_get_ioctl(char *basedev, struct ipv6_tnl_parm *p) |
| 109 | { |
| 110 | struct ifreq ifr; |
| 111 | int fd; |
| 112 | int err; |
| 113 | |
| 114 | // fprintf(stderr, "do_get_ioctl\n"); |
| 115 | |
| 116 | strcpy(ifr.ifr_name, basedev); |
| 117 | ifr.ifr_ifru.ifru_data = (void *)p; |
| 118 | fd = socket(AF_INET6, SOCK_DGRAM, 0); |
| 119 | err = ioctl(fd, SIOCGETTUNNEL, &ifr); |
| 120 | if (err) |
| 121 | perror("ioctl"); |
| 122 | close(fd); |
| 123 | return err; |
| 124 | } |
| 125 | |
| 126 | static int do_add_ioctl(int cmd, char *basedev, struct ipv6_tnl_parm *p) |
| 127 | { |
| 128 | struct ifreq ifr; |
| 129 | int fd; |
| 130 | int err; |
| 131 | |
| 132 | // fprintf(stderr, "do_add_ioctl\n"); |
| 133 | |
| 134 | strcpy(ifr.ifr_name, basedev); |
| 135 | ifr.ifr_ifru.ifru_data = (void *)p; |
| 136 | fd = socket(AF_INET6, SOCK_DGRAM, 0); |
| 137 | err = ioctl(fd, cmd, &ifr); |
| 138 | if (err) |
| 139 | perror("ioctl"); |
| 140 | close(fd); |
| 141 | return err; |
| 142 | } |
| 143 | |
| 144 | |
| 145 | static int do_del_ioctl(char *basedev, struct ipv6_tnl_parm *p) |
| 146 | { |
| 147 | struct ifreq ifr; |
| 148 | int fd; |
| 149 | int err; |
| 150 | |
| 151 | // fprintf(stderr, "do_del_ioctl\n"); |
| 152 | |
| 153 | strcpy(ifr.ifr_name, basedev); |
| 154 | ifr.ifr_ifru.ifru_data = (void *)p; |
| 155 | fd = socket(AF_INET6, SOCK_DGRAM, 0); |
| 156 | err = ioctl(fd, SIOCDELTUNNEL, &ifr); |
| 157 | if (err) |
| 158 | perror("ioctl"); |
| 159 | close(fd); |
| 160 | return err; |
| 161 | } |
| 162 | |
| 163 | void print_tunnel(struct ipv6_tnl_parm *p) |
| 164 | { |
| 165 | char remote[64]; |
| 166 | char local[64]; |
| 167 | |
| 168 | inet_ntop(AF_INET6, &p->raddr, remote, sizeof(remote)); |
| 169 | inet_ntop(AF_INET6, &p->laddr, local, sizeof(local)); |
| 170 | |
| 171 | printf("%s: %s/IPv6 remote %s local %s", |
| 172 | p->name, |
| 173 | (p->proto == IPPROTO_IPV6 ? "IPv6" : "unknown"), |
| 174 | remote, local); |
| 175 | if (p->link) { |
| 176 | char *n = do_ioctl_get_ifname(p->link); |
| 177 | if (n) |
| 178 | printf(" dev %s", n); |
| 179 | } |
| 180 | if (p->encap_limit) |
| 181 | printf(" encaplimit %d", p->encap_limit); |
| 182 | |
| 183 | printf(" hoplimit %d", p->hop_limit); |
| 184 | |
| 185 | if (p->flow_lbl) |
| 186 | printf(" flowlabel 0x%x", p->flow_lbl); |
| 187 | |
| 188 | if (p->flags) { |
| 189 | char flags[33]; |
| 190 | char *fp = flags; |
| 191 | memset(flags, 0, 33); |
| 192 | if (p->flags & IPV6_TNL_F_IGN_ENCAP_LIMIT) { |
| 193 | *fp = 'E'; |
| 194 | fp++; |
| 195 | } |
| 196 | if (p->flags & IPV6_TNL_F_USE_ORIG_TCLASS) { |
| 197 | *fp = 'T'; |
| 198 | fp++; |
| 199 | } |
| 200 | if (p->flags & IPV6_TNL_F_ALLOW_LOCAL) { |
| 201 | *fp = 'L'; |
| 202 | fp++; |
| 203 | } |
| 204 | if (p->flags & IPV6_TNL_F_KERNEL_DEV) { |
| 205 | *fp = 'K'; |
| 206 | fp++; |
| 207 | } |
| 208 | if (p->flags & IPV6_TNL_F_MIPV6_DEV) { |
| 209 | *fp = 'M'; |
| 210 | fp++; |
| 211 | } |
| 212 | if (p->flags & IPV6_TNL_F_RCV_ONLY) { |
| 213 | *fp = 'R'; |
| 214 | fp++; |
| 215 | } |
| 216 | printf(" flags %s", flags); |
| 217 | } |
| 218 | printf("\n"); |
| 219 | } |
| 220 | |
| 221 | void resolve_name(char *name, struct in6_addr *ip6) |
| 222 | { |
| 223 | struct addrinfo ai, *res; |
| 224 | int err; |
| 225 | memset(&ai, 0, sizeof(struct addrinfo)); |
| 226 | ai.ai_family = AF_INET6; |
| 227 | ai.ai_protocol = IPPROTO_IPV6; |
| 228 | ai.ai_flags = AI_NUMERICHOST; |
| 229 | err = getaddrinfo(name, NULL, &ai, &res); |
| 230 | if (err) |
| 231 | exit(-1); |
| 232 | |
| 233 | *ip6 = ((struct sockaddr_in6 *) (res->ai_addr))->sin6_addr; |
| 234 | freeaddrinfo(res); |
| 235 | } |
| 236 | |
| 237 | int get_u8(__u8 *val, char *arg) |
| 238 | { |
| 239 | unsigned long res = 0; |
| 240 | char *ptr; |
| 241 | |
| 242 | if (!arg || !*arg) |
| 243 | return -1; |
| 244 | res = strtoul(arg, &ptr, 0); |
| 245 | if (!ptr || ptr == arg || *ptr || res > 0xFF) |
| 246 | return -1; |
| 247 | *val = (__u8) res; |
| 248 | return 0; |
| 249 | } |
| 250 | |
| 251 | int get_u20(__u32 *val, char *arg) |
| 252 | { |
| 253 | unsigned long res = 0; |
| 254 | char *ptr; |
| 255 | |
| 256 | if (!arg || !*arg) |
| 257 | return -1; |
| 258 | res = strtoul(arg, &ptr, 0); |
| 259 | if (!ptr || ptr == arg || *ptr || res > 0xFFFFF) |
| 260 | return -1; |
| 261 | *val = res; |
| 262 | return 0; |
| 263 | } |
| 264 | |
| 265 | static int parse_args(int argc, char **argv, struct ipv6_tnl_parm *p) |
| 266 | { |
| 267 | char medium[IFNAMSIZ]; |
| 268 | |
| 269 | // fprintf(stderr, "parse_args\n"); |
| 270 | |
| 271 | memset(p, 0, sizeof(*p)); |
| 272 | p->hop_limit = -1; |
| 273 | p->encap_limit = IPV6_DEFAULT_TNL_ENCAP_LIMIT; |
| 274 | p->proto = IPPROTO_IPV6; |
| 275 | memset(&medium, 0, sizeof(medium)); |
| 276 | |
| 277 | while (argc > 0) { |
| 278 | if (!strcmp(*argv, "--help") || !strcmp(*argv, "-h")) { |
| 279 | usage(); |
| 280 | } else if (!strcmp(*argv, "--allow-local-packets")) { |
| 281 | p->flags |= IPV6_TNL_F_ALLOW_LOCAL; |
| 282 | } else if (!strcmp(*argv, "--use-original-traffic-class")) { |
| 283 | p->flags |= IPV6_TNL_F_USE_ORIG_TCLASS; |
| 284 | } else if (!strcmp(*argv, "remote")) { |
| 285 | argv++; |
| 286 | if (--argc <= 0) |
| 287 | usage(); |
| 288 | resolve_name(*argv, &p->raddr); |
| 289 | } else if (!strcmp(*argv, "local")) { |
| 290 | argv++; |
| 291 | if (--argc <= 0) |
| 292 | usage(); |
| 293 | resolve_name(*argv, &p->laddr); |
| 294 | } else if (!strcmp(*argv, "dev")) { |
| 295 | argv++; |
| 296 | if (--argc <= 0) |
| 297 | usage(); |
| 298 | strncpy(medium, *argv, IFNAMSIZ - 1); |
| 299 | } else if (!strcmp(*argv, "hoplimit")) { |
| 300 | __u8 uval; |
| 301 | argv++; |
| 302 | if (--argc <= 0) |
| 303 | usage(); |
| 304 | if (get_u8(&uval, *argv) < -1) |
| 305 | usage(); |
| 306 | p->hop_limit = uval; |
| 307 | } else if (!strcmp(*argv, "flowlabel")) { |
| 308 | __u32 uval; |
| 309 | argv++; |
| 310 | if (--argc <= 0) |
| 311 | usage(); |
| 312 | if (get_u20(&uval, *argv) < -1) |
| 313 | usage(); |
| 314 | p->flow_lbl = uval; |
| 315 | } else if (!strcmp(*argv, "encaplimit")) { |
| 316 | argv++; |
| 317 | if (--argc <= 0) |
| 318 | usage(); |
| 319 | if (!strcmp(*argv, "none")) { |
| 320 | p->flags |= IPV6_TNL_F_IGN_ENCAP_LIMIT; |
| 321 | } else { |
| 322 | __u8 uval; |
| 323 | if (get_u8(&uval, *argv) < -1) |
| 324 | usage(); |
| 325 | p->encap_limit = uval; |
| 326 | } |
| 327 | } else { |
| 328 | if (p->name[0]) |
| 329 | usage(); |
| 330 | strncpy(p->name, *argv, IFNAMSIZ - 1); |
| 331 | } |
| 332 | argc--; argv++; |
| 333 | } |
| 334 | if (medium[0]) { |
| 335 | p->link = do_ioctl_get_ifindex(medium); |
| 336 | if (p->link == 0) |
| 337 | return -1; |
| 338 | } |
| 339 | return 0; |
| 340 | } |
| 341 | |
| 342 | static int do_show(int argc, char **argv) |
| 343 | { |
| 344 | struct ipv6_tnl_parm p; |
| 345 | |
| 346 | if (parse_args(argc, argv, &p) < 0) |
| 347 | return -1; |
| 348 | |
| 349 | if (do_get_ioctl(p.name[0] ? p.name : "ip6tnl0", &p)) |
| 350 | return -1; |
| 351 | |
| 352 | print_tunnel(&p); |
| 353 | return 0; |
| 354 | } |
| 355 | |
| 356 | |
| 357 | |
| 358 | |
| 359 | static int do_add(int cmd, int argc, char **argv) |
| 360 | { |
| 361 | struct ipv6_tnl_parm p; |
| 362 | |
| 363 | if (parse_args(argc, argv, &p) < 0) |
| 364 | return -1; |
| 365 | |
| 366 | return do_add_ioctl(cmd, |
| 367 | cmd == SIOCCHGTUNNEL && p.name[0] ? |
| 368 | p.name : "ip6tnl0", &p); |
| 369 | } |
| 370 | |
| 371 | int do_del(int argc, char **argv) |
| 372 | { |
| 373 | struct ipv6_tnl_parm p; |
| 374 | |
| 375 | if (parse_args(argc, argv, &p) < 0) |
| 376 | return -1; |
| 377 | |
| 378 | return do_del_ioctl(p.name[0] ? p.name : "ip6tnl0", &p); |
| 379 | } |
| 380 | |
| 381 | |
| 382 | int main(int argc, char **argv) |
| 383 | { |
| 384 | argc--; |
| 385 | argv++; |
| 386 | if (argc > 0) { |
| 387 | if (strcmp(*argv, "add") == 0) |
| 388 | return do_add(SIOCADDTUNNEL, argc - 1, argv + 1); |
| 389 | else if (strcmp(*argv, "change") == 0) |
| 390 | return do_add(SIOCCHGTUNNEL, argc - 1, argv + 1); |
| 391 | else if (strcmp(*argv, "del") == 0) |
| 392 | return do_del(argc - 1, argv + 1); |
| 393 | else if (strcmp(*argv, "show") == 0) |
| 394 | return do_show(argc - 1, argv + 1); |
| 395 | else |
| 396 | usage(); |
| 397 | |
| 398 | } else |
| 399 | return do_show(0, NULL); |
| 400 | |
| 401 | return 0; |
| 402 | } |