diff options
author | cinap_lenrek <cinap_lenrek@felloff.net> | 2022-09-17 13:32:58 +0000 |
---|---|---|
committer | cinap_lenrek <cinap_lenrek@felloff.net> | 2022-09-17 13:32:58 +0000 |
commit | 5a1c1b590d319d58a178c6d299e99142bf7d4433 (patch) | |
tree | 7dd34e3085dae2fb95317c0d0c0d15081b11a03a /sys/src | |
parent | 05b08f6665b8b0c14c0cb4caa42929db8d26c6c9 (diff) |
devip: do tcp mss clamping when forwarding packets
when forwarding packets (gating), unconditionally
check tcp-syn packets for the mss-size option and
reduce it to fit the mtu of the outgoing interface.
this is done by exporting a new tcpmssclamp() function
from ip/tcp.c that takes an ip packet and its buffer size
and the effective mtu of the interface and adjusts
the mss value of tcp syn options.
this function is now also used by devbridge, enforcing
a tcp mss below the tunnel mtu.
Diffstat (limited to 'sys/src')
-rw-r--r-- | sys/src/9/ip/ip.c | 6 | ||||
-rw-r--r-- | sys/src/9/ip/ip.h | 5 | ||||
-rw-r--r-- | sys/src/9/ip/ipaux.c | 7 | ||||
-rw-r--r-- | sys/src/9/ip/ipv6.c | 6 | ||||
-rw-r--r-- | sys/src/9/ip/tcp.c | 62 | ||||
-rw-r--r-- | sys/src/9/port/devbridge.c | 100 |
6 files changed, 81 insertions, 105 deletions
diff --git a/sys/src/9/ip/ip.c b/sys/src/9/ip/ip.c index de8b20a0d..89e6cf5a7 100644 --- a/sys/src/9/ip/ip.c +++ b/sys/src/9/ip/ip.c @@ -134,14 +134,16 @@ ipoput4(Fs *f, Block *bp, int gating, int ttl, int tos, Routehint *rh) if(ifc->m == nil) goto raise; - if(!gating){ + medialen = ifc->maxtu - ifc->m->hsize; + if(gating) + tcpmssclamp((uchar*)eh, len, medialen); + else { eh->vihl = IP_VER4|IP_HLEN4; eh->tos = tos; } eh->ttl = ttl; /* If we dont need to fragment just send it */ - medialen = ifc->maxtu - ifc->m->hsize; if(len <= medialen) { hnputs(eh->length, len); if(!gating){ diff --git a/sys/src/9/ip/ip.h b/sys/src/9/ip/ip.h index ca77043cd..56d94d4cf 100644 --- a/sys/src/9/ip/ip.h +++ b/sys/src/9/ip/ip.h @@ -755,6 +755,11 @@ extern void ip_init(Fs*); extern void ip_init_6(Fs*); /* + * tcp.c + */ +extern void tcpmssclamp(uchar*, int, int); + +/* * bootp.c */ extern int bootpread(char*, ulong, int); diff --git a/sys/src/9/ip/ipaux.c b/sys/src/9/ip/ipaux.c index 50e4e6cc1..9f413ca0e 100644 --- a/sys/src/9/ip/ipaux.c +++ b/sys/src/9/ip/ipaux.c @@ -557,8 +557,11 @@ hnputs_csum(void *p, ushort v, uchar *pcsum) { ulong csum; - assert((((uchar*)p - pcsum) & 1) == 0); - + if(((uchar*)p - pcsum) & 1){ + hnputs_csum((uchar*)p-1, (nhgets((uchar*)p-1) & 0xFF00) | v>>8, pcsum); + hnputs_csum((uchar*)p+1, (nhgets((uchar*)p+1) & 0x00FF) | v<<8, pcsum); + return; + } csum = nhgets(pcsum)^0xFFFF; csum += nhgets(p)^0xFFFF; csum += v; diff --git a/sys/src/9/ip/ipv6.c b/sys/src/9/ip/ipv6.c index bfe652d47..c7045de89 100644 --- a/sys/src/9/ip/ipv6.c +++ b/sys/src/9/ip/ipv6.c @@ -88,7 +88,10 @@ ipoput6(Fs *f, Block *bp, int gating, int ttl, int tos, Routehint *rh) if(ifc->m == nil) goto raise; - if(!gating){ + medialen = ifc->maxtu - ifc->m->hsize; + if(gating) + tcpmssclamp((uchar*)eh, len, medialen); + else { eh->vcf[0] = IP_VER6; eh->vcf[0] |= tos >> 4; eh->vcf[1] = tos << 4; @@ -96,7 +99,6 @@ ipoput6(Fs *f, Block *bp, int gating, int ttl, int tos, Routehint *rh) eh->ttl = ttl; /* If we dont need to fragment just send it */ - medialen = ifc->maxtu - ifc->m->hsize; if(len <= medialen) { hnputs(eh->ploadlen, len - IP6HDR); ipifcoput(ifc, bp, V6, gate, rh); diff --git a/sys/src/9/ip/tcp.c b/sys/src/9/ip/tcp.c index d2a83a747..86105230c 100644 --- a/sys/src/9/ip/tcp.c +++ b/sys/src/9/ip/tcp.c @@ -3307,6 +3307,68 @@ tcpforward(Proto *tcp, Block *bp, Route *r) return bp; } +void +tcpmssclamp(uchar *p, int n, int mtu) +{ + Tcp4hdr *h4; + Tcp6hdr *h6; + uchar *pcksum; + int hdrlen, optlen, newmss, oldmss; + + if(n < TCP4_PKT) + return; + h4 = (Tcp4hdr*)p; + h6 = (Tcp6hdr*)p; + if((h4->vihl&0xF0)==IP_VER4) { + if(h4->proto != IP_TCPPROTO) + return; + if(!(h4->tcpflag[1] & SYN)) + return; + hdrlen = (h4->tcpflag[0] >> 2) & ~3; + if(hdrlen > (n - TCP4_PKT)) + return; + n = hdrlen - TCP4_HDRSIZE; + p = h4->tcpopt; + pcksum = h4->tcpcksum; + newmss = mtu - (TCP4_PKT + TCP4_HDRSIZE); + } else { + if(n < TCP6_PKT) + return; + if(h6->proto != IP_TCPPROTO) + return; + if(!(h6->tcpflag[1] & SYN)) + return; + hdrlen = (h6->tcpflag[0] >> 2) & ~3; + if(hdrlen > (n - TCP6_PKT)) + return; + n = hdrlen - TCP6_HDRSIZE; + p = h6->tcpopt; + pcksum = h6->tcpcksum; + newmss = mtu - (TCP6_PKT + TCP6_HDRSIZE); + } + while(n > 0 && *p != EOLOPT) { + if(*p == NOOPOPT) { + n--; + p++; + continue; + } + optlen = p[1]; + if(optlen < 2 || optlen > n) + break; + if(*p == MSSOPT){ + if(optlen != MSS_LENGTH) + break; + oldmss = nhgets(p+2); + if(newmss >= oldmss) + break; + hnputs_csum(p+2, newmss, pcksum); + break; + } + n -= optlen; + p += optlen; + } +} + static char* tcpporthogdefensectl(char *val) { diff --git a/sys/src/9/port/devbridge.c b/sys/src/9/port/devbridge.c index d4413cf71..4fcd7c392 100644 --- a/sys/src/9/port/devbridge.c +++ b/sys/src/9/port/devbridge.c @@ -40,7 +40,6 @@ enum { CacheTimeout= 5*60, /* timeout for cache entry in seconds */ MaxMTU= IP_MAX, /* allow for jumbo frames and large UDP */ - TcpMssMax = 1300, /* max desirable Tcp MSS value */ TunnelMtu = 1400, }; @@ -153,27 +152,6 @@ struct Port uchar member[0x1000/8]; }; -enum { - EOLOPT = 0, - NOOPOPT = 1, - MSSOPT = 2, - MSS_LENGTH = 4, /* Mean segment size */ - SYN = 0x02, /* Pkt. is synchronise */ - TCPHDR = 20, -}; - -struct Tcphdr -{ - uchar sport[2]; - uchar dport[2]; - uchar seq[4]; - uchar ack[4]; - uchar flag[2]; - uchar win[2]; - uchar cksum[2]; - uchar urg[2]; -}; - static Bridge *bridgetab[Maxbridge]; static int bridgegen(Chan *c, char*, Dirtab*, int, int s, Dir *dp); @@ -1011,89 +989,13 @@ ethermultiwrite(Bridge *b, Block *bp, int portid, ushort tag) static void tcpmsshack(Etherpkt *epkt, int n) { - int hl, optlen; - Tcphdr *tcphdr; - ulong mss, cksum; - uchar *optr; - /* ignore non-ip packets */ switch(nhgets(epkt->type)){ case ETIP4: case ETIP6: + tcpmssclamp(epkt->data, n-ETHERHDRSIZE, TunnelMtu-ETHERHDRSIZE); break; - default: - return; - } - n -= ETHERHDRSIZE; - if(n < 1) - return; - switch(epkt->data[0]&0xF0){ - case IP_VER4: - hl = (epkt->data[0]&15)<<2; - if(n < hl+TCPHDR || hl < IP4HDR || epkt->data[9] != TCP) - return; - n -= hl; - tcphdr = (Tcphdr*)(epkt->data + hl); - break; - case IP_VER6: - if(n < IP6HDR+TCPHDR || epkt->data[6] != TCP) - return; - n -= IP6HDR; - tcphdr = (Tcphdr*)(epkt->data + IP6HDR); - break; - default: - return; - } - - /* MSS can only appear in SYN packet */ - if(!(tcphdr->flag[1] & SYN)) - return; - hl = (tcphdr->flag[0] & 0xf0)>>2; - if(n < hl) - return; - - /* check for MSS option */ - optr = (uchar*)tcphdr + TCPHDR; - n = hl - TCPHDR; - for(;;) { - if(n <= 0 || *optr == EOLOPT) - return; - if(*optr == NOOPOPT) { - n--; - optr++; - continue; - } - optlen = optr[1]; - if(optlen < 2 || optlen > n) - return; - if(*optr == MSSOPT && optlen == MSS_LENGTH) - break; - n -= optlen; - optr += optlen; - } - - mss = nhgets(optr+2); - if(mss <= TcpMssMax) - return; - - /* fix checksum */ - cksum = nhgets(tcphdr->cksum); - if(optr-(uchar*)tcphdr & 1) { - // odd alignments are a pain - cksum += nhgets(optr+1); - cksum -= (optr[1]<<8)|(TcpMssMax>>8); - cksum += (cksum>>16); - cksum &= 0xffff; - cksum += nhgets(optr+3); - cksum -= ((TcpMssMax&0xff)<<8)|optr[4]; - cksum += (cksum>>16); - } else { - cksum += mss; - cksum -= TcpMssMax; - cksum += (cksum>>16); } - hnputs(tcphdr->cksum, cksum); - hnputs(optr+2, TcpMssMax); } /* |