aboutsummaryrefslogtreecommitdiffstats
path: root/drivers/net/wireless/rndis_wlan.c
diff options
context:
space:
mode:
Diffstat (limited to 'drivers/net/wireless/rndis_wlan.c')
-rw-r--r--drivers/net/wireless/rndis_wlan.c233
1 files changed, 232 insertions, 1 deletions
diff --git a/drivers/net/wireless/rndis_wlan.c b/drivers/net/wireless/rndis_wlan.c
index 6b6452b0e8c4..7a50cfa18843 100644
--- a/drivers/net/wireless/rndis_wlan.c
+++ b/drivers/net/wireless/rndis_wlan.c
@@ -201,6 +201,24 @@ enum ndis_80211_priv_filter {
201 NDIS_80211_PRIV_8021X_WEP 201 NDIS_80211_PRIV_8021X_WEP
202}; 202};
203 203
204enum ndis_80211_status_type {
205 NDIS_80211_STATUSTYPE_AUTHENTICATION,
206 NDIS_80211_STATUSTYPE_MEDIASTREAMMODE,
207 NDIS_80211_STATUSTYPE_PMKID_CANDIDATELIST,
208 NDIS_80211_STATUSTYPE_RADIOSTATE,
209};
210
211enum ndis_80211_media_stream_mode {
212 NDIS_80211_MEDIA_STREAM_OFF,
213 NDIS_80211_MEDIA_STREAM_ON
214};
215
216enum ndis_80211_radio_status {
217 NDIS_80211_RADIO_STATUS_ON,
218 NDIS_80211_RADIO_STATUS_HARDWARE_OFF,
219 NDIS_80211_RADIO_STATUS_SOFTWARE_OFF,
220};
221
204enum ndis_80211_addkey_bits { 222enum ndis_80211_addkey_bits {
205 NDIS_80211_ADDKEY_8021X_AUTH = cpu_to_le32(1 << 28), 223 NDIS_80211_ADDKEY_8021X_AUTH = cpu_to_le32(1 << 28),
206 NDIS_80211_ADDKEY_SET_INIT_RECV_SEQ = cpu_to_le32(1 << 29), 224 NDIS_80211_ADDKEY_SET_INIT_RECV_SEQ = cpu_to_le32(1 << 29),
@@ -213,6 +231,35 @@ enum ndis_80211_addwep_bits {
213 NDIS_80211_ADDWEP_TRANSMIT_KEY = cpu_to_le32(1 << 31) 231 NDIS_80211_ADDWEP_TRANSMIT_KEY = cpu_to_le32(1 << 31)
214}; 232};
215 233
234struct ndis_80211_auth_request {
235 __le32 length;
236 u8 bssid[6];
237 u8 padding[2];
238 __le32 flags;
239} __attribute__((packed));
240
241struct ndis_80211_pmkid_candidate {
242 u8 bssid[6];
243 u8 padding[2];
244 __le32 flags;
245} __attribute__((packed));
246
247struct ndis_80211_pmkid_cand_list {
248 __le32 version;
249 __le32 num_candidates;
250 struct ndis_80211_pmkid_candidate candidate_list[0];
251} __attribute__((packed));
252
253struct ndis_80211_status_indication {
254 __le32 status_type;
255 union {
256 enum ndis_80211_media_stream_mode media_stream_mode;
257 enum ndis_80211_radio_status radio_status;
258 struct ndis_80211_auth_request auth_request[0];
259 struct ndis_80211_pmkid_cand_list cand_list;
260 } u;
261} __attribute__((packed));
262
216struct ndis_80211_ssid { 263struct ndis_80211_ssid {
217 __le32 length; 264 __le32 length;
218 u8 essid[NDIS_802_11_LENGTH_SSID]; 265 u8 essid[NDIS_802_11_LENGTH_SSID];
@@ -2211,16 +2258,195 @@ static void rndis_wlan_set_multicast_list(struct net_device *dev)
2211 queue_work(priv->workqueue, &priv->work); 2258 queue_work(priv->workqueue, &priv->work);
2212} 2259}
2213 2260
2261
2262static void rndis_wlan_auth_indication(struct usbnet *usbdev,
2263 struct ndis_80211_status_indication *indication,
2264 int len)
2265{
2266 u8 *buf;
2267 const char *type;
2268 int flags, buflen;
2269 bool pairwise_error, group_error;
2270 struct ndis_80211_auth_request *auth_req;
2271
2272 /* must have at least one array entry */
2273 if (len < offsetof(struct ndis_80211_status_indication, u) +
2274 sizeof(struct ndis_80211_auth_request)) {
2275 devinfo(usbdev, "authentication indication: "
2276 "too short message (%i)", len);
2277 return;
2278 }
2279
2280 buf = (void *)&indication->u.auth_request[0];
2281 buflen = len - offsetof(struct ndis_80211_status_indication, u);
2282
2283 while (buflen >= sizeof(*auth_req)) {
2284 auth_req = (void *)buf;
2285 type = "unknown";
2286 flags = le32_to_cpu(auth_req->flags);
2287 pairwise_error = false;
2288 group_error = false;
2289
2290 if (flags & 0x1)
2291 type = "reauth request";
2292 if (flags & 0x2)
2293 type = "key update request";
2294 if (flags & 0x6) {
2295 pairwise_error = true;
2296 type = "pairwise_error";
2297 }
2298 if (flags & 0xe) {
2299 group_error = true;
2300 type = "group_error";
2301 }
2302
2303 devinfo(usbdev, "authentication indication: %s (0x%08x)", type,
2304 le32_to_cpu(auth_req->flags));
2305
2306 if (pairwise_error || group_error) {
2307 union iwreq_data wrqu;
2308 struct iw_michaelmicfailure micfailure;
2309
2310 memset(&micfailure, 0, sizeof(micfailure));
2311 if (pairwise_error)
2312 micfailure.flags |= IW_MICFAILURE_PAIRWISE;
2313 if (group_error)
2314 micfailure.flags |= IW_MICFAILURE_GROUP;
2315
2316 memcpy(micfailure.src_addr.sa_data, auth_req->bssid,
2317 ETH_ALEN);
2318
2319 memset(&wrqu, 0, sizeof(wrqu));
2320 wrqu.data.length = sizeof(micfailure);
2321 wireless_send_event(usbdev->net, IWEVMICHAELMICFAILURE,
2322 &wrqu, (u8 *)&micfailure);
2323 }
2324
2325 buflen -= le32_to_cpu(auth_req->length);
2326 buf += le32_to_cpu(auth_req->length);
2327 }
2328}
2329
2330static void rndis_wlan_pmkid_cand_list_indication(struct usbnet *usbdev,
2331 struct ndis_80211_status_indication *indication,
2332 int len)
2333{
2334 struct ndis_80211_pmkid_cand_list *cand_list;
2335 int list_len, expected_len, i;
2336
2337 if (len < offsetof(struct ndis_80211_status_indication, u) +
2338 sizeof(struct ndis_80211_pmkid_cand_list)) {
2339 devinfo(usbdev, "pmkid candidate list indication: "
2340 "too short message (%i)", len);
2341 return;
2342 }
2343
2344 list_len = le32_to_cpu(indication->u.cand_list.num_candidates) *
2345 sizeof(struct ndis_80211_pmkid_candidate);
2346 expected_len = sizeof(struct ndis_80211_pmkid_cand_list) + list_len +
2347 offsetof(struct ndis_80211_status_indication, u);
2348
2349 if (len < expected_len) {
2350 devinfo(usbdev, "pmkid candidate list indication: "
2351 "list larger than buffer (%i < %i)",
2352 len, expected_len);
2353 return;
2354 }
2355
2356 cand_list = &indication->u.cand_list;
2357
2358 devinfo(usbdev, "pmkid candidate list indication: "
2359 "version %i, candidates %i",
2360 le32_to_cpu(cand_list->version),
2361 le32_to_cpu(cand_list->num_candidates));
2362
2363 if (le32_to_cpu(cand_list->version) != 1)
2364 return;
2365
2366 for (i = 0; i < le32_to_cpu(cand_list->num_candidates); i++) {
2367 struct iw_pmkid_cand pcand;
2368 union iwreq_data wrqu;
2369 struct ndis_80211_pmkid_candidate *cand =
2370 &cand_list->candidate_list[i];
2371
2372 devdbg(usbdev, "cand[%i]: flags: 0x%08x, bssid: %pM",
2373 i, le32_to_cpu(cand->flags), cand->bssid);
2374
2375 memset(&pcand, 0, sizeof(pcand));
2376 if (le32_to_cpu(cand->flags) & 0x01)
2377 pcand.flags |= IW_PMKID_CAND_PREAUTH;
2378 pcand.index = i;
2379 memcpy(pcand.bssid.sa_data, cand->bssid, ETH_ALEN);
2380
2381 memset(&wrqu, 0, sizeof(wrqu));
2382 wrqu.data.length = sizeof(pcand);
2383 wireless_send_event(usbdev->net, IWEVPMKIDCAND, &wrqu,
2384 (u8 *)&pcand);
2385 }
2386}
2387
2388static void rndis_wlan_media_specific_indication(struct usbnet *usbdev,
2389 struct rndis_indicate *msg, int buflen)
2390{
2391 struct ndis_80211_status_indication *indication;
2392 int len, offset;
2393
2394 offset = offsetof(struct rndis_indicate, status) +
2395 le32_to_cpu(msg->offset);
2396 len = le32_to_cpu(msg->length);
2397
2398 if (len < 8) {
2399 devinfo(usbdev, "media specific indication, "
2400 "ignore too short message (%i < 8)", len);
2401 return;
2402 }
2403
2404 if (offset + len > buflen) {
2405 devinfo(usbdev, "media specific indication, "
2406 "too large to fit to buffer (%i > %i)",
2407 offset + len, buflen);
2408 return;
2409 }
2410
2411 indication = (void *)((u8 *)msg + offset);
2412
2413 switch (le32_to_cpu(indication->status_type)) {
2414 case NDIS_80211_STATUSTYPE_RADIOSTATE:
2415 devinfo(usbdev, "radio state indication: %i",
2416 le32_to_cpu(indication->u.radio_status));
2417 return;
2418
2419 case NDIS_80211_STATUSTYPE_MEDIASTREAMMODE:
2420 devinfo(usbdev, "media stream mode indication: %i",
2421 le32_to_cpu(indication->u.media_stream_mode));
2422 return;
2423
2424 case NDIS_80211_STATUSTYPE_AUTHENTICATION:
2425 rndis_wlan_auth_indication(usbdev, indication, len);
2426 return;
2427
2428 case NDIS_80211_STATUSTYPE_PMKID_CANDIDATELIST:
2429 rndis_wlan_pmkid_cand_list_indication(usbdev, indication, len);
2430 return;
2431
2432 default:
2433 devinfo(usbdev, "media specific indication: "
2434 "unknown status type 0x%08x",
2435 le32_to_cpu(indication->status_type));
2436 }
2437}
2438
2439
2214static void rndis_wlan_indication(struct usbnet *usbdev, void *ind, int buflen) 2440static void rndis_wlan_indication(struct usbnet *usbdev, void *ind, int buflen)
2215{ 2441{
2216 struct rndis_wlan_private *priv = get_rndis_wlan_priv(usbdev); 2442 struct rndis_wlan_private *priv = get_rndis_wlan_priv(usbdev);
2217 struct rndis_indicate *msg = ind; 2443 struct rndis_indicate *msg = ind;
2218 2444
2219 /* queue work to avoid recursive calls into rndis_command */
2220 switch (msg->status) { 2445 switch (msg->status) {
2221 case RNDIS_STATUS_MEDIA_CONNECT: 2446 case RNDIS_STATUS_MEDIA_CONNECT:
2222 devinfo(usbdev, "media connect"); 2447 devinfo(usbdev, "media connect");
2223 2448
2449 /* queue work to avoid recursive calls into rndis_command */
2224 set_bit(WORK_LINK_UP, &priv->work_pending); 2450 set_bit(WORK_LINK_UP, &priv->work_pending);
2225 queue_work(priv->workqueue, &priv->work); 2451 queue_work(priv->workqueue, &priv->work);
2226 break; 2452 break;
@@ -2228,10 +2454,15 @@ static void rndis_wlan_indication(struct usbnet *usbdev, void *ind, int buflen)
2228 case RNDIS_STATUS_MEDIA_DISCONNECT: 2454 case RNDIS_STATUS_MEDIA_DISCONNECT:
2229 devinfo(usbdev, "media disconnect"); 2455 devinfo(usbdev, "media disconnect");
2230 2456
2457 /* queue work to avoid recursive calls into rndis_command */
2231 set_bit(WORK_LINK_DOWN, &priv->work_pending); 2458 set_bit(WORK_LINK_DOWN, &priv->work_pending);
2232 queue_work(priv->workqueue, &priv->work); 2459 queue_work(priv->workqueue, &priv->work);
2233 break; 2460 break;
2234 2461
2462 case RNDIS_STATUS_MEDIA_SPECIFIC_INDICATION:
2463 rndis_wlan_media_specific_indication(usbdev, msg, buflen);
2464 break;
2465
2235 default: 2466 default:
2236 devinfo(usbdev, "indication: 0x%08x", 2467 devinfo(usbdev, "indication: 0x%08x",
2237 le32_to_cpu(msg->status)); 2468 le32_to_cpu(msg->status));