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) {