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