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