ieee80211_acl.c revision 170530
1138568Ssam/*- 2170360Ssam * Copyright (c) 2004-2007 Sam Leffler, Errno Consulting 3138568Ssam * All rights reserved. 4138568Ssam * 5138568Ssam * Redistribution and use in source and binary forms, with or without 6138568Ssam * modification, are permitted provided that the following conditions 7138568Ssam * are met: 8138568Ssam * 1. Redistributions of source code must retain the above copyright 9138568Ssam * notice, this list of conditions and the following disclaimer. 10138568Ssam * 2. Redistributions in binary form must reproduce the above copyright 11138568Ssam * notice, this list of conditions and the following disclaimer in the 12138568Ssam * documentation and/or other materials provided with the distribution. 13138568Ssam * 14138568Ssam * THIS SOFTWARE IS PROVIDED BY THE AUTHOR ``AS IS'' AND ANY EXPRESS OR 15138568Ssam * IMPLIED WARRANTIES, INCLUDING, BUT NOT LIMITED TO, THE IMPLIED WARRANTIES 16138568Ssam * OF MERCHANTABILITY AND FITNESS FOR A PARTICULAR PURPOSE ARE DISCLAIMED. 17138568Ssam * IN NO EVENT SHALL THE AUTHOR BE LIABLE FOR ANY DIRECT, INDIRECT, 18138568Ssam * INCIDENTAL, SPECIAL, EXEMPLARY, OR CONSEQUENTIAL DAMAGES (INCLUDING, BUT 19138568Ssam * NOT LIMITED TO, PROCUREMENT OF SUBSTITUTE GOODS OR SERVICES; LOSS OF USE, 20138568Ssam * DATA, OR PROFITS; OR BUSINESS INTERRUPTION) HOWEVER CAUSED AND ON ANY 21138568Ssam * THEORY OF LIABILITY, WHETHER IN CONTRACT, STRICT LIABILITY, OR TORT 22138568Ssam * (INCLUDING NEGLIGENCE OR OTHERWISE) ARISING IN ANY WAY OUT OF THE USE OF 23138568Ssam * THIS SOFTWARE, EVEN IF ADVISED OF THE POSSIBILITY OF SUCH DAMAGE. 24138568Ssam */ 25138568Ssam 26138568Ssam#include <sys/cdefs.h> 27138568Ssam__FBSDID("$FreeBSD: head/sys/net80211/ieee80211_acl.c 170530 2007-06-11 03:36:55Z sam $"); 28138568Ssam 29138568Ssam/* 30138568Ssam * IEEE 802.11 MAC ACL support. 31138568Ssam * 32138568Ssam * When this module is loaded the sender address of each received 33138568Ssam * frame is passed to the iac_check method and the module indicates 34138568Ssam * if the frame should be accepted or rejected. If the policy is 35138568Ssam * set to ACL_POLICY_OPEN then all frames are accepted w/o checking 36138568Ssam * the address. Otherwise, the address is looked up in the database 37138568Ssam * and if found the frame is either accepted (ACL_POLICY_ALLOW) 38138568Ssam * or rejected (ACL_POLICY_DENT). 39138568Ssam */ 40138568Ssam#include <sys/param.h> 41138568Ssam#include <sys/kernel.h> 42138568Ssam#include <sys/systm.h> 43138568Ssam#include <sys/mbuf.h> 44138568Ssam#include <sys/module.h> 45138568Ssam#include <sys/queue.h> 46138568Ssam 47138568Ssam#include <sys/socket.h> 48138568Ssam 49138568Ssam#include <net/if.h> 50138568Ssam#include <net/if_media.h> 51138568Ssam#include <net/ethernet.h> 52138568Ssam#include <net/route.h> 53138568Ssam 54138568Ssam#include <net80211/ieee80211_var.h> 55138568Ssam 56138568Ssamenum { 57138568Ssam ACL_POLICY_OPEN = 0, /* open, don't check ACL's */ 58138568Ssam ACL_POLICY_ALLOW = 1, /* allow traffic from MAC */ 59138568Ssam ACL_POLICY_DENY = 2, /* deny traffic from MAC */ 60138568Ssam}; 61138568Ssam 62138568Ssam#define ACL_HASHSIZE 32 63138568Ssam 64138568Ssamstruct acl { 65138568Ssam TAILQ_ENTRY(acl) acl_list; 66138568Ssam LIST_ENTRY(acl) acl_hash; 67170530Ssam uint8_t acl_macaddr[IEEE80211_ADDR_LEN]; 68138568Ssam}; 69138568Ssamstruct aclstate { 70138568Ssam acl_lock_t as_lock; 71138568Ssam int as_policy; 72149028Ssam int as_nacls; 73138568Ssam TAILQ_HEAD(, acl) as_list; /* list of all ACL's */ 74138568Ssam LIST_HEAD(, acl) as_hash[ACL_HASHSIZE]; 75138568Ssam struct ieee80211com *as_ic; 76138568Ssam}; 77138568Ssam 78138568Ssam/* simple hash is enough for variation of macaddr */ 79138568Ssam#define ACL_HASH(addr) \ 80170530Ssam (((const uint8_t *)(addr))[IEEE80211_ADDR_LEN - 1] % ACL_HASHSIZE) 81138568Ssam 82138568SsamMALLOC_DEFINE(M_80211_ACL, "acl", "802.11 station acl"); 83138568Ssam 84138568Ssamstatic int acl_free_all(struct ieee80211com *); 85138568Ssam 86138568Ssamstatic int 87138568Ssamacl_attach(struct ieee80211com *ic) 88138568Ssam{ 89138568Ssam struct aclstate *as; 90138568Ssam 91138568Ssam MALLOC(as, struct aclstate *, sizeof(struct aclstate), 92149028Ssam M_80211_ACL, M_NOWAIT | M_ZERO); 93138568Ssam if (as == NULL) 94138568Ssam return 0; 95138568Ssam ACL_LOCK_INIT(as, "acl"); 96138568Ssam TAILQ_INIT(&as->as_list); 97138568Ssam as->as_policy = ACL_POLICY_OPEN; 98138568Ssam as->as_ic = ic; 99138568Ssam ic->ic_as = as; 100138568Ssam return 1; 101138568Ssam} 102138568Ssam 103138568Ssamstatic void 104138568Ssamacl_detach(struct ieee80211com *ic) 105138568Ssam{ 106138568Ssam struct aclstate *as = ic->ic_as; 107138568Ssam 108138568Ssam acl_free_all(ic); 109138568Ssam ic->ic_as = NULL; 110138568Ssam ACL_LOCK_DESTROY(as); 111138568Ssam FREE(as, M_DEVBUF); 112138568Ssam} 113138568Ssam 114139503Ssamstatic __inline struct acl * 115170530Ssam_find_acl(struct aclstate *as, const uint8_t *macaddr) 116138568Ssam{ 117138568Ssam struct acl *acl; 118138568Ssam int hash; 119138568Ssam 120138568Ssam hash = ACL_HASH(macaddr); 121138568Ssam LIST_FOREACH(acl, &as->as_hash[hash], acl_hash) { 122138568Ssam if (IEEE80211_ADDR_EQ(acl->acl_macaddr, macaddr)) 123138568Ssam return acl; 124138568Ssam } 125138568Ssam return NULL; 126138568Ssam} 127138568Ssam 128138568Ssamstatic void 129138568Ssam_acl_free(struct aclstate *as, struct acl *acl) 130138568Ssam{ 131138568Ssam ACL_LOCK_ASSERT(as); 132138568Ssam 133138568Ssam TAILQ_REMOVE(&as->as_list, acl, acl_list); 134138568Ssam LIST_REMOVE(acl, acl_hash); 135138568Ssam FREE(acl, M_80211_ACL); 136149028Ssam as->as_nacls--; 137138568Ssam} 138138568Ssam 139138568Ssamstatic int 140170530Ssamacl_check(struct ieee80211com *ic, const uint8_t mac[IEEE80211_ADDR_LEN]) 141138568Ssam{ 142138568Ssam struct aclstate *as = ic->ic_as; 143138568Ssam 144138568Ssam switch (as->as_policy) { 145138568Ssam case ACL_POLICY_OPEN: 146138568Ssam return 1; 147138568Ssam case ACL_POLICY_ALLOW: 148138568Ssam return _find_acl(as, mac) != NULL; 149138568Ssam case ACL_POLICY_DENY: 150138568Ssam return _find_acl(as, mac) == NULL; 151138568Ssam } 152138568Ssam return 0; /* should not happen */ 153138568Ssam} 154138568Ssam 155138568Ssamstatic int 156170530Ssamacl_add(struct ieee80211com *ic, const uint8_t mac[IEEE80211_ADDR_LEN]) 157138568Ssam{ 158138568Ssam struct aclstate *as = ic->ic_as; 159138568Ssam struct acl *acl, *new; 160138568Ssam int hash; 161138568Ssam 162138568Ssam MALLOC(new, struct acl *, sizeof(struct acl), M_80211_ACL, M_NOWAIT | M_ZERO); 163138568Ssam if (new == NULL) { 164138568Ssam IEEE80211_DPRINTF(ic, IEEE80211_MSG_ACL, 165138568Ssam "ACL: add %s failed, no memory\n", ether_sprintf(mac)); 166138568Ssam /* XXX statistic */ 167138568Ssam return ENOMEM; 168138568Ssam } 169138568Ssam 170138568Ssam ACL_LOCK(as); 171138568Ssam hash = ACL_HASH(mac); 172138568Ssam LIST_FOREACH(acl, &as->as_hash[hash], acl_hash) { 173138568Ssam if (IEEE80211_ADDR_EQ(acl->acl_macaddr, mac)) { 174138568Ssam ACL_UNLOCK(as); 175138568Ssam FREE(new, M_80211_ACL); 176138568Ssam IEEE80211_DPRINTF(ic, IEEE80211_MSG_ACL, 177138568Ssam "ACL: add %s failed, already present\n", 178138568Ssam ether_sprintf(mac)); 179138568Ssam return EEXIST; 180138568Ssam } 181138568Ssam } 182138568Ssam IEEE80211_ADDR_COPY(new->acl_macaddr, mac); 183138568Ssam TAILQ_INSERT_TAIL(&as->as_list, new, acl_list); 184138568Ssam LIST_INSERT_HEAD(&as->as_hash[hash], new, acl_hash); 185149028Ssam as->as_nacls++; 186138568Ssam ACL_UNLOCK(as); 187138568Ssam 188138568Ssam IEEE80211_DPRINTF(ic, IEEE80211_MSG_ACL, 189138568Ssam "ACL: add %s\n", ether_sprintf(mac)); 190138568Ssam return 0; 191138568Ssam} 192138568Ssam 193138568Ssamstatic int 194170530Ssamacl_remove(struct ieee80211com *ic, const uint8_t mac[IEEE80211_ADDR_LEN]) 195138568Ssam{ 196138568Ssam struct aclstate *as = ic->ic_as; 197138568Ssam struct acl *acl; 198138568Ssam 199138568Ssam ACL_LOCK(as); 200138568Ssam acl = _find_acl(as, mac); 201138568Ssam if (acl != NULL) 202138568Ssam _acl_free(as, acl); 203138568Ssam ACL_UNLOCK(as); 204138568Ssam 205138568Ssam IEEE80211_DPRINTF(ic, IEEE80211_MSG_ACL, 206138568Ssam "ACL: remove %s%s\n", ether_sprintf(mac), 207138568Ssam acl == NULL ? ", not present" : ""); 208138568Ssam 209138568Ssam return (acl == NULL ? ENOENT : 0); 210138568Ssam} 211138568Ssam 212138568Ssamstatic int 213138568Ssamacl_free_all(struct ieee80211com *ic) 214138568Ssam{ 215138568Ssam struct aclstate *as = ic->ic_as; 216138568Ssam struct acl *acl; 217138568Ssam 218138568Ssam IEEE80211_DPRINTF(ic, IEEE80211_MSG_ACL, "ACL: %s\n", "free all"); 219138568Ssam 220138568Ssam ACL_LOCK(as); 221138568Ssam while ((acl = TAILQ_FIRST(&as->as_list)) != NULL) 222138568Ssam _acl_free(as, acl); 223138568Ssam ACL_UNLOCK(as); 224138568Ssam 225138568Ssam return 0; 226138568Ssam} 227138568Ssam 228138568Ssamstatic int 229138568Ssamacl_setpolicy(struct ieee80211com *ic, int policy) 230138568Ssam{ 231138568Ssam struct aclstate *as = ic->ic_as; 232138568Ssam 233138568Ssam IEEE80211_DPRINTF(ic, IEEE80211_MSG_ACL, 234138568Ssam "ACL: set policy to %u\n", policy); 235138568Ssam 236138568Ssam switch (policy) { 237138568Ssam case IEEE80211_MACCMD_POLICY_OPEN: 238138568Ssam as->as_policy = ACL_POLICY_OPEN; 239138568Ssam break; 240138568Ssam case IEEE80211_MACCMD_POLICY_ALLOW: 241138568Ssam as->as_policy = ACL_POLICY_ALLOW; 242138568Ssam break; 243138568Ssam case IEEE80211_MACCMD_POLICY_DENY: 244138568Ssam as->as_policy = ACL_POLICY_DENY; 245138568Ssam break; 246138568Ssam default: 247138568Ssam return EINVAL; 248138568Ssam } 249138568Ssam return 0; 250138568Ssam} 251138568Ssam 252138568Ssamstatic int 253138568Ssamacl_getpolicy(struct ieee80211com *ic) 254138568Ssam{ 255138568Ssam struct aclstate *as = ic->ic_as; 256138568Ssam 257138568Ssam return as->as_policy; 258138568Ssam} 259138568Ssam 260149028Ssamstatic int 261149028Ssamacl_setioctl(struct ieee80211com *ic, struct ieee80211req *ireq) 262149028Ssam{ 263149028Ssam 264149028Ssam return EINVAL; 265149028Ssam} 266149028Ssam 267149028Ssamstatic int 268149028Ssamacl_getioctl(struct ieee80211com *ic, struct ieee80211req *ireq) 269149028Ssam{ 270149028Ssam struct aclstate *as = ic->ic_as; 271149028Ssam struct acl *acl; 272149028Ssam struct ieee80211req_maclist *ap; 273149028Ssam int error, space, i; 274149028Ssam 275149028Ssam switch (ireq->i_val) { 276149028Ssam case IEEE80211_MACCMD_POLICY: 277149028Ssam ireq->i_val = as->as_policy; 278149028Ssam return 0; 279149028Ssam case IEEE80211_MACCMD_LIST: 280149028Ssam space = as->as_nacls * IEEE80211_ADDR_LEN; 281149028Ssam if (ireq->i_len == 0) { 282149028Ssam ireq->i_len = space; /* return required space */ 283149028Ssam return 0; /* NB: must not error */ 284149028Ssam } 285149028Ssam MALLOC(ap, struct ieee80211req_maclist *, space, 286149028Ssam M_TEMP, M_NOWAIT); 287149028Ssam if (ap == NULL) 288149028Ssam return ENOMEM; 289149028Ssam i = 0; 290149028Ssam ACL_LOCK(as); 291149028Ssam TAILQ_FOREACH(acl, &as->as_list, acl_list) { 292149028Ssam IEEE80211_ADDR_COPY(ap[i].ml_macaddr, acl->acl_macaddr); 293149028Ssam i++; 294149028Ssam } 295149028Ssam ACL_UNLOCK(as); 296149028Ssam if (ireq->i_len >= space) { 297149028Ssam error = copyout(ap, ireq->i_data, space); 298149028Ssam ireq->i_len = space; 299149028Ssam } else 300149028Ssam error = copyout(ap, ireq->i_data, ireq->i_len); 301149028Ssam FREE(ap, M_TEMP); 302149028Ssam return error; 303149028Ssam } 304149028Ssam return EINVAL; 305149028Ssam} 306149028Ssam 307138568Ssamstatic const struct ieee80211_aclator mac = { 308138568Ssam .iac_name = "mac", 309138568Ssam .iac_attach = acl_attach, 310138568Ssam .iac_detach = acl_detach, 311138568Ssam .iac_check = acl_check, 312138568Ssam .iac_add = acl_add, 313138568Ssam .iac_remove = acl_remove, 314138568Ssam .iac_flush = acl_free_all, 315138568Ssam .iac_setpolicy = acl_setpolicy, 316138568Ssam .iac_getpolicy = acl_getpolicy, 317149028Ssam .iac_setioctl = acl_setioctl, 318149028Ssam .iac_getioctl = acl_getioctl, 319138568Ssam}; 320138568Ssam 321138568Ssam/* 322138568Ssam * Module glue. 323138568Ssam */ 324138568Ssamstatic int 325138568Ssamwlan_acl_modevent(module_t mod, int type, void *unused) 326138568Ssam{ 327138568Ssam switch (type) { 328138568Ssam case MOD_LOAD: 329138568Ssam if (bootverbose) 330138568Ssam printf("wlan: <802.11 MAC ACL support>\n"); 331138568Ssam ieee80211_aclator_register(&mac); 332138568Ssam return 0; 333138568Ssam case MOD_UNLOAD: 334138568Ssam ieee80211_aclator_unregister(&mac); 335138568Ssam return 0; 336138568Ssam } 337138568Ssam return EINVAL; 338138568Ssam} 339138568Ssam 340138568Ssamstatic moduledata_t wlan_acl_mod = { 341138568Ssam "wlan_acl", 342138568Ssam wlan_acl_modevent, 343138568Ssam 0 344138568Ssam}; 345138568SsamDECLARE_MODULE(wlan_acl, wlan_acl_mod, SI_SUB_DRIVERS, SI_ORDER_FIRST); 346138568SsamMODULE_VERSION(wlan_acl, 1); 347138568SsamMODULE_DEPEND(wlan_acl, wlan, 1, 1, 1); 348