Move some setup functions from os_routing to os_net
[oonf.git] / src-api / core / os_linux / os_net_linux.c
1
2 /*
3  * The olsr.org Optimized Link-State Routing daemon(olsrd)
4  * Copyright (c) 2004-2013, 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 #include <net/if.h>
43 #include <netinet/in.h>
44 #include <sys/ioctl.h>
45 #include <sys/utsname.h>
46 #include <errno.h>
47 #include <fcntl.h>
48 #include <unistd.h>
49
50 #include "common/common_types.h"
51
52 #include "core/olsr_logging.h"
53 #include "core/olsr_subsystem.h"
54 #include "core/os_net.h"
55
56 /* ip forwarding */
57 #define PROC_IPFORWARD_V4 "/proc/sys/net/ipv4/ip_forward"
58 #define PROC_IPFORWARD_V6 "/proc/sys/net/ipv6/conf/all/forwarding"
59
60 /* Redirect proc entry */
61 #define PROC_IF_REDIRECT "/proc/sys/net/ipv4/conf/%s/send_redirects"
62 #define PROC_ALL_REDIRECT "/proc/sys/net/ipv4/conf/all/send_redirects"
63
64 /* IP spoof proc entry */
65 #define PROC_IF_SPOOF "/proc/sys/net/ipv4/conf/%s/rp_filter"
66 #define PROC_ALL_SPOOF "/proc/sys/net/ipv4/conf/all/rp_filter"
67
68 /* prototypes */
69 static int _init(void);
70 static void _cleanup(void);
71 static void _activate_if_routing(void);
72 static void _deactivate_if_routing(void);
73 static bool _is_at_least_linuxkernel_2_6_31(void);
74 static int _os_linux_writeToProc(const char *file, char *old, char value);
75
76 /* global ioctl sockets for ipv4 and ipv6 */
77 static int _ioctl_v4, _ioctl_v6;
78
79 /* subsystem definition */
80 struct oonf_subsystem oonf_os_net_subsystem = {
81   .init = _init,
82   .cleanup = _cleanup,
83 };
84
85 /* global procfile state before initialization */
86 static char _original_rp_filter;
87 static char _original_icmp_redirect;
88 static char _original_ipv4_forward;
89 static char _original_ipv6_forward;
90
91 /* counter of mesh interfaces for ip_forward configuration */
92 static int _mesh_count = 0;
93
94 /**
95  * Initialize os_net subsystem
96  * @return -1 if an error happened, 0 otherwise
97  */
98 static int
99 _init(void) {
100   _ioctl_v4 = socket(AF_INET, SOCK_DGRAM, 0);
101   if (_ioctl_v4 == -1) {
102     OLSR_WARN(LOG_OS_NET, "Cannot open ipv4 ioctl socket: %s (%d)",
103         strerror(errno), errno);
104     return -1;
105   }
106
107   _ioctl_v6 = socket(AF_INET6, SOCK_DGRAM, 0);
108   if (_ioctl_v6 == -1) {
109     OLSR_WARN(LOG_OS_NET, "Cannot open ipv6 ioctl socket: %s (%d)",
110         strerror(errno), errno);
111
112     /* do not stop here, system might just not support IPv6 */
113   }
114
115   return 0;
116 }
117
118 /**
119  * Cleanup os_net subsystem
120  */
121 static void
122 _cleanup(void) {
123   close (_ioctl_v4);
124   if (_ioctl_v6 != -1) {
125     close (_ioctl_v6);
126   }
127 }
128
129 /**
130  * Receive data from a socket.
131  * @param fd filedescriptor
132  * @param buf buffer for incoming data
133  * @param length length of buffer
134  * @param source pointer to netaddr socket object to store source of packet
135  * @param interf limit received data to certain interface
136  *   (only used if socket cannot be bound to interface)
137  * @return same as recvfrom()
138  */
139 int
140 os_recvfrom(int fd, void *buf, size_t length, union netaddr_socket *source,
141     struct olsr_interface_data *interf __attribute__((unused))) {
142   socklen_t len = sizeof(*source);
143   return recvfrom(fd, buf, length, 0, &source->std, &len);
144 }
145
146 /**
147  * Updates the data of an interface.
148  * The interface data object will be completely overwritten
149  * @param ifdata pointer to an interface data object
150  * @param name name of interface
151  * @return -1 if an error happened, 0 otherwise
152  */
153 int
154 os_net_update_interface(struct olsr_interface_data *ifdata,
155     const char *name) {
156   struct ifreq ifr;
157   struct ifaddrs *ifaddrs;
158   struct ifaddrs *ifa;
159   size_t addrcount;
160   union netaddr_socket *sock;
161   struct netaddr *addr;
162
163   /* cleanup data structure */
164   if (ifdata->addresses) {
165     free(ifdata->addresses);
166   }
167
168   memset(ifdata, 0, sizeof(*ifdata));
169   strscpy(ifdata->name, name, sizeof(ifdata->name));
170
171   /* get interface index */
172   ifdata->index = if_nametoindex(name);
173   if (ifdata->index == 0) {
174     /* interface is not there at the moment */
175     return 0;
176   }
177
178   memset(&ifr, 0, sizeof(ifr));
179   strscpy(ifr.ifr_name, ifdata->name, IF_NAMESIZE);
180
181   if (ioctl(_ioctl_v4, SIOCGIFFLAGS, &ifr) < 0) {
182     OLSR_WARN(LOG_OS_NET,
183         "ioctl SIOCGIFFLAGS (get flags) error on device %s: %s (%d)\n",
184         ifdata->name, strerror(errno), errno);
185     return -1;
186   }
187
188   if ((ifr.ifr_flags & (IFF_UP | IFF_RUNNING)) == (IFF_UP|IFF_RUNNING)) {
189     ifdata->up = true;
190   }
191
192   memset(&ifr, 0, sizeof(ifr));
193   strscpy(ifr.ifr_name, ifdata->name, IF_NAMESIZE);
194
195   if (ioctl(_ioctl_v4, SIOCGIFHWADDR, &ifr) < 0) {
196     OLSR_WARN(LOG_OS_NET,
197         "ioctl SIOCGIFHWADDR (get flags) error on device %s: %s (%d)\n",
198         ifdata->name, strerror(errno), errno);
199     return -1;
200   }
201
202   netaddr_from_binary(&ifdata->mac, ifr.ifr_hwaddr.sa_data, 6, AF_MAC48);
203
204   /* get ip addresses */
205   ifaddrs = NULL;
206   addrcount = 0;
207
208   if (getifaddrs(&ifaddrs)) {
209     OLSR_WARN(LOG_OS_NET,
210         "getifaddrs() failed: %s (%d)", strerror(errno), errno);
211     return -1;
212   }
213
214   for (ifa = ifaddrs; ifa != NULL; ifa = ifa->ifa_next) {
215     if (strcmp(ifdata->name, ifa->ifa_name) == 0 &&
216         (ifa->ifa_addr->sa_family == AF_INET || ifa->ifa_addr->sa_family == AF_INET6)) {
217       addrcount++;
218     }
219   }
220
221   ifdata->addresses = calloc(addrcount, sizeof(struct netaddr));
222   if (ifdata->addresses == NULL) {
223     OLSR_WARN(LOG_OS_NET,
224         "Cannot allocate memory for interface %s with %"PRINTF_SIZE_T_SPECIFIER" prefixes",
225         ifdata->name, addrcount);
226     freeifaddrs(ifaddrs);
227     return -1;
228   }
229
230   for (ifa = ifaddrs; ifa != NULL; ifa = ifa->ifa_next) {
231     if (strcmp(ifdata->name, ifa->ifa_name) == 0 &&
232         (ifa->ifa_addr->sa_family == AF_INET || ifa->ifa_addr->sa_family == AF_INET6)) {
233       sock = (union netaddr_socket *)ifa->ifa_addr;
234       addr = &ifdata->addresses[ifdata->addrcount];
235
236       if (netaddr_from_socket(&ifdata->addresses[ifdata->addrcount], sock) == 0) {
237         ifdata->addrcount++;
238
239         if (netaddr_get_address_family(addr) == AF_INET) {
240           if (!(netaddr_is_in_subnet(&NETADDR_IPV4_LOOPBACK, addr)
241               || netaddr_is_in_subnet(&NETADDR_IPV4_MULTICAST, addr))) {
242             ifdata->if_v4 = addr;
243           }
244         }
245         else if (netaddr_get_address_family(addr) == AF_INET6) {
246           if (netaddr_is_in_subnet(&NETADDR_IPV6_LINKLOCAL, addr)) {
247             ifdata->linklocal_v6_ptr = addr;
248           }
249           else if (!(netaddr_cmp(&NETADDR_IPV6_LOOPBACK, addr) == 0
250               || netaddr_is_in_subnet(&NETADDR_IPV6_MULTICAST, addr)
251               || netaddr_is_in_subnet(&NETADDR_IPV6_IPV4COMPATIBLE, addr)
252               || netaddr_is_in_subnet(&NETADDR_IPV6_IPV4MAPPED, addr))) {
253             ifdata->if_v6 = addr;
254           }
255         }
256       }
257     }
258   }
259
260   freeifaddrs(ifaddrs);
261   return 0;
262 }
263
264 /**
265  * Initialize interface for mesh usage
266  * @param interf pointer to interface object
267  * @return -1 if an error happened, 0 otherwise
268  */
269 int
270 os_net_init_mesh_if(struct olsr_interface *interf) {
271   char procfile[FILENAME_MAX];
272   char old_redirect = 0, old_spoof = 0;
273
274   /* handle global ip_forward setting */
275   _mesh_count++;
276   if (_mesh_count == 1) {
277     _activate_if_routing();
278   }
279
280   /* Generate the procfile name */
281   snprintf(procfile, sizeof(procfile), PROC_IF_REDIRECT, interf->data.name);
282
283   if (_os_linux_writeToProc(procfile, &old_redirect, '0')) {
284     OLSR_WARN(LOG_OS_SYSTEM, "WARNING! Could not disable ICMP redirects! "
285         "You should manually ensure that ICMP redirects are disabled!");
286   }
287
288   /* Generate the procfile name */
289   snprintf(procfile, sizeof(procfile), PROC_IF_SPOOF, interf->data.name);
290
291   if (_os_linux_writeToProc(procfile, &old_spoof, '0')) {
292     OLSR_WARN(LOG_OS_SYSTEM, "WARNING! Could not disable the IP spoof filter! "
293         "You should mannually ensure that IP spoof filtering is disabled!");
294   }
295
296   interf->_original_state = (old_redirect << 8) | (old_spoof);
297   return 0;
298 }
299
300 /**
301  * Cleanup interface after mesh usage
302  * @param interf pointer to interface object
303  */
304 void
305 os_net_cleanup_mesh_if(struct olsr_interface *interf) {
306   char restore_redirect, restore_spoof;
307   char procfile[FILENAME_MAX];
308
309   restore_redirect = (interf->_original_state >> 8) & 255;
310   restore_spoof = (interf->_original_state & 255);
311
312   /* Generate the procfile name */
313   snprintf(procfile, sizeof(procfile), PROC_IF_REDIRECT, interf->data.name);
314
315   if (restore_redirect != 0
316       && _os_linux_writeToProc(procfile, NULL, restore_redirect) != 0) {
317     OLSR_WARN(LOG_OS_SYSTEM, "Could not restore ICMP redirect flag %s to %c",
318         procfile, restore_redirect);
319   }
320
321   /* Generate the procfile name */
322   snprintf(procfile, sizeof(procfile), PROC_IF_SPOOF, interf->data.name);
323
324   if (restore_spoof != 0
325       && _os_linux_writeToProc(procfile, NULL, restore_spoof) != 0) {
326     OLSR_WARN(LOG_OS_SYSTEM, "Could not restore IP spoof flag %s to %c",
327         procfile, restore_spoof);
328   }
329
330   /* handle global ip_forward setting */
331   _mesh_count--;
332   if (_mesh_count == 0) {
333     _deactivate_if_routing();
334   }
335
336   interf->_original_state = 0;
337   return;
338 }
339
340 static void
341 _activate_if_routing(void) {
342   if (_os_linux_writeToProc(PROC_IPFORWARD_V4, &_original_ipv4_forward, '1')) {
343     OLSR_WARN(LOG_OS_SYSTEM, "WARNING! Could not activate ip_forward for ipv4! "
344         "You should manually ensure that ip_forward for ipv4 is activated!");
345   }
346   if (_os_linux_writeToProc(PROC_IPFORWARD_V6, &_original_ipv6_forward, '1')) {
347     OLSR_WARN(LOG_OS_SYSTEM, "WARNING! Could not activate ip_forward for ipv6! "
348         "You should manually ensure that ip_forward for ipv6 is activated!");
349   }
350
351   if (_os_linux_writeToProc(PROC_ALL_REDIRECT, &_original_icmp_redirect, '0')) {
352     OLSR_WARN(LOG_OS_SYSTEM, "WARNING! Could not disable ICMP redirects! "
353         "You should manually ensure that ICMP redirects are disabled!");
354   }
355
356   /* check kernel version and disable global rp_filter */
357   if (_is_at_least_linuxkernel_2_6_31()) {
358     if (_os_linux_writeToProc(PROC_ALL_SPOOF, &_original_rp_filter, '0')) {
359       OLSR_WARN(LOG_OS_SYSTEM, "WARNING! Could not disable global rp_filter "
360           "(necessary for kernel 2.6.31 and newer)! You should manually "
361           "ensure that rp_filter is disabled!");
362     }
363   }
364 }
365
366 static void
367 _deactivate_if_routing(void) {
368   if (_original_icmp_redirect != 0
369       && _os_linux_writeToProc(PROC_ALL_REDIRECT, &_original_icmp_redirect, '0') != 0) {
370     OLSR_WARN(LOG_OS_SYSTEM,
371         "WARNING! Could not restore ICMP redirect flag %s to %c!",
372         PROC_ALL_REDIRECT, _original_icmp_redirect);
373   }
374
375   /* check kernel version and disable global rp_filter */
376   if (_os_linux_writeToProc(PROC_ALL_SPOOF, &_original_rp_filter, '0')) {
377     OLSR_WARN(LOG_OS_SYSTEM,
378         "WARNING! Could not restore global rp_filter flag %s to %c!",
379         PROC_ALL_SPOOF, _original_rp_filter);
380   }
381
382   if (_os_linux_writeToProc(PROC_IPFORWARD_V4, NULL, _original_ipv4_forward)) {
383     OLSR_WARN(LOG_OS_SYSTEM, "WARNING! Could not restore %s to %c!",
384         PROC_IPFORWARD_V4, _original_ipv4_forward);
385   }
386   if (_os_linux_writeToProc(PROC_IPFORWARD_V6, NULL, _original_ipv6_forward)) {
387     OLSR_WARN(LOG_OS_SYSTEM, "WARNING! Could not restore flag %s to %c",
388         PROC_IPFORWARD_V6, _original_ipv6_forward);
389   }
390 }
391
392
393 /**
394  * Overwrite a numeric entry in the procfile system and keep the old
395  * value.
396  * @param file pointer to filename (including full path)
397  * @param old pointer to memory to store old value
398  * @param value new value
399  * @return -1 if an error happened, 0 otherwise
400  */
401 static int
402 _os_linux_writeToProc(const char *file, char *old, char value) {
403   int fd;
404   char rv;
405
406   if ((fd = open(file, O_RDWR)) < 0) {
407     goto writetoproc_error;
408   }
409
410   if (read(fd, &rv, 1) != 1) {
411     goto writetoproc_error;
412   }
413
414   if (rv != value) {
415     if (lseek(fd, SEEK_SET, 0) == -1) {
416       goto writetoproc_error;
417     }
418
419     if (write(fd, &value, 1) != 1) {
420       goto writetoproc_error;
421     }
422
423     OLSR_DEBUG(LOG_OS_SYSTEM, "Writing '%c' (was %c) to %s", value, rv, file);
424   }
425
426   if (close(fd) != 0) {
427     goto writetoproc_error;
428   }
429
430   if (old && rv != value) {
431     *old = rv;
432   }
433
434   return 0;
435
436 writetoproc_error:
437   OLSR_WARN(LOG_OS_SYSTEM,
438     "Error, cannot read proc entry %s: %s (%d)\n",
439     file, strerror(errno), errno);
440   return -1;
441 }
442
443 /**
444  * @return true if linux kernel is at least 2.6.31
445  */
446 static bool
447 _is_at_least_linuxkernel_2_6_31(void) {
448   struct utsname uts;
449   char *next;
450   int first = 0, second = 0, third = 0;
451
452   memset(&uts, 0, sizeof(uts));
453   if (uname(&uts)) {
454     OLSR_WARN(LOG_OS_SYSTEM,
455         "Error, could not read kernel version: %s (%d)\n",
456         strerror(errno), errno);
457     return false;
458   }
459
460   first = strtol(uts.release, &next, 10);
461   /* check for linux 3.x */
462   if (first >= 3) {
463     return true;
464   }
465
466   if (*next != '.') {
467     goto kernel_parse_error;
468   }
469
470   second = strtol(next+1, &next, 10);
471   if (*next != '.') {
472     goto kernel_parse_error;
473   }
474
475   third = strtol(next+1, NULL, 10);
476
477   /* better or equal than linux 2.6.31 ? */
478   return first == 2 && second == 6 && third >= 31;
479
480 kernel_parse_error:
481   OLSR_WARN(LOG_OS_SYSTEM,
482       "Error, cannot parse kernel version: %s\n", uts.release);
483   return false;
484 }