Book a Demo!
CoCalc Logo Icon
StoreFeaturesDocsShareSupportNewsAboutPoliciesSign UpSign In
freebsd
GitHub Repository: freebsd/freebsd-src
Path: blob/main/sys/net80211/ieee80211_acl.c
39475 views
1
/*-
2
* SPDX-License-Identifier: BSD-2-Clause
3
*
4
* Copyright (c) 2004-2008 Sam Leffler, Errno Consulting
5
* All rights reserved.
6
*
7
* Redistribution and use in source and binary forms, with or without
8
* modification, are permitted provided that the following conditions
9
* are met:
10
* 1. Redistributions of source code must retain the above copyright
11
* notice, this list of conditions and the following disclaimer.
12
* 2. Redistributions in binary form must reproduce the above copyright
13
* notice, this list of conditions and the following disclaimer in the
14
* documentation and/or other materials provided with the distribution.
15
*
16
* THIS SOFTWARE IS PROVIDED BY THE AUTHOR ``AS IS'' AND ANY EXPRESS OR
17
* IMPLIED WARRANTIES, INCLUDING, BUT NOT LIMITED TO, THE IMPLIED WARRANTIES
18
* OF MERCHANTABILITY AND FITNESS FOR A PARTICULAR PURPOSE ARE DISCLAIMED.
19
* IN NO EVENT SHALL THE AUTHOR BE LIABLE FOR ANY DIRECT, INDIRECT,
20
* INCIDENTAL, SPECIAL, EXEMPLARY, OR CONSEQUENTIAL DAMAGES (INCLUDING, BUT
21
* NOT LIMITED TO, PROCUREMENT OF SUBSTITUTE GOODS OR SERVICES; LOSS OF USE,
22
* DATA, OR PROFITS; OR BUSINESS INTERRUPTION) HOWEVER CAUSED AND ON ANY
23
* THEORY OF LIABILITY, WHETHER IN CONTRACT, STRICT LIABILITY, OR TORT
24
* (INCLUDING NEGLIGENCE OR OTHERWISE) ARISING IN ANY WAY OUT OF THE USE OF
25
* THIS SOFTWARE, EVEN IF ADVISED OF THE POSSIBILITY OF SUCH DAMAGE.
26
*/
27
28
#include <sys/cdefs.h>
29
/*
30
* IEEE 802.11 MAC ACL support.
31
*
32
* When this module is loaded the sender address of each auth mgt
33
* frame is passed to the iac_check method and the module indicates
34
* if the frame should be accepted or rejected. If the policy is
35
* set to ACL_POLICY_OPEN then all frames are accepted w/o checking
36
* the address. Otherwise, the address is looked up in the database
37
* and if found the frame is either accepted (ACL_POLICY_ALLOW)
38
* or rejected (ACL_POLICY_DENT).
39
*/
40
#include "opt_wlan.h"
41
42
#include <sys/param.h>
43
#include <sys/kernel.h>
44
#include <sys/systm.h>
45
#include <sys/malloc.h>
46
#include <sys/mbuf.h>
47
#include <sys/module.h>
48
#include <sys/queue.h>
49
50
#include <sys/socket.h>
51
52
#include <net/if.h>
53
#include <net/if_media.h>
54
#include <net/ethernet.h>
55
#include <net/route.h>
56
57
#include <net80211/ieee80211_var.h>
58
59
enum {
60
ACL_POLICY_OPEN = 0, /* open, don't check ACL's */
61
ACL_POLICY_ALLOW = 1, /* allow traffic from MAC */
62
ACL_POLICY_DENY = 2, /* deny traffic from MAC */
63
/*
64
* NB: ACL_POLICY_RADIUS must be the same value as
65
* IEEE80211_MACCMD_POLICY_RADIUS because of the way
66
* acl_getpolicy() works.
67
*/
68
ACL_POLICY_RADIUS = 7, /* defer to RADIUS ACL server */
69
};
70
71
#define ACL_HASHSIZE 32
72
73
struct acl {
74
TAILQ_ENTRY(acl) acl_list;
75
LIST_ENTRY(acl) acl_hash;
76
uint8_t acl_macaddr[IEEE80211_ADDR_LEN];
77
};
78
struct aclstate {
79
acl_lock_t as_lock;
80
int as_policy;
81
uint32_t as_nacls;
82
TAILQ_HEAD(, acl) as_list; /* list of all ACL's */
83
LIST_HEAD(, acl) as_hash[ACL_HASHSIZE];
84
struct ieee80211vap *as_vap;
85
};
86
87
/* simple hash is enough for variation of macaddr */
88
#define ACL_HASH(addr) \
89
(((const uint8_t *)(addr))[IEEE80211_ADDR_LEN - 1] % ACL_HASHSIZE)
90
91
static MALLOC_DEFINE(M_80211_ACL, "acl", "802.11 station acl");
92
93
static int acl_free_all(struct ieee80211vap *);
94
95
/* number of references from net80211 layer */
96
static int nrefs = 0;
97
98
static int
99
acl_attach(struct ieee80211vap *vap)
100
{
101
struct aclstate *as;
102
103
as = (struct aclstate *) IEEE80211_MALLOC(sizeof(struct aclstate),
104
M_80211_ACL, IEEE80211_M_NOWAIT | IEEE80211_M_ZERO);
105
if (as == NULL)
106
return 0;
107
ACL_LOCK_INIT(as, "acl");
108
TAILQ_INIT(&as->as_list);
109
as->as_policy = ACL_POLICY_OPEN;
110
as->as_vap = vap;
111
vap->iv_as = as;
112
nrefs++; /* NB: we assume caller locking */
113
return 1;
114
}
115
116
static void
117
acl_detach(struct ieee80211vap *vap)
118
{
119
struct aclstate *as = vap->iv_as;
120
121
KASSERT(nrefs > 0, ("imbalanced attach/detach"));
122
nrefs--; /* NB: we assume caller locking */
123
124
acl_free_all(vap);
125
vap->iv_as = NULL;
126
ACL_LOCK_DESTROY(as);
127
IEEE80211_FREE(as, M_80211_ACL);
128
}
129
130
static __inline struct acl *
131
_find_acl(struct aclstate *as, const uint8_t *macaddr)
132
{
133
struct acl *acl;
134
int hash;
135
136
hash = ACL_HASH(macaddr);
137
LIST_FOREACH(acl, &as->as_hash[hash], acl_hash) {
138
if (IEEE80211_ADDR_EQ(acl->acl_macaddr, macaddr))
139
return acl;
140
}
141
return NULL;
142
}
143
144
static void
145
_acl_free(struct aclstate *as, struct acl *acl)
146
{
147
ACL_LOCK_ASSERT(as);
148
149
TAILQ_REMOVE(&as->as_list, acl, acl_list);
150
LIST_REMOVE(acl, acl_hash);
151
IEEE80211_FREE(acl, M_80211_ACL);
152
as->as_nacls--;
153
}
154
155
static int
156
acl_check(struct ieee80211vap *vap, const struct ieee80211_frame *wh)
157
{
158
struct aclstate *as = vap->iv_as;
159
160
switch (as->as_policy) {
161
case ACL_POLICY_OPEN:
162
case ACL_POLICY_RADIUS:
163
return 1;
164
case ACL_POLICY_ALLOW:
165
return _find_acl(as, wh->i_addr2) != NULL;
166
case ACL_POLICY_DENY:
167
return _find_acl(as, wh->i_addr2) == NULL;
168
}
169
return 0; /* should not happen */
170
}
171
172
static int
173
acl_add(struct ieee80211vap *vap, const uint8_t mac[IEEE80211_ADDR_LEN])
174
{
175
struct aclstate *as = vap->iv_as;
176
struct acl *acl, *new;
177
int hash;
178
179
new = (struct acl *) IEEE80211_MALLOC(sizeof(struct acl),
180
M_80211_ACL, IEEE80211_M_NOWAIT | IEEE80211_M_ZERO);
181
if (new == NULL) {
182
IEEE80211_DPRINTF(vap, IEEE80211_MSG_ACL,
183
"ACL: add %s failed, no memory\n", ether_sprintf(mac));
184
/* XXX statistic */
185
return ENOMEM;
186
}
187
188
ACL_LOCK(as);
189
hash = ACL_HASH(mac);
190
LIST_FOREACH(acl, &as->as_hash[hash], acl_hash) {
191
if (IEEE80211_ADDR_EQ(acl->acl_macaddr, mac)) {
192
ACL_UNLOCK(as);
193
IEEE80211_FREE(new, M_80211_ACL);
194
IEEE80211_DPRINTF(vap, IEEE80211_MSG_ACL,
195
"ACL: add %s failed, already present\n",
196
ether_sprintf(mac));
197
return EEXIST;
198
}
199
}
200
IEEE80211_ADDR_COPY(new->acl_macaddr, mac);
201
TAILQ_INSERT_TAIL(&as->as_list, new, acl_list);
202
LIST_INSERT_HEAD(&as->as_hash[hash], new, acl_hash);
203
as->as_nacls++;
204
ACL_UNLOCK(as);
205
206
IEEE80211_DPRINTF(vap, IEEE80211_MSG_ACL,
207
"ACL: add %s\n", ether_sprintf(mac));
208
return 0;
209
}
210
211
static int
212
acl_remove(struct ieee80211vap *vap, const uint8_t mac[IEEE80211_ADDR_LEN])
213
{
214
struct aclstate *as = vap->iv_as;
215
struct acl *acl;
216
217
ACL_LOCK(as);
218
acl = _find_acl(as, mac);
219
if (acl != NULL)
220
_acl_free(as, acl);
221
ACL_UNLOCK(as);
222
223
IEEE80211_DPRINTF(vap, IEEE80211_MSG_ACL,
224
"ACL: remove %s%s\n", ether_sprintf(mac),
225
acl == NULL ? ", not present" : "");
226
227
return (acl == NULL ? ENOENT : 0);
228
}
229
230
static int
231
acl_free_all(struct ieee80211vap *vap)
232
{
233
struct aclstate *as = vap->iv_as;
234
struct acl *acl;
235
236
IEEE80211_DPRINTF(vap, IEEE80211_MSG_ACL, "ACL: %s\n", "free all");
237
238
ACL_LOCK(as);
239
while ((acl = TAILQ_FIRST(&as->as_list)) != NULL)
240
_acl_free(as, acl);
241
ACL_UNLOCK(as);
242
243
return 0;
244
}
245
246
static int
247
acl_setpolicy(struct ieee80211vap *vap, int policy)
248
{
249
struct aclstate *as = vap->iv_as;
250
251
IEEE80211_DPRINTF(vap, IEEE80211_MSG_ACL,
252
"ACL: set policy to %u\n", policy);
253
254
switch (policy) {
255
case IEEE80211_MACCMD_POLICY_OPEN:
256
as->as_policy = ACL_POLICY_OPEN;
257
break;
258
case IEEE80211_MACCMD_POLICY_ALLOW:
259
as->as_policy = ACL_POLICY_ALLOW;
260
break;
261
case IEEE80211_MACCMD_POLICY_DENY:
262
as->as_policy = ACL_POLICY_DENY;
263
break;
264
case IEEE80211_MACCMD_POLICY_RADIUS:
265
as->as_policy = ACL_POLICY_RADIUS;
266
break;
267
default:
268
return EINVAL;
269
}
270
return 0;
271
}
272
273
static int
274
acl_getpolicy(struct ieee80211vap *vap)
275
{
276
struct aclstate *as = vap->iv_as;
277
278
return as->as_policy;
279
}
280
281
static int
282
acl_setioctl(struct ieee80211vap *vap, struct ieee80211req *ireq)
283
{
284
285
return EINVAL;
286
}
287
288
static int
289
acl_getioctl(struct ieee80211vap *vap, struct ieee80211req *ireq)
290
{
291
struct aclstate *as = vap->iv_as;
292
struct acl *acl;
293
struct ieee80211req_maclist *ap;
294
int error;
295
uint32_t i, space;
296
297
switch (ireq->i_val) {
298
case IEEE80211_MACCMD_POLICY:
299
ireq->i_val = as->as_policy;
300
return 0;
301
case IEEE80211_MACCMD_LIST:
302
space = as->as_nacls * IEEE80211_ADDR_LEN;
303
if (ireq->i_len == 0) {
304
ireq->i_len = space; /* return required space */
305
return 0; /* NB: must not error */
306
}
307
ap = (struct ieee80211req_maclist *) IEEE80211_MALLOC(space,
308
M_TEMP, IEEE80211_M_NOWAIT);
309
if (ap == NULL)
310
return ENOMEM;
311
i = 0;
312
ACL_LOCK(as);
313
TAILQ_FOREACH(acl, &as->as_list, acl_list) {
314
IEEE80211_ADDR_COPY(ap[i].ml_macaddr, acl->acl_macaddr);
315
i++;
316
}
317
ACL_UNLOCK(as);
318
if (ireq->i_len >= space) {
319
error = copyout(ap, ireq->i_data, space);
320
ireq->i_len = space;
321
} else
322
error = copyout(ap, ireq->i_data, ireq->i_len);
323
IEEE80211_FREE(ap, M_TEMP);
324
return error;
325
}
326
return EINVAL;
327
}
328
329
static const struct ieee80211_aclator mac = {
330
.iac_name = "mac",
331
.iac_attach = acl_attach,
332
.iac_detach = acl_detach,
333
.iac_check = acl_check,
334
.iac_add = acl_add,
335
.iac_remove = acl_remove,
336
.iac_flush = acl_free_all,
337
.iac_setpolicy = acl_setpolicy,
338
.iac_getpolicy = acl_getpolicy,
339
.iac_setioctl = acl_setioctl,
340
.iac_getioctl = acl_getioctl,
341
};
342
IEEE80211_ACL_MODULE(wlan_acl, mac, 1);
343
344