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