xref: /netbsd-src/external/bsd/tcpdump/dist/print-olsr.c (revision 3117ece4fc4a4ca4489ba793710b60b0d26bab6c)
1 /*
2  * Copyright (c) 1998-2007 The TCPDUMP project
3  * Copyright (c) 2009  Florian Forster
4  *
5  * Redistribution and use in source and binary forms, with or without
6  * modification, are permitted provided that: (1) source code
7  * distributions retain the above copyright notice and this paragraph
8  * in its entirety, and (2) distributions including binary code include
9  * the above copyright notice and this paragraph in its entirety in
10  * the documentation or other materials provided with the distribution.
11  * THIS SOFTWARE IS PROVIDED ``AS IS'' AND
12  * WITHOUT ANY EXPRESS OR IMPLIED WARRANTIES, INCLUDING, WITHOUT
13  * LIMITATION, THE IMPLIED WARRANTIES OF MERCHANTABILITY AND FITNESS
14  * FOR A PARTICULAR PURPOSE.
15  *
16  * Original code by Hannes Gredler <hannes@gredler.at>
17  * IPv6 additions by Florian Forster <octo at verplant.org>
18  */
19 
20 /* \summary: Optimized Link State Routing Protocol (OLSR) printer */
21 
22 /* specification: RFC 3626 */
23 
24 #include <sys/cdefs.h>
25 #ifndef lint
26 __RCSID("$NetBSD: print-olsr.c,v 1.6 2024/09/02 16:15:32 christos Exp $");
27 #endif
28 
29 #include <config.h>
30 
31 #include "netdissect-stdinc.h"
32 
33 #include "netdissect.h"
34 #include "addrtoname.h"
35 #include "extract.h"
36 
37 /*
38  * RFC 3626 common header
39  *
40  *  0                   1                   2                   3
41  *  0 1 2 3 4 5 6 7 8 9 0 1 2 3 4 5 6 7 8 9 0 1 2 3 4 5 6 7 8 9 0 1
42  * +-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+
43  * |         Packet Length         |    Packet Sequence Number     |
44  * +-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+
45  * |  Message Type |     Vtime     |         Message Size          |
46  * +-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+
47  * |                      Originator Address                       |
48  * +-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+
49  * |  Time To Live |   Hop Count   |    Message Sequence Number    |
50  * +-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+
51  * |                                                               |
52  * :                            MESSAGE                            :
53  * |                                                               |
54  * +-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+
55  * |  Message Type |     Vtime     |         Message Size          |
56  * +-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+
57  * |                      Originator Address                       |
58  * +-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+
59  * |  Time To Live |   Hop Count   |    Message Sequence Number    |
60  * +-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+
61  * |                                                               |
62  * :                            MESSAGE                            :
63  * |                                                               |
64  * +-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+
65  * :                                                               :
66  */
67 
68 struct olsr_common {
69     nd_uint16_t packet_len;
70     nd_uint16_t packet_seq;
71 };
72 
73 #define OLSR_HELLO_MSG         1 /* rfc3626 */
74 #define OLSR_TC_MSG            2 /* rfc3626 */
75 #define OLSR_MID_MSG           3 /* rfc3626 */
76 #define OLSR_HNA_MSG           4 /* rfc3626 */
77 #define OLSR_POWERINFO_MSG   128
78 #define OLSR_NAMESERVICE_MSG 130
79 #define OLSR_HELLO_LQ_MSG    201 /* LQ extensions olsr.org */
80 #define OLSR_TC_LQ_MSG       202 /* LQ extensions olsr.org */
81 
82 static const struct tok olsr_msg_values[] = {
83     { OLSR_HELLO_MSG, "Hello" },
84     { OLSR_TC_MSG, "TC" },
85     { OLSR_MID_MSG, "MID" },
86     { OLSR_HNA_MSG, "HNA" },
87     { OLSR_POWERINFO_MSG, "Powerinfo" },
88     { OLSR_NAMESERVICE_MSG, "Nameservice" },
89     { OLSR_HELLO_LQ_MSG, "Hello-LQ" },
90     { OLSR_TC_LQ_MSG, "TC-LQ" },
91     { 0, NULL}
92 };
93 
94 struct olsr_msg4 {
95     nd_uint8_t  msg_type;
96     nd_uint8_t  vtime;
97     nd_uint16_t msg_len;
98     nd_ipv4     originator;
99     nd_uint8_t  ttl;
100     nd_uint8_t  hopcount;
101     nd_uint16_t msg_seq;
102 };
103 
104 struct olsr_msg6 {
105     nd_uint8_t  msg_type;
106     nd_uint8_t  vtime;
107     nd_uint16_t msg_len;
108     nd_ipv6     originator;
109     nd_uint8_t  ttl;
110     nd_uint8_t  hopcount;
111     nd_uint16_t msg_seq;
112 };
113 
114 struct olsr_hello {
115     nd_byte     res[2];
116     nd_uint8_t  htime;
117     nd_uint8_t  will;
118 };
119 
120 struct olsr_hello_link {
121     nd_uint8_t  link_code;
122     nd_byte     res;
123     nd_uint16_t len;
124 };
125 
126 struct olsr_tc {
127     nd_uint16_t ans_seq;
128     nd_byte     res[2];
129 };
130 
131 struct olsr_hna4 {
132     nd_ipv4 network;
133     nd_ipv4 mask;
134 };
135 
136 struct olsr_hna6 {
137     nd_ipv6 network;
138     nd_ipv6 mask;
139 };
140 
141 
142 /** gateway HNA flags */
143 enum gateway_hna_flags {
144   GW_HNA_FLAG_LINKSPEED   = 1 << 0,
145   GW_HNA_FLAG_IPV4        = 1 << 1,
146   GW_HNA_FLAG_IPV4_NAT    = 1 << 2,
147   GW_HNA_FLAG_IPV6        = 1 << 3,
148   GW_HNA_FLAG_IPV6PREFIX  = 1 << 4
149 };
150 
151 /** gateway HNA field byte offsets in the netmask field of the HNA */
152 enum gateway_hna_fields {
153   GW_HNA_PAD              = 0,
154   GW_HNA_FLAGS            = 1,
155   GW_HNA_UPLINK           = 2,
156   GW_HNA_DOWNLINK         = 3,
157   GW_HNA_V6PREFIXLEN      = 4,
158   GW_HNA_V6PREFIX         = 5
159 };
160 
161 
162 #define OLSR_EXTRACT_LINK_TYPE(link_code) (link_code & 0x3)
163 #define OLSR_EXTRACT_NEIGHBOR_TYPE(link_code) (link_code >> 2)
164 
165 static const struct tok olsr_link_type_values[] = {
166     { 0, "Unspecified" },
167     { 1, "Asymmetric" },
168     { 2, "Symmetric" },
169     { 3, "Lost" },
170     { 0, NULL}
171 };
172 
173 static const struct tok olsr_neighbor_type_values[] = {
174     { 0, "Not-Neighbor" },
175     { 1, "Symmetric" },
176     { 2, "Symmetric-MPR" },
177     { 0, NULL}
178 };
179 
180 struct olsr_lq_neighbor4 {
181     nd_ipv4     neighbor;
182     nd_uint8_t  link_quality;
183     nd_uint8_t  neighbor_link_quality;
184     nd_byte     res[2];
185 };
186 
187 struct olsr_lq_neighbor6 {
188     nd_ipv6     neighbor;
189     nd_uint8_t  link_quality;
190     nd_uint8_t  neighbor_link_quality;
191     nd_byte     res[2];
192 };
193 
194 #define MAX_SMARTGW_SPEED    320000000
195 
196 /**
197  * Convert an encoded 1 byte transport value (5 bits mantissa, 3 bits exponent)
198  * to an uplink/downlink speed value
199  *
200  * @param value the encoded 1 byte transport value
201  * @return the uplink/downlink speed value (in kbit/s)
202  */
203 static uint32_t deserialize_gw_speed(uint8_t value) {
204   uint32_t speed;
205   uint32_t exp;
206 
207   if (!value) {
208     return 0;
209   }
210 
211   if (value == UINT8_MAX) {
212     /* maximum value: also return maximum value */
213     return MAX_SMARTGW_SPEED;
214   }
215 
216   speed = (value >> 3) + 1;
217   exp = value & 7;
218 
219   while (exp != 0) {
220     speed *= 10;
221     exp--;
222   }
223   return speed;
224 }
225 
226 /*
227  * macro to convert the 8-bit mantissa/exponent to a double float
228  * taken from olsr.org.
229  */
230 #define VTIME_SCALE_FACTOR    0.0625
231 #define ME_TO_DOUBLE(me) \
232   (double)(VTIME_SCALE_FACTOR*(1+(double)(me>>4)/16)*(double)(1<<(me&0x0F)))
233 
234 /*
235  * print a neighbor list with LQ extensions.
236  */
237 static int
238 olsr_print_lq_neighbor4(netdissect_options *ndo,
239                         const u_char *msg_data, u_int hello_len)
240 {
241     const struct olsr_lq_neighbor4 *lq_neighbor;
242 
243     while (hello_len >= sizeof(struct olsr_lq_neighbor4)) {
244 
245         lq_neighbor = (const struct olsr_lq_neighbor4 *)msg_data;
246         ND_TCHECK_SIZE(lq_neighbor);
247 
248         ND_PRINT("\n\t      neighbor %s, link-quality %.2f%%"
249                ", neighbor-link-quality %.2f%%",
250                GET_IPADDR_STRING(lq_neighbor->neighbor),
251                ((double) GET_U_1(lq_neighbor->link_quality)/2.55),
252                ((double) GET_U_1(lq_neighbor->neighbor_link_quality)/2.55));
253 
254         msg_data += sizeof(struct olsr_lq_neighbor4);
255         hello_len -= sizeof(struct olsr_lq_neighbor4);
256     }
257     return (0);
258 trunc:
259     return -1;
260 }
261 
262 static int
263 olsr_print_lq_neighbor6(netdissect_options *ndo,
264                         const u_char *msg_data, u_int hello_len)
265 {
266     const struct olsr_lq_neighbor6 *lq_neighbor;
267 
268     while (hello_len >= sizeof(struct olsr_lq_neighbor6)) {
269 
270         lq_neighbor = (const struct olsr_lq_neighbor6 *)msg_data;
271         ND_TCHECK_SIZE(lq_neighbor);
272 
273         ND_PRINT("\n\t      neighbor %s, link-quality %.2f%%"
274                ", neighbor-link-quality %.2f%%",
275                GET_IP6ADDR_STRING(lq_neighbor->neighbor),
276                ((double) GET_U_1(lq_neighbor->link_quality)/2.55),
277                ((double) GET_U_1(lq_neighbor->neighbor_link_quality)/2.55));
278 
279         msg_data += sizeof(struct olsr_lq_neighbor6);
280         hello_len -= sizeof(struct olsr_lq_neighbor6);
281     }
282     return (0);
283 trunc:
284     return -1;
285 }
286 
287 /*
288  * print a neighbor list.
289  */
290 static int
291 olsr_print_neighbor(netdissect_options *ndo,
292                     const u_char *msg_data, u_int hello_len)
293 {
294     int neighbor;
295 
296     ND_PRINT("\n\t      neighbor\n\t\t");
297     neighbor = 1;
298 
299     while (hello_len >= sizeof(nd_ipv4)) {
300         /* print 4 neighbors per line */
301         ND_PRINT("%s%s", GET_IPADDR_STRING(msg_data),
302                neighbor % 4 == 0 ? "\n\t\t" : " ");
303 
304         msg_data += sizeof(nd_ipv4);
305         hello_len -= sizeof(nd_ipv4);
306     }
307     return (0);
308 }
309 
310 
311 void
312 olsr_print(netdissect_options *ndo,
313            const u_char *pptr, u_int length, int is_ipv6)
314 {
315     union {
316         const struct olsr_common *common;
317         const struct olsr_msg4 *msg4;
318         const struct olsr_msg6 *msg6;
319         const struct olsr_hello *hello;
320         const struct olsr_hello_link *hello_link;
321         const struct olsr_tc *tc;
322         const struct olsr_hna4 *hna;
323     } ptr;
324 
325     u_int msg_type, msg_len, msg_tlen, hello_len;
326     uint16_t name_entry_type, name_entry_len;
327     u_int name_entry_padding;
328     uint8_t link_type, neighbor_type;
329     const u_char *tptr, *msg_data;
330 
331     ndo->ndo_protocol = "olsr";
332     tptr = pptr;
333 
334     nd_print_protocol_caps(ndo);
335     ND_PRINT("v%u", (is_ipv6) ? 6 : 4);
336 
337     if (length < sizeof(struct olsr_common)) {
338         goto trunc;
339     }
340 
341     ND_TCHECK_LEN(tptr, sizeof(struct olsr_common));
342 
343     ptr.common = (const struct olsr_common *)tptr;
344     length = ND_MIN(length, GET_BE_U_2(ptr.common->packet_len));
345 
346     ND_PRINT(", seq 0x%04x, length %u",
347             GET_BE_U_2(ptr.common->packet_seq),
348             length);
349 
350     tptr += sizeof(struct olsr_common);
351 
352     /*
353      * In non-verbose mode, just print version.
354      */
355     if (ndo->ndo_vflag < 1) {
356         return;
357     }
358 
359     while (tptr < (pptr+length)) {
360         union
361         {
362             const struct olsr_msg4 *v4;
363             const struct olsr_msg6 *v6;
364         } msgptr;
365         int msg_len_valid = 0;
366 
367         if (is_ipv6) {
368             ND_TCHECK_LEN(tptr, sizeof(struct olsr_msg6));
369             msgptr.v6 = (const struct olsr_msg6 *) tptr;
370             msg_type = GET_U_1(msgptr.v6->msg_type);
371             msg_len = GET_BE_U_2(msgptr.v6->msg_len);
372             if ((msg_len >= sizeof (struct olsr_msg6))
373                     && (msg_len <= length))
374                 msg_len_valid = 1;
375 
376             /* infinite loop check */
377             if (msg_type == 0 || msg_len == 0) {
378                 return;
379             }
380 
381             ND_PRINT("\n\t%s Message (%#04x), originator %s, ttl %u, hop %u"
382                     "\n\t  vtime %.3fs, msg-seq 0x%04x, length %u%s",
383                     tok2str(olsr_msg_values, "Unknown", msg_type),
384                     msg_type, GET_IP6ADDR_STRING(msgptr.v6->originator),
385                     GET_U_1(msgptr.v6->ttl),
386                     GET_U_1(msgptr.v6->hopcount),
387                     ME_TO_DOUBLE(GET_U_1(msgptr.v6->vtime)),
388                     GET_BE_U_2(msgptr.v6->msg_seq),
389                     msg_len, (msg_len_valid == 0) ? " (invalid)" : "");
390             if (!msg_len_valid) {
391                 return;
392             }
393 
394             msg_tlen = msg_len - sizeof(struct olsr_msg6);
395             msg_data = tptr + sizeof(struct olsr_msg6);
396         } else {	/* (!is_ipv6) */
397             ND_TCHECK_LEN(tptr, sizeof(struct olsr_msg4));
398             msgptr.v4 = (const struct olsr_msg4 *) tptr;
399             msg_type = GET_U_1(msgptr.v4->msg_type);
400             msg_len = GET_BE_U_2(msgptr.v4->msg_len);
401             if ((msg_len >= sizeof (struct olsr_msg4))
402                     && (msg_len <= length))
403                 msg_len_valid = 1;
404 
405             /* infinite loop check */
406             if (msg_type == 0 || msg_len == 0) {
407                 return;
408             }
409 
410             ND_PRINT("\n\t%s Message (%#04x), originator %s, ttl %u, hop %u"
411                     "\n\t  vtime %.3fs, msg-seq 0x%04x, length %u%s",
412                     tok2str(olsr_msg_values, "Unknown", msg_type),
413                     msg_type, GET_IPADDR_STRING(msgptr.v4->originator),
414                     GET_U_1(msgptr.v4->ttl),
415                     GET_U_1(msgptr.v4->hopcount),
416                     ME_TO_DOUBLE(GET_U_1(msgptr.v4->vtime)),
417                     GET_BE_U_2(msgptr.v4->msg_seq),
418                     msg_len, (msg_len_valid == 0) ? " (invalid)" : "");
419             if (!msg_len_valid) {
420                 return;
421             }
422 
423             msg_tlen = msg_len - sizeof(struct olsr_msg4);
424             msg_data = tptr + sizeof(struct olsr_msg4);
425         }
426 
427         switch (msg_type) {
428         case OLSR_HELLO_MSG:
429         case OLSR_HELLO_LQ_MSG:
430             if (msg_tlen < sizeof(struct olsr_hello))
431                 goto trunc;
432             ND_TCHECK_LEN(msg_data, sizeof(struct olsr_hello));
433 
434             ptr.hello = (const struct olsr_hello *)msg_data;
435             ND_PRINT("\n\t  hello-time %.3fs, MPR willingness %u",
436                    ME_TO_DOUBLE(GET_U_1(ptr.hello->htime)),
437                    GET_U_1(ptr.hello->will));
438             msg_data += sizeof(struct olsr_hello);
439             msg_tlen -= sizeof(struct olsr_hello);
440 
441             while (msg_tlen >= sizeof(struct olsr_hello_link)) {
442                 int hello_len_valid = 0;
443 
444                 /*
445                  * link-type.
446                  */
447                 ND_TCHECK_LEN(msg_data, sizeof(struct olsr_hello_link));
448 
449                 ptr.hello_link = (const struct olsr_hello_link *)msg_data;
450 
451                 hello_len = GET_BE_U_2(ptr.hello_link->len);
452                 link_type = OLSR_EXTRACT_LINK_TYPE(GET_U_1(ptr.hello_link->link_code));
453                 neighbor_type = OLSR_EXTRACT_NEIGHBOR_TYPE(GET_U_1(ptr.hello_link->link_code));
454 
455                 if ((hello_len <= msg_tlen)
456                         && (hello_len >= sizeof(struct olsr_hello_link)))
457                     hello_len_valid = 1;
458 
459                 ND_PRINT("\n\t    link-type %s, neighbor-type %s, len %u%s",
460                        tok2str(olsr_link_type_values, "Unknown", link_type),
461                        tok2str(olsr_neighbor_type_values, "Unknown", neighbor_type),
462                        hello_len,
463                        (hello_len_valid == 0) ? " (invalid)" : "");
464 
465                 if (hello_len_valid == 0)
466                     break;
467 
468                 msg_data += sizeof(struct olsr_hello_link);
469                 msg_tlen -= sizeof(struct olsr_hello_link);
470                 hello_len -= sizeof(struct olsr_hello_link);
471 
472                 ND_TCHECK_LEN(msg_data, hello_len);
473                 if (msg_type == OLSR_HELLO_MSG) {
474                     if (olsr_print_neighbor(ndo, msg_data, hello_len) == -1)
475                         goto trunc;
476                 } else {
477                     if (is_ipv6) {
478                         if (olsr_print_lq_neighbor6(ndo, msg_data, hello_len) == -1)
479                             goto trunc;
480                     } else {
481                         if (olsr_print_lq_neighbor4(ndo, msg_data, hello_len) == -1)
482                             goto trunc;
483                     }
484                 }
485 
486                 msg_data += hello_len;
487                 msg_tlen -= hello_len;
488             }
489             break;
490 
491         case OLSR_TC_MSG:
492         case OLSR_TC_LQ_MSG:
493             if (msg_tlen < sizeof(struct olsr_tc))
494                 goto trunc;
495             ND_TCHECK_LEN(msg_data, sizeof(struct olsr_tc));
496 
497             ptr.tc = (const struct olsr_tc *)msg_data;
498             ND_PRINT("\n\t    advertised neighbor seq 0x%04x",
499                    GET_BE_U_2(ptr.tc->ans_seq));
500             msg_data += sizeof(struct olsr_tc);
501             msg_tlen -= sizeof(struct olsr_tc);
502 
503             if (msg_type == OLSR_TC_MSG) {
504                 if (olsr_print_neighbor(ndo, msg_data, msg_tlen) == -1)
505                     goto trunc;
506             } else {
507                 if (is_ipv6) {
508                     if (olsr_print_lq_neighbor6(ndo, msg_data, msg_tlen) == -1)
509                         goto trunc;
510                 } else {
511                     if (olsr_print_lq_neighbor4(ndo, msg_data, msg_tlen) == -1)
512                         goto trunc;
513                 }
514             }
515             break;
516 
517         case OLSR_MID_MSG:
518         {
519             u_int addr_size = (u_int)sizeof(nd_ipv4);
520 
521             if (is_ipv6)
522                 addr_size = (u_int)sizeof(nd_ipv6);
523 
524             while (msg_tlen >= addr_size) {
525                 ND_TCHECK_LEN(msg_data, addr_size);
526                 ND_PRINT("\n\t  interface address %s",
527                         is_ipv6 ? GET_IP6ADDR_STRING(msg_data) :
528                         GET_IPADDR_STRING(msg_data));
529 
530                 msg_data += addr_size;
531                 msg_tlen -= addr_size;
532             }
533             break;
534         }
535 
536         case OLSR_HNA_MSG:
537             if (is_ipv6) {
538                 int i = 0;
539 
540                 ND_PRINT("\n\t  Advertised networks (total %u)",
541                         (unsigned int) (msg_tlen / sizeof(struct olsr_hna6)));
542 
543                 while (msg_tlen >= sizeof(struct olsr_hna6)) {
544                     const struct olsr_hna6 *hna6;
545 
546                     ND_TCHECK_LEN(msg_data, sizeof(struct olsr_hna6));
547 
548                     hna6 = (const struct olsr_hna6 *)msg_data;
549 
550                     ND_PRINT("\n\t    #%i: %s/%u",
551                             i, GET_IP6ADDR_STRING(hna6->network),
552                             mask62plen (hna6->mask));
553 
554                     msg_data += sizeof(struct olsr_hna6);
555                     msg_tlen -= sizeof(struct olsr_hna6);
556                 }
557             } else {
558                 int col = 0;
559 
560                 ND_PRINT("\n\t  Advertised networks (total %u)",
561                         (unsigned int) (msg_tlen / sizeof(struct olsr_hna4)));
562 
563                 while (msg_tlen >= sizeof(struct olsr_hna4)) {
564                     ND_TCHECK_LEN(msg_data, sizeof(struct olsr_hna4));
565 
566                     ptr.hna = (const struct olsr_hna4 *)msg_data;
567 
568                     /* print 4 prefixes per line */
569                     if (!ptr.hna->network[0] && !ptr.hna->network[1] &&
570                         !ptr.hna->network[2] && !ptr.hna->network[3] &&
571                         !ptr.hna->mask[GW_HNA_PAD] &&
572                         ptr.hna->mask[GW_HNA_FLAGS]) {
573                             /* smart gateway */
574                             ND_PRINT("%sSmart-Gateway:%s%s%s%s%s %u/%u",
575                                 col == 0 ? "\n\t    " : ", ", /* indent */
576                                 /* sgw */
577                                 /* LINKSPEED */
578                                 (ptr.hna->mask[GW_HNA_FLAGS] &
579                                  GW_HNA_FLAG_LINKSPEED) ? " LINKSPEED" : "",
580                                 /* IPV4 */
581                                 (ptr.hna->mask[GW_HNA_FLAGS] &
582                                  GW_HNA_FLAG_IPV4) ? " IPV4" : "",
583                                 /* IPV4-NAT */
584                                 (ptr.hna->mask[GW_HNA_FLAGS] &
585                                  GW_HNA_FLAG_IPV4_NAT) ? " IPV4-NAT" : "",
586                                 /* IPV6 */
587                                 (ptr.hna->mask[GW_HNA_FLAGS] &
588                                  GW_HNA_FLAG_IPV6) ? " IPV6" : "",
589                                 /* IPv6PREFIX */
590                                 (ptr.hna->mask[GW_HNA_FLAGS] &
591                                  GW_HNA_FLAG_IPV6PREFIX) ? " IPv6-PREFIX" : "",
592                                 /* uplink */
593                                 (ptr.hna->mask[GW_HNA_FLAGS] &
594                                  GW_HNA_FLAG_LINKSPEED) ?
595                                  deserialize_gw_speed(ptr.hna->mask[GW_HNA_UPLINK]) : 0,
596                                 /* downlink */
597                                 (ptr.hna->mask[GW_HNA_FLAGS] &
598                                  GW_HNA_FLAG_LINKSPEED) ?
599                                  deserialize_gw_speed(ptr.hna->mask[GW_HNA_DOWNLINK]) : 0
600                                 );
601                     } else {
602                         /* normal route */
603                         ND_PRINT("%s%s/%u",
604                                 col == 0 ? "\n\t    " : ", ",
605                                 GET_IPADDR_STRING(ptr.hna->network),
606                                 mask2plen(GET_BE_U_4(ptr.hna->mask)));
607                     }
608 
609                     msg_data += sizeof(struct olsr_hna4);
610                     msg_tlen -= sizeof(struct olsr_hna4);
611 
612                     col = (col + 1) % 4;
613                 }
614             }
615             break;
616 
617         case OLSR_NAMESERVICE_MSG:
618         {
619             u_int name_entries;
620             u_int addr_size;
621             int name_entries_valid;
622             u_int i;
623 
624             if (msg_tlen < 4)
625                 goto trunc;
626 
627             name_entries = GET_BE_U_2(msg_data + 2);
628             addr_size = 4;
629             if (is_ipv6)
630                 addr_size = 16;
631 
632             name_entries_valid = 0;
633             if ((name_entries > 0)
634                     && ((name_entries * (4 + addr_size)) <= msg_tlen))
635                 name_entries_valid = 1;
636 
637             ND_PRINT("\n\t  Version %u, Entries %u%s",
638                    GET_BE_U_2(msg_data),
639                    name_entries, (name_entries_valid == 0) ? " (invalid)" : "");
640 
641             if (name_entries_valid == 0)
642                 break;
643 
644             msg_data += 4;
645             msg_tlen -= 4;
646 
647             for (i = 0; i < name_entries; i++) {
648                 int name_entry_len_valid = 0;
649 
650                 if (msg_tlen < 4)
651                     break;
652 
653                 name_entry_type = GET_BE_U_2(msg_data);
654                 name_entry_len = GET_BE_U_2(msg_data + 2);
655 
656                 msg_data += 4;
657                 msg_tlen -= 4;
658 
659                 if ((name_entry_len > 0) && ((addr_size + name_entry_len) <= msg_tlen))
660                     name_entry_len_valid = 1;
661 
662                 ND_PRINT("\n\t    #%u: type %#06x, length %u%s",
663                         (unsigned int) i, name_entry_type,
664                         name_entry_len, (name_entry_len_valid == 0) ? " (invalid)" : "");
665 
666                 if (name_entry_len_valid == 0)
667                     break;
668 
669                 /* 32-bit alignment */
670                 name_entry_padding = 0;
671                 if (name_entry_len%4 != 0)
672                     name_entry_padding = 4-(name_entry_len%4);
673 
674                 if (msg_tlen < addr_size + name_entry_len + name_entry_padding)
675                     goto trunc;
676 
677                 ND_TCHECK_LEN(msg_data,
678                               addr_size + name_entry_len + name_entry_padding);
679 
680                 if (is_ipv6)
681                     ND_PRINT(", address %s, name \"",
682                             GET_IP6ADDR_STRING(msg_data));
683                 else
684                     ND_PRINT(", address %s, name \"",
685                             GET_IPADDR_STRING(msg_data));
686                 (void)nd_printn(ndo, msg_data + addr_size, name_entry_len, NULL);
687                 ND_PRINT("\"");
688 
689                 msg_data += addr_size + name_entry_len + name_entry_padding;
690                 msg_tlen -= addr_size + name_entry_len + name_entry_padding;
691             } /* for (i = 0; i < name_entries; i++) */
692             break;
693         } /* case OLSR_NAMESERVICE_MSG */
694 
695             /*
696              * FIXME those are the defined messages that lack a decoder
697              * you are welcome to contribute code ;-)
698              */
699         case OLSR_POWERINFO_MSG:
700         default:
701             print_unknown_data(ndo, msg_data, "\n\t    ", msg_tlen);
702             break;
703         } /* switch (msg_type) */
704         tptr += msg_len;
705     } /* while (tptr < (pptr+length)) */
706 
707     return;
708 
709  trunc:
710     nd_print_trunc(ndo);
711 }
712