Home | History | Annotate | Line # | Download | only in rump_dhcpclient
      1 /*
      2  * dhcpcd - DHCP client daemon
      3  * Copyright (c) 2006-2008 Roy Marples <roy (at) marples.name>
      4  *
      5  * Redistribution and use in source and binary forms, with or without
      6  * modification, are permitted provided that the following conditions
      7  * are met:
      8  * 1. Redistributions of source code must retain the above copyright
      9  *    notice, this list of conditions and the following disclaimer.
     10  * 2. Redistributions in binary form must reproduce the above copyright
     11  *    notice, this list of conditions and the following disclaimer in the
     12  *    documentation and/or other materials provided with the distribution.
     13  *
     14  * THIS SOFTWARE IS PROVIDED BY THE AUTHOR AND CONTRIBUTORS ``AS IS'' AND
     15  * ANY EXPRESS OR IMPLIED WARRANTIES, INCLUDING, BUT NOT LIMITED TO, THE
     16  * IMPLIED WARRANTIES OF MERCHANTABILITY AND FITNESS FOR A PARTICULAR PURPOSE
     17  * ARE DISCLAIMED.  IN NO EVENT SHALL THE AUTHOR OR CONTRIBUTORS BE LIABLE
     18  * FOR ANY DIRECT, INDIRECT, INCIDENTAL, SPECIAL, EXEMPLARY, OR CONSEQUENTIAL
     19  * DAMAGES (INCLUDING, BUT NOT LIMITED TO, PROCUREMENT OF SUBSTITUTE GOODS
     20  * OR SERVICES; LOSS OF USE, DATA, OR PROFITS; OR BUSINESS INTERRUPTION)
     21  * HOWEVER CAUSED AND ON ANY THEORY OF LIABILITY, WHETHER IN CONTRACT, STRICT
     22  * LIABILITY, OR TORT (INCLUDING NEGLIGENCE OR OTHERWISE) ARISING IN ANY WAY
     23  * OUT OF THE USE OF THIS SOFTWARE, EVEN IF ADVISED OF THE POSSIBILITY OF
     24  * SUCH DAMAGE.
     25  */
     26 
     27 #include <sys/types.h>
     28 #include <sys/ioctl.h>
     29 #include <sys/socket.h>
     30 #include <sys/uio.h>
     31 
     32 #include <net/bpf.h>
     33 #include <net/if.h>
     34 #include <arpa/inet.h>
     35 
     36 #include <errno.h>
     37 #include <fcntl.h>
     38 #include <paths.h>
     39 #include <stdio.h>
     40 #include <stdlib.h>
     41 #include <string.h>
     42 #include <unistd.h>
     43 
     44 #include "common.h"
     45 #include "dhcp.h"
     46 #include "net.h"
     47 #include "bpf-filter.h"
     48 
     49 #include <rump/rump.h>
     50 #include <rump/rump_syscalls.h>
     51 
     52 int
     53 open_socket(struct interface *iface, int protocol)
     54 {
     55 	int fd = -1;
     56 	int *fdp = NULL;
     57 	struct ifreq ifr;
     58 	int buf_len = 0;
     59 	struct bpf_version pv;
     60 	struct bpf_program pf;
     61 #ifdef BIOCIMMEDIATE
     62 	int flags;
     63 #endif
     64 #ifdef _PATH_BPF
     65 	fd = rump_sys_open(_PATH_BPF, O_RDWR | O_NONBLOCK);
     66 #else
     67 	char *device;
     68 	int n = 0;
     69 
     70 	device = xmalloc(sizeof(char) * PATH_MAX);
     71 	do {
     72 		snprintf(device, PATH_MAX, "/dev/bpf%d", n++);
     73 		fd = rump_sys_open(device, O_RDWR | O_NONBLOCK);
     74 	} while (fd == -1 && errno == EBUSY);
     75 	free(device);
     76 #endif
     77 
     78 	if (fd == -1)
     79 		return -1;
     80 
     81 	if (rump_sys_ioctl(fd, BIOCVERSION, &pv) == -1)
     82 		goto eexit;
     83 	if (pv.bv_major != BPF_MAJOR_VERSION ||
     84 	    pv.bv_minor < BPF_MINOR_VERSION) {
     85 		fprintf(stderr, "BPF version mismatch - recompile");
     86 		goto eexit;
     87 	}
     88 
     89 	memset(&ifr, 0, sizeof(ifr));
     90 	strlcpy(ifr.ifr_name, iface->name, sizeof(ifr.ifr_name));
     91 	if (rump_sys_ioctl(fd, BIOCSETIF, &ifr) == -1)
     92 		goto eexit;
     93 
     94 	/* Get the required BPF buffer length from the kernel. */
     95 	if (rump_sys_ioctl(fd, BIOCGBLEN, &buf_len) == -1)
     96 		goto eexit;
     97 	if (iface->buffer_size != (size_t)buf_len) {
     98 		free(iface->buffer);
     99 		iface->buffer_size = buf_len;
    100 		iface->buffer = xmalloc(buf_len);
    101 		iface->buffer_len = iface->buffer_pos = 0;
    102 	}
    103 
    104 #ifdef BIOCIMMEDIATE
    105 	flags = 1;
    106 	if (rump_sys_ioctl(fd, BIOCIMMEDIATE, &flags) == -1)
    107 		goto eexit;
    108 #endif
    109 
    110 	/* Install the DHCP filter */
    111 	if (protocol == ETHERTYPE_ARP) {
    112 		pf.bf_insns = UNCONST(arp_bpf_filter);
    113 		pf.bf_len = arp_bpf_filter_len;
    114 		fdp = &iface->arp_fd;
    115 	} else {
    116 		pf.bf_insns = UNCONST(dhcp_bpf_filter);
    117 		pf.bf_len = dhcp_bpf_filter_len;
    118 		fdp = &iface->raw_fd;
    119 	}
    120 	if (rump_sys_ioctl(fd, BIOCSETF, &pf) == -1)
    121 		goto eexit;
    122 	if (fdp) {
    123 		if (*fdp != -1)
    124 			rump_sys_close(*fdp);
    125 		*fdp = fd;
    126 	}
    127 	return fd;
    128 
    129 eexit:
    130 	free(iface->buffer);
    131 	iface->buffer = NULL;
    132 	rump_sys_close(fd);
    133 	return -1;
    134 }
    135 
    136 ssize_t
    137 send_raw_packet(const struct interface *iface, int protocol,
    138     const void *data, ssize_t len)
    139 {
    140 	struct iovec iov[2];
    141 	struct ether_header hw;
    142 	int fd;
    143 
    144 	memset(&hw, 0, ETHER_HDR_LEN);
    145 	memset(&hw.ether_dhost, 0xff, ETHER_ADDR_LEN);
    146 	hw.ether_type = htons(protocol);
    147 	iov[0].iov_base = &hw;
    148 	iov[0].iov_len = ETHER_HDR_LEN;
    149 	iov[1].iov_base = UNCONST(data);
    150 	iov[1].iov_len = len;
    151 	if (protocol == ETHERTYPE_ARP)
    152 		fd = iface->arp_fd;
    153 	else
    154 		fd = iface->raw_fd;
    155 	return rump_sys_writev(fd, iov, 2);
    156 }
    157 
    158 /* BPF requires that we read the entire buffer.
    159  * So we pass the buffer in the API so we can loop on >1 packet. */
    160 ssize_t
    161 get_raw_packet(struct interface *iface, int protocol,
    162     void *data, ssize_t len)
    163 {
    164 	int fd = -1;
    165 	struct bpf_hdr packet;
    166 	ssize_t bytes;
    167 	const unsigned char *payload;
    168 
    169 	if (protocol == ETHERTYPE_ARP)
    170 		fd = iface->arp_fd;
    171 	else
    172 		fd = iface->raw_fd;
    173 
    174 	for (;;) {
    175 		if (iface->buffer_len == 0) {
    176 			bytes = rump_sys_read(fd, iface->buffer, iface->buffer_size);
    177 			if (bytes == -1)
    178 				return errno == EAGAIN ? 0 : -1;
    179 			else if ((size_t)bytes < sizeof(packet))
    180 				return -1;
    181 			iface->buffer_len = bytes;
    182 			iface->buffer_pos = 0;
    183 		}
    184 		bytes = -1;
    185 		memcpy(&packet, iface->buffer + iface->buffer_pos,
    186 		    sizeof(packet));
    187 		if (packet.bh_caplen != packet.bh_datalen)
    188 			goto next; /* Incomplete packet, drop. */
    189 		if (iface->buffer_pos + packet.bh_caplen + packet.bh_hdrlen >
    190 		    iface->buffer_len)
    191 			goto next; /* Packet beyond buffer, drop. */
    192 		payload = iface->buffer + packet.bh_hdrlen + ETHER_HDR_LEN;
    193 		bytes = packet.bh_caplen - ETHER_HDR_LEN;
    194 		if (bytes > len)
    195 			bytes = len;
    196 		memcpy(data, payload, bytes);
    197 next:
    198 		iface->buffer_pos += BPF_WORDALIGN(packet.bh_hdrlen +
    199 		    packet.bh_caplen);
    200 		if (iface->buffer_pos >= iface->buffer_len)
    201 			iface->buffer_len = iface->buffer_pos = 0;
    202 		if (bytes != -1)
    203 			return bytes;
    204 	}
    205 }
    206