Line data Source code
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 :
25 : /* We store maximum 1K chassis entries */
26 : #define LLDP_MIB_MAX_CHASSIS 1024
27 :
28 : /* Maximum Ports can be attached to any chassis */
29 : #define LLDP_MIB_MAX_PORT_PER_CHASSIS 32
30 :
31 0 : int lldp_read_chassis_id(tlv_packet *tlv,
32 : uint8_t *type,
33 : uint16_t *length,
34 : uint8_t **data) {
35 : uint8_t subtype;
36 : int r;
37 :
38 0 : assert_return(tlv, -EINVAL);
39 :
40 0 : r = lldp_tlv_packet_enter_container(tlv, LLDP_TYPE_CHASSIS_ID);
41 0 : if (r < 0)
42 0 : goto out2;
43 :
44 0 : r = tlv_packet_read_u8(tlv, &subtype);
45 0 : if (r < 0)
46 0 : goto out1;
47 :
48 0 : switch (subtype) {
49 : case LLDP_CHASSIS_SUBTYPE_MAC_ADDRESS:
50 :
51 0 : r = tlv_packet_read_bytes(tlv, data, length);
52 0 : if (r < 0)
53 0 : goto out1;
54 :
55 0 : break;
56 : default:
57 0 : r = -EOPNOTSUPP;
58 0 : break;
59 : }
60 :
61 0 : *type = subtype;
62 :
63 : out1:
64 0 : (void) lldp_tlv_packet_exit_container(tlv);
65 :
66 : out2:
67 0 : return r;
68 : }
69 :
70 0 : int lldp_read_port_id(tlv_packet *tlv,
71 : uint8_t *type,
72 : uint16_t *length,
73 : uint8_t **data) {
74 : uint8_t subtype;
75 : char *s;
76 : int r;
77 :
78 0 : assert_return(tlv, -EINVAL);
79 :
80 0 : r = lldp_tlv_packet_enter_container(tlv, LLDP_TYPE_PORT_ID);
81 0 : if (r < 0)
82 0 : goto out2;
83 :
84 0 : r = tlv_packet_read_u8(tlv, &subtype);
85 0 : if (r < 0)
86 0 : goto out1;
87 :
88 0 : switch (subtype) {
89 : case LLDP_PORT_SUBTYPE_PORT_COMPONENT:
90 : case LLDP_PORT_SUBTYPE_INTERFACE_ALIAS:
91 : case LLDP_PORT_SUBTYPE_INTERFACE_NAME:
92 : case LLDP_PORT_SUBTYPE_LOCALLY_ASSIGNED:
93 :
94 0 : r = tlv_packet_read_string(tlv, &s, length);
95 0 : if (r < 0)
96 0 : goto out1;
97 :
98 0 : *data = (uint8_t *) s;
99 :
100 0 : break;
101 : case LLDP_PORT_SUBTYPE_MAC_ADDRESS:
102 :
103 0 : r = tlv_packet_read_bytes(tlv, data, length);
104 0 : if (r < 0)
105 0 : goto out1;
106 :
107 0 : break;
108 : default:
109 0 : r = -EOPNOTSUPP;
110 0 : break;
111 : }
112 :
113 0 : *type = subtype;
114 :
115 : out1:
116 0 : (void) lldp_tlv_packet_exit_container(tlv);
117 :
118 : out2:
119 0 : return r;
120 : }
121 :
122 0 : int lldp_read_ttl(tlv_packet *tlv, uint16_t *ttl) {
123 : int r;
124 :
125 0 : assert_return(tlv, -EINVAL);
126 :
127 0 : r = lldp_tlv_packet_enter_container(tlv, LLDP_TYPE_TTL);
128 0 : if (r < 0)
129 0 : goto out;
130 :
131 0 : r = tlv_packet_read_u16(tlv, ttl);
132 :
133 0 : (void) lldp_tlv_packet_exit_container(tlv);
134 :
135 : out:
136 0 : return r;
137 : }
138 :
139 0 : int lldp_read_system_name(tlv_packet *tlv,
140 : uint16_t *length,
141 : char **data) {
142 : char *s;
143 : int r;
144 :
145 0 : assert_return(tlv, -EINVAL);
146 :
147 0 : r = lldp_tlv_packet_enter_container(tlv, LLDP_TYPE_SYSTEM_NAME);
148 0 : if (r < 0)
149 0 : return r;
150 :
151 0 : r = tlv_packet_read_string(tlv, &s, length);
152 0 : if (r < 0)
153 0 : goto out;
154 :
155 0 : *data = (char *) s;
156 :
157 : out:
158 0 : (void) lldp_tlv_packet_exit_container(tlv);
159 :
160 0 : return r;
161 : }
162 :
163 0 : int lldp_read_system_description(tlv_packet *tlv,
164 : uint16_t *length,
165 : char **data) {
166 : char *s;
167 : int r;
168 :
169 0 : assert_return(tlv, -EINVAL);
170 :
171 0 : r = lldp_tlv_packet_enter_container(tlv, LLDP_TYPE_SYSTEM_DESCRIPTION);
172 0 : if (r < 0)
173 0 : return r;
174 :
175 0 : r = tlv_packet_read_string(tlv, &s, length);
176 0 : if (r < 0)
177 0 : goto out;
178 :
179 0 : *data = (char *) s;
180 :
181 : out:
182 0 : (void) lldp_tlv_packet_exit_container(tlv);
183 :
184 0 : return r;
185 : }
186 :
187 0 : int lldp_read_port_description(tlv_packet *tlv,
188 : uint16_t *length,
189 : char **data) {
190 : char *s;
191 : int r;
192 :
193 0 : assert_return(tlv, -EINVAL);
194 :
195 0 : r = lldp_tlv_packet_enter_container(tlv, LLDP_TYPE_PORT_DESCRIPTION);
196 0 : if (r < 0)
197 0 : return r;
198 :
199 0 : r = tlv_packet_read_string(tlv, &s, length);
200 0 : if (r < 0)
201 0 : goto out;
202 :
203 0 : *data = (char *) s;
204 :
205 : out:
206 0 : (void) lldp_tlv_packet_exit_container(tlv);
207 :
208 0 : return r;
209 : }
210 :
211 0 : int lldp_read_system_capability(tlv_packet *tlv, uint16_t *data) {
212 : int r;
213 :
214 0 : assert_return(tlv, -EINVAL);
215 :
216 0 : r = lldp_tlv_packet_enter_container(tlv, LLDP_TYPE_SYSTEM_CAPABILITIES);
217 0 : if (r < 0)
218 0 : return r;
219 :
220 0 : r = tlv_packet_read_u16(tlv, data);
221 0 : if (r < 0)
222 0 : goto out;
223 :
224 0 : return 0;
225 : out:
226 :
227 0 : (void) lldp_tlv_packet_exit_container(tlv);
228 :
229 0 : return r;
230 : }
231 :
232 : /* 10.5.5.2.2 mibUpdateObjects ()
233 : * The mibUpdateObjects () procedure updates the MIB objects corresponding to
234 : * the TLVs contained in the received LLDPDU for the LLDP remote system
235 : * indicated by the LLDP remote systems update process defined in 10.3.5 */
236 :
237 0 : int lldp_mib_update_objects(lldp_chassis *c, tlv_packet *tlv) {
238 : lldp_neighbour_port *p;
239 : uint16_t length, ttl;
240 : uint8_t *data;
241 : uint8_t type;
242 : int r;
243 :
244 0 : assert_return(c, -EINVAL);
245 0 : assert_return(tlv, -EINVAL);
246 :
247 0 : r = lldp_read_port_id(tlv, &type, &length, &data);
248 0 : if (r < 0)
249 0 : return r;
250 :
251 : /* Update the packet if we already have */
252 0 : LIST_FOREACH(port, p, c->ports) {
253 :
254 0 : if ((p->type == type && p->length == length && !memcmp(p->data, data, p->length))) {
255 :
256 0 : r = lldp_read_ttl(tlv, &ttl);
257 0 : if (r < 0)
258 0 : return r;
259 :
260 0 : p->until = ttl * USEC_PER_SEC + now(clock_boottime_or_monotonic());
261 :
262 0 : tlv_packet_free(p->packet);
263 0 : p->packet = tlv;
264 :
265 0 : prioq_reshuffle(p->c->by_expiry, p, &p->prioq_idx);
266 :
267 0 : return 0;
268 : }
269 : }
270 :
271 0 : return -1;
272 : }
273 :
274 0 : int lldp_mib_remove_objects(lldp_chassis *c, tlv_packet *tlv) {
275 : lldp_neighbour_port *p, *q;
276 : uint8_t *data;
277 : uint16_t length;
278 : uint8_t type;
279 : int r;
280 :
281 0 : assert_return(c, -EINVAL);
282 0 : assert_return(tlv, -EINVAL);
283 :
284 0 : r = lldp_read_port_id(tlv, &type, &length, &data);
285 0 : if (r < 0)
286 0 : return r;
287 :
288 0 : LIST_FOREACH_SAFE(port, p, q, c->ports) {
289 :
290 : /* Find the port */
291 0 : if (p->type == type && p->length == length && !memcmp(p->data, data, p->length)) {
292 0 : lldp_neighbour_port_remove_and_free(p);
293 0 : break;
294 : }
295 : }
296 :
297 0 : return 0;
298 : }
299 :
300 0 : int lldp_mib_add_objects(Prioq *by_expiry,
301 : Hashmap *neighbour_mib,
302 : tlv_packet *tlv) {
303 0 : _cleanup_lldp_neighbour_port_free_ lldp_neighbour_port *p = NULL;
304 0 : _cleanup_lldp_chassis_free_ lldp_chassis *c = NULL;
305 : lldp_chassis_id chassis_id;
306 0 : bool new_chassis = false;
307 : uint8_t subtype, *data;
308 : uint16_t ttl, length;
309 : int r;
310 :
311 0 : assert_return(by_expiry, -EINVAL);
312 0 : assert_return(neighbour_mib, -EINVAL);
313 0 : assert_return(tlv, -EINVAL);
314 :
315 0 : r = lldp_read_chassis_id(tlv, &subtype, &length, &data);
316 0 : if (r < 0)
317 0 : goto drop;
318 :
319 0 : r = lldp_read_ttl(tlv, &ttl);
320 0 : if (r < 0)
321 0 : goto drop;
322 :
323 : /* Make hash key */
324 0 : chassis_id.type = subtype;
325 0 : chassis_id.length = length;
326 0 : chassis_id.data = data;
327 :
328 : /* Try to find the Chassis */
329 0 : c = hashmap_get(neighbour_mib, &chassis_id);
330 0 : if (!c) {
331 :
332 : /* Don't create chassis if ttl 0 is received . Silently drop it */
333 0 : if (ttl == 0) {
334 0 : log_lldp("TTL value 0 received. Skiping Chassis creation.");
335 0 : goto drop;
336 : }
337 :
338 : /* Admission Control: Can we store this packet ? */
339 0 : if (hashmap_size(neighbour_mib) >= LLDP_MIB_MAX_CHASSIS) {
340 :
341 0 : log_lldp("Exceeding number of chassie: %d. Dropping ...",
342 : hashmap_size(neighbour_mib));
343 0 : goto drop;
344 : }
345 :
346 0 : r = lldp_chassis_new(tlv, by_expiry, neighbour_mib, &c);
347 0 : if (r < 0)
348 0 : goto drop;
349 :
350 0 : new_chassis = true;
351 :
352 0 : r = hashmap_put(neighbour_mib, &c->chassis_id, c);
353 0 : if (r < 0)
354 0 : goto drop;
355 :
356 : } else {
357 :
358 : /* When the TTL field is set to zero, the receiving LLDP agent is notified all
359 : * system information associated with the LLDP agent/port is to be deleted */
360 0 : if (ttl == 0) {
361 0 : log_lldp("TTL value 0 received . Deleting associated Port ...");
362 :
363 0 : lldp_mib_remove_objects(c, tlv);
364 :
365 0 : c = NULL;
366 0 : goto drop;
367 : }
368 :
369 : /* if we already have this port just update it */
370 0 : r = lldp_mib_update_objects(c, tlv);
371 0 : if (r >= 0) {
372 0 : c = NULL;
373 0 : return r;
374 : }
375 :
376 : /* Admission Control: Can this port attached to the existing chassis ? */
377 0 : if (REFCNT_GET(c->n_ref) >= LLDP_MIB_MAX_PORT_PER_CHASSIS) {
378 0 : log_lldp("Port limit reached. Chassis has: %d ports. Dropping ...",
379 : REFCNT_GET(c->n_ref));
380 :
381 0 : c = NULL;
382 0 : goto drop;
383 : }
384 : }
385 :
386 : /* This is a new port */
387 0 : r = lldp_neighbour_port_new(c, tlv, &p);
388 0 : if (r < 0)
389 0 : goto drop;
390 :
391 0 : r = prioq_put(c->by_expiry, p, &p->prioq_idx);
392 0 : if (r < 0)
393 0 : goto drop;
394 :
395 : /* Attach new port to chassis */
396 0 : LIST_PREPEND(port, c->ports, p);
397 0 : REFCNT_INC(c->n_ref);
398 :
399 0 : p = NULL;
400 0 : c = NULL;
401 :
402 0 : return 0;
403 :
404 : drop:
405 0 : tlv_packet_free(tlv);
406 :
407 0 : if (new_chassis)
408 0 : hashmap_remove(neighbour_mib, &c->chassis_id);
409 :
410 0 : return r;
411 : }
412 :
413 0 : void lldp_neighbour_port_remove_and_free(lldp_neighbour_port *p) {
414 : lldp_chassis *c;
415 :
416 0 : assert(p);
417 0 : assert(p->c);
418 :
419 0 : c = p->c;
420 :
421 0 : prioq_remove(c->by_expiry, p, &p->prioq_idx);
422 :
423 0 : LIST_REMOVE(port, c->ports, p);
424 0 : lldp_neighbour_port_free(p);
425 :
426 : /* Drop the Chassis if no port is attached */
427 0 : if (REFCNT_DEC(c->n_ref) <= 1) {
428 0 : hashmap_remove(c->neighbour_mib, &c->chassis_id);
429 0 : lldp_chassis_free(c);
430 : }
431 0 : }
432 :
433 0 : void lldp_neighbour_port_free(lldp_neighbour_port *p) {
434 :
435 0 : if(!p)
436 0 : return;
437 :
438 0 : tlv_packet_free(p->packet);
439 :
440 0 : free(p->data);
441 0 : free(p);
442 : }
443 :
444 0 : int lldp_neighbour_port_new(lldp_chassis *c,
445 : tlv_packet *tlv,
446 : lldp_neighbour_port **ret) {
447 0 : _cleanup_lldp_neighbour_port_free_ lldp_neighbour_port *p = NULL;
448 : uint16_t length, ttl;
449 : uint8_t *data;
450 : uint8_t type;
451 : int r;
452 :
453 0 : assert(tlv);
454 :
455 0 : r = lldp_read_port_id(tlv, &type, &length, &data);
456 0 : if (r < 0)
457 0 : return r;
458 :
459 0 : r = lldp_read_ttl(tlv, &ttl);
460 0 : if (r < 0)
461 0 : return r;
462 :
463 0 : p = new0(lldp_neighbour_port, 1);
464 0 : if (!p)
465 0 : return -ENOMEM;
466 :
467 0 : p->c = c;
468 0 : p->type = type;
469 0 : p->length = length;
470 0 : p->packet = tlv;
471 0 : p->prioq_idx = PRIOQ_IDX_NULL;
472 0 : p->until = ttl * USEC_PER_SEC + now(clock_boottime_or_monotonic());
473 :
474 0 : p->data = memdup(data, length);
475 0 : if (!p->data)
476 0 : return -ENOMEM;
477 :
478 0 : *ret = p;
479 0 : p = NULL;
480 :
481 0 : return 0;
482 : }
483 :
484 0 : void lldp_chassis_free(lldp_chassis *c) {
485 :
486 0 : if (!c)
487 0 : return;
488 :
489 0 : if (REFCNT_GET(c->n_ref) > 1)
490 0 : return;
491 :
492 0 : free(c->chassis_id.data);
493 0 : free(c);
494 : }
495 :
496 0 : int lldp_chassis_new(tlv_packet *tlv,
497 : Prioq *by_expiry,
498 : Hashmap *neighbour_mib,
499 : lldp_chassis **ret) {
500 0 : _cleanup_lldp_chassis_free_ lldp_chassis *c = NULL;
501 : uint16_t length;
502 : uint8_t *data;
503 : uint8_t type;
504 : int r;
505 :
506 0 : assert(tlv);
507 :
508 0 : r = lldp_read_chassis_id(tlv, &type, &length, &data);
509 0 : if (r < 0)
510 0 : return r;
511 :
512 0 : c = new0(lldp_chassis, 1);
513 0 : if (!c)
514 0 : return -ENOMEM;
515 :
516 0 : c->n_ref = REFCNT_INIT;
517 0 : c->chassis_id.type = type;
518 0 : c->chassis_id.length = length;
519 :
520 0 : c->chassis_id.data = memdup(data, length);
521 0 : if (!c->chassis_id.data)
522 0 : return -ENOMEM;
523 :
524 0 : LIST_HEAD_INIT(c->ports);
525 :
526 0 : c->by_expiry = by_expiry;
527 0 : c->neighbour_mib = neighbour_mib;
528 :
529 0 : *ret = c;
530 0 : c = NULL;
531 :
532 0 : return 0;
533 : }
|