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