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