uboot/net/cdp.c
<<
>>
Prefs
   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 */
  21const 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
  39static int cdp_seq;
  40static int cdp_ok;
  41
  42ushort cdp_native_vlan;
  43ushort cdp_appliance_vlan;
  44
  45static const uchar cdp_snap_hdr[8] = {
  46        0xAA, 0xAA, 0x03, 0x00, 0x00, 0x0C, 0x20, 0x00 };
  47
  48static 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
 106static 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
 222static 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
 239void 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
 346pkt_short:
 347        printf("** CDP packet is too short\n");
 348        return;
 349}
 350
 351void 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