1/*
2 * link_gre6.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:	Dmitry Kozlov <xeb@mail.ru>
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 <linux/ip6_tunnel.h>
22
23#include "rt_names.h"
24#include "utils.h"
25#include "ip_common.h"
26#include "tunnel.h"
27
28#define IP6_FLOWINFO_TCLASS	htonl(0x0FF00000)
29#define IP6_FLOWINFO_FLOWLABEL	htonl(0x000FFFFF)
30
31#define DEFAULT_TNL_HOP_LIMIT	(64)
32
33static void print_usage(FILE *f)
34{
35	fprintf(f, "Usage: ip link { add | set | change | replace | del } NAME\n");
36	fprintf(f, "          type { ip6gre | ip6gretap } [ remote ADDR ] [ local ADDR ]\n");
37	fprintf(f, "          [ [i|o]seq ] [ [i|o]key KEY ] [ [i|o]csum ]\n");
38	fprintf(f, "          [ hoplimit TTL ] [ encaplimit ELIM ]\n");
39	fprintf(f, "          [ tclass TCLASS ] [ flowlabel FLOWLABEL ]\n");
40	fprintf(f, "          [ dscp inherit ] [ dev PHYS_DEV ]\n");
41	fprintf(f, "\n");
42	fprintf(f, "Where: NAME      := STRING\n");
43	fprintf(f, "       ADDR      := IPV6_ADDRESS\n");
44	fprintf(f, "       TTL       := { 0..255 } (default=%d)\n",
45		DEFAULT_TNL_HOP_LIMIT);
46	fprintf(f, "       KEY       := { DOTTED_QUAD | NUMBER }\n");
47	fprintf(f, "       ELIM      := { none | 0..255 }(default=%d)\n",
48		IPV6_DEFAULT_TNL_ENCAP_LIMIT);
49	fprintf(f, "       TCLASS    := { 0x0..0xff | inherit }\n");
50	fprintf(f, "       FLOWLABEL := { 0x0..0xfffff | inherit }\n");
51}
52
53static void usage(void) __attribute__((noreturn));
54static void usage(void)
55{
56	print_usage(stderr);
57	exit(-1);
58}
59
60static int gre_parse_opt(struct link_util *lu, int argc, char **argv,
61			 struct nlmsghdr *n)
62{
63	struct {
64		struct nlmsghdr n;
65		struct ifinfomsg i;
66		char buf[1024];
67	} req;
68	struct ifinfomsg *ifi = (struct ifinfomsg *)(n + 1);
69	struct rtattr *tb[IFLA_MAX + 1];
70	struct rtattr *linkinfo[IFLA_INFO_MAX+1];
71	struct rtattr *greinfo[IFLA_GRE_MAX + 1];
72	__u16 iflags = 0;
73	__u16 oflags = 0;
74	unsigned ikey = 0;
75	unsigned okey = 0;
76	struct in6_addr raddr = IN6ADDR_ANY_INIT;
77	struct in6_addr laddr = IN6ADDR_ANY_INIT;
78	unsigned link = 0;
79	unsigned flowinfo = 0;
80	unsigned flags = 0;
81	__u8 hop_limit = DEFAULT_TNL_HOP_LIMIT;
82	__u8 encap_limit = IPV6_DEFAULT_TNL_ENCAP_LIMIT;
83	int len;
84
85	if (!(n->nlmsg_flags & NLM_F_CREATE)) {
86		memset(&req, 0, sizeof(req));
87
88		req.n.nlmsg_len = NLMSG_LENGTH(sizeof(*ifi));
89		req.n.nlmsg_flags = NLM_F_REQUEST;
90		req.n.nlmsg_type = RTM_GETLINK;
91		req.i.ifi_family = preferred_family;
92		req.i.ifi_index = ifi->ifi_index;
93
94		if (rtnl_talk(&rth, &req.n, &req.n, sizeof(req)) < 0) {
95get_failed:
96			fprintf(stderr,
97				"Failed to get existing tunnel info.\n");
98			return -1;
99		}
100
101		len = req.n.nlmsg_len;
102		len -= NLMSG_LENGTH(sizeof(*ifi));
103		if (len < 0)
104			goto get_failed;
105
106		parse_rtattr(tb, IFLA_MAX, IFLA_RTA(&req.i), len);
107
108		if (!tb[IFLA_LINKINFO])
109			goto get_failed;
110
111		parse_rtattr_nested(linkinfo, IFLA_INFO_MAX, tb[IFLA_LINKINFO]);
112
113		if (!linkinfo[IFLA_INFO_DATA])
114			goto get_failed;
115
116		parse_rtattr_nested(greinfo, IFLA_GRE_MAX,
117				    linkinfo[IFLA_INFO_DATA]);
118
119		if (greinfo[IFLA_GRE_IKEY])
120			ikey = rta_getattr_u32(greinfo[IFLA_GRE_IKEY]);
121
122		if (greinfo[IFLA_GRE_OKEY])
123			okey = rta_getattr_u32(greinfo[IFLA_GRE_OKEY]);
124
125		if (greinfo[IFLA_GRE_IFLAGS])
126			iflags = rta_getattr_u16(greinfo[IFLA_GRE_IFLAGS]);
127
128		if (greinfo[IFLA_GRE_OFLAGS])
129			oflags = rta_getattr_u16(greinfo[IFLA_GRE_OFLAGS]);
130
131		if (greinfo[IFLA_GRE_LOCAL])
132			memcpy(&laddr, RTA_DATA(greinfo[IFLA_GRE_LOCAL]), sizeof(laddr));
133
134		if (greinfo[IFLA_GRE_REMOTE])
135			memcpy(&raddr, RTA_DATA(greinfo[IFLA_GRE_REMOTE]), sizeof(raddr));
136
137		if (greinfo[IFLA_GRE_TTL])
138			hop_limit = rta_getattr_u8(greinfo[IFLA_GRE_TTL]);
139
140		if (greinfo[IFLA_GRE_LINK])
141			link = rta_getattr_u32(greinfo[IFLA_GRE_LINK]);
142
143		if (greinfo[IFLA_GRE_ENCAP_LIMIT])
144			encap_limit = rta_getattr_u8(greinfo[IFLA_GRE_ENCAP_LIMIT]);
145
146		if (greinfo[IFLA_GRE_FLOWINFO])
147			flowinfo = rta_getattr_u32(greinfo[IFLA_GRE_FLOWINFO]);
148
149		if (greinfo[IFLA_GRE_FLAGS])
150			flags = rta_getattr_u32(greinfo[IFLA_GRE_FLAGS]);
151	}
152
153	while (argc > 0) {
154		if (!matches(*argv, "key")) {
155			unsigned uval;
156
157			NEXT_ARG();
158			iflags |= GRE_KEY;
159			oflags |= GRE_KEY;
160			if (strchr(*argv, '.'))
161				uval = get_addr32(*argv);
162			else {
163				if (get_unsigned(&uval, *argv, 0) < 0) {
164					fprintf(stderr,
165						"Invalid value for \"key\"\n");
166					exit(-1);
167				}
168				uval = htonl(uval);
169			}
170
171			ikey = okey = uval;
172		} else if (!matches(*argv, "ikey")) {
173			unsigned uval;
174
175			NEXT_ARG();
176			iflags |= GRE_KEY;
177			if (strchr(*argv, '.'))
178				uval = get_addr32(*argv);
179			else {
180				if (get_unsigned(&uval, *argv, 0)<0) {
181					fprintf(stderr, "invalid value of \"ikey\"\n");
182					exit(-1);
183				}
184				uval = htonl(uval);
185			}
186			ikey = uval;
187		} else if (!matches(*argv, "okey")) {
188			unsigned uval;
189
190			NEXT_ARG();
191			oflags |= GRE_KEY;
192			if (strchr(*argv, '.'))
193				uval = get_addr32(*argv);
194			else {
195				if (get_unsigned(&uval, *argv, 0)<0) {
196					fprintf(stderr, "invalid value of \"okey\"\n");
197					exit(-1);
198				}
199				uval = htonl(uval);
200			}
201			okey = uval;
202		} else if (!matches(*argv, "seq")) {
203			iflags |= GRE_SEQ;
204			oflags |= GRE_SEQ;
205		} else if (!matches(*argv, "iseq")) {
206			iflags |= GRE_SEQ;
207		} else if (!matches(*argv, "oseq")) {
208			oflags |= GRE_SEQ;
209		} else if (!matches(*argv, "csum")) {
210			iflags |= GRE_CSUM;
211			oflags |= GRE_CSUM;
212		} else if (!matches(*argv, "icsum")) {
213			iflags |= GRE_CSUM;
214		} else if (!matches(*argv, "ocsum")) {
215			oflags |= GRE_CSUM;
216		} else if (!matches(*argv, "remote")) {
217			inet_prefix addr;
218			NEXT_ARG();
219			get_prefix(&addr, *argv, preferred_family);
220			if (addr.family == AF_UNSPEC)
221				invarg("\"remote\" address family is AF_UNSPEC", *argv);
222			memcpy(&raddr, &addr.data, sizeof(raddr));
223		} else if (!matches(*argv, "local")) {
224			inet_prefix addr;
225			NEXT_ARG();
226			get_prefix(&addr, *argv, preferred_family);
227			if (addr.family == AF_UNSPEC)
228				invarg("\"local\" address family is AF_UNSPEC", *argv);
229			memcpy(&laddr, &addr.data, sizeof(laddr));
230		} else if (!matches(*argv, "dev")) {
231			NEXT_ARG();
232			link = if_nametoindex(*argv);
233			if (link == 0) {
234				fprintf(stderr, "Cannot find device \"%s\"\n",
235					*argv);
236				exit(-1);
237			}
238		} else if (!matches(*argv, "ttl") ||
239			   !matches(*argv, "hoplimit")) {
240			__u8 uval;
241			NEXT_ARG();
242			if (get_u8(&uval, *argv, 0))
243				invarg("invalid TTL", *argv);
244			hop_limit = uval;
245		} else if (!matches(*argv, "tos") ||
246			   !matches(*argv, "tclass") ||
247			   !matches(*argv, "dsfield")) {
248			__u8 uval;
249			NEXT_ARG();
250			if (strcmp(*argv, "inherit") == 0)
251				flags |= IP6_TNL_F_USE_ORIG_TCLASS;
252			else {
253				if (get_u8(&uval, *argv, 16))
254					invarg("invalid TClass", *argv);
255				flowinfo |= htonl((__u32)uval << 20) & IP6_FLOWINFO_TCLASS;
256				flags &= ~IP6_TNL_F_USE_ORIG_TCLASS;
257			}
258		} else if (strcmp(*argv, "flowlabel") == 0 ||
259			   strcmp(*argv, "fl") == 0) {
260			__u32 uval;
261			NEXT_ARG();
262			if (strcmp(*argv, "inherit") == 0)
263				flags |= IP6_TNL_F_USE_ORIG_FLOWLABEL;
264			else {
265				if (get_u32(&uval, *argv, 16))
266					invarg("invalid Flowlabel", *argv);
267				if (uval > 0xFFFFF)
268					invarg("invalid Flowlabel", *argv);
269				flowinfo |= htonl(uval) & IP6_FLOWINFO_FLOWLABEL;
270				flags &= ~IP6_TNL_F_USE_ORIG_FLOWLABEL;
271			}
272		} else if (strcmp(*argv, "dscp") == 0) {
273			NEXT_ARG();
274			if (strcmp(*argv, "inherit") != 0)
275				invarg("not inherit", *argv);
276			flags |= IP6_TNL_F_RCV_DSCP_COPY;
277		} else
278			usage();
279		argc--; argv++;
280	}
281
282	addattr32(n, 1024, IFLA_GRE_IKEY, ikey);
283	addattr32(n, 1024, IFLA_GRE_OKEY, okey);
284	addattr_l(n, 1024, IFLA_GRE_IFLAGS, &iflags, 2);
285	addattr_l(n, 1024, IFLA_GRE_OFLAGS, &oflags, 2);
286	addattr_l(n, 1024, IFLA_GRE_LOCAL, &laddr, sizeof(laddr));
287	addattr_l(n, 1024, IFLA_GRE_REMOTE, &raddr, sizeof(raddr));
288	if (link)
289		addattr32(n, 1024, IFLA_GRE_LINK, link);
290	addattr_l(n, 1024, IFLA_GRE_TTL, &hop_limit, 1);
291	addattr_l(n, 1024, IFLA_GRE_ENCAP_LIMIT, &encap_limit, 1);
292	addattr_l(n, 1024, IFLA_GRE_FLOWINFO, &flowinfo, 4);
293	addattr_l(n, 1024, IFLA_GRE_FLAGS, &flowinfo, 4);
294
295	return 0;
296}
297
298static void gre_print_opt(struct link_util *lu, FILE *f, struct rtattr *tb[])
299{
300	char s1[1024];
301	char s2[64];
302	const char *local = "any";
303	const char *remote = "any";
304	unsigned iflags = 0;
305	unsigned oflags = 0;
306	unsigned flags = 0;
307	unsigned flowinfo = 0;
308	struct in6_addr in6_addr_any = IN6ADDR_ANY_INIT;
309
310	if (!tb)
311		return;
312
313	if (tb[IFLA_GRE_FLAGS])
314		flags = rta_getattr_u32(tb[IFLA_GRE_FLAGS]);
315
316	if (tb[IFLA_GRE_FLOWINFO])
317		flags = rta_getattr_u32(tb[IFLA_GRE_FLOWINFO]);
318
319	if (tb[IFLA_GRE_REMOTE]) {
320		struct in6_addr addr;
321		memcpy(&addr, RTA_DATA(tb[IFLA_GRE_REMOTE]), sizeof(addr));
322
323		if (memcmp(&addr, &in6_addr_any, sizeof(addr)))
324			remote = format_host(AF_INET6, sizeof(addr), &addr, s1, sizeof(s1));
325	}
326
327	fprintf(f, "remote %s ", remote);
328
329	if (tb[IFLA_GRE_LOCAL]) {
330		struct in6_addr addr;
331		memcpy(&addr, RTA_DATA(tb[IFLA_GRE_LOCAL]), sizeof(addr));
332
333		if (memcmp(&addr, &in6_addr_any, sizeof(addr)))
334			local = format_host(AF_INET6, sizeof(addr), &addr, s1, sizeof(s1));
335	}
336
337	fprintf(f, "local %s ", local);
338
339	if (tb[IFLA_GRE_LINK] && rta_getattr_u32(tb[IFLA_GRE_LINK])) {
340		unsigned link = rta_getattr_u32(tb[IFLA_GRE_LINK]);
341		const char *n = if_indextoname(link, s2);
342
343		if (n)
344			fprintf(f, "dev %s ", n);
345		else
346			fprintf(f, "dev %u ", link);
347	}
348
349	if (tb[IFLA_GRE_TTL] && rta_getattr_u8(tb[IFLA_GRE_TTL]))
350		fprintf(f, "hoplimit %d ", rta_getattr_u8(tb[IFLA_GRE_TTL]));
351
352	if (flags & IP6_TNL_F_IGN_ENCAP_LIMIT)
353		fprintf(f, "encaplimit none ");
354	else if (tb[IFLA_GRE_ENCAP_LIMIT]) {
355		int encap_limit = rta_getattr_u8(tb[IFLA_GRE_ENCAP_LIMIT]);
356
357		fprintf(f, "encaplimit %d ", encap_limit);
358	}
359
360	if (flags & IP6_TNL_F_USE_ORIG_FLOWLABEL)
361		fprintf(f, "flowlabel inherit ");
362	else
363		fprintf(f, "flowlabel 0x%05x ", ntohl(flowinfo & IP6_FLOWINFO_FLOWLABEL));
364
365	if (flags & IP6_TNL_F_RCV_DSCP_COPY)
366		fprintf(f, "dscp inherit ");
367
368	if (tb[IFLA_GRE_IFLAGS])
369		iflags = rta_getattr_u16(tb[IFLA_GRE_IFLAGS]);
370
371	if (tb[IFLA_GRE_OFLAGS])
372		oflags = rta_getattr_u16(tb[IFLA_GRE_OFLAGS]);
373
374	if ((iflags & GRE_KEY) && tb[IFLA_GRE_IKEY]) {
375		inet_ntop(AF_INET, RTA_DATA(tb[IFLA_GRE_IKEY]), s2, sizeof(s2));
376		fprintf(f, "ikey %s ", s2);
377	}
378
379	if ((oflags & GRE_KEY) && tb[IFLA_GRE_OKEY]) {
380		inet_ntop(AF_INET, RTA_DATA(tb[IFLA_GRE_OKEY]), s2, sizeof(s2));
381		fprintf(f, "okey %s ", s2);
382	}
383
384	if (iflags & GRE_SEQ)
385		fputs("iseq ", f);
386	if (oflags & GRE_SEQ)
387		fputs("oseq ", f);
388	if (iflags & GRE_CSUM)
389		fputs("icsum ", f);
390	if (oflags & GRE_CSUM)
391		fputs("ocsum ", f);
392}
393
394static void gre_print_help(struct link_util *lu, int argc, char **argv,
395	FILE *f)
396{
397	print_usage(f);
398}
399
400struct link_util ip6gre_link_util = {
401	.id = "ip6gre",
402	.maxattr = IFLA_GRE_MAX,
403	.parse_opt = gre_parse_opt,
404	.print_opt = gre_print_opt,
405	.print_help = gre_print_help,
406};
407
408struct link_util ip6gretap_link_util = {
409	.id = "ip6gretap",
410	.maxattr = IFLA_GRE_MAX,
411	.parse_opt = gre_parse_opt,
412	.print_opt = gre_print_opt,
413	.print_help = gre_print_help,
414};
415