From 35399784b695c9ac692beba7be7930ee9f24412f Mon Sep 17 00:00:00 2001 From: Guus Sliepen Date: Mon, 22 Dec 2003 11:04:17 +0000 Subject: [PATCH] Improvements for PMTU discovery and IPv4 packet fragmentation. --- src/graph.c | 10 +++++-- src/net_packet.c | 21 +++++++++----- src/net_socket.c | 18 ++++-------- src/node.c | 10 +++++-- src/node.h | 5 ++-- src/protocol_auth.c | 12 ++++++-- src/route.c | 69 +++++++++++++++++++++++++++++++++++++++++---- 7 files changed, 109 insertions(+), 36 deletions(-) diff --git a/src/graph.c b/src/graph.c index 757210cc..3ed1d721 100644 --- a/src/graph.c +++ b/src/graph.c @@ -17,7 +17,7 @@ along with this program; if not, write to the Free Software Foundation, Inc., 675 Mass Ave, Cambridge, MA 02139, USA. - $Id: graph.c,v 1.1.2.33 2003/12/20 21:25:17 guus Exp $ + $Id: graph.c,v 1.1.2.34 2003/12/22 11:04:16 guus Exp $ */ /* We need to generate two trees from the graph: @@ -231,9 +231,9 @@ void sssp_bfs(void) avl_insert_node(node_udp_tree, node); if(e->to->options & OPTION_PMTU_DISCOVERY) { - e->to->mtu = MTU; e->to->mtuprobes = 0; - e->to->probedmtu = 0; + e->to->minmtu = 0; + e->to->maxmtu = MTU; if(e->to->status.validkey) send_mtu_probe(e->to); } @@ -270,6 +270,10 @@ void sssp_bfs(void) n->status.validkey = false; n->status.waitingforkey = false; + n->maxmtu = MTU; + n->minmtu = 0; + n->mtuprobes = 0; + asprintf(&envp[0], "NETNAME=%s", netname ? : ""); asprintf(&envp[1], "DEVICE=%s", device ? : ""); asprintf(&envp[2], "INTERFACE=%s", iface ? : ""); diff --git a/src/net_packet.c b/src/net_packet.c index d2e9aa81..8b49b25a 100644 --- a/src/net_packet.c +++ b/src/net_packet.c @@ -17,7 +17,7 @@ along with this program; if not, write to the Free Software Foundation, Inc., 675 Mass Ave, Cambridge, MA 02139, USA. - $Id: net_packet.c,v 1.1.2.46 2003/12/20 21:09:33 guus Exp $ + $Id: net_packet.c,v 1.1.2.47 2003/12/22 11:04:16 guus Exp $ */ #include "system.h" @@ -47,6 +47,10 @@ #include "utils.h" #include "xalloc.h" +#ifdef WSAEMSGSIZE +#define EMSGSIZE WSAEMSGSIZE +#endif + int keylifetime = 0; int keyexpires = 0; EVP_CIPHER_CTX packet_ctx; @@ -64,20 +68,21 @@ void send_mtu_probe(node_t *n) cp(); n->mtuprobes++; + n->mtuevent = NULL; - if(n->mtuprobes >= 10 && !n->probedmtu) { + if(n->mtuprobes >= 10 && !n->minmtu) { ifdebug(TRAFFIC) logger(LOG_INFO, _("No response to MTU probes from %s (%s)"), n->name, n->hostname); return; } for(i = 0; i < 3; i++) { - if(n->mtuprobes >= 30 || n->probedmtu >= n->mtu) { - n->mtu = n->probedmtu; + if(n->mtuprobes >= 30 || n->minmtu >= n->maxmtu) { + n->mtu = n->minmtu; ifdebug(TRAFFIC) logger(LOG_INFO, _("Fixing MTU of %s (%s) to %d after %d probes"), n->name, n->hostname, n->mtu, n->mtuprobes); return; } - len = n->probedmtu + 1 + random() % (n->mtu - n->probedmtu); + len = n->minmtu + 1 + random() % (n->maxmtu - n->minmtu); if(len < 64) len = 64; @@ -104,8 +109,8 @@ void mtu_probe_h(node_t *n, vpn_packet_t *packet) { packet->data[0] = 1; send_packet(n, packet); } else { - if(n->probedmtu < packet->len) - n->probedmtu = packet->len; + if(n->minmtu < packet->len) + n->minmtu = packet->len; } } @@ -386,6 +391,8 @@ static void send_udppacket(node_t *n, vpn_packet_t *inpkt) if((sendto(listen_socket[sock].udp, (char *) &inpkt->seqno, inpkt->len, 0, &(n->address.sa), SALEN(n->address.sa))) < 0) { logger(LOG_ERR, _("Error sending packet to %s (%s): %s"), n->name, n->hostname, strerror(errno)); if(errno == EMSGSIZE) { + if(n->maxmtu >= origlen) + n->maxmtu = origlen - 1; if(n->mtu >= origlen) n->mtu = origlen - 1; } diff --git a/src/net_socket.c b/src/net_socket.c index b90dcf54..3d1be21e 100644 --- a/src/net_socket.c +++ b/src/net_socket.c @@ -17,7 +17,7 @@ along with this program; if not, write to the Free Software Foundation, Inc., 675 Mass Ave, Cambridge, MA 02139, USA. - $Id: net_socket.c,v 1.1.2.37 2003/12/20 21:09:33 guus Exp $ + $Id: net_socket.c,v 1.1.2.38 2003/12/22 11:04:16 guus Exp $ */ #include "system.h" @@ -163,13 +163,9 @@ int setup_vpn_in_socket(const sockaddr_t *sa) { bool choice; - if(sa->sa.sa_family == AF_INET && get_config_bool(lookup_config(myself->connection->config_tree, "PMTUDiscovery"), &choice) && choice) { + if(get_config_bool(lookup_config(myself->connection->config_tree, "PMTUDiscovery"), &choice) && choice) { option = IP_PMTUDISC_DO; - if(setsockopt(nfd, SOL_IP, IP_MTU_DISCOVER, &option, sizeof(option))) { - closesocket(nfd); - logger(LOG_ERR, _("Can't set PMTU discovery mode: %s"), strerror(errno)); - return -1; - } + setsockopt(nfd, SOL_IP, IP_MTU_DISCOVER, &option, sizeof(option)); } } #endif @@ -178,13 +174,9 @@ int setup_vpn_in_socket(const sockaddr_t *sa) { bool choice; - if(sa->sa.sa_family == AF_INET6 && get_config_bool(lookup_config(myself->connection->config_tree, "PMTUDiscovery"), &choice) && choice) { + if(get_config_bool(lookup_config(myself->connection->config_tree, "PMTUDiscovery"), &choice) && choice) { option = IPV6_PMTUDISC_DO; - if(setsockopt(nfd, SOL_IPV6, IPV6_MTU_DISCOVER, &option, sizeof(option))) { - closesocket(nfd); - logger(LOG_ERR, _("Can't set PMTU discovery mode: %s"), strerror(errno)); - return -1; - } + setsockopt(nfd, SOL_IPV6, IPV6_MTU_DISCOVER, &option, sizeof(option)); } } #endif diff --git a/src/node.c b/src/node.c index 4b21d5ba..35199161 100644 --- a/src/node.c +++ b/src/node.c @@ -17,7 +17,7 @@ along with this program; if not, write to the Free Software Foundation, Inc., 675 Mass Ave, Cambridge, MA 02139, USA. - $Id: node.c,v 1.1.2.30 2003/12/20 21:25:17 guus Exp $ + $Id: node.c,v 1.1.2.31 2003/12/22 11:04:16 guus Exp $ */ #include "system.h" @@ -81,6 +81,7 @@ node_t *new_node(void) n->queue = list_alloc((list_action_t) free); EVP_CIPHER_CTX_init(&n->packet_ctx); n->mtu = MTU; + n->maxmtu = MTU; return n; } @@ -110,6 +111,9 @@ void free_node(node_t *n) sockaddrfree(&n->address); EVP_CIPHER_CTX_cleanup(&n->packet_ctx); + + if(n->mtuevent) + event_del(n->mtuevent); free(n); } @@ -180,11 +184,11 @@ void dump_nodes(void) for(node = node_tree->head; node; node = node->next) { n = node->data; - logger(LOG_DEBUG, _(" %s at %s cipher %d digest %d maclength %d compression %d options %lx status %04x nexthop %s via %s probedmtu %d"), + logger(LOG_DEBUG, _(" %s at %s cipher %d digest %d maclength %d compression %d options %lx status %04x nexthop %s via %s pmtu %d (min %d max %d)"), n->name, n->hostname, n->cipher ? n->cipher->nid : 0, n->digest ? n->digest->type : 0, n->maclength, n->compression, n->options, *(uint32_t *)&n->status, n->nexthop ? n->nexthop->name : "-", - n->via ? n->via->name : "-", n->probedmtu); + n->via ? n->via->name : "-", n->mtu, n->minmtu, n->maxmtu); } logger(LOG_DEBUG, _("End of nodes.")); diff --git a/src/node.h b/src/node.h index 7ce17ebf..dd9c7a12 100644 --- a/src/node.h +++ b/src/node.h @@ -17,7 +17,7 @@ along with this program; if not, write to the Free Software Foundation, Inc., 675 Mass Ave, Cambridge, MA 02139, USA. - $Id: node.h,v 1.1.2.30 2003/12/20 19:47:52 guus Exp $ + $Id: node.h,v 1.1.2.31 2003/12/22 11:04:16 guus Exp $ */ #ifndef __TINC_NODE_H__ @@ -74,7 +74,8 @@ typedef struct node_t { unsigned char late[16]; /* Bitfield marking late packets */ length_t mtu; /* Maximum size of packets to send to this node */ - length_t probedmtu; /* Probed MTU */ + length_t minmtu; /* Probed minimum MTU */ + length_t maxmtu; /* Probed maximum MTU */ int mtuprobes; /* Number of probes */ event_t *mtuevent; /* Probe event */ } node_t; diff --git a/src/protocol_auth.c b/src/protocol_auth.c index 94e602f7..77561b81 100644 --- a/src/protocol_auth.c +++ b/src/protocol_auth.c @@ -17,7 +17,7 @@ along with this program; if not, write to the Free Software Foundation, Inc., 675 Mass Ave, Cambridge, MA 02139, USA. - $Id: protocol_auth.c,v 1.1.4.33 2003/12/20 21:25:17 guus Exp $ + $Id: protocol_auth.c,v 1.1.4.34 2003/12/22 11:04:16 guus Exp $ */ #include "system.h" @@ -479,6 +479,8 @@ bool send_ack(connection_t *c) if((get_config_bool(lookup_config(c->config_tree, "PMTUDiscovery"), &choice) && choice) || myself->options & OPTION_PMTU_DISCOVERY) c->options |= OPTION_PMTU_DISCOVERY; + get_config_int(lookup_config(c->config_tree, "Weight"), &c->estimated_weight); + return send_request(c, "%d %s %d %lx", ACK, myport, c->estimated_weight, c->options); } @@ -519,7 +521,7 @@ bool ack_h(connection_t *c) { char hisport[MAX_STRING_SIZE]; char *hisaddress, *dummy; - int weight; + int weight, mtu; long int options; node_t *n; @@ -554,6 +556,12 @@ bool ack_h(connection_t *c) c->node = n; c->options |= options; + if(get_config_int(lookup_config(c->config_tree, "PMTU"), &mtu) && mtu < n->mtu) + n->mtu = mtu; + + if(get_config_int(lookup_config(myself->connection->config_tree, "PMTU"), &mtu) && mtu < n->mtu) + n->mtu = mtu; + /* Activate this connection */ c->allow_request = ALL; diff --git a/src/route.c b/src/route.c index 367926ea..48ba0e84 100644 --- a/src/route.c +++ b/src/route.c @@ -17,7 +17,7 @@ along with this program; if not, write to the Free Software Foundation, Inc., 675 Mass Ave, Cambridge, MA 02139, USA. - $Id: route.c,v 1.1.2.73 2003/12/20 21:25:17 guus Exp $ + $Id: route.c,v 1.1.2.74 2003/12/22 11:04:17 guus Exp $ */ #include "system.h" @@ -276,6 +276,58 @@ static void route_ipv4_unreachable(node_t *source, vpn_packet_t *packet, uint8_t send_packet(source, packet); } +/* RFC 791 */ + +static __inline__ void fragment_ipv4_packet(node_t *dest, vpn_packet_t *packet) { + struct ip ip; + vpn_packet_t fragment; + int len, maxlen, todo; + uint8_t *offset; + uint16_t ip_off, origf; + + cp(); + + memcpy(&ip, packet->data + ether_size, ip_size); + fragment.priority = packet->priority; + + if(ip.ip_hl != ip_size / 4) + return; + + todo = ntohs(ip.ip_len) - ip_size; + + if(ether_size + ip_size + todo != packet->len) { + ifdebug(TRAFFIC) logger(LOG_WARNING, _("Length of packet (%d) doesn't match length in IPv4 header (%d)"), packet->len, ether_size + ip_size + todo); + return; + } + + ifdebug(TRAFFIC) logger(LOG_INFO, _("Fragmenting packet of %d bytes to %s (%s)"), packet->len, dest->name, dest->hostname); + + offset = packet->data + ether_size + ip_size; + maxlen = (dest->mtu - ether_size - ip_size) & ~0x7; + ip_off = ntohs(ip.ip_off); + origf = ip_off & ~IP_OFFMASK; + ip_off &= IP_OFFMASK; + + while(todo) { + len = todo > maxlen ? maxlen : todo; + memcpy(fragment.data + ether_size + ip_size, offset, len); + todo -= len; + offset += len; + + ip.ip_len = htons(ip_size + len); + ip.ip_off = htons(ip_off | origf | (todo ? IP_MF : 0)); + ip.ip_sum = 0; + ip.ip_sum = inet_checksum(&ip, ip_size, ~0); + memcpy(fragment.data, packet->data, ether_size); + memcpy(fragment.data + ether_size, &ip, ip_size); + fragment.len = ether_size + ip_size + len; + + send_packet(dest, &fragment); + + ip_off += len / 8; + } +} + static __inline__ void route_ipv4_unicast(node_t *source, vpn_packet_t *packet) { subnet_t *subnet; @@ -304,16 +356,21 @@ static __inline__ void route_ipv4_unicast(node_t *source, vpn_packet_t *packet) if(!subnet->owner->status.reachable) route_ipv4_unreachable(source, packet, ICMP_DEST_UNREACH, ICMP_NET_UNREACH); + if(priorityinheritance) + packet->priority = packet->data[15]; + if(subnet->owner->options & OPTION_PMTU_DISCOVERY && packet->len > subnet->owner->mtu && subnet->owner != myself) { ifdebug(TRAFFIC) logger(LOG_INFO, _("Packet for %s (%s) length %d larger than MTU %d"), subnet->owner->name, subnet->owner->hostname, packet->len, subnet->owner->mtu); - packet->len = subnet->owner->mtu; - route_ipv4_unreachable(source, packet, ICMP_DEST_UNREACH, ICMP_FRAG_NEEDED); + if(packet->data[20] & 0x40) { + packet->len = subnet->owner->mtu; + route_ipv4_unreachable(source, packet, ICMP_DEST_UNREACH, ICMP_FRAG_NEEDED); + } else { + fragment_ipv4_packet(subnet->owner, packet); + } + return; } - if(priorityinheritance) - packet->priority = packet->data[15]; - send_packet(subnet->owner, packet); } -- 2.39.5