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