1/*
2 * link_vti.c	VTI 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 *          Saurabh Mohan <saurabh.mohan@vyatta.com> Modified link_gre.c for VTI
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
26
27static void print_usage(FILE *f)
28{
29	fprintf(f,
30		"Usage: ... vti [ remote ADDR ]\n"
31		"               [ local ADDR ]\n"
32		"               [ [i|o]key KEY ]\n"
33		"               [ dev PHYS_DEV ]\n"
34		"               [ fwmark MARK ]\n"
35		"\n"
36		"Where: ADDR := { IP_ADDRESS }\n"
37		"       KEY  := { DOTTED_QUAD | NUMBER }\n"
38		"       MARK := { 0x0..0xffffffff }\n"
39	);
40}
41
42static void usage(void) __attribute__((noreturn));
43static void usage(void)
44{
45	print_usage(stderr);
46	exit(-1);
47}
48
49static int vti_parse_opt(struct link_util *lu, int argc, char **argv,
50			 struct nlmsghdr *n)
51{
52	struct ifinfomsg *ifi = (struct ifinfomsg *)(n + 1);
53	struct {
54		struct nlmsghdr n;
55		struct ifinfomsg i;
56		char buf[1024];
57	} req = {
58		.n.nlmsg_len = NLMSG_LENGTH(sizeof(*ifi)),
59		.n.nlmsg_flags = NLM_F_REQUEST,
60		.n.nlmsg_type = RTM_GETLINK,
61		.i.ifi_family = preferred_family,
62		.i.ifi_index = ifi->ifi_index,
63	};
64	struct rtattr *tb[IFLA_MAX + 1];
65	struct rtattr *linkinfo[IFLA_INFO_MAX+1];
66	struct rtattr *vtiinfo[IFLA_VTI_MAX + 1];
67	unsigned int ikey = 0;
68	unsigned int okey = 0;
69	unsigned int saddr = 0;
70	unsigned int daddr = 0;
71	unsigned int link = 0;
72	unsigned int fwmark = 0;
73	int len;
74
75	if (!(n->nlmsg_flags & NLM_F_CREATE)) {
76		if (rtnl_talk(&rth, &req.n, &req.n, sizeof(req)) < 0) {
77get_failed:
78			fprintf(stderr,
79				"Failed to get existing tunnel info.\n");
80			return -1;
81		}
82
83		len = req.n.nlmsg_len;
84		len -= NLMSG_LENGTH(sizeof(*ifi));
85		if (len < 0)
86			goto get_failed;
87
88		parse_rtattr(tb, IFLA_MAX, IFLA_RTA(&req.i), len);
89
90		if (!tb[IFLA_LINKINFO])
91			goto get_failed;
92
93		parse_rtattr_nested(linkinfo, IFLA_INFO_MAX, tb[IFLA_LINKINFO]);
94
95		if (!linkinfo[IFLA_INFO_DATA])
96			goto get_failed;
97
98		parse_rtattr_nested(vtiinfo, IFLA_VTI_MAX,
99				    linkinfo[IFLA_INFO_DATA]);
100
101		if (vtiinfo[IFLA_VTI_IKEY])
102			ikey = rta_getattr_u32(vtiinfo[IFLA_VTI_IKEY]);
103
104		if (vtiinfo[IFLA_VTI_OKEY])
105			okey = rta_getattr_u32(vtiinfo[IFLA_VTI_OKEY]);
106
107		if (vtiinfo[IFLA_VTI_LOCAL])
108			saddr = rta_getattr_u32(vtiinfo[IFLA_VTI_LOCAL]);
109
110		if (vtiinfo[IFLA_VTI_REMOTE])
111			daddr = rta_getattr_u32(vtiinfo[IFLA_VTI_REMOTE]);
112
113		if (vtiinfo[IFLA_VTI_LINK])
114			link = rta_getattr_u8(vtiinfo[IFLA_VTI_LINK]);
115
116		if (vtiinfo[IFLA_VTI_FWMARK])
117			fwmark = rta_getattr_u32(vtiinfo[IFLA_VTI_FWMARK]);
118	}
119
120	while (argc > 0) {
121		if (!matches(*argv, "key")) {
122			unsigned int uval;
123
124			NEXT_ARG();
125			if (strchr(*argv, '.'))
126				uval = get_addr32(*argv);
127			else {
128				if (get_unsigned(&uval, *argv, 0) < 0) {
129					fprintf(stderr,
130						"Invalid value for \"key\": \"%s\"; it should be an unsigned integer\n", *argv);
131					exit(-1);
132				}
133				uval = htonl(uval);
134			}
135
136			ikey = okey = uval;
137		} else if (!matches(*argv, "ikey")) {
138			unsigned int uval;
139
140			NEXT_ARG();
141			if (strchr(*argv, '.'))
142				uval = get_addr32(*argv);
143			else {
144				if (get_unsigned(&uval, *argv, 0) < 0) {
145					fprintf(stderr, "invalid value for \"ikey\": \"%s\"; it should be an unsigned integer\n", *argv);
146					exit(-1);
147				}
148				uval = htonl(uval);
149			}
150			ikey = uval;
151		} else if (!matches(*argv, "okey")) {
152			unsigned int uval;
153
154			NEXT_ARG();
155			if (strchr(*argv, '.'))
156				uval = get_addr32(*argv);
157			else {
158				if (get_unsigned(&uval, *argv, 0) < 0) {
159					fprintf(stderr, "invalid value for \"okey\": \"%s\"; it should be an unsigned integer\n", *argv);
160					exit(-1);
161				}
162				uval = htonl(uval);
163			}
164			okey = uval;
165		} else if (!matches(*argv, "remote")) {
166			NEXT_ARG();
167			if (!strcmp(*argv, "any")) {
168				fprintf(stderr, "invalid value for \"remote\": \"%s\"\n", *argv);
169				exit(-1);
170			} else {
171				daddr = get_addr32(*argv);
172			}
173		} else if (!matches(*argv, "local")) {
174			NEXT_ARG();
175			if (!strcmp(*argv, "any")) {
176				fprintf(stderr, "invalid value for \"local\": \"%s\"\n", *argv);
177				exit(-1);
178			} else {
179				saddr = get_addr32(*argv);
180			}
181		} else if (!matches(*argv, "dev")) {
182			NEXT_ARG();
183			link = if_nametoindex(*argv);
184			if (link == 0) {
185				fprintf(stderr, "Cannot find device \"%s\"\n",
186					*argv);
187				exit(-1);
188			}
189		} else if (strcmp(*argv, "fwmark") == 0) {
190			NEXT_ARG();
191			if (get_u32(&fwmark, *argv, 0))
192				invarg("invalid fwmark\n", *argv);
193		} else
194			usage();
195		argc--; argv++;
196	}
197
198	addattr32(n, 1024, IFLA_VTI_IKEY, ikey);
199	addattr32(n, 1024, IFLA_VTI_OKEY, okey);
200	addattr_l(n, 1024, IFLA_VTI_LOCAL, &saddr, 4);
201	addattr_l(n, 1024, IFLA_VTI_REMOTE, &daddr, 4);
202	addattr32(n, 1024, IFLA_VTI_FWMARK, fwmark);
203	if (link)
204		addattr32(n, 1024, IFLA_VTI_LINK, link);
205
206	return 0;
207}
208
209static void vti_print_opt(struct link_util *lu, FILE *f, struct rtattr *tb[])
210{
211	const char *local = "any";
212	const char *remote = "any";
213	__u32 key;
214	unsigned int link;
215	char s2[IFNAMSIZ];
216
217	if (!tb)
218		return;
219
220	if (tb[IFLA_VTI_REMOTE]) {
221		unsigned int addr = rta_getattr_u32(tb[IFLA_VTI_REMOTE]);
222
223		if (addr)
224			remote = format_host(AF_INET, 4, &addr);
225	}
226
227	print_string(PRINT_ANY, "remote", "remote %s ", remote);
228
229	if (tb[IFLA_VTI_LOCAL]) {
230		unsigned int addr = rta_getattr_u32(tb[IFLA_VTI_LOCAL]);
231
232		if (addr)
233			local = format_host(AF_INET, 4, &addr);
234	}
235
236	print_string(PRINT_ANY, "local", "local %s ", local);
237
238	if (tb[IFLA_VTI_LINK] &&
239	    (link = rta_getattr_u32(tb[IFLA_VTI_LINK]))) {
240		const char *n = if_indextoname(link, s2);
241
242		if (n)
243			print_string(PRINT_ANY, "link", "dev %s ", n);
244		else
245			print_uint(PRINT_ANY, "link_index", "dev %u ", link);
246	}
247
248	if (tb[IFLA_VTI_IKEY] &&
249	    (key = rta_getattr_u32(tb[IFLA_VTI_IKEY])))
250		print_0xhex(PRINT_ANY, "ikey", "ikey %#x ", ntohl(key));
251
252
253	if (tb[IFLA_VTI_OKEY] &&
254	    (key = rta_getattr_u32(tb[IFLA_VTI_OKEY])))
255		print_0xhex(PRINT_ANY, "okey", "okey %#x ", ntohl(key));
256
257	if (tb[IFLA_VTI_FWMARK]) {
258		__u32 fwmark = rta_getattr_u32(tb[IFLA_VTI_FWMARK]);
259
260		if (fwmark) {
261			SPRINT_BUF(b1);
262
263			snprintf(b1, sizeof(b1), "0x%x", fwmark);
264			print_string(PRINT_ANY, "fwmark", "fwmark %s ", s2);
265		}
266	}
267}
268
269static void vti_print_help(struct link_util *lu, int argc, char **argv,
270	FILE *f)
271{
272	print_usage(f);
273}
274
275struct link_util vti_link_util = {
276	.id = "vti",
277	.maxattr = IFLA_VTI_MAX,
278	.parse_opt = vti_parse_opt,
279	.print_opt = vti_print_opt,
280	.print_help = vti_print_help,
281};
282