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