aboutsummaryrefslogtreecommitdiffstats
path: root/src
diff options
context:
space:
mode:
authorJason A. Donenfeld <Jason@zx2c4.com>2021-04-25 19:55:12 -0400
committerJason A. Donenfeld <Jason@zx2c4.com>2021-04-27 22:49:04 -0400
commite2ea5947743ae4d7faa50459136b6553cbb6b3b8 (patch)
treed009431291ce07b1d7851afd141670ee5274b2f7 /src
parentif_wg: do not increment error counter when sc is null (diff)
downloadwireguard-freebsd-e2ea5947743ae4d7faa50459136b6553cbb6b3b8.tar.xz
wireguard-freebsd-e2ea5947743ae4d7faa50459136b6553cbb6b3b8.zip
if_wg: handle if_transmit and if_output properly
The netmap code goes directly to if_transmit, which means it'll bypass if_output, in which case, there's no packet allocated. Also, we're relying on if_output's sockaddr structure to be legit, but who knows what types of userspace hijynxes can forge this. Rather than relying on that kind of black magic, determine the AF from the actual packet contents. But still insist that it agrees with the sockaddr. The extraction of the type from AF_UNSPEC follows the same pattern as if_gif and if_gre. We also use this as an opportunity to send icmp error messages in the right place. Signed-off-by: Jason A. Donenfeld <Jason@zx2c4.com>
Diffstat (limited to 'src')
-rw-r--r--src/if_wg.c85
1 files changed, 62 insertions, 23 deletions
diff --git a/src/if_wg.c b/src/if_wg.c
index 19fccff..3b93db5 100644
--- a/src/if_wg.c
+++ b/src/if_wg.c
@@ -381,6 +381,7 @@ static void wg_input(struct mbuf *, int, struct inpcb *, const struct sockaddr *
static void wg_peer_send_staged(struct wg_peer *);
static int wg_clone_create(struct if_clone *, int, caddr_t);
static void wg_qflush(struct ifnet *);
+static int wg_xmit(struct ifnet *, struct mbuf *, sa_family_t, uint32_t);
static int wg_transmit(struct ifnet *, struct mbuf *);
static int wg_output(struct ifnet *, struct mbuf *, const struct sockaddr *, struct route *);
static void wg_clone_destroy(struct ifnet *);
@@ -1702,7 +1703,7 @@ wg_deliver_in(struct wg_peer *peer)
struct ifnet *ifp = sc->sc_ifp;
struct wg_packet *pkt;
struct mbuf *m;
- uint32_t af;
+ uint32_t bpf_tap_af;
bool data_recv = false;
while ((pkt = wg_queue_dequeue_serial(&peer->p_decrypt_serial)) != NULL) {
@@ -1735,8 +1736,8 @@ wg_deliver_in(struct wg_peer *peer)
m->m_pkthdr.rcvif = ifp;
- af = pkt->p_af;
- BPF_MTAP2(ifp, &af, sizeof(af), m);
+ bpf_tap_af = pkt->p_af;
+ BPF_MTAP2(ifp, &bpf_tap_af, sizeof(bpf_tap_af), m);
CURVNET_SET(ifp->if_vnet);
M_SETFIB(m, ifp->if_fib);
@@ -2061,32 +2062,35 @@ error:
}
static int
-wg_transmit(struct ifnet *ifp, struct mbuf *m)
+wg_xmit(struct ifnet *ifp, struct mbuf *m, sa_family_t af, uint32_t mtu)
{
- struct wg_packet *pkt = m->m_pkthdr.PH_loc.ptr;
+ struct wg_packet *pkt;
struct wg_softc *sc = ifp->if_softc;
struct wg_peer *peer;
- uint32_t af = pkt->p_af;
int rc = 0;
sa_family_t peer_af;
+ uint32_t bpf_tap_af;
/* Work around lifetime issue in the ipv6 mld code. */
- if (__predict_false((ifp->if_flags & IFF_DYING) || !sc)) {
- rc = ENXIO;
- goto err_free;
- }
+ if (__predict_false((ifp->if_flags & IFF_DYING) || !sc))
+ return (ENXIO);
- BPF_MTAP2(ifp, &af, sizeof(af), m);
+ if ((pkt = wg_packet_alloc(m)) == NULL)
+ return (ENOBUFS);
+ pkt->p_mtu = mtu;
+ pkt->p_af = bpf_tap_af = af;
- if (pkt->p_af == AF_INET) {
+ if (af == AF_INET) {
peer = wg_aip_lookup(sc, AF_INET, &mtod(m, struct ip *)->ip_dst);
- } else if (pkt->p_af == AF_INET6) {
+ } else if (af == AF_INET6) {
peer = wg_aip_lookup(sc, AF_INET6, &mtod(m, struct ip6_hdr *)->ip6_dst);
} else {
rc = EAFNOSUPPORT;
goto err_counter;
}
+ BPF_MTAP2(ifp, &bpf_tap_af, sizeof(bpf_tap_af), m);
+
if (__predict_false(peer == NULL)) {
rc = ENOKEY;
goto err_counter;
@@ -2115,25 +2119,60 @@ err_peer:
noise_remote_put(peer->p_remote);
err_counter:
if_inc_counter(sc->sc_ifp, IFCOUNTER_OERRORS, 1);
- /* TODO: send ICMP unreachable? */
-err_free:
+ if (af == AF_INET) {
+ icmp_error(m, ICMP_UNREACH, ICMP_UNREACH_HOST, 0, 0);
+ pkt->p_mbuf = NULL;
+ } else if (af == AF_INET6) {
+ icmp6_error(m, ICMP6_DST_UNREACH, 0, 0);
+ pkt->p_mbuf = NULL;
+ }
wg_packet_free(pkt);
return (rc);
}
+static inline sa_family_t
+determine_af(struct mbuf *m)
+{
+ u_char ipv;
+ if (m->m_pkthdr.len < sizeof(struct ip))
+ return AF_UNSPEC;
+ ipv = mtod(m, struct ip *)->ip_v;
+ if (ipv == 4)
+ return AF_INET;
+ if (ipv == 6 && m->m_pkthdr.len >= sizeof(struct ip6_hdr))
+ return AF_INET6;
+ return AF_UNSPEC;
+}
+
static int
-wg_output(struct ifnet *ifp, struct mbuf *m, const struct sockaddr *sa, struct route *ro)
+wg_transmit(struct ifnet *ifp, struct mbuf *m)
{
- struct wg_packet *pkt;
+ sa_family_t af = determine_af(m);
- if ((pkt = wg_packet_alloc(m)) == NULL)
- return (ENOBUFS);
+ if (af == AF_UNSPEC) {
+ if_inc_counter(ifp, IFCOUNTER_OERRORS, 1);
+ m_freem(m);
+ return (EAFNOSUPPORT);
+ }
+ return (wg_xmit(ifp, m, af, ifp->if_mtu));
+}
- pkt->p_af = sa->sa_family;
- pkt->p_mtu = (ro != NULL && ro->ro_mtu > 0) ? ro->ro_mtu : ifp->if_mtu;
- m->m_pkthdr.PH_loc.ptr = pkt;
+static int
+wg_output(struct ifnet *ifp, struct mbuf *m, const struct sockaddr *dst, struct route *ro)
+{
+ uint32_t af, mtu;
- return (wg_transmit(ifp, m));
+ if (dst->sa_family == AF_UNSPEC)
+ memcpy(&af, dst->sa_data, sizeof(af));
+ else
+ af = dst->sa_family;
+ if (af == AF_UNSPEC || af != determine_af(m)) {
+ if_inc_counter(ifp, IFCOUNTER_OERRORS, 1);
+ m_freem(m);
+ return (EAFNOSUPPORT);
+ }
+ mtu = (ro != NULL && ro->ro_mtu > 0) ? ro->ro_mtu : ifp->if_mtu;
+ return (wg_xmit(ifp, m, af, mtu));
}
static int