]>
Commit | Line | Data |
---|---|---|
db9ecf05 | 1 | /* SPDX-License-Identifier: LGPL-2.1-or-later */ |
34437b4f LP |
2 | |
3 | #include "alloc-util.h" | |
4 | #include "escape.h" | |
5 | #include "ether-addr-util.h" | |
5cdf13c7 | 6 | #include "hashmap.h" |
34437b4f LP |
7 | #include "hexdecoct.h" |
8 | #include "in-addr-util.h" | |
309a747f | 9 | #include "json-util.h" |
34437b4f | 10 | #include "lldp-neighbor.h" |
5cdf13c7 | 11 | #include "lldp-rx-internal.h" |
0a970718 | 12 | #include "memory-util.h" |
5cdf13c7 DDM |
13 | #include "prioq.h" |
14 | #include "siphash24.h" | |
34437b4f LP |
15 | #include "unaligned.h" |
16 | ||
7a08d314 | 17 | static void lldp_neighbor_id_hash_func(const LLDPNeighborID *id, struct siphash *state) { |
90496cc6 YW |
18 | assert(id); |
19 | assert(state); | |
20 | ||
c01a5c05 YW |
21 | siphash24_compress_safe(id->chassis_id, id->chassis_id_size, state); |
22 | siphash24_compress_typesafe(id->chassis_id_size, state); | |
23 | siphash24_compress_safe(id->port_id, id->port_id_size, state); | |
24 | siphash24_compress_typesafe(id->port_id_size, state); | |
34437b4f LP |
25 | } |
26 | ||
dc5f9c6f | 27 | int lldp_neighbor_id_compare_func(const LLDPNeighborID *x, const LLDPNeighborID *y) { |
90496cc6 YW |
28 | assert(x); |
29 | assert(y); | |
30 | ||
dc6bf94d FB |
31 | return memcmp_nn(x->chassis_id, x->chassis_id_size, y->chassis_id, y->chassis_id_size) |
32 | ?: memcmp_nn(x->port_id, x->port_id_size, y->port_id, y->port_id_size); | |
34437b4f LP |
33 | } |
34 | ||
71c4f7e8 YW |
35 | DEFINE_HASH_OPS_WITH_VALUE_DESTRUCTOR( |
36 | lldp_neighbor_hash_ops, | |
37 | LLDPNeighborID, | |
38 | lldp_neighbor_id_hash_func, | |
39 | lldp_neighbor_id_compare_func, | |
40 | sd_lldp_neighbor, | |
41 | lldp_neighbor_unlink); | |
34437b4f | 42 | |
f9550d11 | 43 | int lldp_neighbor_prioq_compare_func(const sd_lldp_neighbor *x, const sd_lldp_neighbor *y) { |
90496cc6 YW |
44 | assert(x); |
45 | assert(y); | |
46 | ||
a0edd02e | 47 | return CMP(x->until, y->until); |
34437b4f LP |
48 | } |
49 | ||
17347053 | 50 | sd_lldp_neighbor *sd_lldp_neighbor_ref(sd_lldp_neighbor *n) { |
34437b4f LP |
51 | if (!n) |
52 | return NULL; | |
53 | ||
35778343 | 54 | assert(n->n_ref > 0 || n->lldp_rx); |
34437b4f LP |
55 | n->n_ref++; |
56 | ||
57 | return n; | |
58 | } | |
59 | ||
35777f51 YW |
60 | static sd_lldp_neighbor *lldp_neighbor_free(sd_lldp_neighbor *n) { |
61 | if (!n) | |
62 | return NULL; | |
34437b4f LP |
63 | |
64 | free(n->id.port_id); | |
65 | free(n->id.chassis_id); | |
66 | free(n->port_description); | |
67 | free(n->system_name); | |
68 | free(n->system_description); | |
f69b4ae8 | 69 | free(n->mud_url); |
34437b4f LP |
70 | free(n->chassis_id_as_string); |
71 | free(n->port_id_as_string); | |
35777f51 | 72 | return mfree(n); |
34437b4f LP |
73 | } |
74 | ||
17347053 | 75 | sd_lldp_neighbor *sd_lldp_neighbor_unref(sd_lldp_neighbor *n) { |
34437b4f LP |
76 | |
77 | /* Drops one reference from the neighbor. Note that the object is not freed unless it is already unlinked from | |
78 | * the sd_lldp object. */ | |
79 | ||
80 | if (!n) | |
81 | return NULL; | |
82 | ||
83 | assert(n->n_ref > 0); | |
84 | n->n_ref--; | |
85 | ||
35778343 | 86 | if (n->n_ref <= 0 && !n->lldp_rx) |
34437b4f LP |
87 | lldp_neighbor_free(n); |
88 | ||
89 | return NULL; | |
90 | } | |
91 | ||
92 | sd_lldp_neighbor *lldp_neighbor_unlink(sd_lldp_neighbor *n) { | |
93 | ||
94 | /* Removes the neighbor object from the LLDP object, and frees it if it also has no other reference. */ | |
95 | ||
96 | if (!n) | |
97 | return NULL; | |
98 | ||
35778343 | 99 | if (!n->lldp_rx) |
34437b4f LP |
100 | return NULL; |
101 | ||
8ae1a821 LP |
102 | /* Only remove the neighbor object from the hash table if it's in there, don't complain if it isn't. This is |
103 | * because we are used as destructor call for hashmap_clear() and thus sometimes are called to de-register | |
104 | * ourselves from the hashtable and sometimes are called after we already are de-registered. */ | |
105 | ||
35778343 | 106 | (void) hashmap_remove_value(n->lldp_rx->neighbor_by_id, &n->id, n); |
8ae1a821 | 107 | |
35778343 | 108 | assert_se(prioq_remove(n->lldp_rx->neighbor_by_expiry, n, &n->prioq_idx) >= 0); |
34437b4f | 109 | |
35778343 | 110 | n->lldp_rx = NULL; |
34437b4f LP |
111 | |
112 | if (n->n_ref <= 0) | |
113 | lldp_neighbor_free(n); | |
114 | ||
115 | return NULL; | |
116 | } | |
117 | ||
118 | sd_lldp_neighbor *lldp_neighbor_new(size_t raw_size) { | |
119 | sd_lldp_neighbor *n; | |
120 | ||
4e88a46b YW |
121 | if (raw_size > SIZE_MAX - ALIGN(sizeof(sd_lldp_neighbor))) |
122 | return NULL; | |
123 | ||
34437b4f LP |
124 | n = malloc0(ALIGN(sizeof(sd_lldp_neighbor)) + raw_size); |
125 | if (!n) | |
126 | return NULL; | |
127 | ||
128 | n->raw_size = raw_size; | |
129 | n->n_ref = 1; | |
130 | ||
131 | return n; | |
132 | } | |
133 | ||
35778343 | 134 | static int parse_string(sd_lldp_rx *lldp_rx, char **s, const void *q, size_t n) { |
34437b4f LP |
135 | const char *p = q; |
136 | char *k; | |
137 | ||
138 | assert(s); | |
139 | assert(p || n == 0); | |
140 | ||
141 | if (*s) { | |
35778343 | 142 | log_lldp_rx(lldp_rx, "Found duplicate string, ignoring field."); |
34437b4f LP |
143 | return 0; |
144 | } | |
145 | ||
146 | /* Strip trailing NULs, just to be nice */ | |
147 | while (n > 0 && p[n-1] == 0) | |
148 | n--; | |
149 | ||
150 | if (n <= 0) /* Ignore empty strings */ | |
151 | return 0; | |
152 | ||
153 | /* Look for inner NULs */ | |
154 | if (memchr(p, 0, n)) { | |
35778343 | 155 | log_lldp_rx(lldp_rx, "Found inner NUL in string, ignoring field."); |
34437b4f LP |
156 | return 0; |
157 | } | |
158 | ||
159 | /* Let's escape weird chars, for security reasons */ | |
160 | k = cescape_length(p, n); | |
161 | if (!k) | |
35388783 | 162 | return log_oom_debug(); |
34437b4f | 163 | |
d6f2cd67 | 164 | free_and_replace(*s, k); |
34437b4f LP |
165 | |
166 | return 1; | |
167 | } | |
168 | ||
169 | int lldp_neighbor_parse(sd_lldp_neighbor *n) { | |
170 | struct ether_header h; | |
171 | const uint8_t *p; | |
172 | size_t left; | |
173 | int r; | |
174 | ||
175 | assert(n); | |
176 | ||
35388783 | 177 | if (n->raw_size < sizeof(struct ether_header)) |
35778343 YW |
178 | return log_lldp_rx_errno(n->lldp_rx, SYNTHETIC_ERRNO(EBADMSG), |
179 | "Received truncated packet, ignoring."); | |
34437b4f LP |
180 | |
181 | memcpy(&h, LLDP_NEIGHBOR_RAW(n), sizeof(h)); | |
182 | ||
92b64168 | 183 | if (h.ether_type != htobe16(ETH_P_LLDP)) |
35778343 YW |
184 | return log_lldp_rx_errno(n->lldp_rx, SYNTHETIC_ERRNO(EBADMSG), |
185 | "Received packet with wrong type, ignoring."); | |
34437b4f LP |
186 | |
187 | if (h.ether_dhost[0] != 0x01 || | |
188 | h.ether_dhost[1] != 0x80 || | |
189 | h.ether_dhost[2] != 0xc2 || | |
190 | h.ether_dhost[3] != 0x00 || | |
191 | h.ether_dhost[4] != 0x00 || | |
35388783 | 192 | !IN_SET(h.ether_dhost[5], 0x00, 0x03, 0x0e)) |
35778343 YW |
193 | return log_lldp_rx_errno(n->lldp_rx, SYNTHETIC_ERRNO(EBADMSG), |
194 | "Received packet with wrong destination address, ignoring."); | |
34437b4f LP |
195 | |
196 | memcpy(&n->source_address, h.ether_shost, sizeof(struct ether_addr)); | |
197 | memcpy(&n->destination_address, h.ether_dhost, sizeof(struct ether_addr)); | |
198 | ||
199 | p = (const uint8_t*) LLDP_NEIGHBOR_RAW(n) + sizeof(struct ether_header); | |
200 | left = n->raw_size - sizeof(struct ether_header); | |
201 | ||
202 | for (;;) { | |
203 | uint8_t type; | |
204 | uint16_t length; | |
205 | ||
35388783 | 206 | if (left < 2) |
35778343 | 207 | return log_lldp_rx_errno(n->lldp_rx, SYNTHETIC_ERRNO(EBADMSG), |
35388783 | 208 | "TLV lacks header, ignoring."); |
34437b4f LP |
209 | |
210 | type = p[0] >> 1; | |
211 | length = p[1] + (((uint16_t) (p[0] & 1)) << 8); | |
212 | p += 2, left -= 2; | |
213 | ||
35388783 | 214 | if (left < length) |
35778343 YW |
215 | return log_lldp_rx_errno(n->lldp_rx, SYNTHETIC_ERRNO(EBADMSG), |
216 | "TLV truncated, ignoring datagram."); | |
34437b4f LP |
217 | |
218 | switch (type) { | |
219 | ||
6afa6767 | 220 | case SD_LLDP_TYPE_END: |
35388783 | 221 | if (length != 0) |
35778343 YW |
222 | return log_lldp_rx_errno(n->lldp_rx, SYNTHETIC_ERRNO(EBADMSG), |
223 | "End marker TLV not zero-sized, ignoring datagram."); | |
34437b4f | 224 | |
8c7c7100 | 225 | /* Note that after processing the SD_LLDP_TYPE_END left could still be > 0 |
226 | * as the message may contain padding (see IEEE 802.1AB-2016, sec. 8.5.12) */ | |
227 | ||
34437b4f LP |
228 | goto end_marker; |
229 | ||
6afa6767 | 230 | case SD_LLDP_TYPE_CHASSIS_ID: |
35388783 YW |
231 | if (length < 2 || length > 256) |
232 | /* includes the chassis subtype, hence one extra byte */ | |
35778343 YW |
233 | return log_lldp_rx_errno(n->lldp_rx, SYNTHETIC_ERRNO(EBADMSG), |
234 | "Chassis ID field size out of range, ignoring datagram."); | |
35388783 YW |
235 | |
236 | if (n->id.chassis_id) | |
35778343 YW |
237 | return log_lldp_rx_errno(n->lldp_rx, SYNTHETIC_ERRNO(EBADMSG), |
238 | "Duplicate chassis ID field, ignoring datagram."); | |
34437b4f LP |
239 | |
240 | n->id.chassis_id = memdup(p, length); | |
241 | if (!n->id.chassis_id) | |
35388783 | 242 | return log_oom_debug(); |
34437b4f LP |
243 | |
244 | n->id.chassis_id_size = length; | |
245 | break; | |
246 | ||
6afa6767 | 247 | case SD_LLDP_TYPE_PORT_ID: |
35388783 YW |
248 | if (length < 2 || length > 256) |
249 | /* includes the port subtype, hence one extra byte */ | |
35778343 YW |
250 | return log_lldp_rx_errno(n->lldp_rx, SYNTHETIC_ERRNO(EBADMSG), |
251 | "Port ID field size out of range, ignoring datagram."); | |
35388783 YW |
252 | |
253 | if (n->id.port_id) | |
35778343 YW |
254 | return log_lldp_rx_errno(n->lldp_rx, SYNTHETIC_ERRNO(EBADMSG), |
255 | "Duplicate port ID field, ignoring datagram."); | |
34437b4f LP |
256 | |
257 | n->id.port_id = memdup(p, length); | |
258 | if (!n->id.port_id) | |
35388783 | 259 | return log_oom_debug(); |
34437b4f LP |
260 | |
261 | n->id.port_id_size = length; | |
262 | break; | |
263 | ||
6afa6767 | 264 | case SD_LLDP_TYPE_TTL: |
35388783 | 265 | if (length != 2) |
35778343 YW |
266 | return log_lldp_rx_errno(n->lldp_rx, SYNTHETIC_ERRNO(EBADMSG), |
267 | "TTL field has wrong size, ignoring datagram."); | |
34437b4f | 268 | |
35388783 | 269 | if (n->has_ttl) |
35778343 YW |
270 | return log_lldp_rx_errno(n->lldp_rx, SYNTHETIC_ERRNO(EBADMSG), |
271 | "Duplicate TTL field, ignoring datagram."); | |
34437b4f LP |
272 | |
273 | n->ttl = unaligned_read_be16(p); | |
274 | n->has_ttl = true; | |
275 | break; | |
276 | ||
6afa6767 | 277 | case SD_LLDP_TYPE_PORT_DESCRIPTION: |
35778343 | 278 | r = parse_string(n->lldp_rx, &n->port_description, p, length); |
34437b4f LP |
279 | if (r < 0) |
280 | return r; | |
281 | break; | |
282 | ||
6afa6767 | 283 | case SD_LLDP_TYPE_SYSTEM_NAME: |
35778343 | 284 | r = parse_string(n->lldp_rx, &n->system_name, p, length); |
34437b4f LP |
285 | if (r < 0) |
286 | return r; | |
287 | break; | |
288 | ||
6afa6767 | 289 | case SD_LLDP_TYPE_SYSTEM_DESCRIPTION: |
35778343 | 290 | r = parse_string(n->lldp_rx, &n->system_description, p, length); |
34437b4f LP |
291 | if (r < 0) |
292 | return r; | |
293 | break; | |
294 | ||
6afa6767 | 295 | case SD_LLDP_TYPE_SYSTEM_CAPABILITIES: |
34437b4f | 296 | if (length != 4) |
35778343 YW |
297 | return log_lldp_rx_errno(n->lldp_rx, SYNTHETIC_ERRNO(EBADMSG), |
298 | "System capabilities field has wrong size."); | |
34437b4f | 299 | |
35388783 YW |
300 | n->system_capabilities = unaligned_read_be16(p); |
301 | n->enabled_capabilities = unaligned_read_be16(p + 2); | |
302 | n->has_capabilities = true; | |
34437b4f LP |
303 | break; |
304 | ||
35388783 | 305 | case SD_LLDP_TYPE_PRIVATE: |
34437b4f | 306 | if (length < 4) |
35778343 YW |
307 | return log_lldp_rx_errno(n->lldp_rx, SYNTHETIC_ERRNO(EBADMSG), |
308 | "Found private TLV that is too short, ignoring."); | |
35388783 YW |
309 | |
310 | /* RFC 8520: MUD URL */ | |
3be1e84f YW |
311 | if (memcmp(p, SD_LLDP_OUI_IANA_MUD, sizeof(SD_LLDP_OUI_IANA_MUD)) == 0) { |
312 | r = parse_string(n->lldp_rx, &n->mud_url, p + sizeof(SD_LLDP_OUI_IANA_MUD), | |
313 | length - sizeof(SD_LLDP_OUI_IANA_MUD)); | |
35388783 YW |
314 | if (r < 0) |
315 | return r; | |
f69b4ae8 | 316 | } |
496b21ab LA |
317 | |
318 | /* IEEE 802.1: VLAN ID */ | |
319 | if (memcmp(p, SD_LLDP_OUI_802_1_VLAN_ID, sizeof(SD_LLDP_OUI_802_1_VLAN_ID)) == 0) { | |
320 | if (length != (sizeof(SD_LLDP_OUI_802_1_VLAN_ID) + sizeof(uint16_t))) | |
321 | return log_lldp_rx_errno(n->lldp_rx, SYNTHETIC_ERRNO(EBADMSG), | |
322 | "Found 802.1 VLAN ID TLV with wrong length, ignoring."); | |
323 | ||
324 | n->has_port_vlan_id = true; | |
325 | n->port_vlan_id = unaligned_read_be16(p + sizeof(SD_LLDP_OUI_802_1_VLAN_ID)); | |
326 | } | |
34437b4f LP |
327 | break; |
328 | } | |
329 | ||
34437b4f LP |
330 | p += length, left -= length; |
331 | } | |
332 | ||
333 | end_marker: | |
35388783 | 334 | if (!n->id.chassis_id || !n->id.port_id || !n->has_ttl) |
35778343 YW |
335 | return log_lldp_rx_errno(n->lldp_rx, SYNTHETIC_ERRNO(EBADMSG), |
336 | "One or more mandatory TLV missing in datagram. Ignoring."); | |
34437b4f LP |
337 | |
338 | n->rindex = sizeof(struct ether_header); | |
339 | ||
340 | return 0; | |
341 | } | |
342 | ||
343 | void lldp_neighbor_start_ttl(sd_lldp_neighbor *n) { | |
344 | assert(n); | |
345 | ||
16fed825 LP |
346 | if (n->ttl > 0) { |
347 | usec_t base; | |
348 | ||
349 | /* Use the packet's timestamp if there is one known */ | |
ba4e0427 | 350 | base = triple_timestamp_by_clock(&n->timestamp, CLOCK_BOOTTIME); |
0da36375 | 351 | if (!timestamp_is_set(base)) |
ba4e0427 | 352 | base = now(CLOCK_BOOTTIME); /* Otherwise, take the current time */ |
16fed825 LP |
353 | |
354 | n->until = usec_add(base, n->ttl * USEC_PER_SEC); | |
355 | } else | |
34437b4f LP |
356 | n->until = 0; |
357 | ||
35778343 YW |
358 | if (n->lldp_rx) |
359 | prioq_reshuffle(n->lldp_rx->neighbor_by_expiry, n, &n->prioq_idx); | |
34437b4f LP |
360 | } |
361 | ||
362 | bool lldp_neighbor_equal(const sd_lldp_neighbor *a, const sd_lldp_neighbor *b) { | |
363 | if (a == b) | |
364 | return true; | |
365 | ||
366 | if (!a || !b) | |
367 | return false; | |
368 | ||
369 | if (a->raw_size != b->raw_size) | |
370 | return false; | |
371 | ||
372 | return memcmp(LLDP_NEIGHBOR_RAW(a), LLDP_NEIGHBOR_RAW(b), a->raw_size) == 0; | |
373 | } | |
374 | ||
17347053 | 375 | int sd_lldp_neighbor_get_source_address(sd_lldp_neighbor *n, struct ether_addr* address) { |
34437b4f LP |
376 | assert_return(n, -EINVAL); |
377 | assert_return(address, -EINVAL); | |
378 | ||
379 | *address = n->source_address; | |
380 | return 0; | |
381 | } | |
382 | ||
17347053 | 383 | int sd_lldp_neighbor_get_destination_address(sd_lldp_neighbor *n, struct ether_addr* address) { |
34437b4f LP |
384 | assert_return(n, -EINVAL); |
385 | assert_return(address, -EINVAL); | |
386 | ||
387 | *address = n->destination_address; | |
388 | return 0; | |
389 | } | |
390 | ||
17347053 | 391 | int sd_lldp_neighbor_get_chassis_id(sd_lldp_neighbor *n, uint8_t *type, const void **ret, size_t *size) { |
34437b4f LP |
392 | assert_return(n, -EINVAL); |
393 | assert_return(type, -EINVAL); | |
394 | assert_return(ret, -EINVAL); | |
395 | assert_return(size, -EINVAL); | |
396 | ||
397 | assert(n->id.chassis_id_size > 0); | |
398 | ||
399 | *type = *(uint8_t*) n->id.chassis_id; | |
400 | *ret = (uint8_t*) n->id.chassis_id + 1; | |
401 | *size = n->id.chassis_id_size - 1; | |
402 | ||
403 | return 0; | |
404 | } | |
405 | ||
406 | static int format_mac_address(const void *data, size_t sz, char **ret) { | |
407 | struct ether_addr a; | |
408 | char *k; | |
409 | ||
410 | assert(data || sz <= 0); | |
411 | ||
412 | if (sz != 7) | |
413 | return 0; | |
414 | ||
415 | memcpy(&a, (uint8_t*) data + 1, sizeof(a)); | |
416 | ||
417 | k = new(char, ETHER_ADDR_TO_STRING_MAX); | |
418 | if (!k) | |
419 | return -ENOMEM; | |
420 | ||
421 | *ret = ether_addr_to_string(&a, k); | |
422 | return 1; | |
423 | } | |
424 | ||
425 | static int format_network_address(const void *data, size_t sz, char **ret) { | |
426 | union in_addr_union a; | |
b3ad5fa9 | 427 | int family, r; |
34437b4f LP |
428 | |
429 | if (sz == 6 && ((uint8_t*) data)[1] == 1) { | |
430 | memcpy(&a.in, (uint8_t*) data + 2, sizeof(a.in)); | |
431 | family = AF_INET; | |
432 | } else if (sz == 18 && ((uint8_t*) data)[1] == 2) { | |
433 | memcpy(&a.in6, (uint8_t*) data + 2, sizeof(a.in6)); | |
434 | family = AF_INET6; | |
435 | } else | |
436 | return 0; | |
437 | ||
b3ad5fa9 ZJS |
438 | r = in_addr_to_string(family, &a, ret); |
439 | if (r < 0) | |
440 | return r; | |
441 | return 1; | |
34437b4f LP |
442 | } |
443 | ||
17347053 | 444 | int sd_lldp_neighbor_get_chassis_id_as_string(sd_lldp_neighbor *n, const char **ret) { |
34437b4f LP |
445 | char *k; |
446 | int r; | |
447 | ||
448 | assert_return(n, -EINVAL); | |
449 | assert_return(ret, -EINVAL); | |
450 | ||
451 | if (n->chassis_id_as_string) { | |
452 | *ret = n->chassis_id_as_string; | |
453 | return 0; | |
454 | } | |
455 | ||
456 | assert(n->id.chassis_id_size > 0); | |
457 | ||
458 | switch (*(uint8_t*) n->id.chassis_id) { | |
459 | ||
6afa6767 BG |
460 | case SD_LLDP_CHASSIS_SUBTYPE_CHASSIS_COMPONENT: |
461 | case SD_LLDP_CHASSIS_SUBTYPE_INTERFACE_ALIAS: | |
462 | case SD_LLDP_CHASSIS_SUBTYPE_PORT_COMPONENT: | |
463 | case SD_LLDP_CHASSIS_SUBTYPE_INTERFACE_NAME: | |
464 | case SD_LLDP_CHASSIS_SUBTYPE_LOCALLY_ASSIGNED: | |
34437b4f LP |
465 | k = cescape_length((char*) n->id.chassis_id + 1, n->id.chassis_id_size - 1); |
466 | if (!k) | |
467 | return -ENOMEM; | |
468 | ||
469 | goto done; | |
470 | ||
6afa6767 | 471 | case SD_LLDP_CHASSIS_SUBTYPE_MAC_ADDRESS: |
34437b4f LP |
472 | r = format_mac_address(n->id.chassis_id, n->id.chassis_id_size, &k); |
473 | if (r < 0) | |
474 | return r; | |
475 | if (r > 0) | |
476 | goto done; | |
477 | ||
478 | break; | |
479 | ||
6afa6767 | 480 | case SD_LLDP_CHASSIS_SUBTYPE_NETWORK_ADDRESS: |
34437b4f LP |
481 | r = format_network_address(n->id.chassis_id, n->id.chassis_id_size, &k); |
482 | if (r < 0) | |
483 | return r; | |
484 | if (r > 0) | |
485 | goto done; | |
486 | ||
487 | break; | |
488 | } | |
489 | ||
490 | /* Generic fallback */ | |
491 | k = hexmem(n->id.chassis_id, n->id.chassis_id_size); | |
492 | if (!k) | |
493 | return -ENOMEM; | |
494 | ||
495 | done: | |
496 | *ret = n->chassis_id_as_string = k; | |
497 | return 0; | |
498 | } | |
499 | ||
17347053 | 500 | int sd_lldp_neighbor_get_port_id(sd_lldp_neighbor *n, uint8_t *type, const void **ret, size_t *size) { |
34437b4f LP |
501 | assert_return(n, -EINVAL); |
502 | assert_return(type, -EINVAL); | |
503 | assert_return(ret, -EINVAL); | |
504 | assert_return(size, -EINVAL); | |
505 | ||
506 | assert(n->id.port_id_size > 0); | |
507 | ||
508 | *type = *(uint8_t*) n->id.port_id; | |
509 | *ret = (uint8_t*) n->id.port_id + 1; | |
510 | *size = n->id.port_id_size - 1; | |
511 | ||
512 | return 0; | |
513 | } | |
514 | ||
17347053 | 515 | int sd_lldp_neighbor_get_port_id_as_string(sd_lldp_neighbor *n, const char **ret) { |
34437b4f LP |
516 | char *k; |
517 | int r; | |
518 | ||
519 | assert_return(n, -EINVAL); | |
520 | assert_return(ret, -EINVAL); | |
521 | ||
522 | if (n->port_id_as_string) { | |
523 | *ret = n->port_id_as_string; | |
524 | return 0; | |
525 | } | |
526 | ||
527 | assert(n->id.port_id_size > 0); | |
528 | ||
529 | switch (*(uint8_t*) n->id.port_id) { | |
530 | ||
6afa6767 BG |
531 | case SD_LLDP_PORT_SUBTYPE_INTERFACE_ALIAS: |
532 | case SD_LLDP_PORT_SUBTYPE_PORT_COMPONENT: | |
533 | case SD_LLDP_PORT_SUBTYPE_INTERFACE_NAME: | |
534 | case SD_LLDP_PORT_SUBTYPE_LOCALLY_ASSIGNED: | |
34437b4f LP |
535 | k = cescape_length((char*) n->id.port_id + 1, n->id.port_id_size - 1); |
536 | if (!k) | |
537 | return -ENOMEM; | |
538 | ||
539 | goto done; | |
540 | ||
6afa6767 | 541 | case SD_LLDP_PORT_SUBTYPE_MAC_ADDRESS: |
34437b4f LP |
542 | r = format_mac_address(n->id.port_id, n->id.port_id_size, &k); |
543 | if (r < 0) | |
544 | return r; | |
545 | if (r > 0) | |
546 | goto done; | |
547 | ||
548 | break; | |
549 | ||
6afa6767 | 550 | case SD_LLDP_PORT_SUBTYPE_NETWORK_ADDRESS: |
34437b4f LP |
551 | r = format_network_address(n->id.port_id, n->id.port_id_size, &k); |
552 | if (r < 0) | |
553 | return r; | |
554 | if (r > 0) | |
555 | goto done; | |
556 | ||
557 | break; | |
558 | } | |
559 | ||
560 | /* Generic fallback */ | |
561 | k = hexmem(n->id.port_id, n->id.port_id_size); | |
562 | if (!k) | |
563 | return -ENOMEM; | |
564 | ||
565 | done: | |
566 | *ret = n->port_id_as_string = k; | |
567 | return 0; | |
568 | } | |
569 | ||
17347053 | 570 | int sd_lldp_neighbor_get_ttl(sd_lldp_neighbor *n, uint16_t *ret_sec) { |
34437b4f | 571 | assert_return(n, -EINVAL); |
8a19206d | 572 | assert_return(ret_sec, -EINVAL); |
34437b4f | 573 | |
8a19206d | 574 | *ret_sec = n->ttl; |
34437b4f LP |
575 | return 0; |
576 | } | |
577 | ||
17347053 | 578 | int sd_lldp_neighbor_get_system_name(sd_lldp_neighbor *n, const char **ret) { |
34437b4f LP |
579 | assert_return(n, -EINVAL); |
580 | assert_return(ret, -EINVAL); | |
581 | ||
582 | if (!n->system_name) | |
583 | return -ENODATA; | |
584 | ||
585 | *ret = n->system_name; | |
586 | return 0; | |
587 | } | |
588 | ||
17347053 | 589 | int sd_lldp_neighbor_get_system_description(sd_lldp_neighbor *n, const char **ret) { |
34437b4f LP |
590 | assert_return(n, -EINVAL); |
591 | assert_return(ret, -EINVAL); | |
592 | ||
593 | if (!n->system_description) | |
594 | return -ENODATA; | |
595 | ||
596 | *ret = n->system_description; | |
597 | return 0; | |
598 | } | |
599 | ||
17347053 | 600 | int sd_lldp_neighbor_get_port_description(sd_lldp_neighbor *n, const char **ret) { |
34437b4f LP |
601 | assert_return(n, -EINVAL); |
602 | assert_return(ret, -EINVAL); | |
603 | ||
604 | if (!n->port_description) | |
605 | return -ENODATA; | |
606 | ||
607 | *ret = n->port_description; | |
608 | return 0; | |
609 | } | |
610 | ||
17347053 | 611 | int sd_lldp_neighbor_get_mud_url(sd_lldp_neighbor *n, const char **ret) { |
f69b4ae8 SS |
612 | assert_return(n, -EINVAL); |
613 | assert_return(ret, -EINVAL); | |
614 | ||
615 | if (!n->mud_url) | |
616 | return -ENODATA; | |
617 | ||
618 | *ret = n->mud_url; | |
619 | return 0; | |
620 | } | |
621 | ||
17347053 | 622 | int sd_lldp_neighbor_get_system_capabilities(sd_lldp_neighbor *n, uint16_t *ret) { |
34437b4f LP |
623 | assert_return(n, -EINVAL); |
624 | assert_return(ret, -EINVAL); | |
625 | ||
626 | if (!n->has_capabilities) | |
627 | return -ENODATA; | |
628 | ||
629 | *ret = n->system_capabilities; | |
630 | return 0; | |
631 | } | |
632 | ||
17347053 | 633 | int sd_lldp_neighbor_get_enabled_capabilities(sd_lldp_neighbor *n, uint16_t *ret) { |
34437b4f LP |
634 | assert_return(n, -EINVAL); |
635 | assert_return(ret, -EINVAL); | |
636 | ||
637 | if (!n->has_capabilities) | |
638 | return -ENODATA; | |
639 | ||
640 | *ret = n->enabled_capabilities; | |
641 | return 0; | |
642 | } | |
643 | ||
496b21ab LA |
644 | int sd_lldp_neighbor_get_port_vlan_id(sd_lldp_neighbor *n, uint16_t *ret) { |
645 | assert_return(n, -EINVAL); | |
646 | assert_return(ret, -EINVAL); | |
647 | ||
648 | if (!n->has_port_vlan_id) | |
649 | return -ENODATA; | |
650 | ||
651 | *ret = n->port_vlan_id; | |
652 | return 0; | |
653 | } | |
654 | ||
17347053 | 655 | int sd_lldp_neighbor_tlv_rewind(sd_lldp_neighbor *n) { |
34437b4f LP |
656 | assert_return(n, -EINVAL); |
657 | ||
658 | assert(n->raw_size >= sizeof(struct ether_header)); | |
659 | n->rindex = sizeof(struct ether_header); | |
660 | ||
a85b46c3 | 661 | return n->rindex < n->raw_size; |
34437b4f LP |
662 | } |
663 | ||
17347053 | 664 | int sd_lldp_neighbor_tlv_next(sd_lldp_neighbor *n) { |
34437b4f LP |
665 | size_t length; |
666 | ||
667 | assert_return(n, -EINVAL); | |
668 | ||
669 | if (n->rindex == n->raw_size) /* EOF */ | |
670 | return -ESPIPE; | |
671 | ||
672 | if (n->rindex + 2 > n->raw_size) /* Truncated message */ | |
673 | return -EBADMSG; | |
674 | ||
f137029b | 675 | length = LLDP_NEIGHBOR_TLV_LENGTH(n); |
34437b4f LP |
676 | if (n->rindex + 2 + length > n->raw_size) |
677 | return -EBADMSG; | |
678 | ||
679 | n->rindex += 2 + length; | |
680 | return n->rindex < n->raw_size; | |
681 | } | |
682 | ||
17347053 | 683 | int sd_lldp_neighbor_tlv_get_type(sd_lldp_neighbor *n, uint8_t *type) { |
34437b4f LP |
684 | assert_return(n, -EINVAL); |
685 | assert_return(type, -EINVAL); | |
686 | ||
687 | if (n->rindex == n->raw_size) /* EOF */ | |
688 | return -ESPIPE; | |
689 | ||
690 | if (n->rindex + 2 > n->raw_size) | |
691 | return -EBADMSG; | |
692 | ||
f137029b | 693 | *type = LLDP_NEIGHBOR_TLV_TYPE(n); |
34437b4f LP |
694 | return 0; |
695 | } | |
696 | ||
17347053 | 697 | int sd_lldp_neighbor_tlv_is_type(sd_lldp_neighbor *n, uint8_t type) { |
34437b4f LP |
698 | uint8_t k; |
699 | int r; | |
700 | ||
701 | assert_return(n, -EINVAL); | |
702 | ||
703 | r = sd_lldp_neighbor_tlv_get_type(n, &k); | |
704 | if (r < 0) | |
705 | return r; | |
706 | ||
707 | return type == k; | |
708 | } | |
709 | ||
a40f6dd4 | 710 | int sd_lldp_neighbor_tlv_get_oui(sd_lldp_neighbor *n, uint8_t oui[static 3], uint8_t *subtype) { |
34437b4f LP |
711 | const uint8_t *d; |
712 | size_t length; | |
713 | int r; | |
714 | ||
715 | assert_return(n, -EINVAL); | |
716 | assert_return(oui, -EINVAL); | |
717 | assert_return(subtype, -EINVAL); | |
718 | ||
6afa6767 | 719 | r = sd_lldp_neighbor_tlv_is_type(n, SD_LLDP_TYPE_PRIVATE); |
34437b4f LP |
720 | if (r < 0) |
721 | return r; | |
722 | if (r == 0) | |
723 | return -ENXIO; | |
724 | ||
f137029b | 725 | length = LLDP_NEIGHBOR_TLV_LENGTH(n); |
34437b4f LP |
726 | if (length < 4) |
727 | return -EBADMSG; | |
728 | ||
729 | if (n->rindex + 2 + length > n->raw_size) | |
730 | return -EBADMSG; | |
731 | ||
f137029b | 732 | d = LLDP_NEIGHBOR_TLV_DATA(n); |
34437b4f LP |
733 | memcpy(oui, d, 3); |
734 | *subtype = d[3]; | |
735 | ||
736 | return 0; | |
737 | } | |
738 | ||
a40f6dd4 | 739 | int sd_lldp_neighbor_tlv_is_oui(sd_lldp_neighbor *n, const uint8_t oui[static 3], uint8_t subtype) { |
34437b4f LP |
740 | uint8_t k[3], st; |
741 | int r; | |
742 | ||
743 | r = sd_lldp_neighbor_tlv_get_oui(n, k, &st); | |
744 | if (r == -ENXIO) | |
745 | return 0; | |
746 | if (r < 0) | |
747 | return r; | |
748 | ||
749 | return memcmp(k, oui, 3) == 0 && st == subtype; | |
750 | } | |
751 | ||
17347053 | 752 | int sd_lldp_neighbor_tlv_get_raw(sd_lldp_neighbor *n, const void **ret, size_t *size) { |
34437b4f LP |
753 | size_t length; |
754 | ||
755 | assert_return(n, -EINVAL); | |
756 | assert_return(ret, -EINVAL); | |
757 | assert_return(size, -EINVAL); | |
758 | ||
759 | /* Note that this returns the full TLV, including the TLV header */ | |
760 | ||
761 | if (n->rindex + 2 > n->raw_size) | |
762 | return -EBADMSG; | |
763 | ||
f137029b | 764 | length = LLDP_NEIGHBOR_TLV_LENGTH(n); |
34437b4f LP |
765 | if (n->rindex + 2 + length > n->raw_size) |
766 | return -EBADMSG; | |
767 | ||
768 | *ret = (uint8_t*) LLDP_NEIGHBOR_RAW(n) + n->rindex; | |
769 | *size = length + 2; | |
770 | ||
771 | return 0; | |
772 | } | |
16fed825 | 773 | |
17347053 | 774 | int sd_lldp_neighbor_get_timestamp(sd_lldp_neighbor *n, clockid_t clock, uint64_t *ret) { |
16fed825 LP |
775 | assert_return(n, -EINVAL); |
776 | assert_return(TRIPLE_TIMESTAMP_HAS_CLOCK(clock), -EOPNOTSUPP); | |
777 | assert_return(clock_supported(clock), -EOPNOTSUPP); | |
778 | assert_return(ret, -EINVAL); | |
779 | ||
780 | if (!triple_timestamp_is_set(&n->timestamp)) | |
781 | return -ENODATA; | |
782 | ||
783 | *ret = triple_timestamp_by_clock(&n->timestamp, clock); | |
784 | return 0; | |
785 | } | |
329146a9 | 786 | |
309a747f | 787 | int lldp_neighbor_build_json(sd_lldp_neighbor *n, sd_json_variant **ret) { |
329146a9 TP |
788 | const char *chassis_id = NULL, *port_id = NULL, *port_description = NULL, |
789 | *system_name = NULL, *system_description = NULL; | |
790 | uint16_t cc = 0; | |
791 | bool valid_cc; | |
496b21ab LA |
792 | uint16_t vlanid = 0; |
793 | bool valid_vlanid; | |
329146a9 TP |
794 | |
795 | assert(n); | |
796 | assert(ret); | |
797 | ||
798 | (void) sd_lldp_neighbor_get_chassis_id_as_string(n, &chassis_id); | |
799 | (void) sd_lldp_neighbor_get_port_id_as_string(n, &port_id); | |
800 | (void) sd_lldp_neighbor_get_port_description(n, &port_description); | |
801 | (void) sd_lldp_neighbor_get_system_name(n, &system_name); | |
802 | (void) sd_lldp_neighbor_get_system_description(n, &system_description); | |
803 | ||
e2290a77 | 804 | valid_cc = sd_lldp_neighbor_get_enabled_capabilities(n, &cc) >= 0; |
496b21ab | 805 | valid_vlanid = sd_lldp_neighbor_get_port_vlan_id(n, &vlanid) >= 0; |
329146a9 | 806 | |
be5bee2a LP |
807 | return sd_json_buildo( |
808 | ret, | |
809 | JSON_BUILD_PAIR_STRING_NON_EMPTY("ChassisID", chassis_id), | |
810 | SD_JSON_BUILD_PAIR_BYTE_ARRAY("RawChassisID", n->id.chassis_id, n->id.chassis_id_size), | |
811 | JSON_BUILD_PAIR_STRING_NON_EMPTY("PortID", port_id), | |
812 | SD_JSON_BUILD_PAIR_BYTE_ARRAY("RawPortID", n->id.port_id, n->id.port_id_size), | |
813 | JSON_BUILD_PAIR_STRING_NON_EMPTY("PortDescription", port_description), | |
814 | JSON_BUILD_PAIR_STRING_NON_EMPTY("SystemName", system_name), | |
815 | JSON_BUILD_PAIR_STRING_NON_EMPTY("SystemDescription", system_description), | |
496b21ab | 816 | SD_JSON_BUILD_PAIR_CONDITION(valid_cc, "EnabledCapabilities", SD_JSON_BUILD_UNSIGNED(cc)), |
4c45437b | 817 | JSON_BUILD_PAIR_STRING_NON_EMPTY("MUDURL", n->mud_url), |
496b21ab | 818 | SD_JSON_BUILD_PAIR_CONDITION(valid_vlanid, "VlanID", SD_JSON_BUILD_UNSIGNED(vlanid))); |
329146a9 | 819 | } |