{
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;
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
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;
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;
/* 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;
}
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;
};
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);
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;
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;
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;
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;
}
#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);
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;
{ -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);
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 */
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
int
open_socket(struct interface *iface, int protocol)
{
- int s;
+ int flags, s;
union sockunion {
struct sockaddr sa;
struct sockaddr_in sin;
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:
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;
/* 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;
}