📄 in_pcb.c
字号:
in_pcbdetach(inp);
#ifdef IPSEC
ipsec_pcbdisconn(inp->inp_sp);
#endif
}
void
in_pcbdetach(inp)
struct inpcb *inp;
{
struct socket *so = inp->inp_socket;
struct inpcbinfo *ipi = inp->inp_pcbinfo;
struct rtentry *rt = inp->inp_route.ro_rt;
#ifdef IPSEC
ipsec4_delete_pcbpolicy(inp);
#endif /*IPSEC*/
inp->inp_gencnt = ++ipi->ipi_gencnt;
in_pcbremlists(inp);
so->so_pcb = 0;
sofree(so);
if (inp->inp_options)
(void)m_free(inp->inp_options);
if (rt) {
/*
* route deletion requires reference count to be <= zero
*/
if ((rt->rt_flags & RTF_DELCLONE) &&
(rt->rt_flags & RTF_WASCLONED) &&
(rt->rt_refcnt <= 1)) {
rt->rt_refcnt--;
rt->rt_flags &= ~RTF_UP;
rtrequest(RTM_DELETE, rt_key(rt),
rt->rt_gateway, rt_mask(rt),
rt->rt_flags, (struct rtentry **)0);
}
else
rtfree(rt);
}
ip_freemoptions(inp->inp_moptions);
inp->inp_vflag = 0;
zfreei(ipi->ipi_zone, inp);
}
/*
* The calling convention of in_setsockaddr() and in_setpeeraddr() was
* modified to match the pru_sockaddr() and pru_peeraddr() entry points
* in struct pr_usrreqs, so that protocols can just reference then directly
* without the need for a wrapper function. The socket must have a valid
* (i.e., non-nil) PCB, but it should be impossible to get an invalid one
* except through a kernel programming error, so it is acceptable to panic
* (or in this case trap) if the PCB is invalid. (Actually, we don't trap
* because there actually /is/ a programming error somewhere... XXX)
*/
int
in_setsockaddr(so, nam)
struct socket *so;
struct sockaddr **nam;
{
int s;
register struct inpcb *inp;
register struct sockaddr_in *sin;
/*
* Do the malloc first in case it blocks.
*/
MALLOC(sin, struct sockaddr_in *, sizeof *sin, M_SONAME,
M_WAITOK | M_ZERO);
sin->sin_family = AF_INET;
sin->sin_len = sizeof(*sin);
s = splnet();
inp = sotoinpcb(so);
if (!inp) {
splx(s);
free(sin, M_SONAME);
return ECONNRESET;
}
sin->sin_port = inp->inp_lport;
sin->sin_addr = inp->inp_laddr;
splx(s);
*nam = (struct sockaddr *)sin;
return 0;
}
int
in_setpeeraddr(so, nam)
struct socket *so;
struct sockaddr **nam;
{
int s;
struct inpcb *inp;
register struct sockaddr_in *sin;
/*
* Do the malloc first in case it blocks.
*/
MALLOC(sin, struct sockaddr_in *, sizeof *sin, M_SONAME,
M_WAITOK | M_ZERO);
sin->sin_family = AF_INET;
sin->sin_len = sizeof(*sin);
s = splnet();
inp = sotoinpcb(so);
if (!inp) {
splx(s);
free(sin, M_SONAME);
return ECONNRESET;
}
sin->sin_port = inp->inp_fport;
sin->sin_addr = inp->inp_faddr;
splx(s);
*nam = (struct sockaddr *)sin;
return 0;
}
void
in_pcbnotifyall(head, faddr, _errno, notify)
struct inpcbhead *head;
struct in_addr faddr;
void (*notify) __P((struct inpcb *, int));
{
struct inpcb *inp, *ninp;
int s;
s = splnet();
for (inp = LIST_FIRST(head); inp != NULL; inp = ninp) {
ninp = LIST_NEXT(inp, inp_list);
#ifdef INET6
if ((inp->inp_vflag & INP_IPV4) == 0)
continue;
#endif
if (inp->inp_faddr.s_addr != faddr.s_addr ||
inp->inp_socket == NULL)
continue;
(*notify)(inp, _errno);
}
splx(s);
}
void
in_pcbpurgeif0(head, ifp)
struct inpcb *head;
struct ifnet *ifp;
{
struct inpcb *inp;
struct ip_moptions *imo;
int i, gap;
for (inp = head; inp != NULL; inp = LIST_NEXT(inp, inp_list)) {
imo = inp->inp_moptions;
if ((inp->inp_vflag & INP_IPV4) &&
imo != NULL) {
/*
* Unselect the outgoing interface if it is being
* detached.
*/
if (imo->imo_multicast_ifp == ifp)
imo->imo_multicast_ifp = NULL;
/*
* Drop multicast group membership if we joined
* through the interface being detached.
*/
for (i = 0, gap = 0; i < imo->imo_num_memberships;
i++) {
if (imo->imo_membership[i]->inm_ifp == ifp) {
in_delmulti(imo->imo_membership[i]);
gap++;
} else if (gap != 0)
imo->imo_membership[i - gap] =
imo->imo_membership[i];
}
imo->imo_num_memberships -= gap;
}
}
}
/*
* 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)) {
bzero((caddr_t)&info, sizeof(info));
info.rti_info[RTAX_DST] =
(struct sockaddr *)&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);
inp->inp_route.ro_rt = 0;
rtfree(rt);
/*
* A new route can be allocated
* the next time output is attempted.
*/
}
}
/*
* 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.
*/
}
}
/*
* Lookup a PCB based on the local address and port.
*/
struct inpcb *
in_pcblookup_local(pcbinfo, laddr, lport_arg, wild_okay)
struct inpcbinfo *pcbinfo;
struct in_addr laddr;
u_int lport_arg;
int wild_okay;
{
register struct inpcb *inp;
int matchwild = 3, wildcard;
u_short lport = lport_arg;
if (!wild_okay) {
struct inpcbhead *head;
/*
* Look for an unconnected (wildcard foreign addr) PCB that
* matches the local address and port we're looking for.
*/
head = &pcbinfo->hashbase[INP_PCBHASH(INADDR_ANY, lport, 0, pcbinfo->hashmask)];
LIST_FOREACH(inp, head, inp_hash) {
#ifdef INET6
if ((inp->inp_vflag & INP_IPV4) == 0)
continue;
#endif
if (inp->inp_faddr.s_addr == INADDR_ANY &&
inp->inp_laddr.s_addr == laddr.s_addr &&
inp->inp_lport == lport) {
/*
* Found.
*/
return (inp);
}
}
/*
* Not found.
*/
return (NULL);
} else {
struct inpcbporthead *porthash;
struct inpcbport *phd;
struct inpcb *match = NULL;
/*
* Best fit PCB lookup.
*
* First see if this local port is in use by looking on the
* port hash list.
*/
porthash = &pcbinfo->porthashbase[INP_PCBPORTHASH(lport,
pcbinfo->porthashmask)];
LIST_FOREACH(phd, porthash, phd_hash) {
if (phd->phd_port == lport)
break;
}
if (phd != NULL) {
/*
* Port is in use by one or more PCBs. Look for best
* fit.
*/
LIST_FOREACH(inp, &phd->phd_pcblist, inp_portlist) {
wildcard = 0;
#ifdef INET6
if ((inp->inp_vflag & INP_IPV4) == 0)
continue;
#endif
if (inp->inp_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++;
}
if (wildcard < matchwild) {
match = inp;
matchwild = wildcard;
if (matchwild == 0) {
break;
}
}
}
}
return (match);
}
}
/*
* Lookup PCB in hash list.
*/
struct inpcb *
in_pcblookup_hash(pcbinfo, faddr, fport_arg, laddr, lport_arg, wildcard,
ifp)
struct inpcbinfo *pcbinfo;
struct in_addr faddr, laddr;
u_int fport_arg, lport_arg;
int wildcard;
struct ifnet *ifp;
{
struct inpcbhead *head;
register struct inpcb *inp;
u_short fport = fport_arg, lport = lport_arg;
/*
* First look for an exact match.
*/
head = &pcbinfo->hashbase[INP_PCBHASH(faddr.s_addr, lport, fport, pcbinfo->hashmask)];
LIST_FOREACH(inp, head, inp_hash) {
#ifdef INET6
if ((inp->inp_vflag & INP_IPV4) == 0)
continue;
#endif
if (inp->inp_faddr.s_addr == faddr.s_addr &&
inp->inp_laddr.s_addr == laddr.s_addr &&
inp->inp_fport == fport &&
inp->inp_lport == lport) {
/*
* Found.
*/
return (inp);
}
}
if (wildcard) {
struct inpcb *local_wild = NULL;
#if defined(INET6)
struct inpcb *local_wild_mapped = NULL;
#endif /* defined(INET6) */
head = &pcbinfo->hashbase[INP_PCBHASH(INADDR_ANY, lport, 0, pcbinfo->hashmask)];
LIST_FOREACH(inp, head, inp_hash) {
#ifdef INET6
if ((inp->inp_vflag & INP_IPV4) == 0)
continue;
#endif
if (inp->inp_faddr.s_addr == INADDR_ANY &&
inp->inp_lport == lport) {
#if defined(NFAITH) && NFAITH > 0
if (ifp && ifp->if_type == IFT_FAITH &&
(inp->inp_flags & INP_FAITH) == 0)
continue;
#endif
if (inp->inp_laddr.s_addr == laddr.s_addr)
return (inp);
else if (inp->inp_laddr.s_addr == INADDR_ANY) {
#if defined(INET6)
if (INP_CHECK_SOCKAF(inp->inp_socket,
AF_INET6))
local_wild_mapped = inp;
else
#endif /* defined(INET6) */
local_wild = inp;
}
}
}
#if defined(INET6)
if (local_wild == NULL)
return (local_wild_mapped);
#endif /* defined(INET6) */
return (local_wild);
}
/*
* Not found.
*/
return (NULL);
}
/*
* Insert PCB onto various hash lists.
*/
int
in_pcbinshash(inp)
struct inpcb *inp;
{
struct inpcbhead *pcbhash;
struct inpcbporthead *pcbporthash;
struct inpcbinfo *pcbinfo = inp->inp_pcbinfo;
struct inpcbport *phd;
u_int32_t hashkey_faddr;
#ifdef INET6
if (inp->inp_vflag & INP_IPV6)
hashkey_faddr = inp->in6p_faddr.s6_addr32[3] /* XXX */;
else
#endif /* INET6 */
hashkey_faddr = inp->inp_faddr.s_addr;
pcbhash = &pcbinfo->hashbase[INP_PCBHASH(hashkey_faddr,
inp->inp_lport, inp->inp_fport, pcbinfo->hashmask)];
pcbporthash = &pcbinfo->porthashbase[INP_PCBPORTHASH(inp->inp_lport,
pcbinfo->porthashmask)];
/*
* Go through port list and look for a head for this lport.
*/
LIST_FOREACH(phd, pcbporthash, phd_hash) {
if (phd->phd_port == inp->inp_lport)
break;
}
/*
* If none exists, malloc one and tack it on.
*/
if (phd == NULL) {
MALLOC(phd, struct inpcbport *, sizeof(struct inpcbport), M_PCB, M_NOWAIT);
if (phd == NULL) {
return (ENOBUFS); /* XXX */
}
phd->phd_port = inp->inp_lport;
LIST_INIT(&phd->phd_pcblist);
LIST_INSERT_HEAD(pcbporthash, phd, phd_hash);
}
inp->inp_phd = phd;
LIST_INSERT_HEAD(&phd->phd_pcblist, inp, inp_portlist);
LIST_INSERT_HEAD(pcbhash, inp, inp_hash);
return (0);
}
/*
* Move PCB to the proper hash bucket when { faddr, fport } have been
* changed. NOTE: This does not handle the case of the lport changing (the
* hashed port list would have to be updated as well), so the lport must
* not change after in_pcbinshash() has been called.
*/
void
in_pcbrehash(inp)
struct inpcb *inp;
{
struct inpcbhead *head;
u_int32_t hashkey_faddr;
#ifdef INET6
if (inp->inp_vflag & INP_IPV6)
hashkey_faddr = inp->in6p_faddr.s6_addr32[3] /* XXX */;
else
#endif /* INET6 */
hashkey_faddr = inp->inp_faddr.s_addr;
head = &inp->inp_pcbinfo->hashbase[INP_PCBHASH(hashkey_faddr,
inp->inp_lport, inp->inp_fport, inp->inp_pcbinfo->hashmask)];
LIST_REMOVE(inp, inp_hash);
LIST_INSERT_HEAD(head, inp, inp_hash);
}
/*
* Remove PCB from various lists.
*/
void
in_pcbremlists(inp)
struct inpcb *inp;
{
inp->inp_gencnt = ++inp->inp_pcbinfo->ipi_gencnt;
if (inp->inp_lport) {
struct inpcbport *phd = inp->inp_phd;
LIST_REMOVE(inp, inp_hash);
LIST_REMOVE(inp, inp_portlist);
if (LIST_FIRST(&phd->phd_pcblist) == NULL) {
LIST_REMOVE(phd, phd_hash);
free(phd, M_PCB);
}
}
LIST_REMOVE(inp, inp_list);
inp->inp_pcbinfo->ipi_count--;
}
⌨️ 快捷键说明
复制代码
Ctrl + C
搜索代码
Ctrl + F
全屏模式
F11
切换主题
Ctrl + Shift + D
显示快捷键
?
增大字号
Ctrl + =
减小字号
Ctrl + -