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