Check the destination ethernet address when not in promiscous mode.

Fix problem where packets would be duplicated, possibly looping, when
a domU is doing IP routing.
Problem reported and fix tested by Mike M. Volokhov on port-xen

While there, add some __predict_false/true in conditionnals where
appropriate, remove a always-true test, and fix handling of
mbuf shortage.
This commit is contained in:
bouyer 2006-02-01 19:12:02 +00:00
parent 1edbda9a2e
commit 86110231a4
2 changed files with 63 additions and 29 deletions

View File

@ -1,4 +1,4 @@
/* $NetBSD: if_xennet.c,v 1.41 2006/01/26 19:17:25 bouyer Exp $ */
/* $NetBSD: if_xennet.c,v 1.42 2006/02/01 19:12:02 bouyer Exp $ */
/*
*
@ -33,7 +33,7 @@
#include <sys/cdefs.h>
__KERNEL_RCSID(0, "$NetBSD: if_xennet.c,v 1.41 2006/01/26 19:17:25 bouyer Exp $");
__KERNEL_RCSID(0, "$NetBSD: if_xennet.c,v 1.42 2006/02/01 19:12:02 bouyer Exp $");
#include "opt_inet.h"
#include "opt_nfs_boot.h"
@ -691,6 +691,7 @@ xen_network_handler(void *arg)
mmu_update_t *mmu = rx_mmu;
multicall_entry_t *mcl = rx_mcl;
struct mbuf *m;
void *pktp;
xennet_start(ifp); /* to cleanup TX bufs and keep the ifq_send going */
@ -711,7 +712,7 @@ xen_network_handler(void *arg)
/* XXXcl check rx->status for error */
MGETHDR(m, M_DONTWAIT, MT_DATA);
if (m == NULL) {
if (__predict_false(m == NULL)) {
printf("xennet: rx no mbuf\n");
ifp->if_ierrors++;
break;
@ -739,14 +740,12 @@ xen_network_handler(void *arg)
/* Do all the remapping work, and M->P updates, in one
* big hypercall. */
if ((mcl - rx_mcl) != 0) {
mcl->op = __HYPERVISOR_mmu_update;
mcl->args[0] = (unsigned long)rx_mmu;
mcl->args[1] = mmu - rx_mmu;
mcl->args[2] = 0;
mcl++;
(void)HYPERVISOR_multicall(rx_mcl, mcl - rx_mcl);
}
mcl->op = __HYPERVISOR_mmu_update;
mcl->args[0] = (unsigned long)rx_mmu;
mcl->args[1] = mmu - rx_mmu;
mcl->args[2] = 0;
mcl++;
(void)HYPERVISOR_multicall(rx_mcl, mcl - rx_mcl);
if (0)
printf("page mapped at va %08lx -> %08x/%08lx\n",
sc->sc_rx_bufa[rx->id].xb_rx.xbrx_va,
@ -763,11 +762,23 @@ xen_network_handler(void *arg)
(void *)(PTE_BASE[x86_btop
(sc->sc_rx_bufa[rx->id].xb_rx.xbrx_va)] & PG_FRAME)));
pktp = (void *)(sc->sc_rx_bufa[rx->id].xb_rx.xbrx_va +
(rx->addr & PAGE_MASK));
if ((ifp->if_flags & IFF_PROMISC) == 0) {
struct ether_header *eh = pktp;
if (ETHER_IS_MULTICAST(eh->ether_dhost) == 0 &&
memcmp(LLADDR(ifp->if_sadl), eh->ether_dhost,
ETHER_ADDR_LEN) != 0) {
xennet_rx_push_buffer(sc, rx->id);
m_freem(m);
continue; /* packet not for us */
}
}
m->m_pkthdr.rcvif = ifp;
if (sc->sc_rx->req_prod != sc->sc_rx->resp_prod) {
if (__predict_true(
sc->sc_rx->req_prod != sc->sc_rx->resp_prod)) {
m->m_len = m->m_pkthdr.len = rx->status;
MEXTADD(m, (void *)(sc->sc_rx_bufa[rx->id].xb_rx.
xbrx_va + (rx->addr & PAGE_MASK)), rx->status,
MEXTADD(m, pktp, rx->status,
M_DEVBUF, xennet_rx_mbuf_free,
&sc->sc_rx_bufa[rx->id]);
} else {
@ -778,14 +789,13 @@ xen_network_handler(void *arg)
*/
m->m_len = MHLEN;
m->m_pkthdr.len = 0;
m_copyback(m, 0, rx->status,
(caddr_t)(sc->sc_rx_bufa[rx->id].xb_rx.xbrx_va +
(rx->addr & PAGE_MASK)));
m_copyback(m, 0, rx->status, pktp);
xennet_rx_push_buffer(sc, rx->id);
if (m->m_pkthdr.len < rx->status) {
/* out of memory, just drop packets */
ifp->if_ierrors++;
m_freem(m);
break;
continue;
}
}

View File

@ -1,4 +1,4 @@
/* $NetBSD: xennetback.c,v 1.19 2006/01/23 20:19:08 yamt Exp $ */
/* $NetBSD: xennetback.c,v 1.20 2006/02/01 19:12:02 bouyer Exp $ */
/*
* Copyright (c) 2005 Manuel Bouyer.
@ -568,8 +568,8 @@ again:
txreq =
&xneti->xni_txring->ring[MASK_NETIF_TX_IDX(req_cons)].req;
XENPRINTF(("%s pkt size %d\n", xneti->xni_if.if_xname, txreq->size));
if ((ifp->if_flags & (IFF_UP | IFF_RUNNING)) !=
(IFF_UP | IFF_RUNNING)) {
if (__predict_false((ifp->if_flags & (IFF_UP | IFF_RUNNING)) !=
(IFF_UP | IFF_RUNNING))) {
/* interface not up, drop */
xennetback_tx_response(xneti, txreq->id,
NETIF_RSP_DROPPED);
@ -578,8 +578,8 @@ again:
/*
* Do some sanity checks, and map the packet's page.
*/
if (txreq->size < ETHER_HDR_LEN ||
txreq->size > (ETHER_MAX_LEN - ETHER_CRC_LEN)) {
if (__predict_false(txreq->size < ETHER_HDR_LEN ||
txreq->size > (ETHER_MAX_LEN - ETHER_CRC_LEN))) {
printf("%s: packet size %d too big\n",
ifp->if_xname, txreq->size);
xennetback_tx_response(xneti, txreq->id,
@ -588,7 +588,8 @@ again:
continue;
}
/* don't cross page boundaries */
if ((txreq->addr & PAGE_MASK) + txreq->size > PAGE_SIZE) {
if (__predict_false(
(txreq->addr & PAGE_MASK) + txreq->size > PAGE_SIZE)) {
printf("%s: packet cross page boundary\n",
ifp->if_xname);
xennetback_tx_response(xneti, txreq->id,
@ -598,7 +599,7 @@ again:
}
/* get a mbuf for this packet */
MGETHDR(m, M_DONTWAIT, MT_DATA);
if (m == NULL) {
if (__predict_false(m == NULL)) {
static struct timeval lasttime;
if (ratecheck(&lasttime, &xni_pool_errintvl))
printf("%s: mbuf alloc failed\n",
@ -615,7 +616,7 @@ again:
txreq->size, txreq->id, MASK_NETIF_TX_IDX(req_cons)));
pkt = pool_get(&xni_pkt_pool, PR_NOWAIT);
if (pkt == NULL) {
if (__predict_false(pkt == NULL)) {
static struct timeval lasttime;
if (ratecheck(&lasttime, &xni_pool_errintvl))
printf("%s: xnbpkt alloc failed\n",
@ -635,7 +636,7 @@ again:
}
if (pkt_page == NULL) {
pkt_page = pool_get(&xni_page_pool, PR_NOWAIT);
if (pkt_page == NULL) {
if (__predict_false(pkt_page == NULL)) {
static struct timeval lasttime;
if (ratecheck(&lasttime, &xni_pool_errintvl))
printf("%s: xnbpa alloc failed\n",
@ -648,8 +649,8 @@ again:
continue;
}
pkt_page->refcount = 0;
if (xen_shm_map(&pkt_ma,
1, xneti->domid, &pkt_va, 0) != 0) {
if (__predict_false(xen_shm_map(&pkt_ma,
1, xneti->domid, &pkt_va, 0) != 0)) {
static struct timeval lasttime;
if (ratecheck(&lasttime, &xni_pool_errintvl))
printf("%s: can't map packet page\n",
@ -658,6 +659,8 @@ again:
NETIF_RSP_DROPPED);
ifp->if_ierrors++;
m_freem(m);
pool_put(&xni_pkt_pool, pkt);
pool_put(&xni_page_pool, pkt_page);
continue;
}
XENPRINTF(("new pkt_page va 0x%lx mbuf %p\n",
@ -671,6 +674,27 @@ again:
XENPRINTF(("pkt_page refcount %d va 0x%lx m %p\n",
pkt_page->refcount, pkt_va, m));
}
if ((ifp->if_flags & IFF_PROMISC) == 0) {
struct ether_header *eh =
(void*)(pkt_va | (txreq->addr & PAGE_MASK));
if (ETHER_IS_MULTICAST(eh->ether_dhost) == 0 &&
memcmp(LLADDR(ifp->if_sadl), eh->ether_dhost,
ETHER_ADDR_LEN) != 0) {
pool_put(&xni_pkt_pool, pkt);
m_freem(m);
if (pkt_page->refcount == 0) {
xen_shm_unmap(pkt_page->va,
&pkt_page->ma, 1, xneti->domid);
SLIST_REMOVE(pkt_hash, pkt_page,
xni_page, xni_page_next);
pool_put(&xni_page_pool, pkt_page);
}
xennetback_tx_response(xneti, txreq->id,
NETIF_RSP_OKAY);
continue; /* packet is not for us */
}
}
if (MASK_NETIF_TX_IDX(req_cons + 1) ==
MASK_NETIF_TX_IDX(xneti->xni_txring->resp_prod)) {
/*