一种SO_BINDTODEVICE选项的替代方法

本文介绍了在Linux环境下,如何通过策略路由机制实现特定SOCKET绑定到指定网络接口,而无需使用SO_BINDTODEVICE选项和ROOT权限。通过编辑路由表文件、添加路由规则和策略路由,可以显式控制socket走不同的网络接口。这种方法改变了内核路由寻址过程,避免了使用SO_BINDTODEVICE的复杂性和权限问题。

因为业务逻辑需要,我在linux下尝试使用setsockopt方法中传入参数SO_BINDTODEVICE尝试将特定SOCKET绑定到指定的网络接口,需要ROOT权限实在是过于麻烦,而且经常莫名奇妙的不起作用,所以我采用了其它的办法来达到这个功能。

方法很简单,而且不许要root权限,简单说来就是使用linux中的策略路由机制。具体做法如下:

首先,编辑路由表文件,新建两个路由表,取名为tabeth0,tabwlan0

sudo vim /etc/iproute2/rt_tables

编辑完之后你的路由表看上去应该是这个样子的:

#
# reserved values
#
255     local
254     main
253     default
250     tabeth0
249     tabwlan0
0       unspec
#
# local
#
#1      inr.ruhep

然后在路由表中分别加入规则,走默认网关,我的eth0的ip是211.69.198.138,网关是211,69,198.254,我的wlan0接口ip是192.168.1.113,网关是192.168.1.1

sudo ip route add default via 211.69.198.254 dev eth0 table 250
sudo ip route add default via 192.168.1.1 dev wlan0 table 249

第三,加入在策略路由中加入规则,源地址是192.168.1.1的数据查询tabwlan0表,源地址是211.69.198.138的数据包查询tabeth0表

sudo ip rule add from 211.69.198.138 table tabeth0
sudo ip rule add from 192.168.1.113 table tabwlan0


最后,编个程序测试一下,此时不再需要使用SO_BINDDEVICE选项,只需要给自己的socket绑定相应的地址就可以了,关键代码如下

 struct sockaddr_in client_addr,client_addr1;
                bzero(&client_addr,sizeof(client_addr)); //把一段内存区的内容全部设置为0
                client_addr.sin_family = AF_INET;    //internet协议族
                client_addr.sin_addr.s_addr = htons("211.69.198.138");
                client_addr.sin_port = htons(51118); 
                client_addr1.sin_family = AF_INET; 
                 client_addr1.sin_addr.s_addr = htons("192.168.1.113");
                 client_addr1.sin_port = htons(51118);  
                sock=socket(AF_INET,SOCK_STREAM,0);
                sock1=socket(AF_INET,SOCK_STREAM,0);    
                bind(sock,(struct sockaddr*)&client_addr,sizeof(struct sockaddr));
                bind(sock1,(struct sockaddr*)&client_addr,sizeof(struct sockaddr));
                struct sockaddr_in server_addr;
                    bzero(&server_addr,sizeof(server_addr));
                    server_addr.sin_family = AF_INET;
                    inet_aton("211.69.198.222",&server_addr.sin_addr);
                    server_addr.sin_port = htons(44127);
                    socklen_t server_addr_length = sizeof(server_addr);
                    int ret1=connect(sock,(struct sockaddr*)&server_addr, server_addr_length);
                     int ret2=connect(sock1,(struct sockaddr*)&server_addr, server_addr_length);
                    printf("eth0 :%d\n",ret1);
                    printf("wlan0 :%d\n",ret2);


运行后可以看到输出:

eth0:0

eth1:0

说明可以显式控制socket走不同的网络接口,如果还不放心通过wireshark抓不同的网络接口的包来分析。


why?

为啥这么做可以呢?无论SO_BINDTODEVICE选项还是本文所给的方法,实际上都是改变了linux内核路由寻址的过程。传统的socket在发送数据包时,经过内核路由表进行寻址,按优先级从高到低的顺序来决定网络接口(顺便说下,这个内核路由表也就是输入命令route -n显示出的表,优先级从上到下即为从高到低),而默认的内核路由表在ip rule中也就是main表,使用本文所给的方法,当你添加新的策略路由时,它的优先级是高于内核路由表的,自然内核默认的路由就没有起作用,而是跳转到了自定义的路由表去寻址。使用SO_BINDTODEVICE时,在查询内核默认路由表的时候,除了要匹配目的地址之外,还需要匹配相应设备,通过这种方式也改变了默认的路由机制。


ps:通常来说,开启eth0会使wlan0的网关失效,请别忘了重新设置一下网光。

pps:我的系统是 ubuntu 10.10

评论
成就一亿技术人!
拼手气红包6.0元
还能输入1000个字符
 
红包 添加红包
表情包 插入表情
 条评论被折叠 查看
添加红包

请填写红包祝福语或标题

红包个数最小为10个

红包金额最低5元

当前余额3.43前往充值 >
需支付:10.00
成就一亿技术人!
领取后你会自动成为博主和红包主的粉丝 规则
hope_wisdom
发出的红包
实付
使用余额支付
点击重新获取
扫码支付
钱包余额 0

抵扣说明:

1.余额是钱包充值的虚拟货币,按照1:1的比例进行支付金额的抵扣。
2.余额无法直接购买下载,可以购买VIP、付费专栏及课程。

余额充值