1
0
mirror of https://github.com/systemd/systemd.git synced 2025-01-11 09:18:07 +03:00

Merge pull request #31373 from yuwata/network-neighbor-advertisement

network: add basic support of neighbor advertisement
This commit is contained in:
Yu Watanabe 2024-04-05 05:54:12 +09:00 committed by GitHub
commit 36ca167220
No known key found for this signature in database
GPG Key ID: B5690EEEBB952194
11 changed files with 514 additions and 9 deletions

View File

@ -42,6 +42,7 @@ int icmp6_bind(int ifindex, bool is_router) {
.ipv6mr_interface = ifindex,
};
ICMP6_FILTER_SETPASS(ND_ROUTER_ADVERT, &filter);
ICMP6_FILTER_SETPASS(ND_NEIGHBOR_ADVERT, &filter);
}
s = socket(AF_INET6, SOCK_RAW | SOCK_CLOEXEC | SOCK_NONBLOCK, IPPROTO_ICMPV6);

View File

@ -28,6 +28,7 @@ sources = files(
'sd-lldp-rx.c',
'sd-lldp-tx.c',
'sd-ndisc.c',
'sd-ndisc-neighbor.c',
'sd-ndisc-router.c',
'sd-radv.c',
)

View File

@ -0,0 +1,21 @@
/* SPDX-License-Identifier: LGPL-2.1-or-later */
#pragma once
#include "sd-ndisc.h"
#include "icmp6-packet.h"
#include "set.h"
struct sd_ndisc_neighbor {
unsigned n_ref;
ICMP6Packet *packet;
uint32_t flags;
struct in6_addr target_address;
Set *options;
};
sd_ndisc_neighbor* ndisc_neighbor_new(ICMP6Packet *packet);
int ndisc_neighbor_parse(sd_ndisc *nd, sd_ndisc_neighbor *na);

View File

@ -0,0 +1,126 @@
/* SPDX-License-Identifier: LGPL-2.1-or-later */
#include <netinet/icmp6.h>
#include "sd-ndisc.h"
#include "alloc-util.h"
#include "in-addr-util.h"
#include "ndisc-internal.h"
#include "ndisc-neighbor-internal.h"
#include "ndisc-option.h"
static sd_ndisc_neighbor* ndisc_neighbor_free(sd_ndisc_neighbor *na) {
if (!na)
return NULL;
icmp6_packet_unref(na->packet);
set_free(na->options);
return mfree(na);
}
DEFINE_PUBLIC_TRIVIAL_REF_UNREF_FUNC(sd_ndisc_neighbor, sd_ndisc_neighbor, ndisc_neighbor_free);
sd_ndisc_neighbor* ndisc_neighbor_new(ICMP6Packet *packet) {
sd_ndisc_neighbor *na;
assert(packet);
na = new(sd_ndisc_neighbor, 1);
if (!na)
return NULL;
*na = (sd_ndisc_neighbor) {
.n_ref = 1,
.packet = icmp6_packet_ref(packet),
};
return na;
}
int ndisc_neighbor_parse(sd_ndisc *nd, sd_ndisc_neighbor *na) {
int r;
assert(na);
if (na->packet->raw_size < sizeof(struct nd_neighbor_advert))
return log_ndisc_errno(nd, SYNTHETIC_ERRNO(EBADMSG),
"Too small to be a neighbor advertisement, ignoring datagram.");
/* Neighbor advertisement packets are neatly aligned to 64-bit boundaries, hence we can access them directly */
const struct nd_neighbor_advert *a = (const struct nd_neighbor_advert*) na->packet->raw_packet;
assert(a->nd_na_type == ND_NEIGHBOR_ADVERT);
assert(a->nd_na_code == 0);
na->flags = a->nd_na_flags_reserved; /* the first 3 bits */
na->target_address = a->nd_na_target;
/* RFC 4861 section 4.4:
* For solicited advertisements, the Target Address field in the Neighbor Solicitation message that
* prompted this advertisement. For an unsolicited advertisement, the address whose link-layer
* address has changed. The Target Address MUST NOT be a multicast address.
*
* Here, we only check if the target address is a link-layer address (or a null address, for safety)
* when the message is an unsolicited neighbor advertisement. */
if (!FLAGS_SET(na->flags, ND_NA_FLAG_SOLICITED))
if (!in6_addr_is_link_local(&na->target_address) && !in6_addr_is_null(&na->target_address))
return log_ndisc_errno(nd, SYNTHETIC_ERRNO(EBADMSG),
"Received ND packet with an invalid target address (%s), ignoring datagram.",
IN6_ADDR_TO_STRING(&na->target_address));
r = ndisc_parse_options(na->packet, &na->options);
if (r < 0)
return log_ndisc_errno(nd, r, "Failed to parse NDisc options in neighbor advertisement message, ignoring: %m");
return 0;
}
int sd_ndisc_neighbor_get_sender_address(sd_ndisc_neighbor *na, struct in6_addr *ret) {
assert_return(na, -EINVAL);
return icmp6_packet_get_sender_address(na->packet, ret);
}
int sd_ndisc_neighbor_get_target_address(sd_ndisc_neighbor *na, struct in6_addr *ret) {
assert_return(na, -EINVAL);
if (in6_addr_is_null(&na->target_address))
/* fall back to the sender address, for safety. */
return sd_ndisc_neighbor_get_sender_address(na, ret);
if (ret)
*ret = na->target_address;
return 0;
}
int sd_ndisc_neighbor_get_target_mac(sd_ndisc_neighbor *na, struct ether_addr *ret) {
assert_return(na, -EINVAL);
return ndisc_option_get_mac(na->options, SD_NDISC_OPTION_TARGET_LL_ADDRESS, ret);
}
int sd_ndisc_neighbor_get_flags(sd_ndisc_neighbor *na, uint32_t *ret) {
assert_return(na, -EINVAL);
if (ret)
*ret = na->flags;
return 0;
}
int sd_ndisc_neighbor_is_router(sd_ndisc_neighbor *na) {
assert_return(na, -EINVAL);
return FLAGS_SET(na->flags, ND_NA_FLAG_ROUTER);
}
int sd_ndisc_neighbor_is_solicited(sd_ndisc_neighbor *na) {
assert_return(na, -EINVAL);
return FLAGS_SET(na->flags, ND_NA_FLAG_SOLICITED);
}
int sd_ndisc_neighbor_is_override(sd_ndisc_neighbor *na) {
assert_return(na, -EINVAL);
return FLAGS_SET(na->flags, ND_NA_FLAG_OVERRIDE);
}

View File

@ -16,6 +16,7 @@
#include "in-addr-util.h"
#include "memory-util.h"
#include "ndisc-internal.h"
#include "ndisc-neighbor-internal.h"
#include "ndisc-router-internal.h"
#include "network-common.h"
#include "random-util.h"
@ -26,8 +27,9 @@
#define NDISC_TIMEOUT_NO_RA_USEC (NDISC_ROUTER_SOLICITATION_INTERVAL * NDISC_MAX_ROUTER_SOLICITATIONS)
static const char * const ndisc_event_table[_SD_NDISC_EVENT_MAX] = {
[SD_NDISC_EVENT_TIMEOUT] = "timeout",
[SD_NDISC_EVENT_ROUTER] = "router",
[SD_NDISC_EVENT_TIMEOUT] = "timeout",
[SD_NDISC_EVENT_ROUTER] = "router",
[SD_NDISC_EVENT_NEIGHBOR] = "neighbor",
};
DEFINE_STRING_TABLE_LOOKUP(ndisc_event, sd_ndisc_event_t);
@ -225,6 +227,36 @@ static int ndisc_handle_router(sd_ndisc *nd, ICMP6Packet *packet) {
return 0;
}
static int ndisc_handle_neighbor(sd_ndisc *nd, ICMP6Packet *packet) {
_cleanup_(sd_ndisc_neighbor_unrefp) sd_ndisc_neighbor *na = NULL;
struct in6_addr a;
int r;
assert(nd);
assert(packet);
na = ndisc_neighbor_new(packet);
if (!na)
return -ENOMEM;
r = ndisc_neighbor_parse(nd, na);
if (r < 0)
return r;
r = sd_ndisc_neighbor_get_sender_address(na, &a);
if (r < 0)
return r;
log_ndisc(nd, "Received Neighbor Advertisement from %s: Router=%s, Solicited=%s, Override=%s",
IN6_ADDR_TO_STRING(&a),
yes_no(sd_ndisc_neighbor_is_router(na) > 0),
yes_no(sd_ndisc_neighbor_is_solicited(na) > 0),
yes_no(sd_ndisc_neighbor_is_override(na) > 0));
ndisc_callback(nd, SD_NDISC_EVENT_NEIGHBOR, na);
return 0;
}
static int ndisc_recv(sd_event_source *s, int fd, uint32_t revents, void *userdata) {
_cleanup_(icmp6_packet_unrefp) ICMP6Packet *packet = NULL;
sd_ndisc *nd = ASSERT_PTR(userdata);
@ -262,6 +294,10 @@ static int ndisc_recv(sd_event_source *s, int fd, uint32_t revents, void *userda
(void) ndisc_handle_router(nd, packet);
break;
case ND_NEIGHBOR_ADVERT:
(void) ndisc_handle_neighbor(nd, packet);
break;
default:
log_ndisc(nd, "Received an ICMPv6 packet with unexpected type %i, ignoring.", r);
}

View File

@ -195,7 +195,7 @@ static int send_ra(uint8_t flags) {
return 0;
}
static void test_callback(sd_ndisc *nd, sd_ndisc_event_t event, void *message, void *userdata) {
static void test_callback_ra(sd_ndisc *nd, sd_ndisc_event_t event, void *message, void *userdata) {
sd_event *e = userdata;
static unsigned idx = 0;
uint64_t flags_array[] = {
@ -253,7 +253,7 @@ TEST(rs) {
assert_se(sd_ndisc_set_ifindex(nd, 42) >= 0);
assert_se(sd_ndisc_set_mac(nd, &mac_addr) >= 0);
assert_se(sd_ndisc_set_callback(nd, test_callback, e) >= 0);
assert_se(sd_ndisc_set_callback(nd, test_callback_ra, e) >= 0);
assert_se(sd_event_add_time_relative(e, NULL, CLOCK_BOOTTIME,
30 * USEC_PER_SEC, 0,
@ -342,7 +342,7 @@ TEST(invalid_domain) {
assert_se(sd_ndisc_set_ifindex(nd, 42) >= 0);
assert_se(sd_ndisc_set_mac(nd, &mac_addr) >= 0);
assert_se(sd_ndisc_set_callback(nd, test_callback, e) >= 0);
assert_se(sd_ndisc_set_callback(nd, test_callback_ra, e) >= 0);
assert_se(sd_event_add_time_relative(e, NULL, CLOCK_BOOTTIME,
30 * USEC_PER_SEC, 0,
@ -358,6 +358,120 @@ TEST(invalid_domain) {
test_fd[1] = -EBADF;
}
static void neighbor_dump(sd_ndisc_neighbor *na) {
struct in6_addr addr;
uint32_t flags;
assert_se(na);
log_info("--");
assert_se(sd_ndisc_neighbor_get_sender_address(na, &addr) >= 0);
log_info("Sender: %s", IN6_ADDR_TO_STRING(&addr));
assert_se(sd_ndisc_neighbor_get_flags(na, &flags) >= 0);
log_info("Flags: Router:%s, Solicited:%s, Override: %s",
yes_no(flags & ND_NA_FLAG_ROUTER),
yes_no(flags & ND_NA_FLAG_SOLICITED),
yes_no(flags & ND_NA_FLAG_OVERRIDE));
assert_se(sd_ndisc_neighbor_is_router(na) == FLAGS_SET(flags, ND_NA_FLAG_ROUTER));
assert_se(sd_ndisc_neighbor_is_solicited(na) == FLAGS_SET(flags, ND_NA_FLAG_SOLICITED));
assert_se(sd_ndisc_neighbor_is_override(na) == FLAGS_SET(flags, ND_NA_FLAG_OVERRIDE));
}
static int send_na(uint32_t flags) {
uint8_t advertisement[] = {
/* struct nd_neighbor_advert */
0x88, 0x00, 0xde, 0x83, 0x00, 0x00, 0x00, 0x00,
0xfe, 0x80, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00,
0xfe, 0x80, 0x00, 0x00, 0x00, 0x00, 0x00, 0x01,
/* type = 0x02 (SD_NDISC_OPTION_TARGET_LL_ADDRESS), length = 8 */
0x01, 0x01, 'A', 'B', 'C', '1', '2', '3',
};
((struct nd_neighbor_advert*) advertisement)->nd_na_flags_reserved = flags;
assert_se(write(test_fd[1], advertisement, sizeof(advertisement)) == sizeof(advertisement));
if (verbose)
printf(" sent NA with flag 0x%02x\n", flags);
return 0;
}
static void test_callback_na(sd_ndisc *nd, sd_ndisc_event_t event, void *message, void *userdata) {
sd_event *e = userdata;
static unsigned idx = 0;
uint32_t flags_array[] = {
0,
0,
ND_NA_FLAG_ROUTER,
ND_NA_FLAG_SOLICITED,
ND_NA_FLAG_SOLICITED | ND_NA_FLAG_OVERRIDE,
};
uint32_t flags;
assert_se(nd);
if (event != SD_NDISC_EVENT_NEIGHBOR)
return;
sd_ndisc_neighbor *rt = ASSERT_PTR(message);
neighbor_dump(rt);
assert_se(sd_ndisc_neighbor_get_flags(rt, &flags) >= 0);
assert_se(flags == flags_array[idx]);
idx++;
if (verbose)
printf(" got event 0x%02" PRIx32 "\n", flags);
if (idx < ELEMENTSOF(flags_array)) {
send_na(flags_array[idx]);
return;
}
idx = 0;
sd_event_exit(e, 0);
}
static int on_recv_rs_na(sd_event_source *s, int fd, uint32_t revents, void *userdata) {
_cleanup_(icmp6_packet_unrefp) ICMP6Packet *packet = NULL;
assert_se(icmp6_packet_receive(fd, &packet) >= 0);
return send_na(0);
}
TEST(na) {
_cleanup_(sd_event_unrefp) sd_event *e = NULL;
_cleanup_(sd_event_source_unrefp) sd_event_source *s = NULL;
_cleanup_(sd_ndisc_unrefp) sd_ndisc *nd = NULL;
assert_se(sd_event_new(&e) >= 0);
assert_se(sd_ndisc_new(&nd) >= 0);
assert_se(nd);
assert_se(sd_ndisc_attach_event(nd, e, 0) >= 0);
assert_se(sd_ndisc_set_ifindex(nd, 42) >= 0);
assert_se(sd_ndisc_set_mac(nd, &mac_addr) >= 0);
assert_se(sd_ndisc_set_callback(nd, test_callback_na, e) >= 0);
assert_se(sd_event_add_time_relative(e, NULL, CLOCK_BOOTTIME,
30 * USEC_PER_SEC, 0,
NULL, INT_TO_PTR(-ETIMEDOUT)) >= 0);
assert_se(sd_ndisc_start(nd) >= 0);
assert_se(sd_event_add_io(e, &s, test_fd[1], EPOLLIN, on_recv_rs_na, nd) >= 0);
assert_se(sd_event_source_set_io_fd_own(s, true) >= 0);
assert_se(sd_event_loop(e) >= 0);
test_fd[1] = -EBADF;
}
static int on_recv_rs_timeout(sd_event_source *s, int fd, uint32_t revents, void *userdata) {
_cleanup_(icmp6_packet_unrefp) ICMP6Packet *packet = NULL;
sd_ndisc *nd = ASSERT_PTR(userdata);

View File

@ -1391,7 +1391,7 @@ static int ndisc_router_process_options(Link *link, sd_ndisc_router *rt) {
}
}
static int ndisc_drop_outdated(Link *link, usec_t timestamp_usec) {
static int ndisc_drop_outdated(Link *link, const struct in6_addr *router, usec_t timestamp_usec) {
bool updated = false;
NDiscDNSSL *dnssl;
NDiscRDNSS *rdnss;
@ -1420,6 +1420,9 @@ static int ndisc_drop_outdated(Link *link, usec_t timestamp_usec) {
if (route->lifetime_usec > timestamp_usec)
continue; /* the route is still valid */
if (router && !in6_addr_equal(&route->provider.in6, router))
continue;
r = route_remove_and_cancel(route, link->manager);
if (r < 0)
RET_GATHER(ret, log_link_warning_errno(link, r, "Failed to remove outdated SLAAC route, ignoring: %m"));
@ -1432,6 +1435,9 @@ static int ndisc_drop_outdated(Link *link, usec_t timestamp_usec) {
if (address->lifetime_valid_usec > timestamp_usec)
continue; /* the address is still valid */
if (router && !in6_addr_equal(&address->provider.in6, router))
continue;
r = address_remove_and_cancel(address, link);
if (r < 0)
RET_GATHER(ret, log_link_warning_errno(link, r, "Failed to remove outdated SLAAC address, ignoring: %m"));
@ -1441,6 +1447,9 @@ static int ndisc_drop_outdated(Link *link, usec_t timestamp_usec) {
if (rdnss->lifetime_usec > timestamp_usec)
continue; /* the DNS server is still valid */
if (router && !in6_addr_equal(&rdnss->router, router))
continue;
free(set_remove(link->ndisc_rdnss, rdnss));
updated = true;
}
@ -1449,6 +1458,9 @@ static int ndisc_drop_outdated(Link *link, usec_t timestamp_usec) {
if (dnssl->lifetime_usec > timestamp_usec)
continue; /* the DNS domain is still valid */
if (router && !in6_addr_equal(&dnssl->router, router))
continue;
free(set_remove(link->ndisc_dnssl, dnssl));
updated = true;
}
@ -1457,6 +1469,9 @@ static int ndisc_drop_outdated(Link *link, usec_t timestamp_usec) {
if (cp->lifetime_usec > timestamp_usec)
continue; /* the captive portal is still valid */
if (router && !in6_addr_equal(&cp->router, router))
continue;
ndisc_captive_portal_free(set_remove(link->ndisc_captive_portals, cp));
updated = true;
}
@ -1465,6 +1480,9 @@ static int ndisc_drop_outdated(Link *link, usec_t timestamp_usec) {
if (p64->lifetime_usec > timestamp_usec)
continue; /* the pref64 prefix is still valid */
if (router && !in6_addr_equal(&p64->router, router))
continue;
free(set_remove(link->ndisc_pref64, p64));
/* The pref64 prefix is not exported through the state file, hence it is not necessary to set
* the 'updated' flag. */
@ -1486,7 +1504,7 @@ static int ndisc_expire_handler(sd_event_source *s, uint64_t usec, void *userdat
assert_se(sd_event_now(link->manager->event, CLOCK_BOOTTIME, &now_usec) >= 0);
(void) ndisc_drop_outdated(link, now_usec);
(void) ndisc_drop_outdated(link, /* router = */ NULL, now_usec);
(void) ndisc_setup_expire(link);
return 0;
}
@ -1635,7 +1653,7 @@ static int ndisc_router_handler(Link *link, sd_ndisc_router *rt) {
if (r < 0)
return r;
r = ndisc_drop_outdated(link, timestamp_usec);
r = ndisc_drop_outdated(link, /* router = */ NULL, timestamp_usec);
if (r < 0)
return r;
@ -1679,6 +1697,131 @@ static int ndisc_router_handler(Link *link, sd_ndisc_router *rt) {
return 0;
}
static int ndisc_neighbor_handle_non_router_message(Link *link, sd_ndisc_neighbor *na) {
struct in6_addr address;
int r;
assert(link);
assert(na);
/* Received Neighbor Advertisement message without Router flag. The node might have been a router,
* and now it is not. Let's drop all configurations based on RAs sent from the node. */
r = sd_ndisc_neighbor_get_target_address(na, &address);
if (r == -ENODATA)
return 0;
if (r < 0)
return r;
(void) ndisc_drop_outdated(link, /* router = */ &address, /* timestamp_usec = */ USEC_INFINITY);
return 0;
}
static int ndisc_neighbor_handle_router_message(Link *link, sd_ndisc_neighbor *na) {
struct in6_addr current_address, original_address;
int r;
assert(link);
assert(link->manager);
assert(na);
/* Received Neighbor Advertisement message with Router flag. If the router address is changed, update
* the provider field of configurations. */
r = sd_ndisc_neighbor_get_sender_address(na, &current_address);
if (r == -ENODATA)
return 0;
if (r < 0)
return r;
r = sd_ndisc_neighbor_get_target_address(na, &original_address);
if (r == -ENODATA)
return 0;
if (r < 0)
return r;
if (in6_addr_equal(&current_address, &original_address))
return 0; /* the router address is not changed */
Route *route;
SET_FOREACH(route, link->manager->routes) {
if (route->source != NETWORK_CONFIG_SOURCE_NDISC)
continue;
if (route->nexthop.ifindex != link->ifindex)
continue;
if (!in6_addr_equal(&route->provider.in6, &original_address))
continue;
route->provider.in6 = current_address;
}
Address *address;
SET_FOREACH(address, link->addresses) {
if (address->source != NETWORK_CONFIG_SOURCE_NDISC)
continue;
if (!in6_addr_equal(&address->provider.in6, &original_address))
continue;
address->provider.in6 = current_address;
}
NDiscRDNSS *rdnss;
SET_FOREACH(rdnss, link->ndisc_rdnss) {
if (!in6_addr_equal(&rdnss->router, &original_address))
continue;
rdnss->router = current_address;
}
NDiscDNSSL *dnssl;
SET_FOREACH(dnssl, link->ndisc_dnssl) {
if (!in6_addr_equal(&dnssl->router, &original_address))
continue;
dnssl->router = current_address;
}
NDiscCaptivePortal *cp;
SET_FOREACH(cp, link->ndisc_captive_portals) {
if (!in6_addr_equal(&cp->router, &original_address))
continue;
cp->router = current_address;
}
NDiscPREF64 *p64;
SET_FOREACH(p64, link->ndisc_pref64) {
if (!in6_addr_equal(&p64->router, &original_address))
continue;
p64->router = current_address;
}
return 0;
}
static int ndisc_neighbor_handler(Link *link, sd_ndisc_neighbor *na) {
int r;
assert(link);
assert(na);
r = sd_ndisc_neighbor_is_router(na);
if (r < 0)
return r;
if (r == 0)
r = ndisc_neighbor_handle_non_router_message(link, na);
else
r = ndisc_neighbor_handle_router_message(link, na);
if (r < 0)
return r;
return 0;
}
static void ndisc_handler(sd_ndisc *nd, sd_ndisc_event_t event, void *message, void *userdata) {
Link *link = ASSERT_PTR(userdata);
int r;
@ -1696,6 +1839,14 @@ static void ndisc_handler(sd_ndisc *nd, sd_ndisc_event_t event, void *message, v
}
break;
case SD_NDISC_EVENT_NEIGHBOR:
r = ndisc_neighbor_handler(link, ASSERT_PTR(message));
if (r < 0 && r != -EBADMSG) {
link_enter_failed(link);
return;
}
break;
case SD_NDISC_EVENT_TIMEOUT:
log_link_debug(link, "NDisc handler get timeout event");
if (link->ndisc_messages == 0) {
@ -1825,7 +1976,7 @@ void ndisc_flush(Link *link) {
assert(link);
/* Remove all addresses, routes, RDNSS, DNSSL, and Captive Portal entries, without exception. */
(void) ndisc_drop_outdated(link, /* timestamp_usec = */ USEC_INFINITY);
(void) ndisc_drop_outdated(link, /* router = */ NULL, /* timestamp_usec = */ USEC_INFINITY);
link->ndisc_rdnss = set_free(link->ndisc_rdnss);
link->ndisc_dnssl = set_free(link->ndisc_dnssl);

View File

@ -38,6 +38,7 @@ _not_installed_headers = [
'sd-lldp-tx.h',
'sd-lldp.h',
'sd-ndisc.h',
'sd-ndisc-neighbor.h',
'sd-ndisc-protocol.h',
'sd-ndisc-router.h',
'sd-netlink.h',

View File

@ -0,0 +1,50 @@
/* SPDX-License-Identifier: LGPL-2.1-or-later */
#ifndef foosdndiscneighborfoo
#define foosdndiscneighborfoo
/***
systemd is free software; you can redistribute it and/or modify it
under the terms of the GNU Lesser General Public License as published by
the Free Software Foundation; either version 2.1 of the License, or
(at your option) any later version.
systemd is distributed in the hope that it will be useful, but
WITHOUT ANY WARRANTY; without even the implied warranty of
MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the GNU
Lesser General Public License for more details.
You should have received a copy of the GNU Lesser General Public License
along with systemd; If not, see <https://www.gnu.org/licenses/>.
***/
#include <inttypes.h>
#include <net/ethernet.h>
#include <netinet/in.h>
#include <sys/types.h>
#include <time.h>
#include "_sd-common.h"
_SD_BEGIN_DECLARATIONS;
typedef struct sd_ndisc_neighbor sd_ndisc_neighbor;
sd_ndisc_neighbor *sd_ndisc_neighbor_ref(sd_ndisc_neighbor *na);
sd_ndisc_neighbor *sd_ndisc_neighbor_unref(sd_ndisc_neighbor *na);
_SD_DEFINE_POINTER_CLEANUP_FUNC(sd_ndisc_neighbor, sd_ndisc_neighbor_unref);
int sd_ndisc_neighbor_get_sender_address(sd_ndisc_neighbor *na, struct in6_addr *ret);
/* RFC 4861 section 4.4:
* For solicited advertisements, the Target Address field in the Neighbor Solicitation message that prompted
* this advertisement. For an unsolicited advertisement, the address whose link-layer address has changed.
* The Target Address MUST NOT be a multicast address. */
int sd_ndisc_neighbor_get_target_address(sd_ndisc_neighbor *na, struct in6_addr *ret);
int sd_ndisc_neighbor_get_target_mac(sd_ndisc_neighbor *na, struct ether_addr *ret);
int sd_ndisc_neighbor_get_flags(sd_ndisc_neighbor *na, uint32_t *ret);
int sd_ndisc_neighbor_is_router(sd_ndisc_neighbor *na);
int sd_ndisc_neighbor_is_solicited(sd_ndisc_neighbor *na);
int sd_ndisc_neighbor_is_override(sd_ndisc_neighbor *na);
_SD_END_DECLARATIONS;
#endif

View File

@ -26,6 +26,7 @@
#include <sys/types.h>
#include "sd-event.h"
#include "sd-ndisc-neighbor.h"
#include "sd-ndisc-protocol.h"
#include "sd-ndisc-router.h"
@ -38,6 +39,7 @@ typedef struct sd_ndisc sd_ndisc;
__extension__ typedef enum sd_ndisc_event_t {
SD_NDISC_EVENT_TIMEOUT,
SD_NDISC_EVENT_ROUTER,
SD_NDISC_EVENT_NEIGHBOR,
_SD_NDISC_EVENT_MAX,
_SD_NDISC_EVENT_INVALID = -EINVAL,
_SD_ENUM_FORCE_S64(NDISC_EVENT)

View File

@ -6,3 +6,5 @@ Name=veth-peer
IPv6AcceptRA=no
Address=2600::1/0
Address=192.168.5.1/24
# To make the kernel send NA with IsRouter flag.
IPv6Forwarding=yes