root/netinet/in_pcb.c

/* [<][>][^][v][top][bottom][index][help] */

DEFINITIONS

This source file includes following definitions.
  1. in_pcbinit
  2. in_baddynamic
  3. in_pcballoc
  4. in_pcbbind
  5. in_pcbconnect
  6. in_pcbdisconnect
  7. in_pcbdetach
  8. in_setsockaddr
  9. in_setpeeraddr
  10. in_pcbnotifyall
  11. in_losing
  12. in_rtchange
  13. in_pcblookup
  14. in_pcbrtentry
  15. in_selectsrc
  16. in_pcbrehash
  17. in_pcbhashlookup
  18. in6_pcbhashlookup
  19. in_pcblookup_listen
  20. in6_pcblookup_listen

    1 /*      $OpenBSD: in_pcb.c,v 1.89 2007/04/10 17:47:55 miod Exp $        */
    2 /*      $NetBSD: in_pcb.c,v 1.25 1996/02/13 23:41:53 christos Exp $     */
    3 
    4 /*
    5  * Copyright (c) 1982, 1986, 1991, 1993
    6  *      The Regents of the University of California.  All rights reserved.
    7  *
    8  * Redistribution and use in source and binary forms, with or without
    9  * modification, are permitted provided that the following conditions
   10  * are met:
   11  * 1. Redistributions of source code must retain the above copyright
   12  *    notice, this list of conditions and the following disclaimer.
   13  * 2. Redistributions in binary form must reproduce the above copyright
   14  *    notice, this list of conditions and the following disclaimer in the
   15  *    documentation and/or other materials provided with the distribution.
   16  * 3. Neither the name of the University nor the names of its contributors
   17  *    may be used to endorse or promote products derived from this software
   18  *    without specific prior written permission.
   19  *
   20  * THIS SOFTWARE IS PROVIDED BY THE REGENTS AND CONTRIBUTORS ``AS IS'' AND
   21  * ANY EXPRESS OR IMPLIED WARRANTIES, INCLUDING, BUT NOT LIMITED TO, THE
   22  * IMPLIED WARRANTIES OF MERCHANTABILITY AND FITNESS FOR A PARTICULAR PURPOSE
   23  * ARE DISCLAIMED.  IN NO EVENT SHALL THE REGENTS OR CONTRIBUTORS BE LIABLE
   24  * FOR ANY DIRECT, INDIRECT, INCIDENTAL, SPECIAL, EXEMPLARY, OR CONSEQUENTIAL
   25  * DAMAGES (INCLUDING, BUT NOT LIMITED TO, PROCUREMENT OF SUBSTITUTE GOODS
   26  * OR SERVICES; LOSS OF USE, DATA, OR PROFITS; OR BUSINESS INTERRUPTION)
   27  * HOWEVER CAUSED AND ON ANY THEORY OF LIABILITY, WHETHER IN CONTRACT, STRICT
   28  * LIABILITY, OR TORT (INCLUDING NEGLIGENCE OR OTHERWISE) ARISING IN ANY WAY
   29  * OUT OF THE USE OF THIS SOFTWARE, EVEN IF ADVISED OF THE POSSIBILITY OF
   30  * SUCH DAMAGE.
   31  *
   32  *      @(#)COPYRIGHT   1.1 (NRL) 17 January 1995
   33  *
   34  * NRL grants permission for redistribution and use in source and binary
   35  * forms, with or without modification, of the software and documentation
   36  * created at NRL provided that the following conditions are met:
   37  *
   38  * 1. Redistributions of source code must retain the above copyright
   39  *    notice, this list of conditions and the following disclaimer.
   40  * 2. Redistributions in binary form must reproduce the above copyright
   41  *    notice, this list of conditions and the following disclaimer in the
   42  *    documentation and/or other materials provided with the distribution.
   43  * 3. All advertising materials mentioning features or use of this software
   44  *    must display the following acknowledgements:
   45  *      This product includes software developed by the University of
   46  *      California, Berkeley and its contributors.
   47  *      This product includes software developed at the Information
   48  *      Technology Division, US Naval Research Laboratory.
   49  * 4. Neither the name of the NRL nor the names of its contributors
   50  *    may be used to endorse or promote products derived from this software
   51  *    without specific prior written permission.
   52  *
   53  * THE SOFTWARE PROVIDED BY NRL IS PROVIDED BY NRL AND CONTRIBUTORS ``AS
   54  * IS'' AND ANY EXPRESS OR IMPLIED WARRANTIES, INCLUDING, BUT NOT LIMITED
   55  * TO, THE IMPLIED WARRANTIES OF MERCHANTABILITY AND FITNESS FOR A
   56  * PARTICULAR PURPOSE ARE DISCLAIMED.  IN NO EVENT SHALL NRL OR
   57  * CONTRIBUTORS BE LIABLE FOR ANY DIRECT, INDIRECT, INCIDENTAL, SPECIAL,
   58  * EXEMPLARY, OR CONSEQUENTIAL DAMAGES (INCLUDING, BUT NOT LIMITED TO,
   59  * PROCUREMENT OF SUBSTITUTE GOODS OR SERVICES; LOSS OF USE, DATA, OR
   60  * PROFITS; OR BUSINESS INTERRUPTION) HOWEVER CAUSED AND ON ANY THEORY OF
   61  * LIABILITY, WHETHER IN CONTRACT, STRICT LIABILITY, OR TORT (INCLUDING
   62  * NEGLIGENCE OR OTHERWISE) ARISING IN ANY WAY OUT OF THE USE OF THIS
   63  * SOFTWARE, EVEN IF ADVISED OF THE POSSIBILITY OF SUCH DAMAGE.
   64  *
   65  * The views and conclusions contained in the software and documentation
   66  * are those of the authors and should not be interpreted as representing
   67  * official policies, either expressed or implied, of the US Naval
   68  * Research Laboratory (NRL).
   69  */
   70 
   71 #include <sys/param.h>
   72 #include <sys/systm.h>
   73 #include <sys/mbuf.h>
   74 #include <sys/protosw.h>
   75 #include <sys/socket.h>
   76 #include <sys/socketvar.h>
   77 #include <sys/proc.h>
   78 #include <sys/domain.h>
   79 
   80 #include <net/if.h>
   81 #include <net/route.h>
   82 
   83 #include <netinet/in.h>
   84 #include <netinet/in_systm.h>
   85 #include <netinet/ip.h>
   86 #include <netinet/in_pcb.h>
   87 #include <netinet/in_var.h>
   88 #include <netinet/ip_var.h>
   89 #include <dev/rndvar.h>
   90 
   91 #include <sys/mount.h>
   92 #include <nfs/nfsproto.h>
   93 
   94 #ifdef INET6
   95 #include <netinet6/ip6_var.h>
   96 #endif /* INET6 */
   97 #ifdef IPSEC
   98 #include <netinet/ip_esp.h>
   99 #endif /* IPSEC */
  100 
  101 struct  in_addr zeroin_addr;
  102 
  103 extern int ipsec_auth_default_level;
  104 extern int ipsec_esp_trans_default_level;
  105 extern int ipsec_esp_network_default_level;
  106 extern int ipsec_ipcomp_default_level;
  107 
  108 /*
  109  * These configure the range of local port addresses assigned to
  110  * "unspecified" outgoing connections/packets/whatever.
  111  */
  112 int ipport_firstauto = IPPORT_RESERVED;
  113 int ipport_lastauto = IPPORT_USERRESERVED;
  114 int ipport_hifirstauto = IPPORT_HIFIRSTAUTO;
  115 int ipport_hilastauto = IPPORT_HILASTAUTO;
  116 
  117 struct pool inpcb_pool;
  118 int inpcb_pool_initalized = 0;
  119 
  120 #define INPCBHASH(table, faddr, fport, laddr, lport) \
  121         &(table)->inpt_hashtbl[(ntohl((faddr)->s_addr) + \
  122         ntohs((fport)) + ntohs((lport))) & (table->inpt_hash)]
  123 
  124 #define IN6PCBHASH(table, faddr, fport, laddr, lport) \
  125         &(table)->inpt_hashtbl[(ntohl((faddr)->s6_addr32[0] ^ \
  126         (faddr)->s6_addr32[3]) + ntohs((fport)) + ntohs((lport))) & \
  127         (table->inpt_hash)]
  128 
  129 #define INPCBLHASH(table, lport) \
  130         &(table)->inpt_lhashtbl[lport & table->inpt_lhash]
  131 
  132 void
  133 in_pcbinit(table, hashsize)
  134         struct inpcbtable *table;
  135         int hashsize;
  136 {
  137 
  138         CIRCLEQ_INIT(&table->inpt_queue);
  139         table->inpt_hashtbl = hashinit(hashsize, M_PCB, M_NOWAIT,
  140             &table->inpt_hash);
  141         if (table->inpt_hashtbl == NULL)
  142                 panic("in_pcbinit: hashinit failed");
  143         table->inpt_lhashtbl = hashinit(hashsize, M_PCB, M_NOWAIT,
  144             &table->inpt_lhash);
  145         if (table->inpt_lhashtbl == NULL)
  146                 panic("in_pcbinit: hashinit failed for lport");
  147         table->inpt_lastport = 0;
  148 }
  149 
  150 struct baddynamicports baddynamicports;
  151 
  152 /*
  153  * Check if the specified port is invalid for dynamic allocation.
  154  */
  155 int
  156 in_baddynamic(port, proto)
  157         u_int16_t port;
  158         u_int16_t proto;
  159 {
  160 
  161 
  162         switch (proto) {
  163         case IPPROTO_TCP:
  164                 if (port == NFS_PORT)
  165                         return (1);
  166                 if (port < IPPORT_RESERVED/2 || port >= IPPORT_RESERVED)
  167                         return (0);
  168                 return (DP_ISSET(baddynamicports.tcp, port));
  169         case IPPROTO_UDP:
  170 #ifdef IPSEC
  171                 if (port == udpencap_port)
  172                         return (1);
  173 #endif
  174                 if (port == NFS_PORT)
  175                         return (1);
  176                 if (port < IPPORT_RESERVED/2 || port >= IPPORT_RESERVED)
  177                         return (0);
  178                 return (DP_ISSET(baddynamicports.udp, port));
  179         default:
  180                 return (0);
  181         }
  182 }
  183 
  184 int
  185 in_pcballoc(so, v)
  186         struct socket *so;
  187         void *v;
  188 {
  189         struct inpcbtable *table = v;
  190         struct inpcb *inp;
  191         int s;
  192 
  193         if (inpcb_pool_initalized == 0) {
  194                 pool_init(&inpcb_pool, sizeof(struct inpcb), 0, 0, 0,
  195                     "inpcbpl", NULL);
  196                 inpcb_pool_initalized = 1;
  197         }
  198         inp = pool_get(&inpcb_pool, PR_NOWAIT);
  199         if (inp == NULL)
  200                 return (ENOBUFS);
  201         bzero((caddr_t)inp, sizeof(*inp));
  202         inp->inp_table = table;
  203         inp->inp_socket = so;
  204         inp->inp_seclevel[SL_AUTH] = ipsec_auth_default_level;
  205         inp->inp_seclevel[SL_ESP_TRANS] = ipsec_esp_trans_default_level;
  206         inp->inp_seclevel[SL_ESP_NETWORK] = ipsec_esp_network_default_level;
  207         inp->inp_seclevel[SL_IPCOMP] = ipsec_ipcomp_default_level;
  208         s = splnet();
  209         CIRCLEQ_INSERT_HEAD(&table->inpt_queue, inp, inp_queue);
  210         LIST_INSERT_HEAD(INPCBLHASH(table, inp->inp_lport), inp, inp_lhash);
  211         LIST_INSERT_HEAD(INPCBHASH(table, &inp->inp_faddr, inp->inp_fport,
  212             &inp->inp_laddr, inp->inp_lport), inp, inp_hash);
  213         splx(s);
  214         so->so_pcb = inp;
  215         inp->inp_hops = -1;
  216 
  217 #ifdef INET6
  218         /*
  219          * Small change in this function to set the INP_IPV6 flag so routines
  220          * outside pcb-specific routines don't need to use sotopf(), and all
  221          * of its pointer chasing, later.
  222          */
  223         if (sotopf(so) == PF_INET6)
  224                 inp->inp_flags = INP_IPV6;
  225         inp->in6p_cksum = -1;
  226 #endif /* INET6 */
  227         return (0);
  228 }
  229 
  230 int
  231 in_pcbbind(v, nam)
  232         void *v;
  233         struct mbuf *nam;
  234 {
  235         struct inpcb *inp = v;
  236         struct socket *so = inp->inp_socket;
  237         struct inpcbtable *table = inp->inp_table;
  238         u_int16_t *lastport = &inp->inp_table->inpt_lastport;
  239         struct sockaddr_in *sin;
  240         struct proc *p = curproc;               /* XXX */
  241         u_int16_t lport = 0;
  242         int wild = 0, reuseport = (so->so_options & SO_REUSEPORT);
  243         int error;
  244 
  245 #ifdef INET6
  246         if (sotopf(so) == PF_INET6)
  247                 return in6_pcbbind(inp, nam);
  248 #endif /* INET6 */
  249 
  250         if (TAILQ_EMPTY(&in_ifaddr))
  251                 return (EADDRNOTAVAIL);
  252         if (inp->inp_lport || inp->inp_laddr.s_addr != INADDR_ANY)
  253                 return (EINVAL);
  254         if ((so->so_options & (SO_REUSEADDR|SO_REUSEPORT)) == 0 &&
  255             ((so->so_proto->pr_flags & PR_CONNREQUIRED) == 0 ||
  256              (so->so_options & SO_ACCEPTCONN) == 0))
  257                 wild = INPLOOKUP_WILDCARD;
  258         if (nam) {
  259                 sin = mtod(nam, struct sockaddr_in *);
  260                 if (nam->m_len != sizeof (*sin))
  261                         return (EINVAL);
  262 #ifdef notdef
  263                 /*
  264                  * We should check the family, but old programs
  265                  * incorrectly fail to initialize it.
  266                  */
  267                 if (sin->sin_family != AF_INET)
  268                         return (EAFNOSUPPORT);
  269 #endif
  270                 lport = sin->sin_port;
  271                 if (IN_MULTICAST(sin->sin_addr.s_addr)) {
  272                         /*
  273                          * Treat SO_REUSEADDR as SO_REUSEPORT for multicast;
  274                          * allow complete duplication of binding if
  275                          * SO_REUSEPORT is set, or if SO_REUSEADDR is set
  276                          * and a multicast address is bound on both
  277                          * new and duplicated sockets.
  278                          */
  279                         if (so->so_options & SO_REUSEADDR)
  280                                 reuseport = SO_REUSEADDR|SO_REUSEPORT;
  281                 } else if (sin->sin_addr.s_addr != INADDR_ANY) {
  282                         sin->sin_port = 0;              /* yech... */
  283                         if (in_iawithaddr(sin->sin_addr, NULL) == 0)
  284                                 return (EADDRNOTAVAIL);
  285                 }
  286                 if (lport) {
  287                         struct inpcb *t;
  288 
  289                         /* GROSS */
  290                         if (ntohs(lport) < IPPORT_RESERVED &&
  291                             (error = suser(p, 0)))
  292                                 return (EACCES);
  293                         if (so->so_euid) {
  294                                 t = in_pcblookup(table, &zeroin_addr, 0,
  295                                     &sin->sin_addr, lport, INPLOOKUP_WILDCARD);
  296                                 if (t && (so->so_euid != t->inp_socket->so_euid))
  297                                         return (EADDRINUSE);
  298                         }
  299                         t = in_pcblookup(table, &zeroin_addr, 0,
  300                             &sin->sin_addr, lport, wild);
  301                         if (t && (reuseport & t->inp_socket->so_options) == 0)
  302                                 return (EADDRINUSE);
  303                 }
  304                 inp->inp_laddr = sin->sin_addr;
  305         }
  306         if (lport == 0) {
  307                 u_int16_t first, last;
  308                 int count;
  309 
  310                 if (inp->inp_flags & INP_HIGHPORT) {
  311                         first = ipport_hifirstauto;     /* sysctl */
  312                         last = ipport_hilastauto;
  313                 } else if (inp->inp_flags & INP_LOWPORT) {
  314                         if ((error = suser(p, 0)))
  315                                 return (EACCES);
  316                         first = IPPORT_RESERVED-1; /* 1023 */
  317                         last = 600;                /* not IPPORT_RESERVED/2 */
  318                 } else {
  319                         first = ipport_firstauto;       /* sysctl */
  320                         last  = ipport_lastauto;
  321                 }
  322 
  323                 /*
  324                  * Simple check to ensure all ports are not used up causing
  325                  * a deadlock here.
  326                  *
  327                  * We split the two cases (up and down) so that the direction
  328                  * is not being tested on each round of the loop.
  329                  */
  330 
  331                 if (first > last) {
  332                         /*
  333                          * counting down
  334                          */
  335                         count = first - last;
  336                         if (count)
  337                                 *lastport = first - (arc4random() % count);
  338 
  339                         do {
  340                                 if (count-- < 0)        /* completely used? */
  341                                         return (EADDRNOTAVAIL);
  342                                 --*lastport;
  343                                 if (*lastport > first || *lastport < last)
  344                                         *lastport = first;
  345                                 lport = htons(*lastport);
  346                         } while (in_baddynamic(*lastport, so->so_proto->pr_protocol) ||
  347                             in_pcblookup(table, &zeroin_addr, 0,
  348                             &inp->inp_laddr, lport, wild));
  349                 } else {
  350                         /*
  351                          * counting up
  352                          */
  353                         count = last - first;
  354                         if (count)
  355                                 *lastport = first + (arc4random() % count);
  356 
  357                         do {
  358                                 if (count-- < 0)        /* completely used? */
  359                                         return (EADDRNOTAVAIL);
  360                                 ++*lastport;
  361                                 if (*lastport < first || *lastport > last)
  362                                         *lastport = first;
  363                                 lport = htons(*lastport);
  364                         } while (in_baddynamic(*lastport, so->so_proto->pr_protocol) ||
  365                             in_pcblookup(table, &zeroin_addr, 0,
  366                             &inp->inp_laddr, lport, wild));
  367                 }
  368         }
  369         inp->inp_lport = lport;
  370         in_pcbrehash(inp);
  371         return (0);
  372 }
  373 
  374 /*
  375  * Connect from a socket to a specified address.
  376  * Both address and port must be specified in argument sin.
  377  * If don't have a local address for this socket yet,
  378  * then pick one.
  379  */
  380 int
  381 in_pcbconnect(v, nam)
  382         void *v;
  383         struct mbuf *nam;
  384 {
  385         struct inpcb *inp = v;
  386         struct sockaddr_in *ifaddr = NULL;
  387         struct sockaddr_in *sin = mtod(nam, struct sockaddr_in *);
  388 
  389 #ifdef INET6
  390         if (sotopf(inp->inp_socket) == PF_INET6)
  391                 return (in6_pcbconnect(inp, nam));
  392         if ((inp->inp_flags & INP_IPV6) != 0)
  393                 panic("IPv6 pcb passed into in_pcbconnect");
  394 #endif /* INET6 */
  395 
  396         if (nam->m_len != sizeof (*sin))
  397                 return (EINVAL);
  398         if (sin->sin_family != AF_INET)
  399                 return (EAFNOSUPPORT);
  400         if (sin->sin_port == 0)
  401                 return (EADDRNOTAVAIL);
  402         if (!TAILQ_EMPTY(&in_ifaddr)) {
  403                 /*
  404                  * If the destination address is INADDR_ANY,
  405                  * use the primary local address.
  406                  * If the supplied address is INADDR_BROADCAST,
  407                  * and the primary interface supports broadcast,
  408                  * choose the broadcast address for that interface.
  409                  */
  410                 if (sin->sin_addr.s_addr == INADDR_ANY)
  411                         sin->sin_addr = TAILQ_FIRST(&in_ifaddr)->ia_addr.sin_addr;
  412                 else if (sin->sin_addr.s_addr == INADDR_BROADCAST &&
  413                   (TAILQ_FIRST(&in_ifaddr)->ia_ifp->if_flags & IFF_BROADCAST))
  414                         sin->sin_addr = TAILQ_FIRST(&in_ifaddr)->ia_broadaddr.sin_addr;
  415         }
  416         if (inp->inp_laddr.s_addr == INADDR_ANY) {
  417                 int error;
  418                 ifaddr = in_selectsrc(sin, &inp->inp_route,
  419                         inp->inp_socket->so_options, inp->inp_moptions, &error);
  420                 if (ifaddr == NULL) {
  421                         if (error == 0)
  422                                 error = EADDRNOTAVAIL;
  423                         return error;
  424                 }
  425         }
  426         if (in_pcbhashlookup(inp->inp_table, sin->sin_addr, sin->sin_port,
  427             inp->inp_laddr.s_addr ? inp->inp_laddr : ifaddr->sin_addr,
  428             inp->inp_lport) != 0)
  429                 return (EADDRINUSE);
  430         if (inp->inp_laddr.s_addr == INADDR_ANY) {
  431                 if (inp->inp_lport == 0 &&
  432                     in_pcbbind(inp, (struct mbuf *)0) == EADDRNOTAVAIL)
  433                         return (EADDRNOTAVAIL);
  434                 inp->inp_laddr = ifaddr->sin_addr;
  435         }
  436         inp->inp_faddr = sin->sin_addr;
  437         inp->inp_fport = sin->sin_port;
  438         in_pcbrehash(inp);
  439 #ifdef IPSEC
  440         {
  441                 int error; /* This is just ignored */
  442 
  443                 /* Cause an IPsec SA to be established. */
  444                 ipsp_spd_inp(NULL, AF_INET, 0, &error, IPSP_DIRECTION_OUT,
  445                     NULL, inp, NULL);
  446         }
  447 #endif
  448         return (0);
  449 }
  450 
  451 void
  452 in_pcbdisconnect(v)
  453         void *v;
  454 {
  455         struct inpcb *inp = v;
  456 
  457         switch (sotopf(inp->inp_socket)) {
  458 #ifdef INET6
  459         case PF_INET6:
  460                 inp->inp_faddr6 = in6addr_any;
  461                 break;
  462 #endif
  463         case PF_INET:
  464                 inp->inp_faddr.s_addr = INADDR_ANY;
  465                 break;
  466         }
  467 
  468         inp->inp_fport = 0;
  469         in_pcbrehash(inp);
  470         if (inp->inp_socket->so_state & SS_NOFDREF)
  471                 in_pcbdetach(inp);
  472 }
  473 
  474 void
  475 in_pcbdetach(v)
  476         void *v;
  477 {
  478         struct inpcb *inp = v;
  479         struct socket *so = inp->inp_socket;
  480         int s;
  481 
  482         so->so_pcb = 0;
  483         sofree(so);
  484         if (inp->inp_options)
  485                 (void)m_freem(inp->inp_options);
  486         if (inp->inp_route.ro_rt)
  487                 rtfree(inp->inp_route.ro_rt);
  488 #ifdef INET6
  489         if (inp->inp_flags & INP_IPV6)
  490                 ip6_freemoptions(inp->inp_moptions6);
  491         else
  492 #endif
  493                 ip_freemoptions(inp->inp_moptions);
  494 #ifdef IPSEC
  495         /* IPsec cleanup here */
  496         s = spltdb();
  497         if (inp->inp_tdb_in)
  498                 TAILQ_REMOVE(&inp->inp_tdb_in->tdb_inp_in,
  499                              inp, inp_tdb_in_next);
  500         if (inp->inp_tdb_out)
  501                 TAILQ_REMOVE(&inp->inp_tdb_out->tdb_inp_out, inp,
  502                              inp_tdb_out_next);
  503         if (inp->inp_ipsec_remotecred)
  504                 ipsp_reffree(inp->inp_ipsec_remotecred);
  505         if (inp->inp_ipsec_remoteauth)
  506                 ipsp_reffree(inp->inp_ipsec_remoteauth);
  507         if (inp->inp_ipo)
  508                 ipsec_delete_policy(inp->inp_ipo);
  509         splx(s);
  510 #endif
  511         s = splnet();
  512         LIST_REMOVE(inp, inp_lhash);
  513         LIST_REMOVE(inp, inp_hash);
  514         CIRCLEQ_REMOVE(&inp->inp_table->inpt_queue, inp, inp_queue);
  515         splx(s);
  516         pool_put(&inpcb_pool, inp);
  517 }
  518 
  519 void
  520 in_setsockaddr(inp, nam)
  521         struct inpcb *inp;
  522         struct mbuf *nam;
  523 {
  524         struct sockaddr_in *sin;
  525 
  526         nam->m_len = sizeof (*sin);
  527         sin = mtod(nam, struct sockaddr_in *);
  528         bzero((caddr_t)sin, sizeof (*sin));
  529         sin->sin_family = AF_INET;
  530         sin->sin_len = sizeof(*sin);
  531         sin->sin_port = inp->inp_lport;
  532         sin->sin_addr = inp->inp_laddr;
  533 }
  534 
  535 void
  536 in_setpeeraddr(inp, nam)
  537         struct inpcb *inp;
  538         struct mbuf *nam;
  539 {
  540         struct sockaddr_in *sin;
  541 
  542 #ifdef INET6
  543         if (sotopf(inp->inp_socket) == PF_INET6) {
  544                 in6_setpeeraddr(inp, nam);
  545                 return;
  546         }
  547 #endif /* INET6 */
  548 
  549         nam->m_len = sizeof (*sin);
  550         sin = mtod(nam, struct sockaddr_in *);
  551         bzero((caddr_t)sin, sizeof (*sin));
  552         sin->sin_family = AF_INET;
  553         sin->sin_len = sizeof(*sin);
  554         sin->sin_port = inp->inp_fport;
  555         sin->sin_addr = inp->inp_faddr;
  556 }
  557 
  558 /*
  559  * Pass some notification to all connections of a protocol
  560  * associated with address dst.  The "usual action" will be
  561  * taken, depending on the ctlinput cmd.  The caller must filter any
  562  * cmds that are uninteresting (e.g., no error in the map).
  563  * Call the protocol specific routine (if any) to report
  564  * any errors for each matching socket.
  565  *
  566  * Must be called at splsoftnet.
  567  */
  568 void
  569 in_pcbnotifyall(table, dst, errno, notify)
  570         struct inpcbtable *table;
  571         struct sockaddr *dst;
  572         int errno;
  573         void (*notify)(struct inpcb *, int);
  574 {
  575         struct inpcb *inp, *oinp;
  576         struct in_addr faddr;
  577 
  578         splassert(IPL_SOFTNET);
  579 
  580 #ifdef INET6
  581         /*
  582          * See in6_pcbnotify() for IPv6 codepath.  By the time this
  583          * gets called, the addresses passed are either definitely IPv4 or
  584          * IPv6; *_pcbnotify() never gets called with v4-mapped v6 addresses.
  585          */
  586 #endif /* INET6 */
  587 
  588         if (dst->sa_family != AF_INET)
  589                 return;
  590         faddr = satosin(dst)->sin_addr;
  591         if (faddr.s_addr == INADDR_ANY)
  592                 return;
  593 
  594         for (inp = CIRCLEQ_FIRST(&table->inpt_queue);
  595             inp != CIRCLEQ_END(&table->inpt_queue);) {
  596 #ifdef INET6
  597                 if (inp->inp_flags & INP_IPV6) {
  598                         inp = CIRCLEQ_NEXT(inp, inp_queue);
  599                         continue;
  600                 }
  601 #endif
  602                 if (inp->inp_faddr.s_addr != faddr.s_addr ||
  603                     inp->inp_socket == 0) {
  604                         inp = CIRCLEQ_NEXT(inp, inp_queue);
  605                         continue;
  606                 }
  607                 oinp = inp;
  608                 inp = CIRCLEQ_NEXT(inp, inp_queue);
  609                 if (notify)
  610                         (*notify)(oinp, errno);
  611         }
  612 }
  613 
  614 /*
  615  * Check for alternatives when higher level complains
  616  * about service problems.  For now, invalidate cached
  617  * routing information.  If the route was created dynamically
  618  * (by a redirect), time to try a default gateway again.
  619  */
  620 void
  621 in_losing(inp)
  622         struct inpcb *inp;
  623 {
  624         struct rtentry *rt;
  625         struct rt_addrinfo info;
  626 
  627         if ((rt = inp->inp_route.ro_rt)) {
  628                 inp->inp_route.ro_rt = 0;
  629                 bzero((caddr_t)&info, sizeof(info));
  630                 info.rti_info[RTAX_DST] = &inp->inp_route.ro_dst;
  631                 info.rti_info[RTAX_GATEWAY] = rt->rt_gateway;
  632                 info.rti_info[RTAX_NETMASK] = rt_mask(rt);
  633                 rt_missmsg(RTM_LOSING, &info, rt->rt_flags, rt->rt_ifp, 0, 0);
  634                 if (rt->rt_flags & RTF_DYNAMIC)
  635                         (void) rtrequest(RTM_DELETE, rt_key(rt),
  636                                 rt->rt_gateway, rt_mask(rt), rt->rt_flags,
  637                                 (struct rtentry **)0, 0);
  638                 /*
  639                  * A new route can be allocated
  640                  * the next time output is attempted.
  641                  * rtfree() needs to be called in anycase because the inp
  642                  * is still holding a reference to rt.
  643                  */
  644                 rtfree(rt);
  645         }
  646 }
  647 
  648 /*
  649  * After a routing change, flush old routing
  650  * and allocate a (hopefully) better one.
  651  */
  652 void
  653 in_rtchange(inp, errno)
  654         struct inpcb *inp;
  655         int errno;
  656 {
  657         if (inp->inp_route.ro_rt) {
  658                 rtfree(inp->inp_route.ro_rt);
  659                 inp->inp_route.ro_rt = 0;
  660                 /*
  661                  * A new route can be allocated the next time
  662                  * output is attempted.
  663                  */
  664         }
  665 }
  666 
  667 struct inpcb *
  668 in_pcblookup(table, faddrp, fport_arg, laddrp, lport_arg, flags)
  669         struct inpcbtable *table;
  670         void *faddrp, *laddrp;
  671         u_int fport_arg, lport_arg;
  672         int flags;
  673 {
  674         struct inpcb *inp, *match = 0;
  675         int matchwild = 3, wildcard;
  676         u_int16_t fport = fport_arg, lport = lport_arg;
  677         struct in_addr faddr = *(struct in_addr *)faddrp;
  678         struct in_addr laddr = *(struct in_addr *)laddrp;
  679 
  680         for (inp = LIST_FIRST(INPCBLHASH(table, lport)); inp;
  681             inp = LIST_NEXT(inp, inp_lhash)) {
  682                 if (inp->inp_lport != lport)
  683                         continue;
  684                 wildcard = 0;
  685 #ifdef INET6
  686                 if (flags & INPLOOKUP_IPV6) {
  687                         struct in6_addr *laddr6 = (struct in6_addr *)laddrp;
  688                         struct in6_addr *faddr6 = (struct in6_addr *)faddrp;
  689 
  690                         if (!(inp->inp_flags & INP_IPV6))
  691                                 continue;
  692 
  693                         if (!IN6_IS_ADDR_UNSPECIFIED(&inp->inp_laddr6)) {
  694                                 if (IN6_IS_ADDR_UNSPECIFIED(laddr6))
  695                                         wildcard++;
  696                                 else if (!IN6_ARE_ADDR_EQUAL(&inp->inp_laddr6, laddr6))
  697                                         continue;
  698                         } else {
  699                                 if (!IN6_IS_ADDR_UNSPECIFIED(laddr6))
  700                                         wildcard++;
  701                         }
  702 
  703                         if (!IN6_IS_ADDR_UNSPECIFIED(&inp->inp_faddr6)) {
  704                                 if (IN6_IS_ADDR_UNSPECIFIED(faddr6))
  705                                         wildcard++;
  706                                 else if (!IN6_ARE_ADDR_EQUAL(&inp->inp_faddr6,
  707                                     faddr6) || inp->inp_fport != fport)
  708                                         continue;
  709                         } else {
  710                                 if (!IN6_IS_ADDR_UNSPECIFIED(faddr6))
  711                                         wildcard++;
  712                         }
  713                 } else
  714 #endif /* INET6 */
  715                 {
  716 #ifdef INET6
  717                         if (inp->inp_flags & INP_IPV6)
  718                                 continue;
  719 #endif /* INET6 */
  720 
  721                         if (inp->inp_faddr.s_addr != INADDR_ANY) {
  722                                 if (faddr.s_addr == INADDR_ANY)
  723                                         wildcard++;
  724                                 else if (inp->inp_faddr.s_addr != faddr.s_addr ||
  725                                     inp->inp_fport != fport)
  726                                         continue;
  727                         } else {
  728                                 if (faddr.s_addr != INADDR_ANY)
  729                                         wildcard++;
  730                         }
  731                         if (inp->inp_laddr.s_addr != INADDR_ANY) {
  732                                 if (laddr.s_addr == INADDR_ANY)
  733                                         wildcard++;
  734                                 else if (inp->inp_laddr.s_addr != laddr.s_addr)
  735                                         continue;
  736                         } else {
  737                                 if (laddr.s_addr != INADDR_ANY)
  738                                         wildcard++;
  739                         }
  740                 }
  741                 if ((!wildcard || (flags & INPLOOKUP_WILDCARD)) &&
  742                     wildcard < matchwild) {
  743                         match = inp;
  744                         if ((matchwild = wildcard) == 0)
  745                                 break;
  746                 }
  747         }
  748         return (match);
  749 }
  750 
  751 struct rtentry *
  752 in_pcbrtentry(inp)
  753         struct inpcb *inp;
  754 {
  755         struct route *ro;
  756 
  757         ro = &inp->inp_route;
  758 
  759         /*
  760          * No route yet, so try to acquire one.
  761          */
  762         if (ro->ro_rt == NULL) {
  763 #ifdef INET6
  764                 bzero(ro, sizeof(struct route_in6));
  765 #else
  766                 bzero(ro, sizeof(struct route));
  767 #endif
  768 
  769                 switch(sotopf(inp->inp_socket)) {
  770 #ifdef INET6
  771                 case PF_INET6:
  772                         if (IN6_IS_ADDR_UNSPECIFIED(&inp->inp_faddr6))
  773                                 break;
  774                         ro->ro_dst.sa_family = AF_INET6;
  775                         ro->ro_dst.sa_len = sizeof(struct sockaddr_in6);
  776                         ((struct sockaddr_in6 *) &ro->ro_dst)->sin6_addr =
  777                             inp->inp_faddr6;
  778                         rtalloc_mpath(ro, &inp->inp_laddr6.s6_addr32[0], 0);
  779                         break;
  780 #endif /* INET6 */
  781                 case PF_INET:
  782                         if (inp->inp_faddr.s_addr == INADDR_ANY)
  783                                 break;
  784                         ro->ro_dst.sa_family = AF_INET;
  785                         ro->ro_dst.sa_len = sizeof(ro->ro_dst);
  786                         satosin(&ro->ro_dst)->sin_addr = inp->inp_faddr;
  787                         rtalloc_mpath(ro, &inp->inp_laddr.s_addr, 0);
  788                         break;
  789                 }
  790         }
  791         return (ro->ro_rt);
  792 }
  793 
  794 struct sockaddr_in *
  795 in_selectsrc(sin, ro, soopts, mopts, errorp)
  796         struct sockaddr_in *sin;
  797         struct route *ro;
  798         int soopts;
  799         struct ip_moptions *mopts;
  800         int *errorp;
  801 {
  802         struct sockaddr_in *sin2;
  803         struct in_ifaddr *ia;
  804 
  805         ia = (struct in_ifaddr *)0;
  806         /*
  807          * If route is known or can be allocated now,
  808          * our src addr is taken from the i/f, else punt.
  809          */
  810         if (ro->ro_rt &&
  811             (satosin(&ro->ro_dst)->sin_addr.s_addr !=
  812                 sin->sin_addr.s_addr ||
  813             soopts & SO_DONTROUTE)) {
  814                 RTFREE(ro->ro_rt);
  815                 ro->ro_rt = (struct rtentry *)0;
  816         }
  817         if ((soopts & SO_DONTROUTE) == 0 && /*XXX*/
  818             (ro->ro_rt == (struct rtentry *)0 ||
  819             ro->ro_rt->rt_ifp == (struct ifnet *)0)) {
  820                 /* No route yet, so try to acquire one */
  821                 ro->ro_dst.sa_family = AF_INET;
  822                 ro->ro_dst.sa_len = sizeof(struct sockaddr_in);
  823                 satosin(&ro->ro_dst)->sin_addr = sin->sin_addr;
  824                 rtalloc_mpath(ro, NULL, 0);
  825 
  826                 /*
  827                  * It is important to bzero out the rest of the
  828                  * struct sockaddr_in when mixing v6 & v4!
  829                  */
  830                 sin2 = (struct sockaddr_in *)&ro->ro_dst;
  831                 bzero(sin2->sin_zero, sizeof(sin2->sin_zero));
  832         }
  833         /*
  834          * If we found a route, use the address
  835          * corresponding to the outgoing interface
  836          * unless it is the loopback (in case a route
  837          * to our address on another net goes to loopback).
  838          */
  839         if (ro->ro_rt && !(ro->ro_rt->rt_ifp->if_flags & IFF_LOOPBACK))
  840                 ia = ifatoia(ro->ro_rt->rt_ifa);
  841         if (ia == 0) {
  842                 u_int16_t fport = sin->sin_port;
  843 
  844                 sin->sin_port = 0;
  845                 ia = ifatoia(ifa_ifwithdstaddr(sintosa(sin)));
  846                 if (ia == 0)
  847                         ia = ifatoia(ifa_ifwithnet(sintosa(sin)));
  848                 sin->sin_port = fport;
  849                 if (ia == 0)
  850                         ia = TAILQ_FIRST(&in_ifaddr);
  851                 if (ia == 0) {
  852                         *errorp = EADDRNOTAVAIL;
  853                         return NULL;
  854                 }
  855         }
  856         /*
  857          * If the destination address is multicast and an outgoing
  858          * interface has been set as a multicast option, use the
  859          * address of that interface as our source address.
  860          */
  861         if (IN_MULTICAST(sin->sin_addr.s_addr) && mopts != NULL) {
  862                 struct ip_moptions *imo;
  863                 struct ifnet *ifp;
  864 
  865                 imo = mopts;
  866                 if (imo->imo_multicast_ifp != NULL) {
  867                         ifp = imo->imo_multicast_ifp;
  868                         TAILQ_FOREACH(ia, &in_ifaddr, ia_list)
  869                                 if (ia->ia_ifp == ifp)
  870                                         break;
  871                         if (ia == 0) {
  872                                 *errorp = EADDRNOTAVAIL;
  873                                 return NULL;
  874                         }
  875                 }
  876         }
  877         return satosin(&ia->ia_addr);
  878 }
  879 
  880 void
  881 in_pcbrehash(inp)
  882         struct inpcb *inp;
  883 {
  884         struct inpcbtable *table = inp->inp_table;
  885         int s;
  886 
  887         s = splnet();
  888         LIST_REMOVE(inp, inp_lhash);
  889         LIST_INSERT_HEAD(INPCBLHASH(table, inp->inp_lport), inp, inp_lhash);
  890         LIST_REMOVE(inp, inp_hash);
  891 #ifdef INET6
  892         if (inp->inp_flags & INP_IPV6) {
  893                 LIST_INSERT_HEAD(IN6PCBHASH(table, &inp->inp_faddr6,
  894                     inp->inp_fport, &inp->inp_laddr6, inp->inp_lport),
  895                     inp, inp_hash);
  896         } else {
  897 #endif /* INET6 */
  898                 LIST_INSERT_HEAD(INPCBHASH(table, &inp->inp_faddr,
  899                     inp->inp_fport, &inp->inp_laddr, inp->inp_lport),
  900                     inp, inp_hash);
  901 #ifdef INET6
  902         }
  903 #endif /* INET6 */
  904         splx(s);
  905 }
  906 
  907 #ifdef DIAGNOSTIC
  908 int     in_pcbnotifymiss = 0;
  909 #endif
  910 
  911 /*
  912  * The in(6)_pcbhashlookup functions are used to locate connected sockets
  913  * quickly:
  914  *              faddr.fport <-> laddr.lport
  915  * No wildcard matching is done so that listening sockets are not found.
  916  * If the functions return NULL in(6)_pcblookup_listen can be used to
  917  * find a listening/bound socket that may accept the connection.
  918  * After those two lookups no other are necessary.
  919  */
  920 struct inpcb *
  921 in_pcbhashlookup(table, faddr, fport_arg, laddr, lport_arg)
  922         struct inpcbtable *table;
  923         struct in_addr faddr, laddr;
  924         u_int fport_arg, lport_arg;
  925 {
  926         struct inpcbhead *head;
  927         struct inpcb *inp;
  928         u_int16_t fport = fport_arg, lport = lport_arg;
  929 
  930         head = INPCBHASH(table, &faddr, fport, &laddr, lport);
  931         LIST_FOREACH(inp, head, inp_hash) {
  932 #ifdef INET6
  933                 if (inp->inp_flags & INP_IPV6)
  934                         continue;       /*XXX*/
  935 #endif
  936                 if (inp->inp_faddr.s_addr == faddr.s_addr &&
  937                     inp->inp_fport == fport &&
  938                     inp->inp_lport == lport &&
  939                     inp->inp_laddr.s_addr == laddr.s_addr) {
  940                         /*
  941                          * Move this PCB to the head of hash chain so that
  942                          * repeated accesses are quicker.  This is analogous to
  943                          * the historic single-entry PCB cache.
  944                          */
  945                         if (inp != LIST_FIRST(head)) {
  946                                 LIST_REMOVE(inp, inp_hash);
  947                                 LIST_INSERT_HEAD(head, inp, inp_hash);
  948                         }
  949                         break;
  950                 }
  951         }
  952 #ifdef DIAGNOSTIC
  953         if (inp == NULL && in_pcbnotifymiss) {
  954                 printf("in_pcbhashlookup: faddr=%08x fport=%d laddr=%08x lport=%d\n",
  955                     ntohl(faddr.s_addr), ntohs(fport),
  956                     ntohl(laddr.s_addr), ntohs(lport));
  957         }
  958 #endif
  959         return (inp);
  960 }
  961 
  962 #ifdef INET6
  963 struct inpcb *
  964 in6_pcbhashlookup(table, faddr, fport_arg, laddr, lport_arg)
  965         struct inpcbtable *table;
  966         struct in6_addr *faddr, *laddr;
  967         u_int fport_arg, lport_arg;
  968 {
  969         struct inpcbhead *head;
  970         struct inpcb *inp;
  971         u_int16_t fport = fport_arg, lport = lport_arg;
  972 
  973         head = IN6PCBHASH(table, faddr, fport, laddr, lport);
  974         LIST_FOREACH(inp, head, inp_hash) {
  975                 if (!(inp->inp_flags & INP_IPV6))
  976                         continue;
  977                 if (IN6_ARE_ADDR_EQUAL(&inp->inp_faddr6, faddr) &&
  978                     inp->inp_fport == fport && inp->inp_lport == lport &&
  979                     IN6_ARE_ADDR_EQUAL(&inp->inp_laddr6, laddr)) {
  980                         /*
  981                          * Move this PCB to the head of hash chain so that
  982                          * repeated accesses are quicker.  This is analogous to
  983                          * the historic single-entry PCB cache.
  984                          */
  985                         if (inp != LIST_FIRST(head)) {
  986                                 LIST_REMOVE(inp, inp_hash);
  987                                 LIST_INSERT_HEAD(head, inp, inp_hash);
  988                         }
  989                         break;
  990                 }
  991         }
  992 #ifdef DIAGNOSTIC
  993         if (inp == NULL && in_pcbnotifymiss) {
  994                 printf("in6_pcbhashlookup: faddr=");
  995                 printf(" fport=%d laddr=", ntohs(fport));
  996                 printf(" lport=%d\n", ntohs(lport));
  997         }
  998 #endif
  999         return (inp);
 1000 }
 1001 #endif /* INET6 */
 1002 
 1003 /*
 1004  * The in(6)_pcblookup_listen functions are used to locate listening
 1005  * sockets quickly.  This are sockets with unspecified foreign address
 1006  * and port:
 1007  *              *.*     <-> laddr.lport
 1008  *              *.*     <->     *.lport
 1009  */
 1010 struct inpcb *
 1011 in_pcblookup_listen(table, laddr, lport_arg, reverse)
 1012         struct inpcbtable *table;
 1013         struct in_addr laddr;
 1014         u_int lport_arg;
 1015         int reverse;
 1016 {
 1017         struct inpcbhead *head;
 1018         struct in_addr *key1, *key2;
 1019         struct inpcb *inp;
 1020         u_int16_t lport = lport_arg;
 1021 
 1022         if (reverse) {
 1023                 key1 = &zeroin_addr;
 1024                 key2 = &laddr;
 1025         } else {
 1026                 key1 = &laddr;
 1027                 key2 = &zeroin_addr;
 1028         }
 1029 
 1030         head = INPCBHASH(table, &zeroin_addr, 0, key1, lport);
 1031         LIST_FOREACH(inp, head, inp_hash) {
 1032 #ifdef INET6
 1033                 if (inp->inp_flags & INP_IPV6)
 1034                         continue;       /*XXX*/
 1035 #endif
 1036                 if (inp->inp_lport == lport && inp->inp_fport == 0 &&
 1037                     inp->inp_laddr.s_addr == key1->s_addr &&
 1038                     inp->inp_faddr.s_addr == INADDR_ANY)
 1039                         break;
 1040         }
 1041         if (inp == NULL && key1->s_addr != key2->s_addr) {
 1042                 head = INPCBHASH(table, &zeroin_addr, 0, key2, lport);
 1043                 LIST_FOREACH(inp, head, inp_hash) {
 1044 #ifdef INET6
 1045                         if (inp->inp_flags & INP_IPV6)
 1046                                 continue;       /*XXX*/
 1047 #endif
 1048                         if (inp->inp_lport == lport && inp->inp_fport == 0 &&
 1049                             inp->inp_laddr.s_addr == key2->s_addr &&
 1050                             inp->inp_faddr.s_addr == INADDR_ANY)
 1051                                 break;
 1052                 }
 1053         }
 1054 #ifdef DIAGNOSTIC
 1055         if (inp == NULL && in_pcbnotifymiss) {
 1056                 printf("in_pcblookup_listen: laddr=%08x lport=%d\n",
 1057                     ntohl(laddr.s_addr), ntohs(lport));
 1058         }
 1059 #endif
 1060         /*
 1061          * Move this PCB to the head of hash chain so that
 1062          * repeated accesses are quicker.  This is analogous to
 1063          * the historic single-entry PCB cache.
 1064          */
 1065         if (inp != NULL && inp != LIST_FIRST(head)) {
 1066                 LIST_REMOVE(inp, inp_hash);
 1067                 LIST_INSERT_HEAD(head, inp, inp_hash);
 1068         }
 1069         return (inp);
 1070 }
 1071 
 1072 #ifdef INET6
 1073 struct inpcb *
 1074 in6_pcblookup_listen(table, laddr, lport_arg, reverse)
 1075         struct inpcbtable *table;
 1076         struct in6_addr *laddr;
 1077         u_int lport_arg;
 1078         int reverse;
 1079 {
 1080         struct inpcbhead *head;
 1081         struct in6_addr *key1, *key2;
 1082         struct inpcb *inp;
 1083         u_int16_t lport = lport_arg;
 1084 
 1085         if (reverse) {
 1086                 key1 = &zeroin6_addr;
 1087                 key2 = laddr;
 1088         } else {
 1089                 key1 = laddr;
 1090                 key2 = &zeroin6_addr;
 1091         }
 1092 
 1093         head = IN6PCBHASH(table, &zeroin6_addr, 0, key1, lport);
 1094         LIST_FOREACH(inp, head, inp_hash) {
 1095                 if (!(inp->inp_flags & INP_IPV6))
 1096                         continue;
 1097                 if (inp->inp_lport == lport && inp->inp_fport == 0 &&
 1098                     IN6_ARE_ADDR_EQUAL(&inp->inp_laddr6, key1) &&
 1099                     IN6_IS_ADDR_UNSPECIFIED(&inp->inp_faddr6))
 1100                         break;
 1101         }
 1102         if (inp == NULL && ! IN6_ARE_ADDR_EQUAL(key1, key2)) {
 1103                 head = IN6PCBHASH(table, &zeroin6_addr, 0, key2, lport);
 1104                 LIST_FOREACH(inp, head, inp_hash) {
 1105                         if (!(inp->inp_flags & INP_IPV6))
 1106                                 continue;
 1107                         if (inp->inp_lport == lport && inp->inp_fport == 0 &&
 1108                             IN6_ARE_ADDR_EQUAL(&inp->inp_laddr6, key2) &&
 1109                             IN6_IS_ADDR_UNSPECIFIED(&inp->inp_faddr6))
 1110                                 break;
 1111                 }
 1112         }
 1113 #ifdef DIAGNOSTIC
 1114         if (inp == NULL && in_pcbnotifymiss) {
 1115                 printf("in6_pcblookup_listen: laddr= lport=%d\n",
 1116                     ntohs(lport));
 1117         }
 1118 #endif
 1119         /*
 1120          * Move this PCB to the head of hash chain so that
 1121          * repeated accesses are quicker.  This is analogous to
 1122          * the historic single-entry PCB cache.
 1123          */
 1124         if (inp != NULL && inp != LIST_FIRST(head)) {
 1125                 LIST_REMOVE(inp, inp_hash);
 1126                 LIST_INSERT_HEAD(head, inp, inp_hash);
 1127         }
 1128         return (inp);
 1129 }
 1130 #endif /* INET6 */

/* [<][>][^][v][top][bottom][index][help] */