]> git.proxmox.com Git - mirror_ubuntu-bionic-kernel.git/blame - net/ipv4/netfilter/nf_conntrack_l3proto_ipv4.c
netfilter: nf_conntrack: refactor l4proto support for netns
[mirror_ubuntu-bionic-kernel.git] / net / ipv4 / netfilter / nf_conntrack_l3proto_ipv4.c
CommitLineData
73e4022f 1
9fb9cbb1
YK
2/* (C) 1999-2001 Paul `Rusty' Russell
3 * (C) 2002-2004 Netfilter Core Team <coreteam@netfilter.org>
4 *
5 * This program is free software; you can redistribute it and/or modify
6 * it under the terms of the GNU General Public License version 2 as
7 * published by the Free Software Foundation.
9fb9cbb1
YK
8 */
9
9fb9cbb1
YK
10#include <linux/types.h>
11#include <linux/ip.h>
12#include <linux/netfilter.h>
13#include <linux/module.h>
14#include <linux/skbuff.h>
15#include <linux/icmp.h>
16#include <linux/sysctl.h>
0ae2cfe7 17#include <net/route.h>
9fb9cbb1
YK
18#include <net/ip.h>
19
20#include <linux/netfilter_ipv4.h>
21#include <net/netfilter/nf_conntrack.h>
22#include <net/netfilter/nf_conntrack_helper.h>
605dcad6 23#include <net/netfilter/nf_conntrack_l4proto.h>
9fb9cbb1 24#include <net/netfilter/nf_conntrack_l3proto.h>
5d0aa2cc 25#include <net/netfilter/nf_conntrack_zones.h>
9fb9cbb1
YK
26#include <net/netfilter/nf_conntrack_core.h>
27#include <net/netfilter/ipv4/nf_conntrack_ipv4.h>
dd13b010 28#include <net/netfilter/nf_nat_helper.h>
73e4022f 29#include <net/netfilter/ipv4/nf_defrag_ipv4.h>
74f7a655 30#include <net/netfilter/nf_log.h>
dd13b010 31
8ce8439a
JE
32static bool ipv4_pkt_to_tuple(const struct sk_buff *skb, unsigned int nhoff,
33 struct nf_conntrack_tuple *tuple)
9fb9cbb1 34{
32948588
JE
35 const __be32 *ap;
36 __be32 _addrs[2];
9fb9cbb1
YK
37 ap = skb_header_pointer(skb, nhoff + offsetof(struct iphdr, saddr),
38 sizeof(u_int32_t) * 2, _addrs);
39 if (ap == NULL)
8ce8439a 40 return false;
9fb9cbb1
YK
41
42 tuple->src.u3.ip = ap[0];
43 tuple->dst.u3.ip = ap[1];
44
8ce8439a 45 return true;
9fb9cbb1
YK
46}
47
8ce8439a
JE
48static bool ipv4_invert_tuple(struct nf_conntrack_tuple *tuple,
49 const struct nf_conntrack_tuple *orig)
9fb9cbb1
YK
50{
51 tuple->src.u3.ip = orig->dst.u3.ip;
52 tuple->dst.u3.ip = orig->src.u3.ip;
53
8ce8439a 54 return true;
9fb9cbb1
YK
55}
56
57static int ipv4_print_tuple(struct seq_file *s,
58 const struct nf_conntrack_tuple *tuple)
59{
cffee385
HH
60 return seq_printf(s, "src=%pI4 dst=%pI4 ",
61 &tuple->src.u3.ip, &tuple->dst.u3.ip);
9fb9cbb1
YK
62}
63
ffc30690
YK
64static int ipv4_get_l4proto(const struct sk_buff *skb, unsigned int nhoff,
65 unsigned int *dataoff, u_int8_t *protonum)
9fb9cbb1 66{
32948588
JE
67 const struct iphdr *iph;
68 struct iphdr _iph;
ffc30690
YK
69
70 iph = skb_header_pointer(skb, nhoff, sizeof(_iph), &_iph);
71 if (iph == NULL)
8430eac2 72 return -NF_ACCEPT;
ffc30690 73
0fb96701
PM
74 /* Conntrack defragments packets, we might still see fragments
75 * inside ICMP packets though. */
76 if (iph->frag_off & htons(IP_OFFSET))
8430eac2 77 return -NF_ACCEPT;
9fb9cbb1 78
ffc30690
YK
79 *dataoff = nhoff + (iph->ihl << 2);
80 *protonum = iph->protocol;
9fb9cbb1 81
07153c6e
JK
82 /* Check bogus IP headers */
83 if (*dataoff > skb->len) {
84 pr_debug("nf_conntrack_ipv4: bogus IPv4 packet: "
85 "nhoff %u, ihl %u, skblen %u\n",
86 nhoff, iph->ihl << 2, skb->len);
87 return -NF_ACCEPT;
88 }
89
9fb9cbb1
YK
90 return NF_ACCEPT;
91}
92
12f7a505
PNA
93static unsigned int ipv4_helper(unsigned int hooknum,
94 struct sk_buff *skb,
95 const struct net_device *in,
96 const struct net_device *out,
97 int (*okfn)(struct sk_buff *))
9fb9cbb1
YK
98{
99 struct nf_conn *ct;
100 enum ip_conntrack_info ctinfo;
32948588
JE
101 const struct nf_conn_help *help;
102 const struct nf_conntrack_helper *helper;
dd13b010 103 unsigned int ret;
9fb9cbb1
YK
104
105 /* This is where we call the helper: as the packet goes out. */
3db05fea 106 ct = nf_ct_get(skb, &ctinfo);
fb048833 107 if (!ct || ctinfo == IP_CT_RELATED_REPLY)
12f7a505 108 return NF_ACCEPT;
dc808fe2
HW
109
110 help = nfct_help(ct);
3c158f7f 111 if (!help)
12f7a505 112 return NF_ACCEPT;
dd13b010 113
3c158f7f
PM
114 /* rcu_read_lock()ed by nf_hook_slow */
115 helper = rcu_dereference(help->helper);
116 if (!helper)
12f7a505 117 return NF_ACCEPT;
dd13b010
PM
118
119 ret = helper->help(skb, skb_network_offset(skb) + ip_hdrlen(skb),
120 ct, ctinfo);
12f7a505 121 if (ret != NF_ACCEPT && (ret & NF_VERDICT_MASK) != NF_QUEUE) {
74f7a655
PM
122 nf_log_packet(NFPROTO_IPV4, hooknum, skb, in, out, NULL,
123 "nf_ct_%s: dropping packet", helper->name);
74f7a655 124 }
12f7a505
PNA
125 return ret;
126}
127
128static unsigned int ipv4_confirm(unsigned int hooknum,
129 struct sk_buff *skb,
130 const struct net_device *in,
131 const struct net_device *out,
132 int (*okfn)(struct sk_buff *))
133{
134 struct nf_conn *ct;
135 enum ip_conntrack_info ctinfo;
136
137 ct = nf_ct_get(skb, &ctinfo);
138 if (!ct || ctinfo == IP_CT_RELATED_REPLY)
139 goto out;
dd13b010 140
42c1edd3
JA
141 /* adjust seqs for loopback traffic only in outgoing direction */
142 if (test_bit(IPS_SEQ_ADJUST_BIT, &ct->status) &&
143 !nf_is_loopback_packet(skb)) {
dd13b010
PM
144 typeof(nf_nat_seq_adjust_hook) seq_adjust;
145
146 seq_adjust = rcu_dereference(nf_nat_seq_adjust_hook);
051966c0
PM
147 if (!seq_adjust ||
148 !seq_adjust(skb, ct, ctinfo, ip_hdrlen(skb))) {
1db7a748 149 NF_CT_STAT_INC_ATOMIC(nf_ct_net(ct), drop);
dd13b010 150 return NF_DROP;
1db7a748 151 }
dd13b010
PM
152 }
153out:
154 /* We've seen it coming out the other side: confirm it */
155 return nf_conntrack_confirm(skb);
9fb9cbb1
YK
156}
157
9fb9cbb1 158static unsigned int ipv4_conntrack_in(unsigned int hooknum,
3db05fea 159 struct sk_buff *skb,
9fb9cbb1
YK
160 const struct net_device *in,
161 const struct net_device *out,
162 int (*okfn)(struct sk_buff *))
163{
a702a65f 164 return nf_conntrack_in(dev_net(in), PF_INET, hooknum, skb);
9fb9cbb1
YK
165}
166
167static unsigned int ipv4_conntrack_local(unsigned int hooknum,
3db05fea 168 struct sk_buff *skb,
e905a9ed
YH
169 const struct net_device *in,
170 const struct net_device *out,
171 int (*okfn)(struct sk_buff *))
9fb9cbb1
YK
172{
173 /* root is playing with raw sockets. */
3db05fea 174 if (skb->len < sizeof(struct iphdr) ||
88843104 175 ip_hdrlen(skb) < sizeof(struct iphdr))
9fb9cbb1 176 return NF_ACCEPT;
a702a65f 177 return nf_conntrack_in(dev_net(out), PF_INET, hooknum, skb);
9fb9cbb1
YK
178}
179
180/* Connection tracking may drop packets, but never alters them, so
181 make it the first hook. */
1999414a 182static struct nf_hook_ops ipv4_conntrack_ops[] __read_mostly = {
964ddaa1
PM
183 {
184 .hook = ipv4_conntrack_in,
185 .owner = THIS_MODULE,
57750a22 186 .pf = NFPROTO_IPV4,
6e23ae2a 187 .hooknum = NF_INET_PRE_ROUTING,
964ddaa1
PM
188 .priority = NF_IP_PRI_CONNTRACK,
189 },
964ddaa1
PM
190 {
191 .hook = ipv4_conntrack_local,
192 .owner = THIS_MODULE,
57750a22 193 .pf = NFPROTO_IPV4,
6e23ae2a 194 .hooknum = NF_INET_LOCAL_OUT,
964ddaa1
PM
195 .priority = NF_IP_PRI_CONNTRACK,
196 },
12f7a505
PNA
197 {
198 .hook = ipv4_helper,
199 .owner = THIS_MODULE,
200 .pf = NFPROTO_IPV4,
201 .hooknum = NF_INET_POST_ROUTING,
202 .priority = NF_IP_PRI_CONNTRACK_HELPER,
203 },
964ddaa1
PM
204 {
205 .hook = ipv4_confirm,
206 .owner = THIS_MODULE,
57750a22 207 .pf = NFPROTO_IPV4,
6e23ae2a 208 .hooknum = NF_INET_POST_ROUTING,
964ddaa1
PM
209 .priority = NF_IP_PRI_CONNTRACK_CONFIRM,
210 },
12f7a505
PNA
211 {
212 .hook = ipv4_helper,
213 .owner = THIS_MODULE,
214 .pf = NFPROTO_IPV4,
215 .hooknum = NF_INET_LOCAL_IN,
216 .priority = NF_IP_PRI_CONNTRACK_HELPER,
217 },
964ddaa1
PM
218 {
219 .hook = ipv4_confirm,
220 .owner = THIS_MODULE,
57750a22 221 .pf = NFPROTO_IPV4,
6e23ae2a 222 .hooknum = NF_INET_LOCAL_IN,
964ddaa1
PM
223 .priority = NF_IP_PRI_CONNTRACK_CONFIRM,
224 },
9fb9cbb1
YK
225};
226
a999e683
PM
227#if defined(CONFIG_SYSCTL) && defined(CONFIG_NF_CONNTRACK_PROC_COMPAT)
228static int log_invalid_proto_min = 0;
229static int log_invalid_proto_max = 255;
230
231static ctl_table ip_ct_sysctl_table[] = {
232 {
a999e683 233 .procname = "ip_conntrack_max",
a999e683
PM
234 .maxlen = sizeof(int),
235 .mode = 0644,
6d9f239a 236 .proc_handler = proc_dointvec,
a999e683
PM
237 },
238 {
a999e683 239 .procname = "ip_conntrack_count",
a999e683
PM
240 .maxlen = sizeof(int),
241 .mode = 0444,
6d9f239a 242 .proc_handler = proc_dointvec,
a999e683
PM
243 },
244 {
a999e683 245 .procname = "ip_conntrack_buckets",
a999e683
PM
246 .maxlen = sizeof(unsigned int),
247 .mode = 0444,
6d9f239a 248 .proc_handler = proc_dointvec,
a999e683
PM
249 },
250 {
a999e683 251 .procname = "ip_conntrack_checksum",
a999e683
PM
252 .maxlen = sizeof(int),
253 .mode = 0644,
6d9f239a 254 .proc_handler = proc_dointvec,
a999e683
PM
255 },
256 {
a999e683 257 .procname = "ip_conntrack_log_invalid",
a999e683
PM
258 .maxlen = sizeof(unsigned int),
259 .mode = 0644,
6d9f239a 260 .proc_handler = proc_dointvec_minmax,
a999e683
PM
261 .extra1 = &log_invalid_proto_min,
262 .extra2 = &log_invalid_proto_max,
263 },
f8572d8f 264 { }
a999e683
PM
265};
266#endif /* CONFIG_SYSCTL && CONFIG_NF_CONNTRACK_PROC_COMPAT */
267
9fb9cbb1
YK
268/* Fast function for those who don't want to parse /proc (and I don't
269 blame them). */
270/* Reversing the socket's dst/src point of view gives us the reply
271 mapping. */
272static int
273getorigdst(struct sock *sk, int optval, void __user *user, int *len)
274{
32948588
JE
275 const struct inet_sock *inet = inet_sk(sk);
276 const struct nf_conntrack_tuple_hash *h;
9fb9cbb1 277 struct nf_conntrack_tuple tuple;
e905a9ed 278
443a70d5 279 memset(&tuple, 0, sizeof(tuple));
c720c7e8
ED
280 tuple.src.u3.ip = inet->inet_rcv_saddr;
281 tuple.src.u.tcp.port = inet->inet_sport;
282 tuple.dst.u3.ip = inet->inet_daddr;
283 tuple.dst.u.tcp.port = inet->inet_dport;
9fb9cbb1 284 tuple.src.l3num = PF_INET;
54981279 285 tuple.dst.protonum = sk->sk_protocol;
9fb9cbb1 286
54981279
RL
287 /* We only do TCP and SCTP at the moment: is there a better way? */
288 if (sk->sk_protocol != IPPROTO_TCP && sk->sk_protocol != IPPROTO_SCTP) {
289 pr_debug("SO_ORIGINAL_DST: Not a TCP/SCTP socket\n");
9fb9cbb1
YK
290 return -ENOPROTOOPT;
291 }
292
293 if ((unsigned int) *len < sizeof(struct sockaddr_in)) {
0d53778e
PM
294 pr_debug("SO_ORIGINAL_DST: len %d not %Zu\n",
295 *len, sizeof(struct sockaddr_in));
9fb9cbb1
YK
296 return -EINVAL;
297 }
298
5d0aa2cc 299 h = nf_conntrack_find_get(sock_net(sk), NF_CT_DEFAULT_ZONE, &tuple);
9fb9cbb1
YK
300 if (h) {
301 struct sockaddr_in sin;
302 struct nf_conn *ct = nf_ct_tuplehash_to_ctrack(h);
303
304 sin.sin_family = AF_INET;
305 sin.sin_port = ct->tuplehash[IP_CT_DIR_ORIGINAL]
306 .tuple.dst.u.tcp.port;
307 sin.sin_addr.s_addr = ct->tuplehash[IP_CT_DIR_ORIGINAL]
308 .tuple.dst.u3.ip;
6c813c3f 309 memset(sin.sin_zero, 0, sizeof(sin.sin_zero));
9fb9cbb1 310
cffee385
HH
311 pr_debug("SO_ORIGINAL_DST: %pI4 %u\n",
312 &sin.sin_addr.s_addr, ntohs(sin.sin_port));
9fb9cbb1
YK
313 nf_ct_put(ct);
314 if (copy_to_user(user, &sin, sizeof(sin)) != 0)
315 return -EFAULT;
316 else
317 return 0;
318 }
cffee385
HH
319 pr_debug("SO_ORIGINAL_DST: Can't find %pI4/%u-%pI4/%u.\n",
320 &tuple.src.u3.ip, ntohs(tuple.src.u.tcp.port),
321 &tuple.dst.u3.ip, ntohs(tuple.dst.u.tcp.port));
9fb9cbb1
YK
322 return -ENOENT;
323}
324
e281db5c 325#if defined(CONFIG_NF_CT_NETLINK) || defined(CONFIG_NF_CT_NETLINK_MODULE)
c1d10adb
PNA
326
327#include <linux/netfilter/nfnetlink.h>
328#include <linux/netfilter/nfnetlink_conntrack.h>
329
fdf70832 330static int ipv4_tuple_to_nlattr(struct sk_buff *skb,
c1d10adb
PNA
331 const struct nf_conntrack_tuple *tuple)
332{
d317e4f6
DM
333 if (nla_put_be32(skb, CTA_IP_V4_SRC, tuple->src.u3.ip) ||
334 nla_put_be32(skb, CTA_IP_V4_DST, tuple->dst.u3.ip))
335 goto nla_put_failure;
c1d10adb
PNA
336 return 0;
337
df6fb868 338nla_put_failure:
c1d10adb
PNA
339 return -1;
340}
341
f73e924c
PM
342static const struct nla_policy ipv4_nla_policy[CTA_IP_MAX+1] = {
343 [CTA_IP_V4_SRC] = { .type = NLA_U32 },
344 [CTA_IP_V4_DST] = { .type = NLA_U32 },
c1d10adb
PNA
345};
346
fdf70832 347static int ipv4_nlattr_to_tuple(struct nlattr *tb[],
c1d10adb
PNA
348 struct nf_conntrack_tuple *t)
349{
df6fb868 350 if (!tb[CTA_IP_V4_SRC] || !tb[CTA_IP_V4_DST])
c1d10adb
PNA
351 return -EINVAL;
352
77236b6e
PM
353 t->src.u3.ip = nla_get_be32(tb[CTA_IP_V4_SRC]);
354 t->dst.u3.ip = nla_get_be32(tb[CTA_IP_V4_DST]);
c1d10adb
PNA
355
356 return 0;
357}
a400c30e
HE
358
359static int ipv4_nlattr_tuple_size(void)
360{
361 return nla_policy_len(ipv4_nla_policy, CTA_IP_MAX + 1);
362}
c1d10adb
PNA
363#endif
364
9fb9cbb1
YK
365static struct nf_sockopt_ops so_getorigdst = {
366 .pf = PF_INET,
367 .get_optmin = SO_ORIGINAL_DST,
368 .get_optmax = SO_ORIGINAL_DST+1,
369 .get = &getorigdst,
16fcec35 370 .owner = THIS_MODULE,
9fb9cbb1
YK
371};
372
3ea04dd3
G
373static int ipv4_init_net(struct net *net)
374{
375#if defined(CONFIG_SYSCTL) && defined(CONFIG_NF_CONNTRACK_PROC_COMPAT)
376 struct nf_ip_net *in = &net->ct.nf_ct_proto;
377 in->ctl_table = kmemdup(ip_ct_sysctl_table,
378 sizeof(ip_ct_sysctl_table),
379 GFP_KERNEL);
380 if (!in->ctl_table)
381 return -ENOMEM;
382
383 in->ctl_table[0].data = &nf_conntrack_max;
384 in->ctl_table[1].data = &net->ct.count;
385 in->ctl_table[2].data = &net->ct.htable_size;
386 in->ctl_table[3].data = &net->ct.sysctl_checksum;
387 in->ctl_table[4].data = &net->ct.sysctl_log_invalid;
388#endif
389 return 0;
390}
391
61075af5 392struct nf_conntrack_l3proto nf_conntrack_l3proto_ipv4 __read_mostly = {
9fb9cbb1
YK
393 .l3proto = PF_INET,
394 .name = "ipv4",
395 .pkt_to_tuple = ipv4_pkt_to_tuple,
396 .invert_tuple = ipv4_invert_tuple,
397 .print_tuple = ipv4_print_tuple,
ffc30690 398 .get_l4proto = ipv4_get_l4proto,
e281db5c 399#if defined(CONFIG_NF_CT_NETLINK) || defined(CONFIG_NF_CT_NETLINK_MODULE)
fdf70832 400 .tuple_to_nlattr = ipv4_tuple_to_nlattr,
a400c30e 401 .nlattr_tuple_size = ipv4_nlattr_tuple_size,
fdf70832 402 .nlattr_to_tuple = ipv4_nlattr_to_tuple,
f73e924c 403 .nla_policy = ipv4_nla_policy,
a999e683
PM
404#endif
405#if defined(CONFIG_SYSCTL) && defined(CONFIG_NF_CONNTRACK_PROC_COMPAT)
f99e8f71 406 .ctl_table_path = "net/ipv4/netfilter",
c1d10adb 407#endif
3ea04dd3 408 .init_net = ipv4_init_net,
9fb9cbb1
YK
409 .me = THIS_MODULE,
410};
411
fae718dd
PM
412module_param_call(hashsize, nf_conntrack_set_hashsize, param_get_uint,
413 &nf_conntrack_htable_size, 0600);
414
32292a7f 415MODULE_ALIAS("nf_conntrack-" __stringify(AF_INET));
d2483dde 416MODULE_ALIAS("ip_conntrack");
32292a7f
PM
417MODULE_LICENSE("GPL");
418
3ea04dd3
G
419static int ipv4_net_init(struct net *net)
420{
421 int ret = 0;
422
c296bb4d 423 ret = nf_ct_l4proto_pernet_register(net, &nf_conntrack_l4proto_tcp4);
3ea04dd3 424 if (ret < 0) {
c296bb4d 425 pr_err("nf_conntrack_tcp4: pernet registration failed\n");
3ea04dd3
G
426 goto out_tcp;
427 }
c296bb4d 428 ret = nf_ct_l4proto_pernet_register(net, &nf_conntrack_l4proto_udp4);
3ea04dd3 429 if (ret < 0) {
c296bb4d 430 pr_err("nf_conntrack_udp4: pernet registration failed\n");
3ea04dd3
G
431 goto out_udp;
432 }
c296bb4d 433 ret = nf_ct_l4proto_pernet_register(net, &nf_conntrack_l4proto_icmp);
3ea04dd3 434 if (ret < 0) {
c296bb4d 435 pr_err("nf_conntrack_icmp4: pernet registration failed\n");
3ea04dd3
G
436 goto out_icmp;
437 }
6330750d 438 ret = nf_ct_l3proto_pernet_register(net, &nf_conntrack_l3proto_ipv4);
3ea04dd3 439 if (ret < 0) {
6330750d 440 pr_err("nf_conntrack_ipv4: pernet registration failed\n");
3ea04dd3
G
441 goto out_ipv4;
442 }
443 return 0;
444out_ipv4:
c296bb4d 445 nf_ct_l4proto_pernet_unregister(net, &nf_conntrack_l4proto_icmp);
3ea04dd3 446out_icmp:
c296bb4d 447 nf_ct_l4proto_pernet_unregister(net, &nf_conntrack_l4proto_udp4);
3ea04dd3 448out_udp:
c296bb4d 449 nf_ct_l4proto_pernet_unregister(net, &nf_conntrack_l4proto_tcp4);
3ea04dd3
G
450out_tcp:
451 return ret;
452}
453
454static void ipv4_net_exit(struct net *net)
455{
6330750d 456 nf_ct_l3proto_pernet_unregister(net, &nf_conntrack_l3proto_ipv4);
c296bb4d
G
457 nf_ct_l4proto_pernet_unregister(net, &nf_conntrack_l4proto_icmp);
458 nf_ct_l4proto_pernet_unregister(net, &nf_conntrack_l4proto_udp4);
459 nf_ct_l4proto_pernet_unregister(net, &nf_conntrack_l4proto_tcp4);
3ea04dd3
G
460}
461
462static struct pernet_operations ipv4_net_ops = {
463 .init = ipv4_net_init,
464 .exit = ipv4_net_exit,
465};
466
32292a7f 467static int __init nf_conntrack_l3proto_ipv4_init(void)
9fb9cbb1
YK
468{
469 int ret = 0;
470
32292a7f 471 need_conntrack();
73e4022f 472 nf_defrag_ipv4_enable();
9fb9cbb1
YK
473
474 ret = nf_register_sockopt(&so_getorigdst);
475 if (ret < 0) {
476 printk(KERN_ERR "Unable to register netfilter socket option\n");
32292a7f 477 return ret;
9fb9cbb1
YK
478 }
479
3ea04dd3 480 ret = register_pernet_subsys(&ipv4_net_ops);
9fb9cbb1 481 if (ret < 0) {
3ea04dd3 482 pr_err("nf_conntrack_ipv4: can't register pernet ops\n");
9fb9cbb1
YK
483 goto cleanup_sockopt;
484 }
485
964ddaa1
PM
486 ret = nf_register_hooks(ipv4_conntrack_ops,
487 ARRAY_SIZE(ipv4_conntrack_ops));
9fb9cbb1 488 if (ret < 0) {
654d0fbd 489 pr_err("nf_conntrack_ipv4: can't register hooks.\n");
3ea04dd3 490 goto cleanup_pernet;
9fb9cbb1 491 }
6330750d 492
c296bb4d
G
493 ret = nf_ct_l4proto_register(&nf_conntrack_l4proto_tcp4);
494 if (ret < 0) {
495 pr_err("nf_conntrack_ipv4: can't register tcp4 proto.\n");
496 goto cleanup_hooks;
497 }
498
499 ret = nf_ct_l4proto_register(&nf_conntrack_l4proto_udp4);
500 if (ret < 0) {
501 pr_err("nf_conntrack_ipv4: can't register udp4 proto.\n");
502 goto cleanup_tcp4;
503 }
504
505 ret = nf_ct_l4proto_register(&nf_conntrack_l4proto_icmp);
506 if (ret < 0) {
507 pr_err("nf_conntrack_ipv4: can't register icmpv4 proto.\n");
508 goto cleanup_udp4;
509 }
510
6330750d
G
511 ret = nf_ct_l3proto_register(&nf_conntrack_l3proto_ipv4);
512 if (ret < 0) {
513 pr_err("nf_conntrack_ipv4: can't register ipv4 proto.\n");
c296bb4d 514 goto cleanup_icmpv4;
6330750d
G
515 }
516
e4bd8bce
PM
517#if defined(CONFIG_PROC_FS) && defined(CONFIG_NF_CONNTRACK_PROC_COMPAT)
518 ret = nf_conntrack_ipv4_compat_init();
519 if (ret < 0)
6330750d 520 goto cleanup_proto;
e4bd8bce 521#endif
9fb9cbb1 522 return ret;
e4bd8bce 523#if defined(CONFIG_PROC_FS) && defined(CONFIG_NF_CONNTRACK_PROC_COMPAT)
6330750d
G
524 cleanup_proto:
525 nf_ct_l3proto_unregister(&nf_conntrack_l3proto_ipv4);
526#endif
c296bb4d
G
527 cleanup_icmpv4:
528 nf_ct_l4proto_unregister(&nf_conntrack_l4proto_icmp);
529 cleanup_udp4:
530 nf_ct_l4proto_unregister(&nf_conntrack_l4proto_udp4);
531 cleanup_tcp4:
532 nf_ct_l4proto_unregister(&nf_conntrack_l4proto_tcp4);
e4bd8bce 533 cleanup_hooks:
e905a9ed 534 nf_unregister_hooks(ipv4_conntrack_ops, ARRAY_SIZE(ipv4_conntrack_ops));
3ea04dd3
G
535 cleanup_pernet:
536 unregister_pernet_subsys(&ipv4_net_ops);
9fb9cbb1
YK
537 cleanup_sockopt:
538 nf_unregister_sockopt(&so_getorigdst);
9fb9cbb1
YK
539 return ret;
540}
541
65b4b4e8 542static void __exit nf_conntrack_l3proto_ipv4_fini(void)
9fb9cbb1 543{
32292a7f 544 synchronize_net();
e4bd8bce
PM
545#if defined(CONFIG_PROC_FS) && defined(CONFIG_NF_CONNTRACK_PROC_COMPAT)
546 nf_conntrack_ipv4_compat_fini();
547#endif
6330750d 548 nf_ct_l3proto_unregister(&nf_conntrack_l3proto_ipv4);
c296bb4d
G
549 nf_ct_l4proto_unregister(&nf_conntrack_l4proto_icmp);
550 nf_ct_l4proto_unregister(&nf_conntrack_l4proto_udp4);
551 nf_ct_l4proto_unregister(&nf_conntrack_l4proto_tcp4);
32292a7f 552 nf_unregister_hooks(ipv4_conntrack_ops, ARRAY_SIZE(ipv4_conntrack_ops));
3ea04dd3 553 unregister_pernet_subsys(&ipv4_net_ops);
32292a7f 554 nf_unregister_sockopt(&so_getorigdst);
9fb9cbb1
YK
555}
556
65b4b4e8
AM
557module_init(nf_conntrack_l3proto_ipv4_init);
558module_exit(nf_conntrack_l3proto_ipv4_fini);
591e6206
PM
559
560void need_ipv4_conntrack(void)
561{
562 return;
563}
564EXPORT_SYMBOL_GPL(need_ipv4_conntrack);