Changeset View
Changeset View
Standalone View
Standalone View
sys/netinet6/in6_pcb.c
Show First 20 Lines • Show All 119 Lines • ▼ Show 20 Lines | |||||
{ | { | ||||
struct socket *so = inp->inp_socket; | struct socket *so = inp->inp_socket; | ||||
struct sockaddr_in6 *sin6 = (struct sockaddr_in6 *)NULL; | struct sockaddr_in6 *sin6 = (struct sockaddr_in6 *)NULL; | ||||
struct inpcbinfo *pcbinfo = inp->inp_pcbinfo; | struct inpcbinfo *pcbinfo = inp->inp_pcbinfo; | ||||
u_short lport = 0; | u_short lport = 0; | ||||
int error, lookupflags = 0; | int error, lookupflags = 0; | ||||
int reuseport = (so->so_options & SO_REUSEPORT); | int reuseport = (so->so_options & SO_REUSEPORT); | ||||
/* | |||||
* XXX: Maybe we could let SO_REUSEPORT_LB set SO_REUSEPORT bit here | |||||
* so that we don't have to add to the (already messy) code below. | |||||
*/ | |||||
int reuseport_lb = (so->so_options & SO_REUSEPORT_LB); | |||||
INP_WLOCK_ASSERT(inp); | INP_WLOCK_ASSERT(inp); | ||||
INP_HASH_WLOCK_ASSERT(pcbinfo); | INP_HASH_WLOCK_ASSERT(pcbinfo); | ||||
if (TAILQ_EMPTY(&V_in6_ifaddrhead)) /* XXX broken! */ | if (TAILQ_EMPTY(&V_in6_ifaddrhead)) /* XXX broken! */ | ||||
return (EADDRNOTAVAIL); | return (EADDRNOTAVAIL); | ||||
if (inp->inp_lport || !IN6_IS_ADDR_UNSPECIFIED(&inp->in6p_laddr)) | if (inp->inp_lport || !IN6_IS_ADDR_UNSPECIFIED(&inp->in6p_laddr)) | ||||
return (EINVAL); | return (EINVAL); | ||||
if ((so->so_options & (SO_REUSEADDR|SO_REUSEPORT)) == 0) | if ((so->so_options & (SO_REUSEADDR|SO_REUSEPORT|SO_REUSEPORT_LB)) == 0) | ||||
lookupflags = INPLOOKUP_WILDCARD; | lookupflags = INPLOOKUP_WILDCARD; | ||||
if (nam == NULL) { | if (nam == NULL) { | ||||
if ((error = prison_local_ip6(cred, &inp->in6p_laddr, | if ((error = prison_local_ip6(cred, &inp->in6p_laddr, | ||||
((inp->inp_flags & IN6P_IPV6_V6ONLY) != 0))) != 0) | ((inp->inp_flags & IN6P_IPV6_V6ONLY) != 0))) != 0) | ||||
return (error); | return (error); | ||||
} else { | } else { | ||||
sin6 = (struct sockaddr_in6 *)nam; | sin6 = (struct sockaddr_in6 *)nam; | ||||
if (nam->sa_len != sizeof(*sin6)) | if (nam->sa_len != sizeof(*sin6)) | ||||
Show All 17 Lines | if (IN6_IS_ADDR_MULTICAST(&sin6->sin6_addr)) { | ||||
* Treat SO_REUSEADDR as SO_REUSEPORT for multicast; | * Treat SO_REUSEADDR as SO_REUSEPORT for multicast; | ||||
* allow compepte duplication of binding if | * allow compepte duplication of binding if | ||||
* SO_REUSEPORT is set, or if SO_REUSEADDR is set | * SO_REUSEPORT is set, or if SO_REUSEADDR is set | ||||
* and a multicast address is bound on both | * and a multicast address is bound on both | ||||
* new and duplicated sockets. | * new and duplicated sockets. | ||||
*/ | */ | ||||
if ((so->so_options & (SO_REUSEADDR|SO_REUSEPORT)) != 0) | if ((so->so_options & (SO_REUSEADDR|SO_REUSEPORT)) != 0) | ||||
reuseport = SO_REUSEADDR|SO_REUSEPORT; | reuseport = SO_REUSEADDR|SO_REUSEPORT; | ||||
/* | |||||
* XXX: How to deal with SO_REUSEPORT_LB here? | |||||
rwatson: Use a C-style comment. Comment should be a full sentence. | |||||
* Treat same as SO_REUSEPORT for now. | |||||
*/ | |||||
if ((so->so_options & | |||||
(SO_REUSEADDR|SO_REUSEPORT_LB)) != 0) | |||||
reuseport_lb = SO_REUSEADDR|SO_REUSEPORT_LB; | |||||
} else if (!IN6_IS_ADDR_UNSPECIFIED(&sin6->sin6_addr)) { | } else if (!IN6_IS_ADDR_UNSPECIFIED(&sin6->sin6_addr)) { | ||||
struct ifaddr *ifa; | struct ifaddr *ifa; | ||||
sin6->sin6_port = 0; /* yech... */ | sin6->sin6_port = 0; /* yech... */ | ||||
if ((ifa = ifa_ifwithaddr((struct sockaddr *)sin6)) == | if ((ifa = ifa_ifwithaddr((struct sockaddr *)sin6)) == | ||||
NULL && | NULL && | ||||
(inp->inp_flags & INP_BINDANY) == 0) { | (inp->inp_flags & INP_BINDANY) == 0) { | ||||
return (EADDRNOTAVAIL); | return (EADDRNOTAVAIL); | ||||
Show All 32 Lines | if (lport) { | ||||
INPLOOKUP_WILDCARD, cred); | INPLOOKUP_WILDCARD, cred); | ||||
if (t && | if (t && | ||||
((inp->inp_flags2 & INP_BINDMULTI) == 0) && | ((inp->inp_flags2 & INP_BINDMULTI) == 0) && | ||||
((t->inp_flags & INP_TIMEWAIT) == 0) && | ((t->inp_flags & INP_TIMEWAIT) == 0) && | ||||
(so->so_type != SOCK_STREAM || | (so->so_type != SOCK_STREAM || | ||||
IN6_IS_ADDR_UNSPECIFIED(&t->in6p_faddr)) && | IN6_IS_ADDR_UNSPECIFIED(&t->in6p_faddr)) && | ||||
(!IN6_IS_ADDR_UNSPECIFIED(&sin6->sin6_addr) || | (!IN6_IS_ADDR_UNSPECIFIED(&sin6->sin6_addr) || | ||||
!IN6_IS_ADDR_UNSPECIFIED(&t->in6p_laddr) || | !IN6_IS_ADDR_UNSPECIFIED(&t->in6p_laddr) || | ||||
(t->inp_flags2 & INP_REUSEPORT) == 0) && | (t->inp_flags2 & INP_REUSEPORT) || | ||||
(t->inp_flags2 & INP_REUSEPORT_LB) == 0) && | |||||
(inp->inp_cred->cr_uid != | (inp->inp_cred->cr_uid != | ||||
t->inp_cred->cr_uid)) | t->inp_cred->cr_uid)) | ||||
return (EADDRINUSE); | return (EADDRINUSE); | ||||
/* | /* | ||||
* If the socket is a BINDMULTI socket, then | * If the socket is a BINDMULTI socket, then | ||||
* the credentials need to match and the | * the credentials need to match and the | ||||
* original socket also has to have been bound | * original socket also has to have been bound | ||||
Show All 33 Lines | #endif | ||||
/* | /* | ||||
* XXXRW: If an incpb has had its timewait | * XXXRW: If an incpb has had its timewait | ||||
* state recycled, we treat the address as | * state recycled, we treat the address as | ||||
* being in use (for now). This is better | * being in use (for now). This is better | ||||
* than a panic, but not desirable. | * than a panic, but not desirable. | ||||
*/ | */ | ||||
tw = intotw(t); | tw = intotw(t); | ||||
if (tw == NULL || | if (tw == NULL || | ||||
(reuseport & tw->tw_so_options) == 0) | ((reuseport & tw->tw_so_options) == 0 && | ||||
(reuseport_lb & tw->tw_so_options) == 0)) | |||||
return (EADDRINUSE); | return (EADDRINUSE); | ||||
} else if (t && (reuseport & inp_so_options(t)) == 0) { | } else if (t && (reuseport & inp_so_options(t)) == 0 && | ||||
(reuseport_lb & inp_so_options(t)) == 0) { | |||||
return (EADDRINUSE); | return (EADDRINUSE); | ||||
} | } | ||||
#ifdef INET | #ifdef INET | ||||
if ((inp->inp_flags & IN6P_IPV6_V6ONLY) == 0 && | if ((inp->inp_flags & IN6P_IPV6_V6ONLY) == 0 && | ||||
IN6_IS_ADDR_UNSPECIFIED(&sin6->sin6_addr)) { | IN6_IS_ADDR_UNSPECIFIED(&sin6->sin6_addr)) { | ||||
struct sockaddr_in sin; | struct sockaddr_in sin; | ||||
in6_sin6_2_sin(&sin, sin6); | in6_sin6_2_sin(&sin, sin6); | ||||
t = in_pcblookup_local(pcbinfo, sin.sin_addr, | t = in_pcblookup_local(pcbinfo, sin.sin_addr, | ||||
lport, lookupflags, cred); | lport, lookupflags, cred); | ||||
if (t && t->inp_flags & INP_TIMEWAIT) { | if (t && t->inp_flags & INP_TIMEWAIT) { | ||||
tw = intotw(t); | tw = intotw(t); | ||||
if (tw == NULL) | if (tw == NULL) | ||||
return (EADDRINUSE); | return (EADDRINUSE); | ||||
if ((reuseport & tw->tw_so_options) == 0 | if ((reuseport & tw->tw_so_options) == 0 | ||||
&& (reuseport_lb & tw->tw_so_options) == 0 | |||||
&& (ntohl(t->inp_laddr.s_addr) != | && (ntohl(t->inp_laddr.s_addr) != | ||||
INADDR_ANY || ((inp->inp_vflag & | INADDR_ANY || ((inp->inp_vflag & | ||||
INP_IPV6PROTO) == | INP_IPV6PROTO) == | ||||
(t->inp_vflag & INP_IPV6PROTO)))) | (t->inp_vflag & INP_IPV6PROTO)))) | ||||
return (EADDRINUSE); | return (EADDRINUSE); | ||||
} else if (t && | } else if (t && | ||||
(reuseport & inp_so_options(t)) == 0 && | (reuseport & inp_so_options(t)) == 0 && | ||||
(reuseport_lb & inp_so_options(t)) == 0 && | |||||
(ntohl(t->inp_laddr.s_addr) != INADDR_ANY || | (ntohl(t->inp_laddr.s_addr) != INADDR_ANY || | ||||
(t->inp_vflag & INP_IPV6PROTO) != 0)) | (t->inp_vflag & INP_IPV6PROTO) != 0)) { | ||||
return (EADDRINUSE); | return (EADDRINUSE); | ||||
} | } | ||||
} | |||||
#endif | #endif | ||||
} | } | ||||
inp->in6p_laddr = sin6->sin6_addr; | inp->in6p_laddr = sin6->sin6_addr; | ||||
} | } | ||||
if (lport == 0) { | if (lport == 0) { | ||||
if ((error = in6_pcbsetport(&inp->in6p_laddr, inp, cred)) != 0) { | if ((error = in6_pcbsetport(&inp->in6p_laddr, inp, cred)) != 0) { | ||||
/* Undo an address bind that may have occurred. */ | /* Undo an address bind that may have occurred. */ | ||||
inp->in6p_laddr = in6addr_any; | inp->in6p_laddr = in6addr_any; | ||||
▲ Show 20 Lines • Show All 546 Lines • ▼ Show 20 Lines | if (inp->inp_route6.ro_rt) { | ||||
RTFREE(inp->inp_route6.ro_rt); | RTFREE(inp->inp_route6.ro_rt); | ||||
inp->inp_route6.ro_rt = (struct rtentry *)NULL; | inp->inp_route6.ro_rt = (struct rtentry *)NULL; | ||||
} | } | ||||
if (inp->inp_route.ro_lle) | if (inp->inp_route.ro_lle) | ||||
LLE_FREE(inp->inp_route.ro_lle); /* zeros ro_lle */ | LLE_FREE(inp->inp_route.ro_lle); /* zeros ro_lle */ | ||||
return inp; | return inp; | ||||
} | } | ||||
static struct inpcb * | |||||
in6_pcblookup_lbgroup(const struct inpcbinfo *pcbinfo, | |||||
const struct in6_addr *laddr, uint16_t lport, const struct in6_addr *faddr, | |||||
uint16_t fport, int lookupflags) | |||||
{ | |||||
struct inpcb *local_wild = NULL; | |||||
const struct inpcblbgrouphead *hdr; | |||||
struct inpcblbgroup *grp; | |||||
struct inpcblbgroup *grp_local_wild; | |||||
uint32_t idx; | |||||
INP_HASH_LOCK_ASSERT(pcbinfo); | |||||
hdr = &pcbinfo->ipi_lbgrouphashbase[INP_PCBLBGROUP_PORTHASH( | |||||
lport, pcbinfo->ipi_lbgrouphashmask)]; | |||||
/* | |||||
* Order of socket selection: | |||||
* 1. non-wild. | |||||
* 2. wild (if lookupflags contains INPLOOKUP_WILDCARD). | |||||
* | |||||
* NOTE: | |||||
* - Load balanced group does not contain jailed sockets. | |||||
* - Load balanced does not contain IPv4 mapped INET6 wild sockets. | |||||
*/ | |||||
LIST_FOREACH(grp, hdr, il_list) { | |||||
if (grp->il_lport == lport) { | |||||
idx = 0; | |||||
int pkt_hash = INP_PCBLBGROUP_PKTHASH( | |||||
Done Inline Actionsstyle(9) prefers that these variables be declared above rather than in a block here. rwatson: style(9) prefers that these variables be declared above rather than in a block here. | |||||
INP6_PCBHASHKEY(faddr), lport, fport); | |||||
idx = pkt_hash % grp->il_inpcnt; | |||||
if (IN6_ARE_ADDR_EQUAL(&grp->il6_laddr, laddr)) { | |||||
return (grp->il_inp[idx]); | |||||
} else { | |||||
if (IN6_IS_ADDR_UNSPECIFIED(&grp->il6_laddr) && | |||||
(lookupflags & INPLOOKUP_WILDCARD)) { | |||||
local_wild = grp->il_inp[idx]; | |||||
grp_local_wild = grp; | |||||
} | |||||
} | |||||
} | |||||
} | |||||
if (local_wild != NULL) { | |||||
return (local_wild); | |||||
} | |||||
return (NULL); | |||||
} | |||||
#ifdef PCBGROUP | #ifdef PCBGROUP | ||||
/* | /* | ||||
* Lookup PCB in hash list, using pcbgroup tables. | * Lookup PCB in hash list, using pcbgroup tables. | ||||
*/ | */ | ||||
static struct inpcb * | static struct inpcb * | ||||
in6_pcblookup_group(struct inpcbinfo *pcbinfo, struct inpcbgroup *pcbgroup, | in6_pcblookup_group(struct inpcbinfo *pcbinfo, struct inpcbgroup *pcbgroup, | ||||
struct in6_addr *faddr, u_int fport_arg, struct in6_addr *laddr, | struct in6_addr *faddr, u_int fport_arg, struct in6_addr *laddr, | ||||
u_int lport_arg, int lookupflags, struct ifnet *ifp) | u_int lport_arg, int lookupflags, struct ifnet *ifp) | ||||
▲ Show 20 Lines • Show All 229 Lines • ▼ Show 20 Lines | if (IN6_ARE_ADDR_EQUAL(&inp->in6p_faddr, faddr) && | ||||
if (prison_flag(inp->inp_cred, PR_IP6)) | if (prison_flag(inp->inp_cred, PR_IP6)) | ||||
return (inp); | return (inp); | ||||
if (tmpinp == NULL) | if (tmpinp == NULL) | ||||
tmpinp = inp; | tmpinp = inp; | ||||
} | } | ||||
} | } | ||||
if (tmpinp != NULL) | if (tmpinp != NULL) | ||||
return (tmpinp); | return (tmpinp); | ||||
/* | |||||
* Then look in lb group (for wildcard match). | |||||
Done Inline ActionsComment should be a full sentence. rwatson: Comment should be a full sentence. | |||||
*/ | |||||
if (pcbinfo->ipi_lbgrouphashbase != NULL && | |||||
(lookupflags & INPLOOKUP_WILDCARD)) { | |||||
inp = in6_pcblookup_lbgroup(pcbinfo, laddr, lport, faddr, | |||||
fport, lookupflags); | |||||
if (inp != NULL) { | |||||
return (inp); | |||||
} | |||||
} | |||||
/* | /* | ||||
* Then look for a wildcard match, if requested. | * Then look for a wildcard match, if requested. | ||||
*/ | */ | ||||
if ((lookupflags & INPLOOKUP_WILDCARD) != 0) { | if ((lookupflags & INPLOOKUP_WILDCARD) != 0) { | ||||
struct inpcb *local_wild = NULL, *local_exact = NULL; | struct inpcb *local_wild = NULL, *local_exact = NULL; | ||||
struct inpcb *jail_wild = NULL; | struct inpcb *jail_wild = NULL; | ||||
int injail; | int injail; | ||||
▲ Show 20 Lines • Show All 204 Lines • Show Last 20 Lines |
Use a C-style comment. Comment should be a full sentence.