gpt4 book ai didi

c - SO_BINDTODEVICE Linux 套接字选项的问题

转载 作者:IT王子 更新时间:2023-10-29 00:23:20 30 4
gpt4 key购买 nike

我的 PC 有两个网卡。一个 (eth0) 用于 LAN/internet,另一个用于与一个微 Controller 设备进行 UDP 通信。微 Controller 有一个 IP (192.168.7.2) 和一个 MAC 地址。第二台 pc 网络适配器 (eth1) 有 192.168.7.1。

微 Controller 有一个非常简单的 IP 堆栈,因此 mc 发送 UDP 数据包的最简单方法是广播它们。

在 PC 端,我想接收广播 - 但只能来自 eth1。所以我尝试将 UDP 套接字绑定(bind)到 eth1 设备。

问题(下面的源代码):

  1. setsockopt(sock, SOL_SOCKET, SO_BINDTODEVICE, device, sizeof(device)) 需要 root 权限,为什么? (设置其他选项作为用户)

  2. getsockopt(sock, SOL_SOCKET, SO_BINDTODEVICE, (void *)buffer, &opt_length) 给出“协议(protocol)不可用”。我想读回我通过 setsockopt 命令设置的设备。

  3. 我在哪里可以找到有用的信息?我查看了一些 Linux 编程、网络书籍,但例如 SO_BINDTODEVICE 选项我只能在互联网上找到。

我的冗长(肮脏的)测试程序显示了问题。设置和取回 SO_RCVTIMEOSO_BROADCAST 选项按预期工作。

在用户退出时运行代码:

could not set SO_BINDTODEVICE (Operation not permitted)"

使用 sudo 运行会得到:

SO_BINDTODEVICE set
./mc-test: could not get SO_BINDTODEVICE (Protocol not available)

所以,设置选项似乎有效,但无法读取它?

/* SO_BINDTODEVICE test */ 

#include <sys/types.h>
#include <sys/socket.h>
#include <netinet/in.h>
#include <arpa/inet.h>
#include <netdb.h>
#include <stdio.h>
#include <unistd.h>
#include <string.h>
#include <stdlib.h>
#include <sys/time.h>
#include <errno.h>

#define MC_IP "192.168.7.2"
#define MC_PORT (54321)
#define MY_PORT (54321)
#define MY_DEVICE "eth1"

#define BUFFERSIZE (1000)

/* global variables */
int sock;
struct sockaddr_in MC_addr;
struct sockaddr_in my_addr;
char buffer[BUFFERSIZE];

int main(int argc, char *argv[])
{
unsigned int echolen, clientlen;
int rc, n;
char opt_buffer[1000];
struct protoent *udp_protoent;
struct timeval receive_timeout;
int optval;
socklen_t opt_length;

/* Create the UDP socket */
if ((sock = socket(AF_INET, SOCK_DGRAM, IPPROTO_UDP)) < 0)
{
printf ("%s: failed to create UDP socket (%s) \n",
argv[0], strerror(errno));
exit (EXIT_FAILURE);
}
printf ("UDP socket created\n");

/* set the recvfrom timeout value */
receive_timeout.tv_sec = 5;
receive_timeout.tv_usec = 0;
rc=setsockopt(sock, SOL_SOCKET, SO_RCVTIMEO, &receive_timeout,
sizeof(receive_timeout));
if (rc != 0)
{
printf ("%s: could not set SO_RCVTIMEO (%s)\n",
argv[0], strerror(errno));
exit (EXIT_FAILURE);
}
printf ("set timeout to\ntime [s]: %d\ntime [ms]: %d\n", receive_timeout.tv_sec, receive_timeout.tv_usec);
/* verify the recvfrom timeout value */
rc=getsockopt(sock, SOL_SOCKET, SO_RCVTIMEO, &receive_timeout, &opt_length);
if (rc != 0)
{
printf ("%s: could not get socket options (%s)\n",
argv[0], strerror(errno));
exit (EXIT_FAILURE);
}
printf ("timeout value\ntime [s]: %d\ntime [ms]: %d\n", receive_timeout.tv_sec, receive_timeout.tv_usec);

/* allow broadcast messages for the socket */
int true = 1;
rc=setsockopt(sock, SOL_SOCKET, SO_BROADCAST, &true, sizeof(true));
if (rc != 0)
{
printf ("%s: could not set SO_BROADCAST (%s)\n",
argv[0], strerror(errno));
exit (EXIT_FAILURE);
}
printf ("set SO_BROADCAST\n");
/* verify SO_BROADCAST setting */
rc=getsockopt(sock, SOL_SOCKET, SO_BROADCAST, &optval, &opt_length);
if (optval != 0)
{
printf("SO_BROADCAST is enabled\n");
}

/* bind the socket to one network device */
const char device[] = MY_DEVICE;
rc=setsockopt(sock, SOL_SOCKET, SO_BINDTODEVICE, device, sizeof(device));
if (rc != 0)
{
printf ("%s: could not set SO_BINDTODEVICE (%s)\n",
argv[0], strerror(errno));
exit (EXIT_FAILURE);
}
printf ("SO_BINDTODEVICE set\n");
/* verify SO_BINDTODEVICE setting */
rc = getsockopt(sock, SOL_SOCKET, SO_BINDTODEVICE, (void *)buffer, &opt_length);
if (rc != 0)
{
printf ("%s: could not get SO_BINDTODEVICE (%s)\n",
argv[0], strerror(errno));
exit (EXIT_FAILURE);
}
if (rc == 0)
{
printf("SO_BINDTODEVICE is: %s\n", buffer);
}


/* Construct the server sockaddr_in structure */
memset(&MC_addr, 0, sizeof(MC_addr)); /* Clear struct */
MC_addr.sin_family = AF_INET; /* Internet/IP */
MC_addr.sin_addr.s_addr = inet_addr(MC_IP); /* IP address */
MC_addr.sin_port = htons(MC_PORT); /* server port */

/* bind my own Port */
my_addr.sin_family = AF_INET;
my_addr.sin_addr.s_addr = INADDR_ANY; /* INADDR_ANY all local addresses */
my_addr.sin_port = htons(MY_PORT);
rc = bind (sock, (struct sockaddr *) &my_addr, sizeof(my_addr));
if (rc < 0)
{
printf ("%s: could not bind port (%s)\n",
argv[0], strerror(errno));
exit (EXIT_FAILURE);
}
printf ("port bound\n");

/* identify mc */
buffer[0] = (char)1;
buffer[1] = (char)0;
send_data (buffer, 2);
printf ("sent command: %d\n", (char)buffer[0]);

rc=receive_data(buffer);
printf ("%d bytes received\n", rc);
buffer[rc] = (char)0; /* string end symbol */
printf ("%d - %s\n", (int)(char)buffer[0], &buffer[1]);

close(sock);
printf ("socket closed\n");

exit(0);
}

/* send data to the MC *****************************************************/
/* buffer points to the bytes to send */
/* buf_length is the number of bytes to send */
/* returns allways 0 */
int send_data( char *buffer, int buf_length )
{
int rc;

rc = sendto (sock, buffer, buf_length, 0,
(struct sockaddr *) &MC_addr,
sizeof(MC_addr));
if (rc < 0)
{
printf ("could not send data\n");
close (sock);
exit (EXIT_FAILURE);
}
return(0);
}

/* receive data from the MC *****************************************************/
/* buffer points to the memory for the received data */
/* max BUFFERSIZE bytes can be received */
/* returns number of bytes received */
int receive_data(char *buffer)
{
int rc, MC_addr_length;

MC_addr_length = sizeof(MC_addr);
rc = recvfrom (sock, buffer, BUFFERSIZE, 0,
(struct sockaddr *) &MC_addr,
&MC_addr_length);
if (rc < 0)
{
printf ("could not receive data\n");
close (sock);
exit (EXIT_FAILURE);
}
return(rc);
}

最佳答案

在看到关于如何实际使用 SO_BINDTODEVICE 的相互矛盾的答案后,我已经研究了一段时间。 Some sources声称正确的用法是传入一个 struct ifreq 指针,该指针具有通过 ioctl 获得的设备名称和索引。例如:

struct ifreq ifr;
memset(&ifr, 0, sizeof(struct ifreq));
snprintf(ifr.ifr_name, sizeof(ifr.ifr_name), "eth0");
ioctl(fd, SIOCGIFINDEX, &ifr);
setsockopt(fd, SOL_SOCKET, SO_BINDTODEVICE, (void*)&ifr, sizeof(struct ifreq));

哪里Beej's networking tutorial表示将设备名称作为字符指针传递。例如:

char *devname = "eth0";
setsockopt(fd, SOL_SOCKET, SO_BINDTODEVICE, devname, strlen(devname));

这两种方法我都试过了,它们都按要求做了,但我想指出,第一种方法中获得的设备索引是多余的。如果您查看 net/core/sock.c 中的内核代码, sock_bindtodevice 只是复制设备名称字符串,调用 dev_get_by_name_rcu 获取设备并绑定(bind)到它。

第一种方法起作用的原因是设备名称是ifreq 结构中的第一个元素,参见http://linux.die.net/man/7/netdevice .

注:SO_BINDTODEVICE requires elevated permissions :

  • 以完全 root 权限运行可执行文件
  • 构建可执行文件后,您可以使用 sudo setcap 授予可执行文件使用此特定套接字选项的权限,然后您可以在没有 root 权限的情况下运行可执行文件,并且可执行文件有权使用 SO_BINDTODEVICE 功能(通过先前调用 setcap)。

关于c - SO_BINDTODEVICE Linux 套接字选项的问题,我们在Stack Overflow上找到一个类似的问题: https://stackoverflow.com/questions/1207746/

30 4 0
Copyright 2021 - 2024 cfsdn All Rights Reserved 蜀ICP备2022000587号
广告合作:1813099741@qq.com 6ren.com