From 9b014a6e2be3da3b127ffc6c52b5abb26e24116e Mon Sep 17 00:00:00 2001 From: ru Date: Fri, 11 Jun 2004 18:52:39 +0000 Subject: Keep sources directly in the tree. --- net/tcpmssd/Makefile | 24 ++- net/tcpmssd/distinfo | 2 - net/tcpmssd/files/patch-aa | 11 -- net/tcpmssd/src/tcpmssd.8 | 95 +++++++++++ net/tcpmssd/src/tcpmssd.c | 381 +++++++++++++++++++++++++++++++++++++++++++++ 5 files changed, 496 insertions(+), 17 deletions(-) delete mode 100644 net/tcpmssd/distinfo delete mode 100644 net/tcpmssd/files/patch-aa create mode 100644 net/tcpmssd/src/tcpmssd.8 create mode 100644 net/tcpmssd/src/tcpmssd.c (limited to 'net/tcpmssd') diff --git a/net/tcpmssd/Makefile b/net/tcpmssd/Makefile index 77f6eca6880..b3bc750c7db 100644 --- a/net/tcpmssd/Makefile +++ b/net/tcpmssd/Makefile @@ -8,14 +8,30 @@ PORTNAME= tcpmssd PORTVERSION= 1.0 CATEGORIES= net -MASTER_SITES= ${MASTER_SITE_LOCAL} -MASTER_SITE_SUBDIR=ru +MASTER_SITES= # none +DISTFILES= # none MAINTAINER= ru@FreeBSD.org -COMMENT= A divert(4) daemon to correct requested receive segment size for TCP traffic +COMMENT= A utility to correct requested TCP receive segment size + +WRKSRC= ${WRKDIR}/src + +SRC= ${.CURDIR}/src MAN8= tcpmssd.8 -MANCOMPRESSED= maybe PLIST_FILES= bin/tcpmssd +do-fetch: + @${DO_NADA} + +pre-patch: + @${CP} -R ${SRC} ${WRKDIR} + +do-build: + (cd ${WRKSRC} && ${MAKE} tcpmssd) + +do-install: + ${INSTALL_PROGRAM} ${WRKSRC}/tcpmssd ${PREFIX}/bin + ${INSTALL_MAN} ${WRKSRC}/tcpmssd.8 ${MAN8PREFIX}/man/man8 + .include diff --git a/net/tcpmssd/distinfo b/net/tcpmssd/distinfo deleted file mode 100644 index cc1e53542c9..00000000000 --- a/net/tcpmssd/distinfo +++ /dev/null @@ -1,2 +0,0 @@ -MD5 (tcpmssd-1.0.tar.gz) = 5917fd99f4d232d4e3f2da07cf198fac -SIZE (tcpmssd-1.0.tar.gz) = 5009 diff --git a/net/tcpmssd/files/patch-aa b/net/tcpmssd/files/patch-aa deleted file mode 100644 index 73f3953d5fb..00000000000 --- a/net/tcpmssd/files/patch-aa +++ /dev/null @@ -1,11 +0,0 @@ ---- tcpmssd.c~ Mon Jul 17 20:57:24 2000 -+++ tcpmssd.c Mon Nov 13 10:10:37 2000 -@@ -176,7 +176,7 @@ - continue; - } - -- if (FD_ISSET(rtsock, &fdset)) { -+ if (rtsock != -1 && FD_ISSET(rtsock, &fdset)) { - struct if_msghdr ifm; - - if ((pktlen = read(rtsock, &ifm, sizeof(ifm))) == -1) { diff --git a/net/tcpmssd/src/tcpmssd.8 b/net/tcpmssd/src/tcpmssd.8 new file mode 100644 index 00000000000..0fc6f105e63 --- /dev/null +++ b/net/tcpmssd/src/tcpmssd.8 @@ -0,0 +1,95 @@ +.\" $Id: tcpmssd.8,v 1.1 2000/07/17 17:58:03 ru Exp $ +.Dd July 17, 2000 +.Dt TCPMSSD 8 +.Os FreeBSD +.Sh NAME +.Nm tcpmssd +.Nd TCP Maximum Segment Size option corrector +.Sh SYNOPSIS +.Nm +.Op Fl v +.Fl p Ar port +.Eo \&{ +.Fl i Ar iface | Fl m Ar mtu +.Ec \&} +.Sh DESCRIPTION +.Nm +is a program that adjusts outgoing TCP SYN packets so that the maximum +receive segment size is not greater than the amount allowed by the +interface MTU. +.Pp +This is necessary in many setups to avoid problems caused by routers that +drop ICMP +.Dq Datagram Too Big +messages, thus breaking Path MTU discovery algorithm (RFC 1191). +Without these messages, the originating machine sends data, it passes +the rogue router then hits a machine that has an MTU that is not big +enough for the data. +Because the IP +.Dq don't fragment +option is set, this machine sends an ICMP +.Dq Datagram Too Big +message back to the originator and drops the packet. +The rogue router drops the ICMP and the originator never gets to +discover that it must reduce the Path MTU value or exclude the IP +.Dq don't fragment +option from its outgoing data. +.Pp +.Nm +normally runs in the background as a daemon. +It intercepts TCP packets +from a +.Xr divert 4 +socket bound to the +.Ar port +specified with the +.Fl p +option and reduces the value of TCP MSS option if necessary so that +the incoming TCP messages will pass through this host without need to +send ICMP +.Dq Datagram Too Big +messages. +.Pp +The maximum value for the TCP MSS option is determined based on a MTU +given either as an absolute value with the +.Fl m +option or derived from a network interface specified with the +.Fl i +option. +.Pp +If run with the +.Fl v +option, +.Nm +does not detach from its controlling terminal and writes various diagnostic +messages to the standard error output. +.Pp +The following steps are necessary to run +.Nm No : +.Bl -enum +.It +Build your kernel with the following options: +.Bd -literal -offset indent +options IPFIREWALL +options IPDIVERT +.Ed +.Pp +Refer to the Handbook for detailed instructions on building a custom +kernel. +.It +Make sure to redirect TCP traffic to the +.Xr divert 4 +port +.Ar port . +Refer to the +.Xr ipfw 8 +manual page for details. +.El +.Sh SEE ALSO +.Xr divert 4 , +.Xr ipfw 8 . +.Sh AUTHORS +This program was written by +.An Ruslan Ermilov Aq ru@FreeBSD.org +based on work done by +.An Patrick Bihan-Faou Aq patrick@mindstep.com . diff --git a/net/tcpmssd/src/tcpmssd.c b/net/tcpmssd/src/tcpmssd.c new file mode 100644 index 00000000000..5eacff430e8 --- /dev/null +++ b/net/tcpmssd/src/tcpmssd.c @@ -0,0 +1,381 @@ +/*- + * Copyright (c) 2000 Ruslan Ermilov + * All rights reserved. + * + * Redistribution and use in source and binary forms, with or without + * modification, are permitted provided that the following conditions + * are met: + * 1. Redistributions of source code must retain the above copyright + * notice, this list of conditions and the following disclaimer. + * 2. Redistributions in binary form must reproduce the above copyright + * notice, this list of conditions and the following disclaimer in the + * documentation and/or other materials provided with the distribution. + * + * THIS SOFTWARE IS PROVIDED BY THE AUTHOR AND CONTRIBUTORS ``AS IS'' AND + * ANY EXPRESS OR IMPLIED WARRANTIES, INCLUDING, BUT NOT LIMITED TO, THE + * IMPLIED WARRANTIES OF MERCHANTABILITY AND FITNESS FOR A PARTICULAR PURPOSE + * ARE DISCLAIMED. IN NO EVENT SHALL THE AUTHOR OR CONTRIBUTORS BE LIABLE + * FOR ANY DIRECT, INDIRECT, INCIDENTAL, SPECIAL, EXEMPLARY, OR CONSEQUENTIAL + * DAMAGES (INCLUDING, BUT NOT LIMITED TO, PROCUREMENT OF SUBSTITUTE GOODS + * OR SERVICES; LOSS OF USE, DATA, OR PROFITS; OR BUSINESS INTERRUPTION) + * HOWEVER CAUSED AND ON ANY THEORY OF LIABILITY, WHETHER IN CONTRACT, STRICT + * LIABILITY, OR TORT (INCLUDING NEGLIGENCE OR OTHERWISE) ARISING IN ANY WAY + * OUT OF THE USE OF THIS SOFTWARE, EVEN IF ADVISED OF THE POSSIBILITY OF + * SUCH DAMAGE. + * + * $Id: tcpmssd.c,v 1.5 2000/07/17 17:57:24 ru Exp $ + */ + +#include +#include +#include +#include +#include +#include +#include +#include +#include +#include +#include +#include +#include +#include +#include +#include +#include +#include + +static void correct_mss(struct tcphdr *, ssize_t len, u_int16_t); +static int if_mtu(const char *, u_long *); +static void sigterm_handler(int); +static void usage(void); + +int verbose; +char pidfilename[MAXPATHLEN]; + +/*- + * We are in liberal position about MSS + * (RFC 879, section 7). + */ +#define MAXMSS(mtu) (mtu - sizeof(struct ip) - sizeof(struct tcphdr)) + +int +main(int argc, char *argv[]) +{ + char pkt[IP_MAXPACKET]; + ssize_t pktlen, hlen; + struct ip *pip = (struct ip *)pkt; + struct sockaddr_in sin; + socklen_t sinlen; + int s; + int mflag; + u_long mtu; + u_int16_t maxmss; + int rtsock; + int ifindex; + int pflag; + u_short port; + int ch; + fd_set fdset; + + mflag = pflag = 0; + port = 0; /* XXX gcc -Wuninitialized */ + ifindex = 0; + rtsock = -1; + + while ((ch = getopt(argc, argv, "i:m:p:v")) != -1) + switch (ch) { + case 'i': + if (!(ifindex = if_mtu(optarg, &mtu))) { + errx(1, "unknown interface %s", optarg); + /* NOTREACHED */ + } + snprintf(pidfilename, sizeof pidfilename, + "%stcpmssd.%s.pid", _PATH_VARRUN, optarg); + break; + case 'm': + if ((mtu = atoi(optarg)) < 68) { + errx(1, "invalid MTU value"); + /* NOTREACHED */ + } + mflag++; + break; + case 'p': + port = htons(atoi(optarg)); + pflag++; + break; + case 'v': + verbose = 1; + break; + case '?': + default: + usage(); + /* NOTREACHED */ + } + argc -= optind; + argv += optind; + + if (!(!mflag - !ifindex) || !pflag || argc) { + usage(); + /* NOTREACHED */ + } + + if ((s = socket(PF_INET, SOCK_RAW, IPPROTO_DIVERT)) == -1) { + err(1, "can't create divert socket"); + /* NOTREACHED */ + } + + bzero(&sin, sizeof(sin)); + sin.sin_family = PF_INET; + sin.sin_addr.s_addr = INADDR_ANY; + sin.sin_port = port; + + if (bind(s, (struct sockaddr *)&sin, sizeof(sin)) == -1) { + err(1, "can't bind divert socket"); + /* NOTREACHED */ + } + + if (ifindex) + if ((rtsock = socket(PF_ROUTE, SOCK_RAW, 0)) == -1) { + err(1, "can't create routing socket"); + /* NOTREACHED */ + } + + maxmss = MAXMSS(mtu); + if (verbose) + fprintf(stderr, "Maximum MSS: %u\n", maxmss); + + if (!verbose) + if (daemon(0, 0) == -1) { + err(1, "can't daemonize"); + /* NOTREACHED */ + } + + if (ifindex) { + FILE *pidfile; + + pidfile = fopen(pidfilename, "w"); + if (pidfile != NULL) { + fprintf(pidfile, "%d\n", (int)getpid()); + fclose(pidfile); + if (signal(SIGTERM, sigterm_handler) == SIG_ERR) { + err(1, "can't install SIGTERM handler"); + /* NOTREACHED */ + } + } + } + + while (1) { + FD_ZERO(&fdset); + FD_SET(s, &fdset); + if (rtsock != -1) + FD_SET(rtsock, &fdset); + + if (select(32, &fdset, (fd_set *)NULL, (fd_set *)NULL, (struct timeval *)NULL) == -1) { + warn("select failed"); + continue; + } + + if (rtsock != -1 && FD_ISSET(rtsock, &fdset)) { + struct if_msghdr ifm; + + if ((pktlen = read(rtsock, &ifm, sizeof(ifm))) == -1) { + warn("read from routing socket failed"); + continue; + } + if (ifm.ifm_version != RTM_VERSION) { + if (verbose) + warnx("routing message version %d " + "not understood", ifm.ifm_version); + continue; + } + if (ifm.ifm_type != RTM_IFINFO || + ifm.ifm_index != ifindex) + continue; + if (mtu != ifm.ifm_data.ifi_mtu) { + mtu = ifm.ifm_data.ifi_mtu; + maxmss = MAXMSS(mtu); + if (verbose) + fprintf(stderr, "Maximum MSS: %u\n", maxmss); + } + } + + if (FD_ISSET(s, &fdset)) { + sinlen = sizeof(sin); + + if ((pktlen = recvfrom(s, pkt, sizeof(pkt), 0, + (struct sockaddr *)&sin, &sinlen)) == -1) + if (errno != EINTR) { + warn("read from divert socket failed"); + continue; + } + + hlen = pip->ip_hl << 2; + + /*- + * Check for MSS option only for outgoing + * TCP packets with zero fragment offset + * and correct total and header lengths. + */ + if (sin.sin_addr.s_addr == INADDR_ANY && + pip->ip_p == IPPROTO_TCP && + (ntohs(pip->ip_off) & IP_OFFMASK) == 0 && + ntohs(pip->ip_len) == pktlen && + hlen <= pktlen && + pktlen - hlen >= sizeof(struct tcphdr)) + correct_mss((struct tcphdr *)(pkt + hlen), + pktlen - hlen, maxmss); + + if (sendto(s, pkt, pktlen, 0, + (struct sockaddr *)&sin, sinlen) == -1) + warn("write to divert socket failed"); + } + } +} + + +/*- + * The following macro is used to update an + * internet checksum. "acc" is a 32-bit + * accumulation of all the changes to the + * checksum (adding in old 16-bit words and + * subtracting out new words), and "cksum" + * is the checksum value to be updated. + */ +#define ADJUST_CHECKSUM(acc, cksum) { \ + acc += cksum; \ + if (acc < 0) { \ + acc = -acc; \ + acc = (acc >> 16) + (acc & 0xffff); \ + acc += acc >> 16; \ + cksum = (u_short) ~acc; \ + } else { \ + acc = (acc >> 16) + (acc & 0xffff); \ + acc += acc >> 16; \ + cksum = (u_short) acc; \ + } \ +} + + +void +correct_mss(struct tcphdr *tc, ssize_t pktlen, u_int16_t maxmss) +{ + int hlen, olen, optlen; + u_char *opt; + u_int16_t *mss; + int accumulate; + + hlen = tc->th_off << 2; + + /* Invalid header length or header without options. */ + if (hlen <= sizeof(struct tcphdr) || hlen > pktlen) + return; + + /* MSS option only allowed within SYN packets. */ + if (!(tc->th_flags & TH_SYN)) + return; + + for (olen = hlen - sizeof(struct tcphdr), opt = (u_char *)(tc + 1); + olen > 0; olen -= optlen, opt += optlen) { + if (*opt == TCPOPT_EOL) + break; + else if (*opt == TCPOPT_NOP) + optlen = 1; + else { + optlen = *(opt + 1); + if (optlen <= 0 || optlen > olen) + break; + if (*opt == TCPOPT_MAXSEG) { + if (optlen != TCPOLEN_MAXSEG) + continue; + mss = (u_int16_t *)(opt + 2); + if (ntohs(*mss) > maxmss) { + if (verbose) + fprintf(stderr, + "MSS: %u -> %u\n", + ntohs(*mss), maxmss); + accumulate = *mss; + *mss = htons(maxmss); + accumulate -= *mss; + ADJUST_CHECKSUM(accumulate, tc->th_sum); + } + } + } + } +} + + +static int +if_mtu(const char *ifn, u_long *mtu) +{ + size_t needed; + int mib[6]; + char *buf, *lim, *next; + struct if_msghdr *ifm; + struct sockaddr_dl *sdl; + int ifindex; + + mib[0] = CTL_NET; + mib[1] = PF_ROUTE; + mib[2] = 0; + mib[3] = AF_INET; /* Only IP addresses please. */ + mib[4] = NET_RT_IFLIST; + mib[5] = 0; /* List all interfaces. */ +/* + * Get interface data. + */ + if (sysctl(mib, 6, NULL, &needed, NULL, 0) == -1) + err(1, "iflist-sysctl-estimate"); + if ((buf = malloc(needed)) == NULL) + errx(1, "malloc failed"); + if (sysctl(mib, 6, buf, &needed, NULL, 0) == -1) + err(1, "iflist-sysctl-get"); + lim = buf + needed; +/* + * Loop through interfaces until one with + * given name is found. This is done to + * find correct interface index for routing + * message processing. + */ + ifindex = 0; + next = buf; + while (next < lim) { + ifm = (struct if_msghdr *)next; + next += ifm->ifm_msglen; + if (ifm->ifm_version != RTM_VERSION) { + if (verbose) + warnx("routing message version %d " + "not understood", ifm->ifm_version); + continue; + } + if (ifm->ifm_type == RTM_IFINFO) { + sdl = (struct sockaddr_dl *)(ifm + 1); + if (strlen(ifn) == sdl->sdl_nlen && + strncmp(ifn, sdl->sdl_data, sdl->sdl_nlen) == 0) { + *mtu = ifm->ifm_data.ifi_mtu; + ifindex = ifm->ifm_index; + break; + } + } + } + free(buf); + return (ifindex); +} + + +static void +sigterm_handler(int sig) +{ + + (void)unlink(pidfilename); + exit(0); +} + + +void +usage(void) +{ + + fprintf(stderr, "usage: tcpmssd [-v] -p port {-i iface | -m mtu}\n"); + exit(1); +} -- cgit