Thanks Greg. We definitely want to backport this fix to all relevant kernels, I will work on these backports soon. Thanks. On Wed, Nov 6, 2019 at 7:42 AM <gregkh@xxxxxxxxxxxxxxxxxxx> wrote: > > > The patch below does not apply to the 4.4-stable tree. > If someone wants it applied there, or to any other stable or longterm > tree, then please email the backport, including the original git commit > id to <stable@xxxxxxxxxxxxxxx>. > > thanks, > > greg k-h > > ------------------ original commit in Linus's tree ------------------ > > From 55667441c84fa5e0911a0aac44fb059c15ba6da2 Mon Sep 17 00:00:00 2001 > From: Eric Dumazet <edumazet@xxxxxxxxxx> > Date: Tue, 22 Oct 2019 07:57:46 -0700 > Subject: [PATCH] net/flow_dissector: switch to siphash > > 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 commit df453700e8d8 > ("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: b56774163f99 ("ipv6: Enable auto flow labels by default") > Fixes: 42240901f7c4 ("ipv6: Implement different admin modes for automatic flow labels") > Fixes: 67800f9b1f4e ("ipv6: Call skb_get_hash_flowi6 to get skb->hash in ip6_make_flowlabel") > Fixes: cb1ce2ef387b ("ipv6: Implement automatic flow label generation on transmit") > Signed-off-by: Eric Dumazet <edumazet@xxxxxxxxxx> > Reported-by: Jonathan Berger <jonathann1@xxxxxxxxx> > Reported-by: Amit Klein <aksecurity@xxxxxxxxx> > Reported-by: Benny Pinkas <benny@xxxxxxxxxx> > Cc: Tom Herbert <tom@xxxxxxxxxxxxxxx> > Signed-off-by: David S. Miller <davem@xxxxxxxxxxxxx> > > diff --git a/include/linux/skbuff.h b/include/linux/skbuff.h > index 7914fdaf4226..a391147c03d4 100644 > --- a/include/linux/skbuff.h > +++ b/include/linux/skbuff.h > @@ -1354,7 +1354,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) > { > diff --git a/include/net/flow_dissector.h b/include/net/flow_dissector.h > index 90bd210be060..5cd12276ae21 100644 > --- a/include/net/flow_dissector.h > +++ b/include/net/flow_dissector.h > @@ -4,6 +4,7 @@ > > #include <linux/types.h> > #include <linux/in6.h> > +#include <linux/siphash.h> > #include <uapi/linux/if_ether.h> > > /** > @@ -276,7 +277,7 @@ struct flow_keys_basic { > 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_vlan cvlan; > diff --git a/include/net/fq.h b/include/net/fq.h > index d126b5d20261..2ad85e683041 100644 > --- a/include/net/fq.h > +++ b/include/net/fq.h > @@ -69,7 +69,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; > diff --git a/include/net/fq_impl.h b/include/net/fq_impl.h > index be40a4b327e3..107c0d700ed6 100644 > --- a/include/net/fq_impl.h > +++ b/include/net/fq_impl.h > @@ -108,7 +108,7 @@ static struct sk_buff *fq_tin_dequeue(struct fq *fq, > > static u32 fq_flow_idx(struct fq *fq, struct sk_buff *skb) > { > - u32 hash = skb_get_hash_perturb(skb, fq->perturbation); > + u32 hash = skb_get_hash_perturb(skb, &fq->perturbation); > > return reciprocal_scale(hash, fq->flows_cnt); > } > @@ -308,7 +308,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 */ > diff --git a/net/core/flow_dissector.c b/net/core/flow_dissector.c > index 7c09d87d3269..68eda10d0680 100644 > --- a/net/core/flow_dissector.c > +++ b/net/core/flow_dissector.c > @@ -1350,30 +1350,21 @@ bool __skb_flow_dissect(const struct net *net, > } > 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)); > > @@ -1388,7 +1379,7 @@ static inline size_t flow_keys_hash_length(const struct flow_keys *flow) > diff -= sizeof(flow->addrs.tipckey); > break; > } > - return (sizeof(*flow) - diff) / sizeof(u32); > + return sizeof(*flow) - diff; > } > > __be32 flow_get_u32_src(const struct flow_keys *flow) > @@ -1454,14 +1445,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; > > @@ -1471,12 +1463,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); > @@ -1524,7 +1517,7 @@ u32 __skb_get_hash_symmetric(const struct sk_buff *skb) > &keys, 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); > > @@ -1544,13 +1537,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; > > diff --git a/net/sched/sch_hhf.c b/net/sched/sch_hhf.c > index 23cd1c873a2c..be35f03b657b 100644 > --- a/net/sched/sch_hhf.c > +++ b/net/sched/sch_hhf.c > @@ -5,11 +5,11 @@ > * Copyright (C) 2013 Nandita Dukkipati <nanditad@xxxxxxxxxx> > */ > > -#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> > > @@ -126,7 +126,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 > @@ -264,7 +264,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; > @@ -582,7 +582,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); > > diff --git a/net/sched/sch_sfb.c b/net/sched/sch_sfb.c > index d448fe3068e5..4074c50ac3d7 100644 > --- a/net/sched/sch_sfb.c > +++ b/net/sched/sch_sfb.c > @@ -18,7 +18,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> > @@ -45,7 +45,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]; > }; > > @@ -217,7 +217,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) > @@ -314,9 +315,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); > } > > > @@ -352,7 +353,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; > diff --git a/net/sched/sch_sfq.c b/net/sched/sch_sfq.c > index 68404a9d2ce4..c787d4d46017 100644 > --- a/net/sched/sch_sfq.c > +++ b/net/sched/sch_sfq.c > @@ -14,7 +14,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> > @@ -117,7 +117,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) */ > @@ -157,7 +157,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, > @@ -607,9 +607,11 @@ static void sfq_perturbation(struct timer_list *t) > struct sfq_sched_data *q = from_timer(q, t, perturb_timer); > struct Qdisc *sch = q->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); > @@ -688,7 +690,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); > @@ -745,7 +747,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); >