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