1 /* 2 * Copyright (c) 1982, 1986, 1991, 1993, 1995 3 * The Regents of the University of California. All rights reserved. 4 * 5 * Redistribution and use in source and binary forms, with or without 6 * modification, are permitted provided that the following conditions 7 * are met: 8 * 1. Redistributions of source code must retain the above copyright 9 * notice, this list of conditions and the following disclaimer. 10 * 2. Redistributions in binary form must reproduce the above copyright 11 * notice, this list of conditions and the following disclaimer in the 12 * documentation and/or other materials provided with the distribution. 13 * 3. All advertising materials mentioning features or use of this software 14 * must display the following acknowledgement: 15 * This product includes software developed by the University of 16 * California, Berkeley and its contributors. 17 * 4. Neither the name of the University nor the names of its contributors 18 * may be used to endorse or promote products derived from this software 19 * without specific prior written permission. 20 * 21 * THIS SOFTWARE IS PROVIDED BY THE REGENTS AND CONTRIBUTORS ``AS IS'' AND 22 * ANY EXPRESS OR IMPLIED WARRANTIES, INCLUDING, BUT NOT LIMITED TO, THE 23 * IMPLIED WARRANTIES OF MERCHANTABILITY AND FITNESS FOR A PARTICULAR PURPOSE 24 * ARE DISCLAIMED. IN NO EVENT SHALL THE REGENTS OR CONTRIBUTORS BE LIABLE 25 * FOR ANY DIRECT, INDIRECT, INCIDENTAL, SPECIAL, EXEMPLARY, OR CONSEQUENTIAL 26 * DAMAGES (INCLUDING, BUT NOT LIMITED TO, PROCUREMENT OF SUBSTITUTE GOODS 27 * OR SERVICES; LOSS OF USE, DATA, OR PROFITS; OR BUSINESS INTERRUPTION) 28 * HOWEVER CAUSED AND ON ANY THEORY OF LIABILITY, WHETHER IN CONTRACT, STRICT 29 * LIABILITY, OR TORT (INCLUDING NEGLIGENCE OR OTHERWISE) ARISING IN ANY WAY 30 * OUT OF THE USE OF THIS SOFTWARE, EVEN IF ADVISED OF THE POSSIBILITY OF 31 * SUCH DAMAGE. 32 * 33 * @(#)in_pcb.c 8.4 (Berkeley) 5/24/95 34 * $Id: in_pcb.c,v 1.21 1996/08/23 18:59:05 phk Exp $ 35 */ 36 37 #include <sys/param.h> 38 #include <sys/queue.h> 39 #include <sys/systm.h> 40 #include <sys/malloc.h> 41 #include <sys/mbuf.h> 42 #include <sys/protosw.h> 43 #include <sys/socket.h> 44 #include <sys/socketvar.h> 45 #include <sys/ioctl.h> 46 #include <sys/errno.h> 47 #include <sys/time.h> 48 #include <sys/proc.h> 49 #include <sys/kernel.h> 50 #include <sys/sysctl.h> 51 52 #include <net/if.h> 53 #include <net/route.h> 54 55 #include <netinet/in.h> 56 #include <netinet/in_systm.h> 57 #include <netinet/ip.h> 58 #include <netinet/in_pcb.h> 59 #include <netinet/in_var.h> 60 #include <netinet/ip_var.h> 61 62 struct in_addr zeroin_addr; 63 64 static void in_pcbinshash __P((struct inpcb *)); 65 static void in_rtchange __P((struct inpcb *, int)); 66 67 /* 68 * These configure the range of local port addresses assigned to 69 * "unspecified" outgoing connections/packets/whatever. 70 */ 71 static int ipport_lowfirstauto = IPPORT_RESERVED - 1; /* 1023 */ 72 static int ipport_lowlastauto = IPPORT_RESERVEDSTART; /* 600 */ 73 static int ipport_firstauto = IPPORT_RESERVED; /* 1024 */ 74 static int ipport_lastauto = IPPORT_USERRESERVED; /* 5000 */ 75 static int ipport_hifirstauto = IPPORT_HIFIRSTAUTO; /* 40000 */ 76 static int ipport_hilastauto = IPPORT_HILASTAUTO; /* 44999 */ 77 78 #define RANGECHK(var, min, max) \ 79 if ((var) < (min)) { (var) = (min); } \ 80 else if ((var) > (max)) { (var) = (max); } 81 82 static int 83 sysctl_net_ipport_check SYSCTL_HANDLER_ARGS 84 { 85 int error = sysctl_handle_int(oidp, 86 oidp->oid_arg1, oidp->oid_arg2, req); 87 if (!error) { 88 RANGECHK(ipport_lowfirstauto, 1, IPPORT_RESERVED - 1); 89 RANGECHK(ipport_lowlastauto, 1, IPPORT_RESERVED - 1); 90 RANGECHK(ipport_firstauto, IPPORT_RESERVED, USHRT_MAX); 91 RANGECHK(ipport_lastauto, IPPORT_RESERVED, USHRT_MAX); 92 RANGECHK(ipport_hifirstauto, IPPORT_RESERVED, USHRT_MAX); 93 RANGECHK(ipport_hilastauto, IPPORT_RESERVED, USHRT_MAX); 94 } 95 return error; 96 } 97 98 #undef RANGECHK 99 100 SYSCTL_NODE(_net_inet_ip, IPPROTO_IP, portrange, CTLFLAG_RW, 0, "IP Ports"); 101 102 SYSCTL_PROC(_net_inet_ip_portrange, OID_AUTO, lowfirst, CTLTYPE_INT|CTLFLAG_RW, 103 &ipport_lowfirstauto, 0, &sysctl_net_ipport_check, "I", ""); 104 SYSCTL_PROC(_net_inet_ip_portrange, OID_AUTO, lowlast, CTLTYPE_INT|CTLFLAG_RW, 105 &ipport_lowlastauto, 0, &sysctl_net_ipport_check, "I", ""); 106 SYSCTL_PROC(_net_inet_ip_portrange, OID_AUTO, first, CTLTYPE_INT|CTLFLAG_RW, 107 &ipport_firstauto, 0, &sysctl_net_ipport_check, "I", ""); 108 SYSCTL_PROC(_net_inet_ip_portrange, OID_AUTO, last, CTLTYPE_INT|CTLFLAG_RW, 109 &ipport_lastauto, 0, &sysctl_net_ipport_check, "I", ""); 110 SYSCTL_PROC(_net_inet_ip_portrange, OID_AUTO, hifirst, CTLTYPE_INT|CTLFLAG_RW, 111 &ipport_hifirstauto, 0, &sysctl_net_ipport_check, "I", ""); 112 SYSCTL_PROC(_net_inet_ip_portrange, OID_AUTO, hilast, CTLTYPE_INT|CTLFLAG_RW, 113 &ipport_hilastauto, 0, &sysctl_net_ipport_check, "I", ""); 114 115 int 116 in_pcballoc(so, pcbinfo) 117 struct socket *so; 118 struct inpcbinfo *pcbinfo; 119 { 120 register struct inpcb *inp; 121 int s; 122 123 MALLOC(inp, struct inpcb *, sizeof(*inp), M_PCB, M_NOWAIT); 124 if (inp == NULL) 125 return (ENOBUFS); 126 bzero((caddr_t)inp, sizeof(*inp)); 127 inp->inp_pcbinfo = pcbinfo; 128 inp->inp_socket = so; 129 s = splnet(); 130 LIST_INSERT_HEAD(pcbinfo->listhead, inp, inp_list); 131 in_pcbinshash(inp); 132 splx(s); 133 so->so_pcb = (caddr_t)inp; 134 return (0); 135 } 136 137 int 138 in_pcbbind(inp, nam) 139 register struct inpcb *inp; 140 struct mbuf *nam; 141 { 142 register struct socket *so = inp->inp_socket; 143 unsigned short *lastport = &inp->inp_pcbinfo->lastport; 144 struct sockaddr_in *sin; 145 struct proc *p = curproc; /* XXX */ 146 u_short lport = 0; 147 int wild = 0, reuseport = (so->so_options & SO_REUSEPORT); 148 int error; 149 150 if (in_ifaddr == 0) 151 return (EADDRNOTAVAIL); 152 if (inp->inp_lport || inp->inp_laddr.s_addr != INADDR_ANY) 153 return (EINVAL); 154 if ((so->so_options & (SO_REUSEADDR|SO_REUSEPORT)) == 0 && 155 ((so->so_proto->pr_flags & PR_CONNREQUIRED) == 0 || 156 (so->so_options & SO_ACCEPTCONN) == 0)) 157 wild = 1; 158 if (nam) { 159 sin = mtod(nam, struct sockaddr_in *); 160 if (nam->m_len != sizeof (*sin)) 161 return (EINVAL); 162 #ifdef notdef 163 /* 164 * We should check the family, but old programs 165 * incorrectly fail to initialize it. 166 */ 167 if (sin->sin_family != AF_INET) 168 return (EAFNOSUPPORT); 169 #endif 170 lport = sin->sin_port; 171 if (IN_MULTICAST(ntohl(sin->sin_addr.s_addr))) { 172 /* 173 * Treat SO_REUSEADDR as SO_REUSEPORT for multicast; 174 * allow complete duplication of binding if 175 * SO_REUSEPORT is set, or if SO_REUSEADDR is set 176 * and a multicast address is bound on both 177 * new and duplicated sockets. 178 */ 179 if (so->so_options & SO_REUSEADDR) 180 reuseport = SO_REUSEADDR|SO_REUSEPORT; 181 } else if (sin->sin_addr.s_addr != INADDR_ANY) { 182 sin->sin_port = 0; /* yech... */ 183 if (ifa_ifwithaddr((struct sockaddr *)sin) == 0) 184 return (EADDRNOTAVAIL); 185 } 186 if (lport) { 187 struct inpcb *t; 188 189 /* GROSS */ 190 if (ntohs(lport) < IPPORT_RESERVED && 191 (error = suser(p->p_ucred, &p->p_acflag))) 192 return (EACCES); 193 t = in_pcblookup(inp->inp_pcbinfo, zeroin_addr, 0, 194 sin->sin_addr, lport, wild); 195 if (t && (reuseport & t->inp_socket->so_options) == 0) 196 return (EADDRINUSE); 197 } 198 inp->inp_laddr = sin->sin_addr; 199 } 200 if (lport == 0) { 201 ushort first, last; 202 int count; 203 204 inp->inp_flags |= INP_ANONPORT; 205 206 if (inp->inp_flags & INP_HIGHPORT) { 207 first = ipport_hifirstauto; /* sysctl */ 208 last = ipport_hilastauto; 209 } else if (inp->inp_flags & INP_LOWPORT) { 210 if (error = suser(p->p_ucred, &p->p_acflag)) 211 return (EACCES); 212 first = ipport_lowfirstauto; /* 1023 */ 213 last = ipport_lowlastauto; /* 600 */ 214 } else { 215 first = ipport_firstauto; /* sysctl */ 216 last = ipport_lastauto; 217 } 218 /* 219 * Simple check to ensure all ports are not used up causing 220 * a deadlock here. 221 * 222 * We split the two cases (up and down) so that the direction 223 * is not being tested on each round of the loop. 224 */ 225 if (first > last) { 226 /* 227 * counting down 228 */ 229 count = first - last; 230 231 do { 232 if (count-- <= 0) /* completely used? */ 233 return (EADDRNOTAVAIL); 234 --*lastport; 235 if (*lastport > first || *lastport < last) 236 *lastport = first; 237 lport = htons(*lastport); 238 } while (in_pcblookup(inp->inp_pcbinfo, 239 zeroin_addr, 0, inp->inp_laddr, lport, wild)); 240 } else { 241 /* 242 * counting up 243 */ 244 count = last - first; 245 246 do { 247 if (count-- <= 0) /* completely used? */ 248 return (EADDRNOTAVAIL); 249 ++*lastport; 250 if (*lastport < first || *lastport > last) 251 *lastport = first; 252 lport = htons(*lastport); 253 } while (in_pcblookup(inp->inp_pcbinfo, 254 zeroin_addr, 0, inp->inp_laddr, lport, wild)); 255 } 256 } 257 inp->inp_lport = lport; 258 in_pcbrehash(inp); 259 return (0); 260 } 261 262 /* 263 * Transform old in_pcbconnect() into an inner subroutine for new 264 * in_pcbconnect(): Do some validity-checking on the remote 265 * address (in mbuf 'nam') and then determine local host address 266 * (i.e., which interface) to use to access that remote host. 267 * 268 * This preserves definition of in_pcbconnect(), while supporting a 269 * slightly different version for T/TCP. (This is more than 270 * a bit of a kludge, but cleaning up the internal interfaces would 271 * have forced minor changes in every protocol). 272 */ 273 274 int 275 in_pcbladdr(inp, nam, plocal_sin) 276 register struct inpcb *inp; 277 struct mbuf *nam; 278 struct sockaddr_in **plocal_sin; 279 { 280 struct in_ifaddr *ia; 281 register struct sockaddr_in *sin = mtod(nam, struct sockaddr_in *); 282 283 if (nam->m_len != sizeof (*sin)) 284 return (EINVAL); 285 if (sin->sin_family != AF_INET) 286 return (EAFNOSUPPORT); 287 if (sin->sin_port == 0) 288 return (EADDRNOTAVAIL); 289 if (in_ifaddr) { 290 /* 291 * If the destination address is INADDR_ANY, 292 * use the primary local address. 293 * If the supplied address is INADDR_BROADCAST, 294 * and the primary interface supports broadcast, 295 * choose the broadcast address for that interface. 296 */ 297 #define satosin(sa) ((struct sockaddr_in *)(sa)) 298 #define sintosa(sin) ((struct sockaddr *)(sin)) 299 #define ifatoia(ifa) ((struct in_ifaddr *)(ifa)) 300 if (sin->sin_addr.s_addr == INADDR_ANY) 301 sin->sin_addr = IA_SIN(in_ifaddr)->sin_addr; 302 else if (sin->sin_addr.s_addr == (u_long)INADDR_BROADCAST && 303 (in_ifaddr->ia_ifp->if_flags & IFF_BROADCAST)) 304 sin->sin_addr = satosin(&in_ifaddr->ia_broadaddr)->sin_addr; 305 } 306 if (inp->inp_laddr.s_addr == INADDR_ANY) { 307 register struct route *ro; 308 309 ia = (struct in_ifaddr *)0; 310 /* 311 * If route is known or can be allocated now, 312 * our src addr is taken from the i/f, else punt. 313 */ 314 ro = &inp->inp_route; 315 if (ro->ro_rt && 316 (satosin(&ro->ro_dst)->sin_addr.s_addr != 317 sin->sin_addr.s_addr || 318 inp->inp_socket->so_options & SO_DONTROUTE)) { 319 RTFREE(ro->ro_rt); 320 ro->ro_rt = (struct rtentry *)0; 321 } 322 if ((inp->inp_socket->so_options & SO_DONTROUTE) == 0 && /*XXX*/ 323 (ro->ro_rt == (struct rtentry *)0 || 324 ro->ro_rt->rt_ifp == (struct ifnet *)0)) { 325 /* No route yet, so try to acquire one */ 326 ro->ro_dst.sa_family = AF_INET; 327 ro->ro_dst.sa_len = sizeof(struct sockaddr_in); 328 ((struct sockaddr_in *) &ro->ro_dst)->sin_addr = 329 sin->sin_addr; 330 rtalloc(ro); 331 } 332 /* 333 * If we found a route, use the address 334 * corresponding to the outgoing interface 335 * unless it is the loopback (in case a route 336 * to our address on another net goes to loopback). 337 */ 338 if (ro->ro_rt && !(ro->ro_rt->rt_ifp->if_flags & IFF_LOOPBACK)) 339 ia = ifatoia(ro->ro_rt->rt_ifa); 340 if (ia == 0) { 341 u_short fport = sin->sin_port; 342 343 sin->sin_port = 0; 344 ia = ifatoia(ifa_ifwithdstaddr(sintosa(sin))); 345 if (ia == 0) 346 ia = ifatoia(ifa_ifwithnet(sintosa(sin))); 347 sin->sin_port = fport; 348 if (ia == 0) 349 ia = in_ifaddr; 350 if (ia == 0) 351 return (EADDRNOTAVAIL); 352 } 353 /* 354 * If the destination address is multicast and an outgoing 355 * interface has been set as a multicast option, use the 356 * address of that interface as our source address. 357 */ 358 if (IN_MULTICAST(ntohl(sin->sin_addr.s_addr)) && 359 inp->inp_moptions != NULL) { 360 struct ip_moptions *imo; 361 struct ifnet *ifp; 362 363 imo = inp->inp_moptions; 364 if (imo->imo_multicast_ifp != NULL) { 365 ifp = imo->imo_multicast_ifp; 366 for (ia = in_ifaddr; ia; ia = ia->ia_next) 367 if (ia->ia_ifp == ifp) 368 break; 369 if (ia == 0) 370 return (EADDRNOTAVAIL); 371 } 372 } 373 /* 374 * Don't do pcblookup call here; return interface in plocal_sin 375 * and exit to caller, that will do the lookup. 376 */ 377 *plocal_sin = &ia->ia_addr; 378 379 } 380 return(0); 381 } 382 383 /* 384 * Outer subroutine: 385 * Connect from a socket to a specified address. 386 * Both address and port must be specified in argument sin. 387 * If don't have a local address for this socket yet, 388 * then pick one. 389 */ 390 int 391 in_pcbconnect(inp, nam) 392 register struct inpcb *inp; 393 struct mbuf *nam; 394 { 395 struct sockaddr_in *ifaddr; 396 register struct sockaddr_in *sin = mtod(nam, struct sockaddr_in *); 397 int error; 398 399 /* 400 * Call inner routine, to assign local interface address. 401 */ 402 if (error = in_pcbladdr(inp, nam, &ifaddr)) 403 return(error); 404 405 if (in_pcblookuphash(inp->inp_pcbinfo, sin->sin_addr, sin->sin_port, 406 inp->inp_laddr.s_addr ? inp->inp_laddr : ifaddr->sin_addr, 407 inp->inp_lport, 0) != NULL) 408 return (EADDRINUSE); 409 if (inp->inp_laddr.s_addr == INADDR_ANY) { 410 if (inp->inp_lport == 0) 411 (void)in_pcbbind(inp, (struct mbuf *)0); 412 inp->inp_laddr = ifaddr->sin_addr; 413 } 414 inp->inp_faddr = sin->sin_addr; 415 inp->inp_fport = sin->sin_port; 416 in_pcbrehash(inp); 417 return (0); 418 } 419 420 void 421 in_pcbdisconnect(inp) 422 struct inpcb *inp; 423 { 424 425 inp->inp_faddr.s_addr = INADDR_ANY; 426 inp->inp_fport = 0; 427 in_pcbrehash(inp); 428 if (inp->inp_socket->so_state & SS_NOFDREF) 429 in_pcbdetach(inp); 430 } 431 432 void 433 in_pcbdetach(inp) 434 struct inpcb *inp; 435 { 436 struct socket *so = inp->inp_socket; 437 int s; 438 439 so->so_pcb = 0; 440 sofree(so); 441 if (inp->inp_options) 442 (void)m_free(inp->inp_options); 443 if (inp->inp_route.ro_rt) 444 rtfree(inp->inp_route.ro_rt); 445 ip_freemoptions(inp->inp_moptions); 446 s = splnet(); 447 LIST_REMOVE(inp, inp_hash); 448 LIST_REMOVE(inp, inp_list); 449 splx(s); 450 FREE(inp, M_PCB); 451 } 452 453 void 454 in_setsockaddr(inp, nam) 455 register struct inpcb *inp; 456 struct mbuf *nam; 457 { 458 register struct sockaddr_in *sin; 459 460 nam->m_len = sizeof (*sin); 461 sin = mtod(nam, struct sockaddr_in *); 462 bzero((caddr_t)sin, sizeof (*sin)); 463 sin->sin_family = AF_INET; 464 sin->sin_len = sizeof(*sin); 465 sin->sin_port = inp->inp_lport; 466 sin->sin_addr = inp->inp_laddr; 467 } 468 469 void 470 in_setpeeraddr(inp, nam) 471 struct inpcb *inp; 472 struct mbuf *nam; 473 { 474 register struct sockaddr_in *sin; 475 476 nam->m_len = sizeof (*sin); 477 sin = mtod(nam, struct sockaddr_in *); 478 bzero((caddr_t)sin, sizeof (*sin)); 479 sin->sin_family = AF_INET; 480 sin->sin_len = sizeof(*sin); 481 sin->sin_port = inp->inp_fport; 482 sin->sin_addr = inp->inp_faddr; 483 } 484 485 /* 486 * Pass some notification to all connections of a protocol 487 * associated with address dst. The local address and/or port numbers 488 * may be specified to limit the search. The "usual action" will be 489 * taken, depending on the ctlinput cmd. The caller must filter any 490 * cmds that are uninteresting (e.g., no error in the map). 491 * Call the protocol specific routine (if any) to report 492 * any errors for each matching socket. 493 * 494 * Must be called at splnet. 495 */ 496 void 497 in_pcbnotify(head, dst, fport_arg, laddr, lport_arg, cmd, notify) 498 struct inpcbhead *head; 499 struct sockaddr *dst; 500 u_int fport_arg, lport_arg; 501 struct in_addr laddr; 502 int cmd; 503 void (*notify) __P((struct inpcb *, int)); 504 { 505 register struct inpcb *inp, *oinp; 506 struct in_addr faddr; 507 u_short fport = fport_arg, lport = lport_arg; 508 int errno, s; 509 510 if ((unsigned)cmd > PRC_NCMDS || dst->sa_family != AF_INET) 511 return; 512 faddr = ((struct sockaddr_in *)dst)->sin_addr; 513 if (faddr.s_addr == INADDR_ANY) 514 return; 515 516 /* 517 * Redirects go to all references to the destination, 518 * and use in_rtchange to invalidate the route cache. 519 * Dead host indications: notify all references to the destination. 520 * Otherwise, if we have knowledge of the local port and address, 521 * deliver only to that socket. 522 */ 523 if (PRC_IS_REDIRECT(cmd) || cmd == PRC_HOSTDEAD) { 524 fport = 0; 525 lport = 0; 526 laddr.s_addr = 0; 527 if (cmd != PRC_HOSTDEAD) 528 notify = in_rtchange; 529 } 530 errno = inetctlerrmap[cmd]; 531 s = splnet(); 532 for (inp = head->lh_first; inp != NULL;) { 533 if (inp->inp_faddr.s_addr != faddr.s_addr || 534 inp->inp_socket == 0 || 535 (lport && inp->inp_lport != lport) || 536 (laddr.s_addr && inp->inp_laddr.s_addr != laddr.s_addr) || 537 (fport && inp->inp_fport != fport)) { 538 inp = inp->inp_list.le_next; 539 continue; 540 } 541 oinp = inp; 542 inp = inp->inp_list.le_next; 543 if (notify) 544 (*notify)(oinp, errno); 545 } 546 splx(s); 547 } 548 549 /* 550 * Check for alternatives when higher level complains 551 * about service problems. For now, invalidate cached 552 * routing information. If the route was created dynamically 553 * (by a redirect), time to try a default gateway again. 554 */ 555 void 556 in_losing(inp) 557 struct inpcb *inp; 558 { 559 register struct rtentry *rt; 560 struct rt_addrinfo info; 561 562 if ((rt = inp->inp_route.ro_rt)) { 563 inp->inp_route.ro_rt = 0; 564 bzero((caddr_t)&info, sizeof(info)); 565 info.rti_info[RTAX_DST] = 566 (struct sockaddr *)&inp->inp_route.ro_dst; 567 info.rti_info[RTAX_GATEWAY] = rt->rt_gateway; 568 info.rti_info[RTAX_NETMASK] = rt_mask(rt); 569 rt_missmsg(RTM_LOSING, &info, rt->rt_flags, 0); 570 if (rt->rt_flags & RTF_DYNAMIC) 571 (void) rtrequest(RTM_DELETE, rt_key(rt), 572 rt->rt_gateway, rt_mask(rt), rt->rt_flags, 573 (struct rtentry **)0); 574 else 575 /* 576 * A new route can be allocated 577 * the next time output is attempted. 578 */ 579 rtfree(rt); 580 } 581 } 582 583 /* 584 * After a routing change, flush old routing 585 * and allocate a (hopefully) better one. 586 */ 587 static void 588 in_rtchange(inp, errno) 589 register struct inpcb *inp; 590 int errno; 591 { 592 if (inp->inp_route.ro_rt) { 593 rtfree(inp->inp_route.ro_rt); 594 inp->inp_route.ro_rt = 0; 595 /* 596 * A new route can be allocated the next time 597 * output is attempted. 598 */ 599 } 600 } 601 602 struct inpcb * 603 in_pcblookup(pcbinfo, faddr, fport_arg, laddr, lport_arg, wild_okay) 604 struct inpcbinfo *pcbinfo; 605 struct in_addr faddr, laddr; 606 u_int fport_arg, lport_arg; 607 int wild_okay; 608 { 609 register struct inpcb *inp, *match = NULL; 610 int matchwild = 3, wildcard; 611 u_short fport = fport_arg, lport = lport_arg; 612 int s; 613 614 s = splnet(); 615 616 for (inp = pcbinfo->listhead->lh_first; inp != NULL; inp = inp->inp_list.le_next) { 617 if (inp->inp_lport != lport) 618 continue; 619 wildcard = 0; 620 if (inp->inp_faddr.s_addr != INADDR_ANY) { 621 if (faddr.s_addr == INADDR_ANY) 622 wildcard++; 623 else if (inp->inp_faddr.s_addr != faddr.s_addr || 624 inp->inp_fport != fport) 625 continue; 626 } else { 627 if (faddr.s_addr != INADDR_ANY) 628 wildcard++; 629 } 630 if (inp->inp_laddr.s_addr != INADDR_ANY) { 631 if (laddr.s_addr == INADDR_ANY) 632 wildcard++; 633 else if (inp->inp_laddr.s_addr != laddr.s_addr) 634 continue; 635 } else { 636 if (laddr.s_addr != INADDR_ANY) 637 wildcard++; 638 } 639 if (wildcard && wild_okay == 0) 640 continue; 641 if (wildcard < matchwild) { 642 match = inp; 643 matchwild = wildcard; 644 if (matchwild == 0) { 645 break; 646 } 647 } 648 } 649 splx(s); 650 return (match); 651 } 652 653 /* 654 * Lookup PCB in hash list. 655 */ 656 struct inpcb * 657 in_pcblookuphash(pcbinfo, faddr, fport_arg, laddr, lport_arg, wildcard) 658 struct inpcbinfo *pcbinfo; 659 struct in_addr faddr, laddr; 660 u_int fport_arg, lport_arg; 661 int wildcard; 662 { 663 struct inpcbhead *head; 664 register struct inpcb *inp; 665 u_short fport = fport_arg, lport = lport_arg; 666 int s; 667 668 s = splnet(); 669 /* 670 * First look for an exact match. 671 */ 672 head = &pcbinfo->hashbase[(faddr.s_addr + lport + fport) % pcbinfo->hashsize]; 673 for (inp = head->lh_first; inp != NULL; inp = inp->inp_hash.le_next) { 674 if (inp->inp_faddr.s_addr == faddr.s_addr && 675 inp->inp_fport == fport && inp->inp_lport == lport && 676 inp->inp_laddr.s_addr == laddr.s_addr) 677 goto found; 678 } 679 if (wildcard) { 680 struct inpcb *local_wild = NULL; 681 682 head = &pcbinfo->hashbase[(INADDR_ANY + lport) % pcbinfo->hashsize]; 683 for (inp = head->lh_first; inp != NULL; inp = inp->inp_hash.le_next) { 684 if (inp->inp_faddr.s_addr == INADDR_ANY && 685 inp->inp_fport == 0 && inp->inp_lport == lport) { 686 if (inp->inp_laddr.s_addr == laddr.s_addr) 687 goto found; 688 else if (inp->inp_laddr.s_addr == INADDR_ANY) 689 local_wild = inp; 690 } 691 } 692 if (local_wild != NULL) { 693 inp = local_wild; 694 goto found; 695 } 696 } 697 splx(s); 698 return (NULL); 699 700 found: 701 /* 702 * Move PCB to head of this hash chain so that it can be 703 * found more quickly in the future. 704 * XXX - this is a pessimization on machines with few 705 * concurrent connections. 706 */ 707 if (inp != head->lh_first) { 708 LIST_REMOVE(inp, inp_hash); 709 LIST_INSERT_HEAD(head, inp, inp_hash); 710 } 711 splx(s); 712 return (inp); 713 } 714 715 /* 716 * Insert PCB into hash chain. Must be called at splnet. 717 */ 718 static void 719 in_pcbinshash(inp) 720 struct inpcb *inp; 721 { 722 struct inpcbhead *head; 723 724 head = &inp->inp_pcbinfo->hashbase[(inp->inp_faddr.s_addr + 725 inp->inp_lport + inp->inp_fport) % inp->inp_pcbinfo->hashsize]; 726 727 LIST_INSERT_HEAD(head, inp, inp_hash); 728 } 729 730 void 731 in_pcbrehash(inp) 732 struct inpcb *inp; 733 { 734 struct inpcbhead *head; 735 int s; 736 737 s = splnet(); 738 LIST_REMOVE(inp, inp_hash); 739 740 head = &inp->inp_pcbinfo->hashbase[(inp->inp_faddr.s_addr + 741 inp->inp_lport + inp->inp_fport) % inp->inp_pcbinfo->hashsize]; 742 743 LIST_INSERT_HEAD(head, inp, inp_hash); 744 splx(s); 745 } 746