]> git.proxmox.com Git - mirror_iproute2.git/blob - ip/iptunnel.c
don't need this either.
[mirror_iproute2.git] / ip / iptunnel.c
1 /*
2 * iptunnel.c "ip tunnel"
3 *
4 * This program is free software; you can redistribute it and/or
5 * modify it under the terms of the GNU General Public License
6 * as published by the Free Software Foundation; either version
7 * 2 of the License, or (at your option) any later version.
8 *
9 * Authors: Alexey Kuznetsov, <kuznet@ms2.inr.ac.ru>
10 *
11 *
12 * Changes:
13 *
14 * Rani Assaf <rani@magic.metawire.com> 980929: resolve addresses
15 * Rani Assaf <rani@magic.metawire.com> 980930: do not allow key for ipip/sit
16 * Phil Karn <karn@ka9q.ampr.org> 990408: "pmtudisc" flag
17 */
18
19 #include <stdio.h>
20 #include <stdlib.h>
21 #include <string.h>
22 #include <unistd.h>
23 #include <syslog.h>
24 #include <fcntl.h>
25 #include <sys/socket.h>
26 #include <netinet/in.h>
27 #include <arpa/inet.h>
28 #include <sys/ioctl.h>
29 #include <linux/if.h>
30 #include <linux/if_arp.h>
31 #include <linux/ip.h>
32 #include <linux/if_tunnel.h>
33
34 #include "rt_names.h"
35 #include "utils.h"
36
37 static void usage(void) __attribute__((noreturn));
38
39 static void usage(void)
40 {
41 fprintf(stderr, "Usage: ip tunnel { add | change | del | show } [ NAME ]\n");
42 fprintf(stderr, " [ mode { ipip | gre | sit } ] [ remote ADDR ] [ local ADDR ]\n");
43 fprintf(stderr, " [ [i|o]seq ] [ [i|o]key KEY ] [ [i|o]csum ]\n");
44 fprintf(stderr, " [ ttl TTL ] [ tos TOS ] [ [no]pmtudisc ] [ dev PHYS_DEV ]\n");
45 fprintf(stderr, "\n");
46 fprintf(stderr, "Where: NAME := STRING\n");
47 fprintf(stderr, " ADDR := { IP_ADDRESS | any }\n");
48 fprintf(stderr, " TOS := { NUMBER | inherit }\n");
49 fprintf(stderr, " TTL := { 1..255 | inherit }\n");
50 fprintf(stderr, " KEY := { DOTTED_QUAD | NUMBER }\n");
51 exit(-1);
52 }
53
54 static int do_ioctl_get_ifindex(const char *dev)
55 {
56 struct ifreq ifr;
57 int fd;
58 int err;
59
60 strncpy(ifr.ifr_name, dev, IFNAMSIZ);
61 fd = socket(AF_INET, SOCK_DGRAM, 0);
62 err = ioctl(fd, SIOCGIFINDEX, &ifr);
63 if (err) {
64 perror("ioctl");
65 return 0;
66 }
67 close(fd);
68 return ifr.ifr_ifindex;
69 }
70
71 static int do_ioctl_get_iftype(const char *dev)
72 {
73 struct ifreq ifr;
74 int fd;
75 int err;
76
77 strncpy(ifr.ifr_name, dev, IFNAMSIZ);
78 fd = socket(AF_INET, SOCK_DGRAM, 0);
79 err = ioctl(fd, SIOCGIFHWADDR, &ifr);
80 if (err) {
81 perror("ioctl");
82 return -1;
83 }
84 close(fd);
85 return ifr.ifr_addr.sa_family;
86 }
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 ifr.ifr_ifindex = idx;
96 fd = socket(AF_INET, SOCK_DGRAM, 0);
97 err = ioctl(fd, SIOCGIFNAME, &ifr);
98 if (err) {
99 perror("ioctl");
100 return NULL;
101 }
102 close(fd);
103 return ifr.ifr_name;
104 }
105
106
107 static int do_get_ioctl(const char *basedev, struct ip_tunnel_parm *p)
108 {
109 struct ifreq ifr;
110 int fd;
111 int err;
112
113 strncpy(ifr.ifr_name, basedev, IFNAMSIZ);
114 ifr.ifr_ifru.ifru_data = (void*)p;
115 fd = socket(AF_INET, SOCK_DGRAM, 0);
116 err = ioctl(fd, SIOCGETTUNNEL, &ifr);
117 if (err)
118 perror("ioctl");
119 close(fd);
120 return err;
121 }
122
123 static int do_add_ioctl(int cmd, const char *basedev, struct ip_tunnel_parm *p)
124 {
125 struct ifreq ifr;
126 int fd;
127 int err;
128
129 if (cmd == SIOCCHGTUNNEL && p->name[0])
130 strncpy(ifr.ifr_name, p->name, IFNAMSIZ);
131 else
132 strncpy(ifr.ifr_name, basedev, IFNAMSIZ);
133 ifr.ifr_ifru.ifru_data = (void*)p;
134 fd = socket(AF_INET, SOCK_DGRAM, 0);
135 err = ioctl(fd, cmd, &ifr);
136 if (err)
137 perror("ioctl");
138 close(fd);
139 return err;
140 }
141
142 static int do_del_ioctl(const char *basedev, struct ip_tunnel_parm *p)
143 {
144 struct ifreq ifr;
145 int fd;
146 int err;
147
148 if (p->name[0])
149 strncpy(ifr.ifr_name, p->name, IFNAMSIZ);
150 else
151 strncpy(ifr.ifr_name, basedev, IFNAMSIZ);
152 ifr.ifr_ifru.ifru_data = (void*)p;
153 fd = socket(AF_INET, SOCK_DGRAM, 0);
154 err = ioctl(fd, SIOCDELTUNNEL, &ifr);
155 if (err)
156 perror("ioctl");
157 close(fd);
158 return err;
159 }
160
161 static int parse_args(int argc, char **argv, int cmd, struct ip_tunnel_parm *p)
162 {
163 int count = 0;
164 char medium[IFNAMSIZ];
165
166 memset(p, 0, sizeof(*p));
167 memset(&medium, 0, sizeof(medium));
168
169 p->iph.version = 4;
170 p->iph.ihl = 5;
171 #ifndef IP_DF
172 #define IP_DF 0x4000 /* Flag: "Don't Fragment" */
173 #endif
174 p->iph.frag_off = htons(IP_DF);
175
176 while (argc > 0) {
177 if (strcmp(*argv, "mode") == 0) {
178 NEXT_ARG();
179 if (strcmp(*argv, "ipip") == 0 ||
180 strcmp(*argv, "ip/ip") == 0) {
181 if (p->iph.protocol && p->iph.protocol != IPPROTO_IPIP) {
182 fprintf(stderr,"You managed to ask for more than one tunnel mode.\n");
183 exit(-1);
184 }
185 p->iph.protocol = IPPROTO_IPIP;
186 } else if (strcmp(*argv, "gre") == 0 ||
187 strcmp(*argv, "gre/ip") == 0) {
188 if (p->iph.protocol && p->iph.protocol != IPPROTO_GRE) {
189 fprintf(stderr,"You managed to ask for more than one tunnel mode.\n");
190 exit(-1);
191 }
192 p->iph.protocol = IPPROTO_GRE;
193 } else if (strcmp(*argv, "sit") == 0 ||
194 strcmp(*argv, "ipv6/ip") == 0) {
195 if (p->iph.protocol && p->iph.protocol != IPPROTO_IPV6) {
196 fprintf(stderr,"You managed to ask for more than one tunnel mode.\n");
197 exit(-1);
198 }
199 p->iph.protocol = IPPROTO_IPV6;
200 } else {
201 fprintf(stderr,"Cannot guess tunnel mode.\n");
202 exit(-1);
203 }
204 } else if (strcmp(*argv, "key") == 0) {
205 unsigned uval;
206 NEXT_ARG();
207 p->i_flags |= GRE_KEY;
208 p->o_flags |= GRE_KEY;
209 if (strchr(*argv, '.'))
210 p->i_key = p->o_key = get_addr32(*argv);
211 else {
212 if (get_unsigned(&uval, *argv, 0)<0) {
213 fprintf(stderr, "invalid value of \"key\"\n");
214 exit(-1);
215 }
216 p->i_key = p->o_key = htonl(uval);
217 }
218 } else if (strcmp(*argv, "ikey") == 0) {
219 unsigned uval;
220 NEXT_ARG();
221 p->i_flags |= GRE_KEY;
222 if (strchr(*argv, '.'))
223 p->o_key = get_addr32(*argv);
224 else {
225 if (get_unsigned(&uval, *argv, 0)<0) {
226 fprintf(stderr, "invalid value of \"ikey\"\n");
227 exit(-1);
228 }
229 p->i_key = htonl(uval);
230 }
231 } else if (strcmp(*argv, "okey") == 0) {
232 unsigned uval;
233 NEXT_ARG();
234 p->o_flags |= GRE_KEY;
235 if (strchr(*argv, '.'))
236 p->o_key = get_addr32(*argv);
237 else {
238 if (get_unsigned(&uval, *argv, 0)<0) {
239 fprintf(stderr, "invalid value of \"okey\"\n");
240 exit(-1);
241 }
242 p->o_key = htonl(uval);
243 }
244 } else if (strcmp(*argv, "seq") == 0) {
245 p->i_flags |= GRE_SEQ;
246 p->o_flags |= GRE_SEQ;
247 } else if (strcmp(*argv, "iseq") == 0) {
248 p->i_flags |= GRE_SEQ;
249 } else if (strcmp(*argv, "oseq") == 0) {
250 p->o_flags |= GRE_SEQ;
251 } else if (strcmp(*argv, "csum") == 0) {
252 p->i_flags |= GRE_CSUM;
253 p->o_flags |= GRE_CSUM;
254 } else if (strcmp(*argv, "icsum") == 0) {
255 p->i_flags |= GRE_CSUM;
256 } else if (strcmp(*argv, "ocsum") == 0) {
257 p->o_flags |= GRE_CSUM;
258 } else if (strcmp(*argv, "nopmtudisc") == 0) {
259 p->iph.frag_off = 0;
260 } else if (strcmp(*argv, "pmtudisc") == 0) {
261 p->iph.frag_off = htons(IP_DF);
262 } else if (strcmp(*argv, "remote") == 0) {
263 NEXT_ARG();
264 if (strcmp(*argv, "any"))
265 p->iph.daddr = get_addr32(*argv);
266 } else if (strcmp(*argv, "local") == 0) {
267 NEXT_ARG();
268 if (strcmp(*argv, "any"))
269 p->iph.saddr = get_addr32(*argv);
270 } else if (strcmp(*argv, "dev") == 0) {
271 NEXT_ARG();
272 strncpy(medium, *argv, IFNAMSIZ-1);
273 } else if (strcmp(*argv, "ttl") == 0) {
274 unsigned uval;
275 NEXT_ARG();
276 if (strcmp(*argv, "inherit") != 0) {
277 if (get_unsigned(&uval, *argv, 0))
278 invarg("invalid TTL\n", *argv);
279 if (uval > 255)
280 invarg("TTL must be <=255\n", *argv);
281 p->iph.ttl = uval;
282 }
283 } else if (strcmp(*argv, "tos") == 0 ||
284 matches(*argv, "dsfield") == 0) {
285 __u32 uval;
286 NEXT_ARG();
287 if (strcmp(*argv, "inherit") != 0) {
288 if (rtnl_dsfield_a2n(&uval, *argv))
289 invarg("bad TOS value", *argv);
290 p->iph.tos = uval;
291 } else
292 p->iph.tos = 1;
293 } else {
294 if (strcmp(*argv, "name") == 0) {
295 NEXT_ARG();
296 }
297 if (matches(*argv, "help") == 0)
298 usage();
299 if (p->name[0])
300 duparg2("name", *argv);
301 strncpy(p->name, *argv, IFNAMSIZ);
302 if (cmd == SIOCCHGTUNNEL && count == 0) {
303 struct ip_tunnel_parm old_p;
304 memset(&old_p, 0, sizeof(old_p));
305 if (do_get_ioctl(*argv, &old_p))
306 return -1;
307 *p = old_p;
308 }
309 }
310 count++;
311 argc--; argv++;
312 }
313
314
315 if (p->iph.protocol == 0) {
316 if (memcmp(p->name, "gre", 3) == 0)
317 p->iph.protocol = IPPROTO_GRE;
318 else if (memcmp(p->name, "ipip", 4) == 0)
319 p->iph.protocol = IPPROTO_IPIP;
320 else if (memcmp(p->name, "sit", 3) == 0)
321 p->iph.protocol = IPPROTO_IPV6;
322 }
323
324 if (p->iph.protocol == IPPROTO_IPIP || p->iph.protocol == IPPROTO_IPV6) {
325 if ((p->i_flags & GRE_KEY) || (p->o_flags & GRE_KEY)) {
326 fprintf(stderr, "Keys are not allowed with ipip and sit.\n");
327 return -1;
328 }
329 }
330
331 if (medium[0]) {
332 p->link = do_ioctl_get_ifindex(medium);
333 if (p->link == 0)
334 return -1;
335 }
336
337 if (p->i_key == 0 && IN_MULTICAST(ntohl(p->iph.daddr))) {
338 p->i_key = p->iph.daddr;
339 p->i_flags |= GRE_KEY;
340 }
341 if (p->o_key == 0 && IN_MULTICAST(ntohl(p->iph.daddr))) {
342 p->o_key = p->iph.daddr;
343 p->o_flags |= GRE_KEY;
344 }
345 if (IN_MULTICAST(ntohl(p->iph.daddr)) && !p->iph.saddr) {
346 fprintf(stderr, "Broadcast tunnel requires a source address.\n");
347 return -1;
348 }
349 return 0;
350 }
351
352
353 static int do_add(int cmd, int argc, char **argv)
354 {
355 struct ip_tunnel_parm p;
356
357 if (parse_args(argc, argv, cmd, &p) < 0)
358 return -1;
359
360 if (p.iph.ttl && p.iph.frag_off == 0) {
361 fprintf(stderr, "ttl != 0 and noptmudisc are incompatible\n");
362 return -1;
363 }
364
365 switch (p.iph.protocol) {
366 case IPPROTO_IPIP:
367 return do_add_ioctl(cmd, "tunl0", &p);
368 case IPPROTO_GRE:
369 return do_add_ioctl(cmd, "gre0", &p);
370 case IPPROTO_IPV6:
371 return do_add_ioctl(cmd, "sit0", &p);
372 default:
373 fprintf(stderr, "cannot determine tunnel mode (ipip, gre or sit)\n");
374 return -1;
375 }
376 return -1;
377 }
378
379 int do_del(int argc, char **argv)
380 {
381 struct ip_tunnel_parm p;
382
383 if (parse_args(argc, argv, SIOCDELTUNNEL, &p) < 0)
384 return -1;
385
386 switch (p.iph.protocol) {
387 case IPPROTO_IPIP:
388 return do_del_ioctl("tunl0", &p);
389 case IPPROTO_GRE:
390 return do_del_ioctl("gre0", &p);
391 case IPPROTO_IPV6:
392 return do_del_ioctl("sit0", &p);
393 default:
394 return do_del_ioctl(p.name, &p);
395 }
396 return -1;
397 }
398
399 void print_tunnel(struct ip_tunnel_parm *p)
400 {
401 char s1[1024];
402 char s2[1024];
403 char s3[64];
404 char s4[64];
405
406 inet_ntop(AF_INET, &p->i_key, s3, sizeof(s3));
407 inet_ntop(AF_INET, &p->o_key, s4, sizeof(s4));
408
409 /* Do not use format_host() for local addr,
410 * symbolic name will not be useful.
411 */
412 printf("%s: %s/ip remote %s local %s ",
413 p->name,
414 p->iph.protocol == IPPROTO_IPIP ? "ip" :
415 (p->iph.protocol == IPPROTO_GRE ? "gre" :
416 (p->iph.protocol == IPPROTO_IPV6 ? "ipv6" : "unknown")),
417 p->iph.daddr ? format_host(AF_INET, 4, &p->iph.daddr, s1, sizeof(s1)) : "any",
418 p->iph.saddr ? rt_addr_n2a(AF_INET, 4, &p->iph.saddr, s2, sizeof(s2)) : "any");
419
420 if (p->link) {
421 char *n = do_ioctl_get_ifname(p->link);
422 if (n)
423 printf(" dev %s ", n);
424 }
425
426 if (p->iph.ttl)
427 printf(" ttl %d ", p->iph.ttl);
428 else
429 printf(" ttl inherit ");
430
431 if (p->iph.tos) {
432 SPRINT_BUF(b1);
433 printf(" tos");
434 if (p->iph.tos&1)
435 printf(" inherit");
436 if (p->iph.tos&~1)
437 printf("%c%s ", p->iph.tos&1 ? '/' : ' ',
438 rtnl_dsfield_n2a(p->iph.tos&~1, b1, sizeof(b1)));
439 }
440
441 if (!(p->iph.frag_off&htons(IP_DF)))
442 printf(" nopmtudisc");
443
444 if ((p->i_flags&GRE_KEY) && (p->o_flags&GRE_KEY) && p->o_key == p->i_key)
445 printf(" key %s", s3);
446 else if ((p->i_flags|p->o_flags)&GRE_KEY) {
447 if (p->i_flags&GRE_KEY)
448 printf(" ikey %s ", s3);
449 if (p->o_flags&GRE_KEY)
450 printf(" okey %s ", s4);
451 }
452
453 if (p->i_flags&GRE_SEQ)
454 printf("%s Drop packets out of sequence.\n", _SL_);
455 if (p->i_flags&GRE_CSUM)
456 printf("%s Checksum in received packet is required.", _SL_);
457 if (p->o_flags&GRE_SEQ)
458 printf("%s Sequence packets on output.", _SL_);
459 if (p->o_flags&GRE_CSUM)
460 printf("%s Checksum output packets.", _SL_);
461 }
462
463 static int do_tunnels_list(struct ip_tunnel_parm *p)
464 {
465 char name[IFNAMSIZ];
466 unsigned long rx_bytes, rx_packets, rx_errs, rx_drops,
467 rx_fifo, rx_frame,
468 tx_bytes, tx_packets, tx_errs, tx_drops,
469 tx_fifo, tx_colls, tx_carrier, rx_multi;
470 int type;
471 struct ip_tunnel_parm p1;
472
473 char buf[512];
474 FILE *fp = fopen("/proc/net/dev", "r");
475 if (fp == NULL) {
476 perror("fopen");
477 return -1;
478 }
479
480 fgets(buf, sizeof(buf), fp);
481 fgets(buf, sizeof(buf), fp);
482
483 while (fgets(buf, sizeof(buf), fp) != NULL) {
484 char *ptr;
485 buf[sizeof(buf) - 1] = 0;
486 if ((ptr = strchr(buf, ':')) == NULL ||
487 (*ptr++ = 0, sscanf(buf, "%s", name) != 1)) {
488 fprintf(stderr, "Wrong format of /proc/net/dev. Sorry.\n");
489 return -1;
490 }
491 if (sscanf(ptr, "%ld%ld%ld%ld%ld%ld%ld%*d%ld%ld%ld%ld%ld%ld%ld",
492 &rx_bytes, &rx_packets, &rx_errs, &rx_drops,
493 &rx_fifo, &rx_frame, &rx_multi,
494 &tx_bytes, &tx_packets, &tx_errs, &tx_drops,
495 &tx_fifo, &tx_colls, &tx_carrier) != 14)
496 continue;
497 if (p->name[0] && strcmp(p->name, name))
498 continue;
499 type = do_ioctl_get_iftype(name);
500 if (type == -1) {
501 fprintf(stderr, "Failed to get type of [%s]\n", name);
502 continue;
503 }
504 if (type != ARPHRD_TUNNEL && type != ARPHRD_IPGRE && type != ARPHRD_SIT)
505 continue;
506 memset(&p1, 0, sizeof(p1));
507 if (do_get_ioctl(name, &p1))
508 continue;
509 if ((p->link && p1.link != p->link) ||
510 (p->name[0] && strcmp(p1.name, p->name)) ||
511 (p->iph.daddr && p1.iph.daddr != p->iph.daddr) ||
512 (p->iph.saddr && p1.iph.saddr != p->iph.saddr) ||
513 (p->i_key && p1.i_key != p->i_key))
514 continue;
515 print_tunnel(&p1);
516 if (show_stats) {
517 printf("%s", _SL_);
518 printf("RX: Packets Bytes Errors CsumErrs OutOfSeq Mcasts%s", _SL_);
519 printf(" %-10ld %-12ld %-6ld %-8ld %-8ld %-8ld%s",
520 rx_packets, rx_bytes, rx_errs, rx_frame, rx_fifo, rx_multi, _SL_);
521 printf("TX: Packets Bytes Errors DeadLoop NoRoute NoBufs%s", _SL_);
522 printf(" %-10ld %-12ld %-6ld %-8ld %-8ld %-6ld",
523 tx_packets, tx_bytes, tx_errs, tx_colls, tx_carrier, tx_drops);
524 }
525 printf("\n");
526 }
527 return 0;
528 }
529
530 static int do_show(int argc, char **argv)
531 {
532 int err;
533 struct ip_tunnel_parm p;
534
535 if (parse_args(argc, argv, SIOCGETTUNNEL, &p) < 0)
536 return -1;
537
538 switch (p.iph.protocol) {
539 case IPPROTO_IPIP:
540 err = do_get_ioctl(p.name[0] ? p.name : "tunl0", &p);
541 break;
542 case IPPROTO_GRE:
543 err = do_get_ioctl(p.name[0] ? p.name : "gre0", &p);
544 break;
545 case IPPROTO_IPV6:
546 err = do_get_ioctl(p.name[0] ? p.name : "sit0", &p);
547 break;
548 default:
549 do_tunnels_list(&p);
550 return 0;
551 }
552 if (err)
553 return -1;
554
555 print_tunnel(&p);
556 printf("\n");
557 return 0;
558 }
559
560 int do_iptunnel(int argc, char **argv)
561 {
562 if (argc > 0) {
563 if (matches(*argv, "add") == 0)
564 return do_add(SIOCADDTUNNEL, argc-1, argv+1);
565 if (matches(*argv, "change") == 0)
566 return do_add(SIOCCHGTUNNEL, argc-1, argv+1);
567 if (matches(*argv, "del") == 0)
568 return do_del(argc-1, argv+1);
569 if (matches(*argv, "show") == 0 ||
570 matches(*argv, "lst") == 0 ||
571 matches(*argv, "list") == 0)
572 return do_show(argc-1, argv+1);
573 if (matches(*argv, "help") == 0)
574 usage();
575 } else
576 return do_show(0, NULL);
577
578 fprintf(stderr, "Command \"%s\" is unknown, try \"ip tunnel help\".\n", *argv);
579 exit(-1);
580 }