]>
Commit | Line | Data |
---|---|---|
8cb33098 TL |
1 | /* bpf.c |
2 | ||
3 | BPF socket interface code, originally contributed by Archie Cobbs. */ | |
4 | ||
5 | /* | |
6 | * Copyright (c) 1995, 1996 The Internet Software Consortium. | |
7 | * All rights reserved. | |
8 | * | |
9 | * Redistribution and use in source and binary forms, with or without | |
10 | * modification, are permitted provided that the following conditions | |
11 | * are met: | |
12 | * | |
13 | * 1. Redistributions of source code must retain the above copyright | |
14 | * notice, this list of conditions and the following disclaimer. | |
15 | * 2. Redistributions in binary form must reproduce the above copyright | |
16 | * notice, this list of conditions and the following disclaimer in the | |
17 | * documentation and/or other materials provided with the distribution. | |
18 | * 3. Neither the name of The Internet Software Consortium nor the names | |
19 | * of its contributors may be used to endorse or promote products derived | |
20 | * from this software without specific prior written permission. | |
21 | * | |
22 | * THIS SOFTWARE IS PROVIDED BY THE INTERNET SOFTWARE CONSORTIUM AND | |
23 | * CONTRIBUTORS ``AS IS'' AND ANY EXPRESS OR IMPLIED WARRANTIES, | |
24 | * INCLUDING, BUT NOT LIMITED TO, THE IMPLIED WARRANTIES OF | |
25 | * MERCHANTABILITY AND FITNESS FOR A PARTICULAR PURPOSE ARE | |
26 | * DISCLAIMED. IN NO EVENT SHALL THE INTERNET SOFTWARE CONSORTIUM OR | |
27 | * CONTRIBUTORS BE LIABLE FOR ANY DIRECT, INDIRECT, INCIDENTAL, | |
28 | * SPECIAL, EXEMPLARY, OR CONSEQUENTIAL DAMAGES (INCLUDING, BUT NOT | |
29 | * LIMITED TO, PROCUREMENT OF SUBSTITUTE GOODS OR SERVICES; LOSS OF | |
30 | * USE, DATA, OR PROFITS; OR BUSINESS INTERRUPTION) HOWEVER CAUSED AND | |
31 | * ON ANY THEORY OF LIABILITY, WHETHER IN CONTRACT, STRICT LIABILITY, | |
32 | * OR TORT (INCLUDING NEGLIGENCE OR OTHERWISE) ARISING IN ANY WAY OUT | |
33 | * OF THE USE OF THIS SOFTWARE, EVEN IF ADVISED OF THE POSSIBILITY OF | |
34 | * SUCH DAMAGE. | |
35 | * | |
36 | * This software has been written for the Internet Software Consortium | |
37 | * by Ted Lemon <mellon@fugue.com> in cooperation with Vixie | |
38 | * Enterprises. To learn more about the Internet Software Consortium, | |
39 | * see ``http://www.vix.com/isc''. To learn more about Vixie | |
40 | * Enterprises, see ``http://www.vix.com''. | |
41 | */ | |
42 | ||
43 | #ifndef lint | |
44 | static char copyright[] = | |
4595a58c | 45 | "$Id: bpf.c,v 1.18 1997/06/08 03:18:07 mellon Exp $ Copyright (c) 1995, 1996 The Internet Software Consortium. All rights reserved.\n"; |
8cb33098 TL |
46 | #endif /* not lint */ |
47 | ||
48 | #include "dhcpd.h" | |
d64c8816 | 49 | #if defined (USE_BPF_SEND) || defined (USE_BPF_RECEIVE) |
8cb33098 | 50 | #include <sys/ioctl.h> |
045820ee | 51 | #include <sys/uio.h> |
8cb33098 TL |
52 | |
53 | #include <net/bpf.h> | |
019f8a12 TL |
54 | #ifdef NEED_OSF_PFILT_HACKS |
55 | #include <net/pfilt.h> | |
56 | #endif | |
8cb33098 | 57 | #include <netinet/in_systm.h> |
c41e81e4 TL |
58 | #include "includes/netinet/ip.h" |
59 | #include "includes/netinet/udp.h" | |
60 | #include "includes/netinet/if_ether.h" | |
8cb33098 | 61 | |
c3585217 TL |
62 | /* Reinitializes the specified interface after an address change. This |
63 | is not required for packet-filter APIs. */ | |
64 | ||
65 | #ifdef USE_BPF_SEND | |
66 | void if_reinitialize_send (info) | |
67 | struct interface_info *info; | |
68 | { | |
69 | } | |
70 | #endif | |
71 | ||
72 | #ifdef USE_BPF_RECEIVE | |
73 | void if_reinitialize_receive (info) | |
74 | struct interface_info *info; | |
75 | { | |
76 | } | |
77 | #endif | |
78 | ||
8cb33098 TL |
79 | /* Called by get_interface_list for each interface that's discovered. |
80 | Opens a packet filter for each interface and adds it to the select | |
81 | mask. */ | |
82 | ||
c3585217 | 83 | int if_register_bpf (info) |
045820ee | 84 | struct interface_info *info; |
8cb33098 | 85 | { |
045820ee | 86 | int sock; |
8cb33098 | 87 | char filename[50]; |
045820ee | 88 | int b; |
8cb33098 TL |
89 | |
90 | /* Open a BPF device */ | |
91 | for (b = 0; 1; b++) { | |
d64c8816 | 92 | #ifndef NO_SNPRINTF |
019f8a12 | 93 | snprintf(filename, sizeof(filename), BPF_FORMAT, b); |
d64c8816 | 94 | #else |
019f8a12 | 95 | sprintf(filename, BPF_FORMAT, b); |
d64c8816 | 96 | #endif |
045820ee TL |
97 | sock = open (filename, O_RDWR, 0); |
98 | if (sock < 0) { | |
8cb33098 TL |
99 | if (errno == EBUSY) { |
100 | continue; | |
101 | } else { | |
102 | error ("Can't find free bpf: %m"); | |
103 | } | |
104 | } else { | |
105 | break; | |
106 | } | |
107 | } | |
108 | ||
109 | /* Set the BPF device to point at this interface. */ | |
c3585217 | 110 | if (ioctl (sock, BIOCSETIF, info -> ifp) < 0) |
c255efc9 TL |
111 | error ("Can't attach interface %s to bpf device %s: %m", |
112 | info -> name, filename); | |
045820ee TL |
113 | |
114 | return sock; | |
115 | } | |
116 | #endif /* USE_BPF_SEND || USE_BPF_RECEIVE */ | |
117 | ||
118 | #ifdef USE_BPF_SEND | |
c3585217 | 119 | void if_register_send (info) |
045820ee | 120 | struct interface_info *info; |
045820ee TL |
121 | { |
122 | /* If we're using the bpf API for sending and receiving, | |
123 | we don't need to register this interface twice. */ | |
124 | #ifndef USE_BPF_RECEIVE | |
125 | info -> wfdesc = if_register_bpf (info, interface); | |
126 | #else | |
127 | info -> wfdesc = info -> rfdesc; | |
128 | #endif | |
f1c1b296 TL |
129 | note ("Sending on BPF/%s/%s/%s", |
130 | info -> name, | |
131 | print_hw_addr (info -> hw_address.htype, | |
132 | info -> hw_address.hlen, | |
133 | info -> hw_address.haddr), | |
939327b9 TL |
134 | (info -> shared_network ? |
135 | info -> shared_network -> name : "unattached")); | |
8cb33098 | 136 | } |
045820ee TL |
137 | #endif /* USE_BPF_SEND */ |
138 | ||
139 | #ifdef USE_BPF_RECEIVE | |
140 | /* Packet filter program... | |
141 | XXX Changes to the filter program may require changes to the constant | |
142 | offsets used in if_register_send to patch the BPF program! XXX */ | |
143 | ||
144 | struct bpf_insn filter [] = { | |
145 | /* Make sure this is an IP packet... */ | |
146 | BPF_STMT (BPF_LD + BPF_H + BPF_ABS, 12), | |
f1c1b296 | 147 | BPF_JUMP (BPF_JMP + BPF_JEQ + BPF_K, ETHERTYPE_IP, 0, 8), |
045820ee TL |
148 | |
149 | /* Make sure it's a UDP packet... */ | |
150 | BPF_STMT (BPF_LD + BPF_B + BPF_ABS, 23), | |
f1c1b296 | 151 | BPF_JUMP (BPF_JMP + BPF_JEQ + BPF_K, IPPROTO_UDP, 0, 6), |
045820ee TL |
152 | |
153 | /* Make sure this isn't a fragment... */ | |
154 | BPF_STMT(BPF_LD + BPF_H + BPF_ABS, 20), | |
f1c1b296 | 155 | BPF_JUMP(BPF_JMP + BPF_JSET + BPF_K, 0x1fff, 4, 0), |
045820ee TL |
156 | |
157 | /* Get the IP header length... */ | |
158 | BPF_STMT (BPF_LDX + BPF_B + BPF_MSH, 14), | |
159 | ||
160 | /* Make sure it's to the right port... */ | |
161 | BPF_STMT (BPF_LD + BPF_H + BPF_IND, 16), | |
f1c1b296 | 162 | BPF_JUMP (BPF_JMP + BPF_JEQ + BPF_K, 67, 0, 1), /* patch */ |
8cb33098 | 163 | |
045820ee TL |
164 | /* If we passed all the tests, ask for the whole packet. */ |
165 | BPF_STMT(BPF_RET+BPF_K, (u_int)-1), | |
166 | ||
167 | /* Otherwise, drop it. */ | |
168 | BPF_STMT(BPF_RET+BPF_K, 0), | |
169 | }; | |
170 | ||
c3585217 | 171 | void if_register_receive (info) |
045820ee | 172 | struct interface_info *info; |
045820ee TL |
173 | { |
174 | int flag = 1; | |
175 | struct bpf_version v; | |
176 | u_int32_t addr; | |
177 | struct bpf_program p; | |
019f8a12 | 178 | u_int32_t bits; |
045820ee TL |
179 | |
180 | /* Open a BPF device and hang it on this interface... */ | |
c3585217 | 181 | info -> rfdesc = if_register_bpf (info); |
045820ee TL |
182 | |
183 | /* Make sure the BPF version is in range... */ | |
184 | if (ioctl (info -> rfdesc, BIOCVERSION, &v) < 0) | |
185 | error ("Can't get BPF version: %m"); | |
186 | ||
187 | if (v.bv_major != BPF_MAJOR_VERSION || | |
188 | v.bv_minor < BPF_MINOR_VERSION) | |
189 | error ("Kernel BPF version out of range - recompile dhcpd!"); | |
190 | ||
191 | /* Set immediate mode so that reads return as soon as a packet | |
192 | comes in, rather than waiting for the input buffer to fill with | |
193 | packets. */ | |
194 | if (ioctl (info -> rfdesc, BIOCIMMEDIATE, &flag) < 0) | |
195 | error ("Can't set immediate mode on bpf device: %m"); | |
196 | ||
019f8a12 TL |
197 | #ifdef NEED_OSF_PFILT_HACKS |
198 | /* Allow the copyall flag to be set... */ | |
199 | if (ioctl(info -> rfdesc, EIOCALLOWCOPYALL, &flag) < 0) | |
200 | error ("Can't set ALLOWCOPYALL: %m"); | |
201 | ||
202 | /* Clear all the packet filter mode bits first... */ | |
203 | bits = 0; | |
204 | if (ioctl (info -> rfdesc, EIOCMBIS, &bits) < 0) | |
205 | error ("Can't clear pfilt bits: %m"); | |
206 | ||
207 | /* Set the ENBATCH, ENCOPYALL, ENBPFHDR bits... */ | |
208 | bits = ENBATCH | ENCOPYALL | ENBPFHDR; | |
209 | if (ioctl (info -> rfdesc, EIOCMBIS, &bits) < 0) | |
210 | error ("Can't set ENBATCH|ENCOPYALL|ENBPFHDR: %m"); | |
211 | #endif | |
045820ee TL |
212 | /* Get the required BPF buffer length from the kernel. */ |
213 | if (ioctl (info -> rfdesc, BIOCGBLEN, &info -> rbuf_max) < 0) | |
214 | error ("Can't get bpf buffer length: %m"); | |
215 | info -> rbuf = malloc (info -> rbuf_max); | |
216 | if (!info -> rbuf) | |
217 | error ("Can't allocate %d bytes for bpf input buffer."); | |
218 | info -> rbuf_offset = 0; | |
219 | info -> rbuf_len = 0; | |
220 | ||
045820ee TL |
221 | /* Set up the bpf filter program structure. */ |
222 | p.bf_len = sizeof filter / sizeof (struct bpf_insn); | |
223 | p.bf_insns = filter; | |
224 | ||
229b6208 TL |
225 | /* Patch the server port into the BPF program... |
226 | XXX changes to filter program may require changes | |
227 | to the insn number(s) used below! XXX */ | |
a60076f9 | 228 | filter [8].k = ntohs (local_port); |
229b6208 | 229 | |
045820ee TL |
230 | if (ioctl (info -> rfdesc, BIOCSETF, &p) < 0) |
231 | error ("Can't install packet filter program: %m"); | |
f1c1b296 TL |
232 | note ("Listening on BPF/%s/%s/%s", |
233 | info -> name, | |
234 | print_hw_addr (info -> hw_address.htype, | |
235 | info -> hw_address.hlen, | |
236 | info -> hw_address.haddr), | |
939327b9 TL |
237 | (info -> shared_network ? |
238 | info -> shared_network -> name : "unattached")); | |
045820ee TL |
239 | } |
240 | #endif /* USE_BPF_RECEIVE */ | |
241 | ||
242 | #ifdef USE_BPF_SEND | |
4595a58c | 243 | ssize_t send_packet (interface, packet, raw, len, from, to, hto) |
045820ee TL |
244 | struct interface_info *interface; |
245 | struct packet *packet; | |
246 | struct dhcp_packet *raw; | |
247 | size_t len; | |
f1c1b296 | 248 | struct in_addr from; |
045820ee TL |
249 | struct sockaddr_in *to; |
250 | struct hardware *hto; | |
251 | { | |
252 | int bufp = 0; | |
253 | unsigned char buf [256]; | |
254 | struct iovec iov [2]; | |
255 | ||
256 | /* Assemble the headers... */ | |
257 | assemble_hw_header (interface, buf, &bufp, hto); | |
f1c1b296 | 258 | assemble_udp_ip_header (interface, buf, &bufp, from.s_addr, |
045820ee TL |
259 | to -> sin_addr.s_addr, to -> sin_port, |
260 | (unsigned char *)raw, len); | |
261 | ||
262 | /* Fire it off */ | |
019f8a12 | 263 | iov [0].iov_base = (char *)buf; |
045820ee TL |
264 | iov [0].iov_len = bufp; |
265 | iov [1].iov_base = (char *)raw; | |
266 | iov [1].iov_len = len; | |
267 | ||
268 | return writev(interface -> wfdesc, iov, 2); | |
269 | } | |
270 | #endif /* USE_BPF_SEND */ | |
271 | ||
272 | #ifdef USE_BPF_RECEIVE | |
4595a58c | 273 | ssize_t receive_packet (interface, buf, len, from, hfrom) |
045820ee TL |
274 | struct interface_info *interface; |
275 | unsigned char *buf; | |
276 | size_t len; | |
277 | struct sockaddr_in *from; | |
278 | struct hardware *hfrom; | |
279 | { | |
280 | int length = 0; | |
281 | int offset = 0; | |
282 | struct bpf_hdr hdr; | |
283 | ||
284 | /* All this complexity is because BPF doesn't guarantee | |
285 | that only one packet will be returned at a time. We're | |
286 | getting what we deserve, though - this is a terrible abuse | |
287 | of the BPF interface. Sigh. */ | |
288 | ||
289 | /* Process packets until we get one we can return or until we've | |
290 | done a read and gotten nothing we can return... */ | |
291 | ||
292 | do { | |
293 | /* If the buffer is empty, fill it. */ | |
294 | if (interface -> rbuf_offset == interface -> rbuf_len) { | |
295 | length = read (interface -> rfdesc, | |
296 | interface -> rbuf, | |
297 | interface -> rbuf_max); | |
298 | if (length <= 0) | |
299 | return length; | |
300 | interface -> rbuf_offset = 0; | |
301 | interface -> rbuf_len = length; | |
302 | } | |
303 | ||
304 | /* If there isn't room for a whole bpf header, something went | |
305 | wrong, but we'll ignore it and hope it goes away... XXX */ | |
306 | if (interface -> rbuf_len - | |
307 | interface -> rbuf_offset < sizeof hdr) { | |
308 | interface -> rbuf_offset = interface -> rbuf_len; | |
309 | continue; | |
310 | } | |
311 | ||
312 | /* Copy out a bpf header... */ | |
313 | memcpy (&hdr, &interface -> rbuf [interface -> rbuf_offset], | |
314 | sizeof hdr); | |
315 | ||
316 | /* If the bpf header plus data doesn't fit in what's left | |
317 | of the buffer, stick head in sand yet again... */ | |
318 | if (interface -> rbuf_offset + | |
319 | hdr.bh_hdrlen + hdr.bh_caplen > interface -> rbuf_len) { | |
320 | interface -> rbuf_offset = interface -> rbuf_len; | |
321 | continue; | |
322 | } | |
323 | ||
324 | /* If the captured data wasn't the whole packet, or if | |
325 | the packet won't fit in the input buffer, all we | |
326 | can do is drop it. */ | |
327 | if (hdr.bh_caplen != hdr.bh_datalen) { | |
328 | interface -> rbuf_offset += | |
329 | hdr.bh_hdrlen = hdr.bh_caplen; | |
330 | continue; | |
331 | } | |
332 | ||
333 | /* Skip over the BPF header... */ | |
334 | interface -> rbuf_offset += hdr.bh_hdrlen; | |
335 | ||
336 | /* Decode the physical header... */ | |
337 | offset = decode_hw_header (interface, | |
338 | interface -> rbuf, | |
339 | interface -> rbuf_offset, | |
340 | hfrom); | |
341 | ||
342 | /* If a physical layer checksum failed (dunno of any | |
343 | physical layer that supports this, but WTH), skip this | |
344 | packet. */ | |
345 | if (offset < 0) { | |
346 | interface -> rbuf_offset += hdr.bh_caplen; | |
347 | continue; | |
348 | } | |
349 | interface -> rbuf_offset += offset; | |
350 | hdr.bh_caplen -= offset; | |
351 | ||
352 | /* Decode the IP and UDP headers... */ | |
353 | offset = decode_udp_ip_header (interface, | |
354 | interface -> rbuf, | |
355 | interface -> rbuf_offset, | |
356 | from, | |
357 | (unsigned char *)0, | |
358 | hdr.bh_caplen); | |
359 | ||
360 | /* If the IP or UDP checksum was bad, skip the packet... */ | |
361 | if (offset < 0) { | |
362 | interface -> rbuf_offset += hdr.bh_caplen; | |
363 | continue; | |
364 | } | |
365 | interface -> rbuf_offset += offset; | |
366 | hdr.bh_caplen -= offset; | |
367 | ||
368 | /* If there's not enough room to stash the packet data, | |
369 | we have to skip it (this shouldn't happen in real | |
370 | life, though). */ | |
371 | if (hdr.bh_caplen > len) { | |
372 | interface -> rbuf_offset += hdr.bh_caplen; | |
373 | continue; | |
374 | } | |
375 | ||
376 | /* Copy out the data in the packet... */ | |
377 | memcpy (buf, interface -> rbuf + interface -> rbuf_offset, | |
378 | hdr.bh_caplen); | |
379 | interface -> rbuf_offset += hdr.bh_caplen; | |
380 | return hdr.bh_caplen; | |
381 | } while (!length); | |
382 | return 0; | |
383 | } | |
384 | #endif |