1 /* 2 * dhcpcd - DHCP client daemon 3 * Copyright (c) 2006-2008 Roy Marples <roy@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