diff options
author | Jussi Kivilinna <jussi.kivilinna@mbnet.fi> | 2010-03-04 11:27:36 -0500 |
---|---|---|
committer | John W. Linville <linville@tuxdriver.com> | 2010-03-10 17:44:28 -0500 |
commit | 5a7d05830de1ecfdaf0a9fb43e4aa08abbdbfe9f (patch) | |
tree | 88fd43490767cd215d232e94c0edc5a4c605a5be | |
parent | 0308383f9591c991b3eb865c4f5ea2a87242afac (diff) |
rndis_wlan: Implement cfg80211 PMKSA API
Add support for cfg80211 set_pmksa/del_pmksa/flush_pmksa. Updating PMKID
entry list is done on driver side since NDIS API requires full list update.
v2:
- fixed to use new netdev_dbg/warn/etc instead of old devdbg/warn/etc
- fixed false padding from struct ndis_80211_bssid_info
Signed-off-by: Jussi Kivilinna <jussi.kivilinna@mbnet.fi>
Signed-off-by: John W. Linville <linville@tuxdriver.com>
-rw-r--r-- | drivers/net/wireless/rndis_wlan.c | 282 |
1 files changed, 282 insertions, 0 deletions
diff --git a/drivers/net/wireless/rndis_wlan.c b/drivers/net/wireless/rndis_wlan.c index 3433461995a7..267afd714c74 100644 --- a/drivers/net/wireless/rndis_wlan.c +++ b/drivers/net/wireless/rndis_wlan.c | |||
@@ -372,6 +372,17 @@ struct ndis_80211_capability { | |||
372 | struct ndis_80211_auth_encr_pair auth_encr_pair[0]; | 372 | struct ndis_80211_auth_encr_pair auth_encr_pair[0]; |
373 | } __attribute__((packed)); | 373 | } __attribute__((packed)); |
374 | 374 | ||
375 | struct ndis_80211_bssid_info { | ||
376 | u8 bssid[6]; | ||
377 | u8 pmkid[16]; | ||
378 | }; | ||
379 | |||
380 | struct ndis_80211_pmkid { | ||
381 | __le32 length; | ||
382 | __le32 bssid_info_count; | ||
383 | struct ndis_80211_bssid_info bssid_info[0]; | ||
384 | }; | ||
385 | |||
375 | /* | 386 | /* |
376 | * private data | 387 | * private data |
377 | */ | 388 | */ |
@@ -542,6 +553,14 @@ static int rndis_get_station(struct wiphy *wiphy, struct net_device *dev, | |||
542 | static int rndis_dump_station(struct wiphy *wiphy, struct net_device *dev, | 553 | static int rndis_dump_station(struct wiphy *wiphy, struct net_device *dev, |
543 | int idx, u8 *mac, struct station_info *sinfo); | 554 | int idx, u8 *mac, struct station_info *sinfo); |
544 | 555 | ||
556 | static int rndis_set_pmksa(struct wiphy *wiphy, struct net_device *netdev, | ||
557 | struct cfg80211_pmksa *pmksa); | ||
558 | |||
559 | static int rndis_del_pmksa(struct wiphy *wiphy, struct net_device *netdev, | ||
560 | struct cfg80211_pmksa *pmksa); | ||
561 | |||
562 | static int rndis_flush_pmksa(struct wiphy *wiphy, struct net_device *netdev); | ||
563 | |||
545 | static struct cfg80211_ops rndis_config_ops = { | 564 | static struct cfg80211_ops rndis_config_ops = { |
546 | .change_virtual_intf = rndis_change_virtual_intf, | 565 | .change_virtual_intf = rndis_change_virtual_intf, |
547 | .scan = rndis_scan, | 566 | .scan = rndis_scan, |
@@ -558,6 +577,9 @@ static struct cfg80211_ops rndis_config_ops = { | |||
558 | .set_default_key = rndis_set_default_key, | 577 | .set_default_key = rndis_set_default_key, |
559 | .get_station = rndis_get_station, | 578 | .get_station = rndis_get_station, |
560 | .dump_station = rndis_dump_station, | 579 | .dump_station = rndis_dump_station, |
580 | .set_pmksa = rndis_set_pmksa, | ||
581 | .del_pmksa = rndis_del_pmksa, | ||
582 | .flush_pmksa = rndis_flush_pmksa, | ||
561 | }; | 583 | }; |
562 | 584 | ||
563 | static void *rndis_wiphy_privid = &rndis_wiphy_privid; | 585 | static void *rndis_wiphy_privid = &rndis_wiphy_privid; |
@@ -1580,6 +1602,194 @@ static void set_multicast_list(struct usbnet *usbdev) | |||
1580 | le32_to_cpu(filter), ret); | 1602 | le32_to_cpu(filter), ret); |
1581 | } | 1603 | } |
1582 | 1604 | ||
1605 | #ifdef DEBUG | ||
1606 | static void debug_print_pmkids(struct usbnet *usbdev, | ||
1607 | struct ndis_80211_pmkid *pmkids, | ||
1608 | const char *func_str) | ||
1609 | { | ||
1610 | struct rndis_wlan_private *priv = get_rndis_wlan_priv(usbdev); | ||
1611 | int i, len, count, max_pmkids, entry_len; | ||
1612 | |||
1613 | max_pmkids = priv->wdev.wiphy->max_num_pmkids; | ||
1614 | len = le32_to_cpu(pmkids->length); | ||
1615 | count = le32_to_cpu(pmkids->bssid_info_count); | ||
1616 | |||
1617 | entry_len = (count > 0) ? (len - sizeof(*pmkids)) / count : -1; | ||
1618 | |||
1619 | netdev_dbg(usbdev->net, "%s(): %d PMKIDs (data len: %d, entry len: " | ||
1620 | "%d)\n", func_str, count, len, entry_len); | ||
1621 | |||
1622 | if (count > max_pmkids) | ||
1623 | count = max_pmkids; | ||
1624 | |||
1625 | for (i = 0; i < count; i++) { | ||
1626 | u32 *tmp = (u32 *)pmkids->bssid_info[i].pmkid; | ||
1627 | |||
1628 | netdev_dbg(usbdev->net, "%s(): bssid: %pM, " | ||
1629 | "pmkid: %08X:%08X:%08X:%08X\n", | ||
1630 | func_str, pmkids->bssid_info[i].bssid, | ||
1631 | cpu_to_be32(tmp[0]), cpu_to_be32(tmp[1]), | ||
1632 | cpu_to_be32(tmp[2]), cpu_to_be32(tmp[3])); | ||
1633 | } | ||
1634 | } | ||
1635 | #else | ||
1636 | static void debug_print_pmkids(struct usbnet *usbdev, | ||
1637 | struct ndis_80211_pmkid *pmkids, | ||
1638 | const char *func_str) | ||
1639 | { | ||
1640 | return; | ||
1641 | } | ||
1642 | #endif | ||
1643 | |||
1644 | static struct ndis_80211_pmkid *get_device_pmkids(struct usbnet *usbdev) | ||
1645 | { | ||
1646 | struct rndis_wlan_private *priv = get_rndis_wlan_priv(usbdev); | ||
1647 | struct ndis_80211_pmkid *pmkids; | ||
1648 | int len, ret, max_pmkids; | ||
1649 | |||
1650 | max_pmkids = priv->wdev.wiphy->max_num_pmkids; | ||
1651 | len = sizeof(*pmkids) + max_pmkids * sizeof(pmkids->bssid_info[0]); | ||
1652 | |||
1653 | pmkids = kzalloc(len, GFP_KERNEL); | ||
1654 | if (!pmkids) | ||
1655 | return ERR_PTR(-ENOMEM); | ||
1656 | |||
1657 | pmkids->length = cpu_to_le32(len); | ||
1658 | pmkids->bssid_info_count = cpu_to_le32(max_pmkids); | ||
1659 | |||
1660 | ret = rndis_query_oid(usbdev, OID_802_11_PMKID, pmkids, &len); | ||
1661 | if (ret < 0) { | ||
1662 | netdev_dbg(usbdev->net, "%s(): OID_802_11_PMKID(%d, %d)" | ||
1663 | " -> %d\n", __func__, len, max_pmkids, ret); | ||
1664 | |||
1665 | kfree(pmkids); | ||
1666 | return ERR_PTR(ret); | ||
1667 | } | ||
1668 | |||
1669 | if (le32_to_cpu(pmkids->bssid_info_count) > max_pmkids) | ||
1670 | pmkids->bssid_info_count = cpu_to_le32(max_pmkids); | ||
1671 | |||
1672 | debug_print_pmkids(usbdev, pmkids, __func__); | ||
1673 | |||
1674 | return pmkids; | ||
1675 | } | ||
1676 | |||
1677 | static int set_device_pmkids(struct usbnet *usbdev, | ||
1678 | struct ndis_80211_pmkid *pmkids) | ||
1679 | { | ||
1680 | int ret, len, num_pmkids; | ||
1681 | |||
1682 | num_pmkids = le32_to_cpu(pmkids->bssid_info_count); | ||
1683 | len = sizeof(*pmkids) + num_pmkids * sizeof(pmkids->bssid_info[0]); | ||
1684 | pmkids->length = cpu_to_le32(len); | ||
1685 | |||
1686 | debug_print_pmkids(usbdev, pmkids, __func__); | ||
1687 | |||
1688 | ret = rndis_set_oid(usbdev, OID_802_11_PMKID, pmkids, | ||
1689 | le32_to_cpu(pmkids->length)); | ||
1690 | if (ret < 0) { | ||
1691 | netdev_dbg(usbdev->net, "%s(): OID_802_11_PMKID(%d, %d) -> %d" | ||
1692 | "\n", __func__, len, num_pmkids, ret); | ||
1693 | } | ||
1694 | |||
1695 | kfree(pmkids); | ||
1696 | return ret; | ||
1697 | } | ||
1698 | |||
1699 | static struct ndis_80211_pmkid *remove_pmkid(struct usbnet *usbdev, | ||
1700 | struct ndis_80211_pmkid *pmkids, | ||
1701 | struct cfg80211_pmksa *pmksa, | ||
1702 | int max_pmkids) | ||
1703 | { | ||
1704 | int i, len, count, newlen, err; | ||
1705 | |||
1706 | len = le32_to_cpu(pmkids->length); | ||
1707 | count = le32_to_cpu(pmkids->bssid_info_count); | ||
1708 | |||
1709 | if (count > max_pmkids) | ||
1710 | count = max_pmkids; | ||
1711 | |||
1712 | for (i = 0; i < count; i++) | ||
1713 | if (!compare_ether_addr(pmkids->bssid_info[i].bssid, | ||
1714 | pmksa->bssid)) | ||
1715 | break; | ||
1716 | |||
1717 | /* pmkid not found */ | ||
1718 | if (i == count) { | ||
1719 | netdev_dbg(usbdev->net, "%s(): bssid not found (%pM)\n", | ||
1720 | __func__, pmksa->bssid); | ||
1721 | err = -ENOENT; | ||
1722 | goto error; | ||
1723 | } | ||
1724 | |||
1725 | for (; i + 1 < count; i++) | ||
1726 | pmkids->bssid_info[i] = pmkids->bssid_info[i + 1]; | ||
1727 | |||
1728 | count--; | ||
1729 | newlen = sizeof(*pmkids) + count * sizeof(pmkids->bssid_info[0]); | ||
1730 | |||
1731 | pmkids->length = cpu_to_le32(newlen); | ||
1732 | pmkids->bssid_info_count = cpu_to_le32(count); | ||
1733 | |||
1734 | return pmkids; | ||
1735 | error: | ||
1736 | kfree(pmkids); | ||
1737 | return ERR_PTR(err); | ||
1738 | } | ||
1739 | |||
1740 | static struct ndis_80211_pmkid *update_pmkid(struct usbnet *usbdev, | ||
1741 | struct ndis_80211_pmkid *pmkids, | ||
1742 | struct cfg80211_pmksa *pmksa, | ||
1743 | int max_pmkids) | ||
1744 | { | ||
1745 | int i, err, len, count, newlen; | ||
1746 | |||
1747 | len = le32_to_cpu(pmkids->length); | ||
1748 | count = le32_to_cpu(pmkids->bssid_info_count); | ||
1749 | |||
1750 | if (count > max_pmkids) | ||
1751 | count = max_pmkids; | ||
1752 | |||
1753 | /* update with new pmkid */ | ||
1754 | for (i = 0; i < count; i++) { | ||
1755 | if (compare_ether_addr(pmkids->bssid_info[i].bssid, | ||
1756 | pmksa->bssid)) | ||
1757 | continue; | ||
1758 | |||
1759 | memcpy(pmkids->bssid_info[i].pmkid, pmksa->pmkid, | ||
1760 | WLAN_PMKID_LEN); | ||
1761 | |||
1762 | return pmkids; | ||
1763 | } | ||
1764 | |||
1765 | /* out of space, return error */ | ||
1766 | if (i == max_pmkids) { | ||
1767 | netdev_dbg(usbdev->net, "%s(): out of space\n", __func__); | ||
1768 | err = -ENOSPC; | ||
1769 | goto error; | ||
1770 | } | ||
1771 | |||
1772 | /* add new pmkid */ | ||
1773 | newlen = sizeof(*pmkids) + (count + 1) * sizeof(pmkids->bssid_info[0]); | ||
1774 | |||
1775 | pmkids = krealloc(pmkids, newlen, GFP_KERNEL); | ||
1776 | if (!pmkids) { | ||
1777 | err = -ENOMEM; | ||
1778 | goto error; | ||
1779 | } | ||
1780 | |||
1781 | pmkids->length = cpu_to_le32(newlen); | ||
1782 | pmkids->bssid_info_count = cpu_to_le32(count + 1); | ||
1783 | |||
1784 | memcpy(pmkids->bssid_info[count].bssid, pmksa->bssid, ETH_ALEN); | ||
1785 | memcpy(pmkids->bssid_info[count].pmkid, pmksa->pmkid, WLAN_PMKID_LEN); | ||
1786 | |||
1787 | return pmkids; | ||
1788 | error: | ||
1789 | kfree(pmkids); | ||
1790 | return ERR_PTR(err); | ||
1791 | } | ||
1792 | |||
1583 | /* | 1793 | /* |
1584 | * cfg80211 ops | 1794 | * cfg80211 ops |
1585 | */ | 1795 | */ |
@@ -2190,6 +2400,78 @@ static int rndis_dump_station(struct wiphy *wiphy, struct net_device *dev, | |||
2190 | return 0; | 2400 | return 0; |
2191 | } | 2401 | } |
2192 | 2402 | ||
2403 | static int rndis_set_pmksa(struct wiphy *wiphy, struct net_device *netdev, | ||
2404 | struct cfg80211_pmksa *pmksa) | ||
2405 | { | ||
2406 | struct rndis_wlan_private *priv = wiphy_priv(wiphy); | ||
2407 | struct usbnet *usbdev = priv->usbdev; | ||
2408 | struct ndis_80211_pmkid *pmkids; | ||
2409 | u32 *tmp = (u32 *)pmksa->pmkid; | ||
2410 | |||
2411 | netdev_dbg(usbdev->net, "%s(%pM, %08X:%08X:%08X:%08X)\n", __func__, | ||
2412 | pmksa->bssid, | ||
2413 | cpu_to_be32(tmp[0]), cpu_to_be32(tmp[1]), | ||
2414 | cpu_to_be32(tmp[2]), cpu_to_be32(tmp[3])); | ||
2415 | |||
2416 | pmkids = get_device_pmkids(usbdev); | ||
2417 | if (IS_ERR(pmkids)) { | ||
2418 | /* couldn't read PMKID cache from device */ | ||
2419 | return PTR_ERR(pmkids); | ||
2420 | } | ||
2421 | |||
2422 | pmkids = update_pmkid(usbdev, pmkids, pmksa, wiphy->max_num_pmkids); | ||
2423 | if (IS_ERR(pmkids)) { | ||
2424 | /* not found, list full, etc */ | ||
2425 | return PTR_ERR(pmkids); | ||
2426 | } | ||
2427 | |||
2428 | return set_device_pmkids(usbdev, pmkids); | ||
2429 | } | ||
2430 | |||
2431 | static int rndis_del_pmksa(struct wiphy *wiphy, struct net_device *netdev, | ||
2432 | struct cfg80211_pmksa *pmksa) | ||
2433 | { | ||
2434 | struct rndis_wlan_private *priv = wiphy_priv(wiphy); | ||
2435 | struct usbnet *usbdev = priv->usbdev; | ||
2436 | struct ndis_80211_pmkid *pmkids; | ||
2437 | u32 *tmp = (u32 *)pmksa->pmkid; | ||
2438 | |||
2439 | netdev_dbg(usbdev->net, "%s(%pM, %08X:%08X:%08X:%08X)\n", __func__, | ||
2440 | pmksa->bssid, | ||
2441 | cpu_to_be32(tmp[0]), cpu_to_be32(tmp[1]), | ||
2442 | cpu_to_be32(tmp[2]), cpu_to_be32(tmp[3])); | ||
2443 | |||
2444 | pmkids = get_device_pmkids(usbdev); | ||
2445 | if (IS_ERR(pmkids)) { | ||
2446 | /* Couldn't read PMKID cache from device */ | ||
2447 | return PTR_ERR(pmkids); | ||
2448 | } | ||
2449 | |||
2450 | pmkids = remove_pmkid(usbdev, pmkids, pmksa, wiphy->max_num_pmkids); | ||
2451 | if (IS_ERR(pmkids)) { | ||
2452 | /* not found, etc */ | ||
2453 | return PTR_ERR(pmkids); | ||
2454 | } | ||
2455 | |||
2456 | return set_device_pmkids(usbdev, pmkids); | ||
2457 | } | ||
2458 | |||
2459 | static int rndis_flush_pmksa(struct wiphy *wiphy, struct net_device *netdev) | ||
2460 | { | ||
2461 | struct rndis_wlan_private *priv = wiphy_priv(wiphy); | ||
2462 | struct usbnet *usbdev = priv->usbdev; | ||
2463 | struct ndis_80211_pmkid pmkid; | ||
2464 | |||
2465 | netdev_dbg(usbdev->net, "%s()\n", __func__); | ||
2466 | |||
2467 | memset(&pmkid, 0, sizeof(pmkid)); | ||
2468 | |||
2469 | pmkid.length = cpu_to_le32(sizeof(pmkid)); | ||
2470 | pmkid.bssid_info_count = cpu_to_le32(0); | ||
2471 | |||
2472 | return rndis_set_oid(usbdev, OID_802_11_PMKID, &pmkid, sizeof(pmkid)); | ||
2473 | } | ||
2474 | |||
2193 | /* | 2475 | /* |
2194 | * workers, indication handlers, device poller | 2476 | * workers, indication handlers, device poller |
2195 | */ | 2477 | */ |