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