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/>.
25 #include "alloc-util.h"
26 #include "lldp-internal.h"
28 /* We store maximum 1K chassis entries */
29 #define LLDP_MIB_MAX_CHASSIS 1024
31 /* Maximum Ports can be attached to any chassis */
32 #define LLDP_MIB_MAX_PORT_PER_CHASSIS 32
34 /* 10.5.5.2.2 mibUpdateObjects ()
35 * The mibUpdateObjects () procedure updates the MIB objects corresponding to
36 * the TLVs contained in the received LLDPDU for the LLDP remote system
37 * indicated by the LLDP remote systems update process defined in 10.3.5 */
39 int lldp_mib_update_objects(lldp_chassis
*c
, tlv_packet
*tlv
) {
40 lldp_neighbour_port
*p
;
46 assert_return(c
, -EINVAL
);
47 assert_return(tlv
, -EINVAL
);
49 r
= sd_lldp_packet_read_port_id(tlv
, &type
, &data
, &length
);
53 /* Update the packet if we already have */
54 LIST_FOREACH(port
, p
, c
->ports
) {
56 if ((p
->type
== type
&& p
->length
== length
&& !memcmp(p
->data
, data
, p
->length
))) {
58 r
= sd_lldp_packet_read_ttl(tlv
, &ttl
);
62 p
->until
= ttl
* USEC_PER_SEC
+ now(clock_boottime_or_monotonic());
64 sd_lldp_packet_unref(p
->packet
);
67 prioq_reshuffle(p
->c
->by_expiry
, p
, &p
->prioq_idx
);
76 int lldp_mib_remove_objects(lldp_chassis
*c
, tlv_packet
*tlv
) {
77 lldp_neighbour_port
*p
, *q
;
83 assert_return(c
, -EINVAL
);
84 assert_return(tlv
, -EINVAL
);
86 r
= sd_lldp_packet_read_port_id(tlv
, &type
, &data
, &length
);
90 LIST_FOREACH_SAFE(port
, p
, q
, c
->ports
) {
93 if (p
->type
== type
&& p
->length
== length
&& !memcmp(p
->data
, data
, p
->length
)) {
94 lldp_neighbour_port_remove_and_free(p
);
102 int lldp_mib_add_objects(Prioq
*by_expiry
,
103 Hashmap
*neighbour_mib
,
105 _cleanup_lldp_neighbour_port_free_ lldp_neighbour_port
*p
= NULL
;
106 _cleanup_lldp_chassis_free_ lldp_chassis
*c
= NULL
;
107 lldp_chassis_id chassis_id
;
108 bool new_chassis
= false;
109 uint8_t subtype
, *data
;
110 uint16_t ttl
, length
;
113 assert_return(by_expiry
, -EINVAL
);
114 assert_return(neighbour_mib
, -EINVAL
);
115 assert_return(tlv
, -EINVAL
);
117 r
= sd_lldp_packet_read_chassis_id(tlv
, &subtype
, &data
, &length
);
121 r
= sd_lldp_packet_read_ttl(tlv
, &ttl
);
126 chassis_id
.type
= subtype
;
127 chassis_id
.length
= length
;
128 chassis_id
.data
= data
;
130 /* Try to find the Chassis */
131 c
= hashmap_get(neighbour_mib
, &chassis_id
);
134 /* Don't create chassis if ttl 0 is received . Silently drop it */
136 log_lldp("TTL value 0 received. Skiping Chassis creation.");
140 /* Admission Control: Can we store this packet ? */
141 if (hashmap_size(neighbour_mib
) >= LLDP_MIB_MAX_CHASSIS
) {
143 log_lldp("Exceeding number of chassie: %d. Dropping ...",
144 hashmap_size(neighbour_mib
));
148 r
= lldp_chassis_new(tlv
, by_expiry
, neighbour_mib
, &c
);
154 r
= hashmap_put(neighbour_mib
, &c
->chassis_id
, c
);
160 /* When the TTL field is set to zero, the receiving LLDP agent is notified all
161 * system information associated with the LLDP agent/port is to be deleted */
163 log_lldp("TTL value 0 received . Deleting associated Port ...");
165 lldp_mib_remove_objects(c
, tlv
);
171 /* if we already have this port just update it */
172 r
= lldp_mib_update_objects(c
, tlv
);
178 /* Admission Control: Can this port attached to the existing chassis ? */
179 if (c
->n_ref
>= LLDP_MIB_MAX_PORT_PER_CHASSIS
) {
180 log_lldp("Port limit reached. Chassis has: %d ports. Dropping ...", c
->n_ref
);
187 /* This is a new port */
188 r
= lldp_neighbour_port_new(c
, tlv
, &p
);
192 r
= prioq_put(c
->by_expiry
, p
, &p
->prioq_idx
);
196 /* Attach new port to chassis */
197 LIST_PREPEND(port
, c
->ports
, p
);
206 sd_lldp_packet_unref(tlv
);
209 hashmap_remove(neighbour_mib
, &c
->chassis_id
);
214 void lldp_neighbour_port_remove_and_free(lldp_neighbour_port
*p
) {
222 prioq_remove(c
->by_expiry
, p
, &p
->prioq_idx
);
224 LIST_REMOVE(port
, c
->ports
, p
);
225 lldp_neighbour_port_free(p
);
227 /* Drop the Chassis if no port is attached */
230 hashmap_remove(c
->neighbour_mib
, &c
->chassis_id
);
231 lldp_chassis_free(c
);
235 void lldp_neighbour_port_free(lldp_neighbour_port
*p
) {
240 sd_lldp_packet_unref(p
->packet
);
246 int lldp_neighbour_port_new(lldp_chassis
*c
,
248 lldp_neighbour_port
**ret
) {
249 _cleanup_lldp_neighbour_port_free_ lldp_neighbour_port
*p
= NULL
;
250 uint16_t length
, ttl
;
257 r
= sd_lldp_packet_read_port_id(tlv
, &type
, &data
, &length
);
261 r
= sd_lldp_packet_read_ttl(tlv
, &ttl
);
265 p
= new0(lldp_neighbour_port
, 1);
273 p
->prioq_idx
= PRIOQ_IDX_NULL
;
274 p
->until
= ttl
* USEC_PER_SEC
+ now(clock_boottime_or_monotonic());
276 p
->data
= memdup(data
, length
);
286 void lldp_chassis_free(lldp_chassis
*c
) {
294 free(c
->chassis_id
.data
);
298 int lldp_chassis_new(tlv_packet
*tlv
,
300 Hashmap
*neighbour_mib
,
301 lldp_chassis
**ret
) {
302 _cleanup_lldp_chassis_free_ lldp_chassis
*c
= NULL
;
310 r
= sd_lldp_packet_read_chassis_id(tlv
, &type
, &data
, &length
);
314 c
= new0(lldp_chassis
, 1);
319 c
->chassis_id
.type
= type
;
320 c
->chassis_id
.length
= length
;
322 c
->chassis_id
.data
= memdup(data
, length
);
323 if (!c
->chassis_id
.data
)
326 LIST_HEAD_INIT(c
->ports
);
328 c
->by_expiry
= by_expiry
;
329 c
->neighbour_mib
= neighbour_mib
;
337 int lldp_receive_packet(sd_event_source
*s
, int fd
, uint32_t revents
, void *userdata
) {
338 _cleanup_lldp_packet_unref_ tlv_packet
*packet
= NULL
;
346 r
= tlv_packet_new(&packet
);
350 length
= read(fd
, &packet
->pdu
, sizeof(packet
->pdu
));
352 /* Silently drop the packet */
353 if ((size_t) length
> ETHER_MAX_LEN
)
356 packet
->userdata
= userdata
;
361 return lldp_handle_packet(p
, (uint16_t) length
);