source: rtems/cpukit/libnetworking/netinet/in_pcb.c @ dd967330

4.104.114.9
Last change on this file since dd967330 was dd967330, checked in by Ralf Corsepius <ralf.corsepius@…>, on Sep 1, 2008 at 6:36:17 AM

Stop using old-style function definitions.

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