Rename "subsystems" directory to "base"
[oonf.git] / src / generic / dlep / dlep_session.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/libcore/oonf_logging.h>
50 #include <oonf/base/oonf_class.h>
51 #include <oonf/base/oonf_stream_socket.h>
52 #include <oonf/base/oonf_timer.h>
53
54 #include <oonf/generic/dlep/dlep_extension.h>
55 #include <oonf/generic/dlep/dlep_session.h>
56 #include <oonf/generic/dlep/dlep_writer.h>
57
58 /**
59  * internal constants of DLEP session
60  */
61 enum
62 {
63   /*! size increase step for DLEP value storage */
64   SESSION_VALUE_STEP = 128,
65 };
66
67 static int _update_allowed_tlvs(struct dlep_session *session);
68 static enum dlep_parser_error _parse_tlvstream(struct dlep_session *session, const uint8_t *buffer, size_t length);
69 static enum dlep_parser_error _check_mandatory(
70   struct dlep_session *session, struct dlep_extension *ext, int32_t signal_type);
71 static enum dlep_parser_error _check_duplicate(
72   struct dlep_session *session, struct dlep_extension *ext, int32_t signal_type);
73 static enum dlep_parser_error _call_extension_processing(
74   struct dlep_session *parser, struct dlep_extension *ext, int32_t signal_type);
75 static struct dlep_parser_tlv *_add_session_tlv(struct dlep_session_parser *parser, uint16_t id);
76 static enum dlep_parser_error _handle_extension(
77   struct dlep_session *session, struct dlep_extension *ext, uint32_t signal_type);
78 static enum dlep_parser_error _process_tlvs(
79   struct dlep_session *, int32_t signal_type, uint16_t signal_length, const uint8_t *tlvs);
80 static void _send_terminate(struct dlep_session *session, enum dlep_status status, const char *status_text);
81 static void _cb_destination_timeout(struct oonf_timer_instance *);
82
83 static struct oonf_class _tlv_class = {
84   .name = "dlep reader tlv",
85   .size = sizeof(struct dlep_parser_tlv),
86 };
87
88 static struct oonf_class _local_neighbor_class = {
89   .name = "dlep neighbor",
90   .size = sizeof(struct dlep_local_neighbor),
91 };
92
93 static struct oonf_timer_class _destination_ack_class = {
94   .name = "dlep destination ack",
95   .callback = _cb_destination_timeout,
96 };
97
98 /**
99  * Initialize DLEP session system
100  */
101 void
102 dlep_session_init(void) {
103   oonf_class_add(&_tlv_class);
104   oonf_class_add(&_local_neighbor_class);
105   oonf_timer_add(&_destination_ack_class);
106 }
107
108 /**
109  * Initialize a session, will hook in the base extension
110  * @param session session to initialize
111  * @param l2_ifname name of layer2 interface for dlep session
112  * @param l2_origin layer2 db origin id for session
113  * @param l2_default_origin layer2 originator that shall be used for setting defaults
114  * @param out output buffer for session
115  * @param radio true if this is a radio session,
116  *   false for a router session
117  * @param if_changed interface listener for session, can be NULL
118  * @param log_source logging source for session
119  * @return -1 if an error happened, 0 otherwise
120  */
121 int
122 dlep_session_add(struct dlep_session *session, const char *l2_ifname, const struct oonf_layer2_origin *l2_origin,
123   const struct oonf_layer2_origin *l2_default_origin, struct autobuf *out, bool radio,
124   int (*if_changed)(struct os_interface_listener *), enum oonf_log_source log_source) {
125   struct dlep_session_parser *parser;
126   struct dlep_extension *ext;
127
128   parser = &session->parser;
129
130   avl_init(&parser->allowed_tlvs, avl_comp_uint16, false);
131   avl_init(&session->local_neighbor_tree, avl_comp_netaddr, false);
132
133   session->log_source = log_source;
134   session->l2_origin = l2_origin;
135   session->l2_default_origin = l2_default_origin;
136   session->radio = radio;
137   session->writer.out = out;
138   session->_peer_state = DLEP_PEER_WAIT_FOR_INIT;
139
140   /* remember interface name */
141   session->l2_listener.name = l2_ifname;
142   session->l2_listener.if_changed = if_changed;
143
144   /* get interface listener to lock interface */
145   if (!os_interface_add(&session->l2_listener)) {
146     OONF_WARN(session->log_source, "Cannot activate interface listener for %s", l2_ifname);
147     dlep_session_remove(session);
148     return -1;
149   }
150
151   parser->values = calloc(SESSION_VALUE_STEP, sizeof(struct dlep_parser_value));
152   if (!parser->values) {
153     OONF_WARN(session->log_source, "Cannot allocate values buffer for %s", l2_ifname);
154     dlep_session_remove(session);
155     return -1;
156   }
157
158   /* generate full list of extensions */
159   avl_for_each_element(dlep_extension_get_tree(), ext, _node) {
160     OONF_DEBUG(session->log_source, "Add extension %d to session", ext->id);
161     parser->extensions[parser->extension_count] = ext;
162     parser->extension_count++;
163   }
164
165   if (_update_allowed_tlvs(session)) {
166     OONF_WARN(session->log_source, "Could not update allowed TLVs for %s", l2_ifname);
167     dlep_session_remove(session);
168     return -1;
169   }
170
171   avl_init(&session->_ip_prefix_modification, avl_comp_netaddr, false);
172
173   OONF_INFO(session->log_source, "Add session on %s", session->l2_listener.name);
174   return 0;
175 }
176
177 /**
178  * Remove a DLEP session
179  * @param session dlep session
180  */
181 void
182 dlep_session_remove(struct dlep_session *session) {
183   struct dlep_parser_tlv *tlv, *tlv_it;
184   struct dlep_session_parser *parser;
185 #ifdef OONF_LOG_DEBUG_INFO
186   struct netaddr_str nbuf;
187 #endif
188
189   OONF_DEBUG(session->log_source, "Remove session if %s to %s", session->l2_listener.name,
190     netaddr_socket_to_string(&nbuf, &session->remote_socket));
191
192   os_interface_remove(&session->l2_listener);
193
194   parser = &session->parser;
195   avl_for_each_element_safe(&parser->allowed_tlvs, tlv, _node, tlv_it) {
196     avl_remove(&parser->allowed_tlvs, &tlv->_node);
197     oonf_class_free(&_tlv_class, tlv);
198   }
199
200   oonf_timer_stop(&session->local_event_timer);
201   oonf_timer_stop(&session->remote_heartbeat_timeout);
202
203   parser->extension_count = 0;
204
205   free(parser->values);
206   parser->values = NULL;
207
208   session->_peer_state = DLEP_PEER_NOT_CONNECTED;
209 }
210
211 /**
212  * Send peer termination
213  * @param session dlep session
214  * @param status DLEP status code for termination
215  * @param status_text text message for termination
216  */
217 void
218 dlep_session_terminate(struct dlep_session *session, enum dlep_status status, const char *status_text) {
219   if (session->restrict_signal != DLEP_ALL_SIGNALS) {
220     dlep_session_generate_signal_status(session, DLEP_SESSION_TERMINATION, NULL, status, status_text);
221     session->cb_send_buffer(session, 0);
222   }
223   session->restrict_signal = DLEP_SESSION_TERMINATION_ACK;
224 }
225
226 /**
227  * Update the list of active dlep extensions for a session
228  * @param session dlep session
229  * @param extvalues array with allowed DLEP sessions
230  * @param extcount number of bytes in array
231  * @return -1 if an error happened, 0 otherwise
232  */
233 int
234 dlep_session_update_extensions(struct dlep_session *session, const uint8_t *extvalues, size_t extcount, bool radio) {
235   struct dlep_extension *ext;
236   size_t i, j;
237   bool deactivate;
238   uint16_t extid;
239   OONF_INFO(session->log_source, "Update session extension list");
240
241   /* deactivate all extensions not present anymore  */
242   for (j = DLEP_EXTENSION_BASE_COUNT; j < session->parser.extension_count; j++) {
243     deactivate = true;
244
245     for (i = 0; i < extcount; i++) {
246       memcpy(&extid, &extvalues[i * 2], sizeof(extid));
247       if (ntohs(extid) == session->parser.extensions[j]->id) {
248         deactivate = false;
249         break;
250       }
251     }
252
253     if (deactivate) {
254       if (radio) {
255         session->parser.extensions[j]->cb_session_deactivate_radio(session);
256       }
257       else {
258         session->parser.extensions[j]->cb_session_deactivate_router(session);
259       }
260     }
261   }
262
263   /* generate new session extension list */
264   session->parser.extension_count = DLEP_EXTENSION_BASE_COUNT;
265   for (i = 0; i < extcount; i++) {
266     memcpy(&extid, &extvalues[i * 2], sizeof(extid));
267
268     ext = dlep_extension_get(ntohs(extid));
269     if (ext) {
270       OONF_INFO(session->log_source, "Add extension: %d", ntohs(extid));
271
272       session->parser.extensions[session->parser.extension_count] = ext;
273       session->parser.extension_count++;
274     }
275   }
276   return _update_allowed_tlvs(session);
277 }
278
279 /**
280  * Process data in DLEP session TCP input buffer
281  * @param tcp_session TCP session
282  * @param session DLEP session
283  * @return new TCP session state
284  */
285 enum oonf_stream_session_state
286 dlep_session_process_tcp(struct oonf_stream_session *tcp_session, struct dlep_session *session)
287 {
288   ssize_t processed;
289
290   OONF_DEBUG(
291     session->log_source, "Process TCP buffer of %" PRINTF_SIZE_T_SPECIFIER " bytes", abuf_getlen(&tcp_session->in));
292
293   processed = dlep_session_process_buffer(session, abuf_getptr(&tcp_session->in), abuf_getlen(&tcp_session->in), false);
294
295   if (processed < 0) {
296     /* session is most likely invalid now */
297     return STREAM_SESSION_CLEANUP;
298   }
299
300   if (session->restrict_signal == DLEP_KILL_SESSION) {
301     return STREAM_SESSION_CLEANUP;
302   }
303
304   OONF_DEBUG(session->log_source, "Processed %" PRINTF_SSIZE_T_SPECIFIER " bytes", processed);
305
306   abuf_pull(&tcp_session->in, processed);
307
308   if (abuf_getlen(session->writer.out) > 0) {
309     OONF_DEBUG(
310       session->log_source, "Trigger sending %" PRINTF_SIZE_T_SPECIFIER " bytes", abuf_getlen(session->writer.out));
311
312     /* send answer */
313     oonf_stream_flush(tcp_session);
314   }
315
316   if (session->restrict_signal == DLEP_KILL_SESSION) {
317     return STREAM_SESSION_CLEANUP;
318   }
319   return STREAM_SESSION_ACTIVE;
320 }
321
322 /**
323  * Process the content of a buffer as DLEP signal(s)
324  * @param session dlep session
325  * @param buffer pointer to buffer
326  * @param length length of buffer
327  * @return number of bytes of buffer which were parsed and
328  *   can be removed, -1 if an error happened
329  */
330 ssize_t
331 dlep_session_process_buffer(struct dlep_session *session, const void *buffer, size_t length, bool is_udp) {
332   ssize_t result, offset;
333   const char *ptr;
334
335   offset = 0;
336   ptr = buffer;
337
338   OONF_DEBUG(session->log_source,
339     "Processing buffer of"
340     " %" PRINTF_SIZE_T_SPECIFIER " bytes",
341     length);
342   while (length > 0) {
343     OONF_DEBUG(session->log_source,
344       "Processing message at offset"
345       " %" PRINTF_SSIZE_T_SPECIFIER,
346       offset);
347
348     if ((result = dlep_session_process_signal(session, &ptr[offset], length, is_udp)) <= 0) {
349       if (result < 0) {
350         return result;
351       }
352       break;
353     }
354
355     if (session->restrict_signal == DLEP_KILL_SESSION) {
356       return offset;
357     }
358     length -= result;
359     offset += result;
360   }
361   return offset;
362 }
363
364 /**
365  * Process a DLEP signal/message
366  * @param session dlep session
367  * @param ptr pointer to buffer with DLEP signal/message
368  * @param length length of buffer
369  * @return number of bytes parsed, 0 if a generic error happened,
370  *   negative to return a parser result enum
371  */
372 ssize_t
373 dlep_session_process_signal(struct dlep_session *session, const void *ptr, size_t length, bool is_udp) {
374   enum dlep_parser_error result;
375   uint16_t original_signal_type;
376   int32_t signal_type;
377   uint16_t signal_length;
378   const uint8_t *buffer;
379 #ifdef OONF_LOG_DEBUG_INFO
380   struct netaddr_str nbuf;
381 #endif
382
383   session->next_restrict_signal = DLEP_KEEP_RESTRICTION;
384
385   if (length < 4) {
386     /* not enough data for a signal type */
387     OONF_DEBUG(session->log_source,
388       "Not enough data to process"
389       " signal from %s (%" PRINTF_SIZE_T_SPECIFIER " bytes)",
390       netaddr_socket_to_string(&nbuf, &session->remote_socket), length);
391
392     return 0;
393   }
394
395   buffer = ptr;
396
397   /* copy data */
398   memcpy(&original_signal_type, &buffer[0], sizeof(original_signal_type));
399   memcpy(&signal_length, &buffer[2], sizeof(signal_length));
400   signal_type = ntohs(original_signal_type);
401   signal_length = ntohs(signal_length);
402
403   if (is_udp) {
404     signal_type += DLEP_IS_UDP_SIGNAL;
405   }
406
407   if (length < (size_t)signal_length + 4u) {
408     /* not enough data for signal */
409     OONF_DEBUG(session->log_source,
410       "Not enough data to process"
411       " signal %u (length %u) from %s"
412       " (%" PRINTF_SIZE_T_SPECIFIER " bytes)",
413       signal_type, signal_length, netaddr_socket_to_string(&nbuf, &session->remote_socket), length);
414     return 0;
415   }
416
417   OONF_DEBUG_HEX(session->log_source, buffer, signal_length + 4,
418     "Process signal %d from %s (%" PRINTF_SIZE_T_SPECIFIER " bytes)", signal_type,
419     netaddr_socket_to_string(&nbuf, &session->remote_socket), length);
420
421   if (session->restrict_signal != DLEP_ALL_SIGNALS && session->restrict_signal != signal_type) {
422     OONF_DEBUG(session->log_source,
423       "Signal should have been %d,"
424       " drop session",
425       session->restrict_signal);
426     /* we only accept a single type and we got the wrong one */
427     return -1;
428   }
429
430   result = _process_tlvs(session, signal_type, signal_length, &buffer[4]);
431
432   if (result == DLEP_NEW_PARSER_TERMINDATED) {
433     /* session is now invalid, end parser */
434     return result;
435   }
436   if (result != DLEP_NEW_PARSER_OKAY) {
437     OONF_WARN(session->log_source, "Parser error: %d", result);
438     _send_terminate(session, DLEP_STATUS_INVALID_DATA, "Incoming signal could not be parsed");
439   }
440   else if (session->next_restrict_signal != DLEP_KEEP_RESTRICTION) {
441     session->restrict_signal = session->next_restrict_signal;
442   }
443
444   /* skip forward */
445   return signal_length + 4;
446 }
447
448 /**
449  * Add a neighbor to the local DLEP storage
450  * @param session dlep session
451  * @param key neighbor key (MAC plus link id)
452  * @return pointer to dlep neighbor, NULL if out of memory
453  */
454 struct dlep_local_neighbor *
455 dlep_session_add_local_neighbor(struct dlep_session *session, const struct oonf_layer2_neigh_key *key) {
456   struct dlep_local_neighbor *local;
457   if ((local = dlep_session_get_local_neighbor(session, key))) {
458     return local;
459   }
460
461   if (key->link_id_length != 0 && key->link_id_length != session->cfg.lid_length) {
462     /* LIDs not allowed */
463     return NULL;
464   }
465   local = oonf_class_malloc(&_local_neighbor_class);
466   if (!local) {
467     return NULL;
468   }
469
470   /* hook into tree */
471   memcpy(&local->key, key, sizeof(*key));
472   local->_node.key = &local->key;
473   avl_insert(&session->local_neighbor_tree, &local->_node);
474
475   /* initialize timer */
476   local->_ack_timeout.class = &_destination_ack_class;
477
478   /* initialize backpointer */
479   local->session = session;
480
481   avl_init(&local->_ip_prefix_modification, avl_comp_netaddr, false);
482
483   return local;
484 }
485
486 /**
487  * Remove a neighbor from the DLEP storage
488  * @param session dlep session
489  * @param local DLEP neighbor
490  */
491 void
492 dlep_session_remove_local_neighbor(struct dlep_session *session, struct dlep_local_neighbor *local) {
493   avl_remove(&session->local_neighbor_tree, &local->_node);
494   oonf_timer_stop(&local->_ack_timeout);
495   oonf_class_free(&_local_neighbor_class, local);
496 }
497
498 /**
499  * Get the layer2 neigbor for a DLEP session MAC address
500  * @param session dlep session
501  * @param key neighbor key (MAC address plus link id)
502  * @return layer2 neighbor, NULL if not found
503  */
504 struct oonf_layer2_neigh *
505 dlep_session_get_local_l2_neighbor(struct dlep_session *session, const struct oonf_layer2_neigh_key *key) {
506   struct dlep_local_neighbor *dlep_neigh;
507   struct oonf_layer2_neigh *l2neigh;
508   struct oonf_layer2_net *l2net;
509 #ifdef OONF_LOG_INFO
510   union oonf_layer2_neigh_key_str nbuf1, nbuf2;
511 #endif
512
513   dlep_neigh = dlep_session_get_local_neighbor(session, key);
514   if (!dlep_neigh) {
515     OONF_INFO(session->log_source, "Could not find local neighbor for %s",
516               oonf_layer2_neigh_key_to_string(&nbuf1, key, true));
517     return NULL;
518   }
519
520   l2net = oonf_layer2_net_get(session->l2_listener.name);
521   if (!l2net) {
522     OONF_DEBUG(session->log_source, "Could not find l2net %s for new neighbor", session->l2_listener.name);
523     return NULL;
524   }
525
526   l2neigh = oonf_layer2_neigh_get_lid(l2net, &dlep_neigh->neigh_key);
527   if (!l2neigh) {
528     OONF_INFO(session->log_source,
529       "Could not find l2neigh for neighbor %s (%s)",
530       oonf_layer2_neigh_key_to_string(&nbuf1, key, true),
531       oonf_layer2_neigh_key_to_string(&nbuf2, &dlep_neigh->neigh_key, true));
532     return NULL;
533   }
534   return l2neigh;
535 }
536
537 struct oonf_layer2_neigh *
538 dlep_session_get_l2_from_neighbor(struct dlep_local_neighbor *dlep_neigh) {
539   struct oonf_layer2_neigh *l2neigh;
540   struct oonf_layer2_net *l2net;
541 #ifdef OONF_LOG_INFO
542   union oonf_layer2_neigh_key_str nbuf;
543 #endif
544
545   l2net = oonf_layer2_net_get(dlep_neigh->session->l2_listener.name);
546   if (!l2net) {
547     OONF_DEBUG(dlep_neigh->session->log_source, "Could not find l2net %s for new neighbor",
548       dlep_neigh->session->l2_listener.name);
549     return NULL;
550   }
551
552   l2neigh = oonf_layer2_neigh_get_lid(l2net, &dlep_neigh->neigh_key);
553   if (!l2neigh) {
554     OONF_INFO(dlep_neigh->session->log_source,
555       "Could not find l2neigh for neighbor %s",
556       oonf_layer2_neigh_key_to_string(&nbuf, &dlep_neigh->neigh_key, true));
557     return NULL;
558   }
559   return l2neigh;
560 }
561
562 /**
563  * Generate a DLEP signal/message
564  * @param session dlep session
565  * @param signal signal id
566  * @param neighbor neighbor MAC address the signal should refer to,
567  *   might be NULL
568  * @return -1 if an error happened, 0 otherwise
569  */
570 static int
571 _generate_signal(struct dlep_session *session, int32_t signal, const struct oonf_layer2_neigh_key *neighbor) {
572   struct dlep_extension *ext;
573   size_t e, s;
574
575   size_t len;
576 #ifdef OONF_LOG_DEBUG_INFO
577   union oonf_layer2_neigh_key_str nkbuf;
578   struct netaddr_str nbuf2;
579 #endif
580
581   OONF_DEBUG(session->log_source, "Generate signal %u for %s on %s (%s)", signal,
582              oonf_layer2_neigh_key_to_string(&nkbuf, neighbor, true),
583              session->l2_listener.name, netaddr_socket_to_string(&nbuf2, &session->remote_socket));
584
585   len = abuf_getlen(session->writer.out);
586
587   /* generate signal, mask out UDP/TCP difference */
588   dlep_writer_start_signal(&session->writer, signal & 65535);
589   for (e = 0; e < session->parser.extension_count; e++) {
590     ext = session->parser.extensions[e];
591
592     for (s = 0; s < ext->signal_count; s++) {
593       if (ext->signals[s].id != signal) {
594         continue;
595       }
596
597       if (session->radio && ext->signals[s].add_radio_tlvs) {
598         OONF_DEBUG(session->log_source, "Add tlvs for radio extension %d", ext->id);
599         if (ext->signals[s].add_radio_tlvs(ext, session, neighbor)) {
600           abuf_setlen(session->writer.out, len);
601           return -1;
602         }
603       }
604       else if (!session->radio && ext->signals[s].add_router_tlvs) {
605         OONF_DEBUG(session->log_source, "Add tlvs for router extension %d", ext->id);
606         if (ext->signals[s].add_router_tlvs(ext, session, neighbor)) {
607           abuf_setlen(session->writer.out, len);
608           return -1;
609         }
610       }
611       break;
612     }
613   }
614
615   OONF_DEBUG(
616     session->log_source, "generated %" PRINTF_SIZE_T_SPECIFIER " bytes", abuf_getlen(session->writer.out) - len);
617   return 0;
618 }
619
620 /**
621  * Generate a DLEP signal/message
622  * @param session dlep session
623  * @param signal signal id
624  * @param neighbor neighbor MAC address (plus link id) the signal should refer to,
625  *   might be NULL
626  * @return -1 if an error happened, 0 otherwise
627  */
628 int
629 dlep_session_generate_signal(struct dlep_session *session, int32_t signal, const struct oonf_layer2_neigh_key *neighbor) {
630   if (_generate_signal(session, signal, neighbor)) {
631     OONF_WARN(session->log_source, "Could not generate signal %u", signal);
632     return -1;
633   }
634   return dlep_writer_finish_signal(&session->writer, session->log_source);
635 }
636
637 /**
638  * Generate a DLEP signal/message with a DLEP status TLV
639  * @param session dlep session
640  * @param signal signal id
641  * @param neighbor neighbor MAC address (plus link id) the signal should refer to,
642  *   might be NULL
643  * @param status DLEP status code
644  * @param msg ZERO terminated DLEP status text
645  * @return -1 if an error happened, 0 otherwise
646  */
647 int
648 dlep_session_generate_signal_status(struct dlep_session *session, int32_t signal, const struct oonf_layer2_neigh_key *neighbor,
649   enum dlep_status status, const char *msg) {
650   if (_generate_signal(session, signal, neighbor)) {
651     OONF_WARN(session->log_source, "Could not generate signal %u", signal);
652     return -1;
653   }
654   if (dlep_writer_add_status(&session->writer, status, msg)) {
655     OONF_WARN(session->log_source, "Could not add status TLV");
656     return -1;
657   }
658   return dlep_writer_finish_signal(&session->writer, session->log_source);
659 }
660
661 /**
662  * Get the value of the first DLEP TLV of a specific type
663  * @param session dlep session
664  * @param tlvtype DLEP TLV type
665  * @return DLEP value, NULL if not found
666  */
667 struct dlep_parser_value *
668 dlep_session_get_tlv_value(struct dlep_session *session, uint16_t tlvtype) {
669   struct dlep_parser_tlv *tlv;
670   struct dlep_parser_value *value;
671
672   tlv = dlep_parser_get_tlv(&session->parser, tlvtype);
673   if (!tlv) {
674     OONF_INFO(session->log_source, "Could not find TLV type %u", tlvtype);
675     return NULL;
676   }
677
678   value = dlep_session_get_tlv_first_value(session, tlv);
679   if (!value) {
680     OONF_INFO(session->log_source, "Could not find value of TLV type %u", tlvtype);
681     return NULL;
682   }
683   else {
684     OONF_DEBUG(session->log_source, "TLV %u has value", tlvtype);
685   }
686   return value;
687 }
688
689 /**
690  * Update the list of allowed TLVs based on the list of allowed
691  * extensions
692  * @param session dlep session
693  * @return -1 if extensions are inconsistent, 0 otherwise
694  */
695 static int
696 _update_allowed_tlvs(struct dlep_session *session) {
697   struct dlep_session_parser *parser;
698   struct dlep_parser_tlv *tlv, *tlv_it;
699   struct dlep_extension *ext;
700   size_t e, t;
701   uint16_t id;
702
703   parser = &session->parser;
704
705   /* mark all existing allowed tlvs */
706   avl_for_each_element_safe(&parser->allowed_tlvs, tlv, _node, tlv_it) {
707     tlv->remove = true;
708   }
709
710   /* allocate new allowed tlvs structures */
711   for (e = 0; e < parser->extension_count; e++) {
712     ext = parser->extensions[e];
713
714     /* for all extensions */
715     for (t = 0; t < ext->tlv_count; t++) {
716       /* for all tlvs */
717       id = ext->tlvs[t].id;
718       tlv = dlep_parser_get_tlv(parser, id);
719       if (!tlv) {
720         /* new tlv found! */
721         if (!(tlv = _add_session_tlv(parser, id))) {
722           return -1;
723         }
724         tlv->length_min = ext->tlvs[t].length_min;
725         tlv->length_max = ext->tlvs[t].length_max;
726       }
727       else if (tlv->length_min != ext->tlvs[t].length_min || tlv->length_max != ext->tlvs[t].length_max) {
728         OONF_WARN(session->log_source,
729           "Two extensions conflict about"
730           " tlv %u minimal/maximum length",
731           id);
732         return -1;
733       }
734
735       tlv->remove = false;
736     }
737   }
738
739   /* remove all existing allowed tlvs that are not supported anymore */
740   avl_for_each_element_safe(&parser->allowed_tlvs, tlv, _node, tlv_it) {
741     if (tlv->remove) {
742       avl_remove(&parser->allowed_tlvs, &tlv->_node);
743       oonf_class_free(&_tlv_class, tlv);
744     }
745   }
746
747   return 0;
748 }
749
750 /**
751  * Check constraints of extensions and call the relevant callbacks
752  * @param session dlep session
753  * @param ext dlep extension
754  * @param signal_type signal type
755  * @return dlep parser error, 0 of everything is fine
756  */
757 static enum dlep_parser_error
758 _handle_extension(struct dlep_session *session, struct dlep_extension *ext, uint32_t signal_type) {
759   enum dlep_parser_error result;
760   bool active;
761   size_t e;
762
763   active = false;
764
765   /* only handle active extensions */
766   for (e = 0; e < session->parser.extension_count; e++) {
767     if (session->parser.extensions[e] == ext) {
768       active = true;
769       break;
770     }
771   }
772   if (!active) {
773     /* not active at the moment */
774     return DLEP_NEW_PARSER_OKAY;
775   }
776
777   if ((result = _check_mandatory(session, ext, signal_type))) {
778     OONF_DEBUG(session->log_source, "check_mandatory result: %d", result);
779     return result;
780   }
781   if ((result = _check_duplicate(session, ext, signal_type))) {
782     OONF_DEBUG(session->log_source, "check_duplicate result: %d", result);
783     return result;
784   }
785
786   if ((result = _call_extension_processing(session, ext, signal_type))) {
787     OONF_DEBUG(session->log_source, "extension processing failed: %d", result);
788     return result;
789   }
790
791   return DLEP_NEW_PARSER_OKAY;
792 }
793
794 /**
795  * Parse a DLEP tlv
796  * @param session dlep session
797  * @param signal_type dlep signal/message type
798  * @param signal_length signal/message length
799  * @param tlvs pointer to bytearray with TLVs
800  * @return dlep parser status
801  */
802 static enum dlep_parser_error
803 _process_tlvs(struct dlep_session *session, int32_t signal_type, uint16_t signal_length, const uint8_t *tlvs) {
804   enum dlep_parser_error result;
805   struct dlep_extension *ext;
806
807   /* start at the beginning of the tlvs */
808   if ((result = _parse_tlvstream(session, tlvs, signal_length))) {
809     OONF_DEBUG(session->log_source, "parse_tlvstream result: %d", result);
810     return result;
811   }
812
813   avl_for_each_element(dlep_extension_get_tree(), ext, _node) {
814     if ((result = _handle_extension(session, ext, signal_type))) {
815       return result;
816     }
817   }
818
819   return DLEP_NEW_PARSER_OKAY;
820 }
821
822 /**
823  * terminate a DLEP session
824  * @param session dlep session
825  * @param status DLEP status code for termination
826  * @param status_text text message for termination
827  */
828 static void
829 _send_terminate(struct dlep_session *session, enum dlep_status status, const char *status_text) {
830   if (session->restrict_signal != DLEP_UDP_PEER_DISCOVERY && session->restrict_signal != DLEP_UDP_PEER_OFFER) {
831     dlep_session_generate_signal_status(session, DLEP_SESSION_TERMINATION, NULL, status, status_text);
832
833     session->restrict_signal = DLEP_SESSION_TERMINATION_ACK;
834     session->next_restrict_signal = DLEP_SESSION_TERMINATION_ACK;
835   }
836 }
837
838 /**
839  * Callback when a destination up/down signal times out
840  * @param ptr timer instance that fired
841  */
842 static void
843 _cb_destination_timeout(struct oonf_timer_instance *ptr) {
844   struct dlep_local_neighbor *local;
845
846   local = container_of(ptr, struct dlep_local_neighbor, _ack_timeout);
847   if (local->session->cb_destination_timeout) {
848     local->session->cb_destination_timeout(local->session, local);
849   }
850 }
851
852 /**
853  * parse a stream of DLEP tlvs
854  * @param session dlep session
855  * @param buffer TLV buffer
856  * @param length buffer size
857  * @return DLEP parser status
858  */
859 static enum dlep_parser_error
860 _parse_tlvstream(struct dlep_session *session, const uint8_t *buffer, size_t length) {
861   struct dlep_session_parser *parser;
862   struct dlep_parser_tlv *tlv;
863   struct dlep_parser_value *value;
864   uint16_t tlv_type;
865   uint16_t tlv_length;
866   size_t tlv_count, idx;
867
868   parser = &session->parser;
869   parser->tlv_ptr = buffer;
870   tlv_count = 0;
871   idx = 0;
872
873   avl_for_each_element(&parser->allowed_tlvs, tlv, _node) {
874     tlv->tlv_first = -1;
875     tlv->tlv_last = -1;
876   }
877
878   while (idx < length) {
879     if (length - idx < 4) {
880       /* too short for a TLV, end parsing */
881       return DLEP_NEW_PARSER_INCOMPLETE_TLV_HEADER;
882     }
883
884     /* copy header */
885     memcpy(&tlv_type, &buffer[idx], sizeof(tlv_type));
886     idx += sizeof(tlv_type);
887     tlv_type = ntohs(tlv_type);
888
889     memcpy(&tlv_length, &buffer[idx], sizeof(tlv_length));
890     idx += sizeof(tlv_length);
891     tlv_length = ntohs(tlv_length);
892
893     if (idx + tlv_length > length) {
894       OONF_WARN(session->log_source,
895         "TLV %u incomplete: "
896         "%" PRINTF_SIZE_T_SPECIFIER " > %" PRINTF_SIZE_T_SPECIFIER,
897         tlv_type, idx + tlv_length, length);
898       return DLEP_NEW_PARSER_INCOMPLETE_TLV;
899     }
900
901     /* check if tlv is supported */
902     tlv = dlep_parser_get_tlv(parser, tlv_type);
903     if (!tlv) {
904       OONF_INFO(session->log_source, "Unsupported TLV %u", tlv_type);
905       return DLEP_NEW_PARSER_UNSUPPORTED_TLV;
906     }
907
908     /* check length */
909     if (tlv->length_max < tlv_length || tlv->length_min > tlv_length) {
910       OONF_WARN(session->log_source,
911         "TLV %u has wrong size,"
912         " %d is not between %u and %u",
913         tlv_type, tlv_length, tlv->length_min, tlv->length_max);
914       return DLEP_NEW_PARSER_ILLEGAL_TLV_LENGTH;
915     }
916
917     /* check if we need to allocate more space for value pointers */
918     if (parser->value_max_count == tlv_count) {
919       /* allocate more */
920       value = realloc(parser->values, sizeof(*value) * (tlv_count + SESSION_VALUE_STEP));
921       if (!value) {
922         return DLEP_NEW_PARSER_OUT_OF_MEMORY;
923       }
924       parser->value_max_count += SESSION_VALUE_STEP;
925       parser->values = value;
926     }
927
928     OONF_DEBUG_HEX(session->log_source, &buffer[idx], tlv_length, "Received TLV %u", tlv_type);
929
930     /* remember tlv value */
931     value = &parser->values[tlv_count];
932     value->tlv_next = -1;
933     value->index = idx;
934     value->length = tlv_length;
935
936     if (tlv->tlv_last == -1) {
937       /* first tlv */
938       tlv->tlv_first = tlv_count;
939     }
940     else {
941       /* one more */
942       value = &parser->values[tlv->tlv_last];
943       value->tlv_next = tlv_count;
944     }
945     tlv->tlv_last = tlv_count;
946     tlv_count++;
947
948     idx += tlv_length;
949   }
950
951   return DLEP_NEW_PARSER_OKAY;
952 }
953
954 /**
955  * Check if all mandatory TLVs were found
956  * @param session dlep session
957  * @param ext dlep extension
958  * @param signal_type dlep signal/message type
959  * @return dlep parser status
960  */
961 static enum dlep_parser_error
962 _check_mandatory(struct dlep_session *session, struct dlep_extension *ext, int32_t signal_type) {
963   struct dlep_session_parser *parser;
964   struct dlep_parser_tlv *tlv;
965   struct dlep_extension_signal *extsig;
966   size_t s, t;
967
968   parser = &session->parser;
969
970   extsig = NULL;
971   for (s = 0; s < ext->signal_count; s++) {
972     if (ext->signals[s].id == signal_type) {
973       extsig = &ext->signals[s];
974       break;
975     }
976   }
977
978   if (!extsig) {
979     return DLEP_NEW_PARSER_OKAY;
980   }
981
982   for (t = 0; t < extsig->mandatory_tlv_count; t++) {
983     tlv = dlep_parser_get_tlv(parser, extsig->mandatory_tlvs[t]);
984     if (!tlv) {
985       OONF_WARN(session->log_source,
986         "Could not find tlv data for"
987         " mandatory TLV %u in extension %d",
988         extsig->mandatory_tlvs[t], ext->id);
989       return DLEP_NEW_PARSER_INTERNAL_ERROR;
990     }
991
992     if (tlv->tlv_first == -1) {
993       OONF_WARN(session->log_source,
994         "Missing mandatory TLV"
995         " %u in extension %d",
996         extsig->mandatory_tlvs[t], ext->id);
997       return DLEP_NEW_PARSER_MISSING_MANDATORY_TLV;
998     }
999   }
1000   return DLEP_NEW_PARSER_OKAY;
1001 }
1002
1003 /**
1004  * Check if all duplicate TLVs were allowed to be duplicates
1005  * @param session dlep session
1006  * @param ext dlep extension
1007  * @param signal_type dlep signal/message type
1008  * @return dlep parser status
1009  */
1010 static enum dlep_parser_error
1011 _check_duplicate(struct dlep_session *session, struct dlep_extension *ext, int32_t signal_type) {
1012   struct dlep_session_parser *parser;
1013   struct dlep_parser_tlv *tlv;
1014   struct dlep_extension_signal *extsig;
1015   size_t s, t, dt;
1016   bool okay;
1017
1018   parser = &session->parser;
1019
1020   extsig = NULL;
1021   for (s = 0; s < ext->signal_count; s++) {
1022     extsig = &ext->signals[s];
1023     if (ext->signals[s].id == signal_type) {
1024       extsig = &ext->signals[s];
1025       break;
1026     }
1027   }
1028
1029   if (!extsig) {
1030     return DLEP_NEW_PARSER_OKAY;
1031   }
1032
1033   for (t = 0; t < extsig->supported_tlv_count; t++) {
1034     tlv = avl_find_element(&parser->allowed_tlvs, &extsig->supported_tlvs[t], tlv, _node);
1035     if (tlv == NULL || tlv->tlv_first == tlv->tlv_last) {
1036       continue;
1037     }
1038
1039     okay = false;
1040     for (dt = 0; dt < extsig->duplicate_tlv_count; dt++) {
1041       if (extsig->duplicate_tlvs[dt] == tlv->id) {
1042         okay = true;
1043         break;
1044       }
1045     }
1046     if (!okay) {
1047       OONF_WARN(session->log_source,
1048         "Duplicate not allowed"
1049         " for TLV %u in extension %d",
1050         tlv->id, ext->id);
1051       return DLEP_NEW_PARSER_DUPLICATE_TLV;
1052     }
1053   }
1054   return DLEP_NEW_PARSER_OKAY;
1055 }
1056
1057 /**
1058  * Call extension processing hooks for parsed signal/message
1059  * @param session dlep session
1060  * @param ext dlep_extension
1061  * @param signal_type dlep signal/message type
1062  * @return dlep parser status
1063  */
1064 static enum dlep_parser_error
1065 _call_extension_processing(struct dlep_session *session, struct dlep_extension *ext, int32_t signal_type) {
1066   size_t s;
1067
1068   for (s = 0; s < ext->signal_count; s++) {
1069     if (ext->signals[s].id != signal_type) {
1070       continue;
1071     }
1072
1073     if (session->radio) {
1074       if (ext->signals[s].process_radio && ext->signals[s].process_radio(ext, session)) {
1075         OONF_DEBUG(session->log_source, "Error in radio signal processing of extension '%s'", ext->name);
1076         return -1;
1077       }
1078     }
1079     else {
1080       if (ext->signals[s].process_router && ext->signals[s].process_router(ext, session)) {
1081         OONF_DEBUG(session->log_source, "Error in router signal processing of extension '%s'", ext->name);
1082         return -1;
1083       }
1084     }
1085     break;
1086   }
1087   return DLEP_NEW_PARSER_OKAY;
1088 }
1089
1090 /**
1091  * Add a TLV to the allowed TLVs of a DLEP session
1092  * @param parser dlep session parser
1093  * @param id DLEP TLV id
1094  * @return dlep parser TLV, NULL if out of memory
1095  */
1096 static struct dlep_parser_tlv *
1097 _add_session_tlv(struct dlep_session_parser *parser, uint16_t id) {
1098   struct dlep_parser_tlv *tlv;
1099
1100   tlv = oonf_class_malloc(&_tlv_class);
1101   if (!tlv) {
1102     return NULL;
1103   }
1104
1105   tlv->id = id;
1106   tlv->_node.key = &tlv->id;
1107   tlv->tlv_first = -1;
1108   tlv->tlv_last = -1;
1109
1110   avl_insert(&parser->allowed_tlvs, &tlv->_node);
1111   return tlv;
1112 }