xref: /freebsd/sys/net80211/ieee80211_acl.c (revision b032f27c)
18a1b9b6aSSam Leffler /*-
2b032f27cSSam Leffler  * Copyright (c) 2004-2008 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  *
148a1b9b6aSSam Leffler  * THIS SOFTWARE IS PROVIDED BY THE AUTHOR ``AS IS'' AND ANY EXPRESS OR
158a1b9b6aSSam Leffler  * IMPLIED WARRANTIES, INCLUDING, BUT NOT LIMITED TO, THE IMPLIED WARRANTIES
168a1b9b6aSSam Leffler  * OF MERCHANTABILITY AND FITNESS FOR A PARTICULAR PURPOSE ARE DISCLAIMED.
178a1b9b6aSSam Leffler  * IN NO EVENT SHALL THE AUTHOR BE LIABLE FOR ANY DIRECT, INDIRECT,
188a1b9b6aSSam Leffler  * INCIDENTAL, SPECIAL, EXEMPLARY, OR CONSEQUENTIAL DAMAGES (INCLUDING, BUT
198a1b9b6aSSam Leffler  * NOT LIMITED TO, PROCUREMENT OF SUBSTITUTE GOODS OR SERVICES; LOSS OF USE,
208a1b9b6aSSam Leffler  * DATA, OR PROFITS; OR BUSINESS INTERRUPTION) HOWEVER CAUSED AND ON ANY
218a1b9b6aSSam Leffler  * THEORY OF LIABILITY, WHETHER IN CONTRACT, STRICT LIABILITY, OR TORT
228a1b9b6aSSam Leffler  * (INCLUDING NEGLIGENCE OR OTHERWISE) ARISING IN ANY WAY OUT OF THE USE OF
238a1b9b6aSSam Leffler  * THIS SOFTWARE, EVEN IF ADVISED OF THE POSSIBILITY OF SUCH DAMAGE.
248a1b9b6aSSam Leffler  */
258a1b9b6aSSam Leffler 
268a1b9b6aSSam Leffler #include <sys/cdefs.h>
278a1b9b6aSSam Leffler __FBSDID("$FreeBSD$");
288a1b9b6aSSam Leffler 
298a1b9b6aSSam Leffler /*
308a1b9b6aSSam Leffler  * IEEE 802.11 MAC ACL support.
318a1b9b6aSSam Leffler  *
32b032f27cSSam Leffler  * When this module is loaded the sender address of each auth mgt
338a1b9b6aSSam Leffler  * frame is passed to the iac_check method and the module indicates
348a1b9b6aSSam Leffler  * if the frame should be accepted or rejected.  If the policy is
358a1b9b6aSSam Leffler  * set to ACL_POLICY_OPEN then all frames are accepted w/o checking
368a1b9b6aSSam Leffler  * the address.  Otherwise, the address is looked up in the database
378a1b9b6aSSam Leffler  * and if found the frame is either accepted (ACL_POLICY_ALLOW)
388a1b9b6aSSam Leffler  * or rejected (ACL_POLICY_DENT).
398a1b9b6aSSam Leffler  */
40b032f27cSSam Leffler #include "opt_wlan.h"
41b032f27cSSam Leffler 
428a1b9b6aSSam Leffler #include <sys/param.h>
438a1b9b6aSSam Leffler #include <sys/kernel.h>
448a1b9b6aSSam Leffler #include <sys/systm.h>
458a1b9b6aSSam Leffler #include <sys/mbuf.h>
468a1b9b6aSSam Leffler #include <sys/module.h>
478a1b9b6aSSam Leffler #include <sys/queue.h>
488a1b9b6aSSam Leffler 
498a1b9b6aSSam Leffler #include <sys/socket.h>
508a1b9b6aSSam Leffler 
518a1b9b6aSSam Leffler #include <net/if.h>
528a1b9b6aSSam Leffler #include <net/if_media.h>
538a1b9b6aSSam Leffler #include <net/ethernet.h>
548a1b9b6aSSam Leffler #include <net/route.h>
558a1b9b6aSSam Leffler 
568a1b9b6aSSam Leffler #include <net80211/ieee80211_var.h>
578a1b9b6aSSam Leffler 
588a1b9b6aSSam Leffler enum {
598a1b9b6aSSam Leffler 	ACL_POLICY_OPEN		= 0,	/* open, don't check ACL's */
608a1b9b6aSSam Leffler 	ACL_POLICY_ALLOW	= 1,	/* allow traffic from MAC */
618a1b9b6aSSam Leffler 	ACL_POLICY_DENY		= 2,	/* deny traffic from MAC */
62b032f27cSSam Leffler 	/*
63b032f27cSSam Leffler 	 * NB: ACL_POLICY_RADIUS must be the same value as
64b032f27cSSam Leffler 	 *     IEEE80211_MACCMD_POLICY_RADIUS because of the way
65b032f27cSSam Leffler 	 *     acl_getpolicy() works.
66b032f27cSSam Leffler 	 */
67b032f27cSSam Leffler 	ACL_POLICY_RADIUS	= 7,	/* defer to RADIUS ACL server */
688a1b9b6aSSam Leffler };
698a1b9b6aSSam Leffler 
708a1b9b6aSSam Leffler #define	ACL_HASHSIZE	32
718a1b9b6aSSam Leffler 
728a1b9b6aSSam Leffler struct acl {
738a1b9b6aSSam Leffler 	TAILQ_ENTRY(acl)	acl_list;
748a1b9b6aSSam Leffler 	LIST_ENTRY(acl)		acl_hash;
7568e8e04eSSam Leffler 	uint8_t			acl_macaddr[IEEE80211_ADDR_LEN];
768a1b9b6aSSam Leffler };
778a1b9b6aSSam Leffler struct aclstate {
788a1b9b6aSSam Leffler 	acl_lock_t		as_lock;
798a1b9b6aSSam Leffler 	int			as_policy;
80188757f5SSam Leffler 	int			as_nacls;
818a1b9b6aSSam Leffler 	TAILQ_HEAD(, acl)	as_list;	/* list of all ACL's */
828a1b9b6aSSam Leffler 	LIST_HEAD(, acl)	as_hash[ACL_HASHSIZE];
83b032f27cSSam Leffler 	struct ieee80211vap	*as_vap;
848a1b9b6aSSam Leffler };
858a1b9b6aSSam Leffler 
868a1b9b6aSSam Leffler /* simple hash is enough for variation of macaddr */
878a1b9b6aSSam Leffler #define	ACL_HASH(addr)	\
8868e8e04eSSam Leffler 	(((const uint8_t *)(addr))[IEEE80211_ADDR_LEN - 1] % ACL_HASHSIZE)
898a1b9b6aSSam Leffler 
908a1b9b6aSSam Leffler MALLOC_DEFINE(M_80211_ACL, "acl", "802.11 station acl");
918a1b9b6aSSam Leffler 
92b032f27cSSam Leffler static	int acl_free_all(struct ieee80211vap *);
93b032f27cSSam Leffler 
94b032f27cSSam Leffler /* number of references from net80211 layer */
95b032f27cSSam Leffler static	int nrefs = 0;
968a1b9b6aSSam Leffler 
978a1b9b6aSSam Leffler static int
98b032f27cSSam Leffler acl_attach(struct ieee80211vap *vap)
998a1b9b6aSSam Leffler {
1008a1b9b6aSSam Leffler 	struct aclstate *as;
1018a1b9b6aSSam Leffler 
1028a1b9b6aSSam Leffler 	MALLOC(as, struct aclstate *, sizeof(struct aclstate),
103188757f5SSam Leffler 		M_80211_ACL, M_NOWAIT | M_ZERO);
1048a1b9b6aSSam Leffler 	if (as == NULL)
1058a1b9b6aSSam Leffler 		return 0;
1068a1b9b6aSSam Leffler 	ACL_LOCK_INIT(as, "acl");
1078a1b9b6aSSam Leffler 	TAILQ_INIT(&as->as_list);
1088a1b9b6aSSam Leffler 	as->as_policy = ACL_POLICY_OPEN;
109b032f27cSSam Leffler 	as->as_vap = vap;
110b032f27cSSam Leffler 	vap->iv_as = as;
111b032f27cSSam Leffler 	nrefs++;			/* NB: we assume caller locking */
1128a1b9b6aSSam Leffler 	return 1;
1138a1b9b6aSSam Leffler }
1148a1b9b6aSSam Leffler 
1158a1b9b6aSSam Leffler static void
116b032f27cSSam Leffler acl_detach(struct ieee80211vap *vap)
1178a1b9b6aSSam Leffler {
118b032f27cSSam Leffler 	struct aclstate *as = vap->iv_as;
1198a1b9b6aSSam Leffler 
120b032f27cSSam Leffler 	KASSERT(nrefs > 0, ("imbalanced attach/detach"));
121b032f27cSSam Leffler 	nrefs--;			/* NB: we assume caller locking */
122b032f27cSSam Leffler 
123b032f27cSSam Leffler 	acl_free_all(vap);
124b032f27cSSam Leffler 	vap->iv_as = NULL;
1258a1b9b6aSSam Leffler 	ACL_LOCK_DESTROY(as);
126b032f27cSSam Leffler 	FREE(as, M_80211_ACL);
1278a1b9b6aSSam Leffler }
1288a1b9b6aSSam Leffler 
12968f5ddcdSSam Leffler static __inline struct acl *
13068e8e04eSSam Leffler _find_acl(struct aclstate *as, const uint8_t *macaddr)
1318a1b9b6aSSam Leffler {
1328a1b9b6aSSam Leffler 	struct acl *acl;
1338a1b9b6aSSam Leffler 	int hash;
1348a1b9b6aSSam Leffler 
1358a1b9b6aSSam Leffler 	hash = ACL_HASH(macaddr);
1368a1b9b6aSSam Leffler 	LIST_FOREACH(acl, &as->as_hash[hash], acl_hash) {
1378a1b9b6aSSam Leffler 		if (IEEE80211_ADDR_EQ(acl->acl_macaddr, macaddr))
1388a1b9b6aSSam Leffler 			return acl;
1398a1b9b6aSSam Leffler 	}
1408a1b9b6aSSam Leffler 	return NULL;
1418a1b9b6aSSam Leffler }
1428a1b9b6aSSam Leffler 
1438a1b9b6aSSam Leffler static void
1448a1b9b6aSSam Leffler _acl_free(struct aclstate *as, struct acl *acl)
1458a1b9b6aSSam Leffler {
1468a1b9b6aSSam Leffler 	ACL_LOCK_ASSERT(as);
1478a1b9b6aSSam Leffler 
1488a1b9b6aSSam Leffler 	TAILQ_REMOVE(&as->as_list, acl, acl_list);
1498a1b9b6aSSam Leffler 	LIST_REMOVE(acl, acl_hash);
1508a1b9b6aSSam Leffler 	FREE(acl, M_80211_ACL);
151188757f5SSam Leffler 	as->as_nacls--;
1528a1b9b6aSSam Leffler }
1538a1b9b6aSSam Leffler 
1548a1b9b6aSSam Leffler static int
155b032f27cSSam Leffler acl_check(struct ieee80211vap *vap, const uint8_t mac[IEEE80211_ADDR_LEN])
1568a1b9b6aSSam Leffler {
157b032f27cSSam Leffler 	struct aclstate *as = vap->iv_as;
1588a1b9b6aSSam Leffler 
1598a1b9b6aSSam Leffler 	switch (as->as_policy) {
1608a1b9b6aSSam Leffler 	case ACL_POLICY_OPEN:
161b032f27cSSam Leffler 	case ACL_POLICY_RADIUS:
1628a1b9b6aSSam Leffler 		return 1;
1638a1b9b6aSSam Leffler 	case ACL_POLICY_ALLOW:
1648a1b9b6aSSam Leffler 		return _find_acl(as, mac) != NULL;
1658a1b9b6aSSam Leffler 	case ACL_POLICY_DENY:
1668a1b9b6aSSam Leffler 		return _find_acl(as, mac) == NULL;
1678a1b9b6aSSam Leffler 	}
1688a1b9b6aSSam Leffler 	return 0;		/* should not happen */
1698a1b9b6aSSam Leffler }
1708a1b9b6aSSam Leffler 
1718a1b9b6aSSam Leffler static int
172b032f27cSSam Leffler acl_add(struct ieee80211vap *vap, const uint8_t mac[IEEE80211_ADDR_LEN])
1738a1b9b6aSSam Leffler {
174b032f27cSSam Leffler 	struct aclstate *as = vap->iv_as;
1758a1b9b6aSSam Leffler 	struct acl *acl, *new;
1768a1b9b6aSSam Leffler 	int hash;
1778a1b9b6aSSam Leffler 
1788a1b9b6aSSam Leffler 	MALLOC(new, struct acl *, sizeof(struct acl), M_80211_ACL, M_NOWAIT | M_ZERO);
1798a1b9b6aSSam Leffler 	if (new == NULL) {
180b032f27cSSam Leffler 		IEEE80211_DPRINTF(vap, IEEE80211_MSG_ACL,
1818a1b9b6aSSam Leffler 			"ACL: add %s failed, no memory\n", ether_sprintf(mac));
1828a1b9b6aSSam Leffler 		/* XXX statistic */
1838a1b9b6aSSam Leffler 		return ENOMEM;
1848a1b9b6aSSam Leffler 	}
1858a1b9b6aSSam Leffler 
1868a1b9b6aSSam Leffler 	ACL_LOCK(as);
1878a1b9b6aSSam Leffler 	hash = ACL_HASH(mac);
1888a1b9b6aSSam Leffler 	LIST_FOREACH(acl, &as->as_hash[hash], acl_hash) {
1898a1b9b6aSSam Leffler 		if (IEEE80211_ADDR_EQ(acl->acl_macaddr, mac)) {
1908a1b9b6aSSam Leffler 			ACL_UNLOCK(as);
1918a1b9b6aSSam Leffler 			FREE(new, M_80211_ACL);
192b032f27cSSam Leffler 			IEEE80211_DPRINTF(vap, IEEE80211_MSG_ACL,
1938a1b9b6aSSam Leffler 				"ACL: add %s failed, already present\n",
1948a1b9b6aSSam Leffler 				ether_sprintf(mac));
1958a1b9b6aSSam Leffler 			return EEXIST;
1968a1b9b6aSSam Leffler 		}
1978a1b9b6aSSam Leffler 	}
1988a1b9b6aSSam Leffler 	IEEE80211_ADDR_COPY(new->acl_macaddr, mac);
1998a1b9b6aSSam Leffler 	TAILQ_INSERT_TAIL(&as->as_list, new, acl_list);
2008a1b9b6aSSam Leffler 	LIST_INSERT_HEAD(&as->as_hash[hash], new, acl_hash);
201188757f5SSam Leffler 	as->as_nacls++;
2028a1b9b6aSSam Leffler 	ACL_UNLOCK(as);
2038a1b9b6aSSam Leffler 
204b032f27cSSam Leffler 	IEEE80211_DPRINTF(vap, IEEE80211_MSG_ACL,
2058a1b9b6aSSam Leffler 		"ACL: add %s\n", ether_sprintf(mac));
2068a1b9b6aSSam Leffler 	return 0;
2078a1b9b6aSSam Leffler }
2088a1b9b6aSSam Leffler 
2098a1b9b6aSSam Leffler static int
210b032f27cSSam Leffler acl_remove(struct ieee80211vap *vap, const uint8_t mac[IEEE80211_ADDR_LEN])
2118a1b9b6aSSam Leffler {
212b032f27cSSam Leffler 	struct aclstate *as = vap->iv_as;
2138a1b9b6aSSam Leffler 	struct acl *acl;
2148a1b9b6aSSam Leffler 
2158a1b9b6aSSam Leffler 	ACL_LOCK(as);
2168a1b9b6aSSam Leffler 	acl = _find_acl(as, mac);
2178a1b9b6aSSam Leffler 	if (acl != NULL)
2188a1b9b6aSSam Leffler 		_acl_free(as, acl);
2198a1b9b6aSSam Leffler 	ACL_UNLOCK(as);
2208a1b9b6aSSam Leffler 
221b032f27cSSam Leffler 	IEEE80211_DPRINTF(vap, IEEE80211_MSG_ACL,
2228a1b9b6aSSam Leffler 		"ACL: remove %s%s\n", ether_sprintf(mac),
2238a1b9b6aSSam Leffler 		acl == NULL ? ", not present" : "");
2248a1b9b6aSSam Leffler 
2258a1b9b6aSSam Leffler 	return (acl == NULL ? ENOENT : 0);
2268a1b9b6aSSam Leffler }
2278a1b9b6aSSam Leffler 
2288a1b9b6aSSam Leffler static int
229b032f27cSSam Leffler acl_free_all(struct ieee80211vap *vap)
2308a1b9b6aSSam Leffler {
231b032f27cSSam Leffler 	struct aclstate *as = vap->iv_as;
2328a1b9b6aSSam Leffler 	struct acl *acl;
2338a1b9b6aSSam Leffler 
234b032f27cSSam Leffler 	IEEE80211_DPRINTF(vap, IEEE80211_MSG_ACL, "ACL: %s\n", "free all");
2358a1b9b6aSSam Leffler 
2368a1b9b6aSSam Leffler 	ACL_LOCK(as);
2378a1b9b6aSSam Leffler 	while ((acl = TAILQ_FIRST(&as->as_list)) != NULL)
2388a1b9b6aSSam Leffler 		_acl_free(as, acl);
2398a1b9b6aSSam Leffler 	ACL_UNLOCK(as);
2408a1b9b6aSSam Leffler 
2418a1b9b6aSSam Leffler 	return 0;
2428a1b9b6aSSam Leffler }
2438a1b9b6aSSam Leffler 
2448a1b9b6aSSam Leffler static int
245b032f27cSSam Leffler acl_setpolicy(struct ieee80211vap *vap, int policy)
2468a1b9b6aSSam Leffler {
247b032f27cSSam Leffler 	struct aclstate *as = vap->iv_as;
2488a1b9b6aSSam Leffler 
249b032f27cSSam Leffler 	IEEE80211_DPRINTF(vap, IEEE80211_MSG_ACL,
2508a1b9b6aSSam Leffler 		"ACL: set policy to %u\n", policy);
2518a1b9b6aSSam Leffler 
2528a1b9b6aSSam Leffler 	switch (policy) {
2538a1b9b6aSSam Leffler 	case IEEE80211_MACCMD_POLICY_OPEN:
2548a1b9b6aSSam Leffler 		as->as_policy = ACL_POLICY_OPEN;
2558a1b9b6aSSam Leffler 		break;
2568a1b9b6aSSam Leffler 	case IEEE80211_MACCMD_POLICY_ALLOW:
2578a1b9b6aSSam Leffler 		as->as_policy = ACL_POLICY_ALLOW;
2588a1b9b6aSSam Leffler 		break;
2598a1b9b6aSSam Leffler 	case IEEE80211_MACCMD_POLICY_DENY:
2608a1b9b6aSSam Leffler 		as->as_policy = ACL_POLICY_DENY;
2618a1b9b6aSSam Leffler 		break;
262b032f27cSSam Leffler 	case IEEE80211_MACCMD_POLICY_RADIUS:
263b032f27cSSam Leffler 		as->as_policy = ACL_POLICY_RADIUS;
264b032f27cSSam Leffler 		break;
2658a1b9b6aSSam Leffler 	default:
2668a1b9b6aSSam Leffler 		return EINVAL;
2678a1b9b6aSSam Leffler 	}
2688a1b9b6aSSam Leffler 	return 0;
2698a1b9b6aSSam Leffler }
2708a1b9b6aSSam Leffler 
2718a1b9b6aSSam Leffler static int
272b032f27cSSam Leffler acl_getpolicy(struct ieee80211vap *vap)
2738a1b9b6aSSam Leffler {
274b032f27cSSam Leffler 	struct aclstate *as = vap->iv_as;
2758a1b9b6aSSam Leffler 
2768a1b9b6aSSam Leffler 	return as->as_policy;
2778a1b9b6aSSam Leffler }
2788a1b9b6aSSam Leffler 
279188757f5SSam Leffler static int
280b032f27cSSam Leffler acl_setioctl(struct ieee80211vap *vap, struct ieee80211req *ireq)
281188757f5SSam Leffler {
282188757f5SSam Leffler 
283188757f5SSam Leffler 	return EINVAL;
284188757f5SSam Leffler }
285188757f5SSam Leffler 
286188757f5SSam Leffler static int
287b032f27cSSam Leffler acl_getioctl(struct ieee80211vap *vap, struct ieee80211req *ireq)
288188757f5SSam Leffler {
289b032f27cSSam Leffler 	struct aclstate *as = vap->iv_as;
290188757f5SSam Leffler 	struct acl *acl;
291188757f5SSam Leffler 	struct ieee80211req_maclist *ap;
292188757f5SSam Leffler 	int error, space, i;
293188757f5SSam Leffler 
294188757f5SSam Leffler 	switch (ireq->i_val) {
295188757f5SSam Leffler 	case IEEE80211_MACCMD_POLICY:
296188757f5SSam Leffler 		ireq->i_val = as->as_policy;
297188757f5SSam Leffler 		return 0;
298188757f5SSam Leffler 	case IEEE80211_MACCMD_LIST:
299188757f5SSam Leffler 		space = as->as_nacls * IEEE80211_ADDR_LEN;
300188757f5SSam Leffler 		if (ireq->i_len == 0) {
301188757f5SSam Leffler 			ireq->i_len = space;	/* return required space */
302188757f5SSam Leffler 			return 0;		/* NB: must not error */
303188757f5SSam Leffler 		}
304188757f5SSam Leffler 		MALLOC(ap, struct ieee80211req_maclist *, space,
305188757f5SSam Leffler 		    M_TEMP, M_NOWAIT);
306188757f5SSam Leffler 		if (ap == NULL)
307188757f5SSam Leffler 			return ENOMEM;
308188757f5SSam Leffler 		i = 0;
309188757f5SSam Leffler 		ACL_LOCK(as);
310188757f5SSam Leffler 		TAILQ_FOREACH(acl, &as->as_list, acl_list) {
311188757f5SSam Leffler 			IEEE80211_ADDR_COPY(ap[i].ml_macaddr, acl->acl_macaddr);
312188757f5SSam Leffler 			i++;
313188757f5SSam Leffler 		}
314188757f5SSam Leffler 		ACL_UNLOCK(as);
315188757f5SSam Leffler 		if (ireq->i_len >= space) {
316188757f5SSam Leffler 			error = copyout(ap, ireq->i_data, space);
317188757f5SSam Leffler 			ireq->i_len = space;
318188757f5SSam Leffler 		} else
319188757f5SSam Leffler 			error = copyout(ap, ireq->i_data, ireq->i_len);
320188757f5SSam Leffler 		FREE(ap, M_TEMP);
321188757f5SSam Leffler 		return error;
322188757f5SSam Leffler 	}
323188757f5SSam Leffler 	return EINVAL;
324188757f5SSam Leffler }
325188757f5SSam Leffler 
3268a1b9b6aSSam Leffler static const struct ieee80211_aclator mac = {
3278a1b9b6aSSam Leffler 	.iac_name	= "mac",
3288a1b9b6aSSam Leffler 	.iac_attach	= acl_attach,
3298a1b9b6aSSam Leffler 	.iac_detach	= acl_detach,
3308a1b9b6aSSam Leffler 	.iac_check	= acl_check,
3318a1b9b6aSSam Leffler 	.iac_add	= acl_add,
3328a1b9b6aSSam Leffler 	.iac_remove	= acl_remove,
3338a1b9b6aSSam Leffler 	.iac_flush	= acl_free_all,
3348a1b9b6aSSam Leffler 	.iac_setpolicy	= acl_setpolicy,
3358a1b9b6aSSam Leffler 	.iac_getpolicy	= acl_getpolicy,
336188757f5SSam Leffler 	.iac_setioctl	= acl_setioctl,
337188757f5SSam Leffler 	.iac_getioctl	= acl_getioctl,
3388a1b9b6aSSam Leffler };
339b032f27cSSam Leffler IEEE80211_ACL_MODULE(wlan_acl, mac, 1);
340