Consider an application that opens AF_PACKET socket to listen for packets on a specific interface.
The canonical way to do it is:
- Open a socket
- Bind it to the interface
The code may be like (error checks omitted for conciseness):
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);
Now, considering that network traffic may be high-rate and/or the executing thread may be preempted between socket and bind syscalls, it could happen that before binding, the socket buffer already received packets, possibly from other interfaces.
This is as defined in packet(7):
By default, all packets of the specified protocol type are passed to a packet socket. To get packets only from a specific interface use bind(2) specifying an address in a struct sockaddr_ll to bind the packet socket to an interface. Fields used for binding are sll_family (should be AF_PACKET), sll_protocol, and sll_ifindex.
What will be the way to ensure that socket buffer is not populated with unwanted packets between socket and bind?
I've attempted two solutions so far:
- After calling bind, set temporary drop-all sock filter, flush socket, disable sock filter
- Use (undocummented?) third parameter
protocol = 0in socket() call to drop packets unless bind is called (now, with non-zero protocol).
The simplified code for first solution is:
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));
The second solution seems most elegant, since bind can provide both ethertype and interface index at the same time, but it seems like undefined behavior. Looking into net/packet/af_packet.c, within packet_create, proto is being used for hooks, but not validated before (i.e. no error is returned):
if (proto) {
po->prot_hook.type = proto;
__register_prot_hook(sk);
}