Commit 1dbdce30 authored by Guillaume Nault's avatar Guillaume Nault Committed by Jakub Kicinski
Browse files

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.

Signed-off-by: default avatarGuillaume Nault <gnault@redhat.com>
Link: https://patch.msgid.link/fd416275262b1f518d5abfcef740ce4f4a1a6522.1734357769.git.gnault@redhat.com


Signed-off-by: default avatarJakub Kicinski <kuba@kernel.org>
parent 6ed34721
Loading
Loading
Loading
Loading
+28 −0
Original line number Diff line number Diff line
@@ -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,
+2 −12
Original line number Diff line number Diff line
@@ -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);