Allow dynamic linking of framework to application
[oonf.git] / src / core / os_linux / os_system_linux.c
1 /*
2  * os_netlink.c
3  *
4  *  Created on: Oct 19, 2011
5  *      Author: rogge
6  */
7
8 /* must be first because of a problem with linux/rtnetlink.h */
9 #include <sys/socket.h>
10
11 /* and now the rest of the includes */
12 #include <linux/rtnetlink.h>
13 #include <linux/types.h>
14 #include <net/if.h>
15 #include <netinet/in.h>
16 #include <sys/ioctl.h>
17 #include <sys/utsname.h>
18 #include <errno.h>
19 #include <fcntl.h>
20 #include <unistd.h>
21
22 #include "common/common_types.h"
23 #include "common/string.h"
24 #include "olsr_interface.h"
25 #include "olsr_socket.h"
26 #include "olsr.h"
27 #include "os_system.h"
28
29 /* buffer for reading netlink messages */
30 #define NETLINK_BUFFER_SIZE 4096
31
32 /* ip forwarding */
33 #define PROC_IPFORWARD_V4 "/proc/sys/net/ipv4/ip_forward"
34 #define PROC_IPFORWARD_V6 "/proc/sys/net/ipv6/conf/all/forwarding"
35
36 /* Redirect proc entry */
37 #define PROC_IF_REDIRECT "/proc/sys/net/ipv4/conf/%s/send_redirects"
38 #define PROC_ALL_REDIRECT "/proc/sys/net/ipv4/conf/all/send_redirects"
39
40 /* IP spoof proc entry */
41 #define PROC_IF_SPOOF "/proc/sys/net/ipv4/conf/%s/rp_filter"
42 #define PROC_ALL_SPOOF "/proc/sys/net/ipv4/conf/all/rp_filter"
43
44 static int _writeToProc(const char *file, char *old, char value);
45 static bool _is_at_least_linuxkernel_2_6_31(void);
46 static void _netlink_handler(int fd, void *data,
47     bool event_read, bool event_write);
48 static void _handle_nl_link(void);
49 static void _handle_nl_addr(void);
50
51 /* global procfile state before initialization */
52 static char _original_rp_filter;
53 static char _original_icmp_redirect;
54
55 /* rtnetlink socket */
56 static int _rtnetlink_fd = -1;
57 static struct olsr_socket_entry _rtnetlink_socket = {
58   .process = _netlink_handler,
59   .event_read = true,
60 };
61
62 /* ioctl socket */
63 static int _ioctl_fd = -1;
64
65 /* static buffers for reading a netlink message */
66 static struct iovec _netlink_iov;
67 static struct sockaddr_nl _netlink_nladdr;
68 static struct msghdr _netlink_msg = {
69   &_netlink_nladdr,
70   sizeof(_netlink_nladdr),
71   &_netlink_iov,
72   1,
73   NULL,
74   0,
75   0
76 };
77
78 struct nlmsghdr *_netlink_header;
79
80 OLSR_SUBSYSTEM_STATE(_os_system_state);
81
82 /**
83  * Initialize os-specific subsystem
84  * @return -1 if an error happened, 0 otherwise
85  */
86 int
87 os_system_init(void) {
88   struct sockaddr_nl addr;
89
90   if (olsr_subsystem_is_initialized(&_os_system_state)) {
91     return 0;
92   }
93
94   _ioctl_fd = socket(AF_INET, SOCK_DGRAM, 0);
95   if (_ioctl_fd == -1) {
96     OLSR_WARN(LOG_OS_SYSTEM, "Cannot open ioctl socket: %s (%d)",
97         strerror(errno), errno);
98     return -1;
99   }
100   _netlink_header = calloc(NETLINK_BUFFER_SIZE, 1);
101   if (_netlink_header == NULL) {
102     OLSR_WARN_OOM(LOG_OS_SYSTEM);
103     close(_ioctl_fd);
104     return -1;
105   }
106   _netlink_iov.iov_base = _netlink_header;
107   _netlink_iov.iov_len = NETLINK_BUFFER_SIZE;
108
109   _rtnetlink_fd = socket(PF_NETLINK, SOCK_RAW, NETLINK_ROUTE);
110   if (_rtnetlink_fd < 0) {
111     OLSR_WARN(LOG_OS_SYSTEM, "Cannot open rtnetlink socket: %s (%d)",
112         strerror(errno), errno);
113     free(_netlink_header);
114     close(_ioctl_fd);
115     return -1;
116   }
117
118   memset(&addr, 0, sizeof(addr));
119   addr.nl_family = AF_NETLINK;
120   addr.nl_groups = RTMGRP_LINK | RTMGRP_IPV4_IFADDR | RTMGRP_IPV6_IFADDR;
121
122   /* kernel will assign appropiate number instead of pid */
123   addr.nl_pid = 0;
124
125   if (bind(_rtnetlink_fd, (struct sockaddr *)&addr, sizeof(addr))<0) {
126     OLSR_WARN(LOG_OS_SYSTEM, "Could not bind rtnetlink socket: %s (%d)",
127         strerror(errno), errno);
128     close (_rtnetlink_fd);
129     free(_netlink_header);
130     close(_ioctl_fd);
131     return -1;
132   }
133
134   /* add socket listener */
135   _rtnetlink_socket.fd = _rtnetlink_fd;
136   olsr_socket_add(&_rtnetlink_socket);
137
138   /* mark both flags non-used */
139   _original_icmp_redirect = 0;
140   _original_rp_filter = 0;
141
142   if (_writeToProc(PROC_ALL_REDIRECT, &_original_icmp_redirect, '0')) {
143     OLSR_WARN(LOG_OS_SYSTEM, "WARNING! Could not disable ICMP redirects! "
144         "You should manually ensure that ICMP redirects are disabled!");
145   }
146
147   /* check kernel version and disable global rp_filter */
148   if (_is_at_least_linuxkernel_2_6_31()) {
149     if (_writeToProc(PROC_ALL_SPOOF, &_original_rp_filter, '0')) {
150       OLSR_WARN(LOG_OS_SYSTEM, "WARNING! Could not disable global rp_filter "
151           "(necessary for kernel 2.6.31 and newer)! You should manually "
152           "ensure that rp_filter is disabled!");
153     }
154   }
155
156   olsr_subsystem_init(&_os_system_state);
157   return 0;
158 }
159
160 /**
161  * Cleanup os-specific subsystem
162  */
163 void
164 os_system_cleanup(void) {
165   if (olsr_subsystem_cleanup(&_os_system_state))
166     return;
167
168   if (_original_icmp_redirect != 0
169       && _writeToProc(PROC_ALL_REDIRECT, &_original_icmp_redirect, '0') != 0) {
170     OLSR_WARN(LOG_OS_SYSTEM,
171         "WARNING! Could not restore ICMP redirect flag %s to %c!",
172         PROC_ALL_REDIRECT, _original_icmp_redirect);
173   }
174
175   /* check kernel version and disable global rp_filter */
176   if (_writeToProc(PROC_ALL_SPOOF, &_original_rp_filter, '0')) {
177     OLSR_WARN(LOG_OS_SYSTEM,
178         "WARNING! Could not restore global rp_filter flag %s to %c!",
179         PROC_ALL_SPOOF, _original_rp_filter);
180   }
181
182   close(_rtnetlink_fd);
183   close(_ioctl_fd);
184 }
185
186 /**
187  * Initialize interface for mesh usage
188  * @param interf pointer to interface object
189  * @return -1 if an error happened, 0 otherwise
190  */
191 int
192 os_system_init_mesh_if(struct olsr_interface *interf) {
193   char procfile[FILENAME_MAX];
194   char old_redirect = 0, old_spoof = 0;
195
196   /* Generate the procfile name */
197   snprintf(procfile, sizeof(procfile), PROC_IF_REDIRECT, interf->name);
198
199   if (_writeToProc(procfile, &old_redirect, '0')) {
200     OLSR_WARN(LOG_OS_SYSTEM, "WARNING! Could not disable ICMP redirects! "
201         "You should manually ensure that ICMP redirects are disabled!");
202   }
203
204   /* Generate the procfile name */
205   snprintf(procfile, sizeof(procfile), PROC_IF_SPOOF, interf->name);
206
207   if (_writeToProc(procfile, &old_spoof, '0')) {
208     OLSR_WARN(LOG_OS_SYSTEM, "WARNING! Could not disable the IP spoof filter! "
209         "You should mannually ensure that IP spoof filtering is disabled!");
210   }
211
212   interf->_original_state = (old_redirect << 8) | (old_spoof);
213   return 0;
214 }
215
216 /**
217  * Cleanup interface after mesh usage
218  * @param interf pointer to interface object
219  */
220 void
221 os_system_cleanup_mesh_if(struct olsr_interface *interf) {
222   char restore_redirect, restore_spoof;
223   char procfile[FILENAME_MAX];
224
225   restore_redirect = (interf->_original_state >> 8) & 255;
226   restore_spoof = (interf->_original_state & 255);
227
228   /* Generate the procfile name */
229   snprintf(procfile, sizeof(procfile), PROC_IF_REDIRECT, interf->name);
230
231   if (restore_redirect != 0
232       && _writeToProc(procfile, NULL, restore_redirect) != 0) {
233     OLSR_WARN(LOG_OS_SYSTEM, "Could not restore ICMP redirect flag %s to %c",
234         procfile, restore_redirect);
235   }
236
237   /* Generate the procfile name */
238   snprintf(procfile, sizeof(procfile), PROC_IF_SPOOF, interf->name);
239
240   if (restore_spoof != 0
241       && _writeToProc(procfile, NULL, restore_spoof) != 0) {
242     OLSR_WARN(LOG_OS_SYSTEM, "Could not restore IP spoof flag %s to %c",
243         procfile, restore_spoof);
244   }
245
246   interf->_original_state = 0;
247   return;
248 }
249
250 /**
251  * Set interface up or down
252  * @param dev pointer to name of interface
253  * @param up true if interface should be up, false if down
254  * @return -1 if an error happened, 0 otherwise
255  */
256 int
257 os_system_set_interface_state(const char *dev, bool up) {
258   int oldflags;
259   struct ifreq ifr;
260
261   memset(&ifr, 0, sizeof(ifr));
262   strscpy(ifr.ifr_name, dev, IFNAMSIZ);
263
264   if (ioctl(_ioctl_fd, SIOCGIFFLAGS, &ifr) < 0) {
265     OLSR_WARN(LOG_OS_SYSTEM,
266         "ioctl SIOCGIFFLAGS (get flags) error on device %s: %s (%d)\n",
267         dev, strerror(errno), errno);
268     return -1;
269   }
270
271   oldflags = ifr.ifr_flags;
272   if (up) {
273     ifr.ifr_flags |= IFF_UP;
274   }
275   else {
276     ifr.ifr_flags &= ~IFF_UP;
277   }
278
279   if (oldflags == ifr.ifr_flags) {
280     /* interface is already up/down */
281     return 0;
282   }
283
284   if (ioctl(_ioctl_fd, SIOCSIFFLAGS, &ifr) < 0) {
285     OLSR_WARN(LOG_OS_SYSTEM,
286         "ioctl SIOCSIFFLAGS (set flags %s) error on device %s: %s (%d)\n",
287         up ? "up" : "down", dev, strerror(errno), errno);
288     return -1;
289   }
290   return 0;
291 }
292
293 /**
294  * Overwrite a numeric entry in the procfile system and keep the old
295  * value.
296  * @param file pointer to filename (including full path)
297  * @param old pointer to memory to store old value
298  * @param value new value
299  * @return -1 if an error happened, 0 otherwise
300  */
301 static int
302 _writeToProc(const char *file, char *old, char value) {
303   int fd;
304   char rv;
305
306   if ((fd = open(file, O_RDWR)) < 0) {
307     goto writetoproc_error;
308   }
309
310   if (read(fd, &rv, 1) != 1) {
311     goto writetoproc_error;
312   }
313
314   if (rv != value) {
315     if (lseek(fd, SEEK_SET, 0) == -1) {
316       goto writetoproc_error;
317     }
318
319     if (write(fd, &value, 1) != 1) {
320       goto writetoproc_error;
321     }
322
323     OLSR_DEBUG(LOG_OS_SYSTEM, "Writing '%c' (was %c) to %s", value, rv, file);
324   }
325
326   if (close(fd) != 0) {
327     goto writetoproc_error;
328   }
329
330   if (old && rv != value) {
331     *old = rv;
332   }
333
334   return 0;
335
336 writetoproc_error:
337   OLSR_WARN(LOG_OS_SYSTEM,
338     "Error, cannot read proc entry %s: %s (%d)\n",
339     file, strerror(errno), errno);
340   return -1;
341 }
342
343 static bool
344 _is_at_least_linuxkernel_2_6_31(void) {
345   struct utsname uts;
346   char *next;
347   int first = 0, second = 0, third = 0;
348
349   memset(&uts, 0, sizeof(uts));
350   if (uname(&uts)) {
351     OLSR_WARN(LOG_OS_SYSTEM,
352         "Error, could not read kernel version: %s (%d)\n",
353         strerror(errno), errno);
354     return false;
355   }
356
357   first = strtol(uts.release, &next, 10);
358   /* check for linux 3.x */
359   if (first >= 3) {
360     return true;
361   }
362
363   if (*next != '.') {
364     goto kernel_parse_error;
365   }
366
367   second = strtol(next+1, &next, 10);
368   if (*next != '.') {
369     goto kernel_parse_error;
370   }
371
372   third = strtol(next+1, NULL, 10);
373
374   /* better or equal than linux 2.6.31 ? */
375   return first == 2 && second == 6 && third >= 31;
376
377 kernel_parse_error:
378   OLSR_WARN(LOG_OS_SYSTEM,
379       "Error, cannot parse kernel version: %s\n", uts.release);
380   return false;
381 }
382
383 static void
384 _netlink_handler(int fd,
385     void *data __attribute__((unused)),
386     bool event_read,
387     bool event_write __attribute__((unused))) {
388   int len, plen;
389   int ret;
390
391   if (!event_read) {
392     return;
393   }
394
395   if ((ret = recvmsg(fd, &_netlink_msg, MSG_DONTWAIT)) >= 0) {
396     /*check message*/
397     len = _netlink_header->nlmsg_len;
398     plen = len - sizeof(_netlink_header);
399     if (len > ret || plen < 0) {
400       OLSR_WARN(LOG_OS_SYSTEM,
401           "Malformed netlink message: len=%d left=%d plen=%d\n",
402               len, ret, plen);
403       return;
404     }
405
406     OLSR_DEBUG(LOG_OS_SYSTEM,
407         "Netlink message received: type %d\n", _netlink_header->nlmsg_type);
408
409     switch (_netlink_header->nlmsg_type) {
410       case RTM_NEWLINK:
411       case RTM_DELLINK:
412         _handle_nl_link();
413         break;
414
415       case RTM_NEWADDR:
416       case RTM_DELADDR:
417         _handle_nl_addr();
418         break;
419       default:
420         break;
421     }
422   }
423   else if (errno != EAGAIN) {
424     OLSR_WARN(LOG_OS_SYSTEM,"netlink recvmsg error: %s (%d)\n",
425         strerror(errno), errno);
426   }
427 }
428
429 static void
430 _handle_nl_link(void) {
431   struct ifinfomsg *ifi;
432   char if_name[IF_NAMESIZE];
433
434   ifi = (struct ifinfomsg *) NLMSG_DATA(_netlink_header);
435
436   if (if_indextoname(ifi->ifi_index, if_name) == NULL) {
437     OLSR_WARN(LOG_OS_SYSTEM,
438         "Failed to convert if-index to name: %d", ifi->ifi_index);
439     return;
440   }
441
442   OLSR_DEBUG(LOG_OS_SYSTEM, "Linkstatus of interface '%s' changed", if_name);
443   olsr_interface_trigger_change(if_name);
444 }
445
446 static void
447 _handle_nl_addr(void) {
448   struct ifaddrmsg *ifa;
449
450   char if_name[IF_NAMESIZE];
451
452   ifa = (struct ifaddrmsg *) NLMSG_DATA(_netlink_header);
453
454   if (if_indextoname(ifa->ifa_index, if_name) == NULL) {
455     OLSR_WARN(LOG_OS_SYSTEM,
456         "Failed to convert if-index to name: %d", ifa->ifa_index);
457     return;
458   }
459
460   OLSR_DEBUG(LOG_OS_SYSTEM, "Address of interface '%s' changed", if_name);
461   olsr_interface_trigger_change(if_name);
462 }