web-dev-qa-db-fra.com

comment lier un socket brut à une interface spécifique

Mon application fonctionne sur CentOS 5.5 . J'utilise raw socket pour envoyer des données:

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
}

Comment puis-je lier ce socket à une interface réseau spécifique (disons eth1)?

26
Dima
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);

Première ligne: configurez votre variable

Deuxième ligne: indique au programme à quelle interface se lier

Lignes 3-5: obtenez la longueur du nom de l'interface et vérifiez si sa taille n'est pas trop grande.

Six lignes: définissez les options de socket pour socket sd, en liaison avec le périphérique opt.

setsockopt prototype:

int setsockopt(int s, int level, int optname, const void *optval, socklen_t optlen);

Assurez-vous également d'inclure les fichiers d'en-tête if.h, socket.h et string.h.

38
Andrew Sledge

Comme mentionné précédemment, la bonne chose à faire est d'utiliser le struct ifreq pour spécifier le nom de l'interface. Voici mon exemple de code.

#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));

J'aimerais également ajouter que du point de vue de la sécurité, s'il est bon de lier le socket à une interface, il n’a pas de sens d’utiliser INADDR_ANY comme adresse IP à l’écoute. Cela ferait apparaître le port ouvert dans netstat sur toutes les interfaces réseau. 

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  

Au lieu de cela, j'ai spécifié une adresse IP spécifique à l'interface utilisée (un VLAN privé). Cela corrigeait aussi la sortie 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  
16
viv

Lier le socket à une adresse IP d'interface spécifique

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));
}

Lie la socket à un nom d'interface spécifique

int bind_using_iface_name(int fd, char *iface_name)
{
    return setsockopt(fd, SOL_SOCKET, SO_BINDTODEVICE, iface_name, strlen(iface_name))
}

Dans bind_using_iface_ip, vous devez établir une liaison avec un port 0. Et aussi, si la fd est une socket brute, vous devez passer le port en tant que 0. Ce mécanisme de liaison est commun à tous les types de sockets tels que raw, dgram et stream.

0
rashok