ieee80211_acl.c revision 149028
11802Sphk/*-
21802Sphk * Copyright (c) 2004-2005 Sam Leffler, Errno Consulting
31802Sphk * All rights reserved.
41802Sphk *
51802Sphk * Redistribution and use in source and binary forms, with or without
61802Sphk * modification, are permitted provided that the following conditions
71802Sphk * are met:
81802Sphk * 1. Redistributions of source code must retain the above copyright
91802Sphk *    notice, this list of conditions and the following disclaimer.
101802Sphk * 2. Redistributions in binary form must reproduce the above copyright
111802Sphk *    notice, this list of conditions and the following disclaimer in the
121802Sphk *    documentation and/or other materials provided with the distribution.
131802Sphk * 3. The name of the author may not be used to endorse or promote products
141802Sphk *    derived from this software without specific prior written permission.
151802Sphk *
161802Sphk * Alternatively, this software may be distributed under the terms of the
171802Sphk * GNU General Public License ("GPL") version 2 as published by the Free
181802Sphk * Software Foundation.
191802Sphk *
201802Sphk * THIS SOFTWARE IS PROVIDED BY THE AUTHOR ``AS IS'' AND ANY EXPRESS OR
211802Sphk * IMPLIED WARRANTIES, INCLUDING, BUT NOT LIMITED TO, THE IMPLIED WARRANTIES
221802Sphk * OF MERCHANTABILITY AND FITNESS FOR A PARTICULAR PURPOSE ARE DISCLAIMED.
231802Sphk * IN NO EVENT SHALL THE AUTHOR BE LIABLE FOR ANY DIRECT, INDIRECT,
241802Sphk * INCIDENTAL, SPECIAL, EXEMPLARY, OR CONSEQUENTIAL DAMAGES (INCLUDING, BUT
251802Sphk * NOT LIMITED TO, PROCUREMENT OF SUBSTITUTE GOODS OR SERVICES; LOSS OF USE,
261802Sphk * DATA, OR PROFITS; OR BUSINESS INTERRUPTION) HOWEVER CAUSED AND ON ANY
271802Sphk * THEORY OF LIABILITY, WHETHER IN CONTRACT, STRICT LIABILITY, OR TORT
281802Sphk * (INCLUDING NEGLIGENCE OR OTHERWISE) ARISING IN ANY WAY OUT OF THE USE OF
291802Sphk * THIS SOFTWARE, EVEN IF ADVISED OF THE POSSIBILITY OF SUCH DAMAGE.
301802Sphk */
311802Sphk
321802Sphk#include <sys/cdefs.h>
331802Sphk__FBSDID("$FreeBSD: head/sys/net80211/ieee80211_acl.c 149028 2005-08-13 17:31:48Z sam $");
341802Sphk
351802Sphk/*
361802Sphk * IEEE 802.11 MAC ACL support.
371802Sphk *
381802Sphk * When this module is loaded the sender address of each received
391802Sphk * frame is passed to the iac_check method and the module indicates
401802Sphk * if the frame should be accepted or rejected.  If the policy is
411802Sphk * set to ACL_POLICY_OPEN then all frames are accepted w/o checking
421802Sphk * the address.  Otherwise, the address is looked up in the database
431802Sphk * and if found the frame is either accepted (ACL_POLICY_ALLOW)
441802Sphk * or rejected (ACL_POLICY_DENT).
451802Sphk */
461802Sphk#include <sys/param.h>
471802Sphk#include <sys/kernel.h>
481802Sphk#include <sys/systm.h>
491802Sphk#include <sys/mbuf.h>
501802Sphk#include <sys/module.h>
511802Sphk#include <sys/queue.h>
521802Sphk
531802Sphk#include <sys/socket.h>
544245Sphk
551802Sphk#include <net/if.h>
566596Sphk#include <net/if_media.h>
576596Sphk#include <net/ethernet.h>
586596Sphk#include <net/route.h>
596596Sphk
606596Sphk#include <net80211/ieee80211_var.h>
616596Sphk
626596Sphkenum {
636596Sphk	ACL_POLICY_OPEN		= 0,	/* open, don't check ACL's */
646596Sphk	ACL_POLICY_ALLOW	= 1,	/* allow traffic from MAC */
656596Sphk	ACL_POLICY_DENY		= 2,	/* deny traffic from MAC */
666596Sphk};
676596Sphk
686596Sphk#define	ACL_HASHSIZE	32
696596Sphk
706596Sphkstruct acl {
716596Sphk	TAILQ_ENTRY(acl)	acl_list;
726596Sphk	LIST_ENTRY(acl)		acl_hash;
736596Sphk	u_int8_t		acl_macaddr[IEEE80211_ADDR_LEN];
746596Sphk};
756596Sphkstruct aclstate {
766596Sphk	acl_lock_t		as_lock;
776596Sphk	int			as_policy;
786596Sphk	int			as_nacls;
796596Sphk	TAILQ_HEAD(, acl)	as_list;	/* list of all ACL's */
806596Sphk	LIST_HEAD(, acl)	as_hash[ACL_HASHSIZE];
816596Sphk	struct ieee80211com	*as_ic;
826596Sphk};
836596Sphk
846596Sphk/* simple hash is enough for variation of macaddr */
856596Sphk#define	ACL_HASH(addr)	\
866596Sphk	(((const u_int8_t *)(addr))[IEEE80211_ADDR_LEN - 1] % ACL_HASHSIZE)
876596Sphk
886596SphkMALLOC_DEFINE(M_80211_ACL, "acl", "802.11 station acl");
896596Sphk
906596Sphkstatic	int acl_free_all(struct ieee80211com *);
916596Sphk
926596Sphkstatic int
936596Sphkacl_attach(struct ieee80211com *ic)
941802Sphk{
951802Sphk	struct aclstate *as;
961802Sphk
971802Sphk	MALLOC(as, struct aclstate *, sizeof(struct aclstate),
981802Sphk		M_80211_ACL, M_NOWAIT | M_ZERO);
991802Sphk	if (as == NULL)
1001802Sphk		return 0;
1011802Sphk	ACL_LOCK_INIT(as, "acl");
1021802Sphk	TAILQ_INIT(&as->as_list);
1031802Sphk	as->as_policy = ACL_POLICY_OPEN;
1041802Sphk	as->as_ic = ic;
1051802Sphk	ic->ic_as = as;
1061802Sphk	return 1;
1071802Sphk}
1081802Sphk
1091802Sphkstatic void
1101802Sphkacl_detach(struct ieee80211com *ic)
1111802Sphk{
1121802Sphk	struct aclstate *as = ic->ic_as;
1131802Sphk
1141802Sphk	acl_free_all(ic);
1151802Sphk	ic->ic_as = NULL;
1161802Sphk	ACL_LOCK_DESTROY(as);
1171802Sphk	FREE(as, M_DEVBUF);
1181802Sphk}
1191802Sphk
1201802Sphkstatic __inline struct acl *
1211802Sphk_find_acl(struct aclstate *as, const u_int8_t *macaddr)
1221802Sphk{
1231802Sphk	struct acl *acl;
1241802Sphk	int hash;
1251802Sphk
1261802Sphk	hash = ACL_HASH(macaddr);
1271802Sphk	LIST_FOREACH(acl, &as->as_hash[hash], acl_hash) {
1281802Sphk		if (IEEE80211_ADDR_EQ(acl->acl_macaddr, macaddr))
1291802Sphk			return acl;
1301802Sphk	}
1311802Sphk	return NULL;
1321802Sphk}
1331802Sphk
1341802Sphkstatic void
1351802Sphk_acl_free(struct aclstate *as, struct acl *acl)
1361802Sphk{
1371802Sphk	ACL_LOCK_ASSERT(as);
1381802Sphk
1391802Sphk	TAILQ_REMOVE(&as->as_list, acl, acl_list);
1401802Sphk	LIST_REMOVE(acl, acl_hash);
1411802Sphk	FREE(acl, M_80211_ACL);
1421802Sphk	as->as_nacls--;
1431802Sphk}
1441802Sphk
1451802Sphkstatic int
1461802Sphkacl_check(struct ieee80211com *ic, const u_int8_t mac[IEEE80211_ADDR_LEN])
1471802Sphk{
1481802Sphk	struct aclstate *as = ic->ic_as;
1491802Sphk
1501802Sphk	switch (as->as_policy) {
1511802Sphk	case ACL_POLICY_OPEN:
1521802Sphk		return 1;
1531802Sphk	case ACL_POLICY_ALLOW:
1541802Sphk		return _find_acl(as, mac) != NULL;
1554245Sphk	case ACL_POLICY_DENY:
1561802Sphk		return _find_acl(as, mac) == NULL;
1571802Sphk	}
1581802Sphk	return 0;		/* should not happen */
1591802Sphk}
1601802Sphk
1611802Sphkstatic int
1621802Sphkacl_add(struct ieee80211com *ic, const u_int8_t mac[IEEE80211_ADDR_LEN])
1631802Sphk{
1641802Sphk	struct aclstate *as = ic->ic_as;
1651802Sphk	struct acl *acl, *new;
1661802Sphk	int hash;
1671802Sphk
1681802Sphk	MALLOC(new, struct acl *, sizeof(struct acl), M_80211_ACL, M_NOWAIT | M_ZERO);
1691802Sphk	if (new == NULL) {
1701802Sphk		IEEE80211_DPRINTF(ic, IEEE80211_MSG_ACL,
1711802Sphk			"ACL: add %s failed, no memory\n", ether_sprintf(mac));
1721802Sphk		/* XXX statistic */
1731802Sphk		return ENOMEM;
1741802Sphk	}
1751802Sphk
1761802Sphk	ACL_LOCK(as);
1771802Sphk	hash = ACL_HASH(mac);
1781802Sphk	LIST_FOREACH(acl, &as->as_hash[hash], acl_hash) {
1791802Sphk		if (IEEE80211_ADDR_EQ(acl->acl_macaddr, mac)) {
1801802Sphk			ACL_UNLOCK(as);
1811802Sphk			FREE(new, M_80211_ACL);
1821802Sphk			IEEE80211_DPRINTF(ic, IEEE80211_MSG_ACL,
1831802Sphk				"ACL: add %s failed, already present\n",
1841802Sphk				ether_sprintf(mac));
1851802Sphk			return EEXIST;
1861802Sphk		}
1871802Sphk	}
1881802Sphk	IEEE80211_ADDR_COPY(new->acl_macaddr, mac);
1891802Sphk	TAILQ_INSERT_TAIL(&as->as_list, new, acl_list);
1901802Sphk	LIST_INSERT_HEAD(&as->as_hash[hash], new, acl_hash);
1911802Sphk	as->as_nacls++;
1921802Sphk	ACL_UNLOCK(as);
1931802Sphk
1941802Sphk	IEEE80211_DPRINTF(ic, IEEE80211_MSG_ACL,
1951802Sphk		"ACL: add %s\n", ether_sprintf(mac));
1961802Sphk	return 0;
1971802Sphk}
1981802Sphk
1991802Sphkstatic int
2001802Sphkacl_remove(struct ieee80211com *ic, const u_int8_t mac[IEEE80211_ADDR_LEN])
2011802Sphk{
2021802Sphk	struct aclstate *as = ic->ic_as;
2031802Sphk	struct acl *acl;
2041802Sphk
2051802Sphk	ACL_LOCK(as);
2061802Sphk	acl = _find_acl(as, mac);
2071802Sphk	if (acl != NULL)
2081802Sphk		_acl_free(as, acl);
2091802Sphk	ACL_UNLOCK(as);
2101802Sphk
2111802Sphk	IEEE80211_DPRINTF(ic, IEEE80211_MSG_ACL,
2121802Sphk		"ACL: remove %s%s\n", ether_sprintf(mac),
2131802Sphk		acl == NULL ? ", not present" : "");
2141802Sphk
2151802Sphk	return (acl == NULL ? ENOENT : 0);
2161802Sphk}
2171802Sphk
2181802Sphkstatic int
2191802Sphkacl_free_all(struct ieee80211com *ic)
2201802Sphk{
2211802Sphk	struct aclstate *as = ic->ic_as;
2221802Sphk	struct acl *acl;
2231802Sphk
2241802Sphk	IEEE80211_DPRINTF(ic, IEEE80211_MSG_ACL, "ACL: %s\n", "free all");
2254245Sphk
2261802Sphk	ACL_LOCK(as);
2271802Sphk	while ((acl = TAILQ_FIRST(&as->as_list)) != NULL)
2281802Sphk		_acl_free(as, acl);
2291802Sphk	ACL_UNLOCK(as);
2301802Sphk
2311802Sphk	return 0;
2321802Sphk}
2331802Sphk
2341802Sphkstatic int
2351802Sphkacl_setpolicy(struct ieee80211com *ic, int policy)
2361802Sphk{
2371802Sphk	struct aclstate *as = ic->ic_as;
2381802Sphk
2391802Sphk	IEEE80211_DPRINTF(ic, IEEE80211_MSG_ACL,
2401802Sphk		"ACL: set policy to %u\n", policy);
2411802Sphk
2421802Sphk	switch (policy) {
2431802Sphk	case IEEE80211_MACCMD_POLICY_OPEN:
2441802Sphk		as->as_policy = ACL_POLICY_OPEN;
2451802Sphk		break;
2461802Sphk	case IEEE80211_MACCMD_POLICY_ALLOW:
2471802Sphk		as->as_policy = ACL_POLICY_ALLOW;
2481802Sphk		break;
2491802Sphk	case IEEE80211_MACCMD_POLICY_DENY:
2501802Sphk		as->as_policy = ACL_POLICY_DENY;
2511802Sphk		break;
2521802Sphk	default:
2531802Sphk		return EINVAL;
2541802Sphk	}
2551802Sphk	return 0;
2561802Sphk}
2571802Sphk
2581802Sphkstatic int
2591802Sphkacl_getpolicy(struct ieee80211com *ic)
2601802Sphk{
2611802Sphk	struct aclstate *as = ic->ic_as;
2621802Sphk
2631802Sphk	return as->as_policy;
2641802Sphk}
2651802Sphk
2661802Sphkstatic int
2671802Sphkacl_setioctl(struct ieee80211com *ic, struct ieee80211req *ireq)
2681802Sphk{
2691802Sphk
2701802Sphk	return EINVAL;
2711802Sphk}
2721802Sphk
2731802Sphkstatic int
2741802Sphkacl_getioctl(struct ieee80211com *ic, struct ieee80211req *ireq)
2751802Sphk{
2761802Sphk	struct aclstate *as = ic->ic_as;
2771802Sphk	struct acl *acl;
2781802Sphk	struct ieee80211req_maclist *ap;
2791802Sphk	int error, space, i;
2801802Sphk
2811802Sphk	switch (ireq->i_val) {
2821802Sphk	case IEEE80211_MACCMD_POLICY:
2831802Sphk		ireq->i_val = as->as_policy;
2841802Sphk		return 0;
2851802Sphk	case IEEE80211_MACCMD_LIST:
2861802Sphk		space = as->as_nacls * IEEE80211_ADDR_LEN;
2871802Sphk		if (ireq->i_len == 0) {
2881802Sphk			ireq->i_len = space;	/* return required space */
2891802Sphk			return 0;		/* NB: must not error */
2901802Sphk		}
2911802Sphk		MALLOC(ap, struct ieee80211req_maclist *, space,
2921802Sphk			M_TEMP, M_NOWAIT);
2931802Sphk		if (ap == NULL)
2941802Sphk			return ENOMEM;
2951802Sphk		i = 0;
2961802Sphk		ACL_LOCK(as);
2971802Sphk		TAILQ_FOREACH(acl, &as->as_list, acl_list) {
2981802Sphk			IEEE80211_ADDR_COPY(ap[i].ml_macaddr, acl->acl_macaddr);
2991802Sphk			i++;
3001802Sphk		}
3011802Sphk		ACL_UNLOCK(as);
3021802Sphk		if (ireq->i_len >= space) {
3031802Sphk			error = copyout(ap, ireq->i_data, space);
3041802Sphk			ireq->i_len = space;
3051802Sphk		} else
3061802Sphk			error = copyout(ap, ireq->i_data, ireq->i_len);
3071802Sphk		FREE(ap, M_TEMP);
3081802Sphk		return error;
3091802Sphk	}
3101802Sphk	return EINVAL;
3111802Sphk}
3121802Sphk
313static const struct ieee80211_aclator mac = {
314	.iac_name	= "mac",
315	.iac_attach	= acl_attach,
316	.iac_detach	= acl_detach,
317	.iac_check	= acl_check,
318	.iac_add	= acl_add,
319	.iac_remove	= acl_remove,
320	.iac_flush	= acl_free_all,
321	.iac_setpolicy	= acl_setpolicy,
322	.iac_getpolicy	= acl_getpolicy,
323	.iac_setioctl	= acl_setioctl,
324	.iac_getioctl	= acl_getioctl,
325};
326
327/*
328 * Module glue.
329 */
330static int
331wlan_acl_modevent(module_t mod, int type, void *unused)
332{
333	switch (type) {
334	case MOD_LOAD:
335		if (bootverbose)
336			printf("wlan: <802.11 MAC ACL support>\n");
337		ieee80211_aclator_register(&mac);
338		return 0;
339	case MOD_UNLOAD:
340		ieee80211_aclator_unregister(&mac);
341		return 0;
342	}
343	return EINVAL;
344}
345
346static moduledata_t wlan_acl_mod = {
347	"wlan_acl",
348	wlan_acl_modevent,
349	0
350};
351DECLARE_MODULE(wlan_acl, wlan_acl_mod, SI_SUB_DRIVERS, SI_ORDER_FIRST);
352MODULE_VERSION(wlan_acl, 1);
353MODULE_DEPEND(wlan_acl, wlan, 1, 1, 1);
354