diff options
author | Pablo Neira Ayuso <pablo@netfilter.org> | 2015-05-26 12:41:40 -0400 |
---|---|---|
committer | Pablo Neira Ayuso <pablo@netfilter.org> | 2015-05-26 12:41:23 -0400 |
commit | ed6c4136f1571bd6ab362afc3410905a8a69ca42 (patch) | |
tree | 4b8e910c631dce906891caf07a6ab88ec7e12c4f | |
parent | ebddf1a8d78aa3436353fae75c4396e50cb2d6cf (diff) |
netfilter: nf_tables: add netdev table to filter from ingress
This allows us to create netdev tables that contain ingress chains. Use
skb_header_pointer() as we may see shared sk_buffs at this stage.
This change provides access to the existing nf_tables features from the ingress
hook.
Signed-off-by: Pablo Neira Ayuso <pablo@netfilter.org>
-rw-r--r-- | include/net/netns/nftables.h | 1 | ||||
-rw-r--r-- | net/netfilter/Kconfig | 5 | ||||
-rw-r--r-- | net/netfilter/Makefile | 1 | ||||
-rw-r--r-- | net/netfilter/nf_tables_netdev.c | 183 |
4 files changed, 190 insertions, 0 deletions
diff --git a/include/net/netns/nftables.h b/include/net/netns/nftables.h index eee608b12cc9..c80781146019 100644 --- a/include/net/netns/nftables.h +++ b/include/net/netns/nftables.h | |||
@@ -13,6 +13,7 @@ struct netns_nftables { | |||
13 | struct nft_af_info *inet; | 13 | struct nft_af_info *inet; |
14 | struct nft_af_info *arp; | 14 | struct nft_af_info *arp; |
15 | struct nft_af_info *bridge; | 15 | struct nft_af_info *bridge; |
16 | struct nft_af_info *netdev; | ||
16 | unsigned int base_seq; | 17 | unsigned int base_seq; |
17 | u8 gencursor; | 18 | u8 gencursor; |
18 | }; | 19 | }; |
diff --git a/net/netfilter/Kconfig b/net/netfilter/Kconfig index 9a89e7c67d78..bd5aaebaa9a2 100644 --- a/net/netfilter/Kconfig +++ b/net/netfilter/Kconfig | |||
@@ -456,6 +456,11 @@ config NF_TABLES_INET | |||
456 | help | 456 | help |
457 | This option enables support for a mixed IPv4/IPv6 "inet" table. | 457 | This option enables support for a mixed IPv4/IPv6 "inet" table. |
458 | 458 | ||
459 | config NF_TABLES_NETDEV | ||
460 | tristate "Netfilter nf_tables netdev tables support" | ||
461 | help | ||
462 | This option enables support for the "netdev" table. | ||
463 | |||
459 | config NFT_EXTHDR | 464 | config NFT_EXTHDR |
460 | tristate "Netfilter nf_tables IPv6 exthdr module" | 465 | tristate "Netfilter nf_tables IPv6 exthdr module" |
461 | help | 466 | help |
diff --git a/net/netfilter/Makefile b/net/netfilter/Makefile index a87d8b8ec730..70d026d46fe7 100644 --- a/net/netfilter/Makefile +++ b/net/netfilter/Makefile | |||
@@ -75,6 +75,7 @@ nf_tables-objs += nft_bitwise.o nft_byteorder.o nft_payload.o | |||
75 | 75 | ||
76 | obj-$(CONFIG_NF_TABLES) += nf_tables.o | 76 | obj-$(CONFIG_NF_TABLES) += nf_tables.o |
77 | obj-$(CONFIG_NF_TABLES_INET) += nf_tables_inet.o | 77 | obj-$(CONFIG_NF_TABLES_INET) += nf_tables_inet.o |
78 | obj-$(CONFIG_NF_TABLES_NETDEV) += nf_tables_netdev.o | ||
78 | obj-$(CONFIG_NFT_COMPAT) += nft_compat.o | 79 | obj-$(CONFIG_NFT_COMPAT) += nft_compat.o |
79 | obj-$(CONFIG_NFT_EXTHDR) += nft_exthdr.o | 80 | obj-$(CONFIG_NFT_EXTHDR) += nft_exthdr.o |
80 | obj-$(CONFIG_NFT_META) += nft_meta.o | 81 | obj-$(CONFIG_NFT_META) += nft_meta.o |
diff --git a/net/netfilter/nf_tables_netdev.c b/net/netfilter/nf_tables_netdev.c new file mode 100644 index 000000000000..04cb17057f46 --- /dev/null +++ b/net/netfilter/nf_tables_netdev.c | |||
@@ -0,0 +1,183 @@ | |||
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> | ||
11 | #include <net/netfilter/nf_tables.h> | ||
12 | #include <linux/ip.h> | ||
13 | #include <linux/ipv6.h> | ||
14 | #include <net/netfilter/nf_tables_ipv4.h> | ||
15 | #include <net/netfilter/nf_tables_ipv6.h> | ||
16 | |||
17 | static inline void | ||
18 | nft_netdev_set_pktinfo_ipv4(struct nft_pktinfo *pkt, | ||
19 | const struct nf_hook_ops *ops, struct sk_buff *skb, | ||
20 | const struct nf_hook_state *state) | ||
21 | { | ||
22 | struct iphdr *iph, _iph; | ||
23 | u32 len, thoff; | ||
24 | |||
25 | nft_set_pktinfo(pkt, ops, skb, state); | ||
26 | |||
27 | iph = skb_header_pointer(skb, skb_network_offset(skb), sizeof(*iph), | ||
28 | &_iph); | ||
29 | if (!iph) | ||
30 | return; | ||
31 | |||
32 | iph = ip_hdr(skb); | ||
33 | if (iph->ihl < 5 || iph->version != 4) | ||
34 | return; | ||
35 | |||
36 | len = ntohs(iph->tot_len); | ||
37 | thoff = iph->ihl * 4; | ||
38 | if (skb->len < len) | ||
39 | return; | ||
40 | else if (len < thoff) | ||
41 | return; | ||
42 | |||
43 | pkt->tprot = iph->protocol; | ||
44 | pkt->xt.thoff = thoff; | ||
45 | pkt->xt.fragoff = ntohs(iph->frag_off) & IP_OFFSET; | ||
46 | } | ||
47 | |||
48 | static inline void | ||
49 | __nft_netdev_set_pktinfo_ipv6(struct nft_pktinfo *pkt, | ||
50 | const struct nf_hook_ops *ops, | ||
51 | struct sk_buff *skb, | ||
52 | const struct nf_hook_state *state) | ||
53 | { | ||
54 | #if IS_ENABLED(CONFIG_IPV6) | ||
55 | struct ipv6hdr *ip6h, _ip6h; | ||
56 | unsigned int thoff = 0; | ||
57 | unsigned short frag_off; | ||
58 | int protohdr; | ||
59 | u32 pkt_len; | ||
60 | |||
61 | ip6h = skb_header_pointer(skb, skb_network_offset(skb), sizeof(*ip6h), | ||
62 | &_ip6h); | ||
63 | if (!ip6h) | ||
64 | return; | ||
65 | |||
66 | if (ip6h->version != 6) | ||
67 | return; | ||
68 | |||
69 | pkt_len = ntohs(ip6h->payload_len); | ||
70 | if (pkt_len + sizeof(*ip6h) > skb->len) | ||
71 | return; | ||
72 | |||
73 | protohdr = ipv6_find_hdr(pkt->skb, &thoff, -1, &frag_off, NULL); | ||
74 | if (protohdr < 0) | ||
75 | return; | ||
76 | |||
77 | pkt->tprot = protohdr; | ||
78 | pkt->xt.thoff = thoff; | ||
79 | pkt->xt.fragoff = frag_off; | ||
80 | #endif | ||
81 | } | ||
82 | |||
83 | static inline void nft_netdev_set_pktinfo_ipv6(struct nft_pktinfo *pkt, | ||
84 | const struct nf_hook_ops *ops, | ||
85 | struct sk_buff *skb, | ||
86 | const struct nf_hook_state *state) | ||
87 | { | ||
88 | nft_set_pktinfo(pkt, ops, skb, state); | ||
89 | __nft_netdev_set_pktinfo_ipv6(pkt, ops, skb, state); | ||
90 | } | ||
91 | |||
92 | static unsigned int | ||
93 | nft_do_chain_netdev(const struct nf_hook_ops *ops, struct sk_buff *skb, | ||
94 | const struct nf_hook_state *state) | ||
95 | { | ||
96 | struct nft_pktinfo pkt; | ||
97 | |||
98 | switch (eth_hdr(skb)->h_proto) { | ||
99 | case htons(ETH_P_IP): | ||
100 | nft_netdev_set_pktinfo_ipv4(&pkt, ops, skb, state); | ||
101 | break; | ||
102 | case htons(ETH_P_IPV6): | ||
103 | nft_netdev_set_pktinfo_ipv6(&pkt, ops, skb, state); | ||
104 | break; | ||
105 | default: | ||
106 | nft_set_pktinfo(&pkt, ops, skb, state); | ||
107 | break; | ||
108 | } | ||
109 | |||
110 | return nft_do_chain(&pkt, ops); | ||
111 | } | ||
112 | |||
113 | static struct nft_af_info nft_af_netdev __read_mostly = { | ||
114 | .family = NFPROTO_NETDEV, | ||
115 | .nhooks = NF_NETDEV_NUMHOOKS, | ||
116 | .owner = THIS_MODULE, | ||
117 | .flags = NFT_AF_NEEDS_DEV, | ||
118 | .nops = 1, | ||
119 | .hooks = { | ||
120 | [NF_NETDEV_INGRESS] = nft_do_chain_netdev, | ||
121 | }, | ||
122 | }; | ||
123 | |||
124 | static int nf_tables_netdev_init_net(struct net *net) | ||
125 | { | ||
126 | net->nft.netdev = kmalloc(sizeof(struct nft_af_info), GFP_KERNEL); | ||
127 | if (net->nft.netdev == NULL) | ||
128 | return -ENOMEM; | ||
129 | |||
130 | memcpy(net->nft.netdev, &nft_af_netdev, sizeof(nft_af_netdev)); | ||
131 | |||
132 | if (nft_register_afinfo(net, net->nft.netdev) < 0) | ||
133 | goto err; | ||
134 | |||
135 | return 0; | ||
136 | err: | ||
137 | kfree(net->nft.netdev); | ||
138 | return -ENOMEM; | ||
139 | } | ||
140 | |||
141 | static void nf_tables_netdev_exit_net(struct net *net) | ||
142 | { | ||
143 | nft_unregister_afinfo(net->nft.netdev); | ||
144 | kfree(net->nft.netdev); | ||
145 | } | ||
146 | |||
147 | static struct pernet_operations nf_tables_netdev_net_ops = { | ||
148 | .init = nf_tables_netdev_init_net, | ||
149 | .exit = nf_tables_netdev_exit_net, | ||
150 | }; | ||
151 | |||
152 | static const struct nf_chain_type nft_filter_chain_netdev = { | ||
153 | .name = "filter", | ||
154 | .type = NFT_CHAIN_T_DEFAULT, | ||
155 | .family = NFPROTO_NETDEV, | ||
156 | .owner = THIS_MODULE, | ||
157 | .hook_mask = (1 << NF_NETDEV_INGRESS), | ||
158 | }; | ||
159 | |||
160 | static int __init nf_tables_netdev_init(void) | ||
161 | { | ||
162 | int ret; | ||
163 | |||
164 | nft_register_chain_type(&nft_filter_chain_netdev); | ||
165 | ret = register_pernet_subsys(&nf_tables_netdev_net_ops); | ||
166 | if (ret < 0) | ||
167 | nft_unregister_chain_type(&nft_filter_chain_netdev); | ||
168 | |||
169 | return ret; | ||
170 | } | ||
171 | |||
172 | static void __exit nf_tables_netdev_exit(void) | ||
173 | { | ||
174 | unregister_pernet_subsys(&nf_tables_netdev_net_ops); | ||
175 | nft_unregister_chain_type(&nft_filter_chain_netdev); | ||
176 | } | ||
177 | |||
178 | module_init(nf_tables_netdev_init); | ||
179 | module_exit(nf_tables_netdev_exit); | ||
180 | |||
181 | MODULE_LICENSE("GPL"); | ||
182 | MODULE_AUTHOR("Pablo Neira Ayuso <pablo@netfilter.org>"); | ||
183 | MODULE_ALIAS_NFT_FAMILY(5); /* NFPROTO_NETDEV */ | ||