Subject: Re: MSS clamping proposal
To: None <tech-kern@NetBSD.ORG>
From: Martin Husemann <martin@duskware.de>
List: tech-kern
Date: 03/13/2002 22:11:56
--jI8keyz6grp/JLjh
Content-Type: text/plain; charset=us-ascii
Content-Disposition: inline

Ok, to me it looks like we nearly have a consensus.

I verified that you can do 1:1 NATs and clamp thereby, without actually
touching IP addresses:

List of active MAP/Redirect filters:
map pppoe0 217.0.156.252/32  -> 217.0.156.252/32  portmap tcp/udp 40000:42999 mssclamp 1452
map pppoe0 217.0.156.252/32  -> 217.0.156.252/32  mssclamp 1452

(There portmap line wasn't needed, I just forgot to delete it)

I cleaned up the code (long lines, etc...) and attach a new version (only
the ip_nat.c part has changed, IIRC).

Martin

--jI8keyz6grp/JLjh
Content-Type: text/plain; charset=us-ascii
Content-Disposition: attachment; filename="clamp.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/13 20:57:21
@@ -153,8 +153,8 @@
 static	hostmap_t *nat_hostmap __P((ipnat_t *, struct in_addr,
 				    struct in_addr));
 static	void	nat_hostmapdel __P((struct hostmap *));
+static	void	tcp_mss_clamp __P((tcphdr_t *, uint32_t, fr_info_t *, u_short *));
 
-
 int nat_init()
 {
 	KMALLOCS(nat_table[0], nat_t **, sizeof(nat_t *) * ipf_nattable_sz);
@@ -1127,6 +1127,47 @@
 	return i;
 }
 
+/*
+ * Check for MSS option and clamp it if necessary.
+ */
+static __inline void
+tcp_mss_clamp(tcp, maxmss, fin, csump)
+	tcphdr_t *tcp;
+	uint32_t maxmss;
+	fr_info_t *fin;
+	u_short *csump;
+{
+	uint8_t *cp;
+	uint32_t opt, mss, sumd;
+	int hlen;
+
+	hlen = tcp->th_off << 2;
+	if (hlen > sizeof(*tcp)) {
+		cp = (uint8_t *)tcp + sizeof(*tcp);
+
+		while (hlen > 0) {
+			opt = *cp++;
+			switch(opt) {
+			case TCPOPT_MAXSEG:
+				++cp;
+				mss = (uint32_t)ntohs(*(short *)cp);
+				if (mss > maxmss) {
+					*(short *)cp = htons((short)(maxmss));
+					CALC_SUMD(mss, maxmss, sumd);
+					fix_outcksum(fin, csump, sumd);
+				}
+				hlen = 0;
+				break;
+			case TCPOPT_EOL:
+			case TCPOPT_NOP:
+				hlen--;
+			default:
+				hlen -= *cp;
+				cp += *cp - 2;
+			}
+		}
+	}
+}
 
 /*
  * Create a new NAT table entry.
@@ -1459,6 +1500,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 +2511,15 @@
 				 */
 				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)
+					tcp_mss_clamp(tcp, nat->nat_mssclamp, fin, csump);
+
 				MUTEX_EXIT(&nat->nat_lock);
 			} else if (fin->fin_p == IPPROTO_UDP) {
 				udphdr_t *udp = (udphdr_t *)tcp;
Index: ip_nat.h
===================================================================
RCS file: /cvsroot/syssrc/sys/netinet/ip_nat.h,v
retrieving revision 1.24
diff -c -u -r1.24 ip_nat.h
--- ip_nat.h	2002/01/24 08:23:14	1.24
+++ ip_nat.h	2002/03/13 20:57:21
@@ -77,6 +77,7 @@
 	struct	in_addr	nat_inip;
 	struct	in_addr	nat_outip;
 	struct	in_addr	nat_oip;	/* other ip */
+	u_32_t	nat_mssclamp;		/* if != zero clamp MSS to this */
 	U_QUAD_T	nat_pkts;
 	U_QUAD_T	nat_bytes;
 	u_short	nat_oport;		/* other port */
@@ -113,6 +114,7 @@
 	struct	in_addr	in_nextip;
 	u_short	in_pnext;
 	u_short	in_ippip;	/* IP #'s per IP# */
+	u_32_t	in_mssclamp;	/* if != zero clamp MSS to this */
 	u_32_t	in_flags;	/* From here to in_dport must be reflected */
 	u_short	in_spare;
 	u_short	in_ppip;	/* ports per IP */
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/13 20:58:36
@@ -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/13 20:58:36
@@ -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,

--jI8keyz6grp/JLjh--