132176cfdSRui Paulo /*-
2f186073cSJoerg Sonnenberger * Copyright (c) 2001 Atsushi Onoe
332176cfdSRui Paulo * Copyright (c) 2002-2009 Sam Leffler, Errno Consulting
4f186073cSJoerg Sonnenberger * All rights reserved.
5f186073cSJoerg Sonnenberger *
6f186073cSJoerg Sonnenberger * Redistribution and use in source and binary forms, with or without
7f186073cSJoerg Sonnenberger * modification, are permitted provided that the following conditions
8f186073cSJoerg Sonnenberger * are met:
9f186073cSJoerg Sonnenberger * 1. Redistributions of source code must retain the above copyright
10f186073cSJoerg Sonnenberger * notice, this list of conditions and the following disclaimer.
11f186073cSJoerg Sonnenberger * 2. Redistributions in binary form must reproduce the above copyright
12f186073cSJoerg Sonnenberger * notice, this list of conditions and the following disclaimer in the
13f186073cSJoerg Sonnenberger * documentation and/or other materials provided with the distribution.
14f186073cSJoerg Sonnenberger *
15f186073cSJoerg Sonnenberger * THIS SOFTWARE IS PROVIDED BY THE AUTHOR ``AS IS'' AND ANY EXPRESS OR
16f186073cSJoerg Sonnenberger * IMPLIED WARRANTIES, INCLUDING, BUT NOT LIMITED TO, THE IMPLIED WARRANTIES
17f186073cSJoerg Sonnenberger * OF MERCHANTABILITY AND FITNESS FOR A PARTICULAR PURPOSE ARE DISCLAIMED.
18f186073cSJoerg Sonnenberger * IN NO EVENT SHALL THE AUTHOR BE LIABLE FOR ANY DIRECT, INDIRECT,
19f186073cSJoerg Sonnenberger * INCIDENTAL, SPECIAL, EXEMPLARY, OR CONSEQUENTIAL DAMAGES (INCLUDING, BUT
20f186073cSJoerg Sonnenberger * NOT LIMITED TO, PROCUREMENT OF SUBSTITUTE GOODS OR SERVICES; LOSS OF USE,
21f186073cSJoerg Sonnenberger * DATA, OR PROFITS; OR BUSINESS INTERRUPTION) HOWEVER CAUSED AND ON ANY
22f186073cSJoerg Sonnenberger * THEORY OF LIABILITY, WHETHER IN CONTRACT, STRICT LIABILITY, OR TORT
23f186073cSJoerg Sonnenberger * (INCLUDING NEGLIGENCE OR OTHERWISE) ARISING IN ANY WAY OUT OF THE USE OF
24f186073cSJoerg Sonnenberger * THIS SOFTWARE, EVEN IF ADVISED OF THE POSSIBILITY OF SUCH DAMAGE.
25f186073cSJoerg Sonnenberger */
26f186073cSJoerg Sonnenberger
27085ff963SMatthew Dillon #include <sys/cdefs.h>
28085ff963SMatthew Dillon __FBSDID("$FreeBSD$");
29085ff963SMatthew Dillon
30f186073cSJoerg Sonnenberger /*
31085ff963SMatthew Dillon * IEEE 802.11 ioctl support (FreeBSD-specific)
32f186073cSJoerg Sonnenberger */
33f186073cSJoerg Sonnenberger
34f186073cSJoerg Sonnenberger #include "opt_inet.h"
3532176cfdSRui Paulo #include "opt_wlan.h"
36f186073cSJoerg Sonnenberger
37f186073cSJoerg Sonnenberger #include <sys/endian.h>
38f186073cSJoerg Sonnenberger #include <sys/param.h>
39f186073cSJoerg Sonnenberger #include <sys/kernel.h>
404f655ef5SMatthew Dillon #include <sys/malloc.h>
41*2b3f93eaSMatthew Dillon #include <sys/caps.h>
42f186073cSJoerg Sonnenberger #include <sys/socket.h>
43f186073cSJoerg Sonnenberger #include <sys/sockio.h>
44f186073cSJoerg Sonnenberger #include <sys/systm.h>
45f186073cSJoerg Sonnenberger
46f186073cSJoerg Sonnenberger #include <net/if.h>
4722603758SRui Paulo #include <net/if_var.h>
48841ab66cSSepherosa Ziehau #include <net/if_dl.h>
49f186073cSJoerg Sonnenberger #include <net/if_media.h>
50f186073cSJoerg Sonnenberger #include <net/ethernet.h>
51f186073cSJoerg Sonnenberger
52f186073cSJoerg Sonnenberger #ifdef INET
53f186073cSJoerg Sonnenberger #include <netinet/in.h>
54f186073cSJoerg Sonnenberger #include <netinet/if_ether.h>
55f186073cSJoerg Sonnenberger #endif
56f186073cSJoerg Sonnenberger
57f186073cSJoerg Sonnenberger #include <netproto/802_11/ieee80211_var.h>
58f186073cSJoerg Sonnenberger #include <netproto/802_11/ieee80211_ioctl.h>
5932176cfdSRui Paulo #include <netproto/802_11/ieee80211_regdomain.h>
6032176cfdSRui Paulo #include <netproto/802_11/ieee80211_input.h>
61f186073cSJoerg Sonnenberger
6232176cfdSRui Paulo #define IS_UP_AUTO(_vap) \
6332176cfdSRui Paulo (IFNET_IS_UP_RUNNING((_vap)->iv_ifp) && \
6432176cfdSRui Paulo (_vap)->iv_roaming == IEEE80211_ROAMING_AUTO)
65f186073cSJoerg Sonnenberger
6632176cfdSRui Paulo static const uint8_t zerobssid[IEEE80211_ADDR_LEN];
6732176cfdSRui Paulo static struct ieee80211_channel *findchannel(struct ieee80211com *,
6832176cfdSRui Paulo int ieee, int mode);
69085ff963SMatthew Dillon static int ieee80211_scanreq(struct ieee80211vap *,
70085ff963SMatthew Dillon struct ieee80211_scan_req *);
71841ab66cSSepherosa Ziehau
724f655ef5SMatthew Dillon static int
ieee80211_ioctl_getkey(struct ieee80211vap * vap,struct ieee80211req * ireq)7332176cfdSRui Paulo ieee80211_ioctl_getkey(struct ieee80211vap *vap, struct ieee80211req *ireq)
74f186073cSJoerg Sonnenberger {
7532176cfdSRui Paulo struct ieee80211com *ic = vap->iv_ic;
76841ab66cSSepherosa Ziehau struct ieee80211_node *ni;
77841ab66cSSepherosa Ziehau struct ieee80211req_key ik;
78841ab66cSSepherosa Ziehau struct ieee80211_key *wk;
79841ab66cSSepherosa Ziehau const struct ieee80211_cipher *cip;
80841ab66cSSepherosa Ziehau u_int kid;
81841ab66cSSepherosa Ziehau int error;
82841ab66cSSepherosa Ziehau
83841ab66cSSepherosa Ziehau if (ireq->i_len != sizeof(ik))
84841ab66cSSepherosa Ziehau return EINVAL;
85841ab66cSSepherosa Ziehau error = copyin(ireq->i_data, &ik, sizeof(ik));
86841ab66cSSepherosa Ziehau if (error)
87841ab66cSSepherosa Ziehau return error;
88841ab66cSSepherosa Ziehau kid = ik.ik_keyix;
89841ab66cSSepherosa Ziehau if (kid == IEEE80211_KEYIX_NONE) {
9032176cfdSRui Paulo ni = ieee80211_find_vap_node(&ic->ic_sta, vap, ik.ik_macaddr);
91841ab66cSSepherosa Ziehau if (ni == NULL)
9232176cfdSRui Paulo return ENOENT;
93841ab66cSSepherosa Ziehau wk = &ni->ni_ucastkey;
94841ab66cSSepherosa Ziehau } else {
95841ab66cSSepherosa Ziehau if (kid >= IEEE80211_WEP_NKID)
96841ab66cSSepherosa Ziehau return EINVAL;
9732176cfdSRui Paulo wk = &vap->iv_nw_keys[kid];
9832176cfdSRui Paulo IEEE80211_ADDR_COPY(&ik.ik_macaddr, vap->iv_bss->ni_macaddr);
99841ab66cSSepherosa Ziehau ni = NULL;
100841ab66cSSepherosa Ziehau }
101841ab66cSSepherosa Ziehau cip = wk->wk_cipher;
102841ab66cSSepherosa Ziehau ik.ik_type = cip->ic_cipher;
103841ab66cSSepherosa Ziehau ik.ik_keylen = wk->wk_keylen;
104841ab66cSSepherosa Ziehau ik.ik_flags = wk->wk_flags & (IEEE80211_KEY_XMIT | IEEE80211_KEY_RECV);
10532176cfdSRui Paulo if (wk->wk_keyix == vap->iv_def_txkey)
106841ab66cSSepherosa Ziehau ik.ik_flags |= IEEE80211_KEY_DEFAULT;
107*2b3f93eaSMatthew Dillon if (caps_priv_check_self(SYSCAP_NONET_WIFI) == 0) {
108841ab66cSSepherosa Ziehau /* NB: only root can read key data */
10932176cfdSRui Paulo ik.ik_keyrsc = wk->wk_keyrsc[IEEE80211_NONQOS_TID];
110841ab66cSSepherosa Ziehau ik.ik_keytsc = wk->wk_keytsc;
111841ab66cSSepherosa Ziehau memcpy(ik.ik_keydata, wk->wk_key, wk->wk_keylen);
112841ab66cSSepherosa Ziehau if (cip->ic_cipher == IEEE80211_CIPHER_TKIP) {
113841ab66cSSepherosa Ziehau memcpy(ik.ik_keydata+wk->wk_keylen,
114841ab66cSSepherosa Ziehau wk->wk_key + IEEE80211_KEYBUF_SIZE,
115841ab66cSSepherosa Ziehau IEEE80211_MICBUF_SIZE);
116841ab66cSSepherosa Ziehau ik.ik_keylen += IEEE80211_MICBUF_SIZE;
117841ab66cSSepherosa Ziehau }
118841ab66cSSepherosa Ziehau } else {
119841ab66cSSepherosa Ziehau ik.ik_keyrsc = 0;
120841ab66cSSepherosa Ziehau ik.ik_keytsc = 0;
121841ab66cSSepherosa Ziehau memset(ik.ik_keydata, 0, sizeof(ik.ik_keydata));
122841ab66cSSepherosa Ziehau }
123841ab66cSSepherosa Ziehau if (ni != NULL)
124841ab66cSSepherosa Ziehau ieee80211_free_node(ni);
125841ab66cSSepherosa Ziehau return copyout(&ik, ireq->i_data, sizeof(ik));
126841ab66cSSepherosa Ziehau }
127841ab66cSSepherosa Ziehau
1284f655ef5SMatthew Dillon static int
ieee80211_ioctl_getchanlist(struct ieee80211vap * vap,struct ieee80211req * ireq)12932176cfdSRui Paulo ieee80211_ioctl_getchanlist(struct ieee80211vap *vap, struct ieee80211req *ireq)
130841ab66cSSepherosa Ziehau {
13132176cfdSRui Paulo struct ieee80211com *ic = vap->iv_ic;
132841ab66cSSepherosa Ziehau
133841ab66cSSepherosa Ziehau if (sizeof(ic->ic_chan_active) < ireq->i_len)
134841ab66cSSepherosa Ziehau ireq->i_len = sizeof(ic->ic_chan_active);
135841ab66cSSepherosa Ziehau return copyout(&ic->ic_chan_active, ireq->i_data, ireq->i_len);
136841ab66cSSepherosa Ziehau }
137841ab66cSSepherosa Ziehau
1384f655ef5SMatthew Dillon static int
ieee80211_ioctl_getchaninfo(struct ieee80211vap * vap,struct ieee80211req * ireq)13932176cfdSRui Paulo ieee80211_ioctl_getchaninfo(struct ieee80211vap *vap, struct ieee80211req *ireq)
140841ab66cSSepherosa Ziehau {
14132176cfdSRui Paulo struct ieee80211com *ic = vap->iv_ic;
142085ff963SMatthew Dillon uint32_t space;
143841ab66cSSepherosa Ziehau
144841ab66cSSepherosa Ziehau space = __offsetof(struct ieee80211req_chaninfo,
14532176cfdSRui Paulo ic_chans[ic->ic_nchans]);
146841ab66cSSepherosa Ziehau if (space > ireq->i_len)
147841ab66cSSepherosa Ziehau space = ireq->i_len;
14832176cfdSRui Paulo /* XXX assumes compatible layout */
14932176cfdSRui Paulo return copyout(&ic->ic_nchans, ireq->i_data, space);
150841ab66cSSepherosa Ziehau }
151841ab66cSSepherosa Ziehau
1524f655ef5SMatthew Dillon static int
ieee80211_ioctl_getwpaie(struct ieee80211vap * vap,struct ieee80211req * ireq,int req)15332176cfdSRui Paulo ieee80211_ioctl_getwpaie(struct ieee80211vap *vap,
15432176cfdSRui Paulo struct ieee80211req *ireq, int req)
155841ab66cSSepherosa Ziehau {
156841ab66cSSepherosa Ziehau struct ieee80211_node *ni;
1574f655ef5SMatthew Dillon struct ieee80211req_wpaie2 *wpaie;
158841ab66cSSepherosa Ziehau int error;
159841ab66cSSepherosa Ziehau
160841ab66cSSepherosa Ziehau if (ireq->i_len < IEEE80211_ADDR_LEN)
161841ab66cSSepherosa Ziehau return EINVAL;
1624f655ef5SMatthew Dillon #if defined(__DragonFly__)
1634f655ef5SMatthew Dillon wpaie = kmalloc(sizeof(*wpaie), M_TEMP, M_INTWAIT | M_ZERO);
1644f655ef5SMatthew Dillon #else
1654f655ef5SMatthew Dillon wpaie = IEEE80211_MALLOC(sizeof(*wpaie), M_TEMP,
1664f655ef5SMatthew Dillon IEEE80211_M_NOWAIT | IEEE80211_M_ZERO);
1674f655ef5SMatthew Dillon #endif
1684f655ef5SMatthew Dillon if (wpaie == NULL)
1694f655ef5SMatthew Dillon return ENOMEM;
1704f655ef5SMatthew Dillon error = copyin(ireq->i_data, wpaie->wpa_macaddr, IEEE80211_ADDR_LEN);
171841ab66cSSepherosa Ziehau if (error != 0)
1724f655ef5SMatthew Dillon goto bad;
1734f655ef5SMatthew Dillon ni = ieee80211_find_vap_node(&vap->iv_ic->ic_sta, vap, wpaie->wpa_macaddr);
1744f655ef5SMatthew Dillon if (ni == NULL) {
1754f655ef5SMatthew Dillon error = ENOENT;
1764f655ef5SMatthew Dillon goto bad;
1774f655ef5SMatthew Dillon }
17832176cfdSRui Paulo if (ni->ni_ies.wpa_ie != NULL) {
17932176cfdSRui Paulo int ielen = ni->ni_ies.wpa_ie[1] + 2;
1804f655ef5SMatthew Dillon if (ielen > sizeof(wpaie->wpa_ie))
1814f655ef5SMatthew Dillon ielen = sizeof(wpaie->wpa_ie);
1824f655ef5SMatthew Dillon memcpy(wpaie->wpa_ie, ni->ni_ies.wpa_ie, ielen);
18332176cfdSRui Paulo }
18432176cfdSRui Paulo if (req == IEEE80211_IOC_WPAIE2) {
18532176cfdSRui Paulo if (ni->ni_ies.rsn_ie != NULL) {
18632176cfdSRui Paulo int ielen = ni->ni_ies.rsn_ie[1] + 2;
1874f655ef5SMatthew Dillon if (ielen > sizeof(wpaie->rsn_ie))
1884f655ef5SMatthew Dillon ielen = sizeof(wpaie->rsn_ie);
1894f655ef5SMatthew Dillon memcpy(wpaie->rsn_ie, ni->ni_ies.rsn_ie, ielen);
19032176cfdSRui Paulo }
19132176cfdSRui Paulo if (ireq->i_len > sizeof(struct ieee80211req_wpaie2))
19232176cfdSRui Paulo ireq->i_len = sizeof(struct ieee80211req_wpaie2);
19332176cfdSRui Paulo } else {
19432176cfdSRui Paulo /* compatibility op, may overwrite wpa ie */
19532176cfdSRui Paulo /* XXX check ic_flags? */
19632176cfdSRui Paulo if (ni->ni_ies.rsn_ie != NULL) {
19732176cfdSRui Paulo int ielen = ni->ni_ies.rsn_ie[1] + 2;
1984f655ef5SMatthew Dillon if (ielen > sizeof(wpaie->wpa_ie))
1994f655ef5SMatthew Dillon ielen = sizeof(wpaie->wpa_ie);
2004f655ef5SMatthew Dillon memcpy(wpaie->wpa_ie, ni->ni_ies.rsn_ie, ielen);
20132176cfdSRui Paulo }
20232176cfdSRui Paulo if (ireq->i_len > sizeof(struct ieee80211req_wpaie))
20332176cfdSRui Paulo ireq->i_len = sizeof(struct ieee80211req_wpaie);
204841ab66cSSepherosa Ziehau }
205841ab66cSSepherosa Ziehau ieee80211_free_node(ni);
2064f655ef5SMatthew Dillon error = copyout(wpaie, ireq->i_data, ireq->i_len);
2074f655ef5SMatthew Dillon bad:
2084f655ef5SMatthew Dillon IEEE80211_FREE(wpaie, M_TEMP);
2094f655ef5SMatthew Dillon return error;
210841ab66cSSepherosa Ziehau }
211841ab66cSSepherosa Ziehau
2124f655ef5SMatthew Dillon static int
ieee80211_ioctl_getstastats(struct ieee80211vap * vap,struct ieee80211req * ireq)21332176cfdSRui Paulo ieee80211_ioctl_getstastats(struct ieee80211vap *vap, struct ieee80211req *ireq)
214841ab66cSSepherosa Ziehau {
215841ab66cSSepherosa Ziehau struct ieee80211_node *ni;
216841ab66cSSepherosa Ziehau uint8_t macaddr[IEEE80211_ADDR_LEN];
217085ff963SMatthew Dillon const size_t off = __offsetof(struct ieee80211req_sta_stats, is_stats);
218841ab66cSSepherosa Ziehau int error;
219841ab66cSSepherosa Ziehau
220841ab66cSSepherosa Ziehau if (ireq->i_len < off)
221841ab66cSSepherosa Ziehau return EINVAL;
222841ab66cSSepherosa Ziehau error = copyin(ireq->i_data, macaddr, IEEE80211_ADDR_LEN);
223841ab66cSSepherosa Ziehau if (error != 0)
224841ab66cSSepherosa Ziehau return error;
22532176cfdSRui Paulo ni = ieee80211_find_vap_node(&vap->iv_ic->ic_sta, vap, macaddr);
22632176cfdSRui Paulo if (ni == NULL)
2270d17a301SSepherosa Ziehau return ENOENT;
228841ab66cSSepherosa Ziehau if (ireq->i_len > sizeof(struct ieee80211req_sta_stats))
229841ab66cSSepherosa Ziehau ireq->i_len = sizeof(struct ieee80211req_sta_stats);
230841ab66cSSepherosa Ziehau /* NB: copy out only the statistics */
231841ab66cSSepherosa Ziehau error = copyout(&ni->ni_stats, (uint8_t *) ireq->i_data + off,
232841ab66cSSepherosa Ziehau ireq->i_len - off);
233841ab66cSSepherosa Ziehau ieee80211_free_node(ni);
234841ab66cSSepherosa Ziehau return error;
235841ab66cSSepherosa Ziehau }
236841ab66cSSepherosa Ziehau
23732176cfdSRui Paulo struct scanreq {
23832176cfdSRui Paulo struct ieee80211req_scan_result *sr;
23932176cfdSRui Paulo size_t space;
24032176cfdSRui Paulo };
24132176cfdSRui Paulo
24232176cfdSRui Paulo static size_t
scan_space(const struct ieee80211_scan_entry * se,int * ielen)24332176cfdSRui Paulo scan_space(const struct ieee80211_scan_entry *se, int *ielen)
244841ab66cSSepherosa Ziehau {
24532176cfdSRui Paulo size_t len;
246841ab66cSSepherosa Ziehau
24732176cfdSRui Paulo *ielen = se->se_ies.len;
248841ab66cSSepherosa Ziehau /*
24932176cfdSRui Paulo * NB: ie's can be no more than 255 bytes and the max 802.11
25032176cfdSRui Paulo * packet is <3Kbytes so we are sure this doesn't overflow
25132176cfdSRui Paulo * 16-bits; if this is a concern we can drop the ie's.
252841ab66cSSepherosa Ziehau */
25332176cfdSRui Paulo len = sizeof(struct ieee80211req_scan_result) + se->se_ssid[1] +
25432176cfdSRui Paulo se->se_meshid[1] + *ielen;
25532176cfdSRui Paulo return roundup(len, sizeof(uint32_t));
256841ab66cSSepherosa Ziehau }
257841ab66cSSepherosa Ziehau
25832176cfdSRui Paulo static void
get_scan_space(void * arg,const struct ieee80211_scan_entry * se)25932176cfdSRui Paulo get_scan_space(void *arg, const struct ieee80211_scan_entry *se)
260841ab66cSSepherosa Ziehau {
26132176cfdSRui Paulo struct scanreq *req = arg;
26232176cfdSRui Paulo int ielen;
263841ab66cSSepherosa Ziehau
26432176cfdSRui Paulo req->space += scan_space(se, &ielen);
26532176cfdSRui Paulo }
26632176cfdSRui Paulo
2674f655ef5SMatthew Dillon static void
get_scan_result(void * arg,const struct ieee80211_scan_entry * se)26832176cfdSRui Paulo get_scan_result(void *arg, const struct ieee80211_scan_entry *se)
26932176cfdSRui Paulo {
27032176cfdSRui Paulo struct scanreq *req = arg;
27132176cfdSRui Paulo struct ieee80211req_scan_result *sr;
27232176cfdSRui Paulo int ielen, len, nr, nxr;
27332176cfdSRui Paulo uint8_t *cp;
27432176cfdSRui Paulo
27532176cfdSRui Paulo len = scan_space(se, &ielen);
27632176cfdSRui Paulo if (len > req->space)
27732176cfdSRui Paulo return;
27832176cfdSRui Paulo
27932176cfdSRui Paulo sr = req->sr;
28032176cfdSRui Paulo KASSERT(len <= 65535 && ielen <= 65535,
28132176cfdSRui Paulo ("len %u ssid %u ie %u", len, se->se_ssid[1], ielen));
28232176cfdSRui Paulo sr->isr_len = len;
28332176cfdSRui Paulo sr->isr_ie_off = sizeof(struct ieee80211req_scan_result);
28432176cfdSRui Paulo sr->isr_ie_len = ielen;
28532176cfdSRui Paulo sr->isr_freq = se->se_chan->ic_freq;
28632176cfdSRui Paulo sr->isr_flags = se->se_chan->ic_flags;
28732176cfdSRui Paulo sr->isr_rssi = se->se_rssi;
28832176cfdSRui Paulo sr->isr_noise = se->se_noise;
28932176cfdSRui Paulo sr->isr_intval = se->se_intval;
29032176cfdSRui Paulo sr->isr_capinfo = se->se_capinfo;
29132176cfdSRui Paulo sr->isr_erp = se->se_erp;
29232176cfdSRui Paulo IEEE80211_ADDR_COPY(sr->isr_bssid, se->se_bssid);
29332176cfdSRui Paulo nr = min(se->se_rates[1], IEEE80211_RATE_MAXSIZE);
29432176cfdSRui Paulo memcpy(sr->isr_rates, se->se_rates+2, nr);
29532176cfdSRui Paulo nxr = min(se->se_xrates[1], IEEE80211_RATE_MAXSIZE - nr);
29632176cfdSRui Paulo memcpy(sr->isr_rates+nr, se->se_xrates+2, nxr);
29732176cfdSRui Paulo sr->isr_nrates = nr + nxr;
29832176cfdSRui Paulo
29932176cfdSRui Paulo /* copy SSID */
30032176cfdSRui Paulo sr->isr_ssid_len = se->se_ssid[1];
30132176cfdSRui Paulo cp = ((uint8_t *)sr) + sr->isr_ie_off;
30232176cfdSRui Paulo memcpy(cp, se->se_ssid+2, sr->isr_ssid_len);
30332176cfdSRui Paulo
30432176cfdSRui Paulo /* copy mesh id */
30532176cfdSRui Paulo cp += sr->isr_ssid_len;
30632176cfdSRui Paulo sr->isr_meshid_len = se->se_meshid[1];
30732176cfdSRui Paulo memcpy(cp, se->se_meshid+2, sr->isr_meshid_len);
30832176cfdSRui Paulo cp += sr->isr_meshid_len;
30932176cfdSRui Paulo
31032176cfdSRui Paulo if (ielen)
31132176cfdSRui Paulo memcpy(cp, se->se_ies.data, ielen);
31232176cfdSRui Paulo
31332176cfdSRui Paulo req->space -= len;
31432176cfdSRui Paulo req->sr = (struct ieee80211req_scan_result *)(((uint8_t *)sr) + len);
31532176cfdSRui Paulo }
31632176cfdSRui Paulo
3174f655ef5SMatthew Dillon static int
ieee80211_ioctl_getscanresults(struct ieee80211vap * vap,struct ieee80211req * ireq)31832176cfdSRui Paulo ieee80211_ioctl_getscanresults(struct ieee80211vap *vap,
31932176cfdSRui Paulo struct ieee80211req *ireq)
32032176cfdSRui Paulo {
32132176cfdSRui Paulo struct scanreq req;
32232176cfdSRui Paulo int error;
32332176cfdSRui Paulo
32432176cfdSRui Paulo if (ireq->i_len < sizeof(struct scanreq))
32532176cfdSRui Paulo return EFAULT;
32632176cfdSRui Paulo
327841ab66cSSepherosa Ziehau error = 0;
32832176cfdSRui Paulo req.space = 0;
32932176cfdSRui Paulo ieee80211_scan_iterate(vap, get_scan_space, &req);
33032176cfdSRui Paulo if (req.space > ireq->i_len)
33132176cfdSRui Paulo req.space = ireq->i_len;
33232176cfdSRui Paulo if (req.space > 0) {
333085ff963SMatthew Dillon uint32_t space;
33432176cfdSRui Paulo void *p;
33532176cfdSRui Paulo
33632176cfdSRui Paulo space = req.space;
33732176cfdSRui Paulo /* XXX M_WAITOK after driver lock released */
3384f655ef5SMatthew Dillon #if defined(__DragonFly__)
339fcaa651dSRui Paulo p = kmalloc(space, M_TEMP, M_INTWAIT | M_ZERO);
3404f655ef5SMatthew Dillon #else
3414f655ef5SMatthew Dillon p = IEEE80211_MALLOC(space, M_TEMP,
3424f655ef5SMatthew Dillon IEEE80211_M_NOWAIT | IEEE80211_M_ZERO);
3434f655ef5SMatthew Dillon #endif
34432176cfdSRui Paulo if (p == NULL)
34532176cfdSRui Paulo return ENOMEM;
34632176cfdSRui Paulo req.sr = p;
34732176cfdSRui Paulo ieee80211_scan_iterate(vap, get_scan_result, &req);
34832176cfdSRui Paulo ireq->i_len = space - req.space;
34932176cfdSRui Paulo error = copyout(p, ireq->i_data, ireq->i_len);
3504f655ef5SMatthew Dillon IEEE80211_FREE(p, M_TEMP);
35132176cfdSRui Paulo } else
35232176cfdSRui Paulo ireq->i_len = 0;
35332176cfdSRui Paulo
354841ab66cSSepherosa Ziehau return error;
355841ab66cSSepherosa Ziehau }
356841ab66cSSepherosa Ziehau
357841ab66cSSepherosa Ziehau struct stainforeq {
35832176cfdSRui Paulo struct ieee80211vap *vap;
359841ab66cSSepherosa Ziehau struct ieee80211req_sta_info *si;
360841ab66cSSepherosa Ziehau size_t space;
361841ab66cSSepherosa Ziehau };
362841ab66cSSepherosa Ziehau
363841ab66cSSepherosa Ziehau static size_t
sta_space(const struct ieee80211_node * ni,size_t * ielen)364841ab66cSSepherosa Ziehau sta_space(const struct ieee80211_node *ni, size_t *ielen)
365841ab66cSSepherosa Ziehau {
36632176cfdSRui Paulo *ielen = ni->ni_ies.len;
367841ab66cSSepherosa Ziehau return roundup(sizeof(struct ieee80211req_sta_info) + *ielen,
368841ab66cSSepherosa Ziehau sizeof(uint32_t));
369841ab66cSSepherosa Ziehau }
370841ab66cSSepherosa Ziehau
371841ab66cSSepherosa Ziehau static void
get_sta_space(void * arg,struct ieee80211_node * ni)372841ab66cSSepherosa Ziehau get_sta_space(void *arg, struct ieee80211_node *ni)
373841ab66cSSepherosa Ziehau {
374841ab66cSSepherosa Ziehau struct stainforeq *req = arg;
375841ab66cSSepherosa Ziehau size_t ielen;
376841ab66cSSepherosa Ziehau
37732176cfdSRui Paulo if (req->vap != ni->ni_vap)
37832176cfdSRui Paulo return;
37932176cfdSRui Paulo if (ni->ni_vap->iv_opmode == IEEE80211_M_HOSTAP &&
380841ab66cSSepherosa Ziehau ni->ni_associd == 0) /* only associated stations */
381841ab66cSSepherosa Ziehau return;
382841ab66cSSepherosa Ziehau req->space += sta_space(ni, &ielen);
383841ab66cSSepherosa Ziehau }
384841ab66cSSepherosa Ziehau
3854f655ef5SMatthew Dillon static void
get_sta_info(void * arg,struct ieee80211_node * ni)386841ab66cSSepherosa Ziehau get_sta_info(void *arg, struct ieee80211_node *ni)
387841ab66cSSepherosa Ziehau {
388841ab66cSSepherosa Ziehau struct stainforeq *req = arg;
38932176cfdSRui Paulo struct ieee80211vap *vap = ni->ni_vap;
390841ab66cSSepherosa Ziehau struct ieee80211req_sta_info *si;
391841ab66cSSepherosa Ziehau size_t ielen, len;
392841ab66cSSepherosa Ziehau uint8_t *cp;
393841ab66cSSepherosa Ziehau
39432176cfdSRui Paulo if (req->vap != ni->ni_vap)
39532176cfdSRui Paulo return;
39632176cfdSRui Paulo if (vap->iv_opmode == IEEE80211_M_HOSTAP &&
397841ab66cSSepherosa Ziehau ni->ni_associd == 0) /* only associated stations */
398841ab66cSSepherosa Ziehau return;
399841ab66cSSepherosa Ziehau if (ni->ni_chan == IEEE80211_CHAN_ANYC) /* XXX bogus entry */
400841ab66cSSepherosa Ziehau return;
401841ab66cSSepherosa Ziehau len = sta_space(ni, &ielen);
402841ab66cSSepherosa Ziehau if (len > req->space)
403841ab66cSSepherosa Ziehau return;
404841ab66cSSepherosa Ziehau si = req->si;
405841ab66cSSepherosa Ziehau si->isi_len = len;
40632176cfdSRui Paulo si->isi_ie_off = sizeof(struct ieee80211req_sta_info);
407841ab66cSSepherosa Ziehau si->isi_ie_len = ielen;
408841ab66cSSepherosa Ziehau si->isi_freq = ni->ni_chan->ic_freq;
409841ab66cSSepherosa Ziehau si->isi_flags = ni->ni_chan->ic_flags;
410841ab66cSSepherosa Ziehau si->isi_state = ni->ni_flags;
411841ab66cSSepherosa Ziehau si->isi_authmode = ni->ni_authmode;
41232176cfdSRui Paulo vap->iv_ic->ic_node_getsignal(ni, &si->isi_rssi, &si->isi_noise);
41332176cfdSRui Paulo vap->iv_ic->ic_node_getmimoinfo(ni, &si->isi_mimo);
41432176cfdSRui Paulo si->isi_capinfo = ni->ni_capinfo;
415841ab66cSSepherosa Ziehau si->isi_erp = ni->ni_erp;
416841ab66cSSepherosa Ziehau IEEE80211_ADDR_COPY(si->isi_macaddr, ni->ni_macaddr);
417841ab66cSSepherosa Ziehau si->isi_nrates = ni->ni_rates.rs_nrates;
418841ab66cSSepherosa Ziehau if (si->isi_nrates > 15)
419841ab66cSSepherosa Ziehau si->isi_nrates = 15;
420841ab66cSSepherosa Ziehau memcpy(si->isi_rates, ni->ni_rates.rs_rates, si->isi_nrates);
421841ab66cSSepherosa Ziehau si->isi_txrate = ni->ni_txrate;
42232176cfdSRui Paulo if (si->isi_txrate & IEEE80211_RATE_MCS) {
42332176cfdSRui Paulo const struct ieee80211_mcs_rates *mcs =
42432176cfdSRui Paulo &ieee80211_htrates[ni->ni_txrate &~ IEEE80211_RATE_MCS];
42532176cfdSRui Paulo if (IEEE80211_IS_CHAN_HT40(ni->ni_chan)) {
42632176cfdSRui Paulo if (ni->ni_flags & IEEE80211_NODE_SGI40)
42732176cfdSRui Paulo si->isi_txmbps = mcs->ht40_rate_800ns;
42832176cfdSRui Paulo else
42932176cfdSRui Paulo si->isi_txmbps = mcs->ht40_rate_400ns;
43032176cfdSRui Paulo } else {
43132176cfdSRui Paulo if (ni->ni_flags & IEEE80211_NODE_SGI20)
43232176cfdSRui Paulo si->isi_txmbps = mcs->ht20_rate_800ns;
43332176cfdSRui Paulo else
43432176cfdSRui Paulo si->isi_txmbps = mcs->ht20_rate_400ns;
43532176cfdSRui Paulo }
43632176cfdSRui Paulo } else
43732176cfdSRui Paulo si->isi_txmbps = si->isi_txrate;
438841ab66cSSepherosa Ziehau si->isi_associd = ni->ni_associd;
439841ab66cSSepherosa Ziehau si->isi_txpower = ni->ni_txpower;
440841ab66cSSepherosa Ziehau si->isi_vlan = ni->ni_vlan;
441841ab66cSSepherosa Ziehau if (ni->ni_flags & IEEE80211_NODE_QOS) {
442841ab66cSSepherosa Ziehau memcpy(si->isi_txseqs, ni->ni_txseqs, sizeof(ni->ni_txseqs));
443841ab66cSSepherosa Ziehau memcpy(si->isi_rxseqs, ni->ni_rxseqs, sizeof(ni->ni_rxseqs));
444841ab66cSSepherosa Ziehau } else {
44532176cfdSRui Paulo si->isi_txseqs[0] = ni->ni_txseqs[IEEE80211_NONQOS_TID];
44632176cfdSRui Paulo si->isi_rxseqs[0] = ni->ni_rxseqs[IEEE80211_NONQOS_TID];
447841ab66cSSepherosa Ziehau }
448841ab66cSSepherosa Ziehau /* NB: leave all cases in case we relax ni_associd == 0 check */
449841ab66cSSepherosa Ziehau if (ieee80211_node_is_authorized(ni))
45032176cfdSRui Paulo si->isi_inact = vap->iv_inact_run;
45132176cfdSRui Paulo else if (ni->ni_associd != 0 ||
45232176cfdSRui Paulo (vap->iv_opmode == IEEE80211_M_WDS &&
45332176cfdSRui Paulo (vap->iv_flags_ext & IEEE80211_FEXT_WDSLEGACY)))
45432176cfdSRui Paulo si->isi_inact = vap->iv_inact_auth;
455841ab66cSSepherosa Ziehau else
45632176cfdSRui Paulo si->isi_inact = vap->iv_inact_init;
457841ab66cSSepherosa Ziehau si->isi_inact = (si->isi_inact - ni->ni_inact) * IEEE80211_INACT_WAIT;
45832176cfdSRui Paulo si->isi_localid = ni->ni_mllid;
45932176cfdSRui Paulo si->isi_peerid = ni->ni_mlpid;
46032176cfdSRui Paulo si->isi_peerstate = ni->ni_mlstate;
461841ab66cSSepherosa Ziehau
46232176cfdSRui Paulo if (ielen) {
46332176cfdSRui Paulo cp = ((uint8_t *)si) + si->isi_ie_off;
46432176cfdSRui Paulo memcpy(cp, ni->ni_ies.data, ielen);
465841ab66cSSepherosa Ziehau }
466841ab66cSSepherosa Ziehau
467841ab66cSSepherosa Ziehau req->si = (struct ieee80211req_sta_info *)(((uint8_t *)si) + len);
468841ab66cSSepherosa Ziehau req->space -= len;
469841ab66cSSepherosa Ziehau }
470841ab66cSSepherosa Ziehau
4714f655ef5SMatthew Dillon static int
getstainfo_common(struct ieee80211vap * vap,struct ieee80211req * ireq,struct ieee80211_node * ni,size_t off)47232176cfdSRui Paulo getstainfo_common(struct ieee80211vap *vap, struct ieee80211req *ireq,
473085ff963SMatthew Dillon struct ieee80211_node *ni, size_t off)
474841ab66cSSepherosa Ziehau {
47532176cfdSRui Paulo struct ieee80211com *ic = vap->iv_ic;
476841ab66cSSepherosa Ziehau struct stainforeq req;
47732176cfdSRui Paulo size_t space;
47832176cfdSRui Paulo void *p;
479841ab66cSSepherosa Ziehau int error;
480841ab66cSSepherosa Ziehau
48132176cfdSRui Paulo error = 0;
482841ab66cSSepherosa Ziehau req.space = 0;
48332176cfdSRui Paulo req.vap = vap;
4840d17a301SSepherosa Ziehau if (ni == NULL)
485841ab66cSSepherosa Ziehau ieee80211_iterate_nodes(&ic->ic_sta, get_sta_space, &req);
4860d17a301SSepherosa Ziehau else
4870d17a301SSepherosa Ziehau get_sta_space(&req, ni);
488841ab66cSSepherosa Ziehau if (req.space > ireq->i_len)
489841ab66cSSepherosa Ziehau req.space = ireq->i_len;
490841ab66cSSepherosa Ziehau if (req.space > 0) {
491841ab66cSSepherosa Ziehau space = req.space;
492841ab66cSSepherosa Ziehau /* XXX M_WAITOK after driver lock released */
4934f655ef5SMatthew Dillon #if defined(__DragonFly__)
494fcaa651dSRui Paulo p = kmalloc(space, M_TEMP, M_INTWAIT | M_ZERO);
4954f655ef5SMatthew Dillon #else
4964f655ef5SMatthew Dillon p = IEEE80211_MALLOC(space, M_TEMP,
4974f655ef5SMatthew Dillon IEEE80211_M_NOWAIT | IEEE80211_M_ZERO);
4984f655ef5SMatthew Dillon #endif
4990d17a301SSepherosa Ziehau if (p == NULL) {
5000d17a301SSepherosa Ziehau error = ENOMEM;
5010d17a301SSepherosa Ziehau goto bad;
5020d17a301SSepherosa Ziehau }
503841ab66cSSepherosa Ziehau req.si = p;
5040d17a301SSepherosa Ziehau if (ni == NULL)
505841ab66cSSepherosa Ziehau ieee80211_iterate_nodes(&ic->ic_sta, get_sta_info, &req);
5060d17a301SSepherosa Ziehau else
5070d17a301SSepherosa Ziehau get_sta_info(&req, ni);
508841ab66cSSepherosa Ziehau ireq->i_len = space - req.space;
5090d17a301SSepherosa Ziehau error = copyout(p, (uint8_t *) ireq->i_data+off, ireq->i_len);
5104f655ef5SMatthew Dillon IEEE80211_FREE(p, M_TEMP);
51132176cfdSRui Paulo } else
512841ab66cSSepherosa Ziehau ireq->i_len = 0;
5130d17a301SSepherosa Ziehau bad:
5140d17a301SSepherosa Ziehau if (ni != NULL)
5150d17a301SSepherosa Ziehau ieee80211_free_node(ni);
516841ab66cSSepherosa Ziehau return error;
517841ab66cSSepherosa Ziehau }
518841ab66cSSepherosa Ziehau
5194f655ef5SMatthew Dillon static int
ieee80211_ioctl_getstainfo(struct ieee80211vap * vap,struct ieee80211req * ireq)52032176cfdSRui Paulo ieee80211_ioctl_getstainfo(struct ieee80211vap *vap, struct ieee80211req *ireq)
52132176cfdSRui Paulo {
52232176cfdSRui Paulo uint8_t macaddr[IEEE80211_ADDR_LEN];
523085ff963SMatthew Dillon const size_t off = __offsetof(struct ieee80211req_sta_req, info);
52432176cfdSRui Paulo struct ieee80211_node *ni;
52532176cfdSRui Paulo int error;
52632176cfdSRui Paulo
52732176cfdSRui Paulo if (ireq->i_len < sizeof(struct ieee80211req_sta_req))
52832176cfdSRui Paulo return EFAULT;
52932176cfdSRui Paulo error = copyin(ireq->i_data, macaddr, IEEE80211_ADDR_LEN);
53032176cfdSRui Paulo if (error != 0)
53132176cfdSRui Paulo return error;
53232176cfdSRui Paulo if (IEEE80211_ADDR_EQ(macaddr, vap->iv_ifp->if_broadcastaddr)) {
53332176cfdSRui Paulo ni = NULL;
53432176cfdSRui Paulo } else {
53532176cfdSRui Paulo ni = ieee80211_find_vap_node(&vap->iv_ic->ic_sta, vap, macaddr);
53632176cfdSRui Paulo if (ni == NULL)
53732176cfdSRui Paulo return ENOENT;
53832176cfdSRui Paulo }
53932176cfdSRui Paulo return getstainfo_common(vap, ireq, ni, off);
54032176cfdSRui Paulo }
54132176cfdSRui Paulo
5424f655ef5SMatthew Dillon static int
ieee80211_ioctl_getstatxpow(struct ieee80211vap * vap,struct ieee80211req * ireq)54332176cfdSRui Paulo ieee80211_ioctl_getstatxpow(struct ieee80211vap *vap, struct ieee80211req *ireq)
544841ab66cSSepherosa Ziehau {
545841ab66cSSepherosa Ziehau struct ieee80211_node *ni;
546841ab66cSSepherosa Ziehau struct ieee80211req_sta_txpow txpow;
547841ab66cSSepherosa Ziehau int error;
548841ab66cSSepherosa Ziehau
549841ab66cSSepherosa Ziehau if (ireq->i_len != sizeof(txpow))
550841ab66cSSepherosa Ziehau return EINVAL;
551841ab66cSSepherosa Ziehau error = copyin(ireq->i_data, &txpow, sizeof(txpow));
552841ab66cSSepherosa Ziehau if (error != 0)
553841ab66cSSepherosa Ziehau return error;
55432176cfdSRui Paulo ni = ieee80211_find_vap_node(&vap->iv_ic->ic_sta, vap, txpow.it_macaddr);
555841ab66cSSepherosa Ziehau if (ni == NULL)
55632176cfdSRui Paulo return ENOENT;
557841ab66cSSepherosa Ziehau txpow.it_txpow = ni->ni_txpower;
558841ab66cSSepherosa Ziehau error = copyout(&txpow, ireq->i_data, sizeof(txpow));
559841ab66cSSepherosa Ziehau ieee80211_free_node(ni);
560841ab66cSSepherosa Ziehau return error;
561841ab66cSSepherosa Ziehau }
562841ab66cSSepherosa Ziehau
5634f655ef5SMatthew Dillon static int
ieee80211_ioctl_getwmeparam(struct ieee80211vap * vap,struct ieee80211req * ireq)56432176cfdSRui Paulo ieee80211_ioctl_getwmeparam(struct ieee80211vap *vap, struct ieee80211req *ireq)
565841ab66cSSepherosa Ziehau {
56632176cfdSRui Paulo struct ieee80211com *ic = vap->iv_ic;
567841ab66cSSepherosa Ziehau struct ieee80211_wme_state *wme = &ic->ic_wme;
568841ab66cSSepherosa Ziehau struct wmeParams *wmep;
569841ab66cSSepherosa Ziehau int ac;
570841ab66cSSepherosa Ziehau
571841ab66cSSepherosa Ziehau if ((ic->ic_caps & IEEE80211_C_WME) == 0)
572841ab66cSSepherosa Ziehau return EINVAL;
573841ab66cSSepherosa Ziehau
574841ab66cSSepherosa Ziehau ac = (ireq->i_len & IEEE80211_WMEPARAM_VAL);
575841ab66cSSepherosa Ziehau if (ac >= WME_NUM_AC)
576841ab66cSSepherosa Ziehau ac = WME_AC_BE;
577841ab66cSSepherosa Ziehau if (ireq->i_len & IEEE80211_WMEPARAM_BSS)
578841ab66cSSepherosa Ziehau wmep = &wme->wme_wmeBssChanParams.cap_wmeParams[ac];
579841ab66cSSepherosa Ziehau else
580841ab66cSSepherosa Ziehau wmep = &wme->wme_wmeChanParams.cap_wmeParams[ac];
581841ab66cSSepherosa Ziehau switch (ireq->i_type) {
582841ab66cSSepherosa Ziehau case IEEE80211_IOC_WME_CWMIN: /* WME: CWmin */
583841ab66cSSepherosa Ziehau ireq->i_val = wmep->wmep_logcwmin;
584841ab66cSSepherosa Ziehau break;
585841ab66cSSepherosa Ziehau case IEEE80211_IOC_WME_CWMAX: /* WME: CWmax */
586841ab66cSSepherosa Ziehau ireq->i_val = wmep->wmep_logcwmax;
587841ab66cSSepherosa Ziehau break;
588841ab66cSSepherosa Ziehau case IEEE80211_IOC_WME_AIFS: /* WME: AIFS */
589841ab66cSSepherosa Ziehau ireq->i_val = wmep->wmep_aifsn;
590841ab66cSSepherosa Ziehau break;
591841ab66cSSepherosa Ziehau case IEEE80211_IOC_WME_TXOPLIMIT: /* WME: txops limit */
592841ab66cSSepherosa Ziehau ireq->i_val = wmep->wmep_txopLimit;
593841ab66cSSepherosa Ziehau break;
594841ab66cSSepherosa Ziehau case IEEE80211_IOC_WME_ACM: /* WME: ACM (bss only) */
595841ab66cSSepherosa Ziehau wmep = &wme->wme_wmeBssChanParams.cap_wmeParams[ac];
596841ab66cSSepherosa Ziehau ireq->i_val = wmep->wmep_acm;
597841ab66cSSepherosa Ziehau break;
598841ab66cSSepherosa Ziehau case IEEE80211_IOC_WME_ACKPOLICY: /* WME: ACK policy (!bss only)*/
599841ab66cSSepherosa Ziehau wmep = &wme->wme_wmeChanParams.cap_wmeParams[ac];
600841ab66cSSepherosa Ziehau ireq->i_val = !wmep->wmep_noackPolicy;
601841ab66cSSepherosa Ziehau break;
602841ab66cSSepherosa Ziehau }
603841ab66cSSepherosa Ziehau return 0;
604841ab66cSSepherosa Ziehau }
605841ab66cSSepherosa Ziehau
6064f655ef5SMatthew Dillon static int
ieee80211_ioctl_getmaccmd(struct ieee80211vap * vap,struct ieee80211req * ireq)60732176cfdSRui Paulo ieee80211_ioctl_getmaccmd(struct ieee80211vap *vap, struct ieee80211req *ireq)
608841ab66cSSepherosa Ziehau {
60932176cfdSRui Paulo const struct ieee80211_aclator *acl = vap->iv_acl;
610841ab66cSSepherosa Ziehau
61132176cfdSRui Paulo return (acl == NULL ? EINVAL : acl->iac_getioctl(vap, ireq));
61232176cfdSRui Paulo }
61332176cfdSRui Paulo
6144f655ef5SMatthew Dillon static int
ieee80211_ioctl_getcurchan(struct ieee80211vap * vap,struct ieee80211req * ireq)61532176cfdSRui Paulo ieee80211_ioctl_getcurchan(struct ieee80211vap *vap, struct ieee80211req *ireq)
61632176cfdSRui Paulo {
61732176cfdSRui Paulo struct ieee80211com *ic = vap->iv_ic;
61832176cfdSRui Paulo struct ieee80211_channel *c;
61932176cfdSRui Paulo
62032176cfdSRui Paulo if (ireq->i_len != sizeof(struct ieee80211_channel))
62132176cfdSRui Paulo return EINVAL;
62232176cfdSRui Paulo /*
62332176cfdSRui Paulo * vap's may have different operating channels when HT is
62432176cfdSRui Paulo * in use. When in RUN state report the vap-specific channel.
62532176cfdSRui Paulo * Otherwise return curchan.
62632176cfdSRui Paulo */
627d98a0bcfSMatthew Dillon if (vap->iv_state == IEEE80211_S_RUN || vap->iv_state == IEEE80211_S_SLEEP)
62832176cfdSRui Paulo c = vap->iv_bss->ni_chan;
62932176cfdSRui Paulo else
63032176cfdSRui Paulo c = ic->ic_curchan;
63132176cfdSRui Paulo return copyout(c, ireq->i_data, sizeof(*c));
63232176cfdSRui Paulo }
63332176cfdSRui Paulo
63432176cfdSRui Paulo static int
getappie(const struct ieee80211_appie * aie,struct ieee80211req * ireq)63532176cfdSRui Paulo getappie(const struct ieee80211_appie *aie, struct ieee80211req *ireq)
63632176cfdSRui Paulo {
63732176cfdSRui Paulo if (aie == NULL)
63832176cfdSRui Paulo return EINVAL;
63932176cfdSRui Paulo /* NB: truncate, caller can check length */
64032176cfdSRui Paulo if (ireq->i_len > aie->ie_len)
64132176cfdSRui Paulo ireq->i_len = aie->ie_len;
64232176cfdSRui Paulo return copyout(aie->ie_data, ireq->i_data, ireq->i_len);
64332176cfdSRui Paulo }
64432176cfdSRui Paulo
64532176cfdSRui Paulo static int
ieee80211_ioctl_getappie(struct ieee80211vap * vap,struct ieee80211req * ireq)64632176cfdSRui Paulo ieee80211_ioctl_getappie(struct ieee80211vap *vap, struct ieee80211req *ireq)
64732176cfdSRui Paulo {
64832176cfdSRui Paulo uint8_t fc0;
64932176cfdSRui Paulo
65032176cfdSRui Paulo fc0 = ireq->i_val & 0xff;
65132176cfdSRui Paulo if ((fc0 & IEEE80211_FC0_TYPE_MASK) != IEEE80211_FC0_TYPE_MGT)
65232176cfdSRui Paulo return EINVAL;
65332176cfdSRui Paulo /* NB: could check iv_opmode and reject but hardly worth the effort */
65432176cfdSRui Paulo switch (fc0 & IEEE80211_FC0_SUBTYPE_MASK) {
65532176cfdSRui Paulo case IEEE80211_FC0_SUBTYPE_BEACON:
65632176cfdSRui Paulo return getappie(vap->iv_appie_beacon, ireq);
65732176cfdSRui Paulo case IEEE80211_FC0_SUBTYPE_PROBE_RESP:
65832176cfdSRui Paulo return getappie(vap->iv_appie_proberesp, ireq);
65932176cfdSRui Paulo case IEEE80211_FC0_SUBTYPE_ASSOC_RESP:
66032176cfdSRui Paulo return getappie(vap->iv_appie_assocresp, ireq);
66132176cfdSRui Paulo case IEEE80211_FC0_SUBTYPE_PROBE_REQ:
66232176cfdSRui Paulo return getappie(vap->iv_appie_probereq, ireq);
66332176cfdSRui Paulo case IEEE80211_FC0_SUBTYPE_ASSOC_REQ:
66432176cfdSRui Paulo return getappie(vap->iv_appie_assocreq, ireq);
66532176cfdSRui Paulo case IEEE80211_FC0_SUBTYPE_BEACON|IEEE80211_FC0_SUBTYPE_PROBE_RESP:
66632176cfdSRui Paulo return getappie(vap->iv_appie_wpa, ireq);
66732176cfdSRui Paulo }
66832176cfdSRui Paulo return EINVAL;
66932176cfdSRui Paulo }
67032176cfdSRui Paulo
6714f655ef5SMatthew Dillon static int
ieee80211_ioctl_getregdomain(struct ieee80211vap * vap,const struct ieee80211req * ireq)67232176cfdSRui Paulo ieee80211_ioctl_getregdomain(struct ieee80211vap *vap,
67332176cfdSRui Paulo const struct ieee80211req *ireq)
67432176cfdSRui Paulo {
67532176cfdSRui Paulo struct ieee80211com *ic = vap->iv_ic;
67632176cfdSRui Paulo
67732176cfdSRui Paulo if (ireq->i_len != sizeof(ic->ic_regdomain))
67832176cfdSRui Paulo return EINVAL;
67932176cfdSRui Paulo return copyout(&ic->ic_regdomain, ireq->i_data,
68032176cfdSRui Paulo sizeof(ic->ic_regdomain));
68132176cfdSRui Paulo }
68232176cfdSRui Paulo
6834f655ef5SMatthew Dillon static int
ieee80211_ioctl_getroam(struct ieee80211vap * vap,const struct ieee80211req * ireq)68432176cfdSRui Paulo ieee80211_ioctl_getroam(struct ieee80211vap *vap,
68532176cfdSRui Paulo const struct ieee80211req *ireq)
68632176cfdSRui Paulo {
68732176cfdSRui Paulo size_t len = ireq->i_len;
68832176cfdSRui Paulo /* NB: accept short requests for backwards compat */
68932176cfdSRui Paulo if (len > sizeof(vap->iv_roamparms))
69032176cfdSRui Paulo len = sizeof(vap->iv_roamparms);
69132176cfdSRui Paulo return copyout(vap->iv_roamparms, ireq->i_data, len);
69232176cfdSRui Paulo }
69332176cfdSRui Paulo
6944f655ef5SMatthew Dillon static int
ieee80211_ioctl_gettxparams(struct ieee80211vap * vap,const struct ieee80211req * ireq)69532176cfdSRui Paulo ieee80211_ioctl_gettxparams(struct ieee80211vap *vap,
69632176cfdSRui Paulo const struct ieee80211req *ireq)
69732176cfdSRui Paulo {
69832176cfdSRui Paulo size_t len = ireq->i_len;
69932176cfdSRui Paulo /* NB: accept short requests for backwards compat */
70032176cfdSRui Paulo if (len > sizeof(vap->iv_txparms))
70132176cfdSRui Paulo len = sizeof(vap->iv_txparms);
70232176cfdSRui Paulo return copyout(vap->iv_txparms, ireq->i_data, len);
70332176cfdSRui Paulo }
70432176cfdSRui Paulo
7054f655ef5SMatthew Dillon static int
ieee80211_ioctl_getdevcaps(struct ieee80211com * ic,const struct ieee80211req * ireq)70632176cfdSRui Paulo ieee80211_ioctl_getdevcaps(struct ieee80211com *ic,
70732176cfdSRui Paulo const struct ieee80211req *ireq)
70832176cfdSRui Paulo {
70932176cfdSRui Paulo struct ieee80211_devcaps_req *dc;
71032176cfdSRui Paulo struct ieee80211req_chaninfo *ci;
71132176cfdSRui Paulo int maxchans, error;
71232176cfdSRui Paulo
71332176cfdSRui Paulo maxchans = 1 + ((ireq->i_len - sizeof(struct ieee80211_devcaps_req)) /
71432176cfdSRui Paulo sizeof(struct ieee80211_channel));
71532176cfdSRui Paulo /* NB: require 1 so we know ic_nchans is accessible */
71632176cfdSRui Paulo if (maxchans < 1)
71732176cfdSRui Paulo return EINVAL;
71832176cfdSRui Paulo /* constrain max request size, 2K channels is ~24Kbytes */
71932176cfdSRui Paulo if (maxchans > 2048)
72032176cfdSRui Paulo maxchans = 2048;
7214f655ef5SMatthew Dillon #if defined(__DragonFly__)
72232176cfdSRui Paulo dc = (struct ieee80211_devcaps_req *)
723085ff963SMatthew Dillon kmalloc(IEEE80211_DEVCAPS_SIZE(maxchans), M_TEMP, M_INTWAIT | M_ZERO);
7244f655ef5SMatthew Dillon #else
7254f655ef5SMatthew Dillon dc = (struct ieee80211_devcaps_req *)
7264f655ef5SMatthew Dillon IEEE80211_MALLOC(IEEE80211_DEVCAPS_SIZE(maxchans), M_TEMP,
7274f655ef5SMatthew Dillon IEEE80211_M_NOWAIT | IEEE80211_M_ZERO);
7284f655ef5SMatthew Dillon #endif
72932176cfdSRui Paulo if (dc == NULL)
73032176cfdSRui Paulo return ENOMEM;
73132176cfdSRui Paulo dc->dc_drivercaps = ic->ic_caps;
73232176cfdSRui Paulo dc->dc_cryptocaps = ic->ic_cryptocaps;
73332176cfdSRui Paulo dc->dc_htcaps = ic->ic_htcaps;
73432176cfdSRui Paulo ci = &dc->dc_chaninfo;
73532176cfdSRui Paulo ic->ic_getradiocaps(ic, maxchans, &ci->ic_nchans, ci->ic_chans);
73632176cfdSRui Paulo KASSERT(ci->ic_nchans <= maxchans,
73732176cfdSRui Paulo ("nchans %d maxchans %d", ci->ic_nchans, maxchans));
73832176cfdSRui Paulo ieee80211_sort_channels(ci->ic_chans, ci->ic_nchans);
73932176cfdSRui Paulo error = copyout(dc, ireq->i_data, IEEE80211_DEVCAPS_SPACE(dc));
7404f655ef5SMatthew Dillon IEEE80211_FREE(dc, M_TEMP);
74132176cfdSRui Paulo return error;
74232176cfdSRui Paulo }
74332176cfdSRui Paulo
7444f655ef5SMatthew Dillon static int
ieee80211_ioctl_getstavlan(struct ieee80211vap * vap,struct ieee80211req * ireq)74532176cfdSRui Paulo ieee80211_ioctl_getstavlan(struct ieee80211vap *vap, struct ieee80211req *ireq)
74632176cfdSRui Paulo {
74732176cfdSRui Paulo struct ieee80211_node *ni;
74832176cfdSRui Paulo struct ieee80211req_sta_vlan vlan;
74932176cfdSRui Paulo int error;
75032176cfdSRui Paulo
75132176cfdSRui Paulo if (ireq->i_len != sizeof(vlan))
75232176cfdSRui Paulo return EINVAL;
75332176cfdSRui Paulo error = copyin(ireq->i_data, &vlan, sizeof(vlan));
75432176cfdSRui Paulo if (error != 0)
75532176cfdSRui Paulo return error;
75632176cfdSRui Paulo if (!IEEE80211_ADDR_EQ(vlan.sv_macaddr, zerobssid)) {
75732176cfdSRui Paulo ni = ieee80211_find_vap_node(&vap->iv_ic->ic_sta, vap,
75832176cfdSRui Paulo vlan.sv_macaddr);
75932176cfdSRui Paulo if (ni == NULL)
76032176cfdSRui Paulo return ENOENT;
76132176cfdSRui Paulo } else
76232176cfdSRui Paulo ni = ieee80211_ref_node(vap->iv_bss);
76332176cfdSRui Paulo vlan.sv_vlan = ni->ni_vlan;
76432176cfdSRui Paulo error = copyout(&vlan, ireq->i_data, sizeof(vlan));
76532176cfdSRui Paulo ieee80211_free_node(ni);
76632176cfdSRui Paulo return error;
76732176cfdSRui Paulo }
76832176cfdSRui Paulo
76932176cfdSRui Paulo /*
77032176cfdSRui Paulo * Dummy ioctl get handler so the linker set is defined.
77132176cfdSRui Paulo */
77232176cfdSRui Paulo static int
dummy_ioctl_get(struct ieee80211vap * vap,struct ieee80211req * ireq)77332176cfdSRui Paulo dummy_ioctl_get(struct ieee80211vap *vap, struct ieee80211req *ireq)
77432176cfdSRui Paulo {
77532176cfdSRui Paulo return ENOSYS;
77632176cfdSRui Paulo }
77732176cfdSRui Paulo IEEE80211_IOCTL_GET(dummy, dummy_ioctl_get);
77832176cfdSRui Paulo
77932176cfdSRui Paulo static int
ieee80211_ioctl_getdefault(struct ieee80211vap * vap,struct ieee80211req * ireq)78032176cfdSRui Paulo ieee80211_ioctl_getdefault(struct ieee80211vap *vap, struct ieee80211req *ireq)
78132176cfdSRui Paulo {
78232176cfdSRui Paulo ieee80211_ioctl_getfunc * const *get;
78332176cfdSRui Paulo int error;
78432176cfdSRui Paulo
78532176cfdSRui Paulo SET_FOREACH(get, ieee80211_ioctl_getset) {
78632176cfdSRui Paulo error = (*get)(vap, ireq);
78732176cfdSRui Paulo if (error != ENOSYS)
78832176cfdSRui Paulo return error;
78932176cfdSRui Paulo }
79032176cfdSRui Paulo return EINVAL;
791841ab66cSSepherosa Ziehau }
792841ab66cSSepherosa Ziehau
7934f655ef5SMatthew Dillon static int
ieee80211_ioctl_get80211(struct ieee80211vap * vap,u_long cmd,struct ieee80211req * ireq)79432176cfdSRui Paulo ieee80211_ioctl_get80211(struct ieee80211vap *vap, u_long cmd,
79532176cfdSRui Paulo struct ieee80211req *ireq)
796841ab66cSSepherosa Ziehau {
79732176cfdSRui Paulo #define MS(_v, _f) (((_v) & _f) >> _f##_S)
79832176cfdSRui Paulo struct ieee80211com *ic = vap->iv_ic;
79932176cfdSRui Paulo u_int kid, len;
800f186073cSJoerg Sonnenberger uint8_t tmpkey[IEEE80211_KEYBUF_SIZE];
801f186073cSJoerg Sonnenberger char tmpssid[IEEE80211_NWID_LEN];
80232176cfdSRui Paulo int error = 0;
803f186073cSJoerg Sonnenberger
804f186073cSJoerg Sonnenberger switch (ireq->i_type) {
805f186073cSJoerg Sonnenberger case IEEE80211_IOC_SSID:
80632176cfdSRui Paulo switch (vap->iv_state) {
807f186073cSJoerg Sonnenberger case IEEE80211_S_INIT:
808f186073cSJoerg Sonnenberger case IEEE80211_S_SCAN:
80932176cfdSRui Paulo ireq->i_len = vap->iv_des_ssid[0].len;
81032176cfdSRui Paulo memcpy(tmpssid, vap->iv_des_ssid[0].ssid, ireq->i_len);
811f186073cSJoerg Sonnenberger break;
812f186073cSJoerg Sonnenberger default:
81332176cfdSRui Paulo ireq->i_len = vap->iv_bss->ni_esslen;
81432176cfdSRui Paulo memcpy(tmpssid, vap->iv_bss->ni_essid, ireq->i_len);
815f186073cSJoerg Sonnenberger break;
816f186073cSJoerg Sonnenberger }
817f186073cSJoerg Sonnenberger error = copyout(tmpssid, ireq->i_data, ireq->i_len);
818f186073cSJoerg Sonnenberger break;
819f186073cSJoerg Sonnenberger case IEEE80211_IOC_NUMSSIDS:
820f186073cSJoerg Sonnenberger ireq->i_val = 1;
821f186073cSJoerg Sonnenberger break;
822f186073cSJoerg Sonnenberger case IEEE80211_IOC_WEP:
82332176cfdSRui Paulo if ((vap->iv_flags & IEEE80211_F_PRIVACY) == 0)
824841ab66cSSepherosa Ziehau ireq->i_val = IEEE80211_WEP_OFF;
82532176cfdSRui Paulo else if (vap->iv_flags & IEEE80211_F_DROPUNENC)
826841ab66cSSepherosa Ziehau ireq->i_val = IEEE80211_WEP_ON;
827841ab66cSSepherosa Ziehau else
828841ab66cSSepherosa Ziehau ireq->i_val = IEEE80211_WEP_MIXED;
829f186073cSJoerg Sonnenberger break;
830f186073cSJoerg Sonnenberger case IEEE80211_IOC_WEPKEY:
831f186073cSJoerg Sonnenberger kid = (u_int) ireq->i_val;
832841ab66cSSepherosa Ziehau if (kid >= IEEE80211_WEP_NKID)
833841ab66cSSepherosa Ziehau return EINVAL;
83432176cfdSRui Paulo len = (u_int) vap->iv_nw_keys[kid].wk_keylen;
835f186073cSJoerg Sonnenberger /* NB: only root can read WEP keys */
836*2b3f93eaSMatthew Dillon if (caps_priv_check_self(SYSCAP_NONET_WIFI) == 0) {
83732176cfdSRui Paulo bcopy(vap->iv_nw_keys[kid].wk_key, tmpkey, len);
838f186073cSJoerg Sonnenberger } else {
839f186073cSJoerg Sonnenberger bzero(tmpkey, len);
840f186073cSJoerg Sonnenberger }
841f186073cSJoerg Sonnenberger ireq->i_len = len;
842f186073cSJoerg Sonnenberger error = copyout(tmpkey, ireq->i_data, len);
843f186073cSJoerg Sonnenberger break;
844f186073cSJoerg Sonnenberger case IEEE80211_IOC_NUMWEPKEYS:
845f186073cSJoerg Sonnenberger ireq->i_val = IEEE80211_WEP_NKID;
846f186073cSJoerg Sonnenberger break;
847f186073cSJoerg Sonnenberger case IEEE80211_IOC_WEPTXKEY:
84832176cfdSRui Paulo ireq->i_val = vap->iv_def_txkey;
849f186073cSJoerg Sonnenberger break;
850f186073cSJoerg Sonnenberger case IEEE80211_IOC_AUTHMODE:
85132176cfdSRui Paulo if (vap->iv_flags & IEEE80211_F_WPA)
852841ab66cSSepherosa Ziehau ireq->i_val = IEEE80211_AUTH_WPA;
853841ab66cSSepherosa Ziehau else
85432176cfdSRui Paulo ireq->i_val = vap->iv_bss->ni_authmode;
855f186073cSJoerg Sonnenberger break;
856f186073cSJoerg Sonnenberger case IEEE80211_IOC_CHANNEL:
857841ab66cSSepherosa Ziehau ireq->i_val = ieee80211_chan2ieee(ic, ic->ic_curchan);
858f186073cSJoerg Sonnenberger break;
859f186073cSJoerg Sonnenberger case IEEE80211_IOC_POWERSAVE:
86032176cfdSRui Paulo if (vap->iv_flags & IEEE80211_F_PMGTON)
861f186073cSJoerg Sonnenberger ireq->i_val = IEEE80211_POWERSAVE_ON;
862f186073cSJoerg Sonnenberger else
863f186073cSJoerg Sonnenberger ireq->i_val = IEEE80211_POWERSAVE_OFF;
864f186073cSJoerg Sonnenberger break;
865f186073cSJoerg Sonnenberger case IEEE80211_IOC_POWERSAVESLEEP:
866f186073cSJoerg Sonnenberger ireq->i_val = ic->ic_lintval;
867f186073cSJoerg Sonnenberger break;
868f186073cSJoerg Sonnenberger case IEEE80211_IOC_RTSTHRESHOLD:
86932176cfdSRui Paulo ireq->i_val = vap->iv_rtsthreshold;
870f186073cSJoerg Sonnenberger break;
871f186073cSJoerg Sonnenberger case IEEE80211_IOC_PROTMODE:
872f186073cSJoerg Sonnenberger ireq->i_val = ic->ic_protmode;
873f186073cSJoerg Sonnenberger break;
874f186073cSJoerg Sonnenberger case IEEE80211_IOC_TXPOWER:
87532176cfdSRui Paulo /*
87632176cfdSRui Paulo * Tx power limit is the min of max regulatory
87732176cfdSRui Paulo * power, any user-set limit, and the max the
87832176cfdSRui Paulo * radio can do.
87932176cfdSRui Paulo */
88032176cfdSRui Paulo ireq->i_val = 2*ic->ic_curchan->ic_maxregpower;
88132176cfdSRui Paulo if (ireq->i_val > ic->ic_txpowlimit)
882841ab66cSSepherosa Ziehau ireq->i_val = ic->ic_txpowlimit;
88332176cfdSRui Paulo if (ireq->i_val > ic->ic_curchan->ic_maxpower)
88432176cfdSRui Paulo ireq->i_val = ic->ic_curchan->ic_maxpower;
885841ab66cSSepherosa Ziehau break;
886841ab66cSSepherosa Ziehau case IEEE80211_IOC_WPA:
88732176cfdSRui Paulo switch (vap->iv_flags & IEEE80211_F_WPA) {
888841ab66cSSepherosa Ziehau case IEEE80211_F_WPA1:
889841ab66cSSepherosa Ziehau ireq->i_val = 1;
890841ab66cSSepherosa Ziehau break;
891841ab66cSSepherosa Ziehau case IEEE80211_F_WPA2:
892841ab66cSSepherosa Ziehau ireq->i_val = 2;
893841ab66cSSepherosa Ziehau break;
894841ab66cSSepherosa Ziehau case IEEE80211_F_WPA1 | IEEE80211_F_WPA2:
895841ab66cSSepherosa Ziehau ireq->i_val = 3;
896841ab66cSSepherosa Ziehau break;
897841ab66cSSepherosa Ziehau default:
898841ab66cSSepherosa Ziehau ireq->i_val = 0;
899841ab66cSSepherosa Ziehau break;
900841ab66cSSepherosa Ziehau }
901841ab66cSSepherosa Ziehau break;
902841ab66cSSepherosa Ziehau case IEEE80211_IOC_CHANLIST:
90332176cfdSRui Paulo error = ieee80211_ioctl_getchanlist(vap, ireq);
904841ab66cSSepherosa Ziehau break;
905841ab66cSSepherosa Ziehau case IEEE80211_IOC_ROAMING:
90632176cfdSRui Paulo ireq->i_val = vap->iv_roaming;
907841ab66cSSepherosa Ziehau break;
908841ab66cSSepherosa Ziehau case IEEE80211_IOC_PRIVACY:
90932176cfdSRui Paulo ireq->i_val = (vap->iv_flags & IEEE80211_F_PRIVACY) != 0;
910841ab66cSSepherosa Ziehau break;
911841ab66cSSepherosa Ziehau case IEEE80211_IOC_DROPUNENCRYPTED:
91232176cfdSRui Paulo ireq->i_val = (vap->iv_flags & IEEE80211_F_DROPUNENC) != 0;
913841ab66cSSepherosa Ziehau break;
914841ab66cSSepherosa Ziehau case IEEE80211_IOC_COUNTERMEASURES:
91532176cfdSRui Paulo ireq->i_val = (vap->iv_flags & IEEE80211_F_COUNTERM) != 0;
916841ab66cSSepherosa Ziehau break;
917841ab66cSSepherosa Ziehau case IEEE80211_IOC_WME:
91832176cfdSRui Paulo ireq->i_val = (vap->iv_flags & IEEE80211_F_WME) != 0;
919841ab66cSSepherosa Ziehau break;
920841ab66cSSepherosa Ziehau case IEEE80211_IOC_HIDESSID:
92132176cfdSRui Paulo ireq->i_val = (vap->iv_flags & IEEE80211_F_HIDESSID) != 0;
922841ab66cSSepherosa Ziehau break;
923841ab66cSSepherosa Ziehau case IEEE80211_IOC_APBRIDGE:
92432176cfdSRui Paulo ireq->i_val = (vap->iv_flags & IEEE80211_F_NOBRIDGE) == 0;
925841ab66cSSepherosa Ziehau break;
926841ab66cSSepherosa Ziehau case IEEE80211_IOC_WPAKEY:
92732176cfdSRui Paulo error = ieee80211_ioctl_getkey(vap, ireq);
928841ab66cSSepherosa Ziehau break;
929841ab66cSSepherosa Ziehau case IEEE80211_IOC_CHANINFO:
93032176cfdSRui Paulo error = ieee80211_ioctl_getchaninfo(vap, ireq);
931841ab66cSSepherosa Ziehau break;
932841ab66cSSepherosa Ziehau case IEEE80211_IOC_BSSID:
933841ab66cSSepherosa Ziehau if (ireq->i_len != IEEE80211_ADDR_LEN)
934841ab66cSSepherosa Ziehau return EINVAL;
935d98a0bcfSMatthew Dillon if (vap->iv_state == IEEE80211_S_RUN || vap->iv_state == IEEE80211_S_SLEEP) {
93632176cfdSRui Paulo error = copyout(vap->iv_opmode == IEEE80211_M_WDS ?
93732176cfdSRui Paulo vap->iv_bss->ni_macaddr : vap->iv_bss->ni_bssid,
938841ab66cSSepherosa Ziehau ireq->i_data, ireq->i_len);
93932176cfdSRui Paulo } else
94032176cfdSRui Paulo error = copyout(vap->iv_des_bssid, ireq->i_data,
94132176cfdSRui Paulo ireq->i_len);
942841ab66cSSepherosa Ziehau break;
943841ab66cSSepherosa Ziehau case IEEE80211_IOC_WPAIE:
94432176cfdSRui Paulo case IEEE80211_IOC_WPAIE2:
94532176cfdSRui Paulo error = ieee80211_ioctl_getwpaie(vap, ireq, ireq->i_type);
946841ab66cSSepherosa Ziehau break;
947841ab66cSSepherosa Ziehau case IEEE80211_IOC_SCAN_RESULTS:
94832176cfdSRui Paulo error = ieee80211_ioctl_getscanresults(vap, ireq);
949841ab66cSSepherosa Ziehau break;
950841ab66cSSepherosa Ziehau case IEEE80211_IOC_STA_STATS:
95132176cfdSRui Paulo error = ieee80211_ioctl_getstastats(vap, ireq);
952841ab66cSSepherosa Ziehau break;
953841ab66cSSepherosa Ziehau case IEEE80211_IOC_TXPOWMAX:
95432176cfdSRui Paulo ireq->i_val = vap->iv_bss->ni_txpower;
955841ab66cSSepherosa Ziehau break;
956841ab66cSSepherosa Ziehau case IEEE80211_IOC_STA_TXPOW:
95732176cfdSRui Paulo error = ieee80211_ioctl_getstatxpow(vap, ireq);
958841ab66cSSepherosa Ziehau break;
959841ab66cSSepherosa Ziehau case IEEE80211_IOC_STA_INFO:
96032176cfdSRui Paulo error = ieee80211_ioctl_getstainfo(vap, ireq);
961841ab66cSSepherosa Ziehau break;
962841ab66cSSepherosa Ziehau case IEEE80211_IOC_WME_CWMIN: /* WME: CWmin */
963841ab66cSSepherosa Ziehau case IEEE80211_IOC_WME_CWMAX: /* WME: CWmax */
964841ab66cSSepherosa Ziehau case IEEE80211_IOC_WME_AIFS: /* WME: AIFS */
965841ab66cSSepherosa Ziehau case IEEE80211_IOC_WME_TXOPLIMIT: /* WME: txops limit */
966841ab66cSSepherosa Ziehau case IEEE80211_IOC_WME_ACM: /* WME: ACM (bss only) */
9674f655ef5SMatthew Dillon case IEEE80211_IOC_WME_ACKPOLICY: /* WME: ACK policy (!bss only) */
96832176cfdSRui Paulo error = ieee80211_ioctl_getwmeparam(vap, ireq);
969841ab66cSSepherosa Ziehau break;
970841ab66cSSepherosa Ziehau case IEEE80211_IOC_DTIM_PERIOD:
97132176cfdSRui Paulo ireq->i_val = vap->iv_dtim_period;
972841ab66cSSepherosa Ziehau break;
973841ab66cSSepherosa Ziehau case IEEE80211_IOC_BEACON_INTERVAL:
974841ab66cSSepherosa Ziehau /* NB: get from ic_bss for station mode */
97532176cfdSRui Paulo ireq->i_val = vap->iv_bss->ni_intval;
976841ab66cSSepherosa Ziehau break;
977841ab66cSSepherosa Ziehau case IEEE80211_IOC_PUREG:
97832176cfdSRui Paulo ireq->i_val = (vap->iv_flags & IEEE80211_F_PUREG) != 0;
979841ab66cSSepherosa Ziehau break;
980085ff963SMatthew Dillon case IEEE80211_IOC_QUIET:
981085ff963SMatthew Dillon ireq->i_val = vap->iv_quiet;
982085ff963SMatthew Dillon break;
983085ff963SMatthew Dillon case IEEE80211_IOC_QUIET_COUNT:
984085ff963SMatthew Dillon ireq->i_val = vap->iv_quiet_count;
985085ff963SMatthew Dillon break;
986085ff963SMatthew Dillon case IEEE80211_IOC_QUIET_PERIOD:
987085ff963SMatthew Dillon ireq->i_val = vap->iv_quiet_period;
988085ff963SMatthew Dillon break;
989085ff963SMatthew Dillon case IEEE80211_IOC_QUIET_DUR:
990085ff963SMatthew Dillon ireq->i_val = vap->iv_quiet_duration;
991085ff963SMatthew Dillon break;
992085ff963SMatthew Dillon case IEEE80211_IOC_QUIET_OFFSET:
993085ff963SMatthew Dillon ireq->i_val = vap->iv_quiet_offset;
994085ff963SMatthew Dillon break;
99532176cfdSRui Paulo case IEEE80211_IOC_BGSCAN:
99632176cfdSRui Paulo ireq->i_val = (vap->iv_flags & IEEE80211_F_BGSCAN) != 0;
99732176cfdSRui Paulo break;
99832176cfdSRui Paulo case IEEE80211_IOC_BGSCAN_IDLE:
99932176cfdSRui Paulo ireq->i_val = vap->iv_bgscanidle*hz/1000; /* ms */
100032176cfdSRui Paulo break;
100132176cfdSRui Paulo case IEEE80211_IOC_BGSCAN_INTERVAL:
100232176cfdSRui Paulo ireq->i_val = vap->iv_bgscanintvl/hz; /* seconds */
100332176cfdSRui Paulo break;
100432176cfdSRui Paulo case IEEE80211_IOC_SCANVALID:
100532176cfdSRui Paulo ireq->i_val = vap->iv_scanvalid/hz; /* seconds */
1006841ab66cSSepherosa Ziehau break;
1007841ab66cSSepherosa Ziehau case IEEE80211_IOC_FRAGTHRESHOLD:
100832176cfdSRui Paulo ireq->i_val = vap->iv_fragthreshold;
1009841ab66cSSepherosa Ziehau break;
1010841ab66cSSepherosa Ziehau case IEEE80211_IOC_MACCMD:
101132176cfdSRui Paulo error = ieee80211_ioctl_getmaccmd(vap, ireq);
1012841ab66cSSepherosa Ziehau break;
1013841ab66cSSepherosa Ziehau case IEEE80211_IOC_BURST:
101432176cfdSRui Paulo ireq->i_val = (vap->iv_flags & IEEE80211_F_BURST) != 0;
1015b9334f94SSepherosa Ziehau break;
1016c36e937bSSepherosa Ziehau case IEEE80211_IOC_BMISSTHRESHOLD:
101732176cfdSRui Paulo ireq->i_val = vap->iv_bmissthreshold;
101832176cfdSRui Paulo break;
101932176cfdSRui Paulo case IEEE80211_IOC_CURCHAN:
102032176cfdSRui Paulo error = ieee80211_ioctl_getcurchan(vap, ireq);
102132176cfdSRui Paulo break;
102232176cfdSRui Paulo case IEEE80211_IOC_SHORTGI:
102332176cfdSRui Paulo ireq->i_val = 0;
102432176cfdSRui Paulo if (vap->iv_flags_ht & IEEE80211_FHT_SHORTGI20)
102532176cfdSRui Paulo ireq->i_val |= IEEE80211_HTCAP_SHORTGI20;
102632176cfdSRui Paulo if (vap->iv_flags_ht & IEEE80211_FHT_SHORTGI40)
102732176cfdSRui Paulo ireq->i_val |= IEEE80211_HTCAP_SHORTGI40;
102832176cfdSRui Paulo break;
102932176cfdSRui Paulo case IEEE80211_IOC_AMPDU:
103032176cfdSRui Paulo ireq->i_val = 0;
103132176cfdSRui Paulo if (vap->iv_flags_ht & IEEE80211_FHT_AMPDU_TX)
103232176cfdSRui Paulo ireq->i_val |= 1;
103332176cfdSRui Paulo if (vap->iv_flags_ht & IEEE80211_FHT_AMPDU_RX)
103432176cfdSRui Paulo ireq->i_val |= 2;
103532176cfdSRui Paulo break;
103632176cfdSRui Paulo case IEEE80211_IOC_AMPDU_LIMIT:
103732176cfdSRui Paulo if (vap->iv_opmode == IEEE80211_M_HOSTAP)
103832176cfdSRui Paulo ireq->i_val = vap->iv_ampdu_rxmax;
1039d98a0bcfSMatthew Dillon else if (vap->iv_state == IEEE80211_S_RUN || vap->iv_state == IEEE80211_S_SLEEP)
10404f655ef5SMatthew Dillon /*
10414f655ef5SMatthew Dillon * XXX TODO: this isn't completely correct, as we've
10424f655ef5SMatthew Dillon * negotiated the higher of the two.
10434f655ef5SMatthew Dillon */
104432176cfdSRui Paulo ireq->i_val = MS(vap->iv_bss->ni_htparam,
104532176cfdSRui Paulo IEEE80211_HTCAP_MAXRXAMPDU);
104632176cfdSRui Paulo else
104732176cfdSRui Paulo ireq->i_val = vap->iv_ampdu_limit;
104832176cfdSRui Paulo break;
104932176cfdSRui Paulo case IEEE80211_IOC_AMPDU_DENSITY:
105032176cfdSRui Paulo if (vap->iv_opmode == IEEE80211_M_STA &&
1051d98a0bcfSMatthew Dillon (vap->iv_state == IEEE80211_S_RUN || vap->iv_state == IEEE80211_S_SLEEP))
10524f655ef5SMatthew Dillon /*
10534f655ef5SMatthew Dillon * XXX TODO: this isn't completely correct, as we've
10544f655ef5SMatthew Dillon * negotiated the higher of the two.
10554f655ef5SMatthew Dillon */
105632176cfdSRui Paulo ireq->i_val = MS(vap->iv_bss->ni_htparam,
105732176cfdSRui Paulo IEEE80211_HTCAP_MPDUDENSITY);
105832176cfdSRui Paulo else
105932176cfdSRui Paulo ireq->i_val = vap->iv_ampdu_density;
106032176cfdSRui Paulo break;
106132176cfdSRui Paulo case IEEE80211_IOC_AMSDU:
106232176cfdSRui Paulo ireq->i_val = 0;
106332176cfdSRui Paulo if (vap->iv_flags_ht & IEEE80211_FHT_AMSDU_TX)
106432176cfdSRui Paulo ireq->i_val |= 1;
106532176cfdSRui Paulo if (vap->iv_flags_ht & IEEE80211_FHT_AMSDU_RX)
106632176cfdSRui Paulo ireq->i_val |= 2;
106732176cfdSRui Paulo break;
106832176cfdSRui Paulo case IEEE80211_IOC_AMSDU_LIMIT:
106932176cfdSRui Paulo ireq->i_val = vap->iv_amsdu_limit; /* XXX truncation? */
107032176cfdSRui Paulo break;
107132176cfdSRui Paulo case IEEE80211_IOC_PUREN:
107232176cfdSRui Paulo ireq->i_val = (vap->iv_flags_ht & IEEE80211_FHT_PUREN) != 0;
107332176cfdSRui Paulo break;
107432176cfdSRui Paulo case IEEE80211_IOC_DOTH:
107532176cfdSRui Paulo ireq->i_val = (vap->iv_flags & IEEE80211_F_DOTH) != 0;
107632176cfdSRui Paulo break;
107732176cfdSRui Paulo case IEEE80211_IOC_REGDOMAIN:
107832176cfdSRui Paulo error = ieee80211_ioctl_getregdomain(vap, ireq);
107932176cfdSRui Paulo break;
108032176cfdSRui Paulo case IEEE80211_IOC_ROAM:
108132176cfdSRui Paulo error = ieee80211_ioctl_getroam(vap, ireq);
108232176cfdSRui Paulo break;
108332176cfdSRui Paulo case IEEE80211_IOC_TXPARAMS:
108432176cfdSRui Paulo error = ieee80211_ioctl_gettxparams(vap, ireq);
108532176cfdSRui Paulo break;
108632176cfdSRui Paulo case IEEE80211_IOC_HTCOMPAT:
108732176cfdSRui Paulo ireq->i_val = (vap->iv_flags_ht & IEEE80211_FHT_HTCOMPAT) != 0;
108832176cfdSRui Paulo break;
108932176cfdSRui Paulo case IEEE80211_IOC_DWDS:
109032176cfdSRui Paulo ireq->i_val = (vap->iv_flags & IEEE80211_F_DWDS) != 0;
109132176cfdSRui Paulo break;
109232176cfdSRui Paulo case IEEE80211_IOC_INACTIVITY:
109332176cfdSRui Paulo ireq->i_val = (vap->iv_flags_ext & IEEE80211_FEXT_INACT) != 0;
109432176cfdSRui Paulo break;
109532176cfdSRui Paulo case IEEE80211_IOC_APPIE:
109632176cfdSRui Paulo error = ieee80211_ioctl_getappie(vap, ireq);
109732176cfdSRui Paulo break;
109832176cfdSRui Paulo case IEEE80211_IOC_WPS:
109932176cfdSRui Paulo ireq->i_val = (vap->iv_flags_ext & IEEE80211_FEXT_WPS) != 0;
110032176cfdSRui Paulo break;
110132176cfdSRui Paulo case IEEE80211_IOC_TSN:
110232176cfdSRui Paulo ireq->i_val = (vap->iv_flags_ext & IEEE80211_FEXT_TSN) != 0;
110332176cfdSRui Paulo break;
110432176cfdSRui Paulo case IEEE80211_IOC_DFS:
110532176cfdSRui Paulo ireq->i_val = (vap->iv_flags_ext & IEEE80211_FEXT_DFS) != 0;
110632176cfdSRui Paulo break;
110732176cfdSRui Paulo case IEEE80211_IOC_DOTD:
110832176cfdSRui Paulo ireq->i_val = (vap->iv_flags_ext & IEEE80211_FEXT_DOTD) != 0;
110932176cfdSRui Paulo break;
111032176cfdSRui Paulo case IEEE80211_IOC_DEVCAPS:
111132176cfdSRui Paulo error = ieee80211_ioctl_getdevcaps(ic, ireq);
111232176cfdSRui Paulo break;
111332176cfdSRui Paulo case IEEE80211_IOC_HTPROTMODE:
111432176cfdSRui Paulo ireq->i_val = ic->ic_htprotmode;
111532176cfdSRui Paulo break;
111632176cfdSRui Paulo case IEEE80211_IOC_HTCONF:
111732176cfdSRui Paulo if (vap->iv_flags_ht & IEEE80211_FHT_HT) {
111832176cfdSRui Paulo ireq->i_val = 1;
111932176cfdSRui Paulo if (vap->iv_flags_ht & IEEE80211_FHT_USEHT40)
112032176cfdSRui Paulo ireq->i_val |= 2;
112132176cfdSRui Paulo } else
112232176cfdSRui Paulo ireq->i_val = 0;
112332176cfdSRui Paulo break;
112432176cfdSRui Paulo case IEEE80211_IOC_STA_VLAN:
112532176cfdSRui Paulo error = ieee80211_ioctl_getstavlan(vap, ireq);
112632176cfdSRui Paulo break;
112732176cfdSRui Paulo case IEEE80211_IOC_SMPS:
112832176cfdSRui Paulo if (vap->iv_opmode == IEEE80211_M_STA &&
1129d98a0bcfSMatthew Dillon (vap->iv_state == IEEE80211_S_RUN || vap->iv_state == IEEE80211_S_SLEEP)) {
113032176cfdSRui Paulo if (vap->iv_bss->ni_flags & IEEE80211_NODE_MIMO_RTS)
113132176cfdSRui Paulo ireq->i_val = IEEE80211_HTCAP_SMPS_DYNAMIC;
113232176cfdSRui Paulo else if (vap->iv_bss->ni_flags & IEEE80211_NODE_MIMO_PS)
113332176cfdSRui Paulo ireq->i_val = IEEE80211_HTCAP_SMPS_ENA;
113432176cfdSRui Paulo else
113532176cfdSRui Paulo ireq->i_val = IEEE80211_HTCAP_SMPS_OFF;
113632176cfdSRui Paulo } else
113732176cfdSRui Paulo ireq->i_val = vap->iv_htcaps & IEEE80211_HTCAP_SMPS;
113832176cfdSRui Paulo break;
113932176cfdSRui Paulo case IEEE80211_IOC_RIFS:
114032176cfdSRui Paulo if (vap->iv_opmode == IEEE80211_M_STA &&
1141d98a0bcfSMatthew Dillon (vap->iv_state == IEEE80211_S_RUN || vap->iv_state == IEEE80211_S_SLEEP))
114232176cfdSRui Paulo ireq->i_val =
114332176cfdSRui Paulo (vap->iv_bss->ni_flags & IEEE80211_NODE_RIFS) != 0;
114432176cfdSRui Paulo else
114532176cfdSRui Paulo ireq->i_val =
114632176cfdSRui Paulo (vap->iv_flags_ht & IEEE80211_FHT_RIFS) != 0;
1147c36e937bSSepherosa Ziehau break;
11484f655ef5SMatthew Dillon case IEEE80211_IOC_STBC:
11494f655ef5SMatthew Dillon ireq->i_val = 0;
11504f655ef5SMatthew Dillon if (vap->iv_flags_ht & IEEE80211_FHT_STBC_TX)
11514f655ef5SMatthew Dillon ireq->i_val |= 1;
11524f655ef5SMatthew Dillon if (vap->iv_flags_ht & IEEE80211_FHT_STBC_RX)
11534f655ef5SMatthew Dillon ireq->i_val |= 2;
11544f655ef5SMatthew Dillon break;
1155f186073cSJoerg Sonnenberger default:
115632176cfdSRui Paulo error = ieee80211_ioctl_getdefault(vap, ireq);
1157f186073cSJoerg Sonnenberger break;
1158f186073cSJoerg Sonnenberger }
1159841ab66cSSepherosa Ziehau return error;
116032176cfdSRui Paulo #undef MS
1161841ab66cSSepherosa Ziehau }
1162841ab66cSSepherosa Ziehau
11634f655ef5SMatthew Dillon static int
ieee80211_ioctl_setkey(struct ieee80211vap * vap,struct ieee80211req * ireq)116432176cfdSRui Paulo ieee80211_ioctl_setkey(struct ieee80211vap *vap, struct ieee80211req *ireq)
1165841ab66cSSepherosa Ziehau {
1166841ab66cSSepherosa Ziehau struct ieee80211req_key ik;
1167841ab66cSSepherosa Ziehau struct ieee80211_node *ni;
1168841ab66cSSepherosa Ziehau struct ieee80211_key *wk;
1169841ab66cSSepherosa Ziehau uint16_t kid;
117032176cfdSRui Paulo int error, i;
1171841ab66cSSepherosa Ziehau
1172841ab66cSSepherosa Ziehau if (ireq->i_len != sizeof(ik))
1173841ab66cSSepherosa Ziehau return EINVAL;
1174841ab66cSSepherosa Ziehau error = copyin(ireq->i_data, &ik, sizeof(ik));
1175f186073cSJoerg Sonnenberger if (error)
1176841ab66cSSepherosa Ziehau return error;
1177841ab66cSSepherosa Ziehau /* NB: cipher support is verified by ieee80211_crypt_newkey */
1178841ab66cSSepherosa Ziehau /* NB: this also checks ik->ik_keylen > sizeof(wk->wk_key) */
1179841ab66cSSepherosa Ziehau if (ik.ik_keylen > sizeof(ik.ik_keydata))
1180841ab66cSSepherosa Ziehau return E2BIG;
1181841ab66cSSepherosa Ziehau kid = ik.ik_keyix;
1182841ab66cSSepherosa Ziehau if (kid == IEEE80211_KEYIX_NONE) {
1183841ab66cSSepherosa Ziehau /* XXX unicast keys currently must be tx/rx */
1184841ab66cSSepherosa Ziehau if (ik.ik_flags != (IEEE80211_KEY_XMIT | IEEE80211_KEY_RECV))
1185841ab66cSSepherosa Ziehau return EINVAL;
118632176cfdSRui Paulo if (vap->iv_opmode == IEEE80211_M_STA) {
118732176cfdSRui Paulo ni = ieee80211_ref_node(vap->iv_bss);
1188841ab66cSSepherosa Ziehau if (!IEEE80211_ADDR_EQ(ik.ik_macaddr, ni->ni_bssid)) {
1189841ab66cSSepherosa Ziehau ieee80211_free_node(ni);
1190841ab66cSSepherosa Ziehau return EADDRNOTAVAIL;
1191841ab66cSSepherosa Ziehau }
1192841ab66cSSepherosa Ziehau } else {
119332176cfdSRui Paulo ni = ieee80211_find_vap_node(&vap->iv_ic->ic_sta, vap,
119432176cfdSRui Paulo ik.ik_macaddr);
1195841ab66cSSepherosa Ziehau if (ni == NULL)
1196841ab66cSSepherosa Ziehau return ENOENT;
1197841ab66cSSepherosa Ziehau }
1198841ab66cSSepherosa Ziehau wk = &ni->ni_ucastkey;
1199841ab66cSSepherosa Ziehau } else {
1200841ab66cSSepherosa Ziehau if (kid >= IEEE80211_WEP_NKID)
1201841ab66cSSepherosa Ziehau return EINVAL;
120232176cfdSRui Paulo wk = &vap->iv_nw_keys[kid];
1203841ab66cSSepherosa Ziehau /*
1204841ab66cSSepherosa Ziehau * Global slots start off w/o any assigned key index.
1205841ab66cSSepherosa Ziehau * Force one here for consistency with IEEE80211_IOC_WEPKEY.
1206841ab66cSSepherosa Ziehau */
1207841ab66cSSepherosa Ziehau if (wk->wk_keyix == IEEE80211_KEYIX_NONE)
1208841ab66cSSepherosa Ziehau wk->wk_keyix = kid;
1209841ab66cSSepherosa Ziehau ni = NULL;
1210841ab66cSSepherosa Ziehau }
1211841ab66cSSepherosa Ziehau error = 0;
121232176cfdSRui Paulo ieee80211_key_update_begin(vap);
121332176cfdSRui Paulo if (ieee80211_crypto_newkey(vap, ik.ik_type, ik.ik_flags, wk)) {
1214841ab66cSSepherosa Ziehau wk->wk_keylen = ik.ik_keylen;
1215841ab66cSSepherosa Ziehau /* NB: MIC presence is implied by cipher type */
1216841ab66cSSepherosa Ziehau if (wk->wk_keylen > IEEE80211_KEYBUF_SIZE)
1217841ab66cSSepherosa Ziehau wk->wk_keylen = IEEE80211_KEYBUF_SIZE;
121832176cfdSRui Paulo for (i = 0; i < IEEE80211_TID_SIZE; i++)
121932176cfdSRui Paulo wk->wk_keyrsc[i] = ik.ik_keyrsc;
1220841ab66cSSepherosa Ziehau wk->wk_keytsc = 0; /* new key, reset */
1221841ab66cSSepherosa Ziehau memset(wk->wk_key, 0, sizeof(wk->wk_key));
1222841ab66cSSepherosa Ziehau memcpy(wk->wk_key, ik.ik_keydata, ik.ik_keylen);
122332176cfdSRui Paulo IEEE80211_ADDR_COPY(wk->wk_macaddr,
122432176cfdSRui Paulo ni != NULL ? ni->ni_macaddr : ik.ik_macaddr);
122532176cfdSRui Paulo if (!ieee80211_crypto_setkey(vap, wk))
1226841ab66cSSepherosa Ziehau error = EIO;
1227841ab66cSSepherosa Ziehau else if ((ik.ik_flags & IEEE80211_KEY_DEFAULT))
122832176cfdSRui Paulo vap->iv_def_txkey = kid;
1229841ab66cSSepherosa Ziehau } else
1230841ab66cSSepherosa Ziehau error = ENXIO;
123132176cfdSRui Paulo ieee80211_key_update_end(vap);
1232841ab66cSSepherosa Ziehau if (ni != NULL)
1233841ab66cSSepherosa Ziehau ieee80211_free_node(ni);
1234841ab66cSSepherosa Ziehau return error;
1235841ab66cSSepherosa Ziehau }
1236841ab66cSSepherosa Ziehau
12374f655ef5SMatthew Dillon static int
ieee80211_ioctl_delkey(struct ieee80211vap * vap,struct ieee80211req * ireq)123832176cfdSRui Paulo ieee80211_ioctl_delkey(struct ieee80211vap *vap, struct ieee80211req *ireq)
1239841ab66cSSepherosa Ziehau {
1240841ab66cSSepherosa Ziehau struct ieee80211req_del_key dk;
1241841ab66cSSepherosa Ziehau int kid, error;
1242841ab66cSSepherosa Ziehau
1243841ab66cSSepherosa Ziehau if (ireq->i_len != sizeof(dk))
1244841ab66cSSepherosa Ziehau return EINVAL;
1245841ab66cSSepherosa Ziehau error = copyin(ireq->i_data, &dk, sizeof(dk));
1246841ab66cSSepherosa Ziehau if (error)
1247841ab66cSSepherosa Ziehau return error;
1248841ab66cSSepherosa Ziehau kid = dk.idk_keyix;
1249841ab66cSSepherosa Ziehau /* XXX uint8_t -> uint16_t */
1250841ab66cSSepherosa Ziehau if (dk.idk_keyix == (uint8_t) IEEE80211_KEYIX_NONE) {
1251841ab66cSSepherosa Ziehau struct ieee80211_node *ni;
1252841ab66cSSepherosa Ziehau
125332176cfdSRui Paulo if (vap->iv_opmode == IEEE80211_M_STA) {
125432176cfdSRui Paulo ni = ieee80211_ref_node(vap->iv_bss);
1255841ab66cSSepherosa Ziehau if (!IEEE80211_ADDR_EQ(dk.idk_macaddr, ni->ni_bssid)) {
1256841ab66cSSepherosa Ziehau ieee80211_free_node(ni);
1257841ab66cSSepherosa Ziehau return EADDRNOTAVAIL;
1258841ab66cSSepherosa Ziehau }
1259841ab66cSSepherosa Ziehau } else {
126032176cfdSRui Paulo ni = ieee80211_find_vap_node(&vap->iv_ic->ic_sta, vap,
126132176cfdSRui Paulo dk.idk_macaddr);
1262841ab66cSSepherosa Ziehau if (ni == NULL)
1263841ab66cSSepherosa Ziehau return ENOENT;
1264841ab66cSSepherosa Ziehau }
1265841ab66cSSepherosa Ziehau /* XXX error return */
1266841ab66cSSepherosa Ziehau ieee80211_node_delucastkey(ni);
1267841ab66cSSepherosa Ziehau ieee80211_free_node(ni);
1268841ab66cSSepherosa Ziehau } else {
1269841ab66cSSepherosa Ziehau if (kid >= IEEE80211_WEP_NKID)
1270841ab66cSSepherosa Ziehau return EINVAL;
1271841ab66cSSepherosa Ziehau /* XXX error return */
127232176cfdSRui Paulo ieee80211_crypto_delkey(vap, &vap->iv_nw_keys[kid]);
1273841ab66cSSepherosa Ziehau }
1274841ab66cSSepherosa Ziehau return 0;
1275841ab66cSSepherosa Ziehau }
1276841ab66cSSepherosa Ziehau
127732176cfdSRui Paulo struct mlmeop {
127832176cfdSRui Paulo struct ieee80211vap *vap;
127932176cfdSRui Paulo int op;
128032176cfdSRui Paulo int reason;
128132176cfdSRui Paulo };
128232176cfdSRui Paulo
128332176cfdSRui Paulo static void
mlmedebug(struct ieee80211vap * vap,const uint8_t mac[IEEE80211_ADDR_LEN],int op,int reason)128432176cfdSRui Paulo mlmedebug(struct ieee80211vap *vap, const uint8_t mac[IEEE80211_ADDR_LEN],
128532176cfdSRui Paulo int op, int reason)
128632176cfdSRui Paulo {
128732176cfdSRui Paulo #ifdef IEEE80211_DEBUG
128832176cfdSRui Paulo static const struct {
128932176cfdSRui Paulo int mask;
129032176cfdSRui Paulo const char *opstr;
129132176cfdSRui Paulo } ops[] = {
129232176cfdSRui Paulo { 0, "op#0" },
129332176cfdSRui Paulo { IEEE80211_MSG_IOCTL | IEEE80211_MSG_STATE |
129432176cfdSRui Paulo IEEE80211_MSG_ASSOC, "assoc" },
129532176cfdSRui Paulo { IEEE80211_MSG_IOCTL | IEEE80211_MSG_STATE |
129632176cfdSRui Paulo IEEE80211_MSG_ASSOC, "disassoc" },
129732176cfdSRui Paulo { IEEE80211_MSG_IOCTL | IEEE80211_MSG_STATE |
129832176cfdSRui Paulo IEEE80211_MSG_AUTH, "deauth" },
129932176cfdSRui Paulo { IEEE80211_MSG_IOCTL | IEEE80211_MSG_STATE |
130032176cfdSRui Paulo IEEE80211_MSG_AUTH, "authorize" },
130132176cfdSRui Paulo { IEEE80211_MSG_IOCTL | IEEE80211_MSG_STATE |
130232176cfdSRui Paulo IEEE80211_MSG_AUTH, "unauthorize" },
130332176cfdSRui Paulo };
130432176cfdSRui Paulo
130532176cfdSRui Paulo if (op == IEEE80211_MLME_AUTH) {
130632176cfdSRui Paulo IEEE80211_NOTE_MAC(vap, IEEE80211_MSG_IOCTL |
130732176cfdSRui Paulo IEEE80211_MSG_STATE | IEEE80211_MSG_AUTH, mac,
13084f655ef5SMatthew Dillon "station authenticate %s via MLME (reason: %d (%s))",
130932176cfdSRui Paulo reason == IEEE80211_STATUS_SUCCESS ? "ACCEPT" : "REJECT",
13104f655ef5SMatthew Dillon reason, ieee80211_reason_to_string(reason));
131132176cfdSRui Paulo } else if (!(IEEE80211_MLME_ASSOC <= op && op <= IEEE80211_MLME_AUTH)) {
131232176cfdSRui Paulo IEEE80211_NOTE_MAC(vap, IEEE80211_MSG_ANY, mac,
13134f655ef5SMatthew Dillon "unknown MLME request %d (reason: %d (%s))", op, reason,
13144f655ef5SMatthew Dillon ieee80211_reason_to_string(reason));
131532176cfdSRui Paulo } else if (reason == IEEE80211_STATUS_SUCCESS) {
131632176cfdSRui Paulo IEEE80211_NOTE_MAC(vap, ops[op].mask, mac,
131732176cfdSRui Paulo "station %s via MLME", ops[op].opstr);
131832176cfdSRui Paulo } else {
131932176cfdSRui Paulo IEEE80211_NOTE_MAC(vap, ops[op].mask, mac,
13204f655ef5SMatthew Dillon "station %s via MLME (reason: %d (%s))", ops[op].opstr,
13214f655ef5SMatthew Dillon reason, ieee80211_reason_to_string(reason));
132232176cfdSRui Paulo }
132332176cfdSRui Paulo #endif /* IEEE80211_DEBUG */
132432176cfdSRui Paulo }
132532176cfdSRui Paulo
1326841ab66cSSepherosa Ziehau static void
domlme(void * arg,struct ieee80211_node * ni)1327841ab66cSSepherosa Ziehau domlme(void *arg, struct ieee80211_node *ni)
1328841ab66cSSepherosa Ziehau {
132932176cfdSRui Paulo struct mlmeop *mop = arg;
133032176cfdSRui Paulo struct ieee80211vap *vap = ni->ni_vap;
1331841ab66cSSepherosa Ziehau
133232176cfdSRui Paulo if (vap != mop->vap)
133332176cfdSRui Paulo return;
133432176cfdSRui Paulo /*
133532176cfdSRui Paulo * NB: if ni_associd is zero then the node is already cleaned
133632176cfdSRui Paulo * up and we don't need to do this (we're safely holding a
133732176cfdSRui Paulo * reference but should otherwise not modify it's state).
133832176cfdSRui Paulo */
133932176cfdSRui Paulo if (ni->ni_associd == 0)
134032176cfdSRui Paulo return;
134132176cfdSRui Paulo mlmedebug(vap, ni->ni_macaddr, mop->op, mop->reason);
134232176cfdSRui Paulo if (mop->op == IEEE80211_MLME_DEAUTH) {
134332176cfdSRui Paulo IEEE80211_SEND_MGMT(ni, IEEE80211_FC0_SUBTYPE_DEAUTH,
134432176cfdSRui Paulo mop->reason);
134532176cfdSRui Paulo } else {
134632176cfdSRui Paulo IEEE80211_SEND_MGMT(ni, IEEE80211_FC0_SUBTYPE_DISASSOC,
134732176cfdSRui Paulo mop->reason);
1348841ab66cSSepherosa Ziehau }
134932176cfdSRui Paulo ieee80211_node_leave(ni);
1350841ab66cSSepherosa Ziehau }
1351841ab66cSSepherosa Ziehau
1352841ab66cSSepherosa Ziehau static int
setmlme_dropsta(struct ieee80211vap * vap,const uint8_t mac[IEEE80211_ADDR_LEN],struct mlmeop * mlmeop)135332176cfdSRui Paulo setmlme_dropsta(struct ieee80211vap *vap,
135432176cfdSRui Paulo const uint8_t mac[IEEE80211_ADDR_LEN], struct mlmeop *mlmeop)
135532176cfdSRui Paulo {
13564f655ef5SMatthew Dillon struct ieee80211_node_table *nt = &vap->iv_ic->ic_sta;
135732176cfdSRui Paulo struct ieee80211_node *ni;
135832176cfdSRui Paulo int error = 0;
135932176cfdSRui Paulo
136032176cfdSRui Paulo /* NB: the broadcast address means do 'em all */
13614f655ef5SMatthew Dillon if (!IEEE80211_ADDR_EQ(mac, vap->iv_ifp->if_broadcastaddr)) {
1362085ff963SMatthew Dillon IEEE80211_NODE_LOCK(nt);
136332176cfdSRui Paulo ni = ieee80211_find_node_locked(nt, mac);
1364085ff963SMatthew Dillon IEEE80211_NODE_UNLOCK(nt);
1365085ff963SMatthew Dillon /*
1366085ff963SMatthew Dillon * Don't do the node update inside the node
1367085ff963SMatthew Dillon * table lock. This unfortunately causes LORs
1368085ff963SMatthew Dillon * with drivers and their TX paths.
1369085ff963SMatthew Dillon */
137032176cfdSRui Paulo if (ni != NULL) {
137132176cfdSRui Paulo domlme(mlmeop, ni);
137232176cfdSRui Paulo ieee80211_free_node(ni);
137332176cfdSRui Paulo } else
137432176cfdSRui Paulo error = ENOENT;
137532176cfdSRui Paulo } else {
137632176cfdSRui Paulo ieee80211_iterate_nodes(nt, domlme, mlmeop);
137732176cfdSRui Paulo }
137832176cfdSRui Paulo return error;
137932176cfdSRui Paulo }
138032176cfdSRui Paulo
13814f655ef5SMatthew Dillon static int
setmlme_common(struct ieee80211vap * vap,int op,const uint8_t mac[IEEE80211_ADDR_LEN],int reason)138232176cfdSRui Paulo setmlme_common(struct ieee80211vap *vap, int op,
138332176cfdSRui Paulo const uint8_t mac[IEEE80211_ADDR_LEN], int reason)
138432176cfdSRui Paulo {
138532176cfdSRui Paulo struct ieee80211com *ic = vap->iv_ic;
138632176cfdSRui Paulo struct ieee80211_node_table *nt = &ic->ic_sta;
138732176cfdSRui Paulo struct ieee80211_node *ni;
138832176cfdSRui Paulo struct mlmeop mlmeop;
138932176cfdSRui Paulo int error;
139032176cfdSRui Paulo
139132176cfdSRui Paulo error = 0;
139232176cfdSRui Paulo switch (op) {
139332176cfdSRui Paulo case IEEE80211_MLME_DISASSOC:
139432176cfdSRui Paulo case IEEE80211_MLME_DEAUTH:
139532176cfdSRui Paulo switch (vap->iv_opmode) {
139632176cfdSRui Paulo case IEEE80211_M_STA:
139732176cfdSRui Paulo mlmedebug(vap, vap->iv_bss->ni_macaddr, op, reason);
139832176cfdSRui Paulo /* XXX not quite right */
139932176cfdSRui Paulo ieee80211_new_state(vap, IEEE80211_S_INIT, reason);
140032176cfdSRui Paulo break;
140132176cfdSRui Paulo case IEEE80211_M_HOSTAP:
140232176cfdSRui Paulo mlmeop.vap = vap;
140332176cfdSRui Paulo mlmeop.op = op;
140432176cfdSRui Paulo mlmeop.reason = reason;
140532176cfdSRui Paulo error = setmlme_dropsta(vap, mac, &mlmeop);
140632176cfdSRui Paulo break;
140732176cfdSRui Paulo case IEEE80211_M_WDS:
140832176cfdSRui Paulo /* XXX user app should send raw frame? */
140932176cfdSRui Paulo if (op != IEEE80211_MLME_DEAUTH) {
141032176cfdSRui Paulo error = EINVAL;
141132176cfdSRui Paulo break;
141232176cfdSRui Paulo }
141332176cfdSRui Paulo #if 0
141432176cfdSRui Paulo /* XXX accept any address, simplifies user code */
141532176cfdSRui Paulo if (!IEEE80211_ADDR_EQ(mac, vap->iv_bss->ni_macaddr)) {
141632176cfdSRui Paulo error = EINVAL;
141732176cfdSRui Paulo break;
141832176cfdSRui Paulo }
141932176cfdSRui Paulo #endif
142032176cfdSRui Paulo mlmedebug(vap, vap->iv_bss->ni_macaddr, op, reason);
142132176cfdSRui Paulo ni = ieee80211_ref_node(vap->iv_bss);
142232176cfdSRui Paulo IEEE80211_SEND_MGMT(ni,
142332176cfdSRui Paulo IEEE80211_FC0_SUBTYPE_DEAUTH, reason);
142432176cfdSRui Paulo ieee80211_free_node(ni);
142532176cfdSRui Paulo break;
1426085ff963SMatthew Dillon case IEEE80211_M_MBSS:
1427085ff963SMatthew Dillon IEEE80211_NODE_LOCK(nt);
1428085ff963SMatthew Dillon ni = ieee80211_find_node_locked(nt, mac);
1429085ff963SMatthew Dillon /*
1430085ff963SMatthew Dillon * Don't do the node update inside the node
1431085ff963SMatthew Dillon * table lock. This unfortunately causes LORs
1432085ff963SMatthew Dillon * with drivers and their TX paths.
1433085ff963SMatthew Dillon */
1434085ff963SMatthew Dillon IEEE80211_NODE_UNLOCK(nt);
1435085ff963SMatthew Dillon if (ni != NULL) {
1436085ff963SMatthew Dillon ieee80211_node_leave(ni);
1437085ff963SMatthew Dillon ieee80211_free_node(ni);
1438085ff963SMatthew Dillon } else {
1439085ff963SMatthew Dillon error = ENOENT;
1440085ff963SMatthew Dillon }
1441085ff963SMatthew Dillon break;
144232176cfdSRui Paulo default:
144332176cfdSRui Paulo error = EINVAL;
144432176cfdSRui Paulo break;
144532176cfdSRui Paulo }
144632176cfdSRui Paulo break;
144732176cfdSRui Paulo case IEEE80211_MLME_AUTHORIZE:
144832176cfdSRui Paulo case IEEE80211_MLME_UNAUTHORIZE:
144932176cfdSRui Paulo if (vap->iv_opmode != IEEE80211_M_HOSTAP &&
145032176cfdSRui Paulo vap->iv_opmode != IEEE80211_M_WDS) {
145132176cfdSRui Paulo error = EINVAL;
145232176cfdSRui Paulo break;
145332176cfdSRui Paulo }
1454085ff963SMatthew Dillon IEEE80211_NODE_LOCK(nt);
145532176cfdSRui Paulo ni = ieee80211_find_vap_node_locked(nt, vap, mac);
1456085ff963SMatthew Dillon /*
1457085ff963SMatthew Dillon * Don't do the node update inside the node
1458085ff963SMatthew Dillon * table lock. This unfortunately causes LORs
1459085ff963SMatthew Dillon * with drivers and their TX paths.
1460085ff963SMatthew Dillon */
1461085ff963SMatthew Dillon IEEE80211_NODE_UNLOCK(nt);
146232176cfdSRui Paulo if (ni != NULL) {
146332176cfdSRui Paulo mlmedebug(vap, mac, op, reason);
146432176cfdSRui Paulo if (op == IEEE80211_MLME_AUTHORIZE)
146532176cfdSRui Paulo ieee80211_node_authorize(ni);
146632176cfdSRui Paulo else
146732176cfdSRui Paulo ieee80211_node_unauthorize(ni);
146832176cfdSRui Paulo ieee80211_free_node(ni);
146932176cfdSRui Paulo } else
147032176cfdSRui Paulo error = ENOENT;
147132176cfdSRui Paulo break;
147232176cfdSRui Paulo case IEEE80211_MLME_AUTH:
147332176cfdSRui Paulo if (vap->iv_opmode != IEEE80211_M_HOSTAP) {
147432176cfdSRui Paulo error = EINVAL;
147532176cfdSRui Paulo break;
147632176cfdSRui Paulo }
1477085ff963SMatthew Dillon IEEE80211_NODE_LOCK(nt);
147832176cfdSRui Paulo ni = ieee80211_find_vap_node_locked(nt, vap, mac);
1479085ff963SMatthew Dillon /*
1480085ff963SMatthew Dillon * Don't do the node update inside the node
1481085ff963SMatthew Dillon * table lock. This unfortunately causes LORs
1482085ff963SMatthew Dillon * with drivers and their TX paths.
1483085ff963SMatthew Dillon */
1484085ff963SMatthew Dillon IEEE80211_NODE_UNLOCK(nt);
148532176cfdSRui Paulo if (ni != NULL) {
148632176cfdSRui Paulo mlmedebug(vap, mac, op, reason);
148732176cfdSRui Paulo if (reason == IEEE80211_STATUS_SUCCESS) {
148832176cfdSRui Paulo IEEE80211_SEND_MGMT(ni,
148932176cfdSRui Paulo IEEE80211_FC0_SUBTYPE_AUTH, 2);
149032176cfdSRui Paulo /*
149132176cfdSRui Paulo * For shared key auth, just continue the
149232176cfdSRui Paulo * exchange. Otherwise when 802.1x is not in
149332176cfdSRui Paulo * use mark the port authorized at this point
149432176cfdSRui Paulo * so traffic can flow.
149532176cfdSRui Paulo */
149632176cfdSRui Paulo if (ni->ni_authmode != IEEE80211_AUTH_8021X &&
149732176cfdSRui Paulo ni->ni_challenge == NULL)
149832176cfdSRui Paulo ieee80211_node_authorize(ni);
149932176cfdSRui Paulo } else {
150032176cfdSRui Paulo vap->iv_stats.is_rx_acl++;
150132176cfdSRui Paulo ieee80211_send_error(ni, ni->ni_macaddr,
150232176cfdSRui Paulo IEEE80211_FC0_SUBTYPE_AUTH, 2|(reason<<16));
150332176cfdSRui Paulo ieee80211_node_leave(ni);
150432176cfdSRui Paulo }
150532176cfdSRui Paulo ieee80211_free_node(ni);
150632176cfdSRui Paulo } else
150732176cfdSRui Paulo error = ENOENT;
150832176cfdSRui Paulo break;
150932176cfdSRui Paulo default:
151032176cfdSRui Paulo error = EINVAL;
151132176cfdSRui Paulo break;
151232176cfdSRui Paulo }
151332176cfdSRui Paulo return error;
151432176cfdSRui Paulo }
151532176cfdSRui Paulo
151632176cfdSRui Paulo struct scanlookup {
151732176cfdSRui Paulo const uint8_t *mac;
151832176cfdSRui Paulo int esslen;
151932176cfdSRui Paulo const uint8_t *essid;
152032176cfdSRui Paulo const struct ieee80211_scan_entry *se;
152132176cfdSRui Paulo };
152232176cfdSRui Paulo
152332176cfdSRui Paulo /*
152432176cfdSRui Paulo * Match mac address and any ssid.
152532176cfdSRui Paulo */
152632176cfdSRui Paulo static void
mlmelookup(void * arg,const struct ieee80211_scan_entry * se)152732176cfdSRui Paulo mlmelookup(void *arg, const struct ieee80211_scan_entry *se)
152832176cfdSRui Paulo {
152932176cfdSRui Paulo struct scanlookup *look = arg;
153032176cfdSRui Paulo
153132176cfdSRui Paulo if (!IEEE80211_ADDR_EQ(look->mac, se->se_macaddr))
153232176cfdSRui Paulo return;
153332176cfdSRui Paulo if (look->esslen != 0) {
153432176cfdSRui Paulo if (se->se_ssid[1] != look->esslen)
153532176cfdSRui Paulo return;
153632176cfdSRui Paulo if (memcmp(look->essid, se->se_ssid+2, look->esslen))
153732176cfdSRui Paulo return;
153832176cfdSRui Paulo }
153932176cfdSRui Paulo look->se = se;
154032176cfdSRui Paulo }
154132176cfdSRui Paulo
15424f655ef5SMatthew Dillon static int
setmlme_assoc_sta(struct ieee80211vap * vap,const uint8_t mac[IEEE80211_ADDR_LEN],int ssid_len,const uint8_t ssid[IEEE80211_NWID_LEN])1543085ff963SMatthew Dillon setmlme_assoc_sta(struct ieee80211vap *vap,
1544085ff963SMatthew Dillon const uint8_t mac[IEEE80211_ADDR_LEN], int ssid_len,
1545085ff963SMatthew Dillon const uint8_t ssid[IEEE80211_NWID_LEN])
154632176cfdSRui Paulo {
154732176cfdSRui Paulo struct scanlookup lookup;
154832176cfdSRui Paulo
1549085ff963SMatthew Dillon KASSERT(vap->iv_opmode == IEEE80211_M_STA,
1550085ff963SMatthew Dillon ("expected opmode STA not %s",
1551085ff963SMatthew Dillon ieee80211_opmode_name[vap->iv_opmode]));
155232176cfdSRui Paulo
155332176cfdSRui Paulo /* NB: this is racey if roaming is !manual */
155432176cfdSRui Paulo lookup.se = NULL;
155532176cfdSRui Paulo lookup.mac = mac;
155632176cfdSRui Paulo lookup.esslen = ssid_len;
155732176cfdSRui Paulo lookup.essid = ssid;
155832176cfdSRui Paulo ieee80211_scan_iterate(vap, mlmelookup, &lookup);
155932176cfdSRui Paulo if (lookup.se == NULL)
156032176cfdSRui Paulo return ENOENT;
156132176cfdSRui Paulo mlmedebug(vap, mac, IEEE80211_MLME_ASSOC, 0);
156232176cfdSRui Paulo if (!ieee80211_sta_join(vap, lookup.se->se_chan, lookup.se))
156332176cfdSRui Paulo return EIO; /* XXX unique but could be better */
156432176cfdSRui Paulo return 0;
156532176cfdSRui Paulo }
156632176cfdSRui Paulo
15674f655ef5SMatthew Dillon static int
setmlme_assoc_adhoc(struct ieee80211vap * vap,const uint8_t mac[IEEE80211_ADDR_LEN],int ssid_len,const uint8_t ssid[IEEE80211_NWID_LEN])1568085ff963SMatthew Dillon setmlme_assoc_adhoc(struct ieee80211vap *vap,
1569085ff963SMatthew Dillon const uint8_t mac[IEEE80211_ADDR_LEN], int ssid_len,
1570085ff963SMatthew Dillon const uint8_t ssid[IEEE80211_NWID_LEN])
1571085ff963SMatthew Dillon {
15724f655ef5SMatthew Dillon struct ieee80211_scan_req *sr;
15734f655ef5SMatthew Dillon int error;
1574085ff963SMatthew Dillon
1575085ff963SMatthew Dillon KASSERT(vap->iv_opmode == IEEE80211_M_IBSS ||
1576085ff963SMatthew Dillon vap->iv_opmode == IEEE80211_M_AHDEMO,
1577085ff963SMatthew Dillon ("expected opmode IBSS or AHDEMO not %s",
1578085ff963SMatthew Dillon ieee80211_opmode_name[vap->iv_opmode]));
1579085ff963SMatthew Dillon
1580085ff963SMatthew Dillon if (ssid_len == 0)
1581085ff963SMatthew Dillon return EINVAL;
1582085ff963SMatthew Dillon
15834f655ef5SMatthew Dillon #if defined(__DragonFly__)
15844f655ef5SMatthew Dillon sr = kmalloc(sizeof(*sr), M_TEMP, M_INTWAIT | M_ZERO);
15854f655ef5SMatthew Dillon #else
15864f655ef5SMatthew Dillon sr = IEEE80211_MALLOC(sizeof(*sr), M_TEMP,
15874f655ef5SMatthew Dillon IEEE80211_M_NOWAIT | IEEE80211_M_ZERO);
15884f655ef5SMatthew Dillon #endif
15894f655ef5SMatthew Dillon if (sr == NULL)
15904f655ef5SMatthew Dillon return ENOMEM;
15914f655ef5SMatthew Dillon
1592085ff963SMatthew Dillon /* NB: IEEE80211_IOC_SSID call missing for ap_scan=2. */
1593085ff963SMatthew Dillon memset(vap->iv_des_ssid[0].ssid, 0, IEEE80211_NWID_LEN);
1594085ff963SMatthew Dillon vap->iv_des_ssid[0].len = ssid_len;
1595085ff963SMatthew Dillon memcpy(vap->iv_des_ssid[0].ssid, ssid, ssid_len);
1596085ff963SMatthew Dillon vap->iv_des_nssid = 1;
1597085ff963SMatthew Dillon
15984f655ef5SMatthew Dillon sr->sr_flags = IEEE80211_IOC_SCAN_ACTIVE | IEEE80211_IOC_SCAN_ONCE;
15994f655ef5SMatthew Dillon sr->sr_duration = IEEE80211_IOC_SCAN_FOREVER;
16004f655ef5SMatthew Dillon memcpy(sr->sr_ssid[0].ssid, ssid, ssid_len);
16014f655ef5SMatthew Dillon sr->sr_ssid[0].len = ssid_len;
16024f655ef5SMatthew Dillon sr->sr_nssid = 1;
1603085ff963SMatthew Dillon
16044f655ef5SMatthew Dillon error = ieee80211_scanreq(vap, sr);
16054f655ef5SMatthew Dillon
16064f655ef5SMatthew Dillon IEEE80211_FREE(sr, M_TEMP);
16074f655ef5SMatthew Dillon return error;
1608085ff963SMatthew Dillon }
1609085ff963SMatthew Dillon
16104f655ef5SMatthew Dillon static int
ieee80211_ioctl_setmlme(struct ieee80211vap * vap,struct ieee80211req * ireq)161132176cfdSRui Paulo ieee80211_ioctl_setmlme(struct ieee80211vap *vap, struct ieee80211req *ireq)
1612841ab66cSSepherosa Ziehau {
1613841ab66cSSepherosa Ziehau struct ieee80211req_mlme mlme;
1614841ab66cSSepherosa Ziehau int error;
1615841ab66cSSepherosa Ziehau
1616841ab66cSSepherosa Ziehau if (ireq->i_len != sizeof(mlme))
1617841ab66cSSepherosa Ziehau return EINVAL;
1618841ab66cSSepherosa Ziehau error = copyin(ireq->i_data, &mlme, sizeof(mlme));
1619841ab66cSSepherosa Ziehau if (error)
1620841ab66cSSepherosa Ziehau return error;
1621085ff963SMatthew Dillon if (vap->iv_opmode == IEEE80211_M_STA &&
1622085ff963SMatthew Dillon mlme.im_op == IEEE80211_MLME_ASSOC)
1623085ff963SMatthew Dillon return setmlme_assoc_sta(vap, mlme.im_macaddr,
162432176cfdSRui Paulo vap->iv_des_ssid[0].len, vap->iv_des_ssid[0].ssid);
1625085ff963SMatthew Dillon else if ((vap->iv_opmode == IEEE80211_M_IBSS ||
1626085ff963SMatthew Dillon vap->iv_opmode == IEEE80211_M_AHDEMO) &&
1627085ff963SMatthew Dillon mlme.im_op == IEEE80211_MLME_ASSOC)
1628085ff963SMatthew Dillon return setmlme_assoc_adhoc(vap, mlme.im_macaddr,
1629085ff963SMatthew Dillon mlme.im_ssid_len, mlme.im_ssid);
1630841ab66cSSepherosa Ziehau else
163132176cfdSRui Paulo return setmlme_common(vap, mlme.im_op,
163232176cfdSRui Paulo mlme.im_macaddr, mlme.im_reason);
1633841ab66cSSepherosa Ziehau }
1634841ab66cSSepherosa Ziehau
16354f655ef5SMatthew Dillon static int
ieee80211_ioctl_macmac(struct ieee80211vap * vap,struct ieee80211req * ireq)163632176cfdSRui Paulo ieee80211_ioctl_macmac(struct ieee80211vap *vap, struct ieee80211req *ireq)
1637841ab66cSSepherosa Ziehau {
1638841ab66cSSepherosa Ziehau uint8_t mac[IEEE80211_ADDR_LEN];
163932176cfdSRui Paulo const struct ieee80211_aclator *acl = vap->iv_acl;
1640841ab66cSSepherosa Ziehau int error;
1641841ab66cSSepherosa Ziehau
1642841ab66cSSepherosa Ziehau if (ireq->i_len != sizeof(mac))
1643841ab66cSSepherosa Ziehau return EINVAL;
1644841ab66cSSepherosa Ziehau error = copyin(ireq->i_data, mac, ireq->i_len);
1645841ab66cSSepherosa Ziehau if (error)
1646841ab66cSSepherosa Ziehau return error;
1647841ab66cSSepherosa Ziehau if (acl == NULL) {
1648841ab66cSSepherosa Ziehau acl = ieee80211_aclator_get("mac");
164932176cfdSRui Paulo if (acl == NULL || !acl->iac_attach(vap))
1650841ab66cSSepherosa Ziehau return EINVAL;
165132176cfdSRui Paulo vap->iv_acl = acl;
1652841ab66cSSepherosa Ziehau }
1653841ab66cSSepherosa Ziehau if (ireq->i_type == IEEE80211_IOC_ADDMAC)
165432176cfdSRui Paulo acl->iac_add(vap, mac);
1655841ab66cSSepherosa Ziehau else
165632176cfdSRui Paulo acl->iac_remove(vap, mac);
1657841ab66cSSepherosa Ziehau return 0;
1658841ab66cSSepherosa Ziehau }
1659841ab66cSSepherosa Ziehau
16604f655ef5SMatthew Dillon static int
ieee80211_ioctl_setmaccmd(struct ieee80211vap * vap,struct ieee80211req * ireq)166132176cfdSRui Paulo ieee80211_ioctl_setmaccmd(struct ieee80211vap *vap, struct ieee80211req *ireq)
1662841ab66cSSepherosa Ziehau {
166332176cfdSRui Paulo const struct ieee80211_aclator *acl = vap->iv_acl;
1664841ab66cSSepherosa Ziehau
1665841ab66cSSepherosa Ziehau switch (ireq->i_val) {
1666841ab66cSSepherosa Ziehau case IEEE80211_MACCMD_POLICY_OPEN:
1667841ab66cSSepherosa Ziehau case IEEE80211_MACCMD_POLICY_ALLOW:
1668841ab66cSSepherosa Ziehau case IEEE80211_MACCMD_POLICY_DENY:
166932176cfdSRui Paulo case IEEE80211_MACCMD_POLICY_RADIUS:
1670841ab66cSSepherosa Ziehau if (acl == NULL) {
1671841ab66cSSepherosa Ziehau acl = ieee80211_aclator_get("mac");
167232176cfdSRui Paulo if (acl == NULL || !acl->iac_attach(vap))
1673841ab66cSSepherosa Ziehau return EINVAL;
167432176cfdSRui Paulo vap->iv_acl = acl;
1675841ab66cSSepherosa Ziehau }
167632176cfdSRui Paulo acl->iac_setpolicy(vap, ireq->i_val);
1677841ab66cSSepherosa Ziehau break;
1678841ab66cSSepherosa Ziehau case IEEE80211_MACCMD_FLUSH:
1679841ab66cSSepherosa Ziehau if (acl != NULL)
168032176cfdSRui Paulo acl->iac_flush(vap);
1681841ab66cSSepherosa Ziehau /* NB: silently ignore when not in use */
1682841ab66cSSepherosa Ziehau break;
1683841ab66cSSepherosa Ziehau case IEEE80211_MACCMD_DETACH:
1684841ab66cSSepherosa Ziehau if (acl != NULL) {
168532176cfdSRui Paulo vap->iv_acl = NULL;
168632176cfdSRui Paulo acl->iac_detach(vap);
1687841ab66cSSepherosa Ziehau }
1688841ab66cSSepherosa Ziehau break;
1689841ab66cSSepherosa Ziehau default:
1690841ab66cSSepherosa Ziehau if (acl == NULL)
1691841ab66cSSepherosa Ziehau return EINVAL;
1692841ab66cSSepherosa Ziehau else
169332176cfdSRui Paulo return acl->iac_setioctl(vap, ireq);
1694841ab66cSSepherosa Ziehau }
1695841ab66cSSepherosa Ziehau return 0;
1696841ab66cSSepherosa Ziehau }
1697841ab66cSSepherosa Ziehau
16984f655ef5SMatthew Dillon static int
ieee80211_ioctl_setchanlist(struct ieee80211vap * vap,struct ieee80211req * ireq)169932176cfdSRui Paulo ieee80211_ioctl_setchanlist(struct ieee80211vap *vap, struct ieee80211req *ireq)
1700841ab66cSSepherosa Ziehau {
170132176cfdSRui Paulo struct ieee80211com *ic = vap->iv_ic;
170232176cfdSRui Paulo uint8_t *chanlist, *list;
170332176cfdSRui Paulo int i, nchan, maxchan, error;
1704841ab66cSSepherosa Ziehau
170532176cfdSRui Paulo if (ireq->i_len > sizeof(ic->ic_chan_active))
170632176cfdSRui Paulo ireq->i_len = sizeof(ic->ic_chan_active);
17074f655ef5SMatthew Dillon #if defined(__DragonFly__)
170832176cfdSRui Paulo list = kmalloc(ireq->i_len + IEEE80211_CHAN_BYTES, M_TEMP,
1709fcaa651dSRui Paulo M_INTWAIT | M_ZERO);
17104f655ef5SMatthew Dillon #else
17114f655ef5SMatthew Dillon list = IEEE80211_MALLOC(ireq->i_len + IEEE80211_CHAN_BYTES, M_TEMP,
17124f655ef5SMatthew Dillon IEEE80211_M_NOWAIT | IEEE80211_M_ZERO);
17134f655ef5SMatthew Dillon #endif
171432176cfdSRui Paulo if (list == NULL)
171532176cfdSRui Paulo return ENOMEM;
171632176cfdSRui Paulo error = copyin(ireq->i_data, list, ireq->i_len);
171736e4ebd1SJoe Talbott if (error) {
17184f655ef5SMatthew Dillon IEEE80211_FREE(list, M_TEMP);
1719841ab66cSSepherosa Ziehau return error;
172036e4ebd1SJoe Talbott }
172132176cfdSRui Paulo nchan = 0;
172232176cfdSRui Paulo chanlist = list + ireq->i_len; /* NB: zero'd already */
172332176cfdSRui Paulo maxchan = ireq->i_len * NBBY;
172432176cfdSRui Paulo for (i = 0; i < ic->ic_nchans; i++) {
172532176cfdSRui Paulo const struct ieee80211_channel *c = &ic->ic_channels[i];
1726841ab66cSSepherosa Ziehau /*
172732176cfdSRui Paulo * Calculate the intersection of the user list and the
172832176cfdSRui Paulo * available channels so users can do things like specify
172932176cfdSRui Paulo * 1-255 to get all available channels.
1730841ab66cSSepherosa Ziehau */
173132176cfdSRui Paulo if (c->ic_ieee < maxchan && isset(list, c->ic_ieee)) {
173232176cfdSRui Paulo setbit(chanlist, c->ic_ieee);
173332176cfdSRui Paulo nchan++;
1734841ab66cSSepherosa Ziehau }
1735841ab66cSSepherosa Ziehau }
173636e4ebd1SJoe Talbott if (nchan == 0) {
17374f655ef5SMatthew Dillon IEEE80211_FREE(list, M_TEMP);
173832176cfdSRui Paulo return EINVAL;
173936e4ebd1SJoe Talbott }
174032176cfdSRui Paulo if (ic->ic_bsschan != IEEE80211_CHAN_ANYC && /* XXX */
174132176cfdSRui Paulo isclr(chanlist, ic->ic_bsschan->ic_ieee))
174232176cfdSRui Paulo ic->ic_bsschan = IEEE80211_CHAN_ANYC;
174332176cfdSRui Paulo memcpy(ic->ic_chan_active, chanlist, IEEE80211_CHAN_BYTES);
174432176cfdSRui Paulo ieee80211_scan_flush(vap);
17454f655ef5SMatthew Dillon IEEE80211_FREE(list, M_TEMP);
174632176cfdSRui Paulo return ENETRESET;
1747841ab66cSSepherosa Ziehau }
1748841ab66cSSepherosa Ziehau
17494f655ef5SMatthew Dillon static int
ieee80211_ioctl_setstastats(struct ieee80211vap * vap,struct ieee80211req * ireq)175032176cfdSRui Paulo ieee80211_ioctl_setstastats(struct ieee80211vap *vap, struct ieee80211req *ireq)
1751a63e49b9SSepherosa Ziehau {
1752a63e49b9SSepherosa Ziehau struct ieee80211_node *ni;
1753a63e49b9SSepherosa Ziehau uint8_t macaddr[IEEE80211_ADDR_LEN];
1754a63e49b9SSepherosa Ziehau int error;
1755a63e49b9SSepherosa Ziehau
1756a63e49b9SSepherosa Ziehau /*
1757a63e49b9SSepherosa Ziehau * NB: we could copyin ieee80211req_sta_stats so apps
1758a63e49b9SSepherosa Ziehau * could make selective changes but that's overkill;
1759a63e49b9SSepherosa Ziehau * just clear all stats for now.
1760a63e49b9SSepherosa Ziehau */
1761a63e49b9SSepherosa Ziehau if (ireq->i_len < IEEE80211_ADDR_LEN)
1762a63e49b9SSepherosa Ziehau return EINVAL;
1763a63e49b9SSepherosa Ziehau error = copyin(ireq->i_data, macaddr, IEEE80211_ADDR_LEN);
1764a63e49b9SSepherosa Ziehau if (error != 0)
1765a63e49b9SSepherosa Ziehau return error;
176632176cfdSRui Paulo ni = ieee80211_find_vap_node(&vap->iv_ic->ic_sta, vap, macaddr);
1767a63e49b9SSepherosa Ziehau if (ni == NULL)
176832176cfdSRui Paulo return ENOENT;
176932176cfdSRui Paulo /* XXX require ni_vap == vap? */
1770a63e49b9SSepherosa Ziehau memset(&ni->ni_stats, 0, sizeof(ni->ni_stats));
1771a63e49b9SSepherosa Ziehau ieee80211_free_node(ni);
1772a63e49b9SSepherosa Ziehau return 0;
1773a63e49b9SSepherosa Ziehau }
1774a63e49b9SSepherosa Ziehau
17754f655ef5SMatthew Dillon static int
ieee80211_ioctl_setstatxpow(struct ieee80211vap * vap,struct ieee80211req * ireq)177632176cfdSRui Paulo ieee80211_ioctl_setstatxpow(struct ieee80211vap *vap, struct ieee80211req *ireq)
1777841ab66cSSepherosa Ziehau {
1778841ab66cSSepherosa Ziehau struct ieee80211_node *ni;
1779841ab66cSSepherosa Ziehau struct ieee80211req_sta_txpow txpow;
1780841ab66cSSepherosa Ziehau int error;
1781841ab66cSSepherosa Ziehau
1782841ab66cSSepherosa Ziehau if (ireq->i_len != sizeof(txpow))
1783841ab66cSSepherosa Ziehau return EINVAL;
1784841ab66cSSepherosa Ziehau error = copyin(ireq->i_data, &txpow, sizeof(txpow));
1785841ab66cSSepherosa Ziehau if (error != 0)
1786841ab66cSSepherosa Ziehau return error;
178732176cfdSRui Paulo ni = ieee80211_find_vap_node(&vap->iv_ic->ic_sta, vap, txpow.it_macaddr);
1788841ab66cSSepherosa Ziehau if (ni == NULL)
178932176cfdSRui Paulo return ENOENT;
1790841ab66cSSepherosa Ziehau ni->ni_txpower = txpow.it_txpow;
1791841ab66cSSepherosa Ziehau ieee80211_free_node(ni);
1792841ab66cSSepherosa Ziehau return error;
1793841ab66cSSepherosa Ziehau }
1794841ab66cSSepherosa Ziehau
17954f655ef5SMatthew Dillon static int
ieee80211_ioctl_setwmeparam(struct ieee80211vap * vap,struct ieee80211req * ireq)179632176cfdSRui Paulo ieee80211_ioctl_setwmeparam(struct ieee80211vap *vap, struct ieee80211req *ireq)
1797841ab66cSSepherosa Ziehau {
179832176cfdSRui Paulo struct ieee80211com *ic = vap->iv_ic;
1799841ab66cSSepherosa Ziehau struct ieee80211_wme_state *wme = &ic->ic_wme;
1800841ab66cSSepherosa Ziehau struct wmeParams *wmep, *chanp;
18014f655ef5SMatthew Dillon int isbss, ac, aggrmode;
1802841ab66cSSepherosa Ziehau
1803841ab66cSSepherosa Ziehau if ((ic->ic_caps & IEEE80211_C_WME) == 0)
180432176cfdSRui Paulo return EOPNOTSUPP;
1805841ab66cSSepherosa Ziehau
1806841ab66cSSepherosa Ziehau isbss = (ireq->i_len & IEEE80211_WMEPARAM_BSS);
1807841ab66cSSepherosa Ziehau ac = (ireq->i_len & IEEE80211_WMEPARAM_VAL);
18084f655ef5SMatthew Dillon aggrmode = (wme->wme_flags & WME_F_AGGRMODE);
1809841ab66cSSepherosa Ziehau if (ac >= WME_NUM_AC)
1810841ab66cSSepherosa Ziehau ac = WME_AC_BE;
1811841ab66cSSepherosa Ziehau if (isbss) {
1812841ab66cSSepherosa Ziehau chanp = &wme->wme_bssChanParams.cap_wmeParams[ac];
1813841ab66cSSepherosa Ziehau wmep = &wme->wme_wmeBssChanParams.cap_wmeParams[ac];
1814841ab66cSSepherosa Ziehau } else {
1815841ab66cSSepherosa Ziehau chanp = &wme->wme_chanParams.cap_wmeParams[ac];
1816841ab66cSSepherosa Ziehau wmep = &wme->wme_wmeChanParams.cap_wmeParams[ac];
1817841ab66cSSepherosa Ziehau }
1818841ab66cSSepherosa Ziehau switch (ireq->i_type) {
1819841ab66cSSepherosa Ziehau case IEEE80211_IOC_WME_CWMIN: /* WME: CWmin */
1820841ab66cSSepherosa Ziehau wmep->wmep_logcwmin = ireq->i_val;
18214f655ef5SMatthew Dillon if (!isbss || !aggrmode)
1822841ab66cSSepherosa Ziehau chanp->wmep_logcwmin = ireq->i_val;
1823841ab66cSSepherosa Ziehau break;
1824841ab66cSSepherosa Ziehau case IEEE80211_IOC_WME_CWMAX: /* WME: CWmax */
1825841ab66cSSepherosa Ziehau wmep->wmep_logcwmax = ireq->i_val;
18264f655ef5SMatthew Dillon if (!isbss || !aggrmode)
1827841ab66cSSepherosa Ziehau chanp->wmep_logcwmax = ireq->i_val;
1828841ab66cSSepherosa Ziehau break;
1829841ab66cSSepherosa Ziehau case IEEE80211_IOC_WME_AIFS: /* WME: AIFS */
1830841ab66cSSepherosa Ziehau wmep->wmep_aifsn = ireq->i_val;
18314f655ef5SMatthew Dillon if (!isbss || !aggrmode)
1832841ab66cSSepherosa Ziehau chanp->wmep_aifsn = ireq->i_val;
1833841ab66cSSepherosa Ziehau break;
1834841ab66cSSepherosa Ziehau case IEEE80211_IOC_WME_TXOPLIMIT: /* WME: txops limit */
1835841ab66cSSepherosa Ziehau wmep->wmep_txopLimit = ireq->i_val;
18364f655ef5SMatthew Dillon if (!isbss || !aggrmode)
1837841ab66cSSepherosa Ziehau chanp->wmep_txopLimit = ireq->i_val;
1838841ab66cSSepherosa Ziehau break;
1839841ab66cSSepherosa Ziehau case IEEE80211_IOC_WME_ACM: /* WME: ACM (bss only) */
1840841ab66cSSepherosa Ziehau wmep->wmep_acm = ireq->i_val;
18414f655ef5SMatthew Dillon if (!aggrmode)
1842841ab66cSSepherosa Ziehau chanp->wmep_acm = ireq->i_val;
1843841ab66cSSepherosa Ziehau break;
1844841ab66cSSepherosa Ziehau case IEEE80211_IOC_WME_ACKPOLICY: /* WME: ACK policy (!bss only)*/
1845841ab66cSSepherosa Ziehau wmep->wmep_noackPolicy = chanp->wmep_noackPolicy =
1846841ab66cSSepherosa Ziehau (ireq->i_val) == 0;
1847841ab66cSSepherosa Ziehau break;
1848841ab66cSSepherosa Ziehau }
184932176cfdSRui Paulo ieee80211_wme_updateparams(vap);
1850841ab66cSSepherosa Ziehau return 0;
1851841ab66cSSepherosa Ziehau }
1852841ab66cSSepherosa Ziehau
1853841ab66cSSepherosa Ziehau static int
find11gchannel(struct ieee80211com * ic,int start,int freq)185432176cfdSRui Paulo find11gchannel(struct ieee80211com *ic, int start, int freq)
1855841ab66cSSepherosa Ziehau {
185632176cfdSRui Paulo const struct ieee80211_channel *c;
185732176cfdSRui Paulo int i;
185832176cfdSRui Paulo
185932176cfdSRui Paulo for (i = start+1; i < ic->ic_nchans; i++) {
186032176cfdSRui Paulo c = &ic->ic_channels[i];
186132176cfdSRui Paulo if (c->ic_freq == freq && IEEE80211_IS_CHAN_ANYG(c))
186232176cfdSRui Paulo return 1;
186332176cfdSRui Paulo }
186432176cfdSRui Paulo /* NB: should not be needed but in case things are mis-sorted */
186532176cfdSRui Paulo for (i = 0; i < start; i++) {
186632176cfdSRui Paulo c = &ic->ic_channels[i];
186732176cfdSRui Paulo if (c->ic_freq == freq && IEEE80211_IS_CHAN_ANYG(c))
186832176cfdSRui Paulo return 1;
1869841ab66cSSepherosa Ziehau }
1870841ab66cSSepherosa Ziehau return 0;
1871841ab66cSSepherosa Ziehau }
1872841ab66cSSepherosa Ziehau
187332176cfdSRui Paulo static struct ieee80211_channel *
findchannel(struct ieee80211com * ic,int ieee,int mode)187432176cfdSRui Paulo findchannel(struct ieee80211com *ic, int ieee, int mode)
1875841ab66cSSepherosa Ziehau {
187632176cfdSRui Paulo static const u_int chanflags[IEEE80211_MODE_MAX] = {
187732176cfdSRui Paulo [IEEE80211_MODE_AUTO] = 0,
187832176cfdSRui Paulo [IEEE80211_MODE_11A] = IEEE80211_CHAN_A,
187932176cfdSRui Paulo [IEEE80211_MODE_11B] = IEEE80211_CHAN_B,
188032176cfdSRui Paulo [IEEE80211_MODE_11G] = IEEE80211_CHAN_G,
188132176cfdSRui Paulo [IEEE80211_MODE_FH] = IEEE80211_CHAN_FHSS,
188232176cfdSRui Paulo [IEEE80211_MODE_TURBO_A] = IEEE80211_CHAN_108A,
188332176cfdSRui Paulo [IEEE80211_MODE_TURBO_G] = IEEE80211_CHAN_108G,
188432176cfdSRui Paulo [IEEE80211_MODE_STURBO_A] = IEEE80211_CHAN_STURBO,
188532176cfdSRui Paulo [IEEE80211_MODE_HALF] = IEEE80211_CHAN_HALF,
188632176cfdSRui Paulo [IEEE80211_MODE_QUARTER] = IEEE80211_CHAN_QUARTER,
188732176cfdSRui Paulo /* NB: handled specially below */
188832176cfdSRui Paulo [IEEE80211_MODE_11NA] = IEEE80211_CHAN_A,
188932176cfdSRui Paulo [IEEE80211_MODE_11NG] = IEEE80211_CHAN_G,
189032176cfdSRui Paulo };
189132176cfdSRui Paulo u_int modeflags;
189232176cfdSRui Paulo int i;
189332176cfdSRui Paulo
189432176cfdSRui Paulo modeflags = chanflags[mode];
189532176cfdSRui Paulo for (i = 0; i < ic->ic_nchans; i++) {
189632176cfdSRui Paulo struct ieee80211_channel *c = &ic->ic_channels[i];
189732176cfdSRui Paulo
189832176cfdSRui Paulo if (c->ic_ieee != ieee)
189932176cfdSRui Paulo continue;
190032176cfdSRui Paulo if (mode == IEEE80211_MODE_AUTO) {
190132176cfdSRui Paulo /* ignore turbo channels for autoselect */
190232176cfdSRui Paulo if (IEEE80211_IS_CHAN_TURBO(c))
190332176cfdSRui Paulo continue;
190432176cfdSRui Paulo /*
190532176cfdSRui Paulo * XXX special-case 11b/g channels so we
190632176cfdSRui Paulo * always select the g channel if both
190732176cfdSRui Paulo * are present.
190832176cfdSRui Paulo * XXX prefer HT to non-HT?
190932176cfdSRui Paulo */
191032176cfdSRui Paulo if (!IEEE80211_IS_CHAN_B(c) ||
191132176cfdSRui Paulo !find11gchannel(ic, i, c->ic_freq))
191232176cfdSRui Paulo return c;
191332176cfdSRui Paulo } else {
191432176cfdSRui Paulo /* must check HT specially */
191532176cfdSRui Paulo if ((mode == IEEE80211_MODE_11NA ||
191632176cfdSRui Paulo mode == IEEE80211_MODE_11NG) &&
191732176cfdSRui Paulo !IEEE80211_IS_CHAN_HT(c))
191832176cfdSRui Paulo continue;
191932176cfdSRui Paulo if ((c->ic_flags & modeflags) == modeflags)
192032176cfdSRui Paulo return c;
192132176cfdSRui Paulo }
192232176cfdSRui Paulo }
192332176cfdSRui Paulo return NULL;
192432176cfdSRui Paulo }
192532176cfdSRui Paulo
192632176cfdSRui Paulo /*
192732176cfdSRui Paulo * Check the specified against any desired mode (aka netband).
192832176cfdSRui Paulo * This is only used (presently) when operating in hostap mode
192932176cfdSRui Paulo * to enforce consistency.
193032176cfdSRui Paulo */
193132176cfdSRui Paulo static int
check_mode_consistency(const struct ieee80211_channel * c,int mode)193232176cfdSRui Paulo check_mode_consistency(const struct ieee80211_channel *c, int mode)
193332176cfdSRui Paulo {
193432176cfdSRui Paulo KASSERT(c != IEEE80211_CHAN_ANYC, ("oops, no channel"));
193532176cfdSRui Paulo
193632176cfdSRui Paulo switch (mode) {
193732176cfdSRui Paulo case IEEE80211_MODE_11B:
193832176cfdSRui Paulo return (IEEE80211_IS_CHAN_B(c));
193932176cfdSRui Paulo case IEEE80211_MODE_11G:
194032176cfdSRui Paulo return (IEEE80211_IS_CHAN_ANYG(c) && !IEEE80211_IS_CHAN_HT(c));
194132176cfdSRui Paulo case IEEE80211_MODE_11A:
194232176cfdSRui Paulo return (IEEE80211_IS_CHAN_A(c) && !IEEE80211_IS_CHAN_HT(c));
194332176cfdSRui Paulo case IEEE80211_MODE_STURBO_A:
194432176cfdSRui Paulo return (IEEE80211_IS_CHAN_STURBO(c));
194532176cfdSRui Paulo case IEEE80211_MODE_11NA:
194632176cfdSRui Paulo return (IEEE80211_IS_CHAN_HTA(c));
194732176cfdSRui Paulo case IEEE80211_MODE_11NG:
194832176cfdSRui Paulo return (IEEE80211_IS_CHAN_HTG(c));
194932176cfdSRui Paulo }
195032176cfdSRui Paulo return 1;
195132176cfdSRui Paulo
195232176cfdSRui Paulo }
195332176cfdSRui Paulo
195432176cfdSRui Paulo /*
195532176cfdSRui Paulo * Common code to set the current channel. If the device
195632176cfdSRui Paulo * is up and running this may result in an immediate channel
195732176cfdSRui Paulo * change or a kick of the state machine.
195832176cfdSRui Paulo */
195932176cfdSRui Paulo static int
setcurchan(struct ieee80211vap * vap,struct ieee80211_channel * c)196032176cfdSRui Paulo setcurchan(struct ieee80211vap *vap, struct ieee80211_channel *c)
196132176cfdSRui Paulo {
196232176cfdSRui Paulo struct ieee80211com *ic = vap->iv_ic;
196332176cfdSRui Paulo int error;
196432176cfdSRui Paulo
196532176cfdSRui Paulo if (c != IEEE80211_CHAN_ANYC) {
196632176cfdSRui Paulo if (IEEE80211_IS_CHAN_RADAR(c))
196732176cfdSRui Paulo return EBUSY; /* XXX better code? */
196832176cfdSRui Paulo if (vap->iv_opmode == IEEE80211_M_HOSTAP) {
196932176cfdSRui Paulo if (IEEE80211_IS_CHAN_NOHOSTAP(c))
197032176cfdSRui Paulo return EINVAL;
197132176cfdSRui Paulo if (!check_mode_consistency(c, vap->iv_des_mode))
197232176cfdSRui Paulo return EINVAL;
197332176cfdSRui Paulo } else if (vap->iv_opmode == IEEE80211_M_IBSS) {
197432176cfdSRui Paulo if (IEEE80211_IS_CHAN_NOADHOC(c))
197532176cfdSRui Paulo return EINVAL;
197632176cfdSRui Paulo }
1977d98a0bcfSMatthew Dillon if ((vap->iv_state == IEEE80211_S_RUN || vap->iv_state == IEEE80211_S_SLEEP) &&
197832176cfdSRui Paulo vap->iv_bss->ni_chan == c)
197932176cfdSRui Paulo return 0; /* NB: nothing to do */
198032176cfdSRui Paulo }
198132176cfdSRui Paulo vap->iv_des_chan = c;
198232176cfdSRui Paulo
198332176cfdSRui Paulo error = 0;
198432176cfdSRui Paulo if (vap->iv_opmode == IEEE80211_M_MONITOR &&
198532176cfdSRui Paulo vap->iv_des_chan != IEEE80211_CHAN_ANYC) {
198632176cfdSRui Paulo /*
198732176cfdSRui Paulo * Monitor mode can switch directly.
198832176cfdSRui Paulo */
198932176cfdSRui Paulo if (IFNET_IS_UP_RUNNING(vap->iv_ifp)) {
199032176cfdSRui Paulo /* XXX need state machine for other vap's to follow */
199132176cfdSRui Paulo ieee80211_setcurchan(ic, vap->iv_des_chan);
199232176cfdSRui Paulo vap->iv_bss->ni_chan = ic->ic_curchan;
1993c69e37d6SSascha Wildner } else {
199432176cfdSRui Paulo ic->ic_curchan = vap->iv_des_chan;
199532176cfdSRui Paulo ic->ic_rt = ieee80211_get_ratetable(ic->ic_curchan);
1996c69e37d6SSascha Wildner }
199732176cfdSRui Paulo } else {
199832176cfdSRui Paulo /*
199932176cfdSRui Paulo * Need to go through the state machine in case we
200032176cfdSRui Paulo * need to reassociate or the like. The state machine
200132176cfdSRui Paulo * will pickup the desired channel and avoid scanning.
200232176cfdSRui Paulo */
200332176cfdSRui Paulo if (IS_UP_AUTO(vap))
200432176cfdSRui Paulo ieee80211_new_state(vap, IEEE80211_S_SCAN, 0);
200532176cfdSRui Paulo else if (vap->iv_des_chan != IEEE80211_CHAN_ANYC) {
200632176cfdSRui Paulo /*
200732176cfdSRui Paulo * When not up+running and a real channel has
200832176cfdSRui Paulo * been specified fix the current channel so
200932176cfdSRui Paulo * there is immediate feedback; e.g. via ifconfig.
201032176cfdSRui Paulo */
201132176cfdSRui Paulo ic->ic_curchan = vap->iv_des_chan;
201232176cfdSRui Paulo ic->ic_rt = ieee80211_get_ratetable(ic->ic_curchan);
201332176cfdSRui Paulo }
201432176cfdSRui Paulo }
201532176cfdSRui Paulo return error;
201632176cfdSRui Paulo }
201732176cfdSRui Paulo
201832176cfdSRui Paulo /*
201932176cfdSRui Paulo * Old api for setting the current channel; this is
202032176cfdSRui Paulo * deprecated because channel numbers are ambiguous.
202132176cfdSRui Paulo */
20224f655ef5SMatthew Dillon static int
ieee80211_ioctl_setchannel(struct ieee80211vap * vap,const struct ieee80211req * ireq)202332176cfdSRui Paulo ieee80211_ioctl_setchannel(struct ieee80211vap *vap,
202432176cfdSRui Paulo const struct ieee80211req *ireq)
202532176cfdSRui Paulo {
202632176cfdSRui Paulo struct ieee80211com *ic = vap->iv_ic;
202732176cfdSRui Paulo struct ieee80211_channel *c;
202832176cfdSRui Paulo
202932176cfdSRui Paulo /* XXX 0xffff overflows 16-bit signed */
203032176cfdSRui Paulo if (ireq->i_val == 0 ||
203132176cfdSRui Paulo ireq->i_val == (int16_t) IEEE80211_CHAN_ANY) {
203232176cfdSRui Paulo c = IEEE80211_CHAN_ANYC;
203332176cfdSRui Paulo } else {
203432176cfdSRui Paulo struct ieee80211_channel *c2;
203532176cfdSRui Paulo
203632176cfdSRui Paulo c = findchannel(ic, ireq->i_val, vap->iv_des_mode);
203732176cfdSRui Paulo if (c == NULL) {
203832176cfdSRui Paulo c = findchannel(ic, ireq->i_val,
203932176cfdSRui Paulo IEEE80211_MODE_AUTO);
204032176cfdSRui Paulo if (c == NULL)
204132176cfdSRui Paulo return EINVAL;
204232176cfdSRui Paulo }
204332176cfdSRui Paulo /*
204432176cfdSRui Paulo * Fine tune channel selection based on desired mode:
204532176cfdSRui Paulo * if 11b is requested, find the 11b version of any
204632176cfdSRui Paulo * 11g channel returned,
204732176cfdSRui Paulo * if static turbo, find the turbo version of any
204832176cfdSRui Paulo * 11a channel return,
204932176cfdSRui Paulo * if 11na is requested, find the ht version of any
205032176cfdSRui Paulo * 11a channel returned,
205132176cfdSRui Paulo * if 11ng is requested, find the ht version of any
205232176cfdSRui Paulo * 11g channel returned,
205332176cfdSRui Paulo * otherwise we should be ok with what we've got.
205432176cfdSRui Paulo */
205532176cfdSRui Paulo switch (vap->iv_des_mode) {
205632176cfdSRui Paulo case IEEE80211_MODE_11B:
205732176cfdSRui Paulo if (IEEE80211_IS_CHAN_ANYG(c)) {
205832176cfdSRui Paulo c2 = findchannel(ic, ireq->i_val,
205932176cfdSRui Paulo IEEE80211_MODE_11B);
206032176cfdSRui Paulo /* NB: should not happen, =>'s 11g w/o 11b */
206132176cfdSRui Paulo if (c2 != NULL)
206232176cfdSRui Paulo c = c2;
206332176cfdSRui Paulo }
206432176cfdSRui Paulo break;
206532176cfdSRui Paulo case IEEE80211_MODE_TURBO_A:
206632176cfdSRui Paulo if (IEEE80211_IS_CHAN_A(c)) {
206732176cfdSRui Paulo c2 = findchannel(ic, ireq->i_val,
206832176cfdSRui Paulo IEEE80211_MODE_TURBO_A);
206932176cfdSRui Paulo if (c2 != NULL)
207032176cfdSRui Paulo c = c2;
207132176cfdSRui Paulo }
207232176cfdSRui Paulo break;
207332176cfdSRui Paulo case IEEE80211_MODE_11NA:
207432176cfdSRui Paulo if (IEEE80211_IS_CHAN_A(c)) {
207532176cfdSRui Paulo c2 = findchannel(ic, ireq->i_val,
207632176cfdSRui Paulo IEEE80211_MODE_11NA);
207732176cfdSRui Paulo if (c2 != NULL)
207832176cfdSRui Paulo c = c2;
207932176cfdSRui Paulo }
208032176cfdSRui Paulo break;
208132176cfdSRui Paulo case IEEE80211_MODE_11NG:
208232176cfdSRui Paulo if (IEEE80211_IS_CHAN_ANYG(c)) {
208332176cfdSRui Paulo c2 = findchannel(ic, ireq->i_val,
208432176cfdSRui Paulo IEEE80211_MODE_11NG);
208532176cfdSRui Paulo if (c2 != NULL)
208632176cfdSRui Paulo c = c2;
208732176cfdSRui Paulo }
208832176cfdSRui Paulo break;
208932176cfdSRui Paulo default: /* NB: no static turboG */
209032176cfdSRui Paulo break;
209132176cfdSRui Paulo }
209232176cfdSRui Paulo }
209332176cfdSRui Paulo return setcurchan(vap, c);
209432176cfdSRui Paulo }
209532176cfdSRui Paulo
209632176cfdSRui Paulo /*
209732176cfdSRui Paulo * New/current api for setting the current channel; a complete
209832176cfdSRui Paulo * channel description is provide so there is no ambiguity in
209932176cfdSRui Paulo * identifying the channel.
210032176cfdSRui Paulo */
21014f655ef5SMatthew Dillon static int
ieee80211_ioctl_setcurchan(struct ieee80211vap * vap,const struct ieee80211req * ireq)210232176cfdSRui Paulo ieee80211_ioctl_setcurchan(struct ieee80211vap *vap,
210332176cfdSRui Paulo const struct ieee80211req *ireq)
210432176cfdSRui Paulo {
210532176cfdSRui Paulo struct ieee80211com *ic = vap->iv_ic;
210632176cfdSRui Paulo struct ieee80211_channel chan, *c;
210732176cfdSRui Paulo int error;
210832176cfdSRui Paulo
210932176cfdSRui Paulo if (ireq->i_len != sizeof(chan))
211032176cfdSRui Paulo return EINVAL;
211132176cfdSRui Paulo error = copyin(ireq->i_data, &chan, sizeof(chan));
211232176cfdSRui Paulo if (error != 0)
211332176cfdSRui Paulo return error;
211432176cfdSRui Paulo /* XXX 0xffff overflows 16-bit signed */
211532176cfdSRui Paulo if (chan.ic_freq == 0 || chan.ic_freq == IEEE80211_CHAN_ANY) {
211632176cfdSRui Paulo c = IEEE80211_CHAN_ANYC;
211732176cfdSRui Paulo } else {
211832176cfdSRui Paulo c = ieee80211_find_channel(ic, chan.ic_freq, chan.ic_flags);
211932176cfdSRui Paulo if (c == NULL)
212032176cfdSRui Paulo return EINVAL;
212132176cfdSRui Paulo }
212232176cfdSRui Paulo return setcurchan(vap, c);
212332176cfdSRui Paulo }
212432176cfdSRui Paulo
21254f655ef5SMatthew Dillon static int
ieee80211_ioctl_setregdomain(struct ieee80211vap * vap,const struct ieee80211req * ireq)212632176cfdSRui Paulo ieee80211_ioctl_setregdomain(struct ieee80211vap *vap,
212732176cfdSRui Paulo const struct ieee80211req *ireq)
212832176cfdSRui Paulo {
212932176cfdSRui Paulo struct ieee80211_regdomain_req *reg;
213032176cfdSRui Paulo int nchans, error;
213132176cfdSRui Paulo
213232176cfdSRui Paulo nchans = 1 + ((ireq->i_len - sizeof(struct ieee80211_regdomain_req)) /
213332176cfdSRui Paulo sizeof(struct ieee80211_channel));
213432176cfdSRui Paulo if (!(1 <= nchans && nchans <= IEEE80211_CHAN_MAX)) {
213532176cfdSRui Paulo IEEE80211_DPRINTF(vap, IEEE80211_MSG_IOCTL,
213632176cfdSRui Paulo "%s: bad # chans, i_len %d nchans %d\n", __func__,
213732176cfdSRui Paulo ireq->i_len, nchans);
213832176cfdSRui Paulo return EINVAL;
213932176cfdSRui Paulo }
21404f655ef5SMatthew Dillon #if defined(__DragonFly__)
214132176cfdSRui Paulo reg = (struct ieee80211_regdomain_req *)
2142fcaa651dSRui Paulo kmalloc(IEEE80211_REGDOMAIN_SIZE(nchans), M_TEMP, M_INTWAIT);
21434f655ef5SMatthew Dillon #else
21444f655ef5SMatthew Dillon reg = (struct ieee80211_regdomain_req *)
21454f655ef5SMatthew Dillon IEEE80211_MALLOC(IEEE80211_REGDOMAIN_SIZE(nchans), M_TEMP,
21464f655ef5SMatthew Dillon IEEE80211_M_NOWAIT | IEEE80211_M_ZERO);
21474f655ef5SMatthew Dillon #endif
214832176cfdSRui Paulo if (reg == NULL) {
214932176cfdSRui Paulo IEEE80211_DPRINTF(vap, IEEE80211_MSG_IOCTL,
215032176cfdSRui Paulo "%s: no memory, nchans %d\n", __func__, nchans);
215132176cfdSRui Paulo return ENOMEM;
215232176cfdSRui Paulo }
215332176cfdSRui Paulo error = copyin(ireq->i_data, reg, IEEE80211_REGDOMAIN_SIZE(nchans));
215432176cfdSRui Paulo if (error == 0) {
215532176cfdSRui Paulo /* NB: validate inline channel count against storage size */
215632176cfdSRui Paulo if (reg->chaninfo.ic_nchans != nchans) {
215732176cfdSRui Paulo IEEE80211_DPRINTF(vap, IEEE80211_MSG_IOCTL,
215832176cfdSRui Paulo "%s: chan cnt mismatch, %d != %d\n", __func__,
215932176cfdSRui Paulo reg->chaninfo.ic_nchans, nchans);
216032176cfdSRui Paulo error = EINVAL;
216132176cfdSRui Paulo } else
216232176cfdSRui Paulo error = ieee80211_setregdomain(vap, reg);
216332176cfdSRui Paulo }
21644f655ef5SMatthew Dillon IEEE80211_FREE(reg, M_TEMP);
216532176cfdSRui Paulo
216632176cfdSRui Paulo return (error == 0 ? ENETRESET : error);
216732176cfdSRui Paulo }
216832176cfdSRui Paulo
216932176cfdSRui Paulo static int
ieee80211_ioctl_setroam(struct ieee80211vap * vap,const struct ieee80211req * ireq)217032176cfdSRui Paulo ieee80211_ioctl_setroam(struct ieee80211vap *vap,
217132176cfdSRui Paulo const struct ieee80211req *ireq)
217232176cfdSRui Paulo {
217332176cfdSRui Paulo if (ireq->i_len != sizeof(vap->iv_roamparms))
217432176cfdSRui Paulo return EINVAL;
217532176cfdSRui Paulo /* XXX validate params */
217632176cfdSRui Paulo /* XXX? ENETRESET to push to device? */
217732176cfdSRui Paulo return copyin(ireq->i_data, vap->iv_roamparms,
217832176cfdSRui Paulo sizeof(vap->iv_roamparms));
217932176cfdSRui Paulo }
218032176cfdSRui Paulo
218132176cfdSRui Paulo static int
checkrate(const struct ieee80211_rateset * rs,int rate)218232176cfdSRui Paulo checkrate(const struct ieee80211_rateset *rs, int rate)
218332176cfdSRui Paulo {
218432176cfdSRui Paulo int i;
218532176cfdSRui Paulo
218632176cfdSRui Paulo if (rate == IEEE80211_FIXED_RATE_NONE)
218732176cfdSRui Paulo return 1;
218832176cfdSRui Paulo for (i = 0; i < rs->rs_nrates; i++)
218932176cfdSRui Paulo if ((rs->rs_rates[i] & IEEE80211_RATE_VAL) == rate)
219032176cfdSRui Paulo return 1;
219132176cfdSRui Paulo return 0;
219232176cfdSRui Paulo }
219332176cfdSRui Paulo
219432176cfdSRui Paulo static int
checkmcs(int mcs)219532176cfdSRui Paulo checkmcs(int mcs)
219632176cfdSRui Paulo {
219732176cfdSRui Paulo if (mcs == IEEE80211_FIXED_RATE_NONE)
219832176cfdSRui Paulo return 1;
219932176cfdSRui Paulo if ((mcs & IEEE80211_RATE_MCS) == 0) /* MCS always have 0x80 set */
220032176cfdSRui Paulo return 0;
220132176cfdSRui Paulo return (mcs & 0x7f) <= 15; /* XXX could search ht rate set */
220232176cfdSRui Paulo }
220332176cfdSRui Paulo
22044f655ef5SMatthew Dillon static int
ieee80211_ioctl_settxparams(struct ieee80211vap * vap,const struct ieee80211req * ireq)220532176cfdSRui Paulo ieee80211_ioctl_settxparams(struct ieee80211vap *vap,
220632176cfdSRui Paulo const struct ieee80211req *ireq)
220732176cfdSRui Paulo {
220832176cfdSRui Paulo struct ieee80211com *ic = vap->iv_ic;
220932176cfdSRui Paulo struct ieee80211_txparams_req parms; /* XXX stack use? */
221032176cfdSRui Paulo struct ieee80211_txparam *src, *dst;
221132176cfdSRui Paulo const struct ieee80211_rateset *rs;
221232176cfdSRui Paulo int error, mode, changed, is11n, nmodes;
221332176cfdSRui Paulo
221432176cfdSRui Paulo /* NB: accept short requests for backwards compat */
221532176cfdSRui Paulo if (ireq->i_len > sizeof(parms))
221632176cfdSRui Paulo return EINVAL;
221732176cfdSRui Paulo error = copyin(ireq->i_data, &parms, ireq->i_len);
221832176cfdSRui Paulo if (error != 0)
221932176cfdSRui Paulo return error;
222032176cfdSRui Paulo nmodes = ireq->i_len / sizeof(struct ieee80211_txparam);
222132176cfdSRui Paulo changed = 0;
222232176cfdSRui Paulo /* validate parameters and check if anything changed */
222332176cfdSRui Paulo for (mode = IEEE80211_MODE_11A; mode < nmodes; mode++) {
222432176cfdSRui Paulo if (isclr(ic->ic_modecaps, mode))
222532176cfdSRui Paulo continue;
222632176cfdSRui Paulo src = &parms.params[mode];
222732176cfdSRui Paulo dst = &vap->iv_txparms[mode];
222832176cfdSRui Paulo rs = &ic->ic_sup_rates[mode]; /* NB: 11n maps to legacy */
222932176cfdSRui Paulo is11n = (mode == IEEE80211_MODE_11NA ||
223032176cfdSRui Paulo mode == IEEE80211_MODE_11NG);
223132176cfdSRui Paulo if (src->ucastrate != dst->ucastrate) {
223232176cfdSRui Paulo if (!checkrate(rs, src->ucastrate) &&
223332176cfdSRui Paulo (!is11n || !checkmcs(src->ucastrate)))
223432176cfdSRui Paulo return EINVAL;
223532176cfdSRui Paulo changed++;
223632176cfdSRui Paulo }
223732176cfdSRui Paulo if (src->mcastrate != dst->mcastrate) {
223832176cfdSRui Paulo if (!checkrate(rs, src->mcastrate) &&
223932176cfdSRui Paulo (!is11n || !checkmcs(src->mcastrate)))
224032176cfdSRui Paulo return EINVAL;
224132176cfdSRui Paulo changed++;
224232176cfdSRui Paulo }
224332176cfdSRui Paulo if (src->mgmtrate != dst->mgmtrate) {
224432176cfdSRui Paulo if (!checkrate(rs, src->mgmtrate) &&
224532176cfdSRui Paulo (!is11n || !checkmcs(src->mgmtrate)))
224632176cfdSRui Paulo return EINVAL;
224732176cfdSRui Paulo changed++;
224832176cfdSRui Paulo }
224932176cfdSRui Paulo if (src->maxretry != dst->maxretry) /* NB: no bounds */
225032176cfdSRui Paulo changed++;
225132176cfdSRui Paulo }
225232176cfdSRui Paulo if (changed) {
225332176cfdSRui Paulo /*
225432176cfdSRui Paulo * Copy new parameters in place and notify the
225532176cfdSRui Paulo * driver so it can push state to the device.
225632176cfdSRui Paulo */
225732176cfdSRui Paulo for (mode = IEEE80211_MODE_11A; mode < nmodes; mode++) {
225832176cfdSRui Paulo if (isset(ic->ic_modecaps, mode))
225932176cfdSRui Paulo vap->iv_txparms[mode] = parms.params[mode];
226032176cfdSRui Paulo }
226132176cfdSRui Paulo /* XXX could be more intelligent,
226232176cfdSRui Paulo e.g. don't reset if setting not being used */
226332176cfdSRui Paulo return ENETRESET;
226432176cfdSRui Paulo }
226532176cfdSRui Paulo return 0;
226632176cfdSRui Paulo }
226732176cfdSRui Paulo
226832176cfdSRui Paulo /*
226932176cfdSRui Paulo * Application Information Element support.
227032176cfdSRui Paulo */
227132176cfdSRui Paulo static int
setappie(struct ieee80211_appie ** aie,const struct ieee80211req * ireq)227232176cfdSRui Paulo setappie(struct ieee80211_appie **aie, const struct ieee80211req *ireq)
227332176cfdSRui Paulo {
227432176cfdSRui Paulo struct ieee80211_appie *app = *aie;
227532176cfdSRui Paulo struct ieee80211_appie *napp;
227632176cfdSRui Paulo int error;
227732176cfdSRui Paulo
227832176cfdSRui Paulo if (ireq->i_len == 0) { /* delete any existing ie */
227932176cfdSRui Paulo if (app != NULL) {
228032176cfdSRui Paulo *aie = NULL; /* XXX racey */
22814f655ef5SMatthew Dillon IEEE80211_FREE(app, M_80211_NODE_IE);
228232176cfdSRui Paulo }
228332176cfdSRui Paulo return 0;
228432176cfdSRui Paulo }
228532176cfdSRui Paulo if (!(2 <= ireq->i_len && ireq->i_len <= IEEE80211_MAX_APPIE))
228632176cfdSRui Paulo return EINVAL;
228732176cfdSRui Paulo /*
228832176cfdSRui Paulo * Allocate a new appie structure and copy in the user data.
228932176cfdSRui Paulo * When done swap in the new structure. Note that we do not
229032176cfdSRui Paulo * guard against users holding a ref to the old structure;
229132176cfdSRui Paulo * this must be handled outside this code.
229232176cfdSRui Paulo *
229332176cfdSRui Paulo * XXX bad bad bad
229432176cfdSRui Paulo */
22954f655ef5SMatthew Dillon #if defined(__DragonFly__)
229632176cfdSRui Paulo napp = (struct ieee80211_appie *) kmalloc(
2297085ff963SMatthew Dillon sizeof(struct ieee80211_appie) + ireq->i_len, M_80211_NODE_IE, M_INTWAIT);
22984f655ef5SMatthew Dillon #else
22994f655ef5SMatthew Dillon napp = (struct ieee80211_appie *) IEEE80211_MALLOC(
23004f655ef5SMatthew Dillon sizeof(struct ieee80211_appie) + ireq->i_len, M_80211_NODE_IE,
23014f655ef5SMatthew Dillon IEEE80211_M_NOWAIT);
23024f655ef5SMatthew Dillon #endif
230332176cfdSRui Paulo if (napp == NULL)
230432176cfdSRui Paulo return ENOMEM;
230532176cfdSRui Paulo /* XXX holding ic lock */
230632176cfdSRui Paulo error = copyin(ireq->i_data, napp->ie_data, ireq->i_len);
230732176cfdSRui Paulo if (error) {
23084f655ef5SMatthew Dillon IEEE80211_FREE(napp, M_80211_NODE_IE);
230932176cfdSRui Paulo return error;
231032176cfdSRui Paulo }
231132176cfdSRui Paulo napp->ie_len = ireq->i_len;
231232176cfdSRui Paulo *aie = napp;
231332176cfdSRui Paulo if (app != NULL)
23144f655ef5SMatthew Dillon IEEE80211_FREE(app, M_80211_NODE_IE);
231532176cfdSRui Paulo return 0;
231632176cfdSRui Paulo }
231732176cfdSRui Paulo
231832176cfdSRui Paulo static void
setwparsnie(struct ieee80211vap * vap,uint8_t * ie,int space)231932176cfdSRui Paulo setwparsnie(struct ieee80211vap *vap, uint8_t *ie, int space)
232032176cfdSRui Paulo {
232132176cfdSRui Paulo /* validate data is present as best we can */
232232176cfdSRui Paulo if (space == 0 || 2+ie[1] > space)
232332176cfdSRui Paulo return;
232432176cfdSRui Paulo if (ie[0] == IEEE80211_ELEMID_VENDOR)
232532176cfdSRui Paulo vap->iv_wpa_ie = ie;
232632176cfdSRui Paulo else if (ie[0] == IEEE80211_ELEMID_RSN)
232732176cfdSRui Paulo vap->iv_rsn_ie = ie;
232832176cfdSRui Paulo }
232932176cfdSRui Paulo
23304f655ef5SMatthew Dillon static int
ieee80211_ioctl_setappie_locked(struct ieee80211vap * vap,const struct ieee80211req * ireq,int fc0)233132176cfdSRui Paulo ieee80211_ioctl_setappie_locked(struct ieee80211vap *vap,
233232176cfdSRui Paulo const struct ieee80211req *ireq, int fc0)
233332176cfdSRui Paulo {
233432176cfdSRui Paulo int error;
233532176cfdSRui Paulo
2336085ff963SMatthew Dillon IEEE80211_LOCK_ASSERT(vap->iv_ic);
2337085ff963SMatthew Dillon
233832176cfdSRui Paulo switch (fc0 & IEEE80211_FC0_SUBTYPE_MASK) {
233932176cfdSRui Paulo case IEEE80211_FC0_SUBTYPE_BEACON:
234032176cfdSRui Paulo if (vap->iv_opmode != IEEE80211_M_HOSTAP &&
234132176cfdSRui Paulo vap->iv_opmode != IEEE80211_M_IBSS) {
234232176cfdSRui Paulo error = EINVAL;
234332176cfdSRui Paulo break;
234432176cfdSRui Paulo }
234532176cfdSRui Paulo error = setappie(&vap->iv_appie_beacon, ireq);
234632176cfdSRui Paulo if (error == 0)
234732176cfdSRui Paulo ieee80211_beacon_notify(vap, IEEE80211_BEACON_APPIE);
234832176cfdSRui Paulo break;
234932176cfdSRui Paulo case IEEE80211_FC0_SUBTYPE_PROBE_RESP:
235032176cfdSRui Paulo error = setappie(&vap->iv_appie_proberesp, ireq);
235132176cfdSRui Paulo break;
235232176cfdSRui Paulo case IEEE80211_FC0_SUBTYPE_ASSOC_RESP:
235332176cfdSRui Paulo if (vap->iv_opmode == IEEE80211_M_HOSTAP)
235432176cfdSRui Paulo error = setappie(&vap->iv_appie_assocresp, ireq);
235532176cfdSRui Paulo else
235632176cfdSRui Paulo error = EINVAL;
235732176cfdSRui Paulo break;
235832176cfdSRui Paulo case IEEE80211_FC0_SUBTYPE_PROBE_REQ:
235932176cfdSRui Paulo error = setappie(&vap->iv_appie_probereq, ireq);
236032176cfdSRui Paulo break;
236132176cfdSRui Paulo case IEEE80211_FC0_SUBTYPE_ASSOC_REQ:
236232176cfdSRui Paulo if (vap->iv_opmode == IEEE80211_M_STA)
236332176cfdSRui Paulo error = setappie(&vap->iv_appie_assocreq, ireq);
236432176cfdSRui Paulo else
236532176cfdSRui Paulo error = EINVAL;
236632176cfdSRui Paulo break;
236732176cfdSRui Paulo case (IEEE80211_APPIE_WPA & IEEE80211_FC0_SUBTYPE_MASK):
236832176cfdSRui Paulo error = setappie(&vap->iv_appie_wpa, ireq);
236932176cfdSRui Paulo if (error == 0) {
237032176cfdSRui Paulo /*
237132176cfdSRui Paulo * Must split single blob of data into separate
237232176cfdSRui Paulo * WPA and RSN ie's because they go in different
237332176cfdSRui Paulo * locations in the mgt frames.
237432176cfdSRui Paulo * XXX use IEEE80211_IOC_WPA2 so user code does split
237532176cfdSRui Paulo */
237632176cfdSRui Paulo vap->iv_wpa_ie = NULL;
237732176cfdSRui Paulo vap->iv_rsn_ie = NULL;
237832176cfdSRui Paulo if (vap->iv_appie_wpa != NULL) {
237932176cfdSRui Paulo struct ieee80211_appie *appie =
238032176cfdSRui Paulo vap->iv_appie_wpa;
238132176cfdSRui Paulo uint8_t *data = appie->ie_data;
238232176cfdSRui Paulo
238332176cfdSRui Paulo /* XXX ie length validate is painful, cheat */
238432176cfdSRui Paulo setwparsnie(vap, data, appie->ie_len);
238532176cfdSRui Paulo setwparsnie(vap, data + 2 + data[1],
238632176cfdSRui Paulo appie->ie_len - (2 + data[1]));
238732176cfdSRui Paulo }
238832176cfdSRui Paulo if (vap->iv_opmode == IEEE80211_M_HOSTAP ||
238932176cfdSRui Paulo vap->iv_opmode == IEEE80211_M_IBSS) {
239032176cfdSRui Paulo /*
239132176cfdSRui Paulo * Must rebuild beacon frame as the update
239232176cfdSRui Paulo * mechanism doesn't handle WPA/RSN ie's.
239332176cfdSRui Paulo * Could extend it but it doesn't normally
239432176cfdSRui Paulo * change; this is just to deal with hostapd
239532176cfdSRui Paulo * plumbing the ie after the interface is up.
239632176cfdSRui Paulo */
239732176cfdSRui Paulo error = ENETRESET;
239832176cfdSRui Paulo }
239932176cfdSRui Paulo }
240032176cfdSRui Paulo break;
240132176cfdSRui Paulo default:
240232176cfdSRui Paulo error = EINVAL;
240332176cfdSRui Paulo break;
240432176cfdSRui Paulo }
240532176cfdSRui Paulo return error;
240632176cfdSRui Paulo }
240732176cfdSRui Paulo
24084f655ef5SMatthew Dillon static int
ieee80211_ioctl_setappie(struct ieee80211vap * vap,const struct ieee80211req * ireq)240932176cfdSRui Paulo ieee80211_ioctl_setappie(struct ieee80211vap *vap,
241032176cfdSRui Paulo const struct ieee80211req *ireq)
241132176cfdSRui Paulo {
2412085ff963SMatthew Dillon struct ieee80211com *ic = vap->iv_ic;
241332176cfdSRui Paulo int error;
241432176cfdSRui Paulo uint8_t fc0;
241532176cfdSRui Paulo
241632176cfdSRui Paulo fc0 = ireq->i_val & 0xff;
241732176cfdSRui Paulo if ((fc0 & IEEE80211_FC0_TYPE_MASK) != IEEE80211_FC0_TYPE_MGT)
241832176cfdSRui Paulo return EINVAL;
241932176cfdSRui Paulo /* NB: could check iv_opmode and reject but hardly worth the effort */
2420085ff963SMatthew Dillon IEEE80211_LOCK(ic);
242132176cfdSRui Paulo error = ieee80211_ioctl_setappie_locked(vap, ireq, fc0);
2422085ff963SMatthew Dillon IEEE80211_UNLOCK(ic);
242332176cfdSRui Paulo return error;
242432176cfdSRui Paulo }
242532176cfdSRui Paulo
24264f655ef5SMatthew Dillon static int
ieee80211_ioctl_chanswitch(struct ieee80211vap * vap,struct ieee80211req * ireq)242732176cfdSRui Paulo ieee80211_ioctl_chanswitch(struct ieee80211vap *vap, struct ieee80211req *ireq)
242832176cfdSRui Paulo {
242932176cfdSRui Paulo struct ieee80211com *ic = vap->iv_ic;
243032176cfdSRui Paulo struct ieee80211_chanswitch_req csr;
243132176cfdSRui Paulo struct ieee80211_channel *c;
243232176cfdSRui Paulo int error;
243332176cfdSRui Paulo
243432176cfdSRui Paulo if (ireq->i_len != sizeof(csr))
243532176cfdSRui Paulo return EINVAL;
243632176cfdSRui Paulo error = copyin(ireq->i_data, &csr, sizeof(csr));
243732176cfdSRui Paulo if (error != 0)
243832176cfdSRui Paulo return error;
243932176cfdSRui Paulo /* XXX adhoc mode not supported */
244032176cfdSRui Paulo if (vap->iv_opmode != IEEE80211_M_HOSTAP ||
244132176cfdSRui Paulo (vap->iv_flags & IEEE80211_F_DOTH) == 0)
244232176cfdSRui Paulo return EOPNOTSUPP;
244332176cfdSRui Paulo c = ieee80211_find_channel(ic,
244432176cfdSRui Paulo csr.csa_chan.ic_freq, csr.csa_chan.ic_flags);
244532176cfdSRui Paulo if (c == NULL)
244632176cfdSRui Paulo return ENOENT;
2447085ff963SMatthew Dillon IEEE80211_LOCK(ic);
244832176cfdSRui Paulo if ((ic->ic_flags & IEEE80211_F_CSAPENDING) == 0)
244932176cfdSRui Paulo ieee80211_csa_startswitch(ic, c, csr.csa_mode, csr.csa_count);
245032176cfdSRui Paulo else if (csr.csa_count == 0)
245132176cfdSRui Paulo ieee80211_csa_cancelswitch(ic);
245232176cfdSRui Paulo else
245332176cfdSRui Paulo error = EBUSY;
2454085ff963SMatthew Dillon IEEE80211_UNLOCK(ic);
245532176cfdSRui Paulo return error;
245632176cfdSRui Paulo }
245732176cfdSRui Paulo
2458085ff963SMatthew Dillon static int
ieee80211_scanreq(struct ieee80211vap * vap,struct ieee80211_scan_req * sr)2459085ff963SMatthew Dillon ieee80211_scanreq(struct ieee80211vap *vap, struct ieee80211_scan_req *sr)
246032176cfdSRui Paulo {
246132176cfdSRui Paulo #define IEEE80211_IOC_SCAN_FLAGS \
246232176cfdSRui Paulo (IEEE80211_IOC_SCAN_NOPICK | IEEE80211_IOC_SCAN_ACTIVE | \
246332176cfdSRui Paulo IEEE80211_IOC_SCAN_PICK1ST | IEEE80211_IOC_SCAN_BGSCAN | \
246432176cfdSRui Paulo IEEE80211_IOC_SCAN_ONCE | IEEE80211_IOC_SCAN_NOBCAST | \
246532176cfdSRui Paulo IEEE80211_IOC_SCAN_NOJOIN | IEEE80211_IOC_SCAN_FLUSH | \
246632176cfdSRui Paulo IEEE80211_IOC_SCAN_CHECK)
246732176cfdSRui Paulo struct ieee80211com *ic = vap->iv_ic;
246832176cfdSRui Paulo int error, i;
246932176cfdSRui Paulo
247032176cfdSRui Paulo /* convert duration */
2471085ff963SMatthew Dillon if (sr->sr_duration == IEEE80211_IOC_SCAN_FOREVER)
2472085ff963SMatthew Dillon sr->sr_duration = IEEE80211_SCAN_FOREVER;
247332176cfdSRui Paulo else {
2474085ff963SMatthew Dillon if (sr->sr_duration < IEEE80211_IOC_SCAN_DURATION_MIN ||
2475085ff963SMatthew Dillon sr->sr_duration > IEEE80211_IOC_SCAN_DURATION_MAX)
247632176cfdSRui Paulo return EINVAL;
2477085ff963SMatthew Dillon sr->sr_duration = msecs_to_ticks(sr->sr_duration);
2478085ff963SMatthew Dillon if (sr->sr_duration < 1)
2479085ff963SMatthew Dillon sr->sr_duration = 1;
248032176cfdSRui Paulo }
248132176cfdSRui Paulo /* convert min/max channel dwell */
2482085ff963SMatthew Dillon if (sr->sr_mindwell != 0) {
2483085ff963SMatthew Dillon sr->sr_mindwell = msecs_to_ticks(sr->sr_mindwell);
2484085ff963SMatthew Dillon if (sr->sr_mindwell < 1)
2485085ff963SMatthew Dillon sr->sr_mindwell = 1;
248632176cfdSRui Paulo }
2487085ff963SMatthew Dillon if (sr->sr_maxdwell != 0) {
2488085ff963SMatthew Dillon sr->sr_maxdwell = msecs_to_ticks(sr->sr_maxdwell);
2489085ff963SMatthew Dillon if (sr->sr_maxdwell < 1)
2490085ff963SMatthew Dillon sr->sr_maxdwell = 1;
249132176cfdSRui Paulo }
249232176cfdSRui Paulo /* NB: silently reduce ssid count to what is supported */
2493085ff963SMatthew Dillon if (sr->sr_nssid > IEEE80211_SCAN_MAX_SSID)
2494085ff963SMatthew Dillon sr->sr_nssid = IEEE80211_SCAN_MAX_SSID;
2495085ff963SMatthew Dillon for (i = 0; i < sr->sr_nssid; i++)
2496085ff963SMatthew Dillon if (sr->sr_ssid[i].len > IEEE80211_NWID_LEN)
249732176cfdSRui Paulo return EINVAL;
249832176cfdSRui Paulo /* cleanse flags just in case, could reject if invalid flags */
2499085ff963SMatthew Dillon sr->sr_flags &= IEEE80211_IOC_SCAN_FLAGS;
250032176cfdSRui Paulo /*
250132176cfdSRui Paulo * Add an implicit NOPICK if the vap is not marked UP. This
250232176cfdSRui Paulo * allows applications to scan without joining a bss (or picking
250332176cfdSRui Paulo * a channel and setting up a bss) and without forcing manual
250432176cfdSRui Paulo * roaming mode--you just need to mark the parent device UP.
250532176cfdSRui Paulo */
250632176cfdSRui Paulo if ((vap->iv_ifp->if_flags & IFF_UP) == 0)
2507085ff963SMatthew Dillon sr->sr_flags |= IEEE80211_IOC_SCAN_NOPICK;
250832176cfdSRui Paulo
250932176cfdSRui Paulo IEEE80211_DPRINTF(vap, IEEE80211_MSG_SCAN,
251032176cfdSRui Paulo "%s: flags 0x%x%s duration 0x%x mindwell %u maxdwell %u nssid %d\n",
2511085ff963SMatthew Dillon __func__, sr->sr_flags,
251232176cfdSRui Paulo (vap->iv_ifp->if_flags & IFF_UP) == 0 ? " (!IFF_UP)" : "",
2513085ff963SMatthew Dillon sr->sr_duration, sr->sr_mindwell, sr->sr_maxdwell, sr->sr_nssid);
251432176cfdSRui Paulo /*
251532176cfdSRui Paulo * If we are in INIT state then the driver has never had a chance
251632176cfdSRui Paulo * to setup hardware state to do a scan; we must use the state
251732176cfdSRui Paulo * machine to get us up to the SCAN state but once we reach SCAN
251832176cfdSRui Paulo * state we then want to use the supplied params. Stash the
251932176cfdSRui Paulo * parameters in the vap and mark IEEE80211_FEXT_SCANREQ; the
252032176cfdSRui Paulo * state machines will recognize this and use the stashed params
252132176cfdSRui Paulo * to issue the scan request.
252232176cfdSRui Paulo *
252332176cfdSRui Paulo * Otherwise just invoke the scan machinery directly.
252432176cfdSRui Paulo */
2525085ff963SMatthew Dillon IEEE80211_LOCK(ic);
2526326ab318SImre Vadász if (ic->ic_nrunning == 0) {
2527326ab318SImre Vadász IEEE80211_UNLOCK(ic);
2528326ab318SImre Vadász return ENXIO;
2529326ab318SImre Vadász }
2530326ab318SImre Vadász
253132176cfdSRui Paulo if (vap->iv_state == IEEE80211_S_INIT) {
253232176cfdSRui Paulo /* NB: clobbers previous settings */
2533085ff963SMatthew Dillon vap->iv_scanreq_flags = sr->sr_flags;
2534085ff963SMatthew Dillon vap->iv_scanreq_duration = sr->sr_duration;
2535085ff963SMatthew Dillon vap->iv_scanreq_nssid = sr->sr_nssid;
2536085ff963SMatthew Dillon for (i = 0; i < sr->sr_nssid; i++) {
2537085ff963SMatthew Dillon vap->iv_scanreq_ssid[i].len = sr->sr_ssid[i].len;
2538085ff963SMatthew Dillon memcpy(vap->iv_scanreq_ssid[i].ssid,
2539085ff963SMatthew Dillon sr->sr_ssid[i].ssid, sr->sr_ssid[i].len);
254032176cfdSRui Paulo }
254132176cfdSRui Paulo vap->iv_flags_ext |= IEEE80211_FEXT_SCANREQ;
2542085ff963SMatthew Dillon IEEE80211_UNLOCK(ic);
254332176cfdSRui Paulo ieee80211_new_state(vap, IEEE80211_S_SCAN, 0);
254432176cfdSRui Paulo } else {
254532176cfdSRui Paulo vap->iv_flags_ext &= ~IEEE80211_FEXT_SCANREQ;
2546085ff963SMatthew Dillon IEEE80211_UNLOCK(ic);
2547085ff963SMatthew Dillon if (sr->sr_flags & IEEE80211_IOC_SCAN_CHECK) {
2548085ff963SMatthew Dillon error = ieee80211_check_scan(vap, sr->sr_flags,
2549085ff963SMatthew Dillon sr->sr_duration, sr->sr_mindwell, sr->sr_maxdwell,
2550085ff963SMatthew Dillon sr->sr_nssid,
255132176cfdSRui Paulo /* NB: cheat, we assume structures are compatible */
2552085ff963SMatthew Dillon (const struct ieee80211_scan_ssid *) &sr->sr_ssid[0]);
255332176cfdSRui Paulo } else {
2554085ff963SMatthew Dillon error = ieee80211_start_scan(vap, sr->sr_flags,
2555085ff963SMatthew Dillon sr->sr_duration, sr->sr_mindwell, sr->sr_maxdwell,
2556085ff963SMatthew Dillon sr->sr_nssid,
255732176cfdSRui Paulo /* NB: cheat, we assume structures are compatible */
2558085ff963SMatthew Dillon (const struct ieee80211_scan_ssid *) &sr->sr_ssid[0]);
255932176cfdSRui Paulo }
2560085ff963SMatthew Dillon if (error == 0)
2561085ff963SMatthew Dillon return EINPROGRESS;
256232176cfdSRui Paulo }
2563085ff963SMatthew Dillon return 0;
256432176cfdSRui Paulo #undef IEEE80211_IOC_SCAN_FLAGS
256532176cfdSRui Paulo }
256632176cfdSRui Paulo
25674f655ef5SMatthew Dillon static int
ieee80211_ioctl_scanreq(struct ieee80211vap * vap,struct ieee80211req * ireq)2568085ff963SMatthew Dillon ieee80211_ioctl_scanreq(struct ieee80211vap *vap, struct ieee80211req *ireq)
2569085ff963SMatthew Dillon {
25704f655ef5SMatthew Dillon struct ieee80211_scan_req *sr;
2571085ff963SMatthew Dillon int error;
2572085ff963SMatthew Dillon
25734f655ef5SMatthew Dillon if (ireq->i_len != sizeof(*sr))
2574085ff963SMatthew Dillon return EINVAL;
25754f655ef5SMatthew Dillon #if defined(__DragonFly__)
25764f655ef5SMatthew Dillon sr = kmalloc(sizeof(*sr), M_TEMP, M_INTWAIT | M_ZERO);
25774f655ef5SMatthew Dillon #else
25784f655ef5SMatthew Dillon sr = IEEE80211_MALLOC(sizeof(*sr), M_TEMP,
25794f655ef5SMatthew Dillon IEEE80211_M_NOWAIT | IEEE80211_M_ZERO);
25804f655ef5SMatthew Dillon #endif
25814f655ef5SMatthew Dillon if (sr == NULL)
25824f655ef5SMatthew Dillon return ENOMEM;
25834f655ef5SMatthew Dillon error = copyin(ireq->i_data, sr, sizeof(*sr));
2584085ff963SMatthew Dillon if (error != 0)
25854f655ef5SMatthew Dillon goto bad;
25864f655ef5SMatthew Dillon error = ieee80211_scanreq(vap, sr);
25874f655ef5SMatthew Dillon bad:
25884f655ef5SMatthew Dillon IEEE80211_FREE(sr, M_TEMP);
2589085ff963SMatthew Dillon return error;
2590085ff963SMatthew Dillon }
2591085ff963SMatthew Dillon
25924f655ef5SMatthew Dillon static int
ieee80211_ioctl_setstavlan(struct ieee80211vap * vap,struct ieee80211req * ireq)259332176cfdSRui Paulo ieee80211_ioctl_setstavlan(struct ieee80211vap *vap, struct ieee80211req *ireq)
259432176cfdSRui Paulo {
259532176cfdSRui Paulo struct ieee80211_node *ni;
259632176cfdSRui Paulo struct ieee80211req_sta_vlan vlan;
259732176cfdSRui Paulo int error;
259832176cfdSRui Paulo
259932176cfdSRui Paulo if (ireq->i_len != sizeof(vlan))
260032176cfdSRui Paulo return EINVAL;
260132176cfdSRui Paulo error = copyin(ireq->i_data, &vlan, sizeof(vlan));
260232176cfdSRui Paulo if (error != 0)
260332176cfdSRui Paulo return error;
260432176cfdSRui Paulo if (!IEEE80211_ADDR_EQ(vlan.sv_macaddr, zerobssid)) {
260532176cfdSRui Paulo ni = ieee80211_find_vap_node(&vap->iv_ic->ic_sta, vap,
260632176cfdSRui Paulo vlan.sv_macaddr);
260732176cfdSRui Paulo if (ni == NULL)
260832176cfdSRui Paulo return ENOENT;
260932176cfdSRui Paulo } else
261032176cfdSRui Paulo ni = ieee80211_ref_node(vap->iv_bss);
261132176cfdSRui Paulo ni->ni_vlan = vlan.sv_vlan;
261232176cfdSRui Paulo ieee80211_free_node(ni);
261332176cfdSRui Paulo return error;
261432176cfdSRui Paulo }
261532176cfdSRui Paulo
261632176cfdSRui Paulo static int
isvap11g(const struct ieee80211vap * vap)261732176cfdSRui Paulo isvap11g(const struct ieee80211vap *vap)
261832176cfdSRui Paulo {
261932176cfdSRui Paulo const struct ieee80211_node *bss = vap->iv_bss;
262032176cfdSRui Paulo return bss->ni_chan != IEEE80211_CHAN_ANYC &&
262132176cfdSRui Paulo IEEE80211_IS_CHAN_ANYG(bss->ni_chan);
262232176cfdSRui Paulo }
262332176cfdSRui Paulo
262432176cfdSRui Paulo static int
isvapht(const struct ieee80211vap * vap)262532176cfdSRui Paulo isvapht(const struct ieee80211vap *vap)
262632176cfdSRui Paulo {
262732176cfdSRui Paulo const struct ieee80211_node *bss = vap->iv_bss;
262832176cfdSRui Paulo return bss->ni_chan != IEEE80211_CHAN_ANYC &&
262932176cfdSRui Paulo IEEE80211_IS_CHAN_HT(bss->ni_chan);
263032176cfdSRui Paulo }
263132176cfdSRui Paulo
263232176cfdSRui Paulo /*
263332176cfdSRui Paulo * Dummy ioctl set handler so the linker set is defined.
263432176cfdSRui Paulo */
263532176cfdSRui Paulo static int
dummy_ioctl_set(struct ieee80211vap * vap,struct ieee80211req * ireq)263632176cfdSRui Paulo dummy_ioctl_set(struct ieee80211vap *vap, struct ieee80211req *ireq)
263732176cfdSRui Paulo {
263832176cfdSRui Paulo return ENOSYS;
263932176cfdSRui Paulo }
264032176cfdSRui Paulo IEEE80211_IOCTL_SET(dummy, dummy_ioctl_set);
264132176cfdSRui Paulo
264232176cfdSRui Paulo static int
ieee80211_ioctl_setdefault(struct ieee80211vap * vap,struct ieee80211req * ireq)264332176cfdSRui Paulo ieee80211_ioctl_setdefault(struct ieee80211vap *vap, struct ieee80211req *ireq)
264432176cfdSRui Paulo {
264532176cfdSRui Paulo ieee80211_ioctl_setfunc * const *set;
264632176cfdSRui Paulo int error;
264732176cfdSRui Paulo
264832176cfdSRui Paulo SET_FOREACH(set, ieee80211_ioctl_setset) {
264932176cfdSRui Paulo error = (*set)(vap, ireq);
265032176cfdSRui Paulo if (error != ENOSYS)
265132176cfdSRui Paulo return error;
265232176cfdSRui Paulo }
265332176cfdSRui Paulo return EINVAL;
265432176cfdSRui Paulo }
265532176cfdSRui Paulo
26564f655ef5SMatthew Dillon static int
ieee80211_ioctl_set80211(struct ieee80211vap * vap,u_long cmd,struct ieee80211req * ireq)265732176cfdSRui Paulo ieee80211_ioctl_set80211(struct ieee80211vap *vap, u_long cmd, struct ieee80211req *ireq)
265832176cfdSRui Paulo {
265932176cfdSRui Paulo struct ieee80211com *ic = vap->iv_ic;
2660841ab66cSSepherosa Ziehau int error;
2661841ab66cSSepherosa Ziehau const struct ieee80211_authenticator *auth;
2662841ab66cSSepherosa Ziehau uint8_t tmpkey[IEEE80211_KEYBUF_SIZE];
2663841ab66cSSepherosa Ziehau char tmpssid[IEEE80211_NWID_LEN];
2664841ab66cSSepherosa Ziehau uint8_t tmpbssid[IEEE80211_ADDR_LEN];
2665841ab66cSSepherosa Ziehau struct ieee80211_key *k;
2666841ab66cSSepherosa Ziehau u_int kid;
266732176cfdSRui Paulo uint32_t flags;
2668841ab66cSSepherosa Ziehau
2669841ab66cSSepherosa Ziehau error = 0;
2670f186073cSJoerg Sonnenberger switch (ireq->i_type) {
2671f186073cSJoerg Sonnenberger case IEEE80211_IOC_SSID:
2672f186073cSJoerg Sonnenberger if (ireq->i_val != 0 ||
2673841ab66cSSepherosa Ziehau ireq->i_len > IEEE80211_NWID_LEN)
2674841ab66cSSepherosa Ziehau return EINVAL;
2675f186073cSJoerg Sonnenberger error = copyin(ireq->i_data, tmpssid, ireq->i_len);
2676f186073cSJoerg Sonnenberger if (error)
2677f186073cSJoerg Sonnenberger break;
267832176cfdSRui Paulo memset(vap->iv_des_ssid[0].ssid, 0, IEEE80211_NWID_LEN);
267932176cfdSRui Paulo vap->iv_des_ssid[0].len = ireq->i_len;
268032176cfdSRui Paulo memcpy(vap->iv_des_ssid[0].ssid, tmpssid, ireq->i_len);
268132176cfdSRui Paulo vap->iv_des_nssid = (ireq->i_len > 0);
2682f186073cSJoerg Sonnenberger error = ENETRESET;
2683f186073cSJoerg Sonnenberger break;
2684f186073cSJoerg Sonnenberger case IEEE80211_IOC_WEP:
2685841ab66cSSepherosa Ziehau switch (ireq->i_val) {
2686841ab66cSSepherosa Ziehau case IEEE80211_WEP_OFF:
268732176cfdSRui Paulo vap->iv_flags &= ~IEEE80211_F_PRIVACY;
268832176cfdSRui Paulo vap->iv_flags &= ~IEEE80211_F_DROPUNENC;
2689841ab66cSSepherosa Ziehau break;
2690841ab66cSSepherosa Ziehau case IEEE80211_WEP_ON:
269132176cfdSRui Paulo vap->iv_flags |= IEEE80211_F_PRIVACY;
269232176cfdSRui Paulo vap->iv_flags |= IEEE80211_F_DROPUNENC;
2693841ab66cSSepherosa Ziehau break;
2694841ab66cSSepherosa Ziehau case IEEE80211_WEP_MIXED:
269532176cfdSRui Paulo vap->iv_flags |= IEEE80211_F_PRIVACY;
269632176cfdSRui Paulo vap->iv_flags &= ~IEEE80211_F_DROPUNENC;
2697841ab66cSSepherosa Ziehau break;
2698f186073cSJoerg Sonnenberger }
2699f186073cSJoerg Sonnenberger error = ENETRESET;
2700f186073cSJoerg Sonnenberger break;
2701f186073cSJoerg Sonnenberger case IEEE80211_IOC_WEPKEY:
2702f186073cSJoerg Sonnenberger kid = (u_int) ireq->i_val;
2703841ab66cSSepherosa Ziehau if (kid >= IEEE80211_WEP_NKID)
2704841ab66cSSepherosa Ziehau return EINVAL;
270532176cfdSRui Paulo k = &vap->iv_nw_keys[kid];
2706841ab66cSSepherosa Ziehau if (ireq->i_len == 0) {
2707841ab66cSSepherosa Ziehau /* zero-len =>'s delete any existing key */
270832176cfdSRui Paulo (void) ieee80211_crypto_delkey(vap, k);
2709f186073cSJoerg Sonnenberger break;
2710f186073cSJoerg Sonnenberger }
2711841ab66cSSepherosa Ziehau if (ireq->i_len > sizeof(tmpkey))
2712841ab66cSSepherosa Ziehau return EINVAL;
2713f186073cSJoerg Sonnenberger memset(tmpkey, 0, sizeof(tmpkey));
2714f186073cSJoerg Sonnenberger error = copyin(ireq->i_data, tmpkey, ireq->i_len);
2715f186073cSJoerg Sonnenberger if (error)
2716f186073cSJoerg Sonnenberger break;
271732176cfdSRui Paulo ieee80211_key_update_begin(vap);
2718841ab66cSSepherosa Ziehau k->wk_keyix = kid; /* NB: force fixed key id */
271932176cfdSRui Paulo if (ieee80211_crypto_newkey(vap, IEEE80211_CIPHER_WEP,
2720841ab66cSSepherosa Ziehau IEEE80211_KEY_XMIT | IEEE80211_KEY_RECV, k)) {
2721841ab66cSSepherosa Ziehau k->wk_keylen = ireq->i_len;
2722841ab66cSSepherosa Ziehau memcpy(k->wk_key, tmpkey, sizeof(tmpkey));
272332176cfdSRui Paulo IEEE80211_ADDR_COPY(k->wk_macaddr, vap->iv_myaddr);
272432176cfdSRui Paulo if (!ieee80211_crypto_setkey(vap, k))
2725841ab66cSSepherosa Ziehau error = EINVAL;
2726841ab66cSSepherosa Ziehau } else
2727841ab66cSSepherosa Ziehau error = EINVAL;
272832176cfdSRui Paulo ieee80211_key_update_end(vap);
2729f186073cSJoerg Sonnenberger break;
2730f186073cSJoerg Sonnenberger case IEEE80211_IOC_WEPTXKEY:
2731f186073cSJoerg Sonnenberger kid = (u_int) ireq->i_val;
2732841ab66cSSepherosa Ziehau if (kid >= IEEE80211_WEP_NKID &&
2733841ab66cSSepherosa Ziehau (uint16_t) kid != IEEE80211_KEYIX_NONE)
2734841ab66cSSepherosa Ziehau return EINVAL;
273532176cfdSRui Paulo vap->iv_def_txkey = kid;
2736841ab66cSSepherosa Ziehau break;
2737841ab66cSSepherosa Ziehau case IEEE80211_IOC_AUTHMODE:
2738841ab66cSSepherosa Ziehau switch (ireq->i_val) {
2739841ab66cSSepherosa Ziehau case IEEE80211_AUTH_WPA:
2740841ab66cSSepherosa Ziehau case IEEE80211_AUTH_8021X: /* 802.1x */
2741841ab66cSSepherosa Ziehau case IEEE80211_AUTH_OPEN: /* open */
2742841ab66cSSepherosa Ziehau case IEEE80211_AUTH_SHARED: /* shared-key */
2743841ab66cSSepherosa Ziehau case IEEE80211_AUTH_AUTO: /* auto */
2744841ab66cSSepherosa Ziehau auth = ieee80211_authenticator_get(ireq->i_val);
2745841ab66cSSepherosa Ziehau if (auth == NULL)
2746841ab66cSSepherosa Ziehau return EINVAL;
2747841ab66cSSepherosa Ziehau break;
2748841ab66cSSepherosa Ziehau default:
2749841ab66cSSepherosa Ziehau return EINVAL;
2750841ab66cSSepherosa Ziehau }
2751841ab66cSSepherosa Ziehau switch (ireq->i_val) {
2752841ab66cSSepherosa Ziehau case IEEE80211_AUTH_WPA: /* WPA w/ 802.1x */
275332176cfdSRui Paulo vap->iv_flags |= IEEE80211_F_PRIVACY;
2754841ab66cSSepherosa Ziehau ireq->i_val = IEEE80211_AUTH_8021X;
2755841ab66cSSepherosa Ziehau break;
2756841ab66cSSepherosa Ziehau case IEEE80211_AUTH_OPEN: /* open */
275732176cfdSRui Paulo vap->iv_flags &= ~(IEEE80211_F_WPA|IEEE80211_F_PRIVACY);
2758841ab66cSSepherosa Ziehau break;
2759841ab66cSSepherosa Ziehau case IEEE80211_AUTH_SHARED: /* shared-key */
2760841ab66cSSepherosa Ziehau case IEEE80211_AUTH_8021X: /* 802.1x */
276132176cfdSRui Paulo vap->iv_flags &= ~IEEE80211_F_WPA;
2762841ab66cSSepherosa Ziehau /* both require a key so mark the PRIVACY capability */
276332176cfdSRui Paulo vap->iv_flags |= IEEE80211_F_PRIVACY;
2764841ab66cSSepherosa Ziehau break;
2765841ab66cSSepherosa Ziehau case IEEE80211_AUTH_AUTO: /* auto */
276632176cfdSRui Paulo vap->iv_flags &= ~IEEE80211_F_WPA;
2767841ab66cSSepherosa Ziehau /* XXX PRIVACY handling? */
2768841ab66cSSepherosa Ziehau /* XXX what's the right way to do this? */
2769f186073cSJoerg Sonnenberger break;
2770f186073cSJoerg Sonnenberger }
2771841ab66cSSepherosa Ziehau /* NB: authenticator attach/detach happens on state change */
277232176cfdSRui Paulo vap->iv_bss->ni_authmode = ireq->i_val;
2773841ab66cSSepherosa Ziehau /* XXX mixed/mode/usage? */
277432176cfdSRui Paulo vap->iv_auth = auth;
2775f186073cSJoerg Sonnenberger error = ENETRESET;
2776f186073cSJoerg Sonnenberger break;
2777f186073cSJoerg Sonnenberger case IEEE80211_IOC_CHANNEL:
277832176cfdSRui Paulo error = ieee80211_ioctl_setchannel(vap, ireq);
2779f186073cSJoerg Sonnenberger break;
2780f186073cSJoerg Sonnenberger case IEEE80211_IOC_POWERSAVE:
2781f186073cSJoerg Sonnenberger switch (ireq->i_val) {
2782f186073cSJoerg Sonnenberger case IEEE80211_POWERSAVE_OFF:
278332176cfdSRui Paulo if (vap->iv_flags & IEEE80211_F_PMGTON) {
278432176cfdSRui Paulo ieee80211_syncflag(vap, -IEEE80211_F_PMGTON);
278532176cfdSRui Paulo error = ERESTART;
2786f186073cSJoerg Sonnenberger }
2787f186073cSJoerg Sonnenberger break;
2788f186073cSJoerg Sonnenberger case IEEE80211_POWERSAVE_ON:
278932176cfdSRui Paulo if ((vap->iv_caps & IEEE80211_C_PMGT) == 0)
279032176cfdSRui Paulo error = EOPNOTSUPP;
279132176cfdSRui Paulo else if ((vap->iv_flags & IEEE80211_F_PMGTON) == 0) {
279232176cfdSRui Paulo ieee80211_syncflag(vap, IEEE80211_F_PMGTON);
279332176cfdSRui Paulo error = ERESTART;
2794f186073cSJoerg Sonnenberger }
2795f186073cSJoerg Sonnenberger break;
2796f186073cSJoerg Sonnenberger default:
2797f186073cSJoerg Sonnenberger error = EINVAL;
2798f186073cSJoerg Sonnenberger break;
2799f186073cSJoerg Sonnenberger }
2800f186073cSJoerg Sonnenberger break;
2801f186073cSJoerg Sonnenberger case IEEE80211_IOC_POWERSAVESLEEP:
2802841ab66cSSepherosa Ziehau if (ireq->i_val < 0)
2803841ab66cSSepherosa Ziehau return EINVAL;
2804f186073cSJoerg Sonnenberger ic->ic_lintval = ireq->i_val;
280532176cfdSRui Paulo error = ERESTART;
2806f186073cSJoerg Sonnenberger break;
2807f186073cSJoerg Sonnenberger case IEEE80211_IOC_RTSTHRESHOLD:
2808841ab66cSSepherosa Ziehau if (!(IEEE80211_RTS_MIN <= ireq->i_val &&
2809841ab66cSSepherosa Ziehau ireq->i_val <= IEEE80211_RTS_MAX))
2810841ab66cSSepherosa Ziehau return EINVAL;
281132176cfdSRui Paulo vap->iv_rtsthreshold = ireq->i_val;
281232176cfdSRui Paulo error = ERESTART;
2813f186073cSJoerg Sonnenberger break;
2814f186073cSJoerg Sonnenberger case IEEE80211_IOC_PROTMODE:
2815841ab66cSSepherosa Ziehau if (ireq->i_val > IEEE80211_PROT_RTSCTS)
2816841ab66cSSepherosa Ziehau return EINVAL;
2817085ff963SMatthew Dillon ic->ic_protmode = (enum ieee80211_protmode)ireq->i_val;
2818f186073cSJoerg Sonnenberger /* NB: if not operating in 11g this can wait */
281932176cfdSRui Paulo if (ic->ic_bsschan != IEEE80211_CHAN_ANYC &&
282032176cfdSRui Paulo IEEE80211_IS_CHAN_ANYG(ic->ic_bsschan))
282132176cfdSRui Paulo error = ERESTART;
2822f186073cSJoerg Sonnenberger break;
2823f186073cSJoerg Sonnenberger case IEEE80211_IOC_TXPOWER:
2824841ab66cSSepherosa Ziehau if ((ic->ic_caps & IEEE80211_C_TXPMGT) == 0)
282532176cfdSRui Paulo return EOPNOTSUPP;
282632176cfdSRui Paulo if (!(IEEE80211_TXPOWER_MIN <= ireq->i_val &&
282732176cfdSRui Paulo ireq->i_val <= IEEE80211_TXPOWER_MAX))
2828841ab66cSSepherosa Ziehau return EINVAL;
2829841ab66cSSepherosa Ziehau ic->ic_txpowlimit = ireq->i_val;
283032176cfdSRui Paulo error = ERESTART;
2831841ab66cSSepherosa Ziehau break;
2832841ab66cSSepherosa Ziehau case IEEE80211_IOC_ROAMING:
2833841ab66cSSepherosa Ziehau if (!(IEEE80211_ROAMING_DEVICE <= ireq->i_val &&
2834841ab66cSSepherosa Ziehau ireq->i_val <= IEEE80211_ROAMING_MANUAL))
2835841ab66cSSepherosa Ziehau return EINVAL;
2836085ff963SMatthew Dillon vap->iv_roaming = (enum ieee80211_roamingmode)ireq->i_val;
2837841ab66cSSepherosa Ziehau /* XXXX reset? */
2838841ab66cSSepherosa Ziehau break;
2839841ab66cSSepherosa Ziehau case IEEE80211_IOC_PRIVACY:
2840841ab66cSSepherosa Ziehau if (ireq->i_val) {
2841841ab66cSSepherosa Ziehau /* XXX check for key state? */
284232176cfdSRui Paulo vap->iv_flags |= IEEE80211_F_PRIVACY;
2843841ab66cSSepherosa Ziehau } else
284432176cfdSRui Paulo vap->iv_flags &= ~IEEE80211_F_PRIVACY;
284532176cfdSRui Paulo /* XXX ERESTART? */
2846841ab66cSSepherosa Ziehau break;
2847841ab66cSSepherosa Ziehau case IEEE80211_IOC_DROPUNENCRYPTED:
2848841ab66cSSepherosa Ziehau if (ireq->i_val)
284932176cfdSRui Paulo vap->iv_flags |= IEEE80211_F_DROPUNENC;
2850841ab66cSSepherosa Ziehau else
285132176cfdSRui Paulo vap->iv_flags &= ~IEEE80211_F_DROPUNENC;
285232176cfdSRui Paulo /* XXX ERESTART? */
2853841ab66cSSepherosa Ziehau break;
2854841ab66cSSepherosa Ziehau case IEEE80211_IOC_WPAKEY:
285532176cfdSRui Paulo error = ieee80211_ioctl_setkey(vap, ireq);
2856841ab66cSSepherosa Ziehau break;
2857841ab66cSSepherosa Ziehau case IEEE80211_IOC_DELKEY:
285832176cfdSRui Paulo error = ieee80211_ioctl_delkey(vap, ireq);
2859841ab66cSSepherosa Ziehau break;
2860841ab66cSSepherosa Ziehau case IEEE80211_IOC_MLME:
286132176cfdSRui Paulo error = ieee80211_ioctl_setmlme(vap, ireq);
2862841ab66cSSepherosa Ziehau break;
2863841ab66cSSepherosa Ziehau case IEEE80211_IOC_COUNTERMEASURES:
2864841ab66cSSepherosa Ziehau if (ireq->i_val) {
286532176cfdSRui Paulo if ((vap->iv_flags & IEEE80211_F_WPA) == 0)
286632176cfdSRui Paulo return EOPNOTSUPP;
286732176cfdSRui Paulo vap->iv_flags |= IEEE80211_F_COUNTERM;
2868841ab66cSSepherosa Ziehau } else
286932176cfdSRui Paulo vap->iv_flags &= ~IEEE80211_F_COUNTERM;
287032176cfdSRui Paulo /* XXX ERESTART? */
2871841ab66cSSepherosa Ziehau break;
2872841ab66cSSepherosa Ziehau case IEEE80211_IOC_WPA:
2873841ab66cSSepherosa Ziehau if (ireq->i_val > 3)
2874841ab66cSSepherosa Ziehau return EINVAL;
2875841ab66cSSepherosa Ziehau /* XXX verify ciphers available */
287632176cfdSRui Paulo flags = vap->iv_flags & ~IEEE80211_F_WPA;
2877841ab66cSSepherosa Ziehau switch (ireq->i_val) {
28784f655ef5SMatthew Dillon case 0:
28794f655ef5SMatthew Dillon /* wpa_supplicant calls this to clear the WPA config */
28804f655ef5SMatthew Dillon break;
2881841ab66cSSepherosa Ziehau case 1:
288232176cfdSRui Paulo if (!(vap->iv_caps & IEEE80211_C_WPA1))
288332176cfdSRui Paulo return EOPNOTSUPP;
288432176cfdSRui Paulo flags |= IEEE80211_F_WPA1;
2885841ab66cSSepherosa Ziehau break;
2886841ab66cSSepherosa Ziehau case 2:
288732176cfdSRui Paulo if (!(vap->iv_caps & IEEE80211_C_WPA2))
288832176cfdSRui Paulo return EOPNOTSUPP;
288932176cfdSRui Paulo flags |= IEEE80211_F_WPA2;
2890841ab66cSSepherosa Ziehau break;
2891841ab66cSSepherosa Ziehau case 3:
289232176cfdSRui Paulo if ((vap->iv_caps & IEEE80211_C_WPA) != IEEE80211_C_WPA)
289332176cfdSRui Paulo return EOPNOTSUPP;
289432176cfdSRui Paulo flags |= IEEE80211_F_WPA1 | IEEE80211_F_WPA2;
2895f186073cSJoerg Sonnenberger break;
289632176cfdSRui Paulo default: /* Can't set any -> error */
289732176cfdSRui Paulo return EOPNOTSUPP;
2898f186073cSJoerg Sonnenberger }
289932176cfdSRui Paulo vap->iv_flags = flags;
290032176cfdSRui Paulo error = ERESTART; /* NB: can change beacon frame */
2901841ab66cSSepherosa Ziehau break;
2902841ab66cSSepherosa Ziehau case IEEE80211_IOC_WME:
2903841ab66cSSepherosa Ziehau if (ireq->i_val) {
290432176cfdSRui Paulo if ((vap->iv_caps & IEEE80211_C_WME) == 0)
290532176cfdSRui Paulo return EOPNOTSUPP;
290632176cfdSRui Paulo ieee80211_syncflag(vap, IEEE80211_F_WME);
2907841ab66cSSepherosa Ziehau } else
290832176cfdSRui Paulo ieee80211_syncflag(vap, -IEEE80211_F_WME);
290932176cfdSRui Paulo error = ERESTART; /* NB: can change beacon frame */
2910841ab66cSSepherosa Ziehau break;
2911841ab66cSSepherosa Ziehau case IEEE80211_IOC_HIDESSID:
2912841ab66cSSepherosa Ziehau if (ireq->i_val)
291332176cfdSRui Paulo vap->iv_flags |= IEEE80211_F_HIDESSID;
2914841ab66cSSepherosa Ziehau else
291532176cfdSRui Paulo vap->iv_flags &= ~IEEE80211_F_HIDESSID;
291632176cfdSRui Paulo error = ERESTART; /* XXX ENETRESET? */
2917f186073cSJoerg Sonnenberger break;
2918841ab66cSSepherosa Ziehau case IEEE80211_IOC_APBRIDGE:
2919841ab66cSSepherosa Ziehau if (ireq->i_val == 0)
292032176cfdSRui Paulo vap->iv_flags |= IEEE80211_F_NOBRIDGE;
2921841ab66cSSepherosa Ziehau else
292232176cfdSRui Paulo vap->iv_flags &= ~IEEE80211_F_NOBRIDGE;
2923841ab66cSSepherosa Ziehau break;
2924841ab66cSSepherosa Ziehau case IEEE80211_IOC_BSSID:
2925841ab66cSSepherosa Ziehau if (ireq->i_len != sizeof(tmpbssid))
2926841ab66cSSepherosa Ziehau return EINVAL;
2927841ab66cSSepherosa Ziehau error = copyin(ireq->i_data, tmpbssid, ireq->i_len);
2928841ab66cSSepherosa Ziehau if (error)
2929841ab66cSSepherosa Ziehau break;
293032176cfdSRui Paulo IEEE80211_ADDR_COPY(vap->iv_des_bssid, tmpbssid);
293132176cfdSRui Paulo if (IEEE80211_ADDR_EQ(vap->iv_des_bssid, zerobssid))
293232176cfdSRui Paulo vap->iv_flags &= ~IEEE80211_F_DESBSSID;
2933841ab66cSSepherosa Ziehau else
293432176cfdSRui Paulo vap->iv_flags |= IEEE80211_F_DESBSSID;
2935841ab66cSSepherosa Ziehau error = ENETRESET;
2936841ab66cSSepherosa Ziehau break;
2937841ab66cSSepherosa Ziehau case IEEE80211_IOC_CHANLIST:
293832176cfdSRui Paulo error = ieee80211_ioctl_setchanlist(vap, ireq);
2939841ab66cSSepherosa Ziehau break;
294032176cfdSRui Paulo #define OLD_IEEE80211_IOC_SCAN_REQ 23
294132176cfdSRui Paulo #ifdef OLD_IEEE80211_IOC_SCAN_REQ
294232176cfdSRui Paulo case OLD_IEEE80211_IOC_SCAN_REQ:
294332176cfdSRui Paulo IEEE80211_DPRINTF(vap, IEEE80211_MSG_SCAN,
294432176cfdSRui Paulo "%s: active scan request\n", __func__);
294532176cfdSRui Paulo /*
294632176cfdSRui Paulo * If we are in INIT state then the driver has never
294732176cfdSRui Paulo * had a chance to setup hardware state to do a scan;
294832176cfdSRui Paulo * use the state machine to get us up the SCAN state.
294932176cfdSRui Paulo * Otherwise just invoke the scan machinery to start
295032176cfdSRui Paulo * a one-time scan.
295132176cfdSRui Paulo */
295232176cfdSRui Paulo if (vap->iv_state == IEEE80211_S_INIT)
295332176cfdSRui Paulo ieee80211_new_state(vap, IEEE80211_S_SCAN, 0);
295432176cfdSRui Paulo else
295532176cfdSRui Paulo (void) ieee80211_start_scan(vap,
295632176cfdSRui Paulo IEEE80211_SCAN_ACTIVE |
295732176cfdSRui Paulo IEEE80211_SCAN_NOPICK |
295832176cfdSRui Paulo IEEE80211_SCAN_ONCE,
295932176cfdSRui Paulo IEEE80211_SCAN_FOREVER, 0, 0,
296032176cfdSRui Paulo /* XXX use ioctl params */
296132176cfdSRui Paulo vap->iv_des_nssid, vap->iv_des_ssid);
296232176cfdSRui Paulo break;
296332176cfdSRui Paulo #endif /* OLD_IEEE80211_IOC_SCAN_REQ */
2964841ab66cSSepherosa Ziehau case IEEE80211_IOC_SCAN_REQ:
296532176cfdSRui Paulo error = ieee80211_ioctl_scanreq(vap, ireq);
2966841ab66cSSepherosa Ziehau break;
296732176cfdSRui Paulo case IEEE80211_IOC_SCAN_CANCEL:
296832176cfdSRui Paulo IEEE80211_DPRINTF(vap, IEEE80211_MSG_SCAN,
296932176cfdSRui Paulo "%s: cancel scan\n", __func__);
297032176cfdSRui Paulo ieee80211_cancel_scan(vap);
297132176cfdSRui Paulo break;
297232176cfdSRui Paulo case IEEE80211_IOC_HTCONF:
297332176cfdSRui Paulo if (ireq->i_val & 1)
297432176cfdSRui Paulo ieee80211_syncflag_ht(vap, IEEE80211_FHT_HT);
297532176cfdSRui Paulo else
297632176cfdSRui Paulo ieee80211_syncflag_ht(vap, -IEEE80211_FHT_HT);
297732176cfdSRui Paulo if (ireq->i_val & 2)
297832176cfdSRui Paulo ieee80211_syncflag_ht(vap, IEEE80211_FHT_USEHT40);
297932176cfdSRui Paulo else
298032176cfdSRui Paulo ieee80211_syncflag_ht(vap, -IEEE80211_FHT_USEHT40);
298132176cfdSRui Paulo error = ENETRESET;
2982841ab66cSSepherosa Ziehau break;
2983841ab66cSSepherosa Ziehau case IEEE80211_IOC_ADDMAC:
2984841ab66cSSepherosa Ziehau case IEEE80211_IOC_DELMAC:
298532176cfdSRui Paulo error = ieee80211_ioctl_macmac(vap, ireq);
2986841ab66cSSepherosa Ziehau break;
2987841ab66cSSepherosa Ziehau case IEEE80211_IOC_MACCMD:
298832176cfdSRui Paulo error = ieee80211_ioctl_setmaccmd(vap, ireq);
2989841ab66cSSepherosa Ziehau break;
2990a63e49b9SSepherosa Ziehau case IEEE80211_IOC_STA_STATS:
299132176cfdSRui Paulo error = ieee80211_ioctl_setstastats(vap, ireq);
2992a63e49b9SSepherosa Ziehau break;
2993841ab66cSSepherosa Ziehau case IEEE80211_IOC_STA_TXPOW:
299432176cfdSRui Paulo error = ieee80211_ioctl_setstatxpow(vap, ireq);
2995841ab66cSSepherosa Ziehau break;
2996841ab66cSSepherosa Ziehau case IEEE80211_IOC_WME_CWMIN: /* WME: CWmin */
2997841ab66cSSepherosa Ziehau case IEEE80211_IOC_WME_CWMAX: /* WME: CWmax */
2998841ab66cSSepherosa Ziehau case IEEE80211_IOC_WME_AIFS: /* WME: AIFS */
2999841ab66cSSepherosa Ziehau case IEEE80211_IOC_WME_TXOPLIMIT: /* WME: txops limit */
3000841ab66cSSepherosa Ziehau case IEEE80211_IOC_WME_ACM: /* WME: ACM (bss only) */
30014f655ef5SMatthew Dillon case IEEE80211_IOC_WME_ACKPOLICY: /* WME: ACK policy (!bss only) */
300232176cfdSRui Paulo error = ieee80211_ioctl_setwmeparam(vap, ireq);
3003841ab66cSSepherosa Ziehau break;
3004841ab66cSSepherosa Ziehau case IEEE80211_IOC_DTIM_PERIOD:
300532176cfdSRui Paulo if (vap->iv_opmode != IEEE80211_M_HOSTAP &&
300632176cfdSRui Paulo vap->iv_opmode != IEEE80211_M_MBSS &&
300732176cfdSRui Paulo vap->iv_opmode != IEEE80211_M_IBSS)
3008841ab66cSSepherosa Ziehau return EINVAL;
3009841ab66cSSepherosa Ziehau if (IEEE80211_DTIM_MIN <= ireq->i_val &&
3010841ab66cSSepherosa Ziehau ireq->i_val <= IEEE80211_DTIM_MAX) {
301132176cfdSRui Paulo vap->iv_dtim_period = ireq->i_val;
3012841ab66cSSepherosa Ziehau error = ENETRESET; /* requires restart */
3013841ab66cSSepherosa Ziehau } else
3014841ab66cSSepherosa Ziehau error = EINVAL;
3015841ab66cSSepherosa Ziehau break;
3016841ab66cSSepherosa Ziehau case IEEE80211_IOC_BEACON_INTERVAL:
301732176cfdSRui Paulo if (vap->iv_opmode != IEEE80211_M_HOSTAP &&
301832176cfdSRui Paulo vap->iv_opmode != IEEE80211_M_MBSS &&
301932176cfdSRui Paulo vap->iv_opmode != IEEE80211_M_IBSS)
3020841ab66cSSepherosa Ziehau return EINVAL;
3021841ab66cSSepherosa Ziehau if (IEEE80211_BINTVAL_MIN <= ireq->i_val &&
3022841ab66cSSepherosa Ziehau ireq->i_val <= IEEE80211_BINTVAL_MAX) {
3023841ab66cSSepherosa Ziehau ic->ic_bintval = ireq->i_val;
3024841ab66cSSepherosa Ziehau error = ENETRESET; /* requires restart */
3025841ab66cSSepherosa Ziehau } else
3026841ab66cSSepherosa Ziehau error = EINVAL;
3027841ab66cSSepherosa Ziehau break;
3028841ab66cSSepherosa Ziehau case IEEE80211_IOC_PUREG:
3029841ab66cSSepherosa Ziehau if (ireq->i_val)
303032176cfdSRui Paulo vap->iv_flags |= IEEE80211_F_PUREG;
3031841ab66cSSepherosa Ziehau else
303232176cfdSRui Paulo vap->iv_flags &= ~IEEE80211_F_PUREG;
303332176cfdSRui Paulo /* NB: reset only if we're operating on an 11g channel */
303432176cfdSRui Paulo if (isvap11g(vap))
3035841ab66cSSepherosa Ziehau error = ENETRESET;
3036841ab66cSSepherosa Ziehau break;
3037085ff963SMatthew Dillon case IEEE80211_IOC_QUIET:
3038085ff963SMatthew Dillon vap->iv_quiet= ireq->i_val;
3039085ff963SMatthew Dillon break;
3040085ff963SMatthew Dillon case IEEE80211_IOC_QUIET_COUNT:
3041085ff963SMatthew Dillon vap->iv_quiet_count=ireq->i_val;
3042085ff963SMatthew Dillon break;
3043085ff963SMatthew Dillon case IEEE80211_IOC_QUIET_PERIOD:
3044085ff963SMatthew Dillon vap->iv_quiet_period=ireq->i_val;
3045085ff963SMatthew Dillon break;
3046085ff963SMatthew Dillon case IEEE80211_IOC_QUIET_OFFSET:
3047085ff963SMatthew Dillon vap->iv_quiet_offset=ireq->i_val;
3048085ff963SMatthew Dillon break;
3049085ff963SMatthew Dillon case IEEE80211_IOC_QUIET_DUR:
3050085ff963SMatthew Dillon if(ireq->i_val < vap->iv_bss->ni_intval)
3051085ff963SMatthew Dillon vap->iv_quiet_duration = ireq->i_val;
3052085ff963SMatthew Dillon else
3053085ff963SMatthew Dillon error = EINVAL;
3054085ff963SMatthew Dillon break;
305532176cfdSRui Paulo case IEEE80211_IOC_BGSCAN:
305632176cfdSRui Paulo if (ireq->i_val) {
305732176cfdSRui Paulo if ((vap->iv_caps & IEEE80211_C_BGSCAN) == 0)
305832176cfdSRui Paulo return EOPNOTSUPP;
305932176cfdSRui Paulo vap->iv_flags |= IEEE80211_F_BGSCAN;
306032176cfdSRui Paulo } else
306132176cfdSRui Paulo vap->iv_flags &= ~IEEE80211_F_BGSCAN;
306232176cfdSRui Paulo break;
306332176cfdSRui Paulo case IEEE80211_IOC_BGSCAN_IDLE:
306432176cfdSRui Paulo if (ireq->i_val >= IEEE80211_BGSCAN_IDLE_MIN)
306532176cfdSRui Paulo vap->iv_bgscanidle = ireq->i_val*hz/1000;
306632176cfdSRui Paulo else
306732176cfdSRui Paulo error = EINVAL;
306832176cfdSRui Paulo break;
306932176cfdSRui Paulo case IEEE80211_IOC_BGSCAN_INTERVAL:
307032176cfdSRui Paulo if (ireq->i_val >= IEEE80211_BGSCAN_INTVAL_MIN)
307132176cfdSRui Paulo vap->iv_bgscanintvl = ireq->i_val*hz;
307232176cfdSRui Paulo else
307332176cfdSRui Paulo error = EINVAL;
307432176cfdSRui Paulo break;
307532176cfdSRui Paulo case IEEE80211_IOC_SCANVALID:
307632176cfdSRui Paulo if (ireq->i_val >= IEEE80211_SCAN_VALID_MIN)
307732176cfdSRui Paulo vap->iv_scanvalid = ireq->i_val*hz;
307832176cfdSRui Paulo else
307932176cfdSRui Paulo error = EINVAL;
3080841ab66cSSepherosa Ziehau break;
3081841ab66cSSepherosa Ziehau case IEEE80211_IOC_FRAGTHRESHOLD:
308232176cfdSRui Paulo if ((vap->iv_caps & IEEE80211_C_TXFRAG) == 0 &&
3083841ab66cSSepherosa Ziehau ireq->i_val != IEEE80211_FRAG_MAX)
308432176cfdSRui Paulo return EOPNOTSUPP;
3085841ab66cSSepherosa Ziehau if (!(IEEE80211_FRAG_MIN <= ireq->i_val &&
3086841ab66cSSepherosa Ziehau ireq->i_val <= IEEE80211_FRAG_MAX))
3087841ab66cSSepherosa Ziehau return EINVAL;
308832176cfdSRui Paulo vap->iv_fragthreshold = ireq->i_val;
308932176cfdSRui Paulo error = ERESTART;
3090841ab66cSSepherosa Ziehau break;
3091841ab66cSSepherosa Ziehau case IEEE80211_IOC_BURST:
3092841ab66cSSepherosa Ziehau if (ireq->i_val) {
309332176cfdSRui Paulo if ((vap->iv_caps & IEEE80211_C_BURST) == 0)
309432176cfdSRui Paulo return EOPNOTSUPP;
309532176cfdSRui Paulo ieee80211_syncflag(vap, IEEE80211_F_BURST);
3096841ab66cSSepherosa Ziehau } else
309732176cfdSRui Paulo ieee80211_syncflag(vap, -IEEE80211_F_BURST);
309832176cfdSRui Paulo error = ERESTART;
3099b9334f94SSepherosa Ziehau break;
3100c36e937bSSepherosa Ziehau case IEEE80211_IOC_BMISSTHRESHOLD:
3101c36e937bSSepherosa Ziehau if (!(IEEE80211_HWBMISS_MIN <= ireq->i_val &&
3102c36e937bSSepherosa Ziehau ireq->i_val <= IEEE80211_HWBMISS_MAX))
3103c36e937bSSepherosa Ziehau return EINVAL;
310432176cfdSRui Paulo vap->iv_bmissthreshold = ireq->i_val;
310532176cfdSRui Paulo error = ERESTART;
310632176cfdSRui Paulo break;
310732176cfdSRui Paulo case IEEE80211_IOC_CURCHAN:
310832176cfdSRui Paulo error = ieee80211_ioctl_setcurchan(vap, ireq);
310932176cfdSRui Paulo break;
311032176cfdSRui Paulo case IEEE80211_IOC_SHORTGI:
311132176cfdSRui Paulo if (ireq->i_val) {
311232176cfdSRui Paulo #define IEEE80211_HTCAP_SHORTGI \
311332176cfdSRui Paulo (IEEE80211_HTCAP_SHORTGI20 | IEEE80211_HTCAP_SHORTGI40)
311432176cfdSRui Paulo if (((ireq->i_val ^ vap->iv_htcaps) & IEEE80211_HTCAP_SHORTGI) != 0)
311532176cfdSRui Paulo return EINVAL;
311632176cfdSRui Paulo if (ireq->i_val & IEEE80211_HTCAP_SHORTGI20)
311732176cfdSRui Paulo vap->iv_flags_ht |= IEEE80211_FHT_SHORTGI20;
311832176cfdSRui Paulo if (ireq->i_val & IEEE80211_HTCAP_SHORTGI40)
311932176cfdSRui Paulo vap->iv_flags_ht |= IEEE80211_FHT_SHORTGI40;
312032176cfdSRui Paulo #undef IEEE80211_HTCAP_SHORTGI
312132176cfdSRui Paulo } else
312232176cfdSRui Paulo vap->iv_flags_ht &=
312332176cfdSRui Paulo ~(IEEE80211_FHT_SHORTGI20 | IEEE80211_FHT_SHORTGI40);
312432176cfdSRui Paulo error = ERESTART;
312532176cfdSRui Paulo break;
312632176cfdSRui Paulo case IEEE80211_IOC_AMPDU:
312732176cfdSRui Paulo if (ireq->i_val && (vap->iv_htcaps & IEEE80211_HTC_AMPDU) == 0)
312832176cfdSRui Paulo return EINVAL;
312932176cfdSRui Paulo if (ireq->i_val & 1)
313032176cfdSRui Paulo vap->iv_flags_ht |= IEEE80211_FHT_AMPDU_TX;
313132176cfdSRui Paulo else
313232176cfdSRui Paulo vap->iv_flags_ht &= ~IEEE80211_FHT_AMPDU_TX;
313332176cfdSRui Paulo if (ireq->i_val & 2)
313432176cfdSRui Paulo vap->iv_flags_ht |= IEEE80211_FHT_AMPDU_RX;
313532176cfdSRui Paulo else
313632176cfdSRui Paulo vap->iv_flags_ht &= ~IEEE80211_FHT_AMPDU_RX;
313732176cfdSRui Paulo /* NB: reset only if we're operating on an 11n channel */
313832176cfdSRui Paulo if (isvapht(vap))
313932176cfdSRui Paulo error = ERESTART;
314032176cfdSRui Paulo break;
314132176cfdSRui Paulo case IEEE80211_IOC_AMPDU_LIMIT:
314232176cfdSRui Paulo if (!(IEEE80211_HTCAP_MAXRXAMPDU_8K <= ireq->i_val &&
314332176cfdSRui Paulo ireq->i_val <= IEEE80211_HTCAP_MAXRXAMPDU_64K))
314432176cfdSRui Paulo return EINVAL;
314532176cfdSRui Paulo if (vap->iv_opmode == IEEE80211_M_HOSTAP)
314632176cfdSRui Paulo vap->iv_ampdu_rxmax = ireq->i_val;
314732176cfdSRui Paulo else
314832176cfdSRui Paulo vap->iv_ampdu_limit = ireq->i_val;
314932176cfdSRui Paulo error = ERESTART;
315032176cfdSRui Paulo break;
315132176cfdSRui Paulo case IEEE80211_IOC_AMPDU_DENSITY:
315232176cfdSRui Paulo if (!(IEEE80211_HTCAP_MPDUDENSITY_NA <= ireq->i_val &&
315332176cfdSRui Paulo ireq->i_val <= IEEE80211_HTCAP_MPDUDENSITY_16))
315432176cfdSRui Paulo return EINVAL;
315532176cfdSRui Paulo vap->iv_ampdu_density = ireq->i_val;
315632176cfdSRui Paulo error = ERESTART;
315732176cfdSRui Paulo break;
315832176cfdSRui Paulo case IEEE80211_IOC_AMSDU:
315932176cfdSRui Paulo if (ireq->i_val && (vap->iv_htcaps & IEEE80211_HTC_AMSDU) == 0)
316032176cfdSRui Paulo return EINVAL;
316132176cfdSRui Paulo if (ireq->i_val & 1)
316232176cfdSRui Paulo vap->iv_flags_ht |= IEEE80211_FHT_AMSDU_TX;
316332176cfdSRui Paulo else
316432176cfdSRui Paulo vap->iv_flags_ht &= ~IEEE80211_FHT_AMSDU_TX;
316532176cfdSRui Paulo if (ireq->i_val & 2)
316632176cfdSRui Paulo vap->iv_flags_ht |= IEEE80211_FHT_AMSDU_RX;
316732176cfdSRui Paulo else
316832176cfdSRui Paulo vap->iv_flags_ht &= ~IEEE80211_FHT_AMSDU_RX;
316932176cfdSRui Paulo /* NB: reset only if we're operating on an 11n channel */
317032176cfdSRui Paulo if (isvapht(vap))
317132176cfdSRui Paulo error = ERESTART;
317232176cfdSRui Paulo break;
317332176cfdSRui Paulo case IEEE80211_IOC_AMSDU_LIMIT:
317432176cfdSRui Paulo /* XXX validate */
317532176cfdSRui Paulo vap->iv_amsdu_limit = ireq->i_val; /* XXX truncation? */
317632176cfdSRui Paulo break;
317732176cfdSRui Paulo case IEEE80211_IOC_PUREN:
317832176cfdSRui Paulo if (ireq->i_val) {
317932176cfdSRui Paulo if ((vap->iv_flags_ht & IEEE80211_FHT_HT) == 0)
318032176cfdSRui Paulo return EINVAL;
318132176cfdSRui Paulo vap->iv_flags_ht |= IEEE80211_FHT_PUREN;
318232176cfdSRui Paulo } else
318332176cfdSRui Paulo vap->iv_flags_ht &= ~IEEE80211_FHT_PUREN;
318432176cfdSRui Paulo /* NB: reset only if we're operating on an 11n channel */
318532176cfdSRui Paulo if (isvapht(vap))
318632176cfdSRui Paulo error = ERESTART;
318732176cfdSRui Paulo break;
318832176cfdSRui Paulo case IEEE80211_IOC_DOTH:
318932176cfdSRui Paulo if (ireq->i_val) {
319032176cfdSRui Paulo #if 0
319132176cfdSRui Paulo /* XXX no capability */
319232176cfdSRui Paulo if ((vap->iv_caps & IEEE80211_C_DOTH) == 0)
319332176cfdSRui Paulo return EOPNOTSUPP;
319432176cfdSRui Paulo #endif
319532176cfdSRui Paulo vap->iv_flags |= IEEE80211_F_DOTH;
319632176cfdSRui Paulo } else
319732176cfdSRui Paulo vap->iv_flags &= ~IEEE80211_F_DOTH;
319832176cfdSRui Paulo error = ENETRESET;
319932176cfdSRui Paulo break;
320032176cfdSRui Paulo case IEEE80211_IOC_REGDOMAIN:
320132176cfdSRui Paulo error = ieee80211_ioctl_setregdomain(vap, ireq);
320232176cfdSRui Paulo break;
320332176cfdSRui Paulo case IEEE80211_IOC_ROAM:
320432176cfdSRui Paulo error = ieee80211_ioctl_setroam(vap, ireq);
320532176cfdSRui Paulo break;
320632176cfdSRui Paulo case IEEE80211_IOC_TXPARAMS:
320732176cfdSRui Paulo error = ieee80211_ioctl_settxparams(vap, ireq);
320832176cfdSRui Paulo break;
320932176cfdSRui Paulo case IEEE80211_IOC_HTCOMPAT:
321032176cfdSRui Paulo if (ireq->i_val) {
321132176cfdSRui Paulo if ((vap->iv_flags_ht & IEEE80211_FHT_HT) == 0)
321232176cfdSRui Paulo return EOPNOTSUPP;
321332176cfdSRui Paulo vap->iv_flags_ht |= IEEE80211_FHT_HTCOMPAT;
321432176cfdSRui Paulo } else
321532176cfdSRui Paulo vap->iv_flags_ht &= ~IEEE80211_FHT_HTCOMPAT;
321632176cfdSRui Paulo /* NB: reset only if we're operating on an 11n channel */
321732176cfdSRui Paulo if (isvapht(vap))
321832176cfdSRui Paulo error = ERESTART;
321932176cfdSRui Paulo break;
322032176cfdSRui Paulo case IEEE80211_IOC_DWDS:
322132176cfdSRui Paulo if (ireq->i_val) {
322232176cfdSRui Paulo /* NB: DWDS only makes sense for WDS-capable devices */
322332176cfdSRui Paulo if ((ic->ic_caps & IEEE80211_C_WDS) == 0)
322432176cfdSRui Paulo return EOPNOTSUPP;
322532176cfdSRui Paulo /* NB: DWDS is used only with ap+sta vaps */
322632176cfdSRui Paulo if (vap->iv_opmode != IEEE80211_M_HOSTAP &&
322732176cfdSRui Paulo vap->iv_opmode != IEEE80211_M_STA)
322832176cfdSRui Paulo return EINVAL;
322932176cfdSRui Paulo vap->iv_flags |= IEEE80211_F_DWDS;
323032176cfdSRui Paulo if (vap->iv_opmode == IEEE80211_M_STA)
323132176cfdSRui Paulo vap->iv_flags_ext |= IEEE80211_FEXT_4ADDR;
323232176cfdSRui Paulo } else {
323332176cfdSRui Paulo vap->iv_flags &= ~IEEE80211_F_DWDS;
323432176cfdSRui Paulo if (vap->iv_opmode == IEEE80211_M_STA)
323532176cfdSRui Paulo vap->iv_flags_ext &= ~IEEE80211_FEXT_4ADDR;
323632176cfdSRui Paulo }
323732176cfdSRui Paulo break;
323832176cfdSRui Paulo case IEEE80211_IOC_INACTIVITY:
323932176cfdSRui Paulo if (ireq->i_val)
324032176cfdSRui Paulo vap->iv_flags_ext |= IEEE80211_FEXT_INACT;
324132176cfdSRui Paulo else
324232176cfdSRui Paulo vap->iv_flags_ext &= ~IEEE80211_FEXT_INACT;
324332176cfdSRui Paulo break;
324432176cfdSRui Paulo case IEEE80211_IOC_APPIE:
324532176cfdSRui Paulo error = ieee80211_ioctl_setappie(vap, ireq);
324632176cfdSRui Paulo break;
324732176cfdSRui Paulo case IEEE80211_IOC_WPS:
324832176cfdSRui Paulo if (ireq->i_val) {
324932176cfdSRui Paulo if ((vap->iv_caps & IEEE80211_C_WPA) == 0)
325032176cfdSRui Paulo return EOPNOTSUPP;
325132176cfdSRui Paulo vap->iv_flags_ext |= IEEE80211_FEXT_WPS;
325232176cfdSRui Paulo } else
325332176cfdSRui Paulo vap->iv_flags_ext &= ~IEEE80211_FEXT_WPS;
325432176cfdSRui Paulo break;
325532176cfdSRui Paulo case IEEE80211_IOC_TSN:
325632176cfdSRui Paulo if (ireq->i_val) {
325732176cfdSRui Paulo if ((vap->iv_caps & IEEE80211_C_WPA) == 0)
325832176cfdSRui Paulo return EOPNOTSUPP;
325932176cfdSRui Paulo vap->iv_flags_ext |= IEEE80211_FEXT_TSN;
326032176cfdSRui Paulo } else
326132176cfdSRui Paulo vap->iv_flags_ext &= ~IEEE80211_FEXT_TSN;
326232176cfdSRui Paulo break;
326332176cfdSRui Paulo case IEEE80211_IOC_CHANSWITCH:
326432176cfdSRui Paulo error = ieee80211_ioctl_chanswitch(vap, ireq);
326532176cfdSRui Paulo break;
326632176cfdSRui Paulo case IEEE80211_IOC_DFS:
326732176cfdSRui Paulo if (ireq->i_val) {
326832176cfdSRui Paulo if ((vap->iv_caps & IEEE80211_C_DFS) == 0)
326932176cfdSRui Paulo return EOPNOTSUPP;
327032176cfdSRui Paulo /* NB: DFS requires 11h support */
327132176cfdSRui Paulo if ((vap->iv_flags & IEEE80211_F_DOTH) == 0)
327232176cfdSRui Paulo return EINVAL;
327332176cfdSRui Paulo vap->iv_flags_ext |= IEEE80211_FEXT_DFS;
327432176cfdSRui Paulo } else
327532176cfdSRui Paulo vap->iv_flags_ext &= ~IEEE80211_FEXT_DFS;
327632176cfdSRui Paulo break;
327732176cfdSRui Paulo case IEEE80211_IOC_DOTD:
327832176cfdSRui Paulo if (ireq->i_val)
327932176cfdSRui Paulo vap->iv_flags_ext |= IEEE80211_FEXT_DOTD;
328032176cfdSRui Paulo else
328132176cfdSRui Paulo vap->iv_flags_ext &= ~IEEE80211_FEXT_DOTD;
328232176cfdSRui Paulo if (vap->iv_opmode == IEEE80211_M_STA)
328332176cfdSRui Paulo error = ENETRESET;
328432176cfdSRui Paulo break;
328532176cfdSRui Paulo case IEEE80211_IOC_HTPROTMODE:
328632176cfdSRui Paulo if (ireq->i_val > IEEE80211_PROT_RTSCTS)
328732176cfdSRui Paulo return EINVAL;
328832176cfdSRui Paulo ic->ic_htprotmode = ireq->i_val ?
328932176cfdSRui Paulo IEEE80211_PROT_RTSCTS : IEEE80211_PROT_NONE;
329032176cfdSRui Paulo /* NB: if not operating in 11n this can wait */
329132176cfdSRui Paulo if (isvapht(vap))
329232176cfdSRui Paulo error = ERESTART;
329332176cfdSRui Paulo break;
329432176cfdSRui Paulo case IEEE80211_IOC_STA_VLAN:
329532176cfdSRui Paulo error = ieee80211_ioctl_setstavlan(vap, ireq);
329632176cfdSRui Paulo break;
329732176cfdSRui Paulo case IEEE80211_IOC_SMPS:
329832176cfdSRui Paulo if ((ireq->i_val &~ IEEE80211_HTCAP_SMPS) != 0 ||
329932176cfdSRui Paulo ireq->i_val == 0x0008) /* value of 2 is reserved */
330032176cfdSRui Paulo return EINVAL;
330132176cfdSRui Paulo if (ireq->i_val != IEEE80211_HTCAP_SMPS_OFF &&
330232176cfdSRui Paulo (vap->iv_htcaps & IEEE80211_HTC_SMPS) == 0)
330332176cfdSRui Paulo return EOPNOTSUPP;
330432176cfdSRui Paulo vap->iv_htcaps = (vap->iv_htcaps &~ IEEE80211_HTCAP_SMPS) |
330532176cfdSRui Paulo ireq->i_val;
330632176cfdSRui Paulo /* NB: if not operating in 11n this can wait */
330732176cfdSRui Paulo if (isvapht(vap))
330832176cfdSRui Paulo error = ERESTART;
330932176cfdSRui Paulo break;
331032176cfdSRui Paulo case IEEE80211_IOC_RIFS:
331132176cfdSRui Paulo if (ireq->i_val != 0) {
331232176cfdSRui Paulo if ((vap->iv_htcaps & IEEE80211_HTC_RIFS) == 0)
331332176cfdSRui Paulo return EOPNOTSUPP;
331432176cfdSRui Paulo vap->iv_flags_ht |= IEEE80211_FHT_RIFS;
331532176cfdSRui Paulo } else
331632176cfdSRui Paulo vap->iv_flags_ht &= ~IEEE80211_FHT_RIFS;
331732176cfdSRui Paulo /* NB: if not operating in 11n this can wait */
331832176cfdSRui Paulo if (isvapht(vap))
331932176cfdSRui Paulo error = ERESTART;
3320c36e937bSSepherosa Ziehau break;
33214f655ef5SMatthew Dillon case IEEE80211_IOC_STBC:
33224f655ef5SMatthew Dillon /* Check if we can do STBC TX/RX before changing the setting */
33234f655ef5SMatthew Dillon if ((ireq->i_val & 1) &&
33244f655ef5SMatthew Dillon ((vap->iv_htcaps & IEEE80211_HTCAP_TXSTBC) == 0))
33254f655ef5SMatthew Dillon return EOPNOTSUPP;
33264f655ef5SMatthew Dillon if ((ireq->i_val & 2) &&
33274f655ef5SMatthew Dillon ((vap->iv_htcaps & IEEE80211_HTCAP_RXSTBC) == 0))
33284f655ef5SMatthew Dillon return EOPNOTSUPP;
33294f655ef5SMatthew Dillon
33304f655ef5SMatthew Dillon /* TX */
33314f655ef5SMatthew Dillon if (ireq->i_val & 1)
33324f655ef5SMatthew Dillon vap->iv_flags_ht |= IEEE80211_FHT_STBC_TX;
33334f655ef5SMatthew Dillon else
33344f655ef5SMatthew Dillon vap->iv_flags_ht &= ~IEEE80211_FHT_STBC_TX;
33354f655ef5SMatthew Dillon
33364f655ef5SMatthew Dillon /* RX */
33374f655ef5SMatthew Dillon if (ireq->i_val & 2)
33384f655ef5SMatthew Dillon vap->iv_flags_ht |= IEEE80211_FHT_STBC_RX;
33394f655ef5SMatthew Dillon else
33404f655ef5SMatthew Dillon vap->iv_flags_ht &= ~IEEE80211_FHT_STBC_RX;
33414f655ef5SMatthew Dillon
33424f655ef5SMatthew Dillon /* NB: reset only if we're operating on an 11n channel */
33434f655ef5SMatthew Dillon if (isvapht(vap))
33444f655ef5SMatthew Dillon error = ERESTART;
33454f655ef5SMatthew Dillon break;
3346f186073cSJoerg Sonnenberger default:
334732176cfdSRui Paulo error = ieee80211_ioctl_setdefault(vap, ireq);
3348f186073cSJoerg Sonnenberger break;
3349f186073cSJoerg Sonnenberger }
335032176cfdSRui Paulo /*
335132176cfdSRui Paulo * The convention is that ENETRESET means an operation
335232176cfdSRui Paulo * requires a complete re-initialization of the device (e.g.
335332176cfdSRui Paulo * changing something that affects the association state).
335432176cfdSRui Paulo * ERESTART means the request may be handled with only a
335532176cfdSRui Paulo * reload of the hardware state. We hand ERESTART requests
335632176cfdSRui Paulo * to the iv_reset callback so the driver can decide. If
335732176cfdSRui Paulo * a device does not fillin iv_reset then it defaults to one
335832176cfdSRui Paulo * that returns ENETRESET. Otherwise a driver may return
335932176cfdSRui Paulo * ENETRESET (in which case a full reset will be done) or
336032176cfdSRui Paulo * 0 to mean there's no need to do anything (e.g. when the
336132176cfdSRui Paulo * change has no effect on the driver/device).
336232176cfdSRui Paulo */
336332176cfdSRui Paulo if (error == ERESTART)
336432176cfdSRui Paulo error = IFNET_IS_UP_RUNNING(vap->iv_ifp) ?
336532176cfdSRui Paulo vap->iv_reset(vap, ireq->i_type) : 0;
336632176cfdSRui Paulo if (error == ENETRESET) {
336732176cfdSRui Paulo /* XXX need to re-think AUTO handling */
336832176cfdSRui Paulo if (IS_UP_AUTO(vap))
336932176cfdSRui Paulo ieee80211_init(vap);
3370841ab66cSSepherosa Ziehau error = 0;
337132176cfdSRui Paulo }
3372841ab66cSSepherosa Ziehau return error;
3373841ab66cSSepherosa Ziehau }
3374841ab66cSSepherosa Ziehau
3375085ff963SMatthew Dillon #if defined(__DragonFly__)
337632176cfdSRui Paulo int
ieee80211_ioctl(struct ifnet * ifp,u_long cmd,caddr_t data,struct ucred * ucred)337734a60cf6SRui Paulo ieee80211_ioctl(struct ifnet *ifp, u_long cmd, caddr_t data, struct ucred *ucred)
3378085ff963SMatthew Dillon #else
3379085ff963SMatthew Dillon int
3380085ff963SMatthew Dillon ieee80211_ioctl(struct ifnet *ifp, u_long cmd, caddr_t data)
3381085ff963SMatthew Dillon #endif
338232176cfdSRui Paulo {
338332176cfdSRui Paulo struct ieee80211vap *vap = ifp->if_softc;
338432176cfdSRui Paulo struct ieee80211com *ic = vap->iv_ic;
33854f655ef5SMatthew Dillon int error = 0, wait = 0;
3386841ab66cSSepherosa Ziehau struct ifreq *ifr;
3387085ff963SMatthew Dillon struct ifaddr *ifa; /* XXX */
338893d6499bSJohannes Hofmann
3389841ab66cSSepherosa Ziehau switch (cmd) {
339032176cfdSRui Paulo case SIOCSIFFLAGS:
3391085ff963SMatthew Dillon IEEE80211_LOCK(ic);
33924f655ef5SMatthew Dillon if ((ifp->if_flags ^ vap->iv_ifflags) & IFF_PROMISC) {
33934f655ef5SMatthew Dillon /*
33944f655ef5SMatthew Dillon * Enable promiscuous mode when:
33954f655ef5SMatthew Dillon * 1. Interface is not a member of bridge, or
33964f655ef5SMatthew Dillon * 2. Requested by user, or
33974f655ef5SMatthew Dillon * 3. In monitor (or adhoc-demo) mode.
33984f655ef5SMatthew Dillon */
33994f655ef5SMatthew Dillon if (ifp->if_bridge == NULL ||
34004f655ef5SMatthew Dillon (ifp->if_flags & IFF_PPROMISC) != 0 ||
34014f655ef5SMatthew Dillon vap->iv_opmode == IEEE80211_M_MONITOR ||
34024f655ef5SMatthew Dillon (vap->iv_opmode == IEEE80211_M_AHDEMO &&
34034f655ef5SMatthew Dillon (vap->iv_caps & IEEE80211_C_TDMA) == 0)) {
34044f655ef5SMatthew Dillon ieee80211_promisc(vap,
34054f655ef5SMatthew Dillon ifp->if_flags & IFF_PROMISC);
34064f655ef5SMatthew Dillon vap->iv_ifflags ^= IFF_PROMISC;
34074f655ef5SMatthew Dillon }
34084f655ef5SMatthew Dillon }
34094f655ef5SMatthew Dillon if ((ifp->if_flags ^ vap->iv_ifflags) & IFF_ALLMULTI) {
34104f655ef5SMatthew Dillon ieee80211_allmulti(vap, ifp->if_flags & IFF_ALLMULTI);
34114f655ef5SMatthew Dillon vap->iv_ifflags ^= IFF_ALLMULTI;
34124f655ef5SMatthew Dillon }
341332176cfdSRui Paulo if (ifp->if_flags & IFF_UP) {
341432176cfdSRui Paulo /*
341532176cfdSRui Paulo * Bring ourself up unless we're already operational.
341632176cfdSRui Paulo * If we're the first vap and the parent is not up
341732176cfdSRui Paulo * then it will automatically be brought up as a
341832176cfdSRui Paulo * side-effect of bringing ourself up.
341932176cfdSRui Paulo */
34204f655ef5SMatthew Dillon if (vap->iv_state == IEEE80211_S_INIT) {
34214f655ef5SMatthew Dillon if (ic->ic_nrunning == 0)
34224f655ef5SMatthew Dillon wait = 1;
342332176cfdSRui Paulo ieee80211_start_locked(vap);
34244f655ef5SMatthew Dillon }
3425085ff963SMatthew Dillon } else if (ifp->if_drv_flags & IFF_DRV_RUNNING) {
342632176cfdSRui Paulo /*
342732176cfdSRui Paulo * Stop ourself. If we are the last vap to be
342832176cfdSRui Paulo * marked down the parent will also be taken down.
342932176cfdSRui Paulo */
34304f655ef5SMatthew Dillon if (ic->ic_nrunning == 1)
34314f655ef5SMatthew Dillon wait = 1;
343232176cfdSRui Paulo ieee80211_stop_locked(vap);
343332176cfdSRui Paulo }
3434085ff963SMatthew Dillon IEEE80211_UNLOCK(ic);
343532176cfdSRui Paulo /* Wait for parent ioctl handler if it was queued */
34364f655ef5SMatthew Dillon if (wait) {
3437085ff963SMatthew Dillon #if defined(__DragonFly__)
3438085ff963SMatthew Dillon /* DragonFly: release serializer to avoid deadlock */
3439085ff963SMatthew Dillon wlan_serialize_exit();
3440085ff963SMatthew Dillon #endif
34414f655ef5SMatthew Dillon ieee80211_waitfor_parent(ic);
34424f655ef5SMatthew Dillon
34434f655ef5SMatthew Dillon /*
34444f655ef5SMatthew Dillon * Check if the MAC address was changed
34454f655ef5SMatthew Dillon * via SIOCSIFLLADDR ioctl.
34464f655ef5SMatthew Dillon */
34474f655ef5SMatthew Dillon if ((ifp->if_flags & IFF_UP) == 0 &&
34484f655ef5SMatthew Dillon !IEEE80211_ADDR_EQ(vap->iv_myaddr, IF_LLADDR(ifp)))
34494f655ef5SMatthew Dillon IEEE80211_ADDR_COPY(vap->iv_myaddr,
34504f655ef5SMatthew Dillon IF_LLADDR(ifp));
34514f655ef5SMatthew Dillon #if defined(__DragonFly__)
34524f655ef5SMatthew Dillon wlan_serialize_enter();
34534f655ef5SMatthew Dillon #endif
34544f655ef5SMatthew Dillon }
345532176cfdSRui Paulo break;
345632176cfdSRui Paulo case SIOCADDMULTI:
345732176cfdSRui Paulo case SIOCDELMULTI:
3458085ff963SMatthew Dillon /* DragonFly: serializer must be held */
34594f655ef5SMatthew Dillon ieee80211_runtask(ic, &ic->ic_mcast_task);
346032176cfdSRui Paulo break;
3461841ab66cSSepherosa Ziehau case SIOCSIFMEDIA:
3462841ab66cSSepherosa Ziehau case SIOCGIFMEDIA:
3463085ff963SMatthew Dillon /* DragonFly: serializer must be held */
346432176cfdSRui Paulo ifr = (struct ifreq *)data;
346532176cfdSRui Paulo error = ifmedia_ioctl(ifp, ifr, &vap->iv_media, cmd);
3466841ab66cSSepherosa Ziehau break;
3467841ab66cSSepherosa Ziehau case SIOCG80211:
346832176cfdSRui Paulo error = ieee80211_ioctl_get80211(vap, cmd,
3469841ab66cSSepherosa Ziehau (struct ieee80211req *) data);
3470f186073cSJoerg Sonnenberger break;
347132176cfdSRui Paulo case SIOCS80211:
3472*2b3f93eaSMatthew Dillon error = caps_priv_check_self(SYSCAP_NONET_WIFI);
347332176cfdSRui Paulo if (error == 0)
347432176cfdSRui Paulo error = ieee80211_ioctl_set80211(vap, cmd,
347532176cfdSRui Paulo (struct ieee80211req *) data);
3476f186073cSJoerg Sonnenberger break;
3477f186073cSJoerg Sonnenberger case SIOCG80211STATS:
3478f186073cSJoerg Sonnenberger ifr = (struct ifreq *)data;
347932176cfdSRui Paulo copyout(&vap->iv_stats, ifr->ifr_data, sizeof (vap->iv_stats));
3480f186073cSJoerg Sonnenberger break;
3481f186073cSJoerg Sonnenberger case SIOCSIFMTU:
3482f186073cSJoerg Sonnenberger ifr = (struct ifreq *)data;
3483f186073cSJoerg Sonnenberger if (!(IEEE80211_MTU_MIN <= ifr->ifr_mtu &&
3484f186073cSJoerg Sonnenberger ifr->ifr_mtu <= IEEE80211_MTU_MAX))
3485f186073cSJoerg Sonnenberger error = EINVAL;
3486f186073cSJoerg Sonnenberger else
3487f186073cSJoerg Sonnenberger ifp->if_mtu = ifr->ifr_mtu;
3488f186073cSJoerg Sonnenberger break;
3489085ff963SMatthew Dillon case SIOCSIFADDR:
3490085ff963SMatthew Dillon /*
34914f655ef5SMatthew Dillon * XXX Handle this directly so we can suppress if_init calls.
3492085ff963SMatthew Dillon * XXX This should be done in ether_ioctl but for the moment
3493085ff963SMatthew Dillon * XXX there are too many other parts of the system that
34944f655ef5SMatthew Dillon * XXX set IFF_UP and so suppress if_init being called when
3495085ff963SMatthew Dillon * XXX it should be.
3496085ff963SMatthew Dillon */
3497085ff963SMatthew Dillon ifa = (struct ifaddr *) data;
3498085ff963SMatthew Dillon switch (ifa->ifa_addr->sa_family) {
3499085ff963SMatthew Dillon #ifdef INET
3500085ff963SMatthew Dillon case AF_INET:
3501085ff963SMatthew Dillon if ((ifp->if_flags & IFF_UP) == 0) {
3502085ff963SMatthew Dillon ifp->if_flags |= IFF_UP;
3503085ff963SMatthew Dillon ifp->if_init(ifp->if_softc);
3504085ff963SMatthew Dillon }
3505085ff963SMatthew Dillon arp_ifinit(ifp, ifa);
3506085ff963SMatthew Dillon break;
3507085ff963SMatthew Dillon #endif
3508085ff963SMatthew Dillon default:
3509085ff963SMatthew Dillon if ((ifp->if_flags & IFF_UP) == 0) {
3510085ff963SMatthew Dillon ifp->if_flags |= IFF_UP;
3511085ff963SMatthew Dillon ifp->if_init(ifp->if_softc);
3512085ff963SMatthew Dillon }
3513085ff963SMatthew Dillon break;
3514085ff963SMatthew Dillon }
3515085ff963SMatthew Dillon break;
3516f186073cSJoerg Sonnenberger default:
35174f655ef5SMatthew Dillon /*
35184f655ef5SMatthew Dillon * Pass unknown ioctls first to the driver, and if it
35194f655ef5SMatthew Dillon * returns ENOTTY, then to the generic Ethernet handler.
35204f655ef5SMatthew Dillon */
35214f655ef5SMatthew Dillon if (ic->ic_ioctl != NULL &&
35224f655ef5SMatthew Dillon (error = ic->ic_ioctl(ic, cmd, data)) != ENOTTY)
35234f655ef5SMatthew Dillon break;
3524085ff963SMatthew Dillon /* DragonFly: serializer must be held */
3525f186073cSJoerg Sonnenberger error = ether_ioctl(ifp, cmd, data);
3526f186073cSJoerg Sonnenberger break;
3527f186073cSJoerg Sonnenberger }
35284f655ef5SMatthew Dillon return (error);
3529f186073cSJoerg Sonnenberger }
3530