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