xref: /openbmc/u-boot/net/cdp.c (revision ef64e782)
1 // SPDX-License-Identifier: GPL-2.0
2 /*
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 
12 #include <common.h>
13 #include <net.h>
14 #if defined(CONFIG_CDP_VERSION)
15 #include <timestamp.h>
16 #endif
17 
18 #include "cdp.h"
19 
20 /* Ethernet bcast address */
21 const u8 net_cdp_ethaddr[6] = { 0x01, 0x00, 0x0c, 0xcc, 0xcc, 0xcc };
22 
23 #define CDP_DEVICE_ID_TLV		0x0001
24 #define CDP_ADDRESS_TLV			0x0002
25 #define CDP_PORT_ID_TLV			0x0003
26 #define CDP_CAPABILITIES_TLV		0x0004
27 #define CDP_VERSION_TLV			0x0005
28 #define CDP_PLATFORM_TLV		0x0006
29 #define CDP_NATIVE_VLAN_TLV		0x000a
30 #define CDP_APPLIANCE_VLAN_TLV		0x000e
31 #define CDP_TRIGGER_TLV			0x000f
32 #define CDP_POWER_CONSUMPTION_TLV	0x0010
33 #define CDP_SYSNAME_TLV			0x0014
34 #define CDP_SYSOBJECT_TLV		0x0015
35 #define CDP_MANAGEMENT_ADDRESS_TLV	0x0016
36 
37 #define CDP_TIMEOUT			250UL	/* one packet every 250ms */
38 
39 static int cdp_seq;
40 static int cdp_ok;
41 
42 ushort cdp_native_vlan;
43 ushort cdp_appliance_vlan;
44 
45 static const uchar cdp_snap_hdr[8] = {
46 	0xAA, 0xAA, 0x03, 0x00, 0x00, 0x0C, 0x20, 0x00 };
47 
48 static ushort cdp_compute_csum(const uchar *buff, ushort len)
49 {
50 	ushort csum;
51 	int     odd;
52 	ulong   result = 0;
53 	ushort  leftover;
54 	ushort *p;
55 
56 	if (len > 0) {
57 		odd = 1 & (ulong)buff;
58 		if (odd) {
59 			result = *buff << 8;
60 			len--;
61 			buff++;
62 		}
63 		while (len > 1) {
64 			p = (ushort *)buff;
65 			result += *p++;
66 			buff = (uchar *)p;
67 			if (result & 0x80000000)
68 				result = (result & 0xFFFF) + (result >> 16);
69 			len -= 2;
70 		}
71 		if (len) {
72 			leftover = (signed short)(*(const signed char *)buff);
73 			/*
74 			 * CISCO SUCKS big time! (and blows too):
75 			 * CDP uses the IP checksum algorithm with a twist;
76 			 * for the last byte it *sign* extends and sums.
77 			 */
78 			result = (result & 0xffff0000) |
79 				 ((result + leftover) & 0x0000ffff);
80 		}
81 		while (result >> 16)
82 			result = (result & 0xFFFF) + (result >> 16);
83 
84 		if (odd)
85 			result = ((result >> 8) & 0xff) |
86 				 ((result & 0xff) << 8);
87 	}
88 
89 	/* add up 16-bit and 17-bit words for 17+c bits */
90 	result = (result & 0xffff) + (result >> 16);
91 	/* add up 16-bit and 2-bit for 16+c bit */
92 	result = (result & 0xffff) + (result >> 16);
93 	/* add up carry.. */
94 	result = (result & 0xffff) + (result >> 16);
95 
96 	/* negate */
97 	csum = ~(ushort)result;
98 
99 	/* run time endian detection */
100 	if (csum != htons(csum))	/* little endian */
101 		csum = htons(csum);
102 
103 	return csum;
104 }
105 
106 static int cdp_send_trigger(void)
107 {
108 	uchar *pkt;
109 	ushort *s;
110 	ushort *cp;
111 	struct ethernet_hdr *et;
112 	int len;
113 	ushort chksum;
114 #if	defined(CONFIG_CDP_DEVICE_ID) || defined(CONFIG_CDP_PORT_ID)   || \
115 	defined(CONFIG_CDP_VERSION)   || defined(CONFIG_CDP_PLATFORM)
116 	char buf[32];
117 #endif
118 
119 	pkt = net_tx_packet;
120 	et = (struct ethernet_hdr *)pkt;
121 
122 	/* NOTE: trigger sent not on any VLAN */
123 
124 	/* form ethernet header */
125 	memcpy(et->et_dest, net_cdp_ethaddr, 6);
126 	memcpy(et->et_src, net_ethaddr, 6);
127 
128 	pkt += ETHER_HDR_SIZE;
129 
130 	/* SNAP header */
131 	memcpy((uchar *)pkt, cdp_snap_hdr, sizeof(cdp_snap_hdr));
132 	pkt += sizeof(cdp_snap_hdr);
133 
134 	/* CDP header */
135 	*pkt++ = 0x02;				/* CDP version 2 */
136 	*pkt++ = 180;				/* TTL */
137 	s = (ushort *)pkt;
138 	cp = s;
139 	/* checksum (0 for later calculation) */
140 	*s++ = htons(0);
141 
142 	/* CDP fields */
143 #ifdef CONFIG_CDP_DEVICE_ID
144 	*s++ = htons(CDP_DEVICE_ID_TLV);
145 	*s++ = htons(CONFIG_CDP_DEVICE_ID);
146 	sprintf(buf, CONFIG_CDP_DEVICE_ID_PREFIX "%pm", net_ethaddr);
147 	memcpy((uchar *)s, buf, 16);
148 	s += 16 / 2;
149 #endif
150 
151 #ifdef CONFIG_CDP_PORT_ID
152 	*s++ = htons(CDP_PORT_ID_TLV);
153 	memset(buf, 0, sizeof(buf));
154 	sprintf(buf, CONFIG_CDP_PORT_ID, eth_get_dev_index());
155 	len = strlen(buf);
156 	if (len & 1)	/* make it even */
157 		len++;
158 	*s++ = htons(len + 4);
159 	memcpy((uchar *)s, buf, len);
160 	s += len / 2;
161 #endif
162 
163 #ifdef CONFIG_CDP_CAPABILITIES
164 	*s++ = htons(CDP_CAPABILITIES_TLV);
165 	*s++ = htons(8);
166 	*(ulong *)s = htonl(CONFIG_CDP_CAPABILITIES);
167 	s += 2;
168 #endif
169 
170 #ifdef CONFIG_CDP_VERSION
171 	*s++ = htons(CDP_VERSION_TLV);
172 	memset(buf, 0, sizeof(buf));
173 	strcpy(buf, CONFIG_CDP_VERSION);
174 	len = strlen(buf);
175 	if (len & 1)	/* make it even */
176 		len++;
177 	*s++ = htons(len + 4);
178 	memcpy((uchar *)s, buf, len);
179 	s += len / 2;
180 #endif
181 
182 #ifdef CONFIG_CDP_PLATFORM
183 	*s++ = htons(CDP_PLATFORM_TLV);
184 	memset(buf, 0, sizeof(buf));
185 	strcpy(buf, CONFIG_CDP_PLATFORM);
186 	len = strlen(buf);
187 	if (len & 1)	/* make it even */
188 		len++;
189 	*s++ = htons(len + 4);
190 	memcpy((uchar *)s, buf, len);
191 	s += len / 2;
192 #endif
193 
194 #ifdef CONFIG_CDP_TRIGGER
195 	*s++ = htons(CDP_TRIGGER_TLV);
196 	*s++ = htons(8);
197 	*(ulong *)s = htonl(CONFIG_CDP_TRIGGER);
198 	s += 2;
199 #endif
200 
201 #ifdef CONFIG_CDP_POWER_CONSUMPTION
202 	*s++ = htons(CDP_POWER_CONSUMPTION_TLV);
203 	*s++ = htons(6);
204 	*s++ = htons(CONFIG_CDP_POWER_CONSUMPTION);
205 #endif
206 
207 	/* length of ethernet packet */
208 	len = (uchar *)s - ((uchar *)net_tx_packet + ETHER_HDR_SIZE);
209 	et->et_protlen = htons(len);
210 
211 	len = ETHER_HDR_SIZE + sizeof(cdp_snap_hdr);
212 	chksum = cdp_compute_csum((uchar *)net_tx_packet + len,
213 				  (uchar *)s - (net_tx_packet + len));
214 	if (chksum == 0)
215 		chksum = 0xFFFF;
216 	*cp = htons(chksum);
217 
218 	net_send_packet(net_tx_packet, (uchar *)s - net_tx_packet);
219 	return 0;
220 }
221 
222 static void cdp_timeout_handler(void)
223 {
224 	cdp_seq++;
225 
226 	if (cdp_seq < 3) {
227 		net_set_timeout_handler(CDP_TIMEOUT, cdp_timeout_handler);
228 		cdp_send_trigger();
229 		return;
230 	}
231 
232 	/* if not OK try again */
233 	if (!cdp_ok)
234 		net_start_again();
235 	else
236 		net_set_state(NETLOOP_SUCCESS);
237 }
238 
239 void cdp_receive(const uchar *pkt, unsigned len)
240 {
241 	const uchar *t;
242 	const ushort *ss;
243 	ushort type, tlen;
244 	ushort vlan, nvlan;
245 
246 	/* minimum size? */
247 	if (len < sizeof(cdp_snap_hdr) + 4)
248 		goto pkt_short;
249 
250 	/* check for valid CDP SNAP header */
251 	if (memcmp(pkt, cdp_snap_hdr, sizeof(cdp_snap_hdr)) != 0)
252 		return;
253 
254 	pkt += sizeof(cdp_snap_hdr);
255 	len -= sizeof(cdp_snap_hdr);
256 
257 	/* Version of CDP protocol must be >= 2 and TTL != 0 */
258 	if (pkt[0] < 0x02 || pkt[1] == 0)
259 		return;
260 
261 	/*
262 	 * if version is greater than 0x02 maybe we'll have a problem;
263 	 * output a warning
264 	 */
265 	if (pkt[0] != 0x02)
266 		printf("**WARNING: CDP packet received with a protocol version "
267 				"%d > 2\n", pkt[0] & 0xff);
268 
269 	if (cdp_compute_csum(pkt, len) != 0)
270 		return;
271 
272 	pkt += 4;
273 	len -= 4;
274 
275 	vlan = htons(-1);
276 	nvlan = htons(-1);
277 	while (len > 0) {
278 		if (len < 4)
279 			goto pkt_short;
280 
281 		ss = (const ushort *)pkt;
282 		type = ntohs(ss[0]);
283 		tlen = ntohs(ss[1]);
284 		if (tlen > len)
285 			goto pkt_short;
286 
287 		pkt += tlen;
288 		len -= tlen;
289 
290 		ss += 2;	/* point ss to the data of the TLV */
291 		tlen -= 4;
292 
293 		switch (type) {
294 		case CDP_DEVICE_ID_TLV:
295 			break;
296 		case CDP_ADDRESS_TLV:
297 			break;
298 		case CDP_PORT_ID_TLV:
299 			break;
300 		case CDP_CAPABILITIES_TLV:
301 			break;
302 		case CDP_VERSION_TLV:
303 			break;
304 		case CDP_PLATFORM_TLV:
305 			break;
306 		case CDP_NATIVE_VLAN_TLV:
307 			nvlan = *ss;
308 			break;
309 		case CDP_APPLIANCE_VLAN_TLV:
310 			t = (const uchar *)ss;
311 			while (tlen > 0) {
312 				if (tlen < 3)
313 					goto pkt_short;
314 
315 				ss = (const ushort *)(t + 1);
316 
317 #ifdef CONFIG_CDP_APPLIANCE_VLAN_TYPE
318 				if (t[0] == CONFIG_CDP_APPLIANCE_VLAN_TYPE)
319 					vlan = *ss;
320 #else
321 				/* XXX will this work; dunno */
322 				vlan = ntohs(*ss);
323 #endif
324 				t += 3; tlen -= 3;
325 			}
326 			break;
327 		case CDP_TRIGGER_TLV:
328 			break;
329 		case CDP_POWER_CONSUMPTION_TLV:
330 			break;
331 		case CDP_SYSNAME_TLV:
332 			break;
333 		case CDP_SYSOBJECT_TLV:
334 			break;
335 		case CDP_MANAGEMENT_ADDRESS_TLV:
336 			break;
337 		}
338 	}
339 
340 	cdp_appliance_vlan = vlan;
341 	cdp_native_vlan = nvlan;
342 
343 	cdp_ok = 1;
344 	return;
345 
346 pkt_short:
347 	printf("** CDP packet is too short\n");
348 	return;
349 }
350 
351 void cdp_start(void)
352 {
353 	printf("Using %s device\n", eth_get_name());
354 	cdp_seq = 0;
355 	cdp_ok = 0;
356 
357 	cdp_native_vlan = htons(-1);
358 	cdp_appliance_vlan = htons(-1);
359 
360 	net_set_timeout_handler(CDP_TIMEOUT, cdp_timeout_handler);
361 
362 	cdp_send_trigger();
363 }
364