diff --git a/share/man/man4/tap.4 b/share/man/man4/tap.4 --- a/share/man/man4/tap.4 +++ b/share/man/man4/tap.4 @@ -1,7 +1,7 @@ .\" $FreeBSD$ .\" Based on PR#2411 .\" -.Dd January 13, 2020 +.Dd March 10, 2023 .Dt TAP 4 .Os .Sh NAME @@ -313,7 +313,16 @@ VMware .Dv SIOCSIFFLAGS . .El +.Ss NETMAP +.Xr netmap 4 +applications may open +.Nm +interfaces. +Packets written to the interface are placed unmodified on the RX ring, and +packets written to the host TX ring are handled exactly as if they had +been written to the interface directly. .Sh SEE ALSO .Xr inet 4 , .Xr intro 4 , +.Xr netmap 4 , .Xr tun 4 diff --git a/share/man/man4/tun.4 b/share/man/man4/tun.4 --- a/share/man/man4/tun.4 +++ b/share/man/man4/tun.4 @@ -2,7 +2,7 @@ .\" $FreeBSD$ .\" Based on PR#2411 .\" -.Dd March 17, 2020 +.Dd March 10, 2023 .Dt TUN 4 .Os .Sh NAME @@ -328,6 +328,31 @@ If the interface is up when the data device is not open output packets are always thrown away rather than letting them pile up. +.Sh NETMAP +.Nm +interfaces can be opened by +.Xr netmap 4 +applications. +In this configuration, +.Nm +will insert fake Ethernet headers into received packets before they are +handed to +.Xr netmap 4 . +The ethertype will be one of +.Dv ETHERTYPE_IP +or +.Dv ETHERTYPE_IPV6 +depending on the type of packet written to the interface. +The source MAC address will be +.Dq 02:02:02:02:02:02 +and the destination MAC address will be +.Dq 06:06:06:06:06:06 . +.Pp +When a +.Xr netmap 4 +application submits a packet to the host ring, the ethertype is used to +determine whether the packet is to be treated as an IPv4 or IPv6 packet, +and the Ethernet header is stripped before any further processing is done. .Sh SEE ALSO .Xr ioctl 2 , .Xr read 2 , @@ -336,6 +361,7 @@ .Xr devname 3 , .Xr inet 4 , .Xr intro 4 , +.Xr netmap 4 , .Xr pty 4 , .Xr tap 4 , .Xr ifconfig 8 diff --git a/sys/net/if_tuntap.c b/sys/net/if_tuntap.c --- a/sys/net/if_tuntap.c +++ b/sys/net/if_tuntap.c @@ -226,6 +226,7 @@ static void tunrename(void *arg, struct ifnet *ifp); static int tunifioctl(struct ifnet *, u_long, caddr_t); static void tuninit(struct ifnet *); +static void tuninput(if_t ifp, struct mbuf *m); static void tunifinit(void *xtp); static int tuntapmodevent(module_t, int, void *); static int tunoutput(struct ifnet *, struct mbuf *, @@ -978,6 +979,7 @@ ifp->if_mtu = TUNMTU; ifp->if_start = tunstart; ifp->if_output = tunoutput; + ifp->if_input = tuninput; ifp->if_snd.ifq_drv_maxlen = 0; IFQ_SET_READY(&ifp->if_snd); @@ -1369,6 +1371,48 @@ return (error); } +static void +tuninput(if_t ifp, struct mbuf *m) +{ + struct ether_header *eh; + u_int proto; + + KASSERT(if_getcapenable(ifp) & IFCAP_NETMAP, + ("%s: ifnet %s not in netmap mode", __func__, if_name(ifp))); + M_ASSERTPKTHDR(m); + + if (__predict_false(m->m_len < ETHER_HDR_LEN)) { + m = m_pullup(m, ETHER_HDR_LEN); + if (m == NULL) { + if_inc_counter(ifp, IFCOUNTER_IERRORS, 1); + return; + } + } + + eh = mtod(m, struct ether_header *); + switch (ntohs(eh->ether_type)) { + case ETHERTYPE_IP: + proto = NETISR_IP; + break; + case ETHERTYPE_IPV6: + proto = NETISR_IPV6; + break; + default: + if_inc_counter(ifp, IFCOUNTER_IERRORS, 1); + m_freem(m); + return; + } + + m_adj(m, ETHER_HDR_LEN); + if_inc_counter(ifp, IFCOUNTER_IPACKETS, 1); + if_inc_counter(ifp, IFCOUNTER_IBYTES, m->m_pkthdr.len); + + M_SETFIB(m, ifp->if_fib); + CURVNET_SET(ifp->if_vnet); + netisr_dispatch(proto, m); + CURVNET_RESTORE(); +} + /* * tunoutput - queue packets from higher level ready to put out. */ @@ -1791,12 +1835,38 @@ return (0); } +#ifdef DEV_NETMAP +/* + * Pass L3 packets to netmap. Create a dummy ethernet header for this purpose, + * with hard-coded source and destination addresses. + */ +static int +tunwrite_netmap(if_t ifp, struct mbuf *m, int isr) +{ + struct ether_header *eh; + + KASSERT(isr == NETISR_IP || isr == NETISR_IPV6, + ("%s: unexpected isr %d", __func__, isr)); + + M_PREPEND(m, ETHER_HDR_LEN, M_NOWAIT); + if (m == NULL) + return (ENOBUFS); + + eh = mtod(m, struct ether_header *); + eh->ether_type = htons(isr == NETISR_IP ? ETHERTYPE_IP : ETHERTYPE_IPV6); + memcpy(eh->ether_shost, "\x02\x02\x02\x02\x02\x02", ETHER_ADDR_LEN); + memcpy(eh->ether_dhost, "\x06\x06\x06\x06\x06\x06", ETHER_ADDR_LEN); + if_input(ifp, m); + return (0); +} +#endif + static int tunwrite_l3(struct tuntap_softc *tp, struct mbuf *m) { struct epoch_tracker et; struct ifnet *ifp; - int family, isr; + int error, family, isr; ifp = TUN2IFP(tp); /* Could be unlocked read? */ @@ -1835,11 +1905,18 @@ if_inc_counter(ifp, IFCOUNTER_IPACKETS, 1); CURVNET_SET(ifp->if_vnet); M_SETFIB(m, ifp->if_fib); + + error = 0; NET_EPOCH_ENTER(et); - netisr_dispatch(isr, m); +#ifdef DEV_NETMAP + if ((if_getcapenable(ifp) & IFCAP_NETMAP) != 0) + error = tunwrite_netmap(ifp, m, isr); + else +#endif + netisr_dispatch(isr, m); NET_EPOCH_EXIT(et); CURVNET_RESTORE(); - return (0); + return (error); } /*