ieee80211_acl.c revision 138568
1138568Ssam/*- 2138568Ssam * Copyright (c) 2004 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 * 3. The name of the author may not be used to endorse or promote products 14138568Ssam * derived from this software without specific prior written permission. 15138568Ssam * 16138568Ssam * Alternatively, this software may be distributed under the terms of the 17138568Ssam * GNU General Public License ("GPL") version 2 as published by the Free 18138568Ssam * Software Foundation. 19138568Ssam * 20138568Ssam * THIS SOFTWARE IS PROVIDED BY THE AUTHOR ``AS IS'' AND ANY EXPRESS OR 21138568Ssam * IMPLIED WARRANTIES, INCLUDING, BUT NOT LIMITED TO, THE IMPLIED WARRANTIES 22138568Ssam * OF MERCHANTABILITY AND FITNESS FOR A PARTICULAR PURPOSE ARE DISCLAIMED. 23138568Ssam * IN NO EVENT SHALL THE AUTHOR BE LIABLE FOR ANY DIRECT, INDIRECT, 24138568Ssam * INCIDENTAL, SPECIAL, EXEMPLARY, OR CONSEQUENTIAL DAMAGES (INCLUDING, BUT 25138568Ssam * NOT LIMITED TO, PROCUREMENT OF SUBSTITUTE GOODS OR SERVICES; LOSS OF USE, 26138568Ssam * DATA, OR PROFITS; OR BUSINESS INTERRUPTION) HOWEVER CAUSED AND ON ANY 27138568Ssam * THEORY OF LIABILITY, WHETHER IN CONTRACT, STRICT LIABILITY, OR TORT 28138568Ssam * (INCLUDING NEGLIGENCE OR OTHERWISE) ARISING IN ANY WAY OUT OF THE USE OF 29138568Ssam * THIS SOFTWARE, EVEN IF ADVISED OF THE POSSIBILITY OF SUCH DAMAGE. 30138568Ssam */ 31138568Ssam 32138568Ssam#include <sys/cdefs.h> 33138568Ssam__FBSDID("$FreeBSD: head/sys/net80211/ieee80211_acl.c 138568 2004-12-08 17:26:47Z sam $"); 34138568Ssam 35138568Ssam/* 36138568Ssam * IEEE 802.11 MAC ACL support. 37138568Ssam * 38138568Ssam * When this module is loaded the sender address of each received 39138568Ssam * frame is passed to the iac_check method and the module indicates 40138568Ssam * if the frame should be accepted or rejected. If the policy is 41138568Ssam * set to ACL_POLICY_OPEN then all frames are accepted w/o checking 42138568Ssam * the address. Otherwise, the address is looked up in the database 43138568Ssam * and if found the frame is either accepted (ACL_POLICY_ALLOW) 44138568Ssam * or rejected (ACL_POLICY_DENT). 45138568Ssam */ 46138568Ssam#include <sys/param.h> 47138568Ssam#include <sys/kernel.h> 48138568Ssam#include <sys/systm.h> 49138568Ssam#include <sys/mbuf.h> 50138568Ssam#include <sys/module.h> 51138568Ssam#include <sys/queue.h> 52138568Ssam 53138568Ssam#include <sys/socket.h> 54138568Ssam 55138568Ssam#include <net/if.h> 56138568Ssam#include <net/if_media.h> 57138568Ssam#include <net/ethernet.h> 58138568Ssam#include <net/route.h> 59138568Ssam 60138568Ssam#include <net80211/ieee80211_var.h> 61138568Ssam 62138568Ssamenum { 63138568Ssam ACL_POLICY_OPEN = 0, /* open, don't check ACL's */ 64138568Ssam ACL_POLICY_ALLOW = 1, /* allow traffic from MAC */ 65138568Ssam ACL_POLICY_DENY = 2, /* deny traffic from MAC */ 66138568Ssam}; 67138568Ssam 68138568Ssam#define ACL_HASHSIZE 32 69138568Ssam 70138568Ssamstruct acl { 71138568Ssam TAILQ_ENTRY(acl) acl_list; 72138568Ssam LIST_ENTRY(acl) acl_hash; 73138568Ssam u_int8_t acl_macaddr[IEEE80211_ADDR_LEN]; 74138568Ssam}; 75138568Ssamstruct aclstate { 76138568Ssam acl_lock_t as_lock; 77138568Ssam int as_policy; 78138568Ssam TAILQ_HEAD(, acl) as_list; /* list of all ACL's */ 79138568Ssam LIST_HEAD(, acl) as_hash[ACL_HASHSIZE]; 80138568Ssam struct ieee80211com *as_ic; 81138568Ssam}; 82138568Ssam 83138568Ssam/* simple hash is enough for variation of macaddr */ 84138568Ssam#define ACL_HASH(addr) \ 85138568Ssam (((const u_int8_t *)(addr))[IEEE80211_ADDR_LEN - 1] % ACL_HASHSIZE) 86138568Ssam 87138568SsamMALLOC_DEFINE(M_80211_ACL, "acl", "802.11 station acl"); 88138568Ssam 89138568Ssamstatic int acl_free_all(struct ieee80211com *); 90138568Ssam 91138568Ssamstatic int 92138568Ssamacl_attach(struct ieee80211com *ic) 93138568Ssam{ 94138568Ssam struct aclstate *as; 95138568Ssam 96138568Ssam MALLOC(as, struct aclstate *, sizeof(struct aclstate), 97138568Ssam M_DEVBUF, M_NOWAIT | M_ZERO); 98138568Ssam if (as == NULL) 99138568Ssam return 0; 100138568Ssam ACL_LOCK_INIT(as, "acl"); 101138568Ssam TAILQ_INIT(&as->as_list); 102138568Ssam as->as_policy = ACL_POLICY_OPEN; 103138568Ssam as->as_ic = ic; 104138568Ssam ic->ic_as = as; 105138568Ssam return 1; 106138568Ssam} 107138568Ssam 108138568Ssamstatic void 109138568Ssamacl_detach(struct ieee80211com *ic) 110138568Ssam{ 111138568Ssam struct aclstate *as = ic->ic_as; 112138568Ssam 113138568Ssam acl_free_all(ic); 114138568Ssam ic->ic_as = NULL; 115138568Ssam ACL_LOCK_DESTROY(as); 116138568Ssam FREE(as, M_DEVBUF); 117138568Ssam} 118138568Ssam 119138568Ssamstatic inline struct acl * 120138568Ssam_find_acl(struct aclstate *as, const u_int8_t *macaddr) 121138568Ssam{ 122138568Ssam struct acl *acl; 123138568Ssam int hash; 124138568Ssam 125138568Ssam hash = ACL_HASH(macaddr); 126138568Ssam LIST_FOREACH(acl, &as->as_hash[hash], acl_hash) { 127138568Ssam if (IEEE80211_ADDR_EQ(acl->acl_macaddr, macaddr)) 128138568Ssam return acl; 129138568Ssam } 130138568Ssam return NULL; 131138568Ssam} 132138568Ssam 133138568Ssamstatic void 134138568Ssam_acl_free(struct aclstate *as, struct acl *acl) 135138568Ssam{ 136138568Ssam ACL_LOCK_ASSERT(as); 137138568Ssam 138138568Ssam TAILQ_REMOVE(&as->as_list, acl, acl_list); 139138568Ssam LIST_REMOVE(acl, acl_hash); 140138568Ssam FREE(acl, M_80211_ACL); 141138568Ssam} 142138568Ssam 143138568Ssamstatic int 144138568Ssamacl_check(struct ieee80211com *ic, const u_int8_t mac[IEEE80211_ADDR_LEN]) 145138568Ssam{ 146138568Ssam struct aclstate *as = ic->ic_as; 147138568Ssam 148138568Ssam switch (as->as_policy) { 149138568Ssam case ACL_POLICY_OPEN: 150138568Ssam return 1; 151138568Ssam case ACL_POLICY_ALLOW: 152138568Ssam return _find_acl(as, mac) != NULL; 153138568Ssam case ACL_POLICY_DENY: 154138568Ssam return _find_acl(as, mac) == NULL; 155138568Ssam } 156138568Ssam return 0; /* should not happen */ 157138568Ssam} 158138568Ssam 159138568Ssamstatic int 160138568Ssamacl_add(struct ieee80211com *ic, const u_int8_t mac[IEEE80211_ADDR_LEN]) 161138568Ssam{ 162138568Ssam struct aclstate *as = ic->ic_as; 163138568Ssam struct acl *acl, *new; 164138568Ssam int hash; 165138568Ssam 166138568Ssam MALLOC(new, struct acl *, sizeof(struct acl), M_80211_ACL, M_NOWAIT | M_ZERO); 167138568Ssam if (new == NULL) { 168138568Ssam IEEE80211_DPRINTF(ic, IEEE80211_MSG_ACL, 169138568Ssam "ACL: add %s failed, no memory\n", ether_sprintf(mac)); 170138568Ssam /* XXX statistic */ 171138568Ssam return ENOMEM; 172138568Ssam } 173138568Ssam 174138568Ssam ACL_LOCK(as); 175138568Ssam hash = ACL_HASH(mac); 176138568Ssam LIST_FOREACH(acl, &as->as_hash[hash], acl_hash) { 177138568Ssam if (IEEE80211_ADDR_EQ(acl->acl_macaddr, mac)) { 178138568Ssam ACL_UNLOCK(as); 179138568Ssam FREE(new, M_80211_ACL); 180138568Ssam IEEE80211_DPRINTF(ic, IEEE80211_MSG_ACL, 181138568Ssam "ACL: add %s failed, already present\n", 182138568Ssam ether_sprintf(mac)); 183138568Ssam return EEXIST; 184138568Ssam } 185138568Ssam } 186138568Ssam IEEE80211_ADDR_COPY(new->acl_macaddr, mac); 187138568Ssam TAILQ_INSERT_TAIL(&as->as_list, new, acl_list); 188138568Ssam LIST_INSERT_HEAD(&as->as_hash[hash], new, acl_hash); 189138568Ssam ACL_UNLOCK(as); 190138568Ssam 191138568Ssam IEEE80211_DPRINTF(ic, IEEE80211_MSG_ACL, 192138568Ssam "ACL: add %s\n", ether_sprintf(mac)); 193138568Ssam return 0; 194138568Ssam} 195138568Ssam 196138568Ssamstatic int 197138568Ssamacl_remove(struct ieee80211com *ic, const u_int8_t mac[IEEE80211_ADDR_LEN]) 198138568Ssam{ 199138568Ssam struct aclstate *as = ic->ic_as; 200138568Ssam struct acl *acl; 201138568Ssam 202138568Ssam ACL_LOCK(as); 203138568Ssam acl = _find_acl(as, mac); 204138568Ssam if (acl != NULL) 205138568Ssam _acl_free(as, acl); 206138568Ssam ACL_UNLOCK(as); 207138568Ssam 208138568Ssam IEEE80211_DPRINTF(ic, IEEE80211_MSG_ACL, 209138568Ssam "ACL: remove %s%s\n", ether_sprintf(mac), 210138568Ssam acl == NULL ? ", not present" : ""); 211138568Ssam 212138568Ssam return (acl == NULL ? ENOENT : 0); 213138568Ssam} 214138568Ssam 215138568Ssamstatic int 216138568Ssamacl_free_all(struct ieee80211com *ic) 217138568Ssam{ 218138568Ssam struct aclstate *as = ic->ic_as; 219138568Ssam struct acl *acl; 220138568Ssam 221138568Ssam IEEE80211_DPRINTF(ic, IEEE80211_MSG_ACL, "ACL: %s\n", "free all"); 222138568Ssam 223138568Ssam ACL_LOCK(as); 224138568Ssam while ((acl = TAILQ_FIRST(&as->as_list)) != NULL) 225138568Ssam _acl_free(as, acl); 226138568Ssam ACL_UNLOCK(as); 227138568Ssam 228138568Ssam return 0; 229138568Ssam} 230138568Ssam 231138568Ssamstatic int 232138568Ssamacl_setpolicy(struct ieee80211com *ic, int policy) 233138568Ssam{ 234138568Ssam struct aclstate *as = ic->ic_as; 235138568Ssam 236138568Ssam IEEE80211_DPRINTF(ic, IEEE80211_MSG_ACL, 237138568Ssam "ACL: set policy to %u\n", policy); 238138568Ssam 239138568Ssam switch (policy) { 240138568Ssam case IEEE80211_MACCMD_POLICY_OPEN: 241138568Ssam as->as_policy = ACL_POLICY_OPEN; 242138568Ssam break; 243138568Ssam case IEEE80211_MACCMD_POLICY_ALLOW: 244138568Ssam as->as_policy = ACL_POLICY_ALLOW; 245138568Ssam break; 246138568Ssam case IEEE80211_MACCMD_POLICY_DENY: 247138568Ssam as->as_policy = ACL_POLICY_DENY; 248138568Ssam break; 249138568Ssam default: 250138568Ssam return EINVAL; 251138568Ssam } 252138568Ssam return 0; 253138568Ssam} 254138568Ssam 255138568Ssamstatic int 256138568Ssamacl_getpolicy(struct ieee80211com *ic) 257138568Ssam{ 258138568Ssam struct aclstate *as = ic->ic_as; 259138568Ssam 260138568Ssam return as->as_policy; 261138568Ssam} 262138568Ssam 263138568Ssamstatic const struct ieee80211_aclator mac = { 264138568Ssam .iac_name = "mac", 265138568Ssam .iac_attach = acl_attach, 266138568Ssam .iac_detach = acl_detach, 267138568Ssam .iac_check = acl_check, 268138568Ssam .iac_add = acl_add, 269138568Ssam .iac_remove = acl_remove, 270138568Ssam .iac_flush = acl_free_all, 271138568Ssam .iac_setpolicy = acl_setpolicy, 272138568Ssam .iac_getpolicy = acl_getpolicy, 273138568Ssam}; 274138568Ssam 275138568Ssam/* 276138568Ssam * Module glue. 277138568Ssam */ 278138568Ssamstatic int 279138568Ssamwlan_acl_modevent(module_t mod, int type, void *unused) 280138568Ssam{ 281138568Ssam switch (type) { 282138568Ssam case MOD_LOAD: 283138568Ssam if (bootverbose) 284138568Ssam printf("wlan: <802.11 MAC ACL support>\n"); 285138568Ssam ieee80211_aclator_register(&mac); 286138568Ssam return 0; 287138568Ssam case MOD_UNLOAD: 288138568Ssam ieee80211_aclator_unregister(&mac); 289138568Ssam return 0; 290138568Ssam } 291138568Ssam return EINVAL; 292138568Ssam} 293138568Ssam 294138568Ssamstatic moduledata_t wlan_acl_mod = { 295138568Ssam "wlan_acl", 296138568Ssam wlan_acl_modevent, 297138568Ssam 0 298138568Ssam}; 299138568SsamDECLARE_MODULE(wlan_acl, wlan_acl_mod, SI_SUB_DRIVERS, SI_ORDER_FIRST); 300138568SsamMODULE_VERSION(wlan_acl, 1); 301138568SsamMODULE_DEPEND(wlan_acl, wlan, 1, 1, 1); 302