Rename "subsystems" directory to "base"
[oonf.git] / src / generic / dlep / dlep_extension.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 <stdlib.h>
47
48 #include <oonf/libcommon/avl.h>
49 #include <oonf/libcommon/avl_comp.h>
50 #include <oonf/oonf.h>
51
52 #include <oonf/base/oonf_layer2.h>
53
54 #include <oonf/generic/dlep/dlep_extension.h>
55 #include <oonf/generic/dlep/dlep_reader.h>
56 #include <oonf/generic/dlep/dlep_session.h>
57 #include <oonf/generic/dlep/dlep_writer.h>
58
59 static int _process_interface_specific_update(struct dlep_extension *ext, struct dlep_session *session);
60
61 static struct avl_tree _extension_tree;
62
63 static uint16_t *_id_array = NULL;
64 static uint16_t _id_array_length = 0;
65
66 /**
67  * Initialize the dlep extension system
68  */
69 void
70 dlep_extension_init(void) {
71   avl_init(&_extension_tree, avl_comp_int32, false);
72 }
73
74 /**
75  * Cleanup DLEP extension resources
76  */
77 void
78 dlep_extension_cleanup(void) {
79   free(_id_array);
80   _id_array = NULL;
81   _id_array_length = 0;
82 }
83
84 /**
85  * Add a new dlep extension
86  * @param ext pointer to initialized extension handler
87  */
88 void
89 dlep_extension_add(struct dlep_extension *ext) {
90   uint16_t *ptr;
91
92   if (avl_is_node_added(&ext->_node)) {
93     return;
94   }
95
96   /* add to tree */
97   ext->_node.key = &ext->id;
98   avl_insert(&_extension_tree, &ext->_node);
99
100   /* refresh id array */
101   ptr = realloc(_id_array, sizeof(uint16_t) * _extension_tree.count);
102   if (!ptr) {
103     return;
104   }
105
106   _id_array_length = 0;
107   _id_array = ptr;
108
109   avl_for_each_element(&_extension_tree, ext, _node) {
110     if (ext->id >= 0 && ext->id <= 0xffff) {
111       ptr[_id_array_length] = htons(ext->id);
112       _id_array_length++;
113     }
114   }
115 }
116
117 /**
118  * Get tree of dlep extensions
119  * @return tree of extensions
120  */
121 struct avl_tree *
122 dlep_extension_get_tree(void) {
123   return &_extension_tree;
124 }
125
126 /**
127  * Add processing callbacks to DLEP extension
128  * @param ext dlep extension
129  * @param radio true if radio extension, false if router
130  * @param processing array of dlep extension processing handlers
131  * @param proc_count number of processing handlers
132  */
133 void
134 dlep_extension_add_processing(
135   struct dlep_extension *ext, bool radio, struct dlep_extension_implementation *processing, size_t proc_count) {
136   size_t i, j;
137
138   for (j = 0; j < proc_count; j++) {
139     for (i = 0; i < ext->signal_count; i++) {
140       if (ext->signals[i].id == processing[j].id) {
141         if (radio) {
142           ext->signals[i].process_radio = processing[j].process;
143           ext->signals[i].add_radio_tlvs = processing[j].add_tlvs;
144         }
145         else {
146           ext->signals[i].process_router = processing[j].process;
147           ext->signals[i].add_router_tlvs = processing[j].add_tlvs;
148         }
149         break;
150       }
151     }
152   }
153 }
154
155 /**
156  * Get the array of supported dlep extension ids
157  * @param length pointer to length field to store id count
158  * @return pointer to array with ids
159  */
160 const uint16_t *
161 dlep_extension_get_ids(uint16_t *length) {
162   *length = _id_array_length;
163   return _id_array;
164 }
165
166 /**
167  * Handle peer init ack for DLEP extension by automatically
168  * mapping oonf_layer2_data to DLEP TLVs
169  * @param ext dlep extension
170  * @param session dlep session
171  * @return -1 if an error happened, 0 otherwise
172  */
173 enum dlep_parser_error
174 dlep_extension_router_process_session_init_ack(struct dlep_extension *ext, struct dlep_session *session) {
175   if (session->restrict_signal != DLEP_SESSION_INITIALIZATION_ACK) {
176     /* ignore unless we are in initialization mode */
177     return DLEP_NEW_PARSER_OKAY;
178   }
179   return _process_interface_specific_update(ext, session);
180 }
181
182 /**
183  * Handle peer update for DLEP extension by automatically
184  * mapping oonf_layer2_data to DLEP TLVs
185  * @param ext dlep extension
186  * @param session dlep session
187  * @return -1 if an error happened, 0 otherwise
188  */
189 enum dlep_parser_error
190 dlep_extension_router_process_session_update(struct dlep_extension *ext, struct dlep_session *session) {
191   if (session->restrict_signal != DLEP_ALL_SIGNALS) {
192     /* ignore unless we have an established session */
193     return DLEP_NEW_PARSER_OKAY;
194   }
195
196   return _process_interface_specific_update(ext, session);
197 }
198
199 int
200 dlep_extension_get_l2_neighbor_key(struct oonf_layer2_neigh_key *key, struct dlep_session *session) {
201   memset(key, 0, sizeof(*key));
202   if (dlep_reader_mac_tlv(key, session, NULL)) {
203     OONF_INFO(session->log_source, "mac tlv missing");
204     return -1;
205   }
206
207   if (dlep_reader_lid_tlv(key, session, NULL)) {
208     OONF_DEBUG(session->log_source, "lid tlv not present");
209   }
210   else if (key->link_id_length != session->cfg.lid_length) {
211     OONF_INFO(session->log_source, "LID TLV (length=%u) with bad length (should be %u)",
212               key->link_id_length, session->cfg.lid_length);
213     return -1;
214   }
215   return 0;
216 }
217
218 struct oonf_layer2_neigh *
219 dlep_extension_get_l2_neighbor(struct dlep_session *session) {
220   struct oonf_layer2_net *l2net;
221
222   struct oonf_layer2_neigh_key key;
223
224   if (dlep_extension_get_l2_neighbor_key(&key, session)) {
225     return NULL;
226   }
227
228   l2net = oonf_layer2_net_get(session->l2_listener.name);
229   if (!l2net) {
230     return NULL;
231   }
232   return oonf_layer2_neigh_get_lid(l2net, &key);
233 }
234
235 /**
236  * Handle handle destination up/update for DLEP extension
237  * by automatically mapping oonf_layer2_data to DLEP TLVs
238  * @param ext dlep extension
239  * @param session dlep session
240  * @return -1 if an error happened, 0 otherwise
241  */
242 enum dlep_parser_error
243 dlep_extension_router_process_destination(struct dlep_extension *ext, struct dlep_session *session) {
244   struct oonf_layer2_neigh *l2neigh;
245   enum dlep_parser_error result;
246
247   if (session->restrict_signal != DLEP_ALL_SIGNALS) {
248     /* ignore unless we have an established session */
249     return DLEP_NEW_PARSER_OKAY;
250   }
251
252   l2neigh = dlep_extension_get_l2_neighbor(session);
253   if (!l2neigh) {
254     return DLEP_NEW_PARSER_OKAY;
255   }
256
257   result = dlep_reader_map_l2neigh_data(l2neigh->data, session, ext);
258   if (result) {
259     OONF_INFO(session->log_source, "tlv mapping for extension %d failed: %d", ext->id, result);
260     return DLEP_NEW_PARSER_UNSUPPORTED_TLV;
261   }
262   return DLEP_NEW_PARSER_OKAY;
263 }
264
265 /**
266  * Generate peer init ack for DLEP extension by automatically
267  * mapping oonf_layer2_data to DLEP TLVs
268  * @param ext dlep extension
269  * @param session dlep session
270  * @param neigh unused for this callback
271  * @return -1 if an error happened, 0 otherwise
272  */
273 int
274 dlep_extension_radio_write_session_init_ack(
275   struct dlep_extension *ext, struct dlep_session *session, const struct oonf_layer2_neigh_key *neigh __attribute__((unused))) {
276   const struct oonf_layer2_metadata *meta;
277   struct oonf_layer2_net *l2net;
278   struct oonf_layer2_data *l2data;
279   enum oonf_layer2_neighbor_index neigh_idx;
280   enum oonf_layer2_network_index net_idx;
281   size_t i;
282   int result;
283
284   /* first make sure defaults are set correctly */
285   l2net = oonf_layer2_net_add(session->l2_listener.name);
286   if (!l2net) {
287     OONF_WARN(session->log_source, "Could not add l2net for new interface");
288     return -1;
289   }
290
291   /* adding default neighbor data for mandatory values */
292   for (i = 0; i < ext->neigh_mapping_count; i++) {
293     if (!ext->neigh_mapping[i].mandatory) {
294       continue;
295     }
296
297     neigh_idx = ext->neigh_mapping[i].layer2;
298     l2data = &l2net->neighdata[neigh_idx];
299
300     if (!oonf_layer2_data_has_value(l2data)) {
301       meta = oonf_layer2_neigh_metadata_get(neigh_idx);
302       oonf_layer2_data_set(l2data, session->l2_default_origin, meta->type, &ext->neigh_mapping[i].default_value);
303     }
304   }
305
306   /* adding default interface data for mandatory values */
307   for (i = 0; i < ext->if_mapping_count; i++) {
308     if (!ext->if_mapping[i].mandatory) {
309       continue;
310     }
311
312     net_idx = ext->if_mapping[i].layer2;
313     l2data = &l2net->data[net_idx];
314
315     if (!oonf_layer2_data_has_value(l2data)) {
316       meta = oonf_layer2_net_metadata_get(net_idx);
317       oonf_layer2_data_set(l2data, session->l2_default_origin, meta->type, &ext->if_mapping[i].default_value);
318     }
319   }
320
321   /* write default metric values */
322   OONF_DEBUG(session->log_source, "Mapping default neighbor data (%s) to TLVs", l2net->name);
323   result = dlep_writer_map_l2neigh_data(&session->writer, ext, l2net->neighdata, NULL);
324   if (result) {
325     OONF_WARN(session->log_source, "tlv mapping for extension %d failed: %d", ext->id, result);
326     return result;
327   }
328
329   /* write network wide data */
330   OONF_DEBUG(session->log_source, "Mapping if data (%s) to TLVs", l2net->name);
331   result = dlep_writer_map_l2net_data(&session->writer, ext, l2net->data);
332   if (result) {
333     OONF_WARN(session->log_source, "tlv mapping for extension %d failed: %d", ext->id, result);
334     return result;
335   }
336   return 0;
337 }
338
339 /**
340  * Generate peer update for DLEP extension by automatically
341  * mapping oonf_layer2_data to DLEP TLVs
342  * @param ext dlep extension
343  * @param session dlep session
344  * @param neigh unused for this callback
345  * @return -1 if an error happened, 0 otherwise
346  */
347 int
348 dlep_extension_radio_write_session_update(
349   struct dlep_extension *ext, struct dlep_session *session, const struct oonf_layer2_neigh_key *neigh __attribute__((unused))) {
350   struct oonf_layer2_net *l2net;
351   int result;
352
353   l2net = oonf_layer2_net_get(session->l2_listener.name);
354   if (!l2net) {
355     OONF_WARN(session->log_source, "Could not find l2net for new interface");
356     return -1;
357   }
358
359   result = dlep_writer_map_l2neigh_data(&session->writer, ext, l2net->neighdata, NULL);
360   if (result) {
361     OONF_WARN(session->log_source, "tlv mapping for extension %d failed: %d", ext->id, result);
362     return result;
363   }
364
365   result = dlep_writer_map_l2net_data(&session->writer, ext, l2net->data);
366   if (result) {
367     OONF_WARN(session->log_source, "tlv mapping for extension %d failed: %d", ext->id, result);
368     return result;
369   }
370   return 0;
371 }
372
373 /**
374  * Generate destination up/update for DLEP extension
375  * by automatically mapping oonf_layer2_data to DLEP TLVs
376  * @param ext dlep extension
377  * @param session dlep session
378  * @param neigh neighbor that should be updated
379  * @return -1 if an error happened, 0 otherwise
380  */
381 int
382 dlep_extension_radio_write_destination(
383   struct dlep_extension *ext, struct dlep_session *session, const struct oonf_layer2_neigh_key *neigh) {
384   struct oonf_layer2_neigh *l2neigh;
385   union oonf_layer2_neigh_key_str nbuf;
386   int result;
387
388   l2neigh = dlep_session_get_local_l2_neighbor(session, neigh);
389   if (!l2neigh) {
390     OONF_WARN(session->log_source,
391       "Could not find l2neigh "
392       "for neighbor %s",
393       oonf_layer2_neigh_key_to_string(&nbuf, neigh, true));
394     return -1;
395   }
396
397   result = dlep_writer_map_l2neigh_data(&session->writer, ext, l2neigh->data, l2neigh->network->neighdata);
398   if (result) {
399     OONF_WARN(session->log_source,
400       "tlv mapping for extension %d and neighbor %s failed: %d",
401       ext->id, oonf_layer2_neigh_key_to_string(&nbuf, neigh, true), result);
402     return result;
403   }
404   return 0;
405 }
406
407 /**
408  * Handle peer update and session init ACK for DLEP extension
409  * by automatically mapping oonf_layer2_data to DLEP TLVs
410  * @param ext dlep extension
411  * @param session dlep session
412  * @return -1 if an error happened, 0 otherwise
413  */
414 static enum dlep_parser_error
415 _process_interface_specific_update(struct dlep_extension *ext, struct dlep_session *session) {
416   struct oonf_layer2_net *l2net;
417   int result;
418
419   l2net = oonf_layer2_net_add(session->l2_listener.name);
420   if (!l2net) {
421     OONF_INFO(session->log_source, "Could not add l2net for new interface");
422     return DLEP_NEW_PARSER_INTERNAL_ERROR;
423   }
424
425   result = dlep_reader_map_l2neigh_data(l2net->neighdata, session, ext);
426   if (result) {
427     OONF_INFO(session->log_source, "tlv mapping for extension %d failed: %d", ext->id, result);
428     return DLEP_NEW_PARSER_UNSUPPORTED_TLV;
429   }
430
431   result = dlep_reader_map_l2net_data(l2net->data, session, ext);
432   if (result) {
433     OONF_INFO(session->log_source, "tlv mapping for extension %d failed: %d", ext->id, result);
434     return DLEP_NEW_PARSER_UNSUPPORTED_TLV;
435   }
436   return DLEP_NEW_PARSER_OKAY;
437 }