aboutsummaryrefslogtreecommitdiffstats
diff options
context:
space:
mode:
authorMatt Dunwoodie <ncon@mail.noconroy.net>2019-09-28 21:50:20 +0100
committerMatt Dunwoodie <ncon@mail.noconroy.net>2019-09-30 22:56:35 +0100
commit10d253dadaca40fd8b2abce4dc8e5565b46e2510 (patch)
treec54c0857b0feca96a5373c82e44a77ff2470f209
parentAdd more verbose messages (diff)
downloadwireguard-openbsd-10d253dadaca40fd8b2abce4dc8e5565b46e2510.tar.xz
wireguard-openbsd-10d253dadaca40fd8b2abce4dc8e5565b46e2510.zip
Big re-work of wireguard.c
-rw-r--r--src/fixedmap.h28
-rw-r--r--src/if_wg.c824
-rw-r--r--src/kern_wg.c120
-rw-r--r--src/wireguard.c1120
-rw-r--r--src/wireguard.h518
5 files changed, 1186 insertions, 1424 deletions
diff --git a/src/fixedmap.h b/src/fixedmap.h
index 0226fd6..dad4d59 100644
--- a/src/fixedmap.h
+++ b/src/fixedmap.h
@@ -18,29 +18,33 @@
#define __FIXEDMAP_H__
#include <sys/types.h>
-#include <sys/rwlock.h>
+#include <sys/refcnt.h>
+
+#define FM_FOREACH(item, fm) \
+ for (item = (fm)->map; item < (fm)->map + (fm)->size; item++)
+#define FM_FOREACH_FILLED(item, fm) \
+ FM_FOREACH(item, fm) if (item->state == FM_ITEM_FILLED)
+#define FM_FOREACH_EMPTY(item, fm) \
+ FM_FOREACH(item, fm) if (item->state == FM_ITEM_EMPTY)
struct fixed_map {
- struct rwlock lock;
- uint8_t bits;
+ size_t size;
struct map_item {
enum {
FM_ITEM_EMPTY = 0,
- FM_ITEM_RESERVED,
FM_ITEM_FILLED,
- } state;
- uint32_t key;
- void *value;
- } *map;
+ } state;
+ uint32_t key;
+ struct refcnt cnt;
+ void *value;
+ } *map;
};
void fm_init(struct fixed_map *);
void fm_destroy(struct fixed_map *);
-void fm_resize(struct fixed_map *, size_t);
-uint32_t fm_reserve(struct fixed_map *);
-void fm_set(struct fixed_map *, uint32_t, void *);
uint32_t fm_insert(struct fixed_map *, void *);
-void *fm_lookup(struct fixed_map *, uint32_t);
+void *fm_ref(struct fixed_map *, uint32_t);
+void fm_put(struct fixed_map *, uint32_t);
void fm_remove(struct fixed_map *, uint32_t);
#endif /* __FIXEDMAP_H__ */
diff --git a/src/if_wg.c b/src/if_wg.c
index 24cb33e..53b4f15 100644
--- a/src/if_wg.c
+++ b/src/if_wg.c
@@ -51,81 +51,61 @@
#endif
struct wg_tag {
- struct wg_softc *t_sc;
- struct wg_peer *t_peer;
- union wg_ip t_ip;
- enum wg_pkt_type t_type;
+ union wg_ip t_ip;
+ struct wg_softc *t_sc;
+ uint32_t t_dst;
+ enum wg_pkt_type t_type;
enum wg_pkt_state {
+ WG_PKT_STATE_EMPTY = 0,
WG_PKT_STATE_NEW,
WG_PKT_STATE_DONE,
WG_PKT_STATE_DEAD,
- WG_PKT_STATE_REQUEUED,
- } t_state;
+ } t_state;
};
-struct wg_peer {
- RB_ENTRY(wg_peer) p_entry;
- struct wg_softc *p_sc;
- union wg_ip p_ip;
- uint64_t p_tx_bytes, p_rx_bytes;
- struct mbuf_queue p_outgoing;
- SLIST_HEAD(, wg_route) p_aip;
-
- struct wg_handshake p_hs;
- /* TODO add second session */
- struct wg_session p_sess;
- struct wg_timers p_timers;
+struct wg_route {
+ union wg_ip r_ip;
+ struct wg_peer *r_peer;
+ /* TODO do we need r_sc? not used anywhere else */
+ struct wg_softc *r_sc;
+ struct mbuf_queue r_outgoing;
+ SLIST_HEAD(, wg_route_entry) r_aip;
};
-struct wg_route {
- SLIST_ENTRY(wg_route) r_next;
- struct wg_cidr r_cidr;
- struct wg_peer *r_peer;
+struct wg_route_entry {
+ SLIST_ENTRY(wg_route_entry) r_entry;
+ struct wg_cidr r_cidr;
+ struct wg_route *r_dst;
};
struct wg_softc {
- SLIST_ENTRY(wg_softc) sc_next;
- struct ifnet sc_if;
-
- in_port_t sc_port;
-
- struct wg_keypair sc_kp;
- struct wg_cookie_maker sc_cm;
-
- struct art_root *sc_ip_rt;
- struct socket *sc_so4;
+ SLIST_ENTRY(wg_softc) sc_next;
+ struct ifnet sc_if;
+ in_port_t sc_port;
+ struct wg_device sc_dev;
+ struct art_root *sc_ip_rt;
+ struct socket *sc_so4;
#ifdef INET6
- struct art_root *sc_ip6_rt;
- struct socket *sc_so6;
+ struct art_root *sc_ip6_rt;
+ struct socket *sc_so6;
#endif
-
- struct taskq *sc_taskq;
-
- struct task sc_tx_task;
- struct task sc_tx_slow_task;
- struct task sc_rx_task;
- struct task sc_rx_slow_task;
-
- struct mpq sc_tx_queue;
- struct mpq sc_tx_slow_queue;
- struct mpq sc_rx_queue;
- struct mpq sc_rx_slow_queue;
-
- struct fixed_map sc_id_map;
- RB_HEAD(peer_tree, wg_peer) sc_peer_tree;
+ struct taskq *sc_taskq;
+ struct task sc_tx_task;
+ struct task sc_tx_slow_task;
+ struct task sc_rx_task;
+ struct task sc_rx_slow_task;
+ struct mpq sc_tx_queue;
+ struct mpq sc_tx_slow_queue;
+ struct mpq sc_rx_queue;
+ struct mpq sc_rx_slow_queue;
};
struct bloom_bucket wg_bb;
+struct bloom_bucket wg_bb_verified;
+
#define DPRINTF(sc, ...) do { if (ISSET((sc)->sc_if.if_flags, IFF_DEBUG)) \
printf("wg: " __VA_ARGS__); } while (0)
-#define DPRINTF_ERROR(sc, m, e) DPRINTF(sc, "%s: %s\n", m, wg_error_str[e])
-#define DPRINTF_HS(sc, m, hs) DPRINTF(sc, "peer %x: %s\n", \
- wg_handshake_shortkey(hs), m)
-#define DPRINTF_HS_SESS(sc, m, hs, sess) DPRINTF(sc, "peer %x (%x): %s\n", \
- wg_handshake_shortkey(hs), wg_session_id(sess), m)
-#define DPRINTF_HS_ERROR(sc, m, hs, e) DPRINTF(sc, "peer %x: %s: %s\n", \
- wg_handshake_shortkey(hs), m, wg_error_str[e])
#ifdef INET6
#define AF_VAL(af, v4, v6) ((af) == AF_INET ? v4 : (af) == AF_INET6 ? v6 : 0)
@@ -133,27 +113,21 @@ struct bloom_bucket wg_bb;
#define AF_VAL(af, v4, _) ((af) == AF_INET ? v4 : 0)
#endif
+#define drop_pkt_err(e) do { err = e; goto drop; } while (0)
+#define peer_route(p) ((struct wg_route *)(p)->p_arg)
+
/* if_wg.c */
-void wg_softc_peer_destroy(struct wg_softc *, struct wg_peer *);
-struct wg_peer *wg_softc_peer_lookup(struct wg_softc *,
- uint8_t [WG_KEY_SIZE]);
-int wg_softc_route_add(struct wg_softc *, struct wg_cidr *,
- struct wg_peer *);
+int wg_softc_route_add(struct wg_softc *, struct wg_cidr *, struct wg_route *);
int wg_softc_route_delete(struct wg_softc *, struct wg_cidr *);
-struct wg_peer *wg_softc_route_lookup(struct wg_softc *, struct mbuf *, bool);
+struct wg_route *wg_softc_route_lookup(struct wg_softc *, struct mbuf *, bool);
int wg_mbuf_ratelimit(struct wg_softc *, struct mbuf *);
struct wg_tag *wg_mbuf_get_tag(struct mbuf *);
-int wg_peer_cmp(struct wg_peer *, struct wg_peer *);
-void wg_peer_init(struct wg_peer *);
-void wg_peer_broken(struct wg_peer *);
-void wg_peer_cleanup(struct wg_peer *);
-void wg_peer_new_session(struct wg_peer *);
-
-void wg_peer_send_initiation(struct wg_peer *);
-void wg_peer_send_response(struct wg_peer *);
-void wg_peer_send_keepalive(struct wg_peer *);
+void wg_route_init(struct wg_route *);
+void wg_route_broken(struct wg_route *);
+void wg_route_queue(struct wg_route *, enum wg_pkt_type);
+void wg_peer_queue(struct wg_peer *, enum wg_pkt_type);
void wg_encrypt_hs(struct mbuf *);
void wg_encrypt(struct mbuf *);
@@ -180,82 +154,17 @@ void wg_ioctl_get_serv(struct wg_softc *, struct wg_get_serv *);
int wg_ioctl_get_peer(struct wg_softc *, struct wg_get_peer *);
int wg_ioctl(struct ifnet *, u_long, caddr_t);
-RB_PROTOTYPE(peer_tree, wg_peer, p_entry, wg_peer_cmp)
-RB_GENERATE(peer_tree, wg_peer, p_entry, wg_peer_cmp)
-
MPQ_WORKER(wg_tx_slow_task_fn, wg_encrypt_hs, wg_output_deliver);
MPQ_WORKER(wg_tx_task_fn, wg_encrypt, wg_output_deliver);
MPQ_WORKER(wg_rx_slow_task_fn, wg_decrypt_hs, m_freem);
MPQ_WORKER(wg_rx_task_fn, wg_decrypt, wg_input_deliver);
-struct wg_peer *
-wg_softc_peer_create(struct wg_softc *sc, uint8_t key[WG_KEY_SIZE])
-{
- size_t i = 0;
- struct wg_peer *p, *_p;
-
- p = malloc(sizeof(struct wg_peer), M_DEVBUF, M_WAITOK | M_ZERO);
- p->p_sc = sc;
-
- mq_init(&p->p_outgoing, IFQ_MAXLEN, IPL_NET);
- wg_handshake_init(&p->p_hs);
- wg_session_init(&p->p_sess);
-
- wg_handshake_setkey(&p->p_hs, key);
- wg_handshake_setkeypair(&p->p_hs, &sc->sc_kp);
-
- wg_timer_setup(&p->p_timers, p,
- (void (*)(void *)) wg_peer_send_keepalive,
- (void (*)(void *)) wg_peer_broken,
- (void (*)(void *)) wg_peer_send_initiation,
- (void (*)(void *)) wg_peer_cleanup);
-
- /* Insert into tree, then count peers */
- RB_INSERT(peer_tree, &sc->sc_peer_tree, p);
- RB_FOREACH(_p, peer_tree, &sc->sc_peer_tree)
- i++;
- fm_resize(&sc->sc_id_map, i * 3);
-
- DPRINTF_HS(sc, "created", &p->p_hs);
- return p;
-}
-
-void
-wg_softc_peer_destroy(struct wg_softc *sc, struct wg_peer *p)
-{
- struct wg_route *route, *troute;
-
- /* Remove from tree */
- RB_REMOVE(peer_tree, &sc->sc_peer_tree, p);
-
- /* Remove all allowed IPs for peer */
- SLIST_FOREACH_SAFE(route, &p->p_aip, r_next, troute)
- wg_softc_route_delete(sc, &route->r_cidr);
-
- /* Remove all active timers and sessions on for peer */
- wg_peer_cleanup(p);
-
- DPRINTF_HS(sc, "destroyed", &p->p_hs);
-
- /* Finally, no references should exist, we can free */
- free(p, M_DEVBUF, sizeof(struct wg_peer));
-}
-
-struct wg_peer *
-wg_softc_peer_lookup(struct wg_softc *sc, uint8_t key[WG_KEY_SIZE])
-{
- struct wg_peer p;
- wg_handshake_init(&p.p_hs);
- wg_handshake_setkey(&p.p_hs, key);
- return RB_FIND(peer_tree, &sc->sc_peer_tree, &p);
-}
-
int
-wg_softc_route_add(struct wg_softc *sc, struct wg_cidr *cidr, struct wg_peer *p)
+wg_softc_route_add(struct wg_softc *sc, struct wg_cidr *cidr, struct wg_route *r)
{
struct art_node *node;
struct art_root *root;
- struct wg_route *route;
+ struct wg_route_entry *route;
if ((root = AF_VAL(cidr->c_af, sc->sc_ip_rt, sc->sc_ip6_rt)) == NULL)
return EINVAL;
@@ -270,9 +179,9 @@ wg_softc_route_add(struct wg_softc *sc, struct wg_cidr *cidr, struct wg_peer *p)
return EEXIST;
}
- route = malloc(sizeof(struct wg_route), M_DEVBUF, M_WAITOK);
- SLIST_INSERT_HEAD(&p->p_aip, route, r_next);
- route->r_peer = p;
+ route = malloc(sizeof(*route), M_DEVBUF, M_WAITOK);
+ SLIST_INSERT_HEAD(&r->r_aip, route, r_entry);
+ route->r_dst = r;
route->r_cidr = *cidr;
node->an_gc = (struct art_node *) route;
@@ -286,7 +195,7 @@ wg_softc_route_delete(struct wg_softc *sc, struct wg_cidr *cidr)
struct srp_ref sr;
struct art_node *node;
struct art_root *root;
- struct wg_route *route;
+ struct wg_route_entry *route;
if ((root = AF_VAL(cidr->c_af, sc->sc_ip_rt, sc->sc_ip6_rt)) == NULL)
return EINVAL;
@@ -303,8 +212,8 @@ wg_softc_route_delete(struct wg_softc *sc, struct wg_cidr *cidr)
panic("art_delete failed to delete node %p", node);
srp_leave(&sr);
- route = (struct wg_route *) node->an_gc;
- SLIST_REMOVE(&route->r_peer->p_aip, route, wg_route, r_next);
+ route = (struct wg_route_entry *) node->an_gc;
+ SLIST_REMOVE(&route->r_dst->r_aip, route, wg_route_entry, r_entry);
free(route, M_DEVBUF, sizeof(struct wg_route));
free(node, M_DEVBUF, sizeof(*node));
@@ -313,7 +222,7 @@ wg_softc_route_delete(struct wg_softc *sc, struct wg_cidr *cidr)
return 0;
}
-struct wg_peer *
+struct wg_route *
wg_softc_route_lookup(struct wg_softc * sc, struct mbuf * m, bool out)
{
struct ip *iphdr;
@@ -322,7 +231,7 @@ wg_softc_route_lookup(struct wg_softc * sc, struct mbuf * m, bool out)
#endif
struct art_node *node;
struct srp_ref sr;
- struct wg_peer *p = NULL;
+ struct wg_route *r = NULL;
switch (m->m_pkthdr.ph_family) {
case AF_INET:
@@ -346,11 +255,11 @@ wg_softc_route_lookup(struct wg_softc * sc, struct mbuf * m, bool out)
}
if (node != NULL)
- p = ((struct wg_route *) node->an_gc)->r_peer;
+ r = ((struct wg_route_entry *) node->an_gc)->r_dst;
srp_leave(&sr);
- return p;
+ return r;
}
int
@@ -387,18 +296,18 @@ wg_mbuf_ratelimit(struct wg_softc *sc, struct mbuf *m)
*/
/* Calculate cookie and mac from packet */
- wg_cookie_from_token(&c, &sc->sc_cm, token, token_len);
+ wg_cookie_from_token(&c, &sc->sc_dev.d_cookie_maker, token, token_len);
switch (wg_pkt_type(mtod(m, uint8_t *), m->m_pkthdr.len)) {
case WG_PKT_INITIATION:
init = mtod(m, struct wg_msg_initiation *);
- e = wg_handshake_initiation_valid_mac2(&c, init);
+ e = wg_msg_initiation_valid_mac2(init, &c);
sender = init->sender;
mac = init->mac1;
break;
case WG_PKT_RESPONSE:
resp = mtod(m, struct wg_msg_response *);
- e = wg_handshake_response_valid_mac2(&c, resp);
+ e = wg_msg_response_valid_mac2(resp, &c);
sender = resp->sender;
mac = resp->mac1;
break;
@@ -420,7 +329,7 @@ wg_mbuf_ratelimit(struct wg_softc *sc, struct mbuf *m)
DPRINTF(sc, "transmit cookie\n");
- wg_handshake_make_cookie(&sc->sc_kp, &c, sender, mac, cookie);
+ wg_device_make_cookie(&sc->sc_dev, &c, sender, mac, cookie);
bzero(&peernam, sizeof(struct mbuf));
@@ -463,388 +372,247 @@ wg_mbuf_get_tag(struct mbuf *m)
return ((struct wg_tag *)(mtag + 1));
}
-int
-wg_peer_cmp(struct wg_peer *p1, struct wg_peer *p2)
-{
- return wg_handshake_keycmp(&p1->p_hs, &p2->p_hs);
-}
-
+/* The following functions are the peer send functions */
void
-wg_peer_init(struct wg_peer *p)
+wg_route_send_initiation(struct wg_route *r)
{
- wg_handshake_reset_attempts(&p->p_hs);
- wg_peer_send_initiation(p);
+ wg_route_queue(r, WG_PKT_INITIATION);
}
void
-wg_peer_broken(struct wg_peer *p)
+wg_route_broken(struct wg_route *r)
{
- DPRINTF_HS_SESS(p->p_sc, "broken", &p->p_hs, &p->p_sess);
- wg_peer_cleanup(p);
- wg_peer_init(p);
+ wg_peer_clean(r->r_peer);
+ wg_route_queue(r, WG_PKT_INITIATION);
}
void
-wg_peer_cleanup(struct wg_peer *p)
+wg_route_send_keepalive(struct wg_route *r)
{
- DPRINTF_HS(p->p_sc, "cleanup", &p->p_hs);
- fm_remove(&p->p_sc->sc_id_map, wg_handshake_id(&p->p_hs));
- fm_remove(&p->p_sc->sc_id_map, wg_session_id(&p->p_sess));
- wg_handshake_reset(&p->p_hs);
- wg_session_reset(&p->p_sess);
- wg_timer_stop(&p->p_timers);
+ wg_route_queue(r, WG_PKT_TRANSPORT);
}
void
-wg_peer_new_session(struct wg_peer *p)
+wg_route_clean(struct wg_route *r)
{
- struct mbuf *m;
- enum wg_error e;
-
- if ((e = wg_session_confirm(&p->p_sess)) != WG_OK) {
- DPRINTF_HS_ERROR(p->p_sc, "confirm session", &p->p_hs, e);
- return;
- }
-
- DPRINTF_HS_SESS(p->p_sc, "session created", &p->p_hs, &p->p_sess);
-
- wg_timer_session_made(&p->p_timers);
- wg_timer_reinit_unflag(&p->p_timers);
- wg_timer_cleanup_tick(&p->p_timers);
-
- if (mq_empty(&p->p_outgoing))
- wg_peer_send_keepalive(p);
-
- while ((m = mq_dequeue(&p->p_outgoing)) != NULL)
- mpq_enqueue(&p->p_sc->sc_tx_queue, m);
- task_add(p->p_sc->sc_taskq, &p->p_sc->sc_tx_task);
+ wg_peer_clean(r->r_peer);
}
-/* The following functions are the peer send functions */
void
-wg_peer_send_initiation(struct wg_peer *p)
+wg_route_queue(struct wg_route *route, enum wg_pkt_type type)
{
- struct mbuf *m = m_clget(NULL, M_WAITOK, sizeof(struct wg_msg_initiation));
- struct wg_tag *tag = wg_mbuf_get_tag(m);
-
- tag->t_peer = p;
- tag->t_type = WG_PKT_INITIATION;
- tag->t_state = WG_PKT_STATE_NEW;
-
- m->m_len = sizeof(struct wg_msg_initiation);
- m_calchdrlen(m);
-
- mpq_enqueue(&p->p_sc->sc_tx_slow_queue, m);
- task_add(p->p_sc->sc_taskq, &p->p_sc->sc_tx_slow_task);
-}
+ struct mbuf *m;
+ struct wg_tag *tag;
+ struct wg_softc *sc = route->r_sc;
-void
-wg_peer_send_response(struct wg_peer *p)
-{
- struct mbuf *m = m_clget(NULL, M_WAITOK, sizeof(struct wg_msg_response));
- struct wg_tag *tag = wg_mbuf_get_tag(m);
+ m = m_clget(NULL, M_WAITOK, wg_pkt_len[type]);
+ tag = wg_mbuf_get_tag(m);
- tag->t_peer = p;
- tag->t_type = WG_PKT_RESPONSE;
+ tag->t_sc = sc;
+ tag->t_dst = route->r_peer->p_id;
+ tag->t_type = type;
tag->t_state = WG_PKT_STATE_NEW;
- m->m_len = sizeof(struct wg_msg_response);
+ m->m_len = wg_pkt_len[type];
m_calchdrlen(m);
- mpq_enqueue(&p->p_sc->sc_tx_slow_queue, m);
- task_add(p->p_sc->sc_taskq, &p->p_sc->sc_tx_slow_task);
+ if (type == WG_PKT_TRANSPORT) {
+ /* TODO calculate length better */
+ m->m_len = 0;
+ m_calchdrlen(m);
+ mpq_enqueue(&sc->sc_tx_queue, m);
+ task_add(sc->sc_taskq, &sc->sc_tx_task);
+ } else {
+ mpq_enqueue(&sc->sc_tx_slow_queue, m);
+ task_add(sc->sc_taskq, &sc->sc_tx_slow_task);
+ }
}
void
-wg_peer_send_keepalive(struct wg_peer *p)
+wg_peer_queue(struct wg_peer *peer, enum wg_pkt_type type)
{
- struct mbuf *m = m_clget(NULL, M_WAITOK, 0);
- struct wg_tag *tag = wg_mbuf_get_tag(m);
-
- tag->t_peer = p;
- tag->t_type = WG_PKT_TRANSPORT;
- tag->t_state = WG_PKT_STATE_NEW;
-
- m->m_len = 0;
- m_calchdrlen(m);
-
- mpq_enqueue(&p->p_sc->sc_tx_queue, m);
- task_add(p->p_sc->sc_taskq, &p->p_sc->sc_tx_task);
+ wg_route_queue(peer->p_arg, type);
}
void
wg_encrypt_hs(struct mbuf *m)
{
- uint32_t id;
- enum wg_error e;
- struct wg_msg_initiation *init = mtod(m, struct wg_msg_initiation *);
- struct wg_msg_response *resp = mtod(m, struct wg_msg_response *);
+ enum wg_error err;
+ struct wg_session *session;
struct wg_tag *tag = wg_mbuf_get_tag(m);
- struct wg_peer *p = tag->t_peer;
if (tag->t_state != WG_PKT_STATE_NEW)
panic("not a new state packet: %d\n", tag->t_state);
switch (tag->t_type) {
case WG_PKT_INITIATION:
- if (wg_handshake_initiation_ready(&p->p_hs) != WG_OK)
- goto bad;
-
- fm_remove(&p->p_sc->sc_id_map, wg_handshake_id(&p->p_hs));
- wg_handshake_reset(&p->p_hs);
- id = fm_reserve(&p->p_sc->sc_id_map);
- if ((e = wg_handshake_make_initiation(&p->p_hs, id, init)) != WG_OK) {
- fm_remove(&p->p_sc->sc_id_map, id);
- DPRINTF_HS_ERROR(p->p_sc, "make initiation", &p->p_hs, e);
- goto bad;
- }
- fm_set(&p->p_sc->sc_id_map, id, p);
- wg_timer_reinit_flag(&p->p_timers);
-
- DPRINTF_HS(p->p_sc, "transmit initiation", &p->p_hs);
+ if ((err = wg_device_tx_initiation(&tag->t_sc->sc_dev,
+ mtod(m, struct wg_msg_initiation *), tag->t_dst,
+ &session)) != WG_OK)
+ drop_pkt_err(err);
+ wg_timer_reinit_flag(&session->s_peer->p_timers);
break;
case WG_PKT_RESPONSE:
- fm_remove(&p->p_sc->sc_id_map, wg_handshake_id(&p->p_hs));
- id = fm_reserve(&p->p_sc->sc_id_map);
- if ((e = wg_handshake_make_response(&p->p_hs, id, resp)) != WG_OK) {
- fm_remove(&p->p_sc->sc_id_map, id);
- DPRINTF_HS_ERROR(p->p_sc, "make response", &p->p_hs, e);
- goto bad;
- }
- fm_set(&p->p_sc->sc_id_map, id, p);
-
- fm_remove(&p->p_sc->sc_id_map, wg_session_id(&p->p_sess));
- wg_session_reset(&p->p_sess);
-
- if ((e = wg_session_from_handshake(&p->p_sess, &p->p_hs)) != WG_OK) {
- DPRINTF_HS_ERROR(p->p_sc, "make session", &p->p_hs, e);
- goto bad;
- };
-
- DPRINTF_HS(p->p_sc, "transmit response", &p->p_hs);
+ if ((err = wg_device_tx_response(&tag->t_sc->sc_dev,
+ mtod(m, struct wg_msg_response *), tag->t_dst,
+ &session)) != WG_OK)
+ drop_pkt_err(err);
+ wg_timer_reinit_unflag(&session->s_peer->p_timers);
break;
default:
panic("invalid packet type: %d\n", tag->t_type);
}
+ DPRINTF(tag->t_sc, "%s tx for %x\n", wg_pkt_str[tag->t_type],
+ session->s_local_id);
+
m->m_pkthdr.ph_cookie = m;
tag->t_state = WG_PKT_STATE_DONE;
+ tag->t_ip = peer_route(session->s_peer)->r_ip;
+ wg_session_put(session);
return;
-bad:
+drop:
+ DPRINTF(tag->t_sc, "failed %s tx: %s\n", wg_pkt_str[tag->t_type],
+ wg_error_str[err]);
tag->t_state = WG_PKT_STATE_DEAD;
- return;
-
}
void
wg_encrypt(struct mbuf *m)
{
+ enum wg_error err;
+
struct mbuf *em;
+ struct wg_tag *tag;
+ struct wg_session *session;
struct wg_msg_transport *msg;
+
size_t plain_len = m->m_pkthdr.len;
size_t padding_len = WG_PADDING_SIZE(plain_len);
- size_t encrypted_len = WG_ENCRYPTED_SIZE(plain_len + padding_len);
+ size_t clear_len = plain_len + padding_len;
+ size_t encrypted_len = WG_ENCRYPTED_SIZE(clear_len);
size_t total_len = encrypted_len + sizeof(struct wg_msg_transport);
- struct wg_tag *tag = wg_mbuf_get_tag(m);
- struct wg_peer *p = tag->t_peer;
- if (tag->t_state != WG_PKT_STATE_NEW && tag->t_state != WG_PKT_STATE_REQUEUED)
- panic("not a new/pass state packet: %d\n", tag->t_state);
+ tag = wg_mbuf_get_tag(m);
+
+ if (tag->t_state != WG_PKT_STATE_NEW)
+ panic("not a new state packet: %d\n", tag->t_state);
em = m_clget(NULL, M_WAITOK, total_len);
+ m->m_pkthdr.ph_cookie = em;
em->m_len = total_len;
m_calchdrlen(em);
-
msg = mtod(em, struct wg_msg_transport *);
+
m_copydata(m, 0, plain_len, msg->data);
bzero(msg->data + plain_len, padding_len);
- switch (wg_session_encrypt(&p->p_sess, msg, plain_len + padding_len)) {
- case WG_REKEY:
- wg_peer_init(p);
- case WG_OK:
- break;
- case WG_REJECT:
- tag->t_state = WG_PKT_STATE_REQUEUED;
- if (mq_push(&p->p_outgoing, m) != 0)
- counters_inc(p->p_sc->sc_if.if_counters, ifc_oqdrops);
- wg_peer_init(p);
- m_freem(em);
- return;
- default:
- goto free;
- }
+ if ((err = wg_device_tx_transport(&tag->t_sc->sc_dev, msg, clear_len,
+ tag->t_dst, &session)) != WG_OK)
+ goto drop;
if (plain_len > 0) {
- p->p_tx_bytes += m->m_pkthdr.len;
- counters_pkt(p->p_sc->sc_if.if_counters, ifc_opackets,
- ifc_obytes, m->m_pkthdr.len);
- wg_timer_broken_flag(&p->p_timers);
+ counters_pkt(tag->t_sc->sc_if.if_counters, ifc_opackets,
+ ifc_obytes, plain_len);
+ wg_timer_broken_flag(&session->s_peer->p_timers);
} else {
- DPRINTF_HS_SESS(p->p_sc, "tx keepalive", &p->p_hs, &p->p_sess);
+ DPRINTF(tag->t_sc, "send keepalive\n");
}
- wg_timer_keepalive_unflag(&p->p_timers);
- wg_timer_persistent_keepalive_tick(&p->p_timers);
+ wg_timer_keepalive_unflag(&session->s_peer->p_timers);
+ wg_timer_persistent_keepalive_tick(&session->s_peer->p_timers);
m->m_pkthdr.ph_cookie = em;
tag->t_state = WG_PKT_STATE_DONE;
+ tag->t_ip = peer_route(session->s_peer)->r_ip;
+ wg_session_put(session);
return;
-free:
- counters_inc(p->p_sc->sc_if.if_counters, ifc_oerrors);
-
+drop:
m_freem(em);
+ counters_inc(tag->t_sc->sc_if.if_counters, ifc_oerrors);
tag->t_state = WG_PKT_STATE_DEAD;
}
void
wg_decrypt_hs(struct mbuf *m)
{
- enum wg_error e;
- struct wg_peer *p;
- struct wg_handshake hs;
- struct wg_tag *tag = wg_mbuf_get_tag(m);
- struct wg_softc *sc = tag->t_sc;
+ enum wg_error err = WG_OK;
- struct wg_msg_initiation *init;
- struct wg_msg_response *resp;
- struct wg_msg_cookie *cookie;
+ struct wg_tag *tag;
+ struct wg_session *session;
+ tag = wg_mbuf_get_tag(m);
m_defrag(m, M_WAIT);
if (tag->t_state != WG_PKT_STATE_NEW)
- panic("not a new state packet: %d\n", tag->t_state);
+ panic("not a new state packet: %d", tag->t_state);
switch (tag->t_type) {
case WG_PKT_INITIATION:
- if (wg_mbuf_ratelimit(sc, m)) {
- DPRINTF(sc, "dropped initiation");
- return;
- }
-
- init = mtod(m, struct wg_msg_initiation *);
- if ((e = wg_handshake_recv_initiation(&hs, &sc->sc_kp, init))
- != WG_OK) {
- DPRINTF_ERROR(sc, "receive initiation", e);
- return;
- }
-
- if ((p = wg_softc_peer_lookup(sc, hs.hs_spub)) == NULL) {
- DPRINTF(sc, "receive initiation: unknown peer\n");
- return;
- }
+ if (wg_mbuf_ratelimit(tag->t_sc, m))
+ drop_pkt_err(WG_RATELIMIT);
- fm_remove(&sc->sc_id_map, wg_handshake_id(&p->p_hs));
- wg_handshake_reset(&p->p_hs);
+ if ((err = wg_device_rx_initiation(&tag->t_sc->sc_dev,
+ mtod(m, struct wg_msg_initiation *), &session)) != WG_OK)
+ drop_pkt_err(err);
- if ((e = wg_handshake_merge(&p->p_hs, &hs)) != WG_OK) {
- DPRINTF_HS_ERROR(sc, "merge handshake", &p->p_hs, e);
- return;
- }
-
- tag->t_peer = p;
- DPRINTF_HS(sc, "receive initiation", &p->p_hs);
-
- wg_peer_send_response(tag->t_peer);
break;
case WG_PKT_RESPONSE:
- if (wg_mbuf_ratelimit(sc, m)) {
- DPRINTF(sc, "dropped response");
- return;
- }
+ if (wg_mbuf_ratelimit(tag->t_sc, m))
+ drop_pkt_err(WG_RATELIMIT);
- resp = mtod(m, struct wg_msg_response *);
- if ((p = fm_lookup(&sc->sc_id_map, resp->receiver)) == NULL) {
- DPRINTF(sc, "unknown response id: %x\n", resp->receiver);
- return;
- }
+ if ((err = wg_device_rx_response(&tag->t_sc->sc_dev,
+ mtod(m, struct wg_msg_response *), &session)) != WG_OK)
+ drop_pkt_err(err);
- if ((e = wg_handshake_recv_response(&p->p_hs, resp)) != 0) {
- DPRINTF_HS_ERROR(sc, "receive response", &p->p_hs, e);
- return;
- }
-
- fm_remove(&sc->sc_id_map, wg_session_id(&p->p_sess));
- wg_session_reset(&p->p_sess);
-
- if ((e = wg_session_from_handshake(&p->p_sess, &p->p_hs)) != WG_OK) {
- DPRINTF_HS_ERROR(sc, "make session", &p->p_hs, e);
- return;
- };
-
- tag->t_peer = p;
- DPRINTF_HS(sc, "receive response", &p->p_hs);
-
- wg_peer_new_session(tag->t_peer);
+ wg_timer_reinit_unflag(&session->s_peer->p_timers);
break;
case WG_PKT_COOKIE:
- if ((p = fm_lookup(&sc->sc_id_map, cookie->receiver)) == NULL) {
- DPRINTF(sc, "unknown cookie id: %x\n", cookie->receiver);
- return;
- }
-
- cookie = mtod(m, struct wg_msg_cookie *);
- if ((e = wg_handshake_recv_cookie(&p->p_hs, cookie)) != 0) {
- DPRINTF_HS_ERROR(sc, "receive cookie", &p->p_hs, e);
- return;
- }
-
- tag->t_peer = p;
- DPRINTF_HS(sc, "receive cookie", &p->p_hs);
+ if ((err = wg_device_rx_cookie(&tag->t_sc->sc_dev,
+ mtod(m, struct wg_msg_cookie *), &session)) != WG_OK)
+ drop_pkt_err(err);
break;
default:
- panic("not a handshake packet: %d\n", tag->t_type);
+ panic("not a handshake packet: %d", tag->t_type);
}
- if (tag->t_peer != NULL)
- tag->t_peer->p_ip = tag->t_ip;
+ DPRINTF(tag->t_sc, "%s rx for %x\n", wg_pkt_str[tag->t_type], session->s_local_id);
+
+ tag->t_state = WG_PKT_STATE_DONE;
+ peer_route(session->s_peer)->r_ip = tag->t_ip;
+ wg_session_put(session);
+ return;
+drop:
+ counters_inc(tag->t_sc->sc_if.if_counters, ifc_ierrors);
+ DPRINTF(tag->t_sc, "failed %s rx: %s\n", wg_pkt_str[tag->t_type], wg_error_str[err]);
+ tag->t_state = WG_PKT_STATE_DEAD;
}
void
wg_decrypt(struct mbuf *m)
{
- struct wg_peer *p;
+ enum wg_error err;
+ struct wg_session *session;
struct wg_tag *tag = wg_mbuf_get_tag(m);
- struct wg_softc *sc = tag->t_sc;
struct wg_msg_transport *msg = mtod(m, struct wg_msg_transport *);
m_defrag(m, M_WAIT);
- if (tag->t_type != WG_PKT_TRANSPORT)
- panic("not a transport packet: %d\n", tag->t_type);
- if (tag->t_state != WG_PKT_STATE_NEW)
- panic("not a new state packet: %d\n", tag->t_state);
-
- if ((p = fm_lookup(&sc->sc_id_map, msg->receiver)) == NULL) {
- DPRINTF(sc, "unknown transport id: %x\n", msg->receiver);
- goto error;
- }
+ KASSERT(tag->t_type == WG_PKT_TRANSPORT);
+ KASSERT(tag->t_state == WG_PKT_STATE_NEW);
- if (wg_session_ready(&p->p_sess) != WG_OK)
- wg_peer_new_session(p);
-
- switch (wg_session_decrypt(&p->p_sess, msg, m->m_pkthdr.len)) {
- case WG_REKEY:
- wg_peer_init(p);
- case WG_OK:
- break;
- default:
- goto error;
- }
+ if ((err = wg_device_rx_transport(&tag->t_sc->sc_dev, msg,
+ m->m_pkthdr.len, &session)) != WG_OK)
+ drop_pkt_err(err);
m_adj(m, sizeof(struct wg_msg_transport));
- m_calchdrlen(m);
-
if (m->m_pkthdr.len == WG_ENCRYPTED_SIZE(0)) {
- wg_timer_persistent_keepalive_tick(&p->p_timers);
- wg_timer_broken_unflag(&p->p_timers);
tag->t_state = WG_PKT_STATE_DEAD;
- DPRINTF_HS_SESS(p->p_sc, "rx keepalive", &p->p_hs, &p->p_sess);
- return;
+ DPRINTF(tag->t_sc, "recv keepalive\n");
+ goto keepalive;
}
-
if (mtod(m, struct ip *)->ip_v == IPVERSION)
m->m_pkthdr.ph_family = AF_INET;
#ifdef INET6
@@ -852,32 +620,36 @@ wg_decrypt(struct mbuf *m)
IPV6_VERSION)
m->m_pkthdr.ph_family = AF_INET6;
#endif
- else goto error;
+ else
+ drop_pkt_err(WG_INVALID_PKT);
/* Lookup outgoing route for incoming packet ip src, for crypto key
* routing. 'false' indicates src address */
- if (wg_softc_route_lookup(sc, m, false) != p)
- goto error;
+ if (wg_softc_route_lookup(tag->t_sc, m, false) != session->s_peer->p_arg)
+ drop_pkt_err(WG_ALLOWED_IP_FAIL);
- p->p_rx_bytes += m->m_pkthdr.len;
- counters_pkt(sc->sc_if.if_counters, ifc_ipackets, ifc_ibytes,
+ counters_pkt(tag->t_sc->sc_if.if_counters, ifc_ipackets, ifc_ibytes,
m->m_pkthdr.len);
- wg_timer_keepalive_flag(&p->p_timers);
- wg_timer_persistent_keepalive_tick(&p->p_timers);
- wg_timer_broken_unflag(&p->p_timers);
+ wg_timer_keepalive_flag(&session->s_peer->p_timers);
- m->m_pkthdr.ph_ifidx = sc->sc_if.if_index;
- m->m_pkthdr.ph_rtableid = sc->sc_if.if_rdomain;
+ m->m_pkthdr.ph_ifidx = tag->t_sc->sc_if.if_index;
+ m->m_pkthdr.ph_rtableid = tag->t_sc->sc_if.if_rdomain;
m->m_flags &= ~(M_MCAST | M_BCAST);
pf_pkt_addr_changed(m);
- tag->t_peer = p;
- tag->t_peer->p_ip = tag->t_ip;
tag->t_state = WG_PKT_STATE_DONE;
+
+keepalive:
+ wg_timer_persistent_keepalive_tick(&session->s_peer->p_timers);
+ wg_timer_broken_unflag(&session->s_peer->p_timers);
+
+ peer_route(session->s_peer)->r_ip = tag->t_ip;
+ wg_session_put(session);
return;
-error:
+drop:
+ counters_inc(tag->t_sc->sc_if.if_counters, ifc_ierrors);
+ DPRINTF(tag->t_sc, "failed transport: %s\n", wg_error_str[err]);
tag->t_state = WG_PKT_STATE_DEAD;
- counters_inc(sc->sc_if.if_counters, ifc_ierrors);
}
/* The following functions are the stack facing network functions. They will
@@ -900,6 +672,7 @@ wg_output(struct ifnet *ifp, struct mbuf *m, struct sockaddr *sa,
{
int error;
struct wg_tag *tag;
+ struct wg_route *route;
struct wg_softc *sc = ifp->if_softc;
if (!AF_VAL(sa->sa_family, 1, 1)) {
@@ -916,11 +689,9 @@ wg_output(struct ifnet *ifp, struct mbuf *m, struct sockaddr *sa,
m->m_pkthdr.ph_family = sa->sa_family;
- tag->t_type = WG_PKT_TRANSPORT;
- tag->t_state = WG_PKT_STATE_NEW;
- tag->t_peer = wg_softc_route_lookup(sc, m, true);
+ route = wg_softc_route_lookup(sc, m, true);
- if (tag->t_peer == NULL || tag->t_peer->p_ip.sa.sa_family == AF_UNSPEC) {
+ if (route == NULL || route->r_ip.sa.sa_family == AF_UNSPEC) {
if (m->m_pkthdr.ph_family == AF_INET)
icmp_error(m, ICMP_UNREACH, ICMP_UNREACH_HOST, 0, 0);
#ifdef INET6
@@ -940,6 +711,18 @@ wg_output(struct ifnet *ifp, struct mbuf *m, struct sockaddr *sa,
BPF_DIRECTION_IN);
#endif
+ tag->t_sc = sc;
+ tag->t_dst = route->r_peer->p_id;
+ tag->t_type = WG_PKT_TRANSPORT;
+ tag->t_state = WG_PKT_STATE_NEW;
+
+ if (wg_peer_has_transport_session(route->r_peer) != WG_OK) {
+ if (mq_push(&route->r_outgoing, m) != 0)
+ counters_inc(sc->sc_if.if_counters, ifc_oqdrops);
+ wg_route_send_initiation(route);
+ return 0;
+ }
+
if ((error = if_enqueue(ifp, m)) != 0) {
counters_inc(sc->sc_if.if_counters, ifc_oqdrops);
if_start(ifp);
@@ -967,9 +750,9 @@ wg_input(void *_sc, struct mbuf *m, struct sockaddr *sa, int hlen)
goto free;
memcpy(&tag->t_ip.sa, sa, sa->sa_len);
- tag->t_state = WG_PKT_STATE_NEW;
- tag->t_type = wg_pkt_type(mtod(m, uint8_t *), m->m_pkthdr.len);
tag->t_sc = sc;
+ tag->t_type = wg_pkt_type(mtod(m, uint8_t *), m->m_pkthdr.len);
+ tag->t_state = WG_PKT_STATE_NEW;
switch (tag->t_type) {
case WG_PKT_INITIATION:
@@ -1002,12 +785,10 @@ wg_input_deliver(struct mbuf *m)
struct wg_tag *tag = wg_mbuf_get_tag(m);
struct wg_softc *sc = tag->t_sc;
- if (tag->t_state == WG_PKT_STATE_DEAD)
- m_freem(m);
- else if (tag->t_state == WG_PKT_STATE_NEW)
- panic("unexpected state on: %p\n", m);
- else if (tag->t_state == WG_PKT_STATE_REQUEUED)
+ if (tag->t_state == WG_PKT_STATE_NEW)
panic("unexpected state on: %p\n", m);
+ else if (tag->t_state == WG_PKT_STATE_DEAD)
+ m_freem(m);
else if (tag->t_state == WG_PKT_STATE_DONE) {
#if NBPFILTER > 0
if (sc->sc_if.if_bpf)
@@ -1031,38 +812,37 @@ wg_output_deliver(struct mbuf *m)
{
int error;
struct socket *so;
+ struct wg_softc *sc;
struct mbuf peernam, *em;
struct wg_tag *tag = wg_mbuf_get_tag(m);
- struct wg_peer *p = tag->t_peer;
- struct wg_softc *sc = p->p_sc;
- if (tag->t_state == WG_PKT_STATE_DEAD)
- m_freem(m);
- else if (tag->t_state == WG_PKT_STATE_NEW)
+ if (tag->t_state == WG_PKT_STATE_NEW)
panic("unexpected state on: %p\n", m);
- else if (tag->t_state == WG_PKT_STATE_REQUEUED)
- DPRINTF_HS(sc, "data packet waiting for session\n", &tag->t_peer->p_hs);
+ else if (tag->t_state == WG_PKT_STATE_DEAD)
+ m_freem(m);
else if (tag->t_state == WG_PKT_STATE_DONE) {
em = m->m_pkthdr.ph_cookie;
- if (em != m)
- m_freem(m);
-
bzero(&peernam, sizeof(struct mbuf));
peernam.m_type = MT_SONAME;
- peernam.m_data = (caddr_t) &p->p_ip;
- peernam.m_len = p->p_ip.sa.sa_len;
+ peernam.m_data = (caddr_t) &tag->t_ip;
+ peernam.m_len = tag->t_ip.sa.sa_len;
- so = AF_VAL(p->p_ip.sa.sa_family, sc->sc_so4, sc->sc_so6);
- int s = solock(so);
+ sc = tag->t_sc;
+
+ so = AF_VAL(tag->t_ip.sa.sa_family, sc->sc_so4, sc->sc_so6);
if (so) {
+ int s = solock(so);
if ((error = so->so_proto->pr_usrreq(so, PRU_SEND, em,
&peernam, NULL, NULL)) != 0)
DPRINTF(sc, "unable to send: %d\n", error);
+ sounlock(so, s);
} else {
m_freem(em);
}
- sounlock(so, s);
+
+ if (em != m)
+ m_freem(m);
} else {
panic("invalid state: %d\n", tag->t_state);
}
@@ -1102,9 +882,7 @@ wg_clone_create(struct if_clone * ifc, int unit)
inp->inp_upcall_arg = sc;
#endif
- fm_init(&sc->sc_id_map);
- fm_resize(&sc->sc_id_map, 1);
- RB_INIT(&sc->sc_peer_tree);
+ wg_device_init(&sc->sc_dev, wg_peer_queue);
sc->sc_taskq = taskq_create("wg", ncpus, IPL_NET, TASKQ_MPSAFE);
task_set(&sc->sc_tx_task, wg_tx_task_fn, &sc->sc_tx_queue);
@@ -1155,16 +933,13 @@ wg_clone_create(struct if_clone * ifc, int unit)
int
wg_clone_destroy(struct ifnet * ifp)
{
- struct wg_peer *p, *tp;
struct wg_softc *sc = ifp->if_softc;
CLR(sc->sc_if.if_flags, IFF_RUNNING);
- RB_FOREACH_SAFE(p, peer_tree, &sc->sc_peer_tree, tp)
- wg_softc_peer_destroy(sc, p);
+ /* TODO cleanup device */
taskq_destroy(sc->sc_taskq);
- fm_destroy(&sc->sc_id_map);
soclose(sc->sc_so4, 0);
#ifdef INET6
@@ -1245,55 +1020,87 @@ int
wg_ioctl_set_peer(struct wg_softc *sc, u_long cmd, struct wg_set_peer *wsp)
{
int ret = 0;
- struct wg_peer *p;
- struct wg_route *route, *troute;
+ struct wg_peer *peer;
+ struct wg_route *route;
+ struct wg_route_entry *raip, *traip;
+
+ struct wg_pubkey pub;
+ struct wg_privkey shared;
if (suser(curproc))
return EPERM;
/* Silently ignore set peer when the key is for ourselves */
- if (!memcmp(sc->sc_kp.pub, wsp->sp_pubkey, WG_KEY_SIZE))
- return 0;
+ //if (!memcmp(sc->sc_kp.pub, wsp->sp_pubkey, WG_KEY_SIZE))
+ // return 0;
if (IS_NULL_KEY(wsp->sp_pubkey) || IS_MASKED_KEY(wsp->sp_pubkey))
return EINVAL;
- if ((p = wg_softc_peer_lookup(sc, wsp->sp_pubkey)) == NULL)
- p = wg_softc_peer_create(sc, wsp->sp_pubkey);
+
+ /* TODO compat with byte key */
+ memcpy(pub.k, wsp->sp_pubkey, sizeof(pub.k));
+ if ((peer = wg_device_ref_peerkey(&sc->sc_dev, &pub)) == NULL) {
+ route = malloc(sizeof(*route), M_DEVBUF, M_WAITOK | M_ZERO);
+ peer = wg_device_new_peer(&sc->sc_dev, &pub, route);
+ wg_peer_ref(peer);
+
+ route->r_sc = sc;
+ route->r_peer = peer;
+ mq_init(&route->r_outgoing, IFQ_MAXLEN, IPL_NET);
+ SLIST_INIT(&route->r_aip);
+
+ wg_timer_setup(&peer->p_timers, route,
+ (void (*)(void *)) wg_route_send_keepalive,
+ (void (*)(void *)) wg_route_broken,
+ (void (*)(void *)) wg_route_send_initiation,
+ (void (*)(void *)) wg_route_clean);
+ } else {
+ route = peer->p_arg;
+ }
switch (cmd) {
case SIOCSWGPEERIP:
- p->p_ip = wsp->sp_ip;
+ route->r_ip = wsp->sp_ip;
break;
case SIOCSWGPEERPSK:
if (IS_NULL_KEY(wsp->sp_psk) || IS_MASKED_KEY(wsp->sp_psk))
return EINVAL;
- wg_handshake_setshared(&p->p_hs, wsp->sp_psk);
+ /* TODO compat with byte key */
+ memcpy(shared.k, wsp->sp_psk, sizeof(shared.k));
+ wg_peer_setshared(peer, &shared);
break;
case SIOCSWGPEERPKA:
- wg_timer_persistent_keepalive_set(&p->p_timers, wsp->sp_pka);
+ wg_timer_persistent_keepalive_set(&peer->p_timers, wsp->sp_pka);
break;
case SIOCDWGPEER:
- wg_softc_peer_destroy(sc, p);
+ SLIST_FOREACH_SAFE(raip, &route->r_aip, r_entry, traip)
+ wg_softc_route_delete(sc, &raip->r_cidr);
+ wg_peer_put(peer);
+ free(peer->p_arg, M_DEVBUF, sizeof(struct wg_route));
+ wg_peer_drop(peer);
break;
case SIOCSWGPEERAIP:
- ret = wg_softc_route_add(sc, &wsp->sp_aip, p);
+ ret = wg_softc_route_add(sc, &wsp->sp_aip, route);
break;
case SIOCDWGPEERAIP:
ret = wg_softc_route_delete(sc, &wsp->sp_aip);
break;
case SIOCCWGPEERAIP:
- SLIST_FOREACH_SAFE(route, &p->p_aip, r_next, troute)
- wg_softc_route_delete(sc, &route->r_cidr);
+ SLIST_FOREACH_SAFE(raip, &route->r_aip, r_entry, traip)
+ wg_softc_route_delete(sc, &raip->r_cidr);
break;
}
+ wg_peer_put(peer);
+
return ret;
}
int
wg_ioctl_set_serv(struct wg_softc *sc, u_long cmd, struct wg_set_serv *wss)
{
- struct wg_peer *p;
+ struct wg_peer *peer;
struct wg_keypair kp;
+ struct wg_privkey priv;
if (suser(curproc))
return EPERM;
@@ -1304,21 +1111,18 @@ wg_ioctl_set_serv(struct wg_softc *sc, u_long cmd, struct wg_set_serv *wss)
return wg_bind_port(sc);
case SIOCSWGSERVKEY: /* Set WireGuard server private + public key */
/* Check that the new key does not belong to a peer */
- wg_keypair_from_bytes(&kp, wss->ss_privkey);
- if (wg_softc_peer_lookup(sc, kp.pub))
+ memcpy(priv.k, wss->ss_privkey, sizeof(priv.k));
+ wg_keypair_from_key(&kp, &priv);
+ peer = wg_device_ref_peerkey(&sc->sc_dev, &kp.pub);
+ if (peer != NULL){
+ wg_peer_put(peer);
return ENOTSUP;
+ }
if (IS_NULL_KEY(wss->ss_privkey) ||
IS_MASKED_KEY(wss->ss_privkey))
return EINVAL;
- /* Set keypair, clear stack variable */
- sc->sc_kp = kp;
- explicit_bzero(&kp, sizeof(kp));
- /* Clear all current sessions that used the old keypair */
- RB_FOREACH(p, peer_tree, &sc->sc_peer_tree) {
- wg_handshake_setkeypair(&p->p_hs, &sc->sc_kp);
- wg_peer_cleanup(p);
- }
+ wg_device_setkey(&sc->sc_dev, &priv);
break;
case SIOCCWGPEERS: /* Clear all WireGuard peers */
break;
@@ -1330,23 +1134,27 @@ void
wg_ioctl_get_serv(struct wg_softc *sc, struct wg_get_serv *wgs)
{
size_t num = 0;
- struct wg_peer *p;
- uint8_t key[WG_KEY_SIZE];
+ /*struct wg_peer *peer; */
+ struct wg_keypair *kp;
+ //uint8_t key[WG_KEY_SIZE];
wgs->gs_port = sc->sc_port;
- memcpy(wgs->gs_pubkey, sc->sc_kp.pub, sizeof(wgs->gs_pubkey));
+ kp = &sc->sc_dev.d_keypair;
+ memcpy(wgs->gs_pubkey, kp->pub.k, sizeof(wgs->gs_pubkey));
/* We only want to pass the private key to root */
- if (!suser(curproc) || IS_NULL_KEY(sc->sc_kp.priv))
- memcpy(wgs->gs_privkey, sc->sc_kp.priv,
- sizeof(wgs->gs_privkey));
+ if (!suser(curproc) || IS_NULL_KEY(kp->priv.k))
+ memcpy(wgs->gs_privkey, kp->priv.k, sizeof(wgs->gs_privkey));
else
memset(wgs->gs_privkey, 0xff, sizeof(wgs->gs_privkey));
- RB_FOREACH(p, peer_tree, &sc->sc_peer_tree) {
+ struct map_item *item;
+ struct wg_peer *peer;
+
+ FM_FOREACH_FILLED(item, &sc->sc_dev.d_peers) {
if (num < wgs->gs_num_peers) {
- wg_handshake_getkey(&p->p_hs, key);
- copyout(key, wgs->gs_peers[num], WG_KEY_SIZE);
+ peer = item->value;
+ copyout(peer->p_remote.k, wgs->gs_peers[num], WG_KEY_SIZE);
}
num++;
}
@@ -1357,34 +1165,40 @@ int
wg_ioctl_get_peer(struct wg_softc *sc, struct wg_get_peer *wgp)
{
size_t num = 0;
- struct wg_peer *p;
- struct wg_route *r;
- uint8_t shared[WG_KEY_SIZE];
+ struct wg_pubkey pub;
+ struct wg_privkey shared;
+ struct wg_peer *peer;
+ struct wg_route *route;
- if ((p = wg_softc_peer_lookup(sc, wgp->gp_pubkey)) == NULL)
+ memcpy(pub.k, wgp->gp_pubkey, sizeof(pub.k));
+
+ if ((peer = wg_device_ref_peerkey(&sc->sc_dev, &pub)) == NULL)
return ENOENT;
+ route = peer->p_arg;
+
/* We only want to pass the preshared key to root */
- wg_handshake_getshared(&p->p_hs, shared);
- if (!suser(curproc) || IS_NULL_KEY(shared))
- memcpy(wgp->gp_psk, shared, sizeof(wgp->gp_psk));
+ wg_peer_getshared(peer, &shared);
+ if (!suser(curproc) || IS_NULL_KEY(shared.k))
+ memcpy(wgp->gp_psk, shared.k, sizeof(wgp->gp_psk));
else
memset(wgp->gp_psk, 0xff, sizeof(wgp->gp_psk));
- wgp->gp_last_handshake = wg_timer_session_last(&p->p_timers);
- wgp->gp_pka = wg_timer_persistent_keepalive_get(&p->p_timers);
+ wgp->gp_last_handshake = wg_peer_last_handshake(peer);
+ wgp->gp_pka = wg_timer_persistent_keepalive_get(&peer->p_timers);
- wgp->gp_tx_bytes = p->p_tx_bytes;
- wgp->gp_rx_bytes = p->p_rx_bytes;
- wgp->gp_ip = p->p_ip;
+ wgp->gp_tx_bytes = peer->p_tx_bytes;
+ wgp->gp_rx_bytes = peer->p_rx_bytes;
+ wgp->gp_ip = route->r_ip;
- SLIST_FOREACH(r, &p->p_aip, r_next) {
+ struct wg_route_entry *r;
+ SLIST_FOREACH(r, &route->r_aip, r_entry) {
if (num < wgp->gp_num_aip)
- copyout(&r->r_cidr, &wgp->gp_aip[num],
- sizeof(*wgp->gp_aip));
+ copyout(&r->r_cidr, &wgp->gp_aip[num], sizeof(*wgp->gp_aip));
num++;
}
wgp->gp_num_aip = num;
+ wg_peer_put(peer);
return 0;
}
diff --git a/src/kern_wg.c b/src/kern_wg.c
index c319523..4cad94a 100644
--- a/src/kern_wg.c
+++ b/src/kern_wg.c
@@ -162,109 +162,95 @@ bb_load(struct bloom_bucket *bb)
* fixedmap.h
*/
-#define FM_SIZE(fm) (1 << (fm)->bits)
-#define FM_ITEM(fm, k) ((fm)->map[(k) & ((fm)->bits - 1)])
+void fm_resize(struct fixed_map *, size_t);
void
fm_init(struct fixed_map *fm)
{
- rw_init(&fm->lock, "fixed_map");
- fm->bits = 0;
- fm->map = NULL;
-}
-
-void
-fm_destroy(struct fixed_map *fm)
-{
- free(fm->map, M_DEVBUF, 0);
+ bzero(fm, sizeof(*fm));
+ fm_resize(fm, 128);
}
void
fm_resize(struct fixed_map *fm, size_t size)
{
- struct map_item *old_map;
- size_t old_size, i;
-
- if (size <= FM_SIZE(fm))
- return;
-
- rw_enter_write(&fm->lock);
- old_size = FM_SIZE(fm);
- old_map = fm->map;
+ size_t i;
+ size_t osize = fm->size;
+ struct map_item *omap = fm->map;
- for(fm->bits = 1; size >>= 1; fm->bits++);
- fm->map = mallocarray(FM_SIZE(fm), sizeof(*fm->map), M_DEVBUF, M_WAITOK | M_ZERO);
+ for (fm->size = 1; fm->size < size; fm->size <<= 1);
+ KASSERT(fm->size >= osize);
+ fm->map = mallocarray(fm->size, sizeof(*fm->map), M_DEVBUF, M_WAITOK | M_ZERO);
- if (old_map != NULL) {
- for (i = 0; i < old_size; i++)
- FM_ITEM(fm, old_map[i].key) = old_map[i];
- free(old_map, M_DEVBUF, 0);
+ if (omap != NULL) {
+ for (i = 0; i < osize; i++)
+ fm->map[omap[i].key % fm->size] = omap[i];
+ free(omap, M_DEVBUF, 0);
}
- rw_exit_write(&fm->lock);
+}
+
+void
+fm_destroy(struct fixed_map *fm)
+{
+ struct map_item *item;
+ FM_FOREACH_FILLED(item, fm)
+ refcnt_finalize(&item->cnt, "fm_wait");
+ free(fm->map, M_DEVBUF, 0);
}
uint32_t
-fm_reserve(struct fixed_map *fm)
+fm_insert(struct fixed_map *fm, void *v)
{
- size_t i;
- struct map_item *item = NULL;
+ struct map_item *item = NULL, *iter;
- rw_enter_write(&fm->lock);
- for (i = 0; i < FM_SIZE(fm); i++) {
- if (fm->map[i].state == FM_ITEM_EMPTY) {
- item = &fm->map[i];
- break;
- }
+retry:
+ FM_FOREACH_EMPTY(iter, fm) {
+ item = iter;
+ break;
}
- if (item == NULL)
- panic("unable to find space in fixed map");
+ if (item == NULL) {
+ fm_resize(fm, fm->size * 2);
+ goto retry;
+ }
- item->key = (arc4random() << fm->bits) + i;
- item->state = FM_ITEM_RESERVED;
- rw_exit_write(&fm->lock);
+ /* TODO make not zero */
+ item->key = (arc4random() & ~(fm->size - 1)) + (item - fm->map);
+ item->state = FM_ITEM_FILLED;
+ item->value = v;
+ refcnt_init(&item->cnt);
return item->key;
}
void
-fm_set(struct fixed_map *fm, uint32_t k, void *v)
-{
- rw_enter_write(&fm->lock);
- if (FM_ITEM(fm, k).key == k ) {
- FM_ITEM(fm, k).value = v;
- FM_ITEM(fm, k).state = FM_ITEM_FILLED;
- }
- rw_exit_write(&fm->lock);
-}
-
-uint32_t
-fm_insert(struct fixed_map *fm, void *v)
+fm_put(struct fixed_map *fm, uint32_t k)
{
- uint32_t k = fm_reserve(fm);
- fm_set(fm, k, v);
- return k;
+ struct map_item *item = fm->map + (k & (fm->size - 1));
+ if (item->key != k)
+ panic("unexpected key, should exist");
+ refcnt_rele_wake(&item->cnt);
}
void *
-fm_lookup(struct fixed_map *fm, uint32_t k)
+fm_ref(struct fixed_map *fm, uint32_t k)
{
- void *v;
- rw_enter_read(&fm->lock);
- v = FM_ITEM(fm, k).key == k ? FM_ITEM(fm, k).value : NULL;
- rw_exit_read(&fm->lock);
- return v;
+ struct map_item *item = fm->map + (k & (fm->size - 1));
+ if (item->key != k)
+ return NULL;
+ refcnt_take(&item->cnt);
+ return item->value;
}
void
fm_remove(struct fixed_map *fm, uint32_t k)
{
- rw_enter_write(&fm->lock);
- if (FM_ITEM(fm, k).key == k) {
- FM_ITEM(fm, k).value = NULL;
- FM_ITEM(fm, k).state = FM_ITEM_EMPTY;
+ struct map_item *item = fm->map + (k & (fm->size - 1));
+ if (item->key == k) {
+ refcnt_finalize(&item->cnt, "fm_wait");
+ item->state = FM_ITEM_EMPTY;
+ item->value = NULL;
}
- rw_exit_write(&fm->lock);
}
/*
diff --git a/src/wireguard.c b/src/wireguard.c
index f8b9fb4..758d707 100644
--- a/src/wireguard.c
+++ b/src/wireguard.c
@@ -15,10 +15,12 @@
*/
#include <sys/types.h>
+#include <sys/malloc.h>
#include <sys/systm.h>
#include <sys/rwlock.h>
#include <sys/time.h>
#include <sys/antireplay.h>
+#include <lib/libkern/libkern.h>
#include <crypto/blake2s.h>
#include <crypto/curve25519.h>
@@ -38,11 +40,6 @@
#define WG_MSG_COOKIE htole32(3)
#define WG_MSG_TRANSPORT htole32(4)
-/* Temp defines */
-#define offsetof(type, member) ((size_t)(&((type *)0)->member))
-#define CTASSERT(x) extern char _ctassert[(x) ? 1 : -1 ] \
- __attribute__((__unused__))
-
/* Ensure struct sizes are correct at compile time. This allows us to overlay
* the struct on the incoming network packet and access it as if it was a
* struct. */
@@ -50,7 +47,6 @@ CTASSERT(sizeof(struct wg_msg_initiation) == 148);
CTASSERT(sizeof(struct wg_msg_response) == 92);
CTASSERT(sizeof(struct wg_msg_cookie) == 64);
CTASSERT(sizeof(struct wg_msg_transport) == 16);
-//CTASSERT(sizeof(struct wg_timestamp) == 12);
CTASSERT(WG_KEY_SIZE == CHACHA20POLY1305_KEY_SIZE);
CTASSERT(WG_HASH_SIZE == BLAKE2S_HASH_SIZE);
@@ -60,717 +56,751 @@ CTASSERT(WG_MSG_PADDING_SIZE == CHACHA20POLY1305_AUTHTAG_SIZE);
CTASSERT(WG_XNONCE_SIZE == XCHACHA20POLY1305_NONCE_SIZE);
#define ret_error(err) do { ret = err; goto leave; } while (0)
+#define offsetof(type, member) ((size_t)(&((type *)0)->member))
-/* WireGuard crypto helper functions */
+/* wireguard.c */
+void wg_kdf(uint8_t [WG_HASH_SIZE], uint8_t [WG_HASH_SIZE],
+ uint8_t [WG_HASH_SIZE], uint8_t [WG_KEY_SIZE], uint8_t *, size_t);
+void wg_hash2(uint8_t [WG_HASH_SIZE], uint8_t *, size_t, uint8_t *, size_t);
+void wg_mix_hash(struct wg_handshake *, uint8_t *, size_t);
+void wg_mix_dh(struct wg_handshake *, uint8_t [WG_KEY_SIZE], uint8_t [WG_KEY_SIZE]);
+void wg_mix_psk(struct wg_handshake *, uint8_t [WG_KEY_SIZE]);
+void wg_handshake_encrypt(struct wg_handshake *, uint8_t *, uint8_t *, size_t);
+int wg_handshake_decrypt(struct wg_handshake *, uint8_t *, uint8_t *, size_t);
+void wg_timestamp_get(uint8_t [WG_TIMESTAMP_SIZE]);
+int wg_timespec_timedout(struct timespec *, time_t);
+enum wg_pkt_type wg_pkt_type(uint8_t *, size_t);
+void wg_keypair_generate(struct wg_keypair *);
+enum wg_error wg_msg_initiation_valid_mac2(struct wg_msg_initiation *, struct wg_cookie *);
+enum wg_error wg_msg_response_valid_mac2(struct wg_msg_response *, struct wg_cookie *);
+
+/* Some crappy API */
void
-wg_kdf(uint8_t first[WG_HASH_SIZE], uint8_t second[WG_HASH_SIZE], uint8_t third[WG_HASH_SIZE], uint8_t key[WG_KEY_SIZE], uint8_t * input, size_t input_len)
+wg_device_init(struct wg_device *dev,
+ void (*outq)(struct wg_peer *, enum wg_pkt_type))
{
- uint8_t buffer[WG_HASH_SIZE + 1];
- uint8_t secret[WG_HASH_SIZE];
-
- blake2s_hmac(secret, input, key, WG_HASH_SIZE, input_len, WG_KEY_SIZE);
-
- if (!first)
- return;
-
- buffer[0] = 1;
- blake2s_hmac(buffer, buffer, secret, WG_HASH_SIZE, 1, WG_KEY_SIZE);
- memcpy(first, buffer, WG_HASH_SIZE);
+ bzero(dev, sizeof(*dev));
+ dev->d_outq = outq;
+ fm_init(&dev->d_peers);
+ fm_init(&dev->d_sessions);
+ rw_init(&dev->d_lock, "wg_device");
+ /* d_cookie_maker, d_keypair initialised to 0 */
+}
- if (!second)
- return;
+void
+wg_device_setkey(struct wg_device *dev, struct wg_privkey *key)
+{
+ rw_enter_write(&dev->d_lock);
+ wg_keypair_from_key(&dev->d_keypair, key);
+ rw_exit_write(&dev->d_lock);
+}
- buffer[WG_HASH_SIZE] = 2;
- blake2s_hmac(buffer, buffer, secret, WG_HASH_SIZE, WG_HASH_SIZE + 1, WG_KEY_SIZE);
- memcpy(second, buffer, WG_HASH_SIZE);
+struct wg_peer *
+wg_device_new_peer(struct wg_device *dev, struct wg_pubkey *key, void *arg)
+{
+ struct wg_peer *peer;
+ peer = malloc(sizeof(*peer), M_DEVBUF, M_WAITOK | M_ZERO);
- if (!third)
- return;
+ peer->p_arg = arg;
+ peer->p_device = dev;
+ peer->p_remote = *key;
+ rw_init(&peer->p_lock, "wg_peer");
+ SLIST_INIT(&peer->p_sessions);
- buffer[WG_HASH_SIZE] = 3;
- blake2s_hmac(buffer, buffer, secret, WG_HASH_SIZE, WG_HASH_SIZE + 1, WG_KEY_SIZE);
- memcpy(third, buffer, WG_HASH_SIZE);
+ rw_enter_write(&dev->d_lock);
+ peer->p_id = fm_insert(&dev->d_peers, peer);
+ rw_exit_write(&dev->d_lock);
+ return peer;
}
-void
-wg_hash2(uint8_t out[WG_HASH_SIZE], uint8_t * in1, size_t in1_len, uint8_t * in2, size_t in2_len)
+struct wg_peer *
+wg_device_ref_peerkey(struct wg_device *dev, struct wg_pubkey *key)
{
- struct blake2s_state s;
- blake2s_init(&s, WG_HASH_SIZE);
- blake2s_update(&s, in1, in1_len);
- blake2s_update(&s, in2, in2_len);
- blake2s_final(&s, out, WG_HASH_SIZE);
+ uint32_t id = 0;
+ struct wg_peer *peer;
+ struct map_item *item;
+ rw_enter_read(&dev->d_lock);
+ FM_FOREACH_FILLED(item, &dev->d_peers) {
+ peer = item->value;
+ if (memcmp(key->k, peer->p_remote.k, sizeof(key->k)) == 0)
+ id = peer->p_id;
+ }
+ rw_exit_read(&dev->d_lock);
+ return wg_device_ref_peerid(dev, id);
}
-void
-wg_mix_hash(struct wg_handshake * hs, uint8_t * in, size_t in_len)
+struct wg_peer *
+wg_device_ref_peerid(struct wg_device *dev, uint32_t id)
{
- wg_hash2(hs->hs_hash, hs->hs_hash, WG_HASH_SIZE, in, in_len);
+ struct wg_peer *peer;
+ rw_enter_read(&dev->d_lock);
+ peer = fm_ref(&dev->d_peers, id);
+ rw_exit_read(&dev->d_lock);
+ return peer;
}
void
-wg_mix_dh(struct wg_handshake * hs, uint8_t priv[WG_KEY_SIZE], uint8_t pub[WG_KEY_SIZE])
+wg_peer_ref(struct wg_peer *peer)
{
- uint8_t DH [WG_KEY_SIZE];
- crypto_scalarmult_curve25519(DH, priv, pub);
- wg_kdf(hs->hs_ck, hs->hs_k, NULL, hs->hs_ck, DH, WG_KEY_SIZE);
- explicit_bzero(DH, WG_KEY_SIZE);
+ wg_device_ref_peerid(peer->p_device, peer->p_id);
}
void
-wg_mix_psk(struct wg_handshake * hs, uint8_t psk[WG_KEY_SIZE])
+wg_peer_put(struct wg_peer *peer)
{
- uint8_t t [WG_HASH_SIZE];
- wg_kdf(hs->hs_ck, t, hs->hs_k, hs->hs_ck, psk, WG_KEY_SIZE);
- wg_mix_hash(hs, t, WG_HASH_SIZE);
- explicit_bzero(t, WG_HASH_SIZE);
+ struct wg_device *dev = peer->p_device;
+ rw_enter_read(&dev->d_lock);
+ fm_put(&dev->d_peers, peer->p_id);
+ rw_exit_read(&dev->d_lock);
}
void
-wg_handshake_encrypt(struct wg_handshake *hs, uint8_t *dst, uint8_t *src, size_t srclen)
+wg_peer_drop(struct wg_peer *peer)
{
- return chacha20poly1305_encrypt(dst, src, srclen, hs->hs_hash, WG_HASH_SIZE, 0, hs->hs_k);
+ struct wg_device *dev = peer->p_device;
+ rw_enter_read(&dev->d_lock);
+ fm_put(&dev->d_peers, peer->p_id);
+ fm_remove(&dev->d_peers, peer->p_id);
+ rw_exit_read(&dev->d_lock);
}
-int
-wg_handshake_decrypt(struct wg_handshake *hs, uint8_t *dst, uint8_t *src, size_t srclen)
+void
+wg_peer_attach_session(struct wg_peer *peer, struct wg_session *session)
{
- return chacha20poly1305_decrypt(dst, src, srclen, hs->hs_hash, WG_HASH_SIZE, 0, hs->hs_k);
+ rw_assert_wrlock(&peer->p_lock);
+ KASSERT(session->s_peer == NULL);
+ session->s_peer = peer;
+ SLIST_INSERT_HEAD(&peer->p_sessions, session, s_entry);
}
void
-wg_timestamp_get(uint8_t ts[WG_TIMESTAMP_SIZE])
+wg_peer_setshared(struct wg_peer *peer, struct wg_privkey *key)
{
- struct timespec tv;
- getnanotime(&tv);
- *(uint64_t *) (ts) = htobe64(0x400000000000000aULL + tv.tv_sec);
- *(uint32_t *) (ts + 8) = htobe32(tv.tv_nsec);
-}
-
-int
-wg_timespec_timedout(struct timespec * start, time_t timeout)
-{
- struct timespec now;
-
- getnanotime(&now);
-
- return now.tv_sec == start->tv_sec + timeout ?
- now.tv_nsec > start->tv_nsec :
- now.tv_sec > start->tv_sec + timeout;
+ rw_enter_write(&peer->p_lock);
+ peer->p_shared = *key;
+ rw_exit_write(&peer->p_lock);
}
void
-wg_handshake_init(struct wg_handshake *hs)
+wg_peer_getshared(struct wg_peer *peer, struct wg_privkey *key)
{
- bzero(hs, sizeof(*hs));
- rw_init(&hs->hs_lock, "wg_handshake");
+ rw_enter_read(&peer->p_lock);
+ *key = peer->p_shared;
+ rw_exit_read(&peer->p_lock);
}
void
-wg_session_init(struct wg_session *s)
+wg_peer_clean(struct wg_peer *peer)
{
- bzero(s, sizeof(*s));
- rw_init(&s->s_lock, "wg_session");
+ struct wg_session *session;
+ rw_enter_write(&peer->p_lock);
+ SLIST_FOREACH(session, &peer->p_sessions, s_entry)
+ wg_session_drop(session);
+ rw_enter_write(&peer->p_lock);
}
-enum wg_error
-wg_handshake_merge(struct wg_handshake *dst, struct wg_handshake *src)
+struct timespec
+wg_peer_last_handshake(struct wg_peer *peer)
{
- enum wg_error ret = WG_OK;
- rw_enter_write(&dst->hs_lock);
-
- if (src->hs_state != WG_STATE_RECV_INITIATION)
- ret_error(WG_STATE);
-
- if (dst->hs_state != WG_STATE_CLEAN)
- ret_error(WG_STATE);
-
- if (memcmp(dst->hs_spub, src->hs_spub, WG_KEY_SIZE) != 0)
- ret_error(WG_STATE);
-
- if (memcmp(dst->hs_timestamp, src->hs_timestamp, WG_TIMESTAMP_SIZE) >= 0)
- ret_error(WG_TIMESTAMP);
-
- /* Copy state struct */
- dst->hs_ss = src->hs_ss;
-
-leave:
- rw_exit_write(&dst->hs_lock);
- explicit_bzero(src, sizeof(struct wg_handshake));
+ struct timespec ret;
+ rw_enter_read(&peer->p_lock);
+ ret = peer->p_last_handshake;
+ rw_exit_read(&peer->p_lock);
return ret;
}
-void
-wg_handshake_reset_attempts(struct wg_handshake *hs)
-{
- rw_enter_write(&hs->hs_lock);
- hs->hs_attempts = 0;
- rw_exit_write(&hs->hs_lock);
+enum wg_error
+wg_peer_has_transport_session(struct wg_peer *peer)
+{
+ enum wg_error err = WG_REJECT;
+ struct wg_session *session;
+ rw_enter_read(&peer->p_lock);
+ SLIST_FOREACH(session, &peer->p_sessions, s_entry) {
+ if (session->s_state == WG_STATE_INITIATOR ||
+ session->s_state == WG_STATE_RESPONDER) {
+ err = WG_OK;
+ break;
+ }
+ }
+ rw_exit_read(&peer->p_lock);
+ return err;
}
-uint32_t
-wg_handshake_shortkey(struct wg_handshake *hs)
+struct wg_session *
+wg_device_new_session(struct wg_device *dev)
{
- uint32_t shortkey;
- rw_enter_read(&hs->hs_lock);
- shortkey = hs->hs_spub[0] << 24 | hs->hs_spub[1] << 16 |
- hs->hs_spub[2] << 8 | hs->hs_spub[3] << 0;
- rw_exit_read(&hs->hs_lock);
- return shortkey;
-}
+ struct wg_session *session;
+ session = malloc(sizeof(*session), M_DEVBUF, M_WAITOK | M_ZERO);
-int
-wg_handshake_keycmp(struct wg_handshake *hs1, struct wg_handshake *hs2)
-{
- uint8_t k1[WG_KEY_SIZE], k2[WG_KEY_SIZE];
- rw_enter_read(&hs1->hs_lock);
- memcpy(k1, hs1->hs_spub, WG_KEY_SIZE);
- rw_exit_read(&hs1->hs_lock);
- rw_enter_read(&hs2->hs_lock);
- memcpy(k2, hs2->hs_spub, WG_KEY_SIZE);
- rw_exit_read(&hs2->hs_lock);
- return memcmp(k1, k2, WG_KEY_SIZE);
-}
+ getnanotime(&session->s_created);
-void
-wg_handshake_setkeypair(struct wg_handshake *hs, struct wg_keypair *kp)
-{
- rw_enter_write(&hs->hs_lock);
- hs->hs_skey = *kp;
- rw_exit_write(&hs->hs_lock);
+ rw_enter_write(&dev->d_lock);
+ session->s_local_id = fm_insert(&dev->d_sessions, session);
+ rw_exit_write(&dev->d_lock);
+ return session;
}
-void
-wg_handshake_setkey(struct wg_handshake *hs, uint8_t key[WG_KEY_SIZE])
+struct wg_session *
+wg_device_ref_session(struct wg_device *dev, uint32_t id)
{
- rw_enter_write(&hs->hs_lock);
- memcpy(hs->hs_spub, key, WG_KEY_SIZE);
- rw_exit_write(&hs->hs_lock);
+ struct wg_session *session;
+ rw_enter_read(&dev->d_lock);
+ session = fm_ref(&dev->d_sessions, id);
+ rw_exit_read(&dev->d_lock);
+ return session;
}
void
-wg_handshake_setshared(struct wg_handshake *hs, uint8_t shared[WG_KEY_SIZE])
+wg_session_ref(struct wg_session *session)
{
- rw_enter_write(&hs->hs_lock);
- memcpy(hs->hs_shared, shared, WG_KEY_SIZE);
- rw_exit_write(&hs->hs_lock);
+ wg_device_ref_session(session->s_peer->p_device, session->s_local_id);
}
void
-wg_handshake_getkey(struct wg_handshake *hs, uint8_t key[WG_KEY_SIZE])
+wg_session_put(struct wg_session *session)
{
- rw_enter_read(&hs->hs_lock);
- memcpy(key, hs->hs_spub, WG_KEY_SIZE);
- rw_exit_read(&hs->hs_lock);
+ struct wg_device *dev = session->s_peer->p_device;
+ rw_enter_read(&dev->d_lock);
+ fm_put(&dev->d_sessions, session->s_local_id);
+ rw_exit_read(&dev->d_lock);
}
void
-wg_handshake_getshared(struct wg_handshake *hs, uint8_t shared[WG_KEY_SIZE])
+wg_session_drop(struct wg_session *session)
{
- rw_enter_read(&hs->hs_lock);
- memcpy(shared, hs->hs_shared, WG_KEY_SIZE);
- rw_exit_read(&hs->hs_lock);
+ struct wg_device *dev = session->s_peer->p_device;
+ rw_enter_write(&dev->d_lock);
+ fm_put(&dev->d_sessions, session->s_local_id);
+ fm_remove(&dev->d_sessions, session->s_local_id);
+ rw_exit_write(&dev->d_lock);
}
-uint32_t
-wg_handshake_id(struct wg_handshake *hs)
+/* Crypto */
+enum wg_error
+wg_device_rx_initiation(struct wg_device *dev, struct wg_msg_initiation *init,
+ struct wg_session **s)
{
- /* Don't need to lock here, as id should never be changed. */
- return hs->hs_local_id;
-}
+ struct wg_peer *peer;
+ struct wg_handshake hs;
+ struct wg_timestamp ts;
+ struct wg_pubkey remote;
+ struct wg_session *session;
-uint32_t
-wg_session_id(struct wg_session *s)
-{
- /* Don't need to lock here, as id should never be changed. */
- return s->s_local_id;
-}
+ enum wg_error ret = WG_OK;
-void
-wg_handshake_reset(struct wg_handshake *hs)
-{
- rw_enter_write(&hs->hs_lock);
- bzero(&hs->hs_ss, sizeof(hs->hs_ss));
- rw_exit_write(&hs->hs_lock);
-}
+ rw_enter_read(&dev->d_lock);
-void
-wg_session_reset(struct wg_session *s)
-{
- rw_enter_write(&s->s_lock);
- bzero(&s->s_ss, sizeof(s->s_ss));
- rw_exit_write(&s->s_lock);
-}
+ memcpy(hs.h_remote.k, init->ephemeral, WG_KEY_SIZE);
+ wg_keypair_generate(&hs.h_local);
-enum wg_error
-wg_handshake_initiation_ready(struct wg_handshake *hs)
-{
- int timo;
- rw_enter_read(&hs->hs_lock);
- timo = wg_timespec_timedout(&hs->hs_last_initiation, WG_REKEY_TIMEOUT);
- rw_exit_read(&hs->hs_lock);
- return timo ? WG_OK : WG_HS_RATE;
-}
+ wg_hash2(hs.h_ck, WG_CONSTRUCTION, strlen(WG_CONSTRUCTION), NULL, 0);
+ memcpy(hs.h_hash, hs.h_ck, WG_HASH_SIZE);
+ wg_mix_hash(&hs, WG_IDENTIFIER, strlen(WG_IDENTIFIER));
-enum wg_error
-wg_session_from_handshake(struct wg_session *s, struct wg_handshake *hs)
-{
- enum wg_error ret = WG_OK;
+ wg_mix_hash(&hs, dev->d_keypair.pub.k, WG_KEY_SIZE);
+ wg_kdf(hs.h_ck, NULL, NULL, hs.h_ck, hs.h_remote.k, WG_KEY_SIZE);
+ wg_mix_hash(&hs, hs.h_remote.k, WG_KEY_SIZE);
+ wg_mix_dh(&hs, dev->d_keypair.priv.k, hs.h_remote.k);
- rw_enter_write(&s->s_lock);
- rw_enter_write(&hs->hs_lock);
+ if (!wg_handshake_decrypt(&hs, remote.k, init->static_pub,
+ WG_ENCRYPTED_SIZE(sizeof(remote.k))))
+ ret_error(WG_DECRYPT);
- if (s->s_state != WG_STATE_CLEAN)
- ret_error(WG_STATE);
+ wg_mix_hash(&hs, init->static_pub, sizeof(init->static_pub));
+ wg_mix_dh(&hs, dev->d_keypair.priv.k, remote.k);
- if (hs->hs_state == WG_STATE_RECV_RESPONSE)
- wg_kdf(s->s_txkey, s->s_rxkey, NULL, hs->hs_ck, NULL, 0);
- else if (hs->hs_state == WG_STATE_MADE_RESPONSE)
- wg_kdf(s->s_rxkey, s->s_txkey, NULL, hs->hs_ck, NULL, 0);
- else
- ret_error(WG_STATE);
+ if (!wg_handshake_decrypt(&hs, ts.t, init->timestamp,
+ WG_ENCRYPTED_SIZE(sizeof(ts.t))))
+ ret_error(WG_DECRYPT);
- antireplay_init(&s->s_ar);
- s->s_txcounter = s->s_rxcounter = 0;
- s->s_local_id = hs->hs_local_id;
- s->s_remote_id = hs->hs_remote_id;
- s->s_state = hs->hs_state;
+ wg_mix_hash(&hs, init->timestamp, sizeof(init->timestamp));
+ wg_hash2(hs.h_k, WG_MAC1, strlen(WG_MAC1), dev->d_keypair.pub.k,
+ WG_KEY_SIZE);
- getnanotime(&s->s_created);
-leave:
- bzero(&hs->hs_ss, sizeof(hs->hs_ss));
- rw_exit_write(&hs->hs_lock);
- rw_exit_write(&s->s_lock);
- return ret;
-}
+ blake2s(hs.h_mac, (void *)init, hs.h_k, sizeof(hs.h_mac),
+ offsetof(struct wg_msg_initiation, mac1), sizeof(hs.h_k));
-enum wg_error
-wg_session_confirm(struct wg_session *s)
-{
- enum wg_error ret = WG_OK;
- rw_enter_write(&s->s_lock);
- if (s->s_state == WG_STATE_RECV_RESPONSE)
- s->s_state = WG_STATE_INITIATOR;
- else if (s->s_state == WG_STATE_MADE_RESPONSE)
- s->s_state = WG_STATE_RESPONDER;
- else
- ret = WG_STATE;
- rw_exit_write(&s->s_lock);
- return ret;
-}
+ if (timingsafe_bcmp(hs.h_mac, init->mac1, WG_MAC_SIZE))
+ ret_error(WG_MAC);
-enum wg_error
-wg_session_ready(struct wg_session *s)
-{
- enum wg_error e;
- rw_enter_read(&s->s_lock);
- e = (s->s_state == WG_STATE_INITIATOR ||
- s->s_state == WG_STATE_RESPONDER) ? WG_OK : WG_STATE;
- rw_exit_read(&s->s_lock);
- return e;
-}
+ if ((peer = wg_device_ref_peerkey(dev, &remote)) == NULL)
+ ret_error(WG_UNKNOWN_PEER);
-int
-wg_handshake_need_cookie(struct wg_handshake *hs)
-{
- return !wg_timespec_timedout(&hs->hs_cookie.time, WG_COOKIE_VALID_TIME);
-}
+ if (memcmp(&ts, &peer->p_timestamp, WG_TIMESTAMP_SIZE) < 0)
+ ret_error(WG_TIMESTAMP);
-int
-wg_session_reject_tx(struct wg_session *s)
-{
- return wg_timespec_timedout(&s->s_created, WG_REJECT_AFTER_TIME) ||
- s->s_txcounter > WG_REJECT_AFTER_MESSAGES;
-}
+ rw_exit_read(&dev->d_lock);
-int
-wg_session_reject_rx(struct wg_session *s)
-{
- return wg_timespec_timedout(&s->s_created, WG_REJECT_AFTER_TIME) ||
- s->s_rxcounter > WG_REJECT_AFTER_MESSAGES;
-}
+ /* TODO Fix locking */
+ /* TODO rotate out old peers */
+ session = wg_device_new_session(dev);
+ session->s_handshake = hs;
+ session->s_remote_id = init->sender;
+ session->s_state = WG_STATE_RECV_INITIATION;
+ explicit_bzero(&hs, sizeof(hs));
+ *s = session;
-int
-wg_session_rekey_tx(struct wg_session *s)
-{
- if (s->s_state == WG_STATE_INITIATOR &&
- wg_timespec_timedout(&s->s_created, WG_REKEY_AFTER_TIME))
- return 1;
- if (s->s_txcounter > WG_REKEY_AFTER_MESSAGES)
- return 1;
- return 0;
-}
+ rw_enter_write(&peer->p_lock);
+ wg_peer_attach_session(peer, session);
+ rw_exit_write(&peer->p_lock);
-int
-wg_session_rekey_rx(struct wg_session *s)
-{
- if (s->s_state == WG_STATE_RESPONDER &&
- wg_timespec_timedout(&s->s_created, WG_REKEY_AFTER_TIME_RECV))
- return 1;
- return 0;
-}
+ dev->d_outq(peer, WG_PKT_RESPONSE);
+
+ wg_peer_put(peer);
+ return WG_OK;
+leave:
+ rw_exit_read(&dev->d_lock);
+ explicit_bzero(&hs, sizeof(hs));
+ return ret;
+}
enum wg_error
-wg_handshake_make_initiation(struct wg_handshake *hs, uint32_t id, struct wg_msg_initiation *m)
+wg_device_rx_response(struct wg_device *dev, struct wg_msg_response *resp,
+ struct wg_session **s)
{
+ struct wg_handshake hs;
+ struct wg_session *session;
+
enum wg_error ret = WG_OK;
- rw_enter_write(&hs->hs_lock);
+ if ((session = wg_device_ref_session(dev, resp->receiver)) == NULL)
+ return WG_ID;
- if (hs->hs_state != WG_STATE_CLEAN)
+ rw_enter_read(&dev->d_lock);
+ rw_enter_write(&session->s_lock);
+
+ if (session->s_state != WG_STATE_MADE_INITIATION)
ret_error(WG_STATE);
- if (hs->hs_attempts >= WG_REKEY_ATTEMPT_COUNT)
- ret_error(WG_HS_ATTEMPTS);
+ /* Make a local copy so we don't clobber the real HS */
+ hs = session->s_handshake;
- wg_keypair_generate(&hs->hs_ekey);
+ memcpy(hs.h_remote.k, resp->ephemeral, WG_KEY_SIZE);
- m->type = WG_MSG_INITIATION;
- m->sender = hs->hs_local_id = id;
- memcpy(m->ephemeral, hs->hs_ekey.pub, WG_KEY_SIZE);
+ wg_kdf(hs.h_ck, NULL, NULL, hs.h_ck, hs.h_remote.k, WG_KEY_SIZE);
+ wg_mix_hash(&hs, hs.h_remote.k, WG_KEY_SIZE);
- wg_hash2(hs->hs_ck, WG_CONSTRUCTION, strlen(WG_CONSTRUCTION), NULL, 0);
- memcpy(hs->hs_hash, hs->hs_ck, WG_HASH_SIZE);
- wg_mix_hash(hs, WG_IDENTIFIER, strlen(WG_IDENTIFIER));
+ wg_mix_dh(&hs, hs.h_local.priv.k, hs.h_remote.k);
+ wg_mix_dh(&hs, dev->d_keypair.priv.k, hs.h_remote.k);
- wg_mix_hash(hs, hs->hs_spub, WG_KEY_SIZE);
- wg_kdf(hs->hs_ck, NULL, NULL, hs->hs_ck, hs->hs_ekey.pub, WG_KEY_SIZE);
- wg_mix_hash(hs, hs->hs_ekey.pub, WG_KEY_SIZE);
- wg_mix_dh(hs, hs->hs_ekey.priv, hs->hs_spub);
+ wg_mix_psk(&hs, session->s_peer->p_shared.k);
- wg_handshake_encrypt(hs, m->static_pub, hs->hs_skey.pub, WG_KEY_SIZE);
-
- wg_mix_hash(hs, m->static_pub, WG_ENCRYPTED_SIZE(WG_KEY_SIZE));
- wg_mix_dh(hs, hs->hs_skey.priv, hs->hs_spub);
+ if (!wg_handshake_decrypt(&hs, NULL, resp->empty, WG_ENCRYPTED_SIZE(0)))
+ ret_error(WG_DECRYPT);
- wg_timestamp_get(m->timestamp);
+ wg_mix_hash(&hs, resp->empty, WG_ENCRYPTED_SIZE(0));
- wg_handshake_encrypt(hs, m->timestamp, m->timestamp, WG_TIMESTAMP_SIZE);
- wg_mix_hash(hs, m->timestamp, WG_ENCRYPTED_SIZE(WG_TIMESTAMP_SIZE));
- wg_hash2(hs->hs_k, WG_MAC1, strlen(WG_MAC1), hs->hs_spub, WG_KEY_SIZE);
+ wg_hash2(hs.h_k, WG_MAC1, strlen(WG_MAC1), dev->d_keypair.pub.k,
+ WG_KEY_SIZE);
+ blake2s(hs.h_mac, (void *)resp, hs.h_k, sizeof(hs.h_mac),
+ offsetof(struct wg_msg_response, mac1), sizeof(hs.h_k));
- blake2s(m->mac1, (void *) m, hs->hs_k, sizeof(m->mac1), offsetof(struct wg_msg_initiation, mac1), sizeof(hs->hs_k));
- memcpy(hs->hs_mac, m->mac1, sizeof(hs->hs_mac));
+ if (timingsafe_bcmp(hs.h_mac, resp->mac1, WG_MAC_SIZE))
+ ret_error(WG_MAC);
- if (wg_handshake_need_cookie(hs))
- blake2s(m->mac2, (void *)m, hs->hs_cookie.cookie, sizeof(m->mac2), offsetof(struct wg_msg_initiation, mac2), sizeof(hs->hs_cookie.cookie));
- else
- bzero(m->mac2, WG_MAC_SIZE);
+ session->s_handshake = hs;
+ session->s_remote_id = resp->sender;
+ session->s_state = WG_STATE_INITIATOR;
- hs->hs_attempts++;
- getnanotime(&hs->hs_last_initiation);
- hs->hs_state = WG_STATE_MADE_INITIATION;
- hs->hs_remote_id = 0;
+ wg_kdf(session->s_keyset.k_txkey.k, session->s_keyset.k_rxkey.k,
+ NULL, session->s_handshake.h_ck, NULL, 0);
+ antireplay_init(&session->s_keyset.k_ar);
+ *s = session;
leave:
- rw_exit_write(&hs->hs_lock);
+ rw_exit_write(&session->s_lock);
+ rw_exit_read(&dev->d_lock);
+ explicit_bzero(&hs, sizeof(hs));
return ret;
+
}
enum wg_error
-wg_handshake_make_response(struct wg_handshake *hs, uint32_t id, struct wg_msg_response *m)
+wg_device_rx_cookie(struct wg_device *dev, struct wg_msg_cookie *cookie,
+ struct wg_session **s)
{
- enum wg_error ret = WG_OK;
-
- rw_enter_write(&hs->hs_lock);
-
- if (hs->hs_state != WG_STATE_RECV_INITIATION)
- ret_error(WG_STATE);
-
- m->type = WG_MSG_RESPONSE;
- m->sender = hs->hs_local_id = id;
- m->receiver = hs->hs_remote_id;
-
- wg_kdf(hs->hs_ck, NULL, NULL, hs->hs_ck, hs->hs_ekey.pub, WG_KEY_SIZE);
- wg_mix_hash(hs, hs->hs_ekey.pub, WG_KEY_SIZE);
+ uint8_t key[WG_KEY_SIZE];
+ uint8_t value[WG_COOKIE_SIZE];
+ struct wg_session *session;
- memcpy(m->ephemeral, hs->hs_ekey.pub, WG_KEY_SIZE);
+ enum wg_error ret = WG_OK;
- wg_mix_dh(hs, hs->hs_ekey.priv, hs->hs_epub);
- wg_mix_dh(hs, hs->hs_ekey.priv, hs->hs_spub);
+ if ((session = wg_device_ref_session(dev, cookie->receiver)) == NULL)
+ return WG_ID;
- wg_mix_psk(hs, hs->hs_shared);
+ rw_enter_write(&session->s_peer->p_lock);
+ rw_enter_read(&session->s_lock);
- wg_handshake_encrypt(hs, m->empty, NULL, 0);
+ if (session->s_state != WG_STATE_WAIT_CONFIRM &&
+ session->s_state != WG_STATE_MADE_INITIATION)
+ ret_error(WG_STATE);
- wg_mix_hash(hs, m->empty, WG_ENCRYPTED_SIZE(0));
+ wg_hash2(key, WG_COOKIE, strlen(WG_COOKIE), session->s_peer->p_remote.k,
+ WG_KEY_SIZE);
- wg_hash2(hs->hs_k, WG_MAC1, strlen(WG_MAC1), hs->hs_spub, WG_KEY_SIZE);
- blake2s(m->mac1, (void *)m, hs->hs_k, sizeof(m->mac1), offsetof(struct wg_msg_response, mac1), sizeof(hs->hs_k));
- memcpy(hs->hs_mac, m->mac1, sizeof(hs->hs_mac));
+ if(!xchacha20poly1305_decrypt(value, cookie->value, sizeof(cookie->value),
+ session->s_handshake.h_mac, WG_MAC_SIZE, cookie->nonce, key))
+ ret_error(WG_DECRYPT);
- if (wg_handshake_need_cookie(hs))
- blake2s(m->mac2, (void *)m, hs->hs_cookie.cookie, sizeof(m->mac2), offsetof(struct wg_msg_response, mac2), sizeof(hs->hs_cookie.cookie));
- else
- bzero(m->mac2, WG_MAC_SIZE);
+ memcpy(session->s_peer->p_cookie.cookie, value,
+ sizeof(session->s_peer->p_cookie.cookie));
+ getnanotime(&session->s_peer->p_cookie.time);
- hs->hs_state = WG_STATE_MADE_RESPONSE;
+ *s = session;
leave:
- rw_exit_write(&hs->hs_lock);
+ rw_exit_read(&session->s_lock);
+ rw_exit_write(&session->s_peer->p_lock);
return ret;
}
-void
-wg_cookie_from_token(struct wg_cookie *c, struct wg_cookie_maker *cm, uint8_t *ip, uint8_t ip_len)
+enum wg_error
+wg_device_rx_transport(struct wg_device *dev, struct wg_msg_transport *msg,
+ size_t len, struct wg_session **s)
{
- if (wg_timespec_timedout(&cm->time, WG_COOKIE_VALID_TIME)) {
- getnanotime(&cm->time);
- arc4random_buf(cm->seed, sizeof(cm->seed));
+ enum wg_error ret = WG_OK;
+ size_t data_len = len - offsetof(struct wg_msg_transport, data);
+ uint64_t counter = letoh64(msg->counter);
+ struct wg_session *session;
+
+ if ((session = wg_device_ref_session(dev, msg->receiver)) == NULL)
+ return WG_ID;
+
+ rw_enter_read(&session->s_lock);
+
+ if (session->s_state == WG_STATE_WAIT_CONFIRM) {
+ wg_kdf(session->s_keyset.k_rxkey.k, session->s_keyset.k_txkey.k,
+ NULL, session->s_handshake.h_ck, NULL, 0);
+ antireplay_init(&session->s_keyset.k_ar);
+ session->s_state = WG_STATE_RESPONDER;
}
- blake2s(c->cookie, ip, cm->seed, WG_MAC_SIZE, ip_len, WG_COOKIE_SIZE);
-}
+ if (session->s_state != WG_STATE_INITIATOR &&
+ session->s_state != WG_STATE_RESPONDER)
+ ret_error(WG_STATE);
-enum wg_error
-wg_handshake_make_cookie(struct wg_keypair *kp, struct wg_cookie *c, uint32_t sender, uint8_t mac[WG_MAC_SIZE], struct wg_msg_cookie *m)
-{
- uint8_t key[WG_KEY_SIZE]; // Same as WG_HASH_SIZE
+ if (wg_timespec_timedout(&session->s_created, WG_REJECT_AFTER_TIME) ||
+ session->s_keyset.k_rxcounter > WG_REJECT_AFTER_MESSAGES)
+ ret_error(WG_REJECT);
- m->type = WG_MSG_COOKIE;
- m->receiver = sender;
- arc4random_buf(m->nonce, sizeof(m->nonce));
+ if (!chacha20poly1305_decrypt(msg->data, msg->data, data_len, NULL, 0,
+ msg->counter, session->s_keyset.k_rxkey.k))
+ ret_error(WG_DECRYPT);
- wg_hash2(key, WG_COOKIE, strlen(WG_COOKIE), kp->pub, WG_KEY_SIZE);
- xchacha20poly1305_encrypt(m->cookie, c->cookie, WG_MAC_SIZE, mac, WG_MAC_SIZE, m->nonce, key);
+ /* TODO fix read lock -> write lock */
+ if (antireplay_update(&session->s_keyset.k_ar, counter))
+ ret_error(WG_REPLAY);
- return WG_OK;
-}
+ session->s_keyset.k_rxcounter = counter;
-enum wg_error
-wg_handshake_initiation_valid_mac2(struct wg_cookie *c, struct wg_msg_initiation *m)
-{
- uint8_t mac[WG_MAC_SIZE];
- blake2s(mac, (uint8_t *)m, c->cookie, WG_MAC_SIZE, offsetof(struct wg_msg_initiation, mac2), WG_COOKIE_SIZE);
- return timingsafe_bcmp(mac, m->mac2, WG_MAC_SIZE) ? WG_MAC : WG_OK;
-}
+ if (session->s_state == WG_STATE_RESPONDER &&
+ wg_timespec_timedout(&session->s_created, WG_REKEY_AFTER_TIME_RECV))
+ dev->d_outq(session->s_peer, WG_PKT_INITIATION);
-enum wg_error
-wg_handshake_response_valid_mac2(struct wg_cookie *c, struct wg_msg_response *m)
-{
- uint8_t mac[WG_MAC_SIZE];
- blake2s(mac, (uint8_t *)m, c->cookie, WG_MAC_SIZE, offsetof(struct wg_msg_response, mac2), WG_COOKIE_SIZE);
- return timingsafe_bcmp(mac, m->mac2, WG_MAC_SIZE) ? WG_MAC : WG_OK;
+ *s = session;
+leave:
+ rw_exit_read(&session->s_lock);
+ return ret;
}
+
enum wg_error
-wg_handshake_recv_initiation(struct wg_handshake *ths, struct wg_keypair *kp, struct wg_msg_initiation *m)
+wg_device_tx_initiation(struct wg_device *dev, struct wg_msg_initiation *init,
+ uint32_t id, struct wg_session **s)
{
+ struct wg_peer *peer;
+ struct wg_handshake *hs;
+ struct wg_session *session;
+
enum wg_error ret = WG_OK;
- /* Setup */
- ths->hs_state = WG_STATE_RECV_INITIATION;
- ths->hs_skey = *kp;
- ths->hs_local_id = 0;
- ths->hs_remote_id = m->sender;
- memcpy(ths->hs_epub, m->ephemeral, WG_KEY_SIZE);
+ if ((peer = wg_device_ref_peerid(dev, id)) == NULL)
+ return WG_ID;
- wg_keypair_generate(&ths->hs_ekey);
+ rw_enter_write(&peer->p_lock);
- /* Initialise hash */
- wg_hash2(ths->hs_ck, WG_CONSTRUCTION, strlen(WG_CONSTRUCTION), NULL, 0);
- memcpy(ths->hs_hash, ths->hs_ck, WG_HASH_SIZE);
- wg_mix_hash(ths, WG_IDENTIFIER, strlen(WG_IDENTIFIER));
+ if (!wg_timespec_timedout(&peer->p_last_initiation, WG_REKEY_TIMEOUT))
+ ret_error(WG_HS_RATE);
- /* KDF and mix */
- wg_mix_hash(ths, ths->hs_skey.pub, WG_KEY_SIZE);
- wg_kdf(ths->hs_ck, NULL, NULL, ths->hs_ck, ths->hs_epub, WG_KEY_SIZE);
- wg_mix_hash(ths, ths->hs_epub, WG_KEY_SIZE);
- wg_mix_dh(ths, ths->hs_skey.priv, ths->hs_epub);
+ if (peer->p_attempts >= WG_REKEY_ATTEMPT_COUNT)
+ ret_error(WG_HS_ATTEMPTS);
- if (!wg_handshake_decrypt(ths, ths->hs_spub, m->static_pub, WG_ENCRYPTED_SIZE(WG_KEY_SIZE)))
- ret_error(WG_DECRYPT);
+ session = wg_device_new_session(dev);
+ hs = &session->s_handshake;
- wg_mix_hash(ths, m->static_pub, WG_ENCRYPTED_SIZE(WG_KEY_SIZE));
- wg_mix_dh(ths, ths->hs_skey.priv, ths->hs_spub);
+ wg_keypair_generate(&hs->h_local);
- if (!wg_handshake_decrypt(ths, ths->hs_timestamp, m->timestamp, WG_ENCRYPTED_SIZE(WG_TIMESTAMP_SIZE)))
- ret_error(WG_DECRYPT);
+ init->type = WG_MSG_INITIATION;
+ init->sender = session->s_local_id;
+ memcpy(init->ephemeral, hs->h_local.pub.k, WG_KEY_SIZE);
- wg_mix_hash(ths, m->timestamp, WG_ENCRYPTED_SIZE(WG_TIMESTAMP_SIZE));
- wg_hash2(ths->hs_k, WG_MAC1, strlen(WG_MAC1), ths->hs_skey.pub, WG_KEY_SIZE);
+ wg_hash2(hs->h_ck, WG_CONSTRUCTION, strlen(WG_CONSTRUCTION), NULL, 0);
+ memcpy(hs->h_hash, hs->h_ck, WG_HASH_SIZE);
+ wg_mix_hash(hs, WG_IDENTIFIER, strlen(WG_IDENTIFIER));
- blake2s(ths->hs_mac, (void *)m, ths->hs_k, sizeof(ths->hs_mac), offsetof(struct wg_msg_initiation, mac1), sizeof(ths->hs_k));
+ wg_mix_hash(hs, peer->p_remote.k, WG_KEY_SIZE);
+ wg_kdf(hs->h_ck, NULL, NULL, hs->h_ck, hs->h_local.pub.k, WG_KEY_SIZE);
+ wg_mix_hash(hs, hs->h_local.pub.k, WG_KEY_SIZE);
+ wg_mix_dh(hs, hs->h_local.priv.k, peer->p_remote.k);
- if (timingsafe_bcmp(ths->hs_mac, m->mac1, WG_MAC_SIZE))
- ret_error(WG_MAC);
+ wg_handshake_encrypt(hs, init->static_pub, dev->d_keypair.pub.k,
+ WG_KEY_SIZE);
+
+ wg_mix_hash(hs, init->static_pub, WG_ENCRYPTED_SIZE(WG_KEY_SIZE));
+ wg_mix_dh(hs, dev->d_keypair.priv.k, peer->p_remote.k);
+
+ wg_timestamp_get(init->timestamp);
+ wg_handshake_encrypt(hs, init->timestamp, init->timestamp, WG_TIMESTAMP_SIZE);
+ wg_mix_hash(hs, init->timestamp, WG_ENCRYPTED_SIZE(WG_TIMESTAMP_SIZE));
+ wg_hash2(hs->h_k, WG_MAC1, strlen(WG_MAC1), peer->p_remote.k, WG_KEY_SIZE);
+
+ blake2s(init->mac1, (void *) init, hs->h_k, sizeof(init->mac1),
+ offsetof(struct wg_msg_initiation, mac1), sizeof(hs->h_k));
+ memcpy(hs->h_mac, init->mac1, sizeof(hs->h_mac));
+
+ if (wg_timespec_timedout(&peer->p_cookie.time, WG_COOKIE_VALID_TIME))
+ bzero(init->mac2, WG_MAC_SIZE);
+ else
+ blake2s(init->mac2, (void *)init, peer->p_cookie.cookie,
+ sizeof(init->mac2), offsetof(struct wg_msg_initiation, mac2),
+ sizeof(peer->p_cookie.cookie));
+
+ peer->p_attempts++;
+ getnanotime(&peer->p_last_initiation);
+ session->s_state = WG_STATE_MADE_INITIATION;
+ wg_peer_attach_session(peer, session);
+ *s = session;
leave:
+ wg_peer_put(peer);
+ rw_exit_write(&peer->p_lock);
return ret;
}
-
enum wg_error
-wg_handshake_recv_response(struct wg_handshake *hs, struct wg_msg_response *m)
+wg_device_tx_response(struct wg_device *dev, struct wg_msg_response *resp,
+ uint32_t id, struct wg_session **s)
{
+ struct wg_peer *peer;
+ struct wg_handshake *hs;
+ struct wg_session *session;
+
enum wg_error ret = WG_OK;
- uint8_t mac1[WG_MAC_SIZE];
- struct wg_handshake tmp_hs;
- rw_enter_write(&hs->hs_lock);
+ if ((peer = wg_device_ref_peerid(dev, id)) == NULL)
+ return WG_ID;
- if (hs->hs_state != WG_STATE_MADE_INITIATION)
- ret_error(WG_STATE);
+ rw_enter_read(&peer->p_lock);
- if (m->receiver != hs->hs_local_id)
+ if ((session = SLIST_FIRST(&peer->p_sessions)) == NULL)
ret_error(WG_ID);
- tmp_hs = *hs;
+ /* TODO lock session */
- tmp_hs.hs_remote_id = m->sender;
- memcpy(tmp_hs.hs_epub, m->ephemeral, WG_KEY_SIZE);
+ if (session->s_state != WG_STATE_RECV_INITIATION)
+ ret_error(WG_STATE);
- wg_kdf(tmp_hs.hs_ck, NULL, NULL, tmp_hs.hs_ck, tmp_hs.hs_epub, WG_KEY_SIZE);
- wg_mix_hash(&tmp_hs, tmp_hs.hs_epub, WG_KEY_SIZE);
+ wg_session_ref(session);
- wg_mix_dh(&tmp_hs, tmp_hs.hs_ekey.priv, tmp_hs.hs_epub);
- wg_mix_dh(&tmp_hs, tmp_hs.hs_skey.priv, tmp_hs.hs_epub);
+ resp->type = WG_MSG_RESPONSE;
+ resp->sender = session->s_local_id;
+ resp->receiver = session->s_remote_id;
- wg_mix_psk(&tmp_hs, tmp_hs.hs_shared);
+ hs = &session->s_handshake;
- if (!wg_handshake_decrypt(&tmp_hs, NULL, m->empty, WG_MAC_SIZE))
- ret_error(WG_DECRYPT);
+ wg_kdf(hs->h_ck, NULL, NULL, hs->h_ck, hs->h_local.pub.k, WG_KEY_SIZE);
+ wg_mix_hash(hs, hs->h_local.pub.k, WG_KEY_SIZE);
- wg_mix_hash(&tmp_hs, m->empty, WG_ENCRYPTED_SIZE(0));
+ memcpy(resp->ephemeral, hs->h_local.pub.k, WG_KEY_SIZE);
- wg_hash2(tmp_hs.hs_k, WG_MAC1, strlen(WG_MAC1), tmp_hs.hs_skey.pub, WG_KEY_SIZE);
- blake2s(mac1, (void *)m, tmp_hs.hs_k, sizeof(mac1), offsetof(struct wg_msg_response, mac1), sizeof(tmp_hs.hs_k));
+ wg_mix_dh(hs, hs->h_local.priv.k, hs->h_remote.k);
+ wg_mix_dh(hs, hs->h_local.priv.k, peer->p_remote.k);
- if (timingsafe_bcmp(mac1, m->mac1, WG_MAC_SIZE))
- ret_error(WG_MAC);
+ wg_mix_psk(hs, peer->p_shared.k);
- *hs = tmp_hs;
- hs->hs_state = WG_STATE_RECV_RESPONSE;
+ wg_handshake_encrypt(hs, resp->empty, NULL, 0);
+
+ wg_mix_hash(hs, resp->empty, WG_ENCRYPTED_SIZE(0));
+
+ wg_hash2(hs->h_k, WG_MAC1, strlen(WG_MAC1), peer->p_remote.k, WG_KEY_SIZE);
+ blake2s(resp->mac1, (void *)resp, hs->h_k, sizeof(resp->mac1),
+ offsetof(struct wg_msg_response, mac1), sizeof(hs->h_k));
+ memcpy(hs->h_mac, resp->mac1, sizeof(hs->h_mac));
+
+ if (wg_timespec_timedout(&peer->p_cookie.time, WG_COOKIE_VALID_TIME))
+ bzero(resp->mac2, WG_MAC_SIZE);
+ else
+ blake2s(resp->mac2, (void *)resp, peer->p_cookie.cookie,
+ sizeof(resp->mac2), offsetof(struct wg_msg_response, mac2),
+ sizeof(peer->p_cookie.cookie));
+
+ session->s_state = WG_STATE_WAIT_CONFIRM;
+ *s = session;
leave:
- rw_exit_write(&hs->hs_lock);
- explicit_bzero(&tmp_hs, sizeof(tmp_hs));
+ wg_peer_put(peer);
+ rw_exit_read(&peer->p_lock);
return ret;
}
enum wg_error
-wg_handshake_recv_cookie(struct wg_handshake *hs, struct wg_msg_cookie *m)
+wg_device_tx_cookie(struct wg_device *dev, struct wg_msg_cookie *cookie,
+ uint32_t id, struct wg_session **s)
{
- uint8_t key[WG_KEY_SIZE];
- uint8_t value[WG_COOKIE_SIZE];
- enum wg_error ret = WG_OK;
-
- rw_enter_write(&hs->hs_lock);
+ panic("should not call this yet");
+}
- if (hs->hs_state != WG_STATE_MADE_RESPONSE && hs->hs_state != WG_STATE_MADE_INITIATION)
- ret_error(WG_STATE);
+enum wg_error
+wg_device_tx_transport(struct wg_device *dev, struct wg_msg_transport *msg,
+ size_t len, uint32_t id, struct wg_session **s)
+{
- if (m->receiver != hs->hs_local_id)
- ret_error(WG_ID);
+ uint64_t counter = letoh64(msg->counter);
+ struct wg_session *session;
+ struct wg_peer *peer;
+ enum wg_error ret = WG_OK;
- wg_hash2(key, WG_COOKIE, strlen(WG_COOKIE), hs->hs_spub, WG_KEY_SIZE);
+ if ((peer = wg_device_ref_peerid(dev, id)) == NULL)
+ return WG_ID;
- if(!xchacha20poly1305_decrypt(value, m->cookie, sizeof(m->cookie),
- hs->hs_mac, WG_MAC_SIZE, m->nonce, key))
- ret_error(WG_DECRYPT);
+ rw_enter_read(&peer->p_lock);
- memcpy(hs->hs_cookie.cookie, value, sizeof(hs->hs_cookie.cookie));
- getnanotime(&hs->hs_cookie.time);
+ if ((session = SLIST_FIRST(&peer->p_sessions)) == NULL)
+ ret_error(WG_ID);
-leave:
- rw_exit_write(&hs->hs_lock);
- return ret;
-}
+ wg_session_ref(session);
-enum wg_error
-wg_session_encrypt(struct wg_session *s, struct wg_msg_transport *m, size_t len)
-{
- uint64_t counter;
- enum wg_error ret = WG_OK;
+ rw_enter_read(&session->s_lock);
- rw_enter_write(&s->s_lock);
+ if (session->s_state != WG_STATE_INITIATOR &&
+ session->s_state != WG_STATE_RESPONDER)
+ ret_error(WG_STATE);
- if (wg_session_reject_tx(s))
+ if (wg_timespec_timedout(&session->s_created, WG_REJECT_AFTER_TIME) ||
+ session->s_keyset.k_txcounter > WG_REJECT_AFTER_MESSAGES)
ret_error(WG_REJECT);
- if (s->s_state != WG_STATE_INITIATOR && s->s_state != WG_STATE_RESPONDER)
- ret_error(WG_STATE);
-
- counter = s->s_txcounter++;
- rw_enter(&s->s_lock, RW_DOWNGRADE);
+ counter = session->s_keyset.k_txcounter++;
- m->type = WG_MSG_TRANSPORT;
- m->receiver = s->s_remote_id;
- m->counter = htole64(counter);
+ msg->type = WG_MSG_TRANSPORT;
+ msg->receiver = session->s_remote_id;
+ msg->counter = htole64(counter);
- chacha20poly1305_encrypt(m->data, m->data, len, NULL, 0, m->counter, s->s_txkey);
+ chacha20poly1305_encrypt(msg->data, msg->data, len, NULL, 0, msg->counter,
+ session->s_keyset.k_txkey.k);
/* Packet OK, but we do want a rekey */
- if (wg_session_rekey_tx(s))
- ret = WG_REKEY;
+ if ((session->s_state == WG_STATE_INITIATOR &&
+ wg_timespec_timedout(&session->s_created, WG_REKEY_AFTER_TIME)) ||
+ session->s_keyset.k_txcounter > WG_REKEY_AFTER_MESSAGES)
+ ret_error(WG_REKEY);
+
+ *s = session;
leave:
- rw_exit(&s->s_lock);
+ rw_exit(&session->s_lock);
+ rw_exit_read(&peer->p_lock);
return ret;
}
-enum wg_error
-wg_session_decrypt(struct wg_session *s, struct wg_msg_transport *m, size_t len)
+/* WireGuard crypto helper functions */
+void
+wg_kdf(uint8_t first[WG_HASH_SIZE], uint8_t second[WG_HASH_SIZE],
+ uint8_t third[WG_HASH_SIZE], uint8_t key[WG_KEY_SIZE], uint8_t * input,
+ size_t input_len)
{
- enum wg_error ret = WG_OK;
- size_t data_len = len - offsetof(struct wg_msg_transport, data);
- uint64_t counter = letoh64(m->counter);
+ uint8_t buffer[WG_HASH_SIZE + 1];
+ uint8_t secret[WG_HASH_SIZE];
- rw_enter_read(&s->s_lock);
+ blake2s_hmac(secret, input, key, WG_HASH_SIZE, input_len, WG_KEY_SIZE);
- /* Check session is valid */
- if (wg_session_reject_rx(s))
- ret_error(WG_REJECT);
+ if (!first)
+ return;
- if (s->s_state != WG_STATE_INITIATOR && s->s_state != WG_STATE_RESPONDER)
- ret_error(WG_STATE);
+ buffer[0] = 1;
+ blake2s_hmac(buffer, buffer, secret, WG_HASH_SIZE, 1, WG_KEY_SIZE);
+ memcpy(first, buffer, WG_HASH_SIZE);
- if (m->receiver != s->s_local_id)
- ret_error(WG_ID);
+ if (!second)
+ return;
- /* Decrypt session */
- if (!chacha20poly1305_decrypt(m->data, m->data, data_len, NULL, 0, m->counter, s->s_rxkey))
- ret_error(WG_DECRYPT);
+ buffer[WG_HASH_SIZE] = 2;
+ blake2s_hmac(buffer, buffer, secret, WG_HASH_SIZE, WG_HASH_SIZE + 1, WG_KEY_SIZE);
+ memcpy(second, buffer, WG_HASH_SIZE);
- /* Check for replay */
- if (antireplay_update(&s->s_ar, counter))
- ret_error(WG_REPLAY);
+ if (!third)
+ return;
- s->s_rxcounter = counter;
+ buffer[WG_HASH_SIZE] = 3;
+ blake2s_hmac(buffer, buffer, secret, WG_HASH_SIZE, WG_HASH_SIZE + 1, WG_KEY_SIZE);
+ memcpy(third, buffer, WG_HASH_SIZE);
+}
- /* Packet OK, but we do want a rekey */
- if (wg_session_rekey_rx(s))
- ret = WG_REKEY;
-leave:
- rw_exit_read(&s->s_lock);
- return ret;
+void
+wg_hash2(uint8_t out[WG_HASH_SIZE], uint8_t * in1, size_t in1_len, uint8_t * in2, size_t in2_len)
+{
+ struct blake2s_state s;
+ blake2s_init(&s, WG_HASH_SIZE);
+ blake2s_update(&s, in1, in1_len);
+ blake2s_update(&s, in2, in2_len);
+ blake2s_final(&s, out, WG_HASH_SIZE);
}
void
-wg_keypair_from_bytes(struct wg_keypair *kp, const uint8_t key[WG_KEY_SIZE])
+wg_mix_hash(struct wg_handshake * hs, uint8_t * in, size_t in_len)
{
- const uint8_t basepoint[WG_KEY_SIZE] = {9};
+ wg_hash2(hs->h_hash, hs->h_hash, WG_HASH_SIZE, in, in_len);
+}
- /* memmove as key may overlap with kp->priv */
- memmove(kp->priv, key, WG_KEY_SIZE);
+void
+wg_mix_dh(struct wg_handshake * hs, uint8_t priv[WG_KEY_SIZE], uint8_t pub[WG_KEY_SIZE])
+{
+ uint8_t DH [WG_KEY_SIZE];
+ crypto_scalarmult_curve25519(DH, priv, pub);
+ wg_kdf(hs->h_ck, hs->h_k, NULL, hs->h_ck, DH, WG_KEY_SIZE);
+ explicit_bzero(DH, WG_KEY_SIZE);
+}
- /* We don't care if the input is not a valid key, we just set
- * the bits and be done with it. The curve25519 library *SHOULD*
- * do this, but since this may be returned to the user, we do
- * it here too. */
- kp->priv[0] &= 248;
- kp->priv[31] &= 127;
- kp->priv[31] |= 64;
+void
+wg_mix_psk(struct wg_handshake * hs, uint8_t psk[WG_KEY_SIZE])
+{
+ uint8_t t [WG_HASH_SIZE];
+ wg_kdf(hs->h_ck, t, hs->h_k, hs->h_ck, psk, WG_KEY_SIZE);
+ wg_mix_hash(hs, t, WG_HASH_SIZE);
+ explicit_bzero(t, WG_HASH_SIZE);
+}
+
+void
+wg_handshake_encrypt(struct wg_handshake *hs, uint8_t *dst, uint8_t *src, size_t srclen)
+{
+ return chacha20poly1305_encrypt(dst, src, srclen, hs->h_hash, WG_HASH_SIZE, 0, hs->h_k);
+}
- crypto_scalarmult_curve25519(kp->pub, kp->priv, basepoint);
+int
+wg_handshake_decrypt(struct wg_handshake *hs, uint8_t *dst, uint8_t *src, size_t srclen)
+{
+ return chacha20poly1305_decrypt(dst, src, srclen, hs->h_hash, WG_HASH_SIZE, 0, hs->h_k);
}
void
-wg_keypair_generate(struct wg_keypair *kp)
+wg_timestamp_get(uint8_t ts[WG_TIMESTAMP_SIZE])
{
- arc4random_buf(kp->priv, WG_KEY_SIZE);
- wg_keypair_from_bytes(kp, kp->priv);
+ struct timespec tv;
+ getnanotime(&tv);
+ *(uint64_t *) (ts) = htobe64(0x400000000000000aULL + tv.tv_sec);
+ *(uint32_t *) (ts + 8) = htobe32(tv.tv_nsec);
}
+int
+wg_timespec_timedout(struct timespec * start, time_t timeout)
+{
+ struct timespec now;
+
+ getnanotime(&now);
+
+ return now.tv_sec == start->tv_sec + timeout ?
+ now.tv_nsec > start->tv_nsec :
+ now.tv_sec > start->tv_sec + timeout;
+}
enum wg_pkt_type
wg_pkt_type(uint8_t *buf, size_t len)
@@ -782,22 +812,90 @@ wg_pkt_type(uint8_t *buf, size_t len)
else
msg = (struct wg_msg_unknown *)buf;
- if (msg->type == WG_MSG_INITIATION && len == sizeof(struct wg_msg_initiation))
+ if (msg->type == WG_MSG_INITIATION && len == wg_pkt_len[msg->type])
return WG_PKT_INITIATION;
- else if (msg->type == WG_MSG_RESPONSE && len == sizeof(struct wg_msg_response))
+ else if (msg->type == WG_MSG_RESPONSE && len == wg_pkt_len[msg->type])
return WG_PKT_RESPONSE;
- else if (msg->type == WG_MSG_COOKIE && len == sizeof(struct wg_msg_cookie))
+ else if (msg->type == WG_MSG_COOKIE && len == wg_pkt_len[msg->type])
return WG_PKT_COOKIE;
- else if (msg->type == WG_MSG_TRANSPORT &&
- len >= WG_ENCRYPTED_SIZE(sizeof(struct wg_msg_transport)))
+ else if (msg->type == WG_MSG_TRANSPORT && len >= wg_pkt_len[msg->type])
return WG_PKT_TRANSPORT;
else
return WG_PKT_UNKNOWN;
}
-/* Timer Functions */
+void
+wg_keypair_from_key(struct wg_keypair *kp, const struct wg_privkey *key)
+{
+ const uint8_t basepoint[WG_KEY_SIZE] = {9};
+
+ /* memmove as key may overlap with kp->priv */
+ memmove(kp->priv.k, key->k, WG_KEY_SIZE);
+
+ /* We don't care if the input is not a valid key, we just set
+ * the bits and be done with it. The curve25519 library *SHOULD*
+ * do this, but since this may be returned to the user, we do
+ * it here too. */
+ kp->priv.k[0] &= 248;
+ kp->priv.k[31] &= 127;
+ kp->priv.k[31] |= 64;
+
+ crypto_scalarmult_curve25519(kp->pub.k, kp->priv.k, basepoint);
+}
void
+wg_keypair_generate(struct wg_keypair *kp)
+{
+ arc4random_buf(kp->priv.k, sizeof(kp->priv.k));
+ wg_keypair_from_key(kp, &kp->priv);
+}
+
+enum wg_error
+wg_msg_initiation_valid_mac2(struct wg_msg_initiation *m, struct wg_cookie *c)
+{
+ uint8_t mac[WG_MAC_SIZE];
+ blake2s(mac, (uint8_t *)m, c->cookie, WG_MAC_SIZE,
+ offsetof(struct wg_msg_initiation, mac2), WG_COOKIE_SIZE);
+ return timingsafe_bcmp(mac, m->mac2, WG_MAC_SIZE) ? WG_MAC : WG_OK;
+}
+
+enum wg_error
+wg_msg_response_valid_mac2(struct wg_msg_response *m, struct wg_cookie *c)
+{
+ uint8_t mac[WG_MAC_SIZE];
+ blake2s(mac, (uint8_t *)m, c->cookie, WG_MAC_SIZE,
+ offsetof(struct wg_msg_response, mac2), WG_COOKIE_SIZE);
+ return timingsafe_bcmp(mac, m->mac2, WG_MAC_SIZE) ? WG_MAC : WG_OK;
+}
+
+void
+wg_cookie_from_token(struct wg_cookie *c, struct wg_cookie_maker *cm,
+ uint8_t *ip, uint8_t ip_len)
+{
+ if (wg_timespec_timedout(&cm->time, WG_COOKIE_VALID_TIME)) {
+ getnanotime(&cm->time);
+ arc4random_buf(cm->seed, sizeof(cm->seed));
+ }
+
+ blake2s(c->cookie, ip, cm->seed, WG_MAC_SIZE, ip_len, WG_COOKIE_SIZE);
+}
+
+void
+wg_device_make_cookie(struct wg_device *dev, struct wg_cookie *c,
+ uint32_t sender, uint8_t mac[WG_MAC_SIZE], struct wg_msg_cookie *m)
+{
+ uint8_t key[WG_KEY_SIZE]; // Same as WG_HASH_SIZE
+
+ m->type = WG_MSG_COOKIE;
+ m->receiver = sender;
+ arc4random_buf(m->nonce, sizeof(m->nonce));
+
+ wg_hash2(key, WG_COOKIE, strlen(WG_COOKIE), dev->d_keypair.pub.k, WG_KEY_SIZE);
+ xchacha20poly1305_encrypt(m->value, c->cookie, WG_MAC_SIZE, mac, WG_MAC_SIZE, m->nonce, key);
+}
+
+/* Timer Functions */
+void
wg_timer_setup(struct wg_timers *t, void *p, void (*keepalive)(void *),
void (*broken)(void *), void (*reinit)(void *), void (*cleanup)(void *))
{
@@ -886,15 +984,3 @@ wg_timer_reinit_unflag(struct wg_timers *t)
{
timeout_del(&t->t_reinit);
}
-
-void
-wg_timer_session_made(struct wg_timers *t)
-{
- getnanotime(&t->t_last_handshake);
-}
-
-struct timespec
-wg_timer_session_last(struct wg_timers *t)
-{
- return t->t_last_handshake;
-}
diff --git a/src/wireguard.h b/src/wireguard.h
index 7bedbef..ecd26e2 100644
--- a/src/wireguard.h
+++ b/src/wireguard.h
@@ -21,6 +21,7 @@
#include <sys/time.h>
#include <sys/timeout.h>
#include <sys/rwlock.h>
+#include <sys/fixedmap.h>
#include <sys/antireplay.h>
#define WG_KEY_SIZE 32
@@ -48,12 +49,49 @@
#define WG_COOKIE_VALID_TIME 120
#define WG_REKEY_AFTER_TIME_RECV (WG_REJECT_AFTER_TIME - WG_KEEPALIVE_TIMEOUT - WG_REKEY_TIMEOUT)
+struct wg_msg_unknown {
+ uint32_t type;
+} __packed;
+
+struct wg_msg_initiation {
+ uint32_t type;
+ uint32_t sender;
+ uint8_t ephemeral[WG_KEY_SIZE];
+ uint8_t static_pub[WG_ENCRYPTED_SIZE(WG_KEY_SIZE)];
+ uint8_t timestamp[WG_ENCRYPTED_SIZE(WG_TIMESTAMP_SIZE)];
+ uint8_t mac1 [WG_MAC_SIZE];
+ uint8_t mac2 [WG_MAC_SIZE];
+} __packed;
+
+struct wg_msg_response {
+ uint32_t type;
+ uint32_t sender;
+ uint32_t receiver;
+ uint8_t ephemeral[WG_KEY_SIZE];
+ uint8_t empty [WG_ENCRYPTED_SIZE(0)];
+ uint8_t mac1 [WG_MAC_SIZE];
+ uint8_t mac2 [WG_MAC_SIZE];
+} __packed;
+
+struct wg_msg_cookie {
+ uint32_t type;
+ uint32_t receiver;
+ uint8_t nonce [WG_XNONCE_SIZE];
+ uint8_t value[WG_ENCRYPTED_SIZE(WG_COOKIE_SIZE)];
+} __packed;
+
+struct wg_msg_transport {
+ uint32_t type;
+ uint32_t receiver;
+ uint64_t counter;
+ uint8_t data [];
+} __packed;
+
enum wg_state {
- WG_STATE_CLEAN = 0,
+ WG_STATE_NEW = 0,
WG_STATE_MADE_INITIATION,
- WG_STATE_MADE_RESPONSE,
WG_STATE_RECV_INITIATION,
- WG_STATE_RECV_RESPONSE,
+ WG_STATE_WAIT_CONFIRM,
WG_STATE_INITIATOR,
WG_STATE_RESPONDER,
};
@@ -66,9 +104,25 @@ enum wg_pkt_type {
WG_PKT_TRANSPORT,
};
+static int wg_pkt_len[] = {
+ sizeof(struct wg_msg_unknown),
+ sizeof(struct wg_msg_initiation),
+ sizeof(struct wg_msg_response),
+ sizeof(struct wg_msg_cookie),
+ WG_ENCRYPTED_SIZE(sizeof(struct wg_msg_transport)),
+};
+
+static char *wg_pkt_str[] = {
+ "unknown",
+ "initiation",
+ "response",
+ "cookie",
+ "transport",
+};
+
struct wg_keypair {
- uint8_t pub[WG_KEY_SIZE];
- uint8_t priv[WG_KEY_SIZE];
+ struct wg_pubkey { uint8_t k[WG_KEY_SIZE]; } pub;
+ struct wg_privkey { uint8_t k[WG_KEY_SIZE]; } priv;
};
struct wg_cookie {
@@ -81,71 +135,6 @@ struct wg_cookie_maker {
struct timespec time;
};
-struct wg_handshake {
- struct rwlock hs_lock;
-
- uint8_t hs_attempts;
- uint8_t hs_spub[WG_KEY_SIZE];
- uint8_t hs_shared[WG_KEY_SIZE];
- struct wg_cookie hs_cookie;
- struct wg_keypair hs_skey;
- struct timespec hs_last_initiation;
- void *hs_arg;
-
- struct {
- enum wg_state ss_state;
- uint32_t ss_local_id;
- uint32_t ss_remote_id;
- uint8_t ss_mac[WG_MAC_SIZE];
- uint8_t ss_hash[WG_HASH_SIZE];
- uint8_t ss_ck[WG_HASH_SIZE];
- uint8_t ss_k[WG_HASH_SIZE];
- uint8_t ss_timestamp[WG_TIMESTAMP_SIZE];
-
- uint8_t ss_epub[WG_KEY_SIZE];
- struct wg_keypair ss_ekey;
- } hs_ss;
-};
-
-#define hs_state hs_ss.ss_state
-#define hs_local_id hs_ss.ss_local_id
-#define hs_remote_id hs_ss.ss_remote_id
-#define hs_mac hs_ss.ss_mac
-#define hs_hash hs_ss.ss_hash
-#define hs_ck hs_ss.ss_ck
-#define hs_k hs_ss.ss_k
-#define hs_timestamp hs_ss.ss_timestamp
-#define hs_epub hs_ss.ss_epub
-#define hs_ekey hs_ss.ss_ekey
-
-struct wg_session {
- struct rwlock s_lock;
- void *s_arg;
- struct {
- enum wg_state ss_state;
- uint64_t ss_txcounter;
- uint64_t ss_rxcounter;
- uint32_t ss_local_id;
- uint32_t ss_remote_id;
- struct timespec ss_created;
-
- uint8_t ss_txkey[WG_KEY_SIZE];
- uint8_t ss_rxkey[WG_KEY_SIZE];
-
- struct antireplay ss_ar;
- } s_ss;
-};
-
-#define s_state s_ss.ss_state
-#define s_txcounter s_ss.ss_txcounter
-#define s_rxcounter s_ss.ss_rxcounter
-#define s_local_id s_ss.ss_local_id
-#define s_remote_id s_ss.ss_remote_id
-#define s_created s_ss.ss_created
-#define s_txkey s_ss.ss_txkey
-#define s_rxkey s_ss.ss_rxkey
-#define s_ar s_ss.ss_ar
-
struct wg_timers {
struct timeout t_ka;
struct timeout t_pka;
@@ -156,52 +145,77 @@ struct wg_timers {
uint16_t t_pka_interval;
};
-struct wg_msg_unknown {
- uint32_t type;
-} __packed;
-
-struct wg_msg_initiation {
- uint32_t type;
- uint32_t sender;
- uint8_t ephemeral[WG_KEY_SIZE];
- uint8_t static_pub[WG_ENCRYPTED_SIZE(WG_KEY_SIZE)];
- uint8_t timestamp[WG_ENCRYPTED_SIZE(WG_TIMESTAMP_SIZE)];
- uint8_t mac1 [WG_MAC_SIZE];
- uint8_t mac2 [WG_MAC_SIZE];
-} __packed;
-
-struct wg_msg_response {
- uint32_t type;
- uint32_t sender;
- uint32_t receiver;
- uint8_t ephemeral[WG_KEY_SIZE];
- uint8_t empty [WG_ENCRYPTED_SIZE(0)];
- uint8_t mac1 [WG_MAC_SIZE];
- uint8_t mac2 [WG_MAC_SIZE];
-} __packed;
+struct wg_session {
+ uint32_t s_local_id; /* Static */
+ uint32_t s_remote_id; /* Static */
+ struct wg_peer *s_peer; /* Static */
+ struct timespec s_created;
+ SLIST_ENTRY(wg_session) s_entry; /* Protected by p_lock */
+
+ /* All protected by s_lock */
+ struct rwlock s_lock;
+ enum wg_state s_state;
+
+ struct wg_handshake {
+ uint8_t h_mac[WG_MAC_SIZE];
+ uint8_t h_hash[WG_HASH_SIZE];
+ uint8_t h_ck[WG_HASH_SIZE];
+ uint8_t h_k[WG_HASH_SIZE];
+ struct wg_pubkey h_remote;
+ struct wg_keypair h_local;
+ } s_handshake;
+
+ struct wg_keyset {
+ uint64_t k_txcounter;
+ uint64_t k_rxcounter;
+ struct wg_privkey k_txkey;
+ struct wg_privkey k_rxkey;
+ struct antireplay k_ar;
+ } s_keyset;
+};
-struct wg_msg_cookie {
- uint32_t type;
- uint32_t receiver;
- uint8_t nonce [WG_XNONCE_SIZE];
- uint8_t cookie[WG_ENCRYPTED_SIZE(WG_COOKIE_SIZE)];
-} __packed;
+struct wg_peer {
+ uint32_t p_id;
+ void *p_arg;
+ struct wg_device *p_device;
+ struct wg_pubkey p_remote;
+ SLIST_ENTRY(wg_peer) p_entry;
+
+ /* All protected by p_lock */
+ struct rwlock p_lock;
+ struct wg_cookie p_cookie;
+ struct wg_timers p_timers;
+ struct timespec p_last_handshake;
+ struct timespec p_last_initiation;
+ uint64_t p_tx_bytes;
+ uint64_t p_rx_bytes;
+ struct wg_privkey p_shared;
+ uint8_t p_attempts;
+ SLIST_HEAD(,wg_session) p_sessions;
+ struct wg_timestamp { uint8_t t[WG_TIMESTAMP_SIZE]; } p_timestamp;
+};
-struct wg_msg_transport {
- uint32_t type;
- uint32_t receiver;
- uint64_t counter;
- uint8_t data [];
-} __packed;
+struct wg_device {
+ void (*d_outq)(struct wg_peer *, enum wg_pkt_type);
+ struct fixed_map d_peers;
+ struct fixed_map d_sessions;
+ struct rwlock d_lock;
+ struct wg_cookie_maker d_cookie_maker;
+ struct wg_keypair d_keypair;
+};
enum wg_error {
WG_OK = 0,
WG_TIMESTAMP,
WG_HS_ATTEMPTS,
WG_HS_RATE,
+ WG_UNKNOWN_PEER,
+ WG_ALLOWED_IP_FAIL,
+ WG_RATELIMIT,
WG_DECRYPT,
WG_REPLAY,
WG_REJECT,
+ WG_INVALID_PKT,
WG_REKEY,
WG_STATE,
WG_MAC,
@@ -213,235 +227,93 @@ static char *wg_error_str[] = {
"invalid timestamp",
"exceeded initiation attemts",
"exceeded initiation transmit rate",
+ "unknown peer",
+ "non-allowed ip",
+ "ratelimit",
"unable to decrypt",
"replay detected",
"packet rejected",
+ "packet invalid",
"want rekey",
"invalid state",
"invalid mac",
"invalid ID",
};
-/* wg_handshake_init:
- * Will zero out all memory and setup lock on handshake.
- *
- * wg_handshake_reset:
- * Will zero out all the individual handshake data such as ephemeral keys, and
- * the internal Noise state. It will leave the static keys and counters as is.
- * The state follows:
- * WG_STATE_* -> WG_STATE_CLEAN
- */
-void wg_handshake_init(struct wg_handshake *);
-void wg_handshake_reset(struct wg_handshake *);
-
-/* wg_session_init:
- * Will zero out all memory and setup lock on session.
- *
- * wg_session_reset:
- * Will zero out all the individual session data except the lock, ready for
- * passing the session to wg_session_from_handshake. The state follows:
- * WG_STATE_* -> WG_STATE_CLEAN
- */
-void wg_session_init(struct wg_session *);
-void wg_session_reset(struct wg_session *);
-
-/* wg_handshake_make_initiation:
- * Given a clean wg_handshake and id, fill the wg_msg_initiation with a
- * prepared packet.
- * Error (else WG_OK):
- * WG_STATE: wg_handshake is not WG_STATE_CLEAN.
- * WG_HS_ATTEMPTS: wg_handshake has exceeded WG_REKEY_ATTEMT_COUNT.
- * The state follows:
- * WG_STATE_CLEAN -> WG_STATE_MADE_INITIATION
- *
- * wg_handshake_make_response:
- * Given a wg_handshake that has received an initiation and id, fill the
- * wg_msg_response with a prepared packet.
- * Error (else WG_OK);
- * WG_STATE: wg_handshake is not WG_STATE_RECV_INITIATION
- * The state follows:
- * WG_STATE_RECV_INITIATION -> WG_STATE_MADE_RESPONSE
- *
- * wg_handshake_make_cookie:
- * wg_keypair: local keypair to tie cookie to.
- * wg_cookie: cookie to encrypt
- * uint32_t: sender id that will be placed in cookie.
- * uint8_t[WG_MAG_SIZE]: mac of packet in response to
- * wg_msg_cookie: packet to fill
- */
-enum wg_error wg_handshake_make_initiation(struct wg_handshake *, uint32_t, struct wg_msg_initiation *);
-enum wg_error wg_handshake_make_response(struct wg_handshake *, uint32_t, struct wg_msg_response *);
-enum wg_error wg_handshake_make_cookie(struct wg_keypair *, struct wg_cookie *, uint32_t, uint8_t [WG_MAC_SIZE], struct wg_msg_cookie *);
-
-/*
- * wg_handshake_recv_initiation:
- * Given a temporary wg_handshake that will be merged with an actual
- * wg_handshake, decrypt the wg_msg_initiation with the wg_keypair and store
- * the parameters in the temporary wg_handshake. the hs_spub (handshake static
- * public key) will be set.
- * Error (else WG_OK);
- * WG_DECRYPT: could not decrypt static_pub or timestamp in packet.
- * WG_MAC: invalid mac on packet.
- * The state follows:
- * * -> WG_STATE_RECV_INITIATION
- *
- * wg_handshake_recv_response:
- * Given a wg_handshake that has made an initiation, process the incoming
- * response packet. A temporary handshake is made so that the original is not
- * tainted while processing the incoming packet.
- * Error (else WG_OK);
- * WG_STATE: wg_handshake is not WG_STATE_MADE_INITIATION.
- * WG_ID: the receiver ID (local) doesn't match the stored ID in handshake.
- * WG_DECRYPT: could not decrypt the response packet.
- * WG_MAC: the packet mac was invalid.
- * The state follows:
- * WG_STATE_MADE_INITIATION -> WG_STATE_RECV_RESPONSE
- *
- * wg_handshake_recv_cookie:
- * Given a wg_handshake that has made an initiation or response, ingest a
- * cookie packet and if successful, save the cookie in the handshake. The
- * cookie will automatically be used when calling the next make_initiation or
- * make_response.
- * Error (else WG_OK);
- * WG_STATE: wg_handshake not in WG_STATE_MADE_INITIATION
- * WG_STATE: wg_handshake not in WG_STATE_MADE_RESPONSE
- * WG_ID: the receiver ID (local) doesn't match the stored ID in handshake.
- * WG_DECRYPT: could not decrypt the cookie packet.
- */
-enum wg_error wg_handshake_recv_initiation(struct wg_handshake *, struct wg_keypair *, struct wg_msg_initiation *);
-enum wg_error wg_handshake_recv_response(struct wg_handshake *, struct wg_msg_response *);
-enum wg_error wg_handshake_recv_cookie(struct wg_handshake *, struct wg_msg_cookie *);
-
-/* wg_handshake_merge:
- * Copy the src wg_handshake to the dst wg_handshake. The only purpose of this
- * is to merge a temporary wg_handshake generated from an incoming
- * wg_msg_initiation. It will clear the src handshake before returning.
- * Error (else WG_OK);
- * WG_STATE: wg_handshake (dst) is not WG_STATE_CLEAN.
- * WG_STATE: wg_handshake (src) is not WG_STATE_RECV_INITIATION.
- * WG_STATE: wg_handshake keys do not match.
- * WG_TIMESTAMP: wg_handshake (dst) has a newer timestamp.
- * The state follows:
- * WG_STATE_CLEAN -> WG_STATE_RECV_INITIATION
- */
-enum wg_error wg_handshake_merge(struct wg_handshake *, struct wg_handshake *);
-
-/* wg_handshake_initiation_valid_mac2:
- * Validate mac2 (the IP binding MAC) in a wg_msg_initiation. The wg_cookie
- * must be supplied. It will return WG_MAC if invalid mac or WG_OK if ok.
- *
- * wg_handshake_response_valid_mac2:
- * Validate mac2 (the IP binding MAC) in a wg_msg_initiation. The wg_cookie
- * must be supplied. It will return WG_MAC if invalid mac or WG_OK if ok.
- *
- * wg_handshake_initiation_ready:
- * Check if minimum time has elapsed before sending new initiation. It will
- * return WG_HS_RATE if the caller should wait, or WG_OK if the minimum time
- * has elapsed.
- *
- * wg_handshake_reset_attempts:
- * Reset the handshake attempt count to 0.
- */
-enum wg_error wg_handshake_initiation_valid_mac2(struct wg_cookie *, struct wg_msg_initiation *);
-enum wg_error wg_handshake_response_valid_mac2(struct wg_cookie *, struct wg_msg_response *);
-enum wg_error wg_handshake_initiation_ready(struct wg_handshake *);
-void wg_handshake_reset_attempts(struct wg_handshake *);
-
-/* Get handshake parameters.
- * wg_handshake_id:
- * Return the local handshake ID.
- *
- * wg_handshake_shortkey:
- * Return the first 4 bytes of the handshake remote static public key.
- *
- * wg_session_id:
- * Return the local session ID.
- */
-uint32_t wg_handshake_id(struct wg_handshake *);
-uint32_t wg_handshake_shortkey(struct wg_handshake *);
-uint32_t wg_session_id(struct wg_session *);
-
-/* Set handshake parameters */
-void wg_handshake_setkeypair(struct wg_handshake *, struct wg_keypair *);
-void wg_handshake_setkey(struct wg_handshake *, uint8_t[WG_KEY_SIZE]);
-void wg_handshake_setshared(struct wg_handshake *, uint8_t[WG_KEY_SIZE]);
-void wg_handshake_getkey(struct wg_handshake *, uint8_t[WG_KEY_SIZE]);
-void wg_handshake_getshared(struct wg_handshake *, uint8_t[WG_KEY_SIZE]);
-int wg_handshake_keycmp(struct wg_handshake *, struct wg_handshake *);
-
-/* wg_session_from_handshake:
- * Populate wg_session based on wg_handshake. wg_handshake must be in either
- * WG_STATE_RECV_RESPONSE or WG_STATE_MADE_RESPONSE. The handshake will be
- * zeroed out before returning. The wg_session must be passed to
- * wg_session_confirm before it can be used to encrypt/decrypt messages.
- * Error (else WG_OK);
- * WG_STATE: wg_handshake is not in state WG_STATE_RECV_RESPONSE.
- * WG_STATE: wg_handshake is not in state WG_STATE_MADE_RESPONSE.
- * WG_STATE: wg_session is not in state WG_STATE_CLEAN.
- * The state follows (depending on wg_handshake):
- * WG_STATE_CLEAN -> WG_STATE_RECV_RESPONSE
- * WG_STATE_CLEAN -> WG_STATE_MADE_RESPONSE
- *
- * wg_session_confirm:
- * Confirm a session is ready to be used.
- * Error (else WG_OK);
- * WG_STATE: wg_session is not in state WG_STATE_RECV_RESPONSE.
- * WG_STATE: wg_session is not in state WG_STATE_MADE_RESPONSE.
- * The state follows (depending on wg_handshake):
- * WG_STATE_RECV_RESPONSE -> WG_STATE_INITIATOR
- * WG_STATE_MADE_RESPONSE -> WG_STATE_RESPONDER
- *
- * wg_session_ready:
- * Return weather wg_session_confirm has been called on wg_session. Return
- * WG_STATE if not, and WG_OK if it has.
- */
-enum wg_error wg_session_from_handshake(struct wg_session *, struct wg_handshake *);
-enum wg_error wg_session_confirm(struct wg_session *);
-enum wg_error wg_session_ready(struct wg_session *);
-
-/* wg_session_encrypt:
- * Encrypt a wg_msg_transport of size size_t (data len, not including header).
- * Error (else WG_OK);
- * WG_REJECT: wg_session is not allowed to encrypt, too old.
- * WG_STATE: wg_session is not in state WG_STATE_INITIATOR.
- * WG_STATE: wg_session is not in state WG_STATE_RESPONDER.
- * WG_REKEY: wg_session successfully encrypted, but wants a new session.
- *
- * wg_session_decrypt:
- * Decrypt a wg_msg_transport of size size_t (data len, not including header).
- * Error (else WG_OK);
- * WG_REJECT: wg_session is not allowed to decrypt, too old.
- * WG_STATE: wg_session is not in state WG_STATE_INITIATOR.
- * WG_STATE: wg_session is not in state WG_STATE_RESPONDER.
- * WG_ID: wg_session ID doesn't match incoming packet ID.
- * WG_DECRYPT: wg_session could not decrypt message (bad MAC).
- * WG_REPLAY: wg_session detected replay of packet.
- * WG_REKEY: wg_session successfully decrypted, but wants a new session.
- */
-enum wg_error wg_session_encrypt(struct wg_session *, struct wg_msg_transport *, size_t);
-enum wg_error wg_session_decrypt(struct wg_session *, struct wg_msg_transport *, size_t);
+/* WireGuard functions */
+
+void wg_device_init(struct wg_device *,
+ void (*)(struct wg_peer *, enum wg_pkt_type));
+void wg_device_setkey(struct wg_device *, struct wg_privkey *);
+struct wg_peer *wg_device_new_peer(struct wg_device *, struct wg_pubkey *, void *);
+struct wg_peer *wg_device_ref_peerkey(struct wg_device *, struct wg_pubkey *);
+struct wg_peer *wg_device_ref_peerid(struct wg_device *, uint32_t);
+
+void wg_peer_ref(struct wg_peer *);
+void wg_peer_put(struct wg_peer *);
+void wg_peer_drop(struct wg_peer *);
+void wg_peer_attach_session(struct wg_peer *, struct wg_session *);
+void wg_peer_setshared(struct wg_peer *, struct wg_privkey *);
+void wg_peer_getshared(struct wg_peer *, struct wg_privkey *);
+void wg_peer_clean(struct wg_peer *);
+struct timespec wg_peer_last_handshake(struct wg_peer *);
+enum wg_error wg_peer_has_transport_session(struct wg_peer *);
+
+struct wg_session *wg_device_new_session(struct wg_device *);
+struct wg_session *wg_device_ref_session(struct wg_device *, uint32_t);
+void wg_session_ref(struct wg_session *);
+void wg_session_put(struct wg_session *);
+void wg_session_drop(struct wg_session *);
+
+enum wg_error wg_device_rx_initiation(struct wg_device *,
+ struct wg_msg_initiation *, struct wg_session **);
+enum wg_error wg_device_rx_response(struct wg_device *,
+ struct wg_msg_response *, struct wg_session **);
+enum wg_error wg_device_rx_cookie(struct wg_device *,
+ struct wg_msg_cookie *, struct wg_session **);
+enum wg_error wg_device_rx_transport(struct wg_device *,
+ struct wg_msg_transport *, size_t, struct wg_session **);
+
+enum wg_error wg_device_tx_initiation(struct wg_device *,
+ struct wg_msg_initiation *, uint32_t,
+ struct wg_session **);
+enum wg_error wg_device_tx_response(struct wg_device *,
+ struct wg_msg_response *, uint32_t, struct wg_session **);
+enum wg_error wg_device_tx_cookie(struct wg_device *,
+ struct wg_msg_cookie *, uint32_t, struct wg_session **);
+enum wg_error wg_device_tx_transport(struct wg_device *,
+ struct wg_msg_transport *, size_t, uint32_t,
+ struct wg_session **);
+
+
+enum wg_error wg_msg_initiation_valid_mac2(struct wg_msg_initiation *,
+ struct wg_cookie *);
+enum wg_error wg_msg_response_valid_mac2(struct wg_msg_response *,
+ struct wg_cookie *);
+void wg_cookie_from_token(struct wg_cookie *,
+ struct wg_cookie_maker *, uint8_t *, uint8_t);
+void wg_device_make_cookie(struct wg_device *, struct wg_cookie *,
+ uint32_t, uint8_t[WG_MAC_SIZE], struct wg_msg_cookie *);
+
+void wg_keypair_from_key(struct wg_keypair *,
+ const struct wg_privkey *);
+void wg_keypair_generate(struct wg_keypair *);
+enum wg_pkt_type wg_pkt_type(uint8_t *, size_t);
-/* Minor helper functions */
-void wg_keypair_from_bytes(struct wg_keypair *, const uint8_t [WG_KEY_SIZE]);
-void wg_keypair_generate(struct wg_keypair *);
-enum wg_pkt_type wg_pkt_type(uint8_t *, size_t);
-void wg_cookie_from_token(struct wg_cookie *, struct wg_cookie_maker *, uint8_t *, uint8_t);
/* Timer functions */
-void wg_timer_setup(struct wg_timers *, void *, void (*)(void *),
- void (*)(void *), void (*)(void *), void (*)(void *));
-void wg_timer_stop(struct wg_timers *);
-void wg_timer_persistent_keepalive_tick(struct wg_timers *);
-void wg_timer_persistent_keepalive_set(struct wg_timers *, uint16_t);
-uint16_t wg_timer_persistent_keepalive_get(struct wg_timers *);
-void wg_timer_cleanup_tick(struct wg_timers *);
-void wg_timer_keepalive_flag(struct wg_timers *);
-void wg_timer_keepalive_unflag(struct wg_timers *);
-void wg_timer_broken_flag(struct wg_timers *);
-void wg_timer_broken_unflag(struct wg_timers *);
-void wg_timer_reinit_flag(struct wg_timers *);
-void wg_timer_reinit_unflag(struct wg_timers *);
-void wg_timer_session_made(struct wg_timers *);
-struct timespec wg_timer_session_last(struct wg_timers *);
+void wg_timer_setup(struct wg_timers *, void *, void (*)(void *),
+ void (*)(void *), void (*)(void *), void (*)(void *));
+void wg_timer_stop(struct wg_timers *);
+void wg_timer_persistent_keepalive_tick(struct wg_timers *);
+void wg_timer_persistent_keepalive_set(struct wg_timers *, uint16_t);
+uint16_t wg_timer_persistent_keepalive_get(struct wg_timers *);
+void wg_timer_cleanup_tick(struct wg_timers *);
+void wg_timer_keepalive_flag(struct wg_timers *);
+void wg_timer_keepalive_unflag(struct wg_timers *);
+void wg_timer_broken_flag(struct wg_timers *);
+void wg_timer_broken_unflag(struct wg_timers *);
+void wg_timer_reinit_flag(struct wg_timers *);
+void wg_timer_reinit_unflag(struct wg_timers *);
#endif /* _LIBWG_H_ */