1 /* $NetBSD: uipc_domain.c,v 1.89 2013/09/15 15:37:27 martin Exp $ */ 2 3 /* 4 * Copyright (c) 1982, 1986, 1993 5 * The Regents of the University of California. All rights reserved. 6 * 7 * Redistribution and use in source and binary forms, with or without 8 * modification, are permitted provided that the following conditions 9 * are met: 10 * 1. Redistributions of source code must retain the above copyright 11 * notice, this list of conditions and the following disclaimer. 12 * 2. Redistributions in binary form must reproduce the above copyright 13 * notice, this list of conditions and the following disclaimer in the 14 * documentation and/or other materials provided with the distribution. 15 * 3. Neither the name of the University nor the names of its contributors 16 * may be used to endorse or promote products derived from this software 17 * without specific prior written permission. 18 * 19 * THIS SOFTWARE IS PROVIDED BY THE REGENTS AND CONTRIBUTORS ``AS IS'' AND 20 * ANY EXPRESS OR IMPLIED WARRANTIES, INCLUDING, BUT NOT LIMITED TO, THE 21 * IMPLIED WARRANTIES OF MERCHANTABILITY AND FITNESS FOR A PARTICULAR PURPOSE 22 * ARE DISCLAIMED. IN NO EVENT SHALL THE REGENTS OR CONTRIBUTORS BE LIABLE 23 * FOR ANY DIRECT, INDIRECT, INCIDENTAL, SPECIAL, EXEMPLARY, OR CONSEQUENTIAL 24 * DAMAGES (INCLUDING, BUT NOT LIMITED TO, PROCUREMENT OF SUBSTITUTE GOODS 25 * OR SERVICES; LOSS OF USE, DATA, OR PROFITS; OR BUSINESS INTERRUPTION) 26 * HOWEVER CAUSED AND ON ANY THEORY OF LIABILITY, WHETHER IN CONTRACT, STRICT 27 * LIABILITY, OR TORT (INCLUDING NEGLIGENCE OR OTHERWISE) ARISING IN ANY WAY 28 * OUT OF THE USE OF THIS SOFTWARE, EVEN IF ADVISED OF THE POSSIBILITY OF 29 * SUCH DAMAGE. 30 * 31 * @(#)uipc_domain.c 8.3 (Berkeley) 2/14/95 32 */ 33 34 #include <sys/cdefs.h> 35 __KERNEL_RCSID(0, "$NetBSD: uipc_domain.c,v 1.89 2013/09/15 15:37:27 martin Exp $"); 36 37 #include <sys/param.h> 38 #include <sys/socket.h> 39 #include <sys/socketvar.h> 40 #include <sys/protosw.h> 41 #include <sys/domain.h> 42 #include <sys/mbuf.h> 43 #include <sys/time.h> 44 #include <sys/kernel.h> 45 #include <sys/systm.h> 46 #include <sys/callout.h> 47 #include <sys/queue.h> 48 #include <sys/proc.h> 49 #include <sys/sysctl.h> 50 #include <sys/un.h> 51 #include <sys/unpcb.h> 52 #include <sys/file.h> 53 #include <sys/filedesc.h> 54 #include <sys/kauth.h> 55 #include <netinet/in.h> 56 57 MALLOC_DECLARE(M_SOCKADDR); 58 59 MALLOC_DEFINE(M_SOCKADDR, "sockaddr", "socket endpoints"); 60 61 void pffasttimo(void *); 62 void pfslowtimo(void *); 63 64 struct domainhead domains = STAILQ_HEAD_INITIALIZER(domains); 65 static struct domain *domain_array[AF_MAX]; 66 67 callout_t pffasttimo_ch, pfslowtimo_ch; 68 69 /* 70 * Current time values for fast and slow timeouts. We can use u_int 71 * relatively safely. The fast timer will roll over in 27 years and 72 * the slow timer in 68 years. 73 */ 74 u_int pfslowtimo_now; 75 u_int pffasttimo_now; 76 77 static struct sysctllog *domain_sysctllog; 78 static void sysctl_net_setup(void); 79 80 void 81 domaininit(bool addroute) 82 { 83 __link_set_decl(domains, struct domain); 84 struct domain * const * dpp; 85 struct domain *rt_domain = NULL; 86 87 sysctl_net_setup(); 88 89 /* 90 * Add all of the domains. Make sure the PF_ROUTE 91 * domain is added last. 92 */ 93 __link_set_foreach(dpp, domains) { 94 if ((*dpp)->dom_family == PF_ROUTE) 95 rt_domain = *dpp; 96 else 97 domain_attach(*dpp); 98 } 99 if (rt_domain && addroute) 100 domain_attach(rt_domain); 101 102 callout_init(&pffasttimo_ch, CALLOUT_MPSAFE); 103 callout_init(&pfslowtimo_ch, CALLOUT_MPSAFE); 104 105 callout_reset(&pffasttimo_ch, 1, pffasttimo, NULL); 106 callout_reset(&pfslowtimo_ch, 1, pfslowtimo, NULL); 107 } 108 109 void 110 domain_attach(struct domain *dp) 111 { 112 const struct protosw *pr; 113 114 STAILQ_INSERT_TAIL(&domains, dp, dom_link); 115 if (dp->dom_family < __arraycount(domain_array)) 116 domain_array[dp->dom_family] = dp; 117 118 if (dp->dom_init) 119 (*dp->dom_init)(); 120 121 #ifdef MBUFTRACE 122 if (dp->dom_mowner.mo_name[0] == '\0') { 123 strncpy(dp->dom_mowner.mo_name, dp->dom_name, 124 sizeof(dp->dom_mowner.mo_name)); 125 MOWNER_ATTACH(&dp->dom_mowner); 126 } 127 #endif 128 for (pr = dp->dom_protosw; pr < dp->dom_protoswNPROTOSW; pr++) { 129 if (pr->pr_init) 130 (*pr->pr_init)(); 131 } 132 133 if (max_linkhdr < 16) /* XXX */ 134 max_linkhdr = 16; 135 max_hdr = max_linkhdr + max_protohdr; 136 max_datalen = MHLEN - max_hdr; 137 } 138 139 struct domain * 140 pffinddomain(int family) 141 { 142 struct domain *dp; 143 144 if (family < __arraycount(domain_array) && domain_array[family] != NULL) 145 return domain_array[family]; 146 147 DOMAIN_FOREACH(dp) 148 if (dp->dom_family == family) 149 return dp; 150 return NULL; 151 } 152 153 const struct protosw * 154 pffindtype(int family, int type) 155 { 156 struct domain *dp; 157 const struct protosw *pr; 158 159 dp = pffinddomain(family); 160 if (dp == NULL) 161 return NULL; 162 163 for (pr = dp->dom_protosw; pr < dp->dom_protoswNPROTOSW; pr++) 164 if (pr->pr_type && pr->pr_type == type) 165 return pr; 166 167 return NULL; 168 } 169 170 const struct protosw * 171 pffindproto(int family, int protocol, int type) 172 { 173 struct domain *dp; 174 const struct protosw *pr; 175 const struct protosw *maybe = NULL; 176 177 if (family == 0) 178 return NULL; 179 180 dp = pffinddomain(family); 181 if (dp == NULL) 182 return NULL; 183 184 for (pr = dp->dom_protosw; pr < dp->dom_protoswNPROTOSW; pr++) { 185 if ((pr->pr_protocol == protocol) && (pr->pr_type == type)) 186 return pr; 187 188 if (type == SOCK_RAW && pr->pr_type == SOCK_RAW && 189 pr->pr_protocol == 0 && maybe == NULL) 190 maybe = pr; 191 } 192 return maybe; 193 } 194 195 void * 196 sockaddr_addr(struct sockaddr *sa, socklen_t *slenp) 197 { 198 const struct domain *dom; 199 200 if ((dom = pffinddomain(sa->sa_family)) == NULL || 201 dom->dom_sockaddr_addr == NULL) 202 return NULL; 203 204 return (*dom->dom_sockaddr_addr)(sa, slenp); 205 } 206 207 const void * 208 sockaddr_const_addr(const struct sockaddr *sa, socklen_t *slenp) 209 { 210 const struct domain *dom; 211 212 if ((dom = pffinddomain(sa->sa_family)) == NULL || 213 dom->dom_sockaddr_const_addr == NULL) 214 return NULL; 215 216 return (*dom->dom_sockaddr_const_addr)(sa, slenp); 217 } 218 219 const struct sockaddr * 220 sockaddr_any_by_family(int family) 221 { 222 const struct domain *dom; 223 224 if ((dom = pffinddomain(family)) == NULL) 225 return NULL; 226 227 return dom->dom_sa_any; 228 } 229 230 const struct sockaddr * 231 sockaddr_any(const struct sockaddr *sa) 232 { 233 return sockaddr_any_by_family(sa->sa_family); 234 } 235 236 const void * 237 sockaddr_anyaddr(const struct sockaddr *sa, socklen_t *slenp) 238 { 239 const struct sockaddr *any; 240 241 if ((any = sockaddr_any(sa)) == NULL) 242 return NULL; 243 244 return sockaddr_const_addr(any, slenp); 245 } 246 247 struct sockaddr * 248 sockaddr_alloc(sa_family_t af, socklen_t socklen, int flags) 249 { 250 struct sockaddr *sa; 251 socklen_t reallen = MAX(socklen, offsetof(struct sockaddr, sa_data[0])); 252 253 if ((sa = malloc(reallen, M_SOCKADDR, flags)) == NULL) 254 return NULL; 255 256 sa->sa_family = af; 257 sa->sa_len = reallen; 258 return sa; 259 } 260 261 struct sockaddr * 262 sockaddr_copy(struct sockaddr *dst, socklen_t socklen, 263 const struct sockaddr *src) 264 { 265 if (__predict_false(socklen < src->sa_len)) { 266 panic("%s: source too long, %d < %d bytes", __func__, socklen, 267 src->sa_len); 268 } 269 return memcpy(dst, src, src->sa_len); 270 } 271 272 struct sockaddr * 273 sockaddr_externalize(struct sockaddr *dst, socklen_t socklen, 274 const struct sockaddr *src) 275 { 276 struct domain *dom; 277 278 dom = pffinddomain(src->sa_family); 279 280 if (dom != NULL && dom->dom_sockaddr_externalize != NULL) 281 return (*dom->dom_sockaddr_externalize)(dst, socklen, src); 282 283 return sockaddr_copy(dst, socklen, src); 284 } 285 286 int 287 sockaddr_cmp(const struct sockaddr *sa1, const struct sockaddr *sa2) 288 { 289 int len, rc; 290 struct domain *dom; 291 292 if (sa1->sa_family != sa2->sa_family) 293 return sa1->sa_family - sa2->sa_family; 294 295 dom = pffinddomain(sa1->sa_family); 296 297 if (dom != NULL && dom->dom_sockaddr_cmp != NULL) 298 return (*dom->dom_sockaddr_cmp)(sa1, sa2); 299 300 len = MIN(sa1->sa_len, sa2->sa_len); 301 302 if (dom == NULL || dom->dom_sa_cmplen == 0) { 303 if ((rc = memcmp(sa1, sa2, len)) != 0) 304 return rc; 305 return sa1->sa_len - sa2->sa_len; 306 } 307 308 if ((rc = memcmp((const char *)sa1 + dom->dom_sa_cmpofs, 309 (const char *)sa2 + dom->dom_sa_cmpofs, 310 MIN(dom->dom_sa_cmplen, 311 len - MIN(len, dom->dom_sa_cmpofs)))) != 0) 312 return rc; 313 314 return MIN(dom->dom_sa_cmplen + dom->dom_sa_cmpofs, sa1->sa_len) - 315 MIN(dom->dom_sa_cmplen + dom->dom_sa_cmpofs, sa2->sa_len); 316 } 317 318 struct sockaddr * 319 sockaddr_dup(const struct sockaddr *src, int flags) 320 { 321 struct sockaddr *dst; 322 323 if ((dst = sockaddr_alloc(src->sa_family, src->sa_len, flags)) == NULL) 324 return NULL; 325 326 return sockaddr_copy(dst, dst->sa_len, src); 327 } 328 329 void 330 sockaddr_free(struct sockaddr *sa) 331 { 332 free(sa, M_SOCKADDR); 333 } 334 335 void 336 sockaddr_format(const struct sockaddr *sa, char *buf, size_t len) 337 { 338 const struct sockaddr_un *sun = (const struct sockaddr_un *)sa; 339 const struct sockaddr_in *sin = (const struct sockaddr_in *)sa; 340 const struct sockaddr_in6 *sin6 = (const struct sockaddr_in6 *)sa; 341 const uint8_t *data; 342 size_t data_len; 343 344 if (sa == NULL) { 345 strlcpy(buf, "(null)", len); 346 return; 347 } 348 349 switch (sa->sa_family) { 350 default: 351 snprintf(buf, len, "(unknown socket family %d)", 352 (int)sa->sa_family); 353 return; 354 case AF_LOCAL: 355 strlcpy(buf, "unix:", len); 356 strlcat(buf, sun->sun_path, len); 357 return; 358 case AF_INET: 359 strlcpy(buf, "inet:", len); 360 if (len < 6) 361 return; 362 buf += 5; 363 len -= 5; 364 data = (const uint8_t *)&sin->sin_addr; 365 data_len = sizeof(sin->sin_addr); 366 break; 367 case AF_INET6: 368 strlcpy(buf, "inet6:", len); 369 if (len < 7) 370 return; 371 buf += 6; 372 len -= 6; 373 data = (const uint8_t *)&sin6->sin6_addr; 374 data_len = sizeof(sin6->sin6_addr); 375 break; 376 } 377 for (;;) { 378 if (--len == 0) 379 break; 380 381 uint8_t hi = *data >> 4; 382 uint8_t lo = *data & 15; 383 --data_len; 384 ++data; 385 *buf++ = hi + (hi >= 10 ? 'a' - 10 : '0'); 386 if (--len == 0) 387 break; 388 *buf++ = lo + (lo >= 10 ? 'a' - 10 : '0'); 389 if (data_len == 0) 390 break; 391 } 392 *buf = 0; 393 } 394 395 /* 396 * sysctl helper to stuff PF_LOCAL pcbs into sysctl structures 397 */ 398 static void 399 sysctl_dounpcb(struct kinfo_pcb *pcb, const struct socket *so) 400 { 401 struct unpcb *unp = sotounpcb(so); 402 struct sockaddr_un *un = unp->unp_addr; 403 404 memset(pcb, 0, sizeof(*pcb)); 405 406 pcb->ki_family = so->so_proto->pr_domain->dom_family; 407 pcb->ki_type = so->so_proto->pr_type; 408 pcb->ki_protocol = so->so_proto->pr_protocol; 409 pcb->ki_pflags = unp->unp_flags; 410 411 pcb->ki_pcbaddr = PTRTOUINT64(unp); 412 /* pcb->ki_ppcbaddr = unp has no ppcb... */ 413 pcb->ki_sockaddr = PTRTOUINT64(so); 414 415 pcb->ki_sostate = so->so_state; 416 /* pcb->ki_prstate = unp has no state... */ 417 418 pcb->ki_rcvq = so->so_rcv.sb_cc; 419 pcb->ki_sndq = so->so_snd.sb_cc; 420 421 un = (struct sockaddr_un *)&pcb->ki_src; 422 /* 423 * local domain sockets may bind without having a local 424 * endpoint. bleah! 425 */ 426 if (unp->unp_addr != NULL) { 427 un->sun_len = unp->unp_addr->sun_len; 428 un->sun_family = unp->unp_addr->sun_family; 429 strlcpy(un->sun_path, unp->unp_addr->sun_path, 430 sizeof(pcb->ki_s)); 431 } 432 else { 433 un->sun_len = offsetof(struct sockaddr_un, sun_path); 434 un->sun_family = pcb->ki_family; 435 } 436 if (unp->unp_conn != NULL) { 437 un = (struct sockaddr_un *)&pcb->ki_dst; 438 if (unp->unp_conn->unp_addr != NULL) { 439 un->sun_len = unp->unp_conn->unp_addr->sun_len; 440 un->sun_family = unp->unp_conn->unp_addr->sun_family; 441 un->sun_family = unp->unp_conn->unp_addr->sun_family; 442 strlcpy(un->sun_path, unp->unp_conn->unp_addr->sun_path, 443 sizeof(pcb->ki_d)); 444 } 445 else { 446 un->sun_len = offsetof(struct sockaddr_un, sun_path); 447 un->sun_family = pcb->ki_family; 448 } 449 } 450 451 pcb->ki_inode = unp->unp_ino; 452 pcb->ki_vnode = PTRTOUINT64(unp->unp_vnode); 453 pcb->ki_conn = PTRTOUINT64(unp->unp_conn); 454 pcb->ki_refs = PTRTOUINT64(unp->unp_refs); 455 pcb->ki_nextref = PTRTOUINT64(unp->unp_nextref); 456 } 457 458 static int 459 sysctl_unpcblist(SYSCTLFN_ARGS) 460 { 461 struct file *fp, *dfp; 462 struct socket *so; 463 struct kinfo_pcb pcb; 464 char *dp; 465 size_t len, needed, elem_size, out_size; 466 int error, elem_count, pf, type; 467 468 if (namelen == 1 && name[0] == CTL_QUERY) 469 return sysctl_query(SYSCTLFN_CALL(rnode)); 470 471 if (namelen != 4) 472 return EINVAL; 473 474 if (oldp != NULL) { 475 len = *oldlenp; 476 elem_size = name[2]; 477 elem_count = name[3]; 478 if (elem_size != sizeof(pcb)) 479 return EINVAL; 480 } else { 481 len = 0; 482 elem_size = sizeof(pcb); 483 elem_count = INT_MAX; 484 } 485 error = 0; 486 dp = oldp; 487 out_size = elem_size; 488 needed = 0; 489 490 if (name - oname != 4) 491 return EINVAL; 492 493 pf = oname[1]; 494 type = oname[2]; 495 496 /* 497 * allocate dummy file descriptor to make position in list. 498 */ 499 sysctl_unlock(); 500 if ((dfp = fgetdummy()) == NULL) { 501 sysctl_relock(); 502 return ENOMEM; 503 } 504 505 /* 506 * there's no "list" of local domain sockets, so we have 507 * to walk the file list looking for them. :-/ 508 */ 509 mutex_enter(&filelist_lock); 510 LIST_FOREACH(fp, &filehead, f_list) { 511 if (fp->f_count == 0 || fp->f_type != DTYPE_SOCKET || 512 fp->f_data == NULL) 513 continue; 514 so = (struct socket *)fp->f_data; 515 if (so->so_type != type) 516 continue; 517 if (so->so_proto->pr_domain->dom_family != pf) 518 continue; 519 if (kauth_authorize_network(l->l_cred, KAUTH_NETWORK_SOCKET, 520 KAUTH_REQ_NETWORK_SOCKET_CANSEE, so, NULL, NULL) != 0) 521 continue; 522 if (len >= elem_size && elem_count > 0) { 523 mutex_enter(&fp->f_lock); 524 fp->f_count++; 525 mutex_exit(&fp->f_lock); 526 LIST_INSERT_AFTER(fp, dfp, f_list); 527 mutex_exit(&filelist_lock); 528 sysctl_dounpcb(&pcb, so); 529 error = copyout(&pcb, dp, out_size); 530 closef(fp); 531 mutex_enter(&filelist_lock); 532 LIST_REMOVE(dfp, f_list); 533 if (error) 534 break; 535 dp += elem_size; 536 len -= elem_size; 537 } 538 needed += elem_size; 539 if (elem_count > 0 && elem_count != INT_MAX) 540 elem_count--; 541 } 542 mutex_exit(&filelist_lock); 543 fputdummy(dfp); 544 *oldlenp = needed; 545 if (oldp == NULL) 546 *oldlenp += PCB_SLOP * sizeof(struct kinfo_pcb); 547 sysctl_relock(); 548 549 return error; 550 } 551 552 static void 553 sysctl_net_setup(void) 554 { 555 556 KASSERT(domain_sysctllog == NULL); 557 sysctl_createv(&domain_sysctllog, 0, NULL, NULL, 558 CTLFLAG_PERMANENT, 559 CTLTYPE_NODE, "net", NULL, 560 NULL, 0, NULL, 0, 561 CTL_NET, CTL_EOL); 562 sysctl_createv(&domain_sysctllog, 0, NULL, NULL, 563 CTLFLAG_PERMANENT, 564 CTLTYPE_NODE, "local", 565 SYSCTL_DESCR("PF_LOCAL related settings"), 566 NULL, 0, NULL, 0, 567 CTL_NET, PF_LOCAL, CTL_EOL); 568 sysctl_createv(&domain_sysctllog, 0, NULL, NULL, 569 CTLFLAG_PERMANENT, 570 CTLTYPE_NODE, "stream", 571 SYSCTL_DESCR("SOCK_STREAM settings"), 572 NULL, 0, NULL, 0, 573 CTL_NET, PF_LOCAL, SOCK_STREAM, CTL_EOL); 574 sysctl_createv(&domain_sysctllog, 0, NULL, NULL, 575 CTLFLAG_PERMANENT, 576 CTLTYPE_NODE, "seqpacket", 577 SYSCTL_DESCR("SOCK_SEQPACKET settings"), 578 NULL, 0, NULL, 0, 579 CTL_NET, PF_LOCAL, SOCK_SEQPACKET, CTL_EOL); 580 sysctl_createv(&domain_sysctllog, 0, NULL, NULL, 581 CTLFLAG_PERMANENT, 582 CTLTYPE_NODE, "dgram", 583 SYSCTL_DESCR("SOCK_DGRAM settings"), 584 NULL, 0, NULL, 0, 585 CTL_NET, PF_LOCAL, SOCK_DGRAM, CTL_EOL); 586 587 sysctl_createv(&domain_sysctllog, 0, NULL, NULL, 588 CTLFLAG_PERMANENT, 589 CTLTYPE_STRUCT, "pcblist", 590 SYSCTL_DESCR("SOCK_STREAM protocol control block list"), 591 sysctl_unpcblist, 0, NULL, 0, 592 CTL_NET, PF_LOCAL, SOCK_STREAM, CTL_CREATE, CTL_EOL); 593 sysctl_createv(&domain_sysctllog, 0, NULL, NULL, 594 CTLFLAG_PERMANENT, 595 CTLTYPE_STRUCT, "pcblist", 596 SYSCTL_DESCR("SOCK_SEQPACKET protocol control " 597 "block list"), 598 sysctl_unpcblist, 0, NULL, 0, 599 CTL_NET, PF_LOCAL, SOCK_SEQPACKET, CTL_CREATE, CTL_EOL); 600 sysctl_createv(&domain_sysctllog, 0, NULL, NULL, 601 CTLFLAG_PERMANENT, 602 CTLTYPE_STRUCT, "pcblist", 603 SYSCTL_DESCR("SOCK_DGRAM protocol control block list"), 604 sysctl_unpcblist, 0, NULL, 0, 605 CTL_NET, PF_LOCAL, SOCK_DGRAM, CTL_CREATE, CTL_EOL); 606 } 607 608 void 609 pfctlinput(int cmd, const struct sockaddr *sa) 610 { 611 struct domain *dp; 612 const struct protosw *pr; 613 614 DOMAIN_FOREACH(dp) { 615 for (pr = dp->dom_protosw; pr < dp->dom_protoswNPROTOSW; pr++) { 616 if (pr->pr_ctlinput != NULL) 617 (*pr->pr_ctlinput)(cmd, sa, NULL); 618 } 619 } 620 } 621 622 void 623 pfctlinput2(int cmd, const struct sockaddr *sa, void *ctlparam) 624 { 625 struct domain *dp; 626 const struct protosw *pr; 627 628 if (sa == NULL) 629 return; 630 631 DOMAIN_FOREACH(dp) { 632 /* 633 * the check must be made by xx_ctlinput() anyways, to 634 * make sure we use data item pointed to by ctlparam in 635 * correct way. the following check is made just for safety. 636 */ 637 if (dp->dom_family != sa->sa_family) 638 continue; 639 640 for (pr = dp->dom_protosw; pr < dp->dom_protoswNPROTOSW; pr++) { 641 if (pr->pr_ctlinput != NULL) 642 (*pr->pr_ctlinput)(cmd, sa, ctlparam); 643 } 644 } 645 } 646 647 void 648 pfslowtimo(void *arg) 649 { 650 struct domain *dp; 651 const struct protosw *pr; 652 653 pfslowtimo_now++; 654 655 DOMAIN_FOREACH(dp) { 656 for (pr = dp->dom_protosw; pr < dp->dom_protoswNPROTOSW; pr++) 657 if (pr->pr_slowtimo) 658 (*pr->pr_slowtimo)(); 659 } 660 callout_schedule(&pfslowtimo_ch, hz / PR_SLOWHZ); 661 } 662 663 void 664 pffasttimo(void *arg) 665 { 666 struct domain *dp; 667 const struct protosw *pr; 668 669 pffasttimo_now++; 670 671 DOMAIN_FOREACH(dp) { 672 for (pr = dp->dom_protosw; pr < dp->dom_protoswNPROTOSW; pr++) 673 if (pr->pr_fasttimo) 674 (*pr->pr_fasttimo)(); 675 } 676 callout_schedule(&pffasttimo_ch, hz / PR_FASTHZ); 677 } 678