0ebb43278dfc4c935152dda9e949e82459998fa8
[oonf.git] / src-plugins / generic / dlep / ext_base_proto / proto_router.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 "dlep/dlep_extension.h"
52 #include "dlep/dlep_iana.h"
53 #include "dlep/dlep_reader.h"
54 #include "dlep/dlep_writer.h"
55 #include "dlep/router/dlep_router_interface.h"
56 #include "dlep/router/dlep_router_session.h"
57
58 #include "dlep/ext_base_proto/proto.h"
59 #include "dlep/ext_base_proto/proto_router.h"
60
61 static void _cb_init_router(struct dlep_session *);
62 static void _cb_apply_router(struct dlep_session *);
63 static void _cb_cleanup_router(struct dlep_session *);
64 static void _cb_create_peer_discovery(struct oonf_timer_instance *);
65
66 static int _router_process_peer_offer(struct dlep_extension *, struct dlep_session *);
67 static int _router_process_session_init_ack(struct dlep_extension *, struct dlep_session *);
68 static int _router_process_session_update(struct dlep_extension *, struct dlep_session *);
69 static int _router_process_session_update_ack(struct dlep_extension *, struct dlep_session *);
70 static int _router_process_destination_up(struct dlep_extension *, struct dlep_session *);
71 static int _router_process_destination_up_ack(struct dlep_extension *, struct dlep_session *);
72 static int _router_process_destination_down(struct dlep_extension *, struct dlep_session *);
73 static int _router_process_destination_down_ack(struct dlep_extension *, struct dlep_session *);
74 static int _router_process_destination_update(struct dlep_extension *, struct dlep_session *);
75 static int _router_process_link_char_ack(struct dlep_extension *, struct dlep_session *);
76
77 static int _router_write_peer_discovery(struct dlep_extension *, struct dlep_session *session, const struct netaddr *);
78 static int _router_write_session_init(struct dlep_extension *, struct dlep_session *session, const struct netaddr *);
79
80 static struct dlep_extension_implementation _router_signals[] = {
81   {
82     .id = DLEP_UDP_PEER_DISCOVERY,
83     .add_tlvs = _router_write_peer_discovery,
84   },
85   {
86     .id = DLEP_UDP_PEER_OFFER,
87     .process = _router_process_peer_offer,
88   },
89   {
90     .id = DLEP_SESSION_INITIALIZATION,
91     .add_tlvs = _router_write_session_init,
92   },
93   {
94     .id = DLEP_SESSION_INITIALIZATION_ACK,
95     .process = _router_process_session_init_ack,
96   },
97   {
98     .id = DLEP_SESSION_UPDATE,
99     .process = _router_process_session_update,
100   },
101   {
102     .id = DLEP_SESSION_UPDATE_ACK,
103     .process = _router_process_session_update_ack,
104   },
105   {
106     .id = DLEP_SESSION_TERMINATION,
107     .process = dlep_base_proto_process_session_termination,
108   },
109   {
110     .id = DLEP_SESSION_TERMINATION_ACK,
111     .process = dlep_base_proto_process_session_termination_ack,
112   },
113   {
114     .id = DLEP_DESTINATION_UP,
115     .process = _router_process_destination_up,
116   },
117   {
118     .id = DLEP_DESTINATION_UP_ACK,
119     .process = _router_process_destination_up_ack,
120     .add_tlvs = dlep_base_proto_write_mac_only,
121   },
122   {
123     .id = DLEP_DESTINATION_DOWN,
124     .process = _router_process_destination_down,
125   },
126   {
127     .id = DLEP_DESTINATION_DOWN_ACK,
128     .process = _router_process_destination_down_ack,
129     .add_tlvs = dlep_base_proto_write_mac_only,
130   },
131   {
132     .id = DLEP_DESTINATION_UPDATE,
133     .process = _router_process_destination_update,
134   },
135   {
136     .id = DLEP_HEARTBEAT,
137     .process = dlep_base_proto_process_heartbeat,
138   },
139   {
140     .id = DLEP_LINK_CHARACTERISTICS_ACK,
141     .process = _router_process_link_char_ack,
142   },
143 };
144
145 static struct oonf_timer_class _peer_discovery_class = {
146   .name = "dlep peer discovery",
147   .callback = _cb_create_peer_discovery,
148   .periodic = true,
149 };
150 static struct dlep_extension *_base;
151
152 /**
153  * Initialize the routers DLEP base protocol extension
154  */
155 void
156 dlep_base_proto_router_init(void) {
157   _base = dlep_base_proto_init();
158   dlep_extension_add_processing(_base, false, _router_signals, ARRAYSIZE(_router_signals));
159
160   oonf_timer_add(&_peer_discovery_class);
161
162   _base->cb_session_init_router = _cb_init_router;
163   _base->cb_session_apply_router = _cb_apply_router;
164   _base->cb_session_cleanup_router = _cb_cleanup_router;
165 }
166
167 /**
168  * Callback to initialize the router session
169  * @param session dlep session
170  */
171 static void
172 _cb_init_router(struct dlep_session *session) {
173   if (session->restrict_signal == DLEP_SESSION_INITIALIZATION_ACK) {
174     /*
175      * we are waiting for a Peer Init Ack,
176      * so we need to send a Peer Init
177      */
178     dlep_session_generate_signal(session, DLEP_SESSION_INITIALIZATION, NULL);
179     session->cb_send_buffer(session, 0);
180
181     session->remote_heartbeat_interval = session->cfg.heartbeat_interval;
182     dlep_base_proto_start_remote_heartbeat(session);
183   }
184 }
185
186 /**
187  * Callback to apply new network settings to a router session
188  * @param session dlep session
189  */
190 static void
191 _cb_apply_router(struct dlep_session *session) {
192   OONF_DEBUG(session->log_source, "Initialize base router session");
193   if (session->restrict_signal == DLEP_UDP_PEER_OFFER) {
194     /*
195      * we are waiting for a Peer Offer,
196      * so we need to send Peer Discovery messages
197      */
198     session->local_event_timer.class = &_peer_discovery_class;
199
200     OONF_DEBUG(session->log_source, "Activate discovery with interval %" PRIu64, session->cfg.discovery_interval);
201
202     /* use the "local event" for the discovery timer */
203     oonf_timer_set(&session->local_event_timer, session->cfg.discovery_interval);
204   }
205 }
206
207 /**
208  * Callback to cleanup the router session
209  * @param session dlep session
210  */
211 static void
212 _cb_cleanup_router(struct dlep_session *session) {
213   struct oonf_layer2_net *l2net;
214
215   l2net = oonf_layer2_net_get(session->l2_listener.name);
216   if (l2net) {
217     /* remove DLEP mark from interface */
218     l2net->if_type = OONF_LAYER2_TYPE_UNDEFINED;
219     l2net->if_dlep = false;
220
221     /* and remove all DLEP data */
222     oonf_layer2_net_remove(l2net, session->l2_origin);
223   }
224
225   dlep_base_proto_stop_timers(session);
226 }
227
228 /**
229  * Callback to generate regular peer discovery signals
230  * @param ptr timer instance that fired
231  */
232 static void
233 _cb_create_peer_discovery(struct oonf_timer_instance *ptr) {
234   struct dlep_session *session;
235
236   session = container_of(ptr, struct dlep_session, local_event_timer);
237
238   OONF_DEBUG(session->log_source, "Generate peer discovery");
239
240   dlep_session_generate_signal(session, DLEP_UDP_PEER_DISCOVERY, NULL);
241   session->cb_send_buffer(session, AF_INET);
242
243   dlep_session_generate_signal(session, DLEP_UDP_PEER_DISCOVERY, NULL);
244   session->cb_send_buffer(session, AF_INET6);
245 }
246
247 /**
248  * Process the peer offer signal
249  * @param ext (this) dlep extension
250  * @param session dlep session
251  * @return -1 if an error happened, 0 otherwise
252  */
253 static int
254 _router_process_peer_offer(struct dlep_extension *ext __attribute__((unused)), struct dlep_session *session) {
255   struct dlep_router_if *router_if;
256   union netaddr_socket local, remote;
257   struct dlep_parser_value *value;
258   const struct os_interface_ip *ip;
259   const struct netaddr *result = NULL;
260   struct netaddr addr;
261   uint16_t port;
262   bool tls;
263   struct os_interface *ifdata;
264
265   if (session->restrict_signal != DLEP_UDP_PEER_OFFER) {
266     /* ignore unless we are in discovery mode */
267     return 0;
268   }
269
270   /* optional peer type tlv */
271   dlep_base_proto_print_peer_type(session);
272
273   /* we are looking for a good address to respond to */
274   result = NULL;
275
276   /* remember interface data */
277   ifdata = session->l2_listener.data;
278
279   /* IPv6 offer */
280   value = dlep_session_get_tlv_value(session, DLEP_IPV6_CONPOINT_TLV);
281   while (value) {
282     if (dlep_reader_ipv6_conpoint_tlv(&addr, &port, &tls, session, value)) {
283       return -1;
284     }
285
286     if (tls) {
287       /* TLS not supported at the moment */
288     }
289     else if (netaddr_is_in_subnet(&NETADDR_IPV6_LINKLOCAL, &addr) || result == NULL) {
290       ip = os_interface_get_prefix_from_dst(&addr, ifdata);
291       if (ip) {
292         result = &ip->address;
293         netaddr_socket_init(&remote, &addr, port, ifdata->index);
294       }
295     }
296     value = dlep_session_get_next_tlv_value(session, value);
297   }
298
299   /* IPv4 offer */
300   value = dlep_session_get_tlv_value(session, DLEP_IPV4_CONPOINT_TLV);
301   while (value && !result) {
302     if (dlep_reader_ipv4_conpoint_tlv(&addr, &port, &tls, session, value)) {
303       return -1;
304     }
305
306     if (tls) {
307       /* TLS not supported at the moment */
308     }
309     else {
310       ip = os_interface_get_prefix_from_dst(&addr, ifdata);
311       if (ip) {
312         result = &ip->address;
313         netaddr_socket_init(&remote, &addr, port, ifdata->index);
314       }
315     }
316     value = dlep_session_get_next_tlv_value(session, value);
317   }
318
319   /* remote address of incoming session */
320   if (!result) {
321     netaddr_from_socket(&addr, &session->remote_socket);
322     ip = os_interface_get_prefix_from_dst(&addr, ifdata);
323     if (!ip) {
324       /* no possible way to communicate */
325       OONF_DEBUG(session->log_source, "No matching prefix for incoming connection found");
326       return -1;
327     }
328     result = &ip->address;
329     netaddr_socket_init(&remote, &addr, port, ifdata->index);
330   }
331
332   /* initialize session */
333   netaddr_socket_init(&local, result, 0, ifdata->index);
334
335   router_if = dlep_router_get_by_layer2_if(ifdata->name);
336   if (router_if && &router_if->interf.session == session) {
337     dlep_router_add_session(router_if, &local, &remote);
338     return 0;
339   }
340   /* ignore incoming offer, something is wrong */
341   return -1;
342 }
343
344 /**
345  * Process the peer initialization ack message
346  * @param ext (this) dlep extension
347  * @param session dlep session
348  * @return -1 if an error happened, 0 otherwise
349  */
350 static int
351 _router_process_session_init_ack(struct dlep_extension *ext __attribute__((unused)), struct dlep_session *session) {
352   struct oonf_layer2_net *l2net;
353   struct dlep_parser_value *value;
354   const uint8_t *ptr;
355   int result;
356
357   if (session->restrict_signal != DLEP_SESSION_INITIALIZATION_ACK) {
358     /* ignore unless we are in initialization mode */
359     return 0;
360   }
361
362   /* mandatory heartbeat tlv */
363   if (dlep_reader_heartbeat_tlv(&session->remote_heartbeat_interval, session, NULL)) {
364     OONF_INFO(session->log_source, "no heartbeat tlv, should not happen!");
365     return -1;
366   }
367
368   /* optional extension supported tlv */
369   value = dlep_session_get_tlv_value(session, DLEP_EXTENSIONS_SUPPORTED_TLV);
370   if (value) {
371     ptr = dlep_session_get_tlv_binary(session, value);
372     if (dlep_session_update_extensions(session, ptr, value->length / 2)) {
373       return -1;
374     }
375   }
376   else if (dlep_session_update_extensions(session, NULL, 0)) {
377     return -1;
378   }
379
380   l2net = oonf_layer2_net_add(session->l2_listener.name);
381   if (!l2net) {
382     return -1;
383   }
384
385   /* mark interface as DLEP */
386   l2net->if_type = OONF_LAYER2_TYPE_WIRELESS;
387   l2net->if_dlep = true;
388
389   /* map user data into interface */
390   result = dlep_reader_map_l2neigh_data(l2net->neighdata, session, _base);
391   if (result) {
392     OONF_INFO(session->log_source, "tlv mapping failed for extension %u: %u", ext->id, result);
393     return result;
394   }
395
396   OONF_DEBUG(session->log_source, "Remote heartbeat interval %" PRIu64, session->remote_heartbeat_interval);
397
398   dlep_base_proto_start_local_heartbeat(session);
399   dlep_base_proto_start_remote_heartbeat(session);
400
401   dlep_base_proto_print_status(session);
402
403   session->next_restrict_signal = DLEP_ALL_SIGNALS;
404
405   return 0;
406 }
407
408 /**
409  * Process the peer update message
410  * @param ext (this) dlep extension
411  * @param session dlep session
412  * @return -1 if an error happened, 0 otherwise
413  */
414 static int
415 _router_process_session_update(struct dlep_extension *ext __attribute__((unused)), struct dlep_session *session) {
416   struct oonf_layer2_net *l2net;
417   int result;
418
419   l2net = oonf_layer2_net_add(session->l2_listener.name);
420   if (!l2net) {
421     return -1;
422   }
423
424   result = dlep_reader_map_l2neigh_data(l2net->neighdata, session, _base);
425   if (result) {
426     OONF_INFO(session->log_source, "tlv mapping failed for extension %u: %u", ext->id, result);
427     return -1;
428   }
429
430   /* generate ACK */
431   return dlep_session_generate_signal(session, DLEP_SESSION_UPDATE_ACK, NULL);
432 }
433
434 /**
435  * Process the peer update ack message
436  * @param ext (this) dlep extension
437  * @param session dlep session
438  * @return -1 if an error happened, 0 otherwise
439  */
440 static int
441 _router_process_session_update_ack(struct dlep_extension *ext __attribute__((unused)), struct dlep_session *session) {
442   dlep_base_proto_print_status(session);
443   return 0;
444 }
445
446 /**
447  * Process the destination up message
448  * @param ext (this) dlep extension
449  * @param session dlep session
450  * @return -1 if an error happened, 0 otherwise
451  */
452 static int
453 _router_process_destination_up(struct dlep_extension *ext __attribute__((unused)), struct dlep_session *session) {
454   struct oonf_layer2_net *l2net;
455   struct oonf_layer2_neigh *l2neigh;
456   struct netaddr mac;
457   int result;
458
459   if (dlep_reader_mac_tlv(&mac, session, NULL)) {
460     OONF_INFO(session->log_source, "mac tlv missing");
461     return -1;
462   }
463
464   l2net = oonf_layer2_net_add(session->l2_listener.name);
465   if (!l2net) {
466     return dlep_session_generate_signal_status(
467       session, DLEP_DESTINATION_UP_ACK, &mac, DLEP_STATUS_REQUEST_DENIED, "Not enough memory");
468   }
469   l2neigh = oonf_layer2_neigh_add(l2net, &mac);
470   if (!l2neigh) {
471     return dlep_session_generate_signal_status(
472       session, DLEP_DESTINATION_UP_ACK, &mac, DLEP_STATUS_REQUEST_DENIED, "Not enough memory");
473   }
474
475   result = dlep_reader_map_l2neigh_data(l2neigh->data, session, _base);
476   if (result) {
477     OONF_INFO(session->log_source, "tlv mapping failed for extension %u: %u", ext->id, result);
478     return result;
479   }
480
481   /* generate ACK */
482   return dlep_session_generate_signal_status (session, DLEP_DESTINATION_UP_ACK, &mac, DLEP_STATUS_OKAY, "Success");
483 }
484
485 /**
486  * Process the destination up ack message
487  * @param ext (this) dlep extension
488  * @param session dlep session
489  * @return -1 if an error happened, 0 otherwise
490  */
491 static int
492 _router_process_destination_up_ack(struct dlep_extension *ext __attribute__((unused)), struct dlep_session *session) {
493   dlep_base_proto_print_status(session);
494   return 0;
495 }
496
497 /**
498  * Process the destination down message
499  * @param ext (this) dlep extension
500  * @param session dlep session
501  * @return -1 if an error happened, 0 otherwise
502  */
503 static int
504 _router_process_destination_down(struct dlep_extension *ext __attribute__((unused)), struct dlep_session *session) {
505   struct oonf_layer2_net *l2net;
506   struct oonf_layer2_neigh *l2neigh;
507   struct netaddr mac;
508
509   if (dlep_reader_mac_tlv(&mac, session, NULL)) {
510     return -1;
511   }
512
513   l2net = oonf_layer2_net_get(session->l2_listener.name);
514   if (!l2net) {
515     return 0;
516   }
517
518   l2neigh = oonf_layer2_neigh_get(l2net, &mac);
519   if (!l2neigh) {
520     return 0;
521   }
522
523   /* remove layer2 neighbor */
524   oonf_layer2_neigh_remove(l2neigh, session->l2_origin);
525
526   /* generate ACK */
527   return dlep_session_generate_signal_status(session, DLEP_DESTINATION_DOWN_ACK, &mac, DLEP_STATUS_OKAY, "Success");
528 }
529
530 /**
531  * Process the destination down ack message
532  * @param ext (this) dlep extension
533  * @param session dlep session
534  * @return -1 if an error happened, 0 otherwise
535  */
536 static int
537 _router_process_destination_down_ack(struct dlep_extension *ext __attribute__((unused)), struct dlep_session *session) {
538   dlep_base_proto_print_status(session);
539   return 0;
540 }
541
542 /**
543  * Process the destination update message
544  * @param ext (this) dlep extension
545  * @param session dlep session
546  * @return -1 if an error happened, 0 otherwise
547  */
548 static int
549 _router_process_destination_update(struct dlep_extension *ext __attribute__((unused)), struct dlep_session *session) {
550   struct oonf_layer2_net *l2net;
551   struct oonf_layer2_neigh *l2neigh;
552   struct netaddr mac;
553   int result;
554
555   if (dlep_reader_mac_tlv(&mac, session, NULL)) {
556     return -1;
557   }
558
559   l2net = oonf_layer2_net_get(session->l2_listener.name);
560   if (!l2net) {
561     return 0;
562   }
563
564   l2neigh = oonf_layer2_neigh_get(l2net, &mac);
565   if (!l2neigh) {
566     /* we did not get the destination up signal */
567     return 0;
568   }
569
570   result = dlep_reader_map_l2neigh_data(l2neigh->data, session, _base);
571   if (result) {
572     OONF_INFO(session->log_source, "tlv mapping failed for extension %u: %u", ext->id, result);
573     return result;
574   }
575
576   return 0;
577 }
578
579 /**
580  * Process the link characteristic ack message
581  * @param ext (this) dlep extension
582  * @param session dlep session
583  * @return -1 if an error happened, 0 otherwise
584  */
585 static int
586 _router_process_link_char_ack(struct dlep_extension *ext __attribute__((unused)), struct dlep_session *session) {
587   dlep_base_proto_print_status(session);
588   return 0;
589 }
590
591 /**
592  * Generate a peer discovery signal
593  * @param ext (this) dlep extension
594  * @param session dlep session
595  * @param addr mac address the message should refer to
596  * @return -1 if an error happened, 0 otherwise
597  */
598 static int
599 _router_write_peer_discovery(struct dlep_extension *ext __attribute__((unused)), struct dlep_session *session,
600   const struct netaddr *addr __attribute__((unused))) {
601   if (session->restrict_signal != DLEP_UDP_PEER_OFFER) {
602     return -1;
603   }
604   return 0;
605 }
606
607 /**
608  * Generate a peer init message
609  * @param ext (this) dlep extension
610  * @param session dlep session
611  * @param addr mac address the message should refer to
612  * @return -1 if an error happened, 0 otherwise
613  */
614 static int
615 _router_write_session_init(struct dlep_extension *ext __attribute__((unused)), struct dlep_session *session,
616   const struct netaddr *addr __attribute__((unused))) {
617   const uint16_t *ext_ids;
618   uint16_t ext_count;
619
620   /* write supported extensions */
621   ext_ids = dlep_extension_get_ids(&ext_count);
622   if (ext_count) {
623     dlep_writer_add_supported_extensions(&session->writer, ext_ids, ext_count);
624   }
625
626   dlep_writer_add_heartbeat_tlv(&session->writer, session->cfg.heartbeat_interval);
627
628   /* TODO: report if radio has secured the medium */
629   dlep_writer_add_peer_type_tlv(&session->writer, session->cfg.peer_type, false);
630
631   return 0;
632 }