ieee80211_acl.c revision 170530
11541Srgrimes/*- 21541Srgrimes * Copyright (c) 2004-2007 Sam Leffler, Errno Consulting 31541Srgrimes * All rights reserved. 41541Srgrimes * 51541Srgrimes * Redistribution and use in source and binary forms, with or without 61541Srgrimes * modification, are permitted provided that the following conditions 71541Srgrimes * are met: 81541Srgrimes * 1. Redistributions of source code must retain the above copyright 91541Srgrimes * notice, this list of conditions and the following disclaimer. 101541Srgrimes * 2. Redistributions in binary form must reproduce the above copyright 111541Srgrimes * notice, this list of conditions and the following disclaimer in the 121541Srgrimes * documentation and/or other materials provided with the distribution. 131541Srgrimes * 141541Srgrimes * THIS SOFTWARE IS PROVIDED BY THE AUTHOR ``AS IS'' AND ANY EXPRESS OR 151541Srgrimes * IMPLIED WARRANTIES, INCLUDING, BUT NOT LIMITED TO, THE IMPLIED WARRANTIES 161541Srgrimes * OF MERCHANTABILITY AND FITNESS FOR A PARTICULAR PURPOSE ARE DISCLAIMED. 171541Srgrimes * IN NO EVENT SHALL THE AUTHOR BE LIABLE FOR ANY DIRECT, INDIRECT, 181541Srgrimes * INCIDENTAL, SPECIAL, EXEMPLARY, OR CONSEQUENTIAL DAMAGES (INCLUDING, BUT 191541Srgrimes * NOT LIMITED TO, PROCUREMENT OF SUBSTITUTE GOODS OR SERVICES; LOSS OF USE, 201541Srgrimes * DATA, OR PROFITS; OR BUSINESS INTERRUPTION) HOWEVER CAUSED AND ON ANY 211541Srgrimes * THEORY OF LIABILITY, WHETHER IN CONTRACT, STRICT LIABILITY, OR TORT 221541Srgrimes * (INCLUDING NEGLIGENCE OR OTHERWISE) ARISING IN ANY WAY OUT OF THE USE OF 231541Srgrimes * THIS SOFTWARE, EVEN IF ADVISED OF THE POSSIBILITY OF SUCH DAMAGE. 241541Srgrimes */ 251541Srgrimes 261541Srgrimes#include <sys/cdefs.h> 271541Srgrimes__FBSDID("$FreeBSD: head/sys/net80211/ieee80211_acl.c 170530 2007-06-11 03:36:55Z sam $"); 281541Srgrimes 291541Srgrimes/* 301541Srgrimes * IEEE 802.11 MAC ACL support. 311541Srgrimes * 321541Srgrimes * When this module is loaded the sender address of each received 331541Srgrimes * frame is passed to the iac_check method and the module indicates 341541Srgrimes * if the frame should be accepted or rejected. If the policy is 351541Srgrimes * set to ACL_POLICY_OPEN then all frames are accepted w/o checking 361541Srgrimes * the address. Otherwise, the address is looked up in the database 371541Srgrimes * and if found the frame is either accepted (ACL_POLICY_ALLOW) 381541Srgrimes * or rejected (ACL_POLICY_DENT). 3944549Sdfr */ 401541Srgrimes#include <sys/param.h> 411541Srgrimes#include <sys/kernel.h> 421541Srgrimes#include <sys/systm.h> 431541Srgrimes#include <sys/mbuf.h> 442112Swollman#include <sys/module.h> 452946Swollman#include <sys/queue.h> 461541Srgrimes 4738869Sbde#include <sys/socket.h> 481541Srgrimes 491541Srgrimes#include <net/if.h> 5029653Sdyson#include <net/if_media.h> 511541Srgrimes#include <net/ethernet.h> 5212577Sbde#include <net/route.h> 5330354Sphk 5430354Sphk#include <net80211/ieee80211_var.h> 5510358Sjulian 5640435Speterenum { 5740435Speter ACL_POLICY_OPEN = 0, /* open, don't check ACL's */ 5840435Speter ACL_POLICY_ALLOW = 1, /* allow traffic from MAC */ 5940435Speter ACL_POLICY_DENY = 2, /* deny traffic from MAC */ 6040435Speter}; 6140435Speter 6240435Speter#define ACL_HASHSIZE 32 6340435Speter 6440435Speterstruct acl { 6540435Speter TAILQ_ENTRY(acl) acl_list; 6640435Speter LIST_ENTRY(acl) acl_hash; 6729653Sdyson uint8_t acl_macaddr[IEEE80211_ADDR_LEN]; 6829653Sdyson}; 6929653Sdysonstruct aclstate { 7029653Sdyson acl_lock_t as_lock; 7129653Sdyson int as_policy; 721541Srgrimes int as_nacls; 731541Srgrimes TAILQ_HEAD(, acl) as_list; /* list of all ACL's */ 741541Srgrimes LIST_HEAD(, acl) as_hash[ACL_HASHSIZE]; 751541Srgrimes struct ieee80211com *as_ic; 761541Srgrimes}; 771541Srgrimes 781541Srgrimes/* simple hash is enough for variation of macaddr */ 791541Srgrimes#define ACL_HASH(addr) \ 801541Srgrimes (((const uint8_t *)(addr))[IEEE80211_ADDR_LEN - 1] % ACL_HASHSIZE) 811541Srgrimes 821541SrgrimesMALLOC_DEFINE(M_80211_ACL, "acl", "802.11 station acl"); 831541Srgrimes 841541Srgrimesstatic int acl_free_all(struct ieee80211com *); 851541Srgrimes 861541Srgrimesstatic int 8741056Speteracl_attach(struct ieee80211com *ic) 8841056Speter{ 8943311Sdillon struct aclstate *as; 9041056Speter 9141056Speter MALLOC(as, struct aclstate *, sizeof(struct aclstate), 9241056Speter M_80211_ACL, M_NOWAIT | M_ZERO); 9341056Speter if (as == NULL) 9441056Speter return 0; 9541056Speter ACL_LOCK_INIT(as, "acl"); 9641056Speter TAILQ_INIT(&as->as_list); 9741056Speter as->as_policy = ACL_POLICY_OPEN; 9841056Speter as->as_ic = ic; 9941056Speter ic->ic_as = as; 1001541Srgrimes return 1; 10141056Speter} 10212158Sbde 10312158Sbdestatic void 1041541Srgrimesacl_detach(struct ieee80211com *ic) 10543311Sdillon{ 1061541Srgrimes struct aclstate *as = ic->ic_as; 10741056Speter 10841056Speter acl_free_all(ic); 10941056Speter ic->ic_as = NULL; 1101541Srgrimes ACL_LOCK_DESTROY(as); 11141056Speter FREE(as, M_DEVBUF); 11241056Speter} 11341056Speter 11441056Speterstatic __inline struct acl * 1151541Srgrimes_find_acl(struct aclstate *as, const uint8_t *macaddr) 11641056Speter{ 11741056Speter struct acl *acl; 11841056Speter int hash; 11941056Speter 12041056Speter hash = ACL_HASH(macaddr); 12141056Speter LIST_FOREACH(acl, &as->as_hash[hash], acl_hash) { 12241056Speter if (IEEE80211_ADDR_EQ(acl->acl_macaddr, macaddr)) 12340435Speter return acl; 12441056Speter } 12540435Speter return NULL; 12641056Speter} 12741056Speter 12841056Speterstatic void 12941056Speter_acl_free(struct aclstate *as, struct acl *acl) 13041056Speter{ 13140435Speter ACL_LOCK_ASSERT(as); 13240880Speter 13341056Speter TAILQ_REMOVE(&as->as_list, acl, acl_list); 13441056Speter LIST_REMOVE(acl, acl_hash); 13541056Speter FREE(acl, M_80211_ACL); 13641056Speter as->as_nacls--; 13741056Speter} 13841056Speter 13941056Speterstatic int 14041056Speteracl_check(struct ieee80211com *ic, const uint8_t mac[IEEE80211_ADDR_LEN]) 14141056Speter{ 14241056Speter struct aclstate *as = ic->ic_as; 14341056Speter 14441056Speter switch (as->as_policy) { 14541056Speter case ACL_POLICY_OPEN: 14641056Speter return 1; 14741056Speter case ACL_POLICY_ALLOW: 14841056Speter return _find_acl(as, mac) != NULL; 14941056Speter case ACL_POLICY_DENY: 15041056Speter return _find_acl(as, mac) == NULL; 15141056Speter } 15241056Speter return 0; /* should not happen */ 15340435Speter} 15441056Speter 15540435Speterstatic int 15641056Speteracl_add(struct ieee80211com *ic, const uint8_t mac[IEEE80211_ADDR_LEN]) 15743311Sdillon{ 15841056Speter struct aclstate *as = ic->ic_as; 15943311Sdillon struct acl *acl, *new; 16043311Sdillon int hash; 16141056Speter 16241056Speter MALLOC(new, struct acl *, sizeof(struct acl), M_80211_ACL, M_NOWAIT | M_ZERO); 16341056Speter if (new == NULL) { 16441056Speter IEEE80211_DPRINTF(ic, IEEE80211_MSG_ACL, 16541591Sarchie "ACL: add %s failed, no memory\n", ether_sprintf(mac)); 16641056Speter /* XXX statistic */ 16743311Sdillon return ENOMEM; 16843311Sdillon } 16941056Speter 17041056Speter ACL_LOCK(as); 17141056Speter hash = ACL_HASH(mac); 17241056Speter LIST_FOREACH(acl, &as->as_hash[hash], acl_hash) { 17341056Speter if (IEEE80211_ADDR_EQ(acl->acl_macaddr, mac)) { 17441056Speter ACL_UNLOCK(as); 17541056Speter FREE(new, M_80211_ACL); 17641056Speter IEEE80211_DPRINTF(ic, IEEE80211_MSG_ACL, 17741056Speter "ACL: add %s failed, already present\n", 17841056Speter ether_sprintf(mac)); 17941056Speter return EEXIST; 18041056Speter } 18141056Speter } 18241056Speter IEEE80211_ADDR_COPY(new->acl_macaddr, mac); 18341056Speter TAILQ_INSERT_TAIL(&as->as_list, new, acl_list); 18441056Speter LIST_INSERT_HEAD(&as->as_hash[hash], new, acl_hash); 18541056Speter as->as_nacls++; 18641056Speter ACL_UNLOCK(as); 18741056Speter 18841056Speter IEEE80211_DPRINTF(ic, IEEE80211_MSG_ACL, 1891541Srgrimes "ACL: add %s\n", ether_sprintf(mac)); 19041056Speter return 0; 19141056Speter} 19241056Speter 19341056Speterstatic int 19441056Speteracl_remove(struct ieee80211com *ic, const uint8_t mac[IEEE80211_ADDR_LEN]) 19541056Speter{ 19641056Speter struct aclstate *as = ic->ic_as; 19741056Speter struct acl *acl; 19841056Speter 19941056Speter ACL_LOCK(as); 20041056Speter acl = _find_acl(as, mac); 20141056Speter if (acl != NULL) 20241056Speter _acl_free(as, acl); 20341056Speter ACL_UNLOCK(as); 20441056Speter 20541056Speter IEEE80211_DPRINTF(ic, IEEE80211_MSG_ACL, 20641056Speter "ACL: remove %s%s\n", ether_sprintf(mac), 20741056Speter acl == NULL ? ", not present" : ""); 20841056Speter 20941056Speter return (acl == NULL ? ENOENT : 0); 21041056Speter} 21141056Speter 21241056Speterstatic int 21341056Speteracl_free_all(struct ieee80211com *ic) 21441056Speter{ 21541056Speter struct aclstate *as = ic->ic_as; 21641056Speter struct acl *acl; 21741056Speter 21841056Speter IEEE80211_DPRINTF(ic, IEEE80211_MSG_ACL, "ACL: %s\n", "free all"); 2191541Srgrimes 22041056Speter ACL_LOCK(as); 2211541Srgrimes while ((acl = TAILQ_FIRST(&as->as_list)) != NULL) 2221541Srgrimes _acl_free(as, acl); 22341056Speter ACL_UNLOCK(as); 22443311Sdillon 2251541Srgrimes return 0; 22643311Sdillon} 22743311Sdillon 22841056Speterstatic int 22941056Speteracl_setpolicy(struct ieee80211com *ic, int policy) 23041056Speter{ 23141056Speter struct aclstate *as = ic->ic_as; 23241056Speter 2331541Srgrimes IEEE80211_DPRINTF(ic, IEEE80211_MSG_ACL, 23443351Sdillon "ACL: set policy to %u\n", policy); 23541056Speter 23641056Speter switch (policy) { 23741056Speter case IEEE80211_MACCMD_POLICY_OPEN: 23841056Speter as->as_policy = ACL_POLICY_OPEN; 23941056Speter break; 24041056Speter case IEEE80211_MACCMD_POLICY_ALLOW: 24141056Speter as->as_policy = ACL_POLICY_ALLOW; 24241056Speter break; 24341056Speter case IEEE80211_MACCMD_POLICY_DENY: 24441056Speter as->as_policy = ACL_POLICY_DENY; 24541056Speter break; 24641056Speter default: 24741056Speter return EINVAL; 24841056Speter } 24941056Speter return 0; 25041056Speter} 25141056Speter 25241056Speterstatic int 25341056Speteracl_getpolicy(struct ieee80211com *ic) 25441056Speter{ 25541056Speter struct aclstate *as = ic->ic_as; 25641056Speter 25741056Speter return as->as_policy; 25841056Speter} 25941056Speter 26041056Speterstatic int 26141056Speteracl_setioctl(struct ieee80211com *ic, struct ieee80211req *ireq) 26241056Speter{ 26341056Speter 26441056Speter return EINVAL; 26541056Speter} 26641056Speter 26741056Speterstatic int 26841056Speteracl_getioctl(struct ieee80211com *ic, struct ieee80211req *ireq) 26941056Speter{ 27041056Speter struct aclstate *as = ic->ic_as; 27141056Speter struct acl *acl; 27241056Speter struct ieee80211req_maclist *ap; 27341056Speter int error, space, i; 27441056Speter 27541056Speter switch (ireq->i_val) { 27641056Speter case IEEE80211_MACCMD_POLICY: 27741056Speter ireq->i_val = as->as_policy; 27841056Speter return 0; 27941056Speter case IEEE80211_MACCMD_LIST: 28041056Speter space = as->as_nacls * IEEE80211_ADDR_LEN; 28141056Speter if (ireq->i_len == 0) { 28241056Speter ireq->i_len = space; /* return required space */ 28341056Speter return 0; /* NB: must not error */ 28441056Speter } 28541056Speter MALLOC(ap, struct ieee80211req_maclist *, space, 28643311Sdillon M_TEMP, M_NOWAIT); 28741056Speter if (ap == NULL) 28841056Speter return ENOMEM; 28941056Speter i = 0; 29041056Speter ACL_LOCK(as); 29141056Speter TAILQ_FOREACH(acl, &as->as_list, acl_list) { 29241056Speter IEEE80211_ADDR_COPY(ap[i].ml_macaddr, acl->acl_macaddr); 29341056Speter i++; 29441056Speter } 29541056Speter ACL_UNLOCK(as); 2961541Srgrimes if (ireq->i_len >= space) { 2971541Srgrimes error = copyout(ap, ireq->i_data, space); 2981541Srgrimes ireq->i_len = space; 2991541Srgrimes } else 3001541Srgrimes error = copyout(ap, ireq->i_data, ireq->i_len); 3011541Srgrimes FREE(ap, M_TEMP); 3021541Srgrimes return error; 3031541Srgrimes } 3041541Srgrimes return EINVAL; 3051541Srgrimes} 30610358Sjulian 30710358Sjulianstatic const struct ieee80211_aclator mac = { 30841056Speter .iac_name = "mac", 3091541Srgrimes .iac_attach = acl_attach, 3101541Srgrimes .iac_detach = acl_detach, 31129653Sdyson .iac_check = acl_check, 31229653Sdyson .iac_add = acl_add, 3131541Srgrimes .iac_remove = acl_remove, 3141541Srgrimes .iac_flush = acl_free_all, 3151541Srgrimes .iac_setpolicy = acl_setpolicy, 3161541Srgrimes .iac_getpolicy = acl_getpolicy, 3171541Srgrimes .iac_setioctl = acl_setioctl, 3181541Srgrimes .iac_getioctl = acl_getioctl, 3191541Srgrimes}; 3201541Srgrimes 3211541Srgrimes/* 3221541Srgrimes * Module glue. 32339975Sobrien */ 3241541Srgrimesstatic int 3251541Srgrimeswlan_acl_modevent(module_t mod, int type, void *unused) 32640435Speter{ 32740435Speter switch (type) { 32841056Speter case MOD_LOAD: 32940435Speter if (bootverbose) 33040435Speter printf("wlan: <802.11 MAC ACL support>\n"); 33141056Speter ieee80211_aclator_register(&mac); 33240435Speter return 0; 33340435Speter case MOD_UNLOAD: 33444549Sdfr ieee80211_aclator_unregister(&mac); 33540435Speter return 0; 33640435Speter } 33740435Speter return EINVAL; 33840435Speter} 33940435Speter 34040435Speterstatic moduledata_t wlan_acl_mod = { 34144549Sdfr "wlan_acl", 34240435Speter wlan_acl_modevent, 34340435Speter 0 34440435Speter}; 34540435SpeterDECLARE_MODULE(wlan_acl, wlan_acl_mod, SI_SUB_DRIVERS, SI_ORDER_FIRST); 34640435SpeterMODULE_VERSION(wlan_acl, 1); 34740435SpeterMODULE_DEPEND(wlan_acl, wlan, 1, 1, 1); 34840435Speter