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 "lldp-internal.h"
27 /* We store maximum 1K chassis entries */
28 #define LLDP_MIB_MAX_CHASSIS 1024
30 /* Maximum Ports can be attached to any chassis */
31 #define LLDP_MIB_MAX_PORT_PER_CHASSIS 32
33 /* 10.5.5.2.2 mibUpdateObjects ()
34 * The mibUpdateObjects () procedure updates the MIB objects corresponding to
35 * the TLVs contained in the received LLDPDU for the LLDP remote system
36 * indicated by the LLDP remote systems update process defined in 10.3.5 */
38 int lldp_mib_update_objects(lldp_chassis
*c
, tlv_packet
*tlv
) {
39 lldp_neighbour_port
*p
;
45 assert_return(c
, -EINVAL
);
46 assert_return(tlv
, -EINVAL
);
48 r
= sd_lldp_packet_read_port_id(tlv
, &type
, &data
, &length
);
52 /* Update the packet if we already have */
53 LIST_FOREACH(port
, p
, c
->ports
) {
55 if ((p
->type
== type
&& p
->length
== length
&& !memcmp(p
->data
, data
, p
->length
))) {
57 r
= sd_lldp_packet_read_ttl(tlv
, &ttl
);
61 p
->until
= ttl
* USEC_PER_SEC
+ now(clock_boottime_or_monotonic());
63 sd_lldp_packet_unref(p
->packet
);
66 prioq_reshuffle(p
->c
->by_expiry
, p
, &p
->prioq_idx
);
75 int lldp_mib_remove_objects(lldp_chassis
*c
, tlv_packet
*tlv
) {
76 lldp_neighbour_port
*p
, *q
;
82 assert_return(c
, -EINVAL
);
83 assert_return(tlv
, -EINVAL
);
85 r
= sd_lldp_packet_read_port_id(tlv
, &type
, &data
, &length
);
89 LIST_FOREACH_SAFE(port
, p
, q
, c
->ports
) {
92 if (p
->type
== type
&& p
->length
== length
&& !memcmp(p
->data
, data
, p
->length
)) {
93 lldp_neighbour_port_remove_and_free(p
);
101 int lldp_mib_add_objects(Prioq
*by_expiry
,
102 Hashmap
*neighbour_mib
,
104 _cleanup_lldp_neighbour_port_free_ lldp_neighbour_port
*p
= NULL
;
105 _cleanup_lldp_chassis_free_ lldp_chassis
*c
= NULL
;
106 lldp_chassis_id chassis_id
;
107 bool new_chassis
= false;
108 uint8_t subtype
, *data
;
109 uint16_t ttl
, length
;
112 assert_return(by_expiry
, -EINVAL
);
113 assert_return(neighbour_mib
, -EINVAL
);
114 assert_return(tlv
, -EINVAL
);
116 r
= sd_lldp_packet_read_chassis_id(tlv
, &subtype
, &data
, &length
);
120 r
= sd_lldp_packet_read_ttl(tlv
, &ttl
);
125 chassis_id
.type
= subtype
;
126 chassis_id
.length
= length
;
127 chassis_id
.data
= data
;
129 /* Try to find the Chassis */
130 c
= hashmap_get(neighbour_mib
, &chassis_id
);
133 /* Don't create chassis if ttl 0 is received . Silently drop it */
135 log_lldp("TTL value 0 received. Skiping Chassis creation.");
139 /* Admission Control: Can we store this packet ? */
140 if (hashmap_size(neighbour_mib
) >= LLDP_MIB_MAX_CHASSIS
) {
142 log_lldp("Exceeding number of chassie: %d. Dropping ...",
143 hashmap_size(neighbour_mib
));
147 r
= lldp_chassis_new(tlv
, by_expiry
, neighbour_mib
, &c
);
153 r
= hashmap_put(neighbour_mib
, &c
->chassis_id
, c
);
159 /* When the TTL field is set to zero, the receiving LLDP agent is notified all
160 * system information associated with the LLDP agent/port is to be deleted */
162 log_lldp("TTL value 0 received . Deleting associated Port ...");
164 lldp_mib_remove_objects(c
, tlv
);
170 /* if we already have this port just update it */
171 r
= lldp_mib_update_objects(c
, tlv
);
177 /* Admission Control: Can this port attached to the existing chassis ? */
178 if (c
->n_ref
>= LLDP_MIB_MAX_PORT_PER_CHASSIS
) {
179 log_lldp("Port limit reached. Chassis has: %d ports. Dropping ...", c
->n_ref
);
186 /* This is a new port */
187 r
= lldp_neighbour_port_new(c
, tlv
, &p
);
191 r
= prioq_put(c
->by_expiry
, p
, &p
->prioq_idx
);
195 /* Attach new port to chassis */
196 LIST_PREPEND(port
, c
->ports
, p
);
205 sd_lldp_packet_unref(tlv
);
208 hashmap_remove(neighbour_mib
, &c
->chassis_id
);
213 void lldp_neighbour_port_remove_and_free(lldp_neighbour_port
*p
) {
221 prioq_remove(c
->by_expiry
, p
, &p
->prioq_idx
);
223 LIST_REMOVE(port
, c
->ports
, p
);
224 lldp_neighbour_port_free(p
);
226 /* Drop the Chassis if no port is attached */
229 hashmap_remove(c
->neighbour_mib
, &c
->chassis_id
);
230 lldp_chassis_free(c
);
234 void lldp_neighbour_port_free(lldp_neighbour_port
*p
) {
239 sd_lldp_packet_unref(p
->packet
);
245 int lldp_neighbour_port_new(lldp_chassis
*c
,
247 lldp_neighbour_port
**ret
) {
248 _cleanup_lldp_neighbour_port_free_ lldp_neighbour_port
*p
= NULL
;
249 uint16_t length
, ttl
;
256 r
= sd_lldp_packet_read_port_id(tlv
, &type
, &data
, &length
);
260 r
= sd_lldp_packet_read_ttl(tlv
, &ttl
);
264 p
= new0(lldp_neighbour_port
, 1);
272 p
->prioq_idx
= PRIOQ_IDX_NULL
;
273 p
->until
= ttl
* USEC_PER_SEC
+ now(clock_boottime_or_monotonic());
275 p
->data
= memdup(data
, length
);
285 void lldp_chassis_free(lldp_chassis
*c
) {
293 free(c
->chassis_id
.data
);
297 int lldp_chassis_new(tlv_packet
*tlv
,
299 Hashmap
*neighbour_mib
,
300 lldp_chassis
**ret
) {
301 _cleanup_lldp_chassis_free_ lldp_chassis
*c
= NULL
;
309 r
= sd_lldp_packet_read_chassis_id(tlv
, &type
, &data
, &length
);
313 c
= new0(lldp_chassis
, 1);
318 c
->chassis_id
.type
= type
;
319 c
->chassis_id
.length
= length
;
321 c
->chassis_id
.data
= memdup(data
, length
);
322 if (!c
->chassis_id
.data
)
325 LIST_HEAD_INIT(c
->ports
);
327 c
->by_expiry
= by_expiry
;
328 c
->neighbour_mib
= neighbour_mib
;
336 int lldp_receive_packet(sd_event_source
*s
, int fd
, uint32_t revents
, void *userdata
) {
337 _cleanup_lldp_packet_unref_ tlv_packet
*packet
= NULL
;
345 r
= tlv_packet_new(&packet
);
349 length
= read(fd
, &packet
->pdu
, sizeof(packet
->pdu
));
351 /* Silently drop the packet */
352 if ((size_t) length
> ETHER_MAX_LEN
)
355 packet
->userdata
= userdata
;
360 return lldp_handle_packet(p
, (uint16_t) length
);