Skip to content

Mitigate ARP poisoning by preventing overwrite#58

Open
danielinux wants to merge 3 commits intowolfSSL:masterfrom
danielinux:arp-fixes
Open

Mitigate ARP poisoning by preventing overwrite#58
danielinux wants to merge 3 commits intowolfSSL:masterfrom
danielinux:arp-fixes

Conversation

@danielinux
Copy link
Member

@danielinux danielinux commented Mar 2, 2026

When receiving unsolicited ARP traffic, wolfIP
accepts neighbors addresses. This prevents overwriting/poisoning ARP tables when an attacker is trying to impersonate an existing neighbor.

For latency/performance optimizations, wolfIP is currently still populating the neighbor cache based on valid ARP requests from neighbors, if neighbors were not known.

Fenrir/226

edit: after Copilot's review, I decided to add an aging mechanism to limit issues with renewal of neighbors records. Also added tests to check aging.

Copilot AI review requested due to automatic review settings March 2, 2026 17:00
Copy link
Contributor

Copilot AI left a comment

Choose a reason for hiding this comment

The reason will be displayed to describe this comment to others. Learn more.

Pull request overview

This PR hardens wolfIP’s ARP neighbor learning to mitigate ARP-table poisoning by preventing existing neighbor entries from being overwritten by unsolicited ARP traffic, while still allowing updates when an ARP reply corresponds to a recently-sent ARP request.

Changes:

  • Add tracking for recently-issued ARP requests (pending requests with TTL) to distinguish solicited vs unsolicited ARP replies.
  • Prevent ARP requests and unsolicited ARP replies from overwriting an existing neighbor cache entry.
  • Add unit tests covering: no-overwrite on unsolicited reply, overwrite allowed when reply matches a pending request, and no-overwrite on ARP request.

Reviewed changes

Copilot reviewed 2 out of 2 changed files in this pull request and generated 2 comments.

File Description
src/wolfip.c Adds pending-request tracking and changes ARP neighbor update rules to block unsolicited overwrites.
src/test/unit/unit.c Adds/adjusts ARP unit tests to validate the new overwrite-prevention behavior.

💡 Add Copilot custom instructions for smarter, more guided reviews. Learn how to get started.

When receiving unsolicited ARP traffic, wolfIP
accepts neighbors addresses. This prevents overwriting/poisoning
ARP tables when an attacker is trying to impersonate an existing
neighbor.

For latency/performance optimizations, wolfIP is currently still
populating the neighbor cache based on valid ARP requests from
neighbors, if neighbors were not known.
Copy link
Contributor

Copilot AI left a comment

Choose a reason for hiding this comment

The reason will be displayed to describe this comment to others. Learn more.

Pull request overview

Copilot reviewed 2 out of 2 changed files in this pull request and generated 1 comment.

Comments suppressed due to low confidence (1)

src/wolfip.c:5284

  • arp_pending_record() is called before the send filter check and before verifying the ARP request is actually transmitted. If wolfIP_filter_notify_eth() blocks sending (or ll->send is NULL), a pending entry is still recorded and an attacker can then send a spoofed ARP reply within the TTL that will be treated as solicited and allowed to update/overwrite the neighbor entry. Move the pending record to only occur after the send filter passes (and ideally only when the request is actually sent).
    arp.tip = ee32(tip);
    arp_pending_record(s, if_idx, tip);
    if (ll->send) {
        if (wolfIP_filter_notify_eth(WOLFIP_FILT_SENDING, s, if_idx, &arp.eth,
                                     sizeof(struct arp_packet)) != 0)
            return;

💡 Add Copilot custom instructions for smarter, more guided reviews. Learn how to get started.

}
}

static int arp_neighbor_index(struct wolfIP *s, unsigned int if_idx, ip4 ip)
Copy link
Contributor

Choose a reason for hiding this comment

The reason will be displayed to describe this comment to others. Learn more.

nit: missing docs.
this applies for all the new functions that were introduced.

in particular for this one I think it should be made clear that it returns index or -1, and that it envicts/removesss an entry if it aged out with the new mechanism of aging.

arp->sip = ee32(conf->ip);
arp_store_neighbor(s, if_idx, ee32(sender_ip), sender_mac);
if (arp_neighbor_index(s, if_idx, ee32(sender_ip)) < 0) {
arp_store_neighbor(s, if_idx, ee32(sender_ip), sender_mac);
Copy link
Contributor

Choose a reason for hiding this comment

The reason will be displayed to describe this comment to others. Learn more.

let me know if I understood this correctly: since the ARP_AGING_TIMEOUT is @ 120s, let's say a valid neighbor is added.
and like this neighbor only sends arp requests and no replies, it technicallly will age out right? and it will be removed via the index function, but then it will be ad dded later on again, it uses an entry for no reason and then it will get re-added again

struct arp_neighbor neighbors[MAX_NEIGHBORS];
struct arp_pending_req pending[WOLFIP_ARP_PENDING_MAX];
} arp;
struct arp_pending_entry arp_pending[WOLFIP_ARP_PENDING_MAX];
Copy link
Contributor

Choose a reason for hiding this comment

The reason will be displayed to describe this comment to others. Learn more.

nit: maybe a a one-liner comment on arp_pending_req explaining the difference between arp_pending_req and arp_pending_entry would probably make it more readable to understand why two arrays are required

Sign up for free to join this conversation on GitHub. Already have an account? Sign in to comment

Labels

None yet

Projects

None yet

Development

Successfully merging this pull request may close these issues.

3 participants