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