xref: /openbsd/usr.sbin/dhcrelay6/bpf.c (revision 73471bf0)
1 /*	$OpenBSD: bpf.c,v 1.3 2019/03/18 00:00:59 dlg Exp $ 	*/
2 
3 /* BPF socket interface code, originally contributed by Archie Cobbs. */
4 
5 /*
6  * Copyright (c) 1995, 1996, 1998, 1999
7  * The Internet Software Consortium.    All rights reserved.
8  *
9  * Redistribution and use in source and binary forms, with or without
10  * modification, are permitted provided that the following conditions
11  * are met:
12  *
13  * 1. Redistributions of source code must retain the above copyright
14  *    notice, this list of conditions and the following disclaimer.
15  * 2. Redistributions in binary form must reproduce the above copyright
16  *    notice, this list of conditions and the following disclaimer in the
17  *    documentation and/or other materials provided with the distribution.
18  * 3. Neither the name of The Internet Software Consortium nor the names
19  *    of its contributors may be used to endorse or promote products derived
20  *    from this software without specific prior written permission.
21  *
22  * THIS SOFTWARE IS PROVIDED BY THE INTERNET SOFTWARE CONSORTIUM AND
23  * CONTRIBUTORS ``AS IS'' AND ANY EXPRESS OR IMPLIED WARRANTIES,
24  * INCLUDING, BUT NOT LIMITED TO, THE IMPLIED WARRANTIES OF
25  * MERCHANTABILITY AND FITNESS FOR A PARTICULAR PURPOSE ARE
26  * DISCLAIMED.  IN NO EVENT SHALL THE INTERNET SOFTWARE CONSORTIUM OR
27  * CONTRIBUTORS BE LIABLE FOR ANY DIRECT, INDIRECT, INCIDENTAL,
28  * SPECIAL, EXEMPLARY, OR CONSEQUENTIAL DAMAGES (INCLUDING, BUT NOT
29  * LIMITED TO, PROCUREMENT OF SUBSTITUTE GOODS OR SERVICES; LOSS OF
30  * USE, DATA, OR PROFITS; OR BUSINESS INTERRUPTION) HOWEVER CAUSED AND
31  * ON ANY THEORY OF LIABILITY, WHETHER IN CONTRACT, STRICT LIABILITY,
32  * OR TORT (INCLUDING NEGLIGENCE OR OTHERWISE) ARISING IN ANY WAY OUT
33  * OF THE USE OF THIS SOFTWARE, EVEN IF ADVISED OF THE POSSIBILITY OF
34  * SUCH DAMAGE.
35  *
36  * This software has been written for the Internet Software Consortium
37  * by Ted Lemon <mellon@fugue.com> in cooperation with Vixie
38  * Enterprises.  To learn more about the Internet Software Consortium,
39  * see ``http://www.vix.com/isc''.  To learn more about Vixie
40  * Enterprises, see ``http://www.vix.com''.
41  */
42 
43 #include <sys/types.h>
44 #include <sys/ioctl.h>
45 #include <sys/socket.h>
46 
47 #include <net/bpf.h>
48 #include <net/if.h>
49 
50 #include <netinet/in.h>
51 #include <netinet/if_ether.h>
52 
53 #include <errno.h>
54 #include <fcntl.h>
55 #include <stdio.h>
56 #include <stdlib.h>
57 #include <string.h>
58 #include <unistd.h>
59 
60 #include "dhcp.h"
61 #include "dhcpd.h"
62 #include "log.h"
63 
64 ssize_t send_packet_layer3(struct interface_info *,
65     void *, size_t, struct packet_ctx *);
66 
67 /*
68  * Called by get_interface_list for each interface that's discovered.
69  * Opens a packet filter for each interface and adds it to the select
70  * mask.
71  */
72 int
73 if_register_bpf(struct interface_info *info)
74 {
75 	int sock;
76 
77 	/* Open the BPF device */
78 	if ((sock = open("/dev/bpf", O_RDWR)) == -1)
79 		fatal("Can't open bpf device");
80 
81 	/* Set the BPF device to point at this interface. */
82 	if (ioctl(sock, BIOCSETIF, &info->ifr) == -1)
83 		fatal("Can't attach interface %s to bpf device", info->name);
84 
85 	return (sock);
86 }
87 
88 void
89 if_register_send(struct interface_info *info)
90 {
91 	/*
92 	 * If we're using the bpf API for sending and receiving, we
93 	 * don't need to register this interface twice.
94 	 */
95 	info->wfdesc = info->rfdesc;
96 }
97 
98 /* DHCPv6 BPF filters. */
99 
100 /*
101  * Packet filter program: 'ip6 and udp and dst port DHCP6_SERVER_PORT'
102  */
103 struct bpf_insn dhcp6_bpf_sfilter[] = {
104 	/* Make sure this is an IP packet... */
105 	BPF_STMT(BPF_LD + BPF_H + BPF_ABS, 12),
106 	BPF_JUMP(BPF_JMP + BPF_JEQ + BPF_K, ETHERTYPE_IPV6, 0, 5),
107 
108 	/* Make sure this is an UDP packet. */
109 	BPF_STMT(BPF_LD + BPF_B + BPF_ABS, 20),
110 	BPF_JUMP(BPF_JMP + BPF_JEQ + BPF_K, IPPROTO_UDP, 0, 3),
111 
112 	/* Make sure it is the right port. */
113 	BPF_STMT(BPF_LD + BPF_H + BPF_ABS, 56),
114 	BPF_JUMP(BPF_JMP + BPF_JEQ + BPF_K, DHCP6_SERVER_PORT, 0, 1),
115 
116 	/* If we passed all the tests, ask for the whole packet. */
117 	BPF_STMT(BPF_RET + BPF_K, (u_int)-1),
118 
119 	/* Otherwise, drop it. */
120 	BPF_STMT(BPF_RET + BPF_K, 0),
121 };
122 
123 int dhcp6_bpf_sfilter_len = sizeof(dhcp6_bpf_sfilter) / sizeof(struct bpf_insn);
124 
125 /*
126  * Packet filter program: 'ip6 and udp'
127  */
128 struct bpf_insn dhcp6_bpf_wfilter[] = {
129 	/* Make sure this is an IP packet... */
130 	BPF_STMT(BPF_LD + BPF_H + BPF_ABS, 12),
131 	BPF_JUMP(BPF_JMP + BPF_JEQ + BPF_K, ETHERTYPE_IPV6, 0, 3),
132 
133 	/* Make sure this is an UDP packet. */
134 	BPF_STMT(BPF_LD + BPF_B + BPF_ABS, 20),
135 	BPF_JUMP(BPF_JMP + BPF_JEQ + BPF_K, IPPROTO_UDP, 0, 1),
136 
137 	/* If we passed all the tests, ask for the whole packet. */
138 	BPF_STMT(BPF_RET + BPF_K, (u_int)-1),
139 
140 	/* Otherwise, drop it. */
141 	BPF_STMT(BPF_RET + BPF_K, 0),
142 };
143 
144 int dhcp6_bpf_wfilter_len = sizeof(dhcp6_bpf_wfilter) / sizeof(struct bpf_insn);
145 
146 
147 void
148 if_register_receive(struct interface_info *info)
149 {
150 	struct bpf_version v;
151 	struct bpf_program p;
152 	int flag = 1, sz, cmplt = 0;
153 
154 	/* Open a BPF device and hang it on this interface... */
155 	info->rfdesc = if_register_bpf(info);
156 
157 	/* Make sure the BPF version is in range... */
158 	if (ioctl(info->rfdesc, BIOCVERSION, &v) == -1)
159 		fatal("Can't get BPF version");
160 
161 	if (v.bv_major != BPF_MAJOR_VERSION ||
162 	    v.bv_minor < BPF_MINOR_VERSION)
163 		fatalx("Kernel BPF version out of range - recompile dhcpd!");
164 
165 	/*
166 	 * Set immediate mode so that reads return as soon as a packet
167 	 * comes in, rather than waiting for the input buffer to fill
168 	 * with packets.
169 	 */
170 	if (ioctl(info->rfdesc, BIOCIMMEDIATE, &flag) == -1)
171 		fatal("Can't set immediate mode on bpf device");
172 
173 	/* make sure kernel fills in the source ethernet address */
174 	if (ioctl(info->rfdesc, BIOCSHDRCMPLT, &cmplt) == -1)
175 		fatal("Can't set header complete flag on bpf device");
176 
177 	/* Get the required BPF buffer length from the kernel. */
178 	if (ioctl(info->rfdesc, BIOCGBLEN, &sz) == -1)
179 		fatal("Can't get bpf buffer length");
180 	info->rbuf_max = sz;
181 	info->rbuf = malloc(info->rbuf_max);
182 	if (!info->rbuf)
183 		fatalx("Can't allocate %lu bytes for bpf input buffer.",
184 		    (unsigned long)info->rbuf_max);
185 	info->rbuf_offset = 0;
186 	info->rbuf_len = 0;
187 
188 	/* Set up the bpf filter program structure. */
189 	p.bf_len = dhcp6_bpf_sfilter_len;
190 	p.bf_insns = dhcp6_bpf_sfilter;
191 	if (ioctl(info->rfdesc, BIOCSETF, &p) == -1)
192 		fatal("Can't install packet filter program");
193 
194 	/* Set up the bpf write filter program structure. */
195 	p.bf_len = dhcp6_bpf_wfilter_len;
196 	p.bf_insns = dhcp6_bpf_wfilter;
197 	if (ioctl(info->rfdesc, BIOCSETWF, &p) == -1)
198 		fatal("Can't install write filter program");
199 
200 	/* Only get input packets. */
201 	flag = BPF_DIRECTION_OUT;
202 	if (ioctl(info->rfdesc, BIOCSDIRFILT , &flag) == -1)
203 		fatal("Can't set BPF direction capture");
204 
205 	/* Drop them so they don't go up in the network stack. */
206 	flag = BPF_FILDROP_CAPTURE;
207 	if (ioctl(info->rfdesc, BIOCSFILDROP, &flag) == -1)
208 		fatal("Can't set BPF filter drop");
209 
210 	/* make sure these settings cannot be changed after dropping privs */
211 	if (ioctl(info->rfdesc, BIOCLOCK) == -1)
212 		fatal("Failed to lock bpf descriptor");
213 }
214 
215 ssize_t
216 send_packet_layer3(struct interface_info *intf, void *raw, size_t len,
217     struct packet_ctx *pc)
218 {
219 	struct cmsghdr		*cmsg;
220 	ssize_t			 sendlen;
221 	struct msghdr		 msg;
222 	struct in6_pktinfo	*ipi6;
223 	struct sockaddr_storage	 ss;
224 	struct iovec		 iov[2];
225 	uint8_t			 cmsgbuf[
226 	    CMSG_SPACE(sizeof(struct in6_pktinfo))
227 	];
228 
229 	log_debug("  sending %ld bytes to %s:%d via %s",
230 	    len, v6addr2str(&ss2sin6(&pc->pc_dst)->sin6_addr),
231 	    ntohs(ss2sin6(&pc->pc_dst)->sin6_port), intf->name);
232 
233 	memset(&msg, 0, sizeof(msg));
234 	iov[0].iov_base = raw;
235 	iov[0].iov_len = len;
236 	msg.msg_iov = iov;
237 	msg.msg_iovlen = 1;
238 
239 	ss = pc->pc_dst;
240 	ss2sin6(&ss)->sin6_scope_id = intf->index;
241 	msg.msg_name = &ss;
242 	msg.msg_namelen = ss.ss_len;
243 
244 	/* If binded to multicast we should select an interface. */
245 	if (IN6_IS_ADDR_MULTICAST(&ss2sin6(&ss)->sin6_addr)) {
246 		memset(cmsgbuf, 0, sizeof(cmsgbuf));
247 		msg.msg_control = cmsgbuf;
248 		msg.msg_controllen = sizeof(cmsgbuf);
249 
250 		/* Use the IPV6_PKTINFO to select the interface. */
251 		cmsg = (struct cmsghdr *)CMSG_FIRSTHDR(&msg);
252 		cmsg->cmsg_len = CMSG_LEN(sizeof(*ipi6));
253 		cmsg->cmsg_level = IPPROTO_IPV6;
254 		cmsg->cmsg_type = IPV6_PKTINFO;
255 
256 		ipi6 = (struct in6_pktinfo *)CMSG_DATA(cmsg);
257 		ipi6->ipi6_ifindex = intf->index;
258 	}
259 
260 	if ((sendlen = sendmsg(pc->pc_sd, &msg, 0)) == -1) {
261 		log_warn("  failed to send message");
262 		return -1;
263 	}
264 	if (sendlen < (ssize_t)len)
265 		log_warnx("  sent less bytes than expected (%ld < %ld)",
266 		    sendlen, len);
267 
268 	return sendlen;
269 }
270 
271 ssize_t
272 send_packet(struct interface_info *interface,
273     void *raw, size_t len, struct packet_ctx *pc)
274 {
275 	unsigned char buf[256];
276 	struct iovec iov[2];
277 	int result, bufp = 0;
278 
279 	if (pc->pc_sd != 0)
280 		return send_packet_layer3(interface, raw, len, pc);
281 
282 	/* Assemble the headers... */
283 	assemble_hw_header(buf, &bufp, pc);
284 	assemble_udp_ip6_header(buf, &bufp, pc, raw, len);
285 
286 	/* Fire it off */
287 	iov[0].iov_base = (char *)buf;
288 	iov[0].iov_len = bufp;
289 	iov[1].iov_base = (char *)raw;
290 	iov[1].iov_len = len;
291 
292 	result = writev(interface->wfdesc, iov, 2);
293 	if (result == -1)
294 		log_warn("send_packet");
295 
296 	return (result);
297 }
298 
299 ssize_t
300 receive_packet(struct interface_info *interface, unsigned char *buf,
301     size_t len, struct packet_ctx *pc)
302 {
303 	int length = 0, offset = 0;
304 	struct bpf_hdr hdr;
305 
306 	/*
307 	 * All this complexity is because BPF doesn't guarantee that
308 	 * only one packet will be returned at a time.  We're getting
309 	 * what we deserve, though - this is a terrible abuse of the BPF
310 	 * interface.  Sigh.
311 	 */
312 
313 	/* Process packets until we get one we can return or until we've
314 	 * done a read and gotten nothing we can return...
315 	 */
316 	do {
317 		/* If the buffer is empty, fill it. */
318 		if (interface->rbuf_offset == interface->rbuf_len) {
319 			length = read(interface->rfdesc, interface->rbuf,
320 			    interface->rbuf_max);
321 			if (length <= 0)
322 				return (length);
323 			interface->rbuf_offset = 0;
324 			interface->rbuf_len = length;
325 		}
326 
327 		/*
328 		 * If there isn't room for a whole bpf header, something
329 		 * went wrong, but we'll ignore it and hope it goes
330 		 * away... XXX
331 		 */
332 		if (interface->rbuf_len - interface->rbuf_offset <
333 		    sizeof(hdr)) {
334 			interface->rbuf_offset = interface->rbuf_len;
335 			continue;
336 		}
337 
338 		/* Copy out a bpf header... */
339 		memcpy(&hdr, &interface->rbuf[interface->rbuf_offset],
340 		    sizeof(hdr));
341 
342 		/*
343 		 * If the bpf header plus data doesn't fit in what's
344 		 * left of the buffer, stick head in sand yet again...
345 		 */
346 		if (interface->rbuf_offset + hdr.bh_hdrlen + hdr.bh_caplen >
347 		    interface->rbuf_len) {
348 			interface->rbuf_offset = interface->rbuf_len;
349 			continue;
350 		}
351 
352 		/*
353 		 * If the captured data wasn't the whole packet, or if
354 		 * the packet won't fit in the input buffer, all we can
355 		 * do is drop it.
356 		 */
357 		if (hdr.bh_caplen != hdr.bh_datalen) {
358 			interface->rbuf_offset += hdr.bh_hdrlen =
359 			    hdr.bh_caplen;
360 			continue;
361 		}
362 
363 		/* Skip over the BPF header... */
364 		interface->rbuf_offset += hdr.bh_hdrlen;
365 
366 		/* Decode the physical header... */
367 		offset = decode_hw_header(interface->rbuf,
368 		    interface->rbuf_offset, pc);
369 
370 		/*
371 		 * If a physical layer checksum failed (dunno of any
372 		 * physical layer that supports this, but WTH), skip
373 		 * this packet.
374 		 */
375 		if (offset < 0) {
376 			interface->rbuf_offset += hdr.bh_caplen;
377 			continue;
378 		}
379 		interface->rbuf_offset += offset;
380 		hdr.bh_caplen -= offset;
381 
382 		/* Decode the IP and UDP headers... */
383 		offset = decode_udp_ip6_header(interface->rbuf,
384 		    interface->rbuf_offset, pc, hdr.bh_caplen);
385 
386 		/* If the IP or UDP checksum was bad, skip the packet... */
387 		if (offset < 0) {
388 			interface->rbuf_offset += hdr.bh_caplen;
389 			continue;
390 		}
391 		interface->rbuf_offset += offset;
392 		hdr.bh_caplen -= offset;
393 
394 		/*
395 		 * If there's not enough room to stash the packet data,
396 		 * we have to skip it (this shouldn't happen in real
397 		 * life, though).
398 		 */
399 		if (hdr.bh_caplen > len) {
400 			interface->rbuf_offset += hdr.bh_caplen;
401 			continue;
402 		}
403 
404 		/* Copy out the data in the packet... */
405 		memcpy(buf, interface->rbuf + interface->rbuf_offset,
406 		    hdr.bh_caplen);
407 		interface->rbuf_offset += hdr.bh_caplen;
408 		return (hdr.bh_caplen);
409 	} while (!length);
410 	return (0);
411 }
412