Let tinc figure out the exact MTU of the link.
authorGuus Sliepen <guus@tinc-vpn.org>
Sat, 20 Dec 2003 19:47:53 +0000 (19:47 +0000)
committerGuus Sliepen <guus@tinc-vpn.org>
Sat, 20 Dec 2003 19:47:53 +0000 (19:47 +0000)
12 files changed:
src/connection.h
src/graph.c
src/net.c
src/net.h
src/net_packet.c
src/net_setup.c
src/net_socket.c
src/node.c
src/node.h
src/protocol_auth.c
src/protocol_key.c
src/route.c

index cc6ff71866b94e6aecae8a032d545d77d377c4bb..175bf7ce62dd5ecca06c14bf615b3ea92a60948a 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: connection.h,v 1.1.2.38 2003/11/17 15:30:16 guus Exp $
+    $Id: connection.h,v 1.1.2.39 2003/12/20 19:47:52 guus Exp $
 */
 
 #ifndef __TINC_CONNECTION_H__
@@ -30,6 +30,7 @@
 
 #define OPTION_INDIRECT                0x0001
 #define OPTION_TCPONLY         0x0002
+#define OPTION_DONTFRAGMENT    0x0004
 
 typedef struct connection_status_t {
        int pinged:1;                           /* sent ping */
index cd7fbf361f80657a607c68e2fcddaf33917a198e..d07dd681b7b17e62a0dc971c3093398b479e2ac3 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.30 2003/10/10 16:23:30 guus Exp $
+    $Id: graph.c,v 1.1.2.31 2003/12/20 19:47:52 guus Exp $
 */
 
 /* We need to generate two trees from the graph:
@@ -229,6 +229,12 @@ void sssp_bfs(void)
 
                                        e->to->hostname = sockaddr2hostname(&e->to->address);
                                        avl_insert_node(node_udp_tree, node);
+
+                                       if(e->to->options & OPTION_DONTFRAGMENT) {
+                                               e->to->mtuprobes = 0;
+                                               if(e->to->status.validkey)
+                                                       send_mtu_probe(e->to);
+                                       }
                                }
 
                                node = avl_alloc_node();
index 164497688b70f6b35e76f4d930c89b117e869cd1..a6d2bb7afc30e2425605b8dec1dabd9cabb47a2c 100644 (file)
--- a/src/net.c
+++ b/src/net.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.c,v 1.35.4.202 2003/12/12 19:52:24 guus Exp $
+    $Id: net.c,v 1.35.4.203 2003/12/20 19:47:52 guus Exp $
 */
 
 #include "system.h"
@@ -334,7 +334,8 @@ int main_loop(void)
        while(running) {
                now = time(NULL);
 
-               tv.tv_sec = 1 + (rand() & 7);   /* Approx. 5 seconds, randomized to prevent global synchronisation effects */
+       //      tv.tv_sec = 1 + (rand() & 7);   /* Approx. 5 seconds, randomized to prevent global synchronisation effects */
+               tv.tv_sec = 1;
                tv.tv_usec = 0;
 
                maxfd = build_fdset(&fset);
index cadb76e8296c29aeae32a252310d575a3dffe28b..5b14553856ef52e8ffd890a380cbb6a5078e4036 100644 (file)
--- a/src/net.h
+++ b/src/net.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: net.h,v 1.9.4.72 2003/10/08 12:09:37 guus Exp $
+    $Id: net.h,v 1.9.4.73 2003/12/20 19:47:52 guus Exp $
 */
 
 #ifndef __TINC_NET_H__
@@ -150,6 +150,7 @@ extern int main_loop(void);
 extern void terminate_connection(struct connection_t *, bool);
 extern void flush_queue(struct node_t *);
 extern bool read_rsa_public_key(struct connection_t *);
+extern void send_mtu_probe(struct node_t *);
 
 #ifndef HAVE_MINGW
 #define closesocket(s) close(s)
index af34d0598d54b2d9706e6bff228e0a5c09910157..ac4ad42770061f4a5e331a6f375e5f3cee1f1ba2 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.44 2003/12/12 19:52:25 guus Exp $
+    $Id: net_packet.c,v 1.1.2.45 2003/12/20 19:47:52 guus Exp $
 */
 
 #include "system.h"
@@ -52,9 +52,58 @@ int keyexpires = 0;
 EVP_CIPHER_CTX packet_ctx;
 static char lzo_wrkmem[LZO1X_999_MEM_COMPRESS > LZO1X_1_MEM_COMPRESS ? LZO1X_999_MEM_COMPRESS : LZO1X_1_MEM_COMPRESS];
 
+static void send_udppacket(node_t *, vpn_packet_t *);
 
 #define MAX_SEQNO 1073741824
 
+void send_mtu_probe(node_t *n)
+{
+       vpn_packet_t packet;
+       int len, i;
+       
+       cp();
+
+       n->mtuprobes++;
+
+       for(i = 0; i < 3; i++) {
+               if(n->mtuprobes >= 100 || n->probedmtu >= n->mtu) {
+                       n->mtu = n->probedmtu;
+                       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);
+               if(len < 64)
+                       len = 64;
+               
+               memset(packet.data, 0, 14);
+               RAND_pseudo_bytes(packet.data + 14, len - 14);
+               packet.len = len;
+
+               ifdebug(TRAFFIC) logger(LOG_INFO, _("Sending MTU probe length %d to %s (%s)"), len, n->name, n->hostname);
+
+               send_udppacket(n, &packet);
+       }
+
+       n->mtuevent = xmalloc(sizeof(*n->mtuevent));
+       n->mtuevent->handler = (event_handler_t)send_mtu_probe;
+       n->mtuevent->data = n;
+       n->mtuevent->time = now + 1;
+       event_add(n->mtuevent);
+}
+
+void mtu_probe_h(node_t *n, vpn_packet_t *packet) {
+       ifdebug(TRAFFIC) logger(LOG_INFO, _("Got MTU probe length %d from %s (%s)"), packet->len, n->name, n->hostname);
+
+       if(!packet->data[0]) {
+               packet->data[0] = 1;
+               send_packet(n, packet);
+       } else {
+               if(n->probedmtu < packet->len)
+                       n->probedmtu = packet->len;
+       }
+}
+
 static length_t compress_packet(uint8_t *dest, const uint8_t *source, length_t len, int level)
 {
        if(level == 10) {
@@ -203,7 +252,10 @@ static void receive_udppacket(node_t *n, vpn_packet_t *inpkt)
        if(n->connection)
                n->connection->last_ping_time = now;
 
-       receive_packet(n, inpkt);
+       if(!inpkt->data[12] && !inpkt->data[13])
+               mtu_probe_h(n, inpkt);
+       else
+               receive_packet(n, inpkt);
 }
 
 void receive_tcppacket(connection_t *c, char *buffer, int len)
@@ -328,6 +380,10 @@ 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->mtu >= origlen)
+                               n->mtu = origlen - 1;
+               }
                return;
        }
 
index 2c07ec63ebac40f4141fe85c6ae4906a6169dc5c..e71d4466dddd6fdd246e0cce6147e539d53e0298 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_setup.c,v 1.1.2.47 2003/12/07 14:28:39 guus Exp $
+    $Id: net_setup.c,v 1.1.2.48 2003/12/20 19:47:52 guus Exp $
 */
 
 #include "system.h"
@@ -272,21 +272,20 @@ bool setup_myself(void)
 
        /* Check some options */
 
-       if(get_config_bool(lookup_config(config_tree, "IndirectData"), &choice))
-               if(choice)
-                       myself->options |= OPTION_INDIRECT;
+       if(get_config_bool(lookup_config(config_tree, "IndirectData"), &choice) && choice)
+               myself->options |= OPTION_INDIRECT;
+
+       if(get_config_bool(lookup_config(config_tree, "TCPOnly"), &choice) && choice)
+               myself->options |= OPTION_TCPONLY;
 
-       if(get_config_bool(lookup_config(config_tree, "TCPOnly"), &choice))
-               if(choice)
-                       myself->options |= OPTION_TCPONLY;
+       if(get_config_bool(lookup_config(myself->connection->config_tree, "IndirectData"), &choice) && choice)
+               myself->options |= OPTION_INDIRECT;
 
-       if(get_config_bool(lookup_config(myself->connection->config_tree, "IndirectData"), &choice))
-               if(choice)
-                       myself->options |= OPTION_INDIRECT;
+       if(get_config_bool(lookup_config(myself->connection->config_tree, "TCPOnly"), &choice) && choice)
+               myself->options |= OPTION_TCPONLY;
 
-       if(get_config_bool(lookup_config(myself->connection->config_tree, "TCPOnly"), &choice))
-               if(choice)
-                       myself->options |= OPTION_TCPONLY;
+       if(get_config_bool(lookup_config(myself->connection->config_tree, "DontFragment"), &choice) && choice)
+               myself->options |= OPTION_DONTFRAGMENT;
 
        if(myself->options & OPTION_TCPONLY)
                myself->options |= OPTION_INDIRECT;
index 4e4a00805467b60f3f4e35c8ffc2e7f06f69733f..f740431459410234309eafe7a0d1a1954af06b35 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.35 2003/12/12 19:52:25 guus Exp $
+    $Id: net_socket.c,v 1.1.2.36 2003/12/20 19:47:52 guus Exp $
 */
 
 #include "system.h"
@@ -49,13 +49,10 @@ int listen_sockets;
 
 int setup_listen_socket(const sockaddr_t *sa)
 {
-       int nfd, flags;
+       int nfd;
        char *addrstr;
        int option;
        char *iface;
-#ifdef SO_BINDTODEVICE
-       struct ifreq ifr;
-#endif
 
        cp();
 
@@ -67,13 +64,15 @@ int setup_listen_socket(const sockaddr_t *sa)
        }
 
 #ifdef O_NONBLOCK
-       flags = fcntl(nfd, F_GETFL);
+       {
+               int flags = fcntl(nfd, F_GETFL);
 
-       if(fcntl(nfd, F_SETFL, flags | O_NONBLOCK) < 0) {
-               closesocket(nfd);
-               logger(LOG_ERR, _("System call `%s' failed: %s"), "fcntl",
-                          strerror(errno));
-               return -1;
+               if(fcntl(nfd, F_SETFL, flags | O_NONBLOCK) < 0) {
+                       closesocket(nfd);
+                       logger(LOG_ERR, _("System call `%s' failed: %s"), "fcntl",
+                                  strerror(errno));
+                       return -1;
+               }
        }
 #endif
 
@@ -94,6 +93,8 @@ int setup_listen_socket(const sockaddr_t *sa)
        if(get_config_string
           (lookup_config(config_tree, "BindToInterface"), &iface)) {
 #if defined(SOL_SOCKET) && defined(SO_BINDTODEVICE)
+               struct ifreq ifr;
+
                memset(&ifr, 0, sizeof(ifr));
                strncpy(ifr.ifr_ifrn.ifrn_name, iface, IFNAMSIZ);
 
@@ -129,13 +130,9 @@ int setup_listen_socket(const sockaddr_t *sa)
 
 int setup_vpn_in_socket(const sockaddr_t *sa)
 {
-       int nfd, flags;
+       int nfd;
        char *addrstr;
        int option;
-#if defined(SOL_SOCKET) && defined(SO_BINDTODEVICE)
-       char *iface;
-       struct ifreq ifr;
-#endif
 
        cp();
 
@@ -147,29 +144,51 @@ int setup_vpn_in_socket(const sockaddr_t *sa)
        }
 
 #ifdef O_NONBLOCK
-       flags = fcntl(nfd, F_GETFL);
-       if(fcntl(nfd, F_SETFL, flags | O_NONBLOCK) < 0) {
-               closesocket(nfd);
-               logger(LOG_ERR, _("System call `%s' failed: %s"), "fcntl",
-                          strerror(errno));
-               return -1;
+       {
+               int flags = fcntl(nfd, F_GETFL);
+
+               if(fcntl(nfd, F_SETFL, flags | O_NONBLOCK) < 0) {
+                       closesocket(nfd);
+                       logger(LOG_ERR, _("System call `%s' failed: %s"), "fcntl",
+                                  strerror(errno));
+                       return -1;
+               }
        }
 #endif
 
        option = 1;
        setsockopt(nfd, SOL_SOCKET, SO_REUSEADDR, &option, sizeof(option));
 
-#if defined(SOL_SOCKET) && defined(SO_BINDTODEVICE)
-       if(get_config_string
-          (lookup_config(config_tree, "BindToInterface"), &iface)) {
-               memset(&ifr, 0, sizeof(ifr));
-               strncpy(ifr.ifr_ifrn.ifrn_name, iface, IFNAMSIZ);
+#if defined(SOL_IP) && defined(IP_MTU_DISCOVER) && defined(IP_PMTUDISC_DO)
+       {
+               bool choice;
+
+               if(get_config_bool(lookup_config(myself->connection->config_tree, "DontFragment"), &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 MTU discovery mode: %s"), strerror(errno));
+                               return -1;
+                       }
+               }
+       }
+#endif
 
-               if(setsockopt(nfd, SOL_SOCKET, SO_BINDTODEVICE, &ifr, sizeof(ifr))) {
-                       closesocket(nfd);
-                       logger(LOG_ERR, _("Can't bind to interface %s: %s"), iface,
-                                  strerror(errno));
-                       return -1;
+#if defined(SOL_SOCKET) && defined(SO_BINDTODEVICE)
+       {
+               char *iface;
+               struct ifreq ifr;
+
+               if(get_config_string(lookup_config(config_tree, "BindToInterface"), &iface)) {
+                       memset(&ifr, 0, sizeof(ifr));
+                       strncpy(ifr.ifr_ifrn.ifrn_name, iface, IFNAMSIZ);
+
+                       if(setsockopt(nfd, SOL_SOCKET, SO_BINDTODEVICE, &ifr, sizeof(ifr))) {
+                               closesocket(nfd);
+                               logger(LOG_ERR, _("Can't bind to interface %s: %s"), iface,
+                                          strerror(errno));
+                               return -1;
+                       }
                }
        }
 #endif
index 0fdc1dcc1c1528f5951539d6fd2e433e51f86a6b..e0639149a3fce482f0c95ea08e95edfc8b9da54f 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.28 2003/08/28 21:05:10 guus Exp $
+    $Id: node.c,v 1.1.2.29 2003/12/20 19:47:52 guus Exp $
 */
 
 #include "system.h"
@@ -80,6 +80,7 @@ node_t *new_node(void)
        n->edge_tree = new_edge_tree();
        n->queue = list_alloc((list_action_t) free);
        EVP_CIPHER_CTX_init(&n->packet_ctx);
+       n->mtu = MTU;
 
        return n;
 }
index 4407f993c6ee4d5df49767fe55fe57c92f255fb0..7ce17ebf09edf5abc6bb56214d1c0036c612dd0f 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.29 2003/07/30 21:52:41 guus Exp $
+    $Id: node.h,v 1.1.2.30 2003/12/20 19:47:52 guus Exp $
 */
 
 #ifndef __TINC_NODE_H__
 
 #include "avl_tree.h"
 #include "connection.h"
+#include "event.h"
 #include "list.h"
 #include "subnet.h"
 
 typedef struct node_status_t {
        int active:1;                           /* 1 if active.. */
        int validkey:1;                         /* 1 if we currently have a valid key for him */
-       int waitingforkey:1;            /* 1 if we already sent out a request */
+       int waitingforkey:1;                    /* 1 if we already sent out a request */
        int visited:1;                          /* 1 if this node has been visited by one of the graph algorithms */
        int reachable:1;                        /* 1 if this node is reachable in the graph */
        int indirect:1;                         /* 1 if this node is not directly reachable by us */
@@ -39,7 +40,7 @@ typedef struct node_status_t {
 } node_status_t;
 
 typedef struct node_t {
-       char *name;                                     /* name of this node */
+       char *name;                             /* name of this node */
        long int options;                       /* options turned on for this node */
 
        sockaddr_t address;                     /* his real (internet) ip to send UDP packets to */
@@ -47,30 +48,35 @@ typedef struct node_t {
 
        node_status_t status;
 
-       const EVP_CIPHER *cipher;       /* Cipher type for UDP packets */
-       char *key;                                      /* Cipher key and iv */
+       const EVP_CIPHER *cipher;               /* Cipher type for UDP packets */
+       char *key;                              /* Cipher key and iv */
        int keylength;                          /* Cipher key and iv length */
-       EVP_CIPHER_CTX packet_ctx;      /* Cipher context */
+       EVP_CIPHER_CTX packet_ctx;              /* Cipher context */
        
-       const EVP_MD *digest;           /* Digest type for MAC */
+       const EVP_MD *digest;                   /* Digest type for MAC */
        int maclength;                          /* Length of MAC */
 
        int compression;                        /* Compressionlevel, 0 = no compression */
 
        list_t *queue;                          /* Queue for packets awaiting to be encrypted */
 
-       struct node_t *nexthop;         /* nearest node from us to him */
+       struct node_t *nexthop;                 /* nearest node from us to him */
        struct node_t *via;                     /* next hop for UDP packets */
 
-       avl_tree_t *subnet_tree;        /* Pointer to a tree of subnets belonging to this node */
+       avl_tree_t *subnet_tree;                /* Pointer to a tree of subnets belonging to this node */
 
-       avl_tree_t *edge_tree;          /* Edges with this node as one of the endpoints */
+       avl_tree_t *edge_tree;                  /* Edges with this node as one of the endpoints */
 
        struct connection_t *connection;        /* Connection associated with this node (if a direct connection exists) */
 
-       uint32_t sent_seqno;            /* Sequence number last sent to this node */
-       uint32_t received_seqno;        /* Sequence number last received from this node */
-       unsigned char late[16]; /* Bitfield marking late packets */
+       uint32_t sent_seqno;                    /* Sequence number last sent to this node */
+       uint32_t received_seqno;                /* Sequence number last received from this node */
+       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 */
+       int mtuprobes;                          /* Number of probes */
+       event_t *mtuevent;                      /* Probe event */
 } node_t;
 
 extern struct node_t *myself;
index 8aad5834a65fd6824e7b204c6270da4cac5b3fe0..b50e60db2ecd484c6ccac834163609deaa800de4 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.30 2003/11/17 15:30:18 guus Exp $
+    $Id: protocol_auth.c,v 1.1.4.31 2003/12/20 19:47:52 guus Exp $
 */
 
 #include "system.h"
@@ -476,6 +476,9 @@ bool send_ack(connection_t *c)
        if((get_config_bool(lookup_config(c->config_tree, "TCPOnly"), &choice) && choice) || myself->options & OPTION_TCPONLY)
                c->options |= OPTION_TCPONLY | OPTION_INDIRECT;
 
+       if((get_config_bool(lookup_config(c->config_tree, "DontFragment"), &choice) && choice) || myself->options & OPTION_DONTFRAGMENT)
+               c->options |= OPTION_DONTFRAGMENT;
+
        return send_request(c, "%d %s %d %lx", ACK, myport, c->estimated_weight, c->options);
 }
 
index 049fc1e8073c093a602275f9235a97fbfb4cd15c..b8b1f22379289fbaba071d7cdaa6d76bad827572 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_key.c,v 1.1.4.24 2003/11/17 15:30:18 guus Exp $
+    $Id: protocol_key.c,v 1.1.4.25 2003/12/20 19:47:53 guus Exp $
 */
 
 #include "system.h"
@@ -267,6 +267,8 @@ bool ans_key_h(connection_t *c)
                        return false;
                }
 
+       if(from->options & OPTION_DONTFRAGMENT && !from->mtuprobes)
+               send_mtu_probe(from);
 
        flush_queue(from);
 
index d300e0c7906866e0bdc62af08194d5b8a846c360..8924329bcb44607ec91af2d575e95172bc3649ca 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.71 2003/12/13 21:50:26 guus Exp $
+    $Id: route.c,v 1.1.2.72 2003/12/20 19:47:53 guus Exp $
 */
 
 #include "system.h"
@@ -206,7 +206,7 @@ static __inline__ void route_mac(node_t *source, vpn_packet_t *packet)
 
 /* RFC 792 */
 
-static void route_ipv4_unreachable(node_t *source, vpn_packet_t *packet, uint8_t code)
+static void route_ipv4_unreachable(node_t *source, vpn_packet_t *packet, uint8_t type, uint8_t code)
 {
        struct ip ip = {0};
        struct icmp icmp = {0};
@@ -231,6 +231,9 @@ static void route_ipv4_unreachable(node_t *source, vpn_packet_t *packet, uint8_t
 
        oldlen = packet->len - ether_size;
 
+       if(type == ICMP_DEST_UNREACH && code == ICMP_FRAG_NEEDED)
+               icmp.icmp_nextmtu = htons(packet->len - ether_size);
+
        if(oldlen >= IP_MSS - ip_size - icmp_size)
                oldlen = IP_MSS - ip_size - icmp_size;
        
@@ -256,7 +259,7 @@ static void route_ipv4_unreachable(node_t *source, vpn_packet_t *packet, uint8_t
        
        /* Fill in ICMP header */
        
-       icmp.icmp_type = ICMP_DEST_UNREACH;
+       icmp.icmp_type = type;
        icmp.icmp_code = code;
        icmp.icmp_cksum = 0;
        
@@ -269,7 +272,7 @@ static void route_ipv4_unreachable(node_t *source, vpn_packet_t *packet, uint8_t
        memcpy(packet->data + ether_size + ip_size, &icmp, icmp_size);
        
        packet->len = ether_size + ip_size + icmp_size + oldlen;
-       
+
        send_packet(source, packet);
 }
 
@@ -289,7 +292,7 @@ static __inline__ void route_ipv4_unicast(node_t *source, vpn_packet_t *packet)
                                packet->data[32],
                                packet->data[33]);
 
-               route_ipv4_unreachable(source, packet, ICMP_NET_UNKNOWN);
+               route_ipv4_unreachable(source, packet, ICMP_DEST_UNREACH, ICMP_NET_UNKNOWN);
                return;
        }
        
@@ -299,7 +302,14 @@ static __inline__ void route_ipv4_unicast(node_t *source, vpn_packet_t *packet)
        }
 
        if(!subnet->owner->status.reachable)
-               route_ipv4_unreachable(source, packet, ICMP_NET_UNREACH);
+               route_ipv4_unreachable(source, packet, ICMP_DEST_UNREACH, ICMP_NET_UNREACH);
+
+       if(subnet->owner->options & OPTION_DONTFRAGMENT && 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);
+               return;
+       }
 
        if(priorityinheritance)
                packet->priority = packet->data[15];
@@ -319,7 +329,7 @@ static __inline__ void route_ipv4(node_t *source, vpn_packet_t *packet)
 
 /* RFC 2463 */
 
-static void route_ipv6_unreachable(node_t *source, vpn_packet_t *packet, uint8_t code)
+static void route_ipv6_unreachable(node_t *source, vpn_packet_t *packet, uint8_t type, uint8_t code)
 {
        struct ip6_hdr ip6;
        struct icmp6_hdr icmp6 = {0};
@@ -347,6 +357,9 @@ static void route_ipv6_unreachable(node_t *source, vpn_packet_t *packet, uint8_t
        pseudo.ip6_dst = ip6.ip6_src;
 
        pseudo.length = packet->len - ether_size;
+
+       if(type == ICMP6_PACKET_TOO_BIG)
+               icmp6.icmp6_mtu = htonl(pseudo.length);
        
        if(pseudo.length >= IP_MSS - ip6_size - icmp6_size)
                pseudo.length = IP_MSS - ip6_size - icmp6_size;
@@ -366,7 +379,7 @@ static void route_ipv6_unreachable(node_t *source, vpn_packet_t *packet, uint8_t
 
        /* Fill in ICMP header */
        
-       icmp6.icmp6_type = ICMP6_DST_UNREACH;
+       icmp6.icmp6_type = type;
        icmp6.icmp6_code = code;
        icmp6.icmp6_cksum = 0;
 
@@ -413,7 +426,7 @@ static __inline__ void route_ipv6_unicast(node_t *source, vpn_packet_t *packet)
                                ntohs(*(uint16_t *) &packet->data[50]),
                                ntohs(*(uint16_t *) &packet->data[52]));
 
-               route_ipv6_unreachable(source, packet, ICMP6_DST_UNREACH_ADDR);
+               route_ipv6_unreachable(source, packet, ICMP6_DST_UNREACH, ICMP6_DST_UNREACH_ADDR);
                return;
        }
 
@@ -423,8 +436,15 @@ static __inline__ void route_ipv6_unicast(node_t *source, vpn_packet_t *packet)
        }
 
        if(!subnet->owner->status.reachable)
-               route_ipv6_unreachable(source, packet, ICMP6_DST_UNREACH_NOROUTE);
+               route_ipv6_unreachable(source, packet, ICMP6_DST_UNREACH, ICMP6_DST_UNREACH_NOROUTE);
        
+       if(subnet->owner->options & OPTION_DONTFRAGMENT && 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_ipv6_unreachable(source, packet, ICMP6_PACKET_TOO_BIG, 0);
+               return;
+       }
+
        send_packet(subnet->owner, packet);
 }