summaryrefslogtreecommitdiff
path: root/src/libsystemd-network/lldp-tlv.c
diff options
context:
space:
mode:
authorLennart Poettering <lennart@poettering.net>2016-02-19 17:58:52 +0100
committerLennart Poettering <lennart@poettering.net>2016-02-21 20:40:56 +0100
commit34437b4f9c9c51b0a6f93788bdb9a105b8e46b66 (patch)
tree4c5069ae6356036a4f347ca7acd8df8d0332d97c /src/libsystemd-network/lldp-tlv.c
parent1b4cd0cf11feb7d41f2eff17f86fa55b31bb6841 (diff)
sd-lldp: rework sd-lldp API
This reworks the sd-lldp substantially, simplifying things on one hand, and extending the logic a bit on the other. Specifically: - Besides the sd_lldp object only one other object is maintained now, sd_lldp_neighbor. It's used both as storage for literal LLDP packets, and for maintainging info about peers in the database. Separation between packet, TLV and chassis data is not maintained anymore. This should be a major simplification. - The sd-lldp API has been extended so that a couple of per-neighbor fields may be queried directly, without iterating through the object. Other fields that may appear multiple times, OTOH have to be iterated through. - The maximum number of entries in the neighbor database is now configurable during runtime. - The generation of callbacks from sd_lldp objects is more restricted: callbacks are only invoked when actual data changed. - The TTL information is now hooked with a timer event, so that removals from the neighbor database due to TTLs now result in a callback event. - Querying LLDP neighbor database will now return a strictly ordered array, to guarantee stability. - A "capabilities" mask may now be configured, that selects what type of LLDP neighbor data is collected. This may be used to restrict collection of LLDP info about routers instead of all neighbors. This is now exposed via networkd's LLDP= setting. - sd-lldp's API to serialize the collected data to text files has been removed. Instead, there's now an API to extract the raw binary data from LLDP neighbor objects, as well as one to convert this raw binary data back to an LLDP neighbor object. networkd will save this raw binary data to /run now, and the client side can simply parse the information. - support for parsing the more exotic TLVs has been removed, since we are not using that. Instead there are now APIs to extract the raw data from TLVs. Given how easy it is to parse the TLVs clients should do so now directly instead of relying on our APIs for that. - A lot of the APIs that parse out LLDP strings have been simplified so that they actually return strings, instead of char arrays with a length. To deal with possibly dangerous characters the strings are escaped if needed. - APIs to extract and format the chassis and port IDs as strings has been added. - lldp.h has been simplified a lot. The enums are anonymous now, since they were never used as enums, but simply as constants. Most definitions we don't actually use ourselves have eben removed.
Diffstat (limited to 'src/libsystemd-network/lldp-tlv.c')
-rw-r--r--src/libsystemd-network/lldp-tlv.c638
1 files changed, 0 insertions, 638 deletions
diff --git a/src/libsystemd-network/lldp-tlv.c b/src/libsystemd-network/lldp-tlv.c
deleted file mode 100644
index 9170b50691..0000000000
--- a/src/libsystemd-network/lldp-tlv.c
+++ /dev/null
@@ -1,638 +0,0 @@
-/***
- This file is part of systemd.
-
- Copyright (C) 2014 Tom Gundersen
- Copyright (C) 2014 Susant Sahani
-
- systemd is free software; you can redistribute it and/or modify it
- under the terms of the GNU Lesser General Public License as published by
- the Free Software Foundation; either version 2.1 of the License, or
- (at your option) any later version.
-
- systemd is distributed in the hope that it will be useful, but
- WITHOUT ANY WARRANTY; without even the implied warranty of
- MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the GNU
- Lesser General Public License for more details.
-
- You should have received a copy of the GNU Lesser General Public License
- along with systemd; If not, see <http://www.gnu.org/licenses/>.
-***/
-
-#include <arpa/inet.h>
-#include <net/ethernet.h>
-
-#include "alloc-util.h"
-#include "lldp-tlv.h"
-#include "macro.h"
-
-int tlv_section_new(tlv_section **ret) {
- tlv_section *s;
-
- s = new0(tlv_section, 1);
- if (!s)
- return -ENOMEM;
-
- *ret = s;
-
- return 0;
-}
-
-void tlv_section_free(tlv_section *m) {
-
- if (!m)
- return;
-
- free(m);
-}
-
-int tlv_packet_new(tlv_packet **ret) {
- tlv_packet *m;
-
- m = new0(tlv_packet, 1);
- if (!m)
- return -ENOMEM;
-
- LIST_HEAD_INIT(m->sections);
- m->n_ref = 1;
-
- *ret = m;
-
- return 0;
-}
-
-tlv_packet *sd_lldp_packet_ref(tlv_packet *m) {
-
- if (!m)
- return NULL;
-
- assert(m->n_ref > 0);
- m->n_ref++;
-
- return m;
-}
-
-tlv_packet *sd_lldp_packet_unref(tlv_packet *m) {
- tlv_section *s, *n;
-
- if (!m)
- return NULL;
-
- assert(m->n_ref > 0);
- m->n_ref--;
-
- if (m->n_ref > 0)
- return m;
-
- LIST_FOREACH_SAFE(section, s, n, m->sections)
- tlv_section_free(s);
-
- free(m);
- return NULL;
-}
-
-int tlv_packet_append_bytes(tlv_packet *m, const void *data, size_t data_length) {
- uint8_t *p;
-
- assert_return(m, -EINVAL);
- assert_return(data, -EINVAL);
- assert_return(data_length, -EINVAL);
-
- if (m->length + data_length > ETHER_MAX_LEN)
- return -ENOMEM;
-
- p = m->pdu + m->length;
- memcpy(p, data, data_length);
- m->length += data_length;
-
- return 0;
-}
-
-int tlv_packet_append_u8(tlv_packet *m, uint8_t data) {
-
- assert_return(m, -EINVAL);
-
- return tlv_packet_append_bytes(m, &data, sizeof(uint8_t));
-}
-
-int tlv_packet_append_u16(tlv_packet *m, uint16_t data) {
- uint16_t type;
-
- assert_return(m, -EINVAL);
-
- type = htons(data);
-
- return tlv_packet_append_bytes(m, &type, sizeof(uint16_t));
-}
-
-int tlv_packet_append_u32(tlv_packet *m, uint32_t data) {
- uint32_t type;
-
- assert_return(m, -EINVAL);
-
- type = htonl(data);
-
- return tlv_packet_append_bytes(m, &type, sizeof(uint32_t));
-}
-
-int tlv_packet_append_string(tlv_packet *m, char *data, uint16_t size) {
-
- assert_return(m, -EINVAL);
-
- return tlv_packet_append_bytes(m, data, size);
-}
-
-int lldp_tlv_packet_open_container(tlv_packet *m, uint16_t type) {
-
- assert_return(m, -EINVAL);
-
- m->container_pos = m->pdu + m->length;
-
- return tlv_packet_append_u16(m, type << 9);
-}
-
-int lldp_tlv_packet_close_container(tlv_packet *m) {
- uint16_t type;
-
- assert_return(m, -EINVAL);
- assert_return(m->container_pos, -EINVAL);
-
- memcpy(&type, m->container_pos, sizeof(uint16_t));
-
- type |= htons(((m->pdu + m->length) - (m->container_pos + 2)) & 0x01ff);
- memcpy(m->container_pos, &type, sizeof(uint16_t));
-
- return 0;
-}
-
-static inline int tlv_packet_read_internal(tlv_section *m, void **data) {
-
- assert_return(m->read_pos, -EINVAL);
-
- *data = m->read_pos;
-
- return 0;
-}
-
-int tlv_packet_read_u8(tlv_packet *m, uint8_t *data) {
- void *val = NULL;
- int r;
-
- assert_return(m, -EINVAL);
-
- r = tlv_packet_read_internal(m->container, &val);
- if (r < 0)
- return r;
-
- memcpy(data, val, sizeof(uint8_t));
-
- m->container->read_pos ++;
-
- return 0;
-}
-
-int tlv_packet_read_u16(tlv_packet *m, uint16_t *data) {
- uint16_t t;
- void *val = NULL;
- int r;
-
- assert_return(m, -EINVAL);
-
- r = tlv_packet_read_internal(m->container, &val);
- if (r < 0)
- return r;
-
- memcpy(&t, val, sizeof(uint16_t));
- *data = ntohs(t);
-
- m->container->read_pos += 2;
-
- return 0;
-}
-
-int tlv_packet_read_u32(tlv_packet *m, uint32_t *data) {
- uint32_t t;
- void *val;
- int r;
-
- assert_return(m, -EINVAL);
-
- r = tlv_packet_read_internal(m->container, &val);
- if (r < 0)
- return r;
-
- memcpy(&t, val, sizeof(uint32_t));
- *data = ntohl(t);
-
- m->container->read_pos += 4;
-
- return r;
-}
-
-int tlv_packet_read_string(tlv_packet *m, char **data, uint16_t *data_length) {
- void *val = NULL;
- int r;
-
- assert_return(m, -EINVAL);
-
- r = tlv_packet_read_internal(m->container, &val);
- if (r < 0)
- return r;
-
- *data = (char *) val;
- *data_length = m->container->data + m->container->length - m->container->read_pos;
-
- m->container->read_pos += *data_length;
-
- return 0;
-}
-
-int tlv_packet_read_bytes(tlv_packet *m, uint8_t **data, uint16_t *data_length) {
- void *val = NULL;
- int r;
-
- assert_return(m, -EINVAL);
-
- r = tlv_packet_read_internal(m->container, &val);
- if (r < 0)
- return r;
-
- *data = (uint8_t *) val;
- *data_length = m->container->data + m->container->length - m->container->read_pos;
-
- m->container->read_pos += *data_length;
-
- return 0;
-}
-
-/* parse raw TLV packet */
-int tlv_packet_parse_pdu(tlv_packet *m, uint16_t size) {
- tlv_section *section, *tail;
- uint16_t t, l;
- uint8_t *p;
- int r;
-
- assert_return(m, -EINVAL);
- assert_return(size, -EINVAL);
-
- p = m->pdu;
-
- /* extract Ethernet header */
- memcpy(&m->mac, p, ETH_ALEN);
- p += sizeof(struct ether_header);
-
- for (l = 0; l <= size; ) {
- r = tlv_section_new(&section);
- if (r < 0)
- return r;
-
- memcpy(&t, p, sizeof(uint16_t));
-
- section->type = ntohs(t) >> 9;
- section->length = ntohs(t) & 0x01ff;
-
- if (section->type == LLDP_TYPE_END || section->type >=_LLDP_TYPE_MAX) {
- tlv_section_free(section);
- break;
- }
-
- p += 2;
-
- if (section->type == LLDP_TYPE_PRIVATE &&
- section->length >= LLDP_OUI_LEN + 1) {
- section->oui = p;
- p += LLDP_OUI_LEN;
- section->subtype = *p++;
-
- section->length -= LLDP_OUI_LEN + 1;
- l += LLDP_OUI_LEN + 1;
- }
-
- section->data = p;
-
- LIST_FIND_TAIL(section, m->sections, tail);
- LIST_INSERT_AFTER(section, m->sections, tail, section);
-
- p += section->length;
- l += (section->length + 2);
- }
-
- return 0;
-}
-
-int lldp_tlv_packet_enter_container(tlv_packet *m, uint16_t type) {
- tlv_section *s;
-
- assert_return(m, -EINVAL);
- assert_return(type != LLDP_TYPE_PRIVATE, -EINVAL);
-
- LIST_FOREACH(section, s, m->sections)
- if (s->type == type)
- break;
- if (!s)
- return -1;
-
- m->container = s;
-
- m->container->read_pos = s->data;
- if (!m->container->read_pos) {
- m->container = NULL;
- return -1;
- }
-
- return 0;
-}
-
-int lldp_tlv_packet_enter_container_oui(tlv_packet *m, const uint8_t *oui, uint8_t subtype) {
- tlv_section *s;
-
- assert_return(m, -EINVAL);
- assert_return(oui, -EINVAL);
-
- LIST_FOREACH(section, s, m->sections) {
- if (s->type == LLDP_TYPE_PRIVATE &&
- s->oui &&
- s->subtype == subtype &&
- !memcmp(s->oui, oui, LLDP_OUI_LEN))
- break;
- }
-
- if (!s)
- return -1;
-
- m->container = s;
-
- m->container->read_pos = s->data;
- if (!m->container->read_pos) {
- m->container = NULL;
- return -1;
- }
-
- return 0;
-}
-
-int lldp_tlv_packet_exit_container(tlv_packet *m) {
- assert_return(m, -EINVAL);
-
- m->container = 0;
-
- return 0;
-}
-
-static int lldp_tlv_packet_read_u16_tlv(tlv_packet *tlv, uint16_t type, uint16_t *value) {
- int r, r2;
-
- assert_return(tlv, -EINVAL);
-
- r = lldp_tlv_packet_enter_container(tlv, type);
- if (r < 0)
- return r;
-
- r = tlv_packet_read_u16(tlv, value);
- r2 = lldp_tlv_packet_exit_container(tlv);
-
- return r < 0 ? r : r2;
-}
-
-static int lldp_tlv_packet_read_string_tlv(tlv_packet *tlv, uint16_t type, char **data, uint16_t *length) {
- char *s;
- int r, r2;
-
- assert_return(tlv, -EINVAL);
-
- r = lldp_tlv_packet_enter_container(tlv, type);
- if (r < 0)
- return r;
-
- r = tlv_packet_read_string(tlv, &s, length);
- if (r < 0)
- goto out;
-
- *data = (char *) s;
-
- out:
- r2 = lldp_tlv_packet_exit_container(tlv);
-
- return r < 0 ? r : r2;
-}
-
-int sd_lldp_packet_read_chassis_id(tlv_packet *tlv,
- uint8_t *type,
- uint8_t **data,
- uint16_t *length) {
- uint8_t subtype;
- int r, r2;
-
- assert_return(tlv, -EINVAL);
-
- r = lldp_tlv_packet_enter_container(tlv, LLDP_TYPE_CHASSIS_ID);
- if (r < 0)
- return r;
-
- r = tlv_packet_read_u8(tlv, &subtype);
- if (r < 0)
- goto out;
-
- switch (subtype) {
- case LLDP_CHASSIS_SUBTYPE_MAC_ADDRESS:
-
- r = tlv_packet_read_bytes(tlv, data, length);
- if (r < 0)
- goto out;
-
- break;
- default:
- r = -EOPNOTSUPP;
- break;
- }
-
- *type = subtype;
-
- out:
- r2 = lldp_tlv_packet_exit_container(tlv);
-
- return r < 0 ? r : r2;
-}
-
-int sd_lldp_packet_read_port_id(tlv_packet *tlv,
- uint8_t *type,
- uint8_t **data,
- uint16_t *length) {
- uint8_t subtype;
- char *s;
- int r, r2;
-
- assert_return(tlv, -EINVAL);
-
- r = lldp_tlv_packet_enter_container(tlv, LLDP_TYPE_PORT_ID);
- if (r < 0)
- return r;
-
- r = tlv_packet_read_u8(tlv, &subtype);
- if (r < 0)
- goto out;
-
- switch (subtype) {
- case LLDP_PORT_SUBTYPE_PORT_COMPONENT:
- case LLDP_PORT_SUBTYPE_INTERFACE_ALIAS:
- case LLDP_PORT_SUBTYPE_INTERFACE_NAME:
- case LLDP_PORT_SUBTYPE_LOCALLY_ASSIGNED:
-
- r = tlv_packet_read_string(tlv, &s, length);
- if (r < 0)
- goto out;
-
- *data = (uint8_t *) s;
-
- break;
- case LLDP_PORT_SUBTYPE_MAC_ADDRESS:
-
- r = tlv_packet_read_bytes(tlv, data, length);
- if (r < 0)
- goto out;
-
- break;
- default:
- r = -EOPNOTSUPP;
- break;
- }
-
- *type = subtype;
-
- out:
- r2 = lldp_tlv_packet_exit_container(tlv);
-
- return r < 0 ? r : r2;
-}
-
-int sd_lldp_packet_read_ttl(tlv_packet *tlv, uint16_t *ttl) {
- return lldp_tlv_packet_read_u16_tlv(tlv, LLDP_TYPE_TTL, ttl);
-}
-
-int sd_lldp_packet_read_system_name(tlv_packet *tlv,
- char **data,
- uint16_t *length) {
- return lldp_tlv_packet_read_string_tlv(tlv, LLDP_TYPE_SYSTEM_NAME, data, length);
-}
-
-int sd_lldp_packet_read_system_description(tlv_packet *tlv,
- char **data,
- uint16_t *length) {
- return lldp_tlv_packet_read_string_tlv(tlv, LLDP_TYPE_SYSTEM_DESCRIPTION, data, length);
-}
-
-int sd_lldp_packet_read_port_description(tlv_packet *tlv,
- char **data,
- uint16_t *length) {
- return lldp_tlv_packet_read_string_tlv(tlv, LLDP_TYPE_PORT_DESCRIPTION, data, length);
-}
-
-int sd_lldp_packet_read_system_capability(tlv_packet *tlv, uint16_t *data) {
- return lldp_tlv_packet_read_u16_tlv(tlv, LLDP_TYPE_SYSTEM_CAPABILITIES, data);
-}
-
-int sd_lldp_packet_read_port_vlan_id(tlv_packet *tlv, uint16_t *id) {
- int r, r2;
-
- assert_return(tlv, -EINVAL);
-
- r = lldp_tlv_packet_enter_container_oui(tlv, LLDP_OUI_802_1, LLDP_OUI_SUBTYPE_802_1_PORT_VLAN_ID);
- if (r < 0)
- return r;
-
- r = tlv_packet_read_u16(tlv, id);
- r2 = lldp_tlv_packet_exit_container(tlv);
-
- return r < 0 ? r : r2;
-}
-
-int sd_lldp_packet_read_port_protocol_vlan_id(sd_lldp_packet *tlv, uint8_t *flags, uint16_t *id) {
- int r, r2;
-
- assert_return(tlv, -EINVAL);
-
- r = lldp_tlv_packet_enter_container_oui(tlv, LLDP_OUI_802_1, LLDP_OUI_SUBTYPE_802_1_PORT_PROTOCOL_VLAN_ID);
- if (r < 0)
- return r;
-
- r = tlv_packet_read_u8(tlv, flags);
- if (r >= 0)
- r = tlv_packet_read_u16(tlv, id);
-
- r2 = lldp_tlv_packet_exit_container(tlv);
-
- return r < 0 ? r : r2;
-}
-
-int sd_lldp_packet_read_vlan_name(tlv_packet *tlv, uint16_t *vlan_id, char **name, uint16_t *length) {
- int r, r2;
- uint8_t len = 0;
-
- assert_return(tlv, -EINVAL);
-
- r = lldp_tlv_packet_enter_container_oui(tlv, LLDP_OUI_802_1, LLDP_OUI_SUBTYPE_802_1_VLAN_NAME);
- if (r < 0)
- return r;
-
- r = tlv_packet_read_u16(tlv, vlan_id);
- if (r >= 0)
- r = tlv_packet_read_u8(tlv, &len);
- if (r >= 0)
- r = tlv_packet_read_string(tlv, name, length);
-
- if (r >= 0 && len < *length)
- *length = len;
-
- r2 = lldp_tlv_packet_exit_container(tlv);
-
- return r < 0 ? r : r2;
-}
-
-int sd_lldp_packet_read_management_vid(tlv_packet *tlv, uint16_t *id) {
- int r, r2;
-
- assert_return(tlv, -EINVAL);
-
- r = lldp_tlv_packet_enter_container_oui(tlv, LLDP_OUI_802_1, LLDP_OUI_SUBTYPE_802_1_MANAGEMENT_VID);
- if (r < 0)
- return r;
-
- r = tlv_packet_read_u16(tlv, id);
- r2 = lldp_tlv_packet_exit_container(tlv);
-
- return r < 0 ? r : r2;
-}
-
-int sd_lldp_packet_read_link_aggregation(sd_lldp_packet *tlv, uint8_t *status, uint32_t *id) {
- int r, r2;
-
- assert_return(tlv, -EINVAL);
-
- r = lldp_tlv_packet_enter_container_oui(tlv, LLDP_OUI_802_1, LLDP_OUI_SUBTYPE_802_1_LINK_AGGREGATION);
- if (r < 0)
- return r;
-
- r = tlv_packet_read_u8(tlv, status);
- if (r >= 0)
- r = tlv_packet_read_u32(tlv, id);
-
- r2 = lldp_tlv_packet_exit_container(tlv);
-
- return r < 0 ? r : r2;
-}
-
-int sd_lldp_packet_get_destination_type(tlv_packet *tlv, int *dest) {
- assert_return(tlv, -EINVAL);
- assert_return(dest, -EINVAL);
-
- /* 802.1AB-2009, Table 7-1 */
- if (!memcmp(&tlv->mac, LLDP_MAC_NEAREST_BRIDGE, ETH_ALEN))
- *dest = SD_LLDP_DESTINATION_TYPE_NEAREST_BRIDGE;
- else if (!memcmp(&tlv->mac, LLDP_MAC_NEAREST_NON_TPMR_BRIDGE, ETH_ALEN))
- *dest = SD_LLDP_DESTINATION_TYPE_NEAREST_NON_TPMR_BRIDGE;
- else if (!memcmp(&tlv->mac, LLDP_MAC_NEAREST_CUSTOMER_BRIDGE, ETH_ALEN))
- *dest = SD_LLDP_DESTINATION_TYPE_NEAREST_CUSTOMER_BRIDGE;
- else
- return -EINVAL;
-
- return 0;
-}