Initial import from FreeBSD RELENG_4:
[games.git] / contrib / isc-dhcp / 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) 1996-2002 Internet Software Consortium.
8  * All rights reserved.
9  *
10  * Redistribution and use in source and binary forms, with or without
11  * modification, are permitted provided that the following conditions
12  * are met:
13  *
14  * 1. Redistributions of source code must retain the above copyright
15  *    notice, this list of conditions and the following disclaimer.
16  * 2. Redistributions in binary form must reproduce the above copyright
17  *    notice, this list of conditions and the following disclaimer in the
18  *    documentation and/or other materials provided with the distribution.
19  * 3. Neither the name of The Internet Software Consortium nor the names
20  *    of its contributors may be used to endorse or promote products derived
21  *    from this software without specific prior written permission.
22  *
23  * THIS SOFTWARE IS PROVIDED BY THE INTERNET SOFTWARE CONSORTIUM AND
24  * CONTRIBUTORS ``AS IS'' AND ANY EXPRESS OR IMPLIED WARRANTIES,
25  * INCLUDING, BUT NOT LIMITED TO, THE IMPLIED WARRANTIES OF
26  * MERCHANTABILITY AND FITNESS FOR A PARTICULAR PURPOSE ARE
27  * DISCLAIMED.  IN NO EVENT SHALL THE INTERNET SOFTWARE CONSORTIUM OR
28  * CONTRIBUTORS BE LIABLE FOR ANY DIRECT, INDIRECT, INCIDENTAL,
29  * SPECIAL, EXEMPLARY, OR CONSEQUENTIAL DAMAGES (INCLUDING, BUT NOT
30  * LIMITED TO, PROCUREMENT OF SUBSTITUTE GOODS OR SERVICES; LOSS OF
31  * USE, DATA, OR PROFITS; OR BUSINESS INTERRUPTION) HOWEVER CAUSED AND
32  * ON ANY THEORY OF LIABILITY, WHETHER IN CONTRACT, STRICT LIABILITY,
33  * OR TORT (INCLUDING NEGLIGENCE OR OTHERWISE) ARISING IN ANY WAY OUT
34  * OF THE USE OF THIS SOFTWARE, EVEN IF ADVISED OF THE POSSIBILITY OF
35  * SUCH DAMAGE.
36  */
37
38 #ifndef lint
39 static char copyright[] =
40 "$Id: lpf.c,v 1.29.2.1 2002/11/17 02:26:58 dhankins Exp $ Copyright (c) 1996-2002 The Internet Software Consortium.  All rights reserved.\n";
41 #endif /* not lint */
42
43 #include "dhcpd.h"
44 #if defined (USE_LPF_SEND) || defined (USE_LPF_RECEIVE)
45 #include <sys/ioctl.h>
46 #include <sys/uio.h>
47
48 #include <asm/types.h>
49 #include <linux/filter.h>
50 #include <linux/if_ether.h>
51 #include <netinet/in_systm.h>
52 #include "includes/netinet/ip.h"
53 #include "includes/netinet/udp.h"
54 #include "includes/netinet/if_ether.h"
55
56 /* Reinitializes the specified interface after an address change.   This
57    is not required for packet-filter APIs. */
58
59 #ifdef USE_LPF_SEND
60 void if_reinitialize_send (info)
61         struct interface_info *info;
62 {
63 }
64 #endif
65
66 #ifdef USE_LPF_RECEIVE
67 void if_reinitialize_receive (info)
68         struct interface_info *info;
69 {
70 }
71 #endif
72
73 /* Called by get_interface_list for each interface that's discovered.
74    Opens a packet filter for each interface and adds it to the select
75    mask. */
76
77 int if_register_lpf (info)
78         struct interface_info *info;
79 {
80         int sock;
81         char filename[50];
82         int b;
83         struct sockaddr sa;
84
85         /* Make an LPF socket. */
86         if ((sock = socket(PF_PACKET, SOCK_PACKET,
87                            htons((short)ETH_P_ALL))) < 0) {
88                 if (errno == ENOPROTOOPT || errno == EPROTONOSUPPORT ||
89                     errno == ESOCKTNOSUPPORT || errno == EPFNOSUPPORT ||
90                     errno == EAFNOSUPPORT || errno == EINVAL) {
91                         log_error ("socket: %m - make sure");
92                         log_error ("CONFIG_PACKET (Packet socket) %s",
93                                    "and CONFIG_FILTER");
94                         log_error ("(Socket Filtering) are enabled %s",
95                                    "in your kernel");
96                         log_fatal ("configuration!");
97                 }
98                 log_fatal ("Open a socket for LPF: %m");
99         }
100
101         /* Bind to the interface name */
102         memset (&sa, 0, sizeof sa);
103         sa.sa_family = AF_PACKET;
104         strncpy (sa.sa_data, (const char *)info -> ifp, sizeof sa.sa_data);
105         if (bind (sock, &sa, sizeof sa)) {
106                 if (errno == ENOPROTOOPT || errno == EPROTONOSUPPORT ||
107                     errno == ESOCKTNOSUPPORT || errno == EPFNOSUPPORT ||
108                     errno == EAFNOSUPPORT || errno == EINVAL) {
109                         log_error ("socket: %m - make sure");
110                         log_error ("CONFIG_PACKET (Packet socket) %s",
111                                    "and CONFIG_FILTER");
112                         log_error ("(Socket Filtering) are enabled %s",
113                                    "in your kernel");
114                         log_fatal ("configuration!");
115                 }
116                 log_fatal ("Bind socket to interface: %m");
117         }
118
119         return sock;
120 }
121 #endif /* USE_LPF_SEND || USE_LPF_RECEIVE */
122
123 #ifdef USE_LPF_SEND
124 void if_register_send (info)
125         struct interface_info *info;
126 {
127         /* If we're using the lpf API for sending and receiving,
128            we don't need to register this interface twice. */
129 #ifndef USE_LPF_RECEIVE
130         info -> wfdesc = if_register_lpf (info);
131 #else
132         info -> wfdesc = info -> rfdesc;
133 #endif
134         if (!quiet_interface_discovery)
135                 log_info ("Sending on   LPF/%s/%s%s%s",
136                       info -> name,
137                       print_hw_addr (info -> hw_address.hbuf [0],
138                                      info -> hw_address.hlen - 1,
139                                      &info -> hw_address.hbuf [1]),
140                       (info -> shared_network ? "/" : ""),
141                       (info -> shared_network ?
142                        info -> shared_network -> name : ""));
143 }
144
145 void if_deregister_send (info)
146         struct interface_info *info;
147 {
148         /* don't need to close twice if we are using lpf for sending and
149            receiving */
150 #ifndef USE_LPF_RECEIVE
151         /* for LPF this is simple, packet filters are removed when sockets
152            are closed */
153         close (info -> wfdesc);
154 #endif
155         info -> wfdesc = -1;
156         if (!quiet_interface_discovery)
157                 log_info ("Disabling output on LPF/%s/%s%s%s",
158                       info -> name,
159                       print_hw_addr (info -> hw_address.hbuf [0],
160                                      info -> hw_address.hlen - 1,
161                                      &info -> hw_address.hbuf [1]),
162                       (info -> shared_network ? "/" : ""),
163                       (info -> shared_network ?
164                        info -> shared_network -> name : ""));
165 }
166 #endif /* USE_LPF_SEND */
167
168 #ifdef USE_LPF_RECEIVE
169 /* Defined in bpf.c.   We can't extern these in dhcpd.h without pulling
170    in bpf includes... */
171 extern struct sock_filter dhcp_bpf_filter [];
172 extern int dhcp_bpf_filter_len;
173
174 #if defined (HAVE_TR_SUPPORT)
175 extern struct sock_filter dhcp_bpf_tr_filter [];
176 extern int dhcp_bpf_tr_filter_len;
177 static void lpf_tr_filter_setup (struct interface_info *);
178 #endif
179
180 static void lpf_gen_filter_setup (struct interface_info *);
181
182 void if_register_receive (info)
183         struct interface_info *info;
184 {
185         /* Open a LPF device and hang it on this interface... */
186         info -> rfdesc = if_register_lpf (info);
187
188 #if defined (HAVE_TR_SUPPORT)
189         if (info -> hw_address.hbuf [0] == HTYPE_IEEE802)
190                 lpf_tr_filter_setup (info);
191         else
192 #endif
193                 lpf_gen_filter_setup (info);
194
195         if (!quiet_interface_discovery)
196                 log_info ("Listening on LPF/%s/%s%s%s",
197                           info -> name,
198                           print_hw_addr (info -> hw_address.hbuf [0],
199                                          info -> hw_address.hlen - 1,
200                                          &info -> hw_address.hbuf [1]),
201                           (info -> shared_network ? "/" : ""),
202                           (info -> shared_network ?
203                            info -> shared_network -> name : ""));
204 }
205
206 void if_deregister_receive (info)
207         struct interface_info *info;
208 {
209         /* for LPF this is simple, packet filters are removed when sockets
210            are closed */
211         close (info -> rfdesc);
212         info -> rfdesc = -1;
213         if (!quiet_interface_discovery)
214                 log_info ("Disabling input on LPF/%s/%s%s%s",
215                           info -> name,
216                           print_hw_addr (info -> hw_address.hbuf [0],
217                                          info -> hw_address.hlen - 1,
218                                          &info -> hw_address.hbuf [1]),
219                           (info -> shared_network ? "/" : ""),
220                           (info -> shared_network ?
221                            info -> shared_network -> name : ""));
222 }
223
224 static void lpf_gen_filter_setup (info)
225         struct interface_info *info;
226 {
227         struct sock_fprog p;
228
229         /* Set up the bpf filter program structure.    This is defined in
230            bpf.c */
231         p.len = dhcp_bpf_filter_len;
232         p.filter = dhcp_bpf_filter;
233
234         /* Patch the server port into the LPF  program...
235            XXX changes to filter program may require changes
236            to the insn number(s) used below! XXX */
237         dhcp_bpf_filter [8].k = ntohs ((short)local_port);
238
239         if (setsockopt (info -> rfdesc, SOL_SOCKET, SO_ATTACH_FILTER, &p,
240                         sizeof p) < 0) {
241                 if (errno == ENOPROTOOPT || errno == EPROTONOSUPPORT ||
242                     errno == ESOCKTNOSUPPORT || errno == EPFNOSUPPORT ||
243                     errno == EAFNOSUPPORT) {
244                         log_error ("socket: %m - make sure");
245                         log_error ("CONFIG_PACKET (Packet socket) %s",
246                                    "and CONFIG_FILTER");
247                         log_error ("(Socket Filtering) are enabled %s",
248                                    "in your kernel");
249                         log_fatal ("configuration!");
250                 }
251                 log_fatal ("Can't install packet filter program: %m");
252         }
253 }
254
255 #if defined (HAVE_TR_SUPPORT)
256 static void lpf_tr_filter_setup (info)
257         struct interface_info *info;
258 {
259         struct sock_fprog p;
260
261         /* Set up the bpf filter program structure.    This is defined in
262            bpf.c */
263         p.len = dhcp_bpf_tr_filter_len;
264         p.filter = dhcp_bpf_tr_filter;
265
266         /* Patch the server port into the LPF  program...
267            XXX changes to filter program may require changes
268            XXX to the insn number(s) used below!
269            XXX Token ring filter is null - when/if we have a filter 
270            XXX that's not, we'll need this code.
271            XXX dhcp_bpf_filter [?].k = ntohs (local_port); */
272
273         if (setsockopt (info -> rfdesc, SOL_SOCKET, SO_ATTACH_FILTER, &p,
274                         sizeof p) < 0) {
275                 if (errno == ENOPROTOOPT || errno == EPROTONOSUPPORT ||
276                     errno == ESOCKTNOSUPPORT || errno == EPFNOSUPPORT ||
277                     errno == EAFNOSUPPORT) {
278                         log_error ("socket: %m - make sure");
279                         log_error ("CONFIG_PACKET (Packet socket) %s",
280                                    "and CONFIG_FILTER");
281                         log_error ("(Socket Filtering) are enabled %s",
282                                    "in your kernel");
283                         log_fatal ("configuration!");
284                 }
285                 log_fatal ("Can't install packet filter program: %m");
286         }
287 }
288 #endif /* HAVE_TR_SUPPORT */
289 #endif /* USE_LPF_RECEIVE */
290
291 #ifdef USE_LPF_SEND
292 ssize_t send_packet (interface, packet, raw, len, from, to, hto)
293         struct interface_info *interface;
294         struct packet *packet;
295         struct dhcp_packet *raw;
296         size_t len;
297         struct in_addr from;
298         struct sockaddr_in *to;
299         struct hardware *hto;
300 {
301         unsigned hbufp = 0, ibufp = 0;
302         double hh [16];
303         double ih [1536 / sizeof (double)];
304         unsigned char *buf = (unsigned char *)ih;
305         struct sockaddr sa;
306         int result;
307         int fudge;
308
309         if (!strcmp (interface -> name, "fallback"))
310                 return send_fallback (interface, packet, raw,
311                                       len, from, to, hto);
312
313         /* Assemble the headers... */
314         assemble_hw_header (interface, (unsigned char *)hh, &hbufp, hto);
315         fudge = hbufp % 4;      /* IP header must be word-aligned. */
316         memcpy (buf + fudge, (unsigned char *)hh, hbufp);
317         ibufp = hbufp + fudge;
318         assemble_udp_ip_header (interface, buf, &ibufp, from.s_addr,
319                                 to -> sin_addr.s_addr, to -> sin_port,
320                                 (unsigned char *)raw, len);
321         memcpy (buf + ibufp, raw, len);
322
323         /* For some reason, SOCK_PACKET sockets can't be connected,
324            so we have to do a sentdo every time. */
325         memset (&sa, 0, sizeof sa);
326         sa.sa_family = AF_PACKET;
327         strncpy (sa.sa_data,
328                  (const char *)interface -> ifp, sizeof sa.sa_data);
329
330         result = sendto (interface -> wfdesc,
331                          buf + fudge, ibufp + len - fudge, 0, &sa, sizeof sa);
332         if (result < 0)
333                 log_error ("send_packet: %m");
334         return result;
335 }
336 #endif /* USE_LPF_SEND */
337
338 #ifdef USE_LPF_RECEIVE
339 ssize_t receive_packet (interface, buf, len, from, hfrom)
340         struct interface_info *interface;
341         unsigned char *buf;
342         size_t len;
343         struct sockaddr_in *from;
344         struct hardware *hfrom;
345 {
346         int nread;
347         int length = 0;
348         int offset = 0;
349         unsigned char ibuf [1536];
350         unsigned bufix = 0;
351
352         length = read (interface -> rfdesc, ibuf, sizeof ibuf);
353         if (length <= 0)
354                 return length;
355
356         bufix = 0;
357         /* Decode the physical header... */
358         offset = decode_hw_header (interface, ibuf, bufix, hfrom);
359
360         /* If a physical layer checksum failed (dunno of any
361            physical layer that supports this, but WTH), skip this
362            packet. */
363         if (offset < 0) {
364                 return 0;
365         }
366
367         bufix += offset;
368         length -= offset;
369
370         /* Decode the IP and UDP headers... */
371         offset = decode_udp_ip_header (interface, ibuf, bufix, from,
372                                        (unsigned char *)0, (unsigned)length);
373
374         /* If the IP or UDP checksum was bad, skip the packet... */
375         if (offset < 0)
376                 return 0;
377
378         bufix += offset;
379         length -= offset;
380
381         /* Copy out the data in the packet... */
382         memcpy (buf, &ibuf [bufix], length);
383         return length;
384 }
385
386 int can_unicast_without_arp (ip)
387         struct interface_info *ip;
388 {
389         return 1;
390 }
391
392 int can_receive_unicast_unconfigured (ip)
393         struct interface_info *ip;
394 {
395         return 1;
396 }
397
398 int supports_multiple_interfaces (ip)
399         struct interface_info *ip;
400 {
401         return 1;
402 }
403
404 void maybe_setup_fallback ()
405 {
406         isc_result_t status;
407         struct interface_info *fbi = (struct interface_info *)0;
408         if (setup_fallback (&fbi, MDL)) {
409                 if_register_fallback (fbi);
410                 status = omapi_register_io_object ((omapi_object_t *)fbi,
411                                                    if_readsocket, 0,
412                                                    fallback_discard, 0, 0);
413                 if (status != ISC_R_SUCCESS)
414                         log_fatal ("Can't register I/O handle for %s: %s",
415                                    fbi -> name, isc_result_totext (status));
416                 interface_dereference (&fbi, MDL);
417         }
418 }
419 #endif