2 * Copyright (c) 2008 Vincent Bernat <bernat@luffy.cx>
4 * Permission to use, copy, modify, and distribute this software for any
5 * purpose with or without fee is hereby granted, provided that the above
6 * copyright notice and this permission notice appear in all copies.
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.
25 #include <arpa/inet.h>
31 edp_send(struct lldpd
*global
,
32 struct lldpd_hardware
*hardware
)
34 const u_int8_t mcastaddr
[] = EDP_MULTICAST_ADDR
;
35 const u_int8_t llcorg
[] = LLC_ORG_EXTREME
;
36 struct lldpd_chassis
*chassis
;
38 u_int8_t
*packet
, *pos
, *pos_llc
, *pos_len_eh
, *pos_len_edp
, *pos_edp
, *tlv
, *end
;
41 struct lldpd_vlan
*vlan
;
42 unsigned int state
= 0;
44 u_int8_t edp_fakeversion
[] = {7, 6, 4, 99};
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
};
50 chassis
= hardware
->h_lport
.p_chassis
;
54 length
= hardware
->h_mtu
;
55 if ((packet
= (u_int8_t
*)malloc(length
)) == NULL
)
57 memset(packet
, 0, length
);
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 */
71 POKE_SAVE(pos_llc
) && /* We need to save our
73 compute ethernet len */
75 POKE_UINT8(0xaa) && POKE_UINT8(0xaa) &&
79 POKE_BYTES(llcorg
, sizeof(llcorg
)) &&
80 POKE_UINT16(LLC_PID_EDP
)))
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!?");
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
96 POKE_UINT32(0) && /* Len + Checksum */
99 POKE_BYTES(chassis
->c_id
, ETH_ALEN
)))
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
119 POKE_START_EDP_TLV(EDP_TLV_INFO
)))
121 /* We try to emulate the slot thing */
122 for (i
=0; deviceslot
[i
] != NULL
; i
++) {
123 if (strncmp(hardware
->h_ifname
, deviceslot
[i
],
124 strlen(deviceslot
[i
])) == 0) {
127 POKE_UINT16(atoi(hardware
->h_ifname
+
128 strlen(deviceslot
[i
])))))
133 /* If we don't find a "slot", we say that the
134 interface is in slot 8 */
135 if (deviceslot
[i
] == NULL
) {
138 POKE_UINT16(hardware
->h_ifindex
)))
142 POKE_UINT16(0) && /* vchassis */
143 POKE_UINT32(0) && POKE_UINT16(0) && /* Reserved */
145 POKE_BYTES(edp_fakeversion
, sizeof(edp_fakeversion
)) &&
146 /* Connections, we say that we won't
147 have more interfaces than this
149 POKE_UINT32(0xffffffff) &&
150 POKE_UINT32(0) && POKE_UINT32(0) && POKE_UINT32(0) &&
157 TAILQ_FOREACH(vlan
, &hardware
->h_lport
.p_vlans
,
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 */
168 POKE_BYTES(vlan
->v_name
, strlen(vlan
->v_name
)) &&
176 if ((state
== 1) && (v
== 0)) /* No VLAN, no need to send another TLV */
182 POKE_START_EDP_TLV(EDP_TLV_NULL
) &&
187 /* Compute len and checksum */
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
;
197 if (hardware
->h_ops
->send(global
, hardware
,
198 (char *)packet
, end
- packet
) == -1) {
199 LLOG_WARN("unable to send packet on real device for %s",
211 hardware
->h_tx_cnt
++;
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); \
226 edp_decode(struct lldpd
*cfg
, char *frame
, int s
,
227 struct lldpd_hardware
*hardware
,
228 struct lldpd_chassis
**newchassis
, struct lldpd_port
**newport
)
230 struct lldpd_chassis
*chassis
;
231 struct lldpd_port
*port
;
233 struct lldpd_vlan
*lvlan
= NULL
, *lvlan_next
;
235 const unsigned char edpaddr
[] = EDP_MULTICAST_ADDR
;
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
;
241 struct in_addr address
;
242 struct lldpd_port
*oport
;
245 if ((chassis
= calloc(1, sizeof(struct lldpd_chassis
))) == NULL
) {
246 LLOG_WARN("failed to allocate remote chassis");
249 if ((port
= calloc(1, sizeof(struct lldpd_port
))) == NULL
) {
250 LLOG_WARN("failed to allocate remote port");
255 TAILQ_INIT(&port
->p_vlans
);
259 pos
= (u_int8_t
*)frame
;
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
);
267 if (PEEK_CMP(edpaddr
, sizeof(edpaddr
)) != 0) {
268 LLOG_INFO("frame not targeted at EDP multicast address received on %s",
272 PEEK_DISCARD(ETH_ALEN
); PEEK_DISCARD_UINT16
;
273 PEEK_DISCARD(6); /* LLC: DSAP + SSAP + control + org */
274 if (PEEK_UINT16
!= LLC_PID_EDP
) {
275 LLOG_DEBUG("incorrect LLC protocol ID received on %s",
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",
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 */
291 LLOG_WARNX("incorrect device id type for frame received on %s",
295 if (edp_len
> length
+ 10) {
296 LLOG_WARNX("incorrect size for EDP frame received on %s",
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");
307 PEEK_BYTES(chassis
->c_id
, ETH_ALEN
);
309 /* Let's check checksum */
310 if (frame_checksum(pos_edp
, edp_len
, 0) != 0) {
311 LLOG_WARNX("incorrect EDP checksum for frame received on %s",
316 while (length
&& !gotend
) {
318 LLOG_WARNX("EDP TLV header is too large for "
319 "frame received on %s",
323 if (PEEK_UINT8
!= EDP_TLV_MARKER
) {
324 LLOG_WARNX("incorrect marker starting EDP TLV header for frame "
329 tlv_type
= PEEK_UINT8
;
330 tlv_len
= PEEK_UINT16
- 4;
332 if ((tlv_len
< 0) || (tlv_len
> length
)) {
333 LLOG_DEBUG("incorrect size in EDP TLV header for frame "
336 /* Some poor old Extreme Summit are quite bogus */
342 CHECK_TLV_SIZE(32, "Info");
343 port
->p_id_subtype
= LLDP_PORTID_SUBTYPE_IFNAME
;
344 edp_slot
= PEEK_UINT16
; edp_port
= PEEK_UINT16
;
345 if (asprintf(&port
->p_id
, "%d/%d",
346 edp_slot
+ 1, edp_port
+ 1) == -1) {
347 LLOG_WARN("unable to allocate memory for "
351 port
->p_id_len
= strlen(port
->p_id
);
352 if (asprintf(&port
->p_descr
, "Slot %d / Port %d",
353 edp_slot
+ 1, edp_port
+ 1) == -1) {
354 LLOG_WARN("unable to allocate memory for "
358 PEEK_DISCARD_UINT16
; /* vchassis */
359 PEEK_DISCARD(6); /* Reserved */
360 PEEK_BYTES(version
, 4);
361 if (asprintf(&chassis
->c_descr
,
362 "EDP enabled device, version %d.%d.%d.%d",
363 version
[0], version
[1],
364 version
[2], version
[3]) == -1) {
365 LLOG_WARN("unable to allocate memory for "
366 "chassis description");
370 case EDP_TLV_DISPLAY
:
371 if ((chassis
->c_name
= (char *)calloc(1, tlv_len
+ 1)) == NULL
) {
372 LLOG_WARN("unable to allocate memory for chassis "
376 /* TLV display contains a lot of garbage */
377 PEEK_BYTES(chassis
->c_name
, tlv_len
);
381 LLOG_WARNX("null tlv with incorrect size in frame "
387 LLOG_DEBUG("extra data after edp frame on %s",
393 CHECK_TLV_SIZE(12, "VLAN");
394 if ((lvlan
= (struct lldpd_vlan
*)calloc(1,
395 sizeof(struct lldpd_vlan
))) == NULL
) {
396 LLOG_WARN("unable to allocate vlan");
399 PEEK_DISCARD_UINT16
; /* Flags + reserved */
400 lvlan
->v_vid
= PEEK_UINT16
; /* VID */
401 PEEK_DISCARD(4); /* Reserved */
402 PEEK_BYTES(&address
, sizeof(address
));
404 if ((lvlan
->v_name
= (char *)calloc(1,
405 tlv_len
+ 1 - 12)) == NULL
) {
406 LLOG_WARN("unable to allocate vlan name");
410 PEEK_BYTES(lvlan
->v_name
, tlv_len
- 12);
412 if (address
.s_addr
!= INADDR_ANY
) {
413 if (chassis
->c_mgmt
.s_addr
== INADDR_ANY
)
414 chassis
->c_mgmt
.s_addr
= address
.s_addr
;
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 */
420 ip
= inet_ntoa(address
);
421 if (fnmatch(cfg
->g_mgmt_pattern
,
423 chassis
->c_mgmt
.s_addr
= address
.s_addr
;
426 TAILQ_INSERT_TAIL(&port
->p_vlans
,
432 LLOG_DEBUG("unknown EDP TLV type (%d) received on %s",
433 tlv_type
, hardware
->h_ifname
);
434 hardware
->h_rx_unrecognized_cnt
++;
436 PEEK_DISCARD(tlv
+ tlv_len
- pos
);
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
) ||
445 if (gotvlans
&& gotend
) {
446 /* VLAN can be sent in a separate frames. We need to add
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)))
456 /* We attach the VLANs to the found port */
457 lldpd_vlan_cleanup(oport
);
458 for (lvlan
= TAILQ_FIRST(&port
->p_vlans
);
460 lvlan
= lvlan_next
) {
461 lvlan_next
= TAILQ_NEXT(lvlan
, v_entries
);
462 TAILQ_REMOVE(&port
->p_vlans
, lvlan
, v_entries
);
463 TAILQ_INSERT_TAIL(&oport
->p_vlans
,
466 /* And the IP address */
467 oport
->p_chassis
->c_mgmt
.s_addr
=
468 chassis
->c_mgmt
.s_addr
;
471 /* We discard the remaining frame */
478 LLOG_WARNX("some mandatory tlv are missing for frame received on %s",
482 *newchassis
= chassis
;
487 lldpd_chassis_cleanup(chassis
, 1);
488 lldpd_port_cleanup(cfg
, port
, 1);
493 #endif /* ENABLE_EDP */