我发现可以使用的解决方案如下。首先,我们必须更改ARP和RP设置。在/etc/sysctl.conf中,添加以下内容并重新启动(还有一个命令可以动态设置此内容):
net.ipv4.conf.default.arp_filter = 1net.ipv4.conf.default.rp_filter = 2net.ipv4.conf.all.arp_filter = 1net.ipv4.conf.all.rp_filter = 2
必须使用arp过滤器,以允许eth0的响应通过WAN路由。必须使用rp筛选器选项,才能将传入的数据包与传入的NIC严格关联(与弱模型相比,该模型将传入的数据包与与子网匹配的任何NIC关联起来)。EJP的评论使我迈出了关键的一步。
之后,SO_BINDTODEVICE开始工作。两个套接字中的每个套接字都绑定到其自己的NIC,因此我可以根据消息来自哪个NIC来判断消息来自哪个NIC。
s=socket(AF_INET, SOCK_DGRAM, IPPROTO_UDP);rc=setsockopt(s, SOL_SOCKET, SO_BINDTODEVICE, nic, IF_NAMESIZE);memset((char *) &si_me, 0, sizeof(si_me));si_me.sin_family = AF_INET;si_me.sin_port = htons(LISTEN_PORT);si_me.sin_addr.s_addr = htonl(INADDR_ANY);rc=bind(s, (struct sockaddr *)&si_me, sizeof(si_me))
接下来,我想用源地址是原始请求来自的NIC的数据报来响应传入的数据报。答案是只查找该NIC的地址,然后将传出的套接字绑定到该地址(使用
bind)。
s=socket(AF_INET, SOCK_DGRAM, IPPROTO_UDP)get_nic_addr(nics, (struct sockaddr *)&sa)sa.sin_port = 0;rc = bind(s, (struct sockaddr *)&sa, sizeof(struct sockaddr));sendto(s, ...);int get_nic_addr(const char *nic, struct sockaddr *sa){ struct ifreq ifr; int fd, r; fd = socket(AF_INET, SOCK_DGRAM, 0); if (fd < 0) return -1; ifr.ifr_addr.sa_family = AF_INET; strncpy(ifr.ifr_name, nic, IFNAMSIZ); r = ioctl(fd, SIOCGIFADDR, &ifr); if (r < 0) { ... } close(fd); *sa = *(struct sockaddr *)&ifr.ifr_addr; return 0;}(也许每次都查找NIC地址似乎是一种浪费,但是当地址更改时,这是通过更多的代码来获得通知的方式,并且在不使用电池供电的系统上,这些事务每隔几秒钟发生一次。)



