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