[PATCH] 2.0 TRANSPARENT_PROXY TCP close `fix.'

Paul Rusty Russell (Paul.Russell@rustcorp.com.au)
Sun, 20 Sep 1998 00:23:23 -0700


The problem: Packet leakage when a transparent proxy closes.

When CONFIG_IP_TRANSPARENT_PROXY is enabled, a socket is allowed to
bind() to an IP address which does not belong to the box. So when a
packet comes in, the tcp_established_hash (for TCP) is looked up to
see if it is actually destined for a local socket.

When the socket is closed, and a packet is received, a RST is sent and
it's moved to the CLOSE state immediately. This would normally be OK
(furthur packets will result in a RST), but the state change removes
the socket from the tcp_established_hash, so packets destined for a
socket bound to a foreign address are let through.

This can cause an ACK storm in normal use of this feature (there are
two independent TCP streams, one from client<->proxy, and one from
proxy<->server, with the same SRCIP/DSTIP/SRCPT/DSTPT); the death of
the transproxy causes a `short circuit' between these two connections
(identical, but with different SYN numbers), and the ACK storm follows.

Here is the (small, almost certainly wrong) patch against 2.0.33, (but
problem is still in 2.0.36-pre9), and then the test script and the
source for `transproxy' which simply binds to a foreign port, connects
and then pulls data for a while before closing.

Your kernel logs should show the packets aimed at the proxy being
forwarded (assuming you have forwarding enabled).

tcpdumps, complete kernel logs et. al available on request.

--- linux-2.0.33/net/ipv4/tcp_input.c.~1~ Fri Oct 31 11:34:12 1997
+++ linux-2.0.33/net/ipv4/tcp_input.c Sat Sep 19 23:35:08 1998
@@ -2071,7 +2073,18 @@
sk->err = EPIPE;
sk->error_report(sk);
sk->shutdown = SHUTDOWN_MASK;
+#ifdef CONFIG_IP_TRANSPARENT_PROXY
+ /* This is not right, but we need
+ * to stay around in case the socket
+ * is bound to a remote address;
+ * if it vanishes we start leaking
+ * packets. --PR */
+ tcp_set_state(sk, TCP_TIME_WAIT);
+ tcp_reset_msl_timer(sk, TIME_CLOSE,
+ TCP_TIMEWAIT_LEN);
+#else
tcp_set_state(sk,TCP_CLOSE);
+#endif
kfree_skb(skb, FREE_READ);
return 0;
}
================================================================
#! /bin/bash
# Run `yes | socket -s 9999' on $DEST_IP.
PRETEND_IP=1.2.3.4
PRETEND_PT=5555
DEST_IP=192.168.69.205
DEST_PT=9999
ITER=10

# Log forwarded packets.
sudo ipfwadm -F -i accept -D $PRETEND_IP -o

# Run it.
sudo ./transproxy $PRETEND_IP $PRETEND_PT $DEST_IP $DEST_PT $ITER
================================================================
/* Simple proxy to check transparent proxy features in Linux. */
#include <sys/types.h>
#include <sys/socket.h>
#include <netinet/in.h>
#include <arpa/inet.h>
#include <unistd.h>
#include <stdio.h>
#include <stdlib.h>

/* Use transproxy `remote bind' feature. */
int main(int argc, const char *argv[])
{
int fd;
struct sockaddr_in saddr, daddr;
int addrlen = sizeof(struct sockaddr_in);
unsigned int i;
char buffer[4096];

if (argc != 6) {
fprintf(stderr,
"Usage: %s localaddr localpt remoteaddr remotept iter\n",
argv[0]);
exit(1);
}

saddr.sin_family = daddr.sin_family = AF_INET;

if (!inet_aton(argv[1], &saddr.sin_addr)) {
fprintf(stderr, "Error parsing `%s'\n", argv[1]);
exit(1);
}
saddr.sin_port = htons(atoi(argv[2]));

if (!inet_aton(argv[3], &daddr.sin_addr)) {
fprintf(stderr, "Error parsing `%s'\n", argv[3]);
exit(1);
}
daddr.sin_port = htons(atoi(argv[4]));

fd = socket(PF_INET, SOCK_STREAM, IPPROTO_IP);
if (fd < 0) {
perror("Opening socket");
exit(1);
}

if (bind(fd, (struct sockaddr *)&saddr, addrlen) != 0) {
perror("Binding socket");
exit(1);
}
if (connect(fd, (struct sockaddr *)&daddr, addrlen) != 0) {
perror("Connect failed.");
exit(1);
}

for (i = atoi(argv[5]) + 1; i; i--) {
read(fd, buffer, sizeof(buffer));
write(fd, buffer, 10);
}
close(fd);

return 0;
}

--
 .sig lost in the mail.

- To unsubscribe from this list: send the line "unsubscribe linux-kernel" in the body of a message to majordomo@vger.rutgers.edu Please read the FAQ at http://www.tux.org/lkml/