2b1dc6285c
Preparation patch, extra arg is not used. No functional changes intended. This is needed to replace the xfrm session decode functions with the flow dissector. skb_flow_dissect() cannot be used as-is, because it attempts to deduce the 'struct net' to use for bpf program fetch from skb->sk or skb->dev, but xfrm code path can see skbs that have neither sk or dev filled in. So either flow dissector needs to try harder, e.g. by also trying skb->dst->dev, or we have to pass the struct net explicitly. Passing the struct net doesn't look too bad to me, most places already have it available or can derive it from the output device. Reported-by: kernel test robot <oliver.sang@intel.com> Link: https://lore.kernel.org/netdev/202309271628.27fd2187-oliver.sang@intel.com/ Signed-off-by: Florian Westphal <fw@strlen.de> Reviewed-by: Simon Horman <horms@kernel.org> Signed-off-by: Steffen Klassert <steffen.klassert@secunet.com>
96 lines
2.6 KiB
C
96 lines
2.6 KiB
C
/*
|
|
* IPv4 specific functions of netfilter core
|
|
*
|
|
* Rusty Russell (C) 2000 -- This code is GPL.
|
|
* Patrick McHardy (C) 2006-2012
|
|
*/
|
|
#include <linux/kernel.h>
|
|
#include <linux/netfilter.h>
|
|
#include <linux/netfilter_ipv4.h>
|
|
#include <linux/ip.h>
|
|
#include <linux/skbuff.h>
|
|
#include <linux/gfp.h>
|
|
#include <linux/export.h>
|
|
#include <net/route.h>
|
|
#include <net/xfrm.h>
|
|
#include <net/ip.h>
|
|
#include <net/netfilter/nf_queue.h>
|
|
|
|
/* route_me_harder function, used by iptable_nat, iptable_mangle + ip_queue */
|
|
int ip_route_me_harder(struct net *net, struct sock *sk, struct sk_buff *skb, unsigned int addr_type)
|
|
{
|
|
const struct iphdr *iph = ip_hdr(skb);
|
|
struct rtable *rt;
|
|
struct flowi4 fl4 = {};
|
|
__be32 saddr = iph->saddr;
|
|
__u8 flags;
|
|
struct net_device *dev = skb_dst(skb)->dev;
|
|
struct flow_keys flkeys;
|
|
unsigned int hh_len;
|
|
|
|
sk = sk_to_full_sk(sk);
|
|
flags = sk ? inet_sk_flowi_flags(sk) : 0;
|
|
|
|
if (addr_type == RTN_UNSPEC)
|
|
addr_type = inet_addr_type_dev_table(net, dev, saddr);
|
|
if (addr_type == RTN_LOCAL || addr_type == RTN_UNICAST)
|
|
flags |= FLOWI_FLAG_ANYSRC;
|
|
else
|
|
saddr = 0;
|
|
|
|
/* some non-standard hacks like ipt_REJECT.c:send_reset() can cause
|
|
* packets with foreign saddr to appear on the NF_INET_LOCAL_OUT hook.
|
|
*/
|
|
fl4.daddr = iph->daddr;
|
|
fl4.saddr = saddr;
|
|
fl4.flowi4_tos = RT_TOS(iph->tos);
|
|
fl4.flowi4_oif = sk ? sk->sk_bound_dev_if : 0;
|
|
fl4.flowi4_l3mdev = l3mdev_master_ifindex(dev);
|
|
fl4.flowi4_mark = skb->mark;
|
|
fl4.flowi4_flags = flags;
|
|
fib4_rules_early_flow_dissect(net, skb, &fl4, &flkeys);
|
|
rt = ip_route_output_key(net, &fl4);
|
|
if (IS_ERR(rt))
|
|
return PTR_ERR(rt);
|
|
|
|
/* Drop old route. */
|
|
skb_dst_drop(skb);
|
|
skb_dst_set(skb, &rt->dst);
|
|
|
|
if (skb_dst(skb)->error)
|
|
return skb_dst(skb)->error;
|
|
|
|
#ifdef CONFIG_XFRM
|
|
if (!(IPCB(skb)->flags & IPSKB_XFRM_TRANSFORMED) &&
|
|
xfrm_decode_session(net, skb, flowi4_to_flowi(&fl4), AF_INET) == 0) {
|
|
struct dst_entry *dst = skb_dst(skb);
|
|
skb_dst_set(skb, NULL);
|
|
dst = xfrm_lookup(net, dst, flowi4_to_flowi(&fl4), sk, 0);
|
|
if (IS_ERR(dst))
|
|
return PTR_ERR(dst);
|
|
skb_dst_set(skb, dst);
|
|
}
|
|
#endif
|
|
|
|
/* Change in oif may mean change in hh_len. */
|
|
hh_len = skb_dst(skb)->dev->hard_header_len;
|
|
if (skb_headroom(skb) < hh_len &&
|
|
pskb_expand_head(skb, HH_DATA_ALIGN(hh_len - skb_headroom(skb)),
|
|
0, GFP_ATOMIC))
|
|
return -ENOMEM;
|
|
|
|
return 0;
|
|
}
|
|
EXPORT_SYMBOL(ip_route_me_harder);
|
|
|
|
int nf_ip_route(struct net *net, struct dst_entry **dst, struct flowi *fl,
|
|
bool strict __always_unused)
|
|
{
|
|
struct rtable *rt = ip_route_output_key(net, &fl->u.ip4);
|
|
if (IS_ERR(rt))
|
|
return PTR_ERR(rt);
|
|
*dst = &rt->dst;
|
|
return 0;
|
|
}
|
|
EXPORT_SYMBOL_GPL(nf_ip_route);
|