]>
Commit | Line | Data |
---|---|---|
e735f4d4 MP |
1 | /*** |
2 | This file is part of systemd. | |
3 | ||
4 | Copyright (C) 2014 Tom Gundersen | |
5 | Copyright (C) 2014 Susant Sahani | |
6 | ||
7 | systemd is free software; you can redistribute it and/or modify it | |
8 | under the terms of the GNU Lesser General Public License as published by | |
9 | the Free Software Foundation; either version 2.1 of the License, or | |
10 | (at your option) any later version. | |
11 | ||
12 | systemd is distributed in the hope that it will be useful, but | |
13 | WITHOUT ANY WARRANTY; without even the implied warranty of | |
14 | MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the GNU | |
15 | Lesser General Public License for more details. | |
16 | ||
17 | You should have received a copy of the GNU Lesser General Public License | |
18 | along with systemd; If not, see <http://www.gnu.org/licenses/>. | |
19 | ***/ | |
20 | ||
6300502b | 21 | #include "sd-lldp.h" |
e735f4d4 | 22 | |
db2df898 MP |
23 | #include "alloc-util.h" |
24 | #include "lldp-internal.h" | |
25 | ||
e735f4d4 MP |
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 | ||
e735f4d4 MP |
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 | ||
6300502b | 47 | r = sd_lldp_packet_read_port_id(tlv, &type, &data, &length); |
e735f4d4 MP |
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 | ||
6300502b | 56 | r = sd_lldp_packet_read_ttl(tlv, &ttl); |
e735f4d4 MP |
57 | if (r < 0) |
58 | return r; | |
59 | ||
60 | p->until = ttl * USEC_PER_SEC + now(clock_boottime_or_monotonic()); | |
61 | ||
6300502b | 62 | sd_lldp_packet_unref(p->packet); |
e735f4d4 MP |
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 | ||
6300502b | 84 | r = sd_lldp_packet_read_port_id(tlv, &type, &data, &length); |
e735f4d4 MP |
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 | ||
6300502b | 115 | r = sd_lldp_packet_read_chassis_id(tlv, &subtype, &data, &length); |
e735f4d4 MP |
116 | if (r < 0) |
117 | goto drop; | |
118 | ||
6300502b | 119 | r = sd_lldp_packet_read_ttl(tlv, &ttl); |
e735f4d4 MP |
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 ? */ | |
d9dfd233 MP |
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); | |
e735f4d4 MP |
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); | |
d9dfd233 | 196 | c->n_ref ++; |
e735f4d4 MP |
197 | |
198 | p = NULL; | |
199 | c = NULL; | |
200 | ||
201 | return 0; | |
202 | ||
203 | drop: | |
6300502b | 204 | sd_lldp_packet_unref(tlv); |
e735f4d4 MP |
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 */ | |
d9dfd233 MP |
226 | c->n_ref --; |
227 | if (c->n_ref <= 1) { | |
e735f4d4 MP |
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 | ||
6300502b | 238 | sd_lldp_packet_unref(p->packet); |
e735f4d4 MP |
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 | ||
6300502b | 255 | r = sd_lldp_packet_read_port_id(tlv, &type, &data, &length); |
e735f4d4 MP |
256 | if (r < 0) |
257 | return r; | |
258 | ||
6300502b | 259 | r = sd_lldp_packet_read_ttl(tlv, &ttl); |
e735f4d4 MP |
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 | ||
d9dfd233 | 289 | if (c->n_ref > 1) |
e735f4d4 MP |
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 | ||
6300502b | 308 | r = sd_lldp_packet_read_chassis_id(tlv, &type, &data, &length); |
e735f4d4 MP |
309 | if (r < 0) |
310 | return r; | |
311 | ||
312 | c = new0(lldp_chassis, 1); | |
313 | if (!c) | |
314 | return -ENOMEM; | |
315 | ||
d9dfd233 | 316 | c->n_ref = 1; |
e735f4d4 MP |
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 | } | |
6300502b MP |
334 | |
335 | int lldp_receive_packet(sd_event_source *s, int fd, uint32_t revents, void *userdata) { | |
4c89c718 | 336 | _cleanup_(sd_lldp_packet_unrefp) tlv_packet *packet = NULL; |
6300502b MP |
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 | } |