Subject: MSS clamping proposal
To: None <tech-kern@NetBSD.ORG>
From: Martin Husemann <martin@duskware.de>
List: tech-kern
Date: 03/11/2002 16:26:05
--yrj/dFKFPuw6o+aM
Content-Type: text/plain; charset=us-ascii
Content-Disposition: inline

Before we start:

Please don't turn this into a clueless-firewall-admins-and-how-to-make-them-
comply-to-the-internet-standards discussion.

I've given up on at least two (important for me) cases of these phenomenon.

To enable people forced to use PPPoE by something outside their controll to
use NetBSD on their DSL router, we need some sort of MSS clamping support.

After talking about this with several people for quite some time my
personal preferences are:

 - It should be part of IP-Filter. Doing NAT is a good excuse to touch
   parts of packets we never should touch when acting as a router.
 - It should be configurable per interface.

There has been a patch indirectly forwarded from the IP-Filter lists to
current-users (if someone knows the source of this patch, please let me
know so I can properly credit the author). It is a bit bare-bones, but
working. I extended it to have more controll over when to clamp and to
what MSS.

The patch allows to specify optional mss clamping for each ipnat.conf rule,
like in this example:

map pppoe0 192.168.1.0/24 -> 0/32 portmap tcp/udp 40000:42999 mssclamp 1452
map pppoe0 192.168.1.0/24 -> 0/32 mssclamp 1452 

I'd like to commit this in a few days.

Any objections? Suggestions? Improvements?

Martin

--yrj/dFKFPuw6o+aM
Content-Type: text/plain; charset=us-ascii
Content-Disposition: attachment; filename="mssclamp.patch"

Index: ip_nat.c
===================================================================
RCS file: /cvsroot/syssrc/sys/netinet/ip_nat.c,v
retrieving revision 1.44
diff -c -u -r1.44 ip_nat.c
--- ip_nat.c	2002/01/24 08:23:44	1.44
+++ ip_nat.c	2002/03/11 15:11:04
@@ -1459,6 +1459,7 @@
 	nat->nat_dir = direction;
 	nat->nat_ifp = fin->fin_ifp;
 	nat->nat_ptr = np;
+	nat->nat_mssclamp = np->in_mssclamp;
 	nat->nat_p = fin->fin_p;
 	nat->nat_bytes = 0;
 	nat->nat_pkts = 0;
@@ -2469,6 +2470,42 @@
 				 */
 				if (nat->nat_age == fr_tcpclosed)
 					nat->nat_age = fr_tcplastack;
+
+ 				/*
+ 				 * Do a MSS CLAMPING on a SYN packet, only deal IPv4 for now.
+ 				 */
+ 				if (nat->nat_mssclamp && (tcp->th_flags & TH_SYN) != 0)
+ 				{
+ 					int hlen = tcp->th_off << 2;
+ 					if (hlen > sizeof(*tcp)) {
+ 						uint8_t *cp = (uint8_t *)tcp + sizeof(*tcp);
+ 						uint32_t opt, mss, newmss, sumd;
+ 						
+ 						newmss = nat->nat_mssclamp;
+ 						while (hlen > 0) {
+ 							opt = *cp++;
+ 							switch(opt) {
+ 							  case TCPOPT_MAXSEG:
+ 								++cp;
+ 								mss = (uint32_t)ntohs(*(short *)cp);
+ 								if (mss > newmss) {
+ 									*(short *)cp = htons((short)(newmss));
+ 									CALC_SUMD(mss, newmss, sumd);
+ 									fix_outcksum(fin, csump, sumd);
+ 								}
+ 								hlen = 0;
+ 								break;
+ 							  case TCPOPT_EOL:
+ 							  case TCPOPT_NOP:
+ 								hlen--;
+ 							  default:
+ 								hlen -= *cp;
+ 								cp += *cp - 2;
+ 							}
+ 						}
+ 					}
+ 				}
+
 				MUTEX_EXIT(&nat->nat_lock);
 			} else if (fin->fin_p == IPPROTO_UDP) {
 				udphdr_t *udp = (udphdr_t *)tcp;
Index: natparse.c
===================================================================
RCS file: /cvsroot/basesrc/dist/ipf/natparse.c,v
retrieving revision 1.5
diff -c -u -r1.5 natparse.c
--- natparse.c	2002/01/24 08:21:35	1.5
+++ natparse.c	2002/03/11 15:14:24
@@ -697,6 +697,14 @@
 		cpp++;
 	}
 
+	if (*cpp && !strcasecmp(*cpp, "mssclamp")) {
+		cpp++;
+		if (*cpp) {
+			ipn.in_mssclamp = atoi(*cpp);
+			cpp++;
+		}
+	}
+
 	if (*cpp) {
 		fprintf(stderr, "%d: extra junk at the end of the line: %s\n",
 			linenum, *cpp);
Index: printnat.c
===================================================================
RCS file: /cvsroot/basesrc/dist/ipf/printnat.c,v
retrieving revision 1.2
diff -c -u -r1.2 printnat.c
--- printnat.c	2002/01/24 10:40:12	1.2
+++ printnat.c	2002/03/11 15:14:24
@@ -451,6 +451,8 @@
 		}
 		if (np->in_flags & IPN_FRAG)
 			printf(" frag");
+		if (np->in_mssclamp)
+			printf(" mssclamp %u", (unsigned)np->in_mssclamp);
 		printf("\n");
 		if (opts & OPT_DEBUG) {
 			printf("\tspace %lu nextip %s pnext %d", np->in_space,

--yrj/dFKFPuw6o+aM--