1/* 2 * Copyright (c) 2010-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 <sys/param.h> 30#include <sys/types.h> 31#include <sys/kpi_mbuf.h> 32#include <sys/socket.h> 33#include <sys/kern_control.h> 34#include <sys/mcache.h> 35#include <sys/socketvar.h> 36#include <sys/sysctl.h> 37#include <sys/queue.h> 38#include <sys/priv.h> 39 40#include <kern/clock.h> 41#include <kern/debug.h> 42 43#include <libkern/libkern.h> 44#include <libkern/OSMalloc.h> 45#include <libkern/OSAtomic.h> 46#include <libkern/locks.h> 47 48#include <net/if.h> 49#include <net/if_var.h> 50#include <net/if_types.h> 51#include <net/route.h> 52#include <net/ntstat.h> 53 54#include <netinet/ip_var.h> 55#include <netinet/in_pcb.h> 56#include <netinet/in_var.h> 57#include <netinet/tcp.h> 58#include <netinet/tcp_var.h> 59#include <netinet/tcp_fsm.h> 60#include <netinet/udp.h> 61#include <netinet/udp_var.h> 62#include <netinet6/in6_pcb.h> 63#include <netinet6/in6_var.h> 64 65__private_extern__ int nstat_collect = 1; 66SYSCTL_INT(_net, OID_AUTO, statistics, CTLFLAG_RW | CTLFLAG_LOCKED, 67 &nstat_collect, 0, "Collect detailed statistics"); 68 69static int nstat_privcheck = 0; 70SYSCTL_INT(_net, OID_AUTO, statistics_privcheck, CTLFLAG_RW | CTLFLAG_LOCKED, 71 &nstat_privcheck, 0, "Entitlement check"); 72 73enum 74{ 75 NSTAT_FLAG_CLEANUP = (1 << 0), 76 NSTAT_FLAG_REQCOUNTS = (1 << 1), 77 NSTAT_FLAG_REQDESCS = (1 << 2) 78}; 79 80typedef struct nstat_control_state 81{ 82 struct nstat_control_state *ncs_next; 83 u_int32_t ncs_watching; 84 decl_lck_mtx_data(, mtx); 85 kern_ctl_ref ncs_kctl; 86 u_int32_t ncs_unit; 87 nstat_src_ref_t ncs_next_srcref; 88 struct nstat_src *ncs_srcs; 89 u_int32_t ncs_flags; 90} nstat_control_state; 91 92typedef struct nstat_provider 93{ 94 struct nstat_provider *next; 95 nstat_provider_id_t nstat_provider_id; 96 size_t nstat_descriptor_length; 97 errno_t (*nstat_lookup)(const void *data, u_int32_t length, nstat_provider_cookie_t *out_cookie); 98 int (*nstat_gone)(nstat_provider_cookie_t cookie); 99 errno_t (*nstat_counts)(nstat_provider_cookie_t cookie, struct nstat_counts *out_counts, int *out_gone); 100 errno_t (*nstat_watcher_add)(nstat_control_state *state); 101 void (*nstat_watcher_remove)(nstat_control_state *state); 102 errno_t (*nstat_copy_descriptor)(nstat_provider_cookie_t cookie, void *data, u_int32_t len); 103 void (*nstat_release)(nstat_provider_cookie_t cookie, boolean_t locked); 104} nstat_provider; 105 106 107typedef struct nstat_src 108{ 109 struct nstat_src *next; 110 nstat_src_ref_t srcref; 111 nstat_provider *provider; 112 nstat_provider_cookie_t cookie; 113 uint32_t filter; 114} nstat_src; 115 116static errno_t nstat_control_send_counts(nstat_control_state *, 117 nstat_src *, unsigned long long, int *); 118static int nstat_control_send_description(nstat_control_state *state, nstat_src *src, u_int64_t context); 119static errno_t nstat_control_send_removed(nstat_control_state *, nstat_src *); 120static void nstat_control_cleanup_source(nstat_control_state *state, nstat_src *src, 121 boolean_t); 122 123static u_int32_t nstat_udp_watchers = 0; 124static u_int32_t nstat_tcp_watchers = 0; 125 126static void nstat_control_register(void); 127 128static volatile OSMallocTag nstat_malloc_tag = NULL; 129static nstat_control_state *nstat_controls = NULL; 130static uint64_t nstat_idle_time = 0; 131static decl_lck_mtx_data(, nstat_mtx); 132 133static void 134nstat_copy_sa_out( 135 const struct sockaddr *src, 136 struct sockaddr *dst, 137 int maxlen) 138{ 139 if (src->sa_len > maxlen) return; 140 141 bcopy(src, dst, src->sa_len); 142 if (src->sa_family == AF_INET6 && 143 src->sa_len >= sizeof(struct sockaddr_in6)) 144 { 145 struct sockaddr_in6 *sin6 = (struct sockaddr_in6*)(void *)dst; 146 if (IN6_IS_SCOPE_EMBED(&sin6->sin6_addr)) 147 { 148 if (sin6->sin6_scope_id == 0) 149 sin6->sin6_scope_id = ntohs(sin6->sin6_addr.__u6_addr.__u6_addr16[1]); 150 sin6->sin6_addr.__u6_addr.__u6_addr16[1] = 0; 151 } 152 } 153} 154 155static void 156nstat_ip_to_sockaddr( 157 const struct in_addr *ip, 158 u_int16_t port, 159 struct sockaddr_in *sin, 160 u_int32_t maxlen) 161{ 162 if (maxlen < sizeof(struct sockaddr_in)) 163 return; 164 165 sin->sin_family = AF_INET; 166 sin->sin_len = sizeof(*sin); 167 sin->sin_port = port; 168 sin->sin_addr = *ip; 169} 170 171static void 172nstat_ip6_to_sockaddr( 173 const struct in6_addr *ip6, 174 u_int16_t port, 175 struct sockaddr_in6 *sin6, 176 u_int32_t maxlen) 177{ 178 if (maxlen < sizeof(struct sockaddr_in6)) 179 return; 180 181 sin6->sin6_family = AF_INET6; 182 sin6->sin6_len = sizeof(*sin6); 183 sin6->sin6_port = port; 184 sin6->sin6_addr = *ip6; 185 if (IN6_IS_SCOPE_EMBED(&sin6->sin6_addr)) 186 { 187 sin6->sin6_scope_id = ntohs(sin6->sin6_addr.__u6_addr.__u6_addr16[1]); 188 sin6->sin6_addr.__u6_addr.__u6_addr16[1] = 0; 189 } 190} 191 192#pragma mark -- Network Statistic Providers -- 193 194static errno_t nstat_control_source_add(u_int64_t context, nstat_control_state *state, nstat_provider *provider, nstat_provider_cookie_t cookie); 195struct nstat_provider *nstat_providers = NULL; 196 197static struct nstat_provider* 198nstat_find_provider_by_id( 199 nstat_provider_id_t id) 200{ 201 struct nstat_provider *provider; 202 203 for (provider = nstat_providers; provider != NULL; provider = provider->next) 204 { 205 if (provider->nstat_provider_id == id) 206 break; 207 } 208 209 return provider; 210} 211 212static errno_t 213nstat_lookup_entry( 214 nstat_provider_id_t id, 215 const void *data, 216 u_int32_t length, 217 nstat_provider **out_provider, 218 nstat_provider_cookie_t *out_cookie) 219{ 220 *out_provider = nstat_find_provider_by_id(id); 221 if (*out_provider == NULL) 222 { 223 return ENOENT; 224 } 225 226 return (*out_provider)->nstat_lookup(data, length, out_cookie); 227} 228 229static void nstat_init_route_provider(void); 230static void nstat_init_tcp_provider(void); 231static void nstat_init_udp_provider(void); 232static void nstat_init_ifnet_provider(void); 233 234__private_extern__ void 235nstat_init(void) 236{ 237 if (nstat_malloc_tag != NULL) return; 238 239 OSMallocTag tag = OSMalloc_Tagalloc(NET_STAT_CONTROL_NAME, OSMT_DEFAULT); 240 if (!OSCompareAndSwapPtr(NULL, tag, &nstat_malloc_tag)) 241 { 242 OSMalloc_Tagfree(tag); 243 tag = nstat_malloc_tag; 244 } 245 else 246 { 247 // we need to initialize other things, we do it here as this code path will only be hit once; 248 nstat_init_route_provider(); 249 nstat_init_tcp_provider(); 250 nstat_init_udp_provider(); 251 nstat_init_ifnet_provider(); 252 nstat_control_register(); 253 } 254} 255 256#pragma mark -- Aligned Buffer Allocation -- 257 258struct align_header 259{ 260 u_int32_t offset; 261 u_int32_t length; 262}; 263 264static void* 265nstat_malloc_aligned( 266 u_int32_t length, 267 u_int8_t alignment, 268 OSMallocTag tag) 269{ 270 struct align_header *hdr = NULL; 271 u_int32_t size = length + sizeof(*hdr) + alignment - 1; 272 273 u_int8_t *buffer = OSMalloc(size, tag); 274 if (buffer == NULL) return NULL; 275 276 u_int8_t *aligned = buffer + sizeof(*hdr); 277 aligned = (u_int8_t*)P2ROUNDUP(aligned, alignment); 278 279 hdr = (struct align_header*)(void *)(aligned - sizeof(*hdr)); 280 hdr->offset = aligned - buffer; 281 hdr->length = size; 282 283 return aligned; 284} 285 286static void 287nstat_free_aligned( 288 void *buffer, 289 OSMallocTag tag) 290{ 291 struct align_header *hdr = (struct align_header*)(void *)((u_int8_t*)buffer - sizeof(*hdr)); 292 OSFree(((char*)buffer) - hdr->offset, hdr->length, tag); 293} 294 295#pragma mark -- Route Provider -- 296 297static nstat_provider nstat_route_provider; 298 299static errno_t 300nstat_route_lookup( 301 const void *data, 302 u_int32_t length, 303 nstat_provider_cookie_t *out_cookie) 304{ 305 // rt_lookup doesn't take const params but it doesn't modify the parameters for 306 // the lookup. So...we use a union to eliminate the warning. 307 union 308 { 309 struct sockaddr *sa; 310 const struct sockaddr *const_sa; 311 } dst, mask; 312 313 const nstat_route_add_param *param = (const nstat_route_add_param*)data; 314 *out_cookie = NULL; 315 316 if (length < sizeof(*param)) 317 { 318 return EINVAL; 319 } 320 321 if (param->dst.v4.sin_family == 0 || 322 param->dst.v4.sin_family > AF_MAX || 323 (param->mask.v4.sin_family != 0 && param->mask.v4.sin_family != param->dst.v4.sin_family)) 324 { 325 return EINVAL; 326 } 327 328 if (param->dst.v4.sin_len > sizeof(param->dst) || 329 (param->mask.v4.sin_family && param->mask.v4.sin_len > sizeof(param->mask.v4.sin_len))) 330 { 331 return EINVAL; 332 } 333 334 // TBD: Need to validate length of sockaddr for different families? 335 dst.const_sa = (const struct sockaddr*)¶m->dst; 336 mask.const_sa = param->mask.v4.sin_family ? (const struct sockaddr*)¶m->mask : NULL; 337 338 struct radix_node_head *rnh = rt_tables[dst.sa->sa_family]; 339 if (rnh == NULL) return EAFNOSUPPORT; 340 341 lck_mtx_lock(rnh_lock); 342 struct rtentry *rt = rt_lookup(TRUE, dst.sa, mask.sa, rnh, param->ifindex); 343 lck_mtx_unlock(rnh_lock); 344 345 if (rt) *out_cookie = (nstat_provider_cookie_t)rt; 346 347 return rt ? 0 : ENOENT; 348} 349 350static int 351nstat_route_gone( 352 nstat_provider_cookie_t cookie) 353{ 354 struct rtentry *rt = (struct rtentry*)cookie; 355 return ((rt->rt_flags & RTF_UP) == 0) ? 1 : 0; 356} 357 358static errno_t 359nstat_route_counts( 360 nstat_provider_cookie_t cookie, 361 struct nstat_counts *out_counts, 362 int *out_gone) 363{ 364 struct rtentry *rt = (struct rtentry*)cookie; 365 struct nstat_counts *rt_stats = rt->rt_stats; 366 367 *out_gone = 0; 368 369 if ((rt->rt_flags & RTF_UP) == 0) *out_gone = 1; 370 371 if (rt_stats) 372 { 373 atomic_get_64(out_counts->nstat_rxpackets, &rt_stats->nstat_rxpackets); 374 atomic_get_64(out_counts->nstat_rxbytes, &rt_stats->nstat_rxbytes); 375 atomic_get_64(out_counts->nstat_txpackets, &rt_stats->nstat_txpackets); 376 atomic_get_64(out_counts->nstat_txbytes, &rt_stats->nstat_txbytes); 377 out_counts->nstat_rxduplicatebytes = rt_stats->nstat_rxduplicatebytes; 378 out_counts->nstat_rxoutoforderbytes = rt_stats->nstat_rxoutoforderbytes; 379 out_counts->nstat_txretransmit = rt_stats->nstat_txretransmit; 380 out_counts->nstat_connectattempts = rt_stats->nstat_connectattempts; 381 out_counts->nstat_connectsuccesses = rt_stats->nstat_connectsuccesses; 382 out_counts->nstat_min_rtt = rt_stats->nstat_min_rtt; 383 out_counts->nstat_avg_rtt = rt_stats->nstat_avg_rtt; 384 out_counts->nstat_var_rtt = rt_stats->nstat_var_rtt; 385 out_counts->nstat_cell_rxbytes = out_counts->nstat_cell_txbytes = 0; 386 } 387 else 388 bzero(out_counts, sizeof(*out_counts)); 389 390 return 0; 391} 392 393static void 394nstat_route_release( 395 nstat_provider_cookie_t cookie, 396 __unused int locked) 397{ 398 rtfree((struct rtentry*)cookie); 399} 400 401static u_int32_t nstat_route_watchers = 0; 402 403static int 404nstat_route_walktree_add( 405 struct radix_node *rn, 406 void *context) 407{ 408 errno_t result = 0; 409 struct rtentry *rt = (struct rtentry *)rn; 410 nstat_control_state *state = (nstat_control_state*)context; 411 412 lck_mtx_assert(rnh_lock, LCK_MTX_ASSERT_OWNED); 413 414 /* RTF_UP can't change while rnh_lock is held */ 415 if ((rt->rt_flags & RTF_UP) != 0) 416 { 417 /* Clear RTPRF_OURS if the route is still usable */ 418 RT_LOCK(rt); 419 if (rt_validate(rt)) { 420 RT_ADDREF_LOCKED(rt); 421 RT_UNLOCK(rt); 422 } else { 423 RT_UNLOCK(rt); 424 rt = NULL; 425 } 426 427 /* Otherwise if RTF_CONDEMNED, treat it as if it were down */ 428 if (rt == NULL) 429 return (0); 430 431 result = nstat_control_source_add(0, state, &nstat_route_provider, rt); 432 if (result != 0) 433 rtfree_locked(rt); 434 } 435 436 return result; 437} 438 439static errno_t 440nstat_route_add_watcher( 441 nstat_control_state *state) 442{ 443 int i; 444 errno_t result = 0; 445 OSIncrementAtomic(&nstat_route_watchers); 446 447 lck_mtx_lock(rnh_lock); 448 for (i = 1; i < AF_MAX; i++) 449 { 450 struct radix_node_head *rnh; 451 rnh = rt_tables[i]; 452 if (!rnh) continue; 453 454 result = rnh->rnh_walktree(rnh, nstat_route_walktree_add, state); 455 if (result != 0) 456 { 457 break; 458 } 459 } 460 lck_mtx_unlock(rnh_lock); 461 462 return result; 463} 464 465__private_extern__ void 466nstat_route_new_entry( 467 struct rtentry *rt) 468{ 469 if (nstat_route_watchers == 0) 470 return; 471 472 lck_mtx_lock(&nstat_mtx); 473 if ((rt->rt_flags & RTF_UP) != 0) 474 { 475 nstat_control_state *state; 476 for (state = nstat_controls; state; state = state->ncs_next) 477 { 478 if ((state->ncs_watching & (1 << NSTAT_PROVIDER_ROUTE)) != 0) 479 { 480 // this client is watching routes 481 // acquire a reference for the route 482 RT_ADDREF(rt); 483 484 // add the source, if that fails, release the reference 485 if (nstat_control_source_add(0, state, &nstat_route_provider, rt) != 0) 486 RT_REMREF(rt); 487 } 488 } 489 } 490 lck_mtx_unlock(&nstat_mtx); 491} 492 493static void 494nstat_route_remove_watcher( 495 __unused nstat_control_state *state) 496{ 497 OSDecrementAtomic(&nstat_route_watchers); 498} 499 500static errno_t 501nstat_route_copy_descriptor( 502 nstat_provider_cookie_t cookie, 503 void *data, 504 u_int32_t len) 505{ 506 nstat_route_descriptor *desc = (nstat_route_descriptor*)data; 507 if (len < sizeof(*desc)) 508 { 509 return EINVAL; 510 } 511 bzero(desc, sizeof(*desc)); 512 513 struct rtentry *rt = (struct rtentry*)cookie; 514 desc->id = (uintptr_t)rt; 515 desc->parent_id = (uintptr_t)rt->rt_parent; 516 desc->gateway_id = (uintptr_t)rt->rt_gwroute; 517 518 519 // key/dest 520 struct sockaddr *sa; 521 if ((sa = rt_key(rt))) 522 nstat_copy_sa_out(sa, &desc->dst.sa, sizeof(desc->dst)); 523 524 // mask 525 if ((sa = rt_mask(rt)) && sa->sa_len <= sizeof(desc->mask)) 526 memcpy(&desc->mask, sa, sa->sa_len); 527 528 // gateway 529 if ((sa = rt->rt_gateway)) 530 nstat_copy_sa_out(sa, &desc->gateway.sa, sizeof(desc->gateway)); 531 532 if (rt->rt_ifp) 533 desc->ifindex = rt->rt_ifp->if_index; 534 535 desc->flags = rt->rt_flags; 536 537 return 0; 538} 539 540static void 541nstat_init_route_provider(void) 542{ 543 bzero(&nstat_route_provider, sizeof(nstat_route_provider)); 544 nstat_route_provider.nstat_descriptor_length = sizeof(nstat_route_descriptor); 545 nstat_route_provider.nstat_provider_id = NSTAT_PROVIDER_ROUTE; 546 nstat_route_provider.nstat_lookup = nstat_route_lookup; 547 nstat_route_provider.nstat_gone = nstat_route_gone; 548 nstat_route_provider.nstat_counts = nstat_route_counts; 549 nstat_route_provider.nstat_release = nstat_route_release; 550 nstat_route_provider.nstat_watcher_add = nstat_route_add_watcher; 551 nstat_route_provider.nstat_watcher_remove = nstat_route_remove_watcher; 552 nstat_route_provider.nstat_copy_descriptor = nstat_route_copy_descriptor; 553 nstat_route_provider.next = nstat_providers; 554 nstat_providers = &nstat_route_provider; 555} 556 557#pragma mark -- Route Collection -- 558 559static struct nstat_counts* 560nstat_route_attach( 561 struct rtentry *rte) 562{ 563 struct nstat_counts *result = rte->rt_stats; 564 if (result) return result; 565 566 if (nstat_malloc_tag == NULL) nstat_init(); 567 568 result = nstat_malloc_aligned(sizeof(*result), sizeof(u_int64_t), nstat_malloc_tag); 569 if (!result) return result; 570 571 bzero(result, sizeof(*result)); 572 573 if (!OSCompareAndSwapPtr(NULL, result, &rte->rt_stats)) 574 { 575 nstat_free_aligned(result, nstat_malloc_tag); 576 result = rte->rt_stats; 577 } 578 579 return result; 580} 581 582__private_extern__ void 583nstat_route_detach( 584 struct rtentry *rte) 585{ 586 if (rte->rt_stats) 587 { 588 nstat_free_aligned(rte->rt_stats, nstat_malloc_tag); 589 rte->rt_stats = NULL; 590 } 591} 592 593__private_extern__ void 594nstat_route_connect_attempt( 595 struct rtentry *rte) 596{ 597 while (rte) 598 { 599 struct nstat_counts* stats = nstat_route_attach(rte); 600 if (stats) 601 { 602 OSIncrementAtomic(&stats->nstat_connectattempts); 603 } 604 605 rte = rte->rt_parent; 606 } 607} 608 609__private_extern__ void 610nstat_route_connect_success( 611 struct rtentry *rte) 612{ 613 // This route 614 while (rte) 615 { 616 struct nstat_counts* stats = nstat_route_attach(rte); 617 if (stats) 618 { 619 OSIncrementAtomic(&stats->nstat_connectsuccesses); 620 } 621 622 rte = rte->rt_parent; 623 } 624} 625 626__private_extern__ void 627nstat_route_tx( 628 struct rtentry *rte, 629 u_int32_t packets, 630 u_int32_t bytes, 631 u_int32_t flags) 632{ 633 while (rte) 634 { 635 struct nstat_counts* stats = nstat_route_attach(rte); 636 if (stats) 637 { 638 if ((flags & NSTAT_TX_FLAG_RETRANSMIT) != 0) 639 { 640 OSAddAtomic(bytes, &stats->nstat_txretransmit); 641 } 642 else 643 { 644 OSAddAtomic64((SInt64)packets, (SInt64*)&stats->nstat_txpackets); 645 OSAddAtomic64((SInt64)bytes, (SInt64*)&stats->nstat_txbytes); 646 } 647 } 648 649 rte = rte->rt_parent; 650 } 651} 652 653__private_extern__ void 654nstat_route_rx( 655 struct rtentry *rte, 656 u_int32_t packets, 657 u_int32_t bytes, 658 u_int32_t flags) 659{ 660 while (rte) 661 { 662 struct nstat_counts* stats = nstat_route_attach(rte); 663 if (stats) 664 { 665 if (flags == 0) 666 { 667 OSAddAtomic64((SInt64)packets, (SInt64*)&stats->nstat_rxpackets); 668 OSAddAtomic64((SInt64)bytes, (SInt64*)&stats->nstat_rxbytes); 669 } 670 else 671 { 672 if (flags & NSTAT_RX_FLAG_OUT_OF_ORDER) 673 OSAddAtomic(bytes, &stats->nstat_rxoutoforderbytes); 674 if (flags & NSTAT_RX_FLAG_DUPLICATE) 675 OSAddAtomic(bytes, &stats->nstat_rxduplicatebytes); 676 } 677 } 678 679 rte = rte->rt_parent; 680 } 681} 682 683__private_extern__ void 684nstat_route_rtt( 685 struct rtentry *rte, 686 u_int32_t rtt, 687 u_int32_t rtt_var) 688{ 689 const int32_t factor = 8; 690 691 while (rte) 692 { 693 struct nstat_counts* stats = nstat_route_attach(rte); 694 if (stats) 695 { 696 int32_t oldrtt; 697 int32_t newrtt; 698 699 // average 700 do 701 { 702 oldrtt = stats->nstat_avg_rtt; 703 if (oldrtt == 0) 704 { 705 newrtt = rtt; 706 } 707 else 708 { 709 newrtt = oldrtt - (oldrtt - (int32_t)rtt) / factor; 710 } 711 if (oldrtt == newrtt) break; 712 } while (!OSCompareAndSwap(oldrtt, newrtt, &stats->nstat_avg_rtt)); 713 714 // minimum 715 do 716 { 717 oldrtt = stats->nstat_min_rtt; 718 if (oldrtt != 0 && oldrtt < (int32_t)rtt) 719 { 720 break; 721 } 722 } while (!OSCompareAndSwap(oldrtt, rtt, &stats->nstat_min_rtt)); 723 724 // variance 725 do 726 { 727 oldrtt = stats->nstat_var_rtt; 728 if (oldrtt == 0) 729 { 730 newrtt = rtt_var; 731 } 732 else 733 { 734 newrtt = oldrtt - (oldrtt - (int32_t)rtt_var) / factor; 735 } 736 if (oldrtt == newrtt) break; 737 } while (!OSCompareAndSwap(oldrtt, newrtt, &stats->nstat_var_rtt)); 738 } 739 740 rte = rte->rt_parent; 741 } 742} 743 744 745#pragma mark -- TCP Provider -- 746 747/* 748 * Due to the way the kernel deallocates a process (the process structure 749 * might be gone by the time we get the PCB detach notification), 750 * we need to cache the process name. Without this, proc_name() would 751 * return null and the process name would never be sent to userland. 752 */ 753struct nstat_tcpudp_cookie { 754 struct inpcb *inp; 755 char pname[MAXCOMLEN+1]; 756}; 757 758static struct nstat_tcpudp_cookie * 759nstat_tcpudp_cookie_alloc( 760 struct inpcb *inp, 761 bool ref) 762{ 763 struct nstat_tcpudp_cookie *cookie; 764 765 cookie = OSMalloc(sizeof(*cookie), nstat_malloc_tag); 766 if (cookie == NULL) 767 return NULL; 768 if (ref && in_pcb_checkstate(inp, WNT_ACQUIRE, 0) == WNT_STOPUSING) 769 { 770 OSFree(cookie, sizeof(*cookie), nstat_malloc_tag); 771 return NULL; 772 } 773 bzero(cookie, sizeof(*cookie)); 774 cookie->inp = inp; 775 proc_name(inp->inp_socket->last_pid, cookie->pname, 776 sizeof(cookie->pname)); 777 778 return cookie; 779} 780 781static void 782nstat_tcpudp_cookie_release( 783 struct nstat_tcpudp_cookie *cookie, 784 int inplock) 785{ 786 in_pcb_checkstate(cookie->inp, WNT_RELEASE, inplock); 787 OSFree(cookie, sizeof(*cookie), nstat_malloc_tag); 788} 789 790static nstat_provider nstat_tcp_provider; 791 792static errno_t 793nstat_tcpudp_lookup( 794 struct inpcbinfo *inpinfo, 795 const void *data, 796 u_int32_t length, 797 nstat_provider_cookie_t *out_cookie) 798{ 799 struct inpcb *inp = NULL; 800 801 // parameter validation 802 const nstat_tcp_add_param *param = (const nstat_tcp_add_param*)data; 803 if (length < sizeof(*param)) 804 { 805 return EINVAL; 806 } 807 808 // src and dst must match 809 if (param->remote.v4.sin_family != 0 && 810 param->remote.v4.sin_family != param->local.v4.sin_family) 811 { 812 return EINVAL; 813 } 814 815 816 switch (param->local.v4.sin_family) 817 { 818 case AF_INET: 819 { 820 if (param->local.v4.sin_len != sizeof(param->local.v4) || 821 (param->remote.v4.sin_family != 0 && 822 param->remote.v4.sin_len != sizeof(param->remote.v4))) 823 { 824 return EINVAL; 825 } 826 827 inp = in_pcblookup_hash(inpinfo, param->remote.v4.sin_addr, param->remote.v4.sin_port, 828 param->local.v4.sin_addr, param->local.v4.sin_port, 1, NULL); 829 } 830 break; 831 832#if INET6 833 case AF_INET6: 834 { 835 union 836 { 837 const struct in6_addr *in6c; 838 struct in6_addr *in6; 839 } local, remote; 840 841 if (param->local.v6.sin6_len != sizeof(param->local.v6) || 842 (param->remote.v6.sin6_family != 0 && 843 param->remote.v6.sin6_len != sizeof(param->remote.v6))) 844 { 845 return EINVAL; 846 } 847 848 local.in6c = ¶m->local.v6.sin6_addr; 849 remote.in6c = ¶m->remote.v6.sin6_addr; 850 851 inp = in6_pcblookup_hash(inpinfo, remote.in6, param->remote.v6.sin6_port, 852 local.in6, param->local.v6.sin6_port, 1, NULL); 853 } 854 break; 855#endif 856 857 default: 858 return EINVAL; 859 } 860 861 if (inp == NULL) 862 return ENOENT; 863 864 // At this point we have a ref to the inpcb 865 *out_cookie = nstat_tcpudp_cookie_alloc(inp, false); 866 if (*out_cookie == NULL) 867 in_pcb_checkstate(inp, WNT_RELEASE, 0); 868 869 return 0; 870} 871 872static errno_t 873nstat_tcp_lookup( 874 const void *data, 875 u_int32_t length, 876 nstat_provider_cookie_t *out_cookie) 877{ 878 return nstat_tcpudp_lookup(&tcbinfo, data, length, out_cookie); 879} 880 881static int 882nstat_tcp_gone( 883 nstat_provider_cookie_t cookie) 884{ 885 struct nstat_tcpudp_cookie *tucookie = 886 (struct nstat_tcpudp_cookie *)cookie; 887 struct inpcb *inp; 888 struct tcpcb *tp; 889 890 return (!(inp = tucookie->inp) || 891 !(tp = intotcpcb(inp)) || 892 inp->inp_state == INPCB_STATE_DEAD || 893 tp->t_state == TCPS_TIME_WAIT) ? 1 : 0; 894} 895 896static errno_t 897nstat_tcp_counts( 898 nstat_provider_cookie_t cookie, 899 struct nstat_counts *out_counts, 900 int *out_gone) 901{ 902 struct nstat_tcpudp_cookie *tucookie = 903 (struct nstat_tcpudp_cookie *)cookie; 904 struct inpcb *inp; 905 906 bzero(out_counts, sizeof(*out_counts)); 907 908 *out_gone = 0; 909 910 // if the pcb is in the dead state, we should stop using it 911 if (nstat_tcp_gone(cookie)) 912 { 913 *out_gone = 1; 914 if (!(inp = tucookie->inp) || !intotcpcb(inp)) 915 return EINVAL; 916 } 917 inp = tucookie->inp; 918 struct tcpcb *tp = intotcpcb(inp); 919 920 atomic_get_64(out_counts->nstat_rxpackets, &inp->inp_stat->rxpackets); 921 atomic_get_64(out_counts->nstat_rxbytes, &inp->inp_stat->rxbytes); 922 atomic_get_64(out_counts->nstat_txpackets, &inp->inp_stat->txpackets); 923 atomic_get_64(out_counts->nstat_txbytes, &inp->inp_stat->txbytes); 924 out_counts->nstat_rxduplicatebytes = tp->t_stat.rxduplicatebytes; 925 out_counts->nstat_rxoutoforderbytes = tp->t_stat.rxoutoforderbytes; 926 out_counts->nstat_txretransmit = tp->t_stat.txretransmitbytes; 927 out_counts->nstat_connectattempts = tp->t_state >= TCPS_SYN_SENT ? 1 : 0; 928 out_counts->nstat_connectsuccesses = tp->t_state >= TCPS_ESTABLISHED ? 1 : 0; 929 out_counts->nstat_avg_rtt = tp->t_srtt; 930 out_counts->nstat_min_rtt = tp->t_rttbest; 931 out_counts->nstat_var_rtt = tp->t_rttvar; 932 if (out_counts->nstat_avg_rtt < out_counts->nstat_min_rtt) 933 out_counts->nstat_min_rtt = out_counts->nstat_avg_rtt; 934 atomic_get_64(out_counts->nstat_cell_rxbytes, &inp->inp_cstat->rxbytes); 935 atomic_get_64(out_counts->nstat_cell_txbytes, &inp->inp_cstat->txbytes); 936 atomic_get_64(out_counts->nstat_wifi_rxbytes, &inp->inp_wstat->rxbytes); 937 atomic_get_64(out_counts->nstat_wifi_txbytes, &inp->inp_wstat->txbytes); 938 939 return 0; 940} 941 942static void 943nstat_tcp_release( 944 nstat_provider_cookie_t cookie, 945 int locked) 946{ 947 struct nstat_tcpudp_cookie *tucookie = 948 (struct nstat_tcpudp_cookie *)cookie; 949 950 nstat_tcpudp_cookie_release(tucookie, locked); 951} 952 953static errno_t 954nstat_tcp_add_watcher( 955 nstat_control_state *state) 956{ 957 OSIncrementAtomic(&nstat_tcp_watchers); 958 959 lck_rw_lock_shared(tcbinfo.ipi_lock); 960 961 // Add all current tcp inpcbs. Ignore those in timewait 962 struct inpcb *inp; 963 struct nstat_tcpudp_cookie *cookie; 964 for (inp = LIST_FIRST(tcbinfo.ipi_listhead); inp; inp = LIST_NEXT(inp, inp_list)) 965 { 966 cookie = nstat_tcpudp_cookie_alloc(inp, true); 967 if (cookie == NULL) 968 continue; 969 if (nstat_control_source_add(0, state, &nstat_tcp_provider, 970 cookie) != 0) 971 { 972 nstat_tcpudp_cookie_release(cookie, false); 973 break; 974 } 975 } 976 977 lck_rw_done(tcbinfo.ipi_lock); 978 979 return 0; 980} 981 982static void 983nstat_tcp_remove_watcher( 984 __unused nstat_control_state *state) 985{ 986 OSDecrementAtomic(&nstat_tcp_watchers); 987} 988 989__private_extern__ void 990nstat_tcp_new_pcb( 991 struct inpcb *inp) 992{ 993 struct nstat_tcpudp_cookie *cookie; 994 995 if (nstat_tcp_watchers == 0) 996 return; 997 998 lck_mtx_lock(&nstat_mtx); 999 nstat_control_state *state; 1000 for (state = nstat_controls; state; state = state->ncs_next) 1001 { 1002 if ((state->ncs_watching & (1 << NSTAT_PROVIDER_TCP)) != 0) 1003 { 1004 // this client is watching tcp 1005 // acquire a reference for it 1006 cookie = nstat_tcpudp_cookie_alloc(inp, true); 1007 if (cookie == NULL) 1008 continue; 1009 // add the source, if that fails, release the reference 1010 if (nstat_control_source_add(0, state, 1011 &nstat_tcp_provider, cookie) != 0) 1012 { 1013 nstat_tcpudp_cookie_release(cookie, false); 1014 break; 1015 } 1016 } 1017 } 1018 lck_mtx_unlock(&nstat_mtx); 1019} 1020 1021__private_extern__ void 1022nstat_pcb_detach(struct inpcb *inp) 1023{ 1024 nstat_control_state *state; 1025 nstat_src *src, *prevsrc; 1026 nstat_src *dead_list = NULL; 1027 struct nstat_tcpudp_cookie *tucookie; 1028 1029 if (inp == NULL || (nstat_tcp_watchers == 0 && nstat_udp_watchers == 0)) 1030 return; 1031 1032 lck_mtx_lock(&nstat_mtx); 1033 for (state = nstat_controls; state; state = state->ncs_next) { 1034 lck_mtx_lock(&state->mtx); 1035 for (prevsrc = NULL, src = state->ncs_srcs; src; 1036 prevsrc = src, src = src->next) 1037 { 1038 tucookie = (struct nstat_tcpudp_cookie *)src->cookie; 1039 if (tucookie->inp == inp) 1040 break; 1041 } 1042 1043 if (src) { 1044 // send one last counts notification 1045 nstat_control_send_counts(state, src, 0, NULL); 1046 1047 // send a last description 1048 nstat_control_send_description(state, src, 0); 1049 1050 // send the source removed notification 1051 nstat_control_send_removed(state, src); 1052 1053 if (prevsrc) 1054 prevsrc->next = src->next; 1055 else 1056 state->ncs_srcs = src->next; 1057 1058 src->next = dead_list; 1059 dead_list = src; 1060 } 1061 lck_mtx_unlock(&state->mtx); 1062 } 1063 lck_mtx_unlock(&nstat_mtx); 1064 1065 while (dead_list) { 1066 src = dead_list; 1067 dead_list = src->next; 1068 1069 nstat_control_cleanup_source(NULL, src, TRUE); 1070 } 1071} 1072 1073static errno_t 1074nstat_tcp_copy_descriptor( 1075 nstat_provider_cookie_t cookie, 1076 void *data, 1077 u_int32_t len) 1078{ 1079 if (len < sizeof(nstat_tcp_descriptor)) 1080 { 1081 return EINVAL; 1082 } 1083 1084 if (nstat_tcp_gone(cookie)) 1085 return EINVAL; 1086 1087 nstat_tcp_descriptor *desc = (nstat_tcp_descriptor*)data; 1088 struct nstat_tcpudp_cookie *tucookie = 1089 (struct nstat_tcpudp_cookie *)cookie; 1090 struct inpcb *inp = tucookie->inp; 1091 struct tcpcb *tp = intotcpcb(inp); 1092 bzero(desc, sizeof(*desc)); 1093 1094 if (inp->inp_vflag & INP_IPV6) 1095 { 1096 nstat_ip6_to_sockaddr(&inp->in6p_laddr, inp->inp_lport, 1097 &desc->local.v6, sizeof(desc->local)); 1098 nstat_ip6_to_sockaddr(&inp->in6p_faddr, inp->inp_fport, 1099 &desc->remote.v6, sizeof(desc->remote)); 1100 } 1101 else if (inp->inp_vflag & INP_IPV4) 1102 { 1103 nstat_ip_to_sockaddr(&inp->inp_laddr, inp->inp_lport, 1104 &desc->local.v4, sizeof(desc->local)); 1105 nstat_ip_to_sockaddr(&inp->inp_faddr, inp->inp_fport, 1106 &desc->remote.v4, sizeof(desc->remote)); 1107 } 1108 1109 desc->state = intotcpcb(inp)->t_state; 1110 desc->ifindex = (inp->inp_last_outifp == NULL) ? 0 : 1111 inp->inp_last_outifp->if_index; 1112 1113 // danger - not locked, values could be bogus 1114 desc->txunacked = tp->snd_max - tp->snd_una; 1115 desc->txwindow = tp->snd_wnd; 1116 desc->txcwindow = tp->snd_cwnd; 1117 1118 struct socket *so = inp->inp_socket; 1119 if (so) 1120 { 1121 // TBD - take the socket lock around these to make sure 1122 // they're in sync? 1123 desc->upid = so->last_upid; 1124 desc->pid = so->last_pid; 1125 desc->traffic_class = so->so_traffic_class; 1126 proc_name(desc->pid, desc->pname, sizeof(desc->pname)); 1127 if (desc->pname == NULL || desc->pname[0] == 0) 1128 { 1129 strlcpy(desc->pname, tucookie->pname, 1130 sizeof(desc->pname)); 1131 } 1132 else 1133 { 1134 desc->pname[sizeof(desc->pname) - 1] = 0; 1135 strlcpy(tucookie->pname, desc->pname, 1136 sizeof(tucookie->pname)); 1137 } 1138 memcpy(desc->uuid, so->last_uuid, sizeof(so->last_uuid)); 1139 if (so->so_flags & SOF_DELEGATED) { 1140 desc->eupid = so->e_upid; 1141 desc->epid = so->e_pid; 1142 memcpy(desc->euuid, so->e_uuid, sizeof(so->e_uuid)); 1143 } else { 1144 desc->eupid = desc->upid; 1145 desc->epid = desc->pid; 1146 memcpy(desc->euuid, desc->uuid, sizeof(desc->uuid)); 1147 } 1148 desc->sndbufsize = so->so_snd.sb_hiwat; 1149 desc->sndbufused = so->so_snd.sb_cc; 1150 desc->rcvbufsize = so->so_rcv.sb_hiwat; 1151 desc->rcvbufused = so->so_rcv.sb_cc; 1152 } 1153 1154 return 0; 1155} 1156 1157static void 1158nstat_init_tcp_provider(void) 1159{ 1160 bzero(&nstat_tcp_provider, sizeof(nstat_tcp_provider)); 1161 nstat_tcp_provider.nstat_descriptor_length = sizeof(nstat_tcp_descriptor); 1162 nstat_tcp_provider.nstat_provider_id = NSTAT_PROVIDER_TCP; 1163 nstat_tcp_provider.nstat_lookup = nstat_tcp_lookup; 1164 nstat_tcp_provider.nstat_gone = nstat_tcp_gone; 1165 nstat_tcp_provider.nstat_counts = nstat_tcp_counts; 1166 nstat_tcp_provider.nstat_release = nstat_tcp_release; 1167 nstat_tcp_provider.nstat_watcher_add = nstat_tcp_add_watcher; 1168 nstat_tcp_provider.nstat_watcher_remove = nstat_tcp_remove_watcher; 1169 nstat_tcp_provider.nstat_copy_descriptor = nstat_tcp_copy_descriptor; 1170 nstat_tcp_provider.next = nstat_providers; 1171 nstat_providers = &nstat_tcp_provider; 1172} 1173 1174#pragma mark -- UDP Provider -- 1175 1176static nstat_provider nstat_udp_provider; 1177 1178static errno_t 1179nstat_udp_lookup( 1180 const void *data, 1181 u_int32_t length, 1182 nstat_provider_cookie_t *out_cookie) 1183{ 1184 return nstat_tcpudp_lookup(&udbinfo, data, length, out_cookie); 1185} 1186 1187static int 1188nstat_udp_gone( 1189 nstat_provider_cookie_t cookie) 1190{ 1191 struct nstat_tcpudp_cookie *tucookie = 1192 (struct nstat_tcpudp_cookie *)cookie; 1193 struct inpcb *inp; 1194 1195 return (!(inp = tucookie->inp) || 1196 inp->inp_state == INPCB_STATE_DEAD) ? 1 : 0; 1197} 1198 1199static errno_t 1200nstat_udp_counts( 1201 nstat_provider_cookie_t cookie, 1202 struct nstat_counts *out_counts, 1203 int *out_gone) 1204{ 1205 struct nstat_tcpudp_cookie *tucookie = 1206 (struct nstat_tcpudp_cookie *)cookie; 1207 1208 *out_gone = 0; 1209 1210 // if the pcb is in the dead state, we should stop using it 1211 if (nstat_udp_gone(cookie)) 1212 { 1213 *out_gone = 1; 1214 if (!tucookie->inp) 1215 return EINVAL; 1216 } 1217 struct inpcb *inp = tucookie->inp; 1218 1219 atomic_get_64(out_counts->nstat_rxpackets, &inp->inp_stat->rxpackets); 1220 atomic_get_64(out_counts->nstat_rxbytes, &inp->inp_stat->rxbytes); 1221 atomic_get_64(out_counts->nstat_txpackets, &inp->inp_stat->txpackets); 1222 atomic_get_64(out_counts->nstat_txbytes, &inp->inp_stat->txbytes); 1223 atomic_get_64(out_counts->nstat_cell_rxbytes, &inp->inp_cstat->rxbytes); 1224 atomic_get_64(out_counts->nstat_cell_txbytes, &inp->inp_cstat->txbytes); 1225 atomic_get_64(out_counts->nstat_wifi_rxbytes, &inp->inp_wstat->rxbytes); 1226 atomic_get_64(out_counts->nstat_wifi_txbytes, &inp->inp_wstat->txbytes); 1227 1228 return 0; 1229} 1230 1231static void 1232nstat_udp_release( 1233 nstat_provider_cookie_t cookie, 1234 int locked) 1235{ 1236 struct nstat_tcpudp_cookie *tucookie = 1237 (struct nstat_tcpudp_cookie *)cookie; 1238 1239 nstat_tcpudp_cookie_release(tucookie, locked); 1240} 1241 1242static errno_t 1243nstat_udp_add_watcher( 1244 nstat_control_state *state) 1245{ 1246 struct inpcb *inp; 1247 struct nstat_tcpudp_cookie *cookie; 1248 1249 OSIncrementAtomic(&nstat_udp_watchers); 1250 1251 lck_rw_lock_shared(udbinfo.ipi_lock); 1252 // Add all current UDP inpcbs. 1253 for (inp = LIST_FIRST(udbinfo.ipi_listhead); inp; inp = LIST_NEXT(inp, inp_list)) 1254 { 1255 cookie = nstat_tcpudp_cookie_alloc(inp, true); 1256 if (cookie == NULL) 1257 continue; 1258 if (nstat_control_source_add(0, state, &nstat_udp_provider, 1259 cookie) != 0) 1260 { 1261 nstat_tcpudp_cookie_release(cookie, false); 1262 break; 1263 } 1264 } 1265 1266 lck_rw_done(udbinfo.ipi_lock); 1267 1268 return 0; 1269} 1270 1271static void 1272nstat_udp_remove_watcher( 1273 __unused nstat_control_state *state) 1274{ 1275 OSDecrementAtomic(&nstat_udp_watchers); 1276} 1277 1278__private_extern__ void 1279nstat_udp_new_pcb( 1280 struct inpcb *inp) 1281{ 1282 struct nstat_tcpudp_cookie *cookie; 1283 1284 if (nstat_udp_watchers == 0) 1285 return; 1286 1287 lck_mtx_lock(&nstat_mtx); 1288 nstat_control_state *state; 1289 for (state = nstat_controls; state; state = state->ncs_next) 1290 { 1291 if ((state->ncs_watching & (1 << NSTAT_PROVIDER_UDP)) != 0) 1292 { 1293 // this client is watching tcp 1294 // acquire a reference for it 1295 cookie = nstat_tcpudp_cookie_alloc(inp, true); 1296 if (cookie == NULL) 1297 continue; 1298 // add the source, if that fails, release the reference 1299 if (nstat_control_source_add(0, state, 1300 &nstat_udp_provider, cookie) != 0) 1301 { 1302 nstat_tcpudp_cookie_release(cookie, false); 1303 break; 1304 } 1305 } 1306 } 1307 lck_mtx_unlock(&nstat_mtx); 1308} 1309 1310static errno_t 1311nstat_udp_copy_descriptor( 1312 nstat_provider_cookie_t cookie, 1313 void *data, 1314 u_int32_t len) 1315{ 1316 if (len < sizeof(nstat_udp_descriptor)) 1317 { 1318 return EINVAL; 1319 } 1320 1321 if (nstat_udp_gone(cookie)) 1322 return EINVAL; 1323 1324 struct nstat_tcpudp_cookie *tucookie = 1325 (struct nstat_tcpudp_cookie *)cookie; 1326 nstat_udp_descriptor *desc = (nstat_udp_descriptor*)data; 1327 struct inpcb *inp = tucookie->inp; 1328 1329 bzero(desc, sizeof(*desc)); 1330 1331 if (inp->inp_vflag & INP_IPV6) 1332 { 1333 nstat_ip6_to_sockaddr(&inp->in6p_laddr, inp->inp_lport, 1334 &desc->local.v6, sizeof(desc->local)); 1335 nstat_ip6_to_sockaddr(&inp->in6p_faddr, inp->inp_fport, 1336 &desc->remote.v6, sizeof(desc->remote)); 1337 } 1338 else if (inp->inp_vflag & INP_IPV4) 1339 { 1340 nstat_ip_to_sockaddr(&inp->inp_laddr, inp->inp_lport, 1341 &desc->local.v4, sizeof(desc->local)); 1342 nstat_ip_to_sockaddr(&inp->inp_faddr, inp->inp_fport, 1343 &desc->remote.v4, sizeof(desc->remote)); 1344 } 1345 1346 desc->ifindex = (inp->inp_last_outifp == NULL) ? 0 : 1347 inp->inp_last_outifp->if_index; 1348 1349 struct socket *so = inp->inp_socket; 1350 if (so) 1351 { 1352 // TBD - take the socket lock around these to make sure 1353 // they're in sync? 1354 desc->upid = so->last_upid; 1355 desc->pid = so->last_pid; 1356 proc_name(desc->pid, desc->pname, sizeof(desc->pname)); 1357 if (desc->pname == NULL || desc->pname[0] == 0) 1358 { 1359 strlcpy(desc->pname, tucookie->pname, 1360 sizeof(desc->pname)); 1361 } 1362 else 1363 { 1364 desc->pname[sizeof(desc->pname) - 1] = 0; 1365 strlcpy(tucookie->pname, desc->pname, 1366 sizeof(tucookie->pname)); 1367 } 1368 memcpy(desc->uuid, so->last_uuid, sizeof(so->last_uuid)); 1369 if (so->so_flags & SOF_DELEGATED) { 1370 desc->eupid = so->e_upid; 1371 desc->epid = so->e_pid; 1372 memcpy(desc->euuid, so->e_uuid, sizeof(so->e_uuid)); 1373 } else { 1374 desc->eupid = desc->upid; 1375 desc->epid = desc->pid; 1376 memcpy(desc->euuid, desc->uuid, sizeof(desc->uuid)); 1377 } 1378 desc->rcvbufsize = so->so_rcv.sb_hiwat; 1379 desc->rcvbufused = so->so_rcv.sb_cc; 1380 desc->traffic_class = so->so_traffic_class; 1381 } 1382 1383 return 0; 1384} 1385 1386static void 1387nstat_init_udp_provider(void) 1388{ 1389 bzero(&nstat_udp_provider, sizeof(nstat_udp_provider)); 1390 nstat_udp_provider.nstat_provider_id = NSTAT_PROVIDER_UDP; 1391 nstat_udp_provider.nstat_descriptor_length = sizeof(nstat_udp_descriptor); 1392 nstat_udp_provider.nstat_lookup = nstat_udp_lookup; 1393 nstat_udp_provider.nstat_gone = nstat_udp_gone; 1394 nstat_udp_provider.nstat_counts = nstat_udp_counts; 1395 nstat_udp_provider.nstat_watcher_add = nstat_udp_add_watcher; 1396 nstat_udp_provider.nstat_watcher_remove = nstat_udp_remove_watcher; 1397 nstat_udp_provider.nstat_copy_descriptor = nstat_udp_copy_descriptor; 1398 nstat_udp_provider.nstat_release = nstat_udp_release; 1399 nstat_udp_provider.next = nstat_providers; 1400 nstat_providers = &nstat_udp_provider; 1401} 1402 1403#pragma mark -- ifnet Provider -- 1404 1405static nstat_provider nstat_ifnet_provider; 1406 1407/* 1408 * We store a pointer to the ifnet and the original threshold 1409 * requested by the client. 1410 */ 1411struct nstat_ifnet_cookie 1412{ 1413 struct ifnet *ifp; 1414 uint64_t threshold; 1415}; 1416 1417static errno_t 1418nstat_ifnet_lookup( 1419 const void *data, 1420 u_int32_t length, 1421 nstat_provider_cookie_t *out_cookie) 1422{ 1423 const nstat_ifnet_add_param *param = (nstat_ifnet_add_param *)data; 1424 struct ifnet *ifp; 1425 boolean_t changed = FALSE; 1426 nstat_control_state *state; 1427 nstat_src *src; 1428 struct nstat_ifnet_cookie *cookie; 1429 1430 if (length < sizeof(*param) || param->threshold < 1024*1024) 1431 return EINVAL; 1432 if (nstat_privcheck != 0) { 1433 errno_t result = priv_check_cred(kauth_cred_get(), 1434 PRIV_NET_PRIVILEGED_NETWORK_STATISTICS, 0); 1435 if (result != 0) 1436 return result; 1437 } 1438 cookie = OSMalloc(sizeof(*cookie), nstat_malloc_tag); 1439 if (cookie == NULL) 1440 return ENOMEM; 1441 bzero(cookie, sizeof(*cookie)); 1442 1443 ifnet_head_lock_shared(); 1444 TAILQ_FOREACH(ifp, &ifnet_head, if_link) 1445 { 1446 ifnet_lock_exclusive(ifp); 1447 if (ifp->if_index == param->ifindex) 1448 { 1449 cookie->ifp = ifp; 1450 cookie->threshold = param->threshold; 1451 *out_cookie = cookie; 1452 if (!ifp->if_data_threshold || 1453 ifp->if_data_threshold > param->threshold) 1454 { 1455 changed = TRUE; 1456 ifp->if_data_threshold = param->threshold; 1457 } 1458 ifnet_lock_done(ifp); 1459 ifnet_reference(ifp); 1460 break; 1461 } 1462 ifnet_lock_done(ifp); 1463 } 1464 ifnet_head_done(); 1465 1466 /* 1467 * When we change the threshold to something smaller, we notify 1468 * all of our clients with a description message. 1469 * We won't send a message to the client we are currently serving 1470 * because it has no `ifnet source' yet. 1471 */ 1472 if (changed) 1473 { 1474 lck_mtx_lock(&nstat_mtx); 1475 for (state = nstat_controls; state; state = state->ncs_next) 1476 { 1477 lck_mtx_lock(&state->mtx); 1478 for (src = state->ncs_srcs; src; src = src->next) 1479 { 1480 if (src->provider != &nstat_ifnet_provider) 1481 continue; 1482 nstat_control_send_description(state, src, 0); 1483 } 1484 lck_mtx_unlock(&state->mtx); 1485 } 1486 lck_mtx_unlock(&nstat_mtx); 1487 } 1488 if (cookie->ifp == NULL) 1489 OSFree(cookie, sizeof(*cookie), nstat_malloc_tag); 1490 1491 return ifp ? 0 : EINVAL; 1492} 1493 1494static int 1495nstat_ifnet_gone( 1496 nstat_provider_cookie_t cookie) 1497{ 1498 struct ifnet *ifp; 1499 struct nstat_ifnet_cookie *ifcookie = 1500 (struct nstat_ifnet_cookie *)cookie; 1501 1502 ifnet_head_lock_shared(); 1503 TAILQ_FOREACH(ifp, &ifnet_head, if_link) 1504 { 1505 if (ifp == ifcookie->ifp) 1506 break; 1507 } 1508 ifnet_head_done(); 1509 1510 return ifp ? 0 : 1; 1511} 1512 1513static errno_t 1514nstat_ifnet_counts( 1515 nstat_provider_cookie_t cookie, 1516 struct nstat_counts *out_counts, 1517 int *out_gone) 1518{ 1519 struct nstat_ifnet_cookie *ifcookie = 1520 (struct nstat_ifnet_cookie *)cookie; 1521 struct ifnet *ifp = ifcookie->ifp; 1522 1523 *out_gone = 0; 1524 1525 // if the ifnet is gone, we should stop using it 1526 if (nstat_ifnet_gone(cookie)) 1527 { 1528 *out_gone = 1; 1529 return EINVAL; 1530 } 1531 1532 bzero(out_counts, sizeof(*out_counts)); 1533 out_counts->nstat_rxpackets = ifp->if_ipackets; 1534 out_counts->nstat_rxbytes = ifp->if_ibytes; 1535 out_counts->nstat_txpackets = ifp->if_opackets; 1536 out_counts->nstat_txbytes = ifp->if_obytes; 1537 out_counts->nstat_cell_rxbytes = out_counts->nstat_cell_txbytes = 0; 1538 1539 return 0; 1540} 1541 1542static void 1543nstat_ifnet_release( 1544 nstat_provider_cookie_t cookie, 1545 __unused int locked) 1546{ 1547 struct nstat_ifnet_cookie *ifcookie; 1548 struct ifnet *ifp; 1549 nstat_control_state *state; 1550 nstat_src *src; 1551 uint64_t minthreshold = UINT64_MAX; 1552 1553 /* 1554 * Find all the clients that requested a threshold 1555 * for this ifnet and re-calculate if_data_threshold. 1556 */ 1557 lck_mtx_lock(&nstat_mtx); 1558 for (state = nstat_controls; state; state = state->ncs_next) 1559 { 1560 lck_mtx_lock(&state->mtx); 1561 for (src = state->ncs_srcs; src; src = src->next) 1562 { 1563 /* Skip the provider we are about to detach. */ 1564 if (src->provider != &nstat_ifnet_provider || 1565 src->cookie == cookie) 1566 continue; 1567 ifcookie = (struct nstat_ifnet_cookie *)src->cookie; 1568 if (ifcookie->threshold < minthreshold) 1569 minthreshold = ifcookie->threshold; 1570 } 1571 lck_mtx_unlock(&state->mtx); 1572 } 1573 lck_mtx_unlock(&nstat_mtx); 1574 /* 1575 * Reset if_data_threshold or disable it. 1576 */ 1577 ifcookie = (struct nstat_ifnet_cookie *)cookie; 1578 ifp = ifcookie->ifp; 1579 if (ifnet_is_attached(ifp, 1)) { 1580 ifnet_lock_exclusive(ifp); 1581 if (minthreshold == UINT64_MAX) 1582 ifp->if_data_threshold = 0; 1583 else 1584 ifp->if_data_threshold = minthreshold; 1585 ifnet_lock_done(ifp); 1586 ifnet_decr_iorefcnt(ifp); 1587 } 1588 ifnet_release(ifp); 1589 OSFree(ifcookie, sizeof(*ifcookie), nstat_malloc_tag); 1590} 1591 1592static errno_t 1593nstat_ifnet_copy_descriptor( 1594 nstat_provider_cookie_t cookie, 1595 void *data, 1596 u_int32_t len) 1597{ 1598 nstat_ifnet_descriptor *desc = (nstat_ifnet_descriptor *)data; 1599 struct nstat_ifnet_cookie *ifcookie = 1600 (struct nstat_ifnet_cookie *)cookie; 1601 struct ifnet *ifp = ifcookie->ifp; 1602 1603 if (len < sizeof(nstat_ifnet_descriptor)) 1604 return EINVAL; 1605 1606 if (nstat_ifnet_gone(cookie)) 1607 return EINVAL; 1608 1609 bzero(desc, sizeof(*desc)); 1610 ifnet_lock_shared(ifp); 1611 strlcpy(desc->name, ifp->if_xname, sizeof(desc->name)); 1612 desc->ifindex = ifp->if_index; 1613 desc->threshold = ifp->if_data_threshold; 1614 desc->type = ifp->if_type; 1615 if (ifp->if_desc.ifd_len < sizeof(desc->description)) 1616 memcpy(desc->description, ifp->if_desc.ifd_desc, 1617 sizeof(desc->description)); 1618 ifnet_lock_done(ifp); 1619 1620 return 0; 1621} 1622 1623static void 1624nstat_init_ifnet_provider(void) 1625{ 1626 bzero(&nstat_ifnet_provider, sizeof(nstat_ifnet_provider)); 1627 nstat_ifnet_provider.nstat_provider_id = NSTAT_PROVIDER_IFNET; 1628 nstat_ifnet_provider.nstat_descriptor_length = sizeof(nstat_ifnet_descriptor); 1629 nstat_ifnet_provider.nstat_lookup = nstat_ifnet_lookup; 1630 nstat_ifnet_provider.nstat_gone = nstat_ifnet_gone; 1631 nstat_ifnet_provider.nstat_counts = nstat_ifnet_counts; 1632 nstat_ifnet_provider.nstat_watcher_add = NULL; 1633 nstat_ifnet_provider.nstat_watcher_remove = NULL; 1634 nstat_ifnet_provider.nstat_copy_descriptor = nstat_ifnet_copy_descriptor; 1635 nstat_ifnet_provider.nstat_release = nstat_ifnet_release; 1636 nstat_ifnet_provider.next = nstat_providers; 1637 nstat_providers = &nstat_ifnet_provider; 1638} 1639 1640__private_extern__ void 1641nstat_ifnet_threshold_reached(unsigned int ifindex) 1642{ 1643 nstat_control_state *state; 1644 nstat_src *src; 1645 struct ifnet *ifp; 1646 struct nstat_ifnet_cookie *ifcookie; 1647 1648 lck_mtx_lock(&nstat_mtx); 1649 for (state = nstat_controls; state; state = state->ncs_next) 1650 { 1651 lck_mtx_lock(&state->mtx); 1652 for (src = state->ncs_srcs; src; src = src->next) 1653 { 1654 if (src->provider != &nstat_ifnet_provider) 1655 continue; 1656 ifcookie = (struct nstat_ifnet_cookie *)src->cookie; 1657 ifp = ifcookie->ifp; 1658 if (ifp->if_index != ifindex) 1659 continue; 1660 nstat_control_send_counts(state, src, 0, NULL); 1661 } 1662 lck_mtx_unlock(&state->mtx); 1663 } 1664 lck_mtx_unlock(&nstat_mtx); 1665} 1666 1667#pragma mark -- Kernel Control Socket -- 1668 1669static kern_ctl_ref nstat_ctlref = NULL; 1670static lck_grp_t *nstat_lck_grp = NULL; 1671 1672static errno_t nstat_control_connect(kern_ctl_ref kctl, struct sockaddr_ctl *sac, void **uinfo); 1673static errno_t nstat_control_disconnect(kern_ctl_ref kctl, u_int32_t unit, void *uinfo); 1674static errno_t nstat_control_send(kern_ctl_ref kctl, u_int32_t unit, void *uinfo, mbuf_t m, int flags); 1675 1676 1677 1678static void* 1679nstat_idle_check( 1680 __unused thread_call_param_t p0, 1681 __unused thread_call_param_t p1) 1682{ 1683 lck_mtx_lock(&nstat_mtx); 1684 1685 nstat_idle_time = 0; 1686 1687 nstat_control_state *control; 1688 nstat_src *dead = NULL; 1689 nstat_src *dead_list = NULL; 1690 for (control = nstat_controls; control; control = control->ncs_next) 1691 { 1692 lck_mtx_lock(&control->mtx); 1693 nstat_src **srcpp = &control->ncs_srcs; 1694 1695 if (!(control->ncs_flags & NSTAT_FLAG_REQCOUNTS)) 1696 { 1697 while(*srcpp != NULL) 1698 { 1699 if ((*srcpp)->provider->nstat_gone((*srcpp)->cookie)) 1700 { 1701 // Pull it off the list 1702 dead = *srcpp; 1703 *srcpp = (*srcpp)->next; 1704 1705 // send one last counts notification 1706 nstat_control_send_counts(control, dead, 1707 0, NULL); 1708 1709 // send a last description 1710 nstat_control_send_description(control, dead, 0); 1711 1712 // send the source removed notification 1713 nstat_control_send_removed(control, dead); 1714 1715 // Put this on the list to release later 1716 dead->next = dead_list; 1717 dead_list = dead; 1718 } 1719 else 1720 { 1721 srcpp = &(*srcpp)->next; 1722 } 1723 } 1724 } 1725 control->ncs_flags &= ~NSTAT_FLAG_REQCOUNTS; 1726 lck_mtx_unlock(&control->mtx); 1727 } 1728 1729 if (nstat_controls) 1730 { 1731 clock_interval_to_deadline(60, NSEC_PER_SEC, &nstat_idle_time); 1732 thread_call_func_delayed((thread_call_func_t)nstat_idle_check, NULL, nstat_idle_time); 1733 } 1734 1735 lck_mtx_unlock(&nstat_mtx); 1736 1737 // Release the sources now that we aren't holding lots of locks 1738 while (dead_list) 1739 { 1740 dead = dead_list; 1741 dead_list = dead->next; 1742 1743 nstat_control_cleanup_source(NULL, dead, FALSE); 1744 } 1745 1746 return NULL; 1747} 1748 1749static void 1750nstat_control_register(void) 1751{ 1752 // Create our lock group first 1753 lck_grp_attr_t *grp_attr = lck_grp_attr_alloc_init(); 1754 lck_grp_attr_setdefault(grp_attr); 1755 nstat_lck_grp = lck_grp_alloc_init("network statistics kctl", grp_attr); 1756 lck_grp_attr_free(grp_attr); 1757 1758 lck_mtx_init(&nstat_mtx, nstat_lck_grp, NULL); 1759 1760 // Register the control 1761 struct kern_ctl_reg nstat_control; 1762 bzero(&nstat_control, sizeof(nstat_control)); 1763 strlcpy(nstat_control.ctl_name, NET_STAT_CONTROL_NAME, sizeof(nstat_control.ctl_name)); 1764 nstat_control.ctl_connect = nstat_control_connect; 1765 nstat_control.ctl_disconnect = nstat_control_disconnect; 1766 nstat_control.ctl_send = nstat_control_send; 1767 1768 ctl_register(&nstat_control, &nstat_ctlref); 1769} 1770 1771static void 1772nstat_control_cleanup_source( 1773 nstat_control_state *state, 1774 struct nstat_src *src, 1775 boolean_t locked) 1776{ 1777 if (state) 1778 nstat_control_send_removed(state, src); 1779 1780 // Cleanup the source if we found it. 1781 src->provider->nstat_release(src->cookie, locked); 1782 OSFree(src, sizeof(*src), nstat_malloc_tag); 1783} 1784 1785static errno_t 1786nstat_control_connect( 1787 kern_ctl_ref kctl, 1788 struct sockaddr_ctl *sac, 1789 void **uinfo) 1790{ 1791 nstat_control_state *state = OSMalloc(sizeof(*state), nstat_malloc_tag); 1792 if (state == NULL) return ENOMEM; 1793 1794 bzero(state, sizeof(*state)); 1795 lck_mtx_init(&state->mtx, nstat_lck_grp, NULL); 1796 state->ncs_kctl = kctl; 1797 state->ncs_unit = sac->sc_unit; 1798 state->ncs_flags = NSTAT_FLAG_REQCOUNTS; 1799 *uinfo = state; 1800 1801 lck_mtx_lock(&nstat_mtx); 1802 state->ncs_next = nstat_controls; 1803 nstat_controls = state; 1804 1805 if (nstat_idle_time == 0) 1806 { 1807 clock_interval_to_deadline(60, NSEC_PER_SEC, &nstat_idle_time); 1808 thread_call_func_delayed((thread_call_func_t)nstat_idle_check, NULL, nstat_idle_time); 1809 } 1810 1811 lck_mtx_unlock(&nstat_mtx); 1812 1813 return 0; 1814} 1815 1816static errno_t 1817nstat_control_disconnect( 1818 __unused kern_ctl_ref kctl, 1819 __unused u_int32_t unit, 1820 void *uinfo) 1821{ 1822 u_int32_t watching; 1823 nstat_control_state *state = (nstat_control_state*)uinfo; 1824 1825 // pull it out of the global list of states 1826 lck_mtx_lock(&nstat_mtx); 1827 nstat_control_state **statepp; 1828 for (statepp = &nstat_controls; *statepp; statepp = &(*statepp)->ncs_next) 1829 { 1830 if (*statepp == state) 1831 { 1832 *statepp = state->ncs_next; 1833 break; 1834 } 1835 } 1836 lck_mtx_unlock(&nstat_mtx); 1837 1838 lck_mtx_lock(&state->mtx); 1839 // Stop watching for sources 1840 nstat_provider *provider; 1841 watching = state->ncs_watching; 1842 state->ncs_watching = 0; 1843 for (provider = nstat_providers; provider && watching; provider = provider->next) 1844 { 1845 if ((watching & (1 << provider->nstat_provider_id)) != 0) 1846 { 1847 watching &= ~(1 << provider->nstat_provider_id); 1848 provider->nstat_watcher_remove(state); 1849 } 1850 } 1851 1852 // set cleanup flags 1853 state->ncs_flags |= NSTAT_FLAG_CLEANUP; 1854 1855 // Copy out the list of sources 1856 nstat_src *srcs = state->ncs_srcs; 1857 state->ncs_srcs = NULL; 1858 lck_mtx_unlock(&state->mtx); 1859 1860 while (srcs) 1861 { 1862 nstat_src *src; 1863 1864 // pull it out of the list 1865 src = srcs; 1866 srcs = src->next; 1867 1868 // clean it up 1869 nstat_control_cleanup_source(NULL, src, FALSE); 1870 } 1871 lck_mtx_destroy(&state->mtx, nstat_lck_grp); 1872 OSFree(state, sizeof(*state), nstat_malloc_tag); 1873 1874 return 0; 1875} 1876 1877static nstat_src_ref_t 1878nstat_control_next_src_ref( 1879 nstat_control_state *state) 1880{ 1881 int i = 0; 1882 nstat_src_ref_t toReturn = NSTAT_SRC_REF_INVALID; 1883 1884 for (i = 0; i < 1000 && toReturn == NSTAT_SRC_REF_INVALID; i++) 1885 { 1886 if (state->ncs_next_srcref == NSTAT_SRC_REF_INVALID || 1887 state->ncs_next_srcref == NSTAT_SRC_REF_ALL) 1888 { 1889 state->ncs_next_srcref = 1; 1890 } 1891 1892 nstat_src *src; 1893 for (src = state->ncs_srcs; src; src = src->next) 1894 { 1895 if (src->srcref == state->ncs_next_srcref) 1896 break; 1897 } 1898 1899 if (src == NULL) toReturn = state->ncs_next_srcref; 1900 state->ncs_next_srcref++; 1901 } 1902 1903 return toReturn; 1904} 1905 1906static errno_t 1907nstat_control_send_counts( 1908 nstat_control_state *state, 1909 nstat_src *src, 1910 unsigned long long context, 1911 int *gone) 1912{ 1913 nstat_msg_src_counts counts; 1914 int localgone = 0; 1915 errno_t result = 0; 1916 1917 counts.hdr.type = NSTAT_MSG_TYPE_SRC_COUNTS; 1918 counts.hdr.context = context; 1919 counts.srcref = src->srcref; 1920 bzero(&counts.counts, sizeof(counts.counts)); 1921 if (src->provider->nstat_counts(src->cookie, &counts.counts, 1922 &localgone) == 0) { 1923 if ((src->filter & NSTAT_FILTER_NOZEROBYTES) && 1924 counts.counts.nstat_rxbytes == 0 && 1925 counts.counts.nstat_txbytes == 0) 1926 result = EAGAIN; 1927 else 1928 result = ctl_enqueuedata(state->ncs_kctl, 1929 state->ncs_unit, &counts, sizeof(counts), 1930 CTL_DATA_EOR); 1931 } 1932 if (gone) 1933 *gone = localgone; 1934 return result; 1935} 1936 1937static int 1938nstat_control_send_description( 1939 nstat_control_state *state, 1940 nstat_src *src, 1941 u_int64_t context) 1942{ 1943 // Provider doesn't support getting the descriptor? Done. 1944 if (src->provider->nstat_descriptor_length == 0 || 1945 src->provider->nstat_copy_descriptor == NULL) 1946 { 1947 return EOPNOTSUPP; 1948 } 1949 1950 // Allocate storage for the descriptor message 1951 mbuf_t msg; 1952 unsigned int one = 1; 1953 u_int32_t size = offsetof(nstat_msg_src_description, data) + src->provider->nstat_descriptor_length; 1954 if (mbuf_allocpacket(MBUF_WAITOK, size, &one, &msg) != 0) 1955 { 1956 return ENOMEM; 1957 } 1958 1959 nstat_msg_src_description *desc = (nstat_msg_src_description*)mbuf_data(msg); 1960 mbuf_setlen(msg, size); 1961 mbuf_pkthdr_setlen(msg, mbuf_len(msg)); 1962 1963 // Query the provider for the provider specific bits 1964 errno_t result = src->provider->nstat_copy_descriptor(src->cookie, desc->data, src->provider->nstat_descriptor_length); 1965 1966 if (result != 0) 1967 { 1968 mbuf_freem(msg); 1969 return result; 1970 } 1971 1972 desc->hdr.context = context; 1973 desc->hdr.type = NSTAT_MSG_TYPE_SRC_DESC; 1974 desc->srcref = src->srcref; 1975 desc->provider = src->provider->nstat_provider_id; 1976 1977 result = ctl_enqueuembuf(state->ncs_kctl, state->ncs_unit, msg, CTL_DATA_EOR); 1978 if (result != 0) 1979 { 1980 mbuf_freem(msg); 1981 } 1982 1983 return result; 1984} 1985 1986static errno_t 1987nstat_control_send_removed( 1988 nstat_control_state *state, 1989 nstat_src *src) 1990{ 1991 nstat_msg_src_removed removed; 1992 errno_t result; 1993 1994 removed.hdr.type = NSTAT_MSG_TYPE_SRC_REMOVED; 1995 removed.hdr.context = 0; 1996 removed.srcref = src->srcref; 1997 result = ctl_enqueuedata(state->ncs_kctl, state->ncs_unit, &removed, 1998 sizeof(removed), CTL_DATA_EOR); 1999 2000 return result; 2001} 2002 2003static errno_t 2004nstat_control_handle_add_request( 2005 nstat_control_state *state, 2006 mbuf_t m) 2007{ 2008 errno_t result; 2009 2010 // Verify the header fits in the first mbuf 2011 if (mbuf_len(m) < offsetof(nstat_msg_add_src_req, param)) 2012 { 2013 return EINVAL; 2014 } 2015 2016 // Calculate the length of the parameter field 2017 int32_t paramlength = mbuf_pkthdr_len(m) - offsetof(nstat_msg_add_src_req, param); 2018 if (paramlength < 0 || paramlength > 2 * 1024) 2019 { 2020 return EINVAL; 2021 } 2022 2023 nstat_provider *provider; 2024 nstat_provider_cookie_t cookie; 2025 nstat_msg_add_src_req *req = mbuf_data(m); 2026 if (mbuf_pkthdr_len(m) > mbuf_len(m)) 2027 { 2028 // parameter is too large, we need to make a contiguous copy 2029 void *data = OSMalloc(paramlength, nstat_malloc_tag); 2030 2031 if (!data) return ENOMEM; 2032 result = mbuf_copydata(m, offsetof(nstat_msg_add_src_req, param), paramlength, data); 2033 if (result == 0) 2034 result = nstat_lookup_entry(req->provider, data, paramlength, &provider, &cookie); 2035 OSFree(data, paramlength, nstat_malloc_tag); 2036 } 2037 else 2038 { 2039 result = nstat_lookup_entry(req->provider, (void*)&req->param, paramlength, &provider, &cookie); 2040 } 2041 2042 if (result != 0) 2043 { 2044 return result; 2045 } 2046 2047 result = nstat_control_source_add(req->hdr.context, state, provider, cookie); 2048 if (result != 0) 2049 provider->nstat_release(cookie, 0); 2050 2051 return result; 2052} 2053 2054static errno_t 2055nstat_control_handle_add_all( 2056 nstat_control_state *state, 2057 mbuf_t m) 2058{ 2059 errno_t result = 0; 2060 2061 // Verify the header fits in the first mbuf 2062 if (mbuf_len(m) < sizeof(nstat_msg_add_all_srcs)) 2063 { 2064 return EINVAL; 2065 } 2066 2067 nstat_msg_add_all_srcs *req = mbuf_data(m); 2068 nstat_provider *provider = nstat_find_provider_by_id(req->provider); 2069 2070 if (!provider) return ENOENT; 2071 if (provider->nstat_watcher_add == NULL) return ENOTSUP; 2072 2073 if (nstat_privcheck != 0) { 2074 result = priv_check_cred(kauth_cred_get(), 2075 PRIV_NET_PRIVILEGED_NETWORK_STATISTICS, 0); 2076 if (result != 0) 2077 return result; 2078 } 2079 2080 // Make sure we don't add the provider twice 2081 lck_mtx_lock(&state->mtx); 2082 if ((state->ncs_watching & (1 << provider->nstat_provider_id)) != 0) 2083 result = EALREADY; 2084 state->ncs_watching |= (1 << provider->nstat_provider_id); 2085 lck_mtx_unlock(&state->mtx); 2086 if (result != 0) return result; 2087 2088 result = provider->nstat_watcher_add(state); 2089 if (result != 0) 2090 { 2091 lck_mtx_lock(&state->mtx); 2092 state->ncs_watching &= ~(1 << provider->nstat_provider_id); 2093 lck_mtx_unlock(&state->mtx); 2094 } 2095 2096 if (result == 0) 2097 { 2098 // Notify the client 2099 nstat_msg_hdr success; 2100 success.context = req->hdr.context; 2101 success.type = NSTAT_MSG_TYPE_SUCCESS; 2102 success.pad = 0; 2103 ctl_enqueuedata(state->ncs_kctl, state->ncs_unit, &success, sizeof(success), CTL_DATA_EOR); 2104 } 2105 2106 return result; 2107} 2108 2109static errno_t 2110nstat_control_source_add( 2111 u_int64_t context, 2112 nstat_control_state *state, 2113 nstat_provider *provider, 2114 nstat_provider_cookie_t cookie) 2115{ 2116 // Fill out source added message 2117 mbuf_t msg = NULL; 2118 unsigned int one = 1; 2119 2120 if (mbuf_allocpacket(MBUF_WAITOK, sizeof(nstat_msg_src_added), &one, &msg) != 0) 2121 return ENOMEM; 2122 2123 mbuf_setlen(msg, sizeof(nstat_msg_src_added)); 2124 mbuf_pkthdr_setlen(msg, mbuf_len(msg)); 2125 nstat_msg_src_added *add = mbuf_data(msg); 2126 bzero(add, sizeof(*add)); 2127 add->hdr.type = NSTAT_MSG_TYPE_SRC_ADDED; 2128 add->hdr.context = context; 2129 add->provider = provider->nstat_provider_id; 2130 2131 // Allocate storage for the source 2132 nstat_src *src = OSMalloc(sizeof(*src), nstat_malloc_tag); 2133 if (src == NULL) 2134 { 2135 mbuf_freem(msg); 2136 return ENOMEM; 2137 } 2138 2139 // Fill in the source, including picking an unused source ref 2140 lck_mtx_lock(&state->mtx); 2141 2142 add->srcref = src->srcref = nstat_control_next_src_ref(state); 2143 if (state->ncs_flags & NSTAT_FLAG_CLEANUP || src->srcref == NSTAT_SRC_REF_INVALID) 2144 { 2145 lck_mtx_unlock(&state->mtx); 2146 OSFree(src, sizeof(*src), nstat_malloc_tag); 2147 mbuf_freem(msg); 2148 return EINVAL; 2149 } 2150 src->provider = provider; 2151 src->cookie = cookie; 2152 src->filter = 0; 2153 2154 // send the source added message 2155 errno_t result = ctl_enqueuembuf(state->ncs_kctl, state->ncs_unit, msg, CTL_DATA_EOR); 2156 if (result != 0) 2157 { 2158 lck_mtx_unlock(&state->mtx); 2159 OSFree(src, sizeof(*src), nstat_malloc_tag); 2160 mbuf_freem(msg); 2161 return result; 2162 } 2163 2164 // Put the source in the list 2165 src->next = state->ncs_srcs; 2166 state->ncs_srcs = src; 2167 2168 // send the description message 2169 // not useful as the source is often not complete 2170// nstat_control_send_description(state, src, 0); 2171 2172 lck_mtx_unlock(&state->mtx); 2173 2174 return 0; 2175} 2176 2177static errno_t 2178nstat_control_handle_remove_request( 2179 nstat_control_state *state, 2180 mbuf_t m) 2181{ 2182 nstat_src_ref_t srcref = NSTAT_SRC_REF_INVALID; 2183 2184 if (mbuf_copydata(m, offsetof(nstat_msg_rem_src_req, srcref), sizeof(srcref), &srcref) != 0) 2185 { 2186 return EINVAL; 2187 } 2188 2189 lck_mtx_lock(&state->mtx); 2190 2191 // Remove this source as we look for it 2192 nstat_src **nextp; 2193 nstat_src *src = NULL; 2194 for (nextp = &state->ncs_srcs; *nextp; nextp = &(*nextp)->next) 2195 { 2196 if ((*nextp)->srcref == srcref) 2197 { 2198 src = *nextp; 2199 *nextp = src->next; 2200 break; 2201 } 2202 } 2203 2204 lck_mtx_unlock(&state->mtx); 2205 2206 if (src) nstat_control_cleanup_source(state, src, FALSE); 2207 2208 return src ? 0 : ENOENT; 2209} 2210 2211static errno_t 2212nstat_control_handle_query_request( 2213 nstat_control_state *state, 2214 mbuf_t m) 2215{ 2216 // TBD: handle this from another thread so we can enqueue a lot of data 2217 // As written, if a client requests query all, this function will be 2218 // called from their send of the request message. We will attempt to write 2219 // responses and succeed until the buffer fills up. Since the clients thread 2220 // is blocked on send, it won't be reading unless the client has two threads 2221 // using this socket, one for read and one for write. Two threads probably 2222 // won't work with this code anyhow since we don't have proper locking in 2223 // place yet. 2224 nstat_src *dead_srcs = NULL; 2225 errno_t result = ENOENT; 2226 nstat_msg_query_src_req req; 2227 if (mbuf_copydata(m, 0, sizeof(req), &req) != 0) 2228 { 2229 return EINVAL; 2230 } 2231 2232 lck_mtx_lock(&state->mtx); 2233 if (req.srcref == NSTAT_SRC_REF_ALL) 2234 state->ncs_flags |= NSTAT_FLAG_REQCOUNTS; 2235 nstat_src **srcpp = &state->ncs_srcs; 2236 while (*srcpp != NULL) 2237 { 2238 int gone; 2239 gone = 0; 2240 2241 // XXX ignore IFACE types? 2242 if (req.srcref == NSTAT_SRC_REF_ALL || 2243 (*srcpp)->srcref == req.srcref) 2244 { 2245 result = nstat_control_send_counts(state, *srcpp, 2246 req.hdr.context, &gone); 2247 2248 // If the counts message failed to enqueue then we should clear our flag so 2249 // that a client doesn't miss anything on idle cleanup. 2250 if (result != 0) 2251 state->ncs_flags &= ~NSTAT_FLAG_REQCOUNTS; 2252 2253 if (gone) 2254 { 2255 // send one last descriptor message so client may see last state 2256 2257 nstat_control_send_description(state, *srcpp, 2258 0); 2259 2260 // pull src out of the list 2261 nstat_src *src = *srcpp; 2262 *srcpp = src->next; 2263 2264 src->next = dead_srcs; 2265 dead_srcs = src; 2266 } 2267 2268 if (req.srcref != NSTAT_SRC_REF_ALL) 2269 break; 2270 } 2271 2272 if (!gone) 2273 srcpp = &(*srcpp)->next; 2274 } 2275 lck_mtx_unlock(&state->mtx); 2276 2277 while (dead_srcs) 2278 { 2279 nstat_src *src; 2280 2281 src = dead_srcs; 2282 dead_srcs = src->next; 2283 2284 // release src and send notification 2285 nstat_control_cleanup_source(state, src, FALSE); 2286 } 2287 2288 if (req.srcref == NSTAT_SRC_REF_ALL) 2289 { 2290 nstat_msg_hdr success; 2291 success.context = req.hdr.context; 2292 success.type = NSTAT_MSG_TYPE_SUCCESS; 2293 success.pad = 0; 2294 ctl_enqueuedata(state->ncs_kctl, state->ncs_unit, &success, sizeof(success), CTL_DATA_EOR); 2295 result = 0; 2296 } 2297 2298 return result; 2299} 2300 2301static errno_t 2302nstat_control_handle_get_src_description( 2303 nstat_control_state *state, 2304 mbuf_t m) 2305{ 2306 nstat_msg_get_src_description req; 2307 errno_t result; 2308 nstat_src *src; 2309 2310 if (mbuf_copydata(m, 0, sizeof(req), &req) != 0) 2311 { 2312 return EINVAL; 2313 } 2314 2315 lck_mtx_lock(&state->mtx); 2316 if (req.srcref == NSTAT_SRC_REF_ALL) 2317 state->ncs_flags |= NSTAT_FLAG_REQDESCS; 2318 for (src = state->ncs_srcs; src; src = src->next) 2319 if (req.srcref == NSTAT_SRC_REF_ALL || 2320 src->srcref == req.srcref) 2321 { 2322 result = nstat_control_send_description(state, src, 2323 req.hdr.context); 2324 if (result != 0) 2325 state->ncs_flags &= ~NSTAT_FLAG_REQDESCS; 2326 if (req.srcref != NSTAT_SRC_REF_ALL) 2327 break; 2328 } 2329 lck_mtx_unlock(&state->mtx); 2330 if (req.srcref != NSTAT_SRC_REF_ALL && src == NULL) 2331 result = ENOENT; 2332 else if (req.srcref == NSTAT_SRC_REF_ALL) 2333 { 2334 nstat_msg_hdr success; 2335 success.context = req.hdr.context; 2336 success.type = NSTAT_MSG_TYPE_SUCCESS; 2337 success.pad = 0; 2338 ctl_enqueuedata(state->ncs_kctl, state->ncs_unit, &success, sizeof(success), CTL_DATA_EOR); 2339 result = 0; 2340 } 2341 2342 return result; 2343} 2344 2345static errno_t 2346nstat_control_handle_set_filter( 2347 nstat_control_state *state, 2348 mbuf_t m) 2349{ 2350 nstat_msg_set_filter req; 2351 nstat_src *src; 2352 2353 if (mbuf_copydata(m, 0, sizeof(req), &req) != 0) 2354 return EINVAL; 2355 if (req.srcref == NSTAT_SRC_REF_ALL || 2356 req.srcref == NSTAT_SRC_REF_INVALID) 2357 return EINVAL; 2358 2359 lck_mtx_lock(&state->mtx); 2360 for (src = state->ncs_srcs; src; src = src->next) 2361 if (req.srcref == src->srcref) 2362 { 2363 src->filter = req.filter; 2364 break; 2365 } 2366 lck_mtx_unlock(&state->mtx); 2367 if (src == NULL) 2368 return ENOENT; 2369 2370 return 0; 2371 2372} 2373 2374static errno_t 2375nstat_control_send( 2376 kern_ctl_ref kctl, 2377 u_int32_t unit, 2378 void *uinfo, 2379 mbuf_t m, 2380 __unused int flags) 2381{ 2382 nstat_control_state *state = (nstat_control_state*)uinfo; 2383 struct nstat_msg_hdr *hdr; 2384 struct nstat_msg_hdr storage; 2385 errno_t result = 0; 2386 2387 if (mbuf_pkthdr_len(m) < sizeof(hdr)) 2388 { 2389 // Is this the right thing to do? 2390 mbuf_freem(m); 2391 return EINVAL; 2392 } 2393 2394 if (mbuf_len(m) >= sizeof(*hdr)) 2395 { 2396 hdr = mbuf_data(m); 2397 } 2398 else 2399 { 2400 mbuf_copydata(m, 0, sizeof(storage), &storage); 2401 hdr = &storage; 2402 } 2403 2404 switch (hdr->type) 2405 { 2406 case NSTAT_MSG_TYPE_ADD_SRC: 2407 result = nstat_control_handle_add_request(state, m); 2408 break; 2409 2410 case NSTAT_MSG_TYPE_ADD_ALL_SRCS: 2411 result = nstat_control_handle_add_all(state, m); 2412 break; 2413 2414 case NSTAT_MSG_TYPE_REM_SRC: 2415 result = nstat_control_handle_remove_request(state, m); 2416 break; 2417 2418 case NSTAT_MSG_TYPE_QUERY_SRC: 2419 result = nstat_control_handle_query_request(state, m); 2420 break; 2421 2422 case NSTAT_MSG_TYPE_GET_SRC_DESC: 2423 result = nstat_control_handle_get_src_description(state, m); 2424 break; 2425 2426 case NSTAT_MSG_TYPE_SET_FILTER: 2427 result = nstat_control_handle_set_filter(state, m); 2428 break; 2429 2430 default: 2431 result = EINVAL; 2432 break; 2433 } 2434 2435 if (result != 0) 2436 { 2437 struct nstat_msg_error err; 2438 2439 err.hdr.type = NSTAT_MSG_TYPE_ERROR; 2440 err.hdr.context = hdr->context; 2441 err.error = result; 2442 2443 result = ctl_enqueuedata(kctl, unit, &err, sizeof(err), CTL_DATA_EOR); 2444 } 2445 2446 mbuf_freem(m); 2447 2448 return result; 2449} 2450