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