1 | // SPDX-License-Identifier: GPL-2.0-only |
2 | /* (C) 1999-2001 Paul `Rusty' Russell |
3 | * (C) 2002-2004 Netfilter Core Team <coreteam@netfilter.org> |
4 | */ |
5 | |
6 | #include <linux/types.h> |
7 | #include <linux/ip.h> |
8 | #include <linux/netfilter.h> |
9 | #include <linux/module.h> |
10 | #include <linux/rcupdate.h> |
11 | #include <linux/skbuff.h> |
12 | #include <net/netns/generic.h> |
13 | #include <net/route.h> |
14 | #include <net/ip.h> |
15 | |
16 | #include <linux/netfilter_bridge.h> |
17 | #include <linux/netfilter_ipv4.h> |
18 | #include <net/netfilter/ipv4/nf_defrag_ipv4.h> |
19 | #if IS_ENABLED(CONFIG_NF_CONNTRACK) |
20 | #include <net/netfilter/nf_conntrack.h> |
21 | #endif |
22 | #include <net/netfilter/nf_conntrack_zones.h> |
23 | |
24 | static DEFINE_MUTEX(defrag4_mutex); |
25 | |
26 | static int nf_ct_ipv4_gather_frags(struct net *net, struct sk_buff *skb, |
27 | u_int32_t user) |
28 | { |
29 | int err; |
30 | |
31 | local_bh_disable(); |
32 | err = ip_defrag(net, skb, user); |
33 | local_bh_enable(); |
34 | |
35 | if (!err) |
36 | skb->ignore_df = 1; |
37 | |
38 | return err; |
39 | } |
40 | |
41 | static enum ip_defrag_users nf_ct_defrag_user(unsigned int hooknum, |
42 | struct sk_buff *skb) |
43 | { |
44 | u16 zone_id = NF_CT_DEFAULT_ZONE_ID; |
45 | #if IS_ENABLED(CONFIG_NF_CONNTRACK) |
46 | if (skb_nfct(skb)) { |
47 | enum ip_conntrack_info ctinfo; |
48 | const struct nf_conn *ct = nf_ct_get(skb, ctinfo: &ctinfo); |
49 | |
50 | zone_id = nf_ct_zone_id(zone: nf_ct_zone(ct), CTINFO2DIR(ctinfo)); |
51 | } |
52 | #endif |
53 | if (nf_bridge_in_prerouting(skb)) |
54 | return IP_DEFRAG_CONNTRACK_BRIDGE_IN + zone_id; |
55 | |
56 | if (hooknum == NF_INET_PRE_ROUTING) |
57 | return IP_DEFRAG_CONNTRACK_IN + zone_id; |
58 | else |
59 | return IP_DEFRAG_CONNTRACK_OUT + zone_id; |
60 | } |
61 | |
62 | static unsigned int ipv4_conntrack_defrag(void *priv, |
63 | struct sk_buff *skb, |
64 | const struct nf_hook_state *state) |
65 | { |
66 | struct sock *sk = skb->sk; |
67 | |
68 | if (sk && sk_fullsock(sk) && (sk->sk_family == PF_INET) && |
69 | inet_test_bit(NODEFRAG, sk)) |
70 | return NF_ACCEPT; |
71 | |
72 | #if IS_ENABLED(CONFIG_NF_CONNTRACK) |
73 | #if !IS_ENABLED(CONFIG_NF_NAT) |
74 | /* Previously seen (loopback)? Ignore. Do this before |
75 | fragment check. */ |
76 | if (skb_nfct(skb) && !nf_ct_is_template((struct nf_conn *)skb_nfct(skb))) |
77 | return NF_ACCEPT; |
78 | #endif |
79 | if (skb->_nfct == IP_CT_UNTRACKED) |
80 | return NF_ACCEPT; |
81 | #endif |
82 | /* Gather fragments. */ |
83 | if (ip_is_fragment(iph: ip_hdr(skb))) { |
84 | enum ip_defrag_users user = |
85 | nf_ct_defrag_user(hooknum: state->hook, skb); |
86 | |
87 | if (nf_ct_ipv4_gather_frags(net: state->net, skb, user)) |
88 | return NF_STOLEN; |
89 | } |
90 | return NF_ACCEPT; |
91 | } |
92 | |
93 | static const struct nf_hook_ops ipv4_defrag_ops[] = { |
94 | { |
95 | .hook = ipv4_conntrack_defrag, |
96 | .pf = NFPROTO_IPV4, |
97 | .hooknum = NF_INET_PRE_ROUTING, |
98 | .priority = NF_IP_PRI_CONNTRACK_DEFRAG, |
99 | }, |
100 | { |
101 | .hook = ipv4_conntrack_defrag, |
102 | .pf = NFPROTO_IPV4, |
103 | .hooknum = NF_INET_LOCAL_OUT, |
104 | .priority = NF_IP_PRI_CONNTRACK_DEFRAG, |
105 | }, |
106 | }; |
107 | |
108 | static void __net_exit defrag4_net_exit(struct net *net) |
109 | { |
110 | if (net->nf.defrag_ipv4_users) { |
111 | nf_unregister_net_hooks(net, reg: ipv4_defrag_ops, |
112 | ARRAY_SIZE(ipv4_defrag_ops)); |
113 | net->nf.defrag_ipv4_users = 0; |
114 | } |
115 | } |
116 | |
117 | static const struct nf_defrag_hook defrag_hook = { |
118 | .owner = THIS_MODULE, |
119 | .enable = nf_defrag_ipv4_enable, |
120 | .disable = nf_defrag_ipv4_disable, |
121 | }; |
122 | |
123 | static struct pernet_operations defrag4_net_ops = { |
124 | .exit = defrag4_net_exit, |
125 | }; |
126 | |
127 | static int __init nf_defrag_init(void) |
128 | { |
129 | int err; |
130 | |
131 | err = register_pernet_subsys(&defrag4_net_ops); |
132 | if (err) |
133 | return err; |
134 | |
135 | rcu_assign_pointer(nf_defrag_v4_hook, &defrag_hook); |
136 | return err; |
137 | } |
138 | |
139 | static void __exit nf_defrag_fini(void) |
140 | { |
141 | rcu_assign_pointer(nf_defrag_v4_hook, NULL); |
142 | unregister_pernet_subsys(&defrag4_net_ops); |
143 | } |
144 | |
145 | int nf_defrag_ipv4_enable(struct net *net) |
146 | { |
147 | int err = 0; |
148 | |
149 | mutex_lock(&defrag4_mutex); |
150 | if (net->nf.defrag_ipv4_users == UINT_MAX) { |
151 | err = -EOVERFLOW; |
152 | goto out_unlock; |
153 | } |
154 | |
155 | if (net->nf.defrag_ipv4_users) { |
156 | net->nf.defrag_ipv4_users++; |
157 | goto out_unlock; |
158 | } |
159 | |
160 | err = nf_register_net_hooks(net, reg: ipv4_defrag_ops, |
161 | ARRAY_SIZE(ipv4_defrag_ops)); |
162 | if (err == 0) |
163 | net->nf.defrag_ipv4_users = 1; |
164 | |
165 | out_unlock: |
166 | mutex_unlock(lock: &defrag4_mutex); |
167 | return err; |
168 | } |
169 | EXPORT_SYMBOL_GPL(nf_defrag_ipv4_enable); |
170 | |
171 | void nf_defrag_ipv4_disable(struct net *net) |
172 | { |
173 | mutex_lock(&defrag4_mutex); |
174 | if (net->nf.defrag_ipv4_users) { |
175 | net->nf.defrag_ipv4_users--; |
176 | if (net->nf.defrag_ipv4_users == 0) |
177 | nf_unregister_net_hooks(net, reg: ipv4_defrag_ops, |
178 | ARRAY_SIZE(ipv4_defrag_ops)); |
179 | } |
180 | |
181 | mutex_unlock(lock: &defrag4_mutex); |
182 | } |
183 | EXPORT_SYMBOL_GPL(nf_defrag_ipv4_disable); |
184 | |
185 | module_init(nf_defrag_init); |
186 | module_exit(nf_defrag_fini); |
187 | |
188 | MODULE_LICENSE("GPL" ); |
189 | |