Remove instances in libssl of the constant 28 (for size of IPv4 header + UDP)
and instead use the value provided by the underlying BIO. Also provide some
new DTLS_CTRLs so that the library user can set the mtu without needing to
know this constant. These new DTLS_CTRLs provide the capability to set the
link level mtu to be used (i.e. including this IP/UDP overhead). The previous
DTLS_CTRLs required the library user to subtract this overhead first.

Reviewed-by: Tim Hudson <tjh@openssl.org>
diff --git a/ssl/d1_both.c b/ssl/d1_both.c
index 9a981e8..bea975b 100644
--- a/ssl/d1_both.c
+++ b/ssl/d1_both.c
@@ -156,9 +156,9 @@
 static unsigned char bitmask_end_values[]   = {0xff, 0x01, 0x03, 0x07, 0x0f, 0x1f, 0x3f, 0x7f};
 
 /* XDTLS:  figure out the right values */
-static const unsigned int g_probable_mtu[] = {1500 - 28, 512 - 28, 256 - 28};
+static const unsigned int g_probable_mtu[] = {1500, 512, 256};
 
-static unsigned int dtls1_guess_mtu(unsigned int curr_mtu);
+static void dtls1_guess_mtu(SSL *s);
 static void dtls1_fix_message_header(SSL *s, unsigned long frag_off, 
 	unsigned long frag_len);
 static unsigned char *dtls1_write_message_header(SSL *s,
@@ -226,18 +226,24 @@
 
 static void dtls1_query_mtu(SSL *s)
 {
+	if(s->d1->link_mtu)
+		{
+		s->d1->mtu = s->d1->link_mtu-BIO_dgram_get_mtu_overhead(SSL_get_wbio(s));
+		s->d1->link_mtu = 0;
+		}
+
 	/* AHA!  Figure out the MTU, and stick to the right size */
-	if (s->d1->mtu < dtls1_min_mtu() && !(SSL_get_options(s) & SSL_OP_NO_QUERY_MTU))
+	if (s->d1->mtu < dtls1_min_mtu(s) && !(SSL_get_options(s) & SSL_OP_NO_QUERY_MTU))
 		{
 		s->d1->mtu = 
 			BIO_ctrl(SSL_get_wbio(s), BIO_CTRL_DGRAM_QUERY_MTU, 0, NULL);
 
 		/* I've seen the kernel return bogus numbers when it doesn't know
 		 * (initial write), so just make sure we have a reasonable number */
-		if (s->d1->mtu < dtls1_min_mtu())
+		if (s->d1->mtu < dtls1_min_mtu(s))
 			{
 			s->d1->mtu = 0;
-			s->d1->mtu = dtls1_guess_mtu(s->d1->mtu);
+			dtls1_guess_mtu(s);
 			BIO_ctrl(SSL_get_wbio(s), BIO_CTRL_DGRAM_SET_MTU, 
 				s->d1->mtu, NULL);
 			}
@@ -275,7 +281,7 @@
 		}
 #endif
 
-	OPENSSL_assert(s->d1->mtu >= dtls1_min_mtu());  /* should have something reasonable now */
+	OPENSSL_assert(s->d1->mtu >= dtls1_min_mtu(s));  /* should have something reasonable now */
 
 	if ( s->init_off == 0  && type == SSL3_RT_HANDSHAKE)
 		OPENSSL_assert(s->init_num == 
@@ -1299,26 +1305,40 @@
 	return p;
 	}
 
-unsigned int 
-dtls1_min_mtu(void)
+unsigned int
+dtls1_link_min_mtu(void)
 	{
 	return (g_probable_mtu[(sizeof(g_probable_mtu) / 
 		sizeof(g_probable_mtu[0])) - 1]);
 	}
 
-static unsigned int 
-dtls1_guess_mtu(unsigned int curr_mtu)
+unsigned int
+dtls1_min_mtu(SSL *s)
 	{
+	return dtls1_link_min_mtu()-BIO_dgram_get_mtu_overhead(SSL_get_wbio(s));
+	}
+
+static void 
+dtls1_guess_mtu(SSL *s)
+	{
+	unsigned int curr_mtu;
 	unsigned int i;
+	unsigned int mtu_ovr;
+
+	curr_mtu = s->d1->mtu;
+	mtu_ovr = BIO_dgram_get_mtu_overhead(SSL_get_wbio(s));
 
 	if ( curr_mtu == 0 )
-		return g_probable_mtu[0] ;
-
-	for ( i = 0; i < sizeof(g_probable_mtu)/sizeof(g_probable_mtu[0]); i++)
-		if ( curr_mtu > g_probable_mtu[i])
-			return g_probable_mtu[i];
-
-	return curr_mtu;
+		{
+		curr_mtu = g_probable_mtu[0] - mtu_ovr;
+		}
+	else
+		{
+		for ( i = 0; i < sizeof(g_probable_mtu)/sizeof(g_probable_mtu[0]); i++)
+			if ( curr_mtu > g_probable_mtu[i] - mtu_ovr)
+				return g_probable_mtu[i] - mtu_ovr;
+		}
+	s->d1->mtu = curr_mtu;
 	}
 
 void
diff --git a/ssl/d1_lib.c b/ssl/d1_lib.c
index 09268b8..5b3de08 100644
--- a/ssl/d1_lib.c
+++ b/ssl/d1_lib.c
@@ -139,6 +139,9 @@
 		d1->cookie_len = sizeof(s->d1->cookie);
 		}
 
+	d1->link_mtu = 0;
+	d1->mtu = 0;
+
 	if( ! d1->unprocessed_rcds.q || ! d1->processed_rcds.q 
         || ! d1->buffered_messages || ! d1->sent_messages || ! d1->buffered_app_data.q)
 		{
@@ -234,6 +237,7 @@
 	pqueue sent_messages;
 	pqueue buffered_app_data;
 	unsigned int mtu;
+	unsigned int link_mtu;
 
 	if (s->d1)
 		{
@@ -243,6 +247,7 @@
 		sent_messages = s->d1->sent_messages;
 		buffered_app_data = s->d1->buffered_app_data.q;
 		mtu = s->d1->mtu;
+		link_mtu = s->d1->link_mtu;
 
 		dtls1_clear_queues(s);
 
@@ -256,6 +261,7 @@
 		if (SSL_get_options(s) & SSL_OP_NO_QUERY_MTU)
 			{
 			s->d1->mtu = mtu;
+			s->d1->link_mtu = link_mtu;
 			}
 
 		s->d1->unprocessed_rcds.q = unprocessed_rcds;
@@ -312,6 +318,25 @@
 			}
 		return 0; /* Unexpected state; fail closed. */
 
+		/* Just one protocol version is supported so far;
+		 * fail closed if the version is not as expected. */
+		return s->version == DTLS_MAX_VERSION;
+	case DTLS_CTRL_SET_LINK_MTU:
+		if (larg < (long)dtls1_link_min_mtu())
+			return 0;
+		s->d1->link_mtu = larg;
+		return 1;
+	case DTLS_CTRL_GET_LINK_MIN_MTU:
+		return (long)dtls1_link_min_mtu();
+	case SSL_CTRL_SET_MTU:
+		/*
+		 *  We may not have a BIO set yet so can't call dtls1_min_mtu()
+		 *  We'll have to make do with dtls1_link_min_mtu() and max overhead
+		 */
+		if (larg < (long)dtls1_link_min_mtu() - DTLS1_MAX_MTU_OVERHEAD)
+			return 0;
+		s->d1->mtu = larg;
+		return larg;
 	default:
 		ret = ssl3_ctrl(s, cmd, larg, parg);
 		break;
diff --git a/ssl/dtls1.h b/ssl/dtls1.h
index af86f60..96f7334 100644
--- a/ssl/dtls1.h
+++ b/ssl/dtls1.h
@@ -121,6 +121,9 @@
 #define DTLS1_SCTP_AUTH_LABEL	"EXPORTER_DTLS_OVER_SCTP"
 #endif
 
+/* Max MTU overhead we know about so far is 40 for IPv6 + 8 for UDP */
+#define DTLS1_MAX_MTU_OVERHEAD                   48
+
 typedef struct dtls1_bitmap_st
 	{
 	unsigned long map;		/* track 32 packets on 32-bit systems
@@ -235,6 +238,7 @@
 	/* Is set when listening for new connections with dtls1_listen() */
 	unsigned int listen;
 
+	unsigned int link_mtu; /* max on-the-wire DTLS packet size */
 	unsigned int mtu; /* max DTLS packet size */
 
 	struct hm_header_st w_msg_hdr;
diff --git a/ssl/ssl.h b/ssl/ssl.h
index bc4cd0d..104e4f1 100644
--- a/ssl/ssl.h
+++ b/ssl/ssl.h
@@ -787,6 +787,10 @@
         SSL_ctrl((ssl),SSL_CTRL_MODE,0,NULL)
 #define SSL_set_mtu(ssl, mtu) \
         SSL_ctrl((ssl),SSL_CTRL_SET_MTU,(mtu),NULL)
+#define DTLS_set_link_mtu(ssl, mtu) \
+        SSL_ctrl((ssl),DTLS_CTRL_SET_LINK_MTU,(mtu),NULL)
+#define DTLS_get_link_min_mtu(ssl) \
+        SSL_ctrl((ssl),DTLS_CTRL_GET_LINK_MIN_MTU,0,NULL)
 
 #define SSL_get_secure_renegotiation_support(ssl) \
 	SSL_ctrl((ssl), SSL_CTRL_GET_RI_SUPPORT, 0, NULL)
@@ -1844,6 +1848,8 @@
 
 #define SSL_CTRL_SET_DH_AUTO			118
 #define SSL_CTRL_CHECK_PROTO_VERSION		119
+#define DTLS_CTRL_SET_LINK_MTU			120
+#define DTLS_CTRL_GET_LINK_MIN_MTU		121
 
 
 #define SSL_CERT_SET_FIRST			1
diff --git a/ssl/ssl_lib.c b/ssl/ssl_lib.c
index 43204de..f45b6dd 100644
--- a/ssl/ssl_lib.c
+++ b/ssl/ssl_lib.c
@@ -1129,18 +1129,6 @@
 		l=s->max_cert_list;
 		s->max_cert_list=larg;
 		return(l);
-	case SSL_CTRL_SET_MTU:
-#ifndef OPENSSL_NO_DTLS1
-		if (larg < (long)dtls1_min_mtu())
-			return 0;
-#endif
-
-		if (SSL_IS_DTLS(s))
-			{
-			s->d1->mtu = larg;
-			return larg;
-			}
-		return 0;
 	case SSL_CTRL_SET_MAX_SEND_FRAGMENT:
 		if (larg < 512 || larg > SSL3_RT_MAX_PLAIN_LENGTH)
 			return 0;
diff --git a/ssl/ssl_locl.h b/ssl/ssl_locl.h
index 60f8107..ffbf75b 100644
--- a/ssl/ssl_locl.h
+++ b/ssl/ssl_locl.h
@@ -1218,7 +1218,8 @@
 int dtls1_is_timer_expired(SSL *s);
 void dtls1_double_timeout(SSL *s);
 int dtls1_send_newsession_ticket(SSL *s);
-unsigned int dtls1_min_mtu(void);
+unsigned int dtls1_min_mtu(SSL *s);
+unsigned int dtls1_link_min_mtu(void);
 void dtls1_hm_fragment_free(hm_fragment *frag);
 
 /* some client-only functions */