1/*
2 * Copyright (c) 1998-2007 The TCPDUMP project
3 *
4 * Redistribution and use in source and binary forms, with or without
5 * modification, are permitted provided that: (1) source code
6 * distributions retain the above copyright notice and this paragraph
7 * in its entirety, and (2) distributions including binary code include
8 * the above copyright notice and this paragraph in its entirety in
9 * the documentation or other materials provided with the distribution.
10 * THIS SOFTWARE IS PROVIDED ``AS IS'' AND
11 * WITHOUT ANY EXPRESS OR IMPLIED WARRANTIES, INCLUDING, WITHOUT
12 * LIMITATION, THE IMPLIED WARRANTIES OF MERCHANTABILITY AND FITNESS
13 * FOR A PARTICULAR PURPOSE.
14 *
15 * Original code by Carles Kishimoto <carles.kishimoto@gmail.com>
16 */
17
18/* \summary: Cisco UniDirectional Link Detection (UDLD) protocol printer */
19
20/* specification: RFC 5171 */
21
22#include <sys/cdefs.h>
23#ifndef lint
24__RCSID("$NetBSD: print-udld.c,v 1.4 2023/08/17 20:19:40 christos Exp $");
25#endif
26
27#ifdef HAVE_CONFIG_H
28#include <config.h>
29#endif
30
31#include "netdissect-stdinc.h"
32
33#define ND_LONGJMP_FROM_TCHECK
34#include "netdissect.h"
35#include "extract.h"
36
37
38#define UDLD_HEADER_LEN			4
39#define UDLD_TLV_HEADER_LEN		4
40#define UDLD_DEVICE_ID_TLV		0x0001
41#define UDLD_PORT_ID_TLV		0x0002
42#define UDLD_ECHO_TLV			0x0003
43#define UDLD_MESSAGE_INTERVAL_TLV	0x0004
44#define UDLD_TIMEOUT_INTERVAL_TLV	0x0005
45#define UDLD_DEVICE_NAME_TLV		0x0006
46#define UDLD_SEQ_NUMBER_TLV		0x0007
47
48static const struct tok udld_tlv_values[] = {
49    { UDLD_DEVICE_ID_TLV, "Device-ID TLV"},
50    { UDLD_PORT_ID_TLV, "Port-ID TLV"},
51    { UDLD_ECHO_TLV, "Echo TLV"},
52    { UDLD_MESSAGE_INTERVAL_TLV, "Message Interval TLV"},
53    { UDLD_TIMEOUT_INTERVAL_TLV, "Timeout Interval TLV"},
54    { UDLD_DEVICE_NAME_TLV, "Device Name TLV"},
55    { UDLD_SEQ_NUMBER_TLV,"Sequence Number TLV"},
56    { 0, NULL}
57};
58
59static const struct tok udld_code_values[] = {
60    { 0x00, "Reserved"},
61    { 0x01, "Probe message"},
62    { 0x02, "Echo message"},
63    { 0x03, "Flush message"},
64    { 0, NULL}
65};
66
67static const struct tok udld_flags_bitmap_str[] = {
68    { 1U << 0, "RT"    },
69    { 1U << 1, "RSY"   },
70    { 1U << 2, "MBZ-2" },
71    { 1U << 3, "MBZ-3" },
72    { 1U << 4, "MBZ-4" },
73    { 1U << 5, "MBZ-5" },
74    { 1U << 6, "MBZ-6" },
75    { 1U << 7, "MBZ-7" },
76    { 0, NULL}
77};
78
79/*
80 * UDLD's Protocol Data Unit format:
81 *
82 *  0                   1                   2                   3
83 *  0 1 2 3 4 5 6 7 8 9 0 1 2 3 4 5 6 7 8 9 0 1 2 3 4 5 6 7 8 9 0 1
84 * +-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+
85 * | Ver | Opcode  |     Flags     |           Checksum            |
86 * +-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+
87 * |               List of TLVs (variable length list)             |
88 * |                              ...                              |
89 * +-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+
90 *
91 * TLV format:
92 *
93 *  0                   1                   2                   3
94 *  0 1 2 3 4 5 6 7 8 9 0 1 2 3 4 5 6 7 8 9 0 1 2 3 4 5 6 7 8 9 0 1
95 * +-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+
96 * |             TYPE              |            LENGTH             |
97 * +-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+
98 * |                             VALUE                             |
99 * |                              ...                              |
100 * +-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+
101 *
102 * LENGTH: Length in bytes of the Type, Length, and Value fields.
103 */
104
105#define	UDLD_EXTRACT_VERSION(x) (((x)&0xe0)>>5)
106#define	UDLD_EXTRACT_OPCODE(x) ((x)&0x1f)
107
108void
109udld_print(netdissect_options *ndo,
110           const u_char *tptr, u_int length)
111{
112    uint8_t ver, code, flags;
113
114    ndo->ndo_protocol = "udld";
115    if (length < UDLD_HEADER_LEN)
116        goto invalid;
117
118    ver = UDLD_EXTRACT_VERSION(GET_U_1(tptr));
119    code = UDLD_EXTRACT_OPCODE(GET_U_1(tptr));
120    tptr += 1;
121    length -= 1;
122
123    flags = GET_U_1(tptr);
124    tptr += 1;
125    length -= 1;
126
127    ND_PRINT("UDLDv%u, Code %s (%x), Flags [%s] (0x%02x), length %u",
128           ver,
129           tok2str(udld_code_values, "Reserved", code),
130           code,
131           bittok2str(udld_flags_bitmap_str, "none", flags),
132           flags,
133           length + 2);
134
135    /*
136     * In non-verbose mode, just print version and opcode type
137     */
138    if (ndo->ndo_vflag < 1) {
139        goto tcheck_remainder;
140    }
141
142    ND_PRINT("\n\tChecksum 0x%04x (unverified)", GET_BE_U_2(tptr));
143    tptr += 2;
144    length -= 2;
145
146    while (length) {
147        uint16_t type, len;
148
149        if (length < UDLD_TLV_HEADER_LEN)
150            goto invalid;
151
152	type = GET_BE_U_2(tptr);
153        tptr += 2;
154        length -= 2;
155
156        len  = GET_BE_U_2(tptr);
157        tptr += 2;
158        length -= 2;
159
160        ND_PRINT("\n\t%s (0x%04x) TLV, length %u",
161               tok2str(udld_tlv_values, "Unknown", type),
162               type, len);
163
164        /* infinite loop check */
165        if (len <= UDLD_TLV_HEADER_LEN)
166            goto invalid;
167
168        len -= UDLD_TLV_HEADER_LEN;
169        if (length < len)
170            goto invalid;
171
172        switch (type) {
173        case UDLD_DEVICE_ID_TLV:
174        case UDLD_PORT_ID_TLV:
175        case UDLD_DEVICE_NAME_TLV:
176            ND_PRINT(", ");
177            nd_printjnp(ndo, tptr, len);
178            break;
179
180        case UDLD_ECHO_TLV:
181            ND_PRINT(", ");
182            (void)nd_printn(ndo, tptr, len, NULL);
183            break;
184
185        case UDLD_MESSAGE_INTERVAL_TLV:
186        case UDLD_TIMEOUT_INTERVAL_TLV:
187            if (len != 1)
188                goto invalid;
189            ND_PRINT(", %us", (GET_U_1(tptr)));
190            break;
191
192        case UDLD_SEQ_NUMBER_TLV:
193            if (len != 4)
194                goto invalid;
195            ND_PRINT(", %u", GET_BE_U_4(tptr));
196            break;
197
198        default:
199            ND_TCHECK_LEN(tptr, len);
200            break;
201        }
202        tptr += len;
203        length -= len;
204    }
205
206    return;
207
208invalid:
209    nd_print_invalid(ndo);
210tcheck_remainder:
211    ND_TCHECK_LEN(tptr, length);
212}
213