ieee80211_acl.c revision 228622
132516Sgibbs/*- 240029Sgibbs * Copyright (c) 2004-2008 Sam Leffler, Errno Consulting 332516Sgibbs * All rights reserved. 432516Sgibbs * 532516Sgibbs * Redistribution and use in source and binary forms, with or without 632516Sgibbs * modification, are permitted provided that the following conditions 732516Sgibbs * are met: 832516Sgibbs * 1. Redistributions of source code must retain the above copyright 932516Sgibbs * notice, this list of conditions and the following disclaimer. 1032516Sgibbs * 2. Redistributions in binary form must reproduce the above copyright 1132516Sgibbs * notice, this list of conditions and the following disclaimer in the 1232516Sgibbs * documentation and/or other materials provided with the distribution. 1332516Sgibbs * 1432516Sgibbs * THIS SOFTWARE IS PROVIDED BY THE AUTHOR ``AS IS'' AND ANY EXPRESS OR 1532516Sgibbs * IMPLIED WARRANTIES, INCLUDING, BUT NOT LIMITED TO, THE IMPLIED WARRANTIES 1632516Sgibbs * OF MERCHANTABILITY AND FITNESS FOR A PARTICULAR PURPOSE ARE DISCLAIMED. 1732516Sgibbs * IN NO EVENT SHALL THE AUTHOR BE LIABLE FOR ANY DIRECT, INDIRECT, 1832516Sgibbs * INCIDENTAL, SPECIAL, EXEMPLARY, OR CONSEQUENTIAL DAMAGES (INCLUDING, BUT 1932516Sgibbs * NOT LIMITED TO, PROCUREMENT OF SUBSTITUTE GOODS OR SERVICES; LOSS OF USE, 2032516Sgibbs * DATA, OR PROFITS; OR BUSINESS INTERRUPTION) HOWEVER CAUSED AND ON ANY 2132516Sgibbs * THEORY OF LIABILITY, WHETHER IN CONTRACT, STRICT LIABILITY, OR TORT 2232516Sgibbs * (INCLUDING NEGLIGENCE OR OTHERWISE) ARISING IN ANY WAY OUT OF THE USE OF 2332516Sgibbs * THIS SOFTWARE, EVEN IF ADVISED OF THE POSSIBILITY OF SUCH DAMAGE. 2432516Sgibbs */ 2532516Sgibbs 2650477Speter#include <sys/cdefs.h> 2732516Sgibbs__FBSDID("$FreeBSD: head/sys/net80211/ieee80211_acl.c 228622 2011-12-17 10:32:31Z bschmidt $"); 2832516Sgibbs 2932516Sgibbs/* 3032516Sgibbs * IEEE 802.11 MAC ACL support. 3132516Sgibbs * 3267551Sjhb * When this module is loaded the sender address of each auth mgt 3367551Sjhb * frame is passed to the iac_check method and the module indicates 34112346Smux * if the frame should be accepted or rejected. If the policy is 3576827Salfred * set to ACL_POLICY_OPEN then all frames are accepted w/o checking 3679224Sdillon * the address. Otherwise, the address is looked up in the database 3776827Salfred * and if found the frame is either accepted (ACL_POLICY_ALLOW) 38104486Ssam * or rejected (ACL_POLICY_DENT). 39104486Ssam */ 4032516Sgibbs#include "opt_wlan.h" 4132516Sgibbs 4232516Sgibbs#include <sys/param.h> 43104486Ssam#include <sys/kernel.h> 4432516Sgibbs#include <sys/systm.h> 45112436Smux#include <sys/mbuf.h> 4632516Sgibbs#include <sys/module.h> 4732516Sgibbs#include <sys/queue.h> 4832516Sgibbs 49113228Sjake#include <sys/socket.h> 5032516Sgibbs 5132516Sgibbs#include <net/if.h> 5232516Sgibbs#include <net/if_media.h> 5335767Sgibbs#include <net/ethernet.h> 5432516Sgibbs#include <net/route.h> 5532516Sgibbs 5632516Sgibbs#include <net80211/ieee80211_var.h> 5732516Sgibbs 5832516Sgibbsenum { 5932516Sgibbs ACL_POLICY_OPEN = 0, /* open, don't check ACL's */ 6035767Sgibbs ACL_POLICY_ALLOW = 1, /* allow traffic from MAC */ 6132516Sgibbs ACL_POLICY_DENY = 2, /* deny traffic from MAC */ 6232516Sgibbs /* 6332516Sgibbs * NB: ACL_POLICY_RADIUS must be the same value as 6432516Sgibbs * IEEE80211_MACCMD_POLICY_RADIUS because of the way 6532516Sgibbs * acl_getpolicy() works. 6632516Sgibbs */ 6732516Sgibbs ACL_POLICY_RADIUS = 7, /* defer to RADIUS ACL server */ 6832516Sgibbs}; 6932516Sgibbs 7032516Sgibbs#define ACL_HASHSIZE 32 7132516Sgibbs 7260938Sjakestruct acl { 7332516Sgibbs TAILQ_ENTRY(acl) acl_list; 7432516Sgibbs LIST_ENTRY(acl) acl_hash; 7532516Sgibbs uint8_t acl_macaddr[IEEE80211_ADDR_LEN]; 7632516Sgibbs}; 7760938Sjakestruct aclstate { 7832516Sgibbs acl_lock_t as_lock; 7932516Sgibbs int as_policy; 8032516Sgibbs uint32_t as_nacls; 8132516Sgibbs TAILQ_HEAD(, acl) as_list; /* list of all ACL's */ 8232516Sgibbs LIST_HEAD(, acl) as_hash[ACL_HASHSIZE]; 8332516Sgibbs struct ieee80211vap *as_vap; 8432516Sgibbs}; 8532516Sgibbs 8632516Sgibbs/* simple hash is enough for variation of macaddr */ 8732516Sgibbs#define ACL_HASH(addr) \ 8832516Sgibbs (((const uint8_t *)(addr))[IEEE80211_ADDR_LEN - 1] % ACL_HASHSIZE) 8932516Sgibbs 9032516Sgibbsstatic MALLOC_DEFINE(M_80211_ACL, "acl", "802.11 station acl"); 9132516Sgibbs 9232516Sgibbsstatic int acl_free_all(struct ieee80211vap *); 9360938Sjake 9432516Sgibbs/* number of references from net80211 layer */ 9532516Sgibbsstatic int nrefs = 0; 9660938Sjake 9760938Sjakestatic int 9832516Sgibbsacl_attach(struct ieee80211vap *vap) 9932516Sgibbs{ 100112346Smux struct aclstate *as; 10132516Sgibbs 102113228Sjake as = (struct aclstate *) malloc(sizeof(struct aclstate), 103113228Sjake M_80211_ACL, M_NOWAIT | M_ZERO); 104112569Sjake if (as == NULL) 10532516Sgibbs return 0; 10632516Sgibbs ACL_LOCK_INIT(as, "acl"); 10732516Sgibbs TAILQ_INIT(&as->as_list); 10832516Sgibbs as->as_policy = ACL_POLICY_OPEN; 109112346Smux as->as_vap = vap; 110112346Smux vap->iv_as = as; 111112346Smux nrefs++; /* NB: we assume caller locking */ 11295076Salfred return 1; 11395076Salfred} 11495076Salfred 11595076Salfredstatic void 11695076Salfredacl_detach(struct ieee80211vap *vap) 11795076Salfred{ 11895076Salfred struct aclstate *as = vap->iv_as; 11995076Salfred 12032516Sgibbs KASSERT(nrefs > 0, ("imbalanced attach/detach")); 12132516Sgibbs nrefs--; /* NB: we assume caller locking */ 12232516Sgibbs 12332516Sgibbs acl_free_all(vap); 12432516Sgibbs vap->iv_as = NULL; 12532516Sgibbs ACL_LOCK_DESTROY(as); 12632516Sgibbs free(as, M_80211_ACL); 12732516Sgibbs} 12832516Sgibbs 12932516Sgibbsstatic __inline struct acl * 13032516Sgibbs_find_acl(struct aclstate *as, const uint8_t *macaddr) 13132516Sgibbs{ 13232516Sgibbs struct acl *acl; 13332516Sgibbs int hash; 13432516Sgibbs 13532516Sgibbs hash = ACL_HASH(macaddr); 13632516Sgibbs LIST_FOREACH(acl, &as->as_hash[hash], acl_hash) { 13732516Sgibbs if (IEEE80211_ADDR_EQ(acl->acl_macaddr, macaddr)) 13835767Sgibbs return acl; 13932516Sgibbs } 14032516Sgibbs return NULL; 14132516Sgibbs} 14232516Sgibbs 14335767Sgibbsstatic void 14435767Sgibbs_acl_free(struct aclstate *as, struct acl *acl) 14535767Sgibbs{ 14635767Sgibbs ACL_LOCK_ASSERT(as); 14735767Sgibbs 14832516Sgibbs TAILQ_REMOVE(&as->as_list, acl, acl_list); 14932516Sgibbs LIST_REMOVE(acl, acl_hash); 15032516Sgibbs free(acl, M_80211_ACL); 15132516Sgibbs as->as_nacls--; 15232516Sgibbs} 15332516Sgibbs 15432516Sgibbsstatic int 15532516Sgibbsacl_check(struct ieee80211vap *vap, const struct ieee80211_frame *wh) 15632516Sgibbs{ 15732516Sgibbs struct aclstate *as = vap->iv_as; 15832516Sgibbs 15932516Sgibbs switch (as->as_policy) { 16048449Smjacob case ACL_POLICY_OPEN: 16132516Sgibbs case ACL_POLICY_RADIUS: 162112569Sjake return 1; 163112569Sjake case ACL_POLICY_ALLOW: 164112569Sjake return _find_acl(as, wh->i_addr2) != NULL; 16532516Sgibbs case ACL_POLICY_DENY: 16632516Sgibbs return _find_acl(as, wh->i_addr2) == NULL; 16732516Sgibbs } 16832516Sgibbs return 0; /* should not happen */ 16932516Sgibbs} 17032516Sgibbs 17132516Sgibbsstatic int 17232516Sgibbsacl_add(struct ieee80211vap *vap, const uint8_t mac[IEEE80211_ADDR_LEN]) 17332516Sgibbs{ 17432516Sgibbs struct aclstate *as = vap->iv_as; 17532516Sgibbs struct acl *acl, *new; 17632516Sgibbs int hash; 17732516Sgibbs 17832516Sgibbs new = (struct acl *) malloc(sizeof(struct acl), M_80211_ACL, M_NOWAIT | M_ZERO); 17932516Sgibbs if (new == NULL) { 18032516Sgibbs IEEE80211_DPRINTF(vap, IEEE80211_MSG_ACL, 18132516Sgibbs "ACL: add %s failed, no memory\n", ether_sprintf(mac)); 18235767Sgibbs /* XXX statistic */ 18332516Sgibbs return ENOMEM; 18432516Sgibbs } 18532516Sgibbs 18635256Sdes ACL_LOCK(as); 18732516Sgibbs hash = ACL_HASH(mac); 18832516Sgibbs LIST_FOREACH(acl, &as->as_hash[hash], acl_hash) { 18932516Sgibbs if (IEEE80211_ADDR_EQ(acl->acl_macaddr, mac)) { 19032516Sgibbs ACL_UNLOCK(as); 19132516Sgibbs free(new, M_80211_ACL); 192112436Smux IEEE80211_DPRINTF(vap, IEEE80211_MSG_ACL, 193112436Smux "ACL: add %s failed, already present\n", 19432516Sgibbs ether_sprintf(mac)); 19532516Sgibbs return EEXIST; 196112569Sjake } 197112569Sjake } 19832516Sgibbs IEEE80211_ADDR_COPY(new->acl_macaddr, mac); 19932516Sgibbs TAILQ_INSERT_TAIL(&as->as_list, new, acl_list); 20032516Sgibbs LIST_INSERT_HEAD(&as->as_hash[hash], new, acl_hash); 20132516Sgibbs as->as_nacls++; 20232516Sgibbs ACL_UNLOCK(as); 20332516Sgibbs 20432516Sgibbs IEEE80211_DPRINTF(vap, IEEE80211_MSG_ACL, 20535767Sgibbs "ACL: add %s\n", ether_sprintf(mac)); 20632516Sgibbs return 0; 20732516Sgibbs} 20832516Sgibbs 20932516Sgibbsstatic int 21032516Sgibbsacl_remove(struct ieee80211vap *vap, const uint8_t mac[IEEE80211_ADDR_LEN]) 21132516Sgibbs{ 21232516Sgibbs struct aclstate *as = vap->iv_as; 21332516Sgibbs struct acl *acl; 21432516Sgibbs 21532516Sgibbs ACL_LOCK(as); 21632516Sgibbs acl = _find_acl(as, mac); 21735767Sgibbs if (acl != NULL) 21835767Sgibbs _acl_free(as, acl); 21932516Sgibbs ACL_UNLOCK(as); 22032516Sgibbs 22132516Sgibbs IEEE80211_DPRINTF(vap, IEEE80211_MSG_ACL, 22232516Sgibbs "ACL: remove %s%s\n", ether_sprintf(mac), 22332516Sgibbs acl == NULL ? ", not present" : ""); 22432516Sgibbs 22532516Sgibbs return (acl == NULL ? ENOENT : 0); 22632516Sgibbs} 22732516Sgibbs 22832516Sgibbsstatic int 22932516Sgibbsacl_free_all(struct ieee80211vap *vap) 23032516Sgibbs{ 23132516Sgibbs struct aclstate *as = vap->iv_as; 23232516Sgibbs struct acl *acl; 23332516Sgibbs 23432516Sgibbs IEEE80211_DPRINTF(vap, IEEE80211_MSG_ACL, "ACL: %s\n", "free all"); 23532516Sgibbs 23632516Sgibbs ACL_LOCK(as); 23732516Sgibbs while ((acl = TAILQ_FIRST(&as->as_list)) != NULL) 23832516Sgibbs _acl_free(as, acl); 23932516Sgibbs ACL_UNLOCK(as); 24032516Sgibbs 241112436Smux return 0; 24232516Sgibbs} 24332516Sgibbs 24440029Sgibbsstatic int 24540029Sgibbsacl_setpolicy(struct ieee80211vap *vap, int policy) 24640029Sgibbs{ 24740029Sgibbs struct aclstate *as = vap->iv_as; 24840029Sgibbs 24940029Sgibbs IEEE80211_DPRINTF(vap, IEEE80211_MSG_ACL, 25040029Sgibbs "ACL: set policy to %u\n", policy); 25140029Sgibbs 25232516Sgibbs switch (policy) { 25332516Sgibbs case IEEE80211_MACCMD_POLICY_OPEN: 25432516Sgibbs as->as_policy = ACL_POLICY_OPEN; 25532516Sgibbs break; 25632516Sgibbs case IEEE80211_MACCMD_POLICY_ALLOW: 25732516Sgibbs as->as_policy = ACL_POLICY_ALLOW; 25832516Sgibbs break; 25932516Sgibbs case IEEE80211_MACCMD_POLICY_DENY: 26032516Sgibbs as->as_policy = ACL_POLICY_DENY; 26132516Sgibbs break; 26232516Sgibbs case IEEE80211_MACCMD_POLICY_RADIUS: 26332516Sgibbs as->as_policy = ACL_POLICY_RADIUS; 26432516Sgibbs break; 26532516Sgibbs default: 26632516Sgibbs return EINVAL; 26732516Sgibbs } 268112569Sjake return 0; 26932516Sgibbs} 27032516Sgibbs 27132516Sgibbsstatic int 27232516Sgibbsacl_getpolicy(struct ieee80211vap *vap) 27369781Sdwmalone{ 27469781Sdwmalone struct aclstate *as = vap->iv_as; 27535767Sgibbs 27669781Sdwmalone return as->as_policy; 27769781Sdwmalone} 27869781Sdwmalone 27969781Sdwmalonestatic int 28032516Sgibbsacl_setioctl(struct ieee80211vap *vap, struct ieee80211req *ireq) 28132516Sgibbs{ 28232516Sgibbs 28332516Sgibbs return EINVAL; 28432516Sgibbs} 28535767Sgibbs 28635767Sgibbsstatic int 28735767Sgibbsacl_getioctl(struct ieee80211vap *vap, struct ieee80211req *ireq) 28832516Sgibbs{ 28932516Sgibbs struct aclstate *as = vap->iv_as; 29035767Sgibbs struct acl *acl; 29135767Sgibbs struct ieee80211req_maclist *ap; 29235767Sgibbs int error; 29335767Sgibbs uint32_t i, space; 29435767Sgibbs 29535767Sgibbs switch (ireq->i_val) { 29635767Sgibbs case IEEE80211_MACCMD_POLICY: 29735767Sgibbs ireq->i_val = as->as_policy; 298113228Sjake return 0; 29932516Sgibbs case IEEE80211_MACCMD_LIST: 300113228Sjake space = as->as_nacls * IEEE80211_ADDR_LEN; 301113228Sjake if (ireq->i_len == 0) { 30235767Sgibbs ireq->i_len = space; /* return required space */ 30335767Sgibbs return 0; /* NB: must not error */ 30435767Sgibbs } 30535767Sgibbs ap = (struct ieee80211req_maclist *) malloc(space, 30635767Sgibbs M_TEMP, M_NOWAIT); 30735767Sgibbs if (ap == NULL) 30835767Sgibbs return ENOMEM; 30932516Sgibbs i = 0; 31032516Sgibbs ACL_LOCK(as); 31140029Sgibbs TAILQ_FOREACH(acl, &as->as_list, acl_list) { 31232516Sgibbs IEEE80211_ADDR_COPY(ap[i].ml_macaddr, acl->acl_macaddr); 31332516Sgibbs i++; 31432516Sgibbs } 31532516Sgibbs ACL_UNLOCK(as); 31632516Sgibbs if (ireq->i_len >= space) { 31732516Sgibbs error = copyout(ap, ireq->i_data, space); 31832516Sgibbs ireq->i_len = space; 31932516Sgibbs } else 32032516Sgibbs error = copyout(ap, ireq->i_data, ireq->i_len); 32132516Sgibbs free(ap, M_TEMP); 32232516Sgibbs return error; 32332516Sgibbs } 32432516Sgibbs return EINVAL; 32532516Sgibbs} 32632516Sgibbs 32732516Sgibbsstatic const struct ieee80211_aclator mac = { 32832516Sgibbs .iac_name = "mac", 32932516Sgibbs .iac_attach = acl_attach, 33032516Sgibbs .iac_detach = acl_detach, 33132516Sgibbs .iac_check = acl_check, 33232516Sgibbs .iac_add = acl_add, 33332516Sgibbs .iac_remove = acl_remove, 33435767Sgibbs .iac_flush = acl_free_all, 33535767Sgibbs .iac_setpolicy = acl_setpolicy, 33635767Sgibbs .iac_getpolicy = acl_getpolicy, 33735767Sgibbs .iac_setioctl = acl_setioctl, 33835767Sgibbs .iac_getioctl = acl_getioctl, 33935767Sgibbs}; 34035767SgibbsIEEE80211_ACL_MODULE(wlan_acl, mac, 1); 341110030Sscottl