/* * Copyright (C) 2012 ADARA Networks. All rights reserved. * * Redistribution and use in source and binary forms, with or without * modification, are permitted provided that the following conditions * are met: * 1. Redistributions of source code must retain the above copyright * notice, this list of conditions and the following disclaimer. * 2. Redistributions in binary form must reproduce the above copyright * notice, this list of conditions and the following disclaimer in the * documentation and/or other materials provided with the distribution. * * THIS SOFTWARE IS PROVIDED BY THE AUTHOR AND CONTRIBUTORS ``AS IS'' AND * ANY EXPRESS OR IMPLIED WARRANTIES, INCLUDING, BUT NOT LIMITED TO, THE * IMPLIED WARRANTIES OF MERCHANTABILITY AND FITNESS FOR A PARTICULAR PURPOSE * ARE DISCLAIMED. IN NO EVENT SHALL THE AUTHOR OR CONTRIBUTORS BE LIABLE * FOR ANY DIRECT, INDIRECT, INCIDENTAL, SPECIAL, EXEMPLARY, OR CONSEQUENTIAL * DAMAGES (INCLUDING, BUT NOT LIMITED TO, PROCUREMENT OF SUBSTITUTE GOODS * OR SERVICES; LOSS OF USE, DATA, OR PROFITS; OR BUSINESS INTERRUPTION) * HOWEVER CAUSED AND ON ANY THEORY OF LIABILITY, WHETHER IN CONTRACT, STRICT * LIABILITY, OR TORT (INCLUDING NEGLIGENCE OR OTHERWISE) ARISING IN ANY WAY * OUT OF THE USE OF THIS SOFTWARE, EVEN IF ADVISED OF THE POSSIBILITY OF * SUCH DAMAGE. */ /* * $FreeBSD$ * * Example netmap implementation for a software interface. */ #include #include #include #include #include #include #include #include #include #include #include #include #include #include #include struct nmk_softc { struct ifnet *ifp; struct callout timer; struct mtx nmk_mtx; uint64_t packets; uint64_t bytes; uint64_t bytesum; }; /* * XXX for this example we create only one interface and keep a reference for * destruction. */ static struct nmk_softc *g_nmk_sc; MALLOC_DECLARE(M_NETMAP_KTEST); MALLOC_DEFINE(M_NETMAP_KTEST, "netmap_ktest", "Netmap kernel interface test"); /* Canned sample packet - an ICMP ECHO. */ const u_char canned_packet[] = { 0xff, 0xff, 0xff, 0xff, 0xff, 0xff, /* Ethernet destination */ 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, /* Ethernet source */ 0x08, 0x00, /* Ethernet type */ 0x45, /* IP hdr len & version */ 0x00, /* IP TOS */ 0x00, 0x54, /* IP total length */ 0xcb, 0x13, /* IP ID */ 0x00, 0x00, /* IP fragment offset */ 0x40, /* IP TTL */ 0x01, /* IP protocol */ 0xb9, 0x87, /* IP checksum */ 0xc0, 0xa8, 0x2b, 0x65, /* IP source */ 0x0a, 0x00, 0x00, 0x01, /* IP destination */ 0x08, /* ICMP type */ 0x00, /* ICMP type sub code */ 0xf8, 0xd0, /* ICMP checksum */ 0xc9, 0x76, /* ICMP ID */ 0x00, 0x00, /* ICMP sequence */ 0x45, 0x37, 0x01, 0x73, /* ICMP echo data */ 0x00, 0x01, 0x04, 0x0a, /* ICMP echo data */ 0x08, 0x09, 0x0a, 0x0b, 0x0c, 0x0d, 0x0e, 0x0f, /* ICMP padding */ 0x10, 0x11, 0x12, 0x13, 0x14, 0x15, 0x16, 0x17, /* ICMP padding */ 0x18, 0x19, 0x1a, 0x1b, 0x1c, 0x1d, 0x1e, 0x1f, /* ICMP padding */ 0x20, 0x21, 0x22, 0x23, 0x24, 0x25, 0x26, 0x27, /* ICMP padding */ 0x28, 0x29, 0x2a, 0x2b, 0x2c, 0x2d, 0x2e, 0x2f, /* ICMP padding */ 0x30, 0x31, 0x32, 0x33, 0x34, 0x35, 0x36, 0x37 /* ICMP padding */ }; /* * Wake up userland when space is available to transmit additional packets. */ static void nmk_wake_tx(struct netmap_adapter *na) { //na->tx_rings[0].nr_kflags |= NKR_PENDINTR; selwakeuppri(&na->tx_rings[0].si, PI_NET); selwakeuppri(&na->tx_si, PI_NET); } /* * User has packets to transmit - reconcile kernel view of transmit ring. */ static int nmk_txsync(struct ifnet *ifp, u_int ring_nr, int do_lock) { struct nmk_softc *sc = ifp->if_softc; struct netmap_adapter *na = NA(ifp); struct netmap_kring *kring = &na->tx_rings[ring_nr]; struct netmap_ring *ring = kring->ring; u_int kern = kring->nr_hwcur, user = ring->cur, n; u_int lim = kring->nkr_num_slots - 1; KASSERT(ring_nr = 0, ("%s: bad ring %d", __func__, ring_nr)); /* * In a real driver we'd pass the packets on to a deferred transmit * context (hardware or software). For this demo we just process it * here. Note that this removes a lot of effort manipulating ring * index / count. */ for (n = 0; kern != user; n++) { struct netmap_slot *slot = &ring->slot[kern]; void *addr; u_int i; sc->packets++; sc->bytes += slot->len; addr = NMB(slot); for (i = 0; i < slot->len; i++) sc->bytesum += ((u_char *)addr)[i]; kern = (kern == lim) ? 0 : kern + 1; } // printf("%s consumed %d .. %d\n", __func__, kring->nr_hwcur, kern); kring->nr_hwcur = kern; ring->avail = kring ->nr_hwavail; nmk_wake_tx(na); return 0; } /* * User has processed received packets and is ready for more. */ static int nmk_rxsync(struct ifnet *ifp, u_int ring_nr, int do_lock) { struct netmap_adapter *na = NA(ifp); struct netmap_kring *kring = &na->rx_rings[ring_nr]; struct netmap_ring *ring = kring->ring; u_int kern; u_int user; u_int n; u_int lim = kring->nkr_num_slots - 1; u_int resvd = ring->reserved; KASSERT(ring_nr = 0, ("%s: bad ring %d", __func__, ring_nr)); /* * Pass newly received frames to user - nothing to do here, since * frames are put directly into the ring in nmk_timer. */ /* Process any frames userspace has released. */ user = ring->cur; if (resvd > 0) { user = (user >= resvd) ? user - resvd : user + lim + 1 - resvd; } kern = kring->nr_hwcur; for (n = 0; kern != user; n++) { struct netmap_slot *slot = &ring->slot[kern]; if (slot->flags & NS_BUF_CHANGED) slot->flags &= NS_BUF_CHANGED; kern = (kern == lim) ? 0 : kern + 1; } kring->nr_hwavail -= n; kring->nr_hwcur = kern; ring->avail = kring->nr_hwavail; return (0); } /* * Lock wrapper. We could allow netmap to handle the locking for us, but for * the purpose of this example we do it explicitly. */ static void nmk_lock(struct ifnet *ifp, int what, u_int ring_nr) { struct nmk_softc *sc = ifp->if_softc; KASSERT(ring_nr = 0, ("%s: bad queue %d", __func__, ring_nr)); switch (what) { case NETMAP_CORE_LOCK: case NETMAP_TX_LOCK: case NETMAP_RX_LOCK: mtx_lock(&sc->nmk_mtx); break; case NETMAP_CORE_UNLOCK: case NETMAP_TX_UNLOCK: case NETMAP_RX_UNLOCK: mtx_unlock(&sc->nmk_mtx); break; } } /* * Insert the data specified by buf and len into the single rx ring in na. */ static int nmk_put_packet(struct netmap_adapter *na, const void *buf, u_int len) { struct netmap_kring *kring = &na->rx_rings[0]; struct netmap_ring *ring = kring->ring; struct netmap_slot *slot; u_int lim = kring->nkr_num_slots - 1; u_int kern = kring->nr_hwcur + kring->nr_hwavail; if (kring->nr_hwavail + ring->reserved >= lim) return (ENOBUFS); slot = &ring->slot[kern]; bcopy(buf, NMB(slot), len); slot->len = len; kring->nr_hwavail++; ring->avail = kring->nr_hwavail - ring->reserved; /* XXX make a DTrace SDT probe */ // printf("packet at slot=%u cur=%u avail=%u res=%u\n", kern, ring->cur, ring->avail, ring->reserved); return (0); } /* * Wake up userland when we have packets in the rx ring. */ static void nmk_wake_rx(struct netmap_adapter *na) { na->rx_rings[0].nr_kflags |= NKR_PENDINTR; selwakeuppri(&na->rx_rings[0].si, PI_NET); selwakeuppri(&na->rx_si, PI_NET); } /* * Periodic callback to simulate packet reception. */ static void nmk_timer(void *arg) { struct nmk_softc *sc = (struct nmk_softc *)arg; struct ifnet *ifp = sc->ifp; struct netmap_adapter *na = NA(ifp); int err; err = nmk_put_packet(na, canned_packet, sizeof(canned_packet)); if (err) printf("%s: failed to put packet (%d)", __func__, err); nmk_wake_rx(na); callout_reset(&sc->timer, hz, nmk_timer, sc); } static int nmk_register(struct ifnet *ifp, int onoff) { struct nmk_softc *sc = ifp->if_softc; struct netmap_adapter *na = NA(ifp); struct netmap_slot *slot; int error = 0; if (na == NULL) return (EINVAL); if (onoff) { ifp->if_capenable |= IFCAP_NETMAP; slot = netmap_reset(na, NR_RX, 0, 0); if (slot) { } callout_reset(&sc->timer, hz, nmk_timer, sc); } else { callout_stop(&sc->timer); ifp->if_capenable &= ~IFCAP_NETMAP; } return (error); } static void nmk_attach(struct nmk_softc *sc) { struct netmap_adapter na; bzero(&na, sizeof(na)); na.ifp = sc->ifp; na.num_tx_desc = 1024; na.num_rx_desc = 1024; na.nm_txsync = nmk_txsync; na.nm_rxsync = nmk_rxsync; na.nm_lock = nmk_lock; na.nm_register = nmk_register; netmap_attach(&na, 1); } static void nmk_detach(struct nmk_softc *sc) { ether_ifdetach(sc->ifp); callout_drain(&sc->timer); netmap_detach(sc->ifp); } static void nmk_create(void) { struct ifnet *ifp = NULL; uint32_t macaddr_mid; uint16_t macaddr_hi; static int unit = 0; u_char eaddr[6]; struct nmk_softc *sc; if (unit > 0) return; ifp = if_alloc(IFT_ETHER); if (ifp == NULL) return; if_initname(ifp, "nmk", unit); ifp->if_mtu = ETHERMTU; ifp->if_flags = IFF_BROADCAST | IFF_SIMPLEX | IFF_MULTICAST; sc = malloc(sizeof(*sc), M_NETMAP_KTEST, M_WAITOK | M_ZERO); ifp->if_softc = sc; mtx_init(&sc->nmk_mtx, "nmk0", "netmap kernel test lock", MTX_DEF); callout_init_mtx(&sc->timer, &sc->nmk_mtx, 0); /* Fake MAC address: 00 bd xx xx xx unit - from if_tap.c */ macaddr_hi = htons(0x00bd); macaddr_mid = (uint32_t)ticks; bcopy(&macaddr_hi, eaddr, 2); bcopy(&macaddr_mid, &eaddr[2], 4); eaddr[5] = unit; ether_ifattach(ifp, (void *)eaddr); sc->ifp = ifp; nmk_attach(sc); unit++; g_nmk_sc = sc; return; } static void nmk_destroy(void) { struct nmk_softc *sc = g_nmk_sc; nmk_detach(sc); if_free(sc->ifp); mtx_destroy(&sc->nmk_mtx); free(sc, M_NETMAP_KTEST); } /* * Module event handler */ static int nmk_mod_event(module_t mod, int cmd, void *arg) { int error; error = 0; switch (cmd) { case MOD_LOAD: nmk_create(); break; case MOD_UNLOAD: nmk_destroy(); break; case MOD_SHUTDOWN: case MOD_QUIESCE: default: error = EOPNOTSUPP; break; } return (error); } static moduledata_t mod_data = { "netmap-ktest", nmk_mod_event, 0 }; DECLARE_MODULE(netmap_ktest, mod_data, SI_SUB_PSEUDO, SI_ORDER_ANY);