1 1.2 christos /* $NetBSD: nit.c,v 1.3 2022/04/03 01:10:58 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.3 christos * Copyright (C) 2004-2022 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.3 christos * PO Box 360 26 1.3 christos * Newmarket, NH 03857 USA 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.2 christos __RCSID("$NetBSD: nit.c,v 1.3 2022/04/03 01:10:58 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.2 christos pf.Pf_Filter [pf.Pf_FilterLen++] = *libdhcp_callbacks.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