diff --git a/mud.c b/mud.c index 8473d33..ec11e1a 100644 --- a/mud.c +++ b/mud.c @@ -1,5 +1,3 @@ -#include "mud.h" - #if defined __APPLE__ #define __APPLE_USE_RFC_3542 #endif @@ -8,15 +6,15 @@ #define _GNU_SOURCE #endif +#include "mud.h" + #include -#include #include #include #include #include #include -#include #include #include @@ -65,7 +63,6 @@ #define MUD_KEY_SIZE (32U) #define MUD_MAC_SIZE (16U) #define MUD_PUB_SIZE (crypto_scalarmult_BYTES) -#define MUD_SID_SIZE (8U) #define MUD_PACKET_MIN_SIZE (MUD_U48_SIZE + MUD_MAC_SIZE) #define MUD_PACKET_MAX_SIZE (1500U) @@ -80,33 +77,6 @@ #define MUD_SEND_TIMEOUT (MUD_ONE_SEC) #define MUD_TIME_TOLERANCE (10 * MUD_ONE_MIN) -struct mud_path { - enum mud_state state; - struct sockaddr_storage local_addr, addr; - struct { - uint64_t send_time; - int remote; - struct { - size_t remote; - size_t local; - } mtu; - unsigned char kiss[MUD_SID_SIZE]; - } conf; - uint64_t rdt; - uint64_t rtt; - uint64_t sdt; - uint64_t rst; - uint64_t r_sdt; - uint64_t r_rdt; - uint64_t r_rst; - int64_t r_dt; - uint64_t limit; - uint64_t recv_time; - uint64_t send_time; - uint64_t stat_time; - struct mud_path *next; -}; - struct mud_crypto_opt { unsigned char *dst; struct { @@ -142,7 +112,7 @@ struct mud_packet { } hdr; union { struct { - unsigned char kiss[MUD_SID_SIZE]; + unsigned char kiss[MUD_KISS_SIZE]; unsigned char mtu[MUD_U48_SIZE]; unsigned char state; struct mud_public public; @@ -161,7 +131,8 @@ struct mud { int fd; uint64_t send_timeout; uint64_t time_tolerance; - struct mud_path *path; + struct mud_path *paths; + unsigned count; struct { uint64_t time; unsigned char secret[crypto_scalarmult_SCALARBYTES]; @@ -177,7 +148,7 @@ struct mud { int set; struct sockaddr_storage addr; } peer; - unsigned char kiss[MUD_SID_SIZE]; + unsigned char kiss[MUD_KISS_SIZE]; }; static int @@ -392,41 +363,92 @@ mud_cmp_addr(struct sockaddr_storage *a, struct sockaddr_storage *b) return 1; } +struct mud_path * +mud_get_paths(struct mud *mud, unsigned *ret_count) +{ + unsigned count = 0; + + if (!ret_count) + return NULL; + + for (unsigned i = 0; i < mud->count; i++) { + struct mud_path *path = &mud->paths[i]; + + if (path->state != MUD_EMPTY) + count++; + } + + size_t size = count * sizeof(struct mud_path); + + if (!size) + return NULL; + + struct mud_path *paths = malloc(size); + + if (!paths) + return NULL; + + count = 0; + + for (unsigned i = 0; i < mud->count; i++) { + struct mud_path *path = &mud->paths[i]; + + if (path->state != MUD_EMPTY) + memcpy(&paths[count++], path, sizeof(struct mud_path)); + } + + *ret_count = count; + + return paths; +} + static struct mud_path * mud_get_path(struct mud *mud, struct sockaddr_storage *local_addr, struct sockaddr_storage *addr, int create) { - struct mud_path *path; - if (local_addr->ss_family != addr->ss_family) { errno = EINVAL; return NULL; } - for (path = mud->path; path; path = path->next) { - if (mud_cmp_addr(local_addr, &path->local_addr)) - continue; + for (unsigned i = 0; i < mud->count; i++) { + struct mud_path *path = &mud->paths[i]; - if (mud_cmp_addr(addr, &path->addr)) - continue; - - break; + if (path->state && + !mud_cmp_addr(local_addr, &path->local_addr) && + !mud_cmp_addr(addr, &path->addr)) + return path; } - if (path || !create) - return path; - - path = calloc(1, sizeof(struct mud_path)); - - if (!path) + if (!create) return NULL; - memmove(&path->local_addr, local_addr, sizeof(*local_addr)); - memmove(&path->addr, addr, sizeof(*addr)); + struct mud_path *path = NULL; + + for (unsigned i = 0; i < mud->count; i++) { + if (mud->paths[i].state == MUD_EMPTY) { + path = &mud->paths[i]; + break; + } + } + + if (!path) { + struct mud_path *paths = realloc(mud->paths, + (mud->count + 1) * sizeof(struct mud_path)); + + if (!paths) + return NULL; + + mud->count++; + mud->paths = paths; + + path = &paths[mud->count - 1]; + } + + memcpy(&path->local_addr, local_addr, sizeof(*local_addr)); + memcpy(&path->addr, addr, sizeof(*addr)); path->conf.mtu.local = mud->mtu; // XXX - path->next = mud->path; - mud->path = path; return path; } @@ -564,7 +586,8 @@ mud_set_time_tolerance(struct mud *mud, unsigned long msec) int mud_set_state(struct mud *mud, struct sockaddr *peer, enum mud_state state) { - if (!mud->peer.set || (state >= MUD_LAST)) { + if (!mud->peer.set || + (state < MUD_DOWN) || (state > MUD_UP)) { errno = EINVAL; return -1; } @@ -574,7 +597,7 @@ mud_set_state(struct mud *mud, struct sockaddr *peer, enum mud_state state) if (mud_ss_from_sa(&addr, peer)) return -1; - struct mud_path *path = mud_get_path(mud, &addr, &mud->peer.addr, state != MUD_DOWN); + struct mud_path *path = mud_get_path(mud, &addr, &mud->peer.addr, state > MUD_DOWN); if (!path) return -1; @@ -591,11 +614,13 @@ size_t mud_get_mtu(struct mud *mud) { size_t mtu = mud->mtu; - struct mud_path *path; - for (path = mud->path; path; path = path->next) { + for (unsigned i = 0; i < mud->count; i++) { + struct mud_path *path = &mud->paths[i]; + if (path->conf.mtu.local && path->conf.mtu.local < mtu) mtu = path->conf.mtu.local; + if (path->conf.mtu.remote && path->conf.mtu.remote < mtu) mtu = path->conf.mtu.remote; } @@ -614,11 +639,12 @@ mud_set_mtu(struct mud *mud, size_t mtu) mtu -= MUD_PACKET_MIN_SIZE; - struct mud_path *path; + for (unsigned i = 0; i < mud->count; i++) { + struct mud_path *path = &mud->paths[i]; - for (path = mud->path; path; path = path->next) { if (path->conf.mtu.local == mtu) continue; + path->conf.mtu.local = mtu; path->conf.remote = 0; } @@ -789,17 +815,11 @@ mud_delete(struct mud *mud) if (!mud) return; - while (mud->path) { - struct mud_path *path = mud->path; - mud->path = path->next; - free(path); - } + if (mud->paths) + free(mud->paths); - if (mud->fd != -1) { - int err = errno; + if (mud->fd >= 0) close(mud->fd); - errno = err; - } sodium_free(mud); } @@ -962,21 +982,13 @@ mud_packet_send(struct mud *mud, enum mud_packet_code code, } static void -mud_kiss_path(struct mud *mud, struct mud_path *path) +mud_kiss_path(struct mud *mud, unsigned char *kiss) { - struct mud_path **p = &mud->path; + for (unsigned i = 0; i < mud->count; i++) { + struct mud_path *path = &mud->paths[i]; - while (*p) { - struct mud_path *t = *p; - - if ((t == path) || - !memcmp(t->conf.kiss, path->conf.kiss, sizeof(path->conf.kiss))) { - p = &t->next; - continue; - } - - *p = t->next; - free(t); + if (memcmp(path->conf.kiss, kiss, sizeof(path->conf.kiss))) + memset(path, 0, sizeof(struct mud_path)); } } @@ -1037,7 +1049,7 @@ mud_packet_recv(struct mud *mud, struct mud_path *path, mud->crypto.use_next = 1; } } else { - mud_kiss_path(mud, path); + mud_kiss_path(mud, path->conf.kiss); mud_keyx(mud, packet->data.conf.public.local, packet->data.conf.aes); path->state = (enum mud_state)packet->data.conf.state; @@ -1156,10 +1168,10 @@ mud_update(struct mud *mud) uint64_t now = mud_now(); int update_keyx = !mud_keyx_init(mud, now); - struct mud_path *path = mud->path; + for (unsigned i = 0; i < mud->count; i++) { + struct mud_path *path = &mud->paths[i]; - for (; path; path = path->next) { - if (path->state == MUD_DOWN) + if (path->state < MUD_DOWN) continue; if (update_keyx || mud_timeout(now, path->recv_time, mud->send_timeout + MUD_ONE_SEC)) @@ -1196,17 +1208,18 @@ mud_send(struct mud *mud, const void *data, size_t size, int tc) return -1; } - struct mud_path *path; struct mud_path *path_min = NULL; struct mud_path *path_backup = NULL; int64_t limit_min = INT64_MAX; - for (path = mud->path; path; path = path->next) { - switch (path->state) { - case MUD_BACKUP: path_backup = path; /* FALLTHRU */ - case MUD_DOWN: continue; - default: break; + for (unsigned i = 0; i < mud->count; i++) { + struct mud_path *path = &mud->paths[i]; + + if (path->state <= MUD_DOWN) { + if (path->state == MUD_BACKUP) + path_backup = path; + continue; } int64_t limit = path->limit; diff --git a/mud.h b/mud.h index 7c09d50..e7dd5a5 100644 --- a/mud.h +++ b/mud.h @@ -1,15 +1,44 @@ #pragma once #include +#include +#include + +#define MUD_KISS_SIZE (8U) struct mud; -struct sockaddr; enum mud_state { - MUD_UP = 0, - MUD_BACKUP, + MUD_EMPTY = 0, MUD_DOWN, - MUD_LAST, + MUD_BACKUP, + MUD_UP, +}; + +struct mud_path { + enum mud_state state; + struct sockaddr_storage local_addr, addr; + struct { + uint64_t send_time; + int remote; + struct { + size_t remote; + size_t local; + } mtu; + unsigned char kiss[MUD_KISS_SIZE]; + } conf; + uint64_t rdt; + uint64_t rtt; + uint64_t sdt; + uint64_t rst; + uint64_t r_sdt; + uint64_t r_rdt; + uint64_t r_rst; + int64_t r_dt; + uint64_t limit; + uint64_t recv_time; + uint64_t send_time; + uint64_t stat_time; }; struct mud *mud_create (struct sockaddr *); @@ -34,3 +63,5 @@ int mud_peer (struct mud *, struct sockaddr *); int mud_recv (struct mud *, void *, size_t); int mud_send (struct mud *, const void *, size_t, int); + +struct mud_path *mud_get_paths(struct mud *, unsigned *);