source: rtems/cpukit/libnetworking/netinet/in_pcb.c @ 657e6c93

Last change on this file since 657e6c93 was 657e6c93, checked in by Christian Mauderer <Christian.Mauderer@…>, on Jun 24, 2016 at 5:57:17 AM

libnetworking: Import current <netinet/in.h>

Import the <netinet/in.h> from current FreeBSD. This allows to build
some current software (e.g. libressl).

Add legacy support like

  • prototype for in_cksum(),
  • IPPORT_USERRESERVED,
  • deprecated IPCTL_RT* defines,
  • ip_fw_chk_t and ip_fw_ctl_t,
  • ip_nat_... (IP NAT hooks), and
  • IP_NAT option for get/setsockopt()

to new <rtems/rtems_netinet_in.h>.

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