summaryrefslogtreecommitdiffstats
path: root/src/if_wg_vars.h
diff options
context:
space:
mode:
Diffstat (limited to 'src/if_wg_vars.h')
-rw-r--r--src/if_wg_vars.h443
1 files changed, 443 insertions, 0 deletions
diff --git a/src/if_wg_vars.h b/src/if_wg_vars.h
new file mode 100644
index 00000000000..8059346aeba
--- /dev/null
+++ b/src/if_wg_vars.h
@@ -0,0 +1,443 @@
+/*
+ * Copyright (c) 2019 Matt Dunwoodie <ncon@noconroy.net>
+ *
+ * Permission to use, copy, modify, and distribute this software for any
+ * purpose with or without fee is hereby granted, provided that the above
+ * copyright notice and this permission notice appear in all copies.
+ *
+ * THE SOFTWARE IS PROVIDED "AS IS" AND THE AUTHOR DISCLAIMS ALL WARRANTIES
+ * WITH REGARD TO THIS SOFTWARE INCLUDING ALL IMPLIED WARRANTIES OF
+ * MERCHANTABILITY AND FITNESS. IN NO EVENT SHALL THE AUTHOR BE LIABLE FOR
+ * ANY SPECIAL, DIRECT, INDIRECT, OR CONSEQUENTIAL DAMAGES OR ANY DAMAGES
+ * WHATSOEVER RESULTING FROM LOSS OF USE, DATA OR PROFITS, WHETHER IN AN
+ * ACTION OF CONTRACT, NEGLIGENCE OR OTHER TORTIOUS ACTION, ARISING OUT OF
+ * OR IN CONNECTION WITH THE USE OR PERFORMANCE OF THIS SOFTWARE.
+ */
+
+#ifndef _LIBWG_H_
+#define _LIBWG_H_
+
+#include <sys/types.h>
+#include <sys/time.h>
+#include <sys/timeout.h>
+#include <sys/mutex.h>
+
+/* This is only needed for wg_keypair. */
+#include <net/if_wg.h>
+
+#define WG_KEY_SIZE 32
+#define WG_MAC_SIZE 16
+#define WG_HASH_SIZE 32
+#define WG_COOKIE_SIZE 16
+#define WG_XNONCE_SIZE 24
+#define WG_MSG_PADDING_SIZE 16
+#define WG_COOKIE_RAND_VAL_SIZE 16
+#define WG_COOKIE_ID_VAL_MAXSIZE 32
+#define WG_TIMESTAMP_SIZE 12
+
+#define WG_ENCRYPTED_SIZE(n) ((n) + WG_MAC_SIZE)
+#define WG_PADDING_SIZE(n) ((WG_MSG_PADDING_SIZE - (n)) % WG_MSG_PADDING_SIZE)
+#define WG_ENCRYPTED_PADDED_SIZE(n) WG_ENCRYPTED_SIZE(WG_PADDED_SIZE(n)) //unused
+
+/* Constant for session */
+#define COUNTER_BITS_TOTAL = 2048,
+#define COUNTER_REDUNDANT_BITS = BITS_PER_LONG,
+#define COUNTER_WINDOW_SIZE = COUNTER_BITS_TOTAL - COUNTER_REDUNDANT_BITS
+
+#define WG_REKEY_AFTER_MESSAGES (UINT64_MAX - 0xffff)
+#define WG_REJECT_AFTER_MESSAGES (UINT64_MAX - COUNTER_WINDOW_SIZE - 1)
+#define WG_REKEY_AFTER_TIME 120
+#define WG_REJECT_AFTER_TIME 180
+#define WG_REKEY_ATTEMPT_COUNT 20
+#define WG_REKEY_TIMEOUT 5
+#define WG_KEEPALIVE_TIMEOUT 10
+#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_NEW = 0,
+ WG_STATE_MADE_INITIATION,
+ WG_STATE_RECV_INITIATION,
+ WG_STATE_MADE_RESPONSE,
+ WG_STATE_RECV_RESPONSE,
+ WG_STATE_INITIATOR,
+ WG_STATE_RESPONDER,
+};
+
+enum wg_pkt_type {
+ WG_PKT_UNKNOWN = 0,
+ WG_PKT_INITIATION,
+ WG_PKT_RESPONSE,
+ WG_PKT_COOKIE,
+ WG_PKT_TRANSPORT,
+};
+
+#define ARI_BITS (sizeof(uint64_t) * 8)
+#define ARB_BITS (1<<10) /* 1024 bitmap (960 usable) */
+
+struct antireplay {
+ uint64_t ar_head;
+ uint64_t ar_bitmap[ARB_BITS / ARI_BITS];
+};
+
+void antireplay_init(struct antireplay *);
+int antireplay_update(struct antireplay *, uint64_t);
+
+
+#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 {
+ size_t size;
+ struct mutex mtx;
+ struct map_item {
+ enum {
+ FM_ITEM_EMPTY = 0,
+ FM_ITEM_FILLED,
+ } state;
+ uint32_t key;
+ struct refcnt refcnt;
+ void *value;
+ } *map;
+};
+
+void fm_init(struct fixed_map *, size_t, int);
+void fm_destroy(struct fixed_map *);
+void fm_resize(struct fixed_map *, size_t);
+uint32_t fm_insert(struct fixed_map *, void *);
+void *fm_lookup(struct fixed_map *, uint32_t);
+void fm_put(struct fixed_map *, uint32_t);
+void fm_drop(struct fixed_map *, uint32_t);
+
+/* 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. */
+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);
+/* Constants for the wg protocol */
+#define WG_CONSTRUCTION "Noise_IKpsk2_25519_ChaChaPoly_BLAKE2s"
+#define WG_IDENTIFIER "WireGuard v1 zx2c4 Jason@zx2c4.com"
+#define WG_MAC1 "mac1----"
+#define WG_COOKIE "cookie--"
+
+/* First byte indicating packet type on the wire */
+#define WG_MSG_INITIATION htole32(1)
+#define WG_MSG_RESPONSE htole32(2)
+#define WG_MSG_COOKIE htole32(3)
+#define WG_MSG_TRANSPORT htole32(4)
+
+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_cookie {
+ uint8_t cookie[WG_COOKIE_SIZE];
+ struct timespec time;
+};
+
+struct wg_cookie_maker {
+ uint8_t seed[WG_COOKIE_SIZE];
+ struct timespec time;
+};
+
+struct wg_timers {
+ struct timeout t_ka;
+ struct timeout t_pka;
+ struct timeout t_broken;
+ struct timeout t_reinit;
+ struct timeout t_cleanup;
+ uint16_t t_pka_interval;
+};
+
+struct wg_session {
+ /* Static */
+ uint32_t s_local_id;
+ uint32_t s_remote_id;
+ struct wg_peer *s_peer;
+ struct timespec s_created;
+
+ /* All protected by s_mtx */
+ struct mutex s_mtx;
+ 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_peer {
+ uint32_t p_id;
+ void *p_arg;
+ struct wg_device *p_device;
+ struct wg_pubkey p_remote;
+ struct refcnt p_refcnt;
+
+ /* Atomic */
+ struct wg_timers p_timers;
+
+ /* All protected by p_mtx */
+ struct mutex p_mtx;
+ struct wg_privkey p_shared;
+ struct wg_cookie p_cookie;
+ struct timespec p_last_initiation;
+ uint64_t p_tx_bytes;
+ uint64_t p_rx_bytes;
+ uint8_t p_attempts;
+ 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 {
+ /* Static */
+ void *d_arg;
+ void (*d_cleanup)(struct wg_peer *);
+ void (*d_notify)(struct wg_peer *);
+ void (*d_outq)(struct wg_peer *, enum wg_pkt_type, uint32_t);
+
+ /* Mutex */
+ struct mutex d_mtx;
+ struct wg_cookie_maker d_cookie_maker;
+ struct wg_keypair d_keypair;
+
+ /* Atomic */
+ struct fixed_map d_peers;
+ struct fixed_map d_sessions;
+};
+
+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_STATE,
+ WG_MAC,
+ WG_ID,
+};
+
+static char *wg_error_str[] = {
+ "no error",
+ "invalid timestamp",
+ "exceeded initiation attemts",
+ "exceeded initiation transmit rate",
+ "unknown peer",
+ "non-allowed ip",
+ "ratelimit",
+ "unable to decrypt",
+ "replay detected",
+ "packet rejected",
+ "packet invalid",
+ "invalid state",
+ "invalid mac",
+ "invalid ID",
+};
+
+/* WireGuard functions */
+
+void wg_device_init(struct wg_device *, int,
+ void (*)(struct wg_peer *),
+ void (*)(struct wg_peer *, enum wg_pkt_type, uint32_t),
+ void (*)(struct wg_peer *), void *);
+void wg_device_setkey(struct wg_device *, struct wg_privkey *);
+void wg_device_getkey(struct wg_device *, struct wg_keypair *);
+void wg_device_destroy(struct wg_device *);
+
+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_put(struct wg_peer *);
+void wg_peer_drop(struct wg_peer *);
+
+void wg_peer_reset_attempts(struct wg_peer *);
+void wg_peer_clean(struct wg_peer *);
+void wg_peer_setshared(struct wg_peer *, struct wg_privkey *);
+void wg_peer_getshared(struct wg_peer *, struct wg_privkey *);
+struct timespec wg_peer_last_handshake(struct wg_peer *);
+struct wg_session *wg_peer_last_session(struct wg_peer *);
+
+void wg_session_put(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_cookie *,
+ uint32_t, uint8_t[WG_MAC_SIZE], struct wg_msg_cookie *);
+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_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);
+
+
+/* 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 *);
+
+#endif /* _LIBWG_H_ */
+/*
+ * Copyright (c) 2019 Matt Dunwoodie <ncon@noconroy.net>
+ *
+ * Permission to use, copy, modify, and distribute this software for any
+ * purpose with or without fee is hereby granted, provided that the above
+ * copyright notice and this permission notice appear in all copies.
+ *
+ * THE SOFTWARE IS PROVIDED "AS IS" AND THE AUTHOR DISCLAIMS ALL WARRANTIES
+ * WITH REGARD TO THIS SOFTWARE INCLUDING ALL IMPLIED WARRANTIES OF
+ * MERCHANTABILITY AND FITNESS. IN NO EVENT SHALL THE AUTHOR BE LIABLE FOR
+ * ANY SPECIAL, DIRECT, INDIRECT, OR CONSEQUENTIAL DAMAGES OR ANY DAMAGES
+ * WHATSOEVER RESULTING FROM LOSS OF USE, DATA OR PROFITS, WHETHER IN AN
+ * ACTION OF CONTRACT, NEGLIGENCE OR OTHER TORTIOUS ACTION, ARISING OUT OF
+ * OR IN CONNECTION WITH THE USE OR PERFORMANCE OF THIS SOFTWARE.
+ */
+
+#ifndef _MPQ_H_
+#define _MPQ_H_
+
+#include <sys/param.h>
+#include <sys/mbuf.h>
+#include <sys/systm.h>
+
+struct mpq {
+ struct mutex mpq_mtx;
+ struct mbuf_list mpq_list;
+ struct mbuf *mpq_cursor;
+ void *mpq_serializer;
+};
+
+void mpq_init(struct mpq *, int);
+int mpq_full(struct mpq *);
+int mpq_serialize_try_enter(struct mpq *);
+void mpq_serialize_leave(struct mpq *);
+void mpq_threaddone(struct mpq *, struct mbuf *);
+void mpq_enqueue(struct mpq *, struct mbuf *);
+struct mbuf *mpq_dethread(struct mpq *);
+struct mbuf *mpq_dequeue(struct mpq *);
+void mpq_done(struct mbuf *m);
+
+#define MPQ_LEN 1024
+#define MPQ_WORKER(fn_name, parallel_fn, serial_fn) \
+void fn_name(void *_mpq) { \
+ struct mbuf *m; \
+ struct mpq *mpq = _mpq; \
+ while ((m = mpq_dethread(mpq)) != NULL) { \
+ parallel_fn(m); \
+ mpq_threaddone(mpq, m); \
+ } \
+ if (mpq_serialize_try_enter(mpq) == 0) { \
+ while((m = mpq_dequeue(mpq)) != NULL) { \
+ serial_fn(m); \
+ } \
+ mpq_serialize_leave(mpq); \
+ } \
+} \
+
+#endif /* _MPQ_H_ */