Commit | Line | Data |
---|---|---|
07034aea JK |
1 | /* Copyright (C) 2014 Jozsef Kadlecsik <kadlec@blackhole.kfki.hu> |
2 | * | |
3 | * This program is free software; you can redistribute it and/or modify | |
4 | * it under the terms of the GNU General Public License version 2 as | |
5 | * published by the Free Software Foundation. | |
6 | */ | |
7 | ||
8 | /* Kernel module implementing an IP set type: the hash:mac type */ | |
9 | ||
10 | #include <linux/jhash.h> | |
11 | #include <linux/module.h> | |
12 | #include <linux/etherdevice.h> | |
13 | #include <linux/skbuff.h> | |
14 | #include <linux/errno.h> | |
15 | #include <linux/if_ether.h> | |
16 | #include <net/netlink.h> | |
17 | ||
18 | #include <linux/netfilter.h> | |
19 | #include <linux/netfilter/ipset/ip_set.h> | |
20 | #include <linux/netfilter/ipset/ip_set_hash.h> | |
21 | ||
22 | #define IPSET_TYPE_REV_MIN 0 | |
23 | #define IPSET_TYPE_REV_MAX 0 | |
24 | ||
25 | MODULE_LICENSE("GPL"); | |
26 | MODULE_AUTHOR("Jozsef Kadlecsik <kadlec@blackhole.kfki.hu>"); | |
27 | IP_SET_MODULE_DESC("hash:mac", IPSET_TYPE_REV_MIN, IPSET_TYPE_REV_MAX); | |
28 | MODULE_ALIAS("ip_set_hash:mac"); | |
29 | ||
30 | /* Type specific function prefix */ | |
31 | #define HTYPE hash_mac | |
32 | ||
33 | /* Member elements */ | |
34 | struct hash_mac4_elem { | |
35 | /* Zero valued IP addresses cannot be stored */ | |
36 | union { | |
37 | unsigned char ether[ETH_ALEN]; | |
38 | __be32 foo[2]; | |
39 | }; | |
40 | }; | |
41 | ||
42 | /* Common functions */ | |
43 | ||
44 | static inline bool | |
45 | hash_mac4_data_equal(const struct hash_mac4_elem *e1, | |
46 | const struct hash_mac4_elem *e2, | |
47 | u32 *multi) | |
48 | { | |
49 | return ether_addr_equal(e1->ether, e2->ether); | |
50 | } | |
51 | ||
52 | static inline bool | |
53 | hash_mac4_data_list(struct sk_buff *skb, const struct hash_mac4_elem *e) | |
54 | { | |
728a7e69 SP |
55 | if (nla_put(skb, IPSET_ATTR_ETHER, ETH_ALEN, e->ether)) |
56 | goto nla_put_failure; | |
57 | return false; | |
58 | ||
59 | nla_put_failure: | |
60 | return true; | |
07034aea JK |
61 | } |
62 | ||
63 | static inline void | |
64 | hash_mac4_data_next(struct hash_mac4_elem *next, | |
65 | const struct hash_mac4_elem *e) | |
66 | { | |
67 | } | |
68 | ||
69 | #define MTYPE hash_mac4 | |
07034aea JK |
70 | #define HOST_MASK 32 |
71 | #define IP_SET_EMIT_CREATE | |
72 | #define IP_SET_PROTO_UNDEF | |
73 | #include "ip_set_hash_gen.h" | |
74 | ||
75 | /* Zero valued element is not supported */ | |
76 | static const unsigned char invalid_ether[ETH_ALEN] = { 0 }; | |
77 | ||
78 | static int | |
79 | hash_mac4_kadt(struct ip_set *set, const struct sk_buff *skb, | |
80 | const struct xt_action_param *par, | |
81 | enum ipset_adt adt, struct ip_set_adt_opt *opt) | |
82 | { | |
83 | ipset_adtfn adtfn = set->variant->adt[adt]; | |
84 | struct hash_mac4_elem e = { { .foo[0] = 0, .foo[1] = 0 } }; | |
85 | struct ip_set_ext ext = IP_SET_INIT_KEXT(skb, opt, set); | |
86 | ||
87 | /* MAC can be src only */ | |
88 | if (!(opt->flags & IPSET_DIM_ONE_SRC)) | |
89 | return 0; | |
90 | ||
91 | if (skb_mac_header(skb) < skb->head || | |
ca0f6a5c | 92 | (skb_mac_header(skb) + ETH_HLEN) > skb->data) |
07034aea JK |
93 | return -EINVAL; |
94 | ||
ca0f6a5c | 95 | ether_addr_copy(e.ether, eth_hdr(skb)->h_source); |
07034aea JK |
96 | if (memcmp(e.ether, invalid_ether, ETH_ALEN) == 0) |
97 | return -EINVAL; | |
98 | return adtfn(set, &e, &ext, &opt->ext, opt->cmdflags); | |
99 | } | |
100 | ||
101 | static int | |
102 | hash_mac4_uadt(struct ip_set *set, struct nlattr *tb[], | |
103 | enum ipset_adt adt, u32 *lineno, u32 flags, bool retried) | |
104 | { | |
105 | ipset_adtfn adtfn = set->variant->adt[adt]; | |
106 | struct hash_mac4_elem e = { { .foo[0] = 0, .foo[1] = 0 } }; | |
107 | struct ip_set_ext ext = IP_SET_INIT_UEXT(set); | |
108 | int ret; | |
109 | ||
07034aea JK |
110 | if (tb[IPSET_ATTR_LINENO]) |
111 | *lineno = nla_get_u32(tb[IPSET_ATTR_LINENO]); | |
112 | ||
d8aacd87 JK |
113 | if (unlikely(!tb[IPSET_ATTR_ETHER] || |
114 | nla_len(tb[IPSET_ATTR_ETHER]) != ETH_ALEN)) | |
a212e08e SP |
115 | return -IPSET_ERR_PROTOCOL; |
116 | ||
07034aea JK |
117 | ret = ip_set_get_extensions(set, tb, &ext); |
118 | if (ret) | |
119 | return ret; | |
ca0f6a5c | 120 | ether_addr_copy(e.ether, nla_data(tb[IPSET_ATTR_ETHER])); |
07034aea JK |
121 | if (memcmp(e.ether, invalid_ether, ETH_ALEN) == 0) |
122 | return -IPSET_ERR_HASH_ELEM; | |
123 | ||
124 | return adtfn(set, &e, &ext, &ext, flags); | |
125 | } | |
126 | ||
127 | static struct ip_set_type hash_mac_type __read_mostly = { | |
128 | .name = "hash:mac", | |
129 | .protocol = IPSET_PROTOCOL, | |
130 | .features = IPSET_TYPE_MAC, | |
131 | .dimension = IPSET_DIM_ONE, | |
132 | .family = NFPROTO_UNSPEC, | |
133 | .revision_min = IPSET_TYPE_REV_MIN, | |
134 | .revision_max = IPSET_TYPE_REV_MAX, | |
135 | .create = hash_mac_create, | |
136 | .create_policy = { | |
137 | [IPSET_ATTR_HASHSIZE] = { .type = NLA_U32 }, | |
138 | [IPSET_ATTR_MAXELEM] = { .type = NLA_U32 }, | |
139 | [IPSET_ATTR_PROBES] = { .type = NLA_U8 }, | |
140 | [IPSET_ATTR_RESIZE] = { .type = NLA_U8 }, | |
141 | [IPSET_ATTR_TIMEOUT] = { .type = NLA_U32 }, | |
142 | [IPSET_ATTR_CADT_FLAGS] = { .type = NLA_U32 }, | |
143 | }, | |
144 | .adt_policy = { | |
145 | [IPSET_ATTR_ETHER] = { .type = NLA_BINARY, | |
146 | .len = ETH_ALEN }, | |
147 | [IPSET_ATTR_TIMEOUT] = { .type = NLA_U32 }, | |
148 | [IPSET_ATTR_LINENO] = { .type = NLA_U32 }, | |
149 | [IPSET_ATTR_BYTES] = { .type = NLA_U64 }, | |
150 | [IPSET_ATTR_PACKETS] = { .type = NLA_U64 }, | |
03726186 SP |
151 | [IPSET_ATTR_COMMENT] = { .type = NLA_NUL_STRING, |
152 | .len = IPSET_MAX_COMMENT_SIZE }, | |
07034aea JK |
153 | [IPSET_ATTR_SKBMARK] = { .type = NLA_U64 }, |
154 | [IPSET_ATTR_SKBPRIO] = { .type = NLA_U32 }, | |
155 | [IPSET_ATTR_SKBQUEUE] = { .type = NLA_U16 }, | |
156 | }, | |
157 | .me = THIS_MODULE, | |
158 | }; | |
159 | ||
160 | static int __init | |
161 | hash_mac_init(void) | |
162 | { | |
163 | return ip_set_type_register(&hash_mac_type); | |
164 | } | |
165 | ||
166 | static void __exit | |
167 | hash_mac_fini(void) | |
168 | { | |
18f84d41 | 169 | rcu_barrier(); |
07034aea JK |
170 | ip_set_type_unregister(&hash_mac_type); |
171 | } | |
172 | ||
173 | module_init(hash_mac_init); | |
174 | module_exit(hash_mac_fini); |