linux - tag - cómo vincular el socket sin formato a una interfaz específica
vlan linux (3)
Mi aplicación se ejecuta en CentOS 5.5. Estoy usando socket raw para enviar datos:
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
}
¿Cómo puedo vincular este socket a una interfaz de red específica (digamos eth1)?
Como se mencionó anteriormente, lo correcto es usar struct ifreq
para especificar el nombre de la interfaz. Aquí está mi código de muestra.
#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));
También me gustaría añadir que desde una perspectiva de seguridad, aunque es bueno vincular el socket a una interfaz, no tiene sentido usar INADDR_ANY
como la dirección IP que escucha. Si lo hace, el puerto aparecerá abierto en netstat en todas las interfaces de red.
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
En su lugar, especifiqué una dirección IP específica para la interfaz que se utiliza (una VLAN privada). Esto también solucionó la salida 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
Enlazar el socket a la dirección IP de la interfaz específica
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));
}
Enlazar el socket al nombre de la interfaz específica
int bind_using_iface_name(int fd, char *iface_name)
{
return setsockopt(fd, SOL_SOCKET, SO_BINDTODEVICE, iface_name, strlen(iface_name))
}
En bind_using_iface_ip
, para enlazar a cualquier puerto 0
debe pasar. Y también si el fd
es un socket sin formato, entonces debe pasar el puerto como 0
. Este mecanismo de enlace es común para todos los tipos de sockets, como raw, dgram y stream.
const char *opt;
opt = "eth0";
const len = strnlen(opt, IFNAMSIZ);
if (len == IFNAMESIZ) {
fprintf(stderr, "Too long iface name");
return 1;
}
setsockopt(sd, SOL_SOCKET, SO_BINDTODEVICE, opt, len);
Primera línea: configure su variable
Segunda línea: indique al programa qué interfaz vincular a
Líneas 3-5: obtener la longitud del nombre de la interfaz y comprobar si su tamaño no es demasiado grande.
Six line: establece las opciones de socket para socket sd
, vinculando al dispositivo opt
.
prototipo setsockopt:
int setsockopt(int s, int level, int optname, const void *optval, socklen_t optlen);
Además, asegúrese de incluir los archivos de encabezado socket.h y string.h