patch-pre2.0.7 linux/net/ipv4/af_inet.c

Next file: linux/net/ipv4/arp.c
Previous file: linux/net/ipv4/Config.in
Back to the patch index
Back to the overall index

diff -u --recursive --new-file pre2.0.6/linux/net/ipv4/af_inet.c linux/net/ipv4/af_inet.c
@@ -49,6 +49,7 @@
  *		Alan Cox	:	Locked down bind (see security list).
  *		Alan Cox	:	Loosened bind a little.
  *		Mike McLagan	:	ADD/DEL DLCI Ioctls
+ *	Willy Konynenberg	:	Transparent proxying support.
  *
  *		This program is free software; you can redistribute it and/or
  *		modify it under the terms of the GNU General Public License
@@ -160,10 +161,10 @@
 	struct sock *sk;
 
 	if (base == 0) 
-		base = PROT_SOCK+1+(start % 1024);
+		base = PROT_SOCK+1+(start & 1023);
 	if (base <= PROT_SOCK) 
 	{
-		base += PROT_SOCK+(start % 1024);
+		base += PROT_SOCK+(start & 1023);
 	}
 
 	/*
@@ -181,7 +182,7 @@
 		}
 		if (j == 0) 
 		{
-			start =(i+1+start )%1024;
+			start =(i+1+start )&1023;
 			return(i+base+1);
 		}
 		if (j < size) 
@@ -841,11 +842,25 @@
 	}
 	
 	chk_addr_ret = ip_chk_addr(addr->sin_addr.s_addr);
+#ifdef CONFIG_IP_TRANSPARENT_PROXY
+	/*
+	 * Superuser may bind to any address to allow transparent proxying.
+	 */
+	if (addr->sin_addr.s_addr != 0 && chk_addr_ret != IS_MYADDR && chk_addr_ret != IS_MULTICAST && chk_addr_ret != IS_BROADCAST && !suser())
+#else
 	if (addr->sin_addr.s_addr != 0 && chk_addr_ret != IS_MYADDR && chk_addr_ret != IS_MULTICAST && chk_addr_ret != IS_BROADCAST)
+#endif
 		return(-EADDRNOTAVAIL);	/* Source address MUST be ours! */
 
+#ifndef CONFIG_IP_TRANSPARENT_PROXY
+	/*
+	 * Am I just thick or is this test really always true after the one
+	 * above?  Just taking the test out appears to be the easiest way to
+	 * make binds to remote addresses for transparent proxying work.
+	 */
 	if (chk_addr_ret || addr->sin_addr.s_addr == 0)
 	{
+#endif
 		/*
 		 *      We keep a pair of addresses. rcv_saddr is the one
 		 *      used by get_sock_*(), and saddr is used for transmit.
@@ -859,7 +874,9 @@
 			sk->saddr = 0;  /* Use device */
 		else
 			sk->saddr = addr->sin_addr.s_addr;
+#ifndef CONFIG_IP_TRANSPARENT_PROXY
 	}
+#endif
 	if(sock->type != SOCK_RAW)
 	{
 		/* Make sure we are allowed to bind here. */
@@ -1332,6 +1349,36 @@
 	return(0);
 }
 
+#ifdef CONFIG_IP_TRANSPARENT_PROXY
+/*
+ * Some routines for the for loop in get_sock which sometimes needs to walk
+ * two linked lists in sequence.  Could use macros as well.
+ * Does anyone know a nicer way to code this?
+ */
+static __inline__ struct sock *secondlist(unsigned short hpnum, struct sock *s,
+				int *pfirstpass, struct proto *prot)
+{
+	if (hpnum && s == NULL && (*pfirstpass)-- )
+		return prot->sock_array[hpnum & (SOCK_ARRAY_SIZE - 1)];
+	else
+		return s;
+}
+static __inline__ struct sock *get_sock_loop_init(unsigned short hnum,
+			unsigned short hpnum, struct sock *s,
+			int *pfirstpass, struct proto *prot)
+{
+	s = prot->sock_array[hnum & (SOCK_ARRAY_SIZE - 1)];
+	return secondlist(hpnum, s, pfirstpass, prot);
+}
+static __inline__ struct sock *get_sock_loop_next(unsigned short hnum,
+			unsigned short hpnum, struct sock *s,
+			int *pfirstpass, struct proto *prot)
+{
+	s = s->next;
+	return secondlist(hpnum, s, pfirstpass, prot);
+}
+#endif
+
 /*
  * This routine must find a socket given a TCP or UDP header.
  * Everything is assumed to be in net order.
@@ -1343,14 +1390,22 @@
 
 struct sock *get_sock(struct proto *prot, unsigned short num,
 				unsigned long raddr,
-				unsigned short rnum, unsigned long laddr)
+				unsigned short rnum, unsigned long laddr,
+				unsigned long paddr, unsigned short pnum)
 {
-	struct sock *s;
+	struct sock *s = 0;
 	struct sock *result = NULL;
 	int badness = -1;
 	unsigned short hnum;
+#ifdef CONFIG_IP_TRANSPARENT_PROXY
+	unsigned short hpnum;
+	int firstpass = 1;
+#endif 
 
 	hnum = ntohs(num);
+#ifdef CONFIG_IP_TRANSPARENT_PROXY
+	hpnum = ntohs(pnum);
+#endif 
 
 	/*
 	 * SOCK_ARRAY_SIZE must be a power of two.  This will work better
@@ -1361,19 +1416,43 @@
 	 * socket number when we choose an arbitrary one.
 	 */
 
+#ifdef CONFIG_IP_TRANSPARENT_PROXY
+	for(s = get_sock_loop_init(hnum, hpnum, s, &firstpass, prot);
+		s != NULL;
+		s = get_sock_loop_next(hnum, hpnum, s, &firstpass, prot))
+#else
 	for(s = prot->sock_array[hnum & (SOCK_ARRAY_SIZE - 1)];
 			s != NULL; s = s->next) 
+#endif
 	{
 		int score = 0;
 
+#ifdef CONFIG_IP_TRANSPARENT_PROXY
+		/* accept the addressed port or the redirect (proxy) port */
+		if (s->num != hnum && (hpnum == 0 || s->num != hpnum))
+#else
 		if (s->num != hnum) 
+#endif
 			continue;
 
 		if(s->dead && (s->state == TCP_CLOSE))
 			continue;
 		/* local address matches? */
 		if (s->rcv_saddr) {
+#ifdef CONFIG_IP_TRANSPARENT_PROXY
+			/*
+			 * If this is redirected traffic, it must either
+			 * match on the redirected port/ip-address or on
+			 * the actual destination, not on a mixture.
+			 * There must be a simpler way to express this...
+			 */
+			if (hpnum
+			    ? ((s->num != hpnum || s->rcv_saddr != paddr)
+			      && (s->num != hnum || s->rcv_saddr != laddr))
+			    : (s->rcv_saddr != laddr))
+#else
 			if (s->rcv_saddr != laddr)
+#endif
 				continue;
 			score++;
 		}
@@ -1390,11 +1469,23 @@
 			score++;
 		}
 		/* perfect match? */
+#ifdef CONFIG_IP_TRANSPARENT_PROXY
+		if (score == 3 && s->num == hnum)
+#else
 		if (score == 3)
+#endif
 			return s;
 		/* no, check if this is the best so far.. */
 		if (score <= badness)
 			continue;
+#ifdef CONFIG_IP_TRANSPARENT_PROXY
+		/* don't accept near matches on the actual destination
+		 * port with IN_ADDR_ANY for redirected traffic, but do
+		 * allow explicit remote address listens.  (disputable)
+		 */
+		if (hpnum && s->num != hpnum && !s->rcv_saddr)
+			continue;
+#endif
 		result = s;
 		badness = score;
   	}

FUNET's LINUX-ADM group, linux-adm@nic.funet.fi
TCL-scripts by Sam Shen, slshen@lbl.gov with Sam's (original) version
of this