summaryrefslogtreecommitdiff
diff options
context:
space:
mode:
-rw-r--r--sys/net/if_wg.c1991
-rw-r--r--sys/net/if_wg.h158
2 files changed, 0 insertions, 2149 deletions
diff --git a/sys/net/if_wg.c b/sys/net/if_wg.c
deleted file mode 100644
index 4f491c7d799..00000000000
--- a/sys/net/if_wg.c
+++ /dev/null
@@ -1,1991 +0,0 @@
-/* $OpenBSD: if_wg.c,v 1.1 2019/04/27 05:14:33 dlg Exp $ */
-
-/*
- * Copyright (c) 2018, 2019 David Gwynne <dlg@openbsd.org>
- *
- * Permission to use, copy, modify, and distribute this software for any
- * purpose with or without fee is hereby granted, provided that the above
- * copyright notice and this permission notice appear in all copies.
- *
- * THE SOFTWARE IS PROVIDED "AS IS" AND THE AUTHOR DISCLAIMS ALL WARRANTIES
- * WITH REGARD TO THIS SOFTWARE INCLUDING ALL IMPLIED WARRANTIES OF
- * MERCHANTABILITY AND FITNESS. IN NO EVENT SHALL THE AUTHOR BE LIABLE FOR
- * ANY SPECIAL, DIRECT, INDIRECT, OR CONSEQUENTIAL DAMAGES OR ANY DAMAGES
- * WHATSOEVER RESULTING FROM LOSS OF USE, DATA OR PROFITS, WHETHER IN AN
- * ACTION OF CONTRACT, NEGLIGENCE OR OTHER TORTIOUS ACTION, ARISING OUT OF
- * OR IN CONNECTION WITH THE USE OR PERFORMANCE OF THIS SOFTWARE.
- */
-
-#include <sys/param.h>
-#include <sys/kernel.h>
-#include <sys/proc.h>
-#include <sys/systm.h>
-#include <sys/mbuf.h>
-#include <sys/protosw.h>
-#include <sys/socket.h>
-#include <sys/ioctl.h>
-#include <sys/errno.h>
-#include <sys/syslog.h>
-#include <sys/selinfo.h>
-#include <sys/fcntl.h>
-#include <sys/time.h>
-#include <sys/device.h>
-#include <sys/vnode.h>
-#include <sys/poll.h>
-#include <sys/conf.h>
-#include <sys/file.h>
-#include <sys/filedesc.h>
-#include <sys/socketvar.h>
-
-#include <net/if.h>
-#include <net/if_var.h>
-#include <net/if_types.h>
-#include <net/rtable.h>
-#include <netinet/in.h>
-
-#include <netinet/ip_var.h>
-#include <netinet/udp.h>
-#include <netinet/udp_var.h>
-
-#include <netinet/ip.h>
-#include <net/route.h>
-#include <netinet/in_pcb.h>
-
-#include "bpfilter.h"
-#if NBPFILTER > 0
-#include <net/bpf.h>
-#endif
-
-#include <crypto/chacha.h>
-#include <crypto/poly1305.h>
-
-#include <net/if_wg.h>
-
-struct wg_data_hdr {
- uint32_t msg_type;
- uint32_t index;
- uint32_t counter_lo;
- uint32_t counter_hi;
-} __packed __aligned(4);
-
-CTASSERT(sizeof(struct chacha_key) == sizeof(struct wg_aead_key));
-
-#define DPRINTF(_ifp, _a...) do { \
- if (ISSET((_ifp)->if_flags, IFF_DEBUG)) { \
- printf("%s: ", (_ifp)->if_xname); \
- printf(_a); \
- printf("\n"); \
- } \
-} while (0)
-
-struct wg_data_keys {
- TAILQ_ENTRY(wg_data_keys)
- wk_entry;
- uint32_t wk_tx_idx;
- uint32_t wk_rx_idx;
- uint64_t wk_tx_seq;
- uint64_t wk_rx_seq;
- struct chacha_key wk_tx_key;
- struct chacha_key wk_rx_key;
-
- int wk_born;
- unsigned int wk_rekeyed;
-};
-
-TAILQ_HEAD(wg_data_keys_list, wg_data_keys);
-
-struct wg_device;
-
-struct wg_softc {
- unsigned int sc_unit; /* must be first */
- struct ifnet sc_if;
-
- RBT_ENTRY(wg_softc) sc_entry;
- TAILQ_ENTRY(wg_softc) sc_lentry;
- struct wg_device *sc_device;
-
- struct file * volatile sc_fp;
-
- unsigned int sc_initiator;
-
- int sc_up_stamp;
-
- int sc_rk_stamp;
- TAILQ_ENTRY(wg_softc) sc_rk_entry;
- unsigned int sc_rk_onqueue;
- struct timeout sc_rk_timer;
-
- struct mbuf_queue sc_tx_queue;
- struct task sc_tx_task;
-
- int sc_tx_stamp;
- struct timeout sc_tx_timer;
- struct task sc_tx_keepalive;
-
- /* sc_data_keys is used by wg_start under the ifq serialiaser */
- struct wg_data_keys *sc_tx_data_keys;
- /* the list is used in the rx path under the lock */
- struct rwlock sc_rx_data_keys_lk;
- struct wg_data_keys_list sc_rx_data_keys;
-
- int sc_rx_stamp;
- struct timeout sc_rx_timer;
-};
-
-RBT_HEAD(wg_if_tree, wg_softc);
-TAILQ_HEAD(wg_if_list, wg_softc);
-
-struct wg_device {
- dev_t wgd_dev; /* must be first */
- RBT_ENTRY(wg_device) wgd_entry;
- struct wg_if_list wgd_ifaces;
-
- struct mutex wgd_rk_mtx;
- struct wg_if_list wgd_rk_list;
- unsigned int wgd_rk_reading;
-
- struct selinfo wgd_rsel;
- struct selinfo wgd_wsel;
- struct mutex wgd_sel_mtx;
- int wgd_nbio;
-};
-
-RBT_HEAD(wg_dv_tree, wg_device);
-
-struct {
- struct rwlock wg_dv_lock;
- struct rwlock wg_if_lock;
- struct wg_dv_tree wg_dv_tree;
- struct wg_if_tree wg_if_tree;
-} wg_state = {
- RWLOCK_INITIALIZER("wgdevs"),
- RWLOCK_INITIALIZER("wgifs"),
- RBT_INITIALIZER(),
- RBT_INITIALIZER(),
-};
-
-static int wg_filt_read(struct knote *, long);
-static void wg_filt_read_detach(struct knote *);
-static int wg_filt_write(struct knote *, long);
-static void wg_filt_write_detach(struct knote *);
-
-static struct filterops wg_filtops_read = {
- 1, NULL, wg_filt_read_detach, wg_filt_read
-};
-
-static struct filterops wg_filtops_write = {
- 1, NULL, wg_filt_write_detach, wg_filt_write
-};
-
-static struct wg_device *
- wg_dev_lookup_locked(dev_t);
-static struct wg_device *
- wg_dev_lookup(dev_t);
-
-static inline int
- wg_if_cmp(const struct wg_softc *, const struct wg_softc *);
-static inline int
- wg_dv_cmp(const struct wg_device *, const struct wg_device *);
-
-RBT_PROTOTYPE(wg_if_tree, wg_softc, sc_entry, wg_if_cmp);
-RBT_PROTOTYPE(wg_dv_tree, wg_device, wgd_entry, wg_dv_cmp);
-
-struct wg_aead_ctx {
- struct chacha_stream
- chacha20;
- poly1305_state poly1305;
- size_t datalen;
-};
-
-struct wg_aead_tag {
- uint8_t tag[WG_POLY1305_TAG_LEN];
-} __packed __aligned(4);
-
-static void wg_aead_init(struct wg_aead_ctx *,
- const struct chacha_key *, uint64_t);
-static void wg_aead_encrypt(struct wg_aead_ctx *, void *, size_t len);
-static void wg_aead_verify(struct wg_aead_ctx *, void *, size_t len);
-static void wg_aead_decrypt(struct wg_aead_ctx *, void *, size_t len);
-static void wg_aead_final(struct wg_aead_ctx *, struct wg_aead_tag *);
-
-static int wg_up(struct wg_softc *);
-static int wg_down(struct wg_softc *);
-
-static struct mbuf *
- wg_input(void *, struct mbuf *, int);
-static int wg_output(struct ifnet *, struct mbuf *,
- struct sockaddr *, struct rtentry *rt);
-static void wg_start(struct ifqueue *);
-static int wg_ioctl(struct ifnet *, u_long, caddr_t);
-static void wg_send(void *);
-static void wg_link_state(struct wg_softc *, int);
-static void wg_link_up(struct wg_softc *);
-static void wg_link_down(struct wg_softc *, int);
-
-static void wg_rekey(struct wg_softc *, int);
-static void wg_send_keepalive(void *);
-static void wg_rekey_timer(void *);
-static void wg_tx_timer(void *);
-static void wg_rx_timer(void *);
-
-void
-wgattach(int n)
-{
-
-}
-
-int
-wgopen(dev_t dev, int flag, int mode, struct proc *p)
-{
- struct wg_device *wgd;
- int rv;
-
- rv = suser(p);
- if (rv != 0)
- return (rv);
-
- rv = rw_enter(&wg_state.wg_dv_lock, RW_WRITE | RW_INTR);
- if (rv != 0)
- return (rv);
-
- wgd = wg_dev_lookup_locked(dev);
- if (wgd != NULL) {
- rv = EBUSY;
- goto out;
- }
-
- wgd = malloc(sizeof(*wgd), M_DEVBUF, M_WAITOK|M_CANFAIL);
- if (wgd == NULL) {
- rv = ENOMEM;
- goto out;
- }
- wgd->wgd_dev = dev;
- memset(&wgd->wgd_rsel, 0, sizeof(wgd->wgd_rsel));
- memset(&wgd->wgd_wsel, 0, sizeof(wgd->wgd_wsel));
- mtx_init(&wgd->wgd_sel_mtx, IPL_SOFTNET);
- wgd->wgd_nbio = ISSET(flag, FNONBLOCK) ? IO_NDELAY : 0;
- TAILQ_INIT(&wgd->wgd_ifaces);
-
- mtx_init(&wgd->wgd_rk_mtx, IPL_SOFTNET);
- TAILQ_INIT(&wgd->wgd_rk_list);
- wgd->wgd_rk_reading = 0;
-
- if (RBT_INSERT(wg_dv_tree, &wg_state.wg_dv_tree, wgd) != NULL)
- panic("wg dev tree modified while lock was held");
-
-out:
- rw_exit(&wg_state.wg_dv_lock);
-
- return (rv);
-}
-
-int
-wgclose(dev_t dev, int flag, int mode, struct proc *p)
-{
- struct wg_device *wgd;
- struct wg_softc *sc;
- struct file *fp;
-
- rw_enter(&wg_state.wg_dv_lock, RW_WRITE);
- wgd = wg_dev_lookup_locked(dev);
- KASSERTMSG(wgd != NULL, "wg device missing in close");
-
- RBT_REMOVE(wg_dv_tree, &wg_state.wg_dv_tree, wgd);
- rw_exit(&wg_state.wg_dv_lock);
-
- rw_enter(&wg_state.wg_if_lock, RW_WRITE);
- TAILQ_FOREACH(sc, &wgd->wgd_ifaces, sc_lentry) {
- struct ifnet *ifp = &sc->sc_if;
-
- if (ifp->if_index != 0) {
- NET_LOCK();
- if (ISSET(ifp->if_flags, IFF_RUNNING))
- wg_down(sc);
- NET_UNLOCK();
-
- fp = sc->sc_fp;
- if (fp != NULL) {
- struct socket *so;
- struct inpcb *inp;
- int s;
-
- so = (struct socket *)fp->f_data;
- s = solock(so);
- inp = sotoinpcb(so);
- if (inp->inp_upcall != NULL) {
- inp->inp_upcall_arg = NULL;
- inp->inp_upcall = NULL;
- }
- sounlock(so, s);
-
- sc->sc_fp = NULL;
- FRELE(fp, p);
- }
-
- if_detach(ifp);
- }
-
- RBT_REMOVE(wg_if_tree, &wg_state.wg_if_tree, sc);
- }
- rw_exit(&wg_state.wg_if_lock);
-
- while ((sc = TAILQ_FIRST(&wgd->wgd_ifaces)) != NULL) {
- struct wg_data_keys *wk;
-
- TAILQ_REMOVE(&wgd->wgd_ifaces, sc, sc_lentry);
-
- while ((wk = TAILQ_FIRST(&sc->sc_rx_data_keys)) != NULL) {
- TAILQ_REMOVE(&sc->sc_rx_data_keys, wk, wk_entry);
- free(wk, M_DEVBUF, sizeof(*wk));
- }
-
- task_del(systq, &sc->sc_tx_task);
- mq_purge(&sc->sc_tx_queue);
-
- free(sc, M_DEVBUF, sizeof(*sc));
- }
-
- free(wgd, M_DEVBUF, sizeof(*wgd));
-
- return (0);
-}
-
-static struct wg_softc *
-wg_if_lookup(struct wg_device *wgd, unsigned int unit)
-{
- struct wg_softc *sc;
-
- sc = RBT_FIND(wg_if_tree, &wg_state.wg_if_tree,
- (struct wg_softc *)&unit);
- if (sc == NULL || sc->sc_device != wgd)
- return (NULL);
-
- return (sc);
-}
-
-static int
-wg_if_create(struct wg_device *wgd, unsigned int unit)
-{
- struct wg_softc *sc, *osc;
- int error;
-
- sc = malloc(sizeof(*sc), M_DEVBUF, M_WAITOK|M_CANFAIL|M_ZERO);
- if (sc == NULL)
- return (ENOMEM);
-
- sc->sc_unit = unit;
- sc->sc_device = wgd;
- sc->sc_initiator = 1;
- rw_init(&sc->sc_rx_data_keys_lk, "wgrxkeys");
- TAILQ_INIT(&sc->sc_rx_data_keys);
-
- mq_init(&sc->sc_tx_queue, 128, IPL_SOFTNET);
- task_set(&sc->sc_tx_task, wg_send, sc);
- task_set(&sc->sc_tx_keepalive, wg_send_keepalive, sc);
-
- timeout_set(&sc->sc_rk_timer, wg_rekey_timer, sc);
- timeout_set(&sc->sc_tx_timer, wg_tx_timer, sc);
- timeout_set(&sc->sc_rx_timer, wg_rx_timer, sc);
-
- error = rw_enter(&wg_state.wg_if_lock, RW_WRITE | RW_INTR);
- if (error != 0)
- goto fail;
-
- osc = RBT_INSERT(wg_if_tree, &wg_state.wg_if_tree, sc);
- if (osc != NULL) {
- error = (osc->sc_device == wgd) ? EEXIST : EBUSY;
- goto unlock;
- }
-
- TAILQ_INSERT_TAIL(&wgd->wgd_ifaces, sc, sc_lentry);
- rw_exit(&wg_state.wg_if_lock);
-
- return (0);
-unlock:
- rw_exit(&wg_state.wg_if_lock);
-fail:
- free(sc, M_DEVBUF, sizeof(*sc));
- return (error);
-}
-
-static int
-wg_if_destroy(struct wg_device *wgd, unsigned int unit)
-{
- struct wg_softc *sc;
- int error;
-
- error = rw_enter(&wg_state.wg_if_lock, RW_WRITE | RW_INTR);
- if (error != 0)
- goto fail;
-
- sc = wg_if_lookup(wgd, unit);
- if (sc == NULL) {
- error = ESRCH;
- goto unlock;
- }
-
- if (sc->sc_if.if_index != 0) {
- error = EBUSY;
- goto unlock;
- }
-
- TAILQ_REMOVE(&wgd->wgd_ifaces, sc, sc_lentry);
- RBT_REMOVE(wg_if_tree, &wg_state.wg_if_tree, sc);
- rw_exit(&wg_state.wg_if_lock);
-
- task_del(systq, &sc->sc_tx_task);
- mq_purge(&sc->sc_tx_queue);
-
- free(sc, M_DEVBUF, sizeof(*sc));
-
- return (0);
-
-unlock:
- rw_exit(&wg_state.wg_if_lock);
-fail:
- return (error);
-}
-
-static int
-wg_if_attach(struct wg_device *wgd, unsigned int unit)
-{
- struct wg_softc *sc;
- struct ifnet *ifp;
- int error;
-
- error = rw_enter(&wg_state.wg_if_lock, RW_WRITE | RW_INTR);
- if (error != 0)
- return (error);
-
- sc = wg_if_lookup(wgd, unit);
- if (sc == NULL) {
- error = ESRCH;
- goto unlock;
- }
-
- if (sc->sc_if.if_index != 0) {
- error = EBUSY;
- goto unlock;
- }
-
- ifp = &sc->sc_if;
- snprintf(ifp->if_xname, sizeof(ifp->if_xname), "wg%u", sc->sc_unit);
- ifp->if_softc = sc;
- ifp->if_type = IFT_TUNNEL;
- ifp->if_mtu = 1280;
- ifp->if_flags = IFF_POINTOPOINT|IFF_MULTICAST;
- ifp->if_xflags = IFXF_CLONED|IFXF_MPSAFE;
- ifp->if_output = wg_output;
- ifp->if_qstart = wg_start;
- ifp->if_ioctl = wg_ioctl;
- ifp->if_rtrequest = p2p_rtrequest;
- ifp->if_link_state = LINK_STATE_DOWN;
-
- if_attach(ifp);
- if_alloc_sadl(ifp);
-
- if_addgroup(ifp, "wg");
-
-#if NBPFILTER > 0
- bpfattach(&ifp->if_bpf, ifp, DLT_LOOP, sizeof(uint32_t));
-#endif
-
- KASSERT(ifp->if_index != 0);
- rw_exit(&wg_state.wg_if_lock);
-
- return (0);
-
-unlock:
- rw_exit(&wg_state.wg_if_lock);
- return (error);
-}
-
-static int
-wg_if_detach(struct wg_device *wgd, unsigned int unit)
-{
- struct wg_softc *sc;
- struct ifnet *ifp;
- int error;
-
- error = rw_enter(&wg_state.wg_if_lock, RW_WRITE | RW_INTR);
- if (error != 0)
- return (error);
-
- sc = wg_if_lookup(wgd, unit);
- if (sc == NULL) {
- error = ESRCH;
- goto unlock;
- }
-
- if (sc->sc_if.if_index == 0) {
- error = ENXIO;
- goto unlock;
- }
-
- if (sc->sc_fp != NULL) {
- error = EBUSY;
- goto unlock;
- }
-
- NET_LOCK();
- if (ISSET(ifp->if_flags, IFF_RUNNING))
- wg_down(sc);
- NET_UNLOCK();
-
- if_detach(ifp);
-
- ifp->if_index = 0;
- rw_exit(&wg_state.wg_if_lock);
-
- return (0);
-
-unlock:
- rw_exit(&wg_state.wg_if_lock);
- return (error);
-}
-
-static void
-wg_frele(struct file *fp, struct proc *p)
-{
- struct socket *so;
- struct inpcb *inp;
- int s;
-
- so = (struct socket *)fp->f_data;
- inp = sotoinpcb(so);
-
- s = solock(so);
- inp->inp_upcall = NULL;
- inp->inp_upcall_arg = NULL;
- sounlock(so, s);
-
- FRELE(fp, p);
-}
-
-static int
-wg_if_set_sock(struct wg_device *wgd, struct proc *p,
- const struct wg_if_sock *data)
-{
- unsigned int unit = data->wg_unit;
- struct wg_softc *sc;
- struct ifnet *ifp;
- struct file *fp, *ofp;
- struct socket *so;
- struct inpcb *inp;
- int error;
- int s;
-
- error = rw_enter(&wg_state.wg_if_lock, RW_WRITE | RW_INTR);
- if (error != 0)
- return (error);
-
- sc = wg_if_lookup(wgd, unit);
- if (sc == NULL) {
- error = ESRCH;
- goto unlock;
- }
- ifp = &sc->sc_if;
-
- if (ifp->if_index == 0) {
- error = ENXIO;
- goto unlock;
- }
-
- ofp = sc->sc_fp;
-
- error = getsock(p, data->wg_sock, &fp);
- if (error != 0)
- goto unlock;
-
- so = (struct socket *)fp->f_data;
- if (so->so_proto->pr_protocol != IPPROTO_UDP) {
- FRELE(fp, p);
- error = EPROTONOSUPPORT;
- goto unlock;
- }
- if (!ISSET(so->so_state, SS_ISCONNECTED)) {
- FRELE(fp, p);
- error = ENOTCONN;
- goto unlock;
- }
-
- s = solock(so);
- inp = sotoinpcb(so);
- if (inp->inp_upcall != NULL) {
- FRELE(fp, p);
- sounlock(so, s);
- error = EISCONN;
- goto unlock;
- }
-
- inp->inp_upcall_arg = sc;
- inp->inp_upcall = wg_input;
- sounlock(so, s);
-
- sc->sc_fp = fp;
- ifq_barrier(&ifp->if_snd);
-
- rw_exit(&wg_state.wg_if_lock);
-
- if (ofp)
- wg_frele(ofp, p);
- else {
- KASSERT(ifp->if_link_state == LINK_STATE_DOWN);
- wg_link_state(sc, LINK_STATE_KALIVE_DOWN);
- if (ISSET(ifp->if_flags, IFF_RUNNING)) {
- if (sc->sc_initiator) {
- /* force a rekey */
- wg_rekey(sc, 1);
- }
- }
- }
-
- return (0);
-
-
-unlock:
- rw_exit(&wg_state.wg_if_lock);
- return (error);
-}
-
-static int
-wg_if_clr_sock(struct wg_device *wgd, struct proc *p, unsigned int unit)
-{
- struct wg_softc *sc;
- struct file *fp;
- int error;
-
- error = rw_enter(&wg_state.wg_if_lock, RW_WRITE | RW_INTR);
- if (error != 0)
- return (error);
-
- sc = wg_if_lookup(wgd, unit);
- if (sc == NULL) {
- error = ESRCH;
- goto unlock;
- }
-
- fp = sc->sc_fp;
- if (fp == NULL) {
- error = ENOTCONN;
- goto unlock;
- }
-
- sc->sc_fp = NULL;
- ifq_barrier(&sc->sc_if.if_snd);
-
- rw_exit(&wg_state.wg_if_lock);
-
- wg_frele(fp, p);
- wg_link_down(sc, LINK_STATE_DOWN);
-
- return (0);
-
-unlock:
- rw_exit(&wg_state.wg_if_lock);
- return (error);
-}
-
-static struct wg_data_keys *
-wg_if_insert_data_keys(struct wg_softc *sc, struct wg_data_keys *wk)
-{
- struct wg_data_keys *owk;
-
- TAILQ_FOREACH(owk, &sc->sc_rx_data_keys, wk_entry) {
- if (owk->wk_rx_idx == wk->wk_rx_idx ||
- owk->wk_tx_idx == wk->wk_tx_idx)
- return (owk);
- }
-
- TAILQ_INSERT_HEAD(&sc->sc_rx_data_keys, wk, wk_entry);
-
- return (NULL);
-}
-
-static int
-wg_if_add_keys(struct wg_device *wgd, const struct wg_if_data_keys *keys)
-{
- struct wg_softc *sc;
- struct wg_data_keys *wk;
- unsigned int unit = keys->wg_unit;
- int error;
-
- wk = malloc(sizeof(*wk), M_DEVBUF, M_WAITOK|M_CANFAIL);
- if (wk == NULL)
- return (ENOMEM);
-
- wk->wk_tx_idx = keys->wg_tx_index;
- wk->wk_rx_idx = keys->wg_rx_index;
- wk->wk_tx_seq = 0;
- wk->wk_rx_seq = 0;
- memcpy(&wk->wk_tx_key, &keys->wg_tx_key, sizeof(wk->wk_tx_key));
- memcpy(&wk->wk_rx_key, &keys->wg_rx_key, sizeof(wk->wk_rx_key));
-
- error = rw_enter(&wg_state.wg_if_lock, RW_WRITE | RW_INTR);
- if (error != 0)
- goto free;
-
- sc = wg_if_lookup(wgd, unit);
- if (sc == NULL) {
- error = ESRCH;
- goto unlock;
- }
-
- rw_enter_write(&sc->sc_rx_data_keys_lk);
- if (wg_if_insert_data_keys(sc, wk) != NULL)
- error = EEXIST;
- rw_exit_write(&sc->sc_rx_data_keys_lk);
- if (error != 0)
- goto unlock;
-
- sc->sc_up_stamp = ticks;
-
- sc->sc_tx_data_keys = wk;
- /* make sure wg_start isn't using the old head */
- ifq_barrier(&sc->sc_if.if_snd);
-
- rw_exit(&wg_state.wg_if_lock);
-
- /* XXX locking */
- if (sc->sc_if.if_link_state == LINK_STATE_KALIVE_DOWN) {
- sc->sc_tx_stamp = ticks;
- wg_link_up(sc);
- }
-
- return (0);
-
-unlock:
- rw_exit(&wg_state.wg_if_lock);
-free:
- free(wk, M_DEVBUF, sizeof(*wk));
- return (error);
-}
-
-static int
-wg_if_clr_keys(struct wg_device *wgd, const struct wg_if_data_keys *keys)
-{
- struct wg_softc *sc;
- struct wg_data_keys *wk;
- unsigned int unit = keys->wg_unit;
- int error;
-
- error = rw_enter(&wg_state.wg_if_lock, RW_WRITE | RW_INTR);
- if (error != 0)
- return (error);
-
- sc = wg_if_lookup(wgd, unit);
- if (sc == NULL) {
- error = ESRCH;
- goto unlock;
- }
-
- rw_enter_write(&sc->sc_rx_data_keys_lk);
- TAILQ_FOREACH(wk, &sc->sc_rx_data_keys, wk_entry) {
- if (wk->wk_rx_idx == keys->wg_rx_index &&
- wk->wk_tx_idx == keys->wg_tx_index) {
- TAILQ_REMOVE(&sc->sc_rx_data_keys, wk, wk_entry);
- break;
- }
- }
- rw_exit_write(&sc->sc_rx_data_keys_lk);
-
- if (wk == NULL) {
- error = ENOENT;
- goto unlock;
- }
-
- if (sc->sc_tx_data_keys == wk) {
- sc->sc_tx_data_keys = TAILQ_FIRST(&sc->sc_rx_data_keys);;
- /* make sure wg_start isn't using the old head */
- ifq_barrier(&sc->sc_if.if_snd);
- }
-
- rw_exit(&wg_state.wg_if_lock);
-
- free(wk, M_DEVBUF, sizeof(*wk));
-
- return (0);
-
-unlock:
- rw_exit(&wg_state.wg_if_lock);
- return (error);
-}
-
-static int
-wg_if_set_role(struct wg_device *wgd, const struct wg_if_role *role)
-{
- struct wg_softc *sc;
- int error = 0;
-
- error = rw_enter(&wg_state.wg_if_lock, RW_READ | RW_INTR);
- if (error != 0)
- return (error);
-
- sc = wg_if_lookup(wgd, role->wg_unit);
- if (sc == NULL) {
- error = ESRCH;
- goto unlock;
- }
-
- switch (role->wg_role) {
- case WG_DATA_INITIATOR:
- sc->sc_initiator = 1;
- break;
- case WG_DATA_RESPONDER:
- sc->sc_initiator = 0;
- break;
- default:
- error = EINVAL;
- break;
- }
-
- rw_exit(&wg_state.wg_if_lock);
-
- return (0);
-
-unlock:
- rw_exit(&wg_state.wg_if_lock);
- return (error);
-}
-
-static int
-wg_if_get_role(struct wg_device *wgd, struct wg_if_role *role)
-{
- struct wg_softc *sc;
- int error = 0;
-
- error = rw_enter(&wg_state.wg_if_lock, RW_READ | RW_INTR);
- if (error != 0)
- return (error);
-
- sc = wg_if_lookup(wgd, role->wg_unit);
- if (sc == NULL) {
- error = ESRCH;
- goto unlock;
- }
-
- role->wg_role = sc->sc_initiator ?
- WG_DATA_INITIATOR : WG_DATA_RESPONDER;
-
- rw_exit(&wg_state.wg_if_lock);
-
- return (0);
-
-unlock:
- rw_exit(&wg_state.wg_if_lock);
- return (error);
-}
-
-int
-wgioctl(dev_t dev, u_long cmd, caddr_t data, int flag, struct proc *p)
-{
- struct wg_device *wgd = wg_dev_lookup(dev);
-
- switch (cmd) {
- case FIONBIO:
- wgd->wgd_nbio = *(int *)data ? IO_NDELAY : 0;
- break;
- case FIONREAD:
- *(int *)data = TAILQ_EMPTY(&wgd->wgd_rk_list) ?
- 0 : sizeof(struct wg_msg);
- break;
-
- case WGIFCREATE:
- return (wg_if_create(wgd, *(unsigned int *)data));
- case WGIFATTACH:
- return (wg_if_attach(wgd, *(unsigned int *)data));
- case WGIFDETACH:
- return (wg_if_detach(wgd, *(unsigned int *)data));
- case WGIFDESTROY:
- return (wg_if_destroy(wgd, *(unsigned int *)data));
-
- case WGIFSSOCK:
- return (wg_if_set_sock(wgd, p,
- (const struct wg_if_sock *)data));
- case WGIFDSOCK:
- return (wg_if_clr_sock(wgd, p, *(unsigned int *)data));
-
- case WGIFADDKEYS:
- return (wg_if_add_keys(wgd,
- (const struct wg_if_data_keys *)data));
- case WGIFDELKEYS:
- return (wg_if_clr_keys(wgd,
- (const struct wg_if_data_keys *)data));
-
- case WGIFSROLE:
- return (wg_if_set_role(wgd, (struct wg_if_role *)data));
- case WGIFGROLE:
- return (wg_if_get_role(wgd, (struct wg_if_role *)data));
-
- default:
- return (ENOTTY);
- }
-
- return (0);
-}
-
-int
-wgread(dev_t dev, struct uio *uio, int ioflag)
-{
- struct wg_device *wgd = wg_dev_lookup(dev);
- struct wg_softc *sc;
- struct wg_msg msg;
- int error;
-
- if (uio->uio_resid < 0)
- return (EINVAL);
-
- ioflag |= wgd->wgd_nbio;
-
- mtx_enter(&wgd->wgd_rk_mtx);
-
- sc = TAILQ_FIRST(&wgd->wgd_rk_list);
- if (sc == NULL) {
- if (ioflag & IO_NDELAY) {
- mtx_leave(&wgd->wgd_rk_mtx);
- return (EWOULDBLOCK);
- }
-
- wgd->wgd_rk_reading++;
- do {
- error = msleep(&wgd->wgd_rk_list, &wgd->wgd_rk_mtx,
- (PZERO + 1)|PCATCH, "wgread", 0);
- if (error != 0) {
- wgd->wgd_rk_reading--;
- mtx_leave(&wgd->wgd_rk_mtx);
- return (error);
- }
-
- sc = TAILQ_FIRST(&wgd->wgd_rk_list);
- } while (sc == NULL);
- wgd->wgd_rk_reading--;
- }
-
- sc->sc_rk_stamp = ticks;
- sc->sc_rk_onqueue = 0;
- TAILQ_REMOVE(&wgd->wgd_rk_list, sc, sc_rk_entry);
-
- msg.wg_unit = sc->sc_unit;
- msg.wg_type = WG_MSG_REKEY;
- DPRINTF(&sc->sc_if, "rekey");
-
- mtx_leave(&wgd->wgd_rk_mtx);
-
- return (uiomove(&msg, ulmin(uio->uio_resid, sizeof(msg)), uio));
-}
-
-int
-wgwrite(dev_t dev, struct uio *uio, int ioflag)
-{
- return (EOPNOTSUPP);
-}
-
-int
-wgpoll(dev_t dev, int events, struct proc *p)
-{
- struct wg_device *wgd = wg_dev_lookup(dev);
- int mevents, revents = 0;
-
- mevents = ISSET(events, POLLIN | POLLRDNORM);
- if (mevents) {
- if (!TAILQ_EMPTY(&wgd->wgd_rk_list))
- SET(revents, events & mevents);
- else
- selrecord(p, &wgd->wgd_rsel);
- }
-
- mevents = ISSET(events, POLLOUT | POLLWRNORM);
- if (mevents)
- SET(revents, events & mevents);
-
- return (revents);
-}
-
-int
-wgkqfilter(dev_t dev, struct knote *kn)
-{
- struct wg_device *wgd = wg_dev_lookup(dev);
- struct klist *klist;
-
- switch (kn->kn_filter) {
- case EVFILT_READ:
- klist = &wgd->wgd_rsel.si_note;
- kn->kn_fop = &wg_filtops_read;
- break;
- case EVFILT_WRITE:
- klist = &wgd->wgd_wsel.si_note;
- kn->kn_fop = &wg_filtops_write;
- break;
- default:
- return (EINVAL);
- }
-
- kn->kn_hook = wgd;
-
- mtx_enter(&wgd->wgd_sel_mtx);
- SLIST_INSERT_HEAD(klist, kn, kn_selnext);
- mtx_leave(&wgd->wgd_sel_mtx);
-
- return (0);
-}
-
-static void
-wg_filt_read_detach(struct knote *kn)
-{
- struct wg_device *wgd = kn->kn_hook;
- struct klist *klist = &wgd->wgd_rsel.si_note;
-
- mtx_enter(&wgd->wgd_sel_mtx);
- SLIST_REMOVE(klist, kn, knote, kn_selnext);
- mtx_leave(&wgd->wgd_sel_mtx);
-}
-
-static int
-wg_filt_read(struct knote *kn, long hint)
-{
- struct wg_device *wgd = kn->kn_hook;
-
- kn->kn_data = TAILQ_EMPTY(&wgd->wgd_rk_list) ?
- 0 : sizeof(struct wg_msg);
-
- return (kn->kn_data != 0);
-}
-
-static void
-wg_filt_write_detach(struct knote *kn)
-{
- struct wg_device *wgd = kn->kn_hook;
- struct klist *klist = &wgd->wgd_wsel.si_note;
-
- mtx_enter(&wgd->wgd_sel_mtx);
- SLIST_REMOVE(klist, kn, knote, kn_selnext);
- mtx_leave(&wgd->wgd_sel_mtx);
-}
-
-static int
-wg_filt_write(struct knote *kn, long hint)
-{
- kn->kn_data = 0;
-
- return (0);
-}
-
-static struct wg_device *
-wg_dev_lookup_locked(dev_t dev)
-{
- return (RBT_FIND(wg_dv_tree, &wg_state.wg_dv_tree,
- (const struct wg_device *)&dev));
-}
-
-static struct wg_device *
-wg_dev_lookup(dev_t dev)
-{
- struct wg_device *wgd;
-
- rw_enter_read(&wg_state.wg_dv_lock);
- wgd = wg_dev_lookup_locked(dev);
- rw_exit_read(&wg_state.wg_dv_lock);
-
- return (wgd);
-}
-
-static inline int
-wg_dv_cmp(const struct wg_device *a, const struct wg_device *b)
-{
- if (a->wgd_dev > b->wgd_dev)
- return (1);
- if (a->wgd_dev < b->wgd_dev)
- return (-1);
- return (0);
-}
-
-static inline int
-wg_if_cmp(const struct wg_softc *a, const struct wg_softc *b)
-{
- if (a->sc_unit > b->sc_unit)
- return (1);
- if (a->sc_unit < b->sc_unit)
- return (-1);
- return (0);
-}
-
-RBT_GENERATE(wg_if_tree, wg_softc, sc_entry, wg_if_cmp);
-RBT_GENERATE(wg_dv_tree, wg_device, wgd_entry, wg_dv_cmp);
-
-#define WG_MS2TICKS(_m) (((_m) * 1000) / tick)
-
-static inline int
-wg_ratecheck(int stamp, int interval)
-{
- int diff;
-
- diff = ticks - stamp;
- return (diff >= interval);
-}
-
-static void
-wg_rekey(struct wg_softc *sc, int force)
-{
- struct wg_device *wgd = sc->sc_device;
- int tmo = WG_MS2TICKS(WG_REKEY_TIMEOUT);
- int wake = 0;
- int next = 0;
-
- if (sc->sc_rk_onqueue)
- return;
-
- if (!force && !wg_ratecheck(sc->sc_rk_stamp, tmo))
- return;
-
- mtx_enter(&wgd->wgd_rk_mtx);
- if (!sc->sc_rk_onqueue) {
- if (force || wg_ratecheck(sc->sc_rk_stamp, tmo)) {
- TAILQ_INSERT_TAIL(&wgd->wgd_rk_list, sc, sc_rk_entry);
- sc->sc_rk_onqueue = 1;
-
- wake = wgd->wgd_rk_reading;
- }
- next = 1;
- }
- mtx_leave(&wgd->wgd_rk_mtx);
-
- if (wake)
- wakeup(&wgd->wgd_rk_list);
- selwakeup(&wgd->wgd_rsel);
-
- if (next) {
- timeout_add_msec(&sc->sc_rk_timer,
- WG_REKEY_TIMEOUT + arc4random_uniform(WG_REKEY_JITTER));
- }
-
- sc->sc_tx_stamp = ticks;
-}
-
-static int
-wg_up(struct wg_softc *sc)
-{
- struct ifnet *ifp = &sc->sc_if;
-
- SET(ifp->if_flags, IFF_RUNNING);
-
- if (ifp->if_link_state == LINK_STATE_KALIVE_DOWN) {
- if (sc->sc_initiator) {
- /* force a rekey */
- wg_rekey(sc, 1);
- }
- }
-
- return (0);
-}
-
-static void
-wg_link_state(struct wg_softc *sc, int link_state)
-{
- struct ifnet *ifp = &sc->sc_if;
-
- ifp->if_link_state = link_state;
- if_link_state_change(ifp);
-}
-
-static void
-wg_rekey_timer(void *arg)
-{
- struct wg_softc *sc = arg;
- struct ifnet *ifp = &sc->sc_if;
-
- if (!ISSET(ifp->if_flags, IFF_RUNNING))
- return;
-
- if (ifp->if_link_state == LINK_STATE_UP &&
- wg_ratecheck(sc->sc_up_stamp, WG_MS2TICKS(WG_REKEY_MAX)))
- wg_link_down(sc, LINK_STATE_KALIVE_DOWN);
-
- wg_rekey(sc, 0);
-}
-
-static void
-wg_link_up(struct wg_softc *sc)
-{
- DPRINTF(&sc->sc_if, "link up");
- timeout_add_msec(&sc->sc_tx_timer, WG_KEEPALIVE + WG_REKEY_TIMEOUT);
- timeout_add_msec(&sc->sc_rx_timer, WG_KEEPALIVE);
- wg_link_state(sc, LINK_STATE_UP);
-}
-
-static void
-wg_link_down(struct wg_softc *sc, int link_state)
-{
- DPRINTF(&sc->sc_if, "link down");
- timeout_del(&sc->sc_rx_timer);
- timeout_del(&sc->sc_tx_timer);
-
- if (link_state == LINK_STATE_DOWN)
- timeout_del(&sc->sc_rk_timer);
-
- timeout_barrier(&sc->sc_rx_timer); /* implies tx and rk bar too */
-
- wg_link_state(sc, link_state);
-}
-
-static void
-wg_rx_timer(void *arg)
-{
- struct wg_softc *sc = arg;
- struct ifnet *ifp = &sc->sc_if;
- const int tmo = WG_MS2TICKS(WG_KEEPALIVE);
- int diff;
-
- diff = ticks - sc->sc_rx_stamp;
- DPRINTF(ifp, "%s, tmo %d, diff %d = ticks %d - stamp %d", __func__,
- tmo, diff, ticks, sc->sc_rx_stamp);
- if (diff >= tmo) {
- DPRINTF(ifp, "rx timer expired, sending keepalive");
- ifq_serialize(&ifp->if_snd, &sc->sc_tx_keepalive);
- diff = 0;
- }
-
- DPRINTF(ifp, "%s, timeout_add rx timer %d", __func__, tmo - diff);
- timeout_add(&sc->sc_rx_timer, tmo - diff);
-}
-
-static void
-wg_tx_timer(void *arg)
-{
- struct wg_softc *sc = arg;
- struct ifnet *ifp = &sc->sc_if;
- const int tmo = WG_MS2TICKS(WG_KEEPALIVE + WG_REKEY_TIMEOUT);
- int diff;
-
- diff = ticks - sc->sc_tx_stamp;
- DPRINTF(ifp, "%s, tmo %d, diff %d = ticks %d - stamp %d", __func__,
- tmo, diff, ticks, sc->sc_tx_stamp);
- if (diff >= tmo) {
- DPRINTF(ifp, "tx timer expired, sending rekey");
- wg_rekey(sc, 0);
- diff = 0;
- }
-
- DPRINTF(ifp, "%s, timeout_add tx timer %d", __func__, tmo - diff);
- timeout_add(&sc->sc_tx_timer, tmo - diff);
-}
-
-static int
-wg_down(struct wg_softc *sc)
-{
- struct ifnet *ifp = &sc->sc_if;
-
- CLR(ifp->if_flags, IFF_RUNNING);
- ifq_barrier(&ifp->if_snd);
-
- wg_link_down(sc, sc->sc_fp == NULL ?
- LINK_STATE_DOWN : LINK_STATE_KALIVE_DOWN);
-
- return (0);
-}
-
-static int
-wg_get_tunnel(struct wg_softc *sc, struct if_laddrreq *req)
-{
- struct file *fp;
- struct socket *so;
- struct inpcb *inp;
-// int s;
-
- fp = sc->sc_fp;
- if (fp == NULL)
- return (EADDRNOTAVAIL);
-
- so = (struct socket *)fp->f_data;
-// s = solock(so);
- inp = sotoinpcb(so);
-
- if (inp->inp_flags & INP_IPV6) {
-#ifdef INET6
- struct sockaddr_in6 *sin6;
-
- sin6 = (struct sockaddr_in6 *)&req->addr;
- memset(sin6, 0, sizeof(*sin6));
- sin6->sin6_family = AF_INET6;
- sin6->sin6_len = sizeof(*sin6);
- in6_recoverscope(sin6, &inp->inp_laddr6);
- sin6->sin6_port = inp->inp_lport;
-
- sin6 = (struct sockaddr_in6 *)&req->dstaddr;
- memset(sin6, 0, sizeof(*sin6));
- sin6->sin6_family = AF_INET6;
- sin6->sin6_len = sizeof(*sin6);
- in6_recoverscope(sin6, &inp->inp_faddr6);
- sin6->sin6_port = inp->inp_fport;
-#else /* INET6 */
- unhandled_af(AF_INET6);
-#endif /* INET6 */
- } else {
- struct sockaddr_in *sin;
-
- sin = (struct sockaddr_in *)&req->addr;
- memset(sin, 0, sizeof(*sin));
- sin->sin_family = AF_INET;
- sin->sin_len = sizeof(*sin);
- sin->sin_addr = inp->inp_laddr;
- sin->sin_port = inp->inp_lport;
-
- sin = (struct sockaddr_in *)&req->dstaddr;
- memset(sin, 0, sizeof(*sin));
- sin->sin_family = AF_INET;
- sin->sin_len = sizeof(*sin);
- sin->sin_addr = inp->inp_faddr;
- sin->sin_port = inp->inp_fport;
- }
-
-// sounlock(so, s);
-
- return (0);
-}
-
-static int
-wg_get_rdomain(struct wg_softc *sc, struct ifreq *ifr)
-{
- struct file *fp;
- struct socket *so;
- struct inpcb *inp;
-// int s;
-
- fp = sc->sc_fp;
- if (fp == NULL)
- return (EADDRNOTAVAIL);
-
- so = (struct socket *)fp->f_data;
-// s = solock(so);
- inp = sotoinpcb(so);
-
- ifr->ifr_rdomainid = inp->inp_rtableid;
-
-// sounlock(so, s);
-
- return (0);
-}
-
-static int
-wg_get_ttl(struct wg_softc *sc, struct ifreq *ifr)
-{
- struct file *fp;
- struct socket *so;
- struct inpcb *inp;
-// int s;
-
- fp = sc->sc_fp;
- if (fp == NULL)
- return (EADDRNOTAVAIL);
-
- so = (struct socket *)fp->f_data;
-// s = solock(so);
- inp = sotoinpcb(so);
-
- if (inp->inp_flags & INP_IPV6) {
-#ifdef INET6
- ifr->ifr_ttl = inp->inp_ipv6.ip6_hlim;
-#else /* INET6 */
- unhandled_af(AF_INET6);
-#endif /* INET6 */
- } else {
- ifr->ifr_ttl = inp->inp_ip.ip_ttl;
- }
-
-// sounlock(so, s);
-
- return (0);
-}
-
-static int
-wg_ioctl(struct ifnet *ifp, u_long cmd, caddr_t data)
-{
- struct wg_softc *sc = ifp->if_softc;
- struct ifreq *ifr = (struct ifreq *)data;
- int error = 0;
-
- switch (cmd) {
- case SIOCSIFADDR:
- break;
-
- case SIOCSIFFLAGS:
- if (ISSET(ifp->if_flags, IFF_UP)) {
- if (!ISSET(ifp->if_flags, IFF_RUNNING))
- error = wg_up(sc);
- else
- error = 0;
- } else {
- if (ISSET(ifp->if_flags, IFF_RUNNING))
- error = wg_down(sc);
- }
- break;
-
- case SIOCGLIFPHYADDR:
- error = wg_get_tunnel(sc, (struct if_laddrreq *)data);
- break;
- case SIOCGLIFPHYRTABLE:
- error = wg_get_rdomain(sc, ifr);
- break;
- case SIOCGLIFPHYTTL:
- error = wg_get_ttl(sc, ifr);
- break;
-
- default:
- error = ENOTTY;
- break;
- }
-
- return (error);
-}
-
-static int
-wg_decap(struct ifnet *ifp, struct mbuf *m0,
- const struct chacha_key *rxkey, uint64_t counter)
-{
- struct wg_aead_ctx ctx;
- struct wg_aead_tag ptag, ctag;
- struct mbuf *mn, *m;
- unsigned int len = m0->m_pkthdr.len;
- unsigned int diff;
- int rv = 0;
-
- if (len < sizeof(ptag)) {
- /* must be long enough to contain the tag */
- ifp->if_iqdrops++;
- return (-1);
- }
-
- len -= sizeof(ptag);
- if (len % 16) {
- /* be padded */
- ifp->if_iqdrops++;
- return (-1);
- }
-
- wg_aead_init(&ctx, rxkey, counter);
-
- if (len) {
- /* m_apply, but different */
- mn = m0;
- do {
- m = mn;
- KASSERT(m != NULL);
-
- diff = min(m->m_len, len);
- if (diff) {
- wg_aead_verify(&ctx, m->m_data, diff);
- len -= diff;
- }
-
- mn = m->m_next;
- } while (len > 0);
- } else {
- m = m0;
- diff = 0;
- }
-
- m_copydata(m, diff, sizeof(ptag), (caddr_t)&ptag);
-
- wg_aead_final(&ctx, &ctag);
- if (memcmp(&ctag, &ptag, sizeof(ctag)) != 0) {
- /* mac didnt match */
- ifp->if_ierrors++;
- rv = -1;
- goto out;
- }
-
- /* chop the poly bit off */
- m_freem(m->m_next);
- m->m_next = NULL;
- m->m_len = diff;
- m0->m_pkthdr.len -= sizeof(ptag);
-
- if (len) {
- m = m0;
- do {
- wg_aead_decrypt(&ctx, m->m_data, m->m_len);
- m = m->m_next;
- } while (m != NULL);
- }
-
-out:
- explicit_bzero(&ctx, sizeof(ctx));
-
- return (rv);
-}
-
-static struct wg_data_keys *
-wg_match_rx_data_keys(struct wg_softc *sc, uint32_t index)
-{
- struct wg_data_keys *wk;
-
- TAILQ_FOREACH(wk, &sc->sc_rx_data_keys, wk_entry) {
- if (wk->wk_rx_idx == index)
- return (wk);
- }
-
- return (NULL);
-}
-
-static void
-wg_send_rekey(struct wg_softc *sc)
-{
-
-}
-
-static struct mbuf *
-wg_input(void *ctx, struct mbuf *m, int iphlen)
-{
- struct wg_softc *sc = ctx;
- struct ifnet *ifp = &sc->sc_if;
- struct wg_data_hdr *hdr;
- int hlen = iphlen + sizeof(*hdr);
- struct wg_data_keys *wk;
- struct mbuf *n;
- uint64_t counter;
- uint64_t diff;
- int high;
- int rv;
- void (*input)(struct ifnet *, struct mbuf *);
-
- soassertlocked((struct socket *)sc->sc_fp->f_data);
-
- if (!ISSET(ifp->if_flags, IFF_RUNNING)) {
- /* no point if we're not up */
- return (m);
- }
-
- if (TAILQ_EMPTY(&sc->sc_rx_data_keys)) {
- /* not set up with any keys */
- return (m);
- }
-
- if (m->m_pkthdr.len < hlen) {
- /* decline short packets */
- return (m);
- }
-
- if (m->m_len < hlen) {
- m = m_pullup(m, hlen);
- if (m == NULL) {
- /* oops */
- return (NULL);
- }
- }
-
- hdr = (struct wg_data_hdr *)(mtod(m, uint8_t *) + iphlen);
- if (hdr->msg_type != htole32(WG_MSG_DATA)) {
- /* we only handle data in the kernel */
- return (m);
- }
-
- counter = lemtoh32(&hdr->counter_lo) |
- ((uint64_t)lemtoh32(&hdr->counter_hi) << 32);
-
- if (counter >= WG_REJECT_MSGS)
- goto drop;
-
- /* might be ours now */
-
- rw_enter_write(&sc->sc_rx_data_keys_lk);
-
- wk = wg_match_rx_data_keys(sc, hdr->index);
- if (wk == NULL) {
- /* not this connection */
- goto unlock;
- }
-
- /* avoid {under,over}flow */
- high = (counter >= wk->wk_rx_seq);
- if (high)
- diff = counter - wk->wk_rx_seq;
- else
- diff = wk->wk_rx_seq - counter;
-
- if (diff >= WG_MSGS_WINDOW)
- goto unlock;
-
- m_adj(m, hlen);
-
- rv = wg_decap(ifp, m, &wk->wk_rx_key, counter);
-
- if (rv != 0)
- goto drop;
-
- /* actually ours now according to auth, so move forward */
- if (high)
- wk->wk_rx_seq = counter;
-
- sc->sc_rx_stamp = ticks;
-
- rw_exit_write(&sc->sc_rx_data_keys_lk);
-
- if (counter >= WG_REKEY_MSGS)
- wg_send_rekey(sc);
-
- if (m->m_pkthdr.len == 0) {
- /* keepalive */
- return (NULL);
- }
-
- n = m;
- while (n->m_len == 0) {
- n = n->m_next;
- if (n == NULL) {
- ifp->if_ierrors++;
- goto drop;
- }
- }
-
- switch (*mtod(n, uint8_t *) >> 4) {
- case 4:
- input = ipv4_input;
- m->m_pkthdr.ph_family = AF_INET;
- break;
-
-#ifdef INET6
- case 6:
- input = ipv6_input;
- m->m_pkthdr.ph_family = AF_INET6;
- break;
-#endif
- default:
- ifp->if_noproto++;
- goto drop;
- }
-
- m->m_flags &= ~(M_MCAST|M_BCAST);
- m->m_pkthdr.ph_ifidx = ifp->if_index;
- m->m_pkthdr.ph_rtableid = ifp->if_rdomain;
- m->m_pkthdr.ph_flowid = 0;
-
-#if NPF > 0
- pf_pkt_addr_changed(m);
-#endif
-
- ifp->if_ipackets++;
- ifp->if_ibytes += m->m_pkthdr.len;
-
-#if NBPFILTER > 0
- {
- caddr_t if_bpf = ifp->if_bpf;
- if (if_bpf) {
- bpf_mtap_af(if_bpf, m->m_pkthdr.ph_family, m,
- BPF_DIRECTION_IN);
- }
- }
-#endif
-
- (*input)(ifp, m);
-
- return (NULL);
-
-unlock:
- rw_exit_write(&sc->sc_rx_data_keys_lk);
-drop:
- m_freem(m);
- return (NULL);
-}
-
-static int
-wg_output(struct ifnet *ifp, struct mbuf *m, struct sockaddr *dst,
- struct rtentry *rt)
-{
- struct wg_softc *sc = ifp->if_softc;
- struct m_tag *mtag;
- int error = 0;
-
- if (!ISSET(ifp->if_flags, IFF_RUNNING) ||
- sc->sc_fp == NULL ||
- sc->sc_tx_data_keys == NULL) {
- error = ENETDOWN;
- goto drop;
- }
-
- switch (dst->sa_family) {
- case AF_INET:
-#ifdef INET6
- case AF_INET6:
-#endif
- break;
- default:
- error = EAFNOSUPPORT;
- goto drop;
- }
-
- /* Try to limit infinite recursion through misconfiguration. */
- for (mtag = m_tag_find(m, PACKET_TAG_GRE, NULL); mtag;
- mtag = m_tag_find(m, PACKET_TAG_GRE, mtag)) {
- if (memcmp(mtag + 1, &ifp->if_index,
- sizeof(ifp->if_index)) == 0) {
- error = EIO;
- goto drop;
- }
- }
-
- mtag = m_tag_get(PACKET_TAG_GRE, sizeof(ifp->if_index), M_NOWAIT);
- if (mtag == NULL) {
- error = ENOBUFS;
- goto drop;
- }
- memcpy(mtag + 1, &ifp->if_index, sizeof(ifp->if_index));
- m_tag_prepend(m, mtag);
-
- m->m_pkthdr.ph_family = dst->sa_family;
-
- error = if_enqueue(ifp, m);
- if (error)
- ifp->if_oerrors++;
- return (error);
-drop:
- m_freem(m);
- return (error);
-}
-
-static int
-wg_encap(struct wg_softc *sc, struct wg_data_keys *wk, struct mbuf *m0)
-{
- struct wg_aead_ctx ctx;
- struct wg_aead_tag *tag;
- struct mbuf *mn, *m;
- struct wg_data_hdr *hdr;
- uint64_t counter = wk->wk_tx_seq++;
- int padlen;
-
- wg_aead_init(&ctx, &wk->wk_tx_key, counter);
-
- mn = m0;
- do {
- m = mn;
- mn = m->m_next;
-
- if (m->m_len)
- wg_aead_encrypt(&ctx, mtod(m, void *), m->m_len);
- } while (mn != NULL);
-
- padlen = m0->m_pkthdr.len % 16;
- if (padlen) {
- uint8_t *zero;
-
- padlen = 16 - padlen;
-
- if (m_trailingspace(m) < padlen) {
- mn = m_get(M_DONTWAIT, MT_DATA);
- if (mn == NULL)
- goto drop;
-
- m->m_next = mn;
- m = mn;
-
- m->m_len = 0;
- }
-
- zero = mtod(m, uint8_t *) + m->m_len;
- memset(zero, 0, padlen);
- wg_aead_encrypt(&ctx, zero, padlen);
-
- m0->m_pkthdr.len += padlen;
- m->m_len += padlen;
- }
-
- if (m_trailingspace(m) < sizeof(*tag)) {
- mn = m_get(M_DONTWAIT, MT_DATA);
- if (mn == NULL)
- goto drop;
-
- m->m_next = mn;
- m = mn;
-
- m->m_len = 0;
- }
-
- tag = (struct wg_aead_tag *)(mtod(m, uint8_t *) + m->m_len);
- wg_aead_final(&ctx, tag);
- explicit_bzero(&ctx, sizeof(ctx));
-
- m0->m_pkthdr.len += sizeof(*tag);
- m->m_len += sizeof(*tag);
-
- m0 = m_prepend(m0, sizeof(*hdr), M_DONTWAIT);
- if (m0 == NULL)
- return (-1);
-
- hdr = mtod(m0, struct wg_data_hdr *);
- hdr->msg_type = htole32(WG_MSG_DATA);
- hdr->index = wk->wk_tx_idx;
- htolem32(&hdr->counter_lo, counter);
- htolem32(&hdr->counter_hi, counter >> 32);
-
- if (mq_enqueue(&sc->sc_tx_queue, m0) != 0)
- return (-1);
-
- task_add(systq, &sc->sc_tx_task);
-
- return (0);
-
-drop:
- m_freem(m0);
- explicit_bzero(&ctx, sizeof(ctx));
- return (-1);
-}
-
-static void
-wg_send_keepalive(void *v)
-{
- struct wg_softc *sc = v;
- struct wg_data_keys *wk = sc->sc_tx_data_keys;
- struct mbuf *m;
-
- if (wk == NULL)
- return;
-
- m = m_gethdr(M_DONTWAIT, MT_DATA);
- if (m == NULL)
- return;
-
- m_align(m, sizeof(struct wg_aead_tag));
- m->m_pkthdr.len = m->m_len = 0;
-
- wg_encap(sc, wk, m);
-}
-
-static void
-wg_send(void *v)
-{
- struct wg_softc *sc = v;
- struct ifnet *ifp = &sc->sc_if;
- struct file *fp;
- struct socket *so;
- struct mbuf_list ml;
- struct mbuf *m;
- int error;
- int s;
-
- mq_delist(&sc->sc_tx_queue, &ml);
- if (ml_empty(&ml))
- return;
-
- fp = sc->sc_fp;
- if (fp == NULL || (so = (struct socket *)fp->f_data) == NULL) {
- ml_purge(&ml);
- return;
- }
-
- /* XXX this knows too much about how udp_usrreq works internally. */
-
- s = solock(so);
- while ((m = ml_dequeue(&ml)) != NULL) {
- error = udp_usrreq(so, PRU_SEND, m, NULL, NULL, NULL);
- if (error)
- ifp->if_oerrors++;
- }
- sounlock(so, s);
-
- sc->sc_tx_stamp = ticks;
-
-}
-
-static void
-wg_start(struct ifqueue *ifq)
-{
- struct ifnet *ifp = ifq->ifq_if;
- struct wg_softc *sc = ifp->if_softc;
- struct wg_data_keys *wk = sc->sc_tx_data_keys;
- struct mbuf *m;
-
- if (sc->sc_fp == NULL || wk == NULL) {
- ifq_purge(ifq);
- return;
- }
-
- while ((m = ifq_dequeue(ifq)) != NULL) {
-#if NBPFILTER
- {
- caddr_t if_bpf = ifp->if_bpf;
- if (if_bpf) {
- bpf_mtap_af(if_bpf, m->m_pkthdr.ph_family, m,
- BPF_DIRECTION_OUT);
- }
- }
-#endif
-
- if (!wg_encap(sc, wk, m)) {
- ifq->ifq_errors++;
- continue;
- }
- }
-}
-
-static void
-wg_aead_init(struct wg_aead_ctx *ctx, const struct chacha_key *key,
- uint64_t counter)
-{
- uint8_t block0[CHACHA_BLOCKSIZE];
-
- chacha_stream_keysetup(&ctx->chacha20, key);
-
- /*
- * AEAD-ChaCha20-Poly1305 uses the IETF construction with the 96bit
- * nonce and 32 bit counter starting from 0. WireGuard uses this AEAD
- * with the counter off the wire as the nonce. That nonce is padded
- * with zeros on the left, so the layout of the chacha state is
- * predictable. Set it up directly, rather than stuff bits just so
- * they can be unstuffed straight away.
- */
- ctx->chacha20.ctx.input[12] = 0; /* counter starts at 0 */
- ctx->chacha20.ctx.input[13] = 0; /* nonce padding is 0 */
- ctx->chacha20.ctx.input[14] = counter; /* the rest of the "nonce" */
- ctx->chacha20.ctx.input[15] = counter >> 32;
- ctx->chacha20.used = 0;
-
- /* AEAD-ChaCha20-Poly1305 uses the first block for the poly key */
- bzero(block0, sizeof(block0));
- chacha_stream_update(&ctx->chacha20, block0, block0, sizeof(block0));
- poly1305_init(&ctx->poly1305, block0);
- explicit_bzero(block0, sizeof(block0));
-
- /* data has no aad */
-
- ctx->datalen = 0;
-}
-
-static void
-wg_aead_encrypt(struct wg_aead_ctx *ctx, void *mem, size_t len)
-{
- chacha_stream_update(&ctx->chacha20, mem, mem, len);
- poly1305_update(&ctx->poly1305, mem, len);
- ctx->datalen += len;
-}
-
-static void
-wg_aead_verify(struct wg_aead_ctx *ctx, void *mem, size_t len)
-{
- poly1305_update(&ctx->poly1305, mem, len);
- ctx->datalen += len;
-}
-
-static void
-wg_aead_decrypt(struct wg_aead_ctx *ctx, void *mem, size_t len)
-{
- chacha_stream_update(&ctx->chacha20, mem, mem, len);
-}
-
-static void
-wg_aead_final(struct wg_aead_ctx *ctx, struct wg_aead_tag *tag)
-{
- uint8_t len[8];
- uint64_t j;
- unsigned int i;
-
- /* data has no aad, so aad len is 0 */
- memset(len, 0, sizeof(len));
- poly1305_update(&ctx->poly1305, len, sizeof(len));
-
- j = ctx->datalen;
- for (i = 0; i < sizeof(len); i++) {
- len[i] = j;
- j >>= 8;
- }
- poly1305_update(&ctx->poly1305, len, sizeof(len));
-
- poly1305_finish(&ctx->poly1305, tag->tag);
-}
diff --git a/sys/net/if_wg.h b/sys/net/if_wg.h
deleted file mode 100644
index 107ae9c0b39..00000000000
--- a/sys/net/if_wg.h
+++ /dev/null
@@ -1,158 +0,0 @@
-/* $OpenBSD: if_wg.h,v 1.1 2019/04/27 05:14:33 dlg Exp $ */
-
-/*
- * Copyright (c) 2018 David Gwynne <dlg@openbsd.org>
- *
- * Permission to use, copy, modify, and distribute this software for any
- * purpose with or without fee is hereby granted, provided that the above
- * copyright notice and this permission notice appear in all copies.
- *
- * THE SOFTWARE IS PROVIDED "AS IS" AND THE AUTHOR DISCLAIMS ALL WARRANTIES
- * WITH REGARD TO THIS SOFTWARE INCLUDING ALL IMPLIED WARRANTIES OF
- * MERCHANTABILITY AND FITNESS. IN NO EVENT SHALL THE AUTHOR BE LIABLE FOR
- * ANY SPECIAL, DIRECT, INDIRECT, OR CONSEQUENTIAL DAMAGES OR ANY DAMAGES
- * WHATSOEVER RESULTING FROM LOSS OF USE, DATA OR PROFITS, WHETHER IN AN
- * ACTION OF CONTRACT, NEGLIGENCE OR OTHER TORTIOUS ACTION, ARISING OUT OF
- * OR IN CONNECTION WITH THE USE OR PERFORMANCE OF THIS SOFTWARE.
- */
-
-#ifndef _NET_IF_WG_H_
-#define _NET_IF_WG_H_
-
-/* the timing side of messages */
-
-#define WG_MSGS_WINDOW 2000
-
-#define WG_REKEY_MSGS (~0ULL - 0xffffULL)
-#define WG_REKEY_TIME 120000 /* msec */
-#define WG_KEEPALIVE 10000 /* msec */
-#define WG_REKEY_TIMEOUT 5000 /* msec */
-#define WG_REKEY_JITTER 333 /* msec */
-#define WG_REKEY_MAX 90000 /* msec */
-#define WG_REJECT_MSGS (~0ULL - WG_MSGS_WINDOW)
-#define WG_REJECT_TIME 180000 /* msec */
-
-
-/* the bytes side of messages */
-
-#define WG_POLY1305_TAG_LEN 16
-#define WG_CURVE25519_KEY_LEN 32
-#define WG_AEAD_CHACHA20_POLY1305_KEY_LEN \
- 32
-#define WG_BLAKE2S_OUTPUT_LEN 16
-#define WG_CHACHA20_POLY1305_NONCE_LEN \
- 24
-
-#define WG_AEAD_LEN(_l) ((_l) + WG_POLY1305_TAG_LEN)
-
-#define WG_ENCRYPTED_STATIC_LEN WG_AEAD_LEN(WG_CURVE25519_KEY_LEN)
-
-#define WG_MSG_HANDSHAKE_INIT 1
-#define WG_MSG_HANDSHAKE_RESP 2
-#define WG_MSG_COOKIE_RESP 3
-#define WG_MSG_DATA 4
-
-struct wg_header {
- uint8_t msg_type;
- uint8_t zero[3];
-} __packed __aligned(4);
-
-struct wg_tai64n {
- uint64_t tai_seconds; /* BE */
- uint32_t tai_nanoseconds; /* BE */
-} __packed __aligned(4);
-
-#define WG_ENCRYPTED_TAI64N_LEN WG_AEAD_LEN(sizeof(struct wg_tai64n))
-
-struct wg_handshake_init {
- struct wg_header hdr;
- uint32_t sndr_idx; /* LE */
- uint8_t unencrypted_ephemeral[WG_CURVE25519_KEY_LEN];
- uint8_t encrypted_static[WG_ENCRYPTED_STATIC_LEN];
- uint8_t encrypted_timestamp[WG_ENCRYPTED_TAI64N_LEN];
- uint8_t mac1[WG_BLAKE2S_OUTPUT_LEN];
- uint8_t mac2[WG_BLAKE2S_OUTPUT_LEN];
-} __packed __aligned(4);
-
-struct wg_handshake_resp {
- struct wg_header hdr;
- uint32_t sndr_idx; /* LE */
- uint32_t rcvr_idx; /* LE */
- uint8_t unencrypted_ephemeral[WG_CURVE25519_KEY_LEN];
- uint8_t encrypted_nothing[WG_AEAD_LEN(0)];
- uint8_t mac1[WG_BLAKE2S_OUTPUT_LEN];
- uint8_t mac2[WG_BLAKE2S_OUTPUT_LEN];
-} __packed __aligned(4);
-
-struct wg_cookie_resp {
- struct wg_header hdr;
- uint32_t rcvr_idx; /* LE */
- uint8_t nonce[24];
- uint8_t encrypted_cookie[WG_AEAD_LEN(0)];
-} __packed __aligned(4);
-
-struct wg_data {
- struct wg_header hdr;
- uint32_t rcvr_idx;
- uint32_t counter_lo;
- uint32_t counter_hi;
-
- /* followed by encrypted data */
-} __packed __aligned(4);
-
-/*
- * ioctl interface
- */
-
-#include <sys/ioccom.h>
-
-#define WGIFCREATE _IOW('w', 10, unsigned int)
-#define WGIFATTACH _IOW('w', 11, unsigned int)
-#define WGIFDETACH _IOW('w', 12, unsigned int)
-#define WGIFDESTROY _IOW('w', 13, unsigned int)
-
-struct wg_if_sock {
- unsigned int wg_unit;
- int wg_sock;
-};
-
-#define WGIFSSOCK _IOW('w', 14, struct wg_if_sock)
-#define WGIFDSOCK _IOW('w', 15, unsigned int)
-
-struct wg_aead_key {
- uint8_t key[WG_AEAD_CHACHA20_POLY1305_KEY_LEN];
-};
-
-struct wg_if_data_keys {
- unsigned int wg_unit;
- uint32_t wg_tx_index; /* LE */
- uint32_t wg_rx_index; /* LE */
- struct wg_aead_key wg_tx_key;
- struct wg_aead_key wg_rx_key;
-};
-
-#define WGIFADDKEYS _IOW('w', 16, struct wg_if_data_keys)
-#define WGIFDELKEYS _IOW('w', 17, struct wg_if_data_keys)
-
-struct wg_if_role {
- unsigned int wg_unit;
- unsigned int wg_role;
-#define WG_DATA_INITIATOR 0x696e6974
-#define WG_DATA_RESPONDER 0x72657370
-};
-
-#define WGIFSROLE _IOW('w', 18, struct wg_if_role)
-#define WGIFGROLE _IOWR('w', 20, struct wg_if_role)
-
-/*
- * messages via reads
- */
-
-#define WG_MSG_REKEY 0x746b6579
-
-struct wg_msg {
- unsigned int wg_unit;
- unsigned int wg_type;
-};
-
-#endif /* _NET_IF_WG_H_ */