diff --git a/src/libsystemd-network/meson.build b/src/libsystemd-network/meson.build index 409c893458..fdd8806a23 100644 --- a/src/libsystemd-network/meson.build +++ b/src/libsystemd-network/meson.build @@ -31,6 +31,7 @@ sources = files( 'sd-ndisc-neighbor.c', 'sd-ndisc-redirect.c', 'sd-ndisc-router.c', + 'sd-ndisc-router-solicit.c', 'sd-radv.c', ) diff --git a/src/libsystemd-network/ndisc-router-solicit-internal.h b/src/libsystemd-network/ndisc-router-solicit-internal.h new file mode 100644 index 0000000000..6f0b0af13a --- /dev/null +++ b/src/libsystemd-network/ndisc-router-solicit-internal.h @@ -0,0 +1,18 @@ +/* SPDX-License-Identifier: LGPL-2.1-or-later */ +#pragma once + +#include "sd-radv.h" + +#include "icmp6-packet.h" +#include "set.h" + +struct sd_ndisc_router_solicit { + unsigned n_ref; + + ICMP6Packet *packet; + + Set *options; +}; + +sd_ndisc_router_solicit* ndisc_router_solicit_new(ICMP6Packet *packet); +int ndisc_router_solicit_parse(sd_radv *ra, sd_ndisc_router_solicit *rs); diff --git a/src/libsystemd-network/sd-ndisc-router-solicit.c b/src/libsystemd-network/sd-ndisc-router-solicit.c new file mode 100644 index 0000000000..04e7c26c34 --- /dev/null +++ b/src/libsystemd-network/sd-ndisc-router-solicit.c @@ -0,0 +1,83 @@ +/* SPDX-License-Identifier: LGPL-2.1-or-later */ + +#include + +#include "sd-radv.h" + +#include "alloc-util.h" +#include "in-addr-util.h" +#include "ndisc-option.h" +#include "ndisc-router-solicit-internal.h" +#include "radv-internal.h" + +static sd_ndisc_router_solicit* ndisc_router_solicit_free(sd_ndisc_router_solicit *rs) { + if (!rs) + return NULL; + + icmp6_packet_unref(rs->packet); + set_free(rs->options); + return mfree(rs); +} + +DEFINE_PUBLIC_TRIVIAL_REF_UNREF_FUNC(sd_ndisc_router_solicit, sd_ndisc_router_solicit, ndisc_router_solicit_free); + +sd_ndisc_router_solicit* ndisc_router_solicit_new(ICMP6Packet *packet) { + sd_ndisc_router_solicit *rs; + + assert(packet); + + rs = new(sd_ndisc_router_solicit, 1); + if (!rs) + return NULL; + + *rs = (sd_ndisc_router_solicit) { + .n_ref = 1, + .packet = icmp6_packet_ref(packet), + }; + + return rs; +} + +int ndisc_router_solicit_parse(sd_radv *ra, sd_ndisc_router_solicit *rs) { + int r; + + assert(rs); + assert(rs->packet); + + if (rs->packet->raw_size < sizeof(struct nd_router_solicit)) + return log_radv_errno(ra, SYNTHETIC_ERRNO(EBADMSG), + "Too small to be a router solicit, ignoring."); + + const struct nd_router_solicit *a = (const struct nd_router_solicit*) rs->packet->raw_packet; + assert(a); + assert(a->nd_rs_type == ND_ROUTER_SOLICIT); + assert(a->nd_rs_code == 0); + + r = ndisc_parse_options(rs->packet, &rs->options); + if (r < 0) + return log_radv_errno(ra, r, "Failed to parse NDisc options in router solicit, ignoring datagram: %m"); + + /* RFC 4861 section 4.1. + * Source link-layer address: + * The link-layer address of the sender, if known. MUST NOT be included if the Source + * Address is the unspecified address. Otherwise, it SHOULD be included on link + * layers that have addresses. */ + if (ndisc_option_get_mac(rs->options, SD_NDISC_OPTION_SOURCE_LL_ADDRESS, NULL) >= 0&& + sd_ndisc_router_solicit_get_sender_address(rs, NULL) == -ENODATA) + return log_radv_errno(ra, SYNTHETIC_ERRNO(EBADMSG), + "Router Solicitation message from null address unexpectedly contains source link-layer address option, ignoring datagaram."); + + return 0; +} + +int sd_ndisc_router_solicit_get_sender_address(sd_ndisc_router_solicit *rs, struct in6_addr *ret) { + assert_return(rs, -EINVAL); + + return icmp6_packet_get_sender_address(rs->packet, ret); +} + +int sd_ndisc_router_solicit_get_sender_mac(sd_ndisc_router_solicit *rs, struct ether_addr *ret) { + assert_return(rs, -EINVAL); + + return ndisc_option_get_mac(rs->options, SD_NDISC_OPTION_SOURCE_LL_ADDRESS, ret); +} diff --git a/src/libsystemd-network/sd-radv.c b/src/libsystemd-network/sd-radv.c index 316fcf1d2b..79210f9e2c 100644 --- a/src/libsystemd-network/sd-radv.c +++ b/src/libsystemd-network/sd-radv.c @@ -19,6 +19,7 @@ #include "iovec-util.h" #include "macro.h" #include "memory-util.h" +#include "ndisc-router-solicit-internal.h" #include "network-common.h" #include "radv-internal.h" #include "random-util.h" @@ -237,64 +238,51 @@ static int radv_send_router(sd_radv *ra, const struct in6_addr *dst, usec_t life return 0; } -static int radv_recv(sd_event_source *s, int fd, uint32_t revents, void *userdata) { - sd_radv *ra = ASSERT_PTR(userdata); - struct in6_addr src; - triple_timestamp timestamp; +static int radv_process_packet(sd_radv *ra, ICMP6Packet *packet) { int r; - assert(s); - assert(ra->event); + assert(ra); + assert(packet); - ssize_t buflen = next_datagram_size_fd(fd); - if (ERRNO_IS_NEG_TRANSIENT(buflen) || ERRNO_IS_NEG_DISCONNECT(buflen)) - return 0; - if (buflen < 0) { - log_radv_errno(ra, buflen, "Failed to determine datagram size to read, ignoring: %m"); - return 0; - } + if (icmp6_packet_get_type(packet) != ND_ROUTER_SOLICIT) + return log_radv_errno(ra, SYNTHETIC_ERRNO(EBADMSG), "Received ICMP6 packet with unexpected type, ignoring."); - _cleanup_free_ char *buf = new0(char, buflen); - if (!buf) - return -ENOMEM; + _cleanup_(sd_ndisc_router_solicit_unrefp) sd_ndisc_router_solicit *rs = NULL; + rs = ndisc_router_solicit_new(packet); + if (!rs) + return log_oom_debug(); - r = icmp6_receive(fd, buf, buflen, &src, ×tamp); - if (ERRNO_IS_NEG_TRANSIENT(r) || ERRNO_IS_NEG_DISCONNECT(r)) - return 0; + r = ndisc_router_solicit_parse(ra, rs); if (r < 0) - switch (r) { - case -EADDRNOTAVAIL: - log_radv(ra, "Received RS from neither link-local nor null address, ignoring."); - return 0; + return r; - case -EMULTIHOP: - log_radv(ra, "Received RS with invalid hop limit, ignoring."); - return 0; + struct in6_addr src = {}; + r = sd_ndisc_router_solicit_get_sender_address(rs, &src); + if (r < 0 && r != -ENODATA) /* null address is allowed */ + return log_radv_errno(ra, r, "Failed to get sender address of RS, ignoring: %m"); - case -EPFNOSUPPORT: - log_radv(ra, "Received invalid source address from ICMPv6 socket, ignoring."); - return 0; - - default: - log_radv_errno(ra, r, "Unexpected error receiving from ICMPv6 socket, ignoring: %m"); - return 0; - } - - if ((size_t) buflen < sizeof(struct nd_router_solicit)) { - log_radv(ra, "Too short packet received, ignoring"); - return 0; - } - - /* TODO: if the sender address is null, check that the message does not have the source link-layer - * address option. See RFC 4861 Section 6.1.1. */ - - const char *addr = IN6_ADDR_TO_STRING(&src); r = radv_send_router(ra, &src, ra->lifetime_usec); if (r < 0) - log_radv_errno(ra, r, "Unable to send solicited Router Advertisement to %s, ignoring: %m", addr); - else - log_radv(ra, "Sent solicited Router Advertisement to %s.", addr); + return log_radv_errno(ra, r, "Unable to send solicited Router Advertisement to %s, ignoring: %m", IN6_ADDR_TO_STRING(&src)); + log_radv(ra, "Sent solicited Router Advertisement to %s.", IN6_ADDR_TO_STRING(&src)); + return 0; +} + +static int radv_recv(sd_event_source *s, int fd, uint32_t revents, void *userdata) { + _cleanup_(icmp6_packet_unrefp) ICMP6Packet *packet = NULL; + sd_radv *ra = ASSERT_PTR(userdata); + int r; + + assert(fd >= 0); + + r = icmp6_packet_receive(fd, &packet); + if (r < 0) { + log_radv_errno(ra, r, "Failed to receive ICMPv6 packet, ignoring: %m"); + return 0; + } + + (void) radv_process_packet(ra, packet); return 0; } diff --git a/src/systemd/meson.build b/src/systemd/meson.build index e188bb99b8..de58bff334 100644 --- a/src/systemd/meson.build +++ b/src/systemd/meson.build @@ -42,6 +42,7 @@ _not_installed_headers = [ 'sd-ndisc-protocol.h', 'sd-ndisc-redirect.h', 'sd-ndisc-router.h', + 'sd-ndisc-router-solicit.h', 'sd-netlink.h', 'sd-network.h', 'sd-radv.h', diff --git a/src/systemd/sd-ndisc-router-solicit.h b/src/systemd/sd-ndisc-router-solicit.h new file mode 100644 index 0000000000..ff8c903af8 --- /dev/null +++ b/src/systemd/sd-ndisc-router-solicit.h @@ -0,0 +1,40 @@ +/* SPDX-License-Identifier: LGPL-2.1-or-later */ +#ifndef foosdndiscroutersolicitfoo +#define foosdndiscroutersolicitfoo + +/*** + 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 . +***/ + +#include +#include +#include +#include + +#include "_sd-common.h" + +_SD_BEGIN_DECLARATIONS; + +typedef struct sd_ndisc_router_solicit sd_ndisc_router_solicit; + +sd_ndisc_router_solicit *sd_ndisc_router_solicit_ref(sd_ndisc_router_solicit *rs); +sd_ndisc_router_solicit *sd_ndisc_router_solicit_unref(sd_ndisc_router_solicit *rs); +_SD_DEFINE_POINTER_CLEANUP_FUNC(sd_ndisc_router_solicit, sd_ndisc_router_solicit_unref); + +int sd_ndisc_router_solicit_get_sender_address(sd_ndisc_router_solicit *rs, struct in6_addr *ret); +int sd_ndisc_router_solicit_get_sender_mac(sd_ndisc_router_solicit *rs, struct ether_addr *ret); + +_SD_END_DECLARATIONS; + +#endif diff --git a/src/systemd/sd-radv.h b/src/systemd/sd-radv.h index 8ea0838ee6..79cbb89751 100644 --- a/src/systemd/sd-radv.h +++ b/src/systemd/sd-radv.h @@ -26,7 +26,8 @@ #include "_sd-common.h" #include "sd-event.h" -#include "sd-ndisc.h" +#include "sd-ndisc-protocol.h" +#include "sd-ndisc-router-solicit.h" _SD_BEGIN_DECLARATIONS;