]>
Commit | Line | Data |
---|---|---|
ed6c4136 PNA |
1 | /* |
2 | * Copyright (c) 2015 Pablo Neira Ayuso <pablo@netfilter.org> | |
3 | * | |
4 | * This program is free software; you can redistribute it and/or modify | |
5 | * it under the terms of the GNU General Public License version 2 as | |
6 | * published by the Free Software Foundation. | |
7 | */ | |
8 | ||
9 | #include <linux/init.h> | |
10 | #include <linux/module.h> | |
835b8033 | 11 | #include <linux/netdevice.h> |
ed6c4136 PNA |
12 | #include <net/netfilter/nf_tables.h> |
13 | #include <linux/ip.h> | |
14 | #include <linux/ipv6.h> | |
15 | #include <net/netfilter/nf_tables_ipv4.h> | |
16 | #include <net/netfilter/nf_tables_ipv6.h> | |
17 | ||
ed6c4136 | 18 | static unsigned int |
06198b34 | 19 | nft_do_chain_netdev(void *priv, struct sk_buff *skb, |
ed6c4136 PNA |
20 | const struct nf_hook_state *state) |
21 | { | |
22 | struct nft_pktinfo pkt; | |
23 | ||
aa47e42c | 24 | switch (skb->protocol) { |
ed6c4136 | 25 | case htons(ETH_P_IP): |
ddc8b602 | 26 | nft_set_pktinfo_ipv4_validate(&pkt, skb, state); |
ed6c4136 PNA |
27 | break; |
28 | case htons(ETH_P_IPV6): | |
ddc8b602 | 29 | nft_set_pktinfo_ipv6_validate(&pkt, skb, state); |
ed6c4136 PNA |
30 | break; |
31 | default: | |
beac5afa | 32 | nft_set_pktinfo_unspec(&pkt, skb, state); |
ed6c4136 PNA |
33 | break; |
34 | } | |
35 | ||
06198b34 | 36 | return nft_do_chain(&pkt, priv); |
ed6c4136 PNA |
37 | } |
38 | ||
39 | static struct nft_af_info nft_af_netdev __read_mostly = { | |
40 | .family = NFPROTO_NETDEV, | |
41 | .nhooks = NF_NETDEV_NUMHOOKS, | |
42 | .owner = THIS_MODULE, | |
43 | .flags = NFT_AF_NEEDS_DEV, | |
44 | .nops = 1, | |
45 | .hooks = { | |
46 | [NF_NETDEV_INGRESS] = nft_do_chain_netdev, | |
47 | }, | |
48 | }; | |
49 | ||
50 | static int nf_tables_netdev_init_net(struct net *net) | |
51 | { | |
52 | net->nft.netdev = kmalloc(sizeof(struct nft_af_info), GFP_KERNEL); | |
53 | if (net->nft.netdev == NULL) | |
54 | return -ENOMEM; | |
55 | ||
56 | memcpy(net->nft.netdev, &nft_af_netdev, sizeof(nft_af_netdev)); | |
57 | ||
58 | if (nft_register_afinfo(net, net->nft.netdev) < 0) | |
59 | goto err; | |
60 | ||
61 | return 0; | |
62 | err: | |
63 | kfree(net->nft.netdev); | |
64 | return -ENOMEM; | |
65 | } | |
66 | ||
67 | static void nf_tables_netdev_exit_net(struct net *net) | |
68 | { | |
df05ef87 | 69 | nft_unregister_afinfo(net, net->nft.netdev); |
ed6c4136 PNA |
70 | kfree(net->nft.netdev); |
71 | } | |
72 | ||
73 | static struct pernet_operations nf_tables_netdev_net_ops = { | |
74 | .init = nf_tables_netdev_init_net, | |
75 | .exit = nf_tables_netdev_exit_net, | |
76 | }; | |
77 | ||
78 | static const struct nf_chain_type nft_filter_chain_netdev = { | |
79 | .name = "filter", | |
80 | .type = NFT_CHAIN_T_DEFAULT, | |
81 | .family = NFPROTO_NETDEV, | |
82 | .owner = THIS_MODULE, | |
83 | .hook_mask = (1 << NF_NETDEV_INGRESS), | |
84 | }; | |
85 | ||
5ebe0b0e PNA |
86 | static void nft_netdev_event(unsigned long event, struct net_device *dev, |
87 | struct nft_ctx *ctx) | |
835b8033 | 88 | { |
5ebe0b0e | 89 | struct nft_base_chain *basechain = nft_base_chain(ctx->chain); |
835b8033 | 90 | |
5ebe0b0e | 91 | switch (event) { |
835b8033 PNA |
92 | case NETDEV_UNREGISTER: |
93 | if (strcmp(basechain->dev_name, dev->name) != 0) | |
94 | return; | |
95 | ||
5ebe0b0e | 96 | __nft_release_basechain(ctx); |
835b8033 PNA |
97 | break; |
98 | case NETDEV_CHANGENAME: | |
99 | if (dev->ifindex != basechain->ops[0].dev->ifindex) | |
100 | return; | |
101 | ||
102 | strncpy(basechain->dev_name, dev->name, IFNAMSIZ); | |
103 | break; | |
104 | } | |
105 | } | |
106 | ||
107 | static int nf_tables_netdev_event(struct notifier_block *this, | |
108 | unsigned long event, void *ptr) | |
109 | { | |
110 | struct net_device *dev = netdev_notifier_info_to_dev(ptr); | |
111 | struct nft_af_info *afi; | |
112 | struct nft_table *table; | |
5ebe0b0e PNA |
113 | struct nft_chain *chain, *nr; |
114 | struct nft_ctx ctx = { | |
115 | .net = dev_net(dev), | |
116 | }; | |
117 | ||
118 | if (event != NETDEV_UNREGISTER && | |
119 | event != NETDEV_CHANGENAME) | |
120 | return NOTIFY_DONE; | |
835b8033 PNA |
121 | |
122 | nfnl_lock(NFNL_SUBSYS_NFTABLES); | |
123 | list_for_each_entry(afi, &dev_net(dev)->nft.af_info, list) { | |
5ebe0b0e | 124 | ctx.afi = afi; |
835b8033 PNA |
125 | if (afi->family != NFPROTO_NETDEV) |
126 | continue; | |
127 | ||
128 | list_for_each_entry(table, &afi->tables, list) { | |
5ebe0b0e PNA |
129 | ctx.table = table; |
130 | list_for_each_entry_safe(chain, nr, &table->chains, list) { | |
f323d954 | 131 | if (!nft_is_base_chain(chain)) |
835b8033 PNA |
132 | continue; |
133 | ||
5ebe0b0e PNA |
134 | ctx.chain = chain; |
135 | nft_netdev_event(event, dev, &ctx); | |
835b8033 PNA |
136 | } |
137 | } | |
138 | } | |
139 | nfnl_unlock(NFNL_SUBSYS_NFTABLES); | |
140 | ||
141 | return NOTIFY_DONE; | |
142 | } | |
143 | ||
144 | static struct notifier_block nf_tables_netdev_notifier = { | |
145 | .notifier_call = nf_tables_netdev_event, | |
146 | }; | |
147 | ||
ed6c4136 PNA |
148 | static int __init nf_tables_netdev_init(void) |
149 | { | |
150 | int ret; | |
151 | ||
23d07508 GF |
152 | ret = nft_register_chain_type(&nft_filter_chain_netdev); |
153 | if (ret) | |
154 | return ret; | |
155 | ||
ed6c4136 | 156 | ret = register_pernet_subsys(&nf_tables_netdev_net_ops); |
4e6577de GF |
157 | if (ret) |
158 | goto err1; | |
159 | ||
160 | ret = register_netdevice_notifier(&nf_tables_netdev_notifier); | |
161 | if (ret) | |
162 | goto err2; | |
163 | ||
35b81539 | 164 | return 0; |
4e6577de GF |
165 | |
166 | err2: | |
167 | unregister_pernet_subsys(&nf_tables_netdev_net_ops); | |
168 | err1: | |
169 | nft_unregister_chain_type(&nft_filter_chain_netdev); | |
170 | return ret; | |
ed6c4136 PNA |
171 | } |
172 | ||
173 | static void __exit nf_tables_netdev_exit(void) | |
174 | { | |
835b8033 | 175 | unregister_netdevice_notifier(&nf_tables_netdev_notifier); |
ed6c4136 PNA |
176 | unregister_pernet_subsys(&nf_tables_netdev_net_ops); |
177 | nft_unregister_chain_type(&nft_filter_chain_netdev); | |
178 | } | |
179 | ||
180 | module_init(nf_tables_netdev_init); | |
181 | module_exit(nf_tables_netdev_exit); | |
182 | ||
183 | MODULE_LICENSE("GPL"); | |
184 | MODULE_AUTHOR("Pablo Neira Ayuso <pablo@netfilter.org>"); | |
185 | MODULE_ALIAS_NFT_FAMILY(5); /* NFPROTO_NETDEV */ |