2 This file is part of systemd.
4 Copyright (C) 2014 Tom Gundersen
5 Copyright (C) 2014 Susant Sahani
7 systemd is free software; you can redistribute it and/or modify it
8 under the terms of the GNU Lesser General Public License as published by
9 the Free Software Foundation; either version 2.1 of the License, or
10 (at your option) any later version.
12 systemd is distributed in the hope that it will be useful, but
13 WITHOUT ANY WARRANTY; without even the implied warranty of
14 MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the GNU
15 Lesser General Public License for more details.
17 You should have received a copy of the GNU Lesser General Public License
18 along with systemd; If not, see <http://www.gnu.org/licenses/>.
21 #include <arpa/inet.h>
22 #include <net/ethernet.h>
24 #include "alloc-util.h"
28 int tlv_section_new(tlv_section
**ret
) {
31 s
= new0(tlv_section
, 1);
40 void tlv_section_free(tlv_section
*m
) {
48 int tlv_packet_new(tlv_packet
**ret
) {
51 m
= new0(tlv_packet
, 1);
55 LIST_HEAD_INIT(m
->sections
);
63 tlv_packet
*sd_lldp_packet_ref(tlv_packet
*m
) {
74 tlv_packet
*sd_lldp_packet_unref(tlv_packet
*m
) {
86 LIST_FOREACH_SAFE(section
, s
, n
, m
->sections
)
93 int tlv_packet_append_bytes(tlv_packet
*m
, const void *data
, size_t data_length
) {
96 assert_return(m
, -EINVAL
);
97 assert_return(data
, -EINVAL
);
98 assert_return(data_length
, -EINVAL
);
100 if (m
->length
+ data_length
> ETHER_MAX_LEN
)
103 p
= m
->pdu
+ m
->length
;
104 memcpy(p
, data
, data_length
);
105 m
->length
+= data_length
;
110 int tlv_packet_append_u8(tlv_packet
*m
, uint8_t data
) {
112 assert_return(m
, -EINVAL
);
114 return tlv_packet_append_bytes(m
, &data
, sizeof(uint8_t));
117 int tlv_packet_append_u16(tlv_packet
*m
, uint16_t data
) {
120 assert_return(m
, -EINVAL
);
124 return tlv_packet_append_bytes(m
, &type
, sizeof(uint16_t));
127 int tlv_packet_append_u32(tlv_packet
*m
, uint32_t data
) {
130 assert_return(m
, -EINVAL
);
134 return tlv_packet_append_bytes(m
, &type
, sizeof(uint32_t));
137 int tlv_packet_append_string(tlv_packet
*m
, char *data
, uint16_t size
) {
139 assert_return(m
, -EINVAL
);
141 return tlv_packet_append_bytes(m
, data
, size
);
144 int lldp_tlv_packet_open_container(tlv_packet
*m
, uint16_t type
) {
146 assert_return(m
, -EINVAL
);
148 m
->container_pos
= m
->pdu
+ m
->length
;
150 return tlv_packet_append_u16(m
, type
<< 9);
153 int lldp_tlv_packet_close_container(tlv_packet
*m
) {
156 assert_return(m
, -EINVAL
);
157 assert_return(m
->container_pos
, -EINVAL
);
159 memcpy(&type
, m
->container_pos
, sizeof(uint16_t));
161 type
|= htons(((m
->pdu
+ m
->length
) - (m
->container_pos
+ 2)) & 0x01ff);
162 memcpy(m
->container_pos
, &type
, sizeof(uint16_t));
167 static inline int tlv_packet_read_internal(tlv_section
*m
, void **data
) {
169 assert_return(m
->read_pos
, -EINVAL
);
176 int tlv_packet_read_u8(tlv_packet
*m
, uint8_t *data
) {
180 assert_return(m
, -EINVAL
);
182 r
= tlv_packet_read_internal(m
->container
, &val
);
186 memcpy(data
, val
, sizeof(uint8_t));
188 m
->container
->read_pos
++;
193 int tlv_packet_read_u16(tlv_packet
*m
, uint16_t *data
) {
198 assert_return(m
, -EINVAL
);
200 r
= tlv_packet_read_internal(m
->container
, &val
);
204 memcpy(&t
, val
, sizeof(uint16_t));
207 m
->container
->read_pos
+= 2;
212 int tlv_packet_read_u32(tlv_packet
*m
, uint32_t *data
) {
217 assert_return(m
, -EINVAL
);
219 r
= tlv_packet_read_internal(m
->container
, &val
);
223 memcpy(&t
, val
, sizeof(uint32_t));
226 m
->container
->read_pos
+= 4;
231 int tlv_packet_read_string(tlv_packet
*m
, char **data
, uint16_t *data_length
) {
235 assert_return(m
, -EINVAL
);
237 r
= tlv_packet_read_internal(m
->container
, &val
);
241 *data
= (char *) val
;
242 *data_length
= m
->container
->data
+ m
->container
->length
- m
->container
->read_pos
;
244 m
->container
->read_pos
+= *data_length
;
249 int tlv_packet_read_bytes(tlv_packet
*m
, uint8_t **data
, uint16_t *data_length
) {
253 assert_return(m
, -EINVAL
);
255 r
= tlv_packet_read_internal(m
->container
, &val
);
259 *data
= (uint8_t *) val
;
260 *data_length
= m
->container
->data
+ m
->container
->length
- m
->container
->read_pos
;
262 m
->container
->read_pos
+= *data_length
;
267 /* parse raw TLV packet */
268 int tlv_packet_parse_pdu(tlv_packet
*m
, uint16_t size
) {
269 tlv_section
*section
, *tail
;
274 assert_return(m
, -EINVAL
);
275 assert_return(size
, -EINVAL
);
279 /* extract Ethernet header */
280 memcpy(&m
->mac
, p
, ETH_ALEN
);
281 p
+= sizeof(struct ether_header
);
283 for (l
= 0; l
<= size
; ) {
284 r
= tlv_section_new(§ion
);
288 memcpy(&t
, p
, sizeof(uint16_t));
290 section
->type
= ntohs(t
) >> 9;
291 section
->length
= ntohs(t
) & 0x01ff;
293 if (section
->type
== LLDP_TYPE_END
|| section
->type
>=_LLDP_TYPE_MAX
) {
294 tlv_section_free(section
);
300 if (section
->type
== LLDP_TYPE_PRIVATE
&&
301 section
->length
>= LLDP_OUI_LEN
+ 1) {
304 section
->subtype
= *p
++;
306 section
->length
-= LLDP_OUI_LEN
+ 1;
307 l
+= LLDP_OUI_LEN
+ 1;
312 LIST_FIND_TAIL(section
, m
->sections
, tail
);
313 LIST_INSERT_AFTER(section
, m
->sections
, tail
, section
);
315 p
+= section
->length
;
316 l
+= (section
->length
+ 2);
322 int lldp_tlv_packet_enter_container(tlv_packet
*m
, uint16_t type
) {
325 assert_return(m
, -EINVAL
);
326 assert_return(type
!= LLDP_TYPE_PRIVATE
, -EINVAL
);
328 LIST_FOREACH(section
, s
, m
->sections
)
336 m
->container
->read_pos
= s
->data
;
337 if (!m
->container
->read_pos
) {
345 int lldp_tlv_packet_enter_container_oui(tlv_packet
*m
, const uint8_t *oui
, uint8_t subtype
) {
348 assert_return(m
, -EINVAL
);
349 assert_return(oui
, -EINVAL
);
351 LIST_FOREACH(section
, s
, m
->sections
) {
352 if (s
->type
== LLDP_TYPE_PRIVATE
&&
354 s
->subtype
== subtype
&&
355 !memcmp(s
->oui
, oui
, LLDP_OUI_LEN
))
364 m
->container
->read_pos
= s
->data
;
365 if (!m
->container
->read_pos
) {
373 int lldp_tlv_packet_exit_container(tlv_packet
*m
) {
374 assert_return(m
, -EINVAL
);
381 static int lldp_tlv_packet_read_u16_tlv(tlv_packet
*tlv
, uint16_t type
, uint16_t *value
) {
384 assert_return(tlv
, -EINVAL
);
386 r
= lldp_tlv_packet_enter_container(tlv
, type
);
390 r
= tlv_packet_read_u16(tlv
, value
);
391 r2
= lldp_tlv_packet_exit_container(tlv
);
393 return r
< 0 ? r
: r2
;
396 static int lldp_tlv_packet_read_string_tlv(tlv_packet
*tlv
, uint16_t type
, char **data
, uint16_t *length
) {
400 assert_return(tlv
, -EINVAL
);
402 r
= lldp_tlv_packet_enter_container(tlv
, type
);
406 r
= tlv_packet_read_string(tlv
, &s
, length
);
413 r2
= lldp_tlv_packet_exit_container(tlv
);
415 return r
< 0 ? r
: r2
;
418 int sd_lldp_packet_read_chassis_id(tlv_packet
*tlv
,
425 assert_return(tlv
, -EINVAL
);
427 r
= lldp_tlv_packet_enter_container(tlv
, LLDP_TYPE_CHASSIS_ID
);
431 r
= tlv_packet_read_u8(tlv
, &subtype
);
436 case LLDP_CHASSIS_SUBTYPE_MAC_ADDRESS
:
438 r
= tlv_packet_read_bytes(tlv
, data
, length
);
451 r2
= lldp_tlv_packet_exit_container(tlv
);
453 return r
< 0 ? r
: r2
;
456 int sd_lldp_packet_read_port_id(tlv_packet
*tlv
,
464 assert_return(tlv
, -EINVAL
);
466 r
= lldp_tlv_packet_enter_container(tlv
, LLDP_TYPE_PORT_ID
);
470 r
= tlv_packet_read_u8(tlv
, &subtype
);
475 case LLDP_PORT_SUBTYPE_PORT_COMPONENT
:
476 case LLDP_PORT_SUBTYPE_INTERFACE_ALIAS
:
477 case LLDP_PORT_SUBTYPE_INTERFACE_NAME
:
478 case LLDP_PORT_SUBTYPE_LOCALLY_ASSIGNED
:
480 r
= tlv_packet_read_string(tlv
, &s
, length
);
484 *data
= (uint8_t *) s
;
487 case LLDP_PORT_SUBTYPE_MAC_ADDRESS
:
489 r
= tlv_packet_read_bytes(tlv
, data
, length
);
502 r2
= lldp_tlv_packet_exit_container(tlv
);
504 return r
< 0 ? r
: r2
;
507 int sd_lldp_packet_read_ttl(tlv_packet
*tlv
, uint16_t *ttl
) {
508 return lldp_tlv_packet_read_u16_tlv(tlv
, LLDP_TYPE_TTL
, ttl
);
511 int sd_lldp_packet_read_system_name(tlv_packet
*tlv
,
514 return lldp_tlv_packet_read_string_tlv(tlv
, LLDP_TYPE_SYSTEM_NAME
, data
, length
);
517 int sd_lldp_packet_read_system_description(tlv_packet
*tlv
,
520 return lldp_tlv_packet_read_string_tlv(tlv
, LLDP_TYPE_SYSTEM_DESCRIPTION
, data
, length
);
523 int sd_lldp_packet_read_port_description(tlv_packet
*tlv
,
526 return lldp_tlv_packet_read_string_tlv(tlv
, LLDP_TYPE_PORT_DESCRIPTION
, data
, length
);
529 int sd_lldp_packet_read_system_capability(tlv_packet
*tlv
, uint16_t *data
) {
530 return lldp_tlv_packet_read_u16_tlv(tlv
, LLDP_TYPE_SYSTEM_CAPABILITIES
, data
);
533 int sd_lldp_packet_read_port_vlan_id(tlv_packet
*tlv
, uint16_t *id
) {
536 assert_return(tlv
, -EINVAL
);
538 r
= lldp_tlv_packet_enter_container_oui(tlv
, LLDP_OUI_802_1
, LLDP_OUI_SUBTYPE_802_1_PORT_VLAN_ID
);
542 r
= tlv_packet_read_u16(tlv
, id
);
543 r2
= lldp_tlv_packet_exit_container(tlv
);
545 return r
< 0 ? r
: r2
;
548 int sd_lldp_packet_read_port_protocol_vlan_id(sd_lldp_packet
*tlv
, uint8_t *flags
, uint16_t *id
) {
551 assert_return(tlv
, -EINVAL
);
553 r
= lldp_tlv_packet_enter_container_oui(tlv
, LLDP_OUI_802_1
, LLDP_OUI_SUBTYPE_802_1_PORT_PROTOCOL_VLAN_ID
);
557 r
= tlv_packet_read_u8(tlv
, flags
);
559 r
= tlv_packet_read_u16(tlv
, id
);
561 r2
= lldp_tlv_packet_exit_container(tlv
);
563 return r
< 0 ? r
: r2
;
566 int sd_lldp_packet_read_vlan_name(tlv_packet
*tlv
, uint16_t *vlan_id
, char **name
, uint16_t *length
) {
570 assert_return(tlv
, -EINVAL
);
572 r
= lldp_tlv_packet_enter_container_oui(tlv
, LLDP_OUI_802_1
, LLDP_OUI_SUBTYPE_802_1_VLAN_NAME
);
576 r
= tlv_packet_read_u16(tlv
, vlan_id
);
578 r
= tlv_packet_read_u8(tlv
, &len
);
580 r
= tlv_packet_read_string(tlv
, name
, length
);
582 if (r
>= 0 && len
< *length
)
585 r2
= lldp_tlv_packet_exit_container(tlv
);
587 return r
< 0 ? r
: r2
;
590 int sd_lldp_packet_read_management_vid(tlv_packet
*tlv
, uint16_t *id
) {
593 assert_return(tlv
, -EINVAL
);
595 r
= lldp_tlv_packet_enter_container_oui(tlv
, LLDP_OUI_802_1
, LLDP_OUI_SUBTYPE_802_1_MANAGEMENT_VID
);
599 r
= tlv_packet_read_u16(tlv
, id
);
600 r2
= lldp_tlv_packet_exit_container(tlv
);
602 return r
< 0 ? r
: r2
;
605 int sd_lldp_packet_read_link_aggregation(sd_lldp_packet
*tlv
, uint8_t *status
, uint32_t *id
) {
608 assert_return(tlv
, -EINVAL
);
610 r
= lldp_tlv_packet_enter_container_oui(tlv
, LLDP_OUI_802_1
, LLDP_OUI_SUBTYPE_802_1_LINK_AGGREGATION
);
614 r
= tlv_packet_read_u8(tlv
, status
);
616 r
= tlv_packet_read_u32(tlv
, id
);
618 r2
= lldp_tlv_packet_exit_container(tlv
);
620 return r
< 0 ? r
: r2
;
623 int sd_lldp_packet_get_destination_type(tlv_packet
*tlv
, int *dest
) {
624 assert_return(tlv
, -EINVAL
);
625 assert_return(dest
, -EINVAL
);
627 /* 802.1AB-2009, Table 7-1 */
628 if (!memcmp(&tlv
->mac
, LLDP_MAC_NEAREST_BRIDGE
, ETH_ALEN
))
629 *dest
= SD_LLDP_DESTINATION_TYPE_NEAREST_BRIDGE
;
630 else if (!memcmp(&tlv
->mac
, LLDP_MAC_NEAREST_NON_TPMR_BRIDGE
, ETH_ALEN
))
631 *dest
= SD_LLDP_DESTINATION_TYPE_NEAREST_NON_TPMR_BRIDGE
;
632 else if (!memcmp(&tlv
->mac
, LLDP_MAC_NEAREST_CUSTOMER_BRIDGE
, ETH_ALEN
))
633 *dest
= SD_LLDP_DESTINATION_TYPE_NEAREST_CUSTOMER_BRIDGE
;