| 1 | /* $NetBSD: uipc_domain.c,v 1.96 2014/12/02 19:45:58 christos 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.96 2014/12/02 19:45:58 christos 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 | |
| 56 | #include <netatalk/at.h> |
| 57 | #include <net/if_dl.h> |
| 58 | #include <netinet/in.h> |
| 59 | |
| 60 | MALLOC_DECLARE(M_SOCKADDR); |
| 61 | |
| 62 | MALLOC_DEFINE(M_SOCKADDR, "sockaddr" , "socket endpoints" ); |
| 63 | |
| 64 | void pffasttimo(void *); |
| 65 | void pfslowtimo(void *); |
| 66 | |
| 67 | struct domainhead domains = STAILQ_HEAD_INITIALIZER(domains); |
| 68 | static struct domain *domain_array[AF_MAX]; |
| 69 | |
| 70 | callout_t pffasttimo_ch, pfslowtimo_ch; |
| 71 | |
| 72 | /* |
| 73 | * Current time values for fast and slow timeouts. We can use u_int |
| 74 | * relatively safely. The fast timer will roll over in 27 years and |
| 75 | * the slow timer in 68 years. |
| 76 | */ |
| 77 | u_int pfslowtimo_now; |
| 78 | u_int pffasttimo_now; |
| 79 | |
| 80 | static struct sysctllog *domain_sysctllog; |
| 81 | static void sysctl_net_setup(void); |
| 82 | |
| 83 | /* ensure successful linkage even without any domains in link sets */ |
| 84 | static struct domain domain_dummy; |
| 85 | __link_set_add_rodata(domains,domain_dummy); |
| 86 | |
| 87 | void |
| 88 | domaininit(bool attach) |
| 89 | { |
| 90 | __link_set_decl(domains, struct domain); |
| 91 | struct domain * const * dpp; |
| 92 | struct domain *rt_domain = NULL; |
| 93 | |
| 94 | sysctl_net_setup(); |
| 95 | |
| 96 | /* |
| 97 | * Add all of the domains. Make sure the PF_ROUTE |
| 98 | * domain is added last. |
| 99 | */ |
| 100 | if (attach) { |
| 101 | __link_set_foreach(dpp, domains) { |
| 102 | if (*dpp == &domain_dummy) |
| 103 | continue; |
| 104 | if ((*dpp)->dom_family == PF_ROUTE) |
| 105 | rt_domain = *dpp; |
| 106 | else |
| 107 | domain_attach(*dpp); |
| 108 | } |
| 109 | if (rt_domain) |
| 110 | domain_attach(rt_domain); |
| 111 | } |
| 112 | |
| 113 | callout_init(&pffasttimo_ch, CALLOUT_MPSAFE); |
| 114 | callout_init(&pfslowtimo_ch, CALLOUT_MPSAFE); |
| 115 | |
| 116 | callout_reset(&pffasttimo_ch, 1, pffasttimo, NULL); |
| 117 | callout_reset(&pfslowtimo_ch, 1, pfslowtimo, NULL); |
| 118 | } |
| 119 | |
| 120 | void |
| 121 | domain_attach(struct domain *dp) |
| 122 | { |
| 123 | const struct protosw *pr; |
| 124 | |
| 125 | STAILQ_INSERT_TAIL(&domains, dp, dom_link); |
| 126 | if (dp->dom_family < __arraycount(domain_array)) |
| 127 | domain_array[dp->dom_family] = dp; |
| 128 | |
| 129 | if (dp->dom_init) |
| 130 | (*dp->dom_init)(); |
| 131 | |
| 132 | #ifdef MBUFTRACE |
| 133 | if (dp->dom_mowner.mo_name[0] == '\0') { |
| 134 | strncpy(dp->dom_mowner.mo_name, dp->dom_name, |
| 135 | sizeof(dp->dom_mowner.mo_name)); |
| 136 | MOWNER_ATTACH(&dp->dom_mowner); |
| 137 | } |
| 138 | #endif |
| 139 | for (pr = dp->dom_protosw; pr < dp->dom_protoswNPROTOSW; pr++) { |
| 140 | if (pr->pr_init) |
| 141 | (*pr->pr_init)(); |
| 142 | } |
| 143 | |
| 144 | if (max_linkhdr < 16) /* XXX */ |
| 145 | max_linkhdr = 16; |
| 146 | max_hdr = max_linkhdr + max_protohdr; |
| 147 | max_datalen = MHLEN - max_hdr; |
| 148 | } |
| 149 | |
| 150 | struct domain * |
| 151 | pffinddomain(int family) |
| 152 | { |
| 153 | struct domain *dp; |
| 154 | |
| 155 | if (family < __arraycount(domain_array) && domain_array[family] != NULL) |
| 156 | return domain_array[family]; |
| 157 | |
| 158 | DOMAIN_FOREACH(dp) |
| 159 | if (dp->dom_family == family) |
| 160 | return dp; |
| 161 | return NULL; |
| 162 | } |
| 163 | |
| 164 | const struct protosw * |
| 165 | pffindtype(int family, int type) |
| 166 | { |
| 167 | struct domain *dp; |
| 168 | const struct protosw *pr; |
| 169 | |
| 170 | dp = pffinddomain(family); |
| 171 | if (dp == NULL) |
| 172 | return NULL; |
| 173 | |
| 174 | for (pr = dp->dom_protosw; pr < dp->dom_protoswNPROTOSW; pr++) |
| 175 | if (pr->pr_type && pr->pr_type == type) |
| 176 | return pr; |
| 177 | |
| 178 | return NULL; |
| 179 | } |
| 180 | |
| 181 | const struct protosw * |
| 182 | pffindproto(int family, int protocol, int type) |
| 183 | { |
| 184 | struct domain *dp; |
| 185 | const struct protosw *pr; |
| 186 | const struct protosw *maybe = NULL; |
| 187 | |
| 188 | if (family == 0) |
| 189 | return NULL; |
| 190 | |
| 191 | dp = pffinddomain(family); |
| 192 | if (dp == NULL) |
| 193 | return NULL; |
| 194 | |
| 195 | for (pr = dp->dom_protosw; pr < dp->dom_protoswNPROTOSW; pr++) { |
| 196 | if ((pr->pr_protocol == protocol) && (pr->pr_type == type)) |
| 197 | return pr; |
| 198 | |
| 199 | if (type == SOCK_RAW && pr->pr_type == SOCK_RAW && |
| 200 | pr->pr_protocol == 0 && maybe == NULL) |
| 201 | maybe = pr; |
| 202 | } |
| 203 | return maybe; |
| 204 | } |
| 205 | |
| 206 | void * |
| 207 | sockaddr_addr(struct sockaddr *sa, socklen_t *slenp) |
| 208 | { |
| 209 | const struct domain *dom; |
| 210 | |
| 211 | if ((dom = pffinddomain(sa->sa_family)) == NULL || |
| 212 | dom->dom_sockaddr_addr == NULL) |
| 213 | return NULL; |
| 214 | |
| 215 | return (*dom->dom_sockaddr_addr)(sa, slenp); |
| 216 | } |
| 217 | |
| 218 | const void * |
| 219 | sockaddr_const_addr(const struct sockaddr *sa, socklen_t *slenp) |
| 220 | { |
| 221 | const struct domain *dom; |
| 222 | |
| 223 | if ((dom = pffinddomain(sa->sa_family)) == NULL || |
| 224 | dom->dom_sockaddr_const_addr == NULL) |
| 225 | return NULL; |
| 226 | |
| 227 | return (*dom->dom_sockaddr_const_addr)(sa, slenp); |
| 228 | } |
| 229 | |
| 230 | const struct sockaddr * |
| 231 | sockaddr_any_by_family(int family) |
| 232 | { |
| 233 | const struct domain *dom; |
| 234 | |
| 235 | if ((dom = pffinddomain(family)) == NULL) |
| 236 | return NULL; |
| 237 | |
| 238 | return dom->dom_sa_any; |
| 239 | } |
| 240 | |
| 241 | const struct sockaddr * |
| 242 | sockaddr_any(const struct sockaddr *sa) |
| 243 | { |
| 244 | return sockaddr_any_by_family(sa->sa_family); |
| 245 | } |
| 246 | |
| 247 | const void * |
| 248 | sockaddr_anyaddr(const struct sockaddr *sa, socklen_t *slenp) |
| 249 | { |
| 250 | const struct sockaddr *any; |
| 251 | |
| 252 | if ((any = sockaddr_any(sa)) == NULL) |
| 253 | return NULL; |
| 254 | |
| 255 | return sockaddr_const_addr(any, slenp); |
| 256 | } |
| 257 | |
| 258 | #ifdef DIAGNOSTIC |
| 259 | static void |
| 260 | sockaddr_checklen(const struct sockaddr *sa) |
| 261 | { |
| 262 | socklen_t len = 0; |
| 263 | switch (sa->sa_family) { |
| 264 | case AF_INET: |
| 265 | len = sizeof(struct sockaddr_in); |
| 266 | break; |
| 267 | case AF_INET6: |
| 268 | len = sizeof(struct sockaddr_in6); |
| 269 | break; |
| 270 | case AF_UNIX: |
| 271 | len = sizeof(struct sockaddr_un); |
| 272 | break; |
| 273 | case AF_LINK: |
| 274 | len = sizeof(struct sockaddr_dl); |
| 275 | // As long as it is not 0... |
| 276 | if (sa->sa_len != 0) |
| 277 | return; |
| 278 | break; |
| 279 | case AF_APPLETALK: |
| 280 | len = sizeof(struct sockaddr_at); |
| 281 | break; |
| 282 | default: |
| 283 | printf("%s: Unhandled af=%hhu socklen=%hhu\n" , __func__, |
| 284 | sa->sa_family, sa->sa_len); |
| 285 | return; |
| 286 | } |
| 287 | if (len != sa->sa_len) { |
| 288 | char buf[512]; |
| 289 | sockaddr_format(sa, buf, sizeof(buf)); |
| 290 | printf("%s: %p bad len af=%hhu socklen=%hhu len=%u [%s]\n" , |
| 291 | __func__, sa, sa->sa_family, sa->sa_len, |
| 292 | (unsigned)len, buf); |
| 293 | } |
| 294 | } |
| 295 | #else |
| 296 | #define sockaddr_checklen(sa) ((void)0) |
| 297 | #endif |
| 298 | |
| 299 | struct sockaddr * |
| 300 | sockaddr_alloc(sa_family_t af, socklen_t socklen, int flags) |
| 301 | { |
| 302 | struct sockaddr *sa; |
| 303 | socklen_t reallen = MAX(socklen, offsetof(struct sockaddr, sa_data[0])); |
| 304 | |
| 305 | if ((sa = malloc(reallen, M_SOCKADDR, flags)) == NULL) |
| 306 | return NULL; |
| 307 | |
| 308 | sa->sa_family = af; |
| 309 | sa->sa_len = reallen; |
| 310 | sockaddr_checklen(sa); |
| 311 | return sa; |
| 312 | } |
| 313 | |
| 314 | struct sockaddr * |
| 315 | sockaddr_copy(struct sockaddr *dst, socklen_t socklen, |
| 316 | const struct sockaddr *src) |
| 317 | { |
| 318 | if (__predict_false(socklen < src->sa_len)) { |
| 319 | panic("%s: source too long, %d < %d bytes" , __func__, socklen, |
| 320 | src->sa_len); |
| 321 | } |
| 322 | sockaddr_checklen(src); |
| 323 | return memcpy(dst, src, src->sa_len); |
| 324 | } |
| 325 | |
| 326 | struct sockaddr * |
| 327 | sockaddr_externalize(struct sockaddr *dst, socklen_t socklen, |
| 328 | const struct sockaddr *src) |
| 329 | { |
| 330 | struct domain *dom; |
| 331 | |
| 332 | dom = pffinddomain(src->sa_family); |
| 333 | |
| 334 | if (dom != NULL && dom->dom_sockaddr_externalize != NULL) |
| 335 | return (*dom->dom_sockaddr_externalize)(dst, socklen, src); |
| 336 | |
| 337 | return sockaddr_copy(dst, socklen, src); |
| 338 | } |
| 339 | |
| 340 | int |
| 341 | sockaddr_cmp(const struct sockaddr *sa1, const struct sockaddr *sa2) |
| 342 | { |
| 343 | int len, rc; |
| 344 | struct domain *dom; |
| 345 | |
| 346 | if (sa1->sa_family != sa2->sa_family) |
| 347 | return sa1->sa_family - sa2->sa_family; |
| 348 | |
| 349 | dom = pffinddomain(sa1->sa_family); |
| 350 | |
| 351 | if (dom != NULL && dom->dom_sockaddr_cmp != NULL) |
| 352 | return (*dom->dom_sockaddr_cmp)(sa1, sa2); |
| 353 | |
| 354 | len = MIN(sa1->sa_len, sa2->sa_len); |
| 355 | |
| 356 | if (dom == NULL || dom->dom_sa_cmplen == 0) { |
| 357 | if ((rc = memcmp(sa1, sa2, len)) != 0) |
| 358 | return rc; |
| 359 | return sa1->sa_len - sa2->sa_len; |
| 360 | } |
| 361 | |
| 362 | if ((rc = memcmp((const char *)sa1 + dom->dom_sa_cmpofs, |
| 363 | (const char *)sa2 + dom->dom_sa_cmpofs, |
| 364 | MIN(dom->dom_sa_cmplen, |
| 365 | len - MIN(len, dom->dom_sa_cmpofs)))) != 0) |
| 366 | return rc; |
| 367 | |
| 368 | return MIN(dom->dom_sa_cmplen + dom->dom_sa_cmpofs, sa1->sa_len) - |
| 369 | MIN(dom->dom_sa_cmplen + dom->dom_sa_cmpofs, sa2->sa_len); |
| 370 | } |
| 371 | |
| 372 | struct sockaddr * |
| 373 | sockaddr_dup(const struct sockaddr *src, int flags) |
| 374 | { |
| 375 | struct sockaddr *dst; |
| 376 | |
| 377 | if ((dst = sockaddr_alloc(src->sa_family, src->sa_len, flags)) == NULL) |
| 378 | return NULL; |
| 379 | |
| 380 | return sockaddr_copy(dst, dst->sa_len, src); |
| 381 | } |
| 382 | |
| 383 | void |
| 384 | sockaddr_free(struct sockaddr *sa) |
| 385 | { |
| 386 | free(sa, M_SOCKADDR); |
| 387 | } |
| 388 | |
| 389 | static int |
| 390 | sun_print(char *buf, size_t len, const void *v) |
| 391 | { |
| 392 | const struct sockaddr_un *sun = v; |
| 393 | return snprintf(buf, len, "%s" , sun->sun_path); |
| 394 | } |
| 395 | |
| 396 | int |
| 397 | sockaddr_format(const struct sockaddr *sa, char *buf, size_t len) |
| 398 | { |
| 399 | size_t plen = 0; |
| 400 | |
| 401 | if (sa == NULL) |
| 402 | return strlcpy(buf, "(null)" , len); |
| 403 | |
| 404 | switch (sa->sa_family) { |
| 405 | case AF_LOCAL: |
| 406 | plen = strlcpy(buf, "unix: " , len); |
| 407 | break; |
| 408 | case AF_INET: |
| 409 | plen = strlcpy(buf, "inet: " , len); |
| 410 | break; |
| 411 | case AF_INET6: |
| 412 | plen = strlcpy(buf, "inet6: " , len); |
| 413 | break; |
| 414 | case AF_LINK: |
| 415 | plen = strlcpy(buf, "link: " , len); |
| 416 | break; |
| 417 | case AF_APPLETALK: |
| 418 | plen = strlcpy(buf, "atalk: " , len); |
| 419 | break; |
| 420 | default: |
| 421 | return snprintf(buf, len, "(unknown socket family %d)" , |
| 422 | (int)sa->sa_family); |
| 423 | } |
| 424 | |
| 425 | buf += plen; |
| 426 | if (plen > len) |
| 427 | len = 0; |
| 428 | else |
| 429 | len -= plen; |
| 430 | |
| 431 | switch (sa->sa_family) { |
| 432 | case AF_LOCAL: |
| 433 | return sun_print(buf, len, sa); |
| 434 | case AF_INET: |
| 435 | return sin_print(buf, len, sa); |
| 436 | case AF_INET6: |
| 437 | return sin6_print(buf, len, sa); |
| 438 | case AF_LINK: |
| 439 | return sdl_print(buf, len, sa); |
| 440 | case AF_APPLETALK: |
| 441 | return sat_print(buf, len, sa); |
| 442 | default: |
| 443 | panic("bad family %hhu" , sa->sa_family); |
| 444 | } |
| 445 | } |
| 446 | |
| 447 | /* |
| 448 | * sysctl helper to stuff PF_LOCAL pcbs into sysctl structures |
| 449 | */ |
| 450 | static void |
| 451 | sysctl_dounpcb(struct kinfo_pcb *pcb, const struct socket *so) |
| 452 | { |
| 453 | struct unpcb *unp = sotounpcb(so); |
| 454 | struct sockaddr_un *un = unp->unp_addr; |
| 455 | |
| 456 | memset(pcb, 0, sizeof(*pcb)); |
| 457 | |
| 458 | pcb->ki_family = so->so_proto->pr_domain->dom_family; |
| 459 | pcb->ki_type = so->so_proto->pr_type; |
| 460 | pcb->ki_protocol = so->so_proto->pr_protocol; |
| 461 | pcb->ki_pflags = unp->unp_flags; |
| 462 | |
| 463 | pcb->ki_pcbaddr = PTRTOUINT64(unp); |
| 464 | /* pcb->ki_ppcbaddr = unp has no ppcb... */ |
| 465 | pcb->ki_sockaddr = PTRTOUINT64(so); |
| 466 | |
| 467 | pcb->ki_sostate = so->so_state; |
| 468 | /* pcb->ki_prstate = unp has no state... */ |
| 469 | |
| 470 | pcb->ki_rcvq = so->so_rcv.sb_cc; |
| 471 | pcb->ki_sndq = so->so_snd.sb_cc; |
| 472 | |
| 473 | un = (struct sockaddr_un *)pcb->ki_spad; |
| 474 | /* |
| 475 | * local domain sockets may bind without having a local |
| 476 | * endpoint. bleah! |
| 477 | */ |
| 478 | if (unp->unp_addr != NULL) { |
| 479 | /* |
| 480 | * We've added one to sun_len when allocating to |
| 481 | * hold terminating NUL which we want here. See |
| 482 | * makeun(). |
| 483 | */ |
| 484 | memcpy(un, unp->unp_addr, |
| 485 | min(sizeof(pcb->ki_spad), unp->unp_addr->sun_len + 1)); |
| 486 | } |
| 487 | else { |
| 488 | un->sun_len = offsetof(struct sockaddr_un, sun_path); |
| 489 | un->sun_family = pcb->ki_family; |
| 490 | } |
| 491 | if (unp->unp_conn != NULL) { |
| 492 | un = (struct sockaddr_un *)pcb->ki_dpad; |
| 493 | if (unp->unp_conn->unp_addr != NULL) { |
| 494 | memcpy(un, unp->unp_conn->unp_addr, |
| 495 | min(sizeof(pcb->ki_dpad), unp->unp_conn->unp_addr->sun_len + 1)); |
| 496 | } |
| 497 | else { |
| 498 | un->sun_len = offsetof(struct sockaddr_un, sun_path); |
| 499 | un->sun_family = pcb->ki_family; |
| 500 | } |
| 501 | } |
| 502 | |
| 503 | pcb->ki_inode = unp->unp_ino; |
| 504 | pcb->ki_vnode = PTRTOUINT64(unp->unp_vnode); |
| 505 | pcb->ki_conn = PTRTOUINT64(unp->unp_conn); |
| 506 | pcb->ki_refs = PTRTOUINT64(unp->unp_refs); |
| 507 | pcb->ki_nextref = PTRTOUINT64(unp->unp_nextref); |
| 508 | } |
| 509 | |
| 510 | static int |
| 511 | sysctl_unpcblist(SYSCTLFN_ARGS) |
| 512 | { |
| 513 | struct file *fp, *dfp; |
| 514 | struct socket *so; |
| 515 | struct kinfo_pcb pcb; |
| 516 | char *dp; |
| 517 | size_t len, needed, elem_size, out_size; |
| 518 | int error, elem_count, pf, type; |
| 519 | |
| 520 | if (namelen == 1 && name[0] == CTL_QUERY) |
| 521 | return sysctl_query(SYSCTLFN_CALL(rnode)); |
| 522 | |
| 523 | if (namelen != 4) |
| 524 | return EINVAL; |
| 525 | |
| 526 | if (oldp != NULL) { |
| 527 | len = *oldlenp; |
| 528 | elem_size = name[2]; |
| 529 | elem_count = name[3]; |
| 530 | if (elem_size != sizeof(pcb)) |
| 531 | return EINVAL; |
| 532 | } else { |
| 533 | len = 0; |
| 534 | elem_size = sizeof(pcb); |
| 535 | elem_count = INT_MAX; |
| 536 | } |
| 537 | error = 0; |
| 538 | dp = oldp; |
| 539 | out_size = elem_size; |
| 540 | needed = 0; |
| 541 | |
| 542 | if (name - oname != 4) |
| 543 | return EINVAL; |
| 544 | |
| 545 | pf = oname[1]; |
| 546 | type = oname[2]; |
| 547 | |
| 548 | /* |
| 549 | * allocate dummy file descriptor to make position in list. |
| 550 | */ |
| 551 | sysctl_unlock(); |
| 552 | if ((dfp = fgetdummy()) == NULL) { |
| 553 | sysctl_relock(); |
| 554 | return ENOMEM; |
| 555 | } |
| 556 | |
| 557 | /* |
| 558 | * there's no "list" of local domain sockets, so we have |
| 559 | * to walk the file list looking for them. :-/ |
| 560 | */ |
| 561 | mutex_enter(&filelist_lock); |
| 562 | LIST_FOREACH(fp, &filehead, f_list) { |
| 563 | if (fp->f_count == 0 || fp->f_type != DTYPE_SOCKET || |
| 564 | fp->f_socket == NULL) |
| 565 | continue; |
| 566 | so = fp->f_socket; |
| 567 | if (so->so_type != type) |
| 568 | continue; |
| 569 | if (so->so_proto->pr_domain->dom_family != pf) |
| 570 | continue; |
| 571 | if (kauth_authorize_network(l->l_cred, KAUTH_NETWORK_SOCKET, |
| 572 | KAUTH_REQ_NETWORK_SOCKET_CANSEE, so, NULL, NULL) != 0) |
| 573 | continue; |
| 574 | if (len >= elem_size && elem_count > 0) { |
| 575 | mutex_enter(&fp->f_lock); |
| 576 | fp->f_count++; |
| 577 | mutex_exit(&fp->f_lock); |
| 578 | LIST_INSERT_AFTER(fp, dfp, f_list); |
| 579 | mutex_exit(&filelist_lock); |
| 580 | sysctl_dounpcb(&pcb, so); |
| 581 | error = copyout(&pcb, dp, out_size); |
| 582 | closef(fp); |
| 583 | mutex_enter(&filelist_lock); |
| 584 | LIST_REMOVE(dfp, f_list); |
| 585 | if (error) |
| 586 | break; |
| 587 | dp += elem_size; |
| 588 | len -= elem_size; |
| 589 | } |
| 590 | needed += elem_size; |
| 591 | if (elem_count > 0 && elem_count != INT_MAX) |
| 592 | elem_count--; |
| 593 | } |
| 594 | mutex_exit(&filelist_lock); |
| 595 | fputdummy(dfp); |
| 596 | *oldlenp = needed; |
| 597 | if (oldp == NULL) |
| 598 | *oldlenp += PCB_SLOP * sizeof(struct kinfo_pcb); |
| 599 | sysctl_relock(); |
| 600 | |
| 601 | return error; |
| 602 | } |
| 603 | |
| 604 | static void |
| 605 | sysctl_net_setup(void) |
| 606 | { |
| 607 | |
| 608 | KASSERT(domain_sysctllog == NULL); |
| 609 | sysctl_createv(&domain_sysctllog, 0, NULL, NULL, |
| 610 | CTLFLAG_PERMANENT, |
| 611 | CTLTYPE_NODE, "local" , |
| 612 | SYSCTL_DESCR("PF_LOCAL related settings" ), |
| 613 | NULL, 0, NULL, 0, |
| 614 | CTL_NET, PF_LOCAL, CTL_EOL); |
| 615 | sysctl_createv(&domain_sysctllog, 0, NULL, NULL, |
| 616 | CTLFLAG_PERMANENT, |
| 617 | CTLTYPE_NODE, "stream" , |
| 618 | SYSCTL_DESCR("SOCK_STREAM settings" ), |
| 619 | NULL, 0, NULL, 0, |
| 620 | CTL_NET, PF_LOCAL, SOCK_STREAM, CTL_EOL); |
| 621 | sysctl_createv(&domain_sysctllog, 0, NULL, NULL, |
| 622 | CTLFLAG_PERMANENT, |
| 623 | CTLTYPE_NODE, "seqpacket" , |
| 624 | SYSCTL_DESCR("SOCK_SEQPACKET settings" ), |
| 625 | NULL, 0, NULL, 0, |
| 626 | CTL_NET, PF_LOCAL, SOCK_SEQPACKET, CTL_EOL); |
| 627 | sysctl_createv(&domain_sysctllog, 0, NULL, NULL, |
| 628 | CTLFLAG_PERMANENT, |
| 629 | CTLTYPE_NODE, "dgram" , |
| 630 | SYSCTL_DESCR("SOCK_DGRAM settings" ), |
| 631 | NULL, 0, NULL, 0, |
| 632 | CTL_NET, PF_LOCAL, SOCK_DGRAM, CTL_EOL); |
| 633 | |
| 634 | sysctl_createv(&domain_sysctllog, 0, NULL, NULL, |
| 635 | CTLFLAG_PERMANENT, |
| 636 | CTLTYPE_STRUCT, "pcblist" , |
| 637 | SYSCTL_DESCR("SOCK_STREAM protocol control block list" ), |
| 638 | sysctl_unpcblist, 0, NULL, 0, |
| 639 | CTL_NET, PF_LOCAL, SOCK_STREAM, CTL_CREATE, CTL_EOL); |
| 640 | sysctl_createv(&domain_sysctllog, 0, NULL, NULL, |
| 641 | CTLFLAG_PERMANENT, |
| 642 | CTLTYPE_STRUCT, "pcblist" , |
| 643 | SYSCTL_DESCR("SOCK_SEQPACKET protocol control " |
| 644 | "block list" ), |
| 645 | sysctl_unpcblist, 0, NULL, 0, |
| 646 | CTL_NET, PF_LOCAL, SOCK_SEQPACKET, CTL_CREATE, CTL_EOL); |
| 647 | sysctl_createv(&domain_sysctllog, 0, NULL, NULL, |
| 648 | CTLFLAG_PERMANENT, |
| 649 | CTLTYPE_STRUCT, "pcblist" , |
| 650 | SYSCTL_DESCR("SOCK_DGRAM protocol control block list" ), |
| 651 | sysctl_unpcblist, 0, NULL, 0, |
| 652 | CTL_NET, PF_LOCAL, SOCK_DGRAM, CTL_CREATE, CTL_EOL); |
| 653 | } |
| 654 | |
| 655 | void |
| 656 | pfctlinput(int cmd, const struct sockaddr *sa) |
| 657 | { |
| 658 | struct domain *dp; |
| 659 | const struct protosw *pr; |
| 660 | |
| 661 | DOMAIN_FOREACH(dp) { |
| 662 | for (pr = dp->dom_protosw; pr < dp->dom_protoswNPROTOSW; pr++) { |
| 663 | if (pr->pr_ctlinput != NULL) |
| 664 | (*pr->pr_ctlinput)(cmd, sa, NULL); |
| 665 | } |
| 666 | } |
| 667 | } |
| 668 | |
| 669 | void |
| 670 | pfctlinput2(int cmd, const struct sockaddr *sa, void *ctlparam) |
| 671 | { |
| 672 | struct domain *dp; |
| 673 | const struct protosw *pr; |
| 674 | |
| 675 | if (sa == NULL) |
| 676 | return; |
| 677 | |
| 678 | DOMAIN_FOREACH(dp) { |
| 679 | /* |
| 680 | * the check must be made by xx_ctlinput() anyways, to |
| 681 | * make sure we use data item pointed to by ctlparam in |
| 682 | * correct way. the following check is made just for safety. |
| 683 | */ |
| 684 | if (dp->dom_family != sa->sa_family) |
| 685 | continue; |
| 686 | |
| 687 | for (pr = dp->dom_protosw; pr < dp->dom_protoswNPROTOSW; pr++) { |
| 688 | if (pr->pr_ctlinput != NULL) |
| 689 | (*pr->pr_ctlinput)(cmd, sa, ctlparam); |
| 690 | } |
| 691 | } |
| 692 | } |
| 693 | |
| 694 | void |
| 695 | pfslowtimo(void *arg) |
| 696 | { |
| 697 | struct domain *dp; |
| 698 | const struct protosw *pr; |
| 699 | |
| 700 | pfslowtimo_now++; |
| 701 | |
| 702 | DOMAIN_FOREACH(dp) { |
| 703 | for (pr = dp->dom_protosw; pr < dp->dom_protoswNPROTOSW; pr++) |
| 704 | if (pr->pr_slowtimo) |
| 705 | (*pr->pr_slowtimo)(); |
| 706 | } |
| 707 | callout_schedule(&pfslowtimo_ch, hz / PR_SLOWHZ); |
| 708 | } |
| 709 | |
| 710 | void |
| 711 | pffasttimo(void *arg) |
| 712 | { |
| 713 | struct domain *dp; |
| 714 | const struct protosw *pr; |
| 715 | |
| 716 | pffasttimo_now++; |
| 717 | |
| 718 | DOMAIN_FOREACH(dp) { |
| 719 | for (pr = dp->dom_protosw; pr < dp->dom_protoswNPROTOSW; pr++) |
| 720 | if (pr->pr_fasttimo) |
| 721 | (*pr->pr_fasttimo)(); |
| 722 | } |
| 723 | callout_schedule(&pffasttimo_ch, hz / PR_FASTHZ); |
| 724 | } |
| 725 | |