fbb2957fc6057b2603af330a52bffc4a5bc1d607
[oonf.git] / src / subsystems / os_linux / os_tunnel_linux.c
1
2 /*
3  * The olsr.org Optimized Link-State Routing daemon version 2 (olsrd2)
4  * Copyright (c) 2004-2015, the olsr.org team - see HISTORY file
5  * All rights reserved.
6  *
7  * Redistribution and use in source and binary forms, with or without
8  * modification, are permitted provided that the following conditions
9  * are met:
10  *
11  * * Redistributions of source code must retain the above copyright
12  *   notice, this list of conditions and the following disclaimer.
13  * * Redistributions in binary form must reproduce the above copyright
14  *   notice, this list of conditions and the following disclaimer in
15  *   the documentation and/or other materials provided with the
16  *   distribution.
17  * * Neither the name of olsr.org, olsrd nor the names of its
18  *   contributors may be used to endorse or promote products derived
19  *   from this software without specific prior written permission.
20  *
21  * THIS SOFTWARE IS PROVIDED BY THE COPYRIGHT HOLDERS AND CONTRIBUTORS
22  * "AS IS" AND ANY EXPRESS OR IMPLIED WARRANTIES, INCLUDING, BUT NOT
23  * LIMITED TO, THE IMPLIED WARRANTIES OF MERCHANTABILITY AND FITNESS
24  * FOR A PARTICULAR PURPOSE ARE DISCLAIMED. IN NO EVENT SHALL THE
25  * COPYRIGHT OWNER OR CONTRIBUTORS BE LIABLE FOR ANY DIRECT, INDIRECT,
26  * INCIDENTAL, SPECIAL, EXEMPLARY, OR CONSEQUENTIAL DAMAGES (INCLUDING,
27  * BUT NOT LIMITED TO, PROCUREMENT OF SUBSTITUTE GOODS OR SERVICES;
28  * LOSS OF USE, DATA, OR PROFITS; OR BUSINESS INTERRUPTION) HOWEVER
29  * CAUSED AND ON ANY THEORY OF LIABILITY, WHETHER IN CONTRACT, STRICT
30  * LIABILITY, OR TORT (INCLUDING NEGLIGENCE OR OTHERWISE) ARISING IN
31  * ANY WAY OUT OF THE USE OF THIS SOFTWARE, EVEN IF ADVISED OF THE
32  * POSSIBILITY OF SUCH DAMAGE.
33  *
34  * Visit http://www.olsr.org for more information.
35  *
36  * If you find this software useful feel free to make a donation
37  * to the project. For more information see the website or contact
38  * the copyright holders.
39  *
40  */
41
42 /**
43  * @file
44  */
45
46 #define _LINUX_IP_H
47
48 #include <arpa/inet.h>
49 #include <errno.h>
50 //#include <linux/if_tunnel.h>
51 //#include <linux/ip.h>
52 //#include <linux/ip6_tunnel.h>
53 #include <net/if.h>
54 #include <net/if_arp.h>
55 #include <netinet/ip.h>
56 #include <sys/ioctl.h>
57 #include <sys/socket.h>
58 #include <sys/types.h>
59
60 #include <oonf/libcommon/avl.h>
61 #include <oonf/libcommon/avl_comp.h>
62 #include <oonf/oonf.h>
63 #include <oonf/libcommon/netaddr.h>
64 #include <oonf/libcore/oonf_subsystem.h>
65 #include <oonf/subsystems/os_system.h>
66 #include <oonf/subsystems/os_tunnel.h>
67
68 /* Definitions */
69 #define LOG_OS_TUNNEL _oonf_os_tunnel_subsystem.logging
70
71 /*
72  * private copy of linux tunnel definitions to prevent bad mixture of include files
73  */
74
75 #define SIOCADDTUNNEL (SIOCDEVPRIVATE + 1)
76 #define SIOCDELTUNNEL (SIOCDEVPRIVATE + 2)
77
78 #define IP6_TNL_F_USE_ORIG_TCLASS 0x2
79 #define IP6_TNL_F_USE_ORIG_FLOWLABEL 0x4
80
81 struct ip_tunnel_parm {
82   char name[IF_NAMESIZE];
83   int link;
84   uint16_t i_flags;
85   uint16_t o_flags;
86   uint32_t i_key;
87   uint32_t o_key;
88   struct iphdr iph;
89 };
90
91 struct my_ip6_tnl_parm2 {
92   char name[IFNAMSIZ];
93   int link;
94   uint8_t proto;
95   uint8_t encap_limit;
96   uint8_t hop_limit;
97   uint32_t flowinfo;
98   uint32_t flags;
99   struct in6_addr laddr;
100   struct in6_addr raddr;
101
102   uint16_t i_flags;
103   uint16_t o_flags;
104   uint32_t i_key;
105   uint32_t o_key;
106 };
107
108 /* prototypes */
109 static int _init(void);
110 static void _cleanup(void);
111
112 static int _handle_ipv4_tunnel(struct os_tunnel *tunnel, bool add);
113 static int _handle_ipv6_tunnel(struct os_tunnel *tunnel, bool add);
114 static int _handle_tunnel(struct os_tunnel *tunnel, bool add);
115
116 /* subsystem definition */
117 static const char *_dependencies[] = {
118   OONF_OS_SYSTEM_SUBSYSTEM,
119 };
120
121 static struct oonf_subsystem _oonf_os_tunnel_subsystem = {
122   .name = OONF_OS_TUNNEL_SUBSYSTEM,
123   .dependencies = _dependencies,
124   .dependencies_count = ARRAYSIZE(_dependencies),
125   .init = _init,
126   .cleanup = _cleanup,
127 };
128 DECLARE_OONF_PLUGIN(_oonf_os_tunnel_subsystem);
129
130 enum _tunnel_if_type
131 {
132   _TUNNEL_IP_IN_IP,
133   _TUNNEL_IP_IN_IP6,
134   _TUNNEL_IP6_IN_IP,
135   _TUNNEL_IP6_IN_IP6,
136   _TUNNEL_GRE_IN_IP,
137   _TUNNEL_GRE_IN_IP6,
138
139   /* must be last entry */
140   _TUNNEL_IF_TYPE_COUNT,
141 };
142 static const char *_tunnel_base_if[] = {
143   [_TUNNEL_IP_IN_IP] = "tunl0",
144   [_TUNNEL_IP_IN_IP6] = "ip6tnl0",
145   [_TUNNEL_IP6_IN_IP] = "sit0",
146   [_TUNNEL_IP6_IN_IP6] = "ip6tnl0",
147   [_TUNNEL_GRE_IN_IP] = "gre0",
148   [_TUNNEL_GRE_IN_IP6] = "ip6gre0",
149 };
150
151 static bool _tunnel_base_up[_TUNNEL_IF_TYPE_COUNT];
152
153 static struct avl_tree _tunnel_tree;
154
155 /**
156  * Initialize tunnel interface subsystem
157  * @return -1 if an error happened, 0 otherwise
158  */
159 static int
160 _init(void) {
161   avl_init(&_tunnel_tree, avl_comp_strcasecmp, false);
162   memset(_tunnel_base_up, 0, sizeof(_tunnel_base_up));
163   return 0;
164 }
165
166 /**
167  * Cleanup tunnel interface subsystem
168  */
169 static void
170 _cleanup(void) {
171   struct os_tunnel *tunnel, *tunnel_it;
172
173   avl_for_each_element_safe(&_tunnel_tree, tunnel, _node, tunnel_it) {
174     os_tunnel_remove(tunnel);
175   }
176 }
177
178 /**
179  * Add a new tunnel to the kernel system
180  * @param tunnel initialized tunnel data
181  * @return -1 if an error happened, 0 otherwise
182  */
183 int
184 os_tunnel_linux_add(struct os_tunnel *tunnel) {
185   int result;
186
187   if (avl_is_node_added(&tunnel->_node)) {
188     return -1;
189   }
190
191   result = _handle_tunnel(tunnel, true);
192   if (!result) {
193     tunnel->_node.key = tunnel->p.tunnel_if;
194     avl_insert(&_tunnel_tree, &tunnel->_node);
195
196     tunnel->if_index = if_nametoindex(tunnel->p.tunnel_if);
197   }
198   else {
199     tunnel->if_index = 0;
200   }
201   return result;
202 }
203
204 /**
205  * Remove an existing tunnel to the kernel system
206  * @param tunnel initialized tunnel data
207  * @return -1 if an error happened, 0 otherwise
208  */
209 int
210 os_tunnel_linux_remove(struct os_tunnel *tunnel) {
211   int result;
212
213   if (!avl_is_node_added(&tunnel->_node)) {
214     return -1;
215   }
216
217   result = _handle_tunnel(tunnel, false);
218   if (!result) {
219     avl_remove(&_tunnel_tree, &tunnel->_node);
220   }
221   return result;
222 }
223
224 static void
225 _set_base_tunnel_up(enum _tunnel_if_type type) {
226   struct ifreq ifr;
227   int oldflags;
228
229   if (!_tunnel_base_up[type]) {
230     /* make sure base interface is up for incoming tunnel traffic */
231     memset(&ifr, 0, sizeof(ifr));
232     strscpy(ifr.ifr_name, _tunnel_base_if[type], IF_NAMESIZE);
233
234     if (ioctl(os_system_linux_linux_get_ioctl_fd(AF_INET), SIOCGIFFLAGS, &ifr) < 0) {
235       OONF_WARN(LOG_OS_TUNNEL, "ioctl SIOCGIFFLAGS (get flags) error on device %s: %s (%d)\n", _tunnel_base_if[type],
236         strerror(errno), errno);
237       return;
238     }
239
240     oldflags = ifr.ifr_flags;
241     ifr.ifr_flags |= IFF_UP;
242
243     if (oldflags == ifr.ifr_flags) {
244       /* interface is already up/down */
245       return;
246     }
247
248     if (ioctl(os_system_linux_linux_get_ioctl_fd(AF_INET), SIOCSIFFLAGS, &ifr) < 0) {
249       OONF_WARN(LOG_OS_TUNNEL, "ioctl SIOCSIFFLAGS (set flags up) error on device %s: %s (%d)\n", _tunnel_base_if[type],
250         strerror(errno), errno);
251       return;
252     }
253
254     _tunnel_base_up[type] = true;
255   }
256 }
257
258 /**
259  * Add or remove an IPv4 based tunnel
260  * @param tunnel initialized tunnel data
261  * @param add true if tunnel should be added, false for removal
262  * @return -1 if an error happened, 0 otherwise
263  */
264 static int
265 _handle_ipv4_tunnel(struct os_tunnel *tunnel, bool add) {
266   struct ip_tunnel_parm p;
267   enum _tunnel_if_type type;
268   struct ifreq ifr;
269   int err;
270
271   memset(&p, 0, sizeof(p));
272   memset(&ifr, 0, sizeof(ifr));
273
274   p.iph.version = 4;
275   p.iph.ihl = 5;
276   p.iph.frag_off = htons(IP_DF);
277
278   strscpy(p.name, tunnel->p.tunnel_if, IF_NAMESIZE);
279   if (tunnel->p.base_if[0]) {
280     p.link = if_nametoindex(tunnel->p.base_if);
281   }
282   ifr.ifr_ifru.ifru_data = (void *)&p;
283
284   switch (tunnel->p.inner_type) {
285     case OS_TUNNEL_IPV4:
286       p.iph.protocol = IPPROTO_IPIP;
287       type = _TUNNEL_IP_IN_IP;
288       break;
289     case OS_TUNNEL_IPV6:
290       p.iph.protocol = IPPROTO_IPV6;
291       type = _TUNNEL_IP_IN_IP;
292       break;
293     case OS_TUNNEL_GRE:
294       p.iph.protocol = IPPROTO_GRE;
295       type = _TUNNEL_IP_IN_IP;
296       break;
297     default:
298       return -1;
299   }
300
301   /* inherit TTL by default */
302   p.iph.ttl = tunnel->p.tunnel_ttl;
303
304   /* try to inherit TOS */
305   if (tunnel->p.inhert_tos) {
306     p.iph.tos = 1;
307   }
308
309   strscpy(ifr.ifr_name, _tunnel_base_if[type], IF_NAMESIZE);
310
311   netaddr_to_binary(&p.iph.saddr, &tunnel->p.local, sizeof(p.iph.saddr));
312   netaddr_to_binary(&p.iph.daddr, &tunnel->p.remote, sizeof(p.iph.daddr));
313
314   err = ioctl(os_system_linux_linux_get_ioctl_fd(AF_INET), add ? SIOCADDTUNNEL : SIOCDELTUNNEL, &ifr);
315   if (err) {
316     if (add && (errno == EEXIST)) {
317       /* tunnel with this name already exists, try to remove it! */
318       err = ioctl(os_system_linux_linux_get_ioctl_fd(AF_INET), SIOCDELTUNNEL, &ifr);
319       if (err) {
320         OONF_WARN(LOG_OS_TUNNEL, "Error while %s tunnel %s: tunnel already exists and could not be removed",
321           add ? "adding" : "removing", tunnel->p.tunnel_if);
322         return -1;
323       }
324       return _handle_ipv4_tunnel(tunnel, true);
325     }
326     OONF_WARN(LOG_OS_TUNNEL, "Error while %s tunnel %s: %s (%d)", add ? "adding" : "removing", tunnel->p.tunnel_if,
327       strerror(errno), errno);
328     return -1;
329   }
330
331   if (add) {
332     _set_base_tunnel_up(type);
333   }
334   return 0;
335 }
336
337 /**
338  * Add or remove an IPv6 based tunnel
339  * @param tunnel initialized tunnel data
340  * @param add true if tunnel should be added, false for removal
341  * @return -1 if an error happened, 0 otherwise
342  */
343 static int
344 _handle_ipv6_tunnel(struct os_tunnel *tunnel, bool add) {
345   struct my_ip6_tnl_parm2 p;
346   enum _tunnel_if_type type;
347   struct ifreq ifr;
348   int err;
349   struct netaddr_str nbuf1, nbuf2;
350
351   memset(&p, 0, sizeof(p));
352   memset(&ifr, 0, sizeof(ifr));
353
354   ifr.ifr_ifru.ifru_data = (void *)&p;
355   if (tunnel->p.base_if[0]) {
356     p.link = if_nametoindex(tunnel->p.base_if);
357   }
358
359   strscpy(p.name, tunnel->p.tunnel_if, IF_NAMESIZE);
360
361   switch (tunnel->p.inner_type) {
362     case OS_TUNNEL_IPV4:
363       p.proto = IPPROTO_IPIP;
364       type = _TUNNEL_IP_IN_IP6;
365       break;
366     case OS_TUNNEL_IPV6:
367       p.proto = IPPROTO_IPV6;
368       type = _TUNNEL_IP6_IN_IP6;
369       break;
370     case OS_TUNNEL_GRE:
371       p.proto = IPPROTO_GRE;
372       type = _TUNNEL_GRE_IN_IP6;
373       break;
374     default:
375       return -1;
376   }
377
378   /* set tunnel flags */
379   if (tunnel->p.inhert_tos) {
380     p.flags |= IP6_TNL_F_USE_ORIG_TCLASS;
381   }
382   if (tunnel->p.inhert_flowlabel) {
383     p.flags |= IP6_TNL_F_USE_ORIG_FLOWLABEL;
384   }
385   if (tunnel->p.tunnel_ttl) {
386     p.hop_limit = tunnel->p.tunnel_ttl;
387   }
388
389   strscpy(ifr.ifr_name, _tunnel_base_if[type], IF_NAMESIZE);
390
391   netaddr_to_binary(&p.laddr, &tunnel->p.local, sizeof(p.laddr));
392   netaddr_to_binary(&p.raddr, &tunnel->p.remote, sizeof(p.raddr));
393
394   err = ioctl(os_system_linux_linux_get_ioctl_fd(AF_INET6), add ? SIOCADDTUNNEL : SIOCDELTUNNEL, &ifr);
395   if (err) {
396     OONF_WARN(LOG_OS_TUNNEL, "Error while %s tunnel %s (%d,%s,%s): %s (%d)", add ? "add" : "remove",
397       tunnel->p.tunnel_if, tunnel->p.inner_type, netaddr_to_string(&nbuf1, &tunnel->p.local),
398       netaddr_to_string(&nbuf2, &tunnel->p.remote), strerror(errno), errno);
399     return -1;
400   }
401
402   if (add) {
403     _set_base_tunnel_up(type);
404   }
405   return 0;
406 }
407
408 /**
409  * Add or remove a tunnel
410  * @param tunnel initialized tunnel data
411  * @param add true if tunnel should be added, false for removal
412  * @return -1 if an error happened, 0 otherwise
413  */
414 static int
415 _handle_tunnel(struct os_tunnel *tunnel, bool add) {
416   int af_type;
417   struct netaddr_str nbuf1, nbuf2;
418
419   af_type = netaddr_get_address_family(&tunnel->p.local);
420   if (af_type != netaddr_get_address_family(&tunnel->p.remote)) {
421     OONF_WARN(LOG_OS_TUNNEL, "Inconsistent tunnel endpoints for tunnel %s: local=%s remote=%s", tunnel->p.tunnel_if,
422       netaddr_to_string(&nbuf1, &tunnel->p.local), netaddr_to_string(&nbuf2, &tunnel->p.remote));
423     return -1;
424   }
425
426   switch (af_type) {
427     case AF_INET:
428       return _handle_ipv4_tunnel(tunnel, add);
429     case AF_INET6:
430       return _handle_ipv6_tunnel(tunnel, add);
431     default:
432       OONF_WARN(LOG_OS_TUNNEL, "Bad address family for tunnel %s: %u", tunnel->p.tunnel_if, af_type);
433       return -1;
434   }
435 }