Considere una aplicación que abre el socket AF_PACKET para escuchar paquetes en una interfaz específica.
La forma canónica de hacerlo es:
El código puede ser como (las comprobaciones de error se omiten por concisión):
int sock_fd = socket(AF_PACKET, SOCK_RAW, htons(ETH_P_ALL)); struct sockaddr_ll sock_addr = { .sll_family = AF_PACKET, .sll_ifindex = if_nametoindex("ethXYZ"), }; bind(sock_fd, (struct sockaddr*)&sock_addr, sizeof(sock_addr);Ahora, teniendo en cuenta que el tráfico de la red puede ser de alta velocidad y/o el subproceso de ejecución puede ser desplazado entre las llamadas al sistema de conexión y conexión, podría suceder que antes de la conexión, el búfer del conector ya haya recibido paquetes, posiblemente de otras interfaces.
Esto es como se define en el paquete (7) :
De forma predeterminada, todos los paquetes del tipo de protocolo especificado se pasan a un socket de paquetes. Para obtener paquetes solo de una interfaz específica, use bind(2) especificando una dirección en una estructura sockaddr_ll para vincular el socket del paquete a una interfaz. Los campos utilizados para vincular son sll_family (debe ser AF_PACKET), sll_protocol y sll_ifindex.
¿Cuál será la forma de garantizar que el búfer del socket no se llene con paquetes no deseados entre el socket y el enlace?
He intentado dos soluciones hasta ahora:
protocol = 0 en la llamada socket() para descartar paquetes a menos que se llame a bind (ahora, con un protocolo distinto de cero).El código simplificado para la primera solución es:
setsockopt(sock_fd, SOL_SOCKET, SO_ATTACH_FILTER, &fprog, sizeof(fprog)); while (1) { bytes = recv(sock_fd, buffer, buffer_size, MSG_DONTWAIT); if (bytes == -1) // should check errno break; } setsockopt(sock_fd, SOL_SOCKET, SO_DETACH_FILTER, &fprog, sizeof(fprog)); La segunda solución parece más elegante, ya que bind puede proporcionar tanto ethertype como índice de interfaz al mismo tiempo, pero parece un comportamiento indefinido. Mirando net/packet/af_packet.c , dentro de packet_create , proto se usa para ganchos, pero no se validó antes (es decir, no se devuelve ningún error):
if (proto) { po->prot_hook.type = proto; __register_prot_hook(sk); }Después de buscar en el código del kernel de af_packet.c , parece que el método para crear un socket con el protocolo 0 debería funcionar bien.
Durante la depuración, la función de protocolo packet_rcv nunca se ingresa entre el socket y el enlace, por lo que dichos paquetes no aumentan el búfer del zócalo, lo que significa que sk_rmem_alloc es 0. Después del enlace, se ingresan nuevos paquetes y llenan el búfer como se esperaba, asignando y llenando sk_buff :
[ 548.455055] Called packet_rcv() pkt_type = 1 [ 548.455058] packet_rcv() skb->len = 60 [ 548.455059] packet_rcv() orig = 3, dev = 3 [ 548.455060] packet_rcv() sk_rmem_alloc = 0 [ 554.702140] Called packet_rcv() pkt_type = 1 [ 554.702156] packet_rcv() skb->len = 60 [ 554.702157] packet_rcv() orig = 3, dev = 3 [ 554.702158] packet_rcv() sk_rmem_alloc = 768 El punto clave es que con proto = 0 , packet_create() no llamará a dev_add_pack , por lo tanto, el controlador de protocolo de packet_rcv() no se agregará a la pila de red hasta que se llame a bind() .