You can not select more than 25 topics
Topics must start with a letter or number, can include dashes ('-') and can be up to 35 characters long.
927 lines
25 KiB
927 lines
25 KiB
/* $OpenBSD: in_pcb.c,v 1.34 1999/05/16 21:48:29 niklas Exp $ */
|
|
/* $NetBSD: in_pcb.c,v 1.25 1996/02/13 23:41:53 christos Exp $ */
|
|
|
|
/*
|
|
* Copyright (c) 1982, 1986, 1991, 1993
|
|
* The Regents of the University of California. All rights reserved.
|
|
*
|
|
* Redistribution and use in source and binary forms, with or without
|
|
* modification, are permitted provided that the following conditions
|
|
* are met:
|
|
* 1. Redistributions of source code must retain the above copyright
|
|
* notice, this list of conditions and the following disclaimer.
|
|
* 2. Redistributions in binary form must reproduce the above copyright
|
|
* notice, this list of conditions and the following disclaimer in the
|
|
* documentation and/or other materials provided with the distribution.
|
|
* 3. All advertising materials mentioning features or use of this software
|
|
* must display the following acknowledgement:
|
|
* This product includes software developed by the University of
|
|
* California, Berkeley and its contributors.
|
|
* 4. Neither the name of the University nor the names of its contributors
|
|
* may be used to endorse or promote products derived from this software
|
|
* without specific prior written permission.
|
|
*
|
|
* THIS SOFTWARE IS PROVIDED BY THE REGENTS AND CONTRIBUTORS ``AS IS'' AND
|
|
* ANY EXPRESS OR IMPLIED WARRANTIES, INCLUDING, BUT NOT LIMITED TO, THE
|
|
* IMPLIED WARRANTIES OF MERCHANTABILITY AND FITNESS FOR A PARTICULAR PURPOSE
|
|
* ARE DISCLAIMED. IN NO EVENT SHALL THE REGENTS OR CONTRIBUTORS BE LIABLE
|
|
* FOR ANY DIRECT, INDIRECT, INCIDENTAL, SPECIAL, EXEMPLARY, OR CONSEQUENTIAL
|
|
* DAMAGES (INCLUDING, BUT NOT LIMITED TO, PROCUREMENT OF SUBSTITUTE GOODS
|
|
* OR SERVICES; LOSS OF USE, DATA, OR PROFITS; OR BUSINESS INTERRUPTION)
|
|
* HOWEVER CAUSED AND ON ANY THEORY OF LIABILITY, WHETHER IN CONTRACT, STRICT
|
|
* LIABILITY, OR TORT (INCLUDING NEGLIGENCE OR OTHERWISE) ARISING IN ANY WAY
|
|
* OUT OF THE USE OF THIS SOFTWARE, EVEN IF ADVISED OF THE POSSIBILITY OF
|
|
* SUCH DAMAGE.
|
|
*
|
|
* @(#)in_pcb.c 8.2 (Berkeley) 1/4/94
|
|
*/
|
|
|
|
/*
|
|
%%% portions-copyright-nrl-95
|
|
Portions of this software are Copyright 1995-1998 by Randall Atkinson,
|
|
Ronald Lee, Daniel McDonald, Bao Phan, and Chris Winters. All Rights
|
|
Reserved. All rights under this copyright have been assigned to the US
|
|
Naval Research Laboratory (NRL). The NRL Copyright Notice and License
|
|
Agreement Version 1.1 (January 17, 1995) applies to these portions of the
|
|
software.
|
|
You should have received a copy of the license with this software. If you
|
|
didn't get a copy, you may request one from <license@ipv6.nrl.navy.mil>.
|
|
*/
|
|
|
|
#include <sys/param.h>
|
|
#include <sys/systm.h>
|
|
#include <sys/malloc.h>
|
|
#include <sys/mbuf.h>
|
|
#include <sys/protosw.h>
|
|
#include <sys/socket.h>
|
|
#include <sys/socketvar.h>
|
|
#include <sys/ioctl.h>
|
|
#include <sys/errno.h>
|
|
#include <sys/time.h>
|
|
#include <sys/proc.h>
|
|
#include <sys/domain.h>
|
|
|
|
#include <net/if.h>
|
|
#include <net/route.h>
|
|
|
|
#include <netinet/in.h>
|
|
#include <netinet/in_systm.h>
|
|
#include <netinet/ip.h>
|
|
#include <netinet/in_pcb.h>
|
|
#include <netinet/in_var.h>
|
|
#include <netinet/ip_var.h>
|
|
#if 0
|
|
#include <dev/rndvar.h>
|
|
#endif
|
|
|
|
#ifdef INET6
|
|
#include <netinet6/ipv6_var.h>
|
|
#endif /* INET6 */
|
|
|
|
#ifdef IPSEC
|
|
#include <netinet/ip_ipsp.h>
|
|
|
|
extern int check_ipsec_policy __P((struct inpcb *, u_int32_t));
|
|
#endif
|
|
|
|
struct in_addr zeroin_addr;
|
|
|
|
extern int ipsec_auth_default_level;
|
|
extern int ipsec_esp_trans_default_level;
|
|
extern int ipsec_esp_network_default_level;
|
|
|
|
/*
|
|
* These configure the range of local port addresses assigned to
|
|
* "unspecified" outgoing connections/packets/whatever.
|
|
*/
|
|
int ipport_firstauto = IPPORT_RESERVED; /* 1024 */
|
|
int ipport_lastauto = IPPORT_USERRESERVED; /* 5000 */
|
|
int ipport_hifirstauto = IPPORT_HIFIRSTAUTO; /* 40000 */
|
|
int ipport_hilastauto = IPPORT_HILASTAUTO; /* 44999 */
|
|
|
|
#define INPCBHASH(table, faddr, fport, laddr, lport) \
|
|
&(table)->inpt_hashtbl[(ntohl((faddr)->s_addr) + \
|
|
ntohs((fport)) + ntohs((lport))) & (table->inpt_hash)]
|
|
|
|
#ifdef INET6
|
|
#define IN6PCBHASH(table, faddr, fport, laddr, lport) \
|
|
&(table)->inpt_hashtbl[(ntohl((faddr)->s6_addr32[0] ^ \
|
|
(faddr)->s6_addr32[3]) + ntohs((fport)) + ntohs((lport))) & \
|
|
(table->inpt_hash)]
|
|
#endif
|
|
|
|
void
|
|
in_pcbinit(table, hashsize)
|
|
struct inpcbtable *table;
|
|
int hashsize;
|
|
{
|
|
|
|
CIRCLEQ_INIT(&table->inpt_queue);
|
|
table->inpt_hashtbl = hashinit(hashsize, M_PCB, M_WAITOK, &table->inpt_hash);
|
|
table->inpt_lastport = 0;
|
|
}
|
|
|
|
struct baddynamicports baddynamicports;
|
|
|
|
/*
|
|
* Check if the specified port is invalid for dynamic allocation.
|
|
*/
|
|
int
|
|
in_baddynamic(port, proto)
|
|
u_int16_t port;
|
|
u_int16_t proto;
|
|
{
|
|
|
|
if (port < IPPORT_RESERVED/2 || port >= IPPORT_RESERVED)
|
|
return(0);
|
|
|
|
switch (proto) {
|
|
case IPPROTO_TCP:
|
|
return (DP_ISSET(baddynamicports.tcp, port));
|
|
case IPPROTO_UDP:
|
|
return (DP_ISSET(baddynamicports.udp, port));
|
|
default:
|
|
return (0);
|
|
}
|
|
}
|
|
|
|
int
|
|
in_pcballoc(so, v)
|
|
struct socket *so;
|
|
void *v;
|
|
{
|
|
struct inpcbtable *table = v;
|
|
register struct inpcb *inp;
|
|
int s;
|
|
|
|
MALLOC(inp, struct inpcb *, sizeof(*inp), M_PCB, M_NOWAIT);
|
|
if (inp == NULL)
|
|
return (ENOBUFS);
|
|
bzero((caddr_t)inp, sizeof(*inp));
|
|
inp->inp_table = table;
|
|
inp->inp_socket = so;
|
|
inp->inp_seclevel[SL_AUTH] = ipsec_auth_default_level;
|
|
inp->inp_seclevel[SL_ESP_TRANS] = ipsec_esp_trans_default_level;
|
|
inp->inp_seclevel[SL_ESP_NETWORK] = ipsec_esp_network_default_level;
|
|
s = splnet();
|
|
CIRCLEQ_INSERT_HEAD(&table->inpt_queue, inp, inp_queue);
|
|
LIST_INSERT_HEAD(INPCBHASH(table, &inp->inp_faddr, inp->inp_fport,
|
|
&inp->inp_laddr, inp->inp_lport), inp, inp_hash);
|
|
splx(s);
|
|
so->so_pcb = inp;
|
|
|
|
#ifdef INET6
|
|
/*
|
|
* Small change in this function to set the INP_IPV6 flag so routines
|
|
* outside pcb-specific routines don't need to use sotopf(), and all
|
|
* of it's pointer chasing, later.
|
|
*/
|
|
if (sotopf(so) == PF_INET6)
|
|
inp->inp_flags = INP_IPV6;
|
|
inp->inp_csumoffset = -1;
|
|
#endif /* INET6 */
|
|
return (0);
|
|
}
|
|
|
|
int
|
|
in_pcbbind(v, nam)
|
|
register void *v;
|
|
struct mbuf *nam;
|
|
{
|
|
register struct inpcb *inp = v;
|
|
register struct socket *so = inp->inp_socket;
|
|
register struct inpcbtable *table = inp->inp_table;
|
|
u_int16_t *lastport = &inp->inp_table->inpt_lastport;
|
|
register struct sockaddr_in *sin;
|
|
struct proc *p = curproc; /* XXX */
|
|
u_int16_t lport = 0;
|
|
int wild = 0, reuseport = (so->so_options & SO_REUSEPORT);
|
|
int error;
|
|
|
|
#ifdef INET6
|
|
if (sotopf(so) == PF_INET6)
|
|
return in6_pcbbind(inp, nam);
|
|
#endif /* INET6 */
|
|
|
|
if (in_ifaddr.tqh_first == 0)
|
|
return (EADDRNOTAVAIL);
|
|
if (inp->inp_lport || inp->inp_laddr.s_addr != INADDR_ANY)
|
|
return (EINVAL);
|
|
if ((so->so_options & (SO_REUSEADDR|SO_REUSEPORT)) == 0 &&
|
|
((so->so_proto->pr_flags & PR_CONNREQUIRED) == 0 ||
|
|
(so->so_options & SO_ACCEPTCONN) == 0))
|
|
wild = INPLOOKUP_WILDCARD;
|
|
if (nam) {
|
|
sin = mtod(nam, struct sockaddr_in *);
|
|
if (nam->m_len != sizeof (*sin))
|
|
return (EINVAL);
|
|
#ifdef notdef
|
|
/*
|
|
* We should check the family, but old programs
|
|
* incorrectly fail to initialize it.
|
|
*/
|
|
if (sin->sin_family != AF_INET)
|
|
return (EAFNOSUPPORT);
|
|
#endif
|
|
lport = sin->sin_port;
|
|
if (IN_MULTICAST(sin->sin_addr.s_addr)) {
|
|
/*
|
|
* Treat SO_REUSEADDR as SO_REUSEPORT for multicast;
|
|
* allow complete duplication of binding if
|
|
* SO_REUSEPORT is set, or if SO_REUSEADDR is set
|
|
* and a multicast address is bound on both
|
|
* new and duplicated sockets.
|
|
*/
|
|
if (so->so_options & SO_REUSEADDR)
|
|
reuseport = SO_REUSEADDR|SO_REUSEPORT;
|
|
} else if (sin->sin_addr.s_addr != INADDR_ANY) {
|
|
sin->sin_port = 0; /* yech... */
|
|
if (in_iawithaddr(sin->sin_addr, NULL) == 0)
|
|
return (EADDRNOTAVAIL);
|
|
}
|
|
if (lport) {
|
|
struct inpcb *t;
|
|
|
|
/* GROSS */
|
|
if (ntohs(lport) < IPPORT_RESERVED &&
|
|
(error = suser(p->p_ucred, &p->p_acflag)))
|
|
return (EACCES);
|
|
if (so->so_euid) {
|
|
t = in_pcblookup(table, &zeroin_addr, 0,
|
|
&sin->sin_addr, lport, INPLOOKUP_WILDCARD);
|
|
if (t && (so->so_euid != t->inp_socket->so_euid))
|
|
return (EADDRINUSE);
|
|
}
|
|
t = in_pcblookup(table, &zeroin_addr, 0,
|
|
&sin->sin_addr, lport, wild);
|
|
if (t && (reuseport & t->inp_socket->so_options) == 0)
|
|
return (EADDRINUSE);
|
|
}
|
|
inp->inp_laddr = sin->sin_addr;
|
|
}
|
|
if (lport == 0) {
|
|
u_int16_t first, last, old = 0;
|
|
int count;
|
|
int loopcount = 0;
|
|
|
|
if (inp->inp_flags & INP_HIGHPORT) {
|
|
first = ipport_hifirstauto; /* sysctl */
|
|
last = ipport_hilastauto;
|
|
} else if (inp->inp_flags & INP_LOWPORT) {
|
|
if ((error = suser(p->p_ucred, &p->p_acflag)))
|
|
return (EACCES);
|
|
first = IPPORT_RESERVED-1; /* 1023 */
|
|
last = 600; /* not IPPORT_RESERVED/2 */
|
|
} else {
|
|
first = ipport_firstauto; /* sysctl */
|
|
last = ipport_lastauto;
|
|
}
|
|
|
|
/*
|
|
* Simple check to ensure all ports are not used up causing
|
|
* a deadlock here.
|
|
*
|
|
* We split the two cases (up and down) so that the direction
|
|
* is not being tested on each round of the loop.
|
|
*/
|
|
|
|
portloop:
|
|
if (first > last) {
|
|
/*
|
|
* counting down
|
|
*/
|
|
if (loopcount == 0) { /* only do this once. */
|
|
old = first;
|
|
first -= (arc4random() % (first - last));
|
|
}
|
|
count = first - last;
|
|
*lastport = first; /* restart each time */
|
|
|
|
do {
|
|
if (count-- <= 0) { /* completely used? */
|
|
if (loopcount == 0) {
|
|
last = old;
|
|
loopcount++;
|
|
goto portloop;
|
|
}
|
|
return (EADDRNOTAVAIL);
|
|
}
|
|
--*lastport;
|
|
if (*lastport > first || *lastport < last)
|
|
*lastport = first;
|
|
lport = htons(*lastport);
|
|
} while (in_baddynamic(*lastport, so->so_proto->pr_protocol) ||
|
|
in_pcblookup(table, &zeroin_addr, 0,
|
|
&inp->inp_laddr, lport, wild));
|
|
} else {
|
|
/*
|
|
* counting up
|
|
*/
|
|
if (loopcount == 0) { /* only do this once. */
|
|
old = first;
|
|
first += (arc4random() % (last - first));
|
|
}
|
|
count = last - first;
|
|
*lastport = first; /* restart each time */
|
|
|
|
do {
|
|
if (count-- <= 0) { /* completely used? */
|
|
if (loopcount == 0) {
|
|
first = old;
|
|
loopcount++;
|
|
goto portloop;
|
|
}
|
|
return (EADDRNOTAVAIL);
|
|
}
|
|
++*lastport;
|
|
if (*lastport < first || *lastport > last)
|
|
*lastport = first;
|
|
lport = htons(*lastport);
|
|
} while (in_baddynamic(*lastport, so->so_proto->pr_protocol) ||
|
|
in_pcblookup(table, &zeroin_addr, 0,
|
|
&inp->inp_laddr, lport, wild));
|
|
}
|
|
}
|
|
inp->inp_lport = lport;
|
|
in_pcbrehash(inp);
|
|
return (0);
|
|
}
|
|
|
|
/*
|
|
* Connect from a socket to a specified address.
|
|
* Both address and port must be specified in argument sin.
|
|
* If don't have a local address for this socket yet,
|
|
* then pick one.
|
|
*/
|
|
int
|
|
in_pcbconnect(v, nam)
|
|
register void *v;
|
|
struct mbuf *nam;
|
|
{
|
|
register struct inpcb *inp = v;
|
|
struct in_ifaddr *ia;
|
|
struct sockaddr_in *ifaddr = NULL;
|
|
register struct sockaddr_in *sin = mtod(nam, struct sockaddr_in *);
|
|
struct sockaddr_in *sin2;
|
|
|
|
#ifdef INET6
|
|
if (sotopf(inp->inp_socket) == PF_INET6)
|
|
return (in6_pcbconnect(inp, nam));
|
|
#endif /* INET6 */
|
|
|
|
if (nam->m_len != sizeof (*sin))
|
|
return (EINVAL);
|
|
if (sin->sin_family != AF_INET)
|
|
return (EAFNOSUPPORT);
|
|
if (sin->sin_port == 0)
|
|
return (EADDRNOTAVAIL);
|
|
if (in_ifaddr.tqh_first != 0) {
|
|
/*
|
|
* If the destination address is INADDR_ANY,
|
|
* use the primary local address.
|
|
* If the supplied address is INADDR_BROADCAST,
|
|
* and the primary interface supports broadcast,
|
|
* choose the broadcast address for that interface.
|
|
*/
|
|
if (sin->sin_addr.s_addr == INADDR_ANY)
|
|
sin->sin_addr = in_ifaddr.tqh_first->ia_addr.sin_addr;
|
|
else if (sin->sin_addr.s_addr == INADDR_BROADCAST &&
|
|
(in_ifaddr.tqh_first->ia_ifp->if_flags & IFF_BROADCAST))
|
|
sin->sin_addr = in_ifaddr.tqh_first->ia_broadaddr.sin_addr;
|
|
}
|
|
if (inp->inp_laddr.s_addr == INADDR_ANY) {
|
|
register struct route *ro;
|
|
|
|
ia = (struct in_ifaddr *)0;
|
|
/*
|
|
* If route is known or can be allocated now,
|
|
* our src addr is taken from the i/f, else punt.
|
|
*/
|
|
ro = &inp->inp_route;
|
|
if (ro->ro_rt &&
|
|
(satosin(&ro->ro_dst)->sin_addr.s_addr !=
|
|
sin->sin_addr.s_addr ||
|
|
inp->inp_socket->so_options & SO_DONTROUTE)) {
|
|
RTFREE(ro->ro_rt);
|
|
ro->ro_rt = (struct rtentry *)0;
|
|
}
|
|
if ((inp->inp_socket->so_options & SO_DONTROUTE) == 0 && /*XXX*/
|
|
(ro->ro_rt == (struct rtentry *)0 ||
|
|
ro->ro_rt->rt_ifp == (struct ifnet *)0)) {
|
|
/* No route yet, so try to acquire one */
|
|
ro->ro_dst.sa_family = AF_INET;
|
|
ro->ro_dst.sa_len = sizeof(struct sockaddr_in);
|
|
satosin(&ro->ro_dst)->sin_addr = sin->sin_addr;
|
|
rtalloc(ro);
|
|
|
|
/*
|
|
* It is important to bzero out the rest of the
|
|
* struct sockaddr_in when mixing v6 & v4!
|
|
*/
|
|
sin2 = (struct sockaddr_in *)&ro->ro_dst;
|
|
bzero(sin2->sin_zero, sizeof(sin2->sin_zero));
|
|
}
|
|
/*
|
|
* If we found a route, use the address
|
|
* corresponding to the outgoing interface
|
|
* unless it is the loopback (in case a route
|
|
* to our address on another net goes to loopback).
|
|
*/
|
|
if (ro->ro_rt && !(ro->ro_rt->rt_ifp->if_flags & IFF_LOOPBACK))
|
|
ia = ifatoia(ro->ro_rt->rt_ifa);
|
|
if (ia == 0) {
|
|
u_int16_t fport = sin->sin_port;
|
|
|
|
sin->sin_port = 0;
|
|
ia = ifatoia(ifa_ifwithdstaddr(sintosa(sin)));
|
|
if (ia == 0)
|
|
ia = ifatoia(ifa_ifwithnet(sintosa(sin)));
|
|
sin->sin_port = fport;
|
|
if (ia == 0)
|
|
ia = in_ifaddr.tqh_first;
|
|
if (ia == 0)
|
|
return (EADDRNOTAVAIL);
|
|
}
|
|
/*
|
|
* If the destination address is multicast and an outgoing
|
|
* interface has been set as a multicast option, use the
|
|
* address of that interface as our source address.
|
|
*/
|
|
if (IN_MULTICAST(sin->sin_addr.s_addr) &&
|
|
#ifdef INET6
|
|
inp->inp_moptions != NULL &&
|
|
!(inp->inp_flags & INP_IPV6_MCAST)) {
|
|
#else
|
|
inp->inp_moptions != NULL) {
|
|
#endif
|
|
struct ip_moptions *imo;
|
|
struct ifnet *ifp;
|
|
|
|
imo = inp->inp_moptions;
|
|
if (imo->imo_multicast_ifp != NULL) {
|
|
ifp = imo->imo_multicast_ifp;
|
|
for (ia = in_ifaddr.tqh_first; ia != 0;
|
|
ia = ia->ia_list.tqe_next)
|
|
if (ia->ia_ifp == ifp)
|
|
break;
|
|
if (ia == 0)
|
|
return (EADDRNOTAVAIL);
|
|
}
|
|
}
|
|
ifaddr = satosin(&ia->ia_addr);
|
|
}
|
|
if (in_pcbhashlookup(inp->inp_table, sin->sin_addr, sin->sin_port,
|
|
inp->inp_laddr.s_addr ? inp->inp_laddr : ifaddr->sin_addr,
|
|
inp->inp_lport) != 0)
|
|
return (EADDRINUSE);
|
|
if (inp->inp_laddr.s_addr == INADDR_ANY) {
|
|
if (inp->inp_lport == 0 &&
|
|
in_pcbbind(inp, (struct mbuf *)0) == EADDRNOTAVAIL)
|
|
return (EADDRNOTAVAIL);
|
|
inp->inp_laddr = ifaddr->sin_addr;
|
|
}
|
|
inp->inp_faddr = sin->sin_addr;
|
|
inp->inp_fport = sin->sin_port;
|
|
in_pcbrehash(inp);
|
|
#ifdef IPSEC
|
|
return (check_ipsec_policy(inp, 0));
|
|
#else
|
|
return (0);
|
|
#endif
|
|
}
|
|
|
|
void
|
|
in_pcbdisconnect(v)
|
|
void *v;
|
|
{
|
|
struct inpcb *inp = v;
|
|
|
|
#ifdef INET6
|
|
if (sotopf(inp->inp_socket) == PF_INET6) {
|
|
inp->inp_faddr6 = in6addr_any;
|
|
/* Disconnected AF_INET6 sockets cannot be "v4-mapped" */
|
|
inp->inp_flags &= ~INP_IPV6_MAPPED;
|
|
} else
|
|
#endif
|
|
inp->inp_faddr.s_addr = INADDR_ANY;
|
|
|
|
inp->inp_fport = 0;
|
|
in_pcbrehash(inp);
|
|
if (inp->inp_socket->so_state & SS_NOFDREF)
|
|
in_pcbdetach(inp);
|
|
}
|
|
|
|
void
|
|
in_pcbdetach(v)
|
|
void *v;
|
|
{
|
|
struct inpcb *inp = v;
|
|
struct socket *so = inp->inp_socket;
|
|
int s;
|
|
|
|
so->so_pcb = 0;
|
|
sofree(so);
|
|
if (inp->inp_options)
|
|
(void)m_freem(inp->inp_options);
|
|
if (inp->inp_route.ro_rt)
|
|
rtfree(inp->inp_route.ro_rt);
|
|
#ifdef NOTUSED_BY_PMON
|
|
#ifdef INET6
|
|
if (inp->inp_flags & INP_IPV6_MCAST)
|
|
ipv6_freemoptions(inp->inp_moptions6);
|
|
else
|
|
#endif /* INET6 */
|
|
ip_freemoptions(inp->inp_moptions);
|
|
#endif /* NOTUSED_BY_PMON */
|
|
#ifdef IPSEC
|
|
/* XXX IPsec cleanup here */
|
|
s = spltdb();
|
|
if (inp->inp_tdb)
|
|
TAILQ_REMOVE(&inp->inp_tdb->tdb_inp, inp, inp_tdb_next);
|
|
splx(s);
|
|
#endif
|
|
s = splnet();
|
|
LIST_REMOVE(inp, inp_hash);
|
|
CIRCLEQ_REMOVE(&inp->inp_table->inpt_queue, inp, inp_queue);
|
|
splx(s);
|
|
FREE(inp, M_PCB);
|
|
}
|
|
|
|
void
|
|
in_setsockaddr(inp, nam)
|
|
register struct inpcb *inp;
|
|
struct mbuf *nam;
|
|
{
|
|
register struct sockaddr_in *sin;
|
|
|
|
nam->m_len = sizeof (*sin);
|
|
sin = mtod(nam, struct sockaddr_in *);
|
|
bzero((caddr_t)sin, sizeof (*sin));
|
|
sin->sin_family = AF_INET;
|
|
sin->sin_len = sizeof(*sin);
|
|
sin->sin_port = inp->inp_lport;
|
|
sin->sin_addr = inp->inp_laddr;
|
|
}
|
|
|
|
void
|
|
in_setpeeraddr(inp, nam)
|
|
struct inpcb *inp;
|
|
struct mbuf *nam;
|
|
{
|
|
register struct sockaddr_in *sin;
|
|
|
|
#ifdef INET6
|
|
if (sotopf(inp->inp_socket) == PF_INET6)
|
|
in6_setpeeraddr(inp, nam);
|
|
#endif /* INET6 */
|
|
|
|
nam->m_len = sizeof (*sin);
|
|
sin = mtod(nam, struct sockaddr_in *);
|
|
bzero((caddr_t)sin, sizeof (*sin));
|
|
sin->sin_family = AF_INET;
|
|
sin->sin_len = sizeof(*sin);
|
|
sin->sin_port = inp->inp_fport;
|
|
sin->sin_addr = inp->inp_faddr;
|
|
}
|
|
|
|
/*
|
|
* Pass some notification to all connections of a protocol
|
|
* associated with address dst. The local address and/or port numbers
|
|
* may be specified to limit the search. The "usual action" will be
|
|
* taken, depending on the ctlinput cmd. The caller must filter any
|
|
* cmds that are uninteresting (e.g., no error in the map).
|
|
* Call the protocol specific routine (if any) to report
|
|
* any errors for each matching socket.
|
|
*
|
|
* Must be called at splsoftnet.
|
|
*/
|
|
void
|
|
in_pcbnotify(table, dst, fport_arg, laddr, lport_arg, errno, notify)
|
|
struct inpcbtable *table;
|
|
struct sockaddr *dst;
|
|
u_int fport_arg, lport_arg;
|
|
struct in_addr laddr;
|
|
int errno;
|
|
void (*notify) __P((struct inpcb *, int));
|
|
{
|
|
register struct inpcb *inp, *oinp;
|
|
struct in_addr faddr;
|
|
u_int16_t fport = fport_arg, lport = lport_arg;
|
|
|
|
#ifdef INET6
|
|
/*
|
|
* See in6_pcbnotify() for IPv6 codepath. By the time this
|
|
* gets called, the addresses passed are either definitely IPv4 or
|
|
* IPv6; *_pcbnotify() never gets called with v4-mapped v6 addresses.
|
|
*/
|
|
#endif /* INET6 */
|
|
|
|
if (dst->sa_family != AF_INET)
|
|
return;
|
|
faddr = satosin(dst)->sin_addr;
|
|
if (faddr.s_addr == INADDR_ANY)
|
|
return;
|
|
|
|
for (inp = table->inpt_queue.cqh_first;
|
|
inp != (struct inpcb *)&table->inpt_queue;) {
|
|
if (inp->inp_faddr.s_addr != faddr.s_addr ||
|
|
inp->inp_socket == 0 ||
|
|
inp->inp_fport != fport ||
|
|
inp->inp_lport != lport ||
|
|
inp->inp_laddr.s_addr != laddr.s_addr) {
|
|
inp = inp->inp_queue.cqe_next;
|
|
continue;
|
|
}
|
|
oinp = inp;
|
|
inp = inp->inp_queue.cqe_next;
|
|
if (notify)
|
|
(*notify)(oinp, errno);
|
|
}
|
|
}
|
|
|
|
void
|
|
in_pcbnotifyall(table, dst, errno, notify)
|
|
struct inpcbtable *table;
|
|
struct sockaddr *dst;
|
|
int errno;
|
|
void (*notify) __P((struct inpcb *, int));
|
|
{
|
|
register struct inpcb *inp, *oinp;
|
|
struct in_addr faddr;
|
|
|
|
#ifdef INET6
|
|
/*
|
|
* See in6_pcbnotify() for IPv6 codepath. By the time this
|
|
* gets called, the addresses passed are either definitely IPv4 or
|
|
* IPv6; *_pcbnotify() never gets called with v4-mapped v6 addresses.
|
|
*/
|
|
#endif /* INET6 */
|
|
|
|
if (dst->sa_family != AF_INET)
|
|
return;
|
|
faddr = satosin(dst)->sin_addr;
|
|
if (faddr.s_addr == INADDR_ANY)
|
|
return;
|
|
|
|
for (inp = table->inpt_queue.cqh_first;
|
|
inp != (struct inpcb *)&table->inpt_queue;) {
|
|
if (inp->inp_faddr.s_addr != faddr.s_addr ||
|
|
inp->inp_socket == 0) {
|
|
inp = inp->inp_queue.cqe_next;
|
|
continue;
|
|
}
|
|
oinp = inp;
|
|
inp = inp->inp_queue.cqe_next;
|
|
if (notify)
|
|
(*notify)(oinp, errno);
|
|
}
|
|
}
|
|
|
|
/*
|
|
* Check for alternatives when higher level complains
|
|
* about service problems. For now, invalidate cached
|
|
* routing information. If the route was created dynamically
|
|
* (by a redirect), time to try a default gateway again.
|
|
*/
|
|
void
|
|
in_losing(inp)
|
|
struct inpcb *inp;
|
|
{
|
|
register struct rtentry *rt;
|
|
struct rt_addrinfo info;
|
|
|
|
if ((rt = inp->inp_route.ro_rt)) {
|
|
inp->inp_route.ro_rt = 0;
|
|
bzero((caddr_t)&info, sizeof(info));
|
|
info.rti_info[RTAX_DST] = &inp->inp_route.ro_dst;
|
|
info.rti_info[RTAX_GATEWAY] = rt->rt_gateway;
|
|
info.rti_info[RTAX_NETMASK] = rt_mask(rt);
|
|
rt_missmsg(RTM_LOSING, &info, rt->rt_flags, 0);
|
|
if (rt->rt_flags & RTF_DYNAMIC)
|
|
(void) rtrequest(RTM_DELETE, rt_key(rt),
|
|
rt->rt_gateway, rt_mask(rt), rt->rt_flags,
|
|
(struct rtentry **)0);
|
|
else
|
|
/*
|
|
* A new route can be allocated
|
|
* the next time output is attempted.
|
|
*/
|
|
rtfree(rt);
|
|
}
|
|
}
|
|
|
|
/*
|
|
* After a routing change, flush old routing
|
|
* and allocate a (hopefully) better one.
|
|
*/
|
|
void
|
|
in_rtchange(inp, errno)
|
|
register struct inpcb *inp;
|
|
int errno;
|
|
{
|
|
if (inp->inp_route.ro_rt) {
|
|
rtfree(inp->inp_route.ro_rt);
|
|
inp->inp_route.ro_rt = 0;
|
|
/*
|
|
* A new route can be allocated the next time
|
|
* output is attempted.
|
|
*/
|
|
}
|
|
}
|
|
|
|
struct inpcb *
|
|
in_pcblookup(table, faddrp, fport_arg, laddrp, lport_arg, flags)
|
|
struct inpcbtable *table;
|
|
void *faddrp, *laddrp;
|
|
u_int fport_arg, lport_arg;
|
|
int flags;
|
|
{
|
|
register struct inpcb *inp, *match = 0;
|
|
int matchwild = 3, wildcard;
|
|
u_int16_t fport = fport_arg, lport = lport_arg;
|
|
struct in_addr faddr = *(struct in_addr *)faddrp;
|
|
struct in_addr laddr = *(struct in_addr *)laddrp;
|
|
|
|
for (inp = table->inpt_queue.cqh_first;
|
|
inp != (struct inpcb *)&table->inpt_queue;
|
|
inp = inp->inp_queue.cqe_next) {
|
|
if (inp->inp_lport != lport)
|
|
continue;
|
|
wildcard = 0;
|
|
#ifdef INET6
|
|
if (flags & INPLOOKUP_IPV6) {
|
|
struct in6_addr *laddr6 = (struct in6_addr *)laddrp;
|
|
struct in6_addr *faddr6 = (struct in6_addr *)faddrp;
|
|
|
|
/*
|
|
* Always skip AF_INET sockets when looking for AF_INET6
|
|
* addresses. The only problem with this comes if the
|
|
* PF_INET6 addresses are v4-mapped addresses. From what
|
|
* I've been able to see, none of the callers cause such
|
|
* a situation to occur. If such a situation DID occur,
|
|
* then it is possible to miss a matching PCB.
|
|
*/
|
|
if (!(inp->inp_flags & INP_IPV6))
|
|
continue;
|
|
|
|
if (!IN6_IS_ADDR_UNSPECIFIED(&inp->inp_laddr6)) {
|
|
if (IN6_IS_ADDR_UNSPECIFIED(laddr6))
|
|
wildcard++;
|
|
else if (!IN6_ARE_ADDR_EQUAL(&inp->inp_laddr6, laddr6))
|
|
continue;
|
|
} else {
|
|
if (!IN6_IS_ADDR_UNSPECIFIED(laddr6))
|
|
wildcard++;
|
|
}
|
|
|
|
if (!IN6_IS_ADDR_UNSPECIFIED(&inp->inp_faddr6)) {
|
|
if (IN6_IS_ADDR_UNSPECIFIED(faddr6))
|
|
wildcard++;
|
|
else if (!IN6_ARE_ADDR_EQUAL(&inp->inp_faddr6,
|
|
faddr6) || inp->inp_fport != fport)
|
|
continue;
|
|
} else {
|
|
if (!IN6_IS_ADDR_UNSPECIFIED(faddr6))
|
|
wildcard++;
|
|
}
|
|
} else {
|
|
#endif /* INET6 */
|
|
if (inp->inp_faddr.s_addr != INADDR_ANY) {
|
|
if (faddr.s_addr == INADDR_ANY)
|
|
wildcard++;
|
|
else if (inp->inp_faddr.s_addr != faddr.s_addr ||
|
|
inp->inp_fport != fport)
|
|
continue;
|
|
} else {
|
|
if (faddr.s_addr != INADDR_ANY)
|
|
wildcard++;
|
|
}
|
|
if (inp->inp_laddr.s_addr != INADDR_ANY) {
|
|
if (laddr.s_addr == INADDR_ANY)
|
|
wildcard++;
|
|
else if (inp->inp_laddr.s_addr != laddr.s_addr)
|
|
continue;
|
|
} else {
|
|
if (laddr.s_addr != INADDR_ANY)
|
|
wildcard++;
|
|
}
|
|
#ifdef INET6
|
|
}
|
|
#endif /* INET6 */
|
|
if ((!wildcard || (flags & INPLOOKUP_WILDCARD)) &&
|
|
wildcard < matchwild) {
|
|
match = inp;
|
|
if ((matchwild = wildcard) == 0)
|
|
break;
|
|
}
|
|
}
|
|
return (match);
|
|
}
|
|
|
|
void
|
|
in_pcbrehash(inp)
|
|
struct inpcb *inp;
|
|
{
|
|
struct inpcbtable *table = inp->inp_table;
|
|
int s;
|
|
|
|
s = splnet();
|
|
LIST_REMOVE(inp, inp_hash);
|
|
#ifdef INET6
|
|
if (inp->inp_flags & INP_IPV6) {
|
|
LIST_INSERT_HEAD(IN6PCBHASH(table, &inp->inp_faddr6,
|
|
inp->inp_fport, &inp->inp_laddr6, inp->inp_lport),
|
|
inp, inp_hash);
|
|
} else {
|
|
#endif /* INET6 */
|
|
LIST_INSERT_HEAD(INPCBHASH(table, &inp->inp_faddr,
|
|
inp->inp_fport, &inp->inp_laddr, inp->inp_lport),
|
|
inp, inp_hash);
|
|
#ifdef INET6
|
|
}
|
|
#endif /* INET6 */
|
|
splx(s);
|
|
}
|
|
|
|
#ifdef DIAGNOSTIC
|
|
int in_pcbnotifymiss = 0;
|
|
#endif
|
|
|
|
struct inpcb *
|
|
in_pcbhashlookup(table, faddr, fport_arg, laddr, lport_arg)
|
|
struct inpcbtable *table;
|
|
struct in_addr faddr, laddr;
|
|
u_int fport_arg, lport_arg;
|
|
{
|
|
struct inpcbhead *head;
|
|
register struct inpcb *inp;
|
|
u_int16_t fport = fport_arg, lport = lport_arg;
|
|
|
|
head = INPCBHASH(table, &faddr, fport, &laddr, lport);
|
|
for (inp = head->lh_first; inp != NULL; inp = inp->inp_hash.le_next) {
|
|
if (inp->inp_faddr.s_addr == faddr.s_addr &&
|
|
inp->inp_fport == fport &&
|
|
inp->inp_lport == lport &&
|
|
inp->inp_laddr.s_addr == laddr.s_addr) {
|
|
/*
|
|
* Move this PCB to the head of hash chain so that
|
|
* repeated accesses are quicker. This is analogous to
|
|
* the historic single-entry PCB cache.
|
|
*/
|
|
if (inp != head->lh_first) {
|
|
LIST_REMOVE(inp, inp_hash);
|
|
LIST_INSERT_HEAD(head, inp, inp_hash);
|
|
}
|
|
break;
|
|
}
|
|
}
|
|
#ifdef DIAGNOSTIC
|
|
if (inp == NULL && in_pcbnotifymiss) {
|
|
printf("in_pcbhashlookup: faddr=%08x fport=%d laddr=%08x lport=%d\n",
|
|
ntohl(faddr.s_addr), ntohs(fport),
|
|
ntohl(laddr.s_addr), ntohs(lport));
|
|
}
|
|
#endif
|
|
return (inp);
|
|
}
|
|
|
|
#ifdef INET6
|
|
struct inpcb *
|
|
in6_pcbhashlookup(table, faddr, fport_arg, laddr, lport_arg)
|
|
struct inpcbtable *table;
|
|
struct in6_addr *faddr, *laddr;
|
|
u_int fport_arg, lport_arg;
|
|
{
|
|
struct inpcbhead *head;
|
|
register struct inpcb *inp;
|
|
u_int16_t fport = fport_arg, lport = lport_arg;
|
|
|
|
head = IN6PCBHASH(table, faddr, fport, laddr, lport);
|
|
for (inp = head->lh_first; inp != NULL; inp = inp->inp_hash.le_next) {
|
|
if (!(inp->inp_flags & INP_IPV6))
|
|
continue;
|
|
if (IN6_ARE_ADDR_EQUAL(&inp->inp_faddr6, faddr) &&
|
|
inp->inp_fport == fport && inp->inp_lport == lport &&
|
|
IN6_ARE_ADDR_EQUAL(&inp->inp_laddr6, laddr)) {
|
|
/*
|
|
* Move this PCB to the head of hash chain so that
|
|
* repeated accesses are quicker. This is analogous to
|
|
* the historic single-entry PCB cache.
|
|
*/
|
|
if (inp != head->lh_first) {
|
|
LIST_REMOVE(inp, inp_hash);
|
|
LIST_INSERT_HEAD(head, inp, inp_hash);
|
|
}
|
|
break;
|
|
}
|
|
}
|
|
#ifdef DIAGNOSTIC
|
|
if (inp == NULL && in_pcbnotifymiss) {
|
|
printf("in6_pcblookup_connect: faddr=");
|
|
printf(" fport=%d laddr=", ntohs(fport));
|
|
printf(" lport=%d\n", ntohs(lport));
|
|
}
|
|
#endif
|
|
return (inp);
|
|
}
|
|
#endif /* INET6 */
|
|
|