1 /*-*- Mode: C; c-basic-offset: 8; indent-tabs-mode: nil -*-*/
4 This file is part of systemd.
6 Copyright (C) 2014 Tom Gundersen
7 Copyright (C) 2014 Susant Sahani
9 systemd is free software; you can redistribute it and/or modify it
10 under the terms of the GNU Lesser General Public License as published by
11 the Free Software Foundation; either version 2.1 of the License, or
12 (at your option) any later version.
14 systemd is distributed in the hope that it will be useful, but
15 WITHOUT ANY WARRANTY; without even the implied warranty of
16 MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the GNU
17 Lesser General Public License for more details.
19 You should have received a copy of the GNU Lesser General Public License
20 along with systemd; If not, see <http://www.gnu.org/licenses/>.
23 #include "lldp-internal.h"
25 /* We store maximum 1K chassis entries */
26 #define LLDP_MIB_MAX_CHASSIS 1024
28 /* Maximum Ports can be attached to any chassis */
29 #define LLDP_MIB_MAX_PORT_PER_CHASSIS 32
31 int lldp_read_chassis_id(tlv_packet *tlv,
38 assert_return(tlv, -EINVAL);
40 r = lldp_tlv_packet_enter_container(tlv, LLDP_TYPE_CHASSIS_ID);
44 r = tlv_packet_read_u8(tlv, &subtype);
49 case LLDP_CHASSIS_SUBTYPE_MAC_ADDRESS:
51 r = tlv_packet_read_bytes(tlv, data, length);
64 (void)lldp_tlv_packet_exit_container(tlv);
70 int lldp_read_port_id(tlv_packet *tlv,
78 assert_return(tlv, -EINVAL);
80 r = lldp_tlv_packet_enter_container(tlv, LLDP_TYPE_PORT_ID);
84 r = tlv_packet_read_u8(tlv, &subtype);
89 case LLDP_PORT_SUBTYPE_INTERFACE_NAME:
91 r = tlv_packet_read_string(tlv, &s, length);
95 *data = (uint8_t *) s;
98 case LLDP_PORT_SUBTYPE_MAC_ADDRESS:
100 r = tlv_packet_read_bytes(tlv, data, length);
113 (void)lldp_tlv_packet_exit_container(tlv);
119 int lldp_read_ttl(tlv_packet *tlv, uint16_t *ttl) {
122 assert_return(tlv, -EINVAL);
124 r = lldp_tlv_packet_enter_container(tlv, LLDP_TYPE_TTL);
128 r = tlv_packet_read_u16(tlv, ttl);
130 (void) lldp_tlv_packet_exit_container(tlv);
136 /* 10.5.5.2.2 mibUpdateObjects ()
137 * The mibUpdateObjects () procedure updates the MIB objects corresponding to
138 * the TLVs contained in the received LLDPDU for the LLDP remote system
139 * indicated by the LLDP remote systems update process defined in 10.3.5 */
141 int lldp_mib_update_objects(lldp_chassis *c, tlv_packet *tlv) {
142 lldp_neighbour_port *p;
143 uint16_t length, ttl;
148 assert_return(c, -EINVAL);
149 assert_return(tlv, -EINVAL);
151 r = lldp_read_port_id(tlv, &type, &length, &data);
155 /* Update the packet if we already have */
156 LIST_FOREACH(port, p, c->ports) {
158 if ((p->type == type && p->length == length && !memcmp(p->data, data, p->length))) {
160 r = lldp_read_ttl(tlv, &ttl);
164 p->until = ttl * USEC_PER_SEC + now(clock_boottime_or_monotonic());
166 tlv_packet_free(p->packet);
169 prioq_reshuffle(p->c->by_expiry, p, &p->prioq_idx);
178 int lldp_mib_remove_objects(lldp_chassis *c, tlv_packet *tlv) {
179 lldp_neighbour_port *p, *q;
185 assert_return(c, -EINVAL);
186 assert_return(tlv, -EINVAL);
188 r = lldp_read_port_id(tlv, &type, &length, &data);
192 LIST_FOREACH_SAFE(port, p, q, c->ports) {
195 if (p->type == type && p->length == length && !memcmp(p->data, data, p->length)) {
196 lldp_neighbour_port_remove_and_free(p);
204 int lldp_mib_add_objects(Prioq *by_expiry,
205 Hashmap *neighbour_mib,
207 _cleanup_lldp_neighbour_port_free_ lldp_neighbour_port *p = NULL;
208 _cleanup_lldp_chassis_free_ lldp_chassis *c = NULL;
209 lldp_chassis_id chassis_id;
210 bool new_chassis = false;
211 uint8_t subtype, *data;
212 uint16_t ttl, length;
215 assert_return(by_expiry, -EINVAL);
216 assert_return(neighbour_mib, -EINVAL);
217 assert_return(tlv, -EINVAL);
219 r = lldp_read_chassis_id(tlv, &subtype, &length, &data);
223 r = lldp_read_ttl(tlv, &ttl);
228 chassis_id.type = subtype;
229 chassis_id.length = length;
230 chassis_id.data = data;
232 /* Try to find the Chassis */
233 c = hashmap_get(neighbour_mib, &chassis_id);
236 /* Don't create chassis if ttl 0 is received . Silently drop it */
238 log_lldp("TTL value 0 received. Skiping Chassis creation.");
242 /* Admission Control: Can we store this packet ? */
243 if (hashmap_size(neighbour_mib) >= LLDP_MIB_MAX_CHASSIS) {
245 log_lldp("Exceeding number of chassie: %d. Dropping ...",
246 hashmap_size(neighbour_mib));
250 r = lldp_chassis_new(tlv, by_expiry, neighbour_mib, &c);
256 r = hashmap_put(neighbour_mib, &c->chassis_id, c);
262 /* When the TTL field is set to zero, the receiving LLDP agent is notified all
263 * system information associated with the LLDP agent/port is to be deleted */
265 log_lldp("TTL value 0 received . Deleting associated Port ...");
267 lldp_mib_remove_objects(c, tlv);
273 /* if we already have this port just update it */
274 r = lldp_mib_update_objects(c, tlv);
280 /* Admission Control: Can this port attached to the existing chassis ? */
281 if (REFCNT_GET(c->n_ref) >= LLDP_MIB_MAX_PORT_PER_CHASSIS) {
282 log_lldp("Port limit reached. Chassis has: %d ports. Dropping ...",
283 REFCNT_GET(c->n_ref));
290 /* This is a new port */
291 r = lldp_neighbour_port_new(c, tlv, &p);
295 r = prioq_put(c->by_expiry, p, &p->prioq_idx);
299 /* Attach new port to chassis */
300 LIST_PREPEND(port, c->ports, p);
301 REFCNT_INC(c->n_ref);
309 tlv_packet_free(tlv);
312 hashmap_remove(neighbour_mib, &c->chassis_id);
317 void lldp_neighbour_port_remove_and_free(lldp_neighbour_port *p) {
325 prioq_remove(c->by_expiry, p, &p->prioq_idx);
327 LIST_REMOVE(port, c->ports, p);
328 lldp_neighbour_port_free(p);
330 /* Drop the Chassis if no port is attached */
331 if (REFCNT_DEC(c->n_ref) <= 1) {
332 hashmap_remove(c->neighbour_mib, &c->chassis_id);
333 lldp_chassis_free(c);
337 void lldp_neighbour_port_free(lldp_neighbour_port *p) {
342 tlv_packet_free(p->packet);
348 int lldp_neighbour_port_new(lldp_chassis *c,
350 lldp_neighbour_port **ret) {
351 _cleanup_lldp_neighbour_port_free_ lldp_neighbour_port *p;
352 uint16_t length, ttl;
359 r = lldp_read_port_id(tlv, &type, &length, &data);
363 r = lldp_read_ttl(tlv, &ttl);
367 p = new0(lldp_neighbour_port, 1);
375 p->prioq_idx = PRIOQ_IDX_NULL;
376 p->until = ttl * USEC_PER_SEC + now(clock_boottime_or_monotonic());
378 p->data = memdup(data, length);
388 void lldp_chassis_free(lldp_chassis *c) {
393 if (REFCNT_GET(c->n_ref) > 1)
396 free(c->chassis_id.data);
400 int lldp_chassis_new(tlv_packet *tlv,
402 Hashmap *neighbour_mib,
403 lldp_chassis **ret) {
404 _cleanup_lldp_chassis_free_ lldp_chassis *c;
412 r = lldp_read_chassis_id(tlv, &type, &length, &data);
416 c = new0(lldp_chassis, 1);
420 c->n_ref = REFCNT_INIT;
421 c->chassis_id.type = type;
422 c->chassis_id.length = length;
424 c->chassis_id.data = memdup(data, length);
425 if (!c->chassis_id.data)
428 LIST_HEAD_INIT(c->ports);
430 c->by_expiry = by_expiry;
431 c->neighbour_mib = neighbour_mib;