diff --git a/ip/ip6tunnel.c b/ip/ip6tunnel.c new file mode 100644 index 00000000..c8578e5c --- /dev/null +++ b/ip/ip6tunnel.c @@ -0,0 +1,402 @@ +/* + * Copyright (C)2006 USAGI/WIDE Project + * + * This program is free software; you can redistribute it and/or modify + * it under the terms of the GNU General Public License as published by + * the Free Software Foundation; either version 2 of the License, or + * (at your option) any later version. + * + * This program is distributed in the hope that it will be useful, + * but WITHOUT ANY WARRANTY; without even the implied warranty of + * MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the + * GNU General Public License for more details. + * + * You should have received a copy of the GNU General Public License + * along with this program; if not, write to the Free Software + * Foundation, Inc., 59 Temple Place, Suite 330, Boston, MA 02111-1307 USA + */ +/* + * based on: + * $Id: s.ipv6tunnel.c 1.7 02/12/11 11:21:51+02:00 antti@traci.mipl.mediapoli.com $ + * + */ +/* + * Author: + * Masahide NAKAMURA @USAGI + */ + +#include +#include +#include +#include + +#include +#include +#include +#include +#include +#include +#include + +#include +#include +#include +#include + +#include "ipv6_tunnel.h" + + +int tunnel_local_packets = 0; + +static void usage(void) +{ + fprintf(stderr, "Usage: ipv6tunnel { add | change | del | show } [ NAME ]\n"); + fprintf(stderr, " [ --allow-local-packets ]\n"); + fprintf(stderr, " [ --use-original-traffic-class ]\n"); + fprintf(stderr, " [ remote ADDR local ADDR ]\n"); + fprintf(stderr, " [ dev PHYS_DEV ]\n"); + fprintf(stderr, " [ encaplimit TEL ]\n"); + fprintf(stderr, " [ hoplimit HOPLIMIT ]\n"); + fprintf(stderr, " [ flowlabel FL ]\n"); + fprintf(stderr, "Where: NAME := STRING\n"); + fprintf(stderr, " ADDR := IPV6_ADDRESS\n"); + fprintf(stderr, " HOPLIMIT := 0..255\n"); + fprintf(stderr, " TEL := { none | 0..255 }\n"); + fprintf(stderr, " FL := 0x0..0xfffff\n"); + fprintf(stderr, " PHYS_DEV := STRING\n"); + exit(-1); +} + +static int do_ioctl_get_ifindex(char *dev) +{ + struct ifreq ifr; + int fd; + int err; + +// fprintf(stderr, "do_ioctl_get_ifindex\n"); + + strcpy(ifr.ifr_name, dev); + fd = socket(AF_INET6, SOCK_DGRAM, 0); + err = ioctl(fd, SIOCGIFINDEX, &ifr); + if (err) { + perror("ioctl"); + return 0; + } + close(fd); + return ifr.ifr_ifindex; +} + +static char *do_ioctl_get_ifname(int idx) +{ + static struct ifreq ifr; + int fd; + int err; + +// fprintf(stderr, "do_ioctl_get_ifname\n"); + + ifr.ifr_ifindex = idx; + fd = socket(AF_INET6, SOCK_DGRAM, 0); + err = ioctl(fd, SIOCGIFNAME, &ifr); + if (err) { + perror("ioctl"); + return NULL; + } + close(fd); + return ifr.ifr_name; +} + +static int do_get_ioctl(char *basedev, struct ipv6_tnl_parm *p) +{ + struct ifreq ifr; + int fd; + int err; + +// fprintf(stderr, "do_get_ioctl\n"); + + strcpy(ifr.ifr_name, basedev); + ifr.ifr_ifru.ifru_data = (void *)p; + fd = socket(AF_INET6, SOCK_DGRAM, 0); + err = ioctl(fd, SIOCGETTUNNEL, &ifr); + if (err) + perror("ioctl"); + close(fd); + return err; +} + +static int do_add_ioctl(int cmd, char *basedev, struct ipv6_tnl_parm *p) +{ + struct ifreq ifr; + int fd; + int err; + +// fprintf(stderr, "do_add_ioctl\n"); + + strcpy(ifr.ifr_name, basedev); + ifr.ifr_ifru.ifru_data = (void *)p; + fd = socket(AF_INET6, SOCK_DGRAM, 0); + err = ioctl(fd, cmd, &ifr); + if (err) + perror("ioctl"); + close(fd); + return err; +} + + +static int do_del_ioctl(char *basedev, struct ipv6_tnl_parm *p) +{ + struct ifreq ifr; + int fd; + int err; + +// fprintf(stderr, "do_del_ioctl\n"); + + strcpy(ifr.ifr_name, basedev); + ifr.ifr_ifru.ifru_data = (void *)p; + fd = socket(AF_INET6, SOCK_DGRAM, 0); + err = ioctl(fd, SIOCDELTUNNEL, &ifr); + if (err) + perror("ioctl"); + close(fd); + return err; +} + +void print_tunnel(struct ipv6_tnl_parm *p) +{ + char remote[64]; + char local[64]; + + inet_ntop(AF_INET6, &p->raddr, remote, sizeof(remote)); + inet_ntop(AF_INET6, &p->laddr, local, sizeof(local)); + + printf("%s: %s/IPv6 remote %s local %s", + p->name, + (p->proto == IPPROTO_IPV6 ? "IPv6" : "unknown"), + remote, local); + if (p->link) { + char *n = do_ioctl_get_ifname(p->link); + if (n) + printf(" dev %s", n); + } + if (p->encap_limit) + printf(" encaplimit %d", p->encap_limit); + + printf(" hoplimit %d", p->hop_limit); + + if (p->flow_lbl) + printf(" flowlabel 0x%x", p->flow_lbl); + + if (p->flags) { + char flags[33]; + char *fp = flags; + memset(flags, 0, 33); + if (p->flags & IPV6_TNL_F_IGN_ENCAP_LIMIT) { + *fp = 'E'; + fp++; + } + if (p->flags & IPV6_TNL_F_USE_ORIG_TCLASS) { + *fp = 'T'; + fp++; + } + if (p->flags & IPV6_TNL_F_ALLOW_LOCAL) { + *fp = 'L'; + fp++; + } + if (p->flags & IPV6_TNL_F_KERNEL_DEV) { + *fp = 'K'; + fp++; + } + if (p->flags & IPV6_TNL_F_MIPV6_DEV) { + *fp = 'M'; + fp++; + } + if (p->flags & IPV6_TNL_F_RCV_ONLY) { + *fp = 'R'; + fp++; + } + printf(" flags %s", flags); + } + printf("\n"); +} + +void resolve_name(char *name, struct in6_addr *ip6) +{ + struct addrinfo ai, *res; + int err; + memset(&ai, 0, sizeof(struct addrinfo)); + ai.ai_family = AF_INET6; + ai.ai_protocol = IPPROTO_IPV6; + ai.ai_flags = AI_NUMERICHOST; + err = getaddrinfo(name, NULL, &ai, &res); + if (err) + exit(-1); + + *ip6 = ((struct sockaddr_in6 *) (res->ai_addr))->sin6_addr; + freeaddrinfo(res); +} + +int get_u8(__u8 *val, char *arg) +{ + unsigned long res = 0; + char *ptr; + + if (!arg || !*arg) + return -1; + res = strtoul(arg, &ptr, 0); + if (!ptr || ptr == arg || *ptr || res > 0xFF) + return -1; + *val = (__u8) res; + return 0; +} + +int get_u20(__u32 *val, char *arg) +{ + unsigned long res = 0; + char *ptr; + + if (!arg || !*arg) + return -1; + res = strtoul(arg, &ptr, 0); + if (!ptr || ptr == arg || *ptr || res > 0xFFFFF) + return -1; + *val = res; + return 0; +} + +static int parse_args(int argc, char **argv, struct ipv6_tnl_parm *p) +{ + char medium[IFNAMSIZ]; + +// fprintf(stderr, "parse_args\n"); + + memset(p, 0, sizeof(*p)); + p->hop_limit = -1; + p->encap_limit = IPV6_DEFAULT_TNL_ENCAP_LIMIT; + p->proto = IPPROTO_IPV6; + memset(&medium, 0, sizeof(medium)); + + while (argc > 0) { + if (!strcmp(*argv, "--help") || !strcmp(*argv, "-h")) { + usage(); + } else if (!strcmp(*argv, "--allow-local-packets")) { + p->flags |= IPV6_TNL_F_ALLOW_LOCAL; + } else if (!strcmp(*argv, "--use-original-traffic-class")) { + p->flags |= IPV6_TNL_F_USE_ORIG_TCLASS; + } else if (!strcmp(*argv, "remote")) { + argv++; + if (--argc <= 0) + usage(); + resolve_name(*argv, &p->raddr); + } else if (!strcmp(*argv, "local")) { + argv++; + if (--argc <= 0) + usage(); + resolve_name(*argv, &p->laddr); + } else if (!strcmp(*argv, "dev")) { + argv++; + if (--argc <= 0) + usage(); + strncpy(medium, *argv, IFNAMSIZ - 1); + } else if (!strcmp(*argv, "hoplimit")) { + __u8 uval; + argv++; + if (--argc <= 0) + usage(); + if (get_u8(&uval, *argv) < -1) + usage(); + p->hop_limit = uval; + } else if (!strcmp(*argv, "flowlabel")) { + __u32 uval; + argv++; + if (--argc <= 0) + usage(); + if (get_u20(&uval, *argv) < -1) + usage(); + p->flow_lbl = uval; + } else if (!strcmp(*argv, "encaplimit")) { + argv++; + if (--argc <= 0) + usage(); + if (!strcmp(*argv, "none")) { + p->flags |= IPV6_TNL_F_IGN_ENCAP_LIMIT; + } else { + __u8 uval; + if (get_u8(&uval, *argv) < -1) + usage(); + p->encap_limit = uval; + } + } else { + if (p->name[0]) + usage(); + strncpy(p->name, *argv, IFNAMSIZ - 1); + } + argc--; argv++; + } + if (medium[0]) { + p->link = do_ioctl_get_ifindex(medium); + if (p->link == 0) + return -1; + } + return 0; +} + +static int do_show(int argc, char **argv) +{ + struct ipv6_tnl_parm p; + + if (parse_args(argc, argv, &p) < 0) + return -1; + + if (do_get_ioctl(p.name[0] ? p.name : "ip6tnl0", &p)) + return -1; + + print_tunnel(&p); + return 0; +} + + + + +static int do_add(int cmd, int argc, char **argv) +{ + struct ipv6_tnl_parm p; + + if (parse_args(argc, argv, &p) < 0) + return -1; + + return do_add_ioctl(cmd, + cmd == SIOCCHGTUNNEL && p.name[0] ? + p.name : "ip6tnl0", &p); +} + +int do_del(int argc, char **argv) +{ + struct ipv6_tnl_parm p; + + if (parse_args(argc, argv, &p) < 0) + return -1; + + return do_del_ioctl(p.name[0] ? p.name : "ip6tnl0", &p); +} + + +int main(int argc, char **argv) +{ + argc--; + argv++; + if (argc > 0) { + if (strcmp(*argv, "add") == 0) + return do_add(SIOCADDTUNNEL, argc - 1, argv + 1); + else if (strcmp(*argv, "change") == 0) + return do_add(SIOCCHGTUNNEL, argc - 1, argv + 1); + else if (strcmp(*argv, "del") == 0) + return do_del(argc - 1, argv + 1); + else if (strcmp(*argv, "show") == 0) + return do_show(argc - 1, argv + 1); + else + usage(); + + } else + return do_show(0, NULL); + + return 0; +}