DLEP SESSION UPDATE ACK has mandatory STATUS TLV
[oonf.git] / src-plugins / generic / dlep / ext_base_proto / proto_radio.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/autobuf.h"
47 #include "common/avl.h"
48 #include "common/common_types.h"
49 #include "core/oonf_logging.h"
50
51 #include "subsystems/oonf_class.h"
52 #include "subsystems/oonf_layer2.h"
53
54 #include "dlep/dlep_extension.h"
55 #include "dlep/dlep_iana.h"
56 #include "dlep/dlep_reader.h"
57 #include "dlep/dlep_session.h"
58 #include "dlep/dlep_writer.h"
59 #include "dlep/radio/dlep_radio_interface.h"
60 #include "dlep/radio/dlep_radio_session.h"
61
62 #include "dlep/ext_base_proto/proto.h"
63 #include "dlep/ext_base_proto/proto_radio.h"
64
65 static void _cb_init_radio(struct dlep_session *);
66 static void _cb_cleanup_radio(struct dlep_session *);
67
68 static int _radio_process_peer_discovery(struct dlep_extension *, struct dlep_session *);
69 static int _radio_process_session_init(struct dlep_extension *, struct dlep_session *);
70 static int _radio_process_session_update(struct dlep_extension *, struct dlep_session *);
71 static int _radio_process_session_update_ack(struct dlep_extension *, struct dlep_session *);
72 static int _radio_process_destination_up_ack(struct dlep_extension *, struct dlep_session *);
73 static int _radio_process_destination_down_ack(struct dlep_extension *, struct dlep_session *);
74 static int _radio_process_link_char_request(struct dlep_extension *, struct dlep_session *);
75
76 static int _radio_write_peer_offer(struct dlep_extension *, struct dlep_session *session, const struct netaddr *);
77 static int _radio_write_session_init_ack(struct dlep_extension *, struct dlep_session *session, const struct netaddr *);
78
79 static void _l2_neigh_added_to_session(
80   struct dlep_session *session, struct oonf_layer2_neigh *l2neigh, const struct netaddr *mac);
81 static void _l2_neigh_added(
82   struct oonf_layer2_neigh *l2neigh, struct oonf_layer2_destination *l2dest, const struct netaddr *mac);
83
84 static void _cb_l2_net_changed(void *);
85
86 static void _cb_l2_neigh_added(void *);
87 static void _cb_l2_neigh_changed(void *);
88 static void _cb_l2_neigh_removed(void *);
89
90 static void _cb_l2_dst_added(void *);
91 static void _cb_l2_dst_removed(void *);
92
93 static void _cb_destination_timeout(struct dlep_session *, struct dlep_local_neighbor *);
94
95 static struct dlep_extension_implementation _radio_signals[] = {
96   { .id = DLEP_UDP_PEER_DISCOVERY, .process = _radio_process_peer_discovery },
97   {
98     .id = DLEP_UDP_PEER_OFFER,
99     .add_tlvs = _radio_write_peer_offer,
100   },
101   {
102     .id = DLEP_SESSION_INITIALIZATION,
103     .process = _radio_process_session_init,
104   },
105   {
106     .id = DLEP_SESSION_INITIALIZATION_ACK,
107     .add_tlvs = _radio_write_session_init_ack,
108   },
109   {
110     .id = DLEP_SESSION_UPDATE,
111     .process = _radio_process_session_update,
112   },
113   {
114     .id = DLEP_SESSION_UPDATE_ACK,
115     .process = _radio_process_session_update_ack,
116   },
117   {
118     .id = DLEP_SESSION_TERMINATION,
119     .process = dlep_base_proto_process_session_termination,
120   },
121   {
122     .id = DLEP_SESSION_TERMINATION_ACK,
123     .process = dlep_base_proto_process_session_termination_ack,
124   },
125   {
126     .id = DLEP_DESTINATION_UP,
127     .add_tlvs = dlep_base_proto_write_mac_only,
128   },
129   {
130     .id = DLEP_DESTINATION_UP_ACK,
131     .process = _radio_process_destination_up_ack,
132   },
133   {
134     .id = DLEP_DESTINATION_DOWN,
135     .add_tlvs = dlep_base_proto_write_mac_only,
136   },
137   {
138     .id = DLEP_DESTINATION_DOWN_ACK,
139     .process = _radio_process_destination_down_ack,
140   },
141   {
142     .id = DLEP_DESTINATION_UPDATE,
143     .add_tlvs = dlep_base_proto_write_mac_only,
144   },
145   {
146     .id = DLEP_HEARTBEAT,
147     .process = dlep_base_proto_process_heartbeat,
148   },
149   {
150     .id = DLEP_LINK_CHARACTERISTICS_REQUEST,
151     .process = _radio_process_link_char_request,
152   },
153 };
154
155 static struct oonf_class_extension _layer2_net_listener = {
156   .ext_name = "dlep radio",
157   .class_name = LAYER2_CLASS_NETWORK,
158
159   .cb_change = _cb_l2_net_changed,
160 };
161
162 static struct oonf_class_extension _layer2_neigh_listener = {
163   .ext_name = "dlep radio",
164   .class_name = LAYER2_CLASS_NEIGHBOR,
165
166   .cb_add = _cb_l2_neigh_added,
167   .cb_change = _cb_l2_neigh_changed,
168   .cb_remove = _cb_l2_neigh_removed,
169 };
170
171 static struct oonf_class_extension _layer2_dst_listener = {
172   .ext_name = "dlep radio",
173   .class_name = LAYER2_CLASS_DESTINATION,
174
175   .cb_add = _cb_l2_dst_added,
176   .cb_remove = _cb_l2_dst_removed,
177 };
178
179 static struct dlep_extension *_base;
180
181 /**
182  * Initialize the radios DLEP base protocol extension
183  */
184 void
185 dlep_base_proto_radio_init(void) {
186   _base = dlep_base_proto_init();
187   dlep_extension_add_processing(_base, true, _radio_signals, ARRAYSIZE(_radio_signals));
188
189   oonf_class_extension_add(&_layer2_net_listener);
190   oonf_class_extension_add(&_layer2_neigh_listener);
191   oonf_class_extension_add(&_layer2_dst_listener);
192
193   _base->cb_session_init_radio = _cb_init_radio;
194   _base->cb_session_cleanup_radio = _cb_cleanup_radio;
195 }
196
197 /**
198  * Callback to initialize the radio session
199  * @param session dlep session
200  */
201 static void
202 _cb_init_radio(struct dlep_session *session) {
203   if (session->restrict_signal == DLEP_SESSION_INITIALIZATION) {
204     /*
205      * we are waiting for a Peer Init,
206      */
207     session->remote_heartbeat_interval = session->cfg.heartbeat_interval;
208     dlep_base_proto_start_remote_heartbeat(session);
209   }
210
211   session->cb_destination_timeout = _cb_destination_timeout;
212 }
213
214 /**
215  * Callback to cleanup the radio session
216  * @param session dlep session
217  */
218 static void
219 _cb_cleanup_radio(struct dlep_session *session) {
220   dlep_base_proto_stop_timers(session);
221
222   oonf_class_extension_remove(&_layer2_net_listener);
223   oonf_class_extension_remove(&_layer2_neigh_listener);
224   oonf_class_extension_remove(&_layer2_dst_listener);
225 }
226
227 /**
228  * Process the peer discovery signal
229  * @param ext (this) dlep extension
230  * @param session dlep session
231  * @return -1 if an error happened, 0 otherwise
232  */
233 static int
234 _radio_process_peer_discovery(struct dlep_extension *ext __attribute__((unused)), struct dlep_session *session) {
235   if (session->restrict_signal != DLEP_UDP_PEER_DISCOVERY) {
236     /* ignore unless we are in discovery mode */
237     return 0;
238   }
239   return dlep_session_generate_signal(session, DLEP_UDP_PEER_OFFER, NULL);
240 }
241
242 /**
243  * Process the peer initialization message
244  * @param ext (this) dlep extension
245  * @param session dlep session
246  * @return -1 if an error happened, 0 otherwise
247  */
248 static int
249 _radio_process_session_init(struct dlep_extension *ext __attribute__((unused)), struct dlep_session *session) {
250   struct oonf_layer2_net *l2net;
251   struct oonf_layer2_neigh *l2neigh;
252   struct oonf_layer2_destination *l2dest;
253   struct dlep_parser_value *value;
254   const uint8_t *ptr;
255
256 #ifdef OONF_LOG_DEBUG_INFO
257   struct netaddr_str nbuf;
258 #endif
259
260   if (session->restrict_signal != DLEP_SESSION_INITIALIZATION) {
261     /* ignore unless we are in initialization mode */
262     return 0;
263   }
264
265   /* mandatory heartbeat tlv */
266   if (dlep_reader_heartbeat_tlv(&session->remote_heartbeat_interval, session, NULL)) {
267     OONF_INFO(session->log_source, "no heartbeat tlv");
268     return -1;
269   }
270
271   OONF_DEBUG(session->log_source, "Remote heartbeat interval %" PRIu64, session->remote_heartbeat_interval);
272
273   dlep_base_proto_start_local_heartbeat(session);
274   dlep_base_proto_start_remote_heartbeat(session);
275
276   /* optional peer type tlv */
277   dlep_base_proto_print_peer_type(session);
278
279   /* optional extension supported tlv */
280   value = dlep_session_get_tlv_value(session, DLEP_EXTENSIONS_SUPPORTED_TLV);
281   if (value) {
282     ptr = dlep_session_get_tlv_binary(session, value);
283     if (dlep_session_update_extensions(session, ptr, value->length / 2)) {
284       return -1;
285     }
286   }
287   else if (dlep_session_update_extensions(session, NULL, 0)) {
288     return -1;
289   }
290
291   if (dlep_session_generate_signal(session, DLEP_SESSION_INITIALIZATION_ACK, NULL)) {
292     return -1;
293   }
294
295   /* trigger DESTINATION UP for all existing elements in l2 db */
296   l2net = oonf_layer2_net_get(session->l2_listener.name);
297   if (l2net) {
298     avl_for_each_element(&l2net->neighbors, l2neigh, _node) {
299       if (session->cfg.send_neighbors) {
300         OONF_DEBUG(session->log_source, "Add local neighbor: %s", netaddr_to_string(&nbuf, &l2neigh->addr));
301         _l2_neigh_added_to_session(session, l2neigh, &l2neigh->addr);
302       }
303
304       if (session->cfg.send_proxied) {
305         avl_for_each_element(&l2neigh->destinations, l2dest, _node) {
306           OONF_DEBUG(session->log_source, "Add proxied neighbor: %s", netaddr_to_string(&nbuf, &l2dest->destination));
307           _l2_neigh_added_to_session(session, l2neigh, &l2dest->destination);
308         }
309       }
310     }
311   }
312   session->next_restrict_signal = DLEP_ALL_SIGNALS;
313   session->_peer_state = DLEP_PEER_IDLE;
314   return 0;
315 }
316
317 /**
318  * Process the peer update message
319  * @param ext (this) dlep extension
320  * @param session dlep session
321  * @return -1 if an error happened, 0 otherwise
322  */
323 static int
324 _radio_process_session_update(struct dlep_extension *ext __attribute__((unused)), struct dlep_session *session) {
325   /* we don't support IP address exchange with the router at the moment */
326   return dlep_session_generate_signal_status(session, DLEP_SESSION_UPDATE_ACK, NULL, DLEP_STATUS_OKAY, "Success");
327 }
328
329 /**
330  * Process the peer update ack message
331  * @param ext (this) dlep extension
332  * @param session dlep session
333  * @return always 0
334  */
335 static int
336 _radio_process_session_update_ack(struct dlep_extension *ext __attribute__((unused)), struct dlep_session *session) {
337   dlep_base_proto_print_status(session);
338   if (session->_peer_state == DLEP_PEER_SEND_UPDATE) {
339     if (dlep_session_generate_signal(session, DLEP_SESSION_UPDATE, NULL)) {
340       // TODO: do we need to terminate here?
341       return -1;
342     }
343     session->_peer_state = DLEP_PEER_WAIT_FOR_UPDATE_ACK;
344   }
345   else {
346     session->_peer_state = DLEP_PEER_IDLE;
347   }
348   return 0;
349 }
350
351 /**
352  * Process the destination up ack message
353  * @param ext (this) dlep extension
354  * @param session dlep session
355  * @return -1 if an error happened, 0 otherwise
356  */
357 static int
358 _radio_process_destination_up_ack(struct dlep_extension *ext __attribute__((unused)), struct dlep_session *session) {
359   struct dlep_local_neighbor *local;
360   struct netaddr mac;
361   if (dlep_reader_mac_tlv(&mac, session, NULL)) {
362     return -1;
363   }
364
365   if (dlep_base_proto_print_status(session) == DLEP_STATUS_OKAY) {
366     local = dlep_session_get_local_neighbor(session, &mac);
367     if (local->state == DLEP_NEIGHBOR_UP_SENT) {
368       local->state = DLEP_NEIGHBOR_UP_ACKED;
369       oonf_timer_stop(&local->_ack_timeout);
370
371       if (local->changed) {
372         dlep_session_generate_signal(session, DLEP_DESTINATION_UPDATE, &mac);
373         local->changed = false;
374       }
375     }
376   }
377   return 0;
378 }
379
380 /**
381  * Process the destination down ack message
382  * @param ext (this) dlep extension
383  * @param session dlep session
384  * @return -1 if an error happened, 0 otherwise
385  */
386 static int
387 _radio_process_destination_down_ack(struct dlep_extension *ext __attribute__((unused)), struct dlep_session *session) {
388   struct dlep_local_neighbor *local;
389   struct netaddr mac;
390   if (dlep_reader_mac_tlv(&mac, session, NULL)) {
391     OONF_INFO(session->log_source, "Could not read MAC tlv");
392     return -1;
393   }
394
395   if (dlep_base_proto_print_status(session) == DLEP_STATUS_OKAY) {
396     local = dlep_session_get_local_neighbor(session, &mac);
397     if (local->state == DLEP_NEIGHBOR_DOWN_SENT) {
398       dlep_session_remove_local_neighbor(session, local);
399     }
400   }
401   return 0;
402 }
403
404 /**
405  * Process the link characteristic message
406  * @param ext (this) dlep extension
407  * @param session dlep session
408  * @return -1 if an error happened, 0 otherwise
409  */
410 static int
411 _radio_process_link_char_request(
412   struct dlep_extension *ext __attribute__((unused)), struct dlep_session *session __attribute__((unused))) {
413   /* TODO: Link characteristic processing ? */
414   return 0;
415 }
416
417 /**
418  * Generate a peer offer signal
419  * @param ext (this) dlep extension
420  * @param session dlep session
421  * @param addr mac address the message should refer to
422  * @return -1 if an error happened, 0 otherwise
423  */
424 static int
425 _radio_write_peer_offer(struct dlep_extension *ext __attribute__((unused)), struct dlep_session *session,
426   const struct netaddr *addr __attribute__((unused))) {
427   struct dlep_radio_if *radio_if;
428   struct netaddr local_addr;
429 #ifdef OONF_LOG_DEBUG_INFO
430   struct netaddr_str nbuf;
431 #endif
432
433   radio_if = dlep_radio_get_by_layer2_if(session->l2_listener.data->name);
434   if (!radio_if || &radio_if->interf.session != session) {
435     /* unknown type of session, ignore */
436     return 0;
437   }
438
439   OONF_DEBUG(session->log_source, "Local IPv4 socket: %s",
440     netaddr_socket_to_string(&nbuf, &radio_if->tcp.socket_v4.local_socket));
441   netaddr_from_socket(&local_addr, &radio_if->tcp.socket_v4.local_socket);
442   if (netaddr_get_address_family(&local_addr) == AF_INET) {
443     /* no support for TLS at the moment */
444     dlep_writer_add_ipv4_conpoint_tlv(&session->writer, &local_addr, radio_if->tcp_config.port, false);
445   }
446
447   OONF_DEBUG(session->log_source, "Local IPv6 socket: %s",
448     netaddr_socket_to_string(&nbuf, &radio_if->tcp.socket_v6.local_socket));
449   netaddr_from_socket(&local_addr, &radio_if->tcp.socket_v6.local_socket);
450   if (netaddr_get_address_family(&local_addr) == AF_INET6) {
451     /* no support for TLS at the moment */
452     dlep_writer_add_ipv6_conpoint_tlv(&session->writer, &local_addr, radio_if->tcp_config.port, false);
453   }
454   return 0;
455 }
456
457 /**
458  * Generate a peer init ack signal
459  * @param ext (this) dlep extension
460  * @param session dlep session
461  * @param addr mac address the message should refer to
462  * @return -1 if an error happened, 0 otherwise
463  */
464 static int
465 _radio_write_session_init_ack(struct dlep_extension *ext __attribute__((unused)), struct dlep_session *session,
466   const struct netaddr *addr __attribute__((unused))) {
467   const uint16_t *ext_ids;
468   uint16_t ext_count;
469
470   /* write heartbeat interval */
471   dlep_writer_add_heartbeat_tlv(&session->writer, session->cfg.heartbeat_interval);
472
473   /* write supported extensions */
474   ext_ids = dlep_extension_get_ids(&ext_count);
475   if (ext_count) {
476     dlep_writer_add_supported_extensions(&session->writer, ext_ids, ext_count);
477   }
478
479   /* TODO: check what router will report as flags */
480   dlep_writer_add_peer_type_tlv(&session->writer, session->cfg.peer_type, false);
481
482   dlep_writer_add_status(&session->writer, DLEP_STATUS_OKAY, "");
483
484   return 0;
485 }
486
487 /**
488  * Helper function to add a layer2 neighbor to a dlep session
489  * @param session dlep session
490  * @param l2neigh layer2 neighbor
491  * @param mac MAC address of other endpoint
492  */
493 static void
494 _l2_neigh_added_to_session(struct dlep_session *session, struct oonf_layer2_neigh *l2neigh, const struct netaddr *mac) {
495   struct dlep_local_neighbor *local;
496
497   local = dlep_session_add_local_neighbor(session, mac);
498
499   if (local) {
500     memcpy(&local->neigh_addr, &l2neigh->addr, sizeof(local->neigh_addr));
501
502     dlep_session_generate_signal(session, DLEP_DESTINATION_UP, mac);
503     local->state = DLEP_NEIGHBOR_UP_SENT;
504     oonf_timer_set(&local->_ack_timeout, session->cfg.heartbeat_interval * 2);
505   }
506 }
507
508 /**
509  * Helper function triggered for a new layer2 neighbor
510  * @param l2neigh layer2 neighbor
511  * @param l2dest layer2 destination (might be NULL)
512  * @param mac MAC address of other endpoint
513  */
514 static void
515 _l2_neigh_added(struct oonf_layer2_neigh *l2neigh, struct oonf_layer2_destination *l2dest, const struct netaddr *mac) {
516   struct dlep_radio_if *radio_if;
517   struct dlep_radio_session *radio_session;
518
519   radio_if = dlep_radio_get_by_layer2_if(l2neigh->network->name);
520   if (!radio_if) {
521     return;
522   }
523
524   avl_for_each_element(&radio_if->interf.session_tree, radio_session, _node) {
525     if (l2dest && !radio_session->session.cfg.send_proxied) {
526       continue;
527     }
528     if (!l2dest && !radio_session->session.cfg.send_neighbors) {
529       continue;
530     }
531     _l2_neigh_added_to_session(&radio_if->interf.session, l2neigh, mac);
532   }
533 }
534
535 /**
536  * Helper function triggered when a layer2 neighbor changed
537  * @param l2neigh layer2 neighbor
538  * @param l2dest layer2 destination (might be NULL)
539  * @param mac MAC address of other endpoint
540  */
541 static void
542 _l2_neigh_changed(
543   struct oonf_layer2_neigh *l2neigh, struct oonf_layer2_destination *l2dest, const struct netaddr *mac) {
544   struct dlep_radio_if *radio_if;
545   struct dlep_radio_session *radio_session;
546   struct dlep_local_neighbor *local;
547
548   radio_if = dlep_radio_get_by_layer2_if(l2neigh->network->name);
549   if (!radio_if) {
550     return;
551   }
552
553   avl_for_each_element(&radio_if->interf.session_tree, radio_session, _node) {
554     if (l2dest && !radio_session->session.cfg.send_proxied) {
555       continue;
556     }
557     if (!l2dest && !radio_session->session.cfg.send_neighbors) {
558       continue;
559     }
560
561     local = dlep_session_add_local_neighbor(&radio_session->session, mac);
562
563     if (local) {
564       memcpy(&local->neigh_addr, &l2neigh->addr, sizeof(local->neigh_addr));
565
566       switch (local->state) {
567         case DLEP_NEIGHBOR_UP_SENT:
568           local->changed = true;
569           break;
570         case DLEP_NEIGHBOR_UP_ACKED:
571           dlep_session_generate_signal(&radio_session->session, DLEP_DESTINATION_UPDATE, mac);
572           local->changed = false;
573           break;
574         case DLEP_NEIGHBOR_IDLE:
575         case DLEP_NEIGHBOR_DOWN_SENT:
576         case DLEP_NEIGHBOR_DOWN_ACKED:
577           dlep_session_generate_signal(&radio_session->session, DLEP_DESTINATION_UP, mac);
578           local->state = DLEP_NEIGHBOR_UP_SENT;
579           local->changed = false;
580           oonf_timer_set(&local->_ack_timeout, radio_session->session.cfg.heartbeat_interval * 2);
581           break;
582         default:
583           break;
584       }
585     }
586   }
587 }
588
589 /**
590  * Helper function triggered when a layer2 neighbor is removed
591  * @param l2neigh layer2 neighbor
592  * @param l2dest layer2 destination (might be NULL)
593  * @param mac MAC address of other endpoint
594  */
595 static void
596 _l2_neigh_removed(
597   struct oonf_layer2_neigh *l2neigh, struct oonf_layer2_destination *l2dest, const struct netaddr *mac) {
598   struct dlep_radio_if *radio_if;
599   struct dlep_radio_session *radio_session;
600   struct dlep_local_neighbor *local;
601
602   radio_if = dlep_radio_get_by_layer2_if(l2neigh->network->name);
603   if (!radio_if) {
604     return;
605   }
606
607   avl_for_each_element(&radio_if->interf.session_tree, radio_session, _node) {
608     if (l2dest && !radio_session->session.cfg.send_proxied) {
609       continue;
610     }
611     if (!l2dest && !radio_session->session.cfg.send_neighbors) {
612       continue;
613     }
614
615     local = dlep_session_get_local_neighbor(&radio_session->session, mac);
616     if (!local) {
617       continue;
618     }
619
620     if ((l2dest && netaddr_cmp(&l2neigh->addr, &local->neigh_addr) == 0) ||
621         (!l2dest && netaddr_is_unspec(&local->neigh_addr))) {
622       dlep_session_generate_signal(&radio_session->session, DLEP_DESTINATION_DOWN, mac);
623       local->state = DLEP_NEIGHBOR_DOWN_SENT;
624       oonf_timer_set(&local->_ack_timeout, radio_session->session.cfg.heartbeat_interval * 2);
625     }
626   }
627 }
628
629 static void
630 _cb_l2_net_changed(void *ptr) {
631   struct dlep_radio_session *radio_session;
632   struct dlep_radio_if *radio_if;
633   struct oonf_layer2_net *l2net;
634
635   l2net = ptr;
636
637   radio_if = dlep_radio_get_by_layer2_if(l2net->name);
638   if (!radio_if) {
639     return;
640   }
641
642   avl_for_each_element(&radio_if->interf.session_tree, radio_session, _node) {
643     if (radio_session->session.restrict_signal == DLEP_ALL_SIGNALS) {
644       dlep_session_generate_signal(&radio_session->session, DLEP_SESSION_UPDATE, NULL);
645     }
646   }
647 }
648
649 /**
650  * Callback triggered when a layer2 neighbor object has been added
651  * @param ptr layer2 neighbor
652  */
653 static void
654 _cb_l2_neigh_added(void *ptr) {
655   struct oonf_layer2_neigh *l2neigh = ptr;
656
657   _l2_neigh_added(l2neigh, NULL, &l2neigh->addr);
658 }
659
660 /**
661  * Callback triggered when a layer2 neighbor object has been changed
662  * @param ptr layer2 neighbor
663  */
664 static void
665 _cb_l2_neigh_changed(void *ptr) {
666   struct oonf_layer2_neigh *l2neigh;
667   struct oonf_layer2_destination *l2dst;
668
669   l2neigh = ptr;
670   _l2_neigh_changed(l2neigh, NULL, &l2neigh->addr);
671
672   avl_for_each_element(&l2neigh->destinations, l2dst, _node) {
673     _l2_neigh_changed(l2neigh, l2dst, &l2dst->destination);
674   }
675 }
676
677 /**
678  * Callback triggered when a layer2 neighbor object has been removed
679  * @param ptr layer2 neighbor
680  */
681 static void
682 _cb_l2_neigh_removed(void *ptr) {
683   struct oonf_layer2_neigh *l2neigh = ptr;
684
685   _l2_neigh_removed(l2neigh, NULL, &l2neigh->addr);
686 }
687
688 /**
689  * Callback triggered when a layer2 destination object has been added
690  * @param ptr layer2 destination
691  */
692 static void
693 _cb_l2_dst_added(void *ptr) {
694   struct oonf_layer2_destination *l2dst = ptr;
695
696   _l2_neigh_added(l2dst->neighbor, NULL, &l2dst->destination);
697 }
698
699 /**
700  * Callback triggered when a layer2 destination object has been removed
701  * @param ptr layer2 destination
702  */
703 static void
704 _cb_l2_dst_removed(void *ptr) {
705   struct oonf_layer2_destination *l2dst = ptr;
706
707   _l2_neigh_removed(l2dst->neighbor, NULL, &l2dst->destination);
708 }
709
710 /**
711  * Callback triggered when a destination up/down ack times out
712  * @param session dlep session
713  * @param local local DLEP neighbor
714  */
715 static void
716 _cb_destination_timeout(struct dlep_session *session, struct dlep_local_neighbor *local) {
717   dlep_session_remove_local_neighbor(session, local);
718 }