diff -urp ipw2200-1.0.6/ipw2200.c ipw2200-1.0.6-fix3/ipw2200.c --- ipw2200-1.0.6/ipw2200.c 2005-07-14 03:00:28.000000000 +0800 +++ ipw2200-1.0.6-fix3/ipw2200.c 2005-07-15 13:31:30.000000000 +0800 @@ -5576,6 +5576,55 @@ static void ipw_send_wep_keys(struct ipw } } +static void ipw_set_hw_decrypt_unicast(struct ipw_priv *priv, int level) +{ + if (priv->ieee->host_encrypt) + return; + + switch (level) { + case SEC_LEVEL_3: + priv->sys_config.disable_unicast_decryption = 0; + priv->ieee->host_decrypt = 0; + break; + case SEC_LEVEL_2: + priv->sys_config.disable_unicast_decryption = 1; + priv->ieee->host_decrypt = 1; + break; + case SEC_LEVEL_1: + priv->sys_config.disable_unicast_decryption = 0; + priv->ieee->host_decrypt = 0; + break; + case SEC_LEVEL_0: + priv->sys_config.disable_unicast_decryption = 1; + break; + default: + break; + } +} + +static void ipw_set_hw_decrypt_multicast(struct ipw_priv *priv, int level) +{ + if (priv->ieee->host_encrypt) + return; + + switch (level) { + case SEC_LEVEL_3: + priv->sys_config.disable_multicast_decryption = 0; + break; + case SEC_LEVEL_2: + priv->sys_config.disable_multicast_decryption = 1; + break; + case SEC_LEVEL_1: + priv->sys_config.disable_multicast_decryption = 0; + break; + case SEC_LEVEL_0: + priv->sys_config.disable_multicast_decryption = 1; + break; + default: + break; + } +} + static void ipw_set_hwcrypto_keys(struct ipw_priv *priv) { switch (priv->ieee->sec.level) { @@ -5586,33 +5635,23 @@ static void ipw_set_hwcrypto_keys(struct priv->ieee->sec.active_key); ipw_send_wep_keys(priv, DCW_WEP_KEY_SEC_TYPE_CCM); - priv->sys_config.disable_unicast_decryption = 0; - priv->sys_config.disable_multicast_decryption = 0; - priv->ieee->host_decrypt = 0; break; case SEC_LEVEL_2: if (priv->ieee->sec.flags & SEC_ACTIVE_KEY) ipw_send_tgi_tx_key(priv, DCT_FLAG_EXT_SECURITY_TKIP, priv->ieee->sec.active_key); - - priv->sys_config.disable_unicast_decryption = 1; - priv->sys_config.disable_multicast_decryption = 1; - priv->ieee->host_decrypt = 1; break; case SEC_LEVEL_1: ipw_send_wep_keys(priv, DCW_WEP_KEY_SEC_TYPE_WEP); - priv->sys_config.disable_unicast_decryption = 0; - priv->sys_config.disable_multicast_decryption = 0; - priv->ieee->host_decrypt = 0; break; case SEC_LEVEL_0: - priv->sys_config.disable_unicast_decryption = 1; - priv->sys_config.disable_multicast_decryption = 1; - break; default: break; } + + ipw_set_hw_decrypt_unicast(priv, priv->ieee->sec.level); + ipw_set_hw_decrypt_multicast(priv, priv->ieee->sec.level); } static void ipw_adhoc_check(void *data) @@ -6194,12 +6233,31 @@ static int ipw_wpa_mlme(struct net_devic return ret; } +static int ipw_wpa_ie_cipher2level(u8 cipher) +{ + switch (cipher) { + case 4: /* CCMP */ + return SEC_LEVEL_3; + case 2: /* TKIP */ + return SEC_LEVEL_2; + case 5: /* WEP104 */ + case 1: /* WEP40 */ + return SEC_LEVEL_1; + case 0: /* NONE */ + return SEC_LEVEL_0; + default: + return -1; + } +} + static int ipw_wpa_set_wpa_ie(struct net_device *dev, struct ipw_param *param, int plen) { struct ipw_priv *priv = ieee80211_priv(dev); struct ieee80211_device *ieee = priv->ieee; u8 *buf; + u8 *ptk, *gtk; + int level; if (param->u.wpa_ie.len > MAX_WPA_IE_LEN || (param->u.wpa_ie.len && param->u.wpa_ie.data == NULL)) @@ -6218,8 +6276,35 @@ static int ipw_wpa_set_wpa_ie(struct net kfree(ieee->wpa_ie); ieee->wpa_ie = NULL; ieee->wpa_ie_len = 0; + goto done; + } + + if (priv->ieee->host_encrypt) + goto done; + + /* HACK: Parse wpa_ie here to get pairwise suite, otherwise + * we need to change driver_ipw.c from wpa_supplicant. This + * is OK since -Dipw is deprecated. The -Dwext driver has a + * clean way to handle this. */ + gtk = ptk = (u8 *)ieee->wpa_ie; + if (ieee->wpa_ie[0] == 0x30) { /* RSN IE */ + gtk += 4 + 3; + ptk += 4 + 4 + 2 + 3; + } else { /* WPA IE */ + gtk += 8 + 3; + ptk += 8 + 4 + 2 + 3; } + if (ptk - (u8 *)ieee->wpa_ie > ieee->wpa_ie_len) + return -EINVAL; + + level = ipw_wpa_ie_cipher2level(*gtk); + ipw_set_hw_decrypt_multicast(priv, level); + + level = ipw_wpa_ie_cipher2level(*ptk); + ipw_set_hw_decrypt_unicast(priv, level); + +done: ipw_wpa_assoc_frame(priv, ieee->wpa_ie, ieee->wpa_ie_len); return 0; } @@ -6523,6 +6608,23 @@ out: return err; } +static int wext_cipher2level(int cipher) +{ + switch (cipher) { + case IW_AUTH_CIPHER_NONE: + return SEC_LEVEL_0; + case IW_AUTH_CIPHER_WEP40: + case IW_AUTH_CIPHER_WEP104: + return SEC_LEVEL_1; + case IW_AUTH_CIPHER_TKIP: + return SEC_LEVEL_2; + case IW_AUTH_CIPHER_CCMP: + return SEC_LEVEL_3; + default: + return -1; + } +} + /* SIOCSIWAUTH */ static int ipw_wx_set_auth(struct net_device *dev, struct iw_request_info *info, @@ -6535,8 +6637,15 @@ static int ipw_wx_set_auth(struct net_de switch (param->flags & IW_AUTH_INDEX) { case IW_AUTH_WPA_VERSION: + break; case IW_AUTH_CIPHER_PAIRWISE: + level = wext_cipher2level(wrqu.u.param.value); + ipw_set_hw_decrypt_unicast(priv, level); + break; case IW_AUTH_CIPHER_GROUP: + level = wext_cipher2level(wrqu.u.param.value); + ipw_set_hw_decrypt_multicast(priv, level); + break; case IW_AUTH_KEY_MGMT: /* * ipw2200 does not use these parameters @@ -10249,11 +10358,11 @@ static void shim__set_security(struct ne priv->ieee->sec.level = sec->level; priv->ieee->sec.flags |= SEC_LEVEL; priv->status |= STATUS_SECURITY_UPDATED; - - if (!priv->ieee->host_encrypt && (sec->flags & SEC_ENCRYPT)) - ipw_set_hwcrypto_keys(priv); } + if (!priv->ieee->host_encrypt && (sec->flags & SEC_ENCRYPT)) + ipw_set_hwcrypto_keys(priv); + /* To match current functionality of ipw2100 (which works well w/ * various supplicants, we don't force a disassociate if the * privacy capability changes ... */