diff options
| author | Hauke Mehrtens | 2026-05-23 12:21:10 +0000 |
|---|---|---|
| committer | Hauke Mehrtens | 2026-06-18 00:35:05 +0000 |
| commit | 9c785500a99004b3500a244c2cc00b09909b6213 (patch) | |
| tree | 823bdedf4b188391647e5c1ed2d7f5455989c0b3 | |
| parent | 35536135685620cf51191e21ae3458ca1741d094 (diff) | |
| download | odhcpd-9c785500a99004b3500a244c2cc00b09909b6213.tar.gz | |
ndp: enforce RFC4861 §7.1.1 hop-limit and ICMP-code checks on NS
handle_solicit() previously trusted whatever the AF_PACKET socket
delivered, so a Neighbor Solicitation with Hop Limit < 255 or a
non-zero ICMP Code was forwarded to peer interfaces (triggering proxy
NDP lookups and an NA on the requesting interface). RFC4861 §7.1.1
requires NS messages with those properties to be silently discarded —
the hop-limit rule in particular is what prevents off-link attackers
from forging NS / DAD packets onto our link.
The router.c equivalent (router_icmpv6_valid + the IPV6_RECVHOPLIMIT
cmsg check in odhcpd_receive_packets) already does this for RA/RS;
the NDP path needs to enforce it itself because AF_PACKET sees raw
frames with no kernel-side L3 validation.
Assisted-by: Claude:claude-opus-4-7
Link: https://github.com/openwrt/odhcpd/pull/401
Signed-off-by: Hauke Mehrtens <hauke@hauke-m.de>
(cherry picked from commit 0eade3d4b7a37106f6498f4ce952ecd6ec1b932d)
| -rw-r--r-- | src/ndp.c | 9 |
1 files changed, 9 insertions, 0 deletions
@@ -382,6 +382,15 @@ static void handle_solicit(void *addr, void *data, size_t len, if (len < sizeof(*ip6) + sizeof(*req)) return; // Invalid total length + /* RFC4861 §7.1.1: a Neighbor Solicitation MUST be silently discarded + * if the IP Hop Limit field is not 255 or the ICMP Code is non-zero. + * The hop-limit check is what protects against off-link attackers + * forging NS / DAD messages, so it must not be skipped. Because the + * NDP socket is AF_PACKET (not a kernel-managed ICMPv6 socket), we + * have to enforce it ourselves. */ + if (ip6->ip6_hlim != 255 || req->nd_ns_hdr.icmp6_code != 0) + return; + if (IN6_IS_ADDR_LINKLOCAL(&req->nd_ns_target) || IN6_IS_ADDR_LOOPBACK(&req->nd_ns_target) || IN6_IS_ADDR_MULTICAST(&req->nd_ns_target)) |