328cc07b325964930ea2f90671172cee1462be20
[oonf.git] / src / core / os_linux / os_system_linux.c
1
2 /*
3  * The olsr.org Optimized Link-State Routing daemon(olsrd)
4  * Copyright (c) 2004-2011, 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 /* must be first because of a problem with linux/rtnetlink.h */
43 #include <sys/socket.h>
44
45 /* and now the rest of the includes */
46 #include <linux/rtnetlink.h>
47 #include <linux/types.h>
48 #include <net/if.h>
49 #include <netinet/in.h>
50 #include <sys/ioctl.h>
51 #include <errno.h>
52 #include <unistd.h>
53
54 #include "common/common_types.h"
55 #include "common/string.h"
56 #include "olsr_interface.h"
57 #include "olsr_socket.h"
58 #include "olsr.h"
59 #include "os_system.h"
60
61 #define OS_SYSTEM_NETLINK_TIMEOUT 100
62
63 static void _cb_handle_netlink_timerout(void *);
64 static void _netlink_handler(int fd, void *data,
65     bool event_read, bool event_write);
66 static void _handle_rtnetlink(struct nlmsghdr *hdr);
67
68 static void _handle_nl_err(struct os_system_netlink *, struct nlmsghdr *);
69
70 /* ioctl socket */
71 static int _ioctl_fd = -1;
72
73 /* static buffers for receiving/sending a netlink message */
74 static struct sockaddr_nl _netlink_nladdr = {
75   .nl_family = AF_NETLINK
76 };
77
78 static struct iovec _netlink_rcv_iov;
79 static struct msghdr _netlink_rcv_msg = {
80   &_netlink_nladdr,
81   sizeof(_netlink_nladdr),
82   &_netlink_rcv_iov,
83   1,
84   NULL,
85   0,
86   0
87 };
88
89 static struct nlmsghdr _netlink_hdr_done = {
90   .nlmsg_len = sizeof(struct nlmsghdr),
91   .nlmsg_type = NLMSG_DONE
92 };
93
94 static struct iovec _netlink_send_iov[2] = {
95     { NULL, 0 },
96     { &_netlink_hdr_done, sizeof(_netlink_hdr_done) },
97 };
98
99 static struct msghdr _netlink_send_msg = {
100   &_netlink_nladdr,
101   sizeof(_netlink_nladdr),
102   &_netlink_send_iov[0],
103   2,
104   NULL,
105   0,
106   0
107 };
108
109 /* netlink timeout handling */
110 static struct olsr_timer_info *_netlink_timer;
111
112 /* built in rtnetlink multicast receiver */
113 static struct os_system_netlink _rtnetlink_receiver;
114
115 OLSR_SUBSYSTEM_STATE(_os_system_state);
116
117 /**
118  * Initialize os-specific subsystem
119  * @return -1 if an error happened, 0 otherwise
120  */
121 int
122 os_system_init(void) {
123   if (olsr_subsystem_is_initialized(&_os_system_state)) {
124     return 0;
125   }
126
127   if ((_netlink_timer =
128       olsr_timer_add("rtnetlink feedback timer", _cb_handle_netlink_timerout, false)) == NULL) {
129     OLSR_WARN(LOG_OS_SYSTEM, "Cannot allocate timer class for netlink timeout");
130     return -1;
131   }
132
133   _ioctl_fd = socket(AF_INET, SOCK_DGRAM, 0);
134   if (_ioctl_fd == -1) {
135     OLSR_WARN(LOG_OS_SYSTEM, "Cannot open ioctl socket: %s (%d)",
136         strerror(errno), errno);
137     olsr_timer_remove(_netlink_timer);
138     return -1;
139   }
140
141   if (os_system_netlink_add(&_rtnetlink_receiver, NETLINK_ROUTE,
142       RTMGRP_LINK | RTMGRP_IPV4_IFADDR | RTMGRP_IPV6_IFADDR)) {
143     olsr_timer_remove(_netlink_timer);
144     close(_ioctl_fd);
145     return -1;
146   }
147   _rtnetlink_receiver.cb_message = _handle_rtnetlink;
148   olsr_subsystem_init(&_os_system_state);
149   return 0;
150 }
151
152 /**
153  * Cleanup os-specific subsystem
154  */
155 void
156 os_system_cleanup(void) {
157   if (olsr_subsystem_cleanup(&_os_system_state))
158     return;
159
160   os_system_netlink_remove(&_rtnetlink_receiver);
161   olsr_timer_remove(_netlink_timer);
162   close(_ioctl_fd);
163 }
164
165 /**
166  * Set interface up or down
167  * @param dev pointer to name of interface
168  * @param up true if interface should be up, false if down
169  * @return -1 if an error happened, 0 otherwise
170  */
171 int
172 os_system_set_interface_state(const char *dev, bool up) {
173   int oldflags;
174   struct ifreq ifr;
175
176   memset(&ifr, 0, sizeof(ifr));
177   strscpy(ifr.ifr_name, dev, IFNAMSIZ);
178
179   if (ioctl(_ioctl_fd, SIOCGIFFLAGS, &ifr) < 0) {
180     OLSR_WARN(LOG_OS_SYSTEM,
181         "ioctl SIOCGIFFLAGS (get flags) error on device %s: %s (%d)\n",
182         dev, strerror(errno), errno);
183     return -1;
184   }
185
186   oldflags = ifr.ifr_flags;
187   if (up) {
188     ifr.ifr_flags |= IFF_UP;
189   }
190   else {
191     ifr.ifr_flags &= ~IFF_UP;
192   }
193
194   if (oldflags == ifr.ifr_flags) {
195     /* interface is already up/down */
196     return 0;
197   }
198
199   if (ioctl(_ioctl_fd, SIOCSIFFLAGS, &ifr) < 0) {
200     OLSR_WARN(LOG_OS_SYSTEM,
201         "ioctl SIOCSIFFLAGS (set flags %s) error on device %s: %s (%d)\n",
202         up ? "up" : "down", dev, strerror(errno), errno);
203     return -1;
204   }
205   return 0;
206 }
207
208 /**
209  * Open a new bidirectional netlink socket
210  * @param nl pointer to uninitialized netlink socket handler
211  * @param protocol protocol id (NETLINK_ROUTING for example)
212  * @param multicast multicast groups this socket should listen to
213  * @return -1 if an error happened, 0 otherwise
214  */
215 int
216 os_system_netlink_add(struct os_system_netlink *nl, int protocol, uint32_t multicast) {
217   struct sockaddr_nl addr;
218
219   memset(nl, 0, sizeof(*nl));
220
221   nl->socket.fd = socket(PF_NETLINK, SOCK_RAW, protocol);
222   if (nl->socket.fd < 0) {
223     OLSR_WARN(LOG_OS_SYSTEM, "Cannot open sync rtnetlink socket: %s (%d)",
224         strerror(errno), errno);
225     goto os_add_netlink_fail;
226   }
227
228   if (abuf_init(&nl->out)) {
229     OLSR_WARN_OOM(LOG_OS_SYSTEM);
230     goto os_add_netlink_fail;
231   }
232
233   nl->in = calloc(1, UIO_MAXIOV);
234   if (nl->in == NULL) {
235     OLSR_WARN_OOM(LOG_OS_SYSTEM);
236     goto os_add_netlink_fail;
237   }
238
239
240   memset(&addr, 0, sizeof(addr));
241   addr.nl_family = AF_NETLINK;
242   addr.nl_groups = multicast;
243
244   /* kernel will assign appropriate number instead of pid */
245   /* addr.nl_pid = 0; */
246
247   if (bind(nl->socket.fd, (struct sockaddr *)&addr, sizeof(addr))<0) {
248     OLSR_WARN(LOG_OS_SYSTEM, "Could not bind netlink socket: %s (%d)",
249         strerror(errno), errno);
250     goto os_add_netlink_fail;
251   }
252
253   nl->socket.process = _netlink_handler;
254   nl->socket.event_read = true;
255   nl->socket.data = nl;
256   olsr_socket_add(&nl->socket);
257
258   return 0;
259
260 os_add_netlink_fail:
261   if (nl->socket.fd != -1) {
262     close(nl->socket.fd);
263   }
264   free (nl->in);
265   abuf_free(&nl->out);
266   return -1;
267 }
268
269 /**
270  * Close a netlink socket handler
271  * @param nl pointer to handler
272  */
273 void
274 os_system_netlink_remove(struct os_system_netlink *nl) {
275   olsr_socket_remove(&nl->socket);
276
277   close(nl->socket.fd);
278   free (nl->in);
279   abuf_free(&nl->out);
280 }
281
282 /**
283  * Add a netlink message to the outgoign queue of a handler
284  * @param nl pointer to netlink handler
285  * @param nl_hdr pointer to netlink message
286  * @return sequence number used for message
287  */
288 int
289 os_system_netlink_send(struct os_system_netlink *nl,
290     struct nlmsghdr *nl_hdr) {
291   nl->seq_used = (nl->seq_used + 1) & INT32_MAX;
292
293   nl_hdr->nlmsg_seq = nl->seq_used;
294   nl_hdr->nlmsg_flags |= NLM_F_ACK | NLM_F_MULTI;
295
296   abuf_memcpy(&nl->out, nl_hdr, nl_hdr->nlmsg_len);
297
298   /* trigger write */
299   olsr_socket_set_write(&nl->socket, true);
300   return nl->seq_used;
301 }
302
303 /**
304  * Add an attribute to a netlink message
305  * @param n pointer to netlink header
306  * @param type type of netlink attribute
307  * @param data pointer to data of netlink attribute
308  * @param len length of data of netlink attribute
309  * @return -1 if netlink message got too large, 0 otherwise
310  */
311 int
312 os_system_netlink_addreq(struct nlmsghdr *n,
313     int type, const void *data, int len)
314 {
315   struct rtattr *rta;
316   size_t aligned_msg_len, aligned_attr_len;
317
318   /* calculate aligned length of message and new attribute */
319   aligned_msg_len = NLMSG_ALIGN(n->nlmsg_len);
320   aligned_attr_len = RTA_LENGTH(len);
321
322   if (aligned_msg_len + aligned_attr_len > UIO_MAXIOV) {
323     OLSR_WARN(LOG_OS_SYSTEM, "Netlink message got too large!");
324     return -1;
325   }
326   rta = (struct rtattr *)((void*)((char *)n + aligned_msg_len));
327   rta->rta_type = type;
328   rta->rta_len = aligned_attr_len;
329
330   n->nlmsg_len = aligned_msg_len + aligned_attr_len;
331
332   memcpy(RTA_DATA(rta), data, len);
333   return 0;
334 }
335
336 /**
337  * Handle timeout of netlink acks
338  * @param ptr pointer to netlink handler
339  */
340 static void
341 _cb_handle_netlink_timerout(void *ptr) {
342   struct os_system_netlink *nl = ptr;
343
344   nl->timeout = NULL;
345
346   if (nl->cb_timeout) {
347     nl->cb_timeout();
348   }
349
350   nl->seq_used = 0;
351 }
352
353 /**
354  * Send all netlink messages in the outgoing queue to the kernel
355  * @param nl pointer to netlink handler
356  */
357 static void
358 _flush_netlink_buffer(struct os_system_netlink *nl) {
359   ssize_t ret;
360
361   /* start feedback timer */
362   olsr_timer_set(&nl->timeout, OS_SYSTEM_NETLINK_TIMEOUT*10, 0, nl, _netlink_timer);
363
364   /* send outgoing message */
365   _netlink_send_iov[0].iov_base = abuf_getptr(&nl->out);
366   _netlink_send_iov[0].iov_len = abuf_getlen(&nl->out);
367
368   if ((ret = sendmsg(nl->socket.fd, &_netlink_send_msg, 0)) <= 0) {
369     OLSR_WARN(LOG_OS_SYSTEM,
370         "Cannot send data to netlink socket (%d: %s)",
371         errno, strerror(errno));
372   }
373   else {
374     OLSR_DEBUG(LOG_OS_SYSTEM, "Sent %zd/%zu bytes for netlink seqno: %d",
375         ret, abuf_getlen(&nl->out), nl->seq_used);
376     nl->seq_sent = nl->seq_used;
377     abuf_clear(&nl->out);
378
379     olsr_socket_set_write(&nl->socket, false);
380   }
381 }
382
383 /**
384  * Cleanup netlink handler because all outstanding jobs
385  * are finished
386  * @param nl pointer to os_system_netlink handler
387  */
388 static void
389 _netlink_job_finished(struct os_system_netlink *nl) {
390   if (nl->msg_in_transit > 0) {
391     nl->msg_in_transit--;
392   }
393   if (nl->msg_in_transit == 0) {
394     olsr_timer_stop(nl->timeout);
395     nl->timeout= NULL;
396     nl->seq_used = 0;
397   }
398 }
399
400 /**
401  * Handler for incoming netlink messages
402  * @param fd
403  * @param data
404  * @param event_read
405  * @param event_write
406  */
407 static void
408 _netlink_handler(int fd, void *data, bool event_read, bool event_write) {
409   struct os_system_netlink *nl;
410   struct nlmsghdr *nh;
411   ssize_t ret;
412   size_t len;
413
414   OLSR_DEBUG(LOG_OS_SYSTEM, "Got netlink message (%d/%d)", event_read, event_write);
415   nl = data;
416   if (event_write) {
417     _flush_netlink_buffer(nl);
418   }
419
420   if (!event_read) {
421     return;
422   }
423
424   /* handle incoming messages */
425   _netlink_rcv_iov.iov_base = nl->in;
426   _netlink_rcv_iov.iov_len = UIO_MAXIOV;
427   if ((ret = recvmsg(fd, &_netlink_rcv_msg, MSG_DONTWAIT)) < 0) {
428     if (errno != EAGAIN) {
429       OLSR_WARN(LOG_OS_SYSTEM,"netlink recvmsg error: %s (%d)\n",
430           strerror(errno), errno);
431     }
432     return;
433   }
434
435   /* loop through netlink headers */
436   len = (size_t) ret;
437   for (nh = nl->in; NLMSG_OK (nh, len); nh = NLMSG_NEXT (nh, len)) {
438     OLSR_DEBUG(LOG_OS_SYSTEM,
439         "Netlink message received: type %d\n", nl->in->nlmsg_type);
440
441     switch (nh->nlmsg_type) {
442       case NLMSG_NOOP:
443         break;
444
445       case NLMSG_DONE:
446         OLSR_DEBUG(LOG_OS_SYSTEM, "Netlink message done: %d", nh->nlmsg_seq);
447         if (nl->cb_done) {
448           nl->cb_done(nh->nlmsg_seq);
449         }
450         _netlink_job_finished(nl);
451         /* End of a multipart netlink message reached */
452         return;
453
454       case NLMSG_ERROR:
455         /* Feedback for async netlink message */
456         _handle_nl_err(nl, nh);
457         break;
458
459       default:
460         if (nl->cb_message) {
461           nl->cb_message(nh);
462         }
463         break;
464     }
465   }
466 }
467
468 /**
469  * Handle incoming rtnetlink multicast messages for interface listeners
470  * @param hdr pointer to netlink message
471  */
472 static void
473 _handle_rtnetlink(struct nlmsghdr *hdr) {
474   struct ifinfomsg *ifi;
475   struct ifaddrmsg *ifa;
476
477   char if_name[IF_NAMESIZE];
478
479   if (hdr->nlmsg_type == RTM_NEWLINK || hdr->nlmsg_type == RTM_DELLINK) {
480     ifi = (struct ifinfomsg *) NLMSG_DATA(hdr);
481
482     if (if_indextoname(ifi->ifi_index, if_name) == NULL) {
483       OLSR_WARN(LOG_OS_SYSTEM,
484           "Failed to convert if-index to name: %d", ifi->ifi_index);
485       return;
486     }
487
488     OLSR_DEBUG(LOG_OS_SYSTEM, "Linkstatus of interface '%s' changed", if_name);
489     olsr_interface_trigger_change(if_name);
490   }
491
492   else if (hdr->nlmsg_type == RTM_NEWADDR || hdr->nlmsg_type == RTM_DELADDR) {
493     ifa = (struct ifaddrmsg *) NLMSG_DATA(hdr);
494
495     if (if_indextoname(ifa->ifa_index, if_name) == NULL) {
496       OLSR_WARN(LOG_OS_SYSTEM,
497           "Failed to convert if-index to name: %d", ifa->ifa_index);
498       return;
499     }
500
501     OLSR_DEBUG(LOG_OS_SYSTEM, "Address of interface '%s' changed", if_name);
502     olsr_interface_trigger_change(if_name);
503   }
504 }
505
506 /**
507  * Handle result code in netlink message
508  * @param nl pointer to netlink handler
509  * @param nh pointer to netlink message
510  */
511 static void
512 _handle_nl_err(struct os_system_netlink *nl, struct nlmsghdr *nh) {
513   struct nlmsgerr *err;
514
515   err = (struct nlmsgerr *) NLMSG_DATA(nh);
516
517   OLSR_DEBUG(LOG_OS_SYSTEM, "Received netlink feedback (%u bytes): %s (%d)",
518       nh->nlmsg_len, strerror(-err->error), err->error);
519
520   if (nl->cb_error) {
521     nl->cb_error(err->msg.nlmsg_seq, err->error);
522   }
523   _netlink_job_finished(nl);
524 }