TCP listenall
Matus Harvan
mharvan at inf.ethz.ch
Sun Sep 9 13:41:48 PDT 2007
Hello,
I am a Google Summer of Code student working on mtund, aka Magic
Tunnel Daemon aka Super Tunnel Daemon,
http://wiki.freebsd.org/SuperTunnelDaemon.
For mtund it would be useful to listen on all unused TCP ports,
allowing a client behind firewall to use any possible hole it could
find. To achieve this I would suggest the patch below. It would add a
socket option TCP_LISTENALL. This clearly could only be set on one
socket. When activated, the global variable inp_tlistenall would be
used to assign traffic to unused TCP ports to that socket. In
particular, the changed code in tcp_input() would be used twice - once
for the SYN packet (1st packet) and once for the ACK packet (3rd
packet). inp_tlistenall is protected by the INP_INFO lock on tcbinfo
in all places.
The patch also includes rate limiting to mitigate possible DoS
misuse. With listenall enabled, a portscan becomes a syn
flood. However, as there are already mechanisms to protect against a
syn flood attack, such as syncache and syncookies, the rate limit
might be left out. The disadvanatage of the rate limit is that a
portscan becomes a DoS against the listenall socket.
A testing program is available in perforce:
http://p4web.freebsd.org/@md=d&cd=//depot/projects/soc2007/mharvan-mtund/sys.patches/test_catchall/&c=xpc@//depot/projects/soc2007/mharvan-mtund/sys.patches/test_catchall/tcatchalld.c?ac=22
Note that the tcp listenall feature would be usefull to other programs
beyond mtund, i.e., a modified ssh daemon for people who won't be able
to run mtund, but will be able to run ssh.
Matus
patch:
Index: netinet/tcp.h
===================================================================
RCS file: /home/ncvs/src/sys/netinet/tcp.h,v
retrieving revision 1.40
diff -d -u -r1.40 tcp.h
--- netinet/tcp.h 25 May 2007 21:28:49 -0000 1.40
+++ netinet/tcp.h 8 Sep 2007 10:35:57 -0000
@@ -147,6 +147,7 @@
#define TCP_NOOPT 0x08 /* don't use TCP options */
#define TCP_MD5SIG 0x10 /* use MD5 digests (RFC2385) */
#define TCP_INFO 0x20 /* retrieve tcp_info structure */
+#define TCP_LISTENALL 0x40 /* listen on all unused TCP ports */
#define TCPI_OPT_TIMESTAMPS 0x01
#define TCPI_OPT_SACK 0x02
Index: netinet/tcp_input.c
===================================================================
RCS file: /home/ncvs/src/sys/netinet/tcp_input.c,v
retrieving revision 1.367
diff -d -u -r1.367 tcp_input.c
--- netinet/tcp_input.c 30 Jul 2007 11:06:41 -0000 1.367
+++ netinet/tcp_input.c 8 Sep 2007 10:35:57 -0000
@@ -144,9 +144,15 @@
SYSCTL_INT(_net_inet_tcp, OID_AUTO, recvbuf_max, CTLFLAG_RW,
&tcp_autorcvbuf_max, 0, "Max size of automatic receive buffer");
+static int listenalllim = 5;
+SYSCTL_INT(_net_inet_tcp, OID_AUTO, listenalllim, CTLFLAG_RW | CTLFLAG_SECURE,
+ &listenalllim, 0,
+ "Rate limit on sockets created by the TCP_LISTENALL socket");
+
struct inpcbhead tcb;
#define tcb6 tcb /* for KAME src sync over BSD*'s */
struct inpcbinfo tcbinfo;
+struct inpcb *inp_tlistenall; /* listening on all unused TCP ports */
static void tcp_dooptions(struct tcpopt *, u_char *, int, int);
static void tcp_do_segment(struct mbuf *, struct tcphdr *,
@@ -258,6 +264,11 @@
struct tcphdr tcp_savetcp;
short ostate = 0;
#endif
+ static struct rate {
+ struct timeval lasttime;
+ int curpps;
+ } listenallr;
+
#ifdef INET6
isipv6 = (mtod(m, struct ip *)->ip_v == 6) ? 1 : 0;
@@ -460,6 +471,36 @@
goto dropunlock;
}
#endif /* IPSEC */
+
+ /* listenall socket */
+ if ((inp == NULL) && (inp_tlistenall != NULL)) {
+#ifdef DIAGNOSTIC
+ printf("listenall socket used (0x%x)\n",
+ (unsigned int)inp_tlistenall);
+ char dbuf[INET_ADDRSTRLEN], sbuf[INET_ADDRSTRLEN];
+ strcpy(dbuf, inet_ntoa(ip->ip_dst));
+ strcpy(sbuf, inet_ntoa(ip->ip_src));
+ printf("\tip_src: %s, sport: %hu\n\tip_dst: %s, dport: %hu\n",
+ sbuf, ntohs(th->th_sport), dbuf, ntohs(th->th_dport));
+#endif
+ /* do rate limiting for SYN packets */
+ if (thflags & TH_SYN) {
+ if (listenalllim > 0)
+ if (ppsratecheck(&listenallr.lasttime,
+ &listenallr.curpps, listenalllim))
+ inp = inp_tlistenall;
+#ifdef DIAGNOSTIC
+ else
+ printf("ppsratecheck limited "
+ "tcp_listenall\n");
+#endif
+#ifdef DIAGNOSTIC
+ else
+ printf("ppsratecheck limited tcp_listenall\n");
+#endif
+ } else
+ inp = inp_tlistenall;
+ }
/*
* If the INPCB does not exist then all data in the incoming
Index: netinet/tcp_subr.c
===================================================================
RCS file: /home/ncvs/src/sys/netinet/tcp_subr.c,v
retrieving revision 1.296
diff -d -u -r1.296 tcp_subr.c
--- netinet/tcp_subr.c 16 Aug 2007 01:35:55 -0000 1.296
+++ netinet/tcp_subr.c 8 Sep 2007 10:35:57 -0000
@@ -264,6 +264,7 @@
tcp_rexmit_slop = TCPTV_CPU_VAR;
tcp_inflight_rttthresh = TCPTV_INFLIGHT_RTTTHRESH;
tcp_finwait2_timeout = TCPTV_FINWAIT2_TIMEOUT;
+ inp_tlistenall = NULL;
INP_INFO_LOCK_INIT(&tcbinfo, "tcp");
LIST_INIT(&tcb);
Index: netinet/tcp_usrreq.c
===================================================================
RCS file: /home/ncvs/src/sys/netinet/tcp_usrreq.c,v
retrieving revision 1.160
diff -d -u -r1.160 tcp_usrreq.c
--- netinet/tcp_usrreq.c 30 Jul 2007 11:06:41 -0000 1.160
+++ netinet/tcp_usrreq.c 8 Sep 2007 10:35:57 -0000
@@ -48,6 +48,7 @@
#endif /* INET6 */
#include <sys/socket.h>
#include <sys/socketvar.h>
+#include <sys/priv.h>
#include <sys/protosw.h>
#include <sys/proc.h>
#include <sys/jail.h>
@@ -162,6 +163,13 @@
KASSERT(so->so_pcb == inp, ("tcp_detach: so_pcb != inp"));
KASSERT(inp->inp_socket == so, ("tcp_detach: inp_socket != so"));
+ if (inp == inp_tlistenall) {
+#ifdef DIAGNOSTIC
+ printf("deactivating TCP_LISTENALL - socket closed\n");
+#endif
+ inp_tlistenall = NULL;
+ }
+
tp = intotcpcb(inp);
if (inp->inp_vflag & INP_TIMEWAIT) {
@@ -1338,6 +1346,29 @@
error = EINVAL;
break;
+ case TCP_LISTENALL:
+ error = sooptcopyin(sopt, &optval, sizeof optval,
+ sizeof optval);
+ if (error)
+ break;
+
+ priv_check(curthread,
+ PRIV_NETINET_TCP_LISTENALL);
+ if (error != 0)
+ break;
+
+ if (optval > 0) /* enable LISTENALL */
+ if (inp_tlistenall == NULL)
+ inp_tlistenall = inp;
+ else
+ error = EBUSY;
+
+ else /* disable LISTENALL */
+ if (inp_tlistenall == inp)
+ inp_tlistenall = NULL;
+
+ break;
+
default:
error = ENOPROTOOPT;
break;
@@ -1371,6 +1402,13 @@
case TCP_INFO:
tcp_fill_info(tp, &ti);
error = sooptcopyout(sopt, &ti, sizeof ti);
+ break;
+ case TCP_LISTENALL:
+ if (inp == inp_tlistenall)
+ optval = 1;
+ else
+ optval = 0;
+ error = sooptcopyout(sopt, &optval, sizeof optval);
break;
default:
error = ENOPROTOOPT;
Index: netinet/tcp_var.h
===================================================================
RCS file: /home/ncvs/src/sys/netinet/tcp_var.h,v
retrieving revision 1.155
diff -d -u -r1.155 tcp_var.h
--- netinet/tcp_var.h 28 Jul 2007 12:20:39 -0000 1.155
+++ netinet/tcp_var.h 8 Sep 2007 10:35:57 -0000
@@ -493,6 +493,7 @@
extern struct inpcbhead tcb; /* head of queue of active tcpcb's */
extern struct inpcbinfo tcbinfo;
+extern struct inpcb *inp_tlistenall; /* listening on all unused TCP ports */
extern struct tcpstat tcpstat; /* tcp statistics */
extern int tcp_log_in_vain;
extern int tcp_mssdflt; /* XXX */
Index: sys/priv.h
===================================================================
RCS file: /home/ncvs/src/sys/sys/priv.h,v
retrieving revision 1.15
diff -d -u -r1.15 priv.h
--- sys/priv.h 18 Jun 2007 07:54:27 -0000 1.15
+++ sys/priv.h 8 Sep 2007 10:35:58 -0000
@@ -374,6 +374,7 @@
#define PRIV_NETINET_ALIFETIME6 502 /* Administer IPv6 address lifetimes. */
#define PRIV_NETINET_IPSEC 503 /* Administer IPSEC. */
#define PRIV_NETINET_REUSEPORT 504 /* Allow [rapid] port/address reuse. */
+#define PRIV_NETINET_TCP_LISTENALL 505 /* Allow setting TCP_LISTENALL */
/*
* IPX/SPX privileges.
-------------- next part --------------
A non-text attachment was scrubbed...
Name: not available
Type: application/pgp-signature
Size: 187 bytes
Desc: not available
Url : http://lists.freebsd.org/pipermail/freebsd-net/attachments/20070909/3db16c67/attachment.pgp
More information about the freebsd-net
mailing list