aboutsummaryrefslogtreecommitdiff
path: root/net/tcpmssd
diff options
context:
space:
mode:
authorRuslan Ermilov <ru@FreeBSD.org>2004-06-11 18:52:39 +0000
committerRuslan Ermilov <ru@FreeBSD.org>2004-06-11 18:52:39 +0000
commit3db850ffd55d76659dd0472d6e840f5bcc4a6b03 (patch)
tree2d301d9a3858cfd59cdc9cf0eae86c92e940444e /net/tcpmssd
parenta427774043cebe390ce8d6fd0b84f14037706265 (diff)
downloadports-3db850ffd55d76659dd0472d6e840f5bcc4a6b03.tar.gz
ports-3db850ffd55d76659dd0472d6e840f5bcc4a6b03.zip
Notes
Diffstat (limited to 'net/tcpmssd')
-rw-r--r--net/tcpmssd/Makefile24
-rw-r--r--net/tcpmssd/distinfo2
-rw-r--r--net/tcpmssd/files/patch-aa11
-rw-r--r--net/tcpmssd/src/tcpmssd.895
-rw-r--r--net/tcpmssd/src/tcpmssd.c381
5 files changed, 496 insertions, 17 deletions
diff --git a/net/tcpmssd/Makefile b/net/tcpmssd/Makefile
index 77f6eca6880b..b3bc750c7dbe 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 <bsd.port.mk>
diff --git a/net/tcpmssd/distinfo b/net/tcpmssd/distinfo
deleted file mode 100644
index cc1e53542c9d..000000000000
--- 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 73f3953d5fb6..000000000000
--- 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 000000000000..0fc6f105e631
--- /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 000000000000..5eacff430e87
--- /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 <sys/param.h>
+#include <sys/socket.h>
+#include <sys/sysctl.h>
+#include <net/if.h>
+#include <net/if_dl.h>
+#include <net/route.h>
+#include <netinet/in_systm.h>
+#include <netinet/in.h>
+#include <netinet/ip.h>
+#include <netinet/tcp.h>
+#include <err.h>
+#include <errno.h>
+#include <paths.h>
+#include <signal.h>
+#include <stdio.h>
+#include <stdlib.h>
+#include <string.h>
+#include <unistd.h>
+
+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);
+}