使用 SO_BINDTODEVICE 接口上没有流量绑定
No traffic bind on interface using SO_BINDTODEVICE
我正在尝试使用 SO_BINDTODEVICE
将我的套接字绑定到 eth0。我将 eth0 传递给变量 opt
。我的套接字名称是 socketfd
sockfd = socket(AF_INET, SOCK_DGRAM, 0);
if(sockfd < 0) {
perror("socket(): error ");
return 0;
}
struct ifreq ifr;
memset(&ifr, 0, sizeof(struct ifreq));
fprintf(stderr,ifr.ifr_name, sizeof(ifr.ifr_name), opt);
ioctl(sockfd, SIOCGIFINDEX, &ifr);
setsockopt(sockfd, SOL_SOCKET, SO_BINDTODEVICE, opt, strlen(opt));
fprintf(stderr, "The bind is done on interface %s \n", opt);
fprintf(stderr, "opt length is %zu \n", strlen(opt));
为了进行一些调试,我将 opt 和 strlen(opt) 的值重定向到日志文件,它们正确地获取了值。
cat /home/cayman/log.txt
The bind is done on interface eth0
opt
length is 4
我也通过在 eth0 上捕获数据包来进行验证,但我没有看到通过 eth0 的流量。所有看到的都是其他网络相关流量如下:
tshark -i eth0
1 0.000000 Cisco_51:fd:09 -> Spanning-tree-(for-bridges)_00 STP 62 Conf. Root = 32768/78/00:24:50:51:fd:00 Cost = 0 Port = 0x8009
2 0.326720 192.168.78.1 -> 224.0.0.13 PIMv2 70 Hello
3 1.030538 Cisco_51:fd:09 -> Cisco_51:fd:09 LOOP 62 Reply
4 2.004544 Cisco_51:fd:09 -> Spanning-tree-(for-bridges)_00 STP 62 Conf. Root = 32768/78/00:24:50:51:fd:00 Cost = 0 Port = 0x8009
5 3.254223 192.168.78.1 -> 224.0.0.10 EIGRP 76 Hello
6 4.010168 Cisco_51:fd:09 -> Spanning-tree-(for-bridges)_00 STP 62 Conf. Root = 32768/78/00:24:50:51:fd:00 Cost = 0 Port = 0x8009
7 6.014839 Cisco_51:fd:09 -> Spanning-tree-(for-bridges)_00 STP 62 Conf. Root = 32768/78/00:24:50:51:fd:00 Cost = 0 Port = 0x8009
8 7.896252 192.168.78.1 -> 224.0.0.10 EIGRP 76 Hello
9 8.023003 Cisco_51:fd:09 -> Spanning-tree-(for-bridges)_00 STP 62 Conf. Root = 32768/78/00:24:50:51:fd:00 Cost = 0 Port = 0x8009
我还查看了 this thread 并确保我是 运行 我作为超级用户的代码。
我还查看了 SO_BINDTODEVICE 的 man page,但我不确定可能缺少什么。有什么建议吗?
您没有检查您拨打的大部分电话的 return 代码。它们中的一个或多个可能会以一种揭示额外信息的方式失败。您应该(始终)检查 return 值是否有错误。
编辑:我在您的代码中添加了错误处理:
#include <sys/socket.h>
#include <stdio.h>
#include <sys/ioctl.h>
#include <net/if.h>
#include <string.h>
int main() {
const char* opt = "wlp4s0";
int sockfd = socket(AF_INET, SOCK_DGRAM, 0);
if(sockfd < 0) {
perror("socket(): error ");
return 1;
}
struct ifreq ifr;
memset(&ifr, 0, sizeof(struct ifreq));
fprintf(stderr,ifr.ifr_name, sizeof(ifr.ifr_name), opt);
if (ioctl(sockfd, SIOCGIFINDEX, &ifr) < 0) {
perror("ioctl(): error ");
return 2;
}
if (setsockopt(sockfd, SOL_SOCKET, SO_BINDTODEVICE, opt, strlen(opt)) > 0) {
perror("setsockopt(): error ");
return 3;
}
fprintf(stderr, "The bind is done on interface %s \n", opt);
fprintf(stderr, "opt length is %zu \n", strlen(opt));
}
并发现 ioctl()
失败。然后我注意到您没有初始化 ifr.ifr_name
。如果您将 opt
复制到 ifr.ifr_name
,您的问题会消失吗?
您的第一个 fprintf()
陈述是错误的。您没有正确地将 opt
值输出到 stderr
,并且在调用 SIOCGIFINDEX
.
之前根本没有将 opt
复制到 ifr.ifr_name
尝试更像这样的东西:
if (strlen(opt) >= sizeof(ifr.ifr_name)) {
fprintf(stderr, "%s", "interface name is too long \n");
return 0;
}
...
struct ifreq ifr;
memset(&ifr, 0, sizeof(struct ifreq));
strncpy(ifr.ifr_name, opt, sizeof(ifr.ifr_name));
if (ioctl(sockfd, SIOCGIFINDEX, &ifr) < 0) {
perror("ioctl(): error ");
close(sockfd);
return 0;
}
见Get the index number of a Linux network interface in C using SIOCGIFINDEX
也就是说,如果要绑定到接口名称,则无需在调用 SO_BINDTODEVICE
之前使用 SIOCGIFINDEX
。自己调用SO_BINDTODEVICE
即可:
sockfd = socket(AF_INET, SOCK_DGRAM, 0);
if (sockfd < 0) {
perror("socket(): error ");
return 0;
}
if (setsockopt(sockfd, SOL_SOCKET, SO_BINDTODEVICE, opt, strlen(opt)) < 0) {
perror("setsockopt(): error ");
close(sockfd);
return 0;
}
在这种情况下使用 SIOCGIFINDEX
没有意义。如果您使用 SIOCGIFADDR
检索接口的 IP 地址(请参阅 Get the IP address of a network interface in C using SIOCGIFADDR),然后将该 IP 传递给 bind()
而不是将接口名称传递给 SO_BINDTODEVICE
,那将是有意义的.
我正在尝试使用 SO_BINDTODEVICE
将我的套接字绑定到 eth0。我将 eth0 传递给变量 opt
。我的套接字名称是 socketfd
sockfd = socket(AF_INET, SOCK_DGRAM, 0);
if(sockfd < 0) {
perror("socket(): error ");
return 0;
}
struct ifreq ifr;
memset(&ifr, 0, sizeof(struct ifreq));
fprintf(stderr,ifr.ifr_name, sizeof(ifr.ifr_name), opt);
ioctl(sockfd, SIOCGIFINDEX, &ifr);
setsockopt(sockfd, SOL_SOCKET, SO_BINDTODEVICE, opt, strlen(opt));
fprintf(stderr, "The bind is done on interface %s \n", opt);
fprintf(stderr, "opt length is %zu \n", strlen(opt));
为了进行一些调试,我将 opt 和 strlen(opt) 的值重定向到日志文件,它们正确地获取了值。
cat /home/cayman/log.txt
The bind is done on interface eth0
opt length is 4
我也通过在 eth0 上捕获数据包来进行验证,但我没有看到通过 eth0 的流量。所有看到的都是其他网络相关流量如下:
tshark -i eth0
1 0.000000 Cisco_51:fd:09 -> Spanning-tree-(for-bridges)_00 STP 62 Conf. Root = 32768/78/00:24:50:51:fd:00 Cost = 0 Port = 0x8009
2 0.326720 192.168.78.1 -> 224.0.0.13 PIMv2 70 Hello
3 1.030538 Cisco_51:fd:09 -> Cisco_51:fd:09 LOOP 62 Reply
4 2.004544 Cisco_51:fd:09 -> Spanning-tree-(for-bridges)_00 STP 62 Conf. Root = 32768/78/00:24:50:51:fd:00 Cost = 0 Port = 0x8009
5 3.254223 192.168.78.1 -> 224.0.0.10 EIGRP 76 Hello
6 4.010168 Cisco_51:fd:09 -> Spanning-tree-(for-bridges)_00 STP 62 Conf. Root = 32768/78/00:24:50:51:fd:00 Cost = 0 Port = 0x8009
7 6.014839 Cisco_51:fd:09 -> Spanning-tree-(for-bridges)_00 STP 62 Conf. Root = 32768/78/00:24:50:51:fd:00 Cost = 0 Port = 0x8009
8 7.896252 192.168.78.1 -> 224.0.0.10 EIGRP 76 Hello
9 8.023003 Cisco_51:fd:09 -> Spanning-tree-(for-bridges)_00 STP 62 Conf. Root = 32768/78/00:24:50:51:fd:00 Cost = 0 Port = 0x8009
我还查看了 this thread 并确保我是 运行 我作为超级用户的代码。 我还查看了 SO_BINDTODEVICE 的 man page,但我不确定可能缺少什么。有什么建议吗?
您没有检查您拨打的大部分电话的 return 代码。它们中的一个或多个可能会以一种揭示额外信息的方式失败。您应该(始终)检查 return 值是否有错误。
编辑:我在您的代码中添加了错误处理:
#include <sys/socket.h>
#include <stdio.h>
#include <sys/ioctl.h>
#include <net/if.h>
#include <string.h>
int main() {
const char* opt = "wlp4s0";
int sockfd = socket(AF_INET, SOCK_DGRAM, 0);
if(sockfd < 0) {
perror("socket(): error ");
return 1;
}
struct ifreq ifr;
memset(&ifr, 0, sizeof(struct ifreq));
fprintf(stderr,ifr.ifr_name, sizeof(ifr.ifr_name), opt);
if (ioctl(sockfd, SIOCGIFINDEX, &ifr) < 0) {
perror("ioctl(): error ");
return 2;
}
if (setsockopt(sockfd, SOL_SOCKET, SO_BINDTODEVICE, opt, strlen(opt)) > 0) {
perror("setsockopt(): error ");
return 3;
}
fprintf(stderr, "The bind is done on interface %s \n", opt);
fprintf(stderr, "opt length is %zu \n", strlen(opt));
}
并发现 ioctl()
失败。然后我注意到您没有初始化 ifr.ifr_name
。如果您将 opt
复制到 ifr.ifr_name
,您的问题会消失吗?
您的第一个 fprintf()
陈述是错误的。您没有正确地将 opt
值输出到 stderr
,并且在调用 SIOCGIFINDEX
.
opt
复制到 ifr.ifr_name
尝试更像这样的东西:
if (strlen(opt) >= sizeof(ifr.ifr_name)) {
fprintf(stderr, "%s", "interface name is too long \n");
return 0;
}
...
struct ifreq ifr;
memset(&ifr, 0, sizeof(struct ifreq));
strncpy(ifr.ifr_name, opt, sizeof(ifr.ifr_name));
if (ioctl(sockfd, SIOCGIFINDEX, &ifr) < 0) {
perror("ioctl(): error ");
close(sockfd);
return 0;
}
见Get the index number of a Linux network interface in C using SIOCGIFINDEX
也就是说,如果要绑定到接口名称,则无需在调用 SO_BINDTODEVICE
之前使用 SIOCGIFINDEX
。自己调用SO_BINDTODEVICE
即可:
sockfd = socket(AF_INET, SOCK_DGRAM, 0);
if (sockfd < 0) {
perror("socket(): error ");
return 0;
}
if (setsockopt(sockfd, SOL_SOCKET, SO_BINDTODEVICE, opt, strlen(opt)) < 0) {
perror("setsockopt(): error ");
close(sockfd);
return 0;
}
在这种情况下使用 SIOCGIFINDEX
没有意义。如果您使用 SIOCGIFADDR
检索接口的 IP 地址(请参阅 Get the IP address of a network interface in C using SIOCGIFADDR),然后将该 IP 传递给 bind()
而不是将接口名称传递给 SO_BINDTODEVICE
,那将是有意义的.