1/*
2 * lib/route/link/sit.c        SIT 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 sit SIT
15 * sit link module
16 *
17 * @details
18 * \b Link Type Name: "sit"
19 *
20 * @route_doc{link_sit, SIT 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 SIT_ATTR_LINK          (1 << 0)
35#define SIT_ATTR_LOCAL         (1 << 1)
36#define SIT_ATTR_REMOTE        (1 << 2)
37#define SIT_ATTR_TTL           (1 << 3)
38#define SIT_ATTR_TOS           (1 << 4)
39#define SIT_ATTR_PMTUDISC      (1 << 5)
40#define SIT_ATTR_FLAGS         (1 << 6)
41#define SIT_ATTR_PROTO         (1 << 7)
42
43struct sit_info
44{
45	uint8_t    ttl;
46	uint8_t    tos;
47	uint8_t    pmtudisc;
48	uint8_t    proto;
49	uint16_t   flags;
50	uint32_t   link;
51	uint32_t   local;
52	uint32_t   remote;
53	uint32_t   sit_mask;
54};
55
56static struct nla_policy sit_policy[IFLA_IPTUN_MAX + 1] = {
57	[IFLA_IPTUN_LINK]       = { .type = NLA_U32 },
58	[IFLA_IPTUN_LOCAL]      = { .type = NLA_U32 },
59	[IFLA_IPTUN_REMOTE]     = { .type = NLA_U32 },
60	[IFLA_IPTUN_TTL]        = { .type = NLA_U8 },
61	[IFLA_IPTUN_TOS]        = { .type = NLA_U8 },
62	[IFLA_IPTUN_PMTUDISC]   = { .type = NLA_U8 },
63	[IFLA_IPTUN_FLAGS]      = { .type = NLA_U16 },
64	[IFLA_IPTUN_PROTO]      = { .type = NLA_U8 },
65};
66
67static int sit_alloc(struct rtnl_link *link)
68{
69	struct sit_info *sit;
70
71	sit = calloc(1, sizeof(*sit));
72	if (!sit)
73		return -NLE_NOMEM;
74
75	link->l_info = sit;
76
77	return 0;
78}
79
80static int sit_parse(struct rtnl_link *link, struct nlattr *data,
81		     struct nlattr *xstats)
82{
83	struct nlattr *tb[IFLA_IPTUN_MAX + 1];
84	struct sit_info *sit;
85	int err;
86
87	NL_DBG(3, "Parsing SIT link info");
88
89	err = nla_parse_nested(tb, IFLA_IPTUN_MAX, data, sit_policy);
90	if (err < 0)
91		goto errout;
92
93	err = sit_alloc(link);
94	if (err < 0)
95		goto errout;
96
97	sit = link->l_info;
98
99	if (tb[IFLA_IPTUN_LINK]) {
100		sit->link = nla_get_u32(tb[IFLA_IPTUN_LINK]);
101		sit->sit_mask |= SIT_ATTR_LINK;
102	}
103
104	if (tb[IFLA_IPTUN_LOCAL]) {
105		sit->local = nla_get_u32(tb[IFLA_IPTUN_LOCAL]);
106		sit->sit_mask |= SIT_ATTR_LOCAL;
107	}
108
109	if (tb[IFLA_IPTUN_REMOTE]) {
110		sit->remote = nla_get_u32(tb[IFLA_IPTUN_REMOTE]);
111		sit->sit_mask |= SIT_ATTR_REMOTE;
112	}
113
114	if (tb[IFLA_IPTUN_TTL]) {
115		sit->ttl = nla_get_u8(tb[IFLA_IPTUN_TTL]);
116		sit->sit_mask |= SIT_ATTR_TTL;
117	}
118
119	if (tb[IFLA_IPTUN_TOS]) {
120		sit->tos = nla_get_u8(tb[IFLA_IPTUN_TOS]);
121		sit->sit_mask |= SIT_ATTR_TOS;
122	}
123
124	if (tb[IFLA_IPTUN_PMTUDISC]) {
125		sit->pmtudisc = nla_get_u8(tb[IFLA_IPTUN_PMTUDISC]);
126		sit->sit_mask |= SIT_ATTR_PMTUDISC;
127	}
128
129	if (tb[IFLA_IPTUN_FLAGS]) {
130		sit->flags = nla_get_u16(tb[IFLA_IPTUN_FLAGS]);
131		sit->sit_mask |= SIT_ATTR_FLAGS;
132	}
133
134	if (tb[IFLA_IPTUN_PROTO]) {
135		sit->proto = nla_get_u8(tb[IFLA_IPTUN_PROTO]);
136		sit->sit_mask |= SIT_ATTR_PROTO;
137	}
138
139	err = 0;
140
141 errout:
142	return err;
143}
144
145static int sit_put_attrs(struct nl_msg *msg, struct rtnl_link *link)
146{
147	struct sit_info *sit = link->l_info;
148	struct nlattr *data;
149
150	data = nla_nest_start(msg, IFLA_INFO_DATA);
151	if (!data)
152		return -NLE_MSGSIZE;
153
154	if (sit->sit_mask & SIT_ATTR_LINK)
155		NLA_PUT_U32(msg, IFLA_IPTUN_LINK, sit->link);
156
157	if (sit->sit_mask & SIT_ATTR_LOCAL)
158		NLA_PUT_U32(msg, IFLA_IPTUN_LOCAL, sit->local);
159
160	if (sit->sit_mask & SIT_ATTR_REMOTE)
161		NLA_PUT_U32(msg, IFLA_IPTUN_REMOTE, sit->remote);
162
163	if (sit->sit_mask & SIT_ATTR_TTL)
164		NLA_PUT_U8(msg, IFLA_IPTUN_TTL, sit->ttl);
165
166	if (sit->sit_mask & SIT_ATTR_TOS)
167		NLA_PUT_U8(msg, IFLA_IPTUN_TOS, sit->tos);
168
169	if (sit->sit_mask & SIT_ATTR_PMTUDISC)
170		NLA_PUT_U8(msg, IFLA_IPTUN_PMTUDISC, sit->pmtudisc);
171
172	if (sit->sit_mask & SIT_ATTR_FLAGS)
173		NLA_PUT_U16(msg, IFLA_IPTUN_FLAGS, sit->flags);
174
175	if (sit->sit_mask & SIT_ATTR_PROTO)
176		NLA_PUT_U8(msg, IFLA_IPTUN_PROTO, sit->proto);
177
178	nla_nest_end(msg, data);
179
180nla_put_failure:
181
182	return 0;
183}
184
185static void sit_free(struct rtnl_link *link)
186{
187	struct sit_info *sit = link->l_info;
188
189	free(sit);
190	link->l_info = NULL;
191}
192
193static void sit_dump_line(struct rtnl_link *link, struct nl_dump_params *p)
194{
195	nl_dump(p, "sit : %s", link->l_name);
196}
197
198static void sit_dump_details(struct rtnl_link *link, struct nl_dump_params *p)
199{
200	struct sit_info *sit = link->l_info;
201	char *name, addr[INET_ADDRSTRLEN];
202
203	if (sit->sit_mask & SIT_ATTR_LINK) {
204		nl_dump(p, "      link ");
205		name = rtnl_link_get_name(link);
206		if (name)
207			nl_dump_line(p, "%s\n", name);
208		else
209			nl_dump_line(p, "%u\n", sit->link);
210	}
211
212	if (sit->sit_mask & SIT_ATTR_LOCAL) {
213		nl_dump(p, "      local ");
214		if(inet_ntop(AF_INET, &sit->local, addr, sizeof(addr)))
215			nl_dump_line(p, "%s\n", addr);
216		else
217			nl_dump_line(p, "%#x\n", ntohs(sit->local));
218	}
219
220	if (sit->sit_mask & SIT_ATTR_REMOTE) {
221		nl_dump(p, "      remote ");
222		if(inet_ntop(AF_INET, &sit->remote, addr, sizeof(addr)))
223			nl_dump_line(p, "%s\n", addr);
224		else
225			nl_dump_line(p, "%#x\n", ntohs(sit->remote));
226	}
227
228	if (sit->sit_mask & SIT_ATTR_TTL) {
229		nl_dump(p, "      ttl ");
230		nl_dump_line(p, "%u\n", sit->ttl);
231	}
232
233	if (sit->sit_mask & SIT_ATTR_TOS) {
234		nl_dump(p, "      tos ");
235		nl_dump_line(p, "%u\n", sit->tos);
236	}
237
238	if (sit->sit_mask & SIT_ATTR_FLAGS) {
239		nl_dump(p, "      flags ");
240		nl_dump_line(p, " (%x)\n", sit->flags);
241	}
242
243	if (sit->sit_mask & SIT_ATTR_PROTO) {
244		nl_dump(p, "    proto   ");
245		nl_dump_line(p, " (%x)\n", sit->proto);
246	}
247}
248
249static int sit_clone(struct rtnl_link *dst, struct rtnl_link *src)
250{
251	struct sit_info *sit_dst, *sit_src = src->l_info;
252	int err;
253
254	dst->l_info = NULL;
255
256	err = rtnl_link_set_type(dst, "sit");
257	if (err < 0)
258		return err;
259
260	sit_dst = dst->l_info;
261
262	if (!sit_dst || !sit_src)
263		return -NLE_NOMEM;
264
265	memcpy(sit_dst, sit_src, sizeof(struct sit_info));
266
267	return 0;
268}
269
270static struct rtnl_link_info_ops sit_info_ops = {
271	.io_name                = "sit",
272	.io_alloc               = sit_alloc,
273	.io_parse               = sit_parse,
274	.io_dump = {
275		[NL_DUMP_LINE]  = sit_dump_line,
276		[NL_DUMP_DETAILS] = sit_dump_details,
277	},
278	.io_clone               = sit_clone,
279	.io_put_attrs           = sit_put_attrs,
280	.io_free                = sit_free,
281};
282
283#define IS_SIT_LINK_ASSERT(link)                                           \
284        if ((link)->l_info_ops != &sit_info_ops) {                         \
285                APPBUG("Link is not a sit link. set type \"sit\" first."); \
286                return -NLE_OPNOTSUPP;                                     \
287        }
288
289struct rtnl_link *rtnl_link_sit_alloc(void)
290{
291	struct rtnl_link *link;
292	int err;
293
294	link = rtnl_link_alloc();
295	if (!link)
296		return NULL;
297
298	err = rtnl_link_set_type(link, "sit");
299	if (err < 0) {
300		rtnl_link_put(link);
301		return NULL;
302	}
303
304	return link;
305}
306
307/**
308 * Check if link is a SIT link
309 * @arg link            Link object
310 *
311 * @return True if link is a SIT link, otherwise false is returned.
312 */
313int rtnl_link_is_sit(struct rtnl_link *link)
314{
315	return link->l_info_ops && !strcmp(link->l_info_ops->io_name, "sit");
316}
317
318/**
319 * Create a new sit tunnel device
320 * @arg sock            netlink socket
321 * @arg name            name of the tunnel device
322 *
323 * Creates a new sit tunnel device in the kernel
324 * @return 0 on success or a negative error code
325 */
326int rtnl_link_sit_add(struct nl_sock *sk, const char *name)
327{
328	struct rtnl_link *link;
329	int err;
330
331	link = rtnl_link_sit_alloc();
332	if (!link)
333		return -NLE_NOMEM;
334
335	if(name)
336		rtnl_link_set_name(link, name);
337
338	err = rtnl_link_add(sk, link, NLM_F_CREATE);
339	rtnl_link_put(link);
340
341	return err;
342}
343
344/**
345 * Set SIT tunnel interface index
346 * @arg link            Link object
347 * @arg index           interface index
348 *
349 * @return 0 on success or a negative error code
350 */
351int rtnl_link_sit_set_link(struct rtnl_link *link,  uint32_t index)
352{
353	struct sit_info *sit = link->l_info;
354
355	IS_SIT_LINK_ASSERT(link);
356
357	sit->link = index;
358	sit->sit_mask |= SIT_ATTR_LINK;
359
360	return 0;
361}
362
363/**
364 * Get SIT tunnel interface index
365 * @arg link            Link object
366 *
367 * @return interface index value
368 */
369uint32_t rtnl_link_sit_get_link(struct rtnl_link *link)
370{
371	struct sit_info *sit = link->l_info;
372
373	IS_SIT_LINK_ASSERT(link);
374
375	return sit->link;
376}
377
378/**
379 * Set SIT tunnel local address
380 * @arg link            Link object
381 * @arg addr            local address
382 *
383 * @return 0 on success or a negative error code
384 */
385int rtnl_link_sit_set_local(struct rtnl_link *link, uint32_t addr)
386{
387	struct sit_info *sit = link->l_info;
388
389	IS_SIT_LINK_ASSERT(link);
390
391	sit->local = addr;
392	sit->sit_mask |= SIT_ATTR_LOCAL;
393
394	return 0;
395}
396
397/**
398 * Get SIT tunnel local address
399 * @arg link            Link object
400 *
401 * @return local address value
402 */
403uint32_t rtnl_link_sit_get_local(struct rtnl_link *link)
404{
405	struct sit_info *sit = link->l_info;
406
407	IS_SIT_LINK_ASSERT(link);
408
409	return sit->local;
410}
411
412/**
413 * Set SIT tunnel remote address
414 * @arg link            Link object
415 * @arg remote          remote address
416 *
417 * @return 0 on success or a negative error code
418 */
419int rtnl_link_sit_set_remote(struct rtnl_link *link, uint32_t addr)
420{
421	struct sit_info *sit = link->l_info;
422
423	IS_SIT_LINK_ASSERT(link);
424
425	sit->remote = addr;
426	sit->sit_mask |= SIT_ATTR_REMOTE;
427
428	return 0;
429}
430
431/**
432 * Get SIT tunnel remote address
433 * @arg link            Link object
434 *
435 * @return remote address
436 */
437uint32_t rtnl_link_sit_get_remote(struct rtnl_link *link)
438{
439	struct sit_info *sit = link->l_info;
440
441	IS_SIT_LINK_ASSERT(link);
442
443	return sit->remote;
444}
445
446/**
447 * Set SIT tunnel ttl
448 * @arg link            Link object
449 * @arg ttl             tunnel ttl
450 *
451 * @return 0 on success or a negative error code
452 */
453int rtnl_link_sit_set_ttl(struct rtnl_link *link, uint8_t ttl)
454{
455	struct sit_info *sit = link->l_info;
456
457	IS_SIT_LINK_ASSERT(link);
458
459	sit->ttl = ttl;
460	sit->sit_mask |= SIT_ATTR_TTL;
461
462	return 0;
463}
464
465/**
466 * Get SIT tunnel ttl
467 * @arg link            Link object
468 *
469 * @return ttl value
470 */
471uint8_t rtnl_link_sit_get_ttl(struct rtnl_link *link)
472{
473	struct sit_info *sit = link->l_info;
474
475	IS_SIT_LINK_ASSERT(link);
476
477	return sit->ttl;
478}
479
480/**
481 * Set SIT tunnel tos
482 * @arg link            Link object
483 * @arg tos             tunnel tos
484 *
485 * @return 0 on success or a negative error code
486 */
487int rtnl_link_sit_set_tos(struct rtnl_link *link, uint8_t tos)
488{
489	struct sit_info *sit = link->l_info;
490
491	IS_SIT_LINK_ASSERT(link);
492
493	sit->tos = tos;
494	sit->sit_mask |= SIT_ATTR_TOS;
495
496	return 0;
497}
498
499/**
500 * Get SIT tunnel tos
501 * @arg link            Link object
502 *
503 * @return tos value
504 */
505uint8_t rtnl_link_sit_get_tos(struct rtnl_link *link)
506{
507	struct sit_info *sit = link->l_info;
508
509	IS_SIT_LINK_ASSERT(link);
510
511	return sit->tos;
512}
513
514/**
515 * Set SIT tunnel path MTU discovery
516 * @arg link            Link object
517 * @arg pmtudisc        path MTU discovery
518 *
519 * @return 0 on success or a negative error code
520 */
521int rtnl_link_sit_set_pmtudisc(struct rtnl_link *link, uint8_t pmtudisc)
522{
523	struct sit_info *sit = link->l_info;
524
525	IS_SIT_LINK_ASSERT(link);
526
527	sit->pmtudisc = pmtudisc;
528	sit->sit_mask |= SIT_ATTR_PMTUDISC;
529
530	return 0;
531}
532
533/**
534 * Get SIT path MTU discovery
535 * @arg link            Link object
536 *
537 * @return pmtudisc value
538 */
539uint8_t rtnl_link_sit_get_pmtudisc(struct rtnl_link *link)
540{
541	struct sit_info *sit = link->l_info;
542
543	IS_SIT_LINK_ASSERT(link);
544
545	return sit->pmtudisc;
546}
547
548/**
549 * Set SIT tunnel flags
550 * @arg link            Link object
551 * @arg flags           tunnel flags
552 *
553 * @return 0 on success or a negative error code
554 */
555int rtnl_link_sit_set_flags(struct rtnl_link *link, uint16_t flags)
556{
557	struct sit_info *sit = link->l_info;
558
559	IS_SIT_LINK_ASSERT(link);
560
561	sit->flags = flags;
562	sit->sit_mask |= SIT_ATTR_FLAGS;
563
564	return 0;
565}
566
567/**
568 * Get SIT path flags
569 * @arg link            Link object
570 *
571 * @return flags value
572 */
573uint16_t rtnl_link_sit_get_flags(struct rtnl_link *link)
574{
575	struct sit_info *sit = link->l_info;
576
577	IS_SIT_LINK_ASSERT(link);
578
579	return sit->flags;
580}
581
582/**
583 * Set SIT tunnel proto
584 * @arg link            Link object
585 * @arg proto           tunnel proto
586 *
587 * @return 0 on success or a negative error code
588 */
589int rtnl_link_sit_set_proto(struct rtnl_link *link, uint8_t proto)
590{
591	struct sit_info *sit = link->l_info;
592
593	IS_SIT_LINK_ASSERT(link);
594
595	sit->proto = proto;
596	sit->sit_mask |= SIT_ATTR_PROTO;
597
598	return 0;
599}
600
601/**
602 * Get SIT proto
603 * @arg link            Link object
604 *
605 * @return proto value
606 */
607uint8_t rtnl_link_sit_get_proto(struct rtnl_link *link)
608{
609	struct sit_info *sit = link->l_info;
610
611	IS_SIT_LINK_ASSERT(link);
612
613	return sit->proto;
614}
615
616static void __init sit_init(void)
617{
618	rtnl_link_register_info(&sit_info_ops);
619}
620
621static void __exit sit_exit(void)
622{
623	rtnl_link_unregister_info(&sit_info_ops);
624}
625