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: