blob: 28901d7135dc864dc9e48e877053fda4b7ac2e81 [file] [log] [blame]
developerec4ebe42022-04-12 11:17:45 +08001From: Jonas Gorski <jogo@openwrt.org>
2Subject: ipv6: allow rejecting with "source address failed policy"
3
4RFC6204 L-14 requires rejecting traffic from invalid addresses with
5ICMPv6 Destination Unreachable, Code 5 (Source address failed ingress/
6egress policy) on the LAN side, so add an appropriate rule for that.
7
8Signed-off-by: Jonas Gorski <jogo@openwrt.org>
9---
10 include/net/netns/ipv6.h | 1 +
11 include/uapi/linux/fib_rules.h | 4 +++
12 include/uapi/linux/rtnetlink.h | 1 +
13 net/ipv4/fib_semantics.c | 4 +++
14 net/ipv4/fib_trie.c | 1 +
15 net/ipv4/ipmr.c | 1 +
16 net/ipv6/fib6_rules.c | 4 +++
17 net/ipv6/ip6mr.c | 2 ++
18 net/ipv6/route.c | 58 +++++++++++++++++++++++++++++++++++++++++-
19 9 files changed, 75 insertions(+), 1 deletion(-)
20
21--- a/include/net/netns/ipv6.h
22+++ b/include/net/netns/ipv6.h
23@@ -84,6 +84,7 @@ struct netns_ipv6 {
24 unsigned int fib6_rules_require_fldissect;
25 bool fib6_has_custom_rules;
26 struct rt6_info *ip6_prohibit_entry;
27+ struct rt6_info *ip6_policy_failed_entry;
28 struct rt6_info *ip6_blk_hole_entry;
29 struct fib6_table *fib6_local_tbl;
30 struct fib_rules_ops *fib6_rules_ops;
31--- a/include/uapi/linux/fib_rules.h
32+++ b/include/uapi/linux/fib_rules.h
33@@ -82,6 +82,10 @@ enum {
34 FR_ACT_BLACKHOLE, /* Drop without notification */
35 FR_ACT_UNREACHABLE, /* Drop with ENETUNREACH */
36 FR_ACT_PROHIBIT, /* Drop with EACCES */
37+ FR_ACT_RES9,
38+ FR_ACT_RES10,
39+ FR_ACT_RES11,
40+ FR_ACT_POLICY_FAILED, /* Drop with EACCES */
41 __FR_ACT_MAX,
42 };
43
44--- a/include/uapi/linux/rtnetlink.h
45+++ b/include/uapi/linux/rtnetlink.h
46@@ -235,6 +235,7 @@ enum {
47 RTN_THROW, /* Not in this table */
48 RTN_NAT, /* Translate this address */
49 RTN_XRESOLVE, /* Use external resolver */
50+ RTN_POLICY_FAILED, /* Failed ingress/egress policy */
51 __RTN_MAX
52 };
53
54--- a/net/ipv4/fib_semantics.c
55+++ b/net/ipv4/fib_semantics.c
56@@ -142,6 +142,10 @@ const struct fib_prop fib_props[RTN_MAX
57 .error = -EINVAL,
58 .scope = RT_SCOPE_NOWHERE,
59 },
60+ [RTN_POLICY_FAILED] = {
61+ .error = -EACCES,
62+ .scope = RT_SCOPE_UNIVERSE,
63+ },
64 };
65
66 static void rt_fibinfo_free(struct rtable __rcu **rtp)
67--- a/net/ipv4/fib_trie.c
68+++ b/net/ipv4/fib_trie.c
69@@ -2596,6 +2596,7 @@ static const char *const rtn_type_names[
70 [RTN_THROW] = "THROW",
71 [RTN_NAT] = "NAT",
72 [RTN_XRESOLVE] = "XRESOLVE",
73+ [RTN_POLICY_FAILED] = "POLICY_FAILED",
74 };
75
76 static inline const char *rtn_type(char *buf, size_t len, unsigned int t)
77--- a/net/ipv4/ipmr.c
78+++ b/net/ipv4/ipmr.c
79@@ -173,6 +173,7 @@ static int ipmr_rule_action(struct fib_r
80 case FR_ACT_UNREACHABLE:
81 return -ENETUNREACH;
82 case FR_ACT_PROHIBIT:
83+ case FR_ACT_POLICY_FAILED:
84 return -EACCES;
85 case FR_ACT_BLACKHOLE:
86 default:
87--- a/net/ipv6/fib6_rules.c
88+++ b/net/ipv6/fib6_rules.c
89@@ -216,6 +216,10 @@ static int __fib6_rule_action(struct fib
90 err = -EACCES;
91 rt = net->ipv6.ip6_prohibit_entry;
92 goto discard_pkt;
93+ case FR_ACT_POLICY_FAILED:
94+ err = -EACCES;
95+ rt = net->ipv6.ip6_policy_failed_entry;
96+ goto discard_pkt;
97 }
98
99 tb_id = fib_rule_get_table(rule, arg);
100--- a/net/ipv6/ip6mr.c
101+++ b/net/ipv6/ip6mr.c
102@@ -161,6 +161,8 @@ static int ip6mr_rule_action(struct fib_
103 return -ENETUNREACH;
104 case FR_ACT_PROHIBIT:
105 return -EACCES;
106+ case FR_ACT_POLICY_FAILED:
107+ return -EACCES;
108 case FR_ACT_BLACKHOLE:
109 default:
110 return -EINVAL;
111--- a/net/ipv6/route.c
112+++ b/net/ipv6/route.c
113@@ -94,6 +94,8 @@ static int ip6_pkt_discard(struct sk_bu
114 static int ip6_pkt_discard_out(struct net *net, struct sock *sk, struct sk_buff *skb);
115 static int ip6_pkt_prohibit(struct sk_buff *skb);
116 static int ip6_pkt_prohibit_out(struct net *net, struct sock *sk, struct sk_buff *skb);
117+static int ip6_pkt_policy_failed(struct sk_buff *skb);
118+static int ip6_pkt_policy_failed_out(struct net *net, struct sock *sk, struct sk_buff *skb);
119 static void ip6_link_failure(struct sk_buff *skb);
120 static void ip6_rt_update_pmtu(struct dst_entry *dst, struct sock *sk,
121 struct sk_buff *skb, u32 mtu,
122@@ -327,6 +329,18 @@ static const struct rt6_info ip6_prohibi
123 .rt6i_flags = (RTF_REJECT | RTF_NONEXTHOP),
124 };
125
126+static const struct rt6_info ip6_policy_failed_entry_template = {
127+ .dst = {
128+ .__refcnt = ATOMIC_INIT(1),
129+ .__use = 1,
130+ .obsolete = DST_OBSOLETE_FORCE_CHK,
131+ .error = -EACCES,
132+ .input = ip6_pkt_policy_failed,
133+ .output = ip6_pkt_policy_failed_out,
134+ },
135+ .rt6i_flags = (RTF_REJECT | RTF_NONEXTHOP),
136+};
137+
138 static const struct rt6_info ip6_blk_hole_entry_template = {
139 .dst = {
140 .__refcnt = ATOMIC_INIT(1),
141@@ -1048,6 +1062,7 @@ static const int fib6_prop[RTN_MAX + 1]
142 [RTN_BLACKHOLE] = -EINVAL,
143 [RTN_UNREACHABLE] = -EHOSTUNREACH,
144 [RTN_PROHIBIT] = -EACCES,
145+ [RTN_POLICY_FAILED] = -EACCES,
146 [RTN_THROW] = -EAGAIN,
147 [RTN_NAT] = -EINVAL,
148 [RTN_XRESOLVE] = -EINVAL,
149@@ -1085,6 +1100,10 @@ static void ip6_rt_init_dst_reject(struc
150 rt->dst.output = ip6_pkt_prohibit_out;
151 rt->dst.input = ip6_pkt_prohibit;
152 break;
153+ case RTN_POLICY_FAILED:
154+ rt->dst.output = ip6_pkt_policy_failed_out;
155+ rt->dst.input = ip6_pkt_policy_failed;
156+ break;
157 case RTN_THROW:
158 case RTN_UNREACHABLE:
159 default:
160@@ -4453,6 +4472,17 @@ static int ip6_pkt_prohibit_out(struct n
161 return ip6_pkt_drop(skb, ICMPV6_ADM_PROHIBITED, IPSTATS_MIB_OUTNOROUTES);
162 }
163
164+static int ip6_pkt_policy_failed(struct sk_buff *skb)
165+{
166+ return ip6_pkt_drop(skb, ICMPV6_POLICY_FAIL, IPSTATS_MIB_INNOROUTES);
167+}
168+
169+static int ip6_pkt_policy_failed_out(struct net *net, struct sock *sk, struct sk_buff *skb)
170+{
171+ skb->dev = skb_dst(skb)->dev;
172+ return ip6_pkt_drop(skb, ICMPV6_POLICY_FAIL, IPSTATS_MIB_OUTNOROUTES);
173+}
174+
175 /*
176 * Allocate a dst for local (unicast / anycast) address.
177 */
developer27b55252022-09-05 19:09:45 +0800178@@ -4940,7 +4970,8 @@ static int rtm_to_fib6_config(struct sk_
developerec4ebe42022-04-12 11:17:45 +0800179 if (rtm->rtm_type == RTN_UNREACHABLE ||
180 rtm->rtm_type == RTN_BLACKHOLE ||
181 rtm->rtm_type == RTN_PROHIBIT ||
182- rtm->rtm_type == RTN_THROW)
183+ rtm->rtm_type == RTN_THROW ||
184+ rtm->rtm_type == RTN_POLICY_FAILED)
185 cfg->fc_flags |= RTF_REJECT;
186
187 if (rtm->rtm_type == RTN_LOCAL)
developer27b55252022-09-05 19:09:45 +0800188@@ -6091,6 +6122,8 @@ static int ip6_route_dev_notify(struct n
developerec4ebe42022-04-12 11:17:45 +0800189 #ifdef CONFIG_IPV6_MULTIPLE_TABLES
190 net->ipv6.ip6_prohibit_entry->dst.dev = dev;
191 net->ipv6.ip6_prohibit_entry->rt6i_idev = in6_dev_get(dev);
192+ net->ipv6.ip6_policy_failed_entry->dst.dev = dev;
193+ net->ipv6.ip6_policy_failed_entry->rt6i_idev = in6_dev_get(dev);
194 net->ipv6.ip6_blk_hole_entry->dst.dev = dev;
195 net->ipv6.ip6_blk_hole_entry->rt6i_idev = in6_dev_get(dev);
196 #endif
developer27b55252022-09-05 19:09:45 +0800197@@ -6102,6 +6135,7 @@ static int ip6_route_dev_notify(struct n
developerec4ebe42022-04-12 11:17:45 +0800198 in6_dev_put_clear(&net->ipv6.ip6_null_entry->rt6i_idev);
199 #ifdef CONFIG_IPV6_MULTIPLE_TABLES
200 in6_dev_put_clear(&net->ipv6.ip6_prohibit_entry->rt6i_idev);
201+ in6_dev_put_clear(&net->ipv6.ip6_policy_failed_entry->rt6i_idev);
202 in6_dev_put_clear(&net->ipv6.ip6_blk_hole_entry->rt6i_idev);
203 #endif
204 }
developer27b55252022-09-05 19:09:45 +0800205@@ -6294,6 +6328,8 @@ static int __net_init ip6_route_net_init
developerec4ebe42022-04-12 11:17:45 +0800206
207 #ifdef CONFIG_IPV6_MULTIPLE_TABLES
208 net->ipv6.fib6_has_custom_rules = false;
209+
210+
211 net->ipv6.ip6_prohibit_entry = kmemdup(&ip6_prohibit_entry_template,
212 sizeof(*net->ipv6.ip6_prohibit_entry),
213 GFP_KERNEL);
developer27b55252022-09-05 19:09:45 +0800214@@ -6304,11 +6340,21 @@ static int __net_init ip6_route_net_init
developerec4ebe42022-04-12 11:17:45 +0800215 ip6_template_metrics, true);
216 INIT_LIST_HEAD(&net->ipv6.ip6_prohibit_entry->rt6i_uncached);
217
218+ net->ipv6.ip6_policy_failed_entry =
219+ kmemdup(&ip6_policy_failed_entry_template,
220+ sizeof(*net->ipv6.ip6_policy_failed_entry), GFP_KERNEL);
221+ if (!net->ipv6.ip6_policy_failed_entry)
222+ goto out_ip6_prohibit_entry;
223+ net->ipv6.ip6_policy_failed_entry->dst.ops = &net->ipv6.ip6_dst_ops;
224+ dst_init_metrics(&net->ipv6.ip6_policy_failed_entry->dst,
225+ ip6_template_metrics, true);
226+ INIT_LIST_HEAD(&net->ipv6.ip6_policy_failed_entry->rt6i_uncached);
227+
228 net->ipv6.ip6_blk_hole_entry = kmemdup(&ip6_blk_hole_entry_template,
229 sizeof(*net->ipv6.ip6_blk_hole_entry),
230 GFP_KERNEL);
231 if (!net->ipv6.ip6_blk_hole_entry)
232- goto out_ip6_prohibit_entry;
233+ goto out_ip6_policy_failed_entry;
234 net->ipv6.ip6_blk_hole_entry->dst.ops = &net->ipv6.ip6_dst_ops;
235 dst_init_metrics(&net->ipv6.ip6_blk_hole_entry->dst,
236 ip6_template_metrics, true);
developer27b55252022-09-05 19:09:45 +0800237@@ -6332,6 +6378,8 @@ out:
developerec4ebe42022-04-12 11:17:45 +0800238 return ret;
239
240 #ifdef CONFIG_IPV6_MULTIPLE_TABLES
241+out_ip6_policy_failed_entry:
242+ kfree(net->ipv6.ip6_policy_failed_entry);
243 out_ip6_prohibit_entry:
244 kfree(net->ipv6.ip6_prohibit_entry);
245 out_ip6_null_entry:
developer27b55252022-09-05 19:09:45 +0800246@@ -6351,6 +6399,7 @@ static void __net_exit ip6_route_net_exi
developerec4ebe42022-04-12 11:17:45 +0800247 kfree(net->ipv6.ip6_null_entry);
248 #ifdef CONFIG_IPV6_MULTIPLE_TABLES
249 kfree(net->ipv6.ip6_prohibit_entry);
250+ kfree(net->ipv6.ip6_policy_failed_entry);
251 kfree(net->ipv6.ip6_blk_hole_entry);
252 #endif
253 dst_entries_destroy(&net->ipv6.ip6_dst_ops);
developer27b55252022-09-05 19:09:45 +0800254@@ -6428,6 +6477,9 @@ void __init ip6_route_init_special_entri
developerec4ebe42022-04-12 11:17:45 +0800255 init_net.ipv6.ip6_prohibit_entry->rt6i_idev = in6_dev_get(init_net.loopback_dev);
256 init_net.ipv6.ip6_blk_hole_entry->dst.dev = init_net.loopback_dev;
257 init_net.ipv6.ip6_blk_hole_entry->rt6i_idev = in6_dev_get(init_net.loopback_dev);
258+ init_net.ipv6.ip6_policy_failed_entry->dst.dev = init_net.loopback_dev;
259+ init_net.ipv6.ip6_policy_failed_entry->rt6i_idev =
260+ in6_dev_get(init_net.loopback_dev);
261 #endif
262 }
263