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