Add IPv6 flowid, bindmulti and RSS awareness.

This commit is contained in:
Adrian Chadd 2014-07-12 05:46:33 +00:00
parent a8a2d8003a
commit c7c0d94874
2 changed files with 145 additions and 0 deletions

View File

@ -202,6 +202,7 @@ in6_pcbbind(register struct inpcb *inp, struct sockaddr *nam,
&sin6->sin6_addr, lport,
INPLOOKUP_WILDCARD, cred);
if (t &&
((inp->inp_flags2 & INP_BINDMULTI) == 0) &&
((t->inp_flags & INP_TIMEWAIT) == 0) &&
(so->so_type != SOCK_STREAM ||
IN6_IS_ADDR_UNSPECIFIED(&t->in6p_faddr)) &&
@ -211,6 +212,16 @@ in6_pcbbind(register struct inpcb *inp, struct sockaddr *nam,
(inp->inp_cred->cr_uid !=
t->inp_cred->cr_uid))
return (EADDRINUSE);
/*
* If the socket is a BINDMULTI socket, then
* the credentials need to match and the
* original socket also has to have been bound
* with BINDMULTI.
*/
if (t && (! in_pcbbind_check_bindmulti(inp, t)))
return (EADDRINUSE);
#ifdef INET
if ((inp->inp_flags & IN6P_IPV6_V6ONLY) == 0 &&
IN6_IS_ADDR_UNSPECIFIED(&sin6->sin6_addr)) {
@ -221,6 +232,7 @@ in6_pcbbind(register struct inpcb *inp, struct sockaddr *nam,
sin.sin_addr, lport,
INPLOOKUP_WILDCARD, cred);
if (t &&
((inp->inp_flags2 & INP_BINDMULTI) == 0) &&
((t->inp_flags &
INP_TIMEWAIT) == 0) &&
(so->so_type != SOCK_STREAM ||
@ -229,6 +241,9 @@ in6_pcbbind(register struct inpcb *inp, struct sockaddr *nam,
(inp->inp_cred->cr_uid !=
t->inp_cred->cr_uid))
return (EADDRINUSE);
if (t && (! in_pcbbind_check_bindmulti(inp, t)))
return (EADDRINUSE);
}
#endif
}
@ -890,6 +905,71 @@ in6_pcblookup_group(struct inpcbinfo *pcbinfo, struct inpcbgroup *pcbgroup,
goto found;
}
/*
* Then look for a wildcard match in the pcbgroup.
*/
if ((lookupflags & INPLOOKUP_WILDCARD) != 0) {
struct inpcb *local_wild = NULL, *local_exact = NULL;
struct inpcb *jail_wild = NULL;
int injail;
/*
* Order of socket selection - we always prefer jails.
* 1. jailed, non-wild.
* 2. jailed, wild.
* 3. non-jailed, non-wild.
* 4. non-jailed, wild.
*/
head = &pcbgroup->ipg_hashbase[
INP_PCBHASH(INADDR_ANY, lport, 0, pcbgroup->ipg_hashmask)];
LIST_FOREACH(inp, head, inp_pcbgrouphash) {
/* XXX inp locking */
if ((inp->inp_vflag & INP_IPV6) == 0)
continue;
if (!IN6_IS_ADDR_UNSPECIFIED(&inp->in6p_faddr) ||
inp->inp_lport != lport) {
continue;
}
/* XXX inp locking */
if (faith && (inp->inp_flags & INP_FAITH) == 0)
continue;
injail = prison_flag(inp->inp_cred, PR_IP6);
if (injail) {
if (prison_check_ip6(inp->inp_cred,
laddr) != 0)
continue;
} else {
if (local_exact != NULL)
continue;
}
if (IN6_ARE_ADDR_EQUAL(&inp->in6p_laddr, laddr)) {
if (injail)
goto found;
else
local_exact = inp;
} else if (IN6_IS_ADDR_UNSPECIFIED(&inp->in6p_laddr)) {
if (injail)
jail_wild = inp;
else
local_wild = inp;
}
} /* LIST_FOREACH */
inp = jail_wild;
if (inp == NULL)
inp = jail_wild;
if (inp == NULL)
inp = local_exact;
if (inp == NULL)
inp = local_wild;
if (inp != NULL)
goto found;
}
/*
* Then look for a wildcard match, if requested.
*/

View File

@ -69,6 +69,7 @@ __FBSDID("$FreeBSD$");
#include "opt_ipsec.h"
#include "opt_sctp.h"
#include "opt_route.h"
#include "opt_rss.h"
#include <sys/param.h>
#include <sys/kernel.h>
@ -102,6 +103,7 @@ __FBSDID("$FreeBSD$");
#include <netinet/in_pcb.h>
#include <netinet/tcp_var.h>
#include <netinet6/nd6.h>
#include <netinet/in_rss.h>
#ifdef IPSEC
#include <netipsec/ipsec.h>
@ -1287,6 +1289,10 @@ ip6_ctloutput(struct socket *so, struct sockopt *sopt)
int level, op, optname;
int optlen;
struct thread *td;
#ifdef RSS
uint32_t rss_bucket;
int retval;
#endif
level = sopt->sopt_level;
op = sopt->sopt_dir;
@ -1390,6 +1396,10 @@ ip6_ctloutput(struct socket *so, struct sockopt *sopt)
case IPV6_V6ONLY:
case IPV6_AUTOFLOWLABEL:
case IPV6_BINDANY:
case IPV6_BINDMULTI:
#ifdef RSS
case IPV6_RSS_LISTEN_BUCKET:
#endif
if (optname == IPV6_BINDANY && td != NULL) {
error = priv_check(td,
PRIV_NETINET_BINDANY);
@ -1439,6 +1449,16 @@ do { \
} while (/*CONSTCOND*/ 0)
#define OPTBIT(bit) (in6p->inp_flags & (bit) ? 1 : 0)
#define OPTSET2(bit, val) do { \
INP_WLOCK(in6p); \
if (val) \
in6p->inp_flags2 |= bit; \
else \
in6p->inp_flags2 &= ~bit; \
INP_WUNLOCK(in6p); \
} while (0)
#define OPTBIT2(bit) (in6p->inp_flags2 & (bit) ? 1 : 0)
case IPV6_RECVPKTINFO:
/* cannot mix with RFC2292 */
if (OPTBIT(IN6P_RFC2292)) {
@ -1557,6 +1577,21 @@ do { \
case IPV6_BINDANY:
OPTSET(INP_BINDANY);
break;
case IPV6_BINDMULTI:
OPTSET2(INP_BINDMULTI, optval);
break;
#ifdef RSS
case IPV6_RSS_LISTEN_BUCKET:
if ((optval >= 0) &&
(optval < rss_getnumbuckets())) {
in6p->inp_rss_listen_bucket = optval;
OPTSET2(INP_RSS_BUCKET_SET, 1);
} else {
error = EINVAL;
}
break;
#endif
}
break;
@ -1772,6 +1807,11 @@ do { \
case IPV6_RECVTCLASS:
case IPV6_AUTOFLOWLABEL:
case IPV6_BINDANY:
case IPV6_FLOWID:
case IPV6_FLOWTYPE:
#ifdef RSS
case IPV6_RSSBUCKETID:
#endif
switch (optname) {
case IPV6_RECVHOPOPTS:
@ -1837,6 +1877,31 @@ do { \
case IPV6_BINDANY:
optval = OPTBIT(INP_BINDANY);
break;
case IPV6_FLOWID:
optval = in6p->inp_flowid;
break;
case IPV6_FLOWTYPE:
optval = in6p->inp_flowtype;
break;
#ifdef RSS
case IPV6_RSSBUCKETID:
retval =
rss_hash2bucket(in6p->inp_flowid,
in6p->inp_flowtype,
&rss_bucket);
if (retval == 0)
optval = rss_bucket;
else
error = EINVAL;
break;
#endif
case IPV6_BINDMULTI:
optval = OPTBIT2(INP_BINDMULTI);
break;
}
if (error)
break;