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