xref: /netbsd-src/external/mpl/dhcp/dist/common/lpf.c (revision f407d9293b6650aa8c33d6a995f797bb6aaefd90)
1 /*	$NetBSD: lpf.c,v 1.4 2022/04/03 01:10:58 christos Exp $	*/
2 
3 /* lpf.c
4 
5    Linux packet filter code, contributed by Brian Murrel at Interlinx
6    Support Services in Vancouver, B.C. */
7 
8 /*
9  * Copyright (C) 2004-2022 Internet Systems Consortium, Inc. ("ISC")
10  * Copyright (c) 1996-2003 by Internet Software Consortium
11  *
12  * This Source Code Form is subject to the terms of the Mozilla Public
13  * License, v. 2.0. If a copy of the MPL was not distributed with this
14  * file, You can obtain one at http://mozilla.org/MPL/2.0/.
15  *
16  * THE SOFTWARE IS PROVIDED "AS IS" AND ISC DISCLAIMS ALL WARRANTIES
17  * WITH REGARD TO THIS SOFTWARE INCLUDING ALL IMPLIED WARRANTIES OF
18  * MERCHANTABILITY AND FITNESS.  IN NO EVENT SHALL ISC BE LIABLE FOR
19  * ANY SPECIAL, DIRECT, INDIRECT, OR CONSEQUENTIAL DAMAGES OR ANY DAMAGES
20  * WHATSOEVER RESULTING FROM LOSS OF USE, DATA OR PROFITS, WHETHER IN AN
21  * ACTION OF CONTRACT, NEGLIGENCE OR OTHER TORTIOUS ACTION, ARISING OUT
22  * OF OR IN CONNECTION WITH THE USE OR PERFORMANCE OF THIS SOFTWARE.
23  *
24  *   Internet Systems Consortium, Inc.
25  *   PO Box 360
26  *   Newmarket, NH 03857 USA
27  *   <info@isc.org>
28  *   https://www.isc.org/
29  */
30 
31 #include <sys/cdefs.h>
32 __RCSID("$NetBSD: lpf.c,v 1.4 2022/04/03 01:10:58 christos Exp $");
33 
34 #include "dhcpd.h"
35 #if defined (USE_LPF_SEND) || defined (USE_LPF_RECEIVE)
36 #include <sys/uio.h>
37 #include <errno.h>
38 
39 #include <asm/types.h>
40 #include <linux/filter.h>
41 #include <linux/if_ether.h>
42 #include <linux/if_packet.h>
43 #include <netinet/in_systm.h>
44 #include "includes/netinet/ip.h"
45 #include "includes/netinet/udp.h"
46 #include "includes/netinet/if_ether.h"
47 #endif
48 
49 #if defined (USE_LPF_RECEIVE) || defined (USE_LPF_HWADDR)
50 #include <sys/ioctl.h>
51 #include <sys/socket.h>
52 #include <net/if.h>
53 #endif
54 
55 #if defined (USE_LPF_SEND) || defined (USE_LPF_RECEIVE)
56 /* Reinitializes the specified interface after an address change.   This
57    is not required for packet-filter APIs. */
58 
59 #ifdef USE_LPF_SEND
if_reinitialize_send(info)60 void if_reinitialize_send (info)
61 	struct interface_info *info;
62 {
63 }
64 #endif
65 
66 #ifdef USE_LPF_RECEIVE
if_reinitialize_receive(info)67 void if_reinitialize_receive (info)
68 	struct interface_info *info;
69 {
70 }
71 #endif
72 
73 /* Called by get_interface_list for each interface that's discovered.
74    Opens a packet filter for each interface and adds it to the select
75    mask. */
76 
if_register_lpf(info)77 int if_register_lpf (info)
78 	struct interface_info *info;
79 {
80 	int sock;
81 	union {
82 		struct sockaddr_ll ll;
83 		struct sockaddr common;
84 		} sa;
85 	struct ifreq ifr;
86 
87 	/* Make an LPF socket. */
88 	if ((sock = socket(PF_PACKET, SOCK_RAW,
89 			   htons((short)ETH_P_ALL))) < 0) {
90 		if (errno == ENOPROTOOPT || errno == EPROTONOSUPPORT ||
91 		    errno == ESOCKTNOSUPPORT || errno == EPFNOSUPPORT ||
92 		    errno == EAFNOSUPPORT || errno == EINVAL) {
93 			log_error ("socket: %m - make sure");
94 			log_error ("CONFIG_PACKET (Packet socket) %s",
95 				   "and CONFIG_FILTER");
96 			log_error ("(Socket Filtering) are enabled %s",
97 				   "in your kernel");
98 			log_fatal ("configuration!");
99 		}
100 		log_fatal ("Open a socket for LPF: %m");
101 	}
102 
103 	memset (&ifr, 0, sizeof ifr);
104 	strncpy (ifr.ifr_name, (const char *)info -> ifp, sizeof ifr.ifr_name);
105 	ifr.ifr_name[IFNAMSIZ-1] = '\0';
106 	if (ioctl (sock, SIOCGIFINDEX, &ifr))
107 		log_fatal ("Failed to get interface index: %m");
108 
109 	/* Bind to the interface name */
110 	memset (&sa, 0, sizeof sa);
111 	sa.ll.sll_family = AF_PACKET;
112 	sa.ll.sll_ifindex = ifr.ifr_ifindex;
113 	if (bind (sock, &sa.common, sizeof sa)) {
114 		if (errno == ENOPROTOOPT || errno == EPROTONOSUPPORT ||
115 		    errno == ESOCKTNOSUPPORT || errno == EPFNOSUPPORT ||
116 		    errno == EAFNOSUPPORT || errno == EINVAL) {
117 			log_error ("socket: %m - make sure");
118 			log_error ("CONFIG_PACKET (Packet socket) %s",
119 				   "and CONFIG_FILTER");
120 			log_error ("(Socket Filtering) are enabled %s",
121 				   "in your kernel");
122 			log_fatal ("configuration!");
123 		}
124 		log_fatal ("Bind socket to interface: %m");
125 
126 	}
127 
128 	get_hw_addr(info->name, &info->hw_address);
129 
130 	return sock;
131 }
132 #endif /* USE_LPF_SEND || USE_LPF_RECEIVE */
133 
134 #ifdef USE_LPF_SEND
if_register_send(info)135 void if_register_send (info)
136 	struct interface_info *info;
137 {
138 	/* If we're using the lpf API for sending and receiving,
139 	   we don't need to register this interface twice. */
140 #ifndef USE_LPF_RECEIVE
141 	info -> wfdesc = if_register_lpf (info);
142 #else
143 	info -> wfdesc = info -> rfdesc;
144 #endif
145 	if (!quiet_interface_discovery)
146 		log_info ("Sending on   LPF/%s/%s%s%s",
147 		      info -> name,
148 		      print_hw_addr (info -> hw_address.hbuf [0],
149 				     info -> hw_address.hlen - 1,
150 				     &info -> hw_address.hbuf [1]),
151 		      (info -> shared_network ? "/" : ""),
152 		      (info -> shared_network ?
153 		       info -> shared_network -> name : ""));
154 }
155 
if_deregister_send(info)156 void if_deregister_send (info)
157 	struct interface_info *info;
158 {
159 	/* don't need to close twice if we are using lpf for sending and
160 	   receiving */
161 #ifndef USE_LPF_RECEIVE
162 	/* for LPF this is simple, packet filters are removed when sockets
163 	   are closed */
164 	close (info -> wfdesc);
165 #endif
166 	info -> wfdesc = -1;
167 	if (!quiet_interface_discovery)
168 		log_info ("Disabling output on LPF/%s/%s%s%s",
169 		      info -> name,
170 		      print_hw_addr (info -> hw_address.hbuf [0],
171 				     info -> hw_address.hlen - 1,
172 				     &info -> hw_address.hbuf [1]),
173 		      (info -> shared_network ? "/" : ""),
174 		      (info -> shared_network ?
175 		       info -> shared_network -> name : ""));
176 }
177 #endif /* USE_LPF_SEND */
178 
179 #ifdef USE_LPF_RECEIVE
180 /* Defined in bpf.c.   We can't extern these in dhcpd.h without pulling
181    in bpf includes... */
182 extern struct sock_filter dhcp_bpf_filter [];
183 extern int dhcp_bpf_filter_len;
184 
185 #if defined(RELAY_PORT)
186 extern struct sock_filter dhcp_bpf_relay_filter [];
187 extern int dhcp_bpf_relay_filter_len;
188 #endif
189 
190 #if defined (HAVE_TR_SUPPORT)
191 extern struct sock_filter dhcp_bpf_tr_filter [];
192 extern int dhcp_bpf_tr_filter_len;
193 static void lpf_tr_filter_setup (struct interface_info *);
194 #endif
195 
196 static void lpf_gen_filter_setup (struct interface_info *);
197 
if_register_receive(info)198 void if_register_receive (info)
199 	struct interface_info *info;
200 {
201 	/* Open a LPF device and hang it on this interface... */
202 	info -> rfdesc = if_register_lpf (info);
203 
204 #ifdef PACKET_AUXDATA
205 	{
206 	int val = 1;
207 
208 	if (setsockopt(info->rfdesc, SOL_PACKET, PACKET_AUXDATA,
209 		       &val, sizeof(val)) < 0) {
210 		if (errno != ENOPROTOOPT) {
211 			log_fatal ("Failed to set auxiliary packet data: %m");
212 		}
213 	}
214 	}
215 #endif
216 
217 
218 #if defined (HAVE_TR_SUPPORT)
219 	if (info -> hw_address.hbuf [0] == HTYPE_IEEE802)
220 		lpf_tr_filter_setup (info);
221 	else
222 #endif
223 		lpf_gen_filter_setup (info);
224 
225 	if (!quiet_interface_discovery)
226 		log_info ("Listening on LPF/%s/%s%s%s",
227 			  info -> name,
228 			  print_hw_addr (info -> hw_address.hbuf [0],
229 					 info -> hw_address.hlen - 1,
230 					 &info -> hw_address.hbuf [1]),
231 			  (info -> shared_network ? "/" : ""),
232 			  (info -> shared_network ?
233 			   info -> shared_network -> name : ""));
234 }
235 
if_deregister_receive(info)236 void if_deregister_receive (info)
237 	struct interface_info *info;
238 {
239 	/* for LPF this is simple, packet filters are removed when sockets
240 	   are closed */
241 	close (info -> rfdesc);
242 	info -> rfdesc = -1;
243 	if (!quiet_interface_discovery)
244 		log_info ("Disabling input on LPF/%s/%s%s%s",
245 			  info -> name,
246 			  print_hw_addr (info -> hw_address.hbuf [0],
247 					 info -> hw_address.hlen - 1,
248 					 &info -> hw_address.hbuf [1]),
249 			  (info -> shared_network ? "/" : ""),
250 			  (info -> shared_network ?
251 			   info -> shared_network -> name : ""));
252 }
253 
lpf_gen_filter_setup(info)254 static void lpf_gen_filter_setup (info)
255 	struct interface_info *info;
256 {
257 	struct sock_fprog p;
258 
259 	memset(&p, 0, sizeof(p));
260 
261 	/* Set up the bpf filter program structure.    This is defined in
262 	   bpf.c */
263 	p.len = dhcp_bpf_filter_len;
264 	p.filter = dhcp_bpf_filter;
265 
266         /* Patch the server port into the LPF  program...
267 	   XXX changes to filter program may require changes
268 	   to the insn number(s) used below! XXX */
269 #if defined(RELAY_PORT)
270 	if (relay_port) {
271 		/*
272 		 * If user defined relay UDP port, we need to filter
273 		 * also on the user UDP port.
274 		 */
275 		p.len = dhcp_bpf_relay_filter_len;
276 		p.filter = dhcp_bpf_relay_filter;
277 
278 		dhcp_bpf_relay_filter [10].k = ntohs (relay_port);
279 	}
280 #endif
281 	dhcp_bpf_filter [8].k = ntohs (*libdhcp_callbacks.local_port);
282 
283 	if (setsockopt (info -> rfdesc, SOL_SOCKET, SO_ATTACH_FILTER, &p,
284 			sizeof p) < 0) {
285 		if (errno == ENOPROTOOPT || errno == EPROTONOSUPPORT ||
286 		    errno == ESOCKTNOSUPPORT || errno == EPFNOSUPPORT ||
287 		    errno == EAFNOSUPPORT) {
288 			log_error ("socket: %m - make sure");
289 			log_error ("CONFIG_PACKET (Packet socket) %s",
290 				   "and CONFIG_FILTER");
291 			log_error ("(Socket Filtering) are enabled %s",
292 				   "in your kernel");
293 			log_fatal ("configuration!");
294 		}
295 		log_fatal ("Can't install packet filter program: %m");
296 	}
297 }
298 
299 #if defined (HAVE_TR_SUPPORT)
lpf_tr_filter_setup(info)300 static void lpf_tr_filter_setup (info)
301 	struct interface_info *info;
302 {
303 	struct sock_fprog p;
304 
305 	memset(&p, 0, sizeof(p));
306 
307 	/* Set up the bpf filter program structure.    This is defined in
308 	   bpf.c */
309 	p.len = dhcp_bpf_tr_filter_len;
310 	p.filter = dhcp_bpf_tr_filter;
311 
312         /* Patch the server port into the LPF  program...
313 	   XXX changes to filter program may require changes
314 	   XXX to the insn number(s) used below!
315 	   XXX Token ring filter is null - when/if we have a filter
316 	   XXX that's not, we'll need this code.
317 	   XXX dhcp_bpf_filter [?].k = ntohs (*libdhcp_callbacks.local_port); */
318 
319 	if (setsockopt (info -> rfdesc, SOL_SOCKET, SO_ATTACH_FILTER, &p,
320 			sizeof p) < 0) {
321 		if (errno == ENOPROTOOPT || errno == EPROTONOSUPPORT ||
322 		    errno == ESOCKTNOSUPPORT || errno == EPFNOSUPPORT ||
323 		    errno == EAFNOSUPPORT) {
324 			log_error ("socket: %m - make sure");
325 			log_error ("CONFIG_PACKET (Packet socket) %s",
326 				   "and CONFIG_FILTER");
327 			log_error ("(Socket Filtering) are enabled %s",
328 				   "in your kernel");
329 			log_fatal ("configuration!");
330 		}
331 		log_fatal ("Can't install packet filter program: %m");
332 	}
333 }
334 #endif /* HAVE_TR_SUPPORT */
335 #endif /* USE_LPF_RECEIVE */
336 
337 #ifdef USE_LPF_SEND
send_packet(interface,packet,raw,len,from,to,hto)338 ssize_t send_packet (interface, packet, raw, len, from, to, hto)
339 	struct interface_info *interface;
340 	struct packet *packet;
341 	struct dhcp_packet *raw;
342 	size_t len;
343 	struct in_addr from;
344 	struct sockaddr_in *to;
345 	struct hardware *hto;
346 {
347 	unsigned hbufp = 0, ibufp = 0;
348 	double hh [16];
349 	double ih [1536 / sizeof (double)];
350 	unsigned char *buf = (unsigned char *)ih;
351 	int result;
352 	int fudge;
353 
354 	if (!strcmp (interface -> name, "fallback"))
355 		return send_fallback (interface, packet, raw,
356 				      len, from, to, hto);
357 
358 	if (hto == NULL && interface->anycast_mac_addr.hlen)
359 		hto = &interface->anycast_mac_addr;
360 
361 	/* Assemble the headers... */
362 	assemble_hw_header (interface, (unsigned char *)hh, &hbufp, hto);
363 	fudge = hbufp % 4;	/* IP header must be word-aligned. */
364 	memcpy (buf + fudge, (unsigned char *)hh, hbufp);
365 	ibufp = hbufp + fudge;
366 	assemble_udp_ip_header (interface, buf, &ibufp, from.s_addr,
367 				to -> sin_addr.s_addr, to -> sin_port,
368 				(unsigned char *)raw, len);
369 	memcpy (buf + ibufp, raw, len);
370 	result = write(interface->wfdesc, buf + fudge, ibufp + len - fudge);
371 	if (result < 0)
372 		log_error ("send_packet: %m");
373 	return result;
374 }
375 #endif /* USE_LPF_SEND */
376 
377 #ifdef USE_LPF_RECEIVE
receive_packet(interface,buf,len,from,hfrom)378 ssize_t receive_packet (interface, buf, len, from, hfrom)
379 	struct interface_info *interface;
380 	unsigned char *buf;
381 	size_t len;
382 	struct sockaddr_in *from;
383 	struct hardware *hfrom;
384 {
385 	int length = 0;
386 	int offset = 0;
387 	int csum_ready = 1;
388 	unsigned char ibuf [1536];
389 	unsigned bufix = 0;
390 	unsigned paylen;
391 	struct iovec iov = {
392 		.iov_base = ibuf,
393 		.iov_len = sizeof ibuf,
394 	};
395 #ifdef PACKET_AUXDATA
396 	/*
397 	 * We only need cmsgbuf if we are getting the aux data and we
398 	 * only get the auxdata if it is actually defined
399 	 */
400 	unsigned char cmsgbuf[CMSG_LEN(sizeof(struct tpacket_auxdata))];
401 	struct msghdr msg = {
402 		.msg_iov = &iov,
403 		.msg_iovlen = 1,
404 		.msg_control = cmsgbuf,
405 		.msg_controllen = sizeof(cmsgbuf),
406 	};
407 #else
408 	struct msghdr msg = {
409 		.msg_iov = &iov,
410 		.msg_iovlen = 1,
411 		.msg_control = NULL,
412 		.msg_controllen = 0,
413 	};
414 #endif /* PACKET_AUXDATA */
415 
416 	length = recvmsg (interface->rfdesc, &msg, 0);
417 	if (length <= 0)
418 		return length;
419 
420 #ifdef PACKET_AUXDATA
421 	{
422 	/*  Use auxiliary packet data to:
423 	 *
424 	 *  a. Weed out extraneous VLAN-tagged packets - If the NIC driver is
425 	 *  handling VLAN encapsulation (i.e. stripping/adding VLAN tags),
426 	 *  then an inbound VLAN packet will be seen twice: Once by
427 	 *  the parent interface (e.g. eth0) with a VLAN tag != 0; and once
428 	 *  by the vlan interface (e.g. eth0.n) with a VLAN tag of 0 (i.e none).
429 	 *  We want to discard the packet sent to the parent and thus respond
430 	 *  only over the vlan interface.  (Drivers for Intel PRO/1000 series
431 	 *  NICs perform VLAN encapsulation, while drivers for PCnet series
432 	 *  do not, for example. The linux kernel makes stripped vlan info
433 	 *  visible to user space via CMSG/auxdata, this appears to not be
434 	 *  true for BSD OSs.).  NOTE: this is only supported on linux flavors
435 	 *  which define the tpacket_auxdata.tp_vlan_tci.
436 	 *
437 	 *  b. Determine if checksum is valid for use. It may not be if
438 	 *  checksum offloading is enabled on the interface.  */
439 	struct cmsghdr *cmsg;
440 
441 	for (cmsg = CMSG_FIRSTHDR(&msg); cmsg; cmsg = CMSG_NXTHDR(&msg, cmsg)) {
442 		if (cmsg->cmsg_level == SOL_PACKET &&
443 		    cmsg->cmsg_type == PACKET_AUXDATA) {
444 			struct tpacket_auxdata *aux = (void *)CMSG_DATA(cmsg);
445 #ifdef VLAN_TCI_PRESENT
446 			/* Discard packets with stripped vlan id */
447 			/* VLAN ID is only bottom 12-bits of TCI */
448 			if (aux->tp_vlan_tci & 0x0fff)
449 				return 0;
450 #endif
451 
452 			csum_ready = ((aux->tp_status & TP_STATUS_CSUMNOTREADY)
453 				      ? 0 : 1);
454 		}
455 	}
456 
457 	}
458 #endif /* PACKET_AUXDATA */
459 
460 	bufix = 0;
461 	/* Decode the physical header... */
462 	offset = decode_hw_header (interface, ibuf, bufix, hfrom);
463 
464 	/* If a physical layer checksum failed (dunno of any
465 	   physical layer that supports this, but WTH), skip this
466 	   packet. */
467 	if (offset < 0) {
468 		return 0;
469 	}
470 
471 	bufix += offset;
472 	length -= offset;
473 
474 	/* Decode the IP and UDP headers... */
475 	offset = decode_udp_ip_header (interface, ibuf, bufix, from,
476 				       (unsigned)length, &paylen, csum_ready);
477 
478 	/* If the IP or UDP checksum was bad, skip the packet... */
479 	if (offset < 0)
480 		return 0;
481 
482 	bufix += offset;
483 	length -= offset;
484 
485 	if (length < paylen)
486 		log_fatal("Internal inconsistency at %s:%d.", MDL);
487 
488 	/* Copy out the data in the packet... */
489 	memcpy(buf, &ibuf[bufix], paylen);
490 	return paylen;
491 }
492 
can_unicast_without_arp(ip)493 int can_unicast_without_arp (ip)
494 	struct interface_info *ip;
495 {
496 	return 1;
497 }
498 
can_receive_unicast_unconfigured(ip)499 int can_receive_unicast_unconfigured (ip)
500 	struct interface_info *ip;
501 {
502 	return 1;
503 }
504 
supports_multiple_interfaces(ip)505 int supports_multiple_interfaces (ip)
506 	struct interface_info *ip;
507 {
508 	return 1;
509 }
510 
maybe_setup_fallback()511 void maybe_setup_fallback ()
512 {
513 	isc_result_t status;
514 	struct interface_info *fbi = (struct interface_info *)0;
515 	if (setup_fallback (&fbi, MDL)) {
516 		if_register_fallback (fbi);
517 		status = omapi_register_io_object ((omapi_object_t *)fbi,
518 						   if_readsocket, 0,
519 						   fallback_discard, 0, 0);
520 		if (status != ISC_R_SUCCESS)
521 			log_fatal ("Can't register I/O handle for \"%s\": %s",
522 				   fbi -> name, isc_result_totext (status));
523 		interface_dereference (&fbi, MDL);
524 	}
525 }
526 #endif
527 
528 #if defined (USE_LPF_RECEIVE) || defined (USE_LPF_HWADDR)
529 void
get_hw_addr(const char * name,struct hardware * hw)530 get_hw_addr(const char *name, struct hardware *hw) {
531 	int sock;
532 	struct ifreq tmp;
533 	struct sockaddr *sa;
534 
535 	if (strlen(name) >= sizeof(tmp.ifr_name)) {
536 		log_fatal("Device name too long: \"%s\"", name);
537 	}
538 
539 	sock = socket(AF_INET, SOCK_DGRAM, 0);
540 	if (sock < 0) {
541 		log_fatal("Can't create socket for \"%s\": %m", name);
542 	}
543 
544 	memset(&tmp, 0, sizeof(tmp));
545 	strcpy(tmp.ifr_name, name);
546 	if (ioctl(sock, SIOCGIFHWADDR, &tmp) < 0) {
547 		log_fatal("Error getting hardware address for \"%s\": %m",
548 			  name);
549 	}
550 
551 	sa = &tmp.ifr_hwaddr;
552 	switch (sa->sa_family) {
553 		case ARPHRD_ETHER:
554 			hw->hlen = 7;
555 			hw->hbuf[0] = HTYPE_ETHER;
556 			memcpy(&hw->hbuf[1], sa->sa_data, 6);
557 			break;
558 		case ARPHRD_IEEE802:
559 #ifdef ARPHRD_IEEE802_TR
560 		case ARPHRD_IEEE802_TR:
561 #endif /* ARPHRD_IEEE802_TR */
562 			hw->hlen = 7;
563 			hw->hbuf[0] = HTYPE_IEEE802;
564 			memcpy(&hw->hbuf[1], sa->sa_data, 6);
565 			break;
566 		case ARPHRD_FDDI:
567 			hw->hlen = 7;
568 			hw->hbuf[0] = HTYPE_FDDI;
569 			memcpy(&hw->hbuf[1], sa->sa_data, 6);
570 			break;
571 		default:
572 			log_fatal("Unsupported device type %ld for \"%s\"",
573 				  (long int)sa->sa_family, name);
574 	}
575 
576 	close(sock);
577 }
578 #endif
579