blob: c8578e5c8f6ff4d290f7669ecf80b3da8d2bc19b [file] [log] [blame]
Masahide NAKAMURA9447a0d2006-11-24 12:27:06 +09001/*
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
49int tunnel_local_packets = 0;
50
51static 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
70static 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
89static 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
108static 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
126static 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
145static 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
163void 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
221void 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
237int 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
251int 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
265static 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
342static 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
359static 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
371int 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
382int 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}