Coverage Report

Created: 2025-07-11 06:38

/src/lldpd/src/daemon/protocols/edp.c
Line
Count
Source (jump to first uncovered line)
1
/* -*- mode: c; c-file-style: "openbsd" -*- */
2
/*
3
 * Copyright (c) 2008 Vincent Bernat <bernat@luffy.cx>
4
 *
5
 * Permission to use, copy, modify, and/or distribute this software for any
6
 * purpose with or without fee is hereby granted, provided that the above
7
 * copyright notice and this permission notice appear in all copies.
8
 *
9
 * THE SOFTWARE IS PROVIDED "AS IS" AND THE AUTHOR DISCLAIMS ALL WARRANTIES
10
 * WITH REGARD TO THIS SOFTWARE INCLUDING ALL IMPLIED WARRANTIES OF
11
 * MERCHANTABILITY AND FITNESS. IN NO EVENT SHALL THE AUTHOR BE LIABLE FOR
12
 * ANY SPECIAL, DIRECT, INDIRECT, OR CONSEQUENTIAL DAMAGES OR ANY DAMAGES
13
 * WHATSOEVER RESULTING FROM LOSS OF USE, DATA OR PROFITS, WHETHER IN AN
14
 * ACTION OF CONTRACT, NEGLIGENCE OR OTHER TORTIOUS ACTION, ARISING OUT OF
15
 * OR IN CONNECTION WITH THE USE OR PERFORMANCE OF THIS SOFTWARE.
16
 */
17
18
#include "../lldpd.h"
19
#include "../frame.h"
20
21
#ifdef ENABLE_EDP
22
23
#  include <stdio.h>
24
#  include <unistd.h>
25
#  include <errno.h>
26
#  include <arpa/inet.h>
27
#  include <fnmatch.h>
28
#  include <sys/param.h>
29
30
static int seq = 0;
31
32
int
33
edp_send(struct lldpd *global, struct lldpd_hardware *hardware)
34
0
{
35
0
  const u_int8_t mcastaddr[] = EDP_MULTICAST_ADDR;
36
0
  const u_int8_t llcorg[] = LLC_ORG_EXTREME;
37
0
  struct lldpd_chassis *chassis;
38
0
  int length, i, v;
39
0
  u_int8_t *packet, *pos, *pos_llc, *pos_len_eh, *pos_len_edp, *pos_edp, *tlv,
40
0
      *end;
41
0
  u_int16_t checksum;
42
0
#  ifdef ENABLE_DOT1
43
0
  struct lldpd_vlan *vlan;
44
0
  unsigned int state = 0;
45
0
#  endif
46
0
  u_int8_t edp_fakeversion[] = { 7, 6, 4, 99 };
47
  /* Subsequent XXX can be replaced by other values. We place
48
     them here to ensure the position of "" to be a bit
49
     invariant with version changes. */
50
0
  const char *deviceslot[] = { "eth", "veth", "XXX", "XXX", "XXX", "XXX", "XXX",
51
0
    "XXX", "", NULL };
52
53
0
  log_debug("edp", "send EDP frame on port %s", hardware->h_ifname);
54
55
0
  chassis = hardware->h_lport.p_chassis;
56
0
#  ifdef ENABLE_DOT1
57
0
  while (state != 2) {
58
0
#  endif
59
0
    length = hardware->h_mtu;
60
0
    if ((packet = (u_int8_t *)calloc(1, length)) == NULL) return ENOMEM;
61
0
    pos = packet;
62
0
    v = 0;
63
64
    /* Ethernet header */
65
0
    if (!(POKE_BYTES(mcastaddr, sizeof(mcastaddr)) &&
66
0
      POKE_BYTES(&hardware->h_lladdr, ETHER_ADDR_LEN) &&
67
0
      POKE_SAVE(pos_len_eh) && /* We compute the len later */
68
0
      POKE_UINT16(0)))
69
0
      goto toobig;
70
71
    /* LLC */
72
0
    if (!(POKE_SAVE(pos_llc) && /* We need to save our
73
                 current position to
74
                 compute ethernet len */
75
      /* SSAP and DSAP */
76
0
      POKE_UINT8(0xaa) && POKE_UINT8(0xaa) &&
77
      /* Control field */
78
0
      POKE_UINT8(0x03) &&
79
      /* ORG */
80
0
      POKE_BYTES(llcorg, sizeof(llcorg)) && POKE_UINT16(LLC_PID_EDP)))
81
0
      goto toobig;
82
83
    /* EDP header */
84
0
    if ((chassis->c_id_len != ETHER_ADDR_LEN) ||
85
0
        (chassis->c_id_subtype != LLDP_CHASSISID_SUBTYPE_LLADDR)) {
86
0
      log_warnx("edp",
87
0
          "local chassis does not use MAC address as chassis ID!?");
88
0
      free(packet);
89
0
      return EINVAL;
90
0
    }
91
0
    if (!(POKE_SAVE(pos_edp) && /* Save the start of EDP frame */
92
0
      POKE_UINT8(1) && POKE_UINT8(0) &&
93
0
      POKE_SAVE(pos_len_edp) && /* We compute the len
94
                 and the checksum
95
                 later */
96
0
      POKE_UINT32(0) &&    /* Len + Checksum */
97
0
      POKE_UINT16(seq) && POKE_UINT16(0) &&
98
0
      POKE_BYTES(chassis->c_id, ETHER_ADDR_LEN)))
99
0
      goto toobig;
100
0
    seq++;
101
102
0
#  ifdef ENABLE_DOT1
103
0
    switch (state) {
104
0
    case 0:
105
0
#  endif
106
      /* Display TLV */
107
0
      if (!(POKE_START_EDP_TLV(EDP_TLV_DISPLAY) &&
108
0
        POKE_BYTES(chassis->c_name, strlen(chassis->c_name)) &&
109
0
        POKE_UINT8(0) && /* Add a NULL character
110
                for better
111
                compatibility */
112
0
        POKE_END_EDP_TLV))
113
0
        goto toobig;
114
115
      /* Info TLV */
116
0
      if (!(POKE_START_EDP_TLV(EDP_TLV_INFO))) goto toobig;
117
      /* We try to emulate the slot thing */
118
0
      for (i = 0; deviceslot[i] != NULL; i++) {
119
0
        if (strncmp(hardware->h_ifname, deviceslot[i],
120
0
          strlen(deviceslot[i])) == 0) {
121
0
          if (!(POKE_UINT16(i) &&
122
0
            POKE_UINT16(atoi(hardware->h_ifname +
123
0
                strlen(deviceslot[i])))))
124
0
            goto toobig;
125
0
          break;
126
0
        }
127
0
      }
128
      /* If we don't find a "slot", we say that the
129
         interface is in slot 8 */
130
0
      if (deviceslot[i] == NULL) {
131
0
        if (!(POKE_UINT16(8) &&
132
0
          POKE_UINT16(hardware->h_ifindex)))
133
0
          goto toobig;
134
0
      }
135
0
      if (!(POKE_UINT16(0) &&          /* vchassis */
136
0
        POKE_UINT32(0) && POKE_UINT16(0) && /* Reserved */
137
        /* Version */
138
0
        POKE_BYTES(edp_fakeversion, sizeof(edp_fakeversion)) &&
139
        /* Connections, we say that we won't
140
           have more interfaces than this
141
           mask. */
142
0
        POKE_UINT32(0xffffffff) && POKE_UINT32(0) &&
143
0
        POKE_UINT32(0) && POKE_UINT32(0) && POKE_END_EDP_TLV))
144
0
        goto toobig;
145
146
0
#  ifdef ENABLE_DOT1
147
0
      break;
148
0
    case 1:
149
0
      TAILQ_FOREACH (vlan, &hardware->h_lport.p_vlans, v_entries) {
150
0
        v++;
151
0
        if (!(POKE_START_EDP_TLV(EDP_TLV_VLAN) &&
152
0
          POKE_UINT8(0) && /* Flags: no IP address */
153
0
          POKE_UINT8(0) && /* Reserved */
154
0
          POKE_UINT16(vlan->v_vid) &&
155
0
          POKE_UINT32(0) && /* Reserved */
156
0
          POKE_UINT32(0) && /* IP address */
157
          /* VLAN name */
158
0
          POKE_BYTES(vlan->v_name,
159
0
              strlen(vlan->v_name)) &&
160
0
          POKE_UINT8(0) && POKE_END_EDP_TLV))
161
0
          goto toobig;
162
0
      }
163
0
      break;
164
0
    }
165
166
0
    if ((state == 1) && (v == 0)) {
167
      /* No VLAN, no need to send another TLV */
168
0
      free(packet);
169
0
      break;
170
0
    }
171
0
#  endif
172
173
    /* Null TLV */
174
0
    if (!(POKE_START_EDP_TLV(EDP_TLV_NULL) && POKE_END_EDP_TLV &&
175
0
      POKE_SAVE(end)))
176
0
      goto toobig;
177
178
    /* Compute len and checksum */
179
0
    i = end - pos_llc; /* Ethernet length */
180
0
    v = end - pos_edp; /* EDP length */
181
0
    POKE_RESTORE(pos_len_eh);
182
0
    if (!(POKE_UINT16(i))) goto toobig;
183
0
    POKE_RESTORE(pos_len_edp);
184
0
    if (!(POKE_UINT16(v))) goto toobig;
185
0
    checksum = frame_checksum(pos_edp, v, 0);
186
0
    if (!(POKE_UINT16(checksum))) goto toobig;
187
188
0
    if (interfaces_send_helper(global, hardware, (char *)packet,
189
0
      end - packet) == -1) {
190
0
      log_warn("edp", "unable to send packet on real device for %s",
191
0
          hardware->h_ifname);
192
0
      free(packet);
193
0
      return ENETDOWN;
194
0
    }
195
0
    free(packet);
196
197
0
#  ifdef ENABLE_DOT1
198
0
    state++;
199
0
  }
200
0
#  endif
201
202
0
  hardware->h_tx_cnt++;
203
0
  return 0;
204
0
toobig:
205
0
  free(packet);
206
0
  return E2BIG;
207
0
}
208
209
#  define CHECK_TLV_SIZE(x, name)                                  \
210
0
    do {                                                           \
211
0
      if (tlv_len < (x)) {                                         \
212
0
  log_warnx("edp", name " EDP TLV too short received on %s", \
213
0
      hardware->h_ifname);                                   \
214
0
  goto malformed;                                            \
215
0
      }                                                            \
216
0
    } while (0)
217
218
int
219
edp_decode(struct lldpd *cfg, char *frame, int s, struct lldpd_hardware *hardware,
220
    struct lldpd_chassis **newchassis, struct lldpd_port **newport)
221
0
{
222
0
  struct lldpd_chassis *chassis;
223
0
  struct lldpd_port *port;
224
0
#  ifdef ENABLE_DOT1
225
0
  struct lldpd_mgmt *mgmt, *mgmt_next, *m;
226
0
  struct lldpd_vlan *lvlan = NULL, *lvlan_next;
227
0
#  endif
228
0
  const unsigned char edpaddr[] = EDP_MULTICAST_ADDR;
229
0
  int length, gotend = 0, gotvlans = 0, edp_len, tlv_len, tlv_type;
230
0
  int edp_port, edp_slot;
231
0
  u_int8_t *pos, *pos_edp, *tlv;
232
0
  u_int8_t version[4];
233
0
#  ifdef ENABLE_DOT1
234
0
  struct in_addr address;
235
0
  struct lldpd_port *oport;
236
0
#  endif
237
238
0
  log_debug("edp", "decode EDP frame on port %s", hardware->h_ifname);
239
240
0
  if ((chassis = calloc(1, sizeof(struct lldpd_chassis))) == NULL) {
241
0
    log_warn("edp", "failed to allocate remote chassis");
242
0
    return -1;
243
0
  }
244
0
  TAILQ_INIT(&chassis->c_mgmt);
245
0
  if ((port = calloc(1, sizeof(struct lldpd_port))) == NULL) {
246
0
    log_warn("edp", "failed to allocate remote port");
247
0
    free(chassis);
248
0
    return -1;
249
0
  }
250
0
#  ifdef ENABLE_DOT1
251
0
  TAILQ_INIT(&port->p_vlans);
252
0
#  endif
253
254
0
  length = s;
255
0
  pos = (u_int8_t *)frame;
256
257
0
  if (length < 2 * ETHER_ADDR_LEN + sizeof(u_int16_t) + 8 /* LLC */ + 10 +
258
0
    ETHER_ADDR_LEN /* EDP header */) {
259
0
    log_warnx("edp", "too short EDP frame received on %s",
260
0
        hardware->h_ifname);
261
0
    goto malformed;
262
0
  }
263
264
0
  if (PEEK_CMP(edpaddr, sizeof(edpaddr)) != 0) {
265
0
    log_info("edp",
266
0
        "frame not targeted at EDP multicast address received on %s",
267
0
        hardware->h_ifname);
268
0
    goto malformed;
269
0
  }
270
0
  PEEK_DISCARD(ETHER_ADDR_LEN);
271
0
  PEEK_DISCARD_UINT16;
272
0
  PEEK_DISCARD(6); /* LLC: DSAP + SSAP + control + org */
273
0
  if (PEEK_UINT16 != LLC_PID_EDP) {
274
0
    log_debug("edp", "incorrect LLC protocol ID received on %s",
275
0
        hardware->h_ifname);
276
0
    goto malformed;
277
0
  }
278
279
0
  (void)PEEK_SAVE(pos_edp); /* Save the start of EDP packet */
280
0
  if (PEEK_UINT8 != 1) {
281
0
    log_warnx("edp", "incorrect EDP version for frame received on %s",
282
0
        hardware->h_ifname);
283
0
    goto malformed;
284
0
  }
285
0
  PEEK_DISCARD_UINT8; /* Reserved */
286
0
  edp_len = PEEK_UINT16;
287
0
  PEEK_DISCARD_UINT16; /* Checksum */
288
0
  PEEK_DISCARD_UINT16; /* Sequence */
289
0
  if (PEEK_UINT16 != 0) { /* ID Type = 0 = MAC */
290
0
    log_warnx("edp", "incorrect device id type for frame received on %s",
291
0
        hardware->h_ifname);
292
0
    goto malformed;
293
0
  }
294
0
  if (edp_len > length + 10) {
295
0
    log_warnx("edp", "incorrect size for EDP frame received on %s",
296
0
        hardware->h_ifname);
297
0
    goto malformed;
298
0
  }
299
0
  port->p_ttl = cfg ? cfg->g_config.c_tx_interval * cfg->g_config.c_tx_hold : 0;
300
0
  port->p_ttl = MIN((port->p_ttl + 999) / 1000, 65535);
301
0
  chassis->c_id_subtype = LLDP_CHASSISID_SUBTYPE_LLADDR;
302
0
  chassis->c_id_len = ETHER_ADDR_LEN;
303
0
  if ((chassis->c_id = (char *)malloc(ETHER_ADDR_LEN)) == NULL) {
304
0
    log_warn("edp", "unable to allocate memory for chassis ID");
305
0
    goto malformed;
306
0
  }
307
0
  PEEK_BYTES(chassis->c_id, ETHER_ADDR_LEN);
308
309
#  ifndef FUZZING_BUILD_MODE_UNSAFE_FOR_PRODUCTION
310
  /* Let's check checksum */
311
  if (frame_checksum(pos_edp, edp_len, 0) != 0) {
312
    log_warnx("edp", "incorrect EDP checksum for frame received on %s",
313
        hardware->h_ifname);
314
    goto malformed;
315
  }
316
#  endif
317
318
0
  while (length && !gotend) {
319
0
    if (length < 4) {
320
0
      log_warnx("edp",
321
0
          "EDP TLV header is too large for "
322
0
          "frame received on %s",
323
0
          hardware->h_ifname);
324
0
      goto malformed;
325
0
    }
326
0
    if (PEEK_UINT8 != EDP_TLV_MARKER) {
327
0
      log_warnx("edp",
328
0
          "incorrect marker starting EDP TLV header for frame "
329
0
          "received on %s",
330
0
          hardware->h_ifname);
331
0
      goto malformed;
332
0
    }
333
0
    tlv_type = PEEK_UINT8;
334
0
    tlv_len = PEEK_UINT16 - 4;
335
0
    (void)PEEK_SAVE(tlv);
336
0
    if ((tlv_len < 0) || (tlv_len > length)) {
337
0
      log_debug("edp",
338
0
          "incorrect size in EDP TLV header for frame "
339
0
          "received on %s",
340
0
          hardware->h_ifname);
341
      /* Some poor old Extreme Summit are quite bogus */
342
0
      gotend = 1;
343
0
      break;
344
0
    }
345
0
    switch (tlv_type) {
346
0
    case EDP_TLV_INFO:
347
0
      CHECK_TLV_SIZE(32, "Info");
348
0
      port->p_id_subtype = LLDP_PORTID_SUBTYPE_IFNAME;
349
0
      edp_slot = PEEK_UINT16;
350
0
      edp_port = PEEK_UINT16;
351
0
      free(port->p_id);
352
0
      port->p_id_len =
353
0
          asprintf(&port->p_id, "%d/%d", edp_slot + 1, edp_port + 1);
354
0
      if (port->p_id_len == -1) {
355
0
        log_warn("edp",
356
0
            "unable to allocate memory for "
357
0
            "port ID");
358
0
        goto malformed;
359
0
      }
360
0
      free(port->p_descr);
361
0
      if (asprintf(&port->p_descr, "Slot %d / Port %d", edp_slot + 1,
362
0
        edp_port + 1) == -1) {
363
0
        log_warn("edp",
364
0
            "unable to allocate memory for "
365
0
            "port description");
366
0
        goto malformed;
367
0
      }
368
0
      PEEK_DISCARD_UINT16; /* vchassis */
369
0
      PEEK_DISCARD(6);     /* Reserved */
370
0
      PEEK_BYTES(version, 4);
371
0
      free(chassis->c_descr);
372
0
      if (asprintf(&chassis->c_descr,
373
0
        "EDP enabled device, version %d.%d.%d.%d", version[0],
374
0
        version[1], version[2], version[3]) == -1) {
375
0
        log_warn("edp",
376
0
            "unable to allocate memory for "
377
0
            "chassis description");
378
0
        goto malformed;
379
0
      }
380
0
      break;
381
0
    case EDP_TLV_DISPLAY:
382
0
      free(chassis->c_name);
383
0
      if ((chassis->c_name = (char *)calloc(1, tlv_len + 1)) ==
384
0
          NULL) {
385
0
        log_warn("edp",
386
0
            "unable to allocate memory for chassis "
387
0
            "name");
388
0
        goto malformed;
389
0
      }
390
      /* TLV display contains a lot of garbage */
391
0
      PEEK_BYTES(chassis->c_name, tlv_len);
392
0
      break;
393
0
    case EDP_TLV_NULL:
394
0
      if (tlv_len != 0) {
395
0
        log_warnx("edp",
396
0
            "null tlv with incorrect size in frame "
397
0
            "received on %s",
398
0
            hardware->h_ifname);
399
0
        goto malformed;
400
0
      }
401
0
      if (length)
402
0
        log_debug("edp", "extra data after edp frame on %s",
403
0
            hardware->h_ifname);
404
0
      gotend = 1;
405
0
      break;
406
0
    case EDP_TLV_VLAN:
407
0
#  ifdef ENABLE_DOT1
408
0
      CHECK_TLV_SIZE(12, "VLAN");
409
0
      if ((lvlan = (struct lldpd_vlan *)calloc(1,
410
0
         sizeof(struct lldpd_vlan))) == NULL) {
411
0
        log_warn("edp", "unable to allocate vlan");
412
0
        goto malformed;
413
0
      }
414
0
      PEEK_DISCARD_UINT16;     /* Flags + reserved */
415
0
      lvlan->v_vid = PEEK_UINT16; /* VID */
416
0
      PEEK_DISCARD(4);      /* Reserved */
417
0
      PEEK_BYTES(&address, sizeof(address));
418
419
0
      if (address.s_addr != INADDR_ANY) {
420
0
        mgmt = lldpd_alloc_mgmt(LLDPD_AF_IPV4, &address,
421
0
            sizeof(struct in_addr), 0);
422
0
        if (mgmt == NULL) {
423
0
          log_warn("edp", "Out of memory");
424
0
          goto malformed;
425
0
        }
426
0
        TAILQ_INSERT_TAIL(&chassis->c_mgmt, mgmt, m_entries);
427
0
      }
428
429
0
      if ((lvlan->v_name = (char *)calloc(1, tlv_len + 1 - 12)) ==
430
0
          NULL) {
431
0
        log_warn("edp", "unable to allocate vlan name");
432
0
        goto malformed;
433
0
      }
434
0
      PEEK_BYTES(lvlan->v_name, tlv_len - 12);
435
436
0
      TAILQ_INSERT_TAIL(&port->p_vlans, lvlan, v_entries);
437
0
      lvlan = NULL;
438
0
#  endif
439
0
      gotvlans = 1;
440
0
      break;
441
0
    default:
442
0
      log_debug("edp", "unknown EDP TLV type (%d) received on %s",
443
0
          tlv_type, hardware->h_ifname);
444
0
      hardware->h_rx_unrecognized_cnt++;
445
0
    }
446
0
    PEEK_DISCARD(tlv + tlv_len - pos);
447
0
  }
448
0
  if ((chassis->c_id == NULL) || (port->p_id == NULL) ||
449
0
      (chassis->c_name == NULL) || (chassis->c_descr == NULL) ||
450
0
      (port->p_descr == NULL) || (gotend == 0)) {
451
0
#  ifdef ENABLE_DOT1
452
0
    if (gotvlans && gotend) {
453
      /* VLAN can be sent in a separate frames. We need to add
454
       * those vlans to an existing port */
455
0
      TAILQ_FOREACH (oport, &hardware->h_rports, p_entries) {
456
0
        if (!((oport->p_protocol == LLDPD_MODE_EDP) &&
457
0
          (oport->p_chassis->c_id_subtype ==
458
0
              chassis->c_id_subtype) &&
459
0
          (oport->p_chassis->c_id_len ==
460
0
              chassis->c_id_len) &&
461
0
          (memcmp(oport->p_chassis->c_id, chassis->c_id,
462
0
               chassis->c_id_len) == 0)))
463
0
          continue;
464
        /* We attach the VLANs to the found port */
465
0
        lldpd_vlan_cleanup(oport);
466
0
        for (lvlan = TAILQ_FIRST(&port->p_vlans); lvlan != NULL;
467
0
             lvlan = lvlan_next) {
468
0
          lvlan_next = TAILQ_NEXT(lvlan, v_entries);
469
0
          TAILQ_REMOVE(&port->p_vlans, lvlan, v_entries);
470
0
          TAILQ_INSERT_TAIL(&oport->p_vlans, lvlan,
471
0
              v_entries);
472
0
        }
473
        /* And the IP addresses */
474
0
        for (mgmt = TAILQ_FIRST(&chassis->c_mgmt); mgmt != NULL;
475
0
             mgmt = mgmt_next) {
476
0
          mgmt_next = TAILQ_NEXT(mgmt, m_entries);
477
0
          TAILQ_REMOVE(&chassis->c_mgmt, mgmt, m_entries);
478
          /* Don't add an address that already exists! */
479
0
          TAILQ_FOREACH (m, &chassis->c_mgmt, m_entries)
480
0
            if (m->m_family == mgmt->m_family &&
481
0
                !memcmp(&m->m_addr, &mgmt->m_addr,
482
0
              sizeof(m->m_addr)))
483
0
              break;
484
0
          if (m == NULL)
485
0
            TAILQ_INSERT_TAIL(
486
0
                &oport->p_chassis->c_mgmt, mgmt,
487
0
                m_entries);
488
0
        }
489
0
      }
490
      /* We discard the remaining frame */
491
0
      goto malformed;
492
0
    }
493
#  else
494
    if (gotvlans) goto malformed;
495
#  endif
496
0
    log_warnx("edp",
497
0
        "some mandatory tlv are missing for frame received on %s",
498
0
        hardware->h_ifname);
499
0
    goto malformed;
500
0
  }
501
0
  *newchassis = chassis;
502
0
  *newport = port;
503
0
  return 1;
504
505
0
malformed:
506
0
#  ifdef ENABLE_DOT1
507
0
  free(lvlan);
508
0
#  endif
509
0
  lldpd_chassis_cleanup(chassis, 1);
510
0
  lldpd_port_cleanup(port, 1);
511
0
  free(port);
512
0
  return -1;
513
0
}
514
515
#endif /* ENABLE_EDP */