ieee80211_acl.c revision 170530
11541Srgrimes/*-
21541Srgrimes * Copyright (c) 2004-2007 Sam Leffler, Errno Consulting
31541Srgrimes * All rights reserved.
41541Srgrimes *
51541Srgrimes * Redistribution and use in source and binary forms, with or without
61541Srgrimes * modification, are permitted provided that the following conditions
71541Srgrimes * are met:
81541Srgrimes * 1. Redistributions of source code must retain the above copyright
91541Srgrimes *    notice, this list of conditions and the following disclaimer.
101541Srgrimes * 2. Redistributions in binary form must reproduce the above copyright
111541Srgrimes *    notice, this list of conditions and the following disclaimer in the
121541Srgrimes *    documentation and/or other materials provided with the distribution.
131541Srgrimes *
141541Srgrimes * THIS SOFTWARE IS PROVIDED BY THE AUTHOR ``AS IS'' AND ANY EXPRESS OR
151541Srgrimes * IMPLIED WARRANTIES, INCLUDING, BUT NOT LIMITED TO, THE IMPLIED WARRANTIES
161541Srgrimes * OF MERCHANTABILITY AND FITNESS FOR A PARTICULAR PURPOSE ARE DISCLAIMED.
171541Srgrimes * IN NO EVENT SHALL THE AUTHOR BE LIABLE FOR ANY DIRECT, INDIRECT,
181541Srgrimes * INCIDENTAL, SPECIAL, EXEMPLARY, OR CONSEQUENTIAL DAMAGES (INCLUDING, BUT
191541Srgrimes * NOT LIMITED TO, PROCUREMENT OF SUBSTITUTE GOODS OR SERVICES; LOSS OF USE,
201541Srgrimes * DATA, OR PROFITS; OR BUSINESS INTERRUPTION) HOWEVER CAUSED AND ON ANY
211541Srgrimes * THEORY OF LIABILITY, WHETHER IN CONTRACT, STRICT LIABILITY, OR TORT
221541Srgrimes * (INCLUDING NEGLIGENCE OR OTHERWISE) ARISING IN ANY WAY OUT OF THE USE OF
231541Srgrimes * THIS SOFTWARE, EVEN IF ADVISED OF THE POSSIBILITY OF SUCH DAMAGE.
241541Srgrimes */
251541Srgrimes
261541Srgrimes#include <sys/cdefs.h>
271541Srgrimes__FBSDID("$FreeBSD: head/sys/net80211/ieee80211_acl.c 170530 2007-06-11 03:36:55Z sam $");
281541Srgrimes
291541Srgrimes/*
301541Srgrimes * IEEE 802.11 MAC ACL support.
311541Srgrimes *
321541Srgrimes * When this module is loaded the sender address of each received
331541Srgrimes * frame is passed to the iac_check method and the module indicates
341541Srgrimes * if the frame should be accepted or rejected.  If the policy is
351541Srgrimes * set to ACL_POLICY_OPEN then all frames are accepted w/o checking
361541Srgrimes * the address.  Otherwise, the address is looked up in the database
371541Srgrimes * and if found the frame is either accepted (ACL_POLICY_ALLOW)
381541Srgrimes * or rejected (ACL_POLICY_DENT).
3944549Sdfr */
401541Srgrimes#include <sys/param.h>
411541Srgrimes#include <sys/kernel.h>
421541Srgrimes#include <sys/systm.h>
431541Srgrimes#include <sys/mbuf.h>
442112Swollman#include <sys/module.h>
452946Swollman#include <sys/queue.h>
461541Srgrimes
4738869Sbde#include <sys/socket.h>
481541Srgrimes
491541Srgrimes#include <net/if.h>
5029653Sdyson#include <net/if_media.h>
511541Srgrimes#include <net/ethernet.h>
5212577Sbde#include <net/route.h>
5330354Sphk
5430354Sphk#include <net80211/ieee80211_var.h>
5510358Sjulian
5640435Speterenum {
5740435Speter	ACL_POLICY_OPEN		= 0,	/* open, don't check ACL's */
5840435Speter	ACL_POLICY_ALLOW	= 1,	/* allow traffic from MAC */
5940435Speter	ACL_POLICY_DENY		= 2,	/* deny traffic from MAC */
6040435Speter};
6140435Speter
6240435Speter#define	ACL_HASHSIZE	32
6340435Speter
6440435Speterstruct acl {
6540435Speter	TAILQ_ENTRY(acl)	acl_list;
6640435Speter	LIST_ENTRY(acl)		acl_hash;
6729653Sdyson	uint8_t			acl_macaddr[IEEE80211_ADDR_LEN];
6829653Sdyson};
6929653Sdysonstruct aclstate {
7029653Sdyson	acl_lock_t		as_lock;
7129653Sdyson	int			as_policy;
721541Srgrimes	int			as_nacls;
731541Srgrimes	TAILQ_HEAD(, acl)	as_list;	/* list of all ACL's */
741541Srgrimes	LIST_HEAD(, acl)	as_hash[ACL_HASHSIZE];
751541Srgrimes	struct ieee80211com	*as_ic;
761541Srgrimes};
771541Srgrimes
781541Srgrimes/* simple hash is enough for variation of macaddr */
791541Srgrimes#define	ACL_HASH(addr)	\
801541Srgrimes	(((const uint8_t *)(addr))[IEEE80211_ADDR_LEN - 1] % ACL_HASHSIZE)
811541Srgrimes
821541SrgrimesMALLOC_DEFINE(M_80211_ACL, "acl", "802.11 station acl");
831541Srgrimes
841541Srgrimesstatic	int acl_free_all(struct ieee80211com *);
851541Srgrimes
861541Srgrimesstatic int
8741056Speteracl_attach(struct ieee80211com *ic)
8841056Speter{
8943311Sdillon	struct aclstate *as;
9041056Speter
9141056Speter	MALLOC(as, struct aclstate *, sizeof(struct aclstate),
9241056Speter		M_80211_ACL, M_NOWAIT | M_ZERO);
9341056Speter	if (as == NULL)
9441056Speter		return 0;
9541056Speter	ACL_LOCK_INIT(as, "acl");
9641056Speter	TAILQ_INIT(&as->as_list);
9741056Speter	as->as_policy = ACL_POLICY_OPEN;
9841056Speter	as->as_ic = ic;
9941056Speter	ic->ic_as = as;
1001541Srgrimes	return 1;
10141056Speter}
10212158Sbde
10312158Sbdestatic void
1041541Srgrimesacl_detach(struct ieee80211com *ic)
10543311Sdillon{
1061541Srgrimes	struct aclstate *as = ic->ic_as;
10741056Speter
10841056Speter	acl_free_all(ic);
10941056Speter	ic->ic_as = NULL;
1101541Srgrimes	ACL_LOCK_DESTROY(as);
11141056Speter	FREE(as, M_DEVBUF);
11241056Speter}
11341056Speter
11441056Speterstatic __inline struct acl *
1151541Srgrimes_find_acl(struct aclstate *as, const uint8_t *macaddr)
11641056Speter{
11741056Speter	struct acl *acl;
11841056Speter	int hash;
11941056Speter
12041056Speter	hash = ACL_HASH(macaddr);
12141056Speter	LIST_FOREACH(acl, &as->as_hash[hash], acl_hash) {
12241056Speter		if (IEEE80211_ADDR_EQ(acl->acl_macaddr, macaddr))
12340435Speter			return acl;
12441056Speter	}
12540435Speter	return NULL;
12641056Speter}
12741056Speter
12841056Speterstatic void
12941056Speter_acl_free(struct aclstate *as, struct acl *acl)
13041056Speter{
13140435Speter	ACL_LOCK_ASSERT(as);
13240880Speter
13341056Speter	TAILQ_REMOVE(&as->as_list, acl, acl_list);
13441056Speter	LIST_REMOVE(acl, acl_hash);
13541056Speter	FREE(acl, M_80211_ACL);
13641056Speter	as->as_nacls--;
13741056Speter}
13841056Speter
13941056Speterstatic int
14041056Speteracl_check(struct ieee80211com *ic, const uint8_t mac[IEEE80211_ADDR_LEN])
14141056Speter{
14241056Speter	struct aclstate *as = ic->ic_as;
14341056Speter
14441056Speter	switch (as->as_policy) {
14541056Speter	case ACL_POLICY_OPEN:
14641056Speter		return 1;
14741056Speter	case ACL_POLICY_ALLOW:
14841056Speter		return _find_acl(as, mac) != NULL;
14941056Speter	case ACL_POLICY_DENY:
15041056Speter		return _find_acl(as, mac) == NULL;
15141056Speter	}
15241056Speter	return 0;		/* should not happen */
15340435Speter}
15441056Speter
15540435Speterstatic int
15641056Speteracl_add(struct ieee80211com *ic, const uint8_t mac[IEEE80211_ADDR_LEN])
15743311Sdillon{
15841056Speter	struct aclstate *as = ic->ic_as;
15943311Sdillon	struct acl *acl, *new;
16043311Sdillon	int hash;
16141056Speter
16241056Speter	MALLOC(new, struct acl *, sizeof(struct acl), M_80211_ACL, M_NOWAIT | M_ZERO);
16341056Speter	if (new == NULL) {
16441056Speter		IEEE80211_DPRINTF(ic, IEEE80211_MSG_ACL,
16541591Sarchie			"ACL: add %s failed, no memory\n", ether_sprintf(mac));
16641056Speter		/* XXX statistic */
16743311Sdillon		return ENOMEM;
16843311Sdillon	}
16941056Speter
17041056Speter	ACL_LOCK(as);
17141056Speter	hash = ACL_HASH(mac);
17241056Speter	LIST_FOREACH(acl, &as->as_hash[hash], acl_hash) {
17341056Speter		if (IEEE80211_ADDR_EQ(acl->acl_macaddr, mac)) {
17441056Speter			ACL_UNLOCK(as);
17541056Speter			FREE(new, M_80211_ACL);
17641056Speter			IEEE80211_DPRINTF(ic, IEEE80211_MSG_ACL,
17741056Speter				"ACL: add %s failed, already present\n",
17841056Speter				ether_sprintf(mac));
17941056Speter			return EEXIST;
18041056Speter		}
18141056Speter	}
18241056Speter	IEEE80211_ADDR_COPY(new->acl_macaddr, mac);
18341056Speter	TAILQ_INSERT_TAIL(&as->as_list, new, acl_list);
18441056Speter	LIST_INSERT_HEAD(&as->as_hash[hash], new, acl_hash);
18541056Speter	as->as_nacls++;
18641056Speter	ACL_UNLOCK(as);
18741056Speter
18841056Speter	IEEE80211_DPRINTF(ic, IEEE80211_MSG_ACL,
1891541Srgrimes		"ACL: add %s\n", ether_sprintf(mac));
19041056Speter	return 0;
19141056Speter}
19241056Speter
19341056Speterstatic int
19441056Speteracl_remove(struct ieee80211com *ic, const uint8_t mac[IEEE80211_ADDR_LEN])
19541056Speter{
19641056Speter	struct aclstate *as = ic->ic_as;
19741056Speter	struct acl *acl;
19841056Speter
19941056Speter	ACL_LOCK(as);
20041056Speter	acl = _find_acl(as, mac);
20141056Speter	if (acl != NULL)
20241056Speter		_acl_free(as, acl);
20341056Speter	ACL_UNLOCK(as);
20441056Speter
20541056Speter	IEEE80211_DPRINTF(ic, IEEE80211_MSG_ACL,
20641056Speter		"ACL: remove %s%s\n", ether_sprintf(mac),
20741056Speter		acl == NULL ? ", not present" : "");
20841056Speter
20941056Speter	return (acl == NULL ? ENOENT : 0);
21041056Speter}
21141056Speter
21241056Speterstatic int
21341056Speteracl_free_all(struct ieee80211com *ic)
21441056Speter{
21541056Speter	struct aclstate *as = ic->ic_as;
21641056Speter	struct acl *acl;
21741056Speter
21841056Speter	IEEE80211_DPRINTF(ic, IEEE80211_MSG_ACL, "ACL: %s\n", "free all");
2191541Srgrimes
22041056Speter	ACL_LOCK(as);
2211541Srgrimes	while ((acl = TAILQ_FIRST(&as->as_list)) != NULL)
2221541Srgrimes		_acl_free(as, acl);
22341056Speter	ACL_UNLOCK(as);
22443311Sdillon
2251541Srgrimes	return 0;
22643311Sdillon}
22743311Sdillon
22841056Speterstatic int
22941056Speteracl_setpolicy(struct ieee80211com *ic, int policy)
23041056Speter{
23141056Speter	struct aclstate *as = ic->ic_as;
23241056Speter
2331541Srgrimes	IEEE80211_DPRINTF(ic, IEEE80211_MSG_ACL,
23443351Sdillon		"ACL: set policy to %u\n", policy);
23541056Speter
23641056Speter	switch (policy) {
23741056Speter	case IEEE80211_MACCMD_POLICY_OPEN:
23841056Speter		as->as_policy = ACL_POLICY_OPEN;
23941056Speter		break;
24041056Speter	case IEEE80211_MACCMD_POLICY_ALLOW:
24141056Speter		as->as_policy = ACL_POLICY_ALLOW;
24241056Speter		break;
24341056Speter	case IEEE80211_MACCMD_POLICY_DENY:
24441056Speter		as->as_policy = ACL_POLICY_DENY;
24541056Speter		break;
24641056Speter	default:
24741056Speter		return EINVAL;
24841056Speter	}
24941056Speter	return 0;
25041056Speter}
25141056Speter
25241056Speterstatic int
25341056Speteracl_getpolicy(struct ieee80211com *ic)
25441056Speter{
25541056Speter	struct aclstate *as = ic->ic_as;
25641056Speter
25741056Speter	return as->as_policy;
25841056Speter}
25941056Speter
26041056Speterstatic int
26141056Speteracl_setioctl(struct ieee80211com *ic, struct ieee80211req *ireq)
26241056Speter{
26341056Speter
26441056Speter	return EINVAL;
26541056Speter}
26641056Speter
26741056Speterstatic int
26841056Speteracl_getioctl(struct ieee80211com *ic, struct ieee80211req *ireq)
26941056Speter{
27041056Speter	struct aclstate *as = ic->ic_as;
27141056Speter	struct acl *acl;
27241056Speter	struct ieee80211req_maclist *ap;
27341056Speter	int error, space, i;
27441056Speter
27541056Speter	switch (ireq->i_val) {
27641056Speter	case IEEE80211_MACCMD_POLICY:
27741056Speter		ireq->i_val = as->as_policy;
27841056Speter		return 0;
27941056Speter	case IEEE80211_MACCMD_LIST:
28041056Speter		space = as->as_nacls * IEEE80211_ADDR_LEN;
28141056Speter		if (ireq->i_len == 0) {
28241056Speter			ireq->i_len = space;	/* return required space */
28341056Speter			return 0;		/* NB: must not error */
28441056Speter		}
28541056Speter		MALLOC(ap, struct ieee80211req_maclist *, space,
28643311Sdillon			M_TEMP, M_NOWAIT);
28741056Speter		if (ap == NULL)
28841056Speter			return ENOMEM;
28941056Speter		i = 0;
29041056Speter		ACL_LOCK(as);
29141056Speter		TAILQ_FOREACH(acl, &as->as_list, acl_list) {
29241056Speter			IEEE80211_ADDR_COPY(ap[i].ml_macaddr, acl->acl_macaddr);
29341056Speter			i++;
29441056Speter		}
29541056Speter		ACL_UNLOCK(as);
2961541Srgrimes		if (ireq->i_len >= space) {
2971541Srgrimes			error = copyout(ap, ireq->i_data, space);
2981541Srgrimes			ireq->i_len = space;
2991541Srgrimes		} else
3001541Srgrimes			error = copyout(ap, ireq->i_data, ireq->i_len);
3011541Srgrimes		FREE(ap, M_TEMP);
3021541Srgrimes		return error;
3031541Srgrimes	}
3041541Srgrimes	return EINVAL;
3051541Srgrimes}
30610358Sjulian
30710358Sjulianstatic const struct ieee80211_aclator mac = {
30841056Speter	.iac_name	= "mac",
3091541Srgrimes	.iac_attach	= acl_attach,
3101541Srgrimes	.iac_detach	= acl_detach,
31129653Sdyson	.iac_check	= acl_check,
31229653Sdyson	.iac_add	= acl_add,
3131541Srgrimes	.iac_remove	= acl_remove,
3141541Srgrimes	.iac_flush	= acl_free_all,
3151541Srgrimes	.iac_setpolicy	= acl_setpolicy,
3161541Srgrimes	.iac_getpolicy	= acl_getpolicy,
3171541Srgrimes	.iac_setioctl	= acl_setioctl,
3181541Srgrimes	.iac_getioctl	= acl_getioctl,
3191541Srgrimes};
3201541Srgrimes
3211541Srgrimes/*
3221541Srgrimes * Module glue.
32339975Sobrien */
3241541Srgrimesstatic int
3251541Srgrimeswlan_acl_modevent(module_t mod, int type, void *unused)
32640435Speter{
32740435Speter	switch (type) {
32841056Speter	case MOD_LOAD:
32940435Speter		if (bootverbose)
33040435Speter			printf("wlan: <802.11 MAC ACL support>\n");
33141056Speter		ieee80211_aclator_register(&mac);
33240435Speter		return 0;
33340435Speter	case MOD_UNLOAD:
33444549Sdfr		ieee80211_aclator_unregister(&mac);
33540435Speter		return 0;
33640435Speter	}
33740435Speter	return EINVAL;
33840435Speter}
33940435Speter
34040435Speterstatic moduledata_t wlan_acl_mod = {
34144549Sdfr	"wlan_acl",
34240435Speter	wlan_acl_modevent,
34340435Speter	0
34440435Speter};
34540435SpeterDECLARE_MODULE(wlan_acl, wlan_acl_mod, SI_SUB_DRIVERS, SI_ORDER_FIRST);
34640435SpeterMODULE_VERSION(wlan_acl, 1);
34740435SpeterMODULE_DEPEND(wlan_acl, wlan, 1, 1, 1);
34840435Speter