aboutsummaryrefslogtreecommitdiffstats
diff options
context:
space:
mode:
authorMatt Dunwoodie <ncon@mail.noconroy.net>2019-09-22 14:55:37 +0200
committerMatt Dunwoodie <ncon@mail.noconroy.net>2019-09-22 16:39:58 +0200
commit1e54ee4f0fa137ed578e91e7a9a77a5a209065f1 (patch)
tree2c9656d199e2735f5c9fc49e180a2420b8c99dd7
parentDo outgoing handshake messages in a separate queue (diff)
downloadwireguard-openbsd-1e54ee4f0fa137ed578e91e7a9a77a5a209065f1.tar.xz
wireguard-openbsd-1e54ee4f0fa137ed578e91e7a9a77a5a209065f1.zip
Add second queue for incoming handshake packets
-rw-r--r--src/if_wg.c282
1 files changed, 152 insertions, 130 deletions
diff --git a/src/if_wg.c b/src/if_wg.c
index f18237e..51112e3 100644
--- a/src/if_wg.c
+++ b/src/if_wg.c
@@ -51,6 +51,7 @@
#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;
@@ -103,10 +104,12 @@ struct wg_softc {
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 mbuf_queue sc_rx_queue;
+ struct mbuf_queue sc_rx_slow_queue;
struct fixed_map sc_id_map;
RB_HEAD(peer_tree, wg_peer) sc_peer_tree;
@@ -152,17 +155,15 @@ void wg_peer_send_keepalive(struct wg_peer *);
void wg_encrypt_hs(struct mbuf *);
void wg_encrypt(struct mbuf *);
-void wg_softc_recv_initiation(struct wg_softc *, struct mbuf *);
-void wg_softc_recv_response(struct wg_softc *, struct mbuf *);
-void wg_softc_recv_cookie(struct wg_softc *, struct mbuf *);
-int wg_softc_recv_transport(struct wg_softc *, struct mbuf *);
+void wg_decrypt_hs(struct mbuf *);
+void wg_decrypt(struct mbuf *);
void wg_rx_task_fn(void *);
void wg_start(struct ifnet *);
int wg_output(struct ifnet *, struct mbuf *, struct sockaddr *,
struct rtentry *);
struct mbuf *wg_input(void *, struct mbuf *, struct sockaddr *, int);
-void wg_input_deliver(struct wg_softc *, struct mbuf *);
+void wg_input_deliver(struct mbuf *);
void wg_output_deliver(struct mbuf *);
void wg_output_deliver_buf(struct wg_peer *, uint8_t *, size_t);
@@ -516,8 +517,6 @@ wg_peer_send_initiation(struct wg_peer *p)
m->m_len = sizeof(struct wg_msg_initiation);
m_calchdrlen(m);
- DPRINTF_HS(p->p_sc, "transmit initiation", &p->p_hs);
-
mpq_enqueue(&p->p_sc->sc_tx_slow_queue, m);
task_add(p->p_sc->sc_taskq, &p->p_sc->sc_tx_slow_task);
}
@@ -534,8 +533,6 @@ wg_peer_send_response(struct wg_peer *p)
m->m_len = sizeof(struct wg_msg_response);
m_calchdrlen(m);
- DPRINTF_HS(p->p_sc, "transmit response", &p->p_hs);
-
mpq_enqueue(&p->p_sc->sc_tx_slow_queue, m);
task_add(p->p_sc->sc_taskq, &p->p_sc->sc_tx_slow_task);
}
@@ -552,8 +549,6 @@ wg_peer_send_keepalive(struct wg_peer *p)
m->m_len = 0;
m_calchdrlen(m);
- DPRINTF_HS(p->p_sc, "transmit keepalive", &p->p_hs);
-
mpq_enqueue(&p->p_sc->sc_tx_queue, m);
task_add(p->p_sc->sc_taskq, &p->p_sc->sc_tx_task);
}
@@ -680,106 +675,123 @@ free:
}
void
-wg_softc_recv_initiation(struct wg_softc *sc, struct mbuf *m)
+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_msg_initiation *init = mtod(m, struct wg_msg_initiation *);
+ struct wg_softc *sc = tag->t_sc;
- if (wg_mbuf_ratelimit(sc, m)) {
- DPRINTF(sc, "dropped initiation");
- return;
- }
+ struct wg_msg_initiation *init;
+ struct wg_msg_response *resp;
+ struct wg_msg_cookie *cookie;
- if ((e = wg_handshake_recv_initiation(&hs, &sc->sc_kp, init))
- != WG_OK) {
- DPRINTF_ERROR(sc, "receive initiation", e);
- return;
- }
+ m_defrag(m, M_WAIT);
- if ((p = wg_softc_peer_lookup(sc, hs.hs_spub)) == NULL) {
- DPRINTF(sc, "receive initiation: unknown peer\n");
- return;
- }
+ if (tag->t_state != WG_PKT_STATE_NEW)
+ panic("not a new state packet: %d\n", tag->t_state);
- fm_remove(&sc->sc_id_map, wg_handshake_id(&p->p_hs));
- wg_handshake_reset(&p->p_hs);
+ switch (tag->t_type) {
+ case WG_PKT_INITIATION:
+ if (wg_mbuf_ratelimit(sc, m)) {
+ DPRINTF(sc, "dropped initiation");
+ return;
+ }
- if ((e = wg_handshake_merge(&p->p_hs, &hs)) != WG_OK) {
- DPRINTF_HS_ERROR(sc, "merge handshake", &p->p_hs, e);
- 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;
+ }
- tag->t_peer = p;
- DPRINTF_HS(sc, "receive initiation", &p->p_hs);
-}
+ if ((p = wg_softc_peer_lookup(sc, hs.hs_spub)) == NULL) {
+ DPRINTF(sc, "receive initiation: unknown peer\n");
+ return;
+ }
-void
-wg_softc_recv_response(struct wg_softc *sc, struct mbuf *m)
-{
- enum wg_error e;
- struct wg_peer *p;
- struct wg_tag *tag = wg_mbuf_get_tag(m);
- struct wg_msg_response *resp = mtod(m, struct wg_msg_response *);
+ fm_remove(&sc->sc_id_map, wg_handshake_id(&p->p_hs));
+ wg_handshake_reset(&p->p_hs);
- if (wg_mbuf_ratelimit(sc, m)) {
- DPRINTF(sc, "dropped response");
- return;
- }
+ if ((e = wg_handshake_merge(&p->p_hs, &hs)) != WG_OK) {
+ DPRINTF_HS_ERROR(sc, "merge handshake", &p->p_hs, e);
+ return;
+ }
- if ((p = fm_lookup(&sc->sc_id_map, resp->receiver)) == NULL) {
- DPRINTF(sc, "unknown response id: %x\n", resp->receiver);
- return;
- }
+ tag->t_peer = p;
+ DPRINTF_HS(sc, "receive initiation", &p->p_hs);
- if ((e = wg_handshake_recv_response(&p->p_hs, resp)) != 0) {
- DPRINTF_HS_ERROR(sc, "receive response", &p->p_hs, e);
- return;
- }
+ wg_peer_send_response(tag->t_peer);
+ break;
+ case WG_PKT_RESPONSE:
+ if (wg_mbuf_ratelimit(sc, m)) {
+ DPRINTF(sc, "dropped response");
+ return;
+ }
- fm_remove(&sc->sc_id_map, wg_session_id(&p->p_sess));
- wg_session_reset(&p->p_sess);
+ 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 ((e = wg_session_from_handshake(&p->p_sess, &p->p_hs)) != WG_OK) {
- DPRINTF_HS_ERROR(sc, "make session", &p->p_hs, e);
- return;
- };
+ if ((e = wg_handshake_recv_response(&p->p_hs, resp)) != 0) {
+ DPRINTF_HS_ERROR(sc, "receive response", &p->p_hs, e);
+ return;
+ }
- tag->t_peer = p;
- DPRINTF_HS(sc, "receive response", &p->p_hs);
-}
+ fm_remove(&sc->sc_id_map, wg_session_id(&p->p_sess));
+ wg_session_reset(&p->p_sess);
-void
-wg_softc_recv_cookie(struct wg_softc *sc, struct mbuf *m)
-{
- enum wg_error e;
- struct wg_peer *p;
- struct wg_tag *tag = wg_mbuf_get_tag(m);
- struct wg_msg_cookie *cookie = mtod(m, struct wg_msg_cookie *);
+ 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;
+ };
- if ((p = fm_lookup(&sc->sc_id_map, cookie->receiver)) == NULL) {
- DPRINTF(sc, "unknown cookie id: %x\n", cookie->receiver);
- return;
- }
+ tag->t_peer = p;
+ DPRINTF_HS(sc, "receive response", &p->p_hs);
- if ((e = wg_handshake_recv_cookie(&p->p_hs, cookie)) != 0) {
- DPRINTF_HS_ERROR(sc, "receive cookie", &p->p_hs, e);
- return;
+ wg_peer_new_session(tag->t_peer);
+ 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);
+ break;
+ default:
+ panic("not a handshake packet: %d\n", tag->t_type);
}
- tag->t_peer = p;
- DPRINTF_HS(sc, "receive cookie", &p->p_hs);
+ if (tag->t_peer != NULL)
+ tag->t_peer->p_ip = tag->t_ip;
}
-int
-wg_softc_recv_transport(struct wg_softc *sc, struct mbuf *m)
+void
+wg_decrypt(struct mbuf *m)
{
struct wg_peer *p;
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;
@@ -802,10 +814,10 @@ wg_softc_recv_transport(struct wg_softc *sc, struct mbuf *m)
m_calchdrlen(m);
if (m->m_pkthdr.len == WG_ENCRYPTED_SIZE(0)) {
- m_freem(m);
wg_timer_persistent_keepalive_tick(&p->p_timers);
wg_timer_broken_unflag(&p->p_timers);
- return 1;
+ tag->t_state = WG_PKT_STATE_DEAD;
+ return;
}
@@ -840,43 +852,35 @@ wg_softc_recv_transport(struct wg_softc *sc, struct mbuf *m)
pf_pkt_addr_changed(m);
tag->t_peer = p;
- return 0;
+ tag->t_peer->p_ip = tag->t_ip;
+ tag->t_state = WG_PKT_STATE_DONE;
+ return;
error:
- m_freem(m);
+ tag->t_state = WG_PKT_STATE_DEAD;
counters_inc(sc->sc_if.if_counters, ifc_ierrors);
- return 1;
+}
+
+void
+wg_rx_slow_task_fn(void *_sc)
+{
+ struct mbuf *m;
+ struct wg_softc *sc = _sc;
+
+ while ((m = mq_dequeue(&sc->sc_rx_slow_queue)) != NULL) {
+ wg_decrypt_hs(m);
+ m_freem(m);
+ }
}
void
wg_rx_task_fn(void *_sc)
{
struct mbuf *m;
- struct wg_tag *tag;
- enum wg_pkt_type type;
struct wg_softc *sc = _sc;
while ((m = mq_dequeue(&sc->sc_rx_queue)) != NULL) {
- m_defrag(m, M_WAIT);
- type = wg_pkt_type(mtod(m, uint8_t *), m->m_pkthdr.len);
- tag = wg_mbuf_get_tag(m);
-
- if (type == WG_PKT_INITIATION) {
- wg_softc_recv_initiation(sc, m);
- if (tag->t_peer != NULL)
- wg_peer_send_response(tag->t_peer);
- } else if (type == WG_PKT_RESPONSE) {
- wg_softc_recv_response(sc, m);
- if (tag->t_peer != NULL)
- wg_peer_new_session(tag->t_peer);
- } else if (type == WG_PKT_COOKIE) {
- wg_softc_recv_cookie(sc, m);
- } else if (type == WG_PKT_TRANSPORT) {
- if (wg_softc_recv_transport(sc, m) == 0)
- wg_input_deliver(sc, m);
- }
-
- if (type != WG_PKT_TRANSPORT)
- m_freem(m);
+ wg_decrypt(m);
+ wg_input_deliver(m);
}
}
@@ -960,10 +964,25 @@ 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_type = wg_pkt_type(mtod(m, uint8_t *), m->m_pkthdr.len);
+ tag->t_sc = sc;
+
+ switch (tag->t_type) {
+ case WG_PKT_INITIATION:
+ case WG_PKT_RESPONSE:
+ case WG_PKT_COOKIE:
+ mq_enqueue(&sc->sc_rx_slow_queue, m);
+ task_add(sc->sc_taskq, &sc->sc_rx_slow_task);
+ break;
+ case WG_PKT_TRANSPORT:
+ mq_enqueue(&sc->sc_rx_queue, m);
+ task_add(sc->sc_taskq, &sc->sc_rx_task);
+ break;
+ default:
+ break;
+ }
- mq_enqueue(&sc->sc_rx_queue, m);
- task_add(sc->sc_taskq, &sc->sc_rx_task);
return NULL;
free:
m_freem(m);
@@ -973,28 +992,28 @@ leave:
}
void
-wg_input_deliver(struct wg_softc *sc, struct mbuf *m)
+wg_input_deliver(struct mbuf *m)
{
void (*fn_input)(struct ifnet *, struct mbuf *);
- fn_input = AF_VAL(m->m_pkthdr.ph_family, ipv4_input, ipv6_input);
- NET_LOCK();
- if (fn_input)
- fn_input(&sc->sc_if, m);
- else
- panic("invalid af");
- NET_UNLOCK();
-}
-
-void
-wg_output_deliver_buf(struct wg_peer *p, uint8_t *buf, size_t buflen)
-{
- struct mbuf *m = m_gethdr(M_WAIT, MT_DATA);
struct wg_tag *tag = wg_mbuf_get_tag(m);
- m->m_len = 0;
- m_copyback(m, 0, buflen, buf, M_WAIT);
- tag->t_peer = p;
- tag->t_state = WG_PKT_STATE_DONE;
- wg_output_deliver(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_DONE) {
+ fn_input = AF_VAL(m->m_pkthdr.ph_family, ipv4_input, ipv6_input);
+ NET_LOCK();
+ if (fn_input)
+ fn_input(&sc->sc_if, m);
+ else
+ panic("invalid af");
+ NET_UNLOCK();
+ } else {
+ m_freem(m);
+ DPRINTF(sc, "did not input packet\n");
+ }
}
void
@@ -1076,8 +1095,11 @@ wg_clone_create(struct if_clone * ifc, int unit)
task_set(&sc->sc_tx_task, wg_tx_task_fn, &sc->sc_tx_queue);
task_set(&sc->sc_tx_slow_task, wg_tx_slow_task_fn, &sc->sc_tx_slow_queue);
task_set(&sc->sc_rx_task, wg_rx_task_fn, sc);
+ task_set(&sc->sc_rx_slow_task, wg_rx_slow_task_fn, sc);
mpq_init(&sc->sc_tx_queue, IPL_NET);
+ mpq_init(&sc->sc_tx_slow_queue, IPL_NET);
mq_init(&sc->sc_rx_queue, 1024, IPL_NET);
+ mq_init(&sc->sc_rx_slow_queue, 10, IPL_NET);
/* ifnet */