Home | History | Annotate | Line # | Download | only in net80211
ieee80211_acl.c revision 1.7.46.1
      1 /*-
      2  * Copyright (c) 2004-2007 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  *
     14  * THIS SOFTWARE IS PROVIDED BY THE AUTHOR ``AS IS'' AND ANY EXPRESS OR
     15  * IMPLIED WARRANTIES, INCLUDING, BUT NOT LIMITED TO, THE IMPLIED WARRANTIES
     16  * OF MERCHANTABILITY AND FITNESS FOR A PARTICULAR PURPOSE ARE DISCLAIMED.
     17  * IN NO EVENT SHALL THE AUTHOR BE LIABLE FOR ANY DIRECT, INDIRECT,
     18  * INCIDENTAL, SPECIAL, EXEMPLARY, OR CONSEQUENTIAL DAMAGES (INCLUDING, BUT
     19  * NOT LIMITED TO, PROCUREMENT OF SUBSTITUTE GOODS OR SERVICES; LOSS OF USE,
     20  * DATA, OR PROFITS; OR BUSINESS INTERRUPTION) HOWEVER CAUSED AND ON ANY
     21  * THEORY OF LIABILITY, WHETHER IN CONTRACT, STRICT LIABILITY, OR TORT
     22  * (INCLUDING NEGLIGENCE OR OTHERWISE) ARISING IN ANY WAY OUT OF THE USE OF
     23  * THIS SOFTWARE, EVEN IF ADVISED OF THE POSSIBILITY OF SUCH DAMAGE.
     24  */
     25 
     26 #include <sys/cdefs.h>
     27 #ifdef __FreeBSD__
     28 __FBSDID("$FreeBSD: src/sys/net80211/ieee80211_acl.c,v 1.6 2007/06/11 03:36:54 sam Exp $");
     29 #endif
     30 #ifdef __NetBSD__
     31 __KERNEL_RCSID(0, "$NetBSD: ieee80211_acl.c,v 1.7.46.1 2008/02/22 16:50:25 skrll Exp $");
     32 #endif
     33 
     34 /*
     35  * IEEE 802.11 MAC ACL support.
     36  *
     37  * When this module is loaded the sender address of each received
     38  * frame is passed to the iac_check method and the module indicates
     39  * if the frame should be accepted or rejected.  If the policy is
     40  * set to ACL_POLICY_OPEN then all frames are accepted w/o checking
     41  * the address.  Otherwise, the address is looked up in the database
     42  * and if found the frame is either accepted (ACL_POLICY_ALLOW)
     43  * or rejected (ACL_POLICY_DENT).
     44  */
     45 #include <sys/param.h>
     46 #include <sys/kernel.h>
     47 #include <sys/systm.h>
     48 #include <sys/mbuf.h>
     49 #include <sys/queue.h>
     50 
     51 #include <sys/socket.h>
     52 
     53 #include <net/if.h>
     54 #include <net/if_media.h>
     55 #include <net/if_ether.h>
     56 #include <net/route.h>
     57 
     58 #include <net80211/ieee80211_var.h>
     59 
     60 enum {
     61 	ACL_POLICY_OPEN		= 0,	/* open, don't check ACL's */
     62 	ACL_POLICY_ALLOW	= 1,	/* allow traffic from MAC */
     63 	ACL_POLICY_DENY		= 2,	/* deny traffic from MAC */
     64 };
     65 
     66 #define	ACL_HASHSIZE	32
     67 
     68 struct acl {
     69 	TAILQ_ENTRY(acl)	acl_list;
     70 	LIST_ENTRY(acl)		acl_hash;
     71 	uint8_t			acl_macaddr[IEEE80211_ADDR_LEN];
     72 };
     73 struct aclstate {
     74 	acl_lock_t		as_lock;
     75 	int			as_policy;
     76 	int			as_nacls;
     77 	TAILQ_HEAD(, acl)	as_list;	/* list of all ACL's */
     78 	LIST_HEAD(, acl)	as_hash[ACL_HASHSIZE];
     79 	struct ieee80211com	*as_ic;
     80 };
     81 
     82 /* simple hash is enough for variation of macaddr */
     83 #define	ACL_HASH(addr)	\
     84 	(((const uint8_t *)(addr))[IEEE80211_ADDR_LEN - 1] % ACL_HASHSIZE)
     85 
     86 MALLOC_DEFINE(M_80211_ACL, "acl", "802.11 station acl");
     87 
     88 static	int acl_free_all(struct ieee80211com *);
     89 
     90 static int
     91 acl_attach(struct ieee80211com *ic)
     92 {
     93 	struct aclstate *as;
     94 
     95 	as = malloc(sizeof(struct aclstate), M_80211_ACL,
     96 	    M_NOWAIT | M_ZERO);
     97 	if (as == NULL)
     98 		return 0;
     99 	ACL_LOCK_INIT(as, "acl");
    100 	TAILQ_INIT(&as->as_list);
    101 	as->as_policy = ACL_POLICY_OPEN;
    102 	as->as_ic = ic;
    103 	ic->ic_as = as;
    104 	return 1;
    105 }
    106 
    107 static void
    108 acl_detach(struct ieee80211com *ic)
    109 {
    110 	struct aclstate *as = ic->ic_as;
    111 
    112 	acl_free_all(ic);
    113 	ic->ic_as = NULL;
    114 	ACL_LOCK_DESTROY(as);
    115 	FREE(as, M_DEVBUF);
    116 }
    117 
    118 static __inline struct acl *
    119 _find_acl(struct aclstate *as, const uint8_t *macaddr)
    120 {
    121 	struct acl *acl;
    122 	int hash;
    123 
    124 	hash = ACL_HASH(macaddr);
    125 	LIST_FOREACH(acl, &as->as_hash[hash], acl_hash) {
    126 		if (IEEE80211_ADDR_EQ(acl->acl_macaddr, macaddr))
    127 			return acl;
    128 	}
    129 	return NULL;
    130 }
    131 
    132 static void
    133 _acl_free(struct aclstate *as, struct acl *acl)
    134 {
    135 	ACL_LOCK_ASSERT(as);
    136 
    137 	TAILQ_REMOVE(&as->as_list, acl, acl_list);
    138 	LIST_REMOVE(acl, acl_hash);
    139 	FREE(acl, M_80211_ACL);
    140 	as->as_nacls--;
    141 }
    142 
    143 static int
    144 acl_check(struct ieee80211com *ic, const uint8_t mac[IEEE80211_ADDR_LEN])
    145 {
    146 	struct aclstate *as = ic->ic_as;
    147 
    148 	switch (as->as_policy) {
    149 	case ACL_POLICY_OPEN:
    150 		return 1;
    151 	case ACL_POLICY_ALLOW:
    152 		return _find_acl(as, mac) != NULL;
    153 	case ACL_POLICY_DENY:
    154 		return _find_acl(as, mac) == NULL;
    155 	}
    156 	return 0;		/* should not happen */
    157 }
    158 
    159 static int
    160 acl_add(struct ieee80211com *ic, const uint8_t mac[IEEE80211_ADDR_LEN])
    161 {
    162 	struct aclstate *as = ic->ic_as;
    163 	struct acl *acl, *new;
    164 	int hash;
    165 
    166 	new = malloc(sizeof(struct acl), M_80211_ACL, M_NOWAIT | M_ZERO);
    167 	if (new == NULL) {
    168 		IEEE80211_DPRINTF(ic, IEEE80211_MSG_ACL,
    169 			"ACL: add %s failed, no memory\n", ether_sprintf(mac));
    170 		/* XXX statistic */
    171 		return ENOMEM;
    172 	}
    173 
    174 	ACL_LOCK(as);
    175 	hash = ACL_HASH(mac);
    176 	LIST_FOREACH(acl, &as->as_hash[hash], acl_hash) {
    177 		if (IEEE80211_ADDR_EQ(acl->acl_macaddr, mac)) {
    178 			ACL_UNLOCK(as);
    179 			FREE(new, M_80211_ACL);
    180 			IEEE80211_DPRINTF(ic, IEEE80211_MSG_ACL,
    181 				"ACL: add %s failed, already present\n",
    182 				ether_sprintf(mac));
    183 			return EEXIST;
    184 		}
    185 	}
    186 	IEEE80211_ADDR_COPY(new->acl_macaddr, mac);
    187 	TAILQ_INSERT_TAIL(&as->as_list, new, acl_list);
    188 	LIST_INSERT_HEAD(&as->as_hash[hash], new, acl_hash);
    189 	as->as_nacls++;
    190 	ACL_UNLOCK(as);
    191 
    192 	IEEE80211_DPRINTF(ic, IEEE80211_MSG_ACL,
    193 		"ACL: add %s\n", ether_sprintf(mac));
    194 	return 0;
    195 }
    196 
    197 static int
    198 acl_remove(struct ieee80211com *ic, const uint8_t mac[IEEE80211_ADDR_LEN])
    199 {
    200 	struct aclstate *as = ic->ic_as;
    201 	struct acl *acl;
    202 
    203 	ACL_LOCK(as);
    204 	acl = _find_acl(as, mac);
    205 	if (acl != NULL)
    206 		_acl_free(as, acl);
    207 	ACL_UNLOCK(as);
    208 
    209 	IEEE80211_DPRINTF(ic, IEEE80211_MSG_ACL,
    210 		"ACL: remove %s%s\n", ether_sprintf(mac),
    211 		acl == NULL ? ", not present" : "");
    212 
    213 	return (acl == NULL ? ENOENT : 0);
    214 }
    215 
    216 static int
    217 acl_free_all(struct ieee80211com *ic)
    218 {
    219 	struct aclstate *as = ic->ic_as;
    220 	struct acl *acl;
    221 
    222 	IEEE80211_DPRINTF(ic, IEEE80211_MSG_ACL, "ACL: %s\n", "free all");
    223 
    224 	ACL_LOCK(as);
    225 	while ((acl = TAILQ_FIRST(&as->as_list)) != NULL)
    226 		_acl_free(as, acl);
    227 	ACL_UNLOCK(as);
    228 
    229 	return 0;
    230 }
    231 
    232 static int
    233 acl_setpolicy(struct ieee80211com *ic, int policy)
    234 {
    235 	struct aclstate *as = ic->ic_as;
    236 
    237 	IEEE80211_DPRINTF(ic, IEEE80211_MSG_ACL,
    238 		"ACL: set policy to %u\n", policy);
    239 
    240 	switch (policy) {
    241 	case IEEE80211_MACCMD_POLICY_OPEN:
    242 		as->as_policy = ACL_POLICY_OPEN;
    243 		break;
    244 	case IEEE80211_MACCMD_POLICY_ALLOW:
    245 		as->as_policy = ACL_POLICY_ALLOW;
    246 		break;
    247 	case IEEE80211_MACCMD_POLICY_DENY:
    248 		as->as_policy = ACL_POLICY_DENY;
    249 		break;
    250 	default:
    251 		return EINVAL;
    252 	}
    253 	return 0;
    254 }
    255 
    256 static int
    257 acl_getpolicy(struct ieee80211com *ic)
    258 {
    259 	struct aclstate *as = ic->ic_as;
    260 
    261 	return as->as_policy;
    262 }
    263 
    264 static int
    265 acl_setioctl(struct ieee80211com *ic,
    266     struct ieee80211req *ireq)
    267 {
    268 
    269 	return EINVAL;
    270 }
    271 
    272 static int
    273 acl_getioctl(struct ieee80211com *ic, struct ieee80211req *ireq)
    274 {
    275 	struct aclstate *as = ic->ic_as;
    276 	struct acl *acl;
    277 	struct ieee80211req_maclist *ap;
    278 	int error, space, i;
    279 
    280 	switch (ireq->i_val) {
    281 	case IEEE80211_MACCMD_POLICY:
    282 		ireq->i_val = as->as_policy;
    283 		return 0;
    284 	case IEEE80211_MACCMD_LIST:
    285 		space = as->as_nacls * IEEE80211_ADDR_LEN;
    286 		if (ireq->i_len == 0) {
    287 			ireq->i_len = space;	/* return required space */
    288 			return 0;		/* NB: must not error */
    289 		}
    290 		ap = malloc(space, M_TEMP, M_NOWAIT);
    291 		if (ap == NULL)
    292 			return ENOMEM;
    293 		i = 0;
    294 		ACL_LOCK(as);
    295 		TAILQ_FOREACH(acl, &as->as_list, acl_list) {
    296 			IEEE80211_ADDR_COPY(ap[i].ml_macaddr, acl->acl_macaddr);
    297 			i++;
    298 		}
    299 		ACL_UNLOCK(as);
    300 		if (ireq->i_len >= space) {
    301 			error = copyout(ap, ireq->i_data, space);
    302 			ireq->i_len = space;
    303 		} else
    304 			error = copyout(ap, ireq->i_data, ireq->i_len);
    305 		FREE(ap, M_TEMP);
    306 		return error;
    307 	}
    308 	return EINVAL;
    309 }
    310 
    311 static const struct ieee80211_aclator mac = {
    312 	.iac_name	= "mac",
    313 	.iac_attach	= acl_attach,
    314 	.iac_detach	= acl_detach,
    315 	.iac_check	= acl_check,
    316 	.iac_add	= acl_add,
    317 	.iac_remove	= acl_remove,
    318 	.iac_flush	= acl_free_all,
    319 	.iac_setpolicy	= acl_setpolicy,
    320 	.iac_getpolicy	= acl_getpolicy,
    321 	.iac_setioctl	= acl_setioctl,
    322 	.iac_getioctl	= acl_getioctl,
    323 };
    324