Problèmes avec l'option de socket Linux SO_BINDTODEVICE
J'ai un PC avec deux cartes réseau. Un (eth0
) est LAN/internet et l'autre pour la communication UDP avec un microcontrôleur de l'appareil. Le microcontrôleur dispose d'une IP (192.168.7.2) et une adresse MAC. Le second pc adaptateur de réseau (eth1
) a 192.168.7.1.
Le microcontrôleur est très simple pile IP, de sorte que le moyen le plus facile pour le mc pour envoyer des paquets UDP est de les diffuser.
Sur le côté PC, j'aimerais recevoir les émissions, - mais seulement à partir de eth1
. J'ai donc essayer de lier les sockets UDP à la eth1
appareil.
Les problèmes (le code source ci-dessous):
setsockopt(sock, SOL_SOCKET, SO_BINDTODEVICE, device, sizeof(device))
nécessite les privilèges de root, pourquoi? (réglage d'autres options travaille en tant qu'utilisateur)getsockopt(sock, SOL_SOCKET, SO_BINDTODEVICE, (void *)buffer, &opt_length)
donne "Protocole n'est pas disponible". Je voudrais lire le dos de l'appareil j'ai réglé via lesetsockopt
commande.- Où puis-je trouver la bonne info? J'ai vérifié certains Linux-programmation, réseau des livres, mais par exemple la
SO_BINDTODEVICE
option que j'ai trouvé sur internet.
Ma longue (sale) programme de test montre les problèmes. Réglage et revenir à l' SO_RCVTIMEO
et SO_BROADCAST
options fonctionne comme prévu.
L'exécution du code en tant qu'utilisateur quitte avec:
could not set SO_BINDTODEVICE (Operation not permitted)"
En cours d'exécution avec la commande sudo donne:
SO_BINDTODEVICE set
./mc-test: could not get SO_BINDTODEVICE (Protocol not available)
Ainsi, la définition de l'option semble fonctionner, mais la lecture en arrière n'est pas possible?
/* 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);
}
source d'informationauteur | 2009-07-30
Vous devez vous connecter pour publier un commentaire.
J'ai été à la recherche dans ce pour un certain temps après avoir vu des réponses contradictoires à la façon dont SO_BINDTODEVICE est réellement utilisée. Certaines sources prétendre que le bon usage est de laisser un
struct ifreq
pointeur, ce qui a le nom de l'appareil et de l'indice obtenu par l'intermédiaire d'un ioctl. Par exemple:Où, comme Beej de réseautage tutoriel dit de passer le nom du périphérique comme un pointeur de char. Par exemple:
J'ai essayé les deux de ces méthodes, et ils ont tous deux faire ce qui est nécessaire, mais je voulais noter que l'appareil indice obtenu dans la première méthode est superflu. Si vous regardez le code du noyau dans net/core/chaussette.c
sock_bindtodevice
copie tout simplement le nom de périphérique de la chaîne, les appelsdev_get_by_name_rcu
pour obtenir le dispositif et se lie à elle.La raison que la première approche fonctionne, c'est que le nom de l'appareil est le premier élément dans la
ifreq
structure, voir http://linux.die.net/man/7/netdevice.OK, j'ai regardé dans un peu plus. SO_BINDTODEVICE a été jugé "près obsolètes" en 1999, et qui est la racine seulement en raison de certaines indéterminée "implications en matière de sécurité" (je ne pouvais pas trouver exactement ce).
Toutefois, vous devriez être en mesure d'obtenir le comportement que vous voulez par la liaison INADDR_ANY et le réglage de la IP_PKTINFO socketopt. Cela va passer un message supplémentaire sur la prise qui contient un pktinfo structure décrivant le paquet entrant. Cette structure comprend l'index de l'interface sur laquelle le paquet est entré:
La ipi_ifindex correspond à la ifr_ifindex de la structure ifreq renvoyé par la netdevice ioctls comme SIOCGIFCONF. Donc, vous devriez être en mesure de l'utiliser pour ignorer les paquets reçus sur une interface autre que celui qui vous intéresse.
Doco pour IP_PKTINFO est dans ip(7) et pour l'interface ioctls dans netdevice(7).
- Dessus de la ligne de code suffit pour recevoir des messages de
eth0 interface
seulement.J'ai testé cela sur Linux.
REMARQUE: Cela ne fonctionnera pas si il y a un pont interface de contrôle réel des interfaces.
Meilleures salutations,
Santosh.
Avant de Linux 3.8, cette option de socket pourrait être fixé, mais il ne pouvait pas récupéré avec getsockopt(2). Depuis Linux 3.8, il est lisible. Le optlen argument doit contenir la taille de la mémoire disponible pour recevoir le nom de l'appareil et il est recommandé d'être IFNAMSZ octets. Le vrai nom de l'appareil longueur est rapporté dans le optlen argument.
Juste à la recherche de l'adresse IP de l'interface qui vous intéresse avec getifaddrs(), et de lier votre prise à l'adresse IP avec bind(). Si vous activez SO_BROADCAST sur le support que vous aurez seulement ensuite émissions reçus sur cette interface.
Ou, en fait, vous pouvez passer de l'getifaddrs() de la partie et vient directement de bind() pour 192.168.7.1 si vous le souhaitez.
Le problème que j'ai rencontré semble être que le fait de recevoir des émissions à partir d'une interface spécifique est traitée différemment par Linux, Windows,...
http://www.developerweb.net/forum/showthread.php?t=5722
J'ai maintenant décidé de résoudre le problème (peu de documentation et de mauvaise portabilité) en changeant la pile TCP/IP du microcontrôleur. Il ne sera plus possible d'envoyer des réponses à l'adresse de diffusion, mais au lieu de prendre l'adresse IP/MAC de la prochaine paquet UDP en tant que destination IP/MAC. Alors je peux (côté pc), il suffit de lier le support de l'IP de eth1.
Cheers,
Michael