info: java: update workspace
[olsrd.git] / lib / txtinfo / src / olsrd_txtinfo.c
1 /*
2  * The olsr.org Optimized Link-State Routing daemon (olsrd)
3  *
4  * (c) by the OLSR project
5  *
6  * See our Git repository to find out who worked on this file
7  * and thus is a copyright holder on it.
8  *
9  * All rights reserved.
10  *
11  * Redistribution and use in source and binary forms, with or without
12  * modification, are permitted provided that the following conditions
13  * are met:
14  *
15  * * Redistributions of source code must retain the above copyright
16  *   notice, this list of conditions and the following disclaimer.
17  * * Redistributions in binary form must reproduce the above copyright
18  *   notice, this list of conditions and the following disclaimer in
19  *   the documentation and/or other materials provided with the
20  *   distribution.
21  * * Neither the name of olsr.org, olsrd nor the names of its
22  *   contributors may be used to endorse or promote products derived
23  *   from this software without specific prior written permission.
24  *
25  * THIS SOFTWARE IS PROVIDED BY THE COPYRIGHT HOLDERS AND CONTRIBUTORS
26  * "AS IS" AND ANY EXPRESS OR IMPLIED WARRANTIES, INCLUDING, BUT NOT
27  * LIMITED TO, THE IMPLIED WARRANTIES OF MERCHANTABILITY AND FITNESS
28  * FOR A PARTICULAR PURPOSE ARE DISCLAIMED. IN NO EVENT SHALL THE
29  * COPYRIGHT OWNER OR CONTRIBUTORS BE LIABLE FOR ANY DIRECT, INDIRECT,
30  * INCIDENTAL, SPECIAL, EXEMPLARY, OR CONSEQUENTIAL DAMAGES (INCLUDING,
31  * BUT NOT LIMITED TO, PROCUREMENT OF SUBSTITUTE GOODS OR SERVICES;
32  * LOSS OF USE, DATA, OR PROFITS; OR BUSINESS INTERRUPTION) HOWEVER
33  * CAUSED AND ON ANY THEORY OF LIABILITY, WHETHER IN CONTRACT, STRICT
34  * LIABILITY, OR TORT (INCLUDING NEGLIGENCE OR OTHERWISE) ARISING IN
35  * ANY WAY OUT OF THE USE OF THIS SOFTWARE, EVEN IF ADVISED OF THE
36  * POSSIBILITY OF SUCH DAMAGE.
37  *
38  * Visit http://www.olsr.org for more information.
39  *
40  * If you find this software useful feel free to make a donation
41  * to the project. For more information see the website or contact
42  * the copyright holders.
43  *
44  */
45
46 #include "olsrd_txtinfo.h"
47
48 #include <unistd.h>
49
50 #include "ipcalc.h"
51 #include "builddata.h"
52 #include "neighbor_table.h"
53 #include "mpr_selector_set.h"
54 #include "mid_set.h"
55 #include "routing_table.h"
56 #include "lq_plugin.h"
57 #include "gateway.h"
58 #include "gateway_costs.h"
59 #include "olsrd_plugin.h"
60 #include "info/info_types.h"
61 #include "info/http_headers.h"
62 #include "gateway_default_handler.h"
63
64 unsigned long long get_supported_commands_mask(void) {
65   return (SIW_ALL | SIW_OLSRD_CONF) & ~(SIW_CONFIG | SIW_PLUGINS);
66 }
67
68 bool isCommand(const char *str, unsigned long long siw) {
69   const char * cmd;
70   switch (siw) {
71     case SIW_OLSRD_CONF:
72       cmd = "/con";
73       break;
74
75     case SIW_ALL:
76       cmd = "/all";
77       break;
78
79     case SIW_RUNTIME_ALL:
80       cmd = "/runtime";
81       break;
82
83     case SIW_STARTUP_ALL:
84       cmd = "/startup";
85       break;
86
87     case SIW_NEIGHBORS:
88       cmd = "/nei";
89       break;
90
91     case SIW_LINKS:
92       cmd = "/lin";
93       break;
94
95     case SIW_ROUTES:
96       cmd = "/rou";
97       break;
98
99     case SIW_HNA:
100       cmd = "/hna";
101       break;
102
103     case SIW_MID:
104       cmd = "/mid";
105       break;
106
107     case SIW_TOPOLOGY:
108       cmd = "/top";
109       break;
110
111     case SIW_GATEWAYS:
112       cmd = "/gat";
113       break;
114
115     case SIW_INTERFACES:
116       cmd = "/int";
117       break;
118
119     case SIW_2HOP:
120       cmd = "/2ho";
121       break;
122
123     case SIW_SGW:
124       cmd = "/sgw";
125       break;
126
127     case SIW_VERSION:
128       cmd = "/ver";
129       break;
130
131     case SIW_NEIGHBORS_FREIFUNK:
132       cmd = "/neighbours";
133       break;
134
135     default:
136       return false;
137   }
138
139   return !strcmp(str, cmd);
140 }
141
142 void output_error(struct autobuf *abuf, unsigned int status, const char * req __attribute__((unused)), bool http_headers) {
143   if (http_headers || (status == INFO_HTTP_OK)) {
144     return;
145   }
146
147   /* !http_headers && !INFO_HTTP_OK */
148
149   if (status == INFO_HTTP_NOCONTENT) {
150     /* wget can't handle output of zero length */
151     abuf_puts(abuf, "\n");
152   } else {
153     abuf_appendf(abuf, "error: %s\n", httpStatusToReply(status));
154   }
155 }
156
157 static void ipc_print_neighbors_internal(struct autobuf *abuf, bool list_2hop) {
158   struct ipaddr_str neighAddrBuf;
159   struct neighbor_entry *neigh;
160   struct neighbor_2_list_entry *list_2;
161   int thop_cnt;
162
163   const char * field;
164   if (list_2hop) {
165     field = "(2-hop address)+";
166   } else {
167     field = "2-hop count";
168   }
169
170   abuf_puts(abuf, "Table: Neighbors\n");
171   abuf_appendf(abuf, "IP address\tSYM\tMPR\tMPRS\tWill.\t%s\n", field);
172
173   /* Neighbors */
174   OLSR_FOR_ALL_NBR_ENTRIES(neigh) {
175     abuf_appendf(abuf, "%s\t%s\t%s\t%s\t%d",
176       olsr_ip_to_string(&neighAddrBuf, &neigh->neighbor_main_addr),
177       (neigh->status == SYM) ? "YES" : "NO",
178       neigh->is_mpr ? "YES" : "NO",
179       olsr_lookup_mprs_set(&neigh->neighbor_main_addr) ? "YES" : "NO",
180       neigh->willingness);
181     thop_cnt = 0;
182
183     for (list_2 = neigh->neighbor_2_list.next; list_2 != &neigh->neighbor_2_list; list_2 = list_2->next) {
184       if (list_2hop) {
185         if (list_2->neighbor_2) {
186           abuf_appendf(abuf, "\t%s", olsr_ip_to_string(&neighAddrBuf, &list_2->neighbor_2->neighbor_2_addr));
187         }
188       } else {
189         thop_cnt++;
190       }
191     }
192
193     if (!list_2hop) {
194       abuf_appendf(abuf, "\t%d", thop_cnt);
195     }
196     abuf_puts(abuf, "\n");
197   } OLSR_FOR_ALL_NBR_ENTRIES_END(neigh);
198   abuf_puts(abuf, "\n");
199 }
200
201 void ipc_print_neighbors(struct autobuf *abuf) {
202   ipc_print_neighbors_internal(abuf, false);
203 }
204
205 void ipc_print_links(struct autobuf *abuf) {
206   struct link_entry *my_link;
207
208   const char * field;
209   if (vtime) {
210     field = "VTime";
211   } else {
212     field = "Hyst.";
213   }
214
215   abuf_puts(abuf, "Table: Links\n");
216   abuf_appendf(abuf, "Local IP\tRemote IP\t%s\tLQ\tNLQ\tCost\n", field);
217
218   /* Link set */
219   OLSR_FOR_ALL_LINK_ENTRIES(my_link) {
220     struct ipaddr_str localAddr;
221     struct ipaddr_str remoteAddr;
222     struct lqtextbuffer lqbuffer;
223     struct lqtextbuffer costbuffer;
224     unsigned int diffI = 0;
225     unsigned int diffF = 0;
226
227     if (vtime) {
228       unsigned int diff = my_link->link_timer ? (unsigned int) (my_link->link_timer->timer_clock - now_times) : 0;
229       diffI = diff / 1000;
230       diffF = diff % 1000;
231     }
232
233     abuf_appendf(abuf, "%s\t%s\t%u.%03u\t%s\t%s\n",
234       olsr_ip_to_string(&localAddr, &my_link->local_iface_addr),
235       olsr_ip_to_string(&remoteAddr, &my_link->neighbor_iface_addr),
236       diffI,
237       diffF,
238       get_link_entry_text(my_link, '\t', &lqbuffer),
239       get_linkcost_text(my_link->linkcost, false, &costbuffer));
240   } OLSR_FOR_ALL_LINK_ENTRIES_END(my_link);
241   abuf_puts(abuf, "\n");
242 }
243
244 void ipc_print_routes(struct autobuf *abuf) {
245   struct rt_entry *rt;
246
247   abuf_puts(abuf, "Table: Routes\n");
248   abuf_puts(abuf, "Destination\tGateway IP\tMetric\tETX\tInterface\n");
249
250   /* Walk the route table */
251   OLSR_FOR_ALL_RT_ENTRIES(rt) {
252     struct ipaddr_str dstAddr;
253     struct ipaddr_str nexthopAddr;
254     struct lqtextbuffer costbuffer;
255
256     if (rt->rt_best) {
257       abuf_appendf(abuf, "%s/%d\t%s\t%d\t%s\t%s\t\n",
258         olsr_ip_to_string(&dstAddr, &rt->rt_dst.prefix),
259         rt->rt_dst.prefix_len,
260         olsr_ip_to_string(&nexthopAddr, &rt->rt_best->rtp_nexthop.gateway),
261         rt->rt_best->rtp_metric.hops,
262         get_linkcost_text(rt->rt_best->rtp_metric.cost, true, &costbuffer),
263         if_ifwithindex_name(rt->rt_best->rtp_nexthop.iif_index));
264     }
265   } OLSR_FOR_ALL_RT_ENTRIES_END(rt);
266   abuf_puts(abuf, "\n");
267 }
268
269 void ipc_print_topology(struct autobuf *abuf) {
270   struct tc_entry *tc;
271
272   const char * field;
273   if (vtime) {
274     field = "\tVTime";
275   } else {
276     field = "";
277   }
278
279   abuf_puts(abuf, "Table: Topology\n");
280   abuf_appendf(abuf, "Dest. IP\tLast hop IP\tLQ\tNLQ\tCost%s\n", field);
281
282   /* Topology */
283   OLSR_FOR_ALL_TC_ENTRIES(tc) {
284     struct tc_edge_entry *tc_edge;
285     OLSR_FOR_ALL_TC_EDGE_ENTRIES(tc, tc_edge) {
286       if (tc_edge->edge_inv) {
287         struct ipaddr_str dstAddr;
288         struct ipaddr_str lastHopAddr;
289         struct lqtextbuffer lqbuffer;
290         struct lqtextbuffer costbuffer;
291
292         abuf_appendf(abuf, "%s\t%s\t%s\t%s",
293           olsr_ip_to_string(&dstAddr, &tc_edge->T_dest_addr),
294           olsr_ip_to_string(&lastHopAddr, &tc->addr),
295           get_tc_edge_entry_text(tc_edge, '\t', &lqbuffer),
296           get_linkcost_text(tc_edge->cost, false, &costbuffer));
297
298         if (vtime) {
299           unsigned int diff = (unsigned int) (tc->validity_timer ? (tc->validity_timer->timer_clock - now_times) : 0);
300           abuf_appendf(abuf, "\t%u.%03u", diff / 1000, diff % 1000);
301         }
302
303         abuf_puts(abuf, "\n");
304       }
305     } OLSR_FOR_ALL_TC_EDGE_ENTRIES_END(tc, tc_edge);
306   } OLSR_FOR_ALL_TC_ENTRIES_END(tc);
307   abuf_puts(abuf, "\n");
308 }
309
310 void ipc_print_hna(struct autobuf *abuf) {
311   struct ip_prefix_list *hna;
312   struct hna_entry *tmp_hna;
313   struct ipaddr_str prefixbuf;
314   struct ipaddr_str gwaddrbuf;
315
316   const char * field;
317   if (vtime) {
318     field = "\tVTime";
319   } else {
320     field = "";
321   }
322
323   abuf_puts(abuf, "Table: HNA\n");
324   abuf_appendf(abuf, "Destination\tGateway%s\n", field);
325
326   /* Announced HNA entries */
327   for (hna = olsr_cnf->hna_entries; hna != NULL ; hna = hna->next) {
328     abuf_appendf(abuf, "%s/%d\t%s",
329       olsr_ip_to_string(&prefixbuf, &hna->net.prefix),
330       hna->net.prefix_len,
331       olsr_ip_to_string(&gwaddrbuf, &olsr_cnf->main_addr));
332
333       if (vtime) {
334         abuf_appendf(abuf, "\t%u.%03u", 0, 0);
335       }
336       abuf_puts(abuf, "\n");
337   }
338
339   /* HNA entries */
340   OLSR_FOR_ALL_HNA_ENTRIES(tmp_hna) {
341     struct hna_net *tmp_net;
342
343     /* Check all networks */
344     for (tmp_net = tmp_hna->networks.next; tmp_net != &tmp_hna->networks; tmp_net = tmp_net->next) {
345       abuf_appendf(abuf, "%s/%d\t%s",
346         olsr_ip_to_string(&prefixbuf, &tmp_net->hna_prefix.prefix),
347         tmp_net->hna_prefix.prefix_len,
348         olsr_ip_to_string(&gwaddrbuf, &tmp_hna->A_gateway_addr));
349
350       if (vtime) {
351         unsigned int diff = tmp_net->hna_net_timer ? (unsigned int) (tmp_net->hna_net_timer->timer_clock - now_times) : 0;
352         abuf_appendf(abuf, "\t%u.%03u", diff / 1000, diff % 1000);
353       }
354       abuf_puts(abuf, "\n");
355     }
356   } OLSR_FOR_ALL_HNA_ENTRIES_END(tmp_hna);
357   abuf_puts(abuf, "\n");
358 }
359
360 void ipc_print_mid(struct autobuf *abuf) {
361   int idx;
362
363   const char * field;
364   if (vtime) {
365     field = ":VTime";
366   } else {
367     field = "";
368   }
369
370   abuf_puts(abuf, "Table: MID\n");
371   abuf_appendf(abuf, "IP address\t(Alias%s)+\n", field);
372
373   /* MID */
374   for (idx = 0; idx < HASHSIZE; idx++) {
375     struct mid_entry *entry = mid_set[idx].next;
376
377     while (entry && (entry != &mid_set[idx])) {
378       struct mid_address *alias = entry->aliases;
379       struct ipaddr_str ipAddr;
380
381       abuf_puts(abuf, olsr_ip_to_string(&ipAddr, &entry->main_addr));
382       abuf_puts(abuf, "\t");
383
384       while (alias) {
385         struct ipaddr_str buf2;
386
387         abuf_appendf(abuf, "\t%s", olsr_ip_to_string(&buf2, &alias->alias));
388
389         if (vtime) {
390           unsigned int diff = (unsigned int) (alias->vtime - now_times);
391           abuf_appendf(abuf, ":%u.%03u", diff / 1000, diff % 1000);
392         }
393
394         alias = alias->next_alias;
395       }
396       entry = entry->next;
397       abuf_puts(abuf, "\n");
398     }
399   }
400   abuf_puts(abuf, "\n");
401 }
402
403 void ipc_print_gateways(struct autobuf *abuf) {
404 #ifndef __linux__
405   abuf_puts(abuf, "error: Gateway mode is only supported on Linux\n");
406 #else /* __linux__ */
407   static const char *fmth = "%-6s %-45s %-15s %-6s %-9s %-9s %-7s %-4s %s\n";
408   static const char *fmtv = "%c%c%-4s %-45s %-15s %-6u %-9u %-9u %-7s %-4s %s\n";
409
410   static const char IPV4[] = "ipv4";
411   static const char IPV4_NAT[] = "ipv4(n)";
412   static const char IPV6[] = "ipv6";
413   static const char NONE[] = "-";
414
415   struct gateway_entry *gw;
416   struct gateway_entry *current_gw_4 = olsr_get_inet_gateway(false);
417   struct gateway_entry *current_gw_6 = olsr_get_inet_gateway(true);
418
419   abuf_puts(abuf, "Table: Gateways\n");
420   abuf_appendf(abuf, fmth, "Status", "Gateway IP", "ETX", "Hopcnt", "Uplink", "Downlnk", "IPv4", "IPv6", "Prefix");
421
422   OLSR_FOR_ALL_GATEWAY_ENTRIES(gw) {
423     char v4, v6;
424     const char *v4type, *v6type;
425     struct ipaddr_str originatorbuf;
426     struct lqtextbuffer lqbuf;
427     struct tc_entry *tc = olsr_lookup_tc_entry(&gw->originator);
428
429     if (current_gw_4 && (gw == current_gw_4)) {
430       v4 = 's';
431     } else if (isGwSelectable(gw, false)) {
432       v4 = 'u';
433     } else {
434       v4 = '-';
435     }
436
437     if (current_gw_6 && (gw == current_gw_6)) {
438       v6 = 's';
439     } else if (isGwSelectable(gw, true)) {
440       v6 = 'u';
441     } else {
442       v6 = '-';
443     }
444
445     if (gw->ipv4) {
446       v4type = gw->ipv4nat ? IPV4_NAT : IPV4;
447     } else {
448       v4type = NONE;
449     }
450     if (gw->ipv6) {
451       v6type = IPV6;
452     } else {
453       v6type = NONE;
454     }
455
456     abuf_appendf(abuf, fmtv, //
457       v4, //
458       v6, //
459       "", //
460       olsr_ip_to_string(&originatorbuf, &gw->originator), //
461       get_linkcost_text(!tc ? ROUTE_COST_BROKEN : tc->path_cost, true, &lqbuf), //
462       !tc ? 0 : tc->hops, //
463       gw->uplink, //
464       gw->downlink, //
465       v4type, //
466       v6type, //
467       !gw->external_prefix.prefix_len ? NONE : olsr_ip_prefix_to_string(&gw->external_prefix));
468   } OLSR_FOR_ALL_GATEWAY_ENTRIES_END (gw)
469 #endif /* __linux__ */
470   abuf_puts(abuf, "\n");
471 }
472
473 #ifdef __linux__
474
475 /** interface names for smart gateway tunnel interfaces, IPv4 */
476 extern struct interfaceName * sgwTunnel4InterfaceNames;
477
478 /** interface names for smart gateway tunnel interfaces, IPv6 */
479 extern struct interfaceName * sgwTunnel6InterfaceNames;
480
481 /**
482  * Construct the sgw table for a given ip version
483  *
484  * @param abuf the string buffer
485  * @param ipv6 true for IPv6, false for IPv4
486  * @param fmtv the format for printing
487  */
488 static void sgw_ipvx(struct autobuf *abuf, bool ipv6, const char * fmth, const char * fmtv) {
489   struct interfaceName * sgwTunnelInterfaceNames = !ipv6 ? sgwTunnel4InterfaceNames : sgwTunnel6InterfaceNames;
490   struct gwtextbuffer gwbuf;
491
492   abuf_appendf(abuf, "Table: Smart Gateway IPv%d\n", ipv6 ? 6 : 4);
493   abuf_appendf(abuf, fmth, " ", "Originator", "Prefix", "Uplink", "Downlink", "PathCost", "IPv4", "IPv4-NAT", "IPv6", "Tunnel-Name", "Destination", "Cost");
494
495   if (olsr_cnf->smart_gw_active && sgwTunnelInterfaceNames) {
496     struct gateway_entry * current_gw = olsr_get_inet_gateway(ipv6);
497
498     int i;
499     for (i = 0; i < olsr_cnf->smart_gw_use_count; i++) {
500       struct interfaceName * node = &sgwTunnelInterfaceNames[i];
501       struct gateway_entry * gw = node->gw;
502
503       if (gw) {
504         struct lqtextbuffer lqbuf;
505         struct tc_entry* tc = olsr_lookup_tc_entry(&gw->originator);
506
507         struct ipaddr_str originator;
508         struct ipaddr_str prefix;
509         char prefixAndMask[INET6_ADDRSTRLEN * 2];
510
511         olsr_ip_to_string(&originator, &gw->originator);
512         olsr_ip_to_string(&prefix, &gw->external_prefix.prefix);
513
514         if (!ipv6) {
515           union olsr_ip_addr netmask = { { 0 } };
516           struct ipaddr_str prefixMask;
517           prefix_to_netmask((uint8_t *) &netmask, sizeof(netmask.v4), gw->external_prefix.prefix_len);
518           olsr_ip_to_string(&prefixMask, &netmask);
519           snprintf(prefixAndMask, sizeof(prefixAndMask), "%s/%s", prefix.buf, prefixMask.buf);
520         } else {
521           snprintf(prefixAndMask, sizeof(prefixAndMask), "%s/%d", prefix.buf, gw->external_prefix.prefix_len);
522         }
523
524         abuf_appendf(abuf, fmtv, //
525           (current_gw && (current_gw == gw)) ? "*" : " ", // selected
526           originator.buf, // Originator
527           prefixAndMask, // 4: IP/Mask, 6: IP/Length
528           gw->uplink, // Uplink
529           gw->downlink, // Downlink
530           get_linkcost_text(!tc ? ROUTE_COST_BROKEN : tc->path_cost, true, &lqbuf), // PathCost
531           gw->ipv4 ? "Y" : "N", // IPv4
532           gw->ipv4nat ? "Y" : "N", // IPv4-NAT
533           gw->ipv6 ? "Y" : "N", // IPv6
534           node->name, // Tunnel-Name
535           originator.buf, // Destination
536           get_gwcost_text(gw->path_cost, &gwbuf) // Cost
537           );
538       }
539     }
540   }
541   abuf_puts(abuf, "\n");
542 }
543 #endif /* __linux__ */
544
545 void ipc_print_sgw(struct autobuf *abuf) {
546 #ifndef __linux__
547   abuf_puts(abuf, "error: Gateway mode is only supported on Linux\n");
548 #else
549
550   static const char * fmth4 = "%s%-15s %-31s %-9s %-9s %-10s %-4s %-8s %-4s %-15s %-15s %s\n";
551   static const char * fmtv4 = "%s%-15s %-31s %-9u %-9u %-10s %-4s %-8s %-4s %-15s %-15s %s\n";
552 #if 0
553   static const char * fmth6 = "%s%-45s %-49s %-9s %-9s %-10s %-4s %-8s %-4s %-15s %-45s %s\n";
554   static const char * fmtv6 = "%s%-45s %-49s %-9u %-9u %-10s %-4s %-8s %-4s %-15s %-45s %s\n";
555 #endif
556
557   sgw_ipvx(abuf, false, fmth4, fmtv4);
558 #if 0
559   sgw_ipvx(abuf, true, fmth6, fmtv6);
560 #endif
561 #endif /* __linux__ */
562 }
563
564 void ipc_print_version(struct autobuf *abuf) {
565   abuf_appendf(abuf, "Version: %s\n", olsrd_version);
566   abuf_puts(abuf, "\n");
567 }
568
569 void ipc_print_olsrd_conf(struct autobuf *abuf) {
570   olsrd_write_cnf_autobuf(abuf, olsr_cnf);
571 }
572
573 void ipc_print_interfaces(struct autobuf *abuf) {
574   const struct olsr_if *ifs;
575
576   abuf_puts(abuf, "Table: Interfaces\n");
577   abuf_puts(abuf, "Name\tState\tMTU\tWLAN\tSrc-Adress\tMask\tDst-Adress\n");
578
579   for (ifs = olsr_cnf->interfaces; ifs != NULL ; ifs = ifs->next) {
580     const struct interface_olsr * const rifs = ifs->interf;
581
582     abuf_appendf(abuf, "%s", ifs->name);
583
584     if (!rifs) {
585       abuf_puts(abuf, "\tDOWN\n");
586       continue;
587     }
588     abuf_appendf(abuf, "\tUP\t%d\t%s",
589         rifs->int_mtu,
590         rifs->is_wireless ? "Yes" : "No");
591
592     {
593       struct ipaddr_str addrbuf;
594       struct ipaddr_str maskbuf;
595       struct ipaddr_str bcastbuf;
596
597       if (olsr_cnf->ip_version == AF_INET) {
598         abuf_appendf(abuf, "\t%s\t%s\t%s\n",
599             ip4_to_string(&addrbuf, rifs->int_addr.sin_addr),
600             ip4_to_string(&maskbuf, rifs->int_netmask.sin_addr),
601             ip4_to_string(&bcastbuf, rifs->int_broadaddr.sin_addr));
602       } else {
603         abuf_appendf(abuf, "\t%s\t\t%s\n",
604             ip6_to_string(&addrbuf, &rifs->int6_addr.sin6_addr),
605             ip6_to_string(&bcastbuf, &rifs->int6_multaddr.sin6_addr));
606       }
607     }
608   }
609   abuf_puts(abuf, "\n");
610 }
611
612 void ipc_print_twohop(struct autobuf *abuf) {
613   ipc_print_neighbors_internal(abuf, true);
614 }