Skip to content

Commit 65e9024

Browse files
wdebruijPaolo Abeni
authored andcommitted
ip: load balance tcp connections to single dst addr and port
Load balance new TCP connections across nexthops also when they connect to the same service at a single remote address and port. This affects only port-based multipath hashing: fib_multipath_hash_policy 1 or 3. Local connections must choose both a source address and port when connecting to a remote service, in ip_route_connect. This "chicken-and-egg problem" (commit 2d7192d ("ipv4: Sanitize and simplify ip_route_{connect,newports}()")) is resolved by first selecting a source address, by looking up a route using the zero wildcard source port and address. As a result multiple connections to the same destination address and port have no entropy in fib_multipath_hash. This is not a problem when forwarding, as skb-based hashing has a 4-tuple. Nor when establishing UDP connections, as autobind there selects a port before reaching ip_route_connect. Load balance also TCP, by using a random port in fib_multipath_hash. Port assignment in inet_hash_connect is not atomic with ip_route_connect. Thus ports are unpredictable, effectively random. Implementation details: Do not actually pass a random fl4_sport, as that affects not only hashing, but routing more broadly, and can match a source port based policy route, which existing wildcard port 0 will not. Instead, define a new wildcard flowi flag that is used only for hashing. Selecting a random source is equivalent to just selecting a random hash entirely. But for code clarity, follow the normal 4-tuple hash process and only update this field. fib_multipath_hash can be reached with zero sport from other code paths, so explicitly pass this flowi flag, rather than trying to infer this case in the function itself. Signed-off-by: Willem de Bruijn <[email protected]> Reviewed-by: David Ahern <[email protected]> Reviewed-by: Eric Dumazet <[email protected]> Reviewed-by: Ido Schimmel <[email protected]> Link: https://patch.msgid.link/[email protected] Signed-off-by: Paolo Abeni <[email protected]>
1 parent 32607a3 commit 65e9024

File tree

5 files changed

+26
-6
lines changed

5 files changed

+26
-6
lines changed

include/net/flow.h

Lines changed: 1 addition & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -39,6 +39,7 @@ struct flowi_common {
3939
#define FLOWI_FLAG_ANYSRC 0x01
4040
#define FLOWI_FLAG_KNOWN_NH 0x02
4141
#define FLOWI_FLAG_L3MDEV_OIF 0x04
42+
#define FLOWI_FLAG_ANY_SPORT 0x08
4243
__u32 flowic_secid;
4344
kuid_t flowic_uid;
4445
__u32 flowic_multipath_hash;

include/net/route.h

Lines changed: 3 additions & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -326,6 +326,9 @@ static inline void ip_route_connect_init(struct flowi4 *fl4, __be32 dst,
326326
if (inet_test_bit(TRANSPARENT, sk))
327327
flow_flags |= FLOWI_FLAG_ANYSRC;
328328

329+
if (IS_ENABLED(CONFIG_IP_ROUTE_MULTIPATH) && !sport)
330+
flow_flags |= FLOWI_FLAG_ANY_SPORT;
331+
329332
flowi4_init_output(fl4, oif, READ_ONCE(sk->sk_mark), ip_sock_rt_tos(sk),
330333
ip_sock_rt_scope(sk), protocol, flow_flags, dst,
331334
src, dport, sport, sk->sk_uid);

net/ipv4/route.c

Lines changed: 10 additions & 3 deletions
Original file line numberDiff line numberDiff line change
@@ -2037,8 +2037,12 @@ static u32 fib_multipath_custom_hash_fl4(const struct net *net,
20372037
hash_keys.addrs.v4addrs.dst = fl4->daddr;
20382038
if (hash_fields & FIB_MULTIPATH_HASH_FIELD_IP_PROTO)
20392039
hash_keys.basic.ip_proto = fl4->flowi4_proto;
2040-
if (hash_fields & FIB_MULTIPATH_HASH_FIELD_SRC_PORT)
2041-
hash_keys.ports.src = fl4->fl4_sport;
2040+
if (hash_fields & FIB_MULTIPATH_HASH_FIELD_SRC_PORT) {
2041+
if (fl4->flowi4_flags & FLOWI_FLAG_ANY_SPORT)
2042+
hash_keys.ports.src = (__force __be16)get_random_u16();
2043+
else
2044+
hash_keys.ports.src = fl4->fl4_sport;
2045+
}
20422046
if (hash_fields & FIB_MULTIPATH_HASH_FIELD_DST_PORT)
20432047
hash_keys.ports.dst = fl4->fl4_dport;
20442048

@@ -2093,7 +2097,10 @@ int fib_multipath_hash(const struct net *net, const struct flowi4 *fl4,
20932097
hash_keys.control.addr_type = FLOW_DISSECTOR_KEY_IPV4_ADDRS;
20942098
hash_keys.addrs.v4addrs.src = fl4->saddr;
20952099
hash_keys.addrs.v4addrs.dst = fl4->daddr;
2096-
hash_keys.ports.src = fl4->fl4_sport;
2100+
if (fl4->flowi4_flags & FLOWI_FLAG_ANY_SPORT)
2101+
hash_keys.ports.src = (__force __be16)get_random_u16();
2102+
else
2103+
hash_keys.ports.src = fl4->fl4_sport;
20972104
hash_keys.ports.dst = fl4->fl4_dport;
20982105
hash_keys.basic.ip_proto = fl4->flowi4_proto;
20992106
}

net/ipv6/route.c

Lines changed: 10 additions & 3 deletions
Original file line numberDiff line numberDiff line change
@@ -2492,8 +2492,12 @@ static u32 rt6_multipath_custom_hash_fl6(const struct net *net,
24922492
hash_keys.basic.ip_proto = fl6->flowi6_proto;
24932493
if (hash_fields & FIB_MULTIPATH_HASH_FIELD_FLOWLABEL)
24942494
hash_keys.tags.flow_label = (__force u32)flowi6_get_flowlabel(fl6);
2495-
if (hash_fields & FIB_MULTIPATH_HASH_FIELD_SRC_PORT)
2496-
hash_keys.ports.src = fl6->fl6_sport;
2495+
if (hash_fields & FIB_MULTIPATH_HASH_FIELD_SRC_PORT) {
2496+
if (fl6->flowi6_flags & FLOWI_FLAG_ANY_SPORT)
2497+
hash_keys.ports.src = (__force __be16)get_random_u16();
2498+
else
2499+
hash_keys.ports.src = fl6->fl6_sport;
2500+
}
24972501
if (hash_fields & FIB_MULTIPATH_HASH_FIELD_DST_PORT)
24982502
hash_keys.ports.dst = fl6->fl6_dport;
24992503

@@ -2547,7 +2551,10 @@ u32 rt6_multipath_hash(const struct net *net, const struct flowi6 *fl6,
25472551
hash_keys.control.addr_type = FLOW_DISSECTOR_KEY_IPV6_ADDRS;
25482552
hash_keys.addrs.v6addrs.src = fl6->saddr;
25492553
hash_keys.addrs.v6addrs.dst = fl6->daddr;
2550-
hash_keys.ports.src = fl6->fl6_sport;
2554+
if (fl6->flowi6_flags & FLOWI_FLAG_ANY_SPORT)
2555+
hash_keys.ports.src = (__force __be16)get_random_u16();
2556+
else
2557+
hash_keys.ports.src = fl6->fl6_sport;
25512558
hash_keys.ports.dst = fl6->fl6_dport;
25522559
hash_keys.basic.ip_proto = fl6->flowi6_proto;
25532560
}

net/ipv6/tcp_ipv6.c

Lines changed: 2 additions & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -267,6 +267,8 @@ static int tcp_v6_connect(struct sock *sk, struct sockaddr *uaddr,
267267
fl6.flowi6_mark = sk->sk_mark;
268268
fl6.fl6_dport = usin->sin6_port;
269269
fl6.fl6_sport = inet->inet_sport;
270+
if (IS_ENABLED(CONFIG_IP_ROUTE_MULTIPATH) && !fl6.fl6_sport)
271+
fl6.flowi6_flags = FLOWI_FLAG_ANY_SPORT;
270272
fl6.flowi6_uid = sk->sk_uid;
271273

272274
opt = rcu_dereference_protected(np->opt, lockdep_sock_is_held(sk));

0 commit comments

Comments
 (0)