xref: /netbsd-src/external/mpl/dhcp/dist/common/upf.c (revision f407d9293b6650aa8c33d6a995f797bb6aaefd90)
1 /*	$NetBSD: upf.c,v 1.3 2022/04/03 01:10:58 christos Exp $	*/
2 
3 /* upf.c
4 
5    Ultrix PacketFilter interface code. */
6 
7 /*
8  * Copyright (C) 2004-2022 Internet Systems Consortium, Inc. ("ISC")
9  * Copyright (c) 1996-2003 by Internet Software Consortium
10  *
11  * This Source Code Form is subject to the terms of the Mozilla Public
12  * License, v. 2.0. If a copy of the MPL was not distributed with this
13  * file, You can obtain one at http://mozilla.org/MPL/2.0/.
14  *
15  * THE SOFTWARE IS PROVIDED "AS IS" AND ISC DISCLAIMS ALL WARRANTIES
16  * WITH REGARD TO THIS SOFTWARE INCLUDING ALL IMPLIED WARRANTIES OF
17  * MERCHANTABILITY AND FITNESS.  IN NO EVENT SHALL ISC BE LIABLE FOR
18  * ANY SPECIAL, DIRECT, INDIRECT, OR CONSEQUENTIAL DAMAGES OR ANY DAMAGES
19  * WHATSOEVER RESULTING FROM LOSS OF USE, DATA OR PROFITS, WHETHER IN AN
20  * ACTION OF CONTRACT, NEGLIGENCE OR OTHER TORTIOUS ACTION, ARISING OUT
21  * OF OR IN CONNECTION WITH THE USE OR PERFORMANCE OF THIS SOFTWARE.
22  *
23  *   Internet Systems Consortium, Inc.
24  *   PO Box 360
25  *   Newmarket, NH 03857 USA
26  *   <info@isc.org>
27  *   https://www.isc.org/
28  *
29  */
30 
31 #include <sys/cdefs.h>
32 __RCSID("$NetBSD: upf.c,v 1.3 2022/04/03 01:10:58 christos Exp $");
33 
34 #include "dhcpd.h"
35 #if defined (USE_UPF_SEND) || defined (USE_UPF_RECEIVE)
36 #include <sys/ioctl.h>
37 #include <sys/uio.h>
38 
39 #include <net/pfilt.h>
40 #include <netinet/in_systm.h>
41 #include "includes/netinet/ip.h"
42 #include "includes/netinet/udp.h"
43 #include "includes/netinet/if_ether.h"
44 
45 /* Reinitializes the specified interface after an address change.   This
46    is not required for packet-filter APIs. */
47 
48 #ifdef USE_UPF_SEND
if_reinitialize_send(info)49 void if_reinitialize_send (info)
50 	struct interface_info *info;
51 {
52 }
53 #endif
54 
55 #ifdef USE_UPF_RECEIVE
if_reinitialize_receive(info)56 void if_reinitialize_receive (info)
57 	struct interface_info *info;
58 {
59 }
60 #endif
61 
62 /* Called by get_interface_list for each interface that's discovered.
63    Opens a packet filter for each interface and adds it to the select
64    mask. */
65 
if_register_upf(info)66 int if_register_upf (info)
67 	struct interface_info *info;
68 {
69 	int sock;
70 	char filename[50];
71 	int b;
72 	struct endevp param;
73 
74 	/* Open a UPF device */
75 	for (b = 0; 1; b++) {
76 		/* %Audit% Cannot exceed 36 bytes. %2004.06.17,Safe% */
77 		sprintf(filename, "/dev/pf/pfilt%d", b);
78 
79 		sock = open (filename, O_RDWR, 0);
80 		if (sock < 0) {
81 			if (errno == EBUSY) {
82 				continue;
83 			} else {
84 				log_fatal ("Can't find free upf: %m");
85 			}
86 		} else {
87 			break;
88 		}
89 	}
90 
91 	/* Set the UPF device to point at this interface. */
92 	if (ioctl (sock, EIOCSETIF, info -> ifp) < 0)
93 		log_fatal ("Can't attach interface %s to upf device %s: %m",
94 		       info -> name, filename);
95 
96 	/* Get the hardware address. */
97 	if (ioctl (sock, EIOCDEVP, &param) < 0)
98 		log_fatal ("Can't get interface %s hardware address: %m",
99 		       info -> name);
100 
101 	/* We only know how to do ethernet. */
102 	if (param.end_dev_type != ENDT_10MB)
103 		log_fatal ("Invalid device type on network interface %s: %d",
104 		       info -> name, param.end_dev_type);
105 
106 	if (param.end_addr_len != 6)
107 		log_fatal ("Invalid hardware address length on %s: %d",
108 		       info -> name, param.end_addr_len);
109 
110 	info -> hw_address.hlen = 7;
111 	info -> hw_address.hbuf [0] = ARPHRD_ETHER;
112 	memcpy (&info -> hw_address.hbuf [1], param.end_addr, 6);
113 
114 	return sock;
115 }
116 #endif /* USE_UPF_SEND || USE_UPF_RECEIVE */
117 
118 #ifdef USE_UPF_SEND
if_register_send(info)119 void if_register_send (info)
120 	struct interface_info *info;
121 {
122 	/* If we're using the upf API for sending and receiving,
123 	   we don't need to register this interface twice. */
124 #ifndef USE_UPF_RECEIVE
125 	info -> wfdesc = if_register_upf (info, interface);
126 #else
127 	info -> wfdesc = info -> rfdesc;
128 #endif
129         if (!quiet_interface_discovery)
130 		log_info ("Sending on   UPF/%s/%s%s%s",
131 		      info -> name,
132 		      print_hw_addr (info -> hw_address.hbuf [0],
133 				     info -> hw_address.hlen - 1,
134 				     &info -> hw_address.hbuf [1]),
135 		      (info -> shared_network ? "/" : ""),
136 		      (info -> shared_network ?
137 		       info -> shared_network -> name : ""));
138 }
139 
if_deregister_send(info)140 void if_deregister_send (info)
141 	struct interface_info *info;
142 {
143 #ifndef USE_UPF_RECEIVE
144 	close (info -> wfdesc);
145 #endif
146 	info -> wfdesc = -1;
147         if (!quiet_interface_discovery)
148 		log_info ("Disabling output on UPF/%s/%s%s%s",
149 		      info -> name,
150 		      print_hw_addr (info -> hw_address.hbuf [0],
151 				     info -> hw_address.hlen - 1,
152 				     &info -> hw_address.hbuf [1]),
153 		      (info -> shared_network ? "/" : ""),
154 		      (info -> shared_network ?
155 		       info -> shared_network -> name : ""));
156 }
157 #endif /* USE_UPF_SEND */
158 
159 #ifdef USE_UPF_RECEIVE
160 /* Packet filter program...
161    XXX Changes to the filter program may require changes to the constant
162    offsets used in if_register_send to patch the UPF program! XXX */
163 
164 #if defined(RELAY_PORT)
165 #error "Relay port is not yet supported for UPF"
166 #endif
167 
if_register_receive(info)168 void if_register_receive (info)
169 	struct interface_info *info;
170 {
171 	int flag = 1;
172 	u_int32_t addr;
173 	struct enfilter pf;
174 	u_int32_t bits;
175 
176 	/* Open a UPF device and hang it on this interface... */
177 	info -> rfdesc = if_register_upf (info);
178 
179 	/* Allow the copyall flag to be set... */
180 	if (ioctl(info -> rfdesc, EIOCALLOWCOPYALL, &flag) < 0)
181 		log_fatal ("Can't set ALLOWCOPYALL: %m");
182 
183 	/* Clear all the packet filter mode bits first... */
184 	flag = (ENHOLDSIG | ENBATCH | ENTSTAMP | ENPROMISC |
185 		ENNONEXCL | ENCOPYALL);
186 	if (ioctl (info -> rfdesc, EIOCMBIC, &flag) < 0)
187 		log_fatal ("Can't clear pfilt bits: %m");
188 
189 	/* Set the ENBATCH and ENCOPYALL bits... */
190 	bits = ENBATCH | ENCOPYALL;
191 	if (ioctl (info -> rfdesc, EIOCMBIS, &bits) < 0)
192 		log_fatal ("Can't set ENBATCH|ENCOPYALL: %m");
193 
194 	/* Set up the UPF filter program. */
195 	/* XXX Unlike the BPF filter program, this one won't work if the
196 	   XXX IP packet is fragmented or if there are options on the IP
197 	   XXX header. */
198 	pf.enf_Priority = 0;
199 	pf.enf_FilterLen = 0;
200 
201 	pf.enf_Filter [pf.enf_FilterLen++] = ENF_PUSHWORD + 6;
202 	pf.enf_Filter [pf.enf_FilterLen++] = ENF_PUSHLIT + ENF_CAND;
203 	pf.enf_Filter [pf.enf_FilterLen++] = htons (ETHERTYPE_IP);
204 	pf.enf_Filter [pf.enf_FilterLen++] = ENF_PUSHLIT;
205 	pf.enf_Filter [pf.enf_FilterLen++] = htons (IPPROTO_UDP);
206 	pf.enf_Filter [pf.enf_FilterLen++] = ENF_PUSHWORD + 11;
207 	pf.enf_Filter [pf.enf_FilterLen++] = ENF_PUSHLIT + ENF_AND;
208 	pf.enf_Filter [pf.enf_FilterLen++] = htons (0xFF);
209 	pf.enf_Filter [pf.enf_FilterLen++] = ENF_CAND;
210 	pf.enf_Filter [pf.enf_FilterLen++] = ENF_PUSHWORD + 18;
211 	pf.enf_Filter [pf.enf_FilterLen++] = ENF_PUSHLIT + ENF_CAND;
212 	pf.enf_Filter [pf.enf_FilterLen++] = *libdhcp_callbacks.local_port;
213 
214 	if (ioctl (info -> rfdesc, EIOCSETF, &pf) < 0)
215 		log_fatal ("Can't install packet filter program: %m");
216         if (!quiet_interface_discovery)
217 		log_info ("Listening on UPF/%s/%s%s%s",
218 		      info -> name,
219 		      print_hw_addr (info -> hw_address.hbuf [0],
220 				     info -> hw_address.hlen - 1,
221 				     &info -> hw_address.hbuf [1]),
222 		      (info -> shared_network ? "/" : ""),
223 		      (info -> shared_network ?
224 		       info -> shared_network -> name : ""));
225 }
226 
if_deregister_receive(info)227 void if_deregister_receive (info)
228 	struct interface_info *info;
229 {
230 	close (info -> rfdesc);
231 	info -> rfdesc = -1;
232         if (!quiet_interface_discovery)
233 		log_info ("Disabling input on UPF/%s/%s%s%s",
234 		      info -> name,
235 		      print_hw_addr (info -> hw_address.hbuf [0],
236 				     info -> hw_address.hlen - 1,
237 				     &info -> hw_address.hbuf [1]),
238 		      (info -> shared_network ? "/" : ""),
239 		      (info -> shared_network ?
240 		       info -> shared_network -> name : ""));
241 }
242 #endif /* USE_UPF_RECEIVE */
243 
244 #ifdef USE_UPF_SEND
send_packet(interface,packet,raw,len,from,to,hto)245 ssize_t send_packet (interface, packet, raw, len, from, to, hto)
246 	struct interface_info *interface;
247 	struct packet *packet;
248 	struct dhcp_packet *raw;
249 	size_t len;
250 	struct in_addr from;
251 	struct sockaddr_in *to;
252 	struct hardware *hto;
253 {
254 	unsigned hbufp = 0, ibufp = 0;
255 	double hw [4];
256 	double ip [32];
257 	struct iovec iov [3];
258 	int result;
259 	int fudge;
260 
261 	if (!strcmp (interface -> name, "fallback"))
262 		return send_fallback (interface, packet, raw,
263 				      len, from, to, hto);
264 
265 	if (hto == NULL && interface->anycast_mac_addr.hlen)
266 		hto = &interface->anycast_mac_addr;
267 
268 	/* Assemble the headers... */
269 	assemble_hw_header (interface, (unsigned char *)hw, &hbufp, hto);
270 	assemble_udp_ip_header (interface,
271 				(unsigned char *)ip, &ibufp, from.s_addr,
272 				to -> sin_addr.s_addr, to -> sin_port,
273 				(unsigned char *)raw, len);
274 
275 	/* Fire it off */
276 	iov [0].iov_base = ((char *)hw);
277 	iov [0].iov_len = hbufp;
278 	iov [1].iov_base = ((char *)ip);
279 	iov [1].iov_len = ibufp;
280 	iov [2].iov_base = (char *)raw;
281 	iov [2].iov_len = len;
282 
283 	result = writev(interface -> wfdesc, iov, 3);
284 	if (result < 0)
285 		log_error ("send_packet: %m");
286 	return result;
287 }
288 #endif /* USE_UPF_SEND */
289 
290 #ifdef USE_UPF_RECEIVE
receive_packet(interface,buf,len,from,hfrom)291 ssize_t receive_packet (interface, buf, len, from, hfrom)
292 	struct interface_info *interface;
293 	unsigned char *buf;
294 	size_t len;
295 	struct sockaddr_in *from;
296 	struct hardware *hfrom;
297 {
298 	int nread;
299 	int length = 0;
300 	int offset = 0;
301 	unsigned char ibuf [1500 + sizeof (struct enstamp)];
302 	int bufix = 0;
303 	unsigned paylen;
304 
305 	length = read (interface -> rfdesc, ibuf, sizeof ibuf);
306 	if (length <= 0)
307 		return length;
308 
309 	bufix = sizeof (struct enstamp);
310 	/* Decode the physical header... */
311 	offset = decode_hw_header (interface, ibuf, bufix, hfrom);
312 
313 	/* If a physical layer checksum failed (dunno of any
314 	   physical layer that supports this, but WTH), skip this
315 	   packet. */
316 	if (offset < 0) {
317 		return 0;
318 	}
319 
320 	bufix += offset;
321 	length -= offset;
322 
323 	/* Decode the IP and UDP headers... */
324 	offset = decode_udp_ip_header (interface, ibuf, bufix,
325 				       from, length, &paylen, 1);
326 
327 	/* If the IP or UDP checksum was bad, skip the packet... */
328 	if (offset < 0)
329 		return 0;
330 
331 	bufix += offset;
332 	length -= offset;
333 
334 	if (length < paylen)
335 		log_fatal("Internal inconsistency at %s:%d.", MDL);
336 
337 	/* Copy out the data in the packet... */
338 	memcpy (buf, &ibuf[bufix], paylen);
339 	return paylen;
340 }
341 
can_unicast_without_arp(ip)342 int can_unicast_without_arp (ip)
343 	struct interface_info *ip;
344 {
345 	return 1;
346 }
347 
can_receive_unicast_unconfigured(ip)348 int can_receive_unicast_unconfigured (ip)
349 	struct interface_info *ip;
350 {
351 	return 1;
352 }
353 
supports_multiple_interfaces(ip)354 int supports_multiple_interfaces (ip)
355 	struct interface_info *ip;
356 {
357 	return 1;
358 }
359 
maybe_setup_fallback()360 void maybe_setup_fallback ()
361 {
362 	isc_result_t status;
363 	struct interface_info *fbi = (struct interface_info *)0;
364 	if (setup_fallback (&fbi, MDL)) {
365 		if_register_fallback (fbi);
366 		status = omapi_register_io_object ((omapi_object_t *)fbi,
367 						   if_readsocket, 0,
368 						   fallback_discard, 0, 0);
369 		if (status != ISC_R_SUCCESS)
370 			log_fatal ("Can't register I/O handle for %s: %s",
371 				   fbi -> name, isc_result_totext (status));
372 		interface_dereference (&fbi, MDL);
373 	}
374 }
375 #endif
376