Skip site navigation (1)Skip section navigation (2)
Date:      Mon, 15 Aug 2005 16:34:50 +0200
From:      Daniel Hartmeier <daniel@benzedrine.cx>
To:        Sergey Lapin <slapinid@gmail.com>
Cc:        freebsd-pf@freebsd.org
Subject:   Re: Fwd: Dual-feed: PF setup troubles
Message-ID:  <20050815143449.GA32151@insomnia.benzedrine.cx>
In-Reply-To: <48239d39050815042096f9890@mail.gmail.com>
References:  <42FF47A2.1090208@yuckfou.org> <5111186986.20050814210129@hexren.net> <48239d39050815041712bd714c@mail.gmail.com> <48239d39050815042096f9890@mail.gmail.com>

next in thread | previous in thread | raw e-mail | index | archive | help
I suspect the loop occurs through sys/net/if_ethersubr.c ether_output()

        /*
         * If a simplex interface, and the packet is being sent to our
         * Ethernet address or a broadcast address, loopback a copy.
         * XXX To make a simplex device behave exactly like a duplex
         * device, we should copy in the case of sending to our own
         * ethernet address (thus letting the original actually appear
         * on the wire). However, we don't do that here for security
         * reasons and compatibility with the original behavior.
         */
        if ((ifp->if_flags & IFF_SIMPLEX) && (loop_copy != -1)) {
                int csum_flags = 0;
           
                if (m->m_pkthdr.csum_flags & CSUM_IP)
                        csum_flags |= (CSUM_IP_CHECKED|CSUM_IP_VALID);
                if (m->m_pkthdr.csum_flags & CSUM_DELAY_DATA)
                        csum_flags |= (CSUM_DATA_VALID|CSUM_PSEUDO_HDR);
                
                if ((m->m_flags & M_BCAST) || (loop_copy > 0)) {   
                        struct mbuf *n;

                        if ((n = m_copy(m, 0, (int)M_COPYALL)) != NULL) {
                                n->m_pkthdr.csum_flags |= csum_flags;
                                if (csum_flags & CSUM_DATA_VALID)
                                        n->m_pkthdr.csum_data = 0xffff;
                                (void)if_simloop(ifp, n, dst->sa_family, hlen);
                        } else
                                ifp->if_iqdrops++;
                } else if (bcmp(eh->ether_dhost, eh->ether_shost,
                                ETHER_ADDR_LEN) == 0) {
                        m->m_pkthdr.csum_flags |= csum_flags;
                        if (csum_flags & CSUM_DATA_VALID)
                                m->m_pkthdr.csum_data = 0xffff;
                        (void) if_simloop(ifp, m, dst->sa_family, hlen);
                        return (0);     /* XXX */
                }
        }

You route-to the broadcast packet, pf will call ether_output() to send
it out through the new interface, and this piece of code in there will
send it right back in through that interface again. If your ruleset then
routes that resent packet again, you get a tight endless loop, locking
up the kernel, like you describe.

OpenBSD doesn't have this piece in ether_output(), I'm not sure in what
cases people want outgoing broadcasts on an interface reflected back at
them by the stack.

You can try turning of the IFF_SIMPLEX flag on the interface (unsure
about the entire effects of that), or simply exclude those broadcast
packets from getting routed by pf (which isn't really intentional, is
it?), like

  -              ... from ... to any ...
  +              ... from ... to !255.255.255.255 ...

Or find the person familiar/responsible for this loop_copy code, and ask
whether we can bypass it, for instance when the PF_ROUTED mbuf tag is
present, or such.

I'm not entirely sure this is the loop, but you can confirm by adding
simple printf()s along that code path, you'll see them printed endlessly
when the lockup occurs.

Daniel



Want to link to this message? Use this URL: <https://mail-archive.FreeBSD.org/cgi/mid.cgi?20050815143449.GA32151>