1/*
2 * lib/route/link/ipip.c        IPIP Link Info
3 *
4 *      This library is free software; you can redistribute it and/or
5 *      modify it under the terms of the GNU Lesser General Public
6 *      License as published by the Free Software Foundation version 2.1
7 *      of the License.
8 *
9 * Copyright (c) 2014 Susant Sahani <susant@redhat.com>
10 */
11
12/**
13 * @ingroup link
14 * @defgroup ipip IPIP
15 * ipip link module
16 *
17 * @details
18 * \b Link Type Name: "ipip"
19 *
20 * @route_doc{link_ipip, IPIP Documentation}
21 *
22 * @{
23 */
24
25#include <netlink-private/netlink.h>
26#include <netlink/netlink.h>
27#include <netlink/attr.h>
28#include <netlink/utils.h>
29#include <netlink/object.h>
30#include <netlink/route/rtnl.h>
31#include <netlink-private/route/link/api.h>
32#include <linux/if_tunnel.h>
33
34#define IPIP_ATTR_LINK          (1 << 0)
35#define IPIP_ATTR_LOCAL         (1 << 1)
36#define IPIP_ATTR_REMOTE        (1 << 2)
37#define IPIP_ATTR_TTL           (1 << 3)
38#define IPIP_ATTR_TOS           (1 << 4)
39#define IPIP_ATTR_PMTUDISC      (1 << 5)
40
41struct ipip_info
42{
43	uint8_t    ttl;
44	uint8_t    tos;
45	uint8_t    pmtudisc;
46	uint32_t   link;
47	uint32_t   local;
48	uint32_t   remote;
49	uint32_t   ipip_mask;
50};
51
52static struct nla_policy ipip_policy[IFLA_IPTUN_MAX + 1] = {
53	[IFLA_IPTUN_LINK]       = { .type = NLA_U32 },
54	[IFLA_IPTUN_LOCAL]      = { .type = NLA_U32 },
55	[IFLA_IPTUN_REMOTE]     = { .type = NLA_U32 },
56	[IFLA_IPTUN_TTL]        = { .type = NLA_U8 },
57	[IFLA_IPTUN_TOS]        = { .type = NLA_U8 },
58	[IFLA_IPTUN_PMTUDISC]   = { .type = NLA_U8 },
59};
60
61static int ipip_alloc(struct rtnl_link *link)
62{
63	struct ipip_info *ipip;
64
65	ipip = calloc(1, sizeof(*ipip));
66	if (!ipip)
67		return -NLE_NOMEM;
68
69	link->l_info = ipip;
70
71	return 0;
72}
73
74static int ipip_parse(struct rtnl_link *link, struct nlattr *data,
75                      struct nlattr *xstats)
76{
77	struct nlattr *tb[IFLA_IPTUN_MAX + 1];
78	struct ipip_info *ipip;
79	int err;
80
81	NL_DBG(3, "Parsing IPIP link info");
82
83	err = nla_parse_nested(tb, IFLA_IPTUN_MAX, data, ipip_policy);
84	if (err < 0)
85		goto errout;
86
87	err = ipip_alloc(link);
88	if (err < 0)
89		goto errout;
90
91	ipip = link->l_info;
92
93	if (tb[IFLA_IPTUN_LINK]) {
94		ipip->link = nla_get_u32(tb[IFLA_IPTUN_LINK]);
95		ipip->ipip_mask |= IPIP_ATTR_LINK;
96	}
97
98	if (tb[IFLA_IPTUN_LOCAL]) {
99		ipip->local = nla_get_u32(tb[IFLA_IPTUN_LOCAL]);
100		ipip->ipip_mask |= IPIP_ATTR_LOCAL;
101	}
102
103	if (tb[IFLA_IPTUN_REMOTE]) {
104		ipip->remote = nla_get_u32(tb[IFLA_IPTUN_REMOTE]);
105		ipip->ipip_mask |= IPIP_ATTR_REMOTE;
106	}
107
108	if (tb[IFLA_IPTUN_TTL]) {
109		ipip->ttl = nla_get_u8(tb[IFLA_IPTUN_TTL]);
110		ipip->ipip_mask |= IPIP_ATTR_TTL;
111	}
112
113	if (tb[IFLA_IPTUN_TOS]) {
114		ipip->tos = nla_get_u8(tb[IFLA_IPTUN_TOS]);
115		ipip->ipip_mask |= IPIP_ATTR_TOS;
116	}
117
118	if (tb[IFLA_IPTUN_PMTUDISC]) {
119		ipip->pmtudisc = nla_get_u8(tb[IFLA_IPTUN_PMTUDISC]);
120		ipip->ipip_mask |= IPIP_ATTR_PMTUDISC;
121	}
122
123	err = 0;
124
125errout:
126	return err;
127}
128
129static int ipip_put_attrs(struct nl_msg *msg, struct rtnl_link *link)
130{
131	struct ipip_info *ipip = link->l_info;
132	struct nlattr *data;
133
134	data = nla_nest_start(msg, IFLA_INFO_DATA);
135	if (!data)
136		return -NLE_MSGSIZE;
137
138	if (ipip->ipip_mask & IPIP_ATTR_LINK)
139		NLA_PUT_U32(msg, IFLA_IPTUN_LINK, ipip->link);
140
141	if (ipip->ipip_mask & IPIP_ATTR_LOCAL)
142		NLA_PUT_U32(msg, IFLA_IPTUN_LOCAL, ipip->local);
143
144	if (ipip->ipip_mask & IPIP_ATTR_REMOTE)
145		NLA_PUT_U32(msg, IFLA_IPTUN_REMOTE, ipip->remote);
146
147	if (ipip->ipip_mask & IPIP_ATTR_TTL)
148		NLA_PUT_U8(msg, IFLA_IPTUN_TTL, ipip->ttl);
149
150	if (ipip->ipip_mask & IPIP_ATTR_TOS)
151		NLA_PUT_U8(msg, IFLA_IPTUN_TOS, ipip->tos);
152
153	if (ipip->ipip_mask & IPIP_ATTR_PMTUDISC)
154		NLA_PUT_U8(msg, IFLA_IPTUN_PMTUDISC, ipip->pmtudisc);
155
156	nla_nest_end(msg, data);
157
158nla_put_failure:
159	return 0;
160}
161
162static void ipip_free(struct rtnl_link *link)
163{
164	struct ipip_info *ipip = link->l_info;
165
166	free(ipip);
167	link->l_info = NULL;
168}
169
170static void ipip_dump_line(struct rtnl_link *link, struct nl_dump_params *p)
171{
172	nl_dump(p, "ipip : %s", link->l_name);
173}
174
175static void ipip_dump_details(struct rtnl_link *link, struct nl_dump_params *p)
176{
177	struct ipip_info *ipip = link->l_info;
178	char *name, addr[INET_ADDRSTRLEN];
179
180	if (ipip->ipip_mask & IPIP_ATTR_LINK) {
181		nl_dump(p, "      link ");
182		name = rtnl_link_get_name(link);
183		if (name)
184			nl_dump_line(p, "%s\n", name);
185		else
186			nl_dump_line(p, "%u\n", ipip->link);
187	}
188
189	if (ipip->ipip_mask & IPIP_ATTR_LOCAL) {
190		nl_dump(p, "      local ");
191		if(inet_ntop(AF_INET, &ipip->local, addr, sizeof(addr)))
192			nl_dump_line(p, "%s\n", addr);
193		else
194			nl_dump_line(p, "%#x\n", ntohs(ipip->local));
195	}
196
197	if (ipip->ipip_mask & IPIP_ATTR_REMOTE) {
198		nl_dump(p, "      remote ");
199		if(inet_ntop(AF_INET, &ipip->remote, addr, sizeof(addr)))
200			nl_dump_line(p, "%s\n", addr);
201		else
202			nl_dump_line(p, "%#x\n", ntohs(ipip->remote));
203	}
204
205	if (ipip->ipip_mask & IPIP_ATTR_TTL) {
206		nl_dump(p, "      ttl ");
207		nl_dump_line(p, "%u\n", ipip->ttl);
208	}
209
210	if (ipip->ipip_mask & IPIP_ATTR_TOS) {
211		nl_dump(p, "      tos ");
212		nl_dump_line(p, "%u\n", ipip->tos);
213	}
214
215	if (ipip->ipip_mask & IPIP_ATTR_PMTUDISC) {
216		nl_dump(p, "      pmtudisc ");
217		nl_dump_line(p, "enabled (%#x)\n", ipip->pmtudisc);
218	}
219}
220
221static int ipip_clone(struct rtnl_link *dst, struct rtnl_link *src)
222{
223	struct ipip_info *ipip_dst, *ipip_src = src->l_info;
224	int err;
225
226	dst->l_info = NULL;
227
228	err = rtnl_link_set_type(dst, "ipip");
229	if (err < 0)
230		return err;
231
232	ipip_dst = dst->l_info;
233
234	if (!ipip_dst || !ipip_src)
235		BUG();
236
237	memcpy(ipip_dst, ipip_src, sizeof(struct ipip_info));
238
239	return 0;
240}
241
242static struct rtnl_link_info_ops ipip_info_ops = {
243	.io_name                = "ipip",
244	.io_alloc               = ipip_alloc,
245	.io_parse               = ipip_parse,
246	.io_dump = {
247		[NL_DUMP_LINE]  = ipip_dump_line,
248		[NL_DUMP_DETAILS] = ipip_dump_details,
249	},
250	.io_clone               = ipip_clone,
251	.io_put_attrs           = ipip_put_attrs,
252	.io_free                = ipip_free,
253};
254
255#define IS_IPIP_LINK_ASSERT(link)                                            \
256        if ((link)->l_info_ops != &ipip_info_ops) {                          \
257                APPBUG("Link is not a ipip link. set type \"ipip\" first."); \
258                return -NLE_OPNOTSUPP;                                       \
259        }
260
261struct rtnl_link *rtnl_link_ipip_alloc(void)
262{
263	struct rtnl_link *link;
264	int err;
265
266	link = rtnl_link_alloc();
267	if (!link)
268		return NULL;
269
270	err = rtnl_link_set_type(link, "ipip");
271	if (err < 0) {
272		rtnl_link_put(link);
273		return NULL;
274	}
275
276	return link;
277}
278
279/**
280 * Check if link is a IPIP link
281 * @arg link            Link object
282 *
283 * @return True if link is a IPIP link, otherwise false is returned.
284 */
285int rtnl_link_is_ipip(struct rtnl_link *link)
286{
287	return link->l_info_ops && !strcmp(link->l_info_ops->io_name, "ipip");
288}
289
290/**
291 * Create a new ipip tunnel device
292 * @arg sock            netlink socket
293 * @arg name            name of the tunnel deviceL
294 *
295 * Creates a new ipip tunnel device in the kernel
296 * @return 0 on success or a negative error code
297 */
298int rtnl_link_ipip_add(struct nl_sock *sk, const char *name)
299{
300	struct rtnl_link *link;
301	int err;
302
303	link = rtnl_link_ipip_alloc();
304	if (!link)
305		return -NLE_NOMEM;
306
307	if(name)
308		rtnl_link_set_name(link, name);
309
310	err = rtnl_link_add(sk, link, NLM_F_CREATE);
311	rtnl_link_put(link);
312
313	return err;
314}
315
316/**
317 * Set IPIP tunnel interface index
318 * @arg link            Link object
319 * @arg index           interface index
320 *
321 * @return 0 on success or a negative error code
322 */
323int rtnl_link_ipip_set_link(struct rtnl_link *link,  uint32_t index)
324{
325	struct ipip_info *ipip = link->l_info;
326
327	IS_IPIP_LINK_ASSERT(link);
328
329	ipip->link = index;
330	ipip->ipip_mask |= IPIP_ATTR_LINK;
331
332	return 0;
333}
334
335/**
336 * Get IPIP tunnel interface index
337 * @arg link            Link object
338 *
339 * @return interface index value
340 */
341uint32_t rtnl_link_ipip_get_link(struct rtnl_link *link)
342{
343	struct ipip_info *ipip = link->l_info;
344
345	IS_IPIP_LINK_ASSERT(link);
346
347	return ipip->link;
348}
349
350/**
351 * Set IPIP tunnel local address
352 * @arg link            Link object
353 * @arg addr            local address
354 *
355 * @return 0 on success or a negative error code
356 */
357int rtnl_link_ipip_set_local(struct rtnl_link *link, uint32_t addr)
358{
359	struct ipip_info *ipip = link->l_info;
360
361	IS_IPIP_LINK_ASSERT(link);
362
363	ipip->local = addr;
364	ipip->ipip_mask |= IPIP_ATTR_LOCAL;
365
366	return 0;
367}
368
369/**
370 * Get IPIP tunnel local address
371 * @arg link            Link object
372 *
373 * @return local address value
374 */
375uint32_t rtnl_link_ipip_get_local(struct rtnl_link *link)
376{
377	struct ipip_info *ipip = link->l_info;
378
379	IS_IPIP_LINK_ASSERT(link);
380
381	return ipip->local;
382}
383
384/**
385 * Set IPIP tunnel remote address
386 * @arg link            Link object
387 * @arg remote          remote address
388 *
389 * @return 0 on success or a negative error code
390 */
391int rtnl_link_ipip_set_remote(struct rtnl_link *link, uint32_t addr)
392{
393	struct ipip_info *ipip = link->l_info;
394
395	IS_IPIP_LINK_ASSERT(link);
396
397	ipip->remote = addr;
398	ipip->ipip_mask |= IPIP_ATTR_REMOTE;
399
400	return 0;
401}
402
403/**
404 * Get IPIP tunnel remote address
405 * @arg link            Link object
406 *
407 * @return remote address
408 */
409uint32_t rtnl_link_ipip_get_remote(struct rtnl_link *link)
410{
411	struct ipip_info *ipip = link->l_info;
412
413	IS_IPIP_LINK_ASSERT(link);
414
415	return ipip->remote;
416}
417
418/**
419 * Set IPIP tunnel ttl
420 * @arg link            Link object
421 * @arg ttl             tunnel ttl
422 *
423 * @return 0 on success or a negative error code
424 */
425int rtnl_link_ipip_set_ttl(struct rtnl_link *link, uint8_t ttl)
426{
427	struct ipip_info *ipip = link->l_info;
428
429	IS_IPIP_LINK_ASSERT(link);
430
431	ipip->ttl = ttl;
432	ipip->ipip_mask |= IPIP_ATTR_TTL;
433
434	return 0;
435}
436
437/**
438 * Get IPIP tunnel ttl
439 * @arg link            Link object
440 *
441 * @return ttl value
442 */
443uint8_t rtnl_link_ipip_get_ttl(struct rtnl_link *link)
444{
445	struct ipip_info *ipip = link->l_info;
446
447	IS_IPIP_LINK_ASSERT(link);
448
449	return ipip->ttl;
450}
451
452/**
453 * Set IPIP tunnel tos
454 * @arg link            Link object
455 * @arg tos             tunnel tos
456 *
457 * @return 0 on success or a negative error code
458 */
459int rtnl_link_ipip_set_tos(struct rtnl_link *link, uint8_t tos)
460{
461	struct ipip_info *ipip = link->l_info;
462
463	IS_IPIP_LINK_ASSERT(link);
464
465	ipip->tos = tos;
466	ipip->ipip_mask |= IPIP_ATTR_TOS;
467
468	return 0;
469}
470
471/**
472 * Get IPIP tunnel tos
473 * @arg link            Link object
474 *
475 * @return tos value
476 */
477uint8_t rtnl_link_ipip_get_tos(struct rtnl_link *link)
478{
479	struct ipip_info *ipip = link->l_info;
480
481	IS_IPIP_LINK_ASSERT(link);
482
483	return ipip->tos;
484}
485
486/**
487 * Set IPIP tunnel path MTU discovery
488 * @arg link            Link object
489 * @arg pmtudisc        path MTU discovery
490 *
491 * @return 0 on success or a negative error code
492 */
493int rtnl_link_ipip_set_pmtudisc(struct rtnl_link *link, uint8_t pmtudisc)
494{
495	struct ipip_info *ipip = link->l_info;
496
497	IS_IPIP_LINK_ASSERT(link);
498
499	ipip->pmtudisc = pmtudisc;
500	ipip->ipip_mask |= IPIP_ATTR_PMTUDISC;
501
502	return 0;
503}
504
505/**
506 * Get IPIP path MTU discovery
507 * @arg link            Link object
508 *
509 * @return pmtudisc value
510 */
511uint8_t rtnl_link_ipip_get_pmtudisc(struct rtnl_link *link)
512{
513	struct ipip_info *ipip = link->l_info;
514
515	IS_IPIP_LINK_ASSERT(link);
516
517	return ipip->pmtudisc;
518}
519
520static void __init ipip_init(void)
521{
522	rtnl_link_register_info(&ipip_info_ops);
523}
524
525static void __exit ipip_exit(void)
526{
527	rtnl_link_unregister_info(&ipip_info_ops);
528}
529