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