1/*
2 * iproute_lwtunnel.c
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:	Roopa Prabhu, <roopa@cumulusnetworks.com>
10 * 		Thomas Graf <tgraf@suug.ch>
11 *
12 */
13
14#include <stdio.h>
15#include <stdlib.h>
16#include <unistd.h>
17#include <fcntl.h>
18#include <string.h>
19#include <linux/ila.h>
20#include <linux/lwtunnel.h>
21#include <linux/mpls_iptunnel.h>
22#include <errno.h>
23
24#include "rt_names.h"
25#include "utils.h"
26#include "iproute_lwtunnel.h"
27
28static int read_encap_type(const char *name)
29{
30	if (strcmp(name, "mpls") == 0)
31		return LWTUNNEL_ENCAP_MPLS;
32	else if (strcmp(name, "ip") == 0)
33		return LWTUNNEL_ENCAP_IP;
34	else if (strcmp(name, "ip6") == 0)
35		return LWTUNNEL_ENCAP_IP6;
36	else if (strcmp(name, "ila") == 0)
37		return LWTUNNEL_ENCAP_ILA;
38	else
39		return LWTUNNEL_ENCAP_NONE;
40}
41
42static const char *format_encap_type(int type)
43{
44	switch (type) {
45	case LWTUNNEL_ENCAP_MPLS:
46		return "mpls";
47	case LWTUNNEL_ENCAP_IP:
48		return "ip";
49	case LWTUNNEL_ENCAP_IP6:
50		return "ip6";
51	case LWTUNNEL_ENCAP_ILA:
52		return "ila";
53	default:
54		return "unknown";
55	}
56}
57
58static void print_encap_mpls(FILE *fp, struct rtattr *encap)
59{
60	struct rtattr *tb[MPLS_IPTUNNEL_MAX+1];
61	char abuf[256];
62
63	parse_rtattr_nested(tb, MPLS_IPTUNNEL_MAX, encap);
64
65	if (tb[MPLS_IPTUNNEL_DST])
66		fprintf(fp, " %s ", format_host(AF_MPLS,
67			RTA_PAYLOAD(tb[MPLS_IPTUNNEL_DST]),
68			RTA_DATA(tb[MPLS_IPTUNNEL_DST]),
69			abuf, sizeof(abuf)));
70}
71
72static void print_encap_ip(FILE *fp, struct rtattr *encap)
73{
74	struct rtattr *tb[LWTUNNEL_IP_MAX+1];
75	char abuf[256];
76
77	parse_rtattr_nested(tb, LWTUNNEL_IP_MAX, encap);
78
79	if (tb[LWTUNNEL_IP_ID])
80		fprintf(fp, "id %llu ", ntohll(rta_getattr_u64(tb[LWTUNNEL_IP_ID])));
81
82	if (tb[LWTUNNEL_IP_SRC])
83		fprintf(fp, "src %s ",
84			rt_addr_n2a(AF_INET,
85				    RTA_PAYLOAD(tb[LWTUNNEL_IP_SRC]),
86				    RTA_DATA(tb[LWTUNNEL_IP_SRC]),
87				    abuf, sizeof(abuf)));
88
89	if (tb[LWTUNNEL_IP_DST])
90		fprintf(fp, "dst %s ",
91			rt_addr_n2a(AF_INET,
92				    RTA_PAYLOAD(tb[LWTUNNEL_IP_DST]),
93				    RTA_DATA(tb[LWTUNNEL_IP_DST]),
94				    abuf, sizeof(abuf)));
95
96	if (tb[LWTUNNEL_IP_TTL])
97		fprintf(fp, "ttl %d ", rta_getattr_u8(tb[LWTUNNEL_IP_TTL]));
98
99	if (tb[LWTUNNEL_IP_TOS])
100		fprintf(fp, "tos %d ", rta_getattr_u8(tb[LWTUNNEL_IP_TOS]));
101}
102
103static void print_encap_ila(FILE *fp, struct rtattr *encap)
104{
105	struct rtattr *tb[ILA_ATTR_MAX+1];
106
107	parse_rtattr_nested(tb, ILA_ATTR_MAX, encap);
108
109	if (tb[ILA_ATTR_LOCATOR]) {
110		char abuf[ADDR64_BUF_SIZE];
111
112		addr64_n2a(*(__u64 *)RTA_DATA(tb[ILA_ATTR_LOCATOR]),
113			   abuf, sizeof(abuf));
114		fprintf(fp, " %s ", abuf);
115	}
116}
117
118static void print_encap_ip6(FILE *fp, struct rtattr *encap)
119{
120	struct rtattr *tb[LWTUNNEL_IP6_MAX+1];
121	char abuf[256];
122
123	parse_rtattr_nested(tb, LWTUNNEL_IP6_MAX, encap);
124
125	if (tb[LWTUNNEL_IP6_ID])
126		fprintf(fp, "id %llu ", ntohll(rta_getattr_u64(tb[LWTUNNEL_IP6_ID])));
127
128	if (tb[LWTUNNEL_IP6_SRC])
129		fprintf(fp, "src %s ",
130			rt_addr_n2a(AF_INET6,
131				    RTA_PAYLOAD(tb[LWTUNNEL_IP6_SRC]),
132				    RTA_DATA(tb[LWTUNNEL_IP6_SRC]),
133				    abuf, sizeof(abuf)));
134
135	if (tb[LWTUNNEL_IP6_DST])
136		fprintf(fp, "dst %s ",
137			rt_addr_n2a(AF_INET6,
138				    RTA_PAYLOAD(tb[LWTUNNEL_IP6_DST]),
139				    RTA_DATA(tb[LWTUNNEL_IP6_DST]),
140				    abuf, sizeof(abuf)));
141
142	if (tb[LWTUNNEL_IP6_HOPLIMIT])
143		fprintf(fp, "hoplimit %d ", rta_getattr_u8(tb[LWTUNNEL_IP6_HOPLIMIT]));
144
145	if (tb[LWTUNNEL_IP6_TC])
146		fprintf(fp, "tc %d ", rta_getattr_u8(tb[LWTUNNEL_IP6_TC]));
147}
148
149void lwt_print_encap(FILE *fp, struct rtattr *encap_type,
150			  struct rtattr *encap)
151{
152	int et;
153
154	if (!encap_type)
155		return;
156
157	et = rta_getattr_u16(encap_type);
158
159	fprintf(fp, " encap %s ", format_encap_type(et));
160
161	switch (et) {
162	case LWTUNNEL_ENCAP_MPLS:
163		print_encap_mpls(fp, encap);
164		break;
165	case LWTUNNEL_ENCAP_IP:
166		print_encap_ip(fp, encap);
167		break;
168	case LWTUNNEL_ENCAP_ILA:
169		print_encap_ila(fp, encap);
170		break;
171	case LWTUNNEL_ENCAP_IP6:
172		print_encap_ip6(fp, encap);
173		break;
174	}
175}
176
177static int parse_encap_mpls(struct rtattr *rta, size_t len, int *argcp, char ***argvp)
178{
179	inet_prefix addr;
180	int argc = *argcp;
181	char **argv = *argvp;
182
183	if (get_addr(&addr, *argv, AF_MPLS)) {
184		fprintf(stderr, "Error: an inet address is expected rather than \"%s\".\n", *argv);
185		exit(1);
186	}
187
188	rta_addattr_l(rta, len, MPLS_IPTUNNEL_DST, &addr.data,
189		      addr.bytelen);
190
191	*argcp = argc;
192	*argvp = argv;
193
194	return 0;
195}
196
197static int parse_encap_ip(struct rtattr *rta, size_t len, int *argcp, char ***argvp)
198{
199	int id_ok = 0, dst_ok = 0, tos_ok = 0, ttl_ok = 0;
200	char **argv = *argvp;
201	int argc = *argcp;
202
203	while (argc > 0) {
204		if (strcmp(*argv, "id") == 0) {
205			__u64 id;
206			NEXT_ARG();
207			if (id_ok++)
208				duparg2("id", *argv);
209			if (get_u64(&id, *argv, 0))
210				invarg("\"id\" value is invalid\n", *argv);
211			rta_addattr64(rta, len, LWTUNNEL_IP_ID, htonll(id));
212		} else if (strcmp(*argv, "dst") == 0) {
213			inet_prefix addr;
214			NEXT_ARG();
215			if (dst_ok++)
216				duparg2("dst", *argv);
217			get_addr(&addr, *argv, AF_INET);
218			rta_addattr_l(rta, len, LWTUNNEL_IP_DST, &addr.data, addr.bytelen);
219		} else if (strcmp(*argv, "tos") == 0) {
220			__u32 tos;
221			NEXT_ARG();
222			if (tos_ok++)
223				duparg2("tos", *argv);
224			if (rtnl_dsfield_a2n(&tos, *argv))
225				invarg("\"tos\" value is invalid\n", *argv);
226			rta_addattr8(rta, len, LWTUNNEL_IP_TOS, tos);
227		} else if (strcmp(*argv, "ttl") == 0) {
228			__u8 ttl;
229			NEXT_ARG();
230			if (ttl_ok++)
231				duparg2("ttl", *argv);
232			if (get_u8(&ttl, *argv, 0))
233				invarg("\"ttl\" value is invalid\n", *argv);
234			rta_addattr8(rta, len, LWTUNNEL_IP_TTL, ttl);
235		} else {
236			break;
237		}
238		argc--; argv++;
239	}
240
241	/* argv is currently the first unparsed argument,
242	 * but the lwt_parse_encap() caller will move to the next,
243	 * so step back */
244	*argcp = argc + 1;
245	*argvp = argv - 1;
246
247	return 0;
248}
249
250static int parse_encap_ila(struct rtattr *rta, size_t len,
251			   int *argcp, char ***argvp)
252{
253	__u64 locator;
254	int argc = *argcp;
255	char **argv = *argvp;
256
257	if (get_addr64(&locator, *argv) < 0) {
258		fprintf(stderr, "Bad locator: %s\n", *argv);
259		exit(1);
260	}
261
262	rta_addattr64(rta, 1024, ILA_ATTR_LOCATOR, locator);
263
264	*argcp = argc;
265	*argvp = argv;
266
267	return 0;
268}
269
270static int parse_encap_ip6(struct rtattr *rta, size_t len, int *argcp, char ***argvp)
271{
272	int id_ok = 0, dst_ok = 0, tos_ok = 0, ttl_ok = 0;
273	char **argv = *argvp;
274	int argc = *argcp;
275
276	while (argc > 0) {
277		if (strcmp(*argv, "id") == 0) {
278			__u64 id;
279			NEXT_ARG();
280			if (id_ok++)
281				duparg2("id", *argv);
282			if (get_u64(&id, *argv, 0))
283				invarg("\"id\" value is invalid\n", *argv);
284			rta_addattr64(rta, len, LWTUNNEL_IP6_ID, htonll(id));
285		} else if (strcmp(*argv, "dst") == 0) {
286			inet_prefix addr;
287			NEXT_ARG();
288			if (dst_ok++)
289				duparg2("dst", *argv);
290			get_addr(&addr, *argv, AF_INET6);
291			rta_addattr_l(rta, len, LWTUNNEL_IP6_DST, &addr.data, addr.bytelen);
292		} else if (strcmp(*argv, "tc") == 0) {
293			__u32 tc;
294			NEXT_ARG();
295			if (tos_ok++)
296				duparg2("tc", *argv);
297			if (rtnl_dsfield_a2n(&tc, *argv))
298				invarg("\"tc\" value is invalid\n", *argv);
299			rta_addattr8(rta, len, LWTUNNEL_IP6_TC, tc);
300		} else if (strcmp(*argv, "hoplimit") == 0) {
301			__u8 hoplimit;
302			NEXT_ARG();
303			if (ttl_ok++)
304				duparg2("hoplimit", *argv);
305			if (get_u8(&hoplimit, *argv, 0))
306				invarg("\"hoplimit\" value is invalid\n", *argv);
307			rta_addattr8(rta, len, LWTUNNEL_IP6_HOPLIMIT, hoplimit);
308		} else {
309			break;
310		}
311		argc--; argv++;
312	}
313
314	/* argv is currently the first unparsed argument,
315	 * but the lwt_parse_encap() caller will move to the next,
316	 * so step back */
317	*argcp = argc + 1;
318	*argvp = argv - 1;
319
320	return 0;
321}
322
323int lwt_parse_encap(struct rtattr *rta, size_t len, int *argcp, char ***argvp)
324{
325	struct rtattr *nest;
326	int argc = *argcp;
327	char **argv = *argvp;
328	__u16 type;
329
330	NEXT_ARG();
331	type = read_encap_type(*argv);
332	if (!type)
333		invarg("\"encap type\" value is invalid\n", *argv);
334
335	NEXT_ARG();
336	if (argc <= 1) {
337		fprintf(stderr, "Error: unexpected end of line after \"encap\"\n");
338		exit(-1);
339	}
340
341	nest = rta_nest(rta, 1024, RTA_ENCAP);
342	switch (type) {
343	case LWTUNNEL_ENCAP_MPLS:
344		parse_encap_mpls(rta, len, &argc, &argv);
345		break;
346	case LWTUNNEL_ENCAP_IP:
347		parse_encap_ip(rta, len, &argc, &argv);
348		break;
349	case LWTUNNEL_ENCAP_ILA:
350		parse_encap_ila(rta, len, &argc, &argv);
351		break;
352	case LWTUNNEL_ENCAP_IP6:
353		parse_encap_ip6(rta, len, &argc, &argv);
354		break;
355	default:
356		fprintf(stderr, "Error: unsupported encap type\n");
357		break;
358	}
359	rta_nest_end(rta, nest);
360
361	rta_addattr16(rta, 1024, RTA_ENCAP_TYPE, type);
362
363	*argcp = argc;
364	*argvp = argv;
365
366	return 0;
367}
368