source: MondoRescue/branches/3.3/mindi-busybox/networking/libiproute/iptunnel.c@ 3803

Last change on this file since 3803 was 3621, checked in by Bruno Cornec, 10 years ago

New 3?3 banch for incorporation of latest busybox 1.25. Changing minor version to handle potential incompatibilities.

File size: 14.6 KB
Line 
1/* vi: set sw=4 ts=4: */
2/*
3 * Licensed under GPLv2 or later, see file LICENSE in this source tree.
4 *
5 * Authors: Alexey Kuznetsov, <kuznet@ms2.inr.ac.ru>
6 *
7 * Changes:
8 *
9 * Rani Assaf <rani@magic.metawire.com> 980929: resolve addresses
10 * Rani Assaf <rani@magic.metawire.com> 980930: do not allow key for ipip/sit
11 * Phil Karn <karn@ka9q.ampr.org> 990408: "pmtudisc" flag
12 */
13
14#include <netinet/ip.h>
15#include <net/if.h>
16#include <net/if_arp.h>
17#include <asm/types.h>
18
19#ifndef __constant_htons
20#define __constant_htons htons
21#endif
22
23// FYI: #define SIOCDEVPRIVATE 0x89F0
24
25/* From linux/if_tunnel.h. #including it proved troublesome
26 * (redefiniton errors due to name collisions in linux/ and net[inet]/) */
27#define SIOCGETTUNNEL (SIOCDEVPRIVATE + 0)
28#define SIOCADDTUNNEL (SIOCDEVPRIVATE + 1)
29#define SIOCDELTUNNEL (SIOCDEVPRIVATE + 2)
30#define SIOCCHGTUNNEL (SIOCDEVPRIVATE + 3)
31//#define SIOCGETPRL (SIOCDEVPRIVATE + 4)
32//#define SIOCADDPRL (SIOCDEVPRIVATE + 5)
33//#define SIOCDELPRL (SIOCDEVPRIVATE + 6)
34//#define SIOCCHGPRL (SIOCDEVPRIVATE + 7)
35#define GRE_CSUM __constant_htons(0x8000)
36//#define GRE_ROUTING __constant_htons(0x4000)
37#define GRE_KEY __constant_htons(0x2000)
38#define GRE_SEQ __constant_htons(0x1000)
39//#define GRE_STRICT __constant_htons(0x0800)
40//#define GRE_REC __constant_htons(0x0700)
41//#define GRE_FLAGS __constant_htons(0x00F8)
42//#define GRE_VERSION __constant_htons(0x0007)
43struct ip_tunnel_parm {
44 char name[IFNAMSIZ];
45 int link;
46 uint16_t i_flags;
47 uint16_t o_flags;
48 uint32_t i_key;
49 uint32_t o_key;
50 struct iphdr iph;
51};
52/* SIT-mode i_flags */
53//#define SIT_ISATAP 0x0001
54//struct ip_tunnel_prl {
55// uint32_t addr;
56// uint16_t flags;
57// uint16_t __reserved;
58// uint32_t datalen;
59// uint32_t __reserved2;
60// /* data follows */
61//};
62///* PRL flags */
63//#define PRL_DEFAULT 0x0001
64
65#include "ip_common.h" /* #include "libbb.h" is inside */
66#include "rt_names.h"
67#include "utils.h"
68
69
70/* Dies on error */
71static int do_ioctl_get_ifindex(char *dev)
72{
73 struct ifreq ifr;
74 int fd;
75
76 strncpy_IFNAMSIZ(ifr.ifr_name, dev);
77 fd = xsocket(AF_INET, SOCK_DGRAM, 0);
78 xioctl(fd, SIOCGIFINDEX, &ifr);
79 close(fd);
80 return ifr.ifr_ifindex;
81}
82
83static int do_ioctl_get_iftype(char *dev)
84{
85 struct ifreq ifr;
86 int fd;
87 int err;
88
89 strncpy_IFNAMSIZ(ifr.ifr_name, dev);
90 fd = xsocket(AF_INET, SOCK_DGRAM, 0);
91 err = ioctl_or_warn(fd, SIOCGIFHWADDR, &ifr);
92 close(fd);
93 return err ? -1 : ifr.ifr_addr.sa_family;
94}
95
96static char *do_ioctl_get_ifname(int idx)
97{
98 struct ifreq ifr;
99 int fd;
100 int err;
101
102 ifr.ifr_ifindex = idx;
103 fd = xsocket(AF_INET, SOCK_DGRAM, 0);
104 err = ioctl_or_warn(fd, SIOCGIFNAME, &ifr);
105 close(fd);
106 return err ? NULL : xstrndup(ifr.ifr_name, sizeof(ifr.ifr_name));
107}
108
109static int do_get_ioctl(const char *basedev, struct ip_tunnel_parm *p)
110{
111 struct ifreq ifr;
112 int fd;
113 int err;
114
115 strncpy_IFNAMSIZ(ifr.ifr_name, basedev);
116 ifr.ifr_ifru.ifru_data = (void*)p;
117 fd = xsocket(AF_INET, SOCK_DGRAM, 0);
118 err = ioctl_or_warn(fd, SIOCGETTUNNEL, &ifr);
119 close(fd);
120 return err;
121}
122
123/* Dies on error, otherwise returns 0 */
124static int do_add_ioctl(int cmd, const char *basedev, struct ip_tunnel_parm *p)
125{
126 struct ifreq ifr;
127 int fd;
128
129 if (cmd == SIOCCHGTUNNEL && p->name[0]) {
130 strncpy_IFNAMSIZ(ifr.ifr_name, p->name);
131 } else {
132 strncpy_IFNAMSIZ(ifr.ifr_name, basedev);
133 }
134 ifr.ifr_ifru.ifru_data = (void*)p;
135 fd = xsocket(AF_INET, SOCK_DGRAM, 0);
136#if ENABLE_IOCTL_HEX2STR_ERROR
137 /* #define magic will turn ioctl# into string */
138 if (cmd == SIOCCHGTUNNEL)
139 xioctl(fd, SIOCCHGTUNNEL, &ifr);
140 else
141 xioctl(fd, SIOCADDTUNNEL, &ifr);
142#else
143 xioctl(fd, cmd, &ifr);
144#endif
145 close(fd);
146 return 0;
147}
148
149/* Dies on error, otherwise returns 0 */
150static int do_del_ioctl(const char *basedev, struct ip_tunnel_parm *p)
151{
152 struct ifreq ifr;
153 int fd;
154
155 if (p->name[0]) {
156 strncpy_IFNAMSIZ(ifr.ifr_name, p->name);
157 } else {
158 strncpy_IFNAMSIZ(ifr.ifr_name, basedev);
159 }
160 ifr.ifr_ifru.ifru_data = (void*)p;
161 fd = xsocket(AF_INET, SOCK_DGRAM, 0);
162 xioctl(fd, SIOCDELTUNNEL, &ifr);
163 close(fd);
164 return 0;
165}
166
167/* Dies on error */
168static void parse_args(char **argv, int cmd, struct ip_tunnel_parm *p)
169{
170 static const char keywords[] ALIGN1 =
171 "mode\0""ipip\0""ip/ip\0""gre\0""gre/ip\0""sit\0""ipv6/ip\0"
172 "key\0""ikey\0""okey\0""seq\0""iseq\0""oseq\0"
173 "csum\0""icsum\0""ocsum\0""nopmtudisc\0""pmtudisc\0"
174 "remote\0""any\0""local\0""dev\0"
175 "ttl\0""inherit\0""tos\0""dsfield\0"
176 "name\0";
177 enum {
178 ARG_mode, ARG_ipip, ARG_ip_ip, ARG_gre, ARG_gre_ip, ARG_sit, ARG_ip6_ip,
179 ARG_key, ARG_ikey, ARG_okey, ARG_seq, ARG_iseq, ARG_oseq,
180 ARG_csum, ARG_icsum, ARG_ocsum, ARG_nopmtudisc, ARG_pmtudisc,
181 ARG_remote, ARG_any, ARG_local, ARG_dev,
182 ARG_ttl, ARG_inherit, ARG_tos, ARG_dsfield,
183 ARG_name
184 };
185 int count = 0;
186 char medium[IFNAMSIZ];
187 int key;
188
189 memset(p, 0, sizeof(*p));
190 medium[0] = '\0';
191
192 p->iph.version = 4;
193 p->iph.ihl = 5;
194#ifndef IP_DF
195#define IP_DF 0x4000 /* Flag: "Don't Fragment" */
196#endif
197 p->iph.frag_off = htons(IP_DF);
198
199 while (*argv) {
200 key = index_in_strings(keywords, *argv);
201 if (key == ARG_mode) {
202 NEXT_ARG();
203 key = index_in_strings(keywords, *argv);
204 if (key == ARG_ipip ||
205 key == ARG_ip_ip
206 ) {
207 if (p->iph.protocol && p->iph.protocol != IPPROTO_IPIP) {
208 bb_error_msg_and_die("%s tunnel mode", "you managed to ask for more than one");
209 }
210 p->iph.protocol = IPPROTO_IPIP;
211 } else if (key == ARG_gre ||
212 key == ARG_gre_ip
213 ) {
214 if (p->iph.protocol && p->iph.protocol != IPPROTO_GRE) {
215 bb_error_msg_and_die("%s tunnel mode", "you managed to ask for more than one");
216 }
217 p->iph.protocol = IPPROTO_GRE;
218 } else if (key == ARG_sit ||
219 key == ARG_ip6_ip
220 ) {
221 if (p->iph.protocol && p->iph.protocol != IPPROTO_IPV6) {
222 bb_error_msg_and_die("%s tunnel mode", "you managed to ask for more than one");
223 }
224 p->iph.protocol = IPPROTO_IPV6;
225 } else {
226 bb_error_msg_and_die("%s tunnel mode", "can't guess");
227 }
228 } else if (key == ARG_key) {
229 unsigned uval;
230 NEXT_ARG();
231 p->i_flags |= GRE_KEY;
232 p->o_flags |= GRE_KEY;
233 if (strchr(*argv, '.'))
234 p->i_key = p->o_key = get_addr32(*argv);
235 else {
236 uval = get_unsigned(*argv, "key");
237 p->i_key = p->o_key = htonl(uval);
238 }
239 } else if (key == ARG_ikey) {
240 unsigned uval;
241 NEXT_ARG();
242 p->i_flags |= GRE_KEY;
243 if (strchr(*argv, '.'))
244 p->o_key = get_addr32(*argv);
245 else {
246 uval = get_unsigned(*argv, "ikey");
247 p->i_key = htonl(uval);
248 }
249 } else if (key == ARG_okey) {
250 unsigned uval;
251 NEXT_ARG();
252 p->o_flags |= GRE_KEY;
253 if (strchr(*argv, '.'))
254 p->o_key = get_addr32(*argv);
255 else {
256 uval = get_unsigned(*argv, "okey");
257 p->o_key = htonl(uval);
258 }
259 } else if (key == ARG_seq) {
260 p->i_flags |= GRE_SEQ;
261 p->o_flags |= GRE_SEQ;
262 } else if (key == ARG_iseq) {
263 p->i_flags |= GRE_SEQ;
264 } else if (key == ARG_oseq) {
265 p->o_flags |= GRE_SEQ;
266 } else if (key == ARG_csum) {
267 p->i_flags |= GRE_CSUM;
268 p->o_flags |= GRE_CSUM;
269 } else if (key == ARG_icsum) {
270 p->i_flags |= GRE_CSUM;
271 } else if (key == ARG_ocsum) {
272 p->o_flags |= GRE_CSUM;
273 } else if (key == ARG_nopmtudisc) {
274 p->iph.frag_off = 0;
275 } else if (key == ARG_pmtudisc) {
276 p->iph.frag_off = htons(IP_DF);
277 } else if (key == ARG_remote) {
278 NEXT_ARG();
279 key = index_in_strings(keywords, *argv);
280 if (key != ARG_any)
281 p->iph.daddr = get_addr32(*argv);
282 } else if (key == ARG_local) {
283 NEXT_ARG();
284 key = index_in_strings(keywords, *argv);
285 if (key != ARG_any)
286 p->iph.saddr = get_addr32(*argv);
287 } else if (key == ARG_dev) {
288 NEXT_ARG();
289 strncpy_IFNAMSIZ(medium, *argv);
290 } else if (key == ARG_ttl) {
291 unsigned uval;
292 NEXT_ARG();
293 key = index_in_strings(keywords, *argv);
294 if (key != ARG_inherit) {
295 uval = get_unsigned(*argv, "TTL");
296 if (uval > 255)
297 invarg_1_to_2(*argv, "TTL");
298 p->iph.ttl = uval;
299 }
300 } else if (key == ARG_tos ||
301 key == ARG_dsfield
302 ) {
303 uint32_t uval;
304 NEXT_ARG();
305 key = index_in_strings(keywords, *argv);
306 if (key != ARG_inherit) {
307 if (rtnl_dsfield_a2n(&uval, *argv))
308 invarg_1_to_2(*argv, "TOS");
309 p->iph.tos = uval;
310 } else
311 p->iph.tos = 1;
312 } else {
313 if (key == ARG_name) {
314 NEXT_ARG();
315 }
316 if (p->name[0])
317 duparg2("name", *argv);
318 strncpy_IFNAMSIZ(p->name, *argv);
319 if (cmd == SIOCCHGTUNNEL && count == 0) {
320 struct ip_tunnel_parm old_p;
321 memset(&old_p, 0, sizeof(old_p));
322 if (do_get_ioctl(*argv, &old_p))
323 exit(EXIT_FAILURE);
324 *p = old_p;
325 }
326 }
327 count++;
328 argv++;
329 }
330
331 if (p->iph.protocol == 0) {
332 if (memcmp(p->name, "gre", 3) == 0)
333 p->iph.protocol = IPPROTO_GRE;
334 else if (memcmp(p->name, "ipip", 4) == 0)
335 p->iph.protocol = IPPROTO_IPIP;
336 else if (memcmp(p->name, "sit", 3) == 0)
337 p->iph.protocol = IPPROTO_IPV6;
338 }
339
340 if (p->iph.protocol == IPPROTO_IPIP || p->iph.protocol == IPPROTO_IPV6) {
341 if ((p->i_flags & GRE_KEY) || (p->o_flags & GRE_KEY)) {
342 bb_error_msg_and_die("keys are not allowed with ipip and sit");
343 }
344 }
345
346 if (medium[0]) {
347 p->link = do_ioctl_get_ifindex(medium);
348 }
349
350 if (p->i_key == 0 && IN_MULTICAST(ntohl(p->iph.daddr))) {
351 p->i_key = p->iph.daddr;
352 p->i_flags |= GRE_KEY;
353 }
354 if (p->o_key == 0 && IN_MULTICAST(ntohl(p->iph.daddr))) {
355 p->o_key = p->iph.daddr;
356 p->o_flags |= GRE_KEY;
357 }
358 if (IN_MULTICAST(ntohl(p->iph.daddr)) && !p->iph.saddr) {
359 bb_error_msg_and_die("broadcast tunnel requires a source address");
360 }
361}
362
363/* Return value becomes exitcode. It's okay to not return at all */
364static int do_add(int cmd, char **argv)
365{
366 struct ip_tunnel_parm p;
367
368 parse_args(argv, cmd, &p);
369
370 if (p.iph.ttl && p.iph.frag_off == 0) {
371 bb_error_msg_and_die("ttl != 0 and noptmudisc are incompatible");
372 }
373
374 switch (p.iph.protocol) {
375 case IPPROTO_IPIP:
376 return do_add_ioctl(cmd, "tunl0", &p);
377 case IPPROTO_GRE:
378 return do_add_ioctl(cmd, "gre0", &p);
379 case IPPROTO_IPV6:
380 return do_add_ioctl(cmd, "sit0", &p);
381 default:
382 bb_error_msg_and_die("can't determine tunnel mode (ipip, gre or sit)");
383 }
384}
385
386/* Return value becomes exitcode. It's okay to not return at all */
387static int do_del(char **argv)
388{
389 struct ip_tunnel_parm p;
390
391 parse_args(argv, SIOCDELTUNNEL, &p);
392
393 switch (p.iph.protocol) {
394 case IPPROTO_IPIP:
395 return do_del_ioctl("tunl0", &p);
396 case IPPROTO_GRE:
397 return do_del_ioctl("gre0", &p);
398 case IPPROTO_IPV6:
399 return do_del_ioctl("sit0", &p);
400 default:
401 return do_del_ioctl(p.name, &p);
402 }
403}
404
405static void print_tunnel(struct ip_tunnel_parm *p)
406{
407 char s3[INET_ADDRSTRLEN];
408 char s4[INET_ADDRSTRLEN];
409
410 printf("%s: %s/ip remote %s local %s ",
411 p->name,
412 p->iph.protocol == IPPROTO_IPIP ? "ip" :
413 p->iph.protocol == IPPROTO_GRE ? "gre" :
414 p->iph.protocol == IPPROTO_IPV6 ? "ipv6" :
415 "unknown",
416 p->iph.daddr ? format_host(AF_INET, 4, &p->iph.daddr) : "any",
417 p->iph.saddr ? format_host(AF_INET, 4, &p->iph.saddr) : "any"
418 );
419 if (p->link) {
420 char *n = do_ioctl_get_ifname(p->link);
421 if (n) {
422 printf(" dev %s ", n);
423 free(n);
424 }
425 }
426 if (p->iph.ttl)
427 printf(" ttl %d ", p->iph.ttl);
428 else
429 printf(" ttl inherit ");
430 if (p->iph.tos) {
431 printf(" tos");
432 if (p->iph.tos & 1)
433 printf(" inherit");
434 if (p->iph.tos & ~1)
435 printf("%c%s ", p->iph.tos & 1 ? '/' : ' ',
436 rtnl_dsfield_n2a(p->iph.tos & ~1));
437 }
438 if (!(p->iph.frag_off & htons(IP_DF)))
439 printf(" nopmtudisc");
440
441 inet_ntop(AF_INET, &p->i_key, s3, sizeof(s3));
442 inet_ntop(AF_INET, &p->o_key, s4, sizeof(s4));
443 if ((p->i_flags & GRE_KEY) && (p->o_flags & GRE_KEY) && p->o_key == p->i_key)
444 printf(" key %s", s3);
445 else {
446 if (p->i_flags & GRE_KEY)
447 printf(" ikey %s ", s3);
448 if (p->o_flags & GRE_KEY)
449 printf(" okey %s ", s4);
450 }
451
452 if (p->i_flags & GRE_SEQ)
453 printf("%c Drop packets out of sequence.\n", _SL_);
454 if (p->i_flags & GRE_CSUM)
455 printf("%c Checksum in received packet is required.", _SL_);
456 if (p->o_flags & GRE_SEQ)
457 printf("%c Sequence packets on output.", _SL_);
458 if (p->o_flags & GRE_CSUM)
459 printf("%c Checksum output packets.", _SL_);
460}
461
462static void do_tunnels_list(struct ip_tunnel_parm *p)
463{
464 char name[IFNAMSIZ];
465 unsigned long rx_bytes, rx_packets, rx_errs, rx_drops,
466 rx_fifo, rx_frame,
467 tx_bytes, tx_packets, tx_errs, tx_drops,
468 tx_fifo, tx_colls, tx_carrier, rx_multi;
469 int type;
470 struct ip_tunnel_parm p1;
471 char buf[512];
472 FILE *fp = fopen_or_warn("/proc/net/dev", "r");
473
474 if (fp == NULL) {
475 return;
476 }
477 /* skip headers */
478 fgets(buf, sizeof(buf), fp);
479 fgets(buf, sizeof(buf), fp);
480
481 while (fgets(buf, sizeof(buf), fp) != NULL) {
482 char *ptr;
483
484 /*buf[sizeof(buf) - 1] = 0; - fgets is safe anyway */
485 ptr = strchr(buf, ':');
486 if (ptr == NULL ||
487 (*ptr++ = 0, sscanf(buf, "%s", name) != 1)
488 ) {
489 bb_error_msg("wrong format of /proc/net/dev");
490 return;
491 }
492 if (sscanf(ptr, "%lu%lu%lu%lu%lu%lu%lu%*d%lu%lu%lu%lu%lu%lu%lu",
493 &rx_bytes, &rx_packets, &rx_errs, &rx_drops,
494 &rx_fifo, &rx_frame, &rx_multi,
495 &tx_bytes, &tx_packets, &tx_errs, &tx_drops,
496 &tx_fifo, &tx_colls, &tx_carrier) != 14)
497 continue;
498 if (p->name[0] && strcmp(p->name, name))
499 continue;
500 type = do_ioctl_get_iftype(name);
501 if (type == -1) {
502 bb_error_msg("can't get type of [%s]", name);
503 continue;
504 }
505 if (type != ARPHRD_TUNNEL && type != ARPHRD_IPGRE && type != ARPHRD_SIT)
506 continue;
507 memset(&p1, 0, sizeof(p1));
508 if (do_get_ioctl(name, &p1))
509 continue;
510 if ((p->link && p1.link != p->link) ||
511 (p->name[0] && strcmp(p1.name, p->name)) ||
512 (p->iph.daddr && p1.iph.daddr != p->iph.daddr) ||
513 (p->iph.saddr && p1.iph.saddr != p->iph.saddr) ||
514 (p->i_key && p1.i_key != p->i_key)
515 ) {
516 continue;
517 }
518 print_tunnel(&p1);
519 bb_putchar('\n');
520 }
521}
522
523/* Return value becomes exitcode. It's okay to not return at all */
524static int do_show(char **argv)
525{
526 int err;
527 struct ip_tunnel_parm p;
528
529 parse_args(argv, SIOCGETTUNNEL, &p);
530
531 switch (p.iph.protocol) {
532 case IPPROTO_IPIP:
533 err = do_get_ioctl(p.name[0] ? p.name : "tunl0", &p);
534 break;
535 case IPPROTO_GRE:
536 err = do_get_ioctl(p.name[0] ? p.name : "gre0", &p);
537 break;
538 case IPPROTO_IPV6:
539 err = do_get_ioctl(p.name[0] ? p.name : "sit0", &p);
540 break;
541 default:
542 do_tunnels_list(&p);
543 return 0;
544 }
545 if (err)
546 return -1;
547
548 print_tunnel(&p);
549 bb_putchar('\n');
550 return 0;
551}
552
553/* Return value becomes exitcode. It's okay to not return at all */
554int FAST_FUNC do_iptunnel(char **argv)
555{
556 static const char keywords[] ALIGN1 =
557 "add\0""change\0""delete\0""show\0""list\0""lst\0";
558 enum { ARG_add = 0, ARG_change, ARG_del, ARG_show, ARG_list, ARG_lst };
559
560 if (*argv) {
561 int key = index_in_substrings(keywords, *argv);
562 if (key < 0)
563 invarg_1_to_2(*argv, applet_name);
564 argv++;
565 if (key == ARG_add)
566 return do_add(SIOCADDTUNNEL, argv);
567 if (key == ARG_change)
568 return do_add(SIOCCHGTUNNEL, argv);
569 if (key == ARG_del)
570 return do_del(argv);
571 }
572 return do_show(argv);
573}
Note: See TracBrowser for help on using the repository browser.