Define a BPF for ARP-listening raw sockets that discriminates by ethernet

frame protocol type field, ARP hardware type field, ARP protocol type field,
ARP hardware address length field, and ARP protocol address length field.
This commit is contained in:
Nicholas J. Kain 2011-07-01 00:55:35 -04:00
parent 3e3ecc816f
commit 9ddfab5085

View File

@ -27,6 +27,7 @@
#include <arpa/inet.h>
#include <sys/time.h>
#include <linux/if_packet.h>
#include <linux/filter.h>
#include <fcntl.h>
#include <errno.h>
#include "arp.h"
@ -48,6 +49,35 @@ static int arp_packet_num;
static int arp_open_fd(struct client_state_t *cs)
{
struct sock_filter sf_arp[] = {
// Verify that the frame has ethernet protocol type of ARP.
BPF_STMT(BPF_LD + BPF_H + BPF_ABS, 12),
BPF_JUMP(BPF_JMP + BPF_JEQ + BPF_K, htons(ETH_P_ARP), 1, 0),
BPF_STMT(BPF_RET + BPF_K, 0),
// Verify that the ARP hardware type field indicates Ethernet.
BPF_STMT(BPF_LD + BPF_H + BPF_ABS, 14),
BPF_JUMP(BPF_JMP + BPF_JEQ + BPF_K, htons(ARPHRD_ETHER), 1, 0),
BPF_STMT(BPF_RET + BPF_K, 0),
// Verify that the ARP protocol type field indicates IP.
BPF_STMT(BPF_LD + BPF_H + BPF_ABS, 16),
BPF_JUMP(BPF_JMP + BPF_JEQ + BPF_K, htons(ETH_P_IP), 1, 0),
BPF_STMT(BPF_RET + BPF_K, 0),
// Verify that the ARP hardware address length field is 6.
BPF_STMT(BPF_LD + BPF_B + BPF_ABS, 18),
BPF_JUMP(BPF_JMP + BPF_JEQ + BPF_K, 6, 1, 0),
BPF_STMT(BPF_RET + BPF_K, 0),
// Verify that the ARP protocol address length field is 4.
BPF_STMT(BPF_LD + BPF_B + BPF_ABS, 19),
BPF_JUMP(BPF_JMP + BPF_JEQ + BPF_K, 4, 1, 0),
BPF_STMT(BPF_RET + BPF_K, 0),
// Sanity tests passed, so send all possible data.
BPF_STMT(BPF_RET + BPF_K, 0x0fffffff),
};
struct sock_fprog sfp_arp = {
.len = sizeof sf_arp / sizeof sf_arp[0],
.filter = (struct sock_filter *)sf_arp,
};
if (cs->arpFd != -1)
return 0;
@ -57,6 +87,11 @@ static int arp_open_fd(struct client_state_t *cs)
goto out;
}
// Ignoring error since kernel may lack support for BPF.
if (setsockopt(fd, SOL_SOCKET, SO_ATTACH_FILTER, &sfp_arp,
sizeof sfp_arp) >= 0)
log_line("Attached filter to raw ARP socket fd %d", fd);
int opt = 1;
if (setsockopt(fd, SOL_SOCKET, SO_BROADCAST, &opt, sizeof opt) == -1) {
log_error("arp: failed to set broadcast: %s", strerror(errno));