1/*
2 * link_gre.c	gre driver module
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:	Herbert Xu <herbert@gondor.apana.org.au>
10 *
11 */
12
13#include <string.h>
14#include <net/if.h>
15#include <sys/types.h>
16#include <sys/socket.h>
17#include <arpa/inet.h>
18
19#include <linux/ip.h>
20#include <linux/if_tunnel.h>
21#include "rt_names.h"
22#include "utils.h"
23#include "ip_common.h"
24#include "tunnel.h"
25
26static void usage(void) __attribute__((noreturn));
27static void usage(void)
28{
29	fprintf(stderr, "Usage: ip link { add | set | change | replace | del } NAME\n");
30	fprintf(stderr, "          type { gre | gretap } [ remote ADDR ] [ local ADDR ]\n");
31	fprintf(stderr, "          [ [i|o]seq ] [ [i|o]key KEY ] [ [i|o]csum ]\n");
32	fprintf(stderr, "          [ ttl TTL ] [ tos TOS ] [ [no]pmtudisc ] [ dev PHYS_DEV ]\n");
33	fprintf(stderr, "\n");
34	fprintf(stderr, "Where: NAME := STRING\n");
35	fprintf(stderr, "       ADDR := { IP_ADDRESS | any }\n");
36	fprintf(stderr, "       TOS  := { NUMBER | inherit }\n");
37	fprintf(stderr, "       TTL  := { 1..255 | inherit }\n");
38	fprintf(stderr, "       KEY  := { DOTTED_QUAD | NUMBER }\n");
39	exit(-1);
40}
41
42static int gre_parse_opt(struct link_util *lu, int argc, char **argv,
43			 struct nlmsghdr *n)
44{
45	struct {
46		struct nlmsghdr n;
47		struct ifinfomsg i;
48		char buf[1024];
49	} req;
50	struct ifinfomsg *ifi = (struct ifinfomsg *)(n + 1);
51	struct rtattr *tb[IFLA_MAX + 1];
52	struct rtattr *linkinfo[IFLA_INFO_MAX+1];
53	struct rtattr *greinfo[IFLA_GRE_MAX + 1];
54	__u16 iflags = 0;
55	__u16 oflags = 0;
56	unsigned ikey = 0;
57	unsigned okey = 0;
58	unsigned saddr = 0;
59	unsigned daddr = 0;
60	unsigned link = 0;
61	__u8 pmtudisc = 1;
62	__u8 ttl = 0;
63	__u8 tos = 0;
64	int len;
65
66	if (!(n->nlmsg_flags & NLM_F_CREATE)) {
67		memset(&req, 0, sizeof(req));
68
69		req.n.nlmsg_len = NLMSG_LENGTH(sizeof(*ifi));
70		req.n.nlmsg_flags = NLM_F_REQUEST;
71		req.n.nlmsg_type = RTM_GETLINK;
72		req.i.ifi_family = preferred_family;
73		req.i.ifi_index = ifi->ifi_index;
74
75		if (rtnl_talk(&rth, &req.n, 0, 0, &req.n, NULL, NULL) < 0) {
76get_failed:
77			fprintf(stderr,
78				"Failed to get existing tunnel info.\n");
79			return -1;
80		}
81
82		len = req.n.nlmsg_len;
83		len -= NLMSG_LENGTH(sizeof(*ifi));
84		if (len < 0)
85			goto get_failed;
86
87		parse_rtattr(tb, IFLA_MAX, IFLA_RTA(&req.i), len);
88
89		if (!tb[IFLA_LINKINFO])
90			goto get_failed;
91
92		parse_rtattr_nested(linkinfo, IFLA_INFO_MAX, tb[IFLA_LINKINFO]);
93
94		if (!linkinfo[IFLA_INFO_DATA])
95			goto get_failed;
96
97		parse_rtattr_nested(greinfo, IFLA_GRE_MAX,
98				    linkinfo[IFLA_INFO_DATA]);
99
100		if (greinfo[IFLA_GRE_IKEY])
101			ikey = *(__u32 *)RTA_DATA(greinfo[IFLA_GRE_IKEY]);
102
103		if (greinfo[IFLA_GRE_OKEY])
104			okey = *(__u32 *)RTA_DATA(greinfo[IFLA_GRE_OKEY]);
105
106		if (greinfo[IFLA_GRE_IFLAGS])
107			iflags = *(__u16 *)RTA_DATA(greinfo[IFLA_GRE_IFLAGS]);
108
109		if (greinfo[IFLA_GRE_OFLAGS])
110			oflags = *(__u16 *)RTA_DATA(greinfo[IFLA_GRE_OFLAGS]);
111
112		if (greinfo[IFLA_GRE_LOCAL])
113			saddr = *(__u32 *)RTA_DATA(greinfo[IFLA_GRE_LOCAL]);
114
115		if (greinfo[IFLA_GRE_REMOTE])
116			daddr = *(__u32 *)RTA_DATA(greinfo[IFLA_GRE_REMOTE]);
117
118		if (greinfo[IFLA_GRE_PMTUDISC])
119			pmtudisc = *(__u8 *)RTA_DATA(
120				greinfo[IFLA_GRE_PMTUDISC]);
121
122		if (greinfo[IFLA_GRE_TTL])
123			ttl = *(__u8 *)RTA_DATA(greinfo[IFLA_GRE_TTL]);
124
125		if (greinfo[IFLA_GRE_TOS])
126			tos = *(__u8 *)RTA_DATA(greinfo[IFLA_GRE_TOS]);
127
128		if (greinfo[IFLA_GRE_LINK])
129			link = *(__u8 *)RTA_DATA(greinfo[IFLA_GRE_LINK]);
130	}
131
132	while (argc > 0) {
133		if (!matches(*argv, "key")) {
134			unsigned uval;
135
136			NEXT_ARG();
137			iflags |= GRE_KEY;
138			oflags |= GRE_KEY;
139			if (strchr(*argv, '.'))
140				uval = get_addr32(*argv);
141			else {
142				if (get_unsigned(&uval, *argv, 0) < 0) {
143					fprintf(stderr,
144						"Invalid value for \"key\"\n");
145					exit(-1);
146				}
147				uval = htonl(uval);
148			}
149
150			ikey = okey = uval;
151		} else if (!matches(*argv, "ikey")) {
152			unsigned uval;
153
154			NEXT_ARG();
155			iflags |= GRE_KEY;
156			if (strchr(*argv, '.'))
157				uval = get_addr32(*argv);
158			else {
159				if (get_unsigned(&uval, *argv, 0)<0) {
160					fprintf(stderr, "invalid value of \"ikey\"\n");
161					exit(-1);
162				}
163				uval = htonl(uval);
164			}
165			ikey = uval;
166		} else if (!matches(*argv, "okey")) {
167			unsigned uval;
168
169			NEXT_ARG();
170			oflags |= GRE_KEY;
171			if (strchr(*argv, '.'))
172				uval = get_addr32(*argv);
173			else {
174				if (get_unsigned(&uval, *argv, 0)<0) {
175					fprintf(stderr, "invalid value of \"okey\"\n");
176					exit(-1);
177				}
178				uval = htonl(uval);
179			}
180			okey = uval;
181		} else if (!matches(*argv, "seq")) {
182			iflags |= GRE_SEQ;
183			oflags |= GRE_SEQ;
184		} else if (!matches(*argv, "iseq")) {
185			iflags |= GRE_SEQ;
186		} else if (!matches(*argv, "oseq")) {
187			oflags |= GRE_SEQ;
188		} else if (!matches(*argv, "csum")) {
189			iflags |= GRE_CSUM;
190			oflags |= GRE_CSUM;
191		} else if (!matches(*argv, "icsum")) {
192			iflags |= GRE_CSUM;
193		} else if (!matches(*argv, "ocsum")) {
194			oflags |= GRE_CSUM;
195		} else if (!matches(*argv, "nopmtudisc")) {
196			pmtudisc = 0;
197		} else if (!matches(*argv, "pmtudisc")) {
198			pmtudisc = 1;
199		} else if (!matches(*argv, "remote")) {
200			NEXT_ARG();
201			if (strcmp(*argv, "any"))
202				daddr = get_addr32(*argv);
203		} else if (!matches(*argv, "local")) {
204			NEXT_ARG();
205			if (strcmp(*argv, "any"))
206				saddr = get_addr32(*argv);
207		} else if (!matches(*argv, "dev")) {
208			NEXT_ARG();
209			link = if_nametoindex(*argv);
210			if (link == 0)
211				exit(-1);
212		} else if (!matches(*argv, "ttl") ||
213			   !matches(*argv, "hoplimit")) {
214			unsigned uval;
215
216			NEXT_ARG();
217			if (strcmp(*argv, "inherit") != 0) {
218				if (get_unsigned(&uval, *argv, 0))
219					invarg("invalid TTL\n", *argv);
220				if (uval > 255)
221					invarg("TTL must be <= 255\n", *argv);
222				ttl = uval;
223			}
224		} else if (!matches(*argv, "tos") ||
225			   !matches(*argv, "tclass") ||
226			   !matches(*argv, "dsfield")) {
227			__u32 uval;
228
229			NEXT_ARG();
230			if (strcmp(*argv, "inherit") != 0) {
231				if (rtnl_dsfield_a2n(&uval, *argv))
232					invarg("bad TOS value", *argv);
233				tos = uval;
234			} else
235				tos = 1;
236		} else
237			usage();
238		argc--; argv++;
239	}
240
241	if (!ikey && IN_MULTICAST(ntohl(daddr))) {
242		ikey = daddr;
243		iflags |= GRE_KEY;
244	}
245	if (!okey && IN_MULTICAST(ntohl(daddr))) {
246		okey = daddr;
247		oflags |= GRE_KEY;
248	}
249	if (IN_MULTICAST(ntohl(daddr)) && !saddr) {
250		fprintf(stderr, "Broadcast tunnel requires a source address.\n");
251		return -1;
252	}
253
254	addattr32(n, 1024, IFLA_GRE_IKEY, ikey);
255	addattr32(n, 1024, IFLA_GRE_OKEY, okey);
256	addattr_l(n, 1024, IFLA_GRE_IFLAGS, &iflags, 2);
257	addattr_l(n, 1024, IFLA_GRE_OFLAGS, &oflags, 2);
258	addattr_l(n, 1024, IFLA_GRE_LOCAL, &saddr, 4);
259	addattr_l(n, 1024, IFLA_GRE_REMOTE, &daddr, 4);
260	addattr_l(n, 1024, IFLA_GRE_PMTUDISC, &pmtudisc, 1);
261	if (link)
262		addattr32(n, 1024, IFLA_GRE_LINK, link);
263	addattr_l(n, 1024, IFLA_GRE_TTL, &ttl, 1);
264	addattr_l(n, 1024, IFLA_GRE_TOS, &tos, 1);
265
266	return 0;
267}
268
269static void gre_print_opt(struct link_util *lu, FILE *f, struct rtattr *tb[])
270{
271	char s1[1024];
272	char s2[64];
273	const char *local = "any";
274	const char *remote = "any";
275	unsigned iflags = 0;
276	unsigned oflags = 0;
277
278	if (!tb)
279		return;
280
281	if (tb[IFLA_GRE_REMOTE]) {
282		unsigned addr = *(__u32 *)RTA_DATA(tb[IFLA_GRE_REMOTE]);
283
284		if (addr)
285			remote = format_host(AF_INET, 4, &addr, s1, sizeof(s1));
286	}
287
288	fprintf(f, "remote %s ", remote);
289
290	if (tb[IFLA_GRE_LOCAL]) {
291		unsigned addr = *(__u32 *)RTA_DATA(tb[IFLA_GRE_LOCAL]);
292
293		if (addr)
294			local = format_host(AF_INET, 4, &addr, s1, sizeof(s1));
295	}
296
297	fprintf(f, "local %s ", local);
298
299	if (tb[IFLA_GRE_LINK] && *(__u32 *)RTA_DATA(tb[IFLA_GRE_LINK])) {
300		unsigned link = *(__u32 *)RTA_DATA(tb[IFLA_GRE_LINK]);
301		const char *n = if_indextoname(link, s2);
302
303		if (n)
304			fprintf(f, "dev %s ", n);
305		else
306			fprintf(f, "dev %u ", link);
307	}
308
309	if (tb[IFLA_GRE_TTL] && *(__u8 *)RTA_DATA(tb[IFLA_GRE_TTL]))
310		fprintf(f, "ttl %d ", *(__u8 *)RTA_DATA(tb[IFLA_GRE_TTL]));
311	else
312		fprintf(f, "ttl inherit ");
313
314	if (tb[IFLA_GRE_TOS] && *(__u8 *)RTA_DATA(tb[IFLA_GRE_TOS])) {
315		int tos = *(__u8 *)RTA_DATA(tb[IFLA_GRE_TOS]);
316
317		fputs("tos ", f);
318		if (tos == 1)
319			fputs("inherit ", f);
320		else
321			fprintf(f, "0x%x ", tos);
322	}
323
324	if (tb[IFLA_GRE_PMTUDISC] &&
325	    !*(__u8 *)RTA_DATA(tb[IFLA_GRE_PMTUDISC]))
326		fputs("nopmtudisc ", f);
327
328	if (tb[IFLA_GRE_IFLAGS])
329		iflags = *(__u16 *)RTA_DATA(tb[IFLA_GRE_IFLAGS]);
330
331	if (tb[IFLA_GRE_OFLAGS])
332		oflags = *(__u16 *)RTA_DATA(tb[IFLA_GRE_OFLAGS]);
333
334	if (iflags & GRE_KEY && tb[IFLA_GRE_IKEY] &&
335	    *(__u32 *)RTA_DATA(tb[IFLA_GRE_IKEY])) {
336		inet_ntop(AF_INET, RTA_DATA(tb[IFLA_GRE_IKEY]), s2, sizeof(s2));
337		fprintf(f, "ikey %s ", s2);
338	}
339
340	if (oflags & GRE_KEY && tb[IFLA_GRE_OKEY] &&
341	    *(__u32 *)RTA_DATA(tb[IFLA_GRE_OKEY])) {
342		inet_ntop(AF_INET, RTA_DATA(tb[IFLA_GRE_OKEY]), s2, sizeof(s2));
343		fprintf(f, "ikey %s ", s2);
344	}
345
346	if (iflags & GRE_SEQ)
347		fputs("iseq ", f);
348	if (oflags & GRE_SEQ)
349		fputs("oseq ", f);
350	if (iflags & GRE_CSUM)
351		fputs("icsum ", f);
352	if (oflags & GRE_CSUM)
353		fputs("ocsum ", f);
354}
355
356struct link_util gre_link_util = {
357	.id = "gre",
358	.maxattr = IFLA_GRE_MAX,
359	.parse_opt = gre_parse_opt,
360	.print_opt = gre_print_opt,
361};
362
363struct link_util gretap_link_util = {
364	.id = "gretap",
365	.maxattr = IFLA_GRE_MAX,
366	.parse_opt = gre_parse_opt,
367	.print_opt = gre_print_opt,
368};
369