The Design and Implementation of the FreeBSD Operating System, Second Edition
Now available: The Design and Implementation of the FreeBSD Operating System (Second Edition)


[ source navigation ] [ diff markup ] [ identifier search ] [ freetext search ] [ file search ] [ list types ] [ track identifier ]

FreeBSD/Linux Kernel Cross Reference
sys/netinet/in_pcb.c

Version: -  FREEBSD  -  FREEBSD-13-STABLE  -  FREEBSD-13-0  -  FREEBSD-12-STABLE  -  FREEBSD-12-0  -  FREEBSD-11-STABLE  -  FREEBSD-11-0  -  FREEBSD-10-STABLE  -  FREEBSD-10-0  -  FREEBSD-9-STABLE  -  FREEBSD-9-0  -  FREEBSD-8-STABLE  -  FREEBSD-8-0  -  FREEBSD-7-STABLE  -  FREEBSD-7-0  -  FREEBSD-6-STABLE  -  FREEBSD-6-0  -  FREEBSD-5-STABLE  -  FREEBSD-5-0  -  FREEBSD-4-STABLE  -  FREEBSD-3-STABLE  -  FREEBSD22  -  l41  -  OPENBSD  -  linux-2.6  -  MK84  -  PLAN9  -  xnu-8792 
SearchContext: -  none  -  3  -  10 

    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  * $FreeBSD$
   35  */
   36 
   37 #include <sys/param.h>
   38 #include <sys/systm.h>
   39 #include <sys/malloc.h>
   40 #include <sys/mbuf.h>
   41 #include <sys/protosw.h>
   42 #include <sys/socket.h>
   43 #include <sys/socketvar.h>
   44 #include <sys/proc.h>
   45 #include <sys/kernel.h>
   46 #include <sys/sysctl.h>
   47 
   48 #include <machine/limits.h>
   49 
   50 #include <vm/vm_zone.h>
   51 
   52 #include <net/if.h>
   53 #include <net/route.h>
   54 
   55 #include <netinet/in.h>
   56 #include <netinet/in_pcb.h>
   57 #include <netinet/in_var.h>
   58 #include <netinet/ip_var.h>
   59 
   60 struct  in_addr zeroin_addr;
   61 
   62 static void     in_pcbremlists __P((struct inpcb *));
   63 
   64 /*
   65  * These configure the range of local port addresses assigned to
   66  * "unspecified" outgoing connections/packets/whatever.
   67  */
   68 static int ipport_lowfirstauto  = IPPORT_RESERVED - 1;  /* 1023 */
   69 static int ipport_lowlastauto = IPPORT_RESERVEDSTART;   /* 600 */
   70 static int ipport_firstauto = IPPORT_RESERVED;          /* 1024 */
   71 static int ipport_lastauto  = IPPORT_USERRESERVED;      /* 5000 */
   72 static int ipport_hifirstauto = IPPORT_HIFIRSTAUTO;     /* 49152 */
   73 static int ipport_hilastauto  = IPPORT_HILASTAUTO;      /* 65535 */
   74 
   75 #define RANGECHK(var, min, max) \
   76         if ((var) < (min)) { (var) = (min); } \
   77         else if ((var) > (max)) { (var) = (max); }
   78 
   79 static int
   80 sysctl_net_ipport_check SYSCTL_HANDLER_ARGS
   81 {
   82         int error = sysctl_handle_int(oidp,
   83                 oidp->oid_arg1, oidp->oid_arg2, req);
   84         if (!error) {
   85                 RANGECHK(ipport_lowfirstauto, 1, IPPORT_RESERVED - 1);
   86                 RANGECHK(ipport_lowlastauto, 1, IPPORT_RESERVED - 1);
   87                 RANGECHK(ipport_firstauto, IPPORT_RESERVED, USHRT_MAX);
   88                 RANGECHK(ipport_lastauto, IPPORT_RESERVED, USHRT_MAX);
   89                 RANGECHK(ipport_hifirstauto, IPPORT_RESERVED, USHRT_MAX);
   90                 RANGECHK(ipport_hilastauto, IPPORT_RESERVED, USHRT_MAX);
   91         }
   92         return error;
   93 }
   94 
   95 #undef RANGECHK
   96 
   97 SYSCTL_NODE(_net_inet_ip, IPPROTO_IP, portrange, CTLFLAG_RW, 0, "IP Ports");
   98 
   99 SYSCTL_PROC(_net_inet_ip_portrange, OID_AUTO, lowfirst, CTLTYPE_INT|CTLFLAG_RW,
  100            &ipport_lowfirstauto, 0, &sysctl_net_ipport_check, "I", "");
  101 SYSCTL_PROC(_net_inet_ip_portrange, OID_AUTO, lowlast, CTLTYPE_INT|CTLFLAG_RW,
  102            &ipport_lowlastauto, 0, &sysctl_net_ipport_check, "I", "");
  103 SYSCTL_PROC(_net_inet_ip_portrange, OID_AUTO, first, CTLTYPE_INT|CTLFLAG_RW,
  104            &ipport_firstauto, 0, &sysctl_net_ipport_check, "I", "");
  105 SYSCTL_PROC(_net_inet_ip_portrange, OID_AUTO, last, CTLTYPE_INT|CTLFLAG_RW,
  106            &ipport_lastauto, 0, &sysctl_net_ipport_check, "I", "");
  107 SYSCTL_PROC(_net_inet_ip_portrange, OID_AUTO, hifirst, CTLTYPE_INT|CTLFLAG_RW,
  108            &ipport_hifirstauto, 0, &sysctl_net_ipport_check, "I", "");
  109 SYSCTL_PROC(_net_inet_ip_portrange, OID_AUTO, hilast, CTLTYPE_INT|CTLFLAG_RW,
  110            &ipport_hilastauto, 0, &sysctl_net_ipport_check, "I", "");
  111 
  112 /*
  113  * in_pcb.c: manage the Protocol Control Blocks.
  114  *
  115  * NOTE: It is assumed that most of these functions will be called at
  116  * splnet(). XXX - There are, unfortunately, a few exceptions to this
  117  * rule that should be fixed.
  118  */
  119 
  120 /*
  121  * Allocate a PCB and associate it with the socket.
  122  */
  123 int
  124 in_pcballoc(so, pcbinfo, p)
  125         struct socket *so;
  126         struct inpcbinfo *pcbinfo;
  127         struct proc *p;
  128 {
  129         register struct inpcb *inp;
  130 
  131         inp = zalloci(pcbinfo->ipi_zone);
  132         if (inp == NULL)
  133                 return (ENOBUFS);
  134         bzero((caddr_t)inp, sizeof(*inp));
  135         inp->inp_gencnt = ++pcbinfo->ipi_gencnt;
  136         inp->inp_pcbinfo = pcbinfo;
  137         inp->inp_socket = so;
  138         LIST_INSERT_HEAD(pcbinfo->listhead, inp, inp_list);
  139         pcbinfo->ipi_count++;
  140         so->so_pcb = (caddr_t)inp;
  141         return (0);
  142 }
  143 
  144 int
  145 in_pcbbind(inp, nam, p)
  146         register struct inpcb *inp;
  147         struct sockaddr *nam;
  148         struct proc *p;
  149 {
  150         register struct socket *so = inp->inp_socket;
  151         unsigned short *lastport;
  152         struct sockaddr_in *sin;
  153         struct inpcbinfo *pcbinfo = inp->inp_pcbinfo;
  154         u_short lport = 0;
  155         int wild = 0, reuseport = (so->so_options & SO_REUSEPORT);
  156         int error;
  157 
  158         if (TAILQ_EMPTY(&in_ifaddrhead)) /* XXX broken! */
  159                 return (EADDRNOTAVAIL);
  160         if (inp->inp_lport || inp->inp_laddr.s_addr != INADDR_ANY)
  161                 return (EINVAL);
  162         if ((so->so_options & (SO_REUSEADDR|SO_REUSEPORT)) == 0)
  163                 wild = 1;
  164         if (nam) {
  165                 sin = (struct sockaddr_in *)nam;
  166                 if (nam->sa_len != sizeof (*sin))
  167                         return (EINVAL);
  168 #ifdef notdef
  169                 /*
  170                  * We should check the family, but old programs
  171                  * incorrectly fail to initialize it.
  172                  */
  173                 if (sin->sin_family != AF_INET)
  174                         return (EAFNOSUPPORT);
  175 #endif
  176                 lport = sin->sin_port;
  177                 if (IN_MULTICAST(ntohl(sin->sin_addr.s_addr))) {
  178                         /*
  179                          * Treat SO_REUSEADDR as SO_REUSEPORT for multicast;
  180                          * allow complete duplication of binding if
  181                          * SO_REUSEPORT is set, or if SO_REUSEADDR is set
  182                          * and a multicast address is bound on both
  183                          * new and duplicated sockets.
  184                          */
  185                         if (so->so_options & SO_REUSEADDR)
  186                                 reuseport = SO_REUSEADDR|SO_REUSEPORT;
  187                 } else if (sin->sin_addr.s_addr != INADDR_ANY) {
  188                         sin->sin_port = 0;              /* yech... */
  189                         if (ifa_ifwithaddr((struct sockaddr *)sin) == 0)
  190                                 return (EADDRNOTAVAIL);
  191                 }
  192                 if (lport) {
  193                         struct inpcb *t;
  194 
  195                         /* GROSS */
  196                         if (ntohs(lport) < IPPORT_RESERVED && p &&
  197                             suser(p->p_ucred, &p->p_acflag))
  198                                 return (EACCES);
  199                         if (so->so_cred && so->so_cred->p_ruid != 0 &&
  200                             !IN_MULTICAST(ntohl(sin->sin_addr.s_addr))) {
  201                                 t = in_pcblookup_local(inp->inp_pcbinfo,
  202                                     sin->sin_addr, lport, INPLOOKUP_WILDCARD);
  203                                 if (t &&
  204                                     (ntohl(sin->sin_addr.s_addr) != INADDR_ANY ||
  205                                      ntohl(t->inp_laddr.s_addr) != INADDR_ANY ||
  206                                      (t->inp_socket->so_options &
  207                                          SO_REUSEPORT) == 0) &&
  208                                      (t->inp_socket->so_cred) &&
  209                                      (so->so_cred->p_ruid !=
  210                                        t->inp_socket->so_cred->p_ruid))
  211                                         return (EADDRINUSE);
  212                         }
  213                         t = in_pcblookup_local(pcbinfo, sin->sin_addr,
  214                             lport, wild);
  215                         if (t && (reuseport & t->inp_socket->so_options) == 0)
  216                                 return (EADDRINUSE);
  217                 }
  218                 inp->inp_laddr = sin->sin_addr;
  219         }
  220         if (lport == 0) {
  221                 ushort first, last;
  222                 int count;
  223 
  224                 inp->inp_flags |= INP_ANONPORT;
  225 
  226                 if (inp->inp_flags & INP_HIGHPORT) {
  227                         first = ipport_hifirstauto;     /* sysctl */
  228                         last  = ipport_hilastauto;
  229                         lastport = &pcbinfo->lasthi;
  230                 } else if (inp->inp_flags & INP_LOWPORT) {
  231                         if (p && (error = suser(p->p_ucred, &p->p_acflag)))
  232                                 return error;
  233                         first = ipport_lowfirstauto;    /* 1023 */
  234                         last  = ipport_lowlastauto;     /* 600 */
  235                         lastport = &pcbinfo->lastlow;
  236                 } else {
  237                         first = ipport_firstauto;       /* sysctl */
  238                         last  = ipport_lastauto;
  239                         lastport = &pcbinfo->lastport;
  240                 }
  241                 /*
  242                  * Simple check to ensure all ports are not used up causing
  243                  * a deadlock here.
  244                  *
  245                  * We split the two cases (up and down) so that the direction
  246                  * is not being tested on each round of the loop.
  247                  */
  248                 if (first > last) {
  249                         /*
  250                          * counting down
  251                          */
  252                         count = first - last;
  253 
  254                         do {
  255                                 if (count-- < 0) {      /* completely used? */
  256                                         /*
  257                                          * Undo any address bind that may have
  258                                          * occurred above.
  259                                          */
  260                                         inp->inp_laddr.s_addr = INADDR_ANY;
  261                                         return (EAGAIN);
  262                                 }
  263                                 --*lastport;
  264                                 if (*lastport > first || *lastport < last)
  265                                         *lastport = first;
  266                                 lport = htons(*lastport);
  267                         } while (in_pcblookup_local(pcbinfo,
  268                                  inp->inp_laddr, lport, wild));
  269                 } else {
  270                         /*
  271                          * counting up
  272                          */
  273                         count = last - first;
  274 
  275                         do {
  276                                 if (count-- < 0) {      /* completely used? */
  277                                         /*
  278                                          * Undo any address bind that may have
  279                                          * occurred above.
  280                                          */
  281                                         inp->inp_laddr.s_addr = INADDR_ANY;
  282                                         return (EAGAIN);
  283                                 }
  284                                 ++*lastport;
  285                                 if (*lastport < first || *lastport > last)
  286                                         *lastport = first;
  287                                 lport = htons(*lastport);
  288                         } while (in_pcblookup_local(pcbinfo,
  289                                  inp->inp_laddr, lport, wild));
  290                 }
  291         }
  292         inp->inp_lport = lport;
  293         if (in_pcbinshash(inp) != 0) {
  294                 inp->inp_laddr.s_addr = INADDR_ANY;
  295                 inp->inp_lport = 0;
  296                 return (EAGAIN);
  297         }
  298         return (0);
  299 }
  300 
  301 /*
  302  *   Transform old in_pcbconnect() into an inner subroutine for new
  303  *   in_pcbconnect(): Do some validity-checking on the remote
  304  *   address (in mbuf 'nam') and then determine local host address
  305  *   (i.e., which interface) to use to access that remote host.
  306  *
  307  *   This preserves definition of in_pcbconnect(), while supporting a
  308  *   slightly different version for T/TCP.  (This is more than
  309  *   a bit of a kludge, but cleaning up the internal interfaces would
  310  *   have forced minor changes in every protocol).
  311  */
  312 
  313 int
  314 in_pcbladdr(inp, nam, plocal_sin)
  315         register struct inpcb *inp;
  316         struct sockaddr *nam;
  317         struct sockaddr_in **plocal_sin;
  318 {
  319         struct in_ifaddr *ia;
  320         register struct sockaddr_in *sin = (struct sockaddr_in *)nam;
  321 
  322         if (nam->sa_len != sizeof (*sin))
  323                 return (EINVAL);
  324         if (sin->sin_family != AF_INET)
  325                 return (EAFNOSUPPORT);
  326         if (sin->sin_port == 0)
  327                 return (EADDRNOTAVAIL);
  328         if (!TAILQ_EMPTY(&in_ifaddrhead)) {
  329                 /*
  330                  * If the destination address is INADDR_ANY,
  331                  * use the primary local address.
  332                  * If the supplied address is INADDR_BROADCAST,
  333                  * and the primary interface supports broadcast,
  334                  * choose the broadcast address for that interface.
  335                  */
  336 #define satosin(sa)     ((struct sockaddr_in *)(sa))
  337 #define sintosa(sin)    ((struct sockaddr *)(sin))
  338 #define ifatoia(ifa)    ((struct in_ifaddr *)(ifa))
  339                 if (sin->sin_addr.s_addr == INADDR_ANY)
  340                     sin->sin_addr = IA_SIN(in_ifaddrhead.tqh_first)->sin_addr;
  341                 else if (sin->sin_addr.s_addr == (u_long)INADDR_BROADCAST &&
  342                   (in_ifaddrhead.tqh_first->ia_ifp->if_flags & IFF_BROADCAST))
  343                     sin->sin_addr = satosin(&in_ifaddrhead.tqh_first->ia_broadaddr)->sin_addr;
  344         }
  345         if (inp->inp_laddr.s_addr == INADDR_ANY) {
  346                 register struct route *ro;
  347 
  348                 ia = (struct in_ifaddr *)0;
  349                 /*
  350                  * If route is known or can be allocated now,
  351                  * our src addr is taken from the i/f, else punt.
  352                  */
  353                 ro = &inp->inp_route;
  354                 if (ro->ro_rt &&
  355                     (satosin(&ro->ro_dst)->sin_addr.s_addr !=
  356                         sin->sin_addr.s_addr ||
  357                     inp->inp_socket->so_options & SO_DONTROUTE)) {
  358                         RTFREE(ro->ro_rt);
  359                         ro->ro_rt = (struct rtentry *)0;
  360                 }
  361                 if ((inp->inp_socket->so_options & SO_DONTROUTE) == 0 && /*XXX*/
  362                     (ro->ro_rt == (struct rtentry *)0 ||
  363                     ro->ro_rt->rt_ifp == (struct ifnet *)0)) {
  364                         /* No route yet, so try to acquire one */
  365                         ro->ro_dst.sa_family = AF_INET;
  366                         ro->ro_dst.sa_len = sizeof(struct sockaddr_in);
  367                         ((struct sockaddr_in *) &ro->ro_dst)->sin_addr =
  368                                 sin->sin_addr;
  369                         rtalloc(ro);
  370                 }
  371                 /*
  372                  * If we found a route, use the address
  373                  * corresponding to the outgoing interface
  374                  * unless it is the loopback (in case a route
  375                  * to our address on another net goes to loopback).
  376                  */
  377                 if (ro->ro_rt && !(ro->ro_rt->rt_ifp->if_flags & IFF_LOOPBACK))
  378                         ia = ifatoia(ro->ro_rt->rt_ifa);
  379                 if (ia == 0) {
  380                         u_short fport = sin->sin_port;
  381 
  382                         sin->sin_port = 0;
  383                         ia = ifatoia(ifa_ifwithdstaddr(sintosa(sin)));
  384                         if (ia == 0)
  385                                 ia = ifatoia(ifa_ifwithnet(sintosa(sin)));
  386                         sin->sin_port = fport;
  387                         if (ia == 0)
  388                                 ia = in_ifaddrhead.tqh_first;
  389                         if (ia == 0)
  390                                 return (EADDRNOTAVAIL);
  391                 }
  392                 /*
  393                  * If the destination address is multicast and an outgoing
  394                  * interface has been set as a multicast option, use the
  395                  * address of that interface as our source address.
  396                  */
  397                 if (IN_MULTICAST(ntohl(sin->sin_addr.s_addr)) &&
  398                     inp->inp_moptions != NULL) {
  399                         struct ip_moptions *imo;
  400                         struct ifnet *ifp;
  401 
  402                         imo = inp->inp_moptions;
  403                         if (imo->imo_multicast_ifp != NULL) {
  404                                 ifp = imo->imo_multicast_ifp;
  405                                 for (ia = in_ifaddrhead.tqh_first; ia; 
  406                                      ia = ia->ia_link.tqe_next)
  407                                         if (ia->ia_ifp == ifp)
  408                                                 break;
  409                                 if (ia == 0)
  410                                         return (EADDRNOTAVAIL);
  411                         }
  412                 }
  413         /*
  414          * Don't do pcblookup call here; return interface in plocal_sin
  415          * and exit to caller, that will do the lookup.
  416          */
  417                 *plocal_sin = &ia->ia_addr;
  418 
  419         }
  420         return(0);
  421 }
  422 
  423 /*
  424  * Outer subroutine:
  425  * Connect from a socket to a specified address.
  426  * Both address and port must be specified in argument sin.
  427  * If don't have a local address for this socket yet,
  428  * then pick one.
  429  */
  430 int
  431 in_pcbconnect(inp, nam, p)
  432         register struct inpcb *inp;
  433         struct sockaddr *nam;
  434         struct proc *p;
  435 {
  436         struct sockaddr_in *ifaddr;
  437         register struct sockaddr_in *sin = (struct sockaddr_in *)nam;
  438         int error;
  439 
  440         /*
  441          *   Call inner routine, to assign local interface address.
  442          */
  443         if (error = in_pcbladdr(inp, nam, &ifaddr))
  444                 return(error);
  445 
  446         if (in_pcblookup_hash(inp->inp_pcbinfo, sin->sin_addr, sin->sin_port,
  447             inp->inp_laddr.s_addr ? inp->inp_laddr : ifaddr->sin_addr,
  448             inp->inp_lport, 0) != NULL) {
  449                 return (EADDRINUSE);
  450         }
  451         if (inp->inp_laddr.s_addr == INADDR_ANY) {
  452                 if (inp->inp_lport == 0) {
  453                         error = in_pcbbind(inp, (struct sockaddr *)0, p);
  454                         if (error)
  455                                 return (error);
  456                 }
  457                 inp->inp_laddr = ifaddr->sin_addr;
  458         }
  459         inp->inp_faddr = sin->sin_addr;
  460         inp->inp_fport = sin->sin_port;
  461         in_pcbrehash(inp);
  462         return (0);
  463 }
  464 
  465 void
  466 in_pcbdisconnect(inp)
  467         struct inpcb *inp;
  468 {
  469 
  470         inp->inp_faddr.s_addr = INADDR_ANY;
  471         inp->inp_fport = 0;
  472         in_pcbrehash(inp);
  473         if (inp->inp_socket->so_state & SS_NOFDREF)
  474                 in_pcbdetach(inp);
  475 }
  476 
  477 void
  478 in_pcbdetach(inp)
  479         struct inpcb *inp;
  480 {
  481         struct socket *so = inp->inp_socket;
  482         struct inpcbinfo *ipi = inp->inp_pcbinfo;
  483 
  484         inp->inp_gencnt = ++ipi->ipi_gencnt;
  485         in_pcbremlists(inp);
  486         so->so_pcb = 0;
  487         sofree(so);
  488         if (inp->inp_options)
  489                 (void)m_free(inp->inp_options);
  490         if (inp->inp_route.ro_rt)
  491                 rtfree(inp->inp_route.ro_rt);
  492         ip_freemoptions(inp->inp_moptions);
  493         zfreei(ipi->ipi_zone, inp);
  494 }
  495 
  496 /*
  497  * The calling convention of in_setsockaddr() and in_setpeeraddr() was
  498  * modified to match the pru_sockaddr() and pru_peeraddr() entry points
  499  * in struct pr_usrreqs, so that protocols can just reference then directly
  500  * without the need for a wrapper function.  The socket must have a valid
  501  * (i.e., non-nil) PCB, but it should be impossible to get an invalid one
  502  * except through a kernel programming error, so it is acceptable to panic
  503  * (or in this case trap) if the PCB is invalid.  (Actually, we don't trap
  504  * because there actually /is/ a programming error somewhere... XXX)
  505  */
  506 int
  507 in_setsockaddr(so, nam)
  508         struct socket *so;
  509         struct sockaddr **nam;
  510 {
  511         int s;
  512         register struct inpcb *inp;
  513         register struct sockaddr_in *sin;
  514 
  515         /*
  516          * Do the malloc first in case it blocks.
  517          */
  518         MALLOC(sin, struct sockaddr_in *, sizeof *sin, M_SONAME, M_WAITOK);
  519         bzero(sin, sizeof *sin);
  520         sin->sin_family = AF_INET;
  521         sin->sin_len = sizeof(*sin);
  522 
  523         s = splnet();
  524         inp = sotoinpcb(so);
  525         if (!inp) {
  526                 splx(s);
  527                 free(sin, M_SONAME);
  528                 return EINVAL;
  529         }
  530         sin->sin_port = inp->inp_lport;
  531         sin->sin_addr = inp->inp_laddr;
  532         splx(s);
  533 
  534         *nam = (struct sockaddr *)sin;
  535         return 0;
  536 }
  537 
  538 int
  539 in_setpeeraddr(so, nam)
  540         struct socket *so;
  541         struct sockaddr **nam;
  542 {
  543         int s;
  544         struct inpcb *inp;
  545         register struct sockaddr_in *sin;
  546 
  547         /*
  548          * Do the malloc first in case it blocks.
  549          */
  550         MALLOC(sin, struct sockaddr_in *, sizeof *sin, M_SONAME, M_WAITOK);
  551         bzero((caddr_t)sin, sizeof (*sin));
  552         sin->sin_family = AF_INET;
  553         sin->sin_len = sizeof(*sin);
  554 
  555         s = splnet();
  556         inp = sotoinpcb(so);
  557         if (!inp) {
  558                 splx(s);
  559                 free(sin, M_SONAME);
  560                 return EINVAL;
  561         }
  562         sin->sin_port = inp->inp_fport;
  563         sin->sin_addr = inp->inp_faddr;
  564         splx(s);
  565 
  566         *nam = (struct sockaddr *)sin;
  567         return 0;
  568 }
  569 
  570 /*
  571  * Pass some notification to all connections of a protocol
  572  * associated with address dst.  The local address and/or port numbers
  573  * may be specified to limit the search.  The "usual action" will be
  574  * taken, depending on the ctlinput cmd.  The caller must filter any
  575  * cmds that are uninteresting (e.g., no error in the map).
  576  * Call the protocol specific routine (if any) to report
  577  * any errors for each matching socket.
  578  */
  579 void
  580 in_pcbnotify(head, dst, fport_arg, laddr, lport_arg, cmd, notify)
  581         struct inpcbhead *head;
  582         struct sockaddr *dst;
  583         u_int fport_arg, lport_arg;
  584         struct in_addr laddr;
  585         int cmd;
  586         void (*notify) __P((struct inpcb *, int));
  587 {
  588         register struct inpcb *inp, *oinp;
  589         struct in_addr faddr;
  590         u_short fport = fport_arg, lport = lport_arg;
  591         int errno, s;
  592 
  593         if ((unsigned)cmd > PRC_NCMDS || dst->sa_family != AF_INET)
  594                 return;
  595         faddr = ((struct sockaddr_in *)dst)->sin_addr;
  596         if (faddr.s_addr == INADDR_ANY)
  597                 return;
  598 
  599         errno = inetctlerrmap[cmd];
  600         s = splnet();
  601         for (inp = head->lh_first; inp != NULL;) {
  602                 if (inp->inp_faddr.s_addr != faddr.s_addr ||
  603                     inp->inp_socket == 0 || inp->inp_lport != lport ||
  604                     inp->inp_laddr.s_addr != laddr.s_addr ||
  605                     inp->inp_fport != fport) {
  606                         inp = inp->inp_list.le_next;
  607                         continue;
  608                 }
  609                 oinp = inp;
  610                 inp = inp->inp_list.le_next;
  611                 if (notify)
  612                         (*notify)(oinp, errno);
  613         }
  614         splx(s);
  615 }
  616 
  617 void
  618 in_pcbnotifyall(head, dst, cmd, notify)
  619         struct inpcbhead *head;
  620         struct sockaddr *dst;
  621         int cmd;
  622         void (*notify) __P((struct inpcb *, int));
  623 {
  624         register struct inpcb *inp, *oinp;
  625         struct in_addr faddr;
  626         int errno, s;
  627 
  628         if ((unsigned)cmd > PRC_NCMDS || dst->sa_family != AF_INET)
  629                 return;
  630         faddr = ((struct sockaddr_in *)dst)->sin_addr;
  631         if (faddr.s_addr == INADDR_ANY)
  632                 return;
  633 
  634         errno = inetctlerrmap[cmd];
  635         s = splnet();
  636         for (inp = LIST_FIRST(head); inp != NULL;) {
  637 #ifdef INET6
  638                 if ((inp->inp_vflag & INP_IPV4) == 0) {
  639                         inp = LIST_NEXT(inp, inp_list);
  640                         continue;
  641                 }
  642 #endif
  643                 if (inp->inp_faddr.s_addr != faddr.s_addr || inp->inp_socket == 0) {
  644                                 inp = LIST_NEXT(inp, inp_list);
  645                                 continue;
  646                 }
  647                 oinp = inp;
  648                 inp = LIST_NEXT(inp, inp_list);
  649                 if (notify)
  650                         (*notify)(oinp, errno);
  651         }
  652         splx(s);
  653 }
  654 
  655 /*
  656  * Check for alternatives when higher level complains
  657  * about service problems.  For now, invalidate cached
  658  * routing information.  If the route was created dynamically
  659  * (by a redirect), time to try a default gateway again.
  660  */
  661 void
  662 in_losing(inp)
  663         struct inpcb *inp;
  664 {
  665         register struct rtentry *rt;
  666         struct rt_addrinfo info;
  667 
  668         if ((rt = inp->inp_route.ro_rt)) {
  669                 inp->inp_route.ro_rt = 0;
  670                 bzero((caddr_t)&info, sizeof(info));
  671                 info.rti_info[RTAX_DST] =
  672                         (struct sockaddr *)&inp->inp_route.ro_dst;
  673                 info.rti_info[RTAX_GATEWAY] = rt->rt_gateway;
  674                 info.rti_info[RTAX_NETMASK] = rt_mask(rt);
  675                 rt_missmsg(RTM_LOSING, &info, rt->rt_flags, 0);
  676                 if (rt->rt_flags & RTF_DYNAMIC)
  677                         (void) rtrequest(RTM_DELETE, rt_key(rt),
  678                                 rt->rt_gateway, rt_mask(rt), rt->rt_flags,
  679                                 (struct rtentry **)0);
  680                 else
  681                 /*
  682                  * A new route can be allocated
  683                  * the next time output is attempted.
  684                  */
  685                         rtfree(rt);
  686         }
  687 }
  688 
  689 /*
  690  * After a routing change, flush old routing
  691  * and allocate a (hopefully) better one.
  692  */
  693 void
  694 in_rtchange(inp, errno)
  695         register struct inpcb *inp;
  696         int errno;
  697 {
  698         if (inp->inp_route.ro_rt) {
  699                 rtfree(inp->inp_route.ro_rt);
  700                 inp->inp_route.ro_rt = 0;
  701                 /*
  702                  * A new route can be allocated the next time
  703                  * output is attempted.
  704                  */
  705         }
  706 }
  707 
  708 /*
  709  * Lookup a PCB based on the local address and port.
  710  */
  711 struct inpcb *
  712 in_pcblookup_local(pcbinfo, laddr, lport_arg, wild_okay)
  713         struct inpcbinfo *pcbinfo;
  714         struct in_addr laddr;
  715         u_int lport_arg;
  716         int wild_okay;
  717 {
  718         register struct inpcb *inp;
  719         int matchwild = 3, wildcard;
  720         u_short lport = lport_arg;
  721 
  722         if (!wild_okay) {
  723                 struct inpcbhead *head;
  724                 /*
  725                  * Look for an unconnected (wildcard foreign addr) PCB that
  726                  * matches the local address and port we're looking for.
  727                  */
  728                 head = &pcbinfo->hashbase[INP_PCBHASH(INADDR_ANY, lport, 0, pcbinfo->hashmask)];
  729                 for (inp = head->lh_first; inp != NULL; inp = inp->inp_hash.le_next) {
  730                         if (inp->inp_faddr.s_addr == INADDR_ANY &&
  731                             inp->inp_laddr.s_addr == laddr.s_addr &&
  732                             inp->inp_lport == lport) {
  733                                 /*
  734                                  * Found.
  735                                  */
  736                                 return (inp);
  737                         }
  738                 }
  739                 /*
  740                  * Not found.
  741                  */
  742                 return (NULL);
  743         } else {
  744                 struct inpcbporthead *porthash;
  745                 struct inpcbport *phd;
  746                 struct inpcb *match = NULL;
  747                 /*
  748                  * Best fit PCB lookup.
  749                  *
  750                  * First see if this local port is in use by looking on the
  751                  * port hash list.
  752                  */
  753                 porthash = &pcbinfo->porthashbase[INP_PCBPORTHASH(lport,
  754                     pcbinfo->porthashmask)];
  755                 for (phd = porthash->lh_first; phd != NULL; phd = phd->phd_hash.le_next) {
  756                         if (phd->phd_port == lport)
  757                                 break;
  758                 }
  759                 if (phd != NULL) {
  760                         /*
  761                          * Port is in use by one or more PCBs. Look for best
  762                          * fit.
  763                          */
  764                         for (inp = phd->phd_pcblist.lh_first; inp != NULL;
  765                             inp = inp->inp_portlist.le_next) {
  766                                 wildcard = 0;
  767                                 if (inp->inp_faddr.s_addr != INADDR_ANY)
  768                                         wildcard++;
  769                                 if (inp->inp_laddr.s_addr != INADDR_ANY) {
  770                                         if (laddr.s_addr == INADDR_ANY)
  771                                                 wildcard++;
  772                                         else if (inp->inp_laddr.s_addr != laddr.s_addr)
  773                                                 continue;
  774                                 } else {
  775                                         if (laddr.s_addr != INADDR_ANY)
  776                                                 wildcard++;
  777                                 }
  778                                 if (wildcard < matchwild) {
  779                                         match = inp;
  780                                         matchwild = wildcard;
  781                                         if (matchwild == 0) {
  782                                                 break;
  783                                         }
  784                                 }
  785                         }
  786                 }
  787                 return (match);
  788         }
  789 }
  790 
  791 /*
  792  * Lookup PCB in hash list.
  793  */
  794 struct inpcb *
  795 in_pcblookup_hash(pcbinfo, faddr, fport_arg, laddr, lport_arg, wildcard)
  796         struct inpcbinfo *pcbinfo;
  797         struct in_addr faddr, laddr;
  798         u_int fport_arg, lport_arg;
  799         int wildcard;
  800 {
  801         struct inpcbhead *head;
  802         register struct inpcb *inp;
  803         u_short fport = fport_arg, lport = lport_arg;
  804 
  805         /*
  806          * First look for an exact match.
  807          */
  808         head = &pcbinfo->hashbase[INP_PCBHASH(faddr.s_addr, lport, fport, pcbinfo->hashmask)];
  809         for (inp = head->lh_first; inp != NULL; inp = inp->inp_hash.le_next) {
  810                 if (inp->inp_faddr.s_addr == faddr.s_addr &&
  811                     inp->inp_laddr.s_addr == laddr.s_addr &&
  812                     inp->inp_fport == fport &&
  813                     inp->inp_lport == lport) {
  814                         /*
  815                          * Found.
  816                          */
  817                         return (inp);
  818                 }
  819         }
  820         if (wildcard) {
  821                 struct inpcb *local_wild = NULL;
  822 
  823                 head = &pcbinfo->hashbase[INP_PCBHASH(INADDR_ANY, lport, 0, pcbinfo->hashmask)];
  824                 for (inp = head->lh_first; inp != NULL; inp = inp->inp_hash.le_next) {
  825                         if (inp->inp_faddr.s_addr == INADDR_ANY &&
  826                             inp->inp_lport == lport) {
  827                                 if (inp->inp_laddr.s_addr == laddr.s_addr)
  828                                         return (inp);
  829                                 else if (inp->inp_laddr.s_addr == INADDR_ANY)
  830                                         local_wild = inp;
  831                         }
  832                 }
  833                 return (local_wild);
  834         }
  835 
  836         /*
  837          * Not found.
  838          */
  839         return (NULL);
  840 }
  841 
  842 /*
  843  * Insert PCB onto various hash lists.
  844  */
  845 int
  846 in_pcbinshash(inp)
  847         struct inpcb *inp;
  848 {
  849         struct inpcbhead *pcbhash;
  850         struct inpcbporthead *pcbporthash;
  851         struct inpcbinfo *pcbinfo = inp->inp_pcbinfo;
  852         struct inpcbport *phd;
  853 
  854         pcbhash = &pcbinfo->hashbase[INP_PCBHASH(inp->inp_faddr.s_addr,
  855                  inp->inp_lport, inp->inp_fport, pcbinfo->hashmask)];
  856 
  857         pcbporthash = &pcbinfo->porthashbase[INP_PCBPORTHASH(inp->inp_lport,
  858             pcbinfo->porthashmask)];
  859 
  860         /*
  861          * Go through port list and look for a head for this lport.
  862          */
  863         for (phd = pcbporthash->lh_first; phd != NULL; phd = phd->phd_hash.le_next) {
  864                 if (phd->phd_port == inp->inp_lport)
  865                         break;
  866         }
  867         /*
  868          * If none exists, malloc one and tack it on.
  869          */
  870         if (phd == NULL) {
  871                 MALLOC(phd, struct inpcbport *, sizeof(struct inpcbport), M_PCB, M_NOWAIT);
  872                 if (phd == NULL) {
  873                         return (ENOBUFS); /* XXX */
  874                 }
  875                 phd->phd_port = inp->inp_lport;
  876                 LIST_INIT(&phd->phd_pcblist);
  877                 LIST_INSERT_HEAD(pcbporthash, phd, phd_hash);
  878         }
  879         inp->inp_phd = phd;
  880         LIST_INSERT_HEAD(&phd->phd_pcblist, inp, inp_portlist);
  881         LIST_INSERT_HEAD(pcbhash, inp, inp_hash);
  882         return (0);
  883 }
  884 
  885 /*
  886  * Move PCB to the proper hash bucket when { faddr, fport } have  been
  887  * changed. NOTE: This does not handle the case of the lport changing (the
  888  * hashed port list would have to be updated as well), so the lport must
  889  * not change after in_pcbinshash() has been called.
  890  */
  891 void
  892 in_pcbrehash(inp)
  893         struct inpcb *inp;
  894 {
  895         struct inpcbhead *head;
  896 
  897         head = &inp->inp_pcbinfo->hashbase[INP_PCBHASH(inp->inp_faddr.s_addr,
  898                 inp->inp_lport, inp->inp_fport, inp->inp_pcbinfo->hashmask)];
  899 
  900         LIST_REMOVE(inp, inp_hash);
  901         LIST_INSERT_HEAD(head, inp, inp_hash);
  902 }
  903 
  904 /*
  905  * Remove PCB from various lists.
  906  */
  907 static void
  908 in_pcbremlists(inp)
  909         struct inpcb *inp;
  910 {
  911         inp->inp_gencnt = ++inp->inp_pcbinfo->ipi_gencnt;
  912         if (inp->inp_lport) {
  913                 struct inpcbport *phd = inp->inp_phd;
  914 
  915                 LIST_REMOVE(inp, inp_hash);
  916                 LIST_REMOVE(inp, inp_portlist);
  917                 if (phd->phd_pcblist.lh_first == NULL) {
  918                         LIST_REMOVE(phd, phd_hash);
  919                         free(phd, M_PCB);
  920                 }
  921         }
  922         LIST_REMOVE(inp, inp_list);
  923         inp->inp_pcbinfo->ipi_count--;
  924 }

Cache object: f7d1d462e2a2dd757e385a7af5b89259


[ source navigation ] [ diff markup ] [ identifier search ] [ freetext search ] [ file search ] [ list types ] [ track identifier ]


This page is part of the FreeBSD/Linux Linux Kernel Cross-Reference, and was automatically generated using a modified version of the LXR engine.