Merge branch 'master' into mpr_rework
[oonf.git] / src-plugins / olsrv2 / olsrv2 / olsrv2_reader.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/netaddr.h"
49 #include "core/oonf_logging.h"
50 #include "core/oonf_subsystem.h"
51 #include "subsystems/oonf_duplicate_set.h"
52 #include "subsystems/oonf_rfc5444.h"
53
54 #include "nhdp/nhdp_domain.h"
55
56 #include "olsrv2/olsrv2.h"
57 #include "olsrv2/olsrv2_internal.h"
58 #include "olsrv2/olsrv2_originator.h"
59 #include "olsrv2/olsrv2_reader.h"
60 #include "olsrv2/olsrv2_routing.h"
61 #include "olsrv2/olsrv2_tc.h"
62
63 /* constants and definitions */
64
65 /* OLSRv2 message TLV array index */
66 enum {
67   IDX_TLV_ITIME,
68   IDX_TLV_VTIME,
69   IDX_TLV_CONT_SEQ_NUM,
70   IDX_TLV_MPRTYPES,
71   IDX_TLV_SSR,
72 };
73
74 /* OLSRv2 address TLV array index pass 1 */
75 enum {
76   IDX_ADDRTLV_LINK_METRIC,
77   IDX_ADDRTLV_NBR_ADDR_TYPE,
78   IDX_ADDRTLV_GATEWAY,
79   IDX_ADDRTLV_SRC_PREFIX,
80 };
81
82 /**
83  * session data during TC parsing
84  */
85 struct _olsrv2_data {
86   /*! pointer to tc node of current data */
87   struct olsrv2_tc_node *node;
88
89   /*! validity time of current data */
90   uint64_t vtime;
91
92   /*! true if current TC is not fragmented */
93   bool complete_tc;
94
95   /*! MPR type value of current TC */
96   uint8_t mprtypes[NHDP_MAXIMUM_DOMAINS];
97
98   /*! number of entries in MPR type value */
99   size_t mprtypes_size;
100
101   /*! true if a change happened for this domain */
102   bool changed[NHDP_MAXIMUM_DOMAINS];
103 };
104
105 /* Prototypes */
106 static enum rfc5444_result
107 _cb_messagetlvs(struct rfc5444_reader_tlvblock_context *context);
108
109 static enum rfc5444_result
110 _cb_addresstlvs(struct rfc5444_reader_tlvblock_context *context);
111 static void _handle_gateways(struct rfc5444_reader_tlvblock_entry *tlv,
112     struct os_route_key *ssprefix, const uint32_t *cost_out,
113     const struct netaddr *addr);
114 static enum rfc5444_result _cb_messagetlvs_end(
115     struct rfc5444_reader_tlvblock_context *context, bool dropped);
116
117 /* definition of the RFC5444 reader components */
118 static struct rfc5444_reader_tlvblock_consumer _olsrv2_message_consumer = {
119   .order = RFC5444_MAIN_PARSER_PRIORITY,
120   .msg_id = RFC7181_MSGTYPE_TC,
121   .block_callback = _cb_messagetlvs,
122   .end_callback = _cb_messagetlvs_end,
123 };
124
125 static struct rfc5444_reader_tlvblock_consumer_entry _olsrv2_message_tlvs[] = {
126   [IDX_TLV_ITIME] = { .type = RFC5497_MSGTLV_INTERVAL_TIME, .type_ext = 0, .match_type_ext = true,
127       .min_length = 1, .max_length = 511, .match_length = true },
128   [IDX_TLV_VTIME] = { .type = RFC5497_MSGTLV_VALIDITY_TIME, .type_ext = 0, .match_type_ext = true,
129       .mandatory = true, .min_length = 1, .max_length = 511, .match_length = true },
130   [IDX_TLV_CONT_SEQ_NUM] = { .type = RFC7181_MSGTLV_CONT_SEQ_NUM,
131       .mandatory = true, .min_length = 2, .max_length = 65535, .match_length = true },
132   [IDX_TLV_MPRTYPES] = { .type = DRAFT_MT_MSGTLV_MPR_TYPES,
133       .type_ext = DRAFT_MT_MSGTLV_MPR_TYPES_EXT, .match_type_ext = true,
134       .min_length = 1, .max_length = NHDP_MAXIMUM_DOMAINS, .match_length = true },
135   [IDX_TLV_SSR] = { .type = DRAFT_SSR_MSGTLV_CAPABILITY,
136       .type_ext = DRAFT_SSR_MSGTLV_CAPABILITY_EXT, .match_type_ext = true },
137 };
138
139 static struct rfc5444_reader_tlvblock_consumer _olsrv2_address_consumer = {
140   .order = RFC5444_MAIN_PARSER_PRIORITY,
141   .msg_id = RFC7181_MSGTYPE_TC,
142   .addrblock_consumer = true,
143   .block_callback = _cb_addresstlvs,
144 };
145
146 static struct rfc5444_reader_tlvblock_consumer_entry _olsrv2_address_tlvs[] = {
147   [IDX_ADDRTLV_LINK_METRIC] = { .type = RFC7181_ADDRTLV_LINK_METRIC,
148     .min_length = 2, .max_length = 65535, .match_length = true },
149   [IDX_ADDRTLV_NBR_ADDR_TYPE] = { .type = RFC7181_ADDRTLV_NBR_ADDR_TYPE,
150     .min_length = 1, .max_length = 65535, .match_length = true },
151   [IDX_ADDRTLV_GATEWAY] = { .type = RFC7181_ADDRTLV_GATEWAY,
152     .min_length = 1, .max_length = 65535, .match_length = true },
153   [IDX_ADDRTLV_SRC_PREFIX] = { .type = SRCSPEC_GW_ADDRTLV_SRC_PREFIX,
154     .min_length = 1, .max_length = 17, .match_length = true },
155 };
156
157 /* nhdp multiplexer/protocol */
158 static struct oonf_rfc5444_protocol *_protocol = NULL;
159
160 static struct _olsrv2_data _current;
161
162 /**
163  * Initialize olsrv2 reader
164  * @param p RFC5444 protocol instance
165  */
166 void
167 olsrv2_reader_init(struct oonf_rfc5444_protocol *p) {
168   _protocol = p;
169
170   rfc5444_reader_add_message_consumer(
171       &_protocol->reader, &_olsrv2_message_consumer,
172       _olsrv2_message_tlvs, ARRAYSIZE(_olsrv2_message_tlvs));
173   rfc5444_reader_add_message_consumer(
174       &_protocol->reader, &_olsrv2_address_consumer,
175       _olsrv2_address_tlvs, ARRAYSIZE(_olsrv2_address_tlvs));
176 }
177
178 /**
179  * Cleanup nhdp reader
180  */
181 void
182 olsrv2_reader_cleanup(void) {
183   rfc5444_reader_remove_message_consumer(
184       &_protocol->reader, &_olsrv2_address_consumer);
185   rfc5444_reader_remove_message_consumer(
186       &_protocol->reader, &_olsrv2_message_consumer);
187 }
188
189 /**
190  * Callback that parses message TLVs of TC
191  * @param context
192  * @return
193  */
194 static enum rfc5444_result
195 _cb_messagetlvs(struct rfc5444_reader_tlvblock_context *context) {
196   uint64_t itime;
197   uint16_t ansn;
198   uint8_t tmp;
199   int af_type;
200 #ifdef OONF_LOG_DEBUG_INFO
201   struct netaddr_str buf;
202 #endif
203
204   /*
205    * First remove all old session data.
206    * Do not put anything that could drop a session before this point,
207    * otherwise the cleanup path will run on an outdated session object.
208    */
209   memset(&_current, 0, sizeof(_current));
210
211   OONF_DEBUG(LOG_OLSRV2_R, "Received TC from %s",
212       netaddr_to_string(&buf, _protocol->input.src_address));
213
214   if (!context->has_origaddr || !context->has_hopcount
215       || !context->has_hoplimit || !context->has_seqno) {
216     OONF_DEBUG(LOG_OLSRV2_R, "Missing message flag");
217     return RFC5444_DROP_MESSAGE;
218   }
219
220   if (olsrv2_originator_is_local(&context->orig_addr)) {
221     OONF_DEBUG(LOG_OLSRV2_R, "We are hearing ourself");
222     return RFC5444_DROP_MESSAGE;
223   }
224
225   switch (context->addr_len) {
226     case 4:
227       af_type = AF_INET;
228       break;
229     case 16:
230       af_type = AF_INET6;
231       break;
232     default:
233       af_type = 0;
234       break;
235   }
236
237   if (!oonf_rfc5444_is_interface_active(_protocol->input.interface, af_type)) {
238     OONF_DEBUG(LOG_OLSRV2_R, "We do not handle address length %u on interface %s",
239         context->addr_len, _protocol->input.interface->name);
240     return RFC5444_DROP_MESSAGE;
241   }
242
243   OONF_DEBUG(LOG_OLSRV2_R, "Originator: %s   Seqno: %u",
244       netaddr_to_string(&buf, &context->orig_addr), context->seqno);
245
246   /* get cont_seq_num extension */
247   tmp = _olsrv2_message_tlvs[IDX_TLV_CONT_SEQ_NUM].type_ext;
248   if (tmp != RFC7181_CONT_SEQ_NUM_COMPLETE
249       && tmp != RFC7181_CONT_SEQ_NUM_INCOMPLETE) {
250     OONF_DEBUG(LOG_OLSRV2_R, "Illegal extension of CONT_SEQ_NUM TLV: %u",
251         tmp);
252     return RFC5444_DROP_MESSAGE;
253   }
254   _current.complete_tc = tmp == RFC7181_CONT_SEQ_NUM_COMPLETE;
255
256   /* get ANSN */
257   memcpy(&ansn,
258       _olsrv2_message_tlvs[IDX_TLV_CONT_SEQ_NUM].tlv->single_value, 2);
259   ansn = ntohs(ansn);
260
261   /* get VTime/ITime */
262   tmp = rfc5497_timetlv_get_from_vector(
263       _olsrv2_message_tlvs[IDX_TLV_VTIME].tlv->single_value,
264       _olsrv2_message_tlvs[IDX_TLV_VTIME].tlv->length,
265       context->hopcount);
266   _current.vtime = rfc5497_timetlv_decode(tmp);
267
268   if (_olsrv2_message_tlvs[IDX_TLV_ITIME].tlv) {
269     tmp = rfc5497_timetlv_get_from_vector(
270         _olsrv2_message_tlvs[IDX_TLV_ITIME].tlv->single_value,
271         _olsrv2_message_tlvs[IDX_TLV_ITIME].tlv->length,
272         context->hopcount);
273     itime = rfc5497_timetlv_decode(tmp);
274   }
275   else {
276     itime = 0;
277   }
278
279   /* get mprtypes */
280   _current.mprtypes_size = nhdp_domain_process_mprtypes_tlv(
281       _current.mprtypes, sizeof(_current.mprtypes),
282       _olsrv2_message_tlvs[IDX_TLV_MPRTYPES].tlv);
283
284   /* test if we already forwarded the message */
285   if (!olsrv2_mpr_shall_forwarding(
286       context, _protocol->input.src_address, _current.vtime)) {
287     /* mark message as 'no forward */
288     rfc5444_reader_prevent_forwarding(context);
289   }
290
291   /* test if we already processed the message */
292   if (!olsrv2_mpr_shall_process(context, _current.vtime)) {
293     OONF_DEBUG(LOG_OLSRV2_R, "Processing set says 'do not process'");
294     return RFC5444_DROP_MSG_BUT_FORWARD;
295   }
296
297   /* get tc node */
298   _current.node = olsrv2_tc_node_add(
299       &context->orig_addr, _current.vtime, ansn);
300   if (_current.node == NULL) {
301     OONF_DEBUG(LOG_OLSRV2_R, "Cannot create node");
302     return RFC5444_DROP_MSG_BUT_FORWARD;
303   }
304
305   /* check if the topology information is recent enough */
306   if (_current.complete_tc) {
307     if (rfc5444_seqno_is_smaller(ansn, _current.node->ansn)) {
308       OONF_DEBUG(LOG_OLSRV2_R, "ANSN %u is smaller than last stored ANSN %u",
309           ansn, _current.node->ansn);
310       return RFC5444_DROP_MSG_BUT_FORWARD;
311     }
312   }
313   else {
314     if (!rfc5444_seqno_is_larger(ansn, _current.node->ansn)) {
315       OONF_DEBUG(LOG_OLSRV2_R, "ANSN %u is smaller than last stored ANSN %u",
316           ansn, _current.node->ansn);
317       return RFC5444_DROP_MSG_BUT_FORWARD;
318     }
319   }
320
321   /* overwrite old ansn */
322   _current.node->ansn = ansn;
323
324   /* reset validity time and interval time */
325   oonf_timer_set(&_current.node->_validity_time, _current.vtime);
326   _current.node->interval_time = itime;
327
328   /* set source-specific flags */
329   _current.node->source_specific = _olsrv2_message_tlvs[IDX_TLV_SSR].tlv != NULL;
330
331   /* continue parsing the message */
332   return RFC5444_OKAY;
333 }
334
335 /**
336  * Callback that parses address TLVs of TC
337  * @param context
338  * @return
339  */
340 static enum rfc5444_result
341 _cb_addresstlvs(struct rfc5444_reader_tlvblock_context *context __attribute__((unused))) {
342   struct rfc5444_reader_tlvblock_entry *tlv;
343   struct nhdp_domain *domain;
344   struct olsrv2_tc_edge *edge;
345   struct olsrv2_tc_attachment *end;
346   uint32_t cost_in[NHDP_MAXIMUM_DOMAINS];
347   uint32_t cost_out[NHDP_MAXIMUM_DOMAINS];
348   struct rfc7181_metric_field metric_value;
349   size_t i;
350   struct os_route_key ssprefix;
351
352 #ifdef OONF_LOG_DEBUG_INFO
353   struct netaddr_str buf;
354 #endif
355
356   if (_current.node == NULL) {
357     return RFC5444_OKAY;
358   }
359
360   for (i=0; i<NHDP_MAXIMUM_DOMAINS; i++) {
361     cost_in[i] = RFC7181_METRIC_INFINITE;
362     cost_out[i] = RFC7181_METRIC_INFINITE;
363   }
364
365   OONF_DEBUG(LOG_OLSRV2_R, "Found address in tc: %s",
366       netaddr_to_string(&buf, &context->addr));
367
368   os_routing_init_sourcespec_prefix(&ssprefix, &context->addr);
369
370   for (tlv = _olsrv2_address_tlvs[IDX_ADDRTLV_LINK_METRIC].tlv;
371       tlv; tlv = tlv->next_entry) {
372     domain = nhdp_domain_get_by_ext(tlv->type_ext);
373     if (domain == NULL) {
374       continue;
375     }
376
377     memcpy(&metric_value, tlv->single_value, sizeof(metric_value));
378
379     OONF_DEBUG(LOG_OLSRV2_R, "Metric for domain %d: 0x%02x%02x",
380         domain->index, metric_value.b[0], metric_value.b[1]);
381
382     if (rfc7181_metric_has_flag(&metric_value, RFC7181_LINKMETRIC_INCOMING_NEIGH)) {
383       cost_in[domain->index] = rfc7181_metric_decode(&metric_value);
384       OONF_DEBUG(LOG_OLSRV2_R, "Incoming metric: %d", cost_in[domain->index]);
385      }
386
387     if (rfc7181_metric_has_flag(&metric_value, RFC7181_LINKMETRIC_OUTGOING_NEIGH)) {
388       cost_out[domain->index] = rfc7181_metric_decode(&metric_value);
389       OONF_DEBUG(LOG_OLSRV2_R, "Outgoing metric: %d", cost_out[domain->index]);
390     }
391   }
392
393   if ((tlv = _olsrv2_address_tlvs[IDX_ADDRTLV_NBR_ADDR_TYPE].tlv)) {
394     /* parse originator neighbor */
395     if ((tlv->single_value[0] & RFC7181_NBR_ADDR_TYPE_ORIGINATOR) != 0) {
396       edge = olsrv2_tc_edge_add(_current.node, &context->addr);
397       if (edge) {
398         OONF_DEBUG(LOG_OLSRV2_R, "Address is originator");
399         edge->ansn = _current.node->ansn;
400
401         for (i=0; i<NHDP_MAXIMUM_DOMAINS; i++) {
402           if (cost_out[i] <= RFC7181_METRIC_MAX) {
403             _current.changed[i] |= (edge->cost[i] != cost_out[i]);
404             edge->cost[i] = cost_out[i];
405           }
406           else if (_current.complete_tc) {
407             _current.changed[i] |= (edge->cost[i] != RFC7181_METRIC_INFINITE);
408             edge->cost[i] = RFC7181_METRIC_INFINITE;
409           }
410           if (edge->inverse->virtual && cost_in[i] <= RFC7181_METRIC_MAX) {
411             _current.changed[i] |= (edge->inverse->cost[i] != cost_in[i]);
412             edge->inverse->cost[i] = cost_in[i];
413           }
414           else if (edge->inverse->virtual && _current.complete_tc) {
415             _current.changed[i] |= (edge->inverse->cost[i] != RFC7181_METRIC_INFINITE);
416             edge->inverse->cost[i] = RFC7181_METRIC_INFINITE;
417           }
418         }
419       }
420     }
421     /* parse routable neighbor (which is not an originator) */
422     else if ((tlv->single_value[0] & RFC7181_NBR_ADDR_TYPE_ROUTABLE) != 0) {
423       end = olsrv2_tc_endpoint_add(_current.node, &ssprefix, true);
424       if (end) {
425         OONF_DEBUG(LOG_OLSRV2_R, "Address is routable, but not originator");
426         end->ansn = _current.node->ansn;
427         for (i=0; i< NHDP_MAXIMUM_DOMAINS; i++) {
428           if (cost_out[i] <= RFC7181_METRIC_MAX) {
429             _current.changed[i] |= (end->cost[i] != cost_out[i]);
430             end->cost[i] = cost_out[i];
431           }
432           else if (_current.complete_tc) {
433             _current.changed[i] |= (end->cost[i] != RFC7181_METRIC_INFINITE);
434             end->cost[i] = RFC7181_METRIC_INFINITE;
435           }
436         }
437       }
438     }
439   }
440
441   if ((tlv = _olsrv2_address_tlvs[IDX_ADDRTLV_GATEWAY].tlv)) {
442     _handle_gateways(tlv, &ssprefix, cost_out, &context->addr);
443   }
444   return RFC5444_OKAY;
445 }
446
447 static void
448 _handle_gateways(struct rfc5444_reader_tlvblock_entry *tlv,
449     struct os_route_key *ssprefix, const uint32_t *cost_out,
450     const struct netaddr *addr) {
451   struct olsrv2_tc_attachment *end;
452   struct nhdp_domain *domain;
453   size_t i;
454
455   /* check length */
456   if (tlv->length > 1 && tlv->length < _current.mprtypes_size) {
457     /* bad length */
458     return;
459   }
460
461   switch (tlv->type_ext) {
462     case RFC7181_DSTSPEC_GATEWAY:
463     case RFC7181_SRCSPEC_GATEWAY:
464       /* truncate address */
465       netaddr_truncate(&ssprefix->dst, &ssprefix->dst);
466       break;
467     case RFC7181_SRCSPEC_DEF_GATEWAY:
468       os_routing_init_sourcespec_src_prefix(ssprefix, addr);
469
470       /* truncate address */
471       netaddr_truncate(&ssprefix->src, &ssprefix->src);
472       break;
473     default:
474       return;
475   }
476
477   if (_olsrv2_address_tlvs[IDX_ADDRTLV_SRC_PREFIX].tlv) {
478     /* copy source specific prefix */
479     ssprefix->src._prefix_len = _olsrv2_address_tlvs[IDX_ADDRTLV_SRC_PREFIX].tlv->single_value[0];
480     memcpy(&ssprefix->src._addr[0],
481         &_olsrv2_address_tlvs[IDX_ADDRTLV_SRC_PREFIX].tlv->single_value[1],
482         _olsrv2_address_tlvs[IDX_ADDRTLV_SRC_PREFIX].tlv->length - 1);
483   }
484
485   /* parse attached network */
486   end = olsrv2_tc_endpoint_add(_current.node, ssprefix, false);
487   if (!end) {
488     return;
489   }
490
491   end->ansn = _current.node->ansn;
492
493   if (_current.complete_tc) {
494     /* clear unused metrics */
495     for (i=0; i<NHDP_MAXIMUM_DOMAINS; i++) {
496       end->cost[i] = RFC7181_METRIC_INFINITE;
497     }
498   }
499
500   /* use MT definition of AN tlv */
501   for (i=0; i<_current.mprtypes_size; i++) {
502     domain = nhdp_domain_get_by_ext(_current.mprtypes[i]);
503     if (!domain) {
504       /* unknown domain */
505       continue;
506     }
507
508     end->cost[domain->index] = cost_out[domain->index];
509
510     if (tlv->length == 1) {
511       end->distance[domain->index] = tlv->single_value[0];
512     }
513     else {
514       end->distance[domain->index] = tlv->single_value[i];
515     }
516
517     OONF_DEBUG(LOG_OLSRV2_R, "Address is Attached Network (domain %u): dist=%u",
518         domain->ext, end->distance[domain->index]);
519   }
520 }
521
522 /**
523  * Callback that is called when message parsing of TLV is finished
524  * @param context
525  * @param dropped
526  * @return
527  */
528 static enum rfc5444_result
529 _cb_messagetlvs_end(struct rfc5444_reader_tlvblock_context *context __attribute__((unused)),
530     bool dropped) {
531   /* cleanup everything that is not the current ANSN, check for ss-prefixes */
532   struct olsrv2_tc_edge *edge, *edge_it;
533   struct olsrv2_tc_attachment *end, *end_it;
534   struct nhdp_domain *domain;
535 #ifdef OONF_LOG_DEBUG_INFO
536   struct netaddr_str nbuf1, nbuf2;
537 #endif
538
539   if (dropped || _current.node == NULL) {
540     return RFC5444_OKAY;
541   }
542
543   avl_for_each_element_safe(&_current.node->_edges, edge, _node, edge_it) {
544     if (edge->ansn != _current.node->ansn) {
545       olsrv2_tc_edge_remove(edge);
546     }
547   }
548
549   avl_for_each_element_safe(&_current.node->_attached_networks, end, _src_node, end_it) {
550     if (end->ansn != _current.node->ansn) {
551       olsrv2_tc_endpoint_remove(end);
552     }
553   }
554
555
556   list_for_each_element(nhdp_domain_get_list(), domain, _node) {
557     _current.node->ss_attached_networks[domain->index] = false;
558
559     OONF_DEBUG(LOG_OLSRV2_R, "Look for source-specific attachents of %s:",
560         netaddr_to_string(&nbuf1, &_current.node->target.prefix.dst));
561     avl_for_each_element_safe(&_current.node->_attached_networks, end, _src_node, end_it) {
562       OONF_DEBUG(LOG_OLSRV2_R, "        attachent [%s]/[%s]: %x / %u",
563           netaddr_to_string(&nbuf1, &end->dst->target.prefix.dst),
564           netaddr_to_string(&nbuf2, &end->dst->target.prefix.src),
565           end->cost[domain->index], netaddr_get_prefix_length(&end->dst->target.prefix.src));
566
567       if (end->cost[domain->index] <= RFC7181_METRIC_MAX
568           && netaddr_get_prefix_length(&end->dst->target.prefix.src) > 0) {
569         _current.node->ss_attached_networks[domain->index] = true;
570         break;
571       }
572     }
573   }
574
575   olsrv2_tc_trigger_change(_current.node);
576   _current.node = NULL;
577
578   list_for_each_element(nhdp_domain_get_list(), domain, _node) {
579     if (_current.changed[domain->index]) {
580       olsrv2_routing_domain_changed(domain);
581     }
582   }
583
584   return RFC5444_OKAY;
585 }