Skip to content

Commit f5817ff

Browse files
committed
Merge: CNB96: net: Allow configuration of multipath hash seed
MR: https://gitlab.com/redhat/centos-stream/src/kernel/centos-stream-9/-/merge_requests/5270 JIRA: https://issues.redhat.com/browse/RHEL-59087 Tested: Using attached `net/forwarding/router_mpath_seed.sh` self-test Depends: !5198 Commits: ``` 3e453ca net: ipv4,ipv6: Pass multipath hash computation through a helper 4ee2a8c net: ipv4: Add a sysctl to set multipath hash seed 6f51aed selftests: forwarding: lib: Split sysctl_save() out of sysctl_set() 5f90d93 selftests: forwarding: router_mpath_hash: Add a new selftest ``` Signed-off-by: Ivan Vecera <ivecera@redhat.com> Approved-by: Ivan Vecera <ivecera@redhat.com> Approved-by: Petr Oros <poros@redhat.com> Approved-by: CKI KWF Bot <cki-ci-bot+kwf-gitlab-com@redhat.com> Merged-by: Rado Vrbovsky <rvrbovsk@redhat.com>
2 parents 9ec39da + 404eb5e commit f5817ff

File tree

11 files changed

+479
-13
lines changed

11 files changed

+479
-13
lines changed

Documentation/networking/ip-sysctl.rst

Lines changed: 14 additions & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -129,6 +129,20 @@ fib_multipath_hash_fields - UNSIGNED INTEGER
129129

130130
Default: 0x0007 (source IP, destination IP and IP protocol)
131131

132+
fib_multipath_hash_seed - UNSIGNED INTEGER
133+
The seed value used when calculating hash for multipath routes. Applies
134+
to both IPv4 and IPv6 datapath. Only present for kernels built with
135+
CONFIG_IP_ROUTE_MULTIPATH enabled.
136+
137+
When set to 0, the seed value used for multipath routing defaults to an
138+
internal random-generated one.
139+
140+
The actual hashing algorithm is not specified -- there is no guarantee
141+
that a next hop distribution effected by a given seed will keep stable
142+
across kernel versions.
143+
144+
Default: 0 (random)
145+
132146
fib_sync_mem - UNSIGNED INTEGER
133147
Amount of dirty memory from fib entries that can be backlogged before
134148
synchronize_rcu is forced.

include/net/flow_dissector.h

Lines changed: 2 additions & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -433,6 +433,8 @@ static inline bool flow_keys_have_l4(const struct flow_keys *keys)
433433
}
434434

435435
u32 flow_hash_from_keys(struct flow_keys *keys);
436+
u32 flow_hash_from_keys_seed(struct flow_keys *keys,
437+
const siphash_key_t *keyval);
436438
void skb_flow_get_icmp_tci(const struct sk_buff *skb,
437439
struct flow_dissector_key_icmp *key_icmp,
438440
const void *data, int thoff, int hlen);

include/net/ip_fib.h

Lines changed: 28 additions & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -532,7 +532,35 @@ void fib_nhc_update_mtu(struct fib_nh_common *nhc, u32 new, u32 orig);
532532
#ifdef CONFIG_IP_ROUTE_MULTIPATH
533533
int fib_multipath_hash(const struct net *net, const struct flowi4 *fl4,
534534
const struct sk_buff *skb, struct flow_keys *flkeys);
535+
536+
static void
537+
fib_multipath_hash_construct_key(siphash_key_t *key, u32 mp_seed)
538+
{
539+
u64 mp_seed_64 = mp_seed;
540+
541+
key->key[0] = (mp_seed_64 << 32) | mp_seed_64;
542+
key->key[1] = key->key[0];
543+
}
544+
545+
static inline u32 fib_multipath_hash_from_keys(const struct net *net,
546+
struct flow_keys *keys)
547+
{
548+
siphash_aligned_key_t hash_key;
549+
u32 mp_seed;
550+
551+
mp_seed = READ_ONCE(net->ipv4.sysctl_fib_multipath_hash_seed).mp_seed;
552+
fib_multipath_hash_construct_key(&hash_key, mp_seed);
553+
554+
return flow_hash_from_keys_seed(keys, &hash_key);
555+
}
556+
#else
557+
static inline u32 fib_multipath_hash_from_keys(const struct net *net,
558+
struct flow_keys *keys)
559+
{
560+
return flow_hash_from_keys(keys);
561+
}
535562
#endif
563+
536564
int fib_check_nh(struct net *net, struct fib_nh *nh, u32 table, u8 scope,
537565
struct netlink_ext_ack *extack);
538566
void fib_select_multipath(struct fib_result *res, int hash);

include/net/netns/ipv4.h

Lines changed: 8 additions & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -41,6 +41,13 @@ struct inet_timewait_death_row {
4141

4242
struct tcp_fastopen_context;
4343

44+
#ifdef CONFIG_IP_ROUTE_MULTIPATH
45+
struct sysctl_fib_multipath_hash_seed {
46+
u32 user_seed;
47+
u32 mp_seed;
48+
};
49+
#endif
50+
4451
struct netns_ipv4 {
4552
/* Cacheline organization can be found documented in
4653
* Documentation/networking/net_cachelines/netns_ipv4_sysctl.rst.
@@ -229,6 +236,7 @@ struct netns_ipv4 {
229236
#endif
230237
#endif
231238
#ifdef CONFIG_IP_ROUTE_MULTIPATH
239+
struct sysctl_fib_multipath_hash_seed sysctl_fib_multipath_hash_seed;
232240
u32 sysctl_fib_multipath_hash_fields;
233241
u8 sysctl_fib_multipath_use_neigh;
234242
u8 sysctl_fib_multipath_hash_policy;

net/core/flow_dissector.c

Lines changed: 7 additions & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -1790,6 +1790,13 @@ u32 flow_hash_from_keys(struct flow_keys *keys)
17901790
}
17911791
EXPORT_SYMBOL(flow_hash_from_keys);
17921792

1793+
u32 flow_hash_from_keys_seed(struct flow_keys *keys,
1794+
const siphash_key_t *keyval)
1795+
{
1796+
return __flow_hash_from_keys(keys, keyval);
1797+
}
1798+
EXPORT_SYMBOL(flow_hash_from_keys_seed);
1799+
17931800
static inline u32 ___skb_get_hash(const struct sk_buff *skb,
17941801
struct flow_keys *keys,
17951802
const siphash_key_t *keyval)

net/ipv4/route.c

Lines changed: 6 additions & 6 deletions
Original file line numberDiff line numberDiff line change
@@ -1965,7 +1965,7 @@ static u32 fib_multipath_custom_hash_outer(const struct net *net,
19651965
hash_keys.ports.dst = keys.ports.dst;
19661966

19671967
*p_has_inner = !!(keys.control.flags & FLOW_DIS_ENCAPSULATION);
1968-
return flow_hash_from_keys(&hash_keys);
1968+
return fib_multipath_hash_from_keys(net, &hash_keys);
19691969
}
19701970

19711971
static u32 fib_multipath_custom_hash_inner(const struct net *net,
@@ -2014,7 +2014,7 @@ static u32 fib_multipath_custom_hash_inner(const struct net *net,
20142014
if (hash_fields & FIB_MULTIPATH_HASH_FIELD_INNER_DST_PORT)
20152015
hash_keys.ports.dst = keys.ports.dst;
20162016

2017-
return flow_hash_from_keys(&hash_keys);
2017+
return fib_multipath_hash_from_keys(net, &hash_keys);
20182018
}
20192019

20202020
static u32 fib_multipath_custom_hash_skb(const struct net *net,
@@ -2051,7 +2051,7 @@ static u32 fib_multipath_custom_hash_fl4(const struct net *net,
20512051
if (hash_fields & FIB_MULTIPATH_HASH_FIELD_DST_PORT)
20522052
hash_keys.ports.dst = fl4->fl4_dport;
20532053

2054-
return flow_hash_from_keys(&hash_keys);
2054+
return fib_multipath_hash_from_keys(net, &hash_keys);
20552055
}
20562056

20572057
/* if skb is set it will be used and fl4 can be NULL */
@@ -2072,7 +2072,7 @@ int fib_multipath_hash(const struct net *net, const struct flowi4 *fl4,
20722072
hash_keys.addrs.v4addrs.src = fl4->saddr;
20732073
hash_keys.addrs.v4addrs.dst = fl4->daddr;
20742074
}
2075-
mhash = flow_hash_from_keys(&hash_keys);
2075+
mhash = fib_multipath_hash_from_keys(net, &hash_keys);
20762076
break;
20772077
case 1:
20782078
/* skb is currently provided only when forwarding */
@@ -2106,7 +2106,7 @@ int fib_multipath_hash(const struct net *net, const struct flowi4 *fl4,
21062106
hash_keys.ports.dst = fl4->fl4_dport;
21072107
hash_keys.basic.ip_proto = fl4->flowi4_proto;
21082108
}
2109-
mhash = flow_hash_from_keys(&hash_keys);
2109+
mhash = fib_multipath_hash_from_keys(net, &hash_keys);
21102110
break;
21112111
case 2:
21122112
memset(&hash_keys, 0, sizeof(hash_keys));
@@ -2137,7 +2137,7 @@ int fib_multipath_hash(const struct net *net, const struct flowi4 *fl4,
21372137
hash_keys.addrs.v4addrs.src = fl4->saddr;
21382138
hash_keys.addrs.v4addrs.dst = fl4->daddr;
21392139
}
2140-
mhash = flow_hash_from_keys(&hash_keys);
2140+
mhash = fib_multipath_hash_from_keys(net, &hash_keys);
21412141
break;
21422142
case 3:
21432143
if (skb)

net/ipv4/sysctl_net_ipv4.c

Lines changed: 66 additions & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -427,6 +427,61 @@ static int proc_fib_multipath_hash_fields(struct ctl_table *table, int write,
427427

428428
return ret;
429429
}
430+
431+
static u32 proc_fib_multipath_hash_rand_seed __ro_after_init;
432+
433+
static void proc_fib_multipath_hash_init_rand_seed(void)
434+
{
435+
get_random_bytes(&proc_fib_multipath_hash_rand_seed,
436+
sizeof(proc_fib_multipath_hash_rand_seed));
437+
}
438+
439+
static void proc_fib_multipath_hash_set_seed(struct net *net, u32 user_seed)
440+
{
441+
struct sysctl_fib_multipath_hash_seed new = {
442+
.user_seed = user_seed,
443+
.mp_seed = (user_seed ? user_seed :
444+
proc_fib_multipath_hash_rand_seed),
445+
};
446+
447+
WRITE_ONCE(net->ipv4.sysctl_fib_multipath_hash_seed, new);
448+
}
449+
450+
static int proc_fib_multipath_hash_seed(struct ctl_table *table, int write,
451+
void *buffer, size_t *lenp,
452+
loff_t *ppos)
453+
{
454+
struct sysctl_fib_multipath_hash_seed *mphs;
455+
struct net *net = table->data;
456+
struct ctl_table tmp;
457+
u32 user_seed;
458+
int ret;
459+
460+
mphs = &net->ipv4.sysctl_fib_multipath_hash_seed;
461+
user_seed = mphs->user_seed;
462+
463+
tmp = *table;
464+
tmp.data = &user_seed;
465+
466+
ret = proc_douintvec_minmax(&tmp, write, buffer, lenp, ppos);
467+
468+
if (write && ret == 0) {
469+
proc_fib_multipath_hash_set_seed(net, user_seed);
470+
call_netevent_notifiers(NETEVENT_IPV4_MPATH_HASH_UPDATE, net);
471+
}
472+
473+
return ret;
474+
}
475+
#else
476+
477+
static void proc_fib_multipath_hash_init_rand_seed(void)
478+
{
479+
}
480+
481+
static void proc_fib_multipath_hash_set_seed(struct net *net, u32 user_seed)
482+
{
483+
}
484+
430485
#endif
431486

432487
static struct ctl_table ipv4_table[] = {
@@ -1036,6 +1091,13 @@ static struct ctl_table ipv4_net_table[] = {
10361091
.extra1 = SYSCTL_ONE,
10371092
.extra2 = &fib_multipath_hash_fields_all_mask,
10381093
},
1094+
{
1095+
.procname = "fib_multipath_hash_seed",
1096+
.data = &init_net,
1097+
.maxlen = sizeof(u32),
1098+
.mode = 0644,
1099+
.proc_handler = proc_fib_multipath_hash_seed,
1100+
},
10391101
#endif
10401102
{
10411103
.procname = "ip_unprivileged_port_start",
@@ -1401,6 +1463,8 @@ static __net_init int ipv4_sysctl_init_net(struct net *net)
14011463
if (!net->ipv4.sysctl_local_reserved_ports)
14021464
goto err_ports;
14031465

1466+
proc_fib_multipath_hash_set_seed(net, 0);
1467+
14041468
return 0;
14051469

14061470
err_ports:
@@ -1435,6 +1499,8 @@ static __init int sysctl_ipv4_init(void)
14351499
if (!hdr)
14361500
return -ENOMEM;
14371501

1502+
proc_fib_multipath_hash_init_rand_seed();
1503+
14381504
if (register_pernet_subsys(&ipv4_sysctl_ops)) {
14391505
unregister_net_sysctl_table(hdr);
14401506
return -ENOMEM;

net/ipv6/route.c

Lines changed: 6 additions & 6 deletions
Original file line numberDiff line numberDiff line change
@@ -2367,7 +2367,7 @@ static u32 rt6_multipath_custom_hash_outer(const struct net *net,
23672367
hash_keys.ports.dst = keys.ports.dst;
23682368

23692369
*p_has_inner = !!(keys.control.flags & FLOW_DIS_ENCAPSULATION);
2370-
return flow_hash_from_keys(&hash_keys);
2370+
return fib_multipath_hash_from_keys(net, &hash_keys);
23712371
}
23722372

23732373
static u32 rt6_multipath_custom_hash_inner(const struct net *net,
@@ -2416,7 +2416,7 @@ static u32 rt6_multipath_custom_hash_inner(const struct net *net,
24162416
if (hash_fields & FIB_MULTIPATH_HASH_FIELD_INNER_DST_PORT)
24172417
hash_keys.ports.dst = keys.ports.dst;
24182418

2419-
return flow_hash_from_keys(&hash_keys);
2419+
return fib_multipath_hash_from_keys(net, &hash_keys);
24202420
}
24212421

24222422
static u32 rt6_multipath_custom_hash_skb(const struct net *net,
@@ -2455,7 +2455,7 @@ static u32 rt6_multipath_custom_hash_fl6(const struct net *net,
24552455
if (hash_fields & FIB_MULTIPATH_HASH_FIELD_DST_PORT)
24562456
hash_keys.ports.dst = fl6->fl6_dport;
24572457

2458-
return flow_hash_from_keys(&hash_keys);
2458+
return fib_multipath_hash_from_keys(net, &hash_keys);
24592459
}
24602460

24612461
/* if skb is set it will be used and fl6 can be NULL */
@@ -2477,7 +2477,7 @@ u32 rt6_multipath_hash(const struct net *net, const struct flowi6 *fl6,
24772477
hash_keys.tags.flow_label = (__force u32)flowi6_get_flowlabel(fl6);
24782478
hash_keys.basic.ip_proto = fl6->flowi6_proto;
24792479
}
2480-
mhash = flow_hash_from_keys(&hash_keys);
2480+
mhash = fib_multipath_hash_from_keys(net, &hash_keys);
24812481
break;
24822482
case 1:
24832483
if (skb) {
@@ -2509,7 +2509,7 @@ u32 rt6_multipath_hash(const struct net *net, const struct flowi6 *fl6,
25092509
hash_keys.ports.dst = fl6->fl6_dport;
25102510
hash_keys.basic.ip_proto = fl6->flowi6_proto;
25112511
}
2512-
mhash = flow_hash_from_keys(&hash_keys);
2512+
mhash = fib_multipath_hash_from_keys(net, &hash_keys);
25132513
break;
25142514
case 2:
25152515
memset(&hash_keys, 0, sizeof(hash_keys));
@@ -2546,7 +2546,7 @@ u32 rt6_multipath_hash(const struct net *net, const struct flowi6 *fl6,
25462546
hash_keys.tags.flow_label = (__force u32)flowi6_get_flowlabel(fl6);
25472547
hash_keys.basic.ip_proto = fl6->flowi6_proto;
25482548
}
2549-
mhash = flow_hash_from_keys(&hash_keys);
2549+
mhash = fib_multipath_hash_from_keys(net, &hash_keys);
25502550
break;
25512551
case 3:
25522552
if (skb)

tools/testing/selftests/net/forwarding/Makefile

Lines changed: 1 addition & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -74,6 +74,7 @@ TEST_PROGS = bridge_fdb_learning_limit.sh \
7474
router_broadcast.sh \
7575
router_mpath_nh_res.sh \
7676
router_mpath_nh.sh \
77+
router_mpath_seed.sh \
7778
router_multicast.sh \
7879
router_multipath.sh \
7980
router_nh.sh \

tools/testing/selftests/net/forwarding/lib.sh

Lines changed: 8 additions & 1 deletion
Original file line numberDiff line numberDiff line change
@@ -943,12 +943,19 @@ bridge_ageing_time_get()
943943
}
944944

945945
declare -A SYSCTL_ORIG
946+
sysctl_save()
947+
{
948+
local key=$1; shift
949+
950+
SYSCTL_ORIG[$key]=$(sysctl -n $key)
951+
}
952+
946953
sysctl_set()
947954
{
948955
local key=$1; shift
949956
local value=$1; shift
950957

951-
SYSCTL_ORIG[$key]=$(sysctl -n $key)
958+
sysctl_save "$key"
952959
sysctl -qw $key="$value"
953960
}
954961

0 commit comments

Comments
 (0)