From 8dd1c8a020e3babf5054179b0d30e2aa850d2e2b Mon Sep 17 00:00:00 2001 From: Etienne Dechamps Date: Sat, 27 Sep 2014 18:13:33 +0100 Subject: [PATCH] Prepend source node ID information to UDP datagrams. This commit changes the layout of UDP datagrams to include the 6-byte ID (i.e. node name hash) of the node that crafted the packet at the very beginning of the datagram (i.e. before the seqno). Note that this only applies to SPTPS. This is implemented at the lowest layer, i.e. in handle_incoming_vpn_data() and send_sptps_data() functions. Source ID is added and removed there, in such a way that the upper layers are unaware of its presence. This is the first stepping stone towards supporting UDP relaying in SPTPS, by providing information about the original sender in the packet itself. Nevertheless, even without relaying this commit already provides a few benefits such as being able to reliably determine the source node of a packet in the presence of an unknown source IP address, without having to painfully go through all node keys. This makes tinc's behavior much more scalable in this regard. This change does not break anything with regard to the protocol: It preserves compatibility with 1.0 and even with older pre-1.1 releases thanks to a minor protocol version change (17.4). Source ID information won't be included in packets sent to nodes with minor version < 4. One drawback, however, is that this change increases SPTPS datagram overhead by 6 bytes (the size of the source ID itself). --- src/net.h | 5 ++-- src/net_packet.c | 67 +++++++++++++++++++++++++++++++----------------- src/protocol.h | 2 +- 3 files changed, 47 insertions(+), 27 deletions(-) diff --git a/src/net.h b/src/net.h index 8cf48e7d..adb65f30 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 + padding + HMAC + compressor overhead */ -#define MAXSIZE (MTU + 4 + CIPHER_MAX_BLOCK_SIZE + DIGEST_MAX_SIZE + MTU/64 + 20) +/* 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) /* 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 srcid; /* node ID of the original sender */ uint8_t seqno[4]; /* 32 bits sequence number (network byte order of course) */ uint8_t data[MAXSIZE]; } vpn_packet_t; diff --git a/src/net_packet.c b/src/net_packet.c index 67407dcf..6b7c3b94 100644 --- a/src/net_packet.c +++ b/src/net_packet.c @@ -356,7 +356,7 @@ static bool try_mac(node_t *n, const vpn_packet_t *inpkt) { return digest_verify(n->indigest, &inpkt->seqno, inpkt->len - digest_length(n->indigest), (const char *)&inpkt->seqno + inpkt->len - digest_length(n->indigest)); } -static void receive_udppacket(node_t *n, vpn_packet_t *inpkt) { +static bool receive_udppacket(node_t *n, vpn_packet_t *inpkt) { vpn_packet_t pkt1, pkt2; vpn_packet_t *pkt[] = { &pkt1, &pkt2, &pkt1, &pkt2 }; int nextpkt = 0; @@ -370,15 +370,14 @@ static void receive_udppacket(node_t *n, vpn_packet_t *inpkt) { } else { logger(DEBUG_TRAFFIC, LOG_DEBUG, "Got packet from %s (%s) but he hasn't got our key yet", n->name, n->hostname); } - return; + return false; } - sptps_receive_data(&n->sptps, (char *)&inpkt->seqno, inpkt->len); - return; + return sptps_receive_data(&n->sptps, (char *)&inpkt->seqno, inpkt->len); } if(!n->status.validkey) { logger(DEBUG_TRAFFIC, LOG_DEBUG, "Got packet from %s (%s) but he hasn't got our key yet", n->name, n->hostname); - return; + return false; } /* Check packet length */ @@ -386,7 +385,7 @@ static void receive_udppacket(node_t *n, vpn_packet_t *inpkt) { if(inpkt->len < sizeof inpkt->seqno + digest_length(n->indigest)) { logger(DEBUG_TRAFFIC, LOG_DEBUG, "Got too short packet from %s (%s)", n->name, n->hostname); - return; + return false; } /* Check the message authentication code */ @@ -395,7 +394,7 @@ static void receive_udppacket(node_t *n, vpn_packet_t *inpkt) { inpkt->len -= digest_length(n->indigest); if(!digest_verify(n->indigest, &inpkt->seqno, inpkt->len, (const char *)&inpkt->seqno + inpkt->len)) { logger(DEBUG_TRAFFIC, LOG_DEBUG, "Got unauthenticated packet from %s (%s)", n->name, n->hostname); - return; + return false; } } /* Decrypt the packet */ @@ -406,7 +405,7 @@ static void receive_udppacket(node_t *n, vpn_packet_t *inpkt) { if(!cipher_decrypt(n->incipher, &inpkt->seqno, inpkt->len, &outpkt->seqno, &outlen, true)) { logger(DEBUG_TRAFFIC, LOG_DEBUG, "Error decrypting packet from %s (%s)", n->name, n->hostname); - return; + return false; } outpkt->len = outlen; @@ -426,7 +425,7 @@ static void receive_udppacket(node_t *n, vpn_packet_t *inpkt) { if(n->farfuture++ < replaywin >> 2) { logger(DEBUG_ALWAYS, LOG_WARNING, "Packet from %s (%s) is %d seqs in the future, dropped (%u)", n->name, n->hostname, seqno - n->received_seqno - 1, n->farfuture); - return; + return false; } logger(DEBUG_ALWAYS, LOG_WARNING, "Lost %d packets from %s (%s)", seqno - n->received_seqno - 1, n->name, n->hostname); @@ -435,7 +434,7 @@ static void receive_udppacket(node_t *n, vpn_packet_t *inpkt) { if((n->received_seqno >= replaywin * 8 && seqno <= n->received_seqno - replaywin * 8) || !(n->late[(seqno / 8) % replaywin] & (1 << seqno % 8))) { logger(DEBUG_ALWAYS, LOG_WARNING, "Got late or replayed packet from %s (%s), seqno %d, last received %d", n->name, n->hostname, seqno, n->received_seqno); - return; + return false; } } else { for(int i = n->received_seqno + 1; i < seqno; i++) @@ -465,7 +464,7 @@ static void receive_udppacket(node_t *n, vpn_packet_t *inpkt) { if((outpkt->len = uncompress_packet(outpkt->data, inpkt->data, inpkt->len, n->incompression)) < 0) { logger(DEBUG_TRAFFIC, LOG_ERR, "Error while uncompressing packet from %s (%s)", n->name, n->hostname); - return; + return false; } inpkt = outpkt; @@ -479,6 +478,7 @@ static void receive_udppacket(node_t *n, vpn_packet_t *inpkt) { mtu_probe_h(n, inpkt, origlen); else receive_packet(n, inpkt); + return true; } void receive_tcppacket(connection_t *c, const char *buffer, int len) { @@ -779,7 +779,17 @@ bool send_sptps_data(void *handle, uint8_t type, const char *data, size_t len) { if(!sa) choose_udp_address(to, &sa, &sock); - if(sendto(listen_socket[sock].udp.fd, data, len, 0, &sa->sa, SALEN(sa->sa)) < 0 && !sockwouldblock(sockerrno)) { + bool add_srcid = (to->options >> 24) >= 4; + size_t overhead = 0; + if (add_srcid) overhead += sizeof myself->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; + } + /* 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; + + 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; @@ -995,10 +1005,10 @@ void handle_incoming_vpn_data(void *data, int flags) { char *hostname; sockaddr_t from = {{0}}; socklen_t fromlen = sizeof from; - node_t *n; + node_t *n = NULL; int len; - len = recvfrom(ls->udp.fd, pkt.seqno, MAXSIZE, 0, &from.sa, &fromlen); + len = recvfrom(ls->udp.fd, &pkt.srcid, MAXSIZE, 0, &from.sa, &fromlen); if(len <= 0 || len > MAXSIZE) { if(!sockwouldblock(sockerrno)) @@ -1010,25 +1020,34 @@ void handle_incoming_vpn_data(void *data, int flags) { sockaddrunmap(&from); /* Some braindead IPv6 implementations do stupid things. */ - n = lookup_node_udp(&from); + if(len >= 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)); + n = lookup_node_udp(&from); + } - if(!n) { + if(!n) n = try_harder(&from, &pkt); - if(n) - update_node_udp(n, &from); - else if(debug_level >= DEBUG_PROTOCOL) { + + if(!n) { + if(debug_level >= DEBUG_PROTOCOL) { hostname = sockaddr2hostname(&from); logger(DEBUG_PROTOCOL, LOG_WARNING, "Received UDP packet from unknown source %s", hostname); free(hostname); - return; } - else - return; + return; } - n->sock = ls - listen_socket; + if(!receive_udppacket(n, &pkt)) + return; - receive_udppacket(n, &pkt); + n->sock = ls - listen_socket; + if(sockaddrcmp(&from, &n->address)) + update_node_udp(n, &from); } void handle_device_data(void *data, int flags) { diff --git a/src/protocol.h b/src/protocol.h index 1a1fb3f8..080d50c1 100644 --- a/src/protocol.h +++ b/src/protocol.h @@ -26,7 +26,7 @@ /* Protocol version. Different major versions are incompatible. */ #define PROT_MAJOR 17 -#define PROT_MINOR 3 /* Should not exceed 255! */ +#define PROT_MINOR 4 /* Should not exceed 255! */ /* Silly Windows */ -- 2.20.1