From de1fbdf4403ea2a78f14925ffcc113198d87fc9d Mon Sep 17 00:00:00 2001 From: Guus Sliepen Date: Thu, 8 Nov 2018 20:27:54 +0100 Subject: [PATCH] Move the routing header out of the SPTPS payload. Give every node in the mesh a unique ID (based on a hash of its name), and add the ID of the source and destination packet at the start of a UDP packet, before the SPTPS payload. This allows fast forwarding of packets, and avoids having to look up nodes based on UDP address/port. --- src/Makefile.am | 1 - src/conf.c | 1 - src/devtools.c | 1 - src/have.h | 1 + src/meshlink.c | 28 +++---- src/meshlink_internal.h | 8 +- src/net.c | 2 - src/net.h | 3 + src/net_packet.c | 175 ++++++++++++++++++++++++---------------- src/net_setup.c | 2 - src/node.c | 89 ++++++++++++++++---- src/node.h | 5 +- src/protocol.c | 27 ++++--- src/protocol.h | 25 +++--- src/protocol_auth.c | 14 +--- src/protocol_key.c | 38 --------- src/protocol_misc.c | 53 ------------ src/route.c | 94 --------------------- src/route.h | 30 ------- src/sptps.c | 73 +++++++++++++++++ src/sptps.h | 8 +- 21 files changed, 307 insertions(+), 371 deletions(-) delete mode 100644 src/route.c delete mode 100644 src/route.h diff --git a/src/Makefile.am b/src/Makefile.am index ef0d8d9e..51c3daf2 100644 --- a/src/Makefile.am +++ b/src/Makefile.am @@ -70,7 +70,6 @@ libmeshlink_la_SOURCES = \ protocol_edge.c \ protocol_key.c \ protocol_misc.c \ - route.c route.h \ sockaddr.h \ splay_tree.c splay_tree.h \ sptps.c sptps.h \ diff --git a/src/conf.c b/src/conf.c index e3574ea5..9e4ce4b9 100644 --- a/src/conf.c +++ b/src/conf.c @@ -18,7 +18,6 @@ */ #include "system.h" -#include #include "splay_tree.h" #include "connection.h" diff --git a/src/devtools.c b/src/devtools.c index 3f2c0a49..859a3bee 100644 --- a/src/devtools.c +++ b/src/devtools.c @@ -18,7 +18,6 @@ */ #include "system.h" -#include #include "logger.h" #include "meshlink_internal.h" diff --git a/src/have.h b/src/have.h index 536226d5..ff714a69 100644 --- a/src/have.h +++ b/src/have.h @@ -42,6 +42,7 @@ #include #include #include +#include #ifdef HAVE_MINGW #include diff --git a/src/meshlink.c b/src/meshlink.c index 8da56922..048f72e5 100644 --- a/src/meshlink.c +++ b/src/meshlink.c @@ -38,7 +38,6 @@ typedef struct { #include "netutl.h" #include "node.h" #include "protocol.h" -#include "route.h" #include "sockaddr.h" #include "utils.h" #include "xalloc.h" @@ -1457,7 +1456,7 @@ void meshlink_close(meshlink_handle_t *mesh) { if(mesh->conffile) { fclose(mesh->conffile); - } + } memset(mesh, 0, sizeof(*mesh)); @@ -1535,10 +1534,8 @@ void meshlink_set_log_cb(meshlink_handle_t *mesh, meshlink_log_level_t level, me } bool meshlink_send(meshlink_handle_t *mesh, meshlink_node_t *destination, const void *data, size_t len) { - meshlink_packethdr_t *hdr; - // Validate arguments - if(!mesh || !destination || len >= MAXSIZE - sizeof(*hdr)) { + if(!mesh || !destination || len >= MAXSIZE) { meshlink_errno = MESHLINK_EINVAL; return false; } @@ -1562,16 +1559,11 @@ bool meshlink_send(meshlink_handle_t *mesh, meshlink_node_t *destination, const packet->probe = false; packet->tcp = false; - packet->len = len + sizeof(*hdr); - - hdr = (meshlink_packethdr_t *)packet->data; - memset(hdr, 0, sizeof(*hdr)); - // leave the last byte as 0 to make sure strings are always - // null-terminated if they are longer than the buffer - strncpy((char *)hdr->destination, destination->name, (sizeof(hdr)->destination) - 1); - strncpy((char *)hdr->source, mesh->self->name, (sizeof(hdr)->source) - 1); + packet->len = len; + packet->src = mesh->self->id; + packet->dst = ((node_t *)destination)->id; - memcpy(packet->data + sizeof(*hdr), data, len); + memcpy(packet->data, data, len); // Queue it if(!meshlink_queue_push(&mesh->outpacketqueue, packet)) { @@ -1596,7 +1588,13 @@ void meshlink_send_from_queue(event_loop_t *loop, meshlink_handle_t *mesh) { mesh->self->in_packets++; mesh->self->in_bytes += packet->len; - route(mesh, mesh->self, packet); + node_t *dest = lookup_node_id(mesh, packet->dst); + + if(dest) { + send_packet(mesh, dest, packet); + } + + free(packet); } ssize_t meshlink_get_pmtu(meshlink_handle_t *mesh, meshlink_node_t *destination) { diff --git a/src/meshlink_internal.h b/src/meshlink_internal.h index 92774c49..ac35f276 100644 --- a/src/meshlink_internal.h +++ b/src/meshlink_internal.h @@ -89,6 +89,7 @@ struct meshlink_handle { struct splay_tree_t *config; struct splay_tree_t *edges; struct splay_tree_t *nodes; + struct splay_tree_t *node_ids; struct list_t *connections; struct list_t *outgoings; @@ -119,7 +120,6 @@ struct meshlink_handle { bool default_blacklist; - hash_t *node_udp_cache; struct connection_t *everyone; struct ecdsa *invitation_key; int invitation_timeout; @@ -163,12 +163,6 @@ struct meshlink_channel { meshlink_channel_poll_cb_t poll_cb; }; -/// Header for data packets routed between nodes -typedef struct meshlink_packethdr { - uint8_t destination[16]; - uint8_t source[16]; -} __attribute__((__packed__)) meshlink_packethdr_t; - extern void meshlink_send_from_queue(event_loop_t *el, meshlink_handle_t *mesh); extern void update_node_status(meshlink_handle_t *mesh, struct node_t *n); extern meshlink_log_level_t global_log_level; diff --git a/src/net.c b/src/net.c index 9d5e8171..806fe0d2 100644 --- a/src/net.c +++ b/src/net.c @@ -31,8 +31,6 @@ #include "protocol.h" #include "xalloc.h" -#include - #if !defined(min) static inline int min(int a, int b) { return a < b ? a : b; diff --git a/src/net.h b/src/net.h index 04bd55a5..757d15e6 100644 --- a/src/net.h +++ b/src/net.h @@ -41,6 +41,9 @@ typedef struct vpn_packet_t { unsigned int tcp: 1; }; uint16_t len; /* the actual number of bytes in the `data' field */ + + uint64_t src; + uint64_t dst; uint8_t data[MAXSIZE]; } vpn_packet_t; diff --git a/src/net_packet.c b/src/net_packet.c index 162dcef2..b8c51304 100644 --- a/src/net_packet.c +++ b/src/net_packet.c @@ -28,7 +28,6 @@ #include "net.h" #include "netutl.h" #include "protocol.h" -#include "route.h" #include "utils.h" #include "xalloc.h" @@ -204,25 +203,7 @@ static void mtu_probe_h(meshlink_handle_t *mesh, node_t *n, vpn_packet_t *packet /* VPN packet I/O */ -static void receive_packet(meshlink_handle_t *mesh, node_t *n, vpn_packet_t *packet) { - logger(mesh, MESHLINK_DEBUG, "Received packet of %d bytes from %s", packet->len, n->name); - - if(n->status.blacklisted) { - logger(mesh, MESHLINK_WARNING, "Dropping packet from blacklisted node %s", n->name); - } else { - n->in_packets++; - n->in_bytes += packet->len; - - route(mesh, n, packet); - } -} - -static bool try_mac(meshlink_handle_t *mesh, node_t *n, const vpn_packet_t *inpkt) { - (void)mesh; - return sptps_verify_datagram(&n->sptps, inpkt->data, inpkt->len); -} - -static void receive_udppacket(meshlink_handle_t *mesh, node_t *n, vpn_packet_t *inpkt) { +static void receive_udppacket(meshlink_handle_t *mesh, node_t *n, const void *data, uint16_t len) { if(!n->sptps.state) { if(!n->status.waitingforkey) { logger(mesh, MESHLINK_DEBUG, "Got packet from %s but we haven't exchanged keys yet", n->name); @@ -234,7 +215,7 @@ static void receive_udppacket(meshlink_handle_t *mesh, node_t *n, vpn_packet_t * return; } - sptps_receive_data(&n->sptps, inpkt->data, inpkt->len); + sptps_receive_data(&n->sptps, data, len); } static void send_sptps_packet(meshlink_handle_t *mesh, node_t *n, vpn_packet_t *origpkt) { @@ -400,7 +381,14 @@ bool send_sptps_data(void *handle, uint8_t type, const void *data, size_t len) { choose_udp_address(mesh, to, &sa, &sock); } - if(sendto(mesh->listen_socket[sock].udp.fd, data, len, 0, &sa->sa, SALEN(sa->sa)) < 0 && !sockwouldblock(sockerrno)) { + /* Add the source and destination IDs */ + + vpn_packet_t pkt; + pkt.src = mesh->self->id; + pkt.dst = to->id; + memcpy(pkt.data, data, len); + + if(sendto(mesh->listen_socket[sock].udp.fd, &pkt.src, len + 16, 0, &sa->sa, SALEN(sa->sa)) < 0 && !sockwouldblock(sockerrno)) { if(sockmsgsize(sockerrno)) { if(to->maxmtu >= len) { to->maxmtu = len - 1; @@ -422,6 +410,8 @@ bool receive_sptps_record(void *handle, uint8_t type, const void *data, uint16_t node_t *from = handle; meshlink_handle_t *mesh = from->mesh; + assert(!from->status.blacklisted); + if(type == SPTPS_HANDSHAKE) { if(!from->status.validkey) { logger(mesh, MESHLINK_INFO, "SPTPS key exchange with %s succesful", from->name); @@ -441,16 +431,52 @@ bool receive_sptps_record(void *handle, uint8_t type, const void *data, uint16_t return false; } - vpn_packet_t inpkt; + if(type == SPTPS_UNENCRYPTED) { + /* This is a packet that needs to be forwarded */ + struct { + uint64_t src; + uint64_t dst; + } hdr; + + if(len < sizeof(hdr)) { + logger(mesh, MESHLINK_ERROR, "Too short packet from %s", from->name); + return false; + } + + memcpy(&hdr, data, sizeof(hdr)); + + if(hdr.src == mesh->self->id) { + logger(mesh, MESHLINK_WARNING, "Dropping packet from ourself"); + return true; + } + + node_t *orig = lookup_node_id(mesh, hdr.src); + + if(!orig) { + logger(mesh, MESHLINK_ERROR, "Received packet from %s with unknown source", from->name); + return true; + } + + if(orig->status.blacklisted) { + logger(mesh, MESHLINK_WARNING, "Dropping packet from blacklisted node %s", orig->name); + return true; + } + + if(hdr.dst == mesh->self->id) { + receive_udppacket(mesh, orig, data, len); + } + + // TODO: forward + return true; + } if(type == PKT_PROBE) { + vpn_packet_t inpkt; inpkt.len = len; inpkt.probe = true; memcpy(inpkt.data, data, len); mtu_probe_h(mesh, from, &inpkt, len); return true; - } else { - inpkt.probe = false; } if(type & ~(PKT_COMPRESSED)) { @@ -463,10 +489,10 @@ bool receive_sptps_record(void *handle, uint8_t type, const void *data, uint16_t return false; } - memcpy(inpkt.data, data, len); // TODO: get rid of memcpy - inpkt.len = len; + if(mesh->receive_cb) { + mesh->receive_cb(mesh, (meshlink_node_t *)from, data, len); + } - receive_packet(mesh, from, &inpkt); return true; } @@ -511,38 +537,37 @@ void broadcast_packet(meshlink_handle_t *mesh, const node_t *from, vpn_packet_t } } -static node_t *try_harder(meshlink_handle_t *mesh, const sockaddr_t *from, const vpn_packet_t *pkt) { - node_t *n = NULL; - bool hard = false; - static time_t last_hard_try = 0; +void forward_udppacket(meshlink_handle_t *mesh, const node_t *from, vpn_packet_t *pkt) { + node_t *to = lookup_node_id(mesh, pkt->dst); - for splay_each(edge_t, e, mesh->edges) { - if(!e->to->status.reachable || e->to == mesh->self) { - continue; - } + if(!to) { + logger(mesh, MESHLINK_WARNING, "Received UDP packet from %s with unknown destination", from->name); + return; + } - if(sockaddrcmp_noport(from, &e->address)) { - if(last_hard_try == mesh->loop.now.tv_sec) { - continue; - } + if(pkt->len > to->minmtu) { + // send via TCP + abort(); + } - hard = true; - } + const sockaddr_t *sa; + int sock; - if(!try_mac(mesh, e->to, pkt)) { - continue; - } + choose_udp_address(mesh, to, &sa, &sock); - n = e->to; - break; - } + if(sendto(mesh->listen_socket[sock].udp.fd, &pkt->src, pkt->len + 16, 0, &sa->sa, SALEN(sa->sa)) < 0 && !sockwouldblock(sockerrno)) { + if(sockmsgsize(sockerrno)) { + if(to->maxmtu >= pkt->len) { + to->maxmtu = pkt->len - 1; + } - if(hard) { - last_hard_try = mesh->loop.now.tv_sec; + if(to->mtu >= pkt->len) { + to->mtu = pkt->len - 1; + } + } else { + logger(mesh, MESHLINK_WARNING, "Error forwarding UDP SPTPS packet to %s: %s", to->name, sockstrerror(sockerrno)); + } } - - last_hard_try = mesh->loop.now.tv_sec; - return n; } void handle_incoming_vpn_data(event_loop_t *loop, void *data, int flags) { @@ -556,9 +581,9 @@ void handle_incoming_vpn_data(event_loop_t *loop, void *data, int flags) { node_t *n; int len; - len = recvfrom(ls->udp.fd, pkt.data, MAXSIZE, 0, &from.sa, &fromlen); + len = recvfrom(ls->udp.fd, &pkt.src, 16 + MAXSIZE, 0, &from.sa, &fromlen); - if(len <= 0 || len > MAXSIZE) { + if(len <= 16 || len > MAXSIZE + 16) { if(!sockwouldblock(sockerrno)) { logger(mesh, MESHLINK_ERROR, "Receiving packet failed: %s", sockstrerror(sockerrno)); } @@ -566,25 +591,20 @@ void handle_incoming_vpn_data(event_loop_t *loop, void *data, int flags) { return; } - pkt.len = len; + if(!pkt.src || !pkt.dst) { + logger(mesh, MESHLINK_WARNING, "Unhandled NULL ids in packet header"); + return; + } - sockaddrunmap(&from); /* Some braindead IPv6 implementations do stupid things. */ + pkt.len = len - 16; - n = lookup_node_udp(mesh, &from); + n = lookup_node_id(mesh, pkt.src); if(!n) { - n = try_harder(mesh, &from, &pkt); - - if(n) { - update_node_udp(mesh, n, &from); - } else if(mesh->log_level >= MESHLINK_WARNING) { - hostname = sockaddr2hostname(&from); - logger(mesh, MESHLINK_WARNING, "Received UDP packet from unknown source %s", hostname); - free(hostname); - return; - } else { - return; - } + hostname = sockaddr2hostname(&from); + logger(mesh, MESHLINK_WARNING, "Received UDP packet from unknown source %s", hostname); + free(hostname); + return; } if(n->status.blacklisted) { @@ -592,7 +612,20 @@ void handle_incoming_vpn_data(event_loop_t *loop, void *data, int flags) { return; } + if(n == mesh->self) { + logger(mesh, MESHLINK_WARNING, "Dropping packet from ourself"); + return; + } + n->sock = ls - mesh->listen_socket; - receive_udppacket(mesh, n, &pkt); + if(sockaddrcmp_noport(&n->address, &from)) { + update_node_udp(mesh, n, &from); + } + + if(pkt.dst != mesh->self->id) { + forward_udppacket(mesh, n, &pkt); + } else { + receive_udppacket(mesh, n, pkt.data, pkt.len); + } } diff --git a/src/net_setup.c b/src/net_setup.c index 10fb37b0..811d9811 100644 --- a/src/net_setup.c +++ b/src/net_setup.c @@ -28,7 +28,6 @@ #include "net.h" #include "netutl.h" #include "protocol.h" -#include "route.h" #include "utils.h" #include "xalloc.h" @@ -469,7 +468,6 @@ bool setup_network(meshlink_handle_t *mesh) { mesh->pinginterval = 60; mesh->pingtimeout = 5; - maxoutbufsize = 10 * MTU; if(!setup_myself(mesh)) { return false; diff --git a/src/node.c b/src/node.c index 4f4f5999..164520aa 100644 --- a/src/node.c +++ b/src/node.c @@ -33,22 +33,32 @@ static int node_compare(const node_t *a, const node_t *b) { return strcmp(a->name, b->name); } +static int node_id_compare(const node_t *a, const node_t *b) { + if(a->id < b->id) { + return -1; + } else if(a->id == b->id) { + return 0; + } else { + return 1; + } +} + void init_nodes(meshlink_handle_t *mesh) { mesh->nodes = splay_alloc_tree((splay_compare_t) node_compare, (splay_action_t) free_node); - mesh->node_udp_cache = hash_alloc(0x100, sizeof(sockaddr_t)); + mesh->node_ids = splay_alloc_tree((splay_compare_t) node_id_compare, NULL); } void exit_nodes(meshlink_handle_t *mesh) { - if(mesh->node_udp_cache) { - hash_free(mesh->node_udp_cache); - } - if(mesh->nodes) { splay_delete_tree(mesh->nodes); } - mesh->node_udp_cache = NULL; + if(mesh->node_ids) { + splay_delete_tree(mesh->node_ids); + } + mesh->nodes = NULL; + mesh->node_ids = NULL; } node_t *new_node(void) { @@ -88,6 +98,7 @@ void free_node(node_t *n) { void node_add(meshlink_handle_t *mesh, node_t *n) { n->mesh = mesh; splay_insert(mesh->nodes, n); + update_node_id(mesh, n); } void node_del(meshlink_handle_t *mesh, node_t *n) { @@ -102,15 +113,65 @@ void node_del(meshlink_handle_t *mesh, node_t *n) { node_t *lookup_node(meshlink_handle_t *mesh, const char *name) { const node_t n = {.name = (char *)name}; - node_t *result; - - result = splay_search(mesh->nodes, &n); + return splay_search(mesh->nodes, &n); +} - return result; +node_t *lookup_node_id(meshlink_handle_t *mesh, uint64_t id) { + const node_t n = {.id = id}; + return splay_search(mesh->node_ids, &n); } -node_t *lookup_node_udp(meshlink_handle_t *mesh, const sockaddr_t *sa) { - return hash_search(mesh->node_udp_cache, sa); +void update_node_id(meshlink_handle_t *mesh, node_t *n) { + if(n->id) { + logger(mesh, LOG_WARNING, "Node %s already has id %"PRIu64"\n", n->name, n->id); + return; + } + + struct { + uint8_t public[32]; + uint32_t gen; + } input; + + uint8_t hash[64]; + uint64_t id; + + memset(&input, 0, sizeof input); + + strncpy(input.public, n->name, sizeof input.public); + input.gen = 0; + + while(true) { + sha512(&input, sizeof input, hash); + memcpy(&id, hash, sizeof id); + input.gen++; + + // ID 0 is reserved + if(!id) { + continue; + } + + // Check if there is a conflict with an existing node + node_t *other = lookup_node_id(mesh, id); + int cmp = other ? strcmp(n->name, other->name) : 0; + + // If yes and we sort after the other, try again + if(cmp > 0) { + continue; + } + + if(other) { + splay_delete(mesh->node_ids, other); + } + + n->id = id; + splay_insert(mesh->node_ids, n); + + if(other) { + update_node_id(mesh, other); + } + + break; + } } void update_node_udp(meshlink_handle_t *mesh, node_t *n, const sockaddr_t *sa) { @@ -119,8 +180,6 @@ void update_node_udp(meshlink_handle_t *mesh, node_t *n, const sockaddr_t *sa) { return; } - hash_insert(mesh->node_udp_cache, &n->address, NULL); - if(sa) { n->address = *sa; n->sock = 0; @@ -132,8 +191,6 @@ void update_node_udp(meshlink_handle_t *mesh, node_t *n, const sockaddr_t *sa) { } } - hash_insert(mesh->node_udp_cache, sa, n); - meshlink_hint_address(mesh, (meshlink_node_t *)n, &sa->sa); if(mesh->log_level >= MESHLINK_DEBUG) { diff --git a/src/node.h b/src/node.h index eaba79d0..3314feef 100644 --- a/src/node.h +++ b/src/node.h @@ -53,6 +53,8 @@ typedef struct node_t { int sock; /* Socket to use for outgoing UDP packets */ sockaddr_t address; /* his real (internet) ip to send UDP packets to */ + uint64_t id; /* Unique ID for this node */ + node_status_t status; time_t last_state_change; time_t last_req_key; @@ -95,7 +97,8 @@ extern void free_node(node_t *); extern void node_add(struct meshlink_handle *mesh, node_t *); extern void node_del(struct meshlink_handle *mesh, node_t *); extern node_t *lookup_node(struct meshlink_handle *mesh, const char *); -extern node_t *lookup_node_udp(struct meshlink_handle *mesh, const sockaddr_t *); +extern node_t *lookup_node_id(struct meshlink_handle *mesh, uint64_t id); extern void update_node_udp(struct meshlink_handle *mesh, node_t *, const sockaddr_t *); +extern void update_node_id(struct meshlink_handle *mesh, node_t *); #endif diff --git a/src/protocol.c b/src/protocol.c index 785e20b5..6736cae7 100644 --- a/src/protocol.c +++ b/src/protocol.c @@ -31,22 +31,27 @@ /* Jumptable for the request handlers */ static bool (*request_handlers[])(meshlink_handle_t *, connection_t *, const char *) = { - id_h, NULL, NULL, NULL /* metakey_h, challenge_h, chal_reply_h */, ack_h, - status_h, error_h, termreq_h, - ping_h, pong_h, - NULL, NULL, //add_subnet_h, del_subnet_h, - add_edge_h, del_edge_h, - key_changed_h, req_key_h, ans_key_h, tcppacket_h, NULL, //control_h, + id_h, + ack_h, + ping_h, + pong_h, + add_edge_h, + del_edge_h, + req_key_h, + ans_key_h, }; /* Request names */ static char (*request_name[]) = { - "ID", "METAKEY", "CHALLENGE", "CHAL_REPLY", "ACK", - "STATUS", "ERROR", "TERMREQ", - "PING", "PONG", - "ADD_SUBNET", "DEL_SUBNET", - "ADD_EDGE", "DEL_EDGE", "KEY_CHANGED", "REQ_KEY", "ANS_KEY", "PACKET", "CONTROL", + "ID", + "ACK", + "PING", + "PONG", + "ADD_EDGE", + "DEL_EDGE", + "REQ_KEY", + "ANS_KEY", }; bool check_id(const char *id) { diff --git a/src/protocol.h b/src/protocol.h index 92ccef01..4bf76ee9 100644 --- a/src/protocol.h +++ b/src/protocol.h @@ -24,8 +24,8 @@ /* Protocol version. Different major versions are incompatible. */ -#define PROT_MAJOR 17 -#define PROT_MINOR 3 /* Should not exceed 255! */ +#define PROT_MAJOR 18 +#define PROT_MINOR 0 /* Should not exceed 255! */ /* Silly Windows */ @@ -37,15 +37,15 @@ typedef enum request_t { ALL = -1, /* Guardian for allow_request */ - ID = 0, METAKEY, CHALLENGE, CHAL_REPLY, ACK, - STATUS, ERROR, TERMREQ, - PING, PONG, - ADD_SUBNET, DEL_SUBNET, - ADD_EDGE, DEL_EDGE, - KEY_CHANGED, REQ_KEY, ANS_KEY, - PACKET, + ID = 0, + ACK, + PING, + PONG, + ADD_EDGE, + DEL_EDGE, + REQ_KEY, + ANS_KEY, /* Tinc 1.1 requests */ - CONTROL, REQ_PUBKEY, ANS_PUBKEY, REQ_SPTPS, LAST /* Guardian for the highest request number */ @@ -87,16 +87,12 @@ extern bool send_ping(struct meshlink_handle *mesh, struct connection_t *); extern bool send_pong(struct meshlink_handle *mesh, struct connection_t *); extern bool send_add_edge(struct meshlink_handle *mesh, struct connection_t *, const struct edge_t *, int contradictions); extern bool send_del_edge(struct meshlink_handle *mesh, struct connection_t *, const struct edge_t *, int contradictions); -extern void send_key_changed(struct meshlink_handle *mesh); extern bool send_req_key(struct meshlink_handle *mesh, struct node_t *); /* Request handlers */ extern bool id_h(struct meshlink_handle *mesh, struct connection_t *, const char *); extern bool ack_h(struct meshlink_handle *mesh, struct connection_t *, const char *); -extern bool status_h(struct meshlink_handle *mesh, struct connection_t *, const char *); -extern bool error_h(struct meshlink_handle *mesh, struct connection_t *, const char *); -extern bool termreq_h(struct meshlink_handle *mesh, struct connection_t *, const char *); extern bool ping_h(struct meshlink_handle *mesh, struct connection_t *, const char *); extern bool pong_h(struct meshlink_handle *mesh, struct connection_t *, const char *); extern bool add_edge_h(struct meshlink_handle *mesh, struct connection_t *, const char *); @@ -104,6 +100,5 @@ extern bool del_edge_h(struct meshlink_handle *mesh, struct connection_t *, cons extern bool key_changed_h(struct meshlink_handle *mesh, struct connection_t *, const char *); extern bool req_key_h(struct meshlink_handle *mesh, struct connection_t *, const char *); extern bool ans_key_h(struct meshlink_handle *mesh, struct connection_t *, const char *); -extern bool tcppacket_h(struct meshlink_handle *mesh, struct connection_t *, const char *); #endif diff --git a/src/protocol_auth.c b/src/protocol_auth.c index e56aa881..6227e455 100644 --- a/src/protocol_auth.c +++ b/src/protocol_auth.c @@ -37,8 +37,6 @@ #include "xalloc.h" #include "ed25519/sha512.h" -#include - extern bool node_write_devclass(meshlink_handle_t *mesh, node_t *n); static bool send_proxyrequest(meshlink_handle_t *mesh, connection_t *c) { @@ -342,8 +340,8 @@ bool id_h(meshlink_handle_t *mesh, connection_t *c, const char *request) { free(mykey); - c->protocol_minor = 2; - c->allow_request = 1; + c->protocol_minor = PROT_MINOR; + c->allow_request = LAST; return sptps_start(&c->sptps, c, false, false, mesh->invitation_key, c->ecdsa, meshlink_invitation_label, sizeof(meshlink_invitation_label), send_meta_sptps, receive_invitation_sptps); } @@ -410,14 +408,6 @@ bool id_h(meshlink_handle_t *mesh, connection_t *c, const char *request) { return false; } - /* Forbid version rollback for nodes whose ECDSA key we know */ - - if(ecdsa_active(c->ecdsa) && c->protocol_minor < 2) { - logger(mesh, MESHLINK_ERROR, "Peer %s tries to roll back protocol version to %d.%d", - c->name, c->protocol_major, c->protocol_minor); - return false; - } - c->allow_request = ACK; char label[sizeof(meshlink_tcp_label) + strlen(mesh->self->name) + strlen(c->name) + 2]; diff --git a/src/protocol_key.c b/src/protocol_key.c index cae95047..4218c055 100644 --- a/src/protocol_key.c +++ b/src/protocol_key.c @@ -31,44 +31,6 @@ #include "utils.h" #include "xalloc.h" -void send_key_changed(meshlink_handle_t *mesh) { - send_request(mesh, mesh->everyone, "%d %x %s", KEY_CHANGED, rand(), mesh->self->name); - - /* Force key exchange for connections using SPTPS */ - - for splay_each(node_t, n, mesh->nodes) - if(n->status.reachable && n->status.validkey) { - sptps_force_kex(&n->sptps); - } -} - -bool key_changed_h(meshlink_handle_t *mesh, connection_t *c, const char *request) { - char name[MAX_STRING_SIZE]; - node_t *n; - - if(sscanf(request, "%*d %*x " MAX_STRING, name) != 1) { - logger(mesh, MESHLINK_ERROR, "Got bad %s from %s", "KEY_CHANGED", c->name); - return false; - } - - if(seen_request(mesh, request)) { - return true; - } - - n = lookup_node(mesh, name); - - if(!n) { - logger(mesh, MESHLINK_ERROR, "Got %s from %s origin %s which does not exist", "KEY_CHANGED", c->name, name); - return true; - } - - /* Tell the others */ - - forward_request(mesh, c, request); - - return true; -} - static bool send_initial_sptps_data(void *handle, uint8_t type, const void *data, size_t len) { (void)type; node_t *to = handle; diff --git a/src/protocol_misc.c b/src/protocol_misc.c index 727881bd..b2e30680 100644 --- a/src/protocol_misc.c +++ b/src/protocol_misc.c @@ -29,45 +29,6 @@ #include "protocol.h" #include "utils.h" -int maxoutbufsize = 0; - -/* Status and error notification routines */ - -bool status_h(meshlink_handle_t *mesh, connection_t *c, const char *request) { - int statusno; - char statusstring[MAX_STRING_SIZE]; - - if(sscanf(request, "%*d %d " MAX_STRING, &statusno, statusstring) != 2) { - logger(mesh, MESHLINK_ERROR, "Got bad %s from %s", "STATUS", c->name); - return false; - } - - logger(mesh, MESHLINK_INFO, "Status message from %s: %d: %s", c->name, statusno, statusstring); - - return true; -} - -bool error_h(meshlink_handle_t *mesh, connection_t *c, const char *request) { - int err; - char errorstring[MAX_STRING_SIZE]; - - if(sscanf(request, "%*d %d " MAX_STRING, &err, errorstring) != 2) { - logger(mesh, MESHLINK_ERROR, "Got bad %s from %s", "ERROR", c->name); - return false; - } - - logger(mesh, MESHLINK_INFO, "Error message from %s: %d: %s", c->name, err, errorstring); - - return false; -} - -bool termreq_h(meshlink_handle_t *mesh, connection_t *c, const char *request) { - (void)mesh; - (void)c; - (void)request; - return false; -} - bool send_ping(meshlink_handle_t *mesh, connection_t *c) { c->status.pinged = true; c->last_ping_time = mesh->loop.now.tv_sec; @@ -105,17 +66,3 @@ bool pong_h(meshlink_handle_t *mesh, connection_t *c, const char *request) { return true; } - -/* Sending and receiving packets via TCP */ - -bool tcppacket_h(meshlink_handle_t *mesh, connection_t *c, const char *request) { - short int len; - - if(sscanf(request, "%*d %hd", &len) != 1) { - logger(mesh, MESHLINK_ERROR, "Got bad %s from %s", "PACKET", c->name); - return false; - } - - // This should never happen with MeshLink. - return false; -} diff --git a/src/route.c b/src/route.c deleted file mode 100644 index f7a728dc..00000000 --- a/src/route.c +++ /dev/null @@ -1,94 +0,0 @@ -/* - route.c -- routing - Copyright (C) 2014 Guus Sliepen - - This program is free software; you can redistribute it and/or modify - it under the terms of the GNU General Public License as published by - the Free Software Foundation; either version 2 of the License, or - (at your option) any later version. - - This program is distributed in the hope that it will be useful, - but WITHOUT ANY WARRANTY; without even the implied warranty of - MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the - GNU General Public License for more details. - - You should have received a copy of the GNU General Public License along - with this program; if not, write to the Free Software Foundation, Inc., - 51 Franklin Street, Fifth Floor, Boston, MA 02110-1301 USA. -*/ - -#include "system.h" - -#include "logger.h" -#include "meshlink_internal.h" -#include "net.h" -#include "route.h" -#include "utils.h" - -bool decrement_ttl = false; - -static bool checklength(node_t *source, vpn_packet_t *packet, uint16_t length) { - if(packet->len < length) { - logger(source->mesh, MESHLINK_WARNING, "Got too short packet from %s", source->name); - return false; - } else { - return true; - } -} - -void route(meshlink_handle_t *mesh, node_t *source, vpn_packet_t *packet) { - // TODO: route on name or key - - node_t *owner = NULL; - node_t *via = NULL; - meshlink_packethdr_t *hdr = (meshlink_packethdr_t *) packet->data; - owner = lookup_node(mesh, (char *)hdr->destination); - logger(mesh, MESHLINK_DEBUG, "Routing packet from \"%s\" to \"%s\"\n", hdr->source, hdr->destination); - - //Check Lenght - if(!checklength(source, packet, sizeof(*hdr))) { - return; - } - - if(owner == NULL) { - //Lookup failed - logger(mesh, MESHLINK_WARNING, "Cant lookup the owner of a packet in the route() function. This should never happen!\n"); - logger(mesh, MESHLINK_WARNING, "Destination was: %s\n", hdr->destination); - return; - } - - if(owner == mesh->self) { - const void *payload = packet->data + sizeof(*hdr); - size_t len = packet->len - sizeof(*hdr); - - char hex[len * 2 + 1]; - - if(mesh->log_level >= MESHLINK_DEBUG) { - bin2hex(payload, hex, len); // don't do this unless it's going to be logged - } - - logger(mesh, MESHLINK_DEBUG, "I received a packet for me with payload: %s\n", hex); - - if(mesh->receive_cb) { - mesh->receive_cb(mesh, (meshlink_node_t *)source, payload, len); - } - - return; - } - - if(!owner->status.reachable) { - //TODO: check what to do here, not just print a warning - logger(mesh, MESHLINK_WARNING, "The owner of a packet in the route() function is unreachable. Dropping packet.\n"); - return; - } - - via = (owner->via == mesh->self) ? owner->nexthop : owner->via; - - if(via == source) { - logger(mesh, MESHLINK_ERROR, "Routing loop for packet from %s!", source->name); - return; - } - - send_packet(mesh, owner, packet); - return; -} diff --git a/src/route.h b/src/route.h deleted file mode 100644 index 16e3a97d..00000000 --- a/src/route.h +++ /dev/null @@ -1,30 +0,0 @@ -#ifndef MESHLINK_ROUTE_H -#define MESHLINK_ROUTE_H - -/* - route.h -- header file for route.c - Copyright (C) 2014, 2017 Guus Sliepen - - This program is free software; you can redistribute it and/or modify - it under the terms of the GNU General Public License as published by - the Free Software Foundation; either version 2 of the License, or - (at your option) any later version. - - This program is distributed in the hope that it will be useful, - but WITHOUT ANY WARRANTY; without even the implied warranty of - MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the - GNU General Public License for more details. - - You should have received a copy of the GNU General Public License along - with this program; if not, write to the Free Software Foundation, Inc., - 51 Franklin Street, Fifth Floor, Boston, MA 02110-1301 USA. -*/ - -#include "net.h" -#include "node.h" - -extern bool decrement_ttl; - -extern void route(struct meshlink_handle *mesh, struct node_t *, struct vpn_packet_t *); - -#endif diff --git a/src/sptps.c b/src/sptps.c index f44374ee..a8338256 100644 --- a/src/sptps.c +++ b/src/sptps.c @@ -106,6 +106,7 @@ static bool send_record_priv_datagram(sptps_t *s, uint8_t type, const void *data return s->send_data(s->handle, type, buffer, len + 5UL); } } + // Send a record (private version, accepts all record types, handles encryption and authentication). static bool send_record_priv(sptps_t *s, uint8_t type, const void *data, uint16_t len) { if(s->datagram) { @@ -147,6 +148,38 @@ bool sptps_send_record(sptps_t *s, uint8_t type, const void *data, uint16_t len) return send_record_priv(s, type, data, len); } +// Pass through unencrypted data. +bool sptps_send_unencrypted(sptps_t *s, const void *data, uint16_t len) { + // Sanity checks: application cannot send data before handshake is finished, + // and only non-datagram allowed. + if(!s->outstate) { + return error(s, EINVAL, "Handshake phase not finished yet"); + } + + if(s->datagram) { + return error(s, EINVAL, "Not allowed for datagrams"); + } + + return s->send_data(s->handle, SPTPS_UNENCRYPTED, data, len); +} + +// Expect a given number of unencrypted bytes. +bool sptps_expect_unencrypted(sptps_t *s, uint16_t len) { + // Sanity checks: application cannot send data before handshake is finished, + // and only non-datagram allowed. + if(!s->instate) { + return error(s, EINVAL, "Handshake phase not finished yet"); + } + + if(s->datagram) { + return error(s, EINVAL, "Not allowed for datagrams"); + } + + s->reclen = len; + s->passthrough = true; + return true; +} + // Send a Key EXchange record, containing a random nonce and an ECDHE public key. static bool send_kex(sptps_t *s) { size_t keylen = ECDH_SIZE; @@ -564,6 +597,46 @@ bool sptps_receive_data(sptps_t *s, const void *data, size_t len) { const char *ptr = data; while(len) { + if(s->passthrough) { + if(!s->buflen && s->reclen <= len) { + len -= s->reclen; + ptr += s->reclen; + + s->reclen = 0; + s->passthrough = false; + + if(!s->receive_record(s->handle, SPTPS_UNENCRYPTED, data, s->reclen)) { + return false; + } + + continue; + } + + size_t toread = s->reclen - s->buflen; + if (toread >= len) { + toread = len; + } + + memcpy(s->inbuf + s->buflen, ptr, toread); + s->buflen += toread; + len -= toread; + ptr += toread; + + if(s->buflen < s->reclen) { + return; + } + + s->reclen = 0; + s->passthrough = false; + + if(!s->receive_record(s->handle, SPTPS_UNENCRYPTED, data, s->reclen)) { + return false; + } + + s->buflen = 0; + continue; + } + // First read the 2 length bytes. if(s->buflen < 2) { size_t toread = 2 - s->buflen; diff --git a/src/sptps.h b/src/sptps.h index 11c0d2e6..ba37733e 100644 --- a/src/sptps.h +++ b/src/sptps.h @@ -32,6 +32,7 @@ #define SPTPS_HANDSHAKE 128 // Key exchange and authentication #define SPTPS_ALERT 129 // Warning or error messages #define SPTPS_CLOSE 130 // Application closed the connection +#define SPTPS_UNENCRYPTED 131 // Unencrypted data // Key exchange states #define SPTPS_KEX 1 // Waiting for the first Key EXchange record @@ -45,13 +46,16 @@ typedef bool (*receive_record_t)(void *handle, uint8_t type, const void *data, u typedef struct sptps { bool initiator; bool datagram; + + bool instate; + bool passthrough; + int state; char *inbuf; size_t buflen; uint16_t reclen; - bool instate; chacha_poly1305_ctx_t *incipher; uint32_t inseqno; uint32_t received; @@ -84,6 +88,8 @@ extern void (*sptps_log)(sptps_t *s, int s_errno, const char *format, va_list ap extern bool sptps_start(sptps_t *s, void *handle, bool initiator, bool datagram, ecdsa_t *mykey, ecdsa_t *hiskey, const char *label, size_t labellen, send_data_t send_data, receive_record_t receive_record); extern bool sptps_stop(sptps_t *s); extern bool sptps_send_record(sptps_t *s, uint8_t type, const void *data, uint16_t len); +extern bool sptps_send_unencrypted(sptps_t *s, const void *data, uint16_t len); +extern bool sptps_expect_unencrypted(sptps_t *s, uint16_t len); extern bool sptps_receive_data(sptps_t *s, const void *data, size_t len); extern bool sptps_force_kex(sptps_t *s); extern bool sptps_verify_datagram(sptps_t *s, const void *data, size_t len); -- 2.39.2