18a1b9b6aSSam Leffler /*- 28a1b9b6aSSam Leffler * Copyright (c) 2004 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 * 3. The name of the author may not be used to endorse or promote products 148a1b9b6aSSam Leffler * derived from this software without specific prior written permission. 158a1b9b6aSSam Leffler * 168a1b9b6aSSam Leffler * Alternatively, this software may be distributed under the terms of the 178a1b9b6aSSam Leffler * GNU General Public License ("GPL") version 2 as published by the Free 188a1b9b6aSSam Leffler * Software Foundation. 198a1b9b6aSSam Leffler * 208a1b9b6aSSam Leffler * THIS SOFTWARE IS PROVIDED BY THE AUTHOR ``AS IS'' AND ANY EXPRESS OR 218a1b9b6aSSam Leffler * IMPLIED WARRANTIES, INCLUDING, BUT NOT LIMITED TO, THE IMPLIED WARRANTIES 228a1b9b6aSSam Leffler * OF MERCHANTABILITY AND FITNESS FOR A PARTICULAR PURPOSE ARE DISCLAIMED. 238a1b9b6aSSam Leffler * IN NO EVENT SHALL THE AUTHOR BE LIABLE FOR ANY DIRECT, INDIRECT, 248a1b9b6aSSam Leffler * INCIDENTAL, SPECIAL, EXEMPLARY, OR CONSEQUENTIAL DAMAGES (INCLUDING, BUT 258a1b9b6aSSam Leffler * NOT LIMITED TO, PROCUREMENT OF SUBSTITUTE GOODS OR SERVICES; LOSS OF USE, 268a1b9b6aSSam Leffler * DATA, OR PROFITS; OR BUSINESS INTERRUPTION) HOWEVER CAUSED AND ON ANY 278a1b9b6aSSam Leffler * THEORY OF LIABILITY, WHETHER IN CONTRACT, STRICT LIABILITY, OR TORT 288a1b9b6aSSam Leffler * (INCLUDING NEGLIGENCE OR OTHERWISE) ARISING IN ANY WAY OUT OF THE USE OF 298a1b9b6aSSam Leffler * THIS SOFTWARE, EVEN IF ADVISED OF THE POSSIBILITY OF SUCH DAMAGE. 308a1b9b6aSSam Leffler */ 318a1b9b6aSSam Leffler 328a1b9b6aSSam Leffler #include <sys/cdefs.h> 338a1b9b6aSSam Leffler __FBSDID("$FreeBSD$"); 348a1b9b6aSSam Leffler 358a1b9b6aSSam Leffler /* 368a1b9b6aSSam Leffler * IEEE 802.11 MAC ACL support. 378a1b9b6aSSam Leffler * 388a1b9b6aSSam Leffler * When this module is loaded the sender address of each received 398a1b9b6aSSam Leffler * frame is passed to the iac_check method and the module indicates 408a1b9b6aSSam Leffler * if the frame should be accepted or rejected. If the policy is 418a1b9b6aSSam Leffler * set to ACL_POLICY_OPEN then all frames are accepted w/o checking 428a1b9b6aSSam Leffler * the address. Otherwise, the address is looked up in the database 438a1b9b6aSSam Leffler * and if found the frame is either accepted (ACL_POLICY_ALLOW) 448a1b9b6aSSam Leffler * or rejected (ACL_POLICY_DENT). 458a1b9b6aSSam Leffler */ 468a1b9b6aSSam Leffler #include <sys/param.h> 478a1b9b6aSSam Leffler #include <sys/kernel.h> 488a1b9b6aSSam Leffler #include <sys/systm.h> 498a1b9b6aSSam Leffler #include <sys/mbuf.h> 508a1b9b6aSSam Leffler #include <sys/module.h> 518a1b9b6aSSam Leffler #include <sys/queue.h> 528a1b9b6aSSam Leffler 538a1b9b6aSSam Leffler #include <sys/socket.h> 548a1b9b6aSSam Leffler 558a1b9b6aSSam Leffler #include <net/if.h> 568a1b9b6aSSam Leffler #include <net/if_media.h> 578a1b9b6aSSam Leffler #include <net/ethernet.h> 588a1b9b6aSSam Leffler #include <net/route.h> 598a1b9b6aSSam Leffler 608a1b9b6aSSam Leffler #include <net80211/ieee80211_var.h> 618a1b9b6aSSam Leffler 628a1b9b6aSSam Leffler enum { 638a1b9b6aSSam Leffler ACL_POLICY_OPEN = 0, /* open, don't check ACL's */ 648a1b9b6aSSam Leffler ACL_POLICY_ALLOW = 1, /* allow traffic from MAC */ 658a1b9b6aSSam Leffler ACL_POLICY_DENY = 2, /* deny traffic from MAC */ 668a1b9b6aSSam Leffler }; 678a1b9b6aSSam Leffler 688a1b9b6aSSam Leffler #define ACL_HASHSIZE 32 698a1b9b6aSSam Leffler 708a1b9b6aSSam Leffler struct acl { 718a1b9b6aSSam Leffler TAILQ_ENTRY(acl) acl_list; 728a1b9b6aSSam Leffler LIST_ENTRY(acl) acl_hash; 738a1b9b6aSSam Leffler u_int8_t acl_macaddr[IEEE80211_ADDR_LEN]; 748a1b9b6aSSam Leffler }; 758a1b9b6aSSam Leffler struct aclstate { 768a1b9b6aSSam Leffler acl_lock_t as_lock; 778a1b9b6aSSam Leffler int as_policy; 788a1b9b6aSSam Leffler TAILQ_HEAD(, acl) as_list; /* list of all ACL's */ 798a1b9b6aSSam Leffler LIST_HEAD(, acl) as_hash[ACL_HASHSIZE]; 808a1b9b6aSSam Leffler struct ieee80211com *as_ic; 818a1b9b6aSSam Leffler }; 828a1b9b6aSSam Leffler 838a1b9b6aSSam Leffler /* simple hash is enough for variation of macaddr */ 848a1b9b6aSSam Leffler #define ACL_HASH(addr) \ 858a1b9b6aSSam Leffler (((const u_int8_t *)(addr))[IEEE80211_ADDR_LEN - 1] % ACL_HASHSIZE) 868a1b9b6aSSam Leffler 878a1b9b6aSSam Leffler MALLOC_DEFINE(M_80211_ACL, "acl", "802.11 station acl"); 888a1b9b6aSSam Leffler 898a1b9b6aSSam Leffler static int acl_free_all(struct ieee80211com *); 908a1b9b6aSSam Leffler 918a1b9b6aSSam Leffler static int 928a1b9b6aSSam Leffler acl_attach(struct ieee80211com *ic) 938a1b9b6aSSam Leffler { 948a1b9b6aSSam Leffler struct aclstate *as; 958a1b9b6aSSam Leffler 968a1b9b6aSSam Leffler MALLOC(as, struct aclstate *, sizeof(struct aclstate), 978a1b9b6aSSam Leffler M_DEVBUF, M_NOWAIT | M_ZERO); 988a1b9b6aSSam Leffler if (as == NULL) 998a1b9b6aSSam Leffler return 0; 1008a1b9b6aSSam Leffler ACL_LOCK_INIT(as, "acl"); 1018a1b9b6aSSam Leffler TAILQ_INIT(&as->as_list); 1028a1b9b6aSSam Leffler as->as_policy = ACL_POLICY_OPEN; 1038a1b9b6aSSam Leffler as->as_ic = ic; 1048a1b9b6aSSam Leffler ic->ic_as = as; 1058a1b9b6aSSam Leffler return 1; 1068a1b9b6aSSam Leffler } 1078a1b9b6aSSam Leffler 1088a1b9b6aSSam Leffler static void 1098a1b9b6aSSam Leffler acl_detach(struct ieee80211com *ic) 1108a1b9b6aSSam Leffler { 1118a1b9b6aSSam Leffler struct aclstate *as = ic->ic_as; 1128a1b9b6aSSam Leffler 1138a1b9b6aSSam Leffler acl_free_all(ic); 1148a1b9b6aSSam Leffler ic->ic_as = NULL; 1158a1b9b6aSSam Leffler ACL_LOCK_DESTROY(as); 1168a1b9b6aSSam Leffler FREE(as, M_DEVBUF); 1178a1b9b6aSSam Leffler } 1188a1b9b6aSSam Leffler 11968f5ddcdSSam Leffler static __inline struct acl * 1208a1b9b6aSSam Leffler _find_acl(struct aclstate *as, const u_int8_t *macaddr) 1218a1b9b6aSSam Leffler { 1228a1b9b6aSSam Leffler struct acl *acl; 1238a1b9b6aSSam Leffler int hash; 1248a1b9b6aSSam Leffler 1258a1b9b6aSSam Leffler hash = ACL_HASH(macaddr); 1268a1b9b6aSSam Leffler LIST_FOREACH(acl, &as->as_hash[hash], acl_hash) { 1278a1b9b6aSSam Leffler if (IEEE80211_ADDR_EQ(acl->acl_macaddr, macaddr)) 1288a1b9b6aSSam Leffler return acl; 1298a1b9b6aSSam Leffler } 1308a1b9b6aSSam Leffler return NULL; 1318a1b9b6aSSam Leffler } 1328a1b9b6aSSam Leffler 1338a1b9b6aSSam Leffler static void 1348a1b9b6aSSam Leffler _acl_free(struct aclstate *as, struct acl *acl) 1358a1b9b6aSSam Leffler { 1368a1b9b6aSSam Leffler ACL_LOCK_ASSERT(as); 1378a1b9b6aSSam Leffler 1388a1b9b6aSSam Leffler TAILQ_REMOVE(&as->as_list, acl, acl_list); 1398a1b9b6aSSam Leffler LIST_REMOVE(acl, acl_hash); 1408a1b9b6aSSam Leffler FREE(acl, M_80211_ACL); 1418a1b9b6aSSam Leffler } 1428a1b9b6aSSam Leffler 1438a1b9b6aSSam Leffler static int 1448a1b9b6aSSam Leffler acl_check(struct ieee80211com *ic, const u_int8_t mac[IEEE80211_ADDR_LEN]) 1458a1b9b6aSSam Leffler { 1468a1b9b6aSSam Leffler struct aclstate *as = ic->ic_as; 1478a1b9b6aSSam Leffler 1488a1b9b6aSSam Leffler switch (as->as_policy) { 1498a1b9b6aSSam Leffler case ACL_POLICY_OPEN: 1508a1b9b6aSSam Leffler return 1; 1518a1b9b6aSSam Leffler case ACL_POLICY_ALLOW: 1528a1b9b6aSSam Leffler return _find_acl(as, mac) != NULL; 1538a1b9b6aSSam Leffler case ACL_POLICY_DENY: 1548a1b9b6aSSam Leffler return _find_acl(as, mac) == NULL; 1558a1b9b6aSSam Leffler } 1568a1b9b6aSSam Leffler return 0; /* should not happen */ 1578a1b9b6aSSam Leffler } 1588a1b9b6aSSam Leffler 1598a1b9b6aSSam Leffler static int 1608a1b9b6aSSam Leffler acl_add(struct ieee80211com *ic, const u_int8_t mac[IEEE80211_ADDR_LEN]) 1618a1b9b6aSSam Leffler { 1628a1b9b6aSSam Leffler struct aclstate *as = ic->ic_as; 1638a1b9b6aSSam Leffler struct acl *acl, *new; 1648a1b9b6aSSam Leffler int hash; 1658a1b9b6aSSam Leffler 1668a1b9b6aSSam Leffler MALLOC(new, struct acl *, sizeof(struct acl), M_80211_ACL, M_NOWAIT | M_ZERO); 1678a1b9b6aSSam Leffler if (new == NULL) { 1688a1b9b6aSSam Leffler IEEE80211_DPRINTF(ic, IEEE80211_MSG_ACL, 1698a1b9b6aSSam Leffler "ACL: add %s failed, no memory\n", ether_sprintf(mac)); 1708a1b9b6aSSam Leffler /* XXX statistic */ 1718a1b9b6aSSam Leffler return ENOMEM; 1728a1b9b6aSSam Leffler } 1738a1b9b6aSSam Leffler 1748a1b9b6aSSam Leffler ACL_LOCK(as); 1758a1b9b6aSSam Leffler hash = ACL_HASH(mac); 1768a1b9b6aSSam Leffler LIST_FOREACH(acl, &as->as_hash[hash], acl_hash) { 1778a1b9b6aSSam Leffler if (IEEE80211_ADDR_EQ(acl->acl_macaddr, mac)) { 1788a1b9b6aSSam Leffler ACL_UNLOCK(as); 1798a1b9b6aSSam Leffler FREE(new, M_80211_ACL); 1808a1b9b6aSSam Leffler IEEE80211_DPRINTF(ic, IEEE80211_MSG_ACL, 1818a1b9b6aSSam Leffler "ACL: add %s failed, already present\n", 1828a1b9b6aSSam Leffler ether_sprintf(mac)); 1838a1b9b6aSSam Leffler return EEXIST; 1848a1b9b6aSSam Leffler } 1858a1b9b6aSSam Leffler } 1868a1b9b6aSSam Leffler IEEE80211_ADDR_COPY(new->acl_macaddr, mac); 1878a1b9b6aSSam Leffler TAILQ_INSERT_TAIL(&as->as_list, new, acl_list); 1888a1b9b6aSSam Leffler LIST_INSERT_HEAD(&as->as_hash[hash], new, acl_hash); 1898a1b9b6aSSam Leffler ACL_UNLOCK(as); 1908a1b9b6aSSam Leffler 1918a1b9b6aSSam Leffler IEEE80211_DPRINTF(ic, IEEE80211_MSG_ACL, 1928a1b9b6aSSam Leffler "ACL: add %s\n", ether_sprintf(mac)); 1938a1b9b6aSSam Leffler return 0; 1948a1b9b6aSSam Leffler } 1958a1b9b6aSSam Leffler 1968a1b9b6aSSam Leffler static int 1978a1b9b6aSSam Leffler acl_remove(struct ieee80211com *ic, const u_int8_t mac[IEEE80211_ADDR_LEN]) 1988a1b9b6aSSam Leffler { 1998a1b9b6aSSam Leffler struct aclstate *as = ic->ic_as; 2008a1b9b6aSSam Leffler struct acl *acl; 2018a1b9b6aSSam Leffler 2028a1b9b6aSSam Leffler ACL_LOCK(as); 2038a1b9b6aSSam Leffler acl = _find_acl(as, mac); 2048a1b9b6aSSam Leffler if (acl != NULL) 2058a1b9b6aSSam Leffler _acl_free(as, acl); 2068a1b9b6aSSam Leffler ACL_UNLOCK(as); 2078a1b9b6aSSam Leffler 2088a1b9b6aSSam Leffler IEEE80211_DPRINTF(ic, IEEE80211_MSG_ACL, 2098a1b9b6aSSam Leffler "ACL: remove %s%s\n", ether_sprintf(mac), 2108a1b9b6aSSam Leffler acl == NULL ? ", not present" : ""); 2118a1b9b6aSSam Leffler 2128a1b9b6aSSam Leffler return (acl == NULL ? ENOENT : 0); 2138a1b9b6aSSam Leffler } 2148a1b9b6aSSam Leffler 2158a1b9b6aSSam Leffler static int 2168a1b9b6aSSam Leffler acl_free_all(struct ieee80211com *ic) 2178a1b9b6aSSam Leffler { 2188a1b9b6aSSam Leffler struct aclstate *as = ic->ic_as; 2198a1b9b6aSSam Leffler struct acl *acl; 2208a1b9b6aSSam Leffler 2218a1b9b6aSSam Leffler IEEE80211_DPRINTF(ic, IEEE80211_MSG_ACL, "ACL: %s\n", "free all"); 2228a1b9b6aSSam Leffler 2238a1b9b6aSSam Leffler ACL_LOCK(as); 2248a1b9b6aSSam Leffler while ((acl = TAILQ_FIRST(&as->as_list)) != NULL) 2258a1b9b6aSSam Leffler _acl_free(as, acl); 2268a1b9b6aSSam Leffler ACL_UNLOCK(as); 2278a1b9b6aSSam Leffler 2288a1b9b6aSSam Leffler return 0; 2298a1b9b6aSSam Leffler } 2308a1b9b6aSSam Leffler 2318a1b9b6aSSam Leffler static int 2328a1b9b6aSSam Leffler acl_setpolicy(struct ieee80211com *ic, int policy) 2338a1b9b6aSSam Leffler { 2348a1b9b6aSSam Leffler struct aclstate *as = ic->ic_as; 2358a1b9b6aSSam Leffler 2368a1b9b6aSSam Leffler IEEE80211_DPRINTF(ic, IEEE80211_MSG_ACL, 2378a1b9b6aSSam Leffler "ACL: set policy to %u\n", policy); 2388a1b9b6aSSam Leffler 2398a1b9b6aSSam Leffler switch (policy) { 2408a1b9b6aSSam Leffler case IEEE80211_MACCMD_POLICY_OPEN: 2418a1b9b6aSSam Leffler as->as_policy = ACL_POLICY_OPEN; 2428a1b9b6aSSam Leffler break; 2438a1b9b6aSSam Leffler case IEEE80211_MACCMD_POLICY_ALLOW: 2448a1b9b6aSSam Leffler as->as_policy = ACL_POLICY_ALLOW; 2458a1b9b6aSSam Leffler break; 2468a1b9b6aSSam Leffler case IEEE80211_MACCMD_POLICY_DENY: 2478a1b9b6aSSam Leffler as->as_policy = ACL_POLICY_DENY; 2488a1b9b6aSSam Leffler break; 2498a1b9b6aSSam Leffler default: 2508a1b9b6aSSam Leffler return EINVAL; 2518a1b9b6aSSam Leffler } 2528a1b9b6aSSam Leffler return 0; 2538a1b9b6aSSam Leffler } 2548a1b9b6aSSam Leffler 2558a1b9b6aSSam Leffler static int 2568a1b9b6aSSam Leffler acl_getpolicy(struct ieee80211com *ic) 2578a1b9b6aSSam Leffler { 2588a1b9b6aSSam Leffler struct aclstate *as = ic->ic_as; 2598a1b9b6aSSam Leffler 2608a1b9b6aSSam Leffler return as->as_policy; 2618a1b9b6aSSam Leffler } 2628a1b9b6aSSam Leffler 2638a1b9b6aSSam Leffler static const struct ieee80211_aclator mac = { 2648a1b9b6aSSam Leffler .iac_name = "mac", 2658a1b9b6aSSam Leffler .iac_attach = acl_attach, 2668a1b9b6aSSam Leffler .iac_detach = acl_detach, 2678a1b9b6aSSam Leffler .iac_check = acl_check, 2688a1b9b6aSSam Leffler .iac_add = acl_add, 2698a1b9b6aSSam Leffler .iac_remove = acl_remove, 2708a1b9b6aSSam Leffler .iac_flush = acl_free_all, 2718a1b9b6aSSam Leffler .iac_setpolicy = acl_setpolicy, 2728a1b9b6aSSam Leffler .iac_getpolicy = acl_getpolicy, 2738a1b9b6aSSam Leffler }; 2748a1b9b6aSSam Leffler 2758a1b9b6aSSam Leffler /* 2768a1b9b6aSSam Leffler * Module glue. 2778a1b9b6aSSam Leffler */ 2788a1b9b6aSSam Leffler static int 2798a1b9b6aSSam Leffler wlan_acl_modevent(module_t mod, int type, void *unused) 2808a1b9b6aSSam Leffler { 2818a1b9b6aSSam Leffler switch (type) { 2828a1b9b6aSSam Leffler case MOD_LOAD: 2838a1b9b6aSSam Leffler if (bootverbose) 2848a1b9b6aSSam Leffler printf("wlan: <802.11 MAC ACL support>\n"); 2858a1b9b6aSSam Leffler ieee80211_aclator_register(&mac); 2868a1b9b6aSSam Leffler return 0; 2878a1b9b6aSSam Leffler case MOD_UNLOAD: 2888a1b9b6aSSam Leffler ieee80211_aclator_unregister(&mac); 2898a1b9b6aSSam Leffler return 0; 2908a1b9b6aSSam Leffler } 2918a1b9b6aSSam Leffler return EINVAL; 2928a1b9b6aSSam Leffler } 2938a1b9b6aSSam Leffler 2948a1b9b6aSSam Leffler static moduledata_t wlan_acl_mod = { 2958a1b9b6aSSam Leffler "wlan_acl", 2968a1b9b6aSSam Leffler wlan_acl_modevent, 2978a1b9b6aSSam Leffler 0 2988a1b9b6aSSam Leffler }; 2998a1b9b6aSSam Leffler DECLARE_MODULE(wlan_acl, wlan_acl_mod, SI_SUB_DRIVERS, SI_ORDER_FIRST); 3008a1b9b6aSSam Leffler MODULE_VERSION(wlan_acl, 1); 3018a1b9b6aSSam Leffler MODULE_DEPEND(wlan_acl, wlan, 1, 1, 1); 302