git: 0a35e0afef28 - main - pf: allow unspecified addressed for certain MLD messages
- Go to: [ bottom of page ] [ top of archives ] [ this month ]
Date: Mon, 07 Sep 2026 08:38:08 UTC
The branch main has been updated by kp:
URL: https://cgit.FreeBSD.org/src/commit/?id=0a35e0afef2860a0a96e30e682f6782a323a82a4
commit 0a35e0afef2860a0a96e30e682f6782a323a82a4
Author: Kristof Provost <kp@FreeBSD.org>
AuthorDate: 2026-09-02 17:03:44 +0000
Commit: Kristof Provost <kp@FreeBSD.org>
CommitDate: 2026-09-07 08:37:46 +0000
pf: allow unspecified addressed for certain MLD messages
As per RFC 3590 MLD Report and Done messages are permitted to use the
unspecified address as a source address (e.g. during duplicate address
detection for the first IPv6 address). Allow this, but only this.
Reported by: Alexander Leidinger <Alexander@Leidinger.net>
Reviewed by: bms
See also: OpenBSD, sashan <sashan@openbsd.org>, 60036e8507
Sponsored by: Rubicon Communications, LLC ("Netgate")
Differential Revision: https://reviews.freebsd.org/D59334
---
sys/netpfil/pf/pf.c | 10 +++++++-
tests/sys/netpfil/pf/mld.py | 57 +++++++++++++++++++++++++++++++++++++++++++++
2 files changed, 66 insertions(+), 1 deletion(-)
diff --git a/sys/netpfil/pf/pf.c b/sys/netpfil/pf/pf.c
index 04e919639ddb..322b10c3fce6 100644
--- a/sys/netpfil/pf/pf.c
+++ b/sys/netpfil/pf/pf.c
@@ -11085,9 +11085,17 @@ pf_walk_header6(struct pf_pdesc *pd, struct ip6_hdr *h, u_short *reason)
* local source address. If either one is
* missing then MLD message is invalid and
* should be discarded.
+ * RFC 3590 clarifies that during initial
+ * duplicate address detection nodes may not
+ * have an address, so are permitted to use
+ * the unspecified address, but only for Report
+ * and Done messages.
*/
if ((h->ip6_hlim != 1) ||
- !IN6_IS_ADDR_LINKLOCAL(&h->ip6_src)) {
+ (!IN6_IS_ADDR_LINKLOCAL(&h->ip6_src) &&
+ icmp6.icmp6_type == MLD_LISTENER_QUERY) ||
+ (!IN6_IS_ADDR_LINKLOCAL(&h->ip6_src) &&
+ !IN6_IS_ADDR_UNSPECIFIED(&h->ip6_src))) {
DPFPRINTF(PF_DEBUG_MISC, "Invalid MLD");
REASON_SET(reason, PFRES_IPOPTIONS);
return (PF_DROP);
diff --git a/tests/sys/netpfil/pf/mld.py b/tests/sys/netpfil/pf/mld.py
index b3ef6c21b3de..ab644d3eed4c 100644
--- a/tests/sys/netpfil/pf/mld.py
+++ b/tests/sys/netpfil/pf/mld.py
@@ -25,6 +25,7 @@
# SUCH DAMAGE.
import pytest
+import time
from utils import DelayedSend
from atf_python.sys.net.tools import ToolsHelper
from atf_python.sys.net.vnet import VnetTestTemplate
@@ -45,6 +46,11 @@ class TestMLD(VnetTestTemplate):
])
ToolsHelper.print_output("/sbin/pfctl -x loud")
+ while True:
+ cmd = self.wait_object(vnet.pipe)
+ result = ToolsHelper.get_output(cmd)
+ vnet.pipe.send(result)
+
def find_mld_reply(self, pkt, ifname):
pkt.show()
s = DelayedSend(pkt, ifname)
@@ -88,3 +94,54 @@ class TestMLD(VnetTestTemplate):
# Check if we logged dropping the MLD paacket
dmesg = ToolsHelper.get_output("/sbin/dmesg")
assert dmesg.find("Invalid MLD") != -1
+
+ @pytest.mark.require_user("root")
+ @pytest.mark.require_progs(["scapy"])
+ def test_unspec(self):
+ """Verify that we allow MLD packets from the unspecifed address"""
+ pf_pipe = self.vnet_map["vnet2"].pipe
+ ifname = self.vnet.iface_alias_map["if1"].name
+ ToolsHelper.print_output("/sbin/ifconfig")
+
+ # Import in the correct vnet, so at to not confuse Scapy
+ import scapy.all as sp
+ import scapy.contrib as sc
+ import scapy.contrib.igmp
+ self.sp = sp
+ self.sc = sc
+
+ # MLD packets with an incorrect hop limit get dropped.
+ pkt = sp.Ether() \
+ / sp.IPv6(src="::", dst="ff02::1", hlim=1) \
+ / sp.IPv6ExtHdrHopByHop(options=[ \
+ sp.RouterAlert(value=0) \
+ ]) \
+ / sp.ICMPv6MLReport()
+ # Send the packet, there's no reply to a report
+ pkt.show()
+ sp.sendp(pkt, iface=ifname)
+
+ time.sleep(1)
+
+ # We should not have logged a drop of an MLD packet
+ pf_pipe.send("/sbin/pfctl -sa")
+ out = self.wait_object(pf_pipe)
+ print("out %s" % out)
+ assert out.find("ip-option 0") != -1
+
+ # However, we do still drop queries from the unspecified address
+ pkt = sp.Ether() \
+ / sp.IPv6(src="::", dst="ff02::1", hlim=1) \
+ / sp.IPv6ExtHdrHopByHop(options=[ \
+ sp.RouterAlert(value=0) \
+ ]) \
+ / sp.ICMPv6MLQuery()
+ # Send the packet, there's no reply to a report
+ sp.sendp(pkt, iface=ifname)
+
+ time.sleep(1)
+
+ pf_pipe.send("/sbin/pfctl -sa")
+ out = self.wait_object(pf_pipe)
+ print("out %s" % out)
+ assert out.find("ip-option 1") != -1