in_pcb: use jenkins hash over the entire IPv6 (or IPv4) address

The intent is to provide more entropy than can be provided
by just the 32-bits of the IPv6 address which overlaps with
6to4 tunnels.  This is needed to mitigate potential algorithmic
complexity attacks from attackers who can control large
numbers of IPv6 addresses.

Together with:		gallatin
Reviewed by:		dwmalone, rscheff
Differential revision:	https://reviews.freebsd.org/D33254
This commit is contained in:
Gleb Smirnoff 2021-12-26 10:47:28 -08:00
parent eb8dcdeac2
commit a057769205
4 changed files with 68 additions and 34 deletions

View File

@ -49,7 +49,9 @@ __FBSDID("$FreeBSD$");
#include "opt_rss.h"
#include <sys/param.h>
#include <sys/hash.h>
#include <sys/systm.h>
#include <sys/libkern.h>
#include <sys/lock.h>
#include <sys/malloc.h>
#include <sys/mbuf.h>
@ -246,6 +248,16 @@ SYSCTL_COUNTER_U64(_net_inet_ip_rl, OID_AUTO, chgrl, CTLFLAG_RD,
#endif /* INET */
VNET_DEFINE(uint32_t, in_pcbhashseed);
static void
in_pcbhashseed_init(void)
{
V_in_pcbhashseed = arc4random();
}
VNET_SYSINIT(in_pcbhashseed_init, SI_SUB_PROTO_DOMAIN, SI_ORDER_FIRST,
in_pcbhashseed_init, 0);
/*
* in_pcb.c: manage the Protocol Control Blocks.
*
@ -2085,8 +2097,8 @@ in_pcblookup_local(struct inpcbinfo *pcbinfo, struct in_addr laddr,
* Look for an unconnected (wildcard foreign addr) PCB that
* matches the local address and port we're looking for.
*/
head = &pcbinfo->ipi_hashbase[INP_PCBHASH(INADDR_ANY, lport,
0, pcbinfo->ipi_hashmask)];
head = &pcbinfo->ipi_hashbase[INP_PCBHASH_WILD(lport,
pcbinfo->ipi_hashmask)];
CK_LIST_FOREACH(inp, head, inp_hash) {
#ifdef INET6
/* XXX inp locking */
@ -2214,7 +2226,7 @@ in_pcblookup_lbgroup(const struct inpcbinfo *pcbinfo,
if (grp->il_lport != lport)
continue;
idx = INP_PCBLBGROUP_PKTHASH(faddr->s_addr, lport, fport) %
idx = INP_PCBLBGROUP_PKTHASH(faddr, lport, fport) %
grp->il_inpcnt;
if (grp->il_laddr.s_addr == laddr->s_addr) {
if (numa_domain == M_NODOM ||
@ -2260,7 +2272,7 @@ in_pcblookup_hash_locked(struct inpcbinfo *pcbinfo, struct in_addr faddr,
* First look for an exact match.
*/
tmpinp = NULL;
head = &pcbinfo->ipi_hashbase[INP_PCBHASH(faddr.s_addr, lport, fport,
head = &pcbinfo->ipi_hashbase[INP_PCBHASH(&faddr, lport, fport,
pcbinfo->ipi_hashmask)];
CK_LIST_FOREACH(inp, head, inp_hash) {
#ifdef INET6
@ -2315,8 +2327,8 @@ in_pcblookup_hash_locked(struct inpcbinfo *pcbinfo, struct in_addr faddr,
* 4. non-jailed, wild.
*/
head = &pcbinfo->ipi_hashbase[INP_PCBHASH(INADDR_ANY, lport,
0, pcbinfo->ipi_hashmask)];
head = &pcbinfo->ipi_hashbase[INP_PCBHASH_WILD(lport,
pcbinfo->ipi_hashmask)];
CK_LIST_FOREACH(inp, head, inp_hash) {
#ifdef INET6
/* XXX inp locking */
@ -2439,7 +2451,6 @@ in_pcbinshash(struct inpcb *inp)
struct inpcbporthead *pcbporthash;
struct inpcbinfo *pcbinfo = inp->inp_pcbinfo;
struct inpcbport *phd;
u_int32_t hashkey_faddr;
int so_options;
INP_WLOCK_ASSERT(inp);
@ -2450,13 +2461,12 @@ in_pcbinshash(struct inpcb *inp)
#ifdef INET6
if (inp->inp_vflag & INP_IPV6)
hashkey_faddr = INP6_PCBHASHKEY(&inp->in6p_faddr);
pcbhash = &pcbinfo->ipi_hashbase[INP6_PCBHASH(&inp->in6p_faddr,
inp->inp_lport, inp->inp_fport, pcbinfo->ipi_hashmask)];
else
#endif
hashkey_faddr = inp->inp_faddr.s_addr;
pcbhash = &pcbinfo->ipi_hashbase[INP_PCBHASH(hashkey_faddr,
inp->inp_lport, inp->inp_fport, pcbinfo->ipi_hashmask)];
pcbhash = &pcbinfo->ipi_hashbase[INP_PCBHASH(&inp->inp_faddr,
inp->inp_lport, inp->inp_fport, pcbinfo->ipi_hashmask)];
pcbporthash = &pcbinfo->ipi_porthashbase[
INP_PCBPORTHASH(inp->inp_lport, pcbinfo->ipi_porthashmask)];
@ -2516,7 +2526,6 @@ in_pcbrehash(struct inpcb *inp)
{
struct inpcbinfo *pcbinfo = inp->inp_pcbinfo;
struct inpcbhead *head;
u_int32_t hashkey_faddr;
INP_WLOCK_ASSERT(inp);
INP_HASH_WLOCK_ASSERT(pcbinfo);
@ -2526,13 +2535,12 @@ in_pcbrehash(struct inpcb *inp)
#ifdef INET6
if (inp->inp_vflag & INP_IPV6)
hashkey_faddr = INP6_PCBHASHKEY(&inp->in6p_faddr);
head = &pcbinfo->ipi_hashbase[INP6_PCBHASH(&inp->in6p_faddr,
inp->inp_lport, inp->inp_fport, pcbinfo->ipi_hashmask)];
else
#endif
hashkey_faddr = inp->inp_faddr.s_addr;
head = &pcbinfo->ipi_hashbase[INP_PCBHASH(hashkey_faddr,
inp->inp_lport, inp->inp_fport, pcbinfo->ipi_hashmask)];
head = &pcbinfo->ipi_hashbase[INP_PCBHASH(&inp->inp_faddr,
inp->inp_lport, inp->inp_fport, pcbinfo->ipi_hashmask)];
CK_LIST_REMOVE(inp, inp_hash);
CK_LIST_INSERT_HEAD(head, inp, inp_hash);

View File

@ -73,7 +73,8 @@ typedef uint64_t inp_gen_t;
/*
* PCB with AF_INET6 null bind'ed laddr can receive AF_INET input packet.
* So, AF_INET6 null laddr is also used as AF_INET null laddr, by utilizing
* the following structure.
* the following structure. This requires padding always be zeroed out,
* which is done right after inpcb allocation and stays through its lifetime.
*/
struct in_addr_4in6 {
u_int32_t ia46_pad32[3];
@ -530,13 +531,36 @@ int inp_so_options(const struct inpcb *inp);
#define INP_HASH_WLOCK_ASSERT(ipi) mtx_assert(&(ipi)->ipi_hash_lock, \
MA_OWNED)
#define INP_PCBHASH(faddr, lport, fport, mask) \
(((faddr) ^ ((faddr) >> 16) ^ ntohs((lport) ^ (fport))) & (mask))
#define INP_PCBPORTHASH(lport, mask) \
(ntohs((lport)) & (mask))
#define INP_PCBLBGROUP_PKTHASH(faddr, lport, fport) \
((faddr) ^ ((faddr) >> 16) ^ ntohs((lport) ^ (fport)))
#define INP6_PCBHASHKEY(faddr) ((faddr)->s6_addr32[3])
/*
* Wildcard matching hash is not just a microoptimisation! The hash for
* wildcard IPv4 and wildcard IPv6 must be the same, otherwise AF_INET6
* wildcard bound pcb won't be able to receive AF_INET connections, while:
* jenkins_hash(&zeroes, 1, s) != jenkins_hash(&zeroes, 4, s)
* See also comment above struct in_addr_4in6.
*/
#define IN_ADDR_JHASH32(addr) \
((addr)->s_addr == INADDR_ANY ? V_in_pcbhashseed : \
jenkins_hash32((&(addr)->s_addr), 1, V_in_pcbhashseed))
#define IN6_ADDR_JHASH32(addr) \
(memcmp((addr), &in6addr_any, sizeof(in6addr_any)) == 0 ? \
V_in_pcbhashseed : \
jenkins_hash32((addr)->__u6_addr.__u6_addr32, \
nitems((addr)->__u6_addr.__u6_addr32), V_in_pcbhashseed))
#define INP_PCBHASH(faddr, lport, fport, mask) \
((IN_ADDR_JHASH32(faddr) ^ ntohs((lport) ^ (fport))) & (mask))
#define INP6_PCBHASH(faddr, lport, fport, mask) \
((IN6_ADDR_JHASH32(faddr) ^ ntohs((lport) ^ (fport))) & (mask))
#define INP_PCBHASH_WILD(lport, mask) \
((V_in_pcbhashseed ^ ntohs(lport)) & (mask))
#define INP_PCBLBGROUP_PKTHASH(faddr, lport, fport) \
(IN_ADDR_JHASH32(faddr) ^ ntohs((lport) ^ (fport)))
#define INP6_PCBLBGROUP_PKTHASH(faddr, lport, fport) \
(IN6_ADDR_JHASH32(faddr) ^ ntohs((lport) ^ (fport)))
#define INP_PCBPORTHASH(lport, mask) (ntohs((lport)) & (mask))
/*
* Flags for inp_vflags -- historically version flags only

View File

@ -44,6 +44,9 @@
* Definitions shared between netinet/in_pcb.c and netinet6/in6_pcb.c
*/
VNET_DECLARE(uint32_t, in_pcbhashseed);
#define V_in_pcbhashseed VNET(in_pcbhashseed)
bool inp_smr_lock(struct inpcb *, const inp_lookup_t);
int in_pcb_lport(struct inpcb *, struct in_addr *, u_short *,
struct ucred *, int);

View File

@ -75,6 +75,7 @@ __FBSDID("$FreeBSD$");
#include "opt_route.h"
#include "opt_rss.h"
#include <sys/hash.h>
#include <sys/param.h>
#include <sys/systm.h>
#include <sys/malloc.h>
@ -787,8 +788,7 @@ in6_pcblookup_local(struct inpcbinfo *pcbinfo, struct in6_addr *laddr,
* Look for an unconnected (wildcard foreign addr) PCB that
* matches the local address and port we're looking for.
*/
head = &pcbinfo->ipi_hashbase[INP_PCBHASH(
INP6_PCBHASHKEY(&in6addr_any), lport, 0,
head = &pcbinfo->ipi_hashbase[INP_PCBHASH_WILD(lport,
pcbinfo->ipi_hashmask)];
CK_LIST_FOREACH(inp, head, inp_hash) {
/* XXX inp locking */
@ -972,8 +972,8 @@ in6_pcblookup_lbgroup(const struct inpcbinfo *pcbinfo,
if (grp->il_lport != lport)
continue;
idx = INP_PCBLBGROUP_PKTHASH(INP6_PCBHASHKEY(faddr), lport,
fport) % grp->il_inpcnt;
idx = INP6_PCBLBGROUP_PKTHASH(faddr, lport, fport) %
grp->il_inpcnt;
if (IN6_ARE_ADDR_EQUAL(&grp->il6_laddr, laddr)) {
if (numa_domain == M_NODOM ||
grp->il_numa_domain == numa_domain) {
@ -1015,8 +1015,8 @@ in6_pcblookup_hash_locked(struct inpcbinfo *pcbinfo, struct in6_addr *faddr,
* First look for an exact match.
*/
tmpinp = NULL;
head = &pcbinfo->ipi_hashbase[INP_PCBHASH(
INP6_PCBHASHKEY(faddr), lport, fport, pcbinfo->ipi_hashmask)];
head = &pcbinfo->ipi_hashbase[INP6_PCBHASH(faddr, lport, fport,
pcbinfo->ipi_hashmask)];
CK_LIST_FOREACH(inp, head, inp_hash) {
/* XXX inp locking */
if ((inp->inp_vflag & INP_IPV6) == 0)
@ -1064,8 +1064,7 @@ in6_pcblookup_hash_locked(struct inpcbinfo *pcbinfo, struct in6_addr *faddr,
* 3. non-jailed, non-wild.
* 4. non-jailed, wild.
*/
head = &pcbinfo->ipi_hashbase[INP_PCBHASH(
INP6_PCBHASHKEY(&in6addr_any), lport, 0,
head = &pcbinfo->ipi_hashbase[INP_PCBHASH_WILD(lport,
pcbinfo->ipi_hashmask)];
CK_LIST_FOREACH(inp, head, inp_hash) {
/* XXX inp locking */