/*
net_packet.c -- Handles in- and outgoing VPN packets
Copyright (C) 1998-2005 Ivo Timmermans,
- 2000-2014 Guus Sliepen <guus@tinc-vpn.org>
+ 2000-2016 Guus Sliepen <guus@tinc-vpn.org>
2010 Timothy Redaelli <timothy@redaelli.eu>
2010 Brandon Black <blblack@gmail.com>
static void send_udppacket(node_t *, vpn_packet_t *);
-unsigned replaywin = 16;
+unsigned replaywin = 32;
bool localdiscovery = true;
bool udp_discovery = true;
int udp_discovery_keepalive_interval = 10;
if(!n->status.validkey_in || !digest_active(n->indigest) || inpkt->len < sizeof(seqno_t) + digest_length(n->indigest))
return false;
- return digest_verify(n->indigest, SEQNO(inpkt), inpkt->len - digest_length(n->indigest), DATA(inpkt) + inpkt->len - digest_length(n->indigest));
+ return digest_verify(n->indigest, inpkt->data, inpkt->len - digest_length(n->indigest), inpkt->data + inpkt->len - digest_length(n->indigest));
#endif
}
}
return false;
}
- inpkt->offset += 2 * sizeof(node_id_t);
n->status.udppacket = true;
- bool result = sptps_receive_data(&n->sptps, DATA(inpkt), inpkt->len - 2 * sizeof(node_id_t));
+ bool result = sptps_receive_data(&n->sptps, DATA(inpkt), inpkt->len);
n->status.udppacket = false;
if(!result) {
- logger(DEBUG_TRAFFIC, LOG_ERR, "Got bad packet from %s (%s)", n->name, n->hostname);
+ /* Uh-oh. It might be that the tunnel is stuck in some corrupted state,
+ so let's restart SPTPS in case that helps. But don't do that too often
+ to prevent storms, and because that would make life a little too easy
+ for external attackers trying to DoS us. */
+ if(n->last_req_key < now.tv_sec - 10) {
+ logger(DEBUG_PROTOCOL, LOG_ERR, "Failed to decode raw TCP packet from %s (%s), restarting SPTPS", n->name, n->hostname);
+ send_req_key(n);
+ }
return false;
}
return true;
receive_packet(c->node, &outpkt);
}
+bool receive_tcppacket_sptps(connection_t *c, const char *data, int len) {
+ if (len < sizeof(node_id_t) + sizeof(node_id_t)) {
+ logger(DEBUG_ALWAYS, LOG_ERR, "Got too short TCP SPTPS packet from %s (%s)", c->name, c->hostname);
+ return false;
+ }
+
+ node_t *to = lookup_node_id((node_id_t *)data);
+ data += sizeof(node_id_t); len -= sizeof(node_id_t);
+ if(!to) {
+ logger(DEBUG_PROTOCOL, LOG_ERR, "Got TCP SPTPS packet from %s (%s) with unknown destination ID", c->name, c->hostname);
+ return true;
+ }
+
+ node_t *from = lookup_node_id((node_id_t *)data);
+ data += sizeof(node_id_t); len -= sizeof(node_id_t);
+ if(!from) {
+ logger(DEBUG_PROTOCOL, LOG_ERR, "Got TCP SPTPS packet from %s (%s) with unknown source ID", c->name, c->hostname);
+ return true;
+ }
+
+ if(!to->status.reachable) {
+ /* This can happen in the form of a race condition
+ if the node just became unreachable. */
+ logger(DEBUG_TRAFFIC, LOG_WARNING, "Cannot relay TCP packet from %s (%s) because the destination, %s (%s), is unreachable", from->name, from->hostname, to->name, to->hostname);
+ return true;
+ }
+
+ /* Help the sender reach us over UDP.
+ Note that we only do this if we're the destination or the static relay;
+ otherwise every hop would initiate its own UDP info message, resulting in elevated chatter. */
+ if(to->via == myself)
+ send_udp_info(myself, from);
+
+ /* If we're not the final recipient, relay the packet. */
+
+ if(to != myself) {
+ send_sptps_data(to, from, 0, data, len);
+ try_tx(to, true);
+ return true;
+ }
+
+ /* The packet is for us */
+
+ if(!sptps_receive_data(&from->sptps, data, len)) {
+ /* Uh-oh. It might be that the tunnel is stuck in some corrupted state,
+ so let's restart SPTPS in case that helps. But don't do that too often
+ to prevent storms. */
+ if(from->last_req_key < now.tv_sec - 10) {
+ logger(DEBUG_PROTOCOL, LOG_ERR, "Failed to decode raw TCP packet from %s (%s), restarting SPTPS", from->name, from->hostname);
+ send_req_key(from);
+ }
+ return true;
+ }
+
+ send_mtu_info(myself, from, MTU);
+ return true;
+}
+
static void send_sptps_packet(node_t *n, vpn_packet_t *origpkt) {
if(!n->status.validkey && !n->connection)
return;
uint8_t type = 0;
int offset = 0;
- if(!(DATA(origpkt)[12] | DATA(origpkt)[13])) {
+ if((!(DATA(origpkt)[12] | DATA(origpkt)[13])) && (n->sptps.outstate)) {
sptps_send_record(&n->sptps, PKT_PROBE, (char *)DATA(origpkt), origpkt->len);
return;
}
vpn_packet_t *outpkt;
int origlen = origpkt->len;
size_t outlen;
-#if defined(SOL_IP) && defined(IP_TOS)
- static int priority = 0;
int origpriority = origpkt->priority;
-#endif
pkt1.offset = DEFAULT_PACKET_OFFSET;
pkt2.offset = DEFAULT_PACKET_OFFSET;
if(!sa)
choose_udp_address(n, &sa, &sock);
-#if defined(SOL_IP) && defined(IP_TOS)
- if(priorityinheritance && origpriority != priority
- && listen_socket[n->sock].sa.sa.sa_family == AF_INET) {
- priority = origpriority;
- logger(DEBUG_TRAFFIC, LOG_DEBUG, "Setting outgoing packet priority to %d", priority);
- if(setsockopt(listen_socket[n->sock].udp.fd, SOL_IP, IP_TOS, &priority, sizeof(priority))) /* SO_PRIORITY doesn't seem to work */
- logger(DEBUG_ALWAYS, LOG_ERR, "System call `%s' failed: %s", "setsockopt", sockstrerror(sockerrno));
- }
+ if(priorityinheritance && origpriority != listen_socket[sock].priority) {
+ listen_socket[sock].priority = origpriority;
+ switch(sa->sa.sa_family) {
+#if defined(IPPROTO_IP) && defined(IP_TOS)
+ case AF_INET:
+ logger(DEBUG_TRAFFIC, LOG_DEBUG, "Setting IPv4 outgoing packet priority to %d", origpriority);
+ if(setsockopt(listen_socket[sock].udp.fd, IPPROTO_IP, IP_TOS, (void *)&origpriority, sizeof origpriority)) /* SO_PRIORITY doesn't seem to work */
+ logger(DEBUG_ALWAYS, LOG_ERR, "System call `%s' failed: %s", "setsockopt", sockstrerror(sockerrno));
+ break;
+#endif
+#if defined(IPPROTO_IPV6) & defined(IPV6_TCLASS)
+ case AF_INET6:
+ logger(DEBUG_TRAFFIC, LOG_DEBUG, "Setting IPv6 outgoing packet priority to %d", origpriority);
+ if(setsockopt(listen_socket[sock].udp.fd, IPPROTO_IPV6, IPV6_TCLASS, (void *)&origpriority, sizeof origpriority)) /* SO_PRIORITY doesn't seem to work */
+ logger(DEBUG_ALWAYS, LOG_ERR, "System call `%s' failed: %s", "setsockopt", sockstrerror(sockerrno));
+ break;
#endif
+ default:
+ break;
+ }
+ }
- if(sendto(listen_socket[sock].udp.fd, SEQNO(inpkt), inpkt->len, 0, &sa->sa, SALEN(sa->sa)) < 0 && !sockwouldblock(sockerrno)) {
+ if(sendto(listen_socket[sock].udp.fd, (void *)SEQNO(inpkt), inpkt->len, 0, &sa->sa, SALEN(sa->sa)) < 0 && !sockwouldblock(sockerrno)) {
if(sockmsgsize(sockerrno)) {
if(n->maxmtu >= origlen)
n->maxmtu = origlen - 1;
#endif
}
-static bool send_sptps_data_priv(node_t *to, node_t *from, int type, const void *data, size_t len) {
+bool send_sptps_data(node_t *to, node_t *from, int type, const void *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;
bool tcponly = (myself->options | relay->options) & OPTION_TCPONLY;
- /* 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. */
+ /* 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. */
if(type == SPTPS_HANDSHAKE || tcponly || (!direct && !relay_supported) || (type != PKT_PROBE && (len - SPTPS_DATAGRAM_OVERHEAD) > relay->minmtu)) {
+ if(type != SPTPS_HANDSHAKE && (to->nexthop->connection->options >> 24) >= 7) {
+ char buf[len + sizeof to->id + sizeof from->id]; char* buf_ptr = buf;
+ 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;
+ memcpy(buf_ptr, data, len);
+ logger(DEBUG_TRAFFIC, LOG_INFO, "Sending packet from %s (%s) to %s (%s) via %s (%s) (TCP)", from->name, from->hostname, to->name, to->hostname, to->nexthop->name, to->nexthop->hostname);
+ return send_sptps_tcppacket(to->nexthop->connection, buf, sizeof buf);
+ }
+
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(from == myself && !to->status.validkey) {
+ /* If this is a handshake packet, use ANS_KEY instead of REQ_KEY, for two reasons:
+ - We don't want intermediate nodes to switch to UDP to relay these packets;
+ - ANS_KEY allows us to learn the reflexive UDP address. */
+ if(type == SPTPS_HANDSHAKE) {
to->incompression = myself->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, from->name, to->name, REQ_SPTPS, buf);
+ return send_request(to->nexthop->connection, "%d %s %s %d %s", REQ_KEY, from->name, to->name, SPTPS_PACKET, buf);
}
}
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);
+ logger(DEBUG_TRAFFIC, LOG_INFO, "Sending packet from %s (%s) to %s (%s) via %s (%s) (UDP)", 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
return true;
}
-bool send_sptps_data(void *handle, uint8_t type, const void *data, size_t len) {
- return send_sptps_data_priv(handle, myself, type, data, len);
-}
-
bool receive_sptps_record(void *handle, uint8_t type, const void *data, uint16_t len) {
node_t *from = handle;
node_t *via = (n->via == myself) ? n->nexthop : n->via;
- /* If the static relay doesn't support SPTPS, everything goes via TCP anyway. */
+ /* If we do have a static relay, try everything with that one instead, if it supports relaying. */
- if((via->options >> 24) < 4)
- return;
-
- /* If we do have a static relay, try everything with that one instead. */
-
- if(via != n)
- return try_tx_sptps(via, mtu);
+ if(via != n) {
+ if((via->options >> 24) < 4)
+ return;
+ return try_tx(via, mtu);
+ }
/* Otherwise, try to establish UDP connectivity. */
while we try to establish direct connectivity. */
if(!n->status.udp_confirmed && n != n->nexthop && (n->nexthop->options >> 24) >= 4)
- try_tx_sptps(n->nexthop, mtu);
+ try_tx(n->nexthop, mtu);
}
static void try_tx_legacy(node_t *n, bool mtu) {
}
void try_tx(node_t *n, bool mtu) {
+ if(!n->status.reachable)
+ return;
if(n->status.sptps)
try_tx_sptps(n, mtu);
else
// If it's for myself, write it to the tun/tap device.
if(n == myself) {
- if(overwrite_mac)
+ if(overwrite_mac) {
memcpy(DATA(packet), mymac.x, ETH_ALEN);
+ // Use an arbitrary fake source address.
+ memcpy(DATA(packet) + ETH_ALEN, DATA(packet), ETH_ALEN);
+ DATA(packet)[ETH_ALEN * 2 - 1] ^= 0xFF;
+ }
n->out_packets++;
n->out_bytes += packet->len;
devops.write(packet);
if(n->status.sptps) {
send_sptps_packet(n, packet);
- try_tx_sptps(n, true);
+ try_tx(n, true);
return;
}
}
send_udppacket(via, packet);
- try_tx_legacy(via, true);
+ try_tx(via, true);
}
void broadcast_packet(const node_t *from, vpn_packet_t *packet) {
if(!n->status.reachable || n == myself)
continue;
- if((n->status.sptps && !n->sptps.instate) || !n->status.validkey_in)
+ if(!n->status.validkey_in && !(n->status.sptps && n->sptps.instate))
continue;
bool soft = false;
return match;
}
-void handle_incoming_vpn_data(void *data, int flags) {
- listen_socket_t *ls = data;
- vpn_packet_t pkt;
+static void handle_incoming_vpn_packet(listen_socket_t *ls, vpn_packet_t *pkt, sockaddr_t *addr) {
char *hostname;
node_id_t nullid = {};
- sockaddr_t addr = {};
- socklen_t addrlen = sizeof addr;
node_t *from, *to;
bool direct = false;
- pkt.offset = 0;
- int len = recvfrom(ls->udp.fd, DATA(&pkt), MAXSIZE, 0, &addr.sa, &addrlen);
-
- if(len <= 0 || len > MAXSIZE) {
- if(!sockwouldblock(sockerrno))
- logger(DEBUG_ALWAYS, LOG_ERR, "Receiving packet failed: %s", sockstrerror(sockerrno));
- return;
- }
-
- pkt.len = len;
-
- sockaddrunmap(&addr); /* Some braindead IPv6 implementations do stupid things. */
+ sockaddrunmap(addr); /* Some braindead IPv6 implementations do stupid things. */
// Try to figure out who sent this packet.
- node_t *n = lookup_node_udp(&addr);
+ node_t *n = lookup_node_udp(addr);
if(n && !n->status.udp_confirmed)
n = NULL; // Don't believe it if we don't have confirmation yet.
if(!n) {
// It might be from a 1.1 node, which might have a source ID in the packet.
- pkt.offset = 2 * sizeof(node_id_t);
- from = lookup_node_id(SRCID(&pkt));
- if(from && !memcmp(DSTID(&pkt), &nullid, sizeof nullid) && from->status.sptps) {
- if(sptps_verify_datagram(&from->sptps, DATA(&pkt), pkt.len - 2 * sizeof(node_id_t)))
+ pkt->offset = 2 * sizeof(node_id_t);
+ from = lookup_node_id(SRCID(pkt));
+ if(from && !memcmp(DSTID(pkt), &nullid, sizeof nullid) && from->status.sptps) {
+ if(sptps_verify_datagram(&from->sptps, DATA(pkt), pkt->len - 2 * sizeof(node_id_t)))
n = from;
else
goto skip_harder;
}
if(!n) {
- pkt.offset = 0;
- n = try_harder(&addr, &pkt);
+ pkt->offset = 0;
+ n = try_harder(addr, pkt);
}
skip_harder:
if(!n) {
if(debug_level >= DEBUG_PROTOCOL) {
- hostname = sockaddr2hostname(&addr);
+ hostname = sockaddr2hostname(addr);
logger(DEBUG_PROTOCOL, LOG_WARNING, "Received UDP packet from unknown source %s", hostname);
free(hostname);
}
return;
}
+ pkt->offset = 0;
+
if(n->status.sptps) {
- pkt.offset = 2 * sizeof(node_id_t);
+ bool relay_enabled = (n->options >> 24) >= 4;
+ if (relay_enabled) {
+ pkt->offset = 2 * sizeof(node_id_t);
+ pkt->len -= pkt->offset;
+ }
- if(!memcmp(DSTID(&pkt), &nullid, sizeof nullid)) {
+ if(!memcmp(DSTID(pkt), &nullid, sizeof nullid) || !relay_enabled) {
direct = true;
from = n;
to = myself;
} else {
- from = lookup_node_id(SRCID(&pkt));
- to = lookup_node_id(DSTID(&pkt));
+ from = lookup_node_id(SRCID(pkt));
+ to = lookup_node_id(DSTID(pkt));
}
if(!from || !to) {
logger(DEBUG_PROTOCOL, LOG_WARNING, "Received UDP packet from %s (%s) with unknown source and/or destination ID", n->name, n->hostname);
return;
}
+ if(!to->status.reachable) {
+ /* This can happen in the form of a race condition
+ if the node just became unreachable. */
+ logger(DEBUG_TRAFFIC, LOG_WARNING, "Cannot relay packet from %s (%s) because the destination, %s (%s), is unreachable", from->name, from->hostname, to->name, to->hostname);
+ return;
+ }
+
+ /* The packet is supposed to come from the originator or its static relay
+ (i.e. with no dynamic relays in between).
+ If it did not, "help" the static relay by sending it UDP info.
+ Note that we only do this if we're the destination or the static relay;
+ otherwise every hop would initiate its own UDP info message, resulting in elevated chatter. */
+
+ if(n != from->via && to->via == myself)
+ send_udp_info(myself, from);
+
+ /* If we're not the final recipient, relay the packet. */
+
if(to != myself) {
- send_sptps_data_priv(to, n, 0, DATA(&pkt), pkt.len - 2 * sizeof(node_id_t));
- try_tx_sptps(n, true);
+ send_sptps_data(to, from, 0, DATA(pkt), pkt->len);
+ try_tx(to, true);
return;
}
} else {
from = n;
}
- pkt.offset = 0;
- if(!receive_udppacket(from, &pkt))
+ if(!receive_udppacket(from, pkt))
return;
n->sock = ls - listen_socket;
- if(direct && sockaddrcmp(&addr, &n->address))
- update_node_udp(n, &addr);
+ if(direct && sockaddrcmp(addr, &n->address))
+ update_node_udp(n, addr);
+
+ /* If the packet went through a relay, help the sender find the appropriate MTU
+ through the relay path. */
+
+ if(!direct)
+ send_mtu_info(myself, n, MTU);
+}
+
+void handle_incoming_vpn_data(void *data, int flags) {
+ listen_socket_t *ls = data;
+
+#ifdef HAVE_RECVMMSG
+#define MAX_MSG 64
+ static int num = MAX_MSG;
+ static vpn_packet_t pkt[MAX_MSG];
+ static sockaddr_t addr[MAX_MSG];
+ static struct mmsghdr msg[MAX_MSG];
+ static struct iovec iov[MAX_MSG];
+
+ for(int i = 0; i < num; i++) {
+ pkt[i].offset = 0;
+
+ iov[i] = (struct iovec){
+ .iov_base = DATA(&pkt[i]),
+ .iov_len = MAXSIZE,
+ };
+
+ msg[i].msg_hdr = (struct msghdr){
+ .msg_name = &addr[i].sa,
+ .msg_namelen = sizeof addr[i],
+ .msg_iov = &iov[i],
+ .msg_iovlen = 1,
+ };
+ }
+
+ num = recvmmsg(ls->udp.fd, msg, MAX_MSG, MSG_DONTWAIT, NULL);
+
+ if(num < 0) {
+ if(!sockwouldblock(sockerrno))
+ logger(DEBUG_ALWAYS, LOG_ERR, "Receiving packet failed: %s", sockstrerror(sockerrno));
+ return;
+ }
+
+ for(int i = 0; i < num; i++) {
+ pkt[i].len = msg[i].msg_len;
+ if(pkt[i].len <= 0 || pkt[i].len > MAXSIZE)
+ continue;
+ handle_incoming_vpn_packet(ls, &pkt[i], &addr[i]);
+ }
+#else
+ vpn_packet_t pkt;
+ sockaddr_t addr = {};
+ socklen_t addrlen = sizeof addr;
+
+ pkt.offset = 0;
+ int len = recvfrom(ls->udp.fd, (void *)DATA(&pkt), MAXSIZE, 0, &addr.sa, &addrlen);
+
+ if(len <= 0 || len > MAXSIZE) {
+ if(!sockwouldblock(sockerrno))
+ logger(DEBUG_ALWAYS, LOG_ERR, "Receiving packet failed: %s", sockstrerror(sockerrno));
+ return;
+ }
+
+ pkt.len = len;
+
+ handle_incoming_vpn_packet(ls, &pkt, &addr);
+#endif
}
void handle_device_data(void *data, int flags) {