From 55314f9874ea04ab2b0603fccb4b2f329b06724e Mon Sep 17 00:00:00 2001 From: Roy Marples Date: Sat, 14 May 2016 12:57:32 +0000 Subject: [PATCH] No point in looping on the raw socket anymore thanks to eloop. This allows endless DHCP / ARP to be interuptable. --- arp.c | 109 +++++++++++++++++++++++++-------------------------- dhcp.c | 112 ++++++++++++++++++++++++----------------------------- if-bsd.c | 4 +- if-linux.c | 2 +- if.h | 3 +- 5 files changed, 106 insertions(+), 124 deletions(-) diff --git a/arp.c b/arp.c index ab313673..64f12371 100644 --- a/arp.c +++ b/arp.c @@ -123,7 +123,7 @@ arp_packet(void *arg) { struct interface *ifp = arg; const struct interface *ifn; - uint8_t arp_buffer[ARP_LEN]; + uint8_t buf[ARP_LEN]; struct arphdr ar; struct arp_msg arm; ssize_t bytes; @@ -134,65 +134,62 @@ arp_packet(void *arg) state = ARP_STATE(ifp); flags = 0; - while (!(flags & RAW_EOF)) { - bytes = if_readraw(ifp, state->fd, - arp_buffer, sizeof(arp_buffer), &flags); - if (bytes == -1) { - logger(ifp->ctx, LOG_ERR, - "%s: arp if_readrawpacket: %m", ifp->name); - arp_close(ifp); - return; - } - /* We must have a full ARP header */ - if ((size_t)bytes < sizeof(ar)) - continue; - memcpy(&ar, arp_buffer, sizeof(ar)); - /* Families must match */ - if (ar.ar_hrd != htons(ifp->family)) - continue; + bytes = if_readraw(ifp, state->fd, buf, sizeof(buf), &flags); + if (bytes == -1) { + logger(ifp->ctx, LOG_ERR, + "%s: arp if_readrawpacket: %m", ifp->name); + arp_close(ifp); + return; + } + /* We must have a full ARP header */ + if ((size_t)bytes < sizeof(ar)) + return; + memcpy(&ar, buf, sizeof(ar)); + /* Families must match */ + if (ar.ar_hrd != htons(ifp->family)) + return; #if 0 - /* These checks are enforced in the BPF filter. */ - /* Protocol must be IP. */ - if (ar.ar_pro != htons(ETHERTYPE_IP)) - continue; - /* Only these types are recognised */ - if (ar.ar_op != htons(ARPOP_REPLY) && - ar.ar_op != htons(ARPOP_REQUEST)) - continue; + /* These checks are enforced in the BPF filter. */ + /* Protocol must be IP. */ + if (ar.ar_pro != htons(ETHERTYPE_IP)) + continue; + /* Only these types are recognised */ + if (ar.ar_op != htons(ARPOP_REPLY) && + ar.ar_op != htons(ARPOP_REQUEST)) + continue; #endif - if (ar.ar_pln != sizeof(arm.sip.s_addr)) - continue; - - /* Get pointers to the hardware addreses */ - hw_s = arp_buffer + sizeof(ar); - hw_t = hw_s + ar.ar_hln + ar.ar_pln; - /* Ensure we got all the data */ - if ((hw_t + ar.ar_hln + ar.ar_pln) - arp_buffer > bytes) - continue; - /* Ignore messages from ourself */ - TAILQ_FOREACH(ifn, ifp->ctx->ifaces, next) { - if (ar.ar_hln == ifn->hwlen && - memcmp(hw_s, ifn->hwaddr, ifn->hwlen) == 0) - break; - } - if (ifn) { + if (ar.ar_pln != sizeof(arm.sip.s_addr)) + return; + + /* Get pointers to the hardware addreses */ + hw_s = buf + sizeof(ar); + hw_t = hw_s + ar.ar_hln + ar.ar_pln; + /* Ensure we got all the data */ + if ((hw_t + ar.ar_hln + ar.ar_pln) - buf > bytes) + return; + /* Ignore messages from ourself */ + TAILQ_FOREACH(ifn, ifp->ctx->ifaces, next) { + if (ar.ar_hln == ifn->hwlen && + memcmp(hw_s, ifn->hwaddr, ifn->hwlen) == 0) + break; + } + if (ifn) { #if 0 - logger(ifp->ctx, LOG_DEBUG, - "%s: ignoring ARP from self", ifp->name); + logger(ifp->ctx, LOG_DEBUG, + "%s: ignoring ARP from self", ifp->name); #endif - continue; - } - /* Copy out the HW and IP addresses */ - memcpy(&arm.sha, hw_s, ar.ar_hln); - memcpy(&arm.sip.s_addr, hw_s + ar.ar_hln, ar.ar_pln); - memcpy(&arm.tha, hw_t, ar.ar_hln); - memcpy(&arm.tip.s_addr, hw_t + ar.ar_hln, ar.ar_pln); - - /* Run the conflicts */ - TAILQ_FOREACH_SAFE(astate, &state->arp_states, next, astaten) { - if (astate->conflicted_cb) - astate->conflicted_cb(astate, &arm); - } + return; + } + /* Copy out the HW and IP addresses */ + memcpy(&arm.sha, hw_s, ar.ar_hln); + memcpy(&arm.sip.s_addr, hw_s + ar.ar_hln, ar.ar_pln); + memcpy(&arm.tha, hw_t, ar.ar_hln); + memcpy(&arm.tip.s_addr, hw_t + ar.ar_hln, ar.ar_pln); + + /* Run the conflicts */ + TAILQ_FOREACH_SAFE(astate, &state->arp_states, next, astaten) { + if (astate->conflicted_cb) + astate->conflicted_cb(astate, &arm); } } diff --git a/dhcp.c b/dhcp.c index 2cd1ac62..fc0a0b2f 100644 --- a/dhcp.c +++ b/dhcp.c @@ -3100,69 +3100,57 @@ dhcp_handlepacket(void *arg) /* Need this API due to BPF */ flags = 0; bootp = NULL; - while (!(flags & RAW_EOF)) { - bytes = (size_t)if_readraw(ifp, state->raw_fd, - buf, sizeof(buf), &flags); - if ((ssize_t)bytes == -1) { - logger(ifp->ctx, LOG_ERR, - "%s: dhcp if_readrawpacket: %m", ifp->name); - dhcp_close(ifp); - arp_close(ifp); - break; - } - if (valid_udp_packet(buf, bytes, - &from, flags & RAW_PARTIALCSUM) == -1) - { - logger(ifp->ctx, LOG_ERR, - "%s: invalid UDP packet from %s", - ifp->name, inet_ntoa(from)); - continue; - } - i = whitelisted_ip(ifp->options, from.s_addr); - if (i == 0) { - logger(ifp->ctx, LOG_WARNING, - "%s: non whitelisted DHCP packet from %s", - ifp->name, inet_ntoa(from)); - continue; - } else if (i != 1 && - blacklisted_ip(ifp->options, from.s_addr) == 1) - { - logger(ifp->ctx, LOG_WARNING, - "%s: blacklisted DHCP packet from %s", - ifp->name, inet_ntoa(from)); - continue; - } - if (ifp->flags & IFF_POINTOPOINT && - state->brd.s_addr != from.s_addr) - { - logger(ifp->ctx, LOG_WARNING, - "%s: server %s is not destination", - ifp->name, inet_ntoa(from)); - } - /* - * DHCP has a variable option area rather than a fixed - * vendor area. - * Because DHCP uses the BOOTP protocol it should - * still send BOOTP sized packets to be RFC compliant. - * However some servers send a truncated vendor area. - * dhcpcd can work fine without the vendor area being sent. - */ - bytes = get_udp_data(&bootp, buf); - if (bytes < offsetof(struct bootp, vend)) { - logger(ifp->ctx, LOG_ERR, - "%s: truncated packet (%zu) from %s", - ifp->name, bytes, inet_ntoa(from)); - continue; - } - /* But to make our IS_DHCP macro easy, ensure the vendor - * area has at least 4 octets. */ - while (bytes < offsetof(struct bootp, vend) + 4) - bootp[bytes++] = '\0'; - - dhcp_handledhcp(ifp, (struct bootp *)bootp, bytes, &from); - if (state->raw_fd == -1) - break; + bytes = (size_t)if_readraw(ifp, state->raw_fd,buf, sizeof(buf), &flags); + if ((ssize_t)bytes == -1) { + logger(ifp->ctx, LOG_ERR, + "%s: dhcp if_readrawpacket: %m", ifp->name); + dhcp_close(ifp); + arp_close(ifp); + return; } + if (valid_udp_packet(buf, bytes, &from, flags & RAW_PARTIALCSUM) == -1) + { + logger(ifp->ctx, LOG_ERR, "%s: invalid UDP packet from %s", + ifp->name, inet_ntoa(from)); + return; + } + i = whitelisted_ip(ifp->options, from.s_addr); + if (i == 0) { + logger(ifp->ctx, LOG_WARNING, + "%s: non whitelisted DHCP packet from %s", + ifp->name, inet_ntoa(from)); + return; + } else if (i != 1 && blacklisted_ip(ifp->options, from.s_addr) == 1) { + logger(ifp->ctx, LOG_WARNING, + "%s: blacklisted DHCP packet from %s", + ifp->name, inet_ntoa(from)); + return; + } + if (ifp->flags & IFF_POINTOPOINT && state->brd.s_addr != from.s_addr) { + logger(ifp->ctx, LOG_WARNING, + "%s: server %s is not destination", + ifp->name, inet_ntoa(from)); + } + /* + * DHCP has a variable option area rather than a fixed vendor area. + * Because DHCP uses the BOOTP protocol it should still send BOOTP + * sized packets to be RFC compliant. + * However some servers send a truncated vendor area. + * dhcpcd can work fine without the vendor area being sent. + */ + bytes = get_udp_data(&bootp, buf); + if (bytes < offsetof(struct bootp, vend)) { + logger(ifp->ctx, LOG_ERR, + "%s: truncated packet (%zu) from %s", + ifp->name, bytes, inet_ntoa(from)); + return; + } + /* But to make our IS_DHCP macro easy, ensure the vendor + * area has at least 4 octets. */ + while (bytes < offsetof(struct bootp, vend) + 4) + bootp[bytes++] = '\0'; + + dhcp_handledhcp(ifp, (struct bootp *)bootp, bytes, &from); } static void diff --git a/if-bsd.c b/if-bsd.c index 86215c78..0f35ce2a 100644 --- a/if-bsd.c +++ b/if-bsd.c @@ -522,10 +522,8 @@ if_readraw(struct interface *ifp, int fd, void *data, size_t len, int *flags) next: state->buffer_pos += BPF_WORDALIGN(packet.bh_hdrlen + packet.bh_caplen); - if (state->buffer_pos >= state->buffer_len) { + if (state->buffer_pos >= state->buffer_len) state->buffer_len = state->buffer_pos = 0; - *flags |= RAW_EOF; - } if (bytes != -1) return bytes; } diff --git a/if-linux.c b/if-linux.c index d3d1ec0c..404b2c96 100644 --- a/if-linux.c +++ b/if-linux.c @@ -1334,7 +1334,7 @@ if_readraw(__unused struct interface *ifp, int fd, bytes = recvmsg(fd, &msg, 0); if (bytes == -1) return -1; - *flags = RAW_EOF; /* We only ever read one packet */ + *flags = 0; if (bytes) { #ifdef PACKET_AUXDATA for (cmsg = CMSG_FIRSTHDR(&msg); diff --git a/if.h b/if.h index 33f05cab..b4a9f983 100644 --- a/if.h +++ b/if.h @@ -80,8 +80,7 @@ ((addr & IN_CLASSB_NET) == 0xc0a80000)) #endif -#define RAW_EOF 1 << 0 -#define RAW_PARTIALCSUM 2 << 0 +#define RAW_PARTIALCSUM 1 << 0 #ifdef __sun /* platform does not supply AF_LINK with getifaddrs. */ -- 2.47.3