Linux 如何将原始套接字绑定到特定接口
声明:本页面是StackOverFlow热门问题的中英对照翻译,遵循CC BY-SA 4.0协议,如果您需要使用它,必须同样遵循CC BY-SA许可,注明原文地址和作者信息,同时你必须将它归于原作者(不是我):StackOverFlow
原文地址: http://stackoverflow.com/questions/3998569/
Warning: these are provided under cc-by-sa 4.0 license. You are free to use/share it, But you must attribute it to the original authors (not me):
StackOverFlow
how to bind raw socket to specific interface
提问by Dima
My application is running on CentOS 5.5. I'm using raw socket to send data:
我的应用程序在 CentOS 5.5 上运行。我正在使用原始套接字发送数据:
sd = socket(AF_INET, SOCK_RAW, IPPROTO_RAW);
if (sd < 0) {
// Error
}
const int opt_on = 1;
rc = setsockopt(m_SocketDescriptor, IPPROTO_IP, IP_HDRINCL, &opt_on, sizeof(opt_on));
if (rc < 0) {
close(sd);
// Error
}
struct sockaddr_in sin;
memset(&sin, 0, sizeof(sin));
sin.sin_family = AF_INET;
sin.sin_addr.s_addr = my_ip_address;
if (sendto(m_SocketDescriptor, DataBuffer, (size_t)TotalSize, 0, (struct sockaddr *)&sin, sizeof(struct sockaddr)) < 0) {
close(sd);
// Error
}
How can I bind this socket to specific network interface (say eth1)?
如何将此套接字绑定到特定的网络接口(比如 eth1)?
采纳答案by Andrew Sledge
const char *opt;
opt = "eth0";
const len = strnlen(opt, IFNAMSIZ);
if (len == IFNAMSIZ) {
fprintf(stderr, "Too long iface name");
return 1;
}
setsockopt(sd, SOL_SOCKET, SO_BINDTODEVICE, opt, len);
First line: set up your variable
第一行:设置变量
Second line: tell the program which interface to bind to
第二行:告诉程序绑定哪个接口
Lines 3-5: get length of interface name and check if it's size not too big.
第 3-5 行:获取接口名称的长度并检查它的大小是否太大。
Six line: set the socket options for socket sd
, binding to the device opt
.
六行:设置socket的socket选项sd
,绑定到设备opt
。
setsockopt prototype:
setockopt 原型:
int setsockopt(int s, int level, int optname, const void *optval, socklen_t optlen);
Also, make sure you include the if.h
, socket.h
and string.h
header files
另外,请确保包含if.h
,socket.h
和string.h
头文件
回答by viv
As mentioned earlier, the correct thing to do is use the struct ifreq
to specify the interface name. Here is my code sample.
如前所述,正确的做法是使用struct ifreq
来指定接口名称。这是我的代码示例。
#define SERVERPORT 5555
...
struct ifreq ifr;
/* Create the socket */
sd = socket(AF_INET, SOCK_STREAM, 0);
if (sd < 0)
{
printf("Error in socket() creation - %s", strerror(errno));
}
/* Bind to eth1 interface only - this is a private VLAN */
memset(&ifr, 0, sizeof(ifr));
snprintf(ifr.ifr_name, sizeof(ifr.ifr_name), "eth1");
if ((rc = setsockopt(sd, SOL_SOCKET, SO_BINDTODEVICE, (void *)&ifr, sizeof(ifr))) < 0)
{
perror("Server-setsockopt() error for SO_BINDTODEVICE");
printf("%s\n", strerror(errno));
close(sd);
exit(-1);
}
/* bind to an address */
memset(&serveraddr, 0x00, sizeof(struct sockaddr_in));
serveraddr.sin_family = AF_INET;
serveraddr.sin_port = htons(SERVERPORT);
serveraddr.sin_addr.s_addr = inet_addr("9.1.2.3");
int rc = bind(sd, (struct sockaddr *)&serveraddr, sizeof(serveraddr));
I would also like to add that from a security perspective, while it is good to bind the socket to an interface, it does not make sense to use INADDR_ANY
as the listening IP address. Doing so would make the port appear open in netstat on all network interfaces.
我还想从安全角度补充一点,虽然将套接字绑定到接口是好的,但INADDR_ANY
用作侦听 IP 地址没有意义。这样做会使端口在所有网络接口上的 netstat 中显示为打开状态。
Proto Recv-Q Send-Q Local Address Foreign Address State User Inode PID/Program name
tcp 0 0 0.0.0.0:5555 0.0.0.0:* LISTEN 0 210898 26996/myserver
Instead, I specified an IP address specific to the interface being used (a private VLAN). This fixed the netstat output too:
相反,我指定了特定于正在使用的接口的 IP 地址(专用 VLAN)。这也修复了 netstat 输出:
Proto Recv-Q Send-Q Local Address Foreign Address State User Inode PID/Program name
tcp 0 0 9.1.2.3:5555 0.0.0.0:* LISTEN 0 210898 26996/myserver
回答by rashok
Bind socket to specific interface IP address
将套接字绑定到特定的接口 IP 地址
int bind_using_iface_ip(int fd, char *ipaddr, uint16_t port)
{
struct sockaddr_in localaddr = {0};
localaddr.sin_family = AF_INET;
localaddr.sin_port = htons(port);
localaddr.sin_addr.s_addr = inet_addr(ipaddr);
return bind(fd, (struct sockaddr*) &localaddr, sizeof(struct sockaddr_in));
}
Bind socket to specific interface name
将套接字绑定到特定的接口名称
int bind_using_iface_name(int fd, char *iface_name)
{
return setsockopt(fd, SOL_SOCKET, SO_BINDTODEVICE, iface_name, strlen(iface_name))
}
In bind_using_iface_ip
, to bind to any port 0
should be passed. And also if the fd
is raw socket then need to pass port as 0
. This bind mechanism is common for all kind of sockets like raw, dgram and stream.
在bind_using_iface_ip
, 绑定到任何端口0
都应该通过。而且如果fd
是原始套接字,则需要将端口作为0
. 这种绑定机制适用于所有类型的套接字,如原始、dgram 和流。