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