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

提示:将鼠标放在中文语句上可以显示对应的英文。显示中英文
时间:2020-08-03 23:45:44  来源:igfitidea点击:

how to bind raw socket to specific interface

clinuxsocketsnetwork-programmingraw-sockets

提问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.hand string.hheader files

另外,请确保包含if.h,socket.hstring.h头文件

回答by viv

As mentioned earlier, the correct thing to do is use the struct ifreqto 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_ANYas 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 0should be passed. And also if the fdis 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 和流。