2
votes

I'am writing an network library in C from scratch. I already implemented the Ethernet protocol and now I want to get ARP working. Sending Requests/Replies works fine but receiving isn't working well. When I send an send an Request and wait for the Reply after it, recvfrom() just takes the first incoming ARP packet. But I want to get the Reply from the host replying to my Request.

I can't just receive packets until the correct one arrives because the library should support socket timeouts. (set with setsockopt())

The socket is created like this:

int sfd = socket(PF_PACKET, SOCK_RAW, htons(ETH_P_ARP))

Output of my test program:

Sending ARP-Request ...

0o0o0o0 ARP-FRAME DUMP 0o0o0o0

HwType: 256 (0x7ab74e358670)
PrType: 8 (0x7ab74e358672)
HwALen: 6 (0x7ab74e358674)
PrALen: 4 (0x7ab74e358675)
ArpOP : 256 (0x7ab74e358676)
Sha   : 84:4b:10:14:a0:04 (0x7ab74e358678)
Spa   : 192.168.12.1 (0x7ab74e35867e)
Tha   : 00:00:00:00:00:00 (0x7ab74e358682)
Tpa   : 192.168.0.3 (0x7ab74e358688)

Receiving ARP-Reply ...


0o0o0o0 ARP-FRAME DUMP 0o0o0o0

HwType: 256 (0x7ab74e358670)
PrType: 8 (0x7ab74e358672)
HwALen: 6 (0x7ab74e358674)
PrALen: 4 (0x7ab74e358675)
ArpOP : 512 (0x7ab74e358676)
Sha   : 10:00:00:00:00:01 (0x7ab74e358678)
Spa   : 192.168.12.78 (0x7ab74e35867e)
Tha   : 84:4b:10:14:a0:04 (0x7ab74e358682)
Tpa   : 192.168.12.1 (0x7ab74e358688)

Is it possible to filter incoming ARP packets/how to do it?

Thanks in advance.

--- EDIT ---

I played around with the BPF and it worked fine for filtering out ARP packets. It wrote this filter

ld [28]
jne #0x4e0ca8c0, drop
ret #-1
drop: ret #0

to filter the incoming ARP replies. It should load the source IP from the packet and compare it with the one defined in the code. 0x4e0ca8c0 is the valid IP addr. of the host whose reply I want. tcpdump shows the incoming reply but my program freezes (Waits forever). I used the BPF like this:

struct sock_fprog prog;
struct sock_filter filter[4] =\
{{ 0x20,  0,  0, 0x0000001c },
 { 0x15,  0,  1, 0x4e0ca8c0 },
 { 0x06,  0,  0, 0xffffffff },
 { 0x06,  0,  0, 0000000000 }};

prog.len = 4;
prog.filter = filter;

if(setsockopt(sfd, SOL_SOCKET, SO_ATTACH_FILTER, &prog, sizeof(prog)) ==\
-1) {
    fprintf(stderr, "ERROR enabling bpf: setsockopt(): %s\n",\
    strerror(errno));
    goto ... (error handler);
}
/* Receive incoming ARP reply */

I hope I haven't made any stupid/obvious mistakes.

Thanks in advance!

-- LAST EDIT --

The IP addr. must be in NBO. So with the correct BPF:

struct sock_filter filter[4] =\
{{ 0x20,  0,  0, 0x0000001c },
 { 0x15,  0,  1, 0xc0a80c4e },
 { 0x06,  0,  0, 0xffffffff },
 { 0x06,  0,  0, 0000000000 }};

... only replies from 192.168.12.78 (0xc0a80c4e) are accepted/received.

Huge thanks to Ctx!

1

1 Answers

1
votes

You can use a (properly configured) Berkeley Packet Filter (BPF) for it.

For this, you have to write a (small) BPF-filter, which exactly matches the reply you are looking for.

#include <linux/filter.h>

struct sock_filter filter[NR_INSTRUCTIONS];

filter[0].code = ...;
filter[0].k = ...;
filter[0].jt = ...;
filter[0].jf = ...;
filter[1].code = ...;
...

The "language" is very basic and is described here

Then put the instructions together into a BPF program:

struct sock_fprog prog;
prog.len = NR_INSTRUCTIONS;
prog.filter = filter;

Finally, you can attach the filter to your socket:

setsockopt(fd, SOL_SOCKET, SO_ATTACH_FILTER, &prog, sizeof(prog));

If you configured your filter correctly, you will only receive the matching arp replies on your socket from now on.

For the next request/reply you have to reconfigure the filter to match the new parameters (SO_DETACH_FILTER followed by SO_ATTACH_FILTER with the new program).

For ARP packets it is especially easy to construct a filter, since the offsets of the fields in the packet are fixed.