diff options
Diffstat (limited to 'net/bridge/netfilter')
| -rw-r--r-- | net/bridge/netfilter/Kconfig | 17 | ||||
| -rw-r--r-- | net/bridge/netfilter/Makefile | 3 | ||||
| -rw-r--r-- | net/bridge/netfilter/ebt_among.c | 2 | ||||
| -rw-r--r-- | net/bridge/netfilter/ebt_dnat.c | 2 | ||||
| -rw-r--r-- | net/bridge/netfilter/ebt_ip6.c | 8 | ||||
| -rw-r--r-- | net/bridge/netfilter/ebt_log.c | 2 | ||||
| -rw-r--r-- | net/bridge/netfilter/ebt_redirect.c | 6 | ||||
| -rw-r--r-- | net/bridge/netfilter/ebt_snat.c | 4 | ||||
| -rw-r--r-- | net/bridge/netfilter/ebt_ulog.c | 9 | ||||
| -rw-r--r-- | net/bridge/netfilter/ebt_vlan.c | 3 | ||||
| -rw-r--r-- | net/bridge/netfilter/ebtable_broute.c | 6 | ||||
| -rw-r--r-- | net/bridge/netfilter/ebtable_filter.c | 25 | ||||
| -rw-r--r-- | net/bridge/netfilter/ebtable_nat.c | 25 | ||||
| -rw-r--r-- | net/bridge/netfilter/ebtables.c | 22 | ||||
| -rw-r--r-- | net/bridge/netfilter/nf_tables_bridge.c | 104 | ||||
| -rw-r--r-- | net/bridge/netfilter/nft_meta_bridge.c | 139 | 
16 files changed, 316 insertions, 61 deletions
diff --git a/net/bridge/netfilter/Kconfig b/net/bridge/netfilter/Kconfig index a9aff9c7d02..629dc77874a 100644 --- a/net/bridge/netfilter/Kconfig +++ b/net/bridge/netfilter/Kconfig @@ -1,11 +1,24 @@  #  # Bridge netfilter configuration  # +# +menuconfig NF_TABLES_BRIDGE +	depends on BRIDGE && NETFILTER && NF_TABLES +	tristate "Ethernet Bridge nf_tables support" + +if NF_TABLES_BRIDGE + +config NFT_BRIDGE_META +	tristate "Netfilter nf_table bridge meta support" +	depends on NFT_META +	help +	  Add support for bridge dedicated meta key. + +endif # NF_TABLES_BRIDGE  menuconfig BRIDGE_NF_EBTABLES  	tristate "Ethernet Bridge tables (ebtables) support" -	depends on BRIDGE && NETFILTER -	select NETFILTER_XTABLES +	depends on BRIDGE && NETFILTER && NETFILTER_XTABLES  	help  	  ebtables is a general, extensible frame/packet identification  	  framework. Say 'Y' or 'M' here if you want to do Ethernet diff --git a/net/bridge/netfilter/Makefile b/net/bridge/netfilter/Makefile index 0718699540b..6f2f3943d66 100644 --- a/net/bridge/netfilter/Makefile +++ b/net/bridge/netfilter/Makefile @@ -2,6 +2,9 @@  # Makefile for the netfilter modules for Link Layer filtering on a bridge.  # +obj-$(CONFIG_NF_TABLES_BRIDGE) += nf_tables_bridge.o +obj-$(CONFIG_NFT_BRIDGE_META)  += nft_meta_bridge.o +  obj-$(CONFIG_BRIDGE_NF_EBTABLES) += ebtables.o  # tables diff --git a/net/bridge/netfilter/ebt_among.c b/net/bridge/netfilter/ebt_among.c index 8b84c581be3..9024283d2bc 100644 --- a/net/bridge/netfilter/ebt_among.c +++ b/net/bridge/netfilter/ebt_among.c @@ -28,7 +28,7 @@ static bool ebt_mac_wormhash_contains(const struct ebt_mac_wormhash *wh,  	uint32_t cmp[2] = { 0, 0 };  	int key = ((const unsigned char *)mac)[5]; -	memcpy(((char *) cmp) + 2, mac, 6); +	ether_addr_copy(((char *) cmp) + 2, mac);  	start = wh->table[key];  	limit = wh->table[key + 1];  	if (ip) { diff --git a/net/bridge/netfilter/ebt_dnat.c b/net/bridge/netfilter/ebt_dnat.c index c59f7bfae6e..4e0b0c35932 100644 --- a/net/bridge/netfilter/ebt_dnat.c +++ b/net/bridge/netfilter/ebt_dnat.c @@ -22,7 +22,7 @@ ebt_dnat_tg(struct sk_buff *skb, const struct xt_action_param *par)  	if (!skb_make_writable(skb, 0))  		return EBT_DROP; -	memcpy(eth_hdr(skb)->h_dest, info->mac, ETH_ALEN); +	ether_addr_copy(eth_hdr(skb)->h_dest, info->mac);  	return info->target;  } diff --git a/net/bridge/netfilter/ebt_ip6.c b/net/bridge/netfilter/ebt_ip6.c index 99c85668f55..17fd5f2cb4b 100644 --- a/net/bridge/netfilter/ebt_ip6.c +++ b/net/bridge/netfilter/ebt_ip6.c @@ -48,10 +48,12 @@ ebt_ip6_mt(const struct sk_buff *skb, struct xt_action_param *par)  	if (info->bitmask & EBT_IP6_TCLASS &&  	   FWINV(info->tclass != ipv6_get_dsfield(ih6), EBT_IP6_TCLASS))  		return false; -	if (FWINV(ipv6_masked_addr_cmp(&ih6->saddr, &info->smsk, -				       &info->saddr), EBT_IP6_SOURCE) || +	if ((info->bitmask & EBT_IP6_SOURCE && +	    FWINV(ipv6_masked_addr_cmp(&ih6->saddr, &info->smsk, +				       &info->saddr), EBT_IP6_SOURCE)) || +	    (info->bitmask & EBT_IP6_DEST &&  	    FWINV(ipv6_masked_addr_cmp(&ih6->daddr, &info->dmsk, -				       &info->daddr), EBT_IP6_DEST)) +				       &info->daddr), EBT_IP6_DEST)))  		return false;  	if (info->bitmask & EBT_IP6_PROTO) {  		uint8_t nexthdr = ih6->nexthdr; diff --git a/net/bridge/netfilter/ebt_log.c b/net/bridge/netfilter/ebt_log.c index 19c37a4929b..5322a36867a 100644 --- a/net/bridge/netfilter/ebt_log.c +++ b/net/bridge/netfilter/ebt_log.c @@ -96,7 +96,7 @@ ebt_log_packet(struct net *net, u_int8_t pf, unsigned int hooknum,  		bitmask = NF_LOG_MASK;  	if ((bitmask & EBT_LOG_IP) && eth_hdr(skb)->h_proto == -	   htons(ETH_P_IP)){ +	   htons(ETH_P_IP)) {  		const struct iphdr *ih;  		struct iphdr _iph; diff --git a/net/bridge/netfilter/ebt_redirect.c b/net/bridge/netfilter/ebt_redirect.c index 46624bb6d9b..203964997a5 100644 --- a/net/bridge/netfilter/ebt_redirect.c +++ b/net/bridge/netfilter/ebt_redirect.c @@ -25,10 +25,10 @@ ebt_redirect_tg(struct sk_buff *skb, const struct xt_action_param *par)  	if (par->hooknum != NF_BR_BROUTING)  		/* rcu_read_lock()ed by nf_hook_slow */ -		memcpy(eth_hdr(skb)->h_dest, -		       br_port_get_rcu(par->in)->br->dev->dev_addr, ETH_ALEN); +		ether_addr_copy(eth_hdr(skb)->h_dest, +				br_port_get_rcu(par->in)->br->dev->dev_addr);  	else -		memcpy(eth_hdr(skb)->h_dest, par->in->dev_addr, ETH_ALEN); +		ether_addr_copy(eth_hdr(skb)->h_dest, par->in->dev_addr);  	skb->pkt_type = PACKET_HOST;  	return info->target;  } diff --git a/net/bridge/netfilter/ebt_snat.c b/net/bridge/netfilter/ebt_snat.c index f8f0bd1a1d5..e56ccd060d2 100644 --- a/net/bridge/netfilter/ebt_snat.c +++ b/net/bridge/netfilter/ebt_snat.c @@ -24,7 +24,7 @@ ebt_snat_tg(struct sk_buff *skb, const struct xt_action_param *par)  	if (!skb_make_writable(skb, 0))  		return EBT_DROP; -	memcpy(eth_hdr(skb)->h_source, info->mac, ETH_ALEN); +	ether_addr_copy(eth_hdr(skb)->h_source, info->mac);  	if (!(info->target & NAT_ARP_BIT) &&  	    eth_hdr(skb)->h_proto == htons(ETH_P_ARP)) {  		const struct arphdr *ap; @@ -35,7 +35,7 @@ ebt_snat_tg(struct sk_buff *skb, const struct xt_action_param *par)  			return EBT_DROP;  		if (ap->ar_hln != ETH_ALEN)  			goto out; -		if (skb_store_bits(skb, sizeof(_ah), info->mac,ETH_ALEN)) +		if (skb_store_bits(skb, sizeof(_ah), info->mac, ETH_ALEN))  			return EBT_DROP;  	}  out: diff --git a/net/bridge/netfilter/ebt_ulog.c b/net/bridge/netfilter/ebt_ulog.c index 518093802d1..7c470c371e1 100644 --- a/net/bridge/netfilter/ebt_ulog.c +++ b/net/bridge/netfilter/ebt_ulog.c @@ -181,6 +181,7 @@ static void ebt_ulog_packet(struct net *net, unsigned int hooknr,  	ub->qlen++;  	pm = nlmsg_data(nlh); +	memset(pm, 0, sizeof(*pm));  	/* Fill in the ulog data */  	pm->version = EBT_ULOG_VERSION; @@ -193,8 +194,6 @@ static void ebt_ulog_packet(struct net *net, unsigned int hooknr,  	pm->hook = hooknr;  	if (uloginfo->prefix != NULL)  		strcpy(pm->prefix, uloginfo->prefix); -	else -		*(pm->prefix) = '\0';  	if (in) {  		strcpy(pm->physindev, in->name); @@ -204,16 +203,14 @@ static void ebt_ulog_packet(struct net *net, unsigned int hooknr,  			strcpy(pm->indev, br_port_get_rcu(in)->br->dev->name);  		else  			strcpy(pm->indev, in->name); -	} else -		pm->indev[0] = pm->physindev[0] = '\0'; +	}  	if (out) {  		/* If out exists, then out is a bridge port */  		strcpy(pm->physoutdev, out->name);  		/* rcu_read_lock()ed by nf_hook_slow */  		strcpy(pm->outdev, br_port_get_rcu(out)->br->dev->name); -	} else -		pm->outdev[0] = pm->physoutdev[0] = '\0'; +	}  	if (skb_copy_bits(skb, -ETH_HLEN, pm->data, copy_len) < 0)  		BUG(); diff --git a/net/bridge/netfilter/ebt_vlan.c b/net/bridge/netfilter/ebt_vlan.c index eae67bf0446..8d3f8c7651f 100644 --- a/net/bridge/netfilter/ebt_vlan.c +++ b/net/bridge/netfilter/ebt_vlan.c @@ -14,8 +14,7 @@   * GNU General Public License for more details.   *   * You should have received a copy of the GNU General Public License - * along with this program; if not, write to the Free Software - * Foundation, Inc., 59 Temple Place, Suite 330, Boston, MA  02111-1307  USA + * along with this program; if not, see <http://www.gnu.org/licenses/>.   */  #include <linux/if_ether.h> diff --git a/net/bridge/netfilter/ebtable_broute.c b/net/bridge/netfilter/ebtable_broute.c index dbd1c783431..d2cdf5d6e98 100644 --- a/net/bridge/netfilter/ebtable_broute.c +++ b/net/bridge/netfilter/ebtable_broute.c @@ -23,8 +23,7 @@ static struct ebt_entries initial_chain = {  	.policy		= EBT_ACCEPT,  }; -static struct ebt_replace_kernel initial_table = -{ +static struct ebt_replace_kernel initial_table = {  	.name		= "broute",  	.valid_hooks	= 1 << NF_BR_BROUTING,  	.entries_size	= sizeof(struct ebt_entries), @@ -41,8 +40,7 @@ static int check(const struct ebt_table_info *info, unsigned int valid_hooks)  	return 0;  } -static const struct ebt_table broute_table = -{ +static const struct ebt_table broute_table = {  	.name		= "broute",  	.table		= &initial_table,  	.valid_hooks	= 1 << NF_BR_BROUTING, diff --git a/net/bridge/netfilter/ebtable_filter.c b/net/bridge/netfilter/ebtable_filter.c index 94b2b700cff..ce205aabf9c 100644 --- a/net/bridge/netfilter/ebtable_filter.c +++ b/net/bridge/netfilter/ebtable_filter.c @@ -14,8 +14,7 @@  #define FILTER_VALID_HOOKS ((1 << NF_BR_LOCAL_IN) | (1 << NF_BR_FORWARD) | \     (1 << NF_BR_LOCAL_OUT)) -static struct ebt_entries initial_chains[] = -{ +static struct ebt_entries initial_chains[] = {  	{  		.name	= "INPUT",  		.policy	= EBT_ACCEPT, @@ -30,8 +29,7 @@ static struct ebt_entries initial_chains[] =  	},  }; -static struct ebt_replace_kernel initial_table = -{ +static struct ebt_replace_kernel initial_table = {  	.name		= "filter",  	.valid_hooks	= FILTER_VALID_HOOKS,  	.entries_size	= 3 * sizeof(struct ebt_entries), @@ -50,8 +48,7 @@ static int check(const struct ebt_table_info *info, unsigned int valid_hooks)  	return 0;  } -static const struct ebt_table frame_filter = -{ +static const struct ebt_table frame_filter = {  	.name		= "filter",  	.table		= &initial_table,  	.valid_hooks	= FILTER_VALID_HOOKS, @@ -60,17 +57,21 @@ static const struct ebt_table frame_filter =  };  static unsigned int -ebt_in_hook(unsigned int hook, struct sk_buff *skb, const struct net_device *in, -   const struct net_device *out, int (*okfn)(struct sk_buff *)) +ebt_in_hook(const struct nf_hook_ops *ops, struct sk_buff *skb, +	    const struct net_device *in, const struct net_device *out, +	    int (*okfn)(struct sk_buff *))  { -	return ebt_do_table(hook, skb, in, out, dev_net(in)->xt.frame_filter); +	return ebt_do_table(ops->hooknum, skb, in, out, +			    dev_net(in)->xt.frame_filter);  }  static unsigned int -ebt_out_hook(unsigned int hook, struct sk_buff *skb, const struct net_device *in, -   const struct net_device *out, int (*okfn)(struct sk_buff *)) +ebt_out_hook(const struct nf_hook_ops *ops, struct sk_buff *skb, +	     const struct net_device *in, const struct net_device *out, +	     int (*okfn)(struct sk_buff *))  { -	return ebt_do_table(hook, skb, in, out, dev_net(out)->xt.frame_filter); +	return ebt_do_table(ops->hooknum, skb, in, out, +			    dev_net(out)->xt.frame_filter);  }  static struct nf_hook_ops ebt_ops_filter[] __read_mostly = { diff --git a/net/bridge/netfilter/ebtable_nat.c b/net/bridge/netfilter/ebtable_nat.c index 322555acdd4..a0ac2984fb6 100644 --- a/net/bridge/netfilter/ebtable_nat.c +++ b/net/bridge/netfilter/ebtable_nat.c @@ -14,8 +14,7 @@  #define NAT_VALID_HOOKS ((1 << NF_BR_PRE_ROUTING) | (1 << NF_BR_LOCAL_OUT) | \     (1 << NF_BR_POST_ROUTING)) -static struct ebt_entries initial_chains[] = -{ +static struct ebt_entries initial_chains[] = {  	{  		.name	= "PREROUTING",  		.policy	= EBT_ACCEPT, @@ -30,8 +29,7 @@ static struct ebt_entries initial_chains[] =  	}  }; -static struct ebt_replace_kernel initial_table = -{ +static struct ebt_replace_kernel initial_table = {  	.name		= "nat",  	.valid_hooks	= NAT_VALID_HOOKS,  	.entries_size	= 3 * sizeof(struct ebt_entries), @@ -50,8 +48,7 @@ static int check(const struct ebt_table_info *info, unsigned int valid_hooks)  	return 0;  } -static struct ebt_table frame_nat = -{ +static struct ebt_table frame_nat = {  	.name		= "nat",  	.table		= &initial_table,  	.valid_hooks	= NAT_VALID_HOOKS, @@ -60,17 +57,21 @@ static struct ebt_table frame_nat =  };  static unsigned int -ebt_nat_in(unsigned int hook, struct sk_buff *skb, const struct net_device *in -   , const struct net_device *out, int (*okfn)(struct sk_buff *)) +ebt_nat_in(const struct nf_hook_ops *ops, struct sk_buff *skb, +	   const struct net_device *in, const struct net_device *out, +	   int (*okfn)(struct sk_buff *))  { -	return ebt_do_table(hook, skb, in, out, dev_net(in)->xt.frame_nat); +	return ebt_do_table(ops->hooknum, skb, in, out, +			    dev_net(in)->xt.frame_nat);  }  static unsigned int -ebt_nat_out(unsigned int hook, struct sk_buff *skb, const struct net_device *in -   , const struct net_device *out, int (*okfn)(struct sk_buff *)) +ebt_nat_out(const struct nf_hook_ops *ops, struct sk_buff *skb, +	    const struct net_device *in, const struct net_device *out, +	    int (*okfn)(struct sk_buff *))  { -	return ebt_do_table(hook, skb, in, out, dev_net(out)->xt.frame_nat); +	return ebt_do_table(ops->hooknum, skb, in, out, +			    dev_net(out)->xt.frame_nat);  }  static struct nf_hook_ops ebt_ops_nat[] __read_mostly = { diff --git a/net/bridge/netfilter/ebtables.c b/net/bridge/netfilter/ebtables.c index ac780242838..1059ed3bc25 100644 --- a/net/bridge/netfilter/ebtables.c +++ b/net/bridge/netfilter/ebtables.c @@ -118,10 +118,10 @@ ebt_dev_check(const char *entry, const struct net_device *device)  	/* 1 is the wildcard token */  	while (entry[i] != '\0' && entry[i] != 1 && entry[i] == devname[i])  		i++; -	return (devname[i] != entry[i] && entry[i] != 1); +	return devname[i] != entry[i] && entry[i] != 1;  } -#define FWINV2(bool,invflg) ((bool) ^ !!(e->invflags & invflg)) +#define FWINV2(bool, invflg) ((bool) ^ !!(e->invflags & invflg))  /* process standard matches */  static inline int  ebt_basic_match(const struct ebt_entry *e, const struct sk_buff *skb, @@ -1044,10 +1044,9 @@ static int do_replace_finish(struct net *net, struct ebt_replace *repl,  	if (repl->num_counters &&  	   copy_to_user(repl->counters, counterstmp,  	   repl->num_counters * sizeof(struct ebt_counter))) { -		ret = -EFAULT; +		/* Silent error, can't fail, new table is already in place */ +		net_warn_ratelimited("ebtables: counters copy to user failed while replacing table\n");  	} -	else -		ret = 0;  	/* decrease module count and free resources */  	EBT_ENTRY_ITERATE(table->entries, table->entries_size, @@ -1441,7 +1440,7 @@ static int copy_everything_to_user(struct ebt_table *t, void __user *user,  		return -EFAULT;  	if (*len != sizeof(struct ebt_replace) + entries_size + -	   (tmp.num_counters? nentries * sizeof(struct ebt_counter): 0)) +	   (tmp.num_counters ? nentries * sizeof(struct ebt_counter) : 0))  		return -EINVAL;  	if (tmp.nentries != nentries) { @@ -1477,7 +1476,7 @@ static int do_ebt_set_ctl(struct sock *sk,  	if (!ns_capable(net->user_ns, CAP_NET_ADMIN))  		return -EPERM; -	switch(cmd) { +	switch (cmd) {  	case EBT_SO_SET_ENTRIES:  		ret = do_replace(net, user, len);  		break; @@ -1507,10 +1506,10 @@ static int do_ebt_get_ctl(struct sock *sk, int cmd, void __user *user, int *len)  	if (!t)  		return ret; -	switch(cmd) { +	switch (cmd) {  	case EBT_SO_GET_INFO:  	case EBT_SO_GET_INIT_INFO: -		if (*len != sizeof(struct ebt_replace)){ +		if (*len != sizeof(struct ebt_replace)) {  			ret = -EINVAL;  			mutex_unlock(&ebt_mutex);  			break; @@ -1525,7 +1524,7 @@ static int do_ebt_get_ctl(struct sock *sk, int cmd, void __user *user, int *len)  			tmp.valid_hooks = t->table->valid_hooks;  		}  		mutex_unlock(&ebt_mutex); -		if (copy_to_user(user, &tmp, *len) != 0){ +		if (copy_to_user(user, &tmp, *len) != 0) {  			BUGPRINT("c2u Didn't work\n");  			ret = -EFAULT;  			break; @@ -2375,8 +2374,7 @@ static int compat_do_ebt_get_ctl(struct sock *sk, int cmd,  }  #endif -static struct nf_sockopt_ops ebt_sockopts = -{ +static struct nf_sockopt_ops ebt_sockopts = {  	.pf		= PF_INET,  	.set_optmin	= EBT_BASE_CTL,  	.set_optmax	= EBT_SO_SET_MAX + 1, diff --git a/net/bridge/netfilter/nf_tables_bridge.c b/net/bridge/netfilter/nf_tables_bridge.c new file mode 100644 index 00000000000..5bcc0d8b31f --- /dev/null +++ b/net/bridge/netfilter/nf_tables_bridge.c @@ -0,0 +1,104 @@ +/* + * Copyright (c) 2008 Patrick McHardy <kaber@trash.net> + * Copyright (c) 2013 Pablo Neira Ayuso <pablo@netfilter.org> + * + * This program is free software; you can redistribute it and/or modify + * it under the terms of the GNU General Public License version 2 as + * published by the Free Software Foundation. + * + * Development of this code funded by Astaro AG (http://www.astaro.com/) + */ + +#include <linux/init.h> +#include <linux/module.h> +#include <linux/netfilter_bridge.h> +#include <net/netfilter/nf_tables.h> + +static unsigned int +nft_do_chain_bridge(const struct nf_hook_ops *ops, +		    struct sk_buff *skb, +		    const struct net_device *in, +		    const struct net_device *out, +		    int (*okfn)(struct sk_buff *)) +{ +	struct nft_pktinfo pkt; + +	nft_set_pktinfo(&pkt, ops, skb, in, out); + +	return nft_do_chain(&pkt, ops); +} + +static struct nft_af_info nft_af_bridge __read_mostly = { +	.family		= NFPROTO_BRIDGE, +	.nhooks		= NF_BR_NUMHOOKS, +	.owner		= THIS_MODULE, +	.nops		= 1, +	.hooks		= { +		[NF_BR_LOCAL_IN]	= nft_do_chain_bridge, +		[NF_BR_FORWARD]		= nft_do_chain_bridge, +		[NF_BR_LOCAL_OUT]	= nft_do_chain_bridge, +	}, +}; + +static int nf_tables_bridge_init_net(struct net *net) +{ +	net->nft.bridge = kmalloc(sizeof(struct nft_af_info), GFP_KERNEL); +	if (net->nft.bridge == NULL) +		return -ENOMEM; + +	memcpy(net->nft.bridge, &nft_af_bridge, sizeof(nft_af_bridge)); + +	if (nft_register_afinfo(net, net->nft.bridge) < 0) +		goto err; + +	return 0; +err: +	kfree(net->nft.bridge); +	return -ENOMEM; +} + +static void nf_tables_bridge_exit_net(struct net *net) +{ +	nft_unregister_afinfo(net->nft.bridge); +	kfree(net->nft.bridge); +} + +static struct pernet_operations nf_tables_bridge_net_ops = { +	.init	= nf_tables_bridge_init_net, +	.exit	= nf_tables_bridge_exit_net, +}; + +static const struct nf_chain_type filter_bridge = { +	.name		= "filter", +	.type		= NFT_CHAIN_T_DEFAULT, +	.family		= NFPROTO_BRIDGE, +	.owner		= THIS_MODULE, +	.hook_mask	= (1 << NF_BR_LOCAL_IN) | +			  (1 << NF_BR_FORWARD) | +			  (1 << NF_BR_LOCAL_OUT), +}; + +static int __init nf_tables_bridge_init(void) +{ +	int ret; + +	nft_register_chain_type(&filter_bridge); +	ret = register_pernet_subsys(&nf_tables_bridge_net_ops); +	if (ret < 0) +		nft_unregister_chain_type(&filter_bridge); + +	return ret; +} + +static void __exit nf_tables_bridge_exit(void) +{ +	unregister_pernet_subsys(&nf_tables_bridge_net_ops); +	nft_unregister_chain_type(&filter_bridge); +} + +module_init(nf_tables_bridge_init); +module_exit(nf_tables_bridge_exit); + +MODULE_LICENSE("GPL"); +MODULE_AUTHOR("Patrick McHardy <kaber@trash.net>"); +MODULE_ALIAS_NFT_FAMILY(AF_BRIDGE); diff --git a/net/bridge/netfilter/nft_meta_bridge.c b/net/bridge/netfilter/nft_meta_bridge.c new file mode 100644 index 00000000000..4f02109d708 --- /dev/null +++ b/net/bridge/netfilter/nft_meta_bridge.c @@ -0,0 +1,139 @@ +/* + * Copyright (c) 2014 Intel Corporation + * + * This program is free software; you can redistribute it and/or modify + * it under the terms of the GNU General Public License version 2 as + * published by the Free Software Foundation. + * + */ + +#include <linux/kernel.h> +#include <linux/init.h> +#include <linux/module.h> +#include <linux/netlink.h> +#include <linux/netfilter.h> +#include <linux/netfilter/nf_tables.h> +#include <net/netfilter/nf_tables.h> +#include <net/netfilter/nft_meta.h> + +#include "../br_private.h" + +static void nft_meta_bridge_get_eval(const struct nft_expr *expr, +				     struct nft_data data[NFT_REG_MAX + 1], +				     const struct nft_pktinfo *pkt) +{ +	const struct nft_meta *priv = nft_expr_priv(expr); +	const struct net_device *in = pkt->in, *out = pkt->out; +	struct nft_data *dest = &data[priv->dreg]; +	const struct net_bridge_port *p; + +	switch (priv->key) { +	case NFT_META_BRI_IIFNAME: +		if (in == NULL || (p = br_port_get_rcu(in)) == NULL) +			goto err; +		break; +	case NFT_META_BRI_OIFNAME: +		if (out == NULL || (p = br_port_get_rcu(out)) == NULL) +			goto err; +		break; +	default: +		goto out; +	} + +	strncpy((char *)dest->data, p->br->dev->name, sizeof(dest->data)); +	return; +out: +	return nft_meta_get_eval(expr, data, pkt); +err: +	data[NFT_REG_VERDICT].verdict = NFT_BREAK; +} + +static int nft_meta_bridge_get_init(const struct nft_ctx *ctx, +				    const struct nft_expr *expr, +				    const struct nlattr * const tb[]) +{ +	struct nft_meta *priv = nft_expr_priv(expr); +	int err; + +	priv->key = ntohl(nla_get_be32(tb[NFTA_META_KEY])); +	switch (priv->key) { +	case NFT_META_BRI_IIFNAME: +	case NFT_META_BRI_OIFNAME: +		break; +	default: +		return nft_meta_get_init(ctx, expr, tb); +	} + +	priv->dreg = ntohl(nla_get_be32(tb[NFTA_META_DREG])); +	err = nft_validate_output_register(priv->dreg); +	if (err < 0) +		return err; + +	err = nft_validate_data_load(ctx, priv->dreg, NULL, NFT_DATA_VALUE); +	if (err < 0) +		return err; + +	return 0; +} + +static struct nft_expr_type nft_meta_bridge_type; +static const struct nft_expr_ops nft_meta_bridge_get_ops = { +	.type		= &nft_meta_bridge_type, +	.size		= NFT_EXPR_SIZE(sizeof(struct nft_meta)), +	.eval		= nft_meta_bridge_get_eval, +	.init		= nft_meta_bridge_get_init, +	.dump		= nft_meta_get_dump, +}; + +static const struct nft_expr_ops nft_meta_bridge_set_ops = { +	.type		= &nft_meta_bridge_type, +	.size		= NFT_EXPR_SIZE(sizeof(struct nft_meta)), +	.eval		= nft_meta_set_eval, +	.init		= nft_meta_set_init, +	.dump		= nft_meta_set_dump, +}; + +static const struct nft_expr_ops * +nft_meta_bridge_select_ops(const struct nft_ctx *ctx, +			   const struct nlattr * const tb[]) +{ +	if (tb[NFTA_META_KEY] == NULL) +		return ERR_PTR(-EINVAL); + +	if (tb[NFTA_META_DREG] && tb[NFTA_META_SREG]) +		return ERR_PTR(-EINVAL); + +	if (tb[NFTA_META_DREG]) +		return &nft_meta_bridge_get_ops; + +	if (tb[NFTA_META_SREG]) +		return &nft_meta_bridge_set_ops; + +	return ERR_PTR(-EINVAL); +} + +static struct nft_expr_type nft_meta_bridge_type __read_mostly = { +	.family         = NFPROTO_BRIDGE, +	.name           = "meta", +	.select_ops     = &nft_meta_bridge_select_ops, +	.policy         = nft_meta_policy, +	.maxattr        = NFTA_META_MAX, +	.owner          = THIS_MODULE, +}; + +static int __init nft_meta_bridge_module_init(void) +{ +	return nft_register_expr(&nft_meta_bridge_type); +} + +static void __exit nft_meta_bridge_module_exit(void) +{ +	nft_unregister_expr(&nft_meta_bridge_type); +} + +module_init(nft_meta_bridge_module_init); +module_exit(nft_meta_bridge_module_exit); + +MODULE_LICENSE("GPL"); +MODULE_AUTHOR("Tomasz Bursztyka <tomasz.bursztyka@linux.intel.com>"); +MODULE_ALIAS_NFT_AF_EXPR(AF_BRIDGE, "meta");  | 
