Rename "subsystems" directory to "base"
[oonf.git] / src / nhdp / nhdp / nhdp_writer.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/libcommon/avl_comp.h>
48 #include <oonf/oonf.h>
49 #include <oonf/libcore/oonf_logging.h>
50 #include <oonf/base/oonf_rfc5444.h>
51
52 #include <oonf/nhdp/nhdp/nhdp.h>
53 #include <oonf/nhdp/nhdp/nhdp_domain.h>
54 #include <oonf/nhdp/nhdp/nhdp_interfaces.h>
55 #include <oonf/nhdp/nhdp/nhdp_internal.h>
56 #include <oonf/nhdp/nhdp/nhdp_writer.h>
57
58 /* constants */
59 enum
60 {
61   IDX_ADDRTLV_LOCAL_IF,
62   IDX_ADDRTLV_LINK_STATUS,
63   IDX_ADDRTLV_OTHER_NEIGHB,
64   IDX_ADDRTLV_MPR,
65 };
66
67 /* prototypes */
68 static int _cb_addMessageHeader(struct rfc5444_writer *, struct rfc5444_writer_message *);
69 static void _cb_addMessageTLVs(struct rfc5444_writer *);
70 static void _cb_addAddresses(struct rfc5444_writer *);
71
72 static void _add_link_address(struct rfc5444_writer *writer, struct rfc5444_writer_content_provider *prv,
73   struct nhdp_interface *interf, struct nhdp_naddr *naddr);
74 static void _add_localif_address(struct rfc5444_writer *writer, struct rfc5444_writer_content_provider *prv,
75   struct nhdp_interface *interf, struct nhdp_interface_addr *addr);
76
77 static void _write_metric_tlv(struct rfc5444_writer *writer, struct rfc5444_writer_address *addr,
78   struct nhdp_neighbor *neigh, struct nhdp_link *lnk, struct nhdp_domain *domain);
79
80 /* definition of NHDP writer */
81 static struct rfc5444_writer_message *_nhdp_message = NULL;
82
83 static struct rfc5444_writer_content_provider _nhdp_msgcontent_provider = {
84   .msg_type = RFC6130_MSGTYPE_HELLO,
85   .addMessageTLVs = _cb_addMessageTLVs,
86   .addAddresses = _cb_addAddresses,
87 };
88
89 static struct rfc5444_writer_tlvtype _nhdp_addrtlvs[] = {
90   [IDX_ADDRTLV_LOCAL_IF] = { .type = RFC6130_ADDRTLV_LOCAL_IF },
91   [IDX_ADDRTLV_LINK_STATUS] = { .type = RFC6130_ADDRTLV_LINK_STATUS },
92   [IDX_ADDRTLV_OTHER_NEIGHB] = { .type = RFC6130_ADDRTLV_OTHER_NEIGHB },
93   [IDX_ADDRTLV_MPR] = { .type = RFC7181_ADDRTLV_MPR },
94 };
95
96 static struct oonf_rfc5444_protocol *_protocol;
97
98 static bool _cleanedup = false;
99 static bool _add_mac_tlv = true;
100 static struct nhdp_interface *_nhdp_if = NULL;
101
102 /**
103  * Initialize nhdp writer
104  * @param p rfc5444 protocol
105  * @return -1 if an error happened, 0 otherwise
106  */
107 int
108 nhdp_writer_init(struct oonf_rfc5444_protocol *p) {
109   _protocol = p;
110
111   _nhdp_message = rfc5444_writer_register_message(&_protocol->writer, RFC6130_MSGTYPE_HELLO, true);
112   if (_nhdp_message == NULL) {
113     OONF_WARN(LOG_NHDP_W, "Could not register NHDP Hello message");
114     return -1;
115   }
116
117   _nhdp_message->addMessageHeader = _cb_addMessageHeader;
118
119   if (rfc5444_writer_register_msgcontentprovider(
120         &_protocol->writer, &_nhdp_msgcontent_provider, _nhdp_addrtlvs, ARRAYSIZE(_nhdp_addrtlvs))) {
121     OONF_WARN(LOG_NHDP_W, "Count not register NHDP msg contentprovider");
122     rfc5444_writer_unregister_message(&_protocol->writer, _nhdp_message);
123     return -1;
124   }
125   return 0;
126 }
127
128 /**
129  * Cleanup nhdp writer
130  */
131 void
132 nhdp_writer_cleanup(void) {
133   /* remember we already did shut down the writer */
134   _cleanedup = true;
135
136   /* remove pbb writer */
137   rfc5444_writer_unregister_content_provider(
138     &_protocol->writer, &_nhdp_msgcontent_provider, _nhdp_addrtlvs, ARRAYSIZE(_nhdp_addrtlvs));
139   rfc5444_writer_unregister_message(&_protocol->writer, _nhdp_message);
140 }
141
142 /**
143  * Send a NHDP Hello through the specified interface. This might result
144  * in both an IPv4 and IPv6 message
145  * @param ninterf NHDP interface
146  */
147 void
148 nhdp_writer_send_hello(struct nhdp_interface *ninterf) {
149   enum rfc5444_result result;
150   struct os_interface_listener *interf;
151   struct netaddr_str buf;
152
153   if (_cleanedup) {
154     /* do not send more Hellos during shutdown */
155     return;
156   }
157
158   interf = nhdp_interface_get_if_listener(ninterf);
159   if (interf->data->flags.loopback) {
160     /* no NHDP on loopback interface */
161     return;
162   }
163
164   OONF_DEBUG(LOG_NHDP_W, "Sending Hello to interface %s", nhdp_interface_get_name(ninterf));
165
166   nhdp_domain_recalculate_mpr();
167
168   /* store NHDP interface */
169   _nhdp_if = ninterf;
170
171   /* send IPv4 (if socket is active) */
172   result = oonf_rfc5444_send_if(ninterf->rfc5444_if.interface->multicast4, RFC6130_MSGTYPE_HELLO);
173   if (result < 0) {
174     OONF_WARN(LOG_NHDP_W, "Could not send NHDP message to %s: %s (%d)",
175       netaddr_to_string(&buf, &ninterf->rfc5444_if.interface->multicast4->dst), rfc5444_strerror(result), result);
176   }
177
178   /* send IPV6 (if socket is active) */
179   result = oonf_rfc5444_send_if(ninterf->rfc5444_if.interface->multicast6, RFC6130_MSGTYPE_HELLO);
180   if (result < 0) {
181     OONF_WARN(LOG_NHDP_W, "Could not send NHDP message to %s: %s (%d)",
182       netaddr_to_string(&buf, &ninterf->rfc5444_if.interface->multicast6->dst), rfc5444_strerror(result), result);
183   }
184 }
185
186 /**
187  * activates or deactivates the MAC_TLV in the NHDP Hello messages
188  * @param active true if MAC_TLV should be present
189  */
190 void
191 nhdp_writer_set_mac_TLV_state(bool active) {
192   _add_mac_tlv = active;
193 }
194
195 /**
196  * Callback to initialize the message header for a HELLO message
197  * @param writer RFC5444 writer instance
198  * @param message RFC5444 message
199  */
200 static int
201 _cb_addMessageHeader(struct rfc5444_writer *writer, struct rfc5444_writer_message *message) {
202   struct oonf_rfc5444_target *target;
203   const struct netaddr *originator;
204   struct netaddr_str buf;
205
206   if (!message->target_specific) {
207     OONF_WARN(LOG_NHDP_W, "non interface-specific NHDP message!");
208     return RFC5444_DROP_MESSAGE;
209   }
210
211   target = oonf_rfc5444_get_target_from_writer(writer);
212   if (target != target->interface->multicast6 && target != target->interface->multicast4) {
213     OONF_WARN(LOG_NHDP_W, "Cannot generate unicast nhdp message to %s", netaddr_to_string(&buf, &target->dst));
214     return RFC5444_DROP_MESSAGE;
215   }
216
217   /* get originator */
218   if (writer->msg_addr_len == 4) {
219     originator = nhdp_get_originator(AF_INET);
220   }
221   else {
222     originator = nhdp_get_originator(AF_INET6);
223   }
224
225   OONF_DEBUG(LOG_NHDP_W, "Generate Hello on interface %s with destination %s", target->interface->name,
226     netaddr_to_string(&buf, &target->dst));
227
228   if (originator != NULL && netaddr_get_address_family(originator) != AF_UNSPEC) {
229     OONF_DEBUG(LOG_NHDP_W, "Add originator %s", netaddr_to_string(&buf, originator));
230
231     rfc5444_writer_set_msg_header(writer, message, true, false, false, false);
232     rfc5444_writer_set_msg_originator(writer, message, netaddr_get_binptr(originator));
233   }
234   else {
235     rfc5444_writer_set_msg_header(writer, message, false, false, false, false);
236   }
237   return RFC5444_OKAY;
238 }
239
240 /**
241  * Callback to add the message TLVs to a HELLO message
242  * @param writer RFC5444 writer
243  */
244 static void
245 _cb_addMessageTLVs(struct rfc5444_writer *writer) {
246   uint8_t vtime_encoded, itime_encoded;
247   struct oonf_rfc5444_target *target;
248   const struct netaddr *v4_originator;
249   struct os_interface *os_if;
250   uint8_t willingness[NHDP_MAXIMUM_DOMAINS];
251   size_t willingness_size;
252   uint8_t mprtypes[NHDP_MAXIMUM_DOMAINS];
253   uint8_t mprtypes_size;
254   struct netaddr_str buf;
255
256   target = oonf_rfc5444_get_target_from_writer(writer);
257
258   OONF_ASSERT(target == target->interface->multicast4 || target == target->interface->multicast6,
259                 LOG_NHDP_W, "target for NHDP is no interface multicast: %s", netaddr_to_string(&buf, &target->dst));
260
261   itime_encoded = rfc5497_timetlv_encode(_nhdp_if->refresh_interval);
262   vtime_encoded = rfc5497_timetlv_encode(_nhdp_if->h_hold_time);
263
264   rfc5444_writer_add_messagetlv(writer, RFC5497_MSGTLV_INTERVAL_TIME, 0, &itime_encoded, sizeof(itime_encoded));
265   rfc5444_writer_add_messagetlv(writer, RFC5497_MSGTLV_VALIDITY_TIME, 0, &vtime_encoded, sizeof(vtime_encoded));
266
267   /* generate MPRtypes */
268   mprtypes_size = nhdp_domain_encode_mprtypes_tlvvalue(mprtypes, sizeof(mprtypes));
269   if (mprtypes_size > 1) {
270     rfc5444_writer_add_messagetlv(
271       writer, RFC7722_MSGTLV_MPR_TYPES, RFC7722_MSGTLV_MPR_TYPES_EXT, &mprtypes, mprtypes_size);
272   }
273
274   /* add willingness for all domains */
275   willingness_size = nhdp_domain_encode_willingness_tlvvalue(willingness, sizeof(willingness));
276   rfc5444_writer_add_messagetlv(writer, RFC7181_MSGTLV_MPR_WILLING, 0, &willingness, willingness_size);
277
278   /* get v6 originator (might be unspecified) */
279   v4_originator = nhdp_get_originator(AF_INET);
280
281   /* add V4 originator to V6 message if available and interface is dualstack */
282   if (writer->msg_addr_len == 16 && v4_originator != NULL && netaddr_get_address_family(v4_originator) == AF_INET) {
283     rfc5444_writer_add_messagetlv(
284       writer, NHDP_MSGTLV_IPV4ORIGINATOR, 0, netaddr_get_binptr(v4_originator), netaddr_get_binlength(v4_originator));
285   }
286
287   /* add mac address of local interface */
288   os_if = nhdp_interface_get_if_listener(_nhdp_if)->data;
289
290   if (_add_mac_tlv && netaddr_get_address_family(&os_if->mac) == AF_MAC48) {
291     rfc5444_writer_add_messagetlv(
292       writer, NHDP_MSGTLV_MAC, 0, netaddr_get_binptr(&os_if->mac), netaddr_get_binlength(&os_if->mac));
293   }
294 }
295
296 /**
297  * Add a rfc5444 address with localif TLV to the stream
298  * @param writer RFC5444 writer instance
299  * @param prv RFC5444 content provider instance
300  * @param interf NHDP interface
301  * @param addr NHDP interface address
302  */
303 static void
304 _add_localif_address(struct rfc5444_writer *writer, struct rfc5444_writer_content_provider *prv,
305   struct nhdp_interface *interf, struct nhdp_interface_addr *addr) {
306   struct rfc5444_writer_address *address;
307   struct netaddr_str buf;
308   uint8_t value;
309   bool this_if;
310
311   /* check if address of local interface */
312   this_if = NULL != avl_find_element(&interf->_if_addresses, &addr->if_addr, addr, _if_node);
313
314   OONF_DEBUG(
315     LOG_NHDP_W, "Add %s (%s) to NHDP hello", netaddr_to_string(&buf, &addr->if_addr), this_if ? "this_if" : "other_if");
316
317   /* generate RFC5444 address */
318   address = rfc5444_writer_add_address(writer, prv->creator, &addr->if_addr, true);
319   if (address == NULL) {
320     OONF_WARN(LOG_NHDP_W, "Could not add address %s to NHDP hello", netaddr_to_string(&buf, &addr->if_addr));
321     return;
322   }
323
324   /* Add LOCALIF TLV */
325   if (this_if) {
326     value = RFC6130_LOCALIF_THIS_IF;
327   }
328   else {
329     value = RFC6130_LOCALIF_OTHER_IF;
330   }
331   rfc5444_writer_add_addrtlv(writer, address, &_nhdp_addrtlvs[IDX_ADDRTLV_LOCAL_IF], &value, sizeof(value), true);
332 }
333
334 /**
335  * Add a rfc5444 address with link_status or other_neigh TLV to the stream
336  * @param writer RFC5444 writer instance
337  * @param prv RFC5444 content provider instance
338  * @param interf NHDP interface
339  * @param naddr NHDP interface address
340  */
341 static void
342 _add_link_address(struct rfc5444_writer *writer, struct rfc5444_writer_content_provider *prv,
343   struct nhdp_interface *interf, struct nhdp_naddr *naddr) {
344   struct rfc5444_writer_address *address;
345   struct nhdp_domain *domain;
346   struct nhdp_laddr *laddr;
347   struct netaddr_str buf;
348   uint8_t linkstatus, otherneigh_sym;
349   uint8_t mprvalue[NHDP_MAXIMUM_DOMAINS];
350   size_t len;
351
352   /* initialize flags for default (lost address) address */
353   linkstatus = 255;
354   otherneigh_sym = 0;
355
356   laddr = nhdp_interface_get_link_addr(interf, &naddr->neigh_addr);
357   if (!nhdp_db_neighbor_addr_is_lost(naddr)) {
358     if (laddr != NULL && laddr->link->local_if == interf && laddr->link->status != NHDP_LINK_PENDING) {
359       linkstatus = laddr->link->status;
360     }
361
362     if (naddr->neigh->symmetric > 0 && linkstatus != NHDP_LINK_SYMMETRIC) {
363       otherneigh_sym = NHDP_LINK_SYMMETRIC;
364     }
365   }
366
367   /* generate RFC5444 address */
368   address = rfc5444_writer_add_address(writer, prv->creator, &naddr->neigh_addr, false);
369   if (address == NULL) {
370     OONF_WARN(LOG_NHDP_W, "Could not add address %s to NHDP hello", netaddr_to_string(&buf, &naddr->neigh_addr));
371     return;
372   }
373
374   if (linkstatus != 255) {
375     rfc5444_writer_add_addrtlv(
376       writer, address, &_nhdp_addrtlvs[IDX_ADDRTLV_LINK_STATUS], &linkstatus, sizeof(linkstatus), false);
377
378     OONF_DEBUG(LOG_NHDP_W, "Add %s (linkstatus=%d) to NHDP hello", netaddr_to_string(&buf, &naddr->neigh_addr),
379       laddr->link->status);
380   }
381
382   rfc5444_writer_add_addrtlv(
383     writer, address, &_nhdp_addrtlvs[IDX_ADDRTLV_OTHER_NEIGHB], &otherneigh_sym, sizeof(otherneigh_sym), false);
384
385   OONF_DEBUG(
386     LOG_NHDP_W, "Add %s (otherneigh=%d) to NHDP hello", netaddr_to_string(&buf, &naddr->neigh_addr), otherneigh_sym);
387
388   /* add MPR tlvs */
389   if (laddr != NULL) {
390     len = nhdp_domain_encode_mpr_tlvvalue(mprvalue, sizeof(mprvalue), laddr->link);
391
392     if (len) {
393       rfc5444_writer_add_addrtlv(writer, address, &_nhdp_addrtlvs[IDX_ADDRTLV_MPR], &mprvalue, len, false);
394     }
395   }
396
397   /* add linkcost TLVs */
398   list_for_each_element(nhdp_domain_get_list(), domain, _node) {
399     struct nhdp_link *lnk = NULL;
400     struct nhdp_neighbor *neigh = NULL;
401
402     if (linkstatus == NHDP_LINK_HEARD || linkstatus == NHDP_LINK_SYMMETRIC) {
403       lnk = laddr->link;
404     }
405     if (naddr->neigh->symmetric > 0 &&
406         (linkstatus == NHDP_LINK_SYMMETRIC || otherneigh_sym == RFC6130_OTHERNEIGHB_SYMMETRIC)) {
407       neigh = naddr->neigh;
408     }
409
410     _write_metric_tlv(writer, address, neigh, lnk, domain);
411   }
412 }
413
414 /**
415  * Write up to four metric TLVs to an address
416  * @param writer rfc5444 writer instance
417  * @param addr rfc5444 address
418  * @param neigh symmetric NHDP neighbor, might be NULL
419  * @param lnk symmetric NHDP link, might be NULL
420  * @param domain NHDP domain
421  */
422 static void
423 _write_metric_tlv(struct rfc5444_writer *writer, struct rfc5444_writer_address *addr, struct nhdp_neighbor *neigh,
424   struct nhdp_link *lnk, struct nhdp_domain *domain) {
425   static const enum rfc7181_linkmetric_flags flags[4] = {
426     RFC7181_LINKMETRIC_INCOMING_LINK,
427     RFC7181_LINKMETRIC_OUTGOING_LINK,
428     RFC7181_LINKMETRIC_INCOMING_NEIGH,
429     RFC7181_LINKMETRIC_OUTGOING_NEIGH,
430   };
431 #ifdef OONF_LOG_DEBUG_INFO
432   static const char *lq_name[4] = {
433     "l_in",
434     "l_out",
435     "n_in",
436     "n_out",
437   };
438 #endif
439   struct nhdp_link_domaindata *linkdata;
440   struct nhdp_neighbor_domaindata *neighdata;
441   struct rfc7181_metric_field metric_encoded[4], tlv_value;
442   int i, j, k;
443   uint32_t metrics[4] = { 0, 0, 0, 0 };
444
445   if (lnk == NULL && neigh == NULL) {
446     /* nothing to do */
447     return;
448   }
449
450   /* get link metrics if available */
451   if (lnk != NULL && (lnk->status == NHDP_LINK_HEARD || lnk->status == NHDP_LINK_SYMMETRIC)) {
452     linkdata = nhdp_domain_get_linkdata(domain, lnk);
453     metrics[0] = linkdata->metric.in;
454     metrics[1] = linkdata->metric.out;
455   }
456
457   /* get neighbor metrics if available */
458   if (neigh != NULL && neigh->symmetric > 0) {
459     neighdata = nhdp_domain_get_neighbordata(domain, neigh);
460     metrics[2] = neighdata->metric.in;
461     metrics[3] = neighdata->metric.out;
462   }
463
464   /* check if metric is infinite */
465   for (i = 0; i < 4; i++) {
466     if (metrics[i] > RFC7181_METRIC_MAX) {
467       metrics[i] = 0;
468     }
469   }
470
471   /* encode metrics */
472   for (i = 0; i < 4; i++) {
473     if (metrics[i] > 0) {
474       if (rfc7181_metric_encode(&metric_encoded[i], metrics[i])) {
475         OONF_WARN(LOG_NHDP_W, "Metric encoding for %u failed", metrics[i]);
476         return;
477       }
478     }
479   }
480
481   /* compress four metrics into 1-4 TLVs */
482   k = 0;
483   for (i = 0; i < 4; i++) {
484     /* find first metric value which still must be sent */
485     if (metrics[i] == 0) {
486       continue;
487     }
488
489     /* create value */
490     tlv_value = metric_encoded[i];
491
492     /* mark first metric value */
493     rfc7181_metric_set_flag(&tlv_value, flags[i]);
494
495     /* mark all metric pair that have the same linkmetric */
496     OONF_DEBUG(LOG_NHDP_W, "Add Metric %s (ext %u): 0x%02x%02x (%u)", lq_name[i], domain->ext, tlv_value.b[0],
497       tlv_value.b[1], metrics[i]);
498
499     for (j = 3; j > i; j--) {
500       if (metrics[j] > 0 && memcmp(&metric_encoded[i], &metric_encoded[j], sizeof(metric_encoded[0])) == 0) {
501         rfc7181_metric_set_flag(&tlv_value, flags[j]);
502         metrics[j] = 0;
503
504         OONF_DEBUG(LOG_NHDP_W, "Same metrics for %s (ext %u)", lq_name[j], domain->ext);
505       }
506     }
507
508     /* add to rfc5444 address */
509     rfc5444_writer_add_addrtlv(writer, addr, &domain->_metric_addrtlvs[k++], &tlv_value, sizeof(tlv_value), true);
510   }
511 }
512 /**
513  * Callback to add the addresses and address TLVs to a HELLO message
514  * @param writer RFC5444 writer instance
515  */
516 void
517 _cb_addAddresses(struct rfc5444_writer *writer) {
518   struct oonf_rfc5444_target *target;
519
520   struct nhdp_interface *interf;
521   struct nhdp_interface_addr *addr;
522   struct nhdp_naddr *naddr;
523
524   /* have already be checked for message TLVs, so they cannot be NULL */
525   target = oonf_rfc5444_get_target_from_writer(writer);
526   interf = nhdp_interface_get(target->interface->name);
527
528   /* transmit interface addresses first */
529   avl_for_each_element(nhdp_interface_get_address_tree(), addr, _global_node) {
530     if (addr->removed) {
531       continue;
532     }
533     if (netaddr_get_address_family(&addr->if_addr) == netaddr_get_address_family(&target->dst)) {
534       _add_localif_address(writer, &_nhdp_msgcontent_provider, interf, addr);
535     }
536   }
537
538   /* then transmit neighbor addresses */
539   avl_for_each_element(nhdp_db_get_naddr_tree(), naddr, _global_node) {
540     if (netaddr_get_address_family(&naddr->neigh_addr) == netaddr_get_address_family(&target->dst)) {
541       _add_link_address(writer, &_nhdp_msgcontent_provider, interf, naddr);
542     }
543   }
544 }