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