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