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