blob: d4cfc587ee3563f23f6e0fc63319237dcae6c148 [file] [log] [blame]
Tom Rinif739fcd2018-05-07 17:02:21 -04001// SPDX-License-Identifier: GPL-2.0
Joe Hershbergerf575ae12012-05-23 07:57:59 +00002/*
3 * Copied from Linux Monitor (LiMon) - Networking.
4 *
5 * Copyright 1994 - 2000 Neil Russell.
6 * (See License)
7 * Copyright 2000 Roland Borde
8 * Copyright 2000 Paolo Scaffardi
9 * Copyright 2000-2002 Wolfgang Denk, wd@denx.de
10 */
11
Joe Hershbergerf575ae12012-05-23 07:57:59 +000012#include <net.h>
Joe Hershbergerf575ae12012-05-23 07:57:59 +000013
14#include "cdp.h"
15
16/* Ethernet bcast address */
Joe Hershberger0adb5b72015-04-08 01:41:04 -050017const u8 net_cdp_ethaddr[6] = { 0x01, 0x00, 0x0c, 0xcc, 0xcc, 0xcc };
Joe Hershbergerf575ae12012-05-23 07:57:59 +000018
19#define CDP_DEVICE_ID_TLV 0x0001
20#define CDP_ADDRESS_TLV 0x0002
21#define CDP_PORT_ID_TLV 0x0003
22#define CDP_CAPABILITIES_TLV 0x0004
23#define CDP_VERSION_TLV 0x0005
24#define CDP_PLATFORM_TLV 0x0006
25#define CDP_NATIVE_VLAN_TLV 0x000a
26#define CDP_APPLIANCE_VLAN_TLV 0x000e
27#define CDP_TRIGGER_TLV 0x000f
28#define CDP_POWER_CONSUMPTION_TLV 0x0010
29#define CDP_SYSNAME_TLV 0x0014
30#define CDP_SYSOBJECT_TLV 0x0015
31#define CDP_MANAGEMENT_ADDRESS_TLV 0x0016
32
33#define CDP_TIMEOUT 250UL /* one packet every 250ms */
34
Joe Hershberger6aede5b2015-04-08 01:41:14 -050035static int cdp_seq;
36static int cdp_ok;
Joe Hershbergerf575ae12012-05-23 07:57:59 +000037
Joe Hershberger6aede5b2015-04-08 01:41:14 -050038ushort cdp_native_vlan;
39ushort cdp_appliance_vlan;
Joe Hershbergerf575ae12012-05-23 07:57:59 +000040
Joe Hershberger6aede5b2015-04-08 01:41:14 -050041static const uchar cdp_snap_hdr[8] = {
Joe Hershbergerf575ae12012-05-23 07:57:59 +000042 0xAA, 0xAA, 0x03, 0x00, 0x00, 0x0C, 0x20, 0x00 };
43
Joe Hershberger6aede5b2015-04-08 01:41:14 -050044static ushort cdp_compute_csum(const uchar *buff, ushort len)
Joe Hershbergerf575ae12012-05-23 07:57:59 +000045{
46 ushort csum;
47 int odd;
48 ulong result = 0;
49 ushort leftover;
50 ushort *p;
51
52 if (len > 0) {
53 odd = 1 & (ulong)buff;
54 if (odd) {
55 result = *buff << 8;
56 len--;
57 buff++;
58 }
59 while (len > 1) {
60 p = (ushort *)buff;
61 result += *p++;
62 buff = (uchar *)p;
63 if (result & 0x80000000)
64 result = (result & 0xFFFF) + (result >> 16);
65 len -= 2;
66 }
67 if (len) {
68 leftover = (signed short)(*(const signed char *)buff);
69 /*
70 * CISCO SUCKS big time! (and blows too):
71 * CDP uses the IP checksum algorithm with a twist;
72 * for the last byte it *sign* extends and sums.
73 */
74 result = (result & 0xffff0000) |
75 ((result + leftover) & 0x0000ffff);
76 }
77 while (result >> 16)
78 result = (result & 0xFFFF) + (result >> 16);
79
80 if (odd)
81 result = ((result >> 8) & 0xff) |
82 ((result & 0xff) << 8);
83 }
84
85 /* add up 16-bit and 17-bit words for 17+c bits */
86 result = (result & 0xffff) + (result >> 16);
87 /* add up 16-bit and 2-bit for 16+c bit */
88 result = (result & 0xffff) + (result >> 16);
89 /* add up carry.. */
90 result = (result & 0xffff) + (result >> 16);
91
92 /* negate */
93 csum = ~(ushort)result;
94
95 /* run time endian detection */
96 if (csum != htons(csum)) /* little endian */
97 csum = htons(csum);
98
99 return csum;
100}
101
Joe Hershberger6aede5b2015-04-08 01:41:14 -0500102static int cdp_send_trigger(void)
Joe Hershbergerf575ae12012-05-23 07:57:59 +0000103{
104 uchar *pkt;
105 ushort *s;
106 ushort *cp;
Joe Hershbergercb487f52012-05-23 07:58:06 +0000107 struct ethernet_hdr *et;
Joe Hershbergerf575ae12012-05-23 07:57:59 +0000108 int len;
109 ushort chksum;
110#if defined(CONFIG_CDP_DEVICE_ID) || defined(CONFIG_CDP_PORT_ID) || \
111 defined(CONFIG_CDP_VERSION) || defined(CONFIG_CDP_PLATFORM)
112 char buf[32];
113#endif
114
Joe Hershberger1203fcc2015-04-08 01:41:05 -0500115 pkt = net_tx_packet;
Joe Hershbergercb487f52012-05-23 07:58:06 +0000116 et = (struct ethernet_hdr *)pkt;
Joe Hershbergerf575ae12012-05-23 07:57:59 +0000117
118 /* NOTE: trigger sent not on any VLAN */
119
120 /* form ethernet header */
Joe Hershberger0adb5b72015-04-08 01:41:04 -0500121 memcpy(et->et_dest, net_cdp_ethaddr, 6);
122 memcpy(et->et_src, net_ethaddr, 6);
Joe Hershbergerf575ae12012-05-23 07:57:59 +0000123
124 pkt += ETHER_HDR_SIZE;
125
126 /* SNAP header */
Joe Hershberger6aede5b2015-04-08 01:41:14 -0500127 memcpy((uchar *)pkt, cdp_snap_hdr, sizeof(cdp_snap_hdr));
128 pkt += sizeof(cdp_snap_hdr);
Joe Hershbergerf575ae12012-05-23 07:57:59 +0000129
130 /* CDP header */
131 *pkt++ = 0x02; /* CDP version 2 */
132 *pkt++ = 180; /* TTL */
133 s = (ushort *)pkt;
134 cp = s;
135 /* checksum (0 for later calculation) */
136 *s++ = htons(0);
137
138 /* CDP fields */
139#ifdef CONFIG_CDP_DEVICE_ID
140 *s++ = htons(CDP_DEVICE_ID_TLV);
141 *s++ = htons(CONFIG_CDP_DEVICE_ID);
Joe Hershberger0adb5b72015-04-08 01:41:04 -0500142 sprintf(buf, CONFIG_CDP_DEVICE_ID_PREFIX "%pm", net_ethaddr);
Joe Hershbergerf575ae12012-05-23 07:57:59 +0000143 memcpy((uchar *)s, buf, 16);
144 s += 16 / 2;
145#endif
146
147#ifdef CONFIG_CDP_PORT_ID
148 *s++ = htons(CDP_PORT_ID_TLV);
149 memset(buf, 0, sizeof(buf));
150 sprintf(buf, CONFIG_CDP_PORT_ID, eth_get_dev_index());
151 len = strlen(buf);
152 if (len & 1) /* make it even */
153 len++;
154 *s++ = htons(len + 4);
155 memcpy((uchar *)s, buf, len);
156 s += len / 2;
157#endif
158
159#ifdef CONFIG_CDP_CAPABILITIES
160 *s++ = htons(CDP_CAPABILITIES_TLV);
161 *s++ = htons(8);
162 *(ulong *)s = htonl(CONFIG_CDP_CAPABILITIES);
163 s += 2;
164#endif
165
166#ifdef CONFIG_CDP_VERSION
167 *s++ = htons(CDP_VERSION_TLV);
168 memset(buf, 0, sizeof(buf));
169 strcpy(buf, CONFIG_CDP_VERSION);
170 len = strlen(buf);
171 if (len & 1) /* make it even */
172 len++;
173 *s++ = htons(len + 4);
174 memcpy((uchar *)s, buf, len);
175 s += len / 2;
176#endif
177
178#ifdef CONFIG_CDP_PLATFORM
179 *s++ = htons(CDP_PLATFORM_TLV);
180 memset(buf, 0, sizeof(buf));
181 strcpy(buf, CONFIG_CDP_PLATFORM);
182 len = strlen(buf);
183 if (len & 1) /* make it even */
184 len++;
185 *s++ = htons(len + 4);
186 memcpy((uchar *)s, buf, len);
187 s += len / 2;
188#endif
189
190#ifdef CONFIG_CDP_TRIGGER
191 *s++ = htons(CDP_TRIGGER_TLV);
192 *s++ = htons(8);
193 *(ulong *)s = htonl(CONFIG_CDP_TRIGGER);
194 s += 2;
195#endif
196
197#ifdef CONFIG_CDP_POWER_CONSUMPTION
198 *s++ = htons(CDP_POWER_CONSUMPTION_TLV);
199 *s++ = htons(6);
200 *s++ = htons(CONFIG_CDP_POWER_CONSUMPTION);
201#endif
202
203 /* length of ethernet packet */
Joe Hershberger1203fcc2015-04-08 01:41:05 -0500204 len = (uchar *)s - ((uchar *)net_tx_packet + ETHER_HDR_SIZE);
Joe Hershbergerf575ae12012-05-23 07:57:59 +0000205 et->et_protlen = htons(len);
206
Joe Hershberger6aede5b2015-04-08 01:41:14 -0500207 len = ETHER_HDR_SIZE + sizeof(cdp_snap_hdr);
208 chksum = cdp_compute_csum((uchar *)net_tx_packet + len,
Joe Hershberger1203fcc2015-04-08 01:41:05 -0500209 (uchar *)s - (net_tx_packet + len));
Joe Hershbergerf575ae12012-05-23 07:57:59 +0000210 if (chksum == 0)
211 chksum = 0xFFFF;
212 *cp = htons(chksum);
213
Joe Hershberger1203fcc2015-04-08 01:41:05 -0500214 net_send_packet(net_tx_packet, (uchar *)s - net_tx_packet);
Joe Hershbergerf575ae12012-05-23 07:57:59 +0000215 return 0;
216}
217
Joe Hershberger6aede5b2015-04-08 01:41:14 -0500218static void cdp_timeout_handler(void)
Joe Hershbergerf575ae12012-05-23 07:57:59 +0000219{
Joe Hershberger6aede5b2015-04-08 01:41:14 -0500220 cdp_seq++;
Joe Hershbergerf575ae12012-05-23 07:57:59 +0000221
Joe Hershberger6aede5b2015-04-08 01:41:14 -0500222 if (cdp_seq < 3) {
Joe Hershbergerbc0571f2015-04-08 01:41:21 -0500223 net_set_timeout_handler(CDP_TIMEOUT, cdp_timeout_handler);
Joe Hershberger6aede5b2015-04-08 01:41:14 -0500224 cdp_send_trigger();
Joe Hershbergerf575ae12012-05-23 07:57:59 +0000225 return;
226 }
227
228 /* if not OK try again */
Joe Hershberger6aede5b2015-04-08 01:41:14 -0500229 if (!cdp_ok)
Joe Hershbergerbc0571f2015-04-08 01:41:21 -0500230 net_start_again();
Joe Hershbergerf575ae12012-05-23 07:57:59 +0000231 else
Joe Hershberger22f6e992012-05-23 07:59:14 +0000232 net_set_state(NETLOOP_SUCCESS);
Joe Hershbergerf575ae12012-05-23 07:57:59 +0000233}
234
Joe Hershberger0b4c5ff2012-05-23 07:58:13 +0000235void cdp_receive(const uchar *pkt, unsigned len)
Joe Hershbergerf575ae12012-05-23 07:57:59 +0000236{
237 const uchar *t;
238 const ushort *ss;
239 ushort type, tlen;
240 ushort vlan, nvlan;
241
242 /* minimum size? */
Joe Hershberger6aede5b2015-04-08 01:41:14 -0500243 if (len < sizeof(cdp_snap_hdr) + 4)
Joe Hershbergerf575ae12012-05-23 07:57:59 +0000244 goto pkt_short;
245
246 /* check for valid CDP SNAP header */
Joe Hershberger6aede5b2015-04-08 01:41:14 -0500247 if (memcmp(pkt, cdp_snap_hdr, sizeof(cdp_snap_hdr)) != 0)
Joe Hershbergerf575ae12012-05-23 07:57:59 +0000248 return;
249
Joe Hershberger6aede5b2015-04-08 01:41:14 -0500250 pkt += sizeof(cdp_snap_hdr);
251 len -= sizeof(cdp_snap_hdr);
Joe Hershbergerf575ae12012-05-23 07:57:59 +0000252
253 /* Version of CDP protocol must be >= 2 and TTL != 0 */
254 if (pkt[0] < 0x02 || pkt[1] == 0)
255 return;
256
257 /*
258 * if version is greater than 0x02 maybe we'll have a problem;
259 * output a warning
260 */
261 if (pkt[0] != 0x02)
262 printf("**WARNING: CDP packet received with a protocol version "
263 "%d > 2\n", pkt[0] & 0xff);
264
Joe Hershberger6aede5b2015-04-08 01:41:14 -0500265 if (cdp_compute_csum(pkt, len) != 0)
Joe Hershbergerf575ae12012-05-23 07:57:59 +0000266 return;
267
268 pkt += 4;
269 len -= 4;
270
271 vlan = htons(-1);
272 nvlan = htons(-1);
273 while (len > 0) {
274 if (len < 4)
275 goto pkt_short;
276
277 ss = (const ushort *)pkt;
278 type = ntohs(ss[0]);
279 tlen = ntohs(ss[1]);
280 if (tlen > len)
281 goto pkt_short;
282
283 pkt += tlen;
284 len -= tlen;
285
286 ss += 2; /* point ss to the data of the TLV */
287 tlen -= 4;
288
289 switch (type) {
290 case CDP_DEVICE_ID_TLV:
291 break;
292 case CDP_ADDRESS_TLV:
293 break;
294 case CDP_PORT_ID_TLV:
295 break;
296 case CDP_CAPABILITIES_TLV:
297 break;
298 case CDP_VERSION_TLV:
299 break;
300 case CDP_PLATFORM_TLV:
301 break;
302 case CDP_NATIVE_VLAN_TLV:
303 nvlan = *ss;
304 break;
305 case CDP_APPLIANCE_VLAN_TLV:
306 t = (const uchar *)ss;
307 while (tlen > 0) {
308 if (tlen < 3)
309 goto pkt_short;
310
311 ss = (const ushort *)(t + 1);
312
313#ifdef CONFIG_CDP_APPLIANCE_VLAN_TYPE
314 if (t[0] == CONFIG_CDP_APPLIANCE_VLAN_TYPE)
315 vlan = *ss;
316#else
317 /* XXX will this work; dunno */
318 vlan = ntohs(*ss);
319#endif
320 t += 3; tlen -= 3;
321 }
322 break;
323 case CDP_TRIGGER_TLV:
324 break;
325 case CDP_POWER_CONSUMPTION_TLV:
326 break;
327 case CDP_SYSNAME_TLV:
328 break;
329 case CDP_SYSOBJECT_TLV:
330 break;
331 case CDP_MANAGEMENT_ADDRESS_TLV:
332 break;
333 }
334 }
335
Joe Hershberger6aede5b2015-04-08 01:41:14 -0500336 cdp_appliance_vlan = vlan;
337 cdp_native_vlan = nvlan;
Joe Hershbergerf575ae12012-05-23 07:57:59 +0000338
Joe Hershberger6aede5b2015-04-08 01:41:14 -0500339 cdp_ok = 1;
Joe Hershbergerf575ae12012-05-23 07:57:59 +0000340 return;
341
Joe Hershberger6aede5b2015-04-08 01:41:14 -0500342pkt_short:
Joe Hershbergerf575ae12012-05-23 07:57:59 +0000343 printf("** CDP packet is too short\n");
344 return;
345}
346
Joe Hershberger6aede5b2015-04-08 01:41:14 -0500347void cdp_start(void)
Joe Hershbergerf575ae12012-05-23 07:57:59 +0000348{
349 printf("Using %s device\n", eth_get_name());
Joe Hershberger6aede5b2015-04-08 01:41:14 -0500350 cdp_seq = 0;
351 cdp_ok = 0;
Joe Hershbergerf575ae12012-05-23 07:57:59 +0000352
Joe Hershberger6aede5b2015-04-08 01:41:14 -0500353 cdp_native_vlan = htons(-1);
354 cdp_appliance_vlan = htons(-1);
Joe Hershbergerf575ae12012-05-23 07:57:59 +0000355
Joe Hershbergerbc0571f2015-04-08 01:41:21 -0500356 net_set_timeout_handler(CDP_TIMEOUT, cdp_timeout_handler);
Joe Hershbergerf575ae12012-05-23 07:57:59 +0000357
Joe Hershberger6aede5b2015-04-08 01:41:14 -0500358 cdp_send_trigger();
Joe Hershbergerf575ae12012-05-23 07:57:59 +0000359}