[MEDIUM] add support for TCP MSS adjustment for listeners

Sometimes it can be useful to limit the advertised TCP MSS on
incoming connections, for instance when requests come through
a VPN or when the system is running with jumbo frames enabled.

Passing the "mss <value>" arguments to a "bind" line will set
the value. This works under Linux >= 2.6.28, and maybe a few
earlier ones, though due to an old kernel bug most of earlier
versions will probably ignore it. It is also possible that some
other OSes will support this.
diff --git a/src/cfgparse.c b/src/cfgparse.c
index 9ec869e..413aeae 100644
--- a/src/cfgparse.c
+++ b/src/cfgparse.c
@@ -23,6 +23,8 @@
 #include <fcntl.h>
 #include <unistd.h>
 
+#include <netinet/tcp.h>
+
 #include <common/cfgparse.h>
 #include <common/config.h>
 #include <common/memory.h>
@@ -960,6 +962,35 @@
 				return -1;
 #endif
 			}
+			if (!strcmp(args[cur_arg], "mss")) { /* set MSS of listening socket */
+#ifdef TCP_MAXSEG
+				struct listener *l;
+				int mss;
+
+				if (!*args[cur_arg + 1]) {
+					Alert("parsing [%s:%d] : '%s' : missing MSS value.\n",
+					      file, linenum, args[0]);
+					return -1;
+				}
+
+				mss = str2uic(args[cur_arg + 1]);
+				if (mss < 1 || mss > 65535) {
+					Alert("parsing [%s:%d]: %s expects an MSS value between 1 and 65535.\n",
+					      file, linenum, args[0]);
+					return -1;
+				}
+
+				for (l = curproxy->listen; l != last_listen; l = l->next)
+					l->maxseg = mss;
+
+				cur_arg += 2;
+				continue;
+#else
+				Alert("parsing [%s:%d] : '%s' : '%s' option not implemented.\n",
+				      file, linenum, args[0], args[cur_arg]);
+				return -1;
+#endif
+			}
 			if (!strcmp(args[cur_arg], "transparent")) { /* transparently bind to these addresses */
 #ifdef CONFIG_HAP_LINUX_TPROXY
 				struct listener *l;
diff --git a/src/proto_tcp.c b/src/proto_tcp.c
index e9b3ae3..adf4e23 100644
--- a/src/proto_tcp.c
+++ b/src/proto_tcp.c
@@ -251,6 +251,15 @@
 		}
 	}
 #endif
+#ifdef TCP_MAXSEG
+	if (listener->maxseg) {
+		if (setsockopt(fd, SOL_TCP, TCP_MAXSEG,
+			       &listener->maxseg, sizeof(listener->maxseg)) == -1) {
+			msg = "cannot set MSS";
+			err |= ERR_WARN;
+		}
+	}
+#endif
 	if (bind(fd, (struct sockaddr *)&listener->addr, listener->proto->sock_addrlen) == -1) {
 		err |= ERR_RETRYABLE | ERR_ALERT;
 		msg = "cannot bind socket";