diff --git a/src/net.h b/src/net.h index adb65f30..b570d5e1 100644 --- a/src/net.h +++ b/src/net.h @@ -32,8 +32,8 @@ #define MTU 1518 /* 1500 bytes payload + 14 bytes ethernet header + 4 bytes VLAN tag */ #endif -/* MAXSIZE is the maximum size of an encapsulated packet: MTU + seqno + srcid + padding + HMAC + compressor overhead */ -#define MAXSIZE (MTU + 4 + sizeof(node_id_t) + CIPHER_MAX_BLOCK_SIZE + DIGEST_MAX_SIZE + MTU/64 + 20) +/* MAXSIZE is the maximum size of an encapsulated packet: MTU + seqno + srcid + dstid + padding + HMAC + compressor overhead */ +#define MAXSIZE (MTU + 4 + sizeof(node_id_t) + sizeof(node_id_t) + CIPHER_MAX_BLOCK_SIZE + DIGEST_MAX_SIZE + MTU/64 + 20) /* MAXBUFSIZE is the maximum size of a request: enough for a MAXSIZEd packet or a 8192 bits RSA key */ #define MAXBUFSIZE ((MAXSIZE > 2048 ? MAXSIZE : 2048) + 128) @@ -87,6 +87,7 @@ typedef union sockaddr_t { typedef struct vpn_packet_t { length_t len; /* the actual number of bytes in the `data' field */ int priority; /* priority or TOS */ + node_id_t dstid; /* node ID of the final recipient */ node_id_t srcid; /* node ID of the original sender */ uint8_t seqno[4]; /* 32 bits sequence number (network byte order of course) */ uint8_t data[MAXSIZE]; diff --git a/src/net_packet.c b/src/net_packet.c index 6b7c3b94..a1b174e9 100644 --- a/src/net_packet.c +++ b/src/net_packet.c @@ -751,54 +751,62 @@ end: origpkt->len = origlen; } -bool send_sptps_data(void *handle, uint8_t type, const char *data, size_t len) { - node_t *to = handle; +static bool send_sptps_data_priv(node_t *to, node_t *from, int type, const char *data, size_t len) { + node_t *relay = (to->via != myself && (type == PKT_PROBE || (len - SPTPS_DATAGRAM_OVERHEAD) <= to->via->minmtu)) ? to->via : to->nexthop; + bool direct = from == myself && to == relay; + bool relay_supported = (relay->options >> 24) >= 4; - /* Send it via TCP if it is a handshake packet, TCPOnly is in use, or this packet is larger than the MTU. */ + /* Send it via TCP if it is a handshake packet, TCPOnly is in use, this is a relay packet that the other node cannot understand, or this packet is larger than the MTU. + TODO: When relaying, the original sender does not know the end-to-end PMTU (it only knows the PMTU of the first hop). + This can lead to scenarios where large packets are sent over UDP to relay, but then relay has no choice but fall back to TCP. */ - if(type >= SPTPS_HANDSHAKE || ((myself->options | to->options) & OPTION_TCPONLY) || (type != PKT_PROBE && (len - SPTPS_DATAGRAM_OVERHEAD) > to->minmtu)) { + if(type == SPTPS_HANDSHAKE || ((myself->options | relay->options) & OPTION_TCPONLY) || (!direct && !relay_supported) || (type != PKT_PROBE && (len - SPTPS_DATAGRAM_OVERHEAD) > relay->minmtu)) { char buf[len * 4 / 3 + 5]; b64encode(data, buf, len); /* If no valid key is known yet, send the packets using ANS_KEY requests, to ensure we get to learn the reflexive UDP address. */ - if(!to->status.validkey) { + if(from == myself && !to->status.validkey) { to->incompression = myself->incompression; - return send_request(to->nexthop->connection, "%d %s %s %s -1 -1 -1 %d", ANS_KEY, myself->name, to->name, buf, to->incompression); + return send_request(to->nexthop->connection, "%d %s %s %s -1 -1 -1 %d", ANS_KEY, from->name, to->name, buf, to->incompression); } else { - return send_request(to->nexthop->connection, "%d %s %s %d %s", REQ_KEY, myself->name, to->name, REQ_SPTPS, buf); + return send_request(to->nexthop->connection, "%d %s %s %d %s", REQ_KEY, from->name, to->name, REQ_SPTPS, buf); } } - /* Otherwise, send the packet via UDP */ - - const sockaddr_t *sa = NULL; - int sock; - - if(to->status.send_locally) - choose_local_address(to, &sa, &sock); - if(!sa) - choose_udp_address(to, &sa, &sock); - - bool add_srcid = (to->options >> 24) >= 4; size_t overhead = 0; - if (add_srcid) overhead += sizeof myself->id; + if(relay_supported) overhead += sizeof to->id + sizeof from->id; char buf[len + overhead]; char* buf_ptr = buf; - if(add_srcid) { - memcpy(buf_ptr, &myself->id, sizeof myself->id); buf_ptr += sizeof myself->id; + if(relay_supported) { + if(direct) { + /* Inform the recipient that this packet was sent directly. */ + node_id_t nullid = {0}; + memcpy(buf_ptr, &nullid, sizeof nullid); buf_ptr += sizeof nullid; + } else { + memcpy(buf_ptr, &to->id, sizeof to->id); buf_ptr += sizeof to->id; + } + memcpy(buf_ptr, &from->id, sizeof from->id); buf_ptr += sizeof from->id; + } /* TODO: if this copy turns out to be a performance concern, change sptps_send_record() to add some "pre-padding" to the buffer and use that instead */ memcpy(buf_ptr, data, len); buf_ptr += len; + const sockaddr_t *sa = NULL; + int sock; + if(relay->status.send_locally) + choose_local_address(relay, &sa, &sock); + if(!sa) + choose_udp_address(relay, &sa, &sock); + logger(DEBUG_TRAFFIC, LOG_INFO, "Sending packet from %s (%s) to %s (%s) via %s (%s)", from->name, from->hostname, to->name, to->hostname, relay->name, relay->hostname); if(sendto(listen_socket[sock].udp.fd, buf, buf_ptr - buf, 0, &sa->sa, SALEN(sa->sa)) < 0 && !sockwouldblock(sockerrno)) { if(sockmsgsize(sockerrno)) { // Compensate for SPTPS overhead len -= SPTPS_DATAGRAM_OVERHEAD; - if(to->maxmtu >= len) - to->maxmtu = len - 1; - if(to->mtu >= len) - to->mtu = len - 1; + if(relay->maxmtu >= len) + relay->maxmtu = len - 1; + if(relay->mtu >= len) + relay->mtu = len - 1; } else { - logger(DEBUG_TRAFFIC, LOG_WARNING, "Error sending UDP SPTPS packet to %s (%s): %s", to->name, to->hostname, sockstrerror(sockerrno)); + logger(DEBUG_TRAFFIC, LOG_WARNING, "Error sending UDP SPTPS packet to %s (%s): %s", relay->name, relay->hostname, sockstrerror(sockerrno)); return false; } } @@ -806,6 +814,10 @@ bool send_sptps_data(void *handle, uint8_t type, const char *data, size_t len) { return true; } +bool send_sptps_data(void *handle, uint8_t type, const char *data, size_t len) { + return send_sptps_data_priv(handle, myself, type, data, len); +} + bool receive_sptps_record(void *handle, uint8_t type, const char *data, uint16_t len) { node_t *from = handle; @@ -1006,9 +1018,10 @@ void handle_incoming_vpn_data(void *data, int flags) { sockaddr_t from = {{0}}; socklen_t fromlen = sizeof from; node_t *n = NULL; + node_t *to = myself; int len; - len = recvfrom(ls->udp.fd, &pkt.srcid, MAXSIZE, 0, &from.sa, &fromlen); + len = recvfrom(ls->udp.fd, &pkt.dstid, MAXSIZE, 0, &from.sa, &fromlen); if(len <= 0 || len > MAXSIZE) { if(!sockwouldblock(sockerrno)) @@ -1020,13 +1033,42 @@ void handle_incoming_vpn_data(void *data, int flags) { sockaddrunmap(&from); /* Some braindead IPv6 implementations do stupid things. */ - if(len >= sizeof pkt.srcid) + bool direct = false; + if(len >= sizeof pkt.dstid + sizeof pkt.srcid) { n = lookup_node_id(&pkt.srcid); - if(n) - pkt.len -= sizeof pkt.srcid; - else { - /* Most likely an old-style packet without a source ID. */ - memmove(pkt.seqno, &pkt.srcid, sizeof pkt - offsetof(vpn_packet_t, seqno)); + if(n) { + node_id_t nullid = {0}; + if(memcmp(&pkt.dstid, &nullid, sizeof nullid) == 0) { + /* A zero dstid is used to indicate a direct, non-relayed packet. */ + direct = true; + } else { + to = lookup_node_id(&pkt.dstid); + if(!to) { + logger(DEBUG_PROTOCOL, LOG_WARNING, "Received UDP packet presumably sent by %s (%s) but with unknown destination ID", n->name, n->hostname); + return; + } + } + pkt.len -= sizeof pkt.dstid + sizeof pkt.srcid; + } + } + + if(to != myself) { + /* We are being asked to relay this packet. */ + + /* Don't allow random strangers to relay through us. Note that we check for *any* known address since we are not necessarily the first relay. */ + if (!lookup_node_udp(&from)) { + logger(DEBUG_PROTOCOL, LOG_WARNING, "Refusing to relay packet from (presumably) %s (%s) to (presumably) %s (%s) because the packet comes from an unknown address", n->name, n->hostname, to->name, to->hostname); + return; + } + + send_sptps_data_priv(to, n, 0, pkt.seqno, pkt.len); + return; + } + + if(!n) { + /* Most likely an old-style packet without node IDs. */ + direct = true; + memmove(pkt.seqno, &pkt.dstid, sizeof pkt - offsetof(vpn_packet_t, seqno)); n = lookup_node_udp(&from); } @@ -1046,7 +1088,7 @@ void handle_incoming_vpn_data(void *data, int flags) { return; n->sock = ls - listen_socket; - if(sockaddrcmp(&from, &n->address)) + if(direct && sockaddrcmp(&from, &n->address)) update_node_udp(n, &from); } diff --git a/src/protocol_key.c b/src/protocol_key.c index abde7772..a4d97aeb 100644 --- a/src/protocol_key.c +++ b/src/protocol_key.c @@ -255,6 +255,7 @@ bool req_key_h(connection_t *c, const char *request) { return true; } + /* TODO: forwarding SPTPS packets in this way is inefficient because we send them over TCP without checking for UDP connectivity */ send_request(to->nexthop->connection, "%s", request); }