source: rtems/cpukit/libnetworking/netinet/in_pcb.c @ 36799d4

4.104.114.84.95
Last change on this file since 36799d4 was 36799d4, checked in by Joel Sherrill <joel.sherrill@…>, on Jan 3, 2003 at 6:09:57 PM

2002-11-26 Chris Johns <cjohns@…>

  • Makefile.am: Added sys/linker_set.h
  • kern/Makefile.am: Added kern_mib.c and kern_sysctl.c.
  • kern/uipc_socket.c: OID changed from KERN_SOMAXCONN to KIPC_SOMAXCONN.
  • kern/uipc_socket2.c: OID changed from KERN_MAXSOCKBUF to KIPC_MAXSOCKBUF.
  • net/if_ethersubr.c: FreeBSD 2.2.2 does not have a _net_link node while 5.0 does.
  • net/if_ppp.c: Removed the TEXT_SET define as these macros are now implemented.
  • net/rtsock.c: Enable sysctl support plus fix the bug with the lastest FreeBSD sysctl header file.
  • netinet/icmp_var.h: FreeBSD 2.2.2 does not have a _net_inet_icmp node while 5.0 does.
  • netinet/if_ether.c: FreeBSD 2.2.2 does not have a _net_link_ether node while 5.0 does.
  • netinet/igmp_var.h: FreeBSD 2.2.2 does not have a _net_inet_igmp node while 5.0 does.
  • netinet/in_pcb.c: Fixed the arguments to the sysctl call. Add inp_gencnt and ipi_count. These are used when listing connections.
  • netinet/in_pcb.h: Added counters to aid the listing of connections.
  • netinet/in_var.h: Provide the _net_inet_ip and _net_inet_raw nodes.
  • netinet/ip_fw.c: Disable the firewall sysctl calls.
  • netinet/tcp_subr.c: Merge tcp_pcblist from the lastest FreeBSD source.
  • netinet/tcp_var.h: Add structures needed by net-snmp to list connections.
  • netinet/udp_usrreq.c: Merged udp_pcblist from the lastest FreeBSD source.
  • netinet/udp_var.h: Added the sysctl id UDPCTL_PCBLIST. Used by net-snmp.
  • rtems_glue.c: Call sysctl_register_all when initialising the network stack to register all the sysctl calls. These are in the special sections and required an updated linker script.
  • rtems/rtems_syscall.c: Add the sysctl call.
  • sys/kernel.h: Use the lastest FreeBSD method of handling sysctl structures. This now held in the sys/linker_set.h file.
  • sys/queue.h: This is from the lastest FreeBSD code with the circular code merged back in as it is not used in the lastest FreeBSD kernel.
  • sys/sysctl.h: The lastest sysctl. This was needed to use with the new linker set method. The FreeBSD 2.2.2 version has asm hacks. The lastest version of the FreeBSD does not have these hacks. It uses gcc attribute directives.
  • Property mode set to 100644
File size: 21.0 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 * 3. All advertising materials mentioning features or use of this software
14 *    must display the following acknowledgement:
15 *      This product includes software developed by the University of
16 *      California, Berkeley and its contributors.
17 * 4. Neither the name of the University nor the names of its contributors
18 *    may be used to endorse or promote products derived from this software
19 *    without specific prior written permission.
20 *
21 * THIS SOFTWARE IS PROVIDED BY THE REGENTS AND CONTRIBUTORS ``AS IS'' AND
22 * ANY EXPRESS OR IMPLIED WARRANTIES, INCLUDING, BUT NOT LIMITED TO, THE
23 * IMPLIED WARRANTIES OF MERCHANTABILITY AND FITNESS FOR A PARTICULAR PURPOSE
24 * ARE DISCLAIMED.  IN NO EVENT SHALL THE REGENTS OR CONTRIBUTORS BE LIABLE
25 * FOR ANY DIRECT, INDIRECT, INCIDENTAL, SPECIAL, EXEMPLARY, OR CONSEQUENTIAL
26 * DAMAGES (INCLUDING, BUT NOT LIMITED TO, PROCUREMENT OF SUBSTITUTE GOODS
27 * OR SERVICES; LOSS OF USE, DATA, OR PROFITS; OR BUSINESS INTERRUPTION)
28 * HOWEVER CAUSED AND ON ANY THEORY OF LIABILITY, WHETHER IN CONTRACT, STRICT
29 * LIABILITY, OR TORT (INCLUDING NEGLIGENCE OR OTHERWISE) ARISING IN ANY WAY
30 * OUT OF THE USE OF THIS SOFTWARE, EVEN IF ADVISED OF THE POSSIBILITY OF
31 * SUCH DAMAGE.
32 *
33 *      @(#)in_pcb.c    8.4 (Berkeley) 5/24/95
34 *      $Id$
35 */
36
37#include <sys/param.h>
38#include <sys/queue.h>
39#include <sys/systm.h>
40#include <sys/malloc.h>
41#include <sys/mbuf.h>
42#include <sys/protosw.h>
43#include <sys/socket.h>
44#include <sys/socketvar.h>
45#include <sys/ioctl.h>
46#include <sys/errno.h>
47#include <sys/time.h>
48#include <sys/proc.h>
49#include <sys/kernel.h>
50#include <sys/sysctl.h>
51
52#include <net/if.h>
53#include <net/route.h>
54
55#include <netinet/in.h>
56#include <netinet/in_systm.h>
57#include <netinet/ip.h>
58#include <netinet/in_pcb.h>
59#include <netinet/in_var.h>
60#include <netinet/ip_var.h>
61
62struct  in_addr zeroin_addr;
63
64static void      in_pcbinshash __P((struct inpcb *));
65static void      in_rtchange __P((struct inpcb *, int));
66
67/*
68 * These configure the range of local port addresses assigned to
69 * "unspecified" outgoing connections/packets/whatever.
70 */
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(so, pcbinfo)
117        struct socket *so;
118        struct inpcbinfo *pcbinfo;
119{
120        register struct inpcb *inp;
121        int s;
122
123        MALLOC(inp, struct inpcb *, sizeof(*inp), M_PCB, M_NOWAIT);
124        if (inp == NULL)
125                return (ENOBUFS);
126        bzero((caddr_t)inp, sizeof(*inp));
127        inp->inp_gencnt = ++pcbinfo->ipi_gencnt;
128        inp->inp_pcbinfo = pcbinfo;
129        inp->inp_socket = so;
130        s = splnet();
131        LIST_INSERT_HEAD(pcbinfo->listhead, inp, inp_list);
132        pcbinfo->ipi_count++;
133        in_pcbinshash(inp);
134        splx(s);
135        so->so_pcb = (caddr_t)inp;
136        return (0);
137}
138
139int
140in_pcbbind(inp, nam)
141        register struct inpcb *inp;
142        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                        if (so->so_uid) {
195                                t = in_pcblookup(inp->inp_pcbinfo, zeroin_addr,
196                                    0, sin->sin_addr, lport,
197                                    INPLOOKUP_WILDCARD);
198                                if (t && (so->so_uid != t->inp_socket->so_uid))
199                                        return (EADDRINUSE);
200                        }
201                        t = in_pcblookup(inp->inp_pcbinfo, zeroin_addr, 0,
202                            sin->sin_addr, lport, wild);
203                        if (t && (reuseport & t->inp_socket->so_options) == 0)
204                                return (EADDRINUSE);
205                }
206                inp->inp_laddr = sin->sin_addr;
207        }
208        if (lport == 0) {
209                ushort first, last;
210                int count;
211
212                inp->inp_flags |= INP_ANONPORT;
213
214                if (inp->inp_flags & INP_HIGHPORT) {
215                        first = ipport_hifirstauto;     /* sysctl */
216                        last  = ipport_hilastauto;
217                        lastport = &inp->inp_pcbinfo->lasthi;
218                } else if (inp->inp_flags & INP_LOWPORT) {
219                        if ((error = suser(p->p_ucred, &p->p_acflag)))
220                                return (EACCES);
221                        first = ipport_lowfirstauto;    /* 1023 */
222                        last  = ipport_lowlastauto;     /* 600 */
223                        lastport = &inp->inp_pcbinfo->lastlow;
224                } else {
225                        first = ipport_firstauto;       /* sysctl */
226                        last  = ipport_lastauto;
227                        lastport = &inp->inp_pcbinfo->lastport;
228                }
229                /*
230                 * Simple check to ensure all ports are not used up causing
231                 * a deadlock here.
232                 *
233                 * We split the two cases (up and down) so that the direction
234                 * is not being tested on each round of the loop.
235                 */
236                if (first > last) {
237                        /*
238                         * counting down
239                         */
240                        count = first - last;
241
242                        do {
243                                if (count-- <= 0)       /* completely used? */
244                                        return (EADDRNOTAVAIL);
245                                --*lastport;
246                                if (*lastport > first || *lastport < last)
247                                        *lastport = first;
248                                lport = htons(*lastport);
249                        } while (in_pcblookup(inp->inp_pcbinfo,
250                                 zeroin_addr, 0, inp->inp_laddr, lport, wild));
251                } else {
252                        /*
253                         * counting up
254                         */
255                        count = last - first;
256
257                        do {
258                                if (count-- <= 0)       /* completely used? */
259                                        return (EADDRNOTAVAIL);
260                                ++*lastport;
261                                if (*lastport < first || *lastport > last)
262                                        *lastport = first;
263                                lport = htons(*lastport);
264                        } while (in_pcblookup(inp->inp_pcbinfo,
265                                 zeroin_addr, 0, inp->inp_laddr, lport, wild));
266                }
267        }
268        inp->inp_lport = lport;
269        in_pcbrehash(inp);
270        return (0);
271}
272
273/*
274 *   Transform old in_pcbconnect() into an inner subroutine for new
275 *   in_pcbconnect(): Do some validity-checking on the remote
276 *   address (in mbuf 'nam') and then determine local host address
277 *   (i.e., which interface) to use to access that remote host.
278 *
279 *   This preserves definition of in_pcbconnect(), while supporting a
280 *   slightly different version for T/TCP.  (This is more than
281 *   a bit of a kludge, but cleaning up the internal interfaces would
282 *   have forced minor changes in every protocol).
283 */
284
285int
286in_pcbladdr(inp, nam, plocal_sin)
287        register struct inpcb *inp;
288        struct mbuf *nam;
289        struct sockaddr_in **plocal_sin;
290{
291        struct in_ifaddr *ia;
292        register struct sockaddr_in *sin = mtod(nam, struct sockaddr_in *);
293
294        if (nam->m_len != sizeof (*sin))
295                return (EINVAL);
296        if (sin->sin_family != AF_INET)
297                return (EAFNOSUPPORT);
298        if (sin->sin_port == 0)
299                return (EADDRNOTAVAIL);
300        if (in_ifaddr) {
301                /*
302                 * If the destination address is INADDR_ANY,
303                 * use the primary local address.
304                 * If the supplied address is INADDR_BROADCAST,
305                 * and the primary interface supports broadcast,
306                 * choose the broadcast address for that interface.
307                 */
308#define satosin(sa)     ((struct sockaddr_in *)(sa))
309#define sintosa(sin)    ((struct sockaddr *)(sin))
310#define ifatoia(ifa)    ((struct in_ifaddr *)(ifa))
311                if (sin->sin_addr.s_addr == INADDR_ANY)
312                    sin->sin_addr = IA_SIN(in_ifaddr)->sin_addr;
313                else if (sin->sin_addr.s_addr == (u_long)INADDR_BROADCAST &&
314                  (in_ifaddr->ia_ifp->if_flags & IFF_BROADCAST))
315                    sin->sin_addr = satosin(&in_ifaddr->ia_broadaddr)->sin_addr;
316        }
317        if (inp->inp_laddr.s_addr == INADDR_ANY) {
318                register struct route *ro;
319
320                ia = (struct in_ifaddr *)0;
321                /*
322                 * If route is known or can be allocated now,
323                 * our src addr is taken from the i/f, else punt.
324                 */
325                ro = &inp->inp_route;
326                if (ro->ro_rt &&
327                    (satosin(&ro->ro_dst)->sin_addr.s_addr !=
328                        sin->sin_addr.s_addr ||
329                    inp->inp_socket->so_options & SO_DONTROUTE)) {
330                        RTFREE(ro->ro_rt);
331                        ro->ro_rt = (struct rtentry *)0;
332                }
333                if ((inp->inp_socket->so_options & SO_DONTROUTE) == 0 && /*XXX*/
334                    (ro->ro_rt == (struct rtentry *)0 ||
335                    ro->ro_rt->rt_ifp == (struct ifnet *)0)) {
336                        /* No route yet, so try to acquire one */
337                        ro->ro_dst.sa_family = AF_INET;
338                        ro->ro_dst.sa_len = sizeof(struct sockaddr_in);
339                        ((struct sockaddr_in *) &ro->ro_dst)->sin_addr =
340                                sin->sin_addr;
341                        rtalloc(ro);
342                }
343                /*
344                 * If we found a route, use the address
345                 * corresponding to the outgoing interface
346                 * unless it is the loopback (in case a route
347                 * to our address on another net goes to loopback).
348                 */
349                if (ro->ro_rt && !(ro->ro_rt->rt_ifp->if_flags & IFF_LOOPBACK))
350                        ia = ifatoia(ro->ro_rt->rt_ifa);
351                if (ia == 0) {
352                        u_short fport = sin->sin_port;
353
354                        sin->sin_port = 0;
355                        ia = ifatoia(ifa_ifwithdstaddr(sintosa(sin)));
356                        if (ia == 0)
357                                ia = ifatoia(ifa_ifwithnet(sintosa(sin)));
358                        sin->sin_port = fport;
359                        if (ia == 0)
360                                ia = in_ifaddr;
361                        if (ia == 0)
362                                return (EADDRNOTAVAIL);
363                }
364                /*
365                 * If the destination address is multicast and an outgoing
366                 * interface has been set as a multicast option, use the
367                 * address of that interface as our source address.
368                 */
369                if (IN_MULTICAST(ntohl(sin->sin_addr.s_addr)) &&
370                    inp->inp_moptions != NULL) {
371                        struct ip_moptions *imo;
372                        struct ifnet *ifp;
373
374                        imo = inp->inp_moptions;
375                        if (imo->imo_multicast_ifp != NULL) {
376                                ifp = imo->imo_multicast_ifp;
377                                for (ia = in_ifaddr; ia; ia = ia->ia_next)
378                                        if (ia->ia_ifp == ifp)
379                                                break;
380                                if (ia == 0)
381                                        return (EADDRNOTAVAIL);
382                        }
383                }
384        /*
385         * Don't do pcblookup call here; return interface in plocal_sin
386         * and exit to caller, that will do the lookup.
387         */
388                *plocal_sin = &ia->ia_addr;
389
390        }
391        return(0);
392}
393
394/*
395 * Outer subroutine:
396 * Connect from a socket to a specified address.
397 * Both address and port must be specified in argument sin.
398 * If don't have a local address for this socket yet,
399 * then pick one.
400 */
401int
402in_pcbconnect(inp, nam)
403        register struct inpcb *inp;
404        struct mbuf *nam;
405{
406        struct sockaddr_in *ifaddr;
407        register struct sockaddr_in *sin = mtod(nam, struct sockaddr_in *);
408        int error;
409
410        /*
411         *   Call inner routine, to assign local interface address.
412         */
413        if ((error = in_pcbladdr(inp, nam, &ifaddr)))
414                return(error);
415
416        if (in_pcblookuphash(inp->inp_pcbinfo, sin->sin_addr, sin->sin_port,
417            inp->inp_laddr.s_addr ? inp->inp_laddr : ifaddr->sin_addr,
418            inp->inp_lport, 0) != NULL)
419                return (EADDRINUSE);
420        if (inp->inp_laddr.s_addr == INADDR_ANY) {
421                if (inp->inp_lport == 0)
422                        (void)in_pcbbind(inp, (struct mbuf *)0);
423                inp->inp_laddr = ifaddr->sin_addr;
424        }
425        inp->inp_faddr = sin->sin_addr;
426        inp->inp_fport = sin->sin_port;
427        in_pcbrehash(inp);
428        return (0);
429}
430
431void
432in_pcbdisconnect(inp)
433        struct inpcb *inp;
434{
435
436        inp->inp_faddr.s_addr = INADDR_ANY;
437        inp->inp_fport = 0;
438        in_pcbrehash(inp);
439        if (inp->inp_socket->so_state & SS_NOFDREF)
440                in_pcbdetach(inp);
441}
442
443void
444in_pcbdetach(inp)
445        struct inpcb *inp;
446{
447        struct socket *so = inp->inp_socket;
448        struct inpcbinfo *ipi = inp->inp_pcbinfo;
449        int s;
450
451        inp->inp_gencnt = ++ipi->ipi_gencnt;
452        so->so_pcb = 0;
453        sofree(so);
454        if (inp->inp_options)
455                (void)m_free(inp->inp_options);
456        if (inp->inp_route.ro_rt)
457                rtfree(inp->inp_route.ro_rt);
458        ip_freemoptions(inp->inp_moptions);
459        s = splnet();
460        LIST_REMOVE(inp, inp_hash);
461        LIST_REMOVE(inp, inp_list);
462        splx(s);
463        FREE(inp, M_PCB);
464}
465
466void
467in_setsockaddr(inp, nam)
468        register struct inpcb *inp;
469        struct mbuf *nam;
470{
471        register struct sockaddr_in *sin;
472
473        nam->m_len = sizeof (*sin);
474        sin = mtod(nam, struct sockaddr_in *);
475        bzero((caddr_t)sin, sizeof (*sin));
476        sin->sin_family = AF_INET;
477        sin->sin_len = sizeof(*sin);
478        sin->sin_port = inp->inp_lport;
479        sin->sin_addr = inp->inp_laddr;
480}
481
482void
483in_setpeeraddr(inp, nam)
484        struct inpcb *inp;
485        struct mbuf *nam;
486{
487        register struct sockaddr_in *sin;
488
489        nam->m_len = sizeof (*sin);
490        sin = mtod(nam, struct sockaddr_in *);
491        bzero((caddr_t)sin, sizeof (*sin));
492        sin->sin_family = AF_INET;
493        sin->sin_len = sizeof(*sin);
494        sin->sin_port = inp->inp_fport;
495        sin->sin_addr = inp->inp_faddr;
496}
497
498/*
499 * Pass some notification to all connections of a protocol
500 * associated with address dst.  The local address and/or port numbers
501 * may be specified to limit the search.  The "usual action" will be
502 * taken, depending on the ctlinput cmd.  The caller must filter any
503 * cmds that are uninteresting (e.g., no error in the map).
504 * Call the protocol specific routine (if any) to report
505 * any errors for each matching socket.
506 *
507 * Must be called at splnet.
508 */
509void
510in_pcbnotify(head, dst, fport_arg, laddr, lport_arg, cmd, notify)
511        struct inpcbhead *head;
512        struct sockaddr *dst;
513        u_int fport_arg, lport_arg;
514        struct in_addr laddr;
515        int cmd;
516        void (*notify) __P((struct inpcb *, int));
517{
518        register struct inpcb *inp, *oinp;
519        struct in_addr faddr;
520        u_short fport = fport_arg, lport = lport_arg;
521        int errnum, s;
522
523        if ((unsigned)cmd > PRC_NCMDS || dst->sa_family != AF_INET)
524                return;
525        faddr = ((struct sockaddr_in *)dst)->sin_addr;
526        if (faddr.s_addr == INADDR_ANY)
527                return;
528
529        /*
530         * Redirects go to all references to the destination,
531         * and use in_rtchange to invalidate the route cache.
532         * Dead host indications: notify all references to the destination.
533         * Otherwise, if we have knowledge of the local port and address,
534         * deliver only to that socket.
535         */
536        if (PRC_IS_REDIRECT(cmd) || cmd == PRC_HOSTDEAD) {
537                fport = 0;
538                lport = 0;
539                laddr.s_addr = 0;
540                if (cmd != PRC_HOSTDEAD)
541                        notify = in_rtchange;
542        }
543        errnum = inetctlerrmap[cmd];
544        s = splnet();
545        for (inp = head->lh_first; inp != NULL;) {
546                if (inp->inp_faddr.s_addr != faddr.s_addr ||
547                    inp->inp_socket == 0 ||
548                    (lport && inp->inp_lport != lport) ||
549                    (laddr.s_addr && inp->inp_laddr.s_addr != laddr.s_addr) ||
550                    (fport && inp->inp_fport != fport)) {
551                        inp = inp->inp_list.le_next;
552                        continue;
553                }
554                oinp = inp;
555                inp = inp->inp_list.le_next;
556                if (notify)
557                        (*notify)(oinp, errnum);
558        }
559        splx(s);
560}
561
562/*
563 * Check for alternatives when higher level complains
564 * about service problems.  For now, invalidate cached
565 * routing information.  If the route was created dynamically
566 * (by a redirect), time to try a default gateway again.
567 */
568void
569in_losing(inp)
570        struct inpcb *inp;
571{
572        register struct rtentry *rt;
573        struct rt_addrinfo info;
574
575        if ((rt = inp->inp_route.ro_rt)) {
576                inp->inp_route.ro_rt = 0;
577                bzero((caddr_t)&info, sizeof(info));
578                info.rti_info[RTAX_DST] =
579                        (struct sockaddr *)&inp->inp_route.ro_dst;
580                info.rti_info[RTAX_GATEWAY] = rt->rt_gateway;
581                info.rti_info[RTAX_NETMASK] = rt_mask(rt);
582                rt_missmsg(RTM_LOSING, &info, rt->rt_flags, 0);
583                if (rt->rt_flags & RTF_DYNAMIC)
584                        (void) rtrequest(RTM_DELETE, rt_key(rt),
585                                rt->rt_gateway, rt_mask(rt), rt->rt_flags,
586                                (struct rtentry **)0);
587                else
588                /*
589                 * A new route can be allocated
590                 * the next time output is attempted.
591                 */
592                        rtfree(rt);
593        }
594}
595
596/*
597 * After a routing change, flush old routing
598 * and allocate a (hopefully) better one.
599 */
600static void
601in_rtchange(inp, errnum)
602        register struct inpcb *inp;
603        int errnum;
604{
605        if (inp->inp_route.ro_rt) {
606                rtfree(inp->inp_route.ro_rt);
607                inp->inp_route.ro_rt = 0;
608                /*
609                 * A new route can be allocated the next time
610                 * output is attempted.
611                 */
612        }
613}
614
615struct inpcb *
616in_pcblookup(pcbinfo, faddr, fport_arg, laddr, lport_arg, wild_okay)
617        struct inpcbinfo *pcbinfo;
618        struct in_addr faddr, laddr;
619        u_int fport_arg, lport_arg;
620        int wild_okay;
621{
622        register struct inpcb *inp, *match = NULL;
623        int matchwild = 3, wildcard;
624        u_short fport = fport_arg, lport = lport_arg;
625        int s;
626
627        s = splnet();
628
629        for (inp = pcbinfo->listhead->lh_first; inp != NULL; inp = inp->inp_list.le_next) {
630                if (inp->inp_lport != lport)
631                        continue;
632                wildcard = 0;
633                if (inp->inp_faddr.s_addr != INADDR_ANY) {
634                        if (faddr.s_addr == INADDR_ANY)
635                                wildcard++;
636                        else if (inp->inp_faddr.s_addr != faddr.s_addr ||
637                            inp->inp_fport != fport)
638                                continue;
639                } else {
640                        if (faddr.s_addr != INADDR_ANY)
641                                wildcard++;
642                }
643                if (inp->inp_laddr.s_addr != INADDR_ANY) {
644                        if (laddr.s_addr == INADDR_ANY)
645                                wildcard++;
646                        else if (inp->inp_laddr.s_addr != laddr.s_addr)
647                                continue;
648                } else {
649                        if (laddr.s_addr != INADDR_ANY)
650                                wildcard++;
651                }
652                if (wildcard && wild_okay == 0)
653                        continue;
654                if (wildcard < matchwild) {
655                        match = inp;
656                        matchwild = wildcard;
657                        if (matchwild == 0) {
658                                break;
659                        }
660                }
661        }
662        splx(s);
663        return (match);
664}
665
666/*
667 * Lookup PCB in hash list.
668 */
669struct inpcb *
670in_pcblookuphash(pcbinfo, faddr, fport_arg, laddr, lport_arg, wildcard)
671        struct inpcbinfo *pcbinfo;
672        struct in_addr faddr, laddr;
673        u_int fport_arg, lport_arg;
674        int wildcard;
675{
676        struct inpcbhead *head;
677        register struct inpcb *inp;
678        u_short fport = fport_arg, lport = lport_arg;
679        int s;
680
681        s = splnet();
682        /*
683         * First look for an exact match.
684         */
685        head = &pcbinfo->hashbase[INP_PCBHASH(faddr.s_addr, lport, fport, pcbinfo->hashmask)];
686        for (inp = head->lh_first; inp != NULL; inp = inp->inp_hash.le_next) {
687                if (inp->inp_faddr.s_addr == faddr.s_addr &&
688                    inp->inp_laddr.s_addr == laddr.s_addr &&
689                    inp->inp_fport == fport &&
690                    inp->inp_lport == lport)
691                        goto found;
692        }
693        if (wildcard) {
694                struct inpcb *local_wild = NULL;
695
696                head = &pcbinfo->hashbase[INP_PCBHASH(INADDR_ANY, lport, 0, pcbinfo->hashmask)];
697                for (inp = head->lh_first; inp != NULL; inp = inp->inp_hash.le_next) {
698                        if (inp->inp_faddr.s_addr == INADDR_ANY &&
699                            inp->inp_fport == 0 && inp->inp_lport == lport) {
700                                if (inp->inp_laddr.s_addr == laddr.s_addr)
701                                        goto found;
702                                else if (inp->inp_laddr.s_addr == INADDR_ANY)
703                                        local_wild = inp;
704                        }
705                }
706                if (local_wild != NULL) {
707                        inp = local_wild;
708                        goto found;
709                }
710        }
711        splx(s);
712        return (NULL);
713
714found:
715        /*
716         * Move PCB to head of this hash chain so that it can be
717         * found more quickly in the future.
718         * XXX - this is a pessimization on machines with few
719         * concurrent connections.
720         */
721        if (inp != head->lh_first) {
722                LIST_REMOVE(inp, inp_hash);
723                LIST_INSERT_HEAD(head, inp, inp_hash);
724        }
725        splx(s);
726        return (inp);
727}
728
729/*
730 * Insert PCB into hash chain. Must be called at splnet.
731 */
732static void
733in_pcbinshash(inp)
734        struct inpcb *inp;
735{
736        struct inpcbhead *head;
737
738        head = &inp->inp_pcbinfo->hashbase[INP_PCBHASH(inp->inp_faddr.s_addr,
739                 inp->inp_lport, inp->inp_fport, inp->inp_pcbinfo->hashmask)];
740
741        LIST_INSERT_HEAD(head, inp, inp_hash);
742}
743
744void
745in_pcbrehash(inp)
746        struct inpcb *inp;
747{
748        struct inpcbhead *head;
749        int s;
750
751        s = splnet();
752        LIST_REMOVE(inp, inp_hash);
753
754        head = &inp->inp_pcbinfo->hashbase[INP_PCBHASH(inp->inp_faddr.s_addr,
755                inp->inp_lport, inp->inp_fport, inp->inp_pcbinfo->hashmask)];
756
757        LIST_INSERT_HEAD(head, inp, inp_hash);
758        inp->inp_pcbinfo->ipi_count--;
759        splx(s);
760}
Note: See TracBrowser for help on using the repository browser.