Home | History | Annotate | Line # | Download | only in dist
      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 (at) 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