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