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