f4217edfe64972e1e560bb75c9df0f17c9357166
[oonf.git] / src / generic / nl80211_listener / nl80211_listener.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 #define _GNU_SOURCE
47
48 /* must be first because of a problem with linux/netlink.h */
49 #include <sys/socket.h>
50
51 /* and now the rest of the includes */
52 #include <linux/genetlink.h>
53 #include <linux/netlink.h>
54 #include <linux/types.h>
55 #include <netlink/attr.h>
56 #include <netlink/msg.h>
57 #include <sys/uio.h>
58
59 #include <oonf/libcommon/autobuf.h>
60 #include <oonf/libcommon/avl.h>
61 #include <oonf/libcommon/avl_comp.h>
62 #include <oonf/oonf.h>
63 #include <oonf/libcommon/netaddr.h>
64 #include <oonf/libcommon/netaddr_acl.h>
65 #include <oonf/libcommon/string.h>
66
67 #include <oonf/libconfig/cfg.h>
68 #include <oonf/libconfig/cfg_schema.h>
69 #include <oonf/libcore/oonf_logging.h>
70 #include <oonf/libcore/oonf_subsystem.h>
71 #include <oonf/subsystems/oonf_class.h>
72 #include <oonf/subsystems/oonf_layer2.h>
73 #include <oonf/subsystems/oonf_timer.h>
74 #include <oonf/subsystems/os_interface.h>
75 #include <oonf/subsystems/os_system.h>
76
77 #include <oonf/generic/nl80211_listener/genl_get_family.h>
78 #include <oonf/generic/nl80211_listener/nl80211_get_interface.h>
79 #include <oonf/generic/nl80211_listener/nl80211_get_mpp.h>
80 #include <oonf/generic/nl80211_listener/nl80211_get_station_dump.h>
81 #include <oonf/generic/nl80211_listener/nl80211_get_survey.h>
82 #include <oonf/generic/nl80211_listener/nl80211_get_wiphy.h>
83 #include <oonf/generic/nl80211_listener/nl80211_listener.h>
84 #include <oonf/generic/nl80211_listener/nl80211.h>
85
86 /* definitions */
87
88 /**
89  * nl80211 configuration
90  */
91 struct _nl80211_config {
92   /*! interval between two series of netlink probes */
93   uint64_t interval;
94
95   /*! true if plugin should set multicast rate in the l2 db */
96   bool report_multicast_rate;
97 };
98
99 /**
100  * definition of a query to the nl80211 subsystem
101  */
102 struct _nl80211_query {
103   /*! netlink command that should be queried later */
104   uint8_t cmd;
105
106   /**
107    * Callback to send query
108    * @param nl netlink handler
109    * @param nl_msg netlink message header
110    * @param hdr generic message header
111    * @param interf netlink interface
112    */
113   void (*send)(
114     struct os_system_netlink *nl, struct nlmsghdr *nl_msg, struct genlmsghdr *hdr, struct nl80211_if *interf);
115
116   /**
117    * Callback to process incoming netlink data
118    * @param interf netlink interface
119    * @param hdr netlink message header
120    */
121   void (*process)(struct nl80211_if *interf, struct nlmsghdr *hdr);
122
123   /**
124    * Finalize the processing of the netlink query
125    * @param interf netlink interface
126    */
127   void (*finalize)(struct nl80211_if *interf);
128 };
129
130 /**
131  * Index number for nl80211 configuration entries
132  */
133 enum _nl80211_cfg_idx
134 {
135   IDX_INTERVAL,
136   IDX_INTERFACES,
137   IDX_MC_RATE,
138 };
139
140 /**
141  * index numbers for netlink queries done by listener
142  */
143 enum _if_query
144 {
145   QUERY_START = 0,       //!< QUERY_START
146   QUERY_GET_IF = 0,      //!< QUERY_GET_IF
147   QUERY_GET_WIPHY = 1,   //!< QUERY_GET_WIPHY
148   QUERY_GET_SURVEY = 2,  //!< QUERY_GET_SURVEY
149   QUERY_GET_MPP = 3,     //!< QUERY_GET_MPP
150   QUERY_GET_STATION = 4, //!< QUERY_GET_STATION
151   QUERY_END = 5,         //!< QUERY_END
152
153   QUERY_GET_FAMILY = 6, //!< QUERY_GET_FAMILY
154
155   QUERY_COUNT, //!< QUERY_COUNT
156 };
157
158 /* prototypes */
159 static void _early_cfg_init(void);
160 static int _init(void);
161 static void _cleanup(void);
162
163 static struct nl80211_if *_nl80211_if_get(const char *name);
164 static struct nl80211_if *_nl80211_if_add(const char *name);
165 static void _nl80211_if_remove(struct nl80211_if *);
166
167 static void _cb_config_changed(void);
168 static void _cb_if_config_changed(void);
169
170 static void _cb_transmission_event(struct oonf_timer_instance *);
171 static void _trigger_next_netlink_query(void);
172
173 static void _cb_nl_message(struct nlmsghdr *hdr);
174 static void _cb_nl_error(uint32_t seq, int error);
175 static void _cb_nl_timeout(void);
176 static void _cb_nl_done(uint32_t seq);
177
178 /* configuration */
179 static struct cfg_schema_section _if_section = {
180   CFG_OSIF_SCHEMA_INTERFACE_SECTION_INIT,
181
182   .cb_delta_handler = _cb_if_config_changed,
183   .entries = NULL,
184   .entry_count = 0,
185 };
186
187 static struct cfg_schema_entry _nl80211_entries[] = {
188   [IDX_INTERVAL] = CFG_MAP_CLOCK_MIN(
189     _nl80211_config, interval, "interval", "1.0", "Interval between two linklayer information updates", 100),
190   [IDX_INTERFACES] = CFG_VALIDATE_PRINTABLE_LEN(
191     "if", "", "List of additional interfaces to read nl80211 data from", IF_NAMESIZE, .list = true),
192   [IDX_MC_RATE] = CFG_MAP_BOOL(_nl80211_config, report_multicast_rate, "report_mc_rate", "false",
193     "Activate to write the multicast/broadcast speed into the layer2 database"),
194 };
195
196 static struct cfg_schema_section _nl80211_section = {
197   .type = OONF_NL80211_LISTENER_SUBSYSTEM,
198   .cb_delta_handler = _cb_config_changed,
199   .entries = _nl80211_entries,
200   .entry_count = ARRAYSIZE(_nl80211_entries),
201   .next_section = &_if_section,
202 };
203
204 static struct _nl80211_config _config;
205
206 /* plugin declaration */
207 static const char *_dependencies[] = {
208   OONF_CLASS_SUBSYSTEM,
209   OONF_LAYER2_SUBSYSTEM,
210   OONF_TIMER_SUBSYSTEM,
211   OONF_OS_INTERFACE_SUBSYSTEM,
212   OONF_OS_SYSTEM_SUBSYSTEM,
213 };
214
215 static struct oonf_subsystem _nl80211_listener_subsystem = {
216   .name = OONF_NL80211_LISTENER_SUBSYSTEM,
217   .dependencies = _dependencies,
218   .dependencies_count = ARRAYSIZE(_dependencies),
219   .descr = "OONF nl80211 listener plugin",
220   .author = "Henning Rogge",
221
222   .cfg_section = &_nl80211_section,
223
224   .early_cfg_init = _early_cfg_init,
225   .init = _init,
226   .cleanup = _cleanup,
227 };
228 DECLARE_OONF_PLUGIN(_nl80211_listener_subsystem);
229
230 enum oonf_log_source LOG_NL80211;
231
232 /* netlink specific data */
233 static struct os_system_netlink _netlink_handler = {
234   .name = "nl80211 listener",
235   .used_by = &_nl80211_listener_subsystem,
236   .cb_message = _cb_nl_message,
237   .cb_error = _cb_nl_error,
238   .cb_done = _cb_nl_done,
239   .cb_timeout = _cb_nl_timeout,
240 };
241
242 /* buffer for outgoing netlink message */
243 static uint32_t _nl_msgbuffer[UIO_MAXIOV / 4];
244 static struct nlmsghdr *_nl_msg = (void *)_nl_msgbuffer;
245
246 /* netlink nl80211 identification */
247 static uint32_t _nl80211_id = 0;
248 static uint32_t _nl80211_multicast_group = 0;
249
250 /* layer2 metadata */
251 static struct oonf_layer2_origin _layer2_updated_origin = {
252   .name = "nl80211 updated",
253   .proactive = true,
254   .priority = OONF_LAYER2_ORIGIN_RELIABLE + 1,
255 };
256 static struct oonf_layer2_origin _layer2_data_origin = {
257   .name = "nl80211",
258   .proactive = true,
259   .priority = OONF_LAYER2_ORIGIN_RELIABLE,
260 };
261
262 /* current query data */
263 static struct nl80211_if *_current_query_if = NULL;
264 static enum _if_query _current_query_number = QUERY_START;
265 static bool _current_query_in_progress = false;
266
267 /* timer for generating netlink requests */
268 static struct oonf_timer_class _transmission_timer_info = {
269   .name = "nl80211 listener timer",
270   .callback = _cb_transmission_event,
271   .periodic = true,
272 };
273
274 static struct oonf_timer_instance _transmission_timer = { .class = &_transmission_timer_info };
275
276 /* nl80211_if handling */
277 static struct avl_tree _nl80211_if_tree;
278
279 static struct oonf_class _nl80211_if_class = {
280   .name = "nl80211 if",
281   .size = sizeof(struct nl80211_if),
282 };
283
284 static const struct _nl80211_query _if_query_ops[QUERY_COUNT] = {
285   [QUERY_GET_IF] =
286     {
287       NL80211_CMD_NEW_INTERFACE,
288       nl80211_send_get_interface,
289       nl80211_process_get_interface_result,
290       NULL,
291     },
292   [QUERY_GET_WIPHY] =
293     {
294       NL80211_CMD_NEW_WIPHY,
295       nl80211_send_get_wiphy,
296       nl80211_process_get_wiphy_result,
297       nl80211_finalize_get_wiphy,
298     },
299   [QUERY_GET_SURVEY] =
300     {
301       NL80211_CMD_NEW_SURVEY_RESULTS,
302       nl80211_send_get_survey,
303       nl80211_process_get_survey_result,
304       NULL,
305     },
306   [QUERY_GET_MPP] =
307     {
308       NL80211_CMD_NEW_MPATH,
309       nl80211_send_get_mpp,
310       nl80211_process_get_mpp_result,
311       NULL,
312     },
313   [QUERY_GET_STATION] =
314     {
315       NL80211_CMD_NEW_STATION,
316       nl80211_send_get_station_dump,
317       nl80211_process_get_station_dump_result,
318       NULL,
319     },
320 };
321
322 static void
323 _early_cfg_init(void) {
324   LOG_NL80211 = _nl80211_listener_subsystem.logging;
325 }
326
327 /**
328  * Constructor of plugin
329  * @return 0 if initialization was successful, -1 otherwise
330  */
331 static int
332 _init(void) {
333   if (os_system_linux_netlink_add(&_netlink_handler, NETLINK_GENERIC)) {
334     return -1;
335   }
336
337   /* initialize nl80211 if storage system */
338   oonf_class_add(&_nl80211_if_class);
339   avl_init(&_nl80211_if_tree, avl_comp_strcasecmp, false);
340
341   /* get layer2 origin */
342   oonf_layer2_origin_add(&_layer2_updated_origin);
343   oonf_layer2_origin_add(&_layer2_data_origin);
344
345   oonf_timer_add(&_transmission_timer_info);
346   return 0;
347 }
348
349 /**
350  * Destructor of plugin
351  */
352 static void
353 _cleanup(void) {
354   struct nl80211_if *interf, *it_if;
355   avl_for_each_element_safe(&_nl80211_if_tree, interf, _node, it_if) {
356     _nl80211_if_remove(interf);
357   }
358   oonf_layer2_origin_remove(&_layer2_updated_origin);
359   oonf_layer2_origin_remove(&_layer2_data_origin);
360
361   oonf_timer_stop(&_transmission_timer);
362   oonf_timer_remove(&_transmission_timer_info);
363   os_system_linux_netlink_remove(&_netlink_handler);
364 }
365
366 /**
367  * Add a layer2 destination to the database
368  * @param l2neigh layer2 neighbor
369  * @param dstmac destination mac address
370  * @return layer2 destination
371  */
372 struct oonf_layer2_destination *
373 nl80211_add_dst(struct oonf_layer2_neigh *l2neigh, const struct netaddr *dstmac) {
374   struct oonf_layer2_destination *dst;
375
376   dst = oonf_layer2_destination_add(l2neigh, dstmac, &_layer2_updated_origin);
377   if (dst->origin == &_layer2_data_origin) {
378     dst->origin = &_layer2_updated_origin;
379   }
380   return dst;
381 }
382
383 /**
384  * Change a layer2 network setting
385  * @param l2net pointer to layer2 network
386  * @param idx index of setting
387  * @param value new value
388  * @return true if value changed, false otherwise
389  */
390 bool
391 nl80211_change_l2net_data(struct oonf_layer2_net *l2net, enum oonf_layer2_network_index idx, uint64_t value) {
392   return oonf_layer2_data_set_int64(&l2net->data[idx], &_layer2_updated_origin, value);
393 }
394
395 /**
396  * Change a layer2 network neighbor default setting
397  * @param l2net pointer to layer2 network
398  * @param idx index of setting
399  * @param value new value
400  * @return true if value changed, false otherwise
401  */
402 bool
403 nl80211_change_l2net_neighbor_default(
404   struct oonf_layer2_net *l2net, enum oonf_layer2_neighbor_index idx, uint64_t value) {
405   return oonf_layer2_data_set_int64(&l2net->neighdata[idx], &_layer2_updated_origin, value);
406 }
407
408 /**
409  * Cleanup all data generated by this listener from a layer2 neighbor,
410  * but do not commit data
411  * @param l2neigh pointer to layer2 neighbor
412  */
413 void
414 nl80211_cleanup_l2neigh_data(struct oonf_layer2_neigh *l2neigh) {
415   oonf_layer2_neigh_cleanup(l2neigh, &_layer2_data_origin);
416 }
417
418 /**
419  * Change a layer2 neighbor setting
420  * @param l2neigh pointer to layer2 neighbor
421  * @param idx index of setting
422  * @param value new value
423  * @return true if value changed, false otherwise
424  */
425 bool
426 nl80211_change_l2neigh_data(struct oonf_layer2_neigh *l2neigh, enum oonf_layer2_neighbor_index idx, uint64_t value) {
427   return oonf_layer2_data_set_int64(&l2neigh->data[idx], &_layer2_updated_origin, value);
428 }
429
430 /**
431  * @return true if plugin should create a broadcast entry neighbor
432  */
433 bool
434 nl80211_create_broadcast_neighbor(void) {
435   return _config.report_multicast_rate;
436 }
437
438 /**
439  * Get a nl80211 interface from tree
440  * @param name interface name
441  * @return nl80211 interface, NULL if not found
442  */
443 static struct nl80211_if *
444 _nl80211_if_get(const char *name) {
445   struct nl80211_if *interf;
446
447   return avl_find_element(&_nl80211_if_tree, name, interf, _node);
448 }
449
450 /**
451  * Add a nl80211 interface to the tree
452  * @param name interface name
453  * @return nl80211 interface, NULL if out of memory
454  */
455 static struct nl80211_if *
456 _nl80211_if_add(const char *name) {
457   struct nl80211_if *interf;
458
459   interf = _nl80211_if_get(name);
460   if (interf) {
461     return interf;
462   }
463
464   interf = oonf_class_malloc(&_nl80211_if_class);
465   if (!interf) {
466     return NULL;
467   }
468
469   /* initialize avl node */
470   strscpy(interf->name, name, IF_NAMESIZE);
471   interf->_node.key = interf->name;
472
473   /* initialize l2net */
474   interf->l2net = oonf_layer2_net_add(interf->name);
475   if (!interf->l2net) {
476     oonf_class_free(&_nl80211_if_class, interf);
477     return NULL;
478   }
479
480   if (interf->l2net->if_type == OONF_LAYER2_TYPE_UNDEFINED) {
481     interf->l2net->if_type = OONF_LAYER2_TYPE_WIRELESS;
482   }
483
484   /* initialize interface listener */
485   interf->if_listener.name = interf->name;
486   if (!os_interface_add(&interf->if_listener)) {
487     oonf_layer2_net_remove(interf->l2net, &_layer2_data_origin);
488     oonf_layer2_net_remove(interf->l2net, &_layer2_updated_origin);
489     oonf_class_free(&_nl80211_if_class, interf);
490     return NULL;
491   }
492
493   /* initialize interface */
494   interf->wifi_phy_if = -1;
495
496   OONF_DEBUG(LOG_NL80211, "Add if %s", name);
497   avl_insert(&_nl80211_if_tree, &interf->_node);
498   return interf;
499 }
500
501 /**
502  * Remove a nl80211 interface from tree
503  * @param interf nl80211 interface
504  */
505 static void
506 _nl80211_if_remove(struct nl80211_if *interf) {
507   avl_remove(&_nl80211_if_tree, &interf->_node);
508   os_interface_remove(&interf->if_listener);
509   oonf_class_free(&_nl80211_if_class, interf);
510 }
511
512 /**
513  * Update configuration of interface section
514  */
515 static void
516 _cb_if_config_changed(void) {
517   struct nl80211_if *interf;
518   const char *ifname;
519   char ifbuf[IF_NAMESIZE];
520
521   ifname = cfg_get_phy_if(ifbuf, _if_section.section_name);
522   if (_if_section.pre == NULL) {
523     interf = _nl80211_if_add(ifname);
524     if (interf) {
525       interf->_if_section = true;
526     }
527   }
528
529   if (_if_section.post == NULL) {
530     interf = _nl80211_if_get(ifname);
531     if (interf) {
532       interf->_if_section = false;
533       if (!interf->_nl80211_section) {
534         _nl80211_if_remove(interf);
535       }
536     }
537   }
538 }
539
540 /**
541  * Transmit the next netlink command to nl80211
542  * @param ptr timer instance that fired
543  */
544 static void
545 _cb_transmission_event(struct oonf_timer_instance *ptr __attribute__((unused))) {
546   if (!_current_query_in_progress) {
547     _trigger_next_netlink_query();
548   }
549 }
550
551 /**
552  * Send a netlink message to the nl80211 subsystem
553  * @param interf nl80211 interface for message
554  * @param query query id
555  */
556 static void
557 _send_netlink_message(struct nl80211_if *interf, enum _if_query query) {
558   struct genlmsghdr *hdr;
559
560   memset(&_nl_msgbuffer, 0, sizeof(_nl_msgbuffer));
561
562   /* generic netlink initialization */
563   _nl_msg->nlmsg_len = NLMSG_LENGTH(sizeof(struct genlmsghdr));
564   _nl_msg->nlmsg_flags = NLM_F_REQUEST;
565
566   /* request nl80211 identifier */
567   if (query == QUERY_GET_FAMILY) {
568     /* request nl80211 identifier */
569     _nl_msg->nlmsg_type = GENL_ID_CTRL;
570   }
571   else {
572     _nl_msg->nlmsg_type = _nl80211_id;
573   }
574
575   hdr = NLMSG_DATA(_nl_msg);
576
577   if (query < QUERY_END) {
578     OONF_DEBUG(LOG_NL80211, "Get query %d for interface %s", query, interf->name);
579   }
580
581   if (query == QUERY_GET_FAMILY) {
582     genl_send_get_family(_nl_msg, hdr);
583   }
584   else if (_if_query_ops[query].send) {
585     _if_query_ops[query].send(&_netlink_handler, _nl_msg, hdr, interf);
586   }
587
588   os_system_linux_netlink_send(&_netlink_handler, _nl_msg);
589 }
590
591 /**
592  * Proceed to next query
593  */
594 static void
595 _get_next_query(void) {
596   if (avl_is_empty(&_nl80211_if_tree)) {
597     OONF_DEBUG(LOG_NL80211, "No nl80211 interfaces");
598     _current_query_if = NULL;
599     return;
600   }
601
602   /* no query left to do? start again at the first */
603   if (!_current_query_if) {
604     /* start with first interface and query */
605     _current_query_if = avl_first_element(&_nl80211_if_tree, _current_query_if, _node);
606     _current_query_number = QUERY_START;
607     _current_query_in_progress = true;
608   }
609   else {
610     /* next query */
611     _current_query_number++;
612
613     if (_current_query_number == QUERY_END) {
614       /* commit interface data */
615       if (_current_query_if->ifdata_changed) {
616         /* set fixed flags for nl80211 data */
617         oonf_layer2_data_set_bool(
618           &_current_query_if->l2net->data[OONF_LAYER2_NET_MCS_BY_PROBING], &_layer2_updated_origin, true);
619
620         /* cleanup old data and relable new one, then commit everything */
621         oonf_layer2_net_cleanup(_current_query_if->l2net, &_layer2_data_origin, true);
622         oonf_layer2_net_relabel(_current_query_if->l2net, &_layer2_data_origin, &_layer2_updated_origin);
623         oonf_layer2_net_commit(_current_query_if->l2net);
624         _current_query_if->ifdata_changed = false;
625       }
626
627       _current_query_if = avl_next_element_safe(&_nl80211_if_tree, _current_query_if, _node);
628       _current_query_number = QUERY_START;
629     }
630   }
631 }
632
633 /**
634  * Trigger the next netlink query
635  */
636 static void
637 _trigger_next_netlink_query(void) {
638   if (!_nl80211_id || !_nl80211_multicast_group) {
639     if (_current_query_in_progress) {
640       /* wait for the next timer */
641       _current_query_in_progress = false;
642       return;
643     }
644
645     /* first we need to get the ID and multicast group */
646     OONF_DEBUG(LOG_NL80211, "Get nl80211 family and multicast id");
647     _current_query_in_progress = true;
648     _send_netlink_message(NULL, QUERY_GET_FAMILY);
649     return;
650   }
651
652   /* calculate next interface/query, ignore interfaces that are down */
653   do {
654     _get_next_query();
655   } while (_current_query_if && !_current_query_if->if_listener.data->flags.up);
656
657   if (!_current_query_if) {
658     /* done with this series of queries, wait for next timer */
659     OONF_INFO(LOG_NL80211, "All queries done for all interfaces");
660     _current_query_in_progress = false;
661     return;
662   }
663
664   OONF_INFO(LOG_NL80211, "Sending query %u to interface %s", _current_query_number, _current_query_if->name);
665   _send_netlink_message(_current_query_if, _current_query_number);
666 }
667
668 /**
669  * Parse an incoming netlink message from the kernel
670  * @param hdr pointer to netlink message
671  */
672 static void
673 _cb_nl_message(struct nlmsghdr *hdr) {
674   struct genlmsghdr *gen_hdr;
675
676   gen_hdr = NLMSG_DATA(hdr);
677   if (hdr->nlmsg_type == GENL_ID_CTRL && gen_hdr->cmd == CTRL_CMD_NEWFAMILY) {
678     genl_process_get_family_result(hdr, &_nl80211_id, &_nl80211_multicast_group);
679     return;
680   }
681
682   if (hdr->nlmsg_type != _nl80211_id) {
683     OONF_WARN(LOG_NL80211, "Unhandled netlink message type: %u", hdr->nlmsg_type);
684     return;
685   }
686
687   if (gen_hdr->cmd != _if_query_ops[_current_query_number].cmd) {
688     OONF_INFO(LOG_NL80211, "Received Nl80211 command %u for query %u (should be %u)", gen_hdr->cmd,
689       _current_query_number, _if_query_ops[_current_query_number].cmd);
690   }
691   else if (_if_query_ops[_current_query_number].process) {
692     OONF_DEBUG(LOG_NL80211, "Received Nl80211 command %u for query %u", gen_hdr->cmd, _current_query_number);
693     _if_query_ops[_current_query_number].process(_current_query_if, hdr);
694   }
695 }
696
697 /**
698  * Callback triggered when a netlink message failes
699  * @param seq sequence number
700  * @param error error code
701  */
702 static void
703 _cb_nl_error(uint32_t seq __attribute((unused)), int error __attribute((unused))) {
704   OONF_INFO(LOG_NL80211, "seq %u: Received error %d", seq, error);
705   if (_nl80211_id && _nl80211_multicast_group) {
706     _trigger_next_netlink_query();
707   }
708 }
709
710 /**
711  * Callback triggered when one or more netlink messages time out
712  */
713 static void
714 _cb_nl_timeout(void) {
715   OONF_INFO(LOG_NL80211, "Received timeout");
716   if (_nl80211_id && _nl80211_multicast_group) {
717     if (_if_query_ops[_current_query_number].finalize) {
718       _if_query_ops[_current_query_number].finalize(_current_query_if);
719     }
720     _trigger_next_netlink_query();
721   }
722 }
723
724 /**
725  * Callback triggered when a netlink message is done
726  * @param seq sequence number
727  */
728 static void
729 _cb_nl_done(uint32_t seq __attribute((unused))) {
730   OONF_INFO(LOG_NL80211, "%u: Received done", seq);
731   if (_nl80211_id && _nl80211_multicast_group) {
732     if (_if_query_ops[_current_query_number].finalize) {
733       _if_query_ops[_current_query_number].finalize(_current_query_if);
734     }
735     _trigger_next_netlink_query();
736   }
737 }
738
739 /**
740  * Update configuration of nl80211-listener plugin
741  */
742 static void
743 _cb_config_changed(void) {
744   const struct const_strarray *array;
745   struct nl80211_if *interf;
746   const char *str;
747
748   if (cfg_schema_tobin(&_config, _nl80211_section.post, _nl80211_entries, ARRAYSIZE(_nl80211_entries))) {
749     OONF_WARN(LOG_NL80211, "Could not convert " OONF_NL80211_LISTENER_SUBSYSTEM " config to bin");
750     return;
751   }
752
753   /* set transmission timer */
754   oonf_timer_set_ext(&_transmission_timer, 1, _config.interval);
755
756   /* mark old interfaces for removal */
757   array = cfg_db_get_schema_entry_value(_nl80211_section.pre, &_nl80211_entries[IDX_INTERFACES]);
758   if (array && strarray_get_count_c(array) > 0) {
759     strarray_for_each_element(array, str) {
760       interf = _nl80211_if_get(str);
761       if (interf) {
762         interf->_remove = !interf->_if_section;
763         interf->_nl80211_section = false;
764       }
765     }
766   }
767
768   /* create new interfaces and remove mark */
769   array = cfg_db_get_schema_entry_value(_nl80211_section.post, &_nl80211_entries[IDX_INTERFACES]);
770   if (array && strarray_get_count_c(array) > 0) {
771     strarray_for_each_element(array, str) {
772       interf = _nl80211_if_add(str);
773       if (interf) {
774         /* mark for removal */
775         interf->_remove = false;
776         interf->_nl80211_section = true;
777       }
778     }
779   }
780
781   array = cfg_db_get_schema_entry_value(_nl80211_section.pre, &_nl80211_entries[IDX_INTERFACES]);
782   if (array && strarray_get_count_c(array) > 0) {
783     strarray_for_each_element(array, str) {
784       interf = _nl80211_if_get(str);
785       if (interf && interf->_remove) {
786         _nl80211_if_remove(interf);
787       }
788     }
789   }
790 }