1 | // SPDX-License-Identifier: (GPL-2.0 OR MPL-1.1) |
2 | /* |
3 | * |
4 | * Request/Indication/MacMgmt interface handling functions |
5 | * |
6 | * Copyright (C) 1999 AbsoluteValue Systems, Inc. All Rights Reserved. |
7 | * -------------------------------------------------------------------- |
8 | * |
9 | * linux-wlan |
10 | * |
11 | * -------------------------------------------------------------------- |
12 | * |
13 | * Inquiries regarding the linux-wlan Open Source project can be |
14 | * made directly to: |
15 | * |
16 | * AbsoluteValue Systems Inc. |
17 | * info@linux-wlan.com |
18 | * http://www.linux-wlan.com |
19 | * |
20 | * -------------------------------------------------------------------- |
21 | * |
22 | * Portions of the development of this software were funded by |
23 | * Intersil Corporation as part of PRISM(R) chipset product development. |
24 | * |
25 | * -------------------------------------------------------------------- |
26 | * |
27 | * This file contains the functions, types, and macros to support the |
28 | * MLME request interface that's implemented via the device ioctls. |
29 | * |
30 | * -------------------------------------------------------------------- |
31 | */ |
32 | |
33 | #include <linux/module.h> |
34 | #include <linux/kernel.h> |
35 | #include <linux/sched.h> |
36 | #include <linux/types.h> |
37 | #include <linux/skbuff.h> |
38 | #include <linux/wireless.h> |
39 | #include <linux/netdevice.h> |
40 | #include <linux/etherdevice.h> |
41 | #include <net/sock.h> |
42 | #include <linux/netlink.h> |
43 | |
44 | #include "p80211types.h" |
45 | #include "p80211hdr.h" |
46 | #include "p80211mgmt.h" |
47 | #include "p80211conv.h" |
48 | #include "p80211msg.h" |
49 | #include "p80211netdev.h" |
50 | #include "p80211ioctl.h" |
51 | #include "p80211metadef.h" |
52 | #include "p80211metastruct.h" |
53 | #include "p80211req.h" |
54 | |
55 | static void p80211req_handlemsg(struct wlandevice *wlandev, |
56 | struct p80211msg *msg); |
57 | static void p80211req_mibset_mibget(struct wlandevice *wlandev, |
58 | struct p80211msg_dot11req_mibget *mib_msg, |
59 | int isget); |
60 | |
61 | static void p80211req_handle_action(struct wlandevice *wlandev, u32 *data, |
62 | int isget, u32 flag) |
63 | { |
64 | if (isget) { |
65 | if (wlandev->hostwep & flag) |
66 | *data = P80211ENUM_truth_true; |
67 | else |
68 | *data = P80211ENUM_truth_false; |
69 | } else { |
70 | wlandev->hostwep &= ~flag; |
71 | if (*data == P80211ENUM_truth_true) |
72 | wlandev->hostwep |= flag; |
73 | } |
74 | } |
75 | |
76 | /*---------------------------------------------------------------- |
77 | * p80211req_dorequest |
78 | * |
79 | * Handles an MLME request/confirm message. |
80 | * |
81 | * Arguments: |
82 | * wlandev WLAN device struct |
83 | * msgbuf Buffer containing a request message |
84 | * |
85 | * Returns: |
86 | * 0 on success, an errno otherwise |
87 | * |
88 | * Call context: |
89 | * Potentially blocks the caller, so it's a good idea to |
90 | * not call this function from an interrupt context. |
91 | *---------------------------------------------------------------- |
92 | */ |
93 | int p80211req_dorequest(struct wlandevice *wlandev, u8 *msgbuf) |
94 | { |
95 | struct p80211msg *msg = (struct p80211msg *)msgbuf; |
96 | |
97 | /* Check to make sure the MSD is running */ |
98 | if (!((wlandev->msdstate == WLAN_MSD_HWPRESENT && |
99 | msg->msgcode == DIDMSG_LNXREQ_IFSTATE) || |
100 | wlandev->msdstate == WLAN_MSD_RUNNING || |
101 | wlandev->msdstate == WLAN_MSD_FWLOAD)) { |
102 | return -ENODEV; |
103 | } |
104 | |
105 | /* Check Permissions */ |
106 | if (!capable(CAP_NET_ADMIN) && |
107 | (msg->msgcode != DIDMSG_DOT11REQ_MIBGET)) { |
108 | netdev_err(dev: wlandev->netdev, |
109 | format: "%s: only dot11req_mibget allowed for non-root.\n" , |
110 | wlandev->name); |
111 | return -EPERM; |
112 | } |
113 | |
114 | /* Check for busy status */ |
115 | if (test_and_set_bit(nr: 1, addr: &wlandev->request_pending)) |
116 | return -EBUSY; |
117 | |
118 | /* Allow p80211 to look at msg and handle if desired. */ |
119 | /* So far, all p80211 msgs are immediate, no waitq/timer necessary */ |
120 | /* This may change. */ |
121 | p80211req_handlemsg(wlandev, msg); |
122 | |
123 | /* Pass it down to wlandev via wlandev->mlmerequest */ |
124 | if (wlandev->mlmerequest) |
125 | wlandev->mlmerequest(wlandev, msg); |
126 | |
127 | clear_bit(nr: 1, addr: &wlandev->request_pending); |
128 | return 0; /* if result==0, msg->status still may contain an err */ |
129 | } |
130 | |
131 | /*---------------------------------------------------------------- |
132 | * p80211req_handlemsg |
133 | * |
134 | * p80211 message handler. Primarily looks for messages that |
135 | * belong to p80211 and then dispatches the appropriate response. |
136 | * TODO: we don't do anything yet. Once the linuxMIB is better |
137 | * defined we'll need a get/set handler. |
138 | * |
139 | * Arguments: |
140 | * wlandev WLAN device struct |
141 | * msg message structure |
142 | * |
143 | * Returns: |
144 | * nothing (any results are set in the status field of the msg) |
145 | * |
146 | * Call context: |
147 | * Process thread |
148 | *---------------------------------------------------------------- |
149 | */ |
150 | static void p80211req_handlemsg(struct wlandevice *wlandev, |
151 | struct p80211msg *msg) |
152 | { |
153 | switch (msg->msgcode) { |
154 | case DIDMSG_LNXREQ_HOSTWEP: { |
155 | struct p80211msg_lnxreq_hostwep *req = |
156 | (struct p80211msg_lnxreq_hostwep *)msg; |
157 | wlandev->hostwep &= |
158 | ~(HOSTWEP_DECRYPT | HOSTWEP_ENCRYPT); |
159 | if (req->decrypt.data == P80211ENUM_truth_true) |
160 | wlandev->hostwep |= HOSTWEP_DECRYPT; |
161 | if (req->encrypt.data == P80211ENUM_truth_true) |
162 | wlandev->hostwep |= HOSTWEP_ENCRYPT; |
163 | |
164 | break; |
165 | } |
166 | case DIDMSG_DOT11REQ_MIBGET: |
167 | case DIDMSG_DOT11REQ_MIBSET: { |
168 | int isget = (msg->msgcode == DIDMSG_DOT11REQ_MIBGET); |
169 | struct p80211msg_dot11req_mibget *mib_msg = |
170 | (struct p80211msg_dot11req_mibget *)msg; |
171 | p80211req_mibset_mibget(wlandev, mib_msg, isget); |
172 | break; |
173 | } |
174 | } /* switch msg->msgcode */ |
175 | } |
176 | |
177 | static void p80211req_mibset_mibget(struct wlandevice *wlandev, |
178 | struct p80211msg_dot11req_mibget *mib_msg, |
179 | int isget) |
180 | { |
181 | struct p80211itemd *mibitem = |
182 | (struct p80211itemd *)mib_msg->mibattribute.data; |
183 | struct p80211pstrd *pstr = (struct p80211pstrd *)mibitem->data; |
184 | u8 *key = mibitem->data + sizeof(struct p80211pstrd); |
185 | |
186 | switch (mibitem->did) { |
187 | case didmib_dot11smt_wepdefaultkeystable_key(1): |
188 | case didmib_dot11smt_wepdefaultkeystable_key(2): |
189 | case didmib_dot11smt_wepdefaultkeystable_key(3): |
190 | case didmib_dot11smt_wepdefaultkeystable_key(4): |
191 | if (!isget) |
192 | wep_change_key(wlandev, |
193 | P80211DID_ITEM(mibitem->did) - 1, |
194 | key, keylen: pstr->len); |
195 | break; |
196 | |
197 | case DIDMIB_DOT11SMT_PRIVACYTABLE_WEPDEFAULTKEYID: { |
198 | u32 *data = (u32 *)mibitem->data; |
199 | |
200 | if (isget) { |
201 | *data = wlandev->hostwep & HOSTWEP_DEFAULTKEY_MASK; |
202 | } else { |
203 | wlandev->hostwep &= ~(HOSTWEP_DEFAULTKEY_MASK); |
204 | wlandev->hostwep |= (*data & HOSTWEP_DEFAULTKEY_MASK); |
205 | } |
206 | break; |
207 | } |
208 | case DIDMIB_DOT11SMT_PRIVACYTABLE_PRIVACYINVOKED: { |
209 | u32 *data = (u32 *)mibitem->data; |
210 | |
211 | p80211req_handle_action(wlandev, data, isget, |
212 | HOSTWEP_PRIVACYINVOKED); |
213 | break; |
214 | } |
215 | case DIDMIB_DOT11SMT_PRIVACYTABLE_EXCLUDEUNENCRYPTED: { |
216 | u32 *data = (u32 *)mibitem->data; |
217 | |
218 | p80211req_handle_action(wlandev, data, isget, |
219 | HOSTWEP_EXCLUDEUNENCRYPTED); |
220 | break; |
221 | } |
222 | } |
223 | } |
224 | |