Commit | Line | Data |
---|---|---|
02c7b25e PNA |
1 | #include <linux/init.h> |
2 | #include <linux/kernel.h> | |
3 | #include <linux/netdevice.h> | |
4 | #include <net/net_namespace.h> | |
5 | #include <net/netfilter/nf_tables.h> | |
6 | #include <linux/netfilter_ipv4.h> | |
7 | #include <linux/netfilter_ipv6.h> | |
8 | #include <linux/netfilter_bridge.h> | |
9 | #include <linux/netfilter_arp.h> | |
10 | #include <net/netfilter/nf_tables_ipv4.h> | |
11 | #include <net/netfilter/nf_tables_ipv6.h> | |
12 | ||
13 | #ifdef CONFIG_NF_TABLES_IPV4 | |
14 | static unsigned int nft_do_chain_ipv4(void *priv, | |
15 | struct sk_buff *skb, | |
16 | const struct nf_hook_state *state) | |
17 | { | |
18 | struct nft_pktinfo pkt; | |
19 | ||
20 | nft_set_pktinfo(&pkt, skb, state); | |
21 | nft_set_pktinfo_ipv4(&pkt, skb); | |
22 | ||
23 | return nft_do_chain(&pkt, priv); | |
24 | } | |
25 | ||
26 | static const struct nft_chain_type nft_chain_filter_ipv4 = { | |
27 | .name = "filter", | |
28 | .type = NFT_CHAIN_T_DEFAULT, | |
29 | .family = NFPROTO_IPV4, | |
30 | .hook_mask = (1 << NF_INET_LOCAL_IN) | | |
31 | (1 << NF_INET_LOCAL_OUT) | | |
32 | (1 << NF_INET_FORWARD) | | |
33 | (1 << NF_INET_PRE_ROUTING) | | |
34 | (1 << NF_INET_POST_ROUTING), | |
35 | .hooks = { | |
36 | [NF_INET_LOCAL_IN] = nft_do_chain_ipv4, | |
37 | [NF_INET_LOCAL_OUT] = nft_do_chain_ipv4, | |
38 | [NF_INET_FORWARD] = nft_do_chain_ipv4, | |
39 | [NF_INET_PRE_ROUTING] = nft_do_chain_ipv4, | |
40 | [NF_INET_POST_ROUTING] = nft_do_chain_ipv4, | |
41 | }, | |
42 | }; | |
43 | ||
44 | static void nft_chain_filter_ipv4_init(void) | |
45 | { | |
46 | nft_register_chain_type(&nft_chain_filter_ipv4); | |
47 | } | |
48 | static void nft_chain_filter_ipv4_fini(void) | |
49 | { | |
50 | nft_unregister_chain_type(&nft_chain_filter_ipv4); | |
51 | } | |
52 | ||
53 | #else | |
54 | static inline void nft_chain_filter_ipv4_init(void) {} | |
55 | static inline void nft_chain_filter_ipv4_fini(void) {} | |
56 | #endif /* CONFIG_NF_TABLES_IPV4 */ | |
57 | ||
58 | #ifdef CONFIG_NF_TABLES_ARP | |
59 | static unsigned int nft_do_chain_arp(void *priv, struct sk_buff *skb, | |
60 | const struct nf_hook_state *state) | |
61 | { | |
62 | struct nft_pktinfo pkt; | |
63 | ||
64 | nft_set_pktinfo(&pkt, skb, state); | |
65 | nft_set_pktinfo_unspec(&pkt, skb); | |
66 | ||
67 | return nft_do_chain(&pkt, priv); | |
68 | } | |
69 | ||
70 | static const struct nft_chain_type nft_chain_filter_arp = { | |
71 | .name = "filter", | |
72 | .type = NFT_CHAIN_T_DEFAULT, | |
73 | .family = NFPROTO_ARP, | |
74 | .owner = THIS_MODULE, | |
75 | .hook_mask = (1 << NF_ARP_IN) | | |
76 | (1 << NF_ARP_OUT), | |
77 | .hooks = { | |
78 | [NF_ARP_IN] = nft_do_chain_arp, | |
79 | [NF_ARP_OUT] = nft_do_chain_arp, | |
80 | }, | |
81 | }; | |
82 | ||
83 | static void nft_chain_filter_arp_init(void) | |
84 | { | |
85 | nft_register_chain_type(&nft_chain_filter_arp); | |
86 | } | |
87 | ||
88 | static void nft_chain_filter_arp_fini(void) | |
89 | { | |
90 | nft_unregister_chain_type(&nft_chain_filter_arp); | |
91 | } | |
92 | #else | |
93 | static inline void nft_chain_filter_arp_init(void) {} | |
94 | static inline void nft_chain_filter_arp_fini(void) {} | |
95 | #endif /* CONFIG_NF_TABLES_ARP */ | |
96 | ||
97 | #ifdef CONFIG_NF_TABLES_IPV6 | |
98 | static unsigned int nft_do_chain_ipv6(void *priv, | |
99 | struct sk_buff *skb, | |
100 | const struct nf_hook_state *state) | |
101 | { | |
102 | struct nft_pktinfo pkt; | |
103 | ||
104 | nft_set_pktinfo(&pkt, skb, state); | |
105 | nft_set_pktinfo_ipv6(&pkt, skb); | |
106 | ||
107 | return nft_do_chain(&pkt, priv); | |
108 | } | |
109 | ||
110 | static const struct nft_chain_type nft_chain_filter_ipv6 = { | |
111 | .name = "filter", | |
112 | .type = NFT_CHAIN_T_DEFAULT, | |
113 | .family = NFPROTO_IPV6, | |
114 | .hook_mask = (1 << NF_INET_LOCAL_IN) | | |
115 | (1 << NF_INET_LOCAL_OUT) | | |
116 | (1 << NF_INET_FORWARD) | | |
117 | (1 << NF_INET_PRE_ROUTING) | | |
118 | (1 << NF_INET_POST_ROUTING), | |
119 | .hooks = { | |
120 | [NF_INET_LOCAL_IN] = nft_do_chain_ipv6, | |
121 | [NF_INET_LOCAL_OUT] = nft_do_chain_ipv6, | |
122 | [NF_INET_FORWARD] = nft_do_chain_ipv6, | |
123 | [NF_INET_PRE_ROUTING] = nft_do_chain_ipv6, | |
124 | [NF_INET_POST_ROUTING] = nft_do_chain_ipv6, | |
125 | }, | |
126 | }; | |
127 | ||
128 | static void nft_chain_filter_ipv6_init(void) | |
129 | { | |
130 | nft_register_chain_type(&nft_chain_filter_ipv6); | |
131 | } | |
132 | ||
133 | static void nft_chain_filter_ipv6_fini(void) | |
134 | { | |
135 | nft_unregister_chain_type(&nft_chain_filter_ipv6); | |
136 | } | |
137 | #else | |
138 | static inline void nft_chain_filter_ipv6_init(void) {} | |
139 | static inline void nft_chain_filter_ipv6_fini(void) {} | |
140 | #endif /* CONFIG_NF_TABLES_IPV6 */ | |
141 | ||
142 | #ifdef CONFIG_NF_TABLES_INET | |
143 | static unsigned int nft_do_chain_inet(void *priv, struct sk_buff *skb, | |
144 | const struct nf_hook_state *state) | |
145 | { | |
146 | struct nft_pktinfo pkt; | |
147 | ||
148 | nft_set_pktinfo(&pkt, skb, state); | |
149 | ||
150 | switch (state->pf) { | |
151 | case NFPROTO_IPV4: | |
152 | nft_set_pktinfo_ipv4(&pkt, skb); | |
153 | break; | |
154 | case NFPROTO_IPV6: | |
155 | nft_set_pktinfo_ipv6(&pkt, skb); | |
156 | break; | |
157 | default: | |
158 | break; | |
159 | } | |
160 | ||
161 | return nft_do_chain(&pkt, priv); | |
162 | } | |
163 | ||
164 | static const struct nft_chain_type nft_chain_filter_inet = { | |
165 | .name = "filter", | |
166 | .type = NFT_CHAIN_T_DEFAULT, | |
167 | .family = NFPROTO_INET, | |
168 | .hook_mask = (1 << NF_INET_LOCAL_IN) | | |
169 | (1 << NF_INET_LOCAL_OUT) | | |
170 | (1 << NF_INET_FORWARD) | | |
171 | (1 << NF_INET_PRE_ROUTING) | | |
172 | (1 << NF_INET_POST_ROUTING), | |
173 | .hooks = { | |
174 | [NF_INET_LOCAL_IN] = nft_do_chain_inet, | |
175 | [NF_INET_LOCAL_OUT] = nft_do_chain_inet, | |
176 | [NF_INET_FORWARD] = nft_do_chain_inet, | |
177 | [NF_INET_PRE_ROUTING] = nft_do_chain_inet, | |
178 | [NF_INET_POST_ROUTING] = nft_do_chain_inet, | |
179 | }, | |
180 | }; | |
181 | ||
182 | static void nft_chain_filter_inet_init(void) | |
183 | { | |
184 | nft_register_chain_type(&nft_chain_filter_inet); | |
185 | } | |
186 | ||
187 | static void nft_chain_filter_inet_fini(void) | |
188 | { | |
189 | nft_unregister_chain_type(&nft_chain_filter_inet); | |
190 | } | |
191 | #else | |
192 | static inline void nft_chain_filter_inet_init(void) {} | |
193 | static inline void nft_chain_filter_inet_fini(void) {} | |
194 | #endif /* CONFIG_NF_TABLES_IPV6 */ | |
195 | ||
dfee0e99 | 196 | #if IS_ENABLED(CONFIG_NF_TABLES_BRIDGE) |
02c7b25e PNA |
197 | static unsigned int |
198 | nft_do_chain_bridge(void *priv, | |
199 | struct sk_buff *skb, | |
200 | const struct nf_hook_state *state) | |
201 | { | |
202 | struct nft_pktinfo pkt; | |
203 | ||
204 | nft_set_pktinfo(&pkt, skb, state); | |
205 | ||
206 | switch (eth_hdr(skb)->h_proto) { | |
207 | case htons(ETH_P_IP): | |
208 | nft_set_pktinfo_ipv4_validate(&pkt, skb); | |
209 | break; | |
210 | case htons(ETH_P_IPV6): | |
211 | nft_set_pktinfo_ipv6_validate(&pkt, skb); | |
212 | break; | |
213 | default: | |
214 | nft_set_pktinfo_unspec(&pkt, skb); | |
215 | break; | |
216 | } | |
217 | ||
218 | return nft_do_chain(&pkt, priv); | |
219 | } | |
220 | ||
221 | static const struct nft_chain_type nft_chain_filter_bridge = { | |
222 | .name = "filter", | |
223 | .type = NFT_CHAIN_T_DEFAULT, | |
224 | .family = NFPROTO_BRIDGE, | |
225 | .hook_mask = (1 << NF_BR_PRE_ROUTING) | | |
226 | (1 << NF_BR_LOCAL_IN) | | |
227 | (1 << NF_BR_FORWARD) | | |
228 | (1 << NF_BR_LOCAL_OUT) | | |
229 | (1 << NF_BR_POST_ROUTING), | |
230 | .hooks = { | |
231 | [NF_BR_PRE_ROUTING] = nft_do_chain_bridge, | |
232 | [NF_BR_LOCAL_IN] = nft_do_chain_bridge, | |
233 | [NF_BR_FORWARD] = nft_do_chain_bridge, | |
234 | [NF_BR_LOCAL_OUT] = nft_do_chain_bridge, | |
235 | [NF_BR_POST_ROUTING] = nft_do_chain_bridge, | |
236 | }, | |
237 | }; | |
238 | ||
239 | static void nft_chain_filter_bridge_init(void) | |
240 | { | |
241 | nft_register_chain_type(&nft_chain_filter_bridge); | |
242 | } | |
243 | ||
244 | static void nft_chain_filter_bridge_fini(void) | |
245 | { | |
246 | nft_unregister_chain_type(&nft_chain_filter_bridge); | |
247 | } | |
248 | #else | |
249 | static inline void nft_chain_filter_bridge_init(void) {} | |
250 | static inline void nft_chain_filter_bridge_fini(void) {} | |
251 | #endif /* CONFIG_NF_TABLES_BRIDGE */ | |
252 | ||
253 | #ifdef CONFIG_NF_TABLES_NETDEV | |
254 | static unsigned int nft_do_chain_netdev(void *priv, struct sk_buff *skb, | |
255 | const struct nf_hook_state *state) | |
256 | { | |
257 | struct nft_pktinfo pkt; | |
258 | ||
259 | nft_set_pktinfo(&pkt, skb, state); | |
260 | ||
261 | switch (skb->protocol) { | |
262 | case htons(ETH_P_IP): | |
263 | nft_set_pktinfo_ipv4_validate(&pkt, skb); | |
264 | break; | |
265 | case htons(ETH_P_IPV6): | |
266 | nft_set_pktinfo_ipv6_validate(&pkt, skb); | |
267 | break; | |
268 | default: | |
269 | nft_set_pktinfo_unspec(&pkt, skb); | |
270 | break; | |
271 | } | |
272 | ||
273 | return nft_do_chain(&pkt, priv); | |
274 | } | |
275 | ||
276 | static const struct nft_chain_type nft_chain_filter_netdev = { | |
277 | .name = "filter", | |
278 | .type = NFT_CHAIN_T_DEFAULT, | |
279 | .family = NFPROTO_NETDEV, | |
280 | .hook_mask = (1 << NF_NETDEV_INGRESS), | |
281 | .hooks = { | |
282 | [NF_NETDEV_INGRESS] = nft_do_chain_netdev, | |
283 | }, | |
284 | }; | |
285 | ||
286 | static void nft_netdev_event(unsigned long event, struct net_device *dev, | |
287 | struct nft_ctx *ctx) | |
288 | { | |
289 | struct nft_base_chain *basechain = nft_base_chain(ctx->chain); | |
290 | ||
291 | switch (event) { | |
292 | case NETDEV_UNREGISTER: | |
293 | if (strcmp(basechain->dev_name, dev->name) != 0) | |
294 | return; | |
295 | ||
6a48de01 FW |
296 | /* UNREGISTER events are also happpening on netns exit. |
297 | * | |
298 | * Altough nf_tables core releases all tables/chains, only | |
299 | * this event handler provides guarantee that | |
300 | * basechain.ops->dev is still accessible, so we cannot | |
301 | * skip exiting net namespaces. | |
302 | */ | |
02c7b25e PNA |
303 | __nft_release_basechain(ctx); |
304 | break; | |
305 | case NETDEV_CHANGENAME: | |
306 | if (dev->ifindex != basechain->ops.dev->ifindex) | |
307 | return; | |
308 | ||
309 | strncpy(basechain->dev_name, dev->name, IFNAMSIZ); | |
310 | break; | |
311 | } | |
312 | } | |
313 | ||
314 | static int nf_tables_netdev_event(struct notifier_block *this, | |
315 | unsigned long event, void *ptr) | |
316 | { | |
317 | struct net_device *dev = netdev_notifier_info_to_dev(ptr); | |
318 | struct nft_table *table; | |
319 | struct nft_chain *chain, *nr; | |
320 | struct nft_ctx ctx = { | |
321 | .net = dev_net(dev), | |
322 | }; | |
323 | ||
324 | if (event != NETDEV_UNREGISTER && | |
325 | event != NETDEV_CHANGENAME) | |
326 | return NOTIFY_DONE; | |
327 | ||
f102d66b | 328 | mutex_lock(&ctx.net->nft.commit_mutex); |
02c7b25e PNA |
329 | list_for_each_entry(table, &ctx.net->nft.tables, list) { |
330 | if (table->family != NFPROTO_NETDEV) | |
331 | continue; | |
332 | ||
333 | ctx.family = table->family; | |
334 | ctx.table = table; | |
335 | list_for_each_entry_safe(chain, nr, &table->chains, list) { | |
336 | if (!nft_is_base_chain(chain)) | |
337 | continue; | |
338 | ||
339 | ctx.chain = chain; | |
340 | nft_netdev_event(event, dev, &ctx); | |
341 | } | |
342 | } | |
f102d66b | 343 | mutex_unlock(&ctx.net->nft.commit_mutex); |
02c7b25e PNA |
344 | |
345 | return NOTIFY_DONE; | |
346 | } | |
347 | ||
348 | static struct notifier_block nf_tables_netdev_notifier = { | |
349 | .notifier_call = nf_tables_netdev_event, | |
350 | }; | |
351 | ||
352 | static int nft_chain_filter_netdev_init(void) | |
353 | { | |
354 | int err; | |
355 | ||
356 | nft_register_chain_type(&nft_chain_filter_netdev); | |
357 | ||
358 | err = register_netdevice_notifier(&nf_tables_netdev_notifier); | |
359 | if (err) | |
360 | goto err_register_netdevice_notifier; | |
361 | ||
362 | return 0; | |
363 | ||
364 | err_register_netdevice_notifier: | |
365 | nft_unregister_chain_type(&nft_chain_filter_netdev); | |
366 | ||
367 | return err; | |
368 | } | |
369 | ||
370 | static void nft_chain_filter_netdev_fini(void) | |
371 | { | |
372 | nft_unregister_chain_type(&nft_chain_filter_netdev); | |
373 | unregister_netdevice_notifier(&nf_tables_netdev_notifier); | |
374 | } | |
375 | #else | |
376 | static inline int nft_chain_filter_netdev_init(void) { return 0; } | |
377 | static inline void nft_chain_filter_netdev_fini(void) {} | |
378 | #endif /* CONFIG_NF_TABLES_NETDEV */ | |
379 | ||
380 | int __init nft_chain_filter_init(void) | |
381 | { | |
382 | int err; | |
383 | ||
384 | err = nft_chain_filter_netdev_init(); | |
385 | if (err < 0) | |
386 | return err; | |
387 | ||
388 | nft_chain_filter_ipv4_init(); | |
389 | nft_chain_filter_ipv6_init(); | |
390 | nft_chain_filter_arp_init(); | |
391 | nft_chain_filter_inet_init(); | |
392 | nft_chain_filter_bridge_init(); | |
393 | ||
394 | return 0; | |
395 | } | |
396 | ||
d209df3e | 397 | void nft_chain_filter_fini(void) |
02c7b25e PNA |
398 | { |
399 | nft_chain_filter_bridge_fini(); | |
400 | nft_chain_filter_inet_fini(); | |
401 | nft_chain_filter_arp_fini(); | |
402 | nft_chain_filter_ipv6_fini(); | |
403 | nft_chain_filter_ipv4_fini(); | |
404 | nft_chain_filter_netdev_fini(); | |
405 | } |