From: Guus Sliepen <guus@tinc-vpn.org>
Date: Sat, 20 Dec 2003 19:47:53 +0000 (+0000)
Subject: Let tinc figure out the exact MTU of the link.
X-Git-Tag: release-1.0.3~37^2~19
X-Git-Url: http://git.tinc-vpn.org/git/browse?a=commitdiff_plain;h=6b12bea62fe2e4bd8b5b6bd0e5ca7f53318705db;p=tinc

Let tinc figure out the exact MTU of the link.
---

diff --git a/src/connection.h b/src/connection.h
index cc6ff718..175bf7ce 100644
--- a/src/connection.h
+++ b/src/connection.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: 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 */
diff --git a/src/graph.c b/src/graph.c
index cd7fbf36..d07dd681 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.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();
diff --git a/src/net.c b/src/net.c
index 16449768..a6d2bb7a 100644
--- 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);
diff --git a/src/net.h b/src/net.h
index cadb76e8..5b145538 100644
--- 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)
diff --git a/src/net_packet.c b/src/net_packet.c
index af34d059..ac4ad427 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.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;
 	}
 
diff --git a/src/net_setup.c b/src/net_setup.c
index 2c07ec63..e71d4466 100644
--- a/src/net_setup.c
+++ b/src/net_setup.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_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;
diff --git a/src/net_socket.c b/src/net_socket.c
index 4e4a0080..f7404314 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.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
diff --git a/src/node.c b/src/node.c
index 0fdc1dcc..e0639149 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.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;
 }
diff --git a/src/node.h b/src/node.h
index 4407f993..7ce17ebf 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.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__
@@ -25,13 +25,14 @@
 
 #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;
diff --git a/src/protocol_auth.c b/src/protocol_auth.c
index 8aad5834..b50e60db 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.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);
 }
 
diff --git a/src/protocol_key.c b/src/protocol_key.c
index 049fc1e8..b8b1f223 100644
--- a/src/protocol_key.c
+++ b/src/protocol_key.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_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);
 
diff --git a/src/route.c b/src/route.c
index d300e0c7..8924329b 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.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);
 }