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 via email to