mirror of
https://git.kernel.org/pub/scm/linux/kernel/git/stable/linux.git
synced 2024-09-27 21:03:32 +00:00
net/flow_dissector: switch to siphash
commit55667441c8
upstream. UDP IPv6 packets auto flowlabels are using a 32bit secret (static u32 hashrnd in net/core/flow_dissector.c) and apply jhash() over fields known by the receivers. Attackers can easily infer the 32bit secret and use this information to identify a device and/or user, since this 32bit secret is only set at boot time. Really, using jhash() to generate cookies sent on the wire is a serious security concern. Trying to change the rol32(hash, 16) in ip6_make_flowlabel() would be a dead end. Trying to periodically change the secret (like in sch_sfq.c) could change paths taken in the network for long lived flows. Let's switch to siphash, as we did in commitdf453700e8
("inet: switch IP ID generator to siphash") Using a cryptographically strong pseudo random function will solve this privacy issue and more generally remove other weak points in the stack. Packet schedulers using skb_get_hash_perturb() benefit from this change. Fixes:b56774163f
("ipv6: Enable auto flow labels by default") Fixes:42240901f7
("ipv6: Implement different admin modes for automatic flow labels") Fixes:67800f9b1f
("ipv6: Call skb_get_hash_flowi6 to get skb->hash in ip6_make_flowlabel") Fixes:cb1ce2ef38
("ipv6: Implement automatic flow label generation on transmit") Signed-off-by: Eric Dumazet <edumazet@google.com> Reported-by: Jonathan Berger <jonathann1@walla.com> Reported-by: Amit Klein <aksecurity@gmail.com> Reported-by: Benny Pinkas <benny@pinkas.net> Cc: Tom Herbert <tom@herbertland.com> Signed-off-by: David S. Miller <davem@davemloft.net> Signed-off-by: Greg Kroah-Hartman <gregkh@linuxfoundation.org>
This commit is contained in:
parent
4fa6002a34
commit
a9de6f42e9
8 changed files with 46 additions and 49 deletions
|
@ -1228,7 +1228,8 @@ static inline __u32 skb_get_hash_flowi6(struct sk_buff *skb, const struct flowi6
|
|||
return skb->hash;
|
||||
}
|
||||
|
||||
__u32 skb_get_hash_perturb(const struct sk_buff *skb, u32 perturb);
|
||||
__u32 skb_get_hash_perturb(const struct sk_buff *skb,
|
||||
const siphash_key_t *perturb);
|
||||
|
||||
static inline __u32 skb_get_hash_raw(const struct sk_buff *skb)
|
||||
{
|
||||
|
|
|
@ -4,6 +4,7 @@
|
|||
|
||||
#include <linux/types.h>
|
||||
#include <linux/in6.h>
|
||||
#include <linux/siphash.h>
|
||||
#include <uapi/linux/if_ether.h>
|
||||
|
||||
/**
|
||||
|
@ -229,7 +230,7 @@ struct flow_dissector {
|
|||
struct flow_keys {
|
||||
struct flow_dissector_key_control control;
|
||||
#define FLOW_KEYS_HASH_START_FIELD basic
|
||||
struct flow_dissector_key_basic basic;
|
||||
struct flow_dissector_key_basic basic __aligned(SIPHASH_ALIGNMENT);
|
||||
struct flow_dissector_key_tags tags;
|
||||
struct flow_dissector_key_vlan vlan;
|
||||
struct flow_dissector_key_keyid keyid;
|
||||
|
|
|
@ -70,7 +70,7 @@ struct fq {
|
|||
struct list_head backlogs;
|
||||
spinlock_t lock;
|
||||
u32 flows_cnt;
|
||||
u32 perturbation;
|
||||
siphash_key_t perturbation;
|
||||
u32 limit;
|
||||
u32 memory_limit;
|
||||
u32 memory_usage;
|
||||
|
|
|
@ -105,7 +105,7 @@ static struct fq_flow *fq_flow_classify(struct fq *fq,
|
|||
|
||||
lockdep_assert_held(&fq->lock);
|
||||
|
||||
hash = skb_get_hash_perturb(skb, fq->perturbation);
|
||||
hash = skb_get_hash_perturb(skb, &fq->perturbation);
|
||||
idx = reciprocal_scale(hash, fq->flows_cnt);
|
||||
flow = &fq->flows[idx];
|
||||
|
||||
|
@ -255,7 +255,7 @@ static int fq_init(struct fq *fq, int flows_cnt)
|
|||
INIT_LIST_HEAD(&fq->backlogs);
|
||||
spin_lock_init(&fq->lock);
|
||||
fq->flows_cnt = max_t(u32, flows_cnt, 1);
|
||||
fq->perturbation = prandom_u32();
|
||||
get_random_bytes(&fq->perturbation, sizeof(fq->perturbation));
|
||||
fq->quantum = 300;
|
||||
fq->limit = 8192;
|
||||
fq->memory_limit = 16 << 20; /* 16 MBytes */
|
||||
|
|
|
@ -889,45 +889,34 @@ bool __skb_flow_dissect(const struct sk_buff *skb,
|
|||
}
|
||||
EXPORT_SYMBOL(__skb_flow_dissect);
|
||||
|
||||
static u32 hashrnd __read_mostly;
|
||||
static siphash_key_t hashrnd __read_mostly;
|
||||
static __always_inline void __flow_hash_secret_init(void)
|
||||
{
|
||||
net_get_random_once(&hashrnd, sizeof(hashrnd));
|
||||
}
|
||||
|
||||
static __always_inline u32 __flow_hash_words(const u32 *words, u32 length,
|
||||
u32 keyval)
|
||||
static const void *flow_keys_hash_start(const struct flow_keys *flow)
|
||||
{
|
||||
return jhash2(words, length, keyval);
|
||||
}
|
||||
|
||||
static inline const u32 *flow_keys_hash_start(const struct flow_keys *flow)
|
||||
{
|
||||
const void *p = flow;
|
||||
|
||||
BUILD_BUG_ON(FLOW_KEYS_HASH_OFFSET % sizeof(u32));
|
||||
return (const u32 *)(p + FLOW_KEYS_HASH_OFFSET);
|
||||
BUILD_BUG_ON(FLOW_KEYS_HASH_OFFSET % SIPHASH_ALIGNMENT);
|
||||
return &flow->FLOW_KEYS_HASH_START_FIELD;
|
||||
}
|
||||
|
||||
static inline size_t flow_keys_hash_length(const struct flow_keys *flow)
|
||||
{
|
||||
size_t diff = FLOW_KEYS_HASH_OFFSET + sizeof(flow->addrs);
|
||||
BUILD_BUG_ON((sizeof(*flow) - FLOW_KEYS_HASH_OFFSET) % sizeof(u32));
|
||||
BUILD_BUG_ON(offsetof(typeof(*flow), addrs) !=
|
||||
sizeof(*flow) - sizeof(flow->addrs));
|
||||
size_t len = offsetof(typeof(*flow), addrs) - FLOW_KEYS_HASH_OFFSET;
|
||||
|
||||
switch (flow->control.addr_type) {
|
||||
case FLOW_DISSECTOR_KEY_IPV4_ADDRS:
|
||||
diff -= sizeof(flow->addrs.v4addrs);
|
||||
len += sizeof(flow->addrs.v4addrs);
|
||||
break;
|
||||
case FLOW_DISSECTOR_KEY_IPV6_ADDRS:
|
||||
diff -= sizeof(flow->addrs.v6addrs);
|
||||
len += sizeof(flow->addrs.v6addrs);
|
||||
break;
|
||||
case FLOW_DISSECTOR_KEY_TIPC_ADDRS:
|
||||
diff -= sizeof(flow->addrs.tipcaddrs);
|
||||
len += sizeof(flow->addrs.tipcaddrs);
|
||||
break;
|
||||
}
|
||||
return (sizeof(*flow) - diff) / sizeof(u32);
|
||||
return len;
|
||||
}
|
||||
|
||||
__be32 flow_get_u32_src(const struct flow_keys *flow)
|
||||
|
@ -993,14 +982,15 @@ static inline void __flow_hash_consistentify(struct flow_keys *keys)
|
|||
}
|
||||
}
|
||||
|
||||
static inline u32 __flow_hash_from_keys(struct flow_keys *keys, u32 keyval)
|
||||
static inline u32 __flow_hash_from_keys(struct flow_keys *keys,
|
||||
const siphash_key_t *keyval)
|
||||
{
|
||||
u32 hash;
|
||||
|
||||
__flow_hash_consistentify(keys);
|
||||
|
||||
hash = __flow_hash_words(flow_keys_hash_start(keys),
|
||||
flow_keys_hash_length(keys), keyval);
|
||||
hash = siphash(flow_keys_hash_start(keys),
|
||||
flow_keys_hash_length(keys), keyval);
|
||||
if (!hash)
|
||||
hash = 1;
|
||||
|
||||
|
@ -1010,12 +1000,13 @@ static inline u32 __flow_hash_from_keys(struct flow_keys *keys, u32 keyval)
|
|||
u32 flow_hash_from_keys(struct flow_keys *keys)
|
||||
{
|
||||
__flow_hash_secret_init();
|
||||
return __flow_hash_from_keys(keys, hashrnd);
|
||||
return __flow_hash_from_keys(keys, &hashrnd);
|
||||
}
|
||||
EXPORT_SYMBOL(flow_hash_from_keys);
|
||||
|
||||
static inline u32 ___skb_get_hash(const struct sk_buff *skb,
|
||||
struct flow_keys *keys, u32 keyval)
|
||||
struct flow_keys *keys,
|
||||
const siphash_key_t *keyval)
|
||||
{
|
||||
skb_flow_dissect_flow_keys(skb, keys,
|
||||
FLOW_DISSECTOR_F_STOP_AT_FLOW_LABEL);
|
||||
|
@ -1063,7 +1054,7 @@ u32 __skb_get_hash_symmetric(const struct sk_buff *skb)
|
|||
NULL, 0, 0, 0,
|
||||
FLOW_DISSECTOR_F_STOP_AT_FLOW_LABEL);
|
||||
|
||||
return __flow_hash_from_keys(&keys, hashrnd);
|
||||
return __flow_hash_from_keys(&keys, &hashrnd);
|
||||
}
|
||||
EXPORT_SYMBOL_GPL(__skb_get_hash_symmetric);
|
||||
|
||||
|
@ -1083,13 +1074,14 @@ void __skb_get_hash(struct sk_buff *skb)
|
|||
|
||||
__flow_hash_secret_init();
|
||||
|
||||
hash = ___skb_get_hash(skb, &keys, hashrnd);
|
||||
hash = ___skb_get_hash(skb, &keys, &hashrnd);
|
||||
|
||||
__skb_set_sw_hash(skb, hash, flow_keys_have_l4(&keys));
|
||||
}
|
||||
EXPORT_SYMBOL(__skb_get_hash);
|
||||
|
||||
__u32 skb_get_hash_perturb(const struct sk_buff *skb, u32 perturb)
|
||||
__u32 skb_get_hash_perturb(const struct sk_buff *skb,
|
||||
const siphash_key_t *perturb)
|
||||
{
|
||||
struct flow_keys keys;
|
||||
|
||||
|
|
|
@ -4,11 +4,11 @@
|
|||
* Copyright (C) 2013 Nandita Dukkipati <nanditad@google.com>
|
||||
*/
|
||||
|
||||
#include <linux/jhash.h>
|
||||
#include <linux/jiffies.h>
|
||||
#include <linux/module.h>
|
||||
#include <linux/skbuff.h>
|
||||
#include <linux/vmalloc.h>
|
||||
#include <linux/siphash.h>
|
||||
#include <net/pkt_sched.h>
|
||||
#include <net/sock.h>
|
||||
|
||||
|
@ -125,7 +125,7 @@ struct wdrr_bucket {
|
|||
|
||||
struct hhf_sched_data {
|
||||
struct wdrr_bucket buckets[WDRR_BUCKET_CNT];
|
||||
u32 perturbation; /* hash perturbation */
|
||||
siphash_key_t perturbation; /* hash perturbation */
|
||||
u32 quantum; /* psched_mtu(qdisc_dev(sch)); */
|
||||
u32 drop_overlimit; /* number of times max qdisc packet
|
||||
* limit was hit
|
||||
|
@ -263,7 +263,7 @@ static enum wdrr_bucket_idx hhf_classify(struct sk_buff *skb, struct Qdisc *sch)
|
|||
}
|
||||
|
||||
/* Get hashed flow-id of the skb. */
|
||||
hash = skb_get_hash_perturb(skb, q->perturbation);
|
||||
hash = skb_get_hash_perturb(skb, &q->perturbation);
|
||||
|
||||
/* Check if this packet belongs to an already established HH flow. */
|
||||
flow_pos = hash & HHF_BIT_MASK;
|
||||
|
@ -578,7 +578,7 @@ static int hhf_init(struct Qdisc *sch, struct nlattr *opt)
|
|||
|
||||
sch->limit = 1000;
|
||||
q->quantum = psched_mtu(qdisc_dev(sch));
|
||||
q->perturbation = prandom_u32();
|
||||
get_random_bytes(&q->perturbation, sizeof(q->perturbation));
|
||||
INIT_LIST_HEAD(&q->new_buckets);
|
||||
INIT_LIST_HEAD(&q->old_buckets);
|
||||
|
||||
|
|
|
@ -22,7 +22,7 @@
|
|||
#include <linux/errno.h>
|
||||
#include <linux/skbuff.h>
|
||||
#include <linux/random.h>
|
||||
#include <linux/jhash.h>
|
||||
#include <linux/siphash.h>
|
||||
#include <net/ip.h>
|
||||
#include <net/pkt_sched.h>
|
||||
#include <net/pkt_cls.h>
|
||||
|
@ -49,7 +49,7 @@ struct sfb_bucket {
|
|||
* (Section 4.4 of SFB reference : moving hash functions)
|
||||
*/
|
||||
struct sfb_bins {
|
||||
u32 perturbation; /* jhash perturbation */
|
||||
siphash_key_t perturbation; /* siphash key */
|
||||
struct sfb_bucket bins[SFB_LEVELS][SFB_NUMBUCKETS];
|
||||
};
|
||||
|
||||
|
@ -221,7 +221,8 @@ static u32 sfb_compute_qlen(u32 *prob_r, u32 *avgpm_r, const struct sfb_sched_da
|
|||
|
||||
static void sfb_init_perturbation(u32 slot, struct sfb_sched_data *q)
|
||||
{
|
||||
q->bins[slot].perturbation = prandom_u32();
|
||||
get_random_bytes(&q->bins[slot].perturbation,
|
||||
sizeof(q->bins[slot].perturbation));
|
||||
}
|
||||
|
||||
static void sfb_swap_slot(struct sfb_sched_data *q)
|
||||
|
@ -317,9 +318,9 @@ static int sfb_enqueue(struct sk_buff *skb, struct Qdisc *sch,
|
|||
/* If using external classifiers, get result and record it. */
|
||||
if (!sfb_classify(skb, fl, &ret, &salt))
|
||||
goto other_drop;
|
||||
sfbhash = jhash_1word(salt, q->bins[slot].perturbation);
|
||||
sfbhash = siphash_1u32(salt, &q->bins[slot].perturbation);
|
||||
} else {
|
||||
sfbhash = skb_get_hash_perturb(skb, q->bins[slot].perturbation);
|
||||
sfbhash = skb_get_hash_perturb(skb, &q->bins[slot].perturbation);
|
||||
}
|
||||
|
||||
|
||||
|
@ -355,7 +356,7 @@ static int sfb_enqueue(struct sk_buff *skb, struct Qdisc *sch,
|
|||
/* Inelastic flow */
|
||||
if (q->double_buffering) {
|
||||
sfbhash = skb_get_hash_perturb(skb,
|
||||
q->bins[slot].perturbation);
|
||||
&q->bins[slot].perturbation);
|
||||
if (!sfbhash)
|
||||
sfbhash = 1;
|
||||
sfb_skb_cb(skb)->hashes[slot] = sfbhash;
|
||||
|
|
|
@ -18,7 +18,7 @@
|
|||
#include <linux/errno.h>
|
||||
#include <linux/init.h>
|
||||
#include <linux/skbuff.h>
|
||||
#include <linux/jhash.h>
|
||||
#include <linux/siphash.h>
|
||||
#include <linux/slab.h>
|
||||
#include <linux/vmalloc.h>
|
||||
#include <net/netlink.h>
|
||||
|
@ -121,7 +121,7 @@ struct sfq_sched_data {
|
|||
u8 headdrop;
|
||||
u8 maxdepth; /* limit of packets per flow */
|
||||
|
||||
u32 perturbation;
|
||||
siphash_key_t perturbation;
|
||||
u8 cur_depth; /* depth of longest slot */
|
||||
u8 flags;
|
||||
unsigned short scaled_quantum; /* SFQ_ALLOT_SIZE(quantum) */
|
||||
|
@ -160,7 +160,7 @@ static inline struct sfq_head *sfq_dep_head(struct sfq_sched_data *q, sfq_index
|
|||
static unsigned int sfq_hash(const struct sfq_sched_data *q,
|
||||
const struct sk_buff *skb)
|
||||
{
|
||||
return skb_get_hash_perturb(skb, q->perturbation) & (q->divisor - 1);
|
||||
return skb_get_hash_perturb(skb, &q->perturbation) & (q->divisor - 1);
|
||||
}
|
||||
|
||||
static unsigned int sfq_classify(struct sk_buff *skb, struct Qdisc *sch,
|
||||
|
@ -609,9 +609,11 @@ static void sfq_perturbation(unsigned long arg)
|
|||
struct Qdisc *sch = (struct Qdisc *)arg;
|
||||
struct sfq_sched_data *q = qdisc_priv(sch);
|
||||
spinlock_t *root_lock = qdisc_lock(qdisc_root_sleeping(sch));
|
||||
siphash_key_t nkey;
|
||||
|
||||
get_random_bytes(&nkey, sizeof(nkey));
|
||||
spin_lock(root_lock);
|
||||
q->perturbation = prandom_u32();
|
||||
q->perturbation = nkey;
|
||||
if (!q->filter_list && q->tail)
|
||||
sfq_rehash(sch);
|
||||
spin_unlock(root_lock);
|
||||
|
@ -690,7 +692,7 @@ static int sfq_change(struct Qdisc *sch, struct nlattr *opt)
|
|||
del_timer(&q->perturb_timer);
|
||||
if (q->perturb_period) {
|
||||
mod_timer(&q->perturb_timer, jiffies + q->perturb_period);
|
||||
q->perturbation = prandom_u32();
|
||||
get_random_bytes(&q->perturbation, sizeof(q->perturbation));
|
||||
}
|
||||
sch_tree_unlock(sch);
|
||||
kfree(p);
|
||||
|
@ -746,7 +748,7 @@ static int sfq_init(struct Qdisc *sch, struct nlattr *opt)
|
|||
q->quantum = psched_mtu(qdisc_dev(sch));
|
||||
q->scaled_quantum = SFQ_ALLOT_SIZE(q->quantum);
|
||||
q->perturb_period = 0;
|
||||
q->perturbation = prandom_u32();
|
||||
get_random_bytes(&q->perturbation, sizeof(q->perturbation));
|
||||
|
||||
if (opt) {
|
||||
int err = sfq_change(sch, opt);
|
||||
|
|
Loading…
Reference in a new issue