xref: /minix/external/bsd/dhcp/dist/common/lpf.c (revision fb9c64b2)
1 /*	$NetBSD: lpf.c,v 1.1.1.4 2014/07/12 11:57:44 spz Exp $	*/
2 /* lpf.c
3 
4    Linux packet filter code, contributed by Brian Murrel at Interlinx
5    Support Services in Vancouver, B.C. */
6 
7 /*
8  * Copyright (c) 2009,2012 by Internet Systems Consortium, Inc. ("ISC")
9  * Copyright (c) 2004,2007 by Internet Systems Consortium, Inc. ("ISC")
10  * Copyright (c) 1996-2003 by Internet Software Consortium
11  *
12  * Permission to use, copy, modify, and distribute this software for any
13  * purpose with or without fee is hereby granted, provided that the above
14  * copyright notice and this permission notice appear in all copies.
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  *   950 Charter Street
26  *   Redwood City, CA 94063
27  *   <info@isc.org>
28  *   https://www.isc.org/
29  */
30 
31 #include <sys/cdefs.h>
32 __RCSID("$NetBSD: lpf.c,v 1.1.1.4 2014/07/12 11:57:44 spz 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 <netinet/in_systm.h>
43 #include <net/if_packet.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 <net/if.h>
52 #endif
53 
54 #if defined (USE_LPF_SEND) || defined (USE_LPF_RECEIVE)
55 /* Reinitializes the specified interface after an address change.   This
56    is not required for packet-filter APIs. */
57 
58 #ifdef USE_LPF_SEND
59 void if_reinitialize_send (info)
60 	struct interface_info *info;
61 {
62 }
63 #endif
64 
65 #ifdef USE_LPF_RECEIVE
66 void if_reinitialize_receive (info)
67 	struct interface_info *info;
68 {
69 }
70 #endif
71 
72 /* Called by get_interface_list for each interface that's discovered.
73    Opens a packet filter for each interface and adds it to the select
74    mask. */
75 
76 int if_register_lpf (info)
77 	struct interface_info *info;
78 {
79 	int sock;
80 	struct sockaddr sa;
81 
82 	/* Make an LPF socket. */
83 	if ((sock = socket(PF_PACKET, SOCK_PACKET,
84 			   htons((short)ETH_P_ALL))) < 0) {
85 		if (errno == ENOPROTOOPT || errno == EPROTONOSUPPORT ||
86 		    errno == ESOCKTNOSUPPORT || errno == EPFNOSUPPORT ||
87 		    errno == EAFNOSUPPORT || errno == EINVAL) {
88 			log_error ("socket: %m - make sure");
89 			log_error ("CONFIG_PACKET (Packet socket) %s",
90 				   "and CONFIG_FILTER");
91 			log_error ("(Socket Filtering) are enabled %s",
92 				   "in your kernel");
93 			log_fatal ("configuration!");
94 		}
95 		log_fatal ("Open a socket for LPF: %m");
96 	}
97 
98 	/* Bind to the interface name */
99 	memset (&sa, 0, sizeof sa);
100 	sa.sa_family = AF_PACKET;
101 	strncpy (sa.sa_data, (const char *)info -> ifp, sizeof sa.sa_data);
102 	if (bind (sock, &sa, sizeof sa)) {
103 		if (errno == ENOPROTOOPT || errno == EPROTONOSUPPORT ||
104 		    errno == ESOCKTNOSUPPORT || errno == EPFNOSUPPORT ||
105 		    errno == EAFNOSUPPORT || errno == EINVAL) {
106 			log_error ("socket: %m - make sure");
107 			log_error ("CONFIG_PACKET (Packet socket) %s",
108 				   "and CONFIG_FILTER");
109 			log_error ("(Socket Filtering) are enabled %s",
110 				   "in your kernel");
111 			log_fatal ("configuration!");
112 		}
113 		log_fatal ("Bind socket to interface: %m");
114 	}
115 
116 	get_hw_addr(info->name, &info->hw_address);
117 
118 	return sock;
119 }
120 #endif /* USE_LPF_SEND || USE_LPF_RECEIVE */
121 
122 #ifdef USE_LPF_SEND
123 void if_register_send (info)
124 	struct interface_info *info;
125 {
126 	/* If we're using the lpf API for sending and receiving,
127 	   we don't need to register this interface twice. */
128 #ifndef USE_LPF_RECEIVE
129 	info -> wfdesc = if_register_lpf (info);
130 #else
131 	info -> wfdesc = info -> rfdesc;
132 #endif
133 	if (!quiet_interface_discovery)
134 		log_info ("Sending on   LPF/%s/%s%s%s",
135 		      info -> name,
136 		      print_hw_addr (info -> hw_address.hbuf [0],
137 				     info -> hw_address.hlen - 1,
138 				     &info -> hw_address.hbuf [1]),
139 		      (info -> shared_network ? "/" : ""),
140 		      (info -> shared_network ?
141 		       info -> shared_network -> name : ""));
142 }
143 
144 void if_deregister_send (info)
145 	struct interface_info *info;
146 {
147 	/* don't need to close twice if we are using lpf for sending and
148 	   receiving */
149 #ifndef USE_LPF_RECEIVE
150 	/* for LPF this is simple, packet filters are removed when sockets
151 	   are closed */
152 	close (info -> wfdesc);
153 #endif
154 	info -> wfdesc = -1;
155 	if (!quiet_interface_discovery)
156 		log_info ("Disabling output on LPF/%s/%s%s%s",
157 		      info -> name,
158 		      print_hw_addr (info -> hw_address.hbuf [0],
159 				     info -> hw_address.hlen - 1,
160 				     &info -> hw_address.hbuf [1]),
161 		      (info -> shared_network ? "/" : ""),
162 		      (info -> shared_network ?
163 		       info -> shared_network -> name : ""));
164 }
165 #endif /* USE_LPF_SEND */
166 
167 #ifdef USE_LPF_RECEIVE
168 /* Defined in bpf.c.   We can't extern these in dhcpd.h without pulling
169    in bpf includes... */
170 extern struct sock_filter dhcp_bpf_filter [];
171 extern int dhcp_bpf_filter_len;
172 
173 #if defined (HAVE_TR_SUPPORT)
174 extern struct sock_filter dhcp_bpf_tr_filter [];
175 extern int dhcp_bpf_tr_filter_len;
176 static void lpf_tr_filter_setup (struct interface_info *);
177 #endif
178 
179 static void lpf_gen_filter_setup (struct interface_info *);
180 
181 void if_register_receive (info)
182 	struct interface_info *info;
183 {
184 	/* Open a LPF device and hang it on this interface... */
185 	info -> rfdesc = if_register_lpf (info);
186 
187 #if defined (HAVE_TR_SUPPORT)
188 	if (info -> hw_address.hbuf [0] == HTYPE_IEEE802)
189 		lpf_tr_filter_setup (info);
190 	else
191 #endif
192 		lpf_gen_filter_setup (info);
193 
194 	if (!quiet_interface_discovery)
195 		log_info ("Listening on LPF/%s/%s%s%s",
196 			  info -> name,
197 			  print_hw_addr (info -> hw_address.hbuf [0],
198 					 info -> hw_address.hlen - 1,
199 					 &info -> hw_address.hbuf [1]),
200 			  (info -> shared_network ? "/" : ""),
201 			  (info -> shared_network ?
202 			   info -> shared_network -> name : ""));
203 }
204 
205 void if_deregister_receive (info)
206 	struct interface_info *info;
207 {
208 	/* for LPF this is simple, packet filters are removed when sockets
209 	   are closed */
210 	close (info -> rfdesc);
211 	info -> rfdesc = -1;
212 	if (!quiet_interface_discovery)
213 		log_info ("Disabling input on LPF/%s/%s%s%s",
214 			  info -> name,
215 			  print_hw_addr (info -> hw_address.hbuf [0],
216 					 info -> hw_address.hlen - 1,
217 					 &info -> hw_address.hbuf [1]),
218 			  (info -> shared_network ? "/" : ""),
219 			  (info -> shared_network ?
220 			   info -> shared_network -> name : ""));
221 }
222 
223 static void lpf_gen_filter_setup (info)
224 	struct interface_info *info;
225 {
226 	struct sock_fprog p;
227 
228 	memset(&p, 0, sizeof(p));
229 
230 	/* Set up the bpf filter program structure.    This is defined in
231 	   bpf.c */
232 	p.len = dhcp_bpf_filter_len;
233 	p.filter = dhcp_bpf_filter;
234 
235         /* Patch the server port into the LPF  program...
236 	   XXX changes to filter program may require changes
237 	   to the insn number(s) used below! XXX */
238 	dhcp_bpf_filter [8].k = ntohs ((short)local_port);
239 
240 	if (setsockopt (info -> rfdesc, SOL_SOCKET, SO_ATTACH_FILTER, &p,
241 			sizeof p) < 0) {
242 		if (errno == ENOPROTOOPT || errno == EPROTONOSUPPORT ||
243 		    errno == ESOCKTNOSUPPORT || errno == EPFNOSUPPORT ||
244 		    errno == EAFNOSUPPORT) {
245 			log_error ("socket: %m - make sure");
246 			log_error ("CONFIG_PACKET (Packet socket) %s",
247 				   "and CONFIG_FILTER");
248 			log_error ("(Socket Filtering) are enabled %s",
249 				   "in your kernel");
250 			log_fatal ("configuration!");
251 		}
252 		log_fatal ("Can't install packet filter program: %m");
253 	}
254 }
255 
256 #if defined (HAVE_TR_SUPPORT)
257 static void lpf_tr_filter_setup (info)
258 	struct interface_info *info;
259 {
260 	struct sock_fprog p;
261 
262 	memset(&p, 0, sizeof(p));
263 
264 	/* Set up the bpf filter program structure.    This is defined in
265 	   bpf.c */
266 	p.len = dhcp_bpf_tr_filter_len;
267 	p.filter = dhcp_bpf_tr_filter;
268 
269         /* Patch the server port into the LPF  program...
270 	   XXX changes to filter program may require changes
271 	   XXX to the insn number(s) used below!
272 	   XXX Token ring filter is null - when/if we have a filter
273 	   XXX that's not, we'll need this code.
274 	   XXX dhcp_bpf_filter [?].k = ntohs (local_port); */
275 
276 	if (setsockopt (info -> rfdesc, SOL_SOCKET, SO_ATTACH_FILTER, &p,
277 			sizeof p) < 0) {
278 		if (errno == ENOPROTOOPT || errno == EPROTONOSUPPORT ||
279 		    errno == ESOCKTNOSUPPORT || errno == EPFNOSUPPORT ||
280 		    errno == EAFNOSUPPORT) {
281 			log_error ("socket: %m - make sure");
282 			log_error ("CONFIG_PACKET (Packet socket) %s",
283 				   "and CONFIG_FILTER");
284 			log_error ("(Socket Filtering) are enabled %s",
285 				   "in your kernel");
286 			log_fatal ("configuration!");
287 		}
288 		log_fatal ("Can't install packet filter program: %m");
289 	}
290 }
291 #endif /* HAVE_TR_SUPPORT */
292 #endif /* USE_LPF_RECEIVE */
293 
294 #ifdef USE_LPF_SEND
295 ssize_t send_packet (interface, packet, raw, len, from, to, hto)
296 	struct interface_info *interface;
297 	struct packet *packet;
298 	struct dhcp_packet *raw;
299 	size_t len;
300 	struct in_addr from;
301 	struct sockaddr_in *to;
302 	struct hardware *hto;
303 {
304 	unsigned hbufp = 0, ibufp = 0;
305 	double hh [16];
306 	double ih [1536 / sizeof (double)];
307 	unsigned char *buf = (unsigned char *)ih;
308 	struct sockaddr_pkt sa;
309 	int result;
310 	int fudge;
311 
312 	if (!strcmp (interface -> name, "fallback"))
313 		return send_fallback (interface, packet, raw,
314 				      len, from, to, hto);
315 
316 	if (hto == NULL && interface->anycast_mac_addr.hlen)
317 		hto = &interface->anycast_mac_addr;
318 
319 	/* Assemble the headers... */
320 	assemble_hw_header (interface, (unsigned char *)hh, &hbufp, hto);
321 	fudge = hbufp % 4;	/* IP header must be word-aligned. */
322 	memcpy (buf + fudge, (unsigned char *)hh, hbufp);
323 	ibufp = hbufp + fudge;
324 	assemble_udp_ip_header (interface, buf, &ibufp, from.s_addr,
325 				to -> sin_addr.s_addr, to -> sin_port,
326 				(unsigned char *)raw, len);
327 	memcpy (buf + ibufp, raw, len);
328 
329 	/* For some reason, SOCK_PACKET sockets can't be connected,
330 	   so we have to do a sentdo every time. */
331 	memset (&sa, 0, sizeof sa);
332 	sa.spkt_family = AF_PACKET;
333 	strncpy ((char *)sa.spkt_device,
334 		 (const char *)interface -> ifp, sizeof sa.spkt_device);
335 	sa.spkt_protocol = htons(ETH_P_IP);
336 
337 	result = sendto (interface -> wfdesc,
338 			 buf + fudge, ibufp + len - fudge, 0,
339 			 (const struct sockaddr *)&sa, sizeof sa);
340 	if (result < 0)
341 		log_error ("send_packet: %m");
342 	return result;
343 }
344 #endif /* USE_LPF_SEND */
345 
346 #ifdef USE_LPF_RECEIVE
347 ssize_t receive_packet (interface, buf, len, from, hfrom)
348 	struct interface_info *interface;
349 	unsigned char *buf;
350 	size_t len;
351 	struct sockaddr_in *from;
352 	struct hardware *hfrom;
353 {
354 	int length = 0;
355 	int offset = 0;
356 	unsigned char ibuf [1536];
357 	unsigned bufix = 0;
358 	unsigned paylen;
359 
360 	length = read (interface -> rfdesc, ibuf, sizeof ibuf);
361 	if (length <= 0)
362 		return length;
363 
364 	bufix = 0;
365 	/* Decode the physical header... */
366 	offset = decode_hw_header (interface, ibuf, bufix, hfrom);
367 
368 	/* If a physical layer checksum failed (dunno of any
369 	   physical layer that supports this, but WTH), skip this
370 	   packet. */
371 	if (offset < 0) {
372 		return 0;
373 	}
374 
375 	bufix += offset;
376 	length -= offset;
377 
378 	/* Decode the IP and UDP headers... */
379 	offset = decode_udp_ip_header (interface, ibuf, bufix, from,
380 				       (unsigned)length, &paylen);
381 
382 	/* If the IP or UDP checksum was bad, skip the packet... */
383 	if (offset < 0)
384 		return 0;
385 
386 	bufix += offset;
387 	length -= offset;
388 
389 	if (length < paylen)
390 		log_fatal("Internal inconsistency at %s:%d.", MDL);
391 
392 	/* Copy out the data in the packet... */
393 	memcpy(buf, &ibuf[bufix], paylen);
394 	return paylen;
395 }
396 
397 int can_unicast_without_arp (ip)
398 	struct interface_info *ip;
399 {
400 	return 1;
401 }
402 
403 int can_receive_unicast_unconfigured (ip)
404 	struct interface_info *ip;
405 {
406 	return 1;
407 }
408 
409 int supports_multiple_interfaces (ip)
410 	struct interface_info *ip;
411 {
412 	return 1;
413 }
414 
415 void maybe_setup_fallback ()
416 {
417 	isc_result_t status;
418 	struct interface_info *fbi = (struct interface_info *)0;
419 	if (setup_fallback (&fbi, MDL)) {
420 		if_register_fallback (fbi);
421 		status = omapi_register_io_object ((omapi_object_t *)fbi,
422 						   if_readsocket, 0,
423 						   fallback_discard, 0, 0);
424 		if (status != ISC_R_SUCCESS)
425 			log_fatal ("Can't register I/O handle for \"%s\": %s",
426 				   fbi -> name, isc_result_totext (status));
427 		interface_dereference (&fbi, MDL);
428 	}
429 }
430 #endif
431 
432 #if defined (USE_LPF_RECEIVE) || defined (USE_LPF_HWADDR)
433 void
434 get_hw_addr(const char *name, struct hardware *hw) {
435 	int sock;
436 	struct ifreq tmp;
437 	struct sockaddr *sa;
438 
439 	if (strlen(name) >= sizeof(tmp.ifr_name)) {
440 		log_fatal("Device name too long: \"%s\"", name);
441 	}
442 
443 	sock = socket(AF_INET, SOCK_DGRAM, 0);
444 	if (sock < 0) {
445 		log_fatal("Can't create socket for \"%s\": %m", name);
446 	}
447 
448 	memset(&tmp, 0, sizeof(tmp));
449 	strcpy(tmp.ifr_name, name);
450 	if (ioctl(sock, SIOCGIFHWADDR, &tmp) < 0) {
451 		log_fatal("Error getting hardware address for \"%s\": %m",
452 			  name);
453 	}
454 
455 	sa = &tmp.ifr_hwaddr;
456 	switch (sa->sa_family) {
457 		case ARPHRD_ETHER:
458 			hw->hlen = 7;
459 			hw->hbuf[0] = HTYPE_ETHER;
460 			memcpy(&hw->hbuf[1], sa->sa_data, 6);
461 			break;
462 		case ARPHRD_IEEE802:
463 #ifdef ARPHRD_IEEE802_TR
464 		case ARPHRD_IEEE802_TR:
465 #endif /* ARPHRD_IEEE802_TR */
466 			hw->hlen = 7;
467 			hw->hbuf[0] = HTYPE_IEEE802;
468 			memcpy(&hw->hbuf[1], sa->sa_data, 6);
469 			break;
470 		case ARPHRD_FDDI:
471 			hw->hlen = 7;
472 			hw->hbuf[0] = HTYPE_FDDI;
473 			memcpy(&hw->hbuf[1], sa->sa_data, 6);
474 			break;
475 		default:
476 			log_fatal("Unsupported device type %ld for \"%s\"",
477 				  (long int)sa->sa_family, name);
478 	}
479 
480 	close(sock);
481 }
482 #endif
483