Commit | Line | Data |
---|---|---|
a0ae2562 | 1 | // SPDX-License-Identifier: GPL-2.0 |
8f03dea5 MJ |
2 | |
3 | #include <linux/types.h> | |
4 | #include <linux/netfilter.h> | |
5 | #include <linux/module.h> | |
5a0e3ad6 | 6 | #include <linux/slab.h> |
d62f9ed4 | 7 | #include <linux/mutex.h> |
8f03dea5 MJ |
8 | #include <linux/vmalloc.h> |
9 | #include <linux/stddef.h> | |
10 | #include <linux/err.h> | |
11 | #include <linux/percpu.h> | |
8f03dea5 MJ |
12 | #include <linux/notifier.h> |
13 | #include <linux/kernel.h> | |
14 | #include <linux/netdevice.h> | |
15 | ||
16 | #include <net/netfilter/nf_conntrack.h> | |
605dcad6 | 17 | #include <net/netfilter/nf_conntrack_l4proto.h> |
8f03dea5 | 18 | #include <net/netfilter/nf_conntrack_core.h> |
d035f19f | 19 | #include <net/netfilter/nf_conntrack_bridge.h> |
c4f3db15 | 20 | #include <net/netfilter/nf_log.h> |
8f03dea5 | 21 | |
a0ae2562 FW |
22 | #include <linux/ip.h> |
23 | #include <linux/icmp.h> | |
24 | #include <linux/sysctl.h> | |
25 | #include <net/route.h> | |
26 | #include <net/ip.h> | |
27 | ||
28 | #include <linux/netfilter_ipv4.h> | |
29 | #include <linux/netfilter_ipv6.h> | |
30 | #include <linux/netfilter_ipv6/ip6_tables.h> | |
31 | #include <net/netfilter/nf_conntrack_helper.h> | |
32 | #include <net/netfilter/nf_conntrack_zones.h> | |
33 | #include <net/netfilter/nf_conntrack_seqadj.h> | |
34 | #include <net/netfilter/ipv4/nf_conntrack_ipv4.h> | |
35 | #include <net/netfilter/ipv6/nf_conntrack_ipv6.h> | |
36 | #include <net/netfilter/nf_nat_helper.h> | |
37 | #include <net/netfilter/ipv4/nf_defrag_ipv4.h> | |
38 | #include <net/netfilter/ipv6/nf_defrag_ipv6.h> | |
39 | ||
40 | #include <linux/ipv6.h> | |
41 | #include <linux/in6.h> | |
42 | #include <net/ipv6.h> | |
43 | #include <net/inet_frag.h> | |
44 | ||
45 | extern unsigned int nf_conntrack_net_id; | |
46 | ||
b19caa0c | 47 | static DEFINE_MUTEX(nf_ct_proto_mutex); |
d62f9ed4 | 48 | |
b19caa0c | 49 | #ifdef CONFIG_SYSCTL |
c4f3db15 FW |
50 | __printf(5, 6) |
51 | void nf_l4proto_log_invalid(const struct sk_buff *skb, | |
52 | struct net *net, | |
53 | u16 pf, u8 protonum, | |
54 | const char *fmt, ...) | |
55 | { | |
56 | struct va_format vaf; | |
57 | va_list args; | |
58 | ||
d4866805 | 59 | if (net->ct.sysctl_log_invalid != protonum && |
c4f3db15 FW |
60 | net->ct.sysctl_log_invalid != IPPROTO_RAW) |
61 | return; | |
62 | ||
63 | va_start(args, fmt); | |
64 | vaf.fmt = fmt; | |
65 | vaf.va = &args; | |
66 | ||
67 | nf_log_packet(net, pf, 0, skb, NULL, NULL, NULL, | |
68 | "nf_ct_proto_%d: %pV ", protonum, &vaf); | |
69 | va_end(args); | |
70 | } | |
71 | EXPORT_SYMBOL_GPL(nf_l4proto_log_invalid); | |
3d0b527b FW |
72 | |
73 | __printf(3, 4) | |
74 | void nf_ct_l4proto_log_invalid(const struct sk_buff *skb, | |
75 | const struct nf_conn *ct, | |
76 | const char *fmt, ...) | |
77 | { | |
78 | struct va_format vaf; | |
79 | struct net *net; | |
80 | va_list args; | |
81 | ||
82 | net = nf_ct_net(ct); | |
83 | if (likely(net->ct.sysctl_log_invalid == 0)) | |
84 | return; | |
85 | ||
86 | va_start(args, fmt); | |
87 | vaf.fmt = fmt; | |
88 | vaf.va = &args; | |
89 | ||
90 | nf_l4proto_log_invalid(skb, net, nf_ct_l3num(ct), | |
91 | nf_ct_protonum(ct), "%pV", &vaf); | |
92 | va_end(args); | |
93 | } | |
94 | EXPORT_SYMBOL_GPL(nf_ct_l4proto_log_invalid); | |
d62f9ed4 PM |
95 | #endif |
96 | ||
4a60dc74 | 97 | const struct nf_conntrack_l4proto *nf_ct_l4proto_find(u8 l4proto) |
8f03dea5 | 98 | { |
4a60dc74 FW |
99 | switch (l4proto) { |
100 | case IPPROTO_UDP: return &nf_conntrack_l4proto_udp; | |
101 | case IPPROTO_TCP: return &nf_conntrack_l4proto_tcp; | |
102 | case IPPROTO_ICMP: return &nf_conntrack_l4proto_icmp; | |
103 | #ifdef CONFIG_NF_CT_PROTO_DCCP | |
104 | case IPPROTO_DCCP: return &nf_conntrack_l4proto_dccp; | |
105 | #endif | |
106 | #ifdef CONFIG_NF_CT_PROTO_SCTP | |
107 | case IPPROTO_SCTP: return &nf_conntrack_l4proto_sctp; | |
108 | #endif | |
109 | #ifdef CONFIG_NF_CT_PROTO_UDPLITE | |
110 | case IPPROTO_UDPLITE: return &nf_conntrack_l4proto_udplite; | |
111 | #endif | |
112 | #ifdef CONFIG_NF_CT_PROTO_GRE | |
113 | case IPPROTO_GRE: return &nf_conntrack_l4proto_gre; | |
114 | #endif | |
115 | #if IS_ENABLED(CONFIG_IPV6) | |
116 | case IPPROTO_ICMPV6: return &nf_conntrack_l4proto_icmpv6; | |
117 | #endif /* CONFIG_IPV6 */ | |
8f03dea5 MJ |
118 | } |
119 | ||
4a60dc74 FW |
120 | return &nf_conntrack_l4proto_generic; |
121 | }; | |
122 | EXPORT_SYMBOL_GPL(nf_ct_l4proto_find); | |
0e54d217 | 123 | |
3c171f49 PNA |
124 | unsigned int nf_confirm(struct sk_buff *skb, unsigned int protoff, |
125 | struct nf_conn *ct, enum ip_conntrack_info ctinfo) | |
a0ae2562 | 126 | { |
a0ae2562 | 127 | const struct nf_conn_help *help; |
a0ae2562 FW |
128 | |
129 | help = nfct_help(ct); | |
827318fe FW |
130 | if (help) { |
131 | const struct nf_conntrack_helper *helper; | |
132 | int ret; | |
133 | ||
134 | /* rcu_read_lock()ed by nf_hook_thresh */ | |
135 | helper = rcu_dereference(help->helper); | |
136 | if (helper) { | |
137 | ret = helper->help(skb, | |
138 | protoff, | |
139 | ct, ctinfo); | |
140 | if (ret != NF_ACCEPT) | |
141 | return ret; | |
142 | } | |
143 | } | |
a0ae2562 | 144 | |
827318fe FW |
145 | if (test_bit(IPS_SEQ_ADJUST_BIT, &ct->status) && |
146 | !nf_is_loopback_packet(skb)) { | |
147 | if (!nf_ct_seq_adjust(skb, ct, ctinfo, protoff)) { | |
148 | NF_CT_STAT_INC_ATOMIC(nf_ct_net(ct), drop); | |
149 | return NF_DROP; | |
150 | } | |
151 | } | |
a0ae2562 | 152 | |
827318fe FW |
153 | /* We've seen it coming out the other side: confirm it */ |
154 | return nf_conntrack_confirm(skb); | |
a0ae2562 | 155 | } |
3c171f49 | 156 | EXPORT_SYMBOL_GPL(nf_confirm); |
a0ae2562 FW |
157 | |
158 | static unsigned int ipv4_confirm(void *priv, | |
159 | struct sk_buff *skb, | |
160 | const struct nf_hook_state *state) | |
161 | { | |
a0ae2562 | 162 | enum ip_conntrack_info ctinfo; |
827318fe | 163 | struct nf_conn *ct; |
a0ae2562 FW |
164 | |
165 | ct = nf_ct_get(skb, &ctinfo); | |
166 | if (!ct || ctinfo == IP_CT_RELATED_REPLY) | |
827318fe | 167 | return nf_conntrack_confirm(skb); |
a0ae2562 | 168 | |
827318fe FW |
169 | return nf_confirm(skb, |
170 | skb_network_offset(skb) + ip_hdrlen(skb), | |
171 | ct, ctinfo); | |
a0ae2562 FW |
172 | } |
173 | ||
174 | static unsigned int ipv4_conntrack_in(void *priv, | |
175 | struct sk_buff *skb, | |
176 | const struct nf_hook_state *state) | |
177 | { | |
93e66024 | 178 | return nf_conntrack_in(skb, state); |
a0ae2562 FW |
179 | } |
180 | ||
181 | static unsigned int ipv4_conntrack_local(void *priv, | |
182 | struct sk_buff *skb, | |
183 | const struct nf_hook_state *state) | |
184 | { | |
185 | if (ip_is_fragment(ip_hdr(skb))) { /* IP_NODEFRAG setsockopt set */ | |
186 | enum ip_conntrack_info ctinfo; | |
187 | struct nf_conn *tmpl; | |
188 | ||
189 | tmpl = nf_ct_get(skb, &ctinfo); | |
190 | if (tmpl && nf_ct_is_template(tmpl)) { | |
191 | /* when skipping ct, clear templates to avoid fooling | |
192 | * later targets/matches | |
193 | */ | |
194 | skb->_nfct = 0; | |
195 | nf_ct_put(tmpl); | |
196 | } | |
197 | return NF_ACCEPT; | |
198 | } | |
199 | ||
93e66024 | 200 | return nf_conntrack_in(skb, state); |
a0ae2562 FW |
201 | } |
202 | ||
203 | /* Connection tracking may drop packets, but never alters them, so | |
204 | * make it the first hook. | |
205 | */ | |
206 | static const struct nf_hook_ops ipv4_conntrack_ops[] = { | |
207 | { | |
208 | .hook = ipv4_conntrack_in, | |
209 | .pf = NFPROTO_IPV4, | |
210 | .hooknum = NF_INET_PRE_ROUTING, | |
211 | .priority = NF_IP_PRI_CONNTRACK, | |
212 | }, | |
213 | { | |
214 | .hook = ipv4_conntrack_local, | |
215 | .pf = NFPROTO_IPV4, | |
216 | .hooknum = NF_INET_LOCAL_OUT, | |
217 | .priority = NF_IP_PRI_CONNTRACK, | |
218 | }, | |
a0ae2562 FW |
219 | { |
220 | .hook = ipv4_confirm, | |
221 | .pf = NFPROTO_IPV4, | |
222 | .hooknum = NF_INET_POST_ROUTING, | |
223 | .priority = NF_IP_PRI_CONNTRACK_CONFIRM, | |
224 | }, | |
a0ae2562 FW |
225 | { |
226 | .hook = ipv4_confirm, | |
227 | .pf = NFPROTO_IPV4, | |
228 | .hooknum = NF_INET_LOCAL_IN, | |
229 | .priority = NF_IP_PRI_CONNTRACK_CONFIRM, | |
230 | }, | |
231 | }; | |
232 | ||
233 | /* Fast function for those who don't want to parse /proc (and I don't | |
234 | * blame them). | |
235 | * Reversing the socket's dst/src point of view gives us the reply | |
236 | * mapping. | |
237 | */ | |
238 | static int | |
239 | getorigdst(struct sock *sk, int optval, void __user *user, int *len) | |
240 | { | |
241 | const struct inet_sock *inet = inet_sk(sk); | |
242 | const struct nf_conntrack_tuple_hash *h; | |
243 | struct nf_conntrack_tuple tuple; | |
244 | ||
245 | memset(&tuple, 0, sizeof(tuple)); | |
246 | ||
247 | lock_sock(sk); | |
248 | tuple.src.u3.ip = inet->inet_rcv_saddr; | |
249 | tuple.src.u.tcp.port = inet->inet_sport; | |
250 | tuple.dst.u3.ip = inet->inet_daddr; | |
251 | tuple.dst.u.tcp.port = inet->inet_dport; | |
252 | tuple.src.l3num = PF_INET; | |
253 | tuple.dst.protonum = sk->sk_protocol; | |
254 | release_sock(sk); | |
255 | ||
256 | /* We only do TCP and SCTP at the moment: is there a better way? */ | |
257 | if (tuple.dst.protonum != IPPROTO_TCP && | |
258 | tuple.dst.protonum != IPPROTO_SCTP) { | |
259 | pr_debug("SO_ORIGINAL_DST: Not a TCP/SCTP socket\n"); | |
260 | return -ENOPROTOOPT; | |
261 | } | |
262 | ||
263 | if ((unsigned int)*len < sizeof(struct sockaddr_in)) { | |
264 | pr_debug("SO_ORIGINAL_DST: len %d not %zu\n", | |
265 | *len, sizeof(struct sockaddr_in)); | |
266 | return -EINVAL; | |
267 | } | |
268 | ||
269 | h = nf_conntrack_find_get(sock_net(sk), &nf_ct_zone_dflt, &tuple); | |
270 | if (h) { | |
271 | struct sockaddr_in sin; | |
272 | struct nf_conn *ct = nf_ct_tuplehash_to_ctrack(h); | |
273 | ||
274 | sin.sin_family = AF_INET; | |
275 | sin.sin_port = ct->tuplehash[IP_CT_DIR_ORIGINAL] | |
276 | .tuple.dst.u.tcp.port; | |
277 | sin.sin_addr.s_addr = ct->tuplehash[IP_CT_DIR_ORIGINAL] | |
278 | .tuple.dst.u3.ip; | |
279 | memset(sin.sin_zero, 0, sizeof(sin.sin_zero)); | |
280 | ||
281 | pr_debug("SO_ORIGINAL_DST: %pI4 %u\n", | |
282 | &sin.sin_addr.s_addr, ntohs(sin.sin_port)); | |
283 | nf_ct_put(ct); | |
284 | if (copy_to_user(user, &sin, sizeof(sin)) != 0) | |
285 | return -EFAULT; | |
286 | else | |
287 | return 0; | |
288 | } | |
289 | pr_debug("SO_ORIGINAL_DST: Can't find %pI4/%u-%pI4/%u.\n", | |
290 | &tuple.src.u3.ip, ntohs(tuple.src.u.tcp.port), | |
291 | &tuple.dst.u3.ip, ntohs(tuple.dst.u.tcp.port)); | |
292 | return -ENOENT; | |
293 | } | |
294 | ||
295 | static struct nf_sockopt_ops so_getorigdst = { | |
296 | .pf = PF_INET, | |
297 | .get_optmin = SO_ORIGINAL_DST, | |
298 | .get_optmax = SO_ORIGINAL_DST + 1, | |
299 | .get = getorigdst, | |
300 | .owner = THIS_MODULE, | |
301 | }; | |
302 | ||
303 | #if IS_ENABLED(CONFIG_IPV6) | |
304 | static int | |
305 | ipv6_getorigdst(struct sock *sk, int optval, void __user *user, int *len) | |
306 | { | |
307 | struct nf_conntrack_tuple tuple = { .src.l3num = NFPROTO_IPV6 }; | |
308 | const struct ipv6_pinfo *inet6 = inet6_sk(sk); | |
309 | const struct inet_sock *inet = inet_sk(sk); | |
310 | const struct nf_conntrack_tuple_hash *h; | |
311 | struct sockaddr_in6 sin6; | |
312 | struct nf_conn *ct; | |
313 | __be32 flow_label; | |
314 | int bound_dev_if; | |
315 | ||
316 | lock_sock(sk); | |
317 | tuple.src.u3.in6 = sk->sk_v6_rcv_saddr; | |
318 | tuple.src.u.tcp.port = inet->inet_sport; | |
319 | tuple.dst.u3.in6 = sk->sk_v6_daddr; | |
320 | tuple.dst.u.tcp.port = inet->inet_dport; | |
321 | tuple.dst.protonum = sk->sk_protocol; | |
322 | bound_dev_if = sk->sk_bound_dev_if; | |
323 | flow_label = inet6->flow_label; | |
324 | release_sock(sk); | |
325 | ||
326 | if (tuple.dst.protonum != IPPROTO_TCP && | |
327 | tuple.dst.protonum != IPPROTO_SCTP) | |
328 | return -ENOPROTOOPT; | |
329 | ||
330 | if (*len < 0 || (unsigned int)*len < sizeof(sin6)) | |
331 | return -EINVAL; | |
332 | ||
333 | h = nf_conntrack_find_get(sock_net(sk), &nf_ct_zone_dflt, &tuple); | |
334 | if (!h) { | |
335 | pr_debug("IP6T_SO_ORIGINAL_DST: Can't find %pI6c/%u-%pI6c/%u.\n", | |
336 | &tuple.src.u3.ip6, ntohs(tuple.src.u.tcp.port), | |
337 | &tuple.dst.u3.ip6, ntohs(tuple.dst.u.tcp.port)); | |
338 | return -ENOENT; | |
339 | } | |
340 | ||
341 | ct = nf_ct_tuplehash_to_ctrack(h); | |
342 | ||
343 | sin6.sin6_family = AF_INET6; | |
344 | sin6.sin6_port = ct->tuplehash[IP_CT_DIR_ORIGINAL].tuple.dst.u.tcp.port; | |
345 | sin6.sin6_flowinfo = flow_label & IPV6_FLOWINFO_MASK; | |
346 | memcpy(&sin6.sin6_addr, | |
347 | &ct->tuplehash[IP_CT_DIR_ORIGINAL].tuple.dst.u3.in6, | |
348 | sizeof(sin6.sin6_addr)); | |
349 | ||
350 | nf_ct_put(ct); | |
351 | sin6.sin6_scope_id = ipv6_iface_scope_id(&sin6.sin6_addr, bound_dev_if); | |
352 | return copy_to_user(user, &sin6, sizeof(sin6)) ? -EFAULT : 0; | |
353 | } | |
354 | ||
355 | static struct nf_sockopt_ops so_getorigdst6 = { | |
356 | .pf = NFPROTO_IPV6, | |
357 | .get_optmin = IP6T_SO_ORIGINAL_DST, | |
358 | .get_optmax = IP6T_SO_ORIGINAL_DST + 1, | |
359 | .get = ipv6_getorigdst, | |
360 | .owner = THIS_MODULE, | |
361 | }; | |
362 | ||
363 | static unsigned int ipv6_confirm(void *priv, | |
364 | struct sk_buff *skb, | |
365 | const struct nf_hook_state *state) | |
366 | { | |
367 | struct nf_conn *ct; | |
368 | enum ip_conntrack_info ctinfo; | |
369 | unsigned char pnum = ipv6_hdr(skb)->nexthdr; | |
a0ae2562 | 370 | __be16 frag_off; |
827318fe | 371 | int protoff; |
a0ae2562 FW |
372 | |
373 | ct = nf_ct_get(skb, &ctinfo); | |
374 | if (!ct || ctinfo == IP_CT_RELATED_REPLY) | |
827318fe | 375 | return nf_conntrack_confirm(skb); |
a0ae2562 FW |
376 | |
377 | protoff = ipv6_skip_exthdr(skb, sizeof(struct ipv6hdr), &pnum, | |
378 | &frag_off); | |
379 | if (protoff < 0 || (frag_off & htons(~0x7)) != 0) { | |
380 | pr_debug("proto header not found\n"); | |
827318fe | 381 | return nf_conntrack_confirm(skb); |
a0ae2562 FW |
382 | } |
383 | ||
827318fe | 384 | return nf_confirm(skb, protoff, ct, ctinfo); |
a0ae2562 FW |
385 | } |
386 | ||
387 | static unsigned int ipv6_conntrack_in(void *priv, | |
388 | struct sk_buff *skb, | |
389 | const struct nf_hook_state *state) | |
390 | { | |
93e66024 | 391 | return nf_conntrack_in(skb, state); |
a0ae2562 FW |
392 | } |
393 | ||
394 | static unsigned int ipv6_conntrack_local(void *priv, | |
395 | struct sk_buff *skb, | |
396 | const struct nf_hook_state *state) | |
397 | { | |
93e66024 | 398 | return nf_conntrack_in(skb, state); |
a0ae2562 FW |
399 | } |
400 | ||
a0ae2562 FW |
401 | static const struct nf_hook_ops ipv6_conntrack_ops[] = { |
402 | { | |
403 | .hook = ipv6_conntrack_in, | |
404 | .pf = NFPROTO_IPV6, | |
405 | .hooknum = NF_INET_PRE_ROUTING, | |
406 | .priority = NF_IP6_PRI_CONNTRACK, | |
407 | }, | |
408 | { | |
409 | .hook = ipv6_conntrack_local, | |
410 | .pf = NFPROTO_IPV6, | |
411 | .hooknum = NF_INET_LOCAL_OUT, | |
412 | .priority = NF_IP6_PRI_CONNTRACK, | |
413 | }, | |
a0ae2562 FW |
414 | { |
415 | .hook = ipv6_confirm, | |
416 | .pf = NFPROTO_IPV6, | |
417 | .hooknum = NF_INET_POST_ROUTING, | |
418 | .priority = NF_IP6_PRI_LAST, | |
419 | }, | |
a0ae2562 FW |
420 | { |
421 | .hook = ipv6_confirm, | |
422 | .pf = NFPROTO_IPV6, | |
423 | .hooknum = NF_INET_LOCAL_IN, | |
424 | .priority = NF_IP6_PRI_LAST - 1, | |
425 | }, | |
426 | }; | |
427 | #endif | |
428 | ||
f94e6380 FW |
429 | static int nf_ct_tcp_fixup(struct nf_conn *ct, void *_nfproto) |
430 | { | |
431 | u8 nfproto = (unsigned long)_nfproto; | |
432 | ||
433 | if (nf_ct_l3num(ct) != nfproto) | |
434 | return 0; | |
435 | ||
436 | if (nf_ct_protonum(ct) == IPPROTO_TCP && | |
437 | ct->proto.tcp.state == TCP_CONNTRACK_ESTABLISHED) { | |
438 | ct->proto.tcp.seen[0].td_maxwin = 0; | |
439 | ct->proto.tcp.seen[1].td_maxwin = 0; | |
440 | } | |
441 | ||
442 | return 0; | |
443 | } | |
444 | ||
d035f19f PNA |
445 | static struct nf_ct_bridge_info *nf_ct_bridge_info; |
446 | ||
a0ae2562 FW |
447 | static int nf_ct_netns_do_get(struct net *net, u8 nfproto) |
448 | { | |
449 | struct nf_conntrack_net *cnet = net_generic(net, nf_conntrack_net_id); | |
d035f19f | 450 | bool fixup_needed = false, retry = true; |
a0ae2562 | 451 | int err = 0; |
d035f19f | 452 | retry: |
a0ae2562 FW |
453 | mutex_lock(&nf_ct_proto_mutex); |
454 | ||
455 | switch (nfproto) { | |
456 | case NFPROTO_IPV4: | |
457 | cnet->users4++; | |
458 | if (cnet->users4 > 1) | |
459 | goto out_unlock; | |
460 | err = nf_defrag_ipv4_enable(net); | |
461 | if (err) { | |
462 | cnet->users4 = 0; | |
463 | goto out_unlock; | |
464 | } | |
465 | ||
466 | err = nf_register_net_hooks(net, ipv4_conntrack_ops, | |
467 | ARRAY_SIZE(ipv4_conntrack_ops)); | |
468 | if (err) | |
469 | cnet->users4 = 0; | |
f94e6380 FW |
470 | else |
471 | fixup_needed = true; | |
a0ae2562 FW |
472 | break; |
473 | #if IS_ENABLED(CONFIG_IPV6) | |
474 | case NFPROTO_IPV6: | |
475 | cnet->users6++; | |
476 | if (cnet->users6 > 1) | |
477 | goto out_unlock; | |
478 | err = nf_defrag_ipv6_enable(net); | |
479 | if (err < 0) { | |
480 | cnet->users6 = 0; | |
481 | goto out_unlock; | |
482 | } | |
483 | ||
484 | err = nf_register_net_hooks(net, ipv6_conntrack_ops, | |
485 | ARRAY_SIZE(ipv6_conntrack_ops)); | |
486 | if (err) | |
487 | cnet->users6 = 0; | |
f94e6380 FW |
488 | else |
489 | fixup_needed = true; | |
a0ae2562 FW |
490 | break; |
491 | #endif | |
d035f19f PNA |
492 | case NFPROTO_BRIDGE: |
493 | if (!nf_ct_bridge_info) { | |
494 | if (!retry) { | |
495 | err = -EPROTO; | |
496 | goto out_unlock; | |
497 | } | |
498 | mutex_unlock(&nf_ct_proto_mutex); | |
499 | request_module("nf_conntrack_bridge"); | |
500 | retry = false; | |
501 | goto retry; | |
502 | } | |
503 | if (!try_module_get(nf_ct_bridge_info->me)) { | |
504 | err = -EPROTO; | |
505 | goto out_unlock; | |
506 | } | |
507 | cnet->users_bridge++; | |
508 | if (cnet->users_bridge > 1) | |
509 | goto out_unlock; | |
510 | ||
511 | err = nf_register_net_hooks(net, nf_ct_bridge_info->ops, | |
512 | nf_ct_bridge_info->ops_size); | |
513 | if (err) | |
514 | cnet->users_bridge = 0; | |
515 | else | |
516 | fixup_needed = true; | |
517 | break; | |
a0ae2562 FW |
518 | default: |
519 | err = -EPROTO; | |
520 | break; | |
521 | } | |
522 | out_unlock: | |
523 | mutex_unlock(&nf_ct_proto_mutex); | |
f94e6380 FW |
524 | |
525 | if (fixup_needed) | |
526 | nf_ct_iterate_cleanup_net(net, nf_ct_tcp_fixup, | |
527 | (void *)(unsigned long)nfproto, 0, 0); | |
528 | ||
a0ae2562 FW |
529 | return err; |
530 | } | |
531 | ||
532 | static void nf_ct_netns_do_put(struct net *net, u8 nfproto) | |
533 | { | |
534 | struct nf_conntrack_net *cnet = net_generic(net, nf_conntrack_net_id); | |
535 | ||
536 | mutex_lock(&nf_ct_proto_mutex); | |
537 | switch (nfproto) { | |
538 | case NFPROTO_IPV4: | |
539 | if (cnet->users4 && (--cnet->users4 == 0)) | |
540 | nf_unregister_net_hooks(net, ipv4_conntrack_ops, | |
541 | ARRAY_SIZE(ipv4_conntrack_ops)); | |
542 | break; | |
543 | #if IS_ENABLED(CONFIG_IPV6) | |
544 | case NFPROTO_IPV6: | |
545 | if (cnet->users6 && (--cnet->users6 == 0)) | |
546 | nf_unregister_net_hooks(net, ipv6_conntrack_ops, | |
547 | ARRAY_SIZE(ipv6_conntrack_ops)); | |
548 | break; | |
549 | #endif | |
d035f19f PNA |
550 | case NFPROTO_BRIDGE: |
551 | if (!nf_ct_bridge_info) | |
552 | break; | |
553 | if (cnet->users_bridge && (--cnet->users_bridge == 0)) | |
554 | nf_unregister_net_hooks(net, nf_ct_bridge_info->ops, | |
555 | nf_ct_bridge_info->ops_size); | |
556 | ||
557 | module_put(nf_ct_bridge_info->me); | |
558 | break; | |
a0ae2562 | 559 | } |
a0ae2562 FW |
560 | mutex_unlock(&nf_ct_proto_mutex); |
561 | } | |
562 | ||
af9573be | 563 | static int nf_ct_netns_inet_get(struct net *net) |
a0ae2562 FW |
564 | { |
565 | int err; | |
566 | ||
af9573be PNA |
567 | err = nf_ct_netns_do_get(net, NFPROTO_IPV4); |
568 | if (err < 0) | |
569 | goto err1; | |
570 | err = nf_ct_netns_do_get(net, NFPROTO_IPV6); | |
571 | if (err < 0) | |
572 | goto err2; | |
a0ae2562 | 573 | |
af9573be | 574 | return err; |
a0ae2562 FW |
575 | err2: |
576 | nf_ct_netns_put(net, NFPROTO_IPV4); | |
577 | err1: | |
578 | return err; | |
579 | } | |
af9573be PNA |
580 | |
581 | int nf_ct_netns_get(struct net *net, u8 nfproto) | |
582 | { | |
583 | int err; | |
584 | ||
585 | switch (nfproto) { | |
586 | case NFPROTO_INET: | |
587 | err = nf_ct_netns_inet_get(net); | |
588 | break; | |
589 | case NFPROTO_BRIDGE: | |
590 | err = nf_ct_netns_do_get(net, NFPROTO_BRIDGE); | |
591 | if (err < 0) | |
592 | return err; | |
593 | ||
594 | err = nf_ct_netns_inet_get(net); | |
595 | if (err < 0) { | |
596 | nf_ct_netns_put(net, NFPROTO_BRIDGE); | |
597 | return err; | |
598 | } | |
599 | break; | |
600 | default: | |
601 | err = nf_ct_netns_do_get(net, nfproto); | |
602 | break; | |
603 | } | |
604 | return err; | |
605 | } | |
a0ae2562 FW |
606 | EXPORT_SYMBOL_GPL(nf_ct_netns_get); |
607 | ||
608 | void nf_ct_netns_put(struct net *net, uint8_t nfproto) | |
609 | { | |
af9573be PNA |
610 | switch (nfproto) { |
611 | case NFPROTO_BRIDGE: | |
612 | nf_ct_netns_do_put(net, NFPROTO_BRIDGE); | |
613 | /* fall through */ | |
614 | case NFPROTO_INET: | |
a0ae2562 FW |
615 | nf_ct_netns_do_put(net, NFPROTO_IPV4); |
616 | nf_ct_netns_do_put(net, NFPROTO_IPV6); | |
af9573be PNA |
617 | break; |
618 | default: | |
a0ae2562 | 619 | nf_ct_netns_do_put(net, nfproto); |
af9573be | 620 | break; |
a0ae2562 FW |
621 | } |
622 | } | |
623 | EXPORT_SYMBOL_GPL(nf_ct_netns_put); | |
624 | ||
d035f19f PNA |
625 | void nf_ct_bridge_register(struct nf_ct_bridge_info *info) |
626 | { | |
627 | WARN_ON(nf_ct_bridge_info); | |
628 | mutex_lock(&nf_ct_proto_mutex); | |
629 | nf_ct_bridge_info = info; | |
630 | mutex_unlock(&nf_ct_proto_mutex); | |
631 | } | |
632 | EXPORT_SYMBOL_GPL(nf_ct_bridge_register); | |
633 | ||
634 | void nf_ct_bridge_unregister(struct nf_ct_bridge_info *info) | |
635 | { | |
636 | WARN_ON(!nf_ct_bridge_info); | |
637 | mutex_lock(&nf_ct_proto_mutex); | |
638 | nf_ct_bridge_info = NULL; | |
639 | mutex_unlock(&nf_ct_proto_mutex); | |
640 | } | |
641 | EXPORT_SYMBOL_GPL(nf_ct_bridge_unregister); | |
642 | ||
a0ae2562 FW |
643 | int nf_conntrack_proto_init(void) |
644 | { | |
4a60dc74 | 645 | int ret; |
a0ae2562 FW |
646 | |
647 | ret = nf_register_sockopt(&so_getorigdst); | |
648 | if (ret < 0) | |
649 | return ret; | |
650 | ||
651 | #if IS_ENABLED(CONFIG_IPV6) | |
652 | ret = nf_register_sockopt(&so_getorigdst6); | |
653 | if (ret < 0) | |
654 | goto cleanup_sockopt; | |
655 | #endif | |
dd2934a9 | 656 | |
a0ae2562 | 657 | return ret; |
4a60dc74 | 658 | |
a0ae2562 FW |
659 | #if IS_ENABLED(CONFIG_IPV6) |
660 | cleanup_sockopt: | |
661 | nf_unregister_sockopt(&so_getorigdst6); | |
662 | #endif | |
663 | return ret; | |
664 | } | |
665 | ||
666 | void nf_conntrack_proto_fini(void) | |
667 | { | |
a0ae2562 FW |
668 | nf_unregister_sockopt(&so_getorigdst); |
669 | #if IS_ENABLED(CONFIG_IPV6) | |
670 | nf_unregister_sockopt(&so_getorigdst6); | |
671 | #endif | |
a0ae2562 FW |
672 | } |
673 | ||
4a60dc74 | 674 | void nf_conntrack_proto_pernet_init(struct net *net) |
ac5357eb | 675 | { |
2a389de8 FW |
676 | nf_conntrack_generic_init_net(net); |
677 | nf_conntrack_udp_init_net(net); | |
678 | nf_conntrack_tcp_init_net(net); | |
679 | nf_conntrack_icmp_init_net(net); | |
81e01647 | 680 | #if IS_ENABLED(CONFIG_IPV6) |
2a389de8 | 681 | nf_conntrack_icmpv6_init_net(net); |
81e01647 | 682 | #endif |
2a389de8 FW |
683 | #ifdef CONFIG_NF_CT_PROTO_DCCP |
684 | nf_conntrack_dccp_init_net(net); | |
685 | #endif | |
686 | #ifdef CONFIG_NF_CT_PROTO_SCTP | |
687 | nf_conntrack_sctp_init_net(net); | |
688 | #endif | |
689 | #ifdef CONFIG_NF_CT_PROTO_GRE | |
690 | nf_conntrack_gre_init_net(net); | |
691 | #endif | |
ac5357eb PM |
692 | } |
693 | ||
04d87001 | 694 | void nf_conntrack_proto_pernet_fini(struct net *net) |
ac5357eb | 695 | { |
22fc4c4c FW |
696 | #ifdef CONFIG_NF_CT_PROTO_GRE |
697 | nf_ct_gre_keymap_flush(net); | |
698 | #endif | |
04d87001 G |
699 | } |
700 | ||
a0ae2562 FW |
701 | module_param_call(hashsize, nf_conntrack_set_hashsize, param_get_uint, |
702 | &nf_conntrack_htable_size, 0600); | |
703 | ||
704 | MODULE_ALIAS("ip_conntrack"); | |
705 | MODULE_ALIAS("nf_conntrack-" __stringify(AF_INET)); | |
706 | MODULE_ALIAS("nf_conntrack-" __stringify(AF_INET6)); | |
707 | MODULE_LICENSE("GPL"); |