Improvements for PMTU discovery and IPv4 packet fragmentation.
authorGuus Sliepen <guus@tinc-vpn.org>
Mon, 22 Dec 2003 11:04:17 +0000 (11:04 +0000)
committerGuus Sliepen <guus@tinc-vpn.org>
Mon, 22 Dec 2003 11:04:17 +0000 (11:04 +0000)
src/graph.c
src/net_packet.c
src/net_socket.c
src/node.c
src/node.h
src/protocol_auth.c
src/route.c

index 757210cc9002248bc7d8c8cd247d7d826fb69f0e..3ed1d7213b3c7d35b3373eecdeaee9d3c03dbc55 100644 (file)
@@ -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 ? : "");
index d2e9aa816c846a589fa575d1bf996a238b8b0623..8b49b25a00cd60af832ef7c922aa9f6150ffd625 100644 (file)
@@ -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"
 #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;
                }
index b90dcf540b08f604b4862061b8c6ffb531fd47af..3d1be21ec84113319739dc5c9efe76489a0c6446 100644 (file)
@@ -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
index 4b21d5baf99e80eed07dd3692934bf796b83df34..351991611b2cd2a3d692213b6866cb50df7c4a25 100644 (file)
@@ -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."));
index 7ce17ebf09edf5abc6bb56214d1c0036c612dd0f..dd9c7a12f1ace0858b1a24c28b91d3599fe8dd17 100644 (file)
@@ -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;
index 94e602f74c9c5a7dcc57cc5ac1b69319a9b37701..77561b81b2b58688dc95771ad58c9339d0ba3a36 100644 (file)
@@ -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;
index 367926ea035fd0b102cd4b83baf59c85201438df..48ba0e84f9cf138aede56137b1db3a8a2fe28626 100644 (file)
@@ -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);
 }