[Date Prev][Date Next] [Thread Prev][Thread Next] [Date Index] [Thread Index]

SO_BINDTODEVICE patch for 2.0.30



Due to popular demand here a post of the SO_BINDTODEVICE patch. It will
enable the dhcpd package for Debian to serve multiple Network Cards.

Patch and more information can be found at:

http://gunpowder.Stanford.EDU/software/LinuxChanges.html

On Tue, 5 Aug 1997, Trent Johnson wrote:

>Hi,
>
>Could you send me a copy of the so_bindtodevice patch.  Am I correct in
>think that this patch will allow me to run a dhcp server that servers
>networks on two different ethernet cards?
>
>Trent

--- +++ --- +++ --- +++ --- +++ --- +++ --- +++ --- +++ ---
diff -u -r linux/drivers/net/new_tunnel.c linux-modified/drivers/net/new_tunnel.c
--- linux/drivers/net/new_tunnel.c	Sun Mar 24 02:47:39 1996
+++ linux-modified/drivers/net/new_tunnel.c	Fri May  2 10:48:35 1997
@@ -161,7 +161,7 @@
 	 *  routing tables
 	 */
 	iph = (struct iphdr *) skb->data;
-	if ((rt = ip_rt_route(iph->daddr, 0)) == NULL)
+	if ((rt = ip_rt_route(iph->daddr, 0, skb->sk?skb->sk->bound_device:NULL)) == NULL)
 	{ 
 		/* No route to host */
 		/* Where did the packet come from? */
@@ -194,7 +194,7 @@
 	}
 	ip_rt_put(rt);
 
-	if ((rt = ip_rt_route(target, 0)) == NULL)
+	if ((rt = ip_rt_route(target, 0, skb->sk?skb->sk->bound_device:NULL)) == NULL)
 	{ 
 		/* No route to host */
 		/* Where did the packet come from? */
diff -u -r linux/include/asm-i386/socket.h linux-modified/include/asm-i386/socket.h
--- linux/include/asm-i386/socket.h	Sun Mar 24 02:47:39 1996
+++ linux-modified/include/asm-i386/socket.h	Wed Apr 16 12:25:57 1997
@@ -22,4 +22,6 @@
 #define SO_BSDCOMPAT	14
 /* To add :#define SO_REUSEPORT 15 */
 
+#define SO_BINDTODEVICE 25
+
 #endif /* _ASM_SOCKET_H */
diff -u -r linux/include/linux/route.h linux-modified/include/linux/route.h
--- linux/include/linux/route.h	Tue Oct  8 09:48:40 1996
+++ linux-modified/include/linux/route.h	Thu Apr 17 15:54:24 1997
@@ -52,6 +52,7 @@
 #define RTF_WINDOW	0x0080		/* per route window clamping	  */
 #define RTF_IRTT	0x0100		/* Initial round trip time	  */
 #define RTF_REJECT	0x0200		/* Reject route			  */
+#define RTF_NOTCACHED	0x0400		/* this route isn't cached        */
 
 /*
  *	This structure is passed from the kernel to user space by netlink
diff -u -r linux/include/net/route.h linux-modified/include/net/route.h
--- linux/include/net/route.h	Fri Mar 28 16:10:57 1997
+++ linux-modified/include/net/route.h	Tue Apr 22 14:46:46 1997
@@ -13,6 +13,7 @@
  *		Alan Cox	:	Reformatted. Added ip_rt_local()
  *		Alan Cox	:	Support for TCP parameters.
  *		Alexey Kuznetsov:	Major changes for new routing code.
+ *              Elliot Poger    :       Added support for SO_BINDTODEVICE.
  *
  *	FIXME:
  *		Make atomic ops more generic and hide them in asm/...
@@ -83,7 +84,7 @@
 extern void		ip_rt_flush(struct device *dev);
 extern void		ip_rt_update(int event, struct device *dev);
 extern void		ip_rt_redirect(__u32 src, __u32 dst, __u32 gw, struct device *dev);
-extern struct rtable	*ip_rt_slow_route(__u32 daddr, int local);
+extern struct rtable	*ip_rt_slow_route(__u32 daddr, int local, struct device *dev);
 extern int		rt_get_info(char * buffer, char **start, off_t offset, int length, int dummy);
 extern int		rt_cache_get_info(char *buffer, char **start, off_t offset, int length, int dummy);
 extern int		ip_rt_ioctl(unsigned int cmd, void *arg);
@@ -131,9 +132,9 @@
 #endif
 
 #ifdef CONFIG_KERNELD
-extern struct rtable * ip_rt_route(__u32 daddr, int local);
+extern struct rtable * ip_rt_route(__u32 daddr, int local, struct device *dev);
 #else
-extern __inline__ struct rtable * ip_rt_route(__u32 daddr, int local)
+extern __inline__ struct rtable * ip_rt_route(__u32 daddr, int local, struct device *dev)
 #ifndef MODULE
 {
 	struct rtable * rth;
@@ -142,7 +143,8 @@
 
 	for (rth=ip_rt_hash_table[ip_rt_hash_code(daddr)^local]; rth; rth=rth->rt_next)
 	{
-		if (rth->rt_dst == daddr)
+		/* If an interface is specified, make sure this route points to it. */
+		if ( (rth->rt_dst == daddr) && ((dev==NULL) || (dev==rth->rt_dev)) )
 		{
 			rth->rt_lastuse = jiffies;
 			atomic_inc(&rth->rt_use);
@@ -151,23 +153,23 @@
 			return rth;
 		}
 	}
-	return ip_rt_slow_route (daddr, local);
+	return ip_rt_slow_route (daddr, local, dev);
 }
 #else
 ;
 #endif
 #endif
 
-extern __inline__ struct rtable * ip_check_route(struct rtable ** rp,
-						       __u32 daddr, int local)
+extern __inline__ struct rtable * ip_check_route(struct rtable ** rp, __u32 daddr, 
+						 int local, struct device *dev)
 {
 	struct rtable * rt = *rp;
 
-	if (!rt || rt->rt_dst != daddr || !(rt->rt_flags&RTF_UP)
+	if (!rt || rt->rt_dst != daddr || !(rt->rt_flags&RTF_UP) || (dev!=NULL)
 	    || ((local==1)^((rt->rt_flags&RTF_LOCAL) != 0)))
 	{
 		ip_rt_put(rt);
-		rt = ip_rt_route(daddr, local);
+		rt = ip_rt_route(daddr, local, dev);
 		*rp = rt;
 	}
 	return rt;
diff -u -r linux/include/net/sock.h linux-modified/include/net/sock.h
--- linux/include/net/sock.h	Tue Apr  8 08:47:47 1997
+++ linux-modified/include/net/sock.h	Tue Apr 22 14:41:54 1997
@@ -23,6 +23,7 @@
  *	Pauline Middelink	:	identd support
  *		Alan Cox	:	Eliminate low level recv/recvfrom
  *		David S. Miller	:	New socket lookup architecture for ISS.
+ *              Elliot Poger    :       New field for SO_BINDTODEVICE option.
  *
  *		This program is free software; you can redistribute it and/or
  *		modify it under the terms of the GNU General Public License
@@ -192,6 +193,7 @@
 				broadcast,
 				nonagle,
 				bsdism;
+	struct device           * bound_device;
 	unsigned long	        lingertime;
 	int			proc;
 
diff -u -r linux/net/core/sock.c linux-modified/net/core/sock.c
--- linux/net/core/sock.c	Tue Oct 29 17:42:42 1996
+++ linux-modified/net/core/sock.c	Thu Apr 17 15:54:28 1997
@@ -71,6 +71,7 @@
  *		Alan Cox	: 	Generic socket allocation to make hooks
  *					easier (suggested by Craig Metz).
  *		Michael Pall	:	SO_ERROR returns positive errno again
+ *              Elliot Poger    :       Added support for SO_BINDTODEVICE.
  *
  * To Fix:
  *
@@ -128,6 +129,7 @@
 	int valbool;
 	int err;
 	struct linger ling;
+	struct ifreq req;
 
 	/*
 	 *	Options without arguments
@@ -233,6 +235,34 @@
 			sk->bsdism = valbool;
 			return 0;
 			
+ 	        case SO_BINDTODEVICE:
+ 			/* Bind this socket to a particular device like "eth0",
+ 			 * as specified in an ifreq structure.  If the device 
+ 			 * is "", socket is NOT bound to a device. */
+ 			if (!valbool) {
+ 				sk->bound_device = NULL;
+ 			} else {
+ 				err=verify_area(VERIFY_READ,optval,sizeof(req));
+ 				if(err)
+ 					return err;
+ 				memcpy_fromfs(&req,optval,sizeof(req));
+
+				/* Remove any cached route for this socket. */
+				if (sk->ip_route_cache) {
+					ip_rt_put(sk->ip_route_cache);
+					sk->ip_route_cache=NULL;
+				}
+
+ 				if (*(req.ifr_name) == '\0') {
+ 					sk->bound_device = NULL;
+ 				} else {
+ 					sk->bound_device = dev_get(req.ifr_name);
+ 					if (sk->bound_device == NULL)
+ 						return -EINVAL;
+ 				}
+ 			}
+  			return 0;
+
 		default:
 		  	return(-ENOPROTOOPT);
   	}
diff -u -r linux/net/ipv4/arp.c linux-modified/net/ipv4/arp.c
--- linux/net/ipv4/arp.c	Tue Apr  8 08:47:47 1997
+++ linux-modified/net/ipv4/arp.c	Thu Apr 17 09:50:33 1997
@@ -1975,7 +1975,7 @@
 				memcpy(ha, proxy_entry->ha, dev->addr_len);
 				arp_unlock();
 
-				rt = ip_rt_route(tip, 0);
+				rt = ip_rt_route(tip, 0, NULL);
 				if (rt  && rt->rt_dev != dev)
 					arp_send(ARPOP_REPLY,ETH_P_ARP,sip,dev,tip,sha,ha,sha);
 				ip_rt_put(rt);
@@ -2048,7 +2048,7 @@
 		if (!dev)
 		{
 			struct rtable * rt;
-			rt = ip_rt_route(ip, 0);
+			rt = ip_rt_route(ip, 0, NULL);
 			if (!rt)
 				return -ENETUNREACH;
 			dev = rt->rt_dev;
diff -u -r linux/net/ipv4/icmp.c linux-modified/net/ipv4/icmp.c
--- linux/net/ipv4/icmp.c	Tue Apr  8 08:47:47 1997
+++ linux-modified/net/ipv4/icmp.c	Wed Apr 16 13:12:09 1997
@@ -39,6 +39,7 @@
  *		Thomas Quinot	:	ICMP Dest Unreach codes up to 15 are
  *					valid (RFC 1812).
  *		Alan Cox	:	Spoofing and junk icmp protections.
+ *              Elliot Poger    :       Added support for SO_BINDTODEVICE.
  *
  *
  * RFC1122 (Host Requirements -- Comm. Layer) Status:
@@ -992,8 +993,8 @@
  */
 
 /* This should work with the new hashes now. -DaveM */
-extern struct sock *tcp_v4_lookup(u32 saddr, u16 sport, u32 daddr, u16 dport);
-extern struct sock *udp_v4_lookup(u32 saddr, u16 sport, u32 daddr, u16 dport);
+extern struct sock *tcp_v4_lookup(u32 saddr, u16 sport, u32 daddr, u16 dport, struct device *dev);
+extern struct sock *udp_v4_lookup(u32 saddr, u16 sport, u32 daddr, u16 dport, struct device *dev);
 
 int icmp_chkaddr(struct sk_buff *skb)
 {
@@ -1009,7 +1010,8 @@
 			{
 			struct tcphdr *th = (struct tcphdr *)(((unsigned char *)iph)+(iph->ihl<<2));
 
-			sk = tcp_v4_lookup(iph->saddr, th->source, iph->daddr, th->dest);
+			sk = tcp_v4_lookup(iph->saddr, th->source, iph->daddr, th->dest,
+					   skb->dev);
 			if (!sk) return 0;
 			if (sk->saddr != iph->saddr) return 0;
 			if (sk->daddr != iph->daddr) return 0;
@@ -1023,7 +1025,8 @@
 			{
 			struct udphdr *uh = (struct udphdr *)(((unsigned char *)iph)+(iph->ihl<<2));
 
-			sk = udp_v4_lookup(iph->saddr, uh->source, iph->daddr, uh->dest);
+			sk = udp_v4_lookup(iph->saddr, uh->source, iph->daddr, uh->dest,
+					   skb->dev);
 			if (!sk) return 0;
 			if (sk->saddr != iph->saddr && ip_chk_addr(iph->saddr) != IS_MYADDR)
 				return 0;
diff -u -r linux/net/ipv4/ip_alias.c linux-modified/net/ipv4/ip_alias.c
--- linux/net/ipv4/ip_alias.c	Mon May 13 02:15:24 1996
+++ linux-modified/net/ipv4/ip_alias.c	Thu Apr 17 09:51:04 1997
@@ -102,7 +102,7 @@
 	 *	net_alias module will check if returned device is main_dev's alias
 	 */
 
-	rt = ip_rt_route(addr, 0);
+	rt = ip_rt_route(addr, 0, NULL);
 	if(rt)
 	{
 		dev=rt->rt_dev;
diff -u -r linux/net/ipv4/ip_forward.c linux-modified/net/ipv4/ip_forward.c
--- linux/net/ipv4/ip_forward.c	Tue Apr  8 08:47:47 1997
+++ linux-modified/net/ipv4/ip_forward.c	Thu Apr 17 09:52:28 1997
@@ -177,7 +177,7 @@
 		 * and give it to the IP sender for further processing.
 		 */
 
-		rt = ip_rt_route(target_addr, 0);
+		rt = ip_rt_route(target_addr, 0, NULL);
 
 		if (rt == NULL)
 		{
diff -u -r linux/net/ipv4/ip_output.c linux-modified/net/ipv4/ip_output.c
--- linux/net/ipv4/ip_output.c	Tue Oct 29 17:42:42 1996
+++ linux-modified/net/ipv4/ip_output.c	Fri Apr 18 15:43:59 1997
@@ -26,6 +26,7 @@
  *		Alexander Demenshin:	Missing sk/skb free in ip_queue_xmit
  *					(in case if packet not accepted by
  *					output firewall rules)
+ *              Elliot Poger    :       Added support for SO_BINDTODEVICE.
  */
 
 #include <asm/segment.h>
@@ -218,7 +219,7 @@
 #endif
 	if (rp)
 	{
-		rt = ip_check_route(rp, daddr, skb->localroute);
+		rt = ip_check_route(rp, daddr, skb->localroute, *dev);
 		/*
 		 * If rp != NULL rt_put following below should not
 		 * release route, so that...
@@ -227,7 +228,7 @@
 			atomic_inc(&rt->rt_refcnt);
 	}
 	else
-		rt = ip_rt_route(daddr, skb->localroute);
+		rt = ip_rt_route(daddr, skb->localroute, *dev);
 
 
 	if (*dev == NULL)
@@ -590,7 +591,7 @@
 #endif	
 		rt = ip_check_route(&sk->ip_route_cache, daddr,
 				    sk->localroute || (flags&MSG_DONTROUTE) ||
-				    (opt && opt->is_strictroute));
+				    (opt && opt->is_strictroute), sk->bound_device);
 		if (rt == NULL) 
 		{
 			ip_statistics.IpOutNoRoutes++;
diff -u -r linux/net/ipv4/ip_sockglue.c linux-modified/net/ipv4/ip_sockglue.c
--- linux/net/ipv4/ip_sockglue.c	Tue Apr  8 08:47:47 1997
+++ linux-modified/net/ipv4/ip_sockglue.c	Fri Apr 18 15:44:41 1997
@@ -12,6 +12,7 @@
  *		Martin Mares	:	TOS setting fixed.
  *		Alan Cox	:	Fixed a couple of oopses in Martin's 
  *					TOS tweaks.
+ *              Elliot Poger    :       Added support for SO_BINDTODEVICE.
  */
 
 #include <linux/config.h>
@@ -293,7 +294,7 @@
 				/*
 				 *	Not set so scan.
 				 */
-				if((rt=ip_rt_route(mreq.imr_multiaddr.s_addr,0))!=NULL)
+				if((rt=ip_rt_route(mreq.imr_multiaddr.s_addr,0,sk->bound_device))!=NULL)
 				{
 					dev=rt->rt_dev;
 					atomic_dec(&rt->rt_use);
@@ -345,7 +346,7 @@
  
 			if(mreq.imr_interface.s_addr==INADDR_ANY) 
 			{
-				if((rt=ip_rt_route(mreq.imr_multiaddr.s_addr,0))!=NULL)
+				if((rt=ip_rt_route(mreq.imr_multiaddr.s_addr,0,sk->bound_device))!=NULL)
 			        {
 					dev=rt->rt_dev;
 					atomic_dec(&rt->rt_use);
diff -u -r linux/net/ipv4/rarp.c linux-modified/net/ipv4/rarp.c
--- linux/net/ipv4/rarp.c	Tue Oct 29 17:42:42 1996
+++ linux-modified/net/ipv4/rarp.c	Thu Apr 17 09:57:18 1997
@@ -354,7 +354,7 @@
  *	Is it reachable directly ?
  */
   
-	rt = ip_rt_route(ip, 0);
+	rt = ip_rt_route(ip, 0, NULL);
 	if (rt == NULL)
 		return -ENETUNREACH;
 	dev = rt->rt_dev;
diff -u -r linux/net/ipv4/route.c linux-modified/net/ipv4/route.c
--- linux/net/ipv4/route.c	Tue Apr  8 08:47:47 1997
+++ linux-modified/net/ipv4/route.c	Fri Apr 18 15:45:02 1997
@@ -42,6 +42,7 @@
  *		Bjorn Ekwall	:	Kerneld route support.
  *		Alan Cox	:	Multicast fixed (I hope)
  * 		Pavel Krauz	:	Limited broadcast fixed
+ *              Elliot Poger    :       Added support for SO_BINDTODEVICE.
  *
  *		This program is free software; you can redistribute it and/or
  *		modify it under the terms of the GNU General Public License
@@ -247,9 +248,11 @@
  *	  Host 193.233.7.129 is locally unreachable,
  *	  but old (<=1.3.37) code will send packets destined for it to eth1.
  *
+ * Calling routine can specify a particular interface by setting dev.  If dev==NULL,
+ * any interface will do.
  */
 
-static struct fib_node * fib_lookup_local(__u32 dst)
+static struct fib_node * fib_lookup_local(__u32 dst, struct device *dev)
 {
 	struct fib_zone * fz;
 	struct fib_node * f;
@@ -267,6 +270,8 @@
 		{
 			if ((dst ^ f->fib_dst) & fz->fz_mask)
 				continue;
+			if ( (dev != NULL) && (dev != f->fib_info->fib_dev) )
+				continue;
 			if (!(f->fib_info->fib_flags & RTF_GATEWAY))
 				return f;
 			longest_match_found = 1;
@@ -289,7 +294,7 @@
  *		route add -host 193.233.7.255 eth0
  */
 
-static struct fib_node * fib_lookup(__u32 dst)
+static struct fib_node * fib_lookup(__u32 dst, struct device *dev)
 {
 	struct fib_zone * fz;
 	struct fib_node * f;
@@ -305,6 +310,8 @@
 		{
 			if ((dst ^ f->fib_dst) & fz->fz_mask)
 				continue;
+			if ( (dev != NULL) && (dev != f->fib_info->fib_dev) )
+				continue;
 			return f;
 		}
 	}
@@ -1254,7 +1261,7 @@
 	struct rt_req * rtr;
 	struct rtable * rt;
 
-	rt = ip_rt_route(dst, 0);
+	rt = ip_rt_route(dst, 0, NULL);
 	if (!rt)
 		return;
 
@@ -1323,7 +1330,7 @@
 		if (rth->rt_gateway != daddr)
 		{
 			ip_rt_fast_unlock();
-			rtg = ip_rt_route(rth->rt_gateway, 0);
+			rtg = ip_rt_route(rth->rt_gateway, 0, NULL);
 			ip_rt_fast_lock();
 		}
 
@@ -1395,7 +1402,7 @@
    
  */
 
-struct rtable * ip_rt_slow_route (__u32 daddr, int local)
+struct rtable * ip_rt_slow_route (__u32 daddr, int local, struct device *dev)
 {
 	unsigned hash = ip_rt_hash_code(daddr)^local;
 	struct rtable * rth;
@@ -1415,9 +1422,9 @@
 	}
 
 	if (local)
-		f = fib_lookup_local(daddr);
+		f = fib_lookup_local(daddr, dev);
 	else
-		f = fib_lookup (daddr);
+		f = fib_lookup (daddr, dev);
 
 	if (f)
 	{
@@ -1490,7 +1497,15 @@
 		rth->rt_gateway = rth->rt_dst;
 
 	if (ip_rt_lock == 1)
-		rt_cache_add(hash, rth);
+	{
+		/* Don't add this to the rt_cache if a device was specified,
+		 * because we might have skipped better routes which didn't
+		 * point at the right device. */
+		if (dev != NULL)
+			rth->rt_flags |= RTF_NOTCACHED;
+		else
+			rt_cache_add(hash, rth);
+	}
 	else
 	{
 		rt_free(rth);
@@ -1507,9 +1522,14 @@
 {
 	if (rt)
 		atomic_dec(&rt->rt_refcnt);
+	
+	/* If this rtable entry is not in the cache, we'd better free it once the
+	 * refcnt goes to zero, because nobody else will... */
+	if ( rt && (rt->rt_flags & RTF_NOTCACHED) && (!rt->rt_refcnt) )
+		rt_free(rt);
 }
 
-struct rtable * ip_rt_route(__u32 daddr, int local)
+struct rtable * ip_rt_route(__u32 daddr, int local, struct device *dev)
 {
 	struct rtable * rth;
 
@@ -1517,7 +1537,8 @@
 
 	for (rth=ip_rt_hash_table[ip_rt_hash_code(daddr)^local]; rth; rth=rth->rt_next)
 	{
-		if (rth->rt_dst == daddr)
+		/* If a network device is specified, make sure this route points to it. */
+		if ( (rth->rt_dst == daddr) && ((dev==NULL) || (dev==rth->rt_dev)) )
 		{
 			rth->rt_lastuse = jiffies;
 			atomic_inc(&rth->rt_use);
@@ -1526,7 +1547,7 @@
 			return rth;
 		}
 	}
-	return ip_rt_slow_route (daddr, local);
+	return ip_rt_slow_route (daddr, local, dev);
 }
 
 /*
diff -u -r linux/net/ipv4/tcp.c linux-modified/net/ipv4/tcp.c
--- linux/net/ipv4/tcp.c	Tue Apr  8 08:47:47 1997
+++ linux-modified/net/ipv4/tcp.c	Fri Apr 18 15:45:42 1997
@@ -205,6 +205,7 @@
  *		Theodore Ts'o	:	Do secure TCP sequence numbers.
  *		David S. Miller	:	New socket lookup architecture for ISS.
  *					This code is dedicated to John Dyson.
+ *              Elliot Poger    :       Added support for SO_BINDTODEVICE.
  *					
  * To Fix:
  *		Fast path the code. Two things here - fix the window calculation
@@ -471,6 +472,12 @@
 			unsigned char state = sk2->state;
 			int sk2_reuse = sk2->reuse;
 
+			/* Two sockets can be bound to the same port if they're
+			 * bound to different interfaces... */
+			if (sk->bound_device != sk2->bound_device) {
+				continue;
+			}
+
 			if(!sk2->rcv_saddr || !sk->rcv_saddr) {
 				if((!sk2_reuse)			||
 				   (!sk_reuse)			||
@@ -2197,6 +2204,9 @@
 	buff->sk = sk;
 	buff->free = 0;
 	buff->localroute = sk->localroute;
+
+	/* If this socket is bound to a particular device, make sure we use it. */
+	dev = sk->bound_device;
 
 	/*
 	 *	Put in the IP header and routing stuff.
diff -u -r linux/net/ipv4/tcp_input.c linux-modified/net/ipv4/tcp_input.c
--- linux/net/ipv4/tcp_input.c	Tue Apr  8 08:47:47 1997
+++ linux-modified/net/ipv4/tcp_input.c	Tue Apr 22 15:46:29 1997
@@ -33,6 +33,7 @@
  *				:	with SYN flooding attacks.
  *		David S. Miller	:	New socket lookup architecture for ISS.
  *					This code is dedicated to John Dyson.
+ *              Elliot Poger    :       Added support for SO_BINDTODEVICE.
  */
 
 #include <linux/config.h>
@@ -229,20 +230,42 @@
  * XXX hash twice, once for local addresses bound, and once for
  * XXX the local address wildcarded (because the hash is different).
  */
-static struct sock *tcp_v4_lookup_longway(u32 daddr, unsigned short hnum)
+static struct sock *tcp_v4_lookup_longway(u32 daddr, unsigned short hnum,
+					  struct device *dev)
 {
 	struct sock *sk = tcp_listening_hash[tcp_lhashfn(hnum)];
 	struct sock *result = NULL;
+	int score, hiscore;
 
+	hiscore = 0;
 	for(; sk; sk = sk->next) {
 		if(sk->num == hnum) {
 			__u32 rcv_saddr = sk->rcv_saddr;
+			score = 1;
 
+			/* If this socket is bound to a particular IP address,
+			 * does the dest IPaddr of the packet match it? */
 			if(rcv_saddr) {
-				if(rcv_saddr == daddr)
-					return sk; /* Best possible match. */
-			} else if(!result)
+				if(rcv_saddr != daddr)
+					continue;
+				score++;
+			}
+
+			/* If this socket is bound to a particular interface,
+			 * did the packet come in on it? */
+			if (sk->bound_device) {
+				if (dev != sk->bound_device)
+					continue;
+				score++;
+			}
+
+			/* Check the score--max is 3. */
+			if (score == 3)
+				return sk; /* Best possible match. */
+			if (score > hiscore) {
+				hiscore = score;
 				result = sk;
+			}
 		}
 	}
 	return result;
@@ -252,7 +275,8 @@
  * we need not check it for TCP lookups anymore, thanks Alexey. -DaveM
  */
 static inline struct sock *__tcp_v4_lookup(struct tcphdr *th,
-					   u32 saddr, u16 sport, u32 daddr, u16 dport)
+					   u32 saddr, u16 sport, u32 daddr, 
+					   u16 dport, struct device *dev)
 {
 	unsigned short hnum = ntohs(dport);
 	struct sock *sk;
@@ -266,16 +290,18 @@
 		if(sk->daddr		== saddr		&& /* remote address */
 		   sk->dummy_th.dest	== sport		&& /* remote port    */
 		   sk->num		== hnum			&& /* local port     */
-		   sk->rcv_saddr	== daddr)		   /* local address  */
+		   sk->rcv_saddr	== daddr		&& /* local address  */
+		   ((sk->bound_device==NULL) || (sk->bound_device==dev))  )
 			goto hit; /* You sunk my battleship! */
-	sk = tcp_v4_lookup_longway(daddr, hnum);
+	sk = tcp_v4_lookup_longway(daddr, hnum, dev);
 hit:
 	return sk;
 }
  
-__inline__ struct sock *tcp_v4_lookup(u32 saddr, u16 sport, u32 daddr, u16 dport)
+__inline__ struct sock *tcp_v4_lookup(u32 saddr, u16 sport, u32 daddr, u16 dport,
+				      struct device *dev)
 {
-	return __tcp_v4_lookup(0, saddr, sport, daddr, dport);
+	return __tcp_v4_lookup(0, saddr, sport, daddr, dport, dev);
 }
 
 #ifdef CONFIG_IP_TRANSPARENT_PROXY
@@ -295,7 +321,8 @@
 
 struct sock *tcp_v4_proxy_lookup(unsigned short num, unsigned long raddr,
 				 unsigned short rnum, unsigned long laddr,
-				 unsigned long paddr, unsigned short pnum)
+				 unsigned long paddr, unsigned short pnum,
+				 struct device *dev)
 {
 	struct sock *s, *result = NULL;
 	int badness = -1;
@@ -327,7 +354,12 @@
 					continue;
 				score++;
 			}
-			if(score == 3 && s->num == hnum) {
+			if(s->bound_device) {
+				if (s->bound_device != dev)
+					continue;
+				score++;
+			}
+			if(score == 4 && s->num == hnum) {
 				result = s;
 				break;
 			} else if(score > badness && (s->num == hpnum || s->rcv_saddr)) {
@@ -758,7 +790,8 @@
 	 * 	Note use of sk->user_mss, since user has no direct access to newsk 
 	 */
 
-	rt = ip_rt_route(newsk->opt && newsk->opt->srr ? newsk->opt->faddr : saddr, 0);
+	rt = ip_rt_route(newsk->opt && newsk->opt->srr ? newsk->opt->faddr : saddr, 0,
+			 sk->bound_device);
 	newsk->ip_route_cache = rt;
 	
 	if(rt!=NULL && (rt->rt_flags&RTF_WINDOW))
@@ -1001,7 +1034,8 @@
 	newsk->ip_ttl=sk->ip_ttl;
 	newsk->ip_tos=skb->ip_hdr->tos;
 
-	rt = ip_rt_route(newsk->opt && newsk->opt->srr ? newsk->opt->faddr : saddr, 0);
+	rt = ip_rt_route(newsk->opt && newsk->opt->srr ? newsk->opt->faddr : saddr, 0,
+			 sk->bound_device);
 	newsk->ip_route_cache = rt;
 	
 	if (rt!=NULL && (rt->rt_flags&RTF_WINDOW))
@@ -2196,7 +2230,7 @@
 	struct tcphdr *th = (struct tcphdr *)(skb->h.raw + iph->ihl*4);
 	struct sock *sk;
 
-	sk = tcp_v4_lookup(iph->saddr, th->source, iph->daddr, th->dest);
+	sk = tcp_v4_lookup(iph->saddr, th->source, iph->daddr, th->dest, skb->dev);
 	if (!sk)
 		return 0;
 	/* 0 means accept all LOCAL addresses here, not all the world... */
@@ -2262,7 +2296,7 @@
 #ifdef CONFIG_SYN_COOKIES
 retry_search:
 #endif
-		sk = __tcp_v4_lookup(th, saddr, th->source, daddr, th->dest);
+		sk = __tcp_v4_lookup(th, saddr, th->source, daddr, th->dest, dev);
 		if (!sk)
 			goto no_tcp_socket;
 		skb->sk = sk;
@@ -2572,7 +2606,7 @@
 			sk->shutdown = SHUTDOWN_MASK;
 #ifdef CONFIG_IP_TRANSPARENT_PROXY
 			sk = tcp_v4_proxy_lookup(th->dest, saddr, th->source, daddr,
-						 dev->pa_addr, skb->redirport);
+						 dev->pa_addr, skb->redirport, dev);
 #else
 			sk = NULL;
 #endif
diff -u -r linux/net/ipv4/tcp_output.c linux-modified/net/ipv4/tcp_output.c
--- linux/net/ipv4/tcp_output.c	Tue Apr  8 08:47:47 1997
+++ linux-modified/net/ipv4/tcp_output.c	Fri Apr 18 15:46:58 1997
@@ -32,6 +32,7 @@
  *				  thus the outgoing device does as well, when
  *				  skb's are on the retransmit queue which still
  *				  refer to the old obsolete destination.
+ *              Elliot Poger    : Added support for SO_BINDTODEVICE.
  */
 
 #include <linux/config.h>
@@ -491,7 +492,8 @@
 			/* ANK: UGLY, but the bug, that was here, should be fixed.
 			 */
 			struct options *  opt = (struct options*)skb->proto_priv;
-			rt = ip_check_route(&sk->ip_route_cache, opt->srr?opt->faddr:iph->daddr, skb->localroute);
+			rt = ip_check_route(&sk->ip_route_cache, opt->srr?opt->faddr:iph->daddr,
+					    skb->localroute, sk->bound_device);
 	        }
 
 		iph->id = htons(ip_id_count++);
diff -u -r linux/net/ipv4/udp.c linux-modified/net/ipv4/udp.c
--- linux/net/ipv4/udp.c	Tue Apr  8 08:47:47 1997
+++ linux-modified/net/ipv4/udp.c	Tue Apr 22 15:44:38 1997
@@ -52,6 +52,7 @@
  *		David S. Miller	:	New socket lookup architecture for ISS.
  *					Last socket cache retained as it
  *					does have a high hit rate.
+ *              Elliot Poger    :       Added support for SO_BINDTODEVICE.
  *
  *
  *		This program is free software; you can redistribute it and/or
@@ -130,6 +131,12 @@
 		if((sk2->num == snum) && (sk2 != sk)) {
 			int sk2_reuse = sk2->reuse;
 
+			/* Two sockets can be bound to the same port if they're
+			 * bound to different interfaces... */
+			if (sk->bound_device != sk2->bound_device) {
+				continue;
+			}
+
 			if(!sk2->rcv_saddr || !sk->rcv_saddr) {
 				if((!sk2_reuse) || (!sk_reuse)) {
 					retval = 1;
@@ -255,7 +262,8 @@
 /* UDP is nearly always wildcards out the wazoo, it makes no sense to try
  * harder than this. -DaveM
  */
-__inline__ struct sock *udp_v4_lookup(u32 saddr, u16 sport, u32 daddr, u16 dport)
+__inline__ struct sock *udp_v4_lookup(u32 saddr, u16 sport, u32 daddr, u16 dport,
+				      struct device *dev)
 {
 	struct sock *sk, *result = NULL;
 	unsigned short hnum = ntohs(dport);
@@ -279,7 +287,15 @@
 					continue;
 				score++;
 			}
-			if(score == 3) {
+			/* If this socket is bound to a particular interface,
+			 * did the packet come in on it? */
+			if (sk->bound_device) {
+				if (dev == sk->bound_device)
+					score++;
+				else
+					continue;  /* mismatch--not this sock */
+			}
+			if(score == 4) {
 				result = sk;
 				break;
 			} else if(score > badness) {
@@ -308,7 +324,8 @@
 
 struct sock *udp_v4_proxy_lookup(unsigned short num, unsigned long raddr,
 				 unsigned short rnum, unsigned long laddr,
-				 unsigned long paddr, unsigned short pnum)
+				 unsigned long paddr, unsigned short pnum,
+				 struct device *dev)
 {
 	struct sock *s, *result = NULL;
 	int badness = -1;
@@ -340,7 +357,14 @@
 					continue;
 				score++;
 			}
-			if(score == 3 && s->num == hnum) {
+			/* If this socket is bound to a particular interface,
+			 * did the packet come in on it? */
+			if(s->bound_device) {
+				if (s->bound_device != dev)
+					continue;
+				score++;
+			}
+			if(score == 4 && s->num == hnum) {
 				result = s;
 				break;
 			} else if(score > badness && (s->num == hpnum || s->rcv_saddr)) {
@@ -363,7 +387,8 @@
 					     unsigned short num,
 					     unsigned long raddr,
 					     unsigned short rnum,
-					     unsigned long laddr)
+					     unsigned long laddr,
+					     struct device *dev)
 {
 	struct sock *s = sk;
 	unsigned short hnum = ntohs(num);
@@ -372,6 +397,7 @@
 		    (s->dead && (s->state == TCP_CLOSE))		||
 		    (s->daddr && s->daddr!=raddr)			||
 		    (s->dummy_th.dest != rnum && s->dummy_th.dest != 0) ||
+		    ((s->bound_device) && (s->bound_device!=dev))       ||
 		    (s->rcv_saddr  && s->rcv_saddr != laddr))
 			continue;
 		break;
@@ -408,7 +434,7 @@
 	
 	uh = (struct udphdr *)header;  
    
-	sk = udp_v4_lookup(daddr, uh->dest, saddr, uh->source);
+	sk = udp_v4_lookup(daddr, uh->dest, saddr, uh->source, NULL);
 	if (sk == NULL) 
 	  	return;	/* No socket for error */
   	
@@ -850,7 +876,7 @@
 	if(!sk->broadcast && ip_chk_addr(usin->sin_addr.s_addr)==IS_BROADCAST)
 		return -EACCES;			/* Must turn broadcast on first */
   	
-  	rt=ip_rt_route((__u32)usin->sin_addr.s_addr, sk->localroute);
+  	rt=ip_rt_route((__u32)usin->sin_addr.s_addr, sk->localroute, sk->bound_device);
   	if (rt==NULL)
   		return -ENETUNREACH;
   	if(!sk->saddr)
@@ -918,7 +944,7 @@
 	struct udphdr *uh = (struct udphdr *)(skb->h.raw + iph->ihl*4);
 	struct sock *sk;
 
-	sk = udp_v4_lookup(iph->saddr, uh->source, iph->daddr, uh->dest);
+	sk = udp_v4_lookup(iph->saddr, uh->source, iph->daddr, uh->dest, skb->dev);
 	if (!sk)
 		return 0;
 	/* 0 means accept all LOCAL addresses here, not all the world... */
@@ -940,7 +966,7 @@
 
 	SOCKHASH_LOCK();
 	sk = udp_hash[ntohs(uh->dest) & (UDP_HTABLE_SIZE - 1)];
-	sk = udp_v4_mcast_next(sk, uh->dest, saddr, uh->source, daddr);
+	sk = udp_v4_mcast_next(sk, uh->dest, saddr, uh->source, daddr, skb->dev);
 	if(sk) {
 		struct sock *sknext = NULL;
 
@@ -948,7 +974,7 @@
 			struct sk_buff *skb1 = skb;
 
 			sknext = udp_v4_mcast_next(sk->next, uh->dest, saddr,
-						   uh->source, daddr);
+						   uh->source, daddr, skb->dev);
 			if(sknext)
 				skb1 = skb_clone(skb, GFP_ATOMIC);
 
@@ -1066,10 +1092,10 @@
 #ifdef CONFIG_IP_TRANSPARENT_PROXY
 	if(skb->redirport)
 		sk = udp_v4_proxy_lookup(uh->dest, saddr, uh->source,
-					 daddr, dev->pa_addr, skb->redirport);
+					 daddr, dev->pa_addr, skb->redirport, dev);
 	else
 #endif
-	sk = udp_v4_lookup(saddr, uh->source, daddr, uh->dest);
+	sk = udp_v4_lookup(saddr, uh->source, daddr, uh->dest, dev);
 	
 	if (sk == NULL) 
   	{

Reply to: