1/* 2 * Copyright (c) 1999-2013 Apple Inc. All rights reserved. 3 * 4 * @APPLE_OSREFERENCE_LICENSE_HEADER_START@ 5 * 6 * This file contains Original Code and/or Modifications of Original Code 7 * as defined in and that are subject to the Apple Public Source License 8 * Version 2.0 (the 'License'). You may not use this file except in 9 * compliance with the License. The rights granted to you under the License 10 * may not be used to create, or enable the creation or redistribution of, 11 * unlawful or unlicensed copies of an Apple operating system, or to 12 * circumvent, violate, or enable the circumvention or violation of, any 13 * terms of an Apple operating system software license agreement. 14 * 15 * Please obtain a copy of the License at 16 * http://www.opensource.apple.com/apsl/ and read it before using this file. 17 * 18 * The Original Code and all software distributed under the License are 19 * distributed on an 'AS IS' basis, WITHOUT WARRANTY OF ANY KIND, EITHER 20 * EXPRESS OR IMPLIED, AND APPLE HEREBY DISCLAIMS ALL SUCH WARRANTIES, 21 * INCLUDING WITHOUT LIMITATION, ANY WARRANTIES OF MERCHANTABILITY, 22 * FITNESS FOR A PARTICULAR PURPOSE, QUIET ENJOYMENT OR NON-INFRINGEMENT. 23 * Please see the License for the specific language governing rights and 24 * limitations under the License. 25 * 26 * @APPLE_OSREFERENCE_LICENSE_HEADER_END@ 27 */ 28 29#include <kern/locks.h> 30 31#include <sys/types.h> 32#include <sys/kernel_types.h> 33#include <sys/kauth.h> 34#include <sys/socket.h> 35#include <sys/socketvar.h> 36#include <sys/sockio.h> 37#include <sys/sysctl.h> 38#include <sys/proc.h> 39 40#include <net/if.h> 41#include <net/if_var.h> 42#include <net/if_types.h> 43#include <net/bpf.h> 44#include <net/net_osdep.h> 45#include <net/pktap.h> 46#include <net/iptap.h> 47 48#include <netinet/in_pcb.h> 49#include <netinet/tcp.h> 50#include <netinet/tcp_var.h> 51#define _IP_VHL 52#include <netinet/ip.h> 53#include <netinet/ip_var.h> 54#include <netinet/udp.h> 55#include <netinet/udp_var.h> 56 57#include <netinet/ip6.h> 58#include <netinet6/in6_pcb.h> 59 60#include <netinet/kpi_ipfilter.h> 61 62#include <libkern/OSAtomic.h> 63 64#include <kern/debug.h> 65 66#include <sys/mcache.h> 67 68#include <string.h> 69 70struct iptap_softc { 71 LIST_ENTRY(iptap_softc) iptap_link; 72 uint32_t iptap_unit; 73 uint32_t iptap_dlt_raw_count; 74 uint32_t iptap_dlt_pkttap_count; 75 struct ifnet *iptap_ifp; 76}; 77 78static LIST_HEAD(iptap_list, iptap_softc) iptap_list = LIST_HEAD_INITIALIZER(iptap_list); 79 80static void iptap_lock_shared(void); 81static void iptap_lock_exclusive(void); 82static void iptap_lock_done(void); 83static void iptap_alloc_lock(void); 84 85decl_lck_rw_data(static, iptap_lck_rw); 86static lck_grp_t *iptap_grp; 87 88errno_t iptap_if_output(ifnet_t, mbuf_t); 89errno_t iptap_demux(ifnet_t , mbuf_t, char *, protocol_family_t *); 90errno_t iptap_add_proto(ifnet_t, protocol_family_t, const struct ifnet_demux_desc *, 91 u_int32_t); 92errno_t iptap_del_proto(ifnet_t, protocol_family_t); 93errno_t iptap_getdrvspec(ifnet_t , struct ifdrv64 *); 94errno_t iptap_ioctl(ifnet_t, unsigned long, void *); 95void iptap_detach(ifnet_t); 96errno_t iptap_tap_callback(ifnet_t , u_int32_t , bpf_tap_mode ); 97int iptap_clone_create(struct if_clone *, u_int32_t, void *); 98int iptap_clone_destroy(struct ifnet *); 99 100static int iptap_ipf_register(void); 101static int iptap_ipf_unregister(void); 102static errno_t iptap_ipf_input(void *, mbuf_t *, int, u_int8_t); 103static errno_t iptap_ipf_output(void *, mbuf_t *, ipf_pktopts_t); 104static void iptap_ipf_detach(void *); 105 106static ipfilter_t iptap_ipf4, iptap_ipf6; 107 108void iptap_bpf_tap(struct mbuf *m, u_int32_t proto, int outgoing); 109 110static struct if_clone iptap_cloner = 111 IF_CLONE_INITIALIZER(IPTAP_IFNAME, 112 iptap_clone_create, 113 iptap_clone_destroy, 114 0, 115 IF_MAXUNIT); 116 117SYSCTL_DECL(_net_link); 118SYSCTL_NODE(_net_link, OID_AUTO, iptap, CTLFLAG_RW|CTLFLAG_LOCKED, 0, 119 "iptap virtual interface"); 120 121static int iptap_total_tap_count = 0; 122SYSCTL_INT(_net_link_iptap, OID_AUTO, total_tap_count, CTLFLAG_RD | CTLFLAG_LOCKED, 123 &iptap_total_tap_count, 0, ""); 124 125static int iptap_log = 0; 126SYSCTL_INT(_net_link_iptap, OID_AUTO, log, CTLFLAG_RW | CTLFLAG_LOCKED, 127 &iptap_log, 0, ""); 128 129#define IPTAP_LOG(fmt, ...) \ 130do { \ 131 if ((iptap_log)) \ 132 printf("%s:%d " fmt, __FUNCTION__, __LINE__, ##__VA_ARGS__); \ 133} while(false) 134 135__private_extern__ void 136iptap_init(void) 137{ 138 errno_t error; 139 140 iptap_alloc_lock(); 141 142 error = if_clone_attach(&iptap_cloner); 143 if (error != 0) 144 panic("%s: if_clone_attach() failed, error %d\n", __func__, error); 145} 146 147static void 148iptap_alloc_lock(void) 149{ 150 lck_grp_attr_t *grp_attr; 151 lck_attr_t *attr; 152 153 grp_attr = lck_grp_attr_alloc_init(); 154 lck_grp_attr_setdefault(grp_attr); 155 iptap_grp = lck_grp_alloc_init(IPTAP_IFNAME, grp_attr); 156 lck_grp_attr_free(grp_attr); 157 158 attr = lck_attr_alloc_init(); 159 lck_attr_setdefault(attr); 160 161 lck_rw_init(&iptap_lck_rw, iptap_grp, attr); 162 lck_attr_free(attr); 163} 164 165static void 166iptap_lock_shared(void) 167{ 168 lck_rw_lock_shared(&iptap_lck_rw); 169} 170 171static void 172iptap_lock_exclusive(void) 173{ 174 lck_rw_lock_exclusive(&iptap_lck_rw); 175} 176 177static void 178iptap_lock_done(void) 179{ 180 lck_rw_done(&iptap_lck_rw); 181} 182 183__private_extern__ int 184iptap_clone_create(struct if_clone *ifc, u_int32_t unit, void *params) 185{ 186#pragma unused(params) 187 188 int error = 0; 189 struct iptap_softc *iptap = NULL; 190 struct ifnet_init_params if_init; 191 192 iptap = _MALLOC(sizeof(struct iptap_softc), M_DEVBUF, M_WAITOK | M_ZERO); 193 if (iptap == NULL) { 194 printf("%s: _MALLOC failed\n", __func__); 195 error = ENOMEM; 196 goto done; 197 } 198 iptap->iptap_unit = unit; 199 200 /* 201 * We do not use a set_bpf_tap() function as we rather rely on the more 202 * accurate callback passed to bpf_attach() 203 */ 204 bzero(&if_init, sizeof(struct ifnet_init_params)); 205 if_init.name = ifc->ifc_name; 206 if_init.unit = unit; 207 if_init.type = IFT_OTHER; 208 if_init.family = IFNET_FAMILY_LOOPBACK; 209 if_init.output = iptap_if_output; 210 if_init.demux = iptap_demux; 211 if_init.add_proto = iptap_add_proto; 212 if_init.del_proto = iptap_del_proto; 213 if_init.softc = iptap; 214 if_init.ioctl = iptap_ioctl; 215 if_init.detach = iptap_detach; 216 217 error = ifnet_allocate(&if_init, &iptap->iptap_ifp); 218 if (error != 0) { 219 printf("%s: ifnet_allocate failed, error %d\n", __func__, error); 220 goto done; 221 } 222 223 ifnet_set_flags(iptap->iptap_ifp, IFF_UP, IFF_UP); 224 225 error = ifnet_attach(iptap->iptap_ifp, NULL); 226 if (error != 0) { 227 printf("%s: ifnet_attach failed - error %d\n", __func__, error); 228 ifnet_release(iptap->iptap_ifp); 229 goto done; 230 } 231 232 /* 233 * Attach by default as DLT_PKTAP for packet metadata 234 * Provide DLT_RAW for legacy 235 */ 236 bpf_attach(iptap->iptap_ifp, DLT_PKTAP, sizeof(struct pktap_header), NULL, 237 iptap_tap_callback); 238 bpf_attach(iptap->iptap_ifp, DLT_RAW, 0, NULL, 239 iptap_tap_callback); 240 241 /* Take a reference and add to the global list */ 242 ifnet_reference(iptap->iptap_ifp); 243 244 iptap_lock_exclusive(); 245 246 if (LIST_EMPTY(&iptap_list)) 247 iptap_ipf_register(); 248 LIST_INSERT_HEAD(&iptap_list, iptap, iptap_link); 249 iptap_lock_done(); 250done: 251 if (error != 0) { 252 if (iptap != NULL) 253 _FREE(iptap, M_DEVBUF); 254 } 255 return (error); 256} 257 258__private_extern__ int 259iptap_clone_destroy(struct ifnet *ifp) 260{ 261 int error = 0; 262 263 (void) ifnet_detach(ifp); 264 265 return (error); 266} 267 268/* 269 * This function is called whenever a DLT is set on the interface: 270 * - When interface is attached to a BPF device via BIOCSETIF for the default DLT 271 * - Whenever a new DLT is selected via BIOCSDLT 272 * - When the interface is detached from a BPF device (direction is zero) 273 */ 274__private_extern__ errno_t 275iptap_tap_callback(ifnet_t ifp, u_int32_t dlt, bpf_tap_mode direction) 276{ 277 struct iptap_softc *iptap; 278 279 iptap = ifp->if_softc; 280 if (iptap == NULL) { 281 printf("%s: if_softc is NULL for ifp %s\n", __func__, 282 ifp->if_xname); 283 goto done; 284 } 285 switch (dlt) { 286 case DLT_RAW: 287 if (direction == 0) { 288 if (iptap->iptap_dlt_raw_count > 0) { 289 iptap->iptap_dlt_raw_count--; 290 OSAddAtomic(-1, &iptap_total_tap_count); 291 292 } 293 } else { 294 iptap->iptap_dlt_raw_count++; 295 OSAddAtomic(1, &iptap_total_tap_count); 296 } 297 break; 298 case DLT_PKTAP: 299 if (direction == 0) { 300 if (iptap->iptap_dlt_pkttap_count > 0) { 301 iptap->iptap_dlt_pkttap_count--; 302 OSAddAtomic(-1, &iptap_total_tap_count); 303 } 304 } else { 305 iptap->iptap_dlt_pkttap_count++; 306 OSAddAtomic(1, &iptap_total_tap_count); 307 } 308 break; 309 } 310done: 311 /* 312 * Attachements count must be positive and we're in trouble 313 * if we have more that 2**31 attachements 314 */ 315 VERIFY(iptap_total_tap_count >= 0); 316 317 return (0); 318} 319 320__private_extern__ errno_t 321iptap_if_output(ifnet_t ifp, mbuf_t m) 322{ 323#pragma unused(ifp) 324 325 mbuf_freem(m); 326 return (ENOTSUP); 327} 328 329__private_extern__ errno_t 330iptap_demux(ifnet_t ifp, mbuf_t m, char *header, 331 protocol_family_t *ppf) 332{ 333#pragma unused(ifp) 334#pragma unused(m) 335#pragma unused(header) 336#pragma unused(ppf) 337 338 return (ENOTSUP); 339} 340 341__private_extern__ errno_t 342iptap_add_proto(ifnet_t ifp, protocol_family_t pf, 343 const struct ifnet_demux_desc *dmx, u_int32_t cnt) 344{ 345#pragma unused(ifp) 346#pragma unused(pf) 347#pragma unused(dmx) 348#pragma unused(cnt) 349 350 return (0); 351} 352 353__private_extern__ errno_t 354iptap_del_proto(ifnet_t ifp, protocol_family_t pf) 355{ 356#pragma unused(ifp) 357#pragma unused(pf) 358 359 return (0); 360} 361 362__private_extern__ errno_t 363iptap_getdrvspec(ifnet_t ifp, struct ifdrv64 *ifd) 364{ 365 errno_t error = 0; 366 struct iptap_softc *iptap; 367 368 iptap = ifp->if_softc; 369 if (iptap == NULL) { 370 error = ENOENT; 371 printf("%s: iptap NULL - error %d\n", __func__, error); 372 goto done; 373 } 374 375 switch (ifd->ifd_cmd) { 376 case PKTP_CMD_TAP_COUNT: { 377 uint32_t tap_count = iptap->iptap_dlt_raw_count + iptap->iptap_dlt_pkttap_count; 378 379 if (ifd->ifd_len < sizeof(tap_count)) { 380 printf("%s: PKTP_CMD_TAP_COUNT ifd_len %llu too small - error %d\n", 381 __func__, ifd->ifd_len, error); 382 error = EINVAL; 383 break; 384 } 385 error = copyout(&tap_count, ifd->ifd_data, sizeof(tap_count)); 386 if (error) { 387 printf("%s: PKTP_CMD_TAP_COUNT copyout - error %d\n", __func__, error); 388 goto done; 389 } 390 break; 391 } 392 default: 393 error = EINVAL; 394 break; 395 } 396 397done: 398 return (error); 399} 400 401__private_extern__ errno_t 402iptap_ioctl(ifnet_t ifp, unsigned long cmd, void *data) 403{ 404 errno_t error = 0; 405 406 if ((cmd & IOC_IN)) { 407 error = kauth_authorize_generic(kauth_cred_get(), KAUTH_GENERIC_ISSUSER); 408 if (error) { 409 goto done; 410 } 411 } 412 413 switch (cmd) { 414 case SIOCGDRVSPEC32: { 415 struct ifdrv64 ifd; 416 struct ifdrv32 *ifd32 = (struct ifdrv32 *)data; 417 418 memcpy(ifd.ifd_name, ifd32->ifd_name, sizeof(ifd.ifd_name)); 419 ifd.ifd_cmd = ifd32->ifd_cmd; 420 ifd.ifd_len = ifd32->ifd_len; 421 ifd.ifd_data = ifd32->ifd_data; 422 423 error = iptap_getdrvspec(ifp, &ifd); 424 425 break; 426 } 427 case SIOCGDRVSPEC64: { 428 struct ifdrv64 *ifd64 = (struct ifdrv64 *)data; 429 430 error = iptap_getdrvspec(ifp, ifd64); 431 432 break; 433 } 434 default: 435 error = ENOTSUP; 436 break; 437 } 438done: 439 return (error); 440} 441 442__private_extern__ void 443iptap_detach(ifnet_t ifp) 444{ 445 struct iptap_softc *iptap; 446 447 iptap_lock_exclusive(); 448 449 iptap = ifp->if_softc; 450 ifp->if_softc = NULL; 451 LIST_REMOVE(iptap, iptap_link); 452 453 if (LIST_EMPTY(&iptap_list)) 454 iptap_ipf_unregister(); 455 456 iptap_lock_done(); 457 458 /* Drop reference as it's no more on the global list */ 459 ifnet_release(ifp); 460 461 _FREE(iptap, M_DEVBUF); 462 463 /* This is for the reference taken by ifnet_attach() */ 464 (void) ifnet_release(ifp); 465} 466 467static int 468iptap_ipf_register(void) 469{ 470 struct ipf_filter iptap_ipfinit; 471 int err = 0; 472 473 IPTAP_LOG("\n"); 474 475 bzero(&iptap_ipfinit, sizeof (iptap_ipfinit)); 476 iptap_ipfinit.name = IPTAP_IFNAME; 477 iptap_ipfinit.cookie = &iptap_ipf4; 478 iptap_ipfinit.ipf_input = iptap_ipf_input; 479 iptap_ipfinit.ipf_output = iptap_ipf_output; 480 iptap_ipfinit.ipf_detach = iptap_ipf_detach; 481 482 err = ipf_addv4(&iptap_ipfinit, &iptap_ipf4); 483 if (err != 0) { 484 printf("%s: ipf_addv4 for %s0 failed - %d\n", 485 __func__, IPTAP_IFNAME, err); 486 goto done; 487 } 488 489 iptap_ipfinit.cookie = &iptap_ipf6; 490 err = ipf_addv6(&iptap_ipfinit, &iptap_ipf6); 491 if (err != 0) { 492 printf("%s: ipf_addv6 for %s0 failed - %d\n", 493 __func__, IPTAP_IFNAME, err); 494 (void) ipf_remove(iptap_ipf4); 495 iptap_ipf4 = NULL; 496 goto done; 497 } 498 499done: 500 return (err); 501} 502 503static int 504iptap_ipf_unregister(void) 505{ 506 int err = 0; 507 508 IPTAP_LOG("\n"); 509 510 if (iptap_ipf4 != NULL) { 511 err = ipf_remove(iptap_ipf4); 512 if (err != 0) { 513 printf("%s: ipf_remove (ipv4) for %s0 failed - %d\n", 514 __func__, IPTAP_IFNAME, err); 515 goto done; 516 } 517 iptap_ipf4 = NULL; 518 } 519 520 if (iptap_ipf6 != NULL) { 521 err = ipf_remove(iptap_ipf6); 522 if (err != 0) { 523 printf("%s: ipf_remove (ipv6) for %s0 failed - %d\n", 524 __func__, IPTAP_IFNAME, err); 525 goto done; 526 } 527 iptap_ipf6 = NULL; 528 } 529done: 530 return (err); 531} 532 533static errno_t 534iptap_ipf_input(void *arg, mbuf_t *mp, int off, u_int8_t proto) 535{ 536#pragma unused(off) 537#pragma unused(proto) 538 539 if (arg == (void *)&iptap_ipf4) 540 iptap_bpf_tap(*mp, AF_INET, 0); 541 else if (arg == (void *)&iptap_ipf6) 542 iptap_bpf_tap(*mp, AF_INET6, 0); 543 else 544 IPTAP_LOG("%s:%d bad cookie 0x%llx &iptap_ipf4 0x%llx " 545 "&iptap_ipf6 0x%llx\n", __func__, __LINE__, 546 (uint64_t)VM_KERNEL_ADDRPERM(arg), 547 (uint64_t)VM_KERNEL_ADDRPERM(&iptap_ipf4), 548 (uint64_t)VM_KERNEL_ADDRPERM(&iptap_ipf6)); 549 550 return (0); 551} 552 553static errno_t 554iptap_ipf_output(void *arg, mbuf_t *mp, ipf_pktopts_t opt) 555{ 556#pragma unused(opt) 557 558 if (arg == (void *)&iptap_ipf4) 559 iptap_bpf_tap(*mp, AF_INET, 1); 560 else if (arg == (void *)&iptap_ipf6) 561 iptap_bpf_tap(*mp, AF_INET6, 1); 562 else 563 IPTAP_LOG("%s:%d bad cookie 0x%llx &iptap_ipf4 0x%llx " 564 "&iptap_ipf6 0x%llx\n", __func__, __LINE__, 565 (uint64_t)VM_KERNEL_ADDRPERM(arg), 566 (uint64_t)VM_KERNEL_ADDRPERM(&iptap_ipf4), 567 (uint64_t)VM_KERNEL_ADDRPERM(&iptap_ipf6)); 568 569 return (0); 570} 571 572static void 573iptap_ipf_detach(void *arg) 574{ 575#pragma unused(arg) 576} 577 578__private_extern__ void 579iptap_bpf_tap(struct mbuf *m, u_int32_t proto, int outgoing) 580{ 581 struct iptap_softc *iptap; 582 void (*bpf_tap_func)(ifnet_t , u_int32_t , mbuf_t , void * , size_t ) = 583 outgoing ? bpf_tap_out : bpf_tap_in; 584 585 iptap_lock_shared(); 586 587 LIST_FOREACH(iptap, &iptap_list, iptap_link) { 588 if (iptap->iptap_dlt_raw_count > 0) { 589 bpf_tap_func(iptap->iptap_ifp, DLT_RAW, m, 590 NULL, 0); 591 } 592 if (iptap->iptap_dlt_pkttap_count > 0) { 593 struct { 594 struct pktap_header hdr; 595 u_int32_t proto; 596 } hdr_buffer; 597 struct pktap_header *hdr = &hdr_buffer.hdr; 598 size_t hdr_size = sizeof(hdr_buffer); 599 struct ifnet *ifp = outgoing ? NULL : m->m_pkthdr.rcvif; 600 601 /* Verify the structure is packed */ 602 _CASSERT(sizeof(hdr_buffer) == sizeof(struct pktap_header) + sizeof(u_int32_t)); 603 604 bzero(hdr, sizeof(hdr_buffer)); 605 hdr->pth_length = sizeof(struct pktap_header); 606 hdr->pth_type_next = PTH_TYPE_PACKET; 607 hdr->pth_dlt = DLT_NULL; 608 if (ifp != NULL) 609 snprintf(hdr->pth_ifname, sizeof(hdr->pth_ifname), "%s", 610 ifp->if_xname); 611 hdr_buffer.proto = proto; 612 hdr->pth_flags = outgoing ? PTH_FLAG_DIR_OUT : PTH_FLAG_DIR_IN; 613 hdr->pth_protocol_family = proto; 614 hdr->pth_frame_pre_length = 0; 615 hdr->pth_frame_post_length = 0; 616 hdr->pth_iftype = ifp != NULL ? ifp->if_type : 0; 617 hdr->pth_ifunit = ifp != NULL ? ifp->if_unit : 0; 618 619 pktap_fill_proc_info(hdr, proto, m, 0, outgoing, ifp); 620 621 hdr->pth_svc = so_svc2tc(m->m_pkthdr.pkt_svc); 622 623 bpf_tap_func(iptap->iptap_ifp, DLT_PKTAP, m, hdr, hdr_size); 624 } 625 } 626 627 iptap_lock_done(); 628} 629