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