20a877cf3ee6ee46363971ecc7efc2991a152390
[oonf.git] / src-plugins / 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 "common/avl.h"
49 #include "common/avl_comp.h"
50 #include "common/common_types.h"
51
52 #include "subsystems/oonf_layer2.h"
53
54 #include "dlep/dlep_extension.h"
55 #include "dlep/dlep_reader.h"
56 #include "dlep/dlep_session.h"
57 #include "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 int
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 0;
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 int
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 0;
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 (!session->allow_lids) {
211     OONF_INFO(session->log_source, "LID TLV is not allowed");
212     return -1;
213   }
214   return 0;
215 }
216
217 struct oonf_layer2_neigh *
218 dlep_extension_get_l2_neighbor(struct dlep_session *session) {
219   struct oonf_layer2_net *l2net;
220
221   struct oonf_layer2_neigh_key key;
222
223   if (dlep_extension_get_l2_neighbor_key(&key, session)) {
224     return NULL;
225   }
226
227   l2net = oonf_layer2_net_get(session->l2_listener.name);
228   if (!l2net) {
229     return NULL;
230   }
231   return oonf_layer2_neigh_get_lid(l2net, &key);
232 }
233
234 /**
235  * Handle handle destination up/update for DLEP extension
236  * by automatically mapping oonf_layer2_data to DLEP TLVs
237  * @param ext dlep extension
238  * @param session dlep session
239  * @return -1 if an error happened, 0 otherwise
240  */
241 int
242 dlep_extension_router_process_destination(struct dlep_extension *ext, struct dlep_session *session) {
243   struct oonf_layer2_neigh *l2neigh;
244   int result;
245
246   if (session->restrict_signal != DLEP_ALL_SIGNALS) {
247     /* ignore unless we have an established session */
248     return 0;
249   }
250
251   l2neigh = dlep_extension_get_l2_neighbor(session);
252   if (!l2neigh) {
253     return 0;
254   }
255
256   result = dlep_reader_map_l2neigh_data(l2neigh->data, session, ext);
257   if (result) {
258     OONF_INFO(session->log_source, "tlv mapping for extension %d failed: %d", ext->id, result);
259     return result;
260   }
261   return 0;
262 }
263
264 /**
265  * Generate peer init ack for DLEP extension by automatically
266  * mapping oonf_layer2_data to DLEP TLVs
267  * @param ext dlep extension
268  * @param session dlep session
269  * @param neigh unused for this callback
270  * @return -1 if an error happened, 0 otherwise
271  */
272 int
273 dlep_extension_radio_write_session_init_ack(
274   struct dlep_extension *ext, struct dlep_session *session, const struct oonf_layer2_neigh_key *neigh __attribute__((unused))) {
275   const struct oonf_layer2_metadata *meta;
276   struct oonf_layer2_net *l2net;
277   struct oonf_layer2_data *l2data;
278   enum oonf_layer2_neighbor_index neigh_idx;
279   enum oonf_layer2_network_index net_idx;
280   size_t i;
281   int result;
282
283   /* first make sure defaults are set correctly */
284   l2net = oonf_layer2_net_add(session->l2_listener.name);
285   if (!l2net) {
286     OONF_WARN(session->log_source, "Could not add l2net for new interface");
287     return -1;
288   }
289
290   /* adding default neighbor data for mandatory values */
291   for (i = 0; i < ext->neigh_mapping_count; i++) {
292     if (!ext->neigh_mapping[i].mandatory) {
293       continue;
294     }
295
296     neigh_idx = ext->neigh_mapping[i].layer2;
297     l2data = &l2net->neighdata[neigh_idx];
298
299     if (!oonf_layer2_data_has_value(l2data)) {
300       meta = oonf_layer2_neigh_metadata_get(neigh_idx);
301       oonf_layer2_data_set(l2data, session->l2_default_origin, meta->type, &ext->neigh_mapping[i].default_value);
302     }
303   }
304
305   /* adding default interface data for mandatory values */
306   for (i = 0; i < ext->if_mapping_count; i++) {
307     if (!ext->if_mapping[i].mandatory) {
308       continue;
309     }
310
311     net_idx = ext->if_mapping[i].layer2;
312     l2data = &l2net->data[net_idx];
313
314     if (!oonf_layer2_data_has_value(l2data)) {
315       meta = oonf_layer2_net_metadata_get(net_idx);
316       oonf_layer2_data_set(l2data, session->l2_default_origin, meta->type, &ext->if_mapping[i].default_value);
317     }
318   }
319
320   /* write default metric values */
321   OONF_DEBUG(session->log_source, "Mapping default neighbor data (%s) to TLVs", l2net->name);
322   result = dlep_writer_map_l2neigh_data(&session->writer, ext, l2net->neighdata, NULL);
323   if (result) {
324     OONF_WARN(session->log_source, "tlv mapping for extension %d failed: %d", ext->id, result);
325     return result;
326   }
327
328   /* write network wide data */
329   OONF_DEBUG(session->log_source, "Mapping if data (%s) to TLVs", l2net->name);
330   result = dlep_writer_map_l2net_data(&session->writer, ext, l2net->data);
331   if (result) {
332     OONF_WARN(session->log_source, "tlv mapping for extension %d failed: %d", ext->id, result);
333     return result;
334   }
335   return 0;
336 }
337
338 /**
339  * Generate peer update for DLEP extension by automatically
340  * mapping oonf_layer2_data to DLEP TLVs
341  * @param ext dlep extension
342  * @param session dlep session
343  * @param neigh unused for this callback
344  * @return -1 if an error happened, 0 otherwise
345  */
346 int
347 dlep_extension_radio_write_session_update(
348   struct dlep_extension *ext, struct dlep_session *session, const struct oonf_layer2_neigh_key *neigh __attribute__((unused))) {
349   struct oonf_layer2_net *l2net;
350   int result;
351
352   l2net = oonf_layer2_net_get(session->l2_listener.name);
353   if (!l2net) {
354     OONF_WARN(session->log_source, "Could not find l2net for new interface");
355     return -1;
356   }
357
358   result = dlep_writer_map_l2neigh_data(&session->writer, ext, l2net->neighdata, NULL);
359   if (result) {
360     OONF_WARN(session->log_source, "tlv mapping for extension %d failed: %d", ext->id, result);
361     return result;
362   }
363
364   result = dlep_writer_map_l2net_data(&session->writer, ext, l2net->data);
365   if (result) {
366     OONF_WARN(session->log_source, "tlv mapping for extension %d failed: %d", ext->id, result);
367     return result;
368   }
369   return 0;
370 }
371
372 /**
373  * Generate destination up/update for DLEP extension
374  * by automatically mapping oonf_layer2_data to DLEP TLVs
375  * @param ext dlep extension
376  * @param session dlep session
377  * @param neigh neighbor that should be updated
378  * @return -1 if an error happened, 0 otherwise
379  */
380 int
381 dlep_extension_radio_write_destination(
382   struct dlep_extension *ext, struct dlep_session *session, const struct oonf_layer2_neigh_key *neigh) {
383   struct oonf_layer2_neigh *l2neigh;
384   union oonf_layer2_neigh_key_str nbuf;
385   int result;
386
387   l2neigh = dlep_session_get_local_l2_neighbor(session, neigh);
388   if (!l2neigh) {
389     OONF_WARN(session->log_source,
390       "Could not find l2neigh "
391       "for neighbor %s",
392       oonf_layer2_neigh_key_to_string(&nbuf, neigh, true));
393     return -1;
394   }
395
396   result = dlep_writer_map_l2neigh_data(&session->writer, ext, l2neigh->data, l2neigh->network->neighdata);
397   if (result) {
398     OONF_WARN(session->log_source,
399       "tlv mapping for extension %d and neighbor %s failed: %d",
400       ext->id, oonf_layer2_neigh_key_to_string(&nbuf, neigh, true), result);
401     return result;
402   }
403   return 0;
404 }
405
406 /**
407  * Handle peer update and session init ACK for DLEP extension
408  * by automatically mapping oonf_layer2_data to DLEP TLVs
409  * @param ext dlep extension
410  * @param session dlep session
411  * @return -1 if an error happened, 0 otherwise
412  */
413 static int
414 _process_interface_specific_update(struct dlep_extension *ext, struct dlep_session *session) {
415   struct oonf_layer2_net *l2net;
416   int result;
417
418   l2net = oonf_layer2_net_add(session->l2_listener.name);
419   if (!l2net) {
420     OONF_INFO(session->log_source, "Could not add l2net for new interface");
421     return -1;
422   }
423
424   result = dlep_reader_map_l2neigh_data(l2net->neighdata, session, ext);
425   if (result) {
426     OONF_INFO(session->log_source, "tlv mapping for extension %d failed: %d", ext->id, result);
427     return result;
428   }
429
430   result = dlep_reader_map_l2net_data(l2net->data, session, ext);
431   if (result) {
432     OONF_INFO(session->log_source, "tlv mapping for extension %d failed: %d", ext->id, result);
433     return result;
434   }
435   return 0;
436 }