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