我有一台带两个网卡的电脑.一个(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权限,为什么? (设置其他选项作为用户)
> getsockopt(sock,(void *)buffer,& opt_length)给出“协议不可用”.我想通过setsockopt命令读回我设置的设备.
>哪里可以找到好的信息?我检查了一些Linux编程,网络书籍,但例如我只在互联网上找到的SO_BINDTODEVICE选项.
我的冗长(脏)测试程序显示问题.设置并返回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 created\n"); /* 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 to\ntime [s]: %d\ntime [ms]: %d\n",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 value\ntime [s]: %d\ntime [ms]: %d\n",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_BROADCAST\n"); /* verify SO_BROADCAST setting */ rc=getsockopt(sock,&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,sizeof(device)); if (rc != 0) { printf ("%s: could not set SO_BINDTODEVICE (%s)\n",strerror(errno)); exit (EXIT_FAILURE); } printf ("SO_BINDTODEVICE set\n"); /* 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: %s\n",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 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,(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,BUFFERSIZE,&MC_addr_length); if (rc < 0) { printf ("could not receive data\n"); 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.