2026-03-04 17:47:22 -07:00
|
|
|
// SPDX-License-Identifier: GPL-2.0-or-later
|
2013-04-06 15:24:29 +02:00
|
|
|
/*
|
|
|
|
|
* IPv4 specific functions of netfilter core
|
|
|
|
|
*
|
2026-03-04 17:47:22 -07:00
|
|
|
* Rusty Russell (C) 2000
|
2013-04-06 15:24:29 +02:00
|
|
|
* Patrick McHardy (C) 2006-2012
|
|
|
|
|
*/
|
2005-08-09 19:39:00 -07:00
|
|
|
#include <linux/kernel.h>
|
|
|
|
|
#include <linux/netfilter.h>
|
2005-08-09 19:42:34 -07:00
|
|
|
#include <linux/netfilter_ipv4.h>
|
2006-01-06 23:04:54 -08:00
|
|
|
#include <linux/ip.h>
|
2007-10-14 00:39:55 -07:00
|
|
|
#include <linux/skbuff.h>
|
2010-03-24 17:04:11 +09:00
|
|
|
#include <linux/gfp.h>
|
2011-07-15 11:47:34 -04:00
|
|
|
#include <linux/export.h>
|
2025-08-25 15:37:43 +02:00
|
|
|
#include <net/flow.h>
|
2005-08-09 19:39:00 -07:00
|
|
|
#include <net/route.h>
|
2006-01-06 23:04:54 -08:00
|
|
|
#include <net/xfrm.h>
|
|
|
|
|
#include <net/ip.h>
|
2007-12-05 01:24:48 -08:00
|
|
|
#include <net/netfilter/nf_queue.h>
|
2005-08-09 19:39:00 -07:00
|
|
|
|
|
|
|
|
/* route_me_harder function, used by iptable_nat, iptable_mangle + ip_queue */
|
2020-10-29 03:56:06 +01:00
|
|
|
int ip_route_me_harder(struct net *net, struct sock *sk, struct sk_buff *skb, unsigned int addr_type)
|
2005-08-09 19:39:00 -07:00
|
|
|
{
|
2025-06-30 12:19:31 +00:00
|
|
|
struct net_device *dev = skb_dst_dev(skb);
|
2007-10-15 00:53:15 -07:00
|
|
|
const struct iphdr *iph = ip_hdr(skb);
|
2005-08-09 19:39:00 -07:00
|
|
|
struct rtable *rt;
|
2011-03-12 01:12:47 -05:00
|
|
|
struct flowi4 fl4 = {};
|
2011-06-18 07:53:59 +00:00
|
|
|
__be32 saddr = iph->saddr;
|
2020-10-29 03:56:06 +01:00
|
|
|
__u8 flags;
|
2021-04-14 11:20:32 +03:00
|
|
|
struct flow_keys flkeys;
|
2005-08-09 19:39:00 -07:00
|
|
|
unsigned int hh_len;
|
|
|
|
|
|
2020-10-29 03:56:06 +01:00
|
|
|
sk = sk_to_full_sk(sk);
|
|
|
|
|
flags = sk ? inet_sk_flowi_flags(sk) : 0;
|
|
|
|
|
|
2011-08-07 09:11:00 +00:00
|
|
|
if (addr_type == RTN_UNSPEC)
|
2016-11-09 10:24:40 -08:00
|
|
|
addr_type = inet_addr_type_dev_table(net, dev, saddr);
|
2011-08-07 09:11:00 +00:00
|
|
|
if (addr_type == RTN_LOCAL || addr_type == RTN_UNICAST)
|
|
|
|
|
flags |= FLOWI_FLAG_ANYSRC;
|
|
|
|
|
else
|
|
|
|
|
saddr = 0;
|
2006-10-02 16:11:13 -07:00
|
|
|
|
2005-08-09 19:39:00 -07:00
|
|
|
/* some non-standard hacks like ipt_REJECT.c:send_reset() can cause
|
2007-11-19 18:53:30 -08:00
|
|
|
* packets with foreign saddr to appear on the NF_INET_LOCAL_OUT hook.
|
2005-08-09 19:39:00 -07:00
|
|
|
*/
|
2011-06-18 07:53:59 +00:00
|
|
|
fl4.daddr = iph->daddr;
|
|
|
|
|
fl4.saddr = saddr;
|
2025-08-25 15:37:43 +02:00
|
|
|
fl4.flowi4_dscp = ip4h_dscp(iph);
|
2017-02-17 08:39:28 +01:00
|
|
|
fl4.flowi4_oif = sk ? sk->sk_bound_dev_if : 0;
|
2022-04-19 15:47:01 +02:00
|
|
|
fl4.flowi4_l3mdev = l3mdev_master_ifindex(dev);
|
2011-06-18 07:53:59 +00:00
|
|
|
fl4.flowi4_mark = skb->mark;
|
2011-08-07 09:11:00 +00:00
|
|
|
fl4.flowi4_flags = flags;
|
2021-04-14 11:20:32 +03:00
|
|
|
fib4_rules_early_flow_dissect(net, skb, &fl4, &flkeys);
|
2011-06-18 07:53:59 +00:00
|
|
|
rt = ip_route_output_key(net, &fl4);
|
|
|
|
|
if (IS_ERR(rt))
|
2013-04-05 06:41:10 +00:00
|
|
|
return PTR_ERR(rt);
|
2005-08-09 19:39:00 -07:00
|
|
|
|
2011-06-18 07:53:59 +00:00
|
|
|
/* Drop old route. */
|
|
|
|
|
skb_dst_drop(skb);
|
|
|
|
|
skb_dst_set(skb, &rt->dst);
|
2007-02-09 23:24:47 +09:00
|
|
|
|
2009-06-02 05:19:30 +00:00
|
|
|
if (skb_dst(skb)->error)
|
2013-04-05 06:41:10 +00:00
|
|
|
return skb_dst(skb)->error;
|
2005-08-09 19:39:00 -07:00
|
|
|
|
2006-01-06 23:04:54 -08:00
|
|
|
#ifdef CONFIG_XFRM
|
2007-10-15 00:53:15 -07:00
|
|
|
if (!(IPCB(skb)->flags & IPSKB_XFRM_TRANSFORMED) &&
|
2023-10-04 18:09:51 +02:00
|
|
|
xfrm_decode_session(net, skb, flowi4_to_flowi(&fl4), AF_INET) == 0) {
|
2009-06-02 05:19:30 +00:00
|
|
|
struct dst_entry *dst = skb_dst(skb);
|
2025-08-18 08:40:28 -07:00
|
|
|
/* ignore return value from skb_dstref_steal, xfrm_lookup takes
|
|
|
|
|
* care of dropping the refcnt if needed.
|
|
|
|
|
*/
|
|
|
|
|
skb_dstref_steal(skb);
|
2017-02-17 08:39:28 +01:00
|
|
|
dst = xfrm_lookup(net, dst, flowi4_to_flowi(&fl4), sk, 0);
|
2011-03-02 13:27:41 -08:00
|
|
|
if (IS_ERR(dst))
|
2014-01-15 08:12:50 -08:00
|
|
|
return PTR_ERR(dst);
|
2009-06-02 05:19:30 +00:00
|
|
|
skb_dst_set(skb, dst);
|
|
|
|
|
}
|
2006-01-06 23:04:54 -08:00
|
|
|
#endif
|
|
|
|
|
|
2005-08-09 19:39:00 -07:00
|
|
|
/* Change in oif may mean change in hh_len. */
|
2025-06-30 12:19:31 +00:00
|
|
|
hh_len = skb_dst_dev(skb)->hard_header_len;
|
2007-10-15 00:53:15 -07:00
|
|
|
if (skb_headroom(skb) < hh_len &&
|
2011-11-14 19:00:54 +08:00
|
|
|
pskb_expand_head(skb, HH_DATA_ALIGN(hh_len - skb_headroom(skb)),
|
|
|
|
|
0, GFP_ATOMIC))
|
2013-04-05 06:41:10 +00:00
|
|
|
return -ENOMEM;
|
2005-08-09 19:39:00 -07:00
|
|
|
|
|
|
|
|
return 0;
|
|
|
|
|
}
|
|
|
|
|
EXPORT_SYMBOL(ip_route_me_harder);
|
2005-08-09 19:42:34 -07:00
|
|
|
|
2017-11-27 22:29:52 +01:00
|
|
|
int nf_ip_route(struct net *net, struct dst_entry **dst, struct flowi *fl,
|
|
|
|
|
bool strict __always_unused)
|
2007-12-05 01:22:05 -08:00
|
|
|
{
|
2011-04-04 16:56:29 +02:00
|
|
|
struct rtable *rt = ip_route_output_key(net, &fl->u.ip4);
|
2011-03-02 14:31:35 -08:00
|
|
|
if (IS_ERR(rt))
|
|
|
|
|
return PTR_ERR(rt);
|
|
|
|
|
*dst = &rt->dst;
|
|
|
|
|
return 0;
|
2007-12-05 01:22:05 -08:00
|
|
|
}
|
2017-11-27 22:29:52 +01:00
|
|
|
EXPORT_SYMBOL_GPL(nf_ip_route);
|