Skip to contentSkip to conversation

ipv4: Consolidate route lookups from IPv4 sockets.

PATCH Applied Guillaume Nault 3 participants 8 messages Dec 16, 2024 – Dec 20, 2024

Subsystems (4)
lore
Share & tools
lore
#0 lore raw

cover ipv4: Consolidate route lookups from IPv4 sockets.

Create inet_sk_init_flowi4() so that the different IPv4 code paths that need to do a route lookup based on an IPv4 socket don't need to reimplement that logic.

Guillaume Nault (5):
ipv4: Define inet_sk_init_flowi4() and use it in
inet_sk_rebuild_header().
ipv4: Use inet_sk_init_flowi4() in ip4_datagram_release_cb().
ipv4: Use inet_sk_init_flowi4() in inet_csk_rebuild_route().
ipv4: Use inet_sk_init_flowi4() in __ip_queue_xmit().
l2tp: Use inet_sk_init_flowi4() in l2tp_ip_sendmsg().

 include/net/route.h             | 28 ++++++++++++++++++++++++++++
 net/ipv4/af_inet.c              | 14 ++------------
 net/ipv4/datagram.c             | 11 ++---------
 net/ipv4/inet_connection_sock.c | 11 ++---------
 net/ipv4/ip_output.c            | 16 ++++------------
 net/l2tp/l2tp_ip.c              | 19 ++++++-------------
 6 files changed, 44 insertions(+), 55 deletions(-)
Signature · 1 line
2.39.2
#1 lore raw

PATCH 1/5 ipv4: Define inet_sk_init_flowi4() and use it in inet_sk_rebuild_header().

IPv4 code commonly has to initialise a flowi4 structure from an IPv4 socket. This requires looking at potential IPv4 options to set the proper destination address, call flowi4_init_output() with the correct set of parameters and run the sk_classify_flow security hook.

Instead of reimplementing these operations in different parts of the stack, let's define inet_sk_init_flowi4() which does all these operations.

The first user is inet_sk_rebuild_header(), where inet_sk_init_flowi4() replaces ip_route_output_ports(). Unlike ip_route_output_ports(), which sets the flowi4 structure and performs the route lookup in one go, inet_sk_init_flowi4() only initialises the flow. The route lookup is then done by ip_route_output_flow(). Decoupling flow initialisation from route lookup makes this new interface applicable more broadly as it will allow some users to overwrite specific struct flowi4 members before the route lookup.

---
 include/net/route.h | 28 ++++++++++++++++++++++++++++
 net/ipv4/af_inet.c  | 14 ++------------
 2 files changed, 30 insertions(+), 12 deletions(-)
include/net/route.h +28 -0
index 84cb1e04f5cd..95805dd4ac13 100644
@@ -28,6 +28,7 @@
 #include <net/arp.h>
 #include <net/ndisc.h>
 #include <net/inet_dscp.h>
+#include <net/sock.h>
 #include <linux/in_route.h>
 #include <linux/rtnetlink.h>
 #include <linux/rcupdate.h>
@@ -129,6 +130,33 @@ struct in_device;
 int ip_rt_init(void);
 void rt_cache_flush(struct net *net);
 void rt_flush_dev(struct net_device *dev);
+
+static inline void inet_sk_init_flowi4(const struct inet_sock *inet,
+				       struct flowi4 *fl4)
+{
+	const struct ip_options_rcu *ip4_opt;
+	const struct sock *sk;
+	__be32 daddr;
+
+	rcu_read_lock();
+	ip4_opt = rcu_dereference(inet->inet_opt);
+
+	/* Source routing option overrides the socket destination address */
+	if (ip4_opt && ip4_opt->opt.srr)
+		daddr = ip4_opt->opt.faddr;
+	else
+		daddr = inet->inet_daddr;
+	rcu_read_unlock();
+
+	sk = &inet->sk;
+	flowi4_init_output(fl4, sk->sk_bound_dev_if, READ_ONCE(sk->sk_mark),
+			   ip_sock_rt_tos(sk), ip_sock_rt_scope(sk),
+			   sk->sk_protocol, inet_sk_flowi_flags(sk), daddr,
+			   inet->inet_saddr, inet->inet_dport,
+			   inet->inet_sport, sk->sk_uid);
+	security_sk_classify_flow(sk, flowi4_to_flowi_common(fl4));
+}
+
 struct rtable *ip_route_output_key_hash(struct net *net, struct flowi4 *flp,
 					const struct sk_buff *skb);
 struct rtable *ip_route_output_key_hash_rcu(struct net *net, struct flowi4 *flp,
net/ipv4/af_inet.c +2 -12
index 8095e82de808..21f46ee7b6e9 100644
@@ -1309,8 +1309,6 @@ int inet_sk_rebuild_header(struct sock *sk)
 {
 	struct rtable *rt = dst_rtable(__sk_dst_check(sk, 0));
 	struct inet_sock *inet = inet_sk(sk);
-	__be32 daddr;
-	struct ip_options_rcu *inet_opt;
 	struct flowi4 *fl4;
 	int err;
 
@@ -1319,17 +1317,9 @@ int inet_sk_rebuild_header(struct sock *sk)
 		return 0;
 
 	/* Reroute. */
-	rcu_read_lock();
-	inet_opt = rcu_dereference(inet->inet_opt);
-	daddr = inet->inet_daddr;
-	if (inet_opt && inet_opt->opt.srr)
-		daddr = inet_opt->opt.faddr;
-	rcu_read_unlock();
 	fl4 = &inet->cork.fl.u.ip4;
-	rt = ip_route_output_ports(sock_net(sk), fl4, sk, daddr, inet->inet_saddr,
-				   inet->inet_dport, inet->inet_sport,
-				   sk->sk_protocol, ip_sock_rt_tos(sk),
-				   sk->sk_bound_dev_if);
+	inet_sk_init_flowi4(inet, fl4);
+	rt = ip_route_output_flow(sock_net(sk), fl4, sk);
 	if (!IS_ERR(rt)) {
 		err = 0;
 		sk_setup_caps(sk, &rt->dst);
Signature · 1 line
2.39.2
#2 lore raw

PATCH 2/5 ipv4: Use inet_sk_init_flowi4() in ip4_datagram_release_cb().

In reply to Guillaume Nault

Use inet_sk_init_flowi4() to automatically initialise the flowi4 structure in ip4_datagram_release_cb() instead of passing parameters manually to ip_route_output_ports().

---
 net/ipv4/datagram.c | 11 ++---------
 1 file changed, 2 insertions(+), 9 deletions(-)
net/ipv4/datagram.c +2 -9
index 4aca1f05edd3..4b5bc6eb52e7 100644
@@ -102,8 +102,6 @@ EXPORT_SYMBOL(ip4_datagram_connect);
 void ip4_datagram_release_cb(struct sock *sk)
 {
 	const struct inet_sock *inet = inet_sk(sk);
-	const struct ip_options_rcu *inet_opt;
-	__be32 daddr = inet->inet_daddr;
 	struct dst_entry *dst;
 	struct flowi4 fl4;
 	struct rtable *rt;
@@ -115,14 +113,9 @@ void ip4_datagram_release_cb(struct sock *sk)
 		rcu_read_unlock();
 		return;
 	}
-	inet_opt = rcu_dereference(inet->inet_opt);
-	if (inet_opt && inet_opt->opt.srr)
-		daddr = inet_opt->opt.faddr;
-	rt = ip_route_output_ports(sock_net(sk), &fl4, sk, daddr,
-				   inet->inet_saddr, inet->inet_dport,
-				   inet->inet_sport, sk->sk_protocol,
-				   ip_sock_rt_tos(sk), sk->sk_bound_dev_if);
 
+	inet_sk_init_flowi4(inet, &fl4);
+	rt = ip_route_output_flow(sock_net(sk), &fl4, sk);
 	dst = !IS_ERR(rt) ? &rt->dst : NULL;
 	sk_dst_set(sk, dst);
 
Signature · 1 line
2.39.2
#3 lore raw

PATCH 3/5 ipv4: Use inet_sk_init_flowi4() in inet_csk_rebuild_route().

In reply to Guillaume Nault

Use inet_sk_init_flowi4() to automatically initialise the flowi4 structure in inet_csk_rebuild_route() instead of passing parameters manually to ip_route_output_ports().

---
 net/ipv4/inet_connection_sock.c | 11 ++---------
 1 file changed, 2 insertions(+), 9 deletions(-)
net/ipv4/inet_connection_sock.c +2 -9
index 6872b5aff73e..e4decfb270fa 100644
@@ -1561,20 +1561,13 @@ EXPORT_SYMBOL_GPL(inet_csk_addr2sockaddr);
 static struct dst_entry *inet_csk_rebuild_route(struct sock *sk, struct flowi *fl)
 {
 	const struct inet_sock *inet = inet_sk(sk);
-	const struct ip_options_rcu *inet_opt;
-	__be32 daddr = inet->inet_daddr;
 	struct flowi4 *fl4;
 	struct rtable *rt;
 
 	rcu_read_lock();
-	inet_opt = rcu_dereference(inet->inet_opt);
-	if (inet_opt && inet_opt->opt.srr)
-		daddr = inet_opt->opt.faddr;
 	fl4 = &fl->u.ip4;
-	rt = ip_route_output_ports(sock_net(sk), fl4, sk, daddr,
-				   inet->inet_saddr, inet->inet_dport,
-				   inet->inet_sport, sk->sk_protocol,
-				   ip_sock_rt_tos(sk), sk->sk_bound_dev_if);
+	inet_sk_init_flowi4(inet, fl4);
+	rt = ip_route_output_flow(sock_net(sk), fl4, sk);
 	if (IS_ERR(rt))
 		rt = NULL;
 	if (rt)
Signature · 1 line
2.39.2
#4 lore raw

PATCH 4/5 ipv4: Use inet_sk_init_flowi4() in __ip_queue_xmit().

In reply to Guillaume Nault

Use inet_sk_init_flowi4() to automatically initialise the flowi4 structure in __ip_queue_xmit() instead of passing parameters manually to ip_route_output_ports().

Override ->flowi4_tos with the value passed as parameter since that's required by SCTP.

---
 net/ipv4/ip_output.c | 16 ++++------------
 1 file changed, 4 insertions(+), 12 deletions(-)
net/ipv4/ip_output.c +4 -12
index a59204a8d850..ce3b65d75bae 100644
@@ -478,24 +478,16 @@ int __ip_queue_xmit(struct sock *sk, struct sk_buff *skb, struct flowi *fl,
 	/* Make sure we can route this packet. */
 	rt = dst_rtable(__sk_dst_check(sk, 0));
 	if (!rt) {
-		__be32 daddr;
+		inet_sk_init_flowi4(inet, fl4);
 
-		/* Use correct destination address if we have options. */
-		daddr = inet->inet_daddr;
-		if (inet_opt && inet_opt->opt.srr)
-			daddr = inet_opt->opt.faddr;
+		/* sctp_v4_xmit() uses its own DSCP value */
+		fl4->flowi4_tos = tos & INET_DSCP_MASK;
 
 		/* If this fails, retransmit mechanism of transport layer will
 		 * keep trying until route appears or the connection times
 		 * itself out.
 		 */
-		rt = ip_route_output_ports(net, fl4, sk,
-					   daddr, inet->inet_saddr,
-					   inet->inet_dport,
-					   inet->inet_sport,
-					   sk->sk_protocol,
-					   tos & INET_DSCP_MASK,
-					   sk->sk_bound_dev_if);
+		rt = ip_route_output_flow(net, fl4, sk);
 		if (IS_ERR(rt))
 			goto no_route;
 		sk_setup_caps(sk, &rt->dst);
Signature · 1 line
2.39.2
#5 lore raw

PATCH 5/5 l2tp: Use inet_sk_init_flowi4() in l2tp_ip_sendmsg().

In reply to Guillaume Nault

Use inet_sk_init_flowi4() to automatically initialise the flowi4 structure in l2tp_ip_sendmsg() instead of passing parameters manually to ip_route_output_ports().

Override ->daddr with the value passed in the msghdr structure if provided.

---
 net/l2tp/l2tp_ip.c | 19 ++++++-------------
 1 file changed, 6 insertions(+), 13 deletions(-)
net/l2tp/l2tp_ip.c +6 -13
index 4bc24fddfd52..29795d2839e8 100644
@@ -425,7 +425,6 @@ static int l2tp_ip_sendmsg(struct sock *sk, struct msghdr *msg, size_t len)
 	int rc;
 	struct inet_sock *inet = inet_sk(sk);
 	struct rtable *rt = NULL;
-	struct flowi4 *fl4;
 	int connected = 0;
 	__be32 daddr;
 
@@ -455,7 +454,6 @@ static int l2tp_ip_sendmsg(struct sock *sk, struct msghdr *msg, size_t len)
 		if (sk->sk_state != TCP_ESTABLISHED)
 			goto out;
 
-		daddr = inet->inet_daddr;
 		connected = 1;
 	}
 
@@ -482,29 +480,24 @@ static int l2tp_ip_sendmsg(struct sock *sk, struct msghdr *msg, size_t len)
 		goto error;
 	}
 
-	fl4 = &inet->cork.fl.u.ip4;
 	if (connected)
 		rt = dst_rtable(__sk_dst_check(sk, 0));
 
 	rcu_read_lock();
 	if (!rt) {
-		const struct ip_options_rcu *inet_opt;
+		struct flowi4 *fl4 = &inet->cork.fl.u.ip4;
 
-		inet_opt = rcu_dereference(inet->inet_opt);
+		inet_sk_init_flowi4(inet, fl4);
 
-		/* Use correct destination address if we have options. */
-		if (inet_opt && inet_opt->opt.srr)
-			daddr = inet_opt->opt.faddr;
+		/* Overwrite ->daddr if msg->msg_name was provided */
+		if (!connected)
+			fl4->daddr = daddr;
 
 		/* If this fails, retransmit mechanism of transport layer will
 		 * keep trying until route appears or the connection times
 		 * itself out.
 		 */
-		rt = ip_route_output_ports(sock_net(sk), fl4, sk,
-					   daddr, inet->inet_saddr,
-					   inet->inet_dport, inet->inet_sport,
-					   sk->sk_protocol, ip_sock_rt_tos(sk),
-					   sk->sk_bound_dev_if);
+		rt = ip_route_output_flow(sock_net(sk), fl4, sk);
 		if (IS_ERR(rt))
 			goto no_route;
 		if (connected) {
Signature · 1 line
2.39.2
Reviewed-by on v1 5/5
#6 lore raw

Re: [PATCH net-next 5/5] l2tp: Use inet_sk_init_flowi4() in l2tp_ip_sendmsg().

Quoted 8 lines · Guillaume Nault

Use inet_sk_init_flowi4() to automatically initialise the flowi4 structure in l2tp_ip_sendmsg() instead of passing parameters manually to ip_route_output_ports().

Override ->daddr with the value passed in the msghdr structure if provided.

This all looks good to me.

#7 lore raw
In reply to Guillaume Nault
This series was applied to netdev/net-next.git (main) by Jakub Kicinski <[email protected]>:

Hello:

This series was applied to netdev/net-next.git (main) by Jakub Kicinski <[email protected]>:

Quoted 12 lines · On Mon, 16 Dec 2024 18:21:40 +0100 you wrote:

Create inet_sk_init_flowi4() so that the different IPv4 code paths that need to do a route lookup based on an IPv4 socket don't need to reimplement that logic.

Guillaume Nault (5):
ipv4: Define inet_sk_init_flowi4() and use it in
inet_sk_rebuild_header().
ipv4: Use inet_sk_init_flowi4() in ip4_datagram_release_cb().
ipv4: Use inet_sk_init_flowi4() in inet_csk_rebuild_route().
ipv4: Use inet_sk_init_flowi4() in __ip_queue_xmit().
l2tp: Use inet_sk_init_flowi4() in l2tp_ip_sendmsg().

Quoted 1 line

[...]

Here is the summary with links:
- [net-next,1/5] ipv4: Define inet_sk_init_flowi4() and use it in inet_sk_rebuild_header().

    https://git.kernel.org/netdev/net-next/c/1dbdce30f040
  - [net-next,2/5] ipv4: Use inet_sk_init_flowi4() in ip4_datagram_release_cb().
    https://git.kernel.org/netdev/net-next/c/5be1323b5041
  - [net-next,3/5] ipv4: Use inet_sk_init_flowi4() in inet_csk_rebuild_route().
    https://git.kernel.org/netdev/net-next/c/42e5ffc385f3
  - [net-next,4/5] ipv4: Use inet_sk_init_flowi4() in __ip_queue_xmit().
    https://git.kernel.org/netdev/net-next/c/148721f8e04a
  - [net-next,5/5] l2tp: Use inet_sk_init_flowi4() in l2tp_ip_sendmsg().
    https://git.kernel.org/netdev/net-next/c/c63e9f3b89d3

You are awesome, thank you!

Signature · 2 lines
Deet-doot-dot, I am a bot.
https://korg.docs.kernel.org/patchwork/pwbot.html