]> git.ipfire.org Git - thirdparty/systemd.git/blob - src/libsystemd-network/lldp-internal.c
4012cd483b3b0aae94551e8c1cc2eca4234d07f4
[thirdparty/systemd.git] / src / libsystemd-network / lldp-internal.c
1 /*-*- Mode: C; c-basic-offset: 8; indent-tabs-mode: nil -*-*/
2
3 /***
4 This file is part of systemd.
5
6 Copyright (C) 2014 Tom Gundersen
7 Copyright (C) 2014 Susant Sahani
8
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.
13
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.
18
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/>.
21 ***/
22
23 #include "lldp-internal.h"
24 #include "sd-lldp.h"
25
26 /* We store maximum 1K chassis entries */
27 #define LLDP_MIB_MAX_CHASSIS 1024
28
29 /* Maximum Ports can be attached to any chassis */
30 #define LLDP_MIB_MAX_PORT_PER_CHASSIS 32
31
32 /* 10.5.5.2.2 mibUpdateObjects ()
33 * The mibUpdateObjects () procedure updates the MIB objects corresponding to
34 * the TLVs contained in the received LLDPDU for the LLDP remote system
35 * indicated by the LLDP remote systems update process defined in 10.3.5 */
36
37 int lldp_mib_update_objects(lldp_chassis *c, tlv_packet *tlv) {
38 lldp_neighbour_port *p;
39 uint16_t length, ttl;
40 uint8_t *data;
41 uint8_t type;
42 int r;
43
44 assert_return(c, -EINVAL);
45 assert_return(tlv, -EINVAL);
46
47 r = sd_lldp_packet_read_port_id(tlv, &type, &data, &length);
48 if (r < 0)
49 return r;
50
51 /* Update the packet if we already have */
52 LIST_FOREACH(port, p, c->ports) {
53
54 if ((p->type == type && p->length == length && !memcmp(p->data, data, p->length))) {
55
56 r = sd_lldp_packet_read_ttl(tlv, &ttl);
57 if (r < 0)
58 return r;
59
60 p->until = ttl * USEC_PER_SEC + now(clock_boottime_or_monotonic());
61
62 sd_lldp_packet_unref(p->packet);
63 p->packet = tlv;
64
65 prioq_reshuffle(p->c->by_expiry, p, &p->prioq_idx);
66
67 return 0;
68 }
69 }
70
71 return -1;
72 }
73
74 int lldp_mib_remove_objects(lldp_chassis *c, tlv_packet *tlv) {
75 lldp_neighbour_port *p, *q;
76 uint8_t *data;
77 uint16_t length;
78 uint8_t type;
79 int r;
80
81 assert_return(c, -EINVAL);
82 assert_return(tlv, -EINVAL);
83
84 r = sd_lldp_packet_read_port_id(tlv, &type, &data, &length);
85 if (r < 0)
86 return r;
87
88 LIST_FOREACH_SAFE(port, p, q, c->ports) {
89
90 /* Find the port */
91 if (p->type == type && p->length == length && !memcmp(p->data, data, p->length)) {
92 lldp_neighbour_port_remove_and_free(p);
93 break;
94 }
95 }
96
97 return 0;
98 }
99
100 int lldp_mib_add_objects(Prioq *by_expiry,
101 Hashmap *neighbour_mib,
102 tlv_packet *tlv) {
103 _cleanup_lldp_neighbour_port_free_ lldp_neighbour_port *p = NULL;
104 _cleanup_lldp_chassis_free_ lldp_chassis *c = NULL;
105 lldp_chassis_id chassis_id;
106 bool new_chassis = false;
107 uint8_t subtype, *data;
108 uint16_t ttl, length;
109 int r;
110
111 assert_return(by_expiry, -EINVAL);
112 assert_return(neighbour_mib, -EINVAL);
113 assert_return(tlv, -EINVAL);
114
115 r = sd_lldp_packet_read_chassis_id(tlv, &subtype, &data, &length);
116 if (r < 0)
117 goto drop;
118
119 r = sd_lldp_packet_read_ttl(tlv, &ttl);
120 if (r < 0)
121 goto drop;
122
123 /* Make hash key */
124 chassis_id.type = subtype;
125 chassis_id.length = length;
126 chassis_id.data = data;
127
128 /* Try to find the Chassis */
129 c = hashmap_get(neighbour_mib, &chassis_id);
130 if (!c) {
131
132 /* Don't create chassis if ttl 0 is received . Silently drop it */
133 if (ttl == 0) {
134 log_lldp("TTL value 0 received. Skiping Chassis creation.");
135 goto drop;
136 }
137
138 /* Admission Control: Can we store this packet ? */
139 if (hashmap_size(neighbour_mib) >= LLDP_MIB_MAX_CHASSIS) {
140
141 log_lldp("Exceeding number of chassie: %d. Dropping ...",
142 hashmap_size(neighbour_mib));
143 goto drop;
144 }
145
146 r = lldp_chassis_new(tlv, by_expiry, neighbour_mib, &c);
147 if (r < 0)
148 goto drop;
149
150 new_chassis = true;
151
152 r = hashmap_put(neighbour_mib, &c->chassis_id, c);
153 if (r < 0)
154 goto drop;
155
156 } else {
157
158 /* When the TTL field is set to zero, the receiving LLDP agent is notified all
159 * system information associated with the LLDP agent/port is to be deleted */
160 if (ttl == 0) {
161 log_lldp("TTL value 0 received . Deleting associated Port ...");
162
163 lldp_mib_remove_objects(c, tlv);
164
165 c = NULL;
166 goto drop;
167 }
168
169 /* if we already have this port just update it */
170 r = lldp_mib_update_objects(c, tlv);
171 if (r >= 0) {
172 c = NULL;
173 return r;
174 }
175
176 /* Admission Control: Can this port attached to the existing chassis ? */
177 if (c->n_ref >= LLDP_MIB_MAX_PORT_PER_CHASSIS) {
178 log_lldp("Port limit reached. Chassis has: %d ports. Dropping ...", c->n_ref);
179
180 c = NULL;
181 goto drop;
182 }
183 }
184
185 /* This is a new port */
186 r = lldp_neighbour_port_new(c, tlv, &p);
187 if (r < 0)
188 goto drop;
189
190 r = prioq_put(c->by_expiry, p, &p->prioq_idx);
191 if (r < 0)
192 goto drop;
193
194 /* Attach new port to chassis */
195 LIST_PREPEND(port, c->ports, p);
196 c->n_ref ++;
197
198 p = NULL;
199 c = NULL;
200
201 return 0;
202
203 drop:
204 sd_lldp_packet_unref(tlv);
205
206 if (new_chassis)
207 hashmap_remove(neighbour_mib, &c->chassis_id);
208
209 return r;
210 }
211
212 void lldp_neighbour_port_remove_and_free(lldp_neighbour_port *p) {
213 lldp_chassis *c;
214
215 assert(p);
216 assert(p->c);
217
218 c = p->c;
219
220 prioq_remove(c->by_expiry, p, &p->prioq_idx);
221
222 LIST_REMOVE(port, c->ports, p);
223 lldp_neighbour_port_free(p);
224
225 /* Drop the Chassis if no port is attached */
226 c->n_ref --;
227 if (c->n_ref <= 1) {
228 hashmap_remove(c->neighbour_mib, &c->chassis_id);
229 lldp_chassis_free(c);
230 }
231 }
232
233 void lldp_neighbour_port_free(lldp_neighbour_port *p) {
234
235 if(!p)
236 return;
237
238 sd_lldp_packet_unref(p->packet);
239
240 free(p->data);
241 free(p);
242 }
243
244 int lldp_neighbour_port_new(lldp_chassis *c,
245 tlv_packet *tlv,
246 lldp_neighbour_port **ret) {
247 _cleanup_lldp_neighbour_port_free_ lldp_neighbour_port *p = NULL;
248 uint16_t length, ttl;
249 uint8_t *data;
250 uint8_t type;
251 int r;
252
253 assert(tlv);
254
255 r = sd_lldp_packet_read_port_id(tlv, &type, &data, &length);
256 if (r < 0)
257 return r;
258
259 r = sd_lldp_packet_read_ttl(tlv, &ttl);
260 if (r < 0)
261 return r;
262
263 p = new0(lldp_neighbour_port, 1);
264 if (!p)
265 return -ENOMEM;
266
267 p->c = c;
268 p->type = type;
269 p->length = length;
270 p->packet = tlv;
271 p->prioq_idx = PRIOQ_IDX_NULL;
272 p->until = ttl * USEC_PER_SEC + now(clock_boottime_or_monotonic());
273
274 p->data = memdup(data, length);
275 if (!p->data)
276 return -ENOMEM;
277
278 *ret = p;
279 p = NULL;
280
281 return 0;
282 }
283
284 void lldp_chassis_free(lldp_chassis *c) {
285
286 if (!c)
287 return;
288
289 if (c->n_ref > 1)
290 return;
291
292 free(c->chassis_id.data);
293 free(c);
294 }
295
296 int lldp_chassis_new(tlv_packet *tlv,
297 Prioq *by_expiry,
298 Hashmap *neighbour_mib,
299 lldp_chassis **ret) {
300 _cleanup_lldp_chassis_free_ lldp_chassis *c = NULL;
301 uint16_t length;
302 uint8_t *data;
303 uint8_t type;
304 int r;
305
306 assert(tlv);
307
308 r = sd_lldp_packet_read_chassis_id(tlv, &type, &data, &length);
309 if (r < 0)
310 return r;
311
312 c = new0(lldp_chassis, 1);
313 if (!c)
314 return -ENOMEM;
315
316 c->n_ref = 1;
317 c->chassis_id.type = type;
318 c->chassis_id.length = length;
319
320 c->chassis_id.data = memdup(data, length);
321 if (!c->chassis_id.data)
322 return -ENOMEM;
323
324 LIST_HEAD_INIT(c->ports);
325
326 c->by_expiry = by_expiry;
327 c->neighbour_mib = neighbour_mib;
328
329 *ret = c;
330 c = NULL;
331
332 return 0;
333 }
334
335 int lldp_receive_packet(sd_event_source *s, int fd, uint32_t revents, void *userdata) {
336 _cleanup_lldp_packet_unref_ tlv_packet *packet = NULL;
337 tlv_packet *p;
338 uint16_t length;
339 int r;
340
341 assert(fd);
342 assert(userdata);
343
344 r = tlv_packet_new(&packet);
345 if (r < 0)
346 return r;
347
348 length = read(fd, &packet->pdu, sizeof(packet->pdu));
349
350 /* Silently drop the packet */
351 if ((size_t) length > ETHER_MAX_LEN)
352 return 0;
353
354 packet->userdata = userdata;
355
356 p = packet;
357 packet = NULL;
358
359 return lldp_handle_packet(p, (uint16_t) length);
360 }