SO_BINDTODEVICE Linux套接字选项问题
|
我有一台带两个网卡的电脑.一个(eth0)用于LAN /互联网,另一个用于与一个微控制器设备的UDP通信.微控制器具有IP(192.168.7.2)和MAC地址.第二个pc网络适配器(eth1)具有192.168.7.1. 微控制器具有非常简单的IP堆栈,因此mc发送UDP数据包的最简单方法是广播它们. 在PC端,我想接收广播,但只能从eth1接收.所以我试图将UDP套接字绑定到eth1设备. 问题(源代码如下): > setsockopt(sock,SOL_SOCKET,SO_BINDTODEVICE,device,sizeof(device))需要root权限,为什么? (设置其他选项作为用户) 我的冗长(脏)测试程序显示问题.设置并返回SO_RCVTIMEO和SO_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 createdn");
/* set the recvfrom timeout value */
receive_timeout.tv_sec = 5;
receive_timeout.tv_usec = 0;
rc=setsockopt(sock,SO_RCVTIMEO,&receive_timeout,sizeof(receive_timeout));
if (rc != 0)
{
printf ("%s: could not set SO_RCVTIMEO (%s)n",strerror(errno));
exit (EXIT_FAILURE);
}
printf ("set timeout tontime [s]: %dntime [ms]: %dn",receive_timeout.tv_sec,receive_timeout.tv_usec);
/* verify the recvfrom timeout value */
rc=getsockopt(sock,&opt_length);
if (rc != 0)
{
printf ("%s: could not get socket options (%s)n",strerror(errno));
exit (EXIT_FAILURE);
}
printf ("timeout valuentime [s]: %dntime [ms]: %dn",receive_timeout.tv_usec);
/* allow broadcast messages for the socket */
int true = 1;
rc=setsockopt(sock,SO_BROADCAST,&true,sizeof(true));
if (rc != 0)
{
printf ("%s: could not set SO_BROADCAST (%s)n",strerror(errno));
exit (EXIT_FAILURE);
}
printf ("set SO_BROADCASTn");
/* verify SO_BROADCAST setting */
rc=getsockopt(sock,&optval,&opt_length);
if (optval != 0)
{
printf("SO_BROADCAST is enabledn");
}
/* bind the socket to one network device */
const char device[] = MY_DEVICE;
rc=setsockopt(sock,sizeof(device));
if (rc != 0)
{
printf ("%s: could not set SO_BINDTODEVICE (%s)n",strerror(errno));
exit (EXIT_FAILURE);
}
printf ("SO_BINDTODEVICE setn");
/* verify SO_BINDTODEVICE setting */
rc = getsockopt(sock,&opt_length);
if (rc != 0)
{
printf ("%s: could not get SO_BINDTODEVICE (%s)n",strerror(errno));
exit (EXIT_FAILURE);
}
if (rc == 0)
{
printf("SO_BINDTODEVICE is: %sn",buffer);
}
/* Construct the server sockaddr_in structure */
memset(&MC_addr,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",strerror(errno));
exit (EXIT_FAILURE);
}
printf ("port boundn");
/* identify mc */
buffer[0] = (char)1;
buffer[1] = (char)0;
send_data (buffer,2);
printf ("sent command: %dn",(char)buffer[0]);
rc=receive_data(buffer);
printf ("%d bytes receivedn",rc);
buffer[rc] = (char)0; /* string end symbol */
printf ("%d - %sn",(int)(char)buffer[0],&buffer[1]);
close(sock);
printf ("socket closedn");
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,(struct sockaddr *) &MC_addr,sizeof(MC_addr));
if (rc < 0)
{
printf ("could not send datan");
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,BUFFERSIZE,&MC_addr_length);
if (rc < 0)
{
printf ("could not receive datan");
close (sock);
exit (EXIT_FAILURE);
}
return(rc);
}
解决方法在看到有关SO_BINDTODEVICE实际使用的冲突答案后,我一直在研究这段时间. Some sources声称正确的用法是传递一个结构体ifreq指针,它具有通过ioctl获得的设备名称和索引.例如:struct ifreq ifr; memset(&ifr,sizeof(struct ifreq)); snprintf(ifr.ifr_name,sizeof(ifr.ifr_name),"eth0"); ioctl(fd,SIOCGIFINDEX,&ifr); setsockopt(fd,(void*)&ifr,sizeof(struct ifreq)); 其中Beej’s networking tutorial表示将设备名称作为char指针传递.例如: char *devname = "eth0"; setsockopt(fd,devname,strlen(devname)); 我已经尝试了这两种方法,并且都做了所需的工作,但是我想注意到,在第一种方法中获得的设备索引是多余的.如果您查看net/core/sock.c中的内核代码,sock_bindtodevice只需复制设备名称字符串,调用dev_get_by_name_rcu即可获取设备并绑定到该设备. 第一种方法的原因是设备名称是ifreq结构中的第一个元素,请参见http://linux.die.net/man/7/netdevice. (编辑:安卓应用网) 【声明】本站内容均来自网络,其相关言论仅代表作者个人观点,不代表本站立场。若无意侵犯到您的权利,请及时与联系站长删除相关内容! |
