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

被折叠的 条评论
为什么被折叠?



