tech-net archive

[Date Prev][Date Next][Thread Prev][Thread Next][Date Index][Thread Index][Old Index]

bridge(4) - feedback local packets into ether_input()



Hi List!

So my goal is to run a DHCP server and client on two virtual interfaces
and get them talking to each other. After the initial idea of
vether/pair was shot down I looked into what bridge(4) could do after
much prompting by our beloved Taylor.

Here is what I am trying to achieve WITHOUT BPF* (it works with BPF):
DHCP Server --- > tap0 --- > bridge0 <---- tap1 <----- DHCP client

Because tap(4) will drop packets if there is nothing using it as a TAP
device (in this scenario we're just treating it as an endpoint) we have
to solve the problem in bridge(4).

Because DHCP is essentially a broadcast operation, I copied the logic
from bridge_broadcast() into bridge_output() (used for originating local
packets) where we send the packet to ether_input() if it's broadcast or
multicast. Because bridge_output has coding for if src_if == dst_if I
reversed the logic on sending it to ether_input (src_if != dst_if) so
that the receiving interface doesn't double up.

The attached patch allows this to work fine.
Comments are welcome, especially if you think it breaks something.

Roy

* OK the DHCP client still requires BPF but that can be fixed once PR
kern/48280 is.
Index: sys/net/if_bridge.c
===================================================================
RCS file: /cvsroot/src/sys/net/if_bridge.c,v
retrieving revision 1.118
diff -p -u -r1.118 if_bridge.c
--- sys/net/if_bridge.c	22 Apr 2016 00:25:42 -0000	1.118
+++ sys/net/if_bridge.c	22 Apr 2016 00:40:35 -0000
@@ -1431,6 +1431,14 @@ bridge_output(struct ifnet *ifp, struct 
 	eh = mtod(m, struct ether_header *);
 	sc = ifp->if_bridge;
 
+	if (ETHER_IS_MULTICAST(eh->ether_dhost)) {
+		if (memcmp(etherbroadcastaddr,
+		    eh->ether_dhost, ETHER_ADDR_LEN) == 0)
+			m->m_flags |= M_BCAST;
+		else
+			m->m_flags |= M_MCAST;
+	}
+
 	/*
 	 * If bridge is down, but the original output interface is up,
 	 * go ahead and send out that interface.  Otherwise, the packet
@@ -1446,7 +1454,7 @@ bridge_output(struct ifnet *ifp, struct 
 	 * If the packet is a multicast, or we don't know a better way to
 	 * get there, send to all interfaces.
 	 */
-	if (ETHER_IS_MULTICAST(eh->ether_dhost))
+	if ((m->m_flags & (M_MCAST | M_BCAST)) != 0)
 		dst_if = NULL;
 	else
 		dst_if = bridge_rtlookup(sc, eh->ether_dhost);
@@ -1483,7 +1491,10 @@ bridge_output(struct ifnet *ifp, struct 
 			}
 
 			if (PSLIST_READER_NEXT(bif, struct bridge_iflist,
-			    bif_next) == NULL) {
+			    bif_next) == NULL &&
+			    ((m->m_flags & (M_MCAST | M_BCAST)) == 0 ||
+			    dst_if == ifp))
+			{
 				used = true;
 				mc = m;
 			} else {
@@ -1501,6 +1512,36 @@ bridge_output(struct ifnet *ifp, struct 
 #ifndef NET_MPSAFE
 			splx(s);
 #endif
+
+			if ((m->m_flags & (M_MCAST | M_BCAST)) != 0 &&
+			    dst_if != ifp)
+			{
+				if (PSLIST_READER_NEXT(bif,
+				    struct bridge_iflist, bif_next) == NULL)
+				{
+					used = true;
+					mc = m;
+				} else {
+					mc = m_copym(m, 0, M_COPYALL,
+					    M_DONTWAIT);
+					if (mc == NULL) {
+						sc->sc_if.if_oerrors++;
+						goto next;
+					}
+				}
+
+				mc->m_pkthdr.rcvif = dst_if;
+				mc->m_flags &= ~M_PROMISC;
+
+#ifndef NET_MPSAFE
+				s = splnet();
+#endif
+				ether_input(dst_if, mc);
+#ifndef NET_MPSAFE
+				splx(s);
+#endif
+			}
+
 next:
 			BRIDGE_PSZ_RENTER(s);
 			bridge_release_member(sc, bif, &psref);


Home | Main Index | Thread Index | Old Index