]> git.ipfire.org Git - thirdparty/systemd.git/blame - src/libsystemd-network/sd-lldp-rx.c
sd-lldp-rx: use _cleanup_ attribute at one more place
[thirdparty/systemd.git] / src / libsystemd-network / sd-lldp-rx.c
CommitLineData
db9ecf05 1/* SPDX-License-Identifier: LGPL-2.1-or-later */
ad1ad5c8
SS
2
3#include <arpa/inet.h>
284d1cd0 4#include <linux/sockios.h>
ef118d00 5#include <sys/ioctl.h>
ad1ad5c8 6
3a2ee855 7#include "sd-lldp-rx.h"
07630cea 8
b5efdb8a 9#include "alloc-util.h"
4f0e4d29 10#include "ether-addr-util.h"
6ec11d46 11#include "event-util.h"
3ffd4af2 12#include "fd-util.h"
34437b4f 13#include "lldp-neighbor.h"
032b27f5 14#include "lldp-network.h"
3a2ee855 15#include "lldp-rx-internal.h"
0a970718 16#include "memory-util.h"
61a9fa8f 17#include "network-common.h"
34437b4f 18#include "socket-util.h"
760877e9 19#include "sort-util.h"
4f0e4d29 20#include "string-table.h"
49699bac 21
34437b4f 22#define LLDP_DEFAULT_NEIGHBORS_MAX 128U
032b27f5 23
35778343
YW
24static const char * const lldp_rx_event_table[_SD_LLDP_RX_EVENT_MAX] = {
25 [SD_LLDP_RX_EVENT_ADDED] = "added",
26 [SD_LLDP_RX_EVENT_REMOVED] = "removed",
27 [SD_LLDP_RX_EVENT_UPDATED] = "updated",
28 [SD_LLDP_RX_EVENT_REFRESHED] = "refreshed",
4f0e4d29
YW
29};
30
35778343 31DEFINE_STRING_TABLE_LOOKUP(lldp_rx_event, sd_lldp_rx_event_t);
4f0e4d29 32
35778343
YW
33static void lldp_rx_flush_neighbors(sd_lldp_rx *lldp_rx) {
34 assert(lldp_rx);
ad1ad5c8 35
35778343 36 hashmap_clear(lldp_rx->neighbor_by_id);
ad1ad5c8
SS
37}
38
35778343
YW
39static void lldp_rx_callback(sd_lldp_rx *lldp_rx, sd_lldp_rx_event_t event, sd_lldp_neighbor *n) {
40 assert(lldp_rx);
41 assert(event >= 0 && event < _SD_LLDP_RX_EVENT_MAX);
90dffb22 42
35778343
YW
43 if (!lldp_rx->callback)
44 return (void) log_lldp_rx(lldp_rx, "Received '%s' event.", lldp_rx_event_to_string(event));
90dffb22 45
35778343
YW
46 log_lldp_rx(lldp_rx, "Invoking callback for '%s' event.", lldp_rx_event_to_string(event));
47 lldp_rx->callback(lldp_rx, event, n, lldp_rx->userdata);
90dffb22
LP
48}
49
35778343 50static int lldp_rx_make_space(sd_lldp_rx *lldp_rx, size_t extra) {
34437b4f
LP
51 usec_t t = USEC_INFINITY;
52 bool changed = false;
ad1ad5c8 53
35778343 54 assert(lldp_rx);
ad1ad5c8 55
34437b4f
LP
56 /* Remove all entries that are past their TTL, and more until at least the specified number of extra entries
57 * are free. */
ad1ad5c8 58
34437b4f 59 for (;;) {
90dffb22
LP
60 _cleanup_(sd_lldp_neighbor_unrefp) sd_lldp_neighbor *n = NULL;
61
35778343 62 n = prioq_peek(lldp_rx->neighbor_by_expiry);
34437b4f 63 if (!n)
859c37b1 64 break;
859c37b1 65
90dffb22
LP
66 sd_lldp_neighbor_ref(n);
67
35778343 68 if (hashmap_size(lldp_rx->neighbor_by_id) > LESS_BY(lldp_rx->neighbors_max, extra))
34437b4f 69 goto remove_one;
859c37b1 70
34437b4f
LP
71 if (t == USEC_INFINITY)
72 t = now(clock_boottime_or_monotonic());
859c37b1 73
34437b4f 74 if (n->until > t)
859c37b1 75 break;
859c37b1 76
34437b4f
LP
77 remove_one:
78 lldp_neighbor_unlink(n);
35778343 79 lldp_rx_callback(lldp_rx, SD_LLDP_RX_EVENT_REMOVED, n);
34437b4f
LP
80 changed = true;
81 }
859c37b1 82
34437b4f
LP
83 return changed;
84}
ad1ad5c8 85
35778343
YW
86static bool lldp_rx_keep_neighbor(sd_lldp_rx *lldp_rx, sd_lldp_neighbor *n) {
87 assert(lldp_rx);
90dffb22
LP
88 assert(n);
89
90 /* Don't keep data with a zero TTL */
91 if (n->ttl <= 0)
92 return false;
93
94 /* Filter out data from the filter address */
35778343
YW
95 if (!ether_addr_is_null(&lldp_rx->filter_address) &&
96 ether_addr_equal(&lldp_rx->filter_address, &n->source_address))
90dffb22
LP
97 return false;
98
99 /* Only add if the neighbor has a capability we are interested in. Note that we also store all neighbors with
100 * no caps field set. */
101 if (n->has_capabilities &&
35778343 102 (n->enabled_capabilities & lldp_rx->capability_mask) == 0)
90dffb22
LP
103 return false;
104
105 /* Keep everything else */
106 return true;
107}
108
35778343 109static int lldp_rx_start_timer(sd_lldp_rx *lldp_rx, sd_lldp_neighbor *neighbor);
0513ea4e 110
35778343 111static int lldp_rx_add_neighbor(sd_lldp_rx *lldp_rx, sd_lldp_neighbor *n) {
90dffb22
LP
112 _cleanup_(sd_lldp_neighbor_unrefp) sd_lldp_neighbor *old = NULL;
113 bool keep;
34437b4f 114 int r;
ad1ad5c8 115
35778343 116 assert(lldp_rx);
34437b4f 117 assert(n);
35778343 118 assert(!n->lldp_rx);
34437b4f 119
35778343 120 keep = lldp_rx_keep_neighbor(lldp_rx, n);
90dffb22 121
34437b4f 122 /* First retrieve the old entry for this MSAP */
35778343 123 old = hashmap_get(lldp_rx->neighbor_by_id, &n->id);
34437b4f 124 if (old) {
90dffb22
LP
125 sd_lldp_neighbor_ref(old);
126
127 if (!keep) {
128 lldp_neighbor_unlink(old);
35778343 129 lldp_rx_callback(lldp_rx, SD_LLDP_RX_EVENT_REMOVED, old);
90dffb22
LP
130 return 0;
131 }
132
34437b4f 133 if (lldp_neighbor_equal(n, old)) {
f21f31b2 134 /* Is this equal, then restart the TTL counter, but don't do anything else. */
16fed825 135 old->timestamp = n->timestamp;
35778343
YW
136 lldp_rx_start_timer(lldp_rx, old);
137 lldp_rx_callback(lldp_rx, SD_LLDP_RX_EVENT_REFRESHED, old);
34437b4f 138 return 0;
ad1ad5c8 139 }
34437b4f
LP
140
141 /* Data changed, remove the old entry, and add a new one */
142 lldp_neighbor_unlink(old);
ad1ad5c8 143
90dffb22
LP
144 } else if (!keep)
145 return 0;
ad1ad5c8 146
34437b4f 147 /* Then, make room for at least one new neighbor */
35778343 148 lldp_rx_make_space(lldp_rx, 1);
ad1ad5c8 149
b0a67b20 150 r = hashmap_ensure_put(&lldp_rx->neighbor_by_id, &lldp_neighbor_hash_ops, &n->id, n);
34437b4f 151 if (r < 0)
90dffb22 152 goto finish;
ad1ad5c8 153
b0a67b20 154 r = prioq_ensure_put(&lldp_rx->neighbor_by_expiry, lldp_neighbor_prioq_compare_func, n, &n->prioq_idx);
34437b4f 155 if (r < 0) {
35778343 156 assert_se(hashmap_remove(lldp_rx->neighbor_by_id, &n->id) == n);
90dffb22 157 goto finish;
ad1ad5c8
SS
158 }
159
35778343 160 n->lldp_rx = lldp_rx;
ad1ad5c8 161
35778343
YW
162 lldp_rx_start_timer(lldp_rx, n);
163 lldp_rx_callback(lldp_rx, old ? SD_LLDP_RX_EVENT_UPDATED : SD_LLDP_RX_EVENT_ADDED, n);
90dffb22
LP
164
165 return 1;
166
167finish:
168 if (old)
35778343 169 lldp_rx_callback(lldp_rx, SD_LLDP_RX_EVENT_REMOVED, old);
90dffb22
LP
170
171 return r;
ad1ad5c8
SS
172}
173
35778343 174static int lldp_rx_handle_datagram(sd_lldp_rx *lldp_rx, sd_lldp_neighbor *n) {
34437b4f 175 int r;
ad1ad5c8 176
35778343 177 assert(lldp_rx);
34437b4f 178 assert(n);
ad1ad5c8 179
34437b4f 180 r = lldp_neighbor_parse(n);
34437b4f
LP
181 if (r < 0)
182 return r;
ad1ad5c8 183
35778343 184 r = lldp_rx_add_neighbor(lldp_rx, n);
4be699a8
YW
185 if (r < 0)
186 return log_lldp_rx_errno(lldp_rx, r, "Failed to add datagram. Ignoring.");
ad1ad5c8 187
35778343 188 log_lldp_rx(lldp_rx, "Successfully processed LLDP datagram.");
34437b4f 189 return 0;
ad1ad5c8
SS
190}
191
35778343 192static int lldp_rx_receive_datagram(sd_event_source *s, int fd, uint32_t revents, void *userdata) {
34437b4f
LP
193 _cleanup_(sd_lldp_neighbor_unrefp) sd_lldp_neighbor *n = NULL;
194 ssize_t space, length;
35778343 195 sd_lldp_rx *lldp_rx = userdata;
16fed825 196 struct timespec ts;
49699bac 197
34437b4f 198 assert(fd >= 0);
35778343 199 assert(lldp_rx);
49699bac 200
34437b4f 201 space = next_datagram_size_fd(fd);
35388783 202 if (space < 0) {
35778343 203 log_lldp_rx_errno(lldp_rx, space, "Failed to determine datagram size to read, ignoring: %m");
35388783
YW
204 return 0;
205 }
49699bac 206
34437b4f 207 n = lldp_neighbor_new(space);
4be699a8
YW
208 if (!n) {
209 log_oom_debug();
210 return 0;
211 }
49699bac 212
34437b4f 213 length = recv(fd, LLDP_NEIGHBOR_RAW(n), n->raw_size, MSG_DONTWAIT);
f3315c58 214 if (length < 0) {
4c701096 215 if (IN_SET(errno, EAGAIN, EINTR))
f3315c58
LP
216 return 0;
217
35778343 218 log_lldp_rx_errno(lldp_rx, errno, "Failed to read LLDP datagram, ignoring: %m");
35388783 219 return 0;
f3315c58 220 }
dacd6cee 221
34437b4f 222 if ((size_t) length != n->raw_size) {
35778343 223 log_lldp_rx(lldp_rx, "Packet size mismatch, ignoring");
35388783 224 return 0;
34437b4f 225 }
49699bac 226
16fed825
LP
227 /* Try to get the timestamp of this packet if it is known */
228 if (ioctl(fd, SIOCGSTAMPNS, &ts) >= 0)
229 triple_timestamp_from_realtime(&n->timestamp, timespec_load(&ts));
230 else
231 triple_timestamp_get(&n->timestamp);
232
4be699a8
YW
233 (void) lldp_rx_handle_datagram(lldp_rx, n);
234 return 0;
49699bac
SS
235}
236
35778343
YW
237static void lldp_rx_reset(sd_lldp_rx *lldp_rx) {
238 assert(lldp_rx);
fc6a313b 239
35778343
YW
240 (void) event_source_disable(lldp_rx->timer_event_source);
241 lldp_rx->io_event_source = sd_event_source_disable_unref(lldp_rx->io_event_source);
242 lldp_rx->fd = safe_close(lldp_rx->fd);
fc6a313b
LP
243}
244
b5dce07a
YW
245int sd_lldp_rx_is_running(sd_lldp_rx *lldp_rx) {
246 if (!lldp_rx)
247 return false;
248
249 return lldp_rx->fd >= 0;
250}
251
35778343 252_public_ int sd_lldp_rx_start(sd_lldp_rx *lldp_rx) {
ad1ad5c8
SS
253 int r;
254
35778343
YW
255 assert_return(lldp_rx, -EINVAL);
256 assert_return(lldp_rx->event, -EINVAL);
257 assert_return(lldp_rx->ifindex > 0, -EINVAL);
ad1ad5c8 258
b5dce07a 259 if (sd_lldp_rx_is_running(lldp_rx))
032b27f5 260 return 0;
ad1ad5c8 261
35778343 262 assert(!lldp_rx->io_event_source);
49699bac 263
35778343
YW
264 lldp_rx->fd = lldp_network_bind_raw_socket(lldp_rx->ifindex);
265 if (lldp_rx->fd < 0)
266 return lldp_rx->fd;
032b27f5 267
35778343 268 r = sd_event_add_io(lldp_rx->event, &lldp_rx->io_event_source, lldp_rx->fd, EPOLLIN, lldp_rx_receive_datagram, lldp_rx);
fc6a313b
LP
269 if (r < 0)
270 goto fail;
032b27f5 271
35778343 272 r = sd_event_source_set_priority(lldp_rx->io_event_source, lldp_rx->event_priority);
fc6a313b
LP
273 if (r < 0)
274 goto fail;
032b27f5 275
35778343 276 (void) sd_event_source_set_description(lldp_rx->io_event_source, "lldp-rx-io");
ad1ad5c8 277
35778343 278 log_lldp_rx(lldp_rx, "Started LLDP client");
032b27f5
LP
279 return 1;
280
281fail:
35778343 282 lldp_rx_reset(lldp_rx);
032b27f5 283 return r;
ad1ad5c8
SS
284}
285
35778343 286_public_ int sd_lldp_rx_stop(sd_lldp_rx *lldp_rx) {
b5dce07a 287 if (!sd_lldp_rx_is_running(lldp_rx))
032b27f5 288 return 0;
ad1ad5c8 289
35778343 290 log_lldp_rx(lldp_rx, "Stopping LLDP client");
ad1ad5c8 291
35778343
YW
292 lldp_rx_reset(lldp_rx);
293 lldp_rx_flush_neighbors(lldp_rx);
ad1ad5c8 294
032b27f5 295 return 1;
ad1ad5c8
SS
296}
297
35778343 298_public_ int sd_lldp_rx_attach_event(sd_lldp_rx *lldp_rx, sd_event *event, int64_t priority) {
ad1ad5c8
SS
299 int r;
300
35778343 301 assert_return(lldp_rx, -EINVAL);
b5dce07a 302 assert_return(!sd_lldp_rx_is_running(lldp_rx), -EBUSY);
35778343 303 assert_return(!lldp_rx->event, -EBUSY);
ad1ad5c8
SS
304
305 if (event)
35778343 306 lldp_rx->event = sd_event_ref(event);
ad1ad5c8 307 else {
35778343 308 r = sd_event_default(&lldp_rx->event);
ad1ad5c8
SS
309 if (r < 0)
310 return r;
311 }
312
35778343 313 lldp_rx->event_priority = priority;
ad1ad5c8
SS
314
315 return 0;
316}
317
35778343 318_public_ int sd_lldp_rx_detach_event(sd_lldp_rx *lldp_rx) {
35778343 319 assert_return(lldp_rx, -EINVAL);
b5dce07a 320 assert_return(!sd_lldp_rx_is_running(lldp_rx), -EBUSY);
ad1ad5c8 321
aa3f8d4c
YW
322 lldp_rx->io_event_source = sd_event_source_disable_unref(lldp_rx->io_event_source);
323 lldp_rx->timer_event_source = sd_event_source_disable_unref(lldp_rx->timer_event_source);
35778343 324 lldp_rx->event = sd_event_unref(lldp_rx->event);
ad1ad5c8
SS
325 return 0;
326}
327
35778343
YW
328_public_ sd_event* sd_lldp_rx_get_event(sd_lldp_rx *lldp_rx) {
329 assert_return(lldp_rx, NULL);
3db2ec56 330
35778343 331 return lldp_rx->event;
3db2ec56
LP
332}
333
35778343
YW
334_public_ int sd_lldp_rx_set_callback(sd_lldp_rx *lldp_rx, sd_lldp_rx_callback_t cb, void *userdata) {
335 assert_return(lldp_rx, -EINVAL);
49699bac 336
35778343
YW
337 lldp_rx->callback = cb;
338 lldp_rx->userdata = userdata;
49699bac
SS
339
340 return 0;
341}
342
35778343
YW
343_public_ int sd_lldp_rx_set_ifindex(sd_lldp_rx *lldp_rx, int ifindex) {
344 assert_return(lldp_rx, -EINVAL);
fc6a313b 345 assert_return(ifindex > 0, -EINVAL);
b5dce07a 346 assert_return(!sd_lldp_rx_is_running(lldp_rx), -EBUSY);
fc6a313b 347
35778343 348 lldp_rx->ifindex = ifindex;
fc6a313b
LP
349 return 0;
350}
351
35778343
YW
352int sd_lldp_rx_set_ifname(sd_lldp_rx *lldp_rx, const char *ifname) {
353 assert_return(lldp_rx, -EINVAL);
61a9fa8f
YW
354 assert_return(ifname, -EINVAL);
355
356 if (!ifname_valid_full(ifname, IFNAME_VALID_ALTERNATIVE))
357 return -EINVAL;
358
35778343 359 return free_and_strdup(&lldp_rx->ifname, ifname);
61a9fa8f
YW
360}
361
35778343
YW
362const char *sd_lldp_rx_get_ifname(sd_lldp_rx *lldp_rx) {
363 if (!lldp_rx)
61a9fa8f
YW
364 return NULL;
365
35778343 366 return get_ifname(lldp_rx->ifindex, &lldp_rx->ifname);
61a9fa8f
YW
367}
368
35778343
YW
369static sd_lldp_rx *lldp_rx_free(sd_lldp_rx *lldp_rx) {
370 assert(lldp_rx);
fc6a313b 371
35778343 372 lldp_rx_reset(lldp_rx);
eb2f7502 373
35778343 374 sd_lldp_rx_detach_event(lldp_rx);
eb2f7502 375
35778343 376 lldp_rx_flush_neighbors(lldp_rx);
ad1ad5c8 377
35778343
YW
378 hashmap_free(lldp_rx->neighbor_by_id);
379 prioq_free(lldp_rx->neighbor_by_expiry);
380 free(lldp_rx->ifname);
381 return mfree(lldp_rx);
ad1ad5c8
SS
382}
383
35778343 384DEFINE_PUBLIC_TRIVIAL_REF_UNREF_FUNC(sd_lldp_rx, sd_lldp_rx, lldp_rx_free);
8301aa0b 385
35778343
YW
386_public_ int sd_lldp_rx_new(sd_lldp_rx **ret) {
387 _cleanup_(sd_lldp_rx_unrefp) sd_lldp_rx *lldp_rx = NULL;
ad1ad5c8
SS
388
389 assert_return(ret, -EINVAL);
ad1ad5c8 390
35778343
YW
391 lldp_rx = new(sd_lldp_rx, 1);
392 if (!lldp_rx)
ad1ad5c8
SS
393 return -ENOMEM;
394
35778343 395 *lldp_rx = (sd_lldp_rx) {
8158b90d
YW
396 .n_ref = 1,
397 .fd = -1,
398 .neighbors_max = LLDP_DEFAULT_NEIGHBORS_MAX,
f5fbe71d 399 .capability_mask = UINT16_MAX,
8158b90d 400 };
ad1ad5c8 401
35778343 402 *ret = TAKE_PTR(lldp_rx);
ad1ad5c8
SS
403 return 0;
404}
7434883c 405
34437b4f 406static int on_timer_event(sd_event_source *s, uint64_t usec, void *userdata) {
35778343 407 sd_lldp_rx *lldp_rx = userdata;
bb1d9534 408 int r;
34437b4f 409
35778343 410 r = lldp_rx_make_space(lldp_rx, 0);
35388783 411 if (r < 0) {
35778343 412 log_lldp_rx_errno(lldp_rx, r, "Failed to make space, ignoring: %m");
35388783
YW
413 return 0;
414 }
34437b4f 415
35778343 416 r = lldp_rx_start_timer(lldp_rx, NULL);
35388783 417 if (r < 0) {
35778343 418 log_lldp_rx_errno(lldp_rx, r, "Failed to restart timer, ignoring: %m");
35388783
YW
419 return 0;
420 }
34437b4f 421
34437b4f
LP
422 return 0;
423}
7434883c 424
35778343 425static int lldp_rx_start_timer(sd_lldp_rx *lldp_rx, sd_lldp_neighbor *neighbor) {
34437b4f 426 sd_lldp_neighbor *n;
34437b4f 427
35778343 428 assert(lldp_rx);
34437b4f 429
0513ea4e
TH
430 if (neighbor)
431 lldp_neighbor_start_ttl(neighbor);
432
35778343 433 n = prioq_peek(lldp_rx->neighbor_by_expiry);
6ec11d46 434 if (!n)
35778343 435 return event_source_disable(lldp_rx->timer_event_source);
7434883c 436
35778343 437 if (!lldp_rx->event)
34437b4f
LP
438 return 0;
439
35778343 440 return event_reset_time(lldp_rx->event, &lldp_rx->timer_event_source,
6ec11d46
YW
441 clock_boottime_or_monotonic(),
442 n->until, 0,
35778343
YW
443 on_timer_event, lldp_rx,
444 lldp_rx->event_priority, "lldp-rx-timer", true);
34437b4f
LP
445}
446
90496cc6
YW
447static inline int neighbor_compare_func(sd_lldp_neighbor * const *a, sd_lldp_neighbor * const *b) {
448 assert(a);
449 assert(b);
450 assert(*a);
451 assert(*b);
452
453 return lldp_neighbor_id_compare_func(&(*a)->id, &(*b)->id);
454}
455
35778343 456_public_ int sd_lldp_rx_get_neighbors(sd_lldp_rx *lldp_rx, sd_lldp_neighbor ***ret) {
0cd7e072
YW
457 _cleanup_free_ sd_lldp_neighbor **l = NULL;
458 sd_lldp_neighbor *n;
34437b4f
LP
459 int k = 0, r;
460
35778343 461 assert_return(lldp_rx, -EINVAL);
34437b4f
LP
462 assert_return(ret, -EINVAL);
463
35778343 464 if (hashmap_isempty(lldp_rx->neighbor_by_id)) { /* Special shortcut */
34437b4f 465 *ret = NULL;
7434883c
BG
466 return 0;
467 }
468
35778343 469 l = new0(sd_lldp_neighbor*, hashmap_size(lldp_rx->neighbor_by_id));
34437b4f 470 if (!l)
7434883c
BG
471 return -ENOMEM;
472
35778343 473 r = lldp_rx_start_timer(lldp_rx, NULL);
0cd7e072 474 if (r < 0)
34437b4f 475 return r;
7434883c 476
35778343 477 HASHMAP_FOREACH(n, lldp_rx->neighbor_by_id)
34437b4f
LP
478 l[k++] = sd_lldp_neighbor_ref(n);
479
35778343 480 assert((size_t) k == hashmap_size(lldp_rx->neighbor_by_id));
34437b4f
LP
481
482 /* Return things in a stable order */
93bab288 483 typesafe_qsort(l, k, neighbor_compare_func);
0cd7e072 484 *ret = TAKE_PTR(l);
34437b4f
LP
485
486 return k;
487}
488
35778343
YW
489_public_ int sd_lldp_rx_set_neighbors_max(sd_lldp_rx *lldp_rx, uint64_t m) {
490 assert_return(lldp_rx, -EINVAL);
9141594c 491 assert_return(m > 0, -EINVAL);
34437b4f 492
35778343
YW
493 lldp_rx->neighbors_max = m;
494 lldp_rx_make_space(lldp_rx, 0);
34437b4f
LP
495
496 return 0;
497}
498
35778343
YW
499_public_ int sd_lldp_rx_match_capabilities(sd_lldp_rx *lldp_rx, uint16_t mask) {
500 assert_return(lldp_rx, -EINVAL);
34437b4f
LP
501 assert_return(mask != 0, -EINVAL);
502
35778343 503 lldp_rx->capability_mask = mask;
34437b4f
LP
504
505 return 0;
7434883c 506}
b553a6b1 507
35778343
YW
508_public_ int sd_lldp_rx_set_filter_address(sd_lldp_rx *lldp_rx, const struct ether_addr *addr) {
509 assert_return(lldp_rx, -EINVAL);
b553a6b1
LP
510
511 /* In order to deal nicely with bridges that send back our own packets, allow one address to be filtered, so
512 * that our own can be filtered out here. */
513
a1fb61b0 514 if (addr)
35778343 515 lldp_rx->filter_address = *addr;
a1fb61b0 516 else
35778343 517 zero(lldp_rx->filter_address);
b553a6b1 518
b553a6b1
LP
519 return 0;
520}