1 /*
2 * dhcpcd - DHCP client daemon
3 * Copyright (c) 2013 Antti Kantee <pooka@iki.fi>
4 * Copyright (c) 2006-2008 Roy Marples <roy@marples.name>
5 *
6 * Redistribution and use in source and binary forms, with or without
7 * modification, are permitted provided that the following conditions
8 * are met:
9 * 1. Redistributions of source code must retain the above copyright
10 * notice, this list of conditions and the following disclaimer.
11 * 2. Redistributions in binary form must reproduce the above copyright
12 * notice, this list of conditions and the following disclaimer in the
13 * documentation and/or other materials provided with the distribution.
14 *
15 * THIS SOFTWARE IS PROVIDED BY THE AUTHOR AND CONTRIBUTORS ``AS IS'' AND
16 * ANY EXPRESS OR IMPLIED WARRANTIES, INCLUDING, BUT NOT LIMITED TO, THE
17 * IMPLIED WARRANTIES OF MERCHANTABILITY AND FITNESS FOR A PARTICULAR PURPOSE
18 * ARE DISCLAIMED. IN NO EVENT SHALL THE AUTHOR OR CONTRIBUTORS BE LIABLE
19 * FOR ANY DIRECT, INDIRECT, INCIDENTAL, SPECIAL, EXEMPLARY, OR CONSEQUENTIAL
20 * DAMAGES (INCLUDING, BUT NOT LIMITED TO, PROCUREMENT OF SUBSTITUTE GOODS
21 * OR SERVICES; LOSS OF USE, DATA, OR PROFITS; OR BUSINESS INTERRUPTION)
22 * HOWEVER CAUSED AND ON ANY THEORY OF LIABILITY, WHETHER IN CONTRACT, STRICT
23 * LIABILITY, OR TORT (INCLUDING NEGLIGENCE OR OTHERWISE) ARISING IN ANY WAY
24 * OUT OF THE USE OF THIS SOFTWARE, EVEN IF ADVISED OF THE POSSIBILITY OF
25 * SUCH DAMAGE.
26 */
27
28 #include <sys/param.h>
29 #include <sys/conf.h>
30 #include <sys/ioctl.h>
31 #include <sys/fcntl.h>
32 #include <sys/file.h>
33 #include <sys/filedesc.h>
34 #include <sys/kmem.h>
35 #include <sys/lwp.h>
36 #include <sys/proc.h>
37 #include <sys/socket.h>
38 #include <sys/stat.h>
39 #include <sys/uio.h>
40
41 #include <net/bpf.h>
42 #include <net/if.h>
43
44 #include "dhcp_common.h"
45 #include "dhcp_dhcp.h"
46 #include "dhcp_net.h"
47 #include "dhcp_bpf-filter.h"
48
49 int
dhcp_open_socket(struct interface * iface,int protocol)50 dhcp_open_socket(struct interface *iface, int protocol)
51 {
52 struct lwp *l = curlwp;
53 struct file *fp;
54 devmajor_t bpfmajor;
55 struct ifreq ifr;
56 int buf_len = 0;
57 struct bpf_program pf;
58 int flags, indx = -1;
59 int error, fd;
60
61 /* open bpf withouth going through vfs */
62 bpfmajor = devsw_name2chr("bpf", NULL, 0);
63 if (bpfmajor == NODEVMAJOR) {
64 return EXDEV;
65 }
66 if ((error = cdev_open(makedev(bpfmajor, 0),
67 FREAD|FWRITE, S_IFCHR, curlwp)) != 0) {
68 if (error == EMOVEFD && l->l_dupfd >= 0) {
69 error = fd_dupopen(l->l_dupfd,
70 &indx, FREAD|FWRITE, error);
71 if (error == 0)
72 fd = indx;
73 }
74 if (error)
75 return error;
76 } else {
77 panic("bpf changed?");
78 }
79
80 if ((fp = fd_getfile(fd)) == NULL)
81 panic("file descriptor mismatch");
82
83 memset(&ifr, 0, sizeof(ifr));
84 strlcpy(ifr.ifr_name, iface->name, sizeof(ifr.ifr_name));
85 if ((error = fp->f_ops->fo_ioctl(fp, BIOCSETIF, &ifr)) != 0)
86 goto eexit;
87
88 /* Get the required BPF buffer length from the kernel. */
89 if ((error = fp->f_ops->fo_ioctl(fp, BIOCGBLEN, &buf_len)) != 0)
90 goto eexit;
91 if (iface->buffer_size != (size_t)buf_len) {
92 if (iface->buffer_size)
93 kmem_free(iface->buffer, iface->buffer_size);
94 iface->buffer_size = buf_len;
95 iface->buffer = kmem_alloc(buf_len, KM_SLEEP);
96 iface->buffer_len = iface->buffer_pos = 0;
97 }
98
99 flags = 1;
100 if ((error = fp->f_ops->fo_ioctl(fp, BIOCIMMEDIATE, &flags)) != 0)
101 goto eexit;
102
103 /* Install the DHCP filter */
104 if (protocol == ETHERTYPE_ARP) {
105 pf.bf_insns = UNCONST(arp_bpf_filter);
106 pf.bf_len = arp_bpf_filter_len;
107 iface->arp_fd = fd;
108 } else {
109 pf.bf_insns = UNCONST(dhcp_bpf_filter);
110 pf.bf_len = dhcp_bpf_filter_len;
111 iface->raw_fd = fd;
112 }
113 error = fp->f_ops->fo_ioctl(fp, BIOCSETF, &pf);
114
115 eexit:
116 if (error) {
117 kmem_free(iface->buffer, iface->buffer_size);
118 iface->buffer = NULL;
119 iface->buffer_len = 0;
120 iface->buffer_size = 0;
121 fd_close(fd);
122 } else {
123 fd_putfile(fd);
124 }
125 return error;
126 }
127
128 int
dhcp_send_raw_packet(const struct interface * iface,int protocol,const void * data,ssize_t len)129 dhcp_send_raw_packet(const struct interface *iface, int protocol,
130 const void *data, ssize_t len)
131 {
132 struct uio uio;
133 struct iovec iov[2];
134 struct file *fp;
135 struct ether_header hw;
136 int error;
137 int fd;
138
139 memset(&hw, 0, ETHER_HDR_LEN);
140 memset(&hw.ether_dhost, 0xff, ETHER_ADDR_LEN);
141 hw.ether_type = htons(protocol);
142 iov[0].iov_base = &hw;
143 iov[0].iov_len = ETHER_HDR_LEN;
144 iov[1].iov_base = UNCONST(data);
145 iov[1].iov_len = len;
146
147 uio.uio_iov = iov;
148 uio.uio_iovcnt = 2;
149 uio.uio_rw = UIO_WRITE;
150 uio.uio_vmspace = curproc->p_vmspace;
151 uio.uio_resid = ETHER_HDR_LEN + len;
152
153 if (protocol == ETHERTYPE_ARP)
154 fd = iface->arp_fd;
155 else
156 fd = iface->raw_fd;
157
158 if ((fp = fd_getfile(fd)) == NULL)
159 panic("send_raw_pcaket: fd mismatch");
160
161 error = fp->f_ops->fo_write(fp, 0, &uio, fp->f_cred, 0);
162
163 fd_putfile(fd);
164 return error;
165 }
166
167 /* BPF requires that we read the entire buffer.
168 * So we pass the buffer in the API so we can loop on >1 packet. */
169 ssize_t
dhcp_get_raw_packet(struct interface * iface,int protocol,void * data,ssize_t len)170 dhcp_get_raw_packet(struct interface *iface, int protocol,
171 void *data, ssize_t len)
172 {
173 struct file *fp;
174 int fd = -1;
175 struct bpf_hdr packet;
176 ssize_t bytes;
177 const unsigned char *payload;
178 struct uio uio;
179 int error;
180
181 if (protocol == ETHERTYPE_ARP)
182 fd = iface->arp_fd;
183 else
184 fd = iface->raw_fd;
185
186 fp = fd_getfile(fd);
187 for (;;) {
188 if (iface->buffer_len == 0) {
189 struct iovec iov;
190
191 iov.iov_base = iface->buffer;
192 iov.iov_len = iface->buffer_size;
193
194 uio.uio_iov = &iov;
195 uio.uio_iovcnt = 1;
196 uio.uio_resid = iface->buffer_size;
197 uio.uio_rw = UIO_READ;
198 uio.uio_vmspace = curproc->p_vmspace;
199
200 error = fp->f_ops->fo_read(fp, 0, &uio, fp->f_cred, 0);
201 bytes = iface->buffer_size - uio.uio_resid;
202 if (error != 0) {
203 bytes = -1;
204 break;
205 } else if ((size_t)bytes < sizeof(packet)) {
206 bytes = -1;
207 break;
208 }
209 iface->buffer_len = bytes;
210 iface->buffer_pos = 0;
211 }
212 bytes = -1;
213 memcpy(&packet, iface->buffer + iface->buffer_pos,
214 sizeof(packet));
215 if (packet.bh_caplen != packet.bh_datalen)
216 goto next; /* Incomplete packet, drop. */
217 if (iface->buffer_pos + packet.bh_caplen + packet.bh_hdrlen >
218 iface->buffer_len)
219 goto next; /* Packet beyond buffer, drop. */
220 payload = iface->buffer + packet.bh_hdrlen + ETHER_HDR_LEN;
221 bytes = packet.bh_caplen - ETHER_HDR_LEN;
222 if (bytes > len)
223 bytes = len;
224 memcpy(data, payload, bytes);
225 next:
226 iface->buffer_pos += BPF_WORDALIGN(packet.bh_hdrlen +
227 packet.bh_caplen);
228 if (iface->buffer_pos >= iface->buffer_len)
229 iface->buffer_len = iface->buffer_pos = 0;
230 if (bytes != -1)
231 break;
232 }
233 fd_putfile(fd);
234
235 return bytes;
236 }
237