Blog post

Sending and processing ARP requests/responses using BPF (updated)

ARP BPF OpenBSD FreeBSD Networking

By Marco W. Soijer — November 2020

Share this post

The Address Resolution Protocol (ARP) is handled by the operating system. With the command line tool arp you can see the cache and clear entries, but you can neither trigger a request in order to refresh an entry or to check its validitiy, nor resolve an IPv4 address without sending anything on the Internet layer (3) or above — even a ping involves ICMP.

So you may want to control sending ARP queries from userland, and process the incoming responses. The required interface to the link-layer on Linux and BSD systems is the Berkeley Packet Filter (BPF). Under BSD, it appears as the device /dev/bpf and can be addressed through normal read and write operations, plus some ioctl. This post describes how — by building an ARP scanner that queries all addresses in the local network. You can find the full C code at the end of the page.

The Berkeley Packet Filter (BPF)

The Berkeley Packet Filter is a pseudo device, included with basically all Linux distributions, and all BSD systems — it is also part of BSD-based macOS. BPF provides a raw interface to link-layer network functions and is basically used to build firewalls, sniffers and the like. The way how to interact with BPF differs on the various systems, however — you can find details in the documentation for OpenBSD, FreeBSD, NetBSD, Linux Kernel, or macOS, although the latter also allows more high-level ARP control.

The BSD variants all seem to share the same /dev/bpf access, so what we do here with FreeBSD, can probably be applied to the other BSDs without change (except for the octet names in struct ether_addr as noted below).

The main process goes as follows: open the BPF device for reading and writing, attach it to the network interface on which you want to ARP scan, write the Ethernet frame with the ARP query, read the ARP responses that come back, clean up and close the device. There are two things that require a bit of additional effort: finding your own protocol (IP) and hardware (MAC) address for the chosen interface — so you can fill the Ethernet frame header correctly — and activating a packet filter, in order to receive only the ARP responses you are interested in and not everything that is on the network. Now you know where the name BPF comes from.

Opening the device

In our arpscan.c, we use the first command line argument argv[1] to pass the name of the interface on which we want to do our scan to the main function. So the most outside functionality looks like this:

Excerpt from arpscan.c
//...
175
int main(int argc, char *argv[]) {
176
177
  int fd;
//...
205
  if (argc == 2) {
206
    if ((fd = open("/dev/bpf", O_RDWR)) > 0) {
//...
232
    }
233
    else
234
      fprintf(stderr, "%s (open)\n", strerror(errno));
235
  }
236
  else
237
    fprintf(stdout, "Usage:\tarpscan if\n");
238
239
  return -1;
240
}

With the file descriptor, BPF can be bound to the interface passed as argv[1]; the ioctl request to do so is BIOCSETIF, which takes a pointer to a struct ifreq. Furthermore, we need to create a buffer to receive the incoming frames, so we need BPF's buffer length, requested with BIOCGBLEN; and we want BPF to return any ARP frame it receives immediately, which is set with BIOCSETIF, for which we abuse the int buflen that subsequently receives the buffer size. If binding the interface is successful, it is good to check whether the interface is indeed an Ethernet one (BIOCGDLT). So inside main, we add the following:

Excerpt from arpscan.c
178
  int buflen;
179
  int dlt;
180
  struct ifreq ir;
//...
207
      strncpy(ir.ifr_name, argv[1], IFNAMSIZ);
208
      buflen = 1;
209
      if (ioctl(fd, BIOCSETIF, &ir) != -1
210
          && ioctl(fd, BIOCIMMEDIATE, &buflen) != -1
211
          && ioctl(fd, BIOCGBLEN, &buflen) != -1) {
212
        if (ioctl(fd, BIOCGDLT, &dlt) != -1
213
            && dlt == DLT_EN10MB) {
//...
226
        }
227
        else
228
          fprintf(stderr, "Link type unknown or not Ethernet\n");
229
      }
230
      else
231
        fprintf(stderr, "%s (ioctl)\n", strerror(errno));

Setting the packet filter

Now for the most interesting part: setting the filter. The ioctl request for this is BIOCSETF, which takes a pointer to a struct bpf_program, which in turn is an integer with the length of the programme, followed by an array of instructions (struct bpf_insn *).

The filter language is described with some examples on the OpenBSD BPF manual page. Think machine code, with simple instructions like loading a byte (from the frame) into the accumulator, comparing, jumping — forward only — and returning. Working at the link layer, we need to take care of the whole Ethernet frame, although upon sending, BPF automatically adds any required padding — which is needed here, as ARP messages underrun the minimum frame length of 64 octets — and the CRC32-based frame check sequence. So we are left with the 14 octets of the frame that contain the destination and source addresses as six-octet hardware addresses each plus the length of type word, and the 28 octets of the ARP message.

To have BPF filter out ARP responses, we look at two words:

  • First, within the 14-octet frame header, bytes 12 and 13 (length of type) must equal 0x0806 or ETHERTYPE_ARP, to check that the payload is an ARP message; and
  • second, within the 28-octet ARP message, bytes 6 and 7 (opcode) must equal 2 or ARPOP_REPLY, to ensure that the message is an ARP response.

Thus we add to our main function:

Excerpt from arpscan.c
185
  // BPF rule
186
  struct bpf_insn insns[] = {
187
    // Load word at octet 12
188
    BPF_STMT(BPF_LD | BPF_H | BPF_ABS, 12),
189
    // If not ETHERTYPE_ARP, skip next 3 (and return nothing)
190
    BPF_JUMP(BPF_JMP | BPF_JEQ | BPF_K, ETHERTYPE_ARP, 0, 3),
191
    // Load word at octet 20
192
    BPF_STMT(BPF_LD | BPF_H | BPF_ABS, 20),
193
    // If not ARPOP_REPLY, skip next 1 (and return nothing)
194
    BPF_JUMP(BPF_JMP | BPF_JEQ | BPF_K, ARPOP_REPLY, 0, 1),
195
    // Valid ARP reply received, return message
196
    BPF_STMT(BPF_RET | BPF_K, sizeof(struct ether_arp) + sizeof(struct ether_header)),
197
    // Return nothing
198
    BPF_STMT(BPF_RET | BPF_K, 0),
199
  };
200
  struct bpf_program filter = {
201
    sizeof insns / sizeof(insns[0]),
202
    insns
203
  };
//...
214
          if (ioctl(fd, BIOCSETF, &filter) != -1) {
//...
223
          }
224
          else
225
            fprintf(stderr, "Cannot set BPF rule\n");

Retrieving the interface addresses

Having to create an Ethernet frame ourselves, we need to know the hardware (MAC) and protocol (IP) addresses of the chosen interface. While we are at it, we also collect the address mask for the network, so we can determine what range of addresses to scan. The library function getifaddrs() provides a linked list of struct ifaddrs with everything we need; we only need to look for the right chunks.

Excerpt from arpscan.c
23
// Find own protocol (IP) and hardware (MAC) addresses
24
// Returns true iff both were found
25
26
bool findownaddresses(char *interface, struct ether_addr *ownmac,
27
    struct sockaddr_in *saip, struct sockaddr_in *samask) {
28
29
  struct ifaddrs *ifap, *ifa;
30
  struct sockaddr_dl *sdl;
31
  unsigned int success = 0;
32
33
  if (!getifaddrs(&ifap)) {
34
35
    printf("Self\n");
36
37
    for (ifa = ifap; ifa; ifa = ifa->ifa_next) {
38
39
      if (!strcmp(ifa->ifa_name, interface)) {
40
41
        sdl = (struct sockaddr_dl *)ifa->ifa_addr;
42
43
        if (sdl->sdl_family == AF_LINK
44
            && sdl->sdl_type == IFT_ETHER
45
            && sdl->sdl_alen == ETHER_ADDR_LEN) {
46
          memcpy((u_int8_t *)ownmac, (u_int8_t *)LLADDR(sdl), sizeof(struct ether_addr));
47
          printf("MAC: %02x:%02x:%02x:%02x:%02x:%02x\n",
48
              ownmac->octet[0], // ownmac->ether_addr_octet[...] on OpenBSD
49
              ownmac->octet[1],
50
              ownmac->octet[2],
51
              ownmac->octet[3],
52
              ownmac->octet[4],
53
              ownmac->octet[5]);
54
          success |= 0x01;
55
        }
56
        else if (sdl->sdl_family == AF_INET) {
57
          saip->sin_addr.s_addr = ((struct sockaddr_in *)ifa->ifa_addr)->sin_addr.s_addr;
58
          samask->sin_addr.s_addr = ((struct sockaddr_in *)ifa->ifa_netmask)->sin_addr.s_addr;
59
          printf("%s, ", inet_ntoa(saip->sin_addr));
60
          printf("netmask %s\n", inet_ntoa(samask->sin_addr));
61
          success |= 0x02;
62
        }
63
      }
64
    }
65
    freeifaddrs(ifap);
66
  }
67
  else
68
    fprintf(stderr, "%s (getifaddr)\n", strerror(errno));
69
70
  return (success == 0x03);
71
}
72
//...
174
//...
181
  struct ether_addr ownmac;
182
  struct sockaddr_in saip;
183
  struct sockaddr_in samask;
//...
216
            if (findownaddresses(argv[1], &ownmac, &saip, &samask)) {
//...
221
            else
222
              fprintf(stderr, "Missing address for interface\n");
223
          }
240
}

Writing the queries — or: flood them!

Filling the ARP request frame is straightforward. Using ff:ff:ff:ff:ff:ff as the broadcast destination address, filling in our own IP and MAC address, and setting the opcode and length indicators is equal for all queries:

Excerpt from arpscan.c
74
// Construct ethernet frame header and ARP request
75
76
void prepareframe(struct ether_addr *ownmac, struct sockaddr_in *saip,
77
    struct ether_header *ethhdr, struct ether_arp *etharp) {
78
79
  memset((unsigned char *)&ethhdr->ether_dhost, 0xff, ETHER_ADDR_LEN);
80
  memcpy((unsigned char *)&ethhdr->ether_shost, (unsigned char *)ownmac, ETHER_ADDR_LEN);
81
  ethhdr->ether_type = htons(ETHERTYPE_ARP);
82
83
  etharp->arp_hrd = htons(ARPHRD_ETHER);
84
  etharp->arp_pro = htons(ETHERTYPE_IP);
85
  etharp->arp_hln = ETHER_ADDR_LEN;
86
  etharp->arp_pln = 4;
87
  etharp->arp_op = htons(ARPOP_REQUEST);
88
  memcpy((u_int8_t *)etharp->arp_sha, (u_int8_t *)ownmac, sizeof(struct ether_addr));
89
  memcpy((u_int8_t *)etharp->arp_spa, (u_int8_t *)&(saip->sin_addr.s_addr), 4*sizeof(u_int8_t));
90
  memset((u_int8_t *)etharp->arp_tha, 0, ETHER_ADDR_LEN);
91
92
  return;
93
}

The structures ether_header and ether_arp are defined in the calling function, which also loops through the protocol addresses to query for. The range goes from the network address — the network-mask part of our own address — through the local broadcast address, less our own address. For a /24 network, this leaves 253 addresses to query for. You may not want to apply this arpscan as it is for larger networks…

Excerpt from arpscan.c
136
// Write ARP request to all monocast addresses in the network
137
// (all less network, broadcast, and self)
138
139
void writequeries(int fd, struct ether_addr *ownmac, struct sockaddr_in *saip, struct sockaddr_in *samask) {
140
141
  unsigned char msg[sizeof(struct ether_header) + sizeof(struct ether_arp)];
142
  struct ether_header *ethhdr;
143
  struct ether_arp *etharp;
144
  struct sockaddr_in sat;
145
  uint32_t addr, addrnw, addrbc, addrown;
146
  int len, addlen;
147
148
  ethhdr = (struct ether_header *)msg;
149
  etharp = (struct ether_arp *)(ethhdr + 1);
150
151
  prepareframe(ownmac, saip, ethhdr, etharp);
152
153
  addrown = ntohl(saip->sin_addr.s_addr);
154
  addrnw = ntohl(saip->sin_addr.s_addr & samask->sin_addr.s_addr);
155
  addrbc = addrnw + (0xffffffff - ntohl(samask->sin_addr.s_addr));
156
157
  printf("\nWho has? for %u IP addresses\n", addrbc - addrnw - 2);
158
159
  for (addr = addrnw + 1; addr < addrbc; addr++) {
160
    if (addr != addrown) {
161
      sat.sin_addr.s_addr = htonl(addr);
162
      memcpy((u_int8_t *)etharp->arp_tpa, (u_int8_t *)&(sat.sin_addr.s_addr), 4*sizeof(u_int8_t));
163
      len = 0;
164
      while (len<(int)(sizeof(struct ether_header) + sizeof(struct ether_arp))
165
          && (addlen = write(fd, (char *)ethhdr + len,
166
          sizeof(struct ether_header) + sizeof(struct ether_arp) - len)) >= 0)
167
        len += addlen;
168
    }
169
  }
170
171
  return;
172
}

Did I mention, you may not want to apply this on any network that is not yours? Harmless as such scans may be, people may not like it.

Receiving the responses — or: reel them in!

All we need to do now, is listen to BPF and print out the hardware and protocol addresses that come in. ARP is a simple, connectionless protocol that only works on the local network, so answers arrive quickly. What is not there within half a second (actually a lot less), will not arrive at all. So we stop polling and receiving when no further reply has come in for 500 milliseconds:

Excerpt from arpscan.c
96
// Collect filter outputs until no response for half a second
97
98
void collectresponses(int fd, int buflen) {
99
100
  unsigned char *buffer;
101
  struct bpf_hdr *bpf;
102
  int len;
103
  struct timeval timeout;
104
105
  if ((buffer = (unsigned char *)malloc(buflen))) {
106
107
    bpf = (struct bpf_hdr *)buffer;
108
109
    timeout.tv_sec = 0;
110
    timeout.tv_usec = 500000;
111
112
    if (ioctl(fd, BIOCSRTIMEOUT, &timeout) != -1) {
113
      while ((len = read(fd, buffer, buflen)) > 0)
114
        if (len >= (int)sizeof(struct bpf_hdr)
115
            && len >= bpf->bh_hdrlen + 0x2a
116
            && buffer[bpf->bh_hdrlen + 0x12] == 0x06
117
            && buffer[bpf->bh_hdrlen + 0x13] == 0x04)
118
          printf("\r%s is at %02x:%02x:%02x:%02x:%02x:%02x\n",
119
              inet_ntoa(*(struct in_addr *)(buffer + bpf->bh_hdrlen + 0x1c)),
120
              buffer[bpf->bh_hdrlen + 0x16],
121
              buffer[bpf->bh_hdrlen + 0x17],
122
              buffer[bpf->bh_hdrlen + 0x18],
123
              buffer[bpf->bh_hdrlen + 0x19],
124
              buffer[bpf->bh_hdrlen + 0x1a],
125
              buffer[bpf->bh_hdrlen + 0x1b]);
126
    }
127
128
    free(buffer);
129
  }
130
131
  return;
132
}

You can now almost piece together all of arpscan.c. The only thing that is missing apart from the includes, is the core of our main:

Excerpt from arpscan.c
175
int main(int argc, char *argv[]) {
//...
217
              writequeries(fd, &ownmac, &saip, &samask);
218
              collectresponses(fd, buflen);
219
              exit(0);
//...
240
}

There you go. Address resolution under full control from userland.

Full code

The following C99 source was developed on FreeBSD 12.2 (patched through November 2020), compiled with clang, and run on an amd64 system with Intel NICs.

arpscan.c
1
#include <stdio.h>
2
#include <stdlib.h>
3
#include <stdbool.h>
4
#include <unistd.h>
5
#include <string.h>
6
#include <errno.h>
7
8
#include <fcntl.h>
9
#include <poll.h>
10
#include <ifaddrs.h>
11
#include <arpa/inet.h>
12
#include <net/bpf.h>
13
#include <net/if.h>
14
#include <net/if_dl.h>
15
#include <net/if_types.h>
16
#include <netinet/in.h>
17
#include <netinet/if_ether.h>
18
#include <sys/ioctl.h>
19
#include <sys/types.h>
20
#include <sys/time.h>
21
22
23
// Find own protocol (IP) and hardware (MAC) addresses
24
// Returns true iff both were found
25
26
bool findownaddresses(char *interface, struct ether_addr *ownmac,
27
    struct sockaddr_in *saip, struct sockaddr_in *samask) {
28
29
  struct ifaddrs *ifap, *ifa;
30
  struct sockaddr_dl *sdl;
31
  unsigned int success = 0;
32
33
  if (!getifaddrs(&ifap)) {
34
35
    printf("Self\n");
36
37
    for (ifa = ifap; ifa; ifa = ifa->ifa_next) {
38
39
      if (!strcmp(ifa->ifa_name, interface)) {
40
41
        sdl = (struct sockaddr_dl *)ifa->ifa_addr;
42
43
        if (sdl->sdl_family == AF_LINK
44
            && sdl->sdl_type == IFT_ETHER
45
            && sdl->sdl_alen == ETHER_ADDR_LEN) {
46
          memcpy((u_int8_t *)ownmac, (u_int8_t *)LLADDR(sdl), sizeof(struct ether_addr));
47
          printf("MAC: %02x:%02x:%02x:%02x:%02x:%02x\n",
48
              ownmac->octet[0], // ownmac->ether_addr_octet[...] on OpenBSD
49
              ownmac->octet[1],
50
              ownmac->octet[2],
51
              ownmac->octet[3],
52
              ownmac->octet[4],
53
              ownmac->octet[5]);
54
          success |= 0x01;
55
        }
56
        else if (sdl->sdl_family == AF_INET) {
57
          saip->sin_addr.s_addr = ((struct sockaddr_in *)ifa->ifa_addr)->sin_addr.s_addr;
58
          samask->sin_addr.s_addr = ((struct sockaddr_in *)ifa->ifa_netmask)->sin_addr.s_addr;
59
          printf("%s, ", inet_ntoa(saip->sin_addr));
60
          printf("netmask %s\n", inet_ntoa(samask->sin_addr));
61
          success |= 0x02;
62
        }
63
      }
64
    }
65
    freeifaddrs(ifap);
66
  }
67
  else
68
    fprintf(stderr, "%s (getifaddr)\n", strerror(errno));
69
70
  return (success == 0x03);
71
}
72
73
74
// Construct ethernet frame header and ARP request
75
76
void prepareframe(struct ether_addr *ownmac, struct sockaddr_in *saip,
77
    struct ether_header *ethhdr, struct ether_arp *etharp) {
78
79
  memset((unsigned char *)&ethhdr->ether_dhost, 0xff, ETHER_ADDR_LEN);
80
  memcpy((unsigned char *)&ethhdr->ether_shost, (unsigned char *)ownmac, ETHER_ADDR_LEN);
81
  ethhdr->ether_type = htons(ETHERTYPE_ARP);
82
83
  etharp->arp_hrd = htons(ARPHRD_ETHER);
84
  etharp->arp_pro = htons(ETHERTYPE_IP);
85
  etharp->arp_hln = ETHER_ADDR_LEN;
86
  etharp->arp_pln = 4;
87
  etharp->arp_op = htons(ARPOP_REQUEST);
88
  memcpy((u_int8_t *)etharp->arp_sha, (u_int8_t *)ownmac, sizeof(struct ether_addr));
89
  memcpy((u_int8_t *)etharp->arp_spa, (u_int8_t *)&(saip->sin_addr.s_addr), 4*sizeof(u_int8_t));
90
  memset((u_int8_t *)etharp->arp_tha, 0, ETHER_ADDR_LEN);
91
92
  return;
93
}
94
95
96
// Collect filter outputs until no response for half a second
97
98
void collectresponses(int fd, int buflen) {
99
100
  unsigned char *buffer;
101
  struct bpf_hdr *bpf;
102
  int len;
103
  struct timeval timeout;
104
105
  if ((buffer = (unsigned char *)malloc(buflen))) {
106
107
    bpf = (struct bpf_hdr *)buffer;
108
109
    timeout.tv_sec = 0;
110
    timeout.tv_usec = 500000;
111
112
    if (ioctl(fd, BIOCSRTIMEOUT, &timeout) != -1) {
113
      while ((len = read(fd, buffer, buflen)) > 0)
114
        if (len >= (int)sizeof(struct bpf_hdr)
115
            && len >= bpf->bh_hdrlen + 0x2a
116
            && buffer[bpf->bh_hdrlen + 0x12] == 0x06
117
            && buffer[bpf->bh_hdrlen + 0x13] == 0x04)
118
          printf("\r%s is at %02x:%02x:%02x:%02x:%02x:%02x\n",
119
              inet_ntoa(*(struct in_addr *)(buffer + bpf->bh_hdrlen + 0x1c)),
120
              buffer[bpf->bh_hdrlen + 0x16],
121
              buffer[bpf->bh_hdrlen + 0x17],
122
              buffer[bpf->bh_hdrlen + 0x18],
123
              buffer[bpf->bh_hdrlen + 0x19],
124
              buffer[bpf->bh_hdrlen + 0x1a],
125
              buffer[bpf->bh_hdrlen + 0x1b]);
126
    }
127
128
    free(buffer);
129
  }
130
131
  return;
132
}
133
134
135
136
// Write ARP request to all monocast addresses in the network
137
// (all less network, broadcast, and self)
138
139
void writequeries(int fd, struct ether_addr *ownmac, struct sockaddr_in *saip, struct sockaddr_in *samask) {
140
141
  unsigned char msg[sizeof(struct ether_header) + sizeof(struct ether_arp)];
142
  struct ether_header *ethhdr;
143
  struct ether_arp *etharp;
144
  struct sockaddr_in sat;
145
  uint32_t addr, addrnw, addrbc, addrown;
146
  int len, addlen;
147
148
  ethhdr = (struct ether_header *)msg;
149
  etharp = (struct ether_arp *)(ethhdr + 1);
150
151
  prepareframe(ownmac, saip, ethhdr, etharp);
152
153
  addrown = ntohl(saip->sin_addr.s_addr);
154
  addrnw = ntohl(saip->sin_addr.s_addr & samask->sin_addr.s_addr);
155
  addrbc = addrnw + (0xffffffff - ntohl(samask->sin_addr.s_addr));
156
157
  printf("\nWho has? for %u IP addresses\n", addrbc - addrnw - 2);
158
159
  for (addr = addrnw + 1; addr < addrbc; addr++) {
160
    if (addr != addrown) {
161
      sat.sin_addr.s_addr = htonl(addr);
162
      memcpy((u_int8_t *)etharp->arp_tpa, (u_int8_t *)&(sat.sin_addr.s_addr), 4*sizeof(u_int8_t));
163
      len = 0;
164
      while (len<(int)(sizeof(struct ether_header) + sizeof(struct ether_arp))
165
          && (addlen = write(fd, (char *)ethhdr + len,
166
          sizeof(struct ether_header) + sizeof(struct ether_arp) - len)) >= 0)
167
        len += addlen;
168
    }
169
  }
170
171
  return;
172
}
173
174
175
int main(int argc, char *argv[]) {
176
177
  int fd;
178
  int buflen;
179
  int dlt;
180
  struct ifreq ir;
181
  struct ether_addr ownmac;
182
  struct sockaddr_in saip;
183
  struct sockaddr_in samask;
184
185
  // BPF rule
186
  struct bpf_insn insns[] = {
187
    // Load word at octet 12
188
    BPF_STMT(BPF_LD | BPF_H | BPF_ABS, 12),
189
    // If not ETHERTYPE_ARP, skip next 3 (and return nothing)
190
    BPF_JUMP(BPF_JMP | BPF_JEQ | BPF_K, ETHERTYPE_ARP, 0, 3),
191
    // Load word at octet 20
192
    BPF_STMT(BPF_LD | BPF_H | BPF_ABS, 20),
193
    // If not ARPOP_REPLY, skip next 1 (and return nothing)
194
    BPF_JUMP(BPF_JMP | BPF_JEQ | BPF_K, ARPOP_REPLY, 0, 1),
195
    // Valid ARP reply received, return message
196
    BPF_STMT(BPF_RET | BPF_K, sizeof(struct ether_arp) + sizeof(struct ether_header)),
197
    // Return nothing
198
    BPF_STMT(BPF_RET | BPF_K, 0),
199
  };
200
  struct bpf_program filter = {
201
    sizeof insns / sizeof(insns[0]),
202
    insns
203
  };
204
205
  if (argc == 2) {
206
    if ((fd = open("/dev/bpf", O_RDWR)) > 0) {
207
      strncpy(ir.ifr_name, argv[1], IFNAMSIZ);
208
      buflen = 1;
209
      if (ioctl(fd, BIOCSETIF, &ir) != -1
210
          && ioctl(fd, BIOCIMMEDIATE, &buflen) != -1
211
          && ioctl(fd, BIOCGBLEN, &buflen) != -1) {
212
        if (ioctl(fd, BIOCGDLT, &dlt) != -1
213
            && dlt == DLT_EN10MB) {
214
          if (ioctl(fd, BIOCSETF, &filter) != -1) {
215
216
            if (findownaddresses(argv[1], &ownmac, &saip, &samask)) {
217
              writequeries(fd, &ownmac, &saip, &samask);
218
              collectresponses(fd, buflen);
219
              exit(0);
220
            }
221
            else
222
              fprintf(stderr, "Missing address for interface\n");
223
          }
224
          else
225
            fprintf(stderr, "Cannot set BPF rule\n");
226
        }
227
        else
228
          fprintf(stderr, "Link type unknown or not Ethernet\n");
229
      }
230
      else
231
        fprintf(stderr, "%s (ioctl)\n", strerror(errno));
232
    }
233
    else
234
      fprintf(stderr, "%s (open)\n", strerror(errno));
235
  }
236
  else
237
    fprintf(stdout, "Usage:\tarpscan if\n");
238
239
  return -1;
240
}