Add regression test infrastructure.
[dragonfly.git] / contrib / dhcp-3.0 / common / nit.c
1 /* nit.c
2
3    Network Interface Tap (NIT) network interface code, by Ted Lemon
4    with one crucial tidbit of help from Stu Grossmen. */
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  * This software has been written for Internet Systems Consortium
29  * by Ted Lemon in cooperation with Vixie Enterprises and Nominum, Inc.
30  * To learn more about Internet Systems Consortium, see
31  * ``http://www.isc.org/''.  To learn more about Vixie Enterprises,
32  * see ``http://www.vix.com''.   To learn more about Nominum, Inc., see
33  * ``http://www.nominum.com''.
34  */
35
36 #ifndef lint
37 static char copyright[] =
38 "$Id: nit.c,v 1.34.2.3 2004/11/24 17:39:15 dhankins Exp $ Copyright (c) 2004 Internet Systems Consortium.  All rights reserved.\n";
39 #endif /* not lint */
40
41 #include "dhcpd.h"
42 #if defined (USE_NIT_SEND) || defined (USE_NIT_RECEIVE)
43 #include <sys/ioctl.h>
44 #include <sys/uio.h>
45
46 #include <sys/time.h>
47 #include <net/nit.h>
48 #include <net/nit_if.h>
49 #include <net/nit_pf.h>
50 #include <net/nit_buf.h>
51 #include <sys/stropts.h>
52 #include <net/packetfilt.h>
53
54 #include <netinet/in_systm.h>
55 #include "includes/netinet/ip.h"
56 #include "includes/netinet/udp.h"
57 #include "includes/netinet/if_ether.h"
58
59 /* Reinitializes the specified interface after an address change.   This
60    is not required for packet-filter APIs. */
61
62 #ifdef USE_NIT_SEND
63 void if_reinitialize_send (info)
64         struct interface_info *info;
65 {
66 }
67 #endif
68
69 #ifdef USE_NIT_RECEIVE
70 void if_reinitialize_receive (info)
71         struct interface_info *info;
72 {
73 }
74 #endif
75
76 /* Called by get_interface_list for each interface that's discovered.
77    Opens a packet filter for each interface and adds it to the select
78    mask. */
79
80 int if_register_nit (info)
81         struct interface_info *info;
82 {
83         int sock;
84         char filename[50];
85         struct ifreq ifr;
86         struct strioctl sio;
87
88         /* Open a NIT device */
89         sock = open ("/dev/nit", O_RDWR);
90         if (sock < 0)
91                 log_fatal ("Can't open NIT device for %s: %m", info -> name);
92
93         /* Set the NIT device to point at this interface. */
94         sio.ic_cmd = NIOCBIND;
95         sio.ic_len = sizeof *(info -> ifp);
96         sio.ic_dp = (char *)(info -> ifp);
97         sio.ic_timout = INFTIM;
98         if (ioctl (sock, I_STR, &sio) < 0)
99                 log_fatal ("Can't attach interface %s to nit device: %m",
100                        info -> name);
101
102         /* Get the low-level address... */
103         sio.ic_cmd = SIOCGIFADDR;
104         sio.ic_len = sizeof ifr;
105         sio.ic_dp = (char *)&ifr;
106         sio.ic_timout = INFTIM;
107         if (ioctl (sock, I_STR, &sio) < 0)
108                 log_fatal ("Can't get physical layer address for %s: %m",
109                        info -> name);
110
111         /* XXX code below assumes ethernet interface! */
112         info -> hw_address.hlen = 7;
113         info -> hw_address.hbuf [0] = ARPHRD_ETHER;
114         memcpy (&info -> hw_address.hbuf [1],
115                 ifr.ifr_ifru.ifru_addr.sa_data, 6);
116
117         if (ioctl (sock, I_PUSH, "pf") < 0)
118                 log_fatal ("Can't push packet filter onto NIT for %s: %m",
119                        info -> name);
120
121         return sock;
122 }
123 #endif /* USE_NIT_SEND || USE_NIT_RECEIVE */
124
125 #ifdef USE_NIT_SEND
126 void if_register_send (info)
127         struct interface_info *info;
128 {
129         /* If we're using the nit API for sending and receiving,
130            we don't need to register this interface twice. */
131 #ifndef USE_NIT_RECEIVE
132         struct packetfilt pf;
133         struct strioctl sio;
134
135         info -> wfdesc = if_register_nit (info);
136
137         pf.Pf_Priority = 0;
138         pf.Pf_FilterLen = 1;
139         pf.Pf_Filter [0] = ENF_PUSHZERO;
140
141         /* Set up an NIT filter that rejects everything... */
142         sio.ic_cmd = NIOCSETF;
143         sio.ic_len = sizeof pf;
144         sio.ic_dp = (char *)&pf;
145         sio.ic_timout = INFTIM;
146         if (ioctl (info -> wfdesc, I_STR, &sio) < 0)
147                 log_fatal ("Can't set NIT filter: %m");
148 #else
149         info -> wfdesc = info -> rfdesc;
150 #endif
151         if (!quiet_interface_discovery)
152                 log_info ("Sending on   NIT/%s%s%s",
153                       print_hw_addr (info -> hw_address.hbuf [0],
154                                      info -> hw_address.hlen - 1,
155                                      &info -> hw_address.hbuf [1]),
156                       (info -> shared_network ? "/" : ""),
157                       (info -> shared_network ?
158                        info -> shared_network -> name : ""));
159 }
160
161 void if_deregister_send (info)
162         struct interface_info *info;
163 {
164         /* If we're using the nit API for sending and receiving,
165            we don't need to register this interface twice. */
166 #ifndef USE_NIT_RECEIVE
167         close (info -> wfdesc);
168 #endif
169         info -> wfdesc = -1;
170         if (!quiet_interface_discovery)
171                 log_info ("Disabling output on NIT/%s%s%s",
172                       print_hw_addr (info -> hw_address.hbuf [0],
173                                      info -> hw_address.hlen - 1,
174                                      &info -> hw_address.hbuf [1]),
175                       (info -> shared_network ? "/" : ""),
176                       (info -> shared_network ?
177                        info -> shared_network -> name : ""));
178 }
179 #endif /* USE_NIT_SEND */
180
181 #ifdef USE_NIT_RECEIVE
182 /* Packet filter program...
183    XXX Changes to the filter program may require changes to the constant
184    offsets used in if_register_send to patch the NIT program! XXX */
185
186 void if_register_receive (info)
187         struct interface_info *info;
188 {
189         int flag = 1;
190         u_int32_t x;
191         struct packetfilt pf;
192         struct strioctl sio;
193         u_int16_t addr [2];
194         struct timeval t;
195
196         /* Open a NIT device and hang it on this interface... */
197         info -> rfdesc = if_register_nit (info);
198
199         /* Set the snap length to 0, which means always take the whole
200            packet. */
201         x = 0;
202         if (ioctl (info -> rfdesc, NIOCSSNAP, &x) < 0)
203                 log_fatal ("Can't set NIT snap length on %s: %m", info -> name);
204
205         /* Set the stream to byte stream mode */
206         if (ioctl (info -> rfdesc, I_SRDOPT, RMSGN) != 0)
207                 log_info ("I_SRDOPT failed on %s: %m", info -> name);
208
209 #if 0
210         /* Push on the chunker... */
211         if (ioctl (info -> rfdesc, I_PUSH, "nbuf") < 0)
212                 log_fatal ("Can't push chunker onto NIT STREAM: %m");
213
214         /* Set the timeout to zero. */
215         t.tv_sec = 0;
216         t.tv_usec = 0;
217         if (ioctl (info -> rfdesc, NIOCSTIME, &t) < 0)
218                 log_fatal ("Can't set chunk timeout: %m");
219 #endif
220
221         /* Ask for no header... */
222         x = 0;
223         if (ioctl (info -> rfdesc, NIOCSFLAGS, &x) < 0)
224                 log_fatal ("Can't set NIT flags on %s: %m", info -> name);
225
226         /* Set up the NIT filter program. */
227         /* XXX Unlike the BPF filter program, this one won't work if the
228            XXX IP packet is fragmented or if there are options on the IP
229            XXX header. */
230         pf.Pf_Priority = 0;
231         pf.Pf_FilterLen = 0;
232
233         pf.Pf_Filter [pf.Pf_FilterLen++] = ENF_PUSHWORD + 6;
234         pf.Pf_Filter [pf.Pf_FilterLen++] = ENF_PUSHLIT + ENF_CAND;
235         pf.Pf_Filter [pf.Pf_FilterLen++] = htons (ETHERTYPE_IP);
236         pf.Pf_Filter [pf.Pf_FilterLen++] = ENF_PUSHLIT;
237         pf.Pf_Filter [pf.Pf_FilterLen++] = htons (IPPROTO_UDP);
238         pf.Pf_Filter [pf.Pf_FilterLen++] = ENF_PUSHWORD + 11;
239         pf.Pf_Filter [pf.Pf_FilterLen++] = ENF_PUSHLIT + ENF_AND;
240         pf.Pf_Filter [pf.Pf_FilterLen++] = htons (0xFF);
241         pf.Pf_Filter [pf.Pf_FilterLen++] = ENF_CAND;
242         pf.Pf_Filter [pf.Pf_FilterLen++] = ENF_PUSHWORD + 18;
243         pf.Pf_Filter [pf.Pf_FilterLen++] = ENF_PUSHLIT + ENF_CAND;
244         pf.Pf_Filter [pf.Pf_FilterLen++] = local_port;
245
246         /* Install the filter... */
247         sio.ic_cmd = NIOCSETF;
248         sio.ic_len = sizeof pf;
249         sio.ic_dp = (char *)&pf;
250         sio.ic_timout = INFTIM;
251         if (ioctl (info -> rfdesc, I_STR, &sio) < 0)
252                 log_fatal ("Can't set NIT filter on %s: %m", info -> name);
253
254         if (!quiet_interface_discovery)
255                 log_info ("Listening on NIT/%s%s%s",
256                       print_hw_addr (info -> hw_address.hbuf [0],
257                                      info -> hw_address.hlen - 1,
258                                      &info -> hw_address.hbuf [1]),
259                       (info -> shared_network ? "/" : ""),
260                       (info -> shared_network ?
261                        info -> shared_network -> name : ""));
262 }
263
264 void if_deregister_receive (info)
265         struct interface_info *info;
266 {
267         /* If we're using the nit API for sending and receiving,
268            we don't need to register this interface twice. */
269         close (info -> rfdesc);
270         info -> rfdesc = -1;
271
272         if (!quiet_interface_discovery)
273                 log_info ("Disabling input on NIT/%s%s%s",
274                       print_hw_addr (info -> hw_address.hbuf [0],
275                                      info -> hw_address.hlen - 1,
276                                      &info -> hw_address.hbuf [1]),
277                       (info -> shared_network ? "/" : ""),
278                       (info -> shared_network ?
279                        info -> shared_network -> name : ""));
280 }
281 #endif /* USE_NIT_RECEIVE */
282
283 #ifdef USE_NIT_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, ibufp;
294         double hh [16];
295         double ih [1536 / sizeof (double)];
296         unsigned char *buf = (unsigned char *)ih;
297         struct sockaddr *junk;
298         struct strbuf ctl, data;
299         struct sockaddr_in foo;
300         int result;
301
302         if (!strcmp (interface -> name, "fallback"))
303                 return send_fallback (interface, packet, raw,
304                                       len, from, to, hto);
305
306         /* Start with the sockaddr struct... */
307         junk = (struct sockaddr *)&hh [0];
308         hbufp = (((unsigned char *)&junk -> sa_data [0]) -
309                  (unsigned char *)&hh[0]);
310         ibufp = 0;
311
312         /* Assemble the headers... */
313         assemble_hw_header (interface, (unsigned char *)junk, &hbufp, hto);
314         assemble_udp_ip_header (interface, buf, &ibufp,
315                                 from.s_addr, to -> sin_addr.s_addr,
316                                 to -> sin_port, (unsigned char *)raw, len);
317
318         /* Copy the data into the buffer (yuk). */
319         memcpy (buf + ibufp, raw, len);
320
321         /* Set up the sockaddr structure... */
322 #if USE_SIN_LEN
323         junk -> sa_len = hbufp - 2; /* XXX */
324 #endif
325         junk -> sa_family = AF_UNSPEC;
326
327         /* Set up the msg_buf structure... */
328         ctl.buf = (char *)&hh [0];
329         ctl.maxlen = ctl.len = hbufp;
330         data.buf = (char *)&ih [0];
331         data.maxlen = data.len = ibufp + len;
332
333         result = putmsg (interface -> wfdesc, &ctl, &data, 0);
334         if (result < 0)
335                 log_error ("send_packet: %m");
336         return result;
337 }
338 #endif /* USE_NIT_SEND */
339
340 #ifdef USE_NIT_RECEIVE
341 ssize_t receive_packet (interface, buf, len, from, hfrom)
342         struct interface_info *interface;
343         unsigned char *buf;
344         size_t len;
345         struct sockaddr_in *from;
346         struct hardware *hfrom;
347 {
348         int nread;
349         int length = 0;
350         int offset = 0;
351         unsigned char ibuf [1536];
352         int bufix = 0;
353
354         length = read (interface -> rfdesc, ibuf, sizeof ibuf);
355         if (length <= 0)
356                 return length;
357
358         /* Decode the physical header... */
359         offset = decode_hw_header (interface, ibuf, bufix, hfrom);
360
361         /* If a physical layer checksum failed (dunno of any
362            physical layer that supports this, but WTH), skip this
363            packet. */
364         if (offset < 0) {
365                 return 0;
366         }
367
368         bufix += offset;
369         length -= offset;
370
371         /* Decode the IP and UDP headers... */
372         offset = decode_udp_ip_header (interface, ibuf, bufix,
373                                        from, length);
374
375         /* If the IP or UDP checksum was bad, skip the packet... */
376         if (offset < 0)
377                 return 0;
378
379         bufix += offset;
380         length -= offset;
381
382         /* Copy out the data in the packet... */
383         memcpy (buf, &ibuf [bufix], length);
384         return length;
385 }
386
387 int can_unicast_without_arp (ip)
388         struct interface_info *ip;
389 {
390         return 1;
391 }
392
393 int can_receive_unicast_unconfigured (ip)
394         struct interface_info *ip;
395 {
396         return 1;
397 }
398
399 int supports_multiple_interfaces (ip)
400         struct interface_info *ip;
401 {
402         return 1;
403 }
404
405 void maybe_setup_fallback ()
406 {
407         isc_result_t status;
408         struct interface_info *fbi = (struct interface_info *)0;
409         if (setup_fallback (&fbi, MDL)) {
410                 if_register_fallback (fbi);
411                 status = omapi_register_io_object ((omapi_object_t *)fbi,
412                                                    if_readsocket, 0,
413                                                    fallback_discard, 0, 0);
414                 if (status != ISC_R_SUCCESS)
415                         log_fatal ("Can't register I/O handle for %s: %s",
416                                    fbi -> name, isc_result_totext (status));
417                 interface_dereference (&fbi, MDL);
418         }
419 }
420 #endif