Page MenuHomeFreeBSD

D43460.diff
No OneTemporary

D43460.diff

diff --git a/share/man/man4/wg.4 b/share/man/man4/wg.4
--- a/share/man/man4/wg.4
+++ b/share/man/man4/wg.4
@@ -121,6 +121,19 @@
Although a valid Curve25519 key must have 5 bits set to
specific values, this is done by the interface and so it
will accept any random 32-byte base64 string.
+.Sh NETMAP
+.Xr netmap 4
+applications may open a WireGuard interface in emulated mode.
+The netmap application will receive decrypted, unencapsulated packets prepended
+by a dummy Ethernet header.
+The Ethertype field will be one of
+.Dv ETHERTYPE_IP
+or
+.Dv ETHERTYPE_IPV6
+depending on the address family of the packet.
+Packets transmitted by the application should similarly begin with a dummy
+Ethernet header; this header will be stripped before the packet is encrypted
+and tunneled.
.Sh EXAMPLES
Create a
.Nm
@@ -183,6 +196,7 @@
.Xr ip 4 ,
.Xr ipsec 4 ,
.Xr netintro 4 ,
+.Xr netmap 4 ,
.Xr ovpn 4 ,
.Xr ipf 5 ,
.Xr pf.conf 5 ,
diff --git a/sys/dev/wg/if_wg.c b/sys/dev/wg/if_wg.c
--- a/sys/dev/wg/if_wg.c
+++ b/sys/dev/wg/if_wg.c
@@ -1674,6 +1674,31 @@
}
}
+#ifdef DEV_NETMAP
+/*
+ * Hand a packet to the netmap RX ring, via netmap's
+ * freebsd_generic_rx_handler().
+ */
+static void
+wg_deliver_netmap(if_t ifp, struct mbuf *m, int af)
+{
+ struct ether_header *eh;
+
+ M_PREPEND(m, ETHER_HDR_LEN, M_NOWAIT);
+ if (__predict_false(m == NULL)) {
+ if_inc_counter(ifp, IFCOUNTER_IQDROPS, 1);
+ return;
+ }
+
+ eh = mtod(m, struct ether_header *);
+ eh->ether_type = af == AF_INET ?
+ htons(ETHERTYPE_IP) : htons(ETHERTYPE_IPV6);
+ memcpy(eh->ether_shost, "\x02\x02\x02\x02\x02\x02", ETHER_ADDR_LEN);
+ memcpy(eh->ether_dhost, "\xff\xff\xff\xff\xff\xff", ETHER_ADDR_LEN);
+ if_input(ifp, m);
+}
+#endif
+
static void
wg_deliver_in(struct wg_peer *peer)
{
@@ -1682,6 +1707,7 @@
struct wg_packet *pkt;
struct mbuf *m;
struct epoch_tracker et;
+ int af;
while ((pkt = wg_queue_dequeue_serial(&peer->p_decrypt_serial)) != NULL) {
if (atomic_load_acq_int(&pkt->p_state) != WG_PACKET_CRYPTED)
@@ -1707,19 +1733,25 @@
if (m->m_pkthdr.len == 0)
goto done;
- MPASS(pkt->p_af == AF_INET || pkt->p_af == AF_INET6);
+ af = pkt->p_af;
+ MPASS(af == AF_INET || af == AF_INET6);
pkt->p_mbuf = NULL;
m->m_pkthdr.rcvif = ifp;
NET_EPOCH_ENTER(et);
- BPF_MTAP2_AF(ifp, m, pkt->p_af);
+ BPF_MTAP2_AF(ifp, m, af);
CURVNET_SET(if_getvnet(ifp));
M_SETFIB(m, if_getfib(ifp));
- if (pkt->p_af == AF_INET)
+#ifdef DEV_NETMAP
+ if ((if_getcapenable(ifp) & IFCAP_NETMAP) != 0)
+ wg_deliver_netmap(ifp, m, af);
+ else
+#endif
+ if (af == AF_INET)
netisr_dispatch(NETISR_IP, m);
- if (pkt->p_af == AF_INET6)
+ else if (af == AF_INET6)
netisr_dispatch(NETISR_IPV6, m);
CURVNET_RESTORE();
NET_EPOCH_EXIT(et);
@@ -2164,13 +2196,36 @@
return (0);
}
+#ifdef DEV_NETMAP
+static int
+determine_ethertype_and_pullup(struct mbuf **m, int *etp)
+{
+ struct ether_header *eh;
+
+ *m = m_pullup(*m, sizeof(struct ether_header));
+ if (__predict_false(*m == NULL))
+ return (ENOBUFS);
+ eh = mtod(*m, struct ether_header *);
+ *etp = ntohs(eh->ether_type);
+ if (*etp != ETHERTYPE_IP && *etp != ETHERTYPE_IPV6)
+ return (EAFNOSUPPORT);
+ return (0);
+}
+
+/*
+ * This should only be invoked by netmap, via nm_os_generic_xmit_frame(), to
+ * transmit packets from the netmap TX ring.
+ */
static int
wg_transmit(if_t ifp, struct mbuf *m)
{
sa_family_t af;
- int ret;
+ int et, ret;
struct mbuf *defragged;
+ KASSERT((if_getcapenable(ifp) & IFCAP_NETMAP) != 0,
+ ("%s: ifp %p is not in netmap mode", __func__, ifp));
+
defragged = m_defrag(m, M_NOWAIT);
if (defragged)
m = defragged;
@@ -2180,14 +2235,94 @@
return (ENOBUFS);
}
+ ret = determine_ethertype_and_pullup(&m, &et);
+ if (ret) {
+ xmit_err(ifp, m, NULL, AF_UNSPEC);
+ return (ret);
+ }
+ m_adj(m, sizeof(struct ether_header));
+
ret = determine_af_and_pullup(&m, &af);
if (ret) {
xmit_err(ifp, m, NULL, AF_UNSPEC);
return (ret);
}
- return (wg_xmit(ifp, m, af, if_getmtu(ifp)));
+
+ /*
+ * netmap only gets to see transient errors, since it handles errors by
+ * refusing to advance the transmit ring and retrying later.
+ */
+ ret = wg_xmit(ifp, m, af, if_getmtu(ifp));
+ if (ret == ENOBUFS)
+ return (ret);
+ return (0);
}
+/*
+ * This should only be invoked by netmap, via nm_os_send_up(), to process
+ * packets from the host TX ring.
+ */
+static void
+wg_if_input(if_t ifp, struct mbuf *m)
+{
+ int et;
+
+ KASSERT((if_getcapenable(ifp) & IFCAP_NETMAP) != 0,
+ ("%s: ifp %p is not in netmap mode", __func__, ifp));
+
+ if (determine_ethertype_and_pullup(&m, &et) != 0) {
+ if_inc_counter(ifp, IFCOUNTER_IERRORS, 1);
+ m_freem(m);
+ return;
+ }
+ CURVNET_SET(if_getvnet(ifp));
+ switch (et) {
+ case ETHERTYPE_IP:
+ m_adj(m, sizeof(struct ether_header));
+ netisr_dispatch(NETISR_IP, m);
+ break;
+ case ETHERTYPE_IPV6:
+ m_adj(m, sizeof(struct ether_header));
+ netisr_dispatch(NETISR_IPV6, m);
+ break;
+ default:
+ __assert_unreachable();
+ }
+ CURVNET_RESTORE();
+}
+
+/*
+ * Deliver a packet to the host RX ring. Because the interface is in netmap
+ * mode, the if_transmit() call should pass the packet to netmap_transmit().
+ */
+static int
+wg_xmit_netmap(if_t ifp, struct mbuf *m, int af)
+{
+ struct ether_header *eh;
+
+ if (__predict_false(if_tunnel_check_nesting(ifp, m, MTAG_WGLOOP,
+ MAX_LOOPS))) {
+ printf("%s:%d\n", __func__, __LINE__);
+ if_inc_counter(ifp, IFCOUNTER_IERRORS, 1);
+ m_freem(m);
+ return (ELOOP);
+ }
+
+ M_PREPEND(m, ETHER_HDR_LEN, M_NOWAIT);
+ if (__predict_false(m == NULL)) {
+ if_inc_counter(ifp, IFCOUNTER_IQDROPS, 1);
+ return (ENOBUFS);
+ }
+
+ eh = mtod(m, struct ether_header *);
+ eh->ether_type = af == AF_INET ?
+ htons(ETHERTYPE_IP) : htons(ETHERTYPE_IPV6);
+ memcpy(eh->ether_shost, "\x06\x06\x06\x06\x06\x06", ETHER_ADDR_LEN);
+ memcpy(eh->ether_dhost, "\xff\xff\xff\xff\xff\xff", ETHER_ADDR_LEN);
+ return (if_transmit(ifp, m));
+}
+#endif /* DEV_NETMAP */
+
static int
wg_output(if_t ifp, struct mbuf *m, const struct sockaddr *dst, struct route *ro)
{
@@ -2206,6 +2341,11 @@
return (EAFNOSUPPORT);
}
+#ifdef DEV_NETMAP
+ if ((if_getcapenable(ifp) & IFCAP_NETMAP) != 0)
+ return (wg_xmit_netmap(ifp, m, af));
+#endif
+
defragged = m_defrag(m, M_NOWAIT);
if (defragged)
m = defragged;
@@ -2781,7 +2921,10 @@
if_setinitfn(ifp, wg_init);
if_setreassignfn(ifp, wg_reassign);
if_setqflushfn(ifp, wg_qflush);
+#ifdef DEV_NETMAP
if_settransmitfn(ifp, wg_transmit);
+ if_setinputfn(ifp, wg_if_input);
+#endif
if_setoutputfn(ifp, wg_output);
if_setioctlfn(ifp, wg_ioctl);
if_attach(ifp);

File Metadata

Mime Type
text/plain
Expires
Sun, Nov 17, 1:33 AM (21 h, 44 m)
Storage Engine
blob
Storage Format
Raw Data
Storage Handle
14668786
Default Alt Text
D43460.diff (6 KB)

Event Timeline