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