From: Roy Marples Date: Thu, 15 May 2008 10:10:39 +0000 (+0000) Subject: Improve get_packet API and memcpy to structure instead of using union pointers. X-Git-Tag: v4.0.2~403 X-Git-Url: http://git.ipfire.org/cgi-bin/gitweb.cgi?a=commitdiff_plain;h=b1009e62b221f6ca2d84818766547b92cee64564;p=thirdparty%2Fdhcpcd.git Improve get_packet API and memcpy to structure instead of using union pointers. --- diff --git a/bpf.c b/bpf.c index 470ad22c..7d429533 100644 --- a/bpf.c +++ b/bpf.c @@ -53,14 +53,14 @@ open_socket(struct interface *iface, int protocol) { int fd = -1; struct ifreq ifr; - int buf = 0; + int buf_len = 0; struct bpf_version pv; struct bpf_program pf; #ifdef BIOCIMMEDIATE int flags; #endif #ifdef _PATH_BPF - fd = open(_PATH_BPF, O_RDWR); + fd = open(_PATH_BPF, O_RDWR | O_NONBLOCK); #else char *device; int n = 0; @@ -68,7 +68,7 @@ open_socket(struct interface *iface, int protocol) device = xmalloc(sizeof(char) * PATH_MAX); do { snprintf(device, PATH_MAX, "/dev/bpf%d", n++); - fd = open(device, O_RDWR); + fd = open(device, O_RDWR | O_NONBLOCK); } while (fd == -1 && errno == EBUSY); free(device); #endif @@ -90,9 +90,11 @@ open_socket(struct interface *iface, int protocol) goto eexit; /* Get the required BPF buffer length from the kernel. */ - if (ioctl(fd, BIOCGBLEN, &buf) == -1) + if (ioctl(fd, BIOCGBLEN, &buf_len) == -1) goto eexit; - iface->buffer_length = buf; + iface->buffer_size = buf_len; + iface->buffer = xmalloc(buf_len); + iface->buffer_len = iface->buffer_pos = 0; #ifdef BIOCIMMEDIATE flags = 1; @@ -120,13 +122,15 @@ open_socket(struct interface *iface, int protocol) return fd; eexit: + free(iface->buffer); + iface->buffer = NULL; close(fd); return -1; } ssize_t send_raw_packet(const struct interface *iface, int type, - const unsigned char *data, ssize_t len) + const void *data, ssize_t len) { struct iovec iov[2]; struct ether_header hw; @@ -145,66 +149,49 @@ send_raw_packet(const struct interface *iface, int type, /* BPF requires that we read the entire buffer. * So we pass the buffer in the API so we can loop on >1 dhcp packet. */ ssize_t -get_packet(const struct interface *iface, unsigned char *data, - unsigned char *buffer, ssize_t *buffer_len, ssize_t *buffer_pos) +get_packet(struct interface *iface, void *data, ssize_t len) { - union - { - unsigned char *buffer; - struct bpf_hdr *packet; - } bpf; - union - { - unsigned char *buffer; - struct ether_header *hw; - } hdr; - struct timespec ts; - ssize_t len; - unsigned char *payload; - const uint8_t *d; - - bpf.buffer = buffer; - - if (*buffer_pos < 1) { - memset(bpf.buffer, 0, iface->buffer_length); - *buffer_len = read(iface->fd, bpf.buffer, iface->buffer_length); - *buffer_pos = 0; - if (*buffer_len < 1) { - ts.tv_sec = 3; - ts.tv_nsec = 0; - nanosleep(&ts, NULL); - return -1; + struct bpf_hdr packet; + struct ether_header hw; + ssize_t cur_len; + const unsigned char *payload, *d; + + for (;;) { + if (iface->buffer_len == 0) { + cur_len = read(iface->fd, iface->buffer, + iface->buffer_size); + if (cur_len == -1) + return errno == EAGAIN ? 0 : -1; + else if ((size_t)cur_len < sizeof(packet)) + return -1; + iface->buffer_len = cur_len; } - } else - bpf.buffer += *buffer_pos; - - for (; bpf.buffer - buffer < *buffer_len; - bpf.buffer += BPF_WORDALIGN(bpf.packet->bh_hdrlen + - bpf.packet->bh_caplen), - *buffer_pos = bpf.buffer - buffer) - { - /* Ensure we have the whole packet */ - if (bpf.packet->bh_caplen != bpf.packet->bh_datalen) - continue; - - hdr.buffer = bpf.buffer + bpf.packet->bh_hdrlen; - payload = hdr.buffer + sizeof(*hdr.hw); - - /* If it's an ARP reply, then just send it back */ - if (hdr.hw->ether_type == htons (ETHERTYPE_ARP)) { - len = bpf.packet->bh_caplen - sizeof(*hdr.hw); + + cur_len = -1; + + memcpy(&packet, iface->buffer + iface->buffer_pos, + sizeof(packet)); + if (packet.bh_caplen != packet.bh_datalen) + goto next; /* Incomplete packet, drop. */ + if (iface->buffer_pos + packet.bh_caplen + packet.bh_hdrlen > + iface->buffer_len) + goto next; /* Packet beyond buffer, drop. */ + memcpy(&hw, iface->buffer + packet.bh_hdrlen, sizeof(hw)); + payload = iface->buffer + packet.bh_hdrlen + sizeof(hw); + if (hw.ether_type == htons(ETHERTYPE_ARP)) { + cur_len = packet.bh_caplen - sizeof(hw); memcpy(data, payload, len); - return len; - } else { - if (valid_udp_packet(payload) >= 0) { - len = get_udp_data(&d, payload); - memcpy(data, d, len); - return len; - } - } + } else if (valid_udp_packet(payload) >= 0) { + cur_len = get_udp_data(&d, payload); + memcpy(data, d, cur_len); + } else + cur_len = -1; +next: + iface->buffer_pos += BPF_WORDALIGN(packet.bh_hdrlen + + packet.bh_caplen); + if (iface->buffer_pos >= iface->buffer_len) + iface->buffer_len = iface->buffer_pos = 0; + if (cur_len != -1) + return cur_len; } - - /* No valid packets left, so return */ - *buffer_pos = 0; - return -1; } diff --git a/client.c b/client.c index c3681f49..9639e85c 100644 --- a/client.c +++ b/client.c @@ -105,9 +105,6 @@ struct if_state { time_t nakoff; uint32_t xid; int socket; - unsigned char *buffer; - ssize_t buffer_len; - ssize_t buffer_pos; int *pidfd; }; @@ -1186,24 +1183,19 @@ handle_packet(struct if_state *state, const struct options *options) uint8_t *p; ssize_t bytes; - /* Allocate our buffer space for BPF. - * We cannot do this until we have opened our socket as we don't - * know how much of a buffer we need until then. */ - if (!state->buffer) - state->buffer = xmalloc(iface->buffer_length); - state->buffer_len = iface->buffer_length; - state->buffer_pos = 0; - /* We loop through until our buffer is empty. * The benefit is that if we get >1 DHCP packet in our buffer and * the first one fails for any reason, we can use the next. */ dhcp = xmalloc(sizeof(*dhcp)); - do { - bytes = get_packet(iface, (uint8_t *)dhcp, state->buffer, - &state->buffer_len, &state->buffer_pos); + do { + bytes = get_packet(iface, dhcp, sizeof(*dhcp)); if (bytes == -1) break; + if (bytes == 0) { + free(dhcp); + return 0; + } if (dhcp->cookie != htonl(MAGIC_COOKIE)) { logger(LOG_DEBUG, "bogus cookie, ignoring"); continue; @@ -1226,7 +1218,8 @@ handle_packet(struct if_state *state, const struct options *options) } if (handle_dhcp(state, &dhcp, options) == 0) return 0; - } while (state->buffer_pos != 0); + } while (iface->buffer_pos != 0); + free(dhcp); return -1; } @@ -1302,7 +1295,6 @@ eexit: retval = 0; if (state->options & DHCPCD_DAEMONISED) unlink(options->pidfile); - free(state->buffer); free(state->dhcp); free(state->old_dhcp); free(state); diff --git a/net.c b/net.c index 9de0bdb9..b79af243 100644 --- a/net.c +++ b/net.c @@ -221,7 +221,7 @@ do_interface(const char *ifname, len *= 2; } - for (p = ifc.ifc_buf; p < ifc.ifc_buf + ifc.ifc_len;) { + for (p = (char *)ifc.ifc_buf; p < (char *)ifc.ifc_buf + ifc.ifc_len;) { /* Cast the ifc buffer to an ifreq cleanly */ ifreqs.buffer = p; ifr = ifreqs.ifr; @@ -468,28 +468,20 @@ struct udp_dhcp_packet }; static uint16_t -checksum(uint8_t *addr, uint16_t len) +checksum(const void *data, uint16_t len) { + const uint8_t *addr = data; uint32_t sum = 0; - union - { - uint8_t *addr; - uint16_t *i; - } p; - uint16_t nleft = len; - uint8_t a = 0; - - p.addr = addr; - while (nleft > 1) { - sum += *p.i++; - nleft -= 2; - } - if (nleft == 1) { - memcpy(&a, p.i, 1); - sum += ntohs(a) << 8; + while (len > 1) { + sum += addr[0] + addr[1] * 256; + addr += 2; + len -= 2; } + if (len == 1) + sum += *addr; + sum = (sum >> 16) + (sum & 0xffff); sum += (sum >> 16); @@ -530,7 +522,7 @@ make_udp_packet(uint8_t **packet, const uint8_t *data, size_t length, udp->uh_dport = htons(DHCP_SERVER_PORT); udp->uh_ulen = htons(sizeof(*udp) + length); ip->ip_len = udp->uh_ulen; - udp->uh_sum = checksum((uint8_t *)udpp, sizeof(*udpp)); + udp->uh_sum = checksum(udpp, sizeof(*udpp)); ip->ip_v = IPVERSION; ip->ip_hl = 5; @@ -541,7 +533,7 @@ make_udp_packet(uint8_t **packet, const uint8_t *data, size_t length, ip->ip_off = htons(IP_DF); /* Don't fragment */ ip->ip_ttl = IPDEFTTL; - ip->ip_sum = checksum((uint8_t *)ip, sizeof(*ip)); + ip->ip_sum = checksum(ip, sizeof(*ip)); *packet = (uint8_t *)udpp; return sizeof(*ip) + sizeof(*udp) + length; @@ -550,27 +542,17 @@ make_udp_packet(uint8_t **packet, const uint8_t *data, size_t length, ssize_t get_udp_data(const uint8_t **data, const uint8_t *udp) { - union - { - const uint8_t *data; - const struct udp_dhcp_packet *packet; - } d; - - d.data = udp; - *data = (const uint8_t *)&d.packet->dhcp; - return ntohs(d.packet->ip.ip_len) - - sizeof(d.packet->ip) - - sizeof(d.packet->udp); + struct udp_dhcp_packet packet; + + memcpy(&packet, udp, sizeof(packet)); + *data = udp + offsetof(struct udp_dhcp_packet, dhcp); + return ntohs(packet.ip.ip_len) - sizeof(packet.ip) - sizeof(packet.udp); } int -valid_udp_packet(uint8_t *data) +valid_udp_packet(const uint8_t *data) { - union - { - uint8_t *data; - struct udp_dhcp_packet *packet; - } d; + struct udp_dhcp_packet packet; uint16_t bytes; uint16_t ipsum; uint16_t iplen; @@ -579,39 +561,32 @@ valid_udp_packet(uint8_t *data) struct in_addr dest; int retval = 0; - d.data = data; - bytes = ntohs(d.packet->ip.ip_len); - ipsum = d.packet->ip.ip_sum; - iplen = d.packet->ip.ip_len; - udpsum = d.packet->udp.uh_sum; + memcpy(&packet, data, sizeof(packet)); + bytes = ntohs(packet.ip.ip_len); + ipsum = packet.ip.ip_sum; + iplen = packet.ip.ip_len; + udpsum = packet.udp.uh_sum; - d.data = data; - d.packet->ip.ip_sum = 0; - if (ipsum != checksum((uint8_t *)&d.packet->ip, sizeof(d.packet->ip))) { + if (0 != checksum(&packet.ip, sizeof(packet.ip))) { errno = EINVAL; - retval = -1; - goto eexit; + return -1; } - memcpy(&source, &d.packet->ip.ip_src, sizeof(d.packet->ip.ip_src)); - memcpy(&dest, &d.packet->ip.ip_dst, sizeof(d.packet->ip.ip_dst)); - memset(&d.packet->ip, 0, sizeof(d.packet->ip)); - d.packet->udp.uh_sum = 0; - - d.packet->ip.ip_p = IPPROTO_UDP; - memcpy(&d.packet->ip.ip_src, &source, sizeof(d.packet->ip.ip_src)); - memcpy(&d.packet->ip.ip_dst, &dest, sizeof(d.packet->ip.ip_dst)); - d.packet->ip.ip_len = d.packet->udp.uh_ulen; - if (udpsum && udpsum != checksum(d.data, bytes)) { + packet.ip.ip_sum = 0; + memcpy(&source, &packet.ip.ip_src, sizeof(packet.ip.ip_src)); + memcpy(&dest, &packet.ip.ip_dst, sizeof(packet.ip.ip_dst)); + memset(&packet.ip, 0, sizeof(packet.ip)); + packet.udp.uh_sum = 0; + + packet.ip.ip_p = IPPROTO_UDP; + memcpy(&packet.ip.ip_src, &source, sizeof(packet.ip.ip_src)); + memcpy(&packet.ip.ip_dst, &dest, sizeof(packet.ip.ip_dst)); + packet.ip.ip_len = packet.udp.uh_ulen; + if (udpsum && udpsum != checksum(&packet, bytes)) { errno = EINVAL; retval = -1; } -eexit: - d.packet->ip.ip_sum = ipsum; - d.packet->ip.ip_len = iplen; - d.packet->udp.uh_sum = udpsum; - return retval; } @@ -622,51 +597,38 @@ eexit: #define NCLAIMS 2 #define CLAIM_INTERVAL 200 -/* Linux does not seem to define these handy macros */ -#ifndef ar_sha -#define ar_sha(ap) (((caddr_t)((ap) + 1)) + 0) -#define ar_spa(ap) (((caddr_t)((ap) + 1)) + (ap)->ar_hln) -#define ar_tha(ap) (((caddr_t)((ap) + 1)) + (ap)->ar_hln + (ap)->ar_pln) -#define ar_tpa(ap) (((caddr_t)((ap) + 1)) + 2 * (ap)->ar_hln + (ap)->ar_pln) -#endif - -#ifndef arphdr_len -#define arphdr_len2(ar_hln, ar_pln) (sizeof(struct arphdr) + \ - 2 * (ar_hln) + 2 * (ar_pln)) -#define arphdr_len(ap) (arphdr_len2((ap)->ar_hln, (ap)->ar_pln)) -#endif - static int send_arp(const struct interface *iface, int op, struct in_addr sip, const unsigned char *taddr, struct in_addr tip) { struct arphdr *arp; - size_t arpsize = arphdr_len2(iface->hwlen, sizeof(sip)); - caddr_t tha; + size_t arpsize; + unsigned char *p; int retval; + arpsize = sizeof(*arp) + 2 * iface->hwlen + 2 *sizeof(sip); + arp = xzalloc(arpsize); arp->ar_hrd = htons(iface->family); arp->ar_pro = htons(ETHERTYPE_IP); arp->ar_hln = iface->hwlen; arp->ar_pln = sizeof(sip); arp->ar_op = htons(op); - memcpy(ar_sha(arp), iface->hwaddr, (size_t)arp->ar_hln); - memcpy(ar_spa(arp), &sip, (size_t)arp->ar_pln); - if (taddr) { - /* NetBSD can return NULL from ar_tha, which is probably wrong - * but we still need to deal with it */ - if (! (tha = ar_tha(arp))) { - free(arp); - errno = EINVAL; - return -1; - } - memcpy(tha, taddr, (size_t)arp->ar_hln); - } - memcpy(ar_tpa(arp), &tip, (size_t)arp->ar_pln); + p = (unsigned char *)arp; + p += sizeof(*arp); + memcpy(p, iface->hwaddr, iface->hwlen); + p += iface->hwlen; + memcpy(p, &sip, sizeof(sip)); + p += sizeof(sip); + + if (taddr != NULL) + memcpy(p, taddr, iface->hwlen); + else + memset(p, 0, iface->hwlen); + p += iface->hwlen; + memcpy(p, &tip, sizeof(tip)); - retval = send_raw_packet(iface, ETHERTYPE_ARP, - (uint8_t *)arp, arphdr_len(arp)); + retval = send_raw_packet(iface, ETHERTYPE_ARP, arp, arpsize); if (retval == -1) logger(LOG_ERR,"send_packet: %s", strerror(errno)); free(arp); @@ -676,9 +638,11 @@ send_arp(const struct interface *iface, int op, struct in_addr sip, int arp_claim(struct interface *iface, struct in_addr address) { - struct arphdr *reply = NULL; + struct arphdr reply; + uint8_t arp_reply[sizeof(reply) + 2 * 4 /* IPv4 */ + 2 * 8 /* EUI64 */]; + struct in_addr reply_ipv4; + struct ether_addr reply_mac; long timeout = 0; - unsigned char *buffer; int retval = -1; int nprobes = 0; int nclaims = 0; @@ -687,20 +651,10 @@ arp_claim(struct interface *iface, struct in_addr address) { -1, POLLIN, 0 }, { -1, POLLIN, 0 } }; - ssize_t bufpos = 0; - ssize_t buflen = iface->buffer_length; int bytes; int s = 0; struct timeval stopat; struct timeval now; - union { - unsigned char *c; - struct in_addr *a; - } rp; - union { - unsigned char *c; - struct ether_addr *a; - } rh; if (!iface->arpable) { logger(LOG_DEBUG, "interface `%s' is not ARPable", iface->name); @@ -721,12 +675,8 @@ arp_claim(struct interface *iface, struct in_addr address) fds[0].fd = signal_fd(); fds[1].fd = iface->fd; memset(&null_address, 0, sizeof(null_address)); - buffer = xmalloc(iface->buffer_length); - reply = xmalloc(iface->buffer_length); for (;;) { - bufpos = 0; - buflen = iface->buffer_length; s = 0; /* Only poll if we have a timeout */ @@ -797,53 +747,50 @@ arp_claim(struct interface *iface, struct in_addr address) if (!(fds[1].revents & POLLIN)) continue; - memset(buffer, 0, buflen); - do { - memset(reply, 0, iface->buffer_length); - if ((bytes = get_packet(iface, (unsigned char *) reply, - buffer, - &buflen, &bufpos)) == -1) + for (;;) { + memset(arp_reply, 0, sizeof(arp_reply)); + bytes = get_packet(iface, &arp_reply, sizeof(arp_reply)); + if (bytes <= 0) break; + memcpy(&reply, arp_reply, sizeof(reply)); /* Only these types are recognised */ - if (reply->ar_op != htons(ARPOP_REPLY)) + if (reply.ar_op != htons(ARPOP_REPLY)) continue; /* Protocol must be IP. */ - if (reply->ar_pro != htons(ETHERTYPE_IP)) + if (reply.ar_pro != htons(ETHERTYPE_IP)) continue; - if (reply->ar_pln != sizeof(address)) + if (reply.ar_pln != sizeof(reply_ipv4)) continue; - if ((unsigned)bytes < sizeof(reply) + - 2 * (4 +reply->ar_hln)) + if ((size_t)bytes < sizeof(reply) + 2 * (4 + reply.ar_hln) || + reply.ar_hln > 8) continue; - rp.c = (unsigned char *)ar_spa(reply); - rh.c = (unsigned char *)ar_sha(reply); + memcpy(&reply_mac, arp_reply + sizeof(reply), reply.ar_hln); + memcpy(&reply_ipv4, arp_reply + sizeof(reply) + reply.ar_hln, reply.ar_hln); /* Ensure the ARP reply is for the our address */ - if (rp.a->s_addr != address.s_addr) + if (reply_ipv4.s_addr != address.s_addr) continue; /* Some systems send a reply back from our hwaddress, * which is wierd */ - if (reply->ar_hln == iface->hwlen && - memcmp(rh.c, iface->hwaddr, iface->hwlen) == 0) + if (reply.ar_hln == iface->hwlen && + memcmp(&reply_mac, iface->hwaddr, iface->hwlen) == 0) continue; logger(LOG_ERR, "ARPOP_REPLY received from %s (%s)", - inet_ntoa(*rp.a), - hwaddr_ntoa(rh.c, (size_t)reply->ar_hln)); + inet_ntoa(reply_ipv4), + hwaddr_ntoa((unsigned char *)&reply_mac, (size_t)reply.ar_hln)); retval = -1; goto eexit; - } while (bufpos != 0); + } } eexit: close(iface->fd); iface->fd = -1; - free(buffer); - free(reply); return retval; } #endif diff --git a/net.h b/net.h index ef968e18..8e5d2b37 100644 --- a/net.h +++ b/net.h @@ -105,7 +105,8 @@ struct interface int fd; int udp_fd; - size_t buffer_length; + size_t buffer_size, buffer_pos, buffer_len; + unsigned char *buffer; #ifdef __linux__ int socket_protocol; @@ -160,7 +161,7 @@ int open_udp_socket(struct interface *); ssize_t make_udp_packet(uint8_t **, const uint8_t *, size_t, struct in_addr, struct in_addr); ssize_t get_udp_data(const uint8_t **, const uint8_t *); -int valid_udp_packet(uint8_t *); +int valid_udp_packet(const uint8_t *); #ifdef __linux__ void setup_packet_filters(void); @@ -169,9 +170,8 @@ int open_socket(struct interface *, int); ssize_t send_packet(const struct interface *, struct in_addr, const uint8_t *, ssize_t); ssize_t send_raw_packet(const struct interface *, int, - const uint8_t *, ssize_t); -ssize_t get_packet(const struct interface *, uint8_t *, - uint8_t *, ssize_t *, ssize_t *); + const void *, ssize_t); +ssize_t get_packet(struct interface *, void *, ssize_t); #ifdef ENABLE_ARP int arp_claim(struct interface *, struct in_addr); diff --git a/socket.c b/socket.c index 6ae104f2..136a609b 100644 --- a/socket.c +++ b/socket.c @@ -91,7 +91,7 @@ setup_packet_filters(void) int open_socket(struct interface *iface, int protocol) { - int s; + int flags, s; union sockunion { struct sockaddr sa; struct sockaddr_in sin; @@ -126,13 +126,13 @@ open_socket(struct interface *iface, int protocol) if (bind(s, &su.sa, sizeof(su)) == -1) goto eexit; - + if (close_on_exec(s) == -1) + goto eexit; if (iface->fd > -1) close(iface->fd); iface->fd = s; iface->socket_protocol = protocol; - iface->buffer_length = BUFFER_LENGTH; - + iface->buffer_length = 0; return s; eexit: @@ -142,7 +142,7 @@ eexit: ssize_t send_raw_packet(const struct interface *iface, int type, - const uint8_t *data, ssize_t len) + const void *data, ssize_t len) { union sockunion { struct sockaddr sa; @@ -171,37 +171,26 @@ send_raw_packet(const struct interface *iface, int type, /* Linux has no need for the buffer as we can read as much as we want. * We only have the buffer listed to keep the same API. */ ssize_t -get_packet(const struct interface *iface, uint8_t *data, - uint8_t *buffer, ssize_t *buffer_len, ssize_t *buffer_pos) +get_packet(struct interface *iface, void *data, ssize_t len) { ssize_t bytes; struct timespec ts; const uint8_t *p; - /* We don't use the given buffer, but we need to rewind the position */ - *buffer_pos = 0; + memset(data, 0, len); + bytes = read(iface->fd, data, len); - memset(buffer, 0, iface->buffer_length); - bytes = read(iface->fd, buffer, iface->buffer_length); + if (bytes == -1) + return errno == EGAIN ? 0 : -1; - if (bytes == -1) { - ts.tv_sec = 3; - ts.tv_nsec = 0; - nanosleep(&ts, NULL); - return -1; - } - - *buffer_len = bytes; /* If it's an ARP reply, then just send it back */ - if (iface->socket_protocol == ETHERTYPE_ARP) { - memcpy(data, buffer, bytes); + if (iface->socket_protocol == ETHERTYPE_ARP) return bytes; - } - if (valid_udp_packet(buffer) != 0) + if (valid_udp_packet(data) != 0) return -1; bytes = get_udp_data(&p, buffer); - memcpy(data, p, bytes); + memmove(data, p, bytes); return bytes; }