Skip site navigation (1)Skip section navigation (2)
Date:      Mon, 2 Feb 2026 12:58:05 +0800
From:      Zhenlei Huang <zlei@FreeBSD.org>
To:        Martin Mayer <martin.mayer@m2-it-solutions.de>
Cc:        Vadim Goncharov <vadimnuclight@gmail.com>, "freebsd-net@freebsd.org" <freebsd-net@freebsd.org>, Gleb Smirnoff <glebius@FreeBSD.org>
Subject:   Re: promisc/netgraph question
Message-ID:  <BFA9B02C-7B09-42D5-8DD4-381BA276B68A@FreeBSD.org>
In-Reply-To: <FR2PPF447E0FD2FF81BF6D4A7538B0D0894BC9CA@FR2PPF447E0FD2F.DEUP281.PROD.OUTLOOK.COM>
References:  <FR2PPF447E0FD2F4AB3B97FDEBCB7FC5404BC9FA@FR2PPF447E0FD2F.DEUP281.PROD.OUTLOOK.COM> <FR2PPF447E0FD2FA1F4CFD7A4DF76B5DDD1BC9FA@FR2PPF447E0FD2F.DEUP281.PROD.OUTLOOK.COM> <20260130234931.073ec5df@nuclight.lan> <FR2PPF447E0FD2FF81BF6D4A7538B0D0894BC9CA@FR2PPF447E0FD2F.DEUP281.PROD.OUTLOOK.COM>

index | next in thread | previous in thread | raw e-mail



> On Jan 31, 2026, at 3:44 PM, Martin Mayer <martin.mayer@m2-it-solutions.de> wrote:
> 
> Let me explain my intend from a higher level and forget about the code details for a moment.
> 
> If a packet ingresses an interface where
>  ( dst MAC != interface MAC ) &&
>  ( dst MAC != multicast joined groups ) &&
>  ( dst MAC != broadcast )
> I'd expect
>  - the packet to be directly discarded if !( IFF_PROMISC || IFF_PPROMISC )
>  - the packet to be accepted but not passed to upper layers if ( IFF_PROMISC || IFF_PPROMISC )
> 
> A special case is CARP where IFF_PROMISC is set and should be passed to upper layers if
>  ( dst MAC == CARP instance MAC && CARP enabled )
> 
> I think we have a consensus until here. This is the current (normal) behavior.
> 
> This behavior changes as soon as netgraph is involved.
> If ng_ether hooks are connected, the host skips these checks and passes the packet to the upper layers.
> 
> The practical issue to my question is:
> https://github.com/opnsense/src/issues/279
> 
> I want to understand the root cause of this behavior and want to identify if this is intentionally or if it's a bug.
> As per my understanding, this happens in if_ethersubr.c because the hook for the packet to be claimed by netgraph is BEFORE the destination addresses are checked.
> 
> [...]
> 	/* Allow ng_ether(4) to claim this frame. */
> 	if (ifp->if_l2com != NULL) {
> 		KASSERT(ng_ether_input_p != NULL,
> 		    ("%s: ng_ether_input_p is NULL", __func__));
> 		m->m_flags &= ~M_PROMISC;
> 		(*ng_ether_input_p)(ifp, &m);
> 		if (m == NULL) {
> 			CURVNET_RESTORE();
> 			return;
> 		}
> 		eh = mtod(m, struct ether_header *);
> 	}
> [...]
> 	if (ifp->if_carp && (*carp_forus_p)(ifp, eh->ether_dhost)) {
> 		m->m_flags &= ~M_PROMISC;
> 	} else	{
> 		/*
> 		 * If the frame received was not for our MAC address, set the
> 		 * M_PROMISC flag on the mbuf chain. The frame may need to
> 		 * be seen by the rest of the Ethernet input path in case of
> 		 * re-entry (e.g. bridge, vlan, netgraph) but should not be
> 		 * seen by upper protocol layers.
> 		 */
> 		if (!ETHER_IS_MULTICAST(eh->ether_dhost) &&
> 		    memcmp(IF_LLADDR(ifp), eh->ether_dhost, ETHER_ADDR_LEN) != 0)
> 			m->m_flags |= M_PROMISC;
> 	}
> 
> 	ether_demux(ifp, m);
> [...]
> 
> ether_demux() (which is the re-entry point from ng_ether) won't be able to correctly evaluate (m->m_flags & M_PROMISC).
> 
> Thanks, Martin
> 
> 


The logic from ng_ether(4),

```
/*
 * Handle an mbuf received on the "upper" hook.
 */
static int
ng_ether_rcv_upper(hook_p hook, item_p item)
{
....
        m->m_pkthdr.rcvif = ifp;

        /* Pass the packet to the bridge, it may come back to us */
        if (ifp->if_bridge) {
                BRIDGE_INPUT(ifp, m);
                if (m == NULL)
                        return (0);
        }

        /* Route packet back in */
        ether_demux(ifp, m);
        return (0);
}
```

it lacks checking of the dest MAC of the inbound packets, and unconditionally inject packets ( no matter
whether they're for us or not ) via ether_demux() to the net stack.

Probably we should 
 1. check the dest MAC address prior to passing the packets to ng_ether(4), or
 2. teach ng_ether(4) to check the dest MAC address, or
 3. Move the checking to ether_demux().

The 3rd one has the minimal impact. I'm CCing Gleb who is more familiar with netgraph.

Best regards,
Zhenlei



home | help

Want to link to this message? Use this
URL: <https://mail-archive.FreeBSD.org/cgi/mid.cgi?BFA9B02C-7B09-42D5-8DD4-381BA276B68A>