Commit | Line | Data |
---|---|---|
f229f6ce PM |
1 | /* |
2 | * IPv6 specific functions of netfilter core | |
3 | * | |
4 | * Rusty Russell (C) 2000 -- This code is GPL. | |
5 | * Patrick McHardy (C) 2006-2012 | |
6 | */ | |
020b4c12 | 7 | #include <linux/kernel.h> |
bb94aa16 | 8 | #include <linux/init.h> |
020b4c12 | 9 | #include <linux/ipv6.h> |
2cc7d573 HW |
10 | #include <linux/netfilter.h> |
11 | #include <linux/netfilter_ipv6.h> | |
bc3b2d7f | 12 | #include <linux/export.h> |
2a7851bf | 13 | #include <net/addrconf.h> |
020b4c12 HW |
14 | #include <net/dst.h> |
15 | #include <net/ipv6.h> | |
16 | #include <net/ip6_route.h> | |
3e3850e9 | 17 | #include <net/xfrm.h> |
c01cd429 | 18 | #include <net/netfilter/nf_queue.h> |
764dd163 PNA |
19 | #include <net/netfilter/nf_conntrack_bridge.h> |
20 | #include <net/netfilter/ipv6/nf_defrag_ipv6.h> | |
21 | #include "../bridge/br_private.h" | |
020b4c12 | 22 | |
46d6c5ae | 23 | int ip6_route_me_harder(struct net *net, struct sock *sk_partial, struct sk_buff *skb) |
020b4c12 | 24 | { |
b71d1d42 | 25 | const struct ipv6hdr *iph = ipv6_hdr(skb); |
46d6c5ae | 26 | struct sock *sk = sk_to_full_sk(sk_partial); |
812fa71f | 27 | struct flow_keys flkeys; |
0ad352cb | 28 | unsigned int hh_len; |
020b4c12 | 29 | struct dst_entry *dst; |
15df03c6 EC |
30 | int strict = (ipv6_addr_type(&iph->daddr) & |
31 | (IPV6_ADDR_MULTICAST | IPV6_ADDR_LINKLOCAL)); | |
4c9483b2 | 32 | struct flowi6 fl6 = { |
508b0904 | 33 | .flowi6_oif = sk && sk->sk_bound_dev_if ? sk->sk_bound_dev_if : |
15df03c6 | 34 | strict ? skb_dst(skb)->dev->ifindex : 0, |
4c9483b2 | 35 | .flowi6_mark = skb->mark, |
7d98386d | 36 | .flowi6_uid = sock_net_uid(net, sk), |
4c9483b2 DM |
37 | .daddr = iph->daddr, |
38 | .saddr = iph->saddr, | |
020b4c12 | 39 | }; |
a8951d58 | 40 | int err; |
020b4c12 | 41 | |
812fa71f | 42 | fib6_rules_early_flow_dissect(net, skb, &fl6, &flkeys); |
7d98386d | 43 | dst = ip6_route_output(net, sk, &fl6); |
a8951d58 SP |
44 | err = dst->error; |
45 | if (err) { | |
eef9d90d | 46 | IP6_INC_STATS(net, ip6_dst_idev(dst), IPSTATS_MIB_OUTNOROUTES); |
ba7a46f1 | 47 | net_dbg_ratelimited("ip6_route_me_harder: No more route\n"); |
020b4c12 | 48 | dst_release(dst); |
a8951d58 | 49 | return err; |
020b4c12 HW |
50 | } |
51 | ||
52 | /* Drop old route. */ | |
adf30907 | 53 | skb_dst_drop(skb); |
020b4c12 | 54 | |
adf30907 | 55 | skb_dst_set(skb, dst); |
90348e0e UW |
56 | |
57 | #ifdef CONFIG_XFRM | |
58 | if (!(IP6CB(skb)->flags & IP6SKB_XFRM_TRANSFORMED) && | |
4c9483b2 | 59 | xfrm_decode_session(skb, flowi6_to_flowi(&fl6), AF_INET6) == 0) { |
90348e0e | 60 | skb_dst_set(skb, NULL); |
7d98386d | 61 | dst = xfrm_lookup(net, dst, flowi6_to_flowi(&fl6), sk, 0); |
452edd59 | 62 | if (IS_ERR(dst)) |
58e35d14 | 63 | return PTR_ERR(dst); |
90348e0e UW |
64 | skb_dst_set(skb, dst); |
65 | } | |
66 | #endif | |
67 | ||
0ad352cb PM |
68 | /* Change in oif may mean change in hh_len. */ |
69 | hh_len = skb_dst(skb)->dev->hard_header_len; | |
70 | if (skb_headroom(skb) < hh_len && | |
71 | pskb_expand_head(skb, HH_DATA_ALIGN(hh_len - skb_headroom(skb)), | |
72 | 0, GFP_ATOMIC)) | |
58e35d14 | 73 | return -ENOMEM; |
0ad352cb | 74 | |
020b4c12 HW |
75 | return 0; |
76 | } | |
77 | EXPORT_SYMBOL(ip6_route_me_harder); | |
78 | ||
ce388f45 | 79 | static int nf_ip6_reroute(struct sk_buff *skb, |
02f014d8 | 80 | const struct nf_queue_entry *entry) |
2cc7d573 | 81 | { |
02f014d8 | 82 | struct ip6_rt_info *rt_info = nf_queue_entry_reroute(entry); |
2cc7d573 | 83 | |
1d1de89b | 84 | if (entry->state.hook == NF_INET_LOCAL_OUT) { |
b71d1d42 | 85 | const struct ipv6hdr *iph = ipv6_hdr(skb); |
2cc7d573 | 86 | if (!ipv6_addr_equal(&iph->daddr, &rt_info->daddr) || |
9f40ac71 EL |
87 | !ipv6_addr_equal(&iph->saddr, &rt_info->saddr) || |
88 | skb->mark != rt_info->mark) | |
46d6c5ae | 89 | return ip6_route_me_harder(entry->state.net, entry->state.sk, skb); |
2cc7d573 HW |
90 | } |
91 | return 0; | |
92 | } | |
93 | ||
ac02bcf9 FW |
94 | int __nf_ip6_route(struct net *net, struct dst_entry **dst, |
95 | struct flowi *fl, bool strict) | |
1841a4c7 | 96 | { |
0fae2e77 FW |
97 | static const struct ipv6_pinfo fake_pinfo; |
98 | static const struct inet_sock fake_sk = { | |
99 | /* makes ip6_route_output set RT6_LOOKUP_F_IFACE: */ | |
100 | .sk.sk_bound_dev_if = 1, | |
101 | .pinet6 = (struct ipv6_pinfo *) &fake_pinfo, | |
102 | }; | |
103 | const void *sk = strict ? &fake_sk : NULL; | |
2dad81ad FW |
104 | struct dst_entry *result; |
105 | int err; | |
106 | ||
107 | result = ip6_route_output(net, sk, &fl->u.ip6); | |
108 | err = result->error; | |
109 | if (err) | |
110 | dst_release(result); | |
111 | else | |
112 | *dst = result; | |
113 | return err; | |
1841a4c7 | 114 | } |
ac02bcf9 | 115 | EXPORT_SYMBOL_GPL(__nf_ip6_route); |
1841a4c7 | 116 | |
764dd163 | 117 | int br_ip6_fragment(struct net *net, struct sock *sk, struct sk_buff *skb, |
46705b07 | 118 | struct nf_bridge_frag_data *data, |
764dd163 | 119 | int (*output)(struct net *, struct sock *sk, |
46705b07 | 120 | const struct nf_bridge_frag_data *data, |
764dd163 PNA |
121 | struct sk_buff *)) |
122 | { | |
123 | int frag_max_size = BR_INPUT_SKB_CB(skb)->frag_max_size; | |
9669fffc | 124 | ktime_t tstamp = skb->tstamp; |
764dd163 PNA |
125 | struct ip6_frag_state state; |
126 | u8 *prevhdr, nexthdr = 0; | |
127 | unsigned int mtu, hlen; | |
128 | int hroom, err = 0; | |
129 | __be32 frag_id; | |
130 | ||
131 | err = ip6_find_1stfragopt(skb, &prevhdr); | |
132 | if (err < 0) | |
133 | goto blackhole; | |
134 | hlen = err; | |
135 | nexthdr = *prevhdr; | |
136 | ||
137 | mtu = skb->dev->mtu; | |
138 | if (frag_max_size > mtu || | |
139 | frag_max_size < IPV6_MIN_MTU) | |
140 | goto blackhole; | |
141 | ||
142 | mtu = frag_max_size; | |
143 | if (mtu < hlen + sizeof(struct frag_hdr) + 8) | |
144 | goto blackhole; | |
145 | mtu -= hlen + sizeof(struct frag_hdr); | |
146 | ||
147 | frag_id = ipv6_select_ident(net, &ipv6_hdr(skb)->daddr, | |
148 | &ipv6_hdr(skb)->saddr); | |
149 | ||
150 | if (skb->ip_summed == CHECKSUM_PARTIAL && | |
151 | (err = skb_checksum_help(skb))) | |
152 | goto blackhole; | |
153 | ||
154 | hroom = LL_RESERVED_SPACE(skb->dev); | |
155 | if (skb_has_frag_list(skb)) { | |
156 | unsigned int first_len = skb_pagelen(skb); | |
157 | struct ip6_fraglist_iter iter; | |
158 | struct sk_buff *frag2; | |
159 | ||
160 | if (first_len - hlen > mtu || | |
161 | skb_headroom(skb) < (hroom + sizeof(struct frag_hdr))) | |
162 | goto blackhole; | |
163 | ||
164 | if (skb_cloned(skb)) | |
165 | goto slow_path; | |
166 | ||
167 | skb_walk_frags(skb, frag2) { | |
168 | if (frag2->len > mtu || | |
169 | skb_headroom(frag2) < (hlen + hroom + sizeof(struct frag_hdr))) | |
170 | goto blackhole; | |
171 | ||
172 | /* Partially cloned skb? */ | |
173 | if (skb_shared(frag2)) | |
174 | goto slow_path; | |
175 | } | |
176 | ||
177 | err = ip6_fraglist_init(skb, hlen, prevhdr, nexthdr, frag_id, | |
178 | &iter); | |
179 | if (err < 0) | |
180 | goto blackhole; | |
181 | ||
182 | for (;;) { | |
183 | /* Prepare header of the next frame, | |
184 | * before previous one went down. | |
185 | */ | |
186 | if (iter.frag) | |
187 | ip6_fraglist_prepare(skb, &iter); | |
188 | ||
9669fffc | 189 | skb->tstamp = tstamp; |
764dd163 PNA |
190 | err = output(net, sk, data, skb); |
191 | if (err || !iter.frag) | |
192 | break; | |
193 | ||
194 | skb = ip6_fraglist_next(&iter); | |
195 | } | |
196 | ||
197 | kfree(iter.tmp_hdr); | |
198 | if (!err) | |
199 | return 0; | |
200 | ||
b7034146 | 201 | kfree_skb_list(iter.frag); |
764dd163 PNA |
202 | return err; |
203 | } | |
204 | slow_path: | |
205 | /* This is a linearized skbuff, the original geometry is lost for us. | |
206 | * This may also be a clone skbuff, we could preserve the geometry for | |
207 | * the copies but probably not worth the effort. | |
208 | */ | |
209 | ip6_frag_init(skb, hlen, mtu, skb->dev->needed_tailroom, | |
210 | LL_RESERVED_SPACE(skb->dev), prevhdr, nexthdr, frag_id, | |
211 | &state); | |
212 | ||
213 | while (state.left > 0) { | |
214 | struct sk_buff *skb2; | |
215 | ||
216 | skb2 = ip6_frag_next(skb, &state); | |
217 | if (IS_ERR(skb2)) { | |
218 | err = PTR_ERR(skb2); | |
219 | goto blackhole; | |
220 | } | |
221 | ||
9669fffc | 222 | skb2->tstamp = tstamp; |
764dd163 PNA |
223 | err = output(net, sk, data, skb2); |
224 | if (err) | |
225 | goto blackhole; | |
226 | } | |
227 | consume_skb(skb); | |
228 | return err; | |
229 | ||
230 | blackhole: | |
231 | kfree_skb(skb); | |
232 | return 0; | |
233 | } | |
234 | EXPORT_SYMBOL_GPL(br_ip6_fragment); | |
235 | ||
2a7851bf | 236 | static const struct nf_ipv6_ops ipv6ops = { |
96058728 | 237 | #if IS_MODULE(CONFIG_IPV6) |
f7dcbe2f | 238 | .chk_addr = ipv6_chk_addr, |
96058728 FW |
239 | .route_me_harder = ip6_route_me_harder, |
240 | .dev_get_saddr = ipv6_dev_get_saddr, | |
ac02bcf9 | 241 | .route = __nf_ip6_route, |
8527fa6c | 242 | #if IS_ENABLED(CONFIG_SYN_COOKIES) |
3006a522 FFM |
243 | .cookie_init_sequence = __cookie_v6_init_sequence, |
244 | .cookie_v6_check = __cookie_v6_check, | |
8527fa6c | 245 | #endif |
96058728 | 246 | #endif |
ac02bcf9 | 247 | .route_input = ip6_route_input, |
f7dcbe2f | 248 | .fragment = ip6_fragment, |
ce388f45 | 249 | .reroute = nf_ip6_reroute, |
16e6427c | 250 | #if IS_MODULE(CONFIG_IPV6) |
764dd163 PNA |
251 | .br_fragment = br_ip6_fragment, |
252 | #endif | |
2a7851bf FW |
253 | }; |
254 | ||
2cc7d573 HW |
255 | int __init ipv6_netfilter_init(void) |
256 | { | |
2a7851bf | 257 | RCU_INIT_POINTER(nf_ipv6_ops, &ipv6ops); |
b3a61254 | 258 | return 0; |
2cc7d573 HW |
259 | } |
260 | ||
5bf887f2 DM |
261 | /* This can be called from inet6_init() on errors, so it cannot |
262 | * be marked __exit. -DaveM | |
263 | */ | |
264 | void ipv6_netfilter_fini(void) | |
2cc7d573 | 265 | { |
2a7851bf | 266 | RCU_INIT_POINTER(nf_ipv6_ops, NULL); |
2cc7d573 | 267 | } |