kernel - More if_bridge work + misc fixes
authorMatthew Dillon <dillon@apollo.backplane.com>
Tue, 22 Feb 2011 00:02:30 +0000 (16:02 -0800)
committerMatthew Dillon <dillon@apollo.backplane.com>
Tue, 22 Feb 2011 00:02:30 +0000 (16:02 -0800)
* When bridging packets sent from one of our own MACs we always override
  the ether_shost in the output packet so it comes from the actual
  interface the packet is being sent out on.

* LINK0 will still nominally keep the ether_shost intact when forwarding
  across a bridge, except in the above case.  That is, any foreign MAC
  set as the source coming in on one interface will be retained as the
  source when being thrown out on another interface.  But any local MAC
  will be replaced with the MAC of the outgoing interface.

* When receiving a unicast frame on one interface which is targetted to
  another interface, retain the original rcvif for any vlan or arp
  processing.  Otherwise (for example) if this were an ARP reply the ARP
  code would associate the reply with the wrong interface.  We would want
  the ARP entry to be associated with the first interface, not the second,
  because the first interface is the one the reply actually came in on.

* Adjust the ARP code in if_ether.c to use rcvif and not ifp, and don't
  log if non-matching interfaces are part of the same bridge (unless
  log_arp_wrong_iface is set to 2).

* Augment the ether_reinput_cpu() API to pass additional flags in,
  allowing the caller to specify that m->m_pkthdr.rcvif not be
  overwritten.  Used to support the above features.

* Clear M_HASH in a few more cases in pf.c

sys/net/bridge/if_bridge.c
sys/net/ethernet.h
sys/net/if_ethersubr.c
sys/net/if_var.h
sys/net/pf/pf.c
sys/net/vlan/if_vlan.c
sys/netinet/if_ether.c

index df7ab5b..9fe99ae 100644 (file)
@@ -458,7 +458,7 @@ static int  bridge_ip6_checkbasic(struct mbuf **mp);
 static int     bridge_fragment(struct ifnet *, struct mbuf *,
                    struct ether_header *, int, struct llc *);
 static void    bridge_enqueue_handler(netmsg_t);
-static void    bridge_handoff(struct ifnet *, struct mbuf *);
+static void    bridge_handoff(struct ifnet *, struct mbuf *, int);
 
 static void    bridge_del_bif_handler(netmsg_t);
 static void    bridge_add_bif_handler(netmsg_t);
@@ -1843,6 +1843,7 @@ bridge_output(struct ifnet *ifp, struct mbuf *m)
        struct bridge_softc *sc = ifp->if_bridge;
        struct ether_header *eh;
        struct ifnet *dst_if, *bifp;
+       int from_us;
 
        ASSERT_IFNET_NOT_SERIALIZED_ALL(ifp);
 
@@ -1862,20 +1863,10 @@ bridge_output(struct ifnet *ifp, struct mbuf *m)
        }
        eh = mtod(m, struct ether_header *);
 
-       /*
-        * LINK0 - Enables transparent bridge mode.
-        */
-       if (eh->ether_type == htons(ETHERTYPE_IP) ||
-           eh->ether_type == htons(ETHERTYPE_IPV6)) {
-               if ((bifp->if_flags & IFF_LINK0) &&
-                   (m->m_pkthdr.fw_flags & BRIDGE_MBUF_TAGGED) &&
-                   bridge_rtlookup(sc, m->m_pkthdr.br.ether.ether_shost) !=
-                   bridge_rtlookup(sc, eh->ether_dhost)) {
-                           bcopy(m->m_pkthdr.br.ether.ether_shost,
-                             eh->ether_shost,
-                             sizeof(eh->ether_shost));
-               }
-       }
+       if (memcmp(eh->ether_dhost, IF_LLADDR(bifp), ETHER_ADDR_LEN) == 0)
+               from_us = 1;
+       else
+               from_us = 0;
 
        /*
         * If bridge is down, but the original output interface is up,
@@ -1935,7 +1926,11 @@ bridge_output(struct ifnet *ifp, struct mbuf *m)
                                        continue;
                                }
                        }
-                       bridge_handoff(dst_if, mc);
+
+                       /*
+                        * If the packet is 'from' us override ether_shost.
+                        */
+                       bridge_handoff(dst_if, mc, from_us);
 
                        if (nbif != NULL && !nbif->bif_onlist) {
                                KKASSERT(bif->bif_onlist);
@@ -1956,7 +1951,7 @@ sendunicast:
        if ((dst_if->if_flags & IFF_RUNNING) == 0)
                m_freem(m);
        else
-               bridge_handoff(dst_if, m);
+               bridge_handoff(dst_if, m, from_us);
        return (0);
 }
 
@@ -1964,7 +1959,6 @@ sendunicast:
  * bridge_start:
  *
  *     Start output on a bridge.
- *
  */
 static void
 bridge_start(struct ifnet *ifp)
@@ -1992,22 +1986,6 @@ bridge_start(struct ifnet *ifp)
                }
                eh = mtod(m, struct ether_header *);
 
-               /*
-                * LINK0 - Enable transparent bridge mode (see comments
-                *         in the first LINK0 section above).
-                */
-               if (eh->ether_type == htons(ETHERTYPE_IP) ||
-                   eh->ether_type == htons(ETHERTYPE_IPV6)) {
-                       if ((ifp->if_flags & IFF_LINK0) &&
-                           (m->m_pkthdr.fw_flags & BRIDGE_MBUF_TAGGED) &&
-                           bridge_rtlookup(sc, m->m_pkthdr.br.ether.ether_shost) !=
-                           bridge_rtlookup(sc, eh->ether_dhost)) {
-                               bcopy(m->m_pkthdr.br.ether.ether_shost,
-                                     eh->ether_shost,
-                                     sizeof(eh->ether_shost));
-                       }
-               }
-
                BPF_MTAP(ifp, m);
                ifp->if_opackets++;
 
@@ -2025,6 +2003,9 @@ bridge_start(struct ifnet *ifp)
 /*
  * bridge_forward:
  *
+ *     Forward packets received on a bridge interface via the input
+ *     path.
+ *
  *     The forwarding function of the bridge.
  */
 static void
@@ -2150,7 +2131,7 @@ bridge_forward(struct bridge_softc *sc, struct mbuf *m)
                if (m == NULL)
                        return;
        }
-       bridge_handoff(dst_if, m);
+       bridge_handoff(dst_if, m, 0);
 }
 
 /*
@@ -2300,10 +2281,13 @@ bridge_input(struct ifnet *ifp, struct mbuf *m)
 #endif
                if (mc2 != NULL) {
                        /*
-                        * Don't tap to bpf(4) again; we have
-                        * already done the tapping.
+                        * Don't tap to bpf(4) again; we have already done
+                        * the tapping.
+                        *
+                        * Leave m_pkthdr.rcvif alone, so ARP replies are
+                        * processed as coming in on the correct interface.
                         */
-                       ether_reinput_oncpu(bifp, mc2, 0);
+                       ether_reinput_oncpu(bifp, mc2, REINPUT_KEEPRCVIF);
                }
 
                /* Return the original packet for local processing. */
@@ -2329,7 +2313,12 @@ bridge_input(struct ifnet *ifp, struct mbuf *m)
                if (bif->bif_ifp->if_type != IFT_ETHER)
                        continue;
 
-               /* It is destined for us. */
+               /*
+                * It is destined for us.  Reinput on the same interface
+                * it came in on so things like ARP responses get assigned
+                * to the correct member (the incoming interface) and not
+                * to the member which happens to have the matching dhost.
+                */
                if (memcmp(IF_LLADDR(bif->bif_ifp), eh->ether_dhost,
                    ETHER_ADDR_LEN) == 0) {
                        if (bif->bif_ifp != ifp) {
@@ -2356,9 +2345,15 @@ bridge_input(struct ifnet *ifp, struct mbuf *m)
        /* Perform the bridge forwarding function. */
        bridge_forward(sc, m);
        m = NULL;
+
+       /*
+        * Leave m_pkthdr.rcvif alone, so ARP replies are
+        * processed as coming in on the correct interface.
+        */
 out:
        if (new_ifp != NULL) {
-               ether_reinput_oncpu(new_ifp, m, 1);
+               ether_reinput_oncpu(new_ifp, m,
+                                   REINPUT_KEEPRCVIF|REINPUT_RUNBPF);
                m = NULL;
        }
        return (m);
@@ -2430,16 +2425,24 @@ bridge_start_bcast(struct bridge_softc *sc, struct mbuf *m)
  */
 static void
 bridge_broadcast(struct bridge_softc *sc, struct ifnet *src_if,
-    struct mbuf *m)
+                struct mbuf *m)
 {
        struct bridge_iflist *bif, *nbif;
+       struct ether_header *eh;
        struct mbuf *mc;
        struct ifnet *dst_if, *bifp;
        int used = 0;
+       int from_us;
 
        bifp = sc->sc_ifp;
        ASSERT_IFNET_NOT_SERIALIZED_ALL(bifp);
 
+       eh = mtod(m, struct ether_header *);
+       if (memcmp(eh->ether_dhost, IF_LLADDR(src_if), ETHER_ADDR_LEN) == 0)
+               from_us = 1;
+       else
+               from_us = 0;
+
        if (inet_pfil_hook.ph_hashooks > 0
 #ifdef INET6
            || inet6_pfil_hook.ph_hashooks > 0
@@ -2503,7 +2506,7 @@ bridge_broadcast(struct bridge_softc *sc, struct ifnet *src_if,
                        if (mc == NULL)
                                continue;
                }
-               bridge_handoff(dst_if, mc);
+               bridge_handoff(dst_if, mc, from_us);
 
                if (nbif != NULL && !nbif->bif_onlist) {
                        KKASSERT(bif->bif_onlist);
@@ -3688,13 +3691,16 @@ bridge_enqueue_handler(netmsg_t msg)
        m = nmp->nm_packet;
        dst_ifp = nmp->base.lmsg.u.ms_resultp;
 
-       bridge_handoff(dst_ifp, m);
+       bridge_handoff(dst_ifp, m, 1);
 }
 
 static void
-bridge_handoff(struct ifnet *dst_ifp, struct mbuf *m)
+bridge_handoff(struct ifnet *dst_ifp, struct mbuf *m, int from_us)
 {
        struct mbuf *m0;
+       struct ifnet *bifp;
+
+       bifp = ((struct bridge_softc *)dst_ifp->if_bridge)->sc_ifp;
 
        /* We may be sending a fragment so traverse the mbuf */
        for (; m; m = m0) {
@@ -3703,6 +3709,26 @@ bridge_handoff(struct ifnet *dst_ifp, struct mbuf *m)
                m0 = m->m_nextpkt;
                m->m_nextpkt = NULL;
 
+               /*
+                * If being sent from our host override ether_shost
+                * so any replies go the correct interface.  This is
+                * mandatory or ARP replies will wind up on the wrong
+                * interface.
+                *
+                * Otherwise if we are in transparent mode
+                */
+               if (from_us) {
+                       m_copyback(m,
+                                  offsetof(struct ether_header, ether_shost),
+                                  ETHER_ADDR_LEN, IF_LLADDR(dst_ifp));
+               } else if ((bifp->if_flags & IFF_LINK0) &&
+                          (m->m_pkthdr.fw_flags & BRIDGE_MBUF_TAGGED)) {
+                       m_copyback(m,
+                                  offsetof(struct ether_header, ether_shost),
+                                  ETHER_ADDR_LEN,
+                                  m->m_pkthdr.br.ether.ether_shost);
+               }
+
                if (ifq_is_enabled(&dst_ifp->if_snd))
                        altq_etherclassify(&dst_ifp->if_snd, m, &pktattr);
 
index ca3f335..f1e168c 100644 (file)
@@ -356,8 +356,10 @@ extern const uint8_t       etherbroadcastaddr[ETHER_ADDR_LEN];
 
 #define M_ETHER_BRIDGED                M_PROTO1
 #define M_ETHER_VLANCHECKED    M_PROTO2
+#define M_ETHER_BRIDGED_FROM_US        M_PROTO3
 
-#define M_ETHER_FLAGS          (M_ETHER_BRIDGED | M_ETHER_VLANCHECKED)
+#define M_ETHER_FLAGS          (M_ETHER_BRIDGED | M_ETHER_VLANCHECKED | \
+                                M_ETHER_BRIDGED_FROM_US)
 
 struct ifnet;
 struct mbuf;
index 0805908..41d93c3 100644 (file)
@@ -1327,9 +1327,12 @@ ether_input_oncpu(struct ifnet *ifp, struct mbuf *m)
  *
  * This function should be used by pseudo interface (e.g. vlan(4)),
  * when it tries to claim that the packet is received by it.
+ *
+ * REINPUT_KEEPRCVIF
+ * REINPUT_RUNBPF
  */
 void
-ether_reinput_oncpu(struct ifnet *ifp, struct mbuf *m, int run_bpf)
+ether_reinput_oncpu(struct ifnet *ifp, struct mbuf *m, int reinput_flags)
 {
        /* Discard packet if interface is not up */
        if (!(ifp->if_flags & IFF_UP)) {
@@ -1337,8 +1340,15 @@ ether_reinput_oncpu(struct ifnet *ifp, struct mbuf *m, int run_bpf)
                return;
        }
 
-       /* Change receiving interface */
-       m->m_pkthdr.rcvif = ifp;
+       /*
+        * Change receiving interface.  The bridge will often pass a flag to
+        * ask that this not be done so ARPs get applied to the correct
+        * side.
+        */
+       if ((reinput_flags & REINPUT_KEEPRCVIF) == 0 ||
+           m->m_pkthdr.rcvif == NULL) {
+               m->m_pkthdr.rcvif = ifp;
+       }
 
        /* Update statistics */
        ifp->if_ipackets++;
@@ -1346,7 +1356,7 @@ ether_reinput_oncpu(struct ifnet *ifp, struct mbuf *m, int run_bpf)
        if (m->m_flags & (M_MCAST | M_BCAST))
                ifp->if_imcasts++;
 
-       if (run_bpf)
+       if (reinput_flags & REINPUT_RUNBPF)
                BPF_MTAP(ifp, m);
 
        ether_input_oncpu(ifp, m);
index f88573f..976bb71 100644 (file)
@@ -717,6 +717,10 @@ ifa_forwardmsg(struct lwkt_msg *_lmsg, int _nextcpu)
        ifnet_forwardmsg(_lmsg, _nextcpu);
 }
 
+#define REINPUT_KEEPRCVIF      0x0001  /* ether_reinput_oncpu() */
+#define REINPUT_RUNBPF                 0x0002  /* ether_reinput_oncpu() */
+
+
 extern struct ifnethead ifnet;
 extern struct  ifnet   **ifindex2ifnet;
 extern int ifqmaxlen;
index 16ad833..21c68d0 100644 (file)
@@ -4462,6 +4462,7 @@ pf_test_state_tcp(struct pf_state **state, int direction, struct pfi_kif *kif,
                         * a bridge doesn't try to use it.
                         */
                        m->m_pkthdr.fw_flags &= ~BRIDGE_MBUF_TAGGED;
+                       m->m_flags &= ~M_HASH;
                        pf_change_ap(pd->src, &th->th_sport, pd->ip_sum,
                            &th->th_sum, &nk->addr[pd->sidx],
                            nk->port[pd->sidx], 0, pd->af);
@@ -4546,6 +4547,7 @@ pf_test_state_udp(struct pf_state **state, int direction, struct pfi_kif *kif,
                         * a bridge doesn't try to use it.
                         */
                        m->m_pkthdr.fw_flags &= ~BRIDGE_MBUF_TAGGED;
+                       m->m_flags &= ~M_HASH;
                        pf_change_ap(pd->src, &uh->uh_sport, pd->ip_sum,
                            &uh->uh_sum, &nk->addr[pd->sidx],
                            nk->port[pd->sidx], 1, pd->af);
index 70548f5..4bfcd09 100644 (file)
@@ -553,7 +553,7 @@ vlan_input(struct mbuf *m)
         */
        m->m_flags &= ~M_VLANTAG;
 
-       ether_reinput_oncpu(&ifv->ifv_if, m, 1);
+       ether_reinput_oncpu(&ifv->ifv_if, m, REINPUT_RUNBPF);
 }
 
 static void
index 09026e7..5c9bdb0 100644 (file)
@@ -657,7 +657,16 @@ arp_update_oncpu(struct mbuf *m, in_addr_t saddr, boolean_t create,
 
                /* the following is not an error when doing bridging */
                if (rt->rt_ifp != ifp) {
+                       if (ifp->if_bridge &&
+                           rt->rt_ifp->if_bridge == ifp->if_bridge) {
+                               rt->rt_ifp = ifp;
+                               sdl->sdl_type = ifp->if_type;
+                               sdl->sdl_index = ifp->if_index;
+                               if (dologging && log_arp_wrong_iface < 2)
+                                       dologging = 0;
+                       }
                        if (dologging && log_arp_wrong_iface) {
+
                                log(LOG_ERR,
                                    "arp: %s is on %s "
                                    "but got reply from %*D on %s\n",