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