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