📄 in6_pcb.c
字号:
*/
void
in6_pcbnotify(head, dst, fport_arg, src, lport_arg, cmd, cmdarg, notify)
struct inpcbhead *head;
struct sockaddr *dst, *src;
u_int fport_arg, lport_arg;
int cmd;
void *cmdarg;
void (*notify) __P((struct inpcb *, int));
{
struct inpcb *inp, *ninp;
struct sockaddr_in6 sa6_src, *sa6_dst;
u_short fport = fport_arg, lport = lport_arg;
u_int32_t flowinfo;
int _errno, s;
if ((unsigned)cmd > PRC_NCMDS || dst->sa_family != AF_INET6)
return;
sa6_dst = (struct sockaddr_in6 *)dst;
if (IN6_IS_ADDR_UNSPECIFIED(&sa6_dst->sin6_addr))
return;
/*
* note that src can be NULL when we get notify by local fragmentation.
*/
sa6_src = (src == NULL) ? sa6_any : *(struct sockaddr_in6 *)src;
flowinfo = sa6_src.sin6_flowinfo;
/*
* Redirects go to all references to the destination,
* and use in6_rtchange to invalidate the route cache.
* Dead host indications: also use in6_rtchange to invalidate
* the cache, and deliver the error to all the sockets.
* Otherwise, if we have knowledge of the local port and address,
* deliver only to that socket.
*/
if (PRC_IS_REDIRECT(cmd) || cmd == PRC_HOSTDEAD) {
fport = 0;
lport = 0;
bzero((caddr_t)&sa6_src.sin6_addr, sizeof(sa6_src.sin6_addr));
if (cmd != PRC_HOSTDEAD)
notify = in6_rtchange;
}
_errno = inet6ctlerrmap[cmd];
s = splnet();
for (inp = LIST_FIRST(head); inp != NULL; inp = ninp) {
ninp = LIST_NEXT(inp, inp_list);
if ((inp->inp_vflag & INP_IPV6) == 0)
continue;
/*
* If the error designates a new path MTU for a destination
* and the application (associated with this socket) wanted to
* know the value, notify. Note that we notify for all
* disconnected sockets if the corresponding application
* wanted. This is because some UDP applications keep sending
* sockets disconnected.
* XXX: should we avoid to notify the value to TCP sockets?
*/
if (cmd == PRC_MSGSIZE && (inp->inp_flags & IN6P_MTU) != 0 &&
(IN6_IS_ADDR_UNSPECIFIED(&inp->in6p_faddr) ||
IN6_ARE_ADDR_EQUAL(&inp->in6p_faddr,
&sa6_dst->sin6_addr))) {
ip6_notify_pmtu(inp, (struct sockaddr_in6 *)dst,
(u_int32_t *)cmdarg);
}
/*
* Detect if we should notify the error. If no source and
* destination ports are specifed, but non-zero flowinfo and
* local address match, notify the error. This is the case
* when the error is delivered with an encrypted buffer
* by ESP. Otherwise, just compare addresses and ports
* as usual.
*/
if (lport == 0 && fport == 0 && flowinfo &&
inp->inp_socket != NULL &&
flowinfo == (inp->in6p_flowinfo & IPV6_FLOWLABEL_MASK) &&
IN6_ARE_ADDR_EQUAL(&inp->in6p_laddr, &sa6_src.sin6_addr))
goto do_notify;
else if (!IN6_ARE_ADDR_EQUAL(&inp->in6p_faddr,
&sa6_dst->sin6_addr) ||
inp->inp_socket == 0 ||
(lport && inp->inp_lport != lport) ||
(!IN6_IS_ADDR_UNSPECIFIED(&sa6_src.sin6_addr) &&
!IN6_ARE_ADDR_EQUAL(&inp->in6p_laddr,
&sa6_src.sin6_addr)) ||
(fport && inp->inp_fport != fport))
continue;
do_notify:
if (notify)
(*notify)(inp, _errno);
}
splx(s);
}
/*
* Lookup a PCB based on the local address and port.
*/
struct inpcb *
in6_pcblookup_local(pcbinfo, laddr, lport_arg, wild_okay)
struct inpcbinfo *pcbinfo;
struct in6_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) {
if ((inp->inp_vflag & INP_IPV6) == 0)
continue;
if (IN6_IS_ADDR_UNSPECIFIED(&inp->in6p_faddr) &&
IN6_ARE_ADDR_EQUAL(&inp->in6p_laddr, laddr) &&
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;
if ((inp->inp_vflag & INP_IPV6) == 0)
continue;
if (!IN6_IS_ADDR_UNSPECIFIED(&inp->in6p_faddr))
wildcard++;
if (!IN6_IS_ADDR_UNSPECIFIED(
&inp->in6p_laddr)) {
if (IN6_IS_ADDR_UNSPECIFIED(laddr))
wildcard++;
else if (!IN6_ARE_ADDR_EQUAL(
&inp->in6p_laddr, laddr))
continue;
} else {
if (!IN6_IS_ADDR_UNSPECIFIED(laddr))
wildcard++;
}
if (wildcard < matchwild) {
match = inp;
matchwild = wildcard;
if (matchwild == 0) {
break;
}
}
}
}
return (match);
}
}
void
in6_pcbpurgeif0(head, ifp)
struct in6pcb *head;
struct ifnet *ifp;
{
struct in6pcb *in6p;
struct ip6_moptions *im6o;
struct in6_multi_mship *imm, *nimm;
for (in6p = head; in6p != NULL; in6p = LIST_NEXT(in6p, inp_list)) {
im6o = in6p->in6p_moptions;
if ((in6p->inp_vflag & INP_IPV6) &&
im6o) {
/*
* Unselect the outgoing interface if it is being
* detached.
*/
if (im6o->im6o_multicast_ifp == ifp)
im6o->im6o_multicast_ifp = NULL;
/*
* Drop multicast group membership if we joined
* through the interface being detached.
* XXX controversial - is it really legal for kernel
* to force this?
*/
for (imm = im6o->im6o_memberships.lh_first;
imm != NULL; imm = nimm) {
nimm = imm->i6mm_chain.le_next;
if (imm->i6mm_maddr->in6m_ifp == ifp) {
LIST_REMOVE(imm, i6mm_chain);
in6_delmulti(imm->i6mm_maddr);
free(imm, M_IPMADDR);
}
}
}
}
}
/*
* 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
in6_losing(in6p)
struct inpcb *in6p;
{
struct rtentry *rt;
struct rt_addrinfo info;
if ((rt = in6p->in6p_route.ro_rt) != NULL) {
bzero((caddr_t)&info, sizeof(info));
info.rti_info[RTAX_DST] =
(struct sockaddr *)&in6p->in6p_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);
in6p->in6p_route.ro_rt = NULL;
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
in6_rtchange(inp, _errno)
struct inpcb *inp;
int _errno;
{
if (inp->in6p_route.ro_rt) {
rtfree(inp->in6p_route.ro_rt);
inp->in6p_route.ro_rt = 0;
/*
* A new route can be allocated the next time
* output is attempted.
*/
}
}
/*
* Lookup PCB in hash list.
*/
struct inpcb *
in6_pcblookup_hash(pcbinfo, faddr, fport_arg, laddr, lport_arg, wildcard, ifp)
struct inpcbinfo *pcbinfo;
struct in6_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;
int faith;
#if defined(NFAITH) && NFAITH > 0
faith = faithprefix(laddr);
#else
faith = 0;
#endif
/*
* First look for an exact match.
*/
head = &pcbinfo->hashbase[INP_PCBHASH(faddr->s6_addr32[3] /* XXX */,
lport, fport,
pcbinfo->hashmask)];
LIST_FOREACH(inp, head, inp_hash) {
if ((inp->inp_vflag & INP_IPV6) == 0)
continue;
if (IN6_ARE_ADDR_EQUAL(&inp->in6p_faddr, faddr) &&
IN6_ARE_ADDR_EQUAL(&inp->in6p_laddr, laddr) &&
inp->inp_fport == fport &&
inp->inp_lport == lport) {
/*
* Found.
*/
return (inp);
}
}
if (wildcard) {
struct inpcb *local_wild = NULL;
head = &pcbinfo->hashbase[INP_PCBHASH(INADDR_ANY, lport, 0,
pcbinfo->hashmask)];
LIST_FOREACH(inp, head, inp_hash) {
if ((inp->inp_vflag & INP_IPV6) == 0)
continue;
if (IN6_IS_ADDR_UNSPECIFIED(&inp->in6p_faddr) &&
inp->inp_lport == lport) {
if (faith && (inp->inp_flags & INP_FAITH) == 0)
continue;
if (IN6_ARE_ADDR_EQUAL(&inp->in6p_laddr,
laddr))
return (inp);
else if (IN6_IS_ADDR_UNSPECIFIED(&inp->in6p_laddr))
local_wild = inp;
}
}
return (local_wild);
}
/*
* Not found.
*/
return (NULL);
}
void
init_sin6(struct sockaddr_in6 *sin6, struct mbuf *m)
{
struct ip6_hdr *ip;
ip = mtod(m, struct ip6_hdr *);
bzero(sin6, sizeof(*sin6));
sin6->sin6_len = sizeof(*sin6);
sin6->sin6_family = AF_INET6;
sin6->sin6_addr = ip->ip6_src;
if (IN6_IS_SCOPE_LINKLOCAL(&sin6->sin6_addr))
sin6->sin6_addr.s6_addr16[1] = 0;
sin6->sin6_scope_id =
(m->m_pkthdr.rcvif && IN6_IS_SCOPE_LINKLOCAL(&sin6->sin6_addr))
? m->m_pkthdr.rcvif->if_index : 0;
return;
}
⌨️ 快捷键说明
复制代码
Ctrl + C
搜索代码
Ctrl + F
全屏模式
F11
切换主题
Ctrl + Shift + D
显示快捷键
?
增大字号
Ctrl + =
减小字号
Ctrl + -