16d7fad0ae13818e65943d37b33d3b0e72a87136
[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/avl.h"
47 #include "common/avl_comp.h"
48 #include "common/common_types.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_session.h"
56 #include "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);
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   int32_t i;
128
129   parser = &session->parser;
130
131   avl_init(&parser->allowed_tlvs, avl_comp_uint16, false);
132   avl_init(&session->local_neighbor_tree, avl_comp_netaddr, false);
133
134   session->log_source = log_source;
135   session->l2_origin = l2_origin;
136   session->l2_default_origin = l2_default_origin;
137   session->radio = radio;
138   session->writer.out = out;
139   session->_peer_state = DLEP_PEER_WAIT_FOR_INIT;
140
141   /* remember interface name */
142   session->l2_listener.name = l2_ifname;
143   session->l2_listener.if_changed = if_changed;
144
145   /* get interface listener to lock interface */
146   if (!os_interface_add(&session->l2_listener)) {
147     OONF_WARN(session->log_source, "Cannot activate interface listener for %s", l2_ifname);
148     dlep_session_remove(session);
149     return -1;
150   }
151
152   /* allocate memory for the pointers */
153   parser->extensions = calloc(dlep_extension_get_tree()->count, sizeof(struct dlep_extension *));
154   if (!parser->extensions) {
155     OONF_WARN(session->log_source, "Cannot allocate extension buffer for %s", l2_ifname);
156     dlep_session_remove(session);
157     return -1;
158   }
159
160   /* remember the sessions */
161   parser->extension_count = dlep_extension_get_tree()->count;
162
163   parser->values = calloc(SESSION_VALUE_STEP, sizeof(struct dlep_parser_value));
164   if (!parser->values) {
165     OONF_WARN(session->log_source, "Cannot allocate values buffer for %s", l2_ifname);
166     dlep_session_remove(session);
167     return -1;
168   }
169
170   i = 0;
171   avl_for_each_element(dlep_extension_get_tree(), ext, _node) {
172     OONF_DEBUG(session->log_source, "Add extension %d to session", ext->id);
173     parser->extensions[i] = ext;
174     i++;
175   }
176
177   if (_update_allowed_tlvs(session)) {
178     OONF_WARN(session->log_source, "Could not update allowed TLVs for %s", l2_ifname);
179     dlep_session_remove(session);
180     return -1;
181   }
182
183   avl_init(&session->_ip_prefix_modification, avl_comp_netaddr, false);
184
185   OONF_INFO(session->log_source, "Add session on %s", session->l2_listener.name);
186   return 0;
187 }
188
189 /**
190  * Remove a DLEP session
191  * @param session dlep session
192  */
193 void
194 dlep_session_remove(struct dlep_session *session) {
195   struct dlep_parser_tlv *tlv, *tlv_it;
196   struct dlep_session_parser *parser;
197 #ifdef OONF_LOG_DEBUG_INFO
198   struct netaddr_str nbuf;
199 #endif
200
201   OONF_DEBUG(session->log_source, "Remove session if %s to %s", session->l2_listener.name,
202     netaddr_socket_to_string(&nbuf, &session->remote_socket));
203
204   os_interface_remove(&session->l2_listener);
205
206   parser = &session->parser;
207   avl_for_each_element_safe(&parser->allowed_tlvs, tlv, _node, tlv_it) {
208     avl_remove(&parser->allowed_tlvs, &tlv->_node);
209     oonf_class_free(&_tlv_class, tlv);
210   }
211
212   oonf_timer_stop(&session->local_event_timer);
213   oonf_timer_stop(&session->remote_heartbeat_timeout);
214
215   free(parser->extensions);
216   parser->extensions = NULL;
217
218   free(parser->values);
219   parser->values = NULL;
220
221   session->_peer_state = DLEP_PEER_NOT_CONNECTED;
222 }
223
224 /**
225  * Send peer termination
226  * @param session dlep session
227  */
228 void
229 dlep_session_terminate(struct dlep_session *session) {
230   if (session->restrict_signal != DLEP_ALL_SIGNALS) {
231     dlep_session_generate_signal(session, DLEP_SESSION_TERMINATION, NULL);
232     session->cb_send_buffer(session, 0);
233   }
234   session->restrict_signal = DLEP_SESSION_TERMINATION_ACK;
235 }
236
237 /**
238  * Update the list of active dlep extensions for a session
239  * @param session dlep session
240  * @param extvalues array with allowed DLEP sessions
241  * @param extcount number of bytes in array
242  * @return -1 if an error happened, 0 otherwise
243  */
244 int
245 dlep_session_update_extensions(struct dlep_session *session, const uint8_t *extvalues, size_t extcount) {
246   struct dlep_extension **ext_array, *ext;
247   size_t count, i;
248   uint16_t extid;
249
250   OONF_INFO(session->log_source, "Update session extension list");
251
252   /* keep entry a few entries untouched, these are the base extensions */
253   count = DLEP_EXTENSION_BASE_COUNT;
254   for (i = 0; i < extcount; i++) {
255     memcpy(&extid, &extvalues[i * 2], sizeof(extid));
256
257     if (dlep_extension_get(ntohs(extid))) {
258       count++;
259       OONF_INFO(session->log_source, "Add extension: %d", ntohs(extid));
260     }
261   }
262
263   if (count != session->parser.extension_count) {
264     ext_array = realloc(session->parser.extensions, sizeof(struct dlep_extension *) * count);
265     if (!ext_array) {
266       return -1;
267     }
268
269     session->parser.extensions = ext_array;
270     session->parser.extension_count = count;
271   }
272
273   count = DLEP_EXTENSION_BASE_COUNT;
274   for (i = 0; i < extcount; i++) {
275     memcpy(&extid, &extvalues[i * 2], sizeof(extid));
276     if ((ext = dlep_extension_get(ntohs(extid)))) {
277       session->parser.extensions[count] = ext;
278       count++;
279     }
280   }
281
282   return _update_allowed_tlvs(session);
283 }
284
285 /**
286  * Process data in DLEP session TCP input buffer
287  * @param tcp_session TCP session
288  * @param session DLEP session
289  * @return new TCP session state
290  */
291 enum oonf_stream_session_state
292 dlep_session_process_tcp(struct oonf_stream_session *tcp_session, struct dlep_session *session)
293 {
294   ssize_t processed;
295
296   OONF_DEBUG(
297     session->log_source, "Process TCP buffer of %" PRINTF_SIZE_T_SPECIFIER " bytes", abuf_getlen(&tcp_session->in));
298
299   processed = dlep_session_process_buffer(session, abuf_getptr(&tcp_session->in), abuf_getlen(&tcp_session->in), false);
300
301   if (processed < 0) {
302     /* session is most likely invalid now */
303     return STREAM_SESSION_CLEANUP;
304   }
305
306   if (session->restrict_signal == DLEP_KILL_SESSION) {
307     return STREAM_SESSION_CLEANUP;
308   }
309
310   OONF_DEBUG(session->log_source, "Processed %" PRINTF_SSIZE_T_SPECIFIER " bytes", processed);
311
312   abuf_pull(&tcp_session->in, processed);
313
314   if (abuf_getlen(session->writer.out) > 0) {
315     OONF_DEBUG(
316       session->log_source, "Trigger sending %" PRINTF_SIZE_T_SPECIFIER " bytes", abuf_getlen(session->writer.out));
317
318     /* send answer */
319     oonf_stream_flush(tcp_session);
320   }
321
322   if (session->restrict_signal == DLEP_KILL_SESSION) {
323     return STREAM_SESSION_CLEANUP;
324   }
325   return STREAM_SESSION_ACTIVE;
326 }
327
328 /**
329  * Process the content of a buffer as DLEP signal(s)
330  * @param session dlep session
331  * @param buffer pointer to buffer
332  * @param length length of buffer
333  * @return number of bytes of buffer which were parsed and
334  *   can be removed, -1 if an error happened
335  */
336 ssize_t
337 dlep_session_process_buffer(struct dlep_session *session, const void *buffer, size_t length, bool is_udp) {
338   ssize_t result, offset;
339   const char *ptr;
340
341   offset = 0;
342   ptr = buffer;
343
344   OONF_DEBUG(session->log_source,
345     "Processing buffer of"
346     " %" PRINTF_SIZE_T_SPECIFIER " bytes",
347     length);
348   while (length > 0) {
349     OONF_DEBUG(session->log_source,
350       "Processing message at offset"
351       " %" PRINTF_SSIZE_T_SPECIFIER,
352       offset);
353
354     if ((result = dlep_session_process_signal(session, &ptr[offset], length, is_udp)) <= 0) {
355       if (result < 0) {
356         return result;
357       }
358       break;
359     }
360
361     if (session->restrict_signal == DLEP_KILL_SESSION) {
362       return offset;
363     }
364     length -= result;
365     offset += result;
366   }
367   return offset;
368 }
369
370 /**
371  * Process a DLEP signal/message
372  * @param session dlep session
373  * @param ptr pointer to buffer with DLEP signal/message
374  * @param length length of buffer
375  * @return number of bytes parsed, 0 if a generic error happened,
376  *   negative to return a parser result enum
377  */
378 ssize_t
379 dlep_session_process_signal(struct dlep_session *session, const void *ptr, size_t length, bool is_udp) {
380   enum dlep_parser_error result;
381   uint16_t original_signal_type;
382   int32_t signal_type;
383   uint16_t signal_length;
384   const uint8_t *buffer;
385 #ifdef OONF_LOG_DEBUG_INFO
386   struct netaddr_str nbuf;
387 #endif
388
389   session->next_restrict_signal = DLEP_KEEP_RESTRICTION;
390
391   if (length < 4) {
392     /* not enough data for a signal type */
393     OONF_DEBUG(session->log_source,
394       "Not enough data to process"
395       " signal from %s (%" PRINTF_SIZE_T_SPECIFIER " bytes)",
396       netaddr_socket_to_string(&nbuf, &session->remote_socket), length);
397
398     return 0;
399   }
400
401   buffer = ptr;
402
403   /* copy data */
404   memcpy(&original_signal_type, &buffer[0], sizeof(original_signal_type));
405   memcpy(&signal_length, &buffer[2], sizeof(signal_length));
406   signal_type = ntohs(original_signal_type);
407   signal_length = ntohs(signal_length);
408
409   if (is_udp) {
410     signal_type += DLEP_IS_UDP_SIGNAL;
411   }
412
413   if (length < (size_t)signal_length + 4u) {
414     /* not enough data for signal */
415     OONF_DEBUG(session->log_source,
416       "Not enough data to process"
417       " signal %u (length %u) from %s"
418       " (%" PRINTF_SIZE_T_SPECIFIER " bytes)",
419       signal_type, signal_length, netaddr_socket_to_string(&nbuf, &session->remote_socket), length);
420     return 0;
421   }
422
423   OONF_DEBUG_HEX(session->log_source, buffer, signal_length + 4,
424     "Process signal %d from %s (%" PRINTF_SIZE_T_SPECIFIER " bytes)", signal_type,
425     netaddr_socket_to_string(&nbuf, &session->remote_socket), length);
426
427   if (session->restrict_signal != DLEP_ALL_SIGNALS && session->restrict_signal != signal_type) {
428     OONF_DEBUG(session->log_source,
429       "Signal should have been %d,"
430       " drop session",
431       session->restrict_signal);
432     /* we only accept a single type and we got the wrong one */
433     return -1;
434   }
435
436   result = _process_tlvs(session, signal_type, signal_length, &buffer[4]);
437
438   if (result == DLEP_NEW_PARSER_TERMINDATED) {
439     /* session is now invalid, end parser */
440     return result;
441   }
442   if (result != DLEP_NEW_PARSER_OKAY) {
443     OONF_WARN(session->log_source, "Parser error: %d", result);
444     _send_terminate(session);
445   }
446   else if (session->next_restrict_signal != DLEP_KEEP_RESTRICTION) {
447     session->restrict_signal = session->next_restrict_signal;
448   }
449
450   /* skip forward */
451   return signal_length + 4;
452 }
453
454 /**
455  * Add a neighbor to the local DLEP storage
456  * @param session dlep session
457  * @param neigh neighbor MAC address
458  * @return pointer to dlep neighbor, NULL if out of memory
459  */
460 struct dlep_local_neighbor *
461 dlep_session_add_local_neighbor(struct dlep_session *session, const struct netaddr *neigh) {
462   struct dlep_local_neighbor *local;
463   if ((local = dlep_session_get_local_neighbor(session, neigh))) {
464     return local;
465   }
466
467   local = oonf_class_malloc(&_local_neighbor_class);
468   if (!local) {
469     return NULL;
470   }
471
472   /* hook into tree */
473   memcpy(&local->addr, neigh, sizeof(local->addr));
474   local->_node.key = &local->addr;
475   avl_insert(&session->local_neighbor_tree, &local->_node);
476
477   /* initialize timer */
478   local->_ack_timeout.class = &_destination_ack_class;
479
480   /* initialize backpointer */
481   local->session = session;
482
483   avl_init(&local->_ip_prefix_modification, avl_comp_netaddr, false);
484
485   return local;
486 }
487
488 /**
489  * Remove a neighbor from the DLEP storage
490  * @param session dlep session
491  * @param local DLEP neighbor
492  */
493 void
494 dlep_session_remove_local_neighbor(struct dlep_session *session, struct dlep_local_neighbor *local) {
495   avl_remove(&session->local_neighbor_tree, &local->_node);
496   oonf_timer_stop(&local->_ack_timeout);
497   oonf_class_free(&_local_neighbor_class, local);
498 }
499
500 /**
501  * Get the layer2 neigbor for a DLEP session MAC address
502  * @param session dlep session
503  * @param neigh MAC address of neighbor
504  * @return layer2 neighbor, NULL if not found
505  */
506 struct oonf_layer2_neigh *
507 dlep_session_get_local_l2_neighbor(struct dlep_session *session, const struct netaddr *neigh) {
508   struct dlep_local_neighbor *dlep_neigh;
509   struct oonf_layer2_neigh *l2neigh;
510   struct oonf_layer2_net *l2net;
511 #ifdef OONF_LOG_INFO
512   struct netaddr_str nbuf1, nbuf2;
513 #endif
514
515   dlep_neigh = dlep_session_get_local_neighbor(session, neigh);
516   if (!dlep_neigh) {
517     OONF_INFO(session->log_source, "Could not find local neighbor for %s", netaddr_to_string(&nbuf1, neigh));
518     return NULL;
519   }
520
521   l2net = oonf_layer2_net_get(session->l2_listener.name);
522   if (!l2net) {
523     OONF_DEBUG(session->log_source, "Could not find l2net %s for new neighbor", session->l2_listener.name);
524     return NULL;
525   }
526
527   l2neigh = oonf_layer2_neigh_get(l2net, &dlep_neigh->neigh_addr);
528   if (!l2neigh) {
529     OONF_INFO(session->log_source,
530       "Could not find l2neigh "
531       "for neighbor %s (%s)",
532       netaddr_to_string(&nbuf1, neigh), netaddr_to_string(&nbuf2, &dlep_neigh->neigh_addr));
533     return NULL;
534   }
535   return l2neigh;
536 }
537
538 struct oonf_layer2_neigh *
539 dlep_session_get_l2_from_neighbor(struct dlep_local_neighbor *dlep_neigh) {
540   struct oonf_layer2_neigh *l2neigh;
541   struct oonf_layer2_net *l2net;
542 #ifdef OONF_LOG_INFO
543   struct netaddr_str nbuf;
544 #endif
545
546   l2net = oonf_layer2_net_get(dlep_neigh->session->l2_listener.name);
547   if (!l2net) {
548     OONF_DEBUG(dlep_neigh->session->log_source, "Could not find l2net %s for new neighbor",
549       dlep_neigh->session->l2_listener.name);
550     return NULL;
551   }
552
553   l2neigh = oonf_layer2_neigh_get(l2net, &dlep_neigh->neigh_addr);
554   if (!l2neigh) {
555     OONF_INFO(dlep_neigh->session->log_source,
556       "Could not find l2neigh "
557       "for neighbor %s",
558       netaddr_to_string(&nbuf, &dlep_neigh->neigh_addr));
559     return NULL;
560   }
561   return l2neigh;
562 }
563
564 /**
565  * Generate a DLEP signal/message
566  * @param session dlep session
567  * @param signal signal id
568  * @param neighbor neighbor MAC address the signal should refer to,
569  *   might be NULL
570  * @return -1 if an error happened, 0 otherwise
571  */
572 static int
573 _generate_signal(struct dlep_session *session, int32_t signal, const struct netaddr *neighbor) {
574   struct dlep_extension *ext;
575   size_t e, s;
576
577   size_t len;
578 #ifdef OONF_LOG_DEBUG_INFO
579   struct netaddr_str nbuf1, nbuf2;
580 #endif
581
582   OONF_DEBUG(session->log_source, "Generate signal %u for %s on %s (%s)", signal, netaddr_to_string(&nbuf1, neighbor),
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 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 netaddr *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 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 netaddr *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  */
826 static void
827 _send_terminate(struct dlep_session *session) {
828   if (session->restrict_signal != DLEP_UDP_PEER_DISCOVERY && session->restrict_signal != DLEP_UDP_PEER_OFFER) {
829     dlep_session_generate_signal(session, DLEP_SESSION_TERMINATION, NULL);
830
831     session->restrict_signal = DLEP_SESSION_TERMINATION_ACK;
832     session->next_restrict_signal = DLEP_SESSION_TERMINATION_ACK;
833   }
834 }
835
836 /**
837  * Callback when a destination up/down signal times out
838  * @param ptr timer instance that fired
839  */
840 static void
841 _cb_destination_timeout(struct oonf_timer_instance *ptr) {
842   struct dlep_local_neighbor *local;
843
844   local = container_of(ptr, struct dlep_local_neighbor, _ack_timeout);
845   if (local->session->cb_destination_timeout) {
846     local->session->cb_destination_timeout(local->session, local);
847   }
848 }
849
850 /**
851  * parse a stream of DLEP tlvs
852  * @param session dlep session
853  * @param buffer TLV buffer
854  * @param length buffer size
855  * @return DLEP parser status
856  */
857 static enum dlep_parser_error
858 _parse_tlvstream(struct dlep_session *session, const uint8_t *buffer, size_t length) {
859   struct dlep_session_parser *parser;
860   struct dlep_parser_tlv *tlv;
861   struct dlep_parser_value *value;
862   uint16_t tlv_type;
863   uint16_t tlv_length;
864   size_t tlv_count, idx;
865
866   parser = &session->parser;
867   parser->tlv_ptr = buffer;
868   tlv_count = 0;
869   idx = 0;
870
871   avl_for_each_element(&parser->allowed_tlvs, tlv, _node) {
872     tlv->tlv_first = -1;
873     tlv->tlv_last = -1;
874   }
875
876   while (idx < length) {
877     if (length - idx < 4) {
878       /* too short for a TLV, end parsing */
879       return DLEP_NEW_PARSER_INCOMPLETE_TLV_HEADER;
880     }
881
882     /* copy header */
883     memcpy(&tlv_type, &buffer[idx], sizeof(tlv_type));
884     idx += sizeof(tlv_type);
885     tlv_type = ntohs(tlv_type);
886
887     memcpy(&tlv_length, &buffer[idx], sizeof(tlv_length));
888     idx += sizeof(tlv_length);
889     tlv_length = ntohs(tlv_length);
890
891     if (idx + tlv_length > length) {
892       OONF_WARN(session->log_source,
893         "TLV %u incomplete: "
894         "%" PRINTF_SIZE_T_SPECIFIER " > %" PRINTF_SIZE_T_SPECIFIER,
895         tlv_type, idx + tlv_length, length);
896       return DLEP_NEW_PARSER_INCOMPLETE_TLV;
897     }
898
899     /* check if tlv is supported */
900     tlv = dlep_parser_get_tlv(parser, tlv_type);
901     if (!tlv) {
902       OONF_INFO(session->log_source, "Unsupported TLV %u", tlv_type);
903       return DLEP_NEW_PARSER_UNSUPPORTED_TLV;
904     }
905
906     /* check length */
907     if (tlv->length_max < tlv_length || tlv->length_min > tlv_length) {
908       OONF_WARN(session->log_source,
909         "TLV %u has wrong size,"
910         " %d is not between %u and %u",
911         tlv_type, tlv_length, tlv->length_min, tlv->length_max);
912       return DLEP_NEW_PARSER_ILLEGAL_TLV_LENGTH;
913     }
914
915     /* check if we need to allocate more space for value pointers */
916     if (parser->value_max_count == tlv_count) {
917       /* allocate more */
918       value = realloc(parser->values, sizeof(*value) * (tlv_count + SESSION_VALUE_STEP));
919       if (!value) {
920         return DLEP_NEW_PARSER_OUT_OF_MEMORY;
921       }
922       parser->value_max_count += SESSION_VALUE_STEP;
923       parser->values = value;
924     }
925
926     OONF_DEBUG_HEX(session->log_source, &buffer[idx], tlv_length, "Received TLV %u", tlv_type);
927
928     /* remember tlv value */
929     value = &parser->values[tlv_count];
930     value->tlv_next = -1;
931     value->index = idx;
932     value->length = tlv_length;
933
934     if (tlv->tlv_last == -1) {
935       /* first tlv */
936       tlv->tlv_first = tlv_count;
937     }
938     else {
939       /* one more */
940       value = &parser->values[tlv->tlv_last];
941       value->tlv_next = tlv_count;
942     }
943     tlv->tlv_last = tlv_count;
944     tlv_count++;
945
946     idx += tlv_length;
947   }
948
949   return DLEP_NEW_PARSER_OKAY;
950 }
951
952 /**
953  * Check if all mandatory TLVs were found
954  * @param session dlep session
955  * @param ext dlep extension
956  * @param signal_type dlep signal/message type
957  * @return dlep parser status
958  */
959 static enum dlep_parser_error
960 _check_mandatory(struct dlep_session *session, struct dlep_extension *ext, int32_t signal_type) {
961   struct dlep_session_parser *parser;
962   struct dlep_parser_tlv *tlv;
963   struct dlep_extension_signal *extsig;
964   size_t s, t;
965
966   parser = &session->parser;
967
968   extsig = NULL;
969   for (s = 0; s < ext->signal_count; s++) {
970     if (ext->signals[s].id == signal_type) {
971       extsig = &ext->signals[s];
972       break;
973     }
974   }
975
976   if (!extsig) {
977     return DLEP_NEW_PARSER_OKAY;
978   }
979
980   for (t = 0; t < extsig->mandatory_tlv_count; t++) {
981     tlv = dlep_parser_get_tlv(parser, extsig->mandatory_tlvs[t]);
982     if (!tlv) {
983       OONF_WARN(session->log_source,
984         "Could not find tlv data for"
985         " mandatory TLV %u in extension %d",
986         extsig->mandatory_tlvs[t], ext->id);
987       return DLEP_NEW_PARSER_INTERNAL_ERROR;
988     }
989
990     if (tlv->tlv_first == -1) {
991       OONF_WARN(session->log_source,
992         "Missing mandatory TLV"
993         " %u in extension %d",
994         extsig->mandatory_tlvs[t], ext->id);
995       return DLEP_NEW_PARSER_MISSING_MANDATORY_TLV;
996     }
997   }
998   return DLEP_NEW_PARSER_OKAY;
999 }
1000
1001 /**
1002  * Check if all duplicate TLVs were allowed to be duplicates
1003  * @param session dlep session
1004  * @param ext dlep extension
1005  * @param signal_type dlep signal/message type
1006  * @return dlep parser status
1007  */
1008 static enum dlep_parser_error
1009 _check_duplicate(struct dlep_session *session, struct dlep_extension *ext, int32_t signal_type) {
1010   struct dlep_session_parser *parser;
1011   struct dlep_parser_tlv *tlv;
1012   struct dlep_extension_signal *extsig;
1013   size_t s, t, dt;
1014   bool okay;
1015
1016   parser = &session->parser;
1017
1018   extsig = NULL;
1019   for (s = 0; s < ext->signal_count; s++) {
1020     extsig = &ext->signals[s];
1021     if (ext->signals[s].id == signal_type) {
1022       extsig = &ext->signals[s];
1023       break;
1024     }
1025   }
1026
1027   if (!extsig) {
1028     return DLEP_NEW_PARSER_OKAY;
1029   }
1030
1031   for (t = 0; t < extsig->supported_tlv_count; t++) {
1032     tlv = avl_find_element(&parser->allowed_tlvs, &extsig->supported_tlvs[t], tlv, _node);
1033     if (tlv == NULL || tlv->tlv_first == tlv->tlv_last) {
1034       continue;
1035     }
1036
1037     okay = false;
1038     for (dt = 0; dt < extsig->duplicate_tlv_count; dt++) {
1039       if (extsig->duplicate_tlvs[dt] == tlv->id) {
1040         okay = true;
1041         break;
1042       }
1043     }
1044     if (!okay) {
1045       OONF_WARN(session->log_source,
1046         "Duplicate not allowed"
1047         " for TLV %u in extension %d",
1048         tlv->id, ext->id);
1049       return DLEP_NEW_PARSER_DUPLICATE_TLV;
1050     }
1051   }
1052   return DLEP_NEW_PARSER_OKAY;
1053 }
1054
1055 /**
1056  * Call extension processing hooks for parsed signal/message
1057  * @param session dlep session
1058  * @param ext dlep_extension
1059  * @param signal_type dlep signal/message type
1060  * @return dlep parser status
1061  */
1062 static enum dlep_parser_error
1063 _call_extension_processing(struct dlep_session *session, struct dlep_extension *ext, int32_t signal_type) {
1064   size_t s;
1065
1066   for (s = 0; s < ext->signal_count; s++) {
1067     if (ext->signals[s].id != signal_type) {
1068       continue;
1069     }
1070
1071     if (session->radio) {
1072       if (ext->signals[s].process_radio(ext, session)) {
1073         OONF_DEBUG(session->log_source, "Error in radio signal processing of extension '%s'", ext->name);
1074         return -1;
1075       }
1076     }
1077     else {
1078       if (ext->signals[s].process_router(ext, session)) {
1079         OONF_DEBUG(session->log_source, "Error in router signal processing of extension '%s'", ext->name);
1080         return -1;
1081       }
1082     }
1083     break;
1084   }
1085   return DLEP_NEW_PARSER_OKAY;
1086 }
1087
1088 /**
1089  * Add a TLV to the allowed TLVs of a DLEP session
1090  * @param parser dlep session parser
1091  * @param id DLEP TLV id
1092  * @return dlep parser TLV, NULL if out of memory
1093  */
1094 static struct dlep_parser_tlv *
1095 _add_session_tlv(struct dlep_session_parser *parser, uint16_t id) {
1096   struct dlep_parser_tlv *tlv;
1097
1098   tlv = oonf_class_malloc(&_tlv_class);
1099   if (!tlv) {
1100     return NULL;
1101   }
1102
1103   tlv->id = id;
1104   tlv->_node.key = &tlv->id;
1105   tlv->tlv_first = -1;
1106   tlv->tlv_last = -1;
1107
1108   avl_insert(&parser->allowed_tlvs, &tlv->_node);
1109   return tlv;
1110 }