Remove netlink commands that trigger an error from the outgoing queue
[oonf.git] / src-plugins / subsystems / os_linux / os_system_linux.c
1
2 /*
3  * The olsr.org Optimized Link-State Routing daemon version 2 (olsrd2)
4  * Copyright (c) 2004-2015, 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 /**
43  * @file
44  */
45
46 /* must be first because of a problem with linux/rtnetlink.h */
47 #include <sys/socket.h>
48
49 /* and now the rest of the includes */
50 #include <linux/types.h>
51 #include <linux/netlink.h>
52 #include <linux/rtnetlink.h>
53 #include <linux/socket.h>
54 #include <net/if.h>
55 #include <netinet/in.h>
56 #include <sys/ioctl.h>
57 #include <sys/uio.h>
58 #include <errno.h>
59 #include <unistd.h>
60 #include <sys/time.h>
61 #include <sys/utsname.h>
62 #include <time.h>
63
64 #include "common/common_types.h"
65 #include "common/string.h"
66 #include "core/oonf_subsystem.h"
67 #include "subsystems/oonf_socket.h"
68
69 #include "subsystems/os_system.h"
70 #include "subsystems/os_linux/os_system_linux.h"
71
72 #include <stdio.h>
73
74 #ifndef SOL_NETLINK
75 /*! socket netlink type */
76 #define SOL_NETLINK 270
77 #endif
78
79 /* Definitions */
80 #define LOG_OS_SYSTEM _oonf_os_system_subsystem.logging
81
82 /* prototypes */
83 static int _init(void);
84 static void _cleanup(void);
85
86 static void _cb_handle_netlink_timeout(struct oonf_timer_instance *);
87 static void _netlink_handler(struct oonf_socket_entry *entry);
88 static void _enqueue_netlink_buffer(struct os_system_netlink *nl);
89 static void _handle_nl_err(struct os_system_netlink *, struct nlmsghdr *);
90 static void _flush_netlink_buffer(struct os_system_netlink *nl);
91
92 /* static buffers for receiving/sending a netlink message */
93 static struct sockaddr_nl _netlink_nladdr = {
94   .nl_family = AF_NETLINK
95 };
96
97 static struct iovec _netlink_rcv_iov;
98 static struct msghdr _netlink_rcv_msg = {
99   &_netlink_nladdr,
100   sizeof(_netlink_nladdr),
101   &_netlink_rcv_iov,
102   1,
103   NULL,
104   0,
105   0
106 };
107
108 static struct nlmsghdr _netlink_hdr_done = {
109   .nlmsg_len = sizeof(struct nlmsghdr),
110   .nlmsg_type = NLMSG_DONE
111 };
112
113 static struct iovec _netlink_send_iov[] = {
114     { NULL, 0 },
115     { &_netlink_hdr_done, sizeof(_netlink_hdr_done) },
116 };
117
118 static struct msghdr _netlink_send_msg = {
119   &_netlink_nladdr,
120   sizeof(_netlink_nladdr),
121   _netlink_send_iov,
122   ARRAYSIZE(_netlink_send_iov),
123   NULL,
124   0,
125   0
126 };
127
128 /* netlink timeout handling */
129 static struct oonf_timer_class _netlink_timer= {
130   .name = "netlink feedback timer",
131   .callback = _cb_handle_netlink_timeout,
132 };
133
134 /* subsystem definition */
135 static const char *_dependencies[] = {
136   OONF_SOCKET_SUBSYSTEM,
137 };
138
139 static struct oonf_subsystem _oonf_os_system_subsystem = {
140   .name = OONF_OS_SYSTEM_SUBSYSTEM,
141   .dependencies = _dependencies,
142   .dependencies_count = ARRAYSIZE(_dependencies),
143   .init = _init,
144   .cleanup = _cleanup,
145 };
146 DECLARE_OONF_PLUGIN(_oonf_os_system_subsystem);
147
148 /* tracking of used netlink sequence numbers */
149 static uint32_t _seq_used = 0;
150
151 /* global ioctl sockets for ipv4 and ipv6 */
152 static int _ioctl_v4, _ioctl_v6;
153
154 /* empty netlink buffer */
155 static struct os_system_netlink_buffer _dummy_buffer;
156
157 /**
158  * Initialize os-specific subsystem
159  * @return -1 if an error happened, 0 otherwise
160  */
161 static int
162 _init(void) {
163   _ioctl_v4 = socket(AF_INET, SOCK_DGRAM, 0);
164   if (_ioctl_v4 == -1) {
165     OONF_WARN(LOG_OS_SYSTEM, "Cannot open ipv4 ioctl socket: %s (%d)",
166         strerror(errno), errno);
167     return -1;
168   }
169
170   _ioctl_v6 = socket(AF_INET6, SOCK_DGRAM, 0);
171   if (_ioctl_v6 == -1) {
172     OONF_INFO(LOG_OS_SYSTEM, "Node is not IPv6 capable");
173   }
174
175   oonf_timer_add(&_netlink_timer);
176   return 0;
177 }
178
179 /**
180  * Cleanup os-specific subsystem
181  */
182 static void
183 _cleanup(void) {
184   oonf_timer_remove(&_netlink_timer);
185   close (_ioctl_v4);
186   if (_ioctl_v6 != -1) {
187     close (_ioctl_v6);
188   }
189 }
190
191 /**
192  * @return true if IPv6 is supported, false otherwise
193  */
194 bool
195 os_system_linux_is_ipv6_supported(void) {
196   return _ioctl_v6 != -1;
197 }
198
199 /**
200  * @param v1 first version number part
201  * @param v2 second version number part
202  * @param v3 third version number part
203  * @return true if linux kernel is at least a specific version
204  */
205 bool
206 os_system_linux_is_minimal_kernel(int v1, int v2, int v3) {
207   struct utsname uts;
208   char *next;
209   int first = 0, second = 0, third = 0;
210
211   memset(&uts, 0, sizeof(uts));
212   if (uname(&uts)) {
213     OONF_WARN(LOG_OS_SYSTEM,
214         "Error, could not read kernel version: %s (%d)\n",
215         strerror(errno), errno);
216     return false;
217   }
218
219   first = strtol(uts.release, &next, 10);
220   /* check for linux 3.x */
221   if (first > v1) {
222     return true;
223   }
224   else if (first < v1) {
225     return false;
226   }
227
228   if (*next != '.') {
229     goto kernel_parse_error;
230   }
231
232   second = strtol(next+1, &next, 10);
233   if (second > v2) {
234     return true;
235   }
236   if (second < v2) {
237     return false;
238   }
239   if (*next != '.') {
240     goto kernel_parse_error;
241   }
242
243   third = strtol(next+1, NULL, 10);
244   return third >= v3;
245
246 kernel_parse_error:
247   OONF_WARN(LOG_OS_SYSTEM,
248       "Error, cannot parse kernel version: %s\n", uts.release);
249   return false;
250 }
251
252 /**
253  * Returns an operation system socket for ioctl usage
254  * @param af_type address family type
255  * @return socket file descriptor, -1 if not surrported
256  */
257 int
258 os_system_linux_linux_get_ioctl_fd(int af_type) {
259   switch (af_type) {
260     case AF_INET:
261       return _ioctl_v4;
262     case AF_INET6:
263       return _ioctl_v6;
264     default:
265       return -1;
266   }
267 }
268
269 /**
270  * Open a new bidirectional netlink socket
271  * @param nl pointer to initialized netlink socket handler
272  * @param protocol protocol id (NETLINK_ROUTING for example)
273  * @return -1 if an error happened, 0 otherwise
274  */
275 int
276 os_system_linux_netlink_add(struct os_system_netlink *nl, int protocol) {
277   struct sockaddr_nl addr;
278   int recvbuf;
279   int fd;
280
281   fd = socket(PF_NETLINK, SOCK_RAW, protocol);
282   if (fd < 0) {
283     OONF_WARN(nl->used_by->logging, "Cannot open netlink socket '%s': %s (%d)",
284         nl->name, strerror(errno), errno);
285     goto os_add_netlink_fail;
286   }
287
288   if (os_fd_init(&nl->socket.fd, fd)) {
289     OONF_WARN(nl->used_by->logging, "Could not initialize socket representation");
290     goto os_add_netlink_fail;
291   }
292   if (abuf_init(&nl->out)) {
293     OONF_WARN(nl->used_by->logging, "Not enough memory for"
294         " netlink '%s'output buffer", nl->name);
295     goto os_add_netlink_fail;
296   }
297   abuf_memcpy(&nl->out, &_dummy_buffer, sizeof(_dummy_buffer));
298
299   nl->in = calloc(1, getpagesize());
300   if (nl->in == NULL) {
301     OONF_WARN(nl->used_by->logging, "Not enough memory for netlink '%s' input buffer",
302         nl->name);
303     goto os_add_netlink_fail;
304   }
305   nl->in_len = getpagesize();
306
307   memset(&addr, 0, sizeof(addr));
308   addr.nl_family = AF_NETLINK;
309
310 #if defined(SO_RCVBUF)
311   recvbuf = 65536*16;
312   if (setsockopt(nl->socket.fd.fd, SOL_SOCKET, SO_RCVBUF,
313       &recvbuf, sizeof(recvbuf))) {
314     OONF_WARN(nl->used_by->logging, "Cannot setup receive buffer size for"
315         " netlink socket '%s': %s (%d)\n", nl->name, strerror(errno), errno);
316   }
317 #endif
318
319   if (bind(nl->socket.fd.fd, (struct sockaddr *)&addr, sizeof(addr))<0) {
320     OONF_WARN(nl->used_by->logging, "Could not bind netlink socket %s: %s (%d)",
321         nl->name, strerror(errno), errno);
322     goto os_add_netlink_fail;
323   }
324
325   nl->socket.name = "os_system_netlink";
326   nl->socket.process = _netlink_handler;
327   oonf_socket_add(&nl->socket);
328   oonf_socket_set_read(&nl->socket, true);
329
330   nl->timeout.class = &_netlink_timer;
331
332   list_init_head(&nl->buffered);
333   return 0;
334
335 os_add_netlink_fail:
336   os_fd_invalidate(&nl->socket.fd);
337   if (fd != -1) {
338     close(fd);
339   }
340   free (nl->in);
341   abuf_free(&nl->out);
342   fd = -1;
343   return -1;
344 }
345
346 /**
347  * Close a netlink socket handler
348  * @param nl pointer to handler
349  */
350 void
351 os_system_linux_netlink_remove(struct os_system_netlink *nl) {
352   if (os_fd_is_initialized(&nl->socket.fd)) {
353     oonf_socket_remove(&nl->socket);
354
355     os_fd_close(&nl->socket.fd);
356     free (nl->in);
357     abuf_free(&nl->out);
358   }
359 }
360
361 /**
362  * add netlink message to buffer
363  * @param nl netlink message
364  */
365 static void
366 _enqueue_netlink_buffer(struct os_system_netlink *nl) {
367   struct os_system_netlink_buffer *bufptr;
368
369   /* initialize new buffer */
370   bufptr = (struct os_system_netlink_buffer *)abuf_getptr(&nl->out);
371   bufptr->total = abuf_getlen(&nl->out) - sizeof(*bufptr);
372   bufptr->messages = nl->out_messages;
373
374   /* append to end of queue */
375   list_add_tail(&nl->buffered, &bufptr->_node);
376   nl->out_messages = 0;
377
378   /* get a new outgoing buffer */
379   abuf_init(&nl->out);
380   abuf_memcpy(&nl->out, &_dummy_buffer, sizeof(_dummy_buffer));
381 }
382
383 /**
384  * Add a netlink message to the outgoign queue of a handler
385  * @param nl pointer to netlink handler
386  * @param nl_hdr pointer to netlink message
387  * @return sequence number used for message
388  */
389 int
390 os_system_linux_netlink_send(struct os_system_netlink *nl,
391     struct nlmsghdr *nl_hdr) {
392   _seq_used = (_seq_used + 1) & INT32_MAX;
393   OONF_DEBUG(nl->used_by->logging, "Prepare to send netlink '%s' message %u (%u bytes)",
394       nl->name, _seq_used, nl_hdr->nlmsg_len);
395
396   nl_hdr->nlmsg_seq = _seq_used;
397   nl_hdr->nlmsg_flags |= NLM_F_ACK | NLM_F_MULTI;
398
399   if (nl_hdr->nlmsg_len + abuf_getlen(&nl->out) > (size_t)getpagesize()) {
400     _enqueue_netlink_buffer(nl);
401   }
402   abuf_memcpy(&nl->out, nl_hdr, nl_hdr->nlmsg_len);
403
404   OONF_DEBUG_HEX(nl->used_by->logging, nl_hdr, nl_hdr->nlmsg_len,
405       "Content of netlink '%s' message:", nl->name);
406   
407   nl->out_messages++;
408
409   /* trigger write */
410   if (nl->msg_in_transit == 0) {
411     oonf_socket_set_write(&nl->socket, true);
412   }
413   return _seq_used;
414 }
415
416 /**
417  * Join a list of multicast groups for a netlink socket
418  * @param nl pointer to netlink handler
419  * @param groups pointer to array of multicast groups
420  * @param groupcount number of entries in groups array
421  * @return -1 if an error happened, 0 otherwise
422  */
423 int
424 os_system_linux_netlink_add_mc(struct os_system_netlink *nl,
425     const uint32_t *groups, size_t groupcount) {
426   size_t i;
427
428   for (i=0; i<groupcount; i++) {
429     if (setsockopt(os_fd_get_fd(&nl->socket.fd),
430         SOL_NETLINK, NETLINK_ADD_MEMBERSHIP,
431              &groups[i], sizeof(groups[i]))) {
432       OONF_WARN(nl->used_by->logging,
433           "Could not join netlink '%s' mc group: %x", nl->name, groups[i]);
434       return -1;
435     }
436   }
437   return 0;
438 }
439
440 /**
441  * Leave a list of multicast groups for a netlink socket
442  * @param nl pointer to netlink handler
443  * @param groups pointer to array of multicast groups
444  * @param groupcount number of entries in groups array
445  * @return -1 if an error happened, 0 otherwise
446  */
447 int
448 os_system_linux_netlink_drop_mc(struct os_system_netlink *nl,
449     const int *groups, size_t groupcount) {
450   size_t i;
451
452   for (i=0; i<groupcount; i++) {
453     if (setsockopt(os_fd_get_fd(&nl->socket.fd),
454         SOL_NETLINK, NETLINK_DROP_MEMBERSHIP,
455              &groups[i], sizeof(groups[i]))) {
456       OONF_WARN(nl->used_by->logging,
457           "Could not drop netlink '%s' mc group: %x", nl->name, groups[i]);
458       return -1;
459     }
460   }
461   return 0;
462 }
463
464 /**
465  * Add an attribute to a netlink message
466  * @param nl pinter to os netlink handler
467  * @param nlmsg pointer to netlink header
468  * @param type type of netlink attribute
469  * @param data pointer to data of netlink attribute
470  * @param len length of data of netlink attribute
471  * @return -1 if netlink message got too large, 0 otherwise
472  */
473 int
474 os_system_linux_netlink_addreq(struct os_system_netlink *nl,
475     struct nlmsghdr *nlmsg, int type, const void *data, int len) {
476   struct nlattr *nl_attr;
477   size_t aligned_msg_len, aligned_attr_len;
478
479   /* calculate aligned length of message and new attribute */
480   aligned_msg_len = NLMSG_ALIGN(nlmsg->nlmsg_len);
481   aligned_attr_len = NLA_HDRLEN + len;
482
483   if (aligned_msg_len + aligned_attr_len > UIO_MAXIOV) {
484     OONF_WARN(LOG_OS_SYSTEM, "Netlink '%s' message got too large!", nl->name);
485     return -1;
486   }
487
488   nl_attr = (struct nlattr *) ((void*)((char *)nlmsg + aligned_msg_len));
489   nl_attr->nla_type = type;
490   nl_attr->nla_len = aligned_attr_len;
491
492   /* fix length of netlink message */
493   nlmsg->nlmsg_len = aligned_msg_len + aligned_attr_len;
494
495   if (len) {
496     memcpy((char *)nl_attr + NLA_HDRLEN, data, len);
497   }
498   return 0;
499 }
500
501 /**
502  * Handle timeout of netlink acks
503  * @param ptr timer instance that fired
504  */
505 static void
506 _cb_handle_netlink_timeout(struct oonf_timer_instance *ptr) {
507   struct os_system_netlink *nl;
508
509   nl = container_of(ptr, struct os_system_netlink, timeout);
510
511   if (nl->cb_timeout) {
512     nl->cb_timeout();
513   }
514   nl->msg_in_transit = 0;
515 }
516
517 /**
518  * Send all netlink messages in the outgoing queue to the kernel
519  * @param nl pointer to netlink handler
520  */
521 static void
522 _flush_netlink_buffer(struct os_system_netlink *nl) {
523   struct os_system_netlink_buffer *buffer;
524   ssize_t ret;
525   int err;
526
527   if (nl->msg_in_transit > 0) {
528     oonf_socket_set_write(&nl->socket, false);
529     return;
530   }
531
532   if (list_is_empty(&nl->buffered)) {
533     if (abuf_getlen(&nl->out) > sizeof(struct os_system_netlink_buffer)) {
534       _enqueue_netlink_buffer(nl);
535     }
536     else {
537       oonf_socket_set_write(&nl->socket, false);
538       return;
539     }
540   }
541
542   /* get first buffer */
543   buffer = list_first_element(&nl->buffered, buffer, _node);
544
545   /* send outgoing message */
546   _netlink_send_iov[0].iov_base = (char *)(buffer) + sizeof(*buffer);
547   _netlink_send_iov[0].iov_len = buffer->total;
548
549   if ((ret = sendmsg(os_fd_get_fd(&nl->socket.fd),
550         &_netlink_send_msg, MSG_DONTWAIT)) <= 0) {
551     err = errno;
552 #if EAGAIN == EWOULDBLOCK
553     if (err != EAGAIN) {
554 #else
555     if (err != EAGAIN && err != EWOULDBLOCK) {
556 #endif
557       OONF_WARN(nl->used_by->logging,
558           "Cannot send data (%"PRINTF_SIZE_T_SPECIFIER" bytes)"
559           " to netlink socket %s: %s (%d)",
560           abuf_getlen(&nl->out), nl->name, strerror(err), err);
561
562       /* remove netlink message from internal queue */
563       nl->cb_error(nl->in->nlmsg_seq, err);
564     }
565   }
566   else {
567     nl->msg_in_transit += buffer->messages;
568
569     OONF_DEBUG(nl->used_by->logging,
570         "netlink %s: Sent %u bytes (%u messages in transit)",
571         nl->name, buffer->total, nl->msg_in_transit);
572
573     /* start feedback timer */
574     oonf_timer_set(&nl->timeout, OS_SYSTEM_NETLINK_TIMEOUT);
575   }
576
577   list_remove(&buffer->_node);
578   free(buffer);
579
580   oonf_socket_set_write(&nl->socket, !list_is_empty(&nl->buffered));
581 }
582
583 /**
584  * Cleanup netlink handler because all outstanding jobs
585  * are finished
586  * @param nl pointer to os_system_netlink handler
587  */
588 static void
589 _netlink_job_finished(struct os_system_netlink *nl) {
590   if (nl->msg_in_transit > 0) {
591     nl->msg_in_transit--;
592   }
593   if (nl->msg_in_transit == 0) {
594     oonf_timer_stop(&nl->timeout);
595
596     if (!list_is_empty(&nl->buffered)
597         || nl->out_messages > 0) {
598       oonf_socket_set_write(&nl->socket, true);
599     }
600   }
601   OONF_DEBUG(nl->used_by->logging, "netlink '%s' finished: %d still in transit",
602       nl->name, nl->msg_in_transit);
603 }
604
605 /**
606  * Handler for incoming netlink messages
607  * @param entry OONF socket entry creating the callback
608  */
609 static void
610 _netlink_handler(struct oonf_socket_entry *entry) {
611   struct os_system_netlink *nl;
612   struct nlmsghdr *nh;
613   ssize_t ret;
614   size_t len;
615   int flags;
616   uint32_t current_seq = 0;
617   bool trigger_is_done;
618
619   nl = container_of(entry, typeof(*nl), socket);
620   if (oonf_socket_is_write(entry)) {
621     _flush_netlink_buffer(nl);
622   }
623
624   if (!oonf_socket_is_read(entry)) {
625     return;
626   }
627
628   /* handle incoming messages */
629   _netlink_rcv_msg.msg_flags = 0;
630   flags = MSG_PEEK;
631
632 netlink_rcv_retry:
633   _netlink_rcv_iov.iov_base = nl->in;
634   _netlink_rcv_iov.iov_len = nl->in_len;
635
636   OONF_DEBUG(nl->used_by->logging, "Read netlink '%s' message with"
637       " %"PRINTF_SIZE_T_SPECIFIER" bytes buffer",
638       nl->name, nl->in_len);
639   if ((ret = recvmsg(entry->fd.fd, &_netlink_rcv_msg, MSG_DONTWAIT | flags)) < 0) {
640 #if EAGAIN == EWOULDBLOCK
641     if (errno != EAGAIN) {
642 #else
643     if (errno != EAGAIN && errno != EWOULDBLOCK) {
644 #endif
645       OONF_WARN(nl->used_by->logging,"netlink '%s' recvmsg error: %s (%d)\n",
646           nl->name, strerror(errno), errno);
647     }
648     else {
649       oonf_socket_set_read(&nl->socket, true);
650     }
651     return;
652   }
653
654   /* not enough buffer space ? */
655   if (nl->in_len < (size_t)ret || (_netlink_rcv_msg.msg_flags & MSG_TRUNC) != 0) {
656     void *ptr;
657
658     ret = ret / getpagesize();
659     ret++;
660     ret *= getpagesize();
661
662     ptr = realloc(nl->in, ret);
663     if (!ptr) {
664       OONF_WARN(nl->used_by->logging, "Not enough memory to"
665           " increase netlink '%s' input buffer", nl->name);
666       return;
667     }
668     nl->in = ptr;
669     nl->in_len = ret;
670     goto netlink_rcv_retry;
671   }
672   if (flags) {
673     /* it worked, not remove the message from the queue */
674     flags = 0;
675     OONF_DEBUG(nl->used_by->logging, "Got estimate of netlink '%s'"
676         " message size, retrieve it", nl->name);
677     goto netlink_rcv_retry;
678   }
679
680   OONF_DEBUG(nl->used_by->logging, "Got netlink '%s' message of %"
681       PRINTF_SSIZE_T_SPECIFIER" bytes", nl->name, ret);
682   OONF_DEBUG_HEX(nl->used_by->logging, nl->in, ret,
683       "Content of netlink '%s' message:", nl->name);
684   
685   trigger_is_done = false;
686
687   /* loop through netlink headers */
688   len = (size_t) ret;
689   for (nh = nl->in; NLMSG_OK (nh, len); nh = NLMSG_NEXT (nh, len)) {
690     OONF_DEBUG(nl->used_by->logging,
691         "Netlink '%s' message received: type %d seq %u\n",
692         nl->name, nh->nlmsg_type, nh->nlmsg_seq);
693
694     if (nh == nl->in) {
695       current_seq = nh->nlmsg_seq;
696     }
697
698     if (current_seq != nh->nlmsg_seq && trigger_is_done) {
699       if (nl->cb_done) {
700         nl->cb_done(current_seq);
701       }
702       trigger_is_done = false;
703     }
704
705     switch (nh->nlmsg_type) {
706       case NLMSG_NOOP:
707         break;
708
709       case NLMSG_DONE:
710         /* End of a multipart netlink message reached */
711         trigger_is_done = true;
712         break;
713
714       case NLMSG_ERROR:
715         /* Feedback for async netlink message */
716         trigger_is_done = false;
717         _handle_nl_err(nl, nh);
718         break;
719
720       default:
721         if (nl->cb_message) {
722           nl->cb_message(nh);
723         }
724         break;
725     }
726   }
727
728   if (trigger_is_done) {
729     oonf_timer_stop(&nl->timeout);
730     if (nl->cb_done) {
731       nl->cb_done(current_seq);
732     }
733     _netlink_job_finished(nl);
734   }
735
736   /* reset timeout if necessary */
737   if (oonf_timer_is_active(&nl->timeout)) {
738     oonf_timer_set(&nl->timeout, OS_SYSTEM_NETLINK_TIMEOUT);
739   }
740 }
741
742 /**
743  * Handle result code in netlink message
744  * @param nl pointer to netlink handler
745  * @param nh pointer to netlink message
746  */
747 static void
748 _handle_nl_err(struct os_system_netlink *nl, struct nlmsghdr *nh) {
749   struct nlmsgerr *err;
750
751   err = (struct nlmsgerr *) NLMSG_DATA(nh);
752
753   OONF_DEBUG(nl->used_by->logging,
754       "Received netlink '%s' seq %u feedback (%u bytes): %s (%d)",
755       nl->name, nh->nlmsg_seq, nh->nlmsg_len, strerror(-err->error), -err->error);
756
757   if (err->error) {
758     if (nl->cb_error) {
759       nl->cb_error(err->msg.nlmsg_seq, -err->error);
760     }
761   }
762   else {
763     if (nl->cb_done) {
764       nl->cb_done(err->msg.nlmsg_seq);
765     }
766   }
767
768   _netlink_job_finished(nl);
769 }