aboutsummaryrefslogtreecommitdiffstats
diff options
context:
space:
mode:
authorMatt Dunwoodie <ncon@mail.noconroy.net>2019-10-02 00:29:34 +0100
committerMatt Dunwoodie <ncon@mail.noconroy.net>2019-10-02 00:29:34 +0100
commit4c3ed084b726cbb7cf61e150434c836306ee35e5 (patch)
treebe75f7748ac1e0045f8e2694af4a45577a2c4cb9
parentUpdate DPRINTF calls to report the interface name. (diff)
downloadwireguard-openbsd-4c3ed084b726cbb7cf61e150434c836306ee35e5.tar.xz
wireguard-openbsd-4c3ed084b726cbb7cf61e150434c836306ee35e5.zip
I don't have a good commit message for this
Changes: * Move fixedmap lock to fixedmap struct, rather than relying on d_lock. * Adjust outq to supply the ID to lookup later. This requires the caller to know the standard convention, peer id on initiation, session id otherwise. * Added fixed number session management, that is one handshake session, one current session and one old session. * Fix some ref counting
-rw-r--r--src/fixedmap.h1
-rw-r--r--src/if_wg.c48
-rw-r--r--src/kern_wg.c49
-rw-r--r--src/wireguard.c358
-rw-r--r--src/wireguard.h20
5 files changed, 280 insertions, 196 deletions
diff --git a/src/fixedmap.h b/src/fixedmap.h
index dad4d59..d56a9b7 100644
--- a/src/fixedmap.h
+++ b/src/fixedmap.h
@@ -29,6 +29,7 @@
struct fixed_map {
size_t size;
+ struct rwlock lock;
struct map_item {
enum {
FM_ITEM_EMPTY = 0,
diff --git a/src/if_wg.c b/src/if_wg.c
index a69d2ee..0d22adf 100644
--- a/src/if_wg.c
+++ b/src/if_wg.c
@@ -130,8 +130,8 @@ struct wg_tag *wg_mbuf_get_tag(struct mbuf *);
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_route_queue(struct wg_route *, enum wg_pkt_type, uint32_t);
+void wg_peer_queue(struct wg_peer *, enum wg_pkt_type, uint32_t);
void wg_encrypt_hs(struct mbuf *);
void wg_encrypt(struct mbuf *);
@@ -381,7 +381,7 @@ wg_mbuf_get_tag(struct mbuf *m)
void
wg_route_send_initiation(struct wg_route *r)
{
- wg_route_queue(r, WG_PKT_INITIATION);
+ wg_route_queue(r, WG_PKT_INITIATION, r->r_peer->p_id);
}
void
@@ -389,13 +389,19 @@ wg_route_broken(struct wg_route *r)
{
DPRINTF(r->r_sc, "broken session %x\n", r->r_peer->p_id);
wg_peer_clean(r->r_peer);
- wg_route_queue(r, WG_PKT_INITIATION);
+ wg_route_queue(r, WG_PKT_INITIATION, r->r_peer->p_id);
}
void
wg_route_send_keepalive(struct wg_route *r)
{
- wg_route_queue(r, WG_PKT_TRANSPORT);
+ struct wg_session *session;
+ if ((session = wg_peer_ks_session(r->r_peer)) == NULL) {
+ wg_route_send_initiation(r);
+ } else {
+ wg_route_queue(r, WG_PKT_TRANSPORT, session->s_local_id);
+ wg_session_put(session);
+ }
}
void
@@ -406,7 +412,7 @@ wg_route_clean(struct wg_route *r)
}
void
-wg_route_queue(struct wg_route *route, enum wg_pkt_type type)
+wg_route_queue(struct wg_route *route, enum wg_pkt_type type, uint32_t dst)
{
struct mbuf *m;
struct wg_tag *tag;
@@ -416,7 +422,7 @@ wg_route_queue(struct wg_route *route, enum wg_pkt_type type)
tag = wg_mbuf_get_tag(m);
tag->t_sc = sc;
- tag->t_dst = route->r_peer->p_id;
+ tag->t_dst = dst;
tag->t_type = type;
tag->t_state = WG_PKT_STATE_NEW;
@@ -424,7 +430,6 @@ wg_route_queue(struct wg_route *route, enum wg_pkt_type type)
m_calchdrlen(m);
if (type == WG_PKT_TRANSPORT) {
- /* TODO calculate length better */
m->m_len = 0;
m_calchdrlen(m);
mpq_enqueue(&sc->sc_tx_queue, m);
@@ -436,9 +441,9 @@ wg_route_queue(struct wg_route *route, enum wg_pkt_type type)
}
void
-wg_peer_queue(struct wg_peer *peer, enum wg_pkt_type type)
+wg_peer_queue(struct wg_peer *peer, enum wg_pkt_type type, uint32_t dst)
{
- wg_route_queue(peer->p_arg, type);
+ wg_route_queue(peer->p_arg, type, dst);
}
void
@@ -470,7 +475,7 @@ wg_encrypt_hs(struct mbuf *m)
panic("invalid packet type: %d\n", tag->t_type);
}
- DPRINTF(tag->t_sc, "%s tx for %x, id %x\n", wg_pkt_str[tag->t_type],
+ DPRINTF(tag->t_sc, "tx %s for %x, id %x\n", wg_pkt_str[tag->t_type],
session->s_peer->p_id, session->s_local_id);
m->m_pkthdr.ph_cookie = m;
@@ -479,7 +484,7 @@ wg_encrypt_hs(struct mbuf *m)
wg_session_put(session);
return;
drop:
- DPRINTF(tag->t_sc, "failed %s tx: %s\n", wg_pkt_str[tag->t_type],
+ DPRINTF(tag->t_sc, "%s tx failed: %s\n", wg_pkt_str[tag->t_type],
wg_error_str[err]);
tag->t_state = WG_PKT_STATE_DEAD;
}
@@ -583,7 +588,7 @@ wg_decrypt_hs(struct mbuf *m)
panic("not a handshake packet: %d", tag->t_type);
}
- DPRINTF(tag->t_sc, "%s rx for %x, id %x\n", wg_pkt_str[tag->t_type],
+ DPRINTF(tag->t_sc, "rx %s for %x, id %x\n", wg_pkt_str[tag->t_type],
session->s_peer->p_id, session->s_local_id);
tag->t_state = WG_PKT_STATE_DONE;
@@ -592,7 +597,7 @@ wg_decrypt_hs(struct mbuf *m)
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]);
+ DPRINTF(tag->t_sc, "%s rx failed: %s\n", wg_pkt_str[tag->t_type], wg_error_str[err]);
tag->t_state = WG_PKT_STATE_DEAD;
}
@@ -681,6 +686,7 @@ wg_output(struct ifnet *ifp, struct mbuf *m, struct sockaddr *sa,
int error;
struct wg_tag *tag;
struct wg_route *route;
+ struct wg_session *session;
struct wg_softc *sc = ifp->if_softc;
if (!AF_VAL(sa->sa_family, 1, 1)) {
@@ -719,18 +725,19 @@ 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 ((session = wg_peer_ks_session(route->r_peer)) == NULL) {
if (mq_push(&route->r_outgoing, m) != 0)
counters_inc(sc->sc_if.if_counters, ifc_oqdrops);
wg_route_send_initiation(route);
return 0;
}
+ tag->t_sc = sc;
+ tag->t_dst = session->s_local_id;
+ tag->t_type = WG_PKT_TRANSPORT;
+ tag->t_state = WG_PKT_STATE_NEW;
+ wg_session_put(session);
+
if ((error = if_enqueue(ifp, m)) != 0) {
counters_inc(sc->sc_if.if_counters, ifc_oqdrops);
if_start(ifp);
@@ -1162,6 +1169,7 @@ wg_ioctl_get_serv(struct wg_softc *sc, struct wg_get_serv *wgs)
struct map_item *item;
struct wg_peer *peer;
+ /* For the time being, no lock as we hold kernel lock in ioctl */
FM_FOREACH_FILLED(item, &sc->sc_dev.d_peers) {
if (num < wgs->gs_num_peers) {
peer = item->value;
diff --git a/src/kern_wg.c b/src/kern_wg.c
index 4cad94a..63eb6a3 100644
--- a/src/kern_wg.c
+++ b/src/kern_wg.c
@@ -168,15 +168,21 @@ void
fm_init(struct fixed_map *fm)
{
bzero(fm, sizeof(*fm));
+ rw_init(&fm->lock, "fixedmap");
fm_resize(fm, 128);
}
void
fm_resize(struct fixed_map *fm, size_t size)
{
- size_t i;
- size_t osize = fm->size;
- struct map_item *omap = fm->map;
+ size_t i;
+ size_t osize;
+ struct map_item *omap;
+
+ /* No lock as fm_resize is only called in fm_init (no lock) or
+ * fm_insert (already locked) */
+ osize = fm->size;
+ omap = fm->map;
for (fm->size = 1; fm->size < size; fm->size <<= 1);
KASSERT(fm->size >= osize);
@@ -193,6 +199,7 @@ void
fm_destroy(struct fixed_map *fm)
{
struct map_item *item;
+ /* No lock, as this is the last reference */
FM_FOREACH_FILLED(item, fm)
refcnt_finalize(&item->cnt, "fm_wait");
free(fm->map, M_DEVBUF, 0);
@@ -203,6 +210,7 @@ fm_insert(struct fixed_map *fm, void *v)
{
struct map_item *item = NULL, *iter;
+ rw_enter_write(&fm->lock);
retry:
FM_FOREACH_EMPTY(iter, fm) {
item = iter;
@@ -220,37 +228,56 @@ retry:
item->value = v;
refcnt_init(&item->cnt);
+ rw_exit_write(&fm->lock);
+
return item->key;
}
void
fm_put(struct fixed_map *fm, uint32_t k)
{
- struct map_item *item = fm->map + (k & (fm->size - 1));
+ struct map_item *item;
+
+ rw_enter_read(&fm->lock);
+ item = fm->map + (k & (fm->size - 1));
if (item->key != k)
panic("unexpected key, should exist");
refcnt_rele_wake(&item->cnt);
+ rw_exit_read(&fm->lock);
}
void *
fm_ref(struct fixed_map *fm, uint32_t k)
{
- struct map_item *item = fm->map + (k & (fm->size - 1));
- if (item->key != k)
- return NULL;
- refcnt_take(&item->cnt);
- return item->value;
+ void *v = NULL;
+ struct map_item *item;
+
+ rw_enter_read(&fm->lock);
+ item = fm->map + (k & (fm->size - 1));
+ if (item->key == k) {
+ v = item->value;
+ refcnt_take(&item->cnt);
+ }
+ rw_exit_read(&fm->lock);
+ return v;
}
void
fm_remove(struct fixed_map *fm, uint32_t k)
{
- struct map_item *item = fm->map + (k & (fm->size - 1));
+ struct map_item *item;
+
+ rw_enter_read(&fm->lock);
+ item = fm->map + (k & (fm->size - 1));
+ refcnt_finalize(&item->cnt, "fm_wait");
+ rw_exit_read(&fm->lock);
+
+ rw_enter_write(&fm->lock);
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 758d707..557d6de 100644
--- a/src/wireguard.c
+++ b/src/wireguard.c
@@ -74,10 +74,13 @@ 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 *);
+void wg_session_drop(struct wg_session *);
+void wg_session_ref(struct wg_session *);
+
/* Some crappy API */
void
wg_device_init(struct wg_device *dev,
- void (*outq)(struct wg_peer *, enum wg_pkt_type))
+ void (*outq)(struct wg_peer *, enum wg_pkt_type, uint32_t))
{
bzero(dev, sizeof(*dev));
dev->d_outq = outq;
@@ -105,174 +108,232 @@ wg_device_new_peer(struct wg_device *dev, struct wg_pubkey *key, void *arg)
peer->p_device = dev;
peer->p_remote = *key;
rw_init(&peer->p_lock, "wg_peer");
- SLIST_INIT(&peer->p_sessions);
- rw_enter_write(&dev->d_lock);
+ rw_enter_write(&peer->p_lock);
peer->p_id = fm_insert(&dev->d_peers, peer);
- rw_exit_write(&dev->d_lock);
+ rw_exit_write(&peer->p_lock);
+
+ /* All other elements of wg_peer are nulled by M_ZERO */
return peer;
}
+struct wg_session *
+wg_device_new_session(struct wg_device *dev)
+{
+ struct wg_session *session;
+ session = malloc(sizeof(*session), M_DEVBUF, M_WAITOK | M_ZERO);
+
+ getnanotime(&session->s_created);
+
+ rw_enter_write(&peer->p_lock);
+ session->s_local_id = fm_insert(&dev->d_sessions, session);
+ rw_exit_write(&peer->p_lock);
+
+ return session;
+}
+
+struct wg_peer *
+wg_device_ref_peerid(struct wg_device *dev, uint32_t id)
+{
+ return fm_ref(&dev->d_peers, id);
+}
+
struct wg_peer *
wg_device_ref_peerkey(struct wg_device *dev, struct wg_pubkey *key)
{
+ /* For the time being, we just iterate through peers to find the
+ * matching peer */
uint32_t id = 0;
struct wg_peer *peer;
struct map_item *item;
- rw_enter_read(&dev->d_lock);
+ rw_enter_read(&dev->d_peers.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);
-}
+ rw_exit_read(&dev->d_peers.d_lock);
-struct wg_peer *
-wg_device_ref_peerid(struct wg_device *dev, uint32_t id)
-{
- 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;
+ /* Take reference and return peer */
+ return wg_device_ref_peerid(dev, id);
}
void
wg_peer_ref(struct wg_peer *peer)
{
- wg_device_ref_peerid(peer->p_device, peer->p_id);
+ /* Don't return anything, just take a reference and expect it exists */
+ KASSERT(wg_device_ref_peerid(peer->p_device, peer->p_id) != NULL);
}
void
wg_peer_put(struct wg_peer *peer)
{
- 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);
+ fm_put(&peer->p_device->d_peers, peer->p_id);
}
void
wg_peer_drop(struct wg_peer *peer)
{
- 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);
+ fm_remove(&peer->p_device->d_peers, peer->p_id);
+ free(peer, M_DEVBUF, sizeof(*peer));
+}
+
+struct wg_session *
+wg_device_ref_session(struct wg_device *dev, uint32_t id)
+{
+ return fm_ref(&dev->d_sessions, id);
}
void
-wg_peer_attach_session(struct wg_peer *peer, struct wg_session *session)
+wg_session_ref(struct wg_session *session)
{
- rw_assert_wrlock(&peer->p_lock);
- KASSERT(session->s_peer == NULL);
- session->s_peer = peer;
- SLIST_INSERT_HEAD(&peer->p_sessions, session, s_entry);
+ KASSERT(wg_device_ref_session(session->s_peer->p_device,
+ session->s_local_id) != NULL);
}
void
-wg_peer_setshared(struct wg_peer *peer, struct wg_privkey *key)
+wg_session_put(struct wg_session *session)
{
- rw_enter_write(&peer->p_lock);
- peer->p_shared = *key;
- rw_exit_write(&peer->p_lock);
+ fm_put(&session->s_peer->p_device->d_sessions, session->s_local_id);
}
void
-wg_peer_getshared(struct wg_peer *peer, struct wg_privkey *key)
+wg_session_drop(struct wg_session *session)
{
- rw_enter_read(&peer->p_lock);
- *key = peer->p_shared;
- rw_exit_read(&peer->p_lock);
+ fm_remove(&session->s_peer->p_device->d_sessions, session->s_local_id);
+ free(session, M_DEVBUF, sizeof(*session));
}
void
-wg_peer_clean(struct wg_peer *peer)
+wg_peer_attach_session(struct wg_peer *peer, struct wg_session *session)
{
- struct wg_session *session;
- rw_enter_write(&peer->p_lock);
- SLIST_FOREACH(session, &peer->p_sessions, s_entry)
- wg_session_drop(session);
+ /* Assert the session is not attached to another peer.
+ * Assert the session is for the same device that the peer is for.
+ * Assert the session is newly created, by checking s_state. */
+ KASSERT(session->s_peer == NULL);
+ //KASSERT(session->s_device == peer->p_device);
+ KASSERT(session->s_state == WG_STATE_RECV_INITIATION ||
+ session->s_state == WG_STATE_MADE_INITIATION);
+
rw_enter_write(&peer->p_lock);
+ if (peer->p_hs_session) {
+ wg_session_drop(peer->p_hs_session);
+ peer->p_hs_session = NULL;
+ }
+
+ peer->p_hs_session = session;
+ session->s_peer = peer;
+ rw_exit_write(&peer->p_lock);
}
-struct timespec
-wg_peer_last_handshake(struct wg_peer *peer)
+void
+wg_session_promote(struct wg_session *session)
{
- struct timespec ret;
- rw_enter_read(&peer->p_lock);
- ret = peer->p_last_handshake;
- rw_exit_read(&peer->p_lock);
- return ret;
+ /* TODO check locking state before */
+ struct wg_peer *peer = session->s_peer;
+ struct wg_keyset *ks = &session->s_keyset;
+ struct wg_handshake *hs = &session->s_handshake;
+
+ /* Setup session: derive keys, initialise the antireplay structure */
+ rw_enter_write(&peer->p_lock);
+ rw_enter_write(&session->s_lock);
+ if (session->s_state == WG_STATE_INITIATOR ||
+ session->s_state == WG_STATE_RESPONDER) {
+ goto leave;
+ } else if (session->s_state == WG_STATE_RECV_RESPONSE) {
+ session->s_state = WG_STATE_INITIATOR;
+ wg_kdf(ks->k_txkey.k, ks->k_rxkey.k, NULL, hs->h_ck, NULL, 0);
+ } else if (session->s_state == WG_STATE_MADE_RESPONSE) {
+ session->s_state = WG_STATE_RESPONDER;
+ wg_kdf(ks->k_rxkey.k, ks->k_txkey.k, NULL, hs->h_ck, NULL, 0);
+ } else
+ panic("invalid session state");
+
+ antireplay_init(&ks->k_ar);
+
+ /* Move session to p_ks_session by shuffling older sessions. */
+ if (peer->p_ks_session_old != NULL)
+ wg_session_drop(peer->p_ks_session_old);
+ if (peer->p_ks_session != NULL)
+ peer->p_ks_session_old = peer->p_ks_session;
+ peer->p_ks_session = peer->p_hs_session;
+ peer->p_hs_session = NULL;
+leave:
+ rw_exit_write(&session->s_lock);
+ rw_exit_write(&peer->p_lock);
}
-enum wg_error
-wg_peer_has_transport_session(struct wg_peer *peer)
+struct wg_session *
+wg_peer_hs_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;
- }
- }
+ if ((session = peer->p_hs_session) != NULL)
+ wg_session_ref(session);
rw_exit_read(&peer->p_lock);
- return err;
+ return session;
}
struct wg_session *
-wg_device_new_session(struct wg_device *dev)
+wg_peer_ks_session(struct wg_peer *peer)
{
struct wg_session *session;
- session = malloc(sizeof(*session), M_DEVBUF, M_WAITOK | M_ZERO);
-
- getnanotime(&session->s_created);
-
- rw_enter_write(&dev->d_lock);
- session->s_local_id = fm_insert(&dev->d_sessions, session);
- rw_exit_write(&dev->d_lock);
+ rw_enter_read(&peer->p_lock);
+ session = peer->p_ks_session;
+ if (session != NULL &&
+ (wg_timespec_timedout(&session->s_created, WG_REJECT_AFTER_TIME) ||
+ session->s_keyset.k_txcounter > WG_REJECT_AFTER_MESSAGES))
+ session = NULL;
+ if (session != NULL)
+ wg_session_ref(session);
+ rw_exit_read(&peer->p_lock);
return session;
}
-struct wg_session *
-wg_device_ref_session(struct wg_device *dev, uint32_t id)
+void
+wg_peer_setshared(struct wg_peer *peer, struct wg_privkey *key)
{
- 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;
+ rw_enter_write(&peer->p_lock);
+ peer->p_shared = *key;
+ rw_exit_write(&peer->p_lock);
}
void
-wg_session_ref(struct wg_session *session)
+wg_peer_getshared(struct wg_peer *peer, struct wg_privkey *key)
{
- wg_device_ref_session(session->s_peer->p_device, session->s_local_id);
+ rw_enter_read(&peer->p_lock);
+ *key = peer->p_shared;
+ rw_exit_read(&peer->p_lock);
}
-void
-wg_session_put(struct wg_session *session)
+struct timespec
+wg_peer_last_handshake(struct wg_peer *peer)
{
- 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);
+ struct timespec ret = { 0, 0 };
+ rw_enter_read(&peer->p_lock);
+ if (peer->p_ks_session)
+ ret = peer->p_ks_session->s_created;
+ rw_exit_read(&peer->p_lock);
+ return ret;
}
void
-wg_session_drop(struct wg_session *session)
+wg_peer_clean(struct wg_peer *peer)
{
- 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);
+ rw_enter_write(&peer->p_lock);
+
+ if (peer->p_hs_session != NULL)
+ wg_session_drop(peer->p_hs_session);
+ if (peer->p_ks_session != NULL)
+ wg_session_drop(peer->p_ks_session);
+ if (peer->p_ks_session_old != NULL)
+ wg_session_drop(peer->p_ks_session_old);
+
+ peer->p_hs_session = NULL;
+ peer->p_ks_session = NULL;
+ peer->p_ks_session_old = NULL;
+
+ rw_enter_write(&peer->p_lock);
}
/* Crypto */
@@ -331,8 +392,7 @@ wg_device_rx_initiation(struct wg_device *dev, struct wg_msg_initiation *init,
rw_exit_read(&dev->d_lock);
- /* TODO Fix locking */
- /* TODO rotate out old peers */
+ /* Create new session and add to peer */
session = wg_device_new_session(dev);
session->s_handshake = hs;
session->s_remote_id = init->sender;
@@ -340,15 +400,13 @@ wg_device_rx_initiation(struct wg_device *dev, struct wg_msg_initiation *init,
explicit_bzero(&hs, sizeof(hs));
*s = session;
- rw_enter_write(&peer->p_lock);
wg_peer_attach_session(peer, session);
- rw_exit_write(&peer->p_lock);
-
- dev->d_outq(peer, WG_PKT_RESPONSE);
+ dev->d_outq(peer, WG_PKT_RESPONSE, session->s_local_id);
+ /* Take reference to return to caller */
+ wg_session_ref(session);
wg_peer_put(peer);
-
return WG_OK;
leave:
rw_exit_read(&dev->d_lock);
@@ -402,13 +460,16 @@ wg_device_rx_response(struct wg_device *dev, struct wg_msg_response *resp,
session->s_handshake = hs;
session->s_remote_id = resp->sender;
- session->s_state = WG_STATE_INITIATOR;
+ session->s_state = WG_STATE_RECV_RESPONSE;
- 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);
+ rw_exit_write(&session->s_lock);
+ rw_exit_read(&dev->d_lock);
+ explicit_bzero(&hs, sizeof(hs));
+ wg_session_promote(session);
*s = session;
+
+ return ret;
leave:
rw_exit_write(&session->s_lock);
rw_exit_read(&dev->d_lock);
@@ -433,8 +494,8 @@ wg_device_rx_cookie(struct wg_device *dev, struct wg_msg_cookie *cookie,
rw_enter_write(&session->s_peer->p_lock);
rw_enter_read(&session->s_lock);
- if (session->s_state != WG_STATE_WAIT_CONFIRM &&
- session->s_state != WG_STATE_MADE_INITIATION)
+ if (session->s_state != WG_STATE_MADE_INITIATION &&
+ session->s_state != WG_STATE_MADE_RESPONSE)
ret_error(WG_STATE);
wg_hash2(key, WG_COOKIE, strlen(WG_COOKIE), session->s_peer->p_remote.k,
@@ -467,14 +528,9 @@ wg_device_rx_transport(struct wg_device *dev, struct wg_msg_transport *msg,
if ((session = wg_device_ref_session(dev, msg->receiver)) == NULL)
return WG_ID;
+ wg_session_promote(session);
+
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;
- }
if (session->s_state != WG_STATE_INITIATOR &&
session->s_state != WG_STATE_RESPONDER)
@@ -496,7 +552,7 @@ wg_device_rx_transport(struct wg_device *dev, struct wg_msg_transport *msg,
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);
+ dev->d_outq(session->s_peer, WG_PKT_INITIATION, session->s_peer->p_id);
*s = session;
leave:
@@ -518,7 +574,7 @@ wg_device_tx_initiation(struct wg_device *dev, struct wg_msg_initiation *init,
if ((peer = wg_device_ref_peerid(dev, id)) == NULL)
return WG_ID;
- rw_enter_write(&peer->p_lock);
+ rw_enter_read(&peer->p_lock);
if (!wg_timespec_timedout(&peer->p_last_initiation, WG_REKEY_TIMEOUT))
ret_error(WG_HS_RATE);
@@ -567,14 +623,24 @@ wg_device_tx_initiation(struct wg_device *dev, struct wg_msg_initiation *init,
sizeof(init->mac2), offsetof(struct wg_msg_initiation, mac2),
sizeof(peer->p_cookie.cookie));
- peer->p_attempts++;
+ rw_exit_read(&peer->p_lock);
+
+ /* TODO lock? */
getnanotime(&peer->p_last_initiation);
+ peer->p_attempts++;
+
+ /* Attach session to peer */
session->s_state = WG_STATE_MADE_INITIATION;
wg_peer_attach_session(peer, session);
*s = session;
-leave:
+
+ /* Take reference to return to caller */
+ wg_session_ref(session);
wg_peer_put(peer);
+ return WG_OK;
+leave:
rw_exit_write(&peer->p_lock);
+ wg_peer_put(peer);
return ret;
}
@@ -582,27 +648,20 @@ enum wg_error
wg_device_tx_response(struct wg_device *dev, struct wg_msg_response *resp,
uint32_t id, struct wg_session **s)
{
- struct wg_peer *peer;
+ enum wg_error ret = WG_OK;
+
struct wg_handshake *hs;
struct wg_session *session;
- enum wg_error ret = WG_OK;
-
- if ((peer = wg_device_ref_peerid(dev, id)) == NULL)
+ if ((session = wg_device_ref_session(dev, id)) == NULL)
return WG_ID;
- rw_enter_read(&peer->p_lock);
-
- if ((session = SLIST_FIRST(&peer->p_sessions)) == NULL)
- ret_error(WG_ID);
-
- /* TODO lock session */
+ rw_enter_read(&session->s_peer->p_lock);
+ rw_enter_write(&session->s_lock);
if (session->s_state != WG_STATE_RECV_INITIATION)
ret_error(WG_STATE);
- wg_session_ref(session);
-
resp->type = WG_MSG_RESPONSE;
resp->sender = session->s_local_id;
resp->receiver = session->s_remote_id;
@@ -615,31 +674,35 @@ wg_device_tx_response(struct wg_device *dev, struct wg_msg_response *resp,
memcpy(resp->ephemeral, hs->h_local.pub.k, WG_KEY_SIZE);
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);
+ wg_mix_dh(hs, hs->h_local.priv.k, session->s_peer->p_remote.k);
- wg_mix_psk(hs, peer->p_shared.k);
+ wg_mix_psk(hs, session->s_peer->p_shared.k);
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);
+ wg_hash2(hs->h_k, WG_MAC1, strlen(WG_MAC1), session->s_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))
+ if (wg_timespec_timedout(&session->s_peer->p_cookie.time, WG_COOKIE_VALID_TIME))
bzero(resp->mac2, WG_MAC_SIZE);
else
- blake2s(resp->mac2, (void *)resp, peer->p_cookie.cookie,
+ blake2s(resp->mac2, (void *)resp, session->s_peer->p_cookie.cookie,
sizeof(resp->mac2), offsetof(struct wg_msg_response, mac2),
- sizeof(peer->p_cookie.cookie));
+ sizeof(session->s_peer->p_cookie.cookie));
- session->s_state = WG_STATE_WAIT_CONFIRM;
+ session->s_state = WG_STATE_MADE_RESPONSE;
*s = session;
+ rw_exit_write(&session->s_lock);
+ rw_exit_read(&session->s_peer->p_lock);
+ return ret;
leave:
- wg_peer_put(peer);
- rw_exit_read(&peer->p_lock);
+ rw_exit_write(&session->s_lock);
+ rw_exit_read(&session->s_peer->p_lock);
+ wg_session_put(session);
return ret;
}
@@ -654,22 +717,12 @@ 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)
{
-
- uint64_t counter = letoh64(msg->counter);
- struct wg_session *session;
- struct wg_peer *peer;
enum wg_error ret = WG_OK;
+ struct wg_session *session;
- if ((peer = wg_device_ref_peerid(dev, id)) == NULL)
+ if ((session = wg_device_ref_session(dev, id)) == NULL)
return WG_ID;
- rw_enter_read(&peer->p_lock);
-
- if ((session = SLIST_FIRST(&peer->p_sessions)) == NULL)
- ret_error(WG_ID);
-
- wg_session_ref(session);
-
rw_enter_read(&session->s_lock);
if (session->s_state != WG_STATE_INITIATOR &&
@@ -680,11 +733,9 @@ wg_device_tx_transport(struct wg_device *dev, struct wg_msg_transport *msg,
session->s_keyset.k_txcounter > WG_REJECT_AFTER_MESSAGES)
ret_error(WG_REJECT);
- counter = session->s_keyset.k_txcounter++;
-
msg->type = WG_MSG_TRANSPORT;
msg->receiver = session->s_remote_id;
- msg->counter = htole64(counter);
+ msg->counter = htole64(session->s_keyset.k_txcounter++);
chacha20poly1305_encrypt(msg->data, msg->data, len, NULL, 0, msg->counter,
session->s_keyset.k_txkey.k);
@@ -693,12 +744,11 @@ wg_device_tx_transport(struct wg_device *dev, struct wg_msg_transport *msg,
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);
+ dev->d_outq(session->s_peer, WG_PKT_INITIATION, session->s_peer->p_id);
*s = session;
leave:
- rw_exit(&session->s_lock);
- rw_exit_read(&peer->p_lock);
+ rw_exit_read(&session->s_lock);
return ret;
}
diff --git a/src/wireguard.h b/src/wireguard.h
index ecd26e2..332cb28 100644
--- a/src/wireguard.h
+++ b/src/wireguard.h
@@ -91,7 +91,8 @@ enum wg_state {
WG_STATE_NEW = 0,
WG_STATE_MADE_INITIATION,
WG_STATE_RECV_INITIATION,
- WG_STATE_WAIT_CONFIRM,
+ WG_STATE_MADE_RESPONSE,
+ WG_STATE_RECV_RESPONSE,
WG_STATE_INITIATOR,
WG_STATE_RESPONDER,
};
@@ -141,7 +142,6 @@ struct wg_timers {
struct timeout t_broken;
struct timeout t_reinit;
struct timeout t_cleanup;
- struct timespec t_last_handshake;
uint16_t t_pka_interval;
};
@@ -150,7 +150,6 @@ struct wg_session {
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;
@@ -185,18 +184,19 @@ struct wg_peer {
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_session *p_hs_session;
+ struct wg_session *p_ks_session;
+ struct wg_session *p_ks_session_old;
struct wg_timestamp { uint8_t t[WG_TIMESTAMP_SIZE]; } p_timestamp;
};
struct wg_device {
- void (*d_outq)(struct wg_peer *, enum wg_pkt_type);
+ void (*d_outq)(struct wg_peer *, enum wg_pkt_type, uint32_t);
struct fixed_map d_peers;
struct fixed_map d_sessions;
struct rwlock d_lock;
@@ -216,7 +216,6 @@ enum wg_error {
WG_REPLAY,
WG_REJECT,
WG_INVALID_PKT,
- WG_REKEY,
WG_STATE,
WG_MAC,
WG_ID,
@@ -243,7 +242,7 @@ static char *wg_error_str[] = {
/* WireGuard functions */
void wg_device_init(struct wg_device *,
- void (*)(struct wg_peer *, enum wg_pkt_type));
+ void (*)(struct wg_peer *, enum wg_pkt_type, uint32_t));
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 *);
@@ -257,13 +256,12 @@ 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_peer_hs_session(struct wg_peer *);
+struct wg_session *wg_peer_ks_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 **);