8ec8a8eb7ad026f131e05c1e8fd4af02fcad4c59
[oonf.git] / src / generic / layer2_config / layer2_config.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 #include <oonf/libcommon/avl.h>
47 #include <oonf/libcommon/avl_comp.h>
48 #include <oonf/oonf.h>
49 #include <oonf/libconfig/cfg_schema.h>
50 #include <oonf/libconfig/cfg_tobin.h>
51 #include <oonf/libconfig/cfg_validate.h>
52 #include <oonf/libcore/oonf_logging.h>
53 #include <oonf/libcore/oonf_subsystem.h>
54 #include <oonf/subsystems/oonf_class.h>
55 #include <oonf/subsystems/oonf_layer2.h>
56 #include <oonf/subsystems/oonf_timer.h>
57
58 #include <oonf/generic/layer2_config/layer2_config.h>
59
60 /* definitions and constants */
61 #define LOG_LAYER2_CONFIG _oonf_layer2_config_subsystem.logging
62
63 enum
64 {
65   _MAX_L2_VALUE_LEN = 64
66 };
67
68 enum l2_data_type
69 {
70   L2_NET,
71   L2_NET_IP,
72   L2_DEF,
73   L2_NEIGH,
74   L2_NEIGH_IP,
75   L2_DST,
76
77   L2_TYPE_COUNT,
78 };
79
80 /* one layer2 configuration option for an interface */
81 struct l2_config_data {
82   enum l2_data_type config_type;
83   struct netaddr mac;
84
85   int data_idx;
86
87   enum oonf_layer2_data_type data_type;
88   union oonf_layer2_value data;
89   char txt_value[_MAX_L2_VALUE_LEN];
90 };
91
92 /* all configuration options for an interface */
93 struct l2_config_if_data {
94   char interf[IF_NAMESIZE];
95
96   struct oonf_timer_instance _reconfigure_timer;
97   struct avl_node _node;
98
99   size_t count;
100   struct l2_config_data d[0];
101 };
102
103 /* Prototypes */
104 static int _init(void);
105 static void _cleanup(void);
106
107 static struct l2_config_if_data *_add_if_data(const char *ifname, size_t data_count);
108 static void _remove_if_data(struct l2_config_if_data *);
109
110 static int _cb_if_value_validator(struct autobuf *out, const char *section_name, const char *entry_name,
111   const char *value, struct cfg_schema_entry *entries, size_t entry_count);
112 static int _cb_if_value_tobin(struct cfg_schema_entry *entries, size_t entry_count, const char *value, void *ptr);
113 static int _cb_neigh_value_validator(struct autobuf *out, const char *section_name, const char *entry_name,
114   const char *value, struct cfg_schema_entry *entries, size_t entry_count);
115 static int _cb_neigh_value_tobin(struct cfg_schema_entry *entries, size_t entry_count, const char *value, void *ptr);
116
117 static void _cb_update_l2net(void *);
118 static void _cb_update_l2neigh(void *);
119 static void _cb_reconfigure(struct oonf_timer_instance *);
120
121 static void _configure_if_data(struct l2_config_if_data *if_data);
122 static void _cb_config_changed(void);
123
124 /* define configuration entries */
125
126 /*! configuration for linkdata */
127 static struct cfg_schema_entry _l2net_entries[] = {
128   CFG_MAP_CHOICE_L2NET(l2_config_data, data_idx, "l2net_key", "", "Layer2 network key for configuration"),
129   CFG_MAP_STRING_ARRAY(l2_config_data, txt_value, "l2net_value", "", "Layer2 network value", _MAX_L2_VALUE_LEN),
130 };
131 static struct cfg_schema_entry _l2net_def_entries[] = {
132   CFG_MAP_CHOICE_L2NEIGH(l2_config_data, data_idx, "l2neigh_key", "", "Layer2 neighbor key for configuration"),
133   CFG_MAP_STRING_ARRAY(
134     l2_config_data, txt_value, "l2neigh_value", "", "Layer2 neighbor value for default neighbor data", _MAX_L2_VALUE_LEN),
135 };
136 static struct cfg_schema_entry _l2neigh_entries[] = {
137   CFG_MAP_CHOICE_L2NEIGH(l2_config_data, data_idx, "l2neigh_key", "", "Layer2 neighbor key for configuration"),
138   CFG_MAP_STRING_ARRAY(l2_config_data, txt_value, "l2neigh_value", "", "Layer2 neighbor value", _MAX_L2_VALUE_LEN),
139   CFG_MAP_NETADDR_MAC48(l2_config_data, mac, "l2neigh_mac", "", "MAC address of neighbor", false, false),
140 };
141 static struct cfg_schema_entry _l2neigh_ip_entries[] = {
142   CFG_MAP_NETADDR_MAC48(l2_config_data, mac, "l2neigh_mac", "", "MAC address of neighbor", false, false),
143   CFG_MAP_NETADDR_V46(l2_config_data, data.addr, "l2neigh_ip", "", "IP address to neighbor", false, false),
144 };
145 static struct cfg_schema_entry _l2neigh_dst_entries[] = {
146   CFG_MAP_NETADDR_MAC48(l2_config_data, mac, "l2neigh_mac", "", "MAC address of neighbor", false, false),
147   CFG_MAP_NETADDR_MAC48(
148     l2_config_data, data.addr, "l2neigh_dst", "", "Secondary MAC address of neighbor", false, false),
149 };
150
151 static struct cfg_schema_token_customizer _if_value_customizer = {
152   .cb_validator = _cb_if_value_validator,
153   .cb_tobin = _cb_if_value_tobin,
154 };
155
156 static struct cfg_schema_token_customizer _neigh_value_customizer = {
157   .cb_validator = _cb_neigh_value_validator,
158   .cb_tobin = _cb_neigh_value_tobin,
159 };
160
161 static struct cfg_schema_entry _l2_config_if_entries[] = {
162   [L2_NET] = CFG_VALIDATE_TOKENS_CUSTOM("l2net", "",
163     "Sets an interface wide layer2 entry into the database."
164     " Parameters are the key of the interface data followed by the data.",
165     _l2net_entries, _if_value_customizer, .list = true),
166   [L2_NET_IP] = CFG_VALIDATE_NETADDR_V46(
167     "l2net_ip", "", "Sets an ip address/prefix for the local radio in the database", true, false, .list = true),
168   [L2_DEF] = CFG_VALIDATE_TOKENS_CUSTOM("l2default", "",
169     "Sets an interface wide default neighbor layer2 entry into the database."
170     " Parameters are the key of the neighbor data followed by the data.",
171     _l2net_def_entries, _neigh_value_customizer, .list = true),
172   [L2_NEIGH] = CFG_VALIDATE_TOKENS_CUSTOM("l2neighbor", "",
173     "Sets an neighbor specific layer2 entry into the database."
174     " Parameters are the key of the neighbor data followed by the data and the mac address of the neighbor.",
175     _l2neigh_entries, _neigh_value_customizer, .list = true),
176   [L2_NEIGH_IP] = CFG_VALIDATE_TOKENS("l2neighbor_ip", "",
177     "Sets an neighbor specific ip address/prefix into the database."
178     " Parameters are the mac address and then the ip address/prefix.",
179     _l2neigh_ip_entries, .list = true),
180   [L2_DST] = CFG_VALIDATE_TOKENS("l2destination", "",
181     "Sets an neighbor specific bridged MAC destination into the database."
182     " Parameters are the mac address of the neighbor and then the proxied mac address.",
183     _l2neigh_dst_entries, .list = true),
184 };
185
186 static struct cfg_schema_section _l2_config_section = {
187   CFG_OSIF_SCHEMA_INTERFACE_SECTION_INIT,
188
189   .cb_delta_handler = _cb_config_changed,
190   .entries = _l2_config_if_entries,
191   .entry_count = ARRAYSIZE(_l2_config_if_entries),
192 };
193
194 /* declare subsystem */
195 static const char *_dependencies[] = {
196   OONF_LAYER2_SUBSYSTEM,
197   OONF_TIMER_SUBSYSTEM,
198 };
199 static struct oonf_subsystem _oonf_layer2_config_subsystem = {
200   .name = OONF_LAYER2_CONFIG_SUBSYSTEM,
201   .dependencies = _dependencies,
202   .dependencies_count = ARRAYSIZE(_dependencies),
203   .init = _init,
204   .cleanup = _cleanup,
205
206   .cfg_section = &_l2_config_section,
207 };
208 DECLARE_OONF_PLUGIN(_oonf_layer2_config_subsystem);
209
210 /* originator for smooth set/remove of configured layer2 values */
211 static struct oonf_layer2_origin _l2_origin_current = {
212   .name = "layer2 config",
213   .priority = OONF_LAYER2_ORIGIN_CONFIGURED,
214 };
215 static struct oonf_layer2_origin _l2_origin_old = {
216   .name = "layer2 config old",
217   .priority = OONF_LAYER2_ORIGIN_CONFIGURED,
218 };
219
220 /* listener for removal of layer2 data */
221 static struct oonf_class_extension _l2net_listener = {
222   .ext_name = "link config listener",
223   .class_name = LAYER2_CLASS_NETWORK,
224
225   .cb_remove = _cb_update_l2net,
226   .cb_change = _cb_update_l2net,
227 };
228 static struct oonf_class_extension _l2neigh_listener = {
229   .ext_name = "link config listener",
230   .class_name = LAYER2_CLASS_NEIGHBOR,
231
232   .cb_remove = _cb_update_l2neigh,
233   .cb_change = _cb_update_l2neigh,
234 };
235
236 /* interface data */
237 static struct avl_tree _if_data_tree;
238
239 /* interface reconfiguration timer */
240 static struct oonf_timer_class _reconfigure_timer = {
241   .name = "layer2 reconfiguration",
242   .callback = _cb_reconfigure,
243 };
244
245 /**
246  * Subsystem constructor
247  * @return always returns 0
248  */
249 static int
250 _init(void) {
251   oonf_layer2_origin_add(&_l2_origin_current);
252   oonf_layer2_origin_add(&_l2_origin_old);
253
254   oonf_class_extension_add(&_l2net_listener);
255   oonf_class_extension_add(&_l2neigh_listener);
256
257   oonf_timer_add(&_reconfigure_timer);
258
259   avl_init(&_if_data_tree, avl_comp_strcasecmp, false);
260   return 0;
261 }
262
263 /**
264  * Subsystem destructor
265  */
266 static void
267 _cleanup(void) {
268   struct l2_config_if_data *if_data, *if_data_it;
269
270   avl_for_each_element_safe(&_if_data_tree, if_data, _node, if_data_it) {
271     _remove_if_data(if_data);
272   }
273   oonf_timer_remove(&_reconfigure_timer);
274
275   oonf_class_extension_remove(&_l2net_listener);
276   oonf_class_extension_remove(&_l2neigh_listener);
277
278   oonf_layer2_origin_remove(&_l2_origin_current);
279   oonf_layer2_origin_remove(&_l2_origin_old);
280 }
281
282 /**
283  * Add a new layer2 config interface data
284  * @param ifname name of interface
285  * @param data_count number of data entries
286  * @return interface entry, NULL if out of memory
287  */
288 static struct l2_config_if_data *
289 _add_if_data(const char *ifname, size_t data_count) {
290   struct l2_config_if_data *if_data;
291
292   if_data = avl_find_element(&_if_data_tree, ifname, if_data, _node);
293   if (if_data) {
294     _remove_if_data(if_data);
295   }
296
297   if_data = calloc(1, sizeof(struct l2_config_if_data) + data_count * sizeof(struct l2_config_data));
298   if (!if_data) {
299     OONF_WARN(LOG_LAYER2_CONFIG, "Out of memory for %" PRINTF_SIZE_T_SPECIFIER " ifdata entries for interface %s",
300       data_count, _l2_config_section.section_name);
301     return NULL;
302   }
303
304   /* hook into tree */
305   strscpy(if_data->interf, ifname, IF_NAMESIZE);
306   if_data->_node.key = if_data->interf;
307
308   avl_insert(&_if_data_tree, &if_data->_node);
309
310   /* initialize timer */
311   if_data->_reconfigure_timer.class = &_reconfigure_timer;
312
313   return if_data;
314 }
315
316 /**
317  * removes a layer2 config interface data
318  * @param if_data interface data
319  */
320 static void
321 _remove_if_data(struct l2_config_if_data *if_data) {
322   if (!avl_is_node_added(&if_data->_node)) {
323     return;
324   }
325
326   oonf_timer_stop(&if_data->_reconfigure_timer);
327   avl_remove(&_if_data_tree, &if_data->_node);
328   free(if_data);
329 }
330
331 /**
332  * Validate interface setting for layer2 data
333  * @param out buffer for validation errors
334  * @param section_name name of configuration section
335  * @param entry_name name of configuration entries
336  * @param value value to be validated
337  * @param entries subentries of token config options
338  * @param entry_count number of subentries
339  * @return -1 if an error happened, 0 otherwise
340  */
341 static int
342 _cb_if_value_validator(struct autobuf *out, const char *section_name, const char *entry_name, const char *value,
343   struct cfg_schema_entry *entries, size_t entry_count) {
344   struct l2_config_data l2_data;
345   const struct oonf_layer2_metadata *meta;
346   union oonf_layer2_value dst;
347
348   if (cfg_tobin_tokens(&l2_data, value, entries, entry_count, NULL)) {
349     return -1;
350   }
351
352   meta = oonf_layer2_net_metadata_get(l2_data.data_idx);
353
354   if (oonf_layer2_data_parse_string(&dst, meta, l2_data.txt_value)) {
355     cfg_append_printable_line(out,
356       "Value '%s' for entry '%s' in section %s does not use the data"
357       " type %s for layer2 network key %s",
358       value, entry_name, section_name, oonf_layer2_data_get_type_string(meta->type), meta->key);
359   }
360   return 0;
361 }
362
363 /**
364  * Finalize binary conversion of layer2 interface config entry
365  * @param entries configuration subentries
366  * @param entry_count number of subentries
367  * @param value (non-tokenized) value of tokens
368  * @param ptr pointer to binary destination
369  * @return -1 if an error happened, 0 otherwise
370  */
371 static int
372 _cb_if_value_tobin(struct cfg_schema_entry *entries __attribute__((unused)), size_t entry_count __attribute__((unused)),
373   const char *value __attribute__((unused)), void *ptr) {
374   struct l2_config_data *l2_data;
375   const struct oonf_layer2_metadata *meta;
376
377   l2_data = ptr;
378
379   meta = oonf_layer2_net_metadata_get(l2_data->data_idx);
380   if (oonf_layer2_data_parse_string(&l2_data->data, meta, l2_data->txt_value)) {
381     return -1;
382   }
383   l2_data->data_type = meta->type;
384   return 0;
385 }
386
387 /**
388  * Validate neighbor setting for layer2 data
389  * @param out buffer for validation errors
390  * @param section_name name of configuration section
391  * @param entry_name name of configuration entries
392  * @param value value to be validated
393  * @param entries subentries of token config options
394  * @param entry_count number of subentries
395  * @return -1 if an error happened, 0 otherwise
396  */
397 static int
398 _cb_neigh_value_validator(struct autobuf *out, const char *section_name, const char *entry_name, const char *value,
399   struct cfg_schema_entry *entries, size_t entry_count) {
400   struct l2_config_data l2_data;
401   const struct oonf_layer2_metadata *meta;
402   union oonf_layer2_value dst;
403
404   if (cfg_tobin_tokens(&l2_data, value, entries, entry_count, NULL)) {
405     return -1;
406   }
407
408   meta = oonf_layer2_neigh_metadata_get(l2_data.data_idx);
409
410   if (oonf_layer2_data_parse_string(&dst, meta, l2_data.txt_value)) {
411     cfg_append_printable_line(out,
412       "Value '%s' for entry '%s' in section %s does not use the data"
413       " type %s for layer2 neighbor key %s",
414       value, entry_name, section_name, oonf_layer2_data_get_type_string(meta->type), meta->key);
415   }
416   return 0;
417 }
418
419 /**
420  * Finalize binary conversion of layer2 network config entry
421  * @param entries configuration subentries
422  * @param entry_count number of subentries
423  * @param value (non-tokenized) value of tokens
424  * @param ptr pointer to binary destination
425  * @return -1 if an error happened, 0 otherwise
426  */
427 static int
428 _cb_neigh_value_tobin(struct cfg_schema_entry *entries __attribute__((unused)),
429   size_t entry_count __attribute__((unused)), const char *value __attribute__((unused)), void *ptr) {
430   struct l2_config_data *l2_data;
431   const struct oonf_layer2_metadata *meta;
432
433   l2_data = ptr;
434
435   meta = oonf_layer2_neigh_metadata_get(l2_data->data_idx);
436   if (oonf_layer2_data_parse_string(&l2_data->data, meta, l2_data->txt_value)) {
437     return -1;
438   }
439   l2_data->data_type = meta->type;
440   return 0;
441 }
442
443 /**
444  * Callback when a layer2 network entry is changed/removed
445  * @param ptr l2net entry
446  */
447 static void
448 _cb_update_l2net(void *ptr) {
449   struct oonf_layer2_net *l2net;
450   struct l2_config_if_data *if_data;
451
452   l2net = ptr;
453
454   if_data = avl_find_element(&_if_data_tree, l2net->name, if_data, _node);
455   if (if_data && !oonf_timer_is_active(&if_data->_reconfigure_timer)) {
456     OONF_DEBUG(LOG_LAYER2_CONFIG, "Received update for l2net: %s", l2net->name);
457     oonf_timer_set(&if_data->_reconfigure_timer, LAYER2_RECONFIG_DELAY);
458   }
459 }
460
461 /**
462  * Callback when a layer2 neighbor entry is changed/removed
463  * @param ptr l2net entry
464  */
465 static void
466 _cb_update_l2neigh(void *ptr) {
467   struct oonf_layer2_neigh *l2neigh;
468   struct l2_config_if_data *if_data;
469
470   l2neigh = ptr;
471
472   if_data = avl_find_element(&_if_data_tree, l2neigh->network->name, if_data, _node);
473   if (if_data && !oonf_timer_is_active(&if_data->_reconfigure_timer)) {
474     OONF_DEBUG(LOG_LAYER2_CONFIG, "Received update for l2neigh: %s", l2neigh->network->name);
475     oonf_timer_set(&if_data->_reconfigure_timer, LAYER2_RECONFIG_DELAY);
476   }
477 }
478
479 /**
480  * Timer called for delayed layer2 config update
481  * @param timer timer entry
482  */
483 static void
484 _cb_reconfigure(struct oonf_timer_instance *timer) {
485   struct l2_config_if_data *if_data;
486
487   if_data = container_of(timer, typeof(*if_data), _reconfigure_timer);
488   _configure_if_data(if_data);
489 }
490
491 /**
492  * Apply a layer2 config interface to the l2 database
493  * @param if_data interface data
494  */
495 static void
496 _configure_if_data(struct l2_config_if_data *if_data) {
497   struct oonf_layer2_neigh *l2neigh;
498   struct oonf_layer2_net *l2net;
499   struct l2_config_data *entry;
500   struct oonf_layer2_neigh_key key;
501   size_t i;
502
503   l2net = oonf_layer2_net_get(if_data->interf);
504   if (!l2net && if_data->count > 0) {
505     l2net = oonf_layer2_net_add(if_data->interf);
506     if (!l2net) {
507       return;
508     }
509   }
510
511   /* relabel old entries */
512   if (l2net) {
513     oonf_layer2_net_relabel(l2net, &_l2_origin_old, &_l2_origin_current);
514
515     for (i = 0; i < if_data->count; i++) {
516       entry = &if_data->d[i];
517
518       switch (entry->config_type) {
519         case L2_NET:
520           oonf_layer2_data_set(&l2net->data[entry->data_idx], &_l2_origin_current, entry->data_type, &entry->data);
521           break;
522         case L2_NET_IP:
523           oonf_layer2_net_add_ip(l2net, &_l2_origin_current, &entry->data.addr);
524           break;
525         case L2_DEF:
526           oonf_layer2_data_set(&l2net->neighdata[entry->data_idx], &_l2_origin_current, entry->data_type, &entry->data);
527           break;
528         case L2_NEIGH:
529           memset(&key, 0, sizeof(key));
530           memcpy(&key.addr, &entry->mac, sizeof(key.addr));
531           l2neigh = oonf_layer2_neigh_add_lid(l2net, &key);
532           if (l2neigh) {
533             oonf_layer2_data_set(&l2neigh->data[entry->data_idx], &_l2_origin_current, entry->data_type, &entry->data);
534           }
535           break;
536         case L2_NEIGH_IP:
537           l2neigh = oonf_layer2_neigh_add(l2net, &entry->mac);
538           if (l2neigh) {
539             oonf_layer2_neigh_add_ip(l2neigh, &_l2_origin_current, &entry->data.addr);
540           }
541           break;
542         case L2_DST:
543           l2neigh = oonf_layer2_neigh_add(l2net, &entry->mac);
544           if (l2neigh) {
545             oonf_layer2_destination_add(l2neigh, &entry->data.addr, &_l2_origin_current);
546           }
547           break;
548         default:
549           break;
550       }
551     }
552
553     /* remove old data */
554     oonf_layer2_net_remove(l2net, &_l2_origin_old);
555   }
556
557   /* stop update timer */
558   oonf_timer_stop(&if_data->_reconfigure_timer);
559 }
560
561 /**
562  * Parse configuration change
563  */
564 static void
565 _cb_config_changed(void) {
566   struct l2_config_if_data *if_data;
567   struct cfg_entry *entry;
568   char ifbuf[IF_NAMESIZE];
569   const char *txt_value;
570   const char *ifname;
571   size_t i, total;
572
573   ifname = cfg_get_phy_if(ifbuf, _l2_config_section.section_name);
574   if_data = avl_find_element(&_if_data_tree, ifname, if_data, _node);
575   if (if_data) {
576     _remove_if_data(if_data);
577   }
578
579   if (!_l2_config_section.post) {
580     /* section was removed */
581     return;
582   }
583
584   total = 0;
585   for (i = 0; i < L2_TYPE_COUNT; i++) {
586     entry = cfg_db_get_entry(_l2_config_section.post, _l2_config_if_entries[i].key.entry);
587     if (entry) {
588       total += strarray_get_count(&entry->val);
589     }
590   }
591
592   if_data = _add_if_data(_l2_config_section.section_name, total);
593   if (!if_data) {
594     OONF_WARN(LOG_LAYER2_CONFIG, "Out of memory for %" PRINTF_SIZE_T_SPECIFIER " ifdata entries for interface %s",
595       total, _l2_config_section.section_name);
596     return;
597   }
598
599   /* initialize header */
600   strscpy(if_data->interf, _l2_config_section.section_name, IF_NAMESIZE);
601   if_data->_node.key = if_data->interf;
602
603   avl_insert(&_if_data_tree, &if_data->_node);
604
605   /* initialize data */
606   total = 0;
607   for (i = 0; i < L2_TYPE_COUNT; i++) {
608     entry = cfg_db_get_entry(_l2_config_section.post, _l2_config_if_entries[i].key.entry);
609     if (entry) {
610       strarray_for_each_element(&entry->val, txt_value) {
611         /*
612          * assume that the data type is "address", we will overwrite it for
613          * other variants in the next function call.
614          */
615         if_data->d[total].data_type = OONF_LAYER2_NETWORK_DATA;
616         if (!cfg_tobin_tokens(&if_data->d[total], txt_value, _l2_config_if_entries[i].validate_param[0].ptr,
617               _l2_config_if_entries[i].validate_param[1].s, _l2_config_if_entries[i].validate_param[2].ptr)) {
618           if_data->d[total].config_type = i;
619           total++;
620         }
621       }
622     }
623   }
624
625   if_data->count = total;
626
627   /* reconfigure layer2 database */
628   _configure_if_data(if_data);
629 }