Re: promisc/netgraph question

Martin Mayer <[email protected]>
Newsgroups gmane.os.freebsd.devel.net
Message-ID <FR2PPF447E0FD2FF81BF6D4A7538B0D0894BC9CA@FR2PPF447E0FD2F.DEUP281.PROD.OUTLOOK.COM>
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
lmpx.com only provides a reader for public news (NNTP) servers. It is not affiliated with the servers or forums shown here and is not responsible for the content of articles, which is written by their respective authors.