xref: /freebsd/contrib/tcpdump/print-eigrp.c (revision 7fdf597e96a02165cfe22ff357b857d5fa15ed8a)
1 /*
2  * Copyright (c) 1998-2004  Hannes Gredler <hannes@gredler.at>
3  *      The TCPDUMP project
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 
17 /* \summary: Enhanced Interior Gateway Routing Protocol (EIGRP) printer */
18 
19 /*
20  * specification:
21  *
22  * https://web.archive.org/web/20190722221712/https://www.rhyshaden.com/eigrp.htm
23  * RFC 7868
24  */
25 
26 #include <config.h>
27 
28 #include "netdissect-stdinc.h"
29 
30 #include <string.h>
31 
32 #include "netdissect.h"
33 #include "extract.h"
34 #include "addrtoname.h"
35 
36 
37 struct eigrp_common_header {
38     nd_uint8_t  version;
39     nd_uint8_t  opcode;
40     nd_uint16_t checksum;
41     nd_uint32_t flags;
42     nd_uint32_t seq;
43     nd_uint32_t ack;
44     nd_uint16_t vrid;
45     nd_uint16_t asn;
46 };
47 
48 #define	EIGRP_VERSION                        2
49 
50 #define	EIGRP_OPCODE_UPDATE                  1
51 #define	EIGRP_OPCODE_QUERY                   3
52 #define	EIGRP_OPCODE_REPLY                   4
53 #define	EIGRP_OPCODE_HELLO                   5
54 #define	EIGRP_OPCODE_IPXSAP                  6
55 #define	EIGRP_OPCODE_PROBE                   7
56 
57 static const struct tok eigrp_opcode_values[] = {
58     { EIGRP_OPCODE_UPDATE, "Update" },
59     { EIGRP_OPCODE_QUERY, "Query" },
60     { EIGRP_OPCODE_REPLY, "Reply" },
61     { EIGRP_OPCODE_HELLO, "Hello" },
62     { EIGRP_OPCODE_IPXSAP, "IPX SAP" },
63     { EIGRP_OPCODE_PROBE, "Probe" },
64     { 0, NULL}
65 };
66 
67 static const struct tok eigrp_common_header_flag_values[] = {
68     { 0x01, "Init" },
69     { 0x02, "Conditionally Received" },
70     { 0x04, "Restart" },
71     { 0x08, "End-of-Table" },
72     { 0, NULL}
73 };
74 
75 struct eigrp_tlv_header {
76     nd_uint16_t type;
77     nd_uint16_t length;
78 };
79 
80 #define EIGRP_TLV_GENERAL_PARM   0x0001
81 #define EIGRP_TLV_AUTH           0x0002
82 #define EIGRP_TLV_SEQ            0x0003
83 #define EIGRP_TLV_SW_VERSION     0x0004
84 #define EIGRP_TLV_MCAST_SEQ      0x0005
85 #define EIGRP_TLV_IP_INT         0x0102
86 #define EIGRP_TLV_IP_EXT         0x0103
87 #define EIGRP_TLV_AT_INT         0x0202
88 #define EIGRP_TLV_AT_EXT         0x0203
89 #define EIGRP_TLV_AT_CABLE_SETUP 0x0204
90 #define EIGRP_TLV_IPX_INT        0x0302
91 #define EIGRP_TLV_IPX_EXT        0x0303
92 
93 static const struct tok eigrp_tlv_values[] = {
94     { EIGRP_TLV_GENERAL_PARM, "General Parameters"},
95     { EIGRP_TLV_AUTH, "Authentication"},
96     { EIGRP_TLV_SEQ, "Sequence"},
97     { EIGRP_TLV_SW_VERSION, "Software Version"},
98     { EIGRP_TLV_MCAST_SEQ, "Next Multicast Sequence"},
99     { EIGRP_TLV_IP_INT, "IP Internal routes"},
100     { EIGRP_TLV_IP_EXT, "IP External routes"},
101     { EIGRP_TLV_AT_INT, "AppleTalk Internal routes"},
102     { EIGRP_TLV_AT_EXT, "AppleTalk External routes"},
103     { EIGRP_TLV_AT_CABLE_SETUP, "AppleTalk Cable setup"},
104     { EIGRP_TLV_IPX_INT, "IPX Internal routes"},
105     { EIGRP_TLV_IPX_EXT, "IPX External routes"},
106     { 0, NULL}
107 };
108 
109 struct eigrp_tlv_general_parm_t {
110     nd_uint8_t  k1;
111     nd_uint8_t  k2;
112     nd_uint8_t  k3;
113     nd_uint8_t  k4;
114     nd_uint8_t  k5;
115     nd_uint8_t  res;
116     nd_uint16_t holdtime;
117 };
118 
119 struct eigrp_tlv_sw_version_t {
120     nd_uint8_t ios_major;
121     nd_uint8_t ios_minor;
122     nd_uint8_t eigrp_major;
123     nd_uint8_t eigrp_minor;
124 };
125 
126 struct eigrp_tlv_ip_int_t {
127     nd_ipv4     nexthop;
128     nd_uint32_t delay;
129     nd_uint32_t bandwidth;
130     nd_uint24_t mtu;
131     nd_uint8_t  hopcount;
132     nd_uint8_t  reliability;
133     nd_uint8_t  load;
134     nd_byte     reserved[2];
135     nd_uint8_t  plen;
136     nd_uint8_t  destination; /* variable length [1-4] bytes encoding */
137 };
138 
139 struct eigrp_tlv_ip_ext_t {
140     nd_ipv4     nexthop;
141     nd_ipv4     origin_router;
142     nd_uint32_t origin_as;
143     nd_uint32_t tag;
144     nd_uint32_t metric;
145     nd_byte     reserved[2];
146     nd_uint8_t  proto_id;
147     nd_uint8_t  flags;
148     nd_uint32_t delay;
149     nd_uint32_t bandwidth;
150     nd_uint24_t mtu;
151     nd_uint8_t  hopcount;
152     nd_uint8_t  reliability;
153     nd_uint8_t  load;
154     nd_byte     reserved2[2];
155     nd_uint8_t  plen;
156     nd_uint8_t  destination; /* variable length [1-4] bytes encoding */
157 };
158 
159 struct eigrp_tlv_at_cable_setup_t {
160     nd_uint16_t cable_start;
161     nd_uint16_t cable_end;
162     nd_uint32_t router_id;
163 };
164 
165 struct eigrp_tlv_at_int_t {
166     nd_byte     nexthop[4];
167     nd_uint32_t delay;
168     nd_uint32_t bandwidth;
169     nd_uint24_t mtu;
170     nd_uint8_t  hopcount;
171     nd_uint8_t  reliability;
172     nd_uint8_t  load;
173     nd_byte     reserved[2];
174     nd_uint16_t cable_start;
175     nd_uint16_t cable_end;
176 };
177 
178 struct eigrp_tlv_at_ext_t {
179     nd_byte     nexthop[4];
180     nd_uint32_t origin_router;
181     nd_uint32_t origin_as;
182     nd_uint32_t tag;
183     nd_uint8_t  proto_id;
184     nd_uint8_t  flags;
185     nd_uint16_t metric;
186     nd_uint32_t delay;
187     nd_uint32_t bandwidth;
188     nd_uint24_t mtu;
189     nd_uint8_t  hopcount;
190     nd_uint8_t  reliability;
191     nd_uint8_t  load;
192     nd_byte     reserved2[2];
193     nd_uint16_t cable_start;
194     nd_uint16_t cable_end;
195 };
196 
197 static const struct tok eigrp_ext_proto_id_values[] = {
198     { 0x01, "IGRP" },
199     { 0x02, "EIGRP" },
200     { 0x03, "Static" },
201     { 0x04, "RIP" },
202     { 0x05, "Hello" },
203     { 0x06, "OSPF" },
204     { 0x07, "IS-IS" },
205     { 0x08, "EGP" },
206     { 0x09, "BGP" },
207     { 0x0a, "IDRP" },
208     { 0x0b, "Connected" },
209     { 0, NULL}
210 };
211 
212 void
213 eigrp_print(netdissect_options *ndo, const u_char *pptr, u_int len)
214 {
215     const struct eigrp_common_header *eigrp_com_header;
216     const struct eigrp_tlv_header *eigrp_tlv_header;
217     const u_char *tptr,*tlv_tptr;
218     u_int tlen,eigrp_tlv_len,eigrp_tlv_type,tlv_tlen, byte_length, bit_length;
219     uint8_t prefix[4];
220 
221     union {
222         const struct eigrp_tlv_general_parm_t *eigrp_tlv_general_parm;
223         const struct eigrp_tlv_sw_version_t *eigrp_tlv_sw_version;
224         const struct eigrp_tlv_ip_int_t *eigrp_tlv_ip_int;
225         const struct eigrp_tlv_ip_ext_t *eigrp_tlv_ip_ext;
226         const struct eigrp_tlv_at_cable_setup_t *eigrp_tlv_at_cable_setup;
227         const struct eigrp_tlv_at_int_t *eigrp_tlv_at_int;
228         const struct eigrp_tlv_at_ext_t *eigrp_tlv_at_ext;
229     } tlv_ptr;
230 
231     ndo->ndo_protocol = "eigrp";
232     tptr=pptr;
233     eigrp_com_header = (const struct eigrp_common_header *)pptr;
234     ND_TCHECK_SIZE(eigrp_com_header);
235 
236     /*
237      * Sanity checking of the header.
238      */
239     if (GET_U_1(eigrp_com_header->version) != EIGRP_VERSION) {
240         ND_PRINT("EIGRP version %u packet not supported",
241                  GET_U_1(eigrp_com_header->version));
242         return;
243     }
244 
245     /* in non-verbose mode just lets print the basic Message Type*/
246     if (ndo->ndo_vflag < 1) {
247         ND_PRINT("EIGRP %s, length: %u",
248                tok2str(eigrp_opcode_values, "unknown (%u)",GET_U_1(eigrp_com_header->opcode)),
249                len);
250         return;
251     }
252 
253     /* ok they seem to want to know everything - lets fully decode it */
254 
255     if (len < sizeof(struct eigrp_common_header)) {
256         ND_PRINT("EIGRP %s, length: %u (too short, < %zu)",
257                tok2str(eigrp_opcode_values, "unknown (%u)",GET_U_1(eigrp_com_header->opcode)),
258                len, sizeof(struct eigrp_common_header));
259         return;
260     }
261     tlen=len-sizeof(struct eigrp_common_header);
262 
263     ND_PRINT("\n\tEIGRP v%u, opcode: %s (%u), chksum: 0x%04x, Flags: [%s]"
264              "\n\tseq: 0x%08x, ack: 0x%08x, VRID: %u, AS: %u, length: %u",
265            GET_U_1(eigrp_com_header->version),
266            tok2str(eigrp_opcode_values, "unknown, type: %u",GET_U_1(eigrp_com_header->opcode)),
267            GET_U_1(eigrp_com_header->opcode),
268            GET_BE_U_2(eigrp_com_header->checksum),
269            bittok2str(eigrp_common_header_flag_values,
270                    "none",
271                    GET_BE_U_4(eigrp_com_header->flags)),
272            GET_BE_U_4(eigrp_com_header->seq),
273            GET_BE_U_4(eigrp_com_header->ack),
274            GET_BE_U_2(eigrp_com_header->vrid),
275            GET_BE_U_2(eigrp_com_header->asn),
276            tlen);
277 
278     tptr+=sizeof(struct eigrp_common_header);
279 
280     while(tlen>0) {
281         /* did we capture enough for fully decoding the object header ? */
282         ND_TCHECK_LEN(tptr, sizeof(struct eigrp_tlv_header));
283 
284         eigrp_tlv_header = (const struct eigrp_tlv_header *)tptr;
285         eigrp_tlv_len=GET_BE_U_2(eigrp_tlv_header->length);
286         eigrp_tlv_type=GET_BE_U_2(eigrp_tlv_header->type);
287 
288 
289         if (eigrp_tlv_len < sizeof(struct eigrp_tlv_header) ||
290             eigrp_tlv_len > tlen) {
291             print_unknown_data(ndo,tptr+sizeof(struct eigrp_tlv_header),"\n\t    ",tlen);
292             return;
293         }
294 
295         ND_PRINT("\n\t  %s TLV (0x%04x), length: %u",
296                tok2str(eigrp_tlv_values,
297                        "Unknown",
298                        eigrp_tlv_type),
299                eigrp_tlv_type,
300                eigrp_tlv_len);
301 
302         if (eigrp_tlv_len < sizeof(struct eigrp_tlv_header)) {
303                 ND_PRINT(" (too short, < %zu)",
304                          sizeof(struct eigrp_tlv_header));
305                 break;
306         }
307         tlv_tptr=tptr+sizeof(struct eigrp_tlv_header);
308         tlv_tlen=eigrp_tlv_len-sizeof(struct eigrp_tlv_header);
309 
310         /* did we capture enough for fully decoding the object ? */
311         ND_TCHECK_LEN(tptr, eigrp_tlv_len);
312 
313         switch(eigrp_tlv_type) {
314 
315         case EIGRP_TLV_GENERAL_PARM:
316             tlv_ptr.eigrp_tlv_general_parm = (const struct eigrp_tlv_general_parm_t *)tlv_tptr;
317             if (tlv_tlen < sizeof(*tlv_ptr.eigrp_tlv_general_parm)) {
318                 ND_PRINT(" (too short, < %zu)",
319 			 sizeof(struct eigrp_tlv_header) + sizeof(*tlv_ptr.eigrp_tlv_general_parm));
320                 break;
321             }
322 
323             ND_PRINT("\n\t    holdtime: %us, k1 %u, k2 %u, k3 %u, k4 %u, k5 %u",
324                    GET_BE_U_2(tlv_ptr.eigrp_tlv_general_parm->holdtime),
325                    GET_U_1(tlv_ptr.eigrp_tlv_general_parm->k1),
326                    GET_U_1(tlv_ptr.eigrp_tlv_general_parm->k2),
327                    GET_U_1(tlv_ptr.eigrp_tlv_general_parm->k3),
328                    GET_U_1(tlv_ptr.eigrp_tlv_general_parm->k4),
329                    GET_U_1(tlv_ptr.eigrp_tlv_general_parm->k5));
330             break;
331 
332         case EIGRP_TLV_SW_VERSION:
333             tlv_ptr.eigrp_tlv_sw_version = (const struct eigrp_tlv_sw_version_t *)tlv_tptr;
334             if (tlv_tlen < sizeof(*tlv_ptr.eigrp_tlv_sw_version)) {
335                 ND_PRINT(" (too short, < %zu)",
336                          sizeof(struct eigrp_tlv_header) + sizeof(*tlv_ptr.eigrp_tlv_sw_version));
337                 break;
338             }
339 
340             ND_PRINT("\n\t    IOS version: %u.%u, EIGRP version %u.%u",
341                    GET_U_1(tlv_ptr.eigrp_tlv_sw_version->ios_major),
342                    GET_U_1(tlv_ptr.eigrp_tlv_sw_version->ios_minor),
343                    GET_U_1(tlv_ptr.eigrp_tlv_sw_version->eigrp_major),
344                    GET_U_1(tlv_ptr.eigrp_tlv_sw_version->eigrp_minor));
345             break;
346 
347         case EIGRP_TLV_IP_INT:
348             tlv_ptr.eigrp_tlv_ip_int = (const struct eigrp_tlv_ip_int_t *)tlv_tptr;
349             if (tlv_tlen < sizeof(*tlv_ptr.eigrp_tlv_ip_int)) {
350                 ND_PRINT(" (too short, < %zu)",
351                          sizeof(struct eigrp_tlv_header) + sizeof(*tlv_ptr.eigrp_tlv_ip_int));
352                 break;
353             }
354 
355             bit_length = GET_U_1(tlv_ptr.eigrp_tlv_ip_int->plen);
356             if (bit_length > 32) {
357                 ND_PRINT("\n\t    illegal prefix length %u",bit_length);
358                 break;
359             }
360             byte_length = (bit_length + 7) / 8; /* variable length encoding */
361             memset(prefix, 0, 4);
362             GET_CPY_BYTES(prefix, tlv_ptr.eigrp_tlv_ip_int->destination, byte_length);
363 
364             ND_PRINT("\n\t    IPv4 prefix: %15s/%u, nexthop: ",
365                    ipaddr_string(ndo, prefix),	/* local buffer, not packet data; don't use GET_IPADDR_STRING() */
366                    bit_length);
367             if (GET_BE_U_4(tlv_ptr.eigrp_tlv_ip_int->nexthop) == 0)
368                 ND_PRINT("self");
369             else
370                 ND_PRINT("%s",
371                          GET_IPADDR_STRING(tlv_ptr.eigrp_tlv_ip_int->nexthop));
372 
373             ND_PRINT("\n\t      delay %u ms, bandwidth %u Kbps, mtu %u, hop %u, reliability %u, load %u",
374                    (GET_BE_U_4(tlv_ptr.eigrp_tlv_ip_int->delay)/100),
375                    GET_BE_U_4(tlv_ptr.eigrp_tlv_ip_int->bandwidth),
376                    GET_BE_U_3(tlv_ptr.eigrp_tlv_ip_int->mtu),
377                    GET_U_1(tlv_ptr.eigrp_tlv_ip_int->hopcount),
378                    GET_U_1(tlv_ptr.eigrp_tlv_ip_int->reliability),
379                    GET_U_1(tlv_ptr.eigrp_tlv_ip_int->load));
380             break;
381 
382         case EIGRP_TLV_IP_EXT:
383             tlv_ptr.eigrp_tlv_ip_ext = (const struct eigrp_tlv_ip_ext_t *)tlv_tptr;
384             if (tlv_tlen < sizeof(*tlv_ptr.eigrp_tlv_ip_ext)) {
385                 ND_PRINT(" (too short, < %zu)",
386                          sizeof(struct eigrp_tlv_header) + sizeof(*tlv_ptr.eigrp_tlv_ip_ext));
387                 break;
388             }
389 
390             bit_length = GET_U_1(tlv_ptr.eigrp_tlv_ip_ext->plen);
391             if (bit_length > 32) {
392                 ND_PRINT("\n\t    illegal prefix length %u",bit_length);
393                 break;
394             }
395             byte_length = (bit_length + 7) / 8; /* variable length encoding */
396             memset(prefix, 0, 4);
397             GET_CPY_BYTES(prefix, tlv_ptr.eigrp_tlv_ip_ext->destination, byte_length);
398 
399             ND_PRINT("\n\t    IPv4 prefix: %15s/%u, nexthop: ",
400                    ipaddr_string(ndo, prefix),	/* local buffer, not packet data; don't use GET_IPADDR_STRING() */
401                    bit_length);
402             if (GET_BE_U_4(tlv_ptr.eigrp_tlv_ip_ext->nexthop) == 0)
403                 ND_PRINT("self");
404             else
405                 ND_PRINT("%s",
406                          GET_IPADDR_STRING(tlv_ptr.eigrp_tlv_ip_ext->nexthop));
407 
408             ND_PRINT("\n\t      origin-router %s, origin-as %u, origin-proto %s, flags [0x%02x], tag 0x%08x, metric %u",
409                    GET_IPADDR_STRING(tlv_ptr.eigrp_tlv_ip_ext->origin_router),
410                    GET_BE_U_4(tlv_ptr.eigrp_tlv_ip_ext->origin_as),
411                    tok2str(eigrp_ext_proto_id_values,"unknown",GET_U_1(tlv_ptr.eigrp_tlv_ip_ext->proto_id)),
412                    GET_U_1(tlv_ptr.eigrp_tlv_ip_ext->flags),
413                    GET_BE_U_4(tlv_ptr.eigrp_tlv_ip_ext->tag),
414                    GET_BE_U_4(tlv_ptr.eigrp_tlv_ip_ext->metric));
415 
416             ND_PRINT("\n\t      delay %u ms, bandwidth %u Kbps, mtu %u, hop %u, reliability %u, load %u",
417                    (GET_BE_U_4(tlv_ptr.eigrp_tlv_ip_ext->delay)/100),
418                    GET_BE_U_4(tlv_ptr.eigrp_tlv_ip_ext->bandwidth),
419                    GET_BE_U_3(tlv_ptr.eigrp_tlv_ip_ext->mtu),
420                    GET_U_1(tlv_ptr.eigrp_tlv_ip_ext->hopcount),
421                    GET_U_1(tlv_ptr.eigrp_tlv_ip_ext->reliability),
422                    GET_U_1(tlv_ptr.eigrp_tlv_ip_ext->load));
423             break;
424 
425         case EIGRP_TLV_AT_CABLE_SETUP:
426             tlv_ptr.eigrp_tlv_at_cable_setup = (const struct eigrp_tlv_at_cable_setup_t *)tlv_tptr;
427             if (tlv_tlen < sizeof(*tlv_ptr.eigrp_tlv_at_cable_setup)) {
428                 ND_PRINT(" (too short, < %zu)",
429                          sizeof(struct eigrp_tlv_header) + sizeof(*tlv_ptr.eigrp_tlv_at_cable_setup));
430                 break;
431             }
432 
433             ND_PRINT("\n\t    Cable-range: %u-%u, Router-ID %u",
434                    GET_BE_U_2(tlv_ptr.eigrp_tlv_at_cable_setup->cable_start),
435                    GET_BE_U_2(tlv_ptr.eigrp_tlv_at_cable_setup->cable_end),
436                    GET_BE_U_4(tlv_ptr.eigrp_tlv_at_cable_setup->router_id));
437             break;
438 
439         case EIGRP_TLV_AT_INT:
440             tlv_ptr.eigrp_tlv_at_int = (const struct eigrp_tlv_at_int_t *)tlv_tptr;
441             if (tlv_tlen < sizeof(*tlv_ptr.eigrp_tlv_at_int)) {
442                 ND_PRINT(" (too short, < %zu)",
443                          sizeof(struct eigrp_tlv_header) + sizeof(*tlv_ptr.eigrp_tlv_at_int));
444                 break;
445             }
446 
447             ND_PRINT("\n\t     Cable-Range: %u-%u, nexthop: ",
448                    GET_BE_U_2(tlv_ptr.eigrp_tlv_at_int->cable_start),
449                    GET_BE_U_2(tlv_ptr.eigrp_tlv_at_int->cable_end));
450 
451             if (GET_BE_U_4(tlv_ptr.eigrp_tlv_at_int->nexthop) == 0)
452                 ND_PRINT("self");
453             else
454                 ND_PRINT("%u.%u",
455                        GET_BE_U_2(&tlv_ptr.eigrp_tlv_at_int->nexthop[0]),
456                        GET_BE_U_2(&tlv_ptr.eigrp_tlv_at_int->nexthop[2]));
457 
458             ND_PRINT("\n\t      delay %u ms, bandwidth %u Kbps, mtu %u, hop %u, reliability %u, load %u",
459                    (GET_BE_U_4(tlv_ptr.eigrp_tlv_at_int->delay)/100),
460                    GET_BE_U_4(tlv_ptr.eigrp_tlv_at_int->bandwidth),
461                    GET_BE_U_3(tlv_ptr.eigrp_tlv_at_int->mtu),
462                    GET_U_1(tlv_ptr.eigrp_tlv_at_int->hopcount),
463                    GET_U_1(tlv_ptr.eigrp_tlv_at_int->reliability),
464                    GET_U_1(tlv_ptr.eigrp_tlv_at_int->load));
465             break;
466 
467         case EIGRP_TLV_AT_EXT:
468             tlv_ptr.eigrp_tlv_at_ext = (const struct eigrp_tlv_at_ext_t *)tlv_tptr;
469             if (tlv_tlen < sizeof(*tlv_ptr.eigrp_tlv_at_ext)) {
470                 ND_PRINT(" (too short, < %zu)",
471                          sizeof(struct eigrp_tlv_header) + sizeof(*tlv_ptr.eigrp_tlv_at_ext));
472                 break;
473             }
474 
475             ND_PRINT("\n\t     Cable-Range: %u-%u, nexthop: ",
476                    GET_BE_U_2(tlv_ptr.eigrp_tlv_at_ext->cable_start),
477                    GET_BE_U_2(tlv_ptr.eigrp_tlv_at_ext->cable_end));
478 
479             if (GET_BE_U_4(tlv_ptr.eigrp_tlv_at_ext->nexthop) == 0)
480                 ND_PRINT("self");
481             else
482                 ND_PRINT("%u.%u",
483                        GET_BE_U_2(&tlv_ptr.eigrp_tlv_at_ext->nexthop[0]),
484                        GET_BE_U_2(&tlv_ptr.eigrp_tlv_at_ext->nexthop[2]));
485 
486             ND_PRINT("\n\t      origin-router %u, origin-as %u, origin-proto %s, flags [0x%02x], tag 0x%08x, metric %u",
487                    GET_BE_U_4(tlv_ptr.eigrp_tlv_at_ext->origin_router),
488                    GET_BE_U_4(tlv_ptr.eigrp_tlv_at_ext->origin_as),
489                    tok2str(eigrp_ext_proto_id_values,"unknown",GET_U_1(tlv_ptr.eigrp_tlv_at_ext->proto_id)),
490                    GET_U_1(tlv_ptr.eigrp_tlv_at_ext->flags),
491                    GET_BE_U_4(tlv_ptr.eigrp_tlv_at_ext->tag),
492                    GET_BE_U_2(tlv_ptr.eigrp_tlv_at_ext->metric));
493 
494             ND_PRINT("\n\t      delay %u ms, bandwidth %u Kbps, mtu %u, hop %u, reliability %u, load %u",
495                    (GET_BE_U_4(tlv_ptr.eigrp_tlv_at_ext->delay)/100),
496                    GET_BE_U_4(tlv_ptr.eigrp_tlv_at_ext->bandwidth),
497                    GET_BE_U_3(tlv_ptr.eigrp_tlv_at_ext->mtu),
498                    GET_U_1(tlv_ptr.eigrp_tlv_at_ext->hopcount),
499                    GET_U_1(tlv_ptr.eigrp_tlv_at_ext->reliability),
500                    GET_U_1(tlv_ptr.eigrp_tlv_at_ext->load));
501             break;
502 
503             /*
504              * FIXME those are the defined TLVs that lack a decoder
505              * you are welcome to contribute code ;-)
506              */
507 
508         case EIGRP_TLV_AUTH:
509         case EIGRP_TLV_SEQ:
510         case EIGRP_TLV_MCAST_SEQ:
511         case EIGRP_TLV_IPX_INT:
512         case EIGRP_TLV_IPX_EXT:
513 
514         default:
515             if (ndo->ndo_vflag <= 1)
516                 print_unknown_data(ndo,tlv_tptr,"\n\t    ",tlv_tlen);
517             break;
518         }
519         /* do we want to see an additionally hexdump ? */
520         if (ndo->ndo_vflag > 1)
521             print_unknown_data(ndo,tptr+sizeof(struct eigrp_tlv_header),"\n\t    ",
522                                eigrp_tlv_len-sizeof(struct eigrp_tlv_header));
523 
524         tptr+=eigrp_tlv_len;
525         tlen-=eigrp_tlv_len;
526     }
527     return;
528 trunc:
529     nd_print_trunc(ndo);
530 }
531