xref: /freebsd/contrib/tcpdump/print-babel.c (revision 8aac90f18aef7c9eea906c3ff9a001ca7b94f375)
1 /*
2  * Copyright (c) 2007-2011 Grégoire Henry, Juliusz Chroboczek
3  *
4  * Redistribution and use in source and binary forms, with or without
5  * modification, are permitted provided that the following conditions
6  * are met:
7  * 1. Redistributions of source code must retain the above copyright
8  *    notice, this list of conditions and the following disclaimer.
9  * 2. Redistributions in binary form must reproduce the above copyright
10  *    notice, this list of conditions and the following disclaimer in the
11  *    documentation and/or other materials provided with the distribution.
12  * 3. Neither the name of the project nor the names of its contributors
13  *    may be used to endorse or promote products derived from this software
14  *    without specific prior written permission.
15  *
16  * THIS SOFTWARE IS PROVIDED BY THE PROJECT AND CONTRIBUTORS ``AS IS'' AND
17  * ANY EXPRESS OR IMPLIED WARRANTIES, INCLUDING, BUT NOT LIMITED TO, THE
18  * IMPLIED WARRANTIES OF MERCHANTABILITY AND FITNESS FOR A PARTICULAR PURPOSE
19  * ARE DISCLAIMED.  IN NO EVENT SHALL THE PROJECT OR CONTRIBUTORS BE LIABLE
20  * FOR ANY DIRECT, INDIRECT, INCIDENTAL, SPECIAL, EXEMPLARY, OR CONSEQUENTIAL
21  * DAMAGES (INCLUDING, BUT NOT LIMITED TO, PROCUREMENT OF SUBSTITUTE GOODS
22  * OR SERVICES; LOSS OF USE, DATA, OR PROFITS; OR BUSINESS INTERRUPTION)
23  * HOWEVER CAUSED AND ON ANY THEORY OF LIABILITY, WHETHER IN CONTRACT, STRICT
24  * LIABILITY, OR TORT (INCLUDING NEGLIGENCE OR OTHERWISE) ARISING IN ANY WAY
25  * OUT OF THE USE OF THIS SOFTWARE, EVEN IF ADVISED OF THE POSSIBILITY OF
26  * SUCH DAMAGE.
27  */
28 
29 /* \summary: Babel Routing Protocol printer */
30 /* Specifications:
31  *
32  * RFC 6126
33  * RFC 7298
34  * RFC 7557
35  * draft-ietf-babel-rfc6126bis-17
36  * draft-ietf-babel-hmac-10
37  * draft-ietf-babel-source-specific-0
38  */
39 
40 #ifdef HAVE_CONFIG_H
41 #include <config.h>
42 #endif
43 
44 #include "netdissect-stdinc.h"
45 
46 #include <stdio.h>
47 #include <string.h>
48 
49 #include "netdissect.h"
50 #include "addrtoname.h"
51 #include "extract.h"
52 
53 static void babel_print_v2(netdissect_options *, const u_char *cp, u_int length);
54 
55 void
56 babel_print(netdissect_options *ndo,
57             const u_char *cp, u_int length)
58 {
59     ndo->ndo_protocol = "babel";
60     ND_PRINT("babel");
61 
62     ND_TCHECK_4(cp);
63 
64     if(GET_U_1(cp) != 42) {
65         ND_PRINT(" invalid header");
66         return;
67     } else {
68         ND_PRINT(" %u", GET_U_1(cp + 1));
69     }
70 
71     switch(GET_U_1(cp + 1)) {
72     case 2:
73         babel_print_v2(ndo, cp, length);
74         break;
75     default:
76         ND_PRINT(" unknown version");
77         break;
78     }
79 
80     return;
81 
82  trunc:
83     nd_print_trunc(ndo);
84 }
85 
86 /* TLVs */
87 #define MESSAGE_PAD1 0
88 #define MESSAGE_PADN 1
89 #define MESSAGE_ACK_REQ 2
90 #define MESSAGE_ACK 3
91 #define MESSAGE_HELLO 4
92 #define MESSAGE_IHU 5
93 #define MESSAGE_ROUTER_ID 6
94 #define MESSAGE_NH 7
95 #define MESSAGE_UPDATE 8
96 #define MESSAGE_ROUTE_REQUEST 9
97 #define MESSAGE_SEQNO_REQUEST 10
98 #define MESSAGE_TSPC 11
99 #define MESSAGE_HMAC 12
100 #define MESSAGE_UPDATE_SRC_SPECIFIC 13 /* last appearance in draft-boutier-babel-source-specific-01 */
101 #define MESSAGE_REQUEST_SRC_SPECIFIC 14 /* idem */
102 #define MESSAGE_MH_REQUEST_SRC_SPECIFIC 15 /* idem */
103 #define MESSAGE_MAC 16
104 #define MESSAGE_PC 17
105 #define MESSAGE_CHALLENGE_REQUEST 18
106 #define MESSAGE_CHALLENGE_REPLY 19
107 
108 /* sub-TLVs */
109 #define MESSAGE_SUB_PAD1 0
110 #define MESSAGE_SUB_PADN 1
111 #define MESSAGE_SUB_DIVERSITY 2
112 #define MESSAGE_SUB_TIMESTAMP 3
113 
114 /* "Mandatory" bit in sub-TLV types */
115 #define MANDATORY_MASK 0x80
116 
117 /* Flags for the Hello TLV */
118 #define UNICAST_MASK 0x8000
119 
120 /* Diversity sub-TLV channel codes */
121 static const struct tok diversity_str[] = {
122     { 0,   "reserved" },
123     { 255, "all"      },
124     { 0, NULL }
125 };
126 
127 static const char *
128 format_id(netdissect_options *ndo, const u_char *id)
129 {
130     static char buf[25];
131     snprintf(buf, 25, "%02x:%02x:%02x:%02x:%02x:%02x:%02x:%02x",
132              GET_U_1(id), GET_U_1(id + 1), GET_U_1(id + 2),
133              GET_U_1(id + 3), GET_U_1(id + 4), GET_U_1(id + 5),
134              GET_U_1(id + 6), GET_U_1(id + 7));
135     buf[24] = '\0';
136     return buf;
137 }
138 
139 static const unsigned char v4prefix[16] =
140     {0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0xFF, 0xFF, 0, 0, 0, 0 };
141 
142 static const char *
143 format_prefix(netdissect_options *ndo, const u_char *prefix, unsigned char plen)
144 {
145     static char buf[50];
146 
147     /*
148      * prefix points to a buffer on the stack into which the prefix has
149      * been placed, so we can't use GET_IPADDR_STRING() or
150      * GET_IP6ADDR_STRING() on it.
151      */
152     if(plen >= 96 && memcmp(prefix, v4prefix, 12) == 0)
153         snprintf(buf, 50, "%s/%u", ipaddr_string(ndo, prefix + 12), plen - 96);
154     else
155         snprintf(buf, 50, "%s/%u", ip6addr_string(ndo, prefix), plen);
156     buf[49] = '\0';
157     return buf;
158 }
159 
160 static const char *
161 format_address(netdissect_options *ndo, const u_char *prefix)
162 {
163     /*
164      * prefix points to a buffer on the stack into which the prefix has
165      * been placed, so we can't use GET_IPADDR_STRING() or
166      * GET_IP6ADDR_STRING() on it.
167      */
168     if(memcmp(prefix, v4prefix, 12) == 0)
169         return ipaddr_string(ndo, prefix + 12);
170     else
171         return ip6addr_string(ndo, prefix);
172 }
173 
174 static const char *
175 format_interval(const uint16_t i)
176 {
177     static char buf[sizeof("000.00s")];
178 
179     if (i == 0)
180         return "0.0s (bogus)";
181     snprintf(buf, sizeof(buf), "%u.%02us", i / 100, i % 100);
182     return buf;
183 }
184 
185 static const char *
186 format_interval_update(const uint16_t i)
187 {
188     return i == 0xFFFF ? "infinity" : format_interval(i);
189 }
190 
191 static const char *
192 format_timestamp(const uint32_t i)
193 {
194     static char buf[sizeof("0000.000000s")];
195     snprintf(buf, sizeof(buf), "%u.%06us", i / 1000000, i % 1000000);
196     return buf;
197 }
198 
199 /* Return number of octets consumed from the input buffer (not the prefix length
200  * in bytes), or -1 for encoding error. */
201 static int
202 network_prefix(int ae, int plen, unsigned int omitted,
203                const unsigned char *p, const unsigned char *dp,
204                unsigned int len, unsigned char *p_r)
205 {
206     unsigned pb;
207     unsigned char prefix[16];
208     int consumed = 0;
209 
210     if(plen >= 0)
211         pb = (plen + 7) / 8;
212     else if(ae == 1)
213         pb = 4;
214     else
215         pb = 16;
216 
217     if(pb > 16)
218         return -1;
219 
220     memset(prefix, 0, 16);
221 
222     switch(ae) {
223     case 0: break;
224     case 1:
225         if(omitted > 4 || pb > 4 || (pb > omitted && len < pb - omitted))
226             return -1;
227         memcpy(prefix, v4prefix, 12);
228         if(omitted) {
229             if (dp == NULL) return -1;
230             memcpy(prefix, dp, 12 + omitted);
231         }
232         if(pb > omitted) {
233             memcpy(prefix + 12 + omitted, p, pb - omitted);
234             consumed = pb - omitted;
235         }
236         break;
237     case 2:
238         if(omitted > 16 || (pb > omitted && len < pb - omitted))
239             return -1;
240         if(omitted) {
241             if (dp == NULL) return -1;
242             memcpy(prefix, dp, omitted);
243         }
244         if(pb > omitted) {
245             memcpy(prefix + omitted, p, pb - omitted);
246             consumed = pb - omitted;
247         }
248         break;
249     case 3:
250         if(pb > 8 && len < pb - 8) return -1;
251         prefix[0] = 0xfe;
252         prefix[1] = 0x80;
253         if(pb > 8) {
254             memcpy(prefix + 8, p, pb - 8);
255             consumed = pb - 8;
256         }
257         break;
258     default:
259         return -1;
260     }
261 
262     memcpy(p_r, prefix, 16);
263     return consumed;
264 }
265 
266 static int
267 network_address(int ae, const unsigned char *a, unsigned int len,
268                 unsigned char *a_r)
269 {
270     return network_prefix(ae, -1, 0, a, NULL, len, a_r);
271 }
272 
273 /*
274  * Sub-TLVs consume the "extra data" of Babel TLVs (see Section 4.3 of RFC6126),
275  * their encoding is similar to the encoding of TLVs, but the type namespace is
276  * different:
277  *
278  * o Type 0 stands for Pad1 sub-TLV with the same encoding as the Pad1 TLV.
279  * o Type 1 stands for PadN sub-TLV with the same encoding as the PadN TLV.
280  * o Type 2 stands for Diversity sub-TLV, which propagates diversity routing
281  *   data. Its body is a variable-length sequence of 8-bit unsigned integers,
282  *   each representing per-hop number of interfering radio channel for the
283  *   prefix. Channel 0 is invalid and must not be used in the sub-TLV, channel
284  *   255 interferes with any other channel.
285  * o Type 3 stands for Timestamp sub-TLV, used to compute RTT between
286  *   neighbours. In the case of a Hello TLV, the body stores a 32-bits
287  *   timestamp, while in the case of a IHU TLV, two 32-bits timestamps are
288  *   stored.
289  *
290  * Sub-TLV types 0 and 1 are valid for any TLV type, whether sub-TLV type 2 is
291  * only valid for TLV type 8 (Update). Note that within an Update TLV a missing
292  * Diversity sub-TLV is not the same as a Diversity sub-TLV with an empty body.
293  * The former would mean a lack of any claims about the interference, and the
294  * latter would state that interference is definitely absent.
295  * A type 3 sub-TLV is valid both for Hello and IHU TLVs, though the exact
296  * semantic of the sub-TLV is different in each case.
297  */
298 static void
299 subtlvs_print(netdissect_options *ndo,
300               const u_char *cp, const u_char *ep, const uint8_t tlv_type)
301 {
302     uint8_t subtype, sublen;
303     const char *sep;
304     uint32_t t1, t2;
305 
306     while (cp < ep) {
307         subtype = GET_U_1(cp);
308         cp++;
309         if(subtype == MESSAGE_SUB_PAD1) {
310             ND_PRINT(" sub-pad1");
311             continue;
312         }
313         if ((MANDATORY_MASK & subtype) != 0)
314             ND_PRINT(" (M)");
315         if(cp == ep)
316             goto invalid;
317         sublen = GET_U_1(cp);
318         cp++;
319         if(cp + sublen > ep)
320             goto invalid;
321 
322         switch(subtype) {
323         case MESSAGE_SUB_PADN:
324             ND_PRINT(" sub-padn");
325             cp += sublen;
326             break;
327         case MESSAGE_SUB_DIVERSITY:
328             ND_PRINT(" sub-diversity");
329             if (sublen == 0) {
330                 ND_PRINT(" empty");
331                 break;
332             }
333             sep = " ";
334             while (sublen) {
335                 ND_PRINT("%s%s", sep,
336                          tok2str(diversity_str, "%u", GET_U_1(cp)));
337                 cp++;
338                 sep = "-";
339                 sublen--;
340             }
341             if(tlv_type != MESSAGE_UPDATE &&
342                tlv_type != MESSAGE_UPDATE_SRC_SPECIFIC)
343                 ND_PRINT(" (bogus)");
344             break;
345         case MESSAGE_SUB_TIMESTAMP:
346             ND_PRINT(" sub-timestamp");
347             if(tlv_type == MESSAGE_HELLO) {
348                 if(sublen < 4)
349                     goto invalid;
350                 t1 = GET_BE_U_4(cp);
351                 ND_PRINT(" %s", format_timestamp(t1));
352             } else if(tlv_type == MESSAGE_IHU) {
353                 if(sublen < 8)
354                     goto invalid;
355                 t1 = GET_BE_U_4(cp);
356                 ND_PRINT(" %s", format_timestamp(t1));
357                 t2 = GET_BE_U_4(cp + 4);
358                 ND_PRINT("|%s", format_timestamp(t2));
359             } else
360                 ND_PRINT(" (bogus)");
361             cp += sublen;
362             break;
363         default:
364             ND_PRINT(" sub-unknown-0x%02x", subtype);
365             cp += sublen;
366         } /* switch */
367     } /* while */
368     return;
369 
370  invalid:
371     nd_print_invalid(ndo);
372 }
373 
374 #define ICHECK(i, l) \
375 	if ((i) + (l) > tlvs_length || (i) + (l) > packet_length_remaining) \
376 	    goto invalid;
377 
378 static int
379 babel_print_v2_tlvs(netdissect_options *ndo,
380                     const u_char *cp, u_int tlvs_length,
381                     u_int packet_length_remaining)
382 {
383     u_int i;
384     u_char v4_prefix[16] =
385         {0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0xFF, 0xFF, 0, 0, 0, 0 };
386     u_char v6_prefix[16] = {0};
387 
388     i = 0;
389     while(i < tlvs_length) {
390         const u_char *message;
391         uint8_t type;
392         u_int len;
393 
394         message = cp + i;
395 
396         ICHECK(i, 1);
397         if((type = GET_U_1(message)) == MESSAGE_PAD1) {
398             ND_PRINT(ndo->ndo_vflag ? "\n\tPad 1" : " pad1");
399             i += 1;
400             continue;
401         }
402 
403         ICHECK(i, 2);
404         ND_TCHECK_2(message);
405         len = GET_U_1(message + 1);
406 
407         ICHECK(i, 2 + len);
408         ND_TCHECK_LEN(message, 2 + len);
409 
410         switch(type) {
411         case MESSAGE_PADN: {
412             if (!ndo->ndo_vflag)
413                 ND_PRINT(" padN");
414             else
415                 ND_PRINT("\n\tPad %u", len + 2);
416         }
417             break;
418 
419         case MESSAGE_ACK_REQ: {
420             u_short nonce, interval;
421             if (!ndo->ndo_vflag)
422                 ND_PRINT(" ack-req");
423             else {
424                 ND_PRINT("\n\tAcknowledgment Request ");
425                 if(len < 6) goto invalid;
426                 nonce = GET_BE_U_2(message + 4);
427                 interval = GET_BE_U_2(message + 6);
428                 ND_PRINT("%04x %s", nonce, format_interval(interval));
429             }
430         }
431             break;
432 
433         case MESSAGE_ACK: {
434             u_short nonce;
435             if (!ndo->ndo_vflag)
436                 ND_PRINT(" ack");
437             else {
438                 ND_PRINT("\n\tAcknowledgment ");
439                 if(len < 2) goto invalid;
440                 nonce = GET_BE_U_2(message + 2);
441                 ND_PRINT("%04x", nonce);
442             }
443         }
444             break;
445 
446         case MESSAGE_HELLO:  {
447             u_short seqno, interval, unicast;
448             if (!ndo->ndo_vflag)
449                 ND_PRINT(" hello");
450             else {
451                 ND_PRINT("\n\tHello ");
452                 if(len < 6) goto invalid;
453                 unicast = (GET_BE_U_2(message + 2) & UNICAST_MASK);
454                 seqno = GET_BE_U_2(message + 4);
455                 interval = GET_BE_U_2(message + 6);
456                 if(unicast)
457                      ND_PRINT("(Unicast) ");
458                 ND_PRINT("seqno %u ", seqno);
459                 if(interval!=0)
460                     ND_PRINT("interval %s", format_interval(interval));
461                 else
462                     ND_PRINT("unscheduled");
463                 /* Extra data. */
464                 if(len > 6)
465                     subtlvs_print(ndo, message + 8, message + 2 + len, type);
466             }
467         }
468             break;
469 
470         case MESSAGE_IHU: {
471             unsigned short rxcost, interval;
472             if (!ndo->ndo_vflag)
473                 ND_PRINT(" ihu");
474             else {
475                 u_char address[16];
476                 u_char ae;
477                 int rc;
478                 ND_PRINT("\n\tIHU ");
479                 if(len < 6) goto invalid;
480                 rxcost = GET_BE_U_2(message + 4);
481                 interval = GET_BE_U_2(message + 6);
482                 ae = GET_U_1(message + 2);
483                 rc = network_address(ae, message + 8,
484                                      len - 6, address);
485                 if(rc < 0) { nd_print_trunc(ndo); break; }
486                 ND_PRINT("%s rxcost %u interval %s",
487                        ae == 0 ? "any" : format_address(ndo, address),
488                        rxcost, format_interval(interval));
489                 /* Extra data. */
490                 if((u_int)rc < len - 6)
491                     subtlvs_print(ndo, message + 8 + rc, message + 2 + len,
492                                   type);
493             }
494         }
495             break;
496 
497         case MESSAGE_ROUTER_ID: {
498             if (!ndo->ndo_vflag)
499                 ND_PRINT(" router-id");
500             else {
501                 ND_PRINT("\n\tRouter Id");
502                 if(len < 10) goto invalid;
503                 ND_PRINT(" %s", format_id(ndo, message + 4));
504             }
505         }
506             break;
507 
508         case MESSAGE_NH: {
509             if (!ndo->ndo_vflag)
510                 ND_PRINT(" nh");
511             else {
512                 int rc;
513                 u_char ae;
514                 u_char nh[16];
515                 ND_PRINT("\n\tNext Hop");
516                 if(len < 2) goto invalid;
517                 ae = GET_U_1(message + 2);
518                 rc = network_address(ae, message + 4,
519                                      len - 2, nh);
520                 if(rc < 0) goto invalid;
521                 ND_PRINT(" %s", ae == 0 ? "invalid AE 0" : format_address(ndo, nh));
522             }
523         }
524             break;
525 
526         case MESSAGE_UPDATE: {
527             if (!ndo->ndo_vflag) {
528                 ND_PRINT(" update");
529                 if(len < 10)
530                     goto invalid;
531                 else
532                     ND_PRINT("%s%s%s",
533                            (GET_U_1(message + 3) & 0x80) ? "/prefix": "",
534                            (GET_U_1(message + 3) & 0x40) ? "/id" : "",
535                            (GET_U_1(message + 3) & 0x3f) ? "/unknown" : "");
536             } else {
537                 u_short interval, seqno, metric;
538                 u_char ae, plen;
539                 int rc;
540                 u_char prefix[16];
541                 ND_PRINT("\n\tUpdate");
542                 if(len < 10) goto invalid;
543                 ae = GET_U_1(message + 2);
544                 plen = GET_U_1(message + 4) + (GET_U_1(message + 2) == 1 ? 96 : 0);
545                 rc = network_prefix(ae,
546                                     GET_U_1(message + 4),
547                                     GET_U_1(message + 5),
548                                     message + 12,
549                                     GET_U_1(message + 2) == 1 ? v4_prefix : v6_prefix,
550                                     len - 10, prefix);
551                 if(rc < 0) goto invalid;
552                 interval = GET_BE_U_2(message + 6);
553                 seqno = GET_BE_U_2(message + 8);
554                 metric = GET_BE_U_2(message + 10);
555                 ND_PRINT("%s%s%s %s metric %u seqno %u interval %s",
556                        (GET_U_1(message + 3) & 0x80) ? "/prefix": "",
557                        (GET_U_1(message + 3) & 0x40) ? "/id" : "",
558                        (GET_U_1(message + 3) & 0x3f) ? "/unknown" : "",
559                        ae == 0 ? "any" : format_prefix(ndo, prefix, plen),
560                        metric, seqno, format_interval_update(interval));
561                 if(GET_U_1(message + 3) & 0x80) {
562                     if(GET_U_1(message + 2) == 1)
563                         memcpy(v4_prefix, prefix, 16);
564                     else
565                         memcpy(v6_prefix, prefix, 16);
566                 }
567                 /* extra data? */
568                 if((u_int)rc < len - 10)
569                     subtlvs_print(ndo, message + 12 + rc, message + 2 + len, type);
570             }
571         }
572             break;
573 
574         case MESSAGE_ROUTE_REQUEST: {
575             if (!ndo->ndo_vflag)
576                 ND_PRINT(" route-request");
577             else {
578                 int rc;
579                 u_char prefix[16], ae, plen;
580                 ND_PRINT("\n\tRoute Request ");
581                 if(len < 2) goto invalid;
582                 ae = GET_U_1(message + 2);
583                 plen = GET_U_1(message + 3) + (GET_U_1(message + 2) == 1 ? 96 : 0);
584                 rc = network_prefix(ae,
585                                     GET_U_1(message + 3), 0,
586                                     message + 4, NULL, len - 2, prefix);
587                 if(rc < 0) goto invalid;
588                 ND_PRINT("for %s",
589                        ae == 0 ? "any" : format_prefix(ndo, prefix, plen));
590             }
591         }
592             break;
593 
594         case MESSAGE_SEQNO_REQUEST : {
595             if (!ndo->ndo_vflag)
596                 ND_PRINT(" seqno-request");
597             else {
598                 int rc;
599                 u_short seqno;
600                 u_char prefix[16], ae, plen;
601                 ND_PRINT("\n\tSeqno Request ");
602                 if(len < 14) goto invalid;
603                 ae = GET_U_1(message + 2);
604                 seqno = GET_BE_U_2(message + 4);
605                 rc = network_prefix(ae,
606                                     GET_U_1(message + 3), 0,
607                                     message + 16, NULL, len - 14, prefix);
608                 if(rc < 0) goto invalid;
609                 plen = GET_U_1(message + 3) + (GET_U_1(message + 2) == 1 ? 96 : 0);
610                 ND_PRINT("(%u hops) for %s seqno %u id %s",
611                        GET_U_1(message + 6),
612                        ae == 0 ? "invalid AE 0" : format_prefix(ndo, prefix, plen),
613                        seqno, format_id(ndo, message + 8));
614             }
615         }
616             break;
617         case MESSAGE_TSPC :
618             if (!ndo->ndo_vflag)
619                 ND_PRINT(" tspc");
620             else {
621                 ND_PRINT("\n\tTS/PC ");
622                 if(len < 6) goto invalid;
623                 ND_PRINT("timestamp %u packetcounter %u",
624                           GET_BE_U_4(message + 4),
625                           GET_BE_U_2(message + 2));
626             }
627             break;
628         case MESSAGE_HMAC : {
629             if (!ndo->ndo_vflag)
630                 ND_PRINT(" hmac");
631             else {
632                 unsigned j;
633                 ND_PRINT("\n\tHMAC ");
634                 if(len < 18) goto invalid;
635                 ND_PRINT("key-id %u digest-%u ", GET_BE_U_2(message + 2),
636                          len - 2);
637                 for (j = 0; j < len - 2; j++)
638                     ND_PRINT("%02X", GET_U_1(message + j + 4));
639             }
640         }
641             break;
642 
643         case MESSAGE_UPDATE_SRC_SPECIFIC : {
644             if(!ndo->ndo_vflag) {
645                 ND_PRINT(" ss-update");
646             } else {
647                 u_char prefix[16], src_prefix[16];
648                 u_short interval, seqno, metric;
649                 u_char ae, plen, src_plen, omitted;
650                 int rc;
651                 int parsed_len = 10;
652                 ND_PRINT("\n\tSS-Update");
653                 if(len < 10) goto invalid;
654                 ae = GET_U_1(message + 2);
655                 src_plen = GET_U_1(message + 3);
656                 plen = GET_U_1(message + 4);
657                 omitted = GET_U_1(message + 5);
658                 interval = GET_BE_U_2(message + 6);
659                 seqno = GET_BE_U_2(message + 8);
660                 metric = GET_BE_U_2(message + 10);
661                 rc = network_prefix(ae, plen, omitted, message + 2 + parsed_len,
662                                     ae == 1 ? v4_prefix : v6_prefix,
663                                     len - parsed_len, prefix);
664                 if(rc < 0) goto invalid;
665                 if(ae == 1)
666                     plen += 96;
667                 parsed_len += rc;
668                 rc = network_prefix(ae, src_plen, 0, message + 2 + parsed_len,
669                                     NULL, len - parsed_len, src_prefix);
670                 if(rc < 0) goto invalid;
671                 if(ae == 1)
672                     src_plen += 96;
673                 parsed_len += rc;
674 
675                 ND_PRINT(" %s from", format_prefix(ndo, prefix, plen));
676                 ND_PRINT(" %s metric %u seqno %u interval %s",
677                           format_prefix(ndo, src_prefix, src_plen),
678                           metric, seqno, format_interval_update(interval));
679                 /* extra data? */
680                 if((u_int)parsed_len < len)
681                     subtlvs_print(ndo, message + 2 + parsed_len,
682                                   message + 2 + len, type);
683             }
684         }
685             break;
686 
687         case MESSAGE_REQUEST_SRC_SPECIFIC : {
688             if(!ndo->ndo_vflag)
689                 ND_PRINT(" ss-request");
690             else {
691                 int rc, parsed_len = 3;
692                 u_char ae, plen, src_plen, prefix[16], src_prefix[16];
693                 ND_PRINT("\n\tSS-Request ");
694                 if(len < 3) goto invalid;
695                 ae = GET_U_1(message + 2);
696                 plen = GET_U_1(message + 3);
697                 src_plen = GET_U_1(message + 4);
698                 rc = network_prefix(ae, plen, 0, message + 2 + parsed_len,
699                                     NULL, len - parsed_len, prefix);
700                 if(rc < 0) goto invalid;
701                 if(ae == 1)
702                     plen += 96;
703                 parsed_len += rc;
704                 rc = network_prefix(ae, src_plen, 0, message + 2 + parsed_len,
705                                     NULL, len - parsed_len, src_prefix);
706                 if(rc < 0) goto invalid;
707                 if(ae == 1)
708                     src_plen += 96;
709                 parsed_len += rc;
710                 if(ae == 0) {
711                     ND_PRINT("for any");
712                 } else {
713                     ND_PRINT("for (%s, ", format_prefix(ndo, prefix, plen));
714                     ND_PRINT("%s)", format_prefix(ndo, src_prefix, src_plen));
715                 }
716             }
717         }
718             break;
719 
720         case MESSAGE_MH_REQUEST_SRC_SPECIFIC : {
721             if(!ndo->ndo_vflag)
722                 ND_PRINT(" ss-mh-request");
723             else {
724                 int rc, parsed_len = 14;
725                 u_short seqno;
726                 u_char ae, plen, src_plen, prefix[16], src_prefix[16], hopc;
727                 const u_char *router_id = NULL;
728                 ND_PRINT("\n\tSS-MH-Request ");
729                 if(len < 14) goto invalid;
730                 ae = GET_U_1(message + 2);
731                 plen = GET_U_1(message + 3);
732                 seqno = GET_BE_U_2(message + 4);
733                 hopc = GET_U_1(message + 6);
734                 src_plen = GET_U_1(message + 7);
735                 router_id = message + 8;
736                 rc = network_prefix(ae, plen, 0, message + 2 + parsed_len,
737                                     NULL, len - parsed_len, prefix);
738                 if(rc < 0) goto invalid;
739                 if(ae == 1)
740                     plen += 96;
741                 parsed_len += rc;
742                 rc = network_prefix(ae, src_plen, 0, message + 2 + parsed_len,
743                                     NULL, len - parsed_len, src_prefix);
744                 if(rc < 0) goto invalid;
745                 if(ae == 1)
746                     src_plen += 96;
747                 ND_PRINT("(%u hops) for (%s, ",
748                           hopc, format_prefix(ndo, prefix, plen));
749                 ND_PRINT("%s) seqno %u id %s",
750                           format_prefix(ndo, src_prefix, src_plen),
751                           seqno, format_id(ndo, router_id));
752             }
753         }
754             break;
755 
756         case MESSAGE_MAC: {
757             if (!ndo->ndo_vflag)
758                 ND_PRINT(" mac");
759             else {
760                 ND_PRINT("\n\tMAC ");
761                 ND_PRINT("len %u", len);
762             }
763         }
764             break;
765 
766         case MESSAGE_PC: {
767             if (!ndo->ndo_vflag)
768                 ND_PRINT(" pc");
769             else {
770                 ND_PRINT("\n\tPC");
771                 if(len < 4) goto invalid;
772                 ND_PRINT(" value %u",
773                     GET_BE_U_4(message + 2));
774                 ND_PRINT(" index len %u", len-4);
775             }
776         }
777             break;
778 
779         case MESSAGE_CHALLENGE_REQUEST: {
780             if (!ndo->ndo_vflag)
781                 ND_PRINT(" challenge_request");
782             else {
783                 ND_PRINT("\n\tChallenge Request");
784                 if(len > 192) goto invalid;
785                 ND_PRINT(" len %u", len);
786             }
787         }
788             break;
789 
790         case MESSAGE_CHALLENGE_REPLY: {
791             if (!ndo->ndo_vflag)
792                 ND_PRINT(" challenge_reply");
793             else {
794                 ND_PRINT("\n\tChallenge Reply");
795                 if (len > 192) goto invalid;
796                 ND_PRINT(" len %u", len);
797             }
798         }
799             break;
800 
801         default:
802             if (!ndo->ndo_vflag)
803                 ND_PRINT(" unknown");
804             else
805                 ND_PRINT("\n\tUnknown message type %u", type);
806         }
807         i += len + 2;
808     }
809 
810     return 0; /* OK */
811 
812 trunc:
813     return -1; /* packet truncated by capture process */
814 
815 invalid:
816     return -2; /* packet is invalid */
817 }
818 
819 static void
820 babel_print_v2(netdissect_options *ndo,
821                const u_char *cp, u_int length)
822 {
823     u_short bodylen;
824     int ret;
825 
826     ND_TCHECK_4(cp);
827     if (length < 4)
828         goto invalid;
829     bodylen = GET_BE_U_2(cp + 2);
830     ND_PRINT(" (%u)", bodylen);
831     length -= 4;
832     cp += 4;
833 
834     /* Process the TLVs in the body */
835     if (length < bodylen)
836         goto invalid;
837     ret = babel_print_v2_tlvs(ndo, cp, bodylen, length);
838     if (ret == -1)
839         goto trunc;
840     if (ret == -2)
841         goto invalid;
842     length -= bodylen;
843     cp += bodylen;
844 
845     /* If there's a trailer, process the TLVs in the trailer */
846     if (length != 0) {
847 	if(ndo->ndo_vflag) ND_PRINT("\n\t----");
848 	else ND_PRINT(" |");
849         ret = babel_print_v2_tlvs(ndo, cp, length, length);
850         if (ret == -1)
851             goto trunc;
852         if (ret == -2)
853             goto invalid;
854     }
855     return;
856 
857  trunc:
858     nd_print_trunc(ndo);
859     return;
860 
861  invalid:
862     nd_print_invalid(ndo);
863 }
864