net: eth-uclass: add return value check
[platform/kernel/u-boot.git] / net / cdp.c
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 }