]> git.ipfire.org Git - thirdparty/systemd.git/blame - src/libsystemd-network/sd-lldp-rx.c
tree-wide: use ASSERT_PTR more
[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 71 if (t == USEC_INFINITY)
ba4e0427 72 t = now(CLOCK_BOOTTIME);
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;
99534007 195 sd_lldp_rx *lldp_rx = ASSERT_PTR(userdata);
16fed825 196 struct timespec ts;
49699bac 197
34437b4f 198 assert(fd >= 0);
49699bac 199
34437b4f 200 space = next_datagram_size_fd(fd);
35388783 201 if (space < 0) {
ab8a8a4e
YW
202 if (ERRNO_IS_TRANSIENT(space) || ERRNO_IS_DISCONNECT(space))
203 return 0;
204
35778343 205 log_lldp_rx_errno(lldp_rx, space, "Failed to determine datagram size to read, ignoring: %m");
35388783
YW
206 return 0;
207 }
49699bac 208
34437b4f 209 n = lldp_neighbor_new(space);
4be699a8
YW
210 if (!n) {
211 log_oom_debug();
212 return 0;
213 }
49699bac 214
34437b4f 215 length = recv(fd, LLDP_NEIGHBOR_RAW(n), n->raw_size, MSG_DONTWAIT);
f3315c58 216 if (length < 0) {
ab8a8a4e 217 if (ERRNO_IS_TRANSIENT(errno) || ERRNO_IS_DISCONNECT(errno))
f3315c58
LP
218 return 0;
219
35778343 220 log_lldp_rx_errno(lldp_rx, errno, "Failed to read LLDP datagram, ignoring: %m");
35388783 221 return 0;
f3315c58 222 }
dacd6cee 223
34437b4f 224 if ((size_t) length != n->raw_size) {
35778343 225 log_lldp_rx(lldp_rx, "Packet size mismatch, ignoring");
35388783 226 return 0;
34437b4f 227 }
49699bac 228
16fed825
LP
229 /* Try to get the timestamp of this packet if it is known */
230 if (ioctl(fd, SIOCGSTAMPNS, &ts) >= 0)
231 triple_timestamp_from_realtime(&n->timestamp, timespec_load(&ts));
232 else
233 triple_timestamp_get(&n->timestamp);
234
4be699a8
YW
235 (void) lldp_rx_handle_datagram(lldp_rx, n);
236 return 0;
49699bac
SS
237}
238
35778343
YW
239static void lldp_rx_reset(sd_lldp_rx *lldp_rx) {
240 assert(lldp_rx);
fc6a313b 241
35778343
YW
242 (void) event_source_disable(lldp_rx->timer_event_source);
243 lldp_rx->io_event_source = sd_event_source_disable_unref(lldp_rx->io_event_source);
244 lldp_rx->fd = safe_close(lldp_rx->fd);
fc6a313b
LP
245}
246
b5dce07a
YW
247int sd_lldp_rx_is_running(sd_lldp_rx *lldp_rx) {
248 if (!lldp_rx)
249 return false;
250
251 return lldp_rx->fd >= 0;
252}
253
17347053 254int sd_lldp_rx_start(sd_lldp_rx *lldp_rx) {
ad1ad5c8
SS
255 int r;
256
35778343
YW
257 assert_return(lldp_rx, -EINVAL);
258 assert_return(lldp_rx->event, -EINVAL);
259 assert_return(lldp_rx->ifindex > 0, -EINVAL);
ad1ad5c8 260
b5dce07a 261 if (sd_lldp_rx_is_running(lldp_rx))
032b27f5 262 return 0;
ad1ad5c8 263
35778343 264 assert(!lldp_rx->io_event_source);
49699bac 265
35778343
YW
266 lldp_rx->fd = lldp_network_bind_raw_socket(lldp_rx->ifindex);
267 if (lldp_rx->fd < 0)
268 return lldp_rx->fd;
032b27f5 269
35778343 270 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
271 if (r < 0)
272 goto fail;
032b27f5 273
35778343 274 r = sd_event_source_set_priority(lldp_rx->io_event_source, lldp_rx->event_priority);
fc6a313b
LP
275 if (r < 0)
276 goto fail;
032b27f5 277
35778343 278 (void) sd_event_source_set_description(lldp_rx->io_event_source, "lldp-rx-io");
ad1ad5c8 279
35778343 280 log_lldp_rx(lldp_rx, "Started LLDP client");
032b27f5
LP
281 return 1;
282
283fail:
35778343 284 lldp_rx_reset(lldp_rx);
032b27f5 285 return r;
ad1ad5c8
SS
286}
287
17347053 288int sd_lldp_rx_stop(sd_lldp_rx *lldp_rx) {
b5dce07a 289 if (!sd_lldp_rx_is_running(lldp_rx))
032b27f5 290 return 0;
ad1ad5c8 291
35778343 292 log_lldp_rx(lldp_rx, "Stopping LLDP client");
ad1ad5c8 293
35778343
YW
294 lldp_rx_reset(lldp_rx);
295 lldp_rx_flush_neighbors(lldp_rx);
ad1ad5c8 296
032b27f5 297 return 1;
ad1ad5c8
SS
298}
299
17347053 300int sd_lldp_rx_attach_event(sd_lldp_rx *lldp_rx, sd_event *event, int64_t priority) {
ad1ad5c8
SS
301 int r;
302
35778343 303 assert_return(lldp_rx, -EINVAL);
b5dce07a 304 assert_return(!sd_lldp_rx_is_running(lldp_rx), -EBUSY);
35778343 305 assert_return(!lldp_rx->event, -EBUSY);
ad1ad5c8
SS
306
307 if (event)
35778343 308 lldp_rx->event = sd_event_ref(event);
ad1ad5c8 309 else {
35778343 310 r = sd_event_default(&lldp_rx->event);
ad1ad5c8
SS
311 if (r < 0)
312 return r;
313 }
314
35778343 315 lldp_rx->event_priority = priority;
ad1ad5c8
SS
316
317 return 0;
318}
319
17347053 320int sd_lldp_rx_detach_event(sd_lldp_rx *lldp_rx) {
35778343 321 assert_return(lldp_rx, -EINVAL);
b5dce07a 322 assert_return(!sd_lldp_rx_is_running(lldp_rx), -EBUSY);
ad1ad5c8 323
aa3f8d4c
YW
324 lldp_rx->io_event_source = sd_event_source_disable_unref(lldp_rx->io_event_source);
325 lldp_rx->timer_event_source = sd_event_source_disable_unref(lldp_rx->timer_event_source);
35778343 326 lldp_rx->event = sd_event_unref(lldp_rx->event);
ad1ad5c8
SS
327 return 0;
328}
329
17347053 330sd_event* sd_lldp_rx_get_event(sd_lldp_rx *lldp_rx) {
35778343 331 assert_return(lldp_rx, NULL);
3db2ec56 332
35778343 333 return lldp_rx->event;
3db2ec56
LP
334}
335
17347053 336int sd_lldp_rx_set_callback(sd_lldp_rx *lldp_rx, sd_lldp_rx_callback_t cb, void *userdata) {
35778343 337 assert_return(lldp_rx, -EINVAL);
49699bac 338
35778343
YW
339 lldp_rx->callback = cb;
340 lldp_rx->userdata = userdata;
49699bac
SS
341
342 return 0;
343}
344
17347053 345int sd_lldp_rx_set_ifindex(sd_lldp_rx *lldp_rx, int ifindex) {
35778343 346 assert_return(lldp_rx, -EINVAL);
fc6a313b 347 assert_return(ifindex > 0, -EINVAL);
b5dce07a 348 assert_return(!sd_lldp_rx_is_running(lldp_rx), -EBUSY);
fc6a313b 349
35778343 350 lldp_rx->ifindex = ifindex;
fc6a313b
LP
351 return 0;
352}
353
35778343
YW
354int sd_lldp_rx_set_ifname(sd_lldp_rx *lldp_rx, const char *ifname) {
355 assert_return(lldp_rx, -EINVAL);
61a9fa8f
YW
356 assert_return(ifname, -EINVAL);
357
358 if (!ifname_valid_full(ifname, IFNAME_VALID_ALTERNATIVE))
359 return -EINVAL;
360
35778343 361 return free_and_strdup(&lldp_rx->ifname, ifname);
61a9fa8f
YW
362}
363
5977b71f
YW
364int sd_lldp_rx_get_ifname(sd_lldp_rx *lldp_rx, const char **ret) {
365 int r;
366
367 assert_return(lldp_rx, -EINVAL);
61a9fa8f 368
5977b71f
YW
369 r = get_ifname(lldp_rx->ifindex, &lldp_rx->ifname);
370 if (r < 0)
371 return r;
372
373 if (ret)
374 *ret = lldp_rx->ifname;
375
376 return 0;
61a9fa8f
YW
377}
378
35778343 379static sd_lldp_rx *lldp_rx_free(sd_lldp_rx *lldp_rx) {
35777f51
YW
380 if (!lldp_rx)
381 return NULL;
fc6a313b 382
35778343 383 lldp_rx_reset(lldp_rx);
eb2f7502 384
35778343 385 sd_lldp_rx_detach_event(lldp_rx);
eb2f7502 386
35778343 387 lldp_rx_flush_neighbors(lldp_rx);
ad1ad5c8 388
35778343
YW
389 hashmap_free(lldp_rx->neighbor_by_id);
390 prioq_free(lldp_rx->neighbor_by_expiry);
391 free(lldp_rx->ifname);
392 return mfree(lldp_rx);
ad1ad5c8
SS
393}
394
35778343 395DEFINE_PUBLIC_TRIVIAL_REF_UNREF_FUNC(sd_lldp_rx, sd_lldp_rx, lldp_rx_free);
8301aa0b 396
17347053 397int sd_lldp_rx_new(sd_lldp_rx **ret) {
35778343 398 _cleanup_(sd_lldp_rx_unrefp) sd_lldp_rx *lldp_rx = NULL;
ad1ad5c8
SS
399
400 assert_return(ret, -EINVAL);
ad1ad5c8 401
35778343
YW
402 lldp_rx = new(sd_lldp_rx, 1);
403 if (!lldp_rx)
ad1ad5c8
SS
404 return -ENOMEM;
405
35778343 406 *lldp_rx = (sd_lldp_rx) {
8158b90d
YW
407 .n_ref = 1,
408 .fd = -1,
409 .neighbors_max = LLDP_DEFAULT_NEIGHBORS_MAX,
f5fbe71d 410 .capability_mask = UINT16_MAX,
8158b90d 411 };
ad1ad5c8 412
35778343 413 *ret = TAKE_PTR(lldp_rx);
ad1ad5c8
SS
414 return 0;
415}
7434883c 416
34437b4f 417static int on_timer_event(sd_event_source *s, uint64_t usec, void *userdata) {
35778343 418 sd_lldp_rx *lldp_rx = userdata;
bb1d9534 419 int r;
34437b4f 420
35778343 421 r = lldp_rx_make_space(lldp_rx, 0);
35388783 422 if (r < 0) {
35778343 423 log_lldp_rx_errno(lldp_rx, r, "Failed to make space, ignoring: %m");
35388783
YW
424 return 0;
425 }
34437b4f 426
35778343 427 r = lldp_rx_start_timer(lldp_rx, NULL);
35388783 428 if (r < 0) {
35778343 429 log_lldp_rx_errno(lldp_rx, r, "Failed to restart timer, ignoring: %m");
35388783
YW
430 return 0;
431 }
34437b4f 432
34437b4f
LP
433 return 0;
434}
7434883c 435
35778343 436static int lldp_rx_start_timer(sd_lldp_rx *lldp_rx, sd_lldp_neighbor *neighbor) {
34437b4f 437 sd_lldp_neighbor *n;
34437b4f 438
35778343 439 assert(lldp_rx);
92466b8d 440 assert(lldp_rx->event);
34437b4f 441
0513ea4e
TH
442 if (neighbor)
443 lldp_neighbor_start_ttl(neighbor);
444
35778343 445 n = prioq_peek(lldp_rx->neighbor_by_expiry);
6ec11d46 446 if (!n)
35778343 447 return event_source_disable(lldp_rx->timer_event_source);
7434883c 448
35778343 449 return event_reset_time(lldp_rx->event, &lldp_rx->timer_event_source,
ba4e0427 450 CLOCK_BOOTTIME,
6ec11d46 451 n->until, 0,
35778343
YW
452 on_timer_event, lldp_rx,
453 lldp_rx->event_priority, "lldp-rx-timer", true);
34437b4f
LP
454}
455
90496cc6
YW
456static inline int neighbor_compare_func(sd_lldp_neighbor * const *a, sd_lldp_neighbor * const *b) {
457 assert(a);
458 assert(b);
459 assert(*a);
460 assert(*b);
461
462 return lldp_neighbor_id_compare_func(&(*a)->id, &(*b)->id);
463}
464
17347053 465int sd_lldp_rx_get_neighbors(sd_lldp_rx *lldp_rx, sd_lldp_neighbor ***ret) {
0cd7e072
YW
466 _cleanup_free_ sd_lldp_neighbor **l = NULL;
467 sd_lldp_neighbor *n;
3e4a2025 468 int k = 0;
34437b4f 469
35778343 470 assert_return(lldp_rx, -EINVAL);
34437b4f
LP
471 assert_return(ret, -EINVAL);
472
35778343 473 if (hashmap_isempty(lldp_rx->neighbor_by_id)) { /* Special shortcut */
34437b4f 474 *ret = NULL;
7434883c
BG
475 return 0;
476 }
477
35778343 478 l = new0(sd_lldp_neighbor*, hashmap_size(lldp_rx->neighbor_by_id));
34437b4f 479 if (!l)
7434883c
BG
480 return -ENOMEM;
481
35778343 482 HASHMAP_FOREACH(n, lldp_rx->neighbor_by_id)
34437b4f
LP
483 l[k++] = sd_lldp_neighbor_ref(n);
484
35778343 485 assert((size_t) k == hashmap_size(lldp_rx->neighbor_by_id));
34437b4f
LP
486
487 /* Return things in a stable order */
93bab288 488 typesafe_qsort(l, k, neighbor_compare_func);
0cd7e072 489 *ret = TAKE_PTR(l);
34437b4f
LP
490
491 return k;
492}
493
17347053 494int sd_lldp_rx_set_neighbors_max(sd_lldp_rx *lldp_rx, uint64_t m) {
35778343 495 assert_return(lldp_rx, -EINVAL);
9141594c 496 assert_return(m > 0, -EINVAL);
34437b4f 497
35778343
YW
498 lldp_rx->neighbors_max = m;
499 lldp_rx_make_space(lldp_rx, 0);
34437b4f
LP
500
501 return 0;
502}
503
17347053 504int sd_lldp_rx_match_capabilities(sd_lldp_rx *lldp_rx, uint16_t mask) {
35778343 505 assert_return(lldp_rx, -EINVAL);
34437b4f
LP
506 assert_return(mask != 0, -EINVAL);
507
35778343 508 lldp_rx->capability_mask = mask;
34437b4f
LP
509
510 return 0;
7434883c 511}
b553a6b1 512
17347053 513int sd_lldp_rx_set_filter_address(sd_lldp_rx *lldp_rx, const struct ether_addr *addr) {
35778343 514 assert_return(lldp_rx, -EINVAL);
b553a6b1
LP
515
516 /* In order to deal nicely with bridges that send back our own packets, allow one address to be filtered, so
517 * that our own can be filtered out here. */
518
a1fb61b0 519 if (addr)
35778343 520 lldp_rx->filter_address = *addr;
a1fb61b0 521 else
35778343 522 zero(lldp_rx->filter_address);
b553a6b1 523
b553a6b1
LP
524 return 0;
525}