1d2912cb1SThomas Gleixner // SPDX-License-Identifier: GPL-2.0-only
21c5ba67dSPablo Neira Ayuso /* Copyright (C) 2014 Jozsef Kadlecsik <kadlec@netfilter.org> */
307034aeaSJozsef Kadlecsik 
407034aeaSJozsef Kadlecsik /* Kernel module implementing an IP set type: the hash:mac type */
507034aeaSJozsef Kadlecsik 
607034aeaSJozsef Kadlecsik #include <linux/jhash.h>
707034aeaSJozsef Kadlecsik #include <linux/module.h>
807034aeaSJozsef Kadlecsik #include <linux/etherdevice.h>
907034aeaSJozsef Kadlecsik #include <linux/skbuff.h>
1007034aeaSJozsef Kadlecsik #include <linux/errno.h>
1107034aeaSJozsef Kadlecsik #include <linux/if_ether.h>
1207034aeaSJozsef Kadlecsik #include <net/netlink.h>
1307034aeaSJozsef Kadlecsik 
1407034aeaSJozsef Kadlecsik #include <linux/netfilter.h>
1507034aeaSJozsef Kadlecsik #include <linux/netfilter/ipset/ip_set.h>
1607034aeaSJozsef Kadlecsik #include <linux/netfilter/ipset/ip_set_hash.h>
1707034aeaSJozsef Kadlecsik 
1807034aeaSJozsef Kadlecsik #define IPSET_TYPE_REV_MIN	0
19*3976ca10SJozsef Kadlecsik #define IPSET_TYPE_REV_MAX	1	/* bucketsize, initval support */
2007034aeaSJozsef Kadlecsik 
2107034aeaSJozsef Kadlecsik MODULE_LICENSE("GPL");
22fe03d474SJozsef Kadlecsik MODULE_AUTHOR("Jozsef Kadlecsik <kadlec@netfilter.org>");
2307034aeaSJozsef Kadlecsik IP_SET_MODULE_DESC("hash:mac", IPSET_TYPE_REV_MIN, IPSET_TYPE_REV_MAX);
2407034aeaSJozsef Kadlecsik MODULE_ALIAS("ip_set_hash:mac");
2507034aeaSJozsef Kadlecsik 
2607034aeaSJozsef Kadlecsik /* Type specific function prefix */
2707034aeaSJozsef Kadlecsik #define HTYPE		hash_mac
2807034aeaSJozsef Kadlecsik 
2907034aeaSJozsef Kadlecsik /* Member elements */
3007034aeaSJozsef Kadlecsik struct hash_mac4_elem {
3107034aeaSJozsef Kadlecsik 	/* Zero valued IP addresses cannot be stored */
3207034aeaSJozsef Kadlecsik 	union {
3307034aeaSJozsef Kadlecsik 		unsigned char ether[ETH_ALEN];
3407034aeaSJozsef Kadlecsik 		__be32 foo[2];
3507034aeaSJozsef Kadlecsik 	};
3607034aeaSJozsef Kadlecsik };
3707034aeaSJozsef Kadlecsik 
3807034aeaSJozsef Kadlecsik /* Common functions */
3907034aeaSJozsef Kadlecsik 
408dea982aSJeremy Sowden static bool
hash_mac4_data_equal(const struct hash_mac4_elem * e1,const struct hash_mac4_elem * e2,u32 * multi)4107034aeaSJozsef Kadlecsik hash_mac4_data_equal(const struct hash_mac4_elem *e1,
4207034aeaSJozsef Kadlecsik 		     const struct hash_mac4_elem *e2,
4307034aeaSJozsef Kadlecsik 		     u32 *multi)
4407034aeaSJozsef Kadlecsik {
4507034aeaSJozsef Kadlecsik 	return ether_addr_equal(e1->ether, e2->ether);
4607034aeaSJozsef Kadlecsik }
4707034aeaSJozsef Kadlecsik 
488dea982aSJeremy Sowden static bool
hash_mac4_data_list(struct sk_buff * skb,const struct hash_mac4_elem * e)4907034aeaSJozsef Kadlecsik hash_mac4_data_list(struct sk_buff *skb, const struct hash_mac4_elem *e)
5007034aeaSJozsef Kadlecsik {
51728a7e69SSergey Popovich 	if (nla_put(skb, IPSET_ATTR_ETHER, ETH_ALEN, e->ether))
52728a7e69SSergey Popovich 		goto nla_put_failure;
53728a7e69SSergey Popovich 	return false;
54728a7e69SSergey Popovich 
55728a7e69SSergey Popovich nla_put_failure:
56728a7e69SSergey Popovich 	return true;
5707034aeaSJozsef Kadlecsik }
5807034aeaSJozsef Kadlecsik 
598dea982aSJeremy Sowden static void
hash_mac4_data_next(struct hash_mac4_elem * next,const struct hash_mac4_elem * e)6007034aeaSJozsef Kadlecsik hash_mac4_data_next(struct hash_mac4_elem *next,
6107034aeaSJozsef Kadlecsik 		    const struct hash_mac4_elem *e)
6207034aeaSJozsef Kadlecsik {
6307034aeaSJozsef Kadlecsik }
6407034aeaSJozsef Kadlecsik 
6507034aeaSJozsef Kadlecsik #define MTYPE		hash_mac4
6607034aeaSJozsef Kadlecsik #define HOST_MASK	32
6707034aeaSJozsef Kadlecsik #define IP_SET_EMIT_CREATE
6807034aeaSJozsef Kadlecsik #define IP_SET_PROTO_UNDEF
6907034aeaSJozsef Kadlecsik #include "ip_set_hash_gen.h"
7007034aeaSJozsef Kadlecsik 
7107034aeaSJozsef Kadlecsik static int
hash_mac4_kadt(struct ip_set * set,const struct sk_buff * skb,const struct xt_action_param * par,enum ipset_adt adt,struct ip_set_adt_opt * opt)7207034aeaSJozsef Kadlecsik hash_mac4_kadt(struct ip_set *set, const struct sk_buff *skb,
7307034aeaSJozsef Kadlecsik 	       const struct xt_action_param *par,
7407034aeaSJozsef Kadlecsik 	       enum ipset_adt adt, struct ip_set_adt_opt *opt)
7507034aeaSJozsef Kadlecsik {
7607034aeaSJozsef Kadlecsik 	ipset_adtfn adtfn = set->variant->adt[adt];
7707034aeaSJozsef Kadlecsik 	struct hash_mac4_elem e = { { .foo[0] = 0, .foo[1] = 0 } };
7807034aeaSJozsef Kadlecsik 	struct ip_set_ext ext = IP_SET_INIT_KEXT(skb, opt, set);
7907034aeaSJozsef Kadlecsik 
8007034aeaSJozsef Kadlecsik 	if (skb_mac_header(skb) < skb->head ||
8107034aeaSJozsef Kadlecsik 	    (skb_mac_header(skb) + ETH_HLEN) > skb->data)
8207034aeaSJozsef Kadlecsik 		return -EINVAL;
8307034aeaSJozsef Kadlecsik 
848cc4ccf5SStefano Brivio 	if (opt->flags & IPSET_DIM_ONE_SRC)
85ca0f6a5cSJozsef Kadlecsik 		ether_addr_copy(e.ether, eth_hdr(skb)->h_source);
868cc4ccf5SStefano Brivio 	else
878cc4ccf5SStefano Brivio 		ether_addr_copy(e.ether, eth_hdr(skb)->h_dest);
888cc4ccf5SStefano Brivio 
8926c97c5dSJoe Perches 	if (is_zero_ether_addr(e.ether))
9007034aeaSJozsef Kadlecsik 		return -EINVAL;
9107034aeaSJozsef Kadlecsik 	return adtfn(set, &e, &ext, &opt->ext, opt->cmdflags);
9207034aeaSJozsef Kadlecsik }
9307034aeaSJozsef Kadlecsik 
9407034aeaSJozsef Kadlecsik static int
hash_mac4_uadt(struct ip_set * set,struct nlattr * tb[],enum ipset_adt adt,u32 * lineno,u32 flags,bool retried)9507034aeaSJozsef Kadlecsik hash_mac4_uadt(struct ip_set *set, struct nlattr *tb[],
9607034aeaSJozsef Kadlecsik 	       enum ipset_adt adt, u32 *lineno, u32 flags, bool retried)
9707034aeaSJozsef Kadlecsik {
9807034aeaSJozsef Kadlecsik 	ipset_adtfn adtfn = set->variant->adt[adt];
9907034aeaSJozsef Kadlecsik 	struct hash_mac4_elem e = { { .foo[0] = 0, .foo[1] = 0 } };
10007034aeaSJozsef Kadlecsik 	struct ip_set_ext ext = IP_SET_INIT_UEXT(set);
10107034aeaSJozsef Kadlecsik 	int ret;
10207034aeaSJozsef Kadlecsik 
10307034aeaSJozsef Kadlecsik 	if (tb[IPSET_ATTR_LINENO])
10407034aeaSJozsef Kadlecsik 		*lineno = nla_get_u32(tb[IPSET_ATTR_LINENO]);
10507034aeaSJozsef Kadlecsik 
106d8aacd87SJozsef Kadlecsik 	if (unlikely(!tb[IPSET_ATTR_ETHER] ||
107d8aacd87SJozsef Kadlecsik 		     nla_len(tb[IPSET_ATTR_ETHER]) != ETH_ALEN))
108a212e08eSSergey Popovich 		return -IPSET_ERR_PROTOCOL;
109a212e08eSSergey Popovich 
11007034aeaSJozsef Kadlecsik 	ret = ip_set_get_extensions(set, tb, &ext);
11107034aeaSJozsef Kadlecsik 	if (ret)
11207034aeaSJozsef Kadlecsik 		return ret;
113ca0f6a5cSJozsef Kadlecsik 	ether_addr_copy(e.ether, nla_data(tb[IPSET_ATTR_ETHER]));
11426c97c5dSJoe Perches 	if (is_zero_ether_addr(e.ether))
11507034aeaSJozsef Kadlecsik 		return -IPSET_ERR_HASH_ELEM;
11607034aeaSJozsef Kadlecsik 
11707034aeaSJozsef Kadlecsik 	return adtfn(set, &e, &ext, &ext, flags);
11807034aeaSJozsef Kadlecsik }
11907034aeaSJozsef Kadlecsik 
12007034aeaSJozsef Kadlecsik static struct ip_set_type hash_mac_type __read_mostly = {
12107034aeaSJozsef Kadlecsik 	.name		= "hash:mac",
12207034aeaSJozsef Kadlecsik 	.protocol	= IPSET_PROTOCOL,
12307034aeaSJozsef Kadlecsik 	.features	= IPSET_TYPE_MAC,
12407034aeaSJozsef Kadlecsik 	.dimension	= IPSET_DIM_ONE,
12507034aeaSJozsef Kadlecsik 	.family		= NFPROTO_UNSPEC,
12607034aeaSJozsef Kadlecsik 	.revision_min	= IPSET_TYPE_REV_MIN,
12707034aeaSJozsef Kadlecsik 	.revision_max	= IPSET_TYPE_REV_MAX,
128ccf0a4b7SJozsef Kadlecsik 	.create_flags[IPSET_TYPE_REV_MAX] = IPSET_CREATE_FLAG_BUCKETSIZE,
12907034aeaSJozsef Kadlecsik 	.create		= hash_mac_create,
13007034aeaSJozsef Kadlecsik 	.create_policy	= {
13107034aeaSJozsef Kadlecsik 		[IPSET_ATTR_HASHSIZE]	= { .type = NLA_U32 },
13207034aeaSJozsef Kadlecsik 		[IPSET_ATTR_MAXELEM]	= { .type = NLA_U32 },
133*3976ca10SJozsef Kadlecsik 		[IPSET_ATTR_INITVAL]	= { .type = NLA_U32 },
134ccf0a4b7SJozsef Kadlecsik 		[IPSET_ATTR_BUCKETSIZE]	= { .type = NLA_U8 },
13507034aeaSJozsef Kadlecsik 		[IPSET_ATTR_RESIZE]	= { .type = NLA_U8  },
13607034aeaSJozsef Kadlecsik 		[IPSET_ATTR_TIMEOUT]	= { .type = NLA_U32 },
13707034aeaSJozsef Kadlecsik 		[IPSET_ATTR_CADT_FLAGS]	= { .type = NLA_U32 },
13807034aeaSJozsef Kadlecsik 	},
13907034aeaSJozsef Kadlecsik 	.adt_policy	= {
14007034aeaSJozsef Kadlecsik 		[IPSET_ATTR_ETHER]	= { .type = NLA_BINARY,
14107034aeaSJozsef Kadlecsik 					    .len  = ETH_ALEN },
14207034aeaSJozsef Kadlecsik 		[IPSET_ATTR_TIMEOUT]	= { .type = NLA_U32 },
14307034aeaSJozsef Kadlecsik 		[IPSET_ATTR_LINENO]	= { .type = NLA_U32 },
14407034aeaSJozsef Kadlecsik 		[IPSET_ATTR_BYTES]	= { .type = NLA_U64 },
14507034aeaSJozsef Kadlecsik 		[IPSET_ATTR_PACKETS]	= { .type = NLA_U64 },
14603726186SSergey Popovich 		[IPSET_ATTR_COMMENT]	= { .type = NLA_NUL_STRING,
14703726186SSergey Popovich 					    .len  = IPSET_MAX_COMMENT_SIZE },
14807034aeaSJozsef Kadlecsik 		[IPSET_ATTR_SKBMARK]	= { .type = NLA_U64 },
14907034aeaSJozsef Kadlecsik 		[IPSET_ATTR_SKBPRIO]	= { .type = NLA_U32 },
15007034aeaSJozsef Kadlecsik 		[IPSET_ATTR_SKBQUEUE]	= { .type = NLA_U16 },
15107034aeaSJozsef Kadlecsik 	},
15207034aeaSJozsef Kadlecsik 	.me		= THIS_MODULE,
15307034aeaSJozsef Kadlecsik };
15407034aeaSJozsef Kadlecsik 
15507034aeaSJozsef Kadlecsik static int __init
hash_mac_init(void)15607034aeaSJozsef Kadlecsik hash_mac_init(void)
15707034aeaSJozsef Kadlecsik {
15807034aeaSJozsef Kadlecsik 	return ip_set_type_register(&hash_mac_type);
15907034aeaSJozsef Kadlecsik }
16007034aeaSJozsef Kadlecsik 
16107034aeaSJozsef Kadlecsik static void __exit
hash_mac_fini(void)16207034aeaSJozsef Kadlecsik hash_mac_fini(void)
16307034aeaSJozsef Kadlecsik {
16418f84d41SJozsef Kadlecsik 	rcu_barrier();
16507034aeaSJozsef Kadlecsik 	ip_set_type_unregister(&hash_mac_type);
16607034aeaSJozsef Kadlecsik }
16707034aeaSJozsef Kadlecsik 
16807034aeaSJozsef Kadlecsik module_init(hash_mac_init);
16907034aeaSJozsef Kadlecsik module_exit(hash_mac_fini);
170