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