Skip to content

Commit 5566744

Browse files
edumazetdavem330
authored andcommitted
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 df45370 ("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: b567741 ("ipv6: Enable auto flow labels by default") Fixes: 4224090 ("ipv6: Implement different admin modes for automatic flow labels") Fixes: 67800f9 ("ipv6: Call skb_get_hash_flowi6 to get skb->hash in ip6_make_flowlabel") Fixes: cb1ce2e ("ipv6: Implement automatic flow label generation on transmit") Signed-off-by: Eric Dumazet <[email protected]> Reported-by: Jonathan Berger <[email protected]> Reported-by: Amit Klein <[email protected]> Reported-by: Benny Pinkas <[email protected]> Cc: Tom Herbert <[email protected]> Signed-off-by: David S. Miller <[email protected]>
1 parent 6c5d9c2 commit 5566744

File tree

8 files changed

+42
-43
lines changed

8 files changed

+42
-43
lines changed

include/linux/skbuff.h

Lines changed: 2 additions & 1 deletion
Original file line numberDiff line numberDiff line change
@@ -1354,7 +1354,8 @@ static inline __u32 skb_get_hash_flowi6(struct sk_buff *skb, const struct flowi6
13541354
return skb->hash;
13551355
}
13561356

1357-
__u32 skb_get_hash_perturb(const struct sk_buff *skb, u32 perturb);
1357+
__u32 skb_get_hash_perturb(const struct sk_buff *skb,
1358+
const siphash_key_t *perturb);
13581359

13591360
static inline __u32 skb_get_hash_raw(const struct sk_buff *skb)
13601361
{

include/net/flow_dissector.h

Lines changed: 2 additions & 1 deletion
Original file line numberDiff line numberDiff line change
@@ -4,6 +4,7 @@
44

55
#include <linux/types.h>
66
#include <linux/in6.h>
7+
#include <linux/siphash.h>
78
#include <uapi/linux/if_ether.h>
89

910
/**
@@ -276,7 +277,7 @@ struct flow_keys_basic {
276277
struct flow_keys {
277278
struct flow_dissector_key_control control;
278279
#define FLOW_KEYS_HASH_START_FIELD basic
279-
struct flow_dissector_key_basic basic;
280+
struct flow_dissector_key_basic basic __aligned(SIPHASH_ALIGNMENT);
280281
struct flow_dissector_key_tags tags;
281282
struct flow_dissector_key_vlan vlan;
282283
struct flow_dissector_key_vlan cvlan;

include/net/fq.h

Lines changed: 1 addition & 1 deletion
Original file line numberDiff line numberDiff line change
@@ -69,7 +69,7 @@ struct fq {
6969
struct list_head backlogs;
7070
spinlock_t lock;
7171
u32 flows_cnt;
72-
u32 perturbation;
72+
siphash_key_t perturbation;
7373
u32 limit;
7474
u32 memory_limit;
7575
u32 memory_usage;

include/net/fq_impl.h

Lines changed: 2 additions & 2 deletions
Original file line numberDiff line numberDiff line change
@@ -108,7 +108,7 @@ static struct sk_buff *fq_tin_dequeue(struct fq *fq,
108108

109109
static u32 fq_flow_idx(struct fq *fq, struct sk_buff *skb)
110110
{
111-
u32 hash = skb_get_hash_perturb(skb, fq->perturbation);
111+
u32 hash = skb_get_hash_perturb(skb, &fq->perturbation);
112112

113113
return reciprocal_scale(hash, fq->flows_cnt);
114114
}
@@ -308,7 +308,7 @@ static int fq_init(struct fq *fq, int flows_cnt)
308308
INIT_LIST_HEAD(&fq->backlogs);
309309
spin_lock_init(&fq->lock);
310310
fq->flows_cnt = max_t(u32, flows_cnt, 1);
311-
fq->perturbation = prandom_u32();
311+
get_random_bytes(&fq->perturbation, sizeof(fq->perturbation));
312312
fq->quantum = 300;
313313
fq->limit = 8192;
314314
fq->memory_limit = 16 << 20; /* 16 MBytes */

net/core/flow_dissector.c

Lines changed: 16 additions & 22 deletions
Original file line numberDiff line numberDiff line change
@@ -1350,30 +1350,21 @@ bool __skb_flow_dissect(const struct net *net,
13501350
}
13511351
EXPORT_SYMBOL(__skb_flow_dissect);
13521352

1353-
static u32 hashrnd __read_mostly;
1353+
static siphash_key_t hashrnd __read_mostly;
13541354
static __always_inline void __flow_hash_secret_init(void)
13551355
{
13561356
net_get_random_once(&hashrnd, sizeof(hashrnd));
13571357
}
13581358

1359-
static __always_inline u32 __flow_hash_words(const u32 *words, u32 length,
1360-
u32 keyval)
1359+
static const void *flow_keys_hash_start(const struct flow_keys *flow)
13611360
{
1362-
return jhash2(words, length, keyval);
1363-
}
1364-
1365-
static inline const u32 *flow_keys_hash_start(const struct flow_keys *flow)
1366-
{
1367-
const void *p = flow;
1368-
1369-
BUILD_BUG_ON(FLOW_KEYS_HASH_OFFSET % sizeof(u32));
1370-
return (const u32 *)(p + FLOW_KEYS_HASH_OFFSET);
1361+
BUILD_BUG_ON(FLOW_KEYS_HASH_OFFSET % SIPHASH_ALIGNMENT);
1362+
return &flow->FLOW_KEYS_HASH_START_FIELD;
13711363
}
13721364

13731365
static inline size_t flow_keys_hash_length(const struct flow_keys *flow)
13741366
{
13751367
size_t diff = FLOW_KEYS_HASH_OFFSET + sizeof(flow->addrs);
1376-
BUILD_BUG_ON((sizeof(*flow) - FLOW_KEYS_HASH_OFFSET) % sizeof(u32));
13771368
BUILD_BUG_ON(offsetof(typeof(*flow), addrs) !=
13781369
sizeof(*flow) - sizeof(flow->addrs));
13791370

@@ -1388,7 +1379,7 @@ static inline size_t flow_keys_hash_length(const struct flow_keys *flow)
13881379
diff -= sizeof(flow->addrs.tipckey);
13891380
break;
13901381
}
1391-
return (sizeof(*flow) - diff) / sizeof(u32);
1382+
return sizeof(*flow) - diff;
13921383
}
13931384

13941385
__be32 flow_get_u32_src(const struct flow_keys *flow)
@@ -1454,14 +1445,15 @@ static inline void __flow_hash_consistentify(struct flow_keys *keys)
14541445
}
14551446
}
14561447

1457-
static inline u32 __flow_hash_from_keys(struct flow_keys *keys, u32 keyval)
1448+
static inline u32 __flow_hash_from_keys(struct flow_keys *keys,
1449+
const siphash_key_t *keyval)
14581450
{
14591451
u32 hash;
14601452

14611453
__flow_hash_consistentify(keys);
14621454

1463-
hash = __flow_hash_words(flow_keys_hash_start(keys),
1464-
flow_keys_hash_length(keys), keyval);
1455+
hash = siphash(flow_keys_hash_start(keys),
1456+
flow_keys_hash_length(keys), keyval);
14651457
if (!hash)
14661458
hash = 1;
14671459

@@ -1471,12 +1463,13 @@ static inline u32 __flow_hash_from_keys(struct flow_keys *keys, u32 keyval)
14711463
u32 flow_hash_from_keys(struct flow_keys *keys)
14721464
{
14731465
__flow_hash_secret_init();
1474-
return __flow_hash_from_keys(keys, hashrnd);
1466+
return __flow_hash_from_keys(keys, &hashrnd);
14751467
}
14761468
EXPORT_SYMBOL(flow_hash_from_keys);
14771469

14781470
static inline u32 ___skb_get_hash(const struct sk_buff *skb,
1479-
struct flow_keys *keys, u32 keyval)
1471+
struct flow_keys *keys,
1472+
const siphash_key_t *keyval)
14801473
{
14811474
skb_flow_dissect_flow_keys(skb, keys,
14821475
FLOW_DISSECTOR_F_STOP_AT_FLOW_LABEL);
@@ -1524,7 +1517,7 @@ u32 __skb_get_hash_symmetric(const struct sk_buff *skb)
15241517
&keys, NULL, 0, 0, 0,
15251518
FLOW_DISSECTOR_F_STOP_AT_FLOW_LABEL);
15261519

1527-
return __flow_hash_from_keys(&keys, hashrnd);
1520+
return __flow_hash_from_keys(&keys, &hashrnd);
15281521
}
15291522
EXPORT_SYMBOL_GPL(__skb_get_hash_symmetric);
15301523

@@ -1544,13 +1537,14 @@ void __skb_get_hash(struct sk_buff *skb)
15441537

15451538
__flow_hash_secret_init();
15461539

1547-
hash = ___skb_get_hash(skb, &keys, hashrnd);
1540+
hash = ___skb_get_hash(skb, &keys, &hashrnd);
15481541

15491542
__skb_set_sw_hash(skb, hash, flow_keys_have_l4(&keys));
15501543
}
15511544
EXPORT_SYMBOL(__skb_get_hash);
15521545

1553-
__u32 skb_get_hash_perturb(const struct sk_buff *skb, u32 perturb)
1546+
__u32 skb_get_hash_perturb(const struct sk_buff *skb,
1547+
const siphash_key_t *perturb)
15541548
{
15551549
struct flow_keys keys;
15561550

net/sched/sch_hhf.c

Lines changed: 4 additions & 4 deletions
Original file line numberDiff line numberDiff line change
@@ -5,11 +5,11 @@
55
* Copyright (C) 2013 Nandita Dukkipati <[email protected]>
66
*/
77

8-
#include <linux/jhash.h>
98
#include <linux/jiffies.h>
109
#include <linux/module.h>
1110
#include <linux/skbuff.h>
1211
#include <linux/vmalloc.h>
12+
#include <linux/siphash.h>
1313
#include <net/pkt_sched.h>
1414
#include <net/sock.h>
1515

@@ -126,7 +126,7 @@ struct wdrr_bucket {
126126

127127
struct hhf_sched_data {
128128
struct wdrr_bucket buckets[WDRR_BUCKET_CNT];
129-
u32 perturbation; /* hash perturbation */
129+
siphash_key_t perturbation; /* hash perturbation */
130130
u32 quantum; /* psched_mtu(qdisc_dev(sch)); */
131131
u32 drop_overlimit; /* number of times max qdisc packet
132132
* limit was hit
@@ -264,7 +264,7 @@ static enum wdrr_bucket_idx hhf_classify(struct sk_buff *skb, struct Qdisc *sch)
264264
}
265265

266266
/* Get hashed flow-id of the skb. */
267-
hash = skb_get_hash_perturb(skb, q->perturbation);
267+
hash = skb_get_hash_perturb(skb, &q->perturbation);
268268

269269
/* Check if this packet belongs to an already established HH flow. */
270270
flow_pos = hash & HHF_BIT_MASK;
@@ -582,7 +582,7 @@ static int hhf_init(struct Qdisc *sch, struct nlattr *opt,
582582

583583
sch->limit = 1000;
584584
q->quantum = psched_mtu(qdisc_dev(sch));
585-
q->perturbation = prandom_u32();
585+
get_random_bytes(&q->perturbation, sizeof(q->perturbation));
586586
INIT_LIST_HEAD(&q->new_buckets);
587587
INIT_LIST_HEAD(&q->old_buckets);
588588

net/sched/sch_sfb.c

Lines changed: 7 additions & 6 deletions
Original file line numberDiff line numberDiff line change
@@ -18,7 +18,7 @@
1818
#include <linux/errno.h>
1919
#include <linux/skbuff.h>
2020
#include <linux/random.h>
21-
#include <linux/jhash.h>
21+
#include <linux/siphash.h>
2222
#include <net/ip.h>
2323
#include <net/pkt_sched.h>
2424
#include <net/pkt_cls.h>
@@ -45,7 +45,7 @@ struct sfb_bucket {
4545
* (Section 4.4 of SFB reference : moving hash functions)
4646
*/
4747
struct sfb_bins {
48-
u32 perturbation; /* jhash perturbation */
48+
siphash_key_t perturbation; /* siphash key */
4949
struct sfb_bucket bins[SFB_LEVELS][SFB_NUMBUCKETS];
5050
};
5151

@@ -217,7 +217,8 @@ static u32 sfb_compute_qlen(u32 *prob_r, u32 *avgpm_r, const struct sfb_sched_da
217217

218218
static void sfb_init_perturbation(u32 slot, struct sfb_sched_data *q)
219219
{
220-
q->bins[slot].perturbation = prandom_u32();
220+
get_random_bytes(&q->bins[slot].perturbation,
221+
sizeof(q->bins[slot].perturbation));
221222
}
222223

223224
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,
314315
/* If using external classifiers, get result and record it. */
315316
if (!sfb_classify(skb, fl, &ret, &salt))
316317
goto other_drop;
317-
sfbhash = jhash_1word(salt, q->bins[slot].perturbation);
318+
sfbhash = siphash_1u32(salt, &q->bins[slot].perturbation);
318319
} else {
319-
sfbhash = skb_get_hash_perturb(skb, q->bins[slot].perturbation);
320+
sfbhash = skb_get_hash_perturb(skb, &q->bins[slot].perturbation);
320321
}
321322

322323

@@ -352,7 +353,7 @@ static int sfb_enqueue(struct sk_buff *skb, struct Qdisc *sch,
352353
/* Inelastic flow */
353354
if (q->double_buffering) {
354355
sfbhash = skb_get_hash_perturb(skb,
355-
q->bins[slot].perturbation);
356+
&q->bins[slot].perturbation);
356357
if (!sfbhash)
357358
sfbhash = 1;
358359
sfb_skb_cb(skb)->hashes[slot] = sfbhash;

net/sched/sch_sfq.c

Lines changed: 8 additions & 6 deletions
Original file line numberDiff line numberDiff line change
@@ -14,7 +14,7 @@
1414
#include <linux/errno.h>
1515
#include <linux/init.h>
1616
#include <linux/skbuff.h>
17-
#include <linux/jhash.h>
17+
#include <linux/siphash.h>
1818
#include <linux/slab.h>
1919
#include <linux/vmalloc.h>
2020
#include <net/netlink.h>
@@ -117,7 +117,7 @@ struct sfq_sched_data {
117117
u8 headdrop;
118118
u8 maxdepth; /* limit of packets per flow */
119119

120-
u32 perturbation;
120+
siphash_key_t perturbation;
121121
u8 cur_depth; /* depth of longest slot */
122122
u8 flags;
123123
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
157157
static unsigned int sfq_hash(const struct sfq_sched_data *q,
158158
const struct sk_buff *skb)
159159
{
160-
return skb_get_hash_perturb(skb, q->perturbation) & (q->divisor - 1);
160+
return skb_get_hash_perturb(skb, &q->perturbation) & (q->divisor - 1);
161161
}
162162

163163
static unsigned int sfq_classify(struct sk_buff *skb, struct Qdisc *sch,
@@ -607,9 +607,11 @@ static void sfq_perturbation(struct timer_list *t)
607607
struct sfq_sched_data *q = from_timer(q, t, perturb_timer);
608608
struct Qdisc *sch = q->sch;
609609
spinlock_t *root_lock = qdisc_lock(qdisc_root_sleeping(sch));
610+
siphash_key_t nkey;
610611

612+
get_random_bytes(&nkey, sizeof(nkey));
611613
spin_lock(root_lock);
612-
q->perturbation = prandom_u32();
614+
q->perturbation = nkey;
613615
if (!q->filter_list && q->tail)
614616
sfq_rehash(sch);
615617
spin_unlock(root_lock);
@@ -688,7 +690,7 @@ static int sfq_change(struct Qdisc *sch, struct nlattr *opt)
688690
del_timer(&q->perturb_timer);
689691
if (q->perturb_period) {
690692
mod_timer(&q->perturb_timer, jiffies + q->perturb_period);
691-
q->perturbation = prandom_u32();
693+
get_random_bytes(&q->perturbation, sizeof(q->perturbation));
692694
}
693695
sch_tree_unlock(sch);
694696
kfree(p);
@@ -745,7 +747,7 @@ static int sfq_init(struct Qdisc *sch, struct nlattr *opt,
745747
q->quantum = psched_mtu(qdisc_dev(sch));
746748
q->scaled_quantum = SFQ_ALLOT_SIZE(q->quantum);
747749
q->perturb_period = 0;
748-
q->perturbation = prandom_u32();
750+
get_random_bytes(&q->perturbation, sizeof(q->perturbation));
749751

750752
if (opt) {
751753
int err = sfq_change(sch, opt);

0 commit comments

Comments
 (0)