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