summaryrefslogtreecommitdiffstats
diff options
context:
space:
mode:
authorRoy Marples <roy@marples.name>2020-02-05 13:29:45 +0000
committerRoy Marples <roy@marples.name>2020-02-05 13:29:45 +0000
commit984b9c34d4c1dab2271fa1cfe3c261fb5ff5caa3 (patch)
treea89b64dd9a40779ece039744cda8d162d960a17c
parent9905946b353a2e11465ad2337f20f7b56d964a36 (diff)
downloaddhcpcd-984b9c34d4c1dab2271fa1cfe3c261fb5ff5caa3.tar.xz
BPF: Return the frame header with the data
For DHCP, we then just skip over the frame header. For ARP, we extract the frame source and destination addresses so we can log the source in the event of a conflict. This is important as a user has found a router which sets the ARP source and destination hardware addresses to all zeros but unicasts the ARP straight to our hardware address. https://serverfault.com/questions/297425/ip-address-conflict-with-mac-address-000000000000 https://discussions.flightaware.com/t/piaware-wont-reconnect-to-wifi-network-if-it-drops-off/59789
-rw-r--r--src/arp.c30
-rw-r--r--src/arp.h7
-rw-r--r--src/bpf.c39
-rw-r--r--src/bpf.h2
-rw-r--r--src/dhcp.c8
-rw-r--r--src/if-linux.c5
6 files changed, 76 insertions, 15 deletions
diff --git a/src/arp.c b/src/arp.c
index 0fdcdd9a..2460a1a3 100644
--- a/src/arp.c
+++ b/src/arp.c
@@ -115,7 +115,8 @@ static void
arp_report_conflicted(const struct arp_state *astate,
const struct arp_msg *amsg)
{
- char buf[HWADDR_LEN * 3];
+ char abuf[HWADDR_LEN * 3];
+ char fbuf[HWADDR_LEN * 3];
if (amsg == NULL) {
logerrx("%s: DAD detected %s",
@@ -123,9 +124,16 @@ arp_report_conflicted(const struct arp_state *astate,
return;
}
- logerrx("%s: hardware address %s claims %s",
- astate->iface->name,
- hwaddr_ntoa(amsg->sha, astate->iface->hwlen, buf, sizeof(buf)),
+ hwaddr_ntoa(amsg->sha, astate->iface->hwlen, abuf, sizeof(abuf));
+ if (bpf_frame_header_len(astate->iface) == 0) {
+ logerrx("%s: %s claims %s",
+ astate->iface->name, abuf, inet_ntoa(astate->addr));
+ return;
+ }
+
+ logerrx("%s: %s(%s) claims %s",
+ astate->iface->name, abuf,
+ hwaddr_ntoa(amsg->fsha, astate->iface->hwlen, fbuf, sizeof(fbuf)),
inet_ntoa(astate->addr));
}
@@ -209,6 +217,7 @@ arp_validate(const struct interface *ifp, struct arphdr *arp)
static void
arp_packet(struct interface *ifp, uint8_t *data, size_t len)
{
+ size_t fl = bpf_frame_header_len(ifp), falen;
const struct interface *ifn;
struct arphdr ar;
struct arp_msg arm;
@@ -216,6 +225,19 @@ arp_packet(struct interface *ifp, uint8_t *data, size_t len)
struct arp_state *astate, *astaten;
uint8_t *hw_s, *hw_t;
+ /* Copy the frame header source and destination out */
+ memset(&arm, 0, sizeof(arm));
+ hw_s = bpf_frame_header_src(ifp, data, &falen);
+ if (hw_s != NULL && falen <= sizeof(arm.fsha))
+ memcpy(arm.fsha, hw_s, falen);
+ hw_t = bpf_frame_header_dst(ifp, data, &falen);
+ if (hw_t != NULL && falen <= sizeof(arm.ftha))
+ memcpy(arm.ftha, hw_t, falen);
+
+ /* Skip past the frame header */
+ data += fl;
+ len -= fl;
+
/* We must have a full ARP header */
if (len < sizeof(ar))
return;
diff --git a/src/arp.h b/src/arp.h
index 4842c357..7522bf28 100644
--- a/src/arp.h
+++ b/src/arp.h
@@ -54,10 +54,13 @@
struct arp_msg {
uint16_t op;
- unsigned char sha[HWADDR_LEN];
+ uint8_t sha[HWADDR_LEN];
struct in_addr sip;
- unsigned char tha[HWADDR_LEN];
+ uint8_t tha[HWADDR_LEN];
struct in_addr tip;
+ /* Frame header and sender to diagnose failures */
+ uint8_t fsha[HWADDR_LEN];
+ uint8_t ftha[HWADDR_LEN];
};
struct arp_state {
diff --git a/src/bpf.c b/src/bpf.c
index 6c31bc2c..8a0a7e33 100644
--- a/src/bpf.c
+++ b/src/bpf.c
@@ -93,6 +93,38 @@ bpf_frame_header_len(const struct interface *ifp)
}
}
+void *
+bpf_frame_header_src(const struct interface *ifp, void *fh, size_t *len)
+{
+ uint8_t *f = fh;
+
+ switch (ifp->family) {
+ case ARPHRD_ETHER:
+ *len = sizeof(((struct ether_header *)0)->ether_shost);
+ return f + offsetof(struct ether_header, ether_shost);
+ default:
+ *len = 0;
+ errno = ENOTSUP;
+ return NULL;
+ }
+}
+
+void *
+bpf_frame_header_dst(const struct interface *ifp, void *fh, size_t *len)
+{
+ uint8_t *f = fh;
+
+ switch (ifp->family) {
+ case ARPHRD_ETHER:
+ *len = sizeof(((struct ether_header *)0)->ether_dhost);
+ return f + offsetof(struct ether_header, ether_dhost);
+ default:
+ *len = 0;
+ errno = ENOTSUP;
+ return NULL;
+ }
+}
+
static const uint8_t etherbcastaddr[] =
{ 0xff, 0xff, 0xff, 0xff, 0xff, 0xff };
@@ -215,7 +247,6 @@ ssize_t
bpf_read(struct interface *ifp, int fd, void *data, size_t len,
unsigned int *flags)
{
- ssize_t fl = (ssize_t)bpf_frame_header_len(ifp);
ssize_t bytes;
struct ipv4_state *state = IPV4_STATE(ifp);
@@ -250,10 +281,10 @@ bpf_read(struct interface *ifp, int fd, void *data, size_t len,
*flags |= BPF_BCAST;
else
*flags &= ~BPF_BCAST;
- payload += fl;
- bytes = (ssize_t)packet.bh_caplen - fl;
- if ((size_t)bytes > len)
+ if (packet.bh_caplen > len)
bytes = (ssize_t)len;
+ else
+ bytes = (ssize_t)packet.bh_caplen;
memcpy(data, payload, (size_t)bytes);
next:
state->buffer_pos += BPF_WORDALIGN(packet.bh_hdrlen +
diff --git a/src/bpf.h b/src/bpf.h
index 46bc6c37..67f4456b 100644
--- a/src/bpf.h
+++ b/src/bpf.h
@@ -57,6 +57,8 @@
extern const char *bpf_name;
size_t bpf_frame_header_len(const struct interface *);
+void *bpf_frame_header_src(const struct interface *, void *, size_t *);
+void *bpf_frame_header_dst(const struct interface *, void *, size_t *);
int bpf_frame_bcast(const struct interface *, const char *frame);
int bpf_open(struct interface *, int (*)(struct interface *, int));
int bpf_close(struct interface *, int);
diff --git a/src/dhcp.c b/src/dhcp.c
index b9757be0..1be4addd 100644
--- a/src/dhcp.c
+++ b/src/dhcp.c
@@ -3435,6 +3435,7 @@ dhcp_readbpf(void *arg)
uint8_t buf[MTU_MAX];
ssize_t bytes;
struct dhcp_state *state = D_STATE(ifp);
+ ssize_t fl = (ssize_t)bpf_frame_header_len(ifp);
/* Some RAW mechanisms are generic file descriptors, not sockets.
* This means we have no kernel call to just get one packet,
@@ -3451,7 +3452,12 @@ dhcp_readbpf(void *arg)
}
break;
}
- dhcp_packet(ifp, buf, (size_t)bytes);
+ if (bytes < fl) {
+ logerrx("%s: %s: short frame header",
+ __func__, ifp->name);
+ break;
+ }
+ dhcp_packet(ifp, buf + fl, (size_t)(bytes - fl));
/* Check we still have a state after processing. */
if ((state = D_STATE(ifp)) == NULL)
break;
diff --git a/src/if-linux.c b/src/if-linux.c
index aad2142b..499ced6c 100644
--- a/src/if-linux.c
+++ b/src/if-linux.c
@@ -1569,16 +1569,13 @@ bpf_read(struct interface *ifp, int s, void *data, size_t len,
*flags |= BPF_EOF; /* We only ever read one packet. */
*flags &= ~BPF_PARTIALCSUM;
if (bytes) {
- ssize_t fl = (ssize_t)bpf_frame_header_len(ifp);
-
if (bpf_frame_bcast(ifp, state->buffer) == 0)
*flags |= BPF_BCAST;
else
*flags &= ~BPF_BCAST;
- bytes -= fl;
if ((size_t)bytes > len)
bytes = (ssize_t)len;
- memcpy(data, state->buffer + fl, (size_t)bytes);
+ memcpy(data, state->buffer, (size_t)bytes);
#ifdef PACKET_AUXDATA
for (cmsg = CMSG_FIRSTHDR(&msg);
cmsg;