aboutsummaryrefslogtreecommitdiffstats
path: root/net/netfilter
diff options
context:
space:
mode:
authorDavid S. Miller <davem@davemloft.net>2015-05-31 03:02:30 -0400
committerDavid S. Miller <davem@davemloft.net>2015-05-31 03:02:30 -0400
commit583d3f5af2a6dfa7866715d9e062dbfb3b66a6f0 (patch)
tree77e7c8514031b67d74bb5ce415074dd098b108ac /net/netfilter
parent5289e4a03fb9ba79f0ad065c26024afe379c3d35 (diff)
parented6c4136f1571bd6ab362afc3410905a8a69ca42 (diff)
Merge git://git.kernel.org/pub/scm/linux/kernel/git/pablo/nf-next
Pablo Neira Ayuso says: ==================== Netfilter updates for net-next The following patchset contains Netfilter updates for net-next, they are: 1) default CONFIG_NETFILTER_INGRESS to y for easier compile-testing of all options. 2) Allow to bind a table to net_device. This introduces the internal NFT_AF_NEEDS_DEV flag to perform a mandatory check for this binding. This is required by the next patch. 3) Add the 'netdev' table family, this new table allows you to create ingress filter basechains. This provides access to the existing nf_tables features from ingress. 4) Kill unused argument from compat_find_calc_{match,target} in ip_tables and ip6_tables, from Florian Westphal. ==================== Signed-off-by: David S. Miller <davem@davemloft.net>
Diffstat (limited to 'net/netfilter')
-rw-r--r--net/netfilter/Kconfig6
-rw-r--r--net/netfilter/Makefile1
-rw-r--r--net/netfilter/nf_tables_api.c46
-rw-r--r--net/netfilter/nf_tables_netdev.c183
4 files changed, 231 insertions, 5 deletions
diff --git a/net/netfilter/Kconfig b/net/netfilter/Kconfig
index 1c78d7fb1da7..fbc8d15c7fda 100644
--- a/net/netfilter/Kconfig
+++ b/net/netfilter/Kconfig
@@ -3,6 +3,7 @@ menu "Core Netfilter Configuration"
3 3
4config NETFILTER_INGRESS 4config NETFILTER_INGRESS
5 bool "Netfilter ingress support" 5 bool "Netfilter ingress support"
6 default y
6 select NET_INGRESS 7 select NET_INGRESS
7 help 8 help
8 This allows you to classify packets from ingress using the Netfilter 9 This allows you to classify packets from ingress using the Netfilter
@@ -455,6 +456,11 @@ config NF_TABLES_INET
455 help 456 help
456 This option enables support for a mixed IPv4/IPv6 "inet" table. 457 This option enables support for a mixed IPv4/IPv6 "inet" table.
457 458
459config NF_TABLES_NETDEV
460 tristate "Netfilter nf_tables netdev tables support"
461 help
462 This option enables support for the "netdev" table.
463
458config NFT_EXTHDR 464config NFT_EXTHDR
459 tristate "Netfilter nf_tables IPv6 exthdr module" 465 tristate "Netfilter nf_tables IPv6 exthdr module"
460 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
76obj-$(CONFIG_NF_TABLES) += nf_tables.o 76obj-$(CONFIG_NF_TABLES) += nf_tables.o
77obj-$(CONFIG_NF_TABLES_INET) += nf_tables_inet.o 77obj-$(CONFIG_NF_TABLES_INET) += nf_tables_inet.o
78obj-$(CONFIG_NF_TABLES_NETDEV) += nf_tables_netdev.o
78obj-$(CONFIG_NFT_COMPAT) += nft_compat.o 79obj-$(CONFIG_NFT_COMPAT) += nft_compat.o
79obj-$(CONFIG_NFT_EXTHDR) += nft_exthdr.o 80obj-$(CONFIG_NFT_EXTHDR) += nft_exthdr.o
80obj-$(CONFIG_NFT_META) += nft_meta.o 81obj-$(CONFIG_NFT_META) += nft_meta.o
diff --git a/net/netfilter/nf_tables_api.c b/net/netfilter/nf_tables_api.c
index 34ded09317e7..4528f122bcd2 100644
--- a/net/netfilter/nf_tables_api.c
+++ b/net/netfilter/nf_tables_api.c
@@ -399,6 +399,8 @@ static const struct nla_policy nft_table_policy[NFTA_TABLE_MAX + 1] = {
399 [NFTA_TABLE_NAME] = { .type = NLA_STRING, 399 [NFTA_TABLE_NAME] = { .type = NLA_STRING,
400 .len = NFT_TABLE_MAXNAMELEN - 1 }, 400 .len = NFT_TABLE_MAXNAMELEN - 1 },
401 [NFTA_TABLE_FLAGS] = { .type = NLA_U32 }, 401 [NFTA_TABLE_FLAGS] = { .type = NLA_U32 },
402 [NFTA_TABLE_DEV] = { .type = NLA_STRING,
403 .len = IFNAMSIZ - 1 },
402}; 404};
403 405
404static int nf_tables_fill_table_info(struct sk_buff *skb, struct net *net, 406static int nf_tables_fill_table_info(struct sk_buff *skb, struct net *net,
@@ -423,6 +425,10 @@ static int nf_tables_fill_table_info(struct sk_buff *skb, struct net *net,
423 nla_put_be32(skb, NFTA_TABLE_USE, htonl(table->use))) 425 nla_put_be32(skb, NFTA_TABLE_USE, htonl(table->use)))
424 goto nla_put_failure; 426 goto nla_put_failure;
425 427
428 if (table->dev &&
429 nla_put_string(skb, NFTA_TABLE_DEV, table->dev->name))
430 goto nla_put_failure;
431
426 nlmsg_end(skb, nlh); 432 nlmsg_end(skb, nlh);
427 return 0; 433 return 0;
428 434
@@ -608,6 +614,11 @@ static int nf_tables_updtable(struct nft_ctx *ctx)
608 if (flags == ctx->table->flags) 614 if (flags == ctx->table->flags)
609 return 0; 615 return 0;
610 616
617 if ((ctx->afi->flags & NFT_AF_NEEDS_DEV) &&
618 ctx->nla[NFTA_TABLE_DEV] &&
619 nla_strcmp(ctx->nla[NFTA_TABLE_DEV], ctx->table->dev->name))
620 return -EOPNOTSUPP;
621
611 trans = nft_trans_alloc(ctx, NFT_MSG_NEWTABLE, 622 trans = nft_trans_alloc(ctx, NFT_MSG_NEWTABLE,
612 sizeof(struct nft_trans_table)); 623 sizeof(struct nft_trans_table));
613 if (trans == NULL) 624 if (trans == NULL)
@@ -645,6 +656,7 @@ static int nf_tables_newtable(struct sock *nlsk, struct sk_buff *skb,
645 struct nft_table *table; 656 struct nft_table *table;
646 struct net *net = sock_net(skb->sk); 657 struct net *net = sock_net(skb->sk);
647 int family = nfmsg->nfgen_family; 658 int family = nfmsg->nfgen_family;
659 struct net_device *dev = NULL;
648 u32 flags = 0; 660 u32 flags = 0;
649 struct nft_ctx ctx; 661 struct nft_ctx ctx;
650 int err; 662 int err;
@@ -679,30 +691,50 @@ static int nf_tables_newtable(struct sock *nlsk, struct sk_buff *skb,
679 return -EINVAL; 691 return -EINVAL;
680 } 692 }
681 693
694 if (afi->flags & NFT_AF_NEEDS_DEV) {
695 char ifname[IFNAMSIZ];
696
697 if (!nla[NFTA_TABLE_DEV])
698 return -EOPNOTSUPP;
699
700 nla_strlcpy(ifname, nla[NFTA_TABLE_DEV], IFNAMSIZ);
701 dev = dev_get_by_name(net, ifname);
702 if (!dev)
703 return -ENOENT;
704 } else if (nla[NFTA_TABLE_DEV]) {
705 return -EOPNOTSUPP;
706 }
707
708 err = -EAFNOSUPPORT;
682 if (!try_module_get(afi->owner)) 709 if (!try_module_get(afi->owner))
683 return -EAFNOSUPPORT; 710 goto err1;
684 711
685 err = -ENOMEM; 712 err = -ENOMEM;
686 table = kzalloc(sizeof(*table), GFP_KERNEL); 713 table = kzalloc(sizeof(*table), GFP_KERNEL);
687 if (table == NULL) 714 if (table == NULL)
688 goto err1; 715 goto err2;
689 716
690 nla_strlcpy(table->name, name, NFT_TABLE_MAXNAMELEN); 717 nla_strlcpy(table->name, name, NFT_TABLE_MAXNAMELEN);
691 INIT_LIST_HEAD(&table->chains); 718 INIT_LIST_HEAD(&table->chains);
692 INIT_LIST_HEAD(&table->sets); 719 INIT_LIST_HEAD(&table->sets);
693 table->flags = flags; 720 table->flags = flags;
721 table->dev = dev;
694 722
695 nft_ctx_init(&ctx, skb, nlh, afi, table, NULL, nla); 723 nft_ctx_init(&ctx, skb, nlh, afi, table, NULL, nla);
696 err = nft_trans_table_add(&ctx, NFT_MSG_NEWTABLE); 724 err = nft_trans_table_add(&ctx, NFT_MSG_NEWTABLE);
697 if (err < 0) 725 if (err < 0)
698 goto err2; 726 goto err3;
699 727
700 list_add_tail_rcu(&table->list, &afi->tables); 728 list_add_tail_rcu(&table->list, &afi->tables);
701 return 0; 729 return 0;
702err2: 730err3:
703 kfree(table); 731 kfree(table);
704err1: 732err2:
705 module_put(afi->owner); 733 module_put(afi->owner);
734err1:
735 if (dev != NULL)
736 dev_put(dev);
737
706 return err; 738 return err;
707} 739}
708 740
@@ -806,6 +838,9 @@ static void nf_tables_table_destroy(struct nft_ctx *ctx)
806{ 838{
807 BUG_ON(ctx->table->use > 0); 839 BUG_ON(ctx->table->use > 0);
808 840
841 if (ctx->table->dev)
842 dev_put(ctx->table->dev);
843
809 kfree(ctx->table); 844 kfree(ctx->table);
810 module_put(ctx->afi->owner); 845 module_put(ctx->afi->owner);
811} 846}
@@ -1361,6 +1396,7 @@ static int nf_tables_newchain(struct sock *nlsk, struct sk_buff *skb,
1361 ops->priority = priority; 1396 ops->priority = priority;
1362 ops->priv = chain; 1397 ops->priv = chain;
1363 ops->hook = afi->hooks[ops->hooknum]; 1398 ops->hook = afi->hooks[ops->hooknum];
1399 ops->dev = table->dev;
1364 if (hookfn) 1400 if (hookfn)
1365 ops->hook = hookfn; 1401 ops->hook = hookfn;
1366 if (afi->hook_ops_init) 1402 if (afi->hook_ops_init)
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
17static inline void
18nft_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
48static 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
83static 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
92static unsigned int
93nft_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
113static 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
124static 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;
136err:
137 kfree(net->nft.netdev);
138 return -ENOMEM;
139}
140
141static void nf_tables_netdev_exit_net(struct net *net)
142{
143 nft_unregister_afinfo(net->nft.netdev);
144 kfree(net->nft.netdev);
145}
146
147static struct pernet_operations nf_tables_netdev_net_ops = {
148 .init = nf_tables_netdev_init_net,
149 .exit = nf_tables_netdev_exit_net,
150};
151
152static 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
160static 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
172static 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
178module_init(nf_tables_netdev_init);
179module_exit(nf_tables_netdev_exit);
180
181MODULE_LICENSE("GPL");
182MODULE_AUTHOR("Pablo Neira Ayuso <pablo@netfilter.org>");
183MODULE_ALIAS_NFT_FAMILY(5); /* NFPROTO_NETDEV */