f860cc331125de9c538e3a0422f66d855d99115f
[olsrd.git] / src / linux / kernel_tunnel.c
1 /*
2  * The olsr.org Optimized Link-State Routing daemon(olsrd)
3  * Copyright (c) 2004, Andreas Tonnesen(andreto@olsr.org)
4  * Copyright (c) 2007, Sven-Ola for the policy routing stuff
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 #ifdef __linux__
43
44 #include "kernel_tunnel.h"
45 #include "kernel_routes.h"
46 #include "log.h"
47 #include "olsr_types.h"
48 #include "net_os.h"
49 #include "olsr_cookie.h"
50 #include "ipcalc.h"
51
52 #include <assert.h>
53
54 //ipip includes
55 #include <arpa/inet.h>
56 #include <netinet/in.h>
57 #include <sys/ioctl.h>
58 #include <net/if.h>
59 #include <linux/ip.h>
60 #include <linux/if_tunnel.h>
61 #include <linux/version.h>
62
63 #if !defined LINUX_VERSION_CODE || !defined KERNEL_VERSION
64   #error "Both LINUX_VERSION_CODE and KERNEL_VERSION need to be defined"
65 #endif /* !defined LINUX_VERSION_CODE || !defined KERNEL_VERSION */
66
67 #if LINUX_VERSION_CODE >= KERNEL_VERSION(2,6,24)
68   #define LINUX_IPV6_TUNNEL
69 #endif /* LINUX_VERSION_CODE >= KERNEL_VERSION(2,6,24) */
70
71 #ifdef LINUX_IPV6_TUNNEL
72 #include <linux/ip6_tunnel.h>
73 #endif /* LINUX_IPV6_TUNNEL */
74
75 //ifup includes
76 #include <sys/socket.h>
77 #include <sys/ioctl.h>
78 #include <sys/types.h>
79 #include <net/if.h>
80
81 static bool store_iptunnel_state;
82 static struct olsr_cookie_info *tunnel_cookie;
83 static struct avl_tree tunnel_tree;
84
85 int olsr_os_init_iptunnel(const char * dev) {
86   tunnel_cookie = olsr_alloc_cookie("iptunnel", OLSR_COOKIE_TYPE_MEMORY);
87   olsr_cookie_set_memory_size(tunnel_cookie, sizeof(struct olsr_iptunnel_entry));
88   avl_init(&tunnel_tree, avl_comp_default);
89
90   store_iptunnel_state = olsr_if_isup(dev);
91   if (store_iptunnel_state) {
92     return 0;
93   }
94   if (olsr_if_set_state(dev, true)) {
95     return -1;
96   }
97
98   return olsr_os_ifip(if_nametoindex(dev), &olsr_cnf->main_addr, true);
99 }
100
101 void olsr_os_cleanup_iptunnel(const char * dev) {
102   while (tunnel_tree.count > 0) {
103     struct olsr_iptunnel_entry *t;
104
105     /* kill tunnel */
106     t = (struct olsr_iptunnel_entry *)avl_walk_first(&tunnel_tree);
107     t->usage = 1;
108
109     olsr_os_del_ipip_tunnel(t);
110   }
111   if (!store_iptunnel_state) {
112     olsr_if_set_state(dev, false);
113   }
114
115   olsr_free_cookie(tunnel_cookie);
116 }
117
118 /**
119  * creates an ipip tunnel (for ipv4 or ipv6)
120  *
121  * @param name interface name
122  * @param target pointer to tunnel target IP, NULL if tunnel should be removed.
123  * Must be of type 'in_addr_t *' for ipv4 and of type 'struct in6_addr *' for
124  * ipv6
125  * @return 0 if an error happened,
126  *   if_index for successful created tunnel, 1 for successful deleted tunnel
127  */
128 static int os_ip_tunnel(const char *name, void *target) {
129         struct ifreq ifr;
130         int err;
131         void * p;
132         char buffer[INET6_ADDRSTRLEN];
133         struct ip_tunnel_parm p4;
134 #ifdef LINUX_IPV6_TUNNEL
135         struct ip6_tnl_parm p6;
136 #endif /* LINUX_IPV6_TUNNEL */
137
138         assert (name != NULL);
139
140         memset(&ifr, 0, sizeof(ifr));
141
142         if (olsr_cnf->ip_version == AF_INET) {
143                 p = &p4;
144
145                 memset(&p4, 0, sizeof(p4));
146                 p4.iph.version = 4;
147                 p4.iph.ihl = 5;
148                 p4.iph.ttl = 64;
149                 p4.iph.protocol = IPPROTO_IPIP;
150                 if (target) {
151                         p4.iph.daddr = *((in_addr_t *) target);
152                 }
153                 strscpy(p4.name, name, IFNAMSIZ);
154         } else {
155 #ifdef LINUX_IPV6_TUNNEL
156                 p = (void *) &p6;
157
158                 memset(&p6, 0, sizeof(p6));
159                 p6.proto = 0; /* any protocol */
160                 if (target) {
161                         p6.raddr = *((struct in6_addr *) target);
162                 }
163                 strscpy(p6.name, name, IFNAMSIZ);
164 #else /* LINUX_IPV6_TUNNEL */
165                 return 0;
166 #endif /* LINUX_IPV6_TUNNEL */
167         }
168
169         strscpy(ifr.ifr_name, name, IFNAMSIZ);
170         ifr.ifr_ifru.ifru_data = p;
171
172         if ((err = ioctl(olsr_cnf->ioctl_s, target != NULL ? SIOCADDTUNNEL : SIOCDELTUNNEL, &ifr))) {
173                 olsr_syslog(OLSR_LOG_ERR, "Cannot %s tunnel %s to %s: %s (%d)\n", target != NULL ? "add" : "remove", name,
174                                 target != NULL ? inet_ntop(olsr_cnf->ip_version, target, buffer, sizeof(buffer)) : "-", strerror(errno),
175                                 errno);
176                 return 0;
177         }
178
179         olsr_syslog(OLSR_LOG_INFO, "Tunnel %s %s, to %s", name, target != NULL ? "added" : "removed",
180                         target != NULL ? inet_ntop(olsr_cnf->ip_version, target, buffer, sizeof(buffer)) : "-");
181
182         return target != NULL ? if_nametoindex(name) : 1;
183 }
184
185 /**
186  * demands an ipip tunnel to a certain target. If no tunnel exists it will be created
187  * @param target ip address of the target
188  * @param transportV4 true if IPv4 traffic is used, false for IPv6 traffic
189  * @param name pointer to name string buffer (length IFNAMSIZ)
190  * @return NULL if an error happened, pointer to olsr_iptunnel_entry otherwise
191  */
192 struct olsr_iptunnel_entry *olsr_os_add_ipip_tunnel(union olsr_ip_addr *target, bool transportV4 __attribute__ ((unused)), char *name) {
193   struct olsr_iptunnel_entry *t;
194
195   assert(olsr_cnf->ip_version == AF_INET6 || transportV4);
196
197   t = (struct olsr_iptunnel_entry *)avl_find(&tunnel_tree, target);
198   if (t == NULL) {
199     int if_idx;
200     struct ipaddr_str buf;
201
202     if_idx = os_ip_tunnel(name, (olsr_cnf->ip_version == AF_INET) ? (void *) &target->v4.s_addr : (void *) &target->v6);
203     if (if_idx == 0) {
204       // cannot create tunnel
205       olsr_syslog(OLSR_LOG_ERR, "Cannot create tunnel %s\n", name);
206       return NULL;
207     }
208
209     if (olsr_if_set_state(name, true)) {
210       os_ip_tunnel(name, NULL);
211       return NULL;
212     }
213
214     /* set originator IP for tunnel */
215     olsr_os_ifip(if_idx, &olsr_cnf->main_addr, true);
216
217     t = olsr_cookie_malloc(tunnel_cookie);
218     memcpy(&t->target, target, sizeof(*target));
219     t->node.key = &t->target;
220
221     strscpy(t->if_name, name, IFNAMSIZ);
222     t->if_index = if_idx;
223
224     avl_insert(&tunnel_tree, &t->node, AVL_DUP_NO);
225   }
226
227   t->usage++;
228   return t;
229 }
230
231 /**
232  * Release an olsr ipip tunnel. Tunnel will be deleted
233  * if this was the last user
234  * @param t pointer to olsr_iptunnel_entry
235  * @param cleanup true to free t's memory
236  */
237 static void internal_olsr_os_del_ipip_tunnel(struct olsr_iptunnel_entry *t, bool cleanup) {
238   if (!cleanup) {
239     if (t->usage == 0) {
240       return;
241     }
242     t->usage--;
243
244     if (t->usage > 0) {
245       return;
246     }
247   }
248
249   olsr_if_set_state(t->if_name, false);
250   os_ip_tunnel(t->if_name, NULL);
251
252   avl_delete(&tunnel_tree, &t->node);
253   if (!cleanup) {
254     olsr_cookie_free(tunnel_cookie, t);
255   }
256 }
257
258 void olsr_os_del_ipip_tunnel(struct olsr_iptunnel_entry *t) {
259   internal_olsr_os_del_ipip_tunnel(t, false);
260 }
261 #endif /* __linux__ */