blob: 6d0b1f3e927bd75f07cc36fe7b087723e05b85b2 [file] [log] [blame]
Patrick McHardyf229f6c2013-04-06 15:24:29 +02001/*
2 * IPv6 specific functions of netfilter core
3 *
4 * Rusty Russell (C) 2000 -- This code is GPL.
5 * Patrick McHardy (C) 2006-2012
6 */
Harald Welte020b4c12005-08-09 19:39:00 -07007#include <linux/kernel.h>
Patrick McHardybb94aa12006-01-09 16:43:13 -08008#include <linux/init.h>
Harald Welte020b4c12005-08-09 19:39:00 -07009#include <linux/ipv6.h>
Harald Welte2cc7d572005-08-09 19:42:34 -070010#include <linux/netfilter.h>
11#include <linux/netfilter_ipv6.h>
Paul Gortmakerbc3b2d72011-07-15 11:47:34 -040012#include <linux/export.h>
Florian Westphal2a7851b2013-05-17 03:56:10 +000013#include <net/addrconf.h>
Harald Welte020b4c12005-08-09 19:39:00 -070014#include <net/dst.h>
15#include <net/ipv6.h>
16#include <net/ip6_route.h>
Patrick McHardy3e3850e2006-01-06 23:04:54 -080017#include <net/xfrm.h>
Patrick McHardyc01cd422007-12-05 01:24:48 -080018#include <net/netfilter/nf_queue.h>
Harald Welte020b4c12005-08-09 19:39:00 -070019
Eric W. Biederman5f5d74d2015-09-25 15:07:31 -050020int ip6_route_me_harder(struct net *net, struct sk_buff *skb)
Harald Welte020b4c12005-08-09 19:39:00 -070021{
Eric Dumazetb71d1d42011-04-22 04:53:02 +000022 const struct ipv6hdr *iph = ipv6_hdr(skb);
Eric Dumazet7d983862018-02-25 11:49:07 -080023 struct sock *sk = sk_to_full_sk(skb->sk);
Patrick McHardy0ad352c2012-08-26 19:14:08 +020024 unsigned int hh_len;
Harald Welte020b4c12005-08-09 19:39:00 -070025 struct dst_entry *dst;
Eli Cooper783359c2019-01-21 18:45:27 +080026 int strict = (ipv6_addr_type(&iph->daddr) &
27 (IPV6_ADDR_MULTICAST | IPV6_ADDR_LINKLOCAL));
David S. Miller4c9483b2011-03-12 16:22:43 -050028 struct flowi6 fl6 = {
Alin Nastac98380902018-11-21 14:00:30 +010029 .flowi6_oif = sk && sk->sk_bound_dev_if ? sk->sk_bound_dev_if :
Eli Cooper783359c2019-01-21 18:45:27 +080030 strict ? skb_dst(skb)->dev->ifindex : 0,
David S. Miller4c9483b2011-03-12 16:22:43 -050031 .flowi6_mark = skb->mark,
Eric Dumazet7d983862018-02-25 11:49:07 -080032 .flowi6_uid = sock_net_uid(net, sk),
David S. Miller4c9483b2011-03-12 16:22:43 -050033 .daddr = iph->daddr,
34 .saddr = iph->saddr,
Harald Welte020b4c12005-08-09 19:39:00 -070035 };
Sergey Popovicha8951d52014-05-08 16:22:35 +030036 int err;
Harald Welte020b4c12005-08-09 19:39:00 -070037
Eric Dumazet7d983862018-02-25 11:49:07 -080038 dst = ip6_route_output(net, sk, &fl6);
Sergey Popovicha8951d52014-05-08 16:22:35 +030039 err = dst->error;
40 if (err) {
Alexey Dobriyaneef9d902008-10-14 22:55:21 -070041 IP6_INC_STATS(net, ip6_dst_idev(dst), IPSTATS_MIB_OUTNOROUTES);
Joe Perchesba7a46f2014-11-11 10:59:17 -080042 net_dbg_ratelimited("ip6_route_me_harder: No more route\n");
Harald Welte020b4c12005-08-09 19:39:00 -070043 dst_release(dst);
Sergey Popovicha8951d52014-05-08 16:22:35 +030044 return err;
Harald Welte020b4c12005-08-09 19:39:00 -070045 }
46
47 /* Drop old route. */
Eric Dumazetadf30902009-06-02 05:19:30 +000048 skb_dst_drop(skb);
Harald Welte020b4c12005-08-09 19:39:00 -070049
Eric Dumazetadf30902009-06-02 05:19:30 +000050 skb_dst_set(skb, dst);
Ulrich Weber90348e02010-04-15 12:37:18 +020051
52#ifdef CONFIG_XFRM
53 if (!(IP6CB(skb)->flags & IP6SKB_XFRM_TRANSFORMED) &&
David S. Miller4c9483b2011-03-12 16:22:43 -050054 xfrm_decode_session(skb, flowi6_to_flowi(&fl6), AF_INET6) == 0) {
Ulrich Weber90348e02010-04-15 12:37:18 +020055 skb_dst_set(skb, NULL);
Eric Dumazet7d983862018-02-25 11:49:07 -080056 dst = xfrm_lookup(net, dst, flowi6_to_flowi(&fl6), sk, 0);
David S. Miller452edd52011-03-02 13:27:41 -080057 if (IS_ERR(dst))
Patrick McHardy58e35d12013-04-05 06:41:11 +000058 return PTR_ERR(dst);
Ulrich Weber90348e02010-04-15 12:37:18 +020059 skb_dst_set(skb, dst);
60 }
61#endif
62
Patrick McHardy0ad352c2012-08-26 19:14:08 +020063 /* Change in oif may mean change in hh_len. */
64 hh_len = skb_dst(skb)->dev->hard_header_len;
65 if (skb_headroom(skb) < hh_len &&
66 pskb_expand_head(skb, HH_DATA_ALIGN(hh_len - skb_headroom(skb)),
67 0, GFP_ATOMIC))
Patrick McHardy58e35d12013-04-05 06:41:11 +000068 return -ENOMEM;
Patrick McHardy0ad352c2012-08-26 19:14:08 +020069
Harald Welte020b4c12005-08-09 19:39:00 -070070 return 0;
71}
72EXPORT_SYMBOL(ip6_route_me_harder);
73
Pablo Neira Ayusoce388f42017-11-27 22:50:26 +010074static int nf_ip6_reroute(struct sk_buff *skb,
Patrick McHardy02f014d2007-12-05 01:26:33 -080075 const struct nf_queue_entry *entry)
Harald Welte2cc7d572005-08-09 19:42:34 -070076{
Patrick McHardy02f014d2007-12-05 01:26:33 -080077 struct ip6_rt_info *rt_info = nf_queue_entry_reroute(entry);
Harald Welte2cc7d572005-08-09 19:42:34 -070078
David S. Miller1d1de892015-04-03 16:31:01 -040079 if (entry->state.hook == NF_INET_LOCAL_OUT) {
Eric Dumazetb71d1d42011-04-22 04:53:02 +000080 const struct ipv6hdr *iph = ipv6_hdr(skb);
Harald Welte2cc7d572005-08-09 19:42:34 -070081 if (!ipv6_addr_equal(&iph->daddr, &rt_info->daddr) ||
Eric Leblond9f40ac72008-11-25 12:18:11 +010082 !ipv6_addr_equal(&iph->saddr, &rt_info->saddr) ||
83 skb->mark != rt_info->mark)
Pablo Neira Ayusoce388f42017-11-27 22:50:26 +010084 return ip6_route_me_harder(entry->state.net, skb);
Harald Welte2cc7d572005-08-09 19:42:34 -070085 }
86 return 0;
87}
88
Florian Westphal31ad3dd2011-04-04 16:56:29 +020089static int nf_ip6_route(struct net *net, struct dst_entry **dst,
Florian Westphal0fae2e72011-04-04 17:00:54 +020090 struct flowi *fl, bool strict)
Patrick McHardy1841a4c2007-12-05 01:22:05 -080091{
Florian Westphal0fae2e72011-04-04 17:00:54 +020092 static const struct ipv6_pinfo fake_pinfo;
93 static const struct inet_sock fake_sk = {
94 /* makes ip6_route_output set RT6_LOOKUP_F_IFACE: */
95 .sk.sk_bound_dev_if = 1,
96 .pinet6 = (struct ipv6_pinfo *) &fake_pinfo,
97 };
98 const void *sk = strict ? &fake_sk : NULL;
Florian Westphal2dad81a2011-10-19 13:23:06 +020099 struct dst_entry *result;
100 int err;
Florian Westphal0fae2e72011-04-04 17:00:54 +0200101
Florian Westphal2dad81a2011-10-19 13:23:06 +0200102 result = ip6_route_output(net, sk, &fl->u.ip6);
103 err = result->error;
104 if (err)
105 dst_release(result);
106 else
107 *dst = result;
108 return err;
Patrick McHardy1841a4c2007-12-05 01:22:05 -0800109}
110
Florian Westphal2a7851b2013-05-17 03:56:10 +0000111static const struct nf_ipv6_ops ipv6ops = {
Pablo Neira Ayusof7dcbe22017-12-20 16:04:18 +0100112 .chk_addr = ipv6_chk_addr,
113 .route_input = ip6_route_input,
114 .fragment = ip6_fragment,
Pablo Neira Ayuso3f87c082017-11-27 22:29:52 +0100115 .route = nf_ip6_route,
Pablo Neira Ayusoce388f42017-11-27 22:50:26 +0100116 .reroute = nf_ip6_reroute,
Florian Westphal2a7851b2013-05-17 03:56:10 +0000117};
118
Harald Welte2cc7d572005-08-09 19:42:34 -0700119int __init ipv6_netfilter_init(void)
120{
Florian Westphal2a7851b2013-05-17 03:56:10 +0000121 RCU_INIT_POINTER(nf_ipv6_ops, &ipv6ops);
Pablo Neira Ayusob3a61252017-12-09 17:05:53 +0100122 return 0;
Harald Welte2cc7d572005-08-09 19:42:34 -0700123}
124
David S. Miller5bf887f2006-01-10 21:02:21 -0800125/* This can be called from inet6_init() on errors, so it cannot
126 * be marked __exit. -DaveM
127 */
128void ipv6_netfilter_fini(void)
Harald Welte2cc7d572005-08-09 19:42:34 -0700129{
Florian Westphal2a7851b2013-05-17 03:56:10 +0000130 RCU_INIT_POINTER(nf_ipv6_ops, NULL);
Harald Welte2cc7d572005-08-09 19:42:34 -0700131}