18a1b9b6aSSam Leffler /*- 2b032f27cSSam Leffler * Copyright (c) 2004-2008 Sam Leffler, Errno Consulting 38a1b9b6aSSam Leffler * All rights reserved. 48a1b9b6aSSam Leffler * 58a1b9b6aSSam Leffler * Redistribution and use in source and binary forms, with or without 68a1b9b6aSSam Leffler * modification, are permitted provided that the following conditions 78a1b9b6aSSam Leffler * are met: 88a1b9b6aSSam Leffler * 1. Redistributions of source code must retain the above copyright 98a1b9b6aSSam Leffler * notice, this list of conditions and the following disclaimer. 108a1b9b6aSSam Leffler * 2. Redistributions in binary form must reproduce the above copyright 118a1b9b6aSSam Leffler * notice, this list of conditions and the following disclaimer in the 128a1b9b6aSSam Leffler * documentation and/or other materials provided with the distribution. 138a1b9b6aSSam Leffler * 148a1b9b6aSSam Leffler * THIS SOFTWARE IS PROVIDED BY THE AUTHOR ``AS IS'' AND ANY EXPRESS OR 158a1b9b6aSSam Leffler * IMPLIED WARRANTIES, INCLUDING, BUT NOT LIMITED TO, THE IMPLIED WARRANTIES 168a1b9b6aSSam Leffler * OF MERCHANTABILITY AND FITNESS FOR A PARTICULAR PURPOSE ARE DISCLAIMED. 178a1b9b6aSSam Leffler * IN NO EVENT SHALL THE AUTHOR BE LIABLE FOR ANY DIRECT, INDIRECT, 188a1b9b6aSSam Leffler * INCIDENTAL, SPECIAL, EXEMPLARY, OR CONSEQUENTIAL DAMAGES (INCLUDING, BUT 198a1b9b6aSSam Leffler * NOT LIMITED TO, PROCUREMENT OF SUBSTITUTE GOODS OR SERVICES; LOSS OF USE, 208a1b9b6aSSam Leffler * DATA, OR PROFITS; OR BUSINESS INTERRUPTION) HOWEVER CAUSED AND ON ANY 218a1b9b6aSSam Leffler * THEORY OF LIABILITY, WHETHER IN CONTRACT, STRICT LIABILITY, OR TORT 228a1b9b6aSSam Leffler * (INCLUDING NEGLIGENCE OR OTHERWISE) ARISING IN ANY WAY OUT OF THE USE OF 238a1b9b6aSSam Leffler * THIS SOFTWARE, EVEN IF ADVISED OF THE POSSIBILITY OF SUCH DAMAGE. 248a1b9b6aSSam Leffler */ 258a1b9b6aSSam Leffler 268a1b9b6aSSam Leffler #include <sys/cdefs.h> 278a1b9b6aSSam Leffler __FBSDID("$FreeBSD$"); 288a1b9b6aSSam Leffler 298a1b9b6aSSam Leffler /* 308a1b9b6aSSam Leffler * IEEE 802.11 MAC ACL support. 318a1b9b6aSSam Leffler * 32b032f27cSSam Leffler * When this module is loaded the sender address of each auth mgt 338a1b9b6aSSam Leffler * frame is passed to the iac_check method and the module indicates 348a1b9b6aSSam Leffler * if the frame should be accepted or rejected. If the policy is 358a1b9b6aSSam Leffler * set to ACL_POLICY_OPEN then all frames are accepted w/o checking 368a1b9b6aSSam Leffler * the address. Otherwise, the address is looked up in the database 378a1b9b6aSSam Leffler * and if found the frame is either accepted (ACL_POLICY_ALLOW) 388a1b9b6aSSam Leffler * or rejected (ACL_POLICY_DENT). 398a1b9b6aSSam Leffler */ 40b032f27cSSam Leffler #include "opt_wlan.h" 41b032f27cSSam Leffler 428a1b9b6aSSam Leffler #include <sys/param.h> 438a1b9b6aSSam Leffler #include <sys/kernel.h> 448a1b9b6aSSam Leffler #include <sys/systm.h> 458a1b9b6aSSam Leffler #include <sys/mbuf.h> 468a1b9b6aSSam Leffler #include <sys/module.h> 478a1b9b6aSSam Leffler #include <sys/queue.h> 488a1b9b6aSSam Leffler 498a1b9b6aSSam Leffler #include <sys/socket.h> 508a1b9b6aSSam Leffler 518a1b9b6aSSam Leffler #include <net/if.h> 528a1b9b6aSSam Leffler #include <net/if_media.h> 538a1b9b6aSSam Leffler #include <net/ethernet.h> 548a1b9b6aSSam Leffler #include <net/route.h> 558a1b9b6aSSam Leffler 568a1b9b6aSSam Leffler #include <net80211/ieee80211_var.h> 578a1b9b6aSSam Leffler 588a1b9b6aSSam Leffler enum { 598a1b9b6aSSam Leffler ACL_POLICY_OPEN = 0, /* open, don't check ACL's */ 608a1b9b6aSSam Leffler ACL_POLICY_ALLOW = 1, /* allow traffic from MAC */ 618a1b9b6aSSam Leffler ACL_POLICY_DENY = 2, /* deny traffic from MAC */ 62b032f27cSSam Leffler /* 63b032f27cSSam Leffler * NB: ACL_POLICY_RADIUS must be the same value as 64b032f27cSSam Leffler * IEEE80211_MACCMD_POLICY_RADIUS because of the way 65b032f27cSSam Leffler * acl_getpolicy() works. 66b032f27cSSam Leffler */ 67b032f27cSSam Leffler ACL_POLICY_RADIUS = 7, /* defer to RADIUS ACL server */ 688a1b9b6aSSam Leffler }; 698a1b9b6aSSam Leffler 708a1b9b6aSSam Leffler #define ACL_HASHSIZE 32 718a1b9b6aSSam Leffler 728a1b9b6aSSam Leffler struct acl { 738a1b9b6aSSam Leffler TAILQ_ENTRY(acl) acl_list; 748a1b9b6aSSam Leffler LIST_ENTRY(acl) acl_hash; 7568e8e04eSSam Leffler uint8_t acl_macaddr[IEEE80211_ADDR_LEN]; 768a1b9b6aSSam Leffler }; 778a1b9b6aSSam Leffler struct aclstate { 788a1b9b6aSSam Leffler acl_lock_t as_lock; 798a1b9b6aSSam Leffler int as_policy; 80188757f5SSam Leffler int as_nacls; 818a1b9b6aSSam Leffler TAILQ_HEAD(, acl) as_list; /* list of all ACL's */ 828a1b9b6aSSam Leffler LIST_HEAD(, acl) as_hash[ACL_HASHSIZE]; 83b032f27cSSam Leffler struct ieee80211vap *as_vap; 848a1b9b6aSSam Leffler }; 858a1b9b6aSSam Leffler 868a1b9b6aSSam Leffler /* simple hash is enough for variation of macaddr */ 878a1b9b6aSSam Leffler #define ACL_HASH(addr) \ 8868e8e04eSSam Leffler (((const uint8_t *)(addr))[IEEE80211_ADDR_LEN - 1] % ACL_HASHSIZE) 898a1b9b6aSSam Leffler 908a1b9b6aSSam Leffler MALLOC_DEFINE(M_80211_ACL, "acl", "802.11 station acl"); 918a1b9b6aSSam Leffler 92b032f27cSSam Leffler static int acl_free_all(struct ieee80211vap *); 93b032f27cSSam Leffler 94b032f27cSSam Leffler /* number of references from net80211 layer */ 95b032f27cSSam Leffler static int nrefs = 0; 968a1b9b6aSSam Leffler 978a1b9b6aSSam Leffler static int 98b032f27cSSam Leffler acl_attach(struct ieee80211vap *vap) 998a1b9b6aSSam Leffler { 1008a1b9b6aSSam Leffler struct aclstate *as; 1018a1b9b6aSSam Leffler 1028a1b9b6aSSam Leffler MALLOC(as, struct aclstate *, sizeof(struct aclstate), 103188757f5SSam Leffler M_80211_ACL, M_NOWAIT | M_ZERO); 1048a1b9b6aSSam Leffler if (as == NULL) 1058a1b9b6aSSam Leffler return 0; 1068a1b9b6aSSam Leffler ACL_LOCK_INIT(as, "acl"); 1078a1b9b6aSSam Leffler TAILQ_INIT(&as->as_list); 1088a1b9b6aSSam Leffler as->as_policy = ACL_POLICY_OPEN; 109b032f27cSSam Leffler as->as_vap = vap; 110b032f27cSSam Leffler vap->iv_as = as; 111b032f27cSSam Leffler nrefs++; /* NB: we assume caller locking */ 1128a1b9b6aSSam Leffler return 1; 1138a1b9b6aSSam Leffler } 1148a1b9b6aSSam Leffler 1158a1b9b6aSSam Leffler static void 116b032f27cSSam Leffler acl_detach(struct ieee80211vap *vap) 1178a1b9b6aSSam Leffler { 118b032f27cSSam Leffler struct aclstate *as = vap->iv_as; 1198a1b9b6aSSam Leffler 120b032f27cSSam Leffler KASSERT(nrefs > 0, ("imbalanced attach/detach")); 121b032f27cSSam Leffler nrefs--; /* NB: we assume caller locking */ 122b032f27cSSam Leffler 123b032f27cSSam Leffler acl_free_all(vap); 124b032f27cSSam Leffler vap->iv_as = NULL; 1258a1b9b6aSSam Leffler ACL_LOCK_DESTROY(as); 126b032f27cSSam Leffler FREE(as, M_80211_ACL); 1278a1b9b6aSSam Leffler } 1288a1b9b6aSSam Leffler 12968f5ddcdSSam Leffler static __inline struct acl * 13068e8e04eSSam Leffler _find_acl(struct aclstate *as, const uint8_t *macaddr) 1318a1b9b6aSSam Leffler { 1328a1b9b6aSSam Leffler struct acl *acl; 1338a1b9b6aSSam Leffler int hash; 1348a1b9b6aSSam Leffler 1358a1b9b6aSSam Leffler hash = ACL_HASH(macaddr); 1368a1b9b6aSSam Leffler LIST_FOREACH(acl, &as->as_hash[hash], acl_hash) { 1378a1b9b6aSSam Leffler if (IEEE80211_ADDR_EQ(acl->acl_macaddr, macaddr)) 1388a1b9b6aSSam Leffler return acl; 1398a1b9b6aSSam Leffler } 1408a1b9b6aSSam Leffler return NULL; 1418a1b9b6aSSam Leffler } 1428a1b9b6aSSam Leffler 1438a1b9b6aSSam Leffler static void 1448a1b9b6aSSam Leffler _acl_free(struct aclstate *as, struct acl *acl) 1458a1b9b6aSSam Leffler { 1468a1b9b6aSSam Leffler ACL_LOCK_ASSERT(as); 1478a1b9b6aSSam Leffler 1488a1b9b6aSSam Leffler TAILQ_REMOVE(&as->as_list, acl, acl_list); 1498a1b9b6aSSam Leffler LIST_REMOVE(acl, acl_hash); 1508a1b9b6aSSam Leffler FREE(acl, M_80211_ACL); 151188757f5SSam Leffler as->as_nacls--; 1528a1b9b6aSSam Leffler } 1538a1b9b6aSSam Leffler 1548a1b9b6aSSam Leffler static int 155b032f27cSSam Leffler acl_check(struct ieee80211vap *vap, const uint8_t mac[IEEE80211_ADDR_LEN]) 1568a1b9b6aSSam Leffler { 157b032f27cSSam Leffler struct aclstate *as = vap->iv_as; 1588a1b9b6aSSam Leffler 1598a1b9b6aSSam Leffler switch (as->as_policy) { 1608a1b9b6aSSam Leffler case ACL_POLICY_OPEN: 161b032f27cSSam Leffler case ACL_POLICY_RADIUS: 1628a1b9b6aSSam Leffler return 1; 1638a1b9b6aSSam Leffler case ACL_POLICY_ALLOW: 1648a1b9b6aSSam Leffler return _find_acl(as, mac) != NULL; 1658a1b9b6aSSam Leffler case ACL_POLICY_DENY: 1668a1b9b6aSSam Leffler return _find_acl(as, mac) == NULL; 1678a1b9b6aSSam Leffler } 1688a1b9b6aSSam Leffler return 0; /* should not happen */ 1698a1b9b6aSSam Leffler } 1708a1b9b6aSSam Leffler 1718a1b9b6aSSam Leffler static int 172b032f27cSSam Leffler acl_add(struct ieee80211vap *vap, const uint8_t mac[IEEE80211_ADDR_LEN]) 1738a1b9b6aSSam Leffler { 174b032f27cSSam Leffler struct aclstate *as = vap->iv_as; 1758a1b9b6aSSam Leffler struct acl *acl, *new; 1768a1b9b6aSSam Leffler int hash; 1778a1b9b6aSSam Leffler 1788a1b9b6aSSam Leffler MALLOC(new, struct acl *, sizeof(struct acl), M_80211_ACL, M_NOWAIT | M_ZERO); 1798a1b9b6aSSam Leffler if (new == NULL) { 180b032f27cSSam Leffler IEEE80211_DPRINTF(vap, IEEE80211_MSG_ACL, 1818a1b9b6aSSam Leffler "ACL: add %s failed, no memory\n", ether_sprintf(mac)); 1828a1b9b6aSSam Leffler /* XXX statistic */ 1838a1b9b6aSSam Leffler return ENOMEM; 1848a1b9b6aSSam Leffler } 1858a1b9b6aSSam Leffler 1868a1b9b6aSSam Leffler ACL_LOCK(as); 1878a1b9b6aSSam Leffler hash = ACL_HASH(mac); 1888a1b9b6aSSam Leffler LIST_FOREACH(acl, &as->as_hash[hash], acl_hash) { 1898a1b9b6aSSam Leffler if (IEEE80211_ADDR_EQ(acl->acl_macaddr, mac)) { 1908a1b9b6aSSam Leffler ACL_UNLOCK(as); 1918a1b9b6aSSam Leffler FREE(new, M_80211_ACL); 192b032f27cSSam Leffler IEEE80211_DPRINTF(vap, IEEE80211_MSG_ACL, 1938a1b9b6aSSam Leffler "ACL: add %s failed, already present\n", 1948a1b9b6aSSam Leffler ether_sprintf(mac)); 1958a1b9b6aSSam Leffler return EEXIST; 1968a1b9b6aSSam Leffler } 1978a1b9b6aSSam Leffler } 1988a1b9b6aSSam Leffler IEEE80211_ADDR_COPY(new->acl_macaddr, mac); 1998a1b9b6aSSam Leffler TAILQ_INSERT_TAIL(&as->as_list, new, acl_list); 2008a1b9b6aSSam Leffler LIST_INSERT_HEAD(&as->as_hash[hash], new, acl_hash); 201188757f5SSam Leffler as->as_nacls++; 2028a1b9b6aSSam Leffler ACL_UNLOCK(as); 2038a1b9b6aSSam Leffler 204b032f27cSSam Leffler IEEE80211_DPRINTF(vap, IEEE80211_MSG_ACL, 2058a1b9b6aSSam Leffler "ACL: add %s\n", ether_sprintf(mac)); 2068a1b9b6aSSam Leffler return 0; 2078a1b9b6aSSam Leffler } 2088a1b9b6aSSam Leffler 2098a1b9b6aSSam Leffler static int 210b032f27cSSam Leffler acl_remove(struct ieee80211vap *vap, const uint8_t mac[IEEE80211_ADDR_LEN]) 2118a1b9b6aSSam Leffler { 212b032f27cSSam Leffler struct aclstate *as = vap->iv_as; 2138a1b9b6aSSam Leffler struct acl *acl; 2148a1b9b6aSSam Leffler 2158a1b9b6aSSam Leffler ACL_LOCK(as); 2168a1b9b6aSSam Leffler acl = _find_acl(as, mac); 2178a1b9b6aSSam Leffler if (acl != NULL) 2188a1b9b6aSSam Leffler _acl_free(as, acl); 2198a1b9b6aSSam Leffler ACL_UNLOCK(as); 2208a1b9b6aSSam Leffler 221b032f27cSSam Leffler IEEE80211_DPRINTF(vap, IEEE80211_MSG_ACL, 2228a1b9b6aSSam Leffler "ACL: remove %s%s\n", ether_sprintf(mac), 2238a1b9b6aSSam Leffler acl == NULL ? ", not present" : ""); 2248a1b9b6aSSam Leffler 2258a1b9b6aSSam Leffler return (acl == NULL ? ENOENT : 0); 2268a1b9b6aSSam Leffler } 2278a1b9b6aSSam Leffler 2288a1b9b6aSSam Leffler static int 229b032f27cSSam Leffler acl_free_all(struct ieee80211vap *vap) 2308a1b9b6aSSam Leffler { 231b032f27cSSam Leffler struct aclstate *as = vap->iv_as; 2328a1b9b6aSSam Leffler struct acl *acl; 2338a1b9b6aSSam Leffler 234b032f27cSSam Leffler IEEE80211_DPRINTF(vap, IEEE80211_MSG_ACL, "ACL: %s\n", "free all"); 2358a1b9b6aSSam Leffler 2368a1b9b6aSSam Leffler ACL_LOCK(as); 2378a1b9b6aSSam Leffler while ((acl = TAILQ_FIRST(&as->as_list)) != NULL) 2388a1b9b6aSSam Leffler _acl_free(as, acl); 2398a1b9b6aSSam Leffler ACL_UNLOCK(as); 2408a1b9b6aSSam Leffler 2418a1b9b6aSSam Leffler return 0; 2428a1b9b6aSSam Leffler } 2438a1b9b6aSSam Leffler 2448a1b9b6aSSam Leffler static int 245b032f27cSSam Leffler acl_setpolicy(struct ieee80211vap *vap, int policy) 2468a1b9b6aSSam Leffler { 247b032f27cSSam Leffler struct aclstate *as = vap->iv_as; 2488a1b9b6aSSam Leffler 249b032f27cSSam Leffler IEEE80211_DPRINTF(vap, IEEE80211_MSG_ACL, 2508a1b9b6aSSam Leffler "ACL: set policy to %u\n", policy); 2518a1b9b6aSSam Leffler 2528a1b9b6aSSam Leffler switch (policy) { 2538a1b9b6aSSam Leffler case IEEE80211_MACCMD_POLICY_OPEN: 2548a1b9b6aSSam Leffler as->as_policy = ACL_POLICY_OPEN; 2558a1b9b6aSSam Leffler break; 2568a1b9b6aSSam Leffler case IEEE80211_MACCMD_POLICY_ALLOW: 2578a1b9b6aSSam Leffler as->as_policy = ACL_POLICY_ALLOW; 2588a1b9b6aSSam Leffler break; 2598a1b9b6aSSam Leffler case IEEE80211_MACCMD_POLICY_DENY: 2608a1b9b6aSSam Leffler as->as_policy = ACL_POLICY_DENY; 2618a1b9b6aSSam Leffler break; 262b032f27cSSam Leffler case IEEE80211_MACCMD_POLICY_RADIUS: 263b032f27cSSam Leffler as->as_policy = ACL_POLICY_RADIUS; 264b032f27cSSam Leffler break; 2658a1b9b6aSSam Leffler default: 2668a1b9b6aSSam Leffler return EINVAL; 2678a1b9b6aSSam Leffler } 2688a1b9b6aSSam Leffler return 0; 2698a1b9b6aSSam Leffler } 2708a1b9b6aSSam Leffler 2718a1b9b6aSSam Leffler static int 272b032f27cSSam Leffler acl_getpolicy(struct ieee80211vap *vap) 2738a1b9b6aSSam Leffler { 274b032f27cSSam Leffler struct aclstate *as = vap->iv_as; 2758a1b9b6aSSam Leffler 2768a1b9b6aSSam Leffler return as->as_policy; 2778a1b9b6aSSam Leffler } 2788a1b9b6aSSam Leffler 279188757f5SSam Leffler static int 280b032f27cSSam Leffler acl_setioctl(struct ieee80211vap *vap, struct ieee80211req *ireq) 281188757f5SSam Leffler { 282188757f5SSam Leffler 283188757f5SSam Leffler return EINVAL; 284188757f5SSam Leffler } 285188757f5SSam Leffler 286188757f5SSam Leffler static int 287b032f27cSSam Leffler acl_getioctl(struct ieee80211vap *vap, struct ieee80211req *ireq) 288188757f5SSam Leffler { 289b032f27cSSam Leffler struct aclstate *as = vap->iv_as; 290188757f5SSam Leffler struct acl *acl; 291188757f5SSam Leffler struct ieee80211req_maclist *ap; 292188757f5SSam Leffler int error, space, i; 293188757f5SSam Leffler 294188757f5SSam Leffler switch (ireq->i_val) { 295188757f5SSam Leffler case IEEE80211_MACCMD_POLICY: 296188757f5SSam Leffler ireq->i_val = as->as_policy; 297188757f5SSam Leffler return 0; 298188757f5SSam Leffler case IEEE80211_MACCMD_LIST: 299188757f5SSam Leffler space = as->as_nacls * IEEE80211_ADDR_LEN; 300188757f5SSam Leffler if (ireq->i_len == 0) { 301188757f5SSam Leffler ireq->i_len = space; /* return required space */ 302188757f5SSam Leffler return 0; /* NB: must not error */ 303188757f5SSam Leffler } 304188757f5SSam Leffler MALLOC(ap, struct ieee80211req_maclist *, space, 305188757f5SSam Leffler M_TEMP, M_NOWAIT); 306188757f5SSam Leffler if (ap == NULL) 307188757f5SSam Leffler return ENOMEM; 308188757f5SSam Leffler i = 0; 309188757f5SSam Leffler ACL_LOCK(as); 310188757f5SSam Leffler TAILQ_FOREACH(acl, &as->as_list, acl_list) { 311188757f5SSam Leffler IEEE80211_ADDR_COPY(ap[i].ml_macaddr, acl->acl_macaddr); 312188757f5SSam Leffler i++; 313188757f5SSam Leffler } 314188757f5SSam Leffler ACL_UNLOCK(as); 315188757f5SSam Leffler if (ireq->i_len >= space) { 316188757f5SSam Leffler error = copyout(ap, ireq->i_data, space); 317188757f5SSam Leffler ireq->i_len = space; 318188757f5SSam Leffler } else 319188757f5SSam Leffler error = copyout(ap, ireq->i_data, ireq->i_len); 320188757f5SSam Leffler FREE(ap, M_TEMP); 321188757f5SSam Leffler return error; 322188757f5SSam Leffler } 323188757f5SSam Leffler return EINVAL; 324188757f5SSam Leffler } 325188757f5SSam Leffler 3268a1b9b6aSSam Leffler static const struct ieee80211_aclator mac = { 3278a1b9b6aSSam Leffler .iac_name = "mac", 3288a1b9b6aSSam Leffler .iac_attach = acl_attach, 3298a1b9b6aSSam Leffler .iac_detach = acl_detach, 3308a1b9b6aSSam Leffler .iac_check = acl_check, 3318a1b9b6aSSam Leffler .iac_add = acl_add, 3328a1b9b6aSSam Leffler .iac_remove = acl_remove, 3338a1b9b6aSSam Leffler .iac_flush = acl_free_all, 3348a1b9b6aSSam Leffler .iac_setpolicy = acl_setpolicy, 3358a1b9b6aSSam Leffler .iac_getpolicy = acl_getpolicy, 336188757f5SSam Leffler .iac_setioctl = acl_setioctl, 337188757f5SSam Leffler .iac_getioctl = acl_getioctl, 3388a1b9b6aSSam Leffler }; 339b032f27cSSam Leffler IEEE80211_ACL_MODULE(wlan_acl, mac, 1); 340