From nobody Mon May 20 13:46:43 2024 X-Original-To: dev-commits-src-all@mlmmj.nyi.freebsd.org Received: from mx1.freebsd.org (mx1.freebsd.org [IPv6:2610:1c1:1:606c::19:1]) by mlmmj.nyi.freebsd.org (Postfix) with ESMTP id 4Vjf4N3FMYz5LJRM; Mon, 20 May 2024 13:46:44 +0000 (UTC) (envelope-from git@FreeBSD.org) Received: from mxrelay.nyi.freebsd.org (mxrelay.nyi.freebsd.org [IPv6:2610:1c1:1:606c::19:3]) (using TLSv1.3 with cipher TLS_AES_256_GCM_SHA384 (256/256 bits) key-exchange X25519 server-signature RSA-PSS (4096 bits) server-digest SHA256 client-signature RSA-PSS (4096 bits) client-digest SHA256) (Client CN "mxrelay.nyi.freebsd.org", Issuer "R3" (verified OK)) by mx1.freebsd.org (Postfix) with ESMTPS id 4Vjf4N10Bdz4Qtw; Mon, 20 May 2024 13:46:44 +0000 (UTC) (envelope-from git@FreeBSD.org) DKIM-Signature: v=1; a=rsa-sha256; c=relaxed/relaxed; d=freebsd.org; s=dkim; t=1716212804; h=from:from:reply-to:subject:subject:date:date:message-id:message-id: to:to:cc:mime-version:mime-version:content-type:content-type: content-transfer-encoding:content-transfer-encoding; bh=zthJzFAa8t2xDdvWv8ygnQbWttVsUo5NW48BsLSpWJ8=; b=muUUywWmI5yejyDZ1kYXMmwTjlQm9igYOttL8WVn68qI9D+WU6X1NoaSOGfr38G3GeL4A8 +2ENZb/ONSKSQn65DmtbDRMfDSe6rPKat9kcTYeVkmShVgdE8IqhSdmyUMi/riGXoP8X11 /jZCFu4i8Yst/ql4JXmbPiyg5awWMDqXlLbP4szMlYerN5pCjygeXfb4Fssx4OjjdjZwVR kkSW5DydbjQ4/+YCBpw+qLBs/RAkzGSM6Gd7OyCkeN17OlC2ScpvGPfkpJY+Sgy785s7AV BPH3B/NCN7KTE3fvI7R7EldE46I3fEQ2i8U66YZexyfUI/N69y81y/qFkGxeWw== ARC-Seal: i=1; s=dkim; d=freebsd.org; t=1716212804; a=rsa-sha256; cv=none; b=lKGrq/xSKPyM0pRjxLSqQVH3/Y+d+tgEx22OERXX4C1S1bL36o6zc4DC2EIgMIm76hZ4b9 VWK3UAVMb9Aw4ma2XOQaJ8C1Sklc+IMbWyh3ZccnUFdWECECy+5wCJREPpc40gadWpD2fQ 4qWqmwTD9aUVRgOpqf6H1iVB6GHY9LhQce4X5aQLxSBOYmLq1RHG7yffKEzcbWIiXZyb4Y 94gisMQl31UP1713C5ds7YCBTIc103KrdOJGfuOQfGMHh+AkCgMUjPwgdP6z2u+/X5DJV7 P3e2aq48bb/Cs13NANZC5t4mhLnowFsY0OS7RjLuRBCu8UvWuE5PNZM65vdPxA== ARC-Authentication-Results: i=1; mx1.freebsd.org; none ARC-Message-Signature: i=1; a=rsa-sha256; c=relaxed/relaxed; d=freebsd.org; s=dkim; t=1716212804; h=from:from:reply-to:subject:subject:date:date:message-id:message-id: to:to:cc:mime-version:mime-version:content-type:content-type: content-transfer-encoding:content-transfer-encoding; bh=zthJzFAa8t2xDdvWv8ygnQbWttVsUo5NW48BsLSpWJ8=; b=SDqqyqFfKpHSO9zzVbpGDHo/kW7LlttN+WmX2IzTcdoUzCRS1QrEEA5luxcYIYtOPeZaBa PA8Nx1xZ4y4ZwhzePKg13fBha/38QZRke0Yf/S17kpjXTfwvVRcsOU3af60rJIeZpY0bZH 87GPDx4kudZFf+zdagHUTT2wjJtNREXcXn46txA9hbn0i78uNFTYwsEag9OAvhqTCYBqBa AlK6PXF6op8j2VVl/jk4BU9hD3g2O74hbpei/nAfu/M2P1/hrjjLoNKtRIEjWahRwpGT/w WBRJvzV6ST3AtTwqTBgy/qIhN6i+P7s+IRuq4uKxmLox5FFsBXw5IjiwQ6Vflw== Received: from gitrepo.freebsd.org (gitrepo.freebsd.org [IPv6:2610:1c1:1:6068::e6a:5]) (using TLSv1.3 with cipher TLS_AES_256_GCM_SHA384 (256/256 bits) key-exchange X25519 server-signature RSA-PSS (4096 bits) server-digest SHA256) (Client did not present a certificate) by mxrelay.nyi.freebsd.org (Postfix) with ESMTPS id 4Vjf4N0c8mzh1g; Mon, 20 May 2024 13:46:44 +0000 (UTC) (envelope-from git@FreeBSD.org) Received: from gitrepo.freebsd.org ([127.0.1.44]) by gitrepo.freebsd.org (8.17.1/8.17.1) with ESMTP id 44KDkhZd062911; Mon, 20 May 2024 13:46:43 GMT (envelope-from git@gitrepo.freebsd.org) Received: (from git@localhost) by gitrepo.freebsd.org (8.17.1/8.17.1/Submit) id 44KDkhS8062908; Mon, 20 May 2024 13:46:43 GMT (envelope-from git) Date: Mon, 20 May 2024 13:46:43 GMT Message-Id: <202405201346.44KDkhS8062908@gitrepo.freebsd.org> To: src-committers@FreeBSD.org, dev-commits-src-all@FreeBSD.org, dev-commits-src-branches@FreeBSD.org From: Mark Johnston Subject: git: 160e7a4c16eb - stable/14 - wg: Add netmap support List-Id: Commit messages for all branches of the src repository List-Archive: https://lists.freebsd.org/archives/dev-commits-src-all List-Help: List-Post: List-Subscribe: List-Unsubscribe: X-BeenThere: dev-commits-src-all@freebsd.org Sender: owner-dev-commits-src-all@FreeBSD.org MIME-Version: 1.0 Content-Type: text/plain; charset=utf-8 Content-Transfer-Encoding: 8bit X-Git-Committer: markj X-Git-Repository: src X-Git-Refname: refs/heads/stable/14 X-Git-Reftype: branch X-Git-Commit: 160e7a4c16ebbec4211a941a706778daf8ea62bd Auto-Submitted: auto-generated The branch stable/14 has been updated by markj: URL: https://cgit.FreeBSD.org/src/commit/?id=160e7a4c16ebbec4211a941a706778daf8ea62bd commit 160e7a4c16ebbec4211a941a706778daf8ea62bd Author: Mark Johnston AuthorDate: 2024-04-20 16:01:28 +0000 Commit: Mark Johnston CommitDate: 2024-05-20 13:42:35 +0000 wg: Add netmap support When in netmap (emulated) mode, wireguard interfaces prepend or strip a dummy ethernet header when interfacing with netmap. The netmap application thus sees unencrypted, de-encapsulated frames with a fixed header. In this mode, netmap hooks the if_input and if_transmit routines of the ifnet. Packets from the host TX ring are handled by wg_if_input(), which simply hands them to the netisr layer; packets which would otherwise be tunneled are intercepted in wg_output() and placed in the host RX ring. The "physical" TX ring is processed by wg_transmit(), which behaves identically to wg_output() when netmap is not enabled, and packets appear in the "physical" RX ring by hooking wg_deliver_in(). Reviewed by: vmaffione MFC after: 1 month Sponsored by: Klara, Inc. Sponsored by: Zenarmor Differential Revision: https://reviews.freebsd.org/D43460 (cherry picked from commit bf454ca88bdf4acfa873386e876ff5e772e6a830) --- share/man/man4/wg.4 | 14 +++++ sys/dev/wg/if_wg.c | 155 ++++++++++++++++++++++++++++++++++++++++++++++++++-- 2 files changed, 163 insertions(+), 6 deletions(-) diff --git a/share/man/man4/wg.4 b/share/man/man4/wg.4 index d0d871e52220..05d6961a9610 100644 --- a/share/man/man4/wg.4 +++ b/share/man/man4/wg.4 @@ -121,6 +121,19 @@ as follows: 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 @@ is not assigned to the allowed IPs of Peer X. .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 index 30429c3725cd..552f47f9645b 100644 --- a/sys/dev/wg/if_wg.c +++ b/sys/dev/wg/if_wg.c @@ -1672,6 +1672,31 @@ error: } } +#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) { @@ -1680,6 +1705,7 @@ wg_deliver_in(struct wg_peer *peer) 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) @@ -1705,19 +1731,25 @@ wg_deliver_in(struct wg_peer *peer) 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); @@ -2162,13 +2194,36 @@ determine_af_and_pullup(struct mbuf **m, sa_family_t *af) 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; @@ -2178,14 +2233,94 @@ wg_transmit(if_t ifp, struct mbuf *m) 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) { @@ -2204,6 +2339,11 @@ wg_output(if_t ifp, struct mbuf *m, const struct sockaddr *dst, struct route *ro 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; @@ -2779,7 +2919,10 @@ wg_clone_create(struct if_clone *ifc, char *name, size_t len, 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);