1 /*- 2 * Copyright (c) 2004-2005 Sam Leffler, Errno Consulting 3 * All rights reserved. 4 * 5 * Redistribution and use in source and binary forms, with or without 6 * modification, are permitted provided that the following conditions 7 * are met: 8 * 1. Redistributions of source code must retain the above copyright 9 * notice, this list of conditions and the following disclaimer. 10 * 2. Redistributions in binary form must reproduce the above copyright 11 * notice, this list of conditions and the following disclaimer in the 12 * documentation and/or other materials provided with the distribution. 13 * 3. The name of the author may not be used to endorse or promote products 14 * derived from this software without specific prior written permission. 15 * 16 * Alternatively, this software may be distributed under the terms of the 17 * GNU General Public License ("GPL") version 2 as published by the Free 18 * Software Foundation. 19 * 20 * THIS SOFTWARE IS PROVIDED BY THE AUTHOR ``AS IS'' AND ANY EXPRESS OR 21 * IMPLIED WARRANTIES, INCLUDING, BUT NOT LIMITED TO, THE IMPLIED WARRANTIES 22 * OF MERCHANTABILITY AND FITNESS FOR A PARTICULAR PURPOSE ARE DISCLAIMED. 23 * IN NO EVENT SHALL THE AUTHOR BE LIABLE FOR ANY DIRECT, INDIRECT, 24 * INCIDENTAL, SPECIAL, EXEMPLARY, OR CONSEQUENTIAL DAMAGES (INCLUDING, BUT 25 * NOT LIMITED TO, PROCUREMENT OF SUBSTITUTE GOODS OR SERVICES; LOSS OF USE, 26 * DATA, OR PROFITS; OR BUSINESS INTERRUPTION) HOWEVER CAUSED AND ON ANY 27 * THEORY OF LIABILITY, WHETHER IN CONTRACT, STRICT LIABILITY, OR TORT 28 * (INCLUDING NEGLIGENCE OR OTHERWISE) ARISING IN ANY WAY OUT OF THE USE OF 29 * THIS SOFTWARE, EVEN IF ADVISED OF THE POSSIBILITY OF SUCH DAMAGE. 30 */ 31 32 #include <sys/cdefs.h> 33 #ifdef __FreeBSD__ 34 __FBSDID("$FreeBSD: src/sys/net80211/ieee80211_acl.c,v 1.3 2004/12/31 22:42:38 sam Exp $"); 35 #endif 36 #ifdef __NetBSD__ 37 __KERNEL_RCSID(0, "$NetBSD: ieee80211_acl.c,v 1.3 2005/07/26 22:52:48 dyoung Exp $"); 38 #endif 39 40 /* 41 * IEEE 802.11 MAC ACL support. 42 * 43 * When this module is loaded the sender address of each received 44 * frame is passed to the iac_check method and the module indicates 45 * if the frame should be accepted or rejected. If the policy is 46 * set to ACL_POLICY_OPEN then all frames are accepted w/o checking 47 * the address. Otherwise, the address is looked up in the database 48 * and if found the frame is either accepted (ACL_POLICY_ALLOW) 49 * or rejected (ACL_POLICY_DENT). 50 */ 51 #include <sys/param.h> 52 #include <sys/kernel.h> 53 #include <sys/systm.h> 54 #include <sys/mbuf.h> 55 #include <sys/queue.h> 56 57 #include <sys/socket.h> 58 59 #include <net/if.h> 60 #include <net/if_media.h> 61 #include <net/if_ether.h> 62 #include <net/route.h> 63 64 #include <net80211/ieee80211_var.h> 65 66 enum { 67 ACL_POLICY_OPEN = 0, /* open, don't check ACL's */ 68 ACL_POLICY_ALLOW = 1, /* allow traffic from MAC */ 69 ACL_POLICY_DENY = 2, /* deny traffic from MAC */ 70 }; 71 72 #define ACL_HASHSIZE 32 73 74 struct acl { 75 TAILQ_ENTRY(acl) acl_list; 76 LIST_ENTRY(acl) acl_hash; 77 u_int8_t acl_macaddr[IEEE80211_ADDR_LEN]; 78 }; 79 struct aclstate { 80 acl_lock_t as_lock; 81 int as_policy; 82 TAILQ_HEAD(, acl) as_list; /* list of all ACL's */ 83 LIST_HEAD(, acl) as_hash[ACL_HASHSIZE]; 84 struct ieee80211com *as_ic; 85 }; 86 87 /* simple hash is enough for variation of macaddr */ 88 #define ACL_HASH(addr) \ 89 (((const u_int8_t *)(addr))[IEEE80211_ADDR_LEN - 1] % ACL_HASHSIZE) 90 91 MALLOC_DEFINE(M_80211_ACL, "acl", "802.11 station acl"); 92 93 static int acl_free_all(struct ieee80211com *); 94 95 static int 96 acl_attach(struct ieee80211com *ic) 97 { 98 struct aclstate *as; 99 100 MALLOC(as, struct aclstate *, sizeof(struct aclstate), 101 M_DEVBUF, M_NOWAIT | M_ZERO); 102 if (as == NULL) 103 return 0; 104 ACL_LOCK_INIT(as, "acl"); 105 TAILQ_INIT(&as->as_list); 106 as->as_policy = ACL_POLICY_OPEN; 107 as->as_ic = ic; 108 ic->ic_as = as; 109 return 1; 110 } 111 112 static void 113 acl_detach(struct ieee80211com *ic) 114 { 115 struct aclstate *as = ic->ic_as; 116 117 acl_free_all(ic); 118 ic->ic_as = NULL; 119 ACL_LOCK_DESTROY(as); 120 FREE(as, M_DEVBUF); 121 } 122 123 static __inline struct acl * 124 _find_acl(struct aclstate *as, const u_int8_t *macaddr) 125 { 126 struct acl *acl; 127 int hash; 128 129 hash = ACL_HASH(macaddr); 130 LIST_FOREACH(acl, &as->as_hash[hash], acl_hash) { 131 if (IEEE80211_ADDR_EQ(acl->acl_macaddr, macaddr)) 132 return acl; 133 } 134 return NULL; 135 } 136 137 static void 138 _acl_free(struct aclstate *as, struct acl *acl) 139 { 140 ACL_LOCK_ASSERT(as); 141 142 TAILQ_REMOVE(&as->as_list, acl, acl_list); 143 LIST_REMOVE(acl, acl_hash); 144 FREE(acl, M_80211_ACL); 145 } 146 147 static int 148 acl_check(struct ieee80211com *ic, const u_int8_t mac[IEEE80211_ADDR_LEN]) 149 { 150 struct aclstate *as = ic->ic_as; 151 152 switch (as->as_policy) { 153 case ACL_POLICY_OPEN: 154 return 1; 155 case ACL_POLICY_ALLOW: 156 return _find_acl(as, mac) != NULL; 157 case ACL_POLICY_DENY: 158 return _find_acl(as, mac) == NULL; 159 } 160 return 0; /* should not happen */ 161 } 162 163 static int 164 acl_add(struct ieee80211com *ic, const u_int8_t mac[IEEE80211_ADDR_LEN]) 165 { 166 struct aclstate *as = ic->ic_as; 167 struct acl *acl, *new; 168 int hash; 169 170 MALLOC(new, struct acl *, sizeof(struct acl), M_80211_ACL, M_NOWAIT | M_ZERO); 171 if (new == NULL) { 172 IEEE80211_DPRINTF(ic, IEEE80211_MSG_ACL, 173 "ACL: add %s failed, no memory\n", ether_sprintf(mac)); 174 /* XXX statistic */ 175 return ENOMEM; 176 } 177 178 ACL_LOCK(as); 179 hash = ACL_HASH(mac); 180 LIST_FOREACH(acl, &as->as_hash[hash], acl_hash) { 181 if (IEEE80211_ADDR_EQ(acl->acl_macaddr, mac)) { 182 ACL_UNLOCK(as); 183 FREE(new, M_80211_ACL); 184 IEEE80211_DPRINTF(ic, IEEE80211_MSG_ACL, 185 "ACL: add %s failed, already present\n", 186 ether_sprintf(mac)); 187 return EEXIST; 188 } 189 } 190 IEEE80211_ADDR_COPY(new->acl_macaddr, mac); 191 TAILQ_INSERT_TAIL(&as->as_list, new, acl_list); 192 LIST_INSERT_HEAD(&as->as_hash[hash], new, acl_hash); 193 ACL_UNLOCK(as); 194 195 IEEE80211_DPRINTF(ic, IEEE80211_MSG_ACL, 196 "ACL: add %s\n", ether_sprintf(mac)); 197 return 0; 198 } 199 200 static int 201 acl_remove(struct ieee80211com *ic, const u_int8_t mac[IEEE80211_ADDR_LEN]) 202 { 203 struct aclstate *as = ic->ic_as; 204 struct acl *acl; 205 206 ACL_LOCK(as); 207 acl = _find_acl(as, mac); 208 if (acl != NULL) 209 _acl_free(as, acl); 210 ACL_UNLOCK(as); 211 212 IEEE80211_DPRINTF(ic, IEEE80211_MSG_ACL, 213 "ACL: remove %s%s\n", ether_sprintf(mac), 214 acl == NULL ? ", not present" : ""); 215 216 return (acl == NULL ? ENOENT : 0); 217 } 218 219 static int 220 acl_free_all(struct ieee80211com *ic) 221 { 222 struct aclstate *as = ic->ic_as; 223 struct acl *acl; 224 225 IEEE80211_DPRINTF(ic, IEEE80211_MSG_ACL, "ACL: %s\n", "free all"); 226 227 ACL_LOCK(as); 228 while ((acl = TAILQ_FIRST(&as->as_list)) != NULL) 229 _acl_free(as, acl); 230 ACL_UNLOCK(as); 231 232 return 0; 233 } 234 235 static int 236 acl_setpolicy(struct ieee80211com *ic, int policy) 237 { 238 struct aclstate *as = ic->ic_as; 239 240 IEEE80211_DPRINTF(ic, IEEE80211_MSG_ACL, 241 "ACL: set policy to %u\n", policy); 242 243 switch (policy) { 244 case IEEE80211_MACCMD_POLICY_OPEN: 245 as->as_policy = ACL_POLICY_OPEN; 246 break; 247 case IEEE80211_MACCMD_POLICY_ALLOW: 248 as->as_policy = ACL_POLICY_ALLOW; 249 break; 250 case IEEE80211_MACCMD_POLICY_DENY: 251 as->as_policy = ACL_POLICY_DENY; 252 break; 253 default: 254 return EINVAL; 255 } 256 return 0; 257 } 258 259 static int 260 acl_getpolicy(struct ieee80211com *ic) 261 { 262 struct aclstate *as = ic->ic_as; 263 264 return as->as_policy; 265 } 266 267 static const struct ieee80211_aclator mac = { 268 .iac_name = "mac", 269 .iac_attach = acl_attach, 270 .iac_detach = acl_detach, 271 .iac_check = acl_check, 272 .iac_add = acl_add, 273 .iac_remove = acl_remove, 274 .iac_flush = acl_free_all, 275 .iac_setpolicy = acl_setpolicy, 276 .iac_getpolicy = acl_getpolicy, 277 }; 278