More cleanup work of the timer API
[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 #include <sys/time.h>
54 #include <time.h>
55
56 #include "common/common_types.h"
57 #include "common/string.h"
58 #include "olsr_interface.h"
59 #include "olsr_socket.h"
60 #include "olsr.h"
61 #include "os_system.h"
62
63 #define OS_SYSTEM_NETLINK_TIMEOUT 100
64
65 static void _cb_handle_netlink_timerout(void *);
66 static void _netlink_handler(int fd, void *data,
67     bool event_read, bool event_write);
68 static void _handle_rtnetlink(struct nlmsghdr *hdr);
69
70 static void _handle_nl_err(struct os_system_netlink *, struct nlmsghdr *);
71
72 /* ioctl socket */
73 static int _ioctl_fd = -1;
74
75 /* static buffers for receiving/sending a netlink message */
76 static struct sockaddr_nl _netlink_nladdr = {
77   .nl_family = AF_NETLINK
78 };
79
80 static struct iovec _netlink_rcv_iov;
81 static struct msghdr _netlink_rcv_msg = {
82   &_netlink_nladdr,
83   sizeof(_netlink_nladdr),
84   &_netlink_rcv_iov,
85   1,
86   NULL,
87   0,
88   0
89 };
90
91 static struct nlmsghdr _netlink_hdr_done = {
92   .nlmsg_len = sizeof(struct nlmsghdr),
93   .nlmsg_type = NLMSG_DONE
94 };
95
96 static struct iovec _netlink_send_iov[2] = {
97     { NULL, 0 },
98     { &_netlink_hdr_done, sizeof(_netlink_hdr_done) },
99 };
100
101 static struct msghdr _netlink_send_msg = {
102   &_netlink_nladdr,
103   sizeof(_netlink_nladdr),
104   &_netlink_send_iov[0],
105   2,
106   NULL,
107   0,
108   0
109 };
110
111 /* netlink timeout handling */
112 static struct olsr_timer_info _netlink_timer= {
113   .name = "rtnetlink feedback timer",
114   .callback = _cb_handle_netlink_timerout,
115 };
116
117 /* built in rtnetlink multicast receiver */
118 static struct os_system_netlink _rtnetlink_receiver;
119
120 OLSR_SUBSYSTEM_STATE(_os_system_state);
121
122 /**
123  * Initialize os-specific subsystem
124  * @return -1 if an error happened, 0 otherwise
125  */
126 int
127 os_system_init(void) {
128   if (olsr_subsystem_is_initialized(&_os_system_state)) {
129     return 0;
130   }
131
132   _ioctl_fd = socket(AF_INET, SOCK_DGRAM, 0);
133   if (_ioctl_fd == -1) {
134     OLSR_WARN(LOG_OS_SYSTEM, "Cannot open ioctl socket: %s (%d)",
135         strerror(errno), errno);
136     return -1;
137   }
138
139   if (os_system_netlink_add(&_rtnetlink_receiver, NETLINK_ROUTE,
140       RTMGRP_LINK | RTMGRP_IPV4_IFADDR | RTMGRP_IPV6_IFADDR)) {
141     close(_ioctl_fd);
142     return -1;
143   }
144   _rtnetlink_receiver.cb_message = _handle_rtnetlink;
145
146   olsr_timer_add(&_netlink_timer);
147
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   olsr_timer_remove(&_netlink_timer);
161   os_system_netlink_remove(&_rtnetlink_receiver);
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   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 rtattr *rta;
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 = RTA_LENGTH(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   rta = (struct rtattr *)((void*)((char *)n + aligned_msg_len));
330   rta->rta_type = type;
331   rta->rta_len = aligned_attr_len;
332
333   n->nlmsg_len = aligned_msg_len + aligned_attr_len;
334
335   memcpy(RTA_DATA(rta), data, len);
336   return 0;
337 }
338
339 /**
340  * Handle timeout of netlink acks
341  * @param ptr pointer to netlink handler
342  */
343 static void
344 _cb_handle_netlink_timerout(void *ptr) {
345   struct os_system_netlink *nl = ptr;
346
347   if (nl->cb_timeout) {
348     nl->cb_timeout();
349   }
350
351   nl->seq_used = 0;
352 }
353
354 /**
355  * Send all netlink messages in the outgoing queue to the kernel
356  * @param nl pointer to netlink handler
357  */
358 static void
359 _flush_netlink_buffer(struct os_system_netlink *nl) {
360   ssize_t ret;
361
362   /* start feedback timer */
363   olsr_timer_set(&nl->timeout, OS_SYSTEM_NETLINK_TIMEOUT);
364
365   /* send outgoing message */
366   _netlink_send_iov[0].iov_base = abuf_getptr(&nl->out);
367   _netlink_send_iov[0].iov_len = abuf_getlen(&nl->out);
368
369   if ((ret = sendmsg(nl->socket.fd, &_netlink_send_msg, 0)) <= 0) {
370     OLSR_WARN(LOG_OS_SYSTEM,
371         "Cannot send data to netlink socket (%d: %s)",
372         errno, strerror(errno));
373   }
374   else {
375     OLSR_DEBUG(LOG_OS_SYSTEM, "Sent %zd/%zu bytes for netlink seqno: %d",
376         ret, abuf_getlen(&nl->out), nl->seq_used);
377     nl->seq_sent = nl->seq_used;
378     abuf_clear(&nl->out);
379
380     olsr_socket_set_write(&nl->socket, false);
381   }
382 }
383
384 /**
385  * Cleanup netlink handler because all outstanding jobs
386  * are finished
387  * @param nl pointer to os_system_netlink handler
388  */
389 static void
390 _netlink_job_finished(struct os_system_netlink *nl) {
391   if (nl->msg_in_transit > 0) {
392     nl->msg_in_transit--;
393   }
394   if (nl->msg_in_transit == 0) {
395     olsr_timer_stop(&nl->timeout);
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 }