132176cfdSRui Paulo /*- 232176cfdSRui Paulo * Copyright (c) 2004-2008 Sam Leffler, Errno Consulting 3841ab66cSSepherosa Ziehau * All rights reserved. 4841ab66cSSepherosa Ziehau * 5841ab66cSSepherosa Ziehau * Redistribution and use in source and binary forms, with or without 6841ab66cSSepherosa Ziehau * modification, are permitted provided that the following conditions 7841ab66cSSepherosa Ziehau * are met: 8841ab66cSSepherosa Ziehau * 1. Redistributions of source code must retain the above copyright 9841ab66cSSepherosa Ziehau * notice, this list of conditions and the following disclaimer. 10841ab66cSSepherosa Ziehau * 2. Redistributions in binary form must reproduce the above copyright 11841ab66cSSepherosa Ziehau * notice, this list of conditions and the following disclaimer in the 12841ab66cSSepherosa Ziehau * documentation and/or other materials provided with the distribution. 13841ab66cSSepherosa Ziehau * 14841ab66cSSepherosa Ziehau * THIS SOFTWARE IS PROVIDED BY THE AUTHOR ``AS IS'' AND ANY EXPRESS OR 15841ab66cSSepherosa Ziehau * IMPLIED WARRANTIES, INCLUDING, BUT NOT LIMITED TO, THE IMPLIED WARRANTIES 16841ab66cSSepherosa Ziehau * OF MERCHANTABILITY AND FITNESS FOR A PARTICULAR PURPOSE ARE DISCLAIMED. 17841ab66cSSepherosa Ziehau * IN NO EVENT SHALL THE AUTHOR BE LIABLE FOR ANY DIRECT, INDIRECT, 18841ab66cSSepherosa Ziehau * INCIDENTAL, SPECIAL, EXEMPLARY, OR CONSEQUENTIAL DAMAGES (INCLUDING, BUT 19841ab66cSSepherosa Ziehau * NOT LIMITED TO, PROCUREMENT OF SUBSTITUTE GOODS OR SERVICES; LOSS OF USE, 20841ab66cSSepherosa Ziehau * DATA, OR PROFITS; OR BUSINESS INTERRUPTION) HOWEVER CAUSED AND ON ANY 21841ab66cSSepherosa Ziehau * THEORY OF LIABILITY, WHETHER IN CONTRACT, STRICT LIABILITY, OR TORT 22841ab66cSSepherosa Ziehau * (INCLUDING NEGLIGENCE OR OTHERWISE) ARISING IN ANY WAY OUT OF THE USE OF 23841ab66cSSepherosa Ziehau * THIS SOFTWARE, EVEN IF ADVISED OF THE POSSIBILITY OF SUCH DAMAGE. 24841ab66cSSepherosa Ziehau * 2532176cfdSRui Paulo * $FreeBSD: head/sys/net80211/ieee80211_acl.c 186302 2008-12-18 23:00:09Z sam $ 26841ab66cSSepherosa Ziehau */ 27841ab66cSSepherosa Ziehau 28841ab66cSSepherosa Ziehau /* 29841ab66cSSepherosa Ziehau * IEEE 802.11 MAC ACL support. 30841ab66cSSepherosa Ziehau * 3132176cfdSRui Paulo * When this module is loaded the sender address of each auth mgt 32841ab66cSSepherosa Ziehau * frame is passed to the iac_check method and the module indicates 33841ab66cSSepherosa Ziehau * if the frame should be accepted or rejected. If the policy is 34841ab66cSSepherosa Ziehau * set to ACL_POLICY_OPEN then all frames are accepted w/o checking 35841ab66cSSepherosa Ziehau * the address. Otherwise, the address is looked up in the database 36841ab66cSSepherosa Ziehau * and if found the frame is either accepted (ACL_POLICY_ALLOW) 37841ab66cSSepherosa Ziehau * or rejected (ACL_POLICY_DENT). 38841ab66cSSepherosa Ziehau */ 3932176cfdSRui Paulo #include "opt_wlan.h" 4032176cfdSRui Paulo 41841ab66cSSepherosa Ziehau #include <sys/param.h> 42841ab66cSSepherosa Ziehau #include <sys/kernel.h> 43841ab66cSSepherosa Ziehau #include <sys/systm.h> 44841ab66cSSepherosa Ziehau #include <sys/mbuf.h> 45841ab66cSSepherosa Ziehau #include <sys/module.h> 46841ab66cSSepherosa Ziehau #include <sys/queue.h> 47841ab66cSSepherosa Ziehau 48841ab66cSSepherosa Ziehau #include <sys/socket.h> 49841ab66cSSepherosa Ziehau 50841ab66cSSepherosa Ziehau #include <net/if.h> 51841ab66cSSepherosa Ziehau #include <net/if_media.h> 52841ab66cSSepherosa Ziehau #include <net/ethernet.h> 53841ab66cSSepherosa Ziehau #include <net/route.h> 54841ab66cSSepherosa Ziehau 55841ab66cSSepherosa Ziehau #include <netproto/802_11/ieee80211_var.h> 56841ab66cSSepherosa Ziehau 57841ab66cSSepherosa Ziehau enum { 58841ab66cSSepherosa Ziehau ACL_POLICY_OPEN = 0, /* open, don't check ACL's */ 59841ab66cSSepherosa Ziehau ACL_POLICY_ALLOW = 1, /* allow traffic from MAC */ 60841ab66cSSepherosa Ziehau ACL_POLICY_DENY = 2, /* deny traffic from MAC */ 6132176cfdSRui Paulo /* 6232176cfdSRui Paulo * NB: ACL_POLICY_RADIUS must be the same value as 6332176cfdSRui Paulo * IEEE80211_MACCMD_POLICY_RADIUS because of the way 6432176cfdSRui Paulo * acl_getpolicy() works. 6532176cfdSRui Paulo */ 6632176cfdSRui Paulo ACL_POLICY_RADIUS = 7, /* defer to RADIUS ACL server */ 67841ab66cSSepherosa Ziehau }; 68841ab66cSSepherosa Ziehau 69841ab66cSSepherosa Ziehau #define ACL_HASHSIZE 32 70841ab66cSSepherosa Ziehau 71841ab66cSSepherosa Ziehau struct acl { 72841ab66cSSepherosa Ziehau TAILQ_ENTRY(acl) acl_list; 73841ab66cSSepherosa Ziehau LIST_ENTRY(acl) acl_hash; 74841ab66cSSepherosa Ziehau uint8_t acl_macaddr[IEEE80211_ADDR_LEN]; 75841ab66cSSepherosa Ziehau }; 76841ab66cSSepherosa Ziehau struct aclstate { 77841ab66cSSepherosa Ziehau int as_policy; 78841ab66cSSepherosa Ziehau int as_nacls; 79841ab66cSSepherosa Ziehau TAILQ_HEAD(, acl) as_list; /* list of all ACL's */ 80841ab66cSSepherosa Ziehau LIST_HEAD(, acl) as_hash[ACL_HASHSIZE]; 8132176cfdSRui Paulo struct ieee80211vap *as_vap; 82841ab66cSSepherosa Ziehau }; 83841ab66cSSepherosa Ziehau 84841ab66cSSepherosa Ziehau /* simple hash is enough for variation of macaddr */ 85841ab66cSSepherosa Ziehau #define ACL_HASH(addr) \ 86841ab66cSSepherosa Ziehau (((const uint8_t *)(addr))[IEEE80211_ADDR_LEN - 1] % ACL_HASHSIZE) 87841ab66cSSepherosa Ziehau 88841ab66cSSepherosa Ziehau MALLOC_DEFINE(M_80211_ACL, "acl", "802.11 station acl"); 89841ab66cSSepherosa Ziehau 9032176cfdSRui Paulo static int acl_free_all(struct ieee80211vap *); 9132176cfdSRui Paulo 9232176cfdSRui Paulo /* number of references from net80211 layer */ 9332176cfdSRui Paulo static int nrefs = 0; 94841ab66cSSepherosa Ziehau 95841ab66cSSepherosa Ziehau static int 9632176cfdSRui Paulo acl_attach(struct ieee80211vap *vap) 97841ab66cSSepherosa Ziehau { 98841ab66cSSepherosa Ziehau struct aclstate *as; 99841ab66cSSepherosa Ziehau 10032176cfdSRui Paulo as = (struct aclstate *) kmalloc(sizeof(struct aclstate), 101c567b546SJoe Talbott M_80211_ACL, M_INTWAIT | M_ZERO); 102841ab66cSSepherosa Ziehau if (as == NULL) 103841ab66cSSepherosa Ziehau return 0; 104841ab66cSSepherosa Ziehau TAILQ_INIT(&as->as_list); 105841ab66cSSepherosa Ziehau as->as_policy = ACL_POLICY_OPEN; 10632176cfdSRui Paulo as->as_vap = vap; 10732176cfdSRui Paulo vap->iv_as = as; 10832176cfdSRui Paulo nrefs++; /* NB: we assume caller locking */ 109841ab66cSSepherosa Ziehau return 1; 110841ab66cSSepherosa Ziehau } 111841ab66cSSepherosa Ziehau 112841ab66cSSepherosa Ziehau static void 11332176cfdSRui Paulo acl_detach(struct ieee80211vap *vap) 114841ab66cSSepherosa Ziehau { 11532176cfdSRui Paulo struct aclstate *as = vap->iv_as; 116841ab66cSSepherosa Ziehau 11732176cfdSRui Paulo KASSERT(nrefs > 0, ("imbalanced attach/detach")); 11832176cfdSRui Paulo nrefs--; /* NB: we assume caller locking */ 11932176cfdSRui Paulo 12032176cfdSRui Paulo acl_free_all(vap); 12132176cfdSRui Paulo vap->iv_as = NULL; 12232176cfdSRui Paulo kfree(as, M_80211_ACL); 123841ab66cSSepherosa Ziehau } 124841ab66cSSepherosa Ziehau 125841ab66cSSepherosa Ziehau static __inline struct acl * 126841ab66cSSepherosa Ziehau _find_acl(struct aclstate *as, const uint8_t *macaddr) 127841ab66cSSepherosa Ziehau { 128841ab66cSSepherosa Ziehau struct acl *acl; 129841ab66cSSepherosa Ziehau int hash; 130841ab66cSSepherosa Ziehau 131841ab66cSSepherosa Ziehau hash = ACL_HASH(macaddr); 132841ab66cSSepherosa Ziehau LIST_FOREACH(acl, &as->as_hash[hash], acl_hash) { 133841ab66cSSepherosa Ziehau if (IEEE80211_ADDR_EQ(acl->acl_macaddr, macaddr)) 134841ab66cSSepherosa Ziehau return acl; 135841ab66cSSepherosa Ziehau } 136841ab66cSSepherosa Ziehau return NULL; 137841ab66cSSepherosa Ziehau } 138841ab66cSSepherosa Ziehau 139841ab66cSSepherosa Ziehau static void 140841ab66cSSepherosa Ziehau _acl_free(struct aclstate *as, struct acl *acl) 141841ab66cSSepherosa Ziehau { 142841ab66cSSepherosa Ziehau 143841ab66cSSepherosa Ziehau TAILQ_REMOVE(&as->as_list, acl, acl_list); 144841ab66cSSepherosa Ziehau LIST_REMOVE(acl, acl_hash); 145efda3bd0SMatthew Dillon kfree(acl, M_80211_ACL); 146841ab66cSSepherosa Ziehau as->as_nacls--; 147841ab66cSSepherosa Ziehau } 148841ab66cSSepherosa Ziehau 149841ab66cSSepherosa Ziehau static int 15032176cfdSRui Paulo acl_check(struct ieee80211vap *vap, const uint8_t mac[IEEE80211_ADDR_LEN]) 151841ab66cSSepherosa Ziehau { 15232176cfdSRui Paulo struct aclstate *as = vap->iv_as; 153841ab66cSSepherosa Ziehau 154841ab66cSSepherosa Ziehau switch (as->as_policy) { 155841ab66cSSepherosa Ziehau case ACL_POLICY_OPEN: 15632176cfdSRui Paulo case ACL_POLICY_RADIUS: 157841ab66cSSepherosa Ziehau return 1; 158841ab66cSSepherosa Ziehau case ACL_POLICY_ALLOW: 159841ab66cSSepherosa Ziehau return _find_acl(as, mac) != NULL; 160841ab66cSSepherosa Ziehau case ACL_POLICY_DENY: 161841ab66cSSepherosa Ziehau return _find_acl(as, mac) == NULL; 162841ab66cSSepherosa Ziehau } 163841ab66cSSepherosa Ziehau return 0; /* should not happen */ 164841ab66cSSepherosa Ziehau } 165841ab66cSSepherosa Ziehau 166841ab66cSSepherosa Ziehau static int 16732176cfdSRui Paulo acl_add(struct ieee80211vap *vap, const uint8_t mac[IEEE80211_ADDR_LEN]) 168841ab66cSSepherosa Ziehau { 16932176cfdSRui Paulo struct aclstate *as = vap->iv_as; 170841ab66cSSepherosa Ziehau struct acl *acl, *new; 171*1e290df3SAntonio Huete Jimenez char ethstr[ETHER_ADDRSTRLEN + 1]; 172841ab66cSSepherosa Ziehau int hash; 173841ab66cSSepherosa Ziehau 174c567b546SJoe Talbott new = (struct acl *) kmalloc(sizeof(struct acl), M_80211_ACL, M_INTWAIT | M_ZERO); 175841ab66cSSepherosa Ziehau if (new == NULL) { 17632176cfdSRui Paulo IEEE80211_DPRINTF(vap, IEEE80211_MSG_ACL, 177*1e290df3SAntonio Huete Jimenez "ACL: add %s failed, no memory\n", kether_ntoa(mac, ethstr)); 178841ab66cSSepherosa Ziehau /* XXX statistic */ 179841ab66cSSepherosa Ziehau return ENOMEM; 180841ab66cSSepherosa Ziehau } 181841ab66cSSepherosa Ziehau 182841ab66cSSepherosa Ziehau hash = ACL_HASH(mac); 183841ab66cSSepherosa Ziehau LIST_FOREACH(acl, &as->as_hash[hash], acl_hash) { 184841ab66cSSepherosa Ziehau if (IEEE80211_ADDR_EQ(acl->acl_macaddr, mac)) { 18532176cfdSRui Paulo kfree(new, M_80211_ACL); 18632176cfdSRui Paulo IEEE80211_DPRINTF(vap, IEEE80211_MSG_ACL, 187*1e290df3SAntonio Huete Jimenez "ACL: add %s failed, already present\n", 188*1e290df3SAntonio Huete Jimenez kether_ntoa(mac, ethstr)); 189841ab66cSSepherosa Ziehau return EEXIST; 190841ab66cSSepherosa Ziehau } 191841ab66cSSepherosa Ziehau } 192841ab66cSSepherosa Ziehau IEEE80211_ADDR_COPY(new->acl_macaddr, mac); 193841ab66cSSepherosa Ziehau TAILQ_INSERT_TAIL(&as->as_list, new, acl_list); 194841ab66cSSepherosa Ziehau LIST_INSERT_HEAD(&as->as_hash[hash], new, acl_hash); 195841ab66cSSepherosa Ziehau as->as_nacls++; 196841ab66cSSepherosa Ziehau 19732176cfdSRui Paulo IEEE80211_DPRINTF(vap, IEEE80211_MSG_ACL, 198*1e290df3SAntonio Huete Jimenez "ACL: add %s\n", kether_ntoa(mac, ethstr)); 199841ab66cSSepherosa Ziehau return 0; 200841ab66cSSepherosa Ziehau } 201841ab66cSSepherosa Ziehau 202841ab66cSSepherosa Ziehau static int 20332176cfdSRui Paulo acl_remove(struct ieee80211vap *vap, const uint8_t mac[IEEE80211_ADDR_LEN]) 204841ab66cSSepherosa Ziehau { 20532176cfdSRui Paulo struct aclstate *as = vap->iv_as; 206841ab66cSSepherosa Ziehau struct acl *acl; 207*1e290df3SAntonio Huete Jimenez char ethstr[ETHER_ADDRSTRLEN + 1]; 208841ab66cSSepherosa Ziehau 209841ab66cSSepherosa Ziehau acl = _find_acl(as, mac); 210841ab66cSSepherosa Ziehau if (acl != NULL) 211841ab66cSSepherosa Ziehau _acl_free(as, acl); 212841ab66cSSepherosa Ziehau 21332176cfdSRui Paulo IEEE80211_DPRINTF(vap, IEEE80211_MSG_ACL, 214*1e290df3SAntonio Huete Jimenez "ACL: remove %s%s\n", kether_ntoa(mac, ethstr), 215841ab66cSSepherosa Ziehau acl == NULL ? ", not present" : ""); 216841ab66cSSepherosa Ziehau 217841ab66cSSepherosa Ziehau return (acl == NULL ? ENOENT : 0); 218841ab66cSSepherosa Ziehau } 219841ab66cSSepherosa Ziehau 220841ab66cSSepherosa Ziehau static int 22132176cfdSRui Paulo acl_free_all(struct ieee80211vap *vap) 222841ab66cSSepherosa Ziehau { 22332176cfdSRui Paulo struct aclstate *as = vap->iv_as; 224841ab66cSSepherosa Ziehau struct acl *acl; 225841ab66cSSepherosa Ziehau 22632176cfdSRui Paulo IEEE80211_DPRINTF(vap, IEEE80211_MSG_ACL, "ACL: %s\n", "free all"); 227841ab66cSSepherosa Ziehau 228841ab66cSSepherosa Ziehau while ((acl = TAILQ_FIRST(&as->as_list)) != NULL) 229841ab66cSSepherosa Ziehau _acl_free(as, acl); 230841ab66cSSepherosa Ziehau 231841ab66cSSepherosa Ziehau return 0; 232841ab66cSSepherosa Ziehau } 233841ab66cSSepherosa Ziehau 234841ab66cSSepherosa Ziehau static int 23532176cfdSRui Paulo acl_setpolicy(struct ieee80211vap *vap, int policy) 236841ab66cSSepherosa Ziehau { 23732176cfdSRui Paulo struct aclstate *as = vap->iv_as; 238841ab66cSSepherosa Ziehau 23932176cfdSRui Paulo IEEE80211_DPRINTF(vap, IEEE80211_MSG_ACL, 240841ab66cSSepherosa Ziehau "ACL: set policy to %u\n", policy); 241841ab66cSSepherosa Ziehau 242841ab66cSSepherosa Ziehau switch (policy) { 243841ab66cSSepherosa Ziehau case IEEE80211_MACCMD_POLICY_OPEN: 244841ab66cSSepherosa Ziehau as->as_policy = ACL_POLICY_OPEN; 245841ab66cSSepherosa Ziehau break; 246841ab66cSSepherosa Ziehau case IEEE80211_MACCMD_POLICY_ALLOW: 247841ab66cSSepherosa Ziehau as->as_policy = ACL_POLICY_ALLOW; 248841ab66cSSepherosa Ziehau break; 249841ab66cSSepherosa Ziehau case IEEE80211_MACCMD_POLICY_DENY: 250841ab66cSSepherosa Ziehau as->as_policy = ACL_POLICY_DENY; 251841ab66cSSepherosa Ziehau break; 25232176cfdSRui Paulo case IEEE80211_MACCMD_POLICY_RADIUS: 25332176cfdSRui Paulo as->as_policy = ACL_POLICY_RADIUS; 25432176cfdSRui Paulo break; 255841ab66cSSepherosa Ziehau default: 256841ab66cSSepherosa Ziehau return EINVAL; 257841ab66cSSepherosa Ziehau } 258841ab66cSSepherosa Ziehau return 0; 259841ab66cSSepherosa Ziehau } 260841ab66cSSepherosa Ziehau 261841ab66cSSepherosa Ziehau static int 26232176cfdSRui Paulo acl_getpolicy(struct ieee80211vap *vap) 263841ab66cSSepherosa Ziehau { 26432176cfdSRui Paulo struct aclstate *as = vap->iv_as; 26532176cfdSRui Paulo 26632176cfdSRui Paulo return as->as_policy; 267841ab66cSSepherosa Ziehau } 268841ab66cSSepherosa Ziehau 269841ab66cSSepherosa Ziehau static int 27032176cfdSRui Paulo acl_setioctl(struct ieee80211vap *vap, struct ieee80211req *ireq) 271841ab66cSSepherosa Ziehau { 27232176cfdSRui Paulo 273841ab66cSSepherosa Ziehau return EINVAL; 274841ab66cSSepherosa Ziehau } 275841ab66cSSepherosa Ziehau 276841ab66cSSepherosa Ziehau static int 27732176cfdSRui Paulo acl_getioctl(struct ieee80211vap *vap, struct ieee80211req *ireq) 278841ab66cSSepherosa Ziehau { 27932176cfdSRui Paulo struct aclstate *as = vap->iv_as; 280841ab66cSSepherosa Ziehau struct acl *acl; 281841ab66cSSepherosa Ziehau struct ieee80211req_maclist *ap; 282841ab66cSSepherosa Ziehau int error, space, i; 283841ab66cSSepherosa Ziehau 284841ab66cSSepherosa Ziehau switch (ireq->i_val) { 285841ab66cSSepherosa Ziehau case IEEE80211_MACCMD_POLICY: 286841ab66cSSepherosa Ziehau ireq->i_val = as->as_policy; 287841ab66cSSepherosa Ziehau return 0; 288841ab66cSSepherosa Ziehau case IEEE80211_MACCMD_LIST: 289841ab66cSSepherosa Ziehau space = as->as_nacls * IEEE80211_ADDR_LEN; 290841ab66cSSepherosa Ziehau if (ireq->i_len == 0) { 291841ab66cSSepherosa Ziehau ireq->i_len = space; /* return required space */ 292841ab66cSSepherosa Ziehau return 0; /* NB: must not error */ 293841ab66cSSepherosa Ziehau } 29432176cfdSRui Paulo ap = (struct ieee80211req_maclist *) kmalloc(space, 295c567b546SJoe Talbott M_TEMP, M_INTWAIT); 296841ab66cSSepherosa Ziehau if (ap == NULL) 297841ab66cSSepherosa Ziehau return ENOMEM; 298841ab66cSSepherosa Ziehau i = 0; 299841ab66cSSepherosa Ziehau TAILQ_FOREACH(acl, &as->as_list, acl_list) { 300841ab66cSSepherosa Ziehau IEEE80211_ADDR_COPY(ap[i].ml_macaddr, acl->acl_macaddr); 301841ab66cSSepherosa Ziehau i++; 302841ab66cSSepherosa Ziehau } 303841ab66cSSepherosa Ziehau if (ireq->i_len >= space) { 304841ab66cSSepherosa Ziehau error = copyout(ap, ireq->i_data, space); 305841ab66cSSepherosa Ziehau ireq->i_len = space; 306841ab66cSSepherosa Ziehau } else 307841ab66cSSepherosa Ziehau error = copyout(ap, ireq->i_data, ireq->i_len); 308efda3bd0SMatthew Dillon kfree(ap, M_TEMP); 309841ab66cSSepherosa Ziehau return error; 310841ab66cSSepherosa Ziehau } 311841ab66cSSepherosa Ziehau return EINVAL; 312841ab66cSSepherosa Ziehau } 313841ab66cSSepherosa Ziehau 314841ab66cSSepherosa Ziehau static const struct ieee80211_aclator mac = { 315841ab66cSSepherosa Ziehau .iac_name = "mac", 316841ab66cSSepherosa Ziehau .iac_attach = acl_attach, 317841ab66cSSepherosa Ziehau .iac_detach = acl_detach, 318841ab66cSSepherosa Ziehau .iac_check = acl_check, 319841ab66cSSepherosa Ziehau .iac_add = acl_add, 320841ab66cSSepherosa Ziehau .iac_remove = acl_remove, 321841ab66cSSepherosa Ziehau .iac_flush = acl_free_all, 322841ab66cSSepherosa Ziehau .iac_setpolicy = acl_setpolicy, 323841ab66cSSepherosa Ziehau .iac_getpolicy = acl_getpolicy, 324841ab66cSSepherosa Ziehau .iac_setioctl = acl_setioctl, 325841ab66cSSepherosa Ziehau .iac_getioctl = acl_getioctl, 326841ab66cSSepherosa Ziehau }; 32732176cfdSRui Paulo IEEE80211_ACL_MODULE(wlan_acl, mac, 1); 328