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