bpf.c 14.8 KB
Newer Older
Ted Lemon's avatar
Ted Lemon committed
1 2 3 4 5
/* bpf.c

   BPF socket interface code, originally contributed by Archie Cobbs. */

/*
Ted Lemon's avatar
Ted Lemon committed
6 7
 * Copyright (c) 1996-2000 Internet Software Consortium.
 * All rights reserved.
Ted Lemon's avatar
Ted Lemon committed
8
 *
Ted Lemon's avatar
Ted Lemon committed
9 10 11
 * Redistribution and use in source and binary forms, with or without
 * modification, are permitted provided that the following conditions
 * are met:
Ted Lemon's avatar
Ted Lemon committed
12
 *
Ted Lemon's avatar
Ted Lemon committed
13 14 15 16 17 18 19 20
 * 1. Redistributions of source code must retain the above copyright
 *    notice, this list of conditions and the following disclaimer.
 * 2. Redistributions in binary form must reproduce the above copyright
 *    notice, this list of conditions and the following disclaimer in the
 *    documentation and/or other materials provided with the distribution.
 * 3. Neither the name of The Internet Software Consortium nor the names
 *    of its contributors may be used to endorse or promote products derived
 *    from this software without specific prior written permission.
Ted Lemon's avatar
Ted Lemon committed
21
 *
Ted Lemon's avatar
Ted Lemon committed
22 23 24 25 26 27 28 29 30 31 32 33 34 35 36 37 38 39 40 41
 * THIS SOFTWARE IS PROVIDED BY THE INTERNET SOFTWARE CONSORTIUM AND
 * CONTRIBUTORS ``AS IS'' AND ANY EXPRESS OR IMPLIED WARRANTIES,
 * INCLUDING, BUT NOT LIMITED TO, THE IMPLIED WARRANTIES OF
 * MERCHANTABILITY AND FITNESS FOR A PARTICULAR PURPOSE ARE
 * DISCLAIMED.  IN NO EVENT SHALL THE INTERNET SOFTWARE CONSORTIUM OR
 * CONTRIBUTORS BE LIABLE FOR ANY DIRECT, INDIRECT, INCIDENTAL,
 * SPECIAL, EXEMPLARY, OR CONSEQUENTIAL DAMAGES (INCLUDING, BUT NOT
 * LIMITED TO, PROCUREMENT OF SUBSTITUTE GOODS OR SERVICES; LOSS OF
 * USE, DATA, OR PROFITS; OR BUSINESS INTERRUPTION) HOWEVER CAUSED AND
 * ON ANY THEORY OF LIABILITY, WHETHER IN CONTRACT, STRICT LIABILITY,
 * OR TORT (INCLUDING NEGLIGENCE OR OTHERWISE) ARISING IN ANY WAY OUT
 * OF THE USE OF THIS SOFTWARE, EVEN IF ADVISED OF THE POSSIBILITY OF
 * SUCH DAMAGE.
 *
 * This software has been written for the Internet Software Consortium
 * by Ted Lemon in cooperation with Vixie Enterprises and Nominum, Inc.
 * To learn more about the Internet Software Consortium, see
 * ``http://www.isc.org/''.  To learn more about Vixie Enterprises,
 * see ``http://www.vix.com''.   To learn more about Nominum, Inc., see
 * ``http://www.nominum.com''.
Ted Lemon's avatar
Ted Lemon committed
42 43 44 45
 */

#ifndef lint
static char copyright[] =
Ted Lemon's avatar
Ted Lemon committed
46
"$Id: bpf.c,v 1.35 2000/03/17 03:59:00 mellon Exp $ Copyright (c) 1995-2000 The Internet Software Consortium.  All rights reserved.\n";
Ted Lemon's avatar
Ted Lemon committed
47 48 49
#endif /* not lint */

#include "dhcpd.h"
Ted Lemon's avatar
Ted Lemon committed
50 51 52 53 54 55 56 57 58 59 60 61 62 63
#if defined (USE_BPF_SEND) || defined (USE_BPF_RECEIVE)	\
				|| defined (USE_LPF_RECEIVE)
# if defined (USE_LPF_RECEIVE)
#  include <asm/types.h>
#  include <linux/filter.h>
#  define bpf_insn sock_filter /* Linux: dare to be gratuitously different. */
# else
#  include <sys/ioctl.h>
#  include <sys/uio.h>
#  include <net/bpf.h>
#  if defined (NEED_OSF_PFILT_HACKS)
#   include <net/pfilt.h>
#  endif
# endif
Ted Lemon's avatar
Ted Lemon committed
64 65

#include <netinet/in_systm.h>
66 67 68
#include "includes/netinet/ip.h"
#include "includes/netinet/udp.h"
#include "includes/netinet/if_ether.h"
Ted Lemon's avatar
Ted Lemon committed
69
#endif
Ted Lemon's avatar
Ted Lemon committed
70

71 72 73 74 75 76 77 78 79 80 81 82 83 84 85 86 87
/* Reinitializes the specified interface after an address change.   This
   is not required for packet-filter APIs. */

#ifdef USE_BPF_SEND
void if_reinitialize_send (info)
	struct interface_info *info;
{
}
#endif

#ifdef USE_BPF_RECEIVE
void if_reinitialize_receive (info)
	struct interface_info *info;
{
}
#endif

Ted Lemon's avatar
Ted Lemon committed
88 89 90 91
/* Called by get_interface_list for each interface that's discovered.
   Opens a packet filter for each interface and adds it to the select
   mask. */

Ted Lemon's avatar
Ted Lemon committed
92
#if defined (USE_BPF_SEND) || defined (USE_BPF_RECEIVE)
93
int if_register_bpf (info)
94
	struct interface_info *info;
Ted Lemon's avatar
Ted Lemon committed
95
{
96
	int sock;
Ted Lemon's avatar
Ted Lemon committed
97
	char filename[50];
98
	int b;
Ted Lemon's avatar
Ted Lemon committed
99 100 101

	/* Open a BPF device */
	for (b = 0; 1; b++) {
102
#ifndef NO_SNPRINTF
103
		snprintf(filename, sizeof(filename), BPF_FORMAT, b);
104
#else
105
		sprintf(filename, BPF_FORMAT, b);
106
#endif
107 108
		sock = open (filename, O_RDWR, 0);
		if (sock < 0) {
Ted Lemon's avatar
Ted Lemon committed
109 110 111
			if (errno == EBUSY) {
				continue;
			} else {
Ted Lemon's avatar
Ted Lemon committed
112
				if (!b)
113
					log_fatal ("No bpf devices.%s%s%s",
Ted Lemon's avatar
Ted Lemon committed
114 115 116
					       "   Please read the README",
					       " section for your operating",
					       " system.");
117
				log_fatal ("Can't find free bpf: %m");
Ted Lemon's avatar
Ted Lemon committed
118 119 120 121 122 123 124
			}
		} else {
			break;
		}
	}

	/* Set the BPF device to point at this interface. */
125
	if (ioctl (sock, BIOCSETIF, info -> ifp) < 0)
126
		log_fatal ("Can't attach interface %s to bpf device %s: %m",
Ted Lemon's avatar
Ted Lemon committed
127
		       info -> name, filename);
128 129 130 131 132 133

	return sock;
}
#endif /* USE_BPF_SEND || USE_BPF_RECEIVE */

#ifdef USE_BPF_SEND
134
void if_register_send (info)
135 136 137 138 139 140 141 142 143
	struct interface_info *info;
{
	/* If we're using the bpf API for sending and receiving,
	   we don't need to register this interface twice. */
#ifndef USE_BPF_RECEIVE
	info -> wfdesc = if_register_bpf (info, interface);
#else
	info -> wfdesc = info -> rfdesc;
#endif
144
	if (!quiet_interface_discovery)
145
		log_info ("Sending on   BPF/%s/%s%s%s",
146
		      info -> name,
Ted Lemon's avatar
Ted Lemon committed
147 148 149
		      print_hw_addr (info -> hw_address.hbuf [0],
				     info -> hw_address.hlen - 1,
				     &info -> hw_address.hbuf [1]),
150
		      (info -> shared_network ? "/" : ""),
151
		      (info -> shared_network ?
152
		       info -> shared_network -> name : ""));
Ted Lemon's avatar
Ted Lemon committed
153
}
154 155 156 157 158 159 160 161 162 163 164 165 166 167 168 169 170 171 172 173 174

void if_deregister_send (info)
	struct interface_info *info;
{
	/* If we're using the bpf API for sending and receiving,
	   we don't need to register this interface twice. */
#ifndef USE_BPF_RECEIVE
	close (info -> wfdesc);
#endif
	info -> wfdesc = -1;

	if (!quiet_interface_discovery)
		log_info ("Disabling output on BPF/%s/%s%s%s",
		      info -> name,
		      print_hw_addr (info -> hw_address.hbuf [0],
				     info -> hw_address.hlen - 1,
				     &info -> hw_address.hbuf [1]),
		      (info -> shared_network ? "/" : ""),
		      (info -> shared_network ?
		       info -> shared_network -> name : ""));
}
175 176
#endif /* USE_BPF_SEND */

Ted Lemon's avatar
Ted Lemon committed
177
#if defined (USE_BPF_RECEIVE) || defined (USE_LPF_RECEIVE)
178 179 180 181
/* Packet filter program...
   XXX Changes to the filter program may require changes to the constant
   offsets used in if_register_send to patch the BPF program! XXX */

Ted Lemon's avatar
Ted Lemon committed
182
struct bpf_insn dhcp_bpf_filter [] = {
183 184
	/* Make sure this is an IP packet... */
	BPF_STMT (BPF_LD + BPF_H + BPF_ABS, 12),
185
	BPF_JUMP (BPF_JMP + BPF_JEQ + BPF_K, ETHERTYPE_IP, 0, 8),
186 187 188

	/* Make sure it's a UDP packet... */
	BPF_STMT (BPF_LD + BPF_B + BPF_ABS, 23),
189
	BPF_JUMP (BPF_JMP + BPF_JEQ + BPF_K, IPPROTO_UDP, 0, 6),
190 191 192

	/* Make sure this isn't a fragment... */
	BPF_STMT(BPF_LD + BPF_H + BPF_ABS, 20),
193
	BPF_JUMP(BPF_JMP + BPF_JSET + BPF_K, 0x1fff, 4, 0),
194 195 196 197 198 199

	/* Get the IP header length... */
	BPF_STMT (BPF_LDX + BPF_B + BPF_MSH, 14),

	/* Make sure it's to the right port... */
	BPF_STMT (BPF_LD + BPF_H + BPF_IND, 16),
200
	BPF_JUMP (BPF_JMP + BPF_JEQ + BPF_K, 67, 0, 1),             /* patch */
Ted Lemon's avatar
Ted Lemon committed
201

202 203 204 205 206 207 208
	/* If we passed all the tests, ask for the whole packet. */
	BPF_STMT(BPF_RET+BPF_K, (u_int)-1),

	/* Otherwise, drop it. */
	BPF_STMT(BPF_RET+BPF_K, 0),
};

Ted Lemon's avatar
Ted Lemon committed
209
int dhcp_bpf_filter_len = sizeof dhcp_bpf_filter / sizeof (struct bpf_insn);
Ted Lemon's avatar
Ted Lemon committed
210 211 212 213 214 215 216 217 218 219 220 221 222
struct bpf_insn dhcp_bpf_tr_filter [] = {
        /* accept all token ring packets due to variable length header */
        /* if we want to get clever, insert the program here */

	/* If we passed all the tests, ask for the whole packet. */
	BPF_STMT(BPF_RET+BPF_K, (u_int)-1),

	/* Otherwise, drop it. */
	BPF_STMT(BPF_RET+BPF_K, 0),
};

int dhcp_bpf_tr_filter_len = (sizeof dhcp_bpf_tr_filter /
			      sizeof (struct bpf_insn));
Ted Lemon's avatar
Ted Lemon committed
223 224 225
#endif

#if defined (USE_BPF_RECEIVE)
226
void if_register_receive (info)
227 228 229 230 231 232
	struct interface_info *info;
{
	int flag = 1;
	struct bpf_version v;
	u_int32_t addr;
	struct bpf_program p;
233
	u_int32_t bits;
234 235

	/* Open a BPF device and hang it on this interface... */
236
	info -> rfdesc = if_register_bpf (info);
237 238 239

	/* Make sure the BPF version is in range... */
	if (ioctl (info -> rfdesc, BIOCVERSION, &v) < 0)
240
		log_fatal ("Can't get BPF version: %m");
241 242 243

	if (v.bv_major != BPF_MAJOR_VERSION ||
	    v.bv_minor < BPF_MINOR_VERSION)
244
		log_fatal ("Kernel BPF version out of range - recompile dhcpd!");
245 246 247 248 249

	/* Set immediate mode so that reads return as soon as a packet
	   comes in, rather than waiting for the input buffer to fill with
	   packets. */
	if (ioctl (info -> rfdesc, BIOCIMMEDIATE, &flag) < 0)
250
		log_fatal ("Can't set immediate mode on bpf device: %m");
251

252 253 254
#ifdef NEED_OSF_PFILT_HACKS
	/* Allow the copyall flag to be set... */
	if (ioctl(info -> rfdesc, EIOCALLOWCOPYALL, &flag) < 0)
255
		log_fatal ("Can't set ALLOWCOPYALL: %m");
256 257 258 259

	/* Clear all the packet filter mode bits first... */
	bits = 0;
	if (ioctl (info -> rfdesc, EIOCMBIS, &bits) < 0)
260
		log_fatal ("Can't clear pfilt bits: %m");
261 262 263 264

	/* Set the ENBATCH, ENCOPYALL, ENBPFHDR bits... */
	bits = ENBATCH | ENCOPYALL | ENBPFHDR;
	if (ioctl (info -> rfdesc, EIOCMBIS, &bits) < 0)
265
		log_fatal ("Can't set ENBATCH|ENCOPYALL|ENBPFHDR: %m");
266
#endif
267 268
	/* Get the required BPF buffer length from the kernel. */
	if (ioctl (info -> rfdesc, BIOCGBLEN, &info -> rbuf_max) < 0)
269
		log_fatal ("Can't get bpf buffer length: %m");
270
	info -> rbuf = dmalloc (info -> rbuf_max, MDL);
271
	if (!info -> rbuf)
272 273
		log_fatal ("Can't allocate %d bytes for bpf input buffer.",
			   info -> rbuf_max);
274 275 276 277
	info -> rbuf_offset = 0;
	info -> rbuf_len = 0;

	/* Set up the bpf filter program structure. */
Ted Lemon's avatar
Ted Lemon committed
278 279
	p.bf_len = dhcp_bpf_filter_len;
	p.bf_insns = dhcp_bpf_filter;
280

Ted Lemon's avatar
Ted Lemon committed
281 282 283
        /* Patch the server port into the BPF  program...
	   XXX changes to filter program may require changes
	   to the insn number(s) used below! XXX */
Ted Lemon's avatar
Ted Lemon committed
284
	dhcp_bpf_filter [8].k = ntohs (local_port);
Ted Lemon's avatar
Ted Lemon committed
285

286
	if (ioctl (info -> rfdesc, BIOCSETF, &p) < 0)
287
		log_fatal ("Can't install packet filter program: %m");
288
	if (!quiet_interface_discovery)
289
		log_info ("Listening on BPF/%s/%s%s%s",
290
		      info -> name,
Ted Lemon's avatar
Ted Lemon committed
291
		      print_hw_addr (info -> hw_address.hbuf [0],
292 293 294 295 296 297 298 299 300 301 302 303 304 305 306 307 308
				     info -> hw_address.hlen - 1,
				     &info -> hw_address.hbuf [1]),
		      (info -> shared_network ? "/" : ""),
		      (info -> shared_network ?
		       info -> shared_network -> name : ""));
}

void if_deregister_receive (info)
	struct interface_info *info;
{
	close (info -> rfdesc);
	info -> rfdesc = -1;

	if (!quiet_interface_discovery)
		log_info ("Disabling input on BPF/%s/%s%s%s",
		      info -> name,
		      print_hw_addr (info -> hw_address.hbuf [0],
Ted Lemon's avatar
Ted Lemon committed
309 310
				     info -> hw_address.hlen - 1,
				     &info -> hw_address.hbuf [1]),
311
		      (info -> shared_network ? "/" : ""),
312
		      (info -> shared_network ?
313
		       info -> shared_network -> name : ""));
314 315 316 317
}
#endif /* USE_BPF_RECEIVE */

#ifdef USE_BPF_SEND
318
ssize_t send_packet (interface, packet, raw, len, from, to, hto)
319 320 321 322
	struct interface_info *interface;
	struct packet *packet;
	struct dhcp_packet *raw;
	size_t len;
323
	struct in_addr from;
324 325 326
	struct sockaddr_in *to;
	struct hardware *hto;
{
327
	unsigned bufp = 0;
328 329
	unsigned char buf [256];
	struct iovec iov [2];
330
	int result;
331

Ted Lemon's avatar
Ted Lemon committed
332 333 334 335
	if (!strcmp (interface -> name, "fallback"))
		return send_fallback (interface, packet, raw,
				      len, from, to, hto);

336 337
	/* Assemble the headers... */
	assemble_hw_header (interface, buf, &bufp, hto);
338
	assemble_udp_ip_header (interface, buf, &bufp, from.s_addr,
339 340 341 342
				to -> sin_addr.s_addr, to -> sin_port,
				(unsigned char *)raw, len);

	/* Fire it off */
343
	iov [0].iov_base = (char *)buf;
344 345 346 347
	iov [0].iov_len = bufp;
	iov [1].iov_base = (char *)raw;
	iov [1].iov_len = len;

348 349
	result = writev(interface -> wfdesc, iov, 2);
	if (result < 0)
Ted Lemon's avatar
Ted Lemon committed
350
		log_error ("send_packet: %m");
351
	return result;
352 353 354 355
}
#endif /* USE_BPF_SEND */

#ifdef USE_BPF_RECEIVE
356
ssize_t receive_packet (interface, buf, len, from, hfrom)
357 358 359 360 361 362 363 364 365 366 367 368 369 370 371 372 373 374 375 376 377 378 379 380 381 382 383 384 385 386 387 388 389 390 391 392 393 394 395 396 397 398 399 400 401 402 403 404 405 406 407 408 409 410 411 412 413 414 415 416 417 418 419 420 421 422 423 424 425 426 427 428 429 430 431 432 433 434 435 436 437 438 439 440 441 442 443 444 445 446 447 448 449 450 451 452 453 454 455 456 457 458 459 460 461 462 463 464 465 466
	struct interface_info *interface;
	unsigned char *buf;
	size_t len;
	struct sockaddr_in *from;
	struct hardware *hfrom;
{
	int length = 0;
	int offset = 0;
	struct bpf_hdr hdr;

	/* All this complexity is because BPF doesn't guarantee
	   that only one packet will be returned at a time.   We're
	   getting what we deserve, though - this is a terrible abuse
	   of the BPF interface.   Sigh. */

	/* Process packets until we get one we can return or until we've
	   done a read and gotten nothing we can return... */

	do {
		/* If the buffer is empty, fill it. */
		if (interface -> rbuf_offset == interface -> rbuf_len) {
			length = read (interface -> rfdesc,
				       interface -> rbuf,
				       interface -> rbuf_max);
			if (length <= 0)
				return length;
			interface -> rbuf_offset = 0;
			interface -> rbuf_len = length;
		}

		/* If there isn't room for a whole bpf header, something went
		   wrong, but we'll ignore it and hope it goes away... XXX */
		if (interface -> rbuf_len -
		    interface -> rbuf_offset < sizeof hdr) {
			interface -> rbuf_offset = interface -> rbuf_len;
			continue;
		}

		/* Copy out a bpf header... */
		memcpy (&hdr, &interface -> rbuf [interface -> rbuf_offset],
			sizeof hdr);

		/* If the bpf header plus data doesn't fit in what's left
		   of the buffer, stick head in sand yet again... */
		if (interface -> rbuf_offset +
		    hdr.bh_hdrlen + hdr.bh_caplen > interface -> rbuf_len) {
			interface -> rbuf_offset = interface -> rbuf_len;
			continue;
		}

		/* If the captured data wasn't the whole packet, or if
		   the packet won't fit in the input buffer, all we
		   can do is drop it. */
		if (hdr.bh_caplen != hdr.bh_datalen) {
			interface -> rbuf_offset +=
				hdr.bh_hdrlen = hdr.bh_caplen;
			continue;
		}

		/* Skip over the BPF header... */
		interface -> rbuf_offset += hdr.bh_hdrlen;

		/* Decode the physical header... */
		offset = decode_hw_header (interface,
					   interface -> rbuf,
					   interface -> rbuf_offset,
					   hfrom);

		/* If a physical layer checksum failed (dunno of any
		   physical layer that supports this, but WTH), skip this
		   packet. */
		if (offset < 0) {
			interface -> rbuf_offset += hdr.bh_caplen;
			continue;
		}
		interface -> rbuf_offset += offset;
		hdr.bh_caplen -= offset;

		/* Decode the IP and UDP headers... */
		offset = decode_udp_ip_header (interface,
					       interface -> rbuf,
					       interface -> rbuf_offset,
					       from,
					       (unsigned char *)0,
					       hdr.bh_caplen);

		/* If the IP or UDP checksum was bad, skip the packet... */
		if (offset < 0) {
			interface -> rbuf_offset += hdr.bh_caplen;
			continue;
		}
		interface -> rbuf_offset += offset;
		hdr.bh_caplen -= offset;

		/* If there's not enough room to stash the packet data,
		   we have to skip it (this shouldn't happen in real
		   life, though). */
		if (hdr.bh_caplen > len) {
			interface -> rbuf_offset += hdr.bh_caplen;
			continue;
		}

		/* Copy out the data in the packet... */
		memcpy (buf, interface -> rbuf + interface -> rbuf_offset,
			hdr.bh_caplen);
		interface -> rbuf_offset += hdr.bh_caplen;
		return hdr.bh_caplen;
	} while (!length);
	return 0;
}
Ted Lemon's avatar
Ted Lemon committed
467

Ted Lemon's avatar
Ted Lemon committed
468 469
int can_unicast_without_arp (ip)
	struct interface_info *ip;
Ted Lemon's avatar
Ted Lemon committed
470 471 472 473
{
	return 1;
}

Ted Lemon's avatar
Ted Lemon committed
474 475
int can_receive_unicast_unconfigured (ip)
	struct interface_info *ip;
476 477 478 479
{
	return 1;
}

Ted Lemon's avatar
Ted Lemon committed
480 481
void maybe_setup_fallback ()
{
Ted Lemon's avatar
Ted Lemon committed
482
	isc_result_t status;
Ted Lemon's avatar
Ted Lemon committed
483 484 485 486
	struct interface_info *fbi;
	fbi = setup_fallback ();
	if (fbi) {
		if_register_fallback (fbi);
Ted Lemon's avatar
Ted Lemon committed
487 488
		fbi -> refcnt = 1;
		fbi -> type = dhcp_type_interface;
489
		status = omapi_register_io_object ((omapi_object_t *)fbi,
Ted Lemon's avatar
Ted Lemon committed
490 491 492 493 494
						   if_readsocket, 0,
						   fallback_discard, 0, 0);
		if (status != ISC_R_SUCCESS)
			log_fatal ("Can't register I/O handle for %s: %s",
				   fbi -> name, isc_result_totext (status));
Ted Lemon's avatar
Ted Lemon committed
495 496
	}
}
497
#endif