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.23 1996/10/30 06:13:09 peter 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; 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 (TAILQ_EMPTY(&in_ifaddrhead)) /* XXX broken! */ 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 lastport = &inp->inp_pcbinfo->lasthi; 210 } else if (inp->inp_flags & INP_LOWPORT) { 211 if (error = suser(p->p_ucred, &p->p_acflag)) 212 return (EACCES); 213 first = ipport_lowfirstauto; /* 1023 */ 214 last = ipport_lowlastauto; /* 600 */ 215 lastport = &inp->inp_pcbinfo->lastlow; 216 } else { 217 first = ipport_firstauto; /* sysctl */ 218 last = ipport_lastauto; 219 lastport = &inp->inp_pcbinfo->lastport; 220 } 221 /* 222 * Simple check to ensure all ports are not used up causing 223 * a deadlock here. 224 * 225 * We split the two cases (up and down) so that the direction 226 * is not being tested on each round of the loop. 227 */ 228 if (first > last) { 229 /* 230 * counting down 231 */ 232 count = first - last; 233 234 do { 235 if (count-- <= 0) /* completely used? */ 236 return (EADDRNOTAVAIL); 237 --*lastport; 238 if (*lastport > first || *lastport < last) 239 *lastport = first; 240 lport = htons(*lastport); 241 } while (in_pcblookup(inp->inp_pcbinfo, 242 zeroin_addr, 0, inp->inp_laddr, lport, wild)); 243 } else { 244 /* 245 * counting up 246 */ 247 count = last - first; 248 249 do { 250 if (count-- <= 0) /* completely used? */ 251 return (EADDRNOTAVAIL); 252 ++*lastport; 253 if (*lastport < first || *lastport > last) 254 *lastport = first; 255 lport = htons(*lastport); 256 } while (in_pcblookup(inp->inp_pcbinfo, 257 zeroin_addr, 0, inp->inp_laddr, lport, wild)); 258 } 259 } 260 inp->inp_lport = lport; 261 in_pcbrehash(inp); 262 return (0); 263 } 264 265 /* 266 * Transform old in_pcbconnect() into an inner subroutine for new 267 * in_pcbconnect(): Do some validity-checking on the remote 268 * address (in mbuf 'nam') and then determine local host address 269 * (i.e., which interface) to use to access that remote host. 270 * 271 * This preserves definition of in_pcbconnect(), while supporting a 272 * slightly different version for T/TCP. (This is more than 273 * a bit of a kludge, but cleaning up the internal interfaces would 274 * have forced minor changes in every protocol). 275 */ 276 277 int 278 in_pcbladdr(inp, nam, plocal_sin) 279 register struct inpcb *inp; 280 struct mbuf *nam; 281 struct sockaddr_in **plocal_sin; 282 { 283 struct in_ifaddr *ia; 284 register struct sockaddr_in *sin = mtod(nam, struct sockaddr_in *); 285 286 if (nam->m_len != sizeof (*sin)) 287 return (EINVAL); 288 if (sin->sin_family != AF_INET) 289 return (EAFNOSUPPORT); 290 if (sin->sin_port == 0) 291 return (EADDRNOTAVAIL); 292 if (!TAILQ_EMPTY(&in_ifaddrhead)) { 293 /* 294 * If the destination address is INADDR_ANY, 295 * use the primary local address. 296 * If the supplied address is INADDR_BROADCAST, 297 * and the primary interface supports broadcast, 298 * choose the broadcast address for that interface. 299 */ 300 #define satosin(sa) ((struct sockaddr_in *)(sa)) 301 #define sintosa(sin) ((struct sockaddr *)(sin)) 302 #define ifatoia(ifa) ((struct in_ifaddr *)(ifa)) 303 if (sin->sin_addr.s_addr == INADDR_ANY) 304 sin->sin_addr = IA_SIN(in_ifaddrhead.tqh_first)->sin_addr; 305 else if (sin->sin_addr.s_addr == (u_long)INADDR_BROADCAST && 306 (in_ifaddrhead.tqh_first->ia_ifp->if_flags & IFF_BROADCAST)) 307 sin->sin_addr = satosin(&in_ifaddrhead.tqh_first->ia_broadaddr)->sin_addr; 308 } 309 if (inp->inp_laddr.s_addr == INADDR_ANY) { 310 register struct route *ro; 311 312 ia = (struct in_ifaddr *)0; 313 /* 314 * If route is known or can be allocated now, 315 * our src addr is taken from the i/f, else punt. 316 */ 317 ro = &inp->inp_route; 318 if (ro->ro_rt && 319 (satosin(&ro->ro_dst)->sin_addr.s_addr != 320 sin->sin_addr.s_addr || 321 inp->inp_socket->so_options & SO_DONTROUTE)) { 322 RTFREE(ro->ro_rt); 323 ro->ro_rt = (struct rtentry *)0; 324 } 325 if ((inp->inp_socket->so_options & SO_DONTROUTE) == 0 && /*XXX*/ 326 (ro->ro_rt == (struct rtentry *)0 || 327 ro->ro_rt->rt_ifp == (struct ifnet *)0)) { 328 /* No route yet, so try to acquire one */ 329 ro->ro_dst.sa_family = AF_INET; 330 ro->ro_dst.sa_len = sizeof(struct sockaddr_in); 331 ((struct sockaddr_in *) &ro->ro_dst)->sin_addr = 332 sin->sin_addr; 333 rtalloc(ro); 334 } 335 /* 336 * If we found a route, use the address 337 * corresponding to the outgoing interface 338 * unless it is the loopback (in case a route 339 * to our address on another net goes to loopback). 340 */ 341 if (ro->ro_rt && !(ro->ro_rt->rt_ifp->if_flags & IFF_LOOPBACK)) 342 ia = ifatoia(ro->ro_rt->rt_ifa); 343 if (ia == 0) { 344 u_short fport = sin->sin_port; 345 346 sin->sin_port = 0; 347 ia = ifatoia(ifa_ifwithdstaddr(sintosa(sin))); 348 if (ia == 0) 349 ia = ifatoia(ifa_ifwithnet(sintosa(sin))); 350 sin->sin_port = fport; 351 if (ia == 0) 352 ia = in_ifaddrhead.tqh_first; 353 if (ia == 0) 354 return (EADDRNOTAVAIL); 355 } 356 /* 357 * If the destination address is multicast and an outgoing 358 * interface has been set as a multicast option, use the 359 * address of that interface as our source address. 360 */ 361 if (IN_MULTICAST(ntohl(sin->sin_addr.s_addr)) && 362 inp->inp_moptions != NULL) { 363 struct ip_moptions *imo; 364 struct ifnet *ifp; 365 366 imo = inp->inp_moptions; 367 if (imo->imo_multicast_ifp != NULL) { 368 ifp = imo->imo_multicast_ifp; 369 for (ia = in_ifaddrhead.tqh_first; ia; 370 ia = ia->ia_link.tqe_next) 371 if (ia->ia_ifp == ifp) 372 break; 373 if (ia == 0) 374 return (EADDRNOTAVAIL); 375 } 376 } 377 /* 378 * Don't do pcblookup call here; return interface in plocal_sin 379 * and exit to caller, that will do the lookup. 380 */ 381 *plocal_sin = &ia->ia_addr; 382 383 } 384 return(0); 385 } 386 387 /* 388 * Outer subroutine: 389 * Connect from a socket to a specified address. 390 * Both address and port must be specified in argument sin. 391 * If don't have a local address for this socket yet, 392 * then pick one. 393 */ 394 int 395 in_pcbconnect(inp, nam) 396 register struct inpcb *inp; 397 struct mbuf *nam; 398 { 399 struct sockaddr_in *ifaddr; 400 register struct sockaddr_in *sin = mtod(nam, struct sockaddr_in *); 401 int error; 402 403 /* 404 * Call inner routine, to assign local interface address. 405 */ 406 if (error = in_pcbladdr(inp, nam, &ifaddr)) 407 return(error); 408 409 if (in_pcblookuphash(inp->inp_pcbinfo, sin->sin_addr, sin->sin_port, 410 inp->inp_laddr.s_addr ? inp->inp_laddr : ifaddr->sin_addr, 411 inp->inp_lport, 0) != NULL) 412 return (EADDRINUSE); 413 if (inp->inp_laddr.s_addr == INADDR_ANY) { 414 if (inp->inp_lport == 0) 415 (void)in_pcbbind(inp, (struct mbuf *)0); 416 inp->inp_laddr = ifaddr->sin_addr; 417 } 418 inp->inp_faddr = sin->sin_addr; 419 inp->inp_fport = sin->sin_port; 420 in_pcbrehash(inp); 421 return (0); 422 } 423 424 void 425 in_pcbdisconnect(inp) 426 struct inpcb *inp; 427 { 428 429 inp->inp_faddr.s_addr = INADDR_ANY; 430 inp->inp_fport = 0; 431 in_pcbrehash(inp); 432 if (inp->inp_socket->so_state & SS_NOFDREF) 433 in_pcbdetach(inp); 434 } 435 436 void 437 in_pcbdetach(inp) 438 struct inpcb *inp; 439 { 440 struct socket *so = inp->inp_socket; 441 int s; 442 443 so->so_pcb = 0; 444 sofree(so); 445 if (inp->inp_options) 446 (void)m_free(inp->inp_options); 447 if (inp->inp_route.ro_rt) 448 rtfree(inp->inp_route.ro_rt); 449 ip_freemoptions(inp->inp_moptions); 450 s = splnet(); 451 LIST_REMOVE(inp, inp_hash); 452 LIST_REMOVE(inp, inp_list); 453 splx(s); 454 FREE(inp, M_PCB); 455 } 456 457 void 458 in_setsockaddr(inp, nam) 459 register struct inpcb *inp; 460 struct mbuf *nam; 461 { 462 register struct sockaddr_in *sin; 463 464 nam->m_len = sizeof (*sin); 465 sin = mtod(nam, struct sockaddr_in *); 466 bzero((caddr_t)sin, sizeof (*sin)); 467 sin->sin_family = AF_INET; 468 sin->sin_len = sizeof(*sin); 469 sin->sin_port = inp->inp_lport; 470 sin->sin_addr = inp->inp_laddr; 471 } 472 473 void 474 in_setpeeraddr(inp, nam) 475 struct inpcb *inp; 476 struct mbuf *nam; 477 { 478 register struct sockaddr_in *sin; 479 480 nam->m_len = sizeof (*sin); 481 sin = mtod(nam, struct sockaddr_in *); 482 bzero((caddr_t)sin, sizeof (*sin)); 483 sin->sin_family = AF_INET; 484 sin->sin_len = sizeof(*sin); 485 sin->sin_port = inp->inp_fport; 486 sin->sin_addr = inp->inp_faddr; 487 } 488 489 /* 490 * Pass some notification to all connections of a protocol 491 * associated with address dst. The local address and/or port numbers 492 * may be specified to limit the search. The "usual action" will be 493 * taken, depending on the ctlinput cmd. The caller must filter any 494 * cmds that are uninteresting (e.g., no error in the map). 495 * Call the protocol specific routine (if any) to report 496 * any errors for each matching socket. 497 * 498 * Must be called at splnet. 499 */ 500 void 501 in_pcbnotify(head, dst, fport_arg, laddr, lport_arg, cmd, notify) 502 struct inpcbhead *head; 503 struct sockaddr *dst; 504 u_int fport_arg, lport_arg; 505 struct in_addr laddr; 506 int cmd; 507 void (*notify) __P((struct inpcb *, int)); 508 { 509 register struct inpcb *inp, *oinp; 510 struct in_addr faddr; 511 u_short fport = fport_arg, lport = lport_arg; 512 int errno, s; 513 514 if ((unsigned)cmd > PRC_NCMDS || dst->sa_family != AF_INET) 515 return; 516 faddr = ((struct sockaddr_in *)dst)->sin_addr; 517 if (faddr.s_addr == INADDR_ANY) 518 return; 519 520 /* 521 * Redirects go to all references to the destination, 522 * and use in_rtchange to invalidate the route cache. 523 * Dead host indications: notify all references to the destination. 524 * Otherwise, if we have knowledge of the local port and address, 525 * deliver only to that socket. 526 */ 527 if (PRC_IS_REDIRECT(cmd) || cmd == PRC_HOSTDEAD) { 528 fport = 0; 529 lport = 0; 530 laddr.s_addr = 0; 531 if (cmd != PRC_HOSTDEAD) 532 notify = in_rtchange; 533 } 534 errno = inetctlerrmap[cmd]; 535 s = splnet(); 536 for (inp = head->lh_first; inp != NULL;) { 537 if (inp->inp_faddr.s_addr != faddr.s_addr || 538 inp->inp_socket == 0 || 539 (lport && inp->inp_lport != lport) || 540 (laddr.s_addr && inp->inp_laddr.s_addr != laddr.s_addr) || 541 (fport && inp->inp_fport != fport)) { 542 inp = inp->inp_list.le_next; 543 continue; 544 } 545 oinp = inp; 546 inp = inp->inp_list.le_next; 547 if (notify) 548 (*notify)(oinp, errno); 549 } 550 splx(s); 551 } 552 553 /* 554 * Check for alternatives when higher level complains 555 * about service problems. For now, invalidate cached 556 * routing information. If the route was created dynamically 557 * (by a redirect), time to try a default gateway again. 558 */ 559 void 560 in_losing(inp) 561 struct inpcb *inp; 562 { 563 register struct rtentry *rt; 564 struct rt_addrinfo info; 565 566 if ((rt = inp->inp_route.ro_rt)) { 567 inp->inp_route.ro_rt = 0; 568 bzero((caddr_t)&info, sizeof(info)); 569 info.rti_info[RTAX_DST] = 570 (struct sockaddr *)&inp->inp_route.ro_dst; 571 info.rti_info[RTAX_GATEWAY] = rt->rt_gateway; 572 info.rti_info[RTAX_NETMASK] = rt_mask(rt); 573 rt_missmsg(RTM_LOSING, &info, rt->rt_flags, 0); 574 if (rt->rt_flags & RTF_DYNAMIC) 575 (void) rtrequest(RTM_DELETE, rt_key(rt), 576 rt->rt_gateway, rt_mask(rt), rt->rt_flags, 577 (struct rtentry **)0); 578 else 579 /* 580 * A new route can be allocated 581 * the next time output is attempted. 582 */ 583 rtfree(rt); 584 } 585 } 586 587 /* 588 * After a routing change, flush old routing 589 * and allocate a (hopefully) better one. 590 */ 591 static void 592 in_rtchange(inp, errno) 593 register struct inpcb *inp; 594 int errno; 595 { 596 if (inp->inp_route.ro_rt) { 597 rtfree(inp->inp_route.ro_rt); 598 inp->inp_route.ro_rt = 0; 599 /* 600 * A new route can be allocated the next time 601 * output is attempted. 602 */ 603 } 604 } 605 606 struct inpcb * 607 in_pcblookup(pcbinfo, faddr, fport_arg, laddr, lport_arg, wild_okay) 608 struct inpcbinfo *pcbinfo; 609 struct in_addr faddr, laddr; 610 u_int fport_arg, lport_arg; 611 int wild_okay; 612 { 613 register struct inpcb *inp, *match = NULL; 614 int matchwild = 3, wildcard; 615 u_short fport = fport_arg, lport = lport_arg; 616 int s; 617 618 s = splnet(); 619 620 for (inp = pcbinfo->listhead->lh_first; inp != NULL; inp = inp->inp_list.le_next) { 621 if (inp->inp_lport != lport) 622 continue; 623 wildcard = 0; 624 if (inp->inp_faddr.s_addr != INADDR_ANY) { 625 if (faddr.s_addr == INADDR_ANY) 626 wildcard++; 627 else if (inp->inp_faddr.s_addr != faddr.s_addr || 628 inp->inp_fport != fport) 629 continue; 630 } else { 631 if (faddr.s_addr != INADDR_ANY) 632 wildcard++; 633 } 634 if (inp->inp_laddr.s_addr != INADDR_ANY) { 635 if (laddr.s_addr == INADDR_ANY) 636 wildcard++; 637 else if (inp->inp_laddr.s_addr != laddr.s_addr) 638 continue; 639 } else { 640 if (laddr.s_addr != INADDR_ANY) 641 wildcard++; 642 } 643 if (wildcard && wild_okay == 0) 644 continue; 645 if (wildcard < matchwild) { 646 match = inp; 647 matchwild = wildcard; 648 if (matchwild == 0) { 649 break; 650 } 651 } 652 } 653 splx(s); 654 return (match); 655 } 656 657 /* 658 * Lookup PCB in hash list. 659 */ 660 struct inpcb * 661 in_pcblookuphash(pcbinfo, faddr, fport_arg, laddr, lport_arg, wildcard) 662 struct inpcbinfo *pcbinfo; 663 struct in_addr faddr, laddr; 664 u_int fport_arg, lport_arg; 665 int wildcard; 666 { 667 struct inpcbhead *head; 668 register struct inpcb *inp; 669 u_short fport = fport_arg, lport = lport_arg; 670 int s; 671 672 s = splnet(); 673 /* 674 * First look for an exact match. 675 */ 676 head = &pcbinfo->hashbase[(faddr.s_addr + lport + fport) % pcbinfo->hashsize]; 677 for (inp = head->lh_first; inp != NULL; inp = inp->inp_hash.le_next) { 678 if (inp->inp_faddr.s_addr == faddr.s_addr && 679 inp->inp_fport == fport && inp->inp_lport == lport && 680 inp->inp_laddr.s_addr == laddr.s_addr) 681 goto found; 682 } 683 if (wildcard) { 684 struct inpcb *local_wild = NULL; 685 686 head = &pcbinfo->hashbase[(INADDR_ANY + lport) % pcbinfo->hashsize]; 687 for (inp = head->lh_first; inp != NULL; inp = inp->inp_hash.le_next) { 688 if (inp->inp_faddr.s_addr == INADDR_ANY && 689 inp->inp_fport == 0 && inp->inp_lport == lport) { 690 if (inp->inp_laddr.s_addr == laddr.s_addr) 691 goto found; 692 else if (inp->inp_laddr.s_addr == INADDR_ANY) 693 local_wild = inp; 694 } 695 } 696 if (local_wild != NULL) { 697 inp = local_wild; 698 goto found; 699 } 700 } 701 splx(s); 702 return (NULL); 703 704 found: 705 /* 706 * Move PCB to head of this hash chain so that it can be 707 * found more quickly in the future. 708 * XXX - this is a pessimization on machines with few 709 * concurrent connections. 710 */ 711 if (inp != head->lh_first) { 712 LIST_REMOVE(inp, inp_hash); 713 LIST_INSERT_HEAD(head, inp, inp_hash); 714 } 715 splx(s); 716 return (inp); 717 } 718 719 /* 720 * Insert PCB into hash chain. Must be called at splnet. 721 */ 722 static void 723 in_pcbinshash(inp) 724 struct inpcb *inp; 725 { 726 struct inpcbhead *head; 727 728 head = &inp->inp_pcbinfo->hashbase[(inp->inp_faddr.s_addr + 729 inp->inp_lport + inp->inp_fport) % inp->inp_pcbinfo->hashsize]; 730 731 LIST_INSERT_HEAD(head, inp, inp_hash); 732 } 733 734 void 735 in_pcbrehash(inp) 736 struct inpcb *inp; 737 { 738 struct inpcbhead *head; 739 int s; 740 741 s = splnet(); 742 LIST_REMOVE(inp, inp_hash); 743 744 head = &inp->inp_pcbinfo->hashbase[(inp->inp_faddr.s_addr + 745 inp->inp_lport + inp->inp_fport) % inp->inp_pcbinfo->hashsize]; 746 747 LIST_INSERT_HEAD(head, inp, inp_hash); 748 splx(s); 749 } 750