summaryrefslogtreecommitdiffstats
diff options
context:
space:
mode:
-rw-r--r--Makefile.inc176
-rw-r--r--ObsoleteFiles.inc2
-rw-r--r--UPDATING8
-rw-r--r--bin/ed/re.c2
-rw-r--r--bin/sh/eval.c3
-rw-r--r--cddl/contrib/opensolaris/cmd/zfs/zfs_main.c22
-rw-r--r--contrib/libstdc++/libsupc++/unwind-cxx.h2
-rw-r--r--contrib/llvm/include/llvm/CodeGen/FunctionLoweringInfo.h5
-rw-r--r--contrib/llvm/include/llvm/CodeGen/MachineBasicBlock.h5
-rw-r--r--contrib/llvm/lib/CodeGen/MachineBasicBlock.cpp33
-rw-r--r--contrib/llvm/lib/CodeGen/SelectionDAG/SelectionDAGBuilder.cpp36
-rw-r--r--contrib/llvm/lib/CodeGen/SelectionDAG/SelectionDAGISel.cpp11
-rw-r--r--contrib/llvm/lib/Target/ARM/ARMFrameLowering.h2
-rw-r--r--contrib/llvm/tools/clang/lib/Sema/SemaDecl.cpp3
-rw-r--r--contrib/smbfs/mount_smbfs/Makefile18
-rw-r--r--contrib/wpa/COPYING352
-rw-r--r--contrib/wpa/README57
-rw-r--r--contrib/wpa/hostapd/.gitignore7
-rw-r--r--contrib/wpa/hostapd/ChangeLog218
-rw-r--r--contrib/wpa/hostapd/Makefile791
-rw-r--r--contrib/wpa/hostapd/README31
-rw-r--r--contrib/wpa/hostapd/README-WPS79
-rw-r--r--contrib/wpa/hostapd/config_file.c1475
-rw-r--r--contrib/wpa/hostapd/config_file.h13
-rw-r--r--contrib/wpa/hostapd/ctrl_iface.c981
-rw-r--r--contrib/wpa/hostapd/ctrl_iface.h23
-rw-r--r--contrib/wpa/hostapd/defconfig108
-rw-r--r--contrib/wpa/hostapd/dump_state.c18
-rw-r--r--contrib/wpa/hostapd/dump_state.h10
-rw-r--r--contrib/wpa/hostapd/eap_register.c20
-rw-r--r--contrib/wpa/hostapd/eap_register.h10
-rw-r--r--contrib/wpa/hostapd/hlr_auc_gw.c367
-rw-r--r--contrib/wpa/hostapd/hlr_auc_gw.txt104
-rw-r--r--contrib/wpa/hostapd/hostapd.conf563
-rw-r--r--contrib/wpa/hostapd/hostapd.eap_user6
-rw-r--r--contrib/wpa/hostapd/hostapd.eap_user_sqlite17
-rw-r--r--contrib/wpa/hostapd/hostapd_cli.c476
-rw-r--r--contrib/wpa/hostapd/main.c226
-rw-r--r--contrib/wpa/hostapd/nt_password_hash.c10
-rw-r--r--contrib/wpa/patches/openssl-0.9.8x-tls-extensions.patch396
-rw-r--r--contrib/wpa/src/Makefile11
-rw-r--r--contrib/wpa/src/ap/Makefile8
-rw-r--r--contrib/wpa/src/ap/accounting.c153
-rw-r--r--contrib/wpa/src/ap/accounting.h17
-rw-r--r--contrib/wpa/src/ap/ap_config.c160
-rw-r--r--contrib/wpa/src/ap/ap_config.h205
-rw-r--r--contrib/wpa/src/ap/ap_drv_ops.c589
-rw-r--r--contrib/wpa/src/ap/ap_drv_ops.h189
-rw-r--r--contrib/wpa/src/ap/ap_list.c48
-rw-r--r--contrib/wpa/src/ap/ap_list.h13
-rw-r--r--contrib/wpa/src/ap/ap_mlme.c10
-rw-r--r--contrib/wpa/src/ap/ap_mlme.h10
-rw-r--r--contrib/wpa/src/ap/authsrv.c23
-rw-r--r--contrib/wpa/src/ap/authsrv.h10
-rw-r--r--contrib/wpa/src/ap/beacon.c553
-rw-r--r--contrib/wpa/src/ap/beacon.h14
-rw-r--r--contrib/wpa/src/ap/ctrl_iface_ap.c209
-rw-r--r--contrib/wpa/src/ap/ctrl_iface_ap.h14
-rw-r--r--contrib/wpa/src/ap/drv_callbacks.c584
-rw-r--r--contrib/wpa/src/ap/eap_user_db.c270
-rw-r--r--contrib/wpa/src/ap/gas_serv.c1172
-rw-r--r--contrib/wpa/src/ap/gas_serv.h71
-rw-r--r--contrib/wpa/src/ap/hostapd.c752
-rw-r--r--contrib/wpa/src/ap/hostapd.h177
-rw-r--r--contrib/wpa/src/ap/hs20.c31
-rw-r--r--contrib/wpa/src/ap/hs20.h16
-rw-r--r--contrib/wpa/src/ap/hw_features.c201
-rw-r--r--contrib/wpa/src/ap/hw_features.h11
-rw-r--r--contrib/wpa/src/ap/iapp.c10
-rw-r--r--contrib/wpa/src/ap/iapp.h10
-rw-r--r--contrib/wpa/src/ap/ieee802_11.c821
-rw-r--r--contrib/wpa/src/ap/ieee802_11.h34
-rw-r--r--contrib/wpa/src/ap/ieee802_11_auth.c289
-rw-r--r--contrib/wpa/src/ap/ieee802_11_auth.h15
-rw-r--r--contrib/wpa/src/ap/ieee802_11_ht.c31
-rw-r--r--contrib/wpa/src/ap/ieee802_11_shared.c458
-rw-r--r--contrib/wpa/src/ap/ieee802_11_vht.c110
-rw-r--r--contrib/wpa/src/ap/ieee802_1x.c557
-rw-r--r--contrib/wpa/src/ap/ieee802_1x.h54
-rw-r--r--contrib/wpa/src/ap/p2p_hostapd.c114
-rw-r--r--contrib/wpa/src/ap/p2p_hostapd.h35
-rw-r--r--contrib/wpa/src/ap/peerkey_auth.c13
-rw-r--r--contrib/wpa/src/ap/pmksa_cache_auth.c31
-rw-r--r--contrib/wpa/src/ap/pmksa_cache_auth.h13
-rw-r--r--contrib/wpa/src/ap/preauth_auth.c10
-rw-r--r--contrib/wpa/src/ap/preauth_auth.h10
-rw-r--r--contrib/wpa/src/ap/sta_info.c372
-rw-r--r--contrib/wpa/src/ap/sta_info.h61
-rw-r--r--contrib/wpa/src/ap/tkip_countermeasures.c60
-rw-r--r--contrib/wpa/src/ap/tkip_countermeasures.h15
-rw-r--r--contrib/wpa/src/ap/utils.c25
-rw-r--r--contrib/wpa/src/ap/vlan_init.c65
-rw-r--r--contrib/wpa/src/ap/vlan_util.c177
-rw-r--r--contrib/wpa/src/ap/vlan_util.h15
-rw-r--r--contrib/wpa/src/ap/wmm.c22
-rw-r--r--contrib/wpa/src/ap/wnm_ap.c271
-rw-r--r--contrib/wpa/src/ap/wnm_ap.h17
-rw-r--r--contrib/wpa/src/ap/wpa_auth.c813
-rw-r--r--contrib/wpa/src/ap/wpa_auth.h29
-rw-r--r--contrib/wpa/src/ap/wpa_auth_ft.c248
-rw-r--r--contrib/wpa/src/ap/wpa_auth_glue.c128
-rw-r--r--contrib/wpa/src/ap/wpa_auth_glue.h10
-rw-r--r--contrib/wpa/src/ap/wpa_auth_i.h22
-rw-r--r--contrib/wpa/src/ap/wpa_auth_ie.c318
-rw-r--r--contrib/wpa/src/ap/wpa_auth_ie.h10
-rw-r--r--contrib/wpa/src/ap/wps_hostapd.c911
-rw-r--r--contrib/wpa/src/ap/wps_hostapd.h46
-rw-r--r--contrib/wpa/src/common/Makefile8
-rw-r--r--contrib/wpa/src/common/defs.h110
-rw-r--r--contrib/wpa/src/common/eapol_common.h50
-rw-r--r--contrib/wpa/src/common/gas.c273
-rw-r--r--contrib/wpa/src/common/gas.h37
-rw-r--r--contrib/wpa/src/common/ieee802_11_common.c194
-rw-r--r--contrib/wpa/src/common/ieee802_11_common.h43
-rw-r--r--contrib/wpa/src/common/ieee802_11_defs.h502
-rw-r--r--contrib/wpa/src/common/privsep_commands.h10
-rw-r--r--contrib/wpa/src/common/version.h6
-rw-r--r--contrib/wpa/src/common/wpa_common.c445
-rw-r--r--contrib/wpa/src/common/wpa_common.h59
-rw-r--r--contrib/wpa/src/common/wpa_ctrl.c219
-rw-r--r--contrib/wpa/src/common/wpa_ctrl.h102
-rw-r--r--contrib/wpa/src/crypto/.gitignore1
-rw-r--r--contrib/wpa/src/crypto/Makefile56
-rw-r--r--contrib/wpa/src/crypto/aes-cbc.c10
-rw-r--r--contrib/wpa/src/crypto/aes-ccm.c212
-rw-r--r--contrib/wpa/src/crypto/aes-ctr.c10
-rw-r--r--contrib/wpa/src/crypto/aes-eax.c10
-rw-r--r--contrib/wpa/src/crypto/aes-encblock.c10
-rw-r--r--contrib/wpa/src/crypto/aes-gcm.c327
-rw-r--r--contrib/wpa/src/crypto/aes-internal-dec.c48
-rw-r--r--contrib/wpa/src/crypto/aes-internal-enc.c37
-rw-r--r--contrib/wpa/src/crypto/aes-internal.c80
-rw-r--r--contrib/wpa/src/crypto/aes-omac1.c10
-rw-r--r--contrib/wpa/src/crypto/aes-unwrap.c10
-rw-r--r--contrib/wpa/src/crypto/aes-wrap.c10
-rw-r--r--contrib/wpa/src/crypto/aes.h10
-rw-r--r--contrib/wpa/src/crypto/aes_i.h25
-rw-r--r--contrib/wpa/src/crypto/aes_wrap.h34
-rw-r--r--contrib/wpa/src/crypto/crypto.h39
-rw-r--r--contrib/wpa/src/crypto/crypto_cryptoapi.c10
-rw-r--r--contrib/wpa/src/crypto/crypto_gnutls.c10
-rw-r--r--contrib/wpa/src/crypto/crypto_internal-cipher.c45
-rw-r--r--contrib/wpa/src/crypto/crypto_internal-modexp.c10
-rw-r--r--contrib/wpa/src/crypto/crypto_internal-rsa.c11
-rw-r--r--contrib/wpa/src/crypto/crypto_internal.c94
-rw-r--r--contrib/wpa/src/crypto/crypto_libtomcrypt.c10
-rw-r--r--contrib/wpa/src/crypto/crypto_none.c10
-rw-r--r--contrib/wpa/src/crypto/crypto_nss.c10
-rw-r--r--contrib/wpa/src/crypto/crypto_openssl.c409
-rw-r--r--contrib/wpa/src/crypto/des-internal.c10
-rw-r--r--contrib/wpa/src/crypto/des_i.h10
-rw-r--r--contrib/wpa/src/crypto/dh_group5.c20
-rw-r--r--contrib/wpa/src/crypto/dh_group5.h13
-rw-r--r--contrib/wpa/src/crypto/dh_groups.c14
-rw-r--r--contrib/wpa/src/crypto/dh_groups.h10
-rw-r--r--contrib/wpa/src/crypto/fips_prf_cryptoapi.c10
-rw-r--r--contrib/wpa/src/crypto/fips_prf_gnutls.c10
-rw-r--r--contrib/wpa/src/crypto/fips_prf_internal.c15
-rw-r--r--contrib/wpa/src/crypto/fips_prf_nss.c10
-rw-r--r--contrib/wpa/src/crypto/fips_prf_openssl.c15
-rw-r--r--contrib/wpa/src/crypto/md4-internal.c10
-rw-r--r--contrib/wpa/src/crypto/md5-internal.c14
-rw-r--r--contrib/wpa/src/crypto/md5-non-fips.c113
-rw-r--r--contrib/wpa/src/crypto/md5.c10
-rw-r--r--contrib/wpa/src/crypto/md5.h20
-rw-r--r--contrib/wpa/src/crypto/md5_i.h10
-rw-r--r--contrib/wpa/src/crypto/milenage.c10
-rw-r--r--contrib/wpa/src/crypto/milenage.h10
-rw-r--r--contrib/wpa/src/crypto/ms_funcs.c128
-rw-r--r--contrib/wpa/src/crypto/ms_funcs.h10
-rw-r--r--contrib/wpa/src/crypto/random.c446
-rw-r--r--contrib/wpa/src/crypto/random.h28
-rw-r--r--contrib/wpa/src/crypto/rc4.c10
-rw-r--r--contrib/wpa/src/crypto/sha1-internal.c10
-rw-r--r--contrib/wpa/src/crypto/sha1-pbkdf2.c18
-rw-r--r--contrib/wpa/src/crypto/sha1-prf.c66
-rw-r--r--contrib/wpa/src/crypto/sha1-tlsprf.c26
-rw-r--r--contrib/wpa/src/crypto/sha1-tprf.c10
-rw-r--r--contrib/wpa/src/crypto/sha1.c63
-rw-r--r--contrib/wpa/src/crypto/sha1.h18
-rw-r--r--contrib/wpa/src/crypto/sha1_i.h10
-rw-r--r--contrib/wpa/src/crypto/sha256-internal.c53
-rw-r--r--contrib/wpa/src/crypto/sha256-prf.c64
-rw-r--r--contrib/wpa/src/crypto/sha256-tlsprf.c66
-rw-r--r--contrib/wpa/src/crypto/sha256.c87
-rw-r--r--contrib/wpa/src/crypto/sha256.h23
-rw-r--r--contrib/wpa/src/crypto/sha256_i.h25
-rw-r--r--contrib/wpa/src/crypto/tls.h90
-rw-r--r--contrib/wpa/src/crypto/tls_gnutls.c341
-rw-r--r--contrib/wpa/src/crypto/tls_internal.c87
-rw-r--r--contrib/wpa/src/crypto/tls_none.c39
-rw-r--r--contrib/wpa/src/crypto/tls_nss.c39
-rw-r--r--contrib/wpa/src/crypto/tls_openssl.c254
-rw-r--r--contrib/wpa/src/crypto/tls_schannel.c39
-rw-r--r--contrib/wpa/src/drivers/.gitignore2
-rw-r--r--contrib/wpa/src/drivers/Makefile9
-rw-r--r--contrib/wpa/src/drivers/driver.h1685
-rw-r--r--contrib/wpa/src/drivers/driver_bsd.c1627
-rw-r--r--contrib/wpa/src/drivers/driver_common.c86
-rw-r--r--contrib/wpa/src/drivers/driver_ndis.c174
-rw-r--r--contrib/wpa/src/drivers/driver_ndis.h10
-rw-r--r--contrib/wpa/src/drivers/driver_ndis_.c10
-rw-r--r--contrib/wpa/src/drivers/driver_ndiswrapper.c378
-rw-r--r--contrib/wpa/src/drivers/driver_privsep.c740
-rw-r--r--contrib/wpa/src/drivers/driver_wired.c19
-rw-r--r--contrib/wpa/src/drivers/drivers.c59
-rw-r--r--contrib/wpa/src/drivers/drivers.mak181
-rw-r--r--contrib/wpa/src/drivers/ndis_events.c10
-rw-r--r--contrib/wpa/src/eap_common/Makefile8
-rw-r--r--contrib/wpa/src/eap_common/chap.c10
-rw-r--r--contrib/wpa/src/eap_common/chap.h10
-rw-r--r--contrib/wpa/src/eap_common/eap_common.c59
-rw-r--r--contrib/wpa/src/eap_common/eap_common.h13
-rw-r--r--contrib/wpa/src/eap_common/eap_defs.h19
-rw-r--r--contrib/wpa/src/eap_common/eap_fast_common.c16
-rw-r--r--contrib/wpa/src/eap_common/eap_fast_common.h10
-rw-r--r--contrib/wpa/src/eap_common/eap_gpsk_common.c10
-rw-r--r--contrib/wpa/src/eap_common/eap_gpsk_common.h10
-rw-r--r--contrib/wpa/src/eap_common/eap_ikev2_common.c10
-rw-r--r--contrib/wpa/src/eap_common/eap_ikev2_common.h10
-rw-r--r--contrib/wpa/src/eap_common/eap_pax_common.c10
-rw-r--r--contrib/wpa/src/eap_common/eap_pax_common.h10
-rw-r--r--contrib/wpa/src/eap_common/eap_peap_common.c23
-rw-r--r--contrib/wpa/src/eap_common/eap_peap_common.h18
-rw-r--r--contrib/wpa/src/eap_common/eap_psk_common.c10
-rw-r--r--contrib/wpa/src/eap_common/eap_psk_common.h10
-rw-r--r--contrib/wpa/src/eap_common/eap_pwd_common.c345
-rw-r--r--contrib/wpa/src/eap_common/eap_pwd_common.h67
-rw-r--r--contrib/wpa/src/eap_common/eap_sake_common.c10
-rw-r--r--contrib/wpa/src/eap_common/eap_sake_common.h10
-rw-r--r--contrib/wpa/src/eap_common/eap_sim_common.c15
-rw-r--r--contrib/wpa/src/eap_common/eap_sim_common.h10
-rw-r--r--contrib/wpa/src/eap_common/eap_tlv_common.h10
-rw-r--r--contrib/wpa/src/eap_common/eap_ttls.h10
-rw-r--r--contrib/wpa/src/eap_common/eap_wsc_common.c10
-rw-r--r--contrib/wpa/src/eap_common/eap_wsc_common.h10
-rw-r--r--contrib/wpa/src/eap_common/ikev2_common.c13
-rw-r--r--contrib/wpa/src/eap_common/ikev2_common.h12
-rw-r--r--contrib/wpa/src/eap_peer/Makefile11
-rw-r--r--contrib/wpa/src/eap_peer/eap.c341
-rw-r--r--contrib/wpa/src/eap_peer/eap.h54
-rw-r--r--contrib/wpa/src/eap_peer/eap_aka.c106
-rw-r--r--contrib/wpa/src/eap_peer/eap_config.h16
-rw-r--r--contrib/wpa/src/eap_peer/eap_fast.c29
-rw-r--r--contrib/wpa/src/eap_peer/eap_fast_pac.c19
-rw-r--r--contrib/wpa/src/eap_peer/eap_fast_pac.h10
-rw-r--r--contrib/wpa/src/eap_peer/eap_gpsk.c13
-rw-r--r--contrib/wpa/src/eap_peer/eap_gtc.c10
-rw-r--r--contrib/wpa/src/eap_peer/eap_i.h15
-rw-r--r--contrib/wpa/src/eap_peer/eap_ikev2.c10
-rw-r--r--contrib/wpa/src/eap_peer/eap_leap.c13
-rw-r--r--contrib/wpa/src/eap_peer/eap_md5.c20
-rw-r--r--contrib/wpa/src/eap_peer/eap_methods.c12
-rw-r--r--contrib/wpa/src/eap_peer/eap_methods.h12
-rw-r--r--contrib/wpa/src/eap_peer/eap_mschapv2.c19
-rw-r--r--contrib/wpa/src/eap_peer/eap_otp.c10
-rw-r--r--contrib/wpa/src/eap_peer/eap_pax.c13
-rw-r--r--contrib/wpa/src/eap_peer/eap_peap.c33
-rw-r--r--contrib/wpa/src/eap_peer/eap_psk.c13
-rw-r--r--contrib/wpa/src/eap_peer/eap_pwd.c922
-rw-r--r--contrib/wpa/src/eap_peer/eap_sake.c13
-rw-r--r--contrib/wpa/src/eap_peer/eap_sim.c117
-rw-r--r--contrib/wpa/src/eap_peer/eap_tls.c93
-rw-r--r--contrib/wpa/src/eap_peer/eap_tls_common.c125
-rw-r--r--contrib/wpa/src/eap_peer/eap_tls_common.h30
-rw-r--r--contrib/wpa/src/eap_peer/eap_tnc.c11
-rw-r--r--contrib/wpa/src/eap_peer/eap_ttls.c483
-rw-r--r--contrib/wpa/src/eap_peer/eap_vendor_test.c12
-rw-r--r--contrib/wpa/src/eap_peer/eap_wsc.c43
-rw-r--r--contrib/wpa/src/eap_peer/ikev2.c15
-rw-r--r--contrib/wpa/src/eap_peer/ikev2.h10
-rw-r--r--contrib/wpa/src/eap_peer/mschapv2.c45
-rw-r--r--contrib/wpa/src/eap_peer/mschapv2.h10
-rw-r--r--contrib/wpa/src/eap_peer/tncc.c14
-rw-r--r--contrib/wpa/src/eap_peer/tncc.h10
-rw-r--r--contrib/wpa/src/eap_server/Makefile8
-rw-r--r--contrib/wpa/src/eap_server/eap.h18
-rw-r--r--contrib/wpa/src/eap_server/eap_i.h21
-rw-r--r--contrib/wpa/src/eap_server/eap_methods.h12
-rw-r--r--contrib/wpa/src/eap_server/eap_server.c59
-rw-r--r--contrib/wpa/src/eap_server/eap_server_aka.c392
-rw-r--r--contrib/wpa/src/eap_server/eap_server_fast.c15
-rw-r--r--contrib/wpa/src/eap_server/eap_server_gpsk.c13
-rw-r--r--contrib/wpa/src/eap_server/eap_server_gtc.c10
-rw-r--r--contrib/wpa/src/eap_server/eap_server_identity.c10
-rw-r--r--contrib/wpa/src/eap_server/eap_server_ikev2.c13
-rw-r--r--contrib/wpa/src/eap_server/eap_server_md5.c23
-rw-r--r--contrib/wpa/src/eap_server/eap_server_methods.c12
-rw-r--r--contrib/wpa/src/eap_server/eap_server_mschapv2.c22
-rw-r--r--contrib/wpa/src/eap_server/eap_server_pax.c13
-rw-r--r--contrib/wpa/src/eap_server/eap_server_peap.c30
-rw-r--r--contrib/wpa/src/eap_server/eap_server_psk.c17
-rw-r--r--contrib/wpa/src/eap_server/eap_server_pwd.c1045
-rw-r--r--contrib/wpa/src/eap_server/eap_server_sake.c13
-rw-r--r--contrib/wpa/src/eap_server/eap_server_sim.c276
-rw-r--r--contrib/wpa/src/eap_server/eap_server_tls.c86
-rw-r--r--contrib/wpa/src/eap_server/eap_server_tls_common.c52
-rw-r--r--contrib/wpa/src/eap_server/eap_server_tnc.c14
-rw-r--r--contrib/wpa/src/eap_server/eap_server_ttls.c295
-rw-r--r--contrib/wpa/src/eap_server/eap_server_vendor_test.c12
-rw-r--r--contrib/wpa/src/eap_server/eap_server_wsc.c25
-rw-r--r--contrib/wpa/src/eap_server/eap_sim_db.c982
-rw-r--r--contrib/wpa/src/eap_server/eap_sim_db.h90
-rw-r--r--contrib/wpa/src/eap_server/eap_tls_common.h15
-rw-r--r--contrib/wpa/src/eap_server/ikev2.c17
-rw-r--r--contrib/wpa/src/eap_server/ikev2.h10
-rw-r--r--contrib/wpa/src/eap_server/tncs.c14
-rw-r--r--contrib/wpa/src/eap_server/tncs.h10
-rw-r--r--contrib/wpa/src/eapol_auth/Makefile8
-rw-r--r--contrib/wpa/src/eapol_auth/eapol_auth_dump.c10
-rw-r--r--contrib/wpa/src/eapol_auth/eapol_auth_sm.c32
-rw-r--r--contrib/wpa/src/eapol_auth/eapol_auth_sm.h17
-rw-r--r--contrib/wpa/src/eapol_auth/eapol_auth_sm_i.h11
-rw-r--r--contrib/wpa/src/eapol_supp/Makefile8
-rw-r--r--contrib/wpa/src/eapol_supp/eapol_supp_sm.c158
-rw-r--r--contrib/wpa/src/eapol_supp/eapol_supp_sm.h66
-rw-r--r--contrib/wpa/src/l2_packet/Makefile8
-rw-r--r--contrib/wpa/src/l2_packet/l2_packet.h10
-rw-r--r--contrib/wpa/src/l2_packet/l2_packet_freebsd.c41
-rw-r--r--contrib/wpa/src/l2_packet/l2_packet_ndis.c10
-rw-r--r--contrib/wpa/src/l2_packet/l2_packet_none.c10
-rw-r--r--contrib/wpa/src/l2_packet/l2_packet_privsep.c261
-rw-r--r--contrib/wpa/src/lib.rules21
-rw-r--r--contrib/wpa/src/p2p/p2p.c4320
-rw-r--r--contrib/wpa/src/p2p/p2p.h1783
-rw-r--r--contrib/wpa/src/p2p/p2p_build.c431
-rw-r--r--contrib/wpa/src/p2p/p2p_dev_disc.c359
-rw-r--r--contrib/wpa/src/p2p/p2p_go_neg.c1242
-rw-r--r--contrib/wpa/src/p2p/p2p_group.c953
-rw-r--r--contrib/wpa/src/p2p/p2p_i.h714
-rw-r--r--contrib/wpa/src/p2p/p2p_invitation.c608
-rw-r--r--contrib/wpa/src/p2p/p2p_parse.c723
-rw-r--r--contrib/wpa/src/p2p/p2p_pd.c484
-rw-r--r--contrib/wpa/src/p2p/p2p_sd.c947
-rw-r--r--contrib/wpa/src/p2p/p2p_utils.c265
-rw-r--r--contrib/wpa/src/radius/.gitignore1
-rw-r--r--contrib/wpa/src/radius/Makefile22
-rw-r--r--contrib/wpa/src/radius/radius.c341
-rw-r--r--contrib/wpa/src/radius/radius.h38
-rw-r--r--contrib/wpa/src/radius/radius_client.c26
-rw-r--r--contrib/wpa/src/radius/radius_client.h12
-rw-r--r--contrib/wpa/src/radius/radius_das.c364
-rw-r--r--contrib/wpa/src/radius/radius_das.h47
-rw-r--r--contrib/wpa/src/radius/radius_server.c63
-rw-r--r--contrib/wpa/src/radius/radius_server.h23
-rw-r--r--contrib/wpa/src/rsn_supp/Makefile8
-rw-r--r--contrib/wpa/src/rsn_supp/peerkey.c54
-rw-r--r--contrib/wpa/src/rsn_supp/peerkey.h10
-rw-r--r--contrib/wpa/src/rsn_supp/pmksa_cache.c152
-rw-r--r--contrib/wpa/src/rsn_supp/pmksa_cache.h39
-rw-r--r--contrib/wpa/src/rsn_supp/preauth.c17
-rw-r--r--contrib/wpa/src/rsn_supp/preauth.h10
-rw-r--r--contrib/wpa/src/rsn_supp/tdls.c2334
-rw-r--r--contrib/wpa/src/rsn_supp/wpa.c894
-rw-r--r--contrib/wpa/src/rsn_supp/wpa.h55
-rw-r--r--contrib/wpa/src/rsn_supp/wpa_ft.c264
-rw-r--r--contrib/wpa/src/rsn_supp/wpa_i.h86
-rw-r--r--contrib/wpa/src/rsn_supp/wpa_ie.c224
-rw-r--r--contrib/wpa/src/rsn_supp/wpa_ie.h22
-rw-r--r--contrib/wpa/src/tls/.gitignore1
-rw-r--r--contrib/wpa/src/tls/Makefile37
-rw-r--r--contrib/wpa/src/tls/asn1.c10
-rw-r--r--contrib/wpa/src/tls/asn1.h10
-rw-r--r--contrib/wpa/src/tls/bignum.c10
-rw-r--r--contrib/wpa/src/tls/bignum.h10
-rw-r--r--contrib/wpa/src/tls/libtommath.c22
-rw-r--r--contrib/wpa/src/tls/pkcs1.c10
-rw-r--r--contrib/wpa/src/tls/pkcs1.h10
-rw-r--r--contrib/wpa/src/tls/pkcs5.c12
-rw-r--r--contrib/wpa/src/tls/pkcs5.h10
-rw-r--r--contrib/wpa/src/tls/pkcs8.c10
-rw-r--r--contrib/wpa/src/tls/pkcs8.h10
-rw-r--r--contrib/wpa/src/tls/rsa.c10
-rw-r--r--contrib/wpa/src/tls/rsa.h10
-rw-r--r--contrib/wpa/src/tls/tlsv1_client.c257
-rw-r--r--contrib/wpa/src/tls/tlsv1_client.h23
-rw-r--r--contrib/wpa/src/tls/tlsv1_client_i.h15
-rw-r--r--contrib/wpa/src/tls/tlsv1_client_read.c51
-rw-r--r--contrib/wpa/src/tls/tlsv1_client_write.c161
-rw-r--r--contrib/wpa/src/tls/tlsv1_common.c101
-rw-r--r--contrib/wpa/src/tls/tlsv1_common.h67
-rw-r--r--contrib/wpa/src/tls/tlsv1_cred.c35
-rw-r--r--contrib/wpa/src/tls/tlsv1_cred.h10
-rw-r--r--contrib/wpa/src/tls/tlsv1_record.c230
-rw-r--r--contrib/wpa/src/tls/tlsv1_record.h19
-rw-r--r--contrib/wpa/src/tls/tlsv1_server.c104
-rw-r--r--contrib/wpa/src/tls/tlsv1_server.h14
-rw-r--r--contrib/wpa/src/tls/tlsv1_server_i.h10
-rw-r--r--contrib/wpa/src/tls/tlsv1_server_read.c157
-rw-r--r--contrib/wpa/src/tls/tlsv1_server_write.c125
-rw-r--r--contrib/wpa/src/tls/x509v3.c23
-rw-r--r--contrib/wpa/src/tls/x509v3.h14
-rw-r--r--contrib/wpa/src/utils/.gitignore1
-rw-r--r--contrib/wpa/src/utils/Makefile36
-rw-r--r--contrib/wpa/src/utils/base64.c37
-rw-r--r--contrib/wpa/src/utils/base64.h10
-rw-r--r--contrib/wpa/src/utils/build_config.h32
-rw-r--r--contrib/wpa/src/utils/common.c287
-rw-r--r--contrib/wpa/src/utils/common.h60
-rw-r--r--contrib/wpa/src/utils/edit.c1174
-rw-r--r--contrib/wpa/src/utils/edit.h21
-rw-r--r--contrib/wpa/src/utils/edit_readline.c192
-rw-r--r--contrib/wpa/src/utils/edit_simple.c92
-rw-r--r--contrib/wpa/src/utils/eloop.c277
-rw-r--r--contrib/wpa/src/utils/eloop.h12
-rw-r--r--contrib/wpa/src/utils/eloop_none.c10
-rw-r--r--contrib/wpa/src/utils/eloop_win.c39
-rw-r--r--contrib/wpa/src/utils/ext_password.c116
-rw-r--r--contrib/wpa/src/utils/ext_password.h33
-rw-r--r--contrib/wpa/src/utils/ext_password_i.h23
-rw-r--r--contrib/wpa/src/utils/ext_password_test.c90
-rw-r--r--contrib/wpa/src/utils/includes.h13
-rw-r--r--contrib/wpa/src/utils/ip_addr.c10
-rw-r--r--contrib/wpa/src/utils/ip_addr.h10
-rw-r--r--contrib/wpa/src/utils/list.h22
-rw-r--r--contrib/wpa/src/utils/os.h47
-rw-r--r--contrib/wpa/src/utils/os_internal.c28
-rw-r--r--contrib/wpa/src/utils/os_none.c15
-rw-r--r--contrib/wpa/src/utils/os_unix.c81
-rw-r--r--contrib/wpa/src/utils/os_win32.c29
-rw-r--r--contrib/wpa/src/utils/pcsc_funcs.c309
-rw-r--r--contrib/wpa/src/utils/pcsc_funcs.h41
-rw-r--r--contrib/wpa/src/utils/radiotap.h3
-rw-r--r--contrib/wpa/src/utils/radiotap_iter.h15
-rw-r--r--contrib/wpa/src/utils/state_machine.h10
-rw-r--r--contrib/wpa/src/utils/trace.c10
-rw-r--r--contrib/wpa/src/utils/trace.h10
-rw-r--r--contrib/wpa/src/utils/uuid.c10
-rw-r--r--contrib/wpa/src/utils/uuid.h10
-rw-r--r--contrib/wpa/src/utils/wpa_debug.c314
-rw-r--r--contrib/wpa/src/utils/wpa_debug.h59
-rw-r--r--contrib/wpa/src/utils/wpabuf.c29
-rw-r--r--contrib/wpa/src/utils/wpabuf.h36
-rw-r--r--contrib/wpa/src/wps/Makefile8
-rw-r--r--contrib/wpa/src/wps/http_client.c23
-rw-r--r--contrib/wpa/src/wps/http_client.h10
-rw-r--r--contrib/wpa/src/wps/http_server.c10
-rw-r--r--contrib/wpa/src/wps/http_server.h10
-rw-r--r--contrib/wpa/src/wps/httpread.c10
-rw-r--r--contrib/wpa/src/wps/httpread.h10
-rw-r--r--contrib/wpa/src/wps/ndef.c118
-rw-r--r--contrib/wpa/src/wps/upnp_xml.c4
-rw-r--r--contrib/wpa/src/wps/upnp_xml.h2
-rw-r--r--contrib/wpa/src/wps/wps.c244
-rw-r--r--contrib/wpa/src/wps/wps.h350
-rw-r--r--contrib/wpa/src/wps/wps_attr_build.c161
-rw-r--r--contrib/wpa/src/wps/wps_attr_parse.c197
-rw-r--r--contrib/wpa/src/wps/wps_attr_parse.h108
-rw-r--r--contrib/wpa/src/wps/wps_attr_process.c45
-rw-r--r--contrib/wpa/src/wps/wps_common.c332
-rw-r--r--contrib/wpa/src/wps/wps_defs.h57
-rw-r--r--contrib/wpa/src/wps/wps_dev_attr.c150
-rw-r--r--contrib/wpa/src/wps/wps_dev_attr.h22
-rw-r--r--contrib/wpa/src/wps/wps_enrollee.c365
-rw-r--r--contrib/wpa/src/wps/wps_er.c429
-rw-r--r--contrib/wpa/src/wps/wps_er.h21
-rw-r--r--contrib/wpa/src/wps/wps_er_ssdp.c29
-rw-r--r--contrib/wpa/src/wps/wps_i.h129
-rw-r--r--contrib/wpa/src/wps/wps_nfc.c117
-rw-r--r--contrib/wpa/src/wps/wps_nfc_pn531.c113
-rw-r--r--contrib/wpa/src/wps/wps_registrar.c1092
-rw-r--r--contrib/wpa/src/wps/wps_ufd.c235
-rw-r--r--contrib/wpa/src/wps/wps_upnp.c290
-rw-r--r--contrib/wpa/src/wps/wps_upnp.h7
-rw-r--r--contrib/wpa/src/wps/wps_upnp_ap.c47
-rw-r--r--contrib/wpa/src/wps/wps_upnp_event.c150
-rw-r--r--contrib/wpa/src/wps/wps_upnp_i.h35
-rw-r--r--contrib/wpa/src/wps/wps_upnp_ssdp.c41
-rw-r--r--contrib/wpa/src/wps/wps_upnp_web.c136
-rw-r--r--contrib/wpa/src/wps/wps_validate.c1975
-rw-r--r--contrib/wpa/wpa_supplicant/.gitignore8
-rw-r--r--contrib/wpa/wpa_supplicant/ChangeLog421
-rw-r--r--contrib/wpa/wpa_supplicant/Makefile1380
-rw-r--r--contrib/wpa/wpa_supplicant/README128
-rw-r--r--contrib/wpa/wpa_supplicant/README-HS20475
-rw-r--r--contrib/wpa/wpa_supplicant/README-P2P560
-rw-r--r--contrib/wpa/wpa_supplicant/README-WPS189
-rw-r--r--contrib/wpa/wpa_supplicant/ap.c648
-rw-r--r--contrib/wpa/wpa_supplicant/ap.h35
-rw-r--r--contrib/wpa/wpa_supplicant/autoscan.c143
-rw-r--r--contrib/wpa/wpa_supplicant/autoscan.h49
-rw-r--r--contrib/wpa/wpa_supplicant/autoscan_exponential.c104
-rw-r--r--contrib/wpa/wpa_supplicant/autoscan_periodic.c85
-rw-r--r--contrib/wpa/wpa_supplicant/bgscan.c31
-rw-r--r--contrib/wpa/wpa_supplicant/bgscan.h31
-rw-r--r--contrib/wpa/wpa_supplicant/bgscan_learn.c607
-rw-r--r--contrib/wpa/wpa_supplicant/bgscan_simple.c79
-rw-r--r--contrib/wpa/wpa_supplicant/blacklist.c30
-rw-r--r--contrib/wpa/wpa_supplicant/blacklist.h10
-rw-r--r--contrib/wpa/wpa_supplicant/bss.c618
-rw-r--r--contrib/wpa/wpa_supplicant/bss.h82
-rw-r--r--contrib/wpa/wpa_supplicant/config.c1123
-rw-r--r--contrib/wpa/wpa_supplicant/config.h514
-rw-r--r--contrib/wpa/wpa_supplicant/config_file.c601
-rw-r--r--contrib/wpa/wpa_supplicant/config_none.c10
-rw-r--r--contrib/wpa/wpa_supplicant/config_ssid.h211
-rw-r--r--contrib/wpa/wpa_supplicant/ctrl_iface.c3869
-rw-r--r--contrib/wpa/wpa_supplicant/ctrl_iface.h10
-rw-r--r--contrib/wpa/wpa_supplicant/ctrl_iface_named_pipe.c10
-rw-r--r--contrib/wpa/wpa_supplicant/ctrl_iface_udp.c49
-rw-r--r--contrib/wpa/wpa_supplicant/ctrl_iface_unix.c74
-rw-r--r--contrib/wpa/wpa_supplicant/dbus/.gitignore1
-rw-r--r--contrib/wpa/wpa_supplicant/dbus/Makefile17
-rw-r--r--contrib/wpa/wpa_supplicant/dbus/dbus_common.c10
-rw-r--r--contrib/wpa/wpa_supplicant/dbus/dbus_common.h10
-rw-r--r--contrib/wpa/wpa_supplicant/dbus/dbus_common_i.h14
-rw-r--r--contrib/wpa/wpa_supplicant/dbus/dbus_dict_helpers.c261
-rw-r--r--contrib/wpa/wpa_supplicant/dbus/dbus_dict_helpers.h56
-rw-r--r--contrib/wpa/wpa_supplicant/dbus/dbus_new.c2414
-rw-r--r--contrib/wpa/wpa_supplicant/dbus/dbus_new.h282
-rw-r--r--contrib/wpa/wpa_supplicant/dbus/dbus_new_handlers.c2322
-rw-r--r--contrib/wpa/wpa_supplicant/dbus/dbus_new_handlers.h277
-rw-r--r--contrib/wpa/wpa_supplicant/dbus/dbus_new_handlers_p2p.c2438
-rw-r--r--contrib/wpa/wpa_supplicant/dbus/dbus_new_handlers_p2p.h211
-rw-r--r--contrib/wpa/wpa_supplicant/dbus/dbus_new_handlers_wps.c120
-rw-r--r--contrib/wpa/wpa_supplicant/dbus/dbus_new_helpers.c540
-rw-r--r--contrib/wpa/wpa_supplicant/dbus/dbus_new_helpers.h41
-rw-r--r--contrib/wpa/wpa_supplicant/dbus/dbus_new_introspect.c27
-rw-r--r--contrib/wpa/wpa_supplicant/dbus/dbus_old.c66
-rw-r--r--contrib/wpa/wpa_supplicant/dbus/dbus_old.h22
-rw-r--r--contrib/wpa/wpa_supplicant/dbus/dbus_old_handlers.c54
-rw-r--r--contrib/wpa/wpa_supplicant/dbus/dbus_old_handlers.h13
-rw-r--r--contrib/wpa/wpa_supplicant/dbus/dbus_old_handlers_wps.c20
-rw-r--r--contrib/wpa/wpa_supplicant/dbus/fi.epitest.hostap.WPASupplicant.service4
-rw-r--r--contrib/wpa/wpa_supplicant/dbus/fi.epitest.hostap.WPASupplicant.service.in5
-rw-r--r--contrib/wpa/wpa_supplicant/dbus/fi.w1.wpa_supplicant1.service4
-rw-r--r--contrib/wpa/wpa_supplicant/dbus/fi.w1.wpa_supplicant1.service.in5
-rw-r--r--contrib/wpa/wpa_supplicant/defconfig157
-rw-r--r--contrib/wpa/wpa_supplicant/doc/docbook/.gitignore6
-rw-r--r--contrib/wpa/wpa_supplicant/doc/docbook/Makefile27
-rw-r--r--contrib/wpa/wpa_supplicant/doc/docbook/manpage.links0
-rw-r--r--contrib/wpa/wpa_supplicant/doc/docbook/manpage.refs4
-rw-r--r--contrib/wpa/wpa_supplicant/doc/docbook/wpa_background.884
-rw-r--r--contrib/wpa/wpa_supplicant/doc/docbook/wpa_background.sgml101
-rw-r--r--contrib/wpa/wpa_supplicant/doc/docbook/wpa_cli.8210
-rw-r--r--contrib/wpa/wpa_supplicant/doc/docbook/wpa_cli.sgml339
-rw-r--r--contrib/wpa/wpa_supplicant/doc/docbook/wpa_gui.851
-rw-r--r--contrib/wpa/wpa_supplicant/doc/docbook/wpa_gui.sgml85
-rw-r--r--contrib/wpa/wpa_supplicant/doc/docbook/wpa_passphrase.840
-rw-r--r--contrib/wpa/wpa_supplicant/doc/docbook/wpa_passphrase.sgml73
-rw-r--r--contrib/wpa/wpa_supplicant/doc/docbook/wpa_priv.8120
-rw-r--r--contrib/wpa/wpa_supplicant/doc/docbook/wpa_priv.sgml148
-rw-r--r--contrib/wpa/wpa_supplicant/doc/docbook/wpa_supplicant.8583
-rw-r--r--contrib/wpa/wpa_supplicant/doc/docbook/wpa_supplicant.conf.5225
-rw-r--r--contrib/wpa/wpa_supplicant/doc/docbook/wpa_supplicant.conf.sgml239
-rw-r--r--contrib/wpa/wpa_supplicant/doc/docbook/wpa_supplicant.sgml827
-rw-r--r--contrib/wpa/wpa_supplicant/driver_i.h384
-rw-r--r--contrib/wpa/wpa_supplicant/eap_register.c30
-rw-r--r--contrib/wpa/wpa_supplicant/eapol_test.c200
-rw-r--r--contrib/wpa/wpa_supplicant/events.c2142
-rwxr-xr-xcontrib/wpa/wpa_supplicant/examples/dbus-listen-preq.py62
-rwxr-xr-xcontrib/wpa/wpa_supplicant/examples/p2p-action-udhcp.sh69
-rwxr-xr-xcontrib/wpa/wpa_supplicant/examples/p2p-action.sh83
-rw-r--r--contrib/wpa/wpa_supplicant/examples/p2p/p2p_connect.py299
-rw-r--r--contrib/wpa/wpa_supplicant/examples/p2p/p2p_disconnect.py169
-rw-r--r--contrib/wpa/wpa_supplicant/examples/p2p/p2p_find.py192
-rw-r--r--contrib/wpa/wpa_supplicant/examples/p2p/p2p_flush.py168
-rw-r--r--contrib/wpa/wpa_supplicant/examples/p2p/p2p_group_add.py222
-rw-r--r--contrib/wpa/wpa_supplicant/examples/p2p/p2p_invite.py201
-rw-r--r--contrib/wpa/wpa_supplicant/examples/p2p/p2p_listen.py182
-rw-r--r--contrib/wpa/wpa_supplicant/examples/p2p/p2p_stop_find.py174
-rw-r--r--contrib/wpa/wpa_supplicant/examples/udhcpd-p2p.conf120
-rwxr-xr-xcontrib/wpa/wpa_supplicant/examples/wpas-dbus-new-signals.py4
-rwxr-xr-xcontrib/wpa/wpa_supplicant/examples/wps-ap-cli78
-rwxr-xr-xcontrib/wpa/wpa_supplicant/examples/wps-nfc.py162
-rw-r--r--contrib/wpa/wpa_supplicant/gas_query.c519
-rw-r--r--contrib/wpa/wpa_supplicant/gas_query.h58
-rw-r--r--contrib/wpa/wpa_supplicant/hs20_supplicant.c185
-rw-r--r--contrib/wpa/wpa_supplicant/hs20_supplicant.h20
-rw-r--r--contrib/wpa/wpa_supplicant/ibss_rsn.c176
-rw-r--r--contrib/wpa/wpa_supplicant/ibss_rsn.h11
-rw-r--r--contrib/wpa/wpa_supplicant/interworking.c2107
-rw-r--r--contrib/wpa/wpa_supplicant/interworking.h32
-rw-r--r--contrib/wpa/wpa_supplicant/main.c68
-rw-r--r--contrib/wpa/wpa_supplicant/main_none.c10
-rw-r--r--contrib/wpa/wpa_supplicant/mlme.c3198
-rw-r--r--contrib/wpa/wpa_supplicant/mlme.h129
-rw-r--r--contrib/wpa/wpa_supplicant/nfc_pw_token.c83
-rw-r--r--contrib/wpa/wpa_supplicant/notify.c311
-rw-r--r--contrib/wpa/wpa_supplicant/notify.h66
-rw-r--r--contrib/wpa/wpa_supplicant/offchannel.c397
-rw-r--r--contrib/wpa/wpa_supplicant/offchannel.h35
-rw-r--r--contrib/wpa/wpa_supplicant/p2p_supplicant.c5484
-rw-r--r--contrib/wpa/wpa_supplicant/p2p_supplicant.h151
-rw-r--r--contrib/wpa/wpa_supplicant/preauth_test.c17
-rw-r--r--contrib/wpa/wpa_supplicant/scan.c1092
-rw-r--r--contrib/wpa/wpa_supplicant/scan.h19
-rw-r--r--contrib/wpa/wpa_supplicant/sme.c971
-rw-r--r--contrib/wpa/wpa_supplicant/sme.h51
-rw-r--r--contrib/wpa/wpa_supplicant/tests/test_eap_sim_common.c12
-rw-r--r--contrib/wpa/wpa_supplicant/tests/test_wpa.c10
-rwxr-xr-xcontrib/wpa/wpa_supplicant/utils/log2pcap.py54
-rw-r--r--contrib/wpa/wpa_supplicant/wifi_display.c251
-rw-r--r--contrib/wpa/wpa_supplicant/wifi_display.h20
-rw-r--r--contrib/wpa/wpa_supplicant/wnm_sta.c423
-rw-r--r--contrib/wpa/wpa_supplicant/wnm_sta.h21
-rw-r--r--contrib/wpa/wpa_supplicant/wpa_cli.c2659
-rw-r--r--contrib/wpa/wpa_supplicant/wpa_passphrase.c12
-rw-r--r--contrib/wpa/wpa_supplicant/wpa_priv.c16
-rw-r--r--contrib/wpa/wpa_supplicant/wpa_supplicant.c2080
-rw-r--r--contrib/wpa/wpa_supplicant/wpa_supplicant.conf316
-rw-r--r--contrib/wpa/wpa_supplicant/wpa_supplicant.nsi112
-rw-r--r--contrib/wpa/wpa_supplicant/wpa_supplicant_conf.mk34
-rwxr-xr-xcontrib/wpa/wpa_supplicant/wpa_supplicant_conf.sh16
-rw-r--r--contrib/wpa/wpa_supplicant/wpa_supplicant_i.h520
-rw-r--r--contrib/wpa/wpa_supplicant/wpa_supplicant_template.conf6
-rw-r--r--contrib/wpa/wpa_supplicant/wpas_glue.c300
-rw-r--r--contrib/wpa/wpa_supplicant/wpas_glue.h18
-rw-r--r--contrib/wpa/wpa_supplicant/wps_supplicant.c1193
-rw-r--r--contrib/wpa/wpa_supplicant/wps_supplicant.h67
-rw-r--r--crypto/heimdal/lib/gssapi/gssapi/gssapi_krb5.h2
-rw-r--r--crypto/openssh/sshd_config2
-rw-r--r--crypto/openssh/sshd_config.52
-rw-r--r--etc/defaults/rc.conf2
-rw-r--r--etc/mtree/BSD.include.dist2
-rw-r--r--etc/mtree/BSD.usr.dist4
-rw-r--r--etc/network.subr28
-rw-r--r--etc/newsyslog.conf1
-rwxr-xr-xetc/rc.resume4
-rw-r--r--etc/syslog.conf4
-rw-r--r--gnu/usr.bin/patch/pch.c11
-rw-r--r--include/Makefile5
-rw-r--r--include/gssapi/gssapi.h27
-rw-r--r--include/iconv.h6
-rw-r--r--include/stdlib.h2
-rw-r--r--lib/Makefile8
-rw-r--r--lib/libc/gen/siginterrupt.c2
-rw-r--r--lib/libc/gen/signal.c2
-rw-r--r--lib/libc/iconv/Symbol.map10
-rw-r--r--lib/libc/iconv/citrus_csmapper.c6
-rw-r--r--lib/libc/iconv/citrus_iconv.c14
-rw-r--r--lib/libc/iconv/citrus_iconv.h2
-rw-r--r--lib/libc/iconv/citrus_iconv_local.h4
-rw-r--r--lib/libc/iconv/citrus_lock.h10
-rw-r--r--lib/libc/iconv/citrus_mapper.c24
-rw-r--r--lib/libc/iconv/citrus_none.c4
-rw-r--r--lib/libc/iconv/citrus_stdenc.h2
-rw-r--r--lib/libc/iconv/citrus_stdenc_local.h8
-rw-r--r--lib/libc/iconv/citrus_stdenc_template.h4
-rw-r--r--lib/libc/iconv/iconv.c21
-rw-r--r--lib/libc/locale/cXXrtomb_iconv.h3
-rw-r--r--lib/libc/locale/mbrtocXX_iconv.h3
-rw-r--r--lib/libc/stdlib/rand.c41
-rw-r--r--lib/libdwarf/dwarf_init.c2
-rw-r--r--lib/libdwarf/dwarf_loc.c2
-rw-r--r--lib/libfetch/fetch.c2
-rw-r--r--lib/libiconv_modules/BIG5/citrus_big5.c4
-rw-r--r--lib/libiconv_modules/DECHanyu/citrus_dechanyu.c4
-rw-r--r--lib/libiconv_modules/EUC/citrus_euc.c4
-rw-r--r--lib/libiconv_modules/EUCTW/citrus_euctw.c4
-rw-r--r--lib/libiconv_modules/GBK2K/citrus_gbk2k.c4
-rw-r--r--lib/libiconv_modules/HZ/citrus_hz.c4
-rw-r--r--lib/libiconv_modules/ISO2022/citrus_iso2022.c6
-rw-r--r--lib/libiconv_modules/JOHAB/citrus_johab.c4
-rw-r--r--lib/libiconv_modules/MSKanji/citrus_mskanji.c4
-rw-r--r--lib/libiconv_modules/UES/citrus_ues.c4
-rw-r--r--lib/libiconv_modules/UTF1632/citrus_utf1632.c4
-rw-r--r--lib/libiconv_modules/UTF7/citrus_utf7.c8
-rw-r--r--lib/libiconv_modules/UTF8/citrus_utf8.c4
-rw-r--r--lib/libiconv_modules/VIQR/citrus_viqr.c4
-rw-r--r--lib/libiconv_modules/ZW/citrus_zw.c4
-rw-r--r--lib/libiconv_modules/iconv_none/citrus_iconv_none.c2
-rw-r--r--lib/libiconv_modules/iconv_std/citrus_iconv_std.c6
-rw-r--r--lib/libprocstat/Makefile1
-rw-r--r--lib/libprocstat/common_kvm.h1
-rw-r--r--lib/libprocstat/libprocstat.c1
-rw-r--r--lib/librt/sigev_thread.c7
-rw-r--r--lib/libstand/nfs.c5
-rw-r--r--lib/libutil/login_times.c2
-rw-r--r--rescue/rescue/Makefile2
-rw-r--r--sbin/devd/devd.86
-rw-r--r--sbin/devd/devd.cc146
-rw-r--r--sbin/dhclient/bpf.c218
-rw-r--r--sbin/dhclient/clparse.c4
-rw-r--r--sbin/dhclient/dhclient.c145
-rw-r--r--sbin/dhclient/dhcpd.h20
-rw-r--r--sbin/dhclient/packet.c12
-rw-r--r--sbin/dhclient/privsep.c5
-rw-r--r--sbin/dhclient/privsep.h5
-rw-r--r--sbin/geom/class/part/gpart.890
-rw-r--r--sbin/hastctl/hastctl.c4
-rw-r--r--sbin/hastd/control.c1
-rw-r--r--sbin/hastd/hastd.836
-rw-r--r--sbin/hastd/refcnt.h10
-rw-r--r--sbin/ifconfig/af_nd6.c2
-rw-r--r--sbin/mdconfig/mdconfig.82
-rw-r--r--sbin/mdconfig/mdconfig.c3
-rw-r--r--sbin/mount/mount.82
-rw-r--r--sbin/mount/mount.c2
-rw-r--r--sbin/nvmecontrol/firmware.c4
-rw-r--r--sbin/reboot/boot_i386.83
-rw-r--r--sbin/swapon/swapon.c330
-rw-r--r--share/examples/Makefile1
-rw-r--r--share/examples/etc/README.examples1
-rw-r--r--share/examples/etc/make.conf4
-rw-r--r--share/man/man4/Makefile1
-rw-r--r--share/man/man4/cc_cdg.4155
-rw-r--r--share/man/man4/oce.42
-rw-r--r--share/man/man4/virtio.46
-rw-r--r--share/man/man4/virtio_balloon.42
-rw-r--r--share/man/man4/virtio_blk.432
-rw-r--r--share/man/man4/virtio_scsi.42
-rw-r--r--share/man/man4/vtnet.42
-rw-r--r--share/man/man5/fstab.52
-rw-r--r--share/man/man5/src.conf.513
-rw-r--r--share/man/man9/locking.9376
-rw-r--r--share/misc/committers-src.dot2
-rw-r--r--share/mk/bsd.libnames.mk1
-rw-r--r--sys/amd64/amd64/pmap.c12
-rw-r--r--sys/amd64/include/counter.h38
-rw-r--r--sys/amd64/vmm/intel/ept.c12
-rw-r--r--sys/amd64/vmm/vmm_instruction_emul.c16
-rw-r--r--sys/amd64/vmm/x86.c48
-rw-r--r--sys/arm/arm/bus_space_generic.c2
-rw-r--r--sys/arm/arm/busdma_machdep-v6.c18
-rw-r--r--sys/arm/arm/cpufunc.c3
-rw-r--r--sys/arm/arm/db_trace.c19
-rw-r--r--sys/arm/arm/exception.S2
-rw-r--r--sys/arm/arm/generic_timer.c394
-rw-r--r--sys/arm/arm/gic.c44
-rw-r--r--sys/arm/arm/identcpu.c2
-rw-r--r--sys/arm/arm/pmap-v6.c10
-rw-r--r--sys/arm/broadcom/bcm2835/bcm2835_mbox.c8
-rw-r--r--sys/arm/broadcom/bcm2835/bcm2835_sdhci.c2
-rw-r--r--sys/arm/conf/ARNDALE135
-rw-r--r--sys/arm/conf/RPI-B7
-rw-r--r--sys/arm/include/armreg.h1
-rw-r--r--sys/arm/include/asmacros.h11
-rw-r--r--sys/arm/include/counter.h40
-rw-r--r--sys/arm/include/intr.h2
-rw-r--r--sys/arm/samsung/exynos/arch_timer.c136
-rw-r--r--sys/arm/samsung/exynos/bus_space.c153
-rw-r--r--sys/arm/samsung/exynos/common.c73
-rw-r--r--sys/arm/samsung/exynos/ehci_exynos5.c367
-rw-r--r--sys/arm/samsung/exynos/exynos5_machdep.c109
-rw-r--r--sys/arm/samsung/exynos/exynos5_mp.c92
-rw-r--r--sys/arm/samsung/exynos/files.exynos524
-rw-r--r--sys/arm/samsung/exynos/std.exynos523
-rw-r--r--sys/arm/samsung/exynos/uart.c377
-rw-r--r--sys/arm/samsung/exynos/uart.h126
-rw-r--r--sys/arm/ti/am335x/am335x_pwm.c149
-rw-r--r--sys/arm/versatile/versatile_pci.c44
-rw-r--r--sys/arm/versatile/versatile_sic.c2
-rw-r--r--sys/boot/fdt/dts/am335x.dtsi30
-rw-r--r--sys/boot/fdt/dts/bcm2835.dtsi (renamed from sys/boot/fdt/dts/bcm2835-rpi-b.dts)239
-rw-r--r--sys/boot/fdt/dts/exynos5250-arndale.dts45
-rw-r--r--sys/boot/fdt/dts/exynos5250.dtsi157
-rw-r--r--sys/boot/fdt/dts/rpi.dts381
-rw-r--r--sys/boot/i386/gptboot/Makefile1
-rw-r--r--sys/boot/i386/gptboot/gptboot.8238
-rw-r--r--sys/cam/cam_ccb.h13
-rw-r--r--sys/cam/ctl/ctl_backend_ramdisk.c11
-rw-r--r--sys/cam/scsi/scsi_da.c2
-rw-r--r--sys/cam/scsi/scsi_xpt.c26
-rw-r--r--sys/cddl/compat/opensolaris/kern/opensolaris_cmn_err.c6
-rw-r--r--sys/cddl/contrib/opensolaris/uts/common/dtrace/fasttrap.c1
-rw-r--r--sys/cddl/contrib/opensolaris/uts/common/fs/zfs/dsl_dir.c2
-rw-r--r--sys/cddl/contrib/opensolaris/uts/common/fs/zfs/sys/zvol.h2
-rw-r--r--sys/cddl/contrib/opensolaris/uts/common/fs/zfs/zfs_acl.c4
-rw-r--r--sys/cddl/contrib/opensolaris/uts/common/fs/zfs/zfs_dir.c2
-rw-r--r--sys/cddl/contrib/opensolaris/uts/common/fs/zfs/zfs_ioctl.c7
-rw-r--r--sys/cddl/contrib/opensolaris/uts/common/fs/zfs/zfs_vfsops.c4
-rw-r--r--sys/cddl/contrib/opensolaris/uts/common/fs/zfs/zfs_vnops.c5
-rw-r--r--sys/cddl/dev/dtmalloc/dtmalloc.c10
-rw-r--r--sys/conf/NOTES7
-rw-r--r--sys/conf/files48
-rw-r--r--sys/conf/files.amd642
-rw-r--r--sys/conf/files.arm2
-rw-r--r--sys/conf/files.i3862
-rw-r--r--sys/conf/files.ia642
-rw-r--r--sys/conf/files.mips2
-rw-r--r--sys/conf/files.pc982
-rw-r--r--sys/conf/files.powerpc2
-rw-r--r--sys/conf/files.sparc642
-rw-r--r--sys/conf/newvers.sh14
-rw-r--r--sys/conf/options8
-rw-r--r--sys/contrib/dev/ath/ath_hal/ar9300/ar9300_attach.c5
-rw-r--r--sys/dev/acpica/acpi_pcib.c9
-rw-r--r--sys/dev/acpica/acpi_pcib_acpi.c8
-rw-r--r--sys/dev/ath/if_ath_rx_edma.c7
-rw-r--r--sys/dev/bce/if_bce.c33
-rw-r--r--sys/dev/bce/if_bcereg.h1
-rw-r--r--sys/dev/bge/if_bge.c30
-rw-r--r--sys/dev/bge/if_bgereg.h1
-rw-r--r--sys/dev/cpuctl/cpuctl.c8
-rw-r--r--sys/dev/cxgbe/adapter.h5
-rw-r--r--sys/dev/cxgbe/common/common.h18
-rw-r--r--sys/dev/cxgbe/common/t4_hw.c145
-rw-r--r--sys/dev/cxgbe/common/t4_hw.h18
-rw-r--r--sys/dev/cxgbe/common/t4_msg.h13
-rw-r--r--sys/dev/cxgbe/common/t4_regs_values.h53
-rw-r--r--sys/dev/cxgbe/firmware/t4fw-1.8.11.0.bin.uu8417
-rw-r--r--sys/dev/cxgbe/firmware/t4fw-1.8.4.0.bin.uu8237
-rw-r--r--sys/dev/cxgbe/firmware/t4fw_cfg.txt8
-rw-r--r--sys/dev/cxgbe/firmware/t4fw_cfg_uwire.txt12
-rw-r--r--sys/dev/cxgbe/firmware/t4fw_interface.h52
-rw-r--r--sys/dev/cxgbe/firmware/t5fw-1.8.22.0.bin.uu8112
-rw-r--r--sys/dev/cxgbe/firmware/t5fw_cfg.txt151
-rw-r--r--sys/dev/cxgbe/firmware/t5fw_cfg_fpga.txt477
-rw-r--r--sys/dev/cxgbe/firmware/t5fw_cfg_uwire.txt568
-rw-r--r--sys/dev/cxgbe/offload.h1
-rw-r--r--sys/dev/cxgbe/t4_main.c174
-rw-r--r--sys/dev/cxgbe/t4_sge.c48
-rw-r--r--sys/dev/cxgbe/tom/t4_connect.c49
-rw-r--r--sys/dev/cxgbe/tom/t4_listen.c24
-rw-r--r--sys/dev/cxgbe/tom/t4_tom.c85
-rw-r--r--sys/dev/cxgbe/tom/t4_tom.h2
-rw-r--r--sys/dev/iwn/if_iwn.c159
-rw-r--r--sys/dev/iwn/if_iwnvar.h1
-rw-r--r--sys/dev/mfi/mfi.c13
-rw-r--r--sys/dev/pci/pci.c31
-rw-r--r--sys/dev/pci/pci_private.h1
-rw-r--r--sys/dev/qlxgbe/ql_os.c8
-rw-r--r--sys/dev/ral/rt2560.c2
-rw-r--r--sys/dev/ral/rt2661.c2
-rw-r--r--sys/dev/ral/rt2860.c7
-rw-r--r--sys/dev/re/if_re.c10
-rw-r--r--sys/dev/uart/uart.h1
-rw-r--r--sys/dev/uart/uart_bus_fdt.c2
-rw-r--r--sys/dev/uart/uart_cpu_fdt.c2
-rw-r--r--sys/dev/uart/uart_subr.c1
-rw-r--r--sys/dev/usb/quirk/usb_quirk.c1
-rw-r--r--sys/dev/usb/usbdevs40
-rw-r--r--sys/dev/usb/wlan/if_rum.c6
-rw-r--r--sys/dev/usb/wlan/if_uath.c6
-rw-r--r--sys/dev/usb/wlan/if_upgt.c6
-rw-r--r--sys/dev/usb/wlan/if_ural.c6
-rw-r--r--sys/dev/usb/wlan/if_urtw.c6
-rw-r--r--sys/dev/usb/wlan/if_urtwn.c18
-rw-r--r--sys/dev/usb/wlan/if_zyd.c6
-rw-r--r--sys/dev/virtio/balloon/virtio_balloon.c52
-rw-r--r--sys/dev/virtio/block/virtio_blk.c296
-rw-r--r--sys/dev/virtio/block/virtio_blk.h19
-rw-r--r--sys/dev/virtio/network/if_vtnet.c110
-rw-r--r--sys/dev/virtio/network/if_vtnetvar.h7
-rw-r--r--sys/dev/virtio/pci/virtio_pci.c488
-rw-r--r--sys/dev/virtio/scsi/virtio_scsi.c120
-rw-r--r--sys/dev/virtio/scsi/virtio_scsivar.h7
-rw-r--r--sys/dev/virtio/virtio.c59
-rw-r--r--sys/dev/virtio/virtio.h7
-rw-r--r--sys/dev/virtio/virtio_bus_if.m5
-rw-r--r--sys/dev/virtio/virtio_if.m5
-rw-r--r--sys/dev/virtio/virtqueue.c16
-rw-r--r--sys/dev/virtio/virtqueue.h7
-rw-r--r--sys/fs/ext2fs/ext2_lookup.c19
-rw-r--r--sys/fs/ext2fs/ext2_vfsops.c2
-rw-r--r--sys/fs/nfsclient/nfs_clport.c15
-rw-r--r--sys/fs/nullfs/null_vnops.c7
-rw-r--r--sys/fs/smbfs/smbfs_node.c2
-rw-r--r--sys/fs/smbfs/smbfs_smb.c169
-rw-r--r--sys/fs/smbfs/smbfs_vfsops.c5
-rw-r--r--sys/fs/smbfs/smbfs_vnops.c9
-rw-r--r--sys/geom/geom_disk.c14
-rw-r--r--sys/geom/geom_disk.h3
-rw-r--r--sys/geom/geom_io.c15
-rw-r--r--sys/i386/include/counter.h87
-rw-r--r--sys/ia64/include/counter.h39
-rw-r--r--sys/kern/Make.tags.inc1
-rw-r--r--sys/kern/kern_acct.c31
-rw-r--r--sys/kern/kern_clock.c10
-rw-r--r--sys/kern/kern_descrip.c76
-rw-r--r--sys/kern/kern_intr.c20
-rw-r--r--sys/kern/kern_synch.c2
-rw-r--r--sys/kern/subr_bus.c42
-rw-r--r--sys/kern/subr_counter.c20
-rw-r--r--sys/kern/subr_vmem.c1372
-rw-r--r--sys/kern/sys_generic.c56
-rw-r--r--sys/kern/uipc_usrreq.c4
-rw-r--r--sys/kern/vfs_bio.c23
-rw-r--r--sys/mips/conf/AP93.hints3
-rw-r--r--sys/mips/conf/ENH20044
-rw-r--r--sys/mips/conf/ENH200.hints124
-rw-r--r--sys/mips/include/counter.h40
-rw-r--r--sys/modules/Makefile12
-rw-r--r--sys/modules/cc/Makefile3
-rw-r--r--sys/modules/cc/cc_cdg/Makefile9
-rw-r--r--sys/modules/cxgbe/Makefile2
-rw-r--r--sys/modules/cxgbe/t4_firmware/Makefile2
-rw-r--r--sys/modules/cxgbe/t5_firmware/Makefile27
-rw-r--r--sys/net/if_bridge.c102
-rw-r--r--sys/net/if_lagg.c30
-rw-r--r--sys/net80211/ieee80211_amrr.c109
-rw-r--r--sys/net80211/ieee80211_ioctl.c4
-rw-r--r--sys/net80211/ieee80211_phy.c193
-rw-r--r--sys/net80211/ieee80211_phy.h51
-rw-r--r--sys/netinet/cc/cc_cdg.c695
-rw-r--r--sys/netinet/if_ether.c2
-rw-r--r--sys/netinet/in_mcast.c2
-rw-r--r--sys/netinet/in_pcb.c20
-rw-r--r--sys/netinet/in_pcb.h2
-rw-r--r--sys/netinet/ip_output.c11
-rw-r--r--sys/netinet/sctp_indata.c2
-rw-r--r--sys/netinet/sctp_indata.h4
-rw-r--r--sys/netinet/sctp_input.c64
-rw-r--r--sys/netinet/sctp_pcb.c142
-rw-r--r--sys/netinet6/in6.c26
-rw-r--r--sys/netinet6/in6_ifattach.c15
-rw-r--r--sys/netinet6/in6_pcb.c7
-rw-r--r--sys/netinet6/in6_var.h1
-rw-r--r--sys/netinet6/ip6_output.c11
-rw-r--r--sys/netinet6/nd6.c24
-rw-r--r--sys/netsmb/smb_conn.c1
-rw-r--r--sys/nfsclient/nfs_subs.c15
-rw-r--r--sys/nfsclient/nfs_vnops.c4
-rw-r--r--sys/powerpc/include/counter.h78
-rw-r--r--sys/powerpc/wii/wii_ipcreg.h65
-rw-r--r--sys/sparc64/include/counter.h39
-rw-r--r--sys/sys/bus.h3
-rw-r--r--sys/sys/malloc.h2
-rw-r--r--sys/sys/socketvar.h2
-rw-r--r--sys/sys/stdatomic.h98
-rw-r--r--sys/sys/vmem.h135
-rw-r--r--sys/ufs/ffs/ffs_alloc.c2
-rw-r--r--sys/ufs/ffs/ffs_balloc.c26
-rw-r--r--sys/ufs/ufs/dinode.h4
-rw-r--r--sys/ufs/ufs/inode.h2
-rw-r--r--sys/ufs/ufs/ufs_extattr.c18
-rw-r--r--sys/ufs/ufs/ufs_vnops.c200
-rw-r--r--sys/vm/uma_core.c2
-rw-r--r--sys/vm/vm.h4
-rw-r--r--sys/vm/vm_init.c38
-rw-r--r--sys/vm/vm_kern.c2
-rw-r--r--sys/vm/vm_kern.h4
-rw-r--r--sys/vm/vm_object.c6
-rw-r--r--sys/vm/vm_page.c1
-rw-r--r--sys/vm/vm_pager.c12
-rw-r--r--sys/vm/vm_pager.h3
-rw-r--r--sys/vm/vm_phys.c1
-rw-r--r--tools/build/options/WITHOUT_SVNLITE4
-rw-r--r--tools/build/options/WITH_SVN5
-rw-r--r--tools/tools/cxgbetool/cxgbetool.c1
-rw-r--r--usr.bin/Makefile2
-rw-r--r--usr.bin/Makefile.amd641
-rw-r--r--usr.bin/Makefile.i3861
-rw-r--r--usr.bin/Makefile.ia641
-rw-r--r--usr.bin/Makefile.powerpc1
-rw-r--r--usr.bin/Makefile.sparc641
-rw-r--r--usr.bin/calendar/calendars/calendar.freebsd1
-rw-r--r--usr.bin/iconv/iconv.c2
-rw-r--r--usr.bin/kdump/kdump.c2
-rw-r--r--usr.bin/killall/killall.14
-rw-r--r--usr.bin/killall/killall.c11
-rw-r--r--usr.bin/mail/popen.c2
-rw-r--r--usr.bin/make/job.c4
-rw-r--r--usr.bin/mkcsmapper/lex.l2
-rw-r--r--usr.bin/mkesdb/lex.l2
-rw-r--r--usr.bin/patch/pch.c10
-rw-r--r--usr.bin/rwho/rwho.c131
-rw-r--r--usr.bin/svn/lib/libapr_util/Makefile6
-rw-r--r--usr.bin/svn/lib/libapr_util/apr_ldap.h52
-rw-r--r--usr.bin/svn/lib/libapr_util/apu.h37
-rw-r--r--usr.bin/svn/lib/libapr_util/apu_config.h67
-rw-r--r--usr.bin/truss/syscalls.c2
-rw-r--r--usr.sbin/Makefile.amd641
-rw-r--r--usr.sbin/Makefile.i3861
-rw-r--r--usr.sbin/Makefile.ia641
-rw-r--r--usr.sbin/Makefile.powerpc1
-rw-r--r--usr.sbin/Makefile.sparc641
-rw-r--r--usr.sbin/bhyve/atpic.c17
-rw-r--r--usr.sbin/bhyve/bhyverun.c8
-rw-r--r--usr.sbin/bhyve/pci_emul.c74
-rw-r--r--usr.sbin/bhyve/pci_emul.h2
-rw-r--r--usr.sbin/bhyve/pci_virtio_net.c83
-rw-r--r--usr.sbin/bsdconfig/include/messages.subr12
-rw-r--r--usr.sbin/bsdconfig/networking/share/Makefile2
-rw-r--r--usr.sbin/bsdconfig/networking/share/services.subr54
-rw-r--r--usr.sbin/bsdconfig/share/media/cdrom.subr6
-rw-r--r--usr.sbin/bsdconfig/share/media/common.subr19
-rw-r--r--usr.sbin/bsdconfig/share/media/directory.subr6
-rw-r--r--usr.sbin/bsdconfig/share/media/dos.subr4
-rw-r--r--usr.sbin/bsdconfig/share/media/floppy.subr2
-rw-r--r--usr.sbin/bsdconfig/share/media/ftp.subr15
-rw-r--r--usr.sbin/bsdconfig/share/media/nfs.subr6
-rw-r--r--usr.sbin/bsdconfig/share/media/ufs.subr4
-rw-r--r--usr.sbin/bsdconfig/share/media/usb.subr4
-rwxr-xr-xusr.sbin/bsdconfig/share/packages/index.subr43
-rwxr-xr-xusr.sbin/bsdconfig/share/packages/packages.subr272
-rw-r--r--usr.sbin/bsdconfig/share/script.subr6
-rw-r--r--usr.sbin/bsdconfig/share/variable.subr23
-rw-r--r--usr.sbin/bsnmpd/modules/snmp_hast/BEGEMOT-HAST-MIB.txt14
-rw-r--r--usr.sbin/bsnmpd/modules/snmp_hast/hast_snmp.c5
-rw-r--r--usr.sbin/bsnmpd/modules/snmp_hast/hast_tree.def1
-rw-r--r--usr.sbin/newsyslog/newsyslog.c20
-rw-r--r--usr.sbin/nfsd/nfsv4.416
-rw-r--r--usr.sbin/pkg_install/add/extract.c3
-rw-r--r--usr.sbin/pkg_install/create/pl.c3
-rw-r--r--usr.sbin/pkg_install/lib/exec.c5
-rw-r--r--usr.sbin/powerd/powerd.850
-rw-r--r--usr.sbin/powerd/powerd.c16
-rw-r--r--usr.sbin/pw/pw_user.c8
-rw-r--r--usr.sbin/rwhod/rwhod.c628
-rw-r--r--usr.sbin/wpa/Makefile.crypto20
-rw-r--r--usr.sbin/wpa/Makefile.inc4
-rw-r--r--usr.sbin/wpa/hostapd/Makefile82
-rw-r--r--usr.sbin/wpa/hostapd/driver_freebsd.c787
-rw-r--r--usr.sbin/wpa/hostapd_cli/Makefile2
-rw-r--r--usr.sbin/wpa/wpa_cli/Makefile2
-rw-r--r--usr.sbin/wpa/wpa_passphrase/Makefile4
-rw-r--r--usr.sbin/wpa/wpa_priv/Makefile17
-rw-r--r--usr.sbin/wpa/wpa_supplicant/Makefile65
-rw-r--r--usr.sbin/wpa/wpa_supplicant/driver_freebsd.c934
1001 files changed, 123219 insertions, 39642 deletions
diff --git a/Makefile.inc1 b/Makefile.inc1
index 2aca77f..64a4b91 100644
--- a/Makefile.inc1
+++ b/Makefile.inc1
@@ -1131,11 +1131,11 @@ legacy:
.endif
.for _tool in tools/build
${_+_}@${ECHODIR} "===> ${_tool} (obj,includes,depend,all,install)"; \
- cd ${.CURDIR}/${_tool}; \
- ${MAKE} DIRPRFX=${_tool}/ obj; \
- ${MAKE} DIRPRFX=${_tool}/ DESTDIR=${MAKEOBJDIRPREFIX}/legacy includes; \
- ${MAKE} DIRPRFX=${_tool}/ depend; \
- ${MAKE} DIRPRFX=${_tool}/ all; \
+ cd ${.CURDIR}/${_tool} && \
+ ${MAKE} DIRPRFX=${_tool}/ obj && \
+ ${MAKE} DIRPRFX=${_tool}/ DESTDIR=${MAKEOBJDIRPREFIX}/legacy includes && \
+ ${MAKE} DIRPRFX=${_tool}/ depend && \
+ ${MAKE} DIRPRFX=${_tool}/ all && \
${MAKE} DIRPRFX=${_tool}/ DESTDIR=${MAKEOBJDIRPREFIX}/legacy install
.endfor
@@ -1264,10 +1264,10 @@ bootstrap-tools:
${_crunch} \
${_nmtree}
${_+_}@${ECHODIR} "===> ${_tool} (obj,depend,all,install)"; \
- cd ${.CURDIR}/${_tool}; \
- ${MAKE} DIRPRFX=${_tool}/ obj; \
- ${MAKE} DIRPRFX=${_tool}/ depend; \
- ${MAKE} DIRPRFX=${_tool}/ all; \
+ cd ${.CURDIR}/${_tool} && \
+ ${MAKE} DIRPRFX=${_tool}/ obj && \
+ ${MAKE} DIRPRFX=${_tool}/ depend && \
+ ${MAKE} DIRPRFX=${_tool}/ all && \
${MAKE} DIRPRFX=${_tool}/ DESTDIR=${MAKEOBJDIRPREFIX}/legacy install
.endfor
@@ -1305,16 +1305,16 @@ build-tools:
usr.bin/mkesdb_static \
usr.bin/mkcsmapper_static
${_+_}@${ECHODIR} "===> ${_tool} (obj,build-tools)"; \
- cd ${.CURDIR}/${_tool}; \
- ${MAKE} DIRPRFX=${_tool}/ obj; \
+ cd ${.CURDIR}/${_tool} && \
+ ${MAKE} DIRPRFX=${_tool}/ obj && \
${MAKE} DIRPRFX=${_tool}/ build-tools
.endfor
.for _tool in \
${_gcc_tools}
${_+_}@${ECHODIR} "===> ${_tool} (obj,depend,all)"; \
- cd ${.CURDIR}/${_tool}; \
- ${MAKE} DIRPRFX=${_tool}/ obj; \
- ${MAKE} DIRPRFX=${_tool}/ depend; \
+ cd ${.CURDIR}/${_tool} && \
+ ${MAKE} DIRPRFX=${_tool}/ obj && \
+ ${MAKE} DIRPRFX=${_tool}/ depend && \
${MAKE} DIRPRFX=${_tool}/ all
.endfor
@@ -1363,10 +1363,10 @@ cross-tools:
${_crunchide} \
${_kgzip}
${_+_}@${ECHODIR} "===> ${_tool} (obj,depend,all,install)"; \
- cd ${.CURDIR}/${_tool}; \
- ${MAKE} DIRPRFX=${_tool}/ obj; \
- ${MAKE} DIRPRFX=${_tool}/ depend; \
- ${MAKE} DIRPRFX=${_tool}/ all; \
+ cd ${.CURDIR}/${_tool} && \
+ ${MAKE} DIRPRFX=${_tool}/ obj && \
+ ${MAKE} DIRPRFX=${_tool}/ depend && \
+ ${MAKE} DIRPRFX=${_tool}/ all && \
${MAKE} DIRPRFX=${_tool}/ DESTDIR=${MAKEOBJDIRPREFIX} install
.endfor
@@ -1545,10 +1545,10 @@ lib/libradius__L: lib/libmd__L
${_lib}__PL: .PHONY
.if exists(${.CURDIR}/${_lib})
${_+_}@${ECHODIR} "===> ${_lib} (obj,depend,all,install)"; \
- cd ${.CURDIR}/${_lib}; \
- ${MAKE} DIRPRFX=${_lib}/ obj; \
- ${MAKE} DIRPRFX=${_lib}/ depend; \
- ${MAKE} -DNO_PROFILE -DNO_PIC DIRPRFX=${_lib}/ all; \
+ cd ${.CURDIR}/${_lib} && \
+ ${MAKE} DIRPRFX=${_lib}/ obj && \
+ ${MAKE} DIRPRFX=${_lib}/ depend && \
+ ${MAKE} -DNO_PROFILE -DNO_PIC DIRPRFX=${_lib}/ all && \
${MAKE} -DNO_PROFILE -DNO_PIC DIRPRFX=${_lib}/ install
.endif
.endfor
@@ -1557,10 +1557,10 @@ ${_lib}__PL: .PHONY
${_lib}__L: .PHONY
.if exists(${.CURDIR}/${_lib})
${_+_}@${ECHODIR} "===> ${_lib} (obj,depend,all,install)"; \
- cd ${.CURDIR}/${_lib}; \
- ${MAKE} DIRPRFX=${_lib}/ obj; \
- ${MAKE} DIRPRFX=${_lib}/ depend; \
- ${MAKE} DIRPRFX=${_lib}/ all; \
+ cd ${.CURDIR}/${_lib} && \
+ ${MAKE} DIRPRFX=${_lib}/ obj && \
+ ${MAKE} DIRPRFX=${_lib}/ depend && \
+ ${MAKE} DIRPRFX=${_lib}/ all && \
${MAKE} DIRPRFX=${_lib}/ install
.endif
.endfor
@@ -1570,10 +1570,10 @@ ${_lib}__L: .PHONY
# modules.
lib/libpam__L: .PHONY
${_+_}@${ECHODIR} "===> lib/libpam (obj,depend,all,install)"; \
- cd ${.CURDIR}/lib/libpam; \
- ${MAKE} DIRPRFX=lib/libpam/ obj; \
- ${MAKE} DIRPRFX=lib/libpam/ depend; \
- ${MAKE} DIRPRFX=lib/libpam/ -D_NO_LIBPAM_SO_YET all; \
+ cd ${.CURDIR}/lib/libpam && \
+ ${MAKE} DIRPRFX=lib/libpam/ obj && \
+ ${MAKE} DIRPRFX=lib/libpam/ depend && \
+ ${MAKE} DIRPRFX=lib/libpam/ -D_NO_LIBPAM_SO_YET all && \
${MAKE} DIRPRFX=lib/libpam/ -D_NO_LIBPAM_SO_YET install
_prereq_libs: ${_prereq_libs:S/$/__PL/}
@@ -1584,7 +1584,7 @@ _generic_libs: ${_generic_libs:S/$/__L/}
.for __target in all clean cleandepend cleandir depend includes obj
.for entry in ${SUBDIR}
${entry}.${__target}__D: .PHONY
- ${_+_}@if test -d ${.CURDIR}/${entry}.${MACHINE_ARCH}; then \
+ ${_+_}@set -e; if test -d ${.CURDIR}/${entry}.${MACHINE_ARCH}; then \
${ECHODIR} "===> ${DIRPRFX}${entry}.${MACHINE_ARCH} (${__target})"; \
edir=${entry}.${MACHINE_ARCH}; \
cd ${.CURDIR}/$${edir}; \
@@ -1822,10 +1822,10 @@ _xb-bootstrap-tools:
.for _tool in \
${_clang_tblgen}
${_+_}@${ECHODIR} "===> ${_tool} (obj,depend,all,install)"; \
- cd ${.CURDIR}/${_tool}; \
- ${CDMAKE} DIRPRFX=${_tool}/ obj; \
- ${CDMAKE} DIRPRFX=${_tool}/ depend; \
- ${CDMAKE} DIRPRFX=${_tool}/ all; \
+ cd ${.CURDIR}/${_tool} && \
+ ${CDMAKE} DIRPRFX=${_tool}/ obj && \
+ ${CDMAKE} DIRPRFX=${_tool}/ depend && \
+ ${CDMAKE} DIRPRFX=${_tool}/ all && \
${CDMAKE} DIRPRFX=${_tool}/ DESTDIR=${CDTMP} install
.endfor
@@ -1841,9 +1841,9 @@ _xb-cross-tools:
${_clang_libs} \
${_clang}
${_+_}@${ECHODIR} "===> xdev ${_tool} (obj,depend,all)"; \
- cd ${.CURDIR}/${_tool}; \
- ${CDMAKE} DIRPRFX=${_tool}/ obj; \
- ${CDMAKE} DIRPRFX=${_tool}/ depend; \
+ cd ${.CURDIR}/${_tool} && \
+ ${CDMAKE} DIRPRFX=${_tool}/ obj && \
+ ${CDMAKE} DIRPRFX=${_tool}/ depend && \
${CDMAKE} DIRPRFX=${_tool}/ all
.endfor
diff --git a/ObsoleteFiles.inc b/ObsoleteFiles.inc
index ee082cc..c60a478 100644
--- a/ObsoleteFiles.inc
+++ b/ObsoleteFiles.inc
@@ -41,7 +41,7 @@
# 20130623: dialog update from 1.1 to 1.2
OLD_LIBS+=usr/lib/libdialog.so.7
OLD_LIBS+=usr/lib32/libdialog.so.7
-# 20130616: vfs_mounted.9 removed
+# 20130616: vfs_mount.9 removed
OLD_FILES+=usr/share/man/man9/vfs_mount.9.gz
# 20130614: remove CVS from base
OLD_FILES+=usr/bin/cvs
diff --git a/UPDATING b/UPDATING
index 130a3b3..9ecc065 100644
--- a/UPDATING
+++ b/UPDATING
@@ -31,6 +31,14 @@ NOTE TO PEOPLE WHO THINK THAT FreeBSD 10.x IS SLOW:
disable the most expensive debugging functionality run
"ln -s 'abort:false,junk:false' /etc/malloc.conf".)
+20130629:
+ Fix targets that run multiple make's to use && rather than ;
+ so that subsequent steps depend on success of previous.
+
+ NOTE: if building 'universe' with -j* on stable/8 or stable/9
+ it would be better to start the build using bmake, to avoid
+ overloading the machine.
+
20130618:
Fix a bug that allowed a tracing process (e.g. gdb) to write
to a memory-mapped file in the traced process's address space
diff --git a/bin/ed/re.c b/bin/ed/re.c
index 08a330d..03a3436 100644
--- a/bin/ed/re.c
+++ b/bin/ed/re.c
@@ -89,7 +89,7 @@ extract_pattern(int delimiter)
default:
break;
case '[':
- if ((nd = parse_char_class(++nd)) == NULL) {
+ if ((nd = parse_char_class(nd + 1)) == NULL) {
errmsg = "unbalanced brackets ([])";
return NULL;
}
diff --git a/bin/sh/eval.c b/bin/sh/eval.c
index d3708b3..1a6d5ed 100644
--- a/bin/sh/eval.c
+++ b/bin/sh/eval.c
@@ -589,7 +589,8 @@ evalpipe(union node *n)
pip[1] = -1;
if (lp->next) {
if (pipe(pip) < 0) {
- close(prevfd);
+ if (prevfd >= 0)
+ close(prevfd);
error("Pipe call failed: %s", strerror(errno));
}
}
diff --git a/cddl/contrib/opensolaris/cmd/zfs/zfs_main.c b/cddl/contrib/opensolaris/cmd/zfs/zfs_main.c
index e24a4f8..b35d272 100644
--- a/cddl/contrib/opensolaris/cmd/zfs/zfs_main.c
+++ b/cddl/contrib/opensolaris/cmd/zfs/zfs_main.c
@@ -5601,8 +5601,8 @@ share_mount_one(zfs_handle_t *zhp, int op, int flags, char *protocol,
(void) fprintf(stderr, gettext("cannot share '%s': "
"legacy share\n"), zfs_get_name(zhp));
- (void) fprintf(stderr, gettext("use share(1M) to "
- "share this filesystem, or set "
+ (void) fprintf(stderr, gettext("to "
+ "share this filesystem set "
"sharenfs property on\n"));
return (1);
}
@@ -5618,7 +5618,7 @@ share_mount_one(zfs_handle_t *zhp, int op, int flags, char *protocol,
(void) fprintf(stderr, gettext("cannot %s '%s': "
"legacy mountpoint\n"), cmdname, zfs_get_name(zhp));
- (void) fprintf(stderr, gettext("use %s(1M) to "
+ (void) fprintf(stderr, gettext("use %s(8) to "
"%s this filesystem\n"), cmdname, cmdname);
return (1);
}
@@ -6056,8 +6056,10 @@ unshare_unmount_path(int op, char *path, int flags, boolean_t is_manual)
strcmp(smbshare_prop, "off") == 0) {
(void) fprintf(stderr, gettext("cannot unshare "
"'%s': legacy share\n"), path);
+#ifdef illumos
(void) fprintf(stderr, gettext("use "
"unshare(1M) to unshare this filesystem\n"));
+#endif
} else if (!zfs_is_shared(zhp)) {
(void) fprintf(stderr, gettext("cannot unshare '%s': "
"not currently shared\n"), path);
@@ -6076,7 +6078,7 @@ unshare_unmount_path(int op, char *path, int flags, boolean_t is_manual)
(void) fprintf(stderr, gettext("cannot unmount "
"'%s': legacy mountpoint\n"),
zfs_get_name(zhp));
- (void) fprintf(stderr, gettext("use umount(1M) "
+ (void) fprintf(stderr, gettext("use umount(8) "
"to unmount this filesystem\n"));
} else {
ret = zfs_unmountall(zhp, flags);
@@ -6298,9 +6300,11 @@ unshare_unmount(int op, int argc, char **argv)
(void) fprintf(stderr, gettext("cannot "
"unshare '%s': legacy share\n"),
zfs_get_name(zhp));
+#ifdef illumos
(void) fprintf(stderr, gettext("use "
"unshare(1M) to unshare this "
"filesystem\n"));
+#endif
ret = 1;
} else if (!zfs_is_shared(zhp)) {
(void) fprintf(stderr, gettext("cannot "
@@ -6318,7 +6322,7 @@ unshare_unmount(int op, int argc, char **argv)
"unmount '%s': legacy "
"mountpoint\n"), zfs_get_name(zhp));
(void) fprintf(stderr, gettext("use "
- "umount(1M) to unmount this "
+ "umount(8) to unmount this "
"filesystem\n"));
ret = 1;
} else if (!zfs_is_mounted(zhp, NULL)) {
@@ -6504,12 +6508,12 @@ manual_mount(int argc, char **argv)
}
} else {
(void) fprintf(stderr, gettext("filesystem '%s' cannot be "
- "mounted using 'mount -F zfs'\n"), dataset);
+ "mounted using 'mount -t zfs'\n"), dataset);
(void) fprintf(stderr, gettext("Use 'zfs set mountpoint=%s' "
"instead.\n"), path);
- (void) fprintf(stderr, gettext("If you must use 'mount -F zfs' "
- "or /etc/vfstab, use 'zfs set mountpoint=legacy'.\n"));
- (void) fprintf(stderr, gettext("See zfs(1M) for more "
+ (void) fprintf(stderr, gettext("If you must use 'mount -t zfs' "
+ "or /etc/fstab, use 'zfs set mountpoint=legacy'.\n"));
+ (void) fprintf(stderr, gettext("See zfs(8) for more "
"information.\n"));
ret = 1;
}
diff --git a/contrib/libstdc++/libsupc++/unwind-cxx.h b/contrib/libstdc++/libsupc++/unwind-cxx.h
index 523c054..7eb150e 100644
--- a/contrib/libstdc++/libsupc++/unwind-cxx.h
+++ b/contrib/libstdc++/libsupc++/unwind-cxx.h
@@ -133,7 +133,7 @@ extern "C" void __cxa_bad_typeid ();
// throws, and if bad_exception needs to be thrown. Called from the
// compiler.
extern "C" void __cxa_call_unexpected (void *) __attribute__((noreturn));
-extern "C" void __cxa_call_terminate (void*) __attribute__((noreturn));
+extern "C" void __cxa_call_terminate (_Unwind_Exception*) __attribute__((noreturn));
#ifdef __ARM_EABI_UNWINDER__
// Arm EABI specified routines.
diff --git a/contrib/llvm/include/llvm/CodeGen/FunctionLoweringInfo.h b/contrib/llvm/include/llvm/CodeGen/FunctionLoweringInfo.h
index ea6cb27..e6f30a2 100644
--- a/contrib/llvm/include/llvm/CodeGen/FunctionLoweringInfo.h
+++ b/contrib/llvm/include/llvm/CodeGen/FunctionLoweringInfo.h
@@ -115,6 +115,11 @@ public:
/// there's no other convenient place for it to live right now.
std::vector<std::pair<MachineInstr*, unsigned> > PHINodesToUpdate;
+ /// If the current MBB is a landing pad, the exception pointer and exception
+ /// selector registers are copied into these virtual registers by
+ /// SelectionDAGISel::PrepareEHLandingPad().
+ unsigned ExceptionPointerVirtReg, ExceptionSelectorVirtReg;
+
explicit FunctionLoweringInfo(const TargetLowering &TLI);
/// set - Initialize this FunctionLoweringInfo with the given Function
diff --git a/contrib/llvm/include/llvm/CodeGen/MachineBasicBlock.h b/contrib/llvm/include/llvm/CodeGen/MachineBasicBlock.h
index 0f2f874..d6f5883 100644
--- a/contrib/llvm/include/llvm/CodeGen/MachineBasicBlock.h
+++ b/contrib/llvm/include/llvm/CodeGen/MachineBasicBlock.h
@@ -296,6 +296,11 @@ public:
/// is an error to add the same register to the same set more than once.
void addLiveIn(unsigned Reg) { LiveIns.push_back(Reg); }
+ /// Add PhysReg as live in to this block, and ensure that there is a copy of
+ /// PhysReg to a virtual register of class RC. Return the virtual register
+ /// that is a copy of the live in PhysReg.
+ unsigned addLiveIn(unsigned PhysReg, const TargetRegisterClass *RC);
+
/// removeLiveIn - Remove the specified register from the live in set.
///
void removeLiveIn(unsigned Reg);
diff --git a/contrib/llvm/lib/CodeGen/MachineBasicBlock.cpp b/contrib/llvm/lib/CodeGen/MachineBasicBlock.cpp
index 78e9950..91810bd 100644
--- a/contrib/llvm/lib/CodeGen/MachineBasicBlock.cpp
+++ b/contrib/llvm/lib/CodeGen/MachineBasicBlock.cpp
@@ -19,6 +19,7 @@
#include "llvm/CodeGen/LiveVariables.h"
#include "llvm/CodeGen/MachineDominators.h"
#include "llvm/CodeGen/MachineFunction.h"
+#include "llvm/CodeGen/MachineInstrBuilder.h"
#include "llvm/CodeGen/MachineLoopInfo.h"
#include "llvm/CodeGen/MachineRegisterInfo.h"
#include "llvm/CodeGen/SlotIndexes.h"
@@ -341,6 +342,38 @@ bool MachineBasicBlock::isLiveIn(unsigned Reg) const {
return I != livein_end();
}
+unsigned
+MachineBasicBlock::addLiveIn(unsigned PhysReg, const TargetRegisterClass *RC) {
+ assert(getParent() && "MBB must be inserted in function");
+ assert(TargetRegisterInfo::isPhysicalRegister(PhysReg) && "Expected physreg");
+ assert(RC && "Register class is required");
+ assert((isLandingPad() || this == &getParent()->front()) &&
+ "Only the entry block and landing pads can have physreg live ins");
+
+ bool LiveIn = isLiveIn(PhysReg);
+ iterator I = SkipPHIsAndLabels(begin()), E = end();
+ MachineRegisterInfo &MRI = getParent()->getRegInfo();
+ const TargetInstrInfo &TII = *getParent()->getTarget().getInstrInfo();
+
+ // Look for an existing copy.
+ if (LiveIn)
+ for (;I != E && I->isCopy(); ++I)
+ if (I->getOperand(1).getReg() == PhysReg) {
+ unsigned VirtReg = I->getOperand(0).getReg();
+ if (!MRI.constrainRegClass(VirtReg, RC))
+ llvm_unreachable("Incompatible live-in register class.");
+ return VirtReg;
+ }
+
+ // No luck, create a virtual register.
+ unsigned VirtReg = MRI.createVirtualRegister(RC);
+ BuildMI(*this, I, DebugLoc(), TII.get(TargetOpcode::COPY), VirtReg)
+ .addReg(PhysReg, RegState::Kill);
+ if (!LiveIn)
+ addLiveIn(PhysReg);
+ return VirtReg;
+}
+
void MachineBasicBlock::moveBefore(MachineBasicBlock *NewAfter) {
getParent()->splice(NewAfter, this);
}
diff --git a/contrib/llvm/lib/CodeGen/SelectionDAG/SelectionDAGBuilder.cpp b/contrib/llvm/lib/CodeGen/SelectionDAG/SelectionDAGBuilder.cpp
index 67db211..194aba8 100644
--- a/contrib/llvm/lib/CodeGen/SelectionDAG/SelectionDAGBuilder.cpp
+++ b/contrib/llvm/lib/CodeGen/SelectionDAG/SelectionDAGBuilder.cpp
@@ -1910,33 +1910,25 @@ void SelectionDAGBuilder::visitLandingPad(const LandingPadInst &LP) {
SmallVector<EVT, 2> ValueVTs;
ComputeValueVTs(TLI, LP.getType(), ValueVTs);
+ assert(ValueVTs.size() == 2 && "Only two-valued landingpads are supported");
- // Insert the EXCEPTIONADDR instruction.
- assert(FuncInfo.MBB->isLandingPad() &&
- "Call to eh.exception not in landing pad!");
- SDVTList VTs = DAG.getVTList(TLI.getPointerTy(), MVT::Other);
+ // Get the two live-in registers as SDValues. The physregs have already been
+ // copied into virtual registers.
SDValue Ops[2];
- Ops[0] = DAG.getRoot();
- SDValue Op1 = DAG.getNode(ISD::EXCEPTIONADDR, getCurDebugLoc(), VTs, Ops, 1);
- SDValue Chain = Op1.getValue(1);
-
- // Insert the EHSELECTION instruction.
- VTs = DAG.getVTList(TLI.getPointerTy(), MVT::Other);
- Ops[0] = Op1;
- Ops[1] = Chain;
- SDValue Op2 = DAG.getNode(ISD::EHSELECTION, getCurDebugLoc(), VTs, Ops, 2);
- Chain = Op2.getValue(1);
- Op2 = DAG.getSExtOrTrunc(Op2, getCurDebugLoc(), MVT::i32);
-
- Ops[0] = Op1;
- Ops[1] = Op2;
+ Ops[0] = DAG.getZExtOrTrunc(
+ DAG.getCopyFromReg(DAG.getEntryNode(), getCurDebugLoc(),
+ FuncInfo.ExceptionPointerVirtReg, TLI.getPointerTy()),
+ getCurDebugLoc(), ValueVTs[0]);
+ Ops[1] = DAG.getZExtOrTrunc(
+ DAG.getCopyFromReg(DAG.getEntryNode(), getCurDebugLoc(),
+ FuncInfo.ExceptionSelectorVirtReg, TLI.getPointerTy()),
+ getCurDebugLoc(), ValueVTs[1]);
+
+ // Merge into one.
SDValue Res = DAG.getNode(ISD::MERGE_VALUES, getCurDebugLoc(),
DAG.getVTList(&ValueVTs[0], ValueVTs.size()),
&Ops[0], 2);
-
- std::pair<SDValue, SDValue> RetPair = std::make_pair(Res, Chain);
- setValue(&LP, RetPair.first);
- DAG.setRoot(RetPair.second);
+ setValue(&LP, Res);
}
/// handleSmallSwitchCaseRange - Emit a series of specific tests (suitable for
diff --git a/contrib/llvm/lib/CodeGen/SelectionDAG/SelectionDAGISel.cpp b/contrib/llvm/lib/CodeGen/SelectionDAG/SelectionDAGISel.cpp
index e21f26e..8b110e3 100644
--- a/contrib/llvm/lib/CodeGen/SelectionDAG/SelectionDAGISel.cpp
+++ b/contrib/llvm/lib/CodeGen/SelectionDAG/SelectionDAGISel.cpp
@@ -827,12 +827,13 @@ void SelectionDAGISel::PrepareEHLandingPad() {
.addSym(Label);
// Mark exception register as live in.
- unsigned Reg = TLI.getExceptionPointerRegister();
- if (Reg) MBB->addLiveIn(Reg);
+ const TargetRegisterClass *PtrRC = TLI.getRegClassFor(TLI.getPointerTy());
+ if (unsigned Reg = TLI.getExceptionPointerRegister())
+ FuncInfo->ExceptionPointerVirtReg = MBB->addLiveIn(Reg, PtrRC);
// Mark exception selector register as live in.
- Reg = TLI.getExceptionSelectorRegister();
- if (Reg) MBB->addLiveIn(Reg);
+ if (unsigned Reg = TLI.getExceptionSelectorRegister())
+ FuncInfo->ExceptionSelectorVirtReg = MBB->addLiveIn(Reg, PtrRC);
}
/// isFoldedOrDeadInstruction - Return true if the specified instruction is
@@ -970,6 +971,8 @@ void SelectionDAGISel::SelectAllBasicBlocks(const Function &Fn) {
FuncInfo->InsertPt = FuncInfo->MBB->getFirstNonPHI();
// Setup an EH landing-pad block.
+ FuncInfo->ExceptionPointerVirtReg = 0;
+ FuncInfo->ExceptionSelectorVirtReg = 0;
if (FuncInfo->MBB->isLandingPad())
PrepareEHLandingPad();
diff --git a/contrib/llvm/lib/Target/ARM/ARMFrameLowering.h b/contrib/llvm/lib/Target/ARM/ARMFrameLowering.h
index efa255a..d95a2cb 100644
--- a/contrib/llvm/lib/Target/ARM/ARMFrameLowering.h
+++ b/contrib/llvm/lib/Target/ARM/ARMFrameLowering.h
@@ -27,7 +27,7 @@ protected:
public:
explicit ARMFrameLowering(const ARMSubtarget &sti)
- : TargetFrameLowering(StackGrowsDown, sti.getStackAlignment(), 0, 4),
+ : TargetFrameLowering(StackGrowsDown, sti.getStackAlignment(), 0, 8),
STI(sti) {
}
diff --git a/contrib/llvm/tools/clang/lib/Sema/SemaDecl.cpp b/contrib/llvm/tools/clang/lib/Sema/SemaDecl.cpp
index f72bec0..fd7cfeb 100644
--- a/contrib/llvm/tools/clang/lib/Sema/SemaDecl.cpp
+++ b/contrib/llvm/tools/clang/lib/Sema/SemaDecl.cpp
@@ -10296,7 +10296,8 @@ void Sema::ActOnTagFinishDefinition(Scope *S, Decl *TagD,
Tag->setTopLevelDeclInObjCContainer();
// Notify the consumer that we've defined a tag.
- Consumer.HandleTagDeclDefinition(Tag);
+ if (!Tag->isInvalidDecl())
+ Consumer.HandleTagDeclDefinition(Tag);
}
void Sema::ActOnObjCContainerFinishDefinition() {
diff --git a/contrib/smbfs/mount_smbfs/Makefile b/contrib/smbfs/mount_smbfs/Makefile
deleted file mode 100644
index ffd79c9..0000000
--- a/contrib/smbfs/mount_smbfs/Makefile
+++ /dev/null
@@ -1,18 +0,0 @@
-# $Id: Makefile,v 1.7 2001/04/16 04:34:26 bp Exp $
-
-PROG= mount_smbfs
-SRCS= mount_smbfs.c getmntopts.c
-MAN8= mount_smbfs.8
-
-BINDIR= /sbin
-#NOSHARED=yes
-
-MOUNT= ${.CURDIR}/../mount
-CFLAGS+= -DSMBFS -I${MOUNT}
-
-.PATH: ${MOUNT}
-
-LDADD+= -lsmb
-DPADD+= ${LIBSMB}
-
-.include <bsd.prog.mk>
diff --git a/contrib/wpa/COPYING b/contrib/wpa/COPYING
index 14f5453..8a98582 100644
--- a/contrib/wpa/COPYING
+++ b/contrib/wpa/COPYING
@@ -1,340 +1,22 @@
- GNU GENERAL PUBLIC LICENSE
- Version 2, June 1991
+wpa_supplicant and hostapd
+--------------------------
- Copyright (C) 1989, 1991 Free Software Foundation, Inc.
- 51 Franklin St, Fifth Floor, Boston, MA 02110-1301 USA
- Everyone is permitted to copy and distribute verbatim copies
- of this license document, but changing it is not allowed.
+Copyright (c) 2002-2012, Jouni Malinen <j@w1.fi> and contributors
+All Rights Reserved.
- Preamble
- The licenses for most software are designed to take away your
-freedom to share and change it. By contrast, the GNU General Public
-License is intended to guarantee your freedom to share and change free
-software--to make sure the software is free for all its users. This
-General Public License applies to most of the Free Software
-Foundation's software and to any other program whose authors commit to
-using it. (Some other Free Software Foundation software is covered by
-the GNU Library General Public License instead.) You can apply it to
-your programs, too.
+See the README file for the current license terms.
- When we speak of free software, we are referring to freedom, not
-price. Our General Public Licenses are designed to make sure that you
-have the freedom to distribute copies of free software (and charge for
-this service if you wish), that you receive source code or can get it
-if you want it, that you can change the software or use pieces of it
-in new free programs; and that you know you can do these things.
+This software was previously distributed under BSD/GPL v2 dual license
+terms that allowed either of those license alternatives to be
+selected. As of February 11, 2012, the project has chosen to use only
+the BSD license option for future distribution. As such, the GPL v2
+license option is no longer used. It should be noted that the BSD
+license option (the one with advertisement clause removed) is compatible
+with GPL and as such, does not prevent use of this software in projects
+that use GPL.
- To protect your rights, we need to make restrictions that forbid
-anyone to deny you these rights or to ask you to surrender the rights.
-These restrictions translate to certain responsibilities for you if you
-distribute copies of the software, or if you modify it.
-
- For example, if you distribute copies of such a program, whether
-gratis or for a fee, you must give the recipients all the rights that
-you have. You must make sure that they, too, receive or can get the
-source code. And you must show them these terms so they know their
-rights.
-
- We protect your rights with two steps: (1) copyright the software, and
-(2) offer you this license which gives you legal permission to copy,
-distribute and/or modify the software.
-
- Also, for each author's protection and ours, we want to make certain
-that everyone understands that there is no warranty for this free
-software. If the software is modified by someone else and passed on, we
-want its recipients to know that what they have is not the original, so
-that any problems introduced by others will not reflect on the original
-authors' reputations.
-
- Finally, any free program is threatened constantly by software
-patents. We wish to avoid the danger that redistributors of a free
-program will individually obtain patent licenses, in effect making the
-program proprietary. To prevent this, we have made it clear that any
-patent must be licensed for everyone's free use or not licensed at all.
-
- The precise terms and conditions for copying, distribution and
-modification follow.
-
- GNU GENERAL PUBLIC LICENSE
- TERMS AND CONDITIONS FOR COPYING, DISTRIBUTION AND MODIFICATION
-
- 0. This License applies to any program or other work which contains
-a notice placed by the copyright holder saying it may be distributed
-under the terms of this General Public License. The "Program", below,
-refers to any such program or work, and a "work based on the Program"
-means either the Program or any derivative work under copyright law:
-that is to say, a work containing the Program or a portion of it,
-either verbatim or with modifications and/or translated into another
-language. (Hereinafter, translation is included without limitation in
-the term "modification".) Each licensee is addressed as "you".
-
-Activities other than copying, distribution and modification are not
-covered by this License; they are outside its scope. The act of
-running the Program is not restricted, and the output from the Program
-is covered only if its contents constitute a work based on the
-Program (independent of having been made by running the Program).
-Whether that is true depends on what the Program does.
-
- 1. You may copy and distribute verbatim copies of the Program's
-source code as you receive it, in any medium, provided that you
-conspicuously and appropriately publish on each copy an appropriate
-copyright notice and disclaimer of warranty; keep intact all the
-notices that refer to this License and to the absence of any warranty;
-and give any other recipients of the Program a copy of this License
-along with the Program.
-
-You may charge a fee for the physical act of transferring a copy, and
-you may at your option offer warranty protection in exchange for a fee.
-
- 2. You may modify your copy or copies of the Program or any portion
-of it, thus forming a work based on the Program, and copy and
-distribute such modifications or work under the terms of Section 1
-above, provided that you also meet all of these conditions:
-
- a) You must cause the modified files to carry prominent notices
- stating that you changed the files and the date of any change.
-
- b) You must cause any work that you distribute or publish, that in
- whole or in part contains or is derived from the Program or any
- part thereof, to be licensed as a whole at no charge to all third
- parties under the terms of this License.
-
- c) If the modified program normally reads commands interactively
- when run, you must cause it, when started running for such
- interactive use in the most ordinary way, to print or display an
- announcement including an appropriate copyright notice and a
- notice that there is no warranty (or else, saying that you provide
- a warranty) and that users may redistribute the program under
- these conditions, and telling the user how to view a copy of this
- License. (Exception: if the Program itself is interactive but
- does not normally print such an announcement, your work based on
- the Program is not required to print an announcement.)
-
-These requirements apply to the modified work as a whole. If
-identifiable sections of that work are not derived from the Program,
-and can be reasonably considered independent and separate works in
-themselves, then this License, and its terms, do not apply to those
-sections when you distribute them as separate works. But when you
-distribute the same sections as part of a whole which is a work based
-on the Program, the distribution of the whole must be on the terms of
-this License, whose permissions for other licensees extend to the
-entire whole, and thus to each and every part regardless of who wrote it.
-
-Thus, it is not the intent of this section to claim rights or contest
-your rights to work written entirely by you; rather, the intent is to
-exercise the right to control the distribution of derivative or
-collective works based on the Program.
-
-In addition, mere aggregation of another work not based on the Program
-with the Program (or with a work based on the Program) on a volume of
-a storage or distribution medium does not bring the other work under
-the scope of this License.
-
- 3. You may copy and distribute the Program (or a work based on it,
-under Section 2) in object code or executable form under the terms of
-Sections 1 and 2 above provided that you also do one of the following:
-
- a) Accompany it with the complete corresponding machine-readable
- source code, which must be distributed under the terms of Sections
- 1 and 2 above on a medium customarily used for software interchange; or,
-
- b) Accompany it with a written offer, valid for at least three
- years, to give any third party, for a charge no more than your
- cost of physically performing source distribution, a complete
- machine-readable copy of the corresponding source code, to be
- distributed under the terms of Sections 1 and 2 above on a medium
- customarily used for software interchange; or,
-
- c) Accompany it with the information you received as to the offer
- to distribute corresponding source code. (This alternative is
- allowed only for noncommercial distribution and only if you
- received the program in object code or executable form with such
- an offer, in accord with Subsection b above.)
-
-The source code for a work means the preferred form of the work for
-making modifications to it. For an executable work, complete source
-code means all the source code for all modules it contains, plus any
-associated interface definition files, plus the scripts used to
-control compilation and installation of the executable. However, as a
-special exception, the source code distributed need not include
-anything that is normally distributed (in either source or binary
-form) with the major components (compiler, kernel, and so on) of the
-operating system on which the executable runs, unless that component
-itself accompanies the executable.
-
-If distribution of executable or object code is made by offering
-access to copy from a designated place, then offering equivalent
-access to copy the source code from the same place counts as
-distribution of the source code, even though third parties are not
-compelled to copy the source along with the object code.
-
- 4. You may not copy, modify, sublicense, or distribute the Program
-except as expressly provided under this License. Any attempt
-otherwise to copy, modify, sublicense or distribute the Program is
-void, and will automatically terminate your rights under this License.
-However, parties who have received copies, or rights, from you under
-this License will not have their licenses terminated so long as such
-parties remain in full compliance.
-
- 5. You are not required to accept this License, since you have not
-signed it. However, nothing else grants you permission to modify or
-distribute the Program or its derivative works. These actions are
-prohibited by law if you do not accept this License. Therefore, by
-modifying or distributing the Program (or any work based on the
-Program), you indicate your acceptance of this License to do so, and
-all its terms and conditions for copying, distributing or modifying
-the Program or works based on it.
-
- 6. Each time you redistribute the Program (or any work based on the
-Program), the recipient automatically receives a license from the
-original licensor to copy, distribute or modify the Program subject to
-these terms and conditions. You may not impose any further
-restrictions on the recipients' exercise of the rights granted herein.
-You are not responsible for enforcing compliance by third parties to
-this License.
-
- 7. If, as a consequence of a court judgment or allegation of patent
-infringement or for any other reason (not limited to patent issues),
-conditions are imposed on you (whether by court order, agreement or
-otherwise) that contradict the conditions of this License, they do not
-excuse you from the conditions of this License. If you cannot
-distribute so as to satisfy simultaneously your obligations under this
-License and any other pertinent obligations, then as a consequence you
-may not distribute the Program at all. For example, if a patent
-license would not permit royalty-free redistribution of the Program by
-all those who receive copies directly or indirectly through you, then
-the only way you could satisfy both it and this License would be to
-refrain entirely from distribution of the Program.
-
-If any portion of this section is held invalid or unenforceable under
-any particular circumstance, the balance of the section is intended to
-apply and the section as a whole is intended to apply in other
-circumstances.
-
-It is not the purpose of this section to induce you to infringe any
-patents or other property right claims or to contest validity of any
-such claims; this section has the sole purpose of protecting the
-integrity of the free software distribution system, which is
-implemented by public license practices. Many people have made
-generous contributions to the wide range of software distributed
-through that system in reliance on consistent application of that
-system; it is up to the author/donor to decide if he or she is willing
-to distribute software through any other system and a licensee cannot
-impose that choice.
-
-This section is intended to make thoroughly clear what is believed to
-be a consequence of the rest of this License.
-
- 8. If the distribution and/or use of the Program is restricted in
-certain countries either by patents or by copyrighted interfaces, the
-original copyright holder who places the Program under this License
-may add an explicit geographical distribution limitation excluding
-those countries, so that distribution is permitted only in or among
-countries not thus excluded. In such case, this License incorporates
-the limitation as if written in the body of this License.
-
- 9. The Free Software Foundation may publish revised and/or new versions
-of the General Public License from time to time. Such new versions will
-be similar in spirit to the present version, but may differ in detail to
-address new problems or concerns.
-
-Each version is given a distinguishing version number. If the Program
-specifies a version number of this License which applies to it and "any
-later version", you have the option of following the terms and conditions
-either of that version or of any later version published by the Free
-Software Foundation. If the Program does not specify a version number of
-this License, you may choose any version ever published by the Free Software
-Foundation.
-
- 10. If you wish to incorporate parts of the Program into other free
-programs whose distribution conditions are different, write to the author
-to ask for permission. For software which is copyrighted by the Free
-Software Foundation, write to the Free Software Foundation; we sometimes
-make exceptions for this. Our decision will be guided by the two goals
-of preserving the free status of all derivatives of our free software and
-of promoting the sharing and reuse of software generally.
-
- NO WARRANTY
-
- 11. BECAUSE THE PROGRAM IS LICENSED FREE OF CHARGE, THERE IS NO WARRANTY
-FOR THE PROGRAM, TO THE EXTENT PERMITTED BY APPLICABLE LAW. EXCEPT WHEN
-OTHERWISE STATED IN WRITING THE COPYRIGHT HOLDERS AND/OR OTHER PARTIES
-PROVIDE THE PROGRAM "AS IS" WITHOUT WARRANTY OF ANY KIND, EITHER EXPRESSED
-OR IMPLIED, INCLUDING, BUT NOT LIMITED TO, THE IMPLIED WARRANTIES OF
-MERCHANTABILITY AND FITNESS FOR A PARTICULAR PURPOSE. THE ENTIRE RISK AS
-TO THE QUALITY AND PERFORMANCE OF THE PROGRAM IS WITH YOU. SHOULD THE
-PROGRAM PROVE DEFECTIVE, YOU ASSUME THE COST OF ALL NECESSARY SERVICING,
-REPAIR OR CORRECTION.
-
- 12. IN NO EVENT UNLESS REQUIRED BY APPLICABLE LAW OR AGREED TO IN WRITING
-WILL ANY COPYRIGHT HOLDER, OR ANY OTHER PARTY WHO MAY MODIFY AND/OR
-REDISTRIBUTE THE PROGRAM AS PERMITTED ABOVE, BE LIABLE TO YOU FOR DAMAGES,
-INCLUDING ANY GENERAL, SPECIAL, INCIDENTAL OR CONSEQUENTIAL DAMAGES ARISING
-OUT OF THE USE OR INABILITY TO USE THE PROGRAM (INCLUDING BUT NOT LIMITED
-TO LOSS OF DATA OR DATA BEING RENDERED INACCURATE OR LOSSES SUSTAINED BY
-YOU OR THIRD PARTIES OR A FAILURE OF THE PROGRAM TO OPERATE WITH ANY OTHER
-PROGRAMS), EVEN IF SUCH HOLDER OR OTHER PARTY HAS BEEN ADVISED OF THE
-POSSIBILITY OF SUCH DAMAGES.
-
- END OF TERMS AND CONDITIONS
-
- How to Apply These Terms to Your New Programs
-
- If you develop a new program, and you want it to be of the greatest
-possible use to the public, the best way to achieve this is to make it
-free software which everyone can redistribute and change under these terms.
-
- To do so, attach the following notices to the program. It is safest
-to attach them to the start of each source file to most effectively
-convey the exclusion of warranty; and each file should have at least
-the "copyright" line and a pointer to where the full notice is found.
-
- <one line to give the program's name and a brief idea of what it does.>
- Copyright (C) 19yy <name of author>
-
- This program is free software; you can redistribute it and/or modify
- it under the terms of the GNU General Public License as published by
- the Free Software Foundation; either version 2 of the License, or
- (at your option) any later version.
-
- This program is distributed in the hope that it will be useful,
- but WITHOUT ANY WARRANTY; without even the implied warranty of
- MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the
- GNU General Public License for more details.
-
- You should have received a copy of the GNU General Public License
- along with this program; if not, write to the Free Software
- Foundation, Inc., 51 Franklin St, Fifth Floor, Boston, MA 02110-1301 USA
-
-
-Also add information on how to contact you by electronic and paper mail.
-
-If the program is interactive, make it output a short notice like this
-when it starts in an interactive mode:
-
- Gnomovision version 69, Copyright (C) 19yy name of author
- Gnomovision comes with ABSOLUTELY NO WARRANTY; for details type `show w'.
- This is free software, and you are welcome to redistribute it
- under certain conditions; type `show c' for details.
-
-The hypothetical commands `show w' and `show c' should show the appropriate
-parts of the General Public License. Of course, the commands you use may
-be called something other than `show w' and `show c'; they could even be
-mouse-clicks or menu items--whatever suits your program.
-
-You should also get your employer (if you work as a programmer) or your
-school, if any, to sign a "copyright disclaimer" for the program, if
-necessary. Here is a sample; alter the names:
-
- Yoyodyne, Inc., hereby disclaims all copyright interest in the program
- `Gnomovision' (which makes passes at compilers) written by James Hacker.
-
- <signature of Ty Coon>, 1 April 1989
- Ty Coon, President of Vice
-
-This General Public License does not permit incorporating your program into
-proprietary programs. If your program is a subroutine library, you may
-consider it more useful to permit linking proprietary applications with the
-library. If this is what you want to do, use the GNU Library General
-Public License instead of this License.
+Some of the files may still include pointers to GPL version 2 license
+terms. However, such copyright and license notifications are maintained
+only for attribution purposes and any distribution of this software
+after February 11, 2012 is no longer under the GPL v2 option.
diff --git a/contrib/wpa/README b/contrib/wpa/README
index 9c6be85..1721a3b 100644
--- a/contrib/wpa/README
+++ b/contrib/wpa/README
@@ -1,19 +1,56 @@
-wpa_supplicant and hostapd v0.6.x
----------------------------------
+wpa_supplicant and hostapd
+--------------------------
-Copyright (c) 2002-2007, Jouni Malinen <j@w1.fi> and contributors
+Copyright (c) 2002-2012, Jouni Malinen <j@w1.fi> and contributors
All Rights Reserved.
-These program is dual-licensed under both the GPL version 2 and BSD
-license. Either license may be used at your option.
+These programs are licensed under the BSD license (the one with
+advertisement clause removed).
+
+If you are submitting changes to the project, please see CONTRIBUTIONS
+file for more instructions.
This package may include either wpa_supplicant, hostapd, or both. See
README file respective subdirectories (wpa_supplicant/README or
hostapd/README) for more details.
-Source code files have been moved around in v0.6.x releases and
-compared to earlier releases, the programs are now build by first
-going to a subdirectory (wpa_supplicant or hostapd) and creating
-build configuration (.config) and running 'make' there (for
-Linux/BSD/cygwin builds).
+Source code files were moved around in v0.6.x releases and compared to
+earlier releases, the programs are now built by first going to a
+subdirectory (wpa_supplicant or hostapd) and creating build
+configuration (.config) and running 'make' there (for Linux/BSD/cygwin
+builds).
+
+
+License
+-------
+
+This software may be distributed, used, and modified under the terms of
+BSD license:
+
+Redistribution and use in source and binary forms, with or without
+modification, are permitted provided that the following conditions are
+met:
+
+1. Redistributions of source code must retain the above copyright
+ notice, this list of conditions and the following disclaimer.
+
+2. Redistributions in binary form must reproduce the above copyright
+ notice, this list of conditions and the following disclaimer in the
+ documentation and/or other materials provided with the distribution.
+
+3. Neither the name(s) of the above-listed copyright holder(s) nor the
+ names of its contributors may be used to endorse or promote products
+ derived from this software without specific prior written permission.
+
+THIS SOFTWARE IS PROVIDED BY THE COPYRIGHT HOLDERS AND CONTRIBUTORS
+"AS IS" AND ANY EXPRESS OR IMPLIED WARRANTIES, INCLUDING, BUT NOT
+LIMITED TO, THE IMPLIED WARRANTIES OF MERCHANTABILITY AND FITNESS FOR
+A PARTICULAR PURPOSE ARE DISCLAIMED. IN NO EVENT SHALL THE COPYRIGHT
+OWNER OR CONTRIBUTORS BE LIABLE FOR ANY DIRECT, INDIRECT, INCIDENTAL,
+SPECIAL, EXEMPLARY, OR CONSEQUENTIAL DAMAGES (INCLUDING, BUT NOT
+LIMITED TO, PROCUREMENT OF SUBSTITUTE GOODS OR SERVICES; LOSS OF USE,
+DATA, OR PROFITS; OR BUSINESS INTERRUPTION) HOWEVER CAUSED AND ON ANY
+THEORY OF LIABILITY, WHETHER IN CONTRACT, STRICT LIABILITY, OR TORT
+(INCLUDING NEGLIGENCE OR OTHERWISE) ARISING IN ANY WAY OUT OF THE USE
+OF THIS SOFTWARE, EVEN IF ADVISED OF THE POSSIBILITY OF SUCH DAMAGE.
diff --git a/contrib/wpa/hostapd/.gitignore b/contrib/wpa/hostapd/.gitignore
deleted file mode 100644
index 6dd2c2f..0000000
--- a/contrib/wpa/hostapd/.gitignore
+++ /dev/null
@@ -1,7 +0,0 @@
-*.d
-.config
-driver_conf.c
-hostapd
-hostapd_cli
-hlr_auc_gw
-nt_password_hash
diff --git a/contrib/wpa/hostapd/ChangeLog b/contrib/wpa/hostapd/ChangeLog
index a7f1f10..6824e5a 100644
--- a/contrib/wpa/hostapd/ChangeLog
+++ b/contrib/wpa/hostapd/ChangeLog
@@ -1,20 +1,206 @@
ChangeLog for hostapd
-2010-09-07 - v0.7.3
- * fixed re-association after WPS not initializing WPA state machine in
- some cases
- * fixed WPS IE update on reconfiguration
- * fixed WPS code not to proxy Probe Request frames for foreign SSIDs
- * added WPS workaround for open networks and some known interop issues
- * fixed WPS Diffie-Hellman derivation to use correct public key length
- * fixed FT RRB messages on big endian CPUs
- * changed WPS protection for brute force AP PIN attacks to disable AP
- PIN only temporarily (but with increasing time) to avoid usability
- issues on Label-only devices
- * added wps_ap_pin command for more secure handling of AP PIN
- operations (e.g., to generate a random AP PIN and only use it for
- short amount of time)
- * fixed HT STBC negotiation
+2013-01-12 - v2.0
+ * added AP-STA-DISCONNECTED ctrl_iface event
+ * improved debug logging (human readable event names, interface name
+ included in more entries)
+ * added number of small changes to make it easier for static analyzers
+ to understand the implementation
+ * added a workaround for Windows 7 Michael MIC failure reporting and
+ use of the Secure bit in EAPOL-Key msg 3/4
+ * fixed number of small bugs (see git logs for more details)
+ * changed OpenSSL to read full certificate chain from server_cert file
+ * nl80211: number of updates to use new cfg80211/nl80211 functionality
+ - replace monitor interface with nl80211 commands
+ - additional information for driver-based AP SME
+ * EAP-pwd:
+ - fix KDF for group 21 and zero-padding
+ - added support for fragmentation
+ - increased maximum number of hunting-and-pecking iterations
+ * avoid excessive Probe Response retries for broadcast Probe Request
+ frames (only with drivers using hostapd SME/MLME)
+ * added preliminary support for using TLS v1.2 (CONFIG_TLSV12=y)
+ * fixed WPS operation stopping on dual concurrent AP
+ * added wps_rf_bands configuration parameter for overriding RF Bands
+ value for WPS
+ * added support for getting per-device PSK from RADIUS Tunnel-Password
+ * added support for libnl 3.2 and newer
+ * increased initial group key handshake retransmit timeout to 500 ms
+ * added a workaround for 4-way handshake to update SNonce even after
+ having sent EAPOL-Key 3/4 to avoid issues with some supplicant
+ implementations that can change SNonce for each EAP-Key 2/4
+ * added a workaround for EAPOL-Key 4/4 using incorrect type value in
+ WPA2 mode (some deployed stations use WPA type in that message)
+ * added a WPS workaround for mixed mode AP Settings with Windows 7
+ * changed WPS AP PIN disabling mechanism to disable the PIN after 10
+ consecutive failures in addition to using the exponential lockout
+ period
+ * added support for WFA Hotspot 2.0
+ - GAS/ANQP advertisement of network information
+ - disable_dgaf parameter to disable downstream group-addressed
+ forwarding
+ * simplified licensing terms by selecting the BSD license as the only
+ alternative
+ * EAP-SIM: fixed re-authentication not to update pseudonym
+ * EAP-SIM: use Notification round before EAP-Failure
+ * EAP-AKA: added support for AT_COUNTER_TOO_SMALL
+ * EAP-AKA: skip AKA/Identity exchange if EAP identity is recognized
+ * EAP-AKA': fixed identity for MK derivation
+ * EAP-AKA': updated to RFC 5448 (username prefixes changed); note: this
+ breaks interoperability with older versions
+ * EAP-SIM/AKA: allow pseudonym to be used after unknown reauth id
+ * changed ANonce to be a random number instead of Counter-based
+ * added support for canceling WPS operations with hostapd_cli wps_cancel
+ * fixed EAP/WPS to PSK transition on reassociation in cases where
+ deauthentication is missed
+ * hlr_auc_gw enhancements:
+ - a new command line parameter -u can be used to enable updating of
+ SQN in Milenage file
+ - use 5 bit IND for SQN updates
+ - SQLite database can now be used to store Milenage information
+ * EAP-SIM/AKA DB: added optional use of SQLite database for pseudonyms
+ and reauth data
+ * added support for Chargeable-User-Identity (RFC 4372)
+ * added radius_auth_req_attr and radius_acct_req_attr configuration
+ parameters to allow adding/overriding of RADIUS attributes in
+ Access-Request and Accounting-Request packets
+ * added support for RADIUS dynamic authorization server (RFC 5176)
+ * added initial support for WNM operations
+ - BSS max idle period
+ - WNM-Sleep Mode
+ * added new WPS NFC ctrl_iface mechanism
+ - removed obsoleted WPS_OOB command (including support for deprecated
+ UFD config_method)
+ * added FT support for drivers that implement MLME internally
+ * added SA Query support for drivers that implement MLME internally
+ * removed default ACM=1 from AC_VO and AC_VI
+ * changed VENDOR-TEST EAP method to use proper private enterprise number
+ (this will not interoperate with older versions)
+ * added hostapd.conf parameter vendor_elements to allow arbitrary vendor
+ specific elements to be added to the Beacon and Probe Response frames
+ * added support for configuring GCMP cipher for IEEE 802.11ad
+ * added support for 256-bit AES with internal TLS implementation
+ * changed EAPOL transmission to use AC_VO if WMM is active
+ * fixed EAP-TLS/PEAP/TTLS/FAST server to validate TLS Message Length
+ correctly; invalid messages could have caused the hostapd process to
+ terminate before this fix [CVE-2012-4445]
+ * limit number of active wildcard PINs for WPS Registrar to one to avoid
+ confusing behavior with multiple wildcard PINs
+ * added a workaround for WPS PBC session overlap detection to avoid
+ interop issues with deployed station implementations that do not
+ remove active PBC indication from Probe Request frames properly
+ * added support for using SQLite for the eap_user database
+ * added Acct-Session-Id attribute into Access-Request messages
+ * fixed EAPOL frame transmission to non-QoS STAs with nl80211
+ (do not send QoS frames if the STA did not negotiate use of QoS for
+ this association)
+
+2012-05-10 - v1.0
+ * Add channel selection support in hostapd. See hostapd.conf.
+ * Add support for IEEE 802.11v Time Advertisement mechanism with UTC
+ TSF offset. See hostapd.conf for config info.
+ * Delay STA entry removal until Deauth/Disassoc TX status in AP mode.
+ This allows the driver to use PS buffering of Deauthentication and
+ Disassociation frames when the STA is in power save sleep. Only
+ available with drivers that provide TX status events for Deauth/
+ Disassoc frames (nl80211).
+ * Allow PMKSA caching to be disabled on the Authenticator. See
+ hostap.conf config parameter disable_pmksa_caching.
+ * atheros: Add support for IEEE 802.11w configuration.
+ * bsd: Add support for setting HT values in IFM_MMASK.
+ * Allow client isolation to be configured with ap_isolate. Client
+ isolation can be used to prevent low-level bridging of frames
+ between associated stations in the BSS. By default, this bridging
+ is allowed.
+ * Allow coexistance of HT BSSes with WEP/TKIP BSSes.
+ * Add require_ht config parameter, which can be used to configure
+ hostapd to reject association with any station that does not support
+ HT PHY.
+ * Add support for writing debug log to a file using "-f" option. Also
+ add relog CLI command to re-open the log file.
+ * Add bridge handling for WDS STA interfaces. By default they are
+ added to the configured bridge of the AP interface (if present),
+ but the user can also specify a separate bridge using cli command
+ wds_bridge.
+ * hostapd_cli:
+ - Add wds_bridge command for specifying bridge for WDS STA
+ interfaces.
+ - Add relog command for reopening log file.
+ - Send AP-STA-DISCONNECTED event when an AP disconnects a station
+ due to inactivity.
+ - Add wps_config ctrl_interface command for configuring AP. This
+ command can be used to configure the AP using the internal WPS
+ registrar. It works in the same way as new AP settings received
+ from an ER.
+ - Many WPS/WPS ER commands - see WPS/WPS ER sections for details.
+ - Add command get version, that returns hostapd version string.
+ * WNM: Add BSS Transition Management Request for ESS Disassoc Imminent.
+ Use hostapd_cli ess_disassoc (STA addr) (URL) to send the
+ notification to the STA.
+ * Allow AP mode to disconnect STAs based on low ACK condition (when
+ the data connection is not working properly, e.g., due to the STA
+ going outside the range of the AP). Disabled by default, enable by
+ config option disassoc_low_ack.
+ * Add WPA_IGNORE_CONFIG_ERRORS build option to continue in case of bad
+ config file.
+ * WPS:
+ - Send AP Settings as a wrapped Credential attribute to ctrl_iface
+ in WPS-NEW-AP-SETTINGS.
+ - Dispatch more WPS events through hostapd ctrl_iface.
+ - Add mechanism for indicating non-standard WPS errors.
+ - Change concurrent radio AP to use only one WPS UPnP instance.
+ - Add wps_check_pin command for processing PIN from user input.
+ UIs can use this command to process a PIN entered by a user and to
+ validate the checksum digit (if present).
+ - Add hostap_cli get_config command to display current AP config.
+ - Add new hostapd_cli command, wps_ap_pin, to manage AP PIN at
+ runtime and support dynamic AP PIN management.
+ - Disable AP PIN after 10 consecutive failures. Slow down attacks
+ on failures up to 10.
+ - Allow AP to start in Enrollee mode without AP PIN for probing,
+ to be compatible with Windows 7.
+ - Add Config Error into WPS-FAIL events to provide more info
+ to the user on how to resolve the issue.
+ - When controlling multiple interfaces:
+ - apply WPS commands to all interfaces configured to use WPS
+ - apply WPS config changes to all interfaces that use WPS
+ - when an attack is detected on any interface, disable AP PIN on
+ all interfaces
+ * WPS ER:
+ - Show SetSelectedRegistrar events as ctrl_iface events.
+ - Add special AP Setup Locked mode to allow read only ER.
+ ap_setup_locked=2 can now be used to enable a special mode where
+ WPS ER can learn the current AP settings, but cannot change them.
+ * WPS 2.0: Add support for WPS 2.0 (CONFIG_WPS2)
+ - Add build option CONFIG_WPS_EXTENSIBILITY_TESTING to enable tool
+ for testing protocol extensibility.
+ - Add build option CONFIG_WPS_STRICT to allow disabling of WPS
+ workarounds.
+ - Add support for AuthorizedMACs attribute.
+ * TDLS:
+ - Allow TDLS use or TDLS channel switching in the BSS to be
+ prohibited in the BSS, using config params tdls_prohibit and
+ tdls_prohibit_chan_switch.
+ * EAP server: Add support for configuring fragment size (see
+ fragment_size in hostapd.conf).
+ * wlantest: Add a tool wlantest for IEEE802.11 protocol testing.
+ wlantest can be used to capture frames from a monitor interface
+ for realtime capturing or from pcap files for offline analysis.
+ * Interworking: Support added for 802.11u. Enable in .config with
+ CONFIG_INTERWORKING. See hostapd.conf for config parameters for
+ interworking.
+ * Android: Add build and runtime support for Android hostapd.
+ * Add a new debug message level for excessive information. Use
+ -ddd to enable.
+ * TLS: Add support for tls_disable_time_checks=1 in client mode.
+ * Internal TLS:
+ - Add support for TLS v1.1 (RFC 4346). Enable with build parameter
+ CONFIG_TLSV11.
+ - Add domainComponent parser for X.509 names
+ * Reorder some IEs to get closer to IEEE 802.11 standard. Move
+ WMM into end of Beacon, Probe Resp and (Re)Assoc Resp frames.
+ Move HT IEs to be later in (Re)Assoc Resp.
+ * Many bugfixes.
2010-04-18 - v0.7.2
* fix WPS internal Registrar use when an external Registrar is also
@@ -98,7 +284,7 @@ ChangeLog for hostapd
* updated management frame protection to use IEEE Std 802.11w-2009
* fixed number of small WPS issues and added workarounds to
interoperate with common deployed broken implementations
- * added some IEEE 802.11n co-existance rules to disable 40 MHz channels
+ * added some IEEE 802.11n co-existence rules to disable 40 MHz channels
or modify primary/secondary channels if needed based on neighboring
networks
* added support for NFC out-of-band mechanism with WPS
diff --git a/contrib/wpa/hostapd/Makefile b/contrib/wpa/hostapd/Makefile
deleted file mode 100644
index 6d344d2..0000000
--- a/contrib/wpa/hostapd/Makefile
+++ /dev/null
@@ -1,791 +0,0 @@
-ifndef CC
-CC=gcc
-endif
-
-ifndef CFLAGS
-CFLAGS = -MMD -O2 -Wall -g
-endif
-
-CFLAGS += -I../src
-CFLAGS += -I../src/utils
-
-# Uncomment following line and set the path to your kernel tree include
-# directory if your C library does not include all header files.
-# CFLAGS += -DUSE_KERNEL_HEADERS -I/usr/src/linux/include
-
--include .config
-
-ifndef CONFIG_OS
-ifdef CONFIG_NATIVE_WINDOWS
-CONFIG_OS=win32
-else
-CONFIG_OS=unix
-endif
-endif
-
-ifeq ($(CONFIG_OS), internal)
-CFLAGS += -DOS_NO_C_LIB_DEFINES
-endif
-
-ifdef CONFIG_NATIVE_WINDOWS
-CFLAGS += -DCONFIG_NATIVE_WINDOWS
-LIBS += -lws2_32
-endif
-
-OBJS += main.o
-OBJS += config_file.o
-
-OBJS += ../src/ap/hostapd.o
-OBJS += ../src/ap/wpa_auth_glue.o
-OBJS += ../src/ap/drv_callbacks.o
-OBJS += ../src/ap/ap_drv_ops.o
-OBJS += ../src/ap/utils.o
-OBJS += ../src/ap/authsrv.o
-OBJS += ../src/ap/ieee802_1x.o
-OBJS += ../src/ap/ap_config.o
-OBJS += ../src/ap/ieee802_11_auth.o
-OBJS += ../src/ap/sta_info.o
-OBJS += ../src/ap/wpa_auth.o
-OBJS += ../src/ap/tkip_countermeasures.o
-OBJS += ../src/ap/ap_mlme.o
-OBJS += ../src/ap/wpa_auth_ie.o
-OBJS += ../src/ap/preauth_auth.o
-OBJS += ../src/ap/pmksa_cache_auth.o
-
-NEED_RC4=y
-NEED_AES=y
-NEED_MD5=y
-NEED_SHA1=y
-
-OBJS += ../src/drivers/drivers.o
-CFLAGS += -DHOSTAPD
-
-ifdef CONFIG_WPA_TRACE
-CFLAGS += -DWPA_TRACE
-OBJS += ../src/utils/trace.o
-LDFLAGS += -rdynamic
-CFLAGS += -funwind-tables
-ifdef CONFIG_WPA_TRACE_BFD
-CFLAGS += -DWPA_TRACE_BFD
-LIBS += -lbfd
-LIBS_c += -lbfd
-endif
-endif
-
-OBJS += ../src/utils/eloop.o
-OBJS += ../src/utils/common.o
-OBJS += ../src/utils/wpa_debug.o
-OBJS += ../src/utils/wpabuf.o
-OBJS += ../src/utils/os_$(CONFIG_OS).o
-OBJS += ../src/utils/ip_addr.o
-
-OBJS += ../src/common/ieee802_11_common.o
-OBJS += ../src/common/wpa_common.o
-
-OBJS += ../src/eapol_auth/eapol_auth_sm.o
-
-
-ifndef CONFIG_NO_DUMP_STATE
-# define HOSTAPD_DUMP_STATE to include SIGUSR1 handler for dumping state to
-# a file (undefine it, if you want to save in binary size)
-CFLAGS += -DHOSTAPD_DUMP_STATE
-OBJS += dump_state.o
-OBJS += ../src/eapol_auth/eapol_auth_dump.o
-endif
-
-ifdef CONFIG_NO_RADIUS
-CFLAGS += -DCONFIG_NO_RADIUS
-CONFIG_NO_ACCOUNTING=y
-else
-OBJS += ../src/radius/radius.o
-OBJS += ../src/radius/radius_client.o
-endif
-
-ifdef CONFIG_NO_ACCOUNTING
-CFLAGS += -DCONFIG_NO_ACCOUNTING
-else
-OBJS += ../src/ap/accounting.o
-endif
-
-ifdef CONFIG_NO_VLAN
-CFLAGS += -DCONFIG_NO_VLAN
-else
-OBJS += ../src/ap/vlan_init.o
-endif
-
-ifdef CONFIG_NO_CTRL_IFACE
-CFLAGS += -DCONFIG_NO_CTRL_IFACE
-else
-OBJS += ctrl_iface.o
-OBJS += ../src/ap/ctrl_iface_ap.o
-endif
-
-OBJS += ../src/crypto/md5.o
-
-CFLAGS += -DCONFIG_CTRL_IFACE -DCONFIG_CTRL_IFACE_UNIX
-
-ifdef CONFIG_IAPP
-CFLAGS += -DCONFIG_IAPP
-OBJS += ../src/ap/iapp.o
-endif
-
-ifdef CONFIG_RSN_PREAUTH
-CFLAGS += -DCONFIG_RSN_PREAUTH
-CONFIG_L2_PACKET=y
-endif
-
-ifdef CONFIG_PEERKEY
-CFLAGS += -DCONFIG_PEERKEY
-OBJS += ../src/ap/peerkey_auth.o
-endif
-
-ifdef CONFIG_IEEE80211W
-CFLAGS += -DCONFIG_IEEE80211W
-NEED_SHA256=y
-NEED_AES_OMAC1=y
-endif
-
-ifdef CONFIG_IEEE80211R
-CFLAGS += -DCONFIG_IEEE80211R
-OBJS += ../src/ap/wpa_auth_ft.o
-NEED_SHA256=y
-NEED_AES_OMAC1=y
-NEED_AES_UNWRAP=y
-endif
-
-ifdef CONFIG_IEEE80211N
-CFLAGS += -DCONFIG_IEEE80211N
-endif
-
-include ../src/drivers/drivers.mak
-OBJS += $(DRV_AP_OBJS)
-CFLAGS += $(DRV_AP_CFLAGS)
-LDFLAGS += $(DRV_AP_LDFLAGS)
-LIBS += $(DRV_AP_LIBS)
-
-ifdef CONFIG_L2_PACKET
-ifdef CONFIG_DNET_PCAP
-ifdef CONFIG_L2_FREEBSD
-LIBS += -lpcap
-OBJS += ../src/l2_packet/l2_packet_freebsd.o
-else
-LIBS += -ldnet -lpcap
-OBJS += ../src/l2_packet/l2_packet_pcap.o
-endif
-else
-OBJS += ../src/l2_packet/l2_packet_linux.o
-endif
-else
-OBJS += ../src/l2_packet/l2_packet_none.o
-endif
-
-
-ifdef CONFIG_EAP_MD5
-CFLAGS += -DEAP_SERVER_MD5
-OBJS += ../src/eap_server/eap_server_md5.o
-CHAP=y
-endif
-
-ifdef CONFIG_EAP_TLS
-CFLAGS += -DEAP_SERVER_TLS
-OBJS += ../src/eap_server/eap_server_tls.o
-TLS_FUNCS=y
-endif
-
-ifdef CONFIG_EAP_PEAP
-CFLAGS += -DEAP_SERVER_PEAP
-OBJS += ../src/eap_server/eap_server_peap.o
-OBJS += ../src/eap_common/eap_peap_common.o
-TLS_FUNCS=y
-CONFIG_EAP_MSCHAPV2=y
-endif
-
-ifdef CONFIG_EAP_TTLS
-CFLAGS += -DEAP_SERVER_TTLS
-OBJS += ../src/eap_server/eap_server_ttls.o
-TLS_FUNCS=y
-CHAP=y
-endif
-
-ifdef CONFIG_EAP_MSCHAPV2
-CFLAGS += -DEAP_SERVER_MSCHAPV2
-OBJS += ../src/eap_server/eap_server_mschapv2.o
-MS_FUNCS=y
-endif
-
-ifdef CONFIG_EAP_GTC
-CFLAGS += -DEAP_SERVER_GTC
-OBJS += ../src/eap_server/eap_server_gtc.o
-endif
-
-ifdef CONFIG_EAP_SIM
-CFLAGS += -DEAP_SERVER_SIM
-OBJS += ../src/eap_server/eap_server_sim.o
-CONFIG_EAP_SIM_COMMON=y
-NEED_AES_CBC=y
-endif
-
-ifdef CONFIG_EAP_AKA
-CFLAGS += -DEAP_SERVER_AKA
-OBJS += ../src/eap_server/eap_server_aka.o
-CONFIG_EAP_SIM_COMMON=y
-NEED_SHA256=y
-NEED_AES_CBC=y
-endif
-
-ifdef CONFIG_EAP_AKA_PRIME
-CFLAGS += -DEAP_SERVER_AKA_PRIME
-endif
-
-ifdef CONFIG_EAP_SIM_COMMON
-OBJS += ../src/eap_common/eap_sim_common.o
-# Example EAP-SIM/AKA interface for GSM/UMTS authentication. This can be
-# replaced with another file implementating the interface specified in
-# eap_sim_db.h.
-OBJS += ../src/eap_server/eap_sim_db.o
-NEED_FIPS186_2_PRF=y
-endif
-
-ifdef CONFIG_EAP_PAX
-CFLAGS += -DEAP_SERVER_PAX
-OBJS += ../src/eap_server/eap_server_pax.o ../src/eap_common/eap_pax_common.o
-endif
-
-ifdef CONFIG_EAP_PSK
-CFLAGS += -DEAP_SERVER_PSK
-OBJS += ../src/eap_server/eap_server_psk.o ../src/eap_common/eap_psk_common.o
-NEED_AES_OMAC1=y
-NEED_AES_ENCBLOCK=y
-NEED_AES_EAX=y
-endif
-
-ifdef CONFIG_EAP_SAKE
-CFLAGS += -DEAP_SERVER_SAKE
-OBJS += ../src/eap_server/eap_server_sake.o ../src/eap_common/eap_sake_common.o
-endif
-
-ifdef CONFIG_EAP_GPSK
-CFLAGS += -DEAP_SERVER_GPSK
-OBJS += ../src/eap_server/eap_server_gpsk.o ../src/eap_common/eap_gpsk_common.o
-ifdef CONFIG_EAP_GPSK_SHA256
-CFLAGS += -DEAP_SERVER_GPSK_SHA256
-endif
-NEED_SHA256=y
-NEED_AES_OMAC1=y
-endif
-
-ifdef CONFIG_EAP_VENDOR_TEST
-CFLAGS += -DEAP_SERVER_VENDOR_TEST
-OBJS += ../src/eap_server/eap_server_vendor_test.o
-endif
-
-ifdef CONFIG_EAP_FAST
-CFLAGS += -DEAP_SERVER_FAST
-OBJS += ../src/eap_server/eap_server_fast.o
-OBJS += ../src/eap_common/eap_fast_common.o
-TLS_FUNCS=y
-NEED_T_PRF=y
-NEED_AES_UNWRAP=y
-endif
-
-ifdef CONFIG_WPS
-CFLAGS += -DCONFIG_WPS -DEAP_SERVER_WSC
-OBJS += ../src/utils/uuid.o
-OBJS += ../src/ap/wps_hostapd.o
-OBJS += ../src/eap_server/eap_server_wsc.o ../src/eap_common/eap_wsc_common.o
-OBJS += ../src/wps/wps.o
-OBJS += ../src/wps/wps_common.o
-OBJS += ../src/wps/wps_attr_parse.o
-OBJS += ../src/wps/wps_attr_build.o
-OBJS += ../src/wps/wps_attr_process.o
-OBJS += ../src/wps/wps_dev_attr.o
-OBJS += ../src/wps/wps_enrollee.o
-OBJS += ../src/wps/wps_registrar.o
-NEED_DH_GROUPS=y
-NEED_SHA256=y
-NEED_BASE64=y
-NEED_AES_CBC=y
-NEED_MODEXP=y
-CONFIG_EAP=y
-
-ifdef CONFIG_WPS_UFD
-CFLAGS += -DCONFIG_WPS_UFD
-OBJS += ../src/wps/wps_ufd.o
-NEED_WPS_OOB=y
-endif
-
-ifdef CONFIG_WPS_NFC
-CFLAGS += -DCONFIG_WPS_NFC
-OBJS += ../src/wps/ndef.o
-OBJS += ../src/wps/wps_nfc.o
-NEED_WPS_OOB=y
-ifdef CONFIG_WPS_NFC_PN531
-PN531_PATH ?= /usr/local/src/nfc
-CFLAGS += -DCONFIG_WPS_NFC_PN531
-CFLAGS += -I${PN531_PATH}/inc
-OBJS += ../src/wps/wps_nfc_pn531.o
-LIBS += ${PN531_PATH}/lib/wpsnfc.dll
-LIBS += ${PN531_PATH}/lib/libnfc_mapping_pn53x.dll
-endif
-endif
-
-ifdef NEED_WPS_OOB
-CFLAGS += -DCONFIG_WPS_OOB
-endif
-
-ifdef CONFIG_WPS_UPNP
-CFLAGS += -DCONFIG_WPS_UPNP
-OBJS += ../src/wps/wps_upnp.o
-OBJS += ../src/wps/wps_upnp_ssdp.o
-OBJS += ../src/wps/wps_upnp_web.o
-OBJS += ../src/wps/wps_upnp_event.o
-OBJS += ../src/wps/wps_upnp_ap.o
-OBJS += ../src/wps/upnp_xml.o
-OBJS += ../src/wps/httpread.o
-OBJS += ../src/wps/http_client.o
-OBJS += ../src/wps/http_server.o
-endif
-
-endif
-
-ifdef CONFIG_EAP_IKEV2
-CFLAGS += -DEAP_SERVER_IKEV2
-OBJS += ../src/eap_server/eap_server_ikev2.o ../src/eap_server/ikev2.o
-OBJS += ../src/eap_common/eap_ikev2_common.o ../src/eap_common/ikev2_common.o
-NEED_DH_GROUPS=y
-NEED_DH_GROUPS_ALL=y
-NEED_MODEXP=y
-NEED_CIPHER=y
-endif
-
-ifdef CONFIG_EAP_TNC
-CFLAGS += -DEAP_SERVER_TNC
-OBJS += ../src/eap_server/eap_server_tnc.o
-OBJS += ../src/eap_server/tncs.o
-NEED_BASE64=y
-ifndef CONFIG_DRIVER_BSD
-LIBS += -ldl
-endif
-endif
-
-# Basic EAP functionality is needed for EAPOL
-OBJS += eap_register.o
-OBJS += ../src/eap_server/eap_server.o
-OBJS += ../src/eap_common/eap_common.o
-OBJS += ../src/eap_server/eap_server_methods.o
-OBJS += ../src/eap_server/eap_server_identity.o
-CFLAGS += -DEAP_SERVER_IDENTITY
-
-ifdef CONFIG_EAP
-CFLAGS += -DEAP_SERVER
-endif
-
-ifdef CONFIG_PKCS12
-CFLAGS += -DPKCS12_FUNCS
-endif
-
-ifdef MS_FUNCS
-OBJS += ../src/crypto/ms_funcs.o
-NEED_DES=y
-NEED_MD4=y
-endif
-
-ifdef CHAP
-OBJS += ../src/eap_common/chap.o
-endif
-
-ifdef TLS_FUNCS
-NEED_DES=y
-# Shared TLS functions (needed for EAP_TLS, EAP_PEAP, and EAP_TTLS)
-CFLAGS += -DEAP_TLS_FUNCS
-OBJS += ../src/eap_server/eap_server_tls_common.o
-NEED_TLS_PRF=y
-endif
-
-ifndef CONFIG_TLS
-CONFIG_TLS=openssl
-endif
-
-ifeq ($(CONFIG_TLS), openssl)
-ifdef TLS_FUNCS
-OBJS += ../src/crypto/tls_openssl.o
-LIBS += -lssl
-endif
-OBJS += ../src/crypto/crypto_openssl.o
-HOBJS += ../src/crypto/crypto_openssl.o
-ifdef NEED_FIPS186_2_PRF
-OBJS += ../src/crypto/fips_prf_openssl.o
-endif
-LIBS += -lcrypto
-LIBS_h += -lcrypto
-endif
-
-ifeq ($(CONFIG_TLS), gnutls)
-ifdef TLS_FUNCS
-OBJS += ../src/crypto/tls_gnutls.o
-LIBS += -lgnutls -lgpg-error
-ifdef CONFIG_GNUTLS_EXTRA
-CFLAGS += -DCONFIG_GNUTLS_EXTRA
-LIBS += -lgnutls-extra
-endif
-endif
-OBJS += ../src/crypto/crypto_gnutls.o
-HOBJS += ../src/crypto/crypto_gnutls.o
-ifdef NEED_FIPS186_2_PRF
-OBJS += ../src/crypto/fips_prf_gnutls.o
-endif
-LIBS += -lgcrypt
-LIBS_h += -lgcrypt
-CONFIG_INTERNAL_SHA256=y
-CONFIG_INTERNAL_RC4=y
-CONFIG_INTERNAL_DH_GROUP5=y
-endif
-
-ifeq ($(CONFIG_TLS), schannel)
-ifdef TLS_FUNCS
-OBJS += ../src/crypto/tls_schannel.o
-endif
-OBJS += ../src/crypto/crypto_cryptoapi.o
-OBJS_p += ../src/crypto/crypto_cryptoapi.o
-CONFIG_INTERNAL_SHA256=y
-CONFIG_INTERNAL_RC4=y
-CONFIG_INTERNAL_DH_GROUP5=y
-endif
-
-ifeq ($(CONFIG_TLS), nss)
-ifdef TLS_FUNCS
-OBJS += ../src/crypto/tls_nss.o
-LIBS += -lssl3
-endif
-OBJS += ../src/crypto/crypto_nss.o
-ifdef NEED_FIPS186_2_PRF
-OBJS += ../src/crypto/fips_prf_nss.o
-endif
-LIBS += -lnss3
-LIBS_h += -lnss3
-CONFIG_INTERNAL_MD4=y
-CONFIG_INTERNAL_DH_GROUP5=y
-endif
-
-ifeq ($(CONFIG_TLS), internal)
-ifndef CONFIG_CRYPTO
-CONFIG_CRYPTO=internal
-endif
-ifdef TLS_FUNCS
-OBJS += ../src/crypto/crypto_internal-rsa.o
-OBJS += ../src/crypto/tls_internal.o
-OBJS += ../src/tls/tlsv1_common.o
-OBJS += ../src/tls/tlsv1_record.o
-OBJS += ../src/tls/tlsv1_cred.o
-OBJS += ../src/tls/tlsv1_server.o
-OBJS += ../src/tls/tlsv1_server_write.o
-OBJS += ../src/tls/tlsv1_server_read.o
-OBJS += ../src/tls/asn1.o
-OBJS += ../src/tls/rsa.o
-OBJS += ../src/tls/x509v3.o
-OBJS += ../src/tls/pkcs1.o
-OBJS += ../src/tls/pkcs5.o
-OBJS += ../src/tls/pkcs8.o
-NEED_SHA256=y
-NEED_BASE64=y
-NEED_TLS_PRF=y
-NEED_MODEXP=y
-NEED_CIPHER=y
-CFLAGS += -DCONFIG_TLS_INTERNAL
-CFLAGS += -DCONFIG_TLS_INTERNAL_SERVER
-endif
-ifdef NEED_CIPHER
-NEED_DES=y
-OBJS += ../src/crypto/crypto_internal-cipher.o
-endif
-ifdef NEED_MODEXP
-OBJS += ../src/crypto/crypto_internal-modexp.o
-OBJS += ../src/tls/bignum.o
-endif
-ifeq ($(CONFIG_CRYPTO), libtomcrypt)
-OBJS += ../src/crypto/crypto_libtomcrypt.o
-LIBS += -ltomcrypt -ltfm
-LIBS_h += -ltomcrypt -ltfm
-CONFIG_INTERNAL_SHA256=y
-CONFIG_INTERNAL_RC4=y
-CONFIG_INTERNAL_DH_GROUP5=y
-endif
-ifeq ($(CONFIG_CRYPTO), internal)
-OBJS += ../src/crypto/crypto_internal.o
-NEED_AES_DEC=y
-CFLAGS += -DCONFIG_CRYPTO_INTERNAL
-ifdef CONFIG_INTERNAL_LIBTOMMATH
-CFLAGS += -DCONFIG_INTERNAL_LIBTOMMATH
-ifdef CONFIG_INTERNAL_LIBTOMMATH_FAST
-CFLAGS += -DLTM_FAST
-endif
-else
-LIBS += -ltommath
-LIBS_h += -ltommath
-endif
-CONFIG_INTERNAL_AES=y
-CONFIG_INTERNAL_DES=y
-CONFIG_INTERNAL_SHA1=y
-CONFIG_INTERNAL_MD4=y
-CONFIG_INTERNAL_MD5=y
-CONFIG_INTERNAL_SHA256=y
-CONFIG_INTERNAL_RC4=y
-CONFIG_INTERNAL_DH_GROUP5=y
-endif
-ifeq ($(CONFIG_CRYPTO), cryptoapi)
-OBJS += ../src/crypto/crypto_cryptoapi.o
-OBJS_p += ../src/crypto/crypto_cryptoapi.o
-CFLAGS += -DCONFIG_CRYPTO_CRYPTOAPI
-CONFIG_INTERNAL_SHA256=y
-CONFIG_INTERNAL_RC4=y
-endif
-endif
-
-ifeq ($(CONFIG_TLS), none)
-ifdef TLS_FUNCS
-OBJS += ../src/crypto/tls_none.o
-CFLAGS += -DEAP_TLS_NONE
-CONFIG_INTERNAL_AES=y
-CONFIG_INTERNAL_SHA1=y
-CONFIG_INTERNAL_MD5=y
-endif
-OBJS += ../src/crypto/crypto_none.o
-OBJS_p += ../src/crypto/crypto_none.o
-CONFIG_INTERNAL_SHA256=y
-CONFIG_INTERNAL_RC4=y
-endif
-
-ifndef TLS_FUNCS
-OBJS += ../src/crypto/tls_none.o
-ifeq ($(CONFIG_TLS), internal)
-CONFIG_INTERNAL_AES=y
-CONFIG_INTERNAL_SHA1=y
-CONFIG_INTERNAL_MD5=y
-CONFIG_INTERNAL_RC4=y
-endif
-endif
-
-AESOBJS = # none so far
-ifdef CONFIG_INTERNAL_AES
-AESOBJS += ../src/crypto/aes-internal.o ../src/crypto/aes-internal-enc.o
-endif
-
-AESOBJS += ../src/crypto/aes-wrap.o
-ifdef NEED_AES_EAX
-AESOBJS += ../src/crypto/aes-eax.o
-NEED_AES_CTR=y
-endif
-ifdef NEED_AES_CTR
-AESOBJS += ../src/crypto/aes-ctr.o
-endif
-ifdef NEED_AES_ENCBLOCK
-AESOBJS += ../src/crypto/aes-encblock.o
-endif
-ifdef NEED_AES_OMAC1
-AESOBJS += ../src/crypto/aes-omac1.o
-endif
-ifdef NEED_AES_UNWRAP
-NEED_AES_DEC=y
-AESOBJS += ../src/crypto/aes-unwrap.o
-endif
-ifdef NEED_AES_CBC
-NEED_AES_DEC=y
-AESOBJS += ../src/crypto/aes-cbc.o
-endif
-ifdef NEED_AES_DEC
-ifdef CONFIG_INTERNAL_AES
-AESOBJS += ../src/crypto/aes-internal-dec.o
-endif
-endif
-ifdef NEED_AES
-OBJS += $(AESOBJS)
-endif
-
-ifdef NEED_SHA1
-SHA1OBJS += ../src/crypto/sha1.o
-ifdef CONFIG_INTERNAL_SHA1
-SHA1OBJS += ../src/crypto/sha1-internal.o
-ifdef NEED_FIPS186_2_PRF
-SHA1OBJS += ../src/crypto/fips_prf_internal.o
-endif
-endif
-SHA1OBJS += ../src/crypto/sha1-pbkdf2.o
-ifdef NEED_T_PRF
-SHA1OBJS += ../src/crypto/sha1-tprf.o
-endif
-ifdef NEED_TLS_PRF
-SHA1OBJS += ../src/crypto/sha1-tlsprf.o
-endif
-endif
-
-ifdef NEED_SHA1
-OBJS += $(SHA1OBJS)
-endif
-
-ifdef NEED_MD5
-ifdef CONFIG_INTERNAL_MD5
-OBJS += ../src/crypto/md5-internal.o
-endif
-endif
-
-ifdef NEED_MD4
-ifdef CONFIG_INTERNAL_MD4
-OBJS += ../src/crypto/md4-internal.o
-endif
-endif
-
-ifdef NEED_DES
-ifdef CONFIG_INTERNAL_DES
-OBJS += ../src/crypto/des-internal.o
-endif
-endif
-
-ifdef NEED_RC4
-ifdef CONFIG_INTERNAL_RC4
-OBJS += ../src/crypto/rc4.o
-endif
-endif
-
-ifdef NEED_SHA256
-OBJS += ../src/crypto/sha256.o
-ifdef CONFIG_INTERNAL_SHA256
-OBJS += ../src/crypto/sha256-internal.o
-endif
-endif
-
-ifdef NEED_DH_GROUPS
-OBJS += ../src/crypto/dh_groups.o
-endif
-ifdef NEED_DH_GROUPS_ALL
-CFLAGS += -DALL_DH_GROUPS
-endif
-ifdef CONFIG_INTERNAL_DH_GROUP5
-ifdef NEED_DH_GROUPS
-OBJS += ../src/crypto/dh_group5.o
-endif
-endif
-
-ifdef CONFIG_RADIUS_SERVER
-CFLAGS += -DRADIUS_SERVER
-OBJS += ../src/radius/radius_server.o
-endif
-
-ifdef CONFIG_IPV6
-CFLAGS += -DCONFIG_IPV6
-endif
-
-ifdef CONFIG_DRIVER_RADIUS_ACL
-CFLAGS += -DCONFIG_DRIVER_RADIUS_ACL
-endif
-
-ifdef CONFIG_FULL_DYNAMIC_VLAN
-# define CONFIG_FULL_DYNAMIC_VLAN to have hostapd manipulate bridges
-# and vlan interfaces for the vlan feature.
-CFLAGS += -DCONFIG_FULL_DYNAMIC_VLAN
-endif
-
-ifdef NEED_BASE64
-OBJS += ../src/utils/base64.o
-endif
-
-ifdef NEED_AP_MLME
-OBJS += ../src/ap/beacon.o
-OBJS += ../src/ap/wmm.o
-OBJS += ../src/ap/ap_list.o
-OBJS += ../src/ap/ieee802_11.o
-OBJS += ../src/ap/hw_features.o
-CFLAGS += -DNEED_AP_MLME
-endif
-ifdef CONFIG_IEEE80211N
-OBJS += ../src/ap/ieee802_11_ht.o
-endif
-
-ifdef CONFIG_NO_STDOUT_DEBUG
-CFLAGS += -DCONFIG_NO_STDOUT_DEBUG
-endif
-
-ALL=hostapd hostapd_cli
-
-all: verify_config $(ALL)
-
-Q=@
-E=echo
-ifeq ($(V), 1)
-Q=
-E=true
-endif
-
-%.o: %.c
- $(Q)$(CC) -c -o $@ $(CFLAGS) $<
- @$(E) " CC " $<
-
-verify_config:
- @if [ ! -r .config ]; then \
- echo 'Building hostapd requires a configuration file'; \
- echo '(.config). See README for more instructions. You can'; \
- echo 'run "cp defconfig .config" to create an example'; \
- echo 'configuration.'; \
- exit 1; \
- fi
-
-install: all
- for i in $(ALL); do cp -f $$i /usr/local/bin/$$i; done
-
-../src/drivers/build.hostapd:
- @if [ -f ../src/drivers/build.wpa_supplicant ]; then \
- $(MAKE) -C ../src/drivers clean; \
- fi
- @touch ../src/drivers/build.hostapd
-
-BCHECK=../src/drivers/build.hostapd
-
-hostapd: $(BCHECK) $(OBJS)
- $(CC) $(LDFLAGS) -o hostapd $(OBJS) $(LIBS)
-
-OBJS_c = hostapd_cli.o ../src/common/wpa_ctrl.o ../src/utils/os_$(CONFIG_OS).o
-ifdef CONFIG_WPA_TRACE
-OBJS_c += ../src/utils/trace.o
-OBJS_c += ../src/utils/wpa_debug.o
-endif
-hostapd_cli: $(OBJS_c)
- $(CC) $(LDFLAGS) -o hostapd_cli $(OBJS_c) $(LIBS_c)
-
-NOBJS = nt_password_hash.o ../src/crypto/ms_funcs.o $(SHA1OBJS) ../src/crypto/md5.o
-ifdef NEED_RC4
-ifdef CONFIG_INTERNAL_RC4
-NOBJS += ../src/crypto/rc4.o
-endif
-endif
-ifdef CONFIG_INTERNAL_MD5
-NOBJS += ../src/crypto/md5-internal.o
-endif
-NOBJS += ../src/crypto/crypto_openssl.o ../src/utils/os_$(CONFIG_OS).o
-NOBJS += ../src/utils/wpa_debug.o
-NOBJS += ../src/utils/wpabuf.o
-ifdef CONFIG_WPA_TRACE
-NOBJS += ../src/utils/trace.o
-LIBS_n += -lbfd
-endif
-ifdef TLS_FUNCS
-LIBS_n += -lcrypto
-endif
-
-HOBJS += hlr_auc_gw.o ../src/utils/common.o ../src/utils/wpa_debug.o ../src/utils/os_$(CONFIG_OS).o ../src/utils/wpabuf.o ../src/crypto/milenage.o
-HOBJS += ../src/crypto/aes-encblock.o
-ifdef CONFIG_INTERNAL_AES
-HOBJS += ../src/crypto/aes-internal.o
-HOBJS += ../src/crypto/aes-internal-enc.o
-endif
-
-nt_password_hash: $(NOBJS)
- $(CC) $(LDFLAGS) -o nt_password_hash $(NOBJS) $(LIBS_n)
-
-hlr_auc_gw: $(HOBJS)
- $(CC) $(LDFLAGS) -o hlr_auc_gw $(HOBJS) $(LIBS_h)
-
-clean:
- $(MAKE) -C ../src clean
- rm -f core *~ *.o hostapd hostapd_cli nt_password_hash hlr_auc_gw
- rm -f *.d
-
--include $(OBJS:%.o=%.d)
diff --git a/contrib/wpa/hostapd/README b/contrib/wpa/hostapd/README
index 1af487d..34dad30 100644
--- a/contrib/wpa/hostapd/README
+++ b/contrib/wpa/hostapd/README
@@ -2,37 +2,22 @@ hostapd - user space IEEE 802.11 AP and IEEE 802.1X/WPA/WPA2/EAP
Authenticator and RADIUS authentication server
================================================================
-Copyright (c) 2002-2010, Jouni Malinen <j@w1.fi> and contributors
+Copyright (c) 2002-2012, Jouni Malinen <j@w1.fi> and contributors
All Rights Reserved.
-This program is dual-licensed under both the GPL version 2 and BSD
-license. Either license may be used at your option.
+This program is licensed under the BSD license (the one with
+advertisement clause removed).
+
+If you are submitting changes to the project, please see CONTRIBUTIONS
+file for more instructions.
License
-------
-GPL v2:
-
-This program is free software; you can redistribute it and/or modify
-it under the terms of the GNU General Public License version 2 as
-published by the Free Software Foundation.
-
-This program is distributed in the hope that it will be useful,
-but WITHOUT ANY WARRANTY; without even the implied warranty of
-MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the
-GNU General Public License for more details.
-
-You should have received a copy of the GNU General Public License
-along with this program; if not, write to the Free Software
-Foundation, Inc., 51 Franklin St, Fifth Floor, Boston, MA 02110-1301 USA
-
-(this copy of the license is in COPYING file)
-
-
-Alternatively, this software may be distributed, used, and modified
-under the terms of BSD license:
+This software may be distributed, used, and modified under the terms of
+BSD license:
Redistribution and use in source and binary forms, with or without
modification, are permitted provided that the following conditions are
diff --git a/contrib/wpa/hostapd/README-WPS b/contrib/wpa/hostapd/README-WPS
index 74f2113..87a6f91 100644
--- a/contrib/wpa/hostapd/README-WPS
+++ b/contrib/wpa/hostapd/README-WPS
@@ -63,8 +63,13 @@ includes WPS support and uses madwifi driver interface:
CONFIG_DRIVER_MADWIFI=y
CFLAGS += -I/usr/src/madwifi-0.9.3
CONFIG_WPS=y
+CONFIG_WPS2=y
CONFIG_WPS_UPNP=y
+Following parameter can be used to enable support for NFC config method:
+
+CONFIG_WPS_NFC=y
+
Following section shows an example runtime configuration
(hostapd.conf) that enables WPS:
@@ -119,6 +124,13 @@ pushbutton event (for PBC) to allow a new WPS Enrollee to join the
network. hostapd uses the control interface as an input channel for
these events.
+The PIN value used in the commands must be processed by an UI to
+remove non-digit characters and potentially, to verify the checksum
+digit. "hostapd_cli wps_check_pin <PIN>" can be used to do such
+processing. It returns FAIL if the PIN is invalid, or FAIL-CHECKSUM if
+the checksum digit is incorrect, or the processed PIN (non-digit
+characters removed) if the PIN is valid.
+
When a client device (WPS Enrollee) connects to hostapd (WPS
Registrar) in order to start PIN mode negotiation for WPS, an
identifier (Enrollee UUID) is sent. hostapd will need to be configured
@@ -171,10 +183,17 @@ hostapd_cli wps_pin any 12345670
To reduce likelihood of PIN being used with other devices or of
forgetting an active PIN available for potential attackers, expiration
-time can be set for the new PIN:
+time in seconds can be set for the new PIN (value 0 indicates no
+expiration):
hostapd_cli wps_pin any 12345670 300
+If the MAC address of the enrollee is known, it should be configured
+to allow the AP to advertise list of authorized enrollees:
+
+hostapd_cli wps_pin 53b63a98-d29e-4457-a2ed-094d7e6a669c \
+ 12345670 300 00:11:22:33:44:55
+
After this, the Enrollee can connect to the AP again and complete WPS
negotiation. At that point, a new, random WPA PSK is generated for the
@@ -221,6 +240,17 @@ hostapd_cli wps_ap_pin set <PIN> [timeout]
- if the optional timeout parameter is given, the AP PIN will be enabled
for the specified number of seconds
+hostapd_cli get_config
+- display the current configuration
+
+hostapd_cli wps_config <new SSID> <auth> <encr> <new key>
+examples:
+ hostapd_cli wps_config testing WPA2PSK CCMP 12345678
+ hostapd_cli wps_config "no security" OPEN NONE ""
+
+<auth> must be one of the following: OPEN WPAPSK WPA2PSK
+<encr> must be one of the following: NONE WEP TKIP CCMP
+
Credential generation and configuration changes
-----------------------------------------------
@@ -251,7 +281,7 @@ WPS-REG-SUCCESS <Enrollee MAC address <UUID-E>
For example:
<2>WPS-REG-SUCCESS 02:66:a0:ee:17:27 2b7093f1-d6fb-5108-adbb-bea66bb87333
-This can be used to tricker change from unconfigured to configured
+This can be used to trigger change from unconfigured to configured
state (random configuration based on the first successful WPS
registration). In addition, this can be used to update AP UI about the
status of WPS registration progress.
@@ -263,3 +293,48 @@ For example:
This can be used to update the externally stored AP configuration and
then update hostapd configuration (followed by restarting of hostapd).
+
+
+WPS with NFC
+------------
+
+WPS can be used with NFC-based configuration method. An NFC tag
+containing a password token from the Enrollee can be used to
+authenticate the connection instead of the PIN. In addition, an NFC tag
+with a configuration token can be used to transfer AP settings without
+going through the WPS protocol.
+
+When the AP acts as an Enrollee, a local NFC tag with a password token
+can be used by touching the NFC interface of an external Registrar. The
+wps_nfc_token command is used to manage use of the NFC password token
+from the AP. "wps_nfc_token enable" enables the use of the AP's NFC
+password token (in place of AP PIN) and "wps_nfc_token disable" disables
+the NFC password token.
+
+The NFC password token that is either pre-configured in the
+configuration file (wps_nfc_dev_pw_id, wps_nfc_dh_pubkey,
+wps_nfc_dh_privkey, wps_nfc_dev_pw) or generated dynamically with
+"wps_nfc_token <WPS|NDEF>" command. The nfc_pw_token tool from
+wpa_supplicant can be used to generate NFC password tokens during
+manufacturing (each AP needs to have its own random keys).
+
+The "wps_nfc_config_token <WPS/NDEF>" command can be used to build an
+NFC configuration token. The output value from this command is a hexdump
+of the current AP configuration (WPS parameter requests this to include
+only the WPS attributes; NDEF parameter requests additional NDEF
+encapsulation to be included). This data needs to be written to an NFC
+tag with an external program. Once written, the NFC configuration token
+can be used to touch an NFC interface on a station to provision the
+credentials needed to access the network.
+
+When the NFC device on the AP reads an NFC tag with a MIME media type
+"application/vnd.wfa.wsc", the NDEF message payload (with or without
+NDEF encapsulation) can be delivered to hostapd using the
+following hostapd_cli command:
+
+wps_nfc_tag_read <hexdump of payload>
+
+If the NFC tag contains a password token, the token is added to the
+internal Registrar. This allows station Enrollee from which the password
+token was received to run through WPS protocol to provision the
+credential.
diff --git a/contrib/wpa/hostapd/config_file.c b/contrib/wpa/hostapd/config_file.c
index 8916b03..2ba7cc1 100644
--- a/contrib/wpa/hostapd/config_file.c
+++ b/contrib/wpa/hostapd/config_file.c
@@ -1,15 +1,9 @@
/*
* hostapd / Configuration file parser
- * Copyright (c) 2003-2009, Jouni Malinen <j@w1.fi>
+ * Copyright (c) 2003-2012, Jouni Malinen <j@w1.fi>
*
- * This program is free software; you can redistribute it and/or modify
- * it under the terms of the GNU General Public License version 2 as
- * published by the Free Software Foundation.
- *
- * Alternatively, this software may be distributed under the terms of BSD
- * license.
- *
- * See README and COPYING for more details.
+ * This software may be distributed under the terms of the BSD license.
+ * See README for more details.
*/
#include "utils/includes.h"
@@ -173,7 +167,7 @@ static int hostapd_config_read_maclist(const char *fname,
if (*pos != '\0')
vlan_id = atoi(pos);
- newacl = os_realloc(*acl, (*num + 1) * sizeof(**acl));
+ newacl = os_realloc_array(*acl, *num + 1, sizeof(**acl));
if (newacl == NULL) {
wpa_printf(MSG_ERROR, "MAC list reallocation failed");
fclose(f);
@@ -206,6 +200,12 @@ static int hostapd_config_read_eap_user(const char *fname,
if (!fname)
return 0;
+ if (os_strncmp(fname, "sqlite:", 7) == 0) {
+ os_free(conf->eap_user_sqlite);
+ conf->eap_user_sqlite = os_strdup(fname + 7);
+ return 0;
+ }
+
f = fopen(fname, "r");
if (!f) {
wpa_printf(MSG_ERROR, "EAP user file '%s' not found.", fname);
@@ -330,7 +330,7 @@ static int hostapd_config_read_eap_user(const char *fname,
}
num_methods++;
- if (num_methods >= EAP_USER_MAX_METHODS)
+ if (num_methods >= EAP_MAX_METHODS)
break;
skip_eap:
if (pos3 == NULL)
@@ -481,7 +481,7 @@ hostapd_config_read_radius_addr(struct hostapd_radius_server **server,
int ret;
static int server_index = 1;
- nserv = os_realloc(*server, (*num_server + 1) * sizeof(*nserv));
+ nserv = os_realloc_array(*server, *num_server + 1, sizeof(*nserv));
if (nserv == NULL)
return -1;
@@ -497,6 +497,100 @@ hostapd_config_read_radius_addr(struct hostapd_radius_server **server,
return ret;
}
+
+
+static struct hostapd_radius_attr *
+hostapd_parse_radius_attr(const char *value)
+{
+ const char *pos;
+ char syntax;
+ struct hostapd_radius_attr *attr;
+ size_t len;
+
+ attr = os_zalloc(sizeof(*attr));
+ if (attr == NULL)
+ return NULL;
+
+ attr->type = atoi(value);
+
+ pos = os_strchr(value, ':');
+ if (pos == NULL) {
+ attr->val = wpabuf_alloc(1);
+ if (attr->val == NULL) {
+ os_free(attr);
+ return NULL;
+ }
+ wpabuf_put_u8(attr->val, 0);
+ return attr;
+ }
+
+ pos++;
+ if (pos[0] == '\0' || pos[1] != ':') {
+ os_free(attr);
+ return NULL;
+ }
+ syntax = *pos++;
+ pos++;
+
+ switch (syntax) {
+ case 's':
+ attr->val = wpabuf_alloc_copy(pos, os_strlen(pos));
+ break;
+ case 'x':
+ len = os_strlen(pos);
+ if (len & 1)
+ break;
+ len /= 2;
+ attr->val = wpabuf_alloc(len);
+ if (attr->val == NULL)
+ break;
+ if (hexstr2bin(pos, wpabuf_put(attr->val, len), len) < 0) {
+ wpabuf_free(attr->val);
+ os_free(attr);
+ return NULL;
+ }
+ break;
+ case 'd':
+ attr->val = wpabuf_alloc(4);
+ if (attr->val)
+ wpabuf_put_be32(attr->val, atoi(pos));
+ break;
+ default:
+ os_free(attr);
+ return NULL;
+ }
+
+ if (attr->val == NULL) {
+ os_free(attr);
+ return NULL;
+ }
+
+ return attr;
+}
+
+
+static int hostapd_parse_das_client(struct hostapd_bss_config *bss,
+ const char *val)
+{
+ char *secret;
+
+ secret = os_strchr(val, ' ');
+ if (secret == NULL)
+ return -1;
+
+ secret++;
+
+ if (hostapd_parse_ip_addr(val, &bss->radius_das_client_addr))
+ return -1;
+
+ os_free(bss->radius_das_shared_secret);
+ bss->radius_das_shared_secret = (u8 *) os_strdup(secret);
+ if (bss->radius_das_shared_secret == NULL)
+ return -1;
+ bss->radius_das_shared_secret_len = os_strlen(secret);
+
+ return 0;
+}
#endif /* CONFIG_NO_RADIUS */
@@ -536,6 +630,12 @@ static int hostapd_config_parse_key_mgmt(int line, const char *value)
else if (os_strcmp(start, "WPA-EAP-SHA256") == 0)
val |= WPA_KEY_MGMT_IEEE8021X_SHA256;
#endif /* CONFIG_IEEE80211W */
+#ifdef CONFIG_SAE
+ else if (os_strcmp(start, "SAE") == 0)
+ val |= WPA_KEY_MGMT_SAE;
+ else if (os_strcmp(start, "FT-SAE") == 0)
+ val |= WPA_KEY_MGMT_FT_SAE;
+#endif /* CONFIG_SAE */
else {
wpa_printf(MSG_ERROR, "Line %d: invalid key_mgmt '%s'",
line, start);
@@ -581,6 +681,8 @@ static int hostapd_config_parse_cipher(int line, const char *value)
*end = '\0';
if (os_strcmp(start, "CCMP") == 0)
val |= WPA_CIPHER_CCMP;
+ else if (os_strcmp(start, "GCMP") == 0)
+ val |= WPA_CIPHER_GCMP;
else if (os_strcmp(start, "TKIP") == 0)
val |= WPA_CIPHER_TKIP;
else if (os_strcmp(start, "WEP104") == 0)
@@ -692,8 +794,8 @@ static int hostapd_config_bss(struct hostapd_config *conf, const char *ifname)
if (*ifname == '\0')
return -1;
- bss = os_realloc(conf->bss, (conf->num_bss + 1) *
- sizeof(struct hostapd_bss_config));
+ bss = os_realloc_array(conf->bss, conf->num_bss + 1,
+ sizeof(struct hostapd_bss_config));
if (bss == NULL) {
wpa_printf(MSG_ERROR, "Failed to allocate memory for "
"multi-BSS entry");
@@ -752,10 +854,7 @@ enum {
IEEE80211_TX_QUEUE_DATA0 = 0, /* used for EDCA AC_VO data */
IEEE80211_TX_QUEUE_DATA1 = 1, /* used for EDCA AC_VI data */
IEEE80211_TX_QUEUE_DATA2 = 2, /* used for EDCA AC_BE data */
- IEEE80211_TX_QUEUE_DATA3 = 3, /* used for EDCA AC_BK data */
- IEEE80211_TX_QUEUE_DATA4 = 4,
- IEEE80211_TX_QUEUE_AFTER_BEACON = 6,
- IEEE80211_TX_QUEUE_BEACON = 7
+ IEEE80211_TX_QUEUE_DATA3 = 3 /* used for EDCA AC_BK data */
};
static int hostapd_config_tx_queue(struct hostapd_config *conf, char *name,
@@ -771,17 +870,21 @@ static int hostapd_config_tx_queue(struct hostapd_config *conf, char *name,
pos[4] >= '0' && pos[4] <= '9' && pos[5] == '_') {
num = pos[4] - '0';
pos += 6;
- } else if (os_strncmp(pos, "after_beacon_", 13) == 0) {
- num = IEEE80211_TX_QUEUE_AFTER_BEACON;
- pos += 13;
- } else if (os_strncmp(pos, "beacon_", 7) == 0) {
- num = IEEE80211_TX_QUEUE_BEACON;
- pos += 7;
+ } else if (os_strncmp(pos, "after_beacon_", 13) == 0 ||
+ os_strncmp(pos, "beacon_", 7) == 0) {
+ wpa_printf(MSG_INFO, "DEPRECATED: '%s' not used", name);
+ return 0;
} else {
wpa_printf(MSG_ERROR, "Unknown tx_queue name '%s'", pos);
return -1;
}
+ if (num >= NUM_TX_QUEUES) {
+ /* for backwards compatibility, do not trigger failure */
+ wpa_printf(MSG_INFO, "DEPRECATED: '%s' not used", name);
+ return 0;
+ }
+
queue = &conf->tx_queue[num];
if (os_strcmp(pos, "aifs") == 0) {
@@ -812,80 +915,6 @@ static int hostapd_config_tx_queue(struct hostapd_config *conf, char *name,
return -1;
}
- queue->configured = 1;
-
- return 0;
-}
-
-
-static int hostapd_config_wmm_ac(struct hostapd_config *conf, char *name,
- char *val)
-{
- int num, v;
- char *pos;
- struct hostapd_wmm_ac_params *ac;
-
- /* skip 'wme_ac_' or 'wmm_ac_' prefix */
- pos = name + 7;
- if (os_strncmp(pos, "be_", 3) == 0) {
- num = 0;
- pos += 3;
- } else if (os_strncmp(pos, "bk_", 3) == 0) {
- num = 1;
- pos += 3;
- } else if (os_strncmp(pos, "vi_", 3) == 0) {
- num = 2;
- pos += 3;
- } else if (os_strncmp(pos, "vo_", 3) == 0) {
- num = 3;
- pos += 3;
- } else {
- wpa_printf(MSG_ERROR, "Unknown WMM name '%s'", pos);
- return -1;
- }
-
- ac = &conf->wmm_ac_params[num];
-
- if (os_strcmp(pos, "aifs") == 0) {
- v = atoi(val);
- if (v < 1 || v > 255) {
- wpa_printf(MSG_ERROR, "Invalid AIFS value %d", v);
- return -1;
- }
- ac->aifs = v;
- } else if (os_strcmp(pos, "cwmin") == 0) {
- v = atoi(val);
- if (v < 0 || v > 12) {
- wpa_printf(MSG_ERROR, "Invalid cwMin value %d", v);
- return -1;
- }
- ac->cwmin = v;
- } else if (os_strcmp(pos, "cwmax") == 0) {
- v = atoi(val);
- if (v < 0 || v > 12) {
- wpa_printf(MSG_ERROR, "Invalid cwMax value %d", v);
- return -1;
- }
- ac->cwmax = v;
- } else if (os_strcmp(pos, "txop_limit") == 0) {
- v = atoi(val);
- if (v < 0 || v > 0xffff) {
- wpa_printf(MSG_ERROR, "Invalid txop value %d", v);
- return -1;
- }
- ac->txop_limit = v;
- } else if (os_strcmp(pos, "acm") == 0) {
- v = atoi(val);
- if (v < 0 || v > 1) {
- wpa_printf(MSG_ERROR, "Invalid acm value %d", v);
- return -1;
- }
- ac->admission_control_mandatory = v;
- } else {
- wpa_printf(MSG_ERROR, "Unknown wmm_ac_ field '%s'", pos);
- return -1;
- }
-
return 0;
}
@@ -1041,6 +1070,71 @@ static int hostapd_config_ht_capab(struct hostapd_config *conf,
#endif /* CONFIG_IEEE80211N */
+#ifdef CONFIG_IEEE80211AC
+static int hostapd_config_vht_capab(struct hostapd_config *conf,
+ const char *capab)
+{
+ if (os_strstr(capab, "[MAX-MPDU-7991]"))
+ conf->vht_capab |= VHT_CAP_MAX_MPDU_LENGTH_7991;
+ if (os_strstr(capab, "[MAX-MPDU-11454]"))
+ conf->vht_capab |= VHT_CAP_MAX_MPDU_LENGTH_11454;
+ if (os_strstr(capab, "[VHT160]"))
+ conf->vht_capab |= VHT_CAP_SUPP_CHAN_WIDTH_160MHZ;
+ if (os_strstr(capab, "[VHT160-80PLUS80]"))
+ conf->vht_capab |= VHT_CAP_SUPP_CHAN_WIDTH_160_80PLUS80MHZ;
+ if (os_strstr(capab, "[VHT160-80PLUS80]"))
+ conf->vht_capab |= VHT_CAP_SUPP_CHAN_WIDTH_160_80PLUS80MHZ;
+ if (os_strstr(capab, "[RXLDPC]"))
+ conf->vht_capab |= VHT_CAP_RXLDPC;
+ if (os_strstr(capab, "[SHORT-GI-80]"))
+ conf->vht_capab |= VHT_CAP_SHORT_GI_80;
+ if (os_strstr(capab, "[SHORT-GI-160]"))
+ conf->vht_capab |= VHT_CAP_SHORT_GI_160;
+ if (os_strstr(capab, "[TX-STBC-2BY1]"))
+ conf->vht_capab |= VHT_CAP_TXSTBC;
+ if (os_strstr(capab, "[RX-STBC-1]"))
+ conf->vht_capab |= VHT_CAP_RXSTBC_1;
+ if (os_strstr(capab, "[RX-STBC-12]"))
+ conf->vht_capab |= VHT_CAP_RXSTBC_2;
+ if (os_strstr(capab, "[RX-STBC-123]"))
+ conf->vht_capab |= VHT_CAP_RXSTBC_3;
+ if (os_strstr(capab, "[RX-STBC-1234]"))
+ conf->vht_capab |= VHT_CAP_RXSTBC_4;
+ if (os_strstr(capab, "[SU-BEAMFORMER]"))
+ conf->vht_capab |= VHT_CAP_MU_BEAMFORMER_CAPABLE;
+ if (os_strstr(capab, "[SU-BEAMFORMEE]"))
+ conf->vht_capab |= VHT_CAP_MU_BEAMFORMEE_CAPABLE;
+ if (os_strstr(capab, "[BF-ANTENNA-2]") &&
+ (conf->vht_capab & VHT_CAP_MU_BEAMFORMER_CAPABLE))
+ conf->vht_capab |= VHT_CAP_BEAMFORMER_ANTENNAS_MAX;
+ if (os_strstr(capab, "[SOUNDING-DIMENSION-2]") &&
+ (conf->vht_capab & VHT_CAP_MU_BEAMFORMER_CAPABLE))
+ conf->vht_capab |= VHT_CAP_SOUNDING_DIMENTION_MAX;
+ if (os_strstr(capab, "[MU-BEAMFORMER]"))
+ conf->vht_capab |= VHT_CAP_MU_BEAMFORMER_CAPABLE;
+ if (os_strstr(capab, "[MU-BEAMFORMEE]"))
+ conf->vht_capab |= VHT_CAP_MU_BEAMFORMEE_CAPABLE;
+ if (os_strstr(capab, "[VHT-TXOP-PS]"))
+ conf->vht_capab |= VHT_CAP_VHT_TXOP_PS;
+ if (os_strstr(capab, "[HTC-VHT]"))
+ conf->vht_capab |= VHT_CAP_HTC_VHT;
+ if (os_strstr(capab, "[MAX-A-MPDU-LEN-EXP0]"))
+ conf->vht_capab |= VHT_CAP_MAX_A_MPDU_LENGTH_EXPONENT;
+ if (os_strstr(capab, "[VHT-LINK-ADAPT2]") &&
+ (conf->vht_capab & VHT_CAP_HTC_VHT))
+ conf->vht_capab |= VHT_CAP_VHT_LINK_ADAPTATION_VHT_UNSOL_MFB;
+ if (os_strstr(capab, "[VHT-LINK-ADAPT3]") &&
+ (conf->vht_capab & VHT_CAP_HTC_VHT))
+ conf->vht_capab |= VHT_CAP_VHT_LINK_ADAPTATION_VHT_MRQ_MFB;
+ if (os_strstr(capab, "[RX-ANTENNA-PATTERN]"))
+ conf->vht_capab |= VHT_CAP_RX_ANTENNA_PATTERN;
+ if (os_strstr(capab, "[TX-ANTENNA-PATTERN]"))
+ conf->vht_capab |= VHT_CAP_TX_ANTENNA_PATTERN;
+ return 0;
+}
+#endif /* CONFIG_IEEE80211AC */
+
+
static int hostapd_config_check_bss(struct hostapd_bss_config *bss,
struct hostapd_config *conf)
{
@@ -1051,9 +1145,18 @@ static int hostapd_config_check_bss(struct hostapd_bss_config *bss,
return -1;
}
+ if (bss->wpa && bss->wpa_psk_radius != PSK_RADIUS_IGNORED &&
+ bss->macaddr_acl != USE_EXTERNAL_RADIUS_AUTH) {
+ wpa_printf(MSG_ERROR, "WPA-PSK using RADIUS enabled, but no "
+ "RADIUS checking (macaddr_acl=2) enabled.");
+ return -1;
+ }
+
if (bss->wpa && (bss->wpa_key_mgmt & WPA_KEY_MGMT_PSK) &&
bss->ssid.wpa_psk == NULL && bss->ssid.wpa_passphrase == NULL &&
- bss->ssid.wpa_psk_file == NULL) {
+ bss->ssid.wpa_psk_file == NULL &&
+ (bss->wpa_psk_radius != PSK_RADIUS_REQUIRED ||
+ bss->macaddr_acl != USE_EXTERNAL_RADIUS_AUTH)) {
wpa_printf(MSG_ERROR, "WPA-PSK enabled, but PSK or passphrase "
"is not configured.");
return -1;
@@ -1076,8 +1179,7 @@ static int hostapd_config_check_bss(struct hostapd_bss_config *bss,
}
#ifdef CONFIG_IEEE80211R
- if ((bss->wpa_key_mgmt &
- (WPA_KEY_MGMT_FT_PSK | WPA_KEY_MGMT_FT_IEEE8021X)) &&
+ if (wpa_key_mgmt_ft(bss->wpa_key_mgmt) &&
(bss->nas_identifier == NULL ||
os_strlen(bss->nas_identifier) < 1 ||
os_strlen(bss->nas_identifier) > FT_R0KH_ID_MAX_LEN)) {
@@ -1089,15 +1191,62 @@ static int hostapd_config_check_bss(struct hostapd_bss_config *bss,
#endif /* CONFIG_IEEE80211R */
#ifdef CONFIG_IEEE80211N
+ if (conf->ieee80211n && conf->hw_mode == HOSTAPD_MODE_IEEE80211B) {
+ bss->disable_11n = 1;
+ wpa_printf(MSG_ERROR, "HT (IEEE 802.11n) in 11b mode is not "
+ "allowed, disabling HT capabilites");
+ }
+
+ if (conf->ieee80211n &&
+ bss->ssid.security_policy == SECURITY_STATIC_WEP) {
+ bss->disable_11n = 1;
+ wpa_printf(MSG_ERROR, "HT (IEEE 802.11n) with WEP is not "
+ "allowed, disabling HT capabilities");
+ }
+
if (conf->ieee80211n && bss->wpa &&
!(bss->wpa_pairwise & WPA_CIPHER_CCMP) &&
- !(bss->rsn_pairwise & WPA_CIPHER_CCMP)) {
+ !(bss->rsn_pairwise & (WPA_CIPHER_CCMP | WPA_CIPHER_GCMP))) {
+ bss->disable_11n = 1;
wpa_printf(MSG_ERROR, "HT (IEEE 802.11n) with WPA/WPA2 "
- "requires CCMP to be enabled");
- return -1;
+ "requires CCMP/GCMP to be enabled, disabling HT "
+ "capabilities");
}
#endif /* CONFIG_IEEE80211N */
+#ifdef CONFIG_WPS2
+ if (bss->wps_state && bss->ignore_broadcast_ssid) {
+ wpa_printf(MSG_INFO, "WPS: ignore_broadcast_ssid "
+ "configuration forced WPS to be disabled");
+ bss->wps_state = 0;
+ }
+
+ if (bss->wps_state && bss->ssid.wep.keys_set && bss->wpa == 0) {
+ wpa_printf(MSG_INFO, "WPS: WEP configuration forced WPS to be "
+ "disabled");
+ bss->wps_state = 0;
+ }
+
+ if (bss->wps_state && bss->wpa &&
+ (!(bss->wpa & 2) ||
+ !(bss->rsn_pairwise & WPA_CIPHER_CCMP))) {
+ wpa_printf(MSG_INFO, "WPS: WPA/TKIP configuration without "
+ "WPA2/CCMP forced WPS to be disabled");
+ bss->wps_state = 0;
+ }
+#endif /* CONFIG_WPS2 */
+
+#ifdef CONFIG_HS20
+ if (bss->hs20 &&
+ (!(bss->wpa & 2) ||
+ !(bss->rsn_pairwise & (WPA_CIPHER_CCMP | WPA_CIPHER_GCMP)))) {
+ wpa_printf(MSG_ERROR, "HS 2.0: WPA2-Enterprise/CCMP "
+ "configuration is required for Hotspot 2.0 "
+ "functionality");
+ return -1;
+ }
+#endif /* CONFIG_HS20 */
+
return 0;
}
@@ -1121,78 +1270,460 @@ static int hostapd_config_check(struct hostapd_config *conf)
}
-/**
- * hostapd_config_read - Read and parse a configuration file
- * @fname: Configuration file name (including path, if needed)
- * Returns: Allocated configuration data structure
- */
-struct hostapd_config * hostapd_config_read(const char *fname)
+#ifdef CONFIG_INTERWORKING
+static int parse_roaming_consortium(struct hostapd_bss_config *bss, char *pos,
+ int line)
{
- struct hostapd_config *conf;
- struct hostapd_bss_config *bss;
- FILE *f;
- char buf[256], *pos;
- int line = 0;
- int errors = 0;
- int pairwise;
- size_t i;
+ size_t len = os_strlen(pos);
+ u8 oi[MAX_ROAMING_CONSORTIUM_LEN];
- f = fopen(fname, "r");
- if (f == NULL) {
- wpa_printf(MSG_ERROR, "Could not open configuration file '%s' "
- "for reading.", fname);
- return NULL;
+ struct hostapd_roaming_consortium *rc;
+
+ if ((len & 1) || len < 2 * 3 || len / 2 > MAX_ROAMING_CONSORTIUM_LEN ||
+ hexstr2bin(pos, oi, len / 2)) {
+ wpa_printf(MSG_ERROR, "Line %d: invalid roaming_consortium "
+ "'%s'", line, pos);
+ return -1;
}
+ len /= 2;
- conf = hostapd_config_defaults();
- if (conf == NULL) {
- fclose(f);
- return NULL;
+ rc = os_realloc_array(bss->roaming_consortium,
+ bss->roaming_consortium_count + 1,
+ sizeof(struct hostapd_roaming_consortium));
+ if (rc == NULL)
+ return -1;
+
+ os_memcpy(rc[bss->roaming_consortium_count].oi, oi, len);
+ rc[bss->roaming_consortium_count].len = len;
+
+ bss->roaming_consortium = rc;
+ bss->roaming_consortium_count++;
+
+ return 0;
+}
+
+
+static int parse_lang_string(struct hostapd_lang_string **array,
+ unsigned int *count, char *pos)
+{
+ char *sep;
+ size_t clen, nlen;
+ struct hostapd_lang_string *ls;
+
+ sep = os_strchr(pos, ':');
+ if (sep == NULL)
+ return -1;
+ *sep++ = '\0';
+
+ clen = os_strlen(pos);
+ if (clen < 2)
+ return -1;
+ nlen = os_strlen(sep);
+ if (nlen > 252)
+ return -1;
+
+ ls = os_realloc_array(*array, *count + 1,
+ sizeof(struct hostapd_lang_string));
+ if (ls == NULL)
+ return -1;
+
+ *array = ls;
+ ls = &(*array)[*count];
+ (*count)++;
+
+ os_memset(ls->lang, 0, sizeof(ls->lang));
+ os_memcpy(ls->lang, pos, clen);
+ ls->name_len = nlen;
+ os_memcpy(ls->name, sep, nlen);
+
+ return 0;
+}
+
+
+static int parse_venue_name(struct hostapd_bss_config *bss, char *pos,
+ int line)
+{
+ if (parse_lang_string(&bss->venue_name, &bss->venue_name_count, pos)) {
+ wpa_printf(MSG_ERROR, "Line %d: Invalid venue_name '%s'",
+ line, pos);
+ return -1;
}
+ return 0;
+}
- /* set default driver based on configuration */
- conf->driver = wpa_drivers[0];
- if (conf->driver == NULL) {
- wpa_printf(MSG_ERROR, "No driver wrappers registered!");
- hostapd_config_free(conf);
- fclose(f);
- return NULL;
+
+static int parse_3gpp_cell_net(struct hostapd_bss_config *bss, char *buf,
+ int line)
+{
+ size_t count;
+ char *pos;
+ u8 *info = NULL, *ipos;
+
+ /* format: <MCC1,MNC1>[;<MCC2,MNC2>][;...] */
+
+ count = 1;
+ for (pos = buf; *pos; pos++) {
+ if ((*pos < '0' && *pos > '9') && *pos != ';' && *pos != ',')
+ goto fail;
+ if (*pos == ';')
+ count++;
}
+ if (1 + count * 3 > 0x7f)
+ goto fail;
- bss = conf->last_bss = conf->bss;
+ info = os_zalloc(2 + 3 + count * 3);
+ if (info == NULL)
+ return -1;
- while (fgets(buf, sizeof(buf), f)) {
- bss = conf->last_bss;
- line++;
+ ipos = info;
+ *ipos++ = 0; /* GUD - Version 1 */
+ *ipos++ = 3 + count * 3; /* User Data Header Length (UDHL) */
+ *ipos++ = 0; /* PLMN List IEI */
+ /* ext(b8) | Length of PLMN List value contents(b7..1) */
+ *ipos++ = 1 + count * 3;
+ *ipos++ = count; /* Number of PLMNs */
+
+ pos = buf;
+ while (pos && *pos) {
+ char *mcc, *mnc;
+ size_t mnc_len;
+
+ mcc = pos;
+ mnc = os_strchr(pos, ',');
+ if (mnc == NULL)
+ goto fail;
+ *mnc++ = '\0';
+ pos = os_strchr(mnc, ';');
+ if (pos)
+ *pos++ = '\0';
+
+ mnc_len = os_strlen(mnc);
+ if (os_strlen(mcc) != 3 || (mnc_len != 2 && mnc_len != 3))
+ goto fail;
+
+ /* BC coded MCC,MNC */
+ /* MCC digit 2 | MCC digit 1 */
+ *ipos++ = ((mcc[1] - '0') << 4) | (mcc[0] - '0');
+ /* MNC digit 3 | MCC digit 3 */
+ *ipos++ = (((mnc_len == 2) ? 0xf0 : ((mnc[2] - '0') << 4))) |
+ (mcc[2] - '0');
+ /* MNC digit 2 | MNC digit 1 */
+ *ipos++ = ((mnc[1] - '0') << 4) | (mnc[0] - '0');
+ }
- if (buf[0] == '#')
- continue;
- pos = buf;
- while (*pos != '\0') {
- if (*pos == '\n') {
- *pos = '\0';
+ os_free(bss->anqp_3gpp_cell_net);
+ bss->anqp_3gpp_cell_net = info;
+ bss->anqp_3gpp_cell_net_len = 2 + 3 + 3 * count;
+ wpa_hexdump(MSG_MSGDUMP, "3GPP Cellular Network information",
+ bss->anqp_3gpp_cell_net, bss->anqp_3gpp_cell_net_len);
+
+ return 0;
+
+fail:
+ wpa_printf(MSG_ERROR, "Line %d: Invalid anqp_3gpp_cell_net: %s",
+ line, buf);
+ os_free(info);
+ return -1;
+}
+
+
+static int parse_nai_realm(struct hostapd_bss_config *bss, char *buf, int line)
+{
+ struct hostapd_nai_realm_data *realm;
+ size_t i, j, len;
+ int *offsets;
+ char *pos, *end, *rpos;
+
+ offsets = os_calloc(bss->nai_realm_count * MAX_NAI_REALMS,
+ sizeof(int));
+ if (offsets == NULL)
+ return -1;
+
+ for (i = 0; i < bss->nai_realm_count; i++) {
+ realm = &bss->nai_realm_data[i];
+ for (j = 0; j < MAX_NAI_REALMS; j++) {
+ offsets[i * MAX_NAI_REALMS + j] =
+ realm->realm[j] ?
+ realm->realm[j] - realm->realm_buf : -1;
+ }
+ }
+
+ realm = os_realloc_array(bss->nai_realm_data, bss->nai_realm_count + 1,
+ sizeof(struct hostapd_nai_realm_data));
+ if (realm == NULL) {
+ os_free(offsets);
+ return -1;
+ }
+ bss->nai_realm_data = realm;
+
+ /* patch the pointers after realloc */
+ for (i = 0; i < bss->nai_realm_count; i++) {
+ realm = &bss->nai_realm_data[i];
+ for (j = 0; j < MAX_NAI_REALMS; j++) {
+ int offs = offsets[i * MAX_NAI_REALMS + j];
+ if (offs >= 0)
+ realm->realm[j] = realm->realm_buf + offs;
+ else
+ realm->realm[j] = NULL;
+ }
+ }
+ os_free(offsets);
+
+ realm = &bss->nai_realm_data[bss->nai_realm_count];
+ os_memset(realm, 0, sizeof(*realm));
+
+ pos = buf;
+ realm->encoding = atoi(pos);
+ pos = os_strchr(pos, ',');
+ if (pos == NULL)
+ goto fail;
+ pos++;
+
+ end = os_strchr(pos, ',');
+ if (end) {
+ len = end - pos;
+ *end = '\0';
+ } else {
+ len = os_strlen(pos);
+ }
+
+ if (len > MAX_NAI_REALMLEN) {
+ wpa_printf(MSG_ERROR, "Too long a realm string (%d > max %d "
+ "characters)", (int) len, MAX_NAI_REALMLEN);
+ goto fail;
+ }
+ os_memcpy(realm->realm_buf, pos, len);
+
+ if (end)
+ pos = end + 1;
+ else
+ pos = NULL;
+
+ while (pos && *pos) {
+ struct hostapd_nai_realm_eap *eap;
+
+ if (realm->eap_method_count >= MAX_NAI_EAP_METHODS) {
+ wpa_printf(MSG_ERROR, "Too many EAP methods");
+ goto fail;
+ }
+
+ eap = &realm->eap_method[realm->eap_method_count];
+ realm->eap_method_count++;
+
+ end = os_strchr(pos, ',');
+ if (end == NULL)
+ end = pos + os_strlen(pos);
+
+ eap->eap_method = atoi(pos);
+ for (;;) {
+ pos = os_strchr(pos, '[');
+ if (pos == NULL || pos > end)
break;
- }
pos++;
+ if (eap->num_auths >= MAX_NAI_AUTH_TYPES) {
+ wpa_printf(MSG_ERROR, "Too many auth params");
+ goto fail;
+ }
+ eap->auth_id[eap->num_auths] = atoi(pos);
+ pos = os_strchr(pos, ':');
+ if (pos == NULL || pos > end)
+ goto fail;
+ pos++;
+ eap->auth_val[eap->num_auths] = atoi(pos);
+ pos = os_strchr(pos, ']');
+ if (pos == NULL || pos > end)
+ goto fail;
+ pos++;
+ eap->num_auths++;
}
- if (buf[0] == '\0')
- continue;
- pos = os_strchr(buf, '=');
- if (pos == NULL) {
- wpa_printf(MSG_ERROR, "Line %d: invalid line '%s'",
- line, buf);
- errors++;
- continue;
+ if (*end != ',')
+ break;
+
+ pos = end + 1;
+ }
+
+ /* Split realm list into null terminated realms */
+ rpos = realm->realm_buf;
+ i = 0;
+ while (*rpos) {
+ if (i >= MAX_NAI_REALMS) {
+ wpa_printf(MSG_ERROR, "Too many realms");
+ goto fail;
}
- *pos = '\0';
- pos++;
+ realm->realm[i++] = rpos;
+ rpos = os_strchr(rpos, ';');
+ if (rpos == NULL)
+ break;
+ *rpos++ = '\0';
+ }
+
+ bss->nai_realm_count++;
+
+ return 0;
+
+fail:
+ wpa_printf(MSG_ERROR, "Line %d: invalid nai_realm '%s'", line, buf);
+ return -1;
+}
+
+#endif /* CONFIG_INTERWORKING */
+
+
+#ifdef CONFIG_HS20
+static int hs20_parse_conn_capab(struct hostapd_bss_config *bss, char *buf,
+ int line)
+{
+ u8 *conn_cap;
+ char *pos;
+
+ if (bss->hs20_connection_capability_len >= 0xfff0)
+ return -1;
+
+ conn_cap = os_realloc(bss->hs20_connection_capability,
+ bss->hs20_connection_capability_len + 4);
+ if (conn_cap == NULL)
+ return -1;
+
+ bss->hs20_connection_capability = conn_cap;
+ conn_cap += bss->hs20_connection_capability_len;
+ pos = buf;
+ conn_cap[0] = atoi(pos);
+ pos = os_strchr(pos, ':');
+ if (pos == NULL)
+ return -1;
+ pos++;
+ WPA_PUT_LE16(conn_cap + 1, atoi(pos));
+ pos = os_strchr(pos, ':');
+ if (pos == NULL)
+ return -1;
+ pos++;
+ conn_cap[3] = atoi(pos);
+ bss->hs20_connection_capability_len += 4;
+
+ return 0;
+}
+
+
+static int hs20_parse_wan_metrics(struct hostapd_bss_config *bss, char *buf,
+ int line)
+{
+ u8 *wan_metrics;
+ char *pos;
+
+ /* <WAN Info>:<DL Speed>:<UL Speed>:<DL Load>:<UL Load>:<LMD> */
+
+ wan_metrics = os_zalloc(13);
+ if (wan_metrics == NULL)
+ return -1;
+
+ pos = buf;
+ /* WAN Info */
+ if (hexstr2bin(pos, wan_metrics, 1) < 0)
+ goto fail;
+ pos += 2;
+ if (*pos != ':')
+ goto fail;
+ pos++;
+
+ /* Downlink Speed */
+ WPA_PUT_LE32(wan_metrics + 1, atoi(pos));
+ pos = os_strchr(pos, ':');
+ if (pos == NULL)
+ goto fail;
+ pos++;
+
+ /* Uplink Speed */
+ WPA_PUT_LE32(wan_metrics + 5, atoi(pos));
+ pos = os_strchr(pos, ':');
+ if (pos == NULL)
+ goto fail;
+ pos++;
+
+ /* Downlink Load */
+ wan_metrics[9] = atoi(pos);
+ pos = os_strchr(pos, ':');
+ if (pos == NULL)
+ goto fail;
+ pos++;
+
+ /* Uplink Load */
+ wan_metrics[10] = atoi(pos);
+ pos = os_strchr(pos, ':');
+ if (pos == NULL)
+ goto fail;
+ pos++;
+
+ /* LMD */
+ WPA_PUT_LE16(wan_metrics + 11, atoi(pos));
+
+ os_free(bss->hs20_wan_metrics);
+ bss->hs20_wan_metrics = wan_metrics;
+
+ return 0;
+
+fail:
+ wpa_printf(MSG_ERROR, "Line %d: Invalid hs20_wan_metrics '%s'",
+ line, pos);
+ os_free(wan_metrics);
+ return -1;
+}
+
+
+static int hs20_parse_oper_friendly_name(struct hostapd_bss_config *bss,
+ char *pos, int line)
+{
+ if (parse_lang_string(&bss->hs20_oper_friendly_name,
+ &bss->hs20_oper_friendly_name_count, pos)) {
+ wpa_printf(MSG_ERROR, "Line %d: Invalid "
+ "hs20_oper_friendly_name '%s'", line, pos);
+ return -1;
+ }
+ return 0;
+}
+#endif /* CONFIG_HS20 */
+
+
+#ifdef CONFIG_WPS_NFC
+static struct wpabuf * hostapd_parse_bin(const char *buf)
+{
+ size_t len;
+ struct wpabuf *ret;
+
+ len = os_strlen(buf);
+ if (len & 0x01)
+ return NULL;
+ len /= 2;
+
+ ret = wpabuf_alloc(len);
+ if (ret == NULL)
+ return NULL;
+
+ if (hexstr2bin(buf, wpabuf_put(ret, len), len)) {
+ wpabuf_free(ret);
+ return NULL;
+ }
+
+ return ret;
+}
+#endif /* CONFIG_WPS_NFC */
+
+static int hostapd_config_fill(struct hostapd_config *conf,
+ struct hostapd_bss_config *bss,
+ char *buf, char *pos, int line)
+{
+ int errors = 0;
+
+ {
if (os_strcmp(buf, "interface") == 0) {
os_strlcpy(conf->bss[0].iface, pos,
sizeof(conf->bss[0].iface));
} else if (os_strcmp(buf, "bridge") == 0) {
os_strlcpy(bss->bridge, pos, sizeof(bss->bridge));
+ } else if (os_strcmp(buf, "wds_bridge") == 0) {
+ os_strlcpy(bss->wds_bridge, pos,
+ sizeof(bss->wds_bridge));
} else if (os_strcmp(buf, "driver") == 0) {
int j;
/* clear to get error below if setting is invalid */
@@ -1233,9 +1764,24 @@ struct hostapd_config * hostapd_config_read(const char *fname)
} else {
os_memcpy(bss->ssid.ssid, pos,
bss->ssid.ssid_len);
- bss->ssid.ssid[bss->ssid.ssid_len] = '\0';
bss->ssid.ssid_set = 1;
}
+ } else if (os_strcmp(buf, "ssid2") == 0) {
+ size_t slen;
+ char *str = wpa_config_parse_string(pos, &slen);
+ if (str == NULL || slen < 1 ||
+ slen > HOSTAPD_MAX_SSID_LEN) {
+ wpa_printf(MSG_ERROR, "Line %d: invalid SSID "
+ "'%s'", line, pos);
+ errors++;
+ } else {
+ os_memcpy(bss->ssid.ssid, str, slen);
+ bss->ssid.ssid_len = slen;
+ bss->ssid.ssid_set = 1;
+ }
+ os_free(str);
+ } else if (os_strcmp(buf, "utf8_ssid") == 0) {
+ bss->ssid.utf8_ssid = atoi(pos) > 0;
} else if (os_strcmp(buf, "macaddr_acl") == 0) {
bss->macaddr_acl = atoi(pos);
if (bss->macaddr_acl != ACCEPT_UNLESS_DENIED &&
@@ -1264,8 +1810,12 @@ struct hostapd_config * hostapd_config_read(const char *fname)
}
} else if (os_strcmp(buf, "wds_sta") == 0) {
bss->wds_sta = atoi(pos);
+ } else if (os_strcmp(buf, "ap_isolate") == 0) {
+ bss->isolate = atoi(pos);
} else if (os_strcmp(buf, "ap_max_inactivity") == 0) {
bss->ap_max_inactivity = atoi(pos);
+ } else if (os_strcmp(buf, "skip_inactivity_poll") == 0) {
+ bss->skip_inactivity_poll = atoi(pos);
} else if (os_strcmp(buf, "country_code") == 0) {
os_memcpy(conf->country, pos, 2);
/* FIX: make this configurable */
@@ -1313,6 +1863,8 @@ struct hostapd_config * hostapd_config_read(const char *fname)
} else if (os_strcmp(buf, "dh_file") == 0) {
os_free(bss->dh_file);
bss->dh_file = os_strdup(pos);
+ } else if (os_strcmp(buf, "fragment_size") == 0) {
+ bss->fragment_size = atoi(pos);
#ifdef EAP_SERVER_FAST
} else if (os_strcmp(buf, "pac_opaque_encr_key") == 0) {
os_free(bss->pac_opaque_encr_key);
@@ -1367,6 +1919,10 @@ struct hostapd_config * hostapd_config_read(const char *fname)
} else if (os_strcmp(buf, "tnc") == 0) {
bss->tnc = atoi(pos);
#endif /* EAP_SERVER_TNC */
+#ifdef EAP_SERVER_PWD
+ } else if (os_strcmp(buf, "pwd_group") == 0) {
+ bss->pwd_group = atoi(pos);
+#endif /* EAP_SERVER_PWD */
#endif /* EAP_SERVER */
} else if (os_strcmp(buf, "eap_message") == 0) {
char *term;
@@ -1376,7 +1932,7 @@ struct hostapd_config * hostapd_config_read(const char *fname)
"allocate memory for "
"eap_req_id_text", line);
errors++;
- continue;
+ return errors;
}
bss->eap_req_id_text_len =
os_strlen(bss->eap_req_id_text);
@@ -1496,6 +2052,51 @@ struct hostapd_config * hostapd_config_read(const char *fname)
} else if (os_strcmp(buf, "radius_acct_interim_interval") == 0)
{
bss->acct_interim_interval = atoi(pos);
+ } else if (os_strcmp(buf, "radius_request_cui") == 0) {
+ bss->radius_request_cui = atoi(pos);
+ } else if (os_strcmp(buf, "radius_auth_req_attr") == 0) {
+ struct hostapd_radius_attr *attr, *a;
+ attr = hostapd_parse_radius_attr(pos);
+ if (attr == NULL) {
+ wpa_printf(MSG_ERROR, "Line %d: invalid "
+ "radius_auth_req_attr", line);
+ errors++;
+ } else if (bss->radius_auth_req_attr == NULL) {
+ bss->radius_auth_req_attr = attr;
+ } else {
+ a = bss->radius_auth_req_attr;
+ while (a->next)
+ a = a->next;
+ a->next = attr;
+ }
+ } else if (os_strcmp(buf, "radius_acct_req_attr") == 0) {
+ struct hostapd_radius_attr *attr, *a;
+ attr = hostapd_parse_radius_attr(pos);
+ if (attr == NULL) {
+ wpa_printf(MSG_ERROR, "Line %d: invalid "
+ "radius_acct_req_attr", line);
+ errors++;
+ } else if (bss->radius_acct_req_attr == NULL) {
+ bss->radius_acct_req_attr = attr;
+ } else {
+ a = bss->radius_acct_req_attr;
+ while (a->next)
+ a = a->next;
+ a->next = attr;
+ }
+ } else if (os_strcmp(buf, "radius_das_port") == 0) {
+ bss->radius_das_port = atoi(pos);
+ } else if (os_strcmp(buf, "radius_das_client") == 0) {
+ if (hostapd_parse_das_client(bss, pos) < 0) {
+ wpa_printf(MSG_ERROR, "Line %d: invalid "
+ "DAS client", line);
+ errors++;
+ }
+ } else if (os_strcmp(buf, "radius_das_time_window") == 0) {
+ bss->radius_das_time_window = atoi(pos);
+ } else if (os_strcmp(buf, "radius_das_require_event_timestamp")
+ == 0) {
+ bss->radius_das_require_event_timestamp = atoi(pos);
#endif /* CONFIG_NO_RADIUS */
} else if (os_strcmp(buf, "auth_algs") == 0) {
bss->auth_algs = atoi(pos);
@@ -1535,6 +2136,8 @@ struct hostapd_config * hostapd_config_read(const char *fname)
} else {
os_free(bss->ssid.wpa_passphrase);
bss->ssid.wpa_passphrase = os_strdup(pos);
+ os_free(bss->ssid.wpa_psk);
+ bss->ssid.wpa_psk = NULL;
}
} else if (os_strcmp(buf, "wpa_psk") == 0) {
os_free(bss->ssid.wpa_psk);
@@ -1550,6 +2153,8 @@ struct hostapd_config * hostapd_config_read(const char *fname)
errors++;
} else {
bss->ssid.wpa_psk->group = 1;
+ os_free(bss->ssid.wpa_passphrase);
+ bss->ssid.wpa_passphrase = NULL;
}
} else if (os_strcmp(buf, "wpa_psk_file") == 0) {
os_free(bss->ssid.wpa_psk_file);
@@ -1564,6 +2169,16 @@ struct hostapd_config * hostapd_config_read(const char *fname)
hostapd_config_parse_key_mgmt(line, pos);
if (bss->wpa_key_mgmt == -1)
errors++;
+ } else if (os_strcmp(buf, "wpa_psk_radius") == 0) {
+ bss->wpa_psk_radius = atoi(pos);
+ if (bss->wpa_psk_radius != PSK_RADIUS_IGNORED &&
+ bss->wpa_psk_radius != PSK_RADIUS_ACCEPTED &&
+ bss->wpa_psk_radius != PSK_RADIUS_REQUIRED) {
+ wpa_printf(MSG_ERROR, "Line %d: unknown "
+ "wpa_psk_radius %d",
+ line, bss->wpa_psk_radius);
+ errors++;
+ }
} else if (os_strcmp(buf, "wpa_pairwise") == 0) {
bss->wpa_pairwise =
hostapd_config_parse_cipher(line, pos);
@@ -1610,7 +2225,7 @@ struct hostapd_config * hostapd_config_read(const char *fname)
wpa_printf(MSG_DEBUG, "Line %d: Invalid "
"mobility_domain '%s'", line, pos);
errors++;
- continue;
+ return errors;
}
} else if (os_strcmp(buf, "r1_key_holder") == 0) {
if (os_strlen(pos) != 2 * FT_R1KH_ID_LEN ||
@@ -1619,7 +2234,7 @@ struct hostapd_config * hostapd_config_read(const char *fname)
wpa_printf(MSG_DEBUG, "Line %d: Invalid "
"r1_key_holder '%s'", line, pos);
errors++;
- continue;
+ return errors;
}
} else if (os_strcmp(buf, "r0_key_lifetime") == 0) {
bss->r0_key_lifetime = atoi(pos);
@@ -1630,17 +2245,19 @@ struct hostapd_config * hostapd_config_read(const char *fname)
wpa_printf(MSG_DEBUG, "Line %d: Invalid "
"r0kh '%s'", line, pos);
errors++;
- continue;
+ return errors;
}
} else if (os_strcmp(buf, "r1kh") == 0) {
if (add_r1kh(bss, pos) < 0) {
wpa_printf(MSG_DEBUG, "Line %d: Invalid "
"r1kh '%s'", line, pos);
errors++;
- continue;
+ return errors;
}
} else if (os_strcmp(buf, "pmk_r1_push") == 0) {
bss->pmk_r1_push = atoi(pos);
+ } else if (os_strcmp(buf, "ft_over_ds") == 0) {
+ bss->ft_over_ds = atoi(pos);
#endif /* CONFIG_IEEE80211R */
#ifndef CONFIG_NO_CTRL_IFACE
} else if (os_strcmp(buf, "ctrl_interface") == 0) {
@@ -1659,7 +2276,7 @@ struct hostapd_config * hostapd_config_read(const char *fname)
wpa_printf(MSG_DEBUG, "ctrl_interface_group=%d"
" (from group name '%s')",
bss->ctrl_interface_gid, group);
- continue;
+ return errors;
}
/* Group name not found - try to parse this as gid */
@@ -1668,7 +2285,7 @@ struct hostapd_config * hostapd_config_read(const char *fname)
wpa_printf(MSG_DEBUG, "Line %d: Invalid group "
"'%s'", line, group);
errors++;
- continue;
+ return errors;
}
bss->ctrl_interface_gid_set = 1;
wpa_printf(MSG_DEBUG, "ctrl_interface_group=%d",
@@ -1696,11 +2313,28 @@ struct hostapd_config * hostapd_config_read(const char *fname)
conf->hw_mode = HOSTAPD_MODE_IEEE80211B;
else if (os_strcmp(pos, "g") == 0)
conf->hw_mode = HOSTAPD_MODE_IEEE80211G;
+ else if (os_strcmp(pos, "ad") == 0)
+ conf->hw_mode = HOSTAPD_MODE_IEEE80211AD;
else {
wpa_printf(MSG_ERROR, "Line %d: unknown "
"hw_mode '%s'", line, pos);
errors++;
}
+ } else if (os_strcmp(buf, "wps_rf_bands") == 0) {
+ if (os_strcmp(pos, "a") == 0)
+ bss->wps_rf_bands = WPS_RF_50GHZ;
+ else if (os_strcmp(pos, "g") == 0 ||
+ os_strcmp(pos, "b") == 0)
+ bss->wps_rf_bands = WPS_RF_24GHZ;
+ else if (os_strcmp(pos, "ag") == 0 ||
+ os_strcmp(pos, "ga") == 0)
+ bss->wps_rf_bands =
+ WPS_RF_24GHZ | WPS_RF_50GHZ;
+ else {
+ wpa_printf(MSG_ERROR, "Line %d: unknown "
+ "wps_rf_band '%s'", line, pos);
+ errors++;
+ }
} else if (os_strcmp(buf, "channel") == 0) {
conf->channel = atoi(pos);
} else if (os_strcmp(buf, "beacon_int") == 0) {
@@ -1797,6 +2431,15 @@ struct hostapd_config * hostapd_config_read(const char *fname)
"read VLAN file '%s'", line, pos);
errors++;
}
+ } else if (os_strcmp(buf, "vlan_naming") == 0) {
+ bss->ssid.vlan_naming = atoi(pos);
+ if (bss->ssid.vlan_naming >= DYNAMIC_VLAN_NAMING_END ||
+ bss->ssid.vlan_naming < 0) {
+ wpa_printf(MSG_ERROR, "Line %d: invalid "
+ "naming scheme %d", line,
+ bss->ssid.vlan_naming);
+ errors++;
+ }
#ifdef CONFIG_FULL_DYNAMIC_VLAN
} else if (os_strcmp(buf, "vlan_tagged_interface") == 0) {
bss->ssid.vlan_tagged_interface = os_strdup(pos);
@@ -1819,7 +2462,8 @@ struct hostapd_config * hostapd_config_read(const char *fname)
bss->wmm_uapsd = atoi(pos);
} else if (os_strncmp(buf, "wme_ac_", 7) == 0 ||
os_strncmp(buf, "wmm_ac_", 7) == 0) {
- if (hostapd_config_wmm_ac(conf, buf, pos)) {
+ if (hostapd_config_wmm_ac(conf->wmm_ac_params, buf,
+ pos)) {
wpa_printf(MSG_ERROR, "Line %d: invalid WMM "
"ac item", line);
errors++;
@@ -1865,9 +2509,33 @@ struct hostapd_config * hostapd_config_read(const char *fname)
"ht_capab", line);
errors++;
}
+ } else if (os_strcmp(buf, "require_ht") == 0) {
+ conf->require_ht = atoi(pos);
#endif /* CONFIG_IEEE80211N */
+#ifdef CONFIG_IEEE80211AC
+ } else if (os_strcmp(buf, "ieee80211ac") == 0) {
+ conf->ieee80211ac = atoi(pos);
+ } else if (os_strcmp(buf, "vht_capab") == 0) {
+ if (hostapd_config_vht_capab(conf, pos) < 0) {
+ wpa_printf(MSG_ERROR, "Line %d: invalid "
+ "vht_capab", line);
+ errors++;
+ }
+ } else if (os_strcmp(buf, "require_vht") == 0) {
+ conf->require_vht = atoi(pos);
+ } else if (os_strcmp(buf, "vht_oper_chwidth") == 0) {
+ conf->vht_oper_chwidth = atoi(pos);
+ } else if (os_strcmp(buf, "vht_oper_centr_freq_seg0_idx") == 0)
+ {
+ conf->vht_oper_centr_freq_seg0_idx = atoi(pos);
+ } else if (os_strcmp(buf, "vht_oper_centr_freq_seg1_idx") == 0)
+ {
+ conf->vht_oper_centr_freq_seg1_idx = atoi(pos);
+#endif /* CONFIG_IEEE80211AC */
} else if (os_strcmp(buf, "max_listen_interval") == 0) {
bss->max_listen_interval = atoi(pos);
+ } else if (os_strcmp(buf, "disable_pmksa_caching") == 0) {
+ bss->disable_pmksa_caching = atoi(pos);
} else if (os_strcmp(buf, "okc") == 0) {
bss->okc = atoi(pos);
#ifdef CONFIG_WPS
@@ -1930,8 +2598,8 @@ struct hostapd_config * hostapd_config_read(const char *fname)
os_free(bss->serial_number);
bss->serial_number = os_strdup(pos);
} else if (os_strcmp(buf, "device_type") == 0) {
- os_free(bss->device_type);
- bss->device_type = os_strdup(pos);
+ if (wps_dev_type_str2bin(pos, bss->device_type))
+ errors++;
} else if (os_strcmp(buf, "config_methods") == 0) {
os_free(bss->config_methods);
bss->config_methods = os_strdup(pos);
@@ -1985,7 +2653,288 @@ struct hostapd_config * hostapd_config_read(const char *fname)
} else if (os_strcmp(buf, "upc") == 0) {
os_free(bss->upc);
bss->upc = os_strdup(pos);
+ } else if (os_strcmp(buf, "pbc_in_m1") == 0) {
+ bss->pbc_in_m1 = atoi(pos);
+#ifdef CONFIG_WPS_NFC
+ } else if (os_strcmp(buf, "wps_nfc_dev_pw_id") == 0) {
+ bss->wps_nfc_dev_pw_id = atoi(pos);
+ if (bss->wps_nfc_dev_pw_id < 0x10 ||
+ bss->wps_nfc_dev_pw_id > 0xffff) {
+ wpa_printf(MSG_ERROR, "Line %d: Invalid "
+ "wps_nfc_dev_pw_id value", line);
+ errors++;
+ }
+ } else if (os_strcmp(buf, "wps_nfc_dh_pubkey") == 0) {
+ wpabuf_free(bss->wps_nfc_dh_pubkey);
+ bss->wps_nfc_dh_pubkey = hostapd_parse_bin(pos);
+ } else if (os_strcmp(buf, "wps_nfc_dh_privkey") == 0) {
+ wpabuf_free(bss->wps_nfc_dh_privkey);
+ bss->wps_nfc_dh_privkey = hostapd_parse_bin(pos);
+ } else if (os_strcmp(buf, "wps_nfc_dev_pw") == 0) {
+ wpabuf_free(bss->wps_nfc_dev_pw);
+ bss->wps_nfc_dev_pw = hostapd_parse_bin(pos);
+#endif /* CONFIG_WPS_NFC */
#endif /* CONFIG_WPS */
+#ifdef CONFIG_P2P_MANAGER
+ } else if (os_strcmp(buf, "manage_p2p") == 0) {
+ int manage = atoi(pos);
+ if (manage)
+ bss->p2p |= P2P_MANAGE;
+ else
+ bss->p2p &= ~P2P_MANAGE;
+ } else if (os_strcmp(buf, "allow_cross_connection") == 0) {
+ if (atoi(pos))
+ bss->p2p |= P2P_ALLOW_CROSS_CONNECTION;
+ else
+ bss->p2p &= ~P2P_ALLOW_CROSS_CONNECTION;
+#endif /* CONFIG_P2P_MANAGER */
+ } else if (os_strcmp(buf, "disassoc_low_ack") == 0) {
+ bss->disassoc_low_ack = atoi(pos);
+ } else if (os_strcmp(buf, "tdls_prohibit") == 0) {
+ int val = atoi(pos);
+ if (val)
+ bss->tdls |= TDLS_PROHIBIT;
+ else
+ bss->tdls &= ~TDLS_PROHIBIT;
+ } else if (os_strcmp(buf, "tdls_prohibit_chan_switch") == 0) {
+ int val = atoi(pos);
+ if (val)
+ bss->tdls |= TDLS_PROHIBIT_CHAN_SWITCH;
+ else
+ bss->tdls &= ~TDLS_PROHIBIT_CHAN_SWITCH;
+#ifdef CONFIG_RSN_TESTING
+ } else if (os_strcmp(buf, "rsn_testing") == 0) {
+ extern int rsn_testing;
+ rsn_testing = atoi(pos);
+#endif /* CONFIG_RSN_TESTING */
+ } else if (os_strcmp(buf, "time_advertisement") == 0) {
+ bss->time_advertisement = atoi(pos);
+ } else if (os_strcmp(buf, "time_zone") == 0) {
+ size_t tz_len = os_strlen(pos);
+ if (tz_len < 4 || tz_len > 255) {
+ wpa_printf(MSG_DEBUG, "Line %d: invalid "
+ "time_zone", line);
+ errors++;
+ return errors;
+ }
+ os_free(bss->time_zone);
+ bss->time_zone = os_strdup(pos);
+ if (bss->time_zone == NULL)
+ errors++;
+#ifdef CONFIG_WNM
+ } else if (os_strcmp(buf, "wnm_sleep_mode") == 0) {
+ bss->wnm_sleep_mode = atoi(pos);
+ } else if (os_strcmp(buf, "bss_transition") == 0) {
+ bss->bss_transition = atoi(pos);
+#endif /* CONFIG_WNM */
+#ifdef CONFIG_INTERWORKING
+ } else if (os_strcmp(buf, "interworking") == 0) {
+ bss->interworking = atoi(pos);
+ } else if (os_strcmp(buf, "access_network_type") == 0) {
+ bss->access_network_type = atoi(pos);
+ if (bss->access_network_type < 0 ||
+ bss->access_network_type > 15) {
+ wpa_printf(MSG_ERROR, "Line %d: invalid "
+ "access_network_type", line);
+ errors++;
+ }
+ } else if (os_strcmp(buf, "internet") == 0) {
+ bss->internet = atoi(pos);
+ } else if (os_strcmp(buf, "asra") == 0) {
+ bss->asra = atoi(pos);
+ } else if (os_strcmp(buf, "esr") == 0) {
+ bss->esr = atoi(pos);
+ } else if (os_strcmp(buf, "uesa") == 0) {
+ bss->uesa = atoi(pos);
+ } else if (os_strcmp(buf, "venue_group") == 0) {
+ bss->venue_group = atoi(pos);
+ bss->venue_info_set = 1;
+ } else if (os_strcmp(buf, "venue_type") == 0) {
+ bss->venue_type = atoi(pos);
+ bss->venue_info_set = 1;
+ } else if (os_strcmp(buf, "hessid") == 0) {
+ if (hwaddr_aton(pos, bss->hessid)) {
+ wpa_printf(MSG_ERROR, "Line %d: invalid "
+ "hessid", line);
+ errors++;
+ }
+ } else if (os_strcmp(buf, "roaming_consortium") == 0) {
+ if (parse_roaming_consortium(bss, pos, line) < 0)
+ errors++;
+ } else if (os_strcmp(buf, "venue_name") == 0) {
+ if (parse_venue_name(bss, pos, line) < 0)
+ errors++;
+ } else if (os_strcmp(buf, "network_auth_type") == 0) {
+ u8 auth_type;
+ u16 redirect_url_len;
+ if (hexstr2bin(pos, &auth_type, 1)) {
+ wpa_printf(MSG_ERROR, "Line %d: Invalid "
+ "network_auth_type '%s'",
+ line, pos);
+ errors++;
+ return errors;
+ }
+ if (auth_type == 0 || auth_type == 2)
+ redirect_url_len = os_strlen(pos + 2);
+ else
+ redirect_url_len = 0;
+ os_free(bss->network_auth_type);
+ bss->network_auth_type =
+ os_malloc(redirect_url_len + 3 + 1);
+ if (bss->network_auth_type == NULL) {
+ errors++;
+ return errors;
+ }
+ *bss->network_auth_type = auth_type;
+ WPA_PUT_LE16(bss->network_auth_type + 1,
+ redirect_url_len);
+ if (redirect_url_len)
+ os_memcpy(bss->network_auth_type + 3,
+ pos + 2, redirect_url_len);
+ bss->network_auth_type_len = 3 + redirect_url_len;
+ } else if (os_strcmp(buf, "ipaddr_type_availability") == 0) {
+ if (hexstr2bin(pos, &bss->ipaddr_type_availability, 1))
+ {
+ wpa_printf(MSG_ERROR, "Line %d: Invalid "
+ "ipaddr_type_availability '%s'",
+ line, pos);
+ bss->ipaddr_type_configured = 0;
+ errors++;
+ return errors;
+ }
+ bss->ipaddr_type_configured = 1;
+ } else if (os_strcmp(buf, "domain_name") == 0) {
+ int j, num_domains, domain_len, domain_list_len = 0;
+ char *tok_start, *tok_prev;
+ u8 *domain_list, *domain_ptr;
+
+ domain_list_len = os_strlen(pos) + 1;
+ domain_list = os_malloc(domain_list_len);
+ if (domain_list == NULL) {
+ errors++;
+ return errors;
+ }
+
+ domain_ptr = domain_list;
+ tok_prev = pos;
+ num_domains = 1;
+ while ((tok_prev = os_strchr(tok_prev, ','))) {
+ num_domains++;
+ tok_prev++;
+ }
+ tok_prev = pos;
+ for (j = 0; j < num_domains; j++) {
+ tok_start = os_strchr(tok_prev, ',');
+ if (tok_start) {
+ domain_len = tok_start - tok_prev;
+ *domain_ptr = domain_len;
+ os_memcpy(domain_ptr + 1, tok_prev,
+ domain_len);
+ domain_ptr += domain_len + 1;
+ tok_prev = ++tok_start;
+ } else {
+ domain_len = os_strlen(tok_prev);
+ *domain_ptr = domain_len;
+ os_memcpy(domain_ptr + 1, tok_prev,
+ domain_len);
+ domain_ptr += domain_len + 1;
+ }
+ }
+
+ os_free(bss->domain_name);
+ bss->domain_name = domain_list;
+ bss->domain_name_len = domain_list_len;
+ } else if (os_strcmp(buf, "anqp_3gpp_cell_net") == 0) {
+ if (parse_3gpp_cell_net(bss, pos, line) < 0)
+ errors++;
+ } else if (os_strcmp(buf, "nai_realm") == 0) {
+ if (parse_nai_realm(bss, pos, line) < 0)
+ errors++;
+ } else if (os_strcmp(buf, "gas_frag_limit") == 0) {
+ bss->gas_frag_limit = atoi(pos);
+ } else if (os_strcmp(buf, "gas_comeback_delay") == 0) {
+ bss->gas_comeback_delay = atoi(pos);
+#endif /* CONFIG_INTERWORKING */
+#ifdef CONFIG_RADIUS_TEST
+ } else if (os_strcmp(buf, "dump_msk_file") == 0) {
+ os_free(bss->dump_msk_file);
+ bss->dump_msk_file = os_strdup(pos);
+#endif /* CONFIG_RADIUS_TEST */
+#ifdef CONFIG_HS20
+ } else if (os_strcmp(buf, "hs20") == 0) {
+ bss->hs20 = atoi(pos);
+ } else if (os_strcmp(buf, "disable_dgaf") == 0) {
+ bss->disable_dgaf = atoi(pos);
+ } else if (os_strcmp(buf, "hs20_oper_friendly_name") == 0) {
+ if (hs20_parse_oper_friendly_name(bss, pos, line) < 0)
+ errors++;
+ } else if (os_strcmp(buf, "hs20_wan_metrics") == 0) {
+ if (hs20_parse_wan_metrics(bss, pos, line) < 0) {
+ errors++;
+ return errors;
+ }
+ } else if (os_strcmp(buf, "hs20_conn_capab") == 0) {
+ if (hs20_parse_conn_capab(bss, pos, line) < 0) {
+ errors++;
+ return errors;
+ }
+ } else if (os_strcmp(buf, "hs20_operating_class") == 0) {
+ u8 *oper_class;
+ size_t oper_class_len;
+ oper_class_len = os_strlen(pos);
+ if (oper_class_len < 2 || (oper_class_len & 0x01)) {
+ wpa_printf(MSG_ERROR, "Line %d: Invalid "
+ "hs20_operating_class '%s'",
+ line, pos);
+ errors++;
+ return errors;
+ }
+ oper_class_len /= 2;
+ oper_class = os_malloc(oper_class_len);
+ if (oper_class == NULL) {
+ errors++;
+ return errors;
+ }
+ if (hexstr2bin(pos, oper_class, oper_class_len)) {
+ wpa_printf(MSG_ERROR, "Line %d: Invalid "
+ "hs20_operating_class '%s'",
+ line, pos);
+ os_free(oper_class);
+ errors++;
+ return errors;
+ }
+ os_free(bss->hs20_operating_class);
+ bss->hs20_operating_class = oper_class;
+ bss->hs20_operating_class_len = oper_class_len;
+#endif /* CONFIG_HS20 */
+ } else if (os_strcmp(buf, "vendor_elements") == 0) {
+ struct wpabuf *elems;
+ size_t len = os_strlen(pos);
+ if (len & 0x01) {
+ wpa_printf(MSG_ERROR, "Line %d: Invalid "
+ "vendor_elements '%s'", line, pos);
+ return 1;
+ }
+ len /= 2;
+ if (len == 0) {
+ wpabuf_free(bss->vendor_elements);
+ bss->vendor_elements = NULL;
+ return 0;
+ }
+
+ elems = wpabuf_alloc(len);
+ if (elems == NULL)
+ return 1;
+
+ if (hexstr2bin(pos, wpabuf_put(elems, len), len)) {
+ wpabuf_free(elems);
+ wpa_printf(MSG_ERROR, "Line %d: Invalid "
+ "vendor_elements '%s'", line, pos);
+ return 1;
+ }
+
+ wpabuf_free(bss->vendor_elements);
+ bss->vendor_elements = elems;
} else {
wpa_printf(MSG_ERROR, "Line %d: unknown configuration "
"item '%s'", line, buf);
@@ -1993,57 +2942,181 @@ struct hostapd_config * hostapd_config_read(const char *fname)
}
}
- fclose(f);
+ return errors;
+}
- for (i = 0; i < conf->num_bss; i++) {
- bss = &conf->bss[i];
- if (bss->individual_wep_key_len == 0) {
- /* individual keys are not use; can use key idx0 for
- * broadcast keys */
- bss->broadcast_key_idx_min = 0;
+static void hostapd_set_security_params(struct hostapd_bss_config *bss)
+{
+ int pairwise;
+
+ if (bss->individual_wep_key_len == 0) {
+ /* individual keys are not use; can use key idx0 for
+ * broadcast keys */
+ bss->broadcast_key_idx_min = 0;
+ }
+
+ /* Select group cipher based on the enabled pairwise cipher
+ * suites */
+ pairwise = 0;
+ if (bss->wpa & 1)
+ pairwise |= bss->wpa_pairwise;
+ if (bss->wpa & 2) {
+ if (bss->rsn_pairwise == 0)
+ bss->rsn_pairwise = bss->wpa_pairwise;
+ pairwise |= bss->rsn_pairwise;
+ }
+ if (pairwise & WPA_CIPHER_TKIP)
+ bss->wpa_group = WPA_CIPHER_TKIP;
+ else if ((pairwise & (WPA_CIPHER_CCMP | WPA_CIPHER_GCMP)) ==
+ WPA_CIPHER_GCMP)
+ bss->wpa_group = WPA_CIPHER_GCMP;
+ else
+ bss->wpa_group = WPA_CIPHER_CCMP;
+
+ bss->radius->auth_server = bss->radius->auth_servers;
+ bss->radius->acct_server = bss->radius->acct_servers;
+
+ if (bss->wpa && bss->ieee802_1x) {
+ bss->ssid.security_policy = SECURITY_WPA;
+ } else if (bss->wpa) {
+ bss->ssid.security_policy = SECURITY_WPA_PSK;
+ } else if (bss->ieee802_1x) {
+ int cipher = WPA_CIPHER_NONE;
+ bss->ssid.security_policy = SECURITY_IEEE_802_1X;
+ bss->ssid.wep.default_len = bss->default_wep_key_len;
+ if (bss->default_wep_key_len)
+ cipher = bss->default_wep_key_len >= 13 ?
+ WPA_CIPHER_WEP104 : WPA_CIPHER_WEP40;
+ bss->wpa_group = cipher;
+ bss->wpa_pairwise = cipher;
+ bss->rsn_pairwise = cipher;
+ } else if (bss->ssid.wep.keys_set) {
+ int cipher = WPA_CIPHER_WEP40;
+ if (bss->ssid.wep.len[0] >= 13)
+ cipher = WPA_CIPHER_WEP104;
+ bss->ssid.security_policy = SECURITY_STATIC_WEP;
+ bss->wpa_group = cipher;
+ bss->wpa_pairwise = cipher;
+ bss->rsn_pairwise = cipher;
+ } else {
+ bss->ssid.security_policy = SECURITY_PLAINTEXT;
+ bss->wpa_group = WPA_CIPHER_NONE;
+ bss->wpa_pairwise = WPA_CIPHER_NONE;
+ bss->rsn_pairwise = WPA_CIPHER_NONE;
+ }
+}
+
+
+/**
+ * hostapd_config_read - Read and parse a configuration file
+ * @fname: Configuration file name (including path, if needed)
+ * Returns: Allocated configuration data structure
+ */
+struct hostapd_config * hostapd_config_read(const char *fname)
+{
+ struct hostapd_config *conf;
+ struct hostapd_bss_config *bss;
+ FILE *f;
+ char buf[512], *pos;
+ int line = 0;
+ int errors = 0;
+ size_t i;
+
+ f = fopen(fname, "r");
+ if (f == NULL) {
+ wpa_printf(MSG_ERROR, "Could not open configuration file '%s' "
+ "for reading.", fname);
+ return NULL;
+ }
+
+ conf = hostapd_config_defaults();
+ if (conf == NULL) {
+ fclose(f);
+ return NULL;
+ }
+
+ /* set default driver based on configuration */
+ conf->driver = wpa_drivers[0];
+ if (conf->driver == NULL) {
+ wpa_printf(MSG_ERROR, "No driver wrappers registered!");
+ hostapd_config_free(conf);
+ fclose(f);
+ return NULL;
+ }
+
+ bss = conf->last_bss = conf->bss;
+
+ while (fgets(buf, sizeof(buf), f)) {
+ bss = conf->last_bss;
+ line++;
+
+ if (buf[0] == '#')
+ continue;
+ pos = buf;
+ while (*pos != '\0') {
+ if (*pos == '\n') {
+ *pos = '\0';
+ break;
+ }
+ pos++;
}
+ if (buf[0] == '\0')
+ continue;
- /* Select group cipher based on the enabled pairwise cipher
- * suites */
- pairwise = 0;
- if (bss->wpa & 1)
- pairwise |= bss->wpa_pairwise;
- if (bss->wpa & 2) {
- if (bss->rsn_pairwise == 0)
- bss->rsn_pairwise = bss->wpa_pairwise;
- pairwise |= bss->rsn_pairwise;
+ pos = os_strchr(buf, '=');
+ if (pos == NULL) {
+ wpa_printf(MSG_ERROR, "Line %d: invalid line '%s'",
+ line, buf);
+ errors++;
+ continue;
}
- if (pairwise & WPA_CIPHER_TKIP)
- bss->wpa_group = WPA_CIPHER_TKIP;
- else
- bss->wpa_group = WPA_CIPHER_CCMP;
-
- bss->radius->auth_server = bss->radius->auth_servers;
- bss->radius->acct_server = bss->radius->acct_servers;
-
- if (bss->wpa && bss->ieee802_1x) {
- bss->ssid.security_policy = SECURITY_WPA;
- } else if (bss->wpa) {
- bss->ssid.security_policy = SECURITY_WPA_PSK;
- } else if (bss->ieee802_1x) {
- bss->ssid.security_policy = SECURITY_IEEE_802_1X;
- bss->ssid.wep.default_len = bss->default_wep_key_len;
- } else if (bss->ssid.wep.keys_set)
- bss->ssid.security_policy = SECURITY_STATIC_WEP;
- else
- bss->ssid.security_policy = SECURITY_PLAINTEXT;
+ *pos = '\0';
+ pos++;
+ errors += hostapd_config_fill(conf, bss, buf, pos, line);
}
+ fclose(f);
+
+ for (i = 0; i < conf->num_bss; i++)
+ hostapd_set_security_params(&conf->bss[i]);
+
if (hostapd_config_check(conf))
errors++;
+#ifndef WPA_IGNORE_CONFIG_ERRORS
if (errors) {
wpa_printf(MSG_ERROR, "%d errors found in configuration file "
"'%s'", errors, fname);
hostapd_config_free(conf);
conf = NULL;
}
+#endif /* WPA_IGNORE_CONFIG_ERRORS */
return conf;
}
+
+
+int hostapd_set_iface(struct hostapd_config *conf,
+ struct hostapd_bss_config *bss, char *field, char *value)
+{
+ int errors;
+ size_t i;
+
+ errors = hostapd_config_fill(conf, bss, field, value, 0);
+ if (errors) {
+ wpa_printf(MSG_INFO, "Failed to set configuration field '%s' "
+ "to value '%s'", field, value);
+ return -1;
+ }
+
+ for (i = 0; i < conf->num_bss; i++)
+ hostapd_set_security_params(&conf->bss[i]);
+
+ if (hostapd_config_check(conf)) {
+ wpa_printf(MSG_ERROR, "Configuration check failed");
+ return -1;
+ }
+
+ return 0;
+}
diff --git a/contrib/wpa/hostapd/config_file.h b/contrib/wpa/hostapd/config_file.h
index 7111a9a..fba57b8 100644
--- a/contrib/wpa/hostapd/config_file.h
+++ b/contrib/wpa/hostapd/config_file.h
@@ -2,19 +2,16 @@
* hostapd / Configuration file parser
* Copyright (c) 2003-2009, Jouni Malinen <j@w1.fi>
*
- * This program is free software; you can redistribute it and/or modify
- * it under the terms of the GNU General Public License version 2 as
- * published by the Free Software Foundation.
- *
- * Alternatively, this software may be distributed under the terms of BSD
- * license.
- *
- * See README and COPYING for more details.
+ * This software may be distributed under the terms of the BSD license.
+ * See README for more details.
*/
#ifndef CONFIG_FILE_H
#define CONFIG_FILE_H
struct hostapd_config * hostapd_config_read(const char *fname);
+int hostapd_set_iface(struct hostapd_config *conf,
+ struct hostapd_bss_config *bss, char *field,
+ char *value);
#endif /* CONFIG_FILE_H */
diff --git a/contrib/wpa/hostapd/ctrl_iface.c b/contrib/wpa/hostapd/ctrl_iface.c
index 9c47ba8..3c9071c 100644
--- a/contrib/wpa/hostapd/ctrl_iface.c
+++ b/contrib/wpa/hostapd/ctrl_iface.c
@@ -1,15 +1,9 @@
/*
* hostapd / UNIX domain socket -based control interface
- * Copyright (c) 2004-2009, Jouni Malinen <j@w1.fi>
+ * Copyright (c) 2004-2012, Jouni Malinen <j@w1.fi>
*
- * This program is free software; you can redistribute it and/or modify
- * it under the terms of the GNU General Public License version 2 as
- * published by the Free Software Foundation.
- *
- * Alternatively, this software may be distributed under the terms of BSD
- * license.
- *
- * See README and COPYING for more details.
+ * This software may be distributed under the terms of the BSD license.
+ * See README for more details.
*/
#include "utils/includes.h"
@@ -22,6 +16,7 @@
#include "utils/common.h"
#include "utils/eloop.h"
+#include "common/version.h"
#include "common/ieee802_11_defs.h"
#include "drivers/driver.h"
#include "radius/radius_client.h"
@@ -31,9 +26,12 @@
#include "ap/wpa_auth.h"
#include "ap/ieee802_11.h"
#include "ap/sta_info.h"
-#include "ap/accounting.h"
#include "ap/wps_hostapd.h"
#include "ap/ctrl_iface_ap.h"
+#include "ap/ap_drv_ops.h"
+#include "wps/wps_defs.h"
+#include "wps/wps.h"
+#include "config_file.h"
#include "ctrl_iface.h"
@@ -155,98 +153,6 @@ static int hostapd_ctrl_iface_new_sta(struct hostapd_data *hapd,
}
-static int hostapd_ctrl_iface_deauthenticate(struct hostapd_data *hapd,
- const char *txtaddr)
-{
- u8 addr[ETH_ALEN];
- struct sta_info *sta;
- const char *pos;
-
- wpa_printf(MSG_DEBUG, "CTRL_IFACE DEAUTHENTICATE %s", txtaddr);
-
- if (hwaddr_aton(txtaddr, addr))
- return -1;
-
- pos = os_strstr(txtaddr, " test=");
- if (pos) {
- struct ieee80211_mgmt mgmt;
- int encrypt;
- if (hapd->driver->send_frame == NULL)
- return -1;
- pos += 6;
- encrypt = atoi(pos);
- os_memset(&mgmt, 0, sizeof(mgmt));
- mgmt.frame_control = IEEE80211_FC(WLAN_FC_TYPE_MGMT,
- WLAN_FC_STYPE_DEAUTH);
- os_memcpy(mgmt.da, addr, ETH_ALEN);
- os_memcpy(mgmt.sa, hapd->own_addr, ETH_ALEN);
- os_memcpy(mgmt.bssid, hapd->own_addr, ETH_ALEN);
- mgmt.u.deauth.reason_code =
- host_to_le16(WLAN_REASON_PREV_AUTH_NOT_VALID);
- if (hapd->driver->send_frame(hapd->drv_priv, (u8 *) &mgmt,
- IEEE80211_HDRLEN +
- sizeof(mgmt.u.deauth),
- encrypt) < 0)
- return -1;
- return 0;
- }
-
- hapd->drv.sta_deauth(hapd, addr, WLAN_REASON_PREV_AUTH_NOT_VALID);
- sta = ap_get_sta(hapd, addr);
- if (sta)
- ap_sta_deauthenticate(hapd, sta,
- WLAN_REASON_PREV_AUTH_NOT_VALID);
-
- return 0;
-}
-
-
-static int hostapd_ctrl_iface_disassociate(struct hostapd_data *hapd,
- const char *txtaddr)
-{
- u8 addr[ETH_ALEN];
- struct sta_info *sta;
- const char *pos;
-
- wpa_printf(MSG_DEBUG, "CTRL_IFACE DISASSOCIATE %s", txtaddr);
-
- if (hwaddr_aton(txtaddr, addr))
- return -1;
-
- pos = os_strstr(txtaddr, " test=");
- if (pos) {
- struct ieee80211_mgmt mgmt;
- int encrypt;
- if (hapd->driver->send_frame == NULL)
- return -1;
- pos += 6;
- encrypt = atoi(pos);
- os_memset(&mgmt, 0, sizeof(mgmt));
- mgmt.frame_control = IEEE80211_FC(WLAN_FC_TYPE_MGMT,
- WLAN_FC_STYPE_DISASSOC);
- os_memcpy(mgmt.da, addr, ETH_ALEN);
- os_memcpy(mgmt.sa, hapd->own_addr, ETH_ALEN);
- os_memcpy(mgmt.bssid, hapd->own_addr, ETH_ALEN);
- mgmt.u.disassoc.reason_code =
- host_to_le16(WLAN_REASON_PREV_AUTH_NOT_VALID);
- if (hapd->driver->send_frame(hapd->drv_priv, (u8 *) &mgmt,
- IEEE80211_HDRLEN +
- sizeof(mgmt.u.deauth),
- encrypt) < 0)
- return -1;
- return 0;
- }
-
- hapd->drv.sta_disassoc(hapd, addr, WLAN_REASON_PREV_AUTH_NOT_VALID);
- sta = ap_get_sta(hapd, addr);
- if (sta)
- ap_sta_disassociate(hapd, sta,
- WLAN_REASON_PREV_AUTH_NOT_VALID);
-
- return 0;
-}
-
-
#ifdef CONFIG_IEEE80211W
#ifdef NEED_AP_MLME
static int hostapd_ctrl_iface_sa_query(struct hostapd_data *hapd,
@@ -275,6 +181,8 @@ static int hostapd_ctrl_iface_wps_pin(struct hostapd_data *hapd, char *txt)
char *pin = os_strchr(txt, ' ');
char *timeout_txt;
int timeout;
+ u8 addr_buf[ETH_ALEN], *addr = NULL;
+ char *pos;
if (pin == NULL)
return -1;
@@ -284,35 +192,167 @@ static int hostapd_ctrl_iface_wps_pin(struct hostapd_data *hapd, char *txt)
if (timeout_txt) {
*timeout_txt++ = '\0';
timeout = atoi(timeout_txt);
+ pos = os_strchr(timeout_txt, ' ');
+ if (pos) {
+ *pos++ = '\0';
+ if (hwaddr_aton(pos, addr_buf) == 0)
+ addr = addr_buf;
+ }
} else
timeout = 0;
- return hostapd_wps_add_pin(hapd, txt, pin, timeout);
+ return hostapd_wps_add_pin(hapd, addr, txt, pin, timeout);
+}
+
+
+static int hostapd_ctrl_iface_wps_check_pin(
+ struct hostapd_data *hapd, char *cmd, char *buf, size_t buflen)
+{
+ char pin[9];
+ size_t len;
+ char *pos;
+ int ret;
+
+ wpa_hexdump_ascii_key(MSG_DEBUG, "WPS_CHECK_PIN",
+ (u8 *) cmd, os_strlen(cmd));
+ for (pos = cmd, len = 0; *pos != '\0'; pos++) {
+ if (*pos < '0' || *pos > '9')
+ continue;
+ pin[len++] = *pos;
+ if (len == 9) {
+ wpa_printf(MSG_DEBUG, "WPS: Too long PIN");
+ return -1;
+ }
+ }
+ if (len != 4 && len != 8) {
+ wpa_printf(MSG_DEBUG, "WPS: Invalid PIN length %d", (int) len);
+ return -1;
+ }
+ pin[len] = '\0';
+
+ if (len == 8) {
+ unsigned int pin_val;
+ pin_val = atoi(pin);
+ if (!wps_pin_valid(pin_val)) {
+ wpa_printf(MSG_DEBUG, "WPS: Invalid checksum digit");
+ ret = os_snprintf(buf, buflen, "FAIL-CHECKSUM\n");
+ if (ret < 0 || (size_t) ret >= buflen)
+ return -1;
+ return ret;
+ }
+ }
+
+ ret = os_snprintf(buf, buflen, "%s", pin);
+ if (ret < 0 || (size_t) ret >= buflen)
+ return -1;
+
+ return ret;
+}
+
+
+#ifdef CONFIG_WPS_NFC
+static int hostapd_ctrl_iface_wps_nfc_tag_read(struct hostapd_data *hapd,
+ char *pos)
+{
+ size_t len;
+ struct wpabuf *buf;
+ int ret;
+
+ len = os_strlen(pos);
+ if (len & 0x01)
+ return -1;
+ len /= 2;
+
+ buf = wpabuf_alloc(len);
+ if (buf == NULL)
+ return -1;
+ if (hexstr2bin(pos, wpabuf_put(buf, len), len) < 0) {
+ wpabuf_free(buf);
+ return -1;
+ }
+
+ ret = hostapd_wps_nfc_tag_read(hapd, buf);
+ wpabuf_free(buf);
+
+ return ret;
}
-#ifdef CONFIG_WPS_OOB
-static int hostapd_ctrl_iface_wps_oob(struct hostapd_data *hapd, char *txt)
+static int hostapd_ctrl_iface_wps_nfc_config_token(struct hostapd_data *hapd,
+ char *cmd, char *reply,
+ size_t max_len)
{
- char *path, *method, *name;
+ int ndef;
+ struct wpabuf *buf;
+ int res;
+
+ if (os_strcmp(cmd, "WPS") == 0)
+ ndef = 0;
+ else if (os_strcmp(cmd, "NDEF") == 0)
+ ndef = 1;
+ else
+ return -1;
- path = os_strchr(txt, ' ');
- if (path == NULL)
+ buf = hostapd_wps_nfc_config_token(hapd, ndef);
+ if (buf == NULL)
return -1;
- *path++ = '\0';
- method = os_strchr(path, ' ');
- if (method == NULL)
+ res = wpa_snprintf_hex_uppercase(reply, max_len, wpabuf_head(buf),
+ wpabuf_len(buf));
+ reply[res++] = '\n';
+ reply[res] = '\0';
+
+ wpabuf_free(buf);
+
+ return res;
+}
+
+
+static int hostapd_ctrl_iface_wps_nfc_token_gen(struct hostapd_data *hapd,
+ char *reply, size_t max_len,
+ int ndef)
+{
+ struct wpabuf *buf;
+ int res;
+
+ buf = hostapd_wps_nfc_token_gen(hapd, ndef);
+ if (buf == NULL)
return -1;
- *method++ = '\0';
- name = os_strchr(method, ' ');
- if (name != NULL)
- *name++ = '\0';
+ res = wpa_snprintf_hex_uppercase(reply, max_len, wpabuf_head(buf),
+ wpabuf_len(buf));
+ reply[res++] = '\n';
+ reply[res] = '\0';
- return hostapd_wps_start_oob(hapd, txt, path, method, name);
+ wpabuf_free(buf);
+
+ return res;
}
-#endif /* CONFIG_WPS_OOB */
+
+
+static int hostapd_ctrl_iface_wps_nfc_token(struct hostapd_data *hapd,
+ char *cmd, char *reply,
+ size_t max_len)
+{
+ if (os_strcmp(cmd, "WPS") == 0)
+ return hostapd_ctrl_iface_wps_nfc_token_gen(hapd, reply,
+ max_len, 0);
+
+ if (os_strcmp(cmd, "NDEF") == 0)
+ return hostapd_ctrl_iface_wps_nfc_token_gen(hapd, reply,
+ max_len, 1);
+
+ if (os_strcmp(cmd, "enable") == 0)
+ return hostapd_wps_nfc_token_enable(hapd);
+
+ if (os_strcmp(cmd, "disable") == 0) {
+ hostapd_wps_nfc_token_disable(hapd);
+ return 0;
+ }
+
+ return -1;
+}
+#endif /* CONFIG_WPS_NFC */
static int hostapd_ctrl_iface_wps_ap_pin(struct hostapd_data *hapd, char *txt,
@@ -366,8 +406,416 @@ static int hostapd_ctrl_iface_wps_ap_pin(struct hostapd_data *hapd, char *txt,
return -1;
}
+
+
+static int hostapd_ctrl_iface_wps_config(struct hostapd_data *hapd, char *txt)
+{
+ char *pos;
+ char *ssid, *auth, *encr = NULL, *key = NULL;
+
+ ssid = txt;
+ pos = os_strchr(txt, ' ');
+ if (!pos)
+ return -1;
+ *pos++ = '\0';
+
+ auth = pos;
+ pos = os_strchr(pos, ' ');
+ if (pos) {
+ *pos++ = '\0';
+ encr = pos;
+ pos = os_strchr(pos, ' ');
+ if (pos) {
+ *pos++ = '\0';
+ key = pos;
+ }
+ }
+
+ return hostapd_wps_config_ap(hapd, ssid, auth, encr, key);
+}
+#endif /* CONFIG_WPS */
+
+
+#ifdef CONFIG_WNM
+
+static int hostapd_ctrl_iface_disassoc_imminent(struct hostapd_data *hapd,
+ const char *cmd)
+{
+ u8 addr[ETH_ALEN];
+ u8 buf[1000], *pos;
+ struct ieee80211_mgmt *mgmt;
+ int disassoc_timer;
+
+ if (hwaddr_aton(cmd, addr))
+ return -1;
+ if (cmd[17] != ' ')
+ return -1;
+ disassoc_timer = atoi(cmd + 17);
+
+ os_memset(buf, 0, sizeof(buf));
+ mgmt = (struct ieee80211_mgmt *) buf;
+ mgmt->frame_control = IEEE80211_FC(WLAN_FC_TYPE_MGMT,
+ WLAN_FC_STYPE_ACTION);
+ os_memcpy(mgmt->da, addr, ETH_ALEN);
+ os_memcpy(mgmt->sa, hapd->own_addr, ETH_ALEN);
+ os_memcpy(mgmt->bssid, hapd->own_addr, ETH_ALEN);
+ mgmt->u.action.category = WLAN_ACTION_WNM;
+ mgmt->u.action.u.bss_tm_req.action = WNM_BSS_TRANS_MGMT_REQ;
+ mgmt->u.action.u.bss_tm_req.dialog_token = 1;
+ mgmt->u.action.u.bss_tm_req.req_mode =
+ WNM_BSS_TM_REQ_DISASSOC_IMMINENT;
+ mgmt->u.action.u.bss_tm_req.disassoc_timer =
+ host_to_le16(disassoc_timer);
+ mgmt->u.action.u.bss_tm_req.validity_interval = 0;
+
+ pos = mgmt->u.action.u.bss_tm_req.variable;
+
+ if (hostapd_drv_send_mlme(hapd, buf, pos - buf, 0) < 0) {
+ wpa_printf(MSG_DEBUG, "Failed to send BSS Transition "
+ "Management Request frame");
+ return -1;
+ }
+
+ return 0;
+}
+
+
+static int hostapd_ctrl_iface_ess_disassoc(struct hostapd_data *hapd,
+ const char *cmd)
+{
+ u8 addr[ETH_ALEN];
+ const char *url;
+ u8 buf[1000], *pos;
+ struct ieee80211_mgmt *mgmt;
+ size_t url_len;
+
+ if (hwaddr_aton(cmd, addr))
+ return -1;
+ url = cmd + 17;
+ if (*url != ' ')
+ return -1;
+ url++;
+ url_len = os_strlen(url);
+ if (url_len > 255)
+ return -1;
+
+ os_memset(buf, 0, sizeof(buf));
+ mgmt = (struct ieee80211_mgmt *) buf;
+ mgmt->frame_control = IEEE80211_FC(WLAN_FC_TYPE_MGMT,
+ WLAN_FC_STYPE_ACTION);
+ os_memcpy(mgmt->da, addr, ETH_ALEN);
+ os_memcpy(mgmt->sa, hapd->own_addr, ETH_ALEN);
+ os_memcpy(mgmt->bssid, hapd->own_addr, ETH_ALEN);
+ mgmt->u.action.category = WLAN_ACTION_WNM;
+ mgmt->u.action.u.bss_tm_req.action = WNM_BSS_TRANS_MGMT_REQ;
+ mgmt->u.action.u.bss_tm_req.dialog_token = 1;
+ mgmt->u.action.u.bss_tm_req.req_mode =
+ WNM_BSS_TM_REQ_ESS_DISASSOC_IMMINENT;
+ mgmt->u.action.u.bss_tm_req.disassoc_timer = host_to_le16(0);
+ mgmt->u.action.u.bss_tm_req.validity_interval = 0;
+
+ pos = mgmt->u.action.u.bss_tm_req.variable;
+
+ /* Session Information URL */
+ *pos++ = url_len;
+ os_memcpy(pos, url, url_len);
+ pos += url_len;
+
+ if (hostapd_drv_send_mlme(hapd, buf, pos - buf, 0) < 0) {
+ wpa_printf(MSG_DEBUG, "Failed to send BSS Transition "
+ "Management Request frame");
+ return -1;
+ }
+
+ return 0;
+}
+
+#endif /* CONFIG_WNM */
+
+
+static int hostapd_ctrl_iface_get_config(struct hostapd_data *hapd,
+ char *buf, size_t buflen)
+{
+ int ret;
+ char *pos, *end;
+
+ pos = buf;
+ end = buf + buflen;
+
+ ret = os_snprintf(pos, end - pos, "bssid=" MACSTR "\n"
+ "ssid=%s\n",
+ MAC2STR(hapd->own_addr),
+ wpa_ssid_txt(hapd->conf->ssid.ssid,
+ hapd->conf->ssid.ssid_len));
+ if (ret < 0 || ret >= end - pos)
+ return pos - buf;
+ pos += ret;
+
+#ifdef CONFIG_WPS
+ ret = os_snprintf(pos, end - pos, "wps_state=%s\n",
+ hapd->conf->wps_state == 0 ? "disabled" :
+ (hapd->conf->wps_state == 1 ? "not configured" :
+ "configured"));
+ if (ret < 0 || ret >= end - pos)
+ return pos - buf;
+ pos += ret;
+
+ if (hapd->conf->wps_state && hapd->conf->wpa &&
+ hapd->conf->ssid.wpa_passphrase) {
+ ret = os_snprintf(pos, end - pos, "passphrase=%s\n",
+ hapd->conf->ssid.wpa_passphrase);
+ if (ret < 0 || ret >= end - pos)
+ return pos - buf;
+ pos += ret;
+ }
+
+ if (hapd->conf->wps_state && hapd->conf->wpa &&
+ hapd->conf->ssid.wpa_psk &&
+ hapd->conf->ssid.wpa_psk->group) {
+ char hex[PMK_LEN * 2 + 1];
+ wpa_snprintf_hex(hex, sizeof(hex),
+ hapd->conf->ssid.wpa_psk->psk, PMK_LEN);
+ ret = os_snprintf(pos, end - pos, "psk=%s\n", hex);
+ if (ret < 0 || ret >= end - pos)
+ return pos - buf;
+ pos += ret;
+ }
#endif /* CONFIG_WPS */
+ if (hapd->conf->wpa && hapd->conf->wpa_key_mgmt) {
+ ret = os_snprintf(pos, end - pos, "key_mgmt=");
+ if (ret < 0 || ret >= end - pos)
+ return pos - buf;
+ pos += ret;
+
+ if (hapd->conf->wpa_key_mgmt & WPA_KEY_MGMT_PSK) {
+ ret = os_snprintf(pos, end - pos, "WPA-PSK ");
+ if (ret < 0 || ret >= end - pos)
+ return pos - buf;
+ pos += ret;
+ }
+ if (hapd->conf->wpa_key_mgmt & WPA_KEY_MGMT_IEEE8021X) {
+ ret = os_snprintf(pos, end - pos, "WPA-EAP ");
+ if (ret < 0 || ret >= end - pos)
+ return pos - buf;
+ pos += ret;
+ }
+#ifdef CONFIG_IEEE80211R
+ if (hapd->conf->wpa_key_mgmt & WPA_KEY_MGMT_FT_PSK) {
+ ret = os_snprintf(pos, end - pos, "FT-PSK ");
+ if (ret < 0 || ret >= end - pos)
+ return pos - buf;
+ pos += ret;
+ }
+ if (hapd->conf->wpa_key_mgmt & WPA_KEY_MGMT_FT_IEEE8021X) {
+ ret = os_snprintf(pos, end - pos, "FT-EAP ");
+ if (ret < 0 || ret >= end - pos)
+ return pos - buf;
+ pos += ret;
+ }
+#endif /* CONFIG_IEEE80211R */
+#ifdef CONFIG_IEEE80211W
+ if (hapd->conf->wpa_key_mgmt & WPA_KEY_MGMT_PSK_SHA256) {
+ ret = os_snprintf(pos, end - pos, "WPA-PSK-SHA256 ");
+ if (ret < 0 || ret >= end - pos)
+ return pos - buf;
+ pos += ret;
+ }
+ if (hapd->conf->wpa_key_mgmt & WPA_KEY_MGMT_IEEE8021X_SHA256) {
+ ret = os_snprintf(pos, end - pos, "WPA-EAP-SHA256 ");
+ if (ret < 0 || ret >= end - pos)
+ return pos - buf;
+ pos += ret;
+ }
+#endif /* CONFIG_IEEE80211W */
+
+ ret = os_snprintf(pos, end - pos, "\n");
+ if (ret < 0 || ret >= end - pos)
+ return pos - buf;
+ pos += ret;
+ }
+
+ if (hapd->conf->wpa && hapd->conf->wpa_group == WPA_CIPHER_CCMP) {
+ ret = os_snprintf(pos, end - pos, "group_cipher=CCMP\n");
+ if (ret < 0 || ret >= end - pos)
+ return pos - buf;
+ pos += ret;
+ } else if (hapd->conf->wpa &&
+ hapd->conf->wpa_group == WPA_CIPHER_GCMP) {
+ ret = os_snprintf(pos, end - pos, "group_cipher=GCMP\n");
+ if (ret < 0 || ret >= end - pos)
+ return pos - buf;
+ pos += ret;
+ } else if (hapd->conf->wpa &&
+ hapd->conf->wpa_group == WPA_CIPHER_TKIP) {
+ ret = os_snprintf(pos, end - pos, "group_cipher=TKIP\n");
+ if (ret < 0 || ret >= end - pos)
+ return pos - buf;
+ pos += ret;
+ }
+
+ if ((hapd->conf->wpa & WPA_PROTO_RSN) && hapd->conf->rsn_pairwise) {
+ ret = os_snprintf(pos, end - pos, "rsn_pairwise_cipher=");
+ if (ret < 0 || ret >= end - pos)
+ return pos - buf;
+ pos += ret;
+
+ if (hapd->conf->rsn_pairwise & WPA_CIPHER_CCMP) {
+ ret = os_snprintf(pos, end - pos, "CCMP ");
+ if (ret < 0 || ret >= end - pos)
+ return pos - buf;
+ pos += ret;
+ }
+ if (hapd->conf->rsn_pairwise & WPA_CIPHER_GCMP) {
+ ret = os_snprintf(pos, end - pos, "GCMP ");
+ if (ret < 0 || ret >= end - pos)
+ return pos - buf;
+ pos += ret;
+ }
+ if (hapd->conf->rsn_pairwise & WPA_CIPHER_TKIP) {
+ ret = os_snprintf(pos, end - pos, "TKIP ");
+ if (ret < 0 || ret >= end - pos)
+ return pos - buf;
+ pos += ret;
+ }
+
+ ret = os_snprintf(pos, end - pos, "\n");
+ if (ret < 0 || ret >= end - pos)
+ return pos - buf;
+ pos += ret;
+ }
+
+ if ((hapd->conf->wpa & WPA_PROTO_WPA) && hapd->conf->wpa_pairwise) {
+ ret = os_snprintf(pos, end - pos, "wpa_pairwise_cipher=");
+ if (ret < 0 || ret >= end - pos)
+ return pos - buf;
+ pos += ret;
+
+ if (hapd->conf->wpa_pairwise & WPA_CIPHER_CCMP) {
+ ret = os_snprintf(pos, end - pos, "CCMP ");
+ if (ret < 0 || ret >= end - pos)
+ return pos - buf;
+ pos += ret;
+ }
+ if (hapd->conf->wpa_pairwise & WPA_CIPHER_GCMP) {
+ ret = os_snprintf(pos, end - pos, "GCMP ");
+ if (ret < 0 || ret >= end - pos)
+ return pos - buf;
+ pos += ret;
+ }
+ if (hapd->conf->wpa_pairwise & WPA_CIPHER_TKIP) {
+ ret = os_snprintf(pos, end - pos, "TKIP ");
+ if (ret < 0 || ret >= end - pos)
+ return pos - buf;
+ pos += ret;
+ }
+
+ ret = os_snprintf(pos, end - pos, "\n");
+ if (ret < 0 || ret >= end - pos)
+ return pos - buf;
+ pos += ret;
+ }
+
+ return pos - buf;
+}
+
+
+static int hostapd_ctrl_iface_set(struct hostapd_data *hapd, char *cmd)
+{
+ char *value;
+ int ret = 0;
+
+ value = os_strchr(cmd, ' ');
+ if (value == NULL)
+ return -1;
+ *value++ = '\0';
+
+ wpa_printf(MSG_DEBUG, "CTRL_IFACE SET '%s'='%s'", cmd, value);
+ if (0) {
+#ifdef CONFIG_WPS_TESTING
+ } else if (os_strcasecmp(cmd, "wps_version_number") == 0) {
+ long int val;
+ val = strtol(value, NULL, 0);
+ if (val < 0 || val > 0xff) {
+ ret = -1;
+ wpa_printf(MSG_DEBUG, "WPS: Invalid "
+ "wps_version_number %ld", val);
+ } else {
+ wps_version_number = val;
+ wpa_printf(MSG_DEBUG, "WPS: Testing - force WPS "
+ "version %u.%u",
+ (wps_version_number & 0xf0) >> 4,
+ wps_version_number & 0x0f);
+ hostapd_wps_update_ie(hapd);
+ }
+ } else if (os_strcasecmp(cmd, "wps_testing_dummy_cred") == 0) {
+ wps_testing_dummy_cred = atoi(value);
+ wpa_printf(MSG_DEBUG, "WPS: Testing - dummy_cred=%d",
+ wps_testing_dummy_cred);
+#endif /* CONFIG_WPS_TESTING */
+#ifdef CONFIG_INTERWORKING
+ } else if (os_strcasecmp(cmd, "gas_frag_limit") == 0) {
+ int val = atoi(value);
+ if (val <= 0)
+ ret = -1;
+ else
+ hapd->gas_frag_limit = val;
+#endif /* CONFIG_INTERWORKING */
+ } else {
+ ret = hostapd_set_iface(hapd->iconf, hapd->conf, cmd, value);
+ }
+
+ return ret;
+}
+
+
+static int hostapd_ctrl_iface_get(struct hostapd_data *hapd, char *cmd,
+ char *buf, size_t buflen)
+{
+ int res;
+
+ wpa_printf(MSG_DEBUG, "CTRL_IFACE GET '%s'", cmd);
+
+ if (os_strcmp(cmd, "version") == 0) {
+ res = os_snprintf(buf, buflen, "%s", VERSION_STR);
+ if (res < 0 || (unsigned int) res >= buflen)
+ return -1;
+ return res;
+ }
+
+ return -1;
+}
+
+
+static int hostapd_ctrl_iface_enable(struct hostapd_iface *iface)
+{
+ if (hostapd_enable_iface(iface) < 0) {
+ wpa_printf(MSG_ERROR, "Enabling of interface failed");
+ return -1;
+ }
+ return 0;
+}
+
+
+static int hostapd_ctrl_iface_reload(struct hostapd_iface *iface)
+{
+ if (hostapd_reload_iface(iface) < 0) {
+ wpa_printf(MSG_ERROR, "Reloading of interface failed");
+ return -1;
+ }
+ return 0;
+}
+
+
+static int hostapd_ctrl_iface_disable(struct hostapd_iface *iface)
+{
+ if (hostapd_disable_iface(iface) < 0) {
+ wpa_printf(MSG_ERROR, "Disabling of interface failed");
+ return -1;
+ }
+ return 0;
+}
+
static void hostapd_ctrl_iface_receive(int sock, void *eloop_ctx,
void *sock_ctx)
@@ -380,6 +828,7 @@ static void hostapd_ctrl_iface_receive(int sock, void *eloop_ctx,
char *reply;
const int reply_size = 4096;
int reply_len;
+ int level = MSG_DEBUG;
res = recvfrom(sock, buf, sizeof(buf) - 1, 0,
(struct sockaddr *) &from, &fromlen);
@@ -388,7 +837,9 @@ static void hostapd_ctrl_iface_receive(int sock, void *eloop_ctx,
return;
}
buf[res] = '\0';
- wpa_hexdump_ascii(MSG_DEBUG, "RX ctrl_iface", (u8 *) buf, res);
+ if (os_strcmp(buf, "PING") == 0)
+ level = MSG_EXCESSIVE;
+ wpa_hexdump_ascii(level, "RX ctrl_iface", (u8 *) buf, res);
reply = os_malloc(reply_size);
if (reply == NULL) {
@@ -403,6 +854,9 @@ static void hostapd_ctrl_iface_receive(int sock, void *eloop_ctx,
if (os_strcmp(buf, "PING") == 0) {
os_memcpy(reply, "PONG\n", 5);
reply_len = 5;
+ } else if (os_strncmp(buf, "RELOG", 5) == 0) {
+ if (wpa_debug_reopen_file() < 0)
+ reply_len = -1;
} else if (os_strcmp(buf, "MIB") == 0) {
reply_len = ieee802_11_get_mib(hapd, reply, reply_size);
if (reply_len >= 0) {
@@ -471,18 +925,59 @@ static void hostapd_ctrl_iface_receive(int sock, void *eloop_ctx,
} else if (os_strncmp(buf, "WPS_PIN ", 8) == 0) {
if (hostapd_ctrl_iface_wps_pin(hapd, buf + 8))
reply_len = -1;
+ } else if (os_strncmp(buf, "WPS_CHECK_PIN ", 14) == 0) {
+ reply_len = hostapd_ctrl_iface_wps_check_pin(
+ hapd, buf + 14, reply, reply_size);
} else if (os_strcmp(buf, "WPS_PBC") == 0) {
- if (hostapd_wps_button_pushed(hapd))
+ if (hostapd_wps_button_pushed(hapd, NULL))
reply_len = -1;
-#ifdef CONFIG_WPS_OOB
- } else if (os_strncmp(buf, "WPS_OOB ", 8) == 0) {
- if (hostapd_ctrl_iface_wps_oob(hapd, buf + 8))
+ } else if (os_strcmp(buf, "WPS_CANCEL") == 0) {
+ if (hostapd_wps_cancel(hapd))
reply_len = -1;
-#endif /* CONFIG_WPS_OOB */
} else if (os_strncmp(buf, "WPS_AP_PIN ", 11) == 0) {
reply_len = hostapd_ctrl_iface_wps_ap_pin(hapd, buf + 11,
reply, reply_size);
+ } else if (os_strncmp(buf, "WPS_CONFIG ", 11) == 0) {
+ if (hostapd_ctrl_iface_wps_config(hapd, buf + 11) < 0)
+ reply_len = -1;
+#ifdef CONFIG_WPS_NFC
+ } else if (os_strncmp(buf, "WPS_NFC_TAG_READ ", 17) == 0) {
+ if (hostapd_ctrl_iface_wps_nfc_tag_read(hapd, buf + 17))
+ reply_len = -1;
+ } else if (os_strncmp(buf, "WPS_NFC_CONFIG_TOKEN ", 21) == 0) {
+ reply_len = hostapd_ctrl_iface_wps_nfc_config_token(
+ hapd, buf + 21, reply, reply_size);
+ } else if (os_strncmp(buf, "WPS_NFC_TOKEN ", 14) == 0) {
+ reply_len = hostapd_ctrl_iface_wps_nfc_token(
+ hapd, buf + 14, reply, reply_size);
+#endif /* CONFIG_WPS_NFC */
#endif /* CONFIG_WPS */
+#ifdef CONFIG_WNM
+ } else if (os_strncmp(buf, "DISASSOC_IMMINENT ", 18) == 0) {
+ if (hostapd_ctrl_iface_disassoc_imminent(hapd, buf + 18))
+ reply_len = -1;
+ } else if (os_strncmp(buf, "ESS_DISASSOC ", 13) == 0) {
+ if (hostapd_ctrl_iface_ess_disassoc(hapd, buf + 13))
+ reply_len = -1;
+#endif /* CONFIG_WNM */
+ } else if (os_strcmp(buf, "GET_CONFIG") == 0) {
+ reply_len = hostapd_ctrl_iface_get_config(hapd, reply,
+ reply_size);
+ } else if (os_strncmp(buf, "SET ", 4) == 0) {
+ if (hostapd_ctrl_iface_set(hapd, buf + 4))
+ reply_len = -1;
+ } else if (os_strncmp(buf, "GET ", 4) == 0) {
+ reply_len = hostapd_ctrl_iface_get(hapd, buf + 4, reply,
+ reply_size);
+ } else if (os_strncmp(buf, "ENABLE", 6) == 0) {
+ if (hostapd_ctrl_iface_enable(hapd->iface))
+ reply_len = -1;
+ } else if (os_strncmp(buf, "RELOAD", 6) == 0) {
+ if (hostapd_ctrl_iface_reload(hapd->iface))
+ reply_len = -1;
+ } else if (os_strncmp(buf, "DISABLE", 7) == 0) {
+ if (hostapd_ctrl_iface_disable(hapd->iface))
+ reply_len = -1;
} else {
os_memcpy(reply, "UNKNOWN COMMAND\n", 16);
reply_len = 16;
@@ -534,7 +1029,10 @@ int hostapd_ctrl_iface_init(struct hostapd_data *hapd)
int s = -1;
char *fname = NULL;
- hapd->ctrl_sock = -1;
+ if (hapd->ctrl_sock > -1) {
+ wpa_printf(MSG_DEBUG, "ctrl_iface already exists!");
+ return 0;
+ }
if (hapd->conf->ctrl_interface == NULL)
return 0;
@@ -550,12 +1048,27 @@ int hostapd_ctrl_iface_init(struct hostapd_data *hapd)
}
if (hapd->conf->ctrl_interface_gid_set &&
- chown(hapd->conf->ctrl_interface, 0,
+ chown(hapd->conf->ctrl_interface, -1,
hapd->conf->ctrl_interface_gid) < 0) {
perror("chown[ctrl_interface]");
return -1;
}
+#ifdef ANDROID
+ /*
+ * Android is using umask 0077 which would leave the control interface
+ * directory without group access. This breaks things since Wi-Fi
+ * framework assumes that this directory can be accessed by other
+ * applications in the wifi group. Fix this by adding group access even
+ * if umask value would prevent this.
+ */
+ if (chmod(hapd->conf->ctrl_interface, S_IRWXU | S_IRWXG) < 0) {
+ wpa_printf(MSG_ERROR, "CTRL: Could not chmod directory: %s",
+ strerror(errno));
+ /* Try to continue anyway */
+ }
+#endif /* ANDROID */
+
if (os_strlen(hapd->conf->ctrl_interface) + 1 +
os_strlen(hapd->conf->iface) >= sizeof(addr.sun_path))
goto fail;
@@ -591,7 +1104,7 @@ int hostapd_ctrl_iface_init(struct hostapd_data *hapd)
}
if (bind(s, (struct sockaddr *) &addr, sizeof(addr)) <
0) {
- perror("bind(PF_UNIX)");
+ perror("hostapd-ctrl-iface: bind(PF_UNIX)");
goto fail;
}
wpa_printf(MSG_DEBUG, "Successfully replaced leftover "
@@ -608,7 +1121,7 @@ int hostapd_ctrl_iface_init(struct hostapd_data *hapd)
}
if (hapd->conf->ctrl_interface_gid_set &&
- chown(fname, 0, hapd->conf->ctrl_interface_gid) < 0) {
+ chown(fname, -1, hapd->conf->ctrl_interface_gid) < 0) {
perror("chown[ctrl_interface/ifname]");
goto fail;
}
@@ -673,6 +1186,220 @@ void hostapd_ctrl_iface_deinit(struct hostapd_data *hapd)
}
+static int hostapd_ctrl_iface_add(struct hapd_interfaces *interfaces,
+ char *buf)
+{
+ if (hostapd_add_iface(interfaces, buf) < 0) {
+ wpa_printf(MSG_ERROR, "Adding interface %s failed", buf);
+ return -1;
+ }
+ return 0;
+}
+
+
+static int hostapd_ctrl_iface_remove(struct hapd_interfaces *interfaces,
+ char *buf)
+{
+ if (hostapd_remove_iface(interfaces, buf) < 0) {
+ wpa_printf(MSG_ERROR, "Removing interface %s failed", buf);
+ return -1;
+ }
+ return 0;
+}
+
+
+static void hostapd_global_ctrl_iface_receive(int sock, void *eloop_ctx,
+ void *sock_ctx)
+{
+ void *interfaces = eloop_ctx;
+ char buf[256];
+ int res;
+ struct sockaddr_un from;
+ socklen_t fromlen = sizeof(from);
+ char reply[24];
+ int reply_len;
+
+ res = recvfrom(sock, buf, sizeof(buf) - 1, 0,
+ (struct sockaddr *) &from, &fromlen);
+ if (res < 0) {
+ perror("recvfrom(ctrl_iface)");
+ return;
+ }
+ buf[res] = '\0';
+
+ os_memcpy(reply, "OK\n", 3);
+ reply_len = 3;
+
+ if (os_strcmp(buf, "PING") == 0) {
+ os_memcpy(reply, "PONG\n", 5);
+ reply_len = 5;
+ } else if (os_strncmp(buf, "ADD ", 4) == 0) {
+ if (hostapd_ctrl_iface_add(interfaces, buf + 4) < 0)
+ reply_len = -1;
+ } else if (os_strncmp(buf, "REMOVE ", 7) == 0) {
+ if (hostapd_ctrl_iface_remove(interfaces, buf + 7) < 0)
+ reply_len = -1;
+ } else {
+ wpa_printf(MSG_DEBUG, "Unrecognized global ctrl_iface command "
+ "ignored");
+ reply_len = -1;
+ }
+
+ if (reply_len < 0) {
+ os_memcpy(reply, "FAIL\n", 5);
+ reply_len = 5;
+ }
+
+ sendto(sock, reply, reply_len, 0, (struct sockaddr *) &from, fromlen);
+}
+
+
+static char * hostapd_global_ctrl_iface_path(struct hapd_interfaces *interface)
+{
+ char *buf;
+ size_t len;
+
+ if (interface->global_iface_path == NULL)
+ return NULL;
+
+ len = os_strlen(interface->global_iface_path) +
+ os_strlen(interface->global_iface_name) + 2;
+ buf = os_malloc(len);
+ if (buf == NULL)
+ return NULL;
+
+ os_snprintf(buf, len, "%s/%s", interface->global_iface_path,
+ interface->global_iface_name);
+ buf[len - 1] = '\0';
+ return buf;
+}
+
+
+int hostapd_global_ctrl_iface_init(struct hapd_interfaces *interface)
+{
+ struct sockaddr_un addr;
+ int s = -1;
+ char *fname = NULL;
+
+ if (interface->global_iface_path == NULL) {
+ wpa_printf(MSG_DEBUG, "ctrl_iface not configured!");
+ return 0;
+ }
+
+ if (mkdir(interface->global_iface_path, S_IRWXU | S_IRWXG) < 0) {
+ if (errno == EEXIST) {
+ wpa_printf(MSG_DEBUG, "Using existing control "
+ "interface directory.");
+ } else {
+ perror("mkdir[ctrl_interface]");
+ goto fail;
+ }
+ }
+
+ if (os_strlen(interface->global_iface_path) + 1 +
+ os_strlen(interface->global_iface_name) >= sizeof(addr.sun_path))
+ goto fail;
+
+ s = socket(PF_UNIX, SOCK_DGRAM, 0);
+ if (s < 0) {
+ perror("socket(PF_UNIX)");
+ goto fail;
+ }
+
+ os_memset(&addr, 0, sizeof(addr));
+#ifdef __FreeBSD__
+ addr.sun_len = sizeof(addr);
+#endif /* __FreeBSD__ */
+ addr.sun_family = AF_UNIX;
+ fname = hostapd_global_ctrl_iface_path(interface);
+ if (fname == NULL)
+ goto fail;
+ os_strlcpy(addr.sun_path, fname, sizeof(addr.sun_path));
+ if (bind(s, (struct sockaddr *) &addr, sizeof(addr)) < 0) {
+ wpa_printf(MSG_DEBUG, "ctrl_iface bind(PF_UNIX) failed: %s",
+ strerror(errno));
+ if (connect(s, (struct sockaddr *) &addr, sizeof(addr)) < 0) {
+ wpa_printf(MSG_DEBUG, "ctrl_iface exists, but does not"
+ " allow connections - assuming it was left"
+ "over from forced program termination");
+ if (unlink(fname) < 0) {
+ perror("unlink[ctrl_iface]");
+ wpa_printf(MSG_ERROR, "Could not unlink "
+ "existing ctrl_iface socket '%s'",
+ fname);
+ goto fail;
+ }
+ if (bind(s, (struct sockaddr *) &addr, sizeof(addr)) <
+ 0) {
+ perror("bind(PF_UNIX)");
+ goto fail;
+ }
+ wpa_printf(MSG_DEBUG, "Successfully replaced leftover "
+ "ctrl_iface socket '%s'", fname);
+ } else {
+ wpa_printf(MSG_INFO, "ctrl_iface exists and seems to "
+ "be in use - cannot override it");
+ wpa_printf(MSG_INFO, "Delete '%s' manually if it is "
+ "not used anymore", fname);
+ os_free(fname);
+ fname = NULL;
+ goto fail;
+ }
+ }
+
+ if (chmod(fname, S_IRWXU | S_IRWXG) < 0) {
+ perror("chmod[ctrl_interface/ifname]");
+ goto fail;
+ }
+ os_free(fname);
+
+ interface->global_ctrl_sock = s;
+ eloop_register_read_sock(s, hostapd_global_ctrl_iface_receive,
+ interface, NULL);
+
+ return 0;
+
+fail:
+ if (s >= 0)
+ close(s);
+ if (fname) {
+ unlink(fname);
+ os_free(fname);
+ }
+ return -1;
+}
+
+
+void hostapd_global_ctrl_iface_deinit(struct hapd_interfaces *interfaces)
+{
+ char *fname = NULL;
+
+ if (interfaces->global_ctrl_sock > -1) {
+ eloop_unregister_read_sock(interfaces->global_ctrl_sock);
+ close(interfaces->global_ctrl_sock);
+ interfaces->global_ctrl_sock = -1;
+ fname = hostapd_global_ctrl_iface_path(interfaces);
+ if (fname) {
+ unlink(fname);
+ os_free(fname);
+ }
+
+ if (interfaces->global_iface_path &&
+ rmdir(interfaces->global_iface_path) < 0) {
+ if (errno == ENOTEMPTY) {
+ wpa_printf(MSG_DEBUG, "Control interface "
+ "directory not empty - leaving it "
+ "behind");
+ } else {
+ perror("rmdir[ctrl_interface]");
+ }
+ }
+ os_free(interfaces->global_iface_path);
+ interfaces->global_iface_path = NULL;
+ }
+}
+
+
static void hostapd_ctrl_iface_send(struct hostapd_data *hapd, int level,
const char *buf, size_t len)
{
diff --git a/contrib/wpa/hostapd/ctrl_iface.h b/contrib/wpa/hostapd/ctrl_iface.h
index c997141..3341a66 100644
--- a/contrib/wpa/hostapd/ctrl_iface.h
+++ b/contrib/wpa/hostapd/ctrl_iface.h
@@ -2,14 +2,8 @@
* hostapd / UNIX domain socket -based control interface
* Copyright (c) 2004, Jouni Malinen <j@w1.fi>
*
- * This program is free software; you can redistribute it and/or modify
- * it under the terms of the GNU General Public License version 2 as
- * published by the Free Software Foundation.
- *
- * Alternatively, this software may be distributed under the terms of BSD
- * license.
- *
- * See README and COPYING for more details.
+ * This software may be distributed under the terms of the BSD license.
+ * See README for more details.
*/
#ifndef CTRL_IFACE_H
@@ -18,6 +12,8 @@
#ifndef CONFIG_NO_CTRL_IFACE
int hostapd_ctrl_iface_init(struct hostapd_data *hapd);
void hostapd_ctrl_iface_deinit(struct hostapd_data *hapd);
+int hostapd_global_ctrl_iface_init(struct hapd_interfaces *interface);
+void hostapd_global_ctrl_iface_deinit(struct hapd_interfaces *interface);
#else /* CONFIG_NO_CTRL_IFACE */
static inline int hostapd_ctrl_iface_init(struct hostapd_data *hapd)
{
@@ -27,6 +23,17 @@ static inline int hostapd_ctrl_iface_init(struct hostapd_data *hapd)
static inline void hostapd_ctrl_iface_deinit(struct hostapd_data *hapd)
{
}
+
+static inline int
+hostapd_global_ctrl_iface_init(struct hapd_interfaces *interface)
+{
+ return 0;
+}
+
+static inline void
+hostapd_global_ctrl_iface_deinit(struct hapd_interfaces *interface)
+{
+}
#endif /* CONFIG_NO_CTRL_IFACE */
#endif /* CTRL_IFACE_H */
diff --git a/contrib/wpa/hostapd/defconfig b/contrib/wpa/hostapd/defconfig
index 1d28c02..b5ddca3 100644
--- a/contrib/wpa/hostapd/defconfig
+++ b/contrib/wpa/hostapd/defconfig
@@ -20,13 +20,7 @@ CONFIG_DRIVER_HOSTAP=y
#CFLAGS += -I../../madwifi # change to the madwifi source directory
# Driver interface for drivers using the nl80211 kernel interface
-#CONFIG_DRIVER_NL80211=y
-# driver_nl80211.c requires a rather new libnl (version 1.1) which may not be
-# shipped with your distribution yet. If that is the case, you need to build
-# newer libnl version and point the hostapd build to use it.
-#LIBNL=/usr/src/libnl
-#CFLAGS += -I$(LIBNL)/include
-#LIBS += -L$(LIBNL)/lib
+CONFIG_DRIVER_NL80211=y
# Driver interface for FreeBSD net80211 layer (e.g., Atheros driver)
#CONFIG_DRIVER_BSD=y
@@ -90,6 +84,9 @@ CONFIG_EAP_TTLS=y
# EAP-PSK for the integrated EAP server (this is _not_ needed for WPA-PSK)
#CONFIG_EAP_PSK=y
+# EAP-pwd for the integrated EAP server (secure authentication with a password)
+#CONFIG_EAP_PWD=y
+
# EAP-SAKE for the integrated EAP server
#CONFIG_EAP_SAKE=y
@@ -107,8 +104,12 @@ CONFIG_EAP_TTLS=y
# Wi-Fi Protected Setup (WPS)
#CONFIG_WPS=y
+# Enable WSC 2.0 support
+#CONFIG_WPS2=y
# Enable UPnP support for external WPS Registrars
#CONFIG_WPS_UPNP=y
+# Enable WPS support with NFC config method
+#CONFIG_WPS_NFC=y
# EAP-IKEv2
#CONFIG_EAP_IKEV2=y
@@ -137,11 +138,22 @@ CONFIG_IPV6=y
# IEEE 802.11n (High Throughput) support
#CONFIG_IEEE80211N=y
+# Wireless Network Management (IEEE Std 802.11v-2011)
+# Note: This is experimental and not complete implementation.
+#CONFIG_WNM=y
+
+# IEEE 802.11ac (Very High Throughput) support
+#CONFIG_IEEE80211AC=y
+
# Remove debugging code that is printing out debug messages to stdout.
# This can be used to reduce the size of the hostapd considerably if debugging
# code is not needed.
#CONFIG_NO_STDOUT_DEBUG=y
+# Add support for writing debug log to a file: -f /tmp/hostapd.log
+# Disabled by default.
+#CONFIG_DEBUG_FILE=y
+
# Remove support for RADIUS accounting
#CONFIG_NO_ACCOUNTING=y
@@ -151,6 +163,14 @@ CONFIG_IPV6=y
# Remove support for VLANs
#CONFIG_NO_VLAN=y
+# Enable support for fully dynamic VLANs. This enables hostapd to
+# automatically create bridge and VLAN interfaces if necessary.
+#CONFIG_FULL_DYNAMIC_VLAN=y
+
+# Use netlink-based kernel API for VLAN operations instead of ioctl()
+# Note: This requires libnl 3.1 or newer.
+#CONFIG_VLAN_NETLINK=y
+
# Remove support for dumping state into a file on SIGUSR1 signal
# This can be used to reduce binary size at the cost of disabling a debugging
# option.
@@ -173,3 +193,77 @@ CONFIG_IPV6=y
#LIBS += -lbfd -liberty -lz
#LIBS_p += -lbfd -liberty -lz
#LIBS_c += -lbfd -liberty -lz
+
+# hostapd depends on strong random number generation being available from the
+# operating system. os_get_random() function is used to fetch random data when
+# needed, e.g., for key generation. On Linux and BSD systems, this works by
+# reading /dev/urandom. It should be noted that the OS entropy pool needs to be
+# properly initialized before hostapd is started. This is important especially
+# on embedded devices that do not have a hardware random number generator and
+# may by default start up with minimal entropy available for random number
+# generation.
+#
+# As a safety net, hostapd is by default trying to internally collect
+# additional entropy for generating random data to mix in with the data
+# fetched from the OS. This by itself is not considered to be very strong, but
+# it may help in cases where the system pool is not initialized properly.
+# However, it is very strongly recommended that the system pool is initialized
+# with enough entropy either by using hardware assisted random number
+# generator or by storing state over device reboots.
+#
+# hostapd can be configured to maintain its own entropy store over restarts to
+# enhance random number generation. This is not perfect, but it is much more
+# secure than using the same sequence of random numbers after every reboot.
+# This can be enabled with -e<entropy file> command line option. The specified
+# file needs to be readable and writable by hostapd.
+#
+# If the os_get_random() is known to provide strong random data (e.g., on
+# Linux/BSD, the board in question is known to have reliable source of random
+# data from /dev/urandom), the internal hostapd random pool can be disabled.
+# This will save some in binary size and CPU use. However, this should only be
+# considered for builds that are known to be used on devices that meet the
+# requirements described above.
+#CONFIG_NO_RANDOM_POOL=y
+
+# Select TLS implementation
+# openssl = OpenSSL (default)
+# gnutls = GnuTLS
+# internal = Internal TLSv1 implementation (experimental)
+# none = Empty template
+#CONFIG_TLS=openssl
+
+# TLS-based EAP methods require at least TLS v1.0. Newer version of TLS (v1.1)
+# can be enabled to get a stronger construction of messages when block ciphers
+# are used.
+#CONFIG_TLSV11=y
+
+# TLS-based EAP methods require at least TLS v1.0. Newer version of TLS (v1.2)
+# can be enabled to enable use of stronger crypto algorithms.
+#CONFIG_TLSV12=y
+
+# If CONFIG_TLS=internal is used, additional library and include paths are
+# needed for LibTomMath. Alternatively, an integrated, minimal version of
+# LibTomMath can be used. See beginning of libtommath.c for details on benefits
+# and drawbacks of this option.
+#CONFIG_INTERNAL_LIBTOMMATH=y
+#ifndef CONFIG_INTERNAL_LIBTOMMATH
+#LTM_PATH=/usr/src/libtommath-0.39
+#CFLAGS += -I$(LTM_PATH)
+#LIBS += -L$(LTM_PATH)
+#LIBS_p += -L$(LTM_PATH)
+#endif
+# At the cost of about 4 kB of additional binary size, the internal LibTomMath
+# can be configured to include faster routines for exptmod, sqr, and div to
+# speed up DH and RSA calculation considerably
+#CONFIG_INTERNAL_LIBTOMMATH_FAST=y
+
+# Interworking (IEEE 802.11u)
+# This can be used to enable functionality to improve interworking with
+# external networks.
+#CONFIG_INTERWORKING=y
+
+# Hotspot 2.0
+#CONFIG_HS20=y
+
+# Enable SQLite database support in hlr_auc_gw, EAP-SIM DB, and eap_user_file
+#CONFIG_SQLITE=y
diff --git a/contrib/wpa/hostapd/dump_state.c b/contrib/wpa/hostapd/dump_state.c
index bba3068..d33e05f 100644
--- a/contrib/wpa/hostapd/dump_state.c
+++ b/contrib/wpa/hostapd/dump_state.c
@@ -2,17 +2,12 @@
* hostapd / State dump
* Copyright (c) 2002-2009, Jouni Malinen <j@w1.fi>
*
- * This program is free software; you can redistribute it and/or modify
- * it under the terms of the GNU General Public License version 2 as
- * published by the Free Software Foundation.
- *
- * Alternatively, this software may be distributed under the terms of BSD
- * license.
- *
- * See README and COPYING for more details.
+ * This software may be distributed under the terms of the BSD license.
+ * See README for more details.
*/
#include "utils/includes.h"
+#include <time.h>
#include "utils/common.h"
#include "radius/radius_client.h"
@@ -106,7 +101,8 @@ static void hostapd_dump_state(struct hostapd_data *hapd)
fprintf(f, "\nSTA=" MACSTR "\n", MAC2STR(sta->addr));
fprintf(f,
- " AID=%d flags=0x%x %s%s%s%s%s%s%s%s%s%s%s%s%s%s%s\n"
+ " AID=%d flags=0x%x %s%s%s%s%s%s%s%s%s%s%s%s%s%s%s%s"
+ "\n"
" capability=0x%x listen_interval=%d\n",
sta->aid,
sta->flags,
@@ -115,8 +111,7 @@ static void hostapd_dump_state(struct hostapd_data *hapd)
(sta->flags & WLAN_STA_PS ? "[PS]" : ""),
(sta->flags & WLAN_STA_TIM ? "[TIM]" : ""),
(sta->flags & WLAN_STA_PERM ? "[PERM]" : ""),
- (sta->flags & WLAN_STA_AUTHORIZED ? "[AUTHORIZED]" :
- ""),
+ (ap_sta_is_authorized(sta) ? "[AUTHORIZED]" : ""),
(sta->flags & WLAN_STA_PENDING_POLL ? "[PENDING_POLL" :
""),
(sta->flags & WLAN_STA_SHORT_PREAMBLE ?
@@ -128,6 +123,7 @@ static void hostapd_dump_state(struct hostapd_data *hapd)
(sta->flags & WLAN_STA_MAYBE_WPS ? "[MAYBE_WPS]" : ""),
(sta->flags & WLAN_STA_WDS ? "[WDS]" : ""),
(sta->flags & WLAN_STA_NONERP ? "[NonERP]" : ""),
+ (sta->flags & WLAN_STA_WPS2 ? "[WPS2]" : ""),
sta->capability,
sta->listen_interval);
diff --git a/contrib/wpa/hostapd/dump_state.h b/contrib/wpa/hostapd/dump_state.h
index e14f08a..a209d65 100644
--- a/contrib/wpa/hostapd/dump_state.h
+++ b/contrib/wpa/hostapd/dump_state.h
@@ -2,14 +2,8 @@
* hostapd / State dump
* Copyright (c) 2002-2009, Jouni Malinen <j@w1.fi>
*
- * This program is free software; you can redistribute it and/or modify
- * it under the terms of the GNU General Public License version 2 as
- * published by the Free Software Foundation.
- *
- * Alternatively, this software may be distributed under the terms of BSD
- * license.
- *
- * See README and COPYING for more details.
+ * This software may be distributed under the terms of the BSD license.
+ * See README for more details.
*/
#ifndef DUMP_STATE_H
diff --git a/contrib/wpa/hostapd/eap_register.c b/contrib/wpa/hostapd/eap_register.c
index ae9bf9d..0a7ff91 100644
--- a/contrib/wpa/hostapd/eap_register.c
+++ b/contrib/wpa/hostapd/eap_register.c
@@ -2,14 +2,8 @@
* EAP method registration
* Copyright (c) 2004-2009, Jouni Malinen <j@w1.fi>
*
- * This program is free software; you can redistribute it and/or modify
- * it under the terms of the GNU General Public License version 2 as
- * published by the Free Software Foundation.
- *
- * Alternatively, this software may be distributed under the terms of BSD
- * license.
- *
- * See README and COPYING for more details.
+ * This software may be distributed under the terms of the BSD license.
+ * See README for more details.
*/
#include "includes.h"
@@ -45,6 +39,11 @@ int eap_server_register_methods(void)
ret = eap_server_tls_register();
#endif /* EAP_SERVER_TLS */
+#ifdef EAP_SERVER_UNAUTH_TLS
+ if (ret == 0)
+ ret = eap_server_unauth_tls_register();
+#endif /* EAP_SERVER_TLS */
+
#ifdef EAP_SERVER_MSCHAPV2
if (ret == 0)
ret = eap_server_mschapv2_register();
@@ -130,5 +129,10 @@ int eap_server_register_methods(void)
ret = eap_server_tnc_register();
#endif /* EAP_SERVER_TNC */
+#ifdef EAP_SERVER_PWD
+ if (ret == 0)
+ ret = eap_server_pwd_register();
+#endif /* EAP_SERVER_PWD */
+
return ret;
}
diff --git a/contrib/wpa/hostapd/eap_register.h b/contrib/wpa/hostapd/eap_register.h
index 82e7171..c342351 100644
--- a/contrib/wpa/hostapd/eap_register.h
+++ b/contrib/wpa/hostapd/eap_register.h
@@ -2,14 +2,8 @@
* EAP method registration
* Copyright (c) 2004-2009, Jouni Malinen <j@w1.fi>
*
- * This program is free software; you can redistribute it and/or modify
- * it under the terms of the GNU General Public License version 2 as
- * published by the Free Software Foundation.
- *
- * Alternatively, this software may be distributed under the terms of BSD
- * license.
- *
- * See README and COPYING for more details.
+ * This software may be distributed under the terms of the BSD license.
+ * See README for more details.
*/
#ifndef EAP_REGISTER_H
diff --git a/contrib/wpa/hostapd/hlr_auc_gw.c b/contrib/wpa/hostapd/hlr_auc_gw.c
index 36934aa..e04e2e9 100644
--- a/contrib/wpa/hostapd/hlr_auc_gw.c
+++ b/contrib/wpa/hostapd/hlr_auc_gw.c
@@ -1,15 +1,9 @@
/*
* HLR/AuC testing gateway for hostapd EAP-SIM/AKA database/authenticator
- * Copyright (c) 2005-2007, Jouni Malinen <j@w1.fi>
+ * Copyright (c) 2005-2007, 2012, Jouni Malinen <j@w1.fi>
*
- * This program is free software; you can redistribute it and/or modify
- * it under the terms of the GNU General Public License version 2 as
- * published by the Free Software Foundation.
- *
- * Alternatively, this software may be distributed under the terms of BSD
- * license.
- *
- * See README and COPYING for more details.
+ * This software may be distributed under the terms of the BSD license.
+ * See README for more details.
*
* This is an example implementation of the EAP-SIM/AKA database/authentication
* gateway interface to HLR/AuC. It is expected to be replaced with an
@@ -40,18 +34,30 @@
* text file in IMSI:Kc:SRES:RAND format, IMSI in ASCII, other fields as hex
* strings. This is used to simulate an HLR/AuC. As such, it is not very useful
* for real life authentication, but it is useful both as an example
- * implementation and for EAP-SIM testing.
+ * implementation and for EAP-SIM/AKA/AKA' testing.
+ *
+ * SQN generation follows the not time-based Profile 2 described in
+ * 3GPP TS 33.102 Annex C.3.2. The length of IND is 5 bits by default, but this
+ * can be changed with a command line options if needed.
*/
#include "includes.h"
#include <sys/un.h>
+#ifdef CONFIG_SQLITE
+#include <sqlite3.h>
+#endif /* CONFIG_SQLITE */
#include "common.h"
#include "crypto/milenage.h"
+#include "crypto/random.h"
static const char *default_socket_path = "/tmp/hlr_auc_gw.sock";
static const char *socket_path;
static int serv_sock = -1;
+static char *milenage_file = NULL;
+static int update_milenage = 0;
+static int sqn_changes = 0;
+static int ind_len = 5;
/* GSM triplets */
struct gsm_triplet {
@@ -72,6 +78,7 @@ struct milenage_parameters {
u8 opc[16];
u8 amf[2];
u8 sqn[6];
+ int set;
};
static struct milenage_parameters *milenage_db = NULL;
@@ -86,6 +93,144 @@ static struct milenage_parameters *milenage_db = NULL;
#define EAP_AKA_CK_LEN 16
+#ifdef CONFIG_SQLITE
+
+static sqlite3 *sqlite_db = NULL;
+static struct milenage_parameters db_tmp_milenage;
+
+
+static int db_table_exists(sqlite3 *db, const char *name)
+{
+ char cmd[128];
+ os_snprintf(cmd, sizeof(cmd), "SELECT 1 FROM %s;", name);
+ return sqlite3_exec(db, cmd, NULL, NULL, NULL) == SQLITE_OK;
+}
+
+
+static int db_table_create_milenage(sqlite3 *db)
+{
+ char *err = NULL;
+ const char *sql =
+ "CREATE TABLE milenage("
+ " imsi INTEGER PRIMARY KEY NOT NULL,"
+ " ki CHAR(32) NOT NULL,"
+ " opc CHAR(32) NOT NULL,"
+ " amf CHAR(4) NOT NULL,"
+ " sqn CHAR(12) NOT NULL"
+ ");";
+
+ printf("Adding database table for milenage information\n");
+ if (sqlite3_exec(db, sql, NULL, NULL, &err) != SQLITE_OK) {
+ printf("SQLite error: %s\n", err);
+ sqlite3_free(err);
+ return -1;
+ }
+
+ return 0;
+}
+
+
+static sqlite3 * db_open(const char *db_file)
+{
+ sqlite3 *db;
+
+ if (sqlite3_open(db_file, &db)) {
+ printf("Failed to open database %s: %s\n",
+ db_file, sqlite3_errmsg(db));
+ sqlite3_close(db);
+ return NULL;
+ }
+
+ if (!db_table_exists(db, "milenage") &&
+ db_table_create_milenage(db) < 0) {
+ sqlite3_close(db);
+ return NULL;
+ }
+
+ return db;
+}
+
+
+static int get_milenage_cb(void *ctx, int argc, char *argv[], char *col[])
+{
+ struct milenage_parameters *m = ctx;
+ int i;
+
+ m->set = 1;
+
+ for (i = 0; i < argc; i++) {
+ if (os_strcmp(col[i], "ki") == 0 && argv[i] &&
+ hexstr2bin(argv[i], m->ki, sizeof(m->ki))) {
+ printf("Invalid ki value in database\n");
+ return -1;
+ }
+
+ if (os_strcmp(col[i], "opc") == 0 && argv[i] &&
+ hexstr2bin(argv[i], m->opc, sizeof(m->opc))) {
+ printf("Invalid opcvalue in database\n");
+ return -1;
+ }
+
+ if (os_strcmp(col[i], "amf") == 0 && argv[i] &&
+ hexstr2bin(argv[i], m->amf, sizeof(m->amf))) {
+ printf("Invalid amf value in database\n");
+ return -1;
+ }
+
+ if (os_strcmp(col[i], "sqn") == 0 && argv[i] &&
+ hexstr2bin(argv[i], m->sqn, sizeof(m->sqn))) {
+ printf("Invalid sqn value in database\n");
+ return -1;
+ }
+ }
+
+ return 0;
+}
+
+
+static struct milenage_parameters * db_get_milenage(const char *imsi_txt)
+{
+ char cmd[128];
+ unsigned long long imsi;
+
+ os_memset(&db_tmp_milenage, 0, sizeof(db_tmp_milenage));
+ imsi = atoll(imsi_txt);
+ os_snprintf(db_tmp_milenage.imsi, sizeof(db_tmp_milenage.imsi),
+ "%llu", imsi);
+ os_snprintf(cmd, sizeof(cmd),
+ "SELECT ki,opc,amf,sqn FROM milenage WHERE imsi=%llu;",
+ imsi);
+ if (sqlite3_exec(sqlite_db, cmd, get_milenage_cb, &db_tmp_milenage,
+ NULL) != SQLITE_OK)
+ return NULL;
+
+ if (!db_tmp_milenage.set)
+ return NULL;
+ return &db_tmp_milenage;
+}
+
+
+static int db_update_milenage_sqn(struct milenage_parameters *m)
+{
+ char cmd[128], val[13], *pos;
+
+ pos = val;
+ pos += wpa_snprintf_hex(pos, sizeof(val), m->sqn, 6);
+ *pos = '\0';
+ os_snprintf(cmd, sizeof(cmd),
+ "UPDATE milenage SET sqn='%s' WHERE imsi=%s;",
+ val, m->imsi);
+ if (sqlite3_exec(sqlite_db, cmd, NULL, NULL, NULL) != SQLITE_OK) {
+ printf("Failed to update SQN in database for IMSI %s\n",
+ m->imsi);
+ return -1;
+ }
+ return 0;
+}
+
+#endif /* CONFIG_SQLITE */
+
+
static int open_socket(const char *path)
{
struct sockaddr_un addr;
@@ -101,7 +246,7 @@ static int open_socket(const char *path)
addr.sun_family = AF_UNIX;
os_strlcpy(addr.sun_path, path, sizeof(addr.sun_path));
if (bind(s, (struct sockaddr *) &addr, sizeof(addr)) < 0) {
- perror("bind(PF_UNIX)");
+ perror("hlr-auc-gw: bind(PF_UNIX)");
close(s);
return -1;
}
@@ -215,7 +360,7 @@ static int read_gsm_triplets(const char *fname)
gsm_db = g;
g = NULL;
}
- free(g);
+ os_free(g);
fclose(f);
@@ -365,7 +510,7 @@ static int read_milenage(const char *fname)
milenage_db = m;
m = NULL;
}
- free(m);
+ os_free(m);
fclose(f);
@@ -373,6 +518,80 @@ static int read_milenage(const char *fname)
}
+static void update_milenage_file(const char *fname)
+{
+ FILE *f, *f2;
+ char buf[500], *pos;
+ char *end = buf + sizeof(buf);
+ struct milenage_parameters *m;
+ size_t imsi_len;
+
+ f = fopen(fname, "r");
+ if (f == NULL) {
+ printf("Could not open Milenage data file '%s'\n", fname);
+ return;
+ }
+
+ snprintf(buf, sizeof(buf), "%s.new", fname);
+ f2 = fopen(buf, "w");
+ if (f2 == NULL) {
+ printf("Could not write Milenage data file '%s'\n", buf);
+ fclose(f);
+ return;
+ }
+
+ while (fgets(buf, sizeof(buf), f)) {
+ /* IMSI Ki OPc AMF SQN */
+ buf[sizeof(buf) - 1] = '\0';
+
+ pos = strchr(buf, ' ');
+ if (buf[0] == '#' || pos == NULL || pos - buf >= 20)
+ goto no_update;
+
+ imsi_len = pos - buf;
+
+ for (m = milenage_db; m; m = m->next) {
+ if (strncmp(buf, m->imsi, imsi_len) == 0 &&
+ m->imsi[imsi_len] == '\0')
+ break;
+ }
+
+ if (!m)
+ goto no_update;
+
+ pos = buf;
+ pos += snprintf(pos, end - pos, "%s ", m->imsi);
+ pos += wpa_snprintf_hex(pos, end - pos, m->ki, 16);
+ *pos++ = ' ';
+ pos += wpa_snprintf_hex(pos, end - pos, m->opc, 16);
+ *pos++ = ' ';
+ pos += wpa_snprintf_hex(pos, end - pos, m->amf, 2);
+ *pos++ = ' ';
+ pos += wpa_snprintf_hex(pos, end - pos, m->sqn, 6);
+ *pos++ = '\n';
+
+ no_update:
+ fprintf(f2, "%s", buf);
+ }
+
+ fclose(f2);
+ fclose(f);
+
+ snprintf(buf, sizeof(buf), "%s.bak", fname);
+ if (rename(fname, buf) < 0) {
+ perror("rename");
+ return;
+ }
+
+ snprintf(buf, sizeof(buf), "%s.new", fname);
+ if (rename(buf, fname) < 0) {
+ perror("rename");
+ return;
+ }
+
+}
+
+
static struct milenage_parameters * get_milenage(const char *imsi)
{
struct milenage_parameters *m = milenage_db;
@@ -383,6 +602,11 @@ static struct milenage_parameters * get_milenage(const char *imsi)
m = m->next;
}
+#ifdef CONFIG_SQLITE
+ if (!m)
+ m = db_get_milenage(imsi);
+#endif /* CONFIG_SQLITE */
+
return m;
}
@@ -418,7 +642,7 @@ static void sim_req_auth(int s, struct sockaddr_un *from, socklen_t fromlen,
if (m) {
u8 _rand[16], sres[4], kc[8];
for (count = 0; count < max_chal; count++) {
- if (os_get_random(_rand, 16) < 0)
+ if (random_get_bytes(_rand, 16) < 0)
return;
gsm_milenage(m->opc, m->ki, _rand, sres, kc);
*rpos++ = ' ';
@@ -465,6 +689,28 @@ send:
}
+static void inc_sqn(u8 *sqn)
+{
+ u64 val, seq, ind;
+
+ /*
+ * SQN = SEQ | IND = SEQ1 | SEQ2 | IND
+ *
+ * The mechanism used here is not time-based, so SEQ2 is void and
+ * SQN = SEQ1 | IND. The length of IND is ind_len bits and the length
+ * of SEQ1 is 48 - ind_len bits.
+ */
+
+ /* Increment both SEQ and IND by one */
+ val = ((u64) WPA_GET_BE32(sqn) << 16) | ((u64) WPA_GET_BE16(sqn + 4));
+ seq = (val >> ind_len) + 1;
+ ind = (val + 1) & ((1 << ind_len) - 1);
+ val = (seq << ind_len) | ind;
+ WPA_PUT_BE32(sqn, val >> 16);
+ WPA_PUT_BE16(sqn + 4, val & 0xffff);
+}
+
+
static void aka_req_auth(int s, struct sockaddr_un *from, socklen_t fromlen,
char *imsi)
{
@@ -478,13 +724,18 @@ static void aka_req_auth(int s, struct sockaddr_un *from, socklen_t fromlen,
size_t res_len;
int ret;
struct milenage_parameters *m;
+ int failed = 0;
m = get_milenage(imsi);
if (m) {
- if (os_get_random(_rand, EAP_AKA_RAND_LEN) < 0)
+ if (random_get_bytes(_rand, EAP_AKA_RAND_LEN) < 0)
return;
res_len = EAP_AKA_RES_MAX_LEN;
- inc_byte_array(m->sqn, 6);
+ inc_sqn(m->sqn);
+#ifdef CONFIG_SQLITE
+ db_update_milenage_sqn(m);
+#endif /* CONFIG_SQLITE */
+ sqn_changes = 1;
printf("AKA: Milenage with SQN=%02x%02x%02x%02x%02x%02x\n",
m->sqn[0], m->sqn[1], m->sqn[2],
m->sqn[3], m->sqn[4], m->sqn[5]);
@@ -501,7 +752,7 @@ static void aka_req_auth(int s, struct sockaddr_un *from, socklen_t fromlen,
memset(res, '2', EAP_AKA_RES_MAX_LEN);
res_len = EAP_AKA_RES_MAX_LEN;
#else /* AKA_USE_FIXED_TEST_VALUES */
- return;
+ failed = 1;
#endif /* AKA_USE_FIXED_TEST_VALUES */
}
@@ -511,6 +762,13 @@ static void aka_req_auth(int s, struct sockaddr_un *from, socklen_t fromlen,
if (ret < 0 || ret >= end - pos)
return;
pos += ret;
+ if (failed) {
+ ret = snprintf(pos, end - pos, "FAILURE");
+ if (ret < 0 || ret >= end - pos)
+ return;
+ pos += ret;
+ goto done;
+ }
pos += wpa_snprintf_hex(pos, end - pos, _rand, EAP_AKA_RAND_LEN);
*pos++ = ' ';
pos += wpa_snprintf_hex(pos, end - pos, autn, EAP_AKA_AUTN_LEN);
@@ -521,6 +779,7 @@ static void aka_req_auth(int s, struct sockaddr_un *from, socklen_t fromlen,
*pos++ = ' ';
pos += wpa_snprintf_hex(pos, end - pos, res, res_len);
+done:
printf("Send: %s\n", reply);
if (sendto(s, reply, pos - reply, 0, (struct sockaddr *) from,
@@ -568,6 +827,10 @@ static void aka_auts(int s, struct sockaddr_un *from, socklen_t fromlen,
printf("AKA-AUTS: Re-synchronized: "
"SQN=%02x%02x%02x%02x%02x%02x\n",
sqn[0], sqn[1], sqn[2], sqn[3], sqn[4], sqn[5]);
+#ifdef CONFIG_SQLITE
+ db_update_milenage_sqn(m);
+#endif /* CONFIG_SQLITE */
+ sqn_changes = 1;
}
}
@@ -614,22 +877,32 @@ static void cleanup(void)
struct gsm_triplet *g, *gprev;
struct milenage_parameters *m, *prev;
+ if (update_milenage && milenage_file && sqn_changes)
+ update_milenage_file(milenage_file);
+
g = gsm_db;
while (g) {
gprev = g;
g = g->next;
- free(gprev);
+ os_free(gprev);
}
m = milenage_db;
while (m) {
prev = m;
m = m->next;
- free(prev);
+ os_free(prev);
}
close(serv_sock);
unlink(socket_path);
+
+#ifdef CONFIG_SQLITE
+ if (sqlite_db) {
+ sqlite3_close(sqlite_db);
+ sqlite_db = NULL;
+ }
+#endif /* CONFIG_SQLITE */
}
@@ -644,18 +917,22 @@ static void usage(void)
{
printf("HLR/AuC testing gateway for hostapd EAP-SIM/AKA "
"database/authenticator\n"
- "Copyright (c) 2005-2007, Jouni Malinen <j@w1.fi>\n"
+ "Copyright (c) 2005-2007, 2012, Jouni Malinen <j@w1.fi>\n"
"\n"
"usage:\n"
- "hlr_auc_gw [-h] [-s<socket path>] [-g<triplet file>] "
- "[-m<milenage file>]\n"
+ "hlr_auc_gw [-hu] [-s<socket path>] [-g<triplet file>] "
+ "[-m<milenage file>] \\\n"
+ " [-D<DB file>] [-i<IND len in bits>]\n"
"\n"
"options:\n"
" -h = show this usage help\n"
+ " -u = update SQN in Milenage file on exit\n"
" -s<socket path> = path for UNIX domain socket\n"
" (default: %s)\n"
" -g<triplet file> = path for GSM authentication triplets\n"
- " -m<milenage file> = path for Milenage keys\n",
+ " -m<milenage file> = path for Milenage keys\n"
+ " -D<DB file> = path to SQLite database\n"
+ " -i<IND len in bits> = IND length for SQN (default: 5)\n",
default_socket_path);
}
@@ -663,34 +940,65 @@ static void usage(void)
int main(int argc, char *argv[])
{
int c;
- char *milenage_file = NULL;
char *gsm_triplet_file = NULL;
+ char *sqlite_db_file = NULL;
+
+ if (os_program_init())
+ return -1;
socket_path = default_socket_path;
for (;;) {
- c = getopt(argc, argv, "g:hm:s:");
+ c = getopt(argc, argv, "D:g:hi:m:s:u");
if (c < 0)
break;
switch (c) {
+ case 'D':
+#ifdef CONFIG_SQLITE
+ sqlite_db_file = optarg;
+ break;
+#else /* CONFIG_SQLITE */
+ printf("No SQLite support included in the build\n");
+ return -1;
+#endif /* CONFIG_SQLITE */
case 'g':
gsm_triplet_file = optarg;
break;
case 'h':
usage();
return 0;
+ case 'i':
+ ind_len = atoi(optarg);
+ if (ind_len < 0 || ind_len > 32) {
+ printf("Invalid IND length\n");
+ return -1;
+ }
+ break;
case 'm':
milenage_file = optarg;
break;
case 's':
socket_path = optarg;
break;
+ case 'u':
+ update_milenage = 1;
+ break;
default:
usage();
return -1;
}
}
+ if (!gsm_triplet_file && !milenage_file && !sqlite_db_file) {
+ usage();
+ return -1;
+ }
+
+#ifdef CONFIG_SQLITE
+ if (sqlite_db_file && (sqlite_db = db_open(sqlite_db_file)) == NULL)
+ return -1;
+#endif /* CONFIG_SQLITE */
+
if (gsm_triplet_file && read_gsm_triplets(gsm_triplet_file) < 0)
return -1;
@@ -710,5 +1018,14 @@ int main(int argc, char *argv[])
for (;;)
process(serv_sock);
+#ifdef CONFIG_SQLITE
+ if (sqlite_db) {
+ sqlite3_close(sqlite_db);
+ sqlite_db = NULL;
+ }
+#endif /* CONFIG_SQLITE */
+
+ os_program_deinit();
+
return 0;
}
diff --git a/contrib/wpa/hostapd/hlr_auc_gw.txt b/contrib/wpa/hostapd/hlr_auc_gw.txt
new file mode 100644
index 0000000..097bbce
--- /dev/null
+++ b/contrib/wpa/hostapd/hlr_auc_gw.txt
@@ -0,0 +1,104 @@
+HLR/AuC testing gateway for hostapd EAP-SIM/AKA database/authenticator
+
+hlr_auc_gw is an example implementation of the EAP-SIM/AKA/AKA'
+database/authentication gateway interface to HLR/AuC. It could be
+replaced with an implementation of SS7 gateway to GSM/UMTS
+authentication center (HLR/AuC). hostapd will send SIM/AKA
+authentication queries over a UNIX domain socket to and external
+program, e.g., hlr_auc_gw.
+
+hlr_auc_gw can be configured with GSM and UMTS authentication data with
+text files: GSM triplet file (see hostapd.sim_db) and Milenage file (see
+hlr_auc_gw.milenage_db). Milenage parameters can be used to generate
+dynamic authentication data for EAP-SIM, EAP-AKA, and EAP-AKA' while the
+GSM triplet data is used for a more static configuration (e.g., triplets
+extracted from a SIM card).
+
+Alternatively, hlr_auc_gw can be built with support for an SQLite
+database for more dynamic operations. This is enabled by adding
+"CONFIG_SQLITE=y" into hostapd/.config before building hlr_auc_gw ("make
+clean; make hlr_auc_gw" in this directory).
+
+hostapd is configured to use hlr_auc_gw with the eap_sim_db parameter in
+hostapd.conf (e.g., "eap_sim_db=unix:/tmp/hlr_auc_gw.sock"). hlr_auc_gw
+is configured with command line parameters:
+
+hlr_auc_gw [-hu] [-s<socket path>] [-g<triplet file>] [-m<milenage file>] \
+ [-D<DB file>] [-i<IND len in bits>]
+
+options:
+ -h = show this usage help
+ -u = update SQN in Milenage file on exit
+ -s<socket path> = path for UNIX domain socket
+ (default: /tmp/hlr_auc_gw.sock)
+ -g<triplet file> = path for GSM authentication triplets
+ -m<milenage file> = path for Milenage keys
+ -D<DB file> = path to SQLite database
+ -i<IND len in bits> = IND length for SQN (default: 5)
+
+
+The SQLite database can be initialized with sqlite, e.g., by running
+following commands in "sqlite3 /path/to/hlr_auc_gw.db":
+
+CREATE TABLE milenage(
+ imsi INTEGER PRIMARY KEY NOT NULL,
+ ki CHAR(32) NOT NULL,
+ opc CHAR(32) NOT NULL,
+ amf CHAR(4) NOT NULL,
+ sqn CHAR(12) NOT NULL
+);
+INSERT INTO milenage(imsi,ki,opc,amf,sqn) VALUES(
+ 232010000000000,
+ '90dca4eda45b53cf0f12d7c9c3bc6a89',
+ 'cb9cccc4b9258e6dca4760379fb82581',
+ '61df',
+ '000000000000'
+);
+INSERT INTO milenage(imsi,ki,opc,amf,sqn) VALUES(
+ 555444333222111,
+ '5122250214c33e723a5dd523fc145fc0',
+ '981d464c7c52eb6e5036234984ad0bcf',
+ 'c3ab',
+ '16f3b3f70fc1'
+);
+
+
+hostapd (EAP server) can also be configured to store the EAP-SIM/AKA
+pseudonyms and reauth information into a SQLite database. This is
+configured with the db parameter within the eap_sim_db configuration
+option.
+
+
+"hlr_auc_gw -D /path/to/hlr_auc_gw.db" can then be used to fetch
+Milenage parameters based on IMSI from the database. The database can be
+updated dynamically while hlr_auc_gw is running to add/remove/modify
+entries.
+
+
+Example configuration files for hostapd to operate as a RADIUS
+authentication server for EAP-SIM/AKA/AKA':
+
+hostapd.conf:
+
+driver=none
+radius_server_clients=hostapd.radius_clients
+eap_server=1
+eap_user_file=hostapd.eap_user
+eap_sim_db=unix:/tmp/hlr_auc_gw.sock db=/tmp/eap_sim.db
+eap_sim_aka_result_ind=1
+
+hostapd.radius_clients:
+
+0.0.0.0/0 radius
+
+hostapd.eap_user:
+
+"0"* AKA
+"1"* SIM
+"2"* AKA
+"3"* SIM
+"4"* AKA
+"5"* SIM
+"6"* AKA'
+"7"* AKA'
+"8"* AKA'
diff --git a/contrib/wpa/hostapd/hostapd.conf b/contrib/wpa/hostapd/hostapd.conf
index b44a818..75b1941 100644
--- a/contrib/wpa/hostapd/hostapd.conf
+++ b/contrib/wpa/hostapd/hostapd.conf
@@ -84,6 +84,14 @@ ctrl_interface_group=0
# SSID to be used in IEEE 802.11 management frames
ssid=test
+# Alternative formats for configuring SSID
+# (double quoted string, hexdump, printf-escaped string)
+#ssid2="test"
+#ssid2=74657374
+#ssid2=P"hello\nthere"
+
+# UTF-8 SSID: Whether the SSID is to be interpreted using UTF-8 encoding
+#utf8_ssid=1
# Country code (ISO/IEC 3166-1). Used to set regulatory domain.
# Set as needed to indicate country in which device is operating.
@@ -98,20 +106,21 @@ ssid=test
#ieee80211d=1
# Operation mode (a = IEEE 802.11a, b = IEEE 802.11b, g = IEEE 802.11g,
+# ad = IEEE 802.11ad (60 GHz); a/g options are used with IEEE 802.11n, too, to
+# specify band)
# Default: IEEE 802.11b
-hw_mode=a
+hw_mode=g
# Channel number (IEEE 802.11)
# (default: 0, i.e., not set)
-# Please note that some drivers (e.g., madwifi) do not use this value from
-# hostapd and the channel will need to be configuration separately with
-# iwconfig.
-channel=60
+# Please note that some drivers do not use this value from hostapd and the
+# channel will need to be configured separately with iwconfig.
+channel=1
# Beacon interval in kus (1.024 ms) (default: 100; range 15..65535)
beacon_int=100
-# DTIM (delivery trafic information message) period (range 1..255):
+# DTIM (delivery traffic information message) period (range 1..255):
# number of beacons between DTIMs (1 = every beacon includes DTIM element)
# (default: 2)
dtim_period=2
@@ -197,8 +206,14 @@ auth_algs=3
# requests for broadcast SSID
ignore_broadcast_ssid=0
+# Additional vendor specfic elements for Beacon and Probe Response frames
+# This parameter can be used to add additional vendor specific element(s) into
+# the end of the Beacon and Probe Response frames. The format for these
+# element(s) is a hexdump of the raw information elements (id+len+payload for
+# one or more elements)
+#vendor_elements=dd0411223301
+
# TX queue parameters (EDCF / bursting)
-# default for all these fields: not set, use hardware defaults
# tx_queue_<queue name>_<param>
# queues: data0, data1, data2, data3, after_beacon, beacon
# (data0 is the highest priority queue)
@@ -240,18 +255,6 @@ ignore_broadcast_ssid=0
#tx_queue_data0_cwmax=7
#tx_queue_data0_burst=1.5
# Note: for IEEE 802.11b mode: cWmin=7 cWmax=15 burst=3.3
-#
-# Special queues; normally not user configurable
-#
-#tx_queue_after_beacon_aifs=2
-#tx_queue_after_beacon_cwmin=15
-#tx_queue_after_beacon_cwmax=1023
-#tx_queue_after_beacon_burst=0
-#
-#tx_queue_beacon_aifs=2
-#tx_queue_beacon_cwmin=3
-#tx_queue_beacon_cwmax=7
-#tx_queue_beacon_burst=1.5
# 802.1D Tag (= UP) to AC mappings
# WMM specifies following mapping of data frames to different ACs. This mapping
@@ -353,6 +356,17 @@ wmm_ac_vo_acm=0
# the STA with a data frame.
# default: 300 (i.e., 5 minutes)
#ap_max_inactivity=300
+#
+# The inactivity polling can be disabled to disconnect stations based on
+# inactivity timeout so that idle stations are more likely to be disconnected
+# even if they are still in range of the AP. This can be done by setting
+# skip_inactivity_poll to 1 (default 0).
+#skip_inactivity_poll=0
+
+# Disassociate stations based on excessive transmission failures or other
+# indications of connection loss. This depends on the driver capabilities and
+# may not be available with all drivers.
+#disassoc_low_ack=1
# Maximum allowed Listen Interval (how many Beacon periods STAs are allowed to
# remain asleep). Default: 65535 (no limit apart from field size)
@@ -364,6 +378,15 @@ wmm_ac_vo_acm=0
# bridging to be used.
#wds_sta=1
+# If bridge parameter is set, the WDS STA interface will be added to the same
+# bridge by default. This can be overridden with the wds_bridge parameter to
+# use a separate bridge.
+#wds_bridge=wds-br0
+
+# Client isolation can be used to prevent low-level bridging of frames between
+# associated stations in the BSS. By default, this bridging is allowed.
+#ap_isolate=1
+
##### IEEE 802.11n related configuration ######################################
# ieee80211n: Whether IEEE 802.11n (HT) is enabled
@@ -407,6 +430,160 @@ wmm_ac_vo_acm=0
# L-SIG TXOP protection support: [LSIG-TXOP-PROT] (disabled if not set)
#ht_capab=[HT40-][SHORT-GI-20][SHORT-GI-40]
+# Require stations to support HT PHY (reject association if they do not)
+#require_ht=1
+
+##### IEEE 802.11ac related configuration #####################################
+
+# ieee80211ac: Whether IEEE 802.11ac (VHT) is enabled
+# 0 = disabled (default)
+# 1 = enabled
+# Note: You will also need to enable WMM for full VHT functionality.
+#ieee80211ac=1
+
+# vht_capab: VHT capabilities (list of flags)
+#
+# vht_max_mpdu_len: [MAX-MPDU-7991] [MAX-MPDU-11454]
+# Indicates maximum MPDU length
+# 0 = 3895 octets (default)
+# 1 = 7991 octets
+# 2 = 11454 octets
+# 3 = reserved
+#
+# supported_chan_width: [VHT160] [VHT160-80PLUS80]
+# Indicates supported Channel widths
+# 0 = 160 MHz & 80+80 channel widths are not supported (default)
+# 1 = 160 MHz channel width is supported
+# 2 = 160 MHz & 80+80 channel widths are supported
+# 3 = reserved
+#
+# Rx LDPC coding capability: [RXLDPC]
+# Indicates support for receiving LDPC coded pkts
+# 0 = Not supported (default)
+# 1 = Supported
+#
+# Short GI for 80 MHz: [SHORT-GI-80]
+# Indicates short GI support for reception of packets transmitted with TXVECTOR
+# params format equal to VHT and CBW = 80Mhz
+# 0 = Not supported (default)
+# 1 = Supported
+#
+# Short GI for 160 MHz: [SHORT-GI-160]
+# Indicates short GI support for reception of packets transmitted with TXVECTOR
+# params format equal to VHT and CBW = 160Mhz
+# 0 = Not supported (default)
+# 1 = Supported
+#
+# Tx STBC: [TX-STBC-2BY1]
+# Indicates support for the transmission of at least 2x1 STBC
+# 0 = Not supported (default)
+# 1 = Supported
+#
+# Rx STBC: [RX-STBC-1] [RX-STBC-12] [RX-STBC-123] [RX-STBC-1234]
+# Indicates support for the reception of PPDUs using STBC
+# 0 = Not supported (default)
+# 1 = support of one spatial stream
+# 2 = support of one and two spatial streams
+# 3 = support of one, two and three spatial streams
+# 4 = support of one, two, three and four spatial streams
+# 5,6,7 = reserved
+#
+# SU Beamformer Capable: [SU-BEAMFORMER]
+# Indicates support for operation as a single user beamformer
+# 0 = Not supported (default)
+# 1 = Supported
+#
+# SU Beamformee Capable: [SU-BEAMFORMEE]
+# Indicates support for operation as a single user beamformee
+# 0 = Not supported (default)
+# 1 = Supported
+#
+# Compressed Steering Number of Beamformer Antennas Supported: [BF-ANTENNA-2]
+# Beamformee's capability indicating the maximum number of beamformer
+# antennas the beamformee can support when sending compressed beamforming
+# feedback
+# If SU beamformer capable, set to maximum value minus 1
+# else reserved (default)
+#
+# Number of Sounding Dimensions: [SOUNDING-DIMENSION-2]
+# Beamformer's capability indicating the maximum value of the NUM_STS parameter
+# in the TXVECTOR of a VHT NDP
+# If SU beamformer capable, set to maximum value minus 1
+# else reserved (default)
+#
+# MU Beamformer Capable: [MU-BEAMFORMER]
+# Indicates support for operation as an MU beamformer
+# 0 = Not supported or sent by Non-AP STA (default)
+# 1 = Supported
+#
+# MU Beamformee Capable: [MU-BEAMFORMEE]
+# Indicates support for operation as an MU beamformee
+# 0 = Not supported or sent by AP (default)
+# 1 = Supported
+#
+# VHT TXOP PS: [VHT-TXOP-PS]
+# Indicates whether or not the AP supports VHT TXOP Power Save Mode
+# or whether or not the STA is in VHT TXOP Power Save mode
+# 0 = VHT AP doesnt support VHT TXOP PS mode (OR) VHT Sta not in VHT TXOP PS
+# mode
+# 1 = VHT AP supports VHT TXOP PS mode (OR) VHT Sta is in VHT TXOP power save
+# mode
+#
+# +HTC-VHT Capable: [HTC-VHT]
+# Indicates whether or not the STA supports receiving a VHT variant HT Control
+# field.
+# 0 = Not supported (default)
+# 1 = supported
+#
+# Maximum A-MPDU Length Exponent: [MAX-A-MPDU-LEN-EXP0]..[MAX-A-MPDU-LEN-EXP7]
+# Indicates the maximum length of A-MPDU pre-EOF padding that the STA can recv
+# This field is an integer in the range of 0 to 7.
+# The length defined by this field is equal to
+# 2 pow(13 + Maximum A-MPDU Length Exponent) -1 octets
+#
+# VHT Link Adaptation Capable: [VHT-LINK-ADAPT2] [VHT-LINK-ADAPT3]
+# Indicates whether or not the STA supports link adaptation using VHT variant
+# HT Control field
+# If +HTC-VHTcapable is 1
+# 0 = (no feedback) if the STA does not provide VHT MFB (default)
+# 1 = reserved
+# 2 = (Unsolicited) if the STA provides only unsolicited VHT MFB
+# 3 = (Both) if the STA can provide VHT MFB in response to VHT MRQ and if the
+# STA provides unsolicited VHT MFB
+# Reserved if +HTC-VHTcapable is 0
+#
+# Rx Antenna Pattern Consistency: [RX-ANTENNA-PATTERN]
+# Indicates the possibility of Rx antenna pattern change
+# 0 = Rx antenna pattern might change during the lifetime of an association
+# 1 = Rx antenna pattern does not change during the lifetime of an association
+#
+# Tx Antenna Pattern Consistency: [TX-ANTENNA-PATTERN]
+# Indicates the possibility of Tx antenna pattern change
+# 0 = Tx antenna pattern might change during the lifetime of an association
+# 1 = Tx antenna pattern does not change during the lifetime of an association
+#vht_capab=[SHORT-GI-80][HTC-VHT]
+#
+# Require stations to support VHT PHY (reject association if they do not)
+#require_vht=1
+
+# 0 = 20 or 40 MHz operating Channel width
+# 1 = 80 MHz channel width
+# 2 = 160 MHz channel width
+# 3 = 80+80 MHz channel width
+#vht_oper_chwidth=1
+#
+# center freq = 5 GHz + (5 * index)
+# So index 42 gives center freq 5.210 GHz
+# which is channel 42 in 5G band
+#
+#vht_oper_centr_freq_seg0_idx=42
+#
+# center freq = 5 GHz + (5 * index)
+# So index 159 gives center freq 5.795 GHz
+# which is channel 159 in 5G band
+#
+#vht_oper_centr_freq_seg1_idx=159
+
##### IEEE 802.1X-2004 related configuration ##################################
# Require IEEE 802.1X authorization
@@ -463,6 +640,8 @@ eapol_key_index_workaround=0
eap_server=0
# Path for EAP server user database
+# If SQLite support is included, this can be set to "sqlite:/path/to/sqlite.db"
+# to use SQLite database instead of a text file.
#eap_user_file=/etc/hostapd.eap_user
# CA certificate (PEM or DER file) for EAP-TLS/PEAP/TTLS
@@ -504,12 +683,21 @@ eap_server=0
# "openssl dhparam -out /etc/hostapd.dh.pem 1024"
#dh_file=/etc/hostapd.dh.pem
+# Fragment size for EAP methods
+#fragment_size=1400
+
+# Finite cyclic group for EAP-pwd. Number maps to group of domain parameters
+# using the IANA repository for IKE (RFC 2409).
+#pwd_group=19
+
# Configuration data for EAP-SIM database/authentication gateway interface.
# This is a text string in implementation specific format. The example
# implementation in eap_sim_db.c uses this as the UNIX domain socket name for
# the HLR/AuC gateway (e.g., hlr_auc_gw). In this case, the path uses "unix:"
-# prefix.
+# prefix. If hostapd is built with SQLite support (CONFIG_SQLITE=y in .config),
+# database file can be described with an optional db=<path> parameter.
#eap_sim_db=unix:/tmp/hlr_auc_gw.sock
+#eap_sim_db=unix:/tmp/hlr_auc_gw.sock db=/tmp/hostapd.db
# Encryption key for EAP-FAST PAC-Opaque values. This key must be a secret,
# random value. It is configured as a 16-octet value in hex format. It can be
@@ -616,6 +804,12 @@ own_ip_addr=127.0.0.1
# 60 (1 minute).
#radius_acct_interim_interval=600
+# Request Chargeable-User-Identity (RFC 4372)
+# This parameter can be used to configure hostapd to request CUI from the
+# RADIUS server by including Chargeable-User-Identity attribute into
+# Access-Request packets.
+#radius_request_cui=1
+
# Dynamic VLAN mode; allow RADIUS authentication server to decide which VLAN
# is used for the stations. This information is parsed from following RADIUS
# attributes based on RFC 3580 and RFC 2868: Tunnel-Type (value 13 = VLAN),
@@ -643,6 +837,61 @@ own_ip_addr=127.0.0.1
# to the bridge.
#vlan_tagged_interface=eth0
+# When hostapd creates a VLAN interface on vlan_tagged_interfaces, it needs
+# to know how to name it.
+# 0 = vlan<XXX>, e.g., vlan1
+# 1 = <vlan_tagged_interface>.<XXX>, e.g. eth0.1
+#vlan_naming=0
+
+# Arbitrary RADIUS attributes can be added into Access-Request and
+# Accounting-Request packets by specifying the contents of the attributes with
+# the following configuration parameters. There can be multiple of these to
+# add multiple attributes. These parameters can also be used to override some
+# of the attributes added automatically by hostapd.
+# Format: <attr_id>[:<syntax:value>]
+# attr_id: RADIUS attribute type (e.g., 26 = Vendor-Specific)
+# syntax: s = string (UTF-8), d = integer, x = octet string
+# value: attribute value in format indicated by the syntax
+# If syntax and value parts are omitted, a null value (single 0x00 octet) is
+# used.
+#
+# Additional Access-Request attributes
+# radius_auth_req_attr=<attr_id>[:<syntax:value>]
+# Examples:
+# Operator-Name = "Operator"
+#radius_auth_req_attr=126:s:Operator
+# Service-Type = Framed (2)
+#radius_auth_req_attr=6:d:2
+# Connect-Info = "testing" (this overrides the automatically generated value)
+#radius_auth_req_attr=77:s:testing
+# Same Connect-Info value set as a hexdump
+#radius_auth_req_attr=77:x:74657374696e67
+
+#
+# Additional Accounting-Request attributes
+# radius_acct_req_attr=<attr_id>[:<syntax:value>]
+# Examples:
+# Operator-Name = "Operator"
+#radius_acct_req_attr=126:s:Operator
+
+# Dynamic Authorization Extensions (RFC 5176)
+# This mechanism can be used to allow dynamic changes to user session based on
+# commands from a RADIUS server (or some other disconnect client that has the
+# needed session information). For example, Disconnect message can be used to
+# request an associated station to be disconnected.
+#
+# This is disabled by default. Set radius_das_port to non-zero UDP port
+# number to enable.
+#radius_das_port=3799
+#
+# DAS client (the host that can send Disconnect/CoA requests) and shared secret
+#radius_das_client=192.168.1.123 shared secret here
+#
+# DAS Event-Timestamp time window in seconds
+#radius_das_time_window=300
+#
+# DAS require Event-Timestamp
+#radius_das_require_event_timestamp=1
##### RADIUS authentication server configuration ##############################
@@ -666,6 +915,7 @@ own_ip_addr=127.0.0.1
# Enable WPA. Setting this variable configures the AP to require WPA (either
# WPA-PSK or WPA-RADIUS/EAP based on other configuration). For WPA-PSK, either
# wpa_psk or wpa_passphrase must be set and wpa_key_mgmt must include WPA-PSK.
+# Instead of wpa_psk / wpa_passphrase, wpa_psk_radius might suffice.
# For WPA-RADIUS/EAP, ieee8021x must be set (but without dynamic WEP keys),
# RADIUS authentication server must be configured, and WPA-EAP must be included
# in wpa_key_mgmt.
@@ -690,6 +940,15 @@ own_ip_addr=127.0.0.1
# configuration reloads.
#wpa_psk_file=/etc/hostapd.wpa_psk
+# Optionally, WPA passphrase can be received from RADIUS authentication server
+# This requires macaddr_acl to be set to 2 (RADIUS)
+# 0 = disabled (default)
+# 1 = optional; use default passphrase/psk if RADIUS server does not include
+# Tunnel-Password
+# 2 = required; reject authentication if RADIUS server does not include
+# Tunnel-Password
+#wpa_psk_radius=0
+
# Set of accepted key management algorithms (WPA-PSK, WPA-EAP, or both). The
# entries are separated with a space. WPA-PSK-SHA256 and WPA-EAP-SHA256 can be
# added to enable SHA256-based stronger algorithms.
@@ -763,6 +1022,13 @@ own_ip_addr=127.0.0.1
# dot11AssociationSAQueryRetryTimeout, 1...4294967295
#assoc_sa_query_retry_timeout=201
+# disable_pmksa_caching: Disable PMKSA caching
+# This parameter can be used to disable caching of PMKSA created through EAP
+# authentication. RSN preauthentication may still end up using PMKSA caching if
+# it is enabled (rsn_preauth=1).
+# 0 = PMKSA caching enabled (default)
+# 1 = PMKSA caching disabled
+#disable_pmksa_caching=0
# okc: Opportunistic Key Caching (aka Proactive Key Caching)
# Allow PMK cache to be shared opportunistically among configured interfaces
@@ -910,8 +1176,21 @@ own_ip_addr=127.0.0.1
# Config Methods
# List of the supported configuration methods
# Available methods: usba ethernet label display ext_nfc_token int_nfc_token
-# nfc_interface push_button keypad
-#config_methods=label display push_button keypad
+# nfc_interface push_button keypad virtual_display physical_display
+# virtual_push_button physical_push_button
+#config_methods=label virtual_display virtual_push_button keypad
+
+# WPS capability discovery workaround for PBC with Windows 7
+# Windows 7 uses incorrect way of figuring out AP's WPS capabilities by acting
+# as a Registrar and using M1 from the AP. The config methods attribute in that
+# message is supposed to indicate only the configuration method supported by
+# the AP in Enrollee role, i.e., to add an external Registrar. For that case,
+# PBC shall not be used and as such, the PushButton config method is removed
+# from M1 by default. If pbc_in_m1=1 is included in the configuration file,
+# the PushButton config method is left in M1 (if included in config_methods
+# parameter) to allow Windows 7 to use PBC instead of PIN (e.g., from a label
+# in the AP).
+#pbc_in_m1=1
# Static access point PIN for initial configuration and adding Registrars
# If not set, hostapd will not allow external WPS Registrars to control the
@@ -981,6 +1260,246 @@ own_ip_addr=127.0.0.1
# 12-digit, all-numeric code that identifies the consumer package.
#upc=123456789012
+# WPS RF Bands (a = 5G, b = 2.4G, g = 2.4G, ag = dual band)
+# This value should be set according to RF band(s) supported by the AP if
+# hw_mode is not set. For dual band dual concurrent devices, this needs to be
+# set to ag to allow both RF bands to be advertized.
+#wps_rf_bands=ag
+
+# NFC password token for WPS
+# These parameters can be used to configure a fixed NFC password token for the
+# AP. This can be generated, e.g., with nfc_pw_token from wpa_supplicant. When
+# these parameters are used, the AP is assumed to be deployed with a NFC tag
+# that includes the matching NFC password token (e.g., written based on the
+# NDEF record from nfc_pw_token).
+#
+#wps_nfc_dev_pw_id: Device Password ID (16..65535)
+#wps_nfc_dh_pubkey: Hexdump of DH Public Key
+#wps_nfc_dh_privkey: Hexdump of DH Private Key
+#wps_nfc_dev_pw: Hexdump of Device Password
+
+##### Wi-Fi Direct (P2P) ######################################################
+
+# Enable P2P Device management
+#manage_p2p=1
+
+# Allow cross connection
+#allow_cross_connection=1
+
+#### TDLS (IEEE 802.11z-2010) #################################################
+
+# Prohibit use of TDLS in this BSS
+#tdls_prohibit=1
+
+# Prohibit use of TDLS Channel Switching in this BSS
+#tdls_prohibit_chan_switch=1
+
+##### IEEE 802.11v-2011 #######################################################
+
+# Time advertisement
+# 0 = disabled (default)
+# 2 = UTC time at which the TSF timer is 0
+#time_advertisement=2
+
+# Local time zone as specified in 8.3 of IEEE Std 1003.1-2004:
+# stdoffset[dst[offset][,start[/time],end[/time]]]
+#time_zone=EST5
+
+# WNM-Sleep Mode (extended sleep mode for stations)
+# 0 = disabled (default)
+# 1 = enabled (allow stations to use WNM-Sleep Mode)
+#wnm_sleep_mode=1
+
+# BSS Transition Management
+# 0 = disabled (default)
+# 1 = enabled
+#bss_transition=1
+
+##### IEEE 802.11u-2011 #######################################################
+
+# Enable Interworking service
+#interworking=1
+
+# Access Network Type
+# 0 = Private network
+# 1 = Private network with guest access
+# 2 = Chargeable public network
+# 3 = Free public network
+# 4 = Personal device network
+# 5 = Emergency services only network
+# 14 = Test or experimental
+# 15 = Wildcard
+#access_network_type=0
+
+# Whether the network provides connectivity to the Internet
+# 0 = Unspecified
+# 1 = Network provides connectivity to the Internet
+#internet=1
+
+# Additional Step Required for Access
+# Note: This is only used with open network, i.e., ASRA shall ne set to 0 if
+# RSN is used.
+#asra=0
+
+# Emergency services reachable
+#esr=0
+
+# Unauthenticated emergency service accessible
+#uesa=0
+
+# Venue Info (optional)
+# The available values are defined in IEEE Std 802.11u-2011, 7.3.1.34.
+# Example values (group,type):
+# 0,0 = Unspecified
+# 1,7 = Convention Center
+# 1,13 = Coffee Shop
+# 2,0 = Unspecified Business
+# 7,1 Private Residence
+#venue_group=7
+#venue_type=1
+
+# Homogeneous ESS identifier (optional; dot11HESSID)
+# If set, this shall be identifical to one of the BSSIDs in the homogeneous
+# ESS and this shall be set to the same value across all BSSs in homogeneous
+# ESS.
+#hessid=02:03:04:05:06:07
+
+# Roaming Consortium List
+# Arbitrary number of Roaming Consortium OIs can be configured with each line
+# adding a new OI to the list. The first three entries are available through
+# Beacon and Probe Response frames. Any additional entry will be available only
+# through ANQP queries. Each OI is between 3 and 15 octets and is configured as
+# a hexstring.
+#roaming_consortium=021122
+#roaming_consortium=2233445566
+
+# Venue Name information
+# This parameter can be used to configure one or more Venue Name Duples for
+# Venue Name ANQP information. Each entry has a two or three character language
+# code (ISO-639) separated by colon from the venue name string.
+# Note that venue_group and venue_type have to be set for Venue Name
+# information to be complete.
+#venue_name=eng:Example venue
+#venue_name=fin:Esimerkkipaikka
+
+# Network Authentication Type
+# This parameter indicates what type of network authentication is used in the
+# network.
+# format: <network auth type indicator (1-octet hex str)> [redirect URL]
+# Network Authentication Type Indicator values:
+# 00 = Acceptance of terms and conditions
+# 01 = On-line enrollment supported
+# 02 = http/https redirection
+# 03 = DNS redirection
+#network_auth_type=00
+#network_auth_type=02http://www.example.com/redirect/me/here/
+
+# IP Address Type Availability
+# format: <1-octet encoded value as hex str>
+# (ipv4_type & 0x3f) << 2 | (ipv6_type & 0x3)
+# ipv4_type:
+# 0 = Address type not available
+# 1 = Public IPv4 address available
+# 2 = Port-restricted IPv4 address available
+# 3 = Single NATed private IPv4 address available
+# 4 = Double NATed private IPv4 address available
+# 5 = Port-restricted IPv4 address and single NATed IPv4 address available
+# 6 = Port-restricted IPv4 address and double NATed IPv4 address available
+# 7 = Availability of the address type is not known
+# ipv6_type:
+# 0 = Address type not available
+# 1 = Address type available
+# 2 = Availability of the address type not known
+#ipaddr_type_availability=14
+
+# Domain Name
+# format: <variable-octet str>[,<variable-octet str>]
+#domain_name=example.com,another.example.com,yet-another.example.com
+
+# 3GPP Cellular Network information
+# format: <MCC1,MNC1>[;<MCC2,MNC2>][;...]
+#anqp_3gpp_cell_net=244,91;310,026;234,56
+
+# NAI Realm information
+# One or more realm can be advertised. Each nai_realm line adds a new realm to
+# the set. These parameters provide information for stations using Interworking
+# network selection to allow automatic connection to a network based on
+# credentials.
+# format: <encoding>,<NAI Realm(s)>[,<EAP Method 1>][,<EAP Method 2>][,...]
+# encoding:
+# 0 = Realm formatted in accordance with IETF RFC 4282
+# 1 = UTF-8 formatted character string that is not formatted in
+# accordance with IETF RFC 4282
+# NAI Realm(s): Semi-colon delimited NAI Realm(s)
+# EAP Method: <EAP Method>[:<[AuthParam1:Val1]>][<[AuthParam2:Val2]>][...]
+# AuthParam (Table 8-188 in IEEE Std 802.11-2012):
+# ID 2 = Non-EAP Inner Authentication Type
+# 1 = PAP, 2 = CHAP, 3 = MSCHAP, 4 = MSCHAPV2
+# ID 3 = Inner authentication EAP Method Type
+# ID 5 = Credential Type
+# 1 = SIM, 2 = USIM, 3 = NFC Secure Element, 4 = Hardware Token,
+# 5 = Softoken, 6 = Certificate, 7 = username/password, 9 = Anonymous,
+# 10 = Vendor Specific
+#nai_realm=0,example.com;example.net
+# EAP methods EAP-TLS with certificate and EAP-TTLS/MSCHAPv2 with
+# username/password
+#nai_realm=0,example.org,13[5:6],21[2:4][5:7]
+
+##### Hotspot 2.0 #############################################################
+
+# Enable Hotspot 2.0 support
+#hs20=1
+
+# Disable Downstream Group-Addressed Forwarding (DGAF)
+# This can be used to configure a network where no group-addressed frames are
+# allowed. The AP will not forward any group-address frames to the stations and
+# random GTKs are issued for each station to prevent associated stations from
+# forging such frames to other stations in the BSS.
+#disable_dgaf=1
+
+# Operator Friendly Name
+# This parameter can be used to configure one or more Operator Friendly Name
+# Duples. Each entry has a two or three character language code (ISO-639)
+# separated by colon from the operator friendly name string.
+#hs20_oper_friendly_name=eng:Example operator
+#hs20_oper_friendly_name=fin:Esimerkkioperaattori
+
+# Connection Capability
+# This can be used to advertise what type of IP traffic can be sent through the
+# hotspot (e.g., due to firewall allowing/blocking protocols/ports).
+# format: <IP Protocol>:<Port Number>:<Status>
+# IP Protocol: 1 = ICMP, 6 = TCP, 17 = UDP
+# Port Number: 0..65535
+# Status: 0 = Closed, 1 = Open, 2 = Unknown
+# Each hs20_conn_capab line is added to the list of advertised tuples.
+#hs20_conn_capab=1:0:2
+#hs20_conn_capab=6:22:1
+#hs20_conn_capab=17:5060:0
+
+# WAN Metrics
+# format: <WAN Info>:<DL Speed>:<UL Speed>:<DL Load>:<UL Load>:<LMD>
+# WAN Info: B0-B1: Link Status, B2: Symmetric Link, B3: At Capabity
+# (encoded as two hex digits)
+# Link Status: 1 = Link up, 2 = Link down, 3 = Link in test state
+# Downlink Speed: Estimate of WAN backhaul link current downlink speed in kbps;
+# 1..4294967295; 0 = unknown
+# Uplink Speed: Estimate of WAN backhaul link current uplink speed in kbps
+# 1..4294967295; 0 = unknown
+# Downlink Load: Current load of downlink WAN connection (scaled to 255 = 100%)
+# Uplink Load: Current load of uplink WAN connection (scaled to 255 = 100%)
+# Load Measurement Duration: Duration for measuring downlink/uplink load in
+# tenths of a second (1..65535); 0 if load cannot be determined
+#hs20_wan_metrics=01:8000:1000:80:240:3000
+
+# Operating Class Indication
+# List of operating classes the BSSes in this ESS use. The Global operating
+# classes in Table E-4 of IEEE Std 802.11-2012 Annex E define the values that
+# can be used in this.
+# format: hexdump of operating class octets
+# for example, operating classes 81 (2.4 GHz channels 1-13) and 115 (5 GHz
+# channels 36-48):
+#hs20_operating_class=5173
+
##### Multiple BSSID support ##################################################
#
# Above configuration is using the default interface (wlan#, or multi-SSID VLAN
diff --git a/contrib/wpa/hostapd/hostapd.eap_user b/contrib/wpa/hostapd/hostapd.eap_user
index ac9a5d8..12a2c61 100644
--- a/contrib/wpa/hostapd/hostapd.eap_user
+++ b/contrib/wpa/hostapd/hostapd.eap_user
@@ -69,6 +69,9 @@
"3"* SIM,TTLS,TLS,PEAP,AKA
"4"* AKA,TTLS,TLS,PEAP,SIM
"5"* SIM,TTLS,TLS,PEAP,AKA
+"6"* AKA'
+"7"* AKA'
+"8"* AKA'
# Wildcard for all other identities
* PEAP,TTLS,TLS,SIM,AKA
@@ -89,3 +92,6 @@
"3"* SIM [2]
"4"* AKA [2]
"5"* SIM [2]
+"6"* AKA' [2]
+"7"* AKA' [2]
+"8"* AKA' [2]
diff --git a/contrib/wpa/hostapd/hostapd.eap_user_sqlite b/contrib/wpa/hostapd/hostapd.eap_user_sqlite
new file mode 100644
index 0000000..f688327
--- /dev/null
+++ b/contrib/wpa/hostapd/hostapd.eap_user_sqlite
@@ -0,0 +1,17 @@
+CREATE TABLE users(
+ identity TEXT PRIMARY KEY,
+ methods TEXT,
+ password TEXT,
+ phase2 INTEGER
+);
+
+CREATE TABLE wildcards(
+ identity TEXT PRIMARY KEY,
+ methods TEXT
+);
+
+INSERT INTO users(identity,methods,password,phase2) VALUES ('user','TTLS-MSCHAPV2','password',1);
+INSERT INTO users(identity,methods,password,phase2) VALUES ('DOMAIN\mschapv2 user','TTLS-MSCHAPV2','password',1);
+
+INSERT INTO wildcards(identity,methods) VALUES ('','TTLS,TLS');
+INSERT INTO wildcards(identity,methods) VALUES ('0','AKA');
diff --git a/contrib/wpa/hostapd/hostapd_cli.c b/contrib/wpa/hostapd/hostapd_cli.c
index 589530e..e8e1bb2 100644
--- a/contrib/wpa/hostapd/hostapd_cli.c
+++ b/contrib/wpa/hostapd/hostapd_cli.c
@@ -1,53 +1,32 @@
/*
* hostapd - command line interface for hostapd daemon
- * Copyright (c) 2004-2010, Jouni Malinen <j@w1.fi>
+ * Copyright (c) 2004-2012, Jouni Malinen <j@w1.fi>
*
- * This program is free software; you can redistribute it and/or modify
- * it under the terms of the GNU General Public License version 2 as
- * published by the Free Software Foundation.
- *
- * Alternatively, this software may be distributed under the terms of BSD
- * license.
- *
- * See README and COPYING for more details.
+ * This software may be distributed under the terms of the BSD license.
+ * See README for more details.
*/
#include "includes.h"
#include <dirent.h>
#include "common/wpa_ctrl.h"
-#include "common.h"
+#include "utils/common.h"
+#include "utils/eloop.h"
+#include "utils/edit.h"
#include "common/version.h"
static const char *hostapd_cli_version =
"hostapd_cli v" VERSION_STR "\n"
-"Copyright (c) 2004-2010, Jouni Malinen <j@w1.fi> and contributors";
+"Copyright (c) 2004-2012, Jouni Malinen <j@w1.fi> and contributors";
static const char *hostapd_cli_license =
-"This program is free software. You can distribute it and/or modify it\n"
-"under the terms of the GNU General Public License version 2.\n"
-"\n"
-"Alternatively, this software may be distributed under the terms of the\n"
-"BSD license. See README and COPYING for more details.\n";
+"This software may be distributed under the terms of the BSD license.\n"
+"See README for more details.\n";
static const char *hostapd_cli_full_license =
-"This program is free software; you can redistribute it and/or modify\n"
-"it under the terms of the GNU General Public License version 2 as\n"
-"published by the Free Software Foundation.\n"
-"\n"
-"This program is distributed in the hope that it will be useful,\n"
-"but WITHOUT ANY WARRANTY; without even the implied warranty of\n"
-"MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the\n"
-"GNU General Public License for more details.\n"
-"\n"
-"You should have received a copy of the GNU General Public License\n"
-"along with this program; if not, write to the Free Software\n"
-"Foundation, Inc., 51 Franklin St, Fifth Floor, Boston, MA 02110-1301 USA\n"
-"\n"
-"Alternatively, this software may be distributed under the terms of the\n"
-"BSD license.\n"
+"This software may be distributed under the terms of the BSD license.\n"
"\n"
"Redistribution and use in source and binary forms, with or without\n"
"modification, are permitted provided that the following conditions are\n"
@@ -89,13 +68,19 @@ static const char *commands_help =
" sa_query <addr> send SA Query to a station\n"
#endif /* CONFIG_IEEE80211W */
#ifdef CONFIG_WPS
-" wps_pin <uuid> <pin> [timeout] add WPS Enrollee PIN (Device Password)\n"
+" wps_pin <uuid> <pin> [timeout] [addr] add WPS Enrollee PIN\n"
+" wps_check_pin <PIN> verify PIN checksum\n"
" wps_pbc indicate button pushed to initiate PBC\n"
-#ifdef CONFIG_WPS_OOB
-" wps_oob <type> <path> <method> use WPS with out-of-band (UFD)\n"
-#endif /* CONFIG_WPS_OOB */
+" wps_cancel cancel the pending WPS operation\n"
+#ifdef CONFIG_WPS_NFC
+" wps_nfc_tag_read <hexdump> report read NFC tag with WPS data\n"
+" wps_nfc_config_token <WPS/NDEF> build NFC configuration token\n"
+" wps_nfc_token <WPS/NDEF/enable/disable> manager NFC password token\n"
+#endif /* CONFIG_WPS_NFC */
" wps_ap_pin <cmd> [params..] enable/disable AP PIN\n"
+" wps_config <SSID> <auth> <encr> <key> configure AP\n"
#endif /* CONFIG_WPS */
+" get_config show current configuration\n"
" help show this usage help\n"
" interface [ifname] show interfaces/select interface\n"
" level <debug level> change debug level\n"
@@ -110,6 +95,7 @@ static char *ctrl_ifname = NULL;
static const char *pid_file = NULL;
static const char *action_file = NULL;
static int ping_interval = 5;
+static int interactive = 0;
static void usage(void)
@@ -218,6 +204,12 @@ static int hostapd_cli_cmd_ping(struct wpa_ctrl *ctrl, int argc, char *argv[])
}
+static int hostapd_cli_cmd_relog(struct wpa_ctrl *ctrl, int argc, char *argv[])
+{
+ return wpa_ctrl_command(ctrl, "RELOG");
+}
+
+
static int hostapd_cli_cmd_mib(struct wpa_ctrl *ctrl, int argc, char *argv[])
{
return wpa_ctrl_command(ctrl, "MIB");
@@ -352,13 +344,16 @@ static int hostapd_cli_cmd_sa_query(struct wpa_ctrl *ctrl, int argc,
static int hostapd_cli_cmd_wps_pin(struct wpa_ctrl *ctrl, int argc,
char *argv[])
{
- char buf[64];
+ char buf[256];
if (argc < 2) {
printf("Invalid 'wps_pin' command - at least two arguments, "
"UUID and PIN, are required.\n");
return -1;
}
- if (argc > 2)
+ if (argc > 3)
+ snprintf(buf, sizeof(buf), "WPS_PIN %s %s %s %s",
+ argv[0], argv[1], argv[2], argv[3]);
+ else if (argc > 2)
snprintf(buf, sizeof(buf), "WPS_PIN %s %s %s",
argv[0], argv[1], argv[2]);
else
@@ -367,6 +362,32 @@ static int hostapd_cli_cmd_wps_pin(struct wpa_ctrl *ctrl, int argc,
}
+static int hostapd_cli_cmd_wps_check_pin(struct wpa_ctrl *ctrl, int argc,
+ char *argv[])
+{
+ char cmd[256];
+ int res;
+
+ if (argc != 1 && argc != 2) {
+ printf("Invalid WPS_CHECK_PIN command: needs one argument:\n"
+ "- PIN to be verified\n");
+ return -1;
+ }
+
+ if (argc == 2)
+ res = os_snprintf(cmd, sizeof(cmd), "WPS_CHECK_PIN %s %s",
+ argv[0], argv[1]);
+ else
+ res = os_snprintf(cmd, sizeof(cmd), "WPS_CHECK_PIN %s",
+ argv[0]);
+ if (res < 0 || (size_t) res >= sizeof(cmd) - 1) {
+ printf("Too long WPS_CHECK_PIN command.\n");
+ return -1;
+ }
+ return wpa_ctrl_command(ctrl, cmd);
+}
+
+
static int hostapd_cli_cmd_wps_pbc(struct wpa_ctrl *ctrl, int argc,
char *argv[])
{
@@ -374,38 +395,82 @@ static int hostapd_cli_cmd_wps_pbc(struct wpa_ctrl *ctrl, int argc,
}
-#ifdef CONFIG_WPS_OOB
-static int hostapd_cli_cmd_wps_oob(struct wpa_ctrl *ctrl, int argc,
- char *argv[])
+static int hostapd_cli_cmd_wps_cancel(struct wpa_ctrl *ctrl, int argc,
+ char *argv[])
{
- char cmd[256];
+ return wpa_ctrl_command(ctrl, "WPS_CANCEL");
+}
+
+
+#ifdef CONFIG_WPS_NFC
+static int hostapd_cli_cmd_wps_nfc_tag_read(struct wpa_ctrl *ctrl, int argc,
+ char *argv[])
+{
+ int ret;
+ char *buf;
+ size_t buflen;
+
+ if (argc != 1) {
+ printf("Invalid 'wps_nfc_tag_read' command - one argument "
+ "is required.\n");
+ return -1;
+ }
+
+ buflen = 18 + os_strlen(argv[0]);
+ buf = os_malloc(buflen);
+ if (buf == NULL)
+ return -1;
+ os_snprintf(buf, buflen, "WPS_NFC_TAG_READ %s", argv[0]);
+
+ ret = wpa_ctrl_command(ctrl, buf);
+ os_free(buf);
+
+ return ret;
+}
+
+
+static int hostapd_cli_cmd_wps_nfc_config_token(struct wpa_ctrl *ctrl,
+ int argc, char *argv[])
+{
+ char cmd[64];
int res;
- if (argc != 3 && argc != 4) {
- printf("Invalid WPS_OOB command: need three or four "
- "arguments:\n"
- "- DEV_TYPE: use 'ufd' or 'nfc'\n"
- "- PATH: path of OOB device like '/mnt'\n"
- "- METHOD: OOB method 'pin-e' or 'pin-r', "
- "'cred'\n"
- "- DEV_NAME: (only for NFC) device name like "
- "'pn531'\n");
+ if (argc != 1) {
+ printf("Invalid 'wps_nfc_config_token' command - one argument "
+ "is required.\n");
return -1;
}
- if (argc == 3)
- res = os_snprintf(cmd, sizeof(cmd), "WPS_OOB %s %s %s",
- argv[0], argv[1], argv[2]);
- else
- res = os_snprintf(cmd, sizeof(cmd), "WPS_OOB %s %s %s %s",
- argv[0], argv[1], argv[2], argv[3]);
+ res = os_snprintf(cmd, sizeof(cmd), "WPS_NFC_CONFIG_TOKEN %s",
+ argv[0]);
+ if (res < 0 || (size_t) res >= sizeof(cmd) - 1) {
+ printf("Too long WPS_NFC_CONFIG_TOKEN command.\n");
+ return -1;
+ }
+ return wpa_ctrl_command(ctrl, cmd);
+}
+
+
+static int hostapd_cli_cmd_wps_nfc_token(struct wpa_ctrl *ctrl,
+ int argc, char *argv[])
+{
+ char cmd[64];
+ int res;
+
+ if (argc != 1) {
+ printf("Invalid 'wps_nfc_token' command - one argument is "
+ "required.\n");
+ return -1;
+ }
+
+ res = os_snprintf(cmd, sizeof(cmd), "WPS_NFC_TOKEN %s", argv[0]);
if (res < 0 || (size_t) res >= sizeof(cmd) - 1) {
- printf("Too long WPS_OOB command.\n");
+ printf("Too long WPS_NFC_TOKEN command.\n");
return -1;
}
return wpa_ctrl_command(ctrl, cmd);
}
-#endif /* CONFIG_WPS_OOB */
+#endif /* CONFIG_WPS_NFC */
static int hostapd_cli_cmd_wps_ap_pin(struct wpa_ctrl *ctrl, int argc,
@@ -427,9 +492,100 @@ static int hostapd_cli_cmd_wps_ap_pin(struct wpa_ctrl *ctrl, int argc,
snprintf(buf, sizeof(buf), "WPS_AP_PIN %s", argv[0]);
return wpa_ctrl_command(ctrl, buf);
}
+
+
+static int hostapd_cli_cmd_wps_config(struct wpa_ctrl *ctrl, int argc,
+ char *argv[])
+{
+ char buf[256];
+ char ssid_hex[2 * 32 + 1];
+ char key_hex[2 * 64 + 1];
+ int i;
+
+ if (argc < 1) {
+ printf("Invalid 'wps_config' command - at least two arguments "
+ "are required.\n");
+ return -1;
+ }
+
+ ssid_hex[0] = '\0';
+ for (i = 0; i < 32; i++) {
+ if (argv[0][i] == '\0')
+ break;
+ os_snprintf(&ssid_hex[i * 2], 3, "%02x", argv[0][i]);
+ }
+
+ key_hex[0] = '\0';
+ if (argc > 3) {
+ for (i = 0; i < 64; i++) {
+ if (argv[3][i] == '\0')
+ break;
+ os_snprintf(&key_hex[i * 2], 3, "%02x",
+ argv[3][i]);
+ }
+ }
+
+ if (argc > 3)
+ snprintf(buf, sizeof(buf), "WPS_CONFIG %s %s %s %s",
+ ssid_hex, argv[1], argv[2], key_hex);
+ else if (argc > 2)
+ snprintf(buf, sizeof(buf), "WPS_CONFIG %s %s %s",
+ ssid_hex, argv[1], argv[2]);
+ else
+ snprintf(buf, sizeof(buf), "WPS_CONFIG %s %s",
+ ssid_hex, argv[1]);
+ return wpa_ctrl_command(ctrl, buf);
+}
#endif /* CONFIG_WPS */
+static int hostapd_cli_cmd_disassoc_imminent(struct wpa_ctrl *ctrl, int argc,
+ char *argv[])
+{
+ char buf[300];
+ int res;
+
+ if (argc < 2) {
+ printf("Invalid 'disassoc_imminent' command - two arguments "
+ "(STA addr and Disassociation Timer) are needed\n");
+ return -1;
+ }
+
+ res = os_snprintf(buf, sizeof(buf), "DISASSOC_IMMINENT %s %s",
+ argv[0], argv[1]);
+ if (res < 0 || res >= (int) sizeof(buf))
+ return -1;
+ return wpa_ctrl_command(ctrl, buf);
+}
+
+
+static int hostapd_cli_cmd_ess_disassoc(struct wpa_ctrl *ctrl, int argc,
+ char *argv[])
+{
+ char buf[300];
+ int res;
+
+ if (argc < 2) {
+ printf("Invalid 'ess_disassoc' command - two arguments (STA "
+ "addr and URL) are needed\n");
+ return -1;
+ }
+
+ res = os_snprintf(buf, sizeof(buf), "ESS_DISASSOC %s %s",
+ argv[0], argv[1]);
+ if (res < 0 || res >= (int) sizeof(buf))
+ return -1;
+ return wpa_ctrl_command(ctrl, buf);
+}
+
+
+static int hostapd_cli_cmd_get_config(struct wpa_ctrl *ctrl, int argc,
+ char *argv[])
+{
+ return wpa_ctrl_command(ctrl, "GET_CONFIG");
+}
+
+
static int wpa_ctrl_command_sta(struct wpa_ctrl *ctrl, char *cmd,
char *addr, size_t addr_len)
{
@@ -499,6 +655,8 @@ static int hostapd_cli_cmd_license(struct wpa_ctrl *ctrl, int argc,
static int hostapd_cli_cmd_quit(struct wpa_ctrl *ctrl, int argc, char *argv[])
{
hostapd_cli_quit = 1;
+ if (interactive)
+ eloop_terminate();
return 0;
}
@@ -567,6 +725,46 @@ static int hostapd_cli_cmd_interface(struct wpa_ctrl *ctrl, int argc,
}
+static int hostapd_cli_cmd_set(struct wpa_ctrl *ctrl, int argc, char *argv[])
+{
+ char cmd[256];
+ int res;
+
+ if (argc != 2) {
+ printf("Invalid SET command: needs two arguments (variable "
+ "name and value)\n");
+ return -1;
+ }
+
+ res = os_snprintf(cmd, sizeof(cmd), "SET %s %s", argv[0], argv[1]);
+ if (res < 0 || (size_t) res >= sizeof(cmd) - 1) {
+ printf("Too long SET command.\n");
+ return -1;
+ }
+ return wpa_ctrl_command(ctrl, cmd);
+}
+
+
+static int hostapd_cli_cmd_get(struct wpa_ctrl *ctrl, int argc, char *argv[])
+{
+ char cmd[256];
+ int res;
+
+ if (argc != 1) {
+ printf("Invalid GET command: needs one argument (variable "
+ "name)\n");
+ return -1;
+ }
+
+ res = os_snprintf(cmd, sizeof(cmd), "GET %s", argv[0]);
+ if (res < 0 || (size_t) res >= sizeof(cmd) - 1) {
+ printf("Too long GET command.\n");
+ return -1;
+ }
+ return wpa_ctrl_command(ctrl, cmd);
+}
+
+
struct hostapd_cli_cmd {
const char *cmd;
int (*handler)(struct wpa_ctrl *ctrl, int argc, char *argv[]);
@@ -575,6 +773,7 @@ struct hostapd_cli_cmd {
static struct hostapd_cli_cmd hostapd_cli_commands[] = {
{ "ping", hostapd_cli_cmd_ping },
{ "mib", hostapd_cli_cmd_mib },
+ { "relog", hostapd_cli_cmd_relog },
{ "sta", hostapd_cli_cmd_sta },
{ "all_sta", hostapd_cli_cmd_all_sta },
{ "new_sta", hostapd_cli_cmd_new_sta },
@@ -585,17 +784,27 @@ static struct hostapd_cli_cmd hostapd_cli_commands[] = {
#endif /* CONFIG_IEEE80211W */
#ifdef CONFIG_WPS
{ "wps_pin", hostapd_cli_cmd_wps_pin },
+ { "wps_check_pin", hostapd_cli_cmd_wps_check_pin },
{ "wps_pbc", hostapd_cli_cmd_wps_pbc },
-#ifdef CONFIG_WPS_OOB
- { "wps_oob", hostapd_cli_cmd_wps_oob },
-#endif /* CONFIG_WPS_OOB */
+ { "wps_cancel", hostapd_cli_cmd_wps_cancel },
+#ifdef CONFIG_WPS_NFC
+ { "wps_nfc_tag_read", hostapd_cli_cmd_wps_nfc_tag_read },
+ { "wps_nfc_config_token", hostapd_cli_cmd_wps_nfc_config_token },
+ { "wps_nfc_token", hostapd_cli_cmd_wps_nfc_token },
+#endif /* CONFIG_WPS_NFC */
{ "wps_ap_pin", hostapd_cli_cmd_wps_ap_pin },
+ { "wps_config", hostapd_cli_cmd_wps_config },
#endif /* CONFIG_WPS */
+ { "disassoc_imminent", hostapd_cli_cmd_disassoc_imminent },
+ { "ess_disassoc", hostapd_cli_cmd_ess_disassoc },
+ { "get_config", hostapd_cli_cmd_get_config },
{ "help", hostapd_cli_cmd_help },
{ "interface", hostapd_cli_cmd_interface },
{ "level", hostapd_cli_cmd_level },
{ "license", hostapd_cli_cmd_license },
{ "quit", hostapd_cli_cmd_quit },
+ { "set", hostapd_cli_cmd_set },
+ { "get", hostapd_cli_cmd_get },
{ NULL, NULL }
};
@@ -610,6 +819,11 @@ static void wpa_request(struct wpa_ctrl *ctrl, int argc, char *argv[])
while (cmd->cmd) {
if (strncasecmp(cmd->cmd, argv[0], strlen(argv[0])) == 0) {
match = cmd;
+ if (os_strcasecmp(cmd->cmd, argv[0]) == 0) {
+ /* we have an exact match */
+ count = 1;
+ break;
+ }
count++;
}
cmd++;
@@ -661,70 +875,39 @@ static void hostapd_cli_recv_pending(struct wpa_ctrl *ctrl, int in_read,
}
-static void hostapd_cli_interactive(void)
-{
- const int max_args = 10;
- char cmd[256], *res, *argv[max_args], *pos;
- int argc;
+#define max_args 10
- printf("\nInteractive mode\n\n");
+static int tokenize_cmd(char *cmd, char *argv[])
+{
+ char *pos;
+ int argc = 0;
- do {
- hostapd_cli_recv_pending(ctrl_conn, 0, 0);
- printf("> ");
- alarm(ping_interval);
- res = fgets(cmd, sizeof(cmd), stdin);
- alarm(0);
- if (res == NULL)
- break;
- pos = cmd;
- while (*pos != '\0') {
- if (*pos == '\n') {
- *pos = '\0';
- break;
- }
+ pos = cmd;
+ for (;;) {
+ while (*pos == ' ')
pos++;
+ if (*pos == '\0')
+ break;
+ argv[argc] = pos;
+ argc++;
+ if (argc == max_args)
+ break;
+ if (*pos == '"') {
+ char *pos2 = os_strrchr(pos, '"');
+ if (pos2)
+ pos = pos2 + 1;
}
- argc = 0;
- pos = cmd;
- for (;;) {
- while (*pos == ' ')
- pos++;
- if (*pos == '\0')
- break;
- argv[argc] = pos;
- argc++;
- if (argc == max_args)
- break;
- while (*pos != '\0' && *pos != ' ')
- pos++;
- if (*pos == ' ')
- *pos++ = '\0';
- }
- if (argc)
- wpa_request(ctrl_conn, argc, argv);
- } while (!hostapd_cli_quit);
-}
-
-
-static void hostapd_cli_cleanup(void)
-{
- hostapd_cli_close_connection();
- if (pid_file)
- os_daemonize_terminate(pid_file);
-
- os_program_deinit();
-}
-
+ while (*pos != '\0' && *pos != ' ')
+ pos++;
+ if (*pos == ' ')
+ *pos++ = '\0';
+ }
-static void hostapd_cli_terminate(int sig)
-{
- hostapd_cli_cleanup();
- exit(0);
+ return argc;
}
-static void hostapd_cli_alarm(int sig)
+static void hostapd_cli_ping(void *eloop_ctx, void *timeout_ctx)
{
if (ctrl_conn && _wpa_ctrl_command(ctrl_conn, "PING", 0)) {
printf("Connection to hostapd lost - trying to reconnect\n");
@@ -744,7 +927,55 @@ static void hostapd_cli_alarm(int sig)
}
if (ctrl_conn)
hostapd_cli_recv_pending(ctrl_conn, 1, 0);
- alarm(ping_interval);
+ eloop_register_timeout(ping_interval, 0, hostapd_cli_ping, NULL, NULL);
+}
+
+
+static void hostapd_cli_eloop_terminate(int sig, void *signal_ctx)
+{
+ eloop_terminate();
+}
+
+
+static void hostapd_cli_edit_cmd_cb(void *ctx, char *cmd)
+{
+ char *argv[max_args];
+ int argc;
+ argc = tokenize_cmd(cmd, argv);
+ if (argc)
+ wpa_request(ctrl_conn, argc, argv);
+}
+
+
+static void hostapd_cli_edit_eof_cb(void *ctx)
+{
+ eloop_terminate();
+}
+
+
+static void hostapd_cli_interactive(void)
+{
+ printf("\nInteractive mode\n\n");
+
+ eloop_register_signal_terminate(hostapd_cli_eloop_terminate, NULL);
+ edit_init(hostapd_cli_edit_cmd_cb, hostapd_cli_edit_eof_cb,
+ NULL, NULL, NULL, NULL);
+ eloop_register_timeout(ping_interval, 0, hostapd_cli_ping, NULL, NULL);
+
+ eloop_run();
+
+ edit_deinit(NULL, NULL);
+ eloop_cancel_timeout(hostapd_cli_ping, NULL, NULL);
+}
+
+
+static void hostapd_cli_cleanup(void)
+{
+ hostapd_cli_close_connection();
+ if (pid_file)
+ os_daemonize_terminate(pid_file);
+
+ os_program_deinit();
}
@@ -787,7 +1018,6 @@ static void hostapd_cli_action(struct wpa_ctrl *ctrl)
int main(int argc, char *argv[])
{
- int interactive;
int warning_displayed = 0;
int c;
int daemonize = 0;
@@ -835,6 +1065,9 @@ int main(int argc, char *argv[])
hostapd_cli_license);
}
+ if (eloop_init())
+ return -1;
+
for (;;) {
if (ctrl_ifname == NULL) {
struct dirent *dent;
@@ -874,10 +1107,6 @@ int main(int argc, char *argv[])
continue;
}
- signal(SIGINT, hostapd_cli_terminate);
- signal(SIGTERM, hostapd_cli_terminate);
- signal(SIGALRM, hostapd_cli_alarm);
-
if (interactive || action_file) {
if (wpa_ctrl_attach(ctrl_conn) == 0) {
hostapd_cli_attached = 1;
@@ -899,6 +1128,7 @@ int main(int argc, char *argv[])
wpa_request(ctrl_conn, argc - optind, &argv[optind]);
os_free(ctrl_ifname);
+ eloop_destroy();
hostapd_cli_cleanup();
return 0;
}
diff --git a/contrib/wpa/hostapd/main.c b/contrib/wpa/hostapd/main.c
index 9c532d4..56f0002 100644
--- a/contrib/wpa/hostapd/main.c
+++ b/contrib/wpa/hostapd/main.c
@@ -1,15 +1,9 @@
/*
* hostapd / main()
- * Copyright (c) 2002-2010, Jouni Malinen <j@w1.fi>
+ * Copyright (c) 2002-2011, Jouni Malinen <j@w1.fi>
*
- * This program is free software; you can redistribute it and/or modify
- * it under the terms of the GNU General Public License version 2 as
- * published by the Free Software Foundation.
- *
- * Alternatively, this software may be distributed under the terms of BSD
- * license.
- *
- * See README and COPYING for more details.
+ * This software may be distributed under the terms of the BSD license.
+ * See README for more details.
*/
#include "utils/includes.h"
@@ -19,6 +13,7 @@
#include "utils/common.h"
#include "utils/eloop.h"
+#include "crypto/random.h"
#include "crypto/tls.h"
#include "common/version.h"
#include "drivers/driver.h"
@@ -26,6 +21,7 @@
#include "eap_server/tncs.h"
#include "ap/hostapd.h"
#include "ap/ap_config.h"
+#include "ap/ap_drv_ops.h"
#include "config_file.h"
#include "eap_register.h"
#include "dump_state.h"
@@ -36,28 +32,15 @@ extern int wpa_debug_level;
extern int wpa_debug_show_keys;
extern int wpa_debug_timestamp;
+extern struct wpa_driver_ops *wpa_drivers[];
-struct hapd_interfaces {
- size_t count;
- struct hostapd_iface **iface;
-};
-
-
-static int hostapd_for_each_interface(struct hapd_interfaces *interfaces,
- int (*cb)(struct hostapd_iface *iface,
- void *ctx), void *ctx)
-{
- size_t i;
- int ret;
- for (i = 0; i < interfaces->count; i++) {
- ret = cb(interfaces->iface[i], ctx);
- if (ret)
- return ret;
- }
+struct hapd_global {
+ void **drv_priv;
+ size_t drv_count;
+};
- return 0;
-}
+static struct hapd_global global;
#ifndef CONFIG_NO_HOSTAPD_LOGGER
@@ -183,14 +166,9 @@ static struct hostapd_iface * hostapd_init(const char *config_file)
if (hapd_iface == NULL)
goto fail;
- hapd_iface->reload_config = hostapd_reload_config;
- hapd_iface->config_read_cb = hostapd_config_read;
hapd_iface->config_fname = os_strdup(config_file);
if (hapd_iface->config_fname == NULL)
goto fail;
- hapd_iface->ctrl_iface_init = hostapd_ctrl_iface_init;
- hapd_iface->ctrl_iface_deinit = hostapd_ctrl_iface_deinit;
- hapd_iface->for_each_interface = hostapd_for_each_interface;
conf = hostapd_config_read(hapd_iface->config_fname);
if (conf == NULL)
@@ -198,7 +176,7 @@ static struct hostapd_iface * hostapd_init(const char *config_file)
hapd_iface->conf = conf;
hapd_iface->num_bss = conf->num_bss;
- hapd_iface->bss = os_zalloc(conf->num_bss *
+ hapd_iface->bss = os_calloc(conf->num_bss,
sizeof(struct hostapd_data *));
if (hapd_iface->bss == NULL)
goto fail;
@@ -233,6 +211,7 @@ static int hostapd_driver_init(struct hostapd_iface *iface)
struct hostapd_data *hapd = iface->bss[0];
struct hostapd_bss_config *conf = hapd->conf;
u8 *b = conf->bssid;
+ struct wpa_driver_capa capa;
if (hapd->driver == NULL || hapd->driver->hapd_init == NULL) {
wpa_printf(MSG_ERROR, "No hostapd driver wrapper available");
@@ -244,15 +223,33 @@ static int hostapd_driver_init(struct hostapd_iface *iface)
b = NULL;
os_memset(&params, 0, sizeof(params));
+ for (i = 0; wpa_drivers[i]; i++) {
+ if (wpa_drivers[i] != hapd->driver)
+ continue;
+
+ if (global.drv_priv[i] == NULL &&
+ wpa_drivers[i]->global_init) {
+ global.drv_priv[i] = wpa_drivers[i]->global_init();
+ if (global.drv_priv[i] == NULL) {
+ wpa_printf(MSG_ERROR, "Failed to initialize "
+ "driver '%s'",
+ wpa_drivers[i]->name);
+ return -1;
+ }
+ }
+
+ params.global_priv = global.drv_priv[i];
+ break;
+ }
params.bssid = b;
params.ifname = hapd->conf->iface;
- params.ssid = (const u8 *) hapd->conf->ssid.ssid;
+ params.ssid = hapd->conf->ssid.ssid;
params.ssid_len = hapd->conf->ssid.ssid_len;
params.test_socket = hapd->conf->test_socket;
params.use_pae_group_addr = hapd->conf->use_pae_group_addr;
params.num_bridge = hapd->iface->num_bss;
- params.bridge = os_zalloc(hapd->iface->num_bss * sizeof(char *));
+ params.bridge = os_calloc(hapd->iface->num_bss, sizeof(char *));
if (params.bridge == NULL)
return -1;
for (i = 0; i < hapd->iface->num_bss; i++) {
@@ -272,22 +269,13 @@ static int hostapd_driver_init(struct hostapd_iface *iface)
return -1;
}
- return 0;
-}
-
+ if (hapd->driver->get_capa &&
+ hapd->driver->get_capa(hapd->drv_priv, &capa) == 0) {
+ iface->drv_flags = capa.flags;
+ iface->probe_resp_offloads = capa.probe_resp_offloads;
+ }
-static void hostapd_interface_deinit_free(struct hostapd_iface *iface)
-{
- const struct wpa_driver_ops *driver;
- void *drv_priv;
- if (iface == NULL)
- return;
- driver = iface->bss[0]->driver;
- drv_priv = iface->bss[0]->drv_priv;
- hostapd_interface_deinit(iface);
- if (driver && driver->hapd_deinit)
- driver->hapd_deinit(drv_priv);
- hostapd_interface_free(iface);
+ return 0;
}
@@ -309,10 +297,13 @@ hostapd_interface_init(struct hapd_interfaces *interfaces,
iface->bss[0]->conf->logger_stdout_level--;
}
- if (hostapd_driver_init(iface) ||
- hostapd_setup_interface(iface)) {
- hostapd_interface_deinit_free(iface);
- return NULL;
+ if (iface->conf->bss[0].iface[0] != 0 ||
+ hostapd_drv_none(iface->bss[0])) {
+ if (hostapd_driver_init(iface) ||
+ hostapd_setup_interface(iface)) {
+ hostapd_interface_deinit_free(iface);
+ return NULL;
+ }
}
return iface;
@@ -363,8 +354,13 @@ static void handle_dump_state(int sig, void *signal_ctx)
#endif /* CONFIG_NATIVE_WINDOWS */
-static int hostapd_global_init(struct hapd_interfaces *interfaces)
+static int hostapd_global_init(struct hapd_interfaces *interfaces,
+ const char *entropy_file)
{
+ int i;
+
+ os_memset(&global, 0, sizeof(global));
+
hostapd_logger_register_cb(hostapd_logger_cb);
if (eap_server_register_methods()) {
@@ -377,6 +373,8 @@ static int hostapd_global_init(struct hapd_interfaces *interfaces)
return -1;
}
+ random_init(entropy_file);
+
#ifndef CONFIG_NATIVE_WINDOWS
eloop_register_signal(SIGHUP, handle_reload, interfaces);
eloop_register_signal(SIGUSR1, handle_dump_state, interfaces);
@@ -387,16 +385,38 @@ static int hostapd_global_init(struct hapd_interfaces *interfaces)
openlog("hostapd", 0, LOG_DAEMON);
#endif /* CONFIG_NATIVE_WINDOWS */
+ for (i = 0; wpa_drivers[i]; i++)
+ global.drv_count++;
+ if (global.drv_count == 0) {
+ wpa_printf(MSG_ERROR, "No drivers enabled");
+ return -1;
+ }
+ global.drv_priv = os_calloc(global.drv_count, sizeof(void *));
+ if (global.drv_priv == NULL)
+ return -1;
+
return 0;
}
static void hostapd_global_deinit(const char *pid_file)
{
+ int i;
+
+ for (i = 0; wpa_drivers[i] && global.drv_priv; i++) {
+ if (!global.drv_priv[i])
+ continue;
+ wpa_drivers[i]->global_deinit(global.drv_priv[i]);
+ }
+ os_free(global.drv_priv);
+ global.drv_priv = NULL;
+
#ifdef EAP_SERVER_TNC
tncs_global_deinit();
#endif /* EAP_SERVER_TNC */
+ random_deinit();
+
eloop_destroy();
#ifndef CONFIG_NATIVE_WINDOWS
@@ -448,7 +468,7 @@ static void show_version(void)
"hostapd v" VERSION_STR "\n"
"User space daemon for IEEE 802.11 AP management,\n"
"IEEE 802.1X/WPA/WPA2/EAP/RADIUS Authenticator\n"
- "Copyright (c) 2002-2010, Jouni Malinen <j@w1.fi> "
+ "Copyright (c) 2002-2012, Jouni Malinen <j@w1.fi> "
"and contributors\n");
}
@@ -458,15 +478,21 @@ static void usage(void)
show_version();
fprintf(stderr,
"\n"
- "usage: hostapd [-hdBKtv] [-P <PID file>] "
- "<configuration file(s)>\n"
+ "usage: hostapd [-hdBKtv] [-P <PID file>] [-e <entropy file>] "
+ "\\\n"
+ " [-g <global ctrl_iface>] <configuration file(s)>\n"
"\n"
"options:\n"
" -h show this usage\n"
" -d show more debug messages (-dd for even more)\n"
" -B run daemon in the background\n"
+ " -e entropy file\n"
+ " -g global control interface path\n"
" -P PID file\n"
" -K include key data in debug messages\n"
+#ifdef CONFIG_DEBUG_FILE
+ " -f log output to debug file instead of stdout\n"
+#endif /* CONFIG_DEBUG_FILE */
" -t include timestamps in some debug messages\n"
" -v show hostapd version\n");
@@ -474,6 +500,37 @@ static void usage(void)
}
+static const char * hostapd_msg_ifname_cb(void *ctx)
+{
+ struct hostapd_data *hapd = ctx;
+ if (hapd && hapd->iconf && hapd->iconf->bss)
+ return hapd->iconf->bss->iface;
+ return NULL;
+}
+
+
+static int hostapd_get_global_ctrl_iface(struct hapd_interfaces *interfaces,
+ const char *path)
+{
+ char *pos;
+ os_free(interfaces->global_iface_path);
+ interfaces->global_iface_path = os_strdup(path);
+ if (interfaces->global_iface_path == NULL)
+ return -1;
+ pos = os_strrchr(interfaces->global_iface_path, '/');
+ if (pos == NULL) {
+ os_free(interfaces->global_iface_path);
+ interfaces->global_iface_path = NULL;
+ return -1;
+ }
+
+ *pos = '\0';
+ interfaces->global_iface_name = pos + 1;
+
+ return 0;
+}
+
+
int main(int argc, char *argv[])
{
struct hapd_interfaces interfaces;
@@ -481,12 +538,25 @@ int main(int argc, char *argv[])
size_t i;
int c, debug = 0, daemonize = 0;
char *pid_file = NULL;
+ const char *log_file = NULL;
+ const char *entropy_file = NULL;
if (os_program_init())
return -1;
+ os_memset(&interfaces, 0, sizeof(interfaces));
+ interfaces.reload_config = hostapd_reload_config;
+ interfaces.config_read_cb = hostapd_config_read;
+ interfaces.for_each_interface = hostapd_for_each_interface;
+ interfaces.ctrl_iface_init = hostapd_ctrl_iface_init;
+ interfaces.ctrl_iface_deinit = hostapd_ctrl_iface_deinit;
+ interfaces.driver_init = hostapd_driver_init;
+ interfaces.global_iface_path = NULL;
+ interfaces.global_iface_name = NULL;
+ interfaces.global_ctrl_sock = -1;
+
for (;;) {
- c = getopt(argc, argv, "BdhKP:tv");
+ c = getopt(argc, argv, "Bde:f:hKP:tvg:");
if (c < 0)
break;
switch (c) {
@@ -501,6 +571,12 @@ int main(int argc, char *argv[])
case 'B':
daemonize++;
break;
+ case 'e':
+ entropy_file = optarg;
+ break;
+ case 'f':
+ log_file = optarg;
+ break;
case 'K':
wpa_debug_show_keys++;
break;
@@ -515,6 +591,9 @@ int main(int argc, char *argv[])
show_version();
exit(1);
break;
+ case 'g':
+ hostapd_get_global_ctrl_iface(&interfaces, optarg);
+ break;
default:
usage();
@@ -522,18 +601,25 @@ int main(int argc, char *argv[])
}
}
- if (optind == argc)
+ if (optind == argc && interfaces.global_iface_path == NULL)
usage();
+ wpa_msg_register_ifname_cb(hostapd_msg_ifname_cb);
+
+ if (log_file)
+ wpa_debug_open_file(log_file);
+
interfaces.count = argc - optind;
- interfaces.iface = os_malloc(interfaces.count *
- sizeof(struct hostapd_iface *));
- if (interfaces.iface == NULL) {
- wpa_printf(MSG_ERROR, "malloc failed\n");
- return -1;
+ if (interfaces.count) {
+ interfaces.iface = os_calloc(interfaces.count,
+ sizeof(struct hostapd_iface *));
+ if (interfaces.iface == NULL) {
+ wpa_printf(MSG_ERROR, "malloc failed");
+ return -1;
+ }
}
- if (hostapd_global_init(&interfaces))
+ if (hostapd_global_init(&interfaces, entropy_file))
return -1;
/* Initialize interfaces */
@@ -545,12 +631,15 @@ int main(int argc, char *argv[])
goto out;
}
+ hostapd_global_ctrl_iface_init(&interfaces);
+
if (hostapd_global_run(&interfaces, daemonize, pid_file))
goto out;
ret = 0;
out:
+ hostapd_global_ctrl_iface_deinit(&interfaces);
/* Deinitialize all interfaces */
for (i = 0; i < interfaces.count; i++)
hostapd_interface_deinit_free(interfaces.iface[i]);
@@ -559,6 +648,9 @@ int main(int argc, char *argv[])
hostapd_global_deinit(pid_file);
os_free(pid_file);
+ if (log_file)
+ wpa_debug_close_file();
+
os_program_deinit();
return ret;
diff --git a/contrib/wpa/hostapd/nt_password_hash.c b/contrib/wpa/hostapd/nt_password_hash.c
index 839802a..7064b9c 100644
--- a/contrib/wpa/hostapd/nt_password_hash.c
+++ b/contrib/wpa/hostapd/nt_password_hash.c
@@ -2,14 +2,8 @@
* hostapd - Plaintext password to NtPasswordHash
* Copyright (c) 2005, Jouni Malinen <j@w1.fi>
*
- * This program is free software; you can redistribute it and/or modify
- * it under the terms of the GNU General Public License version 2 as
- * published by the Free Software Foundation.
- *
- * Alternatively, this software may be distributed under the terms of BSD
- * license.
- *
- * See README and COPYING for more details.
+ * This software may be distributed under the terms of the BSD license.
+ * See README for more details.
*/
#include "includes.h"
diff --git a/contrib/wpa/patches/openssl-0.9.8x-tls-extensions.patch b/contrib/wpa/patches/openssl-0.9.8x-tls-extensions.patch
new file mode 100644
index 0000000..d1c0dbe
--- /dev/null
+++ b/contrib/wpa/patches/openssl-0.9.8x-tls-extensions.patch
@@ -0,0 +1,396 @@
+This patch adds support for TLS SessionTicket extension (RFC 5077) for
+the parts used by EAP-FAST (RFC 4851).
+
+This is based on the patch from Alexey Kobozev <akobozev@cisco.com>
+(sent to openssl-dev mailing list on Tue, 07 Jun 2005 15:40:58 +0300).
+
+OpenSSL 0.9.8x does not enable TLS extension support by default, so it
+will need to be enabled by adding enable-tlsext to config script
+command line.
+
+
+diff -upr openssl-0.9.8x.orig/ssl/s3_clnt.c openssl-0.9.8x/ssl/s3_clnt.c
+--- openssl-0.9.8x.orig/ssl/s3_clnt.c 2011-12-26 21:38:28.000000000 +0200
++++ openssl-0.9.8x/ssl/s3_clnt.c 2012-07-07 10:46:31.501140621 +0300
+@@ -757,6 +757,21 @@ int ssl3_get_server_hello(SSL *s)
+ goto f_err;
+ }
+
++#ifndef OPENSSL_NO_TLSEXT
++ /* check if we want to resume the session based on external pre-shared secret */
++ if (s->version >= TLS1_VERSION && s->tls_session_secret_cb)
++ {
++ SSL_CIPHER *pref_cipher=NULL;
++ s->session->master_key_length=sizeof(s->session->master_key);
++ if (s->tls_session_secret_cb(s, s->session->master_key, &s->session->master_key_length,
++ NULL, &pref_cipher, s->tls_session_secret_cb_arg))
++ {
++ s->session->cipher=pref_cipher ?
++ pref_cipher : ssl_get_cipher_by_char(s,p+j);
++ }
++ }
++#endif /* OPENSSL_NO_TLSEXT */
++
+ if (j != 0 && j == s->session->session_id_length
+ && memcmp(p,s->session->session_id,j) == 0)
+ {
+@@ -2725,11 +2740,8 @@ int ssl3_check_finished(SSL *s)
+ {
+ int ok;
+ long n;
+- /* If we have no ticket or session ID is non-zero length (a match of
+- * a non-zero session length would never reach here) it cannot be a
+- * resumed session.
+- */
+- if (!s->session->tlsext_tick || s->session->session_id_length)
++ /* If we have no ticket it cannot be a resumed session. */
++ if (!s->session->tlsext_tick)
+ return 1;
+ /* this function is called when we really expect a Certificate
+ * message, so permit appropriate message length */
+diff -upr openssl-0.9.8x.orig/ssl/s3_srvr.c openssl-0.9.8x/ssl/s3_srvr.c
+--- openssl-0.9.8x.orig/ssl/s3_srvr.c 2012-02-16 17:21:17.000000000 +0200
++++ openssl-0.9.8x/ssl/s3_srvr.c 2012-07-07 10:46:31.501140621 +0300
+@@ -1009,6 +1009,59 @@ int ssl3_get_client_hello(SSL *s)
+ SSLerr(SSL_F_SSL3_GET_CLIENT_HELLO,SSL_R_CLIENTHELLO_TLSEXT);
+ goto err;
+ }
++
++ /* Check if we want to use external pre-shared secret for this
++ * handshake for not reused session only. We need to generate
++ * server_random before calling tls_session_secret_cb in order to allow
++ * SessionTicket processing to use it in key derivation. */
++ {
++ unsigned long Time;
++ unsigned char *pos;
++ Time=(unsigned long)time(NULL); /* Time */
++ pos=s->s3->server_random;
++ l2n(Time,pos);
++ if (RAND_pseudo_bytes(pos,SSL3_RANDOM_SIZE-4) <= 0)
++ {
++ al=SSL_AD_INTERNAL_ERROR;
++ goto f_err;
++ }
++ }
++
++ if (!s->hit && s->version >= TLS1_VERSION && s->tls_session_secret_cb)
++ {
++ SSL_CIPHER *pref_cipher=NULL;
++
++ s->session->master_key_length=sizeof(s->session->master_key);
++ if(s->tls_session_secret_cb(s, s->session->master_key, &s->session->master_key_length,
++ ciphers, &pref_cipher, s->tls_session_secret_cb_arg))
++ {
++ s->hit=1;
++ s->session->ciphers=ciphers;
++ s->session->verify_result=X509_V_OK;
++
++ ciphers=NULL;
++
++ /* check if some cipher was preferred by call back */
++ pref_cipher=pref_cipher ? pref_cipher : ssl3_choose_cipher(s, s->session->ciphers, SSL_get_ciphers(s));
++ if (pref_cipher == NULL)
++ {
++ al=SSL_AD_HANDSHAKE_FAILURE;
++ SSLerr(SSL_F_SSL3_GET_CLIENT_HELLO,SSL_R_NO_SHARED_CIPHER);
++ goto f_err;
++ }
++
++ s->session->cipher=pref_cipher;
++
++ if (s->cipher_list)
++ sk_SSL_CIPHER_free(s->cipher_list);
++
++ if (s->cipher_list_by_id)
++ sk_SSL_CIPHER_free(s->cipher_list_by_id);
++
++ s->cipher_list = sk_SSL_CIPHER_dup(s->session->ciphers);
++ s->cipher_list_by_id = sk_SSL_CIPHER_dup(s->session->ciphers);
++ }
++ }
+ #endif
+ /* Worst case, we will use the NULL compression, but if we have other
+ * options, we will now look for them. We have i-1 compression
+@@ -1147,16 +1200,22 @@ int ssl3_send_server_hello(SSL *s)
+ unsigned char *buf;
+ unsigned char *p,*d;
+ int i,sl;
+- unsigned long l,Time;
++ unsigned long l;
++#ifdef OPENSSL_NO_TLSEXT
++ unsigned long Time;
++#endif
+
+ if (s->state == SSL3_ST_SW_SRVR_HELLO_A)
+ {
+ buf=(unsigned char *)s->init_buf->data;
++#ifdef OPENSSL_NO_TLSEXT
+ p=s->s3->server_random;
++ /* Generate server_random if it was not needed previously */
+ Time=(unsigned long)time(NULL); /* Time */
+ l2n(Time,p);
+ if (RAND_pseudo_bytes(p,SSL3_RANDOM_SIZE-4) <= 0)
+ return -1;
++#endif
+ /* Do the message type and length last */
+ d=p= &(buf[4]);
+
+diff -upr openssl-0.9.8x.orig/ssl/ssl_err.c openssl-0.9.8x/ssl/ssl_err.c
+--- openssl-0.9.8x.orig/ssl/ssl_err.c 2012-03-12 16:50:55.000000000 +0200
++++ openssl-0.9.8x/ssl/ssl_err.c 2012-07-07 10:46:31.501140621 +0300
+@@ -264,6 +264,7 @@ static ERR_STRING_DATA SSL_str_functs[]=
+ {ERR_FUNC(SSL_F_TLS1_ENC), "TLS1_ENC"},
+ {ERR_FUNC(SSL_F_TLS1_SETUP_KEY_BLOCK), "TLS1_SETUP_KEY_BLOCK"},
+ {ERR_FUNC(SSL_F_WRITE_PENDING), "WRITE_PENDING"},
++{ERR_FUNC(SSL_F_SSL_SET_SESSION_TICKET_EXT), "SSL_set_session_ticket_ext"},
+ {0,NULL}
+ };
+
+diff -upr openssl-0.9.8x.orig/ssl/ssl.h openssl-0.9.8x/ssl/ssl.h
+--- openssl-0.9.8x.orig/ssl/ssl.h 2012-03-12 16:50:55.000000000 +0200
++++ openssl-0.9.8x/ssl/ssl.h 2012-07-07 10:46:31.501140621 +0300
+@@ -344,6 +344,7 @@ extern "C" {
+ * 'struct ssl_st *' function parameters used to prototype callbacks
+ * in SSL_CTX. */
+ typedef struct ssl_st *ssl_crock_st;
++typedef struct tls_session_ticket_ext_st TLS_SESSION_TICKET_EXT;
+
+ /* used to hold info on the particular ciphers used */
+ typedef struct ssl_cipher_st
+@@ -362,6 +363,9 @@ typedef struct ssl_cipher_st
+
+ DECLARE_STACK_OF(SSL_CIPHER)
+
++typedef int (*tls_session_ticket_ext_cb_fn)(SSL *s, const unsigned char *data, int len, void *arg);
++typedef int (*tls_session_secret_cb_fn)(SSL *s, void *secret, int *secret_len, STACK_OF(SSL_CIPHER) *peer_ciphers, SSL_CIPHER **cipher, void *arg);
++
+ /* Used to hold functions for SSLv2 or SSLv3/TLSv1 functions */
+ typedef struct ssl_method_st
+ {
+@@ -1050,6 +1054,18 @@ struct ssl_st
+
+ /* RFC4507 session ticket expected to be received or sent */
+ int tlsext_ticket_expected;
++
++ /* TLS Session Ticket extension override */
++ TLS_SESSION_TICKET_EXT *tlsext_session_ticket;
++
++ /* TLS Session Ticket extension callback */
++ tls_session_ticket_ext_cb_fn tls_session_ticket_ext_cb;
++ void *tls_session_ticket_ext_cb_arg;
++
++ /* TLS pre-shared secret session resumption */
++ tls_session_secret_cb_fn tls_session_secret_cb;
++ void *tls_session_secret_cb_arg;
++
+ SSL_CTX * initial_ctx; /* initial ctx, used to store sessions */
+ #define session_ctx initial_ctx
+ #else
+@@ -1663,6 +1679,15 @@ void *SSL_COMP_get_compression_methods(v
+ int SSL_COMP_add_compression_method(int id,void *cm);
+ #endif
+
++/* TLS extensions functions */
++int SSL_set_session_ticket_ext(SSL *s, void *ext_data, int ext_len);
++
++int SSL_set_session_ticket_ext_cb(SSL *s, tls_session_ticket_ext_cb_fn cb,
++ void *arg);
++
++/* Pre-shared secret session resumption functions */
++int SSL_set_session_secret_cb(SSL *s, tls_session_secret_cb_fn tls_session_secret_cb, void *arg);
++
+ /* BEGIN ERROR CODES */
+ /* The following lines are auto generated by the script mkerr.pl. Any changes
+ * made after this point may be overwritten when the script is next run.
+@@ -1866,6 +1891,7 @@ void ERR_load_SSL_strings(void);
+ #define SSL_F_TLS1_ENC 210
+ #define SSL_F_TLS1_SETUP_KEY_BLOCK 211
+ #define SSL_F_WRITE_PENDING 212
++#define SSL_F_SSL_SET_SESSION_TICKET_EXT 213
+
+ /* Reason codes. */
+ #define SSL_R_APP_DATA_IN_HANDSHAKE 100
+diff -upr openssl-0.9.8x.orig/ssl/ssl_sess.c openssl-0.9.8x/ssl/ssl_sess.c
+--- openssl-0.9.8x.orig/ssl/ssl_sess.c 2010-02-01 18:48:40.000000000 +0200
++++ openssl-0.9.8x/ssl/ssl_sess.c 2012-07-07 10:46:31.501140621 +0300
+@@ -712,6 +712,61 @@ long SSL_CTX_get_timeout(const SSL_CTX *
+ return(s->session_timeout);
+ }
+
++#ifndef OPENSSL_NO_TLSEXT
++int SSL_set_session_secret_cb(SSL *s, int (*tls_session_secret_cb)(SSL *s, void *secret, int *secret_len,
++ STACK_OF(SSL_CIPHER) *peer_ciphers, SSL_CIPHER **cipher, void *arg), void *arg)
++ {
++ if (s == NULL) return(0);
++ s->tls_session_secret_cb = tls_session_secret_cb;
++ s->tls_session_secret_cb_arg = arg;
++ return(1);
++ }
++
++int SSL_set_session_ticket_ext_cb(SSL *s, tls_session_ticket_ext_cb_fn cb,
++ void *arg)
++ {
++ if (s == NULL) return(0);
++ s->tls_session_ticket_ext_cb = cb;
++ s->tls_session_ticket_ext_cb_arg = arg;
++ return(1);
++ }
++
++int SSL_set_session_ticket_ext(SSL *s, void *ext_data, int ext_len)
++ {
++ if (s->version >= TLS1_VERSION)
++ {
++ if (s->tlsext_session_ticket)
++ {
++ OPENSSL_free(s->tlsext_session_ticket);
++ s->tlsext_session_ticket = NULL;
++ }
++
++ s->tlsext_session_ticket = OPENSSL_malloc(sizeof(TLS_SESSION_TICKET_EXT) + ext_len);
++ if (!s->tlsext_session_ticket)
++ {
++ SSLerr(SSL_F_SSL_SET_SESSION_TICKET_EXT, ERR_R_MALLOC_FAILURE);
++ return 0;
++ }
++
++ if (ext_data)
++ {
++ s->tlsext_session_ticket->length = ext_len;
++ s->tlsext_session_ticket->data = s->tlsext_session_ticket + 1;
++ memcpy(s->tlsext_session_ticket->data, ext_data, ext_len);
++ }
++ else
++ {
++ s->tlsext_session_ticket->length = 0;
++ s->tlsext_session_ticket->data = NULL;
++ }
++
++ return 1;
++ }
++
++ return 0;
++ }
++#endif /* OPENSSL_NO_TLSEXT */
++
+ typedef struct timeout_param_st
+ {
+ SSL_CTX *ctx;
+diff -upr openssl-0.9.8x.orig/ssl/t1_lib.c openssl-0.9.8x/ssl/t1_lib.c
+--- openssl-0.9.8x.orig/ssl/t1_lib.c 2012-01-04 16:25:10.000000000 +0200
++++ openssl-0.9.8x/ssl/t1_lib.c 2012-07-07 10:47:31.153140501 +0300
+@@ -106,6 +106,12 @@ int tls1_new(SSL *s)
+
+ void tls1_free(SSL *s)
+ {
++#ifndef OPENSSL_NO_TLSEXT
++ if (s->tlsext_session_ticket)
++ {
++ OPENSSL_free(s->tlsext_session_ticket);
++ }
++#endif
+ ssl3_free(s);
+ }
+
+@@ -206,8 +212,23 @@ unsigned char *ssl_add_clienthello_tlsex
+ int ticklen;
+ if (!s->new_session && s->session && s->session->tlsext_tick)
+ ticklen = s->session->tlsext_ticklen;
++ else if (s->session && s->tlsext_session_ticket &&
++ s->tlsext_session_ticket->data)
++ {
++ ticklen = s->tlsext_session_ticket->length;
++ s->session->tlsext_tick = OPENSSL_malloc(ticklen);
++ if (!s->session->tlsext_tick)
++ return NULL;
++ memcpy(s->session->tlsext_tick,
++ s->tlsext_session_ticket->data,
++ ticklen);
++ s->session->tlsext_ticklen = ticklen;
++ }
+ else
+ ticklen = 0;
++ if (ticklen == 0 && s->tlsext_session_ticket &&
++ s->tlsext_session_ticket->data == NULL)
++ goto skip_ext;
+ /* Check for enough room 2 for extension type, 2 for len
+ * rest for ticket
+ */
+@@ -221,6 +242,7 @@ unsigned char *ssl_add_clienthello_tlsex
+ ret += ticklen;
+ }
+ }
++ skip_ext:
+
+ if (s->tlsext_status_type == TLSEXT_STATUSTYPE_ocsp &&
+ s->version != DTLS1_VERSION)
+@@ -486,6 +508,15 @@ int ssl_parse_clienthello_tlsext(SSL *s,
+ return 0;
+ renegotiate_seen = 1;
+ }
++ else if (type == TLSEXT_TYPE_session_ticket)
++ {
++ if (s->tls_session_ticket_ext_cb &&
++ !s->tls_session_ticket_ext_cb(s, data, size, s->tls_session_ticket_ext_cb_arg))
++ {
++ *al = TLS1_AD_INTERNAL_ERROR;
++ return 0;
++ }
++ }
+ else if (type == TLSEXT_TYPE_status_request &&
+ s->version != DTLS1_VERSION && s->ctx->tlsext_status_cb)
+ {
+@@ -663,6 +694,12 @@ int ssl_parse_serverhello_tlsext(SSL *s,
+ }
+ else if (type == TLSEXT_TYPE_session_ticket)
+ {
++ if (s->tls_session_ticket_ext_cb &&
++ !s->tls_session_ticket_ext_cb(s, data, size, s->tls_session_ticket_ext_cb_arg))
++ {
++ *al = TLS1_AD_INTERNAL_ERROR;
++ return 0;
++ }
+ if ((SSL_get_options(s) & SSL_OP_NO_TICKET)
+ || (size > 0))
+ {
+@@ -920,6 +957,15 @@ int tls1_process_ticket(SSL *s, unsigned
+ s->tlsext_ticket_expected = 1;
+ return 0; /* Cache miss */
+ }
++ if (s->tls_session_secret_cb)
++ {
++ /* Indicate cache miss here and instead of
++ * generating the session from ticket now,
++ * trigger abbreviated handshake based on
++ * external mechanism to calculate the master
++ * secret later. */
++ return 0;
++ }
+ return tls_decrypt_ticket(s, p, size, session_id, len,
+ ret);
+ }
+diff -upr openssl-0.9.8x.orig/ssl/tls1.h openssl-0.9.8x/ssl/tls1.h
+--- openssl-0.9.8x.orig/ssl/tls1.h 2009-11-08 16:51:54.000000000 +0200
++++ openssl-0.9.8x/ssl/tls1.h 2012-07-07 10:46:31.501140621 +0300
+@@ -401,6 +401,13 @@ SSL_CTX_callback_ctrl(ssl,SSL_CTRL_SET_T
+ #define TLS_MD_MASTER_SECRET_CONST "\x6d\x61\x73\x74\x65\x72\x20\x73\x65\x63\x72\x65\x74" /*master secret*/
+ #endif
+
++/* TLS extension struct */
++struct tls_session_ticket_ext_st
++ {
++ unsigned short length;
++ void *data;
++ };
++
+ #ifdef __cplusplus
+ }
+ #endif
+diff -upr openssl-0.9.8x.orig/util/ssleay.num openssl-0.9.8x/util/ssleay.num
+--- openssl-0.9.8x.orig/util/ssleay.num 2008-06-05 13:57:21.000000000 +0300
++++ openssl-0.9.8x/util/ssleay.num 2012-07-07 10:46:31.505140623 +0300
+@@ -242,3 +242,5 @@ SSL_set_SSL_CTX
+ SSL_get_servername 291 EXIST::FUNCTION:TLSEXT
+ SSL_get_servername_type 292 EXIST::FUNCTION:TLSEXT
+ SSL_CTX_set_client_cert_engine 293 EXIST::FUNCTION:ENGINE
++SSL_set_session_ticket_ext 306 EXIST::FUNCTION:TLSEXT
++SSL_set_session_secret_cb 307 EXIST::FUNCTION:TLSEXT
diff --git a/contrib/wpa/src/Makefile b/contrib/wpa/src/Makefile
deleted file mode 100644
index f47da7b..0000000
--- a/contrib/wpa/src/Makefile
+++ /dev/null
@@ -1,11 +0,0 @@
-SUBDIRS=ap common crypto drivers eapol_auth eapol_supp eap_common eap_peer eap_server l2_packet radius rsn_supp tls utils wps
-
-all:
- for d in $(SUBDIRS); do [ -d $$d ] && $(MAKE) -C $$d; done
-
-clean:
- for d in $(SUBDIRS); do [ -d $$d ] && $(MAKE) -C $$d clean; done
- rm -f *~
-
-install:
- for d in $(SUBDIRS); do [ -d $$d ] && $(MAKE) -C $$d install; done
diff --git a/contrib/wpa/src/ap/Makefile b/contrib/wpa/src/ap/Makefile
deleted file mode 100644
index 9c41962..0000000
--- a/contrib/wpa/src/ap/Makefile
+++ /dev/null
@@ -1,8 +0,0 @@
-all:
- @echo Nothing to be made.
-
-clean:
- rm -f *~ *.o *.d
-
-install:
- @echo Nothing to be made.
diff --git a/contrib/wpa/src/ap/accounting.c b/contrib/wpa/src/ap/accounting.c
index 7939c68..9540531 100644
--- a/contrib/wpa/src/ap/accounting.c
+++ b/contrib/wpa/src/ap/accounting.c
@@ -1,15 +1,9 @@
/*
* hostapd / RADIUS Accounting
- * Copyright (c) 2002-2009, Jouni Malinen <j@w1.fi>
+ * Copyright (c) 2002-2009, 2012, Jouni Malinen <j@w1.fi>
*
- * This program is free software; you can redistribute it and/or modify
- * it under the terms of the GNU General Public License version 2 as
- * published by the Free Software Foundation.
- *
- * Alternatively, this software may be distributed under the terms of BSD
- * license.
- *
- * See README and COPYING for more details.
+ * This software may be distributed under the terms of the BSD license.
+ * See README for more details.
*/
#include "utils/includes.h"
@@ -23,6 +17,7 @@
#include "ieee802_1x.h"
#include "ap_config.h"
#include "sta_info.h"
+#include "ap_drv_ops.h"
#include "accounting.h"
@@ -31,8 +26,8 @@
* input/output octets and updates Acct-{Input,Output}-Gigawords. */
#define ACCT_DEFAULT_UPDATE_INTERVAL 300
-static void accounting_sta_get_id(struct hostapd_data *hapd,
- struct sta_info *sta);
+static void accounting_sta_interim(struct hostapd_data *hapd,
+ struct sta_info *sta);
static struct radius_msg * accounting_msg(struct hostapd_data *hapd,
@@ -44,6 +39,7 @@ static struct radius_msg * accounting_msg(struct hostapd_data *hapd,
u8 *val;
size_t len;
int i;
+ struct wpabuf *b;
msg = radius_msg_new(RADIUS_CODE_ACCOUNTING_REQUEST,
radius_client_get_id(hapd->radius));
@@ -72,7 +68,9 @@ static struct radius_msg * accounting_msg(struct hostapd_data *hapd,
goto fail;
}
- if (!radius_msg_add_attr_int32(msg, RADIUS_ATTR_ACCT_AUTHENTIC,
+ if (!hostapd_config_get_radius_attr(hapd->conf->radius_acct_req_attr,
+ RADIUS_ATTR_ACCT_AUTHENTIC) &&
+ !radius_msg_add_attr_int32(msg, RADIUS_ATTR_ACCT_AUTHENTIC,
hapd->conf->ieee802_1x ?
RADIUS_ACCT_AUTHENTIC_RADIUS :
RADIUS_ACCT_AUTHENTIC_LOCAL)) {
@@ -81,7 +79,17 @@ static struct radius_msg * accounting_msg(struct hostapd_data *hapd,
}
if (sta) {
+ /* Use 802.1X identity if available */
val = ieee802_1x_get_identity(sta->eapol_sm, &len);
+
+ /* Use RADIUS ACL identity if 802.1X provides no identity */
+ if (!val && sta->identity) {
+ val = (u8 *) sta->identity;
+ len = os_strlen(sta->identity);
+ }
+
+ /* Use STA MAC if neither 802.1X nor RADIUS ACL provided
+ * identity */
if (!val) {
os_snprintf(buf, sizeof(buf), RADIUS_ADDR_FORMAT,
MAC2STR(sta->addr));
@@ -96,70 +104,11 @@ static struct radius_msg * accounting_msg(struct hostapd_data *hapd,
}
}
- if (hapd->conf->own_ip_addr.af == AF_INET &&
- !radius_msg_add_attr(msg, RADIUS_ATTR_NAS_IP_ADDRESS,
- (u8 *) &hapd->conf->own_ip_addr.u.v4, 4)) {
- printf("Could not add NAS-IP-Address\n");
- goto fail;
- }
-
-#ifdef CONFIG_IPV6
- if (hapd->conf->own_ip_addr.af == AF_INET6 &&
- !radius_msg_add_attr(msg, RADIUS_ATTR_NAS_IPV6_ADDRESS,
- (u8 *) &hapd->conf->own_ip_addr.u.v6, 16)) {
- printf("Could not add NAS-IPv6-Address\n");
- goto fail;
- }
-#endif /* CONFIG_IPV6 */
-
- if (hapd->conf->nas_identifier &&
- !radius_msg_add_attr(msg, RADIUS_ATTR_NAS_IDENTIFIER,
- (u8 *) hapd->conf->nas_identifier,
- os_strlen(hapd->conf->nas_identifier))) {
- printf("Could not add NAS-Identifier\n");
- goto fail;
- }
-
- if (sta &&
- !radius_msg_add_attr_int32(msg, RADIUS_ATTR_NAS_PORT, sta->aid)) {
- printf("Could not add NAS-Port\n");
- goto fail;
- }
-
- os_snprintf(buf, sizeof(buf), RADIUS_802_1X_ADDR_FORMAT ":%s",
- MAC2STR(hapd->own_addr), hapd->conf->ssid.ssid);
- if (!radius_msg_add_attr(msg, RADIUS_ATTR_CALLED_STATION_ID,
- (u8 *) buf, os_strlen(buf))) {
- printf("Could not add Called-Station-Id\n");
+ if (add_common_radius_attr(hapd, hapd->conf->radius_acct_req_attr, sta,
+ msg) < 0)
goto fail;
- }
if (sta) {
- os_snprintf(buf, sizeof(buf), RADIUS_802_1X_ADDR_FORMAT,
- MAC2STR(sta->addr));
- if (!radius_msg_add_attr(msg, RADIUS_ATTR_CALLING_STATION_ID,
- (u8 *) buf, os_strlen(buf))) {
- printf("Could not add Calling-Station-Id\n");
- goto fail;
- }
-
- if (!radius_msg_add_attr_int32(
- msg, RADIUS_ATTR_NAS_PORT_TYPE,
- RADIUS_NAS_PORT_TYPE_IEEE_802_11)) {
- printf("Could not add NAS-Port-Type\n");
- goto fail;
- }
-
- os_snprintf(buf, sizeof(buf), "CONNECT %d%sMbps %s",
- radius_sta_rate(hapd, sta) / 2,
- (radius_sta_rate(hapd, sta) & 1) ? ".5" : "",
- radius_mode_txt(hapd));
- if (!radius_msg_add_attr(msg, RADIUS_ATTR_CONNECT_INFO,
- (u8 *) buf, os_strlen(buf))) {
- printf("Could not add Connect-Info\n");
- goto fail;
- }
-
for (i = 0; ; i++) {
val = ieee802_1x_get_radius_class(sta->eapol_sm, &len,
i);
@@ -172,6 +121,24 @@ static struct radius_msg * accounting_msg(struct hostapd_data *hapd,
goto fail;
}
}
+
+ b = ieee802_1x_get_radius_cui(sta->eapol_sm);
+ if (b &&
+ !radius_msg_add_attr(msg,
+ RADIUS_ATTR_CHARGEABLE_USER_IDENTITY,
+ wpabuf_head(b), wpabuf_len(b))) {
+ wpa_printf(MSG_ERROR, "Could not add CUI");
+ goto fail;
+ }
+
+ if (!b && sta->radius_cui &&
+ !radius_msg_add_attr(msg,
+ RADIUS_ATTR_CHARGEABLE_USER_IDENTITY,
+ (u8 *) sta->radius_cui,
+ os_strlen(sta->radius_cui))) {
+ wpa_printf(MSG_ERROR, "Could not add CUI from ACL");
+ goto fail;
+ }
}
return msg;
@@ -186,7 +153,7 @@ static int accounting_sta_update_stats(struct hostapd_data *hapd,
struct sta_info *sta,
struct hostap_sta_driver_data *data)
{
- if (hapd->drv.read_sta_data(hapd, data, sta->addr))
+ if (hostapd_drv_read_sta_data(hapd, data, sta->addr))
return -1;
if (sta->last_rx_bytes > data->rx_bytes)
@@ -235,21 +202,22 @@ static void accounting_interim_update(void *eloop_ctx, void *timeout_ctx)
void accounting_sta_start(struct hostapd_data *hapd, struct sta_info *sta)
{
struct radius_msg *msg;
+ struct os_time t;
int interval;
if (sta->acct_session_started)
return;
- accounting_sta_get_id(hapd, sta);
hostapd_logger(hapd, sta->addr, HOSTAPD_MODULE_RADIUS,
HOSTAPD_LEVEL_INFO,
"starting accounting session %08X-%08X",
sta->acct_session_id_hi, sta->acct_session_id_lo);
- time(&sta->acct_session_start);
+ os_get_time(&t);
+ sta->acct_session_start = t.sec;
sta->last_rx_bytes = sta->last_tx_bytes = 0;
sta->acct_input_gigawords = sta->acct_output_gigawords = 0;
- hapd->drv.sta_clear_stats(hapd, sta->addr);
+ hostapd_drv_sta_clear_stats(hapd, sta->addr);
if (!hapd->conf->radius->acct_server)
return;
@@ -262,8 +230,9 @@ void accounting_sta_start(struct hostapd_data *hapd, struct sta_info *sta)
hapd, sta);
msg = accounting_msg(hapd, sta, RADIUS_ACCT_STATUS_TYPE_START);
- if (msg)
- radius_client_send(hapd->radius, msg, RADIUS_ACCT, sta->addr);
+ if (msg &&
+ radius_client_send(hapd->radius, msg, RADIUS_ACCT, sta->addr) < 0)
+ radius_msg_free(msg);
sta->acct_session_started = 1;
}
@@ -275,6 +244,7 @@ static void accounting_sta_report(struct hostapd_data *hapd,
struct radius_msg *msg;
int cause = sta->acct_terminate_cause;
struct hostap_sta_driver_data data;
+ struct os_time now;
u32 gigawords;
if (!hapd->conf->radius->acct_server)
@@ -288,8 +258,9 @@ static void accounting_sta_report(struct hostapd_data *hapd,
return;
}
+ os_get_time(&now);
if (!radius_msg_add_attr_int32(msg, RADIUS_ATTR_ACCT_SESSION_TIME,
- time(NULL) - sta->acct_session_start)) {
+ now.sec - sta->acct_session_start)) {
printf("Could not add Acct-Session-Time\n");
goto fail;
}
@@ -344,7 +315,7 @@ static void accounting_sta_report(struct hostapd_data *hapd,
}
if (!radius_msg_add_attr_int32(msg, RADIUS_ATTR_EVENT_TIMESTAMP,
- time(NULL))) {
+ now.sec)) {
printf("Could not add Event-Timestamp\n");
goto fail;
}
@@ -359,9 +330,10 @@ static void accounting_sta_report(struct hostapd_data *hapd,
goto fail;
}
- radius_client_send(hapd->radius, msg,
- stop ? RADIUS_ACCT : RADIUS_ACCT_INTERIM,
- sta->addr);
+ if (radius_client_send(hapd->radius, msg,
+ stop ? RADIUS_ACCT : RADIUS_ACCT_INTERIM,
+ sta->addr) < 0)
+ goto fail;
return;
fail:
@@ -374,7 +346,8 @@ static void accounting_sta_report(struct hostapd_data *hapd,
* @hapd: hostapd BSS data
* @sta: The station
*/
-void accounting_sta_interim(struct hostapd_data *hapd, struct sta_info *sta)
+static void accounting_sta_interim(struct hostapd_data *hapd,
+ struct sta_info *sta)
{
if (sta->acct_session_started)
accounting_sta_report(hapd, sta, 0);
@@ -401,7 +374,7 @@ void accounting_sta_stop(struct hostapd_data *hapd, struct sta_info *sta)
}
-static void accounting_sta_get_id(struct hostapd_data *hapd,
+void accounting_sta_get_id(struct hostapd_data *hapd,
struct sta_info *sta)
{
sta->acct_session_id_lo = hapd->acct_session_id_lo++;
@@ -464,7 +437,8 @@ static void accounting_report_state(struct hostapd_data *hapd, int on)
return;
}
- radius_client_send(hapd->radius, msg, RADIUS_ACCT, NULL);
+ if (radius_client_send(hapd->radius, msg, RADIUS_ACCT, NULL) < 0)
+ radius_msg_free(msg);
}
@@ -475,9 +449,12 @@ static void accounting_report_state(struct hostapd_data *hapd, int on)
*/
int accounting_init(struct hostapd_data *hapd)
{
+ struct os_time now;
+
/* Acct-Session-Id should be unique over reboots. If reliable clock is
* not available, this could be replaced with reboot counter, etc. */
- hapd->acct_session_id_hi = time(NULL);
+ os_get_time(&now);
+ hapd->acct_session_id_hi = now.sec;
if (radius_client_register(hapd->radius, RADIUS_ACCT,
accounting_receive, hapd))
diff --git a/contrib/wpa/src/ap/accounting.h b/contrib/wpa/src/ap/accounting.h
index f3d60f0..dcc54ee 100644
--- a/contrib/wpa/src/ap/accounting.h
+++ b/contrib/wpa/src/ap/accounting.h
@@ -2,21 +2,19 @@
* hostapd / RADIUS Accounting
* Copyright (c) 2002-2005, Jouni Malinen <j@w1.fi>
*
- * This program is free software; you can redistribute it and/or modify
- * it under the terms of the GNU General Public License version 2 as
- * published by the Free Software Foundation.
- *
- * Alternatively, this software may be distributed under the terms of BSD
- * license.
- *
- * See README and COPYING for more details.
+ * This software may be distributed under the terms of the BSD license.
+ * See README for more details.
*/
#ifndef ACCOUNTING_H
#define ACCOUNTING_H
-void accounting_sta_interim(struct hostapd_data *hapd, struct sta_info *sta);
#ifdef CONFIG_NO_ACCOUNTING
+static inline void accounting_sta_get_id(struct hostapd_data *hapd,
+ struct sta_info *sta)
+{
+}
+
static inline void accounting_sta_start(struct hostapd_data *hapd,
struct sta_info *sta)
{
@@ -36,6 +34,7 @@ static inline void accounting_deinit(struct hostapd_data *hapd)
{
}
#else /* CONFIG_NO_ACCOUNTING */
+void accounting_sta_get_id(struct hostapd_data *hapd, struct sta_info *sta);
void accounting_sta_start(struct hostapd_data *hapd, struct sta_info *sta);
void accounting_sta_stop(struct hostapd_data *hapd, struct sta_info *sta);
int accounting_init(struct hostapd_data *hapd);
diff --git a/contrib/wpa/src/ap/ap_config.c b/contrib/wpa/src/ap/ap_config.c
index 5996993..25d26e5 100644
--- a/contrib/wpa/src/ap/ap_config.c
+++ b/contrib/wpa/src/ap/ap_config.c
@@ -1,15 +1,9 @@
/*
* hostapd / Configuration helper functions
- * Copyright (c) 2003-2009, Jouni Malinen <j@w1.fi>
+ * Copyright (c) 2003-2012, Jouni Malinen <j@w1.fi>
*
- * This program is free software; you can redistribute it and/or modify
- * it under the terms of the GNU General Public License version 2 as
- * published by the Free Software Foundation.
- *
- * Alternatively, this software may be distributed under the terms of BSD
- * license.
- *
- * See README and COPYING for more details.
+ * This software may be distributed under the terms of the BSD license.
+ * See README for more details.
*/
#include "utils/includes.h"
@@ -74,6 +68,8 @@ void hostapd_config_defaults_bss(struct hostapd_bss_config *bss)
bss->max_listen_interval = 65535;
+ bss->pwd_group = 19; /* ECC: GF(p=256) */
+
#ifdef CONFIG_IEEE80211W
bss->assoc_sa_query_max_timeout = 1000;
bss->assoc_sa_query_retry_timeout = 201;
@@ -84,23 +80,44 @@ void hostapd_config_defaults_bss(struct hostapd_bss_config *bss)
bss->pac_key_lifetime = 7 * 24 * 60 * 60;
bss->pac_key_refresh_time = 1 * 24 * 60 * 60;
#endif /* EAP_SERVER_FAST */
+
+ /* Set to -1 as defaults depends on HT in setup */
+ bss->wmm_enabled = -1;
+
+#ifdef CONFIG_IEEE80211R
+ bss->ft_over_ds = 1;
+#endif /* CONFIG_IEEE80211R */
+
+ bss->radius_das_time_window = 300;
}
struct hostapd_config * hostapd_config_defaults(void)
{
+#define ecw2cw(ecw) ((1 << (ecw)) - 1)
+
struct hostapd_config *conf;
struct hostapd_bss_config *bss;
- int i;
const int aCWmin = 4, aCWmax = 10;
const struct hostapd_wmm_ac_params ac_bk =
{ aCWmin, aCWmax, 7, 0, 0 }; /* background traffic */
const struct hostapd_wmm_ac_params ac_be =
{ aCWmin, aCWmax, 3, 0, 0 }; /* best effort traffic */
const struct hostapd_wmm_ac_params ac_vi = /* video traffic */
- { aCWmin - 1, aCWmin, 2, 3000 / 32, 1 };
+ { aCWmin - 1, aCWmin, 2, 3000 / 32, 0 };
const struct hostapd_wmm_ac_params ac_vo = /* voice traffic */
- { aCWmin - 2, aCWmin - 1, 2, 1500 / 32, 1 };
+ { aCWmin - 2, aCWmin - 1, 2, 1500 / 32, 0 };
+ const struct hostapd_tx_queue_params txq_bk =
+ { 7, ecw2cw(aCWmin), ecw2cw(aCWmax), 0 };
+ const struct hostapd_tx_queue_params txq_be =
+ { 3, ecw2cw(aCWmin), 4 * (ecw2cw(aCWmin) + 1) - 1, 0};
+ const struct hostapd_tx_queue_params txq_vi =
+ { 1, (ecw2cw(aCWmin) + 1) / 2 - 1, ecw2cw(aCWmin), 30};
+ const struct hostapd_tx_queue_params txq_vo =
+ { 1, (ecw2cw(aCWmin) + 1) / 4 - 1,
+ (ecw2cw(aCWmin) + 1) / 2 - 1, 15};
+
+#undef ecw2cw
conf = os_zalloc(sizeof(*conf));
bss = os_zalloc(sizeof(*bss));
@@ -129,16 +146,21 @@ struct hostapd_config * hostapd_config_defaults(void)
conf->fragm_threshold = -1; /* user driver default: 2346 */
conf->send_probe_response = 1;
- for (i = 0; i < NUM_TX_QUEUES; i++)
- conf->tx_queue[i].aifs = -1; /* use hw default */
-
conf->wmm_ac_params[0] = ac_be;
conf->wmm_ac_params[1] = ac_bk;
conf->wmm_ac_params[2] = ac_vi;
conf->wmm_ac_params[3] = ac_vo;
+ conf->tx_queue[0] = txq_vo;
+ conf->tx_queue[1] = txq_vi;
+ conf->tx_queue[2] = txq_be;
+ conf->tx_queue[3] = txq_bk;
+
conf->ht_capab = HT_CAP_INFO_SMPS_DISABLED;
+ conf->ap_table_max_size = 255;
+ conf->ap_table_expiration_time = 60;
+
return conf;
}
@@ -319,6 +341,30 @@ static void hostapd_config_free_radius(struct hostapd_radius_server *servers,
}
+struct hostapd_radius_attr *
+hostapd_config_get_radius_attr(struct hostapd_radius_attr *attr, u8 type)
+{
+ for (; attr; attr = attr->next) {
+ if (attr->type == type)
+ return attr;
+ }
+ return NULL;
+}
+
+
+static void hostapd_config_free_radius_attr(struct hostapd_radius_attr *attr)
+{
+ struct hostapd_radius_attr *prev;
+
+ while (attr) {
+ prev = attr;
+ attr = attr->next;
+ wpabuf_free(prev->val);
+ os_free(prev);
+ }
+}
+
+
static void hostapd_config_free_eap_user(struct hostapd_eap_user *user)
{
os_free(user->identity);
@@ -365,6 +411,7 @@ static void hostapd_config_free_bss(struct hostapd_bss_config *conf)
user = user->next;
hostapd_config_free_eap_user(prev_user);
}
+ os_free(conf->eap_user_sqlite);
os_free(conf->dump_log_name);
os_free(conf->eap_req_id_text);
@@ -375,6 +422,8 @@ static void hostapd_config_free_bss(struct hostapd_bss_config *conf)
conf->radius->num_auth_servers);
hostapd_config_free_radius(conf->radius->acct_servers,
conf->radius->num_acct_servers);
+ hostapd_config_free_radius_attr(conf->radius_auth_req_attr);
+ hostapd_config_free_radius_attr(conf->radius_acct_req_attr);
os_free(conf->rsn_preauth_interfaces);
os_free(conf->ctrl_interface);
os_free(conf->ca_cert);
@@ -389,6 +438,7 @@ static void hostapd_config_free_bss(struct hostapd_bss_config *conf)
os_free(conf->radius_server_clients);
os_free(conf->test_socket);
os_free(conf->radius);
+ os_free(conf->radius_das_shared_secret);
hostapd_config_free_vlan(conf);
if (conf->ssid.dyn_vlan_keys) {
struct hostapd_ssid *ssid = &conf->ssid;
@@ -403,6 +453,8 @@ static void hostapd_config_free_bss(struct hostapd_bss_config *conf)
ssid->dyn_vlan_keys = NULL;
}
+ os_free(conf->time_zone);
+
#ifdef CONFIG_IEEE80211R
{
struct ft_remote_r0kh *r0kh, *r0kh_prev;
@@ -433,7 +485,6 @@ static void hostapd_config_free_bss(struct hostapd_bss_config *conf)
os_free(conf->model_name);
os_free(conf->model_number);
os_free(conf->serial_number);
- os_free(conf->device_type);
os_free(conf->config_methods);
os_free(conf->ap_pin);
os_free(conf->extra_cred);
@@ -444,7 +495,30 @@ static void hostapd_config_free_bss(struct hostapd_bss_config *conf)
os_free(conf->model_description);
os_free(conf->model_url);
os_free(conf->upc);
+ wpabuf_free(conf->wps_nfc_dh_pubkey);
+ wpabuf_free(conf->wps_nfc_dh_privkey);
+ wpabuf_free(conf->wps_nfc_dev_pw);
#endif /* CONFIG_WPS */
+
+ os_free(conf->roaming_consortium);
+ os_free(conf->venue_name);
+ os_free(conf->nai_realm_data);
+ os_free(conf->network_auth_type);
+ os_free(conf->anqp_3gpp_cell_net);
+ os_free(conf->domain_name);
+
+#ifdef CONFIG_RADIUS_TEST
+ os_free(conf->dump_msk_file);
+#endif /* CONFIG_RADIUS_TEST */
+
+#ifdef CONFIG_HS20
+ os_free(conf->hs20_oper_friendly_name);
+ os_free(conf->hs20_wan_metrics);
+ os_free(conf->hs20_connection_capability);
+ os_free(conf->hs20_operating_class);
+#endif /* CONFIG_HS20 */
+
+ wpabuf_free(conf->vendor_elements);
}
@@ -549,57 +623,3 @@ const u8 * hostapd_get_psk(const struct hostapd_bss_config *conf,
return NULL;
}
-
-
-const struct hostapd_eap_user *
-hostapd_get_eap_user(const struct hostapd_bss_config *conf, const u8 *identity,
- size_t identity_len, int phase2)
-{
- struct hostapd_eap_user *user = conf->eap_user;
-
-#ifdef CONFIG_WPS
- if (conf->wps_state && identity_len == WSC_ID_ENROLLEE_LEN &&
- os_memcmp(identity, WSC_ID_ENROLLEE, WSC_ID_ENROLLEE_LEN) == 0) {
- static struct hostapd_eap_user wsc_enrollee;
- os_memset(&wsc_enrollee, 0, sizeof(wsc_enrollee));
- wsc_enrollee.methods[0].method = eap_server_get_type(
- "WSC", &wsc_enrollee.methods[0].vendor);
- return &wsc_enrollee;
- }
-
- if (conf->wps_state && identity_len == WSC_ID_REGISTRAR_LEN &&
- os_memcmp(identity, WSC_ID_REGISTRAR, WSC_ID_REGISTRAR_LEN) == 0) {
- static struct hostapd_eap_user wsc_registrar;
- os_memset(&wsc_registrar, 0, sizeof(wsc_registrar));
- wsc_registrar.methods[0].method = eap_server_get_type(
- "WSC", &wsc_registrar.methods[0].vendor);
- wsc_registrar.password = (u8 *) conf->ap_pin;
- wsc_registrar.password_len = conf->ap_pin ?
- os_strlen(conf->ap_pin) : 0;
- return &wsc_registrar;
- }
-#endif /* CONFIG_WPS */
-
- while (user) {
- if (!phase2 && user->identity == NULL) {
- /* Wildcard match */
- break;
- }
-
- if (user->phase2 == !!phase2 && user->wildcard_prefix &&
- identity_len >= user->identity_len &&
- os_memcmp(user->identity, identity, user->identity_len) ==
- 0) {
- /* Wildcard prefix match */
- break;
- }
-
- if (user->phase2 == !!phase2 &&
- user->identity_len == identity_len &&
- os_memcmp(user->identity, identity, identity_len) == 0)
- break;
- user = user->next;
- }
-
- return user;
-}
diff --git a/contrib/wpa/src/ap/ap_config.h b/contrib/wpa/src/ap/ap_config.h
index f509b5b..a1d2b04 100644
--- a/contrib/wpa/src/ap/ap_config.h
+++ b/contrib/wpa/src/ap/ap_config.h
@@ -1,15 +1,9 @@
/*
* hostapd / Configuration definitions and helpers functions
- * Copyright (c) 2003-2009, Jouni Malinen <j@w1.fi>
+ * Copyright (c) 2003-2012, Jouni Malinen <j@w1.fi>
*
- * This program is free software; you can redistribute it and/or modify
- * it under the terms of the GNU General Public License version 2 as
- * published by the Free Software Foundation.
- *
- * Alternatively, this software may be distributed under the terms of BSD
- * license.
- *
- * See README and COPYING for more details.
+ * This software may be distributed under the terms of the BSD license.
+ * See README for more details.
*/
#ifndef HOSTAPD_CONFIG_H
@@ -18,6 +12,8 @@
#include "common/defs.h"
#include "ip_addr.h"
#include "common/wpa_common.h"
+#include "common/ieee802_11_common.h"
+#include "wps/wps.h"
#define MAX_STA_COUNT 2007
#define MAX_VLAN_ID 4094
@@ -53,9 +49,10 @@ typedef enum hostap_security_policy {
} secpolicy;
struct hostapd_ssid {
- char ssid[HOSTAPD_MAX_SSID_LEN + 1];
+ u8 ssid[HOSTAPD_MAX_SSID_LEN];
size_t ssid_len;
- int ssid_set;
+ unsigned int ssid_set:1;
+ unsigned int utf8_ssid:1;
char vlan[IFNAMSIZ + 1];
secpolicy security_policy;
@@ -70,6 +67,10 @@ struct hostapd_ssid {
#define DYNAMIC_VLAN_OPTIONAL 1
#define DYNAMIC_VLAN_REQUIRED 2
int dynamic_vlan;
+#define DYNAMIC_VLAN_NAMING_WITHOUT_DEVICE 0
+#define DYNAMIC_VLAN_NAMING_WITH_DEVICE 1
+#define DYNAMIC_VLAN_NAMING_END 2
+ int vlan_naming;
#ifdef CONFIG_FULL_DYNAMIC_VLAN
char *vlan_tagged_interface;
#endif /* CONFIG_FULL_DYNAMIC_VLAN */
@@ -96,6 +97,11 @@ struct hostapd_vlan {
};
#define PMK_LEN 32
+struct hostapd_sta_wpa_psk_short {
+ struct hostapd_sta_wpa_psk_short *next;
+ u8 psk[PMK_LEN];
+};
+
struct hostapd_wpa_psk {
struct hostapd_wpa_psk *next;
int group;
@@ -103,7 +109,6 @@ struct hostapd_wpa_psk {
u8 addr[ETH_ALEN];
};
-#define EAP_USER_MAX_METHODS 8
struct hostapd_eap_user {
struct hostapd_eap_user *next;
u8 *identity;
@@ -111,7 +116,7 @@ struct hostapd_eap_user {
struct {
int vendor;
u32 method;
- } methods[EAP_USER_MAX_METHODS];
+ } methods[EAP_MAX_METHODS];
u8 *password;
size_t password_len;
int phase2;
@@ -122,25 +127,52 @@ struct hostapd_eap_user {
int ttls_auth; /* EAP_TTLS_AUTH_* bitfield */
};
+struct hostapd_radius_attr {
+ u8 type;
+ struct wpabuf *val;
+ struct hostapd_radius_attr *next;
+};
+
-#define NUM_TX_QUEUES 8
+#define NUM_TX_QUEUES 4
struct hostapd_tx_queue_params {
int aifs;
int cwmin;
int cwmax;
int burst; /* maximum burst time in 0.1 ms, i.e., 10 = 1 ms */
- int configured;
};
-struct hostapd_wmm_ac_params {
- int cwmin;
- int cwmax;
- int aifs;
- int txop_limit; /* in units of 32us */
- int admission_control_mandatory;
+
+#define MAX_ROAMING_CONSORTIUM_LEN 15
+
+struct hostapd_roaming_consortium {
+ u8 len;
+ u8 oi[MAX_ROAMING_CONSORTIUM_LEN];
+};
+
+struct hostapd_lang_string {
+ u8 lang[3];
+ u8 name_len;
+ u8 name[252];
};
+#define MAX_NAI_REALMS 10
+#define MAX_NAI_REALMLEN 255
+#define MAX_NAI_EAP_METHODS 5
+#define MAX_NAI_AUTH_TYPES 4
+struct hostapd_nai_realm_data {
+ u8 encoding;
+ char realm_buf[MAX_NAI_REALMLEN + 1];
+ char *realm[MAX_NAI_REALMS];
+ u8 eap_method_count;
+ struct hostapd_nai_realm_eap {
+ u8 eap_method;
+ u8 num_auths;
+ u8 auth_id[MAX_NAI_AUTH_TYPES];
+ u8 auth_val[MAX_NAI_AUTH_TYPES];
+ } eap_method[MAX_NAI_EAP_METHODS];
+};
/**
* struct hostapd_bss_config - Per-BSS configuration
@@ -148,6 +180,7 @@ struct hostapd_wmm_ac_params {
struct hostapd_bss_config {
char iface[IFNAMSIZ + 1];
char bridge[IFNAMSIZ + 1];
+ char wds_bridge[IFNAMSIZ + 1];
enum hostapd_logger_level logger_syslog_level, logger_stdout_level;
@@ -165,11 +198,21 @@ struct hostapd_bss_config {
int eap_server; /* Use internal EAP server instead of external
* RADIUS server */
struct hostapd_eap_user *eap_user;
+ char *eap_user_sqlite;
char *eap_sim_db;
struct hostapd_ip_addr own_ip_addr;
char *nas_identifier;
struct hostapd_radius_servers *radius;
int acct_interim_interval;
+ int radius_request_cui;
+ struct hostapd_radius_attr *radius_auth_req_attr;
+ struct hostapd_radius_attr *radius_acct_req_attr;
+ int radius_das_port;
+ unsigned int radius_das_time_window;
+ int radius_das_require_event_timestamp;
+ struct hostapd_ip_addr radius_das_client_addr;
+ u8 *radius_das_shared_secret;
+ size_t radius_das_shared_secret_len;
struct hostapd_ssid ssid;
@@ -198,6 +241,7 @@ struct hostapd_bss_config {
struct mac_acl_entry *deny_mac;
int num_deny_mac;
int wds_sta;
+ int isolate;
int auth_algs; /* bitfield of allowed IEEE 802.11 authentication
* algorithms, WPA_AUTH_ALG_{OPEN,SHARED,LEAP} */
@@ -211,6 +255,11 @@ struct hostapd_bss_config {
/* dot11AssociationSAQueryRetryTimeout (in TUs) */
int assoc_sa_query_retry_timeout;
#endif /* CONFIG_IEEE80211W */
+ enum {
+ PSK_RADIUS_IGNORED = 0,
+ PSK_RADIUS_ACCEPTED = 1,
+ PSK_RADIUS_REQUIRED = 2
+ } wpa_psk_radius;
int wpa_pairwise;
int wpa_group;
int wpa_group_rekey;
@@ -231,6 +280,7 @@ struct hostapd_bss_config {
struct ft_remote_r0kh *r0kh_list;
struct ft_remote_r1kh *r1kh_list;
int pmk_r1_push;
+ int ft_over_ds;
#endif /* CONFIG_IEEE80211R */
char *ctrl_interface; /* directory for UNIX domain sockets */
@@ -254,6 +304,8 @@ struct hostapd_bss_config {
int pac_key_refresh_time;
int eap_sim_aka_result_ind;
int tnc;
+ int fragment_size;
+ u16 pwd_group;
char *radius_server_clients;
int radius_server_auth_port;
@@ -283,6 +335,7 @@ struct hostapd_bss_config {
*/
u16 max_listen_interval;
+ int disable_pmksa_caching;
int okc; /* Opportunistic Key Caching */
int wps_state;
@@ -295,7 +348,7 @@ struct hostapd_bss_config {
char *model_name;
char *model_number;
char *serial_number;
- char *device_type;
+ u8 device_type[WPS_DEV_TYPE_LEN];
char *config_methods;
u8 os_version[4];
char *ap_pin;
@@ -311,7 +364,97 @@ struct hostapd_bss_config {
char *model_description;
char *model_url;
char *upc;
+ struct wpabuf *wps_vendor_ext[MAX_WPS_VENDOR_EXTENSIONS];
+ int wps_nfc_dev_pw_id;
+ struct wpabuf *wps_nfc_dh_pubkey;
+ struct wpabuf *wps_nfc_dh_privkey;
+ struct wpabuf *wps_nfc_dev_pw;
#endif /* CONFIG_WPS */
+ int pbc_in_m1;
+
+#define P2P_ENABLED BIT(0)
+#define P2P_GROUP_OWNER BIT(1)
+#define P2P_GROUP_FORMATION BIT(2)
+#define P2P_MANAGE BIT(3)
+#define P2P_ALLOW_CROSS_CONNECTION BIT(4)
+ int p2p;
+
+ int disassoc_low_ack;
+ int skip_inactivity_poll;
+
+#define TDLS_PROHIBIT BIT(0)
+#define TDLS_PROHIBIT_CHAN_SWITCH BIT(1)
+ int tdls;
+ int disable_11n;
+ int disable_11ac;
+
+ /* IEEE 802.11v */
+ int time_advertisement;
+ char *time_zone;
+ int wnm_sleep_mode;
+ int bss_transition;
+
+ /* IEEE 802.11u - Interworking */
+ int interworking;
+ int access_network_type;
+ int internet;
+ int asra;
+ int esr;
+ int uesa;
+ int venue_info_set;
+ u8 venue_group;
+ u8 venue_type;
+ u8 hessid[ETH_ALEN];
+
+ /* IEEE 802.11u - Roaming Consortium list */
+ unsigned int roaming_consortium_count;
+ struct hostapd_roaming_consortium *roaming_consortium;
+
+ /* IEEE 802.11u - Venue Name duples */
+ unsigned int venue_name_count;
+ struct hostapd_lang_string *venue_name;
+
+ /* IEEE 802.11u - Network Authentication Type */
+ u8 *network_auth_type;
+ size_t network_auth_type_len;
+
+ /* IEEE 802.11u - IP Address Type Availability */
+ u8 ipaddr_type_availability;
+ u8 ipaddr_type_configured;
+
+ /* IEEE 802.11u - 3GPP Cellular Network */
+ u8 *anqp_3gpp_cell_net;
+ size_t anqp_3gpp_cell_net_len;
+
+ /* IEEE 802.11u - Domain Name */
+ u8 *domain_name;
+ size_t domain_name_len;
+
+ unsigned int nai_realm_count;
+ struct hostapd_nai_realm_data *nai_realm_data;
+
+ u16 gas_comeback_delay;
+ int gas_frag_limit;
+
+#ifdef CONFIG_HS20
+ int hs20;
+ int disable_dgaf;
+ unsigned int hs20_oper_friendly_name_count;
+ struct hostapd_lang_string *hs20_oper_friendly_name;
+ u8 *hs20_wan_metrics;
+ u8 *hs20_connection_capability;
+ size_t hs20_connection_capability_len;
+ u8 *hs20_operating_class;
+ u8 hs20_operating_class_len;
+#endif /* CONFIG_HS20 */
+
+ u8 wps_rf_bands; /* RF bands for WPS (WPS_RF_*) */
+
+#ifdef CONFIG_RADIUS_TEST
+ char *dump_msk_file;
+#endif /* CONFIG_RADIUS_TEST */
+
+ struct wpabuf *vendor_elements;
};
@@ -332,12 +475,6 @@ struct hostapd_config {
LONG_PREAMBLE = 0,
SHORT_PREAMBLE = 1
} preamble;
- enum {
- CTS_PROTECTION_AUTOMATIC = 0,
- CTS_PROTECTION_FORCE_ENABLED = 1,
- CTS_PROTECTION_FORCE_DISABLED = 2,
- CTS_PROTECTION_AUTOMATIC_NO_OLBC = 3,
- } cts_protection_type;
int *supported_rates;
int *basic_rates;
@@ -371,6 +508,13 @@ struct hostapd_config {
u16 ht_capab;
int ieee80211n;
int secondary_channel;
+ int require_ht;
+ u32 vht_capab;
+ int ieee80211ac;
+ int require_vht;
+ u8 vht_oper_chwidth;
+ u8 vht_oper_centr_freq_seg0_idx;
+ u8 vht_oper_centr_freq_seg1_idx;
};
@@ -389,8 +533,7 @@ const u8 * hostapd_get_psk(const struct hostapd_bss_config *conf,
int hostapd_setup_wpa_psk(struct hostapd_bss_config *conf);
const char * hostapd_get_vlan_id_ifname(struct hostapd_vlan *vlan,
int vlan_id);
-const struct hostapd_eap_user *
-hostapd_get_eap_user(const struct hostapd_bss_config *conf, const u8 *identity,
- size_t identity_len, int phase2);
+struct hostapd_radius_attr *
+hostapd_config_get_radius_attr(struct hostapd_radius_attr *attr, u8 type);
#endif /* HOSTAPD_CONFIG_H */
diff --git a/contrib/wpa/src/ap/ap_drv_ops.c b/contrib/wpa/src/ap/ap_drv_ops.c
index f264a3e..02da25b 100644
--- a/contrib/wpa/src/ap/ap_drv_ops.c
+++ b/contrib/wpa/src/ap/ap_drv_ops.c
@@ -2,14 +2,8 @@
* hostapd - Driver operations
* Copyright (c) 2009-2010, Jouni Malinen <j@w1.fi>
*
- * This program is free software; you can redistribute it and/or modify
- * it under the terms of the GNU General Public License version 2 as
- * published by the Free Software Foundation.
- *
- * Alternatively, this software may be distributed under the terms of BSD
- * license.
- *
- * See README and COPYING for more details.
+ * This software may be distributed under the terms of the BSD license.
+ * See README for more details.
*/
#include "utils/includes.h"
@@ -17,14 +11,18 @@
#include "utils/common.h"
#include "drivers/driver.h"
#include "common/ieee802_11_defs.h"
+#include "wps/wps.h"
+#include "p2p/p2p.h"
#include "hostapd.h"
#include "ieee802_11.h"
#include "sta_info.h"
#include "ap_config.h"
+#include "p2p_hostapd.h"
+#include "hs20.h"
#include "ap_drv_ops.h"
-static int hostapd_sta_flags_to_drv(int flags)
+u32 hostapd_sta_flags_to_drv(u32 flags)
{
int res = 0;
if (flags & WLAN_STA_AUTHORIZED)
@@ -39,45 +37,190 @@ static int hostapd_sta_flags_to_drv(int flags)
}
-static int hostapd_set_ap_wps_ie(struct hostapd_data *hapd)
+int hostapd_build_ap_extra_ies(struct hostapd_data *hapd,
+ struct wpabuf **beacon_ret,
+ struct wpabuf **proberesp_ret,
+ struct wpabuf **assocresp_ret)
{
- struct wpabuf *beacon, *proberesp;
- int ret;
+ struct wpabuf *beacon = NULL, *proberesp = NULL, *assocresp = NULL;
+ u8 buf[200], *pos;
- if (hapd->driver == NULL || hapd->driver->set_ap_wps_ie == NULL)
- return 0;
+ *beacon_ret = *proberesp_ret = *assocresp_ret = NULL;
- beacon = hapd->wps_beacon_ie;
- proberesp = hapd->wps_probe_resp_ie;
+ pos = buf;
+ pos = hostapd_eid_time_adv(hapd, pos);
+ if (pos != buf) {
+ if (wpabuf_resize(&beacon, pos - buf) != 0)
+ goto fail;
+ wpabuf_put_data(beacon, buf, pos - buf);
+ }
+ pos = hostapd_eid_time_zone(hapd, pos);
+ if (pos != buf) {
+ if (wpabuf_resize(&proberesp, pos - buf) != 0)
+ goto fail;
+ wpabuf_put_data(proberesp, buf, pos - buf);
+ }
- ret = hapd->driver->set_ap_wps_ie(hapd->drv_priv, beacon, proberesp);
+ pos = buf;
+ pos = hostapd_eid_ext_capab(hapd, pos);
+ if (pos != buf) {
+ if (wpabuf_resize(&assocresp, pos - buf) != 0)
+ goto fail;
+ wpabuf_put_data(assocresp, buf, pos - buf);
+ }
+ pos = hostapd_eid_interworking(hapd, pos);
+ pos = hostapd_eid_adv_proto(hapd, pos);
+ pos = hostapd_eid_roaming_consortium(hapd, pos);
+ if (pos != buf) {
+ if (wpabuf_resize(&beacon, pos - buf) != 0)
+ goto fail;
+ wpabuf_put_data(beacon, buf, pos - buf);
+
+ if (wpabuf_resize(&proberesp, pos - buf) != 0)
+ goto fail;
+ wpabuf_put_data(proberesp, buf, pos - buf);
+ }
- return ret;
+ if (hapd->wps_beacon_ie) {
+ if (wpabuf_resize(&beacon, wpabuf_len(hapd->wps_beacon_ie)) <
+ 0)
+ goto fail;
+ wpabuf_put_buf(beacon, hapd->wps_beacon_ie);
+ }
+
+ if (hapd->wps_probe_resp_ie) {
+ if (wpabuf_resize(&proberesp,
+ wpabuf_len(hapd->wps_probe_resp_ie)) < 0)
+ goto fail;
+ wpabuf_put_buf(proberesp, hapd->wps_probe_resp_ie);
+ }
+
+#ifdef CONFIG_P2P
+ if (hapd->p2p_beacon_ie) {
+ if (wpabuf_resize(&beacon, wpabuf_len(hapd->p2p_beacon_ie)) <
+ 0)
+ goto fail;
+ wpabuf_put_buf(beacon, hapd->p2p_beacon_ie);
+ }
+
+ if (hapd->p2p_probe_resp_ie) {
+ if (wpabuf_resize(&proberesp,
+ wpabuf_len(hapd->p2p_probe_resp_ie)) < 0)
+ goto fail;
+ wpabuf_put_buf(proberesp, hapd->p2p_probe_resp_ie);
+ }
+#endif /* CONFIG_P2P */
+
+#ifdef CONFIG_P2P_MANAGER
+ if (hapd->conf->p2p & P2P_MANAGE) {
+ if (wpabuf_resize(&beacon, 100) == 0) {
+ u8 *start, *p;
+ start = wpabuf_put(beacon, 0);
+ p = hostapd_eid_p2p_manage(hapd, start);
+ wpabuf_put(beacon, p - start);
+ }
+
+ if (wpabuf_resize(&proberesp, 100) == 0) {
+ u8 *start, *p;
+ start = wpabuf_put(proberesp, 0);
+ p = hostapd_eid_p2p_manage(hapd, start);
+ wpabuf_put(proberesp, p - start);
+ }
+ }
+#endif /* CONFIG_P2P_MANAGER */
+
+#ifdef CONFIG_WPS2
+ if (hapd->conf->wps_state) {
+ struct wpabuf *a = wps_build_assoc_resp_ie();
+ if (a && wpabuf_resize(&assocresp, wpabuf_len(a)) == 0)
+ wpabuf_put_buf(assocresp, a);
+ wpabuf_free(a);
+ }
+#endif /* CONFIG_WPS2 */
+
+#ifdef CONFIG_P2P_MANAGER
+ if (hapd->conf->p2p & P2P_MANAGE) {
+ if (wpabuf_resize(&assocresp, 100) == 0) {
+ u8 *start, *p;
+ start = wpabuf_put(assocresp, 0);
+ p = hostapd_eid_p2p_manage(hapd, start);
+ wpabuf_put(assocresp, p - start);
+ }
+ }
+#endif /* CONFIG_P2P_MANAGER */
+
+#ifdef CONFIG_WIFI_DISPLAY
+ if (hapd->p2p_group) {
+ struct wpabuf *a;
+ a = p2p_group_assoc_resp_ie(hapd->p2p_group, P2P_SC_SUCCESS);
+ if (a && wpabuf_resize(&assocresp, wpabuf_len(a)) == 0)
+ wpabuf_put_buf(assocresp, a);
+ wpabuf_free(a);
+ }
+#endif /* CONFIG_WIFI_DISPLAY */
+
+#ifdef CONFIG_HS20
+ pos = buf;
+ pos = hostapd_eid_hs20_indication(hapd, pos);
+ if (pos != buf) {
+ if (wpabuf_resize(&beacon, pos - buf) != 0)
+ goto fail;
+ wpabuf_put_data(beacon, buf, pos - buf);
+
+ if (wpabuf_resize(&proberesp, pos - buf) != 0)
+ goto fail;
+ wpabuf_put_data(proberesp, buf, pos - buf);
+ }
+#endif /* CONFIG_HS20 */
+
+ *beacon_ret = beacon;
+ *proberesp_ret = proberesp;
+ *assocresp_ret = assocresp;
+
+ return 0;
+
+fail:
+ wpabuf_free(beacon);
+ wpabuf_free(proberesp);
+ wpabuf_free(assocresp);
+ return -1;
}
-static int hostapd_send_mgmt_frame(struct hostapd_data *hapd, const void *msg,
- size_t len)
+void hostapd_free_ap_extra_ies(struct hostapd_data *hapd,
+ struct wpabuf *beacon,
+ struct wpabuf *proberesp,
+ struct wpabuf *assocresp)
{
- if (hapd->driver == NULL || hapd->driver->send_mlme == NULL)
- return 0;
- return hapd->driver->send_mlme(hapd->drv_priv, msg, len);
+ wpabuf_free(beacon);
+ wpabuf_free(proberesp);
+ wpabuf_free(assocresp);
}
-static int hostapd_send_eapol(struct hostapd_data *hapd, const u8 *addr,
- const u8 *data, size_t data_len, int encrypt)
+int hostapd_set_ap_wps_ie(struct hostapd_data *hapd)
{
- if (hapd->driver == NULL || hapd->driver->hapd_send_eapol == NULL)
+ struct wpabuf *beacon, *proberesp, *assocresp;
+ int ret;
+
+ if (hapd->driver == NULL || hapd->driver->set_ap_wps_ie == NULL)
return 0;
- return hapd->driver->hapd_send_eapol(hapd->drv_priv, addr, data,
- data_len, encrypt,
- hapd->own_addr);
+
+ if (hostapd_build_ap_extra_ies(hapd, &beacon, &proberesp, &assocresp) <
+ 0)
+ return -1;
+
+ ret = hapd->driver->set_ap_wps_ie(hapd->drv_priv, beacon, proberesp,
+ assocresp);
+
+ hostapd_free_ap_extra_ies(hapd, beacon, proberesp, assocresp);
+
+ return ret;
}
-static int hostapd_set_authorized(struct hostapd_data *hapd,
- struct sta_info *sta, int authorized)
+int hostapd_set_authorized(struct hostapd_data *hapd,
+ struct sta_info *sta, int authorized)
{
if (authorized) {
return hostapd_sta_set_flags(hapd, sta->addr,
@@ -92,39 +235,7 @@ static int hostapd_set_authorized(struct hostapd_data *hapd,
}
-static int hostapd_set_key(const char *ifname, struct hostapd_data *hapd,
- enum wpa_alg alg, const u8 *addr, int key_idx,
- int set_tx, const u8 *seq, size_t seq_len,
- const u8 *key, size_t key_len)
-{
- if (hapd->driver == NULL || hapd->driver->set_key == NULL)
- return 0;
- return hapd->driver->set_key(ifname, hapd->drv_priv, alg, addr,
- key_idx, set_tx, seq, seq_len, key,
- key_len);
-}
-
-
-static int hostapd_read_sta_data(struct hostapd_data *hapd,
- struct hostap_sta_driver_data *data,
- const u8 *addr)
-{
- if (hapd->driver == NULL || hapd->driver->read_sta_data == NULL)
- return -1;
- return hapd->driver->read_sta_data(hapd->drv_priv, data, addr);
-}
-
-
-static int hostapd_sta_clear_stats(struct hostapd_data *hapd, const u8 *addr)
-{
- if (hapd->driver == NULL || hapd->driver->sta_clear_stats == NULL)
- return 0;
- return hapd->driver->sta_clear_stats(hapd->drv_priv, addr);
-}
-
-
-static int hostapd_set_sta_flags(struct hostapd_data *hapd,
- struct sta_info *sta)
+int hostapd_set_sta_flags(struct hostapd_data *hapd, struct sta_info *sta)
{
int set_flags, total_flags, flags_and, flags_or;
total_flags = hostapd_sta_flags_to_drv(sta->flags);
@@ -140,8 +251,8 @@ static int hostapd_set_sta_flags(struct hostapd_data *hapd,
}
-static int hostapd_set_drv_ieee8021x(struct hostapd_data *hapd,
- const char *ifname, int enabled)
+int hostapd_set_drv_ieee8021x(struct hostapd_data *hapd, const char *ifname,
+ int enabled)
{
struct wpa_bss_params params;
os_memset(&params, 0, sizeof(params));
@@ -154,166 +265,80 @@ static int hostapd_set_drv_ieee8021x(struct hostapd_data *hapd,
params.wpa_pairwise = hapd->conf->wpa_pairwise;
params.wpa_key_mgmt = hapd->conf->wpa_key_mgmt;
params.rsn_preauth = hapd->conf->rsn_preauth;
+#ifdef CONFIG_IEEE80211W
+ params.ieee80211w = hapd->conf->ieee80211w;
+#endif /* CONFIG_IEEE80211W */
}
return hostapd_set_ieee8021x(hapd, &params);
}
-static int hostapd_set_radius_acl_auth(struct hostapd_data *hapd,
- const u8 *mac, int accepted,
- u32 session_timeout)
-{
- if (hapd->driver == NULL || hapd->driver->set_radius_acl_auth == NULL)
- return 0;
- return hapd->driver->set_radius_acl_auth(hapd->drv_priv, mac, accepted,
- session_timeout);
-}
-
-
-static int hostapd_set_radius_acl_expire(struct hostapd_data *hapd,
- const u8 *mac)
-{
- if (hapd->driver == NULL ||
- hapd->driver->set_radius_acl_expire == NULL)
- return 0;
- return hapd->driver->set_radius_acl_expire(hapd->drv_priv, mac);
-}
-
-
-static int hostapd_set_bss_params(struct hostapd_data *hapd,
- int use_protection)
-{
- int ret = 0;
- int preamble;
-#ifdef CONFIG_IEEE80211N
- u8 buf[60], *ht_capab, *ht_oper, *pos;
-
- pos = buf;
- ht_capab = pos;
- pos = hostapd_eid_ht_capabilities(hapd, pos);
- ht_oper = pos;
- pos = hostapd_eid_ht_operation(hapd, pos);
- if (pos > ht_oper && ht_oper > ht_capab &&
- hostapd_set_ht_params(hapd, ht_capab + 2, ht_capab[1],
- ht_oper + 2, ht_oper[1])) {
- wpa_printf(MSG_ERROR, "Could not set HT capabilities "
- "for kernel driver");
- ret = -1;
- }
-
-#endif /* CONFIG_IEEE80211N */
-
- if (hostapd_set_cts_protect(hapd, use_protection)) {
- wpa_printf(MSG_ERROR, "Failed to set CTS protect in kernel "
- "driver");
- ret = -1;
- }
-
- if (hapd->iface->current_mode &&
- hapd->iface->current_mode->mode == HOSTAPD_MODE_IEEE80211G &&
- hostapd_set_short_slot_time(hapd,
- hapd->iface->num_sta_no_short_slot_time
- > 0 ? 0 : 1)) {
- wpa_printf(MSG_ERROR, "Failed to set Short Slot Time option "
- "in kernel driver");
- ret = -1;
- }
-
- if (hapd->iface->num_sta_no_short_preamble == 0 &&
- hapd->iconf->preamble == SHORT_PREAMBLE)
- preamble = SHORT_PREAMBLE;
- else
- preamble = LONG_PREAMBLE;
- if (hostapd_set_preamble(hapd, preamble)) {
- wpa_printf(MSG_ERROR, "Could not set preamble for kernel "
- "driver");
- ret = -1;
- }
-
- return ret;
-}
-
-
-static int hostapd_set_beacon(struct hostapd_data *hapd,
- const u8 *head, size_t head_len,
- const u8 *tail, size_t tail_len, int dtim_period,
- int beacon_int)
-{
- if (hapd->driver == NULL || hapd->driver->set_beacon == NULL)
- return 0;
- return hapd->driver->set_beacon(hapd->drv_priv,
- head, head_len, tail, tail_len,
- dtim_period, beacon_int);
-}
-
-
-static int hostapd_vlan_if_add(struct hostapd_data *hapd, const char *ifname)
+int hostapd_vlan_if_add(struct hostapd_data *hapd, const char *ifname)
{
char force_ifname[IFNAMSIZ];
u8 if_addr[ETH_ALEN];
- return hostapd_if_add(hapd, WPA_IF_AP_VLAN, ifname, NULL, NULL, NULL,
- force_ifname, if_addr);
+ return hostapd_if_add(hapd, WPA_IF_AP_VLAN, ifname, hapd->own_addr,
+ NULL, NULL, force_ifname, if_addr, NULL);
}
-static int hostapd_vlan_if_remove(struct hostapd_data *hapd,
- const char *ifname)
+
+int hostapd_vlan_if_remove(struct hostapd_data *hapd, const char *ifname)
{
return hostapd_if_remove(hapd, WPA_IF_AP_VLAN, ifname);
}
-static int hostapd_set_wds_sta(struct hostapd_data *hapd, const u8 *addr,
- int aid, int val)
+int hostapd_set_wds_sta(struct hostapd_data *hapd, const u8 *addr, int aid,
+ int val)
{
- if (hapd->driver == NULL || hapd->driver->set_wds_sta == NULL)
- return 0;
- return hapd->driver->set_wds_sta(hapd->drv_priv, addr, aid, val);
-}
+ const char *bridge = NULL;
-
-static int hostapd_set_sta_vlan(const char *ifname, struct hostapd_data *hapd,
- const u8 *addr, int vlan_id)
-{
- if (hapd->driver == NULL || hapd->driver->set_sta_vlan == NULL)
+ if (hapd->driver == NULL || hapd->driver->set_wds_sta == NULL)
return 0;
- return hapd->driver->set_sta_vlan(hapd->drv_priv, addr, ifname,
- vlan_id);
+ if (hapd->conf->wds_bridge[0])
+ bridge = hapd->conf->wds_bridge;
+ else if (hapd->conf->bridge[0])
+ bridge = hapd->conf->bridge;
+ return hapd->driver->set_wds_sta(hapd->drv_priv, addr, aid, val,
+ bridge);
}
-static int hostapd_get_inact_sec(struct hostapd_data *hapd, const u8 *addr)
+int hostapd_add_sta_node(struct hostapd_data *hapd, const u8 *addr,
+ u16 auth_alg)
{
- if (hapd->driver == NULL || hapd->driver->get_inact_sec == NULL)
+ if (hapd->driver == NULL || hapd->driver->add_sta_node == NULL)
return 0;
- return hapd->driver->get_inact_sec(hapd->drv_priv, addr);
+ return hapd->driver->add_sta_node(hapd->drv_priv, addr, auth_alg);
}
-static int hostapd_sta_deauth(struct hostapd_data *hapd, const u8 *addr,
- int reason)
+int hostapd_sta_auth(struct hostapd_data *hapd, const u8 *addr,
+ u16 seq, u16 status, const u8 *ie, size_t len)
{
- if (hapd->driver == NULL || hapd->driver->sta_deauth == NULL)
+ if (hapd->driver == NULL || hapd->driver->sta_auth == NULL)
return 0;
- return hapd->driver->sta_deauth(hapd->drv_priv, hapd->own_addr, addr,
- reason);
+ return hapd->driver->sta_auth(hapd->drv_priv, hapd->own_addr, addr,
+ seq, status, ie, len);
}
-static int hostapd_sta_disassoc(struct hostapd_data *hapd, const u8 *addr,
- int reason)
+int hostapd_sta_assoc(struct hostapd_data *hapd, const u8 *addr,
+ int reassoc, u16 status, const u8 *ie, size_t len)
{
- if (hapd->driver == NULL || hapd->driver->sta_disassoc == NULL)
+ if (hapd->driver == NULL || hapd->driver->sta_assoc == NULL)
return 0;
- return hapd->driver->sta_disassoc(hapd->drv_priv, hapd->own_addr, addr,
- reason);
+ return hapd->driver->sta_assoc(hapd->drv_priv, hapd->own_addr, addr,
+ reassoc, status, ie, len);
}
-static int hostapd_sta_add(struct hostapd_data *hapd,
- const u8 *addr, u16 aid, u16 capability,
- const u8 *supp_rates, size_t supp_rates_len,
- u16 listen_interval,
- const struct ieee80211_ht_capabilities *ht_capab)
+int hostapd_sta_add(struct hostapd_data *hapd,
+ const u8 *addr, u16 aid, u16 capability,
+ const u8 *supp_rates, size_t supp_rates_len,
+ u16 listen_interval,
+ const struct ieee80211_ht_capabilities *ht_capab,
+ u32 flags, u8 qosinfo)
{
struct hostapd_sta_add_params params;
@@ -330,52 +355,19 @@ static int hostapd_sta_add(struct hostapd_data *hapd,
params.supp_rates_len = supp_rates_len;
params.listen_interval = listen_interval;
params.ht_capabilities = ht_capab;
+ params.flags = hostapd_sta_flags_to_drv(flags);
+ params.qosinfo = qosinfo;
return hapd->driver->sta_add(hapd->drv_priv, &params);
}
-static int hostapd_sta_remove(struct hostapd_data *hapd, const u8 *addr)
+int hostapd_add_tspec(struct hostapd_data *hapd, const u8 *addr,
+ u8 *tspec_ie, size_t tspec_ielen)
{
- if (hapd->driver == NULL || hapd->driver->sta_remove == NULL)
+ if (hapd->driver == NULL || hapd->driver->add_tspec == NULL)
return 0;
- return hapd->driver->sta_remove(hapd->drv_priv, addr);
-}
-
-
-static int hostapd_set_countermeasures(struct hostapd_data *hapd, int enabled)
-{
- if (hapd->driver == NULL ||
- hapd->driver->hapd_set_countermeasures == NULL)
- return 0;
- return hapd->driver->hapd_set_countermeasures(hapd->drv_priv, enabled);
-}
-
-
-void hostapd_set_driver_ops(struct hostapd_driver_ops *ops)
-{
- ops->set_ap_wps_ie = hostapd_set_ap_wps_ie;
- ops->send_mgmt_frame = hostapd_send_mgmt_frame;
- ops->send_eapol = hostapd_send_eapol;
- ops->set_authorized = hostapd_set_authorized;
- ops->set_key = hostapd_set_key;
- ops->read_sta_data = hostapd_read_sta_data;
- ops->sta_clear_stats = hostapd_sta_clear_stats;
- ops->set_sta_flags = hostapd_set_sta_flags;
- ops->set_drv_ieee8021x = hostapd_set_drv_ieee8021x;
- ops->set_radius_acl_auth = hostapd_set_radius_acl_auth;
- ops->set_radius_acl_expire = hostapd_set_radius_acl_expire;
- ops->set_bss_params = hostapd_set_bss_params;
- ops->set_beacon = hostapd_set_beacon;
- ops->vlan_if_add = hostapd_vlan_if_add;
- ops->vlan_if_remove = hostapd_vlan_if_remove;
- ops->set_wds_sta = hostapd_set_wds_sta;
- ops->set_sta_vlan = hostapd_set_sta_vlan;
- ops->get_inact_sec = hostapd_get_inact_sec;
- ops->sta_deauth = hostapd_sta_deauth;
- ops->sta_disassoc = hostapd_sta_disassoc;
- ops->sta_add = hostapd_sta_add;
- ops->sta_remove = hostapd_sta_remove;
- ops->set_countermeasures = hostapd_set_countermeasures;
+ return hapd->driver->add_tspec(hapd->drv_priv, addr, tspec_ie,
+ tspec_ielen);
}
@@ -414,12 +406,14 @@ int hostapd_set_ssid(struct hostapd_data *hapd, const u8 *buf, size_t len)
int hostapd_if_add(struct hostapd_data *hapd, enum wpa_driver_if_type type,
const char *ifname, const u8 *addr, void *bss_ctx,
- void **drv_priv, char *force_ifname, u8 *if_addr)
+ void **drv_priv, char *force_ifname, u8 *if_addr,
+ const char *bridge)
{
if (hapd->driver == NULL || hapd->driver->if_add == NULL)
return -1;
return hapd->driver->if_add(hapd->drv_priv, type, ifname, addr,
- bss_ctx, drv_priv, force_ifname, if_addr);
+ bss_ctx, drv_priv, force_ifname, if_addr,
+ bridge);
}
@@ -502,16 +496,6 @@ int hostapd_sta_set_flags(struct hostapd_data *hapd, u8 *addr,
}
-int hostapd_set_rate_sets(struct hostapd_data *hapd, int *supp_rates,
- int *basic_rates, int mode)
-{
- if (hapd->driver == NULL || hapd->driver->set_rate_sets == NULL)
- return 0;
- return hapd->driver->set_rate_sets(hapd->drv_priv, supp_rates,
- basic_rates, mode);
-}
-
-
int hostapd_set_country(struct hostapd_data *hapd, const char *country)
{
if (hapd->driver == NULL ||
@@ -521,30 +505,6 @@ int hostapd_set_country(struct hostapd_data *hapd, const char *country)
}
-int hostapd_set_cts_protect(struct hostapd_data *hapd, int value)
-{
- if (hapd->driver == NULL || hapd->driver->set_cts_protect == NULL)
- return 0;
- return hapd->driver->set_cts_protect(hapd->drv_priv, value);
-}
-
-
-int hostapd_set_preamble(struct hostapd_data *hapd, int value)
-{
- if (hapd->driver == NULL || hapd->driver->set_preamble == NULL)
- return 0;
- return hapd->driver->set_preamble(hapd->drv_priv, value);
-}
-
-
-int hostapd_set_short_slot_time(struct hostapd_data *hapd, int value)
-{
- if (hapd->driver == NULL || hapd->driver->set_short_slot_time == NULL)
- return 0;
- return hapd->driver->set_short_slot_time(hapd->drv_priv, value);
-}
-
-
int hostapd_set_tx_queue_params(struct hostapd_data *hapd, int queue, int aifs,
int cw_min, int cw_max, int burst_time)
{
@@ -555,15 +515,6 @@ int hostapd_set_tx_queue_params(struct hostapd_data *hapd, int queue, int aifs,
}
-int hostapd_valid_bss_mask(struct hostapd_data *hapd, const u8 *addr,
- const u8 *mask)
-{
- if (hapd->driver == NULL || hapd->driver->valid_bss_mask == NULL)
- return 1;
- return hapd->driver->valid_bss_mask(hapd->drv_priv, addr, mask);
-}
-
-
struct hostapd_hw_modes *
hostapd_get_hw_feature_data(struct hostapd_data *hapd, u16 *num_modes,
u16 *flags)
@@ -584,19 +535,6 @@ int hostapd_driver_commit(struct hostapd_data *hapd)
}
-int hostapd_set_ht_params(struct hostapd_data *hapd,
- const u8 *ht_capab, size_t ht_capab_len,
- const u8 *ht_oper, size_t ht_oper_len)
-{
- if (hapd->driver == NULL || hapd->driver->set_ht_params == NULL ||
- ht_capab == NULL || ht_oper == NULL)
- return 0;
- return hapd->driver->set_ht_params(hapd->drv_priv,
- ht_capab, ht_capab_len,
- ht_oper, ht_oper_len);
-}
-
-
int hostapd_drv_none(struct hostapd_data *hapd)
{
return hapd->driver && os_strcmp(hapd->driver->name, "none") == 0;
@@ -619,3 +557,78 @@ struct wpa_scan_results * hostapd_driver_get_scan_results(
return hapd->driver->get_scan_results2(hapd->drv_priv);
return NULL;
}
+
+
+int hostapd_driver_set_noa(struct hostapd_data *hapd, u8 count, int start,
+ int duration)
+{
+ if (hapd->driver && hapd->driver->set_noa)
+ return hapd->driver->set_noa(hapd->drv_priv, count, start,
+ duration);
+ return -1;
+}
+
+
+int hostapd_drv_set_key(const char *ifname, struct hostapd_data *hapd,
+ enum wpa_alg alg, const u8 *addr,
+ int key_idx, int set_tx,
+ const u8 *seq, size_t seq_len,
+ const u8 *key, size_t key_len)
+{
+ if (hapd->driver == NULL || hapd->driver->set_key == NULL)
+ return 0;
+ return hapd->driver->set_key(ifname, hapd->drv_priv, alg, addr,
+ key_idx, set_tx, seq, seq_len, key,
+ key_len);
+}
+
+
+int hostapd_drv_send_mlme(struct hostapd_data *hapd,
+ const void *msg, size_t len, int noack)
+{
+ if (hapd->driver == NULL || hapd->driver->send_mlme == NULL)
+ return 0;
+ return hapd->driver->send_mlme(hapd->drv_priv, msg, len, noack);
+}
+
+
+int hostapd_drv_sta_deauth(struct hostapd_data *hapd,
+ const u8 *addr, int reason)
+{
+ if (hapd->driver == NULL || hapd->driver->sta_deauth == NULL)
+ return 0;
+ return hapd->driver->sta_deauth(hapd->drv_priv, hapd->own_addr, addr,
+ reason);
+}
+
+
+int hostapd_drv_sta_disassoc(struct hostapd_data *hapd,
+ const u8 *addr, int reason)
+{
+ if (hapd->driver == NULL || hapd->driver->sta_disassoc == NULL)
+ return 0;
+ return hapd->driver->sta_disassoc(hapd->drv_priv, hapd->own_addr, addr,
+ reason);
+}
+
+
+int hostapd_drv_wnm_oper(struct hostapd_data *hapd, enum wnm_oper oper,
+ const u8 *peer, u8 *buf, u16 *buf_len)
+{
+ if (hapd->driver == NULL || hapd->driver->wnm_oper == NULL)
+ return 0;
+ return hapd->driver->wnm_oper(hapd->drv_priv, oper, peer, buf,
+ buf_len);
+}
+
+
+int hostapd_drv_send_action(struct hostapd_data *hapd, unsigned int freq,
+ unsigned int wait, const u8 *dst, const u8 *data,
+ size_t len)
+{
+ if (hapd->driver == NULL || hapd->driver->send_action == NULL)
+ return 0;
+ return hapd->driver->send_action(hapd->drv_priv, freq, wait, dst,
+ hapd->own_addr, hapd->own_addr, data,
+ len, 0);
+}
diff --git a/contrib/wpa/src/ap/ap_drv_ops.h b/contrib/wpa/src/ap/ap_drv_ops.h
index 9b75d09..9c53b99 100644
--- a/contrib/wpa/src/ap/ap_drv_ops.h
+++ b/contrib/wpa/src/ap/ap_drv_ops.h
@@ -2,14 +2,8 @@
* hostapd - Driver operations
* Copyright (c) 2009, Jouni Malinen <j@w1.fi>
*
- * This program is free software; you can redistribute it and/or modify
- * it under the terms of the GNU General Public License version 2 as
- * published by the Free Software Foundation.
- *
- * Alternatively, this software may be distributed under the terms of BSD
- * license.
- *
- * See README and COPYING for more details.
+ * This software may be distributed under the terms of the BSD license.
+ * See README for more details.
*/
#ifndef AP_DRV_OPS
@@ -18,8 +12,32 @@
enum wpa_driver_if_type;
struct wpa_bss_params;
struct wpa_driver_scan_params;
+struct ieee80211_ht_capabilities;
-void hostapd_set_driver_ops(struct hostapd_driver_ops *ops);
+u32 hostapd_sta_flags_to_drv(u32 flags);
+int hostapd_build_ap_extra_ies(struct hostapd_data *hapd,
+ struct wpabuf **beacon,
+ struct wpabuf **proberesp,
+ struct wpabuf **assocresp);
+void hostapd_free_ap_extra_ies(struct hostapd_data *hapd, struct wpabuf *beacon,
+ struct wpabuf *proberesp,
+ struct wpabuf *assocresp);
+int hostapd_set_ap_wps_ie(struct hostapd_data *hapd);
+int hostapd_set_authorized(struct hostapd_data *hapd,
+ struct sta_info *sta, int authorized);
+int hostapd_set_sta_flags(struct hostapd_data *hapd, struct sta_info *sta);
+int hostapd_set_drv_ieee8021x(struct hostapd_data *hapd, const char *ifname,
+ int enabled);
+int hostapd_vlan_if_add(struct hostapd_data *hapd, const char *ifname);
+int hostapd_vlan_if_remove(struct hostapd_data *hapd, const char *ifname);
+int hostapd_set_wds_sta(struct hostapd_data *hapd, const u8 *addr, int aid,
+ int val);
+int hostapd_sta_add(struct hostapd_data *hapd,
+ const u8 *addr, u16 aid, u16 capability,
+ const u8 *supp_rates, size_t supp_rates_len,
+ u16 listen_interval,
+ const struct ieee80211_ht_capabilities *ht_capab,
+ u32 flags, u8 qosinfo);
int hostapd_set_privacy(struct hostapd_data *hapd, int enabled);
int hostapd_set_generic_elem(struct hostapd_data *hapd, const u8 *elem,
size_t elem_len);
@@ -27,7 +45,8 @@ int hostapd_get_ssid(struct hostapd_data *hapd, u8 *buf, size_t len);
int hostapd_set_ssid(struct hostapd_data *hapd, const u8 *buf, size_t len);
int hostapd_if_add(struct hostapd_data *hapd, enum wpa_driver_if_type type,
const char *ifname, const u8 *addr, void *bss_ctx,
- void **drv_priv, char *force_ifname, u8 *if_addr);
+ void **drv_priv, char *force_ifname, u8 *if_addr,
+ const char *bridge);
int hostapd_if_remove(struct hostapd_data *hapd, enum wpa_driver_if_type type,
const char *ifname);
int hostapd_set_ieee8021x(struct hostapd_data *hapd,
@@ -41,27 +60,157 @@ int hostapd_set_rts(struct hostapd_data *hapd, int rts);
int hostapd_set_frag(struct hostapd_data *hapd, int frag);
int hostapd_sta_set_flags(struct hostapd_data *hapd, u8 *addr,
int total_flags, int flags_or, int flags_and);
-int hostapd_set_rate_sets(struct hostapd_data *hapd, int *supp_rates,
- int *basic_rates, int mode);
int hostapd_set_country(struct hostapd_data *hapd, const char *country);
-int hostapd_set_cts_protect(struct hostapd_data *hapd, int value);
-int hostapd_set_preamble(struct hostapd_data *hapd, int value);
-int hostapd_set_short_slot_time(struct hostapd_data *hapd, int value);
int hostapd_set_tx_queue_params(struct hostapd_data *hapd, int queue, int aifs,
int cw_min, int cw_max, int burst_time);
-int hostapd_valid_bss_mask(struct hostapd_data *hapd, const u8 *addr,
- const u8 *mask);
struct hostapd_hw_modes *
hostapd_get_hw_feature_data(struct hostapd_data *hapd, u16 *num_modes,
u16 *flags);
int hostapd_driver_commit(struct hostapd_data *hapd);
-int hostapd_set_ht_params(struct hostapd_data *hapd,
- const u8 *ht_capab, size_t ht_capab_len,
- const u8 *ht_oper, size_t ht_oper_len);
int hostapd_drv_none(struct hostapd_data *hapd);
int hostapd_driver_scan(struct hostapd_data *hapd,
struct wpa_driver_scan_params *params);
struct wpa_scan_results * hostapd_driver_get_scan_results(
struct hostapd_data *hapd);
+int hostapd_driver_set_noa(struct hostapd_data *hapd, u8 count, int start,
+ int duration);
+int hostapd_drv_set_key(const char *ifname,
+ struct hostapd_data *hapd,
+ enum wpa_alg alg, const u8 *addr,
+ int key_idx, int set_tx,
+ const u8 *seq, size_t seq_len,
+ const u8 *key, size_t key_len);
+int hostapd_drv_send_mlme(struct hostapd_data *hapd,
+ const void *msg, size_t len, int noack);
+int hostapd_drv_sta_deauth(struct hostapd_data *hapd,
+ const u8 *addr, int reason);
+int hostapd_drv_sta_disassoc(struct hostapd_data *hapd,
+ const u8 *addr, int reason);
+int hostapd_drv_send_action(struct hostapd_data *hapd, unsigned int freq,
+ unsigned int wait, const u8 *dst, const u8 *data,
+ size_t len);
+int hostapd_add_sta_node(struct hostapd_data *hapd, const u8 *addr,
+ u16 auth_alg);
+int hostapd_sta_auth(struct hostapd_data *hapd, const u8 *addr,
+ u16 seq, u16 status, const u8 *ie, size_t len);
+int hostapd_sta_assoc(struct hostapd_data *hapd, const u8 *addr,
+ int reassoc, u16 status, const u8 *ie, size_t len);
+int hostapd_add_tspec(struct hostapd_data *hapd, const u8 *addr,
+ u8 *tspec_ie, size_t tspec_ielen);
+
+
+#include "drivers/driver.h"
+
+int hostapd_drv_wnm_oper(struct hostapd_data *hapd,
+ enum wnm_oper oper, const u8 *peer,
+ u8 *buf, u16 *buf_len);
+
+static inline int hostapd_drv_set_countermeasures(struct hostapd_data *hapd,
+ int enabled)
+{
+ if (hapd->driver == NULL ||
+ hapd->driver->hapd_set_countermeasures == NULL)
+ return 0;
+ return hapd->driver->hapd_set_countermeasures(hapd->drv_priv, enabled);
+}
+
+static inline int hostapd_drv_set_sta_vlan(const char *ifname,
+ struct hostapd_data *hapd,
+ const u8 *addr, int vlan_id)
+{
+ if (hapd->driver == NULL || hapd->driver->set_sta_vlan == NULL)
+ return 0;
+ return hapd->driver->set_sta_vlan(hapd->drv_priv, addr, ifname,
+ vlan_id);
+}
+
+static inline int hostapd_drv_get_inact_sec(struct hostapd_data *hapd,
+ const u8 *addr)
+{
+ if (hapd->driver == NULL || hapd->driver->get_inact_sec == NULL)
+ return 0;
+ return hapd->driver->get_inact_sec(hapd->drv_priv, addr);
+}
+
+static inline int hostapd_drv_sta_remove(struct hostapd_data *hapd,
+ const u8 *addr)
+{
+ if (hapd->driver == NULL || hapd->driver->sta_remove == NULL)
+ return 0;
+ return hapd->driver->sta_remove(hapd->drv_priv, addr);
+}
+
+static inline int hostapd_drv_hapd_send_eapol(struct hostapd_data *hapd,
+ const u8 *addr, const u8 *data,
+ size_t data_len, int encrypt,
+ u32 flags)
+{
+ if (hapd->driver == NULL || hapd->driver->hapd_send_eapol == NULL)
+ return 0;
+ return hapd->driver->hapd_send_eapol(hapd->drv_priv, addr, data,
+ data_len, encrypt,
+ hapd->own_addr, flags);
+}
+
+static inline int hostapd_drv_read_sta_data(
+ struct hostapd_data *hapd, struct hostap_sta_driver_data *data,
+ const u8 *addr)
+{
+ if (hapd->driver == NULL || hapd->driver->read_sta_data == NULL)
+ return -1;
+ return hapd->driver->read_sta_data(hapd->drv_priv, data, addr);
+}
+
+static inline int hostapd_drv_sta_clear_stats(struct hostapd_data *hapd,
+ const u8 *addr)
+{
+ if (hapd->driver == NULL || hapd->driver->sta_clear_stats == NULL)
+ return 0;
+ return hapd->driver->sta_clear_stats(hapd->drv_priv, addr);
+}
+
+static inline int hostapd_drv_set_ap(struct hostapd_data *hapd,
+ struct wpa_driver_ap_params *params)
+{
+ if (hapd->driver == NULL || hapd->driver->set_ap == NULL)
+ return 0;
+ return hapd->driver->set_ap(hapd->drv_priv, params);
+}
+
+static inline int hostapd_drv_set_radius_acl_auth(struct hostapd_data *hapd,
+ const u8 *mac, int accepted,
+ u32 session_timeout)
+{
+ if (hapd->driver == NULL || hapd->driver->set_radius_acl_auth == NULL)
+ return 0;
+ return hapd->driver->set_radius_acl_auth(hapd->drv_priv, mac, accepted,
+ session_timeout);
+}
+
+static inline int hostapd_drv_set_radius_acl_expire(struct hostapd_data *hapd,
+ const u8 *mac)
+{
+ if (hapd->driver == NULL ||
+ hapd->driver->set_radius_acl_expire == NULL)
+ return 0;
+ return hapd->driver->set_radius_acl_expire(hapd->drv_priv, mac);
+}
+
+static inline int hostapd_drv_set_authmode(struct hostapd_data *hapd,
+ int auth_algs)
+{
+ if (hapd->driver == NULL || hapd->driver->set_authmode == NULL)
+ return 0;
+ return hapd->driver->set_authmode(hapd->drv_priv, auth_algs);
+}
+
+static inline void hostapd_drv_poll_client(struct hostapd_data *hapd,
+ const u8 *own_addr, const u8 *addr,
+ int qos)
+{
+ if (hapd->driver == NULL || hapd->driver->poll_client == NULL)
+ return;
+ hapd->driver->poll_client(hapd->drv_priv, own_addr, addr, qos);
+}
#endif /* AP_DRV_OPS */
diff --git a/contrib/wpa/src/ap/ap_list.c b/contrib/wpa/src/ap/ap_list.c
index 5297dbf..18090ca 100644
--- a/contrib/wpa/src/ap/ap_list.c
+++ b/contrib/wpa/src/ap/ap_list.c
@@ -4,14 +4,8 @@
* Copyright (c) 2003-2004, Instant802 Networks, Inc.
* Copyright (c) 2006, Devicescape Software, Inc.
*
- * This program is free software; you can redistribute it and/or modify
- * it under the terms of the GNU General Public License version 2 as
- * published by the Free Software Foundation.
- *
- * Alternatively, this software may be distributed under the terms of BSD
- * license.
- *
- * See README and COPYING for more details.
+ * This software may be distributed under the terms of the BSD license.
+ * See README for more details.
*/
#include "utils/includes.h"
@@ -227,6 +221,7 @@ void ap_list_process_beacon(struct hostapd_iface *iface,
struct hostapd_frame_info *fi)
{
struct ap_info *ap;
+ struct os_time now;
int new_ap = 0;
size_t len;
int set_beacon = 0;
@@ -256,23 +251,9 @@ void ap_list_process_beacon(struct hostapd_iface *iface,
ap->ssid_len = len;
}
- os_memset(ap->supported_rates, 0, WLAN_SUPP_RATES_MAX);
- len = 0;
- if (elems->supp_rates) {
- len = elems->supp_rates_len;
- if (len > WLAN_SUPP_RATES_MAX)
- len = WLAN_SUPP_RATES_MAX;
- os_memcpy(ap->supported_rates, elems->supp_rates, len);
- }
- if (elems->ext_supp_rates) {
- int len2;
- if (len + elems->ext_supp_rates_len > WLAN_SUPP_RATES_MAX)
- len2 = WLAN_SUPP_RATES_MAX - len;
- else
- len2 = elems->ext_supp_rates_len;
- os_memcpy(ap->supported_rates + len, elems->ext_supp_rates,
- len2);
- }
+ merge_byte_arrays(ap->supported_rates, WLAN_SUPP_RATES_MAX,
+ elems->supp_rates, elems->supp_rates_len,
+ elems->ext_supp_rates, elems->ext_supp_rates_len);
ap->wpa = elems->wpa_ie != NULL;
@@ -292,11 +273,10 @@ void ap_list_process_beacon(struct hostapd_iface *iface,
ap->ht_support = 0;
ap->num_beacons++;
- time(&ap->last_beacon);
- if (fi) {
- ap->ssi_signal = fi->ssi_signal;
+ os_get_time(&now);
+ ap->last_beacon = now.sec;
+ if (fi)
ap->datarate = fi->datarate;
- }
if (!new_ap && ap != iface->ap_list) {
/* move AP entry into the beginning of the list so that the
@@ -324,14 +304,14 @@ void ap_list_process_beacon(struct hostapd_iface *iface,
#endif /* CONFIG_IEEE80211N */
if (set_beacon)
- ieee802_11_set_beacons(iface);
+ ieee802_11_update_beacons(iface);
}
static void ap_list_timer(void *eloop_ctx, void *timeout_ctx)
{
struct hostapd_iface *iface = eloop_ctx;
- time_t now;
+ struct os_time now;
struct ap_info *ap;
int set_beacon = 0;
@@ -340,12 +320,12 @@ static void ap_list_timer(void *eloop_ctx, void *timeout_ctx)
if (!iface->ap_list)
return;
- time(&now);
+ os_get_time(&now);
while (iface->ap_list) {
ap = iface->ap_list->prev;
if (ap->last_beacon + iface->conf->ap_table_expiration_time >=
- now)
+ now.sec)
break;
ap_free_ap(iface, ap);
@@ -379,7 +359,7 @@ static void ap_list_timer(void *eloop_ctx, void *timeout_ctx)
}
if (set_beacon)
- ieee802_11_set_beacons(iface);
+ ieee802_11_update_beacons(iface);
}
diff --git a/contrib/wpa/src/ap/ap_list.h b/contrib/wpa/src/ap/ap_list.h
index f49f58b..f0b4125 100644
--- a/contrib/wpa/src/ap/ap_list.h
+++ b/contrib/wpa/src/ap/ap_list.h
@@ -4,14 +4,8 @@
* Copyright (c) 2003-2004, Instant802 Networks, Inc.
* Copyright (c) 2006, Devicescape Software, Inc.
*
- * This program is free software; you can redistribute it and/or modify
- * it under the terms of the GNU General Public License version 2 as
- * published by the Free Software Foundation.
- *
- * Alternatively, this software may be distributed under the terms of BSD
- * license.
- *
- * See README and COPYING for more details.
+ * This software may be distributed under the terms of the BSD license.
+ * See README for more details.
*/
#ifndef AP_LIST_H
@@ -40,12 +34,11 @@ struct ap_info {
int channel;
int datarate; /* in 100 kbps */
- int ssi_signal;
int ht_support;
unsigned int num_beacons; /* number of beacon frames received */
- time_t last_beacon;
+ os_time_t last_beacon;
int already_seen; /* whether API call AP-NEW has already fetched
* information about this AP */
diff --git a/contrib/wpa/src/ap/ap_mlme.c b/contrib/wpa/src/ap/ap_mlme.c
index 2b09b11..a959694 100644
--- a/contrib/wpa/src/ap/ap_mlme.c
+++ b/contrib/wpa/src/ap/ap_mlme.c
@@ -4,14 +4,8 @@
* Copyright 2003-2004, Instant802 Networks, Inc.
* Copyright 2005-2006, Devicescape Software, Inc.
*
- * This program is free software; you can redistribute it and/or modify
- * it under the terms of the GNU General Public License version 2 as
- * published by the Free Software Foundation.
- *
- * Alternatively, this software may be distributed under the terms of BSD
- * license.
- *
- * See README and COPYING for more details.
+ * This software may be distributed under the terms of the BSD license.
+ * See README for more details.
*/
#include "utils/includes.h"
diff --git a/contrib/wpa/src/ap/ap_mlme.h b/contrib/wpa/src/ap/ap_mlme.h
index c77a939..e7fd69d 100644
--- a/contrib/wpa/src/ap/ap_mlme.h
+++ b/contrib/wpa/src/ap/ap_mlme.h
@@ -4,14 +4,8 @@
* Copyright 2003-2004, Instant802 Networks, Inc.
* Copyright 2005-2006, Devicescape Software, Inc.
*
- * This program is free software; you can redistribute it and/or modify
- * it under the terms of the GNU General Public License version 2 as
- * published by the Free Software Foundation.
- *
- * Alternatively, this software may be distributed under the terms of BSD
- * license.
- *
- * See README and COPYING for more details.
+ * This software may be distributed under the terms of the BSD license.
+ * See README for more details.
*/
#ifndef MLME_H
diff --git a/contrib/wpa/src/ap/authsrv.c b/contrib/wpa/src/ap/authsrv.c
index 0ab0668..d66d97e 100644
--- a/contrib/wpa/src/ap/authsrv.c
+++ b/contrib/wpa/src/ap/authsrv.c
@@ -2,14 +2,8 @@
* Authentication server setup
* Copyright (c) 2002-2009, Jouni Malinen <j@w1.fi>
*
- * This program is free software; you can redistribute it and/or modify
- * it under the terms of the GNU General Public License version 2 as
- * published by the Free Software Foundation.
- *
- * Alternatively, this software may be distributed under the terms of BSD
- * license.
- *
- * See README and COPYING for more details.
+ * This software may be distributed under the terms of the BSD license.
+ * See README for more details.
*/
#include "utils/includes.h"
@@ -60,7 +54,7 @@ static int hostapd_radius_get_eap_user(void *ctx, const u8 *identity,
struct eap_user *user)
{
const struct hostapd_eap_user *eap_user;
- int i, count;
+ int i;
eap_user = hostapd_get_eap_user(ctx, identity, identity_len, phase2);
if (eap_user == NULL)
@@ -70,10 +64,7 @@ static int hostapd_radius_get_eap_user(void *ctx, const u8 *identity,
return 0;
os_memset(user, 0, sizeof(*user));
- count = EAP_USER_MAX_METHODS;
- if (count > EAP_MAX_METHODS)
- count = EAP_MAX_METHODS;
- for (i = 0; i < count; i++) {
+ for (i = 0; i < EAP_MAX_METHODS; i++) {
user->methods[i].vendor = eap_user->methods[i].vendor;
user->methods[i].method = eap_user->methods[i].method;
}
@@ -101,7 +92,7 @@ static int hostapd_setup_radius_srv(struct hostapd_data *hapd)
os_memset(&srv, 0, sizeof(srv));
srv.client_file = conf->radius_server_clients;
srv.auth_port = conf->radius_server_auth_port;
- srv.conf_ctx = conf;
+ srv.conf_ctx = hapd;
srv.eap_sim_db_priv = hapd->eap_sim_db_priv;
srv.ssl_ctx = hapd->ssl_ctx;
srv.msg_ctx = hapd->msg_ctx;
@@ -119,6 +110,10 @@ static int hostapd_setup_radius_srv(struct hostapd_data *hapd)
srv.get_eap_user = hostapd_radius_get_eap_user;
srv.eap_req_id_text = conf->eap_req_id_text;
srv.eap_req_id_text_len = conf->eap_req_id_text_len;
+ srv.pwd_group = conf->pwd_group;
+#ifdef CONFIG_RADIUS_TEST
+ srv.dump_msk_file = conf->dump_msk_file;
+#endif /* CONFIG_RADIUS_TEST */
hapd->radius_srv = radius_server_init(&srv);
if (hapd->radius_srv == NULL) {
diff --git a/contrib/wpa/src/ap/authsrv.h b/contrib/wpa/src/ap/authsrv.h
index be3051e..2f4ed34 100644
--- a/contrib/wpa/src/ap/authsrv.h
+++ b/contrib/wpa/src/ap/authsrv.h
@@ -2,14 +2,8 @@
* Authentication server setup
* Copyright (c) 2002-2009, Jouni Malinen <j@w1.fi>
*
- * This program is free software; you can redistribute it and/or modify
- * it under the terms of the GNU General Public License version 2 as
- * published by the Free Software Foundation.
- *
- * Alternatively, this software may be distributed under the terms of BSD
- * license.
- *
- * See README and COPYING for more details.
+ * This software may be distributed under the terms of the BSD license.
+ * See README for more details.
*/
#ifndef AUTHSRV_H
diff --git a/contrib/wpa/src/ap/beacon.c b/contrib/wpa/src/ap/beacon.c
index 004cc8a..4c47c75 100644
--- a/contrib/wpa/src/ap/beacon.c
+++ b/contrib/wpa/src/ap/beacon.c
@@ -2,7 +2,7 @@
* hostapd / IEEE 802.11 Management: Beacon and Probe Request/Response
* Copyright (c) 2002-2004, Instant802 Networks, Inc.
* Copyright (c) 2005-2006, Devicescape Software, Inc.
- * Copyright (c) 2008-2009, Jouni Malinen <j@w1.fi>
+ * Copyright (c) 2008-2012, Jouni Malinen <j@w1.fi>
*
* This program is free software; you can redistribute it and/or modify
* it under the terms of the GNU General Public License version 2 as
@@ -22,15 +22,22 @@
#include "common/ieee802_11_defs.h"
#include "common/ieee802_11_common.h"
#include "drivers/driver.h"
+#include "wps/wps_defs.h"
+#include "p2p/p2p.h"
#include "hostapd.h"
#include "ieee802_11.h"
#include "wpa_auth.h"
#include "wmm.h"
#include "ap_config.h"
#include "sta_info.h"
+#include "p2p_hostapd.h"
+#include "ap_drv_ops.h"
#include "beacon.h"
+#include "hs20.h"
+#ifdef NEED_AP_MLME
+
static u8 ieee802_11_erp_info(struct hostapd_data *hapd)
{
u8 erp = 0;
@@ -39,23 +46,11 @@ static u8 ieee802_11_erp_info(struct hostapd_data *hapd)
hapd->iface->current_mode->mode != HOSTAPD_MODE_IEEE80211G)
return 0;
- switch (hapd->iconf->cts_protection_type) {
- case CTS_PROTECTION_FORCE_ENABLED:
- erp |= ERP_INFO_NON_ERP_PRESENT | ERP_INFO_USE_PROTECTION;
- break;
- case CTS_PROTECTION_FORCE_DISABLED:
- erp = 0;
- break;
- case CTS_PROTECTION_AUTOMATIC:
- if (hapd->iface->olbc)
- erp |= ERP_INFO_USE_PROTECTION;
- /* continue */
- case CTS_PROTECTION_AUTOMATIC_NO_OLBC:
- if (hapd->iface->num_sta_non_erp > 0) {
- erp |= ERP_INFO_NON_ERP_PRESENT |
- ERP_INFO_USE_PROTECTION;
- }
- break;
+ if (hapd->iface->olbc)
+ erp |= ERP_INFO_USE_PROTECTION;
+ if (hapd->iface->num_sta_non_erp > 0) {
+ erp |= ERP_INFO_NON_ERP_PRESENT |
+ ERP_INFO_USE_PROTECTION;
}
if (hapd->iface->num_sta_no_short_preamble > 0 ||
hapd->iconf->preamble == LONG_PREAMBLE)
@@ -178,8 +173,7 @@ static u8 * hostapd_eid_country(struct hostapd_data *hapd, u8 *eid,
}
-static u8 * hostapd_eid_wpa(struct hostapd_data *hapd, u8 *eid, size_t len,
- struct sta_info *sta)
+static u8 * hostapd_eid_wpa(struct hostapd_data *hapd, u8 *eid, size_t len)
{
const u8 *ie;
size_t ielen;
@@ -193,92 +187,37 @@ static u8 * hostapd_eid_wpa(struct hostapd_data *hapd, u8 *eid, size_t len,
}
-void handle_probe_req(struct hostapd_data *hapd,
- const struct ieee80211_mgmt *mgmt, size_t len)
+static u8 * hostapd_gen_probe_resp(struct hostapd_data *hapd,
+ struct sta_info *sta,
+ const struct ieee80211_mgmt *req,
+ int is_p2p, size_t *resp_len)
{
struct ieee80211_mgmt *resp;
- struct ieee802_11_elems elems;
- char *ssid;
u8 *pos, *epos;
- const u8 *ie;
- size_t ssid_len, ie_len;
- struct sta_info *sta = NULL;
size_t buflen;
- size_t i;
-
- ie = mgmt->u.probe_req.variable;
- ie_len = len - (IEEE80211_HDRLEN + sizeof(mgmt->u.probe_req));
-
- for (i = 0; hapd->probereq_cb && i < hapd->num_probereq_cb; i++)
- if (hapd->probereq_cb[i].cb(hapd->probereq_cb[i].ctx,
- mgmt->sa, ie, ie_len) > 0)
- return;
-
- if (!hapd->iconf->send_probe_response)
- return;
-
- if (ieee802_11_parse_elems(ie, ie_len, &elems, 0) == ParseFailed) {
- wpa_printf(MSG_DEBUG, "Could not parse ProbeReq from " MACSTR,
- MAC2STR(mgmt->sa));
- return;
- }
-
- ssid = NULL;
- ssid_len = 0;
-
- if ((!elems.ssid || !elems.supp_rates)) {
- wpa_printf(MSG_DEBUG, "STA " MACSTR " sent probe request "
- "without SSID or supported rates element",
- MAC2STR(mgmt->sa));
- return;
- }
- if (hapd->conf->ignore_broadcast_ssid && elems.ssid_len == 0) {
- wpa_printf(MSG_MSGDUMP, "Probe Request from " MACSTR " for "
- "broadcast SSID ignored", MAC2STR(mgmt->sa));
- return;
- }
-
- sta = ap_get_sta(hapd, mgmt->sa);
-
- if (elems.ssid_len == 0 ||
- (elems.ssid_len == hapd->conf->ssid.ssid_len &&
- os_memcmp(elems.ssid, hapd->conf->ssid.ssid, elems.ssid_len) ==
- 0)) {
- ssid = hapd->conf->ssid.ssid;
- ssid_len = hapd->conf->ssid.ssid_len;
- if (sta)
- sta->ssid_probe = &hapd->conf->ssid;
- }
-
- if (!ssid) {
- if (!(mgmt->da[0] & 0x01)) {
- char ssid_txt[33];
- ieee802_11_print_ssid(ssid_txt, elems.ssid,
- elems.ssid_len);
- wpa_printf(MSG_MSGDUMP, "Probe Request from " MACSTR
- " for foreign SSID '%s'",
- MAC2STR(mgmt->sa), ssid_txt);
- }
- return;
- }
-
- /* TODO: verify that supp_rates contains at least one matching rate
- * with AP configuration */
#define MAX_PROBERESP_LEN 768
buflen = MAX_PROBERESP_LEN;
#ifdef CONFIG_WPS
if (hapd->wps_probe_resp_ie)
buflen += wpabuf_len(hapd->wps_probe_resp_ie);
#endif /* CONFIG_WPS */
+#ifdef CONFIG_P2P
+ if (hapd->p2p_probe_resp_ie)
+ buflen += wpabuf_len(hapd->p2p_probe_resp_ie);
+#endif /* CONFIG_P2P */
+ if (hapd->conf->vendor_elements)
+ buflen += wpabuf_len(hapd->conf->vendor_elements);
resp = os_zalloc(buflen);
if (resp == NULL)
- return;
+ return NULL;
+
epos = ((u8 *) resp) + MAX_PROBERESP_LEN;
resp->frame_control = IEEE80211_FC(WLAN_FC_TYPE_MGMT,
WLAN_FC_STYPE_PROBE_RESP);
- os_memcpy(resp->da, mgmt->sa, ETH_ALEN);
+ if (req)
+ os_memcpy(resp->da, req->sa, ETH_ALEN);
os_memcpy(resp->sa, hapd->own_addr, ETH_ALEN);
os_memcpy(resp->bssid, hapd->own_addr, ETH_ALEN);
@@ -291,9 +230,9 @@ void handle_probe_req(struct hostapd_data *hapd,
pos = resp->u.probe_resp.variable;
*pos++ = WLAN_EID_SSID;
- *pos++ = ssid_len;
- os_memcpy(pos, ssid, ssid_len);
- pos += ssid_len;
+ *pos++ = hapd->conf->ssid.ssid_len;
+ os_memcpy(pos, hapd->conf->ssid.ssid, hapd->conf->ssid.ssid_len);
+ pos += hapd->conf->ssid.ssid_len;
/* Supported rates */
pos = hostapd_eid_supp_rates(hapd, pos);
@@ -310,13 +249,27 @@ void handle_probe_req(struct hostapd_data *hapd,
pos = hostapd_eid_ext_supp_rates(hapd, pos);
/* RSN, MDIE, WPA */
- pos = hostapd_eid_wpa(hapd, pos, epos - pos, sta);
+ pos = hostapd_eid_wpa(hapd, pos, epos - pos);
#ifdef CONFIG_IEEE80211N
pos = hostapd_eid_ht_capabilities(hapd, pos);
pos = hostapd_eid_ht_operation(hapd, pos);
#endif /* CONFIG_IEEE80211N */
+ pos = hostapd_eid_ext_capab(hapd, pos);
+
+ pos = hostapd_eid_time_adv(hapd, pos);
+ pos = hostapd_eid_time_zone(hapd, pos);
+
+ pos = hostapd_eid_interworking(hapd, pos);
+ pos = hostapd_eid_adv_proto(hapd, pos);
+ pos = hostapd_eid_roaming_consortium(hapd, pos);
+
+#ifdef CONFIG_IEEE80211AC
+ pos = hostapd_eid_vht_capabilities(hapd, pos);
+ pos = hostapd_eid_vht_operation(hapd, pos);
+#endif /* CONFIG_IEEE80211AC */
+
/* Wi-Fi Alliance WMM */
pos = hostapd_eid_wmm(hapd, pos);
@@ -328,23 +281,291 @@ void handle_probe_req(struct hostapd_data *hapd,
}
#endif /* CONFIG_WPS */
- if (hapd->drv.send_mgmt_frame(hapd, resp, pos - (u8 *) resp) < 0)
+#ifdef CONFIG_P2P
+ if ((hapd->conf->p2p & P2P_ENABLED) && is_p2p &&
+ hapd->p2p_probe_resp_ie) {
+ os_memcpy(pos, wpabuf_head(hapd->p2p_probe_resp_ie),
+ wpabuf_len(hapd->p2p_probe_resp_ie));
+ pos += wpabuf_len(hapd->p2p_probe_resp_ie);
+ }
+#endif /* CONFIG_P2P */
+#ifdef CONFIG_P2P_MANAGER
+ if ((hapd->conf->p2p & (P2P_MANAGE | P2P_ENABLED | P2P_GROUP_OWNER)) ==
+ P2P_MANAGE)
+ pos = hostapd_eid_p2p_manage(hapd, pos);
+#endif /* CONFIG_P2P_MANAGER */
+
+#ifdef CONFIG_HS20
+ pos = hostapd_eid_hs20_indication(hapd, pos);
+#endif /* CONFIG_HS20 */
+
+ if (hapd->conf->vendor_elements) {
+ os_memcpy(pos, wpabuf_head(hapd->conf->vendor_elements),
+ wpabuf_len(hapd->conf->vendor_elements));
+ pos += wpabuf_len(hapd->conf->vendor_elements);
+ }
+
+ *resp_len = pos - (u8 *) resp;
+ return (u8 *) resp;
+}
+
+
+enum ssid_match_result {
+ NO_SSID_MATCH,
+ EXACT_SSID_MATCH,
+ WILDCARD_SSID_MATCH
+};
+
+static enum ssid_match_result ssid_match(struct hostapd_data *hapd,
+ const u8 *ssid, size_t ssid_len,
+ const u8 *ssid_list,
+ size_t ssid_list_len)
+{
+ const u8 *pos, *end;
+ int wildcard = 0;
+
+ if (ssid_len == 0)
+ wildcard = 1;
+ if (ssid_len == hapd->conf->ssid.ssid_len &&
+ os_memcmp(ssid, hapd->conf->ssid.ssid, ssid_len) == 0)
+ return EXACT_SSID_MATCH;
+
+ if (ssid_list == NULL)
+ return wildcard ? WILDCARD_SSID_MATCH : NO_SSID_MATCH;
+
+ pos = ssid_list;
+ end = ssid_list + ssid_list_len;
+ while (pos + 1 <= end) {
+ if (pos + 2 + pos[1] > end)
+ break;
+ if (pos[1] == 0)
+ wildcard = 1;
+ if (pos[1] == hapd->conf->ssid.ssid_len &&
+ os_memcmp(pos + 2, hapd->conf->ssid.ssid, pos[1]) == 0)
+ return EXACT_SSID_MATCH;
+ pos += 2 + pos[1];
+ }
+
+ return wildcard ? WILDCARD_SSID_MATCH : NO_SSID_MATCH;
+}
+
+
+void handle_probe_req(struct hostapd_data *hapd,
+ const struct ieee80211_mgmt *mgmt, size_t len,
+ int ssi_signal)
+{
+ u8 *resp;
+ struct ieee802_11_elems elems;
+ const u8 *ie;
+ size_t ie_len;
+ struct sta_info *sta = NULL;
+ size_t i, resp_len;
+ int noack;
+ enum ssid_match_result res;
+
+ ie = mgmt->u.probe_req.variable;
+ if (len < IEEE80211_HDRLEN + sizeof(mgmt->u.probe_req))
+ return;
+ ie_len = len - (IEEE80211_HDRLEN + sizeof(mgmt->u.probe_req));
+
+ for (i = 0; hapd->probereq_cb && i < hapd->num_probereq_cb; i++)
+ if (hapd->probereq_cb[i].cb(hapd->probereq_cb[i].ctx,
+ mgmt->sa, mgmt->da, mgmt->bssid,
+ ie, ie_len, ssi_signal) > 0)
+ return;
+
+ if (!hapd->iconf->send_probe_response)
+ return;
+
+ if (ieee802_11_parse_elems(ie, ie_len, &elems, 0) == ParseFailed) {
+ wpa_printf(MSG_DEBUG, "Could not parse ProbeReq from " MACSTR,
+ MAC2STR(mgmt->sa));
+ return;
+ }
+
+ if ((!elems.ssid || !elems.supp_rates)) {
+ wpa_printf(MSG_DEBUG, "STA " MACSTR " sent probe request "
+ "without SSID or supported rates element",
+ MAC2STR(mgmt->sa));
+ return;
+ }
+
+#ifdef CONFIG_P2P
+ if (hapd->p2p && elems.wps_ie) {
+ struct wpabuf *wps;
+ wps = ieee802_11_vendor_ie_concat(ie, ie_len, WPS_DEV_OUI_WFA);
+ if (wps && !p2p_group_match_dev_type(hapd->p2p_group, wps)) {
+ wpa_printf(MSG_MSGDUMP, "P2P: Ignore Probe Request "
+ "due to mismatch with Requested Device "
+ "Type");
+ wpabuf_free(wps);
+ return;
+ }
+ wpabuf_free(wps);
+ }
+
+ if (hapd->p2p && elems.p2p) {
+ struct wpabuf *p2p;
+ p2p = ieee802_11_vendor_ie_concat(ie, ie_len, P2P_IE_VENDOR_TYPE);
+ if (p2p && !p2p_group_match_dev_id(hapd->p2p_group, p2p)) {
+ wpa_printf(MSG_MSGDUMP, "P2P: Ignore Probe Request "
+ "due to mismatch with Device ID");
+ wpabuf_free(p2p);
+ return;
+ }
+ wpabuf_free(p2p);
+ }
+#endif /* CONFIG_P2P */
+
+ if (hapd->conf->ignore_broadcast_ssid && elems.ssid_len == 0 &&
+ elems.ssid_list_len == 0) {
+ wpa_printf(MSG_MSGDUMP, "Probe Request from " MACSTR " for "
+ "broadcast SSID ignored", MAC2STR(mgmt->sa));
+ return;
+ }
+
+ sta = ap_get_sta(hapd, mgmt->sa);
+
+#ifdef CONFIG_P2P
+ if ((hapd->conf->p2p & P2P_GROUP_OWNER) &&
+ elems.ssid_len == P2P_WILDCARD_SSID_LEN &&
+ os_memcmp(elems.ssid, P2P_WILDCARD_SSID,
+ P2P_WILDCARD_SSID_LEN) == 0) {
+ /* Process P2P Wildcard SSID like Wildcard SSID */
+ elems.ssid_len = 0;
+ }
+#endif /* CONFIG_P2P */
+
+ res = ssid_match(hapd, elems.ssid, elems.ssid_len,
+ elems.ssid_list, elems.ssid_list_len);
+ if (res != NO_SSID_MATCH) {
+ if (sta)
+ sta->ssid_probe = &hapd->conf->ssid;
+ } else {
+ if (!(mgmt->da[0] & 0x01)) {
+ char ssid_txt[33];
+ ieee802_11_print_ssid(ssid_txt, elems.ssid,
+ elems.ssid_len);
+ wpa_printf(MSG_MSGDUMP, "Probe Request from " MACSTR
+ " for foreign SSID '%s' (DA " MACSTR ")%s",
+ MAC2STR(mgmt->sa), ssid_txt,
+ MAC2STR(mgmt->da),
+ elems.ssid_list ? " (SSID list)" : "");
+ }
+ return;
+ }
+
+#ifdef CONFIG_INTERWORKING
+ if (elems.interworking && elems.interworking_len >= 1) {
+ u8 ant = elems.interworking[0] & 0x0f;
+ if (ant != INTERWORKING_ANT_WILDCARD &&
+ ant != hapd->conf->access_network_type) {
+ wpa_printf(MSG_MSGDUMP, "Probe Request from " MACSTR
+ " for mismatching ANT %u ignored",
+ MAC2STR(mgmt->sa), ant);
+ return;
+ }
+ }
+
+ if (elems.interworking &&
+ (elems.interworking_len == 7 || elems.interworking_len == 9)) {
+ const u8 *hessid;
+ if (elems.interworking_len == 7)
+ hessid = elems.interworking + 1;
+ else
+ hessid = elems.interworking + 1 + 2;
+ if (!is_broadcast_ether_addr(hessid) &&
+ os_memcmp(hessid, hapd->conf->hessid, ETH_ALEN) != 0) {
+ wpa_printf(MSG_MSGDUMP, "Probe Request from " MACSTR
+ " for mismatching HESSID " MACSTR
+ " ignored",
+ MAC2STR(mgmt->sa), MAC2STR(hessid));
+ return;
+ }
+ }
+#endif /* CONFIG_INTERWORKING */
+
+ /* TODO: verify that supp_rates contains at least one matching rate
+ * with AP configuration */
+
+ resp = hostapd_gen_probe_resp(hapd, sta, mgmt, elems.p2p != NULL,
+ &resp_len);
+ if (resp == NULL)
+ return;
+
+ /*
+ * If this is a broadcast probe request, apply no ack policy to avoid
+ * excessive retries.
+ */
+ noack = !!(res == WILDCARD_SSID_MATCH &&
+ is_broadcast_ether_addr(mgmt->da));
+
+ if (hostapd_drv_send_mlme(hapd, resp, resp_len, noack) < 0)
perror("handle_probe_req: send");
os_free(resp);
- wpa_printf(MSG_MSGDUMP, "STA " MACSTR " sent probe request for %s "
+ wpa_printf(MSG_EXCESSIVE, "STA " MACSTR " sent probe request for %s "
"SSID", MAC2STR(mgmt->sa),
elems.ssid_len == 0 ? "broadcast" : "our");
}
+static u8 * hostapd_probe_resp_offloads(struct hostapd_data *hapd,
+ size_t *resp_len)
+{
+ /* check probe response offloading caps and print warnings */
+ if (!(hapd->iface->drv_flags & WPA_DRIVER_FLAGS_PROBE_RESP_OFFLOAD))
+ return NULL;
+
+#ifdef CONFIG_WPS
+ if (hapd->conf->wps_state && hapd->wps_probe_resp_ie &&
+ (!(hapd->iface->probe_resp_offloads &
+ (WPA_DRIVER_PROBE_RESP_OFFLOAD_WPS |
+ WPA_DRIVER_PROBE_RESP_OFFLOAD_WPS2))))
+ wpa_printf(MSG_WARNING, "Device is trying to offload WPS "
+ "Probe Response while not supporting this");
+#endif /* CONFIG_WPS */
+
+#ifdef CONFIG_P2P
+ if ((hapd->conf->p2p & P2P_ENABLED) && hapd->p2p_probe_resp_ie &&
+ !(hapd->iface->probe_resp_offloads &
+ WPA_DRIVER_PROBE_RESP_OFFLOAD_P2P))
+ wpa_printf(MSG_WARNING, "Device is trying to offload P2P "
+ "Probe Response while not supporting this");
+#endif /* CONFIG_P2P */
+
+ if (hapd->conf->interworking &&
+ !(hapd->iface->probe_resp_offloads &
+ WPA_DRIVER_PROBE_RESP_OFFLOAD_INTERWORKING))
+ wpa_printf(MSG_WARNING, "Device is trying to offload "
+ "Interworking Probe Response while not supporting "
+ "this");
+
+ /* Generate a Probe Response template for the non-P2P case */
+ return hostapd_gen_probe_resp(hapd, NULL, NULL, 0, resp_len);
+}
+
+#endif /* NEED_AP_MLME */
+
+
void ieee802_11_set_beacon(struct hostapd_data *hapd)
{
- struct ieee80211_mgmt *head;
- u8 *pos, *tail, *tailpos;
+ struct ieee80211_mgmt *head = NULL;
+ u8 *tail = NULL;
+ size_t head_len = 0, tail_len = 0;
+ u8 *resp = NULL;
+ size_t resp_len = 0;
+ struct wpa_driver_ap_params params;
+ struct wpabuf *beacon, *proberesp, *assocresp;
+#ifdef NEED_AP_MLME
u16 capab_info;
- size_t head_len, tail_len;
+ u8 *pos, *tailpos;
+#endif /* NEED_AP_MLME */
+
+ hapd->beacon_set_done = 1;
+
+#ifdef NEED_AP_MLME
#define BEACON_HEAD_BUF_SIZE 256
#define BEACON_TAIL_BUF_SIZE 512
@@ -354,6 +575,12 @@ void ieee802_11_set_beacon(struct hostapd_data *hapd)
if (hapd->conf->wps_state && hapd->wps_beacon_ie)
tail_len += wpabuf_len(hapd->wps_beacon_ie);
#endif /* CONFIG_WPS */
+#ifdef CONFIG_P2P
+ if (hapd->p2p_beacon_ie)
+ tail_len += wpabuf_len(hapd->p2p_beacon_ie);
+#endif /* CONFIG_P2P */
+ if (hapd->conf->vendor_elements)
+ tail_len += wpabuf_len(hapd->conf->vendor_elements);
tailpos = tail = os_malloc(tail_len);
if (head == NULL || tail == NULL) {
wpa_printf(MSG_ERROR, "Failed to set beacon data");
@@ -412,13 +639,30 @@ void ieee802_11_set_beacon(struct hostapd_data *hapd)
/* RSN, MDIE, WPA */
tailpos = hostapd_eid_wpa(hapd, tailpos, tail + BEACON_TAIL_BUF_SIZE -
- tailpos, NULL);
+ tailpos);
#ifdef CONFIG_IEEE80211N
tailpos = hostapd_eid_ht_capabilities(hapd, tailpos);
tailpos = hostapd_eid_ht_operation(hapd, tailpos);
#endif /* CONFIG_IEEE80211N */
+ tailpos = hostapd_eid_ext_capab(hapd, tailpos);
+
+ /*
+ * TODO: Time Advertisement element should only be included in some
+ * DTIM Beacon frames.
+ */
+ tailpos = hostapd_eid_time_adv(hapd, tailpos);
+
+ tailpos = hostapd_eid_interworking(hapd, tailpos);
+ tailpos = hostapd_eid_adv_proto(hapd, tailpos);
+ tailpos = hostapd_eid_roaming_consortium(hapd, tailpos);
+
+#ifdef CONFIG_IEEE80211AC
+ tailpos = hostapd_eid_vht_capabilities(hapd, tailpos);
+ tailpos = hostapd_eid_vht_operation(hapd, tailpos);
+#endif /* CONFIG_IEEE80211AC */
+
/* Wi-Fi Alliance WMM */
tailpos = hostapd_eid_wmm(hapd, tailpos);
@@ -430,19 +674,104 @@ void ieee802_11_set_beacon(struct hostapd_data *hapd)
}
#endif /* CONFIG_WPS */
+#ifdef CONFIG_P2P
+ if ((hapd->conf->p2p & P2P_ENABLED) && hapd->p2p_beacon_ie) {
+ os_memcpy(tailpos, wpabuf_head(hapd->p2p_beacon_ie),
+ wpabuf_len(hapd->p2p_beacon_ie));
+ tailpos += wpabuf_len(hapd->p2p_beacon_ie);
+ }
+#endif /* CONFIG_P2P */
+#ifdef CONFIG_P2P_MANAGER
+ if ((hapd->conf->p2p & (P2P_MANAGE | P2P_ENABLED | P2P_GROUP_OWNER)) ==
+ P2P_MANAGE)
+ tailpos = hostapd_eid_p2p_manage(hapd, tailpos);
+#endif /* CONFIG_P2P_MANAGER */
+
+#ifdef CONFIG_HS20
+ tailpos = hostapd_eid_hs20_indication(hapd, tailpos);
+#endif /* CONFIG_HS20 */
+
+ if (hapd->conf->vendor_elements) {
+ os_memcpy(tailpos, wpabuf_head(hapd->conf->vendor_elements),
+ wpabuf_len(hapd->conf->vendor_elements));
+ tailpos += wpabuf_len(hapd->conf->vendor_elements);
+ }
+
tail_len = tailpos > tail ? tailpos - tail : 0;
- if (hapd->drv.set_beacon(hapd, (u8 *) head, head_len,
- tail, tail_len, hapd->conf->dtim_period,
- hapd->iconf->beacon_int))
- wpa_printf(MSG_ERROR, "Failed to set beacon head/tail or DTIM "
- "period");
+ resp = hostapd_probe_resp_offloads(hapd, &resp_len);
+#endif /* NEED_AP_MLME */
+
+ os_memset(&params, 0, sizeof(params));
+ params.head = (u8 *) head;
+ params.head_len = head_len;
+ params.tail = tail;
+ params.tail_len = tail_len;
+ params.proberesp = resp;
+ params.proberesp_len = resp_len;
+ params.dtim_period = hapd->conf->dtim_period;
+ params.beacon_int = hapd->iconf->beacon_int;
+ params.basic_rates = hapd->iface->basic_rates;
+ params.ssid = hapd->conf->ssid.ssid;
+ params.ssid_len = hapd->conf->ssid.ssid_len;
+ params.pairwise_ciphers = hapd->conf->rsn_pairwise ?
+ hapd->conf->rsn_pairwise : hapd->conf->wpa_pairwise;
+ params.group_cipher = hapd->conf->wpa_group;
+ params.key_mgmt_suites = hapd->conf->wpa_key_mgmt;
+ params.auth_algs = hapd->conf->auth_algs;
+ params.wpa_version = hapd->conf->wpa;
+ params.privacy = hapd->conf->ssid.wep.keys_set || hapd->conf->wpa ||
+ (hapd->conf->ieee802_1x &&
+ (hapd->conf->default_wep_key_len ||
+ hapd->conf->individual_wep_key_len));
+ switch (hapd->conf->ignore_broadcast_ssid) {
+ case 0:
+ params.hide_ssid = NO_SSID_HIDING;
+ break;
+ case 1:
+ params.hide_ssid = HIDDEN_SSID_ZERO_LEN;
+ break;
+ case 2:
+ params.hide_ssid = HIDDEN_SSID_ZERO_CONTENTS;
+ break;
+ }
+ hostapd_build_ap_extra_ies(hapd, &beacon, &proberesp, &assocresp);
+ params.beacon_ies = beacon;
+ params.proberesp_ies = proberesp;
+ params.assocresp_ies = assocresp;
+ params.isolate = hapd->conf->isolate;
+#ifdef NEED_AP_MLME
+ params.cts_protect = !!(ieee802_11_erp_info(hapd) &
+ ERP_INFO_USE_PROTECTION);
+ params.preamble = hapd->iface->num_sta_no_short_preamble == 0 &&
+ hapd->iconf->preamble == SHORT_PREAMBLE;
+ if (hapd->iface->current_mode &&
+ hapd->iface->current_mode->mode == HOSTAPD_MODE_IEEE80211G)
+ params.short_slot_time =
+ hapd->iface->num_sta_no_short_slot_time > 0 ? 0 : 1;
+ else
+ params.short_slot_time = -1;
+ if (!hapd->iconf->ieee80211n || hapd->conf->disable_11n)
+ params.ht_opmode = -1;
+ else
+ params.ht_opmode = hapd->iface->ht_op_mode;
+#endif /* NEED_AP_MLME */
+ params.interworking = hapd->conf->interworking;
+ if (hapd->conf->interworking &&
+ !is_zero_ether_addr(hapd->conf->hessid))
+ params.hessid = hapd->conf->hessid;
+ params.access_network_type = hapd->conf->access_network_type;
+ params.ap_max_inactivity = hapd->conf->ap_max_inactivity;
+#ifdef CONFIG_HS20
+ params.disable_dgaf = hapd->conf->disable_dgaf;
+#endif /* CONFIG_HS20 */
+ if (hostapd_drv_set_ap(hapd, &params))
+ wpa_printf(MSG_ERROR, "Failed to set beacon parameters");
+ hostapd_free_ap_extra_ies(hapd, beacon, proberesp, assocresp);
os_free(tail);
os_free(head);
-
- hapd->drv.set_bss_params(hapd, !!(ieee802_11_erp_info(hapd) &
- ERP_INFO_USE_PROTECTION));
+ os_free(resp);
}
@@ -453,4 +782,14 @@ void ieee802_11_set_beacons(struct hostapd_iface *iface)
ieee802_11_set_beacon(iface->bss[i]);
}
+
+/* only update beacons if started */
+void ieee802_11_update_beacons(struct hostapd_iface *iface)
+{
+ size_t i;
+ for (i = 0; i < iface->num_bss; i++)
+ if (iface->bss[i]->beacon_set_done)
+ ieee802_11_set_beacon(iface->bss[i]);
+}
+
#endif /* CONFIG_NATIVE_WINDOWS */
diff --git a/contrib/wpa/src/ap/beacon.h b/contrib/wpa/src/ap/beacon.h
index c1510e1..37f10d2 100644
--- a/contrib/wpa/src/ap/beacon.h
+++ b/contrib/wpa/src/ap/beacon.h
@@ -19,18 +19,10 @@
struct ieee80211_mgmt;
void handle_probe_req(struct hostapd_data *hapd,
- const struct ieee80211_mgmt *mgmt, size_t len);
-#ifdef NEED_AP_MLME
+ const struct ieee80211_mgmt *mgmt, size_t len,
+ int ssi_signal);
void ieee802_11_set_beacon(struct hostapd_data *hapd);
void ieee802_11_set_beacons(struct hostapd_iface *iface);
-#else /* NEED_AP_MLME */
-static inline void ieee802_11_set_beacon(struct hostapd_data *hapd)
-{
-}
-
-static inline void ieee802_11_set_beacons(struct hostapd_iface *iface)
-{
-}
-#endif /* NEED_AP_MLME */
+void ieee802_11_update_beacons(struct hostapd_iface *iface);
#endif /* BEACON_H */
diff --git a/contrib/wpa/src/ap/ctrl_iface_ap.c b/contrib/wpa/src/ap/ctrl_iface_ap.c
index e50b0a7..c55d3fe 100644
--- a/contrib/wpa/src/ap/ctrl_iface_ap.c
+++ b/contrib/wpa/src/ap/ctrl_iface_ap.c
@@ -2,26 +2,45 @@
* Control interface for shared AP commands
* Copyright (c) 2004-2009, Jouni Malinen <j@w1.fi>
*
- * This program is free software; you can redistribute it and/or modify
- * it under the terms of the GNU General Public License version 2 as
- * published by the Free Software Foundation.
- *
- * Alternatively, this software may be distributed under the terms of BSD
- * license.
- *
- * See README and COPYING for more details.
+ * This software may be distributed under the terms of the BSD license.
+ * See README for more details.
*/
#include "utils/includes.h"
#include "utils/common.h"
+#include "common/ieee802_11_defs.h"
#include "hostapd.h"
#include "ieee802_1x.h"
#include "wpa_auth.h"
#include "ieee802_11.h"
#include "sta_info.h"
#include "wps_hostapd.h"
+#include "p2p_hostapd.h"
#include "ctrl_iface_ap.h"
+#include "ap_drv_ops.h"
+
+
+static int hostapd_get_sta_conn_time(struct sta_info *sta,
+ char *buf, size_t buflen)
+{
+ struct os_time now, age;
+ int len = 0, ret;
+
+ if (!sta->connected_time.sec)
+ return 0;
+
+ os_get_time(&now);
+ os_time_sub(&now, &sta->connected_time, &age);
+
+ ret = os_snprintf(buf + len, buflen - len, "connected_time=%u\n",
+ (unsigned int) age.sec);
+ if (ret < 0 || (size_t) ret >= buflen - len)
+ return len;
+ len += ret;
+
+ return len;
+}
static int hostapd_ctrl_iface_sta_mib(struct hostapd_data *hapd,
@@ -57,6 +76,13 @@ static int hostapd_ctrl_iface_sta_mib(struct hostapd_data *hapd,
buflen - len);
if (res >= 0)
len += res;
+ res = hostapd_p2p_get_mib_sta(hapd, sta, buf + len, buflen - len);
+ if (res >= 0)
+ len += res;
+
+ res = hostapd_get_sta_conn_time(sta, buf + len, buflen - len);
+ if (res >= 0)
+ len += res;
return len;
}
@@ -102,3 +128,170 @@ int hostapd_ctrl_iface_sta_next(struct hostapd_data *hapd, const char *txtaddr,
}
return hostapd_ctrl_iface_sta_mib(hapd, sta->next, buf, buflen);
}
+
+
+#ifdef CONFIG_P2P_MANAGER
+static int p2p_manager_disconnect(struct hostapd_data *hapd, u16 stype,
+ u8 minor_reason_code, const u8 *addr)
+{
+ struct ieee80211_mgmt *mgmt;
+ int ret;
+ u8 *pos;
+
+ if (hapd->driver->send_frame == NULL)
+ return -1;
+
+ mgmt = os_zalloc(sizeof(*mgmt) + 100);
+ if (mgmt == NULL)
+ return -1;
+
+ wpa_dbg(hapd->msg_ctx, MSG_DEBUG, "P2P: Disconnect STA " MACSTR
+ " with minor reason code %u (stype=%u)",
+ MAC2STR(addr), minor_reason_code, stype);
+
+ mgmt->frame_control = IEEE80211_FC(WLAN_FC_TYPE_MGMT, stype);
+ os_memcpy(mgmt->da, addr, ETH_ALEN);
+ os_memcpy(mgmt->sa, hapd->own_addr, ETH_ALEN);
+ os_memcpy(mgmt->bssid, hapd->own_addr, ETH_ALEN);
+ if (stype == WLAN_FC_STYPE_DEAUTH) {
+ mgmt->u.deauth.reason_code =
+ host_to_le16(WLAN_REASON_PREV_AUTH_NOT_VALID);
+ pos = (u8 *) (&mgmt->u.deauth.reason_code + 1);
+ } else {
+ mgmt->u.disassoc.reason_code =
+ host_to_le16(WLAN_REASON_PREV_AUTH_NOT_VALID);
+ pos = (u8 *) (&mgmt->u.disassoc.reason_code + 1);
+ }
+
+ *pos++ = WLAN_EID_VENDOR_SPECIFIC;
+ *pos++ = 4 + 3 + 1;
+ WPA_PUT_BE24(pos, OUI_WFA);
+ pos += 3;
+ *pos++ = P2P_OUI_TYPE;
+
+ *pos++ = P2P_ATTR_MINOR_REASON_CODE;
+ WPA_PUT_LE16(pos, 1);
+ pos += 2;
+ *pos++ = minor_reason_code;
+
+ ret = hapd->driver->send_frame(hapd->drv_priv, (u8 *) mgmt,
+ pos - (u8 *) mgmt, 1);
+ os_free(mgmt);
+
+ return ret < 0 ? -1 : 0;
+}
+#endif /* CONFIG_P2P_MANAGER */
+
+
+int hostapd_ctrl_iface_deauthenticate(struct hostapd_data *hapd,
+ const char *txtaddr)
+{
+ u8 addr[ETH_ALEN];
+ struct sta_info *sta;
+ const char *pos;
+
+ wpa_dbg(hapd->msg_ctx, MSG_DEBUG, "CTRL_IFACE DEAUTHENTICATE %s",
+ txtaddr);
+
+ if (hwaddr_aton(txtaddr, addr))
+ return -1;
+
+ pos = os_strstr(txtaddr, " test=");
+ if (pos) {
+ struct ieee80211_mgmt mgmt;
+ int encrypt;
+ if (hapd->driver->send_frame == NULL)
+ return -1;
+ pos += 6;
+ encrypt = atoi(pos);
+ os_memset(&mgmt, 0, sizeof(mgmt));
+ mgmt.frame_control = IEEE80211_FC(WLAN_FC_TYPE_MGMT,
+ WLAN_FC_STYPE_DEAUTH);
+ os_memcpy(mgmt.da, addr, ETH_ALEN);
+ os_memcpy(mgmt.sa, hapd->own_addr, ETH_ALEN);
+ os_memcpy(mgmt.bssid, hapd->own_addr, ETH_ALEN);
+ mgmt.u.deauth.reason_code =
+ host_to_le16(WLAN_REASON_PREV_AUTH_NOT_VALID);
+ if (hapd->driver->send_frame(hapd->drv_priv, (u8 *) &mgmt,
+ IEEE80211_HDRLEN +
+ sizeof(mgmt.u.deauth),
+ encrypt) < 0)
+ return -1;
+ return 0;
+ }
+
+#ifdef CONFIG_P2P_MANAGER
+ pos = os_strstr(txtaddr, " p2p=");
+ if (pos) {
+ return p2p_manager_disconnect(hapd, WLAN_FC_STYPE_DEAUTH,
+ atoi(pos + 5), addr);
+ }
+#endif /* CONFIG_P2P_MANAGER */
+
+ hostapd_drv_sta_deauth(hapd, addr, WLAN_REASON_PREV_AUTH_NOT_VALID);
+ sta = ap_get_sta(hapd, addr);
+ if (sta)
+ ap_sta_deauthenticate(hapd, sta,
+ WLAN_REASON_PREV_AUTH_NOT_VALID);
+ else if (addr[0] == 0xff)
+ hostapd_free_stas(hapd);
+
+ return 0;
+}
+
+
+int hostapd_ctrl_iface_disassociate(struct hostapd_data *hapd,
+ const char *txtaddr)
+{
+ u8 addr[ETH_ALEN];
+ struct sta_info *sta;
+ const char *pos;
+
+ wpa_dbg(hapd->msg_ctx, MSG_DEBUG, "CTRL_IFACE DISASSOCIATE %s",
+ txtaddr);
+
+ if (hwaddr_aton(txtaddr, addr))
+ return -1;
+
+ pos = os_strstr(txtaddr, " test=");
+ if (pos) {
+ struct ieee80211_mgmt mgmt;
+ int encrypt;
+ if (hapd->driver->send_frame == NULL)
+ return -1;
+ pos += 6;
+ encrypt = atoi(pos);
+ os_memset(&mgmt, 0, sizeof(mgmt));
+ mgmt.frame_control = IEEE80211_FC(WLAN_FC_TYPE_MGMT,
+ WLAN_FC_STYPE_DISASSOC);
+ os_memcpy(mgmt.da, addr, ETH_ALEN);
+ os_memcpy(mgmt.sa, hapd->own_addr, ETH_ALEN);
+ os_memcpy(mgmt.bssid, hapd->own_addr, ETH_ALEN);
+ mgmt.u.disassoc.reason_code =
+ host_to_le16(WLAN_REASON_PREV_AUTH_NOT_VALID);
+ if (hapd->driver->send_frame(hapd->drv_priv, (u8 *) &mgmt,
+ IEEE80211_HDRLEN +
+ sizeof(mgmt.u.deauth),
+ encrypt) < 0)
+ return -1;
+ return 0;
+ }
+
+#ifdef CONFIG_P2P_MANAGER
+ pos = os_strstr(txtaddr, " p2p=");
+ if (pos) {
+ return p2p_manager_disconnect(hapd, WLAN_FC_STYPE_DISASSOC,
+ atoi(pos + 5), addr);
+ }
+#endif /* CONFIG_P2P_MANAGER */
+
+ hostapd_drv_sta_disassoc(hapd, addr, WLAN_REASON_PREV_AUTH_NOT_VALID);
+ sta = ap_get_sta(hapd, addr);
+ if (sta)
+ ap_sta_disassociate(hapd, sta,
+ WLAN_REASON_PREV_AUTH_NOT_VALID);
+ else if (addr[0] == 0xff)
+ hostapd_free_stas(hapd);
+
+ return 0;
+}
diff --git a/contrib/wpa/src/ap/ctrl_iface_ap.h b/contrib/wpa/src/ap/ctrl_iface_ap.h
index 8690bea..e83f894 100644
--- a/contrib/wpa/src/ap/ctrl_iface_ap.h
+++ b/contrib/wpa/src/ap/ctrl_iface_ap.h
@@ -2,14 +2,8 @@
* Control interface for shared AP commands
* Copyright (c) 2004-2009, Jouni Malinen <j@w1.fi>
*
- * This program is free software; you can redistribute it and/or modify
- * it under the terms of the GNU General Public License version 2 as
- * published by the Free Software Foundation.
- *
- * Alternatively, this software may be distributed under the terms of BSD
- * license.
- *
- * See README and COPYING for more details.
+ * This software may be distributed under the terms of the BSD license.
+ * See README for more details.
*/
#ifndef CTRL_IFACE_AP_H
@@ -21,5 +15,9 @@ int hostapd_ctrl_iface_sta(struct hostapd_data *hapd, const char *txtaddr,
char *buf, size_t buflen);
int hostapd_ctrl_iface_sta_next(struct hostapd_data *hapd, const char *txtaddr,
char *buf, size_t buflen);
+int hostapd_ctrl_iface_deauthenticate(struct hostapd_data *hapd,
+ const char *txtaddr);
+int hostapd_ctrl_iface_disassociate(struct hostapd_data *hapd,
+ const char *txtaddr);
#endif /* CTRL_IFACE_AP_H */
diff --git a/contrib/wpa/src/ap/drv_callbacks.c b/contrib/wpa/src/ap/drv_callbacks.c
index 26ef584..8980bec 100644
--- a/contrib/wpa/src/ap/drv_callbacks.c
+++ b/contrib/wpa/src/ap/drv_callbacks.c
@@ -2,14 +2,8 @@
* hostapd / Callback functions for driver wrappers
* Copyright (c) 2002-2009, Jouni Malinen <j@w1.fi>
*
- * This program is free software; you can redistribute it and/or modify
- * it under the terms of the GNU General Public License version 2 as
- * published by the Free Software Foundation.
- *
- * Alternatively, this software may be distributed under the terms of BSD
- * license.
- *
- * See README and COPYING for more details.
+ * This software may be distributed under the terms of the BSD license.
+ * See README for more details.
*/
#include "utils/includes.h"
@@ -19,26 +13,37 @@
#include "drivers/driver.h"
#include "common/ieee802_11_defs.h"
#include "common/ieee802_11_common.h"
-#include "common/wpa_ctrl.h"
+#include "crypto/random.h"
+#include "p2p/p2p.h"
+#include "wps/wps.h"
+#include "wnm_ap.h"
#include "hostapd.h"
#include "ieee802_11.h"
#include "sta_info.h"
#include "accounting.h"
#include "tkip_countermeasures.h"
-#include "iapp.h"
#include "ieee802_1x.h"
#include "wpa_auth.h"
-#include "wmm.h"
#include "wps_hostapd.h"
+#include "ap_drv_ops.h"
#include "ap_config.h"
+#include "hw_features.h"
int hostapd_notif_assoc(struct hostapd_data *hapd, const u8 *addr,
- const u8 *ie, size_t ielen)
+ const u8 *req_ies, size_t req_ies_len, int reassoc)
{
struct sta_info *sta;
int new_assoc, res;
struct ieee802_11_elems elems;
+ const u8 *ie;
+ size_t ielen;
+#ifdef CONFIG_IEEE80211R
+ u8 buf[sizeof(struct ieee80211_mgmt) + 1024];
+ u8 *p = buf;
+#endif /* CONFIG_IEEE80211R */
+ u16 reason = WLAN_REASON_UNSPECIFIED;
+ u16 status = WLAN_STATUS_SUCCESS;
if (addr == NULL) {
/*
@@ -52,11 +57,12 @@ int hostapd_notif_assoc(struct hostapd_data *hapd, const u8 *addr,
"no address");
return -1;
}
+ random_add_randomness(addr, ETH_ALEN);
hostapd_logger(hapd, addr, HOSTAPD_MODULE_IEEE80211,
HOSTAPD_LEVEL_INFO, "associated");
- ieee802_11_parse_elems(ie, ielen, &elems, 0);
+ ieee802_11_parse_elems(req_ies, req_ies_len, &elems, 0);
if (elems.wps_ie) {
ie = elems.wps_ie - 2;
ielen = elems.wps_ie_len + 2;
@@ -79,15 +85,42 @@ int hostapd_notif_assoc(struct hostapd_data *hapd, const u8 *addr,
sta = ap_get_sta(hapd, addr);
if (sta) {
accounting_sta_stop(hapd, sta);
+
+ /*
+ * Make sure that the previously registered inactivity timer
+ * will not remove the STA immediately.
+ */
+ sta->timeout_next = STA_NULLFUNC;
} else {
sta = ap_sta_add(hapd, addr);
- if (sta == NULL)
+ if (sta == NULL) {
+ hostapd_drv_sta_disassoc(hapd, addr,
+ WLAN_REASON_DISASSOC_AP_BUSY);
return -1;
+ }
+ }
+ sta->flags &= ~(WLAN_STA_WPS | WLAN_STA_MAYBE_WPS | WLAN_STA_WPS2);
+
+#ifdef CONFIG_P2P
+ if (elems.p2p) {
+ wpabuf_free(sta->p2p_ie);
+ sta->p2p_ie = ieee802_11_vendor_ie_concat(req_ies, req_ies_len,
+ P2P_IE_VENDOR_TYPE);
}
- sta->flags &= ~(WLAN_STA_WPS | WLAN_STA_MAYBE_WPS);
+#endif /* CONFIG_P2P */
+
+#ifdef CONFIG_HS20
+ wpabuf_free(sta->hs20_ie);
+ if (elems.hs20 && elems.hs20_len > 4) {
+ sta->hs20_ie = wpabuf_alloc_copy(elems.hs20 + 4,
+ elems.hs20_len - 4);
+ } else
+ sta->hs20_ie = NULL;
+#endif /* CONFIG_HS20 */
if (hapd->conf->wpa) {
if (ie == NULL || ielen == 0) {
+#ifdef CONFIG_WPS
if (hapd->conf->wps_state) {
wpa_printf(MSG_DEBUG, "STA did not include "
"WPA/RSN IE in (Re)Association "
@@ -95,15 +128,29 @@ int hostapd_notif_assoc(struct hostapd_data *hapd, const u8 *addr,
sta->flags |= WLAN_STA_MAYBE_WPS;
goto skip_wpa_check;
}
+#endif /* CONFIG_WPS */
wpa_printf(MSG_DEBUG, "No WPA/RSN IE from STA");
return -1;
}
+#ifdef CONFIG_WPS
if (hapd->conf->wps_state && ie[0] == 0xdd && ie[1] >= 4 &&
os_memcmp(ie + 2, "\x00\x50\xf2\x04", 4) == 0) {
+ struct wpabuf *wps;
sta->flags |= WLAN_STA_WPS;
+ wps = ieee802_11_vendor_ie_concat(ie, ielen,
+ WPS_IE_VENDOR_TYPE);
+ if (wps) {
+ if (wps_is_20(wps)) {
+ wpa_printf(MSG_DEBUG, "WPS: STA "
+ "supports WPS 2.0");
+ sta->flags |= WLAN_STA_WPS2;
+ }
+ wpabuf_free(wps);
+ }
goto skip_wpa_check;
}
+#endif /* CONFIG_WPS */
if (sta->wpa_sm == NULL)
sta->wpa_sm = wpa_auth_sta_init(hapd->wpa_auth,
@@ -114,48 +161,156 @@ int hostapd_notif_assoc(struct hostapd_data *hapd, const u8 *addr,
return -1;
}
res = wpa_validate_wpa_ie(hapd->wpa_auth, sta->wpa_sm,
- ie, ielen, NULL, 0);
+ ie, ielen,
+ elems.mdie, elems.mdie_len);
if (res != WPA_IE_OK) {
- int resp;
wpa_printf(MSG_DEBUG, "WPA/RSN information element "
"rejected? (res %u)", res);
wpa_hexdump(MSG_DEBUG, "IE", ie, ielen);
- if (res == WPA_INVALID_GROUP)
- resp = WLAN_REASON_GROUP_CIPHER_NOT_VALID;
- else if (res == WPA_INVALID_PAIRWISE)
- resp = WLAN_REASON_PAIRWISE_CIPHER_NOT_VALID;
- else if (res == WPA_INVALID_AKMP)
- resp = WLAN_REASON_AKMP_NOT_VALID;
+ if (res == WPA_INVALID_GROUP) {
+ reason = WLAN_REASON_GROUP_CIPHER_NOT_VALID;
+ status = WLAN_STATUS_GROUP_CIPHER_NOT_VALID;
+ } else if (res == WPA_INVALID_PAIRWISE) {
+ reason = WLAN_REASON_PAIRWISE_CIPHER_NOT_VALID;
+ status = WLAN_STATUS_PAIRWISE_CIPHER_NOT_VALID;
+ } else if (res == WPA_INVALID_AKMP) {
+ reason = WLAN_REASON_AKMP_NOT_VALID;
+ status = WLAN_STATUS_AKMP_NOT_VALID;
+ }
#ifdef CONFIG_IEEE80211W
- else if (res == WPA_MGMT_FRAME_PROTECTION_VIOLATION)
- resp = WLAN_REASON_INVALID_IE;
- else if (res == WPA_INVALID_MGMT_GROUP_CIPHER)
- resp = WLAN_REASON_GROUP_CIPHER_NOT_VALID;
+ else if (res == WPA_MGMT_FRAME_PROTECTION_VIOLATION) {
+ reason = WLAN_REASON_INVALID_IE;
+ status = WLAN_STATUS_INVALID_IE;
+ } else if (res == WPA_INVALID_MGMT_GROUP_CIPHER) {
+ reason = WLAN_REASON_GROUP_CIPHER_NOT_VALID;
+ status = WLAN_STATUS_GROUP_CIPHER_NOT_VALID;
+ }
#endif /* CONFIG_IEEE80211W */
- else
- resp = WLAN_REASON_INVALID_IE;
- hapd->drv.sta_disassoc(hapd, sta->addr, resp);
- ap_free_sta(hapd, sta);
- return -1;
+ else {
+ reason = WLAN_REASON_INVALID_IE;
+ status = WLAN_STATUS_INVALID_IE;
+ }
+ goto fail;
+ }
+#ifdef CONFIG_IEEE80211W
+ if ((sta->flags & WLAN_STA_MFP) && !sta->sa_query_timed_out &&
+ sta->sa_query_count > 0)
+ ap_check_sa_query_timeout(hapd, sta);
+ if ((sta->flags & WLAN_STA_MFP) && !sta->sa_query_timed_out &&
+ (sta->auth_alg != WLAN_AUTH_FT)) {
+ /*
+ * STA has already been associated with MFP and SA
+ * Query timeout has not been reached. Reject the
+ * association attempt temporarily and start SA Query,
+ * if one is not pending.
+ */
+
+ if (sta->sa_query_count == 0)
+ ap_sta_start_sa_query(hapd, sta);
+
+#ifdef CONFIG_IEEE80211R
+ status = WLAN_STATUS_ASSOC_REJECTED_TEMPORARILY;
+
+ p = hostapd_eid_assoc_comeback_time(hapd, sta, p);
+
+ hostapd_sta_assoc(hapd, addr, reassoc, status, buf,
+ p - buf);
+#endif /* CONFIG_IEEE80211R */
+ return 0;
}
+
+ if (wpa_auth_uses_mfp(sta->wpa_sm))
+ sta->flags |= WLAN_STA_MFP;
+ else
+ sta->flags &= ~WLAN_STA_MFP;
+#endif /* CONFIG_IEEE80211W */
+
+#ifdef CONFIG_IEEE80211R
+ if (sta->auth_alg == WLAN_AUTH_FT) {
+ status = wpa_ft_validate_reassoc(sta->wpa_sm, req_ies,
+ req_ies_len);
+ if (status != WLAN_STATUS_SUCCESS) {
+ if (status == WLAN_STATUS_INVALID_PMKID)
+ reason = WLAN_REASON_INVALID_IE;
+ if (status == WLAN_STATUS_INVALID_MDIE)
+ reason = WLAN_REASON_INVALID_IE;
+ if (status == WLAN_STATUS_INVALID_FTIE)
+ reason = WLAN_REASON_INVALID_IE;
+ goto fail;
+ }
+ }
+#endif /* CONFIG_IEEE80211R */
} else if (hapd->conf->wps_state) {
- if (ie && ielen > 4 && ie[0] == 0xdd && ie[1] >= 4 &&
- os_memcmp(ie + 2, "\x00\x50\xf2\x04", 4) == 0) {
+#ifdef CONFIG_WPS
+ struct wpabuf *wps;
+ if (req_ies)
+ wps = ieee802_11_vendor_ie_concat(req_ies, req_ies_len,
+ WPS_IE_VENDOR_TYPE);
+ else
+ wps = NULL;
+#ifdef CONFIG_WPS_STRICT
+ if (wps && wps_validate_assoc_req(wps) < 0) {
+ reason = WLAN_REASON_INVALID_IE;
+ status = WLAN_STATUS_INVALID_IE;
+ wpabuf_free(wps);
+ goto fail;
+ }
+#endif /* CONFIG_WPS_STRICT */
+ if (wps) {
sta->flags |= WLAN_STA_WPS;
+ if (wps_is_20(wps)) {
+ wpa_printf(MSG_DEBUG, "WPS: STA supports "
+ "WPS 2.0");
+ sta->flags |= WLAN_STA_WPS2;
+ }
} else
sta->flags |= WLAN_STA_MAYBE_WPS;
+ wpabuf_free(wps);
+#endif /* CONFIG_WPS */
}
+#ifdef CONFIG_WPS
skip_wpa_check:
+#endif /* CONFIG_WPS */
+
+#ifdef CONFIG_IEEE80211R
+ p = wpa_sm_write_assoc_resp_ies(sta->wpa_sm, buf, sizeof(buf),
+ sta->auth_alg, req_ies, req_ies_len);
+
+ hostapd_sta_assoc(hapd, addr, reassoc, status, buf, p - buf);
+#else /* CONFIG_IEEE80211R */
+ /* Keep compiler silent about unused variables */
+ if (status) {
+ }
+#endif /* CONFIG_IEEE80211R */
new_assoc = (sta->flags & WLAN_STA_ASSOC) == 0;
sta->flags |= WLAN_STA_AUTH | WLAN_STA_ASSOC;
- wpa_auth_sm_event(sta->wpa_sm, WPA_ASSOC);
+
+ if (reassoc && (sta->auth_alg == WLAN_AUTH_FT))
+ wpa_auth_sm_event(sta->wpa_sm, WPA_ASSOC_FT);
+ else
+ wpa_auth_sm_event(sta->wpa_sm, WPA_ASSOC);
hostapd_new_assoc_sta(hapd, sta, !new_assoc);
ieee802_1x_notify_port_enabled(sta->eapol_sm, 1);
+#ifdef CONFIG_P2P
+ if (req_ies) {
+ p2p_group_notif_assoc(hapd->p2p_group, sta->addr,
+ req_ies, req_ies_len);
+ }
+#endif /* CONFIG_P2P */
+
return 0;
+
+fail:
+#ifdef CONFIG_IEEE80211R
+ hostapd_sta_assoc(hapd, addr, reassoc, status, buf, p - buf);
+#endif /* CONFIG_IEEE80211R */
+ hostapd_drv_sta_disassoc(hapd, sta->addr, reason);
+ ap_free_sta(hapd, sta);
+ return -1;
}
@@ -163,6 +318,19 @@ void hostapd_notif_disassoc(struct hostapd_data *hapd, const u8 *addr)
{
struct sta_info *sta;
+ if (addr == NULL) {
+ /*
+ * This could potentially happen with unexpected event from the
+ * driver wrapper. This was seen at least in one case where the
+ * driver ended up reporting a station mode event while hostapd
+ * was running, so better make sure we stop processing such an
+ * event here.
+ */
+ wpa_printf(MSG_DEBUG, "hostapd_notif_disassoc: Skip event "
+ "with no address");
+ return;
+ }
+
hostapd_logger(hapd, addr, HOSTAPD_MODULE_IEEE80211,
HOSTAPD_LEVEL_INFO, "disassociated");
@@ -173,9 +341,8 @@ void hostapd_notif_disassoc(struct hostapd_data *hapd, const u8 *addr)
return;
}
+ ap_sta_set_authorized(hapd, sta, 0);
sta->flags &= ~(WLAN_STA_AUTH | WLAN_STA_ASSOC);
- wpa_msg(hapd->msg_ctx, MSG_INFO, AP_STA_DISCONNECTED MACSTR,
- MAC2STR(sta->addr));
wpa_auth_sm_event(sta->wpa_sm, WPA_DISASSOC);
sta->acct_terminate_cause = RADIUS_ACCT_TERMINATE_CAUSE_USER_REQUEST;
ieee802_1x_notify_port_enabled(sta->eapol_sm, 0);
@@ -183,50 +350,180 @@ void hostapd_notif_disassoc(struct hostapd_data *hapd, const u8 *addr)
}
-#ifdef HOSTAPD
+void hostapd_event_sta_low_ack(struct hostapd_data *hapd, const u8 *addr)
+{
+ struct sta_info *sta = ap_get_sta(hapd, addr);
+
+ if (!sta || !hapd->conf->disassoc_low_ack)
+ return;
+
+ hostapd_logger(hapd, addr, HOSTAPD_MODULE_IEEE80211,
+ HOSTAPD_LEVEL_INFO, "disconnected due to excessive "
+ "missing ACKs");
+ hostapd_drv_sta_disassoc(hapd, addr, WLAN_REASON_DISASSOC_LOW_ACK);
+ if (sta)
+ ap_sta_disassociate(hapd, sta, WLAN_REASON_DISASSOC_LOW_ACK);
+}
+
+void hostapd_event_ch_switch(struct hostapd_data *hapd, int freq, int ht,
+ int offset)
+{
#ifdef NEED_AP_MLME
+ int channel;
-static const u8 * get_hdr_bssid(const struct ieee80211_hdr *hdr, size_t len)
+ hostapd_logger(hapd, NULL, HOSTAPD_MODULE_IEEE80211,
+ HOSTAPD_LEVEL_INFO, "driver had channel switch: "
+ "freq=%d, ht=%d, offset=%d", freq, ht, offset);
+
+ hapd->iface->freq = freq;
+
+ channel = hostapd_hw_get_channel(hapd, freq);
+ if (!channel) {
+ hostapd_logger(hapd, NULL, HOSTAPD_MODULE_IEEE80211,
+ HOSTAPD_LEVEL_WARNING, "driver switched to "
+ "bad channel!");
+ return;
+ }
+
+ hapd->iconf->channel = channel;
+ hapd->iconf->ieee80211n = ht;
+ hapd->iconf->secondary_channel = offset;
+#endif /* NEED_AP_MLME */
+}
+
+
+int hostapd_probe_req_rx(struct hostapd_data *hapd, const u8 *sa, const u8 *da,
+ const u8 *bssid, const u8 *ie, size_t ie_len,
+ int ssi_signal)
{
- u16 fc, type, stype;
+ size_t i;
+ int ret = 0;
- /*
- * PS-Poll frames are 16 bytes. All other frames are
- * 24 bytes or longer.
- */
- if (len < 16)
- return NULL;
+ if (sa == NULL || ie == NULL)
+ return -1;
- fc = le_to_host16(hdr->frame_control);
- type = WLAN_FC_GET_TYPE(fc);
- stype = WLAN_FC_GET_STYPE(fc);
-
- switch (type) {
- case WLAN_FC_TYPE_DATA:
- if (len < 24)
- return NULL;
- switch (fc & (WLAN_FC_FROMDS | WLAN_FC_TODS)) {
- case WLAN_FC_FROMDS | WLAN_FC_TODS:
- case WLAN_FC_TODS:
- return hdr->addr1;
- case WLAN_FC_FROMDS:
- return hdr->addr2;
- default:
- return NULL;
+ random_add_randomness(sa, ETH_ALEN);
+ for (i = 0; hapd->probereq_cb && i < hapd->num_probereq_cb; i++) {
+ if (hapd->probereq_cb[i].cb(hapd->probereq_cb[i].ctx,
+ sa, da, bssid, ie, ie_len,
+ ssi_signal) > 0) {
+ ret = 1;
+ break;
}
- case WLAN_FC_TYPE_CTRL:
- if (stype != WLAN_FC_STYPE_PSPOLL)
- return NULL;
- return hdr->addr1;
- case WLAN_FC_TYPE_MGMT:
- return hdr->addr3;
- default:
- return NULL;
}
+ return ret;
}
+#ifdef HOSTAPD
+
+#ifdef CONFIG_IEEE80211R
+static void hostapd_notify_auth_ft_finish(void *ctx, const u8 *dst,
+ const u8 *bssid,
+ u16 auth_transaction, u16 status,
+ const u8 *ies, size_t ies_len)
+{
+ struct hostapd_data *hapd = ctx;
+ struct sta_info *sta;
+
+ sta = ap_get_sta(hapd, dst);
+ if (sta == NULL)
+ return;
+
+ hostapd_logger(hapd, dst, HOSTAPD_MODULE_IEEE80211,
+ HOSTAPD_LEVEL_DEBUG, "authentication OK (FT)");
+ sta->flags |= WLAN_STA_AUTH;
+
+ hostapd_sta_auth(hapd, dst, auth_transaction, status, ies, ies_len);
+}
+#endif /* CONFIG_IEEE80211R */
+
+
+static void hostapd_notif_auth(struct hostapd_data *hapd,
+ struct auth_info *rx_auth)
+{
+ struct sta_info *sta;
+ u16 status = WLAN_STATUS_SUCCESS;
+ u8 resp_ies[2 + WLAN_AUTH_CHALLENGE_LEN];
+ size_t resp_ies_len = 0;
+
+ sta = ap_get_sta(hapd, rx_auth->peer);
+ if (!sta) {
+ sta = ap_sta_add(hapd, rx_auth->peer);
+ if (sta == NULL) {
+ status = WLAN_STATUS_UNSPECIFIED_FAILURE;
+ goto fail;
+ }
+ }
+ sta->flags &= ~WLAN_STA_PREAUTH;
+ ieee802_1x_notify_pre_auth(sta->eapol_sm, 0);
+#ifdef CONFIG_IEEE80211R
+ if (rx_auth->auth_type == WLAN_AUTH_FT && hapd->wpa_auth) {
+ sta->auth_alg = WLAN_AUTH_FT;
+ if (sta->wpa_sm == NULL)
+ sta->wpa_sm = wpa_auth_sta_init(hapd->wpa_auth,
+ sta->addr);
+ if (sta->wpa_sm == NULL) {
+ wpa_printf(MSG_DEBUG, "FT: Failed to initialize WPA "
+ "state machine");
+ status = WLAN_STATUS_UNSPECIFIED_FAILURE;
+ goto fail;
+ }
+ wpa_ft_process_auth(sta->wpa_sm, rx_auth->bssid,
+ rx_auth->auth_transaction, rx_auth->ies,
+ rx_auth->ies_len,
+ hostapd_notify_auth_ft_finish, hapd);
+ return;
+ }
+#endif /* CONFIG_IEEE80211R */
+fail:
+ hostapd_sta_auth(hapd, rx_auth->peer, rx_auth->auth_transaction + 1,
+ status, resp_ies, resp_ies_len);
+}
+
+
+static void hostapd_action_rx(struct hostapd_data *hapd,
+ struct rx_action *action)
+{
+ struct sta_info *sta;
+
+ wpa_printf(MSG_DEBUG, "RX_ACTION cat %d action plen %d",
+ action->category, (int) action->len);
+
+ sta = ap_get_sta(hapd, action->sa);
+ if (sta == NULL) {
+ wpa_printf(MSG_DEBUG, "%s: station not found", __func__);
+ return;
+ }
+#ifdef CONFIG_IEEE80211R
+ if (action->category == WLAN_ACTION_FT) {
+ wpa_printf(MSG_DEBUG, "%s: FT_ACTION length %d",
+ __func__, (int) action->len);
+ wpa_ft_action_rx(sta->wpa_sm, action->data, action->len);
+ }
+#endif /* CONFIG_IEEE80211R */
+#ifdef CONFIG_IEEE80211W
+ if (action->category == WLAN_ACTION_SA_QUERY && action->len >= 4) {
+ wpa_printf(MSG_DEBUG, "%s: SA_QUERY_ACTION length %d",
+ __func__, (int) action->len);
+ ieee802_11_sa_query_action(hapd, action->sa,
+ *(action->data + 1),
+ action->data + 2);
+ }
+#endif /* CONFIG_IEEE80211W */
+#ifdef CONFIG_WNM
+ if (action->category == WLAN_ACTION_WNM) {
+ wpa_printf(MSG_DEBUG, "%s: WNM_ACTION length %d",
+ __func__, (int) action->len);
+ ieee802_11_rx_wnm_action_ap(hapd, action);
+ }
+#endif /* CONFIG_WNM */
+}
+
+
+#ifdef NEED_AP_MLME
+
#define HAPD_BROADCAST ((struct hostapd_data *) -1)
static struct hostapd_data * get_hapd_bssid(struct hostapd_iface *iface,
@@ -250,17 +547,14 @@ static struct hostapd_data * get_hapd_bssid(struct hostapd_iface *iface,
static void hostapd_rx_from_unknown_sta(struct hostapd_data *hapd,
- const u8 *frame, size_t len)
+ const u8 *bssid, const u8 *addr,
+ int wds)
{
- const struct ieee80211_hdr *hdr = (const struct ieee80211_hdr *) frame;
- u16 fc = le_to_host16(hdr->frame_control);
- hapd = get_hapd_bssid(hapd->iface, get_hdr_bssid(hdr, len));
+ hapd = get_hapd_bssid(hapd->iface, bssid);
if (hapd == NULL || hapd == HAPD_BROADCAST)
return;
- ieee802_11_rx_from_unknown(hapd, hdr->addr2,
- (fc & (WLAN_FC_TODS | WLAN_FC_FROMDS)) ==
- (WLAN_FC_TODS | WLAN_FC_FROMDS));
+ ieee802_11_rx_from_unknown(hapd, addr, wds);
}
@@ -303,6 +597,48 @@ static void hostapd_mgmt_rx(struct hostapd_data *hapd, struct rx_mgmt *rx_mgmt)
rx_mgmt->frame_len, &fi);
} else
ieee802_11_mgmt(hapd, rx_mgmt->frame, rx_mgmt->frame_len, &fi);
+
+ random_add_randomness(&fi, sizeof(fi));
+}
+
+
+static void hostapd_rx_action(struct hostapd_data *hapd,
+ struct rx_action *rx_action)
+{
+ struct rx_mgmt rx_mgmt;
+ u8 *buf;
+ struct ieee80211_hdr *hdr;
+
+ wpa_printf(MSG_DEBUG, "EVENT_RX_ACTION DA=" MACSTR " SA=" MACSTR
+ " BSSID=" MACSTR " category=%u",
+ MAC2STR(rx_action->da), MAC2STR(rx_action->sa),
+ MAC2STR(rx_action->bssid), rx_action->category);
+ wpa_hexdump(MSG_MSGDUMP, "Received action frame contents",
+ rx_action->data, rx_action->len);
+
+ buf = os_zalloc(24 + 1 + rx_action->len);
+ if (buf == NULL)
+ return;
+ hdr = (struct ieee80211_hdr *) buf;
+ hdr->frame_control = IEEE80211_FC(WLAN_FC_TYPE_MGMT,
+ WLAN_FC_STYPE_ACTION);
+ if (rx_action->category == WLAN_ACTION_SA_QUERY) {
+ /*
+ * Assume frame was protected; it would have been dropped if
+ * not.
+ */
+ hdr->frame_control |= host_to_le16(WLAN_FC_ISWEP);
+ }
+ os_memcpy(hdr->addr1, rx_action->da, ETH_ALEN);
+ os_memcpy(hdr->addr2, rx_action->sa, ETH_ALEN);
+ os_memcpy(hdr->addr3, rx_action->bssid, ETH_ALEN);
+ buf[24] = rx_action->category;
+ os_memcpy(buf + 24 + 1, rx_action->data, rx_action->len);
+ os_memset(&rx_mgmt, 0, sizeof(rx_mgmt));
+ rx_mgmt.frame = buf;
+ rx_mgmt.frame_len = 24 + 1 + rx_action->len;
+ hostapd_mgmt_rx(hapd, &rx_mgmt);
+ os_free(buf);
}
@@ -320,23 +656,6 @@ static void hostapd_mgmt_tx_cb(struct hostapd_data *hapd, const u8 *buf,
#endif /* NEED_AP_MLME */
-static int hostapd_probe_req_rx(struct hostapd_data *hapd, const u8 *sa,
- const u8 *ie, size_t ie_len)
-{
- size_t i;
- int ret = 0;
-
- for (i = 0; hapd->probereq_cb && i < hapd->num_probereq_cb; i++) {
- if (hapd->probereq_cb[i].cb(hapd->probereq_cb[i].ctx,
- sa, ie, ie_len) > 0) {
- ret = 1;
- break;
- }
- }
- return ret;
-}
-
-
static int hostapd_event_new_sta(struct hostapd_data *hapd, const u8 *addr)
{
struct sta_info *sta = ap_get_sta(hapd, addr);
@@ -362,12 +681,15 @@ static void hostapd_event_eapol_rx(struct hostapd_data *hapd, const u8 *src,
const u8 *data, size_t data_len)
{
struct hostapd_iface *iface = hapd->iface;
+ struct sta_info *sta;
size_t j;
for (j = 0; j < iface->num_bss; j++) {
- if (ap_get_sta(iface->bss[j], src)) {
- hapd = iface->bss[j];
- break;
+ if ((sta = ap_get_sta(iface->bss[j], src))) {
+ if (sta->flags & WLAN_STA_ASSOC) {
+ hapd = iface->bss[j];
+ break;
+ }
}
}
@@ -379,6 +701,23 @@ void wpa_supplicant_event(void *ctx, enum wpa_event_type event,
union wpa_event_data *data)
{
struct hostapd_data *hapd = ctx;
+#ifndef CONFIG_NO_STDOUT_DEBUG
+ int level = MSG_DEBUG;
+
+ if (event == EVENT_RX_MGMT && data->rx_mgmt.frame &&
+ data->rx_mgmt.frame_len >= 24) {
+ const struct ieee80211_hdr *hdr;
+ u16 fc;
+ hdr = (const struct ieee80211_hdr *) data->rx_mgmt.frame;
+ fc = le_to_host16(hdr->frame_control);
+ if (WLAN_FC_GET_TYPE(fc) == WLAN_FC_TYPE_MGMT &&
+ WLAN_FC_GET_STYPE(fc) == WLAN_FC_STYPE_BEACON)
+ level = MSG_EXCESSIVE;
+ }
+
+ wpa_dbg(hapd->msg_ctx, level, "Event %s (%d) received",
+ event_to_string(event), event);
+#endif /* CONFIG_NO_STDOUT_DEBUG */
switch (event) {
case EVENT_MICHAEL_MIC_FAILURE:
@@ -395,7 +734,7 @@ void wpa_supplicant_event(void *ctx, enum wpa_event_type event,
break;
#endif /* CONFIG_IEEE80211R */
case EVENT_WPS_BUTTON_PUSHED:
- hostapd_wps_button_pushed(hapd);
+ hostapd_wps_button_pushed(hapd, NULL);
break;
#ifdef NEED_AP_MLME
case EVENT_TX_STATUS:
@@ -414,18 +753,34 @@ void wpa_supplicant_event(void *ctx, enum wpa_event_type event,
break;
}
break;
+ case EVENT_EAPOL_TX_STATUS:
+ hostapd_eapol_tx_status(hapd, data->eapol_tx_status.dst,
+ data->eapol_tx_status.data,
+ data->eapol_tx_status.data_len,
+ data->eapol_tx_status.ack);
+ break;
+ case EVENT_DRIVER_CLIENT_POLL_OK:
+ hostapd_client_poll_ok(hapd, data->client_poll.addr);
+ break;
case EVENT_RX_FROM_UNKNOWN:
- hostapd_rx_from_unknown_sta(hapd, data->rx_from_unknown.frame,
- data->rx_from_unknown.len);
+ hostapd_rx_from_unknown_sta(hapd, data->rx_from_unknown.bssid,
+ data->rx_from_unknown.addr,
+ data->rx_from_unknown.wds);
break;
case EVENT_RX_MGMT:
hostapd_mgmt_rx(hapd, &data->rx_mgmt);
break;
#endif /* NEED_AP_MLME */
case EVENT_RX_PROBE_REQ:
+ if (data->rx_probe_req.sa == NULL ||
+ data->rx_probe_req.ie == NULL)
+ break;
hostapd_probe_req_rx(hapd, data->rx_probe_req.sa,
+ data->rx_probe_req.da,
+ data->rx_probe_req.bssid,
data->rx_probe_req.ie,
- data->rx_probe_req.ie_len);
+ data->rx_probe_req.ie_len,
+ data->rx_probe_req.ssi_signal);
break;
case EVENT_NEW_STA:
hostapd_event_new_sta(hapd, data->new_sta.addr);
@@ -438,7 +793,8 @@ void wpa_supplicant_event(void *ctx, enum wpa_event_type event,
case EVENT_ASSOC:
hostapd_notif_assoc(hapd, data->assoc_info.addr,
data->assoc_info.req_ies,
- data->assoc_info.req_ies_len);
+ data->assoc_info.req_ies_len,
+ data->assoc_info.reassoc);
break;
case EVENT_DISASSOC:
if (data)
@@ -448,6 +804,30 @@ void wpa_supplicant_event(void *ctx, enum wpa_event_type event,
if (data)
hostapd_notif_disassoc(hapd, data->deauth_info.addr);
break;
+ case EVENT_STATION_LOW_ACK:
+ if (!data)
+ break;
+ hostapd_event_sta_low_ack(hapd, data->low_ack.addr);
+ break;
+ case EVENT_RX_ACTION:
+ if (data->rx_action.da == NULL || data->rx_action.sa == NULL ||
+ data->rx_action.bssid == NULL)
+ break;
+#ifdef NEED_AP_MLME
+ hostapd_rx_action(hapd, &data->rx_action);
+#endif /* NEED_AP_MLME */
+ hostapd_action_rx(hapd, &data->rx_action);
+ break;
+ case EVENT_AUTH:
+ hostapd_notif_auth(hapd, &data->auth);
+ break;
+ case EVENT_CH_SWITCH:
+ if (!data)
+ break;
+ hostapd_event_ch_switch(hapd, data->ch_switch.freq,
+ data->ch_switch.ht_enabled,
+ data->ch_switch.ch_offset);
+ break;
default:
wpa_printf(MSG_DEBUG, "Unknown event %d", event);
break;
diff --git a/contrib/wpa/src/ap/eap_user_db.c b/contrib/wpa/src/ap/eap_user_db.c
new file mode 100644
index 0000000..79d50e5
--- /dev/null
+++ b/contrib/wpa/src/ap/eap_user_db.c
@@ -0,0 +1,270 @@
+/*
+ * hostapd / EAP user database
+ * Copyright (c) 2012, Jouni Malinen <j@w1.fi>
+ *
+ * This software may be distributed under the terms of the BSD license.
+ * See README for more details.
+ */
+
+#include "includes.h"
+#ifdef CONFIG_SQLITE
+#include <sqlite3.h>
+#endif /* CONFIG_SQLITE */
+
+#include "common.h"
+#include "eap_common/eap_wsc_common.h"
+#include "eap_server/eap_methods.h"
+#include "eap_server/eap.h"
+#include "ap_config.h"
+#include "hostapd.h"
+
+#ifdef CONFIG_SQLITE
+
+static void set_user_methods(struct hostapd_eap_user *user, const char *methods)
+{
+ char *buf, *start;
+ int num_methods;
+
+ buf = os_strdup(methods);
+ if (buf == NULL)
+ return;
+
+ os_memset(&user->methods, 0, sizeof(user->methods));
+ num_methods = 0;
+ start = buf;
+ while (*start) {
+ char *pos3 = os_strchr(start, ',');
+ if (pos3)
+ *pos3++ = '\0';
+ user->methods[num_methods].method =
+ eap_server_get_type(start,
+ &user->methods[num_methods].vendor);
+ if (user->methods[num_methods].vendor == EAP_VENDOR_IETF &&
+ user->methods[num_methods].method == EAP_TYPE_NONE) {
+ if (os_strcmp(start, "TTLS-PAP") == 0) {
+ user->ttls_auth |= EAP_TTLS_AUTH_PAP;
+ goto skip_eap;
+ }
+ if (os_strcmp(start, "TTLS-CHAP") == 0) {
+ user->ttls_auth |= EAP_TTLS_AUTH_CHAP;
+ goto skip_eap;
+ }
+ if (os_strcmp(start, "TTLS-MSCHAP") == 0) {
+ user->ttls_auth |= EAP_TTLS_AUTH_MSCHAP;
+ goto skip_eap;
+ }
+ if (os_strcmp(start, "TTLS-MSCHAPV2") == 0) {
+ user->ttls_auth |= EAP_TTLS_AUTH_MSCHAPV2;
+ goto skip_eap;
+ }
+ wpa_printf(MSG_INFO, "DB: Unsupported EAP type '%s'",
+ start);
+ os_free(buf);
+ return;
+ }
+
+ num_methods++;
+ if (num_methods >= EAP_MAX_METHODS)
+ break;
+ skip_eap:
+ if (pos3 == NULL)
+ break;
+ start = pos3;
+ }
+
+ os_free(buf);
+}
+
+
+static int get_user_cb(void *ctx, int argc, char *argv[], char *col[])
+{
+ struct hostapd_eap_user *user = ctx;
+ int i;
+
+ for (i = 0; i < argc; i++) {
+ if (os_strcmp(col[i], "password") == 0 && argv[i]) {
+ os_free(user->password);
+ user->password_len = os_strlen(argv[i]);
+ user->password = (u8 *) os_strdup(argv[i]);
+ user->next = (void *) 1;
+ } else if (os_strcmp(col[i], "methods") == 0 && argv[i]) {
+ set_user_methods(user, argv[i]);
+ }
+ }
+
+ return 0;
+}
+
+
+static int get_wildcard_cb(void *ctx, int argc, char *argv[], char *col[])
+{
+ struct hostapd_eap_user *user = ctx;
+ int i, id = -1, methods = -1;
+ size_t len;
+
+ for (i = 0; i < argc; i++) {
+ if (os_strcmp(col[i], "identity") == 0 && argv[i])
+ id = i;
+ else if (os_strcmp(col[i], "methods") == 0 && argv[i])
+ methods = i;
+ }
+
+ if (id < 0 || methods < 0)
+ return 0;
+
+ len = os_strlen(argv[id]);
+ if (len <= user->identity_len &&
+ os_memcmp(argv[id], user->identity, len) == 0 &&
+ (user->password == NULL || len > user->password_len)) {
+ os_free(user->password);
+ user->password_len = os_strlen(argv[id]);
+ user->password = (u8 *) os_strdup(argv[id]);
+ user->next = (void *) 1;
+ set_user_methods(user, argv[methods]);
+ }
+
+ return 0;
+}
+
+
+static const struct hostapd_eap_user *
+eap_user_sqlite_get(struct hostapd_data *hapd, const u8 *identity,
+ size_t identity_len, int phase2)
+{
+ sqlite3 *db;
+ struct hostapd_eap_user *user = NULL;
+ char id_str[256], cmd[300];
+ size_t i;
+
+ if (identity_len >= sizeof(id_str))
+ return NULL;
+ os_memcpy(id_str, identity, identity_len);
+ id_str[identity_len] = '\0';
+ for (i = 0; i < identity_len; i++) {
+ if (id_str[i] >= 'a' && id_str[i] <= 'z')
+ continue;
+ if (id_str[i] >= 'A' && id_str[i] <= 'Z')
+ continue;
+ if (id_str[i] >= '0' && id_str[i] <= '9')
+ continue;
+ if (id_str[i] == '-' || id_str[i] == '_' || id_str[i] == '.' ||
+ id_str[i] == ',' || id_str[i] == '@' || id_str[i] == '\\' ||
+ id_str[i] == '!' || id_str[i] == '#' || id_str[i] == '%' ||
+ id_str[i] == '=' || id_str[i] == ' ')
+ continue;
+ wpa_printf(MSG_INFO, "DB: Unsupported character in identity");
+ return NULL;
+ }
+
+ os_free(hapd->tmp_eap_user.identity);
+ os_free(hapd->tmp_eap_user.password);
+ os_memset(&hapd->tmp_eap_user, 0, sizeof(hapd->tmp_eap_user));
+ hapd->tmp_eap_user.phase2 = phase2;
+ hapd->tmp_eap_user.identity = os_zalloc(identity_len + 1);
+ if (hapd->tmp_eap_user.identity == NULL)
+ return NULL;
+ os_memcpy(hapd->tmp_eap_user.identity, identity, identity_len);
+
+ if (sqlite3_open(hapd->conf->eap_user_sqlite, &db)) {
+ wpa_printf(MSG_INFO, "DB: Failed to open database %s: %s",
+ hapd->conf->eap_user_sqlite, sqlite3_errmsg(db));
+ sqlite3_close(db);
+ return NULL;
+ }
+
+ os_snprintf(cmd, sizeof(cmd),
+ "SELECT password,methods FROM users WHERE "
+ "identity='%s' AND phase2=%d;", id_str, phase2);
+ wpa_printf(MSG_DEBUG, "DB: %s", cmd);
+ if (sqlite3_exec(db, cmd, get_user_cb, &hapd->tmp_eap_user, NULL) !=
+ SQLITE_OK) {
+ wpa_printf(MSG_DEBUG, "DB: Failed to complete SQL operation");
+ } else if (hapd->tmp_eap_user.next)
+ user = &hapd->tmp_eap_user;
+
+ if (user == NULL && !phase2) {
+ os_snprintf(cmd, sizeof(cmd),
+ "SELECT identity,methods FROM wildcards;");
+ wpa_printf(MSG_DEBUG, "DB: %s", cmd);
+ if (sqlite3_exec(db, cmd, get_wildcard_cb, &hapd->tmp_eap_user,
+ NULL) != SQLITE_OK) {
+ wpa_printf(MSG_DEBUG, "DB: Failed to complete SQL "
+ "operation");
+ } else if (hapd->tmp_eap_user.next) {
+ user = &hapd->tmp_eap_user;
+ os_free(user->identity);
+ user->identity = user->password;
+ user->identity_len = user->password_len;
+ user->password = NULL;
+ user->password_len = 0;
+ }
+ }
+
+ sqlite3_close(db);
+
+ return user;
+}
+
+#endif /* CONFIG_SQLITE */
+
+
+const struct hostapd_eap_user *
+hostapd_get_eap_user(struct hostapd_data *hapd, const u8 *identity,
+ size_t identity_len, int phase2)
+{
+ const struct hostapd_bss_config *conf = hapd->conf;
+ struct hostapd_eap_user *user = conf->eap_user;
+
+#ifdef CONFIG_WPS
+ if (conf->wps_state && identity_len == WSC_ID_ENROLLEE_LEN &&
+ os_memcmp(identity, WSC_ID_ENROLLEE, WSC_ID_ENROLLEE_LEN) == 0) {
+ static struct hostapd_eap_user wsc_enrollee;
+ os_memset(&wsc_enrollee, 0, sizeof(wsc_enrollee));
+ wsc_enrollee.methods[0].method = eap_server_get_type(
+ "WSC", &wsc_enrollee.methods[0].vendor);
+ return &wsc_enrollee;
+ }
+
+ if (conf->wps_state && identity_len == WSC_ID_REGISTRAR_LEN &&
+ os_memcmp(identity, WSC_ID_REGISTRAR, WSC_ID_REGISTRAR_LEN) == 0) {
+ static struct hostapd_eap_user wsc_registrar;
+ os_memset(&wsc_registrar, 0, sizeof(wsc_registrar));
+ wsc_registrar.methods[0].method = eap_server_get_type(
+ "WSC", &wsc_registrar.methods[0].vendor);
+ wsc_registrar.password = (u8 *) conf->ap_pin;
+ wsc_registrar.password_len = conf->ap_pin ?
+ os_strlen(conf->ap_pin) : 0;
+ return &wsc_registrar;
+ }
+#endif /* CONFIG_WPS */
+
+ while (user) {
+ if (!phase2 && user->identity == NULL) {
+ /* Wildcard match */
+ break;
+ }
+
+ if (user->phase2 == !!phase2 && user->wildcard_prefix &&
+ identity_len >= user->identity_len &&
+ os_memcmp(user->identity, identity, user->identity_len) ==
+ 0) {
+ /* Wildcard prefix match */
+ break;
+ }
+
+ if (user->phase2 == !!phase2 &&
+ user->identity_len == identity_len &&
+ os_memcmp(user->identity, identity, identity_len) == 0)
+ break;
+ user = user->next;
+ }
+
+#ifdef CONFIG_SQLITE
+ if (user == NULL && conf->eap_user_sqlite) {
+ return eap_user_sqlite_get(hapd, identity, identity_len,
+ phase2);
+ }
+#endif /* CONFIG_SQLITE */
+
+ return user;
+}
diff --git a/contrib/wpa/src/ap/gas_serv.c b/contrib/wpa/src/ap/gas_serv.c
new file mode 100644
index 0000000..851c183
--- /dev/null
+++ b/contrib/wpa/src/ap/gas_serv.c
@@ -0,0 +1,1172 @@
+/*
+ * Generic advertisement service (GAS) server
+ * Copyright (c) 2011-2012, Qualcomm Atheros, Inc.
+ *
+ * This software may be distributed under the terms of the BSD license.
+ * See README for more details.
+ */
+
+#include "includes.h"
+
+#include "common.h"
+#include "common/ieee802_11_defs.h"
+#include "common/gas.h"
+#include "utils/eloop.h"
+#include "hostapd.h"
+#include "ap_config.h"
+#include "ap_drv_ops.h"
+#include "sta_info.h"
+#include "gas_serv.h"
+
+
+static struct gas_dialog_info *
+gas_dialog_create(struct hostapd_data *hapd, const u8 *addr, u8 dialog_token)
+{
+ struct sta_info *sta;
+ struct gas_dialog_info *dia = NULL;
+ int i, j;
+
+ sta = ap_get_sta(hapd, addr);
+ if (!sta) {
+ /*
+ * We need a STA entry to be able to maintain state for
+ * the GAS query.
+ */
+ wpa_printf(MSG_DEBUG, "ANQP: Add a temporary STA entry for "
+ "GAS query");
+ sta = ap_sta_add(hapd, addr);
+ if (!sta) {
+ wpa_printf(MSG_DEBUG, "Failed to add STA " MACSTR
+ " for GAS query", MAC2STR(addr));
+ return NULL;
+ }
+ sta->flags |= WLAN_STA_GAS;
+ /*
+ * The default inactivity is 300 seconds. We don't need
+ * it to be that long.
+ */
+ ap_sta_session_timeout(hapd, sta, 5);
+ }
+
+ if (sta->gas_dialog == NULL) {
+ sta->gas_dialog = os_zalloc(GAS_DIALOG_MAX *
+ sizeof(struct gas_dialog_info));
+ if (sta->gas_dialog == NULL)
+ return NULL;
+ }
+
+ for (i = sta->gas_dialog_next, j = 0; j < GAS_DIALOG_MAX; i++, j++) {
+ if (i == GAS_DIALOG_MAX)
+ i = 0;
+ if (sta->gas_dialog[i].valid)
+ continue;
+ dia = &sta->gas_dialog[i];
+ dia->valid = 1;
+ dia->index = i;
+ dia->dialog_token = dialog_token;
+ sta->gas_dialog_next = (++i == GAS_DIALOG_MAX) ? 0 : i;
+ return dia;
+ }
+
+ wpa_msg(hapd->msg_ctx, MSG_ERROR, "ANQP: Could not create dialog for "
+ MACSTR " dialog_token %u. Consider increasing "
+ "GAS_DIALOG_MAX.", MAC2STR(addr), dialog_token);
+
+ return NULL;
+}
+
+
+struct gas_dialog_info *
+gas_serv_dialog_find(struct hostapd_data *hapd, const u8 *addr,
+ u8 dialog_token)
+{
+ struct sta_info *sta;
+ int i;
+
+ sta = ap_get_sta(hapd, addr);
+ if (!sta) {
+ wpa_printf(MSG_DEBUG, "ANQP: could not find STA " MACSTR,
+ MAC2STR(addr));
+ return NULL;
+ }
+ for (i = 0; sta->gas_dialog && i < GAS_DIALOG_MAX; i++) {
+ if (sta->gas_dialog[i].dialog_token != dialog_token ||
+ !sta->gas_dialog[i].valid)
+ continue;
+ return &sta->gas_dialog[i];
+ }
+ wpa_printf(MSG_DEBUG, "ANQP: Could not find dialog for "
+ MACSTR " dialog_token %u", MAC2STR(addr), dialog_token);
+ return NULL;
+}
+
+
+void gas_serv_dialog_clear(struct gas_dialog_info *dia)
+{
+ wpabuf_free(dia->sd_resp);
+ os_memset(dia, 0, sizeof(*dia));
+}
+
+
+static void gas_serv_free_dialogs(struct hostapd_data *hapd,
+ const u8 *sta_addr)
+{
+ struct sta_info *sta;
+ int i;
+
+ sta = ap_get_sta(hapd, sta_addr);
+ if (sta == NULL || sta->gas_dialog == NULL)
+ return;
+
+ for (i = 0; i < GAS_DIALOG_MAX; i++) {
+ if (sta->gas_dialog[i].valid)
+ return;
+ }
+
+ os_free(sta->gas_dialog);
+ sta->gas_dialog = NULL;
+}
+
+
+#ifdef CONFIG_HS20
+static void anqp_add_hs_capab_list(struct hostapd_data *hapd,
+ struct wpabuf *buf)
+{
+ u8 *len;
+
+ len = gas_anqp_add_element(buf, ANQP_VENDOR_SPECIFIC);
+ wpabuf_put_be24(buf, OUI_WFA);
+ wpabuf_put_u8(buf, HS20_ANQP_OUI_TYPE);
+ wpabuf_put_u8(buf, HS20_STYPE_CAPABILITY_LIST);
+ wpabuf_put_u8(buf, 0); /* Reserved */
+ wpabuf_put_u8(buf, HS20_STYPE_CAPABILITY_LIST);
+ if (hapd->conf->hs20_oper_friendly_name)
+ wpabuf_put_u8(buf, HS20_STYPE_OPERATOR_FRIENDLY_NAME);
+ if (hapd->conf->hs20_wan_metrics)
+ wpabuf_put_u8(buf, HS20_STYPE_WAN_METRICS);
+ if (hapd->conf->hs20_connection_capability)
+ wpabuf_put_u8(buf, HS20_STYPE_CONNECTION_CAPABILITY);
+ if (hapd->conf->nai_realm_data)
+ wpabuf_put_u8(buf, HS20_STYPE_NAI_HOME_REALM_QUERY);
+ if (hapd->conf->hs20_operating_class)
+ wpabuf_put_u8(buf, HS20_STYPE_OPERATING_CLASS);
+ gas_anqp_set_element_len(buf, len);
+}
+#endif /* CONFIG_HS20 */
+
+
+static void anqp_add_capab_list(struct hostapd_data *hapd,
+ struct wpabuf *buf)
+{
+ u8 *len;
+
+ len = gas_anqp_add_element(buf, ANQP_CAPABILITY_LIST);
+ wpabuf_put_le16(buf, ANQP_CAPABILITY_LIST);
+ if (hapd->conf->venue_name)
+ wpabuf_put_le16(buf, ANQP_VENUE_NAME);
+ if (hapd->conf->network_auth_type)
+ wpabuf_put_le16(buf, ANQP_NETWORK_AUTH_TYPE);
+ if (hapd->conf->roaming_consortium)
+ wpabuf_put_le16(buf, ANQP_ROAMING_CONSORTIUM);
+ if (hapd->conf->ipaddr_type_configured)
+ wpabuf_put_le16(buf, ANQP_IP_ADDR_TYPE_AVAILABILITY);
+ if (hapd->conf->nai_realm_data)
+ wpabuf_put_le16(buf, ANQP_NAI_REALM);
+ if (hapd->conf->anqp_3gpp_cell_net)
+ wpabuf_put_le16(buf, ANQP_3GPP_CELLULAR_NETWORK);
+ if (hapd->conf->domain_name)
+ wpabuf_put_le16(buf, ANQP_DOMAIN_NAME);
+#ifdef CONFIG_HS20
+ anqp_add_hs_capab_list(hapd, buf);
+#endif /* CONFIG_HS20 */
+ gas_anqp_set_element_len(buf, len);
+}
+
+
+static void anqp_add_venue_name(struct hostapd_data *hapd, struct wpabuf *buf)
+{
+ if (hapd->conf->venue_name) {
+ u8 *len;
+ unsigned int i;
+ len = gas_anqp_add_element(buf, ANQP_VENUE_NAME);
+ wpabuf_put_u8(buf, hapd->conf->venue_group);
+ wpabuf_put_u8(buf, hapd->conf->venue_type);
+ for (i = 0; i < hapd->conf->venue_name_count; i++) {
+ struct hostapd_lang_string *vn;
+ vn = &hapd->conf->venue_name[i];
+ wpabuf_put_u8(buf, 3 + vn->name_len);
+ wpabuf_put_data(buf, vn->lang, 3);
+ wpabuf_put_data(buf, vn->name, vn->name_len);
+ }
+ gas_anqp_set_element_len(buf, len);
+ }
+}
+
+
+static void anqp_add_network_auth_type(struct hostapd_data *hapd,
+ struct wpabuf *buf)
+{
+ if (hapd->conf->network_auth_type) {
+ wpabuf_put_le16(buf, ANQP_NETWORK_AUTH_TYPE);
+ wpabuf_put_le16(buf, hapd->conf->network_auth_type_len);
+ wpabuf_put_data(buf, hapd->conf->network_auth_type,
+ hapd->conf->network_auth_type_len);
+ }
+}
+
+
+static void anqp_add_roaming_consortium(struct hostapd_data *hapd,
+ struct wpabuf *buf)
+{
+ unsigned int i;
+ u8 *len;
+
+ len = gas_anqp_add_element(buf, ANQP_ROAMING_CONSORTIUM);
+ for (i = 0; i < hapd->conf->roaming_consortium_count; i++) {
+ struct hostapd_roaming_consortium *rc;
+ rc = &hapd->conf->roaming_consortium[i];
+ wpabuf_put_u8(buf, rc->len);
+ wpabuf_put_data(buf, rc->oi, rc->len);
+ }
+ gas_anqp_set_element_len(buf, len);
+}
+
+
+static void anqp_add_ip_addr_type_availability(struct hostapd_data *hapd,
+ struct wpabuf *buf)
+{
+ if (hapd->conf->ipaddr_type_configured) {
+ wpabuf_put_le16(buf, ANQP_IP_ADDR_TYPE_AVAILABILITY);
+ wpabuf_put_le16(buf, 1);
+ wpabuf_put_u8(buf, hapd->conf->ipaddr_type_availability);
+ }
+}
+
+
+static void anqp_add_nai_realm_eap(struct wpabuf *buf,
+ struct hostapd_nai_realm_data *realm)
+{
+ unsigned int i, j;
+
+ wpabuf_put_u8(buf, realm->eap_method_count);
+
+ for (i = 0; i < realm->eap_method_count; i++) {
+ struct hostapd_nai_realm_eap *eap = &realm->eap_method[i];
+ wpabuf_put_u8(buf, 2 + (3 * eap->num_auths));
+ wpabuf_put_u8(buf, eap->eap_method);
+ wpabuf_put_u8(buf, eap->num_auths);
+ for (j = 0; j < eap->num_auths; j++) {
+ wpabuf_put_u8(buf, eap->auth_id[j]);
+ wpabuf_put_u8(buf, 1);
+ wpabuf_put_u8(buf, eap->auth_val[j]);
+ }
+ }
+}
+
+
+static void anqp_add_nai_realm_data(struct wpabuf *buf,
+ struct hostapd_nai_realm_data *realm,
+ unsigned int realm_idx)
+{
+ u8 *realm_data_len;
+
+ wpa_printf(MSG_DEBUG, "realm=%s, len=%d", realm->realm[realm_idx],
+ (int) os_strlen(realm->realm[realm_idx]));
+ realm_data_len = wpabuf_put(buf, 2);
+ wpabuf_put_u8(buf, realm->encoding);
+ wpabuf_put_u8(buf, os_strlen(realm->realm[realm_idx]));
+ wpabuf_put_str(buf, realm->realm[realm_idx]);
+ anqp_add_nai_realm_eap(buf, realm);
+ gas_anqp_set_element_len(buf, realm_data_len);
+}
+
+
+static int hs20_add_nai_home_realm_matches(struct hostapd_data *hapd,
+ struct wpabuf *buf,
+ const u8 *home_realm,
+ size_t home_realm_len)
+{
+ unsigned int i, j, k;
+ u8 num_realms, num_matching = 0, encoding, realm_len, *realm_list_len;
+ struct hostapd_nai_realm_data *realm;
+ const u8 *pos, *realm_name, *end;
+ struct {
+ unsigned int realm_data_idx;
+ unsigned int realm_idx;
+ } matches[10];
+
+ pos = home_realm;
+ end = pos + home_realm_len;
+ if (pos + 1 > end) {
+ wpa_hexdump(MSG_DEBUG, "Too short NAI Home Realm Query",
+ home_realm, home_realm_len);
+ return -1;
+ }
+ num_realms = *pos++;
+
+ for (i = 0; i < num_realms && num_matching < 10; i++) {
+ if (pos + 2 > end) {
+ wpa_hexdump(MSG_DEBUG,
+ "Truncated NAI Home Realm Query",
+ home_realm, home_realm_len);
+ return -1;
+ }
+ encoding = *pos++;
+ realm_len = *pos++;
+ if (pos + realm_len > end) {
+ wpa_hexdump(MSG_DEBUG,
+ "Truncated NAI Home Realm Query",
+ home_realm, home_realm_len);
+ return -1;
+ }
+ realm_name = pos;
+ for (j = 0; j < hapd->conf->nai_realm_count &&
+ num_matching < 10; j++) {
+ const u8 *rpos, *rend;
+ realm = &hapd->conf->nai_realm_data[j];
+ if (encoding != realm->encoding)
+ continue;
+
+ rpos = realm_name;
+ while (rpos < realm_name + realm_len &&
+ num_matching < 10) {
+ for (rend = rpos;
+ rend < realm_name + realm_len; rend++) {
+ if (*rend == ';')
+ break;
+ }
+ for (k = 0; k < MAX_NAI_REALMS &&
+ realm->realm[k] &&
+ num_matching < 10; k++) {
+ if ((int) os_strlen(realm->realm[k]) !=
+ rend - rpos ||
+ os_strncmp((char *) rpos,
+ realm->realm[k],
+ rend - rpos) != 0)
+ continue;
+ matches[num_matching].realm_data_idx =
+ j;
+ matches[num_matching].realm_idx = k;
+ num_matching++;
+ }
+ rpos = rend + 1;
+ }
+ }
+ pos += realm_len;
+ }
+
+ realm_list_len = gas_anqp_add_element(buf, ANQP_NAI_REALM);
+ wpabuf_put_le16(buf, num_matching);
+
+ /*
+ * There are two ways to format. 1. each realm in a NAI Realm Data unit
+ * 2. all realms that share the same EAP methods in a NAI Realm Data
+ * unit. The first format is likely to be bigger in size than the
+ * second, but may be easier to parse and process by the receiver.
+ */
+ for (i = 0; i < num_matching; i++) {
+ wpa_printf(MSG_DEBUG, "realm_idx %d, realm_data_idx %d",
+ matches[i].realm_data_idx, matches[i].realm_idx);
+ realm = &hapd->conf->nai_realm_data[matches[i].realm_data_idx];
+ anqp_add_nai_realm_data(buf, realm, matches[i].realm_idx);
+ }
+ gas_anqp_set_element_len(buf, realm_list_len);
+ return 0;
+}
+
+
+static void anqp_add_nai_realm(struct hostapd_data *hapd, struct wpabuf *buf,
+ const u8 *home_realm, size_t home_realm_len,
+ int nai_realm, int nai_home_realm)
+{
+ if (nai_realm && hapd->conf->nai_realm_data) {
+ u8 *len;
+ unsigned int i, j;
+ len = gas_anqp_add_element(buf, ANQP_NAI_REALM);
+ wpabuf_put_le16(buf, hapd->conf->nai_realm_count);
+ for (i = 0; i < hapd->conf->nai_realm_count; i++) {
+ u8 *realm_data_len, *realm_len;
+ struct hostapd_nai_realm_data *realm;
+
+ realm = &hapd->conf->nai_realm_data[i];
+ realm_data_len = wpabuf_put(buf, 2);
+ wpabuf_put_u8(buf, realm->encoding);
+ realm_len = wpabuf_put(buf, 1);
+ for (j = 0; realm->realm[j]; j++) {
+ if (j > 0)
+ wpabuf_put_u8(buf, ';');
+ wpabuf_put_str(buf, realm->realm[j]);
+ }
+ *realm_len = (u8 *) wpabuf_put(buf, 0) - realm_len - 1;
+ anqp_add_nai_realm_eap(buf, realm);
+ gas_anqp_set_element_len(buf, realm_data_len);
+ }
+ gas_anqp_set_element_len(buf, len);
+ } else if (nai_home_realm && hapd->conf->nai_realm_data) {
+ hs20_add_nai_home_realm_matches(hapd, buf, home_realm,
+ home_realm_len);
+ }
+}
+
+
+static void anqp_add_3gpp_cellular_network(struct hostapd_data *hapd,
+ struct wpabuf *buf)
+{
+ if (hapd->conf->anqp_3gpp_cell_net) {
+ wpabuf_put_le16(buf, ANQP_3GPP_CELLULAR_NETWORK);
+ wpabuf_put_le16(buf,
+ hapd->conf->anqp_3gpp_cell_net_len);
+ wpabuf_put_data(buf, hapd->conf->anqp_3gpp_cell_net,
+ hapd->conf->anqp_3gpp_cell_net_len);
+ }
+}
+
+
+static void anqp_add_domain_name(struct hostapd_data *hapd, struct wpabuf *buf)
+{
+ if (hapd->conf->domain_name) {
+ wpabuf_put_le16(buf, ANQP_DOMAIN_NAME);
+ wpabuf_put_le16(buf, hapd->conf->domain_name_len);
+ wpabuf_put_data(buf, hapd->conf->domain_name,
+ hapd->conf->domain_name_len);
+ }
+}
+
+
+#ifdef CONFIG_HS20
+
+static void anqp_add_operator_friendly_name(struct hostapd_data *hapd,
+ struct wpabuf *buf)
+{
+ if (hapd->conf->hs20_oper_friendly_name) {
+ u8 *len;
+ unsigned int i;
+ len = gas_anqp_add_element(buf, ANQP_VENDOR_SPECIFIC);
+ wpabuf_put_be24(buf, OUI_WFA);
+ wpabuf_put_u8(buf, HS20_ANQP_OUI_TYPE);
+ wpabuf_put_u8(buf, HS20_STYPE_OPERATOR_FRIENDLY_NAME);
+ wpabuf_put_u8(buf, 0); /* Reserved */
+ for (i = 0; i < hapd->conf->hs20_oper_friendly_name_count; i++)
+ {
+ struct hostapd_lang_string *vn;
+ vn = &hapd->conf->hs20_oper_friendly_name[i];
+ wpabuf_put_u8(buf, 3 + vn->name_len);
+ wpabuf_put_data(buf, vn->lang, 3);
+ wpabuf_put_data(buf, vn->name, vn->name_len);
+ }
+ gas_anqp_set_element_len(buf, len);
+ }
+}
+
+
+static void anqp_add_wan_metrics(struct hostapd_data *hapd,
+ struct wpabuf *buf)
+{
+ if (hapd->conf->hs20_wan_metrics) {
+ u8 *len = gas_anqp_add_element(buf, ANQP_VENDOR_SPECIFIC);
+ wpabuf_put_be24(buf, OUI_WFA);
+ wpabuf_put_u8(buf, HS20_ANQP_OUI_TYPE);
+ wpabuf_put_u8(buf, HS20_STYPE_WAN_METRICS);
+ wpabuf_put_u8(buf, 0); /* Reserved */
+ wpabuf_put_data(buf, hapd->conf->hs20_wan_metrics, 13);
+ gas_anqp_set_element_len(buf, len);
+ }
+}
+
+
+static void anqp_add_connection_capability(struct hostapd_data *hapd,
+ struct wpabuf *buf)
+{
+ if (hapd->conf->hs20_connection_capability) {
+ u8 *len = gas_anqp_add_element(buf, ANQP_VENDOR_SPECIFIC);
+ wpabuf_put_be24(buf, OUI_WFA);
+ wpabuf_put_u8(buf, HS20_ANQP_OUI_TYPE);
+ wpabuf_put_u8(buf, HS20_STYPE_CONNECTION_CAPABILITY);
+ wpabuf_put_u8(buf, 0); /* Reserved */
+ wpabuf_put_data(buf, hapd->conf->hs20_connection_capability,
+ hapd->conf->hs20_connection_capability_len);
+ gas_anqp_set_element_len(buf, len);
+ }
+}
+
+
+static void anqp_add_operating_class(struct hostapd_data *hapd,
+ struct wpabuf *buf)
+{
+ if (hapd->conf->hs20_operating_class) {
+ u8 *len = gas_anqp_add_element(buf, ANQP_VENDOR_SPECIFIC);
+ wpabuf_put_be24(buf, OUI_WFA);
+ wpabuf_put_u8(buf, HS20_ANQP_OUI_TYPE);
+ wpabuf_put_u8(buf, HS20_STYPE_OPERATING_CLASS);
+ wpabuf_put_u8(buf, 0); /* Reserved */
+ wpabuf_put_data(buf, hapd->conf->hs20_operating_class,
+ hapd->conf->hs20_operating_class_len);
+ gas_anqp_set_element_len(buf, len);
+ }
+}
+
+#endif /* CONFIG_HS20 */
+
+
+static struct wpabuf *
+gas_serv_build_gas_resp_payload(struct hostapd_data *hapd,
+ unsigned int request,
+ struct gas_dialog_info *di,
+ const u8 *home_realm, size_t home_realm_len)
+{
+ struct wpabuf *buf;
+
+ buf = wpabuf_alloc(1400);
+ if (buf == NULL)
+ return NULL;
+
+ if (request & ANQP_REQ_CAPABILITY_LIST)
+ anqp_add_capab_list(hapd, buf);
+ if (request & ANQP_REQ_VENUE_NAME)
+ anqp_add_venue_name(hapd, buf);
+ if (request & ANQP_REQ_NETWORK_AUTH_TYPE)
+ anqp_add_network_auth_type(hapd, buf);
+ if (request & ANQP_REQ_ROAMING_CONSORTIUM)
+ anqp_add_roaming_consortium(hapd, buf);
+ if (request & ANQP_REQ_IP_ADDR_TYPE_AVAILABILITY)
+ anqp_add_ip_addr_type_availability(hapd, buf);
+ if (request & (ANQP_REQ_NAI_REALM | ANQP_REQ_NAI_HOME_REALM))
+ anqp_add_nai_realm(hapd, buf, home_realm, home_realm_len,
+ request & ANQP_REQ_NAI_REALM,
+ request & ANQP_REQ_NAI_HOME_REALM);
+ if (request & ANQP_REQ_3GPP_CELLULAR_NETWORK)
+ anqp_add_3gpp_cellular_network(hapd, buf);
+ if (request & ANQP_REQ_DOMAIN_NAME)
+ anqp_add_domain_name(hapd, buf);
+
+#ifdef CONFIG_HS20
+ if (request & ANQP_REQ_HS_CAPABILITY_LIST)
+ anqp_add_hs_capab_list(hapd, buf);
+ if (request & ANQP_REQ_OPERATOR_FRIENDLY_NAME)
+ anqp_add_operator_friendly_name(hapd, buf);
+ if (request & ANQP_REQ_WAN_METRICS)
+ anqp_add_wan_metrics(hapd, buf);
+ if (request & ANQP_REQ_CONNECTION_CAPABILITY)
+ anqp_add_connection_capability(hapd, buf);
+ if (request & ANQP_REQ_OPERATING_CLASS)
+ anqp_add_operating_class(hapd, buf);
+#endif /* CONFIG_HS20 */
+
+ return buf;
+}
+
+
+static void gas_serv_clear_cached_ies(void *eloop_data, void *user_ctx)
+{
+ struct gas_dialog_info *dia = eloop_data;
+
+ wpa_printf(MSG_DEBUG, "GAS: Timeout triggered, clearing dialog for "
+ "dialog token %d", dia->dialog_token);
+
+ gas_serv_dialog_clear(dia);
+}
+
+
+struct anqp_query_info {
+ unsigned int request;
+ unsigned int remote_request;
+ const u8 *home_realm_query;
+ size_t home_realm_query_len;
+ u16 remote_delay;
+};
+
+
+static void set_anqp_req(unsigned int bit, const char *name, int local,
+ unsigned int remote, u16 remote_delay,
+ struct anqp_query_info *qi)
+{
+ qi->request |= bit;
+ if (local) {
+ wpa_printf(MSG_DEBUG, "ANQP: %s (local)", name);
+ } else if (bit & remote) {
+ wpa_printf(MSG_DEBUG, "ANQP: %s (remote)", name);
+ qi->remote_request |= bit;
+ if (remote_delay > qi->remote_delay)
+ qi->remote_delay = remote_delay;
+ } else {
+ wpa_printf(MSG_DEBUG, "ANQP: %s not available", name);
+ }
+}
+
+
+static void rx_anqp_query_list_id(struct hostapd_data *hapd, u16 info_id,
+ struct anqp_query_info *qi)
+{
+ switch (info_id) {
+ case ANQP_CAPABILITY_LIST:
+ set_anqp_req(ANQP_REQ_CAPABILITY_LIST, "Capability List", 1, 0,
+ 0, qi);
+ break;
+ case ANQP_VENUE_NAME:
+ set_anqp_req(ANQP_REQ_VENUE_NAME, "Venue Name",
+ hapd->conf->venue_name != NULL, 0, 0, qi);
+ break;
+ case ANQP_NETWORK_AUTH_TYPE:
+ set_anqp_req(ANQP_REQ_NETWORK_AUTH_TYPE, "Network Auth Type",
+ hapd->conf->network_auth_type != NULL,
+ 0, 0, qi);
+ break;
+ case ANQP_ROAMING_CONSORTIUM:
+ set_anqp_req(ANQP_REQ_ROAMING_CONSORTIUM, "Roaming Consortium",
+ hapd->conf->roaming_consortium != NULL, 0, 0, qi);
+ break;
+ case ANQP_IP_ADDR_TYPE_AVAILABILITY:
+ set_anqp_req(ANQP_REQ_IP_ADDR_TYPE_AVAILABILITY,
+ "IP Addr Type Availability",
+ hapd->conf->ipaddr_type_configured,
+ 0, 0, qi);
+ break;
+ case ANQP_NAI_REALM:
+ set_anqp_req(ANQP_REQ_NAI_REALM, "NAI Realm",
+ hapd->conf->nai_realm_data != NULL,
+ 0, 0, qi);
+ break;
+ case ANQP_3GPP_CELLULAR_NETWORK:
+ set_anqp_req(ANQP_REQ_3GPP_CELLULAR_NETWORK,
+ "3GPP Cellular Network",
+ hapd->conf->anqp_3gpp_cell_net != NULL,
+ 0, 0, qi);
+ break;
+ case ANQP_DOMAIN_NAME:
+ set_anqp_req(ANQP_REQ_DOMAIN_NAME, "Domain Name",
+ hapd->conf->domain_name != NULL,
+ 0, 0, qi);
+ break;
+ default:
+ wpa_printf(MSG_DEBUG, "ANQP: Unsupported Info Id %u",
+ info_id);
+ break;
+ }
+}
+
+
+static void rx_anqp_query_list(struct hostapd_data *hapd,
+ const u8 *pos, const u8 *end,
+ struct anqp_query_info *qi)
+{
+ wpa_printf(MSG_DEBUG, "ANQP: %u Info IDs requested in Query list",
+ (unsigned int) (end - pos) / 2);
+
+ while (pos + 2 <= end) {
+ rx_anqp_query_list_id(hapd, WPA_GET_LE16(pos), qi);
+ pos += 2;
+ }
+}
+
+
+#ifdef CONFIG_HS20
+
+static void rx_anqp_hs_query_list(struct hostapd_data *hapd, u8 subtype,
+ struct anqp_query_info *qi)
+{
+ switch (subtype) {
+ case HS20_STYPE_CAPABILITY_LIST:
+ set_anqp_req(ANQP_REQ_HS_CAPABILITY_LIST, "HS Capability List",
+ 1, 0, 0, qi);
+ break;
+ case HS20_STYPE_OPERATOR_FRIENDLY_NAME:
+ set_anqp_req(ANQP_REQ_OPERATOR_FRIENDLY_NAME,
+ "Operator Friendly Name",
+ hapd->conf->hs20_oper_friendly_name != NULL,
+ 0, 0, qi);
+ break;
+ case HS20_STYPE_WAN_METRICS:
+ set_anqp_req(ANQP_REQ_WAN_METRICS, "WAN Metrics",
+ hapd->conf->hs20_wan_metrics != NULL,
+ 0, 0, qi);
+ break;
+ case HS20_STYPE_CONNECTION_CAPABILITY:
+ set_anqp_req(ANQP_REQ_CONNECTION_CAPABILITY,
+ "Connection Capability",
+ hapd->conf->hs20_connection_capability != NULL,
+ 0, 0, qi);
+ break;
+ case HS20_STYPE_OPERATING_CLASS:
+ set_anqp_req(ANQP_REQ_OPERATING_CLASS, "Operating Class",
+ hapd->conf->hs20_operating_class != NULL,
+ 0, 0, qi);
+ break;
+ default:
+ wpa_printf(MSG_DEBUG, "ANQP: Unsupported HS 2.0 subtype %u",
+ subtype);
+ break;
+ }
+}
+
+
+static void rx_anqp_hs_nai_home_realm(struct hostapd_data *hapd,
+ const u8 *pos, const u8 *end,
+ struct anqp_query_info *qi)
+{
+ qi->request |= ANQP_REQ_NAI_HOME_REALM;
+ qi->home_realm_query = pos;
+ qi->home_realm_query_len = end - pos;
+ if (hapd->conf->nai_realm_data != NULL) {
+ wpa_printf(MSG_DEBUG, "ANQP: HS 2.0 NAI Home Realm Query "
+ "(local)");
+ } else {
+ wpa_printf(MSG_DEBUG, "ANQP: HS 2.0 NAI Home Realm Query not "
+ "available");
+ }
+}
+
+
+static void rx_anqp_vendor_specific(struct hostapd_data *hapd,
+ const u8 *pos, const u8 *end,
+ struct anqp_query_info *qi)
+{
+ u32 oui;
+ u8 subtype;
+
+ if (pos + 4 > end) {
+ wpa_printf(MSG_DEBUG, "ANQP: Too short vendor specific ANQP "
+ "Query element");
+ return;
+ }
+
+ oui = WPA_GET_BE24(pos);
+ pos += 3;
+ if (oui != OUI_WFA) {
+ wpa_printf(MSG_DEBUG, "ANQP: Unsupported vendor OUI %06x",
+ oui);
+ return;
+ }
+
+ if (*pos != HS20_ANQP_OUI_TYPE) {
+ wpa_printf(MSG_DEBUG, "ANQP: Unsupported WFA vendor type %u",
+ *pos);
+ return;
+ }
+ pos++;
+
+ if (pos + 1 >= end)
+ return;
+
+ subtype = *pos++;
+ pos++; /* Reserved */
+ switch (subtype) {
+ case HS20_STYPE_QUERY_LIST:
+ wpa_printf(MSG_DEBUG, "ANQP: HS 2.0 Query List");
+ while (pos < end) {
+ rx_anqp_hs_query_list(hapd, *pos, qi);
+ pos++;
+ }
+ break;
+ case HS20_STYPE_NAI_HOME_REALM_QUERY:
+ rx_anqp_hs_nai_home_realm(hapd, pos, end, qi);
+ break;
+ default:
+ wpa_printf(MSG_DEBUG, "ANQP: Unsupported HS 2.0 query subtype "
+ "%u", subtype);
+ break;
+ }
+}
+
+#endif /* CONFIG_HS20 */
+
+
+static void gas_serv_req_local_processing(struct hostapd_data *hapd,
+ const u8 *sa, u8 dialog_token,
+ struct anqp_query_info *qi)
+{
+ struct wpabuf *buf, *tx_buf;
+
+ buf = gas_serv_build_gas_resp_payload(hapd, qi->request, NULL,
+ qi->home_realm_query,
+ qi->home_realm_query_len);
+ wpa_hexdump_buf(MSG_MSGDUMP, "ANQP: Locally generated ANQP responses",
+ buf);
+ if (!buf)
+ return;
+
+ if (wpabuf_len(buf) > hapd->gas_frag_limit ||
+ hapd->conf->gas_comeback_delay) {
+ struct gas_dialog_info *di;
+ u16 comeback_delay = 1;
+
+ if (hapd->conf->gas_comeback_delay) {
+ /* Testing - allow overriding of the delay value */
+ comeback_delay = hapd->conf->gas_comeback_delay;
+ }
+
+ wpa_printf(MSG_DEBUG, "ANQP: Too long response to fit in "
+ "initial response - use GAS comeback");
+ di = gas_dialog_create(hapd, sa, dialog_token);
+ if (!di) {
+ wpa_printf(MSG_INFO, "ANQP: Could not create dialog "
+ "for " MACSTR " (dialog token %u)",
+ MAC2STR(sa), dialog_token);
+ wpabuf_free(buf);
+ return;
+ }
+ di->sd_resp = buf;
+ di->sd_resp_pos = 0;
+ tx_buf = gas_anqp_build_initial_resp_buf(
+ dialog_token, WLAN_STATUS_SUCCESS, comeback_delay,
+ NULL);
+ } else {
+ wpa_printf(MSG_DEBUG, "ANQP: Initial response (no comeback)");
+ tx_buf = gas_anqp_build_initial_resp_buf(
+ dialog_token, WLAN_STATUS_SUCCESS, 0, buf);
+ wpabuf_free(buf);
+ }
+ if (!tx_buf)
+ return;
+
+ hostapd_drv_send_action(hapd, hapd->iface->freq, 0, sa,
+ wpabuf_head(tx_buf), wpabuf_len(tx_buf));
+ wpabuf_free(tx_buf);
+}
+
+
+static void gas_serv_rx_gas_initial_req(struct hostapd_data *hapd,
+ const u8 *sa,
+ const u8 *data, size_t len)
+{
+ const u8 *pos = data;
+ const u8 *end = data + len;
+ const u8 *next;
+ u8 dialog_token;
+ u16 slen;
+ struct anqp_query_info qi;
+ const u8 *adv_proto;
+
+ if (len < 1 + 2)
+ return;
+
+ os_memset(&qi, 0, sizeof(qi));
+
+ dialog_token = *pos++;
+ wpa_msg(hapd->msg_ctx, MSG_DEBUG,
+ "GAS: GAS Initial Request from " MACSTR " (dialog token %u) ",
+ MAC2STR(sa), dialog_token);
+
+ if (*pos != WLAN_EID_ADV_PROTO) {
+ wpa_msg(hapd->msg_ctx, MSG_DEBUG,
+ "GAS: Unexpected IE in GAS Initial Request: %u", *pos);
+ return;
+ }
+ adv_proto = pos++;
+
+ slen = *pos++;
+ next = pos + slen;
+ if (next > end || slen < 2) {
+ wpa_msg(hapd->msg_ctx, MSG_DEBUG,
+ "GAS: Invalid IE in GAS Initial Request");
+ return;
+ }
+ pos++; /* skip QueryRespLenLimit and PAME-BI */
+
+ if (*pos != ACCESS_NETWORK_QUERY_PROTOCOL) {
+ struct wpabuf *buf;
+ wpa_msg(hapd->msg_ctx, MSG_DEBUG,
+ "GAS: Unsupported GAS advertisement protocol id %u",
+ *pos);
+ if (sa[0] & 0x01)
+ return; /* Invalid source address - drop silently */
+ buf = gas_build_initial_resp(
+ dialog_token, WLAN_STATUS_GAS_ADV_PROTO_NOT_SUPPORTED,
+ 0, 2 + slen + 2);
+ if (buf == NULL)
+ return;
+ wpabuf_put_data(buf, adv_proto, 2 + slen);
+ wpabuf_put_le16(buf, 0); /* Query Response Length */
+ hostapd_drv_send_action(hapd, hapd->iface->freq, 0, sa,
+ wpabuf_head(buf), wpabuf_len(buf));
+ wpabuf_free(buf);
+ return;
+ }
+
+ pos = next;
+ /* Query Request */
+ if (pos + 2 > end)
+ return;
+ slen = WPA_GET_LE16(pos);
+ pos += 2;
+ if (pos + slen > end)
+ return;
+ end = pos + slen;
+
+ /* ANQP Query Request */
+ while (pos < end) {
+ u16 info_id, elen;
+
+ if (pos + 4 > end)
+ return;
+
+ info_id = WPA_GET_LE16(pos);
+ pos += 2;
+ elen = WPA_GET_LE16(pos);
+ pos += 2;
+
+ if (pos + elen > end) {
+ wpa_printf(MSG_DEBUG, "ANQP: Invalid Query Request");
+ return;
+ }
+
+ switch (info_id) {
+ case ANQP_QUERY_LIST:
+ rx_anqp_query_list(hapd, pos, pos + elen, &qi);
+ break;
+#ifdef CONFIG_HS20
+ case ANQP_VENDOR_SPECIFIC:
+ rx_anqp_vendor_specific(hapd, pos, pos + elen, &qi);
+ break;
+#endif /* CONFIG_HS20 */
+ default:
+ wpa_printf(MSG_DEBUG, "ANQP: Unsupported Query "
+ "Request element %u", info_id);
+ break;
+ }
+
+ pos += elen;
+ }
+
+ gas_serv_req_local_processing(hapd, sa, dialog_token, &qi);
+}
+
+
+void gas_serv_tx_gas_response(struct hostapd_data *hapd, const u8 *dst,
+ struct gas_dialog_info *dialog)
+{
+ struct wpabuf *buf, *tx_buf;
+ u8 dialog_token = dialog->dialog_token;
+ size_t frag_len;
+
+ if (dialog->sd_resp == NULL) {
+ buf = gas_serv_build_gas_resp_payload(hapd,
+ dialog->all_requested,
+ dialog, NULL, 0);
+ wpa_hexdump_buf(MSG_MSGDUMP, "ANQP: Generated ANQP responses",
+ buf);
+ if (!buf)
+ goto tx_gas_response_done;
+ dialog->sd_resp = buf;
+ dialog->sd_resp_pos = 0;
+ }
+ frag_len = wpabuf_len(dialog->sd_resp) - dialog->sd_resp_pos;
+ if (frag_len > hapd->gas_frag_limit || dialog->comeback_delay ||
+ hapd->conf->gas_comeback_delay) {
+ u16 comeback_delay_tus = dialog->comeback_delay +
+ GAS_SERV_COMEBACK_DELAY_FUDGE;
+ u32 comeback_delay_secs, comeback_delay_usecs;
+
+ if (hapd->conf->gas_comeback_delay) {
+ /* Testing - allow overriding of the delay value */
+ comeback_delay_tus = hapd->conf->gas_comeback_delay;
+ }
+
+ wpa_printf(MSG_DEBUG, "GAS: Response frag_len %u (frag limit "
+ "%u) and comeback delay %u, "
+ "requesting comebacks", (unsigned int) frag_len,
+ (unsigned int) hapd->gas_frag_limit,
+ dialog->comeback_delay);
+ tx_buf = gas_anqp_build_initial_resp_buf(dialog_token,
+ WLAN_STATUS_SUCCESS,
+ comeback_delay_tus,
+ NULL);
+ if (tx_buf) {
+ wpa_msg(hapd->msg_ctx, MSG_DEBUG,
+ "GAS: Tx GAS Initial Resp (comeback = 10TU)");
+ hostapd_drv_send_action(hapd, hapd->iface->freq, 0,
+ dst,
+ wpabuf_head(tx_buf),
+ wpabuf_len(tx_buf));
+ }
+ wpabuf_free(tx_buf);
+
+ /* start a timer of 1.5 * comeback-delay */
+ comeback_delay_tus = comeback_delay_tus +
+ (comeback_delay_tus / 2);
+ comeback_delay_secs = (comeback_delay_tus * 1024) / 1000000;
+ comeback_delay_usecs = (comeback_delay_tus * 1024) -
+ (comeback_delay_secs * 1000000);
+ eloop_register_timeout(comeback_delay_secs,
+ comeback_delay_usecs,
+ gas_serv_clear_cached_ies, dialog,
+ NULL);
+ goto tx_gas_response_done;
+ }
+
+ buf = wpabuf_alloc_copy(wpabuf_head_u8(dialog->sd_resp) +
+ dialog->sd_resp_pos, frag_len);
+ if (buf == NULL) {
+ wpa_msg(hapd->msg_ctx, MSG_DEBUG, "GAS: Buffer allocation "
+ "failed");
+ goto tx_gas_response_done;
+ }
+ tx_buf = gas_anqp_build_initial_resp_buf(dialog_token,
+ WLAN_STATUS_SUCCESS, 0, buf);
+ wpabuf_free(buf);
+ if (tx_buf == NULL)
+ goto tx_gas_response_done;
+ wpa_msg(hapd->msg_ctx, MSG_DEBUG, "GAS: Tx GAS Initial "
+ "Response (frag_id %d frag_len %d)",
+ dialog->sd_frag_id, (int) frag_len);
+ dialog->sd_frag_id++;
+
+ hostapd_drv_send_action(hapd, hapd->iface->freq, 0, dst,
+ wpabuf_head(tx_buf), wpabuf_len(tx_buf));
+ wpabuf_free(tx_buf);
+tx_gas_response_done:
+ gas_serv_clear_cached_ies(dialog, NULL);
+}
+
+
+static void gas_serv_rx_gas_comeback_req(struct hostapd_data *hapd,
+ const u8 *sa,
+ const u8 *data, size_t len)
+{
+ struct gas_dialog_info *dialog;
+ struct wpabuf *buf, *tx_buf;
+ u8 dialog_token;
+ size_t frag_len;
+ int more = 0;
+
+ wpa_hexdump(MSG_DEBUG, "GAS: RX GAS Comeback Request", data, len);
+ if (len < 1)
+ return;
+ dialog_token = *data;
+ wpa_msg(hapd->msg_ctx, MSG_DEBUG, "GAS: Dialog Token: %u",
+ dialog_token);
+
+ dialog = gas_serv_dialog_find(hapd, sa, dialog_token);
+ if (!dialog) {
+ wpa_msg(hapd->msg_ctx, MSG_DEBUG, "GAS: No pending SD "
+ "response fragment for " MACSTR " dialog token %u",
+ MAC2STR(sa), dialog_token);
+
+ if (sa[0] & 0x01)
+ return; /* Invalid source address - drop silently */
+ tx_buf = gas_anqp_build_comeback_resp_buf(
+ dialog_token, WLAN_STATUS_NO_OUTSTANDING_GAS_REQ, 0, 0,
+ 0, NULL);
+ if (tx_buf == NULL)
+ return;
+ goto send_resp;
+ }
+
+ if (dialog->sd_resp == NULL) {
+ wpa_printf(MSG_DEBUG, "GAS: Remote request 0x%x received 0x%x",
+ dialog->requested, dialog->received);
+ if ((dialog->requested & dialog->received) !=
+ dialog->requested) {
+ wpa_printf(MSG_DEBUG, "GAS: Did not receive response "
+ "from remote processing");
+ gas_serv_dialog_clear(dialog);
+ tx_buf = gas_anqp_build_comeback_resp_buf(
+ dialog_token,
+ WLAN_STATUS_GAS_RESP_NOT_RECEIVED, 0, 0, 0,
+ NULL);
+ if (tx_buf == NULL)
+ return;
+ goto send_resp;
+ }
+
+ buf = gas_serv_build_gas_resp_payload(hapd,
+ dialog->all_requested,
+ dialog, NULL, 0);
+ wpa_hexdump_buf(MSG_MSGDUMP, "ANQP: Generated ANQP responses",
+ buf);
+ if (!buf)
+ goto rx_gas_comeback_req_done;
+ dialog->sd_resp = buf;
+ dialog->sd_resp_pos = 0;
+ }
+ frag_len = wpabuf_len(dialog->sd_resp) - dialog->sd_resp_pos;
+ if (frag_len > hapd->gas_frag_limit) {
+ frag_len = hapd->gas_frag_limit;
+ more = 1;
+ }
+ wpa_msg(hapd->msg_ctx, MSG_DEBUG, "GAS: resp frag_len %u",
+ (unsigned int) frag_len);
+ buf = wpabuf_alloc_copy(wpabuf_head_u8(dialog->sd_resp) +
+ dialog->sd_resp_pos, frag_len);
+ if (buf == NULL) {
+ wpa_msg(hapd->msg_ctx, MSG_DEBUG, "GAS: Failed to allocate "
+ "buffer");
+ goto rx_gas_comeback_req_done;
+ }
+ tx_buf = gas_anqp_build_comeback_resp_buf(dialog_token,
+ WLAN_STATUS_SUCCESS,
+ dialog->sd_frag_id,
+ more, 0, buf);
+ wpabuf_free(buf);
+ if (tx_buf == NULL)
+ goto rx_gas_comeback_req_done;
+ wpa_msg(hapd->msg_ctx, MSG_DEBUG, "GAS: Tx GAS Comeback Response "
+ "(frag_id %d more=%d frag_len=%d)",
+ dialog->sd_frag_id, more, (int) frag_len);
+ dialog->sd_frag_id++;
+ dialog->sd_resp_pos += frag_len;
+
+ if (more) {
+ wpa_msg(hapd->msg_ctx, MSG_DEBUG, "GAS: %d more bytes remain "
+ "to be sent",
+ (int) (wpabuf_len(dialog->sd_resp) -
+ dialog->sd_resp_pos));
+ } else {
+ wpa_msg(hapd->msg_ctx, MSG_DEBUG, "GAS: All fragments of "
+ "SD response sent");
+ gas_serv_dialog_clear(dialog);
+ gas_serv_free_dialogs(hapd, sa);
+ }
+
+send_resp:
+ hostapd_drv_send_action(hapd, hapd->iface->freq, 0, sa,
+ wpabuf_head(tx_buf), wpabuf_len(tx_buf));
+ wpabuf_free(tx_buf);
+ return;
+
+rx_gas_comeback_req_done:
+ gas_serv_clear_cached_ies(dialog, NULL);
+}
+
+
+static void gas_serv_rx_public_action(void *ctx, const u8 *buf, size_t len,
+ int freq)
+{
+ struct hostapd_data *hapd = ctx;
+ const struct ieee80211_mgmt *mgmt;
+ size_t hdr_len;
+ const u8 *sa, *data;
+
+ mgmt = (const struct ieee80211_mgmt *) buf;
+ hdr_len = (const u8 *) &mgmt->u.action.u.vs_public_action.action - buf;
+ if (hdr_len > len)
+ return;
+ if (mgmt->u.action.category != WLAN_ACTION_PUBLIC)
+ return;
+ sa = mgmt->sa;
+ len -= hdr_len;
+ data = &mgmt->u.action.u.public_action.action;
+ switch (data[0]) {
+ case WLAN_PA_GAS_INITIAL_REQ:
+ gas_serv_rx_gas_initial_req(hapd, sa, data + 1, len - 1);
+ break;
+ case WLAN_PA_GAS_COMEBACK_REQ:
+ gas_serv_rx_gas_comeback_req(hapd, sa, data + 1, len - 1);
+ break;
+ }
+}
+
+
+int gas_serv_init(struct hostapd_data *hapd)
+{
+ hapd->public_action_cb = gas_serv_rx_public_action;
+ hapd->public_action_cb_ctx = hapd;
+ hapd->gas_frag_limit = 1400;
+ if (hapd->conf->gas_frag_limit > 0)
+ hapd->gas_frag_limit = hapd->conf->gas_frag_limit;
+ return 0;
+}
+
+
+void gas_serv_deinit(struct hostapd_data *hapd)
+{
+}
diff --git a/contrib/wpa/src/ap/gas_serv.h b/contrib/wpa/src/ap/gas_serv.h
new file mode 100644
index 0000000..4213cf6
--- /dev/null
+++ b/contrib/wpa/src/ap/gas_serv.h
@@ -0,0 +1,71 @@
+/*
+ * Generic advertisement service (GAS) server
+ * Copyright (c) 2011-2012, Qualcomm Atheros, Inc.
+ *
+ * This software may be distributed under the terms of the BSD license.
+ * See README for more details.
+ */
+
+#ifndef GAS_SERV_H
+#define GAS_SERV_H
+
+#define ANQP_REQ_CAPABILITY_LIST \
+ (1 << (ANQP_CAPABILITY_LIST - ANQP_QUERY_LIST))
+#define ANQP_REQ_VENUE_NAME \
+ (1 << (ANQP_VENUE_NAME - ANQP_QUERY_LIST))
+#define ANQP_REQ_NETWORK_AUTH_TYPE \
+ (1 << (ANQP_NETWORK_AUTH_TYPE - ANQP_QUERY_LIST))
+#define ANQP_REQ_ROAMING_CONSORTIUM \
+ (1 << (ANQP_ROAMING_CONSORTIUM - ANQP_QUERY_LIST))
+#define ANQP_REQ_IP_ADDR_TYPE_AVAILABILITY \
+ (1 << (ANQP_IP_ADDR_TYPE_AVAILABILITY - ANQP_QUERY_LIST))
+#define ANQP_REQ_NAI_REALM \
+ (1 << (ANQP_NAI_REALM - ANQP_QUERY_LIST))
+#define ANQP_REQ_3GPP_CELLULAR_NETWORK \
+ (1 << (ANQP_3GPP_CELLULAR_NETWORK - ANQP_QUERY_LIST))
+#define ANQP_REQ_DOMAIN_NAME \
+ (1 << (ANQP_DOMAIN_NAME - ANQP_QUERY_LIST))
+#define ANQP_REQ_HS_CAPABILITY_LIST \
+ (0x10000 << HS20_STYPE_CAPABILITY_LIST)
+#define ANQP_REQ_OPERATOR_FRIENDLY_NAME \
+ (0x10000 << HS20_STYPE_OPERATOR_FRIENDLY_NAME)
+#define ANQP_REQ_WAN_METRICS \
+ (0x10000 << HS20_STYPE_WAN_METRICS)
+#define ANQP_REQ_CONNECTION_CAPABILITY \
+ (0x10000 << HS20_STYPE_CONNECTION_CAPABILITY)
+#define ANQP_REQ_NAI_HOME_REALM \
+ (0x10000 << HS20_STYPE_NAI_HOME_REALM_QUERY)
+#define ANQP_REQ_OPERATING_CLASS \
+ (0x10000 << HS20_STYPE_OPERATING_CLASS)
+
+/* To account for latencies between hostapd and external ANQP processor */
+#define GAS_SERV_COMEBACK_DELAY_FUDGE 10
+#define GAS_SERV_MIN_COMEBACK_DELAY 100 /* in TU */
+
+struct gas_dialog_info {
+ u8 valid;
+ u8 index;
+ struct wpabuf *sd_resp; /* Fragmented response */
+ u8 dialog_token;
+ size_t sd_resp_pos; /* Offset in sd_resp */
+ u8 sd_frag_id;
+ u16 comeback_delay;
+
+ unsigned int requested;
+ unsigned int received;
+ unsigned int all_requested;
+};
+
+struct hostapd_data;
+
+void gas_serv_tx_gas_response(struct hostapd_data *hapd, const u8 *dst,
+ struct gas_dialog_info *dialog);
+struct gas_dialog_info *
+gas_serv_dialog_find(struct hostapd_data *hapd, const u8 *addr,
+ u8 dialog_token);
+void gas_serv_dialog_clear(struct gas_dialog_info *dialog);
+
+int gas_serv_init(struct hostapd_data *hapd);
+void gas_serv_deinit(struct hostapd_data *hapd);
+
+#endif /* GAS_SERV_H */
diff --git a/contrib/wpa/src/ap/hostapd.c b/contrib/wpa/src/ap/hostapd.c
index 841f9c5..cef9daf 100644
--- a/contrib/wpa/src/ap/hostapd.c
+++ b/contrib/wpa/src/ap/hostapd.c
@@ -1,15 +1,9 @@
/*
* hostapd / Initialization and configuration
- * Copyright (c) 2002-2009, Jouni Malinen <j@w1.fi>
+ * Copyright (c) 2002-2012, Jouni Malinen <j@w1.fi>
*
- * This program is free software; you can redistribute it and/or modify
- * it under the terms of the GNU General Public License version 2 as
- * published by the Free Software Foundation.
- *
- * Alternatively, this software may be distributed under the terms of BSD
- * license.
- *
- * See README and COPYING for more details.
+ * This software may be distributed under the terms of the BSD license.
+ * See README for more details.
*/
#include "utils/includes.h"
@@ -18,6 +12,7 @@
#include "utils/eloop.h"
#include "common/ieee802_11_defs.h"
#include "radius/radius_client.h"
+#include "radius/radius_das.h"
#include "drivers/driver.h"
#include "hostapd.h"
#include "authsrv.h"
@@ -35,57 +30,56 @@
#include "wpa_auth_glue.h"
#include "ap_drv_ops.h"
#include "ap_config.h"
+#include "p2p_hostapd.h"
+#include "gas_serv.h"
-static int hostapd_flush_old_stations(struct hostapd_data *hapd);
+static int hostapd_flush_old_stations(struct hostapd_data *hapd, u16 reason);
static int hostapd_setup_encryption(char *iface, struct hostapd_data *hapd);
+static int hostapd_broadcast_wep_clear(struct hostapd_data *hapd);
extern int wpa_debug_level;
+extern struct wpa_driver_ops *wpa_drivers[];
-int hostapd_reload_config(struct hostapd_iface *iface)
+int hostapd_for_each_interface(struct hapd_interfaces *interfaces,
+ int (*cb)(struct hostapd_iface *iface,
+ void *ctx), void *ctx)
{
- struct hostapd_data *hapd = iface->bss[0];
- struct hostapd_config *newconf, *oldconf;
- size_t j;
+ size_t i;
+ int ret;
- if (iface->config_read_cb == NULL)
- return -1;
- newconf = iface->config_read_cb(iface->config_fname);
- if (newconf == NULL)
- return -1;
+ for (i = 0; i < interfaces->count; i++) {
+ ret = cb(interfaces->iface[i], ctx);
+ if (ret)
+ return ret;
+ }
- /*
- * Deauthenticate all stations since the new configuration may not
- * allow them to use the BSS anymore.
- */
- for (j = 0; j < iface->num_bss; j++)
- hostapd_flush_old_stations(iface->bss[j]);
+ return 0;
+}
+
+static void hostapd_reload_bss(struct hostapd_data *hapd)
+{
#ifndef CONFIG_NO_RADIUS
- /* TODO: update dynamic data based on changed configuration
- * items (e.g., open/close sockets, etc.) */
- radius_client_flush(hapd->radius, 0);
+ radius_client_reconfig(hapd->radius, hapd->conf->radius);
#endif /* CONFIG_NO_RADIUS */
- oldconf = hapd->iconf;
- hapd->iconf = newconf;
- hapd->conf = &newconf->bss[0];
- iface->conf = newconf;
-
if (hostapd_setup_wpa_psk(hapd->conf)) {
wpa_printf(MSG_ERROR, "Failed to re-configure WPA PSK "
"after reloading configuration");
}
if (hapd->conf->ieee802_1x || hapd->conf->wpa)
- hapd->drv.set_drv_ieee8021x(hapd, hapd->conf->iface, 1);
+ hostapd_set_drv_ieee8021x(hapd, hapd->conf->iface, 1);
else
- hapd->drv.set_drv_ieee8021x(hapd, hapd->conf->iface, 0);
+ hostapd_set_drv_ieee8021x(hapd, hapd->conf->iface, 0);
- if (hapd->conf->wpa && hapd->wpa_auth == NULL)
+ if (hapd->conf->wpa && hapd->wpa_auth == NULL) {
hostapd_setup_wpa(hapd);
- else if (hapd->conf->wpa) {
+ if (hapd->wpa_auth)
+ wpa_init_keys(hapd->wpa_auth);
+ } else if (hapd->conf->wpa) {
const u8 *wpa_ie;
size_t wpa_ie_len;
hostapd_reconfig_wpa(hapd);
@@ -105,15 +99,56 @@ int hostapd_reload_config(struct hostapd_iface *iface)
hostapd_update_wps(hapd);
if (hapd->conf->ssid.ssid_set &&
- hostapd_set_ssid(hapd, (u8 *) hapd->conf->ssid.ssid,
+ hostapd_set_ssid(hapd, hapd->conf->ssid.ssid,
hapd->conf->ssid.ssid_len)) {
wpa_printf(MSG_ERROR, "Could not set SSID for kernel driver");
/* try to continue */
}
+ wpa_printf(MSG_DEBUG, "Reconfigured interface %s", hapd->conf->iface);
+}
+
+
+int hostapd_reload_config(struct hostapd_iface *iface)
+{
+ struct hostapd_data *hapd = iface->bss[0];
+ struct hostapd_config *newconf, *oldconf;
+ size_t j;
+
+ if (iface->interfaces == NULL ||
+ iface->interfaces->config_read_cb == NULL)
+ return -1;
+ newconf = iface->interfaces->config_read_cb(iface->config_fname);
+ if (newconf == NULL)
+ return -1;
+
+ /*
+ * Deauthenticate all stations since the new configuration may not
+ * allow them to use the BSS anymore.
+ */
+ for (j = 0; j < iface->num_bss; j++) {
+ hostapd_flush_old_stations(iface->bss[j],
+ WLAN_REASON_PREV_AUTH_NOT_VALID);
+ hostapd_broadcast_wep_clear(iface->bss[j]);
+
+#ifndef CONFIG_NO_RADIUS
+ /* TODO: update dynamic data based on changed configuration
+ * items (e.g., open/close sockets, etc.) */
+ radius_client_flush(iface->bss[j]->radius, 0);
+#endif /* CONFIG_NO_RADIUS */
+ }
+
+ oldconf = hapd->iconf;
+ iface->conf = newconf;
+
+ for (j = 0; j < iface->num_bss; j++) {
+ hapd = iface->bss[j];
+ hapd->iconf = newconf;
+ hapd->conf = &newconf->bss[j];
+ hostapd_reload_bss(hapd);
+ }
hostapd_config_free(oldconf);
- wpa_printf(MSG_DEBUG, "Reconfigured interface %s", hapd->conf->iface);
return 0;
}
@@ -125,8 +160,8 @@ static void hostapd_broadcast_key_clear_iface(struct hostapd_data *hapd,
int i;
for (i = 0; i < NUM_WEP_KEYS; i++) {
- if (hapd->drv.set_key(ifname, hapd, WPA_ALG_NONE, NULL, i,
- i == 0 ? 1 : 0, NULL, 0, NULL, 0)) {
+ if (hostapd_drv_set_key(ifname, hapd, WPA_ALG_NONE, NULL, i,
+ 0, NULL, 0, NULL, 0)) {
wpa_printf(MSG_DEBUG, "Failed to clear default "
"encryption keys (ifname=%s keyidx=%d)",
ifname, i);
@@ -135,9 +170,9 @@ static void hostapd_broadcast_key_clear_iface(struct hostapd_data *hapd,
#ifdef CONFIG_IEEE80211W
if (hapd->conf->ieee80211w) {
for (i = NUM_WEP_KEYS; i < NUM_WEP_KEYS + 2; i++) {
- if (hapd->drv.set_key(ifname, hapd, WPA_ALG_NONE, NULL,
- i, i == 0 ? 1 : 0, NULL, 0,
- NULL, 0)) {
+ if (hostapd_drv_set_key(ifname, hapd, WPA_ALG_NONE,
+ NULL, i, 0, NULL,
+ 0, NULL, 0)) {
wpa_printf(MSG_DEBUG, "Failed to clear "
"default mgmt encryption keys "
"(ifname=%s keyidx=%d)", ifname, i);
@@ -162,11 +197,10 @@ static int hostapd_broadcast_wep_set(struct hostapd_data *hapd)
idx = ssid->wep.idx;
if (ssid->wep.default_len &&
- hapd->drv.set_key(hapd->conf->iface,
- hapd, WPA_ALG_WEP, NULL, idx,
- idx == ssid->wep.idx,
- NULL, 0, ssid->wep.key[idx],
- ssid->wep.len[idx])) {
+ hostapd_drv_set_key(hapd->conf->iface,
+ hapd, WPA_ALG_WEP, broadcast_ether_addr, idx,
+ 1, NULL, 0, ssid->wep.key[idx],
+ ssid->wep.len[idx])) {
wpa_printf(MSG_WARNING, "Could not set WEP encryption.");
errors++;
}
@@ -184,9 +218,10 @@ static int hostapd_broadcast_wep_set(struct hostapd_data *hapd)
continue;
idx = key->idx;
- if (hapd->drv.set_key(ifname, hapd, WPA_ALG_WEP, NULL,
- idx, idx == key->idx, NULL, 0,
- key->key[idx], key->len[idx])) {
+ if (hostapd_drv_set_key(ifname, hapd, WPA_ALG_WEP,
+ broadcast_ether_addr, idx, 1,
+ NULL, 0, key->key[idx],
+ key->len[idx])) {
wpa_printf(MSG_WARNING, "Could not set "
"dynamic VLAN WEP encryption.");
errors++;
@@ -197,21 +232,9 @@ static int hostapd_broadcast_wep_set(struct hostapd_data *hapd)
return errors;
}
-/**
- * hostapd_cleanup - Per-BSS cleanup (deinitialization)
- * @hapd: Pointer to BSS data
- *
- * This function is used to free all per-BSS data structures and resources.
- * This gets called in a loop for each BSS between calls to
- * hostapd_cleanup_iface_pre() and hostapd_cleanup_iface() when an interface
- * is deinitialized. Most of the modules that are initialized in
- * hostapd_setup_bss() are deinitialized here.
- */
-static void hostapd_cleanup(struct hostapd_data *hapd)
-{
- if (hapd->iface->ctrl_iface_deinit)
- hapd->iface->ctrl_iface_deinit(hapd);
+static void hostapd_free_hapd_data(struct hostapd_data *hapd)
+{
iapp_deinit(hapd->iapp);
hapd->iapp = NULL;
accounting_deinit(hapd);
@@ -221,6 +244,8 @@ static void hostapd_cleanup(struct hostapd_data *hapd)
#ifndef CONFIG_NO_RADIUS
radius_client_deinit(hapd->radius);
hapd->radius = NULL;
+ radius_das_deinit(hapd->radius_das);
+ hapd->radius_das = NULL;
#endif /* CONFIG_NO_RADIUS */
hostapd_deinit_wps(hapd);
@@ -235,6 +260,43 @@ static void hostapd_cleanup(struct hostapd_data *hapd)
os_free(hapd->probereq_cb);
hapd->probereq_cb = NULL;
+
+#ifdef CONFIG_P2P
+ wpabuf_free(hapd->p2p_beacon_ie);
+ hapd->p2p_beacon_ie = NULL;
+ wpabuf_free(hapd->p2p_probe_resp_ie);
+ hapd->p2p_probe_resp_ie = NULL;
+#endif /* CONFIG_P2P */
+
+ wpabuf_free(hapd->time_adv);
+
+#ifdef CONFIG_INTERWORKING
+ gas_serv_deinit(hapd);
+#endif /* CONFIG_INTERWORKING */
+
+#ifdef CONFIG_SQLITE
+ os_free(hapd->tmp_eap_user.identity);
+ os_free(hapd->tmp_eap_user.password);
+#endif /* CONFIG_SQLITE */
+}
+
+
+/**
+ * hostapd_cleanup - Per-BSS cleanup (deinitialization)
+ * @hapd: Pointer to BSS data
+ *
+ * This function is used to free all per-BSS data structures and resources.
+ * This gets called in a loop for each BSS between calls to
+ * hostapd_cleanup_iface_pre() and hostapd_cleanup_iface() when an interface
+ * is deinitialized. Most of the modules that are initialized in
+ * hostapd_setup_bss() are deinitialized here.
+ */
+static void hostapd_cleanup(struct hostapd_data *hapd)
+{
+ if (hapd->iface->interfaces &&
+ hapd->iface->interfaces->ctrl_iface_deinit)
+ hapd->iface->interfaces->ctrl_iface_deinit(hapd);
+ hostapd_free_hapd_data(hapd);
}
@@ -250,6 +312,18 @@ static void hostapd_cleanup_iface_pre(struct hostapd_iface *iface)
}
+static void hostapd_cleanup_iface_partial(struct hostapd_iface *iface)
+{
+ hostapd_free_hw_features(iface->hw_features, iface->num_hw_features);
+ iface->hw_features = NULL;
+ os_free(iface->current_rates);
+ iface->current_rates = NULL;
+ os_free(iface->basic_rates);
+ iface->basic_rates = NULL;
+ ap_list_deinit(iface);
+}
+
+
/**
* hostapd_cleanup_iface - Complete per-interface cleanup
* @iface: Pointer to interface data
@@ -259,11 +333,7 @@ static void hostapd_cleanup_iface_pre(struct hostapd_iface *iface)
*/
static void hostapd_cleanup_iface(struct hostapd_iface *iface)
{
- hostapd_free_hw_features(iface->hw_features, iface->num_hw_features);
- iface->hw_features = NULL;
- os_free(iface->current_rates);
- iface->current_rates = NULL;
- ap_list_deinit(iface);
+ hostapd_cleanup_iface_partial(iface);
hostapd_config_free(iface->conf);
iface->conf = NULL;
@@ -273,6 +343,15 @@ static void hostapd_cleanup_iface(struct hostapd_iface *iface)
}
+static void hostapd_clear_wep(struct hostapd_data *hapd)
+{
+ if (hapd->drv_priv) {
+ hostapd_set_privacy(hapd, 0);
+ hostapd_broadcast_wep_clear(hapd);
+ }
+}
+
+
static int hostapd_setup_encryption(char *iface, struct hostapd_data *hapd)
{
int i;
@@ -284,12 +363,18 @@ static int hostapd_setup_encryption(char *iface, struct hostapd_data *hapd)
return 0;
}
+ /*
+ * When IEEE 802.1X is not enabled, the driver may need to know how to
+ * set authentication algorithms for static WEP.
+ */
+ hostapd_drv_set_authmode(hapd, hapd->conf->auth_algs);
+
for (i = 0; i < 4; i++) {
if (hapd->conf->ssid.wep.key[i] &&
- hapd->drv.set_key(iface, hapd, WPA_ALG_WEP, NULL, i,
- i == hapd->conf->ssid.wep.idx, NULL, 0,
- hapd->conf->ssid.wep.key[i],
- hapd->conf->ssid.wep.len[i])) {
+ hostapd_drv_set_key(iface, hapd, WPA_ALG_WEP, NULL, i,
+ i == hapd->conf->ssid.wep.idx, NULL, 0,
+ hapd->conf->ssid.wep.key[i],
+ hapd->conf->ssid.wep.len[i])) {
wpa_printf(MSG_WARNING, "Could not set WEP "
"encryption.");
return -1;
@@ -303,30 +388,24 @@ static int hostapd_setup_encryption(char *iface, struct hostapd_data *hapd)
}
-static int hostapd_flush_old_stations(struct hostapd_data *hapd)
+static int hostapd_flush_old_stations(struct hostapd_data *hapd, u16 reason)
{
int ret = 0;
+ u8 addr[ETH_ALEN];
if (hostapd_drv_none(hapd) || hapd->drv_priv == NULL)
return 0;
- wpa_printf(MSG_DEBUG, "Flushing old station entries");
+ wpa_dbg(hapd->msg_ctx, MSG_DEBUG, "Flushing old station entries");
if (hostapd_flush(hapd)) {
- wpa_printf(MSG_WARNING, "Could not connect to kernel driver.");
+ wpa_msg(hapd->msg_ctx, MSG_WARNING, "Could not connect to "
+ "kernel driver");
ret = -1;
}
- wpa_printf(MSG_DEBUG, "Deauthenticate all stations");
-
- /* New Prism2.5/3 STA firmware versions seem to have issues with this
- * broadcast deauth frame. This gets the firmware in odd state where
- * nothing works correctly, so let's skip sending this for the hostap
- * driver. */
- if (hapd->driver && os_strcmp(hapd->driver->name, "hostap") != 0) {
- u8 addr[ETH_ALEN];
- os_memset(addr, 0xff, ETH_ALEN);
- hapd->drv.sta_deauth(hapd, addr,
- WLAN_REASON_PREV_AUTH_NOT_VALID);
- }
+ wpa_dbg(hapd->msg_ctx, MSG_DEBUG, "Deauthenticate all stations");
+ os_memset(addr, 0xff, ETH_ALEN);
+ hostapd_drv_sta_deauth(hapd, addr, reason);
+ hostapd_free_stas(hapd);
return ret;
}
@@ -344,7 +423,6 @@ static int hostapd_validate_bssid_configuration(struct hostapd_iface *iface)
u8 mask[ETH_ALEN] = { 0 };
struct hostapd_data *hapd = iface->bss[0];
unsigned int i = iface->conf->num_bss, bits = 0, j;
- int res;
int auto_addr = 0;
if (hostapd_drv_none(hapd))
@@ -408,17 +486,6 @@ skip_mask_ext:
wpa_printf(MSG_DEBUG, "BSS count %lu, BSSID mask " MACSTR " (%d bits)",
(unsigned long) iface->conf->num_bss, MAC2STR(mask), bits);
- res = hostapd_valid_bss_mask(hapd, hapd->own_addr, mask);
- if (res == 0)
- return 0;
-
- if (res < 0) {
- wpa_printf(MSG_ERROR, "Driver did not accept BSSID mask "
- MACSTR " for start address " MACSTR ".",
- MAC2STR(mask), MAC2STR(hapd->own_addr));
- return -1;
- }
-
if (!auto_addr)
return 0;
@@ -452,6 +519,86 @@ static int mac_in_conf(struct hostapd_config *conf, const void *a)
}
+#ifndef CONFIG_NO_RADIUS
+
+static int hostapd_das_nas_mismatch(struct hostapd_data *hapd,
+ struct radius_das_attrs *attr)
+{
+ /* TODO */
+ return 0;
+}
+
+
+static struct sta_info * hostapd_das_find_sta(struct hostapd_data *hapd,
+ struct radius_das_attrs *attr)
+{
+ struct sta_info *sta = NULL;
+ char buf[128];
+
+ if (attr->sta_addr)
+ sta = ap_get_sta(hapd, attr->sta_addr);
+
+ if (sta == NULL && attr->acct_session_id &&
+ attr->acct_session_id_len == 17) {
+ for (sta = hapd->sta_list; sta; sta = sta->next) {
+ os_snprintf(buf, sizeof(buf), "%08X-%08X",
+ sta->acct_session_id_hi,
+ sta->acct_session_id_lo);
+ if (os_memcmp(attr->acct_session_id, buf, 17) == 0)
+ break;
+ }
+ }
+
+ if (sta == NULL && attr->cui) {
+ for (sta = hapd->sta_list; sta; sta = sta->next) {
+ struct wpabuf *cui;
+ cui = ieee802_1x_get_radius_cui(sta->eapol_sm);
+ if (cui && wpabuf_len(cui) == attr->cui_len &&
+ os_memcmp(wpabuf_head(cui), attr->cui,
+ attr->cui_len) == 0)
+ break;
+ }
+ }
+
+ if (sta == NULL && attr->user_name) {
+ for (sta = hapd->sta_list; sta; sta = sta->next) {
+ u8 *identity;
+ size_t identity_len;
+ identity = ieee802_1x_get_identity(sta->eapol_sm,
+ &identity_len);
+ if (identity &&
+ identity_len == attr->user_name_len &&
+ os_memcmp(identity, attr->user_name, identity_len)
+ == 0)
+ break;
+ }
+ }
+
+ return sta;
+}
+
+
+static enum radius_das_res
+hostapd_das_disconnect(void *ctx, struct radius_das_attrs *attr)
+{
+ struct hostapd_data *hapd = ctx;
+ struct sta_info *sta;
+
+ if (hostapd_das_nas_mismatch(hapd, attr))
+ return RADIUS_DAS_NAS_MISMATCH;
+
+ sta = hostapd_das_find_sta(hapd, attr);
+ if (sta == NULL)
+ return RADIUS_DAS_SESSION_NOT_FOUND;
+
+ hostapd_drv_sta_deauth(hapd, sta->addr,
+ WLAN_REASON_PREV_AUTH_NOT_VALID);
+ ap_sta_deauthenticate(hapd, sta, WLAN_REASON_PREV_AUTH_NOT_VALID);
+
+ return RADIUS_DAS_SUCCESS;
+}
+
+#endif /* CONFIG_NO_RADIUS */
/**
@@ -495,14 +642,19 @@ static int hostapd_setup_bss(struct hostapd_data *hapd, int first)
hapd->interface_added = 1;
if (hostapd_if_add(hapd->iface->bss[0], WPA_IF_AP_BSS,
hapd->conf->iface, hapd->own_addr, hapd,
- &hapd->drv_priv, force_ifname, if_addr)) {
+ &hapd->drv_priv, force_ifname, if_addr,
+ hapd->conf->bridge[0] ? hapd->conf->bridge :
+ NULL)) {
wpa_printf(MSG_ERROR, "Failed to add BSS (BSSID="
MACSTR ")", MAC2STR(hapd->own_addr));
return -1;
}
}
- hostapd_flush_old_stations(hapd);
+ if (conf->wmm_enabled < 0)
+ conf->wmm_enabled = hapd->iconf->ieee80211n;
+
+ hostapd_flush_old_stations(hapd, WLAN_REASON_PREV_AUTH_NOT_VALID);
hostapd_set_privacy(hapd, 0);
hostapd_broadcast_wep_clear(hapd);
@@ -535,14 +687,14 @@ static int hostapd_setup_bss(struct hostapd_data *hapd, int first)
set_ssid = 0;
conf->ssid.ssid_len = ssid_len;
os_memcpy(conf->ssid.ssid, ssid, conf->ssid.ssid_len);
- conf->ssid.ssid[conf->ssid.ssid_len] = '\0';
}
if (!hostapd_drv_none(hapd)) {
wpa_printf(MSG_ERROR, "Using interface %s with hwaddr " MACSTR
- " and ssid '%s'",
+ " and ssid \"%s\"",
hapd->conf->iface, MAC2STR(hapd->own_addr),
- hapd->conf->ssid.ssid);
+ wpa_ssid_txt(hapd->conf->ssid.ssid,
+ hapd->conf->ssid.ssid_len));
}
if (hostapd_setup_wpa_psk(conf)) {
@@ -552,7 +704,7 @@ static int hostapd_setup_bss(struct hostapd_data *hapd, int first)
/* Set SSID for the kernel driver (to be used in beacon and probe
* response frames) */
- if (set_ssid && hostapd_set_ssid(hapd, (u8 *) conf->ssid.ssid,
+ if (set_ssid && hostapd_set_ssid(hapd, conf->ssid.ssid,
conf->ssid.ssid_len)) {
wpa_printf(MSG_ERROR, "Could not set SSID for kernel driver");
return -1;
@@ -566,6 +718,27 @@ static int hostapd_setup_bss(struct hostapd_data *hapd, int first)
wpa_printf(MSG_ERROR, "RADIUS client initialization failed.");
return -1;
}
+
+ if (hapd->conf->radius_das_port) {
+ struct radius_das_conf das_conf;
+ os_memset(&das_conf, 0, sizeof(das_conf));
+ das_conf.port = hapd->conf->radius_das_port;
+ das_conf.shared_secret = hapd->conf->radius_das_shared_secret;
+ das_conf.shared_secret_len =
+ hapd->conf->radius_das_shared_secret_len;
+ das_conf.client_addr = &hapd->conf->radius_das_client_addr;
+ das_conf.time_window = hapd->conf->radius_das_time_window;
+ das_conf.require_event_timestamp =
+ hapd->conf->radius_das_require_event_timestamp;
+ das_conf.ctx = hapd;
+ das_conf.disconnect = hostapd_das_disconnect;
+ hapd->radius_das = radius_das_init(&das_conf);
+ if (hapd->radius_das == NULL) {
+ wpa_printf(MSG_ERROR, "RADIUS DAS initialization "
+ "failed.");
+ return -1;
+ }
+ }
#endif /* CONFIG_NO_RADIUS */
if (hostapd_acl_init(hapd)) {
@@ -598,8 +771,16 @@ static int hostapd_setup_bss(struct hostapd_data *hapd, int first)
return -1;
}
- if (hapd->iface->ctrl_iface_init &&
- hapd->iface->ctrl_iface_init(hapd)) {
+#ifdef CONFIG_INTERWORKING
+ if (gas_serv_init(hapd)) {
+ wpa_printf(MSG_ERROR, "GAS server initialization failed");
+ return -1;
+ }
+#endif /* CONFIG_INTERWORKING */
+
+ if (hapd->iface->interfaces &&
+ hapd->iface->interfaces->ctrl_iface_init &&
+ hapd->iface->interfaces->ctrl_iface_init(hapd)) {
wpa_printf(MSG_ERROR, "Failed to setup control interface");
return -1;
}
@@ -611,6 +792,12 @@ static int hostapd_setup_bss(struct hostapd_data *hapd, int first)
ieee802_11_set_beacon(hapd);
+ if (hapd->wpa_auth && wpa_init_keys(hapd->wpa_auth) < 0)
+ return -1;
+
+ if (hapd->driver && hapd->driver->set_operstate)
+ hapd->driver->set_operstate(hapd->drv_priv, 1);
+
return 0;
}
@@ -624,9 +811,6 @@ static void hostapd_tx_queue_params(struct hostapd_iface *iface)
for (i = 0; i < NUM_TX_QUEUES; i++) {
p = &iface->conf->tx_queue[i];
- if (!p->configured)
- continue;
-
if (hostapd_set_tx_queue_params(hapd, i, p->aifs, p->cwmin,
p->cwmax, p->burst)) {
wpa_printf(MSG_DEBUG, "Failed to set TX queue "
@@ -717,6 +901,17 @@ int hostapd_setup_interface_complete(struct hostapd_iface *iface, int err)
}
}
+ if (iface->current_mode) {
+ if (hostapd_prepare_rates(iface, iface->current_mode)) {
+ wpa_printf(MSG_ERROR, "Failed to prepare rates "
+ "table.");
+ hostapd_logger(hapd, NULL, HOSTAPD_MODULE_IEEE80211,
+ HOSTAPD_LEVEL_WARNING,
+ "Failed to prepare rates table.");
+ return -1;
+ }
+ }
+
if (hapd->iconf->rts_threshold > -1 &&
hostapd_set_rts(hapd, hapd->iconf->rts_threshold)) {
wpa_printf(MSG_ERROR, "Could not set RTS threshold for "
@@ -753,6 +948,20 @@ int hostapd_setup_interface_complete(struct hostapd_iface *iface, int err)
return -1;
}
+ /*
+ * WPS UPnP module can be initialized only when the "upnp_iface" is up.
+ * If "interface" and "upnp_iface" are the same (e.g., non-bridge
+ * mode), the interface is up only after driver_commit, so initialize
+ * WPS after driver_commit.
+ */
+ for (j = 0; j < iface->num_bss; j++) {
+ if (hostapd_init_wps_complete(iface->bss[j]))
+ return -1;
+ }
+
+ if (hapd->setup_complete_cb)
+ hapd->setup_complete_cb(hapd->setup_complete_cb_ctx);
+
wpa_printf(MSG_DEBUG, "%s: Setup of interface done.",
iface->bss[0]->conf->iface);
@@ -807,12 +1016,12 @@ hostapd_alloc_bss_data(struct hostapd_iface *hapd_iface,
if (hapd == NULL)
return NULL;
- hostapd_set_driver_ops(&hapd->drv);
hapd->new_assoc_sta_cb = hostapd_new_assoc_sta;
hapd->iconf = conf;
hapd->conf = bss;
hapd->iface = hapd_iface;
hapd->driver = hapd->iconf->driver;
+ hapd->ctrl_sock = -1;
return hapd;
}
@@ -829,7 +1038,8 @@ void hostapd_interface_deinit(struct hostapd_iface *iface)
for (j = 0; j < iface->num_bss; j++) {
struct hostapd_data *hapd = iface->bss[j];
hostapd_free_stas(hapd);
- hostapd_flush_old_stations(hapd);
+ hostapd_flush_old_stations(hapd, WLAN_REASON_DEAUTH_LEAVING);
+ hostapd_clear_wep(hapd);
hostapd_cleanup(hapd);
}
}
@@ -844,6 +1054,293 @@ void hostapd_interface_free(struct hostapd_iface *iface)
}
+#ifdef HOSTAPD
+
+void hostapd_interface_deinit_free(struct hostapd_iface *iface)
+{
+ const struct wpa_driver_ops *driver;
+ void *drv_priv;
+ if (iface == NULL)
+ return;
+ driver = iface->bss[0]->driver;
+ drv_priv = iface->bss[0]->drv_priv;
+ hostapd_interface_deinit(iface);
+ if (driver && driver->hapd_deinit && drv_priv)
+ driver->hapd_deinit(drv_priv);
+ hostapd_interface_free(iface);
+}
+
+
+int hostapd_enable_iface(struct hostapd_iface *hapd_iface)
+{
+ if (hapd_iface->bss[0]->drv_priv != NULL) {
+ wpa_printf(MSG_ERROR, "Interface %s already enabled",
+ hapd_iface->conf->bss[0].iface);
+ return -1;
+ }
+
+ wpa_printf(MSG_DEBUG, "Enable interface %s",
+ hapd_iface->conf->bss[0].iface);
+
+ if (hapd_iface->interfaces == NULL ||
+ hapd_iface->interfaces->driver_init == NULL ||
+ hapd_iface->interfaces->driver_init(hapd_iface) ||
+ hostapd_setup_interface(hapd_iface)) {
+ hostapd_interface_deinit_free(hapd_iface);
+ return -1;
+ }
+ return 0;
+}
+
+
+int hostapd_reload_iface(struct hostapd_iface *hapd_iface)
+{
+ size_t j;
+
+ wpa_printf(MSG_DEBUG, "Reload interface %s",
+ hapd_iface->conf->bss[0].iface);
+ for (j = 0; j < hapd_iface->num_bss; j++) {
+ hostapd_flush_old_stations(hapd_iface->bss[j],
+ WLAN_REASON_PREV_AUTH_NOT_VALID);
+
+#ifndef CONFIG_NO_RADIUS
+ /* TODO: update dynamic data based on changed configuration
+ * items (e.g., open/close sockets, etc.) */
+ radius_client_flush(hapd_iface->bss[j]->radius, 0);
+#endif /* CONFIG_NO_RADIUS */
+
+ hostapd_reload_bss(hapd_iface->bss[j]);
+ }
+ return 0;
+}
+
+
+int hostapd_disable_iface(struct hostapd_iface *hapd_iface)
+{
+ size_t j;
+ struct hostapd_bss_config *bss;
+ const struct wpa_driver_ops *driver;
+ void *drv_priv;
+
+ if (hapd_iface == NULL)
+ return -1;
+ bss = hapd_iface->bss[0]->conf;
+ driver = hapd_iface->bss[0]->driver;
+ drv_priv = hapd_iface->bss[0]->drv_priv;
+
+ /* whatever hostapd_interface_deinit does */
+ for (j = 0; j < hapd_iface->num_bss; j++) {
+ struct hostapd_data *hapd = hapd_iface->bss[j];
+ hostapd_free_stas(hapd);
+ hostapd_flush_old_stations(hapd, WLAN_REASON_DEAUTH_LEAVING);
+ hostapd_clear_wep(hapd);
+ hostapd_free_hapd_data(hapd);
+ }
+
+ if (driver && driver->hapd_deinit && drv_priv) {
+ driver->hapd_deinit(drv_priv);
+ hapd_iface->bss[0]->drv_priv = NULL;
+ }
+
+ /* From hostapd_cleanup_iface: These were initialized in
+ * hostapd_setup_interface and hostapd_setup_interface_complete
+ */
+ hostapd_cleanup_iface_partial(hapd_iface);
+ bss->wpa = 0;
+ bss->wpa_key_mgmt = -1;
+ bss->wpa_pairwise = -1;
+
+ wpa_printf(MSG_DEBUG, "Interface %s disabled", bss->iface);
+ return 0;
+}
+
+
+static struct hostapd_iface *
+hostapd_iface_alloc(struct hapd_interfaces *interfaces)
+{
+ struct hostapd_iface **iface, *hapd_iface;
+
+ iface = os_realloc_array(interfaces->iface, interfaces->count + 1,
+ sizeof(struct hostapd_iface *));
+ if (iface == NULL)
+ return NULL;
+ interfaces->iface = iface;
+ hapd_iface = interfaces->iface[interfaces->count] =
+ os_zalloc(sizeof(*hapd_iface));
+ if (hapd_iface == NULL) {
+ wpa_printf(MSG_ERROR, "%s: Failed to allocate memory for "
+ "the interface", __func__);
+ return NULL;
+ }
+ interfaces->count++;
+ hapd_iface->interfaces = interfaces;
+
+ return hapd_iface;
+}
+
+
+static struct hostapd_config *
+hostapd_config_alloc(struct hapd_interfaces *interfaces, const char *ifname,
+ const char *ctrl_iface)
+{
+ struct hostapd_bss_config *bss;
+ struct hostapd_config *conf;
+
+ /* Allocates memory for bss and conf */
+ conf = hostapd_config_defaults();
+ if (conf == NULL) {
+ wpa_printf(MSG_ERROR, "%s: Failed to allocate memory for "
+ "configuration", __func__);
+ return NULL;
+ }
+
+ conf->driver = wpa_drivers[0];
+ if (conf->driver == NULL) {
+ wpa_printf(MSG_ERROR, "No driver wrappers registered!");
+ hostapd_config_free(conf);
+ return NULL;
+ }
+
+ bss = conf->last_bss = conf->bss;
+
+ os_strlcpy(bss->iface, ifname, sizeof(bss->iface));
+ bss->ctrl_interface = os_strdup(ctrl_iface);
+ if (bss->ctrl_interface == NULL) {
+ hostapd_config_free(conf);
+ return NULL;
+ }
+
+ /* Reading configuration file skipped, will be done in SET!
+ * From reading the configuration till the end has to be done in
+ * SET
+ */
+ return conf;
+}
+
+
+static struct hostapd_iface * hostapd_data_alloc(
+ struct hapd_interfaces *interfaces, struct hostapd_config *conf)
+{
+ size_t i;
+ struct hostapd_iface *hapd_iface =
+ interfaces->iface[interfaces->count - 1];
+ struct hostapd_data *hapd;
+
+ hapd_iface->conf = conf;
+ hapd_iface->num_bss = conf->num_bss;
+
+ hapd_iface->bss = os_zalloc(conf->num_bss *
+ sizeof(struct hostapd_data *));
+ if (hapd_iface->bss == NULL)
+ return NULL;
+
+ for (i = 0; i < conf->num_bss; i++) {
+ hapd = hapd_iface->bss[i] =
+ hostapd_alloc_bss_data(hapd_iface, conf,
+ &conf->bss[i]);
+ if (hapd == NULL)
+ return NULL;
+ hapd->msg_ctx = hapd;
+ }
+
+ hapd_iface->interfaces = interfaces;
+
+ return hapd_iface;
+}
+
+
+int hostapd_add_iface(struct hapd_interfaces *interfaces, char *buf)
+{
+ struct hostapd_config *conf = NULL;
+ struct hostapd_iface *hapd_iface = NULL;
+ char *ptr;
+ size_t i;
+
+ ptr = os_strchr(buf, ' ');
+ if (ptr == NULL)
+ return -1;
+ *ptr++ = '\0';
+
+ for (i = 0; i < interfaces->count; i++) {
+ if (!os_strcmp(interfaces->iface[i]->conf->bss[0].iface,
+ buf)) {
+ wpa_printf(MSG_INFO, "Cannot add interface - it "
+ "already exists");
+ return -1;
+ }
+ }
+
+ hapd_iface = hostapd_iface_alloc(interfaces);
+ if (hapd_iface == NULL) {
+ wpa_printf(MSG_ERROR, "%s: Failed to allocate memory "
+ "for interface", __func__);
+ goto fail;
+ }
+
+ conf = hostapd_config_alloc(interfaces, buf, ptr);
+ if (conf == NULL) {
+ wpa_printf(MSG_ERROR, "%s: Failed to allocate memory "
+ "for configuration", __func__);
+ goto fail;
+ }
+
+ hapd_iface = hostapd_data_alloc(interfaces, conf);
+ if (hapd_iface == NULL) {
+ wpa_printf(MSG_ERROR, "%s: Failed to allocate memory "
+ "for hostapd", __func__);
+ goto fail;
+ }
+
+ if (hapd_iface->interfaces &&
+ hapd_iface->interfaces->ctrl_iface_init &&
+ hapd_iface->interfaces->ctrl_iface_init(hapd_iface->bss[0])) {
+ wpa_printf(MSG_ERROR, "%s: Failed to setup control "
+ "interface", __func__);
+ goto fail;
+ }
+ wpa_printf(MSG_INFO, "Add interface '%s'", conf->bss[0].iface);
+
+ return 0;
+
+fail:
+ if (conf)
+ hostapd_config_free(conf);
+ if (hapd_iface) {
+ os_free(hapd_iface->bss[interfaces->count]);
+ os_free(hapd_iface);
+ }
+ return -1;
+}
+
+
+int hostapd_remove_iface(struct hapd_interfaces *interfaces, char *buf)
+{
+ struct hostapd_iface *hapd_iface;
+ size_t i, k = 0;
+
+ for (i = 0; i < interfaces->count; i++) {
+ hapd_iface = interfaces->iface[i];
+ if (hapd_iface == NULL)
+ return -1;
+ if (!os_strcmp(hapd_iface->conf->bss[0].iface, buf)) {
+ wpa_printf(MSG_INFO, "Remove interface '%s'", buf);
+ hostapd_interface_deinit_free(hapd_iface);
+ k = i;
+ while (k < (interfaces->count - 1)) {
+ interfaces->iface[k] =
+ interfaces->iface[k + 1];
+ k++;
+ }
+ interfaces->count--;
+ return 0;
+ }
+ }
+ return -1;
+}
+
+#endif /* HOSTAPD */
+
+
/**
* hostapd_new_assoc_sta - Notify that a new station associated with the AP
* @hapd: Pointer to BSS data
@@ -859,8 +1356,8 @@ void hostapd_new_assoc_sta(struct hostapd_data *hapd, struct sta_info *sta,
int reassoc)
{
if (hapd->tkip_countermeasures) {
- hapd->drv.sta_deauth(hapd, sta->addr,
- WLAN_REASON_MICHAEL_MIC_FAILURE);
+ hostapd_drv_sta_deauth(hapd, sta->addr,
+ WLAN_REASON_MICHAEL_MIC_FAILURE);
return;
}
@@ -870,11 +1367,22 @@ void hostapd_new_assoc_sta(struct hostapd_data *hapd, struct sta_info *sta,
if (hapd->conf->ieee802_11f)
iapp_new_station(hapd->iapp, sta);
+#ifdef CONFIG_P2P
+ if (sta->p2p_ie == NULL && !sta->no_p2p_set) {
+ sta->no_p2p_set = 1;
+ hapd->num_sta_no_p2p++;
+ if (hapd->num_sta_no_p2p == 1)
+ hostapd_p2p_non_p2p_sta_connected(hapd);
+ }
+#endif /* CONFIG_P2P */
+
/* Start accounting here, if IEEE 802.1X and WPA are not used.
* IEEE 802.1X/WPA code will start accounting after the station has
* been authorized. */
- if (!hapd->conf->ieee802_1x && !hapd->conf->wpa)
+ if (!hapd->conf->ieee802_1x && !hapd->conf->wpa) {
+ os_get_time(&sta->connected_time);
accounting_sta_start(hapd, sta);
+ }
/* Start IEEE 802.1X authentication process for new stations */
ieee802_1x_new_station(hapd, sta);
@@ -884,4 +1392,12 @@ void hostapd_new_assoc_sta(struct hostapd_data *hapd, struct sta_info *sta,
wpa_auth_sm_event(sta->wpa_sm, WPA_REAUTH);
} else
wpa_auth_sta_associated(hapd->wpa_auth, sta->wpa_sm);
+
+ wpa_printf(MSG_DEBUG, "%s: reschedule ap_handle_timer timeout "
+ "for " MACSTR " (%d seconds - ap_max_inactivity)",
+ __func__, MAC2STR(sta->addr),
+ hapd->conf->ap_max_inactivity);
+ eloop_cancel_timeout(ap_handle_timer, hapd, sta);
+ eloop_register_timeout(hapd->conf->ap_max_inactivity, 0,
+ ap_handle_timer, hapd, sta);
}
diff --git a/contrib/wpa/src/ap/hostapd.h b/contrib/wpa/src/ap/hostapd.h
index d0d67c8..f1e7d9f 100644
--- a/contrib/wpa/src/ap/hostapd.h
+++ b/contrib/wpa/src/ap/hostapd.h
@@ -2,34 +2,51 @@
* hostapd / Initialization and configuration
* Copyright (c) 2002-2009, Jouni Malinen <j@w1.fi>
*
- * This program is free software; you can redistribute it and/or modify
- * it under the terms of the GNU General Public License version 2 as
- * published by the Free Software Foundation.
- *
- * Alternatively, this software may be distributed under the terms of BSD
- * license.
- *
- * See README and COPYING for more details.
+ * This software may be distributed under the terms of the BSD license.
+ * See README for more details.
*/
#ifndef HOSTAPD_H
#define HOSTAPD_H
#include "common/defs.h"
+#include "ap_config.h"
struct wpa_driver_ops;
struct wpa_ctrl_dst;
struct radius_server_data;
struct upnp_wps_device_sm;
-struct hapd_interfaces;
struct hostapd_data;
struct sta_info;
struct hostap_sta_driver_data;
struct ieee80211_ht_capabilities;
struct full_dynamic_vlan;
+enum wps_event;
+union wps_event_data;
+
+struct hostapd_iface;
+
+struct hapd_interfaces {
+ int (*reload_config)(struct hostapd_iface *iface);
+ struct hostapd_config * (*config_read_cb)(const char *config_fname);
+ int (*ctrl_iface_init)(struct hostapd_data *hapd);
+ void (*ctrl_iface_deinit)(struct hostapd_data *hapd);
+ int (*for_each_interface)(struct hapd_interfaces *interfaces,
+ int (*cb)(struct hostapd_iface *iface,
+ void *ctx), void *ctx);
+ int (*driver_init)(struct hostapd_iface *iface);
+
+ size_t count;
+ int global_ctrl_sock;
+ char *global_iface_path;
+ char *global_iface_name;
+ struct hostapd_iface **iface;
+};
+
struct hostapd_probereq_cb {
- int (*cb)(void *ctx, const u8 *sa, const u8 *ie, size_t ie_len);
+ int (*cb)(void *ctx, const u8 *sa, const u8 *da, const u8 *bssid,
+ const u8 *ie, size_t ie_len, int ssi_signal);
void *ctx;
};
@@ -43,59 +60,10 @@ struct hostapd_rate_data {
struct hostapd_frame_info {
u32 channel;
u32 datarate;
- u32 ssi_signal;
+ int ssi_signal; /* dBm */
};
-struct hostapd_driver_ops {
- int (*set_ap_wps_ie)(struct hostapd_data *hapd);
- int (*send_mgmt_frame)(struct hostapd_data *hapd, const void *msg,
- size_t len);
- int (*send_eapol)(struct hostapd_data *hapd, const u8 *addr,
- const u8 *data, size_t data_len, int encrypt);
- int (*set_authorized)(struct hostapd_data *hapd, struct sta_info *sta,
- int authorized);
- int (*set_key)(const char *ifname, struct hostapd_data *hapd,
- enum wpa_alg alg, const u8 *addr, int key_idx,
- int set_tx, const u8 *seq, size_t seq_len,
- const u8 *key, size_t key_len);
- int (*read_sta_data)(struct hostapd_data *hapd,
- struct hostap_sta_driver_data *data,
- const u8 *addr);
- int (*sta_clear_stats)(struct hostapd_data *hapd, const u8 *addr);
- int (*set_sta_flags)(struct hostapd_data *hapd, struct sta_info *sta);
- int (*set_drv_ieee8021x)(struct hostapd_data *hapd, const char *ifname,
- int enabled);
- int (*set_radius_acl_auth)(struct hostapd_data *hapd,
- const u8 *mac, int accepted,
- u32 session_timeout);
- int (*set_radius_acl_expire)(struct hostapd_data *hapd,
- const u8 *mac);
- int (*set_bss_params)(struct hostapd_data *hapd, int use_protection);
- int (*set_beacon)(struct hostapd_data *hapd,
- const u8 *head, size_t head_len,
- const u8 *tail, size_t tail_len, int dtim_period,
- int beacon_int);
- int (*vlan_if_add)(struct hostapd_data *hapd, const char *ifname);
- int (*vlan_if_remove)(struct hostapd_data *hapd, const char *ifname);
- int (*set_wds_sta)(struct hostapd_data *hapd, const u8 *addr, int aid,
- int val);
- int (*set_sta_vlan)(const char *ifname, struct hostapd_data *hapd,
- const u8 *addr, int vlan_id);
- int (*get_inact_sec)(struct hostapd_data *hapd, const u8 *addr);
- int (*sta_deauth)(struct hostapd_data *hapd, const u8 *addr,
- int reason);
- int (*sta_disassoc)(struct hostapd_data *hapd, const u8 *addr,
- int reason);
- int (*sta_add)(struct hostapd_data *hapd,
- const u8 *addr, u16 aid, u16 capability,
- const u8 *supp_rates, size_t supp_rates_len,
- u16 listen_interval,
- const struct ieee80211_ht_capabilities *ht_capab);
- int (*sta_remove)(struct hostapd_data *hapd, const u8 *addr);
- int (*set_countermeasures)(struct hostapd_data *hapd, int enabled);
-};
-
/**
* struct hostapd_data - hostapd per-BSS data structure
*/
@@ -123,15 +91,16 @@ struct hostapd_data {
const struct wpa_driver_ops *driver;
void *drv_priv;
- struct hostapd_driver_ops drv;
void (*new_assoc_sta_cb)(struct hostapd_data *hapd,
struct sta_info *sta, int reassoc);
void *msg_ctx; /* ctx for wpa_msg() calls */
+ void *msg_ctx_parent; /* parent interface ctx for wpa_msg() calls */
struct radius_client_data *radius;
u32 acct_session_id_hi, acct_session_id_lo;
+ struct radius_das_data *radius_das;
struct iapp_data *iapp;
@@ -155,6 +124,10 @@ struct hostapd_data {
int parameter_set_count;
+ /* Time Advertisement */
+ u8 time_update_counter;
+ struct wpabuf *time_adv;
+
#ifdef CONFIG_FULL_DYNAMIC_VLAN
struct full_dynamic_vlan *full_dynamic_vlan;
#endif /* CONFIG_FULL_DYNAMIC_VLAN */
@@ -162,10 +135,12 @@ struct hostapd_data {
struct l2_packet_data *l2;
struct wps_context *wps;
+ int beacon_set_done;
struct wpabuf *wps_beacon_ie;
struct wpabuf *wps_probe_resp_ie;
#ifdef CONFIG_WPS
unsigned int ap_pin_failures;
+ unsigned int ap_pin_failures_consecutive;
struct upnp_wps_device_sm *wps_upnp;
unsigned int ap_pin_lockout_time;
#endif /* CONFIG_WPS */
@@ -177,9 +152,46 @@ struct hostapd_data {
int freq);
void *public_action_cb_ctx;
+ int (*vendor_action_cb)(void *ctx, const u8 *buf, size_t len,
+ int freq);
+ void *vendor_action_cb_ctx;
+
void (*wps_reg_success_cb)(void *ctx, const u8 *mac_addr,
const u8 *uuid_e);
void *wps_reg_success_cb_ctx;
+
+ void (*wps_event_cb)(void *ctx, enum wps_event event,
+ union wps_event_data *data);
+ void *wps_event_cb_ctx;
+
+ void (*sta_authorized_cb)(void *ctx, const u8 *mac_addr,
+ int authorized, const u8 *p2p_dev_addr);
+ void *sta_authorized_cb_ctx;
+
+ void (*setup_complete_cb)(void *ctx);
+ void *setup_complete_cb_ctx;
+
+#ifdef CONFIG_P2P
+ struct p2p_data *p2p;
+ struct p2p_group *p2p_group;
+ struct wpabuf *p2p_beacon_ie;
+ struct wpabuf *p2p_probe_resp_ie;
+
+ /* Number of non-P2P association stations */
+ int num_sta_no_p2p;
+
+ /* Periodic NoA (used only when no non-P2P clients in the group) */
+ int noa_enabled;
+ int noa_start;
+ int noa_duration;
+#endif /* CONFIG_P2P */
+#ifdef CONFIG_INTERWORKING
+ size_t gas_frag_limit;
+#endif /* CONFIG_INTERWORKING */
+
+#ifdef CONFIG_SQLITE
+ struct hostapd_eap_user tmp_eap_user;
+#endif /* CONFIG_SQLITE */
};
@@ -189,8 +201,6 @@ struct hostapd_data {
struct hostapd_iface {
struct hapd_interfaces *interfaces;
void *owner;
- int (*reload_config)(struct hostapd_iface *iface);
- struct hostapd_config * (*config_read_cb)(const char *config_fname);
char *config_fname;
struct hostapd_config *conf;
@@ -202,6 +212,14 @@ struct hostapd_iface {
struct ap_info *ap_hash[STA_HASH_SIZE];
struct ap_info *ap_iter_list;
+ unsigned int drv_flags;
+
+ /*
+ * A bitmap of supported protocols for probe response offload. See
+ * struct wpa_driver_capa in driver.h
+ */
+ unsigned int probe_resp_offloads;
+
struct hostapd_hw_modes *hw_features;
int num_hw_features;
struct hostapd_hw_modes *current_mode;
@@ -209,6 +227,7 @@ struct hostapd_iface {
* current_mode->channels */
int num_rates;
struct hostapd_rate_data *current_rates;
+ int *basic_rates;
int freq;
u16 hw_flags;
@@ -239,16 +258,12 @@ struct hostapd_iface {
u16 ht_op_mode;
void (*scan_cb)(struct hostapd_iface *iface);
-
- int (*ctrl_iface_init)(struct hostapd_data *hapd);
- void (*ctrl_iface_deinit)(struct hostapd_data *hapd);
-
- int (*for_each_interface)(struct hapd_interfaces *interfaces,
- int (*cb)(struct hostapd_iface *iface,
- void *ctx), void *ctx);
};
/* hostapd.c */
+int hostapd_for_each_interface(struct hapd_interfaces *interfaces,
+ int (*cb)(struct hostapd_iface *iface,
+ void *ctx), void *ctx);
int hostapd_reload_config(struct hostapd_iface *iface);
struct hostapd_data *
hostapd_alloc_bss_data(struct hostapd_iface *hapd_iface,
@@ -260,17 +275,35 @@ void hostapd_interface_deinit(struct hostapd_iface *iface);
void hostapd_interface_free(struct hostapd_iface *iface);
void hostapd_new_assoc_sta(struct hostapd_data *hapd, struct sta_info *sta,
int reassoc);
+void hostapd_interface_deinit_free(struct hostapd_iface *iface);
+int hostapd_enable_iface(struct hostapd_iface *hapd_iface);
+int hostapd_reload_iface(struct hostapd_iface *hapd_iface);
+int hostapd_disable_iface(struct hostapd_iface *hapd_iface);
+int hostapd_add_iface(struct hapd_interfaces *ifaces, char *buf);
+int hostapd_remove_iface(struct hapd_interfaces *ifaces, char *buf);
/* utils.c */
int hostapd_register_probereq_cb(struct hostapd_data *hapd,
int (*cb)(void *ctx, const u8 *sa,
- const u8 *ie, size_t ie_len),
+ const u8 *da, const u8 *bssid,
+ const u8 *ie, size_t ie_len,
+ int ssi_signal),
void *ctx);
void hostapd_prune_associations(struct hostapd_data *hapd, const u8 *addr);
/* drv_callbacks.c (TODO: move to somewhere else?) */
int hostapd_notif_assoc(struct hostapd_data *hapd, const u8 *addr,
- const u8 *ie, size_t ielen);
+ const u8 *ie, size_t ielen, int reassoc);
void hostapd_notif_disassoc(struct hostapd_data *hapd, const u8 *addr);
+void hostapd_event_sta_low_ack(struct hostapd_data *hapd, const u8 *addr);
+int hostapd_probe_req_rx(struct hostapd_data *hapd, const u8 *sa, const u8 *da,
+ const u8 *bssid, const u8 *ie, size_t ie_len,
+ int ssi_signal);
+void hostapd_event_ch_switch(struct hostapd_data *hapd, int freq, int ht,
+ int offset);
+
+const struct hostapd_eap_user *
+hostapd_get_eap_user(struct hostapd_data *hapd, const u8 *identity,
+ size_t identity_len, int phase2);
#endif /* HOSTAPD_H */
diff --git a/contrib/wpa/src/ap/hs20.c b/contrib/wpa/src/ap/hs20.c
new file mode 100644
index 0000000..45d518b
--- /dev/null
+++ b/contrib/wpa/src/ap/hs20.c
@@ -0,0 +1,31 @@
+/*
+ * Hotspot 2.0 AP ANQP processing
+ * Copyright (c) 2009, Atheros Communications, Inc.
+ * Copyright (c) 2011-2012, Qualcomm Atheros, Inc.
+ *
+ * This software may be distributed under the terms of the BSD license.
+ * See README for more details.
+ */
+
+#include "includes.h"
+
+#include "common.h"
+#include "common/ieee802_11_defs.h"
+#include "hostapd.h"
+#include "ap_config.h"
+#include "hs20.h"
+
+
+u8 * hostapd_eid_hs20_indication(struct hostapd_data *hapd, u8 *eid)
+{
+ if (!hapd->conf->hs20)
+ return eid;
+ *eid++ = WLAN_EID_VENDOR_SPECIFIC;
+ *eid++ = 5;
+ WPA_PUT_BE24(eid, OUI_WFA);
+ eid += 3;
+ *eid++ = HS20_INDICATION_OUI_TYPE;
+ /* Hotspot Configuration: DGAF Enabled */
+ *eid++ = hapd->conf->disable_dgaf ? 0x01 : 0x00;
+ return eid;
+}
diff --git a/contrib/wpa/src/ap/hs20.h b/contrib/wpa/src/ap/hs20.h
new file mode 100644
index 0000000..98698ce
--- /dev/null
+++ b/contrib/wpa/src/ap/hs20.h
@@ -0,0 +1,16 @@
+/*
+ * Hotspot 2.0 AP ANQP processing
+ * Copyright (c) 2011-2012, Qualcomm Atheros, Inc.
+ *
+ * This software may be distributed under the terms of the BSD license.
+ * See README for more details.
+ */
+
+#ifndef HS20_H
+#define HS20_H
+
+struct hostapd_data;
+
+u8 * hostapd_eid_hs20_indication(struct hostapd_data *hapd, u8 *eid);
+
+#endif /* HS20_H */
diff --git a/contrib/wpa/src/ap/hw_features.c b/contrib/wpa/src/ap/hw_features.c
index 0159c72..923b698 100644
--- a/contrib/wpa/src/ap/hw_features.c
+++ b/contrib/wpa/src/ap/hw_features.c
@@ -2,7 +2,7 @@
* hostapd / Hardware feature query and different modes
* Copyright 2002-2003, Instant802 Networks, Inc.
* Copyright 2005-2006, Devicescape Software, Inc.
- * Copyright (c) 2008-2009, Jouni Malinen <j@w1.fi>
+ * Copyright (c) 2008-2012, Jouni Malinen <j@w1.fi>
*
* This program is free software; you can redistribute it and/or modify
* it under the terms of the GNU General Public License version 2 as
@@ -101,8 +101,8 @@ int hostapd_get_hw_features(struct hostapd_iface *iface)
}
-static int hostapd_prepare_rates(struct hostapd_data *hapd,
- struct hostapd_hw_modes *mode)
+int hostapd_prepare_rates(struct hostapd_iface *iface,
+ struct hostapd_hw_modes *mode)
{
int i, num_basic_rates = 0;
int basic_rates_a[] = { 60, 120, 240, -1 };
@@ -110,8 +110,8 @@ static int hostapd_prepare_rates(struct hostapd_data *hapd,
int basic_rates_g[] = { 10, 20, 55, 110, -1 };
int *basic_rates;
- if (hapd->iconf->basic_rates)
- basic_rates = hapd->iconf->basic_rates;
+ if (iface->conf->basic_rates)
+ basic_rates = iface->conf->basic_rates;
else switch (mode->mode) {
case HOSTAPD_MODE_IEEE80211A:
basic_rates = basic_rates_a;
@@ -122,22 +122,28 @@ static int hostapd_prepare_rates(struct hostapd_data *hapd,
case HOSTAPD_MODE_IEEE80211G:
basic_rates = basic_rates_g;
break;
+ case HOSTAPD_MODE_IEEE80211AD:
+ return 0; /* No basic rates for 11ad */
default:
return -1;
}
- if (hostapd_set_rate_sets(hapd, hapd->iconf->supported_rates,
- basic_rates, mode->mode)) {
- wpa_printf(MSG_ERROR, "Failed to update rate sets in kernel "
- "module");
- }
+ i = 0;
+ while (basic_rates[i] >= 0)
+ i++;
+ if (i)
+ i++; /* -1 termination */
+ os_free(iface->basic_rates);
+ iface->basic_rates = os_malloc(i * sizeof(int));
+ if (iface->basic_rates)
+ os_memcpy(iface->basic_rates, basic_rates, i * sizeof(int));
- os_free(hapd->iface->current_rates);
- hapd->iface->num_rates = 0;
+ os_free(iface->current_rates);
+ iface->num_rates = 0;
- hapd->iface->current_rates =
- os_zalloc(mode->num_rates * sizeof(struct hostapd_rate_data));
- if (!hapd->iface->current_rates) {
+ iface->current_rates =
+ os_calloc(mode->num_rates, sizeof(struct hostapd_rate_data));
+ if (!iface->current_rates) {
wpa_printf(MSG_ERROR, "Failed to allocate memory for rate "
"table.");
return -1;
@@ -146,26 +152,27 @@ static int hostapd_prepare_rates(struct hostapd_data *hapd,
for (i = 0; i < mode->num_rates; i++) {
struct hostapd_rate_data *rate;
- if (hapd->iconf->supported_rates &&
- !hostapd_rate_found(hapd->iconf->supported_rates,
+ if (iface->conf->supported_rates &&
+ !hostapd_rate_found(iface->conf->supported_rates,
mode->rates[i]))
continue;
- rate = &hapd->iface->current_rates[hapd->iface->num_rates];
+ rate = &iface->current_rates[iface->num_rates];
rate->rate = mode->rates[i];
if (hostapd_rate_found(basic_rates, rate->rate)) {
rate->flags |= HOSTAPD_RATE_BASIC;
num_basic_rates++;
}
wpa_printf(MSG_DEBUG, "RATE[%d] rate=%d flags=0x%x",
- hapd->iface->num_rates, rate->rate, rate->flags);
- hapd->iface->num_rates++;
+ iface->num_rates, rate->rate, rate->flags);
+ iface->num_rates++;
}
- if (hapd->iface->num_rates == 0 || num_basic_rates == 0) {
+ if ((iface->num_rates == 0 || num_basic_rates == 0) &&
+ (!iface->conf->ieee80211n || !iface->conf->require_ht)) {
wpa_printf(MSG_ERROR, "No rates remaining in supported/basic "
"rate sets (%d,%d).",
- hapd->iface->num_rates, num_basic_rates);
+ iface->num_rates, num_basic_rates);
return -1;
}
@@ -265,11 +272,11 @@ static void ieee80211n_get_pri_sec_chan(struct wpa_scan_res *bss,
oper = (struct ieee80211_ht_operation *) elems.ht_operation;
*pri_chan = oper->control_chan;
if (oper->ht_param & HT_INFO_HT_PARAM_REC_TRANS_CHNL_WIDTH) {
- if (oper->ht_param &
- HT_INFO_HT_PARAM_SECONDARY_CHNL_ABOVE)
+ int sec = oper->ht_param &
+ HT_INFO_HT_PARAM_SECONDARY_CHNL_OFF_MASK;
+ if (sec == HT_INFO_HT_PARAM_SECONDARY_CHNL_ABOVE)
*sec_chan = *pri_chan + 4;
- else if (oper->ht_param &
- HT_INFO_HT_PARAM_SECONDARY_CHNL_BELOW)
+ else if (sec == HT_INFO_HT_PARAM_SECONDARY_CHNL_BELOW)
*sec_chan = *pri_chan - 4;
}
}
@@ -406,27 +413,14 @@ static int ieee80211n_check_40mhz_2g4(struct hostapd_iface *iface,
}
-static void wpa_scan_results_free(struct wpa_scan_results *res)
-{
- size_t i;
-
- if (res == NULL)
- return;
-
- for (i = 0; i < res->num; i++)
- os_free(res->res[i]);
- os_free(res->res);
- os_free(res);
-}
-
-
static void ieee80211n_check_scan(struct hostapd_iface *iface)
{
struct wpa_scan_results *scan_res;
int oper40;
+ int res;
/* Check list of neighboring BSSes (from scan) to see whether 40 MHz is
- * allowed per IEEE 802.11n/D7.0, 11.14.3.2 */
+ * allowed per IEEE Std 802.11-2012, 10.15.3.2 */
iface->scan_cb = NULL;
@@ -452,7 +446,48 @@ static void ieee80211n_check_scan(struct hostapd_iface *iface)
iface->conf->ht_capab &= ~HT_CAP_INFO_SUPP_CHANNEL_WIDTH_SET;
}
- hostapd_setup_interface_complete(iface, 0);
+ res = ieee80211n_allowed_ht40_channel_pair(iface);
+ hostapd_setup_interface_complete(iface, !res);
+}
+
+
+static void ieee80211n_scan_channels_2g4(struct hostapd_iface *iface,
+ struct wpa_driver_scan_params *params)
+{
+ /* Scan only the affected frequency range */
+ int pri_freq, sec_freq;
+ int affected_start, affected_end;
+ int i, pos;
+ struct hostapd_hw_modes *mode;
+
+ if (iface->current_mode == NULL)
+ return;
+
+ pri_freq = hostapd_hw_get_freq(iface->bss[0], iface->conf->channel);
+ if (iface->conf->secondary_channel > 0)
+ sec_freq = pri_freq + 20;
+ else
+ sec_freq = pri_freq - 20;
+ affected_start = (pri_freq + sec_freq) / 2 - 25;
+ affected_end = (pri_freq + sec_freq) / 2 + 25;
+ wpa_printf(MSG_DEBUG, "40 MHz affected channel range: [%d,%d] MHz",
+ affected_start, affected_end);
+
+ mode = iface->current_mode;
+ params->freqs = os_calloc(mode->num_channels + 1, sizeof(int));
+ if (params->freqs == NULL)
+ return;
+ pos = 0;
+
+ for (i = 0; i < mode->num_channels; i++) {
+ struct hostapd_channel_data *chan = &mode->channels[i];
+ if (chan->flag & HOSTAPD_CHAN_DISABLED)
+ continue;
+ if (chan->freq < affected_start ||
+ chan->freq > affected_end)
+ continue;
+ params->freqs[pos++] = chan->freq;
+ }
}
@@ -466,12 +501,15 @@ static int ieee80211n_check_40mhz(struct hostapd_iface *iface)
wpa_printf(MSG_DEBUG, "Scan for neighboring BSSes prior to enabling "
"40 MHz channel");
os_memset(&params, 0, sizeof(params));
- /* TODO: scan only the needed frequency */
+ if (iface->current_mode->mode == HOSTAPD_MODE_IEEE80211G)
+ ieee80211n_scan_channels_2g4(iface, &params);
if (hostapd_driver_scan(iface->bss[0], &params) < 0) {
wpa_printf(MSG_ERROR, "Failed to request a scan of "
"neighboring BSSes");
+ os_free(params.freqs);
return -1;
}
+ os_free(params.freqs);
iface->scan_cb = ieee80211n_check_scan;
return 1;
@@ -483,9 +521,6 @@ static int ieee80211n_supported_ht_capab(struct hostapd_iface *iface)
u16 hw = iface->current_mode->ht_capab;
u16 conf = iface->conf->ht_capab;
- if (!iface->conf->ieee80211n)
- return 1;
-
if ((conf & HT_CAP_INFO_LDPC_CODING_CAP) &&
!(hw & HT_CAP_INFO_LDPC_CODING_CAP)) {
wpa_printf(MSG_ERROR, "Driver does not support configured "
@@ -585,13 +620,15 @@ int hostapd_check_ht_capab(struct hostapd_iface *iface)
{
#ifdef CONFIG_IEEE80211N
int ret;
+ if (!iface->conf->ieee80211n)
+ return 0;
+ if (!ieee80211n_supported_ht_capab(iface))
+ return -1;
ret = ieee80211n_check_40mhz(iface);
if (ret)
return ret;
if (!ieee80211n_allowed_ht40_channel_pair(iface))
return -1;
- if (!ieee80211n_supported_ht_capab(iface))
- return -1;
#endif /* CONFIG_IEEE80211N */
return 0;
@@ -601,7 +638,7 @@ int hostapd_check_ht_capab(struct hostapd_iface *iface)
/**
* hostapd_select_hw_mode - Select the hardware mode
* @iface: Pointer to interface data.
- * Returns: 0 on success, -1 on failure
+ * Returns: 0 on success, < 0 on failure
*
* Sets up the hardware mode, channel, rates, and passive scanning
* based on the configuration.
@@ -628,18 +665,58 @@ int hostapd_select_hw_mode(struct hostapd_iface *iface)
hostapd_logger(iface->bss[0], NULL, HOSTAPD_MODULE_IEEE80211,
HOSTAPD_LEVEL_WARNING,
"Hardware does not support configured mode "
- "(%d)", (int) iface->conf->hw_mode);
- return -1;
+ "(%d) (hw_mode in hostapd.conf)",
+ (int) iface->conf->hw_mode);
+ return -2;
}
ok = 0;
for (j = 0; j < iface->current_mode->num_channels; j++) {
struct hostapd_channel_data *chan =
&iface->current_mode->channels[j];
- if (!(chan->flag & HOSTAPD_CHAN_DISABLED) &&
- (chan->chan == iface->conf->channel)) {
- ok = 1;
- break;
+ if (chan->chan == iface->conf->channel) {
+ if (chan->flag & HOSTAPD_CHAN_DISABLED) {
+ wpa_printf(MSG_ERROR,
+ "channel [%i] (%i) is disabled for "
+ "use in AP mode, flags: 0x%x%s%s%s",
+ j, chan->chan, chan->flag,
+ chan->flag & HOSTAPD_CHAN_NO_IBSS ?
+ " NO-IBSS" : "",
+ chan->flag &
+ HOSTAPD_CHAN_PASSIVE_SCAN ?
+ " PASSIVE-SCAN" : "",
+ chan->flag & HOSTAPD_CHAN_RADAR ?
+ " RADAR" : "");
+ } else {
+ ok = 1;
+ break;
+ }
+ }
+ }
+ if (ok && iface->conf->secondary_channel) {
+ int sec_ok = 0;
+ int sec_chan = iface->conf->channel +
+ iface->conf->secondary_channel * 4;
+ for (j = 0; j < iface->current_mode->num_channels; j++) {
+ struct hostapd_channel_data *chan =
+ &iface->current_mode->channels[j];
+ if (!(chan->flag & HOSTAPD_CHAN_DISABLED) &&
+ (chan->chan == sec_chan)) {
+ sec_ok = 1;
+ break;
+ }
+ }
+ if (!sec_ok) {
+ hostapd_logger(iface->bss[0], NULL,
+ HOSTAPD_MODULE_IEEE80211,
+ HOSTAPD_LEVEL_WARNING,
+ "Configured HT40 secondary channel "
+ "(%d) not found from the channel list "
+ "of current mode (%d) %s",
+ sec_chan, iface->current_mode->mode,
+ hostapd_hw_mode_txt(
+ iface->current_mode->mode));
+ ok = 0;
}
}
if (iface->conf->channel == 0) {
@@ -647,7 +724,7 @@ int hostapd_select_hw_mode(struct hostapd_iface *iface)
* the channel automatically */
wpa_printf(MSG_ERROR, "Channel not configured "
"(hw_mode/channel in hostapd.conf)");
- return -1;
+ return -3;
}
if (ok == 0 && iface->conf->channel != 0) {
hostapd_logger(iface->bss[0], NULL,
@@ -665,15 +742,7 @@ int hostapd_select_hw_mode(struct hostapd_iface *iface)
hostapd_logger(iface->bss[0], NULL, HOSTAPD_MODULE_IEEE80211,
HOSTAPD_LEVEL_WARNING,
"Hardware does not support configured channel");
- return -1;
- }
-
- if (hostapd_prepare_rates(iface->bss[0], iface->current_mode)) {
- wpa_printf(MSG_ERROR, "Failed to prepare rates table.");
- hostapd_logger(iface->bss[0], NULL, HOSTAPD_MODULE_IEEE80211,
- HOSTAPD_LEVEL_WARNING,
- "Failed to prepare rates table.");
- return -1;
+ return -4;
}
return 0;
@@ -689,6 +758,8 @@ const char * hostapd_hw_mode_txt(int mode)
return "IEEE 802.11b";
case HOSTAPD_MODE_IEEE80211G:
return "IEEE 802.11g";
+ case HOSTAPD_MODE_IEEE80211AD:
+ return "IEEE 802.11ad";
default:
return "UNKNOWN";
}
diff --git a/contrib/wpa/src/ap/hw_features.h b/contrib/wpa/src/ap/hw_features.h
index 0295549..abadcd1 100644
--- a/contrib/wpa/src/ap/hw_features.h
+++ b/contrib/wpa/src/ap/hw_features.h
@@ -2,6 +2,7 @@
* hostapd / Hardware feature query and different modes
* Copyright 2002-2003, Instant802 Networks, Inc.
* Copyright 2005-2006, Devicescape Software, Inc.
+ * Copyright (c) 2008-2011, Jouni Malinen <j@w1.fi>
*
* This program is free software; you can redistribute it and/or modify
* it under the terms of the GNU General Public License version 2 as
@@ -25,6 +26,8 @@ const char * hostapd_hw_mode_txt(int mode);
int hostapd_hw_get_freq(struct hostapd_data *hapd, int chan);
int hostapd_hw_get_channel(struct hostapd_data *hapd, int freq);
int hostapd_check_ht_capab(struct hostapd_iface *iface);
+int hostapd_prepare_rates(struct hostapd_iface *iface,
+ struct hostapd_hw_modes *mode);
#else /* NEED_AP_MLME */
static inline void
hostapd_free_hw_features(struct hostapd_hw_modes *hw_features,
@@ -39,7 +42,7 @@ static inline int hostapd_get_hw_features(struct hostapd_iface *iface)
static inline int hostapd_select_hw_mode(struct hostapd_iface *iface)
{
- return -1;
+ return -100;
}
static inline const char * hostapd_hw_mode_txt(int mode)
@@ -57,6 +60,12 @@ static inline int hostapd_check_ht_capab(struct hostapd_iface *iface)
return 0;
}
+static inline int hostapd_prepare_rates(struct hostapd_iface *iface,
+ struct hostapd_hw_modes *mode)
+{
+ return 0;
+}
+
#endif /* NEED_AP_MLME */
#endif /* HW_FEATURES_H */
diff --git a/contrib/wpa/src/ap/iapp.c b/contrib/wpa/src/ap/iapp.c
index 115d91e..be55c69 100644
--- a/contrib/wpa/src/ap/iapp.c
+++ b/contrib/wpa/src/ap/iapp.c
@@ -2,14 +2,8 @@
* hostapd / IEEE 802.11F-2003 Inter-Access Point Protocol (IAPP)
* Copyright (c) 2002-2007, Jouni Malinen <j@w1.fi>
*
- * This program is free software; you can redistribute it and/or modify
- * it under the terms of the GNU General Public License version 2 as
- * published by the Free Software Foundation.
- *
- * Alternatively, this software may be distributed under the terms of BSD
- * license.
- *
- * See README and COPYING for more details.
+ * This software may be distributed under the terms of the BSD license.
+ * See README for more details.
*
* Note: IEEE 802.11F-2003 was a experimental use specification. It has expired
* and IEEE has withdrawn it. In other words, it is likely better to look at
diff --git a/contrib/wpa/src/ap/iapp.h b/contrib/wpa/src/ap/iapp.h
index 5fc01cb..c221183 100644
--- a/contrib/wpa/src/ap/iapp.h
+++ b/contrib/wpa/src/ap/iapp.h
@@ -2,14 +2,8 @@
* hostapd / IEEE 802.11F-2003 Inter-Access Point Protocol (IAPP)
* Copyright (c) 2002-2005, Jouni Malinen <j@w1.fi>
*
- * This program is free software; you can redistribute it and/or modify
- * it under the terms of the GNU General Public License version 2 as
- * published by the Free Software Foundation.
- *
- * Alternatively, this software may be distributed under the terms of BSD
- * license.
- *
- * See README and COPYING for more details.
+ * This software may be distributed under the terms of the BSD license.
+ * See README for more details.
*/
#ifndef IAPP_H
diff --git a/contrib/wpa/src/ap/ieee802_11.c b/contrib/wpa/src/ap/ieee802_11.c
index 3375aa2..51c8d28 100644
--- a/contrib/wpa/src/ap/ieee802_11.c
+++ b/contrib/wpa/src/ap/ieee802_11.c
@@ -1,15 +1,9 @@
/*
* hostapd / IEEE 802.11 Management
- * Copyright (c) 2002-2010, Jouni Malinen <j@w1.fi>
+ * Copyright (c) 2002-2012, Jouni Malinen <j@w1.fi>
*
- * This program is free software; you can redistribute it and/or modify
- * it under the terms of the GNU General Public License version 2 as
- * published by the Free Software Foundation.
- *
- * Alternatively, this software may be distributed under the terms of BSD
- * license.
- *
- * See README and COPYING for more details.
+ * This software may be distributed under the terms of the BSD license.
+ * See README for more details.
*/
#include "utils/includes.h"
@@ -25,6 +19,7 @@
#include "common/wpa_ctrl.h"
#include "radius/radius.h"
#include "radius/radius_client.h"
+#include "p2p/p2p.h"
#include "wps/wps.h"
#include "hostapd.h"
#include "beacon.h"
@@ -37,6 +32,9 @@
#include "accounting.h"
#include "ap_config.h"
#include "ap_mlme.h"
+#include "p2p_hostapd.h"
+#include "ap_drv_ops.h"
+#include "wnm_ap.h"
#include "ieee802_11.h"
@@ -50,6 +48,10 @@ u8 * hostapd_eid_supp_rates(struct hostapd_data *hapd, u8 *eid)
*pos++ = WLAN_EID_SUPP_RATES;
num = hapd->iface->num_rates;
+ if (hapd->iconf->ieee80211n && hapd->iconf->require_ht)
+ num++;
+ if (hapd->iconf->ieee80211ac && hapd->iconf->require_vht)
+ num++;
if (num > 8) {
/* rest of the rates are encoded in Extended supported
* rates element */
@@ -67,6 +69,16 @@ u8 * hostapd_eid_supp_rates(struct hostapd_data *hapd, u8 *eid)
pos++;
}
+ if (hapd->iconf->ieee80211n && hapd->iconf->require_ht && count < 8) {
+ count++;
+ *pos++ = 0x80 | BSS_MEMBERSHIP_SELECTOR_HT_PHY;
+ }
+
+ if (hapd->iconf->ieee80211ac && hapd->iconf->require_vht && count < 8) {
+ count++;
+ *pos++ = 0x80 | BSS_MEMBERSHIP_SELECTOR_VHT_PHY;
+ }
+
return pos;
}
@@ -80,6 +92,10 @@ u8 * hostapd_eid_ext_supp_rates(struct hostapd_data *hapd, u8 *eid)
return eid;
num = hapd->iface->num_rates;
+ if (hapd->iconf->ieee80211n && hapd->iconf->require_ht)
+ num++;
+ if (hapd->iconf->ieee80211ac && hapd->iconf->require_vht)
+ num++;
if (num <= 8)
return eid;
num -= 8;
@@ -98,6 +114,18 @@ u8 * hostapd_eid_ext_supp_rates(struct hostapd_data *hapd, u8 *eid)
pos++;
}
+ if (hapd->iconf->ieee80211n && hapd->iconf->require_ht) {
+ count++;
+ if (count > 8)
+ *pos++ = 0x80 | BSS_MEMBERSHIP_SELECTOR_HT_PHY;
+ }
+
+ if (hapd->iconf->ieee80211ac && hapd->iconf->require_vht) {
+ count++;
+ if (count > 8)
+ *pos++ = 0x80 | BSS_MEMBERSHIP_SELECTOR_VHT_PHY;
+ }
+
return pos;
}
@@ -148,34 +176,6 @@ u16 hostapd_own_capab_info(struct hostapd_data *hapd, struct sta_info *sta,
}
-#ifdef CONFIG_IEEE80211W
-static u8 * hostapd_eid_assoc_comeback_time(struct hostapd_data *hapd,
- struct sta_info *sta, u8 *eid)
-{
- u8 *pos = eid;
- u32 timeout, tu;
- struct os_time now, passed;
-
- *pos++ = WLAN_EID_TIMEOUT_INTERVAL;
- *pos++ = 5;
- *pos++ = WLAN_TIMEOUT_ASSOC_COMEBACK;
- os_get_time(&now);
- os_time_sub(&now, &sta->sa_query_start, &passed);
- tu = (passed.sec * 1000000 + passed.usec) / 1024;
- if (hapd->conf->assoc_sa_query_max_timeout > tu)
- timeout = hapd->conf->assoc_sa_query_max_timeout - tu;
- else
- timeout = 0;
- if (timeout < hapd->conf->assoc_sa_query_max_timeout)
- timeout++; /* add some extra time for local timers */
- WPA_PUT_LE32(pos, timeout);
- pos += 4;
-
- return pos;
-}
-#endif /* CONFIG_IEEE80211W */
-
-
void ieee802_11_print_ssid(char *buf, const u8 *ssid, u8 len)
{
int i;
@@ -204,15 +204,15 @@ static u16 auth_shared_key(struct hostapd_data *hapd, struct sta_info *sta,
if (!sta->challenge) {
/* Generate a pseudo-random challenge */
u8 key[8];
- time_t now;
+ struct os_time now;
int r;
sta->challenge = os_zalloc(WLAN_AUTH_CHALLENGE_LEN);
if (sta->challenge == NULL)
return WLAN_STATUS_UNSPECIFIED_FAILURE;
- now = time(NULL);
- r = random();
- os_memcpy(key, &now, 4);
+ os_get_time(&now);
+ r = os_random();
+ os_memcpy(key, &now.sec, 4);
os_memcpy(key + 4, &r, 4);
rc4_skip(key, sizeof(key), 0,
sta->challenge, WLAN_AUTH_CHALLENGE_LEN);
@@ -282,7 +282,7 @@ static void send_auth_reply(struct hostapd_data *hapd,
" auth_alg=%d auth_transaction=%d resp=%d (IE len=%lu)",
MAC2STR(dst), auth_alg, auth_transaction,
resp, (unsigned long) ies_len);
- if (hapd->drv.send_mgmt_frame(hapd, reply, rlen) < 0)
+ if (hostapd_drv_send_mlme(hapd, reply, rlen, 0) < 0)
perror("send_auth_reply: send");
os_free(buf);
@@ -315,6 +315,142 @@ static void handle_auth_ft_finish(void *ctx, const u8 *dst, const u8 *bssid,
#endif /* CONFIG_IEEE80211R */
+#ifdef CONFIG_SAE
+
+static struct wpabuf * auth_build_sae_commit(struct hostapd_data *hapd,
+ struct sta_info *sta)
+{
+ struct wpabuf *buf;
+
+ buf = wpabuf_alloc(2);
+ if (buf == NULL)
+ return NULL;
+
+ wpabuf_put_le16(buf, 19); /* Finite Cyclic Group */
+ /* TODO: Anti-Clogging Token (if requested) */
+ /* TODO: Scalar */
+ /* TODO: Element */
+
+ return buf;
+}
+
+
+static struct wpabuf * auth_build_sae_confirm(struct hostapd_data *hapd,
+ struct sta_info *sta)
+{
+ struct wpabuf *buf;
+
+ buf = wpabuf_alloc(2);
+ if (buf == NULL)
+ return NULL;
+
+ wpabuf_put_le16(buf, sta->sae_send_confirm);
+ sta->sae_send_confirm++;
+ /* TODO: Confirm */
+
+ return buf;
+}
+
+
+static u16 handle_sae_commit(struct hostapd_data *hapd, struct sta_info *sta,
+ const u8 *data, size_t len)
+{
+ wpa_hexdump(MSG_DEBUG, "SAE commit fields", data, len);
+
+ /* Check Finite Cyclic Group */
+ if (len < 2)
+ return WLAN_STATUS_UNSPECIFIED_FAILURE;
+ if (WPA_GET_LE16(data) != 19) {
+ wpa_printf(MSG_DEBUG, "SAE: Unsupported Finite Cyclic Group %u",
+ WPA_GET_LE16(data));
+ return WLAN_STATUS_FINITE_CYCLIC_GROUP_NOT_SUPPORTED;
+ }
+
+ return WLAN_STATUS_SUCCESS;
+}
+
+
+static u16 handle_sae_confirm(struct hostapd_data *hapd, struct sta_info *sta,
+ const u8 *data, size_t len)
+{
+ u16 rc;
+
+ wpa_hexdump(MSG_DEBUG, "SAE confirm fields", data, len);
+
+ if (len < 2)
+ return WLAN_STATUS_UNSPECIFIED_FAILURE;
+ rc = WPA_GET_LE16(data);
+ wpa_printf(MSG_DEBUG, "SAE: peer-send-confirm %u", rc);
+
+ return WLAN_STATUS_SUCCESS;
+}
+
+
+static void handle_auth_sae(struct hostapd_data *hapd, struct sta_info *sta,
+ const struct ieee80211_mgmt *mgmt, size_t len,
+ u8 auth_transaction)
+{
+ u16 resp = WLAN_STATUS_SUCCESS;
+ struct wpabuf *data;
+
+ if (auth_transaction == 1) {
+ hostapd_logger(hapd, sta->addr, HOSTAPD_MODULE_IEEE80211,
+ HOSTAPD_LEVEL_DEBUG,
+ "start SAE authentication (RX commit)");
+ resp = handle_sae_commit(hapd, sta, mgmt->u.auth.variable,
+ ((u8 *) mgmt) + len -
+ mgmt->u.auth.variable);
+ if (resp == WLAN_STATUS_SUCCESS)
+ sta->sae_state = SAE_COMMIT;
+ } else if (auth_transaction == 2) {
+ if (sta->sae_state != SAE_COMMIT) {
+ hostapd_logger(hapd, sta->addr,
+ HOSTAPD_MODULE_IEEE80211,
+ HOSTAPD_LEVEL_DEBUG,
+ "SAE confirm before commit");
+ resp = WLAN_STATUS_UNKNOWN_AUTH_TRANSACTION;
+ }
+ hostapd_logger(hapd, sta->addr, HOSTAPD_MODULE_IEEE80211,
+ HOSTAPD_LEVEL_DEBUG,
+ "SAE authentication (RX confirm)");
+ resp = handle_sae_confirm(hapd, sta, mgmt->u.auth.variable,
+ ((u8 *) mgmt) + len -
+ mgmt->u.auth.variable);
+ if (resp == WLAN_STATUS_SUCCESS) {
+ sta->flags |= WLAN_STA_AUTH;
+ wpa_auth_sm_event(sta->wpa_sm, WPA_AUTH);
+ sta->auth_alg = WLAN_AUTH_SAE;
+ mlme_authenticate_indication(hapd, sta);
+ }
+ } else {
+ hostapd_logger(hapd, sta->addr, HOSTAPD_MODULE_IEEE80211,
+ HOSTAPD_LEVEL_DEBUG,
+ "unexpected SAE authentication transaction %u",
+ auth_transaction);
+ resp = WLAN_STATUS_UNKNOWN_AUTH_TRANSACTION;
+ }
+
+ sta->auth_alg = WLAN_AUTH_SAE;
+
+ if (resp == WLAN_STATUS_SUCCESS) {
+ if (auth_transaction == 1)
+ data = auth_build_sae_commit(hapd, sta);
+ else
+ data = auth_build_sae_confirm(hapd, sta);
+ if (data == NULL)
+ resp = WLAN_STATUS_UNSPECIFIED_FAILURE;
+ } else
+ data = NULL;
+
+ send_auth_reply(hapd, mgmt->sa, mgmt->bssid, WLAN_AUTH_SAE,
+ auth_transaction, resp,
+ data ? wpabuf_head(data) : (u8 *) "",
+ data ? wpabuf_len(data) : 0);
+ wpabuf_free(data);
+}
+#endif /* CONFIG_SAE */
+
+
static void handle_auth(struct hostapd_data *hapd,
const struct ieee80211_mgmt *mgmt, size_t len)
{
@@ -326,8 +462,11 @@ static void handle_auth(struct hostapd_data *hapd,
const u8 *challenge = NULL;
u32 session_timeout, acct_interim_interval;
int vlan_id = 0;
+ struct hostapd_sta_wpa_psk_short *psk = NULL;
u8 resp_ies[2 + WLAN_AUTH_CHALLENGE_LEN];
size_t resp_ies_len = 0;
+ char *identity = NULL;
+ char *radius_cui = NULL;
if (len < IEEE80211_HDRLEN + sizeof(mgmt->u.auth)) {
printf("handle_auth - too short payload (len=%lu)\n",
@@ -360,11 +499,13 @@ static void handle_auth(struct hostapd_data *hapd,
if (!(((hapd->conf->auth_algs & WPA_AUTH_ALG_OPEN) &&
auth_alg == WLAN_AUTH_OPEN) ||
#ifdef CONFIG_IEEE80211R
- (hapd->conf->wpa &&
- (hapd->conf->wpa_key_mgmt &
- (WPA_KEY_MGMT_FT_IEEE8021X | WPA_KEY_MGMT_FT_PSK)) &&
+ (hapd->conf->wpa && wpa_key_mgmt_ft(hapd->conf->wpa_key_mgmt) &&
auth_alg == WLAN_AUTH_FT) ||
#endif /* CONFIG_IEEE80211R */
+#ifdef CONFIG_SAE
+ (hapd->conf->wpa && wpa_key_mgmt_sae(hapd->conf->wpa_key_mgmt) &&
+ auth_alg == WLAN_AUTH_SAE) ||
+#endif /* CONFIG_SAE */
((hapd->conf->auth_algs & WPA_AUTH_ALG_SHARED) &&
auth_alg == WLAN_AUTH_SHARED_KEY))) {
printf("Unsupported authentication algorithm (%d)\n",
@@ -373,7 +514,7 @@ static void handle_auth(struct hostapd_data *hapd,
goto fail;
}
- if (!(auth_transaction == 1 ||
+ if (!(auth_transaction == 1 || auth_alg == WLAN_AUTH_SAE ||
(auth_alg == WLAN_AUTH_SHARED_KEY && auth_transaction == 3))) {
printf("Unknown authentication transaction number (%d)\n",
auth_transaction);
@@ -390,7 +531,9 @@ static void handle_auth(struct hostapd_data *hapd,
res = hostapd_allowed_address(hapd, mgmt->sa, (u8 *) mgmt, len,
&session_timeout,
- &acct_interim_interval, &vlan_id);
+ &acct_interim_interval, &vlan_id,
+ &psk, &identity, &radius_cui);
+
if (res == HOSTAPD_ACL_REJECT) {
printf("Station " MACSTR " not allowed to authenticate.\n",
MAC2STR(mgmt->sa));
@@ -428,6 +571,19 @@ static void handle_auth(struct hostapd_data *hapd,
HOSTAPD_LEVEL_INFO, "VLAN ID %d", sta->vlan_id);
}
+ hostapd_free_psk_list(sta->psk);
+ if (hapd->conf->wpa_psk_radius != PSK_RADIUS_IGNORED) {
+ sta->psk = psk;
+ psk = NULL;
+ } else {
+ sta->psk = NULL;
+ }
+
+ sta->identity = identity;
+ identity = NULL;
+ sta->radius_cui = radius_cui;
+ radius_cui = NULL;
+
sta->flags &= ~WLAN_STA_PREAUTH;
ieee802_1x_notify_pre_auth(sta->eapol_sm, 0);
@@ -486,9 +642,18 @@ static void handle_auth(struct hostapd_data *hapd,
/* handle_auth_ft_finish() callback will complete auth. */
return;
#endif /* CONFIG_IEEE80211R */
+#ifdef CONFIG_SAE
+ case WLAN_AUTH_SAE:
+ handle_auth_sae(hapd, sta, mgmt, len, auth_transaction);
+ return;
+#endif /* CONFIG_SAE */
}
fail:
+ os_free(identity);
+ os_free(radius_cui);
+ hostapd_free_psk_list(psk);
+
send_auth_reply(hapd, mgmt->sa, mgmt->bssid, auth_alg,
auth_transaction + 1, resp, resp_ies, resp_ies_len);
}
@@ -552,15 +717,22 @@ static u16 check_wmm(struct hostapd_data *hapd, struct sta_info *sta,
const u8 *wmm_ie, size_t wmm_ie_len)
{
sta->flags &= ~WLAN_STA_WMM;
+ sta->qosinfo = 0;
if (wmm_ie && hapd->conf->wmm_enabled) {
- if (hostapd_eid_wmm_valid(hapd, wmm_ie, wmm_ie_len))
+ struct wmm_information_element *wmm;
+
+ if (!hostapd_eid_wmm_valid(hapd, wmm_ie, wmm_ie_len)) {
hostapd_logger(hapd, sta->addr,
HOSTAPD_MODULE_WPA,
HOSTAPD_LEVEL_DEBUG,
"invalid WMM element in association "
"request");
- else
- sta->flags |= WLAN_STA_WMM;
+ return WLAN_STATUS_UNSPECIFIED_FAILURE;
+ }
+
+ sta->flags |= WLAN_STA_WMM;
+ wmm = (struct wmm_information_element *) wmm_ie;
+ sta->qosinfo = wmm->qos_info;
}
return WLAN_STATUS_SUCCESS;
}
@@ -576,35 +748,20 @@ static u16 copy_supp_rates(struct hostapd_data *hapd, struct sta_info *sta,
return WLAN_STATUS_UNSPECIFIED_FAILURE;
}
- if (elems->supp_rates_len > sizeof(sta->supported_rates)) {
+ if (elems->supp_rates_len + elems->ext_supp_rates_len >
+ sizeof(sta->supported_rates)) {
hostapd_logger(hapd, sta->addr, HOSTAPD_MODULE_IEEE80211,
HOSTAPD_LEVEL_DEBUG,
- "Invalid supported rates element length %d",
- elems->supp_rates_len);
+ "Invalid supported rates element length %d+%d",
+ elems->supp_rates_len,
+ elems->ext_supp_rates_len);
return WLAN_STATUS_UNSPECIFIED_FAILURE;
}
- os_memset(sta->supported_rates, 0, sizeof(sta->supported_rates));
- os_memcpy(sta->supported_rates, elems->supp_rates,
- elems->supp_rates_len);
- sta->supported_rates_len = elems->supp_rates_len;
-
- if (elems->ext_supp_rates) {
- if (elems->supp_rates_len + elems->ext_supp_rates_len >
- sizeof(sta->supported_rates)) {
- hostapd_logger(hapd, sta->addr,
- HOSTAPD_MODULE_IEEE80211,
- HOSTAPD_LEVEL_DEBUG,
- "Invalid supported rates element length"
- " %d+%d", elems->supp_rates_len,
- elems->ext_supp_rates_len);
- return WLAN_STATUS_UNSPECIFIED_FAILURE;
- }
-
- os_memcpy(sta->supported_rates + elems->supp_rates_len,
- elems->ext_supp_rates, elems->ext_supp_rates_len);
- sta->supported_rates_len += elems->ext_supp_rates_len;
- }
+ sta->supported_rates_len = merge_byte_arrays(
+ sta->supported_rates, sizeof(sta->supported_rates),
+ elems->supp_rates, elems->supp_rates_len,
+ elems->ext_supp_rates, elems->ext_supp_rates_len);
return WLAN_STATUS_SUCCESS;
}
@@ -635,12 +792,33 @@ static u16 check_assoc_ies(struct hostapd_data *hapd, struct sta_info *sta,
if (resp != WLAN_STATUS_SUCCESS)
return resp;
#ifdef CONFIG_IEEE80211N
- resp = copy_sta_ht_capab(sta, elems.ht_capabilities,
+ resp = copy_sta_ht_capab(hapd, sta, elems.ht_capabilities,
elems.ht_capabilities_len);
if (resp != WLAN_STATUS_SUCCESS)
return resp;
+ if (hapd->iconf->ieee80211n && hapd->iconf->require_ht &&
+ !(sta->flags & WLAN_STA_HT)) {
+ hostapd_logger(hapd, sta->addr, HOSTAPD_MODULE_IEEE80211,
+ HOSTAPD_LEVEL_INFO, "Station does not support "
+ "mandatory HT PHY - reject association");
+ return WLAN_STATUS_ASSOC_DENIED_NO_HT;
+ }
#endif /* CONFIG_IEEE80211N */
+#ifdef CONFIG_IEEE80211AC
+ resp = copy_sta_vht_capab(hapd, sta, elems.vht_capabilities,
+ elems.vht_capabilities_len);
+ if (resp != WLAN_STATUS_SUCCESS)
+ return resp;
+ if (hapd->iconf->ieee80211ac && hapd->iconf->require_vht &&
+ !(sta->flags & WLAN_STA_VHT)) {
+ hostapd_logger(hapd, sta->addr, HOSTAPD_MODULE_IEEE80211,
+ HOSTAPD_LEVEL_INFO, "Station does not support "
+ "mandatory VHT PHY - reject association");
+ return WLAN_STATUS_UNSPECIFIED_FAILURE;
+ }
+#endif /* CONFIG_IEEE80211AC */
+
if ((hapd->conf->wpa & WPA_PROTO_RSN) && elems.rsn_ie) {
wpa_ie = elems.rsn_ie;
wpa_ie_len = elems.rsn_ie_len;
@@ -654,7 +832,7 @@ static u16 check_assoc_ies(struct hostapd_data *hapd, struct sta_info *sta,
}
#ifdef CONFIG_WPS
- sta->flags &= ~(WLAN_STA_WPS | WLAN_STA_MAYBE_WPS);
+ sta->flags &= ~(WLAN_STA_WPS | WLAN_STA_MAYBE_WPS | WLAN_STA_WPS2);
if (hapd->conf->wps_state && elems.wps_ie) {
wpa_printf(MSG_DEBUG, "STA included WPS IE in (Re)Association "
"Request - assume WPS is used");
@@ -662,8 +840,17 @@ static u16 check_assoc_ies(struct hostapd_data *hapd, struct sta_info *sta,
wpabuf_free(sta->wps_ie);
sta->wps_ie = ieee802_11_vendor_ie_concat(ies, ies_len,
WPS_IE_VENDOR_TYPE);
+ if (sta->wps_ie && wps_is_20(sta->wps_ie)) {
+ wpa_printf(MSG_DEBUG, "WPS: STA supports WPS 2.0");
+ sta->flags |= WLAN_STA_WPS2;
+ }
wpa_ie = NULL;
wpa_ie_len = 0;
+ if (sta->wps_ie && wps_validate_assoc_req(sta->wps_ie) < 0) {
+ wpa_printf(MSG_DEBUG, "WPS: Invalid WPS IE in "
+ "(Re)Association Request - reject");
+ return WLAN_STATUS_INVALID_IE;
+ }
} else if (hapd->conf->wps_state && wpa_ie == NULL) {
wpa_printf(MSG_DEBUG, "STA did not include WPA/RSN IE in "
"(Re)Association Request - possible WPS use");
@@ -754,8 +941,18 @@ static u16 check_assoc_ies(struct hostapd_data *hapd, struct sta_info *sta,
}
#endif /* CONFIG_IEEE80211R */
+#ifdef CONFIG_SAE
+ if (wpa_auth_uses_sae(sta->wpa_sm) &&
+ sta->auth_alg != WLAN_AUTH_SAE) {
+ wpa_printf(MSG_DEBUG, "SAE: " MACSTR " tried to use "
+ "SAE AKM after non-SAE auth_alg %u",
+ MAC2STR(sta->addr), sta->auth_alg);
+ return WLAN_STATUS_NOT_SUPPORTED_AUTH_ALG;
+ }
+#endif /* CONFIG_SAE */
+
#ifdef CONFIG_IEEE80211N
- if ((sta->flags & WLAN_STA_HT) &&
+ if ((sta->flags & (WLAN_STA_HT | WLAN_STA_VHT)) &&
wpa_auth_get_pairwise(sta->wpa_sm) == WPA_CIPHER_TKIP) {
hostapd_logger(hapd, sta->addr,
HOSTAPD_MODULE_IEEE80211,
@@ -768,6 +965,29 @@ static u16 check_assoc_ies(struct hostapd_data *hapd, struct sta_info *sta,
} else
wpa_auth_sta_no_wpa(sta->wpa_sm);
+#ifdef CONFIG_P2P
+ if (elems.p2p) {
+ wpabuf_free(sta->p2p_ie);
+ sta->p2p_ie = ieee802_11_vendor_ie_concat(ies, ies_len,
+ P2P_IE_VENDOR_TYPE);
+
+ } else {
+ wpabuf_free(sta->p2p_ie);
+ sta->p2p_ie = NULL;
+ }
+
+ p2p_group_notif_assoc(hapd->p2p_group, sta->addr, ies, ies_len);
+#endif /* CONFIG_P2P */
+
+#ifdef CONFIG_HS20
+ wpabuf_free(sta->hs20_ie);
+ if (elems.hs20 && elems.hs20_len > 4) {
+ sta->hs20_ie = wpabuf_alloc_copy(elems.hs20 + 4,
+ elems.hs20_len - 4);
+ } else
+ sta->hs20_ie = NULL;
+#endif /* CONFIG_HS20 */
+
return WLAN_STATUS_SUCCESS;
}
@@ -788,7 +1008,7 @@ static void send_deauth(struct hostapd_data *hapd, const u8 *addr,
send_len = IEEE80211_HDRLEN + sizeof(reply.u.deauth);
reply.u.deauth.reason_code = host_to_le16(reason_code);
- if (hapd->drv.send_mgmt_frame(hapd, &reply, send_len) < 0)
+ if (hostapd_drv_send_mlme(hapd, &reply, send_len, 0) < 0)
wpa_printf(MSG_INFO, "Failed to send deauth: %s",
strerror(errno));
}
@@ -845,11 +1065,20 @@ static void send_assoc_resp(struct hostapd_data *hapd, struct sta_info *sta,
p = hostapd_eid_ht_operation(hapd, p);
#endif /* CONFIG_IEEE80211N */
+#ifdef CONFIG_IEEE80211AC
+ p = hostapd_eid_vht_capabilities(hapd, p);
+ p = hostapd_eid_vht_operation(hapd, p);
+#endif /* CONFIG_IEEE80211AC */
+
+ p = hostapd_eid_ext_capab(hapd, p);
+ p = hostapd_eid_bss_max_idle_period(hapd, p);
+
if (sta->flags & WLAN_STA_WMM)
p = hostapd_eid_wmm(hapd, p);
#ifdef CONFIG_WPS
- if (sta->flags & WLAN_STA_WPS) {
+ if ((sta->flags & WLAN_STA_WPS) ||
+ ((sta->flags & WLAN_STA_MAYBE_WPS) && hapd->conf->wpa)) {
struct wpabuf *wps = wps_build_assoc_resp_ie();
if (wps) {
os_memcpy(p, wpabuf_head(wps), wpabuf_len(wps));
@@ -859,9 +1088,39 @@ static void send_assoc_resp(struct hostapd_data *hapd, struct sta_info *sta,
}
#endif /* CONFIG_WPS */
+#ifdef CONFIG_P2P
+ if (sta->p2p_ie) {
+ struct wpabuf *p2p_resp_ie;
+ enum p2p_status_code status;
+ switch (status_code) {
+ case WLAN_STATUS_SUCCESS:
+ status = P2P_SC_SUCCESS;
+ break;
+ case WLAN_STATUS_AP_UNABLE_TO_HANDLE_NEW_STA:
+ status = P2P_SC_FAIL_LIMIT_REACHED;
+ break;
+ default:
+ status = P2P_SC_FAIL_INVALID_PARAMS;
+ break;
+ }
+ p2p_resp_ie = p2p_group_assoc_resp_ie(hapd->p2p_group, status);
+ if (p2p_resp_ie) {
+ os_memcpy(p, wpabuf_head(p2p_resp_ie),
+ wpabuf_len(p2p_resp_ie));
+ p += wpabuf_len(p2p_resp_ie);
+ wpabuf_free(p2p_resp_ie);
+ }
+ }
+#endif /* CONFIG_P2P */
+
+#ifdef CONFIG_P2P_MANAGER
+ if (hapd->conf->p2p & P2P_MANAGE)
+ p = hostapd_eid_p2p_manage(hapd, p);
+#endif /* CONFIG_P2P_MANAGER */
+
send_len += p - reply->u.assoc_resp.variable;
- if (hapd->drv.send_mgmt_frame(hapd, reply, send_len) < 0)
+ if (hostapd_drv_send_mlme(hapd, reply, send_len, 0) < 0)
wpa_printf(MSG_INFO, "Failed to send assoc resp: %s",
strerror(errno));
}
@@ -1005,6 +1264,7 @@ static void handle_assoc(struct hostapd_data *hapd,
"association OK (aid %d)", sta->aid);
/* Station will be marked associated, after it acknowledges AssocResp
*/
+ sta->flags |= WLAN_STA_ASSOC_REQ_OK;
#ifdef CONFIG_IEEE80211W
if ((sta->flags & WLAN_STA_MFP) && sta->sa_query_timed_out) {
@@ -1061,9 +1321,8 @@ static void handle_disassoc(struct hostapd_data *hapd,
return;
}
- sta->flags &= ~WLAN_STA_ASSOC;
- wpa_msg(hapd->msg_ctx, MSG_INFO, AP_STA_DISCONNECTED MACSTR,
- MAC2STR(sta->addr));
+ ap_sta_set_authorized(hapd, sta, 0);
+ sta->flags &= ~(WLAN_STA_ASSOC | WLAN_STA_ASSOC_REQ_OK);
wpa_auth_sm_event(sta->wpa_sm, WPA_DISASSOC);
hostapd_logger(hapd, sta->addr, HOSTAPD_MODULE_IEEE80211,
HOSTAPD_LEVEL_INFO, "disassociated");
@@ -1073,7 +1332,7 @@ static void handle_disassoc(struct hostapd_data *hapd,
* authenticated. */
accounting_sta_stop(hapd, sta);
ieee802_1x_free_station(sta);
- hapd->drv.sta_remove(hapd, sta->addr);
+ hostapd_drv_sta_remove(hapd, sta->addr);
if (sta->timeout_next == STA_NULLFUNC ||
sta->timeout_next == STA_DISASSOC) {
@@ -1094,26 +1353,26 @@ static void handle_deauth(struct hostapd_data *hapd,
struct sta_info *sta;
if (len < IEEE80211_HDRLEN + sizeof(mgmt->u.deauth)) {
- printf("handle_deauth - too short payload (len=%lu)\n",
- (unsigned long) len);
+ wpa_msg(hapd->msg_ctx, MSG_DEBUG, "handle_deauth - too short "
+ "payload (len=%lu)", (unsigned long) len);
return;
}
- wpa_printf(MSG_DEBUG, "deauthentication: STA=" MACSTR
- " reason_code=%d",
- MAC2STR(mgmt->sa),
- le_to_host16(mgmt->u.deauth.reason_code));
+ wpa_msg(hapd->msg_ctx, MSG_DEBUG, "deauthentication: STA=" MACSTR
+ " reason_code=%d",
+ MAC2STR(mgmt->sa), le_to_host16(mgmt->u.deauth.reason_code));
sta = ap_get_sta(hapd, mgmt->sa);
if (sta == NULL) {
- printf("Station " MACSTR " trying to deauthenticate, but it "
- "is not authenticated.\n", MAC2STR(mgmt->sa));
+ wpa_msg(hapd->msg_ctx, MSG_DEBUG, "Station " MACSTR " trying "
+ "to deauthenticate, but it is not authenticated",
+ MAC2STR(mgmt->sa));
return;
}
- sta->flags &= ~(WLAN_STA_AUTH | WLAN_STA_ASSOC);
- wpa_msg(hapd->msg_ctx, MSG_INFO, AP_STA_DISCONNECTED MACSTR,
- MAC2STR(sta->addr));
+ ap_sta_set_authorized(hapd, sta, 0);
+ sta->flags &= ~(WLAN_STA_AUTH | WLAN_STA_ASSOC |
+ WLAN_STA_ASSOC_REQ_OK);
wpa_auth_sm_event(sta->wpa_sm, WPA_DEAUTH);
hostapd_logger(hapd, sta->addr, HOSTAPD_MODULE_IEEE80211,
HOSTAPD_LEVEL_DEBUG, "deauthenticated");
@@ -1148,81 +1407,11 @@ static void handle_beacon(struct hostapd_data *hapd,
#ifdef CONFIG_IEEE80211W
-/* MLME-SAQuery.request */
-void ieee802_11_send_sa_query_req(struct hostapd_data *hapd,
- const u8 *addr, const u8 *trans_id)
-{
- struct ieee80211_mgmt mgmt;
- u8 *end;
-
- wpa_printf(MSG_DEBUG, "IEEE 802.11: Sending SA Query Request to "
- MACSTR, MAC2STR(addr));
- wpa_hexdump(MSG_DEBUG, "IEEE 802.11: SA Query Transaction ID",
- trans_id, WLAN_SA_QUERY_TR_ID_LEN);
-
- os_memset(&mgmt, 0, sizeof(mgmt));
- mgmt.frame_control = IEEE80211_FC(WLAN_FC_TYPE_MGMT,
- WLAN_FC_STYPE_ACTION);
- os_memcpy(mgmt.da, addr, ETH_ALEN);
- os_memcpy(mgmt.sa, hapd->own_addr, ETH_ALEN);
- os_memcpy(mgmt.bssid, hapd->own_addr, ETH_ALEN);
- mgmt.u.action.category = WLAN_ACTION_SA_QUERY;
- mgmt.u.action.u.sa_query_req.action = WLAN_SA_QUERY_REQUEST;
- os_memcpy(mgmt.u.action.u.sa_query_req.trans_id, trans_id,
- WLAN_SA_QUERY_TR_ID_LEN);
- end = mgmt.u.action.u.sa_query_req.trans_id + WLAN_SA_QUERY_TR_ID_LEN;
- if (hapd->drv.send_mgmt_frame(hapd, &mgmt, end - (u8 *) &mgmt) < 0)
- perror("ieee802_11_send_sa_query_req: send");
-}
-
-
-static void hostapd_sa_query_request(struct hostapd_data *hapd,
- const struct ieee80211_mgmt *mgmt)
-{
- struct sta_info *sta;
- struct ieee80211_mgmt resp;
- u8 *end;
-
- wpa_printf(MSG_DEBUG, "IEEE 802.11: Received SA Query Request from "
- MACSTR, MAC2STR(mgmt->sa));
- wpa_hexdump(MSG_DEBUG, "IEEE 802.11: SA Query Transaction ID",
- mgmt->u.action.u.sa_query_resp.trans_id,
- WLAN_SA_QUERY_TR_ID_LEN);
-
- sta = ap_get_sta(hapd, mgmt->sa);
- if (sta == NULL || !(sta->flags & WLAN_STA_ASSOC)) {
- wpa_printf(MSG_DEBUG, "IEEE 802.11: Ignore SA Query Request "
- "from unassociated STA " MACSTR, MAC2STR(mgmt->sa));
- return;
- }
-
- wpa_printf(MSG_DEBUG, "IEEE 802.11: Sending SA Query Response to "
- MACSTR, MAC2STR(mgmt->sa));
-
- os_memset(&resp, 0, sizeof(resp));
- resp.frame_control = IEEE80211_FC(WLAN_FC_TYPE_MGMT,
- WLAN_FC_STYPE_ACTION);
- os_memcpy(resp.da, mgmt->sa, ETH_ALEN);
- os_memcpy(resp.sa, hapd->own_addr, ETH_ALEN);
- os_memcpy(resp.bssid, hapd->own_addr, ETH_ALEN);
- resp.u.action.category = WLAN_ACTION_SA_QUERY;
- resp.u.action.u.sa_query_req.action = WLAN_SA_QUERY_RESPONSE;
- os_memcpy(resp.u.action.u.sa_query_req.trans_id,
- mgmt->u.action.u.sa_query_req.trans_id,
- WLAN_SA_QUERY_TR_ID_LEN);
- end = resp.u.action.u.sa_query_req.trans_id + WLAN_SA_QUERY_TR_ID_LEN;
- if (hapd->drv.send_mgmt_frame(hapd, &resp, end - (u8 *) &resp) < 0)
- perror("hostapd_sa_query_request: send");
-}
-
-
static void hostapd_sa_query_action(struct hostapd_data *hapd,
const struct ieee80211_mgmt *mgmt,
size_t len)
{
- struct sta_info *sta;
const u8 *end;
- int i;
end = mgmt->u.action.u.sa_query_resp.trans_id +
WLAN_SA_QUERY_TR_ID_LEN;
@@ -1232,50 +1421,9 @@ static void hostapd_sa_query_action(struct hostapd_data *hapd,
return;
}
- if (mgmt->u.action.u.sa_query_resp.action == WLAN_SA_QUERY_REQUEST) {
- hostapd_sa_query_request(hapd, mgmt);
- return;
- }
-
- if (mgmt->u.action.u.sa_query_resp.action != WLAN_SA_QUERY_RESPONSE) {
- wpa_printf(MSG_DEBUG, "IEEE 802.11: Unexpected SA Query "
- "Action %d", mgmt->u.action.u.sa_query_resp.action);
- return;
- }
-
- wpa_printf(MSG_DEBUG, "IEEE 802.11: Received SA Query Response from "
- MACSTR, MAC2STR(mgmt->sa));
- wpa_hexdump(MSG_DEBUG, "IEEE 802.11: SA Query Transaction ID",
- mgmt->u.action.u.sa_query_resp.trans_id,
- WLAN_SA_QUERY_TR_ID_LEN);
-
- /* MLME-SAQuery.confirm */
-
- sta = ap_get_sta(hapd, mgmt->sa);
- if (sta == NULL || sta->sa_query_trans_id == NULL) {
- wpa_printf(MSG_DEBUG, "IEEE 802.11: No matching STA with "
- "pending SA Query request found");
- return;
- }
-
- for (i = 0; i < sta->sa_query_count; i++) {
- if (os_memcmp(sta->sa_query_trans_id +
- i * WLAN_SA_QUERY_TR_ID_LEN,
- mgmt->u.action.u.sa_query_resp.trans_id,
- WLAN_SA_QUERY_TR_ID_LEN) == 0)
- break;
- }
-
- if (i >= sta->sa_query_count) {
- wpa_printf(MSG_DEBUG, "IEEE 802.11: No matching SA Query "
- "transaction identifier found");
- return;
- }
-
- hostapd_logger(hapd, sta->addr, HOSTAPD_MODULE_IEEE80211,
- HOSTAPD_LEVEL_DEBUG,
- "Reply to pending SA Query received");
- ap_sta_stop_sa_query(hapd, sta);
+ ieee802_11_sa_query_action(hapd, mgmt->sa,
+ mgmt->u.action.u.sa_query_resp.action,
+ mgmt->u.action.u.sa_query_resp.trans_id);
}
@@ -1287,10 +1435,32 @@ static int robust_action_frame(u8 category)
#endif /* CONFIG_IEEE80211W */
+#ifdef CONFIG_WNM
+static void hostapd_wnm_action(struct hostapd_data *hapd, struct sta_info *sta,
+ const struct ieee80211_mgmt *mgmt,
+ size_t len)
+{
+ struct rx_action action;
+ if (len < IEEE80211_HDRLEN + 2)
+ return;
+ os_memset(&action, 0, sizeof(action));
+ action.da = mgmt->da;
+ action.sa = mgmt->sa;
+ action.bssid = mgmt->bssid;
+ action.category = mgmt->u.action.category;
+ action.data = (const u8 *) &mgmt->u.action.u.wnm_sleep_req.action;
+ action.len = len - IEEE80211_HDRLEN - 1;
+ action.freq = hapd->iface->freq;
+ ieee802_11_rx_wnm_action_ap(hapd, &action);
+}
+#endif /* CONFIG_WNM */
+
+
static void handle_action(struct hostapd_data *hapd,
const struct ieee80211_mgmt *mgmt, size_t len)
{
struct sta_info *sta;
+ sta = ap_get_sta(hapd, mgmt->sa);
if (len < IEEE80211_HDRLEN + 1) {
hostapd_logger(hapd, mgmt->sa, HOSTAPD_MODULE_IEEE80211,
@@ -1300,7 +1470,14 @@ static void handle_action(struct hostapd_data *hapd,
return;
}
- sta = ap_get_sta(hapd, mgmt->sa);
+ if (mgmt->u.action.category != WLAN_ACTION_PUBLIC &&
+ (sta == NULL || !(sta->flags & WLAN_STA_ASSOC))) {
+ wpa_printf(MSG_DEBUG, "IEEE 802.11: Ignored Action "
+ "frame (category=%u) from unassociated STA " MACSTR,
+ MAC2STR(mgmt->sa), mgmt->u.action.category);
+ return;
+ }
+
#ifdef CONFIG_IEEE80211W
if (sta && (sta->flags & WLAN_STA_MFP) &&
!(mgmt->frame_control & host_to_le16(WLAN_FC_ISWEP) &&
@@ -1316,20 +1493,10 @@ static void handle_action(struct hostapd_data *hapd,
switch (mgmt->u.action.category) {
#ifdef CONFIG_IEEE80211R
case WLAN_ACTION_FT:
- {
- if (sta == NULL || !(sta->flags & WLAN_STA_ASSOC)) {
- wpa_printf(MSG_DEBUG, "IEEE 802.11: Ignored FT Action "
- "frame from unassociated STA " MACSTR,
- MAC2STR(mgmt->sa));
- return;
- }
-
if (wpa_ft_action_rx(sta->wpa_sm, (u8 *) &mgmt->u.action,
len - IEEE80211_HDRLEN))
break;
-
return;
- }
#endif /* CONFIG_IEEE80211R */
case WLAN_ACTION_WMM:
hostapd_wmm_action(hapd, mgmt, len);
@@ -1339,6 +1506,11 @@ static void handle_action(struct hostapd_data *hapd,
hostapd_sa_query_action(hapd, mgmt, len);
return;
#endif /* CONFIG_IEEE80211W */
+#ifdef CONFIG_WNM
+ case WLAN_ACTION_WNM:
+ hostapd_wnm_action(hapd, sta, mgmt, len);
+ return;
+#endif /* CONFIG_WNM */
case WLAN_ACTION_PUBLIC:
if (hapd->public_action_cb) {
hapd->public_action_cb(hapd->public_action_cb_ctx,
@@ -1347,6 +1519,14 @@ static void handle_action(struct hostapd_data *hapd,
return;
}
break;
+ case WLAN_ACTION_VENDOR_SPECIFIC:
+ if (hapd->vendor_action_cb) {
+ if (hapd->vendor_action_cb(hapd->vendor_action_cb_ctx,
+ (u8 *) mgmt, len,
+ hapd->iface->freq) == 0)
+ return;
+ }
+ break;
}
hostapd_logger(hapd, mgmt->sa, HOSTAPD_MODULE_IEEE80211,
@@ -1374,7 +1554,10 @@ static void handle_action(struct hostapd_data *hapd,
os_memcpy(resp->bssid, hapd->own_addr, ETH_ALEN);
resp->u.action.category |= 0x80;
- hapd->drv.send_mgmt_frame(hapd, resp, len);
+ if (hostapd_drv_send_mlme(hapd, resp, len, 0) < 0) {
+ wpa_printf(MSG_ERROR, "IEEE 802.11: Failed to send "
+ "Action frame");
+ }
os_free(resp);
}
}
@@ -1400,6 +1583,9 @@ void ieee802_11_mgmt(struct hostapd_data *hapd, const u8 *buf, size_t len,
int broadcast;
u16 fc, stype;
+ if (len < 24)
+ return;
+
mgmt = (struct ieee80211_mgmt *) buf;
fc = le_to_host16(mgmt->frame_control);
stype = WLAN_FC_GET_STYPE(fc);
@@ -1414,6 +1600,11 @@ void ieee802_11_mgmt(struct hostapd_data *hapd, const u8 *buf, size_t len,
mgmt->bssid[4] == 0xff && mgmt->bssid[5] == 0xff;
if (!broadcast &&
+#ifdef CONFIG_P2P
+ /* Invitation responses can be sent with the peer MAC as BSSID */
+ !((hapd->conf->p2p & P2P_GROUP_OWNER) &&
+ stype == WLAN_FC_STYPE_ACTION) &&
+#endif /* CONFIG_P2P */
os_memcmp(mgmt->bssid, hapd->own_addr, ETH_ALEN) != 0) {
printf("MGMT: BSSID=" MACSTR " not our address\n",
MAC2STR(mgmt->bssid));
@@ -1422,7 +1613,7 @@ void ieee802_11_mgmt(struct hostapd_data *hapd, const u8 *buf, size_t len,
if (stype == WLAN_FC_STYPE_PROBE_REQ) {
- handle_probe_req(hapd, mgmt, len);
+ handle_probe_req(hapd, mgmt, len, fi->ssi_signal);
return;
}
@@ -1452,7 +1643,7 @@ void ieee802_11_mgmt(struct hostapd_data *hapd, const u8 *buf, size_t len,
handle_disassoc(hapd, mgmt, len);
break;
case WLAN_FC_STYPE_DEAUTH:
- wpa_printf(MSG_DEBUG, "mgmt::deauth");
+ wpa_msg(hapd->msg_ctx, MSG_DEBUG, "mgmt::deauth");
handle_deauth(hapd, mgmt, len);
break;
case WLAN_FC_STYPE_ACTION:
@@ -1518,13 +1709,6 @@ static void handle_assoc_cb(struct hostapd_data *hapd,
int new_assoc = 1;
struct ieee80211_ht_capabilities ht_cap;
- if (!ok) {
- hostapd_logger(hapd, mgmt->da, HOSTAPD_MODULE_IEEE80211,
- HOSTAPD_LEVEL_DEBUG,
- "did not acknowledge association response");
- return;
- }
-
if (len < IEEE80211_HDRLEN + (reassoc ? sizeof(mgmt->u.reassoc_resp) :
sizeof(mgmt->u.assoc_resp))) {
printf("handle_assoc_cb(reassoc=%d) - too short payload "
@@ -1532,11 +1716,6 @@ static void handle_assoc_cb(struct hostapd_data *hapd,
return;
}
- if (reassoc)
- status = le_to_host16(mgmt->u.reassoc_resp.status_code);
- else
- status = le_to_host16(mgmt->u.assoc_resp.status_code);
-
sta = ap_get_sta(hapd, mgmt->da);
if (!sta) {
printf("handle_assoc_cb: STA " MACSTR " not found\n",
@@ -1544,6 +1723,19 @@ static void handle_assoc_cb(struct hostapd_data *hapd,
return;
}
+ if (!ok) {
+ hostapd_logger(hapd, mgmt->da, HOSTAPD_MODULE_IEEE80211,
+ HOSTAPD_LEVEL_DEBUG,
+ "did not acknowledge association response");
+ sta->flags &= ~WLAN_STA_ASSOC_REQ_OK;
+ return;
+ }
+
+ if (reassoc)
+ status = le_to_host16(mgmt->u.reassoc_resp.status_code);
+ else
+ status = le_to_host16(mgmt->u.assoc_resp.status_code);
+
if (status != WLAN_STATUS_SUCCESS)
goto fail;
@@ -1565,9 +1757,7 @@ static void handle_assoc_cb(struct hostapd_data *hapd,
* Open, static WEP, or FT protocol; no separate authorization
* step.
*/
- sta->flags |= WLAN_STA_AUTHORIZED;
- wpa_msg(hapd->msg_ctx, MSG_INFO,
- AP_STA_CONNECTED MACSTR, MAC2STR(sta->addr));
+ ap_sta_set_authorized(hapd, sta, 1);
}
if (reassoc)
@@ -1584,22 +1774,31 @@ static void handle_assoc_cb(struct hostapd_data *hapd,
* cleared and configuration gets updated in case of reassociation back
* to the same AP.
*/
- hapd->drv.sta_remove(hapd, sta->addr);
+ hostapd_drv_sta_remove(hapd, sta->addr);
#ifdef CONFIG_IEEE80211N
if (sta->flags & WLAN_STA_HT)
hostapd_get_ht_capab(hapd, sta->ht_capabilities, &ht_cap);
#endif /* CONFIG_IEEE80211N */
- if (hapd->drv.sta_add(hapd, sta->addr, sta->aid, sta->capability,
- sta->supported_rates, sta->supported_rates_len,
- sta->listen_interval,
- sta->flags & WLAN_STA_HT ? &ht_cap : NULL)) {
+ if (hostapd_sta_add(hapd, sta->addr, sta->aid, sta->capability,
+ sta->supported_rates, sta->supported_rates_len,
+ sta->listen_interval,
+ sta->flags & WLAN_STA_HT ? &ht_cap : NULL,
+ sta->flags, sta->qosinfo)) {
hostapd_logger(hapd, sta->addr, HOSTAPD_MODULE_IEEE80211,
HOSTAPD_LEVEL_NOTICE,
"Could not add STA to kernel driver");
+
+ ap_sta_disconnect(hapd, sta, sta->addr,
+ WLAN_REASON_DISASSOC_AP_BUSY);
+
+ goto fail;
}
+ if (sta->flags & WLAN_STA_WDS)
+ hostapd_set_wds_sta(hapd, sta->addr, sta->aid, 1);
+
if (sta->eapol_sm == NULL) {
/*
* This STA does not use RADIUS server for EAP authentication,
@@ -1614,7 +1813,7 @@ static void handle_assoc_cb(struct hostapd_data *hapd,
goto fail;
}
- hapd->drv.set_sta_flags(hapd, sta);
+ hostapd_set_sta_flags(hapd, sta);
if (sta->auth_alg == WLAN_AUTH_FT)
wpa_auth_sm_event(sta->wpa_sm, WPA_ASSOC_FT);
@@ -1633,6 +1832,54 @@ static void handle_assoc_cb(struct hostapd_data *hapd,
}
+static void handle_deauth_cb(struct hostapd_data *hapd,
+ const struct ieee80211_mgmt *mgmt,
+ size_t len, int ok)
+{
+ struct sta_info *sta;
+ if (mgmt->da[0] & 0x01)
+ return;
+ sta = ap_get_sta(hapd, mgmt->da);
+ if (!sta) {
+ wpa_printf(MSG_DEBUG, "handle_deauth_cb: STA " MACSTR
+ " not found", MAC2STR(mgmt->da));
+ return;
+ }
+ if (ok)
+ wpa_printf(MSG_DEBUG, "STA " MACSTR " acknowledged deauth",
+ MAC2STR(sta->addr));
+ else
+ wpa_printf(MSG_DEBUG, "STA " MACSTR " did not acknowledge "
+ "deauth", MAC2STR(sta->addr));
+
+ ap_sta_deauth_cb(hapd, sta);
+}
+
+
+static void handle_disassoc_cb(struct hostapd_data *hapd,
+ const struct ieee80211_mgmt *mgmt,
+ size_t len, int ok)
+{
+ struct sta_info *sta;
+ if (mgmt->da[0] & 0x01)
+ return;
+ sta = ap_get_sta(hapd, mgmt->da);
+ if (!sta) {
+ wpa_printf(MSG_DEBUG, "handle_disassoc_cb: STA " MACSTR
+ " not found", MAC2STR(mgmt->da));
+ return;
+ }
+ if (ok)
+ wpa_printf(MSG_DEBUG, "STA " MACSTR " acknowledged disassoc",
+ MAC2STR(sta->addr));
+ else
+ wpa_printf(MSG_DEBUG, "STA " MACSTR " did not acknowledge "
+ "disassoc", MAC2STR(sta->addr));
+
+ ap_sta_disassoc_cb(hapd, sta);
+}
+
+
/**
* ieee802_11_mgmt_cb - Process management frame TX status callback
* @hapd: hostapd BSS data structure (the BSS from which the management frame
@@ -1662,10 +1909,15 @@ void ieee802_11_mgmt_cb(struct hostapd_data *hapd, const u8 *buf, size_t len,
handle_assoc_cb(hapd, mgmt, len, 1, ok);
break;
case WLAN_FC_STYPE_PROBE_RESP:
- wpa_printf(MSG_DEBUG, "mgmt::proberesp cb");
+ wpa_printf(MSG_EXCESSIVE, "mgmt::proberesp cb");
break;
case WLAN_FC_STYPE_DEAUTH:
- /* ignore */
+ wpa_printf(MSG_DEBUG, "mgmt::deauth cb");
+ handle_deauth_cb(hapd, mgmt, len, ok);
+ break;
+ case WLAN_FC_STYPE_DISASSOC:
+ wpa_printf(MSG_DEBUG, "mgmt::disassoc cb");
+ handle_disassoc_cb(hapd, mgmt, len, ok);
break;
case WLAN_FC_STYPE_ACTION:
wpa_printf(MSG_DEBUG, "mgmt::action cb");
@@ -1708,7 +1960,7 @@ void hostapd_tx_status(struct hostapd_data *hapd, const u8 *addr,
break;
}
}
- if (sta == NULL)
+ if (sta == NULL || !(sta->flags & WLAN_STA_ASSOC))
return;
if (sta->flags & WLAN_STA_PENDING_POLL) {
wpa_printf(MSG_DEBUG, "STA " MACSTR " %s pending "
@@ -1722,6 +1974,59 @@ void hostapd_tx_status(struct hostapd_data *hapd, const u8 *addr,
}
+void hostapd_eapol_tx_status(struct hostapd_data *hapd, const u8 *dst,
+ const u8 *data, size_t len, int ack)
+{
+ struct sta_info *sta;
+ struct hostapd_iface *iface = hapd->iface;
+
+ sta = ap_get_sta(hapd, dst);
+ if (sta == NULL && iface->num_bss > 1) {
+ size_t j;
+ for (j = 0; j < iface->num_bss; j++) {
+ hapd = iface->bss[j];
+ sta = ap_get_sta(hapd, dst);
+ if (sta)
+ break;
+ }
+ }
+ if (sta == NULL || !(sta->flags & WLAN_STA_ASSOC)) {
+ wpa_printf(MSG_DEBUG, "Ignore TX status for Data frame to STA "
+ MACSTR " that is not currently associated",
+ MAC2STR(dst));
+ return;
+ }
+
+ ieee802_1x_eapol_tx_status(hapd, sta, data, len, ack);
+}
+
+
+void hostapd_client_poll_ok(struct hostapd_data *hapd, const u8 *addr)
+{
+ struct sta_info *sta;
+ struct hostapd_iface *iface = hapd->iface;
+
+ sta = ap_get_sta(hapd, addr);
+ if (sta == NULL && iface->num_bss > 1) {
+ size_t j;
+ for (j = 0; j < iface->num_bss; j++) {
+ hapd = iface->bss[j];
+ sta = ap_get_sta(hapd, addr);
+ if (sta)
+ break;
+ }
+ }
+ if (sta == NULL)
+ return;
+ if (!(sta->flags & WLAN_STA_PENDING_POLL))
+ return;
+
+ wpa_printf(MSG_DEBUG, "STA " MACSTR " ACKed pending "
+ "activity poll", MAC2STR(sta->addr));
+ sta->flags &= ~WLAN_STA_PENDING_POLL;
+}
+
+
void ieee802_11_rx_from_unknown(struct hostapd_data *hapd, const u8 *src,
int wds)
{
@@ -1729,24 +2034,40 @@ void ieee802_11_rx_from_unknown(struct hostapd_data *hapd, const u8 *src,
sta = ap_get_sta(hapd, src);
if (sta && (sta->flags & WLAN_STA_ASSOC)) {
+ if (!hapd->conf->wds_sta)
+ return;
+
if (wds && !(sta->flags & WLAN_STA_WDS)) {
wpa_printf(MSG_DEBUG, "Enable 4-address WDS mode for "
"STA " MACSTR " (aid %u)",
MAC2STR(sta->addr), sta->aid);
sta->flags |= WLAN_STA_WDS;
- hapd->drv.set_wds_sta(hapd, sta->addr, sta->aid, 1);
+ hostapd_set_wds_sta(hapd, sta->addr, sta->aid, 1);
}
return;
}
wpa_printf(MSG_DEBUG, "Data/PS-poll frame from not associated STA "
MACSTR, MAC2STR(src));
+ if (src[0] & 0x01) {
+ /* Broadcast bit set in SA?! Ignore the frame silently. */
+ return;
+ }
+
+ if (sta && (sta->flags & WLAN_STA_ASSOC_REQ_OK)) {
+ wpa_printf(MSG_DEBUG, "Association Response to the STA has "
+ "already been sent, but no TX status yet known - "
+ "ignore Class 3 frame issue with " MACSTR,
+ MAC2STR(src));
+ return;
+ }
+
if (sta && (sta->flags & WLAN_STA_AUTH))
- hapd->drv.sta_disassoc(
+ hostapd_drv_sta_disassoc(
hapd, src,
WLAN_REASON_CLASS3_FRAME_FROM_NONASSOC_STA);
else
- hapd->drv.sta_deauth(
+ hostapd_drv_sta_deauth(
hapd, src,
WLAN_REASON_CLASS3_FRAME_FROM_NONASSOC_STA);
}
diff --git a/contrib/wpa/src/ap/ieee802_11.h b/contrib/wpa/src/ap/ieee802_11.h
index cfc069c..1e5800d 100644
--- a/contrib/wpa/src/ap/ieee802_11.h
+++ b/contrib/wpa/src/ap/ieee802_11.h
@@ -2,14 +2,8 @@
* hostapd / IEEE 802.11 Management
* Copyright (c) 2002-2009, Jouni Malinen <j@w1.fi>
*
- * This program is free software; you can redistribute it and/or modify
- * it under the terms of the GNU General Public License version 2 as
- * published by the Free Software Foundation.
- *
- * Alternatively, this software may be distributed under the terms of BSD
- * license.
- *
- * See README and COPYING for more details.
+ * This software may be distributed under the terms of the BSD license.
+ * See README for more details.
*/
#ifndef IEEE802_11_H
@@ -46,22 +40,42 @@ static inline int ieee802_11_get_mib_sta(struct hostapd_data *hapd,
#endif /* NEED_AP_MLME */
u16 hostapd_own_capab_info(struct hostapd_data *hapd, struct sta_info *sta,
int probe);
+u8 * hostapd_eid_ext_capab(struct hostapd_data *hapd, u8 *eid);
u8 * hostapd_eid_supp_rates(struct hostapd_data *hapd, u8 *eid);
u8 * hostapd_eid_ext_supp_rates(struct hostapd_data *hapd, u8 *eid);
u8 * hostapd_eid_ht_capabilities(struct hostapd_data *hapd, u8 *eid);
u8 * hostapd_eid_ht_operation(struct hostapd_data *hapd, u8 *eid);
+u8 * hostapd_eid_vht_capabilities(struct hostapd_data *hapd, u8 *eid);
+u8 * hostapd_eid_vht_operation(struct hostapd_data *hapd, u8 *eid);
int hostapd_ht_operation_update(struct hostapd_iface *iface);
void ieee802_11_send_sa_query_req(struct hostapd_data *hapd,
const u8 *addr, const u8 *trans_id);
void hostapd_get_ht_capab(struct hostapd_data *hapd,
struct ieee80211_ht_capabilities *ht_cap,
struct ieee80211_ht_capabilities *neg_ht_cap);
-u16 copy_sta_ht_capab(struct sta_info *sta, const u8 *ht_capab,
- size_t ht_capab_len);
+u16 copy_sta_ht_capab(struct hostapd_data *hapd, struct sta_info *sta,
+ const u8 *ht_capab, size_t ht_capab_len);
void update_ht_state(struct hostapd_data *hapd, struct sta_info *sta);
+u16 copy_sta_vht_capab(struct hostapd_data *hapd, struct sta_info *sta,
+ const u8 *vht_capab, size_t vht_capab_len);
void hostapd_tx_status(struct hostapd_data *hapd, const u8 *addr,
const u8 *buf, size_t len, int ack);
+void hostapd_eapol_tx_status(struct hostapd_data *hapd, const u8 *dst,
+ const u8 *data, size_t len, int ack);
void ieee802_11_rx_from_unknown(struct hostapd_data *hapd, const u8 *src,
int wds);
+u8 * hostapd_eid_assoc_comeback_time(struct hostapd_data *hapd,
+ struct sta_info *sta, u8 *eid);
+void ieee802_11_sa_query_action(struct hostapd_data *hapd,
+ const u8 *sa, const u8 action_type,
+ const u8 *trans_id);
+u8 * hostapd_eid_interworking(struct hostapd_data *hapd, u8 *eid);
+u8 * hostapd_eid_adv_proto(struct hostapd_data *hapd, u8 *eid);
+u8 * hostapd_eid_roaming_consortium(struct hostapd_data *hapd, u8 *eid);
+u8 * hostapd_eid_time_adv(struct hostapd_data *hapd, u8 *eid);
+u8 * hostapd_eid_time_zone(struct hostapd_data *hapd, u8 *eid);
+int hostapd_update_time_adv(struct hostapd_data *hapd);
+void hostapd_client_poll_ok(struct hostapd_data *hapd, const u8 *addr);
+u8 * hostapd_eid_bss_max_idle_period(struct hostapd_data *hapd, u8 *eid);
#endif /* IEEE802_11_H */
diff --git a/contrib/wpa/src/ap/ieee802_11_auth.c b/contrib/wpa/src/ap/ieee802_11_auth.c
index dec56d1..c311e55 100644
--- a/contrib/wpa/src/ap/ieee802_11_auth.c
+++ b/contrib/wpa/src/ap/ieee802_11_auth.c
@@ -1,15 +1,9 @@
/*
* hostapd / IEEE 802.11 authentication (ACL)
- * Copyright (c) 2003-2009, Jouni Malinen <j@w1.fi>
+ * Copyright (c) 2003-2012, Jouni Malinen <j@w1.fi>
*
- * This program is free software; you can redistribute it and/or modify
- * it under the terms of the GNU General Public License version 2 as
- * published by the Free Software Foundation.
- *
- * Alternatively, this software may be distributed under the terms of BSD
- * license.
- *
- * See README and COPYING for more details.
+ * This software may be distributed under the terms of the BSD license.
+ * See README for more details.
*
* Access control list for IEEE 802.11 authentication can uses statically
* configured ACL from configuration files or an external RADIUS server.
@@ -21,29 +15,35 @@
#include "utils/common.h"
#include "utils/eloop.h"
+#include "crypto/sha1.h"
#include "radius/radius.h"
#include "radius/radius_client.h"
#include "hostapd.h"
#include "ap_config.h"
+#include "ap_drv_ops.h"
#include "ieee802_11.h"
+#include "ieee802_1x.h"
#include "ieee802_11_auth.h"
#define RADIUS_ACL_TIMEOUT 30
struct hostapd_cached_radius_acl {
- time_t timestamp;
+ os_time_t timestamp;
macaddr addr;
int accepted; /* HOSTAPD_ACL_* */
struct hostapd_cached_radius_acl *next;
u32 session_timeout;
u32 acct_interim_interval;
int vlan_id;
+ struct hostapd_sta_wpa_psk_short *psk;
+ char *identity;
+ char *radius_cui;
};
struct hostapd_acl_query_data {
- time_t timestamp;
+ os_time_t timestamp;
u8 radius_id;
macaddr addr;
u8 *auth_msg; /* IEEE 802.11 authentication frame from station */
@@ -53,6 +53,15 @@ struct hostapd_acl_query_data {
#ifndef CONFIG_NO_RADIUS
+static void hostapd_acl_cache_free_entry(struct hostapd_cached_radius_acl *e)
+{
+ os_free(e->identity);
+ os_free(e->radius_cui);
+ hostapd_free_psk_list(e->psk);
+ os_free(e);
+}
+
+
static void hostapd_acl_cache_free(struct hostapd_cached_radius_acl *acl_cache)
{
struct hostapd_cached_radius_acl *prev;
@@ -60,38 +69,73 @@ static void hostapd_acl_cache_free(struct hostapd_cached_radius_acl *acl_cache)
while (acl_cache) {
prev = acl_cache;
acl_cache = acl_cache->next;
- os_free(prev);
+ hostapd_acl_cache_free_entry(prev);
}
}
+static void copy_psk_list(struct hostapd_sta_wpa_psk_short **psk,
+ struct hostapd_sta_wpa_psk_short *src)
+{
+ struct hostapd_sta_wpa_psk_short **copy_to;
+ struct hostapd_sta_wpa_psk_short *copy_from;
+
+ /* Copy PSK linked list */
+ copy_to = psk;
+ copy_from = src;
+ while (copy_from && copy_to) {
+ *copy_to = os_zalloc(sizeof(struct hostapd_sta_wpa_psk_short));
+ if (*copy_to == NULL)
+ break;
+ os_memcpy(*copy_to, copy_from,
+ sizeof(struct hostapd_sta_wpa_psk_short));
+ copy_from = copy_from->next;
+ copy_to = &((*copy_to)->next);
+ }
+ if (copy_to)
+ *copy_to = NULL;
+}
+
+
static int hostapd_acl_cache_get(struct hostapd_data *hapd, const u8 *addr,
u32 *session_timeout,
- u32 *acct_interim_interval, int *vlan_id)
+ u32 *acct_interim_interval, int *vlan_id,
+ struct hostapd_sta_wpa_psk_short **psk,
+ char **identity, char **radius_cui)
{
struct hostapd_cached_radius_acl *entry;
- time_t now;
+ struct os_time now;
- time(&now);
- entry = hapd->acl_cache;
+ os_get_time(&now);
- while (entry) {
- if (os_memcmp(entry->addr, addr, ETH_ALEN) == 0) {
- if (now - entry->timestamp > RADIUS_ACL_TIMEOUT)
- return -1; /* entry has expired */
- if (entry->accepted == HOSTAPD_ACL_ACCEPT_TIMEOUT)
- if (session_timeout)
- *session_timeout =
- entry->session_timeout;
- if (acct_interim_interval)
- *acct_interim_interval =
- entry->acct_interim_interval;
- if (vlan_id)
- *vlan_id = entry->vlan_id;
- return entry->accepted;
- }
+ for (entry = hapd->acl_cache; entry; entry = entry->next) {
+ if (os_memcmp(entry->addr, addr, ETH_ALEN) != 0)
+ continue;
- entry = entry->next;
+ if (now.sec - entry->timestamp > RADIUS_ACL_TIMEOUT)
+ return -1; /* entry has expired */
+ if (entry->accepted == HOSTAPD_ACL_ACCEPT_TIMEOUT)
+ if (session_timeout)
+ *session_timeout = entry->session_timeout;
+ if (acct_interim_interval)
+ *acct_interim_interval =
+ entry->acct_interim_interval;
+ if (vlan_id)
+ *vlan_id = entry->vlan_id;
+ copy_psk_list(psk, entry->psk);
+ if (identity) {
+ if (entry->identity)
+ *identity = os_strdup(entry->identity);
+ else
+ *identity = NULL;
+ }
+ if (radius_cui) {
+ if (entry->radius_cui)
+ *radius_cui = os_strdup(entry->radius_cui);
+ else
+ *radius_cui = NULL;
+ }
+ return entry->accepted;
}
return -1;
@@ -137,37 +181,9 @@ static int hostapd_radius_acl_query(struct hostapd_data *hapd, const u8 *addr,
goto fail;
}
- if (hapd->conf->own_ip_addr.af == AF_INET &&
- !radius_msg_add_attr(msg, RADIUS_ATTR_NAS_IP_ADDRESS,
- (u8 *) &hapd->conf->own_ip_addr.u.v4, 4)) {
- wpa_printf(MSG_DEBUG, "Could not add NAS-IP-Address");
+ if (add_common_radius_attr(hapd, hapd->conf->radius_auth_req_attr,
+ NULL, msg) < 0)
goto fail;
- }
-
-#ifdef CONFIG_IPV6
- if (hapd->conf->own_ip_addr.af == AF_INET6 &&
- !radius_msg_add_attr(msg, RADIUS_ATTR_NAS_IPV6_ADDRESS,
- (u8 *) &hapd->conf->own_ip_addr.u.v6, 16)) {
- wpa_printf(MSG_DEBUG, "Could not add NAS-IPv6-Address");
- goto fail;
- }
-#endif /* CONFIG_IPV6 */
-
- if (hapd->conf->nas_identifier &&
- !radius_msg_add_attr(msg, RADIUS_ATTR_NAS_IDENTIFIER,
- (u8 *) hapd->conf->nas_identifier,
- os_strlen(hapd->conf->nas_identifier))) {
- wpa_printf(MSG_DEBUG, "Could not add NAS-Identifier");
- goto fail;
- }
-
- os_snprintf(buf, sizeof(buf), RADIUS_802_1X_ADDR_FORMAT ":%s",
- MAC2STR(hapd->own_addr), hapd->conf->ssid.ssid);
- if (!radius_msg_add_attr(msg, RADIUS_ATTR_CALLED_STATION_ID,
- (u8 *) buf, os_strlen(buf))) {
- wpa_printf(MSG_DEBUG, "Could not add Called-Station-Id");
- goto fail;
- }
os_snprintf(buf, sizeof(buf), RADIUS_802_1X_ADDR_FORMAT,
MAC2STR(addr));
@@ -177,12 +193,6 @@ static int hostapd_radius_acl_query(struct hostapd_data *hapd, const u8 *addr,
goto fail;
}
- if (!radius_msg_add_attr_int32(msg, RADIUS_ATTR_NAS_PORT_TYPE,
- RADIUS_NAS_PORT_TYPE_IEEE_802_11)) {
- wpa_printf(MSG_DEBUG, "Could not add NAS-Port-Type");
- goto fail;
- }
-
os_snprintf(buf, sizeof(buf), "CONNECT 11Mbps 802.11b");
if (!radius_msg_add_attr(msg, RADIUS_ATTR_CONNECT_INFO,
(u8 *) buf, os_strlen(buf))) {
@@ -190,7 +200,8 @@ static int hostapd_radius_acl_query(struct hostapd_data *hapd, const u8 *addr,
goto fail;
}
- radius_client_send(hapd->radius, msg, RADIUS_AUTH, addr);
+ if (radius_client_send(hapd->radius, msg, RADIUS_AUTH, addr) < 0)
+ goto fail;
return 0;
fail:
@@ -209,11 +220,19 @@ static int hostapd_radius_acl_query(struct hostapd_data *hapd, const u8 *addr,
* @session_timeout: Buffer for returning session timeout (from RADIUS)
* @acct_interim_interval: Buffer for returning account interval (from RADIUS)
* @vlan_id: Buffer for returning VLAN ID
+ * @psk: Linked list buffer for returning WPA PSK
+ * @identity: Buffer for returning identity (from RADIUS)
+ * @radius_cui: Buffer for returning CUI (from RADIUS)
* Returns: HOSTAPD_ACL_ACCEPT, HOSTAPD_ACL_REJECT, or HOSTAPD_ACL_PENDING
+ *
+ * The caller is responsible for freeing the returned *identity and *radius_cui
+ * values with os_free().
*/
int hostapd_allowed_address(struct hostapd_data *hapd, const u8 *addr,
const u8 *msg, size_t len, u32 *session_timeout,
- u32 *acct_interim_interval, int *vlan_id)
+ u32 *acct_interim_interval, int *vlan_id,
+ struct hostapd_sta_wpa_psk_short **psk,
+ char **identity, char **radius_cui)
{
if (session_timeout)
*session_timeout = 0;
@@ -221,6 +240,12 @@ int hostapd_allowed_address(struct hostapd_data *hapd, const u8 *addr,
*acct_interim_interval = 0;
if (vlan_id)
*vlan_id = 0;
+ if (psk)
+ *psk = NULL;
+ if (identity)
+ *identity = NULL;
+ if (radius_cui)
+ *radius_cui = NULL;
if (hostapd_maclist_found(hapd->conf->accept_mac,
hapd->conf->num_accept_mac, addr, vlan_id))
@@ -240,11 +265,13 @@ int hostapd_allowed_address(struct hostapd_data *hapd, const u8 *addr,
return HOSTAPD_ACL_REJECT;
#else /* CONFIG_NO_RADIUS */
struct hostapd_acl_query_data *query;
+ struct os_time t;
/* Check whether ACL cache has an entry for this station */
int res = hostapd_acl_cache_get(hapd, addr, session_timeout,
acct_interim_interval,
- vlan_id);
+ vlan_id, psk,
+ identity, radius_cui);
if (res == HOSTAPD_ACL_ACCEPT ||
res == HOSTAPD_ACL_ACCEPT_TIMEOUT)
return res;
@@ -256,6 +283,14 @@ int hostapd_allowed_address(struct hostapd_data *hapd, const u8 *addr,
if (os_memcmp(query->addr, addr, ETH_ALEN) == 0) {
/* pending query in RADIUS retransmit queue;
* do not generate a new one */
+ if (identity) {
+ os_free(*identity);
+ *identity = NULL;
+ }
+ if (radius_cui) {
+ os_free(*radius_cui);
+ *radius_cui = NULL;
+ }
return HOSTAPD_ACL_PENDING;
}
query = query->next;
@@ -270,7 +305,8 @@ int hostapd_allowed_address(struct hostapd_data *hapd, const u8 *addr,
wpa_printf(MSG_ERROR, "malloc for query data failed");
return HOSTAPD_ACL_REJECT;
}
- time(&query->timestamp);
+ os_get_time(&t);
+ query->timestamp = t.sec;
os_memcpy(query->addr, addr, ETH_ALEN);
if (hostapd_radius_acl_query(hapd, addr, query)) {
wpa_printf(MSG_DEBUG, "Failed to send Access-Request "
@@ -302,7 +338,7 @@ int hostapd_allowed_address(struct hostapd_data *hapd, const u8 *addr,
#ifndef CONFIG_NO_RADIUS
-static void hostapd_acl_expire_cache(struct hostapd_data *hapd, time_t now)
+static void hostapd_acl_expire_cache(struct hostapd_data *hapd, os_time_t now)
{
struct hostapd_cached_radius_acl *prev, *entry, *tmp;
@@ -317,12 +353,10 @@ static void hostapd_acl_expire_cache(struct hostapd_data *hapd, time_t now)
prev->next = entry->next;
else
hapd->acl_cache = entry->next;
-#ifdef CONFIG_DRIVER_RADIUS_ACL
- hapd->drv.set_radius_acl_expire(hapd, entry->addr);
-#endif /* CONFIG_DRIVER_RADIUS_ACL */
+ hostapd_drv_set_radius_acl_expire(hapd, entry->addr);
tmp = entry;
entry = entry->next;
- os_free(tmp);
+ hostapd_acl_cache_free_entry(tmp);
continue;
}
@@ -332,7 +366,8 @@ static void hostapd_acl_expire_cache(struct hostapd_data *hapd, time_t now)
}
-static void hostapd_acl_expire_queries(struct hostapd_data *hapd, time_t now)
+static void hostapd_acl_expire_queries(struct hostapd_data *hapd,
+ os_time_t now)
{
struct hostapd_acl_query_data *prev, *entry, *tmp;
@@ -368,16 +403,64 @@ static void hostapd_acl_expire_queries(struct hostapd_data *hapd, time_t now)
static void hostapd_acl_expire(void *eloop_ctx, void *timeout_ctx)
{
struct hostapd_data *hapd = eloop_ctx;
- time_t now;
+ struct os_time now;
- time(&now);
- hostapd_acl_expire_cache(hapd, now);
- hostapd_acl_expire_queries(hapd, now);
+ os_get_time(&now);
+ hostapd_acl_expire_cache(hapd, now.sec);
+ hostapd_acl_expire_queries(hapd, now.sec);
eloop_register_timeout(10, 0, hostapd_acl_expire, hapd, NULL);
}
+static void decode_tunnel_passwords(struct hostapd_data *hapd,
+ const u8 *shared_secret,
+ size_t shared_secret_len,
+ struct radius_msg *msg,
+ struct radius_msg *req,
+ struct hostapd_cached_radius_acl *cache)
+{
+ int passphraselen;
+ char *passphrase, *strpassphrase;
+ size_t i;
+ struct hostapd_sta_wpa_psk_short *psk;
+
+ /*
+ * Decode all tunnel passwords as PSK and save them into a linked list.
+ */
+ for (i = 0; ; i++) {
+ passphrase = radius_msg_get_tunnel_password(
+ msg, &passphraselen, shared_secret, shared_secret_len,
+ req, i);
+ /*
+ * Passphrase is NULL iff there is no i-th Tunnel-Password
+ * attribute in msg.
+ */
+ if (passphrase == NULL)
+ break;
+ /*
+ * passphrase does not contain the NULL termination.
+ * Add it here as pbkdf2_sha1() requires it.
+ */
+ strpassphrase = os_zalloc(passphraselen + 1);
+ psk = os_zalloc(sizeof(struct hostapd_sta_wpa_psk_short));
+ if (strpassphrase && psk) {
+ os_memcpy(strpassphrase, passphrase, passphraselen);
+ pbkdf2_sha1(strpassphrase,
+ hapd->conf->ssid.ssid,
+ hapd->conf->ssid.ssid_len, 4096,
+ psk->psk, PMK_LEN);
+ psk->next = cache->psk;
+ cache->psk = psk;
+ psk = NULL;
+ }
+ os_free(strpassphrase);
+ os_free(psk);
+ os_free(passphrase);
+ }
+}
+
+
/**
* hostapd_acl_recv_radius - Process incoming RADIUS Authentication messages
* @msg: RADIUS response message
@@ -397,6 +480,7 @@ hostapd_acl_recv_radius(struct radius_msg *msg, struct radius_msg *req,
struct hostapd_acl_query_data *query, *prev;
struct hostapd_cached_radius_acl *cache;
struct radius_hdr *hdr = radius_msg_get_hdr(msg);
+ struct os_time t;
query = hapd->acl_queries;
prev = NULL;
@@ -431,9 +515,13 @@ hostapd_acl_recv_radius(struct radius_msg *msg, struct radius_msg *req,
wpa_printf(MSG_DEBUG, "Failed to add ACL cache entry");
goto done;
}
- time(&cache->timestamp);
+ os_get_time(&t);
+ cache->timestamp = t.sec;
os_memcpy(cache->addr, query->addr, sizeof(cache->addr));
if (hdr->code == RADIUS_CODE_ACCESS_ACCEPT) {
+ u8 *buf;
+ size_t len;
+
if (radius_msg_get_attr_int32(msg, RADIUS_ATTR_SESSION_TIMEOUT,
&cache->session_timeout) == 0)
cache->accepted = HOSTAPD_ACL_ACCEPT_TIMEOUT;
@@ -452,14 +540,35 @@ hostapd_acl_recv_radius(struct radius_msg *msg, struct radius_msg *req,
}
cache->vlan_id = radius_msg_get_vlanid(msg);
+
+ decode_tunnel_passwords(hapd, shared_secret, shared_secret_len,
+ msg, req, cache);
+
+ if (radius_msg_get_attr_ptr(msg, RADIUS_ATTR_USER_NAME,
+ &buf, &len, NULL) == 0) {
+ cache->identity = os_zalloc(len + 1);
+ if (cache->identity)
+ os_memcpy(cache->identity, buf, len);
+ }
+ if (radius_msg_get_attr_ptr(
+ msg, RADIUS_ATTR_CHARGEABLE_USER_IDENTITY,
+ &buf, &len, NULL) == 0) {
+ cache->radius_cui = os_zalloc(len + 1);
+ if (cache->radius_cui)
+ os_memcpy(cache->radius_cui, buf, len);
+ }
+
+ if (hapd->conf->wpa_psk_radius == PSK_RADIUS_REQUIRED &&
+ !cache->psk)
+ cache->accepted = HOSTAPD_ACL_REJECT;
} else
cache->accepted = HOSTAPD_ACL_REJECT;
cache->next = hapd->acl_cache;
hapd->acl_cache = cache;
#ifdef CONFIG_DRIVER_RADIUS_ACL
- hapd->drv.set_radius_acl_auth(hapd, query->addr, cache->accepted,
- cache->session_timeout);
+ hostapd_drv_set_radius_acl_auth(hapd, query->addr, cache->accepted,
+ cache->session_timeout);
#else /* CONFIG_DRIVER_RADIUS_ACL */
#ifdef NEED_AP_MLME
/* Re-send original authentication frame for 802.11 processing */
@@ -522,3 +631,13 @@ void hostapd_acl_deinit(struct hostapd_data *hapd)
hostapd_acl_query_free(prev);
}
}
+
+
+void hostapd_free_psk_list(struct hostapd_sta_wpa_psk_short *psk)
+{
+ while (psk) {
+ struct hostapd_sta_wpa_psk_short *prev = psk;
+ psk = psk->next;
+ os_free(prev);
+ }
+}
diff --git a/contrib/wpa/src/ap/ieee802_11_auth.h b/contrib/wpa/src/ap/ieee802_11_auth.h
index b2971e5..2bc1065 100644
--- a/contrib/wpa/src/ap/ieee802_11_auth.h
+++ b/contrib/wpa/src/ap/ieee802_11_auth.h
@@ -2,14 +2,8 @@
* hostapd / IEEE 802.11 authentication (ACL)
* Copyright (c) 2003-2005, Jouni Malinen <j@w1.fi>
*
- * This program is free software; you can redistribute it and/or modify
- * it under the terms of the GNU General Public License version 2 as
- * published by the Free Software Foundation.
- *
- * Alternatively, this software may be distributed under the terms of BSD
- * license.
- *
- * See README and COPYING for more details.
+ * This software may be distributed under the terms of the BSD license.
+ * See README for more details.
*/
#ifndef IEEE802_11_AUTH_H
@@ -24,8 +18,11 @@ enum {
int hostapd_allowed_address(struct hostapd_data *hapd, const u8 *addr,
const u8 *msg, size_t len, u32 *session_timeout,
- u32 *acct_interim_interval, int *vlan_id);
+ u32 *acct_interim_interval, int *vlan_id,
+ struct hostapd_sta_wpa_psk_short **psk,
+ char **identity, char **radius_cui);
int hostapd_acl_init(struct hostapd_data *hapd);
void hostapd_acl_deinit(struct hostapd_data *hapd);
+void hostapd_free_psk_list(struct hostapd_sta_wpa_psk_short *psk);
#endif /* IEEE802_11_AUTH_H */
diff --git a/contrib/wpa/src/ap/ieee802_11_ht.c b/contrib/wpa/src/ap/ieee802_11_ht.c
index 7541b83..6c3696f 100644
--- a/contrib/wpa/src/ap/ieee802_11_ht.c
+++ b/contrib/wpa/src/ap/ieee802_11_ht.c
@@ -30,7 +30,8 @@ u8 * hostapd_eid_ht_capabilities(struct hostapd_data *hapd, u8 *eid)
struct ieee80211_ht_capabilities *cap;
u8 *pos = eid;
- if (!hapd->iconf->ieee80211n || !hapd->iface->current_mode)
+ if (!hapd->iconf->ieee80211n || !hapd->iface->current_mode ||
+ hapd->conf->disable_11n)
return eid;
*pos++ = WLAN_EID_HT_CAP;
@@ -58,7 +59,7 @@ u8 * hostapd_eid_ht_operation(struct hostapd_data *hapd, u8 *eid)
struct ieee80211_ht_operation *oper;
u8 *pos = eid;
- if (!hapd->iconf->ieee80211n)
+ if (!hapd->iconf->ieee80211n || hapd->conf->disable_11n)
return eid;
*pos++ = WLAN_EID_HT_OPERATION;
@@ -92,7 +93,6 @@ Set to 1 (HT non-member protection) if there may be non-HT STAs
Set to 2 if only HT STAs are associated in BSS,
however and at least one 20 MHz HT STA is associated
Set to 3 (HT mixed mode) when one or more non-HT STAs are associated
- (currently non-GF HT station is considered as non-HT STA also)
*/
int hostapd_ht_operation_update(struct hostapd_iface *iface)
{
@@ -130,13 +130,8 @@ int hostapd_ht_operation_update(struct hostapd_iface *iface)
op_mode_changes++;
}
- /* Note: currently we switch to the MIXED op mode if HT non-greenfield
- * station is associated. Probably it's a theoretical case, since
- * it looks like all known HT STAs support greenfield.
- */
new_op_mode = 0;
- if (iface->num_sta_no_ht ||
- (iface->ht_op_mode & HT_INFO_OPERATION_MODE_NON_GF_DEVS_PRESENT))
+ if (iface->num_sta_no_ht)
new_op_mode = OP_MODE_MIXED;
else if ((iface->conf->ht_capab & HT_CAP_INFO_SUPP_CHANNEL_WIDTH_SET)
&& iface->num_sta_ht_20mhz)
@@ -160,11 +155,13 @@ int hostapd_ht_operation_update(struct hostapd_iface *iface)
}
-u16 copy_sta_ht_capab(struct sta_info *sta, const u8 *ht_capab,
- size_t ht_capab_len)
+u16 copy_sta_ht_capab(struct hostapd_data *hapd, struct sta_info *sta,
+ const u8 *ht_capab, size_t ht_capab_len)
{
+ /* Disable HT caps for STAs associated to no-HT BSSes. */
if (!ht_capab ||
- ht_capab_len < sizeof(struct ieee80211_ht_capabilities)) {
+ ht_capab_len < sizeof(struct ieee80211_ht_capabilities) ||
+ hapd->conf->disable_11n) {
sta->flags &= ~WLAN_STA_HT;
os_free(sta->ht_capabilities);
sta->ht_capabilities = NULL;
@@ -253,8 +250,14 @@ void hostapd_get_ht_capab(struct hostapd_data *hapd,
return;
os_memcpy(neg_ht_cap, ht_cap, sizeof(*neg_ht_cap));
cap = le_to_host16(neg_ht_cap->ht_capabilities_info);
- cap &= hapd->iconf->ht_capab;
- cap |= (hapd->iconf->ht_capab & HT_CAP_INFO_SMPS_DISABLED);
+
+ /*
+ * Mask out HT features we don't support, but don't overwrite
+ * non-symmetric features like STBC and SMPS. Just because
+ * we're not in dynamic SMPS mode the STA might still be.
+ */
+ cap &= (hapd->iconf->ht_capab | HT_CAP_INFO_RX_STBC_MASK |
+ HT_CAP_INFO_TX_STBC | HT_CAP_INFO_SMPS_MASK);
/*
* STBC needs to be handled specially
diff --git a/contrib/wpa/src/ap/ieee802_11_shared.c b/contrib/wpa/src/ap/ieee802_11_shared.c
new file mode 100644
index 0000000..76f78a7
--- /dev/null
+++ b/contrib/wpa/src/ap/ieee802_11_shared.c
@@ -0,0 +1,458 @@
+/*
+ * hostapd / IEEE 802.11 Management
+ * Copyright (c) 2002-2012, Jouni Malinen <j@w1.fi>
+ *
+ * This software may be distributed under the terms of the BSD license.
+ * See README for more details.
+ */
+
+#include "utils/includes.h"
+
+#include "utils/common.h"
+#include "common/ieee802_11_defs.h"
+#include "hostapd.h"
+#include "sta_info.h"
+#include "ap_config.h"
+#include "ap_drv_ops.h"
+#include "ieee802_11.h"
+
+
+#ifdef CONFIG_IEEE80211W
+
+u8 * hostapd_eid_assoc_comeback_time(struct hostapd_data *hapd,
+ struct sta_info *sta, u8 *eid)
+{
+ u8 *pos = eid;
+ u32 timeout, tu;
+ struct os_time now, passed;
+
+ *pos++ = WLAN_EID_TIMEOUT_INTERVAL;
+ *pos++ = 5;
+ *pos++ = WLAN_TIMEOUT_ASSOC_COMEBACK;
+ os_get_time(&now);
+ os_time_sub(&now, &sta->sa_query_start, &passed);
+ tu = (passed.sec * 1000000 + passed.usec) / 1024;
+ if (hapd->conf->assoc_sa_query_max_timeout > tu)
+ timeout = hapd->conf->assoc_sa_query_max_timeout - tu;
+ else
+ timeout = 0;
+ if (timeout < hapd->conf->assoc_sa_query_max_timeout)
+ timeout++; /* add some extra time for local timers */
+ WPA_PUT_LE32(pos, timeout);
+ pos += 4;
+
+ return pos;
+}
+
+
+/* MLME-SAQuery.request */
+void ieee802_11_send_sa_query_req(struct hostapd_data *hapd,
+ const u8 *addr, const u8 *trans_id)
+{
+ struct ieee80211_mgmt mgmt;
+ u8 *end;
+
+ wpa_printf(MSG_DEBUG, "IEEE 802.11: Sending SA Query Request to "
+ MACSTR, MAC2STR(addr));
+ wpa_hexdump(MSG_DEBUG, "IEEE 802.11: SA Query Transaction ID",
+ trans_id, WLAN_SA_QUERY_TR_ID_LEN);
+
+ os_memset(&mgmt, 0, sizeof(mgmt));
+ mgmt.frame_control = IEEE80211_FC(WLAN_FC_TYPE_MGMT,
+ WLAN_FC_STYPE_ACTION);
+ os_memcpy(mgmt.da, addr, ETH_ALEN);
+ os_memcpy(mgmt.sa, hapd->own_addr, ETH_ALEN);
+ os_memcpy(mgmt.bssid, hapd->own_addr, ETH_ALEN);
+ mgmt.u.action.category = WLAN_ACTION_SA_QUERY;
+ mgmt.u.action.u.sa_query_req.action = WLAN_SA_QUERY_REQUEST;
+ os_memcpy(mgmt.u.action.u.sa_query_req.trans_id, trans_id,
+ WLAN_SA_QUERY_TR_ID_LEN);
+ end = mgmt.u.action.u.sa_query_req.trans_id + WLAN_SA_QUERY_TR_ID_LEN;
+ if (hostapd_drv_send_mlme(hapd, &mgmt, end - (u8 *) &mgmt, 0) < 0)
+ perror("ieee802_11_send_sa_query_req: send");
+}
+
+
+static void ieee802_11_send_sa_query_resp(struct hostapd_data *hapd,
+ const u8 *sa, const u8 *trans_id)
+{
+ struct sta_info *sta;
+ struct ieee80211_mgmt resp;
+ u8 *end;
+
+ wpa_printf(MSG_DEBUG, "IEEE 802.11: Received SA Query Request from "
+ MACSTR, MAC2STR(sa));
+ wpa_hexdump(MSG_DEBUG, "IEEE 802.11: SA Query Transaction ID",
+ trans_id, WLAN_SA_QUERY_TR_ID_LEN);
+
+ sta = ap_get_sta(hapd, sa);
+ if (sta == NULL || !(sta->flags & WLAN_STA_ASSOC)) {
+ wpa_printf(MSG_DEBUG, "IEEE 802.11: Ignore SA Query Request "
+ "from unassociated STA " MACSTR, MAC2STR(sa));
+ return;
+ }
+
+ wpa_printf(MSG_DEBUG, "IEEE 802.11: Sending SA Query Response to "
+ MACSTR, MAC2STR(sa));
+
+ os_memset(&resp, 0, sizeof(resp));
+ resp.frame_control = IEEE80211_FC(WLAN_FC_TYPE_MGMT,
+ WLAN_FC_STYPE_ACTION);
+ os_memcpy(resp.da, sa, ETH_ALEN);
+ os_memcpy(resp.sa, hapd->own_addr, ETH_ALEN);
+ os_memcpy(resp.bssid, hapd->own_addr, ETH_ALEN);
+ resp.u.action.category = WLAN_ACTION_SA_QUERY;
+ resp.u.action.u.sa_query_req.action = WLAN_SA_QUERY_RESPONSE;
+ os_memcpy(resp.u.action.u.sa_query_req.trans_id, trans_id,
+ WLAN_SA_QUERY_TR_ID_LEN);
+ end = resp.u.action.u.sa_query_req.trans_id + WLAN_SA_QUERY_TR_ID_LEN;
+ if (hostapd_drv_send_mlme(hapd, &resp, end - (u8 *) &resp, 0) < 0)
+ perror("ieee80211_mgmt_sa_query_request: send");
+}
+
+
+void ieee802_11_sa_query_action(struct hostapd_data *hapd, const u8 *sa,
+ const u8 action_type, const u8 *trans_id)
+{
+ struct sta_info *sta;
+ int i;
+
+ if (action_type == WLAN_SA_QUERY_REQUEST) {
+ ieee802_11_send_sa_query_resp(hapd, sa, trans_id);
+ return;
+ }
+
+ if (action_type != WLAN_SA_QUERY_RESPONSE) {
+ wpa_printf(MSG_DEBUG, "IEEE 802.11: Unexpected SA Query "
+ "Action %d", action_type);
+ return;
+ }
+
+ wpa_printf(MSG_DEBUG, "IEEE 802.11: Received SA Query Response from "
+ MACSTR, MAC2STR(sa));
+ wpa_hexdump(MSG_DEBUG, "IEEE 802.11: SA Query Transaction ID",
+ trans_id, WLAN_SA_QUERY_TR_ID_LEN);
+
+ /* MLME-SAQuery.confirm */
+
+ sta = ap_get_sta(hapd, sa);
+ if (sta == NULL || sta->sa_query_trans_id == NULL) {
+ wpa_printf(MSG_DEBUG, "IEEE 802.11: No matching STA with "
+ "pending SA Query request found");
+ return;
+ }
+
+ for (i = 0; i < sta->sa_query_count; i++) {
+ if (os_memcmp(sta->sa_query_trans_id +
+ i * WLAN_SA_QUERY_TR_ID_LEN,
+ trans_id, WLAN_SA_QUERY_TR_ID_LEN) == 0)
+ break;
+ }
+
+ if (i >= sta->sa_query_count) {
+ wpa_printf(MSG_DEBUG, "IEEE 802.11: No matching SA Query "
+ "transaction identifier found");
+ return;
+ }
+
+ hostapd_logger(hapd, sta->addr, HOSTAPD_MODULE_IEEE80211,
+ HOSTAPD_LEVEL_DEBUG,
+ "Reply to pending SA Query received");
+ ap_sta_stop_sa_query(hapd, sta);
+}
+
+#endif /* CONFIG_IEEE80211W */
+
+
+u8 * hostapd_eid_ext_capab(struct hostapd_data *hapd, u8 *eid)
+{
+ u8 *pos = eid;
+ u8 len = 0;
+
+ if (hapd->conf->tdls & (TDLS_PROHIBIT | TDLS_PROHIBIT_CHAN_SWITCH))
+ len = 5;
+ if (len < 4 && hapd->conf->interworking)
+ len = 4;
+ if (len < 3 && hapd->conf->wnm_sleep_mode)
+ len = 3;
+ if (len < 7 && hapd->conf->ssid.utf8_ssid)
+ len = 7;
+#ifdef CONFIG_WNM
+ if (len < 4)
+ len = 4;
+#endif /* CONFIG_WNM */
+ if (len == 0)
+ return eid;
+
+ *pos++ = WLAN_EID_EXT_CAPAB;
+ *pos++ = len;
+ *pos++ = 0x00;
+ *pos++ = 0x00;
+
+ *pos = 0x00;
+ if (hapd->conf->wnm_sleep_mode)
+ *pos |= 0x02; /* Bit 17 - WNM-Sleep Mode */
+ if (hapd->conf->bss_transition)
+ *pos |= 0x08; /* Bit 19 - BSS Transition */
+ pos++;
+
+ if (len < 4)
+ return pos;
+ *pos = 0x00;
+#ifdef CONFIG_WNM
+ *pos |= 0x02; /* Bit 25 - SSID List */
+#endif /* CONFIG_WNM */
+ if (hapd->conf->time_advertisement == 2)
+ *pos |= 0x08; /* Bit 27 - UTC TSF Offset */
+ if (hapd->conf->interworking)
+ *pos |= 0x80; /* Bit 31 - Interworking */
+ pos++;
+
+ if (len < 5)
+ return pos;
+ *pos = 0x00;
+ if (hapd->conf->tdls & TDLS_PROHIBIT)
+ *pos |= 0x40; /* Bit 38 - TDLS Prohibited */
+ if (hapd->conf->tdls & TDLS_PROHIBIT_CHAN_SWITCH)
+ *pos |= 0x80; /* Bit 39 - TDLS Channel Switching Prohibited */
+ pos++;
+
+ if (len < 6)
+ return pos;
+ *pos = 0x00;
+ pos++;
+
+ if (len < 7)
+ return pos;
+ *pos = 0x00;
+ if (hapd->conf->ssid.utf8_ssid)
+ *pos |= 0x01; /* Bit 48 - UTF-8 SSID */
+ pos++;
+
+ return pos;
+}
+
+
+u8 * hostapd_eid_interworking(struct hostapd_data *hapd, u8 *eid)
+{
+ u8 *pos = eid;
+#ifdef CONFIG_INTERWORKING
+ u8 *len;
+
+ if (!hapd->conf->interworking)
+ return eid;
+
+ *pos++ = WLAN_EID_INTERWORKING;
+ len = pos++;
+
+ *pos = hapd->conf->access_network_type;
+ if (hapd->conf->internet)
+ *pos |= INTERWORKING_ANO_INTERNET;
+ if (hapd->conf->asra)
+ *pos |= INTERWORKING_ANO_ASRA;
+ if (hapd->conf->esr)
+ *pos |= INTERWORKING_ANO_ESR;
+ if (hapd->conf->uesa)
+ *pos |= INTERWORKING_ANO_UESA;
+ pos++;
+
+ if (hapd->conf->venue_info_set) {
+ *pos++ = hapd->conf->venue_group;
+ *pos++ = hapd->conf->venue_type;
+ }
+
+ if (!is_zero_ether_addr(hapd->conf->hessid)) {
+ os_memcpy(pos, hapd->conf->hessid, ETH_ALEN);
+ pos += ETH_ALEN;
+ }
+
+ *len = pos - len - 1;
+#endif /* CONFIG_INTERWORKING */
+
+ return pos;
+}
+
+
+u8 * hostapd_eid_adv_proto(struct hostapd_data *hapd, u8 *eid)
+{
+ u8 *pos = eid;
+#ifdef CONFIG_INTERWORKING
+
+ /* TODO: Separate configuration for ANQP? */
+ if (!hapd->conf->interworking)
+ return eid;
+
+ *pos++ = WLAN_EID_ADV_PROTO;
+ *pos++ = 2;
+ *pos++ = 0x7F; /* Query Response Length Limit | PAME-BI */
+ *pos++ = ACCESS_NETWORK_QUERY_PROTOCOL;
+#endif /* CONFIG_INTERWORKING */
+
+ return pos;
+}
+
+
+u8 * hostapd_eid_roaming_consortium(struct hostapd_data *hapd, u8 *eid)
+{
+ u8 *pos = eid;
+#ifdef CONFIG_INTERWORKING
+ u8 *len;
+ unsigned int i, count;
+
+ if (!hapd->conf->interworking ||
+ hapd->conf->roaming_consortium == NULL ||
+ hapd->conf->roaming_consortium_count == 0)
+ return eid;
+
+ *pos++ = WLAN_EID_ROAMING_CONSORTIUM;
+ len = pos++;
+
+ /* Number of ANQP OIs (in addition to the max 3 listed here) */
+ if (hapd->conf->roaming_consortium_count > 3 + 255)
+ *pos++ = 255;
+ else if (hapd->conf->roaming_consortium_count > 3)
+ *pos++ = hapd->conf->roaming_consortium_count - 3;
+ else
+ *pos++ = 0;
+
+ /* OU #1 and #2 Lengths */
+ *pos = hapd->conf->roaming_consortium[0].len;
+ if (hapd->conf->roaming_consortium_count > 1)
+ *pos |= hapd->conf->roaming_consortium[1].len << 4;
+ pos++;
+
+ if (hapd->conf->roaming_consortium_count > 3)
+ count = 3;
+ else
+ count = hapd->conf->roaming_consortium_count;
+
+ for (i = 0; i < count; i++) {
+ os_memcpy(pos, hapd->conf->roaming_consortium[i].oi,
+ hapd->conf->roaming_consortium[i].len);
+ pos += hapd->conf->roaming_consortium[i].len;
+ }
+
+ *len = pos - len - 1;
+#endif /* CONFIG_INTERWORKING */
+
+ return pos;
+}
+
+
+u8 * hostapd_eid_time_adv(struct hostapd_data *hapd, u8 *eid)
+{
+ if (hapd->conf->time_advertisement != 2)
+ return eid;
+
+ if (hapd->time_adv == NULL &&
+ hostapd_update_time_adv(hapd) < 0)
+ return eid;
+
+ if (hapd->time_adv == NULL)
+ return eid;
+
+ os_memcpy(eid, wpabuf_head(hapd->time_adv),
+ wpabuf_len(hapd->time_adv));
+ eid += wpabuf_len(hapd->time_adv);
+
+ return eid;
+}
+
+
+u8 * hostapd_eid_time_zone(struct hostapd_data *hapd, u8 *eid)
+{
+ size_t len;
+
+ if (hapd->conf->time_advertisement != 2)
+ return eid;
+
+ len = os_strlen(hapd->conf->time_zone);
+
+ *eid++ = WLAN_EID_TIME_ZONE;
+ *eid++ = len;
+ os_memcpy(eid, hapd->conf->time_zone, len);
+ eid += len;
+
+ return eid;
+}
+
+
+int hostapd_update_time_adv(struct hostapd_data *hapd)
+{
+ const int elen = 2 + 1 + 10 + 5 + 1;
+ struct os_time t;
+ struct os_tm tm;
+ u8 *pos;
+
+ if (hapd->conf->time_advertisement != 2)
+ return 0;
+
+ if (os_get_time(&t) < 0 || os_gmtime(t.sec, &tm) < 0)
+ return -1;
+
+ if (!hapd->time_adv) {
+ hapd->time_adv = wpabuf_alloc(elen);
+ if (hapd->time_adv == NULL)
+ return -1;
+ pos = wpabuf_put(hapd->time_adv, elen);
+ } else
+ pos = wpabuf_mhead_u8(hapd->time_adv);
+
+ *pos++ = WLAN_EID_TIME_ADVERTISEMENT;
+ *pos++ = 1 + 10 + 5 + 1;
+
+ *pos++ = 2; /* UTC time at which the TSF timer is 0 */
+
+ /* Time Value at TSF 0 */
+ /* FIX: need to calculate this based on the current TSF value */
+ WPA_PUT_LE16(pos, tm.year); /* Year */
+ pos += 2;
+ *pos++ = tm.month; /* Month */
+ *pos++ = tm.day; /* Day of month */
+ *pos++ = tm.hour; /* Hours */
+ *pos++ = tm.min; /* Minutes */
+ *pos++ = tm.sec; /* Seconds */
+ WPA_PUT_LE16(pos, 0); /* Milliseconds (not used) */
+ pos += 2;
+ *pos++ = 0; /* Reserved */
+
+ /* Time Error */
+ /* TODO: fill in an estimate on the error */
+ *pos++ = 0;
+ *pos++ = 0;
+ *pos++ = 0;
+ *pos++ = 0;
+ *pos++ = 0;
+
+ *pos++ = hapd->time_update_counter++;
+
+ return 0;
+}
+
+
+u8 * hostapd_eid_bss_max_idle_period(struct hostapd_data *hapd, u8 *eid)
+{
+ u8 *pos = eid;
+
+#ifdef CONFIG_WNM
+ if (hapd->conf->ap_max_inactivity > 0) {
+ unsigned int val;
+ *pos++ = WLAN_EID_BSS_MAX_IDLE_PERIOD;
+ *pos++ = 3;
+ val = hapd->conf->ap_max_inactivity;
+ if (val > 68000)
+ val = 68000;
+ val *= 1000;
+ val /= 1024;
+ if (val == 0)
+ val = 1;
+ if (val > 65535)
+ val = 65535;
+ WPA_PUT_LE16(pos, val);
+ pos += 2;
+ *pos++ = 0x00; /* TODO: Protected Keep-Alive Required */
+ }
+#endif /* CONFIG_WNM */
+
+ return pos;
+}
diff --git a/contrib/wpa/src/ap/ieee802_11_vht.c b/contrib/wpa/src/ap/ieee802_11_vht.c
new file mode 100644
index 0000000..b21c2b7
--- /dev/null
+++ b/contrib/wpa/src/ap/ieee802_11_vht.c
@@ -0,0 +1,110 @@
+/*
+ * hostapd / IEEE 802.11ac VHT
+ * Copyright (c) 2002-2009, Jouni Malinen <j@w1.fi>
+ *
+ * This program is free software; you can redistribute it and/or modify
+ * it under the terms of BSD license
+ *
+ * See README and COPYING for more details.
+ */
+
+#include "utils/includes.h"
+
+#include "utils/common.h"
+#include "common/ieee802_11_defs.h"
+#include "drivers/driver.h"
+#include "hostapd.h"
+#include "ap_config.h"
+#include "sta_info.h"
+#include "beacon.h"
+#include "ieee802_11.h"
+
+
+u8 * hostapd_eid_vht_capabilities(struct hostapd_data *hapd, u8 *eid)
+{
+ struct ieee80211_vht_capabilities *cap;
+ u8 *pos = eid;
+
+ if (!hapd->iconf->ieee80211ac || !hapd->iface->current_mode ||
+ hapd->conf->disable_11ac)
+ return eid;
+
+ *pos++ = WLAN_EID_VHT_CAP;
+ *pos++ = sizeof(*cap);
+
+ cap = (struct ieee80211_vht_capabilities *) pos;
+ os_memset(cap, 0, sizeof(*cap));
+ cap->vht_capabilities_info = host_to_le32(
+ hapd->iface->current_mode->vht_capab);
+
+ /* Supported MCS set comes from hw */
+ os_memcpy(cap->vht_supported_mcs_set,
+ hapd->iface->current_mode->vht_mcs_set, 8);
+
+ pos += sizeof(*cap);
+
+ return pos;
+}
+
+
+u8 * hostapd_eid_vht_operation(struct hostapd_data *hapd, u8 *eid)
+{
+ struct ieee80211_vht_operation *oper;
+ u8 *pos = eid;
+
+ if (!hapd->iconf->ieee80211ac || hapd->conf->disable_11ac)
+ return eid;
+
+ *pos++ = WLAN_EID_VHT_OPERATION;
+ *pos++ = sizeof(*oper);
+
+ oper = (struct ieee80211_vht_operation *) pos;
+ os_memset(oper, 0, sizeof(*oper));
+
+ /*
+ * center freq = 5 GHz + (5 * index)
+ * So index 42 gives center freq 5.210 GHz
+ * which is channel 42 in 5G band
+ */
+ oper->vht_op_info_chan_center_freq_seg0_idx =
+ hapd->iconf->vht_oper_centr_freq_seg0_idx;
+ oper->vht_op_info_chan_center_freq_seg1_idx =
+ hapd->iconf->vht_oper_centr_freq_seg1_idx;
+
+ oper->vht_op_info_chwidth = hapd->iconf->vht_oper_chwidth;
+
+ /* VHT Basic MCS set comes from hw */
+ /* Hard code 1 stream, MCS0-7 is a min Basic VHT MCS rates */
+ oper->vht_basic_mcs_set = host_to_le16(0xfffc);
+ pos += sizeof(*oper);
+
+ return pos;
+}
+
+
+u16 copy_sta_vht_capab(struct hostapd_data *hapd, struct sta_info *sta,
+ const u8 *vht_capab, size_t vht_capab_len)
+{
+ /* Disable VHT caps for STAs associated to no-VHT BSSes. */
+ if (!vht_capab ||
+ vht_capab_len < sizeof(struct ieee80211_vht_capabilities) ||
+ hapd->conf->disable_11ac) {
+ sta->flags &= ~WLAN_STA_VHT;
+ os_free(sta->vht_capabilities);
+ sta->vht_capabilities = NULL;
+ return WLAN_STATUS_SUCCESS;
+ }
+
+ if (sta->vht_capabilities == NULL) {
+ sta->vht_capabilities =
+ os_zalloc(sizeof(struct ieee80211_vht_capabilities));
+ if (sta->vht_capabilities == NULL)
+ return WLAN_STATUS_UNSPECIFIED_FAILURE;
+ }
+
+ sta->flags |= WLAN_STA_VHT;
+ os_memcpy(sta->vht_capabilities, vht_capab,
+ sizeof(struct ieee80211_vht_capabilities));
+
+ return WLAN_STATUS_SUCCESS;
+}
diff --git a/contrib/wpa/src/ap/ieee802_1x.c b/contrib/wpa/src/ap/ieee802_1x.c
index eb160f8..d9c21a8 100644
--- a/contrib/wpa/src/ap/ieee802_1x.c
+++ b/contrib/wpa/src/ap/ieee802_1x.c
@@ -1,15 +1,9 @@
/*
* hostapd / IEEE 802.1X-2004 Authenticator
- * Copyright (c) 2002-2009, Jouni Malinen <j@w1.fi>
+ * Copyright (c) 2002-2012, Jouni Malinen <j@w1.fi>
*
- * This program is free software; you can redistribute it and/or modify
- * it under the terms of the GNU General Public License version 2 as
- * published by the Free Software Foundation.
- *
- * Alternatively, this software may be distributed under the terms of BSD
- * license.
- *
- * See README and COPYING for more details.
+ * This software may be distributed under the terms of the BSD license.
+ * See README for more details.
*/
#include "utils/includes.h"
@@ -18,14 +12,15 @@
#include "utils/eloop.h"
#include "crypto/md5.h"
#include "crypto/crypto.h"
+#include "crypto/random.h"
#include "common/ieee802_11_defs.h"
-#include "common/wpa_ctrl.h"
#include "radius/radius.h"
#include "radius/radius_client.h"
#include "eap_server/eap.h"
#include "eap_common/eap_wsc_common.h"
#include "eapol_auth/eapol_auth_sm.h"
#include "eapol_auth/eapol_auth_sm_i.h"
+#include "p2p/p2p.h"
#include "hostapd.h"
#include "accounting.h"
#include "sta_info.h"
@@ -33,6 +28,7 @@
#include "preauth_auth.h"
#include "pmksa_cache_auth.h"
#include "ap_config.h"
+#include "ap_drv_ops.h"
#include "ieee802_1x.h"
@@ -70,7 +66,9 @@ static void ieee802_1x_send(struct hostapd_data *hapd, struct sta_info *sta,
if (sta->flags & WLAN_STA_PREAUTH) {
rsn_preauth_send(hapd, sta, buf, len);
} else {
- hapd->drv.send_eapol(hapd, sta->addr, buf, len, encrypt);
+ hostapd_drv_hapd_send_eapol(
+ hapd, sta->addr, buf, len,
+ encrypt, hostapd_sta_flags_to_drv(sta->flags));
}
os_free(buf);
@@ -86,21 +84,13 @@ void ieee802_1x_set_sta_authorized(struct hostapd_data *hapd,
return;
if (authorized) {
- if (!(sta->flags & WLAN_STA_AUTHORIZED))
- wpa_msg(hapd->msg_ctx, MSG_INFO,
- AP_STA_CONNECTED MACSTR, MAC2STR(sta->addr));
- sta->flags |= WLAN_STA_AUTHORIZED;
- res = hapd->drv.set_authorized(hapd, sta, 1);
+ ap_sta_set_authorized(hapd, sta, 1);
+ res = hostapd_set_authorized(hapd, sta, 1);
hostapd_logger(hapd, sta->addr, HOSTAPD_MODULE_IEEE8021X,
HOSTAPD_LEVEL_DEBUG, "authorizing port");
} else {
- if ((sta->flags & (WLAN_STA_AUTHORIZED | WLAN_STA_ASSOC)) ==
- (WLAN_STA_AUTHORIZED | WLAN_STA_ASSOC))
- wpa_msg(hapd->msg_ctx, MSG_INFO,
- AP_STA_DISCONNECTED MACSTR,
- MAC2STR(sta->addr));
- sta->flags &= ~WLAN_STA_AUTHORIZED;
- res = hapd->drv.set_authorized(hapd, sta, 0);
+ ap_sta_set_authorized(hapd, sta, 0);
+ res = hostapd_set_authorized(hapd, sta, 0);
hostapd_logger(hapd, sta->addr, HOSTAPD_MODULE_IEEE8021X,
HOSTAPD_LEVEL_DEBUG, "unauthorizing port");
}
@@ -110,8 +100,10 @@ void ieee802_1x_set_sta_authorized(struct hostapd_data *hapd,
"driver (errno=%d).\n", MAC2STR(sta->addr), errno);
}
- if (authorized)
+ if (authorized) {
+ os_get_time(&sta->connected_time);
accounting_sta_start(hapd, sta);
+ }
}
@@ -137,10 +129,10 @@ static void ieee802_1x_tx_key_one(struct hostapd_data *hapd,
hdr = (struct ieee802_1x_hdr *) buf;
key = (struct ieee802_1x_eapol_key *) (hdr + 1);
key->type = EAPOL_KEY_TYPE_RC4;
- key->key_length = htons(key_len);
+ WPA_PUT_BE16(key->key_length, key_len);
wpa_get_ntp_timestamp(key->replay_counter);
- if (os_get_random(key->key_iv, sizeof(key->key_iv))) {
+ if (random_get_bytes(key->key_iv, sizeof(key->key_iv))) {
wpa_printf(MSG_ERROR, "Could not get random numbers");
os_free(buf);
return;
@@ -215,7 +207,7 @@ ieee802_1x_group_alloc(struct hostapd_data *hapd, const char *ifname)
if (!key->key[key->idx])
key->key[key->idx] = os_malloc(key->default_len);
if (key->key[key->idx] == NULL ||
- os_get_random(key->key[key->idx], key->default_len)) {
+ random_get_bytes(key->key[key->idx], key->default_len)) {
printf("Could not generate random WEP key (dynamic VLAN).\n");
os_free(key->key[key->idx]);
key->key[key->idx] = NULL;
@@ -229,11 +221,13 @@ ieee802_1x_group_alloc(struct hostapd_data *hapd, const char *ifname)
wpa_hexdump_key(MSG_DEBUG, "Default WEP key (dynamic VLAN)",
key->key[key->idx], key->len[key->idx]);
- if (hapd->drv.set_key(ifname, hapd, WPA_ALG_WEP, NULL, key->idx, 1,
- NULL, 0, key->key[key->idx], key->len[key->idx]))
+ if (hostapd_drv_set_key(ifname, hapd, WPA_ALG_WEP,
+ broadcast_ether_addr, key->idx, 1,
+ NULL, 0, key->key[key->idx],
+ key->len[key->idx]))
printf("Could not set dynamic VLAN WEP encryption key.\n");
- hapd->drv.set_drv_ieee8021x(hapd, ifname, 1);
+ hostapd_set_drv_ieee8021x(hapd, ifname, 1);
return key;
}
@@ -330,7 +324,8 @@ void ieee802_1x_tx_key(struct hostapd_data *hapd, struct sta_info *sta)
u8 *ikey;
ikey = os_malloc(hapd->conf->individual_wep_key_len);
if (ikey == NULL ||
- os_get_random(ikey, hapd->conf->individual_wep_key_len)) {
+ random_get_bytes(ikey, hapd->conf->individual_wep_key_len))
+ {
wpa_printf(MSG_ERROR, "Could not generate random "
"individual WEP key.");
os_free(ikey);
@@ -345,9 +340,9 @@ void ieee802_1x_tx_key(struct hostapd_data *hapd, struct sta_info *sta)
/* TODO: set encryption in TX callback, i.e., only after STA
* has ACKed EAPOL-Key frame */
- if (hapd->drv.set_key(hapd->conf->iface, hapd, WPA_ALG_WEP,
- sta->addr, 0, 1, NULL, 0, ikey,
- hapd->conf->individual_wep_key_len)) {
+ if (hostapd_drv_set_key(hapd->conf->iface, hapd, WPA_ALG_WEP,
+ sta->addr, 0, 1, NULL, 0, ikey,
+ hapd->conf->individual_wep_key_len)) {
wpa_printf(MSG_ERROR, "Could not set individual WEP "
"encryption.");
}
@@ -360,6 +355,8 @@ void ieee802_1x_tx_key(struct hostapd_data *hapd, struct sta_info *sta)
const char *radius_mode_txt(struct hostapd_data *hapd)
{
switch (hapd->iface->conf->hw_mode) {
+ case HOSTAPD_MODE_IEEE80211AD:
+ return "802.11ad";
case HOSTAPD_MODE_IEEE80211A:
return "802.11a";
case HOSTAPD_MODE_IEEE80211G:
@@ -417,12 +414,142 @@ static void ieee802_1x_learn_identity(struct hostapd_data *hapd,
}
+static int add_common_radius_sta_attr(struct hostapd_data *hapd,
+ struct hostapd_radius_attr *req_attr,
+ struct sta_info *sta,
+ struct radius_msg *msg)
+{
+ char buf[128];
+
+ if (!hostapd_config_get_radius_attr(req_attr,
+ RADIUS_ATTR_NAS_PORT) &&
+ !radius_msg_add_attr_int32(msg, RADIUS_ATTR_NAS_PORT, sta->aid)) {
+ wpa_printf(MSG_ERROR, "Could not add NAS-Port");
+ return -1;
+ }
+
+ os_snprintf(buf, sizeof(buf), RADIUS_802_1X_ADDR_FORMAT,
+ MAC2STR(sta->addr));
+ buf[sizeof(buf) - 1] = '\0';
+ if (!radius_msg_add_attr(msg, RADIUS_ATTR_CALLING_STATION_ID,
+ (u8 *) buf, os_strlen(buf))) {
+ wpa_printf(MSG_ERROR, "Could not add Calling-Station-Id");
+ return -1;
+ }
+
+ if (sta->flags & WLAN_STA_PREAUTH) {
+ os_strlcpy(buf, "IEEE 802.11i Pre-Authentication",
+ sizeof(buf));
+ } else {
+ os_snprintf(buf, sizeof(buf), "CONNECT %d%sMbps %s",
+ radius_sta_rate(hapd, sta) / 2,
+ (radius_sta_rate(hapd, sta) & 1) ? ".5" : "",
+ radius_mode_txt(hapd));
+ buf[sizeof(buf) - 1] = '\0';
+ }
+ if (!hostapd_config_get_radius_attr(req_attr,
+ RADIUS_ATTR_CONNECT_INFO) &&
+ !radius_msg_add_attr(msg, RADIUS_ATTR_CONNECT_INFO,
+ (u8 *) buf, os_strlen(buf))) {
+ wpa_printf(MSG_ERROR, "Could not add Connect-Info");
+ return -1;
+ }
+
+ if (sta->acct_session_id_hi || sta->acct_session_id_lo) {
+ os_snprintf(buf, sizeof(buf), "%08X-%08X",
+ sta->acct_session_id_hi, sta->acct_session_id_lo);
+ if (!radius_msg_add_attr(msg, RADIUS_ATTR_ACCT_SESSION_ID,
+ (u8 *) buf, os_strlen(buf))) {
+ wpa_printf(MSG_ERROR, "Could not add Acct-Session-Id");
+ return -1;
+ }
+ }
+
+ return 0;
+}
+
+
+int add_common_radius_attr(struct hostapd_data *hapd,
+ struct hostapd_radius_attr *req_attr,
+ struct sta_info *sta,
+ struct radius_msg *msg)
+{
+ char buf[128];
+ struct hostapd_radius_attr *attr;
+
+ if (!hostapd_config_get_radius_attr(req_attr,
+ RADIUS_ATTR_NAS_IP_ADDRESS) &&
+ hapd->conf->own_ip_addr.af == AF_INET &&
+ !radius_msg_add_attr(msg, RADIUS_ATTR_NAS_IP_ADDRESS,
+ (u8 *) &hapd->conf->own_ip_addr.u.v4, 4)) {
+ wpa_printf(MSG_ERROR, "Could not add NAS-IP-Address");
+ return -1;
+ }
+
+#ifdef CONFIG_IPV6
+ if (!hostapd_config_get_radius_attr(req_attr,
+ RADIUS_ATTR_NAS_IPV6_ADDRESS) &&
+ hapd->conf->own_ip_addr.af == AF_INET6 &&
+ !radius_msg_add_attr(msg, RADIUS_ATTR_NAS_IPV6_ADDRESS,
+ (u8 *) &hapd->conf->own_ip_addr.u.v6, 16)) {
+ wpa_printf(MSG_ERROR, "Could not add NAS-IPv6-Address");
+ return -1;
+ }
+#endif /* CONFIG_IPV6 */
+
+ if (!hostapd_config_get_radius_attr(req_attr,
+ RADIUS_ATTR_NAS_IDENTIFIER) &&
+ hapd->conf->nas_identifier &&
+ !radius_msg_add_attr(msg, RADIUS_ATTR_NAS_IDENTIFIER,
+ (u8 *) hapd->conf->nas_identifier,
+ os_strlen(hapd->conf->nas_identifier))) {
+ wpa_printf(MSG_ERROR, "Could not add NAS-Identifier");
+ return -1;
+ }
+
+ os_snprintf(buf, sizeof(buf), RADIUS_802_1X_ADDR_FORMAT ":%s",
+ MAC2STR(hapd->own_addr),
+ wpa_ssid_txt(hapd->conf->ssid.ssid,
+ hapd->conf->ssid.ssid_len));
+ buf[sizeof(buf) - 1] = '\0';
+ if (!hostapd_config_get_radius_attr(req_attr,
+ RADIUS_ATTR_CALLED_STATION_ID) &&
+ !radius_msg_add_attr(msg, RADIUS_ATTR_CALLED_STATION_ID,
+ (u8 *) buf, os_strlen(buf))) {
+ wpa_printf(MSG_ERROR, "Could not add Called-Station-Id");
+ return -1;
+ }
+
+ if (!hostapd_config_get_radius_attr(req_attr,
+ RADIUS_ATTR_NAS_PORT_TYPE) &&
+ !radius_msg_add_attr_int32(msg, RADIUS_ATTR_NAS_PORT_TYPE,
+ RADIUS_NAS_PORT_TYPE_IEEE_802_11)) {
+ wpa_printf(MSG_ERROR, "Could not add NAS-Port-Type");
+ return -1;
+ }
+
+ if (sta && add_common_radius_sta_attr(hapd, req_attr, sta, msg) < 0)
+ return -1;
+
+ for (attr = req_attr; attr; attr = attr->next) {
+ if (!radius_msg_add_attr(msg, attr->type,
+ wpabuf_head(attr->val),
+ wpabuf_len(attr->val))) {
+ wpa_printf(MSG_ERROR, "Could not add RADIUS "
+ "attribute");
+ return -1;
+ }
+ }
+
+ return 0;
+}
+
+
static void ieee802_1x_encapsulate_radius(struct hostapd_data *hapd,
struct sta_info *sta,
const u8 *eap, size_t len)
{
struct radius_msg *msg;
- char buf[128];
struct eapol_state_machine *sm = sta->eapol_sm;
if (sm == NULL)
@@ -450,83 +577,20 @@ static void ieee802_1x_encapsulate_radius(struct hostapd_data *hapd,
goto fail;
}
- if (hapd->conf->own_ip_addr.af == AF_INET &&
- !radius_msg_add_attr(msg, RADIUS_ATTR_NAS_IP_ADDRESS,
- (u8 *) &hapd->conf->own_ip_addr.u.v4, 4)) {
- printf("Could not add NAS-IP-Address\n");
- goto fail;
- }
-
-#ifdef CONFIG_IPV6
- if (hapd->conf->own_ip_addr.af == AF_INET6 &&
- !radius_msg_add_attr(msg, RADIUS_ATTR_NAS_IPV6_ADDRESS,
- (u8 *) &hapd->conf->own_ip_addr.u.v6, 16)) {
- printf("Could not add NAS-IPv6-Address\n");
- goto fail;
- }
-#endif /* CONFIG_IPV6 */
-
- if (hapd->conf->nas_identifier &&
- !radius_msg_add_attr(msg, RADIUS_ATTR_NAS_IDENTIFIER,
- (u8 *) hapd->conf->nas_identifier,
- os_strlen(hapd->conf->nas_identifier))) {
- printf("Could not add NAS-Identifier\n");
- goto fail;
- }
-
- if (!radius_msg_add_attr_int32(msg, RADIUS_ATTR_NAS_PORT, sta->aid)) {
- printf("Could not add NAS-Port\n");
- goto fail;
- }
-
- os_snprintf(buf, sizeof(buf), RADIUS_802_1X_ADDR_FORMAT ":%s",
- MAC2STR(hapd->own_addr), hapd->conf->ssid.ssid);
- buf[sizeof(buf) - 1] = '\0';
- if (!radius_msg_add_attr(msg, RADIUS_ATTR_CALLED_STATION_ID,
- (u8 *) buf, os_strlen(buf))) {
- printf("Could not add Called-Station-Id\n");
- goto fail;
- }
-
- os_snprintf(buf, sizeof(buf), RADIUS_802_1X_ADDR_FORMAT,
- MAC2STR(sta->addr));
- buf[sizeof(buf) - 1] = '\0';
- if (!radius_msg_add_attr(msg, RADIUS_ATTR_CALLING_STATION_ID,
- (u8 *) buf, os_strlen(buf))) {
- printf("Could not add Calling-Station-Id\n");
+ if (add_common_radius_attr(hapd, hapd->conf->radius_auth_req_attr, sta,
+ msg) < 0)
goto fail;
- }
/* TODO: should probably check MTU from driver config; 2304 is max for
* IEEE 802.11, but use 1400 to avoid problems with too large packets
*/
- if (!radius_msg_add_attr_int32(msg, RADIUS_ATTR_FRAMED_MTU, 1400)) {
+ if (!hostapd_config_get_radius_attr(hapd->conf->radius_auth_req_attr,
+ RADIUS_ATTR_FRAMED_MTU) &&
+ !radius_msg_add_attr_int32(msg, RADIUS_ATTR_FRAMED_MTU, 1400)) {
printf("Could not add Framed-MTU\n");
goto fail;
}
- if (!radius_msg_add_attr_int32(msg, RADIUS_ATTR_NAS_PORT_TYPE,
- RADIUS_NAS_PORT_TYPE_IEEE_802_11)) {
- printf("Could not add NAS-Port-Type\n");
- goto fail;
- }
-
- if (sta->flags & WLAN_STA_PREAUTH) {
- os_strlcpy(buf, "IEEE 802.11i Pre-Authentication",
- sizeof(buf));
- } else {
- os_snprintf(buf, sizeof(buf), "CONNECT %d%sMbps %s",
- radius_sta_rate(hapd, sta) / 2,
- (radius_sta_rate(hapd, sta) & 1) ? ".5" : "",
- radius_mode_txt(hapd));
- buf[sizeof(buf) - 1] = '\0';
- }
- if (!radius_msg_add_attr(msg, RADIUS_ATTR_CONNECT_INFO,
- (u8 *) buf, os_strlen(buf))) {
- printf("Could not add Connect-Info\n");
- goto fail;
- }
-
if (eap && !radius_msg_add_eap(msg, eap, len)) {
printf("Could not add EAP-Message\n");
goto fail;
@@ -549,7 +613,28 @@ static void ieee802_1x_encapsulate_radius(struct hostapd_data *hapd,
}
}
- radius_client_send(hapd->radius, msg, RADIUS_AUTH, sta->addr);
+ if (hapd->conf->radius_request_cui) {
+ const u8 *cui;
+ size_t cui_len;
+ /* Add previously learned CUI or nul CUI to request CUI */
+ if (sm->radius_cui) {
+ cui = wpabuf_head(sm->radius_cui);
+ cui_len = wpabuf_len(sm->radius_cui);
+ } else {
+ cui = (const u8 *) "\0";
+ cui_len = 1;
+ }
+ if (!radius_msg_add_attr(msg,
+ RADIUS_ATTR_CHARGEABLE_USER_IDENTITY,
+ cui, cui_len)) {
+ wpa_printf(MSG_ERROR, "Could not add CUI");
+ goto fail;
+ }
+ }
+
+ if (radius_client_send(hapd->radius, msg, RADIUS_AUTH, sta->addr) < 0)
+ goto fail;
+
return;
fail:
@@ -652,7 +737,8 @@ ieee802_1x_alloc_eapol_sm(struct hostapd_data *hapd, struct sta_info *sta)
flags |= EAPOL_SM_FROM_PMKSA_CACHE;
}
return eapol_auth_alloc(hapd->eapol_auth, sta->addr, flags,
- sta->wps_ie, sta);
+ sta->wps_ie, sta->p2p_ie, sta,
+ sta->identity, sta->radius_cui);
}
@@ -673,6 +759,7 @@ void ieee802_1x_receive(struct hostapd_data *hapd, const u8 *sa, const u8 *buf,
struct ieee802_1x_eapol_key *key;
u16 datalen;
struct rsn_pmksa_cache_entry *pmksa;
+ int key_mgmt;
if (!hapd->conf->ieee802_1x && !hapd->conf->wpa &&
!hapd->conf->wps_state)
@@ -681,9 +768,10 @@ void ieee802_1x_receive(struct hostapd_data *hapd, const u8 *sa, const u8 *buf,
wpa_printf(MSG_DEBUG, "IEEE 802.1X: %lu bytes from " MACSTR,
(unsigned long) len, MAC2STR(sa));
sta = ap_get_sta(hapd, sa);
- if (!sta || !(sta->flags & WLAN_STA_ASSOC)) {
+ if (!sta || (!(sta->flags & (WLAN_STA_ASSOC | WLAN_STA_PREAUTH)) &&
+ !(hapd->iface->drv_flags & WPA_DRIVER_FLAGS_WIRED))) {
wpa_printf(MSG_DEBUG, "IEEE 802.1X data frame from not "
- "associated STA");
+ "associated/Pre-authenticating STA");
return;
}
@@ -724,10 +812,19 @@ void ieee802_1x_receive(struct hostapd_data *hapd, const u8 *sa, const u8 *buf,
return;
}
- if ((!hapd->conf->ieee802_1x &&
- !(sta->flags & (WLAN_STA_WPS | WLAN_STA_MAYBE_WPS))) ||
- wpa_key_mgmt_wpa_psk(wpa_auth_sta_key_mgmt(sta->wpa_sm)))
+ if (!hapd->conf->ieee802_1x &&
+ !(sta->flags & (WLAN_STA_WPS | WLAN_STA_MAYBE_WPS))) {
+ wpa_printf(MSG_DEBUG, "IEEE 802.1X: Ignore EAPOL message - "
+ "802.1X not enabled and WPS not used");
+ return;
+ }
+
+ key_mgmt = wpa_auth_sta_key_mgmt(sta->wpa_sm);
+ if (key_mgmt != -1 && wpa_key_mgmt_wpa_psk(key_mgmt)) {
+ wpa_printf(MSG_DEBUG, "IEEE 802.1X: Ignore EAPOL message - "
+ "STA is using PSK");
return;
+ }
if (!sta->eapol_sm) {
sta->eapol_sm = ieee802_1x_alloc_eapol_sm(hapd, sta);
@@ -735,14 +832,24 @@ void ieee802_1x_receive(struct hostapd_data *hapd, const u8 *sa, const u8 *buf,
return;
#ifdef CONFIG_WPS
- if (!hapd->conf->ieee802_1x &&
- ((sta->flags & (WLAN_STA_WPS | WLAN_STA_MAYBE_WPS)) ==
- WLAN_STA_MAYBE_WPS)) {
- /*
- * Delay EAPOL frame transmission until a possible WPS
- * STA initiates the handshake with EAPOL-Start.
- */
- sta->eapol_sm->flags |= EAPOL_SM_WAIT_START;
+ if (!hapd->conf->ieee802_1x) {
+ u32 wflags = sta->flags & (WLAN_STA_WPS |
+ WLAN_STA_WPS2 |
+ WLAN_STA_MAYBE_WPS);
+ if (wflags == WLAN_STA_MAYBE_WPS ||
+ wflags == (WLAN_STA_WPS | WLAN_STA_MAYBE_WPS)) {
+ /*
+ * Delay EAPOL frame transmission until a
+ * possible WPS STA initiates the handshake
+ * with EAPOL-Start. Only allow the wait to be
+ * skipped if the STA is known to support WPS
+ * 2.0.
+ */
+ wpa_printf(MSG_DEBUG, "WPS: Do not start "
+ "EAPOL until EAPOL-Start is "
+ "received");
+ sta->eapol_sm->flags |= EAPOL_SM_WAIT_START;
+ }
}
#endif /* CONFIG_WPS */
@@ -776,6 +883,7 @@ void ieee802_1x_receive(struct hostapd_data *hapd, const u8 *sa, const u8 *buf,
}
sta->eapol_sm->eapolStart = TRUE;
sta->eapol_sm->dot1xAuthEapolStartFramesRx++;
+ eap_server_clear_identity(sta->eapol_sm->eap);
wpa_auth_sm_event(sta->wpa_sm, WPA_REAUTH_EAPOL);
break;
@@ -788,11 +896,12 @@ void ieee802_1x_receive(struct hostapd_data *hapd, const u8 *sa, const u8 *buf,
accounting_sta_stop(hapd, sta);
sta->eapol_sm->eapolLogoff = TRUE;
sta->eapol_sm->dot1xAuthEapolLogoffFramesRx++;
+ eap_server_clear_identity(sta->eapol_sm->eap);
break;
case IEEE802_1X_TYPE_EAPOL_KEY:
wpa_printf(MSG_DEBUG, " EAPOL-Key");
- if (!(sta->flags & WLAN_STA_AUTHORIZED)) {
+ if (!ap_sta_is_authorized(sta)) {
wpa_printf(MSG_DEBUG, " Dropped key data from "
"unauthorized Supplicant");
break;
@@ -827,6 +936,7 @@ void ieee802_1x_new_station(struct hostapd_data *hapd, struct sta_info *sta)
struct rsn_pmksa_cache_entry *pmksa;
int reassoc = 1;
int force_1x = 0;
+ int key_mgmt;
#ifdef CONFIG_WPS
if (hapd->conf->wps_state && hapd->conf->wpa &&
@@ -840,9 +950,27 @@ void ieee802_1x_new_station(struct hostapd_data *hapd, struct sta_info *sta)
}
#endif /* CONFIG_WPS */
- if ((!force_1x && !hapd->conf->ieee802_1x) ||
- wpa_key_mgmt_wpa_psk(wpa_auth_sta_key_mgmt(sta->wpa_sm)))
+ if (!force_1x && !hapd->conf->ieee802_1x) {
+ wpa_printf(MSG_DEBUG, "IEEE 802.1X: Ignore STA - "
+ "802.1X not enabled or forced for WPS");
+ /*
+ * Clear any possible EAPOL authenticator state to support
+ * reassociation change from WPS to PSK.
+ */
+ ieee802_1x_free_station(sta);
return;
+ }
+
+ key_mgmt = wpa_auth_sta_key_mgmt(sta->wpa_sm);
+ if (key_mgmt != -1 && wpa_key_mgmt_wpa_psk(key_mgmt)) {
+ wpa_printf(MSG_DEBUG, "IEEE 802.1X: Ignore STA - using PSK");
+ /*
+ * Clear any possible EAPOL authenticator state to support
+ * reassociation change from WPA-EAP to PSK.
+ */
+ ieee802_1x_free_station(sta);
+ return;
+ }
if (sta->eapol_sm == NULL) {
hostapd_logger(hapd, sta->addr, HOSTAPD_MODULE_IEEE8021X,
@@ -860,17 +988,40 @@ void ieee802_1x_new_station(struct hostapd_data *hapd, struct sta_info *sta)
#ifdef CONFIG_WPS
sta->eapol_sm->flags &= ~EAPOL_SM_WAIT_START;
- if (!hapd->conf->ieee802_1x && !(sta->flags & WLAN_STA_WPS)) {
+ if (!hapd->conf->ieee802_1x && !(sta->flags & WLAN_STA_WPS2)) {
/*
- * Delay EAPOL frame transmission until a possible WPS
- * initiates the handshake with EAPOL-Start.
+ * Delay EAPOL frame transmission until a possible WPS STA
+ * initiates the handshake with EAPOL-Start. Only allow the
+ * wait to be skipped if the STA is known to support WPS 2.0.
*/
+ wpa_printf(MSG_DEBUG, "WPS: Do not start EAPOL until "
+ "EAPOL-Start is received");
sta->eapol_sm->flags |= EAPOL_SM_WAIT_START;
}
#endif /* CONFIG_WPS */
sta->eapol_sm->eap_if->portEnabled = TRUE;
+#ifdef CONFIG_IEEE80211R
+ if (sta->auth_alg == WLAN_AUTH_FT) {
+ hostapd_logger(hapd, sta->addr, HOSTAPD_MODULE_IEEE8021X,
+ HOSTAPD_LEVEL_DEBUG,
+ "PMK from FT - skip IEEE 802.1X/EAP");
+ /* Setup EAPOL state machines to already authenticated state
+ * because of existing FT information from R0KH. */
+ sta->eapol_sm->keyRun = TRUE;
+ sta->eapol_sm->eap_if->eapKeyAvailable = TRUE;
+ sta->eapol_sm->auth_pae_state = AUTH_PAE_AUTHENTICATING;
+ sta->eapol_sm->be_auth_state = BE_AUTH_SUCCESS;
+ sta->eapol_sm->authSuccess = TRUE;
+ sta->eapol_sm->authFail = FALSE;
+ if (sta->eapol_sm->eap)
+ eap_sm_notify_cached(sta->eapol_sm->eap);
+ /* TODO: get vlan_id from R0KH using RRB message */
+ return;
+ }
+#endif /* CONFIG_IEEE80211R */
+
pmksa = wpa_auth_sta_get_pmksa(sta->wpa_sm);
if (pmksa) {
int old_vlanid;
@@ -885,6 +1036,7 @@ void ieee802_1x_new_station(struct hostapd_data *hapd, struct sta_info *sta)
sta->eapol_sm->auth_pae_state = AUTH_PAE_AUTHENTICATING;
sta->eapol_sm->be_auth_state = BE_AUTH_SUCCESS;
sta->eapol_sm->authSuccess = TRUE;
+ sta->eapol_sm->authFail = FALSE;
if (sta->eapol_sm->eap)
eap_sm_notify_cached(sta->eapol_sm->eap);
old_vlanid = sta->vlan_id;
@@ -918,6 +1070,7 @@ void ieee802_1x_free_station(struct sta_info *sta)
#ifndef CONFIG_NO_RADIUS
radius_msg_free(sm->last_recv_radius);
radius_free_class(&sm->radius_class);
+ wpabuf_free(sm->radius_cui);
#endif /* CONFIG_NO_RADIUS */
os_free(sm->identity);
@@ -929,9 +1082,8 @@ void ieee802_1x_free_station(struct sta_info *sta)
static void ieee802_1x_decapsulate_radius(struct hostapd_data *hapd,
struct sta_info *sta)
{
- u8 *eap;
- size_t len;
- struct eap_hdr *hdr;
+ struct wpabuf *eap;
+ const struct eap_hdr *hdr;
int eap_type = -1;
char buf[64];
struct radius_msg *msg;
@@ -945,7 +1097,7 @@ static void ieee802_1x_decapsulate_radius(struct hostapd_data *hapd,
msg = sm->last_recv_radius;
- eap = radius_msg_get_eap(msg, &len);
+ eap = radius_msg_get_eap(msg);
if (eap == NULL) {
/* RFC 3579, Chap. 2.6.3:
* RADIUS server SHOULD NOT send Access-Reject/no EAP-Message
@@ -957,19 +1109,19 @@ static void ieee802_1x_decapsulate_radius(struct hostapd_data *hapd,
return;
}
- if (len < sizeof(*hdr)) {
+ if (wpabuf_len(eap) < sizeof(*hdr)) {
hostapd_logger(hapd, sta->addr, HOSTAPD_MODULE_IEEE8021X,
HOSTAPD_LEVEL_WARNING, "too short EAP packet "
"received from authentication server");
- os_free(eap);
+ wpabuf_free(eap);
sm->eap_if->aaaEapNoReq = TRUE;
return;
}
- if (len > sizeof(*hdr))
- eap_type = eap[sizeof(*hdr)];
+ if (wpabuf_len(eap) > sizeof(*hdr))
+ eap_type = (wpabuf_head_u8(eap))[sizeof(*hdr)];
- hdr = (struct eap_hdr *) eap;
+ hdr = wpabuf_head(eap);
switch (hdr->code) {
case EAP_CODE_REQUEST:
if (eap_type >= 0)
@@ -1004,7 +1156,7 @@ static void ieee802_1x_decapsulate_radius(struct hostapd_data *hapd,
sm->eap_if->aaaEapReq = TRUE;
wpabuf_free(sm->eap_if->aaaEapReqData);
- sm->eap_if->aaaEapReqData = wpabuf_alloc_ext_data(eap, len);
+ sm->eap_if->aaaEapReqData = eap;
}
@@ -1069,7 +1221,7 @@ static void ieee802_1x_store_radius_class(struct hostapd_data *hapd,
if (count <= 0)
return;
- nclass = os_zalloc(count * sizeof(struct radius_attr_data));
+ nclass = os_calloc(count, sizeof(struct radius_attr_data));
if (nclass == NULL)
return;
@@ -1139,6 +1291,32 @@ static void ieee802_1x_update_sta_identity(struct hostapd_data *hapd,
}
+/* Update CUI based on Chargeable-User-Identity attribute in Access-Accept */
+static void ieee802_1x_update_sta_cui(struct hostapd_data *hapd,
+ struct sta_info *sta,
+ struct radius_msg *msg)
+{
+ struct eapol_state_machine *sm = sta->eapol_sm;
+ struct wpabuf *cui;
+ u8 *buf;
+ size_t len;
+
+ if (sm == NULL)
+ return;
+
+ if (radius_msg_get_attr_ptr(msg, RADIUS_ATTR_CHARGEABLE_USER_IDENTITY,
+ &buf, &len, NULL) < 0)
+ return;
+
+ cui = wpabuf_alloc_copy(buf, len);
+ if (cui == NULL)
+ return;
+
+ wpabuf_free(sm->radius_cui);
+ sm->radius_cui = cui;
+}
+
+
struct sta_id_search {
u8 identifier;
struct eapol_state_machine *sm;
@@ -1298,6 +1476,7 @@ ieee802_1x_receive_auth(struct radius_msg *msg, struct radius_msg *req,
shared_secret_len);
ieee802_1x_store_radius_class(hapd, sta, msg);
ieee802_1x_update_sta_identity(hapd, sta, msg);
+ ieee802_1x_update_sta_cui(hapd, sta, msg);
if (sm->eap_if->eapKeyAvailable &&
wpa_auth_pmksa_add(sta->wpa_sm, sm->eapol_key_crypt,
session_timeout_set ?
@@ -1363,6 +1542,9 @@ void ieee802_1x_abort_auth(struct hostapd_data *hapd, struct sta_info *sta)
* request and we cannot continue EAP processing (EAP-Failure
* could only be sent if the EAP peer actually replied).
*/
+ wpa_dbg(hapd->msg_ctx, MSG_DEBUG, "EAP Timeout, STA " MACSTR,
+ MAC2STR(sta->addr));
+
sm->eap_if->portEnabled = FALSE;
ap_sta_disconnect(hapd, sta, sta->addr,
WLAN_REASON_PREV_AUTH_NOT_VALID);
@@ -1380,8 +1562,8 @@ static int ieee802_1x_rekey_broadcast(struct hostapd_data *hapd)
os_free(eapol->default_wep_key);
eapol->default_wep_key = os_malloc(hapd->conf->default_wep_key_len);
if (eapol->default_wep_key == NULL ||
- os_get_random(eapol->default_wep_key,
- hapd->conf->default_wep_key_len)) {
+ random_get_bytes(eapol->default_wep_key,
+ hapd->conf->default_wep_key_len)) {
printf("Could not generate random WEP key.\n");
os_free(eapol->default_wep_key);
eapol->default_wep_key = NULL;
@@ -1432,10 +1614,11 @@ static void ieee802_1x_rekey(void *eloop_ctx, void *timeout_ctx)
/* TODO: Could setup key for RX here, but change default TX keyid only
* after new broadcast key has been sent to all stations. */
- if (hapd->drv.set_key(hapd->conf->iface, hapd, WPA_ALG_WEP, NULL,
- eapol->default_wep_key_idx, 1, NULL, 0,
- eapol->default_wep_key,
- hapd->conf->default_wep_key_len)) {
+ if (hostapd_drv_set_key(hapd->conf->iface, hapd, WPA_ALG_WEP,
+ broadcast_ether_addr,
+ eapol->default_wep_key_idx, 1, NULL, 0,
+ eapol->default_wep_key,
+ hapd->conf->default_wep_key_len)) {
hostapd_logger(hapd, NULL, HOSTAPD_MODULE_IEEE8021X,
HOSTAPD_LEVEL_WARNING, "failed to configure a "
"new broadcast key");
@@ -1514,19 +1697,15 @@ static int ieee802_1x_get_eap_user(void *ctx, const u8 *identity,
{
struct hostapd_data *hapd = ctx;
const struct hostapd_eap_user *eap_user;
- int i, count;
+ int i;
- eap_user = hostapd_get_eap_user(hapd->conf, identity,
- identity_len, phase2);
+ eap_user = hostapd_get_eap_user(hapd, identity, identity_len, phase2);
if (eap_user == NULL)
return -1;
os_memset(user, 0, sizeof(*user));
user->phase2 = phase2;
- count = EAP_USER_MAX_METHODS;
- if (count > EAP_MAX_METHODS)
- count = EAP_MAX_METHODS;
- for (i = 0; i < count; i++) {
+ for (i = 0; i < EAP_MAX_METHODS; i++) {
user->methods[i].vendor = eap_user->methods[i].vendor;
user->methods[i].method = eap_user->methods[i].method;
}
@@ -1538,6 +1717,7 @@ static int ieee802_1x_get_eap_user(void *ctx, const u8 *identity,
os_memcpy(user->password, eap_user->password,
eap_user->password_len);
user->password_len = eap_user->password_len;
+ user->password_hash = eap_user->password_hash;
}
user->force_version = eap_user->force_version;
user->ttls_auth = eap_user->ttls_auth;
@@ -1651,6 +1831,9 @@ int ieee802_1x_init(struct hostapd_data *hapd)
conf.eap_sim_aka_result_ind = hapd->conf->eap_sim_aka_result_ind;
conf.tnc = hapd->conf->tnc;
conf.wps = hapd->wps;
+ conf.fragment_size = hapd->conf->fragment_size;
+ conf.pwd_group = hapd->conf->pwd_group;
+ conf.pbc_in_m1 = hapd->conf->pbc_in_m1;
os_memset(&cb, 0, sizeof(cb));
cb.eapol_send = ieee802_1x_eapol_send;
@@ -1669,7 +1852,7 @@ int ieee802_1x_init(struct hostapd_data *hapd)
return -1;
if ((hapd->conf->ieee802_1x || hapd->conf->wpa) &&
- hapd->drv.set_drv_ieee8021x(hapd, hapd->conf->iface, 1))
+ hostapd_set_drv_ieee8021x(hapd, hapd->conf->iface, 1))
return -1;
#ifndef CONFIG_NO_RADIUS
@@ -1680,9 +1863,9 @@ int ieee802_1x_init(struct hostapd_data *hapd)
if (hapd->conf->default_wep_key_len) {
for (i = 0; i < 4; i++)
- hapd->drv.set_key(hapd->conf->iface, hapd,
- WPA_ALG_NONE, NULL, i, 0, NULL, 0,
- NULL, 0);
+ hostapd_drv_set_key(hapd->conf->iface, hapd,
+ WPA_ALG_NONE, NULL, i, 0, NULL, 0,
+ NULL, 0);
ieee802_1x_rekey(hapd, NULL);
@@ -1700,7 +1883,7 @@ void ieee802_1x_deinit(struct hostapd_data *hapd)
if (hapd->driver != NULL &&
(hapd->conf->ieee802_1x || hapd->conf->wpa))
- hapd->drv.set_drv_ieee8021x(hapd, hapd->conf->iface, 0);
+ hostapd_set_drv_ieee8021x(hapd, hapd->conf->iface, 0);
eapol_auth_deinit(hapd->eapol_auth);
hapd->eapol_auth = NULL;
@@ -1711,15 +1894,13 @@ int ieee802_1x_tx_status(struct hostapd_data *hapd, struct sta_info *sta,
const u8 *buf, size_t len, int ack)
{
struct ieee80211_hdr *hdr;
- struct ieee802_1x_hdr *xhdr;
- struct ieee802_1x_eapol_key *key;
u8 *pos;
const unsigned char rfc1042_hdr[ETH_ALEN] =
{ 0xaa, 0xaa, 0x03, 0x00, 0x00, 0x00 };
if (sta == NULL)
return -1;
- if (len < sizeof(*hdr) + sizeof(rfc1042_hdr) + 2 + sizeof(*xhdr))
+ if (len < sizeof(*hdr) + sizeof(rfc1042_hdr) + 2)
return 0;
hdr = (struct ieee80211_hdr *) buf;
@@ -1731,21 +1912,44 @@ int ieee802_1x_tx_status(struct hostapd_data *hapd, struct sta_info *sta,
return 0;
pos += 2;
- xhdr = (struct ieee802_1x_hdr *) pos;
- pos += sizeof(*xhdr);
+ return ieee802_1x_eapol_tx_status(hapd, sta, pos, buf + len - pos,
+ ack);
+}
+
+
+int ieee802_1x_eapol_tx_status(struct hostapd_data *hapd, struct sta_info *sta,
+ const u8 *buf, int len, int ack)
+{
+ const struct ieee802_1x_hdr *xhdr =
+ (const struct ieee802_1x_hdr *) buf;
+ const u8 *pos = buf + sizeof(*xhdr);
+ struct ieee802_1x_eapol_key *key;
+ if (len < (int) sizeof(*xhdr))
+ return 0;
wpa_printf(MSG_DEBUG, "IEEE 802.1X: " MACSTR " TX status - version=%d "
"type=%d length=%d - ack=%d",
MAC2STR(sta->addr), xhdr->version, xhdr->type,
be_to_host16(xhdr->length), ack);
+ if (xhdr->type != IEEE802_1X_TYPE_EAPOL_KEY)
+ return 0;
+
+ if (pos + sizeof(struct wpa_eapol_key) <= buf + len) {
+ const struct wpa_eapol_key *wpa;
+ wpa = (const struct wpa_eapol_key *) pos;
+ if (wpa->type == EAPOL_KEY_TYPE_RSN ||
+ wpa->type == EAPOL_KEY_TYPE_WPA)
+ wpa_auth_eapol_key_tx_status(hapd->wpa_auth,
+ sta->wpa_sm, ack);
+ }
+
/* EAPOL EAP-Packet packets are eventually re-sent by either Supplicant
* or Authenticator state machines, but EAPOL-Key packets are not
- * retransmitted in case of failure. Try to re-sent failed EAPOL-Key
+ * retransmitted in case of failure. Try to re-send failed EAPOL-Key
* packets couple of times because otherwise STA keys become
* unsynchronized with AP. */
- if (xhdr->type == IEEE802_1X_TYPE_EAPOL_KEY && !ack &&
- pos + sizeof(*key) <= buf + len) {
+ if (!ack && pos + sizeof(*key) <= buf + len) {
key = (struct ieee802_1x_eapol_key *) pos;
hostapd_logger(hapd, sta->addr, HOSTAPD_MODULE_IEEE8021X,
HOSTAPD_LEVEL_DEBUG, "did not Ack EAPOL-Key "
@@ -1789,8 +1993,17 @@ u8 * ieee802_1x_get_radius_class(struct eapol_state_machine *sm, size_t *len,
}
+struct wpabuf * ieee802_1x_get_radius_cui(struct eapol_state_machine *sm)
+{
+ if (sm == NULL)
+ return NULL;
+ return sm->radius_cui;
+}
+
+
const u8 * ieee802_1x_get_key(struct eapol_state_machine *sm, size_t *len)
{
+ *len = 0;
if (sm == NULL)
return NULL;
@@ -1848,6 +2061,7 @@ int ieee802_1x_get_mib_sta(struct hostapd_data *hapd, struct sta_info *sta,
{
int len = 0, ret;
struct eapol_state_machine *sm = sta->eapol_sm;
+ struct os_time t;
if (sm == NULL)
return 0;
@@ -1962,6 +2176,7 @@ int ieee802_1x_get_mib_sta(struct hostapd_data *hapd, struct sta_info *sta,
len += ret;
/* dot1xAuthSessionStatsTable */
+ os_get_time(&t);
ret = os_snprintf(buf + len, buflen - len,
/* TODO: dot1xAuthSessionOctetsRx */
/* TODO: dot1xAuthSessionOctetsTx */
@@ -1976,8 +2191,7 @@ int ieee802_1x_get_mib_sta(struct hostapd_data *hapd, struct sta_info *sta,
(wpa_key_mgmt_wpa_ieee8021x(
wpa_auth_sta_key_mgmt(sta->wpa_sm))) ?
1 : 2,
- (unsigned int) (time(NULL) -
- sta->acct_session_start),
+ (unsigned int) (t.sec - sta->acct_session_start),
sm->identity);
if (ret < 0 || (size_t) ret >= buflen - len)
return len;
@@ -2004,22 +2218,25 @@ static void ieee802_1x_finished(struct hostapd_data *hapd,
"Added PMKSA cache entry (IEEE 802.1X)");
}
-#ifdef CONFIG_WPS
- if (!success && (sta->flags & WLAN_STA_WPS)) {
+ if (!success) {
/*
* Many devices require deauthentication after WPS provisioning
* and some may not be be able to do that themselves, so
- * disconnect the client here.
+ * disconnect the client here. In addition, this may also
+ * benefit IEEE 802.1X/EAPOL authentication cases, too since
+ * the EAPOL PAE state machine would remain in HELD state for
+ * considerable amount of time and some EAP methods, like
+ * EAP-FAST with anonymous provisioning, may require another
+ * EAPOL authentication to be started to complete connection.
*/
- wpa_printf(MSG_DEBUG, "WPS: Force disconnection after "
- "EAP-Failure");
+ wpa_dbg(hapd->msg_ctx, MSG_DEBUG, "IEEE 802.1X: Force "
+ "disconnection after EAP-Failure");
/* Add a small sleep to increase likelihood of previously
* requested EAP-Failure TX getting out before this should the
* driver reorder operations.
*/
os_sleep(0, 10000);
ap_sta_disconnect(hapd, sta, sta->addr,
- WLAN_REASON_PREV_AUTH_NOT_VALID);
+ WLAN_REASON_IEEE_802_1X_AUTH_FAILED);
}
-#endif /* CONFIG_WPS */
}
diff --git a/contrib/wpa/src/ap/ieee802_1x.h b/contrib/wpa/src/ap/ieee802_1x.h
index 1a4d2eb..e1df940 100644
--- a/contrib/wpa/src/ap/ieee802_1x.h
+++ b/contrib/wpa/src/ap/ieee802_1x.h
@@ -1,15 +1,9 @@
/*
* hostapd / IEEE 802.1X-2004 Authenticator
- * Copyright (c) 2002-2007, Jouni Malinen <j@w1.fi>
+ * Copyright (c) 2002-2012, Jouni Malinen <j@w1.fi>
*
- * This program is free software; you can redistribute it and/or modify
- * it under the terms of the GNU General Public License version 2 as
- * published by the Free Software Foundation.
- *
- * Alternatively, this software may be distributed under the terms of BSD
- * license.
- *
- * See README and COPYING for more details.
+ * This software may be distributed under the terms of the BSD license.
+ * See README for more details.
*/
#ifndef IEEE802_1X_H
@@ -20,38 +14,8 @@ struct sta_info;
struct eapol_state_machine;
struct hostapd_config;
struct hostapd_bss_config;
-
-#ifdef _MSC_VER
-#pragma pack(push, 1)
-#endif /* _MSC_VER */
-
-/* RFC 3580, 4. RC4 EAPOL-Key Frame */
-
-struct ieee802_1x_eapol_key {
- u8 type;
- u16 key_length;
- u8 replay_counter[8]; /* does not repeat within the life of the keying
- * material used to encrypt the Key field;
- * 64-bit NTP timestamp MAY be used here */
- u8 key_iv[16]; /* cryptographically random number */
- u8 key_index; /* key flag in the most significant bit:
- * 0 = broadcast (default key),
- * 1 = unicast (key mapping key); key index is in the
- * 7 least significant bits */
- u8 key_signature[16]; /* HMAC-MD5 message integrity check computed with
- * MS-MPPE-Send-Key as the key */
-
- /* followed by key: if packet body length = 44 + key length, then the
- * key field (of key_length bytes) contains the key in encrypted form;
- * if packet body length = 44, key field is absent and key_length
- * represents the number of least significant octets from
- * MS-MPPE-Send-Key attribute to be used as the keying material;
- * RC4 key used in encryption = Key-IV + MS-MPPE-Recv-Key */
-} STRUCT_PACKED;
-
-#ifdef _MSC_VER
-#pragma pack(pop)
-#endif /* _MSC_VER */
+struct hostapd_radius_attr;
+struct radius_msg;
void ieee802_1x_receive(struct hostapd_data *hapd, const u8 *sa, const u8 *buf,
@@ -68,9 +32,12 @@ int ieee802_1x_init(struct hostapd_data *hapd);
void ieee802_1x_deinit(struct hostapd_data *hapd);
int ieee802_1x_tx_status(struct hostapd_data *hapd, struct sta_info *sta,
const u8 *buf, size_t len, int ack);
+int ieee802_1x_eapol_tx_status(struct hostapd_data *hapd, struct sta_info *sta,
+ const u8 *data, int len, int ack);
u8 * ieee802_1x_get_identity(struct eapol_state_machine *sm, size_t *len);
u8 * ieee802_1x_get_radius_class(struct eapol_state_machine *sm, size_t *len,
int idx);
+struct wpabuf * ieee802_1x_get_radius_cui(struct eapol_state_machine *sm);
const u8 * ieee802_1x_get_key(struct eapol_state_machine *sm, size_t *len);
void ieee802_1x_notify_port_enabled(struct eapol_state_machine *sm,
int enabled);
@@ -86,4 +53,9 @@ char *eap_type_text(u8 type);
const char *radius_mode_txt(struct hostapd_data *hapd);
int radius_sta_rate(struct hostapd_data *hapd, struct sta_info *sta);
+int add_common_radius_attr(struct hostapd_data *hapd,
+ struct hostapd_radius_attr *req_attr,
+ struct sta_info *sta,
+ struct radius_msg *msg);
+
#endif /* IEEE802_1X_H */
diff --git a/contrib/wpa/src/ap/p2p_hostapd.c b/contrib/wpa/src/ap/p2p_hostapd.c
new file mode 100644
index 0000000..795d313
--- /dev/null
+++ b/contrib/wpa/src/ap/p2p_hostapd.c
@@ -0,0 +1,114 @@
+/*
+ * hostapd / P2P integration
+ * Copyright (c) 2009-2010, Atheros Communications
+ *
+ * This software may be distributed under the terms of the BSD license.
+ * See README for more details.
+ */
+
+#include "utils/includes.h"
+
+#include "utils/common.h"
+#include "common/ieee802_11_defs.h"
+#include "p2p/p2p.h"
+#include "hostapd.h"
+#include "ap_config.h"
+#include "ap_drv_ops.h"
+#include "sta_info.h"
+#include "p2p_hostapd.h"
+
+
+#ifdef CONFIG_P2P
+
+int hostapd_p2p_get_mib_sta(struct hostapd_data *hapd, struct sta_info *sta,
+ char *buf, size_t buflen)
+{
+ if (sta->p2p_ie == NULL)
+ return 0;
+
+ return p2p_ie_text(sta->p2p_ie, buf, buf + buflen);
+}
+
+
+int hostapd_p2p_set_noa(struct hostapd_data *hapd, u8 count, int start,
+ int duration)
+{
+ wpa_printf(MSG_DEBUG, "P2P: Set NoA parameters: count=%u start=%d "
+ "duration=%d", count, start, duration);
+
+ if (count == 0) {
+ hapd->noa_enabled = 0;
+ hapd->noa_start = 0;
+ hapd->noa_duration = 0;
+ }
+
+ if (count != 255) {
+ wpa_printf(MSG_DEBUG, "P2P: Non-periodic NoA - set "
+ "NoA parameters");
+ return hostapd_driver_set_noa(hapd, count, start, duration);
+ }
+
+ hapd->noa_enabled = 1;
+ hapd->noa_start = start;
+ hapd->noa_duration = duration;
+
+ if (hapd->num_sta_no_p2p == 0) {
+ wpa_printf(MSG_DEBUG, "P2P: No legacy STAs connected - update "
+ "periodic NoA parameters");
+ return hostapd_driver_set_noa(hapd, count, start, duration);
+ }
+
+ wpa_printf(MSG_DEBUG, "P2P: Legacy STA(s) connected - do not enable "
+ "periodic NoA");
+
+ return 0;
+}
+
+
+void hostapd_p2p_non_p2p_sta_connected(struct hostapd_data *hapd)
+{
+ wpa_printf(MSG_DEBUG, "P2P: First non-P2P device connected");
+
+ if (hapd->noa_enabled) {
+ wpa_printf(MSG_DEBUG, "P2P: Disable periodic NoA");
+ hostapd_driver_set_noa(hapd, 0, 0, 0);
+ }
+}
+
+
+void hostapd_p2p_non_p2p_sta_disconnected(struct hostapd_data *hapd)
+{
+ wpa_printf(MSG_DEBUG, "P2P: Last non-P2P device disconnected");
+
+ if (hapd->noa_enabled) {
+ wpa_printf(MSG_DEBUG, "P2P: Enable periodic NoA");
+ hostapd_driver_set_noa(hapd, 255, hapd->noa_start,
+ hapd->noa_duration);
+ }
+}
+
+#endif /* CONFIG_P2P */
+
+
+#ifdef CONFIG_P2P_MANAGER
+u8 * hostapd_eid_p2p_manage(struct hostapd_data *hapd, u8 *eid)
+{
+ u8 bitmap;
+ *eid++ = WLAN_EID_VENDOR_SPECIFIC;
+ *eid++ = 4 + 3 + 1;
+ WPA_PUT_BE24(eid, OUI_WFA);
+ eid += 3;
+ *eid++ = P2P_OUI_TYPE;
+
+ *eid++ = P2P_ATTR_MANAGEABILITY;
+ WPA_PUT_LE16(eid, 1);
+ eid += 2;
+ bitmap = P2P_MAN_DEVICE_MANAGEMENT;
+ if (hapd->conf->p2p & P2P_ALLOW_CROSS_CONNECTION)
+ bitmap |= P2P_MAN_CROSS_CONNECTION_PERMITTED;
+ bitmap |= P2P_MAN_COEXISTENCE_OPTIONAL;
+ *eid++ = bitmap;
+
+ return eid;
+}
+#endif /* CONFIG_P2P_MANAGER */
diff --git a/contrib/wpa/src/ap/p2p_hostapd.h b/contrib/wpa/src/ap/p2p_hostapd.h
new file mode 100644
index 0000000..0e3921c
--- /dev/null
+++ b/contrib/wpa/src/ap/p2p_hostapd.h
@@ -0,0 +1,35 @@
+/*
+ * hostapd / P2P integration
+ * Copyright (c) 2009-2010, Atheros Communications
+ *
+ * This software may be distributed under the terms of the BSD license.
+ * See README for more details.
+ */
+
+#ifndef P2P_HOSTAPD_H
+#define P2P_HOSTAPD_H
+
+#ifdef CONFIG_P2P
+
+int hostapd_p2p_get_mib_sta(struct hostapd_data *hapd, struct sta_info *sta,
+ char *buf, size_t buflen);
+int hostapd_p2p_set_noa(struct hostapd_data *hapd, u8 count, int start,
+ int duration);
+void hostapd_p2p_non_p2p_sta_connected(struct hostapd_data *hapd);
+void hostapd_p2p_non_p2p_sta_disconnected(struct hostapd_data *hapd);
+
+
+#else /* CONFIG_P2P */
+
+static inline int hostapd_p2p_get_mib_sta(struct hostapd_data *hapd,
+ struct sta_info *sta,
+ char *buf, size_t buflen)
+{
+ return 0;
+}
+
+#endif /* CONFIG_P2P */
+
+u8 * hostapd_eid_p2p_manage(struct hostapd_data *hapd, u8 *eid);
+
+#endif /* P2P_HOSTAPD_H */
diff --git a/contrib/wpa/src/ap/peerkey_auth.c b/contrib/wpa/src/ap/peerkey_auth.c
index f68c479..ba5c606 100644
--- a/contrib/wpa/src/ap/peerkey_auth.c
+++ b/contrib/wpa/src/ap/peerkey_auth.c
@@ -2,14 +2,8 @@
* hostapd - PeerKey for Direct Link Setup (DLS)
* Copyright (c) 2006-2009, Jouni Malinen <j@w1.fi>
*
- * This program is free software; you can redistribute it and/or modify
- * it under the terms of the GNU General Public License version 2 as
- * published by the Free Software Foundation.
- *
- * Alternatively, this software may be distributed under the terms of BSD
- * license.
- *
- * See README and COPYING for more details.
+ * This software may be distributed under the terms of the BSD license.
+ * See README for more details.
*/
#include "utils/includes.h"
@@ -18,6 +12,7 @@
#include "utils/eloop.h"
#include "crypto/sha1.h"
#include "crypto/sha256.h"
+#include "crypto/random.h"
#include "wpa_auth.h"
#include "wpa_auth_i.h"
#include "wpa_auth_ie.h"
@@ -294,7 +289,7 @@ void wpa_smk_m3(struct wpa_authenticator *wpa_auth,
return;
}
- if (os_get_random(smk, PMK_LEN)) {
+ if (random_get_bytes(smk, PMK_LEN)) {
wpa_printf(MSG_DEBUG, "RSN: Failed to generate SMK");
return;
}
diff --git a/contrib/wpa/src/ap/pmksa_cache_auth.c b/contrib/wpa/src/ap/pmksa_cache_auth.c
index 22f44b7..d27fd30 100644
--- a/contrib/wpa/src/ap/pmksa_cache_auth.c
+++ b/contrib/wpa/src/ap/pmksa_cache_auth.c
@@ -1,15 +1,9 @@
/*
* hostapd - PMKSA cache for IEEE 802.11i RSN
- * Copyright (c) 2004-2008, Jouni Malinen <j@w1.fi>
+ * Copyright (c) 2004-2008, 2012, Jouni Malinen <j@w1.fi>
*
- * This program is free software; you can redistribute it and/or modify
- * it under the terms of the GNU General Public License version 2 as
- * published by the Free Software Foundation.
- *
- * Alternatively, this software may be distributed under the terms of BSD
- * license.
- *
- * See README and COPYING for more details.
+ * This software may be distributed under the terms of the BSD license.
+ * See README for more details.
*/
#include "utils/includes.h"
@@ -46,6 +40,7 @@ static void _pmksa_cache_free_entry(struct rsn_pmksa_cache_entry *entry)
if (entry == NULL)
return;
os_free(entry->identity);
+ wpabuf_free(entry->cui);
#ifndef CONFIG_NO_RADIUS
radius_free_class(&entry->radius_class);
#endif /* CONFIG_NO_RADIUS */
@@ -100,11 +95,9 @@ static void pmksa_cache_expire(void *eloop_ctx, void *timeout_ctx)
os_get_time(&now);
while (pmksa->pmksa && pmksa->pmksa->expiration <= now.sec) {
- struct rsn_pmksa_cache_entry *entry = pmksa->pmksa;
- pmksa->pmksa = entry->next;
wpa_printf(MSG_DEBUG, "RSN: expired PMKSA cache entry for "
- MACSTR, MAC2STR(entry->spa));
- pmksa_cache_free_entry(pmksa, entry);
+ MACSTR, MAC2STR(pmksa->pmksa->spa));
+ pmksa_cache_free_entry(pmksa, pmksa->pmksa);
}
pmksa_cache_set_expiration(pmksa);
@@ -142,6 +135,9 @@ static void pmksa_cache_from_eapol_data(struct rsn_pmksa_cache_entry *entry,
}
}
+ if (eapol->radius_cui)
+ entry->cui = wpabuf_dup(eapol->radius_cui);
+
#ifndef CONFIG_NO_RADIUS
radius_copy_class(&entry->radius_class, &eapol->radius_class);
#endif /* CONFIG_NO_RADIUS */
@@ -169,6 +165,11 @@ void pmksa_cache_to_eapol_data(struct rsn_pmksa_cache_entry *entry,
eapol->identity, eapol->identity_len);
}
+ if (entry->cui) {
+ wpabuf_free(eapol->radius_cui);
+ eapol->radius_cui = wpabuf_dup(entry->cui);
+ }
+
#ifndef CONFIG_NO_RADIUS
radius_free_class(&eapol->radius_class);
radius_copy_class(&eapol->radius_class, &entry->radius_class);
@@ -208,6 +209,8 @@ static void pmksa_cache_link_entry(struct rsn_pmksa_cache *pmksa,
pmksa->pmkid[PMKID_HASH(entry->pmkid)] = entry;
pmksa->pmksa_count++;
+ if (prev == NULL)
+ pmksa_cache_set_expiration(pmksa);
wpa_printf(MSG_DEBUG, "RSN: added PMKSA cache entry for " MACSTR,
MAC2STR(entry->spa));
wpa_hexdump(MSG_DEBUG, "RSN: added PMKID", entry->pmkid, PMKID_LEN);
@@ -305,6 +308,8 @@ pmksa_cache_add_okc(struct rsn_pmksa_cache *pmksa,
old_entry->identity_len);
}
}
+ if (old_entry->cui)
+ entry->cui = wpabuf_dup(old_entry->cui);
#ifndef CONFIG_NO_RADIUS
radius_copy_class(&entry->radius_class, &old_entry->radius_class);
#endif /* CONFIG_NO_RADIUS */
diff --git a/contrib/wpa/src/ap/pmksa_cache_auth.h b/contrib/wpa/src/ap/pmksa_cache_auth.h
index 9628b13..d473f3f 100644
--- a/contrib/wpa/src/ap/pmksa_cache_auth.h
+++ b/contrib/wpa/src/ap/pmksa_cache_auth.h
@@ -1,15 +1,9 @@
/*
* hostapd - PMKSA cache for IEEE 802.11i RSN
- * Copyright (c) 2004-2008, Jouni Malinen <j@w1.fi>
+ * Copyright (c) 2004-2008, 2012, Jouni Malinen <j@w1.fi>
*
- * This program is free software; you can redistribute it and/or modify
- * it under the terms of the GNU General Public License version 2 as
- * published by the Free Software Foundation.
- *
- * Alternatively, this software may be distributed under the terms of BSD
- * license.
- *
- * See README and COPYING for more details.
+ * This software may be distributed under the terms of the BSD license.
+ * See README for more details.
*/
#ifndef PMKSA_CACHE_H
@@ -31,6 +25,7 @@ struct rsn_pmksa_cache_entry {
u8 *identity;
size_t identity_len;
+ struct wpabuf *cui;
struct radius_class_data radius_class;
u8 eap_type_authsrv;
int vlan_id;
diff --git a/contrib/wpa/src/ap/preauth_auth.c b/contrib/wpa/src/ap/preauth_auth.c
index 8e13315..3e0c800 100644
--- a/contrib/wpa/src/ap/preauth_auth.c
+++ b/contrib/wpa/src/ap/preauth_auth.c
@@ -2,14 +2,8 @@
* hostapd - Authenticator for IEEE 802.11i RSN pre-authentication
* Copyright (c) 2004-2007, Jouni Malinen <j@w1.fi>
*
- * This program is free software; you can redistribute it and/or modify
- * it under the terms of the GNU General Public License version 2 as
- * published by the Free Software Foundation.
- *
- * Alternatively, this software may be distributed under the terms of BSD
- * license.
- *
- * See README and COPYING for more details.
+ * This software may be distributed under the terms of the BSD license.
+ * See README for more details.
*/
#include "utils/includes.h"
diff --git a/contrib/wpa/src/ap/preauth_auth.h b/contrib/wpa/src/ap/preauth_auth.h
index 5348bee..69fb356 100644
--- a/contrib/wpa/src/ap/preauth_auth.h
+++ b/contrib/wpa/src/ap/preauth_auth.h
@@ -2,14 +2,8 @@
* hostapd - Authenticator for IEEE 802.11i RSN pre-authentication
* Copyright (c) 2004-2005, Jouni Malinen <j@w1.fi>
*
- * This program is free software; you can redistribute it and/or modify
- * it under the terms of the GNU General Public License version 2 as
- * published by the Free Software Foundation.
- *
- * Alternatively, this software may be distributed under the terms of BSD
- * license.
- *
- * See README and COPYING for more details.
+ * This software may be distributed under the terms of the BSD license.
+ * See README for more details.
*/
#ifndef PREAUTH_H
diff --git a/contrib/wpa/src/ap/sta_info.c b/contrib/wpa/src/ap/sta_info.c
index 335c9a5..97cd013 100644
--- a/contrib/wpa/src/ap/sta_info.c
+++ b/contrib/wpa/src/ap/sta_info.c
@@ -1,15 +1,9 @@
/*
* hostapd / Station table
- * Copyright (c) 2002-2009, Jouni Malinen <j@w1.fi>
+ * Copyright (c) 2002-2011, Jouni Malinen <j@w1.fi>
*
- * This program is free software; you can redistribute it and/or modify
- * it under the terms of the GNU General Public License version 2 as
- * published by the Free Software Foundation.
- *
- * Alternatively, this software may be distributed under the terms of BSD
- * license.
- *
- * See README and COPYING for more details.
+ * This software may be distributed under the terms of the BSD license.
+ * See README for more details.
*/
#include "utils/includes.h"
@@ -17,27 +11,36 @@
#include "utils/common.h"
#include "utils/eloop.h"
#include "common/ieee802_11_defs.h"
+#include "common/wpa_ctrl.h"
#include "radius/radius.h"
#include "radius/radius_client.h"
#include "drivers/driver.h"
+#include "p2p/p2p.h"
#include "hostapd.h"
#include "accounting.h"
#include "ieee802_1x.h"
#include "ieee802_11.h"
+#include "ieee802_11_auth.h"
#include "wpa_auth.h"
#include "preauth_auth.h"
#include "ap_config.h"
#include "beacon.h"
#include "ap_mlme.h"
#include "vlan_init.h"
+#include "p2p_hostapd.h"
+#include "ap_drv_ops.h"
+#include "gas_serv.h"
#include "sta_info.h"
static void ap_sta_remove_in_other_bss(struct hostapd_data *hapd,
struct sta_info *sta);
static void ap_handle_session_timer(void *eloop_ctx, void *timeout_ctx);
+static void ap_sta_deauth_cb_timeout(void *eloop_ctx, void *timeout_ctx);
+static void ap_sta_disassoc_cb_timeout(void *eloop_ctx, void *timeout_ctx);
#ifdef CONFIG_IEEE80211W
static void ap_sa_query_timer(void *eloop_ctx, void *timeout_ctx);
#endif /* CONFIG_IEEE80211W */
+static int ap_sta_remove(struct hostapd_data *hapd, struct sta_info *sta);
int ap_for_each_sta(struct hostapd_data *hapd,
int (*cb)(struct hostapd_data *hapd, struct sta_info *sta,
@@ -121,11 +124,14 @@ void ap_free_sta(struct hostapd_data *hapd, struct sta_info *sta)
accounting_sta_stop(hapd, sta);
+ /* just in case */
+ ap_sta_set_authorized(hapd, sta, 0);
+
if (sta->flags & WLAN_STA_WDS)
- hapd->drv.set_wds_sta(hapd, sta->addr, sta->aid, 0);
+ hostapd_set_wds_sta(hapd, sta->addr, sta->aid, 0);
if (!(sta->flags & WLAN_STA_PREAUTH))
- hapd->drv.sta_remove(hapd, sta->addr);
+ hostapd_drv_sta_remove(hapd, sta->addr);
ap_sta_hash_del(hapd, sta);
ap_sta_list_del(hapd, sta);
@@ -173,6 +179,15 @@ void ap_free_sta(struct hostapd_data *hapd, struct sta_info *sta)
hapd->iface->num_sta_ht_20mhz--;
}
+#ifdef CONFIG_P2P
+ if (sta->no_p2p_set) {
+ sta->no_p2p_set = 0;
+ hapd->num_sta_no_p2p--;
+ if (hapd->num_sta_no_p2p == 0)
+ hostapd_p2p_non_p2p_sta_disconnected(hapd);
+ }
+#endif /* CONFIG_P2P */
+
#if defined(NEED_AP_MLME) && defined(CONFIG_IEEE80211N)
if (hostapd_ht_operation_update(hapd->iface) > 0)
set_beacon++;
@@ -181,8 +196,12 @@ void ap_free_sta(struct hostapd_data *hapd, struct sta_info *sta)
if (set_beacon)
ieee802_11_set_beacons(hapd->iface);
+ wpa_printf(MSG_DEBUG, "%s: cancel ap_handle_timer for " MACSTR,
+ __func__, MAC2STR(sta->addr));
eloop_cancel_timeout(ap_handle_timer, hapd, sta);
eloop_cancel_timeout(ap_handle_session_timer, hapd, sta);
+ eloop_cancel_timeout(ap_sta_deauth_cb_timeout, hapd, sta);
+ eloop_cancel_timeout(ap_sta_disassoc_cb_timeout, hapd, sta);
ieee802_1x_free_station(sta);
wpa_auth_sta_deinit(sta->wpa_sm);
@@ -199,9 +218,27 @@ void ap_free_sta(struct hostapd_data *hapd, struct sta_info *sta)
eloop_cancel_timeout(ap_sa_query_timer, hapd, sta);
#endif /* CONFIG_IEEE80211W */
+#ifdef CONFIG_P2P
+ p2p_group_notif_disassoc(hapd->p2p_group, sta->addr);
+#endif /* CONFIG_P2P */
+
+#ifdef CONFIG_INTERWORKING
+ if (sta->gas_dialog) {
+ int i;
+ for (i = 0; i < GAS_DIALOG_MAX; i++)
+ gas_serv_dialog_clear(&sta->gas_dialog[i]);
+ os_free(sta->gas_dialog);
+ }
+#endif /* CONFIG_INTERWORKING */
+
wpabuf_free(sta->wps_ie);
+ wpabuf_free(sta->p2p_ie);
+ wpabuf_free(sta->hs20_ie);
os_free(sta->ht_capabilities);
+ hostapd_free_psk_list(sta->psk);
+ os_free(sta->identity);
+ os_free(sta->radius_cui);
os_free(sta);
}
@@ -241,6 +278,9 @@ void ap_handle_timer(void *eloop_ctx, void *timeout_ctx)
struct sta_info *sta = timeout_ctx;
unsigned long next_time = 0;
+ wpa_printf(MSG_DEBUG, "%s: " MACSTR " flags=0x%x timeout_next=%d",
+ __func__, MAC2STR(sta->addr), sta->flags,
+ sta->timeout_next);
if (sta->timeout_next == STA_REMOVE) {
hostapd_logger(hapd, sta->addr, HOSTAPD_MODULE_IEEE80211,
HOSTAPD_LEVEL_INFO, "deauthenticated due to "
@@ -253,27 +293,51 @@ void ap_handle_timer(void *eloop_ctx, void *timeout_ctx)
(sta->timeout_next == STA_NULLFUNC ||
sta->timeout_next == STA_DISASSOC)) {
int inactive_sec;
- wpa_printf(MSG_DEBUG, "Checking STA " MACSTR " inactivity:",
- MAC2STR(sta->addr));
- inactive_sec = hapd->drv.get_inact_sec(hapd, sta->addr);
+ /*
+ * Add random value to timeout so that we don't end up bouncing
+ * all stations at the same time if we have lots of associated
+ * stations that are idle (but keep re-associating).
+ */
+ int fuzz = os_random() % 20;
+ inactive_sec = hostapd_drv_get_inact_sec(hapd, sta->addr);
if (inactive_sec == -1) {
- wpa_printf(MSG_DEBUG, "Could not get station info "
- "from kernel driver for " MACSTR ".",
- MAC2STR(sta->addr));
+ wpa_msg(hapd->msg_ctx, MSG_DEBUG,
+ "Check inactivity: Could not "
+ "get station info from kernel driver for "
+ MACSTR, MAC2STR(sta->addr));
+ /*
+ * The driver may not support this functionality.
+ * Anyway, try again after the next inactivity timeout,
+ * but do not disconnect the station now.
+ */
+ next_time = hapd->conf->ap_max_inactivity + fuzz;
} else if (inactive_sec < hapd->conf->ap_max_inactivity &&
sta->flags & WLAN_STA_ASSOC) {
/* station activity detected; reset timeout state */
- wpa_printf(MSG_DEBUG, " Station has been active");
+ wpa_msg(hapd->msg_ctx, MSG_DEBUG,
+ "Station " MACSTR " has been active %is ago",
+ MAC2STR(sta->addr), inactive_sec);
sta->timeout_next = STA_NULLFUNC;
- next_time = hapd->conf->ap_max_inactivity -
+ next_time = hapd->conf->ap_max_inactivity + fuzz -
inactive_sec;
+ } else {
+ wpa_msg(hapd->msg_ctx, MSG_DEBUG,
+ "Station " MACSTR " has been "
+ "inactive too long: %d sec, max allowed: %d",
+ MAC2STR(sta->addr), inactive_sec,
+ hapd->conf->ap_max_inactivity);
+
+ if (hapd->conf->skip_inactivity_poll)
+ sta->timeout_next = STA_DISASSOC;
}
}
if ((sta->flags & WLAN_STA_ASSOC) &&
sta->timeout_next == STA_DISASSOC &&
- !(sta->flags & WLAN_STA_PENDING_POLL)) {
- wpa_printf(MSG_DEBUG, " Station has ACKed data poll");
+ !(sta->flags & WLAN_STA_PENDING_POLL) &&
+ !hapd->conf->skip_inactivity_poll) {
+ wpa_msg(hapd->msg_ctx, MSG_DEBUG, "Station " MACSTR
+ " has ACKed data poll", MAC2STR(sta->addr));
/* data nullfunc frame poll did not produce TX errors; assume
* station ACKed it */
sta->timeout_next = STA_NULLFUNC;
@@ -281,6 +345,9 @@ void ap_handle_timer(void *eloop_ctx, void *timeout_ctx)
}
if (next_time) {
+ wpa_printf(MSG_DEBUG, "%s: register ap_handle_timer timeout "
+ "for " MACSTR " (%lu seconds)",
+ __func__, MAC2STR(sta->addr), next_time);
eloop_register_timeout(next_time, 0, ap_handle_timer, hapd,
sta);
return;
@@ -288,52 +355,24 @@ void ap_handle_timer(void *eloop_ctx, void *timeout_ctx)
if (sta->timeout_next == STA_NULLFUNC &&
(sta->flags & WLAN_STA_ASSOC)) {
-#ifndef CONFIG_NATIVE_WINDOWS
- /* send data frame to poll STA and check whether this frame
- * is ACKed */
- struct ieee80211_hdr hdr;
-
- wpa_printf(MSG_DEBUG, " Polling STA with data frame");
+ wpa_printf(MSG_DEBUG, " Polling STA");
sta->flags |= WLAN_STA_PENDING_POLL;
-
- os_memset(&hdr, 0, sizeof(hdr));
- if (hapd->driver &&
- os_strcmp(hapd->driver->name, "hostap") == 0) {
- /*
- * WLAN_FC_STYPE_NULLFUNC would be more appropriate,
- * but it is apparently not retried so TX Exc events
- * are not received for it.
- */
- hdr.frame_control =
- IEEE80211_FC(WLAN_FC_TYPE_DATA,
- WLAN_FC_STYPE_DATA);
- } else {
- hdr.frame_control =
- IEEE80211_FC(WLAN_FC_TYPE_DATA,
- WLAN_FC_STYPE_NULLFUNC);
- }
-
- hdr.frame_control |= host_to_le16(WLAN_FC_FROMDS);
- os_memcpy(hdr.IEEE80211_DA_FROMDS, sta->addr, ETH_ALEN);
- os_memcpy(hdr.IEEE80211_BSSID_FROMDS, hapd->own_addr,
- ETH_ALEN);
- os_memcpy(hdr.IEEE80211_SA_FROMDS, hapd->own_addr, ETH_ALEN);
-
- if (hapd->drv.send_mgmt_frame(hapd, &hdr, sizeof(hdr)) < 0)
- perror("ap_handle_timer: send");
-#endif /* CONFIG_NATIVE_WINDOWS */
+ hostapd_drv_poll_client(hapd, hapd->own_addr, sta->addr,
+ sta->flags & WLAN_STA_WMM);
} else if (sta->timeout_next != STA_REMOVE) {
int deauth = sta->timeout_next == STA_DEAUTH;
- wpa_printf(MSG_DEBUG, "Sending %s info to STA " MACSTR,
- deauth ? "deauthentication" : "disassociation",
- MAC2STR(sta->addr));
+ wpa_dbg(hapd->msg_ctx, MSG_DEBUG,
+ "Timeout, sending %s info to STA " MACSTR,
+ deauth ? "deauthentication" : "disassociation",
+ MAC2STR(sta->addr));
if (deauth) {
- hapd->drv.sta_deauth(hapd, sta->addr,
- WLAN_REASON_PREV_AUTH_NOT_VALID);
+ hostapd_drv_sta_deauth(
+ hapd, sta->addr,
+ WLAN_REASON_PREV_AUTH_NOT_VALID);
} else {
- hapd->drv.sta_disassoc(
+ hostapd_drv_sta_disassoc(
hapd, sta->addr,
WLAN_REASON_DISASSOC_DUE_TO_INACTIVITY);
}
@@ -342,10 +381,14 @@ void ap_handle_timer(void *eloop_ctx, void *timeout_ctx)
switch (sta->timeout_next) {
case STA_NULLFUNC:
sta->timeout_next = STA_DISASSOC;
+ wpa_printf(MSG_DEBUG, "%s: register ap_handle_timer timeout "
+ "for " MACSTR " (%d seconds - AP_DISASSOC_DELAY)",
+ __func__, MAC2STR(sta->addr), AP_DISASSOC_DELAY);
eloop_register_timeout(AP_DISASSOC_DELAY, 0, ap_handle_timer,
hapd, sta);
break;
case STA_DISASSOC:
+ ap_sta_set_authorized(hapd, sta, 0);
sta->flags &= ~WLAN_STA_ASSOC;
ieee802_1x_notify_port_enabled(sta->eapol_sm, 0);
if (!sta->acct_terminate_cause)
@@ -357,6 +400,9 @@ void ap_handle_timer(void *eloop_ctx, void *timeout_ctx)
HOSTAPD_LEVEL_INFO, "disassociated due to "
"inactivity");
sta->timeout_next = STA_DEAUTH;
+ wpa_printf(MSG_DEBUG, "%s: register ap_handle_timer timeout "
+ "for " MACSTR " (%d seconds - AP_DEAUTH_DELAY)",
+ __func__, MAC2STR(sta->addr), AP_DEAUTH_DELAY);
eloop_register_timeout(AP_DEAUTH_DELAY, 0, ap_handle_timer,
hapd, sta);
mlme_disassociate_indication(
@@ -366,7 +412,7 @@ void ap_handle_timer(void *eloop_ctx, void *timeout_ctx)
case STA_REMOVE:
hostapd_logger(hapd, sta->addr, HOSTAPD_MODULE_IEEE80211,
HOSTAPD_LEVEL_INFO, "deauthenticated due to "
- "inactivity");
+ "inactivity (timer DEAUTH/REMOVE)");
if (!sta->acct_terminate_cause)
sta->acct_terminate_cause =
RADIUS_ACCT_TERMINATE_CAUSE_IDLE_TIMEOUT;
@@ -385,8 +431,14 @@ static void ap_handle_session_timer(void *eloop_ctx, void *timeout_ctx)
struct sta_info *sta = timeout_ctx;
u8 addr[ETH_ALEN];
- if (!(sta->flags & WLAN_STA_AUTH))
+ if (!(sta->flags & WLAN_STA_AUTH)) {
+ if (sta->flags & WLAN_STA_GAS) {
+ wpa_printf(MSG_DEBUG, "GAS: Remove temporary STA "
+ "entry " MACSTR, MAC2STR(sta->addr));
+ ap_free_sta(hapd, sta);
+ }
return;
+ }
mlme_deauthenticate_indication(hapd, sta,
WLAN_REASON_PREV_AUTH_NOT_VALID);
@@ -397,7 +449,7 @@ static void ap_handle_session_timer(void *eloop_ctx, void *timeout_ctx)
RADIUS_ACCT_TERMINATE_CAUSE_SESSION_TIMEOUT;
os_memcpy(addr, sta->addr, ETH_ALEN);
ap_free_sta(hapd, sta);
- hapd->drv.sta_deauth(hapd, addr, WLAN_REASON_PREV_AUTH_NOT_VALID);
+ hostapd_drv_sta_deauth(hapd, addr, WLAN_REASON_PREV_AUTH_NOT_VALID);
}
@@ -441,8 +493,13 @@ struct sta_info * ap_sta_add(struct hostapd_data *hapd, const u8 *addr)
return NULL;
}
sta->acct_interim_interval = hapd->conf->acct_interim_interval;
+ accounting_sta_get_id(hapd, sta);
/* initialize STA info data */
+ wpa_printf(MSG_DEBUG, "%s: register ap_handle_timer timeout "
+ "for " MACSTR " (%d seconds - ap_max_inactivity)",
+ __func__, MAC2STR(addr),
+ hapd->conf->ap_max_inactivity);
eloop_register_timeout(hapd->conf->ap_max_inactivity, 0,
ap_handle_timer, hapd, sta);
os_memcpy(sta->addr, addr, ETH_ALEN);
@@ -463,7 +520,7 @@ static int ap_sta_remove(struct hostapd_data *hapd, struct sta_info *sta)
wpa_printf(MSG_DEBUG, "Removing STA " MACSTR " from kernel driver",
MAC2STR(sta->addr));
- if (hapd->drv.sta_remove(hapd, sta->addr) &&
+ if (hostapd_drv_sta_remove(hapd, sta->addr) &&
sta->flags & WLAN_STA_ASSOC) {
wpa_printf(MSG_DEBUG, "Could not remove station " MACSTR
" from kernel driver.", MAC2STR(sta->addr));
@@ -498,21 +555,51 @@ static void ap_sta_remove_in_other_bss(struct hostapd_data *hapd,
}
+static void ap_sta_disassoc_cb_timeout(void *eloop_ctx, void *timeout_ctx)
+{
+ struct hostapd_data *hapd = eloop_ctx;
+ struct sta_info *sta = timeout_ctx;
+
+ ap_sta_remove(hapd, sta);
+ mlme_disassociate_indication(hapd, sta, sta->disassoc_reason);
+}
+
+
void ap_sta_disassociate(struct hostapd_data *hapd, struct sta_info *sta,
u16 reason)
{
wpa_printf(MSG_DEBUG, "%s: disassociate STA " MACSTR,
hapd->conf->iface, MAC2STR(sta->addr));
sta->flags &= ~WLAN_STA_ASSOC;
- ap_sta_remove(hapd, sta);
+ ap_sta_set_authorized(hapd, sta, 0);
sta->timeout_next = STA_DEAUTH;
+ wpa_printf(MSG_DEBUG, "%s: reschedule ap_handle_timer timeout "
+ "for " MACSTR " (%d seconds - "
+ "AP_MAX_INACTIVITY_AFTER_DISASSOC)",
+ __func__, MAC2STR(sta->addr),
+ AP_MAX_INACTIVITY_AFTER_DISASSOC);
eloop_cancel_timeout(ap_handle_timer, hapd, sta);
eloop_register_timeout(AP_MAX_INACTIVITY_AFTER_DISASSOC, 0,
ap_handle_timer, hapd, sta);
accounting_sta_stop(hapd, sta);
ieee802_1x_free_station(sta);
- mlme_disassociate_indication(hapd, sta, reason);
+ sta->disassoc_reason = reason;
+ sta->flags |= WLAN_STA_PENDING_DISASSOC_CB;
+ eloop_cancel_timeout(ap_sta_disassoc_cb_timeout, hapd, sta);
+ eloop_register_timeout(hapd->iface->drv_flags &
+ WPA_DRIVER_FLAGS_DEAUTH_TX_STATUS ? 2 : 0, 0,
+ ap_sta_disassoc_cb_timeout, hapd, sta);
+}
+
+
+static void ap_sta_deauth_cb_timeout(void *eloop_ctx, void *timeout_ctx)
+{
+ struct hostapd_data *hapd = eloop_ctx;
+ struct sta_info *sta = timeout_ctx;
+
+ ap_sta_remove(hapd, sta);
+ mlme_deauthenticate_indication(hapd, sta, sta->deauth_reason);
}
@@ -522,16 +609,43 @@ void ap_sta_deauthenticate(struct hostapd_data *hapd, struct sta_info *sta,
wpa_printf(MSG_DEBUG, "%s: deauthenticate STA " MACSTR,
hapd->conf->iface, MAC2STR(sta->addr));
sta->flags &= ~(WLAN_STA_AUTH | WLAN_STA_ASSOC);
- ap_sta_remove(hapd, sta);
+ ap_sta_set_authorized(hapd, sta, 0);
sta->timeout_next = STA_REMOVE;
+ wpa_printf(MSG_DEBUG, "%s: reschedule ap_handle_timer timeout "
+ "for " MACSTR " (%d seconds - "
+ "AP_MAX_INACTIVITY_AFTER_DEAUTH)",
+ __func__, MAC2STR(sta->addr),
+ AP_MAX_INACTIVITY_AFTER_DEAUTH);
eloop_cancel_timeout(ap_handle_timer, hapd, sta);
eloop_register_timeout(AP_MAX_INACTIVITY_AFTER_DEAUTH, 0,
ap_handle_timer, hapd, sta);
accounting_sta_stop(hapd, sta);
ieee802_1x_free_station(sta);
- mlme_deauthenticate_indication(hapd, sta, reason);
+ sta->deauth_reason = reason;
+ sta->flags |= WLAN_STA_PENDING_DEAUTH_CB;
+ eloop_cancel_timeout(ap_sta_deauth_cb_timeout, hapd, sta);
+ eloop_register_timeout(hapd->iface->drv_flags &
+ WPA_DRIVER_FLAGS_DEAUTH_TX_STATUS ? 2 : 0, 0,
+ ap_sta_deauth_cb_timeout, hapd, sta);
+}
+
+
+#ifdef CONFIG_WPS
+int ap_sta_wps_cancel(struct hostapd_data *hapd,
+ struct sta_info *sta, void *ctx)
+{
+ if (sta && (sta->flags & WLAN_STA_WPS)) {
+ ap_sta_deauthenticate(hapd, sta,
+ WLAN_REASON_PREV_AUTH_NOT_VALID);
+ wpa_printf(MSG_DEBUG, "WPS: %s: Deauth sta=" MACSTR,
+ __func__, MAC2STR(sta->addr));
+ return 1;
+ }
+
+ return 0;
}
+#endif /* CONFIG_WPS */
int ap_sta_bind_vlan(struct hostapd_data *hapd, struct sta_info *sta,
@@ -636,7 +750,7 @@ int ap_sta_bind_vlan(struct hostapd_data *hapd, struct sta_info *sta,
if (wpa_auth_sta_set_vlan(sta->wpa_sm, sta->vlan_id) < 0)
wpa_printf(MSG_INFO, "Failed to update VLAN-ID for WPA");
- ret = hapd->drv.set_sta_vlan(iface, hapd, sta->addr, sta->vlan_id);
+ ret = hostapd_drv_set_sta_vlan(iface, hapd, sta->addr, sta->vlan_id);
if (ret < 0) {
hostapd_logger(hapd, sta->addr, HOSTAPD_MODULE_IEEE80211,
HOSTAPD_LEVEL_DEBUG, "could not bind the STA "
@@ -686,8 +800,9 @@ static void ap_sa_query_timer(void *eloop_ctx, void *timeout_ctx)
ap_check_sa_query_timeout(hapd, sta))
return;
- nbuf = os_realloc(sta->sa_query_trans_id,
- (sta->sa_query_count + 1) * WLAN_SA_QUERY_TR_ID_LEN);
+ nbuf = os_realloc_array(sta->sa_query_trans_id,
+ sta->sa_query_count + 1,
+ WLAN_SA_QUERY_TR_ID_LEN);
if (nbuf == NULL)
return;
if (sta->sa_query_count == 0) {
@@ -709,9 +824,7 @@ static void ap_sa_query_timer(void *eloop_ctx, void *timeout_ctx)
HOSTAPD_LEVEL_DEBUG,
"association SA Query attempt %d", sta->sa_query_count);
-#ifdef NEED_AP_MLME
ieee802_11_send_sa_query_req(hapd, sta->addr, trans_id);
-#endif /* NEED_AP_MLME */
}
@@ -732,6 +845,73 @@ void ap_sta_stop_sa_query(struct hostapd_data *hapd, struct sta_info *sta)
#endif /* CONFIG_IEEE80211W */
+void ap_sta_set_authorized(struct hostapd_data *hapd, struct sta_info *sta,
+ int authorized)
+{
+ const u8 *dev_addr = NULL;
+#ifdef CONFIG_P2P
+ u8 addr[ETH_ALEN];
+#endif /* CONFIG_P2P */
+
+ if (!!authorized == !!(sta->flags & WLAN_STA_AUTHORIZED))
+ return;
+
+#ifdef CONFIG_P2P
+ if (hapd->p2p_group == NULL) {
+ if (sta->p2p_ie != NULL &&
+ p2p_parse_dev_addr_in_p2p_ie(sta->p2p_ie, addr) == 0)
+ dev_addr = addr;
+ } else
+ dev_addr = p2p_group_get_dev_addr(hapd->p2p_group, sta->addr);
+#endif /* CONFIG_P2P */
+
+ if (authorized) {
+ if (dev_addr)
+ wpa_msg(hapd->msg_ctx, MSG_INFO, AP_STA_CONNECTED
+ MACSTR " p2p_dev_addr=" MACSTR,
+ MAC2STR(sta->addr), MAC2STR(dev_addr));
+ else
+ wpa_msg(hapd->msg_ctx, MSG_INFO, AP_STA_CONNECTED
+ MACSTR, MAC2STR(sta->addr));
+ if (hapd->msg_ctx_parent &&
+ hapd->msg_ctx_parent != hapd->msg_ctx && dev_addr)
+ wpa_msg(hapd->msg_ctx_parent, MSG_INFO,
+ AP_STA_CONNECTED MACSTR " p2p_dev_addr="
+ MACSTR,
+ MAC2STR(sta->addr), MAC2STR(dev_addr));
+ else if (hapd->msg_ctx_parent &&
+ hapd->msg_ctx_parent != hapd->msg_ctx)
+ wpa_msg(hapd->msg_ctx_parent, MSG_INFO,
+ AP_STA_CONNECTED MACSTR, MAC2STR(sta->addr));
+
+ sta->flags |= WLAN_STA_AUTHORIZED;
+ } else {
+ if (dev_addr)
+ wpa_msg(hapd->msg_ctx, MSG_INFO, AP_STA_DISCONNECTED
+ MACSTR " p2p_dev_addr=" MACSTR,
+ MAC2STR(sta->addr), MAC2STR(dev_addr));
+ else
+ wpa_msg(hapd->msg_ctx, MSG_INFO, AP_STA_DISCONNECTED
+ MACSTR, MAC2STR(sta->addr));
+ if (hapd->msg_ctx_parent &&
+ hapd->msg_ctx_parent != hapd->msg_ctx && dev_addr)
+ wpa_msg(hapd->msg_ctx_parent, MSG_INFO,
+ AP_STA_DISCONNECTED MACSTR " p2p_dev_addr="
+ MACSTR, MAC2STR(sta->addr), MAC2STR(dev_addr));
+ else if (hapd->msg_ctx_parent &&
+ hapd->msg_ctx_parent != hapd->msg_ctx)
+ wpa_msg(hapd->msg_ctx_parent, MSG_INFO,
+ AP_STA_DISCONNECTED MACSTR,
+ MAC2STR(sta->addr));
+ sta->flags &= ~WLAN_STA_AUTHORIZED;
+ }
+
+ if (hapd->sta_authorized_cb)
+ hapd->sta_authorized_cb(hapd->sta_authorized_cb_ctx,
+ sta->addr, authorized, dev_addr);
+}
+
+
void ap_sta_disconnect(struct hostapd_data *hapd, struct sta_info *sta,
const u8 *addr, u16 reason)
{
@@ -740,12 +920,52 @@ void ap_sta_disconnect(struct hostapd_data *hapd, struct sta_info *sta,
sta = ap_get_sta(hapd, addr);
if (addr)
- hapd->drv.sta_deauth(hapd, addr, reason);
+ hostapd_drv_sta_deauth(hapd, addr, reason);
if (sta == NULL)
return;
- sta->flags &= ~(WLAN_STA_AUTH | WLAN_STA_ASSOC | WLAN_STA_AUTHORIZED);
+ ap_sta_set_authorized(hapd, sta, 0);
+ wpa_auth_sm_event(sta->wpa_sm, WPA_DEAUTH);
+ ieee802_1x_notify_port_enabled(sta->eapol_sm, 0);
+ sta->flags &= ~(WLAN_STA_AUTH | WLAN_STA_ASSOC);
+ wpa_printf(MSG_DEBUG, "%s: reschedule ap_handle_timer timeout "
+ "for " MACSTR " (%d seconds - "
+ "AP_MAX_INACTIVITY_AFTER_DEAUTH)",
+ __func__, MAC2STR(sta->addr),
+ AP_MAX_INACTIVITY_AFTER_DEAUTH);
eloop_cancel_timeout(ap_handle_timer, hapd, sta);
- eloop_register_timeout(0, 0, ap_handle_timer, hapd, sta);
+ eloop_register_timeout(AP_MAX_INACTIVITY_AFTER_DEAUTH, 0,
+ ap_handle_timer, hapd, sta);
sta->timeout_next = STA_REMOVE;
+
+ sta->deauth_reason = reason;
+ sta->flags |= WLAN_STA_PENDING_DEAUTH_CB;
+ eloop_cancel_timeout(ap_sta_deauth_cb_timeout, hapd, sta);
+ eloop_register_timeout(hapd->iface->drv_flags &
+ WPA_DRIVER_FLAGS_DEAUTH_TX_STATUS ? 2 : 0, 0,
+ ap_sta_deauth_cb_timeout, hapd, sta);
+}
+
+
+void ap_sta_deauth_cb(struct hostapd_data *hapd, struct sta_info *sta)
+{
+ if (!(sta->flags & WLAN_STA_PENDING_DEAUTH_CB)) {
+ wpa_printf(MSG_DEBUG, "Ignore deauth cb for test frame");
+ return;
+ }
+ sta->flags &= ~WLAN_STA_PENDING_DEAUTH_CB;
+ eloop_cancel_timeout(ap_sta_deauth_cb_timeout, hapd, sta);
+ ap_sta_deauth_cb_timeout(hapd, sta);
+}
+
+
+void ap_sta_disassoc_cb(struct hostapd_data *hapd, struct sta_info *sta)
+{
+ if (!(sta->flags & WLAN_STA_PENDING_DISASSOC_CB)) {
+ wpa_printf(MSG_DEBUG, "Ignore disassoc cb for test frame");
+ return;
+ }
+ sta->flags &= ~WLAN_STA_PENDING_DISASSOC_CB;
+ eloop_cancel_timeout(ap_sta_disassoc_cb_timeout, hapd, sta);
+ ap_sta_disassoc_cb_timeout(hapd, sta);
}
diff --git a/contrib/wpa/src/ap/sta_info.h b/contrib/wpa/src/ap/sta_info.h
index 55faa5a..d5e92fa 100644
--- a/contrib/wpa/src/ap/sta_info.h
+++ b/contrib/wpa/src/ap/sta_info.h
@@ -1,15 +1,9 @@
/*
* hostapd / Station table
- * Copyright (c) 2002-2009, Jouni Malinen <j@w1.fi>
+ * Copyright (c) 2002-2011, Jouni Malinen <j@w1.fi>
*
- * This program is free software; you can redistribute it and/or modify
- * it under the terms of the GNU General Public License version 2 as
- * published by the Free Software Foundation.
- *
- * Alternatively, this software may be distributed under the terms of BSD
- * license.
- *
- * See README and COPYING for more details.
+ * This software may be distributed under the terms of the BSD license.
+ * See README for more details.
*/
#ifndef STA_INFO_H
@@ -31,6 +25,12 @@
#define WLAN_STA_WPS BIT(12)
#define WLAN_STA_MAYBE_WPS BIT(13)
#define WLAN_STA_WDS BIT(14)
+#define WLAN_STA_ASSOC_REQ_OK BIT(15)
+#define WLAN_STA_WPS2 BIT(16)
+#define WLAN_STA_GAS BIT(17)
+#define WLAN_STA_VHT BIT(18)
+#define WLAN_STA_PENDING_DISASSOC_CB BIT(29)
+#define WLAN_STA_PENDING_DEAUTH_CB BIT(30)
#define WLAN_STA_NONERP BIT(31)
/* Maximum number of supported rates (from both Supported Rates and Extended
@@ -48,6 +48,7 @@ struct sta_info {
u16 listen_interval; /* or beacon_int for APs */
u8 supported_rates[WLAN_SUPP_RATES_MAX];
int supported_rates_len;
+ u8 qosinfo; /* Valid when WLAN_STA_WMM is set */
unsigned int nonerp_set:1;
unsigned int no_short_slot_time_set:1;
@@ -55,6 +56,7 @@ struct sta_info {
unsigned int no_ht_gf_set:1;
unsigned int no_ht_set:1;
unsigned int ht_20mhz_set:1;
+ unsigned int no_p2p_set:1;
u16 auth_alg;
u8 previous_ap[6];
@@ -63,6 +65,9 @@ struct sta_info {
STA_NULLFUNC = 0, STA_DISASSOC, STA_DEAUTH, STA_REMOVE
} timeout_next;
+ u16 deauth_reason;
+ u16 disassoc_reason;
+
/* IEEE 802.1X related data */
struct eapol_state_machine *eapol_sm;
@@ -90,8 +95,14 @@ struct sta_info {
struct hostapd_ssid *ssid_probe; /* SSID selection based on ProbeReq */
int vlan_id;
+ /* PSKs from RADIUS authentication server */
+ struct hostapd_sta_wpa_psk_short *psk;
+
+ char *identity; /* User-Name from RADIUS */
+ char *radius_cui; /* Chargeable-User-Identity from RADIUS */
struct ieee80211_ht_capabilities *ht_capabilities;
+ struct ieee80211_vht_capabilities *vht_capabilities;
#ifdef CONFIG_IEEE80211W
int sa_query_count; /* number of pending SA Query requests;
@@ -103,7 +114,22 @@ struct sta_info {
struct os_time sa_query_start;
#endif /* CONFIG_IEEE80211W */
+#ifdef CONFIG_INTERWORKING
+#define GAS_DIALOG_MAX 8 /* Max concurrent dialog number */
+ struct gas_dialog_info *gas_dialog;
+ u8 gas_dialog_next;
+#endif /* CONFIG_INTERWORKING */
+
struct wpabuf *wps_ie; /* WPS IE from (Re)Association Request */
+ struct wpabuf *p2p_ie; /* P2P IE from (Re)Association Request */
+ struct wpabuf *hs20_ie; /* HS 2.0 IE from (Re)Association Request */
+
+ struct os_time connected_time;
+
+#ifdef CONFIG_SAE
+ enum { SAE_INIT, SAE_COMMIT, SAE_CONFIRM } sae_state;
+ u16 sae_send_confirm;
+#endif /* CONFIG_SAE */
};
@@ -111,7 +137,7 @@ struct sta_info {
* passed since last received frame from the station, a nullfunc data frame is
* sent to the station. If this frame is not acknowledged and no other frames
* have been received, the station will be disassociated after
- * AP_DISASSOC_DELAY seconds. Similarily, the station will be deauthenticated
+ * AP_DISASSOC_DELAY seconds. Similarly, the station will be deauthenticated
* after AP_DEAUTH_DELAY seconds has passed after disassociation. */
#define AP_MAX_INACTIVITY (5 * 60)
#define AP_DISASSOC_DELAY (1)
@@ -132,7 +158,6 @@ int ap_for_each_sta(struct hostapd_data *hapd,
struct sta_info * ap_get_sta(struct hostapd_data *hapd, const u8 *sta);
void ap_sta_hash_add(struct hostapd_data *hapd, struct sta_info *sta);
void ap_free_sta(struct hostapd_data *hapd, struct sta_info *sta);
-void ap_free_sta(struct hostapd_data *hapd, struct sta_info *sta);
void hostapd_free_stas(struct hostapd_data *hapd);
void ap_handle_timer(void *eloop_ctx, void *timeout_ctx);
void ap_sta_session_timeout(struct hostapd_data *hapd, struct sta_info *sta,
@@ -144,6 +169,10 @@ void ap_sta_disassociate(struct hostapd_data *hapd, struct sta_info *sta,
u16 reason);
void ap_sta_deauthenticate(struct hostapd_data *hapd, struct sta_info *sta,
u16 reason);
+#ifdef CONFIG_WPS
+int ap_sta_wps_cancel(struct hostapd_data *hapd,
+ struct sta_info *sta, void *ctx);
+#endif /* CONFIG_WPS */
int ap_sta_bind_vlan(struct hostapd_data *hapd, struct sta_info *sta,
int old_vlanid);
void ap_sta_start_sa_query(struct hostapd_data *hapd, struct sta_info *sta);
@@ -152,4 +181,14 @@ int ap_check_sa_query_timeout(struct hostapd_data *hapd, struct sta_info *sta);
void ap_sta_disconnect(struct hostapd_data *hapd, struct sta_info *sta,
const u8 *addr, u16 reason);
+void ap_sta_set_authorized(struct hostapd_data *hapd,
+ struct sta_info *sta, int authorized);
+static inline int ap_sta_is_authorized(struct sta_info *sta)
+{
+ return sta->flags & WLAN_STA_AUTHORIZED;
+}
+
+void ap_sta_deauth_cb(struct hostapd_data *hapd, struct sta_info *sta);
+void ap_sta_disassoc_cb(struct hostapd_data *hapd, struct sta_info *sta);
+
#endif /* STA_INFO_H */
diff --git a/contrib/wpa/src/ap/tkip_countermeasures.c b/contrib/wpa/src/ap/tkip_countermeasures.c
index 9690348..4a2ea06 100644
--- a/contrib/wpa/src/ap/tkip_countermeasures.c
+++ b/contrib/wpa/src/ap/tkip_countermeasures.c
@@ -1,15 +1,9 @@
/*
* hostapd / TKIP countermeasures
- * Copyright (c) 2002-2009, Jouni Malinen <j@w1.fi>
+ * Copyright (c) 2002-2012, Jouni Malinen <j@w1.fi>
*
- * This program is free software; you can redistribute it and/or modify
- * it under the terms of the GNU General Public License version 2 as
- * published by the Free Software Foundation.
- *
- * Alternatively, this software may be distributed under the terms of BSD
- * license.
- *
- * See README and COPYING for more details.
+ * This software may be distributed under the terms of the BSD license.
+ * See README for more details.
*/
#include "utils/includes.h"
@@ -17,10 +11,12 @@
#include "utils/common.h"
#include "utils/eloop.h"
#include "common/ieee802_11_defs.h"
+#include "radius/radius.h"
#include "hostapd.h"
#include "sta_info.h"
#include "ap_mlme.h"
#include "wpa_auth.h"
+#include "ap_drv_ops.h"
#include "tkip_countermeasures.h"
@@ -29,7 +25,7 @@ static void ieee80211_tkip_countermeasures_stop(void *eloop_ctx,
{
struct hostapd_data *hapd = eloop_ctx;
hapd->tkip_countermeasures = 0;
- hapd->drv.set_countermeasures(hapd, 0);
+ hostapd_drv_set_countermeasures(hapd, 0);
hostapd_logger(hapd, NULL, HOSTAPD_MODULE_IEEE80211,
HOSTAPD_LEVEL_INFO, "TKIP countermeasures ended");
}
@@ -44,24 +40,36 @@ static void ieee80211_tkip_countermeasures_start(struct hostapd_data *hapd)
wpa_auth_countermeasures_start(hapd->wpa_auth);
hapd->tkip_countermeasures = 1;
- hapd->drv.set_countermeasures(hapd, 1);
+ hostapd_drv_set_countermeasures(hapd, 1);
wpa_gtk_rekey(hapd->wpa_auth);
eloop_cancel_timeout(ieee80211_tkip_countermeasures_stop, hapd, NULL);
eloop_register_timeout(60, 0, ieee80211_tkip_countermeasures_stop,
hapd, NULL);
- for (sta = hapd->sta_list; sta != NULL; sta = sta->next) {
- hapd->drv.sta_deauth(hapd, sta->addr,
- WLAN_REASON_MICHAEL_MIC_FAILURE);
- sta->flags &= ~(WLAN_STA_AUTH | WLAN_STA_ASSOC |
- WLAN_STA_AUTHORIZED);
- hapd->drv.sta_remove(hapd, sta->addr);
+ while ((sta = hapd->sta_list)) {
+ sta->acct_terminate_cause =
+ RADIUS_ACCT_TERMINATE_CAUSE_ADMIN_RESET;
+ if (sta->flags & WLAN_STA_AUTH) {
+ mlme_deauthenticate_indication(
+ hapd, sta,
+ WLAN_REASON_MICHAEL_MIC_FAILURE);
+ }
+ hostapd_drv_sta_deauth(hapd, sta->addr,
+ WLAN_REASON_MICHAEL_MIC_FAILURE);
+ ap_free_sta(hapd, sta);
}
}
-void michael_mic_failure(struct hostapd_data *hapd, const u8 *addr, int local)
+void ieee80211_tkip_countermeasures_deinit(struct hostapd_data *hapd)
+{
+ eloop_cancel_timeout(ieee80211_tkip_countermeasures_stop, hapd, NULL);
+}
+
+
+int michael_mic_failure(struct hostapd_data *hapd, const u8 *addr, int local)
{
- time_t now;
+ struct os_time now;
+ int ret = 0;
if (addr && local) {
struct sta_info *sta = ap_get_sta(hapd, addr);
@@ -77,17 +85,21 @@ void michael_mic_failure(struct hostapd_data *hapd, const u8 *addr, int local)
"MLME-MICHAELMICFAILURE.indication "
"for not associated STA (" MACSTR
") ignored", MAC2STR(addr));
- return;
+ return ret;
}
}
- time(&now);
- if (now > hapd->michael_mic_failure + 60) {
+ os_get_time(&now);
+ if (now.sec > hapd->michael_mic_failure + 60) {
hapd->michael_mic_failures = 1;
} else {
hapd->michael_mic_failures++;
- if (hapd->michael_mic_failures > 1)
+ if (hapd->michael_mic_failures > 1) {
ieee80211_tkip_countermeasures_start(hapd);
+ ret = 1;
+ }
}
- hapd->michael_mic_failure = now;
+ hapd->michael_mic_failure = now.sec;
+
+ return ret;
}
diff --git a/contrib/wpa/src/ap/tkip_countermeasures.h b/contrib/wpa/src/ap/tkip_countermeasures.h
index 5a1afce..d3eaed3 100644
--- a/contrib/wpa/src/ap/tkip_countermeasures.h
+++ b/contrib/wpa/src/ap/tkip_countermeasures.h
@@ -1,20 +1,15 @@
/*
* hostapd / TKIP countermeasures
- * Copyright (c) 2002-2009, Jouni Malinen <j@w1.fi>
+ * Copyright (c) 2002-2012, Jouni Malinen <j@w1.fi>
*
- * This program is free software; you can redistribute it and/or modify
- * it under the terms of the GNU General Public License version 2 as
- * published by the Free Software Foundation.
- *
- * Alternatively, this software may be distributed under the terms of BSD
- * license.
- *
- * See README and COPYING for more details.
+ * This software may be distributed under the terms of the BSD license.
+ * See README for more details.
*/
#ifndef TKIP_COUNTERMEASURES_H
#define TKIP_COUNTERMEASURES_H
-void michael_mic_failure(struct hostapd_data *hapd, const u8 *addr, int local);
+int michael_mic_failure(struct hostapd_data *hapd, const u8 *addr, int local);
+void ieee80211_tkip_countermeasures_deinit(struct hostapd_data *hapd);
#endif /* TKIP_COUNTERMEASURES_H */
diff --git a/contrib/wpa/src/ap/utils.c b/contrib/wpa/src/ap/utils.c
index 0ff48ae..931968c 100644
--- a/contrib/wpa/src/ap/utils.c
+++ b/contrib/wpa/src/ap/utils.c
@@ -2,14 +2,8 @@
* AP mode helper functions
* Copyright (c) 2009, Jouni Malinen <j@w1.fi>
*
- * This program is free software; you can redistribute it and/or modify
- * it under the terms of the GNU General Public License version 2 as
- * published by the Free Software Foundation.
- *
- * Alternatively, this software may be distributed under the terms of BSD
- * license.
- *
- * See README and COPYING for more details.
+ * This software may be distributed under the terms of the BSD license.
+ * See README for more details.
*/
#include "includes.h"
@@ -22,13 +16,15 @@
int hostapd_register_probereq_cb(struct hostapd_data *hapd,
int (*cb)(void *ctx, const u8 *sa,
- const u8 *ie, size_t ie_len),
+ const u8 *da, const u8 *bssid,
+ const u8 *ie, size_t ie_len,
+ int ssi_signal),
void *ctx)
{
struct hostapd_probereq_cb *n;
- n = os_realloc(hapd->probereq_cb, (hapd->num_probereq_cb + 1) *
- sizeof(struct hostapd_probereq_cb));
+ n = os_realloc_array(hapd->probereq_cb, hapd->num_probereq_cb + 1,
+ sizeof(struct hostapd_probereq_cb));
if (n == NULL)
return -1;
@@ -82,7 +78,8 @@ void hostapd_prune_associations(struct hostapd_data *hapd, const u8 *addr)
struct prune_data data;
data.hapd = hapd;
data.addr = addr;
- if (hapd->iface->for_each_interface)
- hapd->iface->for_each_interface(hapd->iface->interfaces,
- prune_associations, &data);
+ if (hapd->iface->interfaces &&
+ hapd->iface->interfaces->for_each_interface)
+ hapd->iface->interfaces->for_each_interface(
+ hapd->iface->interfaces, prune_associations, &data);
}
diff --git a/contrib/wpa/src/ap/vlan_init.c b/contrib/wpa/src/ap/vlan_init.c
index c9d166a..7b1a9e6 100644
--- a/contrib/wpa/src/ap/vlan_init.c
+++ b/contrib/wpa/src/ap/vlan_init.c
@@ -19,7 +19,9 @@
#include "utils/common.h"
#include "hostapd.h"
#include "ap_config.h"
+#include "ap_drv_ops.h"
#include "vlan_init.h"
+#include "vlan_util.h"
#ifdef CONFIG_FULL_DYNAMIC_VLAN
@@ -334,7 +336,9 @@ static int br_getnumports(const char *br_name)
}
-static int vlan_rem(const char *if_name)
+#ifndef CONFIG_VLAN_NETLINK
+
+int vlan_rem(const char *if_name)
{
int fd;
struct vlan_ioctl_args if_request;
@@ -377,7 +381,7 @@ static int vlan_rem(const char *if_name)
returns 1 if the interface already exists
returns 0 otherwise
*/
-static int vlan_add(const char *if_name, int vid)
+int vlan_add(const char *if_name, int vid, const char *vlan_if_name)
{
int fd;
struct vlan_ioctl_args if_request;
@@ -473,6 +477,8 @@ static int vlan_set_name_type(unsigned int name_type)
return 0;
}
+#endif /* CONFIG_VLAN_NETLINK */
+
static void vlan_newlink(char *ifname, struct hostapd_data *hapd)
{
@@ -480,6 +486,7 @@ static void vlan_newlink(char *ifname, struct hostapd_data *hapd)
char br_name[IFNAMSIZ];
struct hostapd_vlan *vlan = hapd->conf->vlan;
char *tagged_interface = hapd->conf->ssid.vlan_tagged_interface;
+ int vlan_naming = hapd->conf->ssid.vlan_naming;
wpa_printf(MSG_DEBUG, "VLAN: vlan_newlink(%s)", ifname);
@@ -495,13 +502,22 @@ static void vlan_newlink(char *ifname, struct hostapd_data *hapd)
ifconfig_up(br_name);
if (tagged_interface) {
-
- if (!vlan_add(tagged_interface, vlan->vlan_id))
+ if (vlan_naming ==
+ DYNAMIC_VLAN_NAMING_WITH_DEVICE)
+ os_snprintf(vlan_ifname,
+ sizeof(vlan_ifname),
+ "%s.%d", tagged_interface,
+ vlan->vlan_id);
+ else
+ os_snprintf(vlan_ifname,
+ sizeof(vlan_ifname),
+ "vlan%d", vlan->vlan_id);
+
+ ifconfig_up(tagged_interface);
+ if (!vlan_add(tagged_interface, vlan->vlan_id,
+ vlan_ifname))
vlan->clean |= DVLAN_CLEAN_VLAN;
- os_snprintf(vlan_ifname, sizeof(vlan_ifname),
- "vlan%d", vlan->vlan_id);
-
if (!br_addif(br_name, vlan_ifname))
vlan->clean |= DVLAN_CLEAN_VLAN_PORT;
@@ -526,6 +542,7 @@ static void vlan_dellink(char *ifname, struct hostapd_data *hapd)
char br_name[IFNAMSIZ];
struct hostapd_vlan *first, *prev, *vlan = hapd->conf->vlan;
char *tagged_interface = hapd->conf->ssid.vlan_tagged_interface;
+ int vlan_naming = hapd->conf->ssid.vlan_naming;
wpa_printf(MSG_DEBUG, "VLAN: vlan_dellink(%s)", ifname);
@@ -540,8 +557,16 @@ static void vlan_dellink(char *ifname, struct hostapd_data *hapd)
br_delif(br_name, vlan->ifname);
if (tagged_interface) {
- os_snprintf(vlan_ifname, sizeof(vlan_ifname),
- "vlan%d", vlan->vlan_id);
+ if (vlan_naming ==
+ DYNAMIC_VLAN_NAMING_WITH_DEVICE)
+ os_snprintf(vlan_ifname,
+ sizeof(vlan_ifname),
+ "%s.%d", tagged_interface,
+ vlan->vlan_id);
+ else
+ os_snprintf(vlan_ifname,
+ sizeof(vlan_ifname),
+ "vlan%d", vlan->vlan_id);
if (vlan->clean & DVLAN_CLEAN_VLAN_PORT)
br_delif(br_name, vlan_ifname);
ifconfig_down(vlan_ifname);
@@ -681,7 +706,12 @@ full_dynamic_vlan_init(struct hostapd_data *hapd)
if (priv == NULL)
return NULL;
- vlan_set_name_type(VLAN_NAME_TYPE_PLUS_VID_NO_PAD);
+#ifndef CONFIG_VLAN_NETLINK
+ vlan_set_name_type(hapd->conf->ssid.vlan_naming ==
+ DYNAMIC_VLAN_NAMING_WITH_DEVICE ?
+ VLAN_NAME_TYPE_RAW_PLUS_VID_NO_PAD :
+ VLAN_NAME_TYPE_PLUS_VID_NO_PAD);
+#endif /* CONFIG_VLAN_NETLINK */
priv->s = socket(PF_NETLINK, SOCK_RAW, NETLINK_ROUTE);
if (priv->s < 0) {
@@ -737,9 +767,10 @@ int vlan_setup_encryption_dyn(struct hostapd_data *hapd,
* functions for setting up dynamic broadcast keys. */
for (i = 0; i < 4; i++) {
if (mssid->wep.key[i] &&
- hapd->drv.set_key(dyn_vlan, hapd, WPA_ALG_WEP, NULL, i,
- i == mssid->wep.idx, NULL, 0,
- mssid->wep.key[i], mssid->wep.len[i])) {
+ hostapd_drv_set_key(dyn_vlan, hapd, WPA_ALG_WEP, NULL, i,
+ i == mssid->wep.idx, NULL, 0,
+ mssid->wep.key[i], mssid->wep.len[i]))
+ {
wpa_printf(MSG_ERROR, "VLAN: Could not set WEP "
"encryption for dynamic VLAN");
return -1;
@@ -755,7 +786,7 @@ static int vlan_dynamic_add(struct hostapd_data *hapd,
{
while (vlan) {
if (vlan->vlan_id != VLAN_ID_WILDCARD) {
- if (hapd->drv.vlan_if_add(hapd, vlan->ifname)) {
+ if (hostapd_vlan_if_add(hapd, vlan->ifname)) {
if (errno != EEXIST) {
wpa_printf(MSG_ERROR, "VLAN: Could "
"not add VLAN %s: %s",
@@ -785,7 +816,7 @@ static void vlan_dynamic_remove(struct hostapd_data *hapd,
next = vlan->next;
if (vlan->vlan_id != VLAN_ID_WILDCARD &&
- hapd->drv.vlan_if_remove(hapd, vlan->ifname)) {
+ hostapd_vlan_if_remove(hapd, vlan->ifname)) {
wpa_printf(MSG_ERROR, "VLAN: Could not remove VLAN "
"iface: %s: %s",
vlan->ifname, strerror(errno));
@@ -859,7 +890,7 @@ struct hostapd_vlan * vlan_add_dynamic(struct hostapd_data *hapd,
pos);
os_free(ifname);
- if (hapd->drv.vlan_if_add(hapd, n->ifname)) {
+ if (hostapd_vlan_if_add(hapd, n->ifname)) {
os_free(n);
return NULL;
}
@@ -897,7 +928,7 @@ int vlan_remove_dynamic(struct hostapd_data *hapd, int vlan_id)
return 1;
if (vlan->dynamic_vlan == 0)
- hapd->drv.vlan_if_remove(hapd, vlan->ifname);
+ hostapd_vlan_if_remove(hapd, vlan->ifname);
return 0;
}
diff --git a/contrib/wpa/src/ap/vlan_util.c b/contrib/wpa/src/ap/vlan_util.c
new file mode 100644
index 0000000..cc54051
--- /dev/null
+++ b/contrib/wpa/src/ap/vlan_util.c
@@ -0,0 +1,177 @@
+/*
+ * hostapd / VLAN netlink api
+ * Copyright (c) 2012, Michael Braun <michael-dev@fami-braun.de>
+ *
+ * This software may be distributed under the terms of the BSD license.
+ * See README for more details.
+ */
+
+#include "utils/includes.h"
+#include <sys/ioctl.h>
+#include <linux/sockios.h>
+#include <linux/if_vlan.h>
+#include <netlink/genl/genl.h>
+#include <netlink/genl/family.h>
+#include <netlink/genl/ctrl.h>
+#include <netlink/route/link.h>
+#include <netlink/route/link/vlan.h>
+
+#include "utils/common.h"
+#include "utils/eloop.h"
+#include "hostapd.h"
+#include "vlan_util.h"
+
+/*
+ * Add a vlan interface with name 'vlan_if_name', VLAN ID 'vid' and
+ * tagged interface 'if_name'.
+ *
+ * returns -1 on error
+ * returns 1 if the interface already exists
+ * returns 0 otherwise
+*/
+int vlan_add(const char *if_name, int vid, const char *vlan_if_name)
+{
+ int ret = -1;
+ struct nl_sock *handle = NULL;
+ struct nl_cache *cache = NULL;
+ struct rtnl_link *rlink = NULL;
+ int if_idx = 0;
+
+ wpa_printf(MSG_DEBUG, "VLAN: vlan_add(if_name=%s, vid=%d, "
+ "vlan_if_name=%s)", if_name, vid, vlan_if_name);
+
+ if ((os_strlen(if_name) + 1) > IFNAMSIZ) {
+ wpa_printf(MSG_ERROR, "VLAN: Interface name too long: '%s'",
+ if_name);
+ return -1;
+ }
+
+ if ((os_strlen(vlan_if_name) + 1) > IFNAMSIZ) {
+ wpa_printf(MSG_ERROR, "VLAN: Interface name too long: '%s'",
+ vlan_if_name);
+ return -1;
+ }
+
+ handle = nl_socket_alloc();
+ if (!handle) {
+ wpa_printf(MSG_ERROR, "VLAN: failed to open netlink socket");
+ goto vlan_add_error;
+ }
+
+ if (nl_connect(handle, NETLINK_ROUTE) < 0) {
+ wpa_printf(MSG_ERROR, "VLAN: failed to connect to netlink");
+ goto vlan_add_error;
+ }
+
+ if (rtnl_link_alloc_cache(handle, AF_UNSPEC, &cache) < 0) {
+ cache = NULL;
+ wpa_printf(MSG_ERROR, "VLAN: failed to alloc cache");
+ goto vlan_add_error;
+ }
+
+ if (!(if_idx = rtnl_link_name2i(cache, if_name))) {
+ /* link does not exist */
+ wpa_printf(MSG_ERROR, "VLAN: interface %s does not exist",
+ if_name);
+ goto vlan_add_error;
+ }
+
+ if ((rlink = rtnl_link_get_by_name(cache, vlan_if_name))) {
+ /* link does exist */
+ rtnl_link_put(rlink);
+ rlink = NULL;
+ wpa_printf(MSG_ERROR, "VLAN: interface %s already exists",
+ vlan_if_name);
+ ret = 1;
+ goto vlan_add_error;
+ }
+
+ rlink = rtnl_link_alloc();
+ if (!rlink) {
+ wpa_printf(MSG_ERROR, "VLAN: failed to allocate new link");
+ goto vlan_add_error;
+ }
+
+ if (rtnl_link_set_type(rlink, "vlan") < 0) {
+ wpa_printf(MSG_ERROR, "VLAN: failed to set link type");
+ goto vlan_add_error;
+ }
+
+ rtnl_link_set_link(rlink, if_idx);
+ rtnl_link_set_name(rlink, vlan_if_name);
+
+ if (rtnl_link_vlan_set_id(rlink, vid) < 0) {
+ wpa_printf(MSG_ERROR, "VLAN: failed to set link vlan id");
+ goto vlan_add_error;
+ }
+
+ if (rtnl_link_add(handle, rlink, NLM_F_CREATE) < 0) {
+ wpa_printf(MSG_ERROR, "VLAN: failed to create link %s for "
+ "vlan %d on %s (%d)",
+ vlan_if_name, vid, if_name, if_idx);
+ goto vlan_add_error;
+ }
+
+ ret = 0;
+
+vlan_add_error:
+ if (rlink)
+ rtnl_link_put(rlink);
+ if (cache)
+ nl_cache_free(cache);
+ if (handle)
+ nl_socket_free(handle);
+ return ret;
+}
+
+
+int vlan_rem(const char *if_name)
+{
+ int ret = -1;
+ struct nl_sock *handle = NULL;
+ struct nl_cache *cache = NULL;
+ struct rtnl_link *rlink = NULL;
+
+ wpa_printf(MSG_DEBUG, "VLAN: vlan_rem(if_name=%s)", if_name);
+
+ handle = nl_socket_alloc();
+ if (!handle) {
+ wpa_printf(MSG_ERROR, "VLAN: failed to open netlink socket");
+ goto vlan_rem_error;
+ }
+
+ if (nl_connect(handle, NETLINK_ROUTE) < 0) {
+ wpa_printf(MSG_ERROR, "VLAN: failed to connect to netlink");
+ goto vlan_rem_error;
+ }
+
+ if (rtnl_link_alloc_cache(handle, AF_UNSPEC, &cache) < 0) {
+ cache = NULL;
+ wpa_printf(MSG_ERROR, "VLAN: failed to alloc cache");
+ goto vlan_rem_error;
+ }
+
+ if (!(rlink = rtnl_link_get_by_name(cache, if_name))) {
+ /* link does not exist */
+ wpa_printf(MSG_ERROR, "VLAN: interface %s does not exists",
+ if_name);
+ goto vlan_rem_error;
+ }
+
+ if (rtnl_link_delete(handle, rlink) < 0) {
+ wpa_printf(MSG_ERROR, "VLAN: failed to remove link %s",
+ if_name);
+ goto vlan_rem_error;
+ }
+
+ ret = 0;
+
+vlan_rem_error:
+ if (rlink)
+ rtnl_link_put(rlink);
+ if (cache)
+ nl_cache_free(cache);
+ if (handle)
+ nl_socket_free(handle);
+ return ret;
+}
diff --git a/contrib/wpa/src/ap/vlan_util.h b/contrib/wpa/src/ap/vlan_util.h
new file mode 100644
index 0000000..bef5a16
--- /dev/null
+++ b/contrib/wpa/src/ap/vlan_util.h
@@ -0,0 +1,15 @@
+/*
+ * hostapd / VLAN netlink api
+ * Copyright (c) 2012, Michael Braun <michael-dev@fami-braun.de>
+ *
+ * This software may be distributed under the terms of the BSD license.
+ * See README for more details.
+ */
+
+#ifndef VLAN_UTIL_H
+#define VLAN_UTIL_H
+
+int vlan_add(const char *if_name, int vid, const char *vlan_if_name);
+int vlan_rem(const char *if_name);
+
+#endif /* VLAN_UTIL_H */
diff --git a/contrib/wpa/src/ap/wmm.c b/contrib/wpa/src/ap/wmm.c
index 3668130..d21c82f 100644
--- a/contrib/wpa/src/ap/wmm.c
+++ b/contrib/wpa/src/ap/wmm.c
@@ -23,6 +23,7 @@
#include "ieee802_11.h"
#include "sta_info.h"
#include "ap_config.h"
+#include "ap_drv_ops.h"
#include "wmm.h"
@@ -71,9 +72,12 @@ u8 * hostapd_eid_wmm(struct hostapd_data *hapd, u8 *eid)
wmm->version = WMM_VERSION;
wmm->qos_info = hapd->parameter_set_count & 0xf;
- if (hapd->conf->wmm_uapsd)
+ if (hapd->conf->wmm_uapsd &&
+ (hapd->iface->drv_flags & WPA_DRIVER_FLAGS_AP_UAPSD))
wmm->qos_info |= 0x80;
+ wmm->reserved = 0;
+
/* fill in a parameter set record for each AC */
for (e = 0; e < 4; e++) {
struct wmm_ac_parameter *ac = &wmm->ac[e];
@@ -94,9 +98,11 @@ u8 * hostapd_eid_wmm(struct hostapd_data *hapd, u8 *eid)
}
-/* This function is called when a station sends an association request with
- * WMM info element. The function returns zero on success or non-zero on any
- * error in WMM element. eid does not include Element ID and Length octets. */
+/*
+ * This function is called when a station sends an association request with
+ * WMM info element. The function returns 1 on success or 0 on any error in WMM
+ * element. eid does not include Element ID and Length octets.
+ */
int hostapd_eid_wmm_valid(struct hostapd_data *hapd, const u8 *eid, size_t len)
{
struct wmm_information_element *wmm;
@@ -106,7 +112,7 @@ int hostapd_eid_wmm_valid(struct hostapd_data *hapd, const u8 *eid, size_t len)
if (len < sizeof(struct wmm_information_element)) {
wpa_printf(MSG_DEBUG, "Too short WMM IE (len=%lu)",
(unsigned long) len);
- return -1;
+ return 0;
}
wmm = (struct wmm_information_element *) eid;
@@ -117,10 +123,10 @@ int hostapd_eid_wmm_valid(struct hostapd_data *hapd, const u8 *eid, size_t len)
if (wmm->oui_subtype != WMM_OUI_SUBTYPE_INFORMATION_ELEMENT ||
wmm->version != WMM_VERSION) {
wpa_printf(MSG_DEBUG, "Unsupported WMM IE Subtype/Version");
- return -1;
+ return 0;
}
- return 0;
+ return 1;
}
@@ -150,7 +156,7 @@ static void wmm_send_action(struct hostapd_data *hapd, const u8 *addr,
os_memcpy(t, tspec, sizeof(struct wmm_tspec_element));
len = ((u8 *) (t + 1)) - buf;
- if (hapd->drv.send_mgmt_frame(hapd, m, len) < 0)
+ if (hostapd_drv_send_mlme(hapd, m, len, 0) < 0)
perror("wmm_send_action: send");
}
diff --git a/contrib/wpa/src/ap/wnm_ap.c b/contrib/wpa/src/ap/wnm_ap.c
new file mode 100644
index 0000000..54a6b85
--- /dev/null
+++ b/contrib/wpa/src/ap/wnm_ap.c
@@ -0,0 +1,271 @@
+/*
+ * hostapd - WNM
+ * Copyright (c) 2011-2012, Qualcomm Atheros, Inc.
+ *
+ * This software may be distributed under the terms of the BSD license.
+ * See README for more details.
+ */
+
+#include "utils/includes.h"
+
+#include "utils/common.h"
+#include "common/ieee802_11_defs.h"
+#include "ap/hostapd.h"
+#include "ap/sta_info.h"
+#include "ap/ap_config.h"
+#include "ap/ap_drv_ops.h"
+#include "ap/wpa_auth.h"
+#include "wnm_ap.h"
+
+#define MAX_TFS_IE_LEN 1024
+
+
+/* get the TFS IE from driver */
+static int ieee80211_11_get_tfs_ie(struct hostapd_data *hapd, const u8 *addr,
+ u8 *buf, u16 *buf_len, enum wnm_oper oper)
+{
+ wpa_printf(MSG_DEBUG, "%s: TFS get operation %d", __func__, oper);
+
+ return hostapd_drv_wnm_oper(hapd, oper, addr, buf, buf_len);
+}
+
+
+/* set the TFS IE to driver */
+static int ieee80211_11_set_tfs_ie(struct hostapd_data *hapd, const u8 *addr,
+ u8 *buf, u16 *buf_len, enum wnm_oper oper)
+{
+ wpa_printf(MSG_DEBUG, "%s: TFS set operation %d", __func__, oper);
+
+ return hostapd_drv_wnm_oper(hapd, oper, addr, buf, buf_len);
+}
+
+
+/* MLME-SLEEPMODE.response */
+static int ieee802_11_send_wnmsleep_resp(struct hostapd_data *hapd,
+ const u8 *addr, u8 dialog_token,
+ u8 action_type, u16 intval)
+{
+ struct ieee80211_mgmt *mgmt;
+ int res;
+ size_t len;
+ size_t gtk_elem_len = 0;
+ size_t igtk_elem_len = 0;
+ struct wnm_sleep_element wnmsleep_ie;
+ u8 *wnmtfs_ie;
+ u8 wnmsleep_ie_len;
+ u16 wnmtfs_ie_len;
+ u8 *pos;
+ struct sta_info *sta;
+ enum wnm_oper tfs_oper = action_type == WNM_SLEEP_MODE_ENTER ?
+ WNM_SLEEP_TFS_RESP_IE_ADD : WNM_SLEEP_TFS_RESP_IE_NONE;
+
+ sta = ap_get_sta(hapd, addr);
+ if (sta == NULL) {
+ wpa_printf(MSG_DEBUG, "%s: station not found", __func__);
+ return -EINVAL;
+ }
+
+ /* WNM-Sleep Mode IE */
+ os_memset(&wnmsleep_ie, 0, sizeof(struct wnm_sleep_element));
+ wnmsleep_ie_len = sizeof(struct wnm_sleep_element);
+ wnmsleep_ie.eid = WLAN_EID_WNMSLEEP;
+ wnmsleep_ie.len = wnmsleep_ie_len - 2;
+ wnmsleep_ie.action_type = action_type;
+ wnmsleep_ie.status = WNM_STATUS_SLEEP_ACCEPT;
+ wnmsleep_ie.intval = intval;
+
+ /* TFS IE(s) */
+ wnmtfs_ie = os_zalloc(MAX_TFS_IE_LEN);
+ if (wnmtfs_ie == NULL)
+ return -1;
+ if (ieee80211_11_get_tfs_ie(hapd, addr, wnmtfs_ie, &wnmtfs_ie_len,
+ tfs_oper)) {
+ wnmtfs_ie_len = 0;
+ os_free(wnmtfs_ie);
+ wnmtfs_ie = NULL;
+ }
+
+#define MAX_GTK_SUBELEM_LEN 45
+#define MAX_IGTK_SUBELEM_LEN 26
+ mgmt = os_zalloc(sizeof(*mgmt) + wnmsleep_ie_len +
+ MAX_GTK_SUBELEM_LEN + MAX_IGTK_SUBELEM_LEN);
+ if (mgmt == NULL) {
+ wpa_printf(MSG_DEBUG, "MLME: Failed to allocate buffer for "
+ "WNM-Sleep Response action frame");
+ return -1;
+ }
+ os_memcpy(mgmt->da, addr, ETH_ALEN);
+ os_memcpy(mgmt->sa, hapd->own_addr, ETH_ALEN);
+ os_memcpy(mgmt->bssid, hapd->own_addr, ETH_ALEN);
+ mgmt->frame_control = IEEE80211_FC(WLAN_FC_TYPE_MGMT,
+ WLAN_FC_STYPE_ACTION);
+ mgmt->u.action.category = WLAN_ACTION_WNM;
+ mgmt->u.action.u.wnm_sleep_resp.action = WNM_SLEEP_MODE_RESP;
+ mgmt->u.action.u.wnm_sleep_resp.dialogtoken = dialog_token;
+ pos = (u8 *)mgmt->u.action.u.wnm_sleep_resp.variable;
+ /* add key data if MFP is enabled */
+ if (!wpa_auth_uses_mfp(sta->wpa_sm) ||
+ action_type != WNM_SLEEP_MODE_EXIT) {
+ mgmt->u.action.u.wnm_sleep_resp.keydata_len = 0;
+ } else {
+ gtk_elem_len = wpa_wnmsleep_gtk_subelem(sta->wpa_sm, pos);
+ pos += gtk_elem_len;
+ wpa_printf(MSG_DEBUG, "Pass 4, gtk_len = %d",
+ (int) gtk_elem_len);
+#ifdef CONFIG_IEEE80211W
+ res = wpa_wnmsleep_igtk_subelem(sta->wpa_sm, pos);
+ if (res < 0) {
+ os_free(wnmtfs_ie);
+ os_free(mgmt);
+ return -1;
+ }
+ igtk_elem_len = res;
+ pos += igtk_elem_len;
+ wpa_printf(MSG_DEBUG, "Pass 4 igtk_len = %d",
+ (int) igtk_elem_len);
+#endif /* CONFIG_IEEE80211W */
+
+ WPA_PUT_LE16((u8 *)
+ &mgmt->u.action.u.wnm_sleep_resp.keydata_len,
+ gtk_elem_len + igtk_elem_len);
+ }
+ os_memcpy(pos, &wnmsleep_ie, wnmsleep_ie_len);
+ /* copy TFS IE here */
+ pos += wnmsleep_ie_len;
+ if (wnmtfs_ie)
+ os_memcpy(pos, wnmtfs_ie, wnmtfs_ie_len);
+
+ len = 1 + sizeof(mgmt->u.action.u.wnm_sleep_resp) + gtk_elem_len +
+ igtk_elem_len + wnmsleep_ie_len + wnmtfs_ie_len;
+
+ /* In driver, response frame should be forced to sent when STA is in
+ * PS mode */
+ res = hostapd_drv_send_action(hapd, hapd->iface->freq, 0,
+ mgmt->da, &mgmt->u.action.category, len);
+
+ if (!res) {
+ wpa_printf(MSG_DEBUG, "Successfully send WNM-Sleep Response "
+ "frame");
+
+ /* when entering wnmsleep
+ * 1. pause the node in driver
+ * 2. mark the node so that AP won't update GTK/IGTK during
+ * WNM Sleep
+ */
+ if (wnmsleep_ie.status == WNM_STATUS_SLEEP_ACCEPT &&
+ wnmsleep_ie.action_type == WNM_SLEEP_MODE_ENTER) {
+ hostapd_drv_wnm_oper(hapd, WNM_SLEEP_ENTER_CONFIRM,
+ addr, NULL, NULL);
+ wpa_set_wnmsleep(sta->wpa_sm, 1);
+ }
+ /* when exiting wnmsleep
+ * 1. unmark the node
+ * 2. start GTK/IGTK update if MFP is not used
+ * 3. unpause the node in driver
+ */
+ if ((wnmsleep_ie.status == WNM_STATUS_SLEEP_ACCEPT ||
+ wnmsleep_ie.status ==
+ WNM_STATUS_SLEEP_EXIT_ACCEPT_GTK_UPDATE) &&
+ wnmsleep_ie.action_type == WNM_SLEEP_MODE_EXIT) {
+ wpa_set_wnmsleep(sta->wpa_sm, 0);
+ hostapd_drv_wnm_oper(hapd, WNM_SLEEP_EXIT_CONFIRM,
+ addr, NULL, NULL);
+ if (!wpa_auth_uses_mfp(sta->wpa_sm))
+ wpa_wnmsleep_rekey_gtk(sta->wpa_sm);
+ }
+ } else
+ wpa_printf(MSG_DEBUG, "Fail to send WNM-Sleep Response frame");
+
+#undef MAX_GTK_SUBELEM_LEN
+#undef MAX_IGTK_SUBELEM_LEN
+ os_free(wnmtfs_ie);
+ os_free(mgmt);
+ return res;
+}
+
+
+static void ieee802_11_rx_wnmsleep_req(struct hostapd_data *hapd,
+ const u8 *addr, const u8 *frm, int len)
+{
+ /* Dialog Token [1] | WNM-Sleep Mode IE | TFS Response IE */
+ const u8 *pos = frm;
+ u8 dialog_token;
+ struct wnm_sleep_element *wnmsleep_ie = NULL;
+ /* multiple TFS Req IE (assuming consecutive) */
+ u8 *tfsreq_ie_start = NULL;
+ u8 *tfsreq_ie_end = NULL;
+ u16 tfsreq_ie_len = 0;
+
+ dialog_token = *pos++;
+ while (pos + 1 < frm + len) {
+ u8 ie_len = pos[1];
+ if (pos + 2 + ie_len > frm + len)
+ break;
+ if (*pos == WLAN_EID_WNMSLEEP)
+ wnmsleep_ie = (struct wnm_sleep_element *) pos;
+ else if (*pos == WLAN_EID_TFS_REQ) {
+ if (!tfsreq_ie_start)
+ tfsreq_ie_start = (u8 *) pos;
+ tfsreq_ie_end = (u8 *) pos;
+ } else
+ wpa_printf(MSG_DEBUG, "WNM: EID %d not recognized",
+ *pos);
+ pos += ie_len + 2;
+ }
+
+ if (!wnmsleep_ie) {
+ wpa_printf(MSG_DEBUG, "No WNM-Sleep IE found");
+ return;
+ }
+
+ if (wnmsleep_ie->action_type == WNM_SLEEP_MODE_ENTER &&
+ tfsreq_ie_start && tfsreq_ie_end &&
+ tfsreq_ie_end - tfsreq_ie_start >= 0) {
+ tfsreq_ie_len = (tfsreq_ie_end + tfsreq_ie_end[1] + 2) -
+ tfsreq_ie_start;
+ wpa_printf(MSG_DEBUG, "TFS Req IE(s) found");
+ /* pass the TFS Req IE(s) to driver for processing */
+ if (ieee80211_11_set_tfs_ie(hapd, addr, tfsreq_ie_start,
+ &tfsreq_ie_len,
+ WNM_SLEEP_TFS_REQ_IE_SET))
+ wpa_printf(MSG_DEBUG, "Fail to set TFS Req IE");
+ }
+
+ ieee802_11_send_wnmsleep_resp(hapd, addr, dialog_token,
+ wnmsleep_ie->action_type,
+ wnmsleep_ie->intval);
+
+ if (wnmsleep_ie->action_type == WNM_SLEEP_MODE_EXIT) {
+ /* clear the tfs after sending the resp frame */
+ ieee80211_11_set_tfs_ie(hapd, addr, tfsreq_ie_start,
+ &tfsreq_ie_len, WNM_SLEEP_TFS_IE_DEL);
+ }
+}
+
+
+int ieee802_11_rx_wnm_action_ap(struct hostapd_data *hapd,
+ struct rx_action *action)
+{
+ if (action->len < 1 || action->data == NULL)
+ return -1;
+
+ switch (action->data[0]) {
+ case WNM_BSS_TRANS_MGMT_QUERY:
+ wpa_printf(MSG_DEBUG, "WNM: BSS Transition Management Query");
+ /* TODO */
+ return -1;
+ case WNM_BSS_TRANS_MGMT_RESP:
+ wpa_printf(MSG_DEBUG, "WNM: BSS Transition Management "
+ "Response");
+ /* TODO */
+ return -1;
+ case WNM_SLEEP_MODE_REQ:
+ ieee802_11_rx_wnmsleep_req(hapd, action->sa, action->data + 1,
+ action->len - 1);
+ return 0;
+ }
+
+ wpa_printf(MSG_DEBUG, "WNM: Unsupported WNM Action %u from " MACSTR,
+ action->data[0], MAC2STR(action->sa));
+ return -1;
+}
diff --git a/contrib/wpa/src/ap/wnm_ap.h b/contrib/wpa/src/ap/wnm_ap.h
new file mode 100644
index 0000000..f05726e
--- /dev/null
+++ b/contrib/wpa/src/ap/wnm_ap.h
@@ -0,0 +1,17 @@
+/*
+ * IEEE 802.11v WNM related functions and structures
+ * Copyright (c) 2011-2012, Qualcomm Atheros, Inc.
+ *
+ * This software may be distributed under the terms of the BSD license.
+ * See README for more details.
+ */
+
+#ifndef WNM_AP_H
+#define WNM_AP_H
+
+struct rx_action;
+
+int ieee802_11_rx_wnm_action_ap(struct hostapd_data *hapd,
+ struct rx_action *action);
+
+#endif /* WNM_AP_H */
diff --git a/contrib/wpa/src/ap/wpa_auth.c b/contrib/wpa/src/ap/wpa_auth.c
index 36cb0f4..2c70149 100644
--- a/contrib/wpa/src/ap/wpa_auth.c
+++ b/contrib/wpa/src/ap/wpa_auth.c
@@ -1,15 +1,9 @@
/*
- * hostapd - IEEE 802.11i-2004 / WPA Authenticator
- * Copyright (c) 2004-2009, Jouni Malinen <j@w1.fi>
+ * IEEE 802.11 RSN / WPA Authenticator
+ * Copyright (c) 2004-2011, Jouni Malinen <j@w1.fi>
*
- * This program is free software; you can redistribute it and/or modify
- * it under the terms of the GNU General Public License version 2 as
- * published by the Free Software Foundation.
- *
- * Alternatively, this software may be distributed under the terms of BSD
- * license.
- *
- * See README and COPYING for more details.
+ * This software may be distributed under the terms of the BSD license.
+ * See README for more details.
*/
#include "utils/includes.h"
@@ -22,6 +16,7 @@
#include "crypto/crypto.h"
#include "crypto/sha1.h"
#include "crypto/sha256.h"
+#include "crypto/random.h"
#include "eapol_auth/eapol_auth_sm.h"
#include "ap_config.h"
#include "ieee802_11.h"
@@ -44,11 +39,14 @@ static void wpa_group_sm_step(struct wpa_authenticator *wpa_auth,
static void wpa_request_new_ptk(struct wpa_state_machine *sm);
static int wpa_gtk_update(struct wpa_authenticator *wpa_auth,
struct wpa_group *group);
+static int wpa_group_config_group_keys(struct wpa_authenticator *wpa_auth,
+ struct wpa_group *group);
static const u32 dot11RSNAConfigGroupUpdateCount = 4;
static const u32 dot11RSNAConfigPairwiseUpdateCount = 4;
static const u32 eapol_key_timeout_first = 100; /* ms */
static const u32 eapol_key_timeout_subseq = 1000; /* ms */
+static const u32 eapol_key_timeout_first_group = 500; /* ms */
/* TODO: make these configurable */
static const int dot11RSNAConfigPMKLifetime = 43200;
@@ -56,11 +54,12 @@ static const int dot11RSNAConfigPMKReauthThreshold = 70;
static const int dot11RSNAConfigSATimeout = 60;
-static inline void wpa_auth_mic_failure_report(
+static inline int wpa_auth_mic_failure_report(
struct wpa_authenticator *wpa_auth, const u8 *addr)
{
if (wpa_auth->cb.mic_failure_report)
- wpa_auth->cb.mic_failure_report(wpa_auth->cb.ctx, addr);
+ return wpa_auth->cb.mic_failure_report(wpa_auth->cb.ctx, addr);
+ return 0;
}
@@ -191,6 +190,7 @@ static void wpa_sta_disconnect(struct wpa_authenticator *wpa_auth,
{
if (wpa_auth->cb.disconnect == NULL)
return;
+ wpa_printf(MSG_DEBUG, "wpa_sta_disconnect STA " MACSTR, MAC2STR(addr));
wpa_auth->cb.disconnect(wpa_auth->cb.ctx, addr,
WLAN_REASON_PREV_AUTH_NOT_VALID);
}
@@ -215,11 +215,13 @@ static void wpa_rekey_gmk(void *eloop_ctx, void *timeout_ctx)
{
struct wpa_authenticator *wpa_auth = eloop_ctx;
- if (os_get_random(wpa_auth->group->GMK, WPA_GMK_LEN)) {
+ if (random_get_bytes(wpa_auth->group->GMK, WPA_GMK_LEN)) {
wpa_printf(MSG_ERROR, "Failed to get random data for WPA "
"initialization.");
} else {
wpa_auth_logger(wpa_auth, NULL, LOGGER_DEBUG, "GMK rekeyd");
+ wpa_hexdump_key(MSG_DEBUG, "GMK",
+ wpa_auth->group->GMK, WPA_GMK_LEN);
}
if (wpa_auth->conf.wpa_gmk_rekey) {
@@ -277,31 +279,40 @@ static void wpa_auth_pmksa_free_cb(struct rsn_pmksa_cache_entry *entry,
}
-static void wpa_group_set_key_len(struct wpa_group *group, int cipher)
+static int wpa_group_init_gmk_and_counter(struct wpa_authenticator *wpa_auth,
+ struct wpa_group *group)
{
- switch (cipher) {
- case WPA_CIPHER_CCMP:
- group->GTK_len = 16;
- break;
- case WPA_CIPHER_TKIP:
- group->GTK_len = 32;
- break;
- case WPA_CIPHER_WEP104:
- group->GTK_len = 13;
- break;
- case WPA_CIPHER_WEP40:
- group->GTK_len = 5;
- break;
- }
+ u8 buf[ETH_ALEN + 8 + sizeof(group)];
+ u8 rkey[32];
+
+ if (random_get_bytes(group->GMK, WPA_GMK_LEN) < 0)
+ return -1;
+ wpa_hexdump_key(MSG_DEBUG, "GMK", group->GMK, WPA_GMK_LEN);
+
+ /*
+ * Counter = PRF-256(Random number, "Init Counter",
+ * Local MAC Address || Time)
+ */
+ os_memcpy(buf, wpa_auth->addr, ETH_ALEN);
+ wpa_get_ntp_timestamp(buf + ETH_ALEN);
+ os_memcpy(buf + ETH_ALEN + 8, &group, sizeof(group));
+ if (random_get_bytes(rkey, sizeof(rkey)) < 0)
+ return -1;
+
+ if (sha1_prf(rkey, sizeof(rkey), "Init Counter", buf, sizeof(buf),
+ group->Counter, WPA_NONCE_LEN) < 0)
+ return -1;
+ wpa_hexdump_key(MSG_DEBUG, "Key Counter",
+ group->Counter, WPA_NONCE_LEN);
+
+ return 0;
}
static struct wpa_group * wpa_group_init(struct wpa_authenticator *wpa_auth,
- int vlan_id)
+ int vlan_id, int delay_init)
{
struct wpa_group *group;
- u8 buf[ETH_ALEN + 8 + sizeof(group)];
- u8 rkey[32];
group = os_zalloc(sizeof(struct wpa_group));
if (group == NULL)
@@ -309,30 +320,37 @@ static struct wpa_group * wpa_group_init(struct wpa_authenticator *wpa_auth,
group->GTKAuthenticator = TRUE;
group->vlan_id = vlan_id;
+ group->GTK_len = wpa_cipher_key_len(wpa_auth->conf.wpa_group);
- wpa_group_set_key_len(group, wpa_auth->conf.wpa_group);
+ if (random_pool_ready() != 1) {
+ wpa_printf(MSG_INFO, "WPA: Not enough entropy in random pool "
+ "for secure operations - update keys later when "
+ "the first station connects");
+ }
- /* Counter = PRF-256(Random number, "Init Counter",
- * Local MAC Address || Time)
+ /*
+ * Set initial GMK/Counter value here. The actual values that will be
+ * used in negotiations will be set once the first station tries to
+ * connect. This allows more time for collecting additional randomness
+ * on embedded devices.
*/
- os_memcpy(buf, wpa_auth->addr, ETH_ALEN);
- wpa_get_ntp_timestamp(buf + ETH_ALEN);
- os_memcpy(buf + ETH_ALEN + 8, &group, sizeof(group));
- if (os_get_random(rkey, sizeof(rkey)) ||
- os_get_random(group->GMK, WPA_GMK_LEN)) {
+ if (wpa_group_init_gmk_and_counter(wpa_auth, group) < 0) {
wpa_printf(MSG_ERROR, "Failed to get random data for WPA "
"initialization.");
os_free(group);
return NULL;
}
- sha1_prf(rkey, sizeof(rkey), "Init Counter", buf, sizeof(buf),
- group->Counter, WPA_NONCE_LEN);
-
group->GInit = TRUE;
- wpa_group_sm_step(wpa_auth, group);
- group->GInit = FALSE;
- wpa_group_sm_step(wpa_auth, group);
+ if (delay_init) {
+ wpa_printf(MSG_DEBUG, "WPA: Delay group state machine start "
+ "until Beacon frames have been configured");
+ /* Initialization is completed in wpa_init_keys(). */
+ } else {
+ wpa_group_sm_step(wpa_auth, group);
+ group->GInit = FALSE;
+ wpa_group_sm_step(wpa_auth, group);
+ }
return group;
}
@@ -364,7 +382,7 @@ struct wpa_authenticator * wpa_init(const u8 *addr,
return NULL;
}
- wpa_auth->group = wpa_group_init(wpa_auth, 0);
+ wpa_auth->group = wpa_group_init(wpa_auth, 0, 1);
if (wpa_auth->group == NULL) {
os_free(wpa_auth->wpa_ie);
os_free(wpa_auth);
@@ -405,6 +423,19 @@ struct wpa_authenticator * wpa_init(const u8 *addr,
}
+int wpa_init_keys(struct wpa_authenticator *wpa_auth)
+{
+ struct wpa_group *group = wpa_auth->group;
+
+ wpa_printf(MSG_DEBUG, "WPA: Start group state machine to set initial "
+ "keys");
+ wpa_group_sm_step(wpa_auth, group);
+ group->GInit = FALSE;
+ wpa_group_sm_step(wpa_auth, group);
+ return 0;
+}
+
+
/**
* wpa_deinit - Deinitialize WPA authenticator
* @wpa_auth: Pointer to WPA authenticator data from wpa_init()
@@ -464,7 +495,7 @@ int wpa_reconfig(struct wpa_authenticator *wpa_auth,
* configuration.
*/
group = wpa_auth->group;
- wpa_group_set_key_len(group, wpa_auth->conf.wpa_group);
+ group->GTK_len = wpa_cipher_key_len(wpa_auth->conf.wpa_group);
group->GInit = TRUE;
wpa_group_sm_step(wpa_auth, group);
group->GInit = FALSE;
@@ -539,6 +570,10 @@ void wpa_auth_sta_no_wpa(struct wpa_state_machine *sm)
static void wpa_free_sta_sm(struct wpa_state_machine *sm)
{
+ if (sm->GUpdateStationKeys) {
+ sm->group->GKeyDoneStations--;
+ sm->GUpdateStationKeys = FALSE;
+ }
#ifdef CONFIG_IEEE80211R
os_free(sm->assoc_resp_ftie);
#endif /* CONFIG_IEEE80211R */
@@ -563,6 +598,7 @@ void wpa_auth_sta_deinit(struct wpa_state_machine *sm)
}
eloop_cancel_timeout(wpa_send_eapol_timeout, sm->wpa_auth, sm);
+ sm->pending_1_of_4_timeout = 0;
eloop_cancel_timeout(wpa_sm_call_step, sm, NULL);
eloop_cancel_timeout(wpa_rekey_ptk, sm->wpa_auth, sm);
if (sm->in_step_loop) {
@@ -586,14 +622,14 @@ static void wpa_request_new_ptk(struct wpa_state_machine *sm)
}
-static int wpa_replay_counter_valid(struct wpa_state_machine *sm,
+static int wpa_replay_counter_valid(struct wpa_key_replay_counter *ctr,
const u8 *replay_counter)
{
int i;
for (i = 0; i < RSNA_MAX_EAPOL_RETRIES; i++) {
- if (!sm->key_replay[i].valid)
+ if (!ctr[i].valid)
break;
- if (os_memcmp(replay_counter, sm->key_replay[i].counter,
+ if (os_memcmp(replay_counter, ctr[i].counter,
WPA_REPLAY_COUNTER_LEN) == 0)
return 1;
}
@@ -601,6 +637,20 @@ static int wpa_replay_counter_valid(struct wpa_state_machine *sm,
}
+static void wpa_replay_counter_mark_invalid(struct wpa_key_replay_counter *ctr,
+ const u8 *replay_counter)
+{
+ int i;
+ for (i = 0; i < RSNA_MAX_EAPOL_RETRIES; i++) {
+ if (ctr[i].valid &&
+ (replay_counter == NULL ||
+ os_memcmp(replay_counter, ctr[i].counter,
+ WPA_REPLAY_COUNTER_LEN) == 0))
+ ctr[i].valid = FALSE;
+ }
+}
+
+
#ifdef CONFIG_IEEE80211R
static int ft_check_msg_2_of_4(struct wpa_authenticator *wpa_auth,
struct wpa_state_machine *sm,
@@ -651,6 +701,39 @@ static int ft_check_msg_2_of_4(struct wpa_authenticator *wpa_auth,
#endif /* CONFIG_IEEE80211R */
+static int wpa_receive_error_report(struct wpa_authenticator *wpa_auth,
+ struct wpa_state_machine *sm, int group)
+{
+ /* Supplicant reported a Michael MIC error */
+ wpa_auth_vlogger(wpa_auth, sm->addr, LOGGER_INFO,
+ "received EAPOL-Key Error Request "
+ "(STA detected Michael MIC failure (group=%d))",
+ group);
+
+ if (group && wpa_auth->conf.wpa_group != WPA_CIPHER_TKIP) {
+ wpa_auth_logger(wpa_auth, sm->addr, LOGGER_INFO,
+ "ignore Michael MIC failure report since "
+ "group cipher is not TKIP");
+ } else if (!group && sm->pairwise != WPA_CIPHER_TKIP) {
+ wpa_auth_logger(wpa_auth, sm->addr, LOGGER_INFO,
+ "ignore Michael MIC failure report since "
+ "pairwise cipher is not TKIP");
+ } else {
+ if (wpa_auth_mic_failure_report(wpa_auth, sm->addr) > 0)
+ return 1; /* STA entry was removed */
+ sm->dot11RSNAStatsTKIPRemoteMICFailures++;
+ wpa_auth->dot11RSNAStatsTKIPRemoteMICFailures++;
+ }
+
+ /*
+ * Error report is not a request for a new key handshake, but since
+ * Authenticator may do it, let's change the keys now anyway.
+ */
+ wpa_request_new_ptk(sm);
+ return 0;
+}
+
+
void wpa_receive(struct wpa_authenticator *wpa_auth,
struct wpa_state_machine *sm,
u8 *data, size_t data_len)
@@ -676,6 +759,9 @@ void wpa_receive(struct wpa_authenticator *wpa_auth,
key = (struct wpa_eapol_key *) (hdr + 1);
key_info = WPA_GET_BE16(key->key_info);
key_data_length = WPA_GET_BE16(key->key_data_length);
+ wpa_printf(MSG_DEBUG, "WPA: Received EAPOL-Key from " MACSTR
+ " key_info=0x%x type=%u key_data_length=%u",
+ MAC2STR(sm->addr), key_info, key->type, key_data_length);
if (key_data_length > data_len - sizeof(*hdr) - sizeof(*key)) {
wpa_printf(MSG_INFO, "WPA: Invalid EAPOL-Key frame - "
"key_data overflow (%d > %lu)",
@@ -686,7 +772,14 @@ void wpa_receive(struct wpa_authenticator *wpa_auth,
}
if (sm->wpa == WPA_VERSION_WPA2) {
- if (key->type != EAPOL_KEY_TYPE_RSN) {
+ if (key->type == EAPOL_KEY_TYPE_WPA) {
+ /*
+ * Some deployed station implementations seem to send
+ * msg 4/4 with incorrect type value in WPA2 mode.
+ */
+ wpa_printf(MSG_DEBUG, "Workaround: Allow EAPOL-Key "
+ "with unexpected WPA type in RSN mode");
+ } else if (key->type != EAPOL_KEY_TYPE_RSN) {
wpa_printf(MSG_DEBUG, "Ignore EAPOL-Key with "
"unexpected type %d in RSN mode",
key->type);
@@ -701,6 +794,11 @@ void wpa_receive(struct wpa_authenticator *wpa_auth,
}
}
+ wpa_hexdump(MSG_DEBUG, "WPA: Received Key Nonce", key->key_nonce,
+ WPA_NONCE_LEN);
+ wpa_hexdump(MSG_DEBUG, "WPA: Received Replay Counter",
+ key->replay_counter, WPA_REPLAY_COUNTER_LEN);
+
/* FIX: verify that the EAPOL-Key frame was encrypted if pairwise keys
* are set */
@@ -734,7 +832,8 @@ void wpa_receive(struct wpa_authenticator *wpa_auth,
if (msg == REQUEST || msg == PAIRWISE_2 || msg == PAIRWISE_4 ||
msg == GROUP_2) {
u16 ver = key_info & WPA_KEY_INFO_TYPE_MASK;
- if (sm->pairwise == WPA_CIPHER_CCMP) {
+ if (sm->pairwise == WPA_CIPHER_CCMP ||
+ sm->pairwise == WPA_CIPHER_GCMP) {
if (wpa_use_aes_cmac(sm) &&
ver != WPA_KEY_INFO_TYPE_AES_128_CMAC) {
wpa_auth_logger(wpa_auth, sm->addr,
@@ -750,7 +849,7 @@ void wpa_receive(struct wpa_authenticator *wpa_auth,
wpa_auth_logger(wpa_auth, sm->addr,
LOGGER_WARNING,
"did not use HMAC-SHA1-AES "
- "with CCMP");
+ "with CCMP/GCMP");
return;
}
}
@@ -768,11 +867,44 @@ void wpa_receive(struct wpa_authenticator *wpa_auth,
}
if (!(key_info & WPA_KEY_INFO_REQUEST) &&
- !wpa_replay_counter_valid(sm, key->replay_counter)) {
+ !wpa_replay_counter_valid(sm->key_replay, key->replay_counter)) {
int i;
- wpa_auth_vlogger(wpa_auth, sm->addr, LOGGER_INFO,
- "received EAPOL-Key %s with unexpected "
- "replay counter", msgtxt);
+
+ if (msg == PAIRWISE_2 &&
+ wpa_replay_counter_valid(sm->prev_key_replay,
+ key->replay_counter) &&
+ sm->wpa_ptk_state == WPA_PTK_PTKINITNEGOTIATING &&
+ os_memcmp(sm->SNonce, key->key_nonce, WPA_NONCE_LEN) != 0)
+ {
+ /*
+ * Some supplicant implementations (e.g., Windows XP
+ * WZC) update SNonce for each EAPOL-Key 2/4. This
+ * breaks the workaround on accepting any of the
+ * pending requests, so allow the SNonce to be updated
+ * even if we have already sent out EAPOL-Key 3/4.
+ */
+ wpa_auth_vlogger(wpa_auth, sm->addr, LOGGER_DEBUG,
+ "Process SNonce update from STA "
+ "based on retransmitted EAPOL-Key "
+ "1/4");
+ sm->update_snonce = 1;
+ wpa_replay_counter_mark_invalid(sm->prev_key_replay,
+ key->replay_counter);
+ goto continue_processing;
+ }
+
+ if (msg == PAIRWISE_2 &&
+ wpa_replay_counter_valid(sm->prev_key_replay,
+ key->replay_counter) &&
+ sm->wpa_ptk_state == WPA_PTK_PTKINITNEGOTIATING) {
+ wpa_auth_vlogger(wpa_auth, sm->addr, LOGGER_DEBUG,
+ "ignore retransmitted EAPOL-Key %s - "
+ "SNonce did not change", msgtxt);
+ } else {
+ wpa_auth_vlogger(wpa_auth, sm->addr, LOGGER_DEBUG,
+ "received EAPOL-Key %s with "
+ "unexpected replay counter", msgtxt);
+ }
for (i = 0; i < RSNA_MAX_EAPOL_RETRIES; i++) {
if (!sm->key_replay[i].valid)
break;
@@ -785,16 +917,37 @@ void wpa_receive(struct wpa_authenticator *wpa_auth,
return;
}
+continue_processing:
switch (msg) {
case PAIRWISE_2:
if (sm->wpa_ptk_state != WPA_PTK_PTKSTART &&
- sm->wpa_ptk_state != WPA_PTK_PTKCALCNEGOTIATING) {
+ sm->wpa_ptk_state != WPA_PTK_PTKCALCNEGOTIATING &&
+ (!sm->update_snonce ||
+ sm->wpa_ptk_state != WPA_PTK_PTKINITNEGOTIATING)) {
wpa_auth_vlogger(wpa_auth, sm->addr, LOGGER_INFO,
"received EAPOL-Key msg 2/4 in "
"invalid state (%d) - dropped",
sm->wpa_ptk_state);
return;
}
+ random_add_randomness(key->key_nonce, WPA_NONCE_LEN);
+ if (sm->group->reject_4way_hs_for_entropy) {
+ /*
+ * The system did not have enough entropy to generate
+ * strong random numbers. Reject the first 4-way
+ * handshake(s) and collect some entropy based on the
+ * information from it. Once enough entropy is
+ * available, the next atempt will trigger GMK/Key
+ * Counter update and the station will be allowed to
+ * continue.
+ */
+ wpa_printf(MSG_DEBUG, "WPA: Reject 4-way handshake to "
+ "collect more entropy for random number "
+ "generation");
+ random_mark_pool_ready();
+ wpa_sta_disconnect(wpa_auth, sm->addr);
+ return;
+ }
if (wpa_parse_kde_ies((u8 *) (key + 1), key_data_length,
&kde) < 0) {
wpa_auth_vlogger(wpa_auth, sm->addr, LOGGER_INFO,
@@ -897,7 +1050,7 @@ void wpa_receive(struct wpa_authenticator *wpa_auth,
}
sm->MICVerified = FALSE;
- if (sm->PTK_valid) {
+ if (sm->PTK_valid && !sm->update_snonce) {
if (wpa_verify_key_mic(&sm->PTK, data, data_len)) {
wpa_auth_logger(wpa_auth, sm->addr, LOGGER_INFO,
"received EAPOL-Key with invalid MIC");
@@ -905,6 +1058,7 @@ void wpa_receive(struct wpa_authenticator *wpa_auth,
}
sm->MICVerified = TRUE;
eloop_cancel_timeout(wpa_send_eapol_timeout, wpa_auth, sm);
+ sm->pending_1_of_4_timeout = 0;
}
if (key_info & WPA_KEY_INFO_REQUEST) {
@@ -930,17 +1084,10 @@ void wpa_receive(struct wpa_authenticator *wpa_auth,
#endif /* CONFIG_PEERKEY */
return;
} else if (key_info & WPA_KEY_INFO_ERROR) {
- /* Supplicant reported a Michael MIC error */
- wpa_auth_logger(wpa_auth, sm->addr, LOGGER_INFO,
- "received EAPOL-Key Error Request "
- "(STA detected Michael MIC failure)");
- wpa_auth_mic_failure_report(wpa_auth, sm->addr);
- sm->dot11RSNAStatsTKIPRemoteMICFailures++;
- wpa_auth->dot11RSNAStatsTKIPRemoteMICFailures++;
- /* Error report is not a request for a new key
- * handshake, but since Authenticator may do it, let's
- * change the keys now anyway. */
- wpa_request_new_ptk(sm);
+ if (wpa_receive_error_report(
+ wpa_auth, sm,
+ !(key_info & WPA_KEY_INFO_KEY_TYPE)) > 0)
+ return; /* STA entry was removed */
} else if (key_info & WPA_KEY_INFO_KEY_TYPE) {
wpa_auth_logger(wpa_auth, sm->addr, LOGGER_INFO,
"received EAPOL-Key Request for new "
@@ -958,19 +1105,34 @@ void wpa_receive(struct wpa_authenticator *wpa_auth,
wpa_auth_logger(wpa_auth, sm->addr, LOGGER_INFO,
"received EAPOL-Key Request for GTK "
"rekeying");
- /* FIX: why was this triggering PTK rekeying for the
- * STA that requested Group Key rekeying?? */
- /* wpa_request_new_ptk(sta->wpa_sm); */
eloop_cancel_timeout(wpa_rekey_gtk, wpa_auth, NULL);
wpa_rekey_gtk(wpa_auth, NULL);
}
} else {
- /* Do not allow the same key replay counter to be reused. This
- * does also invalidate all other pending replay counters if
- * retransmissions were used, i.e., we will only process one of
- * the pending replies and ignore rest if more than one is
- * received. */
- sm->key_replay[0].valid = FALSE;
+ /* Do not allow the same key replay counter to be reused. */
+ wpa_replay_counter_mark_invalid(sm->key_replay,
+ key->replay_counter);
+
+ if (msg == PAIRWISE_2) {
+ /*
+ * Maintain a copy of the pending EAPOL-Key frames in
+ * case the EAPOL-Key frame was retransmitted. This is
+ * needed to allow EAPOL-Key msg 2/4 reply to another
+ * pending msg 1/4 to update the SNonce to work around
+ * unexpected supplicant behavior.
+ */
+ os_memcpy(sm->prev_key_replay, sm->key_replay,
+ sizeof(sm->key_replay));
+ } else {
+ os_memset(sm->prev_key_replay, 0,
+ sizeof(sm->prev_key_replay));
+ }
+
+ /*
+ * Make sure old valid counters are not accepted anymore and
+ * do not get copied again.
+ */
+ wpa_replay_counter_mark_invalid(sm->key_replay, NULL);
}
#ifdef CONFIG_PEERKEY
@@ -987,6 +1149,7 @@ void wpa_receive(struct wpa_authenticator *wpa_auth,
os_memcpy(sm->last_rx_eapol_key, data, data_len);
sm->last_rx_eapol_key_len = data_len;
+ sm->rx_eapol_key_secure = !!(key_info & WPA_KEY_INFO_SECURE);
sm->EAPOLKeyReceived = TRUE;
sm->EAPOLKeyPairwise = !!(key_info & WPA_KEY_INFO_KEY_TYPE);
sm->EAPOLKeyRequest = !!(key_info & WPA_KEY_INFO_REQUEST);
@@ -995,25 +1158,37 @@ void wpa_receive(struct wpa_authenticator *wpa_auth,
}
-static void wpa_gmk_to_gtk(const u8 *gmk, const u8 *addr, const u8 *gnonce,
- u8 *gtk, size_t gtk_len)
+static int wpa_gmk_to_gtk(const u8 *gmk, const char *label, const u8 *addr,
+ const u8 *gnonce, u8 *gtk, size_t gtk_len)
{
- u8 data[ETH_ALEN + WPA_NONCE_LEN];
+ u8 data[ETH_ALEN + WPA_NONCE_LEN + 8 + 16];
+ u8 *pos;
+ int ret = 0;
- /* GTK = PRF-X(GMK, "Group key expansion", AA || GNonce) */
+ /* GTK = PRF-X(GMK, "Group key expansion",
+ * AA || GNonce || Time || random data)
+ * The example described in the IEEE 802.11 standard uses only AA and
+ * GNonce as inputs here. Add some more entropy since this derivation
+ * is done only at the Authenticator and as such, does not need to be
+ * exactly same.
+ */
os_memcpy(data, addr, ETH_ALEN);
os_memcpy(data + ETH_ALEN, gnonce, WPA_NONCE_LEN);
+ pos = data + ETH_ALEN + WPA_NONCE_LEN;
+ wpa_get_ntp_timestamp(pos);
+ pos += 8;
+ if (random_get_bytes(pos, 16) < 0)
+ ret = -1;
#ifdef CONFIG_IEEE80211W
- sha256_prf(gmk, WPA_GMK_LEN, "Group key expansion",
- data, sizeof(data), gtk, gtk_len);
+ sha256_prf(gmk, WPA_GMK_LEN, label, data, sizeof(data), gtk, gtk_len);
#else /* CONFIG_IEEE80211W */
- sha1_prf(gmk, WPA_GMK_LEN, "Group key expansion",
- data, sizeof(data), gtk, gtk_len);
+ if (sha1_prf(gmk, WPA_GMK_LEN, label, data, sizeof(data), gtk, gtk_len)
+ < 0)
+ ret = -1;
#endif /* CONFIG_IEEE80211W */
- wpa_hexdump_key(MSG_DEBUG, "GMK", gmk, WPA_GMK_LEN);
- wpa_hexdump_key(MSG_DEBUG, "GTK", gtk, gtk_len);
+ return ret;
}
@@ -1022,6 +1197,7 @@ static void wpa_send_eapol_timeout(void *eloop_ctx, void *timeout_ctx)
struct wpa_authenticator *wpa_auth = eloop_ctx;
struct wpa_state_machine *sm = timeout_ctx;
+ sm->pending_1_of_4_timeout = 0;
wpa_auth_logger(wpa_auth, sm->addr, LOGGER_DEBUG, "EAPOL-Key timeout");
sm->TimeoutEvt = TRUE;
wpa_sm_step(sm);
@@ -1049,7 +1225,7 @@ void __wpa_send_eapol(struct wpa_authenticator *wpa_auth,
version = force_version;
else if (wpa_use_aes_cmac(sm))
version = WPA_KEY_INFO_TYPE_AES_128_CMAC;
- else if (sm->pairwise == WPA_CIPHER_CCMP)
+ else if (sm->pairwise != WPA_CIPHER_TKIP)
version = WPA_KEY_INFO_TYPE_HMAC_SHA1_AES;
else
version = WPA_KEY_INFO_TYPE_HMAC_MD5_RC4;
@@ -1096,20 +1272,7 @@ void __wpa_send_eapol(struct wpa_authenticator *wpa_auth,
WPA_PUT_BE16(key->key_info, key_info);
alg = pairwise ? sm->pairwise : wpa_auth->conf.wpa_group;
- switch (alg) {
- case WPA_CIPHER_CCMP:
- WPA_PUT_BE16(key->key_length, 16);
- break;
- case WPA_CIPHER_TKIP:
- WPA_PUT_BE16(key->key_length, 32);
- break;
- case WPA_CIPHER_WEP40:
- WPA_PUT_BE16(key->key_length, 5);
- break;
- case WPA_CIPHER_WEP104:
- WPA_PUT_BE16(key->key_length, 13);
- break;
- }
+ WPA_PUT_BE16(key->key_length, wpa_cipher_key_len(alg));
if (key_info & WPA_KEY_INFO_SMK_MESSAGE)
WPA_PUT_BE16(key->key_length, 0);
@@ -1209,10 +1372,15 @@ static void wpa_send_eapol(struct wpa_authenticator *wpa_auth,
keyidx, encr, 0);
ctr = pairwise ? sm->TimeoutCtr : sm->GTimeoutCtr;
- if (ctr == 1)
- timeout_ms = eapol_key_timeout_first;
+ if (ctr == 1 && wpa_auth->conf.tx_status)
+ timeout_ms = pairwise ? eapol_key_timeout_first :
+ eapol_key_timeout_first_group;
else
timeout_ms = eapol_key_timeout_subseq;
+ if (pairwise && ctr == 1 && !(key_info & WPA_KEY_INFO_MIC))
+ sm->pending_1_of_4_timeout = 1;
+ wpa_printf(MSG_DEBUG, "WPA: Use EAPOL-Key timeout of %u ms (retry "
+ "counter %d)", timeout_ms, ctr);
eloop_register_timeout(timeout_ms / 1000, (timeout_ms % 1000) * 1000,
wpa_send_eapol_timeout, wpa_auth, sm);
}
@@ -1247,8 +1415,7 @@ void wpa_remove_ptk(struct wpa_state_machine *sm)
{
sm->PTK_valid = FALSE;
os_memset(&sm->PTK, 0, sizeof(sm->PTK));
- wpa_auth_set_key(sm->wpa_auth, 0, WPA_ALG_NONE, sm->addr, 0, (u8 *) "",
- 0);
+ wpa_auth_set_key(sm->wpa_auth, 0, WPA_ALG_NONE, sm->addr, 0, NULL, 0);
sm->pairwise_set = FALSE;
eloop_cancel_timeout(wpa_rekey_ptk, sm->wpa_auth, sm);
}
@@ -1338,22 +1505,6 @@ int wpa_auth_sm_event(struct wpa_state_machine *sm, wpa_event event)
}
-static enum wpa_alg wpa_alg_enum(int alg)
-{
- switch (alg) {
- case WPA_CIPHER_CCMP:
- return WPA_ALG_CCMP;
- case WPA_CIPHER_TKIP:
- return WPA_ALG_TKIP;
- case WPA_CIPHER_WEP104:
- case WPA_CIPHER_WEP40:
- return WPA_ALG_WEP;
- default:
- return WPA_ALG_NONE;
- }
-}
-
-
SM_STATE(WPA_PTK, INITIALIZE)
{
SM_ENTRY_MA(WPA_PTK, INITIALIZE, wpa_ptk);
@@ -1411,11 +1562,58 @@ SM_STATE(WPA_PTK, AUTHENTICATION)
}
+static void wpa_group_ensure_init(struct wpa_authenticator *wpa_auth,
+ struct wpa_group *group)
+{
+ if (group->first_sta_seen)
+ return;
+ /*
+ * System has run bit further than at the time hostapd was started
+ * potentially very early during boot up. This provides better chances
+ * of collecting more randomness on embedded systems. Re-initialize the
+ * GMK and Counter here to improve their strength if there was not
+ * enough entropy available immediately after system startup.
+ */
+ wpa_printf(MSG_DEBUG, "WPA: Re-initialize GMK/Counter on first "
+ "station");
+ if (random_pool_ready() != 1) {
+ wpa_printf(MSG_INFO, "WPA: Not enough entropy in random pool "
+ "to proceed - reject first 4-way handshake");
+ group->reject_4way_hs_for_entropy = TRUE;
+ } else {
+ group->first_sta_seen = TRUE;
+ group->reject_4way_hs_for_entropy = FALSE;
+ }
+
+ wpa_group_init_gmk_and_counter(wpa_auth, group);
+ wpa_gtk_update(wpa_auth, group);
+ wpa_group_config_group_keys(wpa_auth, group);
+}
+
+
SM_STATE(WPA_PTK, AUTHENTICATION2)
{
SM_ENTRY_MA(WPA_PTK, AUTHENTICATION2, wpa_ptk);
- os_memcpy(sm->ANonce, sm->group->Counter, WPA_NONCE_LEN);
- inc_byte_array(sm->group->Counter, WPA_NONCE_LEN);
+
+ wpa_group_ensure_init(sm->wpa_auth, sm->group);
+
+ /*
+ * Definition of ANonce selection in IEEE Std 802.11i-2004 is somewhat
+ * ambiguous. The Authenticator state machine uses a counter that is
+ * incremented by one for each 4-way handshake. However, the security
+ * analysis of 4-way handshake points out that unpredictable nonces
+ * help in preventing precomputation attacks. Instead of the state
+ * machine definition, use an unpredictable nonce value here to provide
+ * stronger protection against potential precomputation attacks.
+ */
+ if (random_get_bytes(sm->ANonce, WPA_NONCE_LEN)) {
+ wpa_printf(MSG_ERROR, "WPA: Failed to get random data for "
+ "ANonce.");
+ wpa_sta_disconnect(sm->wpa_auth, sm->addr);
+ return;
+ }
+ wpa_hexdump(MSG_DEBUG, "WPA: Assign ANonce", sm->ANonce,
+ WPA_NONCE_LEN);
sm->ReAuthenticationRequest = FALSE;
/* IEEE 802.11i does not clear TimeoutCtr here, but this is more
* logical place than INITIALIZE since AUTHENTICATION2 can be
@@ -1531,7 +1729,7 @@ SM_STATE(WPA_PTK, PTKSTART)
static int wpa_derive_ptk(struct wpa_state_machine *sm, const u8 *pmk,
struct wpa_ptk *ptk)
{
- size_t ptk_len = sm->pairwise == WPA_CIPHER_CCMP ? 48 : 64;
+ size_t ptk_len = sm->pairwise != WPA_CIPHER_TKIP ? 48 : 64;
#ifdef CONFIG_IEEE80211R
if (wpa_key_mgmt_ft(sm->wpa_key_mgmt))
return wpa_auth_derive_ptk_ft(sm, pmk, ptk, ptk_len);
@@ -1554,6 +1752,7 @@ SM_STATE(WPA_PTK, PTKCALCNEGOTIATING)
SM_ENTRY_MA(WPA_PTK, PTKCALCNEGOTIATING, wpa_ptk);
sm->EAPOLKeyReceived = FALSE;
+ sm->update_snonce = FALSE;
/* WPA with IEEE 802.1X: use the derived PMK from EAP
* WPA-PSK: iterate through possible PSKs and select the one matching
@@ -1605,6 +1804,7 @@ SM_STATE(WPA_PTK, PTKCALCNEGOTIATING)
}
#endif /* CONFIG_IEEE80211R */
+ sm->pending_1_of_4_timeout = 0;
eloop_cancel_timeout(wpa_send_eapol_timeout, sm->wpa_auth, sm);
if (wpa_key_mgmt_wpa_psk(sm->wpa_key_mgmt)) {
@@ -1654,6 +1854,14 @@ static u8 * ieee80211w_kde_add(struct wpa_state_machine *sm, u8 *pos)
wpa_auth_get_seqnum(sm->wpa_auth, NULL, gsm->GN_igtk, igtk.pn) < 0)
os_memset(igtk.pn, 0, sizeof(igtk.pn));
os_memcpy(igtk.igtk, gsm->IGTK[gsm->GN_igtk - 4], WPA_IGTK_LEN);
+ if (sm->wpa_auth->conf.disable_gtk) {
+ /*
+ * Provide unique random IGTK to each STA to prevent use of
+ * IGTK in the BSS.
+ */
+ if (random_get_bytes(igtk.igtk, WPA_IGTK_LEN) < 0)
+ return pos;
+ }
pos = wpa_add_kde(pos, RSN_KEY_DATA_IGTK,
(const u8 *) &igtk, sizeof(igtk), NULL, 0);
@@ -1678,7 +1886,7 @@ static u8 * ieee80211w_kde_add(struct wpa_state_machine *sm, u8 *pos)
SM_STATE(WPA_PTK, PTKINITNEGOTIATING)
{
- u8 rsc[WPA_KEY_RSC_LEN], *_rsc, *gtk, *kde, *pos;
+ u8 rsc[WPA_KEY_RSC_LEN], *_rsc, *gtk, *kde, *pos, dummy_gtk[32];
size_t gtk_len, kde_len;
struct wpa_group *gsm = sm->group;
u8 *wpa_ie;
@@ -1716,6 +1924,15 @@ SM_STATE(WPA_PTK, PTKINITNEGOTIATING)
secure = 1;
gtk = gsm->GTK[gsm->GN - 1];
gtk_len = gsm->GTK_len;
+ if (sm->wpa_auth->conf.disable_gtk) {
+ /*
+ * Provide unique random GTK to each STA to prevent use
+ * of GTK in the BSS.
+ */
+ if (random_get_bytes(dummy_gtk, gtk_len) < 0)
+ return;
+ gtk = dummy_gtk;
+ }
keyidx = gsm->GN;
_rsc = rsc;
encr = 1;
@@ -1726,6 +1943,20 @@ SM_STATE(WPA_PTK, PTKINITNEGOTIATING)
gtk_len = 0;
keyidx = 0;
_rsc = NULL;
+ if (sm->rx_eapol_key_secure) {
+ /*
+ * It looks like Windows 7 supplicant tries to use
+ * Secure bit in msg 2/4 after having reported Michael
+ * MIC failure and it then rejects the 4-way handshake
+ * if msg 3/4 does not set Secure bit. Work around this
+ * by setting the Secure bit here even in the case of
+ * WPA if the supplicant used it first.
+ */
+ wpa_auth_logger(sm->wpa_auth, sm->addr, LOGGER_DEBUG,
+ "STA used Secure bit in WPA msg 2/4 - "
+ "set Secure for 3/4 as workaround");
+ secure = 1;
+ }
}
kde_len = wpa_ie_len + ieee80211w_kde_len(sm);
@@ -1813,15 +2044,8 @@ SM_STATE(WPA_PTK, PTKINITDONE)
SM_ENTRY_MA(WPA_PTK, PTKINITDONE, wpa_ptk);
sm->EAPOLKeyReceived = FALSE;
if (sm->Pair) {
- enum wpa_alg alg;
- int klen;
- if (sm->pairwise == WPA_CIPHER_TKIP) {
- alg = WPA_ALG_TKIP;
- klen = 32;
- } else {
- alg = WPA_ALG_CCMP;
- klen = 16;
- }
+ enum wpa_alg alg = wpa_cipher_to_alg(sm->pairwise);
+ int klen = wpa_cipher_key_len(sm->pairwise);
if (wpa_auth_set_key(sm->wpa_auth, 0, alg, sm->addr, 0,
sm->PTK.tk1, klen)) {
wpa_sta_disconnect(sm->wpa_auth, sm->addr);
@@ -1876,8 +2100,11 @@ SM_STEP(WPA_PTK)
if (sm->Init)
SM_ENTER(WPA_PTK, INITIALIZE);
else if (sm->Disconnect
- /* || FIX: dot11RSNAConfigSALifetime timeout */)
+ /* || FIX: dot11RSNAConfigSALifetime timeout */) {
+ wpa_auth_logger(wpa_auth, sm->addr, LOGGER_DEBUG,
+ "WPA_PTK: sm->Disconnect");
SM_ENTER(WPA_PTK, DISCONNECT);
+ }
else if (sm->DeauthenticationRequest)
SM_ENTER(WPA_PTK, DISCONNECTED);
else if (sm->AuthenticationRequest)
@@ -1913,6 +2140,8 @@ SM_STEP(WPA_PTK)
SM_ENTER(WPA_PTK, PTKSTART);
else {
wpa_auth->dot11RSNA4WayHandshakeFailures++;
+ wpa_auth_logger(sm->wpa_auth, sm->addr, LOGGER_INFO,
+ "INITPMK - keyAvailable = false");
SM_ENTER(WPA_PTK, DISCONNECT);
}
break;
@@ -1933,6 +2162,9 @@ SM_STEP(WPA_PTK)
else if (sm->TimeoutCtr >
(int) dot11RSNAConfigPairwiseUpdateCount) {
wpa_auth->dot11RSNA4WayHandshakeFailures++;
+ wpa_auth_vlogger(sm->wpa_auth, sm->addr, LOGGER_DEBUG,
+ "PTKSTART: Retry limit %d reached",
+ dot11RSNAConfigPairwiseUpdateCount);
SM_ENTER(WPA_PTK, DISCONNECT);
} else if (sm->TimeoutEvt)
SM_ENTER(WPA_PTK, PTKSTART);
@@ -1950,12 +2182,18 @@ SM_STEP(WPA_PTK)
SM_ENTER(WPA_PTK, PTKINITNEGOTIATING);
break;
case WPA_PTK_PTKINITNEGOTIATING:
- if (sm->EAPOLKeyReceived && !sm->EAPOLKeyRequest &&
- sm->EAPOLKeyPairwise && sm->MICVerified)
+ if (sm->update_snonce)
+ SM_ENTER(WPA_PTK, PTKCALCNEGOTIATING);
+ else if (sm->EAPOLKeyReceived && !sm->EAPOLKeyRequest &&
+ sm->EAPOLKeyPairwise && sm->MICVerified)
SM_ENTER(WPA_PTK, PTKINITDONE);
else if (sm->TimeoutCtr >
(int) dot11RSNAConfigPairwiseUpdateCount) {
wpa_auth->dot11RSNA4WayHandshakeFailures++;
+ wpa_auth_vlogger(sm->wpa_auth, sm->addr, LOGGER_DEBUG,
+ "PTKINITNEGOTIATING: Retry limit %d "
+ "reached",
+ dot11RSNAConfigPairwiseUpdateCount);
SM_ENTER(WPA_PTK, DISCONNECT);
} else if (sm->TimeoutEvt)
SM_ENTER(WPA_PTK, PTKINITNEGOTIATING);
@@ -1984,6 +2222,7 @@ SM_STATE(WPA_PTK_GROUP, REKEYNEGOTIATING)
struct wpa_group *gsm = sm->group;
u8 *kde, *pos, hdr[2];
size_t kde_len;
+ u8 *gtk, dummy_gtk[32];
SM_ENTRY_MA(WPA_PTK_GROUP, REKEYNEGOTIATING, wpa_ptk_group);
@@ -2004,6 +2243,16 @@ SM_STATE(WPA_PTK_GROUP, REKEYNEGOTIATING)
wpa_auth_logger(sm->wpa_auth, sm->addr, LOGGER_DEBUG,
"sending 1/2 msg of Group Key Handshake");
+ gtk = gsm->GTK[gsm->GN - 1];
+ if (sm->wpa_auth->conf.disable_gtk) {
+ /*
+ * Provide unique random GTK to each STA to prevent use
+ * of GTK in the BSS.
+ */
+ if (random_get_bytes(dummy_gtk, gsm->GTK_len) < 0)
+ return;
+ gtk = dummy_gtk;
+ }
if (sm->wpa == WPA_VERSION_WPA2) {
kde_len = 2 + RSN_SELECTOR_LEN + 2 + gsm->GTK_len +
ieee80211w_kde_len(sm);
@@ -2015,10 +2264,10 @@ SM_STATE(WPA_PTK_GROUP, REKEYNEGOTIATING)
hdr[0] = gsm->GN & 0x03;
hdr[1] = 0;
pos = wpa_add_kde(pos, RSN_KEY_DATA_GROUPKEY, hdr, 2,
- gsm->GTK[gsm->GN - 1], gsm->GTK_len);
+ gtk, gsm->GTK_len);
pos = ieee80211w_kde_add(sm, pos);
} else {
- kde = gsm->GTK[gsm->GN - 1];
+ kde = gtk;
pos = kde + gsm->GTK_len;
}
@@ -2094,20 +2343,24 @@ static int wpa_gtk_update(struct wpa_authenticator *wpa_auth,
{
int ret = 0;
- /* FIX: is this the correct way of getting GNonce? */
os_memcpy(group->GNonce, group->Counter, WPA_NONCE_LEN);
inc_byte_array(group->Counter, WPA_NONCE_LEN);
- wpa_gmk_to_gtk(group->GMK, wpa_auth->addr, group->GNonce,
- group->GTK[group->GN - 1], group->GTK_len);
+ if (wpa_gmk_to_gtk(group->GMK, "Group key expansion",
+ wpa_auth->addr, group->GNonce,
+ group->GTK[group->GN - 1], group->GTK_len) < 0)
+ ret = -1;
+ wpa_hexdump_key(MSG_DEBUG, "GTK",
+ group->GTK[group->GN - 1], group->GTK_len);
#ifdef CONFIG_IEEE80211W
if (wpa_auth->conf.ieee80211w != NO_MGMT_FRAME_PROTECTION) {
- if (os_get_random(group->IGTK[group->GN_igtk - 4],
- WPA_IGTK_LEN) < 0) {
- wpa_printf(MSG_INFO, "RSN: Failed to get new random "
- "IGTK");
+ os_memcpy(group->GNonce, group->Counter, WPA_NONCE_LEN);
+ inc_byte_array(group->Counter, WPA_NONCE_LEN);
+ if (wpa_gmk_to_gtk(group->GMK, "IGTK key expansion",
+ wpa_auth->addr, group->GNonce,
+ group->IGTK[group->GN_igtk - 4],
+ WPA_IGTK_LEN) < 0)
ret = -1;
- }
wpa_hexdump_key(MSG_DEBUG, "IGTK",
group->IGTK[group->GN_igtk - 4], WPA_IGTK_LEN);
}
@@ -2140,28 +2393,118 @@ static void wpa_group_gtk_init(struct wpa_authenticator *wpa_auth,
static int wpa_group_update_sta(struct wpa_state_machine *sm, void *ctx)
{
+ if (ctx != NULL && ctx != sm->group)
+ return 0;
+
if (sm->wpa_ptk_state != WPA_PTK_PTKINITDONE) {
wpa_auth_logger(sm->wpa_auth, sm->addr, LOGGER_DEBUG,
"Not in PTKINITDONE; skip Group Key update");
+ sm->GUpdateStationKeys = FALSE;
return 0;
}
if (sm->GUpdateStationKeys) {
/*
- * This should not really happen, but just in case, make sure
- * we do not count the same STA twice in GKeyDoneStations.
+ * This should not really happen, so add a debug log entry.
+ * Since we clear the GKeyDoneStations before the loop, the
+ * station needs to be counted here anyway.
*/
wpa_auth_logger(sm->wpa_auth, sm->addr, LOGGER_DEBUG,
- "GUpdateStationKeys already set - do not "
- "increment GKeyDoneStations");
- } else {
- sm->group->GKeyDoneStations++;
- sm->GUpdateStationKeys = TRUE;
+ "GUpdateStationKeys was already set when "
+ "marking station for GTK rekeying");
}
+
+ /* Do not rekey GTK/IGTK when STA is in WNM-Sleep Mode */
+ if (sm->is_wnmsleep)
+ return 0;
+
+ sm->group->GKeyDoneStations++;
+ sm->GUpdateStationKeys = TRUE;
+
wpa_sm_step(sm);
return 0;
}
+#ifdef CONFIG_WNM
+/* update GTK when exiting WNM-Sleep Mode */
+void wpa_wnmsleep_rekey_gtk(struct wpa_state_machine *sm)
+{
+ if (sm->is_wnmsleep)
+ return;
+
+ wpa_group_update_sta(sm, NULL);
+}
+
+
+void wpa_set_wnmsleep(struct wpa_state_machine *sm, int flag)
+{
+ sm->is_wnmsleep = !!flag;
+}
+
+
+int wpa_wnmsleep_gtk_subelem(struct wpa_state_machine *sm, u8 *pos)
+{
+ struct wpa_group *gsm = sm->group;
+ u8 *start = pos;
+
+ /*
+ * GTK subelement:
+ * Sub-elem ID[1] | Length[1] | Key Info[2] | Key Length[1] | RSC[8] |
+ * Key[5..32]
+ */
+ *pos++ = WNM_SLEEP_SUBELEM_GTK;
+ *pos++ = 11 + gsm->GTK_len;
+ /* Key ID in B0-B1 of Key Info */
+ WPA_PUT_LE16(pos, gsm->GN & 0x03);
+ pos += 2;
+ *pos++ = gsm->GTK_len;
+ if (wpa_auth_get_seqnum(sm->wpa_auth, NULL, gsm->GN, pos) != 0)
+ return 0;
+ pos += 8;
+ os_memcpy(pos, gsm->GTK[gsm->GN - 1], gsm->GTK_len);
+ pos += gsm->GTK_len;
+
+ wpa_printf(MSG_DEBUG, "WNM: GTK Key ID %u in WNM-Sleep Mode exit",
+ gsm->GN);
+ wpa_hexdump_key(MSG_DEBUG, "WNM: GTK in WNM-Sleep Mode exit",
+ gsm->GTK[gsm->GN - 1], gsm->GTK_len);
+
+ return pos - start;
+}
+
+
+#ifdef CONFIG_IEEE80211W
+int wpa_wnmsleep_igtk_subelem(struct wpa_state_machine *sm, u8 *pos)
+{
+ struct wpa_group *gsm = sm->group;
+ u8 *start = pos;
+
+ /*
+ * IGTK subelement:
+ * Sub-elem ID[1] | Length[1] | KeyID[2] | PN[6] | Key[16]
+ */
+ *pos++ = WNM_SLEEP_SUBELEM_IGTK;
+ *pos++ = 2 + 6 + WPA_IGTK_LEN;
+ WPA_PUT_LE16(pos, gsm->GN_igtk);
+ pos += 2;
+ if (wpa_auth_get_seqnum(sm->wpa_auth, NULL, gsm->GN_igtk, pos) != 0)
+ return 0;
+ pos += 6;
+
+ os_memcpy(pos, gsm->IGTK[gsm->GN_igtk - 4], WPA_IGTK_LEN);
+ pos += WPA_IGTK_LEN;
+
+ wpa_printf(MSG_DEBUG, "WNM: IGTK Key ID %u in WNM-Sleep Mode exit",
+ gsm->GN_igtk);
+ wpa_hexdump_key(MSG_DEBUG, "WNM: IGTK in WNM-Sleep Mode exit",
+ gsm->IGTK[gsm->GN_igtk - 4], WPA_IGTK_LEN);
+
+ return pos - start;
+}
+#endif /* CONFIG_IEEE80211W */
+#endif /* CONFIG_WNM */
+
+
static void wpa_group_setkeys(struct wpa_authenticator *wpa_auth,
struct wpa_group *group)
{
@@ -2185,32 +2528,54 @@ static void wpa_group_setkeys(struct wpa_authenticator *wpa_auth,
* including all STAs that could be in not-yet-completed state. */
wpa_gtk_update(wpa_auth, group);
- wpa_auth_for_each_sta(wpa_auth, wpa_group_update_sta, NULL);
+ if (group->GKeyDoneStations) {
+ wpa_printf(MSG_DEBUG, "wpa_group_setkeys: Unexpected "
+ "GKeyDoneStations=%d when starting new GTK rekey",
+ group->GKeyDoneStations);
+ group->GKeyDoneStations = 0;
+ }
+ wpa_auth_for_each_sta(wpa_auth, wpa_group_update_sta, group);
wpa_printf(MSG_DEBUG, "wpa_group_setkeys: GKeyDoneStations=%d",
group->GKeyDoneStations);
}
-static void wpa_group_setkeysdone(struct wpa_authenticator *wpa_auth,
- struct wpa_group *group)
+static int wpa_group_config_group_keys(struct wpa_authenticator *wpa_auth,
+ struct wpa_group *group)
+{
+ int ret = 0;
+
+ if (wpa_auth_set_key(wpa_auth, group->vlan_id,
+ wpa_cipher_to_alg(wpa_auth->conf.wpa_group),
+ broadcast_ether_addr, group->GN,
+ group->GTK[group->GN - 1], group->GTK_len) < 0)
+ ret = -1;
+
+#ifdef CONFIG_IEEE80211W
+ if (wpa_auth->conf.ieee80211w != NO_MGMT_FRAME_PROTECTION &&
+ wpa_auth_set_key(wpa_auth, group->vlan_id, WPA_ALG_IGTK,
+ broadcast_ether_addr, group->GN_igtk,
+ group->IGTK[group->GN_igtk - 4],
+ WPA_IGTK_LEN) < 0)
+ ret = -1;
+#endif /* CONFIG_IEEE80211W */
+
+ return ret;
+}
+
+
+static int wpa_group_setkeysdone(struct wpa_authenticator *wpa_auth,
+ struct wpa_group *group)
{
wpa_printf(MSG_DEBUG, "WPA: group state machine entering state "
"SETKEYSDONE (VLAN-ID %d)", group->vlan_id);
group->changed = TRUE;
group->wpa_group_state = WPA_GROUP_SETKEYSDONE;
- wpa_auth_set_key(wpa_auth, group->vlan_id,
- wpa_alg_enum(wpa_auth->conf.wpa_group),
- NULL, group->GN, group->GTK[group->GN - 1],
- group->GTK_len);
-#ifdef CONFIG_IEEE80211W
- if (wpa_auth->conf.ieee80211w != NO_MGMT_FRAME_PROTECTION) {
- wpa_auth_set_key(wpa_auth, group->vlan_id, WPA_ALG_IGTK,
- NULL, group->GN_igtk,
- group->IGTK[group->GN_igtk - 4],
- WPA_IGTK_LEN);
- }
-#endif /* CONFIG_IEEE80211W */
+ if (wpa_group_config_group_keys(wpa_auth, group) < 0)
+ return -1;
+
+ return 0;
}
@@ -2310,6 +2675,7 @@ void wpa_gtk_rekey(struct wpa_authenticator *wpa_auth)
group->GN_igtk = tmp;
#endif /* CONFIG_IEEE80211W */
wpa_gtk_update(wpa_auth, group);
+ wpa_group_config_group_keys(wpa_auth, group);
}
}
@@ -2320,23 +2686,6 @@ static const char * wpa_bool_txt(int bool)
}
-static int wpa_cipher_bits(int cipher)
-{
- switch (cipher) {
- case WPA_CIPHER_CCMP:
- return 128;
- case WPA_CIPHER_TKIP:
- return 256;
- case WPA_CIPHER_WEP104:
- return 104;
- case WPA_CIPHER_WEP40:
- return 40;
- default:
- return 0;
- }
-}
-
-
#define RSN_SUITE "%02x-%02x-%02x-%d"
#define RSN_SUITE_ARG(s) \
((s) >> 24) & 0xff, ((s) >> 16) & 0xff, ((s) >> 8) & 0xff, (s) & 0xff
@@ -2345,19 +2694,21 @@ int wpa_get_mib(struct wpa_authenticator *wpa_auth, char *buf, size_t buflen)
{
int len = 0, ret;
char pmkid_txt[PMKID_LEN * 2 + 1];
+#ifdef CONFIG_RSN_PREAUTH
+ const int preauth = 1;
+#else /* CONFIG_RSN_PREAUTH */
+ const int preauth = 0;
+#endif /* CONFIG_RSN_PREAUTH */
if (wpa_auth == NULL)
return len;
ret = os_snprintf(buf + len, buflen - len,
"dot11RSNAOptionImplemented=TRUE\n"
-#ifdef CONFIG_RSN_PREAUTH
- "dot11RSNAPreauthenticationImplemented=TRUE\n"
-#else /* CONFIG_RSN_PREAUTH */
- "dot11RSNAPreauthenticationImplemented=FALSE\n"
-#endif /* CONFIG_RSN_PREAUTH */
+ "dot11RSNAPreauthenticationImplemented=%s\n"
"dot11RSNAEnabled=%s\n"
"dot11RSNAPreauthenticationEnabled=%s\n",
+ wpa_bool_txt(preauth),
wpa_bool_txt(wpa_auth->conf.wpa & WPA_PROTO_RSN),
wpa_bool_txt(wpa_auth->conf.rsn_preauth));
if (ret < 0 || (size_t) ret >= buflen - len)
@@ -2397,7 +2748,7 @@ int wpa_get_mib(struct wpa_authenticator *wpa_auth, char *buf, size_t buflen)
!!wpa_auth->conf.wpa_strict_rekey,
dot11RSNAConfigGroupUpdateCount,
dot11RSNAConfigPairwiseUpdateCount,
- wpa_cipher_bits(wpa_auth->conf.wpa_group),
+ wpa_cipher_key_len(wpa_auth->conf.wpa_group) * 8,
dot11RSNAConfigPMKLifetime,
dot11RSNAConfigPMKReauthThreshold,
dot11RSNAConfigSATimeout,
@@ -2440,29 +2791,10 @@ int wpa_get_mib_sta(struct wpa_state_machine *sm, char *buf, size_t buflen)
/* dot11RSNAStatsEntry */
- if (sm->wpa == WPA_VERSION_WPA) {
- if (sm->pairwise == WPA_CIPHER_CCMP)
- pairwise = WPA_CIPHER_SUITE_CCMP;
- else if (sm->pairwise == WPA_CIPHER_TKIP)
- pairwise = WPA_CIPHER_SUITE_TKIP;
- else if (sm->pairwise == WPA_CIPHER_WEP104)
- pairwise = WPA_CIPHER_SUITE_WEP104;
- else if (sm->pairwise == WPA_CIPHER_WEP40)
- pairwise = WPA_CIPHER_SUITE_WEP40;
- else if (sm->pairwise == WPA_CIPHER_NONE)
- pairwise = WPA_CIPHER_SUITE_NONE;
- } else if (sm->wpa == WPA_VERSION_WPA2) {
- if (sm->pairwise == WPA_CIPHER_CCMP)
- pairwise = RSN_CIPHER_SUITE_CCMP;
- else if (sm->pairwise == WPA_CIPHER_TKIP)
- pairwise = RSN_CIPHER_SUITE_TKIP;
- else if (sm->pairwise == WPA_CIPHER_WEP104)
- pairwise = RSN_CIPHER_SUITE_WEP104;
- else if (sm->pairwise == WPA_CIPHER_WEP40)
- pairwise = RSN_CIPHER_SUITE_WEP40;
- else if (sm->pairwise == WPA_CIPHER_NONE)
- pairwise = RSN_CIPHER_SUITE_NONE;
- } else
+ pairwise = wpa_cipher_to_suite(sm->wpa == WPA_VERSION_WPA2 ?
+ WPA_PROTO_RSN : WPA_PROTO_WPA,
+ sm->pairwise);
+ if (pairwise == 0)
return 0;
ret = os_snprintf(
@@ -2473,7 +2805,7 @@ int wpa_get_mib_sta(struct wpa_state_machine *sm, char *buf, size_t buflen)
"dot11RSNAStatsSelectedPairwiseCipher=" RSN_SUITE "\n"
/* TODO: dot11RSNAStatsTKIPICVErrors */
"dot11RSNAStatsTKIPLocalMICFailures=%u\n"
- "dot11RSNAStatsTKIPRemoveMICFailures=%u\n"
+ "dot11RSNAStatsTKIPRemoteMICFailures=%u\n"
/* TODO: dot11RSNAStatsCCMPReplays */
/* TODO: dot11RSNAStatsCCMPDecryptErrors */
/* TODO: dot11RSNAStatsTKIPReplays */,
@@ -2570,7 +2902,8 @@ const u8 * wpa_auth_get_wpa_ie(struct wpa_authenticator *wpa_auth, size_t *len)
int wpa_auth_pmksa_add(struct wpa_state_machine *sm, const u8 *pmk,
int session_timeout, struct eapol_state_machine *eapol)
{
- if (sm == NULL || sm->wpa != WPA_VERSION_WPA2)
+ if (sm == NULL || sm->wpa != WPA_VERSION_WPA2 ||
+ sm->wpa_auth->conf.disable_pmksa_caching)
return -1;
if (pmksa_cache_auth_add(sm->wpa_auth->pmksa, pmk, PMK_LEN,
@@ -2609,7 +2942,7 @@ wpa_auth_add_group(struct wpa_authenticator *wpa_auth, int vlan_id)
wpa_printf(MSG_DEBUG, "WPA: Add group state machine for VLAN-ID %d",
vlan_id);
- group = wpa_group_init(wpa_auth, vlan_id);
+ group = wpa_group_init(wpa_auth, vlan_id, 0);
if (group == NULL)
return NULL;
@@ -2649,3 +2982,41 @@ int wpa_auth_sta_set_vlan(struct wpa_state_machine *sm, int vlan_id)
sm->group = group;
return 0;
}
+
+
+void wpa_auth_eapol_key_tx_status(struct wpa_authenticator *wpa_auth,
+ struct wpa_state_machine *sm, int ack)
+{
+ if (wpa_auth == NULL || sm == NULL)
+ return;
+ wpa_printf(MSG_DEBUG, "WPA: EAPOL-Key TX status for STA " MACSTR
+ " ack=%d", MAC2STR(sm->addr), ack);
+ if (sm->pending_1_of_4_timeout && ack) {
+ /*
+ * Some deployed supplicant implementations update their SNonce
+ * for each EAPOL-Key 2/4 message even within the same 4-way
+ * handshake and then fail to use the first SNonce when
+ * deriving the PTK. This results in unsuccessful 4-way
+ * handshake whenever the relatively short initial timeout is
+ * reached and EAPOL-Key 1/4 is retransmitted. Try to work
+ * around this by increasing the timeout now that we know that
+ * the station has received the frame.
+ */
+ int timeout_ms = eapol_key_timeout_subseq;
+ wpa_printf(MSG_DEBUG, "WPA: Increase initial EAPOL-Key 1/4 "
+ "timeout by %u ms because of acknowledged frame",
+ timeout_ms);
+ eloop_cancel_timeout(wpa_send_eapol_timeout, wpa_auth, sm);
+ eloop_register_timeout(timeout_ms / 1000,
+ (timeout_ms % 1000) * 1000,
+ wpa_send_eapol_timeout, wpa_auth, sm);
+ }
+}
+
+
+int wpa_auth_uses_sae(struct wpa_state_machine *sm)
+{
+ if (sm == NULL)
+ return 0;
+ return wpa_key_mgmt_sae(sm->wpa_key_mgmt);
+}
diff --git a/contrib/wpa/src/ap/wpa_auth.h b/contrib/wpa/src/ap/wpa_auth.h
index d0136c7..465eec6 100644
--- a/contrib/wpa/src/ap/wpa_auth.h
+++ b/contrib/wpa/src/ap/wpa_auth.h
@@ -2,14 +2,8 @@
* hostapd - IEEE 802.11i-2004 / WPA Authenticator
* Copyright (c) 2004-2007, Jouni Malinen <j@w1.fi>
*
- * This program is free software; you can redistribute it and/or modify
- * it under the terms of the GNU General Public License version 2 as
- * published by the Free Software Foundation.
- *
- * Alternatively, this software may be distributed under the terms of BSD
- * license.
- *
- * See README and COPYING for more details.
+ * This software may be distributed under the terms of the BSD license.
+ * See README for more details.
*/
#ifndef WPA_AUTH_H
@@ -143,7 +137,9 @@ struct wpa_auth_config {
int peerkey;
int wmm_enabled;
int wmm_uapsd;
+ int disable_pmksa_caching;
int okc;
+ int tx_status;
#ifdef CONFIG_IEEE80211W
enum mfp_options ieee80211w;
#endif /* CONFIG_IEEE80211W */
@@ -160,7 +156,10 @@ struct wpa_auth_config {
struct ft_remote_r0kh *r0kh_list;
struct ft_remote_r1kh *r1kh_list;
int pmk_r1_push;
+ int ft_over_ds;
#endif /* CONFIG_IEEE80211R */
+ int disable_gtk;
+ int ap_mlme;
};
typedef enum {
@@ -178,7 +177,7 @@ struct wpa_auth_callbacks {
void (*logger)(void *ctx, const u8 *addr, logger_level level,
const char *txt);
void (*disconnect)(void *ctx, const u8 *addr, u16 reason);
- void (*mic_failure_report)(void *ctx, const u8 *addr);
+ int (*mic_failure_report)(void *ctx, const u8 *addr);
void (*set_eapol)(void *ctx, const u8 *addr, wpa_eapol_variable var,
int value);
int (*get_eapol)(void *ctx, const u8 *addr, wpa_eapol_variable var);
@@ -199,12 +198,15 @@ struct wpa_auth_callbacks {
struct wpa_state_machine * (*add_sta)(void *ctx, const u8 *sta_addr);
int (*send_ft_action)(void *ctx, const u8 *dst,
const u8 *data, size_t data_len);
+ int (*add_tspec)(void *ctx, const u8 *sta_addr, u8 *tspec_ie,
+ size_t tspec_ielen);
#endif /* CONFIG_IEEE80211R */
};
struct wpa_authenticator * wpa_init(const u8 *addr,
struct wpa_auth_config *conf,
struct wpa_auth_callbacks *cb);
+int wpa_init_keys(struct wpa_authenticator *wpa_auth);
void wpa_deinit(struct wpa_authenticator *wpa_auth);
int wpa_reconfig(struct wpa_authenticator *wpa_auth,
struct wpa_auth_config *conf);
@@ -259,6 +261,8 @@ int wpa_auth_pmksa_add_preauth(struct wpa_authenticator *wpa_auth,
int session_timeout,
struct eapol_state_machine *eapol);
int wpa_auth_sta_set_vlan(struct wpa_state_machine *sm, int vlan_id);
+void wpa_auth_eapol_key_tx_status(struct wpa_authenticator *wpa_auth,
+ struct wpa_state_machine *sm, int ack);
#ifdef CONFIG_IEEE80211R
u8 * wpa_sm_write_assoc_resp_ies(struct wpa_state_machine *sm, u8 *pos,
@@ -278,4 +282,11 @@ int wpa_ft_rrb_rx(struct wpa_authenticator *wpa_auth, const u8 *src_addr,
void wpa_ft_push_pmk_r1(struct wpa_authenticator *wpa_auth, const u8 *addr);
#endif /* CONFIG_IEEE80211R */
+void wpa_wnmsleep_rekey_gtk(struct wpa_state_machine *sm);
+void wpa_set_wnmsleep(struct wpa_state_machine *sm, int flag);
+int wpa_wnmsleep_gtk_subelem(struct wpa_state_machine *sm, u8 *pos);
+int wpa_wnmsleep_igtk_subelem(struct wpa_state_machine *sm, u8 *pos);
+
+int wpa_auth_uses_sae(struct wpa_state_machine *sm);
+
#endif /* WPA_AUTH_H */
diff --git a/contrib/wpa/src/ap/wpa_auth_ft.c b/contrib/wpa/src/ap/wpa_auth_ft.c
index c9871d9..48bf79b 100644
--- a/contrib/wpa/src/ap/wpa_auth_ft.c
+++ b/contrib/wpa/src/ap/wpa_auth_ft.c
@@ -2,14 +2,8 @@
* hostapd - IEEE 802.11r - Fast BSS Transition
* Copyright (c) 2004-2009, Jouni Malinen <j@w1.fi>
*
- * This program is free software; you can redistribute it and/or modify
- * it under the terms of the GNU General Public License version 2 as
- * published by the Free Software Foundation.
- *
- * Alternatively, this software may be distributed under the terms of BSD
- * license.
- *
- * See README and COPYING for more details.
+ * This software may be distributed under the terms of the BSD license.
+ * See README for more details.
*/
#include "utils/includes.h"
@@ -18,38 +12,16 @@
#include "common/ieee802_11_defs.h"
#include "common/ieee802_11_common.h"
#include "crypto/aes_wrap.h"
+#include "crypto/random.h"
#include "ap_config.h"
#include "ieee802_11.h"
#include "wmm.h"
#include "wpa_auth.h"
#include "wpa_auth_i.h"
-#include "wpa_auth_ie.h"
#ifdef CONFIG_IEEE80211R
-struct wpa_ft_ies {
- const u8 *mdie;
- size_t mdie_len;
- const u8 *ftie;
- size_t ftie_len;
- const u8 *r1kh_id;
- const u8 *gtk;
- size_t gtk_len;
- const u8 *r0kh_id;
- size_t r0kh_id_len;
- const u8 *rsn;
- size_t rsn_len;
- const u8 *rsn_pmkid;
- const u8 *ric;
- size_t ric_len;
-};
-
-
-static int wpa_ft_parse_ies(const u8 *ies, size_t ies_len,
- struct wpa_ft_ies *parse);
-
-
static int wpa_ft_rrb_send(struct wpa_authenticator *wpa_auth, const u8 *dst,
const u8 *data, size_t data_len)
{
@@ -80,6 +52,19 @@ wpa_ft_add_sta(struct wpa_authenticator *wpa_auth, const u8 *sta_addr)
}
+static int wpa_ft_add_tspec(struct wpa_authenticator *wpa_auth,
+ const u8 *sta_addr,
+ u8 *tspec_ie, size_t tspec_ielen)
+{
+ if (wpa_auth->cb.add_tspec == NULL) {
+ wpa_printf(MSG_DEBUG, "FT: add_tspec is not initialized");
+ return -1;
+ }
+ return wpa_auth->cb.add_tspec(wpa_auth->cb.ctx, sta_addr, tspec_ie,
+ tspec_ielen);
+}
+
+
int wpa_write_mdie(struct wpa_auth_config *conf, u8 *buf, size_t len)
{
u8 *pos = buf;
@@ -91,7 +76,9 @@ int wpa_write_mdie(struct wpa_auth_config *conf, u8 *buf, size_t len)
*pos++ = MOBILITY_DOMAIN_ID_LEN + 1;
os_memcpy(pos, conf->mobility_domain, MOBILITY_DOMAIN_ID_LEN);
pos += MOBILITY_DOMAIN_ID_LEN;
- capab = RSN_FT_CAPAB_FT_OVER_DS;
+ capab = 0;
+ if (conf->ft_over_ds)
+ capab |= RSN_FT_CAPAB_FT_OVER_DS;
*pos++ = capab;
return pos - buf;
@@ -334,7 +321,7 @@ static int wpa_ft_pull_pmk_r1(struct wpa_authenticator *wpa_auth,
/* aes_wrap() does not support inplace encryption, so use a temporary
* buffer for the data. */
- if (os_get_random(f.nonce, sizeof(f.nonce))) {
+ if (random_get_bytes(f.nonce, sizeof(f.nonce))) {
wpa_printf(MSG_DEBUG, "FT: Failed to get random data for "
"nonce");
return -1;
@@ -497,7 +484,8 @@ static u8 * wpa_ft_igtk_subelem(struct wpa_state_machine *sm, size_t *len)
#endif /* CONFIG_IEEE80211W */
-static u8 * wpa_ft_process_rdie(u8 *pos, u8 *end, u8 id, u8 descr_count,
+static u8 * wpa_ft_process_rdie(struct wpa_state_machine *sm,
+ u8 *pos, u8 *end, u8 id, u8 descr_count,
const u8 *ies, size_t ies_len)
{
struct ieee802_11_elems parse;
@@ -530,7 +518,7 @@ static u8 * wpa_ft_process_rdie(u8 *pos, u8 *end, u8 id, u8 descr_count,
}
#ifdef NEED_AP_MLME
- if (parse.wmm_tspec) {
+ if (parse.wmm_tspec && sm->wpa_auth->conf.ap_mlme) {
struct wmm_tspec_element *tspec;
int res;
@@ -567,13 +555,35 @@ static u8 * wpa_ft_process_rdie(u8 *pos, u8 *end, u8 id, u8 descr_count,
}
#endif /* NEED_AP_MLME */
+ if (parse.wmm_tspec && !sm->wpa_auth->conf.ap_mlme) {
+ struct wmm_tspec_element *tspec;
+ int res;
+
+ tspec = (struct wmm_tspec_element *) pos;
+ os_memcpy(tspec, parse.wmm_tspec - 2, sizeof(*tspec));
+ res = wpa_ft_add_tspec(sm->wpa_auth, sm->addr, pos,
+ sizeof(*tspec));
+ if (res >= 0) {
+ if (res)
+ rdie->status_code = host_to_le16(res);
+ else {
+ /* TSPEC accepted; include updated TSPEC in
+ * response */
+ rdie->descr_count = 1;
+ pos += sizeof(*tspec);
+ }
+ return pos;
+ }
+ }
+
wpa_printf(MSG_DEBUG, "FT: No supported resource requested");
rdie->status_code = host_to_le16(WLAN_STATUS_UNSPECIFIED_FAILURE);
return pos;
}
-static u8 * wpa_ft_process_ric(u8 *pos, u8 *end, const u8 *ric, size_t ric_len)
+static u8 * wpa_ft_process_ric(struct wpa_state_machine *sm, u8 *pos, u8 *end,
+ const u8 *ric, size_t ric_len)
{
const u8 *rpos, *start;
const struct rsn_rdie *rdie;
@@ -595,7 +605,7 @@ static u8 * wpa_ft_process_ric(u8 *pos, u8 *end, const u8 *ric, size_t ric_len)
break;
rpos += 2 + rpos[1];
}
- pos = wpa_ft_process_rdie(pos, end, rdie->id,
+ pos = wpa_ft_process_rdie(sm, pos, end, rdie->id,
rdie->descr_count,
start, rpos - start);
}
@@ -704,7 +714,8 @@ u8 * wpa_sm_write_assoc_resp_ies(struct wpa_state_machine *sm, u8 *pos,
ric_start = pos;
if (wpa_ft_parse_ies(req_ies, req_ies_len, &parse) == 0 && parse.ric) {
- pos = wpa_ft_process_ric(pos, end, parse.ric, parse.ric_len);
+ pos = wpa_ft_process_ric(sm, pos, end, parse.ric,
+ parse.ric_len);
if (auth_alg == WLAN_AUTH_FT)
_ftie->mic_control[1] +=
ieee802_11_ie_count(ric_start,
@@ -725,143 +736,6 @@ u8 * wpa_sm_write_assoc_resp_ies(struct wpa_state_machine *sm, u8 *pos,
}
-static int wpa_ft_parse_ftie(const u8 *ie, size_t ie_len,
- struct wpa_ft_ies *parse)
-{
- const u8 *end, *pos;
-
- parse->ftie = ie;
- parse->ftie_len = ie_len;
-
- pos = ie + sizeof(struct rsn_ftie);
- end = ie + ie_len;
-
- while (pos + 2 <= end && pos + 2 + pos[1] <= end) {
- switch (pos[0]) {
- case FTIE_SUBELEM_R1KH_ID:
- if (pos[1] != FT_R1KH_ID_LEN) {
- wpa_printf(MSG_DEBUG, "FT: Invalid R1KH-ID "
- "length in FTIE: %d", pos[1]);
- return -1;
- }
- parse->r1kh_id = pos + 2;
- break;
- case FTIE_SUBELEM_GTK:
- parse->gtk = pos + 2;
- parse->gtk_len = pos[1];
- break;
- case FTIE_SUBELEM_R0KH_ID:
- if (pos[1] < 1 || pos[1] > FT_R0KH_ID_MAX_LEN) {
- wpa_printf(MSG_DEBUG, "FT: Invalid R0KH-ID "
- "length in FTIE: %d", pos[1]);
- return -1;
- }
- parse->r0kh_id = pos + 2;
- parse->r0kh_id_len = pos[1];
- break;
- }
-
- pos += 2 + pos[1];
- }
-
- return 0;
-}
-
-
-static int wpa_ft_parse_ies(const u8 *ies, size_t ies_len,
- struct wpa_ft_ies *parse)
-{
- const u8 *end, *pos;
- struct wpa_ie_data data;
- int ret;
- const struct rsn_ftie *ftie;
- int prot_ie_count = 0;
-
- os_memset(parse, 0, sizeof(*parse));
- if (ies == NULL)
- return 0;
-
- pos = ies;
- end = ies + ies_len;
- while (pos + 2 <= end && pos + 2 + pos[1] <= end) {
- switch (pos[0]) {
- case WLAN_EID_RSN:
- parse->rsn = pos + 2;
- parse->rsn_len = pos[1];
- ret = wpa_parse_wpa_ie_rsn(parse->rsn - 2,
- parse->rsn_len + 2,
- &data);
- if (ret < 0) {
- wpa_printf(MSG_DEBUG, "FT: Failed to parse "
- "RSN IE: %d", ret);
- return -1;
- }
- if (data.num_pmkid == 1 && data.pmkid)
- parse->rsn_pmkid = data.pmkid;
- break;
- case WLAN_EID_MOBILITY_DOMAIN:
- parse->mdie = pos + 2;
- parse->mdie_len = pos[1];
- break;
- case WLAN_EID_FAST_BSS_TRANSITION:
- if (pos[1] < sizeof(*ftie))
- return -1;
- ftie = (const struct rsn_ftie *) (pos + 2);
- prot_ie_count = ftie->mic_control[1];
- if (wpa_ft_parse_ftie(pos + 2, pos[1], parse) < 0)
- return -1;
- break;
- case WLAN_EID_RIC_DATA:
- if (parse->ric == NULL)
- parse->ric = pos;
- }
-
- pos += 2 + pos[1];
- }
-
- if (prot_ie_count == 0)
- return 0; /* no MIC */
-
- /*
- * Check that the protected IE count matches with IEs included in the
- * frame.
- */
- if (parse->rsn)
- prot_ie_count--;
- if (parse->mdie)
- prot_ie_count--;
- if (parse->ftie)
- prot_ie_count--;
- if (prot_ie_count < 0) {
- wpa_printf(MSG_DEBUG, "FT: Some required IEs not included in "
- "the protected IE count");
- return -1;
- }
-
- if (prot_ie_count == 0 && parse->ric) {
- wpa_printf(MSG_DEBUG, "FT: RIC IE(s) in the frame, but not "
- "included in protected IE count");
- return -1;
- }
-
- /* Determine the end of the RIC IE(s) */
- pos = parse->ric;
- while (pos && pos + 2 <= end && pos + 2 + pos[1] <= end &&
- prot_ie_count) {
- prot_ie_count--;
- pos += 2 + pos[1];
- }
- parse->ric_len = pos - parse->ric;
- if (prot_ie_count) {
- wpa_printf(MSG_DEBUG, "FT: %d protected IEs missing from "
- "frame", (int) prot_ie_count);
- return -1;
- }
-
- return 0;
-}
-
-
static inline int wpa_auth_set_key(struct wpa_authenticator *wpa_auth,
int vlan_id,
enum wpa_alg alg, const u8 *addr, int idx,
@@ -880,13 +754,9 @@ void wpa_ft_install_ptk(struct wpa_state_machine *sm)
int klen;
/* MLME-SETKEYS.request(PTK) */
- if (sm->pairwise == WPA_CIPHER_TKIP) {
- alg = WPA_ALG_TKIP;
- klen = 32;
- } else if (sm->pairwise == WPA_CIPHER_CCMP) {
- alg = WPA_ALG_CCMP;
- klen = 16;
- } else {
+ alg = wpa_cipher_to_alg(sm->pairwise);
+ klen = wpa_cipher_key_len(sm->pairwise);
+ if (!wpa_cipher_valid_pairwise(sm->pairwise)) {
wpa_printf(MSG_DEBUG, "FT: Unknown pairwise alg 0x%x - skip "
"PTK configuration", sm->pairwise);
return;
@@ -997,7 +867,7 @@ static u16 wpa_ft_process_auth_req(struct wpa_state_machine *sm,
sm->pmk_r1_name_valid = 1;
os_memcpy(sm->pmk_r1_name, pmk_r1_name, WPA_PMK_NAME_LEN);
- if (os_get_random(sm->ANonce, WPA_NONCE_LEN)) {
+ if (random_get_bytes(sm->ANonce, WPA_NONCE_LEN)) {
wpa_printf(MSG_DEBUG, "FT: Failed to get random data for "
"ANonce");
return WLAN_STATUS_UNSPECIFIED_FAILURE;
@@ -1008,7 +878,7 @@ static u16 wpa_ft_process_auth_req(struct wpa_state_machine *sm,
wpa_hexdump(MSG_DEBUG, "FT: Generated ANonce",
sm->ANonce, WPA_NONCE_LEN);
- ptk_len = pairwise != WPA_CIPHER_CCMP ? 64 : 48;
+ ptk_len = pairwise == WPA_CIPHER_TKIP ? 64 : 48;
wpa_pmk_r1_to_ptk(pmk_r1, sm->SNonce, sm->ANonce, sm->addr,
sm->wpa_auth->addr, pmk_r1_name,
(u8 *) &sm->PTK, ptk_len, ptk_name);
@@ -1204,7 +1074,7 @@ u16 wpa_ft_validate_reassoc(struct wpa_state_machine *sm, const u8 *ies,
count = 3;
if (parse.ric)
- count++;
+ count += ieee802_11_ie_count(parse.ric, parse.ric_len);
if (ftie->mic_control[1] != count) {
wpa_printf(MSG_DEBUG, "FT: Unexpected IE count in MIC "
"Control: received %u expected %u",
@@ -1224,8 +1094,16 @@ u16 wpa_ft_validate_reassoc(struct wpa_state_machine *sm, const u8 *ies,
if (os_memcmp(mic, ftie->mic, 16) != 0) {
wpa_printf(MSG_DEBUG, "FT: Invalid MIC in FTIE");
+ wpa_printf(MSG_DEBUG, "FT: addr=" MACSTR " auth_addr=" MACSTR,
+ MAC2STR(sm->addr), MAC2STR(sm->wpa_auth->addr));
wpa_hexdump(MSG_MSGDUMP, "FT: Received MIC", ftie->mic, 16);
wpa_hexdump(MSG_MSGDUMP, "FT: Calculated MIC", mic, 16);
+ wpa_hexdump(MSG_MSGDUMP, "FT: MDIE",
+ parse.mdie - 2, parse.mdie_len + 2);
+ wpa_hexdump(MSG_MSGDUMP, "FT: FTIE",
+ parse.ftie - 2, parse.ftie_len + 2);
+ wpa_hexdump(MSG_MSGDUMP, "FT: RSN",
+ parse.rsn - 2, parse.rsn_len + 2);
return WLAN_STATUS_INVALID_FTIE;
}
diff --git a/contrib/wpa/src/ap/wpa_auth_glue.c b/contrib/wpa/src/ap/wpa_auth_glue.c
index afa13a6..76c61ea 100644
--- a/contrib/wpa/src/ap/wpa_auth_glue.c
+++ b/contrib/wpa/src/ap/wpa_auth_glue.c
@@ -1,15 +1,9 @@
/*
* hostapd / WPA authenticator glue code
- * Copyright (c) 2002-2009, Jouni Malinen <j@w1.fi>
+ * Copyright (c) 2002-2011, Jouni Malinen <j@w1.fi>
*
- * This program is free software; you can redistribute it and/or modify
- * it under the terms of the GNU General Public License version 2 as
- * published by the Free Software Foundation.
- *
- * Alternatively, this software may be distributed under the terms of BSD
- * license.
- *
- * See README and COPYING for more details.
+ * This software may be distributed under the terms of the BSD license.
+ * See README for more details.
*/
#include "utils/includes.h"
@@ -29,17 +23,13 @@
#include "ap_drv_ops.h"
#include "ap_config.h"
#include "wpa_auth.h"
-
-
-#ifdef CONFIG_IEEE80211R
-static void hostapd_rrb_receive(void *ctx, const u8 *src_addr, const u8 *buf,
- size_t len);
-#endif /* CONFIG_IEEE80211R */
+#include "wpa_auth_glue.h"
static void hostapd_wpa_auth_conf(struct hostapd_bss_config *conf,
struct wpa_auth_config *wconf)
{
+ os_memset(wconf, 0, sizeof(*wconf));
wconf->wpa = conf->wpa;
wconf->wpa_key_mgmt = conf->wpa_key_mgmt;
wconf->wpa_pairwise = conf->wpa_pairwise;
@@ -54,6 +44,7 @@ static void hostapd_wpa_auth_conf(struct hostapd_bss_config *conf,
wconf->peerkey = conf->peerkey;
wconf->wmm_enabled = conf->wmm_enabled;
wconf->wmm_uapsd = conf->wmm_uapsd;
+ wconf->disable_pmksa_caching = conf->disable_pmksa_caching;
wconf->okc = conf->okc;
#ifdef CONFIG_IEEE80211W
wconf->ieee80211w = conf->ieee80211w;
@@ -77,7 +68,11 @@ static void hostapd_wpa_auth_conf(struct hostapd_bss_config *conf,
wconf->r0kh_list = conf->r0kh_list;
wconf->r1kh_list = conf->r1kh_list;
wconf->pmk_r1_push = conf->pmk_r1_push;
+ wconf->ft_over_ds = conf->ft_over_ds;
#endif /* CONFIG_IEEE80211R */
+#ifdef CONFIG_HS20
+ wconf->disable_gtk = conf->disable_dgaf;
+#endif /* CONFIG_HS20 */
}
@@ -117,10 +112,10 @@ static void hostapd_wpa_auth_disconnect(void *ctx, const u8 *addr,
}
-static void hostapd_wpa_auth_mic_failure_report(void *ctx, const u8 *addr)
+static int hostapd_wpa_auth_mic_failure_report(void *ctx, const u8 *addr)
{
struct hostapd_data *hapd = ctx;
- michael_mic_failure(hapd, addr, 0);
+ return michael_mic_failure(hapd, addr, 0);
}
@@ -188,7 +183,24 @@ static const u8 * hostapd_wpa_auth_get_psk(void *ctx, const u8 *addr,
const u8 *prev_psk)
{
struct hostapd_data *hapd = ctx;
- return hostapd_get_psk(hapd->conf, addr, prev_psk);
+ struct sta_info *sta = ap_get_sta(hapd, addr);
+ const u8 *psk = hostapd_get_psk(hapd->conf, addr, prev_psk);
+ /*
+ * This is about to iterate over all psks, prev_psk gives the last
+ * returned psk which should not be returned again.
+ * logic list (all hostapd_get_psk; all sta->psk)
+ */
+ if (sta && sta->psk && !psk) {
+ struct hostapd_sta_wpa_psk_short *pos;
+ psk = sta->psk->psk;
+ for (pos = sta->psk; pos; pos = pos->next) {
+ if (pos->psk == prev_psk) {
+ psk = pos->next ? pos->next->psk : NULL;
+ break;
+ }
+ }
+ }
+ return psk;
}
@@ -230,8 +242,8 @@ static int hostapd_wpa_auth_set_key(void *ctx, int vlan_id, enum wpa_alg alg,
return -1;
}
- return hapd->drv.set_key(ifname, hapd, alg, addr, idx, 1, NULL, 0,
- key, key_len);
+ return hostapd_drv_set_key(ifname, hapd, alg, addr, idx, 1, NULL, 0,
+ key, key_len);
}
@@ -248,7 +260,15 @@ static int hostapd_wpa_auth_send_eapol(void *ctx, const u8 *addr,
int encrypt)
{
struct hostapd_data *hapd = ctx;
- return hapd->drv.send_eapol(hapd, addr, data, data_len, encrypt);
+ struct sta_info *sta;
+ u32 flags = 0;
+
+ sta = ap_get_sta(hapd, addr);
+ if (sta)
+ flags = hostapd_sta_flags_to_drv(sta->flags);
+
+ return hostapd_drv_hapd_send_eapol(hapd, addr, data, data_len,
+ encrypt, flags);
}
@@ -291,12 +311,13 @@ static int hostapd_wpa_auth_for_each_auth(
{
struct hostapd_data *hapd = ctx;
struct wpa_auth_iface_iter_data data;
- if (hapd->iface->for_each_interface == NULL)
+ if (hapd->iface->interfaces == NULL ||
+ hapd->iface->interfaces->for_each_interface == NULL)
return -1;
data.cb = cb;
data.cb_ctx = cb_ctx;
- return hapd->iface->for_each_interface(hapd->iface->interfaces,
- wpa_auth_iface_iter, &data);
+ return hapd->iface->interfaces->for_each_interface(
+ hapd->iface->interfaces, wpa_auth_iface_iter, &data);
}
@@ -327,8 +348,9 @@ static int hostapd_wpa_auth_ft_iter(struct hostapd_iface *iface, void *ctx)
MAC2STR(idata->src_hapd->own_addr),
idata->src_hapd->conf->iface,
MAC2STR(hapd->own_addr), hapd->conf->iface);
- hostapd_rrb_receive(hapd, idata->src_hapd->own_addr,
- idata->data, idata->data_len);
+ wpa_ft_rrb_rx(hapd->wpa_auth,
+ idata->src_hapd->own_addr,
+ idata->data, idata->data_len);
return 1;
}
}
@@ -343,18 +365,21 @@ static int hostapd_wpa_auth_send_ether(void *ctx, const u8 *dst, u16 proto,
const u8 *data, size_t data_len)
{
struct hostapd_data *hapd = ctx;
+ struct l2_ethhdr *buf;
+ int ret;
#ifdef CONFIG_IEEE80211R
- if (proto == ETH_P_RRB && hapd->iface->for_each_interface) {
+ if (proto == ETH_P_RRB && hapd->iface->interfaces &&
+ hapd->iface->interfaces->for_each_interface) {
int res;
struct wpa_auth_ft_iface_iter_data idata;
idata.src_hapd = hapd;
idata.dst = dst;
idata.data = data;
idata.data_len = data_len;
- res = hapd->iface->for_each_interface(hapd->iface->interfaces,
- hostapd_wpa_auth_ft_iter,
- &idata);
+ res = hapd->iface->interfaces->for_each_interface(
+ hapd->iface->interfaces, hostapd_wpa_auth_ft_iter,
+ &idata);
if (res == 1)
return data_len;
}
@@ -366,7 +391,18 @@ static int hostapd_wpa_auth_send_ether(void *ctx, const u8 *dst, u16 proto,
data, data_len);
if (hapd->l2 == NULL)
return -1;
- return l2_packet_send(hapd->l2, dst, proto, data, data_len);
+
+ buf = os_malloc(sizeof(*buf) + data_len);
+ if (buf == NULL)
+ return -1;
+ os_memcpy(buf->h_dest, dst, ETH_ALEN);
+ os_memcpy(buf->h_source, hapd->own_addr, ETH_ALEN);
+ buf->h_proto = host_to_be16(proto);
+ os_memcpy(buf + 1, data, data_len);
+ ret = l2_packet_send(hapd->l2, dst, proto, (u8 *) buf,
+ sizeof(*buf) + data_len);
+ os_free(buf);
+ return ret;
}
@@ -396,7 +432,7 @@ static int hostapd_wpa_auth_send_ft_action(void *ctx, const u8 *dst,
os_memcpy(m->bssid, hapd->own_addr, ETH_ALEN);
os_memcpy(&m->u, data, data_len);
- res = hapd->drv.send_mgmt_frame(hapd, (u8 *) m, mlen);
+ res = hostapd_drv_send_mlme(hapd, (u8 *) m, mlen, 0);
os_free(m);
return res;
}
@@ -408,6 +444,9 @@ hostapd_wpa_auth_add_sta(void *ctx, const u8 *sta_addr)
struct hostapd_data *hapd = ctx;
struct sta_info *sta;
+ if (hostapd_add_sta_node(hapd, sta_addr, WLAN_AUTH_FT) < 0)
+ return NULL;
+
sta = ap_sta_add(hapd, sta_addr);
if (sta == NULL)
return NULL;
@@ -431,7 +470,22 @@ static void hostapd_rrb_receive(void *ctx, const u8 *src_addr, const u8 *buf,
size_t len)
{
struct hostapd_data *hapd = ctx;
- wpa_ft_rrb_rx(hapd->wpa_auth, src_addr, buf, len);
+ struct l2_ethhdr *ethhdr;
+ if (len < sizeof(*ethhdr))
+ return;
+ ethhdr = (struct l2_ethhdr *) buf;
+ wpa_printf(MSG_DEBUG, "FT: RRB received packet " MACSTR " -> "
+ MACSTR, MAC2STR(ethhdr->h_source), MAC2STR(ethhdr->h_dest));
+ wpa_ft_rrb_rx(hapd->wpa_auth, ethhdr->h_source, buf + sizeof(*ethhdr),
+ len - sizeof(*ethhdr));
+}
+
+
+static int hostapd_wpa_auth_add_tspec(void *ctx, const u8 *sta_addr,
+ u8 *tspec_ie, size_t tspec_ielen)
+{
+ struct hostapd_data *hapd = ctx;
+ return hostapd_add_tspec(hapd, sta_addr, tspec_ie, tspec_ielen);
}
#endif /* CONFIG_IEEE80211R */
@@ -445,6 +499,10 @@ int hostapd_setup_wpa(struct hostapd_data *hapd)
size_t wpa_ie_len;
hostapd_wpa_auth_conf(hapd->conf, &_conf);
+ if (hapd->iface->drv_flags & WPA_DRIVER_FLAGS_EAPOL_TX_STATUS)
+ _conf.tx_status = 1;
+ if (hapd->iface->drv_flags & WPA_DRIVER_FLAGS_AP_MLME)
+ _conf.ap_mlme = 1;
os_memset(&cb, 0, sizeof(cb));
cb.ctx = hapd;
cb.logger = hostapd_wpa_auth_logger;
@@ -463,6 +521,7 @@ int hostapd_setup_wpa(struct hostapd_data *hapd)
#ifdef CONFIG_IEEE80211R
cb.send_ft_action = hostapd_wpa_auth_send_ft_action;
cb.add_sta = hostapd_wpa_auth_add_sta;
+ cb.add_tspec = hostapd_wpa_auth_add_tspec;
#endif /* CONFIG_IEEE80211R */
hapd->wpa_auth = wpa_init(hapd->own_addr, &_conf, &cb);
if (hapd->wpa_auth == NULL) {
@@ -494,7 +553,7 @@ int hostapd_setup_wpa(struct hostapd_data *hapd)
hapd->l2 = l2_packet_init(hapd->conf->bridge[0] ?
hapd->conf->bridge :
hapd->conf->iface, NULL, ETH_P_RRB,
- hostapd_rrb_receive, hapd, 0);
+ hostapd_rrb_receive, hapd, 1);
if (hapd->l2 == NULL &&
(hapd->driver == NULL ||
hapd->driver->send_ether == NULL)) {
@@ -520,6 +579,7 @@ void hostapd_reconfig_wpa(struct hostapd_data *hapd)
void hostapd_deinit_wpa(struct hostapd_data *hapd)
{
+ ieee80211_tkip_countermeasures_deinit(hapd);
rsn_preauth_iface_deinit(hapd);
if (hapd->wpa_auth) {
wpa_deinit(hapd->wpa_auth);
diff --git a/contrib/wpa/src/ap/wpa_auth_glue.h b/contrib/wpa/src/ap/wpa_auth_glue.h
index 79d7e05..1b13ae7 100644
--- a/contrib/wpa/src/ap/wpa_auth_glue.h
+++ b/contrib/wpa/src/ap/wpa_auth_glue.h
@@ -2,14 +2,8 @@
* hostapd / WPA authenticator glue code
* Copyright (c) 2002-2009, Jouni Malinen <j@w1.fi>
*
- * This program is free software; you can redistribute it and/or modify
- * it under the terms of the GNU General Public License version 2 as
- * published by the Free Software Foundation.
- *
- * Alternatively, this software may be distributed under the terms of BSD
- * license.
- *
- * See README and COPYING for more details.
+ * This software may be distributed under the terms of the BSD license.
+ * See README for more details.
*/
#ifndef WPA_AUTH_GLUE_H
diff --git a/contrib/wpa/src/ap/wpa_auth_i.h b/contrib/wpa/src/ap/wpa_auth_i.h
index b69129f..97489d3 100644
--- a/contrib/wpa/src/ap/wpa_auth_i.h
+++ b/contrib/wpa/src/ap/wpa_auth_i.h
@@ -2,14 +2,8 @@
* hostapd - IEEE 802.11i-2004 / WPA Authenticator: Internal definitions
* Copyright (c) 2004-2007, Jouni Malinen <j@w1.fi>
*
- * This program is free software; you can redistribute it and/or modify
- * it under the terms of the GNU General Public License version 2 as
- * published by the Free Software Foundation.
- *
- * Alternatively, this software may be distributed under the terms of BSD
- * license.
- *
- * See README and COPYING for more details.
+ * This software may be distributed under the terms of the BSD license.
+ * See README for more details.
*/
#ifndef WPA_AUTH_I_H
@@ -69,10 +63,11 @@ struct wpa_state_machine {
Boolean pairwise_set;
int keycount;
Boolean Pair;
- struct {
+ struct wpa_key_replay_counter {
u8 counter[WPA_REPLAY_COUNTER_LEN];
Boolean valid;
- } key_replay[RSNA_MAX_EAPOL_RETRIES];
+ } key_replay[RSNA_MAX_EAPOL_RETRIES],
+ prev_key_replay[RSNA_MAX_EAPOL_RETRIES];
Boolean PInitAKeys; /* WPA only, not in IEEE 802.11i */
Boolean PTKRequest; /* not in IEEE 802.11i state machine */
Boolean has_GTK;
@@ -86,10 +81,13 @@ struct wpa_state_machine {
unsigned int pending_deinit:1;
unsigned int started:1;
unsigned int mgmt_frame_prot:1;
+ unsigned int rx_eapol_key_secure:1;
+ unsigned int update_snonce:1;
#ifdef CONFIG_IEEE80211R
unsigned int ft_completed:1;
unsigned int pmk_r1_name_valid:1;
#endif /* CONFIG_IEEE80211R */
+ unsigned int is_wnmsleep:1;
u8 req_replay_counter[WPA_REPLAY_COUNTER_LEN];
int req_replay_counter_used;
@@ -120,6 +118,8 @@ struct wpa_state_machine {
* message 2/4 */
u8 *assoc_resp_ftie;
#endif /* CONFIG_IEEE80211R */
+
+ int pending_1_of_4_timeout;
};
@@ -145,6 +145,8 @@ struct wpa_group {
u8 GTK[2][WPA_GTK_MAX_LEN];
u8 GNonce[WPA_NONCE_LEN];
Boolean changed;
+ Boolean first_sta_seen;
+ Boolean reject_4way_hs_for_entropy;
#ifdef CONFIG_IEEE80211W
u8 IGTK[2][WPA_IGTK_LEN];
int GN_igtk, GM_igtk;
diff --git a/contrib/wpa/src/ap/wpa_auth_ie.c b/contrib/wpa/src/ap/wpa_auth_ie.c
index f8a1804..4fd0135 100644
--- a/contrib/wpa/src/ap/wpa_auth_ie.c
+++ b/contrib/wpa/src/ap/wpa_auth_ie.c
@@ -2,14 +2,8 @@
* hostapd - WPA/RSN IE and KDE definitions
* Copyright (c) 2004-2008, Jouni Malinen <j@w1.fi>
*
- * This program is free software; you can redistribute it and/or modify
- * it under the terms of the GNU General Public License version 2 as
- * published by the Free Software Foundation.
- *
- * Alternatively, this software may be distributed under the terms of BSD
- * license.
- *
- * See README and COPYING for more details.
+ * This software may be distributed under the terms of the BSD license.
+ * See README for more details.
*/
#include "utils/includes.h"
@@ -25,11 +19,17 @@
#include "wpa_auth_i.h"
+#ifdef CONFIG_RSN_TESTING
+int rsn_testing = 0;
+#endif /* CONFIG_RSN_TESTING */
+
+
static int wpa_write_wpa_ie(struct wpa_auth_config *conf, u8 *buf, size_t len)
{
struct wpa_ie_hdr *hdr;
int num_suites;
u8 *pos, *count;
+ u32 suite;
hdr = (struct wpa_ie_hdr *) buf;
hdr->elem_id = WLAN_EID_VENDOR_SPECIFIC;
@@ -37,46 +37,25 @@ static int wpa_write_wpa_ie(struct wpa_auth_config *conf, u8 *buf, size_t len)
WPA_PUT_LE16(hdr->version, WPA_VERSION);
pos = (u8 *) (hdr + 1);
- if (conf->wpa_group == WPA_CIPHER_CCMP) {
- RSN_SELECTOR_PUT(pos, WPA_CIPHER_SUITE_CCMP);
- } else if (conf->wpa_group == WPA_CIPHER_TKIP) {
- RSN_SELECTOR_PUT(pos, WPA_CIPHER_SUITE_TKIP);
- } else if (conf->wpa_group == WPA_CIPHER_WEP104) {
- RSN_SELECTOR_PUT(pos, WPA_CIPHER_SUITE_WEP104);
- } else if (conf->wpa_group == WPA_CIPHER_WEP40) {
- RSN_SELECTOR_PUT(pos, WPA_CIPHER_SUITE_WEP40);
- } else {
+ suite = wpa_cipher_to_suite(WPA_PROTO_WPA, conf->wpa_group);
+ if (suite == 0) {
wpa_printf(MSG_DEBUG, "Invalid group cipher (%d).",
conf->wpa_group);
return -1;
}
+ RSN_SELECTOR_PUT(pos, suite);
pos += WPA_SELECTOR_LEN;
- num_suites = 0;
count = pos;
pos += 2;
- if (conf->wpa_pairwise & WPA_CIPHER_CCMP) {
- RSN_SELECTOR_PUT(pos, WPA_CIPHER_SUITE_CCMP);
- pos += WPA_SELECTOR_LEN;
- num_suites++;
- }
- if (conf->wpa_pairwise & WPA_CIPHER_TKIP) {
- RSN_SELECTOR_PUT(pos, WPA_CIPHER_SUITE_TKIP);
- pos += WPA_SELECTOR_LEN;
- num_suites++;
- }
- if (conf->wpa_pairwise & WPA_CIPHER_NONE) {
- RSN_SELECTOR_PUT(pos, WPA_CIPHER_SUITE_NONE);
- pos += WPA_SELECTOR_LEN;
- num_suites++;
- }
-
+ num_suites = wpa_cipher_put_suites(pos, conf->wpa_pairwise);
if (num_suites == 0) {
wpa_printf(MSG_DEBUG, "Invalid pairwise cipher (%d).",
conf->wpa_pairwise);
return -1;
}
+ pos += num_suites * WPA_SELECTOR_LEN;
WPA_PUT_LE16(count, num_suites);
num_suites = 0;
@@ -113,49 +92,48 @@ int wpa_write_rsn_ie(struct wpa_auth_config *conf, u8 *buf, size_t len,
const u8 *pmkid)
{
struct rsn_ie_hdr *hdr;
- int num_suites;
+ int num_suites, res;
u8 *pos, *count;
u16 capab;
+ u32 suite;
hdr = (struct rsn_ie_hdr *) buf;
hdr->elem_id = WLAN_EID_RSN;
WPA_PUT_LE16(hdr->version, RSN_VERSION);
pos = (u8 *) (hdr + 1);
- if (conf->wpa_group == WPA_CIPHER_CCMP) {
- RSN_SELECTOR_PUT(pos, RSN_CIPHER_SUITE_CCMP);
- } else if (conf->wpa_group == WPA_CIPHER_TKIP) {
- RSN_SELECTOR_PUT(pos, RSN_CIPHER_SUITE_TKIP);
- } else if (conf->wpa_group == WPA_CIPHER_WEP104) {
- RSN_SELECTOR_PUT(pos, RSN_CIPHER_SUITE_WEP104);
- } else if (conf->wpa_group == WPA_CIPHER_WEP40) {
- RSN_SELECTOR_PUT(pos, RSN_CIPHER_SUITE_WEP40);
- } else {
+ suite = wpa_cipher_to_suite(WPA_PROTO_RSN, conf->wpa_group);
+ if (suite == 0) {
wpa_printf(MSG_DEBUG, "Invalid group cipher (%d).",
conf->wpa_group);
return -1;
}
+ RSN_SELECTOR_PUT(pos, suite);
pos += RSN_SELECTOR_LEN;
num_suites = 0;
count = pos;
pos += 2;
- if (conf->rsn_pairwise & WPA_CIPHER_CCMP) {
- RSN_SELECTOR_PUT(pos, RSN_CIPHER_SUITE_CCMP);
- pos += RSN_SELECTOR_LEN;
- num_suites++;
- }
- if (conf->rsn_pairwise & WPA_CIPHER_TKIP) {
- RSN_SELECTOR_PUT(pos, RSN_CIPHER_SUITE_TKIP);
+#ifdef CONFIG_RSN_TESTING
+ if (rsn_testing) {
+ RSN_SELECTOR_PUT(pos, RSN_SELECTOR(0x12, 0x34, 0x56, 1));
pos += RSN_SELECTOR_LEN;
num_suites++;
}
- if (conf->rsn_pairwise & WPA_CIPHER_NONE) {
- RSN_SELECTOR_PUT(pos, RSN_CIPHER_SUITE_NONE);
+#endif /* CONFIG_RSN_TESTING */
+
+ res = rsn_cipher_put_suites(pos, conf->rsn_pairwise);
+ num_suites += res;
+ pos += res * RSN_SELECTOR_LEN;
+
+#ifdef CONFIG_RSN_TESTING
+ if (rsn_testing) {
+ RSN_SELECTOR_PUT(pos, RSN_SELECTOR(0x12, 0x34, 0x56, 2));
pos += RSN_SELECTOR_LEN;
num_suites++;
}
+#endif /* CONFIG_RSN_TESTING */
if (num_suites == 0) {
wpa_printf(MSG_DEBUG, "Invalid pairwise cipher (%d).",
@@ -168,6 +146,14 @@ int wpa_write_rsn_ie(struct wpa_auth_config *conf, u8 *buf, size_t len,
count = pos;
pos += 2;
+#ifdef CONFIG_RSN_TESTING
+ if (rsn_testing) {
+ RSN_SELECTOR_PUT(pos, RSN_SELECTOR(0x12, 0x34, 0x56, 1));
+ pos += RSN_SELECTOR_LEN;
+ num_suites++;
+ }
+#endif /* CONFIG_RSN_TESTING */
+
if (conf->wpa_key_mgmt & WPA_KEY_MGMT_IEEE8021X) {
RSN_SELECTOR_PUT(pos, RSN_AUTH_KEY_MGMT_UNSPEC_802_1X);
pos += RSN_SELECTOR_LEN;
@@ -202,6 +188,26 @@ int wpa_write_rsn_ie(struct wpa_auth_config *conf, u8 *buf, size_t len,
num_suites++;
}
#endif /* CONFIG_IEEE80211W */
+#ifdef CONFIG_SAE
+ if (conf->wpa_key_mgmt & WPA_KEY_MGMT_SAE) {
+ RSN_SELECTOR_PUT(pos, RSN_AUTH_KEY_MGMT_SAE);
+ pos += RSN_SELECTOR_LEN;
+ num_suites++;
+ }
+ if (conf->wpa_key_mgmt & WPA_KEY_MGMT_FT_SAE) {
+ RSN_SELECTOR_PUT(pos, RSN_AUTH_KEY_MGMT_FT_SAE);
+ pos += RSN_SELECTOR_LEN;
+ num_suites++;
+ }
+#endif /* CONFIG_SAE */
+
+#ifdef CONFIG_RSN_TESTING
+ if (rsn_testing) {
+ RSN_SELECTOR_PUT(pos, RSN_SELECTOR(0x12, 0x34, 0x56, 2));
+ pos += RSN_SELECTOR_LEN;
+ num_suites++;
+ }
+#endif /* CONFIG_RSN_TESTING */
if (num_suites == 0) {
wpa_printf(MSG_DEBUG, "Invalid key management type (%d).",
@@ -227,6 +233,10 @@ int wpa_write_rsn_ie(struct wpa_auth_config *conf, u8 *buf, size_t len,
capab |= WPA_CAPABILITY_MFPR;
}
#endif /* CONFIG_IEEE80211W */
+#ifdef CONFIG_RSN_TESTING
+ if (rsn_testing)
+ capab |= BIT(8) | BIT(14) | BIT(15);
+#endif /* CONFIG_RSN_TESTING */
WPA_PUT_LE16(pos, capab);
pos += 2;
@@ -256,6 +266,29 @@ int wpa_write_rsn_ie(struct wpa_auth_config *conf, u8 *buf, size_t len,
}
#endif /* CONFIG_IEEE80211W */
+#ifdef CONFIG_RSN_TESTING
+ if (rsn_testing) {
+ /*
+ * Fill in any defined fields and add extra data to the end of
+ * the element.
+ */
+ int pmkid_count_set = pmkid != NULL;
+ if (conf->ieee80211w != NO_MGMT_FRAME_PROTECTION)
+ pmkid_count_set = 1;
+ /* PMKID Count */
+ WPA_PUT_LE16(pos, 0);
+ pos += 2;
+ if (conf->ieee80211w == NO_MGMT_FRAME_PROTECTION) {
+ /* Management Group Cipher Suite */
+ RSN_SELECTOR_PUT(pos, RSN_CIPHER_SUITE_AES_128_CMAC);
+ pos += RSN_SELECTOR_LEN;
+ }
+
+ os_memset(pos, 0x12, 17);
+ pos += 17;
+ }
+#endif /* CONFIG_RSN_TESTING */
+
hdr->len = (pos - buf) - 2;
return pos - buf;
@@ -277,8 +310,7 @@ int wpa_auth_gen_wpa_ie(struct wpa_authenticator *wpa_auth)
pos += res;
}
#ifdef CONFIG_IEEE80211R
- if (wpa_auth->conf.wpa_key_mgmt &
- (WPA_KEY_MGMT_FT_IEEE8021X | WPA_KEY_MGMT_FT_PSK)) {
+ if (wpa_key_mgmt_ft(wpa_auth->conf.wpa_key_mgmt)) {
res = wpa_write_mdie(&wpa_auth->conf, pos,
buf + sizeof(buf) - pos);
if (res < 0)
@@ -322,114 +354,6 @@ u8 * wpa_add_kde(u8 *pos, u32 kde, const u8 *data, size_t data_len,
}
-static int wpa_selector_to_bitfield(const u8 *s)
-{
- if (RSN_SELECTOR_GET(s) == WPA_CIPHER_SUITE_NONE)
- return WPA_CIPHER_NONE;
- if (RSN_SELECTOR_GET(s) == WPA_CIPHER_SUITE_WEP40)
- return WPA_CIPHER_WEP40;
- if (RSN_SELECTOR_GET(s) == WPA_CIPHER_SUITE_TKIP)
- return WPA_CIPHER_TKIP;
- if (RSN_SELECTOR_GET(s) == WPA_CIPHER_SUITE_CCMP)
- return WPA_CIPHER_CCMP;
- if (RSN_SELECTOR_GET(s) == WPA_CIPHER_SUITE_WEP104)
- return WPA_CIPHER_WEP104;
- return 0;
-}
-
-
-static int wpa_key_mgmt_to_bitfield(const u8 *s)
-{
- if (RSN_SELECTOR_GET(s) == WPA_AUTH_KEY_MGMT_UNSPEC_802_1X)
- return WPA_KEY_MGMT_IEEE8021X;
- if (RSN_SELECTOR_GET(s) == WPA_AUTH_KEY_MGMT_PSK_OVER_802_1X)
- return WPA_KEY_MGMT_PSK;
- if (RSN_SELECTOR_GET(s) == WPA_AUTH_KEY_MGMT_NONE)
- return WPA_KEY_MGMT_WPA_NONE;
- return 0;
-}
-
-
-static int wpa_parse_wpa_ie_wpa(const u8 *wpa_ie, size_t wpa_ie_len,
- struct wpa_ie_data *data)
-{
- const struct wpa_ie_hdr *hdr;
- const u8 *pos;
- int left;
- int i, count;
-
- os_memset(data, 0, sizeof(*data));
- data->pairwise_cipher = WPA_CIPHER_TKIP;
- data->group_cipher = WPA_CIPHER_TKIP;
- data->key_mgmt = WPA_KEY_MGMT_IEEE8021X;
- data->mgmt_group_cipher = 0;
-
- if (wpa_ie_len < sizeof(struct wpa_ie_hdr))
- return -1;
-
- hdr = (const struct wpa_ie_hdr *) wpa_ie;
-
- if (hdr->elem_id != WLAN_EID_VENDOR_SPECIFIC ||
- hdr->len != wpa_ie_len - 2 ||
- RSN_SELECTOR_GET(hdr->oui) != WPA_OUI_TYPE ||
- WPA_GET_LE16(hdr->version) != WPA_VERSION) {
- return -2;
- }
-
- pos = (const u8 *) (hdr + 1);
- left = wpa_ie_len - sizeof(*hdr);
-
- if (left >= WPA_SELECTOR_LEN) {
- data->group_cipher = wpa_selector_to_bitfield(pos);
- pos += WPA_SELECTOR_LEN;
- left -= WPA_SELECTOR_LEN;
- } else if (left > 0)
- return -3;
-
- if (left >= 2) {
- data->pairwise_cipher = 0;
- count = WPA_GET_LE16(pos);
- pos += 2;
- left -= 2;
- if (count == 0 || left < count * WPA_SELECTOR_LEN)
- return -4;
- for (i = 0; i < count; i++) {
- data->pairwise_cipher |= wpa_selector_to_bitfield(pos);
- pos += WPA_SELECTOR_LEN;
- left -= WPA_SELECTOR_LEN;
- }
- } else if (left == 1)
- return -5;
-
- if (left >= 2) {
- data->key_mgmt = 0;
- count = WPA_GET_LE16(pos);
- pos += 2;
- left -= 2;
- if (count == 0 || left < count * WPA_SELECTOR_LEN)
- return -6;
- for (i = 0; i < count; i++) {
- data->key_mgmt |= wpa_key_mgmt_to_bitfield(pos);
- pos += WPA_SELECTOR_LEN;
- left -= WPA_SELECTOR_LEN;
- }
- } else if (left == 1)
- return -7;
-
- if (left >= 2) {
- data->capabilities = WPA_GET_LE16(pos);
- pos += 2;
- left -= 2;
- }
-
- if (left > 0) {
- return -8;
- }
-
- return 0;
-}
-
-
struct wpa_auth_okc_iter_data {
struct rsn_pmksa_cache_entry *pmksa;
const u8 *aa;
@@ -495,36 +419,28 @@ int wpa_validate_wpa_ie(struct wpa_authenticator *wpa_auth,
else if (data.key_mgmt & WPA_KEY_MGMT_PSK_SHA256)
selector = RSN_AUTH_KEY_MGMT_PSK_SHA256;
#endif /* CONFIG_IEEE80211W */
+#ifdef CONFIG_SAE
+ else if (data.key_mgmt & WPA_KEY_MGMT_SAE)
+ selector = RSN_AUTH_KEY_MGMT_SAE;
+ else if (data.key_mgmt & WPA_KEY_MGMT_FT_SAE)
+ selector = RSN_AUTH_KEY_MGMT_FT_SAE;
+#endif /* CONFIG_SAE */
else if (data.key_mgmt & WPA_KEY_MGMT_IEEE8021X)
selector = RSN_AUTH_KEY_MGMT_UNSPEC_802_1X;
else if (data.key_mgmt & WPA_KEY_MGMT_PSK)
selector = RSN_AUTH_KEY_MGMT_PSK_OVER_802_1X;
wpa_auth->dot11RSNAAuthenticationSuiteSelected = selector;
- selector = RSN_CIPHER_SUITE_CCMP;
- if (data.pairwise_cipher & WPA_CIPHER_CCMP)
+ selector = wpa_cipher_to_suite(WPA_PROTO_RSN,
+ data.pairwise_cipher);
+ if (!selector)
selector = RSN_CIPHER_SUITE_CCMP;
- else if (data.pairwise_cipher & WPA_CIPHER_TKIP)
- selector = RSN_CIPHER_SUITE_TKIP;
- else if (data.pairwise_cipher & WPA_CIPHER_WEP104)
- selector = RSN_CIPHER_SUITE_WEP104;
- else if (data.pairwise_cipher & WPA_CIPHER_WEP40)
- selector = RSN_CIPHER_SUITE_WEP40;
- else if (data.pairwise_cipher & WPA_CIPHER_NONE)
- selector = RSN_CIPHER_SUITE_NONE;
wpa_auth->dot11RSNAPairwiseCipherSelected = selector;
- selector = RSN_CIPHER_SUITE_CCMP;
- if (data.group_cipher & WPA_CIPHER_CCMP)
+ selector = wpa_cipher_to_suite(WPA_PROTO_RSN,
+ data.group_cipher);
+ if (!selector)
selector = RSN_CIPHER_SUITE_CCMP;
- else if (data.group_cipher & WPA_CIPHER_TKIP)
- selector = RSN_CIPHER_SUITE_TKIP;
- else if (data.group_cipher & WPA_CIPHER_WEP104)
- selector = RSN_CIPHER_SUITE_WEP104;
- else if (data.group_cipher & WPA_CIPHER_WEP40)
- selector = RSN_CIPHER_SUITE_WEP40;
- else if (data.group_cipher & WPA_CIPHER_NONE)
- selector = RSN_CIPHER_SUITE_NONE;
wpa_auth->dot11RSNAGroupCipherSelected = selector;
} else {
res = wpa_parse_wpa_ie_wpa(wpa_ie, wpa_ie_len, &data);
@@ -536,30 +452,16 @@ int wpa_validate_wpa_ie(struct wpa_authenticator *wpa_auth,
selector = WPA_AUTH_KEY_MGMT_PSK_OVER_802_1X;
wpa_auth->dot11RSNAAuthenticationSuiteSelected = selector;
- selector = WPA_CIPHER_SUITE_TKIP;
- if (data.pairwise_cipher & WPA_CIPHER_CCMP)
- selector = WPA_CIPHER_SUITE_CCMP;
- else if (data.pairwise_cipher & WPA_CIPHER_TKIP)
- selector = WPA_CIPHER_SUITE_TKIP;
- else if (data.pairwise_cipher & WPA_CIPHER_WEP104)
- selector = WPA_CIPHER_SUITE_WEP104;
- else if (data.pairwise_cipher & WPA_CIPHER_WEP40)
- selector = WPA_CIPHER_SUITE_WEP40;
- else if (data.pairwise_cipher & WPA_CIPHER_NONE)
- selector = WPA_CIPHER_SUITE_NONE;
+ selector = wpa_cipher_to_suite(WPA_PROTO_WPA,
+ data.pairwise_cipher);
+ if (!selector)
+ selector = RSN_CIPHER_SUITE_TKIP;
wpa_auth->dot11RSNAPairwiseCipherSelected = selector;
- selector = WPA_CIPHER_SUITE_TKIP;
- if (data.group_cipher & WPA_CIPHER_CCMP)
- selector = WPA_CIPHER_SUITE_CCMP;
- else if (data.group_cipher & WPA_CIPHER_TKIP)
+ selector = wpa_cipher_to_suite(WPA_PROTO_WPA,
+ data.group_cipher);
+ if (!selector)
selector = WPA_CIPHER_SUITE_TKIP;
- else if (data.group_cipher & WPA_CIPHER_WEP104)
- selector = WPA_CIPHER_SUITE_WEP104;
- else if (data.group_cipher & WPA_CIPHER_WEP40)
- selector = WPA_CIPHER_SUITE_WEP40;
- else if (data.group_cipher & WPA_CIPHER_NONE)
- selector = WPA_CIPHER_SUITE_NONE;
wpa_auth->dot11RSNAGroupCipherSelected = selector;
}
if (res) {
@@ -595,6 +497,12 @@ int wpa_validate_wpa_ie(struct wpa_authenticator *wpa_auth,
else if (key_mgmt & WPA_KEY_MGMT_PSK_SHA256)
sm->wpa_key_mgmt = WPA_KEY_MGMT_PSK_SHA256;
#endif /* CONFIG_IEEE80211W */
+#ifdef CONFIG_SAE
+ else if (key_mgmt & WPA_KEY_MGMT_SAE)
+ sm->wpa_key_mgmt = WPA_KEY_MGMT_SAE;
+ else if (key_mgmt & WPA_KEY_MGMT_FT_SAE)
+ sm->wpa_key_mgmt = WPA_KEY_MGMT_FT_SAE;
+#endif /* CONFIG_SAE */
else if (key_mgmt & WPA_KEY_MGMT_IEEE8021X)
sm->wpa_key_mgmt = WPA_KEY_MGMT_IEEE8021X;
else
@@ -658,6 +566,8 @@ int wpa_validate_wpa_ie(struct wpa_authenticator *wpa_auth,
if (ciphers & WPA_CIPHER_CCMP)
sm->pairwise = WPA_CIPHER_CCMP;
+ else if (ciphers & WPA_CIPHER_GCMP)
+ sm->pairwise = WPA_CIPHER_GCMP;
else
sm->pairwise = WPA_CIPHER_TKIP;
diff --git a/contrib/wpa/src/ap/wpa_auth_ie.h b/contrib/wpa/src/ap/wpa_auth_ie.h
index 61d4cb4..4999139 100644
--- a/contrib/wpa/src/ap/wpa_auth_ie.h
+++ b/contrib/wpa/src/ap/wpa_auth_ie.h
@@ -2,14 +2,8 @@
* hostapd - WPA/RSN IE and KDE definitions
* Copyright (c) 2004-2007, Jouni Malinen <j@w1.fi>
*
- * This program is free software; you can redistribute it and/or modify
- * it under the terms of the GNU General Public License version 2 as
- * published by the Free Software Foundation.
- *
- * Alternatively, this software may be distributed under the terms of BSD
- * license.
- *
- * See README and COPYING for more details.
+ * This software may be distributed under the terms of the BSD license.
+ * See README for more details.
*/
#ifndef WPA_AUTH_IE_H
diff --git a/contrib/wpa/src/ap/wps_hostapd.c b/contrib/wpa/src/ap/wps_hostapd.c
index a6ffd4d..5ce4f1b 100644
--- a/contrib/wpa/src/ap/wps_hostapd.c
+++ b/contrib/wpa/src/ap/wps_hostapd.c
@@ -1,15 +1,9 @@
/*
* hostapd / WPS integration
- * Copyright (c) 2008-2010, Jouni Malinen <j@w1.fi>
+ * Copyright (c) 2008-2012, Jouni Malinen <j@w1.fi>
*
- * This program is free software; you can redistribute it and/or modify
- * it under the terms of the GNU General Public License version 2 as
- * published by the Free Software Foundation.
- *
- * Alternatively, this software may be distributed under the terms of BSD
- * license.
- *
- * See README and COPYING for more details.
+ * This software may be distributed under the terms of the BSD license.
+ * See README for more details.
*/
#include "utils/includes.h"
@@ -17,7 +11,6 @@
#include "utils/common.h"
#include "utils/eloop.h"
#include "utils/uuid.h"
-#include "crypto/dh_groups.h"
#include "common/wpa_ctrl.h"
#include "common/ieee802_11_defs.h"
#include "common/ieee802_11_common.h"
@@ -26,8 +19,10 @@
#include "wps/wps.h"
#include "wps/wps_defs.h"
#include "wps/wps_dev_attr.h"
+#include "wps/wps_attr_parse.h"
#include "hostapd.h"
#include "ap_config.h"
+#include "ap_drv_ops.h"
#include "beacon.h"
#include "sta_info.h"
#include "wps_hostapd.h"
@@ -40,11 +35,53 @@ static int hostapd_wps_upnp_init(struct hostapd_data *hapd,
static void hostapd_wps_upnp_deinit(struct hostapd_data *hapd);
#endif /* CONFIG_WPS_UPNP */
-static int hostapd_wps_probe_req_rx(void *ctx, const u8 *addr,
- const u8 *ie, size_t ie_len);
+static int hostapd_wps_probe_req_rx(void *ctx, const u8 *addr, const u8 *da,
+ const u8 *bssid,
+ const u8 *ie, size_t ie_len,
+ int ssi_signal);
static void hostapd_wps_ap_pin_timeout(void *eloop_data, void *user_ctx);
+struct wps_for_each_data {
+ int (*func)(struct hostapd_data *h, void *ctx);
+ void *ctx;
+};
+
+
+static int wps_for_each(struct hostapd_iface *iface, void *ctx)
+{
+ struct wps_for_each_data *data = ctx;
+ size_t j;
+
+ if (iface == NULL)
+ return 0;
+ for (j = 0; j < iface->num_bss; j++) {
+ struct hostapd_data *hapd = iface->bss[j];
+ int ret = data->func(hapd, data->ctx);
+ if (ret)
+ return ret;
+ }
+
+ return 0;
+}
+
+
+static int hostapd_wps_for_each(struct hostapd_data *hapd,
+ int (*func)(struct hostapd_data *h, void *ctx),
+ void *ctx)
+{
+ struct hostapd_iface *iface = hapd->iface;
+ struct wps_for_each_data data;
+ data.func = func;
+ data.ctx = ctx;
+ if (iface->interfaces == NULL ||
+ iface->interfaces->for_each_interface == NULL)
+ return wps_for_each(iface, &data);
+ return iface->interfaces->for_each_interface(iface->interfaces,
+ wps_for_each, &data);
+}
+
+
static int hostapd_wps_new_psk_cb(void *ctx, const u8 *mac_addr, const u8 *psk,
size_t psk_len)
{
@@ -100,8 +137,9 @@ static int hostapd_wps_set_ie_cb(void *ctx, struct wpabuf *beacon_ie,
hapd->wps_beacon_ie = beacon_ie;
wpabuf_free(hapd->wps_probe_resp_ie);
hapd->wps_probe_resp_ie = probe_resp_ie;
- ieee802_11_set_beacon(hapd);
- return hapd->drv.set_ap_wps_ie(hapd);
+ if (hapd->beacon_set_done)
+ ieee802_11_set_beacon(hapd);
+ return hostapd_set_ap_wps_ie(hapd);
}
@@ -144,11 +182,30 @@ static void hostapd_wps_pin_needed_cb(void *ctx, const u8 *uuid_e,
}
+struct wps_stop_reg_data {
+ struct hostapd_data *current_hapd;
+ const u8 *uuid_e;
+ const u8 *dev_pw;
+ size_t dev_pw_len;
+};
+
+static int wps_stop_registrar(struct hostapd_data *hapd, void *ctx)
+{
+ struct wps_stop_reg_data *data = ctx;
+ if (hapd != data->current_hapd && hapd->wps != NULL)
+ wps_registrar_complete(hapd->wps->registrar, data->uuid_e,
+ data->dev_pw, data->dev_pw_len);
+ return 0;
+}
+
+
static void hostapd_wps_reg_success_cb(void *ctx, const u8 *mac_addr,
- const u8 *uuid_e)
+ const u8 *uuid_e, const u8 *dev_pw,
+ size_t dev_pw_len)
{
struct hostapd_data *hapd = ctx;
char uuid[40];
+ struct wps_stop_reg_data data;
if (uuid_bin2str(uuid_e, uuid, sizeof(uuid)))
return;
wpa_msg(hapd->msg_ctx, MSG_INFO, WPS_EVENT_REG_SUCCESS MACSTR " %s",
@@ -156,6 +213,11 @@ static void hostapd_wps_reg_success_cb(void *ctx, const u8 *mac_addr,
if (hapd->wps_reg_success_cb)
hapd->wps_reg_success_cb(hapd->wps_reg_success_cb_ctx,
mac_addr, uuid_e);
+ data.current_hapd = hapd;
+ data.uuid_e = uuid_e;
+ data.dev_pw = dev_pw;
+ data.dev_pw_len = dev_pw_len;
+ hostapd_wps_for_each(hapd, wps_stop_registrar, &data);
}
@@ -193,16 +255,31 @@ static void wps_reload_config(void *eloop_data, void *user_ctx)
struct hostapd_iface *iface = eloop_data;
wpa_printf(MSG_DEBUG, "WPS: Reload configuration data");
- if (iface->reload_config(iface) < 0) {
+ if (iface->interfaces == NULL ||
+ iface->interfaces->reload_config(iface) < 0) {
wpa_printf(MSG_WARNING, "WPS: Failed to reload the updated "
"configuration");
}
}
-static int hostapd_wps_cred_cb(void *ctx, const struct wps_credential *cred)
+static void hapd_new_ap_event(struct hostapd_data *hapd, const u8 *attr,
+ size_t attr_len)
{
- struct hostapd_data *hapd = ctx;
+ size_t blen = attr_len * 2 + 1;
+ char *buf = os_malloc(blen);
+ if (buf) {
+ wpa_snprintf_hex(buf, blen, attr, attr_len);
+ wpa_msg(hapd->msg_ctx, MSG_INFO,
+ WPS_EVENT_NEW_AP_SETTINGS "%s", buf);
+ os_free(buf);
+ }
+}
+
+
+static int hapd_wps_cred_cb(struct hostapd_data *hapd, void *ctx)
+{
+ const struct wps_credential *cred = ctx;
FILE *oconf, *nconf;
size_t len, i;
char *tmp_fname;
@@ -210,6 +287,9 @@ static int hostapd_wps_cred_cb(void *ctx, const struct wps_credential *cred)
int multi_bss;
int wpa;
+ if (hapd->wps == NULL)
+ return 0;
+
wpa_hexdump_key(MSG_DEBUG, "WPS: Received Credential attribute",
cred->cred_attr, cred->cred_attr_len);
@@ -226,15 +306,15 @@ static int hostapd_wps_cred_cb(void *ctx, const struct wps_credential *cred)
if ((hapd->conf->wps_cred_processing == 1 ||
hapd->conf->wps_cred_processing == 2) && cred->cred_attr) {
- size_t blen = cred->cred_attr_len * 2 + 1;
- char *_buf = os_malloc(blen);
- if (_buf) {
- wpa_snprintf_hex(_buf, blen,
- cred->cred_attr, cred->cred_attr_len);
- wpa_msg(hapd->msg_ctx, MSG_INFO, "%s%s",
- WPS_EVENT_NEW_AP_SETTINGS, _buf);
- os_free(_buf);
- }
+ hapd_new_ap_event(hapd, cred->cred_attr, cred->cred_attr_len);
+ } else if (hapd->conf->wps_cred_processing == 1 ||
+ hapd->conf->wps_cred_processing == 2) {
+ struct wpabuf *attr;
+ attr = wpabuf_alloc(200);
+ if (attr && wps_build_credential_wrap(attr, cred) == 0)
+ hapd_new_ap_event(hapd, wpabuf_head_u8(attr),
+ wpabuf_len(attr));
+ wpabuf_free(attr);
} else
wpa_msg(hapd->msg_ctx, MSG_INFO, WPS_EVENT_NEW_AP_SETTINGS);
@@ -263,6 +343,8 @@ static int hostapd_wps_cred_cb(void *ctx, const struct wps_credential *cred)
}
hapd->wps->wps_state = WPS_STATE_CONFIGURED;
+ if (hapd->iface->config_fname == NULL)
+ return 0;
len = os_strlen(hapd->iface->config_fname) + 5;
tmp_fname = os_malloc(len);
if (tmp_fname == NULL)
@@ -290,10 +372,17 @@ static int hostapd_wps_cred_cb(void *ctx, const struct wps_credential *cred)
fprintf(nconf, "wps_state=2\n");
- fprintf(nconf, "ssid=");
- for (i = 0; i < cred->ssid_len; i++)
- fputc(cred->ssid[i], nconf);
- fprintf(nconf, "\n");
+ if (is_hex(cred->ssid, cred->ssid_len)) {
+ fprintf(nconf, "ssid2=");
+ for (i = 0; i < cred->ssid_len; i++)
+ fprintf(nconf, "%02x", cred->ssid[i]);
+ fprintf(nconf, "\n");
+ } else {
+ fprintf(nconf, "ssid=");
+ for (i = 0; i < cred->ssid_len; i++)
+ fputc(cred->ssid[i], nconf);
+ fprintf(nconf, "\n");
+ }
if ((cred->auth_type & (WPS_AUTH_WPA2 | WPS_AUTH_WPA2PSK)) &&
(cred->auth_type & (WPS_AUTH_WPA | WPS_AUTH_WPAPSK)))
@@ -383,7 +472,10 @@ static int hostapd_wps_cred_cb(void *ctx, const struct wps_credential *cred)
multi_bss = 1;
if (!multi_bss &&
(str_starts(buf, "ssid=") ||
+ str_starts(buf, "ssid2=") ||
str_starts(buf, "auth_algs=") ||
+ str_starts(buf, "wep_default_key=") ||
+ str_starts(buf, "wep_key") ||
str_starts(buf, "wps_state=") ||
str_starts(buf, "wpa=") ||
str_starts(buf, "wpa_psk=") ||
@@ -414,20 +506,27 @@ static int hostapd_wps_cred_cb(void *ctx, const struct wps_credential *cred)
eloop_register_timeout(0, 100000, wps_reload_config, hapd->iface,
NULL);
- /* TODO: dualband AP may need to update multiple configuration files */
-
wpa_printf(MSG_DEBUG, "WPS: AP configuration updated");
return 0;
}
+static int hostapd_wps_cred_cb(void *ctx, const struct wps_credential *cred)
+{
+ struct hostapd_data *hapd = ctx;
+ return hostapd_wps_for_each(hapd, hapd_wps_cred_cb, (void *) cred);
+}
+
+
static void hostapd_wps_reenable_ap_pin(void *eloop_data, void *user_ctx)
{
struct hostapd_data *hapd = eloop_data;
if (hapd->conf->ap_setup_locked)
return;
+ if (hapd->ap_pin_failures_consecutive >= 10)
+ return;
wpa_printf(MSG_DEBUG, "WPS: Re-enable AP PIN");
wpa_msg(hapd->msg_ctx, MSG_INFO, WPS_EVENT_AP_SETUP_UNLOCKED);
@@ -436,11 +535,12 @@ static void hostapd_wps_reenable_ap_pin(void *eloop_data, void *user_ctx)
}
-static void hostapd_pwd_auth_fail(struct hostapd_data *hapd,
- struct wps_event_pwd_auth_fail *data)
+static int wps_pwd_auth_fail(struct hostapd_data *hapd, void *ctx)
{
- if (!data->enrollee || hapd->conf->ap_pin == NULL)
- return;
+ struct wps_event_pwd_auth_fail *data = ctx;
+
+ if (!data->enrollee || hapd->conf->ap_pin == NULL || hapd->wps == NULL)
+ return 0;
/*
* Registrar failed to prove its knowledge of the AP PIN. Lock AP setup
@@ -448,17 +548,27 @@ static void hostapd_pwd_auth_fail(struct hostapd_data *hapd,
* force attacks.
*/
hapd->ap_pin_failures++;
- wpa_printf(MSG_DEBUG, "WPS: AP PIN authentication failure number %u",
- hapd->ap_pin_failures);
+ hapd->ap_pin_failures_consecutive++;
+ wpa_printf(MSG_DEBUG, "WPS: AP PIN authentication failure number %u "
+ "(%u consecutive)",
+ hapd->ap_pin_failures, hapd->ap_pin_failures_consecutive);
if (hapd->ap_pin_failures < 3)
- return;
+ return 0;
wpa_msg(hapd->msg_ctx, MSG_INFO, WPS_EVENT_AP_SETUP_LOCKED);
hapd->wps->ap_setup_locked = 1;
wps_registrar_update_ie(hapd->wps->registrar);
- if (!hapd->conf->ap_setup_locked) {
+ if (!hapd->conf->ap_setup_locked &&
+ hapd->ap_pin_failures_consecutive >= 10) {
+ /*
+ * In indefinite lockdown - disable automatic AP PIN
+ * reenablement.
+ */
+ eloop_cancel_timeout(hostapd_wps_reenable_ap_pin, hapd, NULL);
+ wpa_printf(MSG_DEBUG, "WPS: AP PIN disabled indefinitely");
+ } else if (!hapd->conf->ap_setup_locked) {
if (hapd->ap_pin_lockout_time == 0)
hapd->ap_pin_lockout_time = 60;
else if (hapd->ap_pin_lockout_time < 365 * 24 * 60 * 60 &&
@@ -473,7 +583,60 @@ static void hostapd_pwd_auth_fail(struct hostapd_data *hapd,
NULL);
}
- /* TODO: dualband AP may need to update other interfaces */
+ return 0;
+}
+
+
+static void hostapd_pwd_auth_fail(struct hostapd_data *hapd,
+ struct wps_event_pwd_auth_fail *data)
+{
+ hostapd_wps_for_each(hapd, wps_pwd_auth_fail, data);
+}
+
+
+static int wps_ap_pin_success(struct hostapd_data *hapd, void *ctx)
+{
+ if (hapd->conf->ap_pin == NULL || hapd->wps == NULL)
+ return 0;
+
+ if (hapd->ap_pin_failures_consecutive == 0)
+ return 0;
+
+ wpa_printf(MSG_DEBUG, "WPS: Clear consecutive AP PIN failure counter "
+ "- total validation failures %u (%u consecutive)",
+ hapd->ap_pin_failures, hapd->ap_pin_failures_consecutive);
+ hapd->ap_pin_failures_consecutive = 0;
+
+ return 0;
+}
+
+
+static void hostapd_wps_ap_pin_success(struct hostapd_data *hapd)
+{
+ hostapd_wps_for_each(hapd, wps_ap_pin_success, NULL);
+}
+
+
+static const char * wps_event_fail_reason[NUM_WPS_EI_VALUES] = {
+ "No Error", /* WPS_EI_NO_ERROR */
+ "TKIP Only Prohibited", /* WPS_EI_SECURITY_TKIP_ONLY_PROHIBITED */
+ "WEP Prohibited" /* WPS_EI_SECURITY_WEP_PROHIBITED */
+};
+
+static void hostapd_wps_event_fail(struct hostapd_data *hapd,
+ struct wps_event_fail *fail)
+{
+ if (fail->error_indication > 0 &&
+ fail->error_indication < NUM_WPS_EI_VALUES) {
+ wpa_msg(hapd->msg_ctx, MSG_INFO,
+ WPS_EVENT_FAIL "msg=%d config_error=%d reason=%d (%s)",
+ fail->msg, fail->config_error, fail->error_indication,
+ wps_event_fail_reason[fail->error_indication]);
+ } else {
+ wpa_msg(hapd->msg_ctx, MSG_INFO,
+ WPS_EVENT_FAIL "msg=%d config_error=%d",
+ fail->msg, fail->config_error);
+ }
}
@@ -482,8 +645,43 @@ static void hostapd_wps_event_cb(void *ctx, enum wps_event event,
{
struct hostapd_data *hapd = ctx;
- if (event == WPS_EV_PWD_AUTH_FAIL)
+ switch (event) {
+ case WPS_EV_M2D:
+ wpa_msg(hapd->msg_ctx, MSG_INFO, WPS_EVENT_M2D);
+ break;
+ case WPS_EV_FAIL:
+ hostapd_wps_event_fail(hapd, &data->fail);
+ break;
+ case WPS_EV_SUCCESS:
+ wpa_msg(hapd->msg_ctx, MSG_INFO, WPS_EVENT_SUCCESS);
+ break;
+ case WPS_EV_PWD_AUTH_FAIL:
hostapd_pwd_auth_fail(hapd, &data->pwd_auth_fail);
+ break;
+ case WPS_EV_PBC_OVERLAP:
+ wpa_msg(hapd->msg_ctx, MSG_INFO, WPS_EVENT_OVERLAP);
+ break;
+ case WPS_EV_PBC_TIMEOUT:
+ wpa_msg(hapd->msg_ctx, MSG_INFO, WPS_EVENT_TIMEOUT);
+ break;
+ case WPS_EV_ER_AP_ADD:
+ break;
+ case WPS_EV_ER_AP_REMOVE:
+ break;
+ case WPS_EV_ER_ENROLLEE_ADD:
+ break;
+ case WPS_EV_ER_ENROLLEE_REMOVE:
+ break;
+ case WPS_EV_ER_AP_SETTINGS:
+ break;
+ case WPS_EV_ER_SET_SELECTED_REGISTRAR:
+ break;
+ case WPS_EV_AP_PIN_SUCCESS:
+ hostapd_wps_ap_pin_success(hapd);
+ break;
+ }
+ if (hapd->wps_event_cb)
+ hapd->wps_event_cb(hapd->wps_event_cb_ctx, event, data);
}
@@ -495,7 +693,84 @@ static void hostapd_wps_clear_ies(struct hostapd_data *hapd)
wpabuf_free(hapd->wps_probe_resp_ie);
hapd->wps_probe_resp_ie = NULL;
- hapd->drv.set_ap_wps_ie(hapd);
+ hostapd_set_ap_wps_ie(hapd);
+}
+
+
+static int get_uuid_cb(struct hostapd_iface *iface, void *ctx)
+{
+ const u8 **uuid = ctx;
+ size_t j;
+
+ if (iface == NULL)
+ return 0;
+ for (j = 0; j < iface->num_bss; j++) {
+ struct hostapd_data *hapd = iface->bss[j];
+ if (hapd->wps && !is_nil_uuid(hapd->wps->uuid)) {
+ *uuid = hapd->wps->uuid;
+ return 1;
+ }
+ }
+
+ return 0;
+}
+
+
+static const u8 * get_own_uuid(struct hostapd_iface *iface)
+{
+ const u8 *uuid;
+ if (iface->interfaces == NULL ||
+ iface->interfaces->for_each_interface == NULL)
+ return NULL;
+ uuid = NULL;
+ iface->interfaces->for_each_interface(iface->interfaces, get_uuid_cb,
+ &uuid);
+ return uuid;
+}
+
+
+static int count_interface_cb(struct hostapd_iface *iface, void *ctx)
+{
+ int *count= ctx;
+ (*count)++;
+ return 0;
+}
+
+
+static int interface_count(struct hostapd_iface *iface)
+{
+ int count = 0;
+ if (iface->interfaces == NULL ||
+ iface->interfaces->for_each_interface == NULL)
+ return 0;
+ iface->interfaces->for_each_interface(iface->interfaces,
+ count_interface_cb, &count);
+ return count;
+}
+
+
+static int hostapd_wps_set_vendor_ext(struct hostapd_data *hapd,
+ struct wps_context *wps)
+{
+ int i;
+
+ for (i = 0; i < MAX_WPS_VENDOR_EXTENSIONS; i++) {
+ wpabuf_free(wps->dev.vendor_ext[i]);
+ wps->dev.vendor_ext[i] = NULL;
+
+ if (hapd->conf->wps_vendor_ext[i] == NULL)
+ continue;
+
+ wps->dev.vendor_ext[i] =
+ wpabuf_dup(hapd->conf->wps_vendor_ext[i]);
+ if (wps->dev.vendor_ext[i] == NULL) {
+ while (--i >= 0)
+ wpabuf_free(wps->dev.vendor_ext[i]);
+ return -1;
+ }
+ }
+
+ return 0;
}
@@ -522,11 +797,22 @@ int hostapd_init_wps(struct hostapd_data *hapd,
wps->wps_state = hapd->conf->wps_state;
wps->ap_setup_locked = hapd->conf->ap_setup_locked;
if (is_nil_uuid(hapd->conf->uuid)) {
- uuid_gen_mac_addr(hapd->own_addr, wps->uuid);
- wpa_hexdump(MSG_DEBUG, "WPS: UUID based on MAC address",
- wps->uuid, UUID_LEN);
- } else
+ const u8 *uuid;
+ uuid = get_own_uuid(hapd->iface);
+ if (uuid) {
+ os_memcpy(wps->uuid, uuid, UUID_LEN);
+ wpa_hexdump(MSG_DEBUG, "WPS: Clone UUID from another "
+ "interface", wps->uuid, UUID_LEN);
+ } else {
+ uuid_gen_mac_addr(hapd->own_addr, wps->uuid);
+ wpa_hexdump(MSG_DEBUG, "WPS: UUID based on MAC "
+ "address", wps->uuid, UUID_LEN);
+ }
+ } else {
os_memcpy(wps->uuid, hapd->conf->uuid, UUID_LEN);
+ wpa_hexdump(MSG_DEBUG, "WPS: Use configured UUID",
+ wps->uuid, UUID_LEN);
+ }
wps->ssid_len = hapd->conf->ssid.ssid_len;
os_memcpy(wps->ssid, hapd->conf->ssid.ssid, wps->ssid_len);
wps->ap = 1;
@@ -543,16 +829,39 @@ int hostapd_init_wps(struct hostapd_data *hapd,
os_strdup(hapd->conf->serial_number) : NULL;
wps->config_methods =
wps_config_methods_str2bin(hapd->conf->config_methods);
- if (hapd->conf->device_type &&
- wps_dev_type_str2bin(hapd->conf->device_type,
- wps->dev.pri_dev_type) < 0) {
- wpa_printf(MSG_ERROR, "WPS: Invalid device_type");
+#ifdef CONFIG_WPS2
+ if ((wps->config_methods &
+ (WPS_CONFIG_DISPLAY | WPS_CONFIG_VIRT_DISPLAY |
+ WPS_CONFIG_PHY_DISPLAY)) == WPS_CONFIG_DISPLAY) {
+ wpa_printf(MSG_INFO, "WPS: Converting display to "
+ "virtual_display for WPS 2.0 compliance");
+ wps->config_methods |= WPS_CONFIG_VIRT_DISPLAY;
+ }
+ if ((wps->config_methods &
+ (WPS_CONFIG_PUSHBUTTON | WPS_CONFIG_VIRT_PUSHBUTTON |
+ WPS_CONFIG_PHY_PUSHBUTTON)) == WPS_CONFIG_PUSHBUTTON) {
+ wpa_printf(MSG_INFO, "WPS: Converting push_button to "
+ "virtual_push_button for WPS 2.0 compliance");
+ wps->config_methods |= WPS_CONFIG_VIRT_PUSHBUTTON;
+ }
+#endif /* CONFIG_WPS2 */
+ os_memcpy(wps->dev.pri_dev_type, hapd->conf->device_type,
+ WPS_DEV_TYPE_LEN);
+
+ if (hostapd_wps_set_vendor_ext(hapd, wps) < 0) {
os_free(wps);
return -1;
}
+
wps->dev.os_version = WPA_GET_BE32(hapd->conf->os_version);
- wps->dev.rf_bands = hapd->iconf->hw_mode == HOSTAPD_MODE_IEEE80211A ?
- WPS_RF_50GHZ : WPS_RF_24GHZ; /* FIX: dualband AP */
+
+ if (conf->wps_rf_bands) {
+ wps->dev.rf_bands = conf->wps_rf_bands;
+ } else {
+ wps->dev.rf_bands =
+ hapd->iconf->hw_mode == HOSTAPD_MODE_IEEE80211A ?
+ WPS_RF_50GHZ : WPS_RF_24GHZ; /* FIX: dualband AP */
+ }
if (conf->wpa & WPA_PROTO_RSN) {
if (conf->wpa_key_mgmt & WPA_KEY_MGMT_PSK)
@@ -647,10 +956,16 @@ int hostapd_init_wps(struct hostapd_data *hapd,
conf->skip_cred_build;
if (conf->ssid.security_policy == SECURITY_STATIC_WEP)
cfg.static_wep_only = 1;
+ cfg.dualband = interface_count(hapd->iface) > 1;
+ if ((wps->dev.rf_bands & (WPS_RF_50GHZ | WPS_RF_24GHZ)) ==
+ (WPS_RF_50GHZ | WPS_RF_24GHZ))
+ cfg.dualband = 1;
+ if (cfg.dualband)
+ wpa_printf(MSG_DEBUG, "WPS: Dualband AP");
wps->registrar = wps_registrar_init(wps, &cfg);
if (wps->registrar == NULL) {
- printf("Failed to initialize WPS Registrar\n");
+ wpa_printf(MSG_ERROR, "Failed to initialize WPS Registrar");
os_free(wps->network_key);
os_free(wps);
return -1;
@@ -662,21 +977,49 @@ int hostapd_init_wps(struct hostapd_data *hapd,
wps->model_description = hapd->conf->model_description;
wps->model_url = hapd->conf->model_url;
wps->upc = hapd->conf->upc;
+#endif /* CONFIG_WPS_UPNP */
+
+ hostapd_register_probereq_cb(hapd, hostapd_wps_probe_req_rx, hapd);
+
+ hapd->wps = wps;
+
+ return 0;
+}
+
+
+int hostapd_init_wps_complete(struct hostapd_data *hapd)
+{
+ struct wps_context *wps = hapd->wps;
+ if (wps == NULL)
+ return 0;
+
+#ifdef CONFIG_WPS_UPNP
if (hostapd_wps_upnp_init(hapd, wps) < 0) {
wpa_printf(MSG_ERROR, "Failed to initialize WPS UPnP");
wps_registrar_deinit(wps->registrar);
os_free(wps->network_key);
os_free(wps);
+ hapd->wps = NULL;
return -1;
}
#endif /* CONFIG_WPS_UPNP */
- hostapd_register_probereq_cb(hapd, hostapd_wps_probe_req_rx, hapd);
+ return 0;
+}
- hapd->wps = wps;
- return 0;
+static void hostapd_wps_nfc_clear(struct wps_context *wps)
+{
+#ifdef CONFIG_WPS_NFC
+ wps->ap_nfc_dev_pw_id = 0;
+ wpabuf_free(wps->ap_nfc_dh_pubkey);
+ wps->ap_nfc_dh_pubkey = NULL;
+ wpabuf_free(wps->ap_nfc_dh_privkey);
+ wps->ap_nfc_dh_privkey = NULL;
+ wpabuf_free(wps->ap_nfc_dev_pw);
+ wps->ap_nfc_dev_pw = NULL;
+#endif /* CONFIG_WPS_NFC */
}
@@ -694,9 +1037,8 @@ void hostapd_deinit_wps(struct hostapd_data *hapd)
wps_device_data_free(&hapd->wps->dev);
wpabuf_free(hapd->wps->dh_pubkey);
wpabuf_free(hapd->wps->dh_privkey);
- wpabuf_free(hapd->wps->oob_conf.pubkey_hash);
- wpabuf_free(hapd->wps->oob_conf.dev_password);
wps_free_pending_msgs(hapd->wps->upnp_msgs);
+ hostapd_wps_nfc_clear(hapd->wps);
os_free(hapd->wps);
hapd->wps = NULL;
hostapd_wps_clear_ies(hapd);
@@ -707,6 +1049,17 @@ void hostapd_update_wps(struct hostapd_data *hapd)
{
if (hapd->wps == NULL)
return;
+
+#ifdef CONFIG_WPS_UPNP
+ hapd->wps->friendly_name = hapd->conf->friendly_name;
+ hapd->wps->manufacturer_url = hapd->conf->manufacturer_url;
+ hapd->wps->model_description = hapd->conf->model_description;
+ hapd->wps->model_url = hapd->conf->model_url;
+ hapd->wps->upc = hapd->conf->upc;
+#endif /* CONFIG_WPS_UPNP */
+
+ hostapd_wps_set_vendor_ext(hapd, hapd->wps);
+
if (hapd->conf->wps_state)
wps_registrar_update_ie(hapd->wps->registrar);
else
@@ -714,88 +1067,97 @@ void hostapd_update_wps(struct hostapd_data *hapd)
}
-int hostapd_wps_add_pin(struct hostapd_data *hapd, const char *uuid,
- const char *pin, int timeout)
+struct wps_add_pin_data {
+ const u8 *addr;
+ const u8 *uuid;
+ const u8 *pin;
+ size_t pin_len;
+ int timeout;
+ int added;
+};
+
+
+static int wps_add_pin(struct hostapd_data *hapd, void *ctx)
{
- u8 u[UUID_LEN];
- int any = 0;
+ struct wps_add_pin_data *data = ctx;
+ int ret;
if (hapd->wps == NULL)
- return -1;
+ return 0;
+ ret = wps_registrar_add_pin(hapd->wps->registrar, data->addr,
+ data->uuid, data->pin, data->pin_len,
+ data->timeout);
+ if (ret == 0)
+ data->added++;
+ return ret;
+}
+
+
+int hostapd_wps_add_pin(struct hostapd_data *hapd, const u8 *addr,
+ const char *uuid, const char *pin, int timeout)
+{
+ u8 u[UUID_LEN];
+ struct wps_add_pin_data data;
+
+ data.addr = addr;
+ data.uuid = u;
+ data.pin = (const u8 *) pin;
+ data.pin_len = os_strlen(pin);
+ data.timeout = timeout;
+ data.added = 0;
+
if (os_strcmp(uuid, "any") == 0)
- any = 1;
- else if (uuid_str2bin(uuid, u))
+ data.uuid = NULL;
+ else {
+ if (uuid_str2bin(uuid, u))
+ return -1;
+ data.uuid = u;
+ }
+ if (hostapd_wps_for_each(hapd, wps_add_pin, &data) < 0)
return -1;
- return wps_registrar_add_pin(hapd->wps->registrar, any ? NULL : u,
- (const u8 *) pin, os_strlen(pin),
- timeout);
+ return data.added ? 0 : -1;
}
-int hostapd_wps_button_pushed(struct hostapd_data *hapd)
+static int wps_button_pushed(struct hostapd_data *hapd, void *ctx)
{
+ const u8 *p2p_dev_addr = ctx;
if (hapd->wps == NULL)
- return -1;
- return wps_registrar_button_pushed(hapd->wps->registrar);
+ return 0;
+ return wps_registrar_button_pushed(hapd->wps->registrar, p2p_dev_addr);
}
-#ifdef CONFIG_WPS_OOB
-int hostapd_wps_start_oob(struct hostapd_data *hapd, char *device_type,
- char *path, char *method, char *name)
+int hostapd_wps_button_pushed(struct hostapd_data *hapd,
+ const u8 *p2p_dev_addr)
{
- struct wps_context *wps = hapd->wps;
- struct oob_device_data *oob_dev;
+ return hostapd_wps_for_each(hapd, wps_button_pushed,
+ (void *) p2p_dev_addr);
+}
- oob_dev = wps_get_oob_device(device_type);
- if (oob_dev == NULL)
- return -1;
- oob_dev->device_path = path;
- oob_dev->device_name = name;
- wps->oob_conf.oob_method = wps_get_oob_method(method);
- if (wps->oob_conf.oob_method == OOB_METHOD_DEV_PWD_R) {
- /*
- * Use pre-configured DH keys in order to be able to write the
- * key hash into the OOB file.
- */
- wpabuf_free(wps->dh_pubkey);
- wpabuf_free(wps->dh_privkey);
- wps->dh_privkey = NULL;
- wps->dh_pubkey = dh_init(dh_groups_get(WPS_DH_GROUP),
- &wps->dh_privkey);
- wps->dh_pubkey = wpabuf_zeropad(wps->dh_pubkey, 192);
- if (wps->dh_pubkey == NULL) {
- wpa_printf(MSG_ERROR, "WPS: Failed to initialize "
- "Diffie-Hellman handshake");
- return -1;
- }
- }
-
- if (wps_process_oob(wps, oob_dev, 1) < 0)
- goto error;
+static int wps_cancel(struct hostapd_data *hapd, void *ctx)
+{
+ if (hapd->wps == NULL)
+ return 0;
- if ((wps->oob_conf.oob_method == OOB_METHOD_DEV_PWD_E ||
- wps->oob_conf.oob_method == OOB_METHOD_DEV_PWD_R) &&
- hostapd_wps_add_pin(hapd, "any",
- wpabuf_head(wps->oob_conf.dev_password), 0) <
- 0)
- goto error;
+ wps_registrar_wps_cancel(hapd->wps->registrar);
+ ap_for_each_sta(hapd, ap_sta_wps_cancel, NULL);
return 0;
+}
-error:
- wpabuf_free(wps->dh_pubkey);
- wps->dh_pubkey = NULL;
- wpabuf_free(wps->dh_privkey);
- wps->dh_privkey = NULL;
- return -1;
+
+int hostapd_wps_cancel(struct hostapd_data *hapd)
+{
+ return hostapd_wps_for_each(hapd, wps_cancel, NULL);
}
-#endif /* CONFIG_WPS_OOB */
-static int hostapd_wps_probe_req_rx(void *ctx, const u8 *addr,
- const u8 *ie, size_t ie_len)
+static int hostapd_wps_probe_req_rx(void *ctx, const u8 *addr, const u8 *da,
+ const u8 *bssid,
+ const u8 *ie, size_t ie_len,
+ int ssi_signal)
{
struct hostapd_data *hapd = ctx;
struct wpabuf *wps_ie;
@@ -819,15 +1181,28 @@ static int hostapd_wps_probe_req_rx(void *ctx, const u8 *addr,
wps_ie = ieee802_11_vendor_ie_concat(ie, ie_len, WPS_DEV_OUI_WFA);
if (wps_ie == NULL)
return 0;
+ if (wps_validate_probe_req(wps_ie, addr) < 0) {
+ wpabuf_free(wps_ie);
+ return 0;
+ }
if (wpabuf_len(wps_ie) > 0) {
- wps_registrar_probe_req_rx(hapd->wps->registrar, addr, wps_ie);
+ int p2p_wildcard = 0;
+#ifdef CONFIG_P2P
+ if (elems.ssid && elems.ssid_len == P2P_WILDCARD_SSID_LEN &&
+ os_memcmp(elems.ssid, P2P_WILDCARD_SSID,
+ P2P_WILDCARD_SSID_LEN) == 0)
+ p2p_wildcard = 1;
+#endif /* CONFIG_P2P */
+ wps_registrar_probe_req_rx(hapd->wps->registrar, addr, wps_ie,
+ p2p_wildcard);
#ifdef CONFIG_WPS_UPNP
/* FIX: what exactly should be included in the WLANEvent?
* WPS attributes? Full ProbeReq frame? */
- upnp_wps_device_send_wlan_event(hapd->wps_upnp, addr,
- UPNP_WPS_WLANEVENT_TYPE_PROBE,
- wps_ie);
+ if (!p2p_wildcard)
+ upnp_wps_device_send_wlan_event(
+ hapd->wps_upnp, addr,
+ UPNP_WPS_WLANEVENT_TYPE_PROBE, wps_ie);
#endif /* CONFIG_WPS_UPNP */
}
@@ -864,6 +1239,7 @@ static int hostapd_rx_req_put_wlan_response(
*/
sta = ap_get_sta(hapd, mac_addr);
+#ifndef CONFIG_WPS_STRICT
if (!sta) {
/*
* Workaround - Intel wsccmd uses bogus NewWLANEventMAC:
@@ -877,8 +1253,9 @@ static int hostapd_rx_req_put_wlan_response(
break;
}
}
+#endif /* CONFIG_WPS_STRICT */
- if (!sta) {
+ if (!sta || !(sta->flags & WLAN_STA_WPS)) {
wpa_printf(MSG_DEBUG, "WPS UPnP: No matching STA found");
return 0;
}
@@ -911,26 +1288,19 @@ static int hostapd_wps_upnp_init(struct hostapd_data *hapd,
if (hapd->conf->ap_pin)
ctx->ap_pin = os_strdup(hapd->conf->ap_pin);
- hapd->wps_upnp = upnp_wps_device_init(ctx, wps, hapd);
- if (hapd->wps_upnp == NULL) {
- os_free(ctx);
+ hapd->wps_upnp = upnp_wps_device_init(ctx, wps, hapd,
+ hapd->conf->upnp_iface);
+ if (hapd->wps_upnp == NULL)
return -1;
- }
wps->wps_upnp = hapd->wps_upnp;
- if (upnp_wps_device_start(hapd->wps_upnp, hapd->conf->upnp_iface)) {
- upnp_wps_device_deinit(hapd->wps_upnp);
- hapd->wps_upnp = NULL;
- return -1;
- }
-
return 0;
}
static void hostapd_wps_upnp_deinit(struct hostapd_data *hapd)
{
- upnp_wps_device_deinit(hapd->wps_upnp);
+ upnp_wps_device_deinit(hapd->wps_upnp, hapd);
}
#endif /* CONFIG_WPS_UPNP */
@@ -950,6 +1320,7 @@ static void hostapd_wps_ap_pin_timeout(void *eloop_data, void *user_ctx)
struct hostapd_data *hapd = eloop_data;
wpa_printf(MSG_DEBUG, "WPS: AP PIN timed out");
hostapd_wps_ap_pin_disable(hapd);
+ wpa_msg(hapd->msg_ctx, MSG_INFO, WPS_EVENT_AP_PIN_DISABLED);
}
@@ -957,6 +1328,7 @@ static void hostapd_wps_ap_pin_enable(struct hostapd_data *hapd, int timeout)
{
wpa_printf(MSG_DEBUG, "WPS: Enabling AP PIN (timeout=%d)", timeout);
hapd->ap_pin_failures = 0;
+ hapd->ap_pin_failures_consecutive = 0;
hapd->conf->ap_setup_locked = 0;
if (hapd->wps->ap_setup_locked) {
wpa_msg(hapd->msg_ctx, MSG_INFO, WPS_EVENT_AP_SETUP_UNLOCKED);
@@ -970,31 +1342,53 @@ static void hostapd_wps_ap_pin_enable(struct hostapd_data *hapd, int timeout)
}
-void hostapd_wps_ap_pin_disable(struct hostapd_data *hapd)
+static int wps_ap_pin_disable(struct hostapd_data *hapd, void *ctx)
{
- wpa_printf(MSG_DEBUG, "WPS: Disabling AP PIN");
os_free(hapd->conf->ap_pin);
hapd->conf->ap_pin = NULL;
#ifdef CONFIG_WPS_UPNP
upnp_wps_set_ap_pin(hapd->wps_upnp, NULL);
#endif /* CONFIG_WPS_UPNP */
eloop_cancel_timeout(hostapd_wps_ap_pin_timeout, hapd, NULL);
+ return 0;
}
-const char * hostapd_wps_ap_pin_random(struct hostapd_data *hapd, int timeout)
+void hostapd_wps_ap_pin_disable(struct hostapd_data *hapd)
{
- unsigned int pin;
+ wpa_printf(MSG_DEBUG, "WPS: Disabling AP PIN");
+ hostapd_wps_for_each(hapd, wps_ap_pin_disable, NULL);
+}
+
+
+struct wps_ap_pin_data {
char pin_txt[9];
+ int timeout;
+};
- pin = wps_generate_pin();
- os_snprintf(pin_txt, sizeof(pin_txt), "%u", pin);
+
+static int wps_ap_pin_set(struct hostapd_data *hapd, void *ctx)
+{
+ struct wps_ap_pin_data *data = ctx;
os_free(hapd->conf->ap_pin);
- hapd->conf->ap_pin = os_strdup(pin_txt);
+ hapd->conf->ap_pin = os_strdup(data->pin_txt);
#ifdef CONFIG_WPS_UPNP
- upnp_wps_set_ap_pin(hapd->wps_upnp, pin_txt);
+ upnp_wps_set_ap_pin(hapd->wps_upnp, data->pin_txt);
#endif /* CONFIG_WPS_UPNP */
- hostapd_wps_ap_pin_enable(hapd, timeout);
+ hostapd_wps_ap_pin_enable(hapd, data->timeout);
+ return 0;
+}
+
+
+const char * hostapd_wps_ap_pin_random(struct hostapd_data *hapd, int timeout)
+{
+ unsigned int pin;
+ struct wps_ap_pin_data data;
+
+ pin = wps_generate_pin();
+ os_snprintf(data.pin_txt, sizeof(data.pin_txt), "%08u", pin);
+ data.timeout = timeout;
+ hostapd_wps_for_each(hapd, wps_ap_pin_set, &data);
return hapd->conf->ap_pin;
}
@@ -1008,13 +1402,228 @@ const char * hostapd_wps_ap_pin_get(struct hostapd_data *hapd)
int hostapd_wps_ap_pin_set(struct hostapd_data *hapd, const char *pin,
int timeout)
{
- os_free(hapd->conf->ap_pin);
- hapd->conf->ap_pin = os_strdup(pin);
- if (hapd->conf->ap_pin == NULL)
+ struct wps_ap_pin_data data;
+ int ret;
+
+ ret = os_snprintf(data.pin_txt, sizeof(data.pin_txt), "%s", pin);
+ if (ret < 0 || ret >= (int) sizeof(data.pin_txt))
return -1;
-#ifdef CONFIG_WPS_UPNP
- upnp_wps_set_ap_pin(hapd->wps_upnp, hapd->conf->ap_pin);
-#endif /* CONFIG_WPS_UPNP */
- hostapd_wps_ap_pin_enable(hapd, timeout);
+ data.timeout = timeout;
+ return hostapd_wps_for_each(hapd, wps_ap_pin_set, &data);
+}
+
+
+static int wps_update_ie(struct hostapd_data *hapd, void *ctx)
+{
+ if (hapd->wps)
+ wps_registrar_update_ie(hapd->wps->registrar);
+ return 0;
+}
+
+
+void hostapd_wps_update_ie(struct hostapd_data *hapd)
+{
+ hostapd_wps_for_each(hapd, wps_update_ie, NULL);
+}
+
+
+int hostapd_wps_config_ap(struct hostapd_data *hapd, const char *ssid,
+ const char *auth, const char *encr, const char *key)
+{
+ struct wps_credential cred;
+ size_t len;
+
+ os_memset(&cred, 0, sizeof(cred));
+
+ len = os_strlen(ssid);
+ if ((len & 1) || len > 2 * sizeof(cred.ssid) ||
+ hexstr2bin(ssid, cred.ssid, len / 2))
+ return -1;
+ cred.ssid_len = len / 2;
+
+ if (os_strncmp(auth, "OPEN", 4) == 0)
+ cred.auth_type = WPS_AUTH_OPEN;
+ else if (os_strncmp(auth, "WPAPSK", 6) == 0)
+ cred.auth_type = WPS_AUTH_WPAPSK;
+ else if (os_strncmp(auth, "WPA2PSK", 7) == 0)
+ cred.auth_type = WPS_AUTH_WPA2PSK;
+ else
+ return -1;
+
+ if (encr) {
+ if (os_strncmp(encr, "NONE", 4) == 0)
+ cred.encr_type = WPS_ENCR_NONE;
+ else if (os_strncmp(encr, "WEP", 3) == 0)
+ cred.encr_type = WPS_ENCR_WEP;
+ else if (os_strncmp(encr, "TKIP", 4) == 0)
+ cred.encr_type = WPS_ENCR_TKIP;
+ else if (os_strncmp(encr, "CCMP", 4) == 0)
+ cred.encr_type = WPS_ENCR_AES;
+ else
+ return -1;
+ } else
+ cred.encr_type = WPS_ENCR_NONE;
+
+ if (key) {
+ len = os_strlen(key);
+ if ((len & 1) || len > 2 * sizeof(cred.key) ||
+ hexstr2bin(key, cred.key, len / 2))
+ return -1;
+ cred.key_len = len / 2;
+ }
+
+ return wps_registrar_config_ap(hapd->wps->registrar, &cred);
+}
+
+
+#ifdef CONFIG_WPS_NFC
+
+struct wps_nfc_password_token_data {
+ const u8 *oob_dev_pw;
+ size_t oob_dev_pw_len;
+ int added;
+};
+
+
+static int wps_add_nfc_password_token(struct hostapd_data *hapd, void *ctx)
+{
+ struct wps_nfc_password_token_data *data = ctx;
+ int ret;
+
+ if (hapd->wps == NULL)
+ return 0;
+ ret = wps_registrar_add_nfc_password_token(hapd->wps->registrar,
+ data->oob_dev_pw,
+ data->oob_dev_pw_len);
+ if (ret == 0)
+ data->added++;
+ return ret;
+}
+
+
+static int hostapd_wps_add_nfc_password_token(struct hostapd_data *hapd,
+ struct wps_parse_attr *attr)
+{
+ struct wps_nfc_password_token_data data;
+
+ data.oob_dev_pw = attr->oob_dev_password;
+ data.oob_dev_pw_len = attr->oob_dev_password_len;
+ data.added = 0;
+ if (hostapd_wps_for_each(hapd, wps_add_nfc_password_token, &data) < 0)
+ return -1;
+ return data.added ? 0 : -1;
+}
+
+
+static int hostapd_wps_nfc_tag_process(struct hostapd_data *hapd,
+ const struct wpabuf *wps)
+{
+ struct wps_parse_attr attr;
+
+ wpa_hexdump_buf(MSG_DEBUG, "WPS: Received NFC tag payload", wps);
+
+ if (wps_parse_msg(wps, &attr)) {
+ wpa_printf(MSG_DEBUG, "WPS: Ignore invalid data from NFC tag");
+ return -1;
+ }
+
+ if (attr.oob_dev_password)
+ return hostapd_wps_add_nfc_password_token(hapd, &attr);
+
+ wpa_printf(MSG_DEBUG, "WPS: Ignore unrecognized NFC tag");
+ return -1;
+}
+
+
+int hostapd_wps_nfc_tag_read(struct hostapd_data *hapd,
+ const struct wpabuf *data)
+{
+ const struct wpabuf *wps = data;
+ struct wpabuf *tmp = NULL;
+ int ret;
+
+ if (wpabuf_len(data) < 4)
+ return -1;
+
+ if (*wpabuf_head_u8(data) != 0x10) {
+ /* Assume this contains full NDEF record */
+ tmp = ndef_parse_wifi(data);
+ if (tmp == NULL) {
+ wpa_printf(MSG_DEBUG, "WPS: Could not parse NDEF");
+ return -1;
+ }
+ wps = tmp;
+ }
+
+ ret = hostapd_wps_nfc_tag_process(hapd, wps);
+ wpabuf_free(tmp);
+ return ret;
+}
+
+
+struct wpabuf * hostapd_wps_nfc_config_token(struct hostapd_data *hapd,
+ int ndef)
+{
+ struct wpabuf *ret;
+
+ if (hapd->wps == NULL)
+ return NULL;
+
+ ret = wps_get_oob_cred(hapd->wps);
+ if (ndef && ret) {
+ struct wpabuf *tmp;
+ tmp = ndef_build_wifi(ret);
+ wpabuf_free(ret);
+ if (tmp == NULL)
+ return NULL;
+ ret = tmp;
+ }
+
+ return ret;
+}
+
+
+struct wpabuf * hostapd_wps_nfc_token_gen(struct hostapd_data *hapd, int ndef)
+{
+ return wps_nfc_token_gen(ndef, &hapd->conf->wps_nfc_dev_pw_id,
+ &hapd->conf->wps_nfc_dh_pubkey,
+ &hapd->conf->wps_nfc_dh_privkey,
+ &hapd->conf->wps_nfc_dev_pw);
+}
+
+
+int hostapd_wps_nfc_token_enable(struct hostapd_data *hapd)
+{
+ struct wps_context *wps = hapd->wps;
+
+ if (wps == NULL)
+ return -1;
+
+ if (!hapd->conf->wps_nfc_dh_pubkey ||
+ !hapd->conf->wps_nfc_dh_privkey ||
+ !hapd->conf->wps_nfc_dev_pw ||
+ !hapd->conf->wps_nfc_dev_pw_id)
+ return -1;
+
+ hostapd_wps_nfc_clear(wps);
+ wps->ap_nfc_dev_pw_id = hapd->conf->wps_nfc_dev_pw_id;
+ wps->ap_nfc_dh_pubkey = wpabuf_dup(hapd->conf->wps_nfc_dh_pubkey);
+ wps->ap_nfc_dh_privkey = wpabuf_dup(hapd->conf->wps_nfc_dh_privkey);
+ wps->ap_nfc_dev_pw = wpabuf_dup(hapd->conf->wps_nfc_dev_pw);
+
+ if (!wps->ap_nfc_dh_pubkey || !wps->ap_nfc_dh_privkey ||
+ !wps->ap_nfc_dev_pw) {
+ hostapd_wps_nfc_clear(wps);
+ return -1;
+ }
+
return 0;
}
+
+
+void hostapd_wps_nfc_token_disable(struct hostapd_data *hapd)
+{
+ hostapd_wps_nfc_clear(hapd->wps);
+}
+
+#endif /* CONFIG_WPS_NFC */
diff --git a/contrib/wpa/src/ap/wps_hostapd.h b/contrib/wpa/src/ap/wps_hostapd.h
index e978a1c..4e5026b 100644
--- a/contrib/wpa/src/ap/wps_hostapd.h
+++ b/contrib/wpa/src/ap/wps_hostapd.h
@@ -1,15 +1,9 @@
/*
* hostapd / WPS integration
- * Copyright (c) 2008-2010, Jouni Malinen <j@w1.fi>
+ * Copyright (c) 2008-2012, Jouni Malinen <j@w1.fi>
*
- * This program is free software; you can redistribute it and/or modify
- * it under the terms of the GNU General Public License version 2 as
- * published by the Free Software Foundation.
- *
- * Alternatively, this software may be distributed under the terms of BSD
- * license.
- *
- * See README and COPYING for more details.
+ * This software may be distributed under the terms of the BSD license.
+ * See README for more details.
*/
#ifndef WPS_HOSTAPD_H
@@ -19,13 +13,14 @@
int hostapd_init_wps(struct hostapd_data *hapd,
struct hostapd_bss_config *conf);
+int hostapd_init_wps_complete(struct hostapd_data *hapd);
void hostapd_deinit_wps(struct hostapd_data *hapd);
void hostapd_update_wps(struct hostapd_data *hapd);
-int hostapd_wps_add_pin(struct hostapd_data *hapd, const char *uuid,
- const char *pin, int timeout);
-int hostapd_wps_button_pushed(struct hostapd_data *hapd);
-int hostapd_wps_start_oob(struct hostapd_data *hapd, char *device_type,
- char *path, char *method, char *name);
+int hostapd_wps_add_pin(struct hostapd_data *hapd, const u8 *addr,
+ const char *uuid, const char *pin, int timeout);
+int hostapd_wps_button_pushed(struct hostapd_data *hapd,
+ const u8 *p2p_dev_addr);
+int hostapd_wps_cancel(struct hostapd_data *hapd);
int hostapd_wps_get_mib_sta(struct hostapd_data *hapd, const u8 *addr,
char *buf, size_t buflen);
void hostapd_wps_ap_pin_disable(struct hostapd_data *hapd);
@@ -33,6 +28,16 @@ const char * hostapd_wps_ap_pin_random(struct hostapd_data *hapd, int timeout);
const char * hostapd_wps_ap_pin_get(struct hostapd_data *hapd);
int hostapd_wps_ap_pin_set(struct hostapd_data *hapd, const char *pin,
int timeout);
+void hostapd_wps_update_ie(struct hostapd_data *hapd);
+int hostapd_wps_config_ap(struct hostapd_data *hapd, const char *ssid,
+ const char *auth, const char *encr, const char *key);
+int hostapd_wps_nfc_tag_read(struct hostapd_data *hapd,
+ const struct wpabuf *data);
+struct wpabuf * hostapd_wps_nfc_config_token(struct hostapd_data *hapd,
+ int ndef);
+struct wpabuf * hostapd_wps_nfc_token_gen(struct hostapd_data *hapd, int ndef);
+int hostapd_wps_nfc_token_enable(struct hostapd_data *hapd);
+void hostapd_wps_nfc_token_disable(struct hostapd_data *hapd);
#else /* CONFIG_WPS */
@@ -46,6 +51,11 @@ static inline void hostapd_deinit_wps(struct hostapd_data *hapd)
{
}
+static inline int hostapd_init_wps_complete(struct hostapd_data *hapd)
+{
+ return 0;
+}
+
static inline void hostapd_update_wps(struct hostapd_data *hapd)
{
}
@@ -57,7 +67,13 @@ static inline int hostapd_wps_get_mib_sta(struct hostapd_data *hapd,
return 0;
}
-static inline int hostapd_wps_button_pushed(struct hostapd_data *hapd)
+static inline int hostapd_wps_button_pushed(struct hostapd_data *hapd,
+ const u8 *p2p_dev_addr)
+{
+ return 0;
+}
+
+static inline int hostapd_wps_cancel(struct hostapd_data *hapd)
{
return 0;
}
diff --git a/contrib/wpa/src/common/Makefile b/contrib/wpa/src/common/Makefile
deleted file mode 100644
index 9c41962..0000000
--- a/contrib/wpa/src/common/Makefile
+++ /dev/null
@@ -1,8 +0,0 @@
-all:
- @echo Nothing to be made.
-
-clean:
- rm -f *~ *.o *.d
-
-install:
- @echo Nothing to be made.
diff --git a/contrib/wpa/src/common/defs.h b/contrib/wpa/src/common/defs.h
index 173bbd1..281dd8a 100644
--- a/contrib/wpa/src/common/defs.h
+++ b/contrib/wpa/src/common/defs.h
@@ -2,14 +2,8 @@
* WPA Supplicant - Common definitions
* Copyright (c) 2004-2008, Jouni Malinen <j@w1.fi>
*
- * This program is free software; you can redistribute it and/or modify
- * it under the terms of the GNU General Public License version 2 as
- * published by the Free Software Foundation.
- *
- * Alternatively, this software may be distributed under the terms of BSD
- * license.
- *
- * See README and COPYING for more details.
+ * This software may be distributed under the terms of the BSD license.
+ * See README for more details.
*/
#ifndef DEFS_H
@@ -32,6 +26,8 @@ typedef enum { FALSE = 0, TRUE = 1 } Boolean;
#ifdef CONFIG_IEEE80211W
#define WPA_CIPHER_AES_128_CMAC BIT(5)
#endif /* CONFIG_IEEE80211W */
+#define WPA_CIPHER_GCMP BIT(6)
+#define WPA_CIPHER_SMS4 BIT(7)
#define WPA_KEY_MGMT_IEEE8021X BIT(0)
#define WPA_KEY_MGMT_PSK BIT(1)
@@ -43,41 +39,73 @@ typedef enum { FALSE = 0, TRUE = 1 } Boolean;
#define WPA_KEY_MGMT_IEEE8021X_SHA256 BIT(7)
#define WPA_KEY_MGMT_PSK_SHA256 BIT(8)
#define WPA_KEY_MGMT_WPS BIT(9)
+#define WPA_KEY_MGMT_SAE BIT(10)
+#define WPA_KEY_MGMT_FT_SAE BIT(11)
+#define WPA_KEY_MGMT_WAPI_PSK BIT(12)
+#define WPA_KEY_MGMT_WAPI_CERT BIT(13)
+#define WPA_KEY_MGMT_CCKM BIT(14)
static inline int wpa_key_mgmt_wpa_ieee8021x(int akm)
{
- return akm == WPA_KEY_MGMT_IEEE8021X ||
- akm == WPA_KEY_MGMT_FT_IEEE8021X ||
- akm == WPA_KEY_MGMT_IEEE8021X_SHA256;
+ return !!(akm & (WPA_KEY_MGMT_IEEE8021X |
+ WPA_KEY_MGMT_FT_IEEE8021X |
+ WPA_KEY_MGMT_CCKM |
+ WPA_KEY_MGMT_IEEE8021X_SHA256));
}
static inline int wpa_key_mgmt_wpa_psk(int akm)
{
- return akm == WPA_KEY_MGMT_PSK ||
- akm == WPA_KEY_MGMT_FT_PSK ||
- akm == WPA_KEY_MGMT_PSK_SHA256;
+ return !!(akm & (WPA_KEY_MGMT_PSK |
+ WPA_KEY_MGMT_FT_PSK |
+ WPA_KEY_MGMT_PSK_SHA256 |
+ WPA_KEY_MGMT_SAE));
}
static inline int wpa_key_mgmt_ft(int akm)
{
- return akm == WPA_KEY_MGMT_FT_PSK ||
- akm == WPA_KEY_MGMT_FT_IEEE8021X;
+ return !!(akm & (WPA_KEY_MGMT_FT_PSK |
+ WPA_KEY_MGMT_FT_IEEE8021X |
+ WPA_KEY_MGMT_FT_SAE));
+}
+
+static inline int wpa_key_mgmt_sae(int akm)
+{
+ return !!(akm & (WPA_KEY_MGMT_SAE |
+ WPA_KEY_MGMT_FT_SAE));
}
static inline int wpa_key_mgmt_sha256(int akm)
{
- return akm == WPA_KEY_MGMT_PSK_SHA256 ||
- akm == WPA_KEY_MGMT_IEEE8021X_SHA256;
+ return !!(akm & (WPA_KEY_MGMT_PSK_SHA256 |
+ WPA_KEY_MGMT_IEEE8021X_SHA256));
+}
+
+static inline int wpa_key_mgmt_wpa(int akm)
+{
+ return wpa_key_mgmt_wpa_ieee8021x(akm) ||
+ wpa_key_mgmt_wpa_psk(akm);
+}
+
+static inline int wpa_key_mgmt_wpa_any(int akm)
+{
+ return wpa_key_mgmt_wpa(akm) || (akm & WPA_KEY_MGMT_WPA_NONE);
+}
+
+static inline int wpa_key_mgmt_cckm(int akm)
+{
+ return akm == WPA_KEY_MGMT_CCKM;
}
#define WPA_PROTO_WPA BIT(0)
#define WPA_PROTO_RSN BIT(1)
+#define WPA_PROTO_WAPI BIT(2)
#define WPA_AUTH_ALG_OPEN BIT(0)
#define WPA_AUTH_ALG_SHARED BIT(1)
#define WPA_AUTH_ALG_LEAP BIT(2)
#define WPA_AUTH_ALG_FT BIT(3)
+#define WPA_AUTH_ALG_SAE BIT(4)
enum wpa_alg {
@@ -86,7 +114,10 @@ enum wpa_alg {
WPA_ALG_TKIP,
WPA_ALG_CCMP,
WPA_ALG_IGTK,
- WPA_ALG_PMK
+ WPA_ALG_PMK,
+ WPA_ALG_GCMP,
+ WPA_ALG_SMS4,
+ WPA_ALG_KRK
};
/**
@@ -97,7 +128,9 @@ enum wpa_cipher {
CIPHER_WEP40,
CIPHER_TKIP,
CIPHER_CCMP,
- CIPHER_WEP104
+ CIPHER_WEP104,
+ CIPHER_GCMP,
+ CIPHER_SMS4
};
/**
@@ -113,7 +146,12 @@ enum wpa_key_mgmt {
KEY_MGMT_FT_PSK,
KEY_MGMT_802_1X_SHA256,
KEY_MGMT_PSK_SHA256,
- KEY_MGMT_WPS
+ KEY_MGMT_WPS,
+ KEY_MGMT_SAE,
+ KEY_MGMT_FT_SAE,
+ KEY_MGMT_WAPI_PSK,
+ KEY_MGMT_WAPI_CERT,
+ KEY_MGMT_CCKM
};
/**
@@ -137,6 +175,15 @@ enum wpa_states {
WPA_DISCONNECTED,
/**
+ * WPA_INTERFACE_DISABLED - Interface disabled
+ *
+ * This stat eis entered if the network interface is disabled, e.g.,
+ * due to rfkill. wpa_supplicant refuses any new operations that would
+ * use the radio until the interface has been enabled.
+ */
+ WPA_INTERFACE_DISABLED,
+
+ /**
* WPA_INACTIVE - Inactive state (wpa_supplicant disabled)
*
* This state is entered if there are no enabled networks in the
@@ -239,8 +286,9 @@ enum wpa_states {
enum mfp_options {
NO_MGMT_FRAME_PROTECTION = 0,
MGMT_FRAME_PROTECTION_OPTIONAL = 1,
- MGMT_FRAME_PROTECTION_REQUIRED = 2
+ MGMT_FRAME_PROTECTION_REQUIRED = 2,
};
+#define MGMT_FRAME_PROTECTION_DEFAULT 3
/**
* enum hostapd_hw_mode - Hardware mode
@@ -249,7 +297,25 @@ enum hostapd_hw_mode {
HOSTAPD_MODE_IEEE80211B,
HOSTAPD_MODE_IEEE80211G,
HOSTAPD_MODE_IEEE80211A,
+ HOSTAPD_MODE_IEEE80211AD,
NUM_HOSTAPD_MODES
};
+/**
+ * enum wpa_ctrl_req_type - Control interface request types
+ */
+enum wpa_ctrl_req_type {
+ WPA_CTRL_REQ_UNKNOWN,
+ WPA_CTRL_REQ_EAP_IDENTITY,
+ WPA_CTRL_REQ_EAP_PASSWORD,
+ WPA_CTRL_REQ_EAP_NEW_PASSWORD,
+ WPA_CTRL_REQ_EAP_PIN,
+ WPA_CTRL_REQ_EAP_OTP,
+ WPA_CTRL_REQ_EAP_PASSPHRASE,
+ NUM_WPA_CTRL_REQS
+};
+
+/* Maximum number of EAP methods to store for EAP server user information */
+#define EAP_MAX_METHODS 8
+
#endif /* DEFS_H */
diff --git a/contrib/wpa/src/common/eapol_common.h b/contrib/wpa/src/common/eapol_common.h
index d70e62d..4811f38 100644
--- a/contrib/wpa/src/common/eapol_common.h
+++ b/contrib/wpa/src/common/eapol_common.h
@@ -2,14 +2,8 @@
* EAPOL definitions shared between hostapd and wpa_supplicant
* Copyright (c) 2002-2007, Jouni Malinen <j@w1.fi>
*
- * This program is free software; you can redistribute it and/or modify
- * it under the terms of the GNU General Public License version 2 as
- * published by the Free Software Foundation.
- *
- * Alternatively, this software may be distributed under the terms of BSD
- * license.
- *
- * See README and COPYING for more details.
+ * This software may be distributed under the terms of the BSD license.
+ * See README for more details.
*/
#ifndef EAPOL_COMMON_H
@@ -44,4 +38,44 @@ enum { IEEE802_1X_TYPE_EAP_PACKET = 0,
enum { EAPOL_KEY_TYPE_RC4 = 1, EAPOL_KEY_TYPE_RSN = 2,
EAPOL_KEY_TYPE_WPA = 254 };
+
+#define IEEE8021X_REPLAY_COUNTER_LEN 8
+#define IEEE8021X_KEY_SIGN_LEN 16
+#define IEEE8021X_KEY_IV_LEN 16
+
+#define IEEE8021X_KEY_INDEX_FLAG 0x80
+#define IEEE8021X_KEY_INDEX_MASK 0x03
+
+#ifdef _MSC_VER
+#pragma pack(push, 1)
+#endif /* _MSC_VER */
+
+struct ieee802_1x_eapol_key {
+ u8 type;
+ /* Note: key_length is unaligned */
+ u8 key_length[2];
+ /* does not repeat within the life of the keying material used to
+ * encrypt the Key field; 64-bit NTP timestamp MAY be used here */
+ u8 replay_counter[IEEE8021X_REPLAY_COUNTER_LEN];
+ u8 key_iv[IEEE8021X_KEY_IV_LEN]; /* cryptographically random number */
+ u8 key_index; /* key flag in the most significant bit:
+ * 0 = broadcast (default key),
+ * 1 = unicast (key mapping key); key index is in the
+ * 7 least significant bits */
+ /* HMAC-MD5 message integrity check computed with MS-MPPE-Send-Key as
+ * the key */
+ u8 key_signature[IEEE8021X_KEY_SIGN_LEN];
+
+ /* followed by key: if packet body length = 44 + key length, then the
+ * key field (of key_length bytes) contains the key in encrypted form;
+ * if packet body length = 44, key field is absent and key_length
+ * represents the number of least significant octets from
+ * MS-MPPE-Send-Key attribute to be used as the keying material;
+ * RC4 key used in encryption = Key-IV + MS-MPPE-Recv-Key */
+} STRUCT_PACKED;
+
+#ifdef _MSC_VER
+#pragma pack(pop)
+#endif /* _MSC_VER */
+
#endif /* EAPOL_COMMON_H */
diff --git a/contrib/wpa/src/common/gas.c b/contrib/wpa/src/common/gas.c
new file mode 100644
index 0000000..cff9254
--- /dev/null
+++ b/contrib/wpa/src/common/gas.c
@@ -0,0 +1,273 @@
+/*
+ * Generic advertisement service (GAS) (IEEE 802.11u)
+ * Copyright (c) 2009, Atheros Communications
+ * Copyright (c) 2011-2012, Qualcomm Atheros
+ *
+ * This software may be distributed under the terms of the BSD license.
+ * See README for more details.
+ */
+
+#include "includes.h"
+
+#include "common.h"
+#include "ieee802_11_defs.h"
+#include "gas.h"
+
+
+static struct wpabuf *
+gas_build_req(u8 action, u8 dialog_token, size_t size)
+{
+ struct wpabuf *buf;
+
+ buf = wpabuf_alloc(100 + size);
+ if (buf == NULL)
+ return NULL;
+
+ wpabuf_put_u8(buf, WLAN_ACTION_PUBLIC);
+ wpabuf_put_u8(buf, action);
+ wpabuf_put_u8(buf, dialog_token);
+
+ return buf;
+}
+
+
+struct wpabuf * gas_build_initial_req(u8 dialog_token, size_t size)
+{
+ return gas_build_req(WLAN_PA_GAS_INITIAL_REQ, dialog_token,
+ size);
+}
+
+
+struct wpabuf * gas_build_comeback_req(u8 dialog_token)
+{
+ return gas_build_req(WLAN_PA_GAS_COMEBACK_REQ, dialog_token, 0);
+}
+
+
+static struct wpabuf *
+gas_build_resp(u8 action, u8 dialog_token, u16 status_code, u8 frag_id,
+ u8 more, u16 comeback_delay, size_t size)
+{
+ struct wpabuf *buf;
+
+ buf = wpabuf_alloc(100 + size);
+ if (buf == NULL)
+ return NULL;
+
+ wpabuf_put_u8(buf, WLAN_ACTION_PUBLIC);
+ wpabuf_put_u8(buf, action);
+ wpabuf_put_u8(buf, dialog_token);
+ wpabuf_put_le16(buf, status_code);
+ if (action == WLAN_PA_GAS_COMEBACK_RESP)
+ wpabuf_put_u8(buf, frag_id | (more ? 0x80 : 0));
+ wpabuf_put_le16(buf, comeback_delay);
+
+ return buf;
+}
+
+
+struct wpabuf *
+gas_build_initial_resp(u8 dialog_token, u16 status_code, u16 comeback_delay,
+ size_t size)
+{
+ return gas_build_resp(WLAN_PA_GAS_INITIAL_RESP, dialog_token,
+ status_code, 0, 0, comeback_delay, size);
+}
+
+
+static struct wpabuf *
+gas_build_comeback_resp(u8 dialog_token, u16 status_code, u8 frag_id, u8 more,
+ u16 comeback_delay, size_t size)
+{
+ return gas_build_resp(WLAN_PA_GAS_COMEBACK_RESP, dialog_token,
+ status_code, frag_id, more, comeback_delay,
+ size);
+}
+
+
+/**
+ * gas_add_adv_proto_anqp - Add an Advertisement Protocol element
+ * @buf: Buffer to which the element is added
+ * @query_resp_len_limit: Query Response Length Limit in units of 256 octets
+ * @pame_bi: Pre-Association Message Exchange BSSID Independent (0/1)
+ *
+ *
+ * @query_resp_len_limit is 0 for request and 1-0x7f for response. 0x7f means
+ * that the maximum limit is determined by the maximum allowable number of
+ * fragments in the GAS Query Response Fragment ID.
+ */
+static void gas_add_adv_proto_anqp(struct wpabuf *buf, u8 query_resp_len_limit,
+ u8 pame_bi)
+{
+ /* Advertisement Protocol IE */
+ wpabuf_put_u8(buf, WLAN_EID_ADV_PROTO);
+ wpabuf_put_u8(buf, 2); /* Length */
+ wpabuf_put_u8(buf, (query_resp_len_limit & 0x7f) |
+ (pame_bi ? 0x80 : 0));
+ /* Advertisement Protocol */
+ wpabuf_put_u8(buf, ACCESS_NETWORK_QUERY_PROTOCOL);
+}
+
+
+struct wpabuf * gas_anqp_build_initial_req(u8 dialog_token, size_t size)
+{
+ struct wpabuf *buf;
+
+ buf = gas_build_initial_req(dialog_token, 4 + size);
+ if (buf == NULL)
+ return NULL;
+
+ gas_add_adv_proto_anqp(buf, 0, 0);
+
+ wpabuf_put(buf, 2); /* Query Request Length to be filled */
+
+ return buf;
+}
+
+
+struct wpabuf * gas_anqp_build_initial_resp(u8 dialog_token, u16 status_code,
+ u16 comeback_delay, size_t size)
+{
+ struct wpabuf *buf;
+
+ buf = gas_build_initial_resp(dialog_token, status_code, comeback_delay,
+ 4 + size);
+ if (buf == NULL)
+ return NULL;
+
+ gas_add_adv_proto_anqp(buf, 0x7f, 0);
+
+ wpabuf_put(buf, 2); /* Query Response Length to be filled */
+
+ return buf;
+}
+
+
+struct wpabuf * gas_anqp_build_initial_resp_buf(u8 dialog_token,
+ u16 status_code,
+ u16 comeback_delay,
+ struct wpabuf *payload)
+{
+ struct wpabuf *buf;
+
+ buf = gas_anqp_build_initial_resp(dialog_token, status_code,
+ comeback_delay,
+ payload ? wpabuf_len(payload) : 0);
+ if (buf == NULL)
+ return NULL;
+
+ if (payload)
+ wpabuf_put_buf(buf, payload);
+
+ gas_anqp_set_len(buf);
+
+ return buf;
+}
+
+
+struct wpabuf * gas_anqp_build_comeback_resp(u8 dialog_token, u16 status_code,
+ u8 frag_id, u8 more,
+ u16 comeback_delay, size_t size)
+{
+ struct wpabuf *buf;
+
+ buf = gas_build_comeback_resp(dialog_token, status_code,
+ frag_id, more, comeback_delay, 4 + size);
+ if (buf == NULL)
+ return NULL;
+
+ gas_add_adv_proto_anqp(buf, 0x7f, 0);
+
+ wpabuf_put(buf, 2); /* Query Response Length to be filled */
+
+ return buf;
+}
+
+
+struct wpabuf * gas_anqp_build_comeback_resp_buf(u8 dialog_token,
+ u16 status_code,
+ u8 frag_id, u8 more,
+ u16 comeback_delay,
+ struct wpabuf *payload)
+{
+ struct wpabuf *buf;
+
+ buf = gas_anqp_build_comeback_resp(dialog_token, status_code, frag_id,
+ more, comeback_delay,
+ payload ? wpabuf_len(payload) : 0);
+ if (buf == NULL)
+ return NULL;
+
+ if (payload)
+ wpabuf_put_buf(buf, payload);
+
+ gas_anqp_set_len(buf);
+
+ return buf;
+}
+
+
+/**
+ * gas_anqp_set_len - Set Query Request/Response Length
+ * @buf: GAS message
+ *
+ * This function is used to update the Query Request/Response Length field once
+ * the payload has been filled.
+ */
+void gas_anqp_set_len(struct wpabuf *buf)
+{
+ u8 action;
+ size_t offset;
+ u8 *len;
+
+ if (buf == NULL || wpabuf_len(buf) < 2)
+ return;
+
+ action = *(wpabuf_head_u8(buf) + 1);
+ switch (action) {
+ case WLAN_PA_GAS_INITIAL_REQ:
+ offset = 3 + 4;
+ break;
+ case WLAN_PA_GAS_INITIAL_RESP:
+ offset = 7 + 4;
+ break;
+ case WLAN_PA_GAS_COMEBACK_RESP:
+ offset = 8 + 4;
+ break;
+ default:
+ return;
+ }
+
+ if (wpabuf_len(buf) < offset + 2)
+ return;
+
+ len = wpabuf_mhead_u8(buf) + offset;
+ WPA_PUT_LE16(len, (u8 *) wpabuf_put(buf, 0) - len - 2);
+}
+
+
+/**
+ * gas_anqp_add_element - Add ANQP element header
+ * @buf: GAS message
+ * @info_id: ANQP Info ID
+ * Returns: Pointer to the Length field for gas_anqp_set_element_len()
+ */
+u8 * gas_anqp_add_element(struct wpabuf *buf, u16 info_id)
+{
+ wpabuf_put_le16(buf, info_id);
+ return wpabuf_put(buf, 2); /* Length to be filled */
+}
+
+
+/**
+ * gas_anqp_set_element_len - Update ANQP element Length field
+ * @buf: GAS message
+ * @len_pos: Length field position from gas_anqp_add_element()
+ *
+ * This function is called after the ANQP element payload has been added to the
+ * buffer.
+ */
+void gas_anqp_set_element_len(struct wpabuf *buf, u8 *len_pos)
+{
+ WPA_PUT_LE16(len_pos, (u8 *) wpabuf_put(buf, 0) - len_pos - 2);
+}
diff --git a/contrib/wpa/src/common/gas.h b/contrib/wpa/src/common/gas.h
new file mode 100644
index 0000000..306adc5
--- /dev/null
+++ b/contrib/wpa/src/common/gas.h
@@ -0,0 +1,37 @@
+/*
+ * Generic advertisement service (GAS) (IEEE 802.11u)
+ * Copyright (c) 2009, Atheros Communications
+ * Copyright (c) 2011-2012, Qualcomm Atheros
+ *
+ * This software may be distributed under the terms of the BSD license.
+ * See README for more details.
+ */
+
+#ifndef GAS_H
+#define GAS_H
+
+struct wpabuf * gas_build_initial_req(u8 dialog_token, size_t size);
+struct wpabuf * gas_build_comeback_req(u8 dialog_token);
+struct wpabuf * gas_build_initial_resp(u8 dialog_token, u16 status_code,
+ u16 comeback_delay, size_t size);
+struct wpabuf * gas_anqp_build_initial_req(u8 dialog_token, size_t size);
+struct wpabuf * gas_anqp_build_initial_resp(u8 dialog_token, u16 status_code,
+ u16 comeback_delay, size_t size);
+struct wpabuf * gas_anqp_build_initial_resp_buf(u8 dialog_token,
+ u16 status_code,
+ u16 comeback_delay,
+ struct wpabuf *payload);
+struct wpabuf * gas_anqp_build_comeback_resp(u8 dialog_token, u16 status_code,
+ u8 frag_id, u8 more,
+ u16 comeback_delay, size_t size);
+struct wpabuf * gas_anqp_build_comeback_resp_buf(u8 dialog_token,
+ u16 status_code,
+ u8 frag_id, u8 more,
+ u16 comeback_delay,
+ struct wpabuf *payload);
+void gas_anqp_set_len(struct wpabuf *buf);
+
+u8 * gas_anqp_add_element(struct wpabuf *buf, u16 info_id);
+void gas_anqp_set_element_len(struct wpabuf *buf, u8 *len_pos);
+
+#endif /* GAS_H */
diff --git a/contrib/wpa/src/common/ieee802_11_common.c b/contrib/wpa/src/common/ieee802_11_common.c
index 96ef5b6..98fadda 100644
--- a/contrib/wpa/src/common/ieee802_11_common.c
+++ b/contrib/wpa/src/common/ieee802_11_common.c
@@ -1,15 +1,9 @@
/*
* IEEE 802.11 Common routines
- * Copyright (c) 2002-2009, Jouni Malinen <j@w1.fi>
+ * Copyright (c) 2002-2012, Jouni Malinen <j@w1.fi>
*
- * This program is free software; you can redistribute it and/or modify
- * it under the terms of the GNU General Public License version 2 as
- * published by the Free Software Foundation.
- *
- * Alternatively, this software may be distributed under the terms of BSD
- * license.
- *
- * See README and COPYING for more details.
+ * This software may be distributed under the terms of the BSD license.
+ * See README for more details.
*/
#include "includes.h"
@@ -75,7 +69,7 @@ static int ieee802_11_parse_vendor_specific(const u8 *pos, size_t elen,
elems->wmm_tspec_len = elen;
break;
default:
- wpa_printf(MSG_MSGDUMP, "unknown WMM "
+ wpa_printf(MSG_EXCESSIVE, "unknown WMM "
"information element ignored "
"(subtype=%d len=%lu)",
pos[4], (unsigned long) elen);
@@ -88,7 +82,33 @@ static int ieee802_11_parse_vendor_specific(const u8 *pos, size_t elen,
elems->wps_ie_len = elen;
break;
default:
- wpa_printf(MSG_MSGDUMP, "Unknown Microsoft "
+ wpa_printf(MSG_EXCESSIVE, "Unknown Microsoft "
+ "information element ignored "
+ "(type=%d len=%lu)",
+ pos[3], (unsigned long) elen);
+ return -1;
+ }
+ break;
+
+ case OUI_WFA:
+ switch (pos[3]) {
+ case P2P_OUI_TYPE:
+ /* Wi-Fi Alliance - P2P IE */
+ elems->p2p = pos;
+ elems->p2p_len = elen;
+ break;
+ case WFD_OUI_TYPE:
+ /* Wi-Fi Alliance - WFD IE */
+ elems->wfd = pos;
+ elems->wfd_len = elen;
+ break;
+ case HS20_INDICATION_OUI_TYPE:
+ /* Hotspot 2.0 */
+ elems->hs20 = pos;
+ elems->hs20_len = elen;
+ break;
+ default:
+ wpa_printf(MSG_MSGDUMP, "Unknown WFA "
"information element ignored "
"(type=%d len=%lu)\n",
pos[3], (unsigned long) elen);
@@ -103,18 +123,18 @@ static int ieee802_11_parse_vendor_specific(const u8 *pos, size_t elen,
elems->vendor_ht_cap_len = elen;
break;
default:
- wpa_printf(MSG_MSGDUMP, "Unknown Broadcom "
+ wpa_printf(MSG_EXCESSIVE, "Unknown Broadcom "
"information element ignored "
- "(type=%d len=%lu)\n",
+ "(type=%d len=%lu)",
pos[3], (unsigned long) elen);
return -1;
}
break;
default:
- wpa_printf(MSG_MSGDUMP, "unknown vendor specific information "
- "element ignored (vendor OUI %02x:%02x:%02x "
- "len=%lu)",
+ wpa_printf(MSG_EXCESSIVE, "unknown vendor specific "
+ "information element ignored (vendor OUI "
+ "%02x:%02x:%02x len=%lu)",
pos[0], pos[1], pos[2], (unsigned long) elen);
return -1;
}
@@ -238,6 +258,36 @@ ParseRes ieee802_11_parse_elems(const u8 *start, size_t len,
elems->ht_operation = pos;
elems->ht_operation_len = elen;
break;
+ case WLAN_EID_VHT_CAP:
+ elems->vht_capabilities = pos;
+ elems->vht_capabilities_len = elen;
+ break;
+ case WLAN_EID_VHT_OPERATION:
+ elems->vht_operation = pos;
+ elems->vht_operation_len = elen;
+ break;
+ case WLAN_EID_LINK_ID:
+ if (elen < 18)
+ break;
+ elems->link_id = pos;
+ break;
+ case WLAN_EID_INTERWORKING:
+ elems->interworking = pos;
+ elems->interworking_len = elen;
+ break;
+ case WLAN_EID_EXT_CAPAB:
+ elems->ext_capab = pos;
+ elems->ext_capab_len = elen;
+ break;
+ case WLAN_EID_BSS_MAX_IDLE_PERIOD:
+ if (elen < 3)
+ break;
+ elems->bss_max_idle_period = pos;
+ break;
+ case WLAN_EID_SSID_LIST:
+ elems->ssid_list = pos;
+ elems->ssid_list_len = elen;
+ break;
default:
unknown++;
if (!show_errors)
@@ -324,3 +374,115 @@ struct wpabuf * ieee802_11_vendor_ie_concat(const u8 *ies, size_t ies_len,
return buf;
}
+
+
+const u8 * get_hdr_bssid(const struct ieee80211_hdr *hdr, size_t len)
+{
+ u16 fc, type, stype;
+
+ /*
+ * PS-Poll frames are 16 bytes. All other frames are
+ * 24 bytes or longer.
+ */
+ if (len < 16)
+ return NULL;
+
+ fc = le_to_host16(hdr->frame_control);
+ type = WLAN_FC_GET_TYPE(fc);
+ stype = WLAN_FC_GET_STYPE(fc);
+
+ switch (type) {
+ case WLAN_FC_TYPE_DATA:
+ if (len < 24)
+ return NULL;
+ switch (fc & (WLAN_FC_FROMDS | WLAN_FC_TODS)) {
+ case WLAN_FC_FROMDS | WLAN_FC_TODS:
+ case WLAN_FC_TODS:
+ return hdr->addr1;
+ case WLAN_FC_FROMDS:
+ return hdr->addr2;
+ default:
+ return NULL;
+ }
+ case WLAN_FC_TYPE_CTRL:
+ if (stype != WLAN_FC_STYPE_PSPOLL)
+ return NULL;
+ return hdr->addr1;
+ case WLAN_FC_TYPE_MGMT:
+ return hdr->addr3;
+ default:
+ return NULL;
+ }
+}
+
+
+int hostapd_config_wmm_ac(struct hostapd_wmm_ac_params wmm_ac_params[],
+ const char *name, const char *val)
+{
+ int num, v;
+ const char *pos;
+ struct hostapd_wmm_ac_params *ac;
+
+ /* skip 'wme_ac_' or 'wmm_ac_' prefix */
+ pos = name + 7;
+ if (os_strncmp(pos, "be_", 3) == 0) {
+ num = 0;
+ pos += 3;
+ } else if (os_strncmp(pos, "bk_", 3) == 0) {
+ num = 1;
+ pos += 3;
+ } else if (os_strncmp(pos, "vi_", 3) == 0) {
+ num = 2;
+ pos += 3;
+ } else if (os_strncmp(pos, "vo_", 3) == 0) {
+ num = 3;
+ pos += 3;
+ } else {
+ wpa_printf(MSG_ERROR, "Unknown WMM name '%s'", pos);
+ return -1;
+ }
+
+ ac = &wmm_ac_params[num];
+
+ if (os_strcmp(pos, "aifs") == 0) {
+ v = atoi(val);
+ if (v < 1 || v > 255) {
+ wpa_printf(MSG_ERROR, "Invalid AIFS value %d", v);
+ return -1;
+ }
+ ac->aifs = v;
+ } else if (os_strcmp(pos, "cwmin") == 0) {
+ v = atoi(val);
+ if (v < 0 || v > 12) {
+ wpa_printf(MSG_ERROR, "Invalid cwMin value %d", v);
+ return -1;
+ }
+ ac->cwmin = v;
+ } else if (os_strcmp(pos, "cwmax") == 0) {
+ v = atoi(val);
+ if (v < 0 || v > 12) {
+ wpa_printf(MSG_ERROR, "Invalid cwMax value %d", v);
+ return -1;
+ }
+ ac->cwmax = v;
+ } else if (os_strcmp(pos, "txop_limit") == 0) {
+ v = atoi(val);
+ if (v < 0 || v > 0xffff) {
+ wpa_printf(MSG_ERROR, "Invalid txop value %d", v);
+ return -1;
+ }
+ ac->txop_limit = v;
+ } else if (os_strcmp(pos, "acm") == 0) {
+ v = atoi(val);
+ if (v < 0 || v > 1) {
+ wpa_printf(MSG_ERROR, "Invalid acm value %d", v);
+ return -1;
+ }
+ ac->admission_control_mandatory = v;
+ } else {
+ wpa_printf(MSG_ERROR, "Unknown wmm_ac_ field '%s'", pos);
+ return -1;
+ }
+
+ return 0;
+}
diff --git a/contrib/wpa/src/common/ieee802_11_common.h b/contrib/wpa/src/common/ieee802_11_common.h
index 4a4f5a7..55fa49d 100644
--- a/contrib/wpa/src/common/ieee802_11_common.h
+++ b/contrib/wpa/src/common/ieee802_11_common.h
@@ -1,15 +1,9 @@
/*
* IEEE 802.11 Common routines
- * Copyright (c) 2002-2009, Jouni Malinen <j@w1.fi>
+ * Copyright (c) 2002-2012, Jouni Malinen <j@w1.fi>
*
- * This program is free software; you can redistribute it and/or modify
- * it under the terms of the GNU General Public License version 2 as
- * published by the Free Software Foundation.
- *
- * Alternatively, this software may be distributed under the terms of BSD
- * license.
- *
- * See README and COPYING for more details.
+ * This software may be distributed under the terms of the BSD license.
+ * See README for more details.
*/
#ifndef IEEE802_11_COMMON_H
@@ -39,7 +33,17 @@ struct ieee802_11_elems {
const u8 *timeout_int;
const u8 *ht_capabilities;
const u8 *ht_operation;
+ const u8 *vht_capabilities;
+ const u8 *vht_operation;
const u8 *vendor_ht_cap;
+ const u8 *p2p;
+ const u8 *wfd;
+ const u8 *link_id;
+ const u8 *interworking;
+ const u8 *hs20;
+ const u8 *ext_capab;
+ const u8 *bss_max_idle_period;
+ const u8 *ssid_list;
u8 ssid_len;
u8 supp_rates_len;
@@ -63,7 +67,15 @@ struct ieee802_11_elems {
u8 timeout_int_len;
u8 ht_capabilities_len;
u8 ht_operation_len;
+ u8 vht_capabilities_len;
+ u8 vht_operation_len;
u8 vendor_ht_cap_len;
+ u8 p2p_len;
+ u8 wfd_len;
+ u8 interworking_len;
+ u8 hs20_len;
+ u8 ext_capab_len;
+ u8 ssid_list_len;
};
typedef enum { ParseOK = 0, ParseUnknown = 1, ParseFailed = -1 } ParseRes;
@@ -74,5 +86,18 @@ ParseRes ieee802_11_parse_elems(const u8 *start, size_t len,
int ieee802_11_ie_count(const u8 *ies, size_t ies_len);
struct wpabuf * ieee802_11_vendor_ie_concat(const u8 *ies, size_t ies_len,
u32 oui_type);
+struct ieee80211_hdr;
+const u8 * get_hdr_bssid(const struct ieee80211_hdr *hdr, size_t len);
+
+struct hostapd_wmm_ac_params {
+ int cwmin;
+ int cwmax;
+ int aifs;
+ int txop_limit; /* in units of 32us */
+ int admission_control_mandatory;
+};
+
+int hostapd_config_wmm_ac(struct hostapd_wmm_ac_params wmm_ac_params[],
+ const char *name, const char *val);
#endif /* IEEE802_11_COMMON_H */
diff --git a/contrib/wpa/src/common/ieee802_11_defs.h b/contrib/wpa/src/common/ieee802_11_defs.h
index 4881e39..e873545 100644
--- a/contrib/wpa/src/common/ieee802_11_defs.h
+++ b/contrib/wpa/src/common/ieee802_11_defs.h
@@ -3,14 +3,8 @@
* Copyright (c) 2002-2009, Jouni Malinen <j@w1.fi>
* Copyright (c) 2007-2008 Intel Corporation
*
- * This program is free software; you can redistribute it and/or modify
- * it under the terms of the GNU General Public License version 2 as
- * published by the Free Software Foundation.
- *
- * Alternatively, this software may be distributed under the terms of BSD
- * license.
- *
- * See README and COPYING for more details.
+ * This software may be distributed under the terms of the BSD license.
+ * See README for more details.
*/
#ifndef IEEE802_11_DEFS_H
@@ -71,11 +65,18 @@
#define WLAN_FC_STYPE_CFPOLL 6
#define WLAN_FC_STYPE_CFACKPOLL 7
#define WLAN_FC_STYPE_QOS_DATA 8
+#define WLAN_FC_STYPE_QOS_DATA_CFACK 9
+#define WLAN_FC_STYPE_QOS_DATA_CFPOLL 10
+#define WLAN_FC_STYPE_QOS_DATA_CFACKPOLL 11
+#define WLAN_FC_STYPE_QOS_NULL 12
+#define WLAN_FC_STYPE_QOS_CFPOLL 14
+#define WLAN_FC_STYPE_QOS_CFACKPOLL 15
/* Authentication algorithms */
#define WLAN_AUTH_OPEN 0
#define WLAN_AUTH_SHARED_KEY 1
#define WLAN_AUTH_FT 2
+#define WLAN_AUTH_SAE 3
#define WLAN_AUTH_LEAP 128
#define WLAN_AUTH_CHALLENGE_LEN 128
@@ -95,6 +96,11 @@
/* Status codes (IEEE 802.11-2007, 7.3.1.9, Table 7-23) */
#define WLAN_STATUS_SUCCESS 0
#define WLAN_STATUS_UNSPECIFIED_FAILURE 1
+#define WLAN_STATUS_TDLS_WAKEUP_ALTERNATE 2
+#define WLAN_STATUS_TDLS_WAKEUP_REJECT 3
+#define WLAN_STATUS_SECURITY_DISABLED 5
+#define WLAN_STATUS_UNACCEPTABLE_LIFETIME 6
+#define WLAN_STATUS_NOT_IN_SAME_BSS 7
#define WLAN_STATUS_CAPS_UNSUPPORTED 10
#define WLAN_STATUS_REASSOC_NO_ASSOC 11
#define WLAN_STATUS_ASSOC_DENIED_UNSPEC 12
@@ -114,9 +120,10 @@
#define WLAN_STATUS_SUPPORTED_CHANNEL_NOT_VALID 24
/* IEEE 802.11g */
#define WLAN_STATUS_ASSOC_DENIED_NO_SHORT_SLOT_TIME 25
-#define WLAN_STATUS_ASSOC_DENIED_NO_ER_PBCC 26
-#define WLAN_STATUS_ASSOC_DENIED_NO_DSSS_OFDM 27
+#define WLAN_STATUS_ASSOC_DENIED_NO_DSSS_OFDM 26
+#define WLAN_STATUS_ASSOC_DENIED_NO_HT 27
#define WLAN_STATUS_R0KH_UNREACHABLE 28
+#define WLAN_STATUS_ASSOC_DENIED_NO_PCO 29
/* IEEE 802.11w */
#define WLAN_STATUS_ASSOC_REJECTED_TEMPORARILY 30
#define WLAN_STATUS_ROBUST_MGMT_FRAME_POLICY_VIOLATION 31
@@ -141,6 +148,19 @@
#define WLAN_STATUS_INVALID_PMKID 53
#define WLAN_STATUS_INVALID_MDIE 54
#define WLAN_STATUS_INVALID_FTIE 55
+#define WLAN_STATUS_GAS_ADV_PROTO_NOT_SUPPORTED 59
+#define WLAN_STATUS_NO_OUTSTANDING_GAS_REQ 60
+#define WLAN_STATUS_GAS_RESP_NOT_RECEIVED 61
+#define WLAN_STATUS_STA_TIMED_OUT_WAITING_FOR_GAS_RESP 62
+#define WLAN_STATUS_GAS_RESP_LARGER_THAN_LIMIT 63
+#define WLAN_STATUS_REQ_REFUSED_HOME 64
+#define WLAN_STATUS_ADV_SRV_UNREACHABLE 65
+#define WLAN_STATUS_REQ_REFUSED_SSPN 67
+#define WLAN_STATUS_REQ_REFUSED_UNAUTH_ACCESS 68
+#define WLAN_STATUS_INVALID_RSNIE 72
+#define WLAN_STATUS_ANTI_CLOGGING_TOKEN_REQ 76
+#define WLAN_STATUS_FINITE_CYCLIC_GROUP_NOT_SUPPORTED 77
+#define WLAN_STATUS_TRANSMISSION_FAILURE 79
/* Reason codes (IEEE 802.11-2007, 7.3.1.7, Table 7-22) */
#define WLAN_REASON_UNSPECIFIED 1
@@ -168,6 +188,10 @@
#define WLAN_REASON_INVALID_RSN_IE_CAPAB 22
#define WLAN_REASON_IEEE_802_1X_AUTH_FAILED 23
#define WLAN_REASON_CIPHER_SUITE_REJECTED 24
+#define WLAN_REASON_TDLS_TEARDOWN_UNREACHABLE 25
+#define WLAN_REASON_TDLS_TEARDOWN_UNSPECIFIED 26
+/* IEEE 802.11e */
+#define WLAN_REASON_DISASSOC_LOW_ACK 34
/* Information Element IDs */
@@ -202,10 +226,33 @@
#define WLAN_EID_RIC_DATA 57
#define WLAN_EID_HT_OPERATION 61
#define WLAN_EID_SECONDARY_CHANNEL_OFFSET 62
+#define WLAN_EID_WAPI 68
+#define WLAN_EID_TIME_ADVERTISEMENT 69
#define WLAN_EID_20_40_BSS_COEXISTENCE 72
#define WLAN_EID_20_40_BSS_INTOLERANT 73
#define WLAN_EID_OVERLAPPING_BSS_SCAN_PARAMS 74
#define WLAN_EID_MMIE 76
+#define WLAN_EID_SSID_LIST 84
+#define WLAN_EID_BSS_MAX_IDLE_PERIOD 90
+#define WLAN_EID_TFS_REQ 91
+#define WLAN_EID_TFS_RESP 92
+#define WLAN_EID_WNMSLEEP 93
+#define WLAN_EID_TIME_ZONE 98
+#define WLAN_EID_LINK_ID 101
+#define WLAN_EID_INTERWORKING 107
+#define WLAN_EID_ADV_PROTO 108
+#define WLAN_EID_ROAMING_CONSORTIUM 111
+#define WLAN_EID_EXT_CAPAB 127
+#define WLAN_EID_CCKM 156
+#define WLAN_EID_VHT_CAP 191
+#define WLAN_EID_VHT_OPERATION 192
+#define WLAN_EID_VHT_EXTENDED_BSS_LOAD 193
+#define WLAN_EID_VHT_WIDE_BW_CHSWITCH 194
+#define WLAN_EID_VHT_TRANSMIT_POWER_ENVELOPE 195
+#define WLAN_EID_VHT_CHANNEL_SWITCH_WRAPPER 196
+#define WLAN_EID_VHT_AID 197
+#define WLAN_EID_VHT_QUIET_CHANNEL 198
+#define WLAN_EID_VHT_OPERATING_MODE_NOTIFICATION 199
#define WLAN_EID_VENDOR_SPECIFIC 221
@@ -219,7 +266,20 @@
#define WLAN_ACTION_FT 6
#define WLAN_ACTION_HT 7
#define WLAN_ACTION_SA_QUERY 8
+#define WLAN_ACTION_WNM 10
+#define WLAN_ACTION_UNPROTECTED_WNM 11
+#define WLAN_ACTION_TDLS 12
#define WLAN_ACTION_WMM 17 /* WMM Specification 1.1 */
+#define WLAN_ACTION_VENDOR_SPECIFIC 127
+
+/* Public action codes */
+#define WLAN_PA_20_40_BSS_COEX 0
+#define WLAN_PA_VENDOR_SPECIFIC 9
+#define WLAN_PA_GAS_INITIAL_REQ 10
+#define WLAN_PA_GAS_INITIAL_RESP 11
+#define WLAN_PA_GAS_COMEBACK_REQ 12
+#define WLAN_PA_GAS_COMEBACK_RESP 13
+#define WLAN_TDLS_DISCOVERY_RESPONSE 14
/* SA Query Action frame (IEEE 802.11w/D8.0, 7.4.9) */
#define WLAN_SA_QUERY_REQUEST 0
@@ -227,11 +287,99 @@
#define WLAN_SA_QUERY_TR_ID_LEN 2
+/* TDLS action codes */
+#define WLAN_TDLS_SETUP_REQUEST 0
+#define WLAN_TDLS_SETUP_RESPONSE 1
+#define WLAN_TDLS_SETUP_CONFIRM 2
+#define WLAN_TDLS_TEARDOWN 3
+#define WLAN_TDLS_PEER_TRAFFIC_INDICATION 4
+#define WLAN_TDLS_CHANNEL_SWITCH_REQUEST 5
+#define WLAN_TDLS_CHANNEL_SWITCH_RESPONSE 6
+#define WLAN_TDLS_PEER_PSM_REQUEST 7
+#define WLAN_TDLS_PEER_PSM_RESPONSE 8
+#define WLAN_TDLS_PEER_TRAFFIC_RESPONSE 9
+#define WLAN_TDLS_DISCOVERY_REQUEST 10
+
/* Timeout Interval Type */
#define WLAN_TIMEOUT_REASSOC_DEADLINE 1
#define WLAN_TIMEOUT_KEY_LIFETIME 2
#define WLAN_TIMEOUT_ASSOC_COMEBACK 3
+/* Interworking element (IEEE 802.11u) - Access Network Options */
+#define INTERWORKING_ANO_ACCESS_NETWORK_MASK 0x0f
+#define INTERWORKING_ANO_INTERNET 0x10
+#define INTERWORKING_ANO_ASRA 0x20
+#define INTERWORKING_ANO_ESR 0x40
+#define INTERWORKING_ANO_UESA 0x80
+
+#define INTERWORKING_ANT_PRIVATE 0
+#define INTERWORKING_ANT_PRIVATE_WITH_GUEST 1
+#define INTERWORKING_ANT_CHARGEABLE_PUBLIC 2
+#define INTERWORKING_ANT_FREE_PUBLIC 3
+#define INTERWORKING_ANT_PERSONAL_DEVICE 4
+#define INTERWORKING_ANT_EMERGENCY_SERVICES 5
+#define INTERWORKING_ANT_TEST 6
+#define INTERWORKING_ANT_WILDCARD 15
+
+/* Advertisement Protocol ID definitions (IEEE Std 802.11u-2011) */
+enum adv_proto_id {
+ ACCESS_NETWORK_QUERY_PROTOCOL = 0,
+ MIH_INFO_SERVICE = 1,
+ MIH_CMD_AND_EVENT_DISCOVERY = 2,
+ EMERGENCY_ALERT_SYSTEM = 3,
+ ADV_PROTO_VENDOR_SPECIFIC = 221
+};
+
+/* Access Network Query Protocol info ID definitions (IEEE Std 802.11u-2011) */
+enum anqp_info_id {
+ ANQP_QUERY_LIST = 256,
+ ANQP_CAPABILITY_LIST = 257,
+ ANQP_VENUE_NAME = 258,
+ ANQP_EMERGENCY_CALL_NUMBER = 259,
+ ANQP_NETWORK_AUTH_TYPE = 260,
+ ANQP_ROAMING_CONSORTIUM = 261,
+ ANQP_IP_ADDR_TYPE_AVAILABILITY = 262,
+ ANQP_NAI_REALM = 263,
+ ANQP_3GPP_CELLULAR_NETWORK = 264,
+ ANQP_AP_GEOSPATIAL_LOCATION = 265,
+ ANQP_AP_CIVIC_LOCATION = 266,
+ ANQP_AP_LOCATION_PUBLIC_URI = 267,
+ ANQP_DOMAIN_NAME = 268,
+ ANQP_EMERGENCY_ALERT_URI = 269,
+ ANQP_EMERGENCY_NAI = 271,
+ ANQP_VENDOR_SPECIFIC = 56797
+};
+
+/* NAI Realm list - EAP Method subfield - Authentication Parameter ID */
+enum nai_realm_eap_auth_param {
+ NAI_REALM_EAP_AUTH_EXPANDED_EAP_METHOD = 1,
+ NAI_REALM_EAP_AUTH_NON_EAP_INNER_AUTH = 2,
+ NAI_REALM_EAP_AUTH_INNER_AUTH_EAP_METHOD = 3,
+ NAI_REALM_EAP_AUTH_EXPANDED_INNER_EAP_METHOD = 4,
+ NAI_REALM_EAP_AUTH_CRED_TYPE = 5,
+ NAI_REALM_EAP_AUTH_TUNNELED_CRED_TYPE = 6,
+ NAI_REALM_EAP_AUTH_VENDOR_SPECIFIC = 221
+};
+
+enum nai_realm_eap_auth_inner_non_eap {
+ NAI_REALM_INNER_NON_EAP_PAP = 1,
+ NAI_REALM_INNER_NON_EAP_CHAP = 2,
+ NAI_REALM_INNER_NON_EAP_MSCHAP = 3,
+ NAI_REALM_INNER_NON_EAP_MSCHAPV2 = 4
+};
+
+enum nai_realm_eap_cred_type {
+ NAI_REALM_CRED_TYPE_SIM = 1,
+ NAI_REALM_CRED_TYPE_USIM = 2,
+ NAI_REALM_CRED_TYPE_NFC_SECURE_ELEMENT = 3,
+ NAI_REALM_CRED_TYPE_HARDWARE_TOKEN = 4,
+ NAI_REALM_CRED_TYPE_SOFTOKEN = 5,
+ NAI_REALM_CRED_TYPE_CERTIFICATE = 6,
+ NAI_REALM_CRED_TYPE_USERNAME_PASSWORD = 7,
+ NAI_REALM_CRED_TYPE_NONE = 8,
+ NAI_REALM_CRED_TYPE_ANONYMOUS = 9,
+ NAI_REALM_CRED_TYPE_VENDOR_SPECIFIC = 10
+};
#ifdef _MSC_VER
#pragma pack(push, 1)
@@ -273,6 +421,7 @@ struct ieee80211_mgmt {
} STRUCT_PACKED auth;
struct {
le16 reason_code;
+ u8 variable[0];
} STRUCT_PACKED deauth;
struct {
le16 capab_info;
@@ -296,6 +445,7 @@ struct ieee80211_mgmt {
} STRUCT_PACKED reassoc_req;
struct {
le16 reason_code;
+ u8 variable[0];
} STRUCT_PACKED disassoc;
struct {
u8 timestamp[8];
@@ -355,12 +505,58 @@ struct ieee80211_mgmt {
u8 action; /* */
u8 trans_id[WLAN_SA_QUERY_TR_ID_LEN];
} STRUCT_PACKED sa_query_resp;
+ struct {
+ u8 action;
+ u8 dialogtoken;
+ u8 variable[0];
+ } STRUCT_PACKED wnm_sleep_req;
+ struct {
+ u8 action;
+ u8 dialogtoken;
+ le16 keydata_len;
+ u8 variable[0];
+ } STRUCT_PACKED wnm_sleep_resp;
+ struct {
+ u8 action;
+ u8 variable[0];
+ } STRUCT_PACKED public_action;
+ struct {
+ u8 action; /* 9 */
+ u8 oui[3];
+ /* Vendor-specific content */
+ u8 variable[0];
+ } STRUCT_PACKED vs_public_action;
+ struct {
+ u8 action; /* 7 */
+ u8 dialog_token;
+ u8 req_mode;
+ le16 disassoc_timer;
+ u8 validity_interval;
+ /* BSS Termination Duration (optional),
+ * Session Information URL (optional),
+ * BSS Transition Candidate List
+ * Entries */
+ u8 variable[0];
+ } STRUCT_PACKED bss_tm_req;
+ struct {
+ u8 action; /* 8 */
+ u8 dialog_token;
+ u8 status_code;
+ u8 bss_termination_delay;
+ /* Target BSSID (optional),
+ * BSS Transition Candidate List
+ * Entries (optional) */
+ u8 variable[0];
+ } STRUCT_PACKED bss_tm_resp;
} u;
} STRUCT_PACKED action;
} u;
} STRUCT_PACKED;
+/* Rx MCS bitmask is in the first 77 bits of supported_mcs_set */
+#define IEEE80211_HT_MCS_MASK_LEN 10
+
struct ieee80211_ht_capabilities {
le16 ht_capabilities_info;
u8 a_mpdu_params;
@@ -379,6 +575,19 @@ struct ieee80211_ht_operation {
u8 basic_set[16];
} STRUCT_PACKED;
+
+struct ieee80211_vht_capabilities {
+ le32 vht_capabilities_info;
+ u8 vht_supported_mcs_set[8];
+} STRUCT_PACKED;
+
+struct ieee80211_vht_operation {
+ u8 vht_op_info_chwidth;
+ u8 vht_op_info_chan_center_freq_seg0_idx;
+ u8 vht_op_info_chan_center_freq_seg1_idx;
+ le16 vht_basic_mcs_set;
+} STRUCT_PACKED;
+
#ifdef _MSC_VER
#pragma pack(pop)
#endif /* _MSC_VER */
@@ -460,7 +669,7 @@ struct ieee80211_ht_operation {
#define OP_MODE_MIXED 3
#define HT_INFO_OPERATION_MODE_OP_MODE_MASK \
- ((le16) (0x0001 | 0x0002))
+ (0x0001 | 0x0002)
#define HT_INFO_OPERATION_MODE_OP_MODE_OFFSET 0
#define HT_INFO_OPERATION_MODE_NON_GF_DEVS_PRESENT ((u8) BIT(2))
#define HT_INFO_OPERATION_MODE_TRANSMIT_BURST_LIMIT ((u8) BIT(3))
@@ -473,11 +682,45 @@ struct ieee80211_ht_operation {
#define HT_INFO_STBC_PARAM_PCO_ACTIVE ((u16) BIT(10))
#define HT_INFO_STBC_PARAM_PCO_PHASE ((u16) BIT(11))
+#define BSS_MEMBERSHIP_SELECTOR_VHT_PHY 126
+#define BSS_MEMBERSHIP_SELECTOR_HT_PHY 127
+
+/* VHT Defines */
+#define VHT_CAP_MAX_MPDU_LENGTH_7991 ((u32) BIT(0))
+#define VHT_CAP_MAX_MPDU_LENGTH_11454 ((u32) BIT(1))
+#define VHT_CAP_SUPP_CHAN_WIDTH_160MHZ ((u32) BIT(2))
+#define VHT_CAP_SUPP_CHAN_WIDTH_160_80PLUS80MHZ ((u32) BIT(3))
+#define VHT_CAP_RXLDPC ((u32) BIT(4))
+#define VHT_CAP_SHORT_GI_80 ((u32) BIT(5))
+#define VHT_CAP_SHORT_GI_160 ((u32) BIT(6))
+#define VHT_CAP_TXSTBC ((u32) BIT(7))
+#define VHT_CAP_RXSTBC_1 ((u32) BIT(8))
+#define VHT_CAP_RXSTBC_2 ((u32) BIT(9))
+#define VHT_CAP_RXSTBC_3 ((u32) BIT(8) | BIT(9))
+#define VHT_CAP_RXSTBC_4 ((u32) BIT(10))
+#define VHT_CAP_SU_BEAMFORMER_CAPABLE ((u32) BIT(11))
+#define VHT_CAP_SU_BEAMFORMEE_CAPABLE ((u32) BIT(12))
+#define VHT_CAP_BEAMFORMER_ANTENNAS_MAX ((u32) BIT(13) | BIT(14))
+#define VHT_CAP_SOUNDING_DIMENTION_MAX ((u32) BIT(16) | BIT(17))
+#define VHT_CAP_MU_BEAMFORMER_CAPABLE ((u32) BIT(19))
+#define VHT_CAP_MU_BEAMFORMEE_CAPABLE ((u32) BIT(20))
+#define VHT_CAP_VHT_TXOP_PS ((u32) BIT(21))
+#define VHT_CAP_HTC_VHT ((u32) BIT(22))
+#define VHT_CAP_MAX_A_MPDU_LENGTH_EXPONENT ((u32) BIT(23))
+#define VHT_CAP_VHT_LINK_ADAPTATION_VHT_UNSOL_MFB ((u32) BIT(27))
+#define VHT_CAP_VHT_LINK_ADAPTATION_VHT_MRQ_MFB ((u32) BIT(26) | BIT(27))
+#define VHT_CAP_RX_ANTENNA_PATTERN ((u32) BIT(28))
+#define VHT_CAP_TX_ANTENNA_PATTERN ((u32) BIT(29))
#define OUI_MICROSOFT 0x0050f2 /* Microsoft (also used in Wi-Fi specs)
* 00:50:F2 */
#define WPA_IE_VENDOR_TYPE 0x0050f201
#define WPS_IE_VENDOR_TYPE 0x0050f204
+#define OUI_WFA 0x506f9a
+#define P2P_IE_VENDOR_TYPE 0x506f9a09
+#define WFD_IE_VENDOR_TYPE 0x506f9a0a
+#define WFD_OUI_TYPE 10
+#define HS20_IE_VENDOR_TYPE 0x506f9a10
#define WMM_OUI_TYPE 2
#define WMM_OUI_SUBTYPE_INFORMATION_ELEMENT 0
@@ -516,6 +759,10 @@ struct wmm_information_element {
} STRUCT_PACKED;
+#define WMM_QOSINFO_STA_AC_MASK 0x0f
+#define WMM_QOSINFO_STA_SP_MASK 0x03
+#define WMM_QOSINFO_STA_SP_SHIFT 5
+
#define WMM_AC_AIFSN_MASK 0x0f
#define WMM_AC_AIFNS_SHIFT 0
#define WMM_AC_ACM 0x10
@@ -544,7 +791,7 @@ struct wmm_parameter_element {
u8 oui_type; /* 2 */
u8 oui_subtype; /* 1 */
u8 version; /* 1 for WMM version 1.0 */
- u8 qos_info; /* AP/STA specif QoS info */
+ u8 qos_info; /* AP/STA specific QoS info */
u8 reserved; /* 0 */
struct wmm_ac_parameter ac[4]; /* AC_BE, AC_BK, AC_VI, AC_VO */
@@ -587,6 +834,140 @@ enum {
};
+#define HS20_INDICATION_OUI_TYPE 16
+#define HS20_ANQP_OUI_TYPE 17
+#define HS20_STYPE_QUERY_LIST 1
+#define HS20_STYPE_CAPABILITY_LIST 2
+#define HS20_STYPE_OPERATOR_FRIENDLY_NAME 3
+#define HS20_STYPE_WAN_METRICS 4
+#define HS20_STYPE_CONNECTION_CAPABILITY 5
+#define HS20_STYPE_NAI_HOME_REALM_QUERY 6
+#define HS20_STYPE_OPERATING_CLASS 7
+
+/* Wi-Fi Direct (P2P) */
+
+#define P2P_OUI_TYPE 9
+
+enum p2p_attr_id {
+ P2P_ATTR_STATUS = 0,
+ P2P_ATTR_MINOR_REASON_CODE = 1,
+ P2P_ATTR_CAPABILITY = 2,
+ P2P_ATTR_DEVICE_ID = 3,
+ P2P_ATTR_GROUP_OWNER_INTENT = 4,
+ P2P_ATTR_CONFIGURATION_TIMEOUT = 5,
+ P2P_ATTR_LISTEN_CHANNEL = 6,
+ P2P_ATTR_GROUP_BSSID = 7,
+ P2P_ATTR_EXT_LISTEN_TIMING = 8,
+ P2P_ATTR_INTENDED_INTERFACE_ADDR = 9,
+ P2P_ATTR_MANAGEABILITY = 10,
+ P2P_ATTR_CHANNEL_LIST = 11,
+ P2P_ATTR_NOTICE_OF_ABSENCE = 12,
+ P2P_ATTR_DEVICE_INFO = 13,
+ P2P_ATTR_GROUP_INFO = 14,
+ P2P_ATTR_GROUP_ID = 15,
+ P2P_ATTR_INTERFACE = 16,
+ P2P_ATTR_OPERATING_CHANNEL = 17,
+ P2P_ATTR_INVITATION_FLAGS = 18,
+ P2P_ATTR_VENDOR_SPECIFIC = 221
+};
+
+#define P2P_MAX_GO_INTENT 15
+
+/* P2P Capability - Device Capability bitmap */
+#define P2P_DEV_CAPAB_SERVICE_DISCOVERY BIT(0)
+#define P2P_DEV_CAPAB_CLIENT_DISCOVERABILITY BIT(1)
+#define P2P_DEV_CAPAB_CONCURRENT_OPER BIT(2)
+#define P2P_DEV_CAPAB_INFRA_MANAGED BIT(3)
+#define P2P_DEV_CAPAB_DEVICE_LIMIT BIT(4)
+#define P2P_DEV_CAPAB_INVITATION_PROCEDURE BIT(5)
+
+/* P2P Capability - Group Capability bitmap */
+#define P2P_GROUP_CAPAB_GROUP_OWNER BIT(0)
+#define P2P_GROUP_CAPAB_PERSISTENT_GROUP BIT(1)
+#define P2P_GROUP_CAPAB_GROUP_LIMIT BIT(2)
+#define P2P_GROUP_CAPAB_INTRA_BSS_DIST BIT(3)
+#define P2P_GROUP_CAPAB_CROSS_CONN BIT(4)
+#define P2P_GROUP_CAPAB_PERSISTENT_RECONN BIT(5)
+#define P2P_GROUP_CAPAB_GROUP_FORMATION BIT(6)
+
+/* Invitation Flags */
+#define P2P_INVITATION_FLAGS_TYPE BIT(0)
+
+/* P2P Manageability */
+#define P2P_MAN_DEVICE_MANAGEMENT BIT(0)
+#define P2P_MAN_CROSS_CONNECTION_PERMITTED BIT(1)
+#define P2P_MAN_COEXISTENCE_OPTIONAL BIT(2)
+
+enum p2p_status_code {
+ P2P_SC_SUCCESS = 0,
+ P2P_SC_FAIL_INFO_CURRENTLY_UNAVAILABLE = 1,
+ P2P_SC_FAIL_INCOMPATIBLE_PARAMS = 2,
+ P2P_SC_FAIL_LIMIT_REACHED = 3,
+ P2P_SC_FAIL_INVALID_PARAMS = 4,
+ P2P_SC_FAIL_UNABLE_TO_ACCOMMODATE = 5,
+ P2P_SC_FAIL_PREV_PROTOCOL_ERROR = 6,
+ P2P_SC_FAIL_NO_COMMON_CHANNELS = 7,
+ P2P_SC_FAIL_UNKNOWN_GROUP = 8,
+ P2P_SC_FAIL_BOTH_GO_INTENT_15 = 9,
+ P2P_SC_FAIL_INCOMPATIBLE_PROV_METHOD = 10,
+ P2P_SC_FAIL_REJECTED_BY_USER = 11,
+};
+
+#define P2P_WILDCARD_SSID "DIRECT-"
+#define P2P_WILDCARD_SSID_LEN 7
+
+/* P2P action frames */
+enum p2p_act_frame_type {
+ P2P_NOA = 0,
+ P2P_PRESENCE_REQ = 1,
+ P2P_PRESENCE_RESP = 2,
+ P2P_GO_DISC_REQ = 3
+};
+
+/* P2P public action frames */
+enum p2p_action_frame_type {
+ P2P_GO_NEG_REQ = 0,
+ P2P_GO_NEG_RESP = 1,
+ P2P_GO_NEG_CONF = 2,
+ P2P_INVITATION_REQ = 3,
+ P2P_INVITATION_RESP = 4,
+ P2P_DEV_DISC_REQ = 5,
+ P2P_DEV_DISC_RESP = 6,
+ P2P_PROV_DISC_REQ = 7,
+ P2P_PROV_DISC_RESP = 8
+};
+
+enum p2p_service_protocol_type {
+ P2P_SERV_ALL_SERVICES = 0,
+ P2P_SERV_BONJOUR = 1,
+ P2P_SERV_UPNP = 2,
+ P2P_SERV_WS_DISCOVERY = 3,
+ P2P_SERV_WIFI_DISPLAY = 4,
+ P2P_SERV_VENDOR_SPECIFIC = 255
+};
+
+enum p2p_sd_status {
+ P2P_SD_SUCCESS = 0,
+ P2P_SD_PROTO_NOT_AVAILABLE = 1,
+ P2P_SD_REQUESTED_INFO_NOT_AVAILABLE = 2,
+ P2P_SD_BAD_REQUEST = 3
+};
+
+
+enum wifi_display_subelem {
+ WFD_SUBELEM_DEVICE_INFO = 0,
+ WFD_SUBELEM_ASSOCIATED_BSSID = 1,
+ WFD_SUBELEM_AUDIO_FORMATS = 2,
+ WFD_SUBELEM_VIDEO_FORMATS = 3,
+ WFD_SUBELEM_3D_VIDEO_FORMATS = 4,
+ WFD_SUBELEM_CONTENT_PROTECTION = 5,
+ WFD_SUBELEM_COUPLED_SINK = 6,
+ WFD_SUBELEM_EXT_CAPAB = 7,
+ WFD_SUBELEM_LOCAL_IP_ADDRESS = 8,
+ WFD_SUBELEM_SESSION_INFO = 9
+};
+
+
#define OUI_BROADCOM 0x00904c /* Broadcom (Epigram) */
#define VENDOR_HT_CAPAB_OUI_TYPE 0x33 /* 00-90-4c:0x33 */
@@ -599,9 +980,106 @@ enum {
#define WLAN_CIPHER_SUITE_CCMP 0x000FAC04
#define WLAN_CIPHER_SUITE_WEP104 0x000FAC05
#define WLAN_CIPHER_SUITE_AES_CMAC 0x000FAC06
+#define WLAN_CIPHER_SUITE_NO_GROUP_ADDR 0x000FAC07
+#define WLAN_CIPHER_SUITE_GCMP 0x000FAC08
+
+#define WLAN_CIPHER_SUITE_SMS4 0x00147201
+
+#define WLAN_CIPHER_SUITE_CKIP 0x00409600
+#define WLAN_CIPHER_SUITE_CKIP_CMIC 0x00409601
+#define WLAN_CIPHER_SUITE_CMIC 0x00409602
+#define WLAN_CIPHER_SUITE_KRK 0x004096FF /* for nl80211 use only */
/* AKM suite selectors */
#define WLAN_AKM_SUITE_8021X 0x000FAC01
#define WLAN_AKM_SUITE_PSK 0x000FAC02
+#define WLAN_AKM_SUITE_CCKM 0x00409600
+
+
+/* IEEE 802.11v - WNM Action field values */
+enum wnm_action {
+ WNM_EVENT_REQ = 0,
+ WNM_EVENT_REPORT = 1,
+ WNM_DIAGNOSTIC_REQ = 2,
+ WNM_DIAGNOSTIC_REPORT = 3,
+ WNM_LOCATION_CFG_REQ = 4,
+ WNM_LOCATION_CFG_RESP = 5,
+ WNM_BSS_TRANS_MGMT_QUERY = 6,
+ WNM_BSS_TRANS_MGMT_REQ = 7,
+ WNM_BSS_TRANS_MGMT_RESP = 8,
+ WNM_FMS_REQ = 9,
+ WNM_FMS_RESP = 10,
+ WNM_COLLOCATED_INTERFERENCE_REQ = 11,
+ WNM_COLLOCATED_INTERFERENCE_REPORT = 12,
+ WNM_TFS_REQ = 13,
+ WNM_TFS_RESP = 14,
+ WNM_TFS_NOTIFY = 15,
+ WNM_SLEEP_MODE_REQ = 16,
+ WNM_SLEEP_MODE_RESP = 17,
+ WNM_TIM_BROADCAST_REQ = 18,
+ WNM_TIM_BROADCAST_RESP = 19,
+ WNM_QOS_TRAFFIC_CAPAB_UPDATE = 20,
+ WNM_CHANNEL_USAGE_REQ = 21,
+ WNM_CHANNEL_USAGE_RESP = 22,
+ WNM_DMS_REQ = 23,
+ WNM_DMS_RESP = 24,
+ WNM_TIMING_MEASUREMENT_REQ = 25,
+ WNM_NOTIFICATION_REQ = 26,
+ WNM_NOTIFICATION_RESP = 27
+};
+
+/* IEEE 802.11v - BSS Transition Management Request - Request Mode */
+#define WNM_BSS_TM_REQ_PREF_CAND_LIST_INCLUDED BIT(0)
+#define WNM_BSS_TM_REQ_ABRIDGED BIT(1)
+#define WNM_BSS_TM_REQ_DISASSOC_IMMINENT BIT(2)
+#define WNM_BSS_TM_REQ_BSS_TERMINATION_INCLUDED BIT(3)
+#define WNM_BSS_TM_REQ_ESS_DISASSOC_IMMINENT BIT(4)
+
+/* IEEE Std 802.11-2012, 8.4.2.62 20/40 BSS Coexistence element */
+#define WLAN_20_40_BSS_COEX_INFO_REQ BIT(0)
+#define WLAN_20_40_BSS_COEX_40MHZ_INTOL BIT(1)
+#define WLAN_20_40_BSS_COEX_20MHZ_WIDTH_REQ BIT(2)
+#define WLAN_20_40_BSS_COEX_OBSS_EXEMPT_REQ BIT(3)
+#define WLAN_20_40_BSS_COEX_OBSS_EXEMPT_GRNT BIT(4)
+
+struct ieee80211_2040_bss_coex_ie {
+ u8 element_id;
+ u8 length;
+ u8 coex_param;
+} STRUCT_PACKED;
+
+struct ieee80211_2040_intol_chan_report {
+ u8 element_id;
+ u8 length;
+ u8 op_class;
+ u8 variable[0]; /* Channel List */
+} STRUCT_PACKED;
+
+/* IEEE 802.11v - WNM-Sleep Mode element */
+struct wnm_sleep_element {
+ u8 eid; /* WLAN_EID_WNMSLEEP */
+ u8 len;
+ u8 action_type; /* WNM_SLEEP_ENTER/WNM_SLEEP_MODE_EXIT */
+ u8 status;
+ le16 intval;
+} STRUCT_PACKED;
+
+#define WNM_SLEEP_MODE_ENTER 0
+#define WNM_SLEEP_MODE_EXIT 1
+
+enum wnm_sleep_mode_response_status {
+ WNM_STATUS_SLEEP_ACCEPT = 0,
+ WNM_STATUS_SLEEP_EXIT_ACCEPT_GTK_UPDATE = 1,
+ WNM_STATUS_DENIED_ACTION = 2,
+ WNM_STATUS_DENIED_TMP = 3,
+ WNM_STATUS_DENIED_KEY = 4,
+ WNM_STATUS_DENIED_OTHER_WNM_SERVICE = 5
+};
+
+/* WNM-Sleep Mode subelement IDs */
+enum wnm_sleep_mode_subelement_id {
+ WNM_SLEEP_SUBELEM_GTK = 0,
+ WNM_SLEEP_SUBELEM_IGTK = 1
+};
#endif /* IEEE802_11_DEFS_H */
diff --git a/contrib/wpa/src/common/privsep_commands.h b/contrib/wpa/src/common/privsep_commands.h
index cc900be..858b51d 100644
--- a/contrib/wpa/src/common/privsep_commands.h
+++ b/contrib/wpa/src/common/privsep_commands.h
@@ -2,14 +2,8 @@
* WPA Supplicant - privilege separation commands
* Copyright (c) 2007-2009, Jouni Malinen <j@w1.fi>
*
- * This program is free software; you can redistribute it and/or modify
- * it under the terms of the GNU General Public License version 2 as
- * published by the Free Software Foundation.
- *
- * Alternatively, this software may be distributed under the terms of BSD
- * license.
- *
- * See README and COPYING for more details.
+ * This software may be distributed under the terms of the BSD license.
+ * See README for more details.
*/
#ifndef PRIVSEP_COMMANDS_H
diff --git a/contrib/wpa/src/common/version.h b/contrib/wpa/src/common/version.h
index 02f34be..04ed0ac 100644
--- a/contrib/wpa/src/common/version.h
+++ b/contrib/wpa/src/common/version.h
@@ -1,6 +1,10 @@
#ifndef VERSION_H
#define VERSION_H
-#define VERSION_STR "0.7.3"
+#ifndef VERSION_STR_POSTFIX
+#define VERSION_STR_POSTFIX ""
+#endif /* VERSION_STR_POSTFIX */
+
+#define VERSION_STR "2.0" VERSION_STR_POSTFIX
#endif /* VERSION_H */
diff --git a/contrib/wpa/src/common/wpa_common.c b/contrib/wpa/src/common/wpa_common.c
index b295f31..8d7a11c 100644
--- a/contrib/wpa/src/common/wpa_common.c
+++ b/contrib/wpa/src/common/wpa_common.c
@@ -2,14 +2,8 @@
* WPA/RSN - Shared functions for supplicant and authenticator
* Copyright (c) 2002-2008, Jouni Malinen <j@w1.fi>
*
- * This program is free software; you can redistribute it and/or modify
- * it under the terms of the GNU General Public License version 2 as
- * published by the Free Software Foundation.
- *
- * Alternatively, this software may be distributed under the terms of BSD
- * license.
- *
- * See README and COPYING for more details.
+ * This software may be distributed under the terms of the BSD license.
+ * See README for more details.
*/
#include "includes.h"
@@ -49,8 +43,10 @@ int wpa_eapol_key_mic(const u8 *key, int ver, const u8 *buf, size_t len,
u8 hash[SHA1_MAC_LEN];
switch (ver) {
+#ifndef CONFIG_FIPS
case WPA_KEY_INFO_TYPE_HMAC_MD5_RC4:
return hmac_md5(key, 16, buf, len, mic);
+#endif /* CONFIG_FIPS */
case WPA_KEY_INFO_TYPE_HMAC_SHA1_AES:
if (hmac_sha1(key, 16, buf, len, hash))
return -1;
@@ -126,6 +122,8 @@ void wpa_pmk_to_ptk(const u8 *pmk, size_t pmk_len, const char *label,
wpa_printf(MSG_DEBUG, "WPA: PTK derivation - A1=" MACSTR " A2=" MACSTR,
MAC2STR(addr1), MAC2STR(addr2));
+ wpa_hexdump(MSG_DEBUG, "WPA: Nonce1", nonce1, WPA_NONCE_LEN);
+ wpa_hexdump(MSG_DEBUG, "WPA: Nonce2", nonce2, WPA_NONCE_LEN);
wpa_hexdump_key(MSG_DEBUG, "WPA: PMK", pmk, pmk_len);
wpa_hexdump_key(MSG_DEBUG, "WPA: PTK", ptk, ptk_len);
}
@@ -186,6 +184,154 @@ int wpa_ft_mic(const u8 *kck, const u8 *sta_addr, const u8 *ap_addr,
return 0;
}
+
+
+static int wpa_ft_parse_ftie(const u8 *ie, size_t ie_len,
+ struct wpa_ft_ies *parse)
+{
+ const u8 *end, *pos;
+
+ parse->ftie = ie;
+ parse->ftie_len = ie_len;
+
+ pos = ie + sizeof(struct rsn_ftie);
+ end = ie + ie_len;
+
+ while (pos + 2 <= end && pos + 2 + pos[1] <= end) {
+ switch (pos[0]) {
+ case FTIE_SUBELEM_R1KH_ID:
+ if (pos[1] != FT_R1KH_ID_LEN) {
+ wpa_printf(MSG_DEBUG, "FT: Invalid R1KH-ID "
+ "length in FTIE: %d", pos[1]);
+ return -1;
+ }
+ parse->r1kh_id = pos + 2;
+ break;
+ case FTIE_SUBELEM_GTK:
+ parse->gtk = pos + 2;
+ parse->gtk_len = pos[1];
+ break;
+ case FTIE_SUBELEM_R0KH_ID:
+ if (pos[1] < 1 || pos[1] > FT_R0KH_ID_MAX_LEN) {
+ wpa_printf(MSG_DEBUG, "FT: Invalid R0KH-ID "
+ "length in FTIE: %d", pos[1]);
+ return -1;
+ }
+ parse->r0kh_id = pos + 2;
+ parse->r0kh_id_len = pos[1];
+ break;
+#ifdef CONFIG_IEEE80211W
+ case FTIE_SUBELEM_IGTK:
+ parse->igtk = pos + 2;
+ parse->igtk_len = pos[1];
+ break;
+#endif /* CONFIG_IEEE80211W */
+ }
+
+ pos += 2 + pos[1];
+ }
+
+ return 0;
+}
+
+
+int wpa_ft_parse_ies(const u8 *ies, size_t ies_len,
+ struct wpa_ft_ies *parse)
+{
+ const u8 *end, *pos;
+ struct wpa_ie_data data;
+ int ret;
+ const struct rsn_ftie *ftie;
+ int prot_ie_count = 0;
+
+ os_memset(parse, 0, sizeof(*parse));
+ if (ies == NULL)
+ return 0;
+
+ pos = ies;
+ end = ies + ies_len;
+ while (pos + 2 <= end && pos + 2 + pos[1] <= end) {
+ switch (pos[0]) {
+ case WLAN_EID_RSN:
+ parse->rsn = pos + 2;
+ parse->rsn_len = pos[1];
+ ret = wpa_parse_wpa_ie_rsn(parse->rsn - 2,
+ parse->rsn_len + 2,
+ &data);
+ if (ret < 0) {
+ wpa_printf(MSG_DEBUG, "FT: Failed to parse "
+ "RSN IE: %d", ret);
+ return -1;
+ }
+ if (data.num_pmkid == 1 && data.pmkid)
+ parse->rsn_pmkid = data.pmkid;
+ break;
+ case WLAN_EID_MOBILITY_DOMAIN:
+ parse->mdie = pos + 2;
+ parse->mdie_len = pos[1];
+ break;
+ case WLAN_EID_FAST_BSS_TRANSITION:
+ if (pos[1] < sizeof(*ftie))
+ return -1;
+ ftie = (const struct rsn_ftie *) (pos + 2);
+ prot_ie_count = ftie->mic_control[1];
+ if (wpa_ft_parse_ftie(pos + 2, pos[1], parse) < 0)
+ return -1;
+ break;
+ case WLAN_EID_TIMEOUT_INTERVAL:
+ parse->tie = pos + 2;
+ parse->tie_len = pos[1];
+ break;
+ case WLAN_EID_RIC_DATA:
+ if (parse->ric == NULL)
+ parse->ric = pos;
+ break;
+ }
+
+ pos += 2 + pos[1];
+ }
+
+ if (prot_ie_count == 0)
+ return 0; /* no MIC */
+
+ /*
+ * Check that the protected IE count matches with IEs included in the
+ * frame.
+ */
+ if (parse->rsn)
+ prot_ie_count--;
+ if (parse->mdie)
+ prot_ie_count--;
+ if (parse->ftie)
+ prot_ie_count--;
+ if (prot_ie_count < 0) {
+ wpa_printf(MSG_DEBUG, "FT: Some required IEs not included in "
+ "the protected IE count");
+ return -1;
+ }
+
+ if (prot_ie_count == 0 && parse->ric) {
+ wpa_printf(MSG_DEBUG, "FT: RIC IE(s) in the frame, but not "
+ "included in protected IE count");
+ return -1;
+ }
+
+ /* Determine the end of the RIC IE(s) */
+ pos = parse->ric;
+ while (pos && pos + 2 <= end && pos + 2 + pos[1] <= end &&
+ prot_ie_count) {
+ prot_ie_count--;
+ pos += 2 + pos[1];
+ }
+ parse->ric_len = pos - parse->ric;
+ if (prot_ie_count) {
+ wpa_printf(MSG_DEBUG, "FT: %d protected IEs missing from "
+ "frame", (int) prot_ie_count);
+ return -1;
+ }
+
+ return 0;
+}
#endif /* CONFIG_IEEE80211R */
@@ -206,6 +352,8 @@ static int rsn_selector_to_bitfield(const u8 *s)
if (RSN_SELECTOR_GET(s) == RSN_CIPHER_SUITE_AES_128_CMAC)
return WPA_CIPHER_AES_128_CMAC;
#endif /* CONFIG_IEEE80211W */
+ if (RSN_SELECTOR_GET(s) == RSN_CIPHER_SUITE_GCMP)
+ return WPA_CIPHER_GCMP;
return 0;
}
@@ -228,6 +376,12 @@ static int rsn_key_mgmt_to_bitfield(const u8 *s)
if (RSN_SELECTOR_GET(s) == RSN_AUTH_KEY_MGMT_PSK_SHA256)
return WPA_KEY_MGMT_PSK_SHA256;
#endif /* CONFIG_IEEE80211W */
+#ifdef CONFIG_SAE
+ if (RSN_SELECTOR_GET(s) == RSN_AUTH_KEY_MGMT_SAE)
+ return WPA_KEY_MGMT_SAE;
+ if (RSN_SELECTOR_GET(s) == RSN_AUTH_KEY_MGMT_FT_SAE)
+ return WPA_KEY_MGMT_FT_SAE;
+#endif /* CONFIG_SAE */
return 0;
}
#endif /* CONFIG_NO_WPA2 */
@@ -403,6 +557,144 @@ int wpa_parse_wpa_ie_rsn(const u8 *rsn_ie, size_t rsn_ie_len,
}
+static int wpa_selector_to_bitfield(const u8 *s)
+{
+ if (RSN_SELECTOR_GET(s) == WPA_CIPHER_SUITE_NONE)
+ return WPA_CIPHER_NONE;
+ if (RSN_SELECTOR_GET(s) == WPA_CIPHER_SUITE_WEP40)
+ return WPA_CIPHER_WEP40;
+ if (RSN_SELECTOR_GET(s) == WPA_CIPHER_SUITE_TKIP)
+ return WPA_CIPHER_TKIP;
+ if (RSN_SELECTOR_GET(s) == WPA_CIPHER_SUITE_CCMP)
+ return WPA_CIPHER_CCMP;
+ if (RSN_SELECTOR_GET(s) == WPA_CIPHER_SUITE_WEP104)
+ return WPA_CIPHER_WEP104;
+ return 0;
+}
+
+
+static int wpa_key_mgmt_to_bitfield(const u8 *s)
+{
+ if (RSN_SELECTOR_GET(s) == WPA_AUTH_KEY_MGMT_UNSPEC_802_1X)
+ return WPA_KEY_MGMT_IEEE8021X;
+ if (RSN_SELECTOR_GET(s) == WPA_AUTH_KEY_MGMT_PSK_OVER_802_1X)
+ return WPA_KEY_MGMT_PSK;
+ if (RSN_SELECTOR_GET(s) == WPA_AUTH_KEY_MGMT_NONE)
+ return WPA_KEY_MGMT_WPA_NONE;
+ return 0;
+}
+
+
+int wpa_parse_wpa_ie_wpa(const u8 *wpa_ie, size_t wpa_ie_len,
+ struct wpa_ie_data *data)
+{
+ const struct wpa_ie_hdr *hdr;
+ const u8 *pos;
+ int left;
+ int i, count;
+
+ os_memset(data, 0, sizeof(*data));
+ data->proto = WPA_PROTO_WPA;
+ data->pairwise_cipher = WPA_CIPHER_TKIP;
+ data->group_cipher = WPA_CIPHER_TKIP;
+ data->key_mgmt = WPA_KEY_MGMT_IEEE8021X;
+ data->capabilities = 0;
+ data->pmkid = NULL;
+ data->num_pmkid = 0;
+ data->mgmt_group_cipher = 0;
+
+ if (wpa_ie_len == 0) {
+ /* No WPA IE - fail silently */
+ return -1;
+ }
+
+ if (wpa_ie_len < sizeof(struct wpa_ie_hdr)) {
+ wpa_printf(MSG_DEBUG, "%s: ie len too short %lu",
+ __func__, (unsigned long) wpa_ie_len);
+ return -1;
+ }
+
+ hdr = (const struct wpa_ie_hdr *) wpa_ie;
+
+ if (hdr->elem_id != WLAN_EID_VENDOR_SPECIFIC ||
+ hdr->len != wpa_ie_len - 2 ||
+ RSN_SELECTOR_GET(hdr->oui) != WPA_OUI_TYPE ||
+ WPA_GET_LE16(hdr->version) != WPA_VERSION) {
+ wpa_printf(MSG_DEBUG, "%s: malformed ie or unknown version",
+ __func__);
+ return -2;
+ }
+
+ pos = (const u8 *) (hdr + 1);
+ left = wpa_ie_len - sizeof(*hdr);
+
+ if (left >= WPA_SELECTOR_LEN) {
+ data->group_cipher = wpa_selector_to_bitfield(pos);
+ pos += WPA_SELECTOR_LEN;
+ left -= WPA_SELECTOR_LEN;
+ } else if (left > 0) {
+ wpa_printf(MSG_DEBUG, "%s: ie length mismatch, %u too much",
+ __func__, left);
+ return -3;
+ }
+
+ if (left >= 2) {
+ data->pairwise_cipher = 0;
+ count = WPA_GET_LE16(pos);
+ pos += 2;
+ left -= 2;
+ if (count == 0 || left < count * WPA_SELECTOR_LEN) {
+ wpa_printf(MSG_DEBUG, "%s: ie count botch (pairwise), "
+ "count %u left %u", __func__, count, left);
+ return -4;
+ }
+ for (i = 0; i < count; i++) {
+ data->pairwise_cipher |= wpa_selector_to_bitfield(pos);
+ pos += WPA_SELECTOR_LEN;
+ left -= WPA_SELECTOR_LEN;
+ }
+ } else if (left == 1) {
+ wpa_printf(MSG_DEBUG, "%s: ie too short (for key mgmt)",
+ __func__);
+ return -5;
+ }
+
+ if (left >= 2) {
+ data->key_mgmt = 0;
+ count = WPA_GET_LE16(pos);
+ pos += 2;
+ left -= 2;
+ if (count == 0 || left < count * WPA_SELECTOR_LEN) {
+ wpa_printf(MSG_DEBUG, "%s: ie count botch (key mgmt), "
+ "count %u left %u", __func__, count, left);
+ return -6;
+ }
+ for (i = 0; i < count; i++) {
+ data->key_mgmt |= wpa_key_mgmt_to_bitfield(pos);
+ pos += WPA_SELECTOR_LEN;
+ left -= WPA_SELECTOR_LEN;
+ }
+ } else if (left == 1) {
+ wpa_printf(MSG_DEBUG, "%s: ie too short (for capabilities)",
+ __func__);
+ return -7;
+ }
+
+ if (left >= 2) {
+ data->capabilities = WPA_GET_LE16(pos);
+ pos += 2;
+ left -= 2;
+ }
+
+ if (left > 0) {
+ wpa_printf(MSG_DEBUG, "%s: ie has %u trailing bytes - ignored",
+ __func__, left);
+ }
+
+ return 0;
+}
+
+
#ifdef CONFIG_IEEE80211R
/**
@@ -624,6 +916,8 @@ const char * wpa_cipher_txt(int cipher)
return "CCMP";
case WPA_CIPHER_CCMP | WPA_CIPHER_TKIP:
return "CCMP+TKIP";
+ case WPA_CIPHER_GCMP:
+ return "GCMP";
default:
return "UNKNOWN";
}
@@ -785,3 +1079,138 @@ int wpa_insert_pmkid(u8 *ies, size_t ies_len, const u8 *pmkid)
return added;
}
#endif /* CONFIG_IEEE80211R */
+
+
+int wpa_cipher_key_len(int cipher)
+{
+ switch (cipher) {
+ case WPA_CIPHER_CCMP:
+ case WPA_CIPHER_GCMP:
+ return 16;
+ case WPA_CIPHER_TKIP:
+ return 32;
+ case WPA_CIPHER_WEP104:
+ return 13;
+ case WPA_CIPHER_WEP40:
+ return 5;
+ }
+
+ return 0;
+}
+
+
+int wpa_cipher_rsc_len(int cipher)
+{
+ switch (cipher) {
+ case WPA_CIPHER_CCMP:
+ case WPA_CIPHER_GCMP:
+ case WPA_CIPHER_TKIP:
+ return 6;
+ case WPA_CIPHER_WEP104:
+ case WPA_CIPHER_WEP40:
+ return 0;
+ }
+
+ return 0;
+}
+
+
+int wpa_cipher_to_alg(int cipher)
+{
+ switch (cipher) {
+ case WPA_CIPHER_CCMP:
+ return WPA_ALG_CCMP;
+ case WPA_CIPHER_GCMP:
+ return WPA_ALG_GCMP;
+ case WPA_CIPHER_TKIP:
+ return WPA_ALG_TKIP;
+ case WPA_CIPHER_WEP104:
+ case WPA_CIPHER_WEP40:
+ return WPA_ALG_WEP;
+ }
+ return WPA_ALG_NONE;
+}
+
+
+int wpa_cipher_valid_pairwise(int cipher)
+{
+ return cipher == WPA_CIPHER_CCMP ||
+ cipher == WPA_CIPHER_GCMP ||
+ cipher == WPA_CIPHER_TKIP;
+}
+
+
+u32 wpa_cipher_to_suite(int proto, int cipher)
+{
+ if (cipher & WPA_CIPHER_CCMP)
+ return (proto == WPA_PROTO_RSN ?
+ RSN_CIPHER_SUITE_CCMP : WPA_CIPHER_SUITE_CCMP);
+ if (cipher & WPA_CIPHER_GCMP)
+ return RSN_CIPHER_SUITE_GCMP;
+ if (cipher & WPA_CIPHER_TKIP)
+ return (proto == WPA_PROTO_RSN ?
+ RSN_CIPHER_SUITE_TKIP : WPA_CIPHER_SUITE_TKIP);
+ if (cipher & WPA_CIPHER_WEP104)
+ return (proto == WPA_PROTO_RSN ?
+ RSN_CIPHER_SUITE_WEP104 : WPA_CIPHER_SUITE_WEP104);
+ if (cipher & WPA_CIPHER_WEP40)
+ return (proto == WPA_PROTO_RSN ?
+ RSN_CIPHER_SUITE_WEP40 : WPA_CIPHER_SUITE_WEP40);
+ if (cipher & WPA_CIPHER_NONE)
+ return (proto == WPA_PROTO_RSN ?
+ RSN_CIPHER_SUITE_NONE : WPA_CIPHER_SUITE_NONE);
+ return 0;
+}
+
+
+int rsn_cipher_put_suites(u8 *pos, int ciphers)
+{
+ int num_suites = 0;
+
+ if (ciphers & WPA_CIPHER_CCMP) {
+ RSN_SELECTOR_PUT(pos, RSN_CIPHER_SUITE_CCMP);
+ pos += RSN_SELECTOR_LEN;
+ num_suites++;
+ }
+ if (ciphers & WPA_CIPHER_GCMP) {
+ RSN_SELECTOR_PUT(pos, RSN_CIPHER_SUITE_GCMP);
+ pos += RSN_SELECTOR_LEN;
+ num_suites++;
+ }
+ if (ciphers & WPA_CIPHER_TKIP) {
+ RSN_SELECTOR_PUT(pos, RSN_CIPHER_SUITE_TKIP);
+ pos += RSN_SELECTOR_LEN;
+ num_suites++;
+ }
+ if (ciphers & WPA_CIPHER_NONE) {
+ RSN_SELECTOR_PUT(pos, RSN_CIPHER_SUITE_NONE);
+ pos += RSN_SELECTOR_LEN;
+ num_suites++;
+ }
+
+ return num_suites;
+}
+
+
+int wpa_cipher_put_suites(u8 *pos, int ciphers)
+{
+ int num_suites = 0;
+
+ if (ciphers & WPA_CIPHER_CCMP) {
+ RSN_SELECTOR_PUT(pos, WPA_CIPHER_SUITE_CCMP);
+ pos += WPA_SELECTOR_LEN;
+ num_suites++;
+ }
+ if (ciphers & WPA_CIPHER_TKIP) {
+ RSN_SELECTOR_PUT(pos, WPA_CIPHER_SUITE_TKIP);
+ pos += WPA_SELECTOR_LEN;
+ num_suites++;
+ }
+ if (ciphers & WPA_CIPHER_NONE) {
+ RSN_SELECTOR_PUT(pos, WPA_CIPHER_SUITE_NONE);
+ pos += WPA_SELECTOR_LEN;
+ num_suites++;
+ }
+
+ return num_suites;
+}
diff --git a/contrib/wpa/src/common/wpa_common.h b/contrib/wpa/src/common/wpa_common.h
index fd8a79f..20c79d8 100644
--- a/contrib/wpa/src/common/wpa_common.h
+++ b/contrib/wpa/src/common/wpa_common.h
@@ -2,14 +2,8 @@
* WPA definitions shared between hostapd and wpa_supplicant
* Copyright (c) 2002-2008, Jouni Malinen <j@w1.fi>
*
- * This program is free software; you can redistribute it and/or modify
- * it under the terms of the GNU General Public License version 2 as
- * published by the Free Software Foundation.
- *
- * Alternatively, this software may be distributed under the terms of BSD
- * license.
- *
- * See README and COPYING for more details.
+ * This software may be distributed under the terms of the BSD license.
+ * See README for more details.
*/
#ifndef WPA_COMMON_H
@@ -38,6 +32,7 @@
#define WPA_AUTH_KEY_MGMT_NONE RSN_SELECTOR(0x00, 0x50, 0xf2, 0)
#define WPA_AUTH_KEY_MGMT_UNSPEC_802_1X RSN_SELECTOR(0x00, 0x50, 0xf2, 1)
#define WPA_AUTH_KEY_MGMT_PSK_OVER_802_1X RSN_SELECTOR(0x00, 0x50, 0xf2, 2)
+#define WPA_AUTH_KEY_MGMT_CCKM RSN_SELECTOR(0x00, 0x40, 0x96, 0)
#define WPA_CIPHER_SUITE_NONE RSN_SELECTOR(0x00, 0x50, 0xf2, 0)
#define WPA_CIPHER_SUITE_WEP40 RSN_SELECTOR(0x00, 0x50, 0xf2, 1)
#define WPA_CIPHER_SUITE_TKIP RSN_SELECTOR(0x00, 0x50, 0xf2, 2)
@@ -56,6 +51,10 @@
#endif /* CONFIG_IEEE80211R */
#define RSN_AUTH_KEY_MGMT_802_1X_SHA256 RSN_SELECTOR(0x00, 0x0f, 0xac, 5)
#define RSN_AUTH_KEY_MGMT_PSK_SHA256 RSN_SELECTOR(0x00, 0x0f, 0xac, 6)
+#define RSN_AUTH_KEY_MGMT_TPK_HANDSHAKE RSN_SELECTOR(0x00, 0x0f, 0xac, 7)
+#define RSN_AUTH_KEY_MGMT_SAE RSN_SELECTOR(0x00, 0x0f, 0xac, 8)
+#define RSN_AUTH_KEY_MGMT_FT_SAE RSN_SELECTOR(0x00, 0x0f, 0xac, 9)
+#define RSN_AUTH_KEY_MGMT_CCKM RSN_SELECTOR(0x00, 0x40, 0x96, 0x00)
#define RSN_CIPHER_SUITE_NONE RSN_SELECTOR(0x00, 0x0f, 0xac, 0)
#define RSN_CIPHER_SUITE_WEP40 RSN_SELECTOR(0x00, 0x0f, 0xac, 1)
@@ -68,6 +67,8 @@
#ifdef CONFIG_IEEE80211W
#define RSN_CIPHER_SUITE_AES_128_CMAC RSN_SELECTOR(0x00, 0x0f, 0xac, 6)
#endif /* CONFIG_IEEE80211W */
+#define RSN_CIPHER_SUITE_NO_GROUP_ADDRESSED RSN_SELECTOR(0x00, 0x0f, 0xac, 7)
+#define RSN_CIPHER_SUITE_GCMP RSN_SELECTOR(0x00, 0x0f, 0xac, 8)
/* EAPOL-Key Key Data Encapsulation
* GroupKey and PeerKey require encryption, otherwise, encryption is optional.
@@ -87,6 +88,9 @@
#ifdef CONFIG_IEEE80211W
#define RSN_KEY_DATA_IGTK RSN_SELECTOR(0x00, 0x0f, 0xac, 9)
#endif /* CONFIG_IEEE80211W */
+#define RSN_KEY_DATA_KEYID RSN_SELECTOR(0x00, 0x0f, 0xac, 10)
+#define RSN_KEY_DATA_MULTIBAND_GTK RSN_SELECTOR(0x00, 0x0f, 0xac, 11)
+#define RSN_KEY_DATA_MULTIBAND_KEYID RSN_SELECTOR(0x00, 0x0f, 0xac, 12)
#define WPA_OUI_TYPE RSN_SELECTOR(0x00, 0x50, 0xf2, 1)
@@ -115,7 +119,13 @@
/* B4-B5: GTKSA Replay Counter */
#define WPA_CAPABILITY_MFPR BIT(6)
#define WPA_CAPABILITY_MFPC BIT(7)
+/* B8: Reserved */
#define WPA_CAPABILITY_PEERKEY_ENABLED BIT(9)
+#define WPA_CAPABILITY_SPP_A_MSDU_CAPABLE BIT(10)
+#define WPA_CAPABILITY_SPP_A_MSDU_REQUIRED BIT(11)
+#define WPA_CAPABILITY_PBAC BIT(12)
+#define WPA_CAPABILITY_EXT_KEY_ID_FOR_UNICAST BIT(13)
+/* B14-B15: Reserved */
/* IEEE 802.11r */
@@ -337,6 +347,8 @@ struct wpa_ie_data {
int wpa_parse_wpa_ie_rsn(const u8 *rsn_ie, size_t rsn_ie_len,
struct wpa_ie_data *data);
+int wpa_parse_wpa_ie_wpa(const u8 *wpa_ie, size_t wpa_ie_len,
+ struct wpa_ie_data *data);
void rsn_pmkid(const u8 *pmk, size_t pmk_len, const u8 *aa, const u8 *spa,
u8 *pmkid, int use_sha256);
@@ -348,4 +360,35 @@ int wpa_compare_rsn_ie(int ft_initial_assoc,
const u8 *ie2, size_t ie2len);
int wpa_insert_pmkid(u8 *ies, size_t ies_len, const u8 *pmkid);
+struct wpa_ft_ies {
+ const u8 *mdie;
+ size_t mdie_len;
+ const u8 *ftie;
+ size_t ftie_len;
+ const u8 *r1kh_id;
+ const u8 *gtk;
+ size_t gtk_len;
+ const u8 *r0kh_id;
+ size_t r0kh_id_len;
+ const u8 *rsn;
+ size_t rsn_len;
+ const u8 *rsn_pmkid;
+ const u8 *tie;
+ size_t tie_len;
+ const u8 *igtk;
+ size_t igtk_len;
+ const u8 *ric;
+ size_t ric_len;
+};
+
+int wpa_ft_parse_ies(const u8 *ies, size_t ies_len, struct wpa_ft_ies *parse);
+
+int wpa_cipher_key_len(int cipher);
+int wpa_cipher_rsc_len(int cipher);
+int wpa_cipher_to_alg(int cipher);
+int wpa_cipher_valid_pairwise(int cipher);
+u32 wpa_cipher_to_suite(int proto, int cipher);
+int rsn_cipher_put_suites(u8 *pos, int ciphers);
+int wpa_cipher_put_suites(u8 *pos, int ciphers);
+
#endif /* WPA_COMMON_H */
diff --git a/contrib/wpa/src/common/wpa_ctrl.c b/contrib/wpa/src/common/wpa_ctrl.c
index 2b4e3aa..58cbe6a 100644
--- a/contrib/wpa/src/common/wpa_ctrl.c
+++ b/contrib/wpa/src/common/wpa_ctrl.c
@@ -2,14 +2,8 @@
* wpa_supplicant/hostapd control interface library
* Copyright (c) 2004-2007, Jouni Malinen <j@w1.fi>
*
- * This program is free software; you can redistribute it and/or modify
- * it under the terms of the GNU General Public License version 2 as
- * published by the Free Software Foundation.
- *
- * Alternatively, this software may be distributed under the terms of BSD
- * license.
- *
- * See README and COPYING for more details.
+ * This software may be distributed under the terms of the BSD license.
+ * See README for more details.
*/
#include "includes.h"
@@ -18,7 +12,18 @@
#ifdef CONFIG_CTRL_IFACE_UNIX
#include <sys/un.h>
+#include <unistd.h>
+#include <fcntl.h>
#endif /* CONFIG_CTRL_IFACE_UNIX */
+#ifdef CONFIG_CTRL_IFACE_UDP_REMOTE
+#include <netdb.h>
+#endif /* CONFIG_CTRL_IFACE_UDP_REMOTE */
+
+#ifdef ANDROID
+#include <dirent.h>
+#include <cutils/sockets.h>
+#include "private/android_filesystem_config.h"
+#endif /* ANDROID */
#include "wpa_ctrl.h"
#include "common.h"
@@ -44,6 +49,8 @@ struct wpa_ctrl {
struct sockaddr_in local;
struct sockaddr_in dest;
char *cookie;
+ char *remote_ifname;
+ char *remote_ip;
#endif /* CONFIG_CTRL_IFACE_UDP */
#ifdef CONFIG_CTRL_IFACE_UNIX
int s;
@@ -58,6 +65,14 @@ struct wpa_ctrl {
#ifdef CONFIG_CTRL_IFACE_UNIX
+#ifndef CONFIG_CTRL_IFACE_CLIENT_DIR
+#define CONFIG_CTRL_IFACE_CLIENT_DIR "/tmp"
+#endif /* CONFIG_CTRL_IFACE_CLIENT_DIR */
+#ifndef CONFIG_CTRL_IFACE_CLIENT_PREFIX
+#define CONFIG_CTRL_IFACE_CLIENT_PREFIX "wpa_ctrl_"
+#endif /* CONFIG_CTRL_IFACE_CLIENT_PREFIX */
+
+
struct wpa_ctrl * wpa_ctrl_open(const char *ctrl_path)
{
struct wpa_ctrl *ctrl;
@@ -65,6 +80,7 @@ struct wpa_ctrl * wpa_ctrl_open(const char *ctrl_path)
int ret;
size_t res;
int tries = 0;
+ int flags;
ctrl = os_malloc(sizeof(*ctrl));
if (ctrl == NULL)
@@ -81,7 +97,9 @@ struct wpa_ctrl * wpa_ctrl_open(const char *ctrl_path)
counter++;
try_again:
ret = os_snprintf(ctrl->local.sun_path, sizeof(ctrl->local.sun_path),
- "/tmp/wpa_ctrl_%d-%d", getpid(), counter);
+ CONFIG_CTRL_IFACE_CLIENT_DIR "/"
+ CONFIG_CTRL_IFACE_CLIENT_PREFIX "%d-%d",
+ (int) getpid(), counter);
if (ret < 0 || (size_t) ret >= sizeof(ctrl->local.sun_path)) {
close(ctrl->s);
os_free(ctrl);
@@ -105,6 +123,31 @@ try_again:
return NULL;
}
+#ifdef ANDROID
+ chmod(ctrl->local.sun_path, S_IRUSR | S_IWUSR | S_IRGRP | S_IWGRP);
+ chown(ctrl->local.sun_path, AID_SYSTEM, AID_WIFI);
+ /*
+ * If the ctrl_path isn't an absolute pathname, assume that
+ * it's the name of a socket in the Android reserved namespace.
+ * Otherwise, it's a normal UNIX domain socket appearing in the
+ * filesystem.
+ */
+ if (ctrl_path != NULL && *ctrl_path != '/') {
+ char buf[21];
+ os_snprintf(buf, sizeof(buf), "wpa_%s", ctrl_path);
+ if (socket_local_client_connect(
+ ctrl->s, buf,
+ ANDROID_SOCKET_NAMESPACE_RESERVED,
+ SOCK_DGRAM) < 0) {
+ close(ctrl->s);
+ unlink(ctrl->local.sun_path);
+ os_free(ctrl);
+ return NULL;
+ }
+ return ctrl;
+ }
+#endif /* ANDROID */
+
ctrl->dest.sun_family = AF_UNIX;
res = os_strlcpy(ctrl->dest.sun_path, ctrl_path,
sizeof(ctrl->dest.sun_path));
@@ -121,17 +164,83 @@ try_again:
return NULL;
}
+ /*
+ * Make socket non-blocking so that we don't hang forever if
+ * target dies unexpectedly.
+ */
+ flags = fcntl(ctrl->s, F_GETFL);
+ if (flags >= 0) {
+ flags |= O_NONBLOCK;
+ if (fcntl(ctrl->s, F_SETFL, flags) < 0) {
+ perror("fcntl(ctrl->s, O_NONBLOCK)");
+ /* Not fatal, continue on.*/
+ }
+ }
+
return ctrl;
}
void wpa_ctrl_close(struct wpa_ctrl *ctrl)
{
+ if (ctrl == NULL)
+ return;
unlink(ctrl->local.sun_path);
- close(ctrl->s);
+ if (ctrl->s >= 0)
+ close(ctrl->s);
os_free(ctrl);
}
+
+#ifdef ANDROID
+/**
+ * wpa_ctrl_cleanup() - Delete any local UNIX domain socket files that
+ * may be left over from clients that were previously connected to
+ * wpa_supplicant. This keeps these files from being orphaned in the
+ * event of crashes that prevented them from being removed as part
+ * of the normal orderly shutdown.
+ */
+void wpa_ctrl_cleanup(void)
+{
+ DIR *dir;
+ struct dirent entry;
+ struct dirent *result;
+ size_t dirnamelen;
+ int prefixlen = os_strlen(CONFIG_CTRL_IFACE_CLIENT_PREFIX);
+ size_t maxcopy;
+ char pathname[PATH_MAX];
+ char *namep;
+
+ if ((dir = opendir(CONFIG_CTRL_IFACE_CLIENT_DIR)) == NULL)
+ return;
+
+ dirnamelen = (size_t) os_snprintf(pathname, sizeof(pathname), "%s/",
+ CONFIG_CTRL_IFACE_CLIENT_DIR);
+ if (dirnamelen >= sizeof(pathname)) {
+ closedir(dir);
+ return;
+ }
+ namep = pathname + dirnamelen;
+ maxcopy = PATH_MAX - dirnamelen;
+ while (readdir_r(dir, &entry, &result) == 0 && result != NULL) {
+ if (os_strncmp(entry.d_name, CONFIG_CTRL_IFACE_CLIENT_PREFIX,
+ prefixlen) == 0) {
+ if (os_strlcpy(namep, entry.d_name, maxcopy) < maxcopy)
+ unlink(pathname);
+ }
+ }
+ closedir(dir);
+}
+#endif /* ANDROID */
+
+#else /* CONFIG_CTRL_IFACE_UNIX */
+
+#ifdef ANDROID
+void wpa_ctrl_cleanup(void)
+{
+}
+#endif /* ANDROID */
+
#endif /* CONFIG_CTRL_IFACE_UNIX */
@@ -142,6 +251,9 @@ struct wpa_ctrl * wpa_ctrl_open(const char *ctrl_path)
struct wpa_ctrl *ctrl;
char buf[128];
size_t len;
+#ifdef CONFIG_CTRL_IFACE_UDP_REMOTE
+ struct hostent *h;
+#endif /* CONFIG_CTRL_IFACE_UDP_REMOTE */
ctrl = os_malloc(sizeof(*ctrl));
if (ctrl == NULL)
@@ -156,7 +268,11 @@ struct wpa_ctrl * wpa_ctrl_open(const char *ctrl_path)
}
ctrl->local.sin_family = AF_INET;
+#ifdef CONFIG_CTRL_IFACE_UDP_REMOTE
+ ctrl->local.sin_addr.s_addr = INADDR_ANY;
+#else /* CONFIG_CTRL_IFACE_UDP_REMOTE */
ctrl->local.sin_addr.s_addr = htonl((127 << 24) | 1);
+#endif /* CONFIG_CTRL_IFACE_UDP_REMOTE */
if (bind(ctrl->s, (struct sockaddr *) &ctrl->local,
sizeof(ctrl->local)) < 0) {
close(ctrl->s);
@@ -167,10 +283,48 @@ struct wpa_ctrl * wpa_ctrl_open(const char *ctrl_path)
ctrl->dest.sin_family = AF_INET;
ctrl->dest.sin_addr.s_addr = htonl((127 << 24) | 1);
ctrl->dest.sin_port = htons(WPA_CTRL_IFACE_PORT);
+
+#ifdef CONFIG_CTRL_IFACE_UDP_REMOTE
+ if (ctrl_path) {
+ char *port, *name;
+ int port_id;
+
+ name = os_strdup(ctrl_path);
+ if (name == NULL) {
+ close(ctrl->s);
+ os_free(ctrl);
+ return NULL;
+ }
+ port = os_strchr(name, ':');
+
+ if (port) {
+ port_id = atoi(&port[1]);
+ port[0] = '\0';
+ } else
+ port_id = WPA_CTRL_IFACE_PORT;
+
+ h = gethostbyname(name);
+ ctrl->remote_ip = os_strdup(name);
+ os_free(name);
+ if (h == NULL) {
+ perror("gethostbyname");
+ close(ctrl->s);
+ os_free(ctrl->remote_ip);
+ os_free(ctrl);
+ return NULL;
+ }
+ ctrl->dest.sin_port = htons(port_id);
+ os_memcpy(h->h_addr, (char *) &ctrl->dest.sin_addr.s_addr,
+ h->h_length);
+ } else
+ ctrl->remote_ip = os_strdup("localhost");
+#endif /* CONFIG_CTRL_IFACE_UDP_REMOTE */
+
if (connect(ctrl->s, (struct sockaddr *) &ctrl->dest,
sizeof(ctrl->dest)) < 0) {
perror("connect");
close(ctrl->s);
+ os_free(ctrl->remote_ip);
os_free(ctrl);
return NULL;
}
@@ -181,14 +335,31 @@ struct wpa_ctrl * wpa_ctrl_open(const char *ctrl_path)
ctrl->cookie = os_strdup(buf);
}
+ if (wpa_ctrl_request(ctrl, "IFNAME", 6, buf, &len, NULL) == 0) {
+ buf[len] = '\0';
+ ctrl->remote_ifname = os_strdup(buf);
+ }
+
return ctrl;
}
+char * wpa_ctrl_get_remote_ifname(struct wpa_ctrl *ctrl)
+{
+#define WPA_CTRL_MAX_PS_NAME 100
+ static char ps[WPA_CTRL_MAX_PS_NAME] = {};
+ os_snprintf(ps, WPA_CTRL_MAX_PS_NAME, "%s/%s",
+ ctrl->remote_ip, ctrl->remote_ifname);
+ return ps;
+}
+
+
void wpa_ctrl_close(struct wpa_ctrl *ctrl)
{
close(ctrl->s);
os_free(ctrl->cookie);
+ os_free(ctrl->remote_ifname);
+ os_free(ctrl->remote_ip);
os_free(ctrl);
}
@@ -201,6 +372,7 @@ int wpa_ctrl_request(struct wpa_ctrl *ctrl, const char *cmd, size_t cmd_len,
void (*msg_cb)(char *msg, size_t len))
{
struct timeval tv;
+ struct os_time started_at;
int res;
fd_set rfds;
const char *_cmd;
@@ -227,18 +399,43 @@ int wpa_ctrl_request(struct wpa_ctrl *ctrl, const char *cmd, size_t cmd_len,
_cmd_len = cmd_len;
}
+ errno = 0;
+ started_at.sec = 0;
+ started_at.usec = 0;
+retry_send:
if (send(ctrl->s, _cmd, _cmd_len, 0) < 0) {
+ if (errno == EAGAIN || errno == EBUSY || errno == EWOULDBLOCK)
+ {
+ /*
+ * Must be a non-blocking socket... Try for a bit
+ * longer before giving up.
+ */
+ if (started_at.sec == 0)
+ os_get_time(&started_at);
+ else {
+ struct os_time n;
+ os_get_time(&n);
+ /* Try for a few seconds. */
+ if (n.sec > started_at.sec + 5)
+ goto send_err;
+ }
+ os_sleep(1, 0);
+ goto retry_send;
+ }
+ send_err:
os_free(cmd_buf);
return -1;
}
os_free(cmd_buf);
for (;;) {
- tv.tv_sec = 2;
+ tv.tv_sec = 10;
tv.tv_usec = 0;
FD_ZERO(&rfds);
FD_SET(ctrl->s, &rfds);
res = select(ctrl->s + 1, &rfds, NULL, NULL, &tv);
+ if (res < 0)
+ return res;
if (FD_ISSET(ctrl->s, &rfds)) {
res = recv(ctrl->s, reply, *reply_len, 0);
if (res < 0)
diff --git a/contrib/wpa/src/common/wpa_ctrl.h b/contrib/wpa/src/common/wpa_ctrl.h
index d770fd4..bb9b558 100644
--- a/contrib/wpa/src/common/wpa_ctrl.h
+++ b/contrib/wpa/src/common/wpa_ctrl.h
@@ -2,14 +2,8 @@
* wpa_supplicant/hostapd control interface library
* Copyright (c) 2004-2006, Jouni Malinen <j@w1.fi>
*
- * This program is free software; you can redistribute it and/or modify
- * it under the terms of the GNU General Public License version 2 as
- * published by the Free Software Foundation.
- *
- * Alternatively, this software may be distributed under the terms of BSD
- * license.
- *
- * See README and COPYING for more details.
+ * This software may be distributed under the terms of the BSD license.
+ * See README for more details.
*/
#ifndef WPA_CTRL_H
@@ -32,6 +26,8 @@ extern "C" {
#define WPA_EVENT_CONNECTED "CTRL-EVENT-CONNECTED "
/** Disconnected, data connection is not available */
#define WPA_EVENT_DISCONNECTED "CTRL-EVENT-DISCONNECTED "
+/** Association rejected during connection attempt */
+#define WPA_EVENT_ASSOC_REJECT "CTRL-EVENT-ASSOC-REJECT "
/** wpa_supplicant is exiting */
#define WPA_EVENT_TERMINATING "CTRL-EVENT-TERMINATING "
/** Password change was completed successfully */
@@ -52,8 +48,14 @@ extern "C" {
#define WPA_EVENT_EAP_SUCCESS "CTRL-EVENT-EAP-SUCCESS "
/** EAP authentication failed (EAP-Failure received) */
#define WPA_EVENT_EAP_FAILURE "CTRL-EVENT-EAP-FAILURE "
+/** Network block temporarily disabled (e.g., due to authentication failure) */
+#define WPA_EVENT_TEMP_DISABLED "CTRL-EVENT-SSID-TEMP-DISABLED "
+/** Temporarily disabled network block re-enabled */
+#define WPA_EVENT_REENABLED "CTRL-EVENT-SSID-REENABLED "
/** New scan results available */
#define WPA_EVENT_SCAN_RESULTS "CTRL-EVENT-SCAN-RESULTS "
+/** wpa_supplicant state change */
+#define WPA_EVENT_STATE_CHANGE "CTRL-EVENT-STATE-CHANGE "
/** A new BSS entry was added (followed by BSS entry id and BSSID) */
#define WPA_EVENT_BSS_ADDED "CTRL-EVENT-BSS-ADDED "
/** A BSS entry was removed (followed by BSS entry id and BSSID) */
@@ -63,6 +65,8 @@ extern "C" {
#define WPS_EVENT_OVERLAP "WPS-OVERLAP-DETECTED "
/** Available WPS AP with active PBC found in scan results */
#define WPS_EVENT_AP_AVAILABLE_PBC "WPS-AP-AVAILABLE-PBC "
+/** Available WPS AP with our address as authorized in scan results */
+#define WPS_EVENT_AP_AVAILABLE_AUTH "WPS-AP-AVAILABLE-AUTH "
/** Available WPS AP with recently selected PIN registrar found in scan results
*/
#define WPS_EVENT_AP_AVAILABLE_PIN "WPS-AP-AVAILABLE-PIN "
@@ -81,11 +85,55 @@ extern "C" {
#define WPS_EVENT_ENROLLEE_SEEN "WPS-ENROLLEE-SEEN "
+#define WPS_EVENT_OPEN_NETWORK "WPS-OPEN-NETWORK "
+
/* WPS ER events */
#define WPS_EVENT_ER_AP_ADD "WPS-ER-AP-ADD "
#define WPS_EVENT_ER_AP_REMOVE "WPS-ER-AP-REMOVE "
#define WPS_EVENT_ER_ENROLLEE_ADD "WPS-ER-ENROLLEE-ADD "
#define WPS_EVENT_ER_ENROLLEE_REMOVE "WPS-ER-ENROLLEE-REMOVE "
+#define WPS_EVENT_ER_AP_SETTINGS "WPS-ER-AP-SETTINGS "
+#define WPS_EVENT_ER_SET_SEL_REG "WPS-ER-AP-SET-SEL-REG "
+
+/** P2P device found */
+#define P2P_EVENT_DEVICE_FOUND "P2P-DEVICE-FOUND "
+
+/** P2P device lost */
+#define P2P_EVENT_DEVICE_LOST "P2P-DEVICE-LOST "
+
+/** A P2P device requested GO negotiation, but we were not ready to start the
+ * negotiation */
+#define P2P_EVENT_GO_NEG_REQUEST "P2P-GO-NEG-REQUEST "
+#define P2P_EVENT_GO_NEG_SUCCESS "P2P-GO-NEG-SUCCESS "
+#define P2P_EVENT_GO_NEG_FAILURE "P2P-GO-NEG-FAILURE "
+#define P2P_EVENT_GROUP_FORMATION_SUCCESS "P2P-GROUP-FORMATION-SUCCESS "
+#define P2P_EVENT_GROUP_FORMATION_FAILURE "P2P-GROUP-FORMATION-FAILURE "
+#define P2P_EVENT_GROUP_STARTED "P2P-GROUP-STARTED "
+#define P2P_EVENT_GROUP_REMOVED "P2P-GROUP-REMOVED "
+#define P2P_EVENT_CROSS_CONNECT_ENABLE "P2P-CROSS-CONNECT-ENABLE "
+#define P2P_EVENT_CROSS_CONNECT_DISABLE "P2P-CROSS-CONNECT-DISABLE "
+/* parameters: <peer address> <PIN> */
+#define P2P_EVENT_PROV_DISC_SHOW_PIN "P2P-PROV-DISC-SHOW-PIN "
+/* parameters: <peer address> */
+#define P2P_EVENT_PROV_DISC_ENTER_PIN "P2P-PROV-DISC-ENTER-PIN "
+/* parameters: <peer address> */
+#define P2P_EVENT_PROV_DISC_PBC_REQ "P2P-PROV-DISC-PBC-REQ "
+/* parameters: <peer address> */
+#define P2P_EVENT_PROV_DISC_PBC_RESP "P2P-PROV-DISC-PBC-RESP "
+/* parameters: <peer address> <status> */
+#define P2P_EVENT_PROV_DISC_FAILURE "P2P-PROV-DISC-FAILURE"
+/* parameters: <freq> <src addr> <dialog token> <update indicator> <TLVs> */
+#define P2P_EVENT_SERV_DISC_REQ "P2P-SERV-DISC-REQ "
+/* parameters: <src addr> <update indicator> <TLVs> */
+#define P2P_EVENT_SERV_DISC_RESP "P2P-SERV-DISC-RESP "
+#define P2P_EVENT_INVITATION_RECEIVED "P2P-INVITATION-RECEIVED "
+#define P2P_EVENT_INVITATION_RESULT "P2P-INVITATION-RESULT "
+#define P2P_EVENT_FIND_STOPPED "P2P-FIND-STOPPED "
+
+#define INTERWORKING_AP "INTERWORKING-AP "
+#define INTERWORKING_NO_MATCH "INTERWORKING-NO-MATCH "
+
+#define GAS_RESPONSE_INFO "GAS-RESPONSE-INFO "
/* hostapd control interface - fixed message prefixes */
#define WPS_EVENT_PIN_NEEDED "WPS-PIN-NEEDED "
@@ -99,6 +147,28 @@ extern "C" {
#define AP_STA_DISCONNECTED "AP-STA-DISCONNECTED "
+/* BSS command information masks */
+
+#define WPA_BSS_MASK_ALL 0xFFFFFFFF
+#define WPA_BSS_MASK_ID BIT(0)
+#define WPA_BSS_MASK_BSSID BIT(1)
+#define WPA_BSS_MASK_FREQ BIT(2)
+#define WPA_BSS_MASK_BEACON_INT BIT(3)
+#define WPA_BSS_MASK_CAPABILITIES BIT(4)
+#define WPA_BSS_MASK_QUAL BIT(5)
+#define WPA_BSS_MASK_NOISE BIT(6)
+#define WPA_BSS_MASK_LEVEL BIT(7)
+#define WPA_BSS_MASK_TSF BIT(8)
+#define WPA_BSS_MASK_AGE BIT(9)
+#define WPA_BSS_MASK_IE BIT(10)
+#define WPA_BSS_MASK_FLAGS BIT(11)
+#define WPA_BSS_MASK_SSID BIT(12)
+#define WPA_BSS_MASK_WPS_SCAN BIT(13)
+#define WPA_BSS_MASK_P2P_SCAN BIT(14)
+#define WPA_BSS_MASK_INTERNETW BIT(15)
+#define WPA_BSS_MASK_WIFI_DISPLAY BIT(16)
+
+
/* wpa_supplicant/hostapd control interface access */
/**
@@ -223,9 +293,25 @@ int wpa_ctrl_pending(struct wpa_ctrl *ctrl);
*/
int wpa_ctrl_get_fd(struct wpa_ctrl *ctrl);
+char * wpa_ctrl_get_remote_ifname(struct wpa_ctrl *ctrl);
+
+#ifdef ANDROID
+/**
+ * wpa_ctrl_cleanup() - Delete any local UNIX domain socket files that
+ * may be left over from clients that were previously connected to
+ * wpa_supplicant. This keeps these files from being orphaned in the
+ * event of crashes that prevented them from being removed as part
+ * of the normal orderly shutdown.
+ */
+void wpa_ctrl_cleanup(void);
+#endif /* ANDROID */
+
#ifdef CONFIG_CTRL_IFACE_UDP
+/* Port range for multiple wpa_supplicant instances and multiple VIFs */
#define WPA_CTRL_IFACE_PORT 9877
+#define WPA_CTRL_IFACE_PORT_LIMIT 50 /* decremented from start */
#define WPA_GLOBAL_CTRL_IFACE_PORT 9878
+#define WPA_GLOBAL_CTRL_IFACE_PORT_LIMIT 20 /* incremented from start */
#endif /* CONFIG_CTRL_IFACE_UDP */
diff --git a/contrib/wpa/src/crypto/.gitignore b/contrib/wpa/src/crypto/.gitignore
deleted file mode 100644
index ee60604..0000000
--- a/contrib/wpa/src/crypto/.gitignore
+++ /dev/null
@@ -1 +0,0 @@
-libcrypto.a
diff --git a/contrib/wpa/src/crypto/Makefile b/contrib/wpa/src/crypto/Makefile
deleted file mode 100644
index 69aa16a..0000000
--- a/contrib/wpa/src/crypto/Makefile
+++ /dev/null
@@ -1,56 +0,0 @@
-all: libcrypto.a
-
-clean:
- rm -f *~ *.o *.d libcrypto.a
-
-install:
- @echo Nothing to be made.
-
-
-include ../lib.rules
-
-CFLAGS += -DCONFIG_TLS_INTERNAL_CLIENT
-CFLAGS += -DCONFIG_TLS_INTERNAL_SERVER
-#CFLAGS += -DALL_DH_GROUPS
-
-LIB_OBJS= \
- aes-cbc.o \
- aes-ctr.o \
- aes-eax.o \
- aes-encblock.o \
- aes-internal.o \
- aes-internal-dec.o \
- aes-internal-enc.o \
- aes-omac1.o \
- aes-unwrap.o \
- aes-wrap.o \
- des-internal.o \
- dh_group5.o \
- dh_groups.o \
- md4-internal.o \
- md5.o \
- md5-internal.o \
- md5-non-fips.o \
- milenage.o \
- ms_funcs.o \
- rc4.o \
- sha1.o \
- sha1-internal.o \
- sha1-pbkdf2.o \
- sha1-tlsprf.o \
- sha1-tprf.o \
- sha256.o \
- sha256-internal.o
-
-LIB_OBJS += crypto_internal.o
-LIB_OBJS += crypto_internal-cipher.o
-LIB_OBJS += crypto_internal-modexp.o
-LIB_OBJS += crypto_internal-rsa.o
-LIB_OBJS += tls_internal.o
-LIB_OBJS += fips_prf_internal.o
-
-
-libcrypto.a: $(LIB_OBJS)
- $(AR) crT $@ $?
-
--include $(OBJS:%.o=%.d)
diff --git a/contrib/wpa/src/crypto/aes-cbc.c b/contrib/wpa/src/crypto/aes-cbc.c
index bd74769..2833cfcc 100644
--- a/contrib/wpa/src/crypto/aes-cbc.c
+++ b/contrib/wpa/src/crypto/aes-cbc.c
@@ -3,14 +3,8 @@
*
* Copyright (c) 2003-2007, Jouni Malinen <j@w1.fi>
*
- * This program is free software; you can redistribute it and/or modify
- * it under the terms of the GNU General Public License version 2 as
- * published by the Free Software Foundation.
- *
- * Alternatively, this software may be distributed under the terms of BSD
- * license.
- *
- * See README and COPYING for more details.
+ * This software may be distributed under the terms of the BSD license.
+ * See README for more details.
*/
#include "includes.h"
diff --git a/contrib/wpa/src/crypto/aes-ccm.c b/contrib/wpa/src/crypto/aes-ccm.c
new file mode 100644
index 0000000..d14670d
--- /dev/null
+++ b/contrib/wpa/src/crypto/aes-ccm.c
@@ -0,0 +1,212 @@
+/*
+ * Counter with CBC-MAC (CCM) with AES
+ *
+ * Copyright (c) 2010-2012, Jouni Malinen <j@w1.fi>
+ *
+ * This software may be distributed under the terms of the BSD license.
+ * See README for more details.
+ */
+
+#include "includes.h"
+
+#include "common.h"
+#include "aes.h"
+#include "aes_wrap.h"
+
+
+static void xor_aes_block(u8 *dst, const u8 *src)
+{
+ u32 *d = (u32 *) dst;
+ u32 *s = (u32 *) src;
+ *d++ ^= *s++;
+ *d++ ^= *s++;
+ *d++ ^= *s++;
+ *d++ ^= *s++;
+}
+
+
+static void aes_ccm_auth_start(void *aes, size_t M, size_t L, const u8 *nonce,
+ const u8 *aad, size_t aad_len, size_t plain_len,
+ u8 *x)
+{
+ u8 aad_buf[2 * AES_BLOCK_SIZE];
+ u8 b[AES_BLOCK_SIZE];
+
+ /* Authentication */
+ /* B_0: Flags | Nonce N | l(m) */
+ b[0] = aad_len ? 0x40 : 0 /* Adata */;
+ b[0] |= (((M - 2) / 2) /* M' */ << 3);
+ b[0] |= (L - 1) /* L' */;
+ os_memcpy(&b[1], nonce, 15 - L);
+ WPA_PUT_BE16(&b[AES_BLOCK_SIZE - L], plain_len);
+
+ wpa_hexdump_key(MSG_EXCESSIVE, "CCM B_0", b, AES_BLOCK_SIZE);
+ aes_encrypt(aes, b, x); /* X_1 = E(K, B_0) */
+
+ if (!aad_len)
+ return;
+
+ WPA_PUT_BE16(aad_buf, aad_len);
+ os_memcpy(aad_buf + 2, aad, aad_len);
+ os_memset(aad_buf + 2 + aad_len, 0, sizeof(aad_buf) - 2 - aad_len);
+
+ xor_aes_block(aad_buf, x);
+ aes_encrypt(aes, aad_buf, x); /* X_2 = E(K, X_1 XOR B_1) */
+
+ if (aad_len > AES_BLOCK_SIZE - 2) {
+ xor_aes_block(&aad_buf[AES_BLOCK_SIZE], x);
+ /* X_3 = E(K, X_2 XOR B_2) */
+ aes_encrypt(aes, &aad_buf[AES_BLOCK_SIZE], x);
+ }
+}
+
+
+static void aes_ccm_auth(void *aes, const u8 *data, size_t len, u8 *x)
+{
+ size_t last = len % AES_BLOCK_SIZE;
+ size_t i;
+
+ for (i = 0; i < len / AES_BLOCK_SIZE; i++) {
+ /* X_i+1 = E(K, X_i XOR B_i) */
+ xor_aes_block(x, data);
+ data += AES_BLOCK_SIZE;
+ aes_encrypt(aes, x, x);
+ }
+ if (last) {
+ /* XOR zero-padded last block */
+ for (i = 0; i < last; i++)
+ x[i] ^= *data++;
+ aes_encrypt(aes, x, x);
+ }
+}
+
+
+static void aes_ccm_encr_start(size_t L, const u8 *nonce, u8 *a)
+{
+ /* A_i = Flags | Nonce N | Counter i */
+ a[0] = L - 1; /* Flags = L' */
+ os_memcpy(&a[1], nonce, 15 - L);
+}
+
+
+static void aes_ccm_encr(void *aes, size_t L, const u8 *in, size_t len, u8 *out,
+ u8 *a)
+{
+ size_t last = len % AES_BLOCK_SIZE;
+ size_t i;
+
+ /* crypt = msg XOR (S_1 | S_2 | ... | S_n) */
+ for (i = 1; i <= len / AES_BLOCK_SIZE; i++) {
+ WPA_PUT_BE16(&a[AES_BLOCK_SIZE - 2], i);
+ /* S_i = E(K, A_i) */
+ aes_encrypt(aes, a, out);
+ xor_aes_block(out, in);
+ out += AES_BLOCK_SIZE;
+ in += AES_BLOCK_SIZE;
+ }
+ if (last) {
+ WPA_PUT_BE16(&a[AES_BLOCK_SIZE - 2], i);
+ aes_encrypt(aes, a, out);
+ /* XOR zero-padded last block */
+ for (i = 0; i < last; i++)
+ *out++ ^= *in++;
+ }
+}
+
+
+static void aes_ccm_encr_auth(void *aes, size_t M, u8 *x, u8 *a, u8 *auth)
+{
+ size_t i;
+ u8 tmp[AES_BLOCK_SIZE];
+
+ wpa_hexdump_key(MSG_EXCESSIVE, "CCM T", x, M);
+ /* U = T XOR S_0; S_0 = E(K, A_0) */
+ WPA_PUT_BE16(&a[AES_BLOCK_SIZE - 2], 0);
+ aes_encrypt(aes, a, tmp);
+ for (i = 0; i < M; i++)
+ auth[i] = x[i] ^ tmp[i];
+ wpa_hexdump_key(MSG_EXCESSIVE, "CCM U", auth, M);
+}
+
+
+static void aes_ccm_decr_auth(void *aes, size_t M, u8 *a, const u8 *auth, u8 *t)
+{
+ size_t i;
+ u8 tmp[AES_BLOCK_SIZE];
+
+ wpa_hexdump_key(MSG_EXCESSIVE, "CCM U", auth, M);
+ /* U = T XOR S_0; S_0 = E(K, A_0) */
+ WPA_PUT_BE16(&a[AES_BLOCK_SIZE - 2], 0);
+ aes_encrypt(aes, a, tmp);
+ for (i = 0; i < M; i++)
+ t[i] = auth[i] ^ tmp[i];
+ wpa_hexdump_key(MSG_EXCESSIVE, "CCM T", t, M);
+}
+
+
+/* AES-CCM with fixed L=2 and aad_len <= 30 assumption */
+int aes_ccm_ae(const u8 *key, size_t key_len, const u8 *nonce,
+ size_t M, const u8 *plain, size_t plain_len,
+ const u8 *aad, size_t aad_len, u8 *crypt, u8 *auth)
+{
+ const size_t L = 2;
+ void *aes;
+ u8 x[AES_BLOCK_SIZE], a[AES_BLOCK_SIZE];
+
+ if (aad_len > 30 || M > AES_BLOCK_SIZE)
+ return -1;
+
+ aes = aes_encrypt_init(key, key_len);
+ if (aes == NULL)
+ return -1;
+
+ aes_ccm_auth_start(aes, M, L, nonce, aad, aad_len, plain_len, x);
+ aes_ccm_auth(aes, plain, plain_len, x);
+
+ /* Encryption */
+ aes_ccm_encr_start(L, nonce, a);
+ aes_ccm_encr(aes, L, plain, plain_len, crypt, a);
+ aes_ccm_encr_auth(aes, M, x, a, auth);
+
+ aes_encrypt_deinit(aes);
+
+ return 0;
+}
+
+
+/* AES-CCM with fixed L=2 and aad_len <= 30 assumption */
+int aes_ccm_ad(const u8 *key, size_t key_len, const u8 *nonce,
+ size_t M, const u8 *crypt, size_t crypt_len,
+ const u8 *aad, size_t aad_len, const u8 *auth, u8 *plain)
+{
+ const size_t L = 2;
+ void *aes;
+ u8 x[AES_BLOCK_SIZE], a[AES_BLOCK_SIZE];
+ u8 t[AES_BLOCK_SIZE];
+
+ if (aad_len > 30 || M > AES_BLOCK_SIZE)
+ return -1;
+
+ aes = aes_encrypt_init(key, key_len);
+ if (aes == NULL)
+ return -1;
+
+ /* Decryption */
+ aes_ccm_encr_start(L, nonce, a);
+ aes_ccm_decr_auth(aes, M, a, auth, t);
+
+ /* plaintext = msg XOR (S_1 | S_2 | ... | S_n) */
+ aes_ccm_encr(aes, L, crypt, crypt_len, plain, a);
+
+ aes_ccm_auth_start(aes, M, L, nonce, aad, aad_len, crypt_len, x);
+ aes_ccm_auth(aes, plain, crypt_len, x);
+
+ aes_encrypt_deinit(aes);
+
+ if (os_memcmp(x, t, M) != 0) {
+ wpa_printf(MSG_EXCESSIVE, "CCM: Auth mismatch");
+ return -1;
+ }
+
+ return 0;
+}
diff --git a/contrib/wpa/src/crypto/aes-ctr.c b/contrib/wpa/src/crypto/aes-ctr.c
index 468f877..d4d874d 100644
--- a/contrib/wpa/src/crypto/aes-ctr.c
+++ b/contrib/wpa/src/crypto/aes-ctr.c
@@ -3,14 +3,8 @@
*
* Copyright (c) 2003-2007, Jouni Malinen <j@w1.fi>
*
- * This program is free software; you can redistribute it and/or modify
- * it under the terms of the GNU General Public License version 2 as
- * published by the Free Software Foundation.
- *
- * Alternatively, this software may be distributed under the terms of BSD
- * license.
- *
- * See README and COPYING for more details.
+ * This software may be distributed under the terms of the BSD license.
+ * See README for more details.
*/
#include "includes.h"
diff --git a/contrib/wpa/src/crypto/aes-eax.c b/contrib/wpa/src/crypto/aes-eax.c
index d5c3971..21941c6 100644
--- a/contrib/wpa/src/crypto/aes-eax.c
+++ b/contrib/wpa/src/crypto/aes-eax.c
@@ -3,14 +3,8 @@
*
* Copyright (c) 2003-2007, Jouni Malinen <j@w1.fi>
*
- * This program is free software; you can redistribute it and/or modify
- * it under the terms of the GNU General Public License version 2 as
- * published by the Free Software Foundation.
- *
- * Alternatively, this software may be distributed under the terms of BSD
- * license.
- *
- * See README and COPYING for more details.
+ * This software may be distributed under the terms of the BSD license.
+ * See README for more details.
*/
#include "includes.h"
diff --git a/contrib/wpa/src/crypto/aes-encblock.c b/contrib/wpa/src/crypto/aes-encblock.c
index 8f35caa..a521621 100644
--- a/contrib/wpa/src/crypto/aes-encblock.c
+++ b/contrib/wpa/src/crypto/aes-encblock.c
@@ -3,14 +3,8 @@
*
* Copyright (c) 2003-2007, Jouni Malinen <j@w1.fi>
*
- * This program is free software; you can redistribute it and/or modify
- * it under the terms of the GNU General Public License version 2 as
- * published by the Free Software Foundation.
- *
- * Alternatively, this software may be distributed under the terms of BSD
- * license.
- *
- * See README and COPYING for more details.
+ * This software may be distributed under the terms of the BSD license.
+ * See README for more details.
*/
#include "includes.h"
diff --git a/contrib/wpa/src/crypto/aes-gcm.c b/contrib/wpa/src/crypto/aes-gcm.c
new file mode 100644
index 0000000..3d91c71
--- /dev/null
+++ b/contrib/wpa/src/crypto/aes-gcm.c
@@ -0,0 +1,327 @@
+/*
+ * Galois/Counter Mode (GCM) and GMAC with AES
+ *
+ * Copyright (c) 2012, Jouni Malinen <j@w1.fi>
+ *
+ * This software may be distributed under the terms of the BSD license.
+ * See README for more details.
+ */
+
+#include "includes.h"
+
+#include "common.h"
+#include "aes.h"
+#include "aes_wrap.h"
+
+static void inc32(u8 *block)
+{
+ u32 val;
+ val = WPA_GET_BE32(block + AES_BLOCK_SIZE - 4);
+ val++;
+ WPA_PUT_BE32(block + AES_BLOCK_SIZE - 4, val);
+}
+
+
+static void xor_block(u8 *dst, const u8 *src)
+{
+ u32 *d = (u32 *) dst;
+ u32 *s = (u32 *) src;
+ *d++ ^= *s++;
+ *d++ ^= *s++;
+ *d++ ^= *s++;
+ *d++ ^= *s++;
+}
+
+
+static void shift_right_block(u8 *v)
+{
+ u32 val;
+
+ val = WPA_GET_BE32(v + 12);
+ val >>= 1;
+ if (v[11] & 0x01)
+ val |= 0x80000000;
+ WPA_PUT_BE32(v + 12, val);
+
+ val = WPA_GET_BE32(v + 8);
+ val >>= 1;
+ if (v[7] & 0x01)
+ val |= 0x80000000;
+ WPA_PUT_BE32(v + 8, val);
+
+ val = WPA_GET_BE32(v + 4);
+ val >>= 1;
+ if (v[3] & 0x01)
+ val |= 0x80000000;
+ WPA_PUT_BE32(v + 4, val);
+
+ val = WPA_GET_BE32(v);
+ val >>= 1;
+ WPA_PUT_BE32(v, val);
+}
+
+
+/* Multiplication in GF(2^128) */
+static void gf_mult(const u8 *x, const u8 *y, u8 *z)
+{
+ u8 v[16];
+ int i, j;
+
+ os_memset(z, 0, 16); /* Z_0 = 0^128 */
+ os_memcpy(v, y, 16); /* V_0 = Y */
+
+ for (i = 0; i < 16; i++) {
+ for (j = 0; j < 8; j++) {
+ if (x[i] & BIT(7 - j)) {
+ /* Z_(i + 1) = Z_i XOR V_i */
+ xor_block(z, v);
+ } else {
+ /* Z_(i + 1) = Z_i */
+ }
+
+ if (v[15] & 0x01) {
+ /* V_(i + 1) = (V_i >> 1) XOR R */
+ shift_right_block(v);
+ /* R = 11100001 || 0^120 */
+ v[0] ^= 0xe1;
+ } else {
+ /* V_(i + 1) = V_i >> 1 */
+ shift_right_block(v);
+ }
+ }
+ }
+}
+
+
+static void ghash_start(u8 *y)
+{
+ /* Y_0 = 0^128 */
+ os_memset(y, 0, 16);
+}
+
+
+static void ghash(const u8 *h, const u8 *x, size_t xlen, u8 *y)
+{
+ size_t m, i;
+ const u8 *xpos = x;
+ u8 tmp[16];
+
+ m = xlen / 16;
+
+ for (i = 0; i < m; i++) {
+ /* Y_i = (Y^(i-1) XOR X_i) dot H */
+ xor_block(y, xpos);
+ xpos += 16;
+
+ /* dot operation:
+ * multiplication operation for binary Galois (finite) field of
+ * 2^128 elements */
+ gf_mult(y, h, tmp);
+ os_memcpy(y, tmp, 16);
+ }
+
+ if (x + xlen > xpos) {
+ /* Add zero padded last block */
+ size_t last = x + xlen - xpos;
+ os_memcpy(tmp, xpos, last);
+ os_memset(tmp + last, 0, sizeof(tmp) - last);
+
+ /* Y_i = (Y^(i-1) XOR X_i) dot H */
+ xor_block(y, tmp);
+
+ /* dot operation:
+ * multiplication operation for binary Galois (finite) field of
+ * 2^128 elements */
+ gf_mult(y, h, tmp);
+ os_memcpy(y, tmp, 16);
+ }
+
+ /* Return Y_m */
+}
+
+
+static void aes_gctr(void *aes, const u8 *icb, const u8 *x, size_t xlen, u8 *y)
+{
+ size_t i, n, last;
+ u8 cb[AES_BLOCK_SIZE], tmp[AES_BLOCK_SIZE];
+ const u8 *xpos = x;
+ u8 *ypos = y;
+
+ if (xlen == 0)
+ return;
+
+ n = xlen / 16;
+
+ os_memcpy(cb, icb, AES_BLOCK_SIZE);
+ /* Full blocks */
+ for (i = 0; i < n; i++) {
+ aes_encrypt(aes, cb, ypos);
+ xor_block(ypos, xpos);
+ xpos += AES_BLOCK_SIZE;
+ ypos += AES_BLOCK_SIZE;
+ inc32(cb);
+ }
+
+ last = x + xlen - xpos;
+ if (last) {
+ /* Last, partial block */
+ aes_encrypt(aes, cb, tmp);
+ for (i = 0; i < last; i++)
+ *ypos++ = *xpos++ ^ tmp[i];
+ }
+}
+
+
+static void * aes_gcm_init_hash_subkey(const u8 *key, size_t key_len, u8 *H)
+{
+ void *aes;
+
+ aes = aes_encrypt_init(key, key_len);
+ if (aes == NULL)
+ return NULL;
+
+ /* Generate hash subkey H = AES_K(0^128) */
+ os_memset(H, 0, AES_BLOCK_SIZE);
+ aes_encrypt(aes, H, H);
+ wpa_hexdump_key(MSG_EXCESSIVE, "Hash subkey H for GHASH",
+ H, AES_BLOCK_SIZE);
+ return aes;
+}
+
+
+static void aes_gcm_prepare_j0(const u8 *iv, size_t iv_len, const u8 *H, u8 *J0)
+{
+ u8 len_buf[16];
+
+ if (iv_len == 12) {
+ /* Prepare block J_0 = IV || 0^31 || 1 [len(IV) = 96] */
+ os_memcpy(J0, iv, iv_len);
+ os_memset(J0 + iv_len, 0, AES_BLOCK_SIZE - iv_len);
+ J0[AES_BLOCK_SIZE - 1] = 0x01;
+ } else {
+ /*
+ * s = 128 * ceil(len(IV)/128) - len(IV)
+ * J_0 = GHASH_H(IV || 0^(s+64) || [len(IV)]_64)
+ */
+ ghash_start(J0);
+ ghash(H, iv, iv_len, J0);
+ WPA_PUT_BE64(len_buf, 0);
+ WPA_PUT_BE64(len_buf + 8, iv_len * 8);
+ ghash(H, len_buf, sizeof(len_buf), J0);
+ }
+}
+
+
+static void aes_gcm_gctr(void *aes, const u8 *J0, const u8 *in, size_t len,
+ u8 *out)
+{
+ u8 J0inc[AES_BLOCK_SIZE];
+
+ if (len == 0)
+ return;
+
+ os_memcpy(J0inc, J0, AES_BLOCK_SIZE);
+ inc32(J0inc);
+ aes_gctr(aes, J0inc, in, len, out);
+}
+
+
+static void aes_gcm_ghash(const u8 *H, const u8 *aad, size_t aad_len,
+ const u8 *crypt, size_t crypt_len, u8 *S)
+{
+ u8 len_buf[16];
+
+ /*
+ * u = 128 * ceil[len(C)/128] - len(C)
+ * v = 128 * ceil[len(A)/128] - len(A)
+ * S = GHASH_H(A || 0^v || C || 0^u || [len(A)]64 || [len(C)]64)
+ * (i.e., zero padded to block size A || C and lengths of each in bits)
+ */
+ ghash_start(S);
+ ghash(H, aad, aad_len, S);
+ ghash(H, crypt, crypt_len, S);
+ WPA_PUT_BE64(len_buf, aad_len * 8);
+ WPA_PUT_BE64(len_buf + 8, crypt_len * 8);
+ ghash(H, len_buf, sizeof(len_buf), S);
+
+ wpa_hexdump_key(MSG_EXCESSIVE, "S = GHASH_H(...)", S, 16);
+}
+
+
+/**
+ * aes_gcm_ae - GCM-AE_K(IV, P, A)
+ */
+int aes_gcm_ae(const u8 *key, size_t key_len, const u8 *iv, size_t iv_len,
+ const u8 *plain, size_t plain_len,
+ const u8 *aad, size_t aad_len, u8 *crypt, u8 *tag)
+{
+ u8 H[AES_BLOCK_SIZE];
+ u8 J0[AES_BLOCK_SIZE];
+ u8 S[16];
+ void *aes;
+
+ aes = aes_gcm_init_hash_subkey(key, key_len, H);
+ if (aes == NULL)
+ return -1;
+
+ aes_gcm_prepare_j0(iv, iv_len, H, J0);
+
+ /* C = GCTR_K(inc_32(J_0), P) */
+ aes_gcm_gctr(aes, J0, plain, plain_len, crypt);
+
+ aes_gcm_ghash(H, aad, aad_len, crypt, plain_len, S);
+
+ /* T = MSB_t(GCTR_K(J_0, S)) */
+ aes_gctr(aes, J0, S, sizeof(S), tag);
+
+ /* Return (C, T) */
+
+ aes_encrypt_deinit(aes);
+
+ return 0;
+}
+
+
+/**
+ * aes_gcm_ad - GCM-AD_K(IV, C, A, T)
+ */
+int aes_gcm_ad(const u8 *key, size_t key_len, const u8 *iv, size_t iv_len,
+ const u8 *crypt, size_t crypt_len,
+ const u8 *aad, size_t aad_len, const u8 *tag, u8 *plain)
+{
+ u8 H[AES_BLOCK_SIZE];
+ u8 J0[AES_BLOCK_SIZE];
+ u8 S[16], T[16];
+ void *aes;
+
+ aes = aes_gcm_init_hash_subkey(key, key_len, H);
+ if (aes == NULL)
+ return -1;
+
+ aes_gcm_prepare_j0(iv, iv_len, H, J0);
+
+ /* P = GCTR_K(inc_32(J_0), C) */
+ aes_gcm_gctr(aes, J0, crypt, crypt_len, plain);
+
+ aes_gcm_ghash(H, aad, aad_len, crypt, crypt_len, S);
+
+ /* T' = MSB_t(GCTR_K(J_0, S)) */
+ aes_gctr(aes, J0, S, sizeof(S), T);
+
+ aes_encrypt_deinit(aes);
+
+ if (os_memcmp(tag, T, 16) != 0) {
+ wpa_printf(MSG_EXCESSIVE, "GCM: Tag mismatch");
+ return -1;
+ }
+
+ return 0;
+}
+
+
+int aes_gmac(const u8 *key, size_t key_len, const u8 *iv, size_t iv_len,
+ const u8 *aad, size_t aad_len, u8 *tag)
+{
+ return aes_gcm_ae(key, key_len, iv, iv_len, NULL, 0, aad, aad_len, NULL,
+ tag);
+}
diff --git a/contrib/wpa/src/crypto/aes-internal-dec.c b/contrib/wpa/src/crypto/aes-internal-dec.c
index 2d32c03..720c703 100644
--- a/contrib/wpa/src/crypto/aes-internal-dec.c
+++ b/contrib/wpa/src/crypto/aes-internal-dec.c
@@ -2,23 +2,16 @@
* AES (Rijndael) cipher - decrypt
*
* Modifications to public domain implementation:
- * - support only 128-bit keys
* - cleanup
* - use C pre-processor to make it easier to change S table access
* - added option (AES_SMALL_TABLES) for reducing code size by about 8 kB at
* cost of reduced throughput (quite small difference on Pentium 4,
* 10-25% when using -O1 or -O2 optimization)
*
- * Copyright (c) 2003-2005, Jouni Malinen <j@w1.fi>
+ * Copyright (c) 2003-2012, Jouni Malinen <j@w1.fi>
*
- * This program is free software; you can redistribute it and/or modify
- * it under the terms of the GNU General Public License version 2 as
- * published by the Free Software Foundation.
- *
- * Alternatively, this software may be distributed under the terms of BSD
- * license.
- *
- * See README and COPYING for more details.
+ * This software may be distributed under the terms of the BSD license.
+ * See README for more details.
*/
#include "includes.h"
@@ -32,13 +25,15 @@
*
* @return the number of rounds for the given cipher key size.
*/
-void rijndaelKeySetupDec(u32 rk[/*44*/], const u8 cipherKey[])
+static int rijndaelKeySetupDec(u32 rk[], const u8 cipherKey[], int keyBits)
{
- int Nr = 10, i, j;
+ int Nr, i, j;
u32 temp;
/* expand the cipher key: */
- rijndaelKeySetupEnc(rk, cipherKey);
+ Nr = rijndaelKeySetupEnc(rk, cipherKey, keyBits);
+ if (Nr < 0)
+ return Nr;
/* invert the order of the round keys: */
for (i = 0, j = 4*Nr; i < j; i += 4, j -= 4) {
temp = rk[i ]; rk[i ] = rk[j ]; rk[j ] = temp;
@@ -57,24 +52,30 @@ void rijndaelKeySetupDec(u32 rk[/*44*/], const u8 cipherKey[])
TD3_(TE4((rk[j] ) & 0xff));
}
}
+
+ return Nr;
}
void * aes_decrypt_init(const u8 *key, size_t len)
{
u32 *rk;
- if (len != 16)
- return NULL;
+ int res;
rk = os_malloc(AES_PRIV_SIZE);
if (rk == NULL)
return NULL;
- rijndaelKeySetupDec(rk, key);
+ res = rijndaelKeySetupDec(rk, key, len * 8);
+ if (res < 0) {
+ os_free(rk);
+ return NULL;
+ }
+ rk[AES_PRIV_NR_POS] = res;
return rk;
}
-static void rijndaelDecrypt(const u32 rk[/*44*/], const u8 ct[16], u8 pt[16])
+static void rijndaelDecrypt(const u32 rk[/*44*/], int Nr, const u8 ct[16],
+ u8 pt[16])
{
u32 s0, s1, s2, s3, t0, t1, t2, t3;
- const int Nr = 10;
#ifndef FULL_UNROLL
int r;
#endif /* ?FULL_UNROLL */
@@ -105,6 +106,14 @@ d##3 = TD0(s##3) ^ TD1(s##2) ^ TD2(s##1) ^ TD3(s##0) ^ rk[4 * i + 3]
ROUND(7,t,s);
ROUND(8,s,t);
ROUND(9,t,s);
+ if (Nr > 10) {
+ ROUND(10,s,t);
+ ROUND(11,t,s);
+ if (Nr > 12) {
+ ROUND(12,s,t);
+ ROUND(13,t,s);
+ }
+ }
rk += Nr << 2;
@@ -140,7 +149,8 @@ d##3 = TD0(s##3) ^ TD1(s##2) ^ TD2(s##1) ^ TD3(s##0) ^ rk[4 * i + 3]
void aes_decrypt(void *ctx, const u8 *crypt, u8 *plain)
{
- rijndaelDecrypt(ctx, crypt, plain);
+ u32 *rk = ctx;
+ rijndaelDecrypt(ctx, rk[AES_PRIV_NR_POS], crypt, plain);
}
diff --git a/contrib/wpa/src/crypto/aes-internal-enc.c b/contrib/wpa/src/crypto/aes-internal-enc.c
index 2f19826..f3c61b8 100644
--- a/contrib/wpa/src/crypto/aes-internal-enc.c
+++ b/contrib/wpa/src/crypto/aes-internal-enc.c
@@ -2,23 +2,16 @@
* AES (Rijndael) cipher - encrypt
*
* Modifications to public domain implementation:
- * - support only 128-bit keys
* - cleanup
* - use C pre-processor to make it easier to change S table access
* - added option (AES_SMALL_TABLES) for reducing code size by about 8 kB at
* cost of reduced throughput (quite small difference on Pentium 4,
* 10-25% when using -O1 or -O2 optimization)
*
- * Copyright (c) 2003-2005, Jouni Malinen <j@w1.fi>
+ * Copyright (c) 2003-2012, Jouni Malinen <j@w1.fi>
*
- * This program is free software; you can redistribute it and/or modify
- * it under the terms of the GNU General Public License version 2 as
- * published by the Free Software Foundation.
- *
- * Alternatively, this software may be distributed under the terms of BSD
- * license.
- *
- * See README and COPYING for more details.
+ * This software may be distributed under the terms of the BSD license.
+ * See README for more details.
*/
#include "includes.h"
@@ -27,10 +20,9 @@
#include "crypto.h"
#include "aes_i.h"
-void rijndaelEncrypt(const u32 rk[/*44*/], const u8 pt[16], u8 ct[16])
+static void rijndaelEncrypt(const u32 rk[], int Nr, const u8 pt[16], u8 ct[16])
{
u32 s0, s1, s2, s3, t0, t1, t2, t3;
- const int Nr = 10;
#ifndef FULL_UNROLL
int r;
#endif /* ?FULL_UNROLL */
@@ -61,6 +53,14 @@ d##3 = TE0(s##3) ^ TE1(s##0) ^ TE2(s##1) ^ TE3(s##2) ^ rk[4 * i + 3]
ROUND(7,t,s);
ROUND(8,s,t);
ROUND(9,t,s);
+ if (Nr > 10) {
+ ROUND(10,s,t);
+ ROUND(11,t,s);
+ if (Nr > 12) {
+ ROUND(12,s,t);
+ ROUND(13,t,s);
+ }
+ }
rk += Nr << 2;
@@ -98,19 +98,24 @@ d##3 = TE0(s##3) ^ TE1(s##0) ^ TE2(s##1) ^ TE3(s##2) ^ rk[4 * i + 3]
void * aes_encrypt_init(const u8 *key, size_t len)
{
u32 *rk;
- if (len != 16)
- return NULL;
+ int res;
rk = os_malloc(AES_PRIV_SIZE);
if (rk == NULL)
return NULL;
- rijndaelKeySetupEnc(rk, key);
+ res = rijndaelKeySetupEnc(rk, key, len * 8);
+ if (res < 0) {
+ os_free(rk);
+ return NULL;
+ }
+ rk[AES_PRIV_NR_POS] = res;
return rk;
}
void aes_encrypt(void *ctx, const u8 *plain, u8 *crypt)
{
- rijndaelEncrypt(ctx, plain, crypt);
+ u32 *rk = ctx;
+ rijndaelEncrypt(ctx, rk[AES_PRIV_NR_POS], plain, crypt);
}
diff --git a/contrib/wpa/src/crypto/aes-internal.c b/contrib/wpa/src/crypto/aes-internal.c
index 4161220..bd4535d 100644
--- a/contrib/wpa/src/crypto/aes-internal.c
+++ b/contrib/wpa/src/crypto/aes-internal.c
@@ -2,23 +2,16 @@
* AES (Rijndael) cipher
*
* Modifications to public domain implementation:
- * - support only 128-bit keys
* - cleanup
* - use C pre-processor to make it easier to change S table access
* - added option (AES_SMALL_TABLES) for reducing code size by about 8 kB at
* cost of reduced throughput (quite small difference on Pentium 4,
* 10-25% when using -O1 or -O2 optimization)
*
- * Copyright (c) 2003-2005, Jouni Malinen <j@w1.fi>
+ * Copyright (c) 2003-2012, Jouni Malinen <j@w1.fi>
*
- * This program is free software; you can redistribute it and/or modify
- * it under the terms of the GNU General Public License version 2 as
- * published by the Free Software Foundation.
- *
- * Alternatively, this software may be distributed under the terms of BSD
- * license.
- *
- * See README and COPYING for more details.
+ * This software may be distributed under the terms of the BSD license.
+ * See README for more details.
*/
#include "includes.h"
@@ -783,7 +776,7 @@ const u8 rcons[] = {
*
* @return the number of rounds for the given cipher key size.
*/
-void rijndaelKeySetupEnc(u32 rk[/*44*/], const u8 cipherKey[])
+int rijndaelKeySetupEnc(u32 rk[], const u8 cipherKey[], int keyBits)
{
int i;
u32 temp;
@@ -792,14 +785,61 @@ void rijndaelKeySetupEnc(u32 rk[/*44*/], const u8 cipherKey[])
rk[1] = GETU32(cipherKey + 4);
rk[2] = GETU32(cipherKey + 8);
rk[3] = GETU32(cipherKey + 12);
- for (i = 0; i < 10; i++) {
- temp = rk[3];
- rk[4] = rk[0] ^
- TE421(temp) ^ TE432(temp) ^ TE443(temp) ^ TE414(temp) ^
- RCON(i);
- rk[5] = rk[1] ^ rk[4];
- rk[6] = rk[2] ^ rk[5];
- rk[7] = rk[3] ^ rk[6];
- rk += 4;
+
+ if (keyBits == 128) {
+ for (i = 0; i < 10; i++) {
+ temp = rk[3];
+ rk[4] = rk[0] ^ TE421(temp) ^ TE432(temp) ^
+ TE443(temp) ^ TE414(temp) ^ RCON(i);
+ rk[5] = rk[1] ^ rk[4];
+ rk[6] = rk[2] ^ rk[5];
+ rk[7] = rk[3] ^ rk[6];
+ rk += 4;
+ }
+ return 10;
+ }
+
+ rk[4] = GETU32(cipherKey + 16);
+ rk[5] = GETU32(cipherKey + 20);
+
+ if (keyBits == 192) {
+ for (i = 0; i < 8; i++) {
+ temp = rk[5];
+ rk[6] = rk[0] ^ TE421(temp) ^ TE432(temp) ^
+ TE443(temp) ^ TE414(temp) ^ RCON(i);
+ rk[7] = rk[1] ^ rk[6];
+ rk[8] = rk[2] ^ rk[7];
+ rk[9] = rk[3] ^ rk[8];
+ if (i == 7)
+ return 12;
+ rk[10] = rk[4] ^ rk[9];
+ rk[11] = rk[5] ^ rk[10];
+ rk += 6;
+ }
}
+
+ rk[6] = GETU32(cipherKey + 24);
+ rk[7] = GETU32(cipherKey + 28);
+
+ if (keyBits == 256) {
+ for (i = 0; i < 7; i++) {
+ temp = rk[7];
+ rk[8] = rk[0] ^ TE421(temp) ^ TE432(temp) ^
+ TE443(temp) ^ TE414(temp) ^ RCON(i);
+ rk[9] = rk[1] ^ rk[8];
+ rk[10] = rk[2] ^ rk[9];
+ rk[11] = rk[3] ^ rk[10];
+ if (i == 6)
+ return 14;
+ temp = rk[11];
+ rk[12] = rk[4] ^ TE411(temp) ^ TE422(temp) ^
+ TE433(temp) ^ TE444(temp);
+ rk[13] = rk[5] ^ rk[12];
+ rk[14] = rk[6] ^ rk[13];
+ rk[15] = rk[7] ^ rk[14];
+ rk += 8;
+ }
+ }
+
+ return -1;
}
diff --git a/contrib/wpa/src/crypto/aes-omac1.c b/contrib/wpa/src/crypto/aes-omac1.c
index f775296..27895eb 100644
--- a/contrib/wpa/src/crypto/aes-omac1.c
+++ b/contrib/wpa/src/crypto/aes-omac1.c
@@ -3,14 +3,8 @@
*
* Copyright (c) 2003-2007, Jouni Malinen <j@w1.fi>
*
- * This program is free software; you can redistribute it and/or modify
- * it under the terms of the GNU General Public License version 2 as
- * published by the Free Software Foundation.
- *
- * Alternatively, this software may be distributed under the terms of BSD
- * license.
- *
- * See README and COPYING for more details.
+ * This software may be distributed under the terms of the BSD license.
+ * See README for more details.
*/
#include "includes.h"
diff --git a/contrib/wpa/src/crypto/aes-unwrap.c b/contrib/wpa/src/crypto/aes-unwrap.c
index f233ffa..9dd5160 100644
--- a/contrib/wpa/src/crypto/aes-unwrap.c
+++ b/contrib/wpa/src/crypto/aes-unwrap.c
@@ -3,14 +3,8 @@
*
* Copyright (c) 2003-2007, Jouni Malinen <j@w1.fi>
*
- * This program is free software; you can redistribute it and/or modify
- * it under the terms of the GNU General Public License version 2 as
- * published by the Free Software Foundation.
- *
- * Alternatively, this software may be distributed under the terms of BSD
- * license.
- *
- * See README and COPYING for more details.
+ * This software may be distributed under the terms of the BSD license.
+ * See README for more details.
*/
#include "includes.h"
diff --git a/contrib/wpa/src/crypto/aes-wrap.c b/contrib/wpa/src/crypto/aes-wrap.c
index 28d0c89..89d6f94 100644
--- a/contrib/wpa/src/crypto/aes-wrap.c
+++ b/contrib/wpa/src/crypto/aes-wrap.c
@@ -3,14 +3,8 @@
*
* Copyright (c) 2003-2007, Jouni Malinen <j@w1.fi>
*
- * This program is free software; you can redistribute it and/or modify
- * it under the terms of the GNU General Public License version 2 as
- * published by the Free Software Foundation.
- *
- * Alternatively, this software may be distributed under the terms of BSD
- * license.
- *
- * See README and COPYING for more details.
+ * This software may be distributed under the terms of the BSD license.
+ * See README for more details.
*/
#include "includes.h"
diff --git a/contrib/wpa/src/crypto/aes.h b/contrib/wpa/src/crypto/aes.h
index ba384a9..2de59e0 100644
--- a/contrib/wpa/src/crypto/aes.h
+++ b/contrib/wpa/src/crypto/aes.h
@@ -2,14 +2,8 @@
* AES functions
* Copyright (c) 2003-2006, Jouni Malinen <j@w1.fi>
*
- * This program is free software; you can redistribute it and/or modify
- * it under the terms of the GNU General Public License version 2 as
- * published by the Free Software Foundation.
- *
- * Alternatively, this software may be distributed under the terms of BSD
- * license.
- *
- * See README and COPYING for more details.
+ * This software may be distributed under the terms of the BSD license.
+ * See README for more details.
*/
#ifndef AES_H
diff --git a/contrib/wpa/src/crypto/aes_i.h b/contrib/wpa/src/crypto/aes_i.h
index 6b40bc7..54375cf 100644
--- a/contrib/wpa/src/crypto/aes_i.h
+++ b/contrib/wpa/src/crypto/aes_i.h
@@ -1,15 +1,9 @@
/*
* AES (Rijndael) cipher
- * Copyright (c) 2003-2005, Jouni Malinen <j@w1.fi>
+ * Copyright (c) 2003-2012, Jouni Malinen <j@w1.fi>
*
- * This program is free software; you can redistribute it and/or modify
- * it under the terms of the GNU General Public License version 2 as
- * published by the Free Software Foundation.
- *
- * Alternatively, this software may be distributed under the terms of BSD
- * license.
- *
- * See README and COPYING for more details.
+ * This software may be distributed under the terms of the BSD license.
+ * See README for more details.
*/
#ifndef AES_I_H
@@ -50,6 +44,10 @@ extern const u8 rcons[10];
#define TE432(i) (Te4[((i) >> 8) & 0xff] & 0x00ff0000)
#define TE443(i) (Te4[(i) & 0xff] & 0x0000ff00)
#define TE414(i) (Te4[((i) >> 24) & 0xff] & 0x000000ff)
+#define TE411(i) (Te4[((i) >> 24) & 0xff] & 0xff000000)
+#define TE422(i) (Te4[((i) >> 16) & 0xff] & 0x00ff0000)
+#define TE433(i) (Te4[((i) >> 8) & 0xff] & 0x0000ff00)
+#define TE444(i) (Te4[(i) & 0xff] & 0x000000ff)
#define TE4(i) (Te4[(i)] & 0x000000ff)
#define TD0(i) Td0[((i) >> 24) & 0xff]
@@ -86,6 +84,10 @@ static inline u32 rotr(u32 val, int bits)
#define TE432(i) (Te0[((i) >> 8) & 0xff] & 0x00ff0000)
#define TE443(i) (Te0[(i) & 0xff] & 0x0000ff00)
#define TE414(i) ((Te0[((i) >> 24) & 0xff] >> 8) & 0x000000ff)
+#define TE411(i) ((Te0[((i) >> 24) & 0xff] << 8) & 0xff000000)
+#define TE422(i) (Te0[((i) >> 16) & 0xff] & 0x00ff0000)
+#define TE433(i) (Te0[((i) >> 8) & 0xff] & 0x0000ff00)
+#define TE444(i) ((Te0[(i) & 0xff] >> 8) & 0x000000ff)
#define TE4(i) ((Te0[(i)] >> 8) & 0x000000ff)
#define TD0(i) Td0[((i) >> 24) & 0xff]
@@ -115,8 +117,9 @@ static inline u32 rotr(u32 val, int bits)
(ct)[2] = (u8)((st) >> 8); (ct)[3] = (u8)(st); }
#endif
-#define AES_PRIV_SIZE (4 * 44)
+#define AES_PRIV_SIZE (4 * 4 * 15 + 4)
+#define AES_PRIV_NR_POS (4 * 15)
-void rijndaelKeySetupEnc(u32 rk[/*44*/], const u8 cipherKey[]);
+int rijndaelKeySetupEnc(u32 rk[], const u8 cipherKey[], int keyBits);
#endif /* AES_I_H */
diff --git a/contrib/wpa/src/crypto/aes_wrap.h b/contrib/wpa/src/crypto/aes_wrap.h
index 4b1c7b0..0433c04 100644
--- a/contrib/wpa/src/crypto/aes_wrap.h
+++ b/contrib/wpa/src/crypto/aes_wrap.h
@@ -6,17 +6,13 @@
* - AES-128 CTR mode encryption
* - AES-128 EAX mode encryption/decryption
* - AES-128 CBC
+ * - AES-GCM
+ * - AES-CCM
*
- * Copyright (c) 2003-2007, Jouni Malinen <j@w1.fi>
+ * Copyright (c) 2003-2012, Jouni Malinen <j@w1.fi>
*
- * This program is free software; you can redistribute it and/or modify
- * it under the terms of the GNU General Public License version 2 as
- * published by the Free Software Foundation.
- *
- * Alternatively, this software may be distributed under the terms of BSD
- * license.
- *
- * See README and COPYING for more details.
+ * This software may be distributed under the terms of the BSD license.
+ * See README for more details.
*/
#ifndef AES_WRAP_H
@@ -44,5 +40,25 @@ int __must_check aes_128_cbc_encrypt(const u8 *key, const u8 *iv, u8 *data,
size_t data_len);
int __must_check aes_128_cbc_decrypt(const u8 *key, const u8 *iv, u8 *data,
size_t data_len);
+int __must_check aes_gcm_ae(const u8 *key, size_t key_len,
+ const u8 *iv, size_t iv_len,
+ const u8 *plain, size_t plain_len,
+ const u8 *aad, size_t aad_len,
+ u8 *crypt, u8 *tag);
+int __must_check aes_gcm_ad(const u8 *key, size_t key_len,
+ const u8 *iv, size_t iv_len,
+ const u8 *crypt, size_t crypt_len,
+ const u8 *aad, size_t aad_len, const u8 *tag,
+ u8 *plain);
+int __must_check aes_gmac(const u8 *key, size_t key_len,
+ const u8 *iv, size_t iv_len,
+ const u8 *aad, size_t aad_len, u8 *tag);
+int __must_check aes_ccm_ae(const u8 *key, size_t key_len, const u8 *nonce,
+ size_t M, const u8 *plain, size_t plain_len,
+ const u8 *aad, size_t aad_len, u8 *crypt, u8 *auth);
+int __must_check aes_ccm_ad(const u8 *key, size_t key_len, const u8 *nonce,
+ size_t M, const u8 *crypt, size_t crypt_len,
+ const u8 *aad, size_t aad_len, const u8 *auth,
+ u8 *plain);
#endif /* AES_WRAP_H */
diff --git a/contrib/wpa/src/crypto/crypto.h b/contrib/wpa/src/crypto/crypto.h
index 587b5a9..26b9acf 100644
--- a/contrib/wpa/src/crypto/crypto.h
+++ b/contrib/wpa/src/crypto/crypto.h
@@ -2,14 +2,8 @@
* WPA Supplicant / wrapper functions for crypto libraries
* Copyright (c) 2004-2009, Jouni Malinen <j@w1.fi>
*
- * This program is free software; you can redistribute it and/or modify
- * it under the terms of the GNU General Public License version 2 as
- * published by the Free Software Foundation.
- *
- * Alternatively, this software may be distributed under the terms of BSD
- * license.
- *
- * See README and COPYING for more details.
+ * This software may be distributed under the terms of the BSD license.
+ * See README for more details.
*
* This file defines the cryptographic functions that need to be implemented
* for wpa_supplicant and hostapd. When TLS is not used, internal
@@ -47,21 +41,6 @@ int md4_vector(size_t num_elem, const u8 *addr[], const size_t *len, u8 *mac);
*/
int md5_vector(size_t num_elem, const u8 *addr[], const size_t *len, u8 *mac);
-#ifdef CONFIG_FIPS
-/**
- * md5_vector_non_fips_allow - MD5 hash for data vector (non-FIPS use allowed)
- * @num_elem: Number of elements in the data vector
- * @addr: Pointers to the data areas
- * @len: Lengths of the data blocks
- * @mac: Buffer for the hash
- * Returns: 0 on success, -1 on failure
- */
-int md5_vector_non_fips_allow(size_t num_elem, const u8 *addr[],
- const size_t *len, u8 *mac);
-#else /* CONFIG_FIPS */
-#define md5_vector_non_fips_allow md5_vector
-#endif /* CONFIG_FIPS */
-
/**
* sha1_vector - SHA-1 hash for data vector
@@ -155,7 +134,8 @@ void aes_decrypt_deinit(void *ctx);
enum crypto_hash_alg {
CRYPTO_HASH_ALG_MD5, CRYPTO_HASH_ALG_SHA1,
- CRYPTO_HASH_ALG_HMAC_MD5, CRYPTO_HASH_ALG_HMAC_SHA1
+ CRYPTO_HASH_ALG_HMAC_MD5, CRYPTO_HASH_ALG_HMAC_SHA1,
+ CRYPTO_HASH_ALG_SHA256, CRYPTO_HASH_ALG_HMAC_SHA256
};
struct crypto_hash;
@@ -466,4 +446,15 @@ int __must_check crypto_mod_exp(const u8 *base, size_t base_len,
int rc4_skip(const u8 *key, size_t keylen, size_t skip,
u8 *data, size_t data_len);
+/**
+ * crypto_get_random - Generate cryptographically strong pseudy-random bytes
+ * @buf: Buffer for data
+ * @len: Number of bytes to generate
+ * Returns: 0 on success, -1 on failure
+ *
+ * If the PRNG does not have enough entropy to ensure unpredictable byte
+ * sequence, this functions must return -1.
+ */
+int crypto_get_random(void *buf, size_t len);
+
#endif /* CRYPTO_H */
diff --git a/contrib/wpa/src/crypto/crypto_cryptoapi.c b/contrib/wpa/src/crypto/crypto_cryptoapi.c
index 2a8d200..55a069b 100644
--- a/contrib/wpa/src/crypto/crypto_cryptoapi.c
+++ b/contrib/wpa/src/crypto/crypto_cryptoapi.c
@@ -2,14 +2,8 @@
* Crypto wrapper for Microsoft CryptoAPI
* Copyright (c) 2005-2009, Jouni Malinen <j@w1.fi>
*
- * This program is free software; you can redistribute it and/or modify
- * it under the terms of the GNU General Public License version 2 as
- * published by the Free Software Foundation.
- *
- * Alternatively, this software may be distributed under the terms of BSD
- * license.
- *
- * See README and COPYING for more details.
+ * This software may be distributed under the terms of the BSD license.
+ * See README for more details.
*/
#include "includes.h"
diff --git a/contrib/wpa/src/crypto/crypto_gnutls.c b/contrib/wpa/src/crypto/crypto_gnutls.c
index 0998cca..0dfd54d 100644
--- a/contrib/wpa/src/crypto/crypto_gnutls.c
+++ b/contrib/wpa/src/crypto/crypto_gnutls.c
@@ -2,14 +2,8 @@
* WPA Supplicant / wrapper functions for libgcrypt
* Copyright (c) 2004-2009, Jouni Malinen <j@w1.fi>
*
- * This program is free software; you can redistribute it and/or modify
- * it under the terms of the GNU General Public License version 2 as
- * published by the Free Software Foundation.
- *
- * Alternatively, this software may be distributed under the terms of BSD
- * license.
- *
- * See README and COPYING for more details.
+ * This software may be distributed under the terms of the BSD license.
+ * See README for more details.
*/
#include "includes.h"
diff --git a/contrib/wpa/src/crypto/crypto_internal-cipher.c b/contrib/wpa/src/crypto/crypto_internal-cipher.c
index 75134f0..ad0930a 100644
--- a/contrib/wpa/src/crypto/crypto_internal-cipher.c
+++ b/contrib/wpa/src/crypto/crypto_internal-cipher.c
@@ -2,14 +2,8 @@
* Crypto wrapper for internal crypto implementation - Cipher wrappers
* Copyright (c) 2006-2009, Jouni Malinen <j@w1.fi>
*
- * This program is free software; you can redistribute it and/or modify
- * it under the terms of the GNU General Public License version 2 as
- * published by the Free Software Foundation.
- *
- * Alternatively, this software may be distributed under the terms of BSD
- * license.
- *
- * See README and COPYING for more details.
+ * This software may be distributed under the terms of the BSD license.
+ * See README for more details.
*/
#include "includes.h"
@@ -30,7 +24,6 @@ struct crypto_cipher {
} rc4;
struct {
u8 cbc[32];
- size_t block_size;
void *ctx_enc;
void *ctx_dec;
} aes;
@@ -69,10 +62,6 @@ struct crypto_cipher * crypto_cipher_init(enum crypto_cipher_alg alg,
os_memcpy(ctx->u.rc4.key, key, key_len);
break;
case CRYPTO_CIPHER_ALG_AES:
- if (key_len > sizeof(ctx->u.aes.cbc)) {
- os_free(ctx);
- return NULL;
- }
ctx->u.aes.ctx_enc = aes_encrypt_init(key, key_len);
if (ctx->u.aes.ctx_enc == NULL) {
os_free(ctx);
@@ -84,8 +73,7 @@ struct crypto_cipher * crypto_cipher_init(enum crypto_cipher_alg alg,
os_free(ctx);
return NULL;
}
- ctx->u.aes.block_size = key_len;
- os_memcpy(ctx->u.aes.cbc, iv, ctx->u.aes.block_size);
+ os_memcpy(ctx->u.aes.cbc, iv, AES_BLOCK_SIZE);
break;
case CRYPTO_CIPHER_ALG_3DES:
if (key_len != 24) {
@@ -126,18 +114,17 @@ int crypto_cipher_encrypt(struct crypto_cipher *ctx, const u8 *plain,
ctx->u.rc4.used_bytes += len;
break;
case CRYPTO_CIPHER_ALG_AES:
- if (len % ctx->u.aes.block_size)
+ if (len % AES_BLOCK_SIZE)
return -1;
- blocks = len / ctx->u.aes.block_size;
+ blocks = len / AES_BLOCK_SIZE;
for (i = 0; i < blocks; i++) {
- for (j = 0; j < ctx->u.aes.block_size; j++)
+ for (j = 0; j < AES_BLOCK_SIZE; j++)
ctx->u.aes.cbc[j] ^= plain[j];
aes_encrypt(ctx->u.aes.ctx_enc, ctx->u.aes.cbc,
ctx->u.aes.cbc);
- os_memcpy(crypt, ctx->u.aes.cbc,
- ctx->u.aes.block_size);
- plain += ctx->u.aes.block_size;
- crypt += ctx->u.aes.block_size;
+ os_memcpy(crypt, ctx->u.aes.cbc, AES_BLOCK_SIZE);
+ plain += AES_BLOCK_SIZE;
+ crypt += AES_BLOCK_SIZE;
}
break;
case CRYPTO_CIPHER_ALG_3DES:
@@ -191,17 +178,17 @@ int crypto_cipher_decrypt(struct crypto_cipher *ctx, const u8 *crypt,
ctx->u.rc4.used_bytes += len;
break;
case CRYPTO_CIPHER_ALG_AES:
- if (len % ctx->u.aes.block_size)
+ if (len % AES_BLOCK_SIZE)
return -1;
- blocks = len / ctx->u.aes.block_size;
+ blocks = len / AES_BLOCK_SIZE;
for (i = 0; i < blocks; i++) {
- os_memcpy(tmp, crypt, ctx->u.aes.block_size);
+ os_memcpy(tmp, crypt, AES_BLOCK_SIZE);
aes_decrypt(ctx->u.aes.ctx_dec, crypt, plain);
- for (j = 0; j < ctx->u.aes.block_size; j++)
+ for (j = 0; j < AES_BLOCK_SIZE; j++)
plain[j] ^= ctx->u.aes.cbc[j];
- os_memcpy(ctx->u.aes.cbc, tmp, ctx->u.aes.block_size);
- plain += ctx->u.aes.block_size;
- crypt += ctx->u.aes.block_size;
+ os_memcpy(ctx->u.aes.cbc, tmp, AES_BLOCK_SIZE);
+ plain += AES_BLOCK_SIZE;
+ crypt += AES_BLOCK_SIZE;
}
break;
case CRYPTO_CIPHER_ALG_3DES:
diff --git a/contrib/wpa/src/crypto/crypto_internal-modexp.c b/contrib/wpa/src/crypto/crypto_internal-modexp.c
index 3124742..9dcabb9 100644
--- a/contrib/wpa/src/crypto/crypto_internal-modexp.c
+++ b/contrib/wpa/src/crypto/crypto_internal-modexp.c
@@ -2,14 +2,8 @@
* Crypto wrapper for internal crypto implementation - modexp
* Copyright (c) 2006-2009, Jouni Malinen <j@w1.fi>
*
- * This program is free software; you can redistribute it and/or modify
- * it under the terms of the GNU General Public License version 2 as
- * published by the Free Software Foundation.
- *
- * Alternatively, this software may be distributed under the terms of BSD
- * license.
- *
- * See README and COPYING for more details.
+ * This software may be distributed under the terms of the BSD license.
+ * See README for more details.
*/
#include "includes.h"
diff --git a/contrib/wpa/src/crypto/crypto_internal-rsa.c b/contrib/wpa/src/crypto/crypto_internal-rsa.c
index 205042c..54209fa 100644
--- a/contrib/wpa/src/crypto/crypto_internal-rsa.c
+++ b/contrib/wpa/src/crypto/crypto_internal-rsa.c
@@ -2,14 +2,8 @@
* Crypto wrapper for internal crypto implementation - RSA parts
* Copyright (c) 2006-2009, Jouni Malinen <j@w1.fi>
*
- * This program is free software; you can redistribute it and/or modify
- * it under the terms of the GNU General Public License version 2 as
- * published by the Free Software Foundation.
- *
- * Alternatively, this software may be distributed under the terms of BSD
- * license.
- *
- * See README and COPYING for more details.
+ * This software may be distributed under the terms of the BSD license.
+ * See README for more details.
*/
#include "includes.h"
@@ -17,7 +11,6 @@
#include "common.h"
#include "crypto.h"
#include "tls/rsa.h"
-#include "tls/bignum.h"
#include "tls/pkcs1.h"
#include "tls/pkcs8.h"
diff --git a/contrib/wpa/src/crypto/crypto_internal.c b/contrib/wpa/src/crypto/crypto_internal.c
index 8fdba65..f3602da 100644
--- a/contrib/wpa/src/crypto/crypto_internal.c
+++ b/contrib/wpa/src/crypto/crypto_internal.c
@@ -1,21 +1,16 @@
/*
* Crypto wrapper for internal crypto implementation
- * Copyright (c) 2006-2009, Jouni Malinen <j@w1.fi>
+ * Copyright (c) 2006-2011, Jouni Malinen <j@w1.fi>
*
- * This program is free software; you can redistribute it and/or modify
- * it under the terms of the GNU General Public License version 2 as
- * published by the Free Software Foundation.
- *
- * Alternatively, this software may be distributed under the terms of BSD
- * license.
- *
- * See README and COPYING for more details.
+ * This software may be distributed under the terms of the BSD license.
+ * See README for more details.
*/
#include "includes.h"
#include "common.h"
#include "crypto.h"
+#include "sha256_i.h"
#include "sha1_i.h"
#include "md5_i.h"
@@ -24,6 +19,9 @@ struct crypto_hash {
union {
struct MD5Context md5;
struct SHA1Context sha1;
+#ifdef CONFIG_SHA256
+ struct sha256_state sha256;
+#endif /* CONFIG_SHA256 */
} u;
u8 key[64];
size_t key_len;
@@ -35,7 +33,7 @@ struct crypto_hash * crypto_hash_init(enum crypto_hash_alg alg, const u8 *key,
{
struct crypto_hash *ctx;
u8 k_pad[64];
- u8 tk[20];
+ u8 tk[32];
size_t i;
ctx = os_zalloc(sizeof(*ctx));
@@ -51,6 +49,11 @@ struct crypto_hash * crypto_hash_init(enum crypto_hash_alg alg, const u8 *key,
case CRYPTO_HASH_ALG_SHA1:
SHA1Init(&ctx->u.sha1);
break;
+#ifdef CONFIG_SHA256
+ case CRYPTO_HASH_ALG_SHA256:
+ sha256_init(&ctx->u.sha256);
+ break;
+#endif /* CONFIG_SHA256 */
case CRYPTO_HASH_ALG_HMAC_MD5:
if (key_len > sizeof(k_pad)) {
MD5Init(&ctx->u.md5);
@@ -63,7 +66,8 @@ struct crypto_hash * crypto_hash_init(enum crypto_hash_alg alg, const u8 *key,
ctx->key_len = key_len;
os_memcpy(k_pad, key, key_len);
- os_memset(k_pad + key_len, 0, sizeof(k_pad) - key_len);
+ if (key_len < sizeof(k_pad))
+ os_memset(k_pad + key_len, 0, sizeof(k_pad) - key_len);
for (i = 0; i < sizeof(k_pad); i++)
k_pad[i] ^= 0x36;
MD5Init(&ctx->u.md5);
@@ -81,12 +85,34 @@ struct crypto_hash * crypto_hash_init(enum crypto_hash_alg alg, const u8 *key,
ctx->key_len = key_len;
os_memcpy(k_pad, key, key_len);
- os_memset(k_pad + key_len, 0, sizeof(k_pad) - key_len);
+ if (key_len < sizeof(k_pad))
+ os_memset(k_pad + key_len, 0, sizeof(k_pad) - key_len);
for (i = 0; i < sizeof(k_pad); i++)
k_pad[i] ^= 0x36;
SHA1Init(&ctx->u.sha1);
SHA1Update(&ctx->u.sha1, k_pad, sizeof(k_pad));
break;
+#ifdef CONFIG_SHA256
+ case CRYPTO_HASH_ALG_HMAC_SHA256:
+ if (key_len > sizeof(k_pad)) {
+ sha256_init(&ctx->u.sha256);
+ sha256_process(&ctx->u.sha256, key, key_len);
+ sha256_done(&ctx->u.sha256, tk);
+ key = tk;
+ key_len = 32;
+ }
+ os_memcpy(ctx->key, key, key_len);
+ ctx->key_len = key_len;
+
+ os_memcpy(k_pad, key, key_len);
+ if (key_len < sizeof(k_pad))
+ os_memset(k_pad + key_len, 0, sizeof(k_pad) - key_len);
+ for (i = 0; i < sizeof(k_pad); i++)
+ k_pad[i] ^= 0x36;
+ sha256_init(&ctx->u.sha256);
+ sha256_process(&ctx->u.sha256, k_pad, sizeof(k_pad));
+ break;
+#endif /* CONFIG_SHA256 */
default:
os_free(ctx);
return NULL;
@@ -110,6 +136,14 @@ void crypto_hash_update(struct crypto_hash *ctx, const u8 *data, size_t len)
case CRYPTO_HASH_ALG_HMAC_SHA1:
SHA1Update(&ctx->u.sha1, data, len);
break;
+#ifdef CONFIG_SHA256
+ case CRYPTO_HASH_ALG_SHA256:
+ case CRYPTO_HASH_ALG_HMAC_SHA256:
+ sha256_process(&ctx->u.sha256, data, len);
+ break;
+#endif /* CONFIG_SHA256 */
+ default:
+ break;
}
}
@@ -146,6 +180,17 @@ int crypto_hash_finish(struct crypto_hash *ctx, u8 *mac, size_t *len)
*len = 20;
SHA1Final(mac, &ctx->u.sha1);
break;
+#ifdef CONFIG_SHA256
+ case CRYPTO_HASH_ALG_SHA256:
+ if (*len < 32) {
+ *len = 32;
+ os_free(ctx);
+ return -1;
+ }
+ *len = 32;
+ sha256_done(&ctx->u.sha256, mac);
+ break;
+#endif /* CONFIG_SHA256 */
case CRYPTO_HASH_ALG_HMAC_MD5:
if (*len < 16) {
*len = 16;
@@ -186,6 +231,31 @@ int crypto_hash_finish(struct crypto_hash *ctx, u8 *mac, size_t *len)
SHA1Update(&ctx->u.sha1, mac, 20);
SHA1Final(mac, &ctx->u.sha1);
break;
+#ifdef CONFIG_SHA256
+ case CRYPTO_HASH_ALG_HMAC_SHA256:
+ if (*len < 32) {
+ *len = 32;
+ os_free(ctx);
+ return -1;
+ }
+ *len = 32;
+
+ sha256_done(&ctx->u.sha256, mac);
+
+ os_memcpy(k_pad, ctx->key, ctx->key_len);
+ os_memset(k_pad + ctx->key_len, 0,
+ sizeof(k_pad) - ctx->key_len);
+ for (i = 0; i < sizeof(k_pad); i++)
+ k_pad[i] ^= 0x5c;
+ sha256_init(&ctx->u.sha256);
+ sha256_process(&ctx->u.sha256, k_pad, sizeof(k_pad));
+ sha256_process(&ctx->u.sha256, mac, 32);
+ sha256_done(&ctx->u.sha256, mac);
+ break;
+#endif /* CONFIG_SHA256 */
+ default:
+ os_free(ctx);
+ return -1;
}
os_free(ctx);
diff --git a/contrib/wpa/src/crypto/crypto_libtomcrypt.c b/contrib/wpa/src/crypto/crypto_libtomcrypt.c
index 52b67a7..a55edd1 100644
--- a/contrib/wpa/src/crypto/crypto_libtomcrypt.c
+++ b/contrib/wpa/src/crypto/crypto_libtomcrypt.c
@@ -2,14 +2,8 @@
* WPA Supplicant / Crypto wrapper for LibTomCrypt (for internal TLSv1)
* Copyright (c) 2005-2006, Jouni Malinen <j@w1.fi>
*
- * This program is free software; you can redistribute it and/or modify
- * it under the terms of the GNU General Public License version 2 as
- * published by the Free Software Foundation.
- *
- * Alternatively, this software may be distributed under the terms of BSD
- * license.
- *
- * See README and COPYING for more details.
+ * This software may be distributed under the terms of the BSD license.
+ * See README for more details.
*/
#include "includes.h"
diff --git a/contrib/wpa/src/crypto/crypto_none.c b/contrib/wpa/src/crypto/crypto_none.c
index 9f43775..011f3f3 100644
--- a/contrib/wpa/src/crypto/crypto_none.c
+++ b/contrib/wpa/src/crypto/crypto_none.c
@@ -2,14 +2,8 @@
* WPA Supplicant / Empty template functions for crypto wrapper
* Copyright (c) 2005, Jouni Malinen <j@w1.fi>
*
- * This program is free software; you can redistribute it and/or modify
- * it under the terms of the GNU General Public License version 2 as
- * published by the Free Software Foundation.
- *
- * Alternatively, this software may be distributed under the terms of BSD
- * license.
- *
- * See README and COPYING for more details.
+ * This software may be distributed under the terms of the BSD license.
+ * See README for more details.
*/
#include "includes.h"
diff --git a/contrib/wpa/src/crypto/crypto_nss.c b/contrib/wpa/src/crypto/crypto_nss.c
index fee4195..acd0a55 100644
--- a/contrib/wpa/src/crypto/crypto_nss.c
+++ b/contrib/wpa/src/crypto/crypto_nss.c
@@ -2,14 +2,8 @@
* Crypto wrapper functions for NSS
* Copyright (c) 2009, Jouni Malinen <j@w1.fi>
*
- * This program is free software; you can redistribute it and/or modify
- * it under the terms of the GNU General Public License version 2 as
- * published by the Free Software Foundation.
- *
- * Alternatively, this software may be distributed under the terms of BSD
- * license.
- *
- * See README and COPYING for more details.
+ * This software may be distributed under the terms of the BSD license.
+ * See README for more details.
*/
#include "includes.h"
diff --git a/contrib/wpa/src/crypto/crypto_openssl.c b/contrib/wpa/src/crypto/crypto_openssl.c
index 08c98af..711e312 100644
--- a/contrib/wpa/src/crypto/crypto_openssl.c
+++ b/contrib/wpa/src/crypto/crypto_openssl.c
@@ -1,15 +1,9 @@
/*
* WPA Supplicant / wrapper functions for libcrypto
- * Copyright (c) 2004-2009, Jouni Malinen <j@w1.fi>
+ * Copyright (c) 2004-2012, Jouni Malinen <j@w1.fi>
*
- * This program is free software; you can redistribute it and/or modify
- * it under the terms of the GNU General Public License version 2 as
- * published by the Free Software Foundation.
- *
- * Alternatively, this software may be distributed under the terms of BSD
- * license.
- *
- * See README and COPYING for more details.
+ * This software may be distributed under the terms of the BSD license.
+ * See README for more details.
*/
#include "includes.h"
@@ -20,6 +14,11 @@
#include <openssl/bn.h>
#include <openssl/evp.h>
#include <openssl/dh.h>
+#include <openssl/hmac.h>
+#include <openssl/rand.h>
+#ifdef CONFIG_OPENSSL_CMAC
+#include <openssl/cmac.h>
+#endif /* CONFIG_OPENSSL_CMAC */
#include "common.h"
#include "wpabuf.h"
@@ -74,21 +73,14 @@ static BIGNUM * get_group5_prime(void)
#define NO_SHA256_WRAPPER
#endif
-static int openssl_digest_vector(const EVP_MD *type, int non_fips,
- size_t num_elem, const u8 *addr[],
- const size_t *len, u8 *mac)
+static int openssl_digest_vector(const EVP_MD *type, size_t num_elem,
+ const u8 *addr[], const size_t *len, u8 *mac)
{
EVP_MD_CTX ctx;
size_t i;
unsigned int mac_len;
EVP_MD_CTX_init(&ctx);
-#ifdef CONFIG_FIPS
-#ifdef OPENSSL_FIPS
- if (non_fips)
- EVP_MD_CTX_set_flags(&ctx, EVP_MD_CTX_FLAG_NON_FIPS_ALLOW);
-#endif /* OPENSSL_FIPS */
-#endif /* CONFIG_FIPS */
if (!EVP_DigestInit_ex(&ctx, type, NULL)) {
wpa_printf(MSG_ERROR, "OpenSSL: EVP_DigestInit_ex failed: %s",
ERR_error_string(ERR_get_error(), NULL));
@@ -114,7 +106,7 @@ static int openssl_digest_vector(const EVP_MD *type, int non_fips,
int md4_vector(size_t num_elem, const u8 *addr[], const size_t *len, u8 *mac)
{
- return openssl_digest_vector(EVP_md4(), 0, num_elem, addr, len, mac);
+ return openssl_digest_vector(EVP_md4(), num_elem, addr, len, mac);
}
@@ -178,22 +170,13 @@ out:
int md5_vector(size_t num_elem, const u8 *addr[], const size_t *len, u8 *mac)
{
- return openssl_digest_vector(EVP_md5(), 0, num_elem, addr, len, mac);
-}
-
-
-#ifdef CONFIG_FIPS
-int md5_vector_non_fips_allow(size_t num_elem, const u8 *addr[],
- const size_t *len, u8 *mac)
-{
- return openssl_digest_vector(EVP_md5(), 1, num_elem, addr, len, mac);
+ return openssl_digest_vector(EVP_md5(), num_elem, addr, len, mac);
}
-#endif /* CONFIG_FIPS */
int sha1_vector(size_t num_elem, const u8 *addr[], const size_t *len, u8 *mac)
{
- return openssl_digest_vector(EVP_sha1(), 0, num_elem, addr, len, mac);
+ return openssl_digest_vector(EVP_sha1(), num_elem, addr, len, mac);
}
@@ -201,60 +184,124 @@ int sha1_vector(size_t num_elem, const u8 *addr[], const size_t *len, u8 *mac)
int sha256_vector(size_t num_elem, const u8 *addr[], const size_t *len,
u8 *mac)
{
- return openssl_digest_vector(EVP_sha256(), 0, num_elem, addr, len,
- mac);
+ return openssl_digest_vector(EVP_sha256(), num_elem, addr, len, mac);
}
#endif /* NO_SHA256_WRAPPER */
+static const EVP_CIPHER * aes_get_evp_cipher(size_t keylen)
+{
+ switch (keylen) {
+ case 16:
+ return EVP_aes_128_ecb();
+ case 24:
+ return EVP_aes_192_ecb();
+ case 32:
+ return EVP_aes_256_ecb();
+ }
+
+ return NULL;
+}
+
+
void * aes_encrypt_init(const u8 *key, size_t len)
{
- AES_KEY *ak;
- ak = os_malloc(sizeof(*ak));
- if (ak == NULL)
+ EVP_CIPHER_CTX *ctx;
+ const EVP_CIPHER *type;
+
+ type = aes_get_evp_cipher(len);
+ if (type == NULL)
return NULL;
- if (AES_set_encrypt_key(key, 8 * len, ak) < 0) {
- os_free(ak);
+
+ ctx = os_malloc(sizeof(*ctx));
+ if (ctx == NULL)
+ return NULL;
+ EVP_CIPHER_CTX_init(ctx);
+ if (EVP_EncryptInit_ex(ctx, type, NULL, key, NULL) != 1) {
+ os_free(ctx);
return NULL;
}
- return ak;
+ EVP_CIPHER_CTX_set_padding(ctx, 0);
+ return ctx;
}
void aes_encrypt(void *ctx, const u8 *plain, u8 *crypt)
{
- AES_encrypt(plain, crypt, ctx);
+ EVP_CIPHER_CTX *c = ctx;
+ int clen = 16;
+ if (EVP_EncryptUpdate(c, crypt, &clen, plain, 16) != 1) {
+ wpa_printf(MSG_ERROR, "OpenSSL: EVP_EncryptUpdate failed: %s",
+ ERR_error_string(ERR_get_error(), NULL));
+ }
}
void aes_encrypt_deinit(void *ctx)
{
- os_free(ctx);
+ EVP_CIPHER_CTX *c = ctx;
+ u8 buf[16];
+ int len = sizeof(buf);
+ if (EVP_EncryptFinal_ex(c, buf, &len) != 1) {
+ wpa_printf(MSG_ERROR, "OpenSSL: EVP_EncryptFinal_ex failed: "
+ "%s", ERR_error_string(ERR_get_error(), NULL));
+ }
+ if (len != 0) {
+ wpa_printf(MSG_ERROR, "OpenSSL: Unexpected padding length %d "
+ "in AES encrypt", len);
+ }
+ EVP_CIPHER_CTX_cleanup(c);
+ os_free(c);
}
void * aes_decrypt_init(const u8 *key, size_t len)
{
- AES_KEY *ak;
- ak = os_malloc(sizeof(*ak));
- if (ak == NULL)
+ EVP_CIPHER_CTX *ctx;
+ const EVP_CIPHER *type;
+
+ type = aes_get_evp_cipher(len);
+ if (type == NULL)
return NULL;
- if (AES_set_decrypt_key(key, 8 * len, ak) < 0) {
- os_free(ak);
+
+ ctx = os_malloc(sizeof(*ctx));
+ if (ctx == NULL)
+ return NULL;
+ EVP_CIPHER_CTX_init(ctx);
+ if (EVP_DecryptInit_ex(ctx, type, NULL, key, NULL) != 1) {
+ os_free(ctx);
return NULL;
}
- return ak;
+ EVP_CIPHER_CTX_set_padding(ctx, 0);
+ return ctx;
}
void aes_decrypt(void *ctx, const u8 *crypt, u8 *plain)
{
- AES_decrypt(crypt, plain, ctx);
+ EVP_CIPHER_CTX *c = ctx;
+ int plen = 16;
+ if (EVP_DecryptUpdate(c, plain, &plen, crypt, 16) != 1) {
+ wpa_printf(MSG_ERROR, "OpenSSL: EVP_DecryptUpdate failed: %s",
+ ERR_error_string(ERR_get_error(), NULL));
+ }
}
void aes_decrypt_deinit(void *ctx)
{
+ EVP_CIPHER_CTX *c = ctx;
+ u8 buf[16];
+ int len = sizeof(buf);
+ if (EVP_DecryptFinal_ex(c, buf, &len) != 1) {
+ wpa_printf(MSG_ERROR, "OpenSSL: EVP_DecryptFinal_ex failed: "
+ "%s", ERR_error_string(ERR_get_error(), NULL));
+ }
+ if (len != 0) {
+ wpa_printf(MSG_ERROR, "OpenSSL: Unexpected padding length %d "
+ "in AES decrypt", len);
+ }
+ EVP_CIPHER_CTX_cleanup(c);
os_free(ctx);
}
@@ -458,6 +505,41 @@ err:
}
+void * dh5_init_fixed(const struct wpabuf *priv, const struct wpabuf *publ)
+{
+ DH *dh;
+
+ dh = DH_new();
+ if (dh == NULL)
+ return NULL;
+
+ dh->g = BN_new();
+ if (dh->g == NULL || BN_set_word(dh->g, 2) != 1)
+ goto err;
+
+ dh->p = get_group5_prime();
+ if (dh->p == NULL)
+ goto err;
+
+ dh->priv_key = BN_bin2bn(wpabuf_head(priv), wpabuf_len(priv), NULL);
+ if (dh->priv_key == NULL)
+ goto err;
+
+ dh->pub_key = BN_bin2bn(wpabuf_head(publ), wpabuf_len(publ), NULL);
+ if (dh->pub_key == NULL)
+ goto err;
+
+ if (DH_generate_key(dh) != 1)
+ goto err;
+
+ return dh;
+
+err:
+ DH_free(dh);
+ return NULL;
+}
+
+
struct wpabuf * dh5_derive_shared(void *ctx, const struct wpabuf *peer_public,
const struct wpabuf *own_private)
{
@@ -503,3 +585,236 @@ void dh5_free(void *ctx)
dh = ctx;
DH_free(dh);
}
+
+
+struct crypto_hash {
+ HMAC_CTX ctx;
+};
+
+
+struct crypto_hash * crypto_hash_init(enum crypto_hash_alg alg, const u8 *key,
+ size_t key_len)
+{
+ struct crypto_hash *ctx;
+ const EVP_MD *md;
+
+ switch (alg) {
+#ifndef OPENSSL_NO_MD5
+ case CRYPTO_HASH_ALG_HMAC_MD5:
+ md = EVP_md5();
+ break;
+#endif /* OPENSSL_NO_MD5 */
+#ifndef OPENSSL_NO_SHA
+ case CRYPTO_HASH_ALG_HMAC_SHA1:
+ md = EVP_sha1();
+ break;
+#endif /* OPENSSL_NO_SHA */
+#ifndef OPENSSL_NO_SHA256
+#ifdef CONFIG_SHA256
+ case CRYPTO_HASH_ALG_HMAC_SHA256:
+ md = EVP_sha256();
+ break;
+#endif /* CONFIG_SHA256 */
+#endif /* OPENSSL_NO_SHA256 */
+ default:
+ return NULL;
+ }
+
+ ctx = os_zalloc(sizeof(*ctx));
+ if (ctx == NULL)
+ return NULL;
+ HMAC_CTX_init(&ctx->ctx);
+
+#if OPENSSL_VERSION_NUMBER < 0x00909000
+ HMAC_Init_ex(&ctx->ctx, key, key_len, md, NULL);
+#else /* openssl < 0.9.9 */
+ if (HMAC_Init_ex(&ctx->ctx, key, key_len, md, NULL) != 1) {
+ os_free(ctx);
+ return NULL;
+ }
+#endif /* openssl < 0.9.9 */
+
+ return ctx;
+}
+
+
+void crypto_hash_update(struct crypto_hash *ctx, const u8 *data, size_t len)
+{
+ if (ctx == NULL)
+ return;
+ HMAC_Update(&ctx->ctx, data, len);
+}
+
+
+int crypto_hash_finish(struct crypto_hash *ctx, u8 *mac, size_t *len)
+{
+ unsigned int mdlen;
+ int res;
+
+ if (ctx == NULL)
+ return -2;
+
+ if (mac == NULL || len == NULL) {
+ os_free(ctx);
+ return 0;
+ }
+
+ mdlen = *len;
+#if OPENSSL_VERSION_NUMBER < 0x00909000
+ HMAC_Final(&ctx->ctx, mac, &mdlen);
+ res = 1;
+#else /* openssl < 0.9.9 */
+ res = HMAC_Final(&ctx->ctx, mac, &mdlen);
+#endif /* openssl < 0.9.9 */
+ HMAC_CTX_cleanup(&ctx->ctx);
+ os_free(ctx);
+
+ if (res == 1) {
+ *len = mdlen;
+ return 0;
+ }
+
+ return -1;
+}
+
+
+int pbkdf2_sha1(const char *passphrase, const u8 *ssid, size_t ssid_len,
+ int iterations, u8 *buf, size_t buflen)
+{
+#if OPENSSL_VERSION_NUMBER < 0x00908000
+ if (PKCS5_PBKDF2_HMAC_SHA1(passphrase, os_strlen(passphrase),
+ (unsigned char *) ssid,
+ ssid_len, 4096, buflen, buf) != 1)
+ return -1;
+#else /* openssl < 0.9.8 */
+ if (PKCS5_PBKDF2_HMAC_SHA1(passphrase, os_strlen(passphrase), ssid,
+ ssid_len, 4096, buflen, buf) != 1)
+ return -1;
+#endif /* openssl < 0.9.8 */
+ return 0;
+}
+
+
+int hmac_sha1_vector(const u8 *key, size_t key_len, size_t num_elem,
+ const u8 *addr[], const size_t *len, u8 *mac)
+{
+ HMAC_CTX ctx;
+ size_t i;
+ unsigned int mdlen;
+ int res;
+
+ HMAC_CTX_init(&ctx);
+#if OPENSSL_VERSION_NUMBER < 0x00909000
+ HMAC_Init_ex(&ctx, key, key_len, EVP_sha1(), NULL);
+#else /* openssl < 0.9.9 */
+ if (HMAC_Init_ex(&ctx, key, key_len, EVP_sha1(), NULL) != 1)
+ return -1;
+#endif /* openssl < 0.9.9 */
+
+ for (i = 0; i < num_elem; i++)
+ HMAC_Update(&ctx, addr[i], len[i]);
+
+ mdlen = 20;
+#if OPENSSL_VERSION_NUMBER < 0x00909000
+ HMAC_Final(&ctx, mac, &mdlen);
+ res = 1;
+#else /* openssl < 0.9.9 */
+ res = HMAC_Final(&ctx, mac, &mdlen);
+#endif /* openssl < 0.9.9 */
+ HMAC_CTX_cleanup(&ctx);
+
+ return res == 1 ? 0 : -1;
+}
+
+
+int hmac_sha1(const u8 *key, size_t key_len, const u8 *data, size_t data_len,
+ u8 *mac)
+{
+ return hmac_sha1_vector(key, key_len, 1, &data, &data_len, mac);
+}
+
+
+#ifdef CONFIG_SHA256
+
+int hmac_sha256_vector(const u8 *key, size_t key_len, size_t num_elem,
+ const u8 *addr[], const size_t *len, u8 *mac)
+{
+ HMAC_CTX ctx;
+ size_t i;
+ unsigned int mdlen;
+ int res;
+
+ HMAC_CTX_init(&ctx);
+#if OPENSSL_VERSION_NUMBER < 0x00909000
+ HMAC_Init_ex(&ctx, key, key_len, EVP_sha256(), NULL);
+#else /* openssl < 0.9.9 */
+ if (HMAC_Init_ex(&ctx, key, key_len, EVP_sha256(), NULL) != 1)
+ return -1;
+#endif /* openssl < 0.9.9 */
+
+ for (i = 0; i < num_elem; i++)
+ HMAC_Update(&ctx, addr[i], len[i]);
+
+ mdlen = 32;
+#if OPENSSL_VERSION_NUMBER < 0x00909000
+ HMAC_Final(&ctx, mac, &mdlen);
+ res = 1;
+#else /* openssl < 0.9.9 */
+ res = HMAC_Final(&ctx, mac, &mdlen);
+#endif /* openssl < 0.9.9 */
+ HMAC_CTX_cleanup(&ctx);
+
+ return res == 1 ? 0 : -1;
+}
+
+
+int hmac_sha256(const u8 *key, size_t key_len, const u8 *data,
+ size_t data_len, u8 *mac)
+{
+ return hmac_sha256_vector(key, key_len, 1, &data, &data_len, mac);
+}
+
+#endif /* CONFIG_SHA256 */
+
+
+int crypto_get_random(void *buf, size_t len)
+{
+ if (RAND_bytes(buf, len) != 1)
+ return -1;
+ return 0;
+}
+
+
+#ifdef CONFIG_OPENSSL_CMAC
+int omac1_aes_128_vector(const u8 *key, size_t num_elem,
+ const u8 *addr[], const size_t *len, u8 *mac)
+{
+ CMAC_CTX *ctx;
+ int ret = -1;
+ size_t outlen, i;
+
+ ctx = CMAC_CTX_new();
+ if (ctx == NULL)
+ return -1;
+
+ if (!CMAC_Init(ctx, key, 16, EVP_aes_128_cbc(), NULL))
+ goto fail;
+ for (i = 0; i < num_elem; i++) {
+ if (!CMAC_Update(ctx, addr[i], len[i]))
+ goto fail;
+ }
+ if (!CMAC_Final(ctx, mac, &outlen) || outlen != 16)
+ goto fail;
+
+ ret = 0;
+fail:
+ CMAC_CTX_free(ctx);
+ return ret;
+}
+
+
+int omac1_aes_128(const u8 *key, const u8 *data, size_t data_len, u8 *mac)
+{
+ return omac1_aes_128_vector(key, 1, &data, &data_len, mac);
+}
+#endif /* CONFIG_OPENSSL_CMAC */
diff --git a/contrib/wpa/src/crypto/des-internal.c b/contrib/wpa/src/crypto/des-internal.c
index ccea950..dec39ef 100644
--- a/contrib/wpa/src/crypto/des-internal.c
+++ b/contrib/wpa/src/crypto/des-internal.c
@@ -4,14 +4,8 @@
* Modifications to LibTomCrypt implementation:
* Copyright (c) 2006-2009, Jouni Malinen <j@w1.fi>
*
- * This program is free software; you can redistribute it and/or modify
- * it under the terms of the GNU General Public License version 2 as
- * published by the Free Software Foundation.
- *
- * Alternatively, this software may be distributed under the terms of BSD
- * license.
- *
- * See README and COPYING for more details.
+ * This software may be distributed under the terms of the BSD license.
+ * See README for more details.
*/
#include "includes.h"
diff --git a/contrib/wpa/src/crypto/des_i.h b/contrib/wpa/src/crypto/des_i.h
index 6f27414..c9563d2 100644
--- a/contrib/wpa/src/crypto/des_i.h
+++ b/contrib/wpa/src/crypto/des_i.h
@@ -2,14 +2,8 @@
* DES and 3DES-EDE ciphers
* Copyright (c) 2006-2009, Jouni Malinen <j@w1.fi>
*
- * This program is free software; you can redistribute it and/or modify
- * it under the terms of the GNU General Public License version 2 as
- * published by the Free Software Foundation.
- *
- * Alternatively, this software may be distributed under the terms of BSD
- * license.
- *
- * See README and COPYING for more details.
+ * This software may be distributed under the terms of the BSD license.
+ * See README for more details.
*/
#ifndef DES_I_H
diff --git a/contrib/wpa/src/crypto/dh_group5.c b/contrib/wpa/src/crypto/dh_group5.c
index 8c475bf..ccdbfc8 100644
--- a/contrib/wpa/src/crypto/dh_group5.c
+++ b/contrib/wpa/src/crypto/dh_group5.c
@@ -1,15 +1,9 @@
/*
* Diffie-Hellman group 5 operations
- * Copyright (c) 2009, Jouni Malinen <j@w1.fi>
+ * Copyright (c) 2009, 2012, Jouni Malinen <j@w1.fi>
*
- * This program is free software; you can redistribute it and/or modify
- * it under the terms of the GNU General Public License version 2 as
- * published by the Free Software Foundation.
- *
- * Alternatively, this software may be distributed under the terms of BSD
- * license.
- *
- * See README and COPYING for more details.
+ * This software may be distributed under the terms of the BSD license.
+ * See README for more details.
*/
#include "includes.h"
@@ -22,12 +16,18 @@
void * dh5_init(struct wpabuf **priv, struct wpabuf **publ)
{
*publ = dh_init(dh_groups_get(5), priv);
- if (*publ == 0)
+ if (*publ == NULL)
return NULL;
return (void *) 1;
}
+void * dh5_init_fixed(const struct wpabuf *priv, const struct wpabuf *publ)
+{
+ return (void *) 1;
+}
+
+
struct wpabuf * dh5_derive_shared(void *ctx, const struct wpabuf *peer_public,
const struct wpabuf *own_private)
{
diff --git a/contrib/wpa/src/crypto/dh_group5.h b/contrib/wpa/src/crypto/dh_group5.h
index 595f111..abee8ea 100644
--- a/contrib/wpa/src/crypto/dh_group5.h
+++ b/contrib/wpa/src/crypto/dh_group5.h
@@ -1,21 +1,16 @@
/*
* Diffie-Hellman group 5 operations
- * Copyright (c) 2009, Jouni Malinen <j@w1.fi>
+ * Copyright (c) 2009, 2012, Jouni Malinen <j@w1.fi>
*
- * This program is free software; you can redistribute it and/or modify
- * it under the terms of the GNU General Public License version 2 as
- * published by the Free Software Foundation.
- *
- * Alternatively, this software may be distributed under the terms of BSD
- * license.
- *
- * See README and COPYING for more details.
+ * This software may be distributed under the terms of the BSD license.
+ * See README for more details.
*/
#ifndef DH_GROUP5_H
#define DH_GROUP5_H
void * dh5_init(struct wpabuf **priv, struct wpabuf **publ);
+void * dh5_init_fixed(const struct wpabuf *priv, const struct wpabuf *publ);
struct wpabuf * dh5_derive_shared(void *ctx, const struct wpabuf *peer_public,
const struct wpabuf *own_private);
void dh5_free(void *ctx);
diff --git a/contrib/wpa/src/crypto/dh_groups.c b/contrib/wpa/src/crypto/dh_groups.c
index 7bd2fb7..f757b6b 100644
--- a/contrib/wpa/src/crypto/dh_groups.c
+++ b/contrib/wpa/src/crypto/dh_groups.c
@@ -2,20 +2,15 @@
* Diffie-Hellman groups
* Copyright (c) 2007, Jouni Malinen <j@w1.fi>
*
- * This program is free software; you can redistribute it and/or modify
- * it under the terms of the GNU General Public License version 2 as
- * published by the Free Software Foundation.
- *
- * Alternatively, this software may be distributed under the terms of BSD
- * license.
- *
- * See README and COPYING for more details.
+ * This software may be distributed under the terms of the BSD license.
+ * See README for more details.
*/
#include "includes.h"
#include "common.h"
#include "crypto.h"
+#include "random.h"
#include "dh_groups.h"
@@ -564,7 +559,8 @@ struct wpabuf * dh_init(const struct dh_group *dh, struct wpabuf **priv)
if (*priv == NULL)
return NULL;
- if (os_get_random(wpabuf_put(*priv, dh->prime_len), dh->prime_len)) {
+ if (random_get_bytes(wpabuf_put(*priv, dh->prime_len), dh->prime_len))
+ {
wpabuf_free(*priv);
*priv = NULL;
return NULL;
diff --git a/contrib/wpa/src/crypto/dh_groups.h b/contrib/wpa/src/crypto/dh_groups.h
index 5c61539..225f006 100644
--- a/contrib/wpa/src/crypto/dh_groups.h
+++ b/contrib/wpa/src/crypto/dh_groups.h
@@ -2,14 +2,8 @@
* Diffie-Hellman groups
* Copyright (c) 2007, Jouni Malinen <j@w1.fi>
*
- * This program is free software; you can redistribute it and/or modify
- * it under the terms of the GNU General Public License version 2 as
- * published by the Free Software Foundation.
- *
- * Alternatively, this software may be distributed under the terms of BSD
- * license.
- *
- * See README and COPYING for more details.
+ * This software may be distributed under the terms of the BSD license.
+ * See README for more details.
*/
#ifndef DH_GROUPS_H
diff --git a/contrib/wpa/src/crypto/fips_prf_cryptoapi.c b/contrib/wpa/src/crypto/fips_prf_cryptoapi.c
index 17d3116..dca93a3 100644
--- a/contrib/wpa/src/crypto/fips_prf_cryptoapi.c
+++ b/contrib/wpa/src/crypto/fips_prf_cryptoapi.c
@@ -2,14 +2,8 @@
* FIPS 186-2 PRF for Microsoft CryptoAPI
* Copyright (c) 2009, Jouni Malinen <j@w1.fi>
*
- * This program is free software; you can redistribute it and/or modify
- * it under the terms of the GNU General Public License version 2 as
- * published by the Free Software Foundation.
- *
- * Alternatively, this software may be distributed under the terms of BSD
- * license.
- *
- * See README and COPYING for more details.
+ * This software may be distributed under the terms of the BSD license.
+ * See README for more details.
*/
#include "includes.h"
diff --git a/contrib/wpa/src/crypto/fips_prf_gnutls.c b/contrib/wpa/src/crypto/fips_prf_gnutls.c
index f742e98..947e6f6 100644
--- a/contrib/wpa/src/crypto/fips_prf_gnutls.c
+++ b/contrib/wpa/src/crypto/fips_prf_gnutls.c
@@ -2,14 +2,8 @@
* FIPS 186-2 PRF for libgcrypt
* Copyright (c) 2004-2009, Jouni Malinen <j@w1.fi>
*
- * This program is free software; you can redistribute it and/or modify
- * it under the terms of the GNU General Public License version 2 as
- * published by the Free Software Foundation.
- *
- * Alternatively, this software may be distributed under the terms of BSD
- * license.
- *
- * See README and COPYING for more details.
+ * This software may be distributed under the terms of the BSD license.
+ * See README for more details.
*/
#include "includes.h"
diff --git a/contrib/wpa/src/crypto/fips_prf_internal.c b/contrib/wpa/src/crypto/fips_prf_internal.c
index a85cb14..a4bf50a 100644
--- a/contrib/wpa/src/crypto/fips_prf_internal.c
+++ b/contrib/wpa/src/crypto/fips_prf_internal.c
@@ -2,14 +2,8 @@
* FIPS 186-2 PRF for internal crypto implementation
* Copyright (c) 2006-2007, Jouni Malinen <j@w1.fi>
*
- * This program is free software; you can redistribute it and/or modify
- * it under the terms of the GNU General Public License version 2 as
- * published by the Free Software Foundation.
- *
- * Alternatively, this software may be distributed under the terms of BSD
- * license.
- *
- * See README and COPYING for more details.
+ * This software may be distributed under the terms of the BSD license.
+ * See README for more details.
*/
#include "includes.h"
@@ -28,13 +22,14 @@ int fips186_2_prf(const u8 *seed, size_t seed_len, u8 *x, size_t xlen)
u8 *xpos = x;
u32 carry;
- if (seed_len > sizeof(xkey))
+ if (seed_len < sizeof(xkey))
+ os_memset(xkey + seed_len, 0, sizeof(xkey) - seed_len);
+ else
seed_len = sizeof(xkey);
/* FIPS 186-2 + change notice 1 */
os_memcpy(xkey, seed, seed_len);
- os_memset(xkey + seed_len, 0, 64 - seed_len);
t[0] = 0x67452301;
t[1] = 0xEFCDAB89;
t[2] = 0x98BADCFE;
diff --git a/contrib/wpa/src/crypto/fips_prf_nss.c b/contrib/wpa/src/crypto/fips_prf_nss.c
index f941983..2c962f4 100644
--- a/contrib/wpa/src/crypto/fips_prf_nss.c
+++ b/contrib/wpa/src/crypto/fips_prf_nss.c
@@ -2,14 +2,8 @@
* FIPS 186-2 PRF for NSS
* Copyright (c) 2009, Jouni Malinen <j@w1.fi>
*
- * This program is free software; you can redistribute it and/or modify
- * it under the terms of the GNU General Public License version 2 as
- * published by the Free Software Foundation.
- *
- * Alternatively, this software may be distributed under the terms of BSD
- * license.
- *
- * See README and COPYING for more details.
+ * This software may be distributed under the terms of the BSD license.
+ * See README for more details.
*/
#include "includes.h"
diff --git a/contrib/wpa/src/crypto/fips_prf_openssl.c b/contrib/wpa/src/crypto/fips_prf_openssl.c
index d0af983..d69ecea 100644
--- a/contrib/wpa/src/crypto/fips_prf_openssl.c
+++ b/contrib/wpa/src/crypto/fips_prf_openssl.c
@@ -2,14 +2,8 @@
* FIPS 186-2 PRF for libcrypto
* Copyright (c) 2004-2005, Jouni Malinen <j@w1.fi>
*
- * This program is free software; you can redistribute it and/or modify
- * it under the terms of the GNU General Public License version 2 as
- * published by the Free Software Foundation.
- *
- * Alternatively, this software may be distributed under the terms of BSD
- * license.
- *
- * See README and COPYING for more details.
+ * This software may be distributed under the terms of the BSD license.
+ * See README for more details.
*/
#include "includes.h"
@@ -37,13 +31,14 @@ int fips186_2_prf(const u8 *seed, size_t seed_len, u8 *x, size_t xlen)
u8 *xpos = x;
u32 carry;
- if (seed_len > sizeof(xkey))
+ if (seed_len < sizeof(xkey))
+ os_memset(xkey + seed_len, 0, sizeof(xkey) - seed_len);
+ else
seed_len = sizeof(xkey);
/* FIPS 186-2 + change notice 1 */
os_memcpy(xkey, seed, seed_len);
- os_memset(xkey + seed_len, 0, 64 - seed_len);
t[0] = 0x67452301;
t[1] = 0xEFCDAB89;
t[2] = 0x98BADCFE;
diff --git a/contrib/wpa/src/crypto/md4-internal.c b/contrib/wpa/src/crypto/md4-internal.c
index d9f499f..cd5e6ca 100644
--- a/contrib/wpa/src/crypto/md4-internal.c
+++ b/contrib/wpa/src/crypto/md4-internal.c
@@ -2,14 +2,8 @@
* MD4 hash implementation
* Copyright (c) 2006, Jouni Malinen <j@w1.fi>
*
- * This program is free software; you can redistribute it and/or modify
- * it under the terms of the GNU General Public License version 2 as
- * published by the Free Software Foundation.
- *
- * Alternatively, this software may be distributed under the terms of BSD
- * license.
- *
- * See README and COPYING for more details.
+ * This software may be distributed under the terms of the BSD license.
+ * See README for more details.
*/
#include "includes.h"
diff --git a/contrib/wpa/src/crypto/md5-internal.c b/contrib/wpa/src/crypto/md5-internal.c
index 137ad91..f0a2a5d 100644
--- a/contrib/wpa/src/crypto/md5-internal.c
+++ b/contrib/wpa/src/crypto/md5-internal.c
@@ -2,14 +2,8 @@
* MD5 hash implementation and interface functions
* Copyright (c) 2003-2005, Jouni Malinen <j@w1.fi>
*
- * This program is free software; you can redistribute it and/or modify
- * it under the terms of the GNU General Public License version 2 as
- * published by the Free Software Foundation.
- *
- * Alternatively, this software may be distributed under the terms of BSD
- * license.
- *
- * See README and COPYING for more details.
+ * This software may be distributed under the terms of the BSD license.
+ * See README for more details.
*/
#include "includes.h"
@@ -182,8 +176,8 @@ void MD5Final(unsigned char digest[16], struct MD5Context *ctx)
byteReverse(ctx->in, 14);
/* Append length in bits and transform */
- ((u32 *) ctx->in)[14] = ctx->bits[0];
- ((u32 *) ctx->in)[15] = ctx->bits[1];
+ ((u32 *) aliasing_hide_typecast(ctx->in, u32))[14] = ctx->bits[0];
+ ((u32 *) aliasing_hide_typecast(ctx->in, u32))[15] = ctx->bits[1];
MD5Transform(ctx->buf, (u32 *) ctx->in);
byteReverse((unsigned char *) ctx->buf, 4);
diff --git a/contrib/wpa/src/crypto/md5-non-fips.c b/contrib/wpa/src/crypto/md5-non-fips.c
deleted file mode 100644
index 6f29201..0000000
--- a/contrib/wpa/src/crypto/md5-non-fips.c
+++ /dev/null
@@ -1,113 +0,0 @@
-/*
- * MD5 hash implementation and interface functions (non-FIPS allowed cases)
- * Copyright (c) 2003-2009, Jouni Malinen <j@w1.fi>
- *
- * This program is free software; you can redistribute it and/or modify
- * it under the terms of the GNU General Public License version 2 as
- * published by the Free Software Foundation.
- *
- * Alternatively, this software may be distributed under the terms of BSD
- * license.
- *
- * See README and COPYING for more details.
- */
-
-#include "includes.h"
-
-#include "common.h"
-#include "md5.h"
-#include "crypto.h"
-
-
-/**
- * hmac_md5_vector_non_fips_allow - HMAC-MD5 over data vector (RFC 2104)
- * @key: Key for HMAC operations
- * @key_len: Length of the key in bytes
- * @num_elem: Number of elements in the data vector
- * @addr: Pointers to the data areas
- * @len: Lengths of the data blocks
- * @mac: Buffer for the hash (16 bytes)
- * Returns: 0 on success, -1 on failure
- */
-int hmac_md5_vector_non_fips_allow(const u8 *key, size_t key_len,
- size_t num_elem, const u8 *addr[],
- const size_t *len, u8 *mac)
-{
- u8 k_pad[64]; /* padding - key XORd with ipad/opad */
- u8 tk[16];
- const u8 *_addr[6];
- size_t i, _len[6];
-
- if (num_elem > 5) {
- /*
- * Fixed limit on the number of fragments to avoid having to
- * allocate memory (which could fail).
- */
- return -1;
- }
-
- /* if key is longer than 64 bytes reset it to key = MD5(key) */
- if (key_len > 64) {
- if (md5_vector_non_fips_allow(1, &key, &key_len, tk))
- return -1;
- key = tk;
- key_len = 16;
- }
-
- /* the HMAC_MD5 transform looks like:
- *
- * MD5(K XOR opad, MD5(K XOR ipad, text))
- *
- * where K is an n byte key
- * ipad is the byte 0x36 repeated 64 times
- * opad is the byte 0x5c repeated 64 times
- * and text is the data being protected */
-
- /* start out by storing key in ipad */
- os_memset(k_pad, 0, sizeof(k_pad));
- os_memcpy(k_pad, key, key_len);
-
- /* XOR key with ipad values */
- for (i = 0; i < 64; i++)
- k_pad[i] ^= 0x36;
-
- /* perform inner MD5 */
- _addr[0] = k_pad;
- _len[0] = 64;
- for (i = 0; i < num_elem; i++) {
- _addr[i + 1] = addr[i];
- _len[i + 1] = len[i];
- }
- if (md5_vector_non_fips_allow(1 + num_elem, _addr, _len, mac))
- return -1;
-
- os_memset(k_pad, 0, sizeof(k_pad));
- os_memcpy(k_pad, key, key_len);
- /* XOR key with opad values */
- for (i = 0; i < 64; i++)
- k_pad[i] ^= 0x5c;
-
- /* perform outer MD5 */
- _addr[0] = k_pad;
- _len[0] = 64;
- _addr[1] = mac;
- _len[1] = MD5_MAC_LEN;
- return md5_vector_non_fips_allow(2, _addr, _len, mac);
-}
-
-
-/**
- * hmac_md5_non_fips_allow - HMAC-MD5 over data buffer (RFC 2104)
- * @key: Key for HMAC operations
- * @key_len: Length of the key in bytes
- * @data: Pointers to the data area
- * @data_len: Length of the data area
- * @mac: Buffer for the hash (16 bytes)
- * Returns: 0 on success, -1 on failure
- */
-int hmac_md5_non_fips_allow(const u8 *key, size_t key_len, const u8 *data,
- size_t data_len, u8 *mac)
-{
- return hmac_md5_vector_non_fips_allow(key, key_len, 1, &data,
- &data_len, mac);
-}
diff --git a/contrib/wpa/src/crypto/md5.c b/contrib/wpa/src/crypto/md5.c
index 7f14e9b..db2b8cc 100644
--- a/contrib/wpa/src/crypto/md5.c
+++ b/contrib/wpa/src/crypto/md5.c
@@ -2,14 +2,8 @@
* MD5 hash implementation and interface functions
* Copyright (c) 2003-2005, Jouni Malinen <j@w1.fi>
*
- * This program is free software; you can redistribute it and/or modify
- * it under the terms of the GNU General Public License version 2 as
- * published by the Free Software Foundation.
- *
- * Alternatively, this software may be distributed under the terms of BSD
- * license.
- *
- * See README and COPYING for more details.
+ * This software may be distributed under the terms of the BSD license.
+ * See README for more details.
*/
#include "includes.h"
diff --git a/contrib/wpa/src/crypto/md5.h b/contrib/wpa/src/crypto/md5.h
index 8952590..33f8426 100644
--- a/contrib/wpa/src/crypto/md5.h
+++ b/contrib/wpa/src/crypto/md5.h
@@ -2,14 +2,8 @@
* MD5 hash implementation and interface functions
* Copyright (c) 2003-2009, Jouni Malinen <j@w1.fi>
*
- * This program is free software; you can redistribute it and/or modify
- * it under the terms of the GNU General Public License version 2 as
- * published by the Free Software Foundation.
- *
- * Alternatively, this software may be distributed under the terms of BSD
- * license.
- *
- * See README and COPYING for more details.
+ * This software may be distributed under the terms of the BSD license.
+ * See README for more details.
*/
#ifndef MD5_H
@@ -21,15 +15,5 @@ int hmac_md5_vector(const u8 *key, size_t key_len, size_t num_elem,
const u8 *addr[], const size_t *len, u8 *mac);
int hmac_md5(const u8 *key, size_t key_len, const u8 *data, size_t data_len,
u8 *mac);
-#ifdef CONFIG_FIPS
-int hmac_md5_vector_non_fips_allow(const u8 *key, size_t key_len,
- size_t num_elem, const u8 *addr[],
- const size_t *len, u8 *mac);
-int hmac_md5_non_fips_allow(const u8 *key, size_t key_len, const u8 *data,
- size_t data_len, u8 *mac);
-#else /* CONFIG_FIPS */
-#define hmac_md5_vector_non_fips_allow hmac_md5_vector
-#define hmac_md5_non_fips_allow hmac_md5
-#endif /* CONFIG_FIPS */
#endif /* MD5_H */
diff --git a/contrib/wpa/src/crypto/md5_i.h b/contrib/wpa/src/crypto/md5_i.h
index b7f6596..7dfc100 100644
--- a/contrib/wpa/src/crypto/md5_i.h
+++ b/contrib/wpa/src/crypto/md5_i.h
@@ -2,14 +2,8 @@
* MD5 internal definitions
* Copyright (c) 2003-2005, Jouni Malinen <j@w1.fi>
*
- * This program is free software; you can redistribute it and/or modify
- * it under the terms of the GNU General Public License version 2 as
- * published by the Free Software Foundation.
- *
- * Alternatively, this software may be distributed under the terms of BSD
- * license.
- *
- * See README and COPYING for more details.
+ * This software may be distributed under the terms of the BSD license.
+ * See README for more details.
*/
#ifndef MD5_I_H
diff --git a/contrib/wpa/src/crypto/milenage.c b/contrib/wpa/src/crypto/milenage.c
index cf0c60e..a7f9c6a 100644
--- a/contrib/wpa/src/crypto/milenage.c
+++ b/contrib/wpa/src/crypto/milenage.c
@@ -2,14 +2,8 @@
* 3GPP AKA - Milenage algorithm (3GPP TS 35.205, .206, .207, .208)
* Copyright (c) 2006-2007 <j@w1.fi>
*
- * This program is free software; you can redistribute it and/or modify
- * it under the terms of the GNU General Public License version 2 as
- * published by the Free Software Foundation.
- *
- * Alternatively, this software may be distributed under the terms of BSD
- * license.
- *
- * See README and COPYING for more details.
+ * This software may be distributed under the terms of the BSD license.
+ * See README for more details.
*
* This file implements an example authentication algorithm defined for 3GPP
* AKA. This can be used to implement a simple HLR/AuC into hlr_auc_gw to allow
diff --git a/contrib/wpa/src/crypto/milenage.h b/contrib/wpa/src/crypto/milenage.h
index d5054d6..62137d9 100644
--- a/contrib/wpa/src/crypto/milenage.h
+++ b/contrib/wpa/src/crypto/milenage.h
@@ -2,14 +2,8 @@
* UMTS AKA - Milenage algorithm (3GPP TS 35.205, .206, .207, .208)
* Copyright (c) 2006-2007 <j@w1.fi>
*
- * This program is free software; you can redistribute it and/or modify
- * it under the terms of the GNU General Public License version 2 as
- * published by the Free Software Foundation.
- *
- * Alternatively, this software may be distributed under the terms of BSD
- * license.
- *
- * See README and COPYING for more details.
+ * This software may be distributed under the terms of the BSD license.
+ * See README for more details.
*/
#ifndef MILENAGE_H
diff --git a/contrib/wpa/src/crypto/ms_funcs.c b/contrib/wpa/src/crypto/ms_funcs.c
index dae15ab..b2bbab2 100644
--- a/contrib/wpa/src/crypto/ms_funcs.c
+++ b/contrib/wpa/src/crypto/ms_funcs.c
@@ -1,15 +1,9 @@
/*
* WPA Supplicant / shared MSCHAPV2 helper functions / RFC 2433 / RFC 2759
- * Copyright (c) 2004-2009, Jouni Malinen <j@w1.fi>
+ * Copyright (c) 2004-2012, Jouni Malinen <j@w1.fi>
*
- * This program is free software; you can redistribute it and/or modify
- * it under the terms of the GNU General Public License version 2 as
- * published by the Free Software Foundation.
- *
- * Alternatively, this software may be distributed under the terms of BSD
- * license.
- *
- * See README and COPYING for more details.
+ * This software may be distributed under the terms of the BSD license.
+ * See README for more details.
*/
#include "includes.h"
@@ -19,6 +13,60 @@
#include "ms_funcs.h"
#include "crypto.h"
+/**
+ * utf8_to_ucs2 - Convert UTF-8 string to UCS-2 encoding
+ * @utf8_string: UTF-8 string (IN)
+ * @utf8_string_len: Length of utf8_string (IN)
+ * @ucs2_buffer: UCS-2 buffer (OUT)
+ * @ucs2_buffer_size: Length of UCS-2 buffer (IN)
+ * @ucs2_string_size: Number of 2-byte words in the resulting UCS-2 string
+ * Returns: 0 on success, -1 on failure
+ */
+static int utf8_to_ucs2(const u8 *utf8_string, size_t utf8_string_len,
+ u8 *ucs2_buffer, size_t ucs2_buffer_size,
+ size_t *ucs2_string_size)
+{
+ size_t i, j;
+
+ for (i = 0, j = 0; i < utf8_string_len; i++) {
+ u8 c = utf8_string[i];
+ if (j >= ucs2_buffer_size) {
+ /* input too long */
+ return -1;
+ }
+ if (c <= 0x7F) {
+ WPA_PUT_LE16(ucs2_buffer + j, c);
+ j += 2;
+ } else if (i == utf8_string_len - 1 ||
+ j >= ucs2_buffer_size - 1) {
+ /* incomplete surrogate */
+ return -1;
+ } else {
+ u8 c2 = utf8_string[++i];
+ if ((c & 0xE0) == 0xC0) {
+ /* two-byte encoding */
+ WPA_PUT_LE16(ucs2_buffer + j,
+ ((c & 0x1F) << 6) | (c2 & 0x3F));
+ j += 2;
+ } else if (i == utf8_string_len ||
+ j >= ucs2_buffer_size - 1) {
+ /* incomplete surrogate */
+ return -1;
+ } else {
+ /* three-byte encoding */
+ u8 c3 = utf8_string[++i];
+ WPA_PUT_LE16(ucs2_buffer + j,
+ ((c & 0xF) << 12) |
+ ((c2 & 0x3F) << 6) | (c3 & 0x3F));
+ }
+ }
+ }
+
+ if (ucs2_string_size)
+ *ucs2_string_size = j / 2;
+ return 0;
+}
+
/**
* challenge_hash - ChallengeHash() - RFC 2759, Sect. 8.2
@@ -53,7 +101,7 @@ static int challenge_hash(const u8 *peer_challenge, const u8 *auth_challenge,
/**
* nt_password_hash - NtPasswordHash() - RFC 2759, Sect. 8.3
- * @password: 0-to-256-unicode-char Password (IN; ASCII)
+ * @password: 0-to-256-unicode-char Password (IN; UTF-8)
* @password_len: Length of password
* @password_hash: 16-octet PasswordHash (OUT)
* Returns: 0 on success, -1 on failure
@@ -62,18 +110,13 @@ int nt_password_hash(const u8 *password, size_t password_len,
u8 *password_hash)
{
u8 buf[512], *pos;
- size_t i, len;
+ size_t len, max_len;
- if (password_len > 256)
- password_len = 256;
-
- /* Convert password into unicode */
- for (i = 0; i < password_len; i++) {
- buf[2 * i] = password[i];
- buf[2 * i + 1] = 0;
- }
+ max_len = sizeof(buf);
+ if (utf8_to_ucs2(password, password_len, buf, max_len, &len) < 0)
+ return -1;
- len = password_len * 2;
+ len *= 2;
pos = buf;
return md4_vector(1, (const u8 **) &pos, &len, password_hash);
}
@@ -117,7 +160,7 @@ void challenge_response(const u8 *challenge, const u8 *password_hash,
* @peer_challenge: 16-octet PeerChallenge (IN)
* @username: 0-to-256-char UserName (IN)
* @username_len: Length of username
- * @password: 0-to-256-unicode-char Password (IN; ASCII)
+ * @password: 0-to-256-unicode-char Password (IN; UTF-8)
* @password_len: Length of password
* @response: 24-octet Response (OUT)
* Returns: 0 on success, -1 on failure
@@ -130,8 +173,9 @@ int generate_nt_response(const u8 *auth_challenge, const u8 *peer_challenge,
u8 challenge[8];
u8 password_hash[16];
- challenge_hash(peer_challenge, auth_challenge, username, username_len,
- challenge);
+ if (challenge_hash(peer_challenge, auth_challenge, username,
+ username_len, challenge))
+ return -1;
if (nt_password_hash(password, password_len, password_hash))
return -1;
challenge_response(challenge, password_hash, response);
@@ -217,15 +261,16 @@ int generate_authenticator_response_pwhash(
if (sha1_vector(3, addr1, len1, response))
return -1;
- challenge_hash(peer_challenge, auth_challenge, username, username_len,
- challenge);
+ if (challenge_hash(peer_challenge, auth_challenge, username,
+ username_len, challenge))
+ return -1;
return sha1_vector(3, addr2, len2, response);
}
/**
* generate_authenticator_response - GenerateAuthenticatorResponse() - RFC 2759, Sect. 8.7
- * @password: 0-to-256-unicode-char Password (IN; ASCII)
+ * @password: 0-to-256-unicode-char Password (IN; UTF-8)
* @password_len: Length of password
* @nt_response: 24-octet NT-Response (IN)
* @peer_challenge: 16-octet PeerChallenge (IN)
@@ -254,7 +299,7 @@ int generate_authenticator_response(const u8 *password, size_t password_len,
/**
* nt_challenge_response - NtChallengeResponse() - RFC 2433, Sect. A.5
* @challenge: 8-octet Challenge (IN)
- * @password: 0-to-256-unicode-char Password (IN; ASCII)
+ * @password: 0-to-256-unicode-char Password (IN; UTF-8)
* @password_len: Length of password
* @response: 24-octet Response (OUT)
* Returns: 0 on success, -1 on failure
@@ -375,7 +420,7 @@ int get_asymetric_start_key(const u8 *master_key, u8 *session_key,
/**
* encrypt_pw_block_with_password_hash - EncryptPwBlockWithPasswordHash() - RFC 2759, Sect. 8.10
- * @password: 0-to-256-unicode-char Password (IN; ASCII)
+ * @password: 0-to-256-unicode-char Password (IN; UTF-8)
* @password_len: Length of password
* @password_hash: 16-octet PasswordHash (IN)
* @pw_block: 516-byte PwBlock (OUT)
@@ -385,18 +430,23 @@ int encrypt_pw_block_with_password_hash(
const u8 *password, size_t password_len,
const u8 *password_hash, u8 *pw_block)
{
- size_t i, offset;
+ size_t ucs2_len, offset;
u8 *pos;
- if (password_len > 256)
+ os_memset(pw_block, 0, PWBLOCK_LEN);
+
+ if (utf8_to_ucs2(password, password_len, pw_block, 512, &ucs2_len) < 0)
return -1;
- os_memset(pw_block, 0, PWBLOCK_LEN);
- offset = (256 - password_len) * 2;
- if (os_get_random(pw_block, offset) < 0)
+ if (ucs2_len > 256)
return -1;
- for (i = 0; i < password_len; i++)
- pw_block[offset + i * 2] = password[i];
+
+ offset = (256 - ucs2_len) * 2;
+ if (offset != 0) {
+ os_memmove(pw_block + offset, pw_block, ucs2_len * 2);
+ if (os_get_random(pw_block, offset) < 0)
+ return -1;
+ }
/*
* PasswordLength is 4 octets, but since the maximum password length is
* 256, only first two (in little endian byte order) can be non-zero.
@@ -410,9 +460,9 @@ int encrypt_pw_block_with_password_hash(
/**
* new_password_encrypted_with_old_nt_password_hash - NewPasswordEncryptedWithOldNtPasswordHash() - RFC 2759, Sect. 8.9
- * @new_password: 0-to-256-unicode-char NewPassword (IN; ASCII)
+ * @new_password: 0-to-256-unicode-char NewPassword (IN; UTF-8)
* @new_password_len: Length of new_password
- * @old_password: 0-to-256-unicode-char OldPassword (IN; ASCII)
+ * @old_password: 0-to-256-unicode-char OldPassword (IN; UTF-8)
* @old_password_len: Length of old_password
* @encrypted_pw_block: 516-octet EncryptedPwBlock (OUT)
* Returns: 0 on success, -1 on failure
@@ -450,9 +500,9 @@ void nt_password_hash_encrypted_with_block(const u8 *password_hash,
/**
* old_nt_password_hash_encrypted_with_new_nt_password_hash - OldNtPasswordHashEncryptedWithNewNtPasswordHash() - RFC 2759, Sect. 8.12
- * @new_password: 0-to-256-unicode-char NewPassword (IN; ASCII)
+ * @new_password: 0-to-256-unicode-char NewPassword (IN; UTF-8)
* @new_password_len: Length of new_password
- * @old_password: 0-to-256-unicode-char OldPassword (IN; ASCII)
+ * @old_password: 0-to-256-unicode-char OldPassword (IN; UTF-8)
* @old_password_len: Length of old_password
* @encrypted_password_hash: 16-octet EncryptedPasswordHash (OUT)
* Returns: 0 on success, -1 on failure
diff --git a/contrib/wpa/src/crypto/ms_funcs.h b/contrib/wpa/src/crypto/ms_funcs.h
index 298dbcf..bd9bfee 100644
--- a/contrib/wpa/src/crypto/ms_funcs.h
+++ b/contrib/wpa/src/crypto/ms_funcs.h
@@ -2,14 +2,8 @@
* WPA Supplicant / shared MSCHAPV2 helper functions / RFC 2433 / RFC 2759
* Copyright (c) 2004-2009, Jouni Malinen <j@w1.fi>
*
- * This program is free software; you can redistribute it and/or modify
- * it under the terms of the GNU General Public License version 2 as
- * published by the Free Software Foundation.
- *
- * Alternatively, this software may be distributed under the terms of BSD
- * license.
- *
- * See README and COPYING for more details.
+ * This software may be distributed under the terms of the BSD license.
+ * See README for more details.
*/
#ifndef MS_FUNCS_H
diff --git a/contrib/wpa/src/crypto/random.c b/contrib/wpa/src/crypto/random.c
new file mode 100644
index 0000000..053740e
--- /dev/null
+++ b/contrib/wpa/src/crypto/random.c
@@ -0,0 +1,446 @@
+/*
+ * Random number generator
+ * Copyright (c) 2010-2011, Jouni Malinen <j@w1.fi>
+ *
+ * This software may be distributed under the terms of the BSD license.
+ * See README for more details.
+ *
+ * This random number generator is used to provide additional entropy to the
+ * one provided by the operating system (os_get_random()) for session key
+ * generation. The os_get_random() output is expected to be secure and the
+ * implementation here is expected to provide only limited protection against
+ * cases where os_get_random() cannot provide strong randomness. This
+ * implementation shall not be assumed to be secure as the sole source of
+ * randomness. The random_get_bytes() function mixes in randomness from
+ * os_get_random() and as such, calls to os_get_random() can be replaced with
+ * calls to random_get_bytes() without reducing security.
+ *
+ * The design here follows partially the design used in the Linux
+ * drivers/char/random.c, but the implementation here is simpler and not as
+ * strong. This is a compromise to reduce duplicated CPU effort and to avoid
+ * extra code/memory size. As pointed out above, os_get_random() needs to be
+ * guaranteed to be secure for any of the security assumptions to hold.
+ */
+
+#include "utils/includes.h"
+#ifdef __linux__
+#include <fcntl.h>
+#endif /* __linux__ */
+
+#include "utils/common.h"
+#include "utils/eloop.h"
+#include "crypto/crypto.h"
+#include "sha1.h"
+#include "random.h"
+
+#define POOL_WORDS 32
+#define POOL_WORDS_MASK (POOL_WORDS - 1)
+#define POOL_TAP1 26
+#define POOL_TAP2 20
+#define POOL_TAP3 14
+#define POOL_TAP4 7
+#define POOL_TAP5 1
+#define EXTRACT_LEN 16
+#define MIN_READY_MARK 2
+
+static u32 pool[POOL_WORDS];
+static unsigned int input_rotate = 0;
+static unsigned int pool_pos = 0;
+static u8 dummy_key[20];
+#ifdef __linux__
+static size_t dummy_key_avail = 0;
+static int random_fd = -1;
+#endif /* __linux__ */
+static unsigned int own_pool_ready = 0;
+#define RANDOM_ENTROPY_SIZE 20
+static char *random_entropy_file = NULL;
+static int random_entropy_file_read = 0;
+
+#define MIN_COLLECT_ENTROPY 1000
+static unsigned int entropy = 0;
+static unsigned int total_collected = 0;
+
+
+static void random_write_entropy(void);
+
+
+static u32 __ROL32(u32 x, u32 y)
+{
+ return (x << (y & 31)) | (x >> (32 - (y & 31)));
+}
+
+
+static void random_mix_pool(const void *buf, size_t len)
+{
+ static const u32 twist[8] = {
+ 0x00000000, 0x3b6e20c8, 0x76dc4190, 0x4db26158,
+ 0xedb88320, 0xd6d6a3e8, 0x9b64c2b0, 0xa00ae278
+ };
+ const u8 *pos = buf;
+ u32 w;
+
+ wpa_hexdump_key(MSG_EXCESSIVE, "random_mix_pool", buf, len);
+
+ while (len--) {
+ w = __ROL32(*pos++, input_rotate & 31);
+ input_rotate += pool_pos ? 7 : 14;
+ pool_pos = (pool_pos - 1) & POOL_WORDS_MASK;
+ w ^= pool[pool_pos];
+ w ^= pool[(pool_pos + POOL_TAP1) & POOL_WORDS_MASK];
+ w ^= pool[(pool_pos + POOL_TAP2) & POOL_WORDS_MASK];
+ w ^= pool[(pool_pos + POOL_TAP3) & POOL_WORDS_MASK];
+ w ^= pool[(pool_pos + POOL_TAP4) & POOL_WORDS_MASK];
+ w ^= pool[(pool_pos + POOL_TAP5) & POOL_WORDS_MASK];
+ pool[pool_pos] = (w >> 3) ^ twist[w & 7];
+ }
+}
+
+
+static void random_extract(u8 *out)
+{
+ unsigned int i;
+ u8 hash[SHA1_MAC_LEN];
+ u32 *hash_ptr;
+ u32 buf[POOL_WORDS / 2];
+
+ /* First, add hash back to pool to make backtracking more difficult. */
+ hmac_sha1(dummy_key, sizeof(dummy_key), (const u8 *) pool,
+ sizeof(pool), hash);
+ random_mix_pool(hash, sizeof(hash));
+ /* Hash half the pool to extra data */
+ for (i = 0; i < POOL_WORDS / 2; i++)
+ buf[i] = pool[(pool_pos - i) & POOL_WORDS_MASK];
+ hmac_sha1(dummy_key, sizeof(dummy_key), (const u8 *) buf,
+ sizeof(buf), hash);
+ /*
+ * Fold the hash to further reduce any potential output pattern.
+ * Though, compromise this to reduce CPU use for the most common output
+ * length (32) and return 16 bytes from instead of only half.
+ */
+ hash_ptr = (u32 *) hash;
+ hash_ptr[0] ^= hash_ptr[4];
+ os_memcpy(out, hash, EXTRACT_LEN);
+}
+
+
+void random_add_randomness(const void *buf, size_t len)
+{
+ struct os_time t;
+ static unsigned int count = 0;
+
+ count++;
+ if (entropy > MIN_COLLECT_ENTROPY && (count & 0x3ff) != 0) {
+ /*
+ * No need to add more entropy at this point, so save CPU and
+ * skip the update.
+ */
+ return;
+ }
+ wpa_printf(MSG_EXCESSIVE, "Add randomness: count=%u entropy=%u",
+ count, entropy);
+
+ os_get_time(&t);
+ wpa_hexdump_key(MSG_EXCESSIVE, "random pool",
+ (const u8 *) pool, sizeof(pool));
+ random_mix_pool(&t, sizeof(t));
+ random_mix_pool(buf, len);
+ wpa_hexdump_key(MSG_EXCESSIVE, "random pool",
+ (const u8 *) pool, sizeof(pool));
+ entropy++;
+ total_collected++;
+}
+
+
+int random_get_bytes(void *buf, size_t len)
+{
+ int ret;
+ u8 *bytes = buf;
+ size_t left;
+
+ wpa_printf(MSG_MSGDUMP, "Get randomness: len=%u entropy=%u",
+ (unsigned int) len, entropy);
+
+ /* Start with assumed strong randomness from OS */
+ ret = os_get_random(buf, len);
+ wpa_hexdump_key(MSG_EXCESSIVE, "random from os_get_random",
+ buf, len);
+
+ /* Mix in additional entropy extracted from the internal pool */
+ left = len;
+ while (left) {
+ size_t siz, i;
+ u8 tmp[EXTRACT_LEN];
+ random_extract(tmp);
+ wpa_hexdump_key(MSG_EXCESSIVE, "random from internal pool",
+ tmp, sizeof(tmp));
+ siz = left > EXTRACT_LEN ? EXTRACT_LEN : left;
+ for (i = 0; i < siz; i++)
+ *bytes++ ^= tmp[i];
+ left -= siz;
+ }
+
+#ifdef CONFIG_FIPS
+ /* Mix in additional entropy from the crypto module */
+ left = len;
+ while (left) {
+ size_t siz, i;
+ u8 tmp[EXTRACT_LEN];
+ if (crypto_get_random(tmp, sizeof(tmp)) < 0) {
+ wpa_printf(MSG_ERROR, "random: No entropy available "
+ "for generating strong random bytes");
+ return -1;
+ }
+ wpa_hexdump_key(MSG_EXCESSIVE, "random from crypto module",
+ tmp, sizeof(tmp));
+ siz = left > EXTRACT_LEN ? EXTRACT_LEN : left;
+ for (i = 0; i < siz; i++)
+ *bytes++ ^= tmp[i];
+ left -= siz;
+ }
+#endif /* CONFIG_FIPS */
+
+ wpa_hexdump_key(MSG_EXCESSIVE, "mixed random", buf, len);
+
+ if (entropy < len)
+ entropy = 0;
+ else
+ entropy -= len;
+
+ return ret;
+}
+
+
+int random_pool_ready(void)
+{
+#ifdef __linux__
+ int fd;
+ ssize_t res;
+
+ /*
+ * Make sure that there is reasonable entropy available before allowing
+ * some key derivation operations to proceed.
+ */
+
+ if (dummy_key_avail == sizeof(dummy_key))
+ return 1; /* Already initialized - good to continue */
+
+ /*
+ * Try to fetch some more data from the kernel high quality
+ * /dev/random. There may not be enough data available at this point,
+ * so use non-blocking read to avoid blocking the application
+ * completely.
+ */
+ fd = open("/dev/random", O_RDONLY | O_NONBLOCK);
+ if (fd < 0) {
+#ifndef CONFIG_NO_STDOUT_DEBUG
+ int error = errno;
+ perror("open(/dev/random)");
+ wpa_printf(MSG_ERROR, "random: Cannot open /dev/random: %s",
+ strerror(error));
+#endif /* CONFIG_NO_STDOUT_DEBUG */
+ return -1;
+ }
+
+ res = read(fd, dummy_key + dummy_key_avail,
+ sizeof(dummy_key) - dummy_key_avail);
+ if (res < 0) {
+ wpa_printf(MSG_ERROR, "random: Cannot read from /dev/random: "
+ "%s", strerror(errno));
+ res = 0;
+ }
+ wpa_printf(MSG_DEBUG, "random: Got %u/%u bytes from "
+ "/dev/random", (unsigned) res,
+ (unsigned) (sizeof(dummy_key) - dummy_key_avail));
+ dummy_key_avail += res;
+ close(fd);
+
+ if (dummy_key_avail == sizeof(dummy_key)) {
+ if (own_pool_ready < MIN_READY_MARK)
+ own_pool_ready = MIN_READY_MARK;
+ random_write_entropy();
+ return 1;
+ }
+
+ wpa_printf(MSG_INFO, "random: Only %u/%u bytes of strong "
+ "random data available from /dev/random",
+ (unsigned) dummy_key_avail, (unsigned) sizeof(dummy_key));
+
+ if (own_pool_ready >= MIN_READY_MARK ||
+ total_collected + 10 * own_pool_ready > MIN_COLLECT_ENTROPY) {
+ wpa_printf(MSG_INFO, "random: Allow operation to proceed "
+ "based on internal entropy");
+ return 1;
+ }
+
+ wpa_printf(MSG_INFO, "random: Not enough entropy pool available for "
+ "secure operations");
+ return 0;
+#else /* __linux__ */
+ /* TODO: could do similar checks on non-Linux platforms */
+ return 1;
+#endif /* __linux__ */
+}
+
+
+void random_mark_pool_ready(void)
+{
+ own_pool_ready++;
+ wpa_printf(MSG_DEBUG, "random: Mark internal entropy pool to be "
+ "ready (count=%u/%u)", own_pool_ready, MIN_READY_MARK);
+ random_write_entropy();
+}
+
+
+#ifdef __linux__
+
+static void random_close_fd(void)
+{
+ if (random_fd >= 0) {
+ eloop_unregister_read_sock(random_fd);
+ close(random_fd);
+ random_fd = -1;
+ }
+}
+
+
+static void random_read_fd(int sock, void *eloop_ctx, void *sock_ctx)
+{
+ ssize_t res;
+
+ if (dummy_key_avail == sizeof(dummy_key)) {
+ random_close_fd();
+ return;
+ }
+
+ res = read(sock, dummy_key + dummy_key_avail,
+ sizeof(dummy_key) - dummy_key_avail);
+ if (res < 0) {
+ wpa_printf(MSG_ERROR, "random: Cannot read from /dev/random: "
+ "%s", strerror(errno));
+ return;
+ }
+
+ wpa_printf(MSG_DEBUG, "random: Got %u/%u bytes from /dev/random",
+ (unsigned) res,
+ (unsigned) (sizeof(dummy_key) - dummy_key_avail));
+ dummy_key_avail += res;
+
+ if (dummy_key_avail == sizeof(dummy_key)) {
+ random_close_fd();
+ if (own_pool_ready < MIN_READY_MARK)
+ own_pool_ready = MIN_READY_MARK;
+ random_write_entropy();
+ }
+}
+
+#endif /* __linux__ */
+
+
+static void random_read_entropy(void)
+{
+ char *buf;
+ size_t len;
+
+ if (!random_entropy_file)
+ return;
+
+ buf = os_readfile(random_entropy_file, &len);
+ if (buf == NULL)
+ return; /* entropy file not yet available */
+
+ if (len != 1 + RANDOM_ENTROPY_SIZE) {
+ wpa_printf(MSG_DEBUG, "random: Invalid entropy file %s",
+ random_entropy_file);
+ os_free(buf);
+ return;
+ }
+
+ own_pool_ready = (u8) buf[0];
+ random_add_randomness(buf + 1, RANDOM_ENTROPY_SIZE);
+ random_entropy_file_read = 1;
+ os_free(buf);
+ wpa_printf(MSG_DEBUG, "random: Added entropy from %s "
+ "(own_pool_ready=%u)",
+ random_entropy_file, own_pool_ready);
+}
+
+
+static void random_write_entropy(void)
+{
+ char buf[RANDOM_ENTROPY_SIZE];
+ FILE *f;
+ u8 opr;
+ int fail = 0;
+
+ if (!random_entropy_file)
+ return;
+
+ if (random_get_bytes(buf, RANDOM_ENTROPY_SIZE) < 0)
+ return;
+
+ f = fopen(random_entropy_file, "wb");
+ if (f == NULL) {
+ wpa_printf(MSG_ERROR, "random: Could not open entropy file %s "
+ "for writing", random_entropy_file);
+ return;
+ }
+
+ opr = own_pool_ready > 0xff ? 0xff : own_pool_ready;
+ if (fwrite(&opr, 1, 1, f) != 1 ||
+ fwrite(buf, RANDOM_ENTROPY_SIZE, 1, f) != 1)
+ fail = 1;
+ fclose(f);
+ if (fail) {
+ wpa_printf(MSG_ERROR, "random: Could not write entropy data "
+ "to %s", random_entropy_file);
+ return;
+ }
+
+ wpa_printf(MSG_DEBUG, "random: Updated entropy file %s "
+ "(own_pool_ready=%u)",
+ random_entropy_file, own_pool_ready);
+}
+
+
+void random_init(const char *entropy_file)
+{
+ os_free(random_entropy_file);
+ if (entropy_file)
+ random_entropy_file = os_strdup(entropy_file);
+ else
+ random_entropy_file = NULL;
+ random_read_entropy();
+
+#ifdef __linux__
+ if (random_fd >= 0)
+ return;
+
+ random_fd = open("/dev/random", O_RDONLY | O_NONBLOCK);
+ if (random_fd < 0) {
+#ifndef CONFIG_NO_STDOUT_DEBUG
+ int error = errno;
+ perror("open(/dev/random)");
+ wpa_printf(MSG_ERROR, "random: Cannot open /dev/random: %s",
+ strerror(error));
+#endif /* CONFIG_NO_STDOUT_DEBUG */
+ return;
+ }
+ wpa_printf(MSG_DEBUG, "random: Trying to read entropy from "
+ "/dev/random");
+
+ eloop_register_read_sock(random_fd, random_read_fd, NULL, NULL);
+#endif /* __linux__ */
+
+ random_write_entropy();
+}
+
+
+void random_deinit(void)
+{
+#ifdef __linux__
+ random_close_fd();
+#endif /* __linux__ */
+ random_write_entropy();
+ os_free(random_entropy_file);
+ random_entropy_file = NULL;
+}
diff --git a/contrib/wpa/src/crypto/random.h b/contrib/wpa/src/crypto/random.h
new file mode 100644
index 0000000..d13e1c4
--- /dev/null
+++ b/contrib/wpa/src/crypto/random.h
@@ -0,0 +1,28 @@
+/*
+ * Random number generator
+ * Copyright (c) 2010-2011, Jouni Malinen <j@w1.fi>
+ *
+ * This software may be distributed under the terms of the BSD license.
+ * See README for more details.
+ */
+
+#ifndef RANDOM_H
+#define RANDOM_H
+
+#ifdef CONFIG_NO_RANDOM_POOL
+#define random_init(e) do { } while (0)
+#define random_deinit() do { } while (0)
+#define random_add_randomness(b, l) do { } while (0)
+#define random_get_bytes(b, l) os_get_random((b), (l))
+#define random_pool_ready() 1
+#define random_mark_pool_ready() do { } while (0)
+#else /* CONFIG_NO_RANDOM_POOL */
+void random_init(const char *entropy_file);
+void random_deinit(void);
+void random_add_randomness(const void *buf, size_t len);
+int random_get_bytes(void *buf, size_t len);
+int random_pool_ready(void);
+void random_mark_pool_ready(void);
+#endif /* CONFIG_NO_RANDOM_POOL */
+
+#endif /* RANDOM_H */
diff --git a/contrib/wpa/src/crypto/rc4.c b/contrib/wpa/src/crypto/rc4.c
index 5ab1be1..98ae269 100644
--- a/contrib/wpa/src/crypto/rc4.c
+++ b/contrib/wpa/src/crypto/rc4.c
@@ -2,14 +2,8 @@
* RC4 stream cipher
* Copyright (c) 2002-2005, Jouni Malinen <j@w1.fi>
*
- * This program is free software; you can redistribute it and/or modify
- * it under the terms of the GNU General Public License version 2 as
- * published by the Free Software Foundation.
- *
- * Alternatively, this software may be distributed under the terms of BSD
- * license.
- *
- * See README and COPYING for more details.
+ * This software may be distributed under the terms of the BSD license.
+ * See README for more details.
*/
#include "includes.h"
diff --git a/contrib/wpa/src/crypto/sha1-internal.c b/contrib/wpa/src/crypto/sha1-internal.c
index 3f05ca1..10bf153 100644
--- a/contrib/wpa/src/crypto/sha1-internal.c
+++ b/contrib/wpa/src/crypto/sha1-internal.c
@@ -2,14 +2,8 @@
* SHA1 hash implementation and interface functions
* Copyright (c) 2003-2005, Jouni Malinen <j@w1.fi>
*
- * This program is free software; you can redistribute it and/or modify
- * it under the terms of the GNU General Public License version 2 as
- * published by the Free Software Foundation.
- *
- * Alternatively, this software may be distributed under the terms of BSD
- * license.
- *
- * See README and COPYING for more details.
+ * This software may be distributed under the terms of the BSD license.
+ * See README for more details.
*/
#include "includes.h"
diff --git a/contrib/wpa/src/crypto/sha1-pbkdf2.c b/contrib/wpa/src/crypto/sha1-pbkdf2.c
index 11323de..8effe2f 100644
--- a/contrib/wpa/src/crypto/sha1-pbkdf2.c
+++ b/contrib/wpa/src/crypto/sha1-pbkdf2.c
@@ -2,24 +2,16 @@
* SHA1-based key derivation function (PBKDF2) for IEEE 802.11i
* Copyright (c) 2003-2005, Jouni Malinen <j@w1.fi>
*
- * This program is free software; you can redistribute it and/or modify
- * it under the terms of the GNU General Public License version 2 as
- * published by the Free Software Foundation.
- *
- * Alternatively, this software may be distributed under the terms of BSD
- * license.
- *
- * See README and COPYING for more details.
+ * This software may be distributed under the terms of the BSD license.
+ * See README for more details.
*/
#include "includes.h"
#include "common.h"
#include "sha1.h"
-#include "md5.h"
-#include "crypto.h"
-static int pbkdf2_sha1_f(const char *passphrase, const char *ssid,
+static int pbkdf2_sha1_f(const char *passphrase, const u8 *ssid,
size_t ssid_len, int iterations, unsigned int count,
u8 *digest)
{
@@ -30,7 +22,7 @@ static int pbkdf2_sha1_f(const char *passphrase, const char *ssid,
size_t len[2];
size_t passphrase_len = os_strlen(passphrase);
- addr[0] = (u8 *) ssid;
+ addr[0] = ssid;
len[0] = ssid_len;
addr[1] = count_buf;
len[1] = 4;
@@ -77,7 +69,7 @@ static int pbkdf2_sha1_f(const char *passphrase, const char *ssid,
* iterations is set to 4096 and buflen to 32. This function is described in
* IEEE Std 802.11-2004, Clause H.4. The main construction is from PKCS#5 v2.0.
*/
-int pbkdf2_sha1(const char *passphrase, const char *ssid, size_t ssid_len,
+int pbkdf2_sha1(const char *passphrase, const u8 *ssid, size_t ssid_len,
int iterations, u8 *buf, size_t buflen)
{
unsigned int count = 0;
diff --git a/contrib/wpa/src/crypto/sha1-prf.c b/contrib/wpa/src/crypto/sha1-prf.c
new file mode 100644
index 0000000..90b9e74
--- /dev/null
+++ b/contrib/wpa/src/crypto/sha1-prf.c
@@ -0,0 +1,66 @@
+/*
+ * SHA1-based PRF
+ * Copyright (c) 2003-2005, Jouni Malinen <j@w1.fi>
+ *
+ * This software may be distributed under the terms of the BSD license.
+ * See README for more details.
+ */
+
+#include "includes.h"
+
+#include "common.h"
+#include "sha1.h"
+#include "crypto.h"
+
+
+/**
+ * sha1_prf - SHA1-based Pseudo-Random Function (PRF) (IEEE 802.11i, 8.5.1.1)
+ * @key: Key for PRF
+ * @key_len: Length of the key in bytes
+ * @label: A unique label for each purpose of the PRF
+ * @data: Extra data to bind into the key
+ * @data_len: Length of the data
+ * @buf: Buffer for the generated pseudo-random key
+ * @buf_len: Number of bytes of key to generate
+ * Returns: 0 on success, -1 of failure
+ *
+ * This function is used to derive new, cryptographically separate keys from a
+ * given key (e.g., PMK in IEEE 802.11i).
+ */
+int sha1_prf(const u8 *key, size_t key_len, const char *label,
+ const u8 *data, size_t data_len, u8 *buf, size_t buf_len)
+{
+ u8 counter = 0;
+ size_t pos, plen;
+ u8 hash[SHA1_MAC_LEN];
+ size_t label_len = os_strlen(label) + 1;
+ const unsigned char *addr[3];
+ size_t len[3];
+
+ addr[0] = (u8 *) label;
+ len[0] = label_len;
+ addr[1] = data;
+ len[1] = data_len;
+ addr[2] = &counter;
+ len[2] = 1;
+
+ pos = 0;
+ while (pos < buf_len) {
+ plen = buf_len - pos;
+ if (plen >= SHA1_MAC_LEN) {
+ if (hmac_sha1_vector(key, key_len, 3, addr, len,
+ &buf[pos]))
+ return -1;
+ pos += SHA1_MAC_LEN;
+ } else {
+ if (hmac_sha1_vector(key, key_len, 3, addr, len,
+ hash))
+ return -1;
+ os_memcpy(&buf[pos], hash, plen);
+ break;
+ }
+ counter++;
+ }
+
+ return 0;
+}
diff --git a/contrib/wpa/src/crypto/sha1-tlsprf.c b/contrib/wpa/src/crypto/sha1-tlsprf.c
index 2c8c029..0effd9b 100644
--- a/contrib/wpa/src/crypto/sha1-tlsprf.c
+++ b/contrib/wpa/src/crypto/sha1-tlsprf.c
@@ -2,14 +2,8 @@
* TLS PRF (SHA1 + MD5)
* Copyright (c) 2003-2005, Jouni Malinen <j@w1.fi>
*
- * This program is free software; you can redistribute it and/or modify
- * it under the terms of the GNU General Public License version 2 as
- * published by the Free Software Foundation.
- *
- * Alternatively, this software may be distributed under the terms of BSD
- * license.
- *
- * See README and COPYING for more details.
+ * This software may be distributed under the terms of the BSD license.
+ * See README for more details.
*/
#include "includes.h"
@@ -17,11 +11,10 @@
#include "common.h"
#include "sha1.h"
#include "md5.h"
-#include "crypto.h"
/**
- * tls_prf - Pseudo-Random Function for TLS (TLS-PRF, RFC 2246)
+ * tls_prf_sha1_md5 - Pseudo-Random Function for TLS (TLS-PRF, RFC 2246)
* @secret: Key for PRF
* @secret_len: Length of the key in bytes
* @label: A unique label for each purpose of the PRF
@@ -34,8 +27,8 @@
* This function is used to derive new, cryptographically separate keys from a
* given key in TLS. This PRF is defined in RFC 2246, Chapter 5.
*/
-int tls_prf(const u8 *secret, size_t secret_len, const char *label,
- const u8 *seed, size_t seed_len, u8 *out, size_t outlen)
+int tls_prf_sha1_md5(const u8 *secret, size_t secret_len, const char *label,
+ const u8 *seed, size_t seed_len, u8 *out, size_t outlen)
{
size_t L_S1, L_S2, i;
const u8 *S1, *S2;
@@ -78,19 +71,16 @@ int tls_prf(const u8 *secret, size_t secret_len, const char *label,
S2--;
}
- hmac_md5_vector_non_fips_allow(S1, L_S1, 2, &MD5_addr[1], &MD5_len[1],
- A_MD5);
+ hmac_md5_vector(S1, L_S1, 2, &MD5_addr[1], &MD5_len[1], A_MD5);
hmac_sha1_vector(S2, L_S2, 2, &SHA1_addr[1], &SHA1_len[1], A_SHA1);
MD5_pos = MD5_MAC_LEN;
SHA1_pos = SHA1_MAC_LEN;
for (i = 0; i < outlen; i++) {
if (MD5_pos == MD5_MAC_LEN) {
- hmac_md5_vector_non_fips_allow(S1, L_S1, 3, MD5_addr,
- MD5_len, P_MD5);
+ hmac_md5_vector(S1, L_S1, 3, MD5_addr, MD5_len, P_MD5);
MD5_pos = 0;
- hmac_md5_non_fips_allow(S1, L_S1, A_MD5, MD5_MAC_LEN,
- A_MD5);
+ hmac_md5(S1, L_S1, A_MD5, MD5_MAC_LEN, A_MD5);
}
if (SHA1_pos == SHA1_MAC_LEN) {
hmac_sha1_vector(S2, L_S2, 3, SHA1_addr, SHA1_len,
diff --git a/contrib/wpa/src/crypto/sha1-tprf.c b/contrib/wpa/src/crypto/sha1-tprf.c
index 4a80e96..a529494 100644
--- a/contrib/wpa/src/crypto/sha1-tprf.c
+++ b/contrib/wpa/src/crypto/sha1-tprf.c
@@ -2,14 +2,8 @@
* SHA1 T-PRF for EAP-FAST
* Copyright (c) 2003-2005, Jouni Malinen <j@w1.fi>
*
- * This program is free software; you can redistribute it and/or modify
- * it under the terms of the GNU General Public License version 2 as
- * published by the Free Software Foundation.
- *
- * Alternatively, this software may be distributed under the terms of BSD
- * license.
- *
- * See README and COPYING for more details.
+ * This software may be distributed under the terms of the BSD license.
+ * See README for more details.
*/
#include "includes.h"
diff --git a/contrib/wpa/src/crypto/sha1.c b/contrib/wpa/src/crypto/sha1.c
index fe00bdb..d48c77d 100644
--- a/contrib/wpa/src/crypto/sha1.c
+++ b/contrib/wpa/src/crypto/sha1.c
@@ -2,14 +2,8 @@
* SHA1 hash implementation and interface functions
* Copyright (c) 2003-2005, Jouni Malinen <j@w1.fi>
*
- * This program is free software; you can redistribute it and/or modify
- * it under the terms of the GNU General Public License version 2 as
- * published by the Free Software Foundation.
- *
- * Alternatively, this software may be distributed under the terms of BSD
- * license.
- *
- * See README and COPYING for more details.
+ * This software may be distributed under the terms of the BSD license.
+ * See README for more details.
*/
#include "includes.h"
@@ -108,56 +102,3 @@ int hmac_sha1(const u8 *key, size_t key_len, const u8 *data, size_t data_len,
{
return hmac_sha1_vector(key, key_len, 1, &data, &data_len, mac);
}
-
-
-/**
- * sha1_prf - SHA1-based Pseudo-Random Function (PRF) (IEEE 802.11i, 8.5.1.1)
- * @key: Key for PRF
- * @key_len: Length of the key in bytes
- * @label: A unique label for each purpose of the PRF
- * @data: Extra data to bind into the key
- * @data_len: Length of the data
- * @buf: Buffer for the generated pseudo-random key
- * @buf_len: Number of bytes of key to generate
- * Returns: 0 on success, -1 of failure
- *
- * This function is used to derive new, cryptographically separate keys from a
- * given key (e.g., PMK in IEEE 802.11i).
- */
-int sha1_prf(const u8 *key, size_t key_len, const char *label,
- const u8 *data, size_t data_len, u8 *buf, size_t buf_len)
-{
- u8 counter = 0;
- size_t pos, plen;
- u8 hash[SHA1_MAC_LEN];
- size_t label_len = os_strlen(label) + 1;
- const unsigned char *addr[3];
- size_t len[3];
-
- addr[0] = (u8 *) label;
- len[0] = label_len;
- addr[1] = data;
- len[1] = data_len;
- addr[2] = &counter;
- len[2] = 1;
-
- pos = 0;
- while (pos < buf_len) {
- plen = buf_len - pos;
- if (plen >= SHA1_MAC_LEN) {
- if (hmac_sha1_vector(key, key_len, 3, addr, len,
- &buf[pos]))
- return -1;
- pos += SHA1_MAC_LEN;
- } else {
- if (hmac_sha1_vector(key, key_len, 3, addr, len,
- hash))
- return -1;
- os_memcpy(&buf[pos], hash, plen);
- break;
- }
- counter++;
- }
-
- return 0;
-}
diff --git a/contrib/wpa/src/crypto/sha1.h b/contrib/wpa/src/crypto/sha1.h
index c1a6233..933cd81 100644
--- a/contrib/wpa/src/crypto/sha1.h
+++ b/contrib/wpa/src/crypto/sha1.h
@@ -2,14 +2,8 @@
* SHA1 hash implementation and interface functions
* Copyright (c) 2003-2009, Jouni Malinen <j@w1.fi>
*
- * This program is free software; you can redistribute it and/or modify
- * it under the terms of the GNU General Public License version 2 as
- * published by the Free Software Foundation.
- *
- * Alternatively, this software may be distributed under the terms of BSD
- * license.
- *
- * See README and COPYING for more details.
+ * This software may be distributed under the terms of the BSD license.
+ * See README for more details.
*/
#ifndef SHA1_H
@@ -25,9 +19,9 @@ int sha1_prf(const u8 *key, size_t key_len, const char *label,
const u8 *data, size_t data_len, u8 *buf, size_t buf_len);
int sha1_t_prf(const u8 *key, size_t key_len, const char *label,
const u8 *seed, size_t seed_len, u8 *buf, size_t buf_len);
-int __must_check tls_prf(const u8 *secret, size_t secret_len,
- const char *label, const u8 *seed, size_t seed_len,
- u8 *out, size_t outlen);
-int pbkdf2_sha1(const char *passphrase, const char *ssid, size_t ssid_len,
+int __must_check tls_prf_sha1_md5(const u8 *secret, size_t secret_len,
+ const char *label, const u8 *seed,
+ size_t seed_len, u8 *out, size_t outlen);
+int pbkdf2_sha1(const char *passphrase, const u8 *ssid, size_t ssid_len,
int iterations, u8 *buf, size_t buflen);
#endif /* SHA1_H */
diff --git a/contrib/wpa/src/crypto/sha1_i.h b/contrib/wpa/src/crypto/sha1_i.h
index ec2f82f..344387e 100644
--- a/contrib/wpa/src/crypto/sha1_i.h
+++ b/contrib/wpa/src/crypto/sha1_i.h
@@ -2,14 +2,8 @@
* SHA1 internal definitions
* Copyright (c) 2003-2005, Jouni Malinen <j@w1.fi>
*
- * This program is free software; you can redistribute it and/or modify
- * it under the terms of the GNU General Public License version 2 as
- * published by the Free Software Foundation.
- *
- * Alternatively, this software may be distributed under the terms of BSD
- * license.
- *
- * See README and COPYING for more details.
+ * This software may be distributed under the terms of the BSD license.
+ * See README for more details.
*/
#ifndef SHA1_I_H
diff --git a/contrib/wpa/src/crypto/sha256-internal.c b/contrib/wpa/src/crypto/sha256-internal.c
index b061373..35299b0 100644
--- a/contrib/wpa/src/crypto/sha256-internal.c
+++ b/contrib/wpa/src/crypto/sha256-internal.c
@@ -1,34 +1,18 @@
/*
* SHA-256 hash implementation and interface functions
- * Copyright (c) 2003-2007, Jouni Malinen <j@w1.fi>
+ * Copyright (c) 2003-2011, Jouni Malinen <j@w1.fi>
*
- * This program is free software; you can redistribute it and/or modify
- * it under the terms of the GNU General Public License version 2 as
- * published by the Free Software Foundation.
- *
- * Alternatively, this software may be distributed under the terms of BSD
- * license.
- *
- * See README and COPYING for more details.
+ * This software may be distributed under the terms of the BSD license.
+ * See README for more details.
*/
#include "includes.h"
#include "common.h"
#include "sha256.h"
+#include "sha256_i.h"
#include "crypto.h"
-struct sha256_state {
- u64 length;
- u32 state[8], curlen;
- u8 buf[64];
-};
-
-static void sha256_init(struct sha256_state *md);
-static int sha256_process(struct sha256_state *md, const unsigned char *in,
- unsigned long inlen);
-static int sha256_done(struct sha256_state *md, unsigned char *out);
-
/**
* sha256_vector - SHA256 hash for data vector
@@ -137,7 +121,7 @@ static int sha256_compress(struct sha256_state *md, unsigned char *buf)
/* Initialize the hash state */
-static void sha256_init(struct sha256_state *md)
+void sha256_init(struct sha256_state *md)
{
md->curlen = 0;
md->length = 0;
@@ -158,32 +142,31 @@ static void sha256_init(struct sha256_state *md)
@param inlen The length of the data (octets)
@return CRYPT_OK if successful
*/
-static int sha256_process(struct sha256_state *md, const unsigned char *in,
- unsigned long inlen)
+int sha256_process(struct sha256_state *md, const unsigned char *in,
+ unsigned long inlen)
{
unsigned long n;
-#define block_size 64
- if (md->curlen > sizeof(md->buf))
+ if (md->curlen >= sizeof(md->buf))
return -1;
while (inlen > 0) {
- if (md->curlen == 0 && inlen >= block_size) {
+ if (md->curlen == 0 && inlen >= SHA256_BLOCK_SIZE) {
if (sha256_compress(md, (unsigned char *) in) < 0)
return -1;
- md->length += block_size * 8;
- in += block_size;
- inlen -= block_size;
+ md->length += SHA256_BLOCK_SIZE * 8;
+ in += SHA256_BLOCK_SIZE;
+ inlen -= SHA256_BLOCK_SIZE;
} else {
- n = MIN(inlen, (block_size - md->curlen));
+ n = MIN(inlen, (SHA256_BLOCK_SIZE - md->curlen));
os_memcpy(md->buf + md->curlen, in, n);
md->curlen += n;
in += n;
inlen -= n;
- if (md->curlen == block_size) {
+ if (md->curlen == SHA256_BLOCK_SIZE) {
if (sha256_compress(md, md->buf) < 0)
return -1;
- md->length += 8 * block_size;
+ md->length += 8 * SHA256_BLOCK_SIZE;
md->curlen = 0;
}
}
@@ -199,7 +182,7 @@ static int sha256_process(struct sha256_state *md, const unsigned char *in,
@param out [out] The destination of the hash (32 bytes)
@return CRYPT_OK if successful
*/
-static int sha256_done(struct sha256_state *md, unsigned char *out)
+int sha256_done(struct sha256_state *md, unsigned char *out)
{
int i;
@@ -217,14 +200,14 @@ static int sha256_done(struct sha256_state *md, unsigned char *out)
* encoding like normal.
*/
if (md->curlen > 56) {
- while (md->curlen < 64) {
+ while (md->curlen < SHA256_BLOCK_SIZE) {
md->buf[md->curlen++] = (unsigned char) 0;
}
sha256_compress(md, md->buf);
md->curlen = 0;
}
- /* pad upto 56 bytes of zeroes */
+ /* pad up to 56 bytes of zeroes */
while (md->curlen < 56) {
md->buf[md->curlen++] = (unsigned char) 0;
}
diff --git a/contrib/wpa/src/crypto/sha256-prf.c b/contrib/wpa/src/crypto/sha256-prf.c
new file mode 100644
index 0000000..0da6d13
--- /dev/null
+++ b/contrib/wpa/src/crypto/sha256-prf.c
@@ -0,0 +1,64 @@
+/*
+ * SHA256-based PRF (IEEE 802.11r)
+ * Copyright (c) 2003-2007, Jouni Malinen <j@w1.fi>
+ *
+ * This software may be distributed under the terms of the BSD license.
+ * See README for more details.
+ */
+
+#include "includes.h"
+
+#include "common.h"
+#include "sha256.h"
+#include "crypto.h"
+
+
+/**
+ * sha256_prf - SHA256-based Pseudo-Random Function (IEEE 802.11r, 8.5.1.5.2)
+ * @key: Key for PRF
+ * @key_len: Length of the key in bytes
+ * @label: A unique label for each purpose of the PRF
+ * @data: Extra data to bind into the key
+ * @data_len: Length of the data
+ * @buf: Buffer for the generated pseudo-random key
+ * @buf_len: Number of bytes of key to generate
+ *
+ * This function is used to derive new, cryptographically separate keys from a
+ * given key.
+ */
+void sha256_prf(const u8 *key, size_t key_len, const char *label,
+ const u8 *data, size_t data_len, u8 *buf, size_t buf_len)
+{
+ u16 counter = 1;
+ size_t pos, plen;
+ u8 hash[SHA256_MAC_LEN];
+ const u8 *addr[4];
+ size_t len[4];
+ u8 counter_le[2], length_le[2];
+
+ addr[0] = counter_le;
+ len[0] = 2;
+ addr[1] = (u8 *) label;
+ len[1] = os_strlen(label);
+ addr[2] = data;
+ len[2] = data_len;
+ addr[3] = length_le;
+ len[3] = sizeof(length_le);
+
+ WPA_PUT_LE16(length_le, buf_len * 8);
+ pos = 0;
+ while (pos < buf_len) {
+ plen = buf_len - pos;
+ WPA_PUT_LE16(counter_le, counter);
+ if (plen >= SHA256_MAC_LEN) {
+ hmac_sha256_vector(key, key_len, 4, addr, len,
+ &buf[pos]);
+ pos += SHA256_MAC_LEN;
+ } else {
+ hmac_sha256_vector(key, key_len, 4, addr, len, hash);
+ os_memcpy(&buf[pos], hash, plen);
+ break;
+ }
+ counter++;
+ }
+}
diff --git a/contrib/wpa/src/crypto/sha256-tlsprf.c b/contrib/wpa/src/crypto/sha256-tlsprf.c
new file mode 100644
index 0000000..0528dad
--- /dev/null
+++ b/contrib/wpa/src/crypto/sha256-tlsprf.c
@@ -0,0 +1,66 @@
+/*
+ * TLS PRF P_SHA256
+ * Copyright (c) 2011, Jouni Malinen <j@w1.fi>
+ *
+ * This software may be distributed under the terms of the BSD license.
+ * See README for more details.
+ */
+
+#include "includes.h"
+
+#include "common.h"
+#include "sha256.h"
+
+
+/**
+ * tls_prf_sha256 - Pseudo-Random Function for TLS v1.2 (P_SHA256, RFC 5246)
+ * @secret: Key for PRF
+ * @secret_len: Length of the key in bytes
+ * @label: A unique label for each purpose of the PRF
+ * @seed: Seed value to bind into the key
+ * @seed_len: Length of the seed
+ * @out: Buffer for the generated pseudo-random key
+ * @outlen: Number of bytes of key to generate
+ * Returns: 0 on success, -1 on failure.
+ *
+ * This function is used to derive new, cryptographically separate keys from a
+ * given key in TLS. This PRF is defined in RFC 2246, Chapter 5.
+ */
+void tls_prf_sha256(const u8 *secret, size_t secret_len, const char *label,
+ const u8 *seed, size_t seed_len, u8 *out, size_t outlen)
+{
+ size_t clen;
+ u8 A[SHA256_MAC_LEN];
+ u8 P[SHA256_MAC_LEN];
+ size_t pos;
+ const unsigned char *addr[3];
+ size_t len[3];
+
+ addr[0] = A;
+ len[0] = SHA256_MAC_LEN;
+ addr[1] = (unsigned char *) label;
+ len[1] = os_strlen(label);
+ addr[2] = seed;
+ len[2] = seed_len;
+
+ /*
+ * RFC 5246, Chapter 5
+ * A(0) = seed, A(i) = HMAC(secret, A(i-1))
+ * P_hash = HMAC(secret, A(1) + seed) + HMAC(secret, A(2) + seed) + ..
+ * PRF(secret, label, seed) = P_SHA256(secret, label + seed)
+ */
+
+ hmac_sha256_vector(secret, secret_len, 2, &addr[1], &len[1], A);
+
+ pos = 0;
+ while (pos < outlen) {
+ hmac_sha256_vector(secret, secret_len, 3, addr, len, P);
+ hmac_sha256(secret, secret_len, A, SHA256_MAC_LEN, A);
+
+ clen = outlen - pos;
+ if (clen > SHA256_MAC_LEN)
+ clen = SHA256_MAC_LEN;
+ os_memcpy(out + pos, P, clen);
+ pos += clen;
+ }
+}
diff --git a/contrib/wpa/src/crypto/sha256.c b/contrib/wpa/src/crypto/sha256.c
index 7f320f9..b55e976 100644
--- a/contrib/wpa/src/crypto/sha256.c
+++ b/contrib/wpa/src/crypto/sha256.c
@@ -1,15 +1,9 @@
/*
* SHA-256 hash implementation and interface functions
- * Copyright (c) 2003-2007, Jouni Malinen <j@w1.fi>
+ * Copyright (c) 2003-2012, Jouni Malinen <j@w1.fi>
*
- * This program is free software; you can redistribute it and/or modify
- * it under the terms of the GNU General Public License version 2 as
- * published by the Free Software Foundation.
- *
- * Alternatively, this software may be distributed under the terms of BSD
- * license.
- *
- * See README and COPYING for more details.
+ * This software may be distributed under the terms of the BSD license.
+ * See README for more details.
*/
#include "includes.h"
@@ -27,9 +21,10 @@
* @addr: Pointers to the data areas
* @len: Lengths of the data blocks
* @mac: Buffer for the hash (32 bytes)
+ * Returns: 0 on success, -1 on failure
*/
-void hmac_sha256_vector(const u8 *key, size_t key_len, size_t num_elem,
- const u8 *addr[], const size_t *len, u8 *mac)
+int hmac_sha256_vector(const u8 *key, size_t key_len, size_t num_elem,
+ const u8 *addr[], const size_t *len, u8 *mac)
{
unsigned char k_pad[64]; /* padding - key XORd with ipad/opad */
unsigned char tk[32];
@@ -41,12 +36,13 @@ void hmac_sha256_vector(const u8 *key, size_t key_len, size_t num_elem,
* Fixed limit on the number of fragments to avoid having to
* allocate memory (which could fail).
*/
- return;
+ return -1;
}
/* if key is longer than 64 bytes reset it to key = SHA256(key) */
if (key_len > 64) {
- sha256_vector(1, &key, &key_len, tk);
+ if (sha256_vector(1, &key, &key_len, tk) < 0)
+ return -1;
key = tk;
key_len = 32;
}
@@ -74,7 +70,8 @@ void hmac_sha256_vector(const u8 *key, size_t key_len, size_t num_elem,
_addr[i + 1] = addr[i];
_len[i + 1] = len[i];
}
- sha256_vector(1 + num_elem, _addr, _len, mac);
+ if (sha256_vector(1 + num_elem, _addr, _len, mac) < 0)
+ return -1;
os_memset(k_pad, 0, sizeof(k_pad));
os_memcpy(k_pad, key, key_len);
@@ -87,7 +84,7 @@ void hmac_sha256_vector(const u8 *key, size_t key_len, size_t num_elem,
_len[0] = 64;
_addr[1] = mac;
_len[1] = SHA256_MAC_LEN;
- sha256_vector(2, _addr, _len, mac);
+ return sha256_vector(2, _addr, _len, mac);
}
@@ -97,61 +94,11 @@ void hmac_sha256_vector(const u8 *key, size_t key_len, size_t num_elem,
* @key_len: Length of the key in bytes
* @data: Pointers to the data area
* @data_len: Length of the data area
- * @mac: Buffer for the hash (20 bytes)
- */
-void hmac_sha256(const u8 *key, size_t key_len, const u8 *data,
- size_t data_len, u8 *mac)
-{
- hmac_sha256_vector(key, key_len, 1, &data, &data_len, mac);
-}
-
-
-/**
- * sha256_prf - SHA256-based Pseudo-Random Function (IEEE 802.11r, 8.5.1.5.2)
- * @key: Key for PRF
- * @key_len: Length of the key in bytes
- * @label: A unique label for each purpose of the PRF
- * @data: Extra data to bind into the key
- * @data_len: Length of the data
- * @buf: Buffer for the generated pseudo-random key
- * @buf_len: Number of bytes of key to generate
- *
- * This function is used to derive new, cryptographically separate keys from a
- * given key.
+ * @mac: Buffer for the hash (32 bytes)
+ * Returns: 0 on success, -1 on failure
*/
-void sha256_prf(const u8 *key, size_t key_len, const char *label,
- const u8 *data, size_t data_len, u8 *buf, size_t buf_len)
+int hmac_sha256(const u8 *key, size_t key_len, const u8 *data,
+ size_t data_len, u8 *mac)
{
- u16 counter = 1;
- size_t pos, plen;
- u8 hash[SHA256_MAC_LEN];
- const u8 *addr[4];
- size_t len[4];
- u8 counter_le[2], length_le[2];
-
- addr[0] = counter_le;
- len[0] = 2;
- addr[1] = (u8 *) label;
- len[1] = os_strlen(label);
- addr[2] = data;
- len[2] = data_len;
- addr[3] = length_le;
- len[3] = sizeof(length_le);
-
- WPA_PUT_LE16(length_le, buf_len * 8);
- pos = 0;
- while (pos < buf_len) {
- plen = buf_len - pos;
- WPA_PUT_LE16(counter_le, counter);
- if (plen >= SHA256_MAC_LEN) {
- hmac_sha256_vector(key, key_len, 4, addr, len,
- &buf[pos]);
- pos += SHA256_MAC_LEN;
- } else {
- hmac_sha256_vector(key, key_len, 4, addr, len, hash);
- os_memcpy(&buf[pos], hash, plen);
- break;
- }
- counter++;
- }
+ return hmac_sha256_vector(key, key_len, 1, &data, &data_len, mac);
}
diff --git a/contrib/wpa/src/crypto/sha256.h b/contrib/wpa/src/crypto/sha256.h
index dc597f0..fcac800 100644
--- a/contrib/wpa/src/crypto/sha256.h
+++ b/contrib/wpa/src/crypto/sha256.h
@@ -1,15 +1,9 @@
/*
* SHA256 hash implementation and interface functions
- * Copyright (c) 2003-2006, Jouni Malinen <j@w1.fi>
+ * Copyright (c) 2003-2011, Jouni Malinen <j@w1.fi>
*
- * This program is free software; you can redistribute it and/or modify
- * it under the terms of the GNU General Public License version 2 as
- * published by the Free Software Foundation.
- *
- * Alternatively, this software may be distributed under the terms of BSD
- * license.
- *
- * See README and COPYING for more details.
+ * This software may be distributed under the terms of the BSD license.
+ * See README for more details.
*/
#ifndef SHA256_H
@@ -17,11 +11,14 @@
#define SHA256_MAC_LEN 32
-void hmac_sha256_vector(const u8 *key, size_t key_len, size_t num_elem,
- const u8 *addr[], const size_t *len, u8 *mac);
-void hmac_sha256(const u8 *key, size_t key_len, const u8 *data,
- size_t data_len, u8 *mac);
+int hmac_sha256_vector(const u8 *key, size_t key_len, size_t num_elem,
+ const u8 *addr[], const size_t *len, u8 *mac);
+int hmac_sha256(const u8 *key, size_t key_len, const u8 *data,
+ size_t data_len, u8 *mac);
void sha256_prf(const u8 *key, size_t key_len, const char *label,
const u8 *data, size_t data_len, u8 *buf, size_t buf_len);
+void tls_prf_sha256(const u8 *secret, size_t secret_len,
+ const char *label, const u8 *seed, size_t seed_len,
+ u8 *out, size_t outlen);
#endif /* SHA256_H */
diff --git a/contrib/wpa/src/crypto/sha256_i.h b/contrib/wpa/src/crypto/sha256_i.h
new file mode 100644
index 0000000..a502d2b
--- /dev/null
+++ b/contrib/wpa/src/crypto/sha256_i.h
@@ -0,0 +1,25 @@
+/*
+ * SHA-256 internal definitions
+ * Copyright (c) 2003-2011, Jouni Malinen <j@w1.fi>
+ *
+ * This software may be distributed under the terms of the BSD license.
+ * See README for more details.
+ */
+
+#ifndef SHA256_I_H
+#define SHA256_I_H
+
+#define SHA256_BLOCK_SIZE 64
+
+struct sha256_state {
+ u64 length;
+ u32 state[8], curlen;
+ u8 buf[SHA256_BLOCK_SIZE];
+};
+
+void sha256_init(struct sha256_state *md);
+int sha256_process(struct sha256_state *md, const unsigned char *in,
+ unsigned long inlen);
+int sha256_done(struct sha256_state *md, unsigned char *out);
+
+#endif /* SHA256_I_H */
diff --git a/contrib/wpa/src/crypto/tls.h b/contrib/wpa/src/crypto/tls.h
index 0928b5b..b61e439 100644
--- a/contrib/wpa/src/crypto/tls.h
+++ b/contrib/wpa/src/crypto/tls.h
@@ -2,14 +2,8 @@
* SSL/TLS interface definition
* Copyright (c) 2004-2010, Jouni Malinen <j@w1.fi>
*
- * This program is free software; you can redistribute it and/or modify
- * it under the terms of the GNU General Public License version 2 as
- * published by the Free Software Foundation.
- *
- * Alternatively, this software may be distributed under the terms of BSD
- * license.
- *
- * See README and COPYING for more details.
+ * This software may be distributed under the terms of the BSD license.
+ * See README for more details.
*/
#ifndef TLS_H
@@ -24,13 +18,13 @@ struct tls_keys {
size_t client_random_len;
const u8 *server_random;
size_t server_random_len;
- const u8 *inner_secret; /* TLS/IA inner secret */
- size_t inner_secret_len;
};
enum tls_event {
+ TLS_CERT_CHAIN_SUCCESS,
TLS_CERT_CHAIN_FAILURE,
- TLS_PEER_CERTIFICATE
+ TLS_PEER_CERTIFICATE,
+ TLS_ALERT
};
/*
@@ -65,6 +59,12 @@ union tls_event_data {
const u8 *hash;
size_t hash_len;
} peer_cert;
+
+ struct {
+ int is_local;
+ const char *type;
+ const char *description;
+ } alert;
};
struct tls_config {
@@ -72,6 +72,7 @@ struct tls_config {
const char *pkcs11_engine_path;
const char *pkcs11_module_path;
int fips_mode;
+ int cert_in_cb;
void (*event_cb)(void *ctx, enum tls_event ev,
union tls_event_data *data);
@@ -80,6 +81,7 @@ struct tls_config {
#define TLS_CONN_ALLOW_SIGN_RSA_MD5 BIT(0)
#define TLS_CONN_DISABLE_TIME_CHECKS BIT(1)
+#define TLS_CONN_DISABLE_SESSION_TICKET BIT(2)
/**
* struct tls_connection_params - Parameters for TLS connection
@@ -114,7 +116,6 @@ struct tls_config {
* specific for now)
* @cert_id: the certificate's id when using engine
* @ca_cert_id: the CA certificate's id when using engine
- * @tls_ia: Whether to enable TLS/IA (for EAP-TTLSv1)
* @flags: Parameter options (TLS_CONN_*)
*
* TLS connection parameters to be configured with tls_connection_set_params()
@@ -142,7 +143,6 @@ struct tls_connection_params {
const char *dh_file;
const u8 *dh_blob;
size_t dh_blob_len;
- int tls_ia;
/* OpenSSL specific variables */
int engine;
@@ -282,20 +282,6 @@ int __must_check tls_connection_set_verify(void *tls_ctx,
int verify_peer);
/**
- * tls_connection_set_ia - Set TLS/IA parameters
- * @tls_ctx: TLS context data from tls_init()
- * @conn: Connection context data from tls_connection_init()
- * @tls_ia: 1 = enable TLS/IA
- * Returns: 0 on success, -1 on failure
- *
- * This function is used to configure TLS/IA in server mode where
- * tls_connection_set_params() is not used.
- */
-int __must_check tls_connection_set_ia(void *tls_ctx,
- struct tls_connection *conn,
- int tls_ia);
-
-/**
* tls_connection_get_keys - Get master key and random data from TLS connection
* @tls_ctx: TLS context data from tls_init()
* @conn: Connection context data from tls_connection_init()
@@ -322,7 +308,7 @@ int __must_check tls_connection_get_keys(void *tls_ctx,
* not exported from the TLS library, tls_connection_prf() is required so that
* further keying material can be derived from the master secret. If not
* implemented, the function will still need to be defined, but it can just
- * return -1. Example implementation of this function is in tls_prf() function
+ * return -1. Example implementation of this function is in tls_prf_sha1_md5()
* when it is called with seed set to client_random|server_random (or
* server_random|client_random).
*/
@@ -364,6 +350,12 @@ struct wpabuf * tls_connection_handshake(void *tls_ctx,
const struct wpabuf *in_data,
struct wpabuf **appl_data);
+struct wpabuf * tls_connection_handshake2(void *tls_ctx,
+ struct tls_connection *conn,
+ const struct wpabuf *in_data,
+ struct wpabuf **appl_data,
+ int *more_data_needed);
+
/**
* tls_connection_server_handshake - Process TLS handshake (server side)
* @tls_ctx: TLS context data from tls_init()
@@ -409,6 +401,11 @@ struct wpabuf * tls_connection_decrypt(void *tls_ctx,
struct tls_connection *conn,
const struct wpabuf *in_data);
+struct wpabuf * tls_connection_decrypt2(void *tls_ctx,
+ struct tls_connection *conn,
+ const struct wpabuf *in_data,
+ int *more_data_needed);
+
/**
* tls_connection_resumed - Was session resumption used
* @tls_ctx: TLS context data from tls_init()
@@ -514,7 +511,6 @@ int tls_connection_get_write_alerts(void *tls_ctx,
int tls_connection_get_keyblock_size(void *tls_ctx,
struct tls_connection *conn);
-#define TLS_CAPABILITY_IA 0x0001 /* TLS Inner Application (TLS/IA) */
/**
* tls_capabilities - Get supported TLS capabilities
* @tls_ctx: TLS context data from tls_init()
@@ -522,42 +518,6 @@ int tls_connection_get_keyblock_size(void *tls_ctx,
*/
unsigned int tls_capabilities(void *tls_ctx);
-/**
- * tls_connection_ia_send_phase_finished - Send a TLS/IA PhaseFinished message
- * @tls_ctx: TLS context data from tls_init()
- * @conn: Connection context data from tls_connection_init()
- * @final: 1 = FinalPhaseFinished, 0 = IntermediatePhaseFinished
- * Returns: Encrypted TLS/IA data, %NULL on failure
- *
- * This function is used to send the TLS/IA end phase message, e.g., when the
- * EAP server completes EAP-TTLSv1.
- */
-struct wpabuf * tls_connection_ia_send_phase_finished(
- void *tls_ctx, struct tls_connection *conn, int final);
-
-/**
- * tls_connection_ia_final_phase_finished - Has final phase been completed
- * @tls_ctx: TLS context data from tls_init()
- * @conn: Connection context data from tls_connection_init()
- * Returns: 1 if valid FinalPhaseFinished has been received, 0 if not, or -1
- * on failure
- */
-int __must_check tls_connection_ia_final_phase_finished(
- void *tls_ctx, struct tls_connection *conn);
-
-/**
- * tls_connection_ia_permute_inner_secret - Permute TLS/IA inner secret
- * @tls_ctx: TLS context data from tls_init()
- * @conn: Connection context data from tls_connection_init()
- * @key: Session key material (session_key vectors with 2-octet length), or
- * %NULL if no session key was generating in the current phase
- * @key_len: Length of session key material
- * Returns: 0 on success, -1 on failure
- */
-int __must_check tls_connection_ia_permute_inner_secret(
- void *tls_ctx, struct tls_connection *conn,
- const u8 *key, size_t key_len);
-
typedef int (*tls_session_ticket_cb)
(void *ctx, const u8 *ticket, size_t len, const u8 *client_random,
const u8 *server_random, u8 *master_secret);
diff --git a/contrib/wpa/src/crypto/tls_gnutls.c b/contrib/wpa/src/crypto/tls_gnutls.c
index c3a7358..a5d72f4 100644
--- a/contrib/wpa/src/crypto/tls_gnutls.c
+++ b/contrib/wpa/src/crypto/tls_gnutls.c
@@ -1,15 +1,9 @@
/*
* SSL/TLS interface functions for GnuTLS
- * Copyright (c) 2004-2009, Jouni Malinen <j@w1.fi>
+ * Copyright (c) 2004-2011, Jouni Malinen <j@w1.fi>
*
- * This program is free software; you can redistribute it and/or modify
- * it under the terms of the GNU General Public License version 2 as
- * published by the Free Software Foundation.
- *
- * Alternatively, this software may be distributed under the terms of BSD
- * license.
- *
- * See README and COPYING for more details.
+ * This software may be distributed under the terms of the BSD license.
+ * See README for more details.
*/
#include "includes.h"
@@ -19,28 +13,12 @@
#include <gnutls/pkcs12.h>
#endif /* PKCS12_FUNCS */
-#ifdef CONFIG_GNUTLS_EXTRA
-#if LIBGNUTLS_VERSION_NUMBER >= 0x010302
-#define GNUTLS_IA
-#include <gnutls/extra.h>
-#if LIBGNUTLS_VERSION_NUMBER == 0x010302
-/* This function is not included in the current gnutls/extra.h even though it
- * should be, so define it here as a workaround for the time being. */
-int gnutls_ia_verify_endphase(gnutls_session_t session, char *checksum);
-#endif /* LIBGNUTLS_VERSION_NUMBER == 0x010302 */
-#endif /* LIBGNUTLS_VERSION_NUMBER >= 0x010302 */
-#endif /* CONFIG_GNUTLS_EXTRA */
-
#include "common.h"
#include "tls.h"
-#ifndef TLS_RANDOM_SIZE
-#define TLS_RANDOM_SIZE 32
-#endif
-#ifndef TLS_MASTER_SIZE
-#define TLS_MASTER_SIZE 48
-#endif
+#define WPA_TLS_RANDOM_SIZE 32
+#define WPA_TLS_MASTER_SIZE 48
#if LIBGNUTLS_VERSION_NUMBER < 0x010302
@@ -77,9 +55,9 @@ typedef struct {
gnutls_mac_algorithm_t write_mac_algorithm;
gnutls_compression_method_t write_compression_algorithm;
cipher_suite_st current_cipher_suite;
- opaque master_secret[TLS_MASTER_SIZE];
- opaque client_random[TLS_RANDOM_SIZE];
- opaque server_random[TLS_RANDOM_SIZE];
+ opaque master_secret[WPA_TLS_MASTER_SIZE];
+ opaque client_random[WPA_TLS_RANDOM_SIZE];
+ opaque server_random[WPA_TLS_RANDOM_SIZE];
/* followed by stuff we are not interested in */
} security_parameters_st;
@@ -118,21 +96,6 @@ struct tls_connection {
int params_set;
gnutls_certificate_credentials_t xcred;
-
- int tls_ia;
- int final_phase_finished;
-
-#ifdef GNUTLS_IA
- gnutls_ia_server_credentials_t iacred_srv;
- gnutls_ia_client_credentials_t iacred_cli;
-
- /* Session keys generated in the current phase for inner secret
- * permutation before generating/verifying PhaseFinished. */
- u8 *session_keys;
- size_t session_keys_len;
-
- u8 inner_secret[TLS_MASTER_SIZE];
-#endif /* GNUTLS_IA */
};
@@ -285,8 +248,12 @@ static ssize_t tls_push_func(gnutls_transport_ptr ptr, const void *buf,
static int tls_gnutls_init_session(struct tls_global *global,
struct tls_connection *conn)
{
+#if LIBGNUTLS_VERSION_NUMBER >= 0x020200
+ const char *err;
+#else /* LIBGNUTLS_VERSION_NUMBER >= 0x020200 */
const int cert_types[2] = { GNUTLS_CRT_X509, 0 };
const int protos[2] = { GNUTLS_TLS1, 0 };
+#endif /* LIBGNUTLS_VERSION_NUMBER < 0x020200 */
int ret;
ret = gnutls_init(&conn->session,
@@ -301,6 +268,15 @@ static int tls_gnutls_init_session(struct tls_global *global,
if (ret < 0)
goto fail;
+#if LIBGNUTLS_VERSION_NUMBER >= 0x020200
+ ret = gnutls_priority_set_direct(conn->session, "NORMAL:-VERS-SSL3.0",
+ &err);
+ if (ret < 0) {
+ wpa_printf(MSG_ERROR, "GnuTLS: Priority string failure at "
+ "'%s'", err);
+ goto fail;
+ }
+#else /* LIBGNUTLS_VERSION_NUMBER >= 0x020200 */
ret = gnutls_certificate_type_set_priority(conn->session, cert_types);
if (ret < 0)
goto fail;
@@ -308,6 +284,7 @@ static int tls_gnutls_init_session(struct tls_global *global,
ret = gnutls_protocol_set_priority(conn->session, protos);
if (ret < 0)
goto fail;
+#endif /* LIBGNUTLS_VERSION_NUMBER < 0x020200 */
gnutls_transport_set_pull_function(conn->session, tls_pull_func);
gnutls_transport_set_push_function(conn->session, tls_push_func);
@@ -364,17 +341,6 @@ void tls_connection_deinit(void *ssl_ctx, struct tls_connection *conn)
if (conn == NULL)
return;
-#ifdef GNUTLS_IA
- if (conn->iacred_srv)
- gnutls_ia_free_server_credentials(conn->iacred_srv);
- if (conn->iacred_cli)
- gnutls_ia_free_client_credentials(conn->iacred_cli);
- if (conn->session_keys) {
- os_memset(conn->session_keys, 0, conn->session_keys_len);
- os_free(conn->session_keys);
- }
-#endif /* GNUTLS_IA */
-
gnutls_certificate_free_credentials(conn->xcred);
gnutls_deinit(conn->session);
os_free(conn->pre_shared_secret);
@@ -407,14 +373,6 @@ int tls_connection_shutdown(void *ssl_ctx, struct tls_connection *conn)
wpabuf_free(conn->push_buf);
conn->push_buf = NULL;
conn->established = 0;
- conn->final_phase_finished = 0;
-#ifdef GNUTLS_IA
- if (conn->session_keys) {
- os_memset(conn->session_keys, 0, conn->session_keys_len);
- os_free(conn->session_keys);
- }
- conn->session_keys_len = 0;
-#endif /* GNUTLS_IA */
gnutls_deinit(conn->session);
if (tls_gnutls_init_session(global, conn)) {
@@ -597,11 +555,13 @@ int tls_connection_set_params(void *tls_ctx, struct tls_connection *conn,
conn->xcred, GNUTLS_VERIFY_ALLOW_SIGN_RSA_MD5);
}
+#if LIBGNUTLS_VERSION_NUMBER >= 0x020800
if (params->flags & TLS_CONN_DISABLE_TIME_CHECKS) {
gnutls_certificate_set_verify_flags(
conn->xcred,
GNUTLS_VERIFY_DISABLE_TIME_CHECKS);
}
+#endif /* LIBGNUTLS_VERSION_NUMBER >= 0x020800 */
}
if (params->client_cert && params->private_key) {
@@ -646,7 +606,6 @@ int tls_connection_set_params(void *tls_ctx, struct tls_connection *conn,
}
}
- conn->tls_ia = params->tls_ia;
conn->params_set = 1;
ret = gnutls_credentials_set(conn->session, GNUTLS_CRD_CERTIFICATE,
@@ -656,28 +615,6 @@ int tls_connection_set_params(void *tls_ctx, struct tls_connection *conn,
gnutls_strerror(ret));
}
-#ifdef GNUTLS_IA
- if (conn->iacred_cli)
- gnutls_ia_free_client_credentials(conn->iacred_cli);
-
- ret = gnutls_ia_allocate_client_credentials(&conn->iacred_cli);
- if (ret) {
- wpa_printf(MSG_DEBUG, "Failed to allocate IA credentials: %s",
- gnutls_strerror(ret));
- return -1;
- }
-
- ret = gnutls_credentials_set(conn->session, GNUTLS_CRD_IA,
- conn->iacred_cli);
- if (ret) {
- wpa_printf(MSG_DEBUG, "Failed to configure IA credentials: %s",
- gnutls_strerror(ret));
- gnutls_ia_free_client_credentials(conn->iacred_cli);
- conn->iacred_cli = NULL;
- return -1;
- }
-#endif /* GNUTLS_IE */
-
return ret;
}
@@ -729,11 +666,13 @@ int tls_global_set_params(void *tls_ctx,
GNUTLS_VERIFY_ALLOW_SIGN_RSA_MD5);
}
+#if LIBGNUTLS_VERSION_NUMBER >= 0x020800
if (params->flags & TLS_CONN_DISABLE_TIME_CHECKS) {
gnutls_certificate_set_verify_flags(
global->xcred,
GNUTLS_VERIFY_DISABLE_TIME_CHECKS);
}
+#endif /* LIBGNUTLS_VERSION_NUMBER >= 0x020800 */
}
if (params->client_cert && params->private_key) {
@@ -822,10 +761,11 @@ int tls_connection_get_keys(void *ssl_ctx, struct tls_connection *conn,
os_memset(keys, 0, sizeof(*keys));
+#if LIBGNUTLS_VERSION_NUMBER < 0x020c00
#ifdef GNUTLS_INTERNAL_STRUCTURE_HACK
sec = &conn->session->security_parameters;
keys->master_key = sec->master_secret;
- keys->master_key_len = TLS_MASTER_SIZE;
+ keys->master_key_len = WPA_TLS_MASTER_SIZE;
keys->client_random = sec->client_random;
keys->server_random = sec->server_random;
#else /* GNUTLS_INTERNAL_STRUCTURE_HACK */
@@ -835,16 +775,12 @@ int tls_connection_get_keys(void *ssl_ctx, struct tls_connection *conn,
(u8 *) gnutls_session_get_server_random(conn->session);
/* No access to master_secret */
#endif /* GNUTLS_INTERNAL_STRUCTURE_HACK */
+#endif /* LIBGNUTLS_VERSION_NUMBER < 0x020c00 */
-#ifdef GNUTLS_IA
- gnutls_ia_extract_inner_secret(conn->session,
- (char *) conn->inner_secret);
- keys->inner_secret = conn->inner_secret;
- keys->inner_secret_len = TLS_MASTER_SIZE;
-#endif /* GNUTLS_IA */
-
- keys->client_random_len = TLS_RANDOM_SIZE;
- keys->server_random_len = TLS_RANDOM_SIZE;
+#if LIBGNUTLS_VERSION_NUMBER < 0x020c00
+ keys->client_random_len = WPA_TLS_RANDOM_SIZE;
+ keys->server_random_len = WPA_TLS_RANDOM_SIZE;
+#endif /* LIBGNUTLS_VERSION_NUMBER < 0x020c00 */
return 0;
}
@@ -883,11 +819,13 @@ static int tls_connection_verify_peer(struct tls_connection *conn,
if (conn->verify_peer && (status & GNUTLS_CERT_INVALID)) {
wpa_printf(MSG_INFO, "TLS: Peer certificate not trusted");
+ *err = GNUTLS_A_INTERNAL_ERROR;
if (status & GNUTLS_CERT_INSECURE_ALGORITHM) {
wpa_printf(MSG_INFO, "TLS: Certificate uses insecure "
"algorithm");
*err = GNUTLS_A_INSUFFICIENT_SECURITY;
}
+#if LIBGNUTLS_VERSION_NUMBER >= 0x020800
if (status & GNUTLS_CERT_NOT_ACTIVATED) {
wpa_printf(MSG_INFO, "TLS: Certificate not yet "
"activated");
@@ -897,6 +835,7 @@ static int tls_connection_verify_peer(struct tls_connection *conn,
wpa_printf(MSG_INFO, "TLS: Certificate expired");
*err = GNUTLS_A_CERTIFICATE_EXPIRED;
}
+#endif /* LIBGNUTLS_VERSION_NUMBER >= 0x020800 */
return -1;
}
@@ -988,7 +927,7 @@ static struct wpabuf * gnutls_get_appl_data(struct tls_connection *conn)
wpabuf_size(ad));
wpa_printf(MSG_DEBUG, "GnuTLS: gnutls_record_recv: %d", res);
if (res < 0) {
- wpa_printf(MSG_DEBUG, "%s - gnutls_ia_recv failed: %d "
+ wpa_printf(MSG_DEBUG, "%s - gnutls_record_recv failed: %d "
"(%s)", __func__, (int) res,
gnutls_strerror(res));
wpabuf_free(ad);
@@ -1062,20 +1001,7 @@ struct wpabuf * tls_connection_handshake(void *tls_ctx,
goto out;
}
-#ifdef CONFIG_GNUTLS_EXTRA
- if (conn->tls_ia && !gnutls_ia_handshake_p(conn->session)) {
- wpa_printf(MSG_INFO, "TLS: No TLS/IA negotiation");
- conn->failed++;
- return NULL;
- }
-#endif /* CONFIG_GNUTLS_EXTRA */
-
- if (conn->tls_ia)
- wpa_printf(MSG_DEBUG, "TLS: Start TLS/IA handshake");
- else {
- wpa_printf(MSG_DEBUG, "TLS: Handshake completed "
- "successfully");
- }
+ wpa_printf(MSG_DEBUG, "TLS: Handshake completed successfully");
conn->established = 1;
if (conn->push_buf == NULL) {
/* Need to return something to get final TLS ACK. */
@@ -1122,12 +1048,6 @@ struct wpabuf * tls_connection_encrypt(void *tls_ctx,
ssize_t res;
struct wpabuf *buf;
-#ifdef GNUTLS_IA
- if (conn->tls_ia)
- res = gnutls_ia_send(conn->session, wpabuf_head(in_data),
- wpabuf_len(in_data));
- else
-#endif /* GNUTLS_IA */
res = gnutls_record_send(conn->session, wpabuf_head(in_data),
wpabuf_len(in_data));
if (res < 0) {
@@ -1170,65 +1090,6 @@ struct wpabuf * tls_connection_decrypt(void *tls_ctx,
if (out == NULL)
return NULL;
-#ifdef GNUTLS_IA
- if (conn->tls_ia) {
- res = gnutls_ia_recv(conn->session, wpabuf_mhead(out),
- wpabuf_size(out));
- if (res == GNUTLS_E_WARNING_IA_IPHF_RECEIVED ||
- res == GNUTLS_E_WARNING_IA_FPHF_RECEIVED) {
- int final = res == GNUTLS_E_WARNING_IA_FPHF_RECEIVED;
- wpa_printf(MSG_DEBUG, "%s: Received %sPhaseFinished",
- __func__, final ? "Final" : "Intermediate");
-
- res = gnutls_ia_permute_inner_secret(
- conn->session, conn->session_keys_len,
- (char *) conn->session_keys);
- if (conn->session_keys) {
- os_memset(conn->session_keys, 0,
- conn->session_keys_len);
- os_free(conn->session_keys);
- }
- conn->session_keys = NULL;
- conn->session_keys_len = 0;
- if (res) {
- wpa_printf(MSG_DEBUG, "%s: Failed to permute "
- "inner secret: %s",
- __func__, gnutls_strerror(res));
- wpabuf_free(out);
- return NULL;
- }
-
- res = gnutls_ia_verify_endphase(conn->session,
- wpabuf_head(out));
- if (res == 0) {
- wpa_printf(MSG_DEBUG, "%s: Correct endphase "
- "checksum", __func__);
- } else {
- wpa_printf(MSG_INFO, "%s: Endphase "
- "verification failed: %s",
- __func__, gnutls_strerror(res));
- wpabuf_free(out);
- return NULL;
- }
-
- if (final)
- conn->final_phase_finished = 1;
-
- return out;
- }
-
- if (res < 0) {
- wpa_printf(MSG_DEBUG, "%s - gnutls_ia_recv failed: %d "
- "(%s)", __func__, (int) res,
- gnutls_strerror(res));
- wpabuf_free(out);
- return NULL;
- }
- wpabuf_put(out, res);
- return out;
- }
-#endif /* GNUTLS_IA */
-
res = gnutls_record_recv(conn->session, wpabuf_mhead(out),
wpabuf_size(out));
if (res < 0) {
@@ -1319,133 +1180,7 @@ int tls_connection_get_keyblock_size(void *tls_ctx,
unsigned int tls_capabilities(void *tls_ctx)
{
- unsigned int capa = 0;
-
-#ifdef GNUTLS_IA
- capa |= TLS_CAPABILITY_IA;
-#endif /* GNUTLS_IA */
-
- return capa;
-}
-
-
-int tls_connection_set_ia(void *tls_ctx, struct tls_connection *conn,
- int tls_ia)
-{
-#ifdef GNUTLS_IA
- int ret;
-
- if (conn == NULL)
- return -1;
-
- conn->tls_ia = tls_ia;
- if (!tls_ia)
- return 0;
-
- ret = gnutls_ia_allocate_server_credentials(&conn->iacred_srv);
- if (ret) {
- wpa_printf(MSG_DEBUG, "Failed to allocate IA credentials: %s",
- gnutls_strerror(ret));
- return -1;
- }
-
- ret = gnutls_credentials_set(conn->session, GNUTLS_CRD_IA,
- conn->iacred_srv);
- if (ret) {
- wpa_printf(MSG_DEBUG, "Failed to configure IA credentials: %s",
- gnutls_strerror(ret));
- gnutls_ia_free_server_credentials(conn->iacred_srv);
- conn->iacred_srv = NULL;
- return -1;
- }
-
- return 0;
-#else /* GNUTLS_IA */
- return -1;
-#endif /* GNUTLS_IA */
-}
-
-
-struct wpabuf * tls_connection_ia_send_phase_finished(
- void *tls_ctx, struct tls_connection *conn, int final)
-{
-#ifdef GNUTLS_IA
- int ret;
- struct wpabuf *buf;
-
- if (conn == NULL || conn->session == NULL || !conn->tls_ia)
- return NULL;
-
- ret = gnutls_ia_permute_inner_secret(conn->session,
- conn->session_keys_len,
- (char *) conn->session_keys);
- if (conn->session_keys) {
- os_memset(conn->session_keys, 0, conn->session_keys_len);
- os_free(conn->session_keys);
- }
- conn->session_keys = NULL;
- conn->session_keys_len = 0;
- if (ret) {
- wpa_printf(MSG_DEBUG, "%s: Failed to permute inner secret: %s",
- __func__, gnutls_strerror(ret));
- return NULL;
- }
-
- ret = gnutls_ia_endphase_send(conn->session, final);
- if (ret) {
- wpa_printf(MSG_DEBUG, "%s: Failed to send endphase: %s",
- __func__, gnutls_strerror(ret));
- return NULL;
- }
-
- buf = conn->push_buf;
- conn->push_buf = NULL;
- return buf;
-#else /* GNUTLS_IA */
- return NULL;
-#endif /* GNUTLS_IA */
-}
-
-
-int tls_connection_ia_final_phase_finished(void *tls_ctx,
- struct tls_connection *conn)
-{
- if (conn == NULL)
- return -1;
-
- return conn->final_phase_finished;
-}
-
-
-int tls_connection_ia_permute_inner_secret(void *tls_ctx,
- struct tls_connection *conn,
- const u8 *key, size_t key_len)
-{
-#ifdef GNUTLS_IA
- if (conn == NULL || !conn->tls_ia)
- return -1;
-
- if (conn->session_keys) {
- os_memset(conn->session_keys, 0, conn->session_keys_len);
- os_free(conn->session_keys);
- }
- conn->session_keys_len = 0;
-
- if (key) {
- conn->session_keys = os_malloc(key_len);
- if (conn->session_keys == NULL)
- return -1;
- os_memcpy(conn->session_keys, key, key_len);
- conn->session_keys_len = key_len;
- } else {
- conn->session_keys = NULL;
- conn->session_keys_len = 0;
- }
-
return 0;
-#else /* GNUTLS_IA */
- return -1;
-#endif /* GNUTLS_IA */
}
diff --git a/contrib/wpa/src/crypto/tls_internal.c b/contrib/wpa/src/crypto/tls_internal.c
index 64124d8..91f0690 100644
--- a/contrib/wpa/src/crypto/tls_internal.c
+++ b/contrib/wpa/src/crypto/tls_internal.c
@@ -1,15 +1,9 @@
/*
* TLS interface functions and an internal TLS implementation
- * Copyright (c) 2004-2009, Jouni Malinen <j@w1.fi>
+ * Copyright (c) 2004-2011, Jouni Malinen <j@w1.fi>
*
- * This program is free software; you can redistribute it and/or modify
- * it under the terms of the GNU General Public License version 2 as
- * published by the Free Software Foundation.
- *
- * Alternatively, this software may be distributed under the terms of BSD
- * license.
- *
- * See README and COPYING for more details.
+ * This software may be distributed under the terms of the BSD license.
+ * See README for more details.
*
* This file interface functions for hostapd/wpa_supplicant to use the
* integrated TLSv1 implementation.
@@ -211,6 +205,9 @@ int tls_connection_set_params(void *tls_ctx, struct tls_connection *conn,
return -1;
}
+ tlsv1_client_set_time_checks(
+ conn->client, !(params->flags & TLS_CONN_DISABLE_TIME_CHECKS));
+
return 0;
#else /* CONFIG_TLS_INTERNAL_CLIENT */
return -1;
@@ -287,13 +284,6 @@ int tls_connection_set_verify(void *tls_ctx, struct tls_connection *conn,
}
-int tls_connection_set_ia(void *tls_ctx, struct tls_connection *conn,
- int tls_ia)
-{
- return -1;
-}
-
-
int tls_connection_get_keys(void *tls_ctx, struct tls_connection *conn,
struct tls_keys *keys)
{
@@ -336,6 +326,17 @@ struct wpabuf * tls_connection_handshake(void *tls_ctx,
const struct wpabuf *in_data,
struct wpabuf **appl_data)
{
+ return tls_connection_handshake2(tls_ctx, conn, in_data, appl_data,
+ NULL);
+}
+
+
+struct wpabuf * tls_connection_handshake2(void *tls_ctx,
+ struct tls_connection *conn,
+ const struct wpabuf *in_data,
+ struct wpabuf **appl_data,
+ int *need_more_data)
+{
#ifdef CONFIG_TLS_INTERNAL_CLIENT
u8 *res, *ad;
size_t res_len, ad_len;
@@ -348,7 +349,7 @@ struct wpabuf * tls_connection_handshake(void *tls_ctx,
res = tlsv1_client_handshake(conn->client,
in_data ? wpabuf_head(in_data) : NULL,
in_data ? wpabuf_len(in_data) : 0,
- &res_len, &ad, &ad_len);
+ &res_len, &ad, &ad_len, need_more_data);
if (res == NULL)
return NULL;
out = wpabuf_alloc_ext_data(res, res_len);
@@ -459,23 +460,23 @@ struct wpabuf * tls_connection_decrypt(void *tls_ctx,
struct tls_connection *conn,
const struct wpabuf *in_data)
{
+ return tls_connection_decrypt2(tls_ctx, conn, in_data, NULL);
+}
+
+
+struct wpabuf * tls_connection_decrypt2(void *tls_ctx,
+ struct tls_connection *conn,
+ const struct wpabuf *in_data,
+ int *need_more_data)
+{
+ if (need_more_data)
+ *need_more_data = 0;
+
#ifdef CONFIG_TLS_INTERNAL_CLIENT
if (conn->client) {
- struct wpabuf *buf;
- int res;
- buf = wpabuf_alloc((wpabuf_len(in_data) + 500) * 3);
- if (buf == NULL)
- return NULL;
- res = tlsv1_client_decrypt(conn->client, wpabuf_head(in_data),
- wpabuf_len(in_data),
- wpabuf_mhead(buf),
- wpabuf_size(buf));
- if (res < 0) {
- wpabuf_free(buf);
- return NULL;
- }
- wpabuf_put(buf, res);
- return buf;
+ return tlsv1_client_decrypt(conn->client, wpabuf_head(in_data),
+ wpabuf_len(in_data),
+ need_more_data);
}
#endif /* CONFIG_TLS_INTERNAL_CLIENT */
#ifdef CONFIG_TLS_INTERNAL_SERVER
@@ -608,28 +609,6 @@ unsigned int tls_capabilities(void *tls_ctx)
}
-struct wpabuf * tls_connection_ia_send_phase_finished(
- void *tls_ctx, struct tls_connection *conn, int final)
-{
- return NULL;
-}
-
-
-int tls_connection_ia_final_phase_finished(void *tls_ctx,
- struct tls_connection *conn)
-{
- return -1;
-}
-
-
-int tls_connection_ia_permute_inner_secret(void *tls_ctx,
- struct tls_connection *conn,
- const u8 *key, size_t key_len)
-{
- return -1;
-}
-
-
int tls_connection_set_session_ticket_cb(void *tls_ctx,
struct tls_connection *conn,
tls_session_ticket_cb cb,
diff --git a/contrib/wpa/src/crypto/tls_none.c b/contrib/wpa/src/crypto/tls_none.c
index 0c836bb..1a1092a 100644
--- a/contrib/wpa/src/crypto/tls_none.c
+++ b/contrib/wpa/src/crypto/tls_none.c
@@ -2,14 +2,8 @@
* SSL/TLS interface functions for no TLS case
* Copyright (c) 2004-2009, Jouni Malinen <j@w1.fi>
*
- * This program is free software; you can redistribute it and/or modify
- * it under the terms of the GNU General Public License version 2 as
- * published by the Free Software Foundation.
- *
- * Alternatively, this software may be distributed under the terms of BSD
- * license.
- *
- * See README and COPYING for more details.
+ * This software may be distributed under the terms of the BSD license.
+ * See README for more details.
*/
#include "includes.h"
@@ -84,13 +78,6 @@ int tls_connection_set_verify(void *tls_ctx, struct tls_connection *conn,
}
-int tls_connection_set_ia(void *tls_ctx, struct tls_connection *conn,
- int tls_ia)
-{
- return -1;
-}
-
-
int tls_connection_get_keys(void *tls_ctx, struct tls_connection *conn,
struct tls_keys *keys)
{
@@ -205,25 +192,3 @@ unsigned int tls_capabilities(void *tls_ctx)
{
return 0;
}
-
-
-struct wpabuf * tls_connection_ia_send_phase_finished(
- void *tls_ctx, struct tls_connection *conn, int final)
-{
- return NULL;
-}
-
-
-int tls_connection_ia_final_phase_finished(void *tls_ctx,
- struct tls_connection *conn)
-{
- return -1;
-}
-
-
-int tls_connection_ia_permute_inner_secret(void *tls_ctx,
- struct tls_connection *conn,
- const u8 *key, size_t key_len)
-{
- return -1;
-}
diff --git a/contrib/wpa/src/crypto/tls_nss.c b/contrib/wpa/src/crypto/tls_nss.c
index ad834b6..c53c192 100644
--- a/contrib/wpa/src/crypto/tls_nss.c
+++ b/contrib/wpa/src/crypto/tls_nss.c
@@ -2,14 +2,8 @@
* SSL/TLS interface functions for NSS
* Copyright (c) 2009, Jouni Malinen <j@w1.fi>
*
- * This program is free software; you can redistribute it and/or modify
- * it under the terms of the GNU General Public License version 2 as
- * published by the Free Software Foundation.
- *
- * Alternatively, this software may be distributed under the terms of BSD
- * license.
- *
- * See README and COPYING for more details.
+ * This software may be distributed under the terms of the BSD license.
+ * See README for more details.
*/
#include "includes.h"
@@ -419,13 +413,6 @@ int tls_connection_set_verify(void *tls_ctx, struct tls_connection *conn,
}
-int tls_connection_set_ia(void *tls_ctx, struct tls_connection *conn,
- int tls_ia)
-{
- return -1;
-}
-
-
int tls_connection_get_keys(void *tls_ctx, struct tls_connection *conn,
struct tls_keys *keys)
{
@@ -649,28 +636,6 @@ unsigned int tls_capabilities(void *tls_ctx)
}
-struct wpabuf * tls_connection_ia_send_phase_finished(
- void *tls_ctx, struct tls_connection *conn, int final)
-{
- return NULL;
-}
-
-
-int tls_connection_ia_final_phase_finished(void *tls_ctx,
- struct tls_connection *conn)
-{
- return -1;
-}
-
-
-int tls_connection_ia_permute_inner_secret(void *tls_ctx,
- struct tls_connection *conn,
- const u8 *key, size_t key_len)
-{
- return -1;
-}
-
-
int tls_connection_set_session_ticket_cb(void *tls_ctx,
struct tls_connection *conn,
tls_session_ticket_cb cb,
diff --git a/contrib/wpa/src/crypto/tls_openssl.c b/contrib/wpa/src/crypto/tls_openssl.c
index c0a40f9..2c3db47 100644
--- a/contrib/wpa/src/crypto/tls_openssl.c
+++ b/contrib/wpa/src/crypto/tls_openssl.c
@@ -1,15 +1,9 @@
/*
* SSL/TLS interface functions for OpenSSL
- * Copyright (c) 2004-2010, Jouni Malinen <j@w1.fi>
+ * Copyright (c) 2004-2011, Jouni Malinen <j@w1.fi>
*
- * This program is free software; you can redistribute it and/or modify
- * it under the terms of the GNU General Public License version 2 as
- * published by the Free Software Foundation.
- *
- * Alternatively, this software may be distributed under the terms of BSD
- * license.
- *
- * See README and COPYING for more details.
+ * This software may be distributed under the terms of the BSD license.
+ * See README for more details.
*/
#include "includes.h"
@@ -28,6 +22,11 @@
#include <openssl/engine.h>
#endif /* OPENSSL_NO_ENGINE */
+#ifdef ANDROID
+#include <openssl/pem.h>
+#include "keystore_get.h"
+#endif /* ANDROID */
+
#include "common.h"
#include "crypto.h"
#include "tls.h"
@@ -54,6 +53,7 @@ struct tls_global {
void (*event_cb)(void *ctx, enum tls_event ev,
union tls_event_data *data);
void *cb_ctx;
+ int cert_in_cb;
};
static struct tls_global *tls_global = NULL;
@@ -81,6 +81,8 @@ struct tls_connection {
unsigned int server_cert_only:1;
u8 srv_cert_hash[32];
+
+ unsigned int flags;
};
@@ -523,6 +525,15 @@ static void ssl_info_cb(const SSL *ssl, int where, int ret)
else
conn->write_alerts++;
}
+ if (tls_global->event_cb != NULL) {
+ union tls_event_data ev;
+ os_memset(&ev, 0, sizeof(ev));
+ ev.alert.is_local = !(where & SSL_CB_READ);
+ ev.alert.type = SSL_alert_type_string_long(ret);
+ ev.alert.description = SSL_alert_desc_string_long(ret);
+ tls_global->event_cb(tls_global->cb_ctx, TLS_ALERT,
+ &ev);
+ }
} else if (where & SSL_CB_EXIT && ret <= 0) {
wpa_printf(MSG_DEBUG, "SSL: %s:%s in %s",
str, ret == 0 ? "failed" : "error",
@@ -687,6 +698,7 @@ void * tls_init(const struct tls_config *conf)
if (conf) {
tls_global->event_cb = conf->event_cb;
tls_global->cb_ctx = conf->cb_ctx;
+ tls_global->cert_in_cb = conf->cert_in_cb;
}
#ifdef CONFIG_FIPS
@@ -697,6 +709,8 @@ void * tls_init(const struct tls_config *conf)
"mode");
ERR_load_crypto_strings();
ERR_print_errors_fp(stderr);
+ os_free(tls_global);
+ tls_global = NULL;
return NULL;
} else
wpa_printf(MSG_INFO, "Running in FIPS mode");
@@ -705,13 +719,15 @@ void * tls_init(const struct tls_config *conf)
if (conf && conf->fips_mode) {
wpa_printf(MSG_ERROR, "FIPS mode requested, but not "
"supported");
+ os_free(tls_global);
+ tls_global = NULL;
return NULL;
}
#endif /* OPENSSL_FIPS */
#endif /* CONFIG_FIPS */
SSL_load_error_strings();
SSL_library_init();
-#ifndef OPENSSL_NO_SHA256
+#if (OPENSSL_VERSION_NUMBER >= 0x0090800fL) && !defined(OPENSSL_NO_SHA256)
EVP_add_digest(EVP_sha256());
#endif /* OPENSSL_NO_SHA256 */
/* TODO: if /dev/urandom is available, PRNG is seeded
@@ -1137,7 +1153,7 @@ static void openssl_tls_cert_event(struct tls_connection *conn,
return;
os_memset(&ev, 0, sizeof(ev));
- if (conn->cert_probe) {
+ if (conn->cert_probe || tls_global->cert_in_cb) {
cert = get_x509_cert(err_cert);
ev.peer_cert.cert = cert;
}
@@ -1178,13 +1194,22 @@ static int tls_verify_cb(int preverify_ok, X509_STORE_CTX *x509_ctx)
X509_NAME_oneline(X509_get_subject_name(err_cert), buf, sizeof(buf));
conn = SSL_get_app_data(ssl);
- match = conn ? conn->subject_match : NULL;
- altmatch = conn ? conn->altsubject_match : NULL;
+ if (conn == NULL)
+ return 0;
+ match = conn->subject_match;
+ altmatch = conn->altsubject_match;
if (!preverify_ok && !conn->ca_cert_verify)
preverify_ok = 1;
if (!preverify_ok && depth > 0 && conn->server_cert_only)
preverify_ok = 1;
+ if (!preverify_ok && (conn->flags & TLS_CONN_DISABLE_TIME_CHECKS) &&
+ (err == X509_V_ERR_CERT_HAS_EXPIRED ||
+ err == X509_V_ERR_CERT_NOT_YET_VALID)) {
+ wpa_printf(MSG_DEBUG, "OpenSSL: Ignore certificate validity "
+ "time mismatch");
+ preverify_ok = 1;
+ }
err_str = X509_verify_cert_error_string(err);
@@ -1253,6 +1278,10 @@ static int tls_verify_cb(int preverify_ok, X509_STORE_CTX *x509_ctx)
TLS_FAIL_SERVER_CHAIN_PROBE);
}
+ if (preverify_ok && tls_global->event_cb != NULL)
+ tls_global->event_cb(tls_global->cb_ctx,
+ TLS_CERT_CHAIN_SUCCESS, NULL);
+
return preverify_ok;
}
@@ -1290,6 +1319,19 @@ static int tls_load_ca_der(void *_ssl_ctx, const char *ca_cert)
#endif /* OPENSSL_NO_STDIO */
+#ifdef ANDROID
+static BIO * BIO_from_keystore(const char *key)
+{
+ BIO *bio = NULL;
+ char value[KEYSTORE_MESSAGE_SIZE];
+ int length = keystore_get(key, strlen(key), value);
+ if (length != -1 && (bio = BIO_new(BIO_s_mem())) != NULL)
+ BIO_write(bio, value, length);
+ return bio;
+}
+#endif /* ANDROID */
+
+
static int tls_connection_ca_cert(void *_ssl_ctx, struct tls_connection *conn,
const char *ca_cert, const u8 *ca_cert_blob,
size_t ca_cert_blob_len, const char *ca_path)
@@ -1380,6 +1422,36 @@ static int tls_connection_ca_cert(void *_ssl_ctx, struct tls_connection *conn,
return 0;
}
+#ifdef ANDROID
+ if (ca_cert && os_strncmp("keystore://", ca_cert, 11) == 0) {
+ BIO *bio = BIO_from_keystore(&ca_cert[11]);
+ STACK_OF(X509_INFO) *stack = NULL;
+ int i;
+
+ if (bio) {
+ stack = PEM_X509_INFO_read_bio(bio, NULL, NULL, NULL);
+ BIO_free(bio);
+ }
+ if (!stack)
+ return -1;
+
+ for (i = 0; i < sk_X509_INFO_num(stack); ++i) {
+ X509_INFO *info = sk_X509_INFO_value(stack, i);
+ if (info->x509) {
+ X509_STORE_add_cert(ssl_ctx->cert_store,
+ info->x509);
+ }
+ if (info->crl) {
+ X509_STORE_add_crl(ssl_ctx->cert_store,
+ info->crl);
+ }
+ }
+ sk_X509_INFO_pop_free(stack, X509_INFO_free);
+ SSL_set_verify(conn->ssl, SSL_VERIFY_PEER, tls_verify_cb);
+ return 0;
+ }
+#endif /* ANDROID */
+
#ifdef CONFIG_NATIVE_WINDOWS
if (ca_cert && tls_cryptoapi_ca_cert(ssl_ctx, conn->ssl, ca_cert) ==
0) {
@@ -1550,26 +1622,42 @@ static int tls_connection_client_cert(struct tls_connection *conn,
if (client_cert == NULL)
return -1;
+#ifdef ANDROID
+ if (os_strncmp("keystore://", client_cert, 11) == 0) {
+ BIO *bio = BIO_from_keystore(&client_cert[11]);
+ X509 *x509 = NULL;
+ int ret = -1;
+ if (bio) {
+ x509 = PEM_read_bio_X509(bio, NULL, NULL, NULL);
+ BIO_free(bio);
+ }
+ if (x509) {
+ if (SSL_use_certificate(conn->ssl, x509) == 1)
+ ret = 0;
+ X509_free(x509);
+ }
+ return ret;
+ }
+#endif /* ANDROID */
+
#ifndef OPENSSL_NO_STDIO
if (SSL_use_certificate_file(conn->ssl, client_cert,
SSL_FILETYPE_ASN1) == 1) {
wpa_printf(MSG_DEBUG, "OpenSSL: SSL_use_certificate_file (DER)"
" --> OK");
return 0;
- } else {
- tls_show_errors(MSG_DEBUG, __func__,
- "SSL_use_certificate_file (DER) failed");
}
if (SSL_use_certificate_file(conn->ssl, client_cert,
SSL_FILETYPE_PEM) == 1) {
+ ERR_clear_error();
wpa_printf(MSG_DEBUG, "OpenSSL: SSL_use_certificate_file (PEM)"
" --> OK");
return 0;
- } else {
- tls_show_errors(MSG_DEBUG, __func__,
- "SSL_use_certificate_file (PEM) failed");
}
+
+ tls_show_errors(MSG_DEBUG, __func__,
+ "SSL_use_certificate_file failed");
#else /* OPENSSL_NO_STDIO */
wpa_printf(MSG_DEBUG, "OpenSSL: %s - OPENSSL_NO_STDIO", __func__);
#endif /* OPENSSL_NO_STDIO */
@@ -1586,6 +1674,7 @@ static int tls_global_client_cert(SSL_CTX *ssl_ctx, const char *client_cert)
if (SSL_CTX_use_certificate_file(ssl_ctx, client_cert,
SSL_FILETYPE_ASN1) != 1 &&
+ SSL_CTX_use_certificate_chain_file(ssl_ctx, client_cert) != 1 &&
SSL_CTX_use_certificate_file(ssl_ctx, client_cert,
SSL_FILETYPE_PEM) != 1) {
tls_show_errors(MSG_INFO, __func__,
@@ -1837,6 +1926,8 @@ static int tls_connection_engine_ca_cert(void *_ssl_ctx,
wpa_printf(MSG_DEBUG, "OpenSSL: %s - added CA certificate from engine "
"to certificate store", __func__);
SSL_set_verify(conn->ssl, SSL_VERIFY_PEER, tls_verify_cb);
+ conn->ca_cert_verify = 1;
+
return 0;
#else /* OPENSSL_NO_ENGINE */
@@ -1900,10 +1991,6 @@ static int tls_connection_private_key(void *_ssl_ctx,
"ASN1(EVP_PKEY_RSA) --> OK");
ok = 1;
break;
- } else {
- tls_show_errors(MSG_DEBUG, __func__,
- "SSL_use_PrivateKey_ASN1(EVP_PKEY_RSA)"
- " failed");
}
if (SSL_use_PrivateKey_ASN1(EVP_PKEY_DSA, conn->ssl,
@@ -1913,10 +2000,6 @@ static int tls_connection_private_key(void *_ssl_ctx,
"ASN1(EVP_PKEY_DSA) --> OK");
ok = 1;
break;
- } else {
- tls_show_errors(MSG_DEBUG, __func__,
- "SSL_use_PrivateKey_ASN1(EVP_PKEY_DSA)"
- " failed");
}
if (SSL_use_RSAPrivateKey_ASN1(conn->ssl,
@@ -1926,9 +2009,6 @@ static int tls_connection_private_key(void *_ssl_ctx,
"SSL_use_RSAPrivateKey_ASN1 --> OK");
ok = 1;
break;
- } else {
- tls_show_errors(MSG_DEBUG, __func__,
- "SSL_use_RSAPrivateKey_ASN1 failed");
}
if (tls_read_pkcs12_blob(ssl_ctx, conn->ssl, private_key_blob,
@@ -1942,6 +2022,26 @@ static int tls_connection_private_key(void *_ssl_ctx,
break;
}
+#ifdef ANDROID
+ if (!ok && private_key &&
+ os_strncmp("keystore://", private_key, 11) == 0) {
+ BIO *bio = BIO_from_keystore(&private_key[11]);
+ EVP_PKEY *pkey = NULL;
+ if (bio) {
+ pkey = PEM_read_bio_PrivateKey(bio, NULL, NULL, NULL);
+ BIO_free(bio);
+ }
+ if (pkey) {
+ if (SSL_use_PrivateKey(conn->ssl, pkey) == 1) {
+ wpa_printf(MSG_DEBUG, "OpenSSL: Private key "
+ "from keystore");
+ ok = 1;
+ }
+ EVP_PKEY_free(pkey);
+ }
+ }
+#endif /* ANDROID */
+
while (!ok && private_key) {
#ifndef OPENSSL_NO_STDIO
if (SSL_use_PrivateKey_file(conn->ssl, private_key,
@@ -1950,10 +2050,6 @@ static int tls_connection_private_key(void *_ssl_ctx,
"SSL_use_PrivateKey_File (DER) --> OK");
ok = 1;
break;
- } else {
- tls_show_errors(MSG_DEBUG, __func__,
- "SSL_use_PrivateKey_File (DER) "
- "failed");
}
if (SSL_use_PrivateKey_file(conn->ssl, private_key,
@@ -1962,10 +2058,6 @@ static int tls_connection_private_key(void *_ssl_ctx,
"SSL_use_PrivateKey_File (PEM) --> OK");
ok = 1;
break;
- } else {
- tls_show_errors(MSG_DEBUG, __func__,
- "SSL_use_PrivateKey_File (PEM) "
- "failed");
}
#else /* OPENSSL_NO_STDIO */
wpa_printf(MSG_DEBUG, "OpenSSL: %s - OPENSSL_NO_STDIO",
@@ -1991,15 +2083,15 @@ static int tls_connection_private_key(void *_ssl_ctx,
}
if (!ok) {
- wpa_printf(MSG_INFO, "OpenSSL: Failed to load private key");
+ tls_show_errors(MSG_INFO, __func__,
+ "Failed to load private key");
os_free(passwd);
- ERR_clear_error();
return -1;
}
ERR_clear_error();
SSL_CTX_set_default_passwd_cb(ssl_ctx, NULL);
os_free(passwd);
-
+
if (!SSL_check_private_key(conn->ssl)) {
tls_show_errors(MSG_INFO, __func__, "Private key failed "
"verification");
@@ -2045,7 +2137,7 @@ static int tls_global_private_key(SSL_CTX *ssl_ctx, const char *private_key,
os_free(passwd);
ERR_clear_error();
SSL_CTX_set_default_passwd_cb(ssl_ctx, NULL);
-
+
if (!SSL_CTX_check_private_key(ssl_ctx)) {
tls_show_errors(MSG_INFO, __func__,
"Private key failed verification");
@@ -2207,6 +2299,11 @@ static int tls_global_dh(SSL_CTX *ssl_ctx, const char *dh_file)
int tls_connection_get_keys(void *ssl_ctx, struct tls_connection *conn,
struct tls_keys *keys)
{
+#ifdef CONFIG_FIPS
+ wpa_printf(MSG_ERROR, "OpenSSL: TLS keys cannot be exported in FIPS "
+ "mode");
+ return -1;
+#else /* CONFIG_FIPS */
SSL *ssl;
if (conn == NULL || keys == NULL)
@@ -2224,6 +2321,7 @@ int tls_connection_get_keys(void *ssl_ctx, struct tls_connection *conn,
keys->server_random_len = SSL3_RANDOM_SIZE;
return 0;
+#endif /* CONFIG_FIPS */
}
@@ -2231,6 +2329,19 @@ int tls_connection_prf(void *tls_ctx, struct tls_connection *conn,
const char *label, int server_random_first,
u8 *out, size_t out_len)
{
+#if OPENSSL_VERSION_NUMBER >= 0x10001000L
+ SSL *ssl;
+ if (conn == NULL)
+ return -1;
+ if (server_random_first)
+ return -1;
+ ssl = conn->ssl;
+ if (SSL_export_keying_material(ssl, out, out_len, label,
+ os_strlen(label), NULL, 0, 0) == 1) {
+ wpa_printf(MSG_DEBUG, "OpenSSL: Using internal PRF");
+ return 0;
+ }
+#endif
return -1;
}
@@ -2663,6 +2774,15 @@ int tls_connection_set_params(void *tls_ctx, struct tls_connection *conn,
return -1;
}
+#ifdef SSL_OP_NO_TICKET
+ if (params->flags & TLS_CONN_DISABLE_SESSION_TICKET)
+ SSL_set_options(conn->ssl, SSL_OP_NO_TICKET);
+ else
+ SSL_clear_options(conn->ssl, SSL_OP_NO_TICKET);
+#endif /* SSL_OP_NO_TICKET */
+
+ conn->flags = params->flags;
+
tls_get_errors(tls_ctx);
return 0;
@@ -2696,6 +2816,13 @@ int tls_global_set_params(void *tls_ctx,
return -1;
}
+#ifdef SSL_OP_NO_TICKET
+ if (params->flags & TLS_CONN_DISABLE_SESSION_TICKET)
+ SSL_CTX_set_options(ssl_ctx, SSL_OP_NO_TICKET);
+ else
+ SSL_CTX_clear_options(ssl_ctx, SSL_OP_NO_TICKET);
+#endif /* SSL_OP_NO_TICKET */
+
return 0;
}
@@ -2705,6 +2832,7 @@ int tls_connection_get_keyblock_size(void *tls_ctx,
{
const EVP_CIPHER *c;
const EVP_MD *h;
+ int md_size;
if (conn == NULL || conn->ssl == NULL ||
conn->ssl->enc_read_ctx == NULL ||
@@ -2718,9 +2846,20 @@ int tls_connection_get_keyblock_size(void *tls_ctx,
#else
h = conn->ssl->read_hash;
#endif
+ if (h)
+ md_size = EVP_MD_size(h);
+#if OPENSSL_VERSION_NUMBER >= 0x10000000L
+ else if (conn->ssl->s3)
+ md_size = conn->ssl->s3->tmp.new_mac_secret_size;
+#endif
+ else
+ return -1;
+ wpa_printf(MSG_DEBUG, "OpenSSL: keyblock size: key_len=%d MD_size=%d "
+ "IV_len=%d", EVP_CIPHER_key_length(c), md_size,
+ EVP_CIPHER_iv_length(c));
return 2 * (EVP_CIPHER_key_length(c) +
- EVP_MD_size(h) +
+ md_size +
EVP_CIPHER_iv_length(c));
}
@@ -2731,35 +2870,6 @@ unsigned int tls_capabilities(void *tls_ctx)
}
-int tls_connection_set_ia(void *tls_ctx, struct tls_connection *conn,
- int tls_ia)
-{
- return -1;
-}
-
-
-struct wpabuf * tls_connection_ia_send_phase_finished(
- void *tls_ctx, struct tls_connection *conn, int final)
-{
- return NULL;
-}
-
-
-int tls_connection_ia_final_phase_finished(void *tls_ctx,
- struct tls_connection *conn)
-{
- return -1;
-}
-
-
-int tls_connection_ia_permute_inner_secret(void *tls_ctx,
- struct tls_connection *conn,
- const u8 *key, size_t key_len)
-{
- return -1;
-}
-
-
#if defined(EAP_FAST) || defined(EAP_FAST_DYNAMIC) || defined(EAP_SERVER_FAST)
/* Pre-shared secred requires a patch to openssl, so this function is
* commented out unless explicitly needed for EAP-FAST in order to be able to
diff --git a/contrib/wpa/src/crypto/tls_schannel.c b/contrib/wpa/src/crypto/tls_schannel.c
index 4a94e99..2c2daa8 100644
--- a/contrib/wpa/src/crypto/tls_schannel.c
+++ b/contrib/wpa/src/crypto/tls_schannel.c
@@ -2,14 +2,8 @@
* SSL/TLS interface functions for Microsoft Schannel
* Copyright (c) 2005-2009, Jouni Malinen <j@w1.fi>
*
- * This program is free software; you can redistribute it and/or modify
- * it under the terms of the GNU General Public License version 2 as
- * published by the Free Software Foundation.
- *
- * Alternatively, this software may be distributed under the terms of BSD
- * license.
- *
- * See README and COPYING for more details.
+ * This software may be distributed under the terms of the BSD license.
+ * See README for more details.
*/
/*
@@ -736,32 +730,3 @@ unsigned int tls_capabilities(void *tls_ctx)
{
return 0;
}
-
-
-int tls_connection_set_ia(void *tls_ctx, struct tls_connection *conn,
- int tls_ia)
-{
- return -1;
-}
-
-
-struct wpabuf * tls_connection_ia_send_phase_finished(
- void *tls_ctx, struct tls_connection *conn, int final);
-{
- return NULL;
-}
-
-
-int tls_connection_ia_final_phase_finished(void *tls_ctx,
- struct tls_connection *conn)
-{
- return -1;
-}
-
-
-int tls_connection_ia_permute_inner_secret(void *tls_ctx,
- struct tls_connection *conn,
- const u8 *key, size_t key_len)
-{
- return -1;
-}
diff --git a/contrib/wpa/src/drivers/.gitignore b/contrib/wpa/src/drivers/.gitignore
deleted file mode 100644
index 1d9e0e6..0000000
--- a/contrib/wpa/src/drivers/.gitignore
+++ /dev/null
@@ -1,2 +0,0 @@
-build.wpa_supplicant
-build.hostapd
diff --git a/contrib/wpa/src/drivers/Makefile b/contrib/wpa/src/drivers/Makefile
deleted file mode 100644
index 07600e5..0000000
--- a/contrib/wpa/src/drivers/Makefile
+++ /dev/null
@@ -1,9 +0,0 @@
-all:
- @echo Nothing to be made.
-
-clean:
- rm -f *~ *.o *.d
- rm -f build.wpa_supplicant build.hostapd
-
-install:
- @echo Nothing to be made.
diff --git a/contrib/wpa/src/drivers/driver.h b/contrib/wpa/src/drivers/driver.h
index fa49da4..7ee71aa 100644
--- a/contrib/wpa/src/drivers/driver.h
+++ b/contrib/wpa/src/drivers/driver.h
@@ -1,15 +1,9 @@
/*
* Driver interface definition
- * Copyright (c) 2003-2010, Jouni Malinen <j@w1.fi>
+ * Copyright (c) 2003-2012, Jouni Malinen <j@w1.fi>
*
- * This program is free software; you can redistribute it and/or modify
- * it under the terms of the GNU General Public License version 2 as
- * published by the Free Software Foundation.
- *
- * Alternatively, this software may be distributed under the terms of BSD
- * license.
- *
- * See README and COPYING for more details.
+ * This software may be distributed under the terms of the BSD license.
+ * See README for more details.
*
* This file defines a driver interface used by both %wpa_supplicant and
* hostapd. The first part of the file defines data structures used in various
@@ -31,6 +25,9 @@
#define HOSTAPD_CHAN_PASSIVE_SCAN 0x00000002
#define HOSTAPD_CHAN_NO_IBSS 0x00000004
#define HOSTAPD_CHAN_RADAR 0x00000008
+#define HOSTAPD_CHAN_HT40PLUS 0x00000010
+#define HOSTAPD_CHAN_HT40MINUS 0x00000020
+#define HOSTAPD_CHAN_HT40 0x00000040
/**
* struct hostapd_channel_data - Channel information
@@ -44,7 +41,7 @@ struct hostapd_channel_data {
/**
* freq - Frequency in MHz
*/
- short freq;
+ int freq;
/**
* flag - Channel flags (HOSTAPD_CHAN_*)
@@ -57,6 +54,8 @@ struct hostapd_channel_data {
u8 max_tx_power;
};
+#define HOSTAPD_MODE_FLAG_HT_INFO_KNOWN BIT(0)
+
/**
* struct hostapd_hw_modes - Supported hardware mode information
*/
@@ -100,6 +99,18 @@ struct hostapd_hw_modes {
* a_mpdu_params - A-MPDU (IEEE 802.11n) parameters
*/
u8 a_mpdu_params;
+
+ /**
+ * vht_capab - VHT (IEEE 802.11ac) capabilities
+ */
+ u32 vht_capab;
+
+ /**
+ * vht_mcs_set - VHT MCS (IEEE 802.11ac) rate parameters
+ */
+ u8 vht_mcs_set[8];
+
+ unsigned int flags; /* HOSTAPD_MODE_FLAG_* */
};
@@ -192,7 +203,7 @@ struct wpa_interface_info {
const char *drv_name;
};
-#define WPAS_MAX_SCAN_SSIDS 4
+#define WPAS_MAX_SCAN_SSIDS 16
/**
* struct wpa_driver_scan_params - Scan parameters
@@ -261,6 +272,24 @@ struct wpa_driver_scan_params {
* num_filter_ssids - Number of entries in filter_ssids array
*/
size_t num_filter_ssids;
+
+ /**
+ * filter_rssi - Filter by RSSI
+ *
+ * The driver may filter scan results in firmware to reduce host
+ * wakeups and thereby save power. Specify the RSSI threshold in s32
+ * dBm.
+ */
+ s32 filter_rssi;
+
+ /**
+ * p2p_probe - Used to disable CCK (802.11b) rates for P2P probes
+ *
+ * When set, the driver is expected to remove rates 1, 2, 5.5, and 11
+ * Mbps from the support rates element(s) in the Probe Request frames
+ * and not to transmit the frames at any of those rates.
+ */
+ u8 p2p_probe;
};
/**
@@ -279,6 +308,22 @@ struct wpa_driver_auth_params {
size_t wep_key_len[4];
int wep_tx_keyidx;
int local_state_change;
+
+ /**
+ * p2p - Whether this connection is a P2P group
+ */
+ int p2p;
+
+ const u8 *sae_data;
+ size_t sae_data_len;
+
+};
+
+enum wps_mode {
+ WPS_MODE_NONE /* no WPS provisioning being used */,
+ WPS_MODE_OPEN /* WPS provisioning with AP that is in open mode */,
+ WPS_MODE_PRIVACY /* WPS provisioning with AP that is using protection
+ */
};
/**
@@ -310,6 +355,13 @@ struct wpa_driver_associate_params {
int freq;
/**
+ * bg_scan_period - Background scan period in seconds, 0 to disable
+ * background scan, or -1 to indicate no change to default driver
+ * configuration
+ */
+ int bg_scan_period;
+
+ /**
* wpa_ie - WPA information element for (Re)Association Request
* WPA information element to be included in (Re)Association
* Request (including information element id and length). Use
@@ -335,6 +387,11 @@ struct wpa_driver_associate_params {
size_t wpa_ie_len;
/**
+ * wpa_proto - Bitfield of WPA_PROTO_* values to indicate WPA/WPA2
+ */
+ unsigned int wpa_proto;
+
+ /**
* pairwise_suite - Selected pairwise cipher suite
*
* This is usually ignored if @wpa_ie is used.
@@ -460,6 +517,237 @@ struct wpa_driver_associate_params {
* association.
*/
const u8 *prev_bssid;
+
+ /**
+ * wps - WPS mode
+ *
+ * If the driver needs to do special configuration for WPS association,
+ * this variable provides more information on what type of association
+ * is being requested. Most drivers should not need ot use this.
+ */
+ enum wps_mode wps;
+
+ /**
+ * p2p - Whether this connection is a P2P group
+ */
+ int p2p;
+
+ /**
+ * uapsd - UAPSD parameters for the network
+ * -1 = do not change defaults
+ * AP mode: 1 = enabled, 0 = disabled
+ * STA mode: bits 0..3 UAPSD enabled for VO,VI,BK,BE
+ */
+ int uapsd;
+
+ /**
+ * fixed_bssid - Whether to force this BSSID in IBSS mode
+ * 1 = Fix this BSSID and prevent merges.
+ * 0 = Do not fix BSSID.
+ */
+ int fixed_bssid;
+
+ /**
+ * disable_ht - Disable HT (IEEE 802.11n) for this connection
+ */
+ int disable_ht;
+
+ /**
+ * HT Capabilities over-rides. Only bits set in the mask will be used,
+ * and not all values are used by the kernel anyway. Currently, MCS,
+ * MPDU and MSDU fields are used.
+ */
+ const u8 *htcaps; /* struct ieee80211_ht_capabilities * */
+ const u8 *htcaps_mask; /* struct ieee80211_ht_capabilities * */
+};
+
+enum hide_ssid {
+ NO_SSID_HIDING,
+ HIDDEN_SSID_ZERO_LEN,
+ HIDDEN_SSID_ZERO_CONTENTS
+};
+
+struct wpa_driver_ap_params {
+ /**
+ * head - Beacon head from IEEE 802.11 header to IEs before TIM IE
+ */
+ const u8 *head;
+
+ /**
+ * head_len - Length of the head buffer in octets
+ */
+ size_t head_len;
+
+ /**
+ * tail - Beacon tail following TIM IE
+ */
+ const u8 *tail;
+
+ /**
+ * tail_len - Length of the tail buffer in octets
+ */
+ size_t tail_len;
+
+ /**
+ * dtim_period - DTIM period
+ */
+ int dtim_period;
+
+ /**
+ * beacon_int - Beacon interval
+ */
+ int beacon_int;
+
+ /**
+ * basic_rates: -1 terminated array of basic rates in 100 kbps
+ *
+ * This parameter can be used to set a specific basic rate set for the
+ * BSS. If %NULL, default basic rate set is used.
+ */
+ int *basic_rates;
+
+ /**
+ * proberesp - Probe Response template
+ *
+ * This is used by drivers that reply to Probe Requests internally in
+ * AP mode and require the full Probe Response template.
+ */
+ const u8 *proberesp;
+
+ /**
+ * proberesp_len - Length of the proberesp buffer in octets
+ */
+ size_t proberesp_len;
+
+ /**
+ * ssid - The SSID to use in Beacon/Probe Response frames
+ */
+ const u8 *ssid;
+
+ /**
+ * ssid_len - Length of the SSID (1..32)
+ */
+ size_t ssid_len;
+
+ /**
+ * hide_ssid - Whether to hide the SSID
+ */
+ enum hide_ssid hide_ssid;
+
+ /**
+ * pairwise_ciphers - WPA_CIPHER_* bitfield
+ */
+ unsigned int pairwise_ciphers;
+
+ /**
+ * group_cipher - WPA_CIPHER_*
+ */
+ unsigned int group_cipher;
+
+ /**
+ * key_mgmt_suites - WPA_KEY_MGMT_* bitfield
+ */
+ unsigned int key_mgmt_suites;
+
+ /**
+ * auth_algs - WPA_AUTH_ALG_* bitfield
+ */
+ unsigned int auth_algs;
+
+ /**
+ * wpa_version - WPA_PROTO_* bitfield
+ */
+ unsigned int wpa_version;
+
+ /**
+ * privacy - Whether privacy is used in the BSS
+ */
+ int privacy;
+
+ /**
+ * beacon_ies - WPS/P2P IE(s) for Beacon frames
+ *
+ * This is used to add IEs like WPS IE and P2P IE by drivers that do
+ * not use the full Beacon template.
+ */
+ const struct wpabuf *beacon_ies;
+
+ /**
+ * proberesp_ies - P2P/WPS IE(s) for Probe Response frames
+ *
+ * This is used to add IEs like WPS IE and P2P IE by drivers that
+ * reply to Probe Request frames internally.
+ */
+ const struct wpabuf *proberesp_ies;
+
+ /**
+ * assocresp_ies - WPS IE(s) for (Re)Association Response frames
+ *
+ * This is used to add IEs like WPS IE by drivers that reply to
+ * (Re)Association Request frames internally.
+ */
+ const struct wpabuf *assocresp_ies;
+
+ /**
+ * isolate - Whether to isolate frames between associated stations
+ *
+ * If this is non-zero, the AP is requested to disable forwarding of
+ * frames between associated stations.
+ */
+ int isolate;
+
+ /**
+ * cts_protect - Whether CTS protection is enabled
+ */
+ int cts_protect;
+
+ /**
+ * preamble - Whether short preamble is enabled
+ */
+ int preamble;
+
+ /**
+ * short_slot_time - Whether short slot time is enabled
+ *
+ * 0 = short slot time disable, 1 = short slot time enabled, -1 = do
+ * not set (e.g., when 802.11g mode is not in use)
+ */
+ int short_slot_time;
+
+ /**
+ * ht_opmode - HT operation mode or -1 if HT not in use
+ */
+ int ht_opmode;
+
+ /**
+ * interworking - Whether Interworking is enabled
+ */
+ int interworking;
+
+ /**
+ * hessid - Homogeneous ESS identifier or %NULL if not set
+ */
+ const u8 *hessid;
+
+ /**
+ * access_network_type - Access Network Type (0..15)
+ *
+ * This is used for filtering Probe Request frames when Interworking is
+ * enabled.
+ */
+ u8 access_network_type;
+
+ /**
+ * ap_max_inactivity - Timeout in seconds to detect STA's inactivity
+ *
+ * This is used by driver which advertises this capability.
+ */
+ int ap_max_inactivity;
+
+ /**
+ * disable_dgaf - Whether group-addressed frames are disabled
+ */
+ int disable_dgaf;
};
/**
@@ -473,12 +761,15 @@ struct wpa_driver_capa {
#define WPA_DRIVER_CAPA_KEY_MGMT_WPA_NONE 0x00000010
#define WPA_DRIVER_CAPA_KEY_MGMT_FT 0x00000020
#define WPA_DRIVER_CAPA_KEY_MGMT_FT_PSK 0x00000040
+#define WPA_DRIVER_CAPA_KEY_MGMT_WAPI_PSK 0x00000080
unsigned int key_mgmt;
#define WPA_DRIVER_CAPA_ENC_WEP40 0x00000001
#define WPA_DRIVER_CAPA_ENC_WEP104 0x00000002
#define WPA_DRIVER_CAPA_ENC_TKIP 0x00000004
#define WPA_DRIVER_CAPA_ENC_CCMP 0x00000008
+#define WPA_DRIVER_CAPA_ENC_WEP128 0x00000010
+#define WPA_DRIVER_CAPA_ENC_GCMP 0x00000020
unsigned int enc;
#define WPA_DRIVER_AUTH_OPEN 0x00000001
@@ -490,7 +781,7 @@ struct wpa_driver_capa {
#define WPA_DRIVER_FLAGS_DRIVER_IE 0x00000001
/* Driver needs static WEP key setup after association command */
#define WPA_DRIVER_FLAGS_SET_KEYS_AFTER_ASSOC 0x00000002
-#define WPA_DRIVER_FLAGS_USER_SPACE_MLME 0x00000004
+/* unused: 0x00000004 */
/* Driver takes care of RSN 4-way handshake internally; PMK is configured with
* struct wpa_driver_ops::set_key using alg = WPA_ALG_PMK */
#define WPA_DRIVER_FLAGS_4WAY_HANDSHAKE 0x00000008
@@ -502,14 +793,86 @@ struct wpa_driver_capa {
#define WPA_DRIVER_FLAGS_AP 0x00000040
/* Driver needs static WEP key setup after association has been completed */
#define WPA_DRIVER_FLAGS_SET_KEYS_AFTER_ASSOC_DONE 0x00000080
+/* Driver takes care of P2P management operations */
+#define WPA_DRIVER_FLAGS_P2P_MGMT 0x00000100
+/* Driver supports concurrent P2P operations */
+#define WPA_DRIVER_FLAGS_P2P_CONCURRENT 0x00000200
+/*
+ * Driver uses the initial interface as a dedicated management interface, i.e.,
+ * it cannot be used for P2P group operations or non-P2P purposes.
+ */
+#define WPA_DRIVER_FLAGS_P2P_DEDICATED_INTERFACE 0x00000400
+/* This interface is P2P capable (P2P Device, GO, or P2P Client */
+#define WPA_DRIVER_FLAGS_P2P_CAPABLE 0x00000800
+/* Driver supports concurrent operations on multiple channels */
+#define WPA_DRIVER_FLAGS_MULTI_CHANNEL_CONCURRENT 0x00001000
+/*
+ * Driver uses the initial interface for P2P management interface and non-P2P
+ * purposes (e.g., connect to infra AP), but this interface cannot be used for
+ * P2P group operations.
+ */
+#define WPA_DRIVER_FLAGS_P2P_MGMT_AND_NON_P2P 0x00002000
+/*
+ * Driver is known to use sane error codes, i.e., when it indicates that
+ * something (e.g., association) fails, there was indeed a failure and the
+ * operation does not end up getting completed successfully later.
+ */
+#define WPA_DRIVER_FLAGS_SANE_ERROR_CODES 0x00004000
+/* Driver supports off-channel TX */
+#define WPA_DRIVER_FLAGS_OFFCHANNEL_TX 0x00008000
+/* Driver indicates TX status events for EAPOL Data frames */
+#define WPA_DRIVER_FLAGS_EAPOL_TX_STATUS 0x00010000
+/* Driver indicates TX status events for Deauth/Disassoc frames */
+#define WPA_DRIVER_FLAGS_DEAUTH_TX_STATUS 0x00020000
+/* Driver supports roaming (BSS selection) in firmware */
+#define WPA_DRIVER_FLAGS_BSS_SELECTION 0x00040000
+/* Driver supports operating as a TDLS peer */
+#define WPA_DRIVER_FLAGS_TDLS_SUPPORT 0x00080000
+/* Driver requires external TDLS setup/teardown/discovery */
+#define WPA_DRIVER_FLAGS_TDLS_EXTERNAL_SETUP 0x00100000
+/* Driver indicates support for Probe Response offloading in AP mode */
+#define WPA_DRIVER_FLAGS_PROBE_RESP_OFFLOAD 0x00200000
+/* Driver supports U-APSD in AP mode */
+#define WPA_DRIVER_FLAGS_AP_UAPSD 0x00400000
+/* Driver supports inactivity timer in AP mode */
+#define WPA_DRIVER_FLAGS_INACTIVITY_TIMER 0x00800000
+/* Driver expects user space implementation of MLME in AP mode */
+#define WPA_DRIVER_FLAGS_AP_MLME 0x01000000
+/* Driver supports SAE with user space SME */
+#define WPA_DRIVER_FLAGS_SAE 0x02000000
+/* Driver makes use of OBSS scan mechanism in wpa_supplicant */
+#define WPA_DRIVER_FLAGS_OBSS_SCAN 0x04000000
unsigned int flags;
int max_scan_ssids;
+ int max_sched_scan_ssids;
+ int sched_scan_supported;
+ int max_match_sets;
/**
* max_remain_on_chan - Maximum remain-on-channel duration in msec
*/
unsigned int max_remain_on_chan;
+
+ /**
+ * max_stations - Maximum number of associated stations the driver
+ * supports in AP mode
+ */
+ unsigned int max_stations;
+
+ /**
+ * probe_resp_offloads - Bitmap of supported protocols by the driver
+ * for Probe Response offloading.
+ */
+/* Driver Probe Response offloading support for WPS ver. 1 */
+#define WPA_DRIVER_PROBE_RESP_OFFLOAD_WPS 0x00000001
+/* Driver Probe Response offloading support for WPS ver. 2 */
+#define WPA_DRIVER_PROBE_RESP_OFFLOAD_WPS2 0x00000002
+/* Driver Probe Response offloading support for P2P */
+#define WPA_DRIVER_PROBE_RESP_OFFLOAD_P2P 0x00000004
+/* Driver Probe Response offloading support for IEEE 802.11u (Interworking) */
+#define WPA_DRIVER_PROBE_RESP_OFFLOAD_INTERWORKING 0x00000008
+ unsigned int probe_resp_offloads;
};
@@ -535,6 +898,9 @@ struct hostapd_sta_add_params {
size_t supp_rates_len;
u16 listen_interval;
const struct ieee80211_ht_capabilities *ht_capabilities;
+ u32 flags; /* bitmask of WPA_STA_* flags */
+ int set; /* Set STA parameters instead of add */
+ u8 qosinfo;
};
struct hostapd_freq_params {
@@ -567,9 +933,26 @@ enum wpa_driver_if_type {
* This interface has its own address and Beacon frame.
*/
WPA_IF_AP_BSS,
+
+ /**
+ * WPA_IF_P2P_GO - P2P Group Owner
+ */
+ WPA_IF_P2P_GO,
+
+ /**
+ * WPA_IF_P2P_CLIENT - P2P Client
+ */
+ WPA_IF_P2P_CLIENT,
+
+ /**
+ * WPA_IF_P2P_GROUP - P2P Group interface (will become either
+ * WPA_IF_P2P_GO or WPA_IF_P2P_CLIENT, but the role is not yet known)
+ */
+ WPA_IF_P2P_GROUP
};
struct wpa_init_params {
+ void *global_priv;
const u8 *bssid;
const char *ifname;
const u8 *ssid;
@@ -595,12 +978,63 @@ struct wpa_bss_params {
int wpa_pairwise;
int wpa_key_mgmt;
int rsn_preauth;
+ enum mfp_options ieee80211w;
};
#define WPA_STA_AUTHORIZED BIT(0)
#define WPA_STA_WMM BIT(1)
#define WPA_STA_SHORT_PREAMBLE BIT(2)
#define WPA_STA_MFP BIT(3)
+#define WPA_STA_TDLS_PEER BIT(4)
+
+/**
+ * struct p2p_params - P2P parameters for driver-based P2P management
+ */
+struct p2p_params {
+ const char *dev_name;
+ u8 pri_dev_type[8];
+#define DRV_MAX_SEC_DEV_TYPES 5
+ u8 sec_dev_type[DRV_MAX_SEC_DEV_TYPES][8];
+ size_t num_sec_dev_types;
+};
+
+enum tdls_oper {
+ TDLS_DISCOVERY_REQ,
+ TDLS_SETUP,
+ TDLS_TEARDOWN,
+ TDLS_ENABLE_LINK,
+ TDLS_DISABLE_LINK,
+ TDLS_ENABLE,
+ TDLS_DISABLE
+};
+
+enum wnm_oper {
+ WNM_SLEEP_ENTER_CONFIRM,
+ WNM_SLEEP_ENTER_FAIL,
+ WNM_SLEEP_EXIT_CONFIRM,
+ WNM_SLEEP_EXIT_FAIL,
+ WNM_SLEEP_TFS_REQ_IE_ADD, /* STA requests driver to add TFS req IE */
+ WNM_SLEEP_TFS_REQ_IE_NONE, /* STA requests empty TFS req IE */
+ WNM_SLEEP_TFS_REQ_IE_SET, /* AP requests driver to set TFS req IE for
+ * a STA */
+ WNM_SLEEP_TFS_RESP_IE_ADD, /* AP requests driver to add TFS resp IE
+ * for a STA */
+ WNM_SLEEP_TFS_RESP_IE_NONE, /* AP requests empty TFS resp IE */
+ WNM_SLEEP_TFS_RESP_IE_SET, /* AP requests driver to set TFS resp IE
+ * for a STA */
+ WNM_SLEEP_TFS_IE_DEL /* AP delete the TFS IE */
+};
+
+/**
+ * struct wpa_signal_info - Information about channel signal quality
+ */
+struct wpa_signal_info {
+ u32 frequency;
+ int above_threshold;
+ int current_signal;
+ int current_noise;
+ int current_txrate;
+};
/**
* struct wpa_driver_ops - Driver interface API definition
@@ -650,10 +1084,15 @@ struct wpa_driver_ops {
* @ifname: Interface name (for multi-SSID/VLAN support)
* @priv: private driver interface data
* @alg: encryption algorithm (%WPA_ALG_NONE, %WPA_ALG_WEP,
- * %WPA_ALG_TKIP, %WPA_ALG_CCMP, %WPA_ALG_IGTK, %WPA_ALG_PMK);
+ * %WPA_ALG_TKIP, %WPA_ALG_CCMP, %WPA_ALG_IGTK, %WPA_ALG_PMK,
+ * %WPA_ALG_GCMP);
* %WPA_ALG_NONE clears the key.
- * @addr: address of the peer STA or ff:ff:ff:ff:ff:ff for
- * broadcast/default keys
+ * @addr: Address of the peer STA (BSSID of the current AP when setting
+ * pairwise key in station mode), ff:ff:ff:ff:ff:ff for
+ * broadcast keys, %NULL for default keys that are used both for
+ * broadcast and unicast; when clearing keys, %NULL is used to
+ * indicate that both the broadcast-only and default key of the
+ * specified key index is to be cleared
* @key_idx: key index (0..3), usually 0 for unicast keys; 0..4095 for
* IGTK
* @set_tx: configure this key as the default Tx key (only used when
@@ -661,13 +1100,13 @@ struct wpa_driver_ops {
* @seq: sequence number/packet number, seq_len octets, the next
* packet number to be used for in replay protection; configured
* for Rx keys (in most cases, this is only used with broadcast
- * keys and set to zero for unicast keys)
+ * keys and set to zero for unicast keys); %NULL if not set
* @seq_len: length of the seq, depends on the algorithm:
- * TKIP: 6 octets, CCMP: 6 octets, IGTK: 6 octets
+ * TKIP: 6 octets, CCMP/GCMP: 6 octets, IGTK: 6 octets
* @key: key buffer; TKIP: 16-byte temporal key, 8-byte Tx Mic key,
* 8-byte Rx Mic Key
* @key_len: length of the key buffer in octets (WEP: 5 or 13,
- * TKIP: 32, CCMP: 16, IGTK: 16)
+ * TKIP: 32, CCMP/GCMP: 16, IGTK: 16)
*
* Returns: 0 on success, -1 on failure
*
@@ -684,7 +1123,7 @@ struct wpa_driver_ops {
* Please note that TKIP keys include separate TX and RX MIC keys and
* some drivers may expect them in different order than wpa_supplicant
* is using. If the TX/RX keys are swapped, all TKIP encrypted packets
- * will tricker Michael MIC errors. This can be fixed by changing the
+ * will trigger Michael MIC errors. This can be fixed by changing the
* order of MIC keys by swapping te bytes 16..23 and 24..31 of the key
* in driver_*.c set_key() implementation, see driver_ndis.c for an
* example on how this can be done.
@@ -764,17 +1203,6 @@ struct wpa_driver_ops {
int (*deauthenticate)(void *priv, const u8 *addr, int reason_code);
/**
- * disassociate - Request driver to disassociate
- * @priv: private driver interface data
- * @addr: peer address (BSSID of the AP)
- * @reason_code: 16-bit reason code to be sent in the disassociation
- * frame
- *
- * Returns: 0 on success, -1 on failure
- */
- int (*disassociate)(void *priv, const u8 *addr, int reason_code);
-
- /**
* associate - Request driver to associate
* @priv: private driver interface data
* @params: association parameters
@@ -951,91 +1379,21 @@ struct wpa_driver_ops {
* flags: Variable for returning hardware feature flags
* Returns: Pointer to allocated hardware data on success or %NULL on
* failure. Caller is responsible for freeing this.
- *
- * This function is only needed for drivers that export MLME
- * (management frame processing) to %wpa_supplicant or hostapd.
*/
struct hostapd_hw_modes * (*get_hw_feature_data)(void *priv,
u16 *num_modes,
u16 *flags);
/**
- * set_channel - Set channel
- * @priv: Private driver interface data
- * @phymode: HOSTAPD_MODE_IEEE80211B, ..
- * @chan: IEEE 802.11 channel number
- * @freq: Frequency of the channel in MHz
- * Returns: 0 on success, -1 on failure
- *
- * This function is only needed for drivers that export MLME
- * (management frame processing) to wpa_supplicant.
- */
- int (*set_channel)(void *priv, enum hostapd_hw_mode phymode, int chan,
- int freq);
-
- /**
- * set_ssid - Set SSID
- * @priv: Private driver interface data
- * @ssid: SSID
- * @ssid_len: SSID length
- * Returns: 0 on success, -1 on failure
- *
- * This function is only needed for drivers that export MLME
- * (management frame processing) to wpa_supplicant.
- */
- int (*set_ssid)(void *priv, const u8 *ssid, size_t ssid_len);
-
- /**
- * set_bssid - Set BSSID
- * @priv: Private driver interface data
- * @bssid: BSSID
- * Returns: 0 on success, -1 on failure
- *
- * This function is only needed for drivers that export MLME
- * (management frame processing) to wpa_supplicant.
- */
- int (*set_bssid)(void *priv, const u8 *bssid);
-
- /**
* send_mlme - Send management frame from MLME
* @priv: Private driver interface data
* @data: IEEE 802.11 management frame with IEEE 802.11 header
* @data_len: Size of the management frame
+ * @noack: Do not wait for this frame to be acked (disable retries)
* Returns: 0 on success, -1 on failure
- *
- * This function is only needed for drivers that export MLME
- * (management frame processing) to wpa_supplicant.
*/
- int (*send_mlme)(void *priv, const u8 *data, size_t data_len);
-
- /**
- * mlme_add_sta - Add a STA entry into the driver/netstack
- * @priv: Private driver interface data
- * @addr: MAC address of the STA (e.g., BSSID of the AP)
- * @supp_rates: Supported rate set (from (Re)AssocResp); in IEEE 802.11
- * format (one octet per rate, 1 = 0.5 Mbps)
- * @supp_rates_len: Number of entries in supp_rates
- * Returns: 0 on success, -1 on failure
- *
- * This function is only needed for drivers that export MLME
- * (management frame processing) to wpa_supplicant. When the MLME code
- * completes association with an AP, this function is called to
- * configure the driver/netstack with a STA entry for data frame
- * processing (TX rate control, encryption/decryption).
- */
- int (*mlme_add_sta)(void *priv, const u8 *addr, const u8 *supp_rates,
- size_t supp_rates_len);
-
- /**
- * mlme_remove_sta - Remove a STA entry from the driver/netstack
- * @priv: Private driver interface data
- * @addr: MAC address of the STA (e.g., BSSID of the AP)
- * Returns: 0 on success, -1 on failure
- *
- * This function is only needed for drivers that export MLME
- * (management frame processing) to wpa_supplicant.
- */
- int (*mlme_remove_sta)(void *priv, const u8 *addr);
+ int (*send_mlme)(void *priv, const u8 *data, size_t data_len,
+ int noack);
/**
* update_ft_ies - Update FT (IEEE 802.11r) IEs
@@ -1164,24 +1522,25 @@ struct wpa_driver_ops {
struct wpa_driver_auth_params *params);
/**
- * set_beacon - Set Beacon frame template
+ * set_ap - Set Beacon and Probe Response information for AP mode
* @priv: Private driver interface data
- * @head: Beacon head from IEEE 802.11 header to IEs before TIM IE
- * @head_len: Length of the head buffer in octets
- * @tail: Beacon tail following TIM IE
- * @tail_len: Length of the tail buffer in octets
- * @dtim_period: DTIM period
- * @beacon_int: Beacon interval
- * Returns: 0 on success, -1 on failure
+ * @params: Parameters to use in AP mode
*
- * This function is used to configure Beacon template for the driver in
+ * This function is used to configure Beacon template and/or extra IEs
+ * to add for Beacon and Probe Response frames for the driver in
* AP mode. The driver is responsible for building the full Beacon
* frame by concatenating the head part with TIM IE generated by the
- * driver/firmware and finishing with the tail part.
+ * driver/firmware and finishing with the tail part. Depending on the
+ * driver architectue, this can be done either by using the full
+ * template or the set of additional IEs (e.g., WPS and P2P IE).
+ * Similarly, Probe Response processing depends on the driver design.
+ * If the driver (or firmware) takes care of replying to Probe Request
+ * frames, the extra IEs provided here needs to be added to the Probe
+ * Response frames.
+ *
+ * Returns: 0 on success, -1 on failure
*/
- int (*set_beacon)(void *priv, const u8 *head, size_t head_len,
- const u8 *tail, size_t tail_len, int dtim_period,
- int beacon_int);
+ int (*set_ap)(void *priv, struct wpa_driver_ap_params *params);
/**
* hapd_init - Initialize driver interface (hostapd only)
@@ -1190,7 +1549,7 @@ struct wpa_driver_ops {
* Returns: Pointer to private data, %NULL on failure
*
* This function is used instead of init() or init2() when the driver
- * wrapper is used withh hostapd.
+ * wrapper is used with hostapd.
*/
void * (*hapd_init)(struct hostapd_data *hapd,
struct wpa_init_params *params);
@@ -1210,8 +1569,10 @@ struct wpa_driver_ops {
* This is an optional function to configure the kernel driver to
* enable/disable IEEE 802.1X support and set WPA/WPA2 parameters. This
* can be left undefined (set to %NULL) if IEEE 802.1X support is
- * always enabled and the driver uses set_beacon() to set WPA/RSN IE
+ * always enabled and the driver uses set_ap() to set WPA/RSN IE
* for Beacon frames.
+ *
+ * DEPRECATED - use set_ap() instead
*/
int (*set_ieee8021x)(void *priv, struct wpa_bss_params *params);
@@ -1223,7 +1584,9 @@ struct wpa_driver_ops {
*
* This is an optional function to configure privacy field in the
* kernel driver for Beacon frames. This can be left undefined (set to
- * %NULL) if the driver uses the Beacon template from set_beacon().
+ * %NULL) if the driver uses the Beacon template from set_ap().
+ *
+ * DEPRECATED - use set_ap() instead
*/
int (*set_privacy)(void *priv, int enabled);
@@ -1237,9 +1600,9 @@ struct wpa_driver_ops {
* Returns: 0 on success, -1 on failure
*
* This function is used to fetch the last used TSC/packet number for
- * a TKIP, CCMP, or BIP/IGTK key. It is mainly used with group keys, so
- * there is no strict requirement on implementing support for unicast
- * keys (i.e., addr != %NULL).
+ * a TKIP, CCMP, GCMP, or BIP/IGTK key. It is mainly used with group
+ * keys, so there is no strict requirement on implementing support for
+ * unicast keys (i.e., addr != %NULL).
*/
int (*get_seqnum)(const char *ifname, void *priv, const u8 *addr,
int idx, u8 *seq);
@@ -1265,12 +1628,14 @@ struct wpa_driver_ops {
* This is an optional function to add information elements in the
* kernel driver for Beacon and Probe Response frames. This can be left
* undefined (set to %NULL) if the driver uses the Beacon template from
- * set_beacon().
+ * set_ap().
+ *
+ * DEPRECATED - use set_ap() instead
*/
int (*set_generic_elem)(void *priv, const u8 *elem, size_t elem_len);
/**
- * read_sta_data - Fetch station data (AP only)
+ * read_sta_data - Fetch station data
* @priv: Private driver interface data
* @data: Buffer for returning station information
* @addr: MAC address of the station
@@ -1287,12 +1652,13 @@ struct wpa_driver_ops {
* @data_len: Length of the EAPOL packet in octets
* @encrypt: Whether the frame should be encrypted
* @own_addr: Source MAC address
+ * @flags: WPA_STA_* flags for the destination station
*
* Returns: 0 on success, -1 on failure
*/
int (*hapd_send_eapol)(void *priv, const u8 *addr, const u8 *data,
size_t data_len, int encrypt,
- const u8 *own_addr);
+ const u8 *own_addr, u32 flags);
/**
* sta_deauth - Deauthenticate a station (AP only)
@@ -1338,8 +1704,7 @@ struct wpa_driver_ops {
* Returns: Length of the SSID on success, -1 on failure
*
* This function need not be implemented if the driver uses Beacon
- * template from set_beacon() and does not reply to Probe Request
- * frames.
+ * template from set_ap() and does not reply to Probe Request frames.
*/
int (*hapd_get_ssid)(void *priv, u8 *buf, int len);
@@ -1349,6 +1714,8 @@ struct wpa_driver_ops {
* @buf: SSID
* @len: Length of the SSID in octets
* Returns: 0 on success, -1 on failure
+ *
+ * DEPRECATED - use set_ap() instead
*/
int (*hapd_set_ssid)(void *priv, const u8 *buf, int len);
@@ -1372,6 +1739,9 @@ struct wpa_driver_ops {
* This function is used to add a station entry to the driver once the
* station has completed association. This is only used if the driver
* does not take care of association processing.
+ *
+ * With TDLS, this function is also used to add or set (params->set 1)
+ * TDLS peer entries.
*/
int (*sta_add)(void *priv, struct hostapd_sta_add_params *params);
@@ -1428,44 +1798,9 @@ struct wpa_driver_ops {
int total_flags, int flags_or, int flags_and);
/**
- * set_rate_sets - Set supported and basic rate sets (AP only)
- * @priv: Private driver interface data
- * @supp_rates: -1 terminated array of supported rates in 100 kbps
- * @basic_rates: -1 terminated array of basic rates in 100 kbps
- * @mode: hardware mode (HOSTAPD_MODE_*)
- * Returns: 0 on success, -1 on failure
- */
- int (*set_rate_sets)(void *priv, int *supp_rates, int *basic_rates,
- int mode);
-
- /**
- * set_cts_protect - Set CTS protection mode (AP only)
- * @priv: Private driver interface data
- * @value: Whether CTS protection is enabled
- * Returns: 0 on success, -1 on failure
- */
- int (*set_cts_protect)(void *priv, int value);
-
- /**
- * set_preamble - Set preamble mode (AP only)
- * @priv: Private driver interface data
- * @value: Whether short preamble is enabled
- * Returns: 0 on success, -1 on failure
- */
- int (*set_preamble)(void *priv, int value);
-
- /**
- * set_short_slot_time - Set short slot time (AP only)
- * @priv: Private driver interface data
- * @value: Whether short slot time is enabled
- * Returns: 0 on success, -1 on failure
- */
- int (*set_short_slot_time)(void *priv, int value);
-
- /**
* set_tx_queue_params - Set TX queue parameters
* @priv: Private driver interface data
- * @queue: Queue number
+ * @queue: Queue number (0 = VO, 1 = VI, 2 = BE, 3 = BK)
* @aifs: AIFS
* @cw_min: cwMin
* @cw_max: cwMax
@@ -1475,17 +1810,6 @@ struct wpa_driver_ops {
int cw_max, int burst_time);
/**
- * valid_bss_mask - Validate BSSID mask
- * @priv: Private driver interface data
- * @addr: Address
- * @mask: Mask
- * Returns: 0 if mask is valid, -1 if mask is not valid, 1 if mask can
- * be used, but the main interface address must be the first address in
- * the block if mask is applied
- */
- int (*valid_bss_mask)(void *priv, const u8 *addr, const u8 *mask);
-
- /**
* if_add - Add a virtual interface
* @priv: Private driver interface data
* @type: Interface type
@@ -1500,11 +1824,13 @@ struct wpa_driver_ops {
* @if_addr: Buffer for returning the allocated interface address
* (this may differ from the requested addr if the driver cannot
* change interface address)
+ * @bridge: Bridge interface to use or %NULL if no bridge configured
* Returns: 0 on success, -1 on failure
*/
int (*if_add)(void *priv, enum wpa_driver_if_type type,
const char *ifname, const u8 *addr, void *bss_ctx,
- void **drv_priv, char *force_ifname, u8 *if_addr);
+ void **drv_priv, char *force_ifname, u8 *if_addr,
+ const char *bridge);
/**
* if_remove - Remove a virtual interface
@@ -1578,33 +1904,36 @@ struct wpa_driver_ops {
int (*set_radius_acl_expire)(void *priv, const u8 *mac);
/**
- * set_ht_params - Set HT parameters (AP only)
- * @priv: Private driver interface data
- * @ht_capab: HT Capabilities IE
- * @ht_capab_len: Length of ht_capab in octets
- * @ht_oper: HT Operation IE
- * @ht_oper_len: Length of ht_oper in octets
- * Returns: 0 on success, -1 on failure
- */
- int (*set_ht_params)(void *priv,
- const u8 *ht_capab, size_t ht_capab_len,
- const u8 *ht_oper, size_t ht_oper_len);
-
- /**
* set_ap_wps_ie - Add WPS IE(s) into Beacon/Probe Response frames (AP)
* @priv: Private driver interface data
* @beacon: WPS IE(s) for Beacon frames or %NULL to remove extra IE(s)
* @proberesp: WPS IE(s) for Probe Response frames or %NULL to remove
* extra IE(s)
+ * @assocresp: WPS IE(s) for (Re)Association Response frames or %NULL
+ * to remove extra IE(s)
* Returns: 0 on success, -1 on failure
*
* This is an optional function to add WPS IE in the kernel driver for
* Beacon and Probe Response frames. This can be left undefined (set
- * to %NULL) if the driver uses the Beacon template from set_beacon()
- * and does not process Probe Request frames.
+ * to %NULL) if the driver uses the Beacon template from set_ap()
+ * and does not process Probe Request frames. If the driver takes care
+ * of (Re)Association frame processing, the assocresp buffer includes
+ * WPS IE(s) that need to be added to (Re)Association Response frames
+ * whenever a (Re)Association Request frame indicated use of WPS.
+ *
+ * This will also be used to add P2P IE(s) into Beacon/Probe Response
+ * frames when operating as a GO. The driver is responsible for adding
+ * timing related attributes (e.g., NoA) in addition to the IEs
+ * included here by appending them after these buffers. This call is
+ * also used to provide Probe Response IEs for P2P Listen state
+ * operations for drivers that generate the Probe Response frames
+ * internally.
+ *
+ * DEPRECATED - use set_ap() instead
*/
int (*set_ap_wps_ie)(void *priv, const struct wpabuf *beacon,
- const struct wpabuf *proberesp);
+ const struct wpabuf *proberesp,
+ const struct wpabuf *assocresp);
/**
* set_supp_port - Set IEEE 802.1X Supplicant Port status
@@ -1620,31 +1949,52 @@ struct wpa_driver_ops {
* @addr: MAC address of the associated station
* @aid: Association ID
* @val: 1 = bind to 4-address WDS; 0 = unbind
+ * @bridge_ifname: Bridge interface to use for the WDS station or %NULL
+ * to indicate that bridge is not to be used
* Returns: 0 on success, -1 on failure
*/
- int (*set_wds_sta)(void *priv, const u8 *addr, int aid, int val);
+ int (*set_wds_sta)(void *priv, const u8 *addr, int aid, int val,
+ const char *bridge_ifname);
/**
* send_action - Transmit an Action frame
* @priv: Private driver interface data
* @freq: Frequency (in MHz) of the channel
+ * @wait: Time to wait off-channel for a response (in ms), or zero
* @dst: Destination MAC address (Address 1)
* @src: Source MAC address (Address 2)
* @bssid: BSSID (Address 3)
* @data: Frame body
* @data_len: data length in octets
+ @ @no_cck: Whether CCK rates must not be used to transmit this frame
* Returns: 0 on success, -1 on failure
*
* This command can be used to request the driver to transmit an action
- * frame to the specified destination. If a remain-on-channel duration
- * is in progress, the frame is transmitted on that channel. Otherwise,
- * the frame is transmitted on the current operational channel if in
- * associated state in station mode or if operating as an AP. If none
- * of these conditions is in effect, send_action() cannot be used.
+ * frame to the specified destination.
+ *
+ * If the %WPA_DRIVER_FLAGS_OFFCHANNEL_TX flag is set, the frame will
+ * be transmitted on the given channel and the device will wait for a
+ * response on that channel for the given wait time.
+ *
+ * If the flag is not set, the wait time will be ignored. In this case,
+ * if a remain-on-channel duration is in progress, the frame must be
+ * transmitted on that channel; alternatively the frame may be sent on
+ * the current operational channel (if in associated state in station
+ * mode or while operating as an AP.)
*/
- int (*send_action)(void *priv, unsigned int freq,
+ int (*send_action)(void *priv, unsigned int freq, unsigned int wait,
const u8 *dst, const u8 *src, const u8 *bssid,
- const u8 *data, size_t data_len);
+ const u8 *data, size_t data_len, int no_cck);
+
+ /**
+ * send_action_cancel_wait - Cancel action frame TX wait
+ * @priv: Private driver interface data
+ *
+ * This command cancels the wait time associated with sending an action
+ * frame. It is only available when %WPA_DRIVER_FLAGS_OFFCHANNEL_TX is
+ * set in the driver flags.
+ */
+ void (*send_action_cancel_wait)(void *priv);
/**
* remain_on_channel - Remain awake on a channel
@@ -1701,28 +2051,25 @@ struct wpa_driver_ops {
int (*probe_req_report)(void *priv, int report);
/**
- * disable_11b_rates - Set whether IEEE 802.11b rates are used for TX
+ * deinit_ap - Deinitialize AP mode
* @priv: Private driver interface data
- * @disabled: Whether IEEE 802.11b rates are disabled
* Returns: 0 on success, -1 on failure (or if not supported)
*
- * This command is used to disable IEEE 802.11b rates (1, 2, 5.5, and
- * 11 Mbps) as TX rates for data and management frames. This can be
- * used to optimize channel use when there is no need to support IEEE
- * 802.11b-only devices.
+ * This optional function can be used to disable AP mode related
+ * configuration and change the driver mode to station mode to allow
+ * normal station operations like scanning to be completed.
*/
- int (*disable_11b_rates)(void *priv, int disabled);
+ int (*deinit_ap)(void *priv);
/**
- * deinit_ap - Deinitialize AP mode
+ * deinit_p2p_cli - Deinitialize P2P client mode
* @priv: Private driver interface data
* Returns: 0 on success, -1 on failure (or if not supported)
*
- * This optional function can be used to disable AP mode related
- * configuration and change the driver mode to station mode to allow
- * normal station operations like scanning to be completed.
+ * This optional function can be used to disable P2P client mode. It
+ * can be used to change the interface type back to station mode.
*/
- int (*deinit_ap)(void *priv);
+ int (*deinit_p2p_cli)(void *priv);
/**
* suspend - Notification on system suspend/hibernate event
@@ -1765,6 +2112,496 @@ struct wpa_driver_ops {
*/
int (*send_frame)(void *priv, const u8 *data, size_t data_len,
int encrypt);
+
+ /**
+ * shared_freq - Get operating frequency of shared interface(s)
+ * @priv: Private driver interface data
+ * Returns: Operating frequency in MHz, 0 if no shared operation in
+ * use, or -1 on failure
+ *
+ * This command can be used to request the current operating frequency
+ * of any virtual interface that shares the same radio to provide
+ * information for channel selection for other virtual interfaces.
+ */
+ int (*shared_freq)(void *priv);
+
+ /**
+ * get_noa - Get current Notice of Absence attribute payload
+ * @priv: Private driver interface data
+ * @buf: Buffer for returning NoA
+ * @buf_len: Buffer length in octets
+ * Returns: Number of octets used in buf, 0 to indicate no NoA is being
+ * advertized, or -1 on failure
+ *
+ * This function is used to fetch the current Notice of Absence
+ * attribute value from GO.
+ */
+ int (*get_noa)(void *priv, u8 *buf, size_t buf_len);
+
+ /**
+ * set_noa - Set Notice of Absence parameters for GO (testing)
+ * @priv: Private driver interface data
+ * @count: Count
+ * @start: Start time in ms from next TBTT
+ * @duration: Duration in ms
+ * Returns: 0 on success or -1 on failure
+ *
+ * This function is used to set Notice of Absence parameters for GO. It
+ * is used only for testing. To disable NoA, all parameters are set to
+ * 0.
+ */
+ int (*set_noa)(void *priv, u8 count, int start, int duration);
+
+ /**
+ * set_p2p_powersave - Set P2P power save options
+ * @priv: Private driver interface data
+ * @legacy_ps: 0 = disable, 1 = enable, 2 = maximum PS, -1 = no change
+ * @opp_ps: 0 = disable, 1 = enable, -1 = no change
+ * @ctwindow: 0.. = change (msec), -1 = no change
+ * Returns: 0 on success or -1 on failure
+ */
+ int (*set_p2p_powersave)(void *priv, int legacy_ps, int opp_ps,
+ int ctwindow);
+
+ /**
+ * ampdu - Enable/disable aggregation
+ * @priv: Private driver interface data
+ * @ampdu: 1/0 = enable/disable A-MPDU aggregation
+ * Returns: 0 on success or -1 on failure
+ */
+ int (*ampdu)(void *priv, int ampdu);
+
+ /**
+ * get_radio_name - Get physical radio name for the device
+ * @priv: Private driver interface data
+ * Returns: Radio name or %NULL if not known
+ *
+ * The returned data must not be modified by the caller. It is assumed
+ * that any interface that has the same radio name as another is
+ * sharing the same physical radio. This information can be used to
+ * share scan results etc. information between the virtual interfaces
+ * to speed up various operations.
+ */
+ const char * (*get_radio_name)(void *priv);
+
+ /**
+ * p2p_find - Start P2P Device Discovery
+ * @priv: Private driver interface data
+ * @timeout: Timeout for find operation in seconds or 0 for no timeout
+ * @type: Device Discovery type (enum p2p_discovery_type)
+ * Returns: 0 on success, -1 on failure
+ *
+ * This function is only used if the driver implements P2P management,
+ * i.e., if it sets WPA_DRIVER_FLAGS_P2P_MGMT in
+ * struct wpa_driver_capa.
+ */
+ int (*p2p_find)(void *priv, unsigned int timeout, int type);
+
+ /**
+ * p2p_stop_find - Stop P2P Device Discovery
+ * @priv: Private driver interface data
+ * Returns: 0 on success, -1 on failure
+ *
+ * This function is only used if the driver implements P2P management,
+ * i.e., if it sets WPA_DRIVER_FLAGS_P2P_MGMT in
+ * struct wpa_driver_capa.
+ */
+ int (*p2p_stop_find)(void *priv);
+
+ /**
+ * p2p_listen - Start P2P Listen state for specified duration
+ * @priv: Private driver interface data
+ * @timeout: Listen state duration in milliseconds
+ * Returns: 0 on success, -1 on failure
+ *
+ * This function can be used to request the P2P module to keep the
+ * device discoverable on the listen channel for an extended set of
+ * time. At least in its current form, this is mainly used for testing
+ * purposes and may not be of much use for normal P2P operations.
+ *
+ * This function is only used if the driver implements P2P management,
+ * i.e., if it sets WPA_DRIVER_FLAGS_P2P_MGMT in
+ * struct wpa_driver_capa.
+ */
+ int (*p2p_listen)(void *priv, unsigned int timeout);
+
+ /**
+ * p2p_connect - Start P2P group formation (GO negotiation)
+ * @priv: Private driver interface data
+ * @peer_addr: MAC address of the peer P2P client
+ * @wps_method: enum p2p_wps_method value indicating config method
+ * @go_intent: Local GO intent value (1..15)
+ * @own_interface_addr: Intended interface address to use with the
+ * group
+ * @force_freq: The only allowed channel frequency in MHz or 0
+ * @persistent_group: Whether to create persistent group
+ * Returns: 0 on success, -1 on failure
+ *
+ * This function is only used if the driver implements P2P management,
+ * i.e., if it sets WPA_DRIVER_FLAGS_P2P_MGMT in
+ * struct wpa_driver_capa.
+ */
+ int (*p2p_connect)(void *priv, const u8 *peer_addr, int wps_method,
+ int go_intent, const u8 *own_interface_addr,
+ unsigned int force_freq, int persistent_group);
+
+ /**
+ * wps_success_cb - Report successfully completed WPS provisioning
+ * @priv: Private driver interface data
+ * @peer_addr: Peer address
+ * Returns: 0 on success, -1 on failure
+ *
+ * This function is used to report successfully completed WPS
+ * provisioning during group formation in both GO/Registrar and
+ * client/Enrollee roles.
+ *
+ * This function is only used if the driver implements P2P management,
+ * i.e., if it sets WPA_DRIVER_FLAGS_P2P_MGMT in
+ * struct wpa_driver_capa.
+ */
+ int (*wps_success_cb)(void *priv, const u8 *peer_addr);
+
+ /**
+ * p2p_group_formation_failed - Report failed WPS provisioning
+ * @priv: Private driver interface data
+ * Returns: 0 on success, -1 on failure
+ *
+ * This function is used to report failed group formation. This can
+ * happen either due to failed WPS provisioning or due to 15 second
+ * timeout during the provisioning phase.
+ *
+ * This function is only used if the driver implements P2P management,
+ * i.e., if it sets WPA_DRIVER_FLAGS_P2P_MGMT in
+ * struct wpa_driver_capa.
+ */
+ int (*p2p_group_formation_failed)(void *priv);
+
+ /**
+ * p2p_set_params - Set P2P parameters
+ * @priv: Private driver interface data
+ * @params: P2P parameters
+ * Returns: 0 on success, -1 on failure
+ *
+ * This function is only used if the driver implements P2P management,
+ * i.e., if it sets WPA_DRIVER_FLAGS_P2P_MGMT in
+ * struct wpa_driver_capa.
+ */
+ int (*p2p_set_params)(void *priv, const struct p2p_params *params);
+
+ /**
+ * p2p_prov_disc_req - Send Provision Discovery Request
+ * @priv: Private driver interface data
+ * @peer_addr: MAC address of the peer P2P client
+ * @config_methods: WPS Config Methods value (only one bit set)
+ * Returns: 0 on success, -1 on failure
+ *
+ * This function can be used to request a discovered P2P peer to
+ * display a PIN (config_methods = WPS_CONFIG_DISPLAY) or be prepared
+ * to enter a PIN from us (config_methods = WPS_CONFIG_KEYPAD). The
+ * Provision Discovery Request frame is transmitted once immediately
+ * and if no response is received, the frame will be sent again
+ * whenever the target device is discovered during device dsicovery
+ * (start with a p2p_find() call). Response from the peer is indicated
+ * with the EVENT_P2P_PROV_DISC_RESPONSE event.
+ *
+ * This function is only used if the driver implements P2P management,
+ * i.e., if it sets WPA_DRIVER_FLAGS_P2P_MGMT in
+ * struct wpa_driver_capa.
+ */
+ int (*p2p_prov_disc_req)(void *priv, const u8 *peer_addr,
+ u16 config_methods, int join);
+
+ /**
+ * p2p_sd_request - Schedule a service discovery query
+ * @priv: Private driver interface data
+ * @dst: Destination peer or %NULL to apply for all peers
+ * @tlvs: P2P Service Query TLV(s)
+ * Returns: Reference to the query or 0 on failure
+ *
+ * Response to the query is indicated with the
+ * EVENT_P2P_SD_RESPONSE driver event.
+ *
+ * This function is only used if the driver implements P2P management,
+ * i.e., if it sets WPA_DRIVER_FLAGS_P2P_MGMT in
+ * struct wpa_driver_capa.
+ */
+ u64 (*p2p_sd_request)(void *priv, const u8 *dst,
+ const struct wpabuf *tlvs);
+
+ /**
+ * p2p_sd_cancel_request - Cancel a pending service discovery query
+ * @priv: Private driver interface data
+ * @req: Query reference from p2p_sd_request()
+ * Returns: 0 on success, -1 on failure
+ *
+ * This function is only used if the driver implements P2P management,
+ * i.e., if it sets WPA_DRIVER_FLAGS_P2P_MGMT in
+ * struct wpa_driver_capa.
+ */
+ int (*p2p_sd_cancel_request)(void *priv, u64 req);
+
+ /**
+ * p2p_sd_response - Send response to a service discovery query
+ * @priv: Private driver interface data
+ * @freq: Frequency from EVENT_P2P_SD_REQUEST event
+ * @dst: Destination address from EVENT_P2P_SD_REQUEST event
+ * @dialog_token: Dialog token from EVENT_P2P_SD_REQUEST event
+ * @resp_tlvs: P2P Service Response TLV(s)
+ * Returns: 0 on success, -1 on failure
+ *
+ * This function is called as a response to the request indicated with
+ * the EVENT_P2P_SD_REQUEST driver event.
+ *
+ * This function is only used if the driver implements P2P management,
+ * i.e., if it sets WPA_DRIVER_FLAGS_P2P_MGMT in
+ * struct wpa_driver_capa.
+ */
+ int (*p2p_sd_response)(void *priv, int freq, const u8 *dst,
+ u8 dialog_token,
+ const struct wpabuf *resp_tlvs);
+
+ /**
+ * p2p_service_update - Indicate a change in local services
+ * @priv: Private driver interface data
+ * Returns: 0 on success, -1 on failure
+ *
+ * This function needs to be called whenever there is a change in
+ * availability of the local services. This will increment the
+ * Service Update Indicator value which will be used in SD Request and
+ * Response frames.
+ *
+ * This function is only used if the driver implements P2P management,
+ * i.e., if it sets WPA_DRIVER_FLAGS_P2P_MGMT in
+ * struct wpa_driver_capa.
+ */
+ int (*p2p_service_update)(void *priv);
+
+ /**
+ * p2p_reject - Reject peer device (explicitly block connections)
+ * @priv: Private driver interface data
+ * @addr: MAC address of the peer
+ * Returns: 0 on success, -1 on failure
+ */
+ int (*p2p_reject)(void *priv, const u8 *addr);
+
+ /**
+ * p2p_invite - Invite a P2P Device into a group
+ * @priv: Private driver interface data
+ * @peer: Device Address of the peer P2P Device
+ * @role: Local role in the group
+ * @bssid: Group BSSID or %NULL if not known
+ * @ssid: Group SSID
+ * @ssid_len: Length of ssid in octets
+ * @go_dev_addr: Forced GO Device Address or %NULL if none
+ * @persistent_group: Whether this is to reinvoke a persistent group
+ * Returns: 0 on success, -1 on failure
+ */
+ int (*p2p_invite)(void *priv, const u8 *peer, int role,
+ const u8 *bssid, const u8 *ssid, size_t ssid_len,
+ const u8 *go_dev_addr, int persistent_group);
+
+ /**
+ * send_tdls_mgmt - for sending TDLS management packets
+ * @priv: private driver interface data
+ * @dst: Destination (peer) MAC address
+ * @action_code: TDLS action code for the mssage
+ * @dialog_token: Dialog Token to use in the message (if needed)
+ * @status_code: Status Code or Reason Code to use (if needed)
+ * @buf: TDLS IEs to add to the message
+ * @len: Length of buf in octets
+ * Returns: 0 on success, negative (<0) on failure
+ *
+ * This optional function can be used to send packet to driver which is
+ * responsible for receiving and sending all TDLS packets.
+ */
+ int (*send_tdls_mgmt)(void *priv, const u8 *dst, u8 action_code,
+ u8 dialog_token, u16 status_code,
+ const u8 *buf, size_t len);
+
+ /**
+ * tdls_oper - Ask the driver to perform high-level TDLS operations
+ * @priv: Private driver interface data
+ * @oper: TDLS high-level operation. See %enum tdls_oper
+ * @peer: Destination (peer) MAC address
+ * Returns: 0 on success, negative (<0) on failure
+ *
+ * This optional function can be used to send high-level TDLS commands
+ * to the driver.
+ */
+ int (*tdls_oper)(void *priv, enum tdls_oper oper, const u8 *peer);
+
+ /**
+ * wnm_oper - Notify driver of the WNM frame reception
+ * @priv: Private driver interface data
+ * @oper: WNM operation. See %enum wnm_oper
+ * @peer: Destination (peer) MAC address
+ * @buf: Buffer for the driver to fill in (for getting IE)
+ * @buf_len: Return the len of buf
+ * Returns: 0 on success, negative (<0) on failure
+ */
+ int (*wnm_oper)(void *priv, enum wnm_oper oper, const u8 *peer,
+ u8 *buf, u16 *buf_len);
+
+ /**
+ * signal_poll - Get current connection information
+ * @priv: Private driver interface data
+ * @signal_info: Connection info structure
+ */
+ int (*signal_poll)(void *priv, struct wpa_signal_info *signal_info);
+
+ /**
+ * set_authmode - Set authentication algorithm(s) for static WEP
+ * @priv: Private driver interface data
+ * @authmode: 1=Open System, 2=Shared Key, 3=both
+ * Returns: 0 on success, -1 on failure
+ *
+ * This function can be used to set authentication algorithms for AP
+ * mode when static WEP is used. If the driver uses user space MLME/SME
+ * implementation, there is no need to implement this function.
+ *
+ * DEPRECATED - use set_ap() instead
+ */
+ int (*set_authmode)(void *priv, int authmode);
+
+ /**
+ * set_rekey_info - Set rekey information
+ * @priv: Private driver interface data
+ * @kek: Current KEK
+ * @kck: Current KCK
+ * @replay_ctr: Current EAPOL-Key Replay Counter
+ *
+ * This optional function can be used to provide information for the
+ * driver/firmware to process EAPOL-Key frames in Group Key Handshake
+ * while the host (including wpa_supplicant) is sleeping.
+ */
+ void (*set_rekey_info)(void *priv, const u8 *kek, const u8 *kck,
+ const u8 *replay_ctr);
+
+ /**
+ * sta_assoc - Station association indication
+ * @priv: Private driver interface data
+ * @own_addr: Source address and BSSID for association frame
+ * @addr: MAC address of the station to associate
+ * @reassoc: flag to indicate re-association
+ * @status: association response status code
+ * @ie: assoc response ie buffer
+ * @len: ie buffer length
+ * Returns: 0 on success, -1 on failure
+ *
+ * This function indicates the driver to send (Re)Association
+ * Response frame to the station.
+ */
+ int (*sta_assoc)(void *priv, const u8 *own_addr, const u8 *addr,
+ int reassoc, u16 status, const u8 *ie, size_t len);
+
+ /**
+ * sta_auth - Station authentication indication
+ * @priv: Private driver interface data
+ * @own_addr: Source address and BSSID for authentication frame
+ * @addr: MAC address of the station to associate
+ * @seq: authentication sequence number
+ * @status: authentication response status code
+ * @ie: authentication frame ie buffer
+ * @len: ie buffer length
+ *
+ * This function indicates the driver to send Authentication frame
+ * to the station.
+ */
+ int (*sta_auth)(void *priv, const u8 *own_addr, const u8 *addr,
+ u16 seq, u16 status, const u8 *ie, size_t len);
+
+ /**
+ * add_tspec - Add traffic stream
+ * @priv: Private driver interface data
+ * @addr: MAC address of the station to associate
+ * @tspec_ie: tspec ie buffer
+ * @tspec_ielen: tspec ie length
+ * Returns: 0 on success, -1 on failure
+ *
+ * This function adds the traffic steam for the station
+ * and fills the medium_time in tspec_ie.
+ */
+ int (*add_tspec)(void *priv, const u8 *addr, u8 *tspec_ie,
+ size_t tspec_ielen);
+
+ /**
+ * add_sta_node - Add a station node in the driver
+ * @priv: Private driver interface data
+ * @addr: MAC address of the station to add
+ * @auth_alg: authentication algorithm used by the station
+ * Returns: 0 on success, -1 on failure
+ *
+ * This function adds the station node in the driver, when
+ * the station gets added by FT-over-DS.
+ */
+ int (*add_sta_node)(void *priv, const u8 *addr, u16 auth_alg);
+
+ /**
+ * sched_scan - Request the driver to initiate scheduled scan
+ * @priv: Private driver interface data
+ * @params: Scan parameters
+ * @interval: Interval between scan cycles in milliseconds
+ * Returns: 0 on success, -1 on failure
+ *
+ * This operation should be used for scheduled scan offload to
+ * the hardware. Every time scan results are available, the
+ * driver should report scan results event for wpa_supplicant
+ * which will eventually request the results with
+ * wpa_driver_get_scan_results2(). This operation is optional
+ * and if not provided or if it returns -1, we fall back to
+ * normal host-scheduled scans.
+ */
+ int (*sched_scan)(void *priv, struct wpa_driver_scan_params *params,
+ u32 interval);
+
+ /**
+ * stop_sched_scan - Request the driver to stop a scheduled scan
+ * @priv: Private driver interface data
+ * Returns: 0 on success, -1 on failure
+ *
+ * This should cause the scheduled scan to be stopped and
+ * results should stop being sent. Must be supported if
+ * sched_scan is supported.
+ */
+ int (*stop_sched_scan)(void *priv);
+
+ /**
+ * poll_client - Probe (null data or such) the given station
+ * @priv: Private driver interface data
+ * @own_addr: MAC address of sending interface
+ * @addr: MAC address of the station to probe
+ * @qos: Indicates whether station is QoS station
+ *
+ * This function is used to verify whether an associated station is
+ * still present. This function does not need to be implemented if the
+ * driver provides such inactivity polling mechanism.
+ */
+ void (*poll_client)(void *priv, const u8 *own_addr,
+ const u8 *addr, int qos);
+
+ /**
+ * radio_disable - Disable/enable radio
+ * @priv: Private driver interface data
+ * @disabled: 1=disable 0=enable radio
+ * Returns: 0 on success, -1 on failure
+ *
+ * This optional command is for testing purposes. It can be used to
+ * disable the radio on a testbed device to simulate out-of-radio-range
+ * conditions.
+ */
+ int (*radio_disable)(void *priv, int disabled);
+
+ /**
+ * switch_channel - Announce channel switch and migrate the GO to the
+ * given frequency
+ * @priv: Private driver interface data
+ * @freq: Frequency in MHz
+ * Returns: 0 on success, -1 on failure
+ *
+ * This function is used to move the GO to the legacy STA channel to
+ * avoid frequency conflict in single channel concurrency.
+ */
+ int (*switch_channel)(void *priv, unsigned int freq);
};
@@ -1887,6 +2724,13 @@ enum wpa_event_type {
EVENT_STKSTART,
/**
+ * EVENT_TDLS - Request TDLS operation
+ *
+ * This event can be used to request a TDLS operation to be performed.
+ */
+ EVENT_TDLS,
+
+ /**
* EVENT_FT_RESPONSE - Report FT (IEEE 802.11r) response IEs
*
* The driver is expected to report the received FT IEs from
@@ -1930,7 +2774,7 @@ enum wpa_event_type {
* EVENT_ASSOC_REJECT - Association rejected
*
* This event should be called when (re)association attempt has been
- * rejected by the AP. Information about authentication result is
+ * rejected by the AP. Information about the association response is
* included in union wpa_event_data::assoc_reject.
*/
EVENT_ASSOC_REJECT,
@@ -2046,7 +2890,163 @@ enum wpa_event_type {
* observed in frames received from the current AP if signal strength
* monitoring has been enabled with signal_monitor().
*/
- EVENT_SIGNAL_CHANGE
+ EVENT_SIGNAL_CHANGE,
+
+ /**
+ * EVENT_INTERFACE_ENABLED - Notify that interface was enabled
+ *
+ * This event is used to indicate that the interface was enabled after
+ * having been previously disabled, e.g., due to rfkill.
+ */
+ EVENT_INTERFACE_ENABLED,
+
+ /**
+ * EVENT_INTERFACE_DISABLED - Notify that interface was disabled
+ *
+ * This event is used to indicate that the interface was disabled,
+ * e.g., due to rfkill.
+ */
+ EVENT_INTERFACE_DISABLED,
+
+ /**
+ * EVENT_CHANNEL_LIST_CHANGED - Channel list changed
+ *
+ * This event is used to indicate that the channel list has changed,
+ * e.g., because of a regulatory domain change triggered by scan
+ * results including an AP advertising a country code.
+ */
+ EVENT_CHANNEL_LIST_CHANGED,
+
+ /**
+ * EVENT_INTERFACE_UNAVAILABLE - Notify that interface is unavailable
+ *
+ * This event is used to indicate that the driver cannot maintain this
+ * interface in its operation mode anymore. The most likely use for
+ * this is to indicate that AP mode operation is not available due to
+ * operating channel would need to be changed to a DFS channel when
+ * the driver does not support radar detection and another virtual
+ * interfaces caused the operating channel to change. Other similar
+ * resource conflicts could also trigger this for station mode
+ * interfaces.
+ */
+ EVENT_INTERFACE_UNAVAILABLE,
+
+ /**
+ * EVENT_BEST_CHANNEL
+ *
+ * Driver generates this event whenever it detects a better channel
+ * (e.g., based on RSSI or channel use). This information can be used
+ * to improve channel selection for a new AP/P2P group.
+ */
+ EVENT_BEST_CHANNEL,
+
+ /**
+ * EVENT_UNPROT_DEAUTH - Unprotected Deauthentication frame received
+ *
+ * This event should be called when a Deauthentication frame is dropped
+ * due to it not being protected (MFP/IEEE 802.11w).
+ * union wpa_event_data::unprot_deauth is required to provide more
+ * details of the frame.
+ */
+ EVENT_UNPROT_DEAUTH,
+
+ /**
+ * EVENT_UNPROT_DISASSOC - Unprotected Disassociation frame received
+ *
+ * This event should be called when a Disassociation frame is dropped
+ * due to it not being protected (MFP/IEEE 802.11w).
+ * union wpa_event_data::unprot_disassoc is required to provide more
+ * details of the frame.
+ */
+ EVENT_UNPROT_DISASSOC,
+
+ /**
+ * EVENT_STATION_LOW_ACK
+ *
+ * Driver generates this event whenever it detected that a particular
+ * station was lost. Detection can be through massive transmission
+ * failures for example.
+ */
+ EVENT_STATION_LOW_ACK,
+
+ /**
+ * EVENT_P2P_DEV_FOUND - Report a discovered P2P device
+ *
+ * This event is used only if the driver implements P2P management
+ * internally. Event data is stored in
+ * union wpa_event_data::p2p_dev_found.
+ */
+ EVENT_P2P_DEV_FOUND,
+
+ /**
+ * EVENT_P2P_GO_NEG_REQ_RX - Report reception of GO Negotiation Request
+ *
+ * This event is used only if the driver implements P2P management
+ * internally. Event data is stored in
+ * union wpa_event_data::p2p_go_neg_req_rx.
+ */
+ EVENT_P2P_GO_NEG_REQ_RX,
+
+ /**
+ * EVENT_P2P_GO_NEG_COMPLETED - Report completion of GO Negotiation
+ *
+ * This event is used only if the driver implements P2P management
+ * internally. Event data is stored in
+ * union wpa_event_data::p2p_go_neg_completed.
+ */
+ EVENT_P2P_GO_NEG_COMPLETED,
+
+ EVENT_P2P_PROV_DISC_REQUEST,
+ EVENT_P2P_PROV_DISC_RESPONSE,
+ EVENT_P2P_SD_REQUEST,
+ EVENT_P2P_SD_RESPONSE,
+
+ /**
+ * EVENT_IBSS_PEER_LOST - IBSS peer not reachable anymore
+ */
+ EVENT_IBSS_PEER_LOST,
+
+ /**
+ * EVENT_DRIVER_GTK_REKEY - Device/driver did GTK rekey
+ *
+ * This event carries the new replay counter to notify wpa_supplicant
+ * of the current EAPOL-Key Replay Counter in case the driver/firmware
+ * completed Group Key Handshake while the host (including
+ * wpa_supplicant was sleeping).
+ */
+ EVENT_DRIVER_GTK_REKEY,
+
+ /**
+ * EVENT_SCHED_SCAN_STOPPED - Scheduled scan was stopped
+ */
+ EVENT_SCHED_SCAN_STOPPED,
+
+ /**
+ * EVENT_DRIVER_CLIENT_POLL_OK - Station responded to poll
+ *
+ * This event indicates that the station responded to the poll
+ * initiated with @poll_client.
+ */
+ EVENT_DRIVER_CLIENT_POLL_OK,
+
+ /**
+ * EVENT_EAPOL_TX_STATUS - notify of EAPOL TX status
+ */
+ EVENT_EAPOL_TX_STATUS,
+
+ /**
+ * EVENT_CH_SWITCH - AP or GO decided to switch channels
+ *
+ * Described in wpa_event_data.ch_switch
+ * */
+ EVENT_CH_SWITCH,
+
+ /**
+ * EVENT_WNM - Request WNM operation
+ *
+ * This event can be used to request a WNM operation to be performed.
+ */
+ EVENT_WNM
};
@@ -2064,6 +3064,11 @@ union wpa_event_data {
*/
struct assoc_info {
/**
+ * reassoc - Flag to indicate association or reassociation
+ */
+ int reassoc;
+
+ /**
* req_ies - (Re)Association Request IEs
*
* If the driver generates WPA/RSN IE, this event data must be
@@ -2146,6 +3151,21 @@ union wpa_event_data {
* Deauthentication frame
*/
u16 reason_code;
+
+ /**
+ * ie - Optional IE(s) in Disassociation frame
+ */
+ const u8 *ie;
+
+ /**
+ * ie_len - Length of ie buffer in octets
+ */
+ size_t ie_len;
+
+ /**
+ * locally_generated - Whether the frame was locally generated
+ */
+ int locally_generated;
} disassoc_info;
/**
@@ -2162,6 +3182,21 @@ union wpa_event_data {
* Deauthentication frame
*/
u16 reason_code;
+
+ /**
+ * ie - Optional IE(s) in Deauthentication frame
+ */
+ const u8 *ie;
+
+ /**
+ * ie_len - Length of ie buffer in octets
+ */
+ size_t ie_len;
+
+ /**
+ * locally_generated - Whether the frame was locally generated
+ */
+ int locally_generated;
} deauth_info;
/**
@@ -2202,6 +3237,36 @@ union wpa_event_data {
} stkstart;
/**
+ * struct tdls - Data for EVENT_TDLS
+ */
+ struct tdls {
+ u8 peer[ETH_ALEN];
+ enum {
+ TDLS_REQUEST_SETUP,
+ TDLS_REQUEST_TEARDOWN
+ } oper;
+ u16 reason_code; /* for teardown */
+ } tdls;
+
+ /**
+ * struct wnm - Data for EVENT_WNM
+ */
+ struct wnm {
+ u8 addr[ETH_ALEN];
+ enum {
+ WNM_OPER_SLEEP,
+ } oper;
+ enum {
+ WNM_SLEEP_ENTER,
+ WNM_SLEEP_EXIT
+ } sleep_action;
+ int sleep_intval;
+ u16 reason_code;
+ u8 *buf;
+ u16 buf_len;
+ } wnm;
+
+ /**
* struct ft_ies - FT information elements (EVENT_FT_RESPONSE)
*
* During FT (IEEE 802.11r) authentication sequence, the driver is
@@ -2233,7 +3298,9 @@ union wpa_event_data {
*/
struct auth_info {
u8 peer[ETH_ALEN];
+ u8 bssid[ETH_ALEN];
u16 auth_type;
+ u16 auth_transaction;
u16 status_code;
const u8 *ies;
size_t ies_len;
@@ -2244,6 +3311,11 @@ union wpa_event_data {
*/
struct assoc_reject {
/**
+ * bssid - BSSID of the AP that rejected association
+ */
+ const u8 *bssid;
+
+ /**
* resp_ies - (Re)Association Response IEs
*
* Optional association data from the driver. This data is not
@@ -2254,7 +3326,7 @@ union wpa_event_data {
* This should start with the first IE (fixed fields before IEs
* are not included).
*/
- u8 *resp_ies;
+ const u8 *resp_ies;
/**
* resp_ies_len - Length of resp_ies in bytes
@@ -2296,8 +3368,9 @@ union wpa_event_data {
* struct rx_from_unknown - Data for EVENT_RX_FROM_UNKNOWN events
*/
struct rx_from_unknown {
- const u8 *frame;
- size_t len;
+ const u8 *bssid;
+ const u8 *addr;
+ int wds;
} rx_from_unknown;
/**
@@ -2307,7 +3380,7 @@ union wpa_event_data {
const u8 *frame;
size_t frame_len;
u32 datarate;
- u32 ssi_signal;
+ int ssi_signal; /* dBm */
} rx_mgmt;
/**
@@ -2405,6 +3478,18 @@ union wpa_event_data {
const u8 *sa;
/**
+ * da - Destination address of the received Probe Request frame
+ * or %NULL if not available
+ */
+ const u8 *da;
+
+ /**
+ * bssid - BSSID of the received Probe Request frame or %NULL
+ * if not available
+ */
+ const u8 *bssid;
+
+ /**
* ie - IEs from the Probe Request body
*/
const u8 *ie;
@@ -2413,6 +3498,11 @@ union wpa_event_data {
* ie_len - Length of ie buffer in octets
*/
size_t ie_len;
+
+ /**
+ * signal - signal strength in dBm (or 0 if not available)
+ */
+ int ssi_signal;
} rx_probe_req;
/**
@@ -2432,11 +3522,155 @@ union wpa_event_data {
} eapol_rx;
/**
- * struct signal_change - Data for EVENT_SIGNAL_CHANGE events
+ * signal_change - Data for EVENT_SIGNAL_CHANGE events
+ */
+ struct wpa_signal_info signal_change;
+
+ /**
+ * struct best_channel - Data for EVENT_BEST_CHANNEL events
+ * @freq_24: Best 2.4 GHz band channel frequency in MHz
+ * @freq_5: Best 5 GHz band channel frequency in MHz
+ * @freq_overall: Best channel frequency in MHz
+ *
+ * 0 can be used to indicate no preference in either band.
+ */
+ struct best_channel {
+ int freq_24;
+ int freq_5;
+ int freq_overall;
+ } best_chan;
+
+ struct unprot_deauth {
+ const u8 *sa;
+ const u8 *da;
+ u16 reason_code;
+ } unprot_deauth;
+
+ struct unprot_disassoc {
+ const u8 *sa;
+ const u8 *da;
+ u16 reason_code;
+ } unprot_disassoc;
+
+ /**
+ * struct low_ack - Data for EVENT_STATION_LOW_ACK events
+ * @addr: station address
+ */
+ struct low_ack {
+ u8 addr[ETH_ALEN];
+ } low_ack;
+
+ /**
+ * struct p2p_dev_found - Data for EVENT_P2P_DEV_FOUND
+ */
+ struct p2p_dev_found {
+ const u8 *addr;
+ const u8 *dev_addr;
+ const u8 *pri_dev_type;
+ const char *dev_name;
+ u16 config_methods;
+ u8 dev_capab;
+ u8 group_capab;
+ } p2p_dev_found;
+
+ /**
+ * struct p2p_go_neg_req_rx - Data for EVENT_P2P_GO_NEG_REQ_RX
+ */
+ struct p2p_go_neg_req_rx {
+ const u8 *src;
+ u16 dev_passwd_id;
+ } p2p_go_neg_req_rx;
+
+ /**
+ * struct p2p_go_neg_completed - Data for EVENT_P2P_GO_NEG_COMPLETED
+ */
+ struct p2p_go_neg_completed {
+ struct p2p_go_neg_results *res;
+ } p2p_go_neg_completed;
+
+ struct p2p_prov_disc_req {
+ const u8 *peer;
+ u16 config_methods;
+ const u8 *dev_addr;
+ const u8 *pri_dev_type;
+ const char *dev_name;
+ u16 supp_config_methods;
+ u8 dev_capab;
+ u8 group_capab;
+ } p2p_prov_disc_req;
+
+ struct p2p_prov_disc_resp {
+ const u8 *peer;
+ u16 config_methods;
+ } p2p_prov_disc_resp;
+
+ struct p2p_sd_req {
+ int freq;
+ const u8 *sa;
+ u8 dialog_token;
+ u16 update_indic;
+ const u8 *tlvs;
+ size_t tlvs_len;
+ } p2p_sd_req;
+
+ struct p2p_sd_resp {
+ const u8 *sa;
+ u16 update_indic;
+ const u8 *tlvs;
+ size_t tlvs_len;
+ } p2p_sd_resp;
+
+ /**
+ * struct ibss_peer_lost - Data for EVENT_IBSS_PEER_LOST
+ */
+ struct ibss_peer_lost {
+ u8 peer[ETH_ALEN];
+ } ibss_peer_lost;
+
+ /**
+ * struct driver_gtk_rekey - Data for EVENT_DRIVER_GTK_REKEY
*/
- struct signal_change {
- int above_threshold;
- } signal_change;
+ struct driver_gtk_rekey {
+ const u8 *bssid;
+ const u8 *replay_ctr;
+ } driver_gtk_rekey;
+
+ /**
+ * struct client_poll - Data for EVENT_DRIVER_CLIENT_POLL_OK events
+ * @addr: station address
+ */
+ struct client_poll {
+ u8 addr[ETH_ALEN];
+ } client_poll;
+
+ /**
+ * struct eapol_tx_status
+ * @dst: Original destination
+ * @data: Data starting with IEEE 802.1X header (!)
+ * @data_len: Length of data
+ * @ack: Indicates ack or lost frame
+ *
+ * This corresponds to hapd_send_eapol if the frame sent
+ * there isn't just reported as EVENT_TX_STATUS.
+ */
+ struct eapol_tx_status {
+ const u8 *dst;
+ const u8 *data;
+ int data_len;
+ int ack;
+ } eapol_tx_status;
+
+ /**
+ * struct ch_switch
+ * @freq: Frequency of new channel in MHz
+ * @ht_enabled: Whether this is an HT channel
+ * @ch_offset: Secondary channel offset
+ */
+ struct ch_switch {
+ int freq;
+ int ht_enabled;
+ int ch_offset;
+ } ch_switch;
};
/**
@@ -2459,10 +3693,11 @@ void wpa_supplicant_event(void *ctx, enum wpa_event_type event,
*/
static inline void drv_event_assoc(void *ctx, const u8 *addr, const u8 *ie,
- size_t ielen)
+ size_t ielen, int reassoc)
{
union wpa_event_data event;
os_memset(&event, 0, sizeof(event));
+ event.assoc_info.reassoc = reassoc;
event.assoc_info.req_ies = ie;
event.assoc_info.req_ies_len = ielen;
event.assoc_info.addr = addr;
@@ -2488,4 +3723,10 @@ static inline void drv_event_eapol_rx(void *ctx, const u8 *src, const u8 *data,
wpa_supplicant_event(ctx, EVENT_EAPOL_RX, &event);
}
+/* driver_common.c */
+void wpa_scan_results_free(struct wpa_scan_results *res);
+
+/* Convert wpa_event_type to a string for logging */
+const char * event_to_string(enum wpa_event_type event);
+
#endif /* DRIVER_H */
diff --git a/contrib/wpa/src/drivers/driver_bsd.c b/contrib/wpa/src/drivers/driver_bsd.c
new file mode 100644
index 0000000..13ca628
--- /dev/null
+++ b/contrib/wpa/src/drivers/driver_bsd.c
@@ -0,0 +1,1627 @@
+/*
+ * WPA Supplicant - driver interaction with BSD net80211 layer
+ * Copyright (c) 2004, Sam Leffler <sam@errno.com>
+ * Copyright (c) 2004, 2Wire, Inc
+ *
+ * This software may be distributed under the terms of the BSD license.
+ * See README for more details.
+ */
+
+#include "includes.h"
+#include <sys/ioctl.h>
+#include <sys/sysctl.h>
+
+#include "common.h"
+#include "driver.h"
+#include "eloop.h"
+#include "common/ieee802_11_defs.h"
+#include "common/wpa_common.h"
+
+#include <net/if.h>
+#include <net/if_media.h>
+
+#ifdef __NetBSD__
+#include <net/if_ether.h>
+#else
+#include <net/ethernet.h>
+#endif
+#include <net/route.h>
+
+#ifdef __DragonFly__
+#include <netproto/802_11/ieee80211_ioctl.h>
+#include <netproto/802_11/ieee80211_dragonfly.h>
+#else /* __DragonFly__ */
+#ifdef __GLIBC__
+#include <netinet/ether.h>
+#endif /* __GLIBC__ */
+#include <net80211/ieee80211.h>
+#include <net80211/ieee80211_ioctl.h>
+#include <net80211/ieee80211_crypto.h>
+#endif /* __DragonFly__ || __GLIBC__ */
+#if defined(__FreeBSD__) || defined(__FreeBSD_kernel__)
+#include <net80211/ieee80211_freebsd.h>
+#endif
+#if __NetBSD__
+#include <net80211/ieee80211_netbsd.h>
+#endif
+
+#include "l2_packet/l2_packet.h"
+
+struct bsd_driver_data {
+ struct hostapd_data *hapd; /* back pointer */
+
+ int sock; /* open socket for 802.11 ioctls */
+ struct l2_packet_data *sock_xmit;/* raw packet xmit socket */
+ int route; /* routing socket for events */
+ char ifname[IFNAMSIZ+1]; /* interface name */
+ unsigned int ifindex; /* interface index */
+ void *ctx;
+ struct wpa_driver_capa capa; /* driver capability */
+ int is_ap; /* Access point mode */
+ int prev_roaming; /* roaming state to restore on deinit */
+ int prev_privacy; /* privacy state to restore on deinit */
+ int prev_wpa; /* wpa state to restore on deinit */
+ enum ieee80211_opmode opmode; /* operation mode */
+};
+
+/* Generic functions for hostapd and wpa_supplicant */
+
+static enum ieee80211_opmode
+get80211opmode(struct bsd_driver_data *drv)
+{
+ struct ifmediareq ifmr;
+
+ (void) memset(&ifmr, 0, sizeof(ifmr));
+ (void) strncpy(ifmr.ifm_name, drv->ifname, sizeof(ifmr.ifm_name));
+
+ if (ioctl(drv->sock, SIOCGIFMEDIA, (caddr_t)&ifmr) >= 0) {
+ if (ifmr.ifm_current & IFM_IEEE80211_ADHOC) {
+ if (ifmr.ifm_current & IFM_FLAG0)
+ return IEEE80211_M_AHDEMO;
+ else
+ return IEEE80211_M_IBSS;
+ }
+ if (ifmr.ifm_current & IFM_IEEE80211_HOSTAP)
+ return IEEE80211_M_HOSTAP;
+ if (ifmr.ifm_current & IFM_IEEE80211_MONITOR)
+ return IEEE80211_M_MONITOR;
+ if (ifmr.ifm_current & IFM_IEEE80211_MBSS)
+ return IEEE80211_M_MBSS;
+ }
+ return IEEE80211_M_STA;
+}
+
+
+static int
+bsd_set80211(void *priv, int op, int val, const void *arg, int arg_len)
+{
+ struct bsd_driver_data *drv = priv;
+ struct ieee80211req ireq;
+
+ os_memset(&ireq, 0, sizeof(ireq));
+ os_strlcpy(ireq.i_name, drv->ifname, sizeof(ireq.i_name));
+ ireq.i_type = op;
+ ireq.i_val = val;
+ ireq.i_data = (void *) arg;
+ ireq.i_len = arg_len;
+
+ if (ioctl(drv->sock, SIOCS80211, &ireq) < 0) {
+ wpa_printf(MSG_ERROR, "ioctl[SIOCS80211, op=%u, val=%u, "
+ "arg_len=%u]: %s", op, val, arg_len,
+ strerror(errno));
+ return -1;
+ }
+ return 0;
+}
+
+static int
+bsd_get80211(void *priv, struct ieee80211req *ireq, int op, void *arg,
+ int arg_len)
+{
+ struct bsd_driver_data *drv = priv;
+
+ os_memset(ireq, 0, sizeof(*ireq));
+ os_strlcpy(ireq->i_name, drv->ifname, sizeof(ireq->i_name));
+ ireq->i_type = op;
+ ireq->i_len = arg_len;
+ ireq->i_data = arg;
+
+ if (ioctl(drv->sock, SIOCG80211, ireq) < 0) {
+ wpa_printf(MSG_ERROR, "ioctl[SIOCS80211, op=%u, "
+ "arg_len=%u]: %s", op, arg_len, strerror(errno));
+ return -1;
+ }
+ return 0;
+}
+
+static int
+get80211var(struct bsd_driver_data *drv, int op, void *arg, int arg_len)
+{
+ struct ieee80211req ireq;
+
+ if (bsd_get80211(drv, &ireq, op, arg, arg_len) < 0)
+ return -1;
+ return ireq.i_len;
+}
+
+static int
+set80211var(struct bsd_driver_data *drv, int op, const void *arg, int arg_len)
+{
+ return bsd_set80211(drv, op, 0, arg, arg_len);
+}
+
+static int
+set80211param(struct bsd_driver_data *drv, int op, int arg)
+{
+ return bsd_set80211(drv, op, arg, NULL, 0);
+}
+
+static int
+bsd_get_ssid(void *priv, u8 *ssid, int len)
+{
+ struct bsd_driver_data *drv = priv;
+#ifdef SIOCG80211NWID
+ struct ieee80211_nwid nwid;
+ struct ifreq ifr;
+
+ os_memset(&ifr, 0, sizeof(ifr));
+ os_strlcpy(ifr.ifr_name, drv->ifname, sizeof(ifr.ifr_name));
+ ifr.ifr_data = (void *)&nwid;
+ if (ioctl(drv->sock, SIOCG80211NWID, &ifr) < 0 ||
+ nwid.i_len > IEEE80211_NWID_LEN)
+ return -1;
+ os_memcpy(ssid, nwid.i_nwid, nwid.i_len);
+ return nwid.i_len;
+#else
+ return get80211var(drv, IEEE80211_IOC_SSID, ssid, IEEE80211_NWID_LEN);
+#endif
+}
+
+static int
+bsd_set_ssid(void *priv, const u8 *ssid, int ssid_len)
+{
+ struct bsd_driver_data *drv = priv;
+#ifdef SIOCS80211NWID
+ struct ieee80211_nwid nwid;
+ struct ifreq ifr;
+
+ os_memcpy(nwid.i_nwid, ssid, ssid_len);
+ nwid.i_len = ssid_len;
+ os_memset(&ifr, 0, sizeof(ifr));
+ os_strlcpy(ifr.ifr_name, drv->ifname, sizeof(ifr.ifr_name));
+ ifr.ifr_data = (void *)&nwid;
+ return ioctl(drv->sock, SIOCS80211NWID, &ifr);
+#else
+ return set80211var(drv, IEEE80211_IOC_SSID, ssid, ssid_len);
+#endif
+}
+
+static int
+bsd_get_if_media(void *priv)
+{
+ struct bsd_driver_data *drv = priv;
+ struct ifmediareq ifmr;
+
+ os_memset(&ifmr, 0, sizeof(ifmr));
+ os_strlcpy(ifmr.ifm_name, drv->ifname, sizeof(ifmr.ifm_name));
+
+ if (ioctl(drv->sock, SIOCGIFMEDIA, &ifmr) < 0) {
+ wpa_printf(MSG_ERROR, "%s: SIOCGIFMEDIA %s", __func__,
+ strerror(errno));
+ return -1;
+ }
+
+ return ifmr.ifm_current;
+}
+
+static int
+bsd_set_if_media(void *priv, int media)
+{
+ struct bsd_driver_data *drv = priv;
+ struct ifreq ifr;
+
+ os_memset(&ifr, 0, sizeof(ifr));
+ os_strlcpy(ifr.ifr_name, drv->ifname, sizeof(ifr.ifr_name));
+ ifr.ifr_media = media;
+
+ if (ioctl(drv->sock, SIOCSIFMEDIA, &ifr) < 0) {
+ wpa_printf(MSG_ERROR, "%s: SIOCSIFMEDIA %s", __func__,
+ strerror(errno));
+ return -1;
+ }
+
+ return 0;
+}
+
+static int
+bsd_set_mediaopt(void *priv, uint32_t mask, uint32_t mode)
+{
+ int media = bsd_get_if_media(priv);
+
+ if (media < 0)
+ return -1;
+ media &= ~mask;
+ media |= mode;
+ if (bsd_set_if_media(priv, media) < 0)
+ return -1;
+ return 0;
+}
+
+static int
+bsd_del_key(void *priv, const u8 *addr, int key_idx)
+{
+ struct ieee80211req_del_key wk;
+
+ os_memset(&wk, 0, sizeof(wk));
+ if (addr == NULL) {
+ wpa_printf(MSG_DEBUG, "%s: key_idx=%d", __func__, key_idx);
+ wk.idk_keyix = key_idx;
+ } else {
+ wpa_printf(MSG_DEBUG, "%s: addr=" MACSTR, __func__,
+ MAC2STR(addr));
+ os_memcpy(wk.idk_macaddr, addr, IEEE80211_ADDR_LEN);
+ wk.idk_keyix = (u_int8_t) IEEE80211_KEYIX_NONE; /* XXX */
+ }
+
+ return set80211var(priv, IEEE80211_IOC_DELKEY, &wk, sizeof(wk));
+}
+
+static int
+bsd_send_mlme_param(void *priv, const u8 op, const u16 reason, const u8 *addr)
+{
+ struct ieee80211req_mlme mlme;
+
+ os_memset(&mlme, 0, sizeof(mlme));
+ mlme.im_op = op;
+ mlme.im_reason = reason;
+ os_memcpy(mlme.im_macaddr, addr, IEEE80211_ADDR_LEN);
+ return set80211var(priv, IEEE80211_IOC_MLME, &mlme, sizeof(mlme));
+}
+
+static int
+bsd_ctrl_iface(void *priv, int enable)
+{
+ struct bsd_driver_data *drv = priv;
+ struct ifreq ifr;
+
+ os_memset(&ifr, 0, sizeof(ifr));
+ os_strlcpy(ifr.ifr_name, drv->ifname, sizeof(ifr.ifr_name));
+
+ if (ioctl(drv->sock, SIOCGIFFLAGS, &ifr) < 0) {
+ perror("ioctl[SIOCGIFFLAGS]");
+ return -1;
+ }
+
+ if (enable) {
+ if (ifr.ifr_flags & IFF_UP)
+ return 0;
+ ifr.ifr_flags |= IFF_UP;
+ } else {
+ if (!(ifr.ifr_flags & IFF_UP))
+ return 0;
+ ifr.ifr_flags &= ~IFF_UP;
+ }
+
+ if (ioctl(drv->sock, SIOCSIFFLAGS, &ifr) < 0) {
+ perror("ioctl[SIOCSIFFLAGS]");
+ return -1;
+ }
+
+ return 0;
+}
+
+static int
+bsd_commit(void *priv)
+{
+ return bsd_ctrl_iface(priv, 1);
+}
+
+static int
+bsd_set_key(const char *ifname, void *priv, enum wpa_alg alg,
+ const unsigned char *addr, int key_idx, int set_tx, const u8 *seq,
+ size_t seq_len, const u8 *key, size_t key_len)
+{
+ struct ieee80211req_key wk;
+ struct bsd_driver_data *drv = priv;
+
+ wpa_printf(MSG_DEBUG, "%s: alg=%d addr=%p key_idx=%d set_tx=%d "
+ "seq_len=%zu key_len=%zu", __func__, alg, addr, key_idx,
+ set_tx, seq_len, key_len);
+
+ if (alg == WPA_ALG_NONE) {
+#ifndef HOSTAPD
+ if (addr == NULL || is_broadcast_ether_addr(addr))
+ return bsd_del_key(priv, NULL, key_idx);
+ else
+#endif /* HOSTAPD */
+ return bsd_del_key(priv, addr, key_idx);
+ }
+
+ os_memset(&wk, 0, sizeof(wk));
+ switch (alg) {
+ case WPA_ALG_WEP:
+ wk.ik_type = IEEE80211_CIPHER_WEP;
+ break;
+ case WPA_ALG_TKIP:
+ wk.ik_type = IEEE80211_CIPHER_TKIP;
+ break;
+ case WPA_ALG_CCMP:
+ wk.ik_type = IEEE80211_CIPHER_AES_CCM;
+ break;
+ default:
+ wpa_printf(MSG_ERROR, "%s: unknown alg=%d", __func__, alg);
+ return -1;
+ }
+
+ wk.ik_flags = IEEE80211_KEY_RECV;
+ if (set_tx)
+ wk.ik_flags |= IEEE80211_KEY_XMIT;
+
+ if (addr == NULL) {
+ os_memset(wk.ik_macaddr, 0xff, IEEE80211_ADDR_LEN);
+ wk.ik_keyix = key_idx;
+ } else {
+ os_memcpy(wk.ik_macaddr, addr, IEEE80211_ADDR_LEN);
+ /*
+ * Deduce whether group/global or unicast key by checking
+ * the address (yech). Note also that we can only mark global
+ * keys default; doing this for a unicast key is an error.
+ */
+ if (is_broadcast_ether_addr(addr)) {
+ wk.ik_flags |= IEEE80211_KEY_GROUP;
+ wk.ik_keyix = key_idx;
+ } else {
+ wk.ik_keyix = key_idx == 0 ? IEEE80211_KEYIX_NONE :
+ key_idx;
+ }
+ }
+ if (wk.ik_keyix != IEEE80211_KEYIX_NONE && set_tx)
+ wk.ik_flags |= IEEE80211_KEY_DEFAULT;
+#ifndef HOSTAPD
+ /*
+ * Ignore replay failures in IBSS and AHDEMO mode.
+ */
+ if (drv->opmode == IEEE80211_M_IBSS ||
+ drv->opmode == IEEE80211_M_AHDEMO)
+ wk.ik_flags |= IEEE80211_KEY_NOREPLAY;
+#endif
+ wk.ik_keylen = key_len;
+ if (seq) {
+#ifdef WORDS_BIGENDIAN
+ /*
+ * wk.ik_keyrsc is in host byte order (big endian), need to
+ * swap it to match with the byte order used in WPA.
+ */
+ int i;
+ u8 *keyrsc = (u8 *) &wk.ik_keyrsc;
+ for (i = 0; i < seq_len; i++)
+ keyrsc[WPA_KEY_RSC_LEN - i - 1] = seq[i];
+#else /* WORDS_BIGENDIAN */
+ os_memcpy(&wk.ik_keyrsc, seq, seq_len);
+#endif /* WORDS_BIGENDIAN */
+ }
+ os_memcpy(wk.ik_keydata, key, key_len);
+
+ return set80211var(priv, IEEE80211_IOC_WPAKEY, &wk, sizeof(wk));
+}
+
+static int
+bsd_configure_wpa(void *priv, struct wpa_bss_params *params)
+{
+#ifndef IEEE80211_IOC_APPIE
+ static const char *ciphernames[] =
+ { "WEP", "TKIP", "AES-OCB", "AES-CCM", "CKIP", "NONE" };
+ int v;
+
+ switch (params->wpa_group) {
+ case WPA_CIPHER_CCMP:
+ v = IEEE80211_CIPHER_AES_CCM;
+ break;
+ case WPA_CIPHER_TKIP:
+ v = IEEE80211_CIPHER_TKIP;
+ break;
+ case WPA_CIPHER_WEP104:
+ v = IEEE80211_CIPHER_WEP;
+ break;
+ case WPA_CIPHER_WEP40:
+ v = IEEE80211_CIPHER_WEP;
+ break;
+ case WPA_CIPHER_NONE:
+ v = IEEE80211_CIPHER_NONE;
+ break;
+ default:
+ printf("Unknown group key cipher %u\n",
+ params->wpa_group);
+ return -1;
+ }
+ wpa_printf(MSG_DEBUG, "%s: group key cipher=%s (%u)",
+ __func__, ciphernames[v], v);
+ if (set80211param(priv, IEEE80211_IOC_MCASTCIPHER, v)) {
+ printf("Unable to set group key cipher to %u (%s)\n",
+ v, ciphernames[v]);
+ return -1;
+ }
+ if (v == IEEE80211_CIPHER_WEP) {
+ /* key length is done only for specific ciphers */
+ v = (params->wpa_group == WPA_CIPHER_WEP104 ? 13 : 5);
+ if (set80211param(priv, IEEE80211_IOC_MCASTKEYLEN, v)) {
+ printf("Unable to set group key length to %u\n", v);
+ return -1;
+ }
+ }
+
+ v = 0;
+ if (params->wpa_pairwise & WPA_CIPHER_CCMP)
+ v |= 1<<IEEE80211_CIPHER_AES_CCM;
+ if (params->wpa_pairwise & WPA_CIPHER_TKIP)
+ v |= 1<<IEEE80211_CIPHER_TKIP;
+ if (params->wpa_pairwise & WPA_CIPHER_NONE)
+ v |= 1<<IEEE80211_CIPHER_NONE;
+ wpa_printf(MSG_DEBUG, "%s: pairwise key ciphers=0x%x", __func__, v);
+ if (set80211param(priv, IEEE80211_IOC_UCASTCIPHERS, v)) {
+ printf("Unable to set pairwise key ciphers to 0x%x\n", v);
+ return -1;
+ }
+
+ wpa_printf(MSG_DEBUG, "%s: key management algorithms=0x%x",
+ __func__, params->wpa_key_mgmt);
+ if (set80211param(priv, IEEE80211_IOC_KEYMGTALGS,
+ params->wpa_key_mgmt)) {
+ printf("Unable to set key management algorithms to 0x%x\n",
+ params->wpa_key_mgmt);
+ return -1;
+ }
+
+ v = 0;
+ if (params->rsn_preauth)
+ v |= BIT(0);
+ wpa_printf(MSG_DEBUG, "%s: rsn capabilities=0x%x",
+ __func__, params->rsn_preauth);
+ if (set80211param(priv, IEEE80211_IOC_RSNCAPS, v)) {
+ printf("Unable to set RSN capabilities to 0x%x\n", v);
+ return -1;
+ }
+#endif /* IEEE80211_IOC_APPIE */
+
+ wpa_printf(MSG_DEBUG, "%s: enable WPA= 0x%x", __func__, params->wpa);
+ if (set80211param(priv, IEEE80211_IOC_WPA, params->wpa)) {
+ printf("Unable to set WPA to %u\n", params->wpa);
+ return -1;
+ }
+ return 0;
+}
+
+static int
+bsd_set_ieee8021x(void *priv, struct wpa_bss_params *params)
+{
+ wpa_printf(MSG_DEBUG, "%s: enabled=%d", __func__, params->enabled);
+
+ if (!params->enabled) {
+ /* XXX restore state */
+ return set80211param(priv, IEEE80211_IOC_AUTHMODE,
+ IEEE80211_AUTH_AUTO);
+ }
+ if (!params->wpa && !params->ieee802_1x) {
+ wpa_printf(MSG_ERROR, "%s: No 802.1X or WPA enabled",
+ __func__);
+ return -1;
+ }
+ if (params->wpa && bsd_configure_wpa(priv, params) != 0) {
+ wpa_printf(MSG_ERROR, "%s: Failed to configure WPA state",
+ __func__);
+ return -1;
+ }
+ if (set80211param(priv, IEEE80211_IOC_AUTHMODE,
+ (params->wpa ? IEEE80211_AUTH_WPA : IEEE80211_AUTH_8021X))) {
+ wpa_printf(MSG_ERROR, "%s: Failed to enable WPA/802.1X",
+ __func__);
+ return -1;
+ }
+ return bsd_ctrl_iface(priv, 1);
+}
+
+static int
+bsd_set_sta_authorized(void *priv, const u8 *addr,
+ int total_flags, int flags_or, int flags_and)
+{
+ int authorized = -1;
+
+ /* For now, only support setting Authorized flag */
+ if (flags_or & WPA_STA_AUTHORIZED)
+ authorized = 1;
+ if (!(flags_and & WPA_STA_AUTHORIZED))
+ authorized = 0;
+
+ if (authorized < 0)
+ return 0;
+
+ return bsd_send_mlme_param(priv, authorized ?
+ IEEE80211_MLME_AUTHORIZE :
+ IEEE80211_MLME_UNAUTHORIZE, 0, addr);
+}
+
+static void
+bsd_new_sta(void *priv, void *ctx, u8 addr[IEEE80211_ADDR_LEN])
+{
+ struct ieee80211req_wpaie ie;
+ int ielen = 0;
+ u8 *iebuf = NULL;
+
+ /*
+ * Fetch and validate any negotiated WPA/RSN parameters.
+ */
+ memset(&ie, 0, sizeof(ie));
+ memcpy(ie.wpa_macaddr, addr, IEEE80211_ADDR_LEN);
+ if (get80211var(priv, IEEE80211_IOC_WPAIE, &ie, sizeof(ie)) < 0) {
+ printf("Failed to get WPA/RSN information element.\n");
+ goto no_ie;
+ }
+ iebuf = ie.wpa_ie;
+ ielen = ie.wpa_ie[1];
+ if (ielen == 0)
+ iebuf = NULL;
+ else
+ ielen += 2;
+
+no_ie:
+ drv_event_assoc(ctx, addr, iebuf, ielen, 0);
+}
+
+static int
+bsd_send_eapol(void *priv, const u8 *addr, const u8 *data, size_t data_len,
+ int encrypt, const u8 *own_addr, u32 flags)
+{
+ struct bsd_driver_data *drv = priv;
+
+ wpa_hexdump(MSG_MSGDUMP, "TX EAPOL", data, data_len);
+
+ return l2_packet_send(drv->sock_xmit, addr, ETH_P_EAPOL, data,
+ data_len);
+}
+
+static int
+bsd_set_freq(void *priv, struct hostapd_freq_params *freq)
+{
+ struct bsd_driver_data *drv = priv;
+#ifdef SIOCS80211CHANNEL
+ struct ieee80211chanreq creq;
+#endif /* SIOCS80211CHANNEL */
+ u32 mode;
+ int channel = freq->channel;
+
+ if (channel < 14) {
+ mode =
+#ifdef CONFIG_IEEE80211N
+ freq->ht_enabled ? IFM_IEEE80211_11NG :
+#endif /* CONFIG_IEEE80211N */
+ IFM_IEEE80211_11G;
+ } else if (channel == 14) {
+ mode = IFM_IEEE80211_11B;
+ } else {
+ mode =
+#ifdef CONFIG_IEEE80211N
+ freq->ht_enabled ? IFM_IEEE80211_11NA :
+#endif /* CONFIG_IEEE80211N */
+ IFM_IEEE80211_11A;
+ }
+ if (bsd_set_mediaopt(drv, IFM_MMASK, mode) < 0) {
+ wpa_printf(MSG_ERROR, "%s: failed to set modulation mode",
+ __func__);
+ return -1;
+ }
+
+#ifdef SIOCS80211CHANNEL
+ os_memset(&creq, 0, sizeof(creq));
+ os_strlcpy(creq.i_name, drv->ifname, sizeof(creq.i_name));
+ creq.i_channel = (u_int16_t)channel;
+ return ioctl(drv->sock, SIOCS80211CHANNEL, &creq);
+#else /* SIOCS80211CHANNEL */
+ return set80211param(priv, IEEE80211_IOC_CHANNEL, channel);
+#endif /* SIOCS80211CHANNEL */
+}
+
+static int
+bsd_set_opt_ie(void *priv, const u8 *ie, size_t ie_len)
+{
+#ifdef IEEE80211_IOC_APPIE
+ wpa_printf(MSG_DEBUG, "%s: set WPA+RSN ie (len %lu)", __func__,
+ (unsigned long)ie_len);
+ return bsd_set80211(priv, IEEE80211_IOC_APPIE, IEEE80211_APPIE_WPA,
+ ie, ie_len);
+#endif /* IEEE80211_IOC_APPIE */
+ return 0;
+}
+
+static int
+rtbuf_len(void)
+{
+ size_t len;
+
+ int mib[6] = {CTL_NET, AF_ROUTE, 0, AF_INET, NET_RT_DUMP, 0};
+
+ if (sysctl(mib, 6, NULL, &len, NULL, 0) < 0) {
+ wpa_printf(MSG_WARNING, "%s failed: %s\n", __func__,
+ strerror(errno));
+ len = 2048;
+ }
+
+ return len;
+}
+
+#ifdef HOSTAPD
+
+/*
+ * Avoid conflicts with hostapd definitions by undefining couple of defines
+ * from net80211 header files.
+ */
+#undef RSN_VERSION
+#undef WPA_VERSION
+#undef WPA_OUI_TYPE
+
+static int bsd_sta_deauth(void *priv, const u8 *own_addr, const u8 *addr,
+ int reason_code);
+
+static const char *
+ether_sprintf(const u8 *addr)
+{
+ static char buf[sizeof(MACSTR)];
+
+ if (addr != NULL)
+ snprintf(buf, sizeof(buf), MACSTR, MAC2STR(addr));
+ else
+ snprintf(buf, sizeof(buf), MACSTR, 0,0,0,0,0,0);
+ return buf;
+}
+
+static int
+bsd_set_privacy(void *priv, int enabled)
+{
+ wpa_printf(MSG_DEBUG, "%s: enabled=%d", __func__, enabled);
+
+ return set80211param(priv, IEEE80211_IOC_PRIVACY, enabled);
+}
+
+static int
+bsd_get_seqnum(const char *ifname, void *priv, const u8 *addr, int idx,
+ u8 *seq)
+{
+ struct ieee80211req_key wk;
+
+ wpa_printf(MSG_DEBUG, "%s: addr=%s idx=%d",
+ __func__, ether_sprintf(addr), idx);
+
+ memset(&wk, 0, sizeof(wk));
+ if (addr == NULL)
+ memset(wk.ik_macaddr, 0xff, IEEE80211_ADDR_LEN);
+ else
+ memcpy(wk.ik_macaddr, addr, IEEE80211_ADDR_LEN);
+ wk.ik_keyix = idx;
+
+ if (get80211var(priv, IEEE80211_IOC_WPAKEY, &wk, sizeof(wk)) < 0) {
+ printf("Failed to get encryption.\n");
+ return -1;
+ }
+
+#ifdef WORDS_BIGENDIAN
+ {
+ /*
+ * wk.ik_keytsc is in host byte order (big endian), need to
+ * swap it to match with the byte order used in WPA.
+ */
+ int i;
+ u8 tmp[WPA_KEY_RSC_LEN];
+ memcpy(tmp, &wk.ik_keytsc, sizeof(wk.ik_keytsc));
+ for (i = 0; i < WPA_KEY_RSC_LEN; i++) {
+ seq[i] = tmp[WPA_KEY_RSC_LEN - i - 1];
+ }
+ }
+#else /* WORDS_BIGENDIAN */
+ memcpy(seq, &wk.ik_keytsc, sizeof(wk.ik_keytsc));
+#endif /* WORDS_BIGENDIAN */
+ return 0;
+}
+
+
+static int
+bsd_flush(void *priv)
+{
+ u8 allsta[IEEE80211_ADDR_LEN];
+
+ memset(allsta, 0xff, IEEE80211_ADDR_LEN);
+ return bsd_sta_deauth(priv, NULL, allsta, IEEE80211_REASON_AUTH_LEAVE);
+}
+
+
+static int
+bsd_read_sta_driver_data(void *priv, struct hostap_sta_driver_data *data,
+ const u8 *addr)
+{
+ struct ieee80211req_sta_stats stats;
+
+ memcpy(stats.is_u.macaddr, addr, IEEE80211_ADDR_LEN);
+ if (get80211var(priv, IEEE80211_IOC_STA_STATS, &stats, sizeof(stats))
+ > 0) {
+ /* XXX? do packets counts include non-data frames? */
+ data->rx_packets = stats.is_stats.ns_rx_data;
+ data->rx_bytes = stats.is_stats.ns_rx_bytes;
+ data->tx_packets = stats.is_stats.ns_tx_data;
+ data->tx_bytes = stats.is_stats.ns_tx_bytes;
+ }
+ return 0;
+}
+
+static int
+bsd_sta_deauth(void *priv, const u8 *own_addr, const u8 *addr, int reason_code)
+{
+ return bsd_send_mlme_param(priv, IEEE80211_MLME_DEAUTH, reason_code,
+ addr);
+}
+
+static int
+bsd_sta_disassoc(void *priv, const u8 *own_addr, const u8 *addr,
+ int reason_code)
+{
+ return bsd_send_mlme_param(priv, IEEE80211_MLME_DISASSOC, reason_code,
+ addr);
+}
+
+static void
+bsd_wireless_event_receive(int sock, void *ctx, void *sock_ctx)
+{
+ struct bsd_driver_data *drv = ctx;
+ char *buf;
+ struct if_announcemsghdr *ifan;
+ struct rt_msghdr *rtm;
+ struct ieee80211_michael_event *mic;
+ struct ieee80211_join_event *join;
+ struct ieee80211_leave_event *leave;
+ int n, len;
+ union wpa_event_data data;
+
+ len = rtbuf_len();
+
+ buf = os_malloc(len);
+ if (buf == NULL) {
+ wpa_printf(MSG_ERROR, "%s os_malloc() failed\n", __func__);
+ return;
+ }
+
+ n = read(sock, buf, len);
+ if (n < 0) {
+ if (errno != EINTR && errno != EAGAIN)
+ wpa_printf(MSG_ERROR, "%s read() failed: %s\n",
+ __func__, strerror(errno));
+ os_free(buf);
+ return;
+ }
+
+ rtm = (struct rt_msghdr *) buf;
+ if (rtm->rtm_version != RTM_VERSION) {
+ wpa_printf(MSG_DEBUG, "Invalid routing message version=%d",
+ rtm->rtm_version);
+ os_free(buf);
+ return;
+ }
+ ifan = (struct if_announcemsghdr *) rtm;
+ switch (rtm->rtm_type) {
+ case RTM_IEEE80211:
+ switch (ifan->ifan_what) {
+ case RTM_IEEE80211_ASSOC:
+ case RTM_IEEE80211_REASSOC:
+ case RTM_IEEE80211_DISASSOC:
+ case RTM_IEEE80211_SCAN:
+ break;
+ case RTM_IEEE80211_LEAVE:
+ leave = (struct ieee80211_leave_event *) &ifan[1];
+ drv_event_disassoc(drv->hapd, leave->iev_addr);
+ break;
+ case RTM_IEEE80211_JOIN:
+#ifdef RTM_IEEE80211_REJOIN
+ case RTM_IEEE80211_REJOIN:
+#endif
+ join = (struct ieee80211_join_event *) &ifan[1];
+ bsd_new_sta(drv, drv->hapd, join->iev_addr);
+ break;
+ case RTM_IEEE80211_REPLAY:
+ /* ignore */
+ break;
+ case RTM_IEEE80211_MICHAEL:
+ mic = (struct ieee80211_michael_event *) &ifan[1];
+ wpa_printf(MSG_DEBUG,
+ "Michael MIC failure wireless event: "
+ "keyix=%u src_addr=" MACSTR, mic->iev_keyix,
+ MAC2STR(mic->iev_src));
+ os_memset(&data, 0, sizeof(data));
+ data.michael_mic_failure.unicast = 1;
+ data.michael_mic_failure.src = mic->iev_src;
+ wpa_supplicant_event(drv->hapd,
+ EVENT_MICHAEL_MIC_FAILURE, &data);
+ break;
+ }
+ break;
+ }
+ os_free(buf);
+}
+
+static void
+handle_read(void *ctx, const u8 *src_addr, const u8 *buf, size_t len)
+{
+ struct bsd_driver_data *drv = ctx;
+ drv_event_eapol_rx(drv->hapd, src_addr, buf, len);
+}
+
+static void *
+bsd_init(struct hostapd_data *hapd, struct wpa_init_params *params)
+{
+ struct bsd_driver_data *drv;
+
+ drv = os_zalloc(sizeof(struct bsd_driver_data));
+ if (drv == NULL) {
+ printf("Could not allocate memory for bsd driver data\n");
+ goto bad;
+ }
+
+ drv->hapd = hapd;
+ drv->sock = socket(PF_INET, SOCK_DGRAM, 0);
+ if (drv->sock < 0) {
+ perror("socket[PF_INET,SOCK_DGRAM]");
+ goto bad;
+ }
+ os_strlcpy(drv->ifname, params->ifname, sizeof(drv->ifname));
+
+ drv->sock_xmit = l2_packet_init(drv->ifname, NULL, ETH_P_EAPOL,
+ handle_read, drv, 0);
+ if (drv->sock_xmit == NULL)
+ goto bad;
+ if (l2_packet_get_own_addr(drv->sock_xmit, params->own_addr))
+ goto bad;
+
+ /* mark down during setup */
+ if (bsd_ctrl_iface(drv, 0) < 0)
+ goto bad;
+
+ drv->route = socket(PF_ROUTE, SOCK_RAW, 0);
+ if (drv->route < 0) {
+ perror("socket(PF_ROUTE,SOCK_RAW)");
+ goto bad;
+ }
+ eloop_register_read_sock(drv->route, bsd_wireless_event_receive, drv,
+ NULL);
+
+ if (bsd_set_mediaopt(drv, IFM_OMASK, IFM_IEEE80211_HOSTAP) < 0) {
+ wpa_printf(MSG_ERROR, "%s: failed to set operation mode",
+ __func__);
+ goto bad;
+ }
+
+ return drv;
+bad:
+ if (drv->sock_xmit != NULL)
+ l2_packet_deinit(drv->sock_xmit);
+ if (drv->sock >= 0)
+ close(drv->sock);
+ if (drv != NULL)
+ os_free(drv);
+ return NULL;
+}
+
+
+static void
+bsd_deinit(void *priv)
+{
+ struct bsd_driver_data *drv = priv;
+
+ if (drv->route >= 0) {
+ eloop_unregister_read_sock(drv->route);
+ close(drv->route);
+ }
+ bsd_ctrl_iface(drv, 0);
+ if (drv->sock >= 0)
+ close(drv->sock);
+ if (drv->sock_xmit != NULL)
+ l2_packet_deinit(drv->sock_xmit);
+ os_free(drv);
+}
+
+#else /* HOSTAPD */
+
+static int
+get80211param(struct bsd_driver_data *drv, int op)
+{
+ struct ieee80211req ireq;
+
+ if (bsd_get80211(drv, &ireq, op, NULL, 0) < 0)
+ return -1;
+ return ireq.i_val;
+}
+
+static int
+wpa_driver_bsd_get_bssid(void *priv, u8 *bssid)
+{
+ struct bsd_driver_data *drv = priv;
+#ifdef SIOCG80211BSSID
+ struct ieee80211_bssid bs;
+
+ os_strlcpy(bs.i_name, drv->ifname, sizeof(bs.i_name));
+ if (ioctl(drv->sock, SIOCG80211BSSID, &bs) < 0)
+ return -1;
+ os_memcpy(bssid, bs.i_bssid, sizeof(bs.i_bssid));
+ return 0;
+#else
+ return get80211var(drv, IEEE80211_IOC_BSSID,
+ bssid, IEEE80211_ADDR_LEN) < 0 ? -1 : 0;
+#endif
+}
+
+static int
+wpa_driver_bsd_get_ssid(void *priv, u8 *ssid)
+{
+ struct bsd_driver_data *drv = priv;
+ return bsd_get_ssid(drv, ssid, 0);
+}
+
+static int
+wpa_driver_bsd_set_wpa_ie(struct bsd_driver_data *drv, const u8 *wpa_ie,
+ size_t wpa_ie_len)
+{
+#ifdef IEEE80211_IOC_APPIE
+ return bsd_set_opt_ie(drv, wpa_ie, wpa_ie_len);
+#else /* IEEE80211_IOC_APPIE */
+ return set80211var(drv, IEEE80211_IOC_OPTIE, wpa_ie, wpa_ie_len);
+#endif /* IEEE80211_IOC_APPIE */
+}
+
+static int
+wpa_driver_bsd_set_wpa_internal(void *priv, int wpa, int privacy)
+{
+ int ret = 0;
+
+ wpa_printf(MSG_DEBUG, "%s: wpa=%d privacy=%d",
+ __FUNCTION__, wpa, privacy);
+
+ if (!wpa && wpa_driver_bsd_set_wpa_ie(priv, NULL, 0) < 0)
+ ret = -1;
+ if (set80211param(priv, IEEE80211_IOC_PRIVACY, privacy) < 0)
+ ret = -1;
+ if (set80211param(priv, IEEE80211_IOC_WPA, wpa) < 0)
+ ret = -1;
+
+ return ret;
+}
+
+static int
+wpa_driver_bsd_set_wpa(void *priv, int enabled)
+{
+ wpa_printf(MSG_DEBUG, "%s: enabled=%d", __FUNCTION__, enabled);
+
+ return wpa_driver_bsd_set_wpa_internal(priv, enabled ? 3 : 0, enabled);
+}
+
+static int
+wpa_driver_bsd_set_countermeasures(void *priv, int enabled)
+{
+ wpa_printf(MSG_DEBUG, "%s: enabled=%d", __func__, enabled);
+ return set80211param(priv, IEEE80211_IOC_COUNTERMEASURES, enabled);
+}
+
+
+static int
+wpa_driver_bsd_set_drop_unencrypted(void *priv, int enabled)
+{
+ wpa_printf(MSG_DEBUG, "%s: enabled=%d", __func__, enabled);
+ return set80211param(priv, IEEE80211_IOC_DROPUNENCRYPTED, enabled);
+}
+
+static int
+wpa_driver_bsd_deauthenticate(void *priv, const u8 *addr, int reason_code)
+{
+ return bsd_send_mlme_param(priv, IEEE80211_MLME_DEAUTH, reason_code,
+ addr);
+}
+
+static int
+wpa_driver_bsd_set_auth_alg(void *priv, int auth_alg)
+{
+ int authmode;
+
+ if ((auth_alg & WPA_AUTH_ALG_OPEN) &&
+ (auth_alg & WPA_AUTH_ALG_SHARED))
+ authmode = IEEE80211_AUTH_AUTO;
+ else if (auth_alg & WPA_AUTH_ALG_SHARED)
+ authmode = IEEE80211_AUTH_SHARED;
+ else
+ authmode = IEEE80211_AUTH_OPEN;
+
+ return set80211param(priv, IEEE80211_IOC_AUTHMODE, authmode);
+}
+
+static void
+handle_read(void *ctx, const u8 *src_addr, const u8 *buf, size_t len)
+{
+ struct bsd_driver_data *drv = ctx;
+
+ drv_event_eapol_rx(drv->ctx, src_addr, buf, len);
+}
+
+static int
+wpa_driver_bsd_associate(void *priv, struct wpa_driver_associate_params *params)
+{
+ struct bsd_driver_data *drv = priv;
+ struct ieee80211req_mlme mlme;
+ u32 mode;
+ int privacy;
+ int ret = 0;
+
+ wpa_printf(MSG_DEBUG,
+ "%s: ssid '%.*s' wpa ie len %u pairwise %u group %u key mgmt %u"
+ , __func__
+ , (unsigned int) params->ssid_len, params->ssid
+ , (unsigned int) params->wpa_ie_len
+ , params->pairwise_suite
+ , params->group_suite
+ , params->key_mgmt_suite
+ );
+
+ switch (params->mode) {
+ case IEEE80211_MODE_INFRA:
+ mode = 0 /* STA */;
+ break;
+ case IEEE80211_MODE_IBSS:
+ mode = IFM_IEEE80211_IBSS;
+ break;
+ case IEEE80211_MODE_AP:
+ mode = IFM_IEEE80211_HOSTAP;
+ break;
+ default:
+ wpa_printf(MSG_ERROR, "%s: unknown operation mode", __func__);
+ return -1;
+ }
+ if (bsd_set_mediaopt(drv, IFM_OMASK, mode) < 0) {
+ wpa_printf(MSG_ERROR, "%s: failed to set operation mode",
+ __func__);
+ return -1;
+ }
+
+ if (params->mode == IEEE80211_MODE_AP) {
+ drv->sock_xmit = l2_packet_init(drv->ifname, NULL, ETH_P_EAPOL,
+ handle_read, drv, 0);
+ if (drv->sock_xmit == NULL)
+ return -1;
+ drv->is_ap = 1;
+ return 0;
+ }
+
+ if (wpa_driver_bsd_set_drop_unencrypted(drv, params->drop_unencrypted)
+ < 0)
+ ret = -1;
+ if (wpa_driver_bsd_set_auth_alg(drv, params->auth_alg) < 0)
+ ret = -1;
+ /* XXX error handling is wrong but unclear what to do... */
+ if (wpa_driver_bsd_set_wpa_ie(drv, params->wpa_ie, params->wpa_ie_len) < 0)
+ return -1;
+
+ privacy = !(params->pairwise_suite == CIPHER_NONE &&
+ params->group_suite == CIPHER_NONE &&
+ params->key_mgmt_suite == KEY_MGMT_NONE &&
+ params->wpa_ie_len == 0);
+ wpa_printf(MSG_DEBUG, "%s: set PRIVACY %u", __func__, privacy);
+
+ if (set80211param(drv, IEEE80211_IOC_PRIVACY, privacy) < 0)
+ return -1;
+
+ if (params->wpa_ie_len &&
+ set80211param(drv, IEEE80211_IOC_WPA,
+ params->wpa_ie[0] == WLAN_EID_RSN ? 2 : 1) < 0)
+ return -1;
+
+ os_memset(&mlme, 0, sizeof(mlme));
+ mlme.im_op = IEEE80211_MLME_ASSOC;
+ if (params->ssid != NULL)
+ os_memcpy(mlme.im_ssid, params->ssid, params->ssid_len);
+ mlme.im_ssid_len = params->ssid_len;
+ if (params->bssid != NULL)
+ os_memcpy(mlme.im_macaddr, params->bssid, IEEE80211_ADDR_LEN);
+ if (set80211var(drv, IEEE80211_IOC_MLME, &mlme, sizeof(mlme)) < 0)
+ return -1;
+ return ret;
+}
+
+static int
+wpa_driver_bsd_scan(void *priv, struct wpa_driver_scan_params *params)
+{
+ struct bsd_driver_data *drv = priv;
+#ifdef IEEE80211_IOC_SCAN_MAX_SSID
+ struct ieee80211_scan_req sr;
+ int i;
+#endif /* IEEE80211_IOC_SCAN_MAX_SSID */
+
+ if (bsd_set_mediaopt(drv, IFM_OMASK, 0 /* STA */) < 0) {
+ wpa_printf(MSG_ERROR, "%s: failed to set operation mode",
+ __func__);
+ return -1;
+ }
+
+ if (set80211param(drv, IEEE80211_IOC_ROAMING,
+ IEEE80211_ROAMING_MANUAL) < 0) {
+ wpa_printf(MSG_ERROR, "%s: failed to set "
+ "wpa_supplicant-based roaming: %s", __func__,
+ strerror(errno));
+ return -1;
+ }
+
+ if (wpa_driver_bsd_set_wpa(drv, 1) < 0) {
+ wpa_printf(MSG_ERROR, "%s: failed to set wpa: %s", __func__,
+ strerror(errno));
+ return -1;
+ }
+
+ /* NB: interface must be marked UP to do a scan */
+ if (bsd_ctrl_iface(drv, 1) < 0)
+ return -1;
+
+#ifdef IEEE80211_IOC_SCAN_MAX_SSID
+ os_memset(&sr, 0, sizeof(sr));
+ sr.sr_flags = IEEE80211_IOC_SCAN_ACTIVE | IEEE80211_IOC_SCAN_ONCE |
+ IEEE80211_IOC_SCAN_NOJOIN;
+ sr.sr_duration = IEEE80211_IOC_SCAN_FOREVER;
+ if (params->num_ssids > 0) {
+ sr.sr_nssid = params->num_ssids;
+#if 0
+ /* Boundary check is done by upper layer */
+ if (sr.sr_nssid > IEEE80211_IOC_SCAN_MAX_SSID)
+ sr.sr_nssid = IEEE80211_IOC_SCAN_MAX_SSID;
+#endif
+
+ /* NB: check scan cache first */
+ sr.sr_flags |= IEEE80211_IOC_SCAN_CHECK;
+ }
+ for (i = 0; i < sr.sr_nssid; i++) {
+ sr.sr_ssid[i].len = params->ssids[i].ssid_len;
+ os_memcpy(sr.sr_ssid[i].ssid, params->ssids[i].ssid,
+ sr.sr_ssid[i].len);
+ }
+
+ /* NB: net80211 delivers a scan complete event so no need to poll */
+ return set80211var(drv, IEEE80211_IOC_SCAN_REQ, &sr, sizeof(sr));
+#else /* IEEE80211_IOC_SCAN_MAX_SSID */
+ /* set desired ssid before scan */
+ if (bsd_set_ssid(drv, params->ssids[0].ssid,
+ params->ssids[0].ssid_len) < 0)
+ return -1;
+
+ /* NB: net80211 delivers a scan complete event so no need to poll */
+ return set80211param(drv, IEEE80211_IOC_SCAN_REQ, 0);
+#endif /* IEEE80211_IOC_SCAN_MAX_SSID */
+}
+
+static void
+wpa_driver_bsd_event_receive(int sock, void *ctx, void *sock_ctx)
+{
+ struct bsd_driver_data *drv = sock_ctx;
+ char *buf;
+ struct if_announcemsghdr *ifan;
+ struct if_msghdr *ifm;
+ struct rt_msghdr *rtm;
+ union wpa_event_data event;
+ struct ieee80211_michael_event *mic;
+ struct ieee80211_leave_event *leave;
+ struct ieee80211_join_event *join;
+ int n, len;
+
+ len = rtbuf_len();
+
+ buf = os_malloc(len);
+ if (buf == NULL) {
+ wpa_printf(MSG_ERROR, "%s os_malloc() failed\n", __func__);
+ return;
+ }
+
+ n = read(sock, buf, len);
+ if (n < 0) {
+ if (errno != EINTR && errno != EAGAIN)
+ wpa_printf(MSG_ERROR, "%s read() failed: %s\n",
+ __func__, strerror(errno));
+ os_free(buf);
+ return;
+ }
+
+ rtm = (struct rt_msghdr *) buf;
+ if (rtm->rtm_version != RTM_VERSION) {
+ wpa_printf(MSG_DEBUG, "Invalid routing message version=%d",
+ rtm->rtm_version);
+ os_free(buf);
+ return;
+ }
+ os_memset(&event, 0, sizeof(event));
+ switch (rtm->rtm_type) {
+ case RTM_IFANNOUNCE:
+ ifan = (struct if_announcemsghdr *) rtm;
+ if (ifan->ifan_index != drv->ifindex)
+ break;
+ os_strlcpy(event.interface_status.ifname, drv->ifname,
+ sizeof(event.interface_status.ifname));
+ switch (ifan->ifan_what) {
+ case IFAN_DEPARTURE:
+ event.interface_status.ievent = EVENT_INTERFACE_REMOVED;
+ default:
+ os_free(buf);
+ return;
+ }
+ wpa_printf(MSG_DEBUG, "RTM_IFANNOUNCE: Interface '%s' %s",
+ event.interface_status.ifname,
+ ifan->ifan_what == IFAN_DEPARTURE ?
+ "removed" : "added");
+ wpa_supplicant_event(ctx, EVENT_INTERFACE_STATUS, &event);
+ break;
+ case RTM_IEEE80211:
+ ifan = (struct if_announcemsghdr *) rtm;
+ if (ifan->ifan_index != drv->ifindex)
+ break;
+ switch (ifan->ifan_what) {
+ case RTM_IEEE80211_ASSOC:
+ case RTM_IEEE80211_REASSOC:
+ if (drv->is_ap)
+ break;
+ wpa_supplicant_event(ctx, EVENT_ASSOC, NULL);
+ break;
+ case RTM_IEEE80211_DISASSOC:
+ if (drv->is_ap)
+ break;
+ wpa_supplicant_event(ctx, EVENT_DISASSOC, NULL);
+ break;
+ case RTM_IEEE80211_SCAN:
+ if (drv->is_ap)
+ break;
+ wpa_supplicant_event(ctx, EVENT_SCAN_RESULTS, NULL);
+ break;
+ case RTM_IEEE80211_LEAVE:
+ leave = (struct ieee80211_leave_event *) &ifan[1];
+ drv_event_disassoc(ctx, leave->iev_addr);
+ break;
+ case RTM_IEEE80211_JOIN:
+#ifdef RTM_IEEE80211_REJOIN
+ case RTM_IEEE80211_REJOIN:
+#endif
+ join = (struct ieee80211_join_event *) &ifan[1];
+ bsd_new_sta(drv, ctx, join->iev_addr);
+ break;
+ case RTM_IEEE80211_REPLAY:
+ /* ignore */
+ break;
+ case RTM_IEEE80211_MICHAEL:
+ mic = (struct ieee80211_michael_event *) &ifan[1];
+ wpa_printf(MSG_DEBUG,
+ "Michael MIC failure wireless event: "
+ "keyix=%u src_addr=" MACSTR, mic->iev_keyix,
+ MAC2STR(mic->iev_src));
+
+ os_memset(&event, 0, sizeof(event));
+ event.michael_mic_failure.unicast =
+ !IEEE80211_IS_MULTICAST(mic->iev_dst);
+ wpa_supplicant_event(ctx, EVENT_MICHAEL_MIC_FAILURE,
+ &event);
+ break;
+ }
+ break;
+ case RTM_IFINFO:
+ ifm = (struct if_msghdr *) rtm;
+ if (ifm->ifm_index != drv->ifindex)
+ break;
+ if ((rtm->rtm_flags & RTF_UP) == 0) {
+ os_strlcpy(event.interface_status.ifname, drv->ifname,
+ sizeof(event.interface_status.ifname));
+ event.interface_status.ievent = EVENT_INTERFACE_REMOVED;
+ wpa_printf(MSG_DEBUG, "RTM_IFINFO: Interface '%s' DOWN",
+ event.interface_status.ifname);
+ wpa_supplicant_event(ctx, EVENT_INTERFACE_STATUS, &event);
+ }
+ break;
+ }
+ os_free(buf);
+}
+
+static void
+wpa_driver_bsd_add_scan_entry(struct wpa_scan_results *res,
+ struct ieee80211req_scan_result *sr)
+{
+ struct wpa_scan_res *result, **tmp;
+ size_t extra_len;
+ u8 *pos;
+
+ extra_len = 2 + sr->isr_ssid_len;
+ extra_len += 2 + sr->isr_nrates;
+ extra_len += 3; /* ERP IE */
+ extra_len += sr->isr_ie_len;
+
+ result = os_zalloc(sizeof(*result) + extra_len);
+ if (result == NULL)
+ return;
+ os_memcpy(result->bssid, sr->isr_bssid, ETH_ALEN);
+ result->freq = sr->isr_freq;
+ result->beacon_int = sr->isr_intval;
+ result->caps = sr->isr_capinfo;
+ result->qual = sr->isr_rssi;
+ result->noise = sr->isr_noise;
+ /*
+ * the rssi value reported by the kernel is in 0.5dB steps relative to
+ * the reported noise floor. see ieee80211_node.h for details.
+ */
+ result->level = sr->isr_rssi / 2 + sr->isr_noise;
+
+ pos = (u8 *)(result + 1);
+
+ *pos++ = WLAN_EID_SSID;
+ *pos++ = sr->isr_ssid_len;
+ os_memcpy(pos, sr + 1, sr->isr_ssid_len);
+ pos += sr->isr_ssid_len;
+
+ /*
+ * Deal all rates as supported rate.
+ * Because net80211 doesn't report extended supported rate or not.
+ */
+ *pos++ = WLAN_EID_SUPP_RATES;
+ *pos++ = sr->isr_nrates;
+ os_memcpy(pos, sr->isr_rates, sr->isr_nrates);
+ pos += sr->isr_nrates;
+
+ *pos++ = WLAN_EID_ERP_INFO;
+ *pos++ = 1;
+ *pos++ = sr->isr_erp;
+
+ os_memcpy(pos, (u8 *)(sr + 1) + sr->isr_ssid_len, sr->isr_ie_len);
+ pos += sr->isr_ie_len;
+
+ result->ie_len = pos - (u8 *)(result + 1);
+
+ tmp = os_realloc_array(res->res, res->num + 1,
+ sizeof(struct wpa_scan_res *));
+ if (tmp == NULL) {
+ os_free(result);
+ return;
+ }
+ tmp[res->num++] = result;
+ res->res = tmp;
+}
+
+struct wpa_scan_results *
+wpa_driver_bsd_get_scan_results2(void *priv)
+{
+ struct ieee80211req_scan_result *sr;
+ struct wpa_scan_results *res;
+ int len, rest;
+ uint8_t buf[24*1024], *pos;
+
+ len = get80211var(priv, IEEE80211_IOC_SCAN_RESULTS, buf, 24*1024);
+ if (len < 0)
+ return NULL;
+
+ res = os_zalloc(sizeof(*res));
+ if (res == NULL)
+ return NULL;
+
+ pos = buf;
+ rest = len;
+ while (rest >= sizeof(struct ieee80211req_scan_result)) {
+ sr = (struct ieee80211req_scan_result *)pos;
+ wpa_driver_bsd_add_scan_entry(res, sr);
+ pos += sr->isr_len;
+ rest -= sr->isr_len;
+ }
+
+ wpa_printf(MSG_DEBUG, "Received %d bytes of scan results (%lu BSSes)",
+ len, (unsigned long)res->num);
+
+ return res;
+}
+
+static int wpa_driver_bsd_capa(struct bsd_driver_data *drv)
+{
+#ifdef IEEE80211_IOC_DEVCAPS
+/* kernel definitions copied from net80211/ieee80211_var.h */
+#define IEEE80211_CIPHER_WEP 0
+#define IEEE80211_CIPHER_TKIP 1
+#define IEEE80211_CIPHER_AES_CCM 3
+#define IEEE80211_CRYPTO_WEP (1<<IEEE80211_CIPHER_WEP)
+#define IEEE80211_CRYPTO_TKIP (1<<IEEE80211_CIPHER_TKIP)
+#define IEEE80211_CRYPTO_AES_CCM (1<<IEEE80211_CIPHER_AES_CCM)
+#define IEEE80211_C_HOSTAP 0x00000400 /* CAPABILITY: HOSTAP avail */
+#define IEEE80211_C_WPA1 0x00800000 /* CAPABILITY: WPA1 avail */
+#define IEEE80211_C_WPA2 0x01000000 /* CAPABILITY: WPA2 avail */
+ struct ieee80211_devcaps_req devcaps;
+
+ if (get80211var(drv, IEEE80211_IOC_DEVCAPS, &devcaps,
+ sizeof(devcaps)) < 0) {
+ wpa_printf(MSG_ERROR, "failed to IEEE80211_IOC_DEVCAPS: %s",
+ strerror(errno));
+ return -1;
+ }
+
+ wpa_printf(MSG_DEBUG, "%s: drivercaps=0x%08x,cryptocaps=0x%08x",
+ __func__, devcaps.dc_drivercaps, devcaps.dc_cryptocaps);
+
+ if (devcaps.dc_drivercaps & IEEE80211_C_WPA1)
+ drv->capa.key_mgmt = WPA_DRIVER_CAPA_KEY_MGMT_WPA |
+ WPA_DRIVER_CAPA_KEY_MGMT_WPA_PSK;
+ if (devcaps.dc_drivercaps & IEEE80211_C_WPA2)
+ drv->capa.key_mgmt = WPA_DRIVER_CAPA_KEY_MGMT_WPA2 |
+ WPA_DRIVER_CAPA_KEY_MGMT_WPA2_PSK;
+
+ if (devcaps.dc_cryptocaps & IEEE80211_CRYPTO_WEP)
+ drv->capa.enc |= WPA_DRIVER_CAPA_ENC_WEP40 |
+ WPA_DRIVER_CAPA_ENC_WEP104;
+ if (devcaps.dc_cryptocaps & IEEE80211_CRYPTO_TKIP)
+ drv->capa.enc |= WPA_DRIVER_CAPA_ENC_TKIP;
+ if (devcaps.dc_cryptocaps & IEEE80211_CRYPTO_AES_CCM)
+ drv->capa.enc |= WPA_DRIVER_CAPA_ENC_CCMP;
+
+ if (devcaps.dc_drivercaps & IEEE80211_C_HOSTAP)
+ drv->capa.flags |= WPA_DRIVER_FLAGS_AP;
+#undef IEEE80211_CIPHER_WEP
+#undef IEEE80211_CIPHER_TKIP
+#undef IEEE80211_CIPHER_AES_CCM
+#undef IEEE80211_CRYPTO_WEP
+#undef IEEE80211_CRYPTO_TKIP
+#undef IEEE80211_CRYPTO_AES_CCM
+#undef IEEE80211_C_HOSTAP
+#undef IEEE80211_C_WPA1
+#undef IEEE80211_C_WPA2
+#else /* IEEE80211_IOC_DEVCAPS */
+ /* For now, assume TKIP, CCMP, WPA, WPA2 are supported */
+ drv->capa.key_mgmt = WPA_DRIVER_CAPA_KEY_MGMT_WPA |
+ WPA_DRIVER_CAPA_KEY_MGMT_WPA_PSK |
+ WPA_DRIVER_CAPA_KEY_MGMT_WPA2 |
+ WPA_DRIVER_CAPA_KEY_MGMT_WPA2_PSK;
+ drv->capa.enc = WPA_DRIVER_CAPA_ENC_WEP40 |
+ WPA_DRIVER_CAPA_ENC_WEP104 |
+ WPA_DRIVER_CAPA_ENC_TKIP |
+ WPA_DRIVER_CAPA_ENC_CCMP;
+ drv->capa.flags |= WPA_DRIVER_FLAGS_AP;
+#endif /* IEEE80211_IOC_DEVCAPS */
+#ifdef IEEE80211_IOC_SCAN_MAX_SSID
+ drv->capa.max_scan_ssids = IEEE80211_IOC_SCAN_MAX_SSID;
+#else /* IEEE80211_IOC_SCAN_MAX_SSID */
+ drv->capa.max_scan_ssids = 1;
+#endif /* IEEE80211_IOC_SCAN_MAX_SSID */
+ drv->capa.auth = WPA_DRIVER_AUTH_OPEN |
+ WPA_DRIVER_AUTH_SHARED |
+ WPA_DRIVER_AUTH_LEAP;
+ return 0;
+}
+
+static void *
+wpa_driver_bsd_init(void *ctx, const char *ifname)
+{
+#define GETPARAM(drv, param, v) \
+ (((v) = get80211param(drv, param)) != -1)
+ struct bsd_driver_data *drv;
+
+ drv = os_zalloc(sizeof(*drv));
+ if (drv == NULL)
+ return NULL;
+ /*
+ * NB: We require the interface name be mappable to an index.
+ * This implies we do not support having wpa_supplicant
+ * wait for an interface to appear. This seems ok; that
+ * doesn't belong here; it's really the job of devd.
+ */
+ drv->ifindex = if_nametoindex(ifname);
+ if (drv->ifindex == 0) {
+ wpa_printf(MSG_DEBUG, "%s: interface %s does not exist",
+ __func__, ifname);
+ goto fail1;
+ }
+ drv->sock = socket(PF_INET, SOCK_DGRAM, 0);
+ if (drv->sock < 0)
+ goto fail1;
+
+ os_strlcpy(drv->ifname, ifname, sizeof(drv->ifname));
+ /* Down interface during setup. */
+ if (bsd_ctrl_iface(drv, 0) < 0)
+ goto fail;
+
+ drv->route = socket(PF_ROUTE, SOCK_RAW, 0);
+ if (drv->route < 0)
+ goto fail;
+ eloop_register_read_sock(drv->route,
+ wpa_driver_bsd_event_receive, ctx, drv);
+
+ drv->ctx = ctx;
+
+ if (!GETPARAM(drv, IEEE80211_IOC_ROAMING, drv->prev_roaming)) {
+ wpa_printf(MSG_DEBUG, "%s: failed to get roaming state: %s",
+ __func__, strerror(errno));
+ goto fail;
+ }
+ if (!GETPARAM(drv, IEEE80211_IOC_PRIVACY, drv->prev_privacy)) {
+ wpa_printf(MSG_DEBUG, "%s: failed to get privacy state: %s",
+ __func__, strerror(errno));
+ goto fail;
+ }
+ if (!GETPARAM(drv, IEEE80211_IOC_WPA, drv->prev_wpa)) {
+ wpa_printf(MSG_DEBUG, "%s: failed to get wpa state: %s",
+ __func__, strerror(errno));
+ goto fail;
+ }
+
+ if (wpa_driver_bsd_capa(drv))
+ goto fail;
+
+ drv->opmode = get80211opmode(drv);
+
+ return drv;
+fail:
+ close(drv->sock);
+fail1:
+ os_free(drv);
+ return NULL;
+#undef GETPARAM
+}
+
+static void
+wpa_driver_bsd_deinit(void *priv)
+{
+ struct bsd_driver_data *drv = priv;
+
+ wpa_driver_bsd_set_wpa(drv, 0);
+ eloop_unregister_read_sock(drv->route);
+
+ /* NB: mark interface down */
+ bsd_ctrl_iface(drv, 0);
+
+ wpa_driver_bsd_set_wpa_internal(drv, drv->prev_wpa, drv->prev_privacy);
+ if (set80211param(drv, IEEE80211_IOC_ROAMING, drv->prev_roaming) < 0)
+ wpa_printf(MSG_DEBUG, "%s: failed to restore roaming state",
+ __func__);
+
+ if (drv->sock_xmit != NULL)
+ l2_packet_deinit(drv->sock_xmit);
+ (void) close(drv->route); /* ioctl socket */
+ (void) close(drv->sock); /* event socket */
+ os_free(drv);
+}
+
+static int
+wpa_driver_bsd_get_capa(void *priv, struct wpa_driver_capa *capa)
+{
+ struct bsd_driver_data *drv = priv;
+
+ os_memcpy(capa, &drv->capa, sizeof(*capa));
+ return 0;
+}
+#endif /* HOSTAPD */
+
+
+const struct wpa_driver_ops wpa_driver_bsd_ops = {
+ .name = "bsd",
+ .desc = "BSD 802.11 support",
+#ifdef HOSTAPD
+ .hapd_init = bsd_init,
+ .hapd_deinit = bsd_deinit,
+ .set_privacy = bsd_set_privacy,
+ .get_seqnum = bsd_get_seqnum,
+ .flush = bsd_flush,
+ .read_sta_data = bsd_read_sta_driver_data,
+ .sta_disassoc = bsd_sta_disassoc,
+ .sta_deauth = bsd_sta_deauth,
+ .sta_set_flags = bsd_set_sta_authorized,
+ .commit = bsd_commit,
+#else /* HOSTAPD */
+ .init = wpa_driver_bsd_init,
+ .deinit = wpa_driver_bsd_deinit,
+ .get_bssid = wpa_driver_bsd_get_bssid,
+ .get_ssid = wpa_driver_bsd_get_ssid,
+ .set_countermeasures = wpa_driver_bsd_set_countermeasures,
+ .scan2 = wpa_driver_bsd_scan,
+ .get_scan_results2 = wpa_driver_bsd_get_scan_results2,
+ .deauthenticate = wpa_driver_bsd_deauthenticate,
+ .associate = wpa_driver_bsd_associate,
+ .get_capa = wpa_driver_bsd_get_capa,
+#endif /* HOSTAPD */
+ .set_freq = bsd_set_freq,
+ .set_key = bsd_set_key,
+ .set_ieee8021x = bsd_set_ieee8021x,
+ .hapd_set_ssid = bsd_set_ssid,
+ .hapd_get_ssid = bsd_get_ssid,
+ .hapd_send_eapol = bsd_send_eapol,
+ .set_generic_elem = bsd_set_opt_ie,
+};
diff --git a/contrib/wpa/src/drivers/driver_common.c b/contrib/wpa/src/drivers/driver_common.c
new file mode 100644
index 0000000..418cf1a
--- /dev/null
+++ b/contrib/wpa/src/drivers/driver_common.c
@@ -0,0 +1,86 @@
+/*
+ * Common driver-related functions
+ * Copyright (c) 2003-2011, Jouni Malinen <j@w1.fi>
+ *
+ * This software may be distributed under the terms of the BSD license.
+ * See README for more details.
+ */
+
+#include "includes.h"
+#include "utils/common.h"
+#include "driver.h"
+
+void wpa_scan_results_free(struct wpa_scan_results *res)
+{
+ size_t i;
+
+ if (res == NULL)
+ return;
+
+ for (i = 0; i < res->num; i++)
+ os_free(res->res[i]);
+ os_free(res->res);
+ os_free(res);
+}
+
+
+const char * event_to_string(enum wpa_event_type event)
+{
+#define E2S(n) case EVENT_ ## n: return #n
+ switch (event) {
+ E2S(ASSOC);
+ E2S(DISASSOC);
+ E2S(MICHAEL_MIC_FAILURE);
+ E2S(SCAN_RESULTS);
+ E2S(ASSOCINFO);
+ E2S(INTERFACE_STATUS);
+ E2S(PMKID_CANDIDATE);
+ E2S(STKSTART);
+ E2S(TDLS);
+ E2S(FT_RESPONSE);
+ E2S(IBSS_RSN_START);
+ E2S(AUTH);
+ E2S(DEAUTH);
+ E2S(ASSOC_REJECT);
+ E2S(AUTH_TIMED_OUT);
+ E2S(ASSOC_TIMED_OUT);
+ E2S(FT_RRB_RX);
+ E2S(WPS_BUTTON_PUSHED);
+ E2S(TX_STATUS);
+ E2S(RX_FROM_UNKNOWN);
+ E2S(RX_MGMT);
+ E2S(RX_ACTION);
+ E2S(REMAIN_ON_CHANNEL);
+ E2S(CANCEL_REMAIN_ON_CHANNEL);
+ E2S(MLME_RX);
+ E2S(RX_PROBE_REQ);
+ E2S(NEW_STA);
+ E2S(EAPOL_RX);
+ E2S(SIGNAL_CHANGE);
+ E2S(INTERFACE_ENABLED);
+ E2S(INTERFACE_DISABLED);
+ E2S(CHANNEL_LIST_CHANGED);
+ E2S(INTERFACE_UNAVAILABLE);
+ E2S(BEST_CHANNEL);
+ E2S(UNPROT_DEAUTH);
+ E2S(UNPROT_DISASSOC);
+ E2S(STATION_LOW_ACK);
+ E2S(P2P_DEV_FOUND);
+ E2S(P2P_GO_NEG_REQ_RX);
+ E2S(P2P_GO_NEG_COMPLETED);
+ E2S(P2P_PROV_DISC_REQUEST);
+ E2S(P2P_PROV_DISC_RESPONSE);
+ E2S(P2P_SD_REQUEST);
+ E2S(P2P_SD_RESPONSE);
+ E2S(IBSS_PEER_LOST);
+ E2S(DRIVER_GTK_REKEY);
+ E2S(SCHED_SCAN_STOPPED);
+ E2S(DRIVER_CLIENT_POLL_OK);
+ E2S(EAPOL_TX_STATUS);
+ E2S(CH_SWITCH);
+ E2S(WNM);
+ }
+
+ return "UNKNOWN";
+#undef E2S
+}
diff --git a/contrib/wpa/src/drivers/driver_ndis.c b/contrib/wpa/src/drivers/driver_ndis.c
index 9344a99..8149a92 100644
--- a/contrib/wpa/src/drivers/driver_ndis.c
+++ b/contrib/wpa/src/drivers/driver_ndis.c
@@ -2,14 +2,8 @@
* WPA Supplicant - Windows/NDIS driver interface
* Copyright (c) 2004-2007, Jouni Malinen <j@w1.fi>
*
- * This program is free software; you can redistribute it and/or modify
- * it under the terms of the GNU General Public License version 2 as
- * published by the Free Software Foundation.
- *
- * Alternatively, this software may be distributed under the terms of BSD
- * license.
- *
- * See README and COPYING for more details.
+ * This software may be distributed under the terms of the BSD license.
+ * See README for more details.
*/
#ifdef __CYGWIN__
@@ -732,14 +726,6 @@ static int wpa_driver_ndis_deauthenticate(void *priv, const u8 *addr,
}
-static int wpa_driver_ndis_disassociate(void *priv, const u8 *addr,
- int reason_code)
-{
- struct wpa_driver_ndis_data *drv = priv;
- return wpa_driver_ndis_disconnect(drv);
-}
-
-
static void wpa_driver_ndis_scan_timeout(void *eloop_ctx, void *timeout_ctx)
{
wpa_printf(MSG_DEBUG, "Scan timeout - try to get results");
@@ -865,7 +851,7 @@ static struct wpa_scan_results * wpa_driver_ndis_get_scan_results(void *priv)
os_free(b);
return NULL;
}
- results->res = os_zalloc(count * sizeof(struct wpa_scan_res *));
+ results->res = os_calloc(count, sizeof(struct wpa_scan_res *));
if (results->res == NULL) {
os_free(results);
os_free(b);
@@ -1002,8 +988,7 @@ static int wpa_driver_ndis_set_key(const char *ifname, void *priv,
int res, pairwise;
u8 bssid[ETH_ALEN];
- if (addr == NULL || os_memcmp(addr, "\xff\xff\xff\xff\xff\xff",
- ETH_ALEN) == 0) {
+ if (addr == NULL || is_broadcast_ether_addr(addr)) {
/* Group Key */
pairwise = 0;
if (wpa_driver_ndis_get_bssid(drv, bssid) < 0)
@@ -1067,6 +1052,7 @@ wpa_driver_ndis_associate(void *priv,
{
struct wpa_driver_ndis_data *drv = priv;
u32 auth_mode, encr, priv_mode, mode;
+ u8 bcast[ETH_ALEN] = { 0xff, 0xff, 0xff, 0xff, 0xff, 0xff };
drv->mode = params->mode;
@@ -1092,7 +1078,6 @@ wpa_driver_ndis_associate(void *priv,
if (params->key_mgmt_suite == KEY_MGMT_NONE ||
params->key_mgmt_suite == KEY_MGMT_802_1X_NO_WPA) {
/* Re-set WEP keys if static WEP configuration is used. */
- u8 bcast[ETH_ALEN] = { 0xff, 0xff, 0xff, 0xff, 0xff, 0xff };
int i;
for (i = 0; i < 4; i++) {
if (!params->wep_key[i])
@@ -1126,6 +1111,22 @@ wpa_driver_ndis_associate(void *priv,
} else if (params->key_mgmt_suite == KEY_MGMT_WPS) {
auth_mode = Ndis802_11AuthModeOpen;
priv_mode = Ndis802_11PrivFilterAcceptAll;
+ if (params->wps == WPS_MODE_PRIVACY) {
+ u8 dummy_key[5] = { 0x11, 0x22, 0x33, 0x44, 0x55 };
+ /*
+ * Some NDIS drivers refuse to associate in open mode
+ * configuration due to Privacy field mismatch, so use
+ * a workaround to make the configuration look like
+ * matching one for WPS provisioning.
+ */
+ wpa_printf(MSG_DEBUG, "NDIS: Set dummy WEP key as a "
+ "workaround to allow driver to associate "
+ "for WPS");
+ wpa_driver_ndis_set_key(drv->ifname, drv, WPA_ALG_WEP,
+ bcast, 0, 1,
+ NULL, 0, dummy_key,
+ sizeof(dummy_key));
+ }
#endif /* CONFIG_WPS */
} else {
priv_mode = Ndis802_11PrivFilter8021xWEP;
@@ -1149,6 +1150,12 @@ wpa_driver_ndis_associate(void *priv,
encr = Ndis802_11Encryption1Enabled;
break;
case CIPHER_NONE:
+#ifdef CONFIG_WPS
+ if (params->wps == WPS_MODE_PRIVACY) {
+ encr = Ndis802_11Encryption1Enabled;
+ break;
+ }
+#endif /* CONFIG_WPS */
if (params->group_suite == CIPHER_CCMP)
encr = Ndis802_11Encryption3Enabled;
else if (params->group_suite == CIPHER_TKIP)
@@ -1157,7 +1164,14 @@ wpa_driver_ndis_associate(void *priv,
encr = Ndis802_11EncryptionDisabled;
break;
default:
+#ifdef CONFIG_WPS
+ if (params->wps == WPS_MODE_PRIVACY) {
+ encr = Ndis802_11Encryption1Enabled;
+ break;
+ }
+#endif /* CONFIG_WPS */
encr = Ndis802_11EncryptionDisabled;
+ break;
};
if (ndis_set_oid(drv, OID_802_11_PRIVACY_FILTER,
@@ -3186,94 +3200,32 @@ wpa_driver_ndis_get_interfaces(void *global_priv)
}
-const struct wpa_driver_ops wpa_driver_ndis_ops = {
- "ndis",
- "Windows NDIS driver",
- wpa_driver_ndis_get_bssid,
- wpa_driver_ndis_get_ssid,
- wpa_driver_ndis_set_key,
- wpa_driver_ndis_init,
- wpa_driver_ndis_deinit,
- NULL /* set_param */,
- NULL /* set_countermeasures */,
- wpa_driver_ndis_deauthenticate,
- wpa_driver_ndis_disassociate,
- wpa_driver_ndis_associate,
- wpa_driver_ndis_add_pmkid,
- wpa_driver_ndis_remove_pmkid,
- wpa_driver_ndis_flush_pmkid,
- wpa_driver_ndis_get_capa,
- wpa_driver_ndis_poll,
- wpa_driver_ndis_get_ifname,
- wpa_driver_ndis_get_mac_addr,
- NULL /* send_eapol */,
- NULL /* set_operstate */,
- NULL /* mlme_setprotection */,
- NULL /* get_hw_feature_data */,
- NULL /* set_channel */,
- NULL /* set_ssid */,
- NULL /* set_bssid */,
- NULL /* send_mlme */,
- NULL /* mlme_add_sta */,
- NULL /* mlme_remove_sta */,
- NULL /* update_ft_ies */,
- NULL /* send_ft_action */,
- wpa_driver_ndis_get_scan_results,
- NULL /* set_country */,
- NULL /* global_init */,
- NULL /* global_deinit */,
- NULL /* init2 */,
- wpa_driver_ndis_get_interfaces,
- wpa_driver_ndis_scan,
- NULL /* authenticate */,
- NULL /* set_beacon */,
- NULL /* hapd_init */,
- NULL /* hapd_deinit */,
- NULL /* set_ieee8021x */,
- NULL /* set_privacy */,
- NULL /* get_seqnum */,
- NULL /* flush */,
- NULL /* set_generic_elem */,
- NULL /* read_sta_data */,
- NULL /* hapd_send_eapol */,
- NULL /* sta_deauth */,
- NULL /* sta_disassoc */,
- NULL /* sta_remove */,
- NULL /* hapd_get_ssid */,
- NULL /* hapd_set_ssid */,
- NULL /* hapd_set_countermeasures */,
- NULL /* sta_add */,
- NULL /* get_inact_sec */,
- NULL /* sta_clear_stats */,
- NULL /* set_freq */,
- NULL /* set_rts */,
- NULL /* set_frag */,
- NULL /* sta_set_flags */,
- NULL /* set_rate_sets */,
- NULL /* set_cts_protect */,
- NULL /* set_preamble */,
- NULL /* set_short_slot_time */,
- NULL /* set_tx_queue_params */,
- NULL /* valid_bss_mask */,
- NULL /* if_add */,
- NULL /* if_remove */,
- NULL /* set_sta_vlan */,
- NULL /* commit */,
- NULL /* send_ether */,
- NULL /* set_radius_acl_auth */,
- NULL /* set_radius_acl_expire */,
- NULL /* set_ht_params */,
- NULL /* set_ap_wps_ie */,
- NULL /* set_supp_port */,
- NULL /* set_wds_sta */,
- NULL /* send_action */,
- NULL /* remain_on_channel */,
- NULL /* cancel_remain_on_channel */,
- NULL /* probe_req_report */,
- NULL /* disable_11b_rates */,
- NULL /* deinit_ap */,
- NULL /* suspend */,
- NULL /* resume */,
- NULL /* signal_monitor */,
- NULL /* send_frame */
-};
+static const char *ndis_drv_name = "ndis";
+static const char *ndis_drv_desc = "Windows NDIS driver";
+
+struct wpa_driver_ops wpa_driver_ndis_ops;
+
+void driver_ndis_init_ops(void)
+{
+ os_memset(&wpa_driver_ndis_ops, 0, sizeof(wpa_driver_ndis_ops));
+ wpa_driver_ndis_ops.name = ndis_drv_name;
+ wpa_driver_ndis_ops.desc = ndis_drv_desc;
+ wpa_driver_ndis_ops.get_bssid = wpa_driver_ndis_get_bssid;
+ wpa_driver_ndis_ops.get_ssid = wpa_driver_ndis_get_ssid;
+ wpa_driver_ndis_ops.set_key = wpa_driver_ndis_set_key;
+ wpa_driver_ndis_ops.init = wpa_driver_ndis_init;
+ wpa_driver_ndis_ops.deinit = wpa_driver_ndis_deinit;
+ wpa_driver_ndis_ops.deauthenticate = wpa_driver_ndis_deauthenticate;
+ wpa_driver_ndis_ops.associate = wpa_driver_ndis_associate;
+ wpa_driver_ndis_ops.add_pmkid = wpa_driver_ndis_add_pmkid;
+ wpa_driver_ndis_ops.remove_pmkid = wpa_driver_ndis_remove_pmkid;
+ wpa_driver_ndis_ops.flush_pmkid = wpa_driver_ndis_flush_pmkid;
+ wpa_driver_ndis_ops.get_capa = wpa_driver_ndis_get_capa;
+ wpa_driver_ndis_ops.poll = wpa_driver_ndis_poll;
+ wpa_driver_ndis_ops.get_ifname = wpa_driver_ndis_get_ifname;
+ wpa_driver_ndis_ops.get_mac_addr = wpa_driver_ndis_get_mac_addr;
+ wpa_driver_ndis_ops.get_scan_results2 =
+ wpa_driver_ndis_get_scan_results;
+ wpa_driver_ndis_ops.get_interfaces = wpa_driver_ndis_get_interfaces;
+ wpa_driver_ndis_ops.scan2 = wpa_driver_ndis_scan;
+}
diff --git a/contrib/wpa/src/drivers/driver_ndis.h b/contrib/wpa/src/drivers/driver_ndis.h
index f263f0e..89d136d 100644
--- a/contrib/wpa/src/drivers/driver_ndis.h
+++ b/contrib/wpa/src/drivers/driver_ndis.h
@@ -2,14 +2,8 @@
* WPA Supplicant - Windows/NDIS driver interface
* Copyright (c) 2004-2006, Jouni Malinen <j@w1.fi>
*
- * This program is free software; you can redistribute it and/or modify
- * it under the terms of the GNU General Public License version 2 as
- * published by the Free Software Foundation.
- *
- * Alternatively, this software may be distributed under the terms of BSD
- * license.
- *
- * See README and COPYING for more details.
+ * This software may be distributed under the terms of the BSD license.
+ * See README for more details.
*/
#ifndef DRIVER_NDIS_H
diff --git a/contrib/wpa/src/drivers/driver_ndis_.c b/contrib/wpa/src/drivers/driver_ndis_.c
index 4bee9aa..4d23001 100644
--- a/contrib/wpa/src/drivers/driver_ndis_.c
+++ b/contrib/wpa/src/drivers/driver_ndis_.c
@@ -2,14 +2,8 @@
* WPA Supplicant - Windows/NDIS driver interface - event processing
* Copyright (c) 2004-2005, Jouni Malinen <j@w1.fi>
*
- * This program is free software; you can redistribute it and/or modify
- * it under the terms of the GNU General Public License version 2 as
- * published by the Free Software Foundation.
- *
- * Alternatively, this software may be distributed under the terms of BSD
- * license.
- *
- * See README and COPYING for more details.
+ * This software may be distributed under the terms of the BSD license.
+ * See README for more details.
*/
#include "includes.h"
diff --git a/contrib/wpa/src/drivers/driver_ndiswrapper.c b/contrib/wpa/src/drivers/driver_ndiswrapper.c
deleted file mode 100644
index cd2f61e..0000000
--- a/contrib/wpa/src/drivers/driver_ndiswrapper.c
+++ /dev/null
@@ -1,378 +0,0 @@
-/*
- * WPA Supplicant - driver interaction with Linux ndiswrapper
- * Copyright (c) 2004-2006, Giridhar Pemmasani <giri@lmc.cs.sunysb.edu>
- * Copyright (c) 2004-2006, Jouni Malinen <j@w1.fi>
- *
- * This program is free software; you can redistribute it and/or modify
- * it under the terms of the GNU General Public License version 2 as
- * published by the Free Software Foundation.
- *
- * Alternatively, this software may be distributed under the terms of BSD
- * license.
- *
- * See README and COPYING for more details.
- *
- * Please note that ndiswrapper supports WPA configuration via Linux wireless
- * extensions and if the kernel includes support for this, driver_wext.c should
- * be used instead of this driver wrapper.
- */
-
-#include "includes.h"
-#include <sys/ioctl.h>
-
-#include "wireless_copy.h"
-#include "common.h"
-#include "driver.h"
-#include "driver_wext.h"
-
-struct wpa_driver_ndiswrapper_data {
- void *wext; /* private data for driver_wext */
- void *ctx;
- char ifname[IFNAMSIZ + 1];
- int sock;
-};
-
-
-struct wpa_key {
- enum wpa_alg alg;
- const u8 *addr;
- int key_index;
- int set_tx;
- const u8 *seq;
- size_t seq_len;
- const u8 *key;
- size_t key_len;
-};
-
-struct wpa_assoc_info {
- const u8 *bssid;
- const u8 *ssid;
- size_t ssid_len;
- int freq;
- const u8 *wpa_ie;
- size_t wpa_ie_len;
- enum wpa_cipher pairwise_suite;
- enum wpa_cipher group_suite;
- enum wpa_key_mgmt key_mgmt_suite;
- int auth_alg;
- int mode;
-};
-
-#define PRIV_RESET SIOCIWFIRSTPRIV+0
-#define WPA_SET_WPA SIOCIWFIRSTPRIV+1
-#define WPA_SET_KEY SIOCIWFIRSTPRIV+2
-#define WPA_ASSOCIATE SIOCIWFIRSTPRIV+3
-#define WPA_DISASSOCIATE SIOCIWFIRSTPRIV+4
-#define WPA_DROP_UNENCRYPTED SIOCIWFIRSTPRIV+5
-#define WPA_SET_COUNTERMEASURES SIOCIWFIRSTPRIV+6
-#define WPA_DEAUTHENTICATE SIOCIWFIRSTPRIV+7
-#define WPA_SET_AUTH_ALG SIOCIWFIRSTPRIV+8
-#define WPA_INIT SIOCIWFIRSTPRIV+9
-#define WPA_DEINIT SIOCIWFIRSTPRIV+10
-#define WPA_GET_CAPA SIOCIWFIRSTPRIV+11
-
-static int wpa_ndiswrapper_set_auth_alg(void *priv, int auth_alg);
-
-static int get_socket(void)
-{
- static const int families[] = {
- AF_INET, AF_IPX, AF_AX25, AF_APPLETALK
- };
- unsigned int i;
- int sock;
-
- for (i = 0; i < sizeof(families) / sizeof(int); ++i) {
- sock = socket(families[i], SOCK_DGRAM, 0);
- if (sock >= 0)
- return sock;
- }
-
- return -1;
-}
-
-static int iw_set_ext(struct wpa_driver_ndiswrapper_data *drv, int request,
- struct iwreq *pwrq)
-{
- os_strlcpy(pwrq->ifr_name, drv->ifname, IFNAMSIZ);
- return ioctl(drv->sock, request, pwrq);
-}
-
-static int wpa_ndiswrapper_set_wpa(void *priv, int enabled)
-{
- struct wpa_driver_ndiswrapper_data *drv = priv;
- struct iwreq priv_req;
- int ret = 0;
-
- os_memset(&priv_req, 0, sizeof(priv_req));
-
- priv_req.u.data.flags = enabled;
- if (iw_set_ext(drv, WPA_SET_WPA, &priv_req) < 0)
- ret = -1;
- return ret;
-}
-
-static int wpa_ndiswrapper_set_key(const char *ifname, void *priv,
- enum wpa_alg alg, const u8 *addr,
- int key_idx, int set_tx,
- const u8 *seq, size_t seq_len,
- const u8 *key, size_t key_len)
-{
- struct wpa_driver_ndiswrapper_data *drv = priv;
- struct wpa_key wpa_key;
- int ret = 0;
- struct iwreq priv_req;
-
- os_memset(&priv_req, 0, sizeof(priv_req));
-
- wpa_key.alg = alg;
- wpa_key.addr = addr;
- wpa_key.key_index = key_idx;
- wpa_key.set_tx = set_tx;
- wpa_key.seq = seq;
- wpa_key.seq_len = seq_len;
- wpa_key.key = key;
- wpa_key.key_len = key_len;
-
- priv_req.u.data.pointer = (void *)&wpa_key;
- priv_req.u.data.length = sizeof(wpa_key);
-
- if (iw_set_ext(drv, WPA_SET_KEY, &priv_req) < 0)
- ret = -1;
-
- if (alg == WPA_ALG_NONE) {
- /*
- * ndiswrapper did not seem to be clearing keys properly in
- * some cases with WPA_SET_KEY. For example, roaming from WPA
- * enabled AP to plaintext one seemed to fail since the driver
- * did not associate. Try to make sure the keys are cleared so
- * that plaintext APs can be used in all cases.
- */
- wpa_driver_wext_set_key(ifname, drv->wext, alg, addr, key_idx,
- set_tx, seq, seq_len, key, key_len);
- }
-
- return ret;
-}
-
-static int wpa_ndiswrapper_set_countermeasures(void *priv, int enabled)
-{
- struct wpa_driver_ndiswrapper_data *drv = priv;
- int ret = 0;
- struct iwreq priv_req;
-
- os_memset(&priv_req, 0, sizeof(priv_req));
-
- priv_req.u.param.value = enabled;
- if (iw_set_ext(drv, WPA_SET_COUNTERMEASURES, &priv_req) < 0)
- ret = -1;
-
- return ret;
-}
-
-static int wpa_ndiswrapper_set_drop_unencrypted(void *priv,
- int enabled)
-{
- struct wpa_driver_ndiswrapper_data *drv = priv;
- int ret = 0;
- struct iwreq priv_req;
-
- os_memset(&priv_req, 0, sizeof(priv_req));
-
- priv_req.u.param.value = enabled;
- if (iw_set_ext(drv, WPA_DROP_UNENCRYPTED, &priv_req) < 0)
- ret = -1;
- return ret;
-}
-
-static int wpa_ndiswrapper_deauthenticate(void *priv, const u8 *addr,
- int reason_code)
-{
- struct wpa_driver_ndiswrapper_data *drv = priv;
- int ret = 0;
- struct iwreq priv_req;
-
- os_memset(&priv_req, 0, sizeof(priv_req));
-
- priv_req.u.param.value = reason_code;
- os_memcpy(&priv_req.u.ap_addr.sa_data, addr, ETH_ALEN);
- if (iw_set_ext(drv, WPA_DEAUTHENTICATE, &priv_req) < 0)
- ret = -1;
- return ret;
-}
-
-static int wpa_ndiswrapper_disassociate(void *priv, const u8 *addr,
- int reason_code)
-{
- struct wpa_driver_ndiswrapper_data *drv = priv;
- int ret = 0;
- struct iwreq priv_req;
-
- os_memset(&priv_req, 0, sizeof(priv_req));
-
- os_memcpy(&priv_req.u.ap_addr.sa_data, addr, ETH_ALEN);
- if (iw_set_ext(drv, WPA_DISASSOCIATE, &priv_req) < 0)
- ret = -1;
- return ret;
-}
-
-static int
-wpa_ndiswrapper_associate(void *priv,
- struct wpa_driver_associate_params *params)
-{
- struct wpa_driver_ndiswrapper_data *drv = priv;
- int ret = 0;
- struct wpa_assoc_info wpa_assoc_info;
- struct iwreq priv_req;
-
- if (wpa_ndiswrapper_set_drop_unencrypted(drv,
- params->drop_unencrypted) < 0)
- ret = -1;
- if (wpa_ndiswrapper_set_auth_alg(drv, params->auth_alg) < 0)
- ret = -1;
-
- os_memset(&priv_req, 0, sizeof(priv_req));
- os_memset(&wpa_assoc_info, 0, sizeof(wpa_assoc_info));
-
- wpa_assoc_info.bssid = params->bssid;
- wpa_assoc_info.ssid = params->ssid;
- wpa_assoc_info.ssid_len = params->ssid_len;
- wpa_assoc_info.freq = params->freq;
- wpa_assoc_info.wpa_ie = params->wpa_ie;
- wpa_assoc_info.wpa_ie_len = params->wpa_ie_len;
- wpa_assoc_info.pairwise_suite = params->pairwise_suite;
- wpa_assoc_info.group_suite = params->group_suite;
- wpa_assoc_info.key_mgmt_suite = params->key_mgmt_suite;
- wpa_assoc_info.auth_alg = params->auth_alg;
- wpa_assoc_info.mode = params->mode;
-
- priv_req.u.data.pointer = (void *)&wpa_assoc_info;
- priv_req.u.data.length = sizeof(wpa_assoc_info);
-
- if (iw_set_ext(drv, WPA_ASSOCIATE, &priv_req) < 0)
- ret = -1;
- return ret;
-}
-
-static int wpa_ndiswrapper_set_auth_alg(void *priv, int auth_alg)
-{
- struct wpa_driver_ndiswrapper_data *drv = priv;
- int ret = 0;
- struct iwreq priv_req;
-
- os_memset(&priv_req, 0, sizeof(priv_req));
-
- priv_req.u.param.value = auth_alg;
- if (iw_set_ext(drv, WPA_SET_AUTH_ALG, &priv_req) < 0)
- ret = -1;
- return ret;
-}
-
-static int wpa_ndiswrapper_get_bssid(void *priv, u8 *bssid)
-{
- struct wpa_driver_ndiswrapper_data *drv = priv;
- return wpa_driver_wext_get_bssid(drv->wext, bssid);
-}
-
-
-static int wpa_ndiswrapper_get_ssid(void *priv, u8 *ssid)
-{
- struct wpa_driver_ndiswrapper_data *drv = priv;
- return wpa_driver_wext_get_ssid(drv->wext, ssid);
-}
-
-
-static int wpa_ndiswrapper_scan(void *priv,
- struct wpa_driver_scan_params *params)
-{
- struct wpa_driver_ndiswrapper_data *drv = priv;
- return wpa_driver_wext_scan(drv->wext, params);
-}
-
-
-static struct wpa_scan_results * wpa_ndiswrapper_get_scan_results(void *priv)
-{
- struct wpa_driver_ndiswrapper_data *drv = priv;
- return wpa_driver_wext_get_scan_results(drv->wext);
-}
-
-
-static int wpa_ndiswrapper_get_capa(void *priv, struct wpa_driver_capa *capa)
-{
- struct wpa_driver_ndiswrapper_data *drv = priv;
- int ret = 0;
- struct iwreq priv_req;
-
- os_memset(&priv_req, 0, sizeof(priv_req));
-
- priv_req.u.data.pointer = (void *) capa;
- priv_req.u.data.length = sizeof(*capa);
- if (iw_set_ext(drv, WPA_GET_CAPA, &priv_req) < 0)
- ret = -1;
- return ret;
-
-}
-
-
-static int wpa_ndiswrapper_set_operstate(void *priv, int state)
-{
- struct wpa_driver_ndiswrapper_data *drv = priv;
- return wpa_driver_wext_set_operstate(drv->wext, state);
-}
-
-
-static void * wpa_ndiswrapper_init(void *ctx, const char *ifname)
-{
- struct wpa_driver_ndiswrapper_data *drv;
-
- drv = os_zalloc(sizeof(*drv));
- if (drv == NULL)
- return NULL;
- drv->wext = wpa_driver_wext_init(ctx, ifname);
- if (drv->wext == NULL) {
- os_free(drv);
- return NULL;
- }
-
- drv->ctx = ctx;
- os_strlcpy(drv->ifname, ifname, sizeof(drv->ifname));
- drv->sock = get_socket();
- if (drv->sock < 0) {
- wpa_driver_wext_deinit(drv->wext);
- os_free(drv);
- return NULL;
- }
-
- wpa_ndiswrapper_set_wpa(drv, 1);
-
- return drv;
-}
-
-
-static void wpa_ndiswrapper_deinit(void *priv)
-{
- struct wpa_driver_ndiswrapper_data *drv = priv;
- wpa_ndiswrapper_set_wpa(drv, 0);
- wpa_driver_wext_deinit(drv->wext);
- close(drv->sock);
- os_free(drv);
-}
-
-
-const struct wpa_driver_ops wpa_driver_ndiswrapper_ops = {
- .name = "ndiswrapper",
- .desc = "Linux ndiswrapper (deprecated; use wext)",
- .set_key = wpa_ndiswrapper_set_key,
- .set_countermeasures = wpa_ndiswrapper_set_countermeasures,
- .deauthenticate = wpa_ndiswrapper_deauthenticate,
- .disassociate = wpa_ndiswrapper_disassociate,
- .associate = wpa_ndiswrapper_associate,
-
- .get_bssid = wpa_ndiswrapper_get_bssid,
- .get_ssid = wpa_ndiswrapper_get_ssid,
- .scan2 = wpa_ndiswrapper_scan,
- .get_scan_results2 = wpa_ndiswrapper_get_scan_results,
- .init = wpa_ndiswrapper_init,
- .deinit = wpa_ndiswrapper_deinit,
- .get_capa = wpa_ndiswrapper_get_capa,
- .set_operstate = wpa_ndiswrapper_set_operstate,
-};
diff --git a/contrib/wpa/src/drivers/driver_privsep.c b/contrib/wpa/src/drivers/driver_privsep.c
new file mode 100644
index 0000000..ed88e71
--- /dev/null
+++ b/contrib/wpa/src/drivers/driver_privsep.c
@@ -0,0 +1,740 @@
+/*
+ * WPA Supplicant - privilege separated driver interface
+ * Copyright (c) 2007-2009, Jouni Malinen <j@w1.fi>
+ *
+ * This software may be distributed under the terms of the BSD license.
+ * See README for more details.
+ */
+
+#include "includes.h"
+#include <sys/un.h>
+
+#include "common.h"
+#include "driver.h"
+#include "eloop.h"
+#include "common/privsep_commands.h"
+
+
+struct wpa_driver_privsep_data {
+ void *ctx;
+ u8 own_addr[ETH_ALEN];
+ int priv_socket;
+ char *own_socket_path;
+ int cmd_socket;
+ char *own_cmd_path;
+ struct sockaddr_un priv_addr;
+ char ifname[16];
+};
+
+
+static int wpa_priv_reg_cmd(struct wpa_driver_privsep_data *drv, int cmd)
+{
+ int res;
+
+ res = sendto(drv->priv_socket, &cmd, sizeof(cmd), 0,
+ (struct sockaddr *) &drv->priv_addr,
+ sizeof(drv->priv_addr));
+ if (res < 0)
+ perror("sendto");
+ return res < 0 ? -1 : 0;
+}
+
+
+static int wpa_priv_cmd(struct wpa_driver_privsep_data *drv, int cmd,
+ const void *data, size_t data_len,
+ void *reply, size_t *reply_len)
+{
+ struct msghdr msg;
+ struct iovec io[2];
+
+ io[0].iov_base = &cmd;
+ io[0].iov_len = sizeof(cmd);
+ io[1].iov_base = (u8 *) data;
+ io[1].iov_len = data_len;
+
+ os_memset(&msg, 0, sizeof(msg));
+ msg.msg_iov = io;
+ msg.msg_iovlen = data ? 2 : 1;
+ msg.msg_name = &drv->priv_addr;
+ msg.msg_namelen = sizeof(drv->priv_addr);
+
+ if (sendmsg(drv->cmd_socket, &msg, 0) < 0) {
+ perror("sendmsg(cmd_socket)");
+ return -1;
+ }
+
+ if (reply) {
+ fd_set rfds;
+ struct timeval tv;
+ int res;
+
+ FD_ZERO(&rfds);
+ FD_SET(drv->cmd_socket, &rfds);
+ tv.tv_sec = 5;
+ tv.tv_usec = 0;
+ res = select(drv->cmd_socket + 1, &rfds, NULL, NULL, &tv);
+ if (res < 0 && errno != EINTR) {
+ perror("select");
+ return -1;
+ }
+
+ if (FD_ISSET(drv->cmd_socket, &rfds)) {
+ res = recv(drv->cmd_socket, reply, *reply_len, 0);
+ if (res < 0) {
+ perror("recv");
+ return -1;
+ }
+ *reply_len = res;
+ } else {
+ wpa_printf(MSG_DEBUG, "PRIVSEP: Timeout while waiting "
+ "for reply (cmd=%d)", cmd);
+ return -1;
+ }
+ }
+
+ return 0;
+}
+
+
+static int wpa_driver_privsep_scan(void *priv,
+ struct wpa_driver_scan_params *params)
+{
+ struct wpa_driver_privsep_data *drv = priv;
+ const u8 *ssid = params->ssids[0].ssid;
+ size_t ssid_len = params->ssids[0].ssid_len;
+ wpa_printf(MSG_DEBUG, "%s: priv=%p", __func__, priv);
+ return wpa_priv_cmd(drv, PRIVSEP_CMD_SCAN, ssid, ssid_len,
+ NULL, NULL);
+}
+
+
+static struct wpa_scan_results *
+wpa_driver_privsep_get_scan_results2(void *priv)
+{
+ struct wpa_driver_privsep_data *drv = priv;
+ int res, num;
+ u8 *buf, *pos, *end;
+ size_t reply_len = 60000;
+ struct wpa_scan_results *results;
+ struct wpa_scan_res *r;
+
+ buf = os_malloc(reply_len);
+ if (buf == NULL)
+ return NULL;
+ res = wpa_priv_cmd(drv, PRIVSEP_CMD_GET_SCAN_RESULTS,
+ NULL, 0, buf, &reply_len);
+ if (res < 0) {
+ os_free(buf);
+ return NULL;
+ }
+
+ wpa_printf(MSG_DEBUG, "privsep: Received %lu bytes of scan results",
+ (unsigned long) reply_len);
+ if (reply_len < sizeof(int)) {
+ wpa_printf(MSG_DEBUG, "privsep: Invalid scan result len %lu",
+ (unsigned long) reply_len);
+ os_free(buf);
+ return NULL;
+ }
+
+ pos = buf;
+ end = buf + reply_len;
+ os_memcpy(&num, pos, sizeof(int));
+ if (num < 0 || num > 1000) {
+ os_free(buf);
+ return NULL;
+ }
+ pos += sizeof(int);
+
+ results = os_zalloc(sizeof(*results));
+ if (results == NULL) {
+ os_free(buf);
+ return NULL;
+ }
+
+ results->res = os_calloc(num, sizeof(struct wpa_scan_res *));
+ if (results->res == NULL) {
+ os_free(results);
+ os_free(buf);
+ return NULL;
+ }
+
+ while (results->num < (size_t) num && pos + sizeof(int) < end) {
+ int len;
+ os_memcpy(&len, pos, sizeof(int));
+ pos += sizeof(int);
+ if (len < 0 || len > 10000 || pos + len > end)
+ break;
+
+ r = os_malloc(len);
+ if (r == NULL)
+ break;
+ os_memcpy(r, pos, len);
+ pos += len;
+ if (sizeof(*r) + r->ie_len > (size_t) len) {
+ os_free(r);
+ break;
+ }
+
+ results->res[results->num++] = r;
+ }
+
+ os_free(buf);
+ return results;
+}
+
+
+static int wpa_driver_privsep_set_key(const char *ifname, void *priv,
+ enum wpa_alg alg, const u8 *addr,
+ int key_idx, int set_tx,
+ const u8 *seq, size_t seq_len,
+ const u8 *key, size_t key_len)
+{
+ struct wpa_driver_privsep_data *drv = priv;
+ struct privsep_cmd_set_key cmd;
+
+ wpa_printf(MSG_DEBUG, "%s: priv=%p alg=%d key_idx=%d set_tx=%d",
+ __func__, priv, alg, key_idx, set_tx);
+
+ os_memset(&cmd, 0, sizeof(cmd));
+ cmd.alg = alg;
+ if (addr)
+ os_memcpy(cmd.addr, addr, ETH_ALEN);
+ else
+ os_memset(cmd.addr, 0xff, ETH_ALEN);
+ cmd.key_idx = key_idx;
+ cmd.set_tx = set_tx;
+ if (seq && seq_len > 0 && seq_len < sizeof(cmd.seq)) {
+ os_memcpy(cmd.seq, seq, seq_len);
+ cmd.seq_len = seq_len;
+ }
+ if (key && key_len > 0 && key_len < sizeof(cmd.key)) {
+ os_memcpy(cmd.key, key, key_len);
+ cmd.key_len = key_len;
+ }
+
+ return wpa_priv_cmd(drv, PRIVSEP_CMD_SET_KEY, &cmd, sizeof(cmd),
+ NULL, NULL);
+}
+
+
+static int wpa_driver_privsep_associate(
+ void *priv, struct wpa_driver_associate_params *params)
+{
+ struct wpa_driver_privsep_data *drv = priv;
+ struct privsep_cmd_associate *data;
+ int res;
+ size_t buflen;
+
+ wpa_printf(MSG_DEBUG, "%s: priv=%p freq=%d pairwise_suite=%d "
+ "group_suite=%d key_mgmt_suite=%d auth_alg=%d mode=%d",
+ __func__, priv, params->freq, params->pairwise_suite,
+ params->group_suite, params->key_mgmt_suite,
+ params->auth_alg, params->mode);
+
+ buflen = sizeof(*data) + params->wpa_ie_len;
+ data = os_zalloc(buflen);
+ if (data == NULL)
+ return -1;
+
+ if (params->bssid)
+ os_memcpy(data->bssid, params->bssid, ETH_ALEN);
+ os_memcpy(data->ssid, params->ssid, params->ssid_len);
+ data->ssid_len = params->ssid_len;
+ data->freq = params->freq;
+ data->pairwise_suite = params->pairwise_suite;
+ data->group_suite = params->group_suite;
+ data->key_mgmt_suite = params->key_mgmt_suite;
+ data->auth_alg = params->auth_alg;
+ data->mode = params->mode;
+ data->wpa_ie_len = params->wpa_ie_len;
+ if (params->wpa_ie)
+ os_memcpy(data + 1, params->wpa_ie, params->wpa_ie_len);
+ /* TODO: add support for other assoc parameters */
+
+ res = wpa_priv_cmd(drv, PRIVSEP_CMD_ASSOCIATE, data, buflen,
+ NULL, NULL);
+ os_free(data);
+
+ return res;
+}
+
+
+static int wpa_driver_privsep_get_bssid(void *priv, u8 *bssid)
+{
+ struct wpa_driver_privsep_data *drv = priv;
+ int res;
+ size_t len = ETH_ALEN;
+
+ res = wpa_priv_cmd(drv, PRIVSEP_CMD_GET_BSSID, NULL, 0, bssid, &len);
+ if (res < 0 || len != ETH_ALEN)
+ return -1;
+ return 0;
+}
+
+
+static int wpa_driver_privsep_get_ssid(void *priv, u8 *ssid)
+{
+ struct wpa_driver_privsep_data *drv = priv;
+ int res, ssid_len;
+ u8 reply[sizeof(int) + 32];
+ size_t len = sizeof(reply);
+
+ res = wpa_priv_cmd(drv, PRIVSEP_CMD_GET_SSID, NULL, 0, reply, &len);
+ if (res < 0 || len < sizeof(int))
+ return -1;
+ os_memcpy(&ssid_len, reply, sizeof(int));
+ if (ssid_len < 0 || ssid_len > 32 || sizeof(int) + ssid_len > len) {
+ wpa_printf(MSG_DEBUG, "privsep: Invalid get SSID reply");
+ return -1;
+ }
+ os_memcpy(ssid, &reply[sizeof(int)], ssid_len);
+ return ssid_len;
+}
+
+
+static int wpa_driver_privsep_deauthenticate(void *priv, const u8 *addr,
+ int reason_code)
+{
+ //struct wpa_driver_privsep_data *drv = priv;
+ wpa_printf(MSG_DEBUG, "%s addr=" MACSTR " reason_code=%d",
+ __func__, MAC2STR(addr), reason_code);
+ wpa_printf(MSG_DEBUG, "%s - TODO", __func__);
+ return 0;
+}
+
+
+static void wpa_driver_privsep_event_assoc(void *ctx,
+ enum wpa_event_type event,
+ u8 *buf, size_t len)
+{
+ union wpa_event_data data;
+ int inc_data = 0;
+ u8 *pos, *end;
+ int ie_len;
+
+ os_memset(&data, 0, sizeof(data));
+
+ pos = buf;
+ end = buf + len;
+
+ if (end - pos < (int) sizeof(int))
+ return;
+ os_memcpy(&ie_len, pos, sizeof(int));
+ pos += sizeof(int);
+ if (ie_len < 0 || ie_len > end - pos)
+ return;
+ if (ie_len) {
+ data.assoc_info.req_ies = pos;
+ data.assoc_info.req_ies_len = ie_len;
+ pos += ie_len;
+ inc_data = 1;
+ }
+
+ wpa_supplicant_event(ctx, event, inc_data ? &data : NULL);
+}
+
+
+static void wpa_driver_privsep_event_interface_status(void *ctx, u8 *buf,
+ size_t len)
+{
+ union wpa_event_data data;
+ int ievent;
+
+ if (len < sizeof(int) ||
+ len - sizeof(int) > sizeof(data.interface_status.ifname))
+ return;
+
+ os_memcpy(&ievent, buf, sizeof(int));
+
+ os_memset(&data, 0, sizeof(data));
+ data.interface_status.ievent = ievent;
+ os_memcpy(data.interface_status.ifname, buf + sizeof(int),
+ len - sizeof(int));
+ wpa_supplicant_event(ctx, EVENT_INTERFACE_STATUS, &data);
+}
+
+
+static void wpa_driver_privsep_event_michael_mic_failure(
+ void *ctx, u8 *buf, size_t len)
+{
+ union wpa_event_data data;
+
+ if (len != sizeof(int))
+ return;
+
+ os_memset(&data, 0, sizeof(data));
+ os_memcpy(&data.michael_mic_failure.unicast, buf, sizeof(int));
+ wpa_supplicant_event(ctx, EVENT_MICHAEL_MIC_FAILURE, &data);
+}
+
+
+static void wpa_driver_privsep_event_pmkid_candidate(void *ctx, u8 *buf,
+ size_t len)
+{
+ union wpa_event_data data;
+
+ if (len != sizeof(struct pmkid_candidate))
+ return;
+
+ os_memset(&data, 0, sizeof(data));
+ os_memcpy(&data.pmkid_candidate, buf, len);
+ wpa_supplicant_event(ctx, EVENT_PMKID_CANDIDATE, &data);
+}
+
+
+static void wpa_driver_privsep_event_stkstart(void *ctx, u8 *buf, size_t len)
+{
+ union wpa_event_data data;
+
+ if (len != ETH_ALEN)
+ return;
+
+ os_memset(&data, 0, sizeof(data));
+ os_memcpy(data.stkstart.peer, buf, ETH_ALEN);
+ wpa_supplicant_event(ctx, EVENT_STKSTART, &data);
+}
+
+
+static void wpa_driver_privsep_event_ft_response(void *ctx, u8 *buf,
+ size_t len)
+{
+ union wpa_event_data data;
+
+ if (len < sizeof(int) + ETH_ALEN)
+ return;
+
+ os_memset(&data, 0, sizeof(data));
+ os_memcpy(&data.ft_ies.ft_action, buf, sizeof(int));
+ os_memcpy(data.ft_ies.target_ap, buf + sizeof(int), ETH_ALEN);
+ data.ft_ies.ies = buf + sizeof(int) + ETH_ALEN;
+ data.ft_ies.ies_len = len - sizeof(int) - ETH_ALEN;
+ wpa_supplicant_event(ctx, EVENT_FT_RESPONSE, &data);
+}
+
+
+static void wpa_driver_privsep_event_rx_eapol(void *ctx, u8 *buf, size_t len)
+{
+ if (len < ETH_ALEN)
+ return;
+ drv_event_eapol_rx(ctx, buf, buf + ETH_ALEN, len - ETH_ALEN);
+}
+
+
+static void wpa_driver_privsep_receive(int sock, void *eloop_ctx,
+ void *sock_ctx)
+{
+ struct wpa_driver_privsep_data *drv = eloop_ctx;
+ u8 *buf, *event_buf;
+ size_t event_len;
+ int res, event;
+ enum privsep_event e;
+ struct sockaddr_un from;
+ socklen_t fromlen = sizeof(from);
+ const size_t buflen = 2000;
+
+ buf = os_malloc(buflen);
+ if (buf == NULL)
+ return;
+ res = recvfrom(sock, buf, buflen, 0,
+ (struct sockaddr *) &from, &fromlen);
+ if (res < 0) {
+ perror("recvfrom(priv_socket)");
+ os_free(buf);
+ return;
+ }
+
+ wpa_printf(MSG_DEBUG, "privsep_driver: received %u bytes", res);
+
+ if (res < (int) sizeof(int)) {
+ wpa_printf(MSG_DEBUG, "Too short event message (len=%d)", res);
+ return;
+ }
+
+ os_memcpy(&event, buf, sizeof(int));
+ event_buf = &buf[sizeof(int)];
+ event_len = res - sizeof(int);
+ wpa_printf(MSG_DEBUG, "privsep: Event %d received (len=%lu)",
+ event, (unsigned long) event_len);
+
+ e = event;
+ switch (e) {
+ case PRIVSEP_EVENT_SCAN_RESULTS:
+ wpa_supplicant_event(drv->ctx, EVENT_SCAN_RESULTS, NULL);
+ break;
+ case PRIVSEP_EVENT_ASSOC:
+ wpa_driver_privsep_event_assoc(drv->ctx, EVENT_ASSOC,
+ event_buf, event_len);
+ break;
+ case PRIVSEP_EVENT_DISASSOC:
+ wpa_supplicant_event(drv->ctx, EVENT_DISASSOC, NULL);
+ break;
+ case PRIVSEP_EVENT_ASSOCINFO:
+ wpa_driver_privsep_event_assoc(drv->ctx, EVENT_ASSOCINFO,
+ event_buf, event_len);
+ break;
+ case PRIVSEP_EVENT_MICHAEL_MIC_FAILURE:
+ wpa_driver_privsep_event_michael_mic_failure(
+ drv->ctx, event_buf, event_len);
+ break;
+ case PRIVSEP_EVENT_INTERFACE_STATUS:
+ wpa_driver_privsep_event_interface_status(drv->ctx, event_buf,
+ event_len);
+ break;
+ case PRIVSEP_EVENT_PMKID_CANDIDATE:
+ wpa_driver_privsep_event_pmkid_candidate(drv->ctx, event_buf,
+ event_len);
+ break;
+ case PRIVSEP_EVENT_STKSTART:
+ wpa_driver_privsep_event_stkstart(drv->ctx, event_buf,
+ event_len);
+ break;
+ case PRIVSEP_EVENT_FT_RESPONSE:
+ wpa_driver_privsep_event_ft_response(drv->ctx, event_buf,
+ event_len);
+ break;
+ case PRIVSEP_EVENT_RX_EAPOL:
+ wpa_driver_privsep_event_rx_eapol(drv->ctx, event_buf,
+ event_len);
+ break;
+ }
+
+ os_free(buf);
+}
+
+
+static void * wpa_driver_privsep_init(void *ctx, const char *ifname)
+{
+ struct wpa_driver_privsep_data *drv;
+
+ drv = os_zalloc(sizeof(*drv));
+ if (drv == NULL)
+ return NULL;
+ drv->ctx = ctx;
+ drv->priv_socket = -1;
+ drv->cmd_socket = -1;
+ os_strlcpy(drv->ifname, ifname, sizeof(drv->ifname));
+
+ return drv;
+}
+
+
+static void wpa_driver_privsep_deinit(void *priv)
+{
+ struct wpa_driver_privsep_data *drv = priv;
+
+ if (drv->priv_socket >= 0) {
+ wpa_priv_reg_cmd(drv, PRIVSEP_CMD_UNREGISTER);
+ eloop_unregister_read_sock(drv->priv_socket);
+ close(drv->priv_socket);
+ }
+
+ if (drv->own_socket_path) {
+ unlink(drv->own_socket_path);
+ os_free(drv->own_socket_path);
+ }
+
+ if (drv->cmd_socket >= 0) {
+ eloop_unregister_read_sock(drv->cmd_socket);
+ close(drv->cmd_socket);
+ }
+
+ if (drv->own_cmd_path) {
+ unlink(drv->own_cmd_path);
+ os_free(drv->own_cmd_path);
+ }
+
+ os_free(drv);
+}
+
+
+static int wpa_driver_privsep_set_param(void *priv, const char *param)
+{
+ struct wpa_driver_privsep_data *drv = priv;
+ const char *pos;
+ char *own_dir, *priv_dir;
+ static unsigned int counter = 0;
+ size_t len;
+ struct sockaddr_un addr;
+
+ wpa_printf(MSG_DEBUG, "%s: param='%s'", __func__, param);
+ if (param == NULL)
+ pos = NULL;
+ else
+ pos = os_strstr(param, "own_dir=");
+ if (pos) {
+ char *end;
+ own_dir = os_strdup(pos + 8);
+ if (own_dir == NULL)
+ return -1;
+ end = os_strchr(own_dir, ' ');
+ if (end)
+ *end = '\0';
+ } else {
+ own_dir = os_strdup("/tmp");
+ if (own_dir == NULL)
+ return -1;
+ }
+
+ if (param == NULL)
+ pos = NULL;
+ else
+ pos = os_strstr(param, "priv_dir=");
+ if (pos) {
+ char *end;
+ priv_dir = os_strdup(pos + 9);
+ if (priv_dir == NULL) {
+ os_free(own_dir);
+ return -1;
+ }
+ end = os_strchr(priv_dir, ' ');
+ if (end)
+ *end = '\0';
+ } else {
+ priv_dir = os_strdup("/var/run/wpa_priv");
+ if (priv_dir == NULL) {
+ os_free(own_dir);
+ return -1;
+ }
+ }
+
+ len = os_strlen(own_dir) + 50;
+ drv->own_socket_path = os_malloc(len);
+ if (drv->own_socket_path == NULL) {
+ os_free(priv_dir);
+ os_free(own_dir);
+ return -1;
+ }
+ os_snprintf(drv->own_socket_path, len, "%s/wpa_privsep-%d-%d",
+ own_dir, getpid(), counter++);
+
+ len = os_strlen(own_dir) + 50;
+ drv->own_cmd_path = os_malloc(len);
+ if (drv->own_cmd_path == NULL) {
+ os_free(drv->own_socket_path);
+ drv->own_socket_path = NULL;
+ os_free(priv_dir);
+ os_free(own_dir);
+ return -1;
+ }
+ os_snprintf(drv->own_cmd_path, len, "%s/wpa_privsep-%d-%d",
+ own_dir, getpid(), counter++);
+
+ os_free(own_dir);
+
+ drv->priv_addr.sun_family = AF_UNIX;
+ os_snprintf(drv->priv_addr.sun_path, sizeof(drv->priv_addr.sun_path),
+ "%s/%s", priv_dir, drv->ifname);
+ os_free(priv_dir);
+
+ drv->priv_socket = socket(PF_UNIX, SOCK_DGRAM, 0);
+ if (drv->priv_socket < 0) {
+ perror("socket(PF_UNIX)");
+ os_free(drv->own_socket_path);
+ drv->own_socket_path = NULL;
+ return -1;
+ }
+
+ os_memset(&addr, 0, sizeof(addr));
+ addr.sun_family = AF_UNIX;
+ os_strlcpy(addr.sun_path, drv->own_socket_path, sizeof(addr.sun_path));
+ if (bind(drv->priv_socket, (struct sockaddr *) &addr, sizeof(addr)) <
+ 0) {
+ perror("privsep-set-params priv-sock: bind(PF_UNIX)");
+ close(drv->priv_socket);
+ drv->priv_socket = -1;
+ unlink(drv->own_socket_path);
+ os_free(drv->own_socket_path);
+ drv->own_socket_path = NULL;
+ return -1;
+ }
+
+ eloop_register_read_sock(drv->priv_socket, wpa_driver_privsep_receive,
+ drv, NULL);
+
+ drv->cmd_socket = socket(PF_UNIX, SOCK_DGRAM, 0);
+ if (drv->cmd_socket < 0) {
+ perror("socket(PF_UNIX)");
+ os_free(drv->own_cmd_path);
+ drv->own_cmd_path = NULL;
+ return -1;
+ }
+
+ os_memset(&addr, 0, sizeof(addr));
+ addr.sun_family = AF_UNIX;
+ os_strlcpy(addr.sun_path, drv->own_cmd_path, sizeof(addr.sun_path));
+ if (bind(drv->cmd_socket, (struct sockaddr *) &addr, sizeof(addr)) < 0)
+ {
+ perror("privsep-set-params cmd-sock: bind(PF_UNIX)");
+ close(drv->cmd_socket);
+ drv->cmd_socket = -1;
+ unlink(drv->own_cmd_path);
+ os_free(drv->own_cmd_path);
+ drv->own_cmd_path = NULL;
+ return -1;
+ }
+
+ if (wpa_priv_reg_cmd(drv, PRIVSEP_CMD_REGISTER) < 0) {
+ wpa_printf(MSG_ERROR, "Failed to register with wpa_priv");
+ return -1;
+ }
+
+ return 0;
+}
+
+
+static int wpa_driver_privsep_get_capa(void *priv,
+ struct wpa_driver_capa *capa)
+{
+ struct wpa_driver_privsep_data *drv = priv;
+ int res;
+ size_t len = sizeof(*capa);
+
+ res = wpa_priv_cmd(drv, PRIVSEP_CMD_GET_CAPA, NULL, 0, capa, &len);
+ if (res < 0 || len != sizeof(*capa))
+ return -1;
+ return 0;
+}
+
+
+static const u8 * wpa_driver_privsep_get_mac_addr(void *priv)
+{
+ struct wpa_driver_privsep_data *drv = priv;
+ wpa_printf(MSG_DEBUG, "%s", __func__);
+ return drv->own_addr;
+}
+
+
+static int wpa_driver_privsep_set_country(void *priv, const char *alpha2)
+{
+ struct wpa_driver_privsep_data *drv = priv;
+ wpa_printf(MSG_DEBUG, "%s country='%s'", __func__, alpha2);
+ return wpa_priv_cmd(drv, PRIVSEP_CMD_SET_COUNTRY, alpha2,
+ os_strlen(alpha2), NULL, NULL);
+}
+
+
+struct wpa_driver_ops wpa_driver_privsep_ops = {
+ "privsep",
+ "wpa_supplicant privilege separated driver",
+ .get_bssid = wpa_driver_privsep_get_bssid,
+ .get_ssid = wpa_driver_privsep_get_ssid,
+ .set_key = wpa_driver_privsep_set_key,
+ .init = wpa_driver_privsep_init,
+ .deinit = wpa_driver_privsep_deinit,
+ .set_param = wpa_driver_privsep_set_param,
+ .scan2 = wpa_driver_privsep_scan,
+ .deauthenticate = wpa_driver_privsep_deauthenticate,
+ .associate = wpa_driver_privsep_associate,
+ .get_capa = wpa_driver_privsep_get_capa,
+ .get_mac_addr = wpa_driver_privsep_get_mac_addr,
+ .get_scan_results2 = wpa_driver_privsep_get_scan_results2,
+ .set_country = wpa_driver_privsep_set_country,
+};
+
+
+struct wpa_driver_ops *wpa_drivers[] =
+{
+ &wpa_driver_privsep_ops,
+ NULL
+};
diff --git a/contrib/wpa/src/drivers/driver_wired.c b/contrib/wpa/src/drivers/driver_wired.c
index 2b197f0..e0f0f22 100644
--- a/contrib/wpa/src/drivers/driver_wired.c
+++ b/contrib/wpa/src/drivers/driver_wired.c
@@ -3,14 +3,8 @@
* Copyright (c) 2005-2009, Jouni Malinen <j@w1.fi>
* Copyright (c) 2004, Gunter Burchardt <tira@isx.de>
*
- * This program is free software; you can redistribute it and/or modify
- * it under the terms of the GNU General Public License version 2 as
- * published by the Free Software Foundation.
- *
- * Alternatively, this software may be distributed under the terms of BSD
- * license.
- *
- * See README and COPYING for more details.
+ * This software may be distributed under the terms of the BSD license.
+ * See README for more details.
*/
#include "includes.h"
@@ -24,6 +18,9 @@
#if defined(__FreeBSD__) || defined(__DragonFly__) || defined(__FreeBSD_kernel__)
#include <net/if_dl.h>
#endif /* defined(__FreeBSD__) || defined(__DragonFly__) || defined(__FreeBSD_kernel__) */
+#ifdef __sun__
+#include <sys/sockio.h>
+#endif /* __sun__ */
#include "common.h"
#include "eloop.h"
@@ -311,7 +308,7 @@ static int wired_init_sockets(struct wpa_driver_wired_data *drv, u8 *own_addr)
static int wired_send_eapol(void *priv, const u8 *addr,
const u8 *data, size_t data_len, int encrypt,
- const u8 *own_addr)
+ const u8 *own_addr, u32 flags)
{
struct wpa_driver_wired_data *drv = priv;
struct ieee8023_hdr *hdr;
@@ -462,6 +459,10 @@ static int wpa_driver_wired_multi(const char *ifname, const u8 *addr, int add)
struct ifreq ifr;
int s;
+#ifdef __sun__
+ return -1;
+#endif /* __sun__ */
+
s = socket(PF_INET, SOCK_DGRAM, 0);
if (s < 0) {
perror("socket");
diff --git a/contrib/wpa/src/drivers/drivers.c b/contrib/wpa/src/drivers/drivers.c
index bffbbde..a92eddf 100644
--- a/contrib/wpa/src/drivers/drivers.c
+++ b/contrib/wpa/src/drivers/drivers.c
@@ -2,14 +2,8 @@
* Driver interface list
* Copyright (c) 2004-2005, Jouni Malinen <j@w1.fi>
*
- * This program is free software; you can redistribute it and/or modify
- * it under the terms of the GNU General Public License version 2 as
- * published by the Free Software Foundation.
- *
- * Alternatively, this software may be distributed under the terms of BSD
- * license.
- *
- * See README and COPYING for more details.
+ * This software may be distributed under the terms of the BSD license.
+ * See README for more details.
*/
#include "includes.h"
@@ -24,25 +18,9 @@ extern struct wpa_driver_ops wpa_driver_nl80211_ops; /* driver_nl80211.c */
#ifdef CONFIG_DRIVER_HOSTAP
extern struct wpa_driver_ops wpa_driver_hostap_ops; /* driver_hostap.c */
#endif /* CONFIG_DRIVER_HOSTAP */
-#ifdef CONFIG_DRIVER_HERMES
-extern struct wpa_driver_ops wpa_driver_hermes_ops; /* driver_hermes.c */
-#endif /* CONFIG_DRIVER_HERMES */
#ifdef CONFIG_DRIVER_MADWIFI
extern struct wpa_driver_ops wpa_driver_madwifi_ops; /* driver_madwifi.c */
#endif /* CONFIG_DRIVER_MADWIFI */
-#ifdef CONFIG_DRIVER_ATMEL
-extern struct wpa_driver_ops wpa_driver_atmel_ops; /* driver_atmel.c */
-#endif /* CONFIG_DRIVER_ATMEL */
-#ifdef CONFIG_DRIVER_NDISWRAPPER
-/* driver_ndiswrapper.c */
-extern struct wpa_driver_ops wpa_driver_ndiswrapper_ops;
-#endif /* CONFIG_DRIVER_NDISWRAPPER */
-#ifdef CONFIG_DRIVER_BROADCOM
-extern struct wpa_driver_ops wpa_driver_broadcom_ops; /* driver_broadcom.c */
-#endif /* CONFIG_DRIVER_BROADCOM */
-#ifdef CONFIG_DRIVER_IPW
-extern struct wpa_driver_ops wpa_driver_ipw_ops; /* driver_ipw.c */
-#endif /* CONFIG_DRIVER_IPW */
#ifdef CONFIG_DRIVER_BSD
extern struct wpa_driver_ops wpa_driver_bsd_ops; /* driver_bsd.c */
#endif /* CONFIG_DRIVER_BSD */
@@ -55,15 +33,6 @@ extern struct wpa_driver_ops wpa_driver_wired_ops; /* driver_wired.c */
#ifdef CONFIG_DRIVER_TEST
extern struct wpa_driver_ops wpa_driver_test_ops; /* driver_test.c */
#endif /* CONFIG_DRIVER_TEST */
-#ifdef CONFIG_DRIVER_RALINK
-extern struct wpa_driver_ops wpa_driver_ralink_ops; /* driver_ralink.c */
-#endif /* CONFIG_DRIVER_RALINK */
-#ifdef CONFIG_DRIVER_OSX
-extern struct wpa_driver_ops wpa_driver_osx_ops; /* driver_osx.m */
-#endif /* CONFIG_DRIVER_OSX */
-#ifdef CONFIG_DRIVER_IPHONE
-extern struct wpa_driver_ops wpa_driver_iphone_ops; /* driver_iphone.m */
-#endif /* CONFIG_DRIVER_IPHONE */
#ifdef CONFIG_DRIVER_ROBOSWITCH
/* driver_roboswitch.c */
extern struct wpa_driver_ops wpa_driver_roboswitch_ops;
@@ -87,24 +56,9 @@ struct wpa_driver_ops *wpa_drivers[] =
#ifdef CONFIG_DRIVER_HOSTAP
&wpa_driver_hostap_ops,
#endif /* CONFIG_DRIVER_HOSTAP */
-#ifdef CONFIG_DRIVER_HERMES
- &wpa_driver_hermes_ops,
-#endif /* CONFIG_DRIVER_HERMES */
#ifdef CONFIG_DRIVER_MADWIFI
&wpa_driver_madwifi_ops,
#endif /* CONFIG_DRIVER_MADWIFI */
-#ifdef CONFIG_DRIVER_ATMEL
- &wpa_driver_atmel_ops,
-#endif /* CONFIG_DRIVER_ATMEL */
-#ifdef CONFIG_DRIVER_NDISWRAPPER
- &wpa_driver_ndiswrapper_ops,
-#endif /* CONFIG_DRIVER_NDISWRAPPER */
-#ifdef CONFIG_DRIVER_BROADCOM
- &wpa_driver_broadcom_ops,
-#endif /* CONFIG_DRIVER_BROADCOM */
-#ifdef CONFIG_DRIVER_IPW
- &wpa_driver_ipw_ops,
-#endif /* CONFIG_DRIVER_IPW */
#ifdef CONFIG_DRIVER_BSD
&wpa_driver_bsd_ops,
#endif /* CONFIG_DRIVER_BSD */
@@ -117,15 +71,6 @@ struct wpa_driver_ops *wpa_drivers[] =
#ifdef CONFIG_DRIVER_TEST
&wpa_driver_test_ops,
#endif /* CONFIG_DRIVER_TEST */
-#ifdef CONFIG_DRIVER_RALINK
- &wpa_driver_ralink_ops,
-#endif /* CONFIG_DRIVER_RALINK */
-#ifdef CONFIG_DRIVER_OSX
- &wpa_driver_osx_ops,
-#endif /* CONFIG_DRIVER_OSX */
-#ifdef CONFIG_DRIVER_IPHONE
- &wpa_driver_iphone_ops,
-#endif /* CONFIG_DRIVER_IPHONE */
#ifdef CONFIG_DRIVER_ROBOSWITCH
&wpa_driver_roboswitch_ops,
#endif /* CONFIG_DRIVER_ROBOSWITCH */
diff --git a/contrib/wpa/src/drivers/drivers.mak b/contrib/wpa/src/drivers/drivers.mak
deleted file mode 100644
index b76b229..0000000
--- a/contrib/wpa/src/drivers/drivers.mak
+++ /dev/null
@@ -1,181 +0,0 @@
-##### COMMON DRIVERS
-
-ifdef CONFIG_DRIVER_HOSTAP
-DRV_CFLAGS += -DCONFIG_DRIVER_HOSTAP
-DRV_OBJS += ../src/drivers/driver_hostap.o
-CONFIG_WIRELESS_EXTENSION=y
-NEED_AP_MLME=y
-NEED_NETLINK=y
-NEED_LINUX_IOCTL=y
-endif
-
-ifdef CONFIG_DRIVER_WIRED
-DRV_CFLAGS += -DCONFIG_DRIVER_WIRED
-DRV_OBJS += ../src/drivers/driver_wired.o
-endif
-
-ifdef CONFIG_DRIVER_MADWIFI
-DRV_CFLAGS += -DCONFIG_DRIVER_MADWIFI
-DRV_OBJS += ../src/drivers/driver_madwifi.o
-CONFIG_WIRELESS_EXTENSION=y
-CONFIG_L2_PACKET=linux
-NEED_NETLINK=y
-NEED_LINUX_IOCTL=y
-endif
-
-ifdef CONFIG_DRIVER_NL80211
-DRV_CFLAGS += -DCONFIG_DRIVER_NL80211
-DRV_OBJS += ../src/drivers/driver_nl80211.o
-DRV_OBJS += ../src/utils/radiotap.o
-NEED_SME=y
-NEED_AP_MLME=y
-NEED_NETLINK=y
-NEED_LINUX_IOCTL=y
-DRV_LIBS += -lnl
-
-ifdef CONFIG_LIBNL20
-DRV_LIBS += -lnl-genl
-DRV_CFLAGS += -DCONFIG_LIBNL20
-endif
-endif
-
-ifdef CONFIG_DRIVER_BSD
-ifndef CONFIG_L2_PACKET
-CONFIG_L2_PACKET=freebsd
-endif
-DRV_CFLAGS += -DCONFIG_DRIVER_BSD
-DRV_OBJS += ../src/drivers/driver_bsd.o
-CONFIG_L2_FREEBSD=y
-CONFIG_DNET_PCAP=y
-endif
-
-ifdef CONFIG_DRIVER_TEST
-DRV_CFLAGS += -DCONFIG_DRIVER_TEST
-DRV_OBJS += ../src/drivers/driver_test.o
-NEED_AP_MLME=y
-endif
-
-ifdef CONFIG_DRIVER_NONE
-DRV_CFLAGS += -DCONFIG_DRIVER_NONE
-DRV_OBJS += ../src/drivers/driver_none.o
-endif
-
-##### PURE AP DRIVERS
-
-ifdef CONFIG_DRIVER_ATHEROS
-DRV_AP_CFLAGS += -DCONFIG_DRIVER_ATHEROS
-DRV_AP_OBJS += ../src/drivers/driver_atheros.o
-CONFIG_L2_PACKET=linux
-NEED_NETLINK=y
-NEED_LINUX_IOCTL=y
-endif
-
-##### PURE CLIENT DRIVERS
-
-ifdef CONFIG_DRIVER_WEXT
-DRV_WPA_CFLAGS += -DCONFIG_DRIVER_WEXT
-CONFIG_WIRELESS_EXTENSION=y
-NEED_NETLINK=y
-NEED_LINUX_IOCTL=y
-endif
-
-ifdef CONFIG_DRIVER_HERMES
-DRV_WPA_CFLAGS += -DCONFIG_DRIVER_HERMES
-DRV_WPA_OBJS += ../src/drivers/driver_hermes.o
-CONFIG_WIRELESS_EXTENSION=y
-endif
-
-ifdef CONFIG_DRIVER_ATMEL
-DRV_WPA_CFLAGS += -DCONFIG_DRIVER_ATMEL
-DRV_WPA_OBJS += ../src/drivers/driver_atmel.o
-CONFIG_WIRELESS_EXTENSION=y
-endif
-
-ifdef CONFIG_DRIVER_NDISWRAPPER
-DRV_WPA_CFLAGS += -DCONFIG_DRIVER_NDISWRAPPER
-DRV_WPA_OBJS += ../src/drivers/driver_ndiswrapper.o
-CONFIG_WIRELESS_EXTENSION=y
-endif
-
-ifdef CONFIG_DRIVER_RALINK
-DRV_WPA_CFLAGS += -DCONFIG_DRIVER_RALINK
-DRV_WPA_OBJS += ../src/drivers/driver_ralink.o
-NEED_NETLINK=y
-NEED_LINUX_IOCTL=y
-endif
-
-ifdef CONFIG_DRIVER_BROADCOM
-DRV_WPA_CFLAGS += -DCONFIG_DRIVER_BROADCOM
-DRV_WPA_OBJS += ../src/drivers/driver_broadcom.o
-endif
-
-ifdef CONFIG_DRIVER_IPW
-DRV_WPA_CFLAGS += -DCONFIG_DRIVER_IPW
-DRV_WPA_OBJS += ../src/drivers/driver_ipw.o
-CONFIG_WIRELESS_EXTENSION=y
-endif
-
-ifdef CONFIG_DRIVER_NDIS
-DRV_WPA_CFLAGS += -DCONFIG_DRIVER_NDIS
-DRV_WPA_OBJS += ../src/drivers/driver_ndis.o
-ifdef CONFIG_NDIS_EVENTS_INTEGRATED
-DRV_WPA_OBJS += ../src/drivers/driver_ndis_.o
-endif
-ifndef CONFIG_L2_PACKET
-CONFIG_L2_PACKET=pcap
-endif
-CONFIG_WINPCAP=y
-ifdef CONFIG_USE_NDISUIO
-DRV_WPA_CFLAGS += -DCONFIG_USE_NDISUIO
-endif
-endif
-
-ifdef CONFIG_DRIVER_OSX
-DRV_WPA_CFLAGS += -DCONFIG_DRIVER_OSX
-DRV_WPA_OBJS += ../src/drivers/driver_osx.o
-DRV_WPA_LDFLAGS += -framework CoreFoundation
-DRV_WPA_LDFLAGS += -F/System/Library/PrivateFrameworks -framework Apple80211
-endif
-
-ifdef CONFIG_DRIVER_IPHONE
-DRV_WPA_CFLAGS += -DCONFIG_DRIVER_IPHONE
-DRV_WPA_OBJS += ../src/drivers/driver_iphone.o
-DRV_WPA_OBJS += ../src/drivers/MobileApple80211.o
-DRV_WPA_LDFLAGS += -framework CoreFoundation
-endif
-
-ifdef CONFIG_DRIVER_ROBOSWITCH
-DRV_WPA_CFLAGS += -DCONFIG_DRIVER_ROBOSWITCH
-DRV_WPA_OBJS += ../src/drivers/driver_roboswitch.o
-endif
-
-ifdef CONFIG_WIRELESS_EXTENSION
-DRV_WPA_CFLAGS += -DCONFIG_WIRELESS_EXTENSION
-DRV_WPA_OBJS += ../src/drivers/driver_wext.o
-endif
-
-ifdef NEED_NETLINK
-DRV_OBJS += ../src/drivers/netlink.o
-endif
-
-ifdef NEED_LINUX_IOCTL
-DRV_OBJS += ../src/drivers/linux_ioctl.o
-endif
-
-
-##### COMMON VARS
-DRV_BOTH_CFLAGS := $(DRV_CFLAGS) $(DRV_WPA_CFLAGS) $(DRV_AP_CFLAGS)
-DRV_WPA_CFLAGS += $(DRV_CFLAGS)
-DRV_AP_CFLAGS += $(DRV_CFLAGS)
-
-DRV_BOTH_LIBS := $(DRV_LIBS) $(DRV_WPA_LIBS) $(DRV_AP_LIBS)
-DRV_WPA_LIBS += $(DRV_LIBS)
-DRV_AP_LIBS += $(DRV_LIBS)
-
-DRV_BOTH_OBJS := $(DRV_OBJS) $(DRV_WPA_OBJS) $(DRV_AP_OBJS)
-DRV_WPA_OBJS += $(DRV_OBJS)
-DRV_AP_OBJS += $(DRV_OBJS)
-
-DRV_BOTH_LDFLAGS := $(DRV_LDFLAGS) $(DRV_WPA_LDFLAGS) $(DRV_AP_LDFLAGS)
-DRV_WPA_LDFLAGS += $(DRV_LDFLAGS)
-DRV_AP_LDFLAGS += $(DRV_LDFLAGS)
diff --git a/contrib/wpa/src/drivers/ndis_events.c b/contrib/wpa/src/drivers/ndis_events.c
index f6eaa7c..93673a3 100644
--- a/contrib/wpa/src/drivers/ndis_events.c
+++ b/contrib/wpa/src/drivers/ndis_events.c
@@ -2,14 +2,8 @@
* ndis_events - Receive NdisMIndicateStatus() events using WMI
* Copyright (c) 2004-2006, Jouni Malinen <j@w1.fi>
*
- * This program is free software; you can redistribute it and/or modify
- * it under the terms of the GNU General Public License version 2 as
- * published by the Free Software Foundation.
- *
- * Alternatively, this software may be distributed under the terms of BSD
- * license.
- *
- * See README and COPYING for more details.
+ * This software may be distributed under the terms of the BSD license.
+ * See README for more details.
*/
#define _WIN32_WINNT 0x0400
diff --git a/contrib/wpa/src/eap_common/Makefile b/contrib/wpa/src/eap_common/Makefile
deleted file mode 100644
index 9c41962..0000000
--- a/contrib/wpa/src/eap_common/Makefile
+++ /dev/null
@@ -1,8 +0,0 @@
-all:
- @echo Nothing to be made.
-
-clean:
- rm -f *~ *.o *.d
-
-install:
- @echo Nothing to be made.
diff --git a/contrib/wpa/src/eap_common/chap.c b/contrib/wpa/src/eap_common/chap.c
index 60bfc1c..820d18a 100644
--- a/contrib/wpa/src/eap_common/chap.c
+++ b/contrib/wpa/src/eap_common/chap.c
@@ -2,14 +2,8 @@
* CHAP-MD5 (RFC 1994)
* Copyright (c) 2007-2009, Jouni Malinen <j@w1.fi>
*
- * This program is free software; you can redistribute it and/or modify
- * it under the terms of the GNU General Public License version 2 as
- * published by the Free Software Foundation.
- *
- * Alternatively, this software may be distributed under the terms of BSD
- * license.
- *
- * See README and COPYING for more details.
+ * This software may be distributed under the terms of the BSD license.
+ * See README for more details.
*/
#include "includes.h"
diff --git a/contrib/wpa/src/eap_common/chap.h b/contrib/wpa/src/eap_common/chap.h
index b9c400c..a791505 100644
--- a/contrib/wpa/src/eap_common/chap.h
+++ b/contrib/wpa/src/eap_common/chap.h
@@ -2,14 +2,8 @@
* CHAP-MD5 (RFC 1994)
* Copyright (c) 2007-2009, Jouni Malinen <j@w1.fi>
*
- * This program is free software; you can redistribute it and/or modify
- * it under the terms of the GNU General Public License version 2 as
- * published by the Free Software Foundation.
- *
- * Alternatively, this software may be distributed under the terms of BSD
- * license.
- *
- * See README and COPYING for more details.
+ * This software may be distributed under the terms of the BSD license.
+ * See README for more details.
*/
#ifndef CHAP_H
diff --git a/contrib/wpa/src/eap_common/eap_common.c b/contrib/wpa/src/eap_common/eap_common.c
index 4afa1dd..7b077cb 100644
--- a/contrib/wpa/src/eap_common/eap_common.c
+++ b/contrib/wpa/src/eap_common/eap_common.c
@@ -1,15 +1,9 @@
/*
* EAP common peer/server definitions
- * Copyright (c) 2004-2007, Jouni Malinen <j@w1.fi>
+ * Copyright (c) 2004-2012, Jouni Malinen <j@w1.fi>
*
- * This program is free software; you can redistribute it and/or modify
- * it under the terms of the GNU General Public License version 2 as
- * published by the Free Software Foundation.
- *
- * Alternatively, this software may be distributed under the terms of BSD
- * license.
- *
- * See README and COPYING for more details.
+ * This software may be distributed under the terms of the BSD license.
+ * See README for more details.
*/
#include "includes.h"
@@ -19,6 +13,41 @@
#include "eap_common.h"
/**
+ * eap_hdr_len_valid - Validate EAP header length field
+ * @msg: EAP frame (starting with EAP header)
+ * @min_payload: Minimum payload length needed
+ * Returns: 1 for valid header, 0 for invalid
+ *
+ * This is a helper function that does minimal validation of EAP messages. The
+ * length field is verified to be large enough to include the header and not
+ * too large to go beyond the end of the buffer.
+ */
+int eap_hdr_len_valid(const struct wpabuf *msg, size_t min_payload)
+{
+ const struct eap_hdr *hdr;
+ size_t len;
+
+ if (msg == NULL)
+ return 0;
+
+ hdr = wpabuf_head(msg);
+
+ if (wpabuf_len(msg) < sizeof(*hdr)) {
+ wpa_printf(MSG_INFO, "EAP: Too short EAP frame");
+ return 0;
+ }
+
+ len = be_to_host16(hdr->length);
+ if (len < sizeof(*hdr) + min_payload || len > wpabuf_len(msg)) {
+ wpa_printf(MSG_INFO, "EAP: Invalid EAP length");
+ return 0;
+ }
+
+ return 1;
+}
+
+
+/**
* eap_hdr_validate - Validate EAP header
* @vendor: Expected EAP Vendor-Id (0 = IETF)
* @eap_type: Expected EAP type number
@@ -41,19 +70,11 @@ const u8 * eap_hdr_validate(int vendor, EapType eap_type,
const u8 *pos;
size_t len;
- hdr = wpabuf_head(msg);
-
- if (wpabuf_len(msg) < sizeof(*hdr)) {
- wpa_printf(MSG_INFO, "EAP: Too short EAP frame");
+ if (!eap_hdr_len_valid(msg, 1))
return NULL;
- }
+ hdr = wpabuf_head(msg);
len = be_to_host16(hdr->length);
- if (len < sizeof(*hdr) + 1 || len > wpabuf_len(msg)) {
- wpa_printf(MSG_INFO, "EAP: Invalid EAP length");
- return NULL;
- }
-
pos = (const u8 *) (hdr + 1);
if (*pos == EAP_TYPE_EXPANDED) {
diff --git a/contrib/wpa/src/eap_common/eap_common.h b/contrib/wpa/src/eap_common/eap_common.h
index b95e76b..8850c1f 100644
--- a/contrib/wpa/src/eap_common/eap_common.h
+++ b/contrib/wpa/src/eap_common/eap_common.h
@@ -1,15 +1,9 @@
/*
* EAP common peer/server definitions
- * Copyright (c) 2004-2007, Jouni Malinen <j@w1.fi>
+ * Copyright (c) 2004-2012, Jouni Malinen <j@w1.fi>
*
- * This program is free software; you can redistribute it and/or modify
- * it under the terms of the GNU General Public License version 2 as
- * published by the Free Software Foundation.
- *
- * Alternatively, this software may be distributed under the terms of BSD
- * license.
- *
- * See README and COPYING for more details.
+ * This software may be distributed under the terms of the BSD license.
+ * See README for more details.
*/
#ifndef EAP_COMMON_H
@@ -17,6 +11,7 @@
#include "wpabuf.h"
+int eap_hdr_len_valid(const struct wpabuf *msg, size_t min_payload);
const u8 * eap_hdr_validate(int vendor, EapType eap_type,
const struct wpabuf *msg, size_t *plen);
struct wpabuf * eap_msg_alloc(int vendor, EapType type, size_t payload_len,
diff --git a/contrib/wpa/src/eap_common/eap_defs.h b/contrib/wpa/src/eap_common/eap_defs.h
index 0efe7ab..0d247c4 100644
--- a/contrib/wpa/src/eap_common/eap_defs.h
+++ b/contrib/wpa/src/eap_common/eap_defs.h
@@ -2,14 +2,8 @@
* EAP server/peer: Shared EAP definitions
* Copyright (c) 2004-2007, Jouni Malinen <j@w1.fi>
*
- * This program is free software; you can redistribute it and/or modify
- * it under the terms of the GNU General Public License version 2 as
- * published by the Free Software Foundation.
- *
- * Alternatively, this software may be distributed under the terms of BSD
- * license.
- *
- * See README and COPYING for more details.
+ * This software may be distributed under the terms of the BSD license.
+ * See README for more details.
*/
#ifndef EAP_DEFS_H
@@ -66,8 +60,9 @@ typedef enum {
EAP_TYPE_PSK = 47 /* RFC 4764 */,
EAP_TYPE_SAKE = 48 /* RFC 4763 */,
EAP_TYPE_IKEV2 = 49 /* RFC 5106 */,
- EAP_TYPE_AKA_PRIME = 50 /* draft-arkko-eap-aka-kdf-10.txt */,
+ EAP_TYPE_AKA_PRIME = 50 /* RFC 5448 */,
EAP_TYPE_GPSK = 51 /* RFC 5433 */,
+ EAP_TYPE_PWD = 52 /* RFC 5931 */,
EAP_TYPE_EXPANDED = 254 /* RFC 3748 */
} EapType;
@@ -76,9 +71,13 @@ typedef enum {
enum {
EAP_VENDOR_IETF = 0,
EAP_VENDOR_MICROSOFT = 0x000137 /* Microsoft */,
- EAP_VENDOR_WFA = 0x00372A /* Wi-Fi Alliance */
+ EAP_VENDOR_WFA = 0x00372A /* Wi-Fi Alliance */,
+ EAP_VENDOR_HOSTAP = 39068 /* hostapd/wpa_supplicant project */
};
+#define EAP_VENDOR_UNAUTH_TLS EAP_VENDOR_HOSTAP
+#define EAP_VENDOR_TYPE_UNAUTH_TLS 1
+
#define EAP_MSK_LEN 64
#define EAP_EMSK_LEN 64
diff --git a/contrib/wpa/src/eap_common/eap_fast_common.c b/contrib/wpa/src/eap_common/eap_fast_common.c
index 4de34a8..04b987d 100644
--- a/contrib/wpa/src/eap_common/eap_fast_common.c
+++ b/contrib/wpa/src/eap_common/eap_fast_common.c
@@ -2,14 +2,8 @@
* EAP-FAST common helper functions (RFC 4851)
* Copyright (c) 2008, Jouni Malinen <j@w1.fi>
*
- * This program is free software; you can redistribute it and/or modify
- * it under the terms of the GNU General Public License version 2 as
- * published by the Free Software Foundation.
- *
- * Alternatively, this software may be distributed under the terms of BSD
- * license.
- *
- * See README and COPYING for more details.
+ * This software may be distributed under the terms of the BSD license.
+ * See README for more details.
*/
#include "includes.h"
@@ -133,9 +127,9 @@ u8 * eap_fast_derive_key(void *ssl_ctx, struct tls_connection *conn,
wpa_hexdump_key(MSG_MSGDUMP, "EAP-FAST: master_secret for key "
"expansion", keys.master_key, keys.master_key_len);
- if (tls_prf(keys.master_key, keys.master_key_len,
- label, rnd, keys.client_random_len +
- keys.server_random_len, out, block_size + len))
+ if (tls_prf_sha1_md5(keys.master_key, keys.master_key_len,
+ label, rnd, keys.client_random_len +
+ keys.server_random_len, out, block_size + len))
goto fail;
os_free(rnd);
os_memmove(out, out + block_size, len);
diff --git a/contrib/wpa/src/eap_common/eap_fast_common.h b/contrib/wpa/src/eap_common/eap_fast_common.h
index c85fd37..8955617 100644
--- a/contrib/wpa/src/eap_common/eap_fast_common.h
+++ b/contrib/wpa/src/eap_common/eap_fast_common.h
@@ -2,14 +2,8 @@
* EAP-FAST definitions (RFC 4851)
* Copyright (c) 2004-2008, Jouni Malinen <j@w1.fi>
*
- * This program is free software; you can redistribute it and/or modify
- * it under the terms of the GNU General Public License version 2 as
- * published by the Free Software Foundation.
- *
- * Alternatively, this software may be distributed under the terms of BSD
- * license.
- *
- * See README and COPYING for more details.
+ * This software may be distributed under the terms of the BSD license.
+ * See README for more details.
*/
#ifndef EAP_FAST_H
diff --git a/contrib/wpa/src/eap_common/eap_gpsk_common.c b/contrib/wpa/src/eap_common/eap_gpsk_common.c
index 4076262..7d106dd 100644
--- a/contrib/wpa/src/eap_common/eap_gpsk_common.c
+++ b/contrib/wpa/src/eap_common/eap_gpsk_common.c
@@ -2,14 +2,8 @@
* EAP server/peer: EAP-GPSK shared routines
* Copyright (c) 2006-2007, Jouni Malinen <j@w1.fi>
*
- * This program is free software; you can redistribute it and/or modify
- * it under the terms of the GNU General Public License version 2 as
- * published by the Free Software Foundation.
- *
- * Alternatively, this software may be distributed under the terms of BSD
- * license.
- *
- * See README and COPYING for more details.
+ * This software may be distributed under the terms of the BSD license.
+ * See README for more details.
*/
#include "includes.h"
diff --git a/contrib/wpa/src/eap_common/eap_gpsk_common.h b/contrib/wpa/src/eap_common/eap_gpsk_common.h
index a30ab97..e3d2b6b 100644
--- a/contrib/wpa/src/eap_common/eap_gpsk_common.h
+++ b/contrib/wpa/src/eap_common/eap_gpsk_common.h
@@ -2,14 +2,8 @@
* EAP server/peer: EAP-GPSK shared routines
* Copyright (c) 2006-2007, Jouni Malinen <j@w1.fi>
*
- * This program is free software; you can redistribute it and/or modify
- * it under the terms of the GNU General Public License version 2 as
- * published by the Free Software Foundation.
- *
- * Alternatively, this software may be distributed under the terms of BSD
- * license.
- *
- * See README and COPYING for more details.
+ * This software may be distributed under the terms of the BSD license.
+ * See README for more details.
*/
#ifndef EAP_GPSK_COMMON_H
diff --git a/contrib/wpa/src/eap_common/eap_ikev2_common.c b/contrib/wpa/src/eap_common/eap_ikev2_common.c
index e9a9c55..6095fd8 100644
--- a/contrib/wpa/src/eap_common/eap_ikev2_common.c
+++ b/contrib/wpa/src/eap_common/eap_ikev2_common.c
@@ -2,14 +2,8 @@
* EAP-IKEv2 common routines
* Copyright (c) 2007, Jouni Malinen <j@w1.fi>
*
- * This program is free software; you can redistribute it and/or modify
- * it under the terms of the GNU General Public License version 2 as
- * published by the Free Software Foundation.
- *
- * Alternatively, this software may be distributed under the terms of BSD
- * license.
- *
- * See README and COPYING for more details.
+ * This software may be distributed under the terms of the BSD license.
+ * See README for more details.
*/
#include "includes.h"
diff --git a/contrib/wpa/src/eap_common/eap_ikev2_common.h b/contrib/wpa/src/eap_common/eap_ikev2_common.h
index a9fc2ca..329ccc4 100644
--- a/contrib/wpa/src/eap_common/eap_ikev2_common.h
+++ b/contrib/wpa/src/eap_common/eap_ikev2_common.h
@@ -2,14 +2,8 @@
* EAP-IKEv2 definitions
* Copyright (c) 2007, Jouni Malinen <j@w1.fi>
*
- * This program is free software; you can redistribute it and/or modify
- * it under the terms of the GNU General Public License version 2 as
- * published by the Free Software Foundation.
- *
- * Alternatively, this software may be distributed under the terms of BSD
- * license.
- *
- * See README and COPYING for more details.
+ * This software may be distributed under the terms of the BSD license.
+ * See README for more details.
*/
#ifndef EAP_IKEV2_COMMON_H
diff --git a/contrib/wpa/src/eap_common/eap_pax_common.c b/contrib/wpa/src/eap_common/eap_pax_common.c
index 32dc80c..b3bbacc 100644
--- a/contrib/wpa/src/eap_common/eap_pax_common.c
+++ b/contrib/wpa/src/eap_common/eap_pax_common.c
@@ -2,14 +2,8 @@
* EAP server/peer: EAP-PAX shared routines
* Copyright (c) 2005, Jouni Malinen <j@w1.fi>
*
- * This program is free software; you can redistribute it and/or modify
- * it under the terms of the GNU General Public License version 2 as
- * published by the Free Software Foundation.
- *
- * Alternatively, this software may be distributed under the terms of BSD
- * license.
- *
- * See README and COPYING for more details.
+ * This software may be distributed under the terms of the BSD license.
+ * See README for more details.
*/
#include "includes.h"
diff --git a/contrib/wpa/src/eap_common/eap_pax_common.h b/contrib/wpa/src/eap_common/eap_pax_common.h
index dcc171e..fb03df2 100644
--- a/contrib/wpa/src/eap_common/eap_pax_common.h
+++ b/contrib/wpa/src/eap_common/eap_pax_common.h
@@ -2,14 +2,8 @@
* EAP server/peer: EAP-PAX shared routines
* Copyright (c) 2005-2007, Jouni Malinen <j@w1.fi>
*
- * This program is free software; you can redistribute it and/or modify
- * it under the terms of the GNU General Public License version 2 as
- * published by the Free Software Foundation.
- *
- * Alternatively, this software may be distributed under the terms of BSD
- * license.
- *
- * See README and COPYING for more details.
+ * This software may be distributed under the terms of the BSD license.
+ * See README for more details.
*/
#ifndef EAP_PAX_COMMON_H
diff --git a/contrib/wpa/src/eap_common/eap_peap_common.c b/contrib/wpa/src/eap_common/eap_peap_common.c
index 3a64b8e..68b8878 100644
--- a/contrib/wpa/src/eap_common/eap_peap_common.c
+++ b/contrib/wpa/src/eap_common/eap_peap_common.c
@@ -1,15 +1,9 @@
/*
* EAP-PEAP common routines
- * Copyright (c) 2008, Jouni Malinen <j@w1.fi>
+ * Copyright (c) 2008-2011, Jouni Malinen <j@w1.fi>
*
- * This program is free software; you can redistribute it and/or modify
- * it under the terms of the GNU General Public License version 2 as
- * published by the Free Software Foundation.
- *
- * Alternatively, this software may be distributed under the terms of BSD
- * license.
- *
- * See README and COPYING for more details.
+ * This software may be distributed under the terms of the BSD license.
+ * See README for more details.
*/
#include "includes.h"
@@ -18,9 +12,9 @@
#include "crypto/sha1.h"
#include "eap_peap_common.h"
-void peap_prfplus(int version, const u8 *key, size_t key_len,
- const char *label, const u8 *seed, size_t seed_len,
- u8 *buf, size_t buf_len)
+int peap_prfplus(int version, const u8 *key, size_t key_len,
+ const char *label, const u8 *seed, size_t seed_len,
+ u8 *buf, size_t buf_len)
{
unsigned char counter = 0;
size_t pos, plen;
@@ -75,7 +69,8 @@ void peap_prfplus(int version, const u8 *key, size_t key_len,
while (pos < buf_len) {
counter++;
plen = buf_len - pos;
- hmac_sha1_vector(key, key_len, 5, addr, len, hash);
+ if (hmac_sha1_vector(key, key_len, 5, addr, len, hash) < 0)
+ return -1;
if (plen >= SHA1_MAC_LEN) {
os_memcpy(&buf[pos], hash, SHA1_MAC_LEN);
pos += SHA1_MAC_LEN;
@@ -85,4 +80,6 @@ void peap_prfplus(int version, const u8 *key, size_t key_len,
}
len[0] = SHA1_MAC_LEN;
}
+
+ return 0;
}
diff --git a/contrib/wpa/src/eap_common/eap_peap_common.h b/contrib/wpa/src/eap_common/eap_peap_common.h
index f59afb0..7aad0df 100644
--- a/contrib/wpa/src/eap_common/eap_peap_common.h
+++ b/contrib/wpa/src/eap_common/eap_peap_common.h
@@ -1,22 +1,16 @@
/*
* EAP-PEAP common routines
- * Copyright (c) 2008, Jouni Malinen <j@w1.fi>
+ * Copyright (c) 2008-2011, Jouni Malinen <j@w1.fi>
*
- * This program is free software; you can redistribute it and/or modify
- * it under the terms of the GNU General Public License version 2 as
- * published by the Free Software Foundation.
- *
- * Alternatively, this software may be distributed under the terms of BSD
- * license.
- *
- * See README and COPYING for more details.
+ * This software may be distributed under the terms of the BSD license.
+ * See README for more details.
*/
#ifndef EAP_PEAP_COMMON_H
#define EAP_PEAP_COMMON_H
-void peap_prfplus(int version, const u8 *key, size_t key_len,
- const char *label, const u8 *seed, size_t seed_len,
- u8 *buf, size_t buf_len);
+int peap_prfplus(int version, const u8 *key, size_t key_len,
+ const char *label, const u8 *seed, size_t seed_len,
+ u8 *buf, size_t buf_len);
#endif /* EAP_PEAP_COMMON_H */
diff --git a/contrib/wpa/src/eap_common/eap_psk_common.c b/contrib/wpa/src/eap_common/eap_psk_common.c
index 7417d5c..638102f 100644
--- a/contrib/wpa/src/eap_common/eap_psk_common.c
+++ b/contrib/wpa/src/eap_common/eap_psk_common.c
@@ -2,14 +2,8 @@
* EAP server/peer: EAP-PSK shared routines
* Copyright (c) 2004-2006, Jouni Malinen <j@w1.fi>
*
- * This program is free software; you can redistribute it and/or modify
- * it under the terms of the GNU General Public License version 2 as
- * published by the Free Software Foundation.
- *
- * Alternatively, this software may be distributed under the terms of BSD
- * license.
- *
- * See README and COPYING for more details.
+ * This software may be distributed under the terms of the BSD license.
+ * See README for more details.
*/
#include "includes.h"
diff --git a/contrib/wpa/src/eap_common/eap_psk_common.h b/contrib/wpa/src/eap_common/eap_psk_common.h
index 8adc054..8bc2c3c 100644
--- a/contrib/wpa/src/eap_common/eap_psk_common.h
+++ b/contrib/wpa/src/eap_common/eap_psk_common.h
@@ -2,14 +2,8 @@
* EAP server/peer: EAP-PSK shared routines
* Copyright (c) 2004-2007, Jouni Malinen <j@w1.fi>
*
- * This program is free software; you can redistribute it and/or modify
- * it under the terms of the GNU General Public License version 2 as
- * published by the Free Software Foundation.
- *
- * Alternatively, this software may be distributed under the terms of BSD
- * license.
- *
- * See README and COPYING for more details.
+ * This software may be distributed under the terms of the BSD license.
+ * See README for more details.
*/
#ifndef EAP_PSK_COMMON_H
diff --git a/contrib/wpa/src/eap_common/eap_pwd_common.c b/contrib/wpa/src/eap_common/eap_pwd_common.c
new file mode 100644
index 0000000..7d6e6b8
--- /dev/null
+++ b/contrib/wpa/src/eap_common/eap_pwd_common.c
@@ -0,0 +1,345 @@
+/*
+ * EAP server/peer: EAP-pwd shared routines
+ * Copyright (c) 2010, Dan Harkins <dharkins@lounge.org>
+ *
+ * This software may be distributed under the terms of the BSD license.
+ * See README for more details.
+ */
+
+#include "includes.h"
+#include "common.h"
+#include "crypto/sha256.h"
+#include "crypto/crypto.h"
+#include "eap_defs.h"
+#include "eap_pwd_common.h"
+
+/* The random function H(x) = HMAC-SHA256(0^32, x) */
+struct crypto_hash * eap_pwd_h_init(void)
+{
+ u8 allzero[SHA256_MAC_LEN];
+ os_memset(allzero, 0, SHA256_MAC_LEN);
+ return crypto_hash_init(CRYPTO_HASH_ALG_HMAC_SHA256, allzero,
+ SHA256_MAC_LEN);
+}
+
+
+void eap_pwd_h_update(struct crypto_hash *hash, const u8 *data, size_t len)
+{
+ crypto_hash_update(hash, data, len);
+}
+
+
+void eap_pwd_h_final(struct crypto_hash *hash, u8 *digest)
+{
+ size_t len = SHA256_MAC_LEN;
+ crypto_hash_finish(hash, digest, &len);
+}
+
+
+/* a counter-based KDF based on NIST SP800-108 */
+static int eap_pwd_kdf(const u8 *key, size_t keylen, const u8 *label,
+ size_t labellen, u8 *result, size_t resultbitlen)
+{
+ struct crypto_hash *hash;
+ u8 digest[SHA256_MAC_LEN];
+ u16 i, ctr, L;
+ size_t resultbytelen, len = 0, mdlen;
+
+ resultbytelen = (resultbitlen + 7) / 8;
+ ctr = 0;
+ L = htons(resultbitlen);
+ while (len < resultbytelen) {
+ ctr++;
+ i = htons(ctr);
+ hash = crypto_hash_init(CRYPTO_HASH_ALG_HMAC_SHA256,
+ key, keylen);
+ if (hash == NULL)
+ return -1;
+ if (ctr > 1)
+ crypto_hash_update(hash, digest, SHA256_MAC_LEN);
+ crypto_hash_update(hash, (u8 *) &i, sizeof(u16));
+ crypto_hash_update(hash, label, labellen);
+ crypto_hash_update(hash, (u8 *) &L, sizeof(u16));
+ mdlen = SHA256_MAC_LEN;
+ if (crypto_hash_finish(hash, digest, &mdlen) < 0)
+ return -1;
+ if ((len + mdlen) > resultbytelen)
+ os_memcpy(result + len, digest, resultbytelen - len);
+ else
+ os_memcpy(result + len, digest, mdlen);
+ len += mdlen;
+ }
+
+ /* since we're expanding to a bit length, mask off the excess */
+ if (resultbitlen % 8) {
+ u8 mask = 0xff;
+ mask <<= (8 - (resultbitlen % 8));
+ result[resultbytelen - 1] &= mask;
+ }
+
+ return 0;
+}
+
+
+/*
+ * compute a "random" secret point on an elliptic curve based
+ * on the password and identities.
+ */
+int compute_password_element(EAP_PWD_group *grp, u16 num,
+ u8 *password, int password_len,
+ u8 *id_server, int id_server_len,
+ u8 *id_peer, int id_peer_len, u8 *token)
+{
+ BIGNUM *x_candidate = NULL, *rnd = NULL, *cofactor = NULL;
+ struct crypto_hash *hash;
+ unsigned char pwe_digest[SHA256_MAC_LEN], *prfbuf = NULL, ctr;
+ int nid, is_odd, ret = 0;
+ size_t primebytelen, primebitlen;
+
+ switch (num) { /* from IANA registry for IKE D-H groups */
+ case 19:
+ nid = NID_X9_62_prime256v1;
+ break;
+ case 20:
+ nid = NID_secp384r1;
+ break;
+ case 21:
+ nid = NID_secp521r1;
+ break;
+ case 25:
+ nid = NID_X9_62_prime192v1;
+ break;
+ case 26:
+ nid = NID_secp224r1;
+ break;
+ default:
+ wpa_printf(MSG_INFO, "EAP-pwd: unsupported group %d", num);
+ return -1;
+ }
+
+ grp->pwe = NULL;
+ grp->order = NULL;
+ grp->prime = NULL;
+
+ if ((grp->group = EC_GROUP_new_by_curve_name(nid)) == NULL) {
+ wpa_printf(MSG_INFO, "EAP-pwd: unable to create EC_GROUP");
+ goto fail;
+ }
+
+ if (((rnd = BN_new()) == NULL) ||
+ ((cofactor = BN_new()) == NULL) ||
+ ((grp->pwe = EC_POINT_new(grp->group)) == NULL) ||
+ ((grp->order = BN_new()) == NULL) ||
+ ((grp->prime = BN_new()) == NULL) ||
+ ((x_candidate = BN_new()) == NULL)) {
+ wpa_printf(MSG_INFO, "EAP-pwd: unable to create bignums");
+ goto fail;
+ }
+
+ if (!EC_GROUP_get_curve_GFp(grp->group, grp->prime, NULL, NULL, NULL))
+ {
+ wpa_printf(MSG_INFO, "EAP-pwd: unable to get prime for GFp "
+ "curve");
+ goto fail;
+ }
+ if (!EC_GROUP_get_order(grp->group, grp->order, NULL)) {
+ wpa_printf(MSG_INFO, "EAP-pwd: unable to get order for curve");
+ goto fail;
+ }
+ if (!EC_GROUP_get_cofactor(grp->group, cofactor, NULL)) {
+ wpa_printf(MSG_INFO, "EAP-pwd: unable to get cofactor for "
+ "curve");
+ goto fail;
+ }
+ primebitlen = BN_num_bits(grp->prime);
+ primebytelen = BN_num_bytes(grp->prime);
+ if ((prfbuf = os_malloc(primebytelen)) == NULL) {
+ wpa_printf(MSG_INFO, "EAP-pwd: unable to malloc space for prf "
+ "buffer");
+ goto fail;
+ }
+ os_memset(prfbuf, 0, primebytelen);
+ ctr = 0;
+ while (1) {
+ if (ctr > 30) {
+ wpa_printf(MSG_INFO, "EAP-pwd: unable to find random "
+ "point on curve for group %d, something's "
+ "fishy", num);
+ goto fail;
+ }
+ ctr++;
+
+ /*
+ * compute counter-mode password value and stretch to prime
+ * pwd-seed = H(token | peer-id | server-id | password |
+ * counter)
+ */
+ hash = eap_pwd_h_init();
+ if (hash == NULL)
+ goto fail;
+ eap_pwd_h_update(hash, token, sizeof(u32));
+ eap_pwd_h_update(hash, id_peer, id_peer_len);
+ eap_pwd_h_update(hash, id_server, id_server_len);
+ eap_pwd_h_update(hash, password, password_len);
+ eap_pwd_h_update(hash, &ctr, sizeof(ctr));
+ eap_pwd_h_final(hash, pwe_digest);
+
+ BN_bin2bn(pwe_digest, SHA256_MAC_LEN, rnd);
+
+ if (eap_pwd_kdf(pwe_digest, SHA256_MAC_LEN,
+ (u8 *) "EAP-pwd Hunting And Pecking",
+ os_strlen("EAP-pwd Hunting And Pecking"),
+ prfbuf, primebitlen) < 0)
+ goto fail;
+
+ BN_bin2bn(prfbuf, primebytelen, x_candidate);
+
+ /*
+ * eap_pwd_kdf() returns a string of bits 0..primebitlen but
+ * BN_bin2bn will treat that string of bits as a big endian
+ * number. If the primebitlen is not an even multiple of 8
+ * then excessive bits-- those _after_ primebitlen-- so now
+ * we have to shift right the amount we masked off.
+ */
+ if (primebitlen % 8)
+ BN_rshift(x_candidate, x_candidate,
+ (8 - (primebitlen % 8)));
+
+ if (BN_ucmp(x_candidate, grp->prime) >= 0)
+ continue;
+
+ wpa_hexdump(MSG_DEBUG, "EAP-pwd: x_candidate",
+ prfbuf, primebytelen);
+
+ /*
+ * need to unambiguously identify the solution, if there is
+ * one...
+ */
+ if (BN_is_odd(rnd))
+ is_odd = 1;
+ else
+ is_odd = 0;
+
+ /*
+ * solve the quadratic equation, if it's not solvable then we
+ * don't have a point
+ */
+ if (!EC_POINT_set_compressed_coordinates_GFp(grp->group,
+ grp->pwe,
+ x_candidate,
+ is_odd, NULL))
+ continue;
+ /*
+ * If there's a solution to the equation then the point must be
+ * on the curve so why check again explicitly? OpenSSL code
+ * says this is required by X9.62. We're not X9.62 but it can't
+ * hurt just to be sure.
+ */
+ if (!EC_POINT_is_on_curve(grp->group, grp->pwe, NULL)) {
+ wpa_printf(MSG_INFO, "EAP-pwd: point is not on curve");
+ continue;
+ }
+
+ if (BN_cmp(cofactor, BN_value_one())) {
+ /* make sure the point is not in a small sub-group */
+ if (!EC_POINT_mul(grp->group, grp->pwe, NULL, grp->pwe,
+ cofactor, NULL)) {
+ wpa_printf(MSG_INFO, "EAP-pwd: cannot "
+ "multiply generator by order");
+ continue;
+ }
+ if (EC_POINT_is_at_infinity(grp->group, grp->pwe)) {
+ wpa_printf(MSG_INFO, "EAP-pwd: point is at "
+ "infinity");
+ continue;
+ }
+ }
+ /* if we got here then we have a new generator. */
+ break;
+ }
+ wpa_printf(MSG_DEBUG, "EAP-pwd: found a PWE in %d tries", ctr);
+ grp->group_num = num;
+ if (0) {
+ fail:
+ EC_GROUP_free(grp->group);
+ grp->group = NULL;
+ EC_POINT_free(grp->pwe);
+ grp->pwe = NULL;
+ BN_free(grp->order);
+ grp->order = NULL;
+ BN_free(grp->prime);
+ grp->prime = NULL;
+ ret = 1;
+ }
+ /* cleanliness and order.... */
+ BN_free(cofactor);
+ BN_free(x_candidate);
+ BN_free(rnd);
+ os_free(prfbuf);
+
+ return ret;
+}
+
+
+int compute_keys(EAP_PWD_group *grp, BN_CTX *bnctx, BIGNUM *k,
+ BIGNUM *peer_scalar, BIGNUM *server_scalar,
+ u8 *confirm_peer, u8 *confirm_server,
+ u32 *ciphersuite, u8 *msk, u8 *emsk)
+{
+ struct crypto_hash *hash;
+ u8 mk[SHA256_MAC_LEN], *cruft;
+ u8 session_id[SHA256_MAC_LEN + 1];
+ u8 msk_emsk[EAP_MSK_LEN + EAP_EMSK_LEN];
+ int offset;
+
+ if ((cruft = os_malloc(BN_num_bytes(grp->prime))) == NULL)
+ return -1;
+
+ /*
+ * first compute the session-id = TypeCode | H(ciphersuite | scal_p |
+ * scal_s)
+ */
+ session_id[0] = EAP_TYPE_PWD;
+ hash = eap_pwd_h_init();
+ if (hash == NULL) {
+ os_free(cruft);
+ return -1;
+ }
+ eap_pwd_h_update(hash, (u8 *) ciphersuite, sizeof(u32));
+ offset = BN_num_bytes(grp->order) - BN_num_bytes(peer_scalar);
+ os_memset(cruft, 0, BN_num_bytes(grp->prime));
+ BN_bn2bin(peer_scalar, cruft + offset);
+ eap_pwd_h_update(hash, cruft, BN_num_bytes(grp->order));
+ offset = BN_num_bytes(grp->order) - BN_num_bytes(server_scalar);
+ os_memset(cruft, 0, BN_num_bytes(grp->prime));
+ BN_bn2bin(server_scalar, cruft + offset);
+ eap_pwd_h_update(hash, cruft, BN_num_bytes(grp->order));
+ eap_pwd_h_final(hash, &session_id[1]);
+
+ /* then compute MK = H(k | confirm-peer | confirm-server) */
+ hash = eap_pwd_h_init();
+ if (hash == NULL) {
+ os_free(cruft);
+ return -1;
+ }
+ offset = BN_num_bytes(grp->prime) - BN_num_bytes(k);
+ os_memset(cruft, 0, BN_num_bytes(grp->prime));
+ BN_bn2bin(k, cruft + offset);
+ eap_pwd_h_update(hash, cruft, BN_num_bytes(grp->prime));
+ os_free(cruft);
+ eap_pwd_h_update(hash, confirm_peer, SHA256_MAC_LEN);
+ eap_pwd_h_update(hash, confirm_server, SHA256_MAC_LEN);
+ eap_pwd_h_final(hash, mk);
+
+ /* stretch the mk with the session-id to get MSK | EMSK */
+ if (eap_pwd_kdf(mk, SHA256_MAC_LEN,
+ session_id, SHA256_MAC_LEN + 1,
+ msk_emsk, (EAP_MSK_LEN + EAP_EMSK_LEN) * 8) < 0) {
+ return -1;
+ }
+
+ os_memcpy(msk, msk_emsk, EAP_MSK_LEN);
+ os_memcpy(emsk, msk_emsk + EAP_MSK_LEN, EAP_EMSK_LEN);
+
+ return 1;
+}
diff --git a/contrib/wpa/src/eap_common/eap_pwd_common.h b/contrib/wpa/src/eap_common/eap_pwd_common.h
new file mode 100644
index 0000000..816e58c
--- /dev/null
+++ b/contrib/wpa/src/eap_common/eap_pwd_common.h
@@ -0,0 +1,67 @@
+/*
+ * EAP server/peer: EAP-pwd shared definitions
+ * Copyright (c) 2009, Dan Harkins <dharkins@lounge.org>
+ *
+ * This software may be distributed under the terms of the BSD license.
+ * See README for more details.
+ */
+
+#ifndef EAP_PWD_COMMON_H
+#define EAP_PWD_COMMON_H
+
+#include <openssl/bn.h>
+#include <openssl/ec.h>
+#include <openssl/evp.h>
+
+/*
+ * definition of a finite cyclic group
+ * TODO: support one based on a prime field
+ */
+typedef struct group_definition_ {
+ u16 group_num;
+ EC_GROUP *group;
+ EC_POINT *pwe;
+ BIGNUM *order;
+ BIGNUM *prime;
+} EAP_PWD_group;
+
+/*
+ * EAP-pwd header, included on all payloads
+ * L(1 bit) | M(1 bit) | exch(6 bits) | total_length(if L is set)
+ */
+#define EAP_PWD_HDR_SIZE 1
+
+#define EAP_PWD_OPCODE_ID_EXCH 1
+#define EAP_PWD_OPCODE_COMMIT_EXCH 2
+#define EAP_PWD_OPCODE_CONFIRM_EXCH 3
+#define EAP_PWD_GET_LENGTH_BIT(x) ((x) & 0x80)
+#define EAP_PWD_SET_LENGTH_BIT(x) ((x) |= 0x80)
+#define EAP_PWD_GET_MORE_BIT(x) ((x) & 0x40)
+#define EAP_PWD_SET_MORE_BIT(x) ((x) |= 0x40)
+#define EAP_PWD_GET_EXCHANGE(x) ((x) & 0x3f)
+#define EAP_PWD_SET_EXCHANGE(x,y) ((x) |= (y))
+
+/* EAP-pwd-ID payload */
+struct eap_pwd_id {
+ be16 group_num;
+ u8 random_function;
+#define EAP_PWD_DEFAULT_RAND_FUNC 1
+ u8 prf;
+#define EAP_PWD_DEFAULT_PRF 1
+ u8 token[4];
+ u8 prep;
+#define EAP_PWD_PREP_NONE 0
+#define EAP_PWD_PREP_MS 1
+ u8 identity[0]; /* length inferred from payload */
+} STRUCT_PACKED;
+
+/* common routines */
+int compute_password_element(EAP_PWD_group *, u16, u8 *, int, u8 *, int, u8 *,
+ int, u8 *);
+int compute_keys(EAP_PWD_group *, BN_CTX *, BIGNUM *, BIGNUM *, BIGNUM *,
+ u8 *, u8 *, u32 *, u8 *, u8 *);
+struct crypto_hash * eap_pwd_h_init(void);
+void eap_pwd_h_update(struct crypto_hash *hash, const u8 *data, size_t len);
+void eap_pwd_h_final(struct crypto_hash *hash, u8 *digest);
+
+#endif /* EAP_PWD_COMMON_H */
diff --git a/contrib/wpa/src/eap_common/eap_sake_common.c b/contrib/wpa/src/eap_common/eap_sake_common.c
index 9002b0c..a76253d 100644
--- a/contrib/wpa/src/eap_common/eap_sake_common.c
+++ b/contrib/wpa/src/eap_common/eap_sake_common.c
@@ -2,14 +2,8 @@
* EAP server/peer: EAP-SAKE shared routines
* Copyright (c) 2006-2007, Jouni Malinen <j@w1.fi>
*
- * This program is free software; you can redistribute it and/or modify
- * it under the terms of the GNU General Public License version 2 as
- * published by the Free Software Foundation.
- *
- * Alternatively, this software may be distributed under the terms of BSD
- * license.
- *
- * See README and COPYING for more details.
+ * This software may be distributed under the terms of the BSD license.
+ * See README for more details.
*/
#include "includes.h"
diff --git a/contrib/wpa/src/eap_common/eap_sake_common.h b/contrib/wpa/src/eap_common/eap_sake_common.h
index 201e207..9e1e757 100644
--- a/contrib/wpa/src/eap_common/eap_sake_common.h
+++ b/contrib/wpa/src/eap_common/eap_sake_common.h
@@ -2,14 +2,8 @@
* EAP server/peer: EAP-SAKE shared routines
* Copyright (c) 2006-2007, Jouni Malinen <j@w1.fi>
*
- * This program is free software; you can redistribute it and/or modify
- * it under the terms of the GNU General Public License version 2 as
- * published by the Free Software Foundation.
- *
- * Alternatively, this software may be distributed under the terms of BSD
- * license.
- *
- * See README and COPYING for more details.
+ * This software may be distributed under the terms of the BSD license.
+ * See README for more details.
*/
#ifndef EAP_SAKE_COMMON_H
diff --git a/contrib/wpa/src/eap_common/eap_sim_common.c b/contrib/wpa/src/eap_common/eap_sim_common.c
index 56b4ded..e1773bf 100644
--- a/contrib/wpa/src/eap_common/eap_sim_common.c
+++ b/contrib/wpa/src/eap_common/eap_sim_common.c
@@ -2,14 +2,8 @@
* EAP peer/server: EAP-SIM/AKA/AKA' shared routines
* Copyright (c) 2004-2008, Jouni Malinen <j@w1.fi>
*
- * This program is free software; you can redistribute it and/or modify
- * it under the terms of the GNU General Public License version 2 as
- * published by the Free Software Foundation.
- *
- * Alternatively, this software may be distributed under the terms of BSD
- * license.
- *
- * See README and COPYING for more details.
+ * This software may be distributed under the terms of the BSD license.
+ * See README for more details.
*/
#include "includes.h"
@@ -20,6 +14,7 @@
#include "crypto/crypto.h"
#include "crypto/sha1.h"
#include "crypto/sha256.h"
+#include "crypto/random.h"
#include "eap_common/eap_defs.h"
#include "eap_common/eap_sim_common.h"
@@ -1121,8 +1116,8 @@ int eap_sim_msg_add_encr_start(struct eap_sim_msg *msg, u8 attr_iv,
if (pos == NULL)
return -1;
msg->iv = (pos - wpabuf_head_u8(msg->buf)) + 4;
- if (os_get_random(wpabuf_mhead_u8(msg->buf) + msg->iv,
- EAP_SIM_IV_LEN)) {
+ if (random_get_bytes(wpabuf_mhead_u8(msg->buf) + msg->iv,
+ EAP_SIM_IV_LEN)) {
msg->iv = 0;
return -1;
}
diff --git a/contrib/wpa/src/eap_common/eap_sim_common.h b/contrib/wpa/src/eap_common/eap_sim_common.h
index 48c8eaa..6021bd2 100644
--- a/contrib/wpa/src/eap_common/eap_sim_common.h
+++ b/contrib/wpa/src/eap_common/eap_sim_common.h
@@ -2,14 +2,8 @@
* EAP peer/server: EAP-SIM/AKA/AKA' shared routines
* Copyright (c) 2004-2008, Jouni Malinen <j@w1.fi>
*
- * This program is free software; you can redistribute it and/or modify
- * it under the terms of the GNU General Public License version 2 as
- * published by the Free Software Foundation.
- *
- * Alternatively, this software may be distributed under the terms of BSD
- * license.
- *
- * See README and COPYING for more details.
+ * This software may be distributed under the terms of the BSD license.
+ * See README for more details.
*/
#ifndef EAP_SIM_COMMON_H
diff --git a/contrib/wpa/src/eap_common/eap_tlv_common.h b/contrib/wpa/src/eap_common/eap_tlv_common.h
index f86015d..3286055 100644
--- a/contrib/wpa/src/eap_common/eap_tlv_common.h
+++ b/contrib/wpa/src/eap_common/eap_tlv_common.h
@@ -2,14 +2,8 @@
* EAP-TLV definitions (draft-josefsson-pppext-eap-tls-eap-10.txt)
* Copyright (c) 2004-2008, Jouni Malinen <j@w1.fi>
*
- * This program is free software; you can redistribute it and/or modify
- * it under the terms of the GNU General Public License version 2 as
- * published by the Free Software Foundation.
- *
- * Alternatively, this software may be distributed under the terms of BSD
- * license.
- *
- * See README and COPYING for more details.
+ * This software may be distributed under the terms of the BSD license.
+ * See README for more details.
*/
#ifndef EAP_TLV_COMMON_H
diff --git a/contrib/wpa/src/eap_common/eap_ttls.h b/contrib/wpa/src/eap_common/eap_ttls.h
index 797d084..17901d4 100644
--- a/contrib/wpa/src/eap_common/eap_ttls.h
+++ b/contrib/wpa/src/eap_common/eap_ttls.h
@@ -2,14 +2,8 @@
* EAP server/peer: EAP-TTLS (RFC 5281)
* Copyright (c) 2004-2007, Jouni Malinen <j@w1.fi>
*
- * This program is free software; you can redistribute it and/or modify
- * it under the terms of the GNU General Public License version 2 as
- * published by the Free Software Foundation.
- *
- * Alternatively, this software may be distributed under the terms of BSD
- * license.
- *
- * See README and COPYING for more details.
+ * This software may be distributed under the terms of the BSD license.
+ * See README for more details.
*/
#ifndef EAP_TTLS_H
diff --git a/contrib/wpa/src/eap_common/eap_wsc_common.c b/contrib/wpa/src/eap_common/eap_wsc_common.c
index 5d4e8cc..7c1496e 100644
--- a/contrib/wpa/src/eap_common/eap_wsc_common.c
+++ b/contrib/wpa/src/eap_common/eap_wsc_common.c
@@ -2,14 +2,8 @@
* EAP-WSC common routines for Wi-Fi Protected Setup
* Copyright (c) 2007, Jouni Malinen <j@w1.fi>
*
- * This program is free software; you can redistribute it and/or modify
- * it under the terms of the GNU General Public License version 2 as
- * published by the Free Software Foundation.
- *
- * Alternatively, this software may be distributed under the terms of BSD
- * license.
- *
- * See README and COPYING for more details.
+ * This software may be distributed under the terms of the BSD license.
+ * See README for more details.
*/
#include "includes.h"
diff --git a/contrib/wpa/src/eap_common/eap_wsc_common.h b/contrib/wpa/src/eap_common/eap_wsc_common.h
index fdf61d3..0e7b653 100644
--- a/contrib/wpa/src/eap_common/eap_wsc_common.h
+++ b/contrib/wpa/src/eap_common/eap_wsc_common.h
@@ -2,14 +2,8 @@
* EAP-WSC definitions for Wi-Fi Protected Setup
* Copyright (c) 2007, Jouni Malinen <j@w1.fi>
*
- * This program is free software; you can redistribute it and/or modify
- * it under the terms of the GNU General Public License version 2 as
- * published by the Free Software Foundation.
- *
- * Alternatively, this software may be distributed under the terms of BSD
- * license.
- *
- * See README and COPYING for more details.
+ * This software may be distributed under the terms of the BSD license.
+ * See README for more details.
*/
#ifndef EAP_WSC_COMMON_H
diff --git a/contrib/wpa/src/eap_common/ikev2_common.c b/contrib/wpa/src/eap_common/ikev2_common.c
index 67754d8..376fcad 100644
--- a/contrib/wpa/src/eap_common/ikev2_common.c
+++ b/contrib/wpa/src/eap_common/ikev2_common.c
@@ -2,14 +2,8 @@
* IKEv2 common routines for initiator and responder
* Copyright (c) 2007, Jouni Malinen <j@w1.fi>
*
- * This program is free software; you can redistribute it and/or modify
- * it under the terms of the GNU General Public License version 2 as
- * published by the Free Software Foundation.
- *
- * Alternatively, this software may be distributed under the terms of BSD
- * license.
- *
- * See README and COPYING for more details.
+ * This software may be distributed under the terms of the BSD license.
+ * See README for more details.
*/
#include "includes.h"
@@ -18,6 +12,7 @@
#include "crypto/crypto.h"
#include "crypto/md5.h"
#include "crypto/sha1.h"
+#include "crypto/random.h"
#include "ikev2_common.h"
@@ -639,7 +634,7 @@ int ikev2_build_encrypted(int encr_id, int integ_id, struct ikev2_keys *keys,
phdr->flags = 0;
iv = wpabuf_put(msg, iv_len);
- if (os_get_random(iv, iv_len)) {
+ if (random_get_bytes(iv, iv_len)) {
wpa_printf(MSG_INFO, "IKEV2: Could not generate IV");
return -1;
}
diff --git a/contrib/wpa/src/eap_common/ikev2_common.h b/contrib/wpa/src/eap_common/ikev2_common.h
index c96a070..45c970b 100644
--- a/contrib/wpa/src/eap_common/ikev2_common.h
+++ b/contrib/wpa/src/eap_common/ikev2_common.h
@@ -2,14 +2,8 @@
* IKEv2 definitions
* Copyright (c) 2007, Jouni Malinen <j@w1.fi>
*
- * This program is free software; you can redistribute it and/or modify
- * it under the terms of the GNU General Public License version 2 as
- * published by the Free Software Foundation.
- *
- * Alternatively, this software may be distributed under the terms of BSD
- * license.
- *
- * See README and COPYING for more details.
+ * This software may be distributed under the terms of the BSD license.
+ * See README for more details.
*/
#ifndef IKEV2_COMMON_H
@@ -139,7 +133,7 @@ enum {
IKEV2_TRANSFORM_ESN = 5
};
-/* IKEv2 Tranform Type 1 (Encryption Algorithm) */
+/* IKEv2 Transform Type 1 (Encryption Algorithm) */
enum {
ENCR_DES_IV64 = 1,
ENCR_DES = 2,
diff --git a/contrib/wpa/src/eap_peer/Makefile b/contrib/wpa/src/eap_peer/Makefile
deleted file mode 100644
index 3651056..0000000
--- a/contrib/wpa/src/eap_peer/Makefile
+++ /dev/null
@@ -1,11 +0,0 @@
-all:
- @echo Nothing to be made.
-
-clean:
- rm -f *~ *.o *.so *.d
-
-install:
- if ls *.so >/dev/null 2>&1; then \
- install -d $(DESTDIR)$(LIBDIR)/wpa_supplicant && \
- cp *.so $(DESTDIR)$(LIBDIR)/wpa_supplicant \
- ; fi
diff --git a/contrib/wpa/src/eap_peer/eap.c b/contrib/wpa/src/eap_peer/eap.c
index b9f186b..a4c9b25 100644
--- a/contrib/wpa/src/eap_peer/eap.c
+++ b/contrib/wpa/src/eap_peer/eap.c
@@ -1,15 +1,9 @@
/*
* EAP peer state machines (RFC 4137)
- * Copyright (c) 2004-2010, Jouni Malinen <j@w1.fi>
+ * Copyright (c) 2004-2012, Jouni Malinen <j@w1.fi>
*
- * This program is free software; you can redistribute it and/or modify
- * it under the terms of the GNU General Public License version 2 as
- * published by the Free Software Foundation.
- *
- * Alternatively, this software may be distributed under the terms of BSD
- * license.
- *
- * See README and COPYING for more details.
+ * This software may be distributed under the terms of the BSD license.
+ * See README for more details.
*
* This file implements the Peer State Machine as defined in RFC 4137. The used
* states and state transitions match mostly with the RFC. However, there are
@@ -26,6 +20,7 @@
#include "common.h"
#include "pcsc_funcs.h"
#include "state_machine.h"
+#include "ext_password.h"
#include "crypto/crypto.h"
#include "crypto/tls.h"
#include "common/wpa_ctrl.h"
@@ -37,6 +32,7 @@
#define STATE_MACHINE_DEBUG_PREFIX "EAP"
#define EAP_MAX_AUTH_ROUNDS 50
+#define EAP_CLIENT_TIMEOUT_DEFAULT 60
static Boolean eap_sm_allowMethod(struct eap_sm *sm, int vendor,
@@ -86,8 +82,21 @@ static struct wpabuf * eapol_get_eapReqData(struct eap_sm *sm)
}
+static void eap_notify_status(struct eap_sm *sm, const char *status,
+ const char *parameter)
+{
+ wpa_printf(MSG_DEBUG, "EAP: Status notification: %s (param=%s)",
+ status, parameter);
+ if (sm->eapol_cb->notify_status)
+ sm->eapol_cb->notify_status(sm->eapol_ctx, status, parameter);
+}
+
+
static void eap_deinit_prev_method(struct eap_sm *sm, const char *txt)
{
+ ext_password_free(sm->ext_pw_buf);
+ sm->ext_pw_buf = NULL;
+
if (sm->m == NULL || sm->eap_method_priv == NULL)
return;
@@ -146,6 +155,7 @@ SM_STATE(EAP, INITIALIZE)
sm->methodState = METHOD_NONE;
sm->allowNotifications = TRUE;
sm->decision = DECISION_FAIL;
+ sm->ClientTimeout = EAP_CLIENT_TIMEOUT_DEFAULT;
eapol_set_int(sm, EAPOL_idleWhile, sm->ClientTimeout);
eapol_set_bool(sm, EAPOL_eapSuccess, FALSE);
eapol_set_bool(sm, EAPOL_eapFail, FALSE);
@@ -179,6 +189,12 @@ SM_STATE(EAP, DISABLED)
{
SM_ENTRY(EAP, DISABLED);
sm->num_rounds = 0;
+ /*
+ * RFC 4137 does not describe clearing of idleWhile here, but doing so
+ * allows the timer tick to be stopped more quickly when EAP is not in
+ * use.
+ */
+ eapol_set_int(sm, EAPOL_idleWhile, 0);
}
@@ -217,6 +233,7 @@ SM_STATE(EAP, GET_METHOD)
{
int reinit;
EapType method;
+ const struct eap_method *eap_method;
SM_ENTRY(EAP, GET_METHOD);
@@ -225,18 +242,24 @@ SM_STATE(EAP, GET_METHOD)
else
method = sm->reqMethod;
+ eap_method = eap_peer_get_eap_method(sm->reqVendor, method);
+
if (!eap_sm_allowMethod(sm, sm->reqVendor, method)) {
wpa_printf(MSG_DEBUG, "EAP: vendor %u method %u not allowed",
sm->reqVendor, method);
wpa_msg(sm->msg_ctx, MSG_INFO, WPA_EVENT_EAP_PROPOSED_METHOD
"vendor=%u method=%u -> NAK",
sm->reqVendor, method);
+ eap_notify_status(sm, "refuse proposed method",
+ eap_method ? eap_method->name : "unknown");
goto nak;
}
wpa_msg(sm->msg_ctx, MSG_INFO, WPA_EVENT_EAP_PROPOSED_METHOD
"vendor=%u method=%u", sm->reqVendor, method);
+ eap_notify_status(sm, "accept proposed method",
+ eap_method ? eap_method->name : "unknown");
/*
* RFC 4137 does not define specific operation for fast
* re-authentication (session resumption). The design here is to allow
@@ -260,7 +283,7 @@ SM_STATE(EAP, GET_METHOD)
sm->selectedMethod = sm->reqMethod;
if (sm->m == NULL)
- sm->m = eap_peer_get_eap_method(sm->reqVendor, method);
+ sm->m = eap_method;
if (!sm->m) {
wpa_printf(MSG_DEBUG, "EAP: Could not find selected method: "
"vendor %d method %d",
@@ -268,6 +291,8 @@ SM_STATE(EAP, GET_METHOD)
goto nak;
}
+ sm->ClientTimeout = EAP_CLIENT_TIMEOUT_DEFAULT;
+
wpa_printf(MSG_DEBUG, "EAP: Initialize selected EAP method: "
"vendor %u method %u (%s)",
sm->reqVendor, method, sm->m->name);
@@ -323,6 +348,7 @@ SM_STATE(EAP, METHOD)
{
struct wpabuf *eapReqData;
struct eap_method_ret ret;
+ int min_len = 1;
SM_ENTRY(EAP, METHOD);
if (sm->m == NULL) {
@@ -331,6 +357,10 @@ SM_STATE(EAP, METHOD)
}
eapReqData = eapol_get_eapReqData(sm);
+ if (sm->m->vendor == EAP_VENDOR_IETF && sm->m->method == EAP_TYPE_LEAP)
+ min_len = 0; /* LEAP uses EAP-Success without payload */
+ if (!eap_hdr_len_valid(eapReqData, min_len))
+ return;
/*
* Get ignore, methodState, decision, allowNotifications, and
@@ -419,6 +449,8 @@ SM_STATE(EAP, IDENTITY)
SM_ENTRY(EAP, IDENTITY);
eapReqData = eapol_get_eapReqData(sm);
+ if (!eap_hdr_len_valid(eapReqData, 1))
+ return;
eap_sm_processIdentity(sm, eapReqData);
wpabuf_free(sm->eapRespData);
sm->eapRespData = NULL;
@@ -435,6 +467,8 @@ SM_STATE(EAP, NOTIFICATION)
SM_ENTRY(EAP, NOTIFICATION);
eapReqData = eapol_get_eapReqData(sm);
+ if (!eap_hdr_len_valid(eapReqData, 1))
+ return;
eap_sm_processNotify(sm, eapReqData);
wpabuf_free(sm->eapRespData);
sm->eapRespData = NULL;
@@ -852,13 +886,17 @@ static struct wpabuf * eap_sm_buildNak(struct eap_sm *sm, int id)
static void eap_sm_processIdentity(struct eap_sm *sm, const struct wpabuf *req)
{
- const struct eap_hdr *hdr = wpabuf_head(req);
- const u8 *pos = (const u8 *) (hdr + 1);
- pos++;
+ const u8 *pos;
+ size_t msg_len;
wpa_msg(sm->msg_ctx, MSG_INFO, WPA_EVENT_EAP_STARTED
"EAP authentication started");
+ pos = eap_hdr_validate(EAP_VENDOR_IETF, EAP_TYPE_IDENTITY, req,
+ &msg_len);
+ if (pos == NULL)
+ return;
+
/*
* RFC 3748 - 5.1: Identity
* Data field may contain a displayable message in UTF-8. If this
@@ -869,15 +907,78 @@ static void eap_sm_processIdentity(struct eap_sm *sm, const struct wpabuf *req)
/* TODO: could save displayable message so that it can be shown to the
* user in case of interaction is required */
wpa_hexdump_ascii(MSG_DEBUG, "EAP: EAP-Request Identity data",
- pos, be_to_host16(hdr->length) - 5);
+ pos, msg_len);
}
#ifdef PCSC_FUNCS
+
+/*
+ * Rules for figuring out MNC length based on IMSI for SIM cards that do not
+ * include MNC length field.
+ */
+static int mnc_len_from_imsi(const char *imsi)
+{
+ char mcc_str[4];
+ unsigned int mcc;
+
+ os_memcpy(mcc_str, imsi, 3);
+ mcc_str[3] = '\0';
+ mcc = atoi(mcc_str);
+
+ if (mcc == 244)
+ return 2; /* Networks in Finland use 2-digit MNC */
+
+ return -1;
+}
+
+
+static int eap_sm_append_3gpp_realm(struct eap_sm *sm, char *imsi,
+ size_t max_len, size_t *imsi_len)
+{
+ int mnc_len;
+ char *pos, mnc[4];
+
+ if (*imsi_len + 36 > max_len) {
+ wpa_printf(MSG_WARNING, "No room for realm in IMSI buffer");
+ return -1;
+ }
+
+ /* MNC (2 or 3 digits) */
+ mnc_len = scard_get_mnc_len(sm->scard_ctx);
+ if (mnc_len < 0)
+ mnc_len = mnc_len_from_imsi(imsi);
+ if (mnc_len < 0) {
+ wpa_printf(MSG_INFO, "Failed to get MNC length from (U)SIM "
+ "assuming 3");
+ mnc_len = 3;
+ }
+
+ if (mnc_len == 2) {
+ mnc[0] = '0';
+ mnc[1] = imsi[3];
+ mnc[2] = imsi[4];
+ } else if (mnc_len == 3) {
+ mnc[0] = imsi[3];
+ mnc[1] = imsi[4];
+ mnc[2] = imsi[5];
+ }
+ mnc[3] = '\0';
+
+ pos = imsi + *imsi_len;
+ pos += os_snprintf(pos, imsi + max_len - pos,
+ "@wlan.mnc%s.mcc%c%c%c.3gppnetwork.org",
+ mnc, imsi[0], imsi[1], imsi[2]);
+ *imsi_len = pos - imsi;
+
+ return 0;
+}
+
+
static int eap_sm_imsi_identity(struct eap_sm *sm,
struct eap_peer_config *conf)
{
- int aka = 0;
+ enum { EAP_SM_SIM, EAP_SM_AKA, EAP_SM_AKA_PRIME } method = EAP_SM_SIM;
char imsi[100];
size_t imsi_len;
struct eap_method_type *m = conf->eap_methods;
@@ -891,11 +992,28 @@ static int eap_sm_imsi_identity(struct eap_sm *sm,
wpa_hexdump_ascii(MSG_DEBUG, "IMSI", (u8 *) imsi, imsi_len);
+ if (imsi_len < 7) {
+ wpa_printf(MSG_WARNING, "Too short IMSI for SIM identity");
+ return -1;
+ }
+
+ if (eap_sm_append_3gpp_realm(sm, imsi, sizeof(imsi), &imsi_len) < 0) {
+ wpa_printf(MSG_WARNING, "Could not add realm to SIM identity");
+ return -1;
+ }
+ wpa_hexdump_ascii(MSG_DEBUG, "IMSI + realm", (u8 *) imsi, imsi_len);
+
for (i = 0; m && (m[i].vendor != EAP_VENDOR_IETF ||
m[i].method != EAP_TYPE_NONE); i++) {
if (m[i].vendor == EAP_VENDOR_IETF &&
+ m[i].method == EAP_TYPE_AKA_PRIME) {
+ method = EAP_SM_AKA_PRIME;
+ break;
+ }
+
+ if (m[i].vendor == EAP_VENDOR_IETF &&
m[i].method == EAP_TYPE_AKA) {
- aka = 1;
+ method = EAP_SM_AKA;
break;
}
}
@@ -908,12 +1026,23 @@ static int eap_sm_imsi_identity(struct eap_sm *sm,
return -1;
}
- conf->identity[0] = aka ? '0' : '1';
+ switch (method) {
+ case EAP_SM_SIM:
+ conf->identity[0] = '1';
+ break;
+ case EAP_SM_AKA:
+ conf->identity[0] = '0';
+ break;
+ case EAP_SM_AKA_PRIME:
+ conf->identity[0] = '6';
+ break;
+ }
os_memcpy(conf->identity + 1, imsi, imsi_len);
conf->identity_len = 1 + imsi_len;
return 0;
}
+
#endif /* PCSC_FUNCS */
@@ -1146,10 +1275,12 @@ static void eap_sm_parseEapReq(struct eap_sm *sm, const struct wpabuf *req)
break;
case EAP_CODE_SUCCESS:
wpa_printf(MSG_DEBUG, "EAP: Received EAP-Success");
+ eap_notify_status(sm, "completion", "success");
sm->rxSuccess = TRUE;
break;
case EAP_CODE_FAILURE:
wpa_printf(MSG_DEBUG, "EAP: Received EAP-Failure");
+ eap_notify_status(sm, "completion", "failure");
sm->rxFailure = TRUE;
break;
default:
@@ -1165,9 +1296,12 @@ static void eap_peer_sm_tls_event(void *ctx, enum tls_event ev,
{
struct eap_sm *sm = ctx;
char *hash_hex = NULL;
- char *cert_hex = NULL;
switch (ev) {
+ case TLS_CERT_CHAIN_SUCCESS:
+ eap_notify_status(sm, "remote certificate verification",
+ "success");
+ break;
case TLS_CERT_CHAIN_FAILURE:
wpa_msg(sm->msg_ctx, MSG_INFO, WPA_EVENT_EAP_TLS_CERT_ERROR
"reason=%d depth=%d subject='%s' err='%s'",
@@ -1175,8 +1309,13 @@ static void eap_peer_sm_tls_event(void *ctx, enum tls_event ev,
data->cert_fail.depth,
data->cert_fail.subject,
data->cert_fail.reason_txt);
+ eap_notify_status(sm, "remote certificate verification",
+ data->cert_fail.reason_txt);
break;
case TLS_PEER_CERTIFICATE:
+ if (!sm->eapol_cb->notify_cert)
+ break;
+
if (data->peer_cert.hash) {
size_t len = data->peer_cert.hash_len * 2 + 1;
hash_hex = os_malloc(len);
@@ -1186,31 +1325,23 @@ static void eap_peer_sm_tls_event(void *ctx, enum tls_event ev,
data->peer_cert.hash_len);
}
}
- wpa_msg(sm->msg_ctx, MSG_INFO, WPA_EVENT_EAP_PEER_CERT
- "depth=%d subject='%s'%s%s",
- data->peer_cert.depth, data->peer_cert.subject,
- hash_hex ? " hash=" : "", hash_hex ? hash_hex : "");
-
- if (data->peer_cert.cert) {
- size_t len = wpabuf_len(data->peer_cert.cert) * 2 + 1;
- cert_hex = os_malloc(len);
- if (cert_hex == NULL)
- break;
- wpa_snprintf_hex(cert_hex, len,
- wpabuf_head(data->peer_cert.cert),
- wpabuf_len(data->peer_cert.cert));
- wpa_msg_ctrl(sm->msg_ctx, MSG_INFO,
- WPA_EVENT_EAP_PEER_CERT
- "depth=%d subject='%s' cert=%s",
- data->peer_cert.depth,
- data->peer_cert.subject,
- cert_hex);
- }
+
+ sm->eapol_cb->notify_cert(sm->eapol_ctx,
+ data->peer_cert.depth,
+ data->peer_cert.subject,
+ hash_hex, data->peer_cert.cert);
+ break;
+ case TLS_ALERT:
+ if (data->alert.is_local)
+ eap_notify_status(sm, "local TLS alert",
+ data->alert.description);
+ else
+ eap_notify_status(sm, "remote TLS alert",
+ data->alert.description);
break;
}
os_free(hash_hex);
- os_free(cert_hex);
}
@@ -1241,7 +1372,7 @@ struct eap_sm * eap_peer_sm_init(void *eapol_ctx,
sm->eapol_ctx = eapol_ctx;
sm->eapol_cb = eapol_cb;
sm->msg_ctx = msg_ctx;
- sm->ClientTimeout = 60;
+ sm->ClientTimeout = EAP_CLIENT_TIMEOUT_DEFAULT;
sm->wps = conf->wps;
os_memset(&tlsconf, 0, sizeof(tlsconf));
@@ -1253,6 +1384,7 @@ struct eap_sm * eap_peer_sm_init(void *eapol_ctx,
#endif /* CONFIG_FIPS */
tlsconf.event_cb = eap_peer_sm_tls_event;
tlsconf.cb_ctx = sm;
+ tlsconf.cert_in_cb = conf->cert_in_cb;
sm->ssl_ctx = tls_init(&tlsconf);
if (sm->ssl_ctx == NULL) {
wpa_printf(MSG_WARNING, "SSL: Failed to initialize TLS "
@@ -1261,6 +1393,13 @@ struct eap_sm * eap_peer_sm_init(void *eapol_ctx,
return NULL;
}
+ sm->ssl_ctx2 = tls_init(&tlsconf);
+ if (sm->ssl_ctx2 == NULL) {
+ wpa_printf(MSG_INFO, "SSL: Failed to initialize TLS "
+ "context (2).");
+ /* Run without separate TLS context within TLS tunnel */
+ }
+
return sm;
}
@@ -1278,6 +1417,8 @@ void eap_peer_sm_deinit(struct eap_sm *sm)
return;
eap_deinit_prev_method(sm, "EAP deinit");
eap_sm_abort(sm);
+ if (sm->ssl_ctx2)
+ tls_deinit(sm->ssl_ctx2);
tls_deinit(sm->ssl_ctx);
os_free(sm);
}
@@ -1477,16 +1618,11 @@ int eap_sm_get_status(struct eap_sm *sm, char *buf, size_t buflen, int verbose)
#if defined(CONFIG_CTRL_IFACE) || !defined(CONFIG_NO_STDOUT_DEBUG)
-typedef enum {
- TYPE_IDENTITY, TYPE_PASSWORD, TYPE_OTP, TYPE_PIN, TYPE_NEW_PASSWORD,
- TYPE_PASSPHRASE
-} eap_ctrl_req_type;
-
-static void eap_sm_request(struct eap_sm *sm, eap_ctrl_req_type type,
+static void eap_sm_request(struct eap_sm *sm, enum wpa_ctrl_req_type field,
const char *msg, size_t msglen)
{
struct eap_peer_config *config;
- char *field, *txt, *tmp;
+ char *txt = NULL, *tmp;
if (sm == NULL)
return;
@@ -1494,29 +1630,20 @@ static void eap_sm_request(struct eap_sm *sm, eap_ctrl_req_type type,
if (config == NULL)
return;
- switch (type) {
- case TYPE_IDENTITY:
- field = "IDENTITY";
- txt = "Identity";
+ switch (field) {
+ case WPA_CTRL_REQ_EAP_IDENTITY:
config->pending_req_identity++;
break;
- case TYPE_PASSWORD:
- field = "PASSWORD";
- txt = "Password";
+ case WPA_CTRL_REQ_EAP_PASSWORD:
config->pending_req_password++;
break;
- case TYPE_NEW_PASSWORD:
- field = "NEW_PASSWORD";
- txt = "New Password";
+ case WPA_CTRL_REQ_EAP_NEW_PASSWORD:
config->pending_req_new_password++;
break;
- case TYPE_PIN:
- field = "PIN";
- txt = "PIN";
+ case WPA_CTRL_REQ_EAP_PIN:
config->pending_req_pin++;
break;
- case TYPE_OTP:
- field = "OTP";
+ case WPA_CTRL_REQ_EAP_OTP:
if (msg) {
tmp = os_malloc(msglen + 3);
if (tmp == NULL)
@@ -1535,9 +1662,7 @@ static void eap_sm_request(struct eap_sm *sm, eap_ctrl_req_type type,
txt = config->pending_req_otp;
}
break;
- case TYPE_PASSPHRASE:
- field = "PASSPHRASE";
- txt = "Private key passphrase";
+ case WPA_CTRL_REQ_EAP_PASSPHRASE:
config->pending_req_passphrase++;
break;
default:
@@ -1551,6 +1676,13 @@ static void eap_sm_request(struct eap_sm *sm, eap_ctrl_req_type type,
#define eap_sm_request(sm, type, msg, msglen) do { } while (0)
#endif /* CONFIG_CTRL_IFACE || !CONFIG_NO_STDOUT_DEBUG */
+const char * eap_sm_get_method_name(struct eap_sm *sm)
+{
+ if (sm->m == NULL)
+ return "UNKNOWN";
+ return sm->m->name;
+}
+
/**
* eap_sm_request_identity - Request identity from user (ctrl_iface)
@@ -1563,7 +1695,7 @@ static void eap_sm_request(struct eap_sm *sm, eap_ctrl_req_type type,
*/
void eap_sm_request_identity(struct eap_sm *sm)
{
- eap_sm_request(sm, TYPE_IDENTITY, NULL, 0);
+ eap_sm_request(sm, WPA_CTRL_REQ_EAP_IDENTITY, NULL, 0);
}
@@ -1578,7 +1710,7 @@ void eap_sm_request_identity(struct eap_sm *sm)
*/
void eap_sm_request_password(struct eap_sm *sm)
{
- eap_sm_request(sm, TYPE_PASSWORD, NULL, 0);
+ eap_sm_request(sm, WPA_CTRL_REQ_EAP_PASSWORD, NULL, 0);
}
@@ -1593,7 +1725,7 @@ void eap_sm_request_password(struct eap_sm *sm)
*/
void eap_sm_request_new_password(struct eap_sm *sm)
{
- eap_sm_request(sm, TYPE_NEW_PASSWORD, NULL, 0);
+ eap_sm_request(sm, WPA_CTRL_REQ_EAP_NEW_PASSWORD, NULL, 0);
}
@@ -1608,7 +1740,7 @@ void eap_sm_request_new_password(struct eap_sm *sm)
*/
void eap_sm_request_pin(struct eap_sm *sm)
{
- eap_sm_request(sm, TYPE_PIN, NULL, 0);
+ eap_sm_request(sm, WPA_CTRL_REQ_EAP_PIN, NULL, 0);
}
@@ -1624,7 +1756,7 @@ void eap_sm_request_pin(struct eap_sm *sm)
*/
void eap_sm_request_otp(struct eap_sm *sm, const char *msg, size_t msg_len)
{
- eap_sm_request(sm, TYPE_OTP, msg, msg_len);
+ eap_sm_request(sm, WPA_CTRL_REQ_EAP_OTP, msg, msg_len);
}
@@ -1639,7 +1771,7 @@ void eap_sm_request_otp(struct eap_sm *sm, const char *msg, size_t msg_len)
*/
void eap_sm_request_passphrase(struct eap_sm *sm)
{
- eap_sm_request(sm, TYPE_PASSPHRASE, NULL, 0);
+ eap_sm_request(sm, WPA_CTRL_REQ_EAP_PASSPHRASE, NULL, 0);
}
@@ -1806,6 +1938,27 @@ const u8 * eap_get_config_identity(struct eap_sm *sm, size_t *len)
}
+static int eap_get_ext_password(struct eap_sm *sm,
+ struct eap_peer_config *config)
+{
+ char *name;
+
+ if (config->password == NULL)
+ return -1;
+
+ name = os_zalloc(config->password_len + 1);
+ if (name == NULL)
+ return -1;
+ os_memcpy(name, config->password, config->password_len);
+
+ ext_password_free(sm->ext_pw_buf);
+ sm->ext_pw_buf = ext_password_get(sm->ext_pw, name);
+ os_free(name);
+
+ return sm->ext_pw_buf == NULL ? -1 : 0;
+}
+
+
/**
* eap_get_config_password - Get password from the network configuration
* @sm: Pointer to EAP state machine allocated with eap_peer_sm_init()
@@ -1817,6 +1970,14 @@ const u8 * eap_get_config_password(struct eap_sm *sm, size_t *len)
struct eap_peer_config *config = eap_get_config(sm);
if (config == NULL)
return NULL;
+
+ if (config->flags & EAP_CONFIG_FLAGS_EXT_PASSWORD) {
+ if (eap_get_ext_password(sm, config) < 0)
+ return NULL;
+ *len = wpabuf_len(sm->ext_pw_buf);
+ return wpabuf_head(sm->ext_pw_buf);
+ }
+
*len = config->password_len;
return config->password;
}
@@ -1836,6 +1997,14 @@ const u8 * eap_get_config_password2(struct eap_sm *sm, size_t *len, int *hash)
struct eap_peer_config *config = eap_get_config(sm);
if (config == NULL)
return NULL;
+
+ if (config->flags & EAP_CONFIG_FLAGS_EXT_PASSWORD) {
+ if (eap_get_ext_password(sm, config) < 0)
+ return NULL;
+ *len = wpabuf_len(sm->ext_pw_buf);
+ return wpabuf_head(sm->ext_pw_buf);
+ }
+
*len = config->password_len;
if (hash)
*hash = !!(config->flags & EAP_CONFIG_FLAGS_PASSWORD_NTHASH);
@@ -1923,6 +2092,15 @@ const char * eap_get_config_phase2(struct eap_sm *sm)
}
+int eap_get_config_fragment_size(struct eap_sm *sm)
+{
+ struct eap_peer_config *config = eap_get_config(sm);
+ if (config == NULL)
+ return -1;
+ return config->fragment_size;
+}
+
+
/**
* eap_key_available - Get key availability (eapKeyAvailable variable)
* @sm: Pointer to EAP state machine allocated with eap_peer_sm_init()
@@ -2138,3 +2316,24 @@ int eap_is_wps_pin_enrollee(struct eap_peer_config *conf)
return 1;
}
+
+
+void eap_sm_set_ext_pw_ctx(struct eap_sm *sm, struct ext_password_data *ext)
+{
+ ext_password_free(sm->ext_pw_buf);
+ sm->ext_pw_buf = NULL;
+ sm->ext_pw = ext;
+}
+
+
+/**
+ * eap_set_anon_id - Set or add anonymous identity
+ * @sm: Pointer to EAP state machine allocated with eap_peer_sm_init()
+ * @id: Anonymous identity (e.g., EAP-SIM pseudonym) or %NULL to clear
+ * @len: Length of anonymous identity in octets
+ */
+void eap_set_anon_id(struct eap_sm *sm, const u8 *id, size_t len)
+{
+ if (sm->eapol_cb->set_anon_id)
+ sm->eapol_cb->set_anon_id(sm->eapol_ctx, id, len);
+}
diff --git a/contrib/wpa/src/eap_peer/eap.h b/contrib/wpa/src/eap_peer/eap.h
index 40d0b69..8bccef1 100644
--- a/contrib/wpa/src/eap_peer/eap.h
+++ b/contrib/wpa/src/eap_peer/eap.h
@@ -1,15 +1,9 @@
/*
* EAP peer state machine functions (RFC 4137)
- * Copyright (c) 2004-2007, Jouni Malinen <j@w1.fi>
+ * Copyright (c) 2004-2012, Jouni Malinen <j@w1.fi>
*
- * This program is free software; you can redistribute it and/or modify
- * it under the terms of the GNU General Public License version 2 as
- * published by the Free Software Foundation.
- *
- * Alternatively, this software may be distributed under the terms of BSD
- * license.
- *
- * See README and COPYING for more details.
+ * This software may be distributed under the terms of the BSD license.
+ * See README for more details.
*/
#ifndef EAP_H
@@ -216,11 +210,39 @@ struct eapol_callbacks {
/**
* eap_param_needed - Notify that EAP parameter is needed
* @ctx: eapol_ctx from eap_peer_sm_init() call
- * @field: Field name (e.g., "IDENTITY")
+ * @field: Field indicator (e.g., WPA_CTRL_REQ_EAP_IDENTITY)
* @txt: User readable text describing the required parameter
*/
- void (*eap_param_needed)(void *ctx, const char *field,
+ void (*eap_param_needed)(void *ctx, enum wpa_ctrl_req_type field,
const char *txt);
+
+ /**
+ * notify_cert - Notification of a peer certificate
+ * @ctx: eapol_ctx from eap_peer_sm_init() call
+ * @depth: Depth in certificate chain (0 = server)
+ * @subject: Subject of the peer certificate
+ * @cert_hash: SHA-256 hash of the certificate
+ * @cert: Peer certificate
+ */
+ void (*notify_cert)(void *ctx, int depth, const char *subject,
+ const char *cert_hash, const struct wpabuf *cert);
+
+ /**
+ * notify_status - Notification of the current EAP state
+ * @ctx: eapol_ctx from eap_peer_sm_init() call
+ * @status: Step in the process of EAP authentication
+ * @parameter: Step-specific parameter, e.g., EAP method name
+ */
+ void (*notify_status)(void *ctx, const char *status,
+ const char *parameter);
+
+ /**
+ * set_anon_id - Set or add anonymous identity
+ * @ctx: eapol_ctx from eap_peer_sm_init() call
+ * @id: Anonymous identity (e.g., EAP-SIM pseudonym) or %NULL to clear
+ * @len: Length of anonymous identity in octets
+ */
+ void (*set_anon_id)(void *ctx, const u8 *id, size_t len);
};
/**
@@ -251,6 +273,11 @@ struct eap_config {
* This is only used by EAP-WSC and can be left %NULL if not available.
*/
struct wps_context *wps;
+
+ /**
+ * cert_in_cb - Include server certificates in callback
+ */
+ int cert_in_cb;
};
struct eap_sm * eap_peer_sm_init(void *eapol_ctx,
@@ -261,6 +288,7 @@ int eap_peer_sm_step(struct eap_sm *sm);
void eap_sm_abort(struct eap_sm *sm);
int eap_sm_get_status(struct eap_sm *sm, char *buf, size_t buflen,
int verbose);
+const char * eap_sm_get_method_name(struct eap_sm *sm);
struct wpabuf * eap_sm_buildIdentity(struct eap_sm *sm, int id, int encrypted);
void eap_sm_request_identity(struct eap_sm *sm);
void eap_sm_request_password(struct eap_sm *sm);
@@ -286,6 +314,10 @@ void eap_invalidate_cached_session(struct eap_sm *sm);
int eap_is_wps_pbc_enrollee(struct eap_peer_config *conf);
int eap_is_wps_pin_enrollee(struct eap_peer_config *conf);
+struct ext_password_data;
+void eap_sm_set_ext_pw_ctx(struct eap_sm *sm, struct ext_password_data *ext);
+void eap_set_anon_id(struct eap_sm *sm, const u8 *id, size_t len);
+
#endif /* IEEE8021X_EAPOL */
#endif /* EAP_H */
diff --git a/contrib/wpa/src/eap_peer/eap_aka.c b/contrib/wpa/src/eap_peer/eap_aka.c
index 182f01a..59861cb 100644
--- a/contrib/wpa/src/eap_peer/eap_aka.c
+++ b/contrib/wpa/src/eap_peer/eap_aka.c
@@ -1,15 +1,9 @@
/*
- * EAP peer method: EAP-AKA (RFC 4187) and EAP-AKA' (draft-arkko-eap-aka-kdf)
- * Copyright (c) 2004-2008, Jouni Malinen <j@w1.fi>
+ * EAP peer method: EAP-AKA (RFC 4187) and EAP-AKA' (RFC 5448)
+ * Copyright (c) 2004-2012, Jouni Malinen <j@w1.fi>
*
- * This program is free software; you can redistribute it and/or modify
- * it under the terms of the GNU General Public License version 2 as
- * published by the Free Software Foundation.
- *
- * Alternatively, this software may be distributed under the terms of BSD
- * license.
- *
- * See README and COPYING for more details.
+ * This software may be distributed under the terms of the BSD license.
+ * See README for more details.
*/
#include "includes.h"
@@ -96,6 +90,7 @@ static void * eap_aka_init(struct eap_sm *sm)
{
struct eap_aka_data *data;
const char *phase1 = eap_get_config_phase1(sm);
+ struct eap_peer_config *config = eap_get_config(sm);
data = os_zalloc(sizeof(*data));
if (data == NULL)
@@ -108,6 +103,15 @@ static void * eap_aka_init(struct eap_sm *sm)
data->result_ind = phase1 && os_strstr(phase1, "result_ind=1") != NULL;
+ if (config && config->anonymous_identity) {
+ data->pseudonym = os_malloc(config->anonymous_identity_len);
+ if (data->pseudonym) {
+ os_memcpy(data->pseudonym, config->anonymous_identity,
+ config->anonymous_identity_len);
+ data->pseudonym_len = config->anonymous_identity_len;
+ }
+ }
+
return data;
}
@@ -233,23 +237,24 @@ static int eap_aka_umts_auth(struct eap_sm *sm, struct eap_aka_data *data)
#define CLEAR_REAUTH_ID 0x02
#define CLEAR_EAP_ID 0x04
-static void eap_aka_clear_identities(struct eap_aka_data *data, int id)
+static void eap_aka_clear_identities(struct eap_sm *sm,
+ struct eap_aka_data *data, int id)
{
- wpa_printf(MSG_DEBUG, "EAP-AKA: forgetting old%s%s%s",
- id & CLEAR_PSEUDONYM ? " pseudonym" : "",
- id & CLEAR_REAUTH_ID ? " reauth_id" : "",
- id & CLEAR_EAP_ID ? " eap_id" : "");
- if (id & CLEAR_PSEUDONYM) {
+ if ((id & CLEAR_PSEUDONYM) && data->pseudonym) {
+ wpa_printf(MSG_DEBUG, "EAP-AKA: forgetting old pseudonym");
os_free(data->pseudonym);
data->pseudonym = NULL;
data->pseudonym_len = 0;
+ eap_set_anon_id(sm, NULL, 0);
}
- if (id & CLEAR_REAUTH_ID) {
+ if ((id & CLEAR_REAUTH_ID) && data->reauth_id) {
+ wpa_printf(MSG_DEBUG, "EAP-AKA: forgetting old reauth_id");
os_free(data->reauth_id);
data->reauth_id = NULL;
data->reauth_id_len = 0;
}
- if (id & CLEAR_EAP_ID) {
+ if ((id & CLEAR_EAP_ID) && data->last_eap_identity) {
+ wpa_printf(MSG_DEBUG, "EAP-AKA: forgetting old eap_id");
os_free(data->last_eap_identity);
data->last_eap_identity = NULL;
data->last_eap_identity_len = 0;
@@ -257,24 +262,45 @@ static void eap_aka_clear_identities(struct eap_aka_data *data, int id)
}
-static int eap_aka_learn_ids(struct eap_aka_data *data,
+static int eap_aka_learn_ids(struct eap_sm *sm, struct eap_aka_data *data,
struct eap_sim_attrs *attr)
{
if (attr->next_pseudonym) {
+ const u8 *identity = NULL;
+ size_t identity_len = 0;
+ const u8 *realm = NULL;
+ size_t realm_len = 0;
+
+ wpa_hexdump_ascii(MSG_DEBUG,
+ "EAP-AKA: (encr) AT_NEXT_PSEUDONYM",
+ attr->next_pseudonym,
+ attr->next_pseudonym_len);
os_free(data->pseudonym);
- data->pseudonym = os_malloc(attr->next_pseudonym_len);
+ /* Look for the realm of the permanent identity */
+ identity = eap_get_config_identity(sm, &identity_len);
+ if (identity) {
+ for (realm = identity, realm_len = identity_len;
+ realm_len > 0; realm_len--, realm++) {
+ if (*realm == '@')
+ break;
+ }
+ }
+ data->pseudonym = os_malloc(attr->next_pseudonym_len +
+ realm_len);
if (data->pseudonym == NULL) {
wpa_printf(MSG_INFO, "EAP-AKA: (encr) No memory for "
"next pseudonym");
+ data->pseudonym_len = 0;
return -1;
}
os_memcpy(data->pseudonym, attr->next_pseudonym,
attr->next_pseudonym_len);
- data->pseudonym_len = attr->next_pseudonym_len;
- wpa_hexdump_ascii(MSG_DEBUG,
- "EAP-AKA: (encr) AT_NEXT_PSEUDONYM",
- data->pseudonym,
- data->pseudonym_len);
+ if (realm_len) {
+ os_memcpy(data->pseudonym + attr->next_pseudonym_len,
+ realm, realm_len);
+ }
+ data->pseudonym_len = attr->next_pseudonym_len + realm_len;
+ eap_set_anon_id(sm, data->pseudonym, data->pseudonym_len);
}
if (attr->next_reauth_id) {
@@ -283,6 +309,7 @@ static int eap_aka_learn_ids(struct eap_aka_data *data,
if (data->reauth_id == NULL) {
wpa_printf(MSG_INFO, "EAP-AKA: (encr) No memory for "
"next reauth_id");
+ data->reauth_id_len = 0;
return -1;
}
os_memcpy(data->reauth_id, attr->next_reauth_id,
@@ -411,6 +438,8 @@ static struct wpabuf * eap_aka_client_error(struct eap_aka_data *data, u8 id,
data->num_id_req = 0;
data->num_notification = 0;
+ wpa_printf(MSG_DEBUG, "EAP-AKA: Send Client-Error (error code %d)",
+ err);
msg = eap_sim_msg_init(EAP_CODE_RESPONSE, id, data->eap_method,
EAP_AKA_SUBTYPE_CLIENT_ERROR);
eap_sim_msg_add(msg, EAP_SIM_AT_CLIENT_ERROR_CODE, err, NULL, 0);
@@ -472,16 +501,16 @@ static struct wpabuf * eap_aka_response_identity(struct eap_sm *sm,
data->pseudonym) {
identity = data->pseudonym;
identity_len = data->pseudonym_len;
- eap_aka_clear_identities(data, CLEAR_REAUTH_ID);
+ eap_aka_clear_identities(sm, data, CLEAR_REAUTH_ID);
} else if (id_req != NO_ID_REQ) {
identity = eap_get_config_identity(sm, &identity_len);
if (identity) {
- eap_aka_clear_identities(data, CLEAR_PSEUDONYM |
+ eap_aka_clear_identities(sm, data, CLEAR_PSEUDONYM |
CLEAR_REAUTH_ID);
}
}
if (id_req != NO_ID_REQ)
- eap_aka_clear_identities(data, CLEAR_EAP_ID);
+ eap_aka_clear_identities(sm, data, CLEAR_EAP_ID);
wpa_printf(MSG_DEBUG, "Generating EAP-AKA Identity (id=%d)", id);
msg = eap_sim_msg_init(EAP_CODE_RESPONSE, id, data->eap_method,
@@ -880,11 +909,11 @@ static struct wpabuf * eap_aka_process_challenge(struct eap_sm *sm,
EAP_AKA_UNABLE_TO_PROCESS_PACKET);
}
- /* Old reauthentication and pseudonym identities must not be used
- * anymore. In other words, if no new identities are received, full
- * authentication will be used on next reauthentication. */
- eap_aka_clear_identities(data, CLEAR_PSEUDONYM | CLEAR_REAUTH_ID |
- CLEAR_EAP_ID);
+ /* Old reauthentication identity must not be used anymore. In
+ * other words, if no new identities are received, full
+ * authentication will be used on next reauthentication (using
+ * pseudonym identity or permanent identity). */
+ eap_aka_clear_identities(sm, data, CLEAR_REAUTH_ID | CLEAR_EAP_ID);
if (attr->encr_data) {
u8 *decrypted;
@@ -895,7 +924,7 @@ static struct wpabuf * eap_aka_process_challenge(struct eap_sm *sm,
return eap_aka_client_error(
data, id, EAP_AKA_UNABLE_TO_PROCESS_PACKET);
}
- eap_aka_learn_ids(data, &eattr);
+ eap_aka_learn_ids(sm, data, &eattr);
os_free(decrypted);
}
@@ -1112,8 +1141,8 @@ static struct wpabuf * eap_aka_process_reauthentication(
data->nonce_s, data->mk,
data->msk, data->emsk);
}
- eap_aka_clear_identities(data, CLEAR_REAUTH_ID | CLEAR_EAP_ID);
- eap_aka_learn_ids(data, &eattr);
+ eap_aka_clear_identities(sm, data, CLEAR_REAUTH_ID | CLEAR_EAP_ID);
+ eap_aka_learn_ids(sm, data, &eattr);
if (data->result_ind && attr->result_ind)
data->use_result_ind = 1;
@@ -1128,7 +1157,8 @@ static struct wpabuf * eap_aka_process_reauthentication(
if (data->counter > EAP_AKA_MAX_FAST_REAUTHS) {
wpa_printf(MSG_DEBUG, "EAP-AKA: Maximum number of "
"fast reauths performed - force fullauth");
- eap_aka_clear_identities(data, CLEAR_REAUTH_ID | CLEAR_EAP_ID);
+ eap_aka_clear_identities(sm, data,
+ CLEAR_REAUTH_ID | CLEAR_EAP_ID);
}
os_free(decrypted);
return eap_aka_response_reauth(data, id, 0, data->nonce_s);
@@ -1246,7 +1276,7 @@ static Boolean eap_aka_has_reauth_data(struct eap_sm *sm, void *priv)
static void eap_aka_deinit_for_reauth(struct eap_sm *sm, void *priv)
{
struct eap_aka_data *data = priv;
- eap_aka_clear_identities(data, CLEAR_EAP_ID);
+ eap_aka_clear_identities(sm, data, CLEAR_EAP_ID);
data->prev_id = -1;
wpabuf_free(data->id_msgs);
data->id_msgs = NULL;
diff --git a/contrib/wpa/src/eap_peer/eap_config.h b/contrib/wpa/src/eap_peer/eap_config.h
index b64b68f..ed90919 100644
--- a/contrib/wpa/src/eap_peer/eap_config.h
+++ b/contrib/wpa/src/eap_peer/eap_config.h
@@ -2,14 +2,8 @@
* EAP peer configuration data
* Copyright (c) 2003-2008, Jouni Malinen <j@w1.fi>
*
- * This program is free software; you can redistribute it and/or modify
- * it under the terms of the GNU General Public License version 2 as
- * published by the Free Software Foundation.
- *
- * Alternatively, this software may be distributed under the terms of BSD
- * license.
- *
- * See README and COPYING for more details.
+ * This software may be distributed under the terms of the BSD license.
+ * See README for more details.
*/
#ifndef EAP_CONFIG_H
@@ -41,6 +35,9 @@ struct eap_peer_config {
*
* If not set, the identity field will be used for both unencrypted and
* protected fields.
+ *
+ * This field can also be used with EAP-SIM/AKA/AKA' to store the
+ * pseudonym identity.
*/
u8 *anonymous_identity;
@@ -625,6 +622,7 @@ struct eap_peer_config {
int fragment_size;
#define EAP_CONFIG_FLAGS_PASSWORD_NTHASH BIT(0)
+#define EAP_CONFIG_FLAGS_EXT_PASSWORD BIT(1)
/**
* flags - Network configuration flags (bitfield)
*
@@ -632,6 +630,8 @@ struct eap_peer_config {
* for the network parameters.
* bit 0 = password is represented as a 16-byte NtPasswordHash value
* instead of plaintext password
+ * bit 1 = password is stored in external storage; the value in the
+ * password field is the name of that external entry
*/
u32 flags;
};
diff --git a/contrib/wpa/src/eap_peer/eap_fast.c b/contrib/wpa/src/eap_peer/eap_fast.c
index 5d3e69d..7ca5288 100644
--- a/contrib/wpa/src/eap_peer/eap_fast.c
+++ b/contrib/wpa/src/eap_peer/eap_fast.c
@@ -2,14 +2,8 @@
* EAP peer method: EAP-FAST (RFC 4851)
* Copyright (c) 2004-2008, Jouni Malinen <j@w1.fi>
*
- * This program is free software; you can redistribute it and/or modify
- * it under the terms of the GNU General Public License version 2 as
- * published by the Free Software Foundation.
- *
- * Alternatively, this software may be distributed under the terms of BSD
- * license.
- *
- * See README and COPYING for more details.
+ * This software may be distributed under the terms of the BSD license.
+ * See README for more details.
*/
#include "includes.h"
@@ -175,7 +169,7 @@ static void * eap_fast_init(struct eap_sm *sm)
data->phase2_type.vendor = EAP_VENDOR_IETF;
data->phase2_type.method = EAP_TYPE_NONE;
- if (eap_peer_tls_ssl_init(sm, &data->ssl, config)) {
+ if (eap_peer_tls_ssl_init(sm, &data->ssl, config, EAP_TYPE_FAST)) {
wpa_printf(MSG_INFO, "EAP-FAST: Failed to initialize SSL.");
eap_fast_deinit(sm, data);
return NULL;
@@ -444,8 +438,9 @@ static int eap_fast_phase2_request(struct eap_sm *sm,
return 0;
}
- if (data->phase2_priv == NULL &&
- eap_fast_init_phase2_method(sm, data) < 0) {
+ if ((data->phase2_priv == NULL &&
+ eap_fast_init_phase2_method(sm, data) < 0) ||
+ data->phase2_method == NULL) {
wpa_printf(MSG_INFO, "EAP-FAST: Failed to initialize "
"Phase 2 EAP method %d", *pos);
ret->methodState = METHOD_DONE;
@@ -542,7 +537,7 @@ static struct wpabuf * eap_fast_tlv_pac_ack(void)
static struct wpabuf * eap_fast_process_eap_payload_tlv(
struct eap_sm *sm, struct eap_fast_data *data,
- struct eap_method_ret *ret, const struct eap_hdr *req,
+ struct eap_method_ret *ret,
u8 *eap_payload_tlv, size_t eap_payload_tlv_len)
{
struct eap_hdr *hdr;
@@ -1037,11 +1032,15 @@ static struct wpabuf * eap_fast_process_pac(struct eap_sm *sm,
} else {
/*
* This is PAC refreshing, i.e., normal authentication that is
- * expected to be completed with an EAP-Success.
+ * expected to be completed with an EAP-Success. However,
+ * RFC 5422, Section 3.5 allows EAP-Failure to be sent even
+ * after protected success exchange in case of EAP-Fast
+ * provisioning, so we better use DECISION_COND_SUCC here
+ * instead of DECISION_UNCOND_SUCC.
*/
wpa_printf(MSG_DEBUG, "EAP-FAST: Send PAC-Acknowledgement TLV "
"- PAC refreshing completed successfully");
- ret->decision = DECISION_UNCOND_SUCC;
+ ret->decision = DECISION_COND_SUCC;
}
ret->methodState = METHOD_DONE;
return eap_fast_tlv_pac_ack();
@@ -1184,7 +1183,7 @@ static int eap_fast_process_decrypted(struct eap_sm *sm,
if (tlv.eap_payload_tlv) {
tmp = eap_fast_process_eap_payload_tlv(
- sm, data, ret, req, tlv.eap_payload_tlv,
+ sm, data, ret, tlv.eap_payload_tlv,
tlv.eap_payload_tlv_len);
resp = wpabuf_concat(resp, tmp);
}
diff --git a/contrib/wpa/src/eap_peer/eap_fast_pac.c b/contrib/wpa/src/eap_peer/eap_fast_pac.c
index 541cce5..8c480b9 100644
--- a/contrib/wpa/src/eap_peer/eap_fast_pac.c
+++ b/contrib/wpa/src/eap_peer/eap_fast_pac.c
@@ -2,14 +2,8 @@
* EAP peer method: EAP-FAST PAC file processing
* Copyright (c) 2004-2006, Jouni Malinen <j@w1.fi>
*
- * This program is free software; you can redistribute it and/or modify
- * it under the terms of the GNU General Public License version 2 as
- * published by the Free Software Foundation.
- *
- * Alternatively, this software may be distributed under the terms of BSD
- * license.
- *
- * See README and COPYING for more details.
+ * This software may be distributed under the terms of the BSD license.
+ * See README for more details.
*/
#include "includes.h"
@@ -428,8 +422,12 @@ int eap_fast_load_pac(struct eap_sm *sm, struct eap_fast_pac **pac_root,
if (eap_fast_init_pac_data(sm, pac_file, &rc) < 0)
return 0;
- if (eap_fast_read_line(&rc, &pos) < 0 ||
- os_strcmp(pac_file_hdr, rc.buf) != 0)
+ if (eap_fast_read_line(&rc, &pos) < 0) {
+ /* empty file - assume it is fine to overwrite */
+ eap_fast_deinit_pac_data(&rc);
+ return 0;
+ }
+ if (os_strcmp(pac_file_hdr, rc.buf) != 0)
err = "Unrecognized header line";
while (!err && eap_fast_read_line(&rc, &pos) == 0) {
@@ -497,6 +495,7 @@ static void eap_fast_write(char **buf, char **pos, size_t *buf_len,
*buf = NULL;
return;
}
+ *pos = nbuf + (*pos - *buf);
*buf = nbuf;
*buf_len += need;
}
diff --git a/contrib/wpa/src/eap_peer/eap_fast_pac.h b/contrib/wpa/src/eap_peer/eap_fast_pac.h
index 9483f96..8815d91 100644
--- a/contrib/wpa/src/eap_peer/eap_fast_pac.h
+++ b/contrib/wpa/src/eap_peer/eap_fast_pac.h
@@ -2,14 +2,8 @@
* EAP peer method: EAP-FAST PAC file processing
* Copyright (c) 2004-2007, Jouni Malinen <j@w1.fi>
*
- * This program is free software; you can redistribute it and/or modify
- * it under the terms of the GNU General Public License version 2 as
- * published by the Free Software Foundation.
- *
- * Alternatively, this software may be distributed under the terms of BSD
- * license.
- *
- * See README and COPYING for more details.
+ * This software may be distributed under the terms of the BSD license.
+ * See README for more details.
*/
#ifndef EAP_FAST_PAC_H
diff --git a/contrib/wpa/src/eap_peer/eap_gpsk.c b/contrib/wpa/src/eap_peer/eap_gpsk.c
index f6a1955..2bd0d48 100644
--- a/contrib/wpa/src/eap_peer/eap_gpsk.c
+++ b/contrib/wpa/src/eap_peer/eap_gpsk.c
@@ -2,19 +2,14 @@
* EAP peer method: EAP-GPSK (RFC 5433)
* Copyright (c) 2006-2008, Jouni Malinen <j@w1.fi>
*
- * This program is free software; you can redistribute it and/or modify
- * it under the terms of the GNU General Public License version 2 as
- * published by the Free Software Foundation.
- *
- * Alternatively, this software may be distributed under the terms of BSD
- * license.
- *
- * See README and COPYING for more details.
+ * This software may be distributed under the terms of the BSD license.
+ * See README for more details.
*/
#include "includes.h"
#include "common.h"
+#include "crypto/random.h"
#include "eap_peer/eap_i.h"
#include "eap_common/eap_gpsk_common.h"
@@ -326,7 +321,7 @@ static struct wpabuf * eap_gpsk_send_gpsk_2(struct eap_gpsk_data *data,
wpabuf_put_be16(resp, data->id_server_len);
wpabuf_put_data(resp, data->id_server, data->id_server_len);
- if (os_get_random(data->rand_peer, EAP_GPSK_RAND_LEN)) {
+ if (random_get_bytes(data->rand_peer, EAP_GPSK_RAND_LEN)) {
wpa_printf(MSG_DEBUG, "EAP-GPSK: Failed to get random data "
"for RAND_Peer");
eap_gpsk_state(data, FAILURE);
diff --git a/contrib/wpa/src/eap_peer/eap_gtc.c b/contrib/wpa/src/eap_peer/eap_gtc.c
index b2b554b..9f3cfbd 100644
--- a/contrib/wpa/src/eap_peer/eap_gtc.c
+++ b/contrib/wpa/src/eap_peer/eap_gtc.c
@@ -2,14 +2,8 @@
* EAP peer method: EAP-GTC (RFC 3748)
* Copyright (c) 2004-2006, Jouni Malinen <j@w1.fi>
*
- * This program is free software; you can redistribute it and/or modify
- * it under the terms of the GNU General Public License version 2 as
- * published by the Free Software Foundation.
- *
- * Alternatively, this software may be distributed under the terms of BSD
- * license.
- *
- * See README and COPYING for more details.
+ * This software may be distributed under the terms of the BSD license.
+ * See README for more details.
*/
#include "includes.h"
diff --git a/contrib/wpa/src/eap_peer/eap_i.h b/contrib/wpa/src/eap_peer/eap_i.h
index e7c826e..dd94317 100644
--- a/contrib/wpa/src/eap_peer/eap_i.h
+++ b/contrib/wpa/src/eap_peer/eap_i.h
@@ -2,14 +2,8 @@
* EAP peer state machines internal structures (RFC 4137)
* Copyright (c) 2004-2007, Jouni Malinen <j@w1.fi>
*
- * This program is free software; you can redistribute it and/or modify
- * it under the terms of the GNU General Public License version 2 as
- * published by the Free Software Foundation.
- *
- * Alternatively, this software may be distributed under the terms of BSD
- * license.
- *
- * See README and COPYING for more details.
+ * This software may be distributed under the terms of the BSD license.
+ * See README for more details.
*/
#ifndef EAP_I_H
@@ -323,6 +317,7 @@ struct eap_sm {
void *msg_ctx;
void *scard_ctx;
void *ssl_ctx;
+ void *ssl_ctx2;
unsigned int workaround;
@@ -335,6 +330,9 @@ struct eap_sm {
struct wps_context *wps;
int prev_failure;
+
+ struct ext_password_data *ext_pw;
+ struct wpabuf *ext_pw_buf;
};
const u8 * eap_get_config_identity(struct eap_sm *sm, size_t *len);
@@ -345,6 +343,7 @@ const u8 * eap_get_config_otp(struct eap_sm *sm, size_t *len);
void eap_clear_config_otp(struct eap_sm *sm);
const char * eap_get_config_phase1(struct eap_sm *sm);
const char * eap_get_config_phase2(struct eap_sm *sm);
+int eap_get_config_fragment_size(struct eap_sm *sm);
struct eap_peer_config * eap_get_config(struct eap_sm *sm);
void eap_set_config_blob(struct eap_sm *sm, struct wpa_config_blob *blob);
const struct wpa_config_blob *
diff --git a/contrib/wpa/src/eap_peer/eap_ikev2.c b/contrib/wpa/src/eap_peer/eap_ikev2.c
index bb49a66..a227f8b 100644
--- a/contrib/wpa/src/eap_peer/eap_ikev2.c
+++ b/contrib/wpa/src/eap_peer/eap_ikev2.c
@@ -2,14 +2,8 @@
* EAP-IKEv2 peer (RFC 5106)
* Copyright (c) 2007, Jouni Malinen <j@w1.fi>
*
- * This program is free software; you can redistribute it and/or modify
- * it under the terms of the GNU General Public License version 2 as
- * published by the Free Software Foundation.
- *
- * Alternatively, this software may be distributed under the terms of BSD
- * license.
- *
- * See README and COPYING for more details.
+ * This software may be distributed under the terms of the BSD license.
+ * See README for more details.
*/
#include "includes.h"
diff --git a/contrib/wpa/src/eap_peer/eap_leap.c b/contrib/wpa/src/eap_peer/eap_leap.c
index a7c94a4..df34013 100644
--- a/contrib/wpa/src/eap_peer/eap_leap.c
+++ b/contrib/wpa/src/eap_peer/eap_leap.c
@@ -2,14 +2,8 @@
* EAP peer method: LEAP
* Copyright (c) 2004-2007, Jouni Malinen <j@w1.fi>
*
- * This program is free software; you can redistribute it and/or modify
- * it under the terms of the GNU General Public License version 2 as
- * published by the Free Software Foundation.
- *
- * Alternatively, this software may be distributed under the terms of BSD
- * license.
- *
- * See README and COPYING for more details.
+ * This software may be distributed under the terms of the BSD license.
+ * See README for more details.
*/
#include "includes.h"
@@ -17,6 +11,7 @@
#include "common.h"
#include "crypto/ms_funcs.h"
#include "crypto/crypto.h"
+#include "crypto/random.h"
#include "eap_i.h"
#define LEAP_VERSION 1
@@ -167,7 +162,7 @@ static struct wpabuf * eap_leap_process_success(struct eap_sm *sm, void *priv,
wpabuf_put_u8(resp, 0); /* unused */
wpabuf_put_u8(resp, LEAP_CHALLENGE_LEN);
pos = wpabuf_put(resp, LEAP_CHALLENGE_LEN);
- if (os_get_random(pos, LEAP_CHALLENGE_LEN)) {
+ if (random_get_bytes(pos, LEAP_CHALLENGE_LEN)) {
wpa_printf(MSG_WARNING, "EAP-LEAP: Failed to read random data "
"for challenge");
wpabuf_free(resp);
diff --git a/contrib/wpa/src/eap_peer/eap_md5.c b/contrib/wpa/src/eap_peer/eap_md5.c
index 0edbae8..d06befa 100644
--- a/contrib/wpa/src/eap_peer/eap_md5.c
+++ b/contrib/wpa/src/eap_peer/eap_md5.c
@@ -1,15 +1,9 @@
/*
* EAP peer method: EAP-MD5 (RFC 3748 and RFC 1994)
- * Copyright (c) 2004-2006, Jouni Malinen <j@w1.fi>
+ * Copyright (c) 2004-2012, Jouni Malinen <j@w1.fi>
*
- * This program is free software; you can redistribute it and/or modify
- * it under the terms of the GNU General Public License version 2 as
- * published by the Free Software Foundation.
- *
- * Alternatively, this software may be distributed under the terms of BSD
- * license.
- *
- * See README and COPYING for more details.
+ * This software may be distributed under the terms of the BSD license.
+ * See README for more details.
*/
#include "includes.h"
@@ -92,7 +86,13 @@ static struct wpabuf * eap_md5_process(struct eap_sm *sm, void *priv,
id = eap_get_id(resp);
rpos = wpabuf_put(resp, CHAP_MD5_LEN);
- chap_md5(id, password, password_len, challenge, challenge_len, rpos);
+ if (chap_md5(id, password, password_len, challenge, challenge_len,
+ rpos)) {
+ wpa_printf(MSG_INFO, "EAP-MD5: CHAP MD5 operation failed");
+ ret->ignore = TRUE;
+ wpabuf_free(resp);
+ return NULL;
+ }
wpa_hexdump(MSG_MSGDUMP, "EAP-MD5: Response", rpos, CHAP_MD5_LEN);
return resp;
diff --git a/contrib/wpa/src/eap_peer/eap_methods.c b/contrib/wpa/src/eap_peer/eap_methods.c
index 3b0af05..83a1457 100644
--- a/contrib/wpa/src/eap_peer/eap_methods.c
+++ b/contrib/wpa/src/eap_peer/eap_methods.c
@@ -2,14 +2,8 @@
* EAP peer: Method registration
* Copyright (c) 2004-2007, Jouni Malinen <j@w1.fi>
*
- * This program is free software; you can redistribute it and/or modify
- * it under the terms of the GNU General Public License version 2 as
- * published by the Free Software Foundation.
- *
- * Alternatively, this software may be distributed under the terms of BSD
- * license.
- *
- * See README and COPYING for more details.
+ * This software may be distributed under the terms of the BSD license.
+ * See README for more details.
*/
#include "includes.h"
@@ -77,6 +71,8 @@ EapType eap_peer_get_type(const char *name, int *vendor)
const char * eap_get_name(int vendor, EapType type)
{
struct eap_method *m;
+ if (vendor == EAP_VENDOR_IETF && type == EAP_TYPE_EXPANDED)
+ return "expanded";
for (m = eap_methods; m; m = m->next) {
if (m->vendor == vendor && m->method == type)
return m->name;
diff --git a/contrib/wpa/src/eap_peer/eap_methods.h b/contrib/wpa/src/eap_peer/eap_methods.h
index 384c61b..4994ff1 100644
--- a/contrib/wpa/src/eap_peer/eap_methods.h
+++ b/contrib/wpa/src/eap_peer/eap_methods.h
@@ -2,14 +2,8 @@
* EAP peer: Method registration
* Copyright (c) 2004-2007, Jouni Malinen <j@w1.fi>
*
- * This program is free software; you can redistribute it and/or modify
- * it under the terms of the GNU General Public License version 2 as
- * published by the Free Software Foundation.
- *
- * Alternatively, this software may be distributed under the terms of BSD
- * license.
- *
- * See README and COPYING for more details.
+ * This software may be distributed under the terms of the BSD license.
+ * See README for more details.
*/
#ifndef EAP_METHODS_H
@@ -91,6 +85,7 @@ static inline int eap_peer_method_unload(struct eap_method *method)
/* EAP peer method registration calls for statically linked in methods */
int eap_peer_md5_register(void);
int eap_peer_tls_register(void);
+int eap_peer_unauth_tls_register(void);
int eap_peer_mschapv2_register(void);
int eap_peer_peap_register(void);
int eap_peer_ttls_register(void);
@@ -109,5 +104,6 @@ int eap_peer_wsc_register(void);
int eap_peer_ikev2_register(void);
int eap_peer_vendor_test_register(void);
int eap_peer_tnc_register(void);
+int eap_peer_pwd_register(void);
#endif /* EAP_METHODS_H */
diff --git a/contrib/wpa/src/eap_peer/eap_mschapv2.c b/contrib/wpa/src/eap_peer/eap_mschapv2.c
index cd410d9..fb6c282 100644
--- a/contrib/wpa/src/eap_peer/eap_mschapv2.c
+++ b/contrib/wpa/src/eap_peer/eap_mschapv2.c
@@ -2,14 +2,8 @@
* EAP peer method: EAP-MSCHAPV2 (draft-kamath-pppext-eap-mschapv2-00.txt)
* Copyright (c) 2004-2008, Jouni Malinen <j@w1.fi>
*
- * This program is free software; you can redistribute it and/or modify
- * it under the terms of the GNU General Public License version 2 as
- * published by the Free Software Foundation.
- *
- * Alternatively, this software may be distributed under the terms of BSD
- * license.
- *
- * See README and COPYING for more details.
+ * This software may be distributed under the terms of the BSD license.
+ * See README for more details.
*
* This file implements EAP peer part of EAP-MSCHAPV2 method (EAP type 26).
* draft-kamath-pppext-eap-mschapv2-00.txt defines the Microsoft EAP CHAP
@@ -23,6 +17,7 @@
#include "common.h"
#include "crypto/ms_funcs.h"
+#include "crypto/random.h"
#include "common/wpa_ctrl.h"
#include "mschapv2.h"
#include "eap_i.h"
@@ -199,7 +194,7 @@ static struct wpabuf * eap_mschapv2_challenge_reply(
"in Phase 1");
peer_challenge = data->peer_challenge;
os_memset(r->peer_challenge, 0, MSCHAPV2_CHAL_LEN);
- } else if (os_get_random(peer_challenge, MSCHAPV2_CHAL_LEN)) {
+ } else if (random_get_bytes(peer_challenge, MSCHAPV2_CHAL_LEN)) {
wpabuf_free(resp);
return NULL;
}
@@ -309,7 +304,9 @@ static void eap_mschapv2_password_changed(struct eap_sm *sm,
"EAP-MSCHAPV2: Password changed successfully");
data->prev_error = 0;
os_free(config->password);
- if (config->flags & EAP_CONFIG_FLAGS_PASSWORD_NTHASH) {
+ if (config->flags & EAP_CONFIG_FLAGS_EXT_PASSWORD) {
+ /* TODO: update external storage */
+ } else if (config->flags & EAP_CONFIG_FLAGS_PASSWORD_NTHASH) {
config->password = os_malloc(16);
config->password_len = 16;
if (config->password) {
@@ -564,7 +561,7 @@ static struct wpabuf * eap_mschapv2_change_password(
}
/* Peer-Challenge */
- if (os_get_random(cp->peer_challenge, MSCHAPV2_CHAL_LEN))
+ if (random_get_bytes(cp->peer_challenge, MSCHAPV2_CHAL_LEN))
goto fail;
/* Reserved, must be zero */
diff --git a/contrib/wpa/src/eap_peer/eap_otp.c b/contrib/wpa/src/eap_peer/eap_otp.c
index 556c22f..9ac744a 100644
--- a/contrib/wpa/src/eap_peer/eap_otp.c
+++ b/contrib/wpa/src/eap_peer/eap_otp.c
@@ -2,14 +2,8 @@
* EAP peer method: EAP-OTP (RFC 3748)
* Copyright (c) 2004-2006, Jouni Malinen <j@w1.fi>
*
- * This program is free software; you can redistribute it and/or modify
- * it under the terms of the GNU General Public License version 2 as
- * published by the Free Software Foundation.
- *
- * Alternatively, this software may be distributed under the terms of BSD
- * license.
- *
- * See README and COPYING for more details.
+ * This software may be distributed under the terms of the BSD license.
+ * See README for more details.
*/
#include "includes.h"
diff --git a/contrib/wpa/src/eap_peer/eap_pax.c b/contrib/wpa/src/eap_peer/eap_pax.c
index 2e04831..7f87052 100644
--- a/contrib/wpa/src/eap_peer/eap_pax.c
+++ b/contrib/wpa/src/eap_peer/eap_pax.c
@@ -2,19 +2,14 @@
* EAP peer method: EAP-PAX (RFC 4746)
* Copyright (c) 2005-2008, Jouni Malinen <j@w1.fi>
*
- * This program is free software; you can redistribute it and/or modify
- * it under the terms of the GNU General Public License version 2 as
- * published by the Free Software Foundation.
- *
- * Alternatively, this software may be distributed under the terms of BSD
- * license.
- *
- * See README and COPYING for more details.
+ * This software may be distributed under the terms of the BSD license.
+ * See README for more details.
*/
#include "includes.h"
#include "common.h"
+#include "crypto/random.h"
#include "eap_common/eap_pax_common.h"
#include "eap_i.h"
@@ -174,7 +169,7 @@ static struct wpabuf * eap_pax_process_std_1(struct eap_pax_data *data,
pos, left);
}
- if (os_get_random(data->rand.r.y, EAP_PAX_RAND_LEN)) {
+ if (random_get_bytes(data->rand.r.y, EAP_PAX_RAND_LEN)) {
wpa_printf(MSG_ERROR, "EAP-PAX: Failed to get random data");
ret->ignore = TRUE;
return NULL;
diff --git a/contrib/wpa/src/eap_peer/eap_peap.c b/contrib/wpa/src/eap_peer/eap_peap.c
index 2b72084..7fff145 100644
--- a/contrib/wpa/src/eap_peer/eap_peap.c
+++ b/contrib/wpa/src/eap_peer/eap_peap.c
@@ -2,14 +2,8 @@
* EAP peer method: EAP-PEAP (draft-josefsson-pppext-eap-tls-eap-10.txt)
* Copyright (c) 2004-2008, Jouni Malinen <j@w1.fi>
*
- * This program is free software; you can redistribute it and/or modify
- * it under the terms of the GNU General Public License version 2 as
- * published by the Free Software Foundation.
- *
- * Alternatively, this software may be distributed under the terms of BSD
- * license.
- *
- * See README and COPYING for more details.
+ * This software may be distributed under the terms of the BSD license.
+ * See README for more details.
*/
#include "includes.h"
@@ -165,7 +159,7 @@ static void * eap_peap_init(struct eap_sm *sm)
data->phase2_type.vendor = EAP_VENDOR_IETF;
data->phase2_type.method = EAP_TYPE_NONE;
- if (eap_peer_tls_ssl_init(sm, &data->ssl, config)) {
+ if (eap_peer_tls_ssl_init(sm, &data->ssl, config, EAP_TYPE_PEAP)) {
wpa_printf(MSG_INFO, "EAP-PEAP: Failed to initialize SSL.");
eap_peap_deinit(sm, data);
return NULL;
@@ -196,7 +190,7 @@ static void eap_peap_deinit(struct eap_sm *sm, void *priv)
* @nak_type: TLV type (EAP_TLV_*)
* Returns: Buffer to the allocated EAP-TLV NAK message or %NULL on failure
*
- * This funtion builds an EAP-TLV NAK message. The caller is responsible for
+ * This function builds an EAP-TLV NAK message. The caller is responsible for
* freeing the returned buffer.
*/
static struct wpabuf * eap_tlv_build_nak(int id, u16 nak_type)
@@ -285,8 +279,10 @@ static int eap_peap_derive_cmk(struct eap_sm *sm, struct eap_peap_data *data)
* in the end of the label just before ISK; is that just a typo?)
*/
wpa_hexdump_key(MSG_DEBUG, "EAP-PEAP: TempKey", tk, 40);
- peap_prfplus(data->peap_version, tk, 40, "Inner Methods Compound Keys",
- isk, sizeof(isk), imck, sizeof(imck));
+ if (peap_prfplus(data->peap_version, tk, 40,
+ "Inner Methods Compound Keys",
+ isk, sizeof(isk), imck, sizeof(imck)) < 0)
+ return -1;
wpa_hexdump_key(MSG_DEBUG, "EAP-PEAP: IMCK (IPMKj)",
imck, sizeof(imck));
@@ -346,8 +342,8 @@ static int eap_tlv_add_cryptobinding(struct eap_sm *sm,
* @status: Status (EAP_TLV_RESULT_SUCCESS or EAP_TLV_RESULT_FAILURE)
* Returns: Buffer to the allocated EAP-TLV Result message or %NULL on failure
*
- * This funtion builds an EAP-TLV Result message. The caller is responsible for
- * freeing the returned buffer.
+ * This function builds an EAP-TLV Result message. The caller is responsible
+ * for freeing the returned buffer.
*/
static struct wpabuf * eap_tlv_build_result(struct eap_sm *sm,
struct eap_peap_data *data,
@@ -1247,9 +1243,12 @@ static u8 * eap_peap_getKey(struct eap_sm *sm, void *priv, size_t *len)
* termination for this label while the one used for deriving
* IPMK|CMK did not use null termination.
*/
- peap_prfplus(data->peap_version, data->ipmk, 40,
- "Session Key Generating Function",
- (u8 *) "\00", 1, csk, sizeof(csk));
+ if (peap_prfplus(data->peap_version, data->ipmk, 40,
+ "Session Key Generating Function",
+ (u8 *) "\00", 1, csk, sizeof(csk)) < 0) {
+ os_free(key);
+ return NULL;
+ }
wpa_hexdump_key(MSG_DEBUG, "EAP-PEAP: CSK", csk, sizeof(csk));
os_memcpy(key, csk, EAP_TLS_KEY_LEN);
wpa_hexdump(MSG_DEBUG, "EAP-PEAP: Derived key",
diff --git a/contrib/wpa/src/eap_peer/eap_psk.c b/contrib/wpa/src/eap_peer/eap_psk.c
index ccf871e..d618fcf 100644
--- a/contrib/wpa/src/eap_peer/eap_psk.c
+++ b/contrib/wpa/src/eap_peer/eap_psk.c
@@ -2,14 +2,8 @@
* EAP peer method: EAP-PSK (RFC 4764)
* Copyright (c) 2004-2008, Jouni Malinen <j@w1.fi>
*
- * This program is free software; you can redistribute it and/or modify
- * it under the terms of the GNU General Public License version 2 as
- * published by the Free Software Foundation.
- *
- * Alternatively, this software may be distributed under the terms of BSD
- * license.
- *
- * See README and COPYING for more details.
+ * This software may be distributed under the terms of the BSD license.
+ * See README for more details.
*
* Note: EAP-PSK is an EAP authentication method and as such, completely
* different from WPA-PSK. This file is not needed for WPA-PSK functionality.
@@ -19,6 +13,7 @@
#include "common.h"
#include "crypto/aes_wrap.h"
+#include "crypto/random.h"
#include "eap_common/eap_psk_common.h"
#include "eap_i.h"
@@ -130,7 +125,7 @@ static struct wpabuf * eap_psk_process_1(struct eap_psk_data *data,
wpa_hexdump_ascii(MSG_DEBUG, "EAP-PSK: ID_S",
data->id_s, data->id_s_len);
- if (os_get_random(data->rand_p, EAP_PSK_RAND_LEN)) {
+ if (random_get_bytes(data->rand_p, EAP_PSK_RAND_LEN)) {
wpa_printf(MSG_ERROR, "EAP-PSK: Failed to get random data");
ret->ignore = TRUE;
return NULL;
diff --git a/contrib/wpa/src/eap_peer/eap_pwd.c b/contrib/wpa/src/eap_peer/eap_pwd.c
new file mode 100644
index 0000000..267d0a5
--- /dev/null
+++ b/contrib/wpa/src/eap_peer/eap_pwd.c
@@ -0,0 +1,922 @@
+/*
+ * EAP peer method: EAP-pwd (RFC 5931)
+ * Copyright (c) 2010, Dan Harkins <dharkins@lounge.org>
+ *
+ * This software may be distributed under the terms of the BSD license.
+ * See README for more details.
+ */
+
+#include "includes.h"
+
+#include "common.h"
+#include "crypto/sha256.h"
+#include "eap_peer/eap_i.h"
+#include "eap_common/eap_pwd_common.h"
+
+
+struct eap_pwd_data {
+ enum {
+ PWD_ID_Req, PWD_Commit_Req, PWD_Confirm_Req, SUCCESS, FAILURE
+ } state;
+ u8 *id_peer;
+ size_t id_peer_len;
+ u8 *id_server;
+ size_t id_server_len;
+ u8 *password;
+ size_t password_len;
+ u16 group_num;
+ EAP_PWD_group *grp;
+
+ struct wpabuf *inbuf;
+ size_t in_frag_pos;
+ struct wpabuf *outbuf;
+ size_t out_frag_pos;
+ size_t mtu;
+
+ BIGNUM *k;
+ BIGNUM *private_value;
+ BIGNUM *server_scalar;
+ BIGNUM *my_scalar;
+ EC_POINT *my_element;
+ EC_POINT *server_element;
+
+ u8 msk[EAP_MSK_LEN];
+ u8 emsk[EAP_EMSK_LEN];
+
+ BN_CTX *bnctx;
+};
+
+
+#ifndef CONFIG_NO_STDOUT_DEBUG
+static const char * eap_pwd_state_txt(int state)
+{
+ switch (state) {
+ case PWD_ID_Req:
+ return "PWD-ID-Req";
+ case PWD_Commit_Req:
+ return "PWD-Commit-Req";
+ case PWD_Confirm_Req:
+ return "PWD-Confirm-Req";
+ case SUCCESS:
+ return "SUCCESS";
+ case FAILURE:
+ return "FAILURE";
+ default:
+ return "PWD-UNK";
+ }
+}
+#endif /* CONFIG_NO_STDOUT_DEBUG */
+
+
+static void eap_pwd_state(struct eap_pwd_data *data, int state)
+{
+ wpa_printf(MSG_DEBUG, "EAP-PWD: %s -> %s",
+ eap_pwd_state_txt(data->state), eap_pwd_state_txt(state));
+ data->state = state;
+}
+
+
+static void * eap_pwd_init(struct eap_sm *sm)
+{
+ struct eap_pwd_data *data;
+ const u8 *identity, *password;
+ size_t identity_len, password_len;
+
+ password = eap_get_config_password(sm, &password_len);
+ if (password == NULL) {
+ wpa_printf(MSG_INFO, "EAP-PWD: No password configured!");
+ return NULL;
+ }
+
+ identity = eap_get_config_identity(sm, &identity_len);
+ if (identity == NULL) {
+ wpa_printf(MSG_INFO, "EAP-PWD: No identity configured!");
+ return NULL;
+ }
+
+ if ((data = os_zalloc(sizeof(*data))) == NULL) {
+ wpa_printf(MSG_INFO, "EAP-PWD: memory allocation data fail");
+ return NULL;
+ }
+
+ if ((data->bnctx = BN_CTX_new()) == NULL) {
+ wpa_printf(MSG_INFO, "EAP-PWD: bn context allocation fail");
+ os_free(data);
+ return NULL;
+ }
+
+ if ((data->id_peer = os_malloc(identity_len)) == NULL) {
+ wpa_printf(MSG_INFO, "EAP-PWD: memory allocation id fail");
+ BN_CTX_free(data->bnctx);
+ os_free(data);
+ return NULL;
+ }
+
+ os_memcpy(data->id_peer, identity, identity_len);
+ data->id_peer_len = identity_len;
+
+ if ((data->password = os_malloc(password_len)) == NULL) {
+ wpa_printf(MSG_INFO, "EAP-PWD: memory allocation psk fail");
+ BN_CTX_free(data->bnctx);
+ os_free(data->id_peer);
+ os_free(data);
+ return NULL;
+ }
+ os_memcpy(data->password, password, password_len);
+ data->password_len = password_len;
+
+ data->out_frag_pos = data->in_frag_pos = 0;
+ data->inbuf = data->outbuf = NULL;
+ data->mtu = 1020; /* default from RFC 5931, make it configurable! */
+
+ data->state = PWD_ID_Req;
+
+ return data;
+}
+
+
+static void eap_pwd_deinit(struct eap_sm *sm, void *priv)
+{
+ struct eap_pwd_data *data = priv;
+
+ BN_free(data->private_value);
+ BN_free(data->server_scalar);
+ BN_free(data->my_scalar);
+ BN_free(data->k);
+ BN_CTX_free(data->bnctx);
+ EC_POINT_free(data->my_element);
+ EC_POINT_free(data->server_element);
+ os_free(data->id_peer);
+ os_free(data->id_server);
+ os_free(data->password);
+ if (data->grp) {
+ EC_GROUP_free(data->grp->group);
+ EC_POINT_free(data->grp->pwe);
+ BN_free(data->grp->order);
+ BN_free(data->grp->prime);
+ os_free(data->grp);
+ }
+ os_free(data);
+}
+
+
+static u8 * eap_pwd_getkey(struct eap_sm *sm, void *priv, size_t *len)
+{
+ struct eap_pwd_data *data = priv;
+ u8 *key;
+
+ if (data->state != SUCCESS)
+ return NULL;
+
+ key = os_malloc(EAP_MSK_LEN);
+ if (key == NULL)
+ return NULL;
+
+ os_memcpy(key, data->msk, EAP_MSK_LEN);
+ *len = EAP_MSK_LEN;
+
+ return key;
+}
+
+
+static void
+eap_pwd_perform_id_exchange(struct eap_sm *sm, struct eap_pwd_data *data,
+ struct eap_method_ret *ret,
+ const struct wpabuf *reqData,
+ const u8 *payload, size_t payload_len)
+{
+ struct eap_pwd_id *id;
+
+ if (data->state != PWD_ID_Req) {
+ ret->ignore = TRUE;
+ eap_pwd_state(data, FAILURE);
+ return;
+ }
+
+ if (payload_len < sizeof(struct eap_pwd_id)) {
+ ret->ignore = TRUE;
+ eap_pwd_state(data, FAILURE);
+ return;
+ }
+
+ id = (struct eap_pwd_id *) payload;
+ data->group_num = be_to_host16(id->group_num);
+ if ((id->random_function != EAP_PWD_DEFAULT_RAND_FUNC) ||
+ (id->prf != EAP_PWD_DEFAULT_PRF)) {
+ ret->ignore = TRUE;
+ eap_pwd_state(data, FAILURE);
+ return;
+ }
+
+ wpa_printf(MSG_DEBUG, "EAP-PWD (peer): using group %d",
+ data->group_num);
+
+ data->id_server = os_malloc(payload_len - sizeof(struct eap_pwd_id));
+ if (data->id_server == NULL) {
+ wpa_printf(MSG_INFO, "EAP-PWD: memory allocation id fail");
+ eap_pwd_state(data, FAILURE);
+ return;
+ }
+ data->id_server_len = payload_len - sizeof(struct eap_pwd_id);
+ os_memcpy(data->id_server, id->identity, data->id_server_len);
+ wpa_hexdump_ascii(MSG_INFO, "EAP-PWD (peer): server sent id of",
+ data->id_server, data->id_server_len);
+
+ if ((data->grp = (EAP_PWD_group *) os_malloc(sizeof(EAP_PWD_group))) ==
+ NULL) {
+ wpa_printf(MSG_INFO, "EAP-PWD: failed to allocate memory for "
+ "group");
+ eap_pwd_state(data, FAILURE);
+ return;
+ }
+
+ /* compute PWE */
+ if (compute_password_element(data->grp, data->group_num,
+ data->password, data->password_len,
+ data->id_server, data->id_server_len,
+ data->id_peer, data->id_peer_len,
+ id->token)) {
+ wpa_printf(MSG_INFO, "EAP-PWD (peer): unable to compute PWE");
+ eap_pwd_state(data, FAILURE);
+ return;
+ }
+
+ wpa_printf(MSG_DEBUG, "EAP-PWD (peer): computed %d bit PWE...",
+ BN_num_bits(data->grp->prime));
+
+ data->outbuf = wpabuf_alloc(sizeof(struct eap_pwd_id) +
+ data->id_peer_len);
+ if (data->outbuf == NULL) {
+ eap_pwd_state(data, FAILURE);
+ return;
+ }
+ wpabuf_put_be16(data->outbuf, data->group_num);
+ wpabuf_put_u8(data->outbuf, EAP_PWD_DEFAULT_RAND_FUNC);
+ wpabuf_put_u8(data->outbuf, EAP_PWD_DEFAULT_PRF);
+ wpabuf_put_data(data->outbuf, id->token, sizeof(id->token));
+ wpabuf_put_u8(data->outbuf, EAP_PWD_PREP_NONE);
+ wpabuf_put_data(data->outbuf, data->id_peer, data->id_peer_len);
+
+ eap_pwd_state(data, PWD_Commit_Req);
+}
+
+
+static void
+eap_pwd_perform_commit_exchange(struct eap_sm *sm, struct eap_pwd_data *data,
+ struct eap_method_ret *ret,
+ const struct wpabuf *reqData,
+ const u8 *payload, size_t payload_len)
+{
+ EC_POINT *K = NULL, *point = NULL;
+ BIGNUM *mask = NULL, *x = NULL, *y = NULL, *cofactor = NULL;
+ u16 offset;
+ u8 *ptr, *scalar = NULL, *element = NULL;
+
+ if (((data->private_value = BN_new()) == NULL) ||
+ ((data->my_element = EC_POINT_new(data->grp->group)) == NULL) ||
+ ((cofactor = BN_new()) == NULL) ||
+ ((data->my_scalar = BN_new()) == NULL) ||
+ ((mask = BN_new()) == NULL)) {
+ wpa_printf(MSG_INFO, "EAP-PWD (peer): scalar allocation fail");
+ goto fin;
+ }
+
+ if (!EC_GROUP_get_cofactor(data->grp->group, cofactor, NULL)) {
+ wpa_printf(MSG_INFO, "EAP-pwd (peer): unable to get cofactor "
+ "for curve");
+ goto fin;
+ }
+
+ BN_rand_range(data->private_value, data->grp->order);
+ BN_rand_range(mask, data->grp->order);
+ BN_add(data->my_scalar, data->private_value, mask);
+ BN_mod(data->my_scalar, data->my_scalar, data->grp->order,
+ data->bnctx);
+
+ if (!EC_POINT_mul(data->grp->group, data->my_element, NULL,
+ data->grp->pwe, mask, data->bnctx)) {
+ wpa_printf(MSG_INFO, "EAP-PWD (peer): element allocation "
+ "fail");
+ eap_pwd_state(data, FAILURE);
+ goto fin;
+ }
+
+ if (!EC_POINT_invert(data->grp->group, data->my_element, data->bnctx))
+ {
+ wpa_printf(MSG_INFO, "EAP-PWD (peer): element inversion fail");
+ goto fin;
+ }
+ BN_free(mask);
+
+ if (((x = BN_new()) == NULL) ||
+ ((y = BN_new()) == NULL)) {
+ wpa_printf(MSG_INFO, "EAP-PWD (peer): point allocation fail");
+ goto fin;
+ }
+
+ /* process the request */
+ if (((data->server_scalar = BN_new()) == NULL) ||
+ ((data->k = BN_new()) == NULL) ||
+ ((K = EC_POINT_new(data->grp->group)) == NULL) ||
+ ((point = EC_POINT_new(data->grp->group)) == NULL) ||
+ ((data->server_element = EC_POINT_new(data->grp->group)) == NULL))
+ {
+ wpa_printf(MSG_INFO, "EAP-PWD (peer): peer data allocation "
+ "fail");
+ goto fin;
+ }
+
+ /* element, x then y, followed by scalar */
+ ptr = (u8 *) payload;
+ BN_bin2bn(ptr, BN_num_bytes(data->grp->prime), x);
+ ptr += BN_num_bytes(data->grp->prime);
+ BN_bin2bn(ptr, BN_num_bytes(data->grp->prime), y);
+ ptr += BN_num_bytes(data->grp->prime);
+ BN_bin2bn(ptr, BN_num_bytes(data->grp->order), data->server_scalar);
+ if (!EC_POINT_set_affine_coordinates_GFp(data->grp->group,
+ data->server_element, x, y,
+ data->bnctx)) {
+ wpa_printf(MSG_INFO, "EAP-PWD (peer): setting peer element "
+ "fail");
+ goto fin;
+ }
+
+ /* check to ensure server's element is not in a small sub-group */
+ if (BN_cmp(cofactor, BN_value_one())) {
+ if (!EC_POINT_mul(data->grp->group, point, NULL,
+ data->server_element, cofactor, NULL)) {
+ wpa_printf(MSG_INFO, "EAP-PWD (peer): cannot multiply "
+ "server element by order!\n");
+ goto fin;
+ }
+ if (EC_POINT_is_at_infinity(data->grp->group, point)) {
+ wpa_printf(MSG_INFO, "EAP-PWD (peer): server element "
+ "is at infinity!\n");
+ goto fin;
+ }
+ }
+
+ /* compute the shared key, k */
+ if ((!EC_POINT_mul(data->grp->group, K, NULL, data->grp->pwe,
+ data->server_scalar, data->bnctx)) ||
+ (!EC_POINT_add(data->grp->group, K, K, data->server_element,
+ data->bnctx)) ||
+ (!EC_POINT_mul(data->grp->group, K, NULL, K, data->private_value,
+ data->bnctx))) {
+ wpa_printf(MSG_INFO, "EAP-PWD (peer): computing shared key "
+ "fail");
+ goto fin;
+ }
+
+ /* ensure that the shared key isn't in a small sub-group */
+ if (BN_cmp(cofactor, BN_value_one())) {
+ if (!EC_POINT_mul(data->grp->group, K, NULL, K, cofactor,
+ NULL)) {
+ wpa_printf(MSG_INFO, "EAP-PWD (peer): cannot multiply "
+ "shared key point by order");
+ goto fin;
+ }
+ }
+
+ /*
+ * This check is strictly speaking just for the case above where
+ * co-factor > 1 but it was suggested that even though this is probably
+ * never going to happen it is a simple and safe check "just to be
+ * sure" so let's be safe.
+ */
+ if (EC_POINT_is_at_infinity(data->grp->group, K)) {
+ wpa_printf(MSG_INFO, "EAP-PWD (peer): shared key point is at "
+ "infinity!\n");
+ goto fin;
+ }
+
+ if (!EC_POINT_get_affine_coordinates_GFp(data->grp->group, K, data->k,
+ NULL, data->bnctx)) {
+ wpa_printf(MSG_INFO, "EAP-PWD (peer): unable to extract "
+ "shared secret from point");
+ goto fin;
+ }
+
+ /* now do the response */
+ if (!EC_POINT_get_affine_coordinates_GFp(data->grp->group,
+ data->my_element, x, y,
+ data->bnctx)) {
+ wpa_printf(MSG_INFO, "EAP-PWD (peer): point assignment fail");
+ goto fin;
+ }
+
+ if (((scalar = os_malloc(BN_num_bytes(data->grp->order))) == NULL) ||
+ ((element = os_malloc(BN_num_bytes(data->grp->prime) * 2)) ==
+ NULL)) {
+ wpa_printf(MSG_INFO, "EAP-PWD (peer): data allocation fail");
+ goto fin;
+ }
+
+ /*
+ * bignums occupy as little memory as possible so one that is
+ * sufficiently smaller than the prime or order might need pre-pending
+ * with zeros.
+ */
+ os_memset(scalar, 0, BN_num_bytes(data->grp->order));
+ os_memset(element, 0, BN_num_bytes(data->grp->prime) * 2);
+ offset = BN_num_bytes(data->grp->order) -
+ BN_num_bytes(data->my_scalar);
+ BN_bn2bin(data->my_scalar, scalar + offset);
+
+ offset = BN_num_bytes(data->grp->prime) - BN_num_bytes(x);
+ BN_bn2bin(x, element + offset);
+ offset = BN_num_bytes(data->grp->prime) - BN_num_bytes(y);
+ BN_bn2bin(y, element + BN_num_bytes(data->grp->prime) + offset);
+
+ data->outbuf = wpabuf_alloc(BN_num_bytes(data->grp->order) +
+ 2 * BN_num_bytes(data->grp->prime));
+ if (data->outbuf == NULL)
+ goto fin;
+
+ /* we send the element as (x,y) follwed by the scalar */
+ wpabuf_put_data(data->outbuf, element,
+ 2 * BN_num_bytes(data->grp->prime));
+ wpabuf_put_data(data->outbuf, scalar, BN_num_bytes(data->grp->order));
+
+fin:
+ os_free(scalar);
+ os_free(element);
+ BN_free(x);
+ BN_free(y);
+ BN_free(cofactor);
+ EC_POINT_free(K);
+ EC_POINT_free(point);
+ if (data->outbuf == NULL)
+ eap_pwd_state(data, FAILURE);
+ else
+ eap_pwd_state(data, PWD_Confirm_Req);
+}
+
+
+static void
+eap_pwd_perform_confirm_exchange(struct eap_sm *sm, struct eap_pwd_data *data,
+ struct eap_method_ret *ret,
+ const struct wpabuf *reqData,
+ const u8 *payload, size_t payload_len)
+{
+ BIGNUM *x = NULL, *y = NULL;
+ struct crypto_hash *hash;
+ u32 cs;
+ u16 grp;
+ u8 conf[SHA256_MAC_LEN], *cruft = NULL, *ptr;
+ int offset;
+
+ /*
+ * first build up the ciphersuite which is group | random_function |
+ * prf
+ */
+ grp = htons(data->group_num);
+ ptr = (u8 *) &cs;
+ os_memcpy(ptr, &grp, sizeof(u16));
+ ptr += sizeof(u16);
+ *ptr = EAP_PWD_DEFAULT_RAND_FUNC;
+ ptr += sizeof(u8);
+ *ptr = EAP_PWD_DEFAULT_PRF;
+
+ /* each component of the cruft will be at most as big as the prime */
+ if (((cruft = os_malloc(BN_num_bytes(data->grp->prime))) == NULL) ||
+ ((x = BN_new()) == NULL) || ((y = BN_new()) == NULL)) {
+ wpa_printf(MSG_INFO, "EAP-PWD (server): confirm allocation "
+ "fail");
+ goto fin;
+ }
+
+ /*
+ * server's commit is H(k | server_element | server_scalar |
+ * peer_element | peer_scalar | ciphersuite)
+ */
+ hash = eap_pwd_h_init();
+ if (hash == NULL)
+ goto fin;
+
+ /*
+ * zero the memory each time because this is mod prime math and some
+ * value may start with a few zeros and the previous one did not.
+ */
+ os_memset(cruft, 0, BN_num_bytes(data->grp->prime));
+ offset = BN_num_bytes(data->grp->prime) - BN_num_bytes(data->k);
+ BN_bn2bin(data->k, cruft + offset);
+ eap_pwd_h_update(hash, cruft, BN_num_bytes(data->grp->prime));
+
+ /* server element: x, y */
+ if (!EC_POINT_get_affine_coordinates_GFp(data->grp->group,
+ data->server_element, x, y,
+ data->bnctx)) {
+ wpa_printf(MSG_INFO, "EAP-PWD (server): confirm point "
+ "assignment fail");
+ goto fin;
+ }
+ os_memset(cruft, 0, BN_num_bytes(data->grp->prime));
+ offset = BN_num_bytes(data->grp->prime) - BN_num_bytes(x);
+ BN_bn2bin(x, cruft + offset);
+ eap_pwd_h_update(hash, cruft, BN_num_bytes(data->grp->prime));
+ os_memset(cruft, 0, BN_num_bytes(data->grp->prime));
+ offset = BN_num_bytes(data->grp->prime) - BN_num_bytes(y);
+ BN_bn2bin(y, cruft + offset);
+ eap_pwd_h_update(hash, cruft, BN_num_bytes(data->grp->prime));
+
+ /* server scalar */
+ os_memset(cruft, 0, BN_num_bytes(data->grp->prime));
+ offset = BN_num_bytes(data->grp->order) -
+ BN_num_bytes(data->server_scalar);
+ BN_bn2bin(data->server_scalar, cruft + offset);
+ eap_pwd_h_update(hash, cruft, BN_num_bytes(data->grp->order));
+
+ /* my element: x, y */
+ if (!EC_POINT_get_affine_coordinates_GFp(data->grp->group,
+ data->my_element, x, y,
+ data->bnctx)) {
+ wpa_printf(MSG_INFO, "EAP-PWD (server): confirm point "
+ "assignment fail");
+ goto fin;
+ }
+
+ os_memset(cruft, 0, BN_num_bytes(data->grp->prime));
+ offset = BN_num_bytes(data->grp->prime) - BN_num_bytes(x);
+ BN_bn2bin(x, cruft + offset);
+ eap_pwd_h_update(hash, cruft, BN_num_bytes(data->grp->prime));
+ os_memset(cruft, 0, BN_num_bytes(data->grp->prime));
+ offset = BN_num_bytes(data->grp->prime) - BN_num_bytes(y);
+ BN_bn2bin(y, cruft + offset);
+ eap_pwd_h_update(hash, cruft, BN_num_bytes(data->grp->prime));
+
+ /* my scalar */
+ os_memset(cruft, 0, BN_num_bytes(data->grp->prime));
+ offset = BN_num_bytes(data->grp->order) -
+ BN_num_bytes(data->my_scalar);
+ BN_bn2bin(data->my_scalar, cruft + offset);
+ eap_pwd_h_update(hash, cruft, BN_num_bytes(data->grp->order));
+
+ /* the ciphersuite */
+ eap_pwd_h_update(hash, (u8 *) &cs, sizeof(u32));
+
+ /* random function fin */
+ eap_pwd_h_final(hash, conf);
+
+ ptr = (u8 *) payload;
+ if (os_memcmp(conf, ptr, SHA256_MAC_LEN)) {
+ wpa_printf(MSG_INFO, "EAP-PWD (peer): confirm did not verify");
+ goto fin;
+ }
+
+ wpa_printf(MSG_DEBUG, "EAP-pwd (peer): confirm verified");
+
+ /*
+ * compute confirm:
+ * H(k | peer_element | peer_scalar | server_element | server_scalar |
+ * ciphersuite)
+ */
+ hash = eap_pwd_h_init();
+ if (hash == NULL)
+ goto fin;
+
+ /* k */
+ os_memset(cruft, 0, BN_num_bytes(data->grp->prime));
+ offset = BN_num_bytes(data->grp->prime) - BN_num_bytes(data->k);
+ BN_bn2bin(data->k, cruft + offset);
+ eap_pwd_h_update(hash, cruft, BN_num_bytes(data->grp->prime));
+
+ /* my element */
+ if (!EC_POINT_get_affine_coordinates_GFp(data->grp->group,
+ data->my_element, x, y,
+ data->bnctx)) {
+ wpa_printf(MSG_INFO, "EAP-PWD (peer): confirm point "
+ "assignment fail");
+ goto fin;
+ }
+ os_memset(cruft, 0, BN_num_bytes(data->grp->prime));
+ offset = BN_num_bytes(data->grp->prime) - BN_num_bytes(x);
+ BN_bn2bin(x, cruft + offset);
+ eap_pwd_h_update(hash, cruft, BN_num_bytes(data->grp->prime));
+ os_memset(cruft, 0, BN_num_bytes(data->grp->prime));
+ offset = BN_num_bytes(data->grp->prime) - BN_num_bytes(y);
+ BN_bn2bin(y, cruft + offset);
+ eap_pwd_h_update(hash, cruft, BN_num_bytes(data->grp->prime));
+
+ /* my scalar */
+ os_memset(cruft, 0, BN_num_bytes(data->grp->prime));
+ offset = BN_num_bytes(data->grp->order) -
+ BN_num_bytes(data->my_scalar);
+ BN_bn2bin(data->my_scalar, cruft + offset);
+ eap_pwd_h_update(hash, cruft, BN_num_bytes(data->grp->order));
+
+ /* server element: x, y */
+ if (!EC_POINT_get_affine_coordinates_GFp(data->grp->group,
+ data->server_element, x, y,
+ data->bnctx)) {
+ wpa_printf(MSG_INFO, "EAP-PWD (peer): confirm point "
+ "assignment fail");
+ goto fin;
+ }
+ os_memset(cruft, 0, BN_num_bytes(data->grp->prime));
+ offset = BN_num_bytes(data->grp->prime) - BN_num_bytes(x);
+ BN_bn2bin(x, cruft + offset);
+ eap_pwd_h_update(hash, cruft, BN_num_bytes(data->grp->prime));
+ os_memset(cruft, 0, BN_num_bytes(data->grp->prime));
+ offset = BN_num_bytes(data->grp->prime) - BN_num_bytes(y);
+ BN_bn2bin(y, cruft + offset);
+ eap_pwd_h_update(hash, cruft, BN_num_bytes(data->grp->prime));
+
+ /* server scalar */
+ os_memset(cruft, 0, BN_num_bytes(data->grp->prime));
+ offset = BN_num_bytes(data->grp->order) -
+ BN_num_bytes(data->server_scalar);
+ BN_bn2bin(data->server_scalar, cruft + offset);
+ eap_pwd_h_update(hash, cruft, BN_num_bytes(data->grp->order));
+
+ /* the ciphersuite */
+ eap_pwd_h_update(hash, (u8 *) &cs, sizeof(u32));
+
+ /* all done */
+ eap_pwd_h_final(hash, conf);
+
+ if (compute_keys(data->grp, data->bnctx, data->k,
+ data->my_scalar, data->server_scalar, conf, ptr,
+ &cs, data->msk, data->emsk) < 0) {
+ wpa_printf(MSG_INFO, "EAP-PWD (peer): unable to compute MSK | "
+ "EMSK");
+ goto fin;
+ }
+
+ data->outbuf = wpabuf_alloc(SHA256_MAC_LEN);
+ if (data->outbuf == NULL)
+ goto fin;
+
+ wpabuf_put_data(data->outbuf, conf, SHA256_MAC_LEN);
+
+fin:
+ os_free(cruft);
+ BN_free(x);
+ BN_free(y);
+ ret->methodState = METHOD_DONE;
+ if (data->outbuf == NULL) {
+ ret->decision = DECISION_FAIL;
+ eap_pwd_state(data, FAILURE);
+ } else {
+ ret->decision = DECISION_UNCOND_SUCC;
+ eap_pwd_state(data, SUCCESS);
+ }
+}
+
+
+static struct wpabuf *
+eap_pwd_process(struct eap_sm *sm, void *priv, struct eap_method_ret *ret,
+ const struct wpabuf *reqData)
+{
+ struct eap_pwd_data *data = priv;
+ struct wpabuf *resp = NULL;
+ const u8 *pos, *buf;
+ size_t len;
+ u16 tot_len = 0;
+ u8 lm_exch;
+
+ pos = eap_hdr_validate(EAP_VENDOR_IETF, EAP_TYPE_PWD, reqData, &len);
+ if ((pos == NULL) || (len < 1)) {
+ wpa_printf(MSG_DEBUG, "EAP-pwd: Got a frame but pos is %s and "
+ "len is %d",
+ pos == NULL ? "NULL" : "not NULL", (int) len);
+ ret->ignore = TRUE;
+ return NULL;
+ }
+
+ ret->ignore = FALSE;
+ ret->methodState = METHOD_MAY_CONT;
+ ret->decision = DECISION_FAIL;
+ ret->allowNotifications = FALSE;
+
+ lm_exch = *pos;
+ pos++; /* skip over the bits and the exch */
+ len--;
+
+ /*
+ * we're fragmenting so send out the next fragment
+ */
+ if (data->out_frag_pos) {
+ /*
+ * this should be an ACK
+ */
+ if (len)
+ wpa_printf(MSG_INFO, "Bad Response! Fragmenting but "
+ "not an ACK");
+
+ wpa_printf(MSG_DEBUG, "EAP-pwd: Got an ACK for a fragment");
+ /*
+ * check if there are going to be more fragments
+ */
+ len = wpabuf_len(data->outbuf) - data->out_frag_pos;
+ if ((len + EAP_PWD_HDR_SIZE) > data->mtu) {
+ len = data->mtu - EAP_PWD_HDR_SIZE;
+ EAP_PWD_SET_MORE_BIT(lm_exch);
+ }
+ resp = eap_msg_alloc(EAP_VENDOR_IETF, EAP_TYPE_PWD,
+ EAP_PWD_HDR_SIZE + len,
+ EAP_CODE_RESPONSE, eap_get_id(reqData));
+ if (resp == NULL) {
+ wpa_printf(MSG_INFO, "Unable to allocate memory for "
+ "next fragment!");
+ return NULL;
+ }
+ wpabuf_put_u8(resp, lm_exch);
+ buf = wpabuf_head_u8(data->outbuf);
+ wpabuf_put_data(resp, buf + data->out_frag_pos, len);
+ data->out_frag_pos += len;
+ /*
+ * this is the last fragment so get rid of the out buffer
+ */
+ if (data->out_frag_pos >= wpabuf_len(data->outbuf)) {
+ wpabuf_free(data->outbuf);
+ data->outbuf = NULL;
+ data->out_frag_pos = 0;
+ }
+ wpa_printf(MSG_DEBUG, "EAP-pwd: Send %s fragment of %d bytes",
+ data->out_frag_pos == 0 ? "last" : "next",
+ (int) len);
+ return resp;
+ }
+
+ /*
+ * see if this is a fragment that needs buffering
+ *
+ * if it's the first fragment there'll be a length field
+ */
+ if (EAP_PWD_GET_LENGTH_BIT(lm_exch)) {
+ tot_len = WPA_GET_BE16(pos);
+ wpa_printf(MSG_DEBUG, "EAP-pwd: Incoming fragments whose "
+ "total length = %d", tot_len);
+ data->inbuf = wpabuf_alloc(tot_len);
+ if (data->inbuf == NULL) {
+ wpa_printf(MSG_INFO, "Out of memory to buffer "
+ "fragments!");
+ return NULL;
+ }
+ pos += sizeof(u16);
+ len -= sizeof(u16);
+ }
+ /*
+ * buffer and ACK the fragment
+ */
+ if (EAP_PWD_GET_MORE_BIT(lm_exch)) {
+ data->in_frag_pos += len;
+ if (data->in_frag_pos > wpabuf_size(data->inbuf)) {
+ wpa_printf(MSG_INFO, "EAP-pwd: Buffer overflow attack "
+ "detected (%d vs. %d)!",
+ (int) data->in_frag_pos,
+ (int) wpabuf_len(data->inbuf));
+ wpabuf_free(data->inbuf);
+ data->in_frag_pos = 0;
+ return NULL;
+ }
+ wpabuf_put_data(data->inbuf, pos, len);
+
+ resp = eap_msg_alloc(EAP_VENDOR_IETF, EAP_TYPE_PWD,
+ EAP_PWD_HDR_SIZE,
+ EAP_CODE_RESPONSE, eap_get_id(reqData));
+ if (resp != NULL)
+ wpabuf_put_u8(resp, (EAP_PWD_GET_EXCHANGE(lm_exch)));
+ wpa_printf(MSG_DEBUG, "EAP-pwd: ACKing a %d byte fragment",
+ (int) len);
+ return resp;
+ }
+ /*
+ * we're buffering and this is the last fragment
+ */
+ if (data->in_frag_pos) {
+ wpabuf_put_data(data->inbuf, pos, len);
+ wpa_printf(MSG_DEBUG, "EAP-pwd: Last fragment, %d bytes",
+ (int) len);
+ data->in_frag_pos += len;
+ pos = wpabuf_head_u8(data->inbuf);
+ len = data->in_frag_pos;
+ }
+ wpa_printf(MSG_DEBUG, "EAP-pwd: processing frame: exch %d, len %d",
+ EAP_PWD_GET_EXCHANGE(lm_exch), (int) len);
+
+ switch (EAP_PWD_GET_EXCHANGE(lm_exch)) {
+ case EAP_PWD_OPCODE_ID_EXCH:
+ eap_pwd_perform_id_exchange(sm, data, ret, reqData,
+ pos, len);
+ break;
+ case EAP_PWD_OPCODE_COMMIT_EXCH:
+ eap_pwd_perform_commit_exchange(sm, data, ret, reqData,
+ pos, len);
+ break;
+ case EAP_PWD_OPCODE_CONFIRM_EXCH:
+ eap_pwd_perform_confirm_exchange(sm, data, ret, reqData,
+ pos, len);
+ break;
+ default:
+ wpa_printf(MSG_INFO, "EAP-pwd: Ignoring message with unknown "
+ "opcode %d", lm_exch);
+ break;
+ }
+ /*
+ * if we buffered the just processed input now's the time to free it
+ */
+ if (data->in_frag_pos) {
+ wpabuf_free(data->inbuf);
+ data->in_frag_pos = 0;
+ }
+
+ if (data->outbuf == NULL)
+ return NULL; /* generic failure */
+
+ /*
+ * we have output! Do we need to fragment it?
+ */
+ len = wpabuf_len(data->outbuf);
+ if ((len + EAP_PWD_HDR_SIZE) > data->mtu) {
+ resp = eap_msg_alloc(EAP_VENDOR_IETF, EAP_TYPE_PWD, data->mtu,
+ EAP_CODE_RESPONSE, eap_get_id(reqData));
+ /*
+ * if so it's the first so include a length field
+ */
+ EAP_PWD_SET_LENGTH_BIT(lm_exch);
+ EAP_PWD_SET_MORE_BIT(lm_exch);
+ tot_len = len;
+ /*
+ * keep the packet at the MTU
+ */
+ len = data->mtu - EAP_PWD_HDR_SIZE - sizeof(u16);
+ wpa_printf(MSG_DEBUG, "EAP-pwd: Fragmenting output, total "
+ "length = %d", tot_len);
+ } else {
+ resp = eap_msg_alloc(EAP_VENDOR_IETF, EAP_TYPE_PWD,
+ EAP_PWD_HDR_SIZE + len,
+ EAP_CODE_RESPONSE, eap_get_id(reqData));
+ }
+ if (resp == NULL)
+ return NULL;
+
+ wpabuf_put_u8(resp, lm_exch);
+ if (EAP_PWD_GET_LENGTH_BIT(lm_exch)) {
+ wpabuf_put_be16(resp, tot_len);
+ data->out_frag_pos += len;
+ }
+ buf = wpabuf_head_u8(data->outbuf);
+ wpabuf_put_data(resp, buf, len);
+ /*
+ * if we're not fragmenting then there's no need to carry this around
+ */
+ if (data->out_frag_pos == 0) {
+ wpabuf_free(data->outbuf);
+ data->outbuf = NULL;
+ data->out_frag_pos = 0;
+ }
+
+ return resp;
+}
+
+
+static Boolean eap_pwd_key_available(struct eap_sm *sm, void *priv)
+{
+ struct eap_pwd_data *data = priv;
+ return data->state == SUCCESS;
+}
+
+
+static u8 * eap_pwd_get_emsk(struct eap_sm *sm, void *priv, size_t *len)
+{
+ struct eap_pwd_data *data = priv;
+ u8 *key;
+
+ if (data->state != SUCCESS)
+ return NULL;
+
+ if ((key = os_malloc(EAP_EMSK_LEN)) == NULL)
+ return NULL;
+
+ os_memcpy(key, data->emsk, EAP_EMSK_LEN);
+ *len = EAP_EMSK_LEN;
+
+ return key;
+}
+
+
+int eap_peer_pwd_register(void)
+{
+ struct eap_method *eap;
+ int ret;
+
+ EVP_add_digest(EVP_sha256());
+ eap = eap_peer_method_alloc(EAP_PEER_METHOD_INTERFACE_VERSION,
+ EAP_VENDOR_IETF, EAP_TYPE_PWD, "PWD");
+ if (eap == NULL)
+ return -1;
+
+ eap->init = eap_pwd_init;
+ eap->deinit = eap_pwd_deinit;
+ eap->process = eap_pwd_process;
+ eap->isKeyAvailable = eap_pwd_key_available;
+ eap->getKey = eap_pwd_getkey;
+ eap->get_emsk = eap_pwd_get_emsk;
+
+ ret = eap_peer_method_register(eap);
+ if (ret)
+ eap_peer_method_free(eap);
+ return ret;
+}
diff --git a/contrib/wpa/src/eap_peer/eap_sake.c b/contrib/wpa/src/eap_peer/eap_sake.c
index bb06bb2..e072f46 100644
--- a/contrib/wpa/src/eap_peer/eap_sake.c
+++ b/contrib/wpa/src/eap_peer/eap_sake.c
@@ -2,19 +2,14 @@
* EAP peer method: EAP-SAKE (RFC 4763)
* Copyright (c) 2006-2008, Jouni Malinen <j@w1.fi>
*
- * This program is free software; you can redistribute it and/or modify
- * it under the terms of the GNU General Public License version 2 as
- * published by the Free Software Foundation.
- *
- * Alternatively, this software may be distributed under the terms of BSD
- * license.
- *
- * See README and COPYING for more details.
+ * This software may be distributed under the terms of the BSD license.
+ * See README for more details.
*/
#include "includes.h"
#include "common.h"
+#include "crypto/random.h"
#include "eap_peer/eap_i.h"
#include "eap_common/eap_sake_common.h"
@@ -223,7 +218,7 @@ static struct wpabuf * eap_sake_process_challenge(struct eap_sm *sm,
wpa_hexdump(MSG_MSGDUMP, "EAP-SAKE: RAND_S (server rand)",
data->rand_s, EAP_SAKE_RAND_LEN);
- if (os_get_random(data->rand_p, EAP_SAKE_RAND_LEN)) {
+ if (random_get_bytes(data->rand_p, EAP_SAKE_RAND_LEN)) {
wpa_printf(MSG_ERROR, "EAP-SAKE: Failed to get random data");
return NULL;
}
diff --git a/contrib/wpa/src/eap_peer/eap_sim.c b/contrib/wpa/src/eap_peer/eap_sim.c
index 3d8afb2..c936a44 100644
--- a/contrib/wpa/src/eap_peer/eap_sim.c
+++ b/contrib/wpa/src/eap_peer/eap_sim.c
@@ -1,15 +1,9 @@
/*
* EAP peer method: EAP-SIM (RFC 4186)
- * Copyright (c) 2004-2008, Jouni Malinen <j@w1.fi>
+ * Copyright (c) 2004-2012, Jouni Malinen <j@w1.fi>
*
- * This program is free software; you can redistribute it and/or modify
- * it under the terms of the GNU General Public License version 2 as
- * published by the Free Software Foundation.
- *
- * Alternatively, this software may be distributed under the terms of BSD
- * license.
- *
- * See README and COPYING for more details.
+ * This software may be distributed under the terms of the BSD license.
+ * See README for more details.
*/
#include "includes.h"
@@ -17,6 +11,7 @@
#include "common.h"
#include "pcsc_funcs.h"
#include "crypto/milenage.h"
+#include "crypto/random.h"
#include "eap_peer/eap_i.h"
#include "eap_config.h"
#include "eap_common/eap_sim_common.h"
@@ -93,7 +88,7 @@ static void * eap_sim_init(struct eap_sm *sm)
if (data == NULL)
return NULL;
- if (os_get_random(data->nonce_mt, EAP_SIM_NONCE_MT_LEN)) {
+ if (random_get_bytes(data->nonce_mt, EAP_SIM_NONCE_MT_LEN)) {
wpa_printf(MSG_WARNING, "EAP-SIM: Failed to get random data "
"for NONCE_MT");
os_free(data);
@@ -122,6 +117,15 @@ static void * eap_sim_init(struct eap_sm *sm)
NULL;
}
+ if (config && config->anonymous_identity) {
+ data->pseudonym = os_malloc(config->anonymous_identity_len);
+ if (data->pseudonym) {
+ os_memcpy(data->pseudonym, config->anonymous_identity,
+ config->anonymous_identity_len);
+ data->pseudonym_len = config->anonymous_identity_len;
+ }
+ }
+
eap_sim_state(data, CONTINUE);
return data;
@@ -263,23 +267,24 @@ static int eap_sim_supported_ver(int version)
#define CLEAR_REAUTH_ID 0x02
#define CLEAR_EAP_ID 0x04
-static void eap_sim_clear_identities(struct eap_sim_data *data, int id)
+static void eap_sim_clear_identities(struct eap_sm *sm,
+ struct eap_sim_data *data, int id)
{
- wpa_printf(MSG_DEBUG, "EAP-SIM: forgetting old%s%s%s",
- id & CLEAR_PSEUDONYM ? " pseudonym" : "",
- id & CLEAR_REAUTH_ID ? " reauth_id" : "",
- id & CLEAR_EAP_ID ? " eap_id" : "");
- if (id & CLEAR_PSEUDONYM) {
+ if ((id & CLEAR_PSEUDONYM) && data->pseudonym) {
+ wpa_printf(MSG_DEBUG, "EAP-SIM: forgetting old pseudonym");
os_free(data->pseudonym);
data->pseudonym = NULL;
data->pseudonym_len = 0;
+ eap_set_anon_id(sm, NULL, 0);
}
- if (id & CLEAR_REAUTH_ID) {
+ if ((id & CLEAR_REAUTH_ID) && data->reauth_id) {
+ wpa_printf(MSG_DEBUG, "EAP-SIM: forgetting old reauth_id");
os_free(data->reauth_id);
data->reauth_id = NULL;
data->reauth_id_len = 0;
}
- if (id & CLEAR_EAP_ID) {
+ if ((id & CLEAR_EAP_ID) && data->last_eap_identity) {
+ wpa_printf(MSG_DEBUG, "EAP-SIM: forgetting old eap_id");
os_free(data->last_eap_identity);
data->last_eap_identity = NULL;
data->last_eap_identity_len = 0;
@@ -287,24 +292,45 @@ static void eap_sim_clear_identities(struct eap_sim_data *data, int id)
}
-static int eap_sim_learn_ids(struct eap_sim_data *data,
+static int eap_sim_learn_ids(struct eap_sm *sm, struct eap_sim_data *data,
struct eap_sim_attrs *attr)
{
if (attr->next_pseudonym) {
+ const u8 *identity = NULL;
+ size_t identity_len = 0;
+ const u8 *realm = NULL;
+ size_t realm_len = 0;
+
+ wpa_hexdump_ascii(MSG_DEBUG,
+ "EAP-SIM: (encr) AT_NEXT_PSEUDONYM",
+ attr->next_pseudonym,
+ attr->next_pseudonym_len);
os_free(data->pseudonym);
- data->pseudonym = os_malloc(attr->next_pseudonym_len);
+ /* Look for the realm of the permanent identity */
+ identity = eap_get_config_identity(sm, &identity_len);
+ if (identity) {
+ for (realm = identity, realm_len = identity_len;
+ realm_len > 0; realm_len--, realm++) {
+ if (*realm == '@')
+ break;
+ }
+ }
+ data->pseudonym = os_malloc(attr->next_pseudonym_len +
+ realm_len);
if (data->pseudonym == NULL) {
wpa_printf(MSG_INFO, "EAP-SIM: (encr) No memory for "
"next pseudonym");
+ data->pseudonym_len = 0;
return -1;
}
os_memcpy(data->pseudonym, attr->next_pseudonym,
attr->next_pseudonym_len);
- data->pseudonym_len = attr->next_pseudonym_len;
- wpa_hexdump_ascii(MSG_DEBUG,
- "EAP-SIM: (encr) AT_NEXT_PSEUDONYM",
- data->pseudonym,
- data->pseudonym_len);
+ if (realm_len) {
+ os_memcpy(data->pseudonym + attr->next_pseudonym_len,
+ realm, realm_len);
+ }
+ data->pseudonym_len = attr->next_pseudonym_len + realm_len;
+ eap_set_anon_id(sm, data->pseudonym, data->pseudonym_len);
}
if (attr->next_reauth_id) {
@@ -313,6 +339,7 @@ static int eap_sim_learn_ids(struct eap_sim_data *data,
if (data->reauth_id == NULL) {
wpa_printf(MSG_INFO, "EAP-SIM: (encr) No memory for "
"next reauth_id");
+ data->reauth_id_len = 0;
return -1;
}
os_memcpy(data->reauth_id, attr->next_reauth_id,
@@ -337,6 +364,8 @@ static struct wpabuf * eap_sim_client_error(struct eap_sim_data *data, u8 id,
data->num_id_req = 0;
data->num_notification = 0;
+ wpa_printf(MSG_DEBUG, "EAP-SIM: Send Client-Error (error code %d)",
+ err);
msg = eap_sim_msg_init(EAP_CODE_RESPONSE, id, EAP_TYPE_SIM,
EAP_SIM_SUBTYPE_CLIENT_ERROR);
eap_sim_msg_add(msg, EAP_SIM_AT_CLIENT_ERROR_CODE, err, NULL, 0);
@@ -361,16 +390,16 @@ static struct wpabuf * eap_sim_response_start(struct eap_sm *sm,
data->pseudonym) {
identity = data->pseudonym;
identity_len = data->pseudonym_len;
- eap_sim_clear_identities(data, CLEAR_REAUTH_ID);
+ eap_sim_clear_identities(sm, data, CLEAR_REAUTH_ID);
} else if (id_req != NO_ID_REQ) {
identity = eap_get_config_identity(sm, &identity_len);
if (identity) {
- eap_sim_clear_identities(data, CLEAR_PSEUDONYM |
+ eap_sim_clear_identities(sm, data, CLEAR_PSEUDONYM |
CLEAR_REAUTH_ID);
}
}
if (id_req != NO_ID_REQ)
- eap_sim_clear_identities(data, CLEAR_EAP_ID);
+ eap_sim_clear_identities(sm, data, CLEAR_EAP_ID);
wpa_printf(MSG_DEBUG, "Generating EAP-SIM Start (id=%d)", id);
msg = eap_sim_msg_init(EAP_CODE_RESPONSE, id,
@@ -417,7 +446,8 @@ static struct wpabuf * eap_sim_response_challenge(struct eap_sim_data *data,
static struct wpabuf * eap_sim_response_reauth(struct eap_sim_data *data,
- u8 id, int counter_too_small)
+ u8 id, int counter_too_small,
+ const u8 *nonce_s)
{
struct eap_sim_msg *msg;
unsigned int counter;
@@ -452,7 +482,7 @@ static struct wpabuf * eap_sim_response_reauth(struct eap_sim_data *data,
}
wpa_printf(MSG_DEBUG, " AT_MAC");
eap_sim_msg_add_mac(msg, EAP_SIM_AT_MAC);
- return eap_sim_msg_finish(msg, data->k_aut, data->nonce_s,
+ return eap_sim_msg_finish(msg, data->k_aut, nonce_s,
EAP_SIM_NONCE_S_LEN);
}
@@ -648,11 +678,11 @@ static struct wpabuf * eap_sim_process_challenge(struct eap_sm *sm,
EAP_SIM_UNABLE_TO_PROCESS_PACKET);
}
- /* Old reauthentication and pseudonym identities must not be used
- * anymore. In other words, if no new identities are received, full
- * authentication will be used on next reauthentication. */
- eap_sim_clear_identities(data, CLEAR_PSEUDONYM | CLEAR_REAUTH_ID |
- CLEAR_EAP_ID);
+ /* Old reauthentication identity must not be used anymore. In
+ * other words, if no new reauth identity is received, full
+ * authentication will be used on next reauthentication (using
+ * pseudonym identity or permanent identity). */
+ eap_sim_clear_identities(sm, data, CLEAR_REAUTH_ID | CLEAR_EAP_ID);
if (attr->encr_data) {
u8 *decrypted;
@@ -663,7 +693,7 @@ static struct wpabuf * eap_sim_process_challenge(struct eap_sm *sm,
return eap_sim_client_error(
data, id, EAP_SIM_UNABLE_TO_PROCESS_PACKET);
}
- eap_sim_learn_ids(data, &eattr);
+ eap_sim_learn_ids(sm, data, &eattr);
os_free(decrypted);
}
@@ -848,7 +878,7 @@ static struct wpabuf * eap_sim_process_reauthentication(
data->reauth_id = NULL;
data->reauth_id_len = 0;
os_free(decrypted);
- return eap_sim_response_reauth(data, id, 1);
+ return eap_sim_response_reauth(data, id, 1, eattr.nonce_s);
}
data->counter = eattr.counter;
@@ -860,8 +890,8 @@ static struct wpabuf * eap_sim_process_reauthentication(
data->reauth_id, data->reauth_id_len,
data->nonce_s, data->mk, data->msk,
data->emsk);
- eap_sim_clear_identities(data, CLEAR_REAUTH_ID | CLEAR_EAP_ID);
- eap_sim_learn_ids(data, &eattr);
+ eap_sim_clear_identities(sm, data, CLEAR_REAUTH_ID | CLEAR_EAP_ID);
+ eap_sim_learn_ids(sm, data, &eattr);
if (data->result_ind && attr->result_ind)
data->use_result_ind = 1;
@@ -876,10 +906,11 @@ static struct wpabuf * eap_sim_process_reauthentication(
if (data->counter > EAP_SIM_MAX_FAST_REAUTHS) {
wpa_printf(MSG_DEBUG, "EAP-SIM: Maximum number of "
"fast reauths performed - force fullauth");
- eap_sim_clear_identities(data, CLEAR_REAUTH_ID | CLEAR_EAP_ID);
+ eap_sim_clear_identities(sm, data,
+ CLEAR_REAUTH_ID | CLEAR_EAP_ID);
}
os_free(decrypted);
- return eap_sim_response_reauth(data, id, 0);
+ return eap_sim_response_reauth(data, id, 0, data->nonce_s);
}
@@ -987,7 +1018,7 @@ static Boolean eap_sim_has_reauth_data(struct eap_sm *sm, void *priv)
static void eap_sim_deinit_for_reauth(struct eap_sm *sm, void *priv)
{
struct eap_sim_data *data = priv;
- eap_sim_clear_identities(data, CLEAR_EAP_ID);
+ eap_sim_clear_identities(sm, data, CLEAR_EAP_ID);
data->use_result_ind = 0;
}
@@ -995,7 +1026,7 @@ static void eap_sim_deinit_for_reauth(struct eap_sm *sm, void *priv)
static void * eap_sim_init_for_reauth(struct eap_sm *sm, void *priv)
{
struct eap_sim_data *data = priv;
- if (os_get_random(data->nonce_mt, EAP_SIM_NONCE_MT_LEN)) {
+ if (random_get_bytes(data->nonce_mt, EAP_SIM_NONCE_MT_LEN)) {
wpa_printf(MSG_WARNING, "EAP-SIM: Failed to get random data "
"for NONCE_MT");
os_free(data);
diff --git a/contrib/wpa/src/eap_peer/eap_tls.c b/contrib/wpa/src/eap_peer/eap_tls.c
index 20b2212..061a72b 100644
--- a/contrib/wpa/src/eap_peer/eap_tls.c
+++ b/contrib/wpa/src/eap_peer/eap_tls.c
@@ -1,15 +1,9 @@
/*
* EAP peer method: EAP-TLS (RFC 2716)
- * Copyright (c) 2004-2008, Jouni Malinen <j@w1.fi>
+ * Copyright (c) 2004-2008, 2012, Jouni Malinen <j@w1.fi>
*
- * This program is free software; you can redistribute it and/or modify
- * it under the terms of the GNU General Public License version 2 as
- * published by the Free Software Foundation.
- *
- * Alternatively, this software may be distributed under the terms of BSD
- * license.
- *
- * See README and COPYING for more details.
+ * This software may be distributed under the terms of the BSD license.
+ * See README for more details.
*/
#include "includes.h"
@@ -27,6 +21,8 @@ static void eap_tls_deinit(struct eap_sm *sm, void *priv);
struct eap_tls_data {
struct eap_ssl_data ssl;
u8 *key_data;
+ void *ssl_ctx;
+ u8 eap_type;
};
@@ -46,7 +42,10 @@ static void * eap_tls_init(struct eap_sm *sm)
if (data == NULL)
return NULL;
- if (eap_peer_tls_ssl_init(sm, &data->ssl, config)) {
+ data->ssl_ctx = sm->init_phase2 && sm->ssl_ctx2 ? sm->ssl_ctx2 :
+ sm->ssl_ctx;
+
+ if (eap_peer_tls_ssl_init(sm, &data->ssl, config, EAP_TYPE_TLS)) {
wpa_printf(MSG_INFO, "EAP-TLS: Failed to initialize SSL.");
eap_tls_deinit(sm, data);
if (config->engine) {
@@ -64,8 +63,37 @@ static void * eap_tls_init(struct eap_sm *sm)
return NULL;
}
+ data->eap_type = EAP_TYPE_TLS;
+
+ return data;
+}
+
+
+#ifdef EAP_UNAUTH_TLS
+static void * eap_unauth_tls_init(struct eap_sm *sm)
+{
+ struct eap_tls_data *data;
+ struct eap_peer_config *config = eap_get_config(sm);
+
+ data = os_zalloc(sizeof(*data));
+ if (data == NULL)
+ return NULL;
+
+ data->ssl_ctx = sm->init_phase2 && sm->ssl_ctx2 ? sm->ssl_ctx2 :
+ sm->ssl_ctx;
+
+ if (eap_peer_tls_ssl_init(sm, &data->ssl, config,
+ EAP_UNAUTH_TLS_TYPE)) {
+ wpa_printf(MSG_INFO, "EAP-TLS: Failed to initialize SSL.");
+ eap_tls_deinit(sm, data);
+ return NULL;
+ }
+
+ data->eap_type = EAP_UNAUTH_TLS_TYPE;
+
return data;
}
+#endif /* EAP_UNAUTH_TLS */
static void eap_tls_deinit(struct eap_sm *sm, void *priv)
@@ -111,7 +139,7 @@ static struct wpabuf * eap_tls_failure(struct eap_sm *sm,
return resp;
}
- return eap_peer_tls_build_ack(id, EAP_TYPE_TLS, 0);
+ return eap_peer_tls_build_ack(id, data->eap_type, 0);
}
@@ -151,7 +179,7 @@ static struct wpabuf * eap_tls_process(struct eap_sm *sm, void *priv,
const u8 *pos;
struct eap_tls_data *data = priv;
- pos = eap_peer_tls_process_init(sm, &data->ssl, EAP_TYPE_TLS, ret,
+ pos = eap_peer_tls_process_init(sm, &data->ssl, data->eap_type, ret,
reqData, &left, &flags);
if (pos == NULL)
return NULL;
@@ -164,19 +192,19 @@ static struct wpabuf * eap_tls_process(struct eap_sm *sm, void *priv,
}
resp = NULL;
- res = eap_peer_tls_process_helper(sm, &data->ssl, EAP_TYPE_TLS, 0, id,
- pos, left, &resp);
+ res = eap_peer_tls_process_helper(sm, &data->ssl, data->eap_type, 0,
+ id, pos, left, &resp);
if (res < 0) {
return eap_tls_failure(sm, data, ret, res, resp, id);
}
- if (tls_connection_established(sm->ssl_ctx, data->ssl.conn))
+ if (tls_connection_established(data->ssl_ctx, data->ssl.conn))
eap_tls_success(sm, data, ret);
if (res == 1) {
wpabuf_free(resp);
- return eap_peer_tls_build_ack(id, EAP_TYPE_TLS, 0);
+ return eap_peer_tls_build_ack(id, data->eap_type, 0);
}
return resp;
@@ -186,7 +214,7 @@ static struct wpabuf * eap_tls_process(struct eap_sm *sm, void *priv,
static Boolean eap_tls_has_reauth_data(struct eap_sm *sm, void *priv)
{
struct eap_tls_data *data = priv;
- return tls_connection_established(sm->ssl_ctx, data->ssl.conn);
+ return tls_connection_established(data->ssl_ctx, data->ssl.conn);
}
@@ -287,3 +315,34 @@ int eap_peer_tls_register(void)
eap_peer_method_free(eap);
return ret;
}
+
+
+#ifdef EAP_UNAUTH_TLS
+int eap_peer_unauth_tls_register(void)
+{
+ struct eap_method *eap;
+ int ret;
+
+ eap = eap_peer_method_alloc(EAP_PEER_METHOD_INTERFACE_VERSION,
+ EAP_VENDOR_UNAUTH_TLS,
+ EAP_VENDOR_TYPE_UNAUTH_TLS, "UNAUTH-TLS");
+ if (eap == NULL)
+ return -1;
+
+ eap->init = eap_unauth_tls_init;
+ eap->deinit = eap_tls_deinit;
+ eap->process = eap_tls_process;
+ eap->isKeyAvailable = eap_tls_isKeyAvailable;
+ eap->getKey = eap_tls_getKey;
+ eap->get_status = eap_tls_get_status;
+ eap->has_reauth_data = eap_tls_has_reauth_data;
+ eap->deinit_for_reauth = eap_tls_deinit_for_reauth;
+ eap->init_for_reauth = eap_tls_init_for_reauth;
+ eap->get_emsk = eap_tls_get_emsk;
+
+ ret = eap_peer_method_register(eap);
+ if (ret)
+ eap_peer_method_free(eap);
+ return ret;
+}
+#endif /* EAP_UNAUTH_TLS */
diff --git a/contrib/wpa/src/eap_peer/eap_tls_common.c b/contrib/wpa/src/eap_peer/eap_tls_common.c
index 7bd50f6..aedd85a 100644
--- a/contrib/wpa/src/eap_peer/eap_tls_common.c
+++ b/contrib/wpa/src/eap_peer/eap_tls_common.c
@@ -1,15 +1,9 @@
/*
* EAP peer: EAP-TLS/PEAP/TTLS/FAST common functions
- * Copyright (c) 2004-2009, Jouni Malinen <j@w1.fi>
+ * Copyright (c) 2004-2012, Jouni Malinen <j@w1.fi>
*
- * This program is free software; you can redistribute it and/or modify
- * it under the terms of the GNU General Public License version 2 as
- * published by the Free Software Foundation.
- *
- * Alternatively, this software may be distributed under the terms of BSD
- * license.
- *
- * See README and COPYING for more details.
+ * This software may be distributed under the terms of the BSD license.
+ * See README for more details.
*/
#include "includes.h"
@@ -22,6 +16,18 @@
#include "eap_config.h"
+static struct wpabuf * eap_tls_msg_alloc(EapType type, size_t payload_len,
+ u8 code, u8 identifier)
+{
+ if (type == EAP_UNAUTH_TLS_TYPE)
+ return eap_msg_alloc(EAP_VENDOR_UNAUTH_TLS,
+ EAP_VENDOR_TYPE_UNAUTH_TLS, payload_len,
+ code, identifier);
+ return eap_msg_alloc(EAP_VENDOR_IETF, type, payload_len, code,
+ identifier);
+}
+
+
static int eap_tls_check_blob(struct eap_sm *sm, const char **name,
const u8 **data, size_t *data_len)
{
@@ -54,6 +60,10 @@ static void eap_tls_params_flags(struct tls_connection_params *params,
params->flags |= TLS_CONN_ALLOW_SIGN_RSA_MD5;
if (os_strstr(txt, "tls_disable_time_checks=1"))
params->flags |= TLS_CONN_DISABLE_TIME_CHECKS;
+ if (os_strstr(txt, "tls_disable_session_ticket=1"))
+ params->flags |= TLS_CONN_DISABLE_SESSION_TICKET;
+ if (os_strstr(txt, "tls_disable_session_ticket=0"))
+ params->flags &= ~TLS_CONN_DISABLE_SESSION_TICKET;
}
@@ -105,6 +115,18 @@ static int eap_tls_params_from_conf(struct eap_sm *sm,
struct eap_peer_config *config, int phase2)
{
os_memset(params, 0, sizeof(*params));
+ if (sm->workaround && data->eap_type != EAP_TYPE_FAST) {
+ /*
+ * Some deployed authentication servers seem to be unable to
+ * handle the TLS Session Ticket extension (they are supposed
+ * to ignore unrecognized TLS extensions, but end up rejecting
+ * the ClientHello instead). As a workaround, disable use of
+ * TLS Sesson Ticket extension for EAP-TLS, EAP-PEAP, and
+ * EAP-TTLS (EAP-FAST uses session ticket, so any server that
+ * supports EAP-FAST does not need this workaround).
+ */
+ params->flags |= TLS_CONN_DISABLE_SESSION_TICKET;
+ }
if (phase2) {
wpa_printf(MSG_DEBUG, "TLS: using phase2 config options");
eap_tls_params_from_conf2(params, config);
@@ -112,7 +134,6 @@ static int eap_tls_params_from_conf(struct eap_sm *sm,
wpa_printf(MSG_DEBUG, "TLS: using phase1 config options");
eap_tls_params_from_conf1(params, config);
}
- params->tls_ia = data->tls_ia;
/*
* Use blob data, if available. Otherwise, leave reference to external
@@ -143,14 +164,14 @@ static int eap_tls_init_connection(struct eap_sm *sm,
{
int res;
- data->conn = tls_connection_init(sm->ssl_ctx);
+ data->conn = tls_connection_init(data->ssl_ctx);
if (data->conn == NULL) {
wpa_printf(MSG_INFO, "SSL: Failed to initialize new TLS "
"connection");
return -1;
}
- res = tls_connection_set_params(sm->ssl_ctx, data->conn, params);
+ res = tls_connection_set_params(data->ssl_ctx, data->conn, params);
if (res == TLS_SET_PARAMS_ENGINE_PRV_INIT_FAILED) {
/*
* At this point with the pkcs11 engine the PIN might be wrong.
@@ -169,13 +190,13 @@ static int eap_tls_init_connection(struct eap_sm *sm,
config->pin = NULL;
eap_sm_request_pin(sm);
sm->ignore = TRUE;
- tls_connection_deinit(sm->ssl_ctx, data->conn);
+ tls_connection_deinit(data->ssl_ctx, data->conn);
data->conn = NULL;
return -1;
} else if (res) {
wpa_printf(MSG_INFO, "TLS: Failed to set TLS connection "
"parameters");
- tls_connection_deinit(sm->ssl_ctx, data->conn);
+ tls_connection_deinit(data->ssl_ctx, data->conn);
data->conn = NULL;
return -1;
}
@@ -189,13 +210,14 @@ static int eap_tls_init_connection(struct eap_sm *sm,
* @sm: Pointer to EAP state machine allocated with eap_peer_sm_init()
* @data: Data for TLS processing
* @config: Pointer to the network configuration
+ * @eap_type: EAP method used in Phase 1 (EAP_TYPE_TLS/PEAP/TTLS/FAST)
* Returns: 0 on success, -1 on failure
*
* This function is used to initialize shared TLS functionality for EAP-TLS,
* EAP-PEAP, EAP-TTLS, and EAP-FAST.
*/
int eap_peer_tls_ssl_init(struct eap_sm *sm, struct eap_ssl_data *data,
- struct eap_peer_config *config)
+ struct eap_peer_config *config, u8 eap_type)
{
struct tls_connection_params params;
@@ -203,7 +225,10 @@ int eap_peer_tls_ssl_init(struct eap_sm *sm, struct eap_ssl_data *data,
return -1;
data->eap = sm;
+ data->eap_type = eap_type;
data->phase2 = sm->init_phase2;
+ data->ssl_ctx = sm->init_phase2 && sm->ssl_ctx2 ? sm->ssl_ctx2 :
+ sm->ssl_ctx;
if (eap_tls_params_from_conf(sm, data, &params, config, data->phase2) <
0)
return -1;
@@ -241,7 +266,7 @@ int eap_peer_tls_ssl_init(struct eap_sm *sm, struct eap_ssl_data *data,
*/
void eap_peer_tls_ssl_deinit(struct eap_sm *sm, struct eap_ssl_data *data)
{
- tls_connection_deinit(sm->ssl_ctx, data->conn);
+ tls_connection_deinit(data->ssl_ctx, data->conn);
eap_peer_tls_reset_input(data);
eap_peer_tls_reset_output(data);
}
@@ -264,7 +289,9 @@ void eap_peer_tls_ssl_deinit(struct eap_sm *sm, struct eap_ssl_data *data)
u8 * eap_peer_tls_derive_key(struct eap_sm *sm, struct eap_ssl_data *data,
const char *label, size_t len)
{
+#ifndef CONFIG_FIPS
struct tls_keys keys;
+#endif /* CONFIG_FIPS */
u8 *rnd = NULL, *out;
out = os_malloc(len);
@@ -272,16 +299,17 @@ u8 * eap_peer_tls_derive_key(struct eap_sm *sm, struct eap_ssl_data *data,
return NULL;
/* First, try to use TLS library function for PRF, if available. */
- if (tls_connection_prf(sm->ssl_ctx, data->conn, label, 0, out, len) ==
- 0)
+ if (tls_connection_prf(data->ssl_ctx, data->conn, label, 0, out, len)
+ == 0)
return out;
+#ifndef CONFIG_FIPS
/*
* TLS library did not support key generation, so get the needed TLS
* session parameters and use an internal implementation of TLS PRF to
* derive the key.
*/
- if (tls_connection_get_keys(sm->ssl_ctx, data->conn, &keys))
+ if (tls_connection_get_keys(data->ssl_ctx, data->conn, &keys))
goto fail;
if (keys.client_random == NULL || keys.server_random == NULL ||
@@ -295,15 +323,16 @@ u8 * eap_peer_tls_derive_key(struct eap_sm *sm, struct eap_ssl_data *data,
os_memcpy(rnd + keys.client_random_len, keys.server_random,
keys.server_random_len);
- if (tls_prf(keys.master_key, keys.master_key_len,
- label, rnd, keys.client_random_len +
- keys.server_random_len, out, len))
+ if (tls_prf_sha1_md5(keys.master_key, keys.master_key_len,
+ label, rnd, keys.client_random_len +
+ keys.server_random_len, out, len))
goto fail;
os_free(rnd);
return out;
fail:
+#endif /* CONFIG_FIPS */
os_free(out);
os_free(rnd);
return NULL;
@@ -361,7 +390,8 @@ static int eap_peer_tls_reassemble_fragment(struct eap_ssl_data *data,
eap_peer_tls_reset_input(data);
return -1;
}
- wpabuf_put_buf(data->tls_in, in_data);
+ if (in_data)
+ wpabuf_put_buf(data->tls_in, in_data);
data->tls_in_left -= in_len;
if (data->tls_in_left > 0) {
@@ -447,14 +477,14 @@ static int eap_tls_process_input(struct eap_sm *sm, struct eap_ssl_data *data,
WPA_ASSERT(data->tls_out == NULL);
}
appl_data = NULL;
- data->tls_out = tls_connection_handshake(sm->ssl_ctx, data->conn,
+ data->tls_out = tls_connection_handshake(data->ssl_ctx, data->conn,
msg, &appl_data);
eap_peer_tls_reset_input(data);
if (appl_data &&
- tls_connection_established(sm->ssl_ctx, data->conn) &&
- !tls_connection_get_failed(sm->ssl_ctx, data->conn)) {
+ tls_connection_established(data->ssl_ctx, data->conn) &&
+ !tls_connection_get_failed(data->ssl_ctx, data->conn)) {
wpa_hexdump_buf_key(MSG_MSGDUMP, "SSL: Application data",
appl_data);
*out_data = appl_data;
@@ -520,9 +550,8 @@ static int eap_tls_process_output(struct eap_ssl_data *data, EapType eap_type,
length_included = 1;
}
- *out_data = eap_msg_alloc(EAP_VENDOR_IETF, eap_type,
- 1 + length_included * 4 + len,
- EAP_CODE_RESPONSE, id);
+ *out_data = eap_tls_msg_alloc(eap_type, 1 + length_included * 4 + len,
+ EAP_CODE_RESPONSE, id);
if (*out_data == NULL)
return -1;
@@ -622,7 +651,7 @@ int eap_peer_tls_process_helper(struct eap_sm *sm, struct eap_ssl_data *data,
return -1;
}
- if (tls_connection_get_failed(sm->ssl_ctx, data->conn)) {
+ if (tls_connection_get_failed(data->ssl_ctx, data->conn)) {
/* TLS processing has failed - return error */
wpa_printf(MSG_DEBUG, "SSL: Failed - tls_out available to "
"report error");
@@ -660,8 +689,7 @@ struct wpabuf * eap_peer_tls_build_ack(u8 id, EapType eap_type,
{
struct wpabuf *resp;
- resp = eap_msg_alloc(EAP_VENDOR_IETF, eap_type, 1, EAP_CODE_RESPONSE,
- id);
+ resp = eap_tls_msg_alloc(eap_type, 1, EAP_CODE_RESPONSE, id);
if (resp == NULL)
return NULL;
wpa_printf(MSG_DEBUG, "SSL: Building ACK (type=%d id=%d ver=%d)",
@@ -681,7 +709,7 @@ int eap_peer_tls_reauth_init(struct eap_sm *sm, struct eap_ssl_data *data)
{
eap_peer_tls_reset_input(data);
eap_peer_tls_reset_output(data);
- return tls_connection_shutdown(sm->ssl_ctx, data->conn);
+ return tls_connection_shutdown(data->ssl_ctx, data->conn);
}
@@ -700,7 +728,8 @@ int eap_peer_tls_status(struct eap_sm *sm, struct eap_ssl_data *data,
char name[128];
int len = 0, ret;
- if (tls_get_cipher(sm->ssl_ctx, data->conn, name, sizeof(name)) == 0) {
+ if (tls_get_cipher(data->ssl_ctx, data->conn, name, sizeof(name)) == 0)
+ {
ret = os_snprintf(buf + len, buflen - len,
"EAP TLS cipher=%s\n", name);
if (ret < 0 || (size_t) ret >= buflen - len)
@@ -747,13 +776,19 @@ const u8 * eap_peer_tls_process_init(struct eap_sm *sm,
size_t left;
unsigned int tls_msg_len;
- if (tls_get_errors(sm->ssl_ctx)) {
+ if (tls_get_errors(data->ssl_ctx)) {
wpa_printf(MSG_INFO, "SSL: TLS errors detected");
ret->ignore = TRUE;
return NULL;
}
- pos = eap_hdr_validate(EAP_VENDOR_IETF, eap_type, reqData, &left);
+ if (eap_type == EAP_UNAUTH_TLS_TYPE)
+ pos = eap_hdr_validate(EAP_VENDOR_UNAUTH_TLS,
+ EAP_VENDOR_TYPE_UNAUTH_TLS, reqData,
+ &left);
+ else
+ pos = eap_hdr_validate(EAP_VENDOR_IETF, eap_type, reqData,
+ &left);
if (pos == NULL) {
ret->ignore = TRUE;
return NULL;
@@ -794,6 +829,14 @@ const u8 * eap_peer_tls_process_init(struct eap_sm *sm,
}
pos += 4;
left -= 4;
+
+ if (left > tls_msg_len) {
+ wpa_printf(MSG_INFO, "SSL: TLS Message Length (%d "
+ "bytes) smaller than this fragment (%d "
+ "bytes)", (int) tls_msg_len, (int) left);
+ ret->ignore = TRUE;
+ return NULL;
+ }
}
ret->ignore = FALSE;
@@ -855,7 +898,7 @@ int eap_peer_tls_decrypt(struct eap_sm *sm, struct eap_ssl_data *data,
if (msg == NULL)
return need_more_input ? 1 : -1;
- *in_decrypted = tls_connection_decrypt(sm->ssl_ctx, data->conn, msg);
+ *in_decrypted = tls_connection_decrypt(data->ssl_ctx, data->conn, msg);
eap_peer_tls_reset_input(data);
if (*in_decrypted == NULL) {
wpa_printf(MSG_INFO, "SSL: Failed to decrypt Phase 2 data");
@@ -883,8 +926,8 @@ int eap_peer_tls_encrypt(struct eap_sm *sm, struct eap_ssl_data *data,
{
if (in_data) {
eap_peer_tls_reset_output(data);
- data->tls_out = tls_connection_encrypt(sm->ssl_ctx, data->conn,
- in_data);
+ data->tls_out = tls_connection_encrypt(data->ssl_ctx,
+ data->conn, in_data);
if (data->tls_out == NULL) {
wpa_printf(MSG_INFO, "SSL: Failed to encrypt Phase 2 "
"data (in_len=%lu)",
@@ -949,8 +992,8 @@ int eap_peer_select_phase2_methods(struct eap_peer_config *config,
"method '%s'", start);
} else {
num_methods++;
- _methods = os_realloc(methods,
- num_methods * sizeof(*methods));
+ _methods = os_realloc_array(methods, num_methods,
+ sizeof(*methods));
if (_methods == NULL) {
os_free(methods);
os_free(buf);
diff --git a/contrib/wpa/src/eap_peer/eap_tls_common.h b/contrib/wpa/src/eap_peer/eap_tls_common.h
index e9e0998..91d3a25 100644
--- a/contrib/wpa/src/eap_peer/eap_tls_common.h
+++ b/contrib/wpa/src/eap_peer/eap_tls_common.h
@@ -1,15 +1,9 @@
/*
* EAP peer: EAP-TLS/PEAP/TTLS/FAST common functions
- * Copyright (c) 2004-2009, Jouni Malinen <j@w1.fi>
+ * Copyright (c) 2004-2009, 2012, Jouni Malinen <j@w1.fi>
*
- * This program is free software; you can redistribute it and/or modify
- * it under the terms of the GNU General Public License version 2 as
- * published by the Free Software Foundation.
- *
- * Alternatively, this software may be distributed under the terms of BSD
- * license.
- *
- * See README and COPYING for more details.
+ * This software may be distributed under the terms of the BSD license.
+ * See README for more details.
*/
#ifndef EAP_TLS_COMMON_H
@@ -66,14 +60,19 @@ struct eap_ssl_data {
int include_tls_length;
/**
- * tls_ia - Whether TLS/IA is enabled for this TLS connection
+ * eap - EAP state machine allocated with eap_peer_sm_init()
*/
- int tls_ia;
+ struct eap_sm *eap;
/**
- * eap - EAP state machine allocated with eap_peer_sm_init()
+ * ssl_ctx - TLS library context to use for the connection
*/
- struct eap_sm *eap;
+ void *ssl_ctx;
+
+ /**
+ * eap_type - EAP method used in Phase 1 (EAP_TYPE_TLS/PEAP/TTLS/FAST)
+ */
+ u8 eap_type;
};
@@ -86,9 +85,12 @@ struct eap_ssl_data {
/* could be up to 128 bytes, but only the first 64 bytes are used */
#define EAP_TLS_KEY_LEN 64
+/* dummy type used as a flag for UNAUTH-TLS */
+#define EAP_UNAUTH_TLS_TYPE 255
+
int eap_peer_tls_ssl_init(struct eap_sm *sm, struct eap_ssl_data *data,
- struct eap_peer_config *config);
+ struct eap_peer_config *config, u8 eap_type);
void eap_peer_tls_ssl_deinit(struct eap_sm *sm, struct eap_ssl_data *data);
u8 * eap_peer_tls_derive_key(struct eap_sm *sm, struct eap_ssl_data *data,
const char *label, size_t len);
diff --git a/contrib/wpa/src/eap_peer/eap_tnc.c b/contrib/wpa/src/eap_peer/eap_tnc.c
index 6c95f72..bc13647 100644
--- a/contrib/wpa/src/eap_peer/eap_tnc.c
+++ b/contrib/wpa/src/eap_peer/eap_tnc.c
@@ -2,20 +2,13 @@
* EAP peer method: EAP-TNC (Trusted Network Connect)
* Copyright (c) 2007, Jouni Malinen <j@w1.fi>
*
- * This program is free software; you can redistribute it and/or modify
- * it under the terms of the GNU General Public License version 2 as
- * published by the Free Software Foundation.
- *
- * Alternatively, this software may be distributed under the terms of BSD
- * license.
- *
- * See README and COPYING for more details.
+ * This software may be distributed under the terms of the BSD license.
+ * See README for more details.
*/
#include "includes.h"
#include "common.h"
-#include "base64.h"
#include "eap_i.h"
#include "tncc.h"
diff --git a/contrib/wpa/src/eap_peer/eap_ttls.c b/contrib/wpa/src/eap_peer/eap_ttls.c
index 2573780..9360a42 100644
--- a/contrib/wpa/src/eap_peer/eap_ttls.c
+++ b/contrib/wpa/src/eap_peer/eap_ttls.c
@@ -1,15 +1,9 @@
/*
* EAP peer method: EAP-TTLS (RFC 5281)
- * Copyright (c) 2004-2008, Jouni Malinen <j@w1.fi>
+ * Copyright (c) 2004-2011, Jouni Malinen <j@w1.fi>
*
- * This program is free software; you can redistribute it and/or modify
- * it under the terms of the GNU General Public License version 2 as
- * published by the Free Software Foundation.
- *
- * Alternatively, this software may be distributed under the terms of BSD
- * license.
- *
- * See README and COPYING for more details.
+ * This software may be distributed under the terms of the BSD license.
+ * See README for more details.
*/
#include "includes.h"
@@ -26,17 +20,7 @@
#include "eap_config.h"
-/* Maximum supported TTLS version
- * 0 = RFC 5281
- * 1 = draft-funk-eap-ttls-v1-00.txt
- */
-#ifndef EAP_TTLS_VERSION
-#define EAP_TTLS_VERSION 0 /* TTLSv1 implementation is not yet complete */
-#endif /* EAP_TTLS_VERSION */
-
-
-#define MSCHAPV2_KEY_LEN 16
-#define MSCHAPV2_NT_RESPONSE_LEN 24
+#define EAP_TTLS_VERSION 0
static void eap_ttls_deinit(struct eap_sm *sm, void *priv);
@@ -44,9 +28,8 @@ static void eap_ttls_deinit(struct eap_sm *sm, void *priv);
struct eap_ttls_data {
struct eap_ssl_data ssl;
- int ssl_initialized;
- int ttls_version, force_ttls_version;
+ int ttls_version;
const struct eap_method *phase2_method;
void *phase2_priv;
@@ -91,22 +74,9 @@ static void * eap_ttls_init(struct eap_sm *sm)
if (data == NULL)
return NULL;
data->ttls_version = EAP_TTLS_VERSION;
- data->force_ttls_version = -1;
selected = "EAP";
data->phase2_type = EAP_TTLS_PHASE2_EAP;
-#if EAP_TTLS_VERSION > 0
- if (config && config->phase1) {
- const char *pos = os_strstr(config->phase1, "ttlsver=");
- if (pos) {
- data->force_ttls_version = atoi(pos + 8);
- data->ttls_version = data->force_ttls_version;
- wpa_printf(MSG_DEBUG, "EAP-TTLS: Forced TTLS version "
- "%d", data->force_ttls_version);
- }
- }
-#endif /* EAP_TTLS_VERSION */
-
if (config && config->phase2) {
if (os_strstr(config->phase2, "autheap=")) {
selected = "EAP";
@@ -140,19 +110,11 @@ static void * eap_ttls_init(struct eap_sm *sm)
data->phase2_eap_type.method = EAP_TYPE_NONE;
}
-#if EAP_TTLS_VERSION > 0
- if (!(tls_capabilities(sm->ssl_ctx) & TLS_CAPABILITY_IA) &&
- data->ttls_version > 0) {
- if (data->force_ttls_version > 0) {
- wpa_printf(MSG_INFO, "EAP-TTLS: Forced TTLSv%d and "
- "TLS library does not support TLS/IA.",
- data->force_ttls_version);
- eap_ttls_deinit(sm, data);
- return NULL;
- }
- data->ttls_version = 0;
+ if (eap_peer_tls_ssl_init(sm, &data->ssl, config, EAP_TYPE_TTLS)) {
+ wpa_printf(MSG_INFO, "EAP-TTLS: Failed to initialize SSL.");
+ eap_ttls_deinit(sm, data);
+ return NULL;
}
-#endif /* EAP_TTLS_VERSION */
return data;
}
@@ -176,8 +138,7 @@ static void eap_ttls_deinit(struct eap_sm *sm, void *priv)
return;
eap_ttls_phase2_eap_deinit(sm, data);
os_free(data->phase2_eap_types);
- if (data->ssl_initialized)
- eap_peer_tls_ssl_deinit(sm, &data->ssl);
+ eap_peer_tls_ssl_deinit(sm, &data->ssl);
os_free(data->key_data);
wpabuf_free(data->pending_phase2_req);
os_free(data);
@@ -202,7 +163,7 @@ static u8 * eap_ttls_avp_hdr(u8 *avphdr, u32 avp_code, u32 vendor_id,
}
avp->avp_code = host_to_be32(avp_code);
- avp->avp_length = host_to_be32((flags << 24) | (hdrlen + len));
+ avp->avp_length = host_to_be32((flags << 24) | (u32) (hdrlen + len));
return avphdr + hdrlen;
}
@@ -246,39 +207,6 @@ static int eap_ttls_avp_encapsulate(struct wpabuf **resp, u32 avp_code,
}
-#if EAP_TTLS_VERSION > 0
-static int eap_ttls_ia_permute_inner_secret(struct eap_sm *sm,
- struct eap_ttls_data *data,
- const u8 *key, size_t key_len)
-{
- u8 *buf;
- size_t buf_len;
- int ret;
-
- if (key) {
- buf_len = 2 + key_len;
- buf = os_malloc(buf_len);
- if (buf == NULL)
- return -1;
- WPA_PUT_BE16(buf, key_len);
- os_memcpy(buf + 2, key, key_len);
- } else {
- buf = NULL;
- buf_len = 0;
- }
-
- wpa_hexdump_key(MSG_DEBUG, "EAP-TTLS: Session keys for TLS/IA inner "
- "secret permutation", buf, buf_len);
- ret = tls_connection_ia_permute_inner_secret(sm->ssl_ctx,
- data->ssl.conn,
- buf, buf_len);
- os_free(buf);
-
- return ret;
-}
-#endif /* EAP_TTLS_VERSION */
-
-
static int eap_ttls_v0_derive_key(struct eap_sm *sm,
struct eap_ttls_data *data)
{
@@ -298,156 +226,10 @@ static int eap_ttls_v0_derive_key(struct eap_sm *sm,
}
-#if EAP_TTLS_VERSION > 0
-static int eap_ttls_v1_derive_key(struct eap_sm *sm,
- struct eap_ttls_data *data)
-{
- struct tls_keys keys;
- u8 *rnd;
-
- os_free(data->key_data);
- data->key_data = NULL;
-
- os_memset(&keys, 0, sizeof(keys));
- if (tls_connection_get_keys(sm->ssl_ctx, data->ssl.conn, &keys) ||
- keys.client_random == NULL || keys.server_random == NULL ||
- keys.inner_secret == NULL) {
- wpa_printf(MSG_INFO, "EAP-TTLS: Could not get inner secret, "
- "client random, or server random to derive keying "
- "material");
- return -1;
- }
-
- rnd = os_malloc(keys.client_random_len + keys.server_random_len);
- data->key_data = os_malloc(EAP_TLS_KEY_LEN);
- if (rnd == NULL || data->key_data == NULL) {
- wpa_printf(MSG_INFO, "EAP-TTLS: No memory for key derivation");
- os_free(rnd);
- os_free(data->key_data);
- data->key_data = NULL;
- return -1;
- }
- os_memcpy(rnd, keys.client_random, keys.client_random_len);
- os_memcpy(rnd + keys.client_random_len, keys.server_random,
- keys.server_random_len);
-
- if (tls_prf(keys.inner_secret, keys.inner_secret_len,
- "ttls v1 keying material", rnd, keys.client_random_len +
- keys.server_random_len, data->key_data, EAP_TLS_KEY_LEN)) {
- wpa_printf(MSG_DEBUG, "EAP-TTLS: Failed to derive key");
- os_free(rnd);
- os_free(data->key_data);
- data->key_data = NULL;
- return -1;
- }
-
- wpa_hexdump(MSG_DEBUG, "EAP-TTLS: client/server random",
- rnd, keys.client_random_len + keys.server_random_len);
- wpa_hexdump_key(MSG_DEBUG, "EAP-TTLS: TLS/IA inner secret",
- keys.inner_secret, keys.inner_secret_len);
-
- os_free(rnd);
-
- wpa_hexdump_key(MSG_DEBUG, "EAP-TTLS: Derived key",
- data->key_data, EAP_TLS_KEY_LEN);
-
- return 0;
-}
-#endif /* EAP_TTLS_VERSION */
-
-
static u8 * eap_ttls_implicit_challenge(struct eap_sm *sm,
struct eap_ttls_data *data, size_t len)
{
-#if EAP_TTLS_VERSION > 0
- struct tls_keys keys;
- u8 *challenge, *rnd;
-#endif /* EAP_TTLS_VERSION */
-
- if (data->ttls_version == 0) {
- return eap_peer_tls_derive_key(sm, &data->ssl,
- "ttls challenge", len);
- }
-
-#if EAP_TTLS_VERSION > 0
-
- os_memset(&keys, 0, sizeof(keys));
- if (tls_connection_get_keys(sm->ssl_ctx, data->ssl.conn, &keys) ||
- keys.client_random == NULL || keys.server_random == NULL ||
- keys.inner_secret == NULL) {
- wpa_printf(MSG_INFO, "EAP-TTLS: Could not get inner secret, "
- "client random, or server random to derive "
- "implicit challenge");
- return NULL;
- }
-
- rnd = os_malloc(keys.client_random_len + keys.server_random_len);
- challenge = os_malloc(len);
- if (rnd == NULL || challenge == NULL) {
- wpa_printf(MSG_INFO, "EAP-TTLS: No memory for implicit "
- "challenge derivation");
- os_free(rnd);
- os_free(challenge);
- return NULL;
- }
- os_memcpy(rnd, keys.server_random, keys.server_random_len);
- os_memcpy(rnd + keys.server_random_len, keys.client_random,
- keys.client_random_len);
-
- if (tls_prf(keys.inner_secret, keys.inner_secret_len,
- "inner application challenge", rnd,
- keys.client_random_len + keys.server_random_len,
- challenge, len)) {
- wpa_printf(MSG_DEBUG, "EAP-TTLS: Failed to derive implicit "
- "challenge");
- os_free(rnd);
- os_free(challenge);
- return NULL;
- }
-
- os_free(rnd);
-
- wpa_hexdump_key(MSG_DEBUG, "EAP-TTLS: Derived implicit challenge",
- challenge, len);
-
- return challenge;
-
-#else /* EAP_TTLS_VERSION */
-
- return NULL;
-
-#endif /* EAP_TTLS_VERSION */
-}
-
-
-static void eap_ttlsv1_phase2_eap_finish(struct eap_sm *sm,
- struct eap_ttls_data *data,
- struct eap_method_ret *ret)
-{
-#if EAP_TTLS_VERSION > 0
- if (data->ttls_version > 0) {
- const struct eap_method *m = data->phase2_method;
- void *priv = data->phase2_priv;
-
- /* TTLSv1 requires TLS/IA FinalPhaseFinished */
- if (ret->decision == DECISION_UNCOND_SUCC)
- ret->decision = DECISION_COND_SUCC;
- ret->methodState = METHOD_CONT;
-
- if (ret->decision == DECISION_COND_SUCC &&
- m->isKeyAvailable && m->getKey &&
- m->isKeyAvailable(sm, priv)) {
- u8 *key;
- size_t key_len;
- key = m->getKey(sm, priv, &key_len);
- if (key) {
- eap_ttls_ia_permute_inner_secret(
- sm, data, key, key_len);
- os_free(key);
- }
- }
- }
-#endif /* EAP_TTLS_VERSION */
+ return eap_peer_tls_derive_key(sm, &data->ssl, "ttls challenge", len);
}
@@ -494,7 +276,6 @@ static int eap_ttls_phase2_eap_process(struct eap_sm *sm,
ret->methodState = iret.methodState;
ret->decision = iret.decision;
}
- eap_ttlsv1_phase2_eap_finish(sm, data, ret);
return 0;
}
@@ -615,31 +396,12 @@ static int eap_ttls_phase2_request_eap(struct eap_sm *sm,
}
-static void eap_ttlsv1_permute_inner(struct eap_sm *sm,
- struct eap_ttls_data *data)
-{
-#if EAP_TTLS_VERSION > 0
- u8 session_key[2 * MSCHAPV2_KEY_LEN];
-
- if (data->ttls_version == 0)
- return;
-
- get_asymetric_start_key(data->master_key, session_key,
- MSCHAPV2_KEY_LEN, 0, 0);
- get_asymetric_start_key(data->master_key,
- session_key + MSCHAPV2_KEY_LEN,
- MSCHAPV2_KEY_LEN, 1, 0);
- eap_ttls_ia_permute_inner_secret(sm, data, session_key,
- sizeof(session_key));
-#endif /* EAP_TTLS_VERSION */
-}
-
-
static int eap_ttls_phase2_request_mschapv2(struct eap_sm *sm,
struct eap_ttls_data *data,
struct eap_method_ret *ret,
struct wpabuf **resp)
{
+#ifdef EAP_MSCHAPv2
struct wpabuf *msg;
u8 *buf, *pos, *challenge, *peer_challenge;
const u8 *identity, *password;
@@ -674,7 +436,6 @@ static int eap_ttls_phase2_request_mschapv2(struct eap_sm *sm,
"implicit challenge");
return -1;
}
- peer_challenge = challenge + 1 + EAP_TTLS_MSCHAPV2_CHALLENGE_LEN;
pos = eap_ttls_avp_add(buf, pos, RADIUS_ATTR_MS_CHAP_CHALLENGE,
RADIUS_VENDOR_ID_MICROSOFT, 1,
@@ -687,7 +448,14 @@ static int eap_ttls_phase2_request_mschapv2(struct eap_sm *sm,
data->ident = challenge[EAP_TTLS_MSCHAPV2_CHALLENGE_LEN];
*pos++ = data->ident;
*pos++ = 0; /* Flags */
- os_memcpy(pos, peer_challenge, EAP_TTLS_MSCHAPV2_CHALLENGE_LEN);
+ if (os_get_random(pos, EAP_TTLS_MSCHAPV2_CHALLENGE_LEN) < 0) {
+ os_free(challenge);
+ wpabuf_free(msg);
+ wpa_printf(MSG_ERROR, "EAP-TTLS/MSCHAPV2: Failed to get "
+ "random data for peer challenge");
+ return -1;
+ }
+ peer_challenge = pos;
pos += EAP_TTLS_MSCHAPV2_CHALLENGE_LEN;
os_memset(pos, 0, 8); /* Reserved, must be zero */
pos += 8;
@@ -695,6 +463,7 @@ static int eap_ttls_phase2_request_mschapv2(struct eap_sm *sm,
password_len, pwhash, challenge,
peer_challenge, pos, data->auth_response,
data->master_key)) {
+ os_free(challenge);
wpabuf_free(msg);
wpa_printf(MSG_ERROR, "EAP-TTLS/MSCHAPV2: Failed to derive "
"response");
@@ -702,8 +471,6 @@ static int eap_ttls_phase2_request_mschapv2(struct eap_sm *sm,
}
data->auth_response_valid = 1;
- eap_ttlsv1_permute_inner(sm, data);
-
pos += 24;
os_free(challenge);
AVP_PAD(buf, pos);
@@ -711,7 +478,7 @@ static int eap_ttls_phase2_request_mschapv2(struct eap_sm *sm,
wpabuf_put(msg, pos - buf);
*resp = msg;
- if (sm->workaround && data->ttls_version == 0) {
+ if (sm->workaround) {
/* At least FreeRADIUS seems to be terminating
* EAP-TTLS/MSHCAPV2 without the expected MS-CHAP-v2 Success
* packet. */
@@ -722,6 +489,10 @@ static int eap_ttls_phase2_request_mschapv2(struct eap_sm *sm,
}
return 0;
+#else /* EAP_MSCHAPv2 */
+ wpa_printf(MSG_ERROR, "EAP-TTLS: MSCHAPv2 not included in the build");
+ return -1;
+#endif /* EAP_MSCHAPv2 */
}
@@ -798,17 +569,10 @@ static int eap_ttls_phase2_request_mschap(struct eap_sm *sm,
wpabuf_put(msg, pos - buf);
*resp = msg;
- if (data->ttls_version > 0) {
- /* EAP-TTLSv1 uses TLS/IA FinalPhaseFinished to report success,
- * so do not allow connection to be terminated yet. */
- ret->methodState = METHOD_CONT;
- ret->decision = DECISION_COND_SUCC;
- } else {
- /* EAP-TTLS/MSCHAP does not provide tunneled success
- * notification, so assume that Phase2 succeeds. */
- ret->methodState = METHOD_DONE;
- ret->decision = DECISION_COND_SUCC;
- }
+ /* EAP-TTLS/MSCHAP does not provide tunneled success
+ * notification, so assume that Phase2 succeeds. */
+ ret->methodState = METHOD_DONE;
+ ret->decision = DECISION_COND_SUCC;
return 0;
}
@@ -859,17 +623,10 @@ static int eap_ttls_phase2_request_pap(struct eap_sm *sm,
wpabuf_put(msg, pos - buf);
*resp = msg;
- if (data->ttls_version > 0) {
- /* EAP-TTLSv1 uses TLS/IA FinalPhaseFinished to report success,
- * so do not allow connection to be terminated yet. */
- ret->methodState = METHOD_CONT;
- ret->decision = DECISION_COND_SUCC;
- } else {
- /* EAP-TTLS/PAP does not provide tunneled success notification,
- * so assume that Phase2 succeeds. */
- ret->methodState = METHOD_DONE;
- ret->decision = DECISION_COND_SUCC;
- }
+ /* EAP-TTLS/PAP does not provide tunneled success notification,
+ * so assume that Phase2 succeeds. */
+ ret->methodState = METHOD_DONE;
+ ret->decision = DECISION_COND_SUCC;
return 0;
}
@@ -942,17 +699,10 @@ static int eap_ttls_phase2_request_chap(struct eap_sm *sm,
wpabuf_put(msg, pos - buf);
*resp = msg;
- if (data->ttls_version > 0) {
- /* EAP-TTLSv1 uses TLS/IA FinalPhaseFinished to report success,
- * so do not allow connection to be terminated yet. */
- ret->methodState = METHOD_CONT;
- ret->decision = DECISION_COND_SUCC;
- } else {
- /* EAP-TTLS/CHAP does not provide tunneled success
- * notification, so assume that Phase2 succeeds. */
- ret->methodState = METHOD_DONE;
- ret->decision = DECISION_COND_SUCC;
- }
+ /* EAP-TTLS/CHAP does not provide tunneled success
+ * notification, so assume that Phase2 succeeds. */
+ ret->methodState = METHOD_DONE;
+ ret->decision = DECISION_COND_SUCC;
return 0;
}
@@ -1027,36 +777,6 @@ static int eap_ttls_phase2_request(struct eap_sm *sm,
}
-#if EAP_TTLS_VERSION > 0
-static struct wpabuf * eap_ttls_build_phase_finished(
- struct eap_sm *sm, struct eap_ttls_data *data, int id, int final)
-{
- struct wpabuf *req, *buf;
-
- buf = tls_connection_ia_send_phase_finished(sm->ssl_ctx,
- data->ssl.conn,
- final);
- if (buf == NULL)
- return NULL;
-
- req = eap_msg_alloc(EAP_VENDOR_IETF, EAP_TYPE_TTLS,
- 1 + wpabuf_len(buf),
- EAP_CODE_RESPONSE, id);
- if (req == NULL) {
- wpabuf_free(buf);
- return NULL;
- }
-
- wpabuf_put_u8(req, data->ttls_version);
- wpabuf_put_buf(req, buf);
- wpabuf_free(buf);
- eap_update_len(req);
-
- return req;
-}
-#endif /* EAP_TTLS_VERSION */
-
-
struct ttls_parse_avp {
u8 *mschapv2;
u8 *eapdata;
@@ -1327,6 +1047,7 @@ static int eap_ttls_process_phase2_mschapv2(struct eap_sm *sm,
struct eap_method_ret *ret,
struct ttls_parse_avp *parse)
{
+#ifdef EAP_MSCHAPv2
if (parse->mschapv2_error) {
wpa_printf(MSG_DEBUG, "EAP-TTLS/MSCHAPV2: Received "
"MS-CHAP-Error - failed");
@@ -1366,25 +1087,19 @@ static int eap_ttls_process_phase2_mschapv2(struct eap_sm *sm,
wpa_printf(MSG_INFO, "EAP-TTLS: Phase 2 MSCHAPV2 "
"authentication succeeded");
- if (data->ttls_version > 0) {
- /*
- * EAP-TTLSv1 uses TLS/IA FinalPhaseFinished to report
- * success, so do not allow connection to be terminated
- * yet.
- */
- ret->methodState = METHOD_CONT;
- ret->decision = DECISION_COND_SUCC;
- } else {
- ret->methodState = METHOD_DONE;
- ret->decision = DECISION_UNCOND_SUCC;
- data->phase2_success = 1;
- }
+ ret->methodState = METHOD_DONE;
+ ret->decision = DECISION_UNCOND_SUCC;
+ data->phase2_success = 1;
/*
* Reply with empty data; authentication server will reply
* with EAP-Success after this.
*/
return 1;
+#else /* EAP_MSCHAPv2 */
+ wpa_printf(MSG_ERROR, "EAP-TTLS: MSCHAPv2 not included in the build");
+ return -1;
+#endif /* EAP_MSCHAPv2 */
}
@@ -1493,24 +1208,6 @@ static int eap_ttls_process_decrypted(struct eap_sm *sm,
}
-#if EAP_TTLS_VERSION > 0
-static void eap_ttls_final_phase_finished(struct eap_sm *sm,
- struct eap_ttls_data *data,
- struct eap_method_ret *ret,
- u8 identifier,
- struct wpabuf **out_data)
-{
- wpa_printf(MSG_DEBUG, "EAP-TTLS: FinalPhaseFinished received");
- wpa_printf(MSG_INFO, "EAP-TTLS: TLS/IA authentication succeeded");
- ret->methodState = METHOD_DONE;
- ret->decision = DECISION_UNCOND_SUCC;
- data->phase2_success = 1;
- *out_data = eap_ttls_build_phase_finished(sm, data, identifier, 1);
- eap_ttls_v1_derive_key(sm, data);
-}
-#endif /* EAP_TTLS_VERSION */
-
-
static int eap_ttls_implicit_identity_request(struct eap_sm *sm,
struct eap_ttls_data *data,
struct eap_method_ret *ret,
@@ -1534,6 +1231,21 @@ static int eap_ttls_implicit_identity_request(struct eap_sm *sm,
"processing failed");
retval = -1;
} else {
+ struct eap_peer_config *config = eap_get_config(sm);
+ if (resp == NULL &&
+ (config->pending_req_identity ||
+ config->pending_req_password ||
+ config->pending_req_otp ||
+ config->pending_req_new_password)) {
+ /*
+ * Use empty buffer to force implicit request
+ * processing when EAP request is re-processed after
+ * user input.
+ */
+ wpabuf_free(data->pending_phase2_req);
+ data->pending_phase2_req = wpabuf_alloc(0);
+ }
+
retval = eap_ttls_encrypt_response(sm, data, resp, identifier,
out_data);
}
@@ -1627,17 +1339,6 @@ static int eap_ttls_decrypt(struct eap_sm *sm, struct eap_ttls_data *data,
if (retval)
goto done;
-#if EAP_TTLS_VERSION > 0
- if (data->ttls_version > 0 &&
- (in_decrypted == NULL || wpabuf_len(in_decrypted) == 0) &&
- tls_connection_ia_final_phase_finished(sm->ssl_ctx,
- data->ssl.conn)) {
- eap_ttls_final_phase_finished(sm, data, ret, identifier,
- out_data);
- goto done;
- }
-#endif /* EAP_TTLS_VERSION */
-
continue_req:
data->phase2_start = 0;
@@ -1662,46 +1363,6 @@ done:
}
-static int eap_ttls_process_start(struct eap_sm *sm,
- struct eap_ttls_data *data, u8 flags,
- struct eap_method_ret *ret)
-{
- struct eap_peer_config *config = eap_get_config(sm);
-
- wpa_printf(MSG_DEBUG, "EAP-TTLS: Start (server ver=%d, own ver=%d)",
- flags & EAP_TLS_VERSION_MASK, data->ttls_version);
-#if EAP_TTLS_VERSION > 0
- if ((flags & EAP_TLS_VERSION_MASK) < data->ttls_version)
- data->ttls_version = flags & EAP_TLS_VERSION_MASK;
- if (data->force_ttls_version >= 0 &&
- data->force_ttls_version != data->ttls_version) {
- wpa_printf(MSG_WARNING, "EAP-TTLS: Failed to select "
- "forced TTLS version %d",
- data->force_ttls_version);
- ret->methodState = METHOD_DONE;
- ret->decision = DECISION_FAIL;
- ret->allowNotifications = FALSE;
- return -1;
- }
- wpa_printf(MSG_DEBUG, "EAP-TTLS: Using TTLS version %d",
- data->ttls_version);
-
- if (data->ttls_version > 0)
- data->ssl.tls_ia = 1;
-#endif /* EAP_TTLS_VERSION */
- if (!data->ssl_initialized &&
- eap_peer_tls_ssl_init(sm, &data->ssl, config)) {
- wpa_printf(MSG_INFO, "EAP-TTLS: Failed to initialize SSL.");
- return -1;
- }
- data->ssl_initialized = 1;
-
- wpa_printf(MSG_DEBUG, "EAP-TTLS: Start");
-
- return 0;
-}
-
-
static int eap_ttls_process_handshake(struct eap_sm *sm,
struct eap_ttls_data *data,
struct eap_method_ret *ret,
@@ -1725,8 +1386,7 @@ static int eap_ttls_process_handshake(struct eap_sm *sm,
ret->methodState = METHOD_MAY_CONT;
}
data->phase2_start = 1;
- if (data->ttls_version == 0)
- eap_ttls_v0_derive_key(sm, data);
+ eap_ttls_v0_derive_key(sm, data);
if (*out_data == NULL || wpabuf_len(*out_data) == 0) {
if (eap_ttls_decrypt(sm, data, ret, identifier,
@@ -1761,7 +1421,7 @@ static void eap_ttls_check_auth_status(struct eap_sm *sm,
struct eap_ttls_data *data,
struct eap_method_ret *ret)
{
- if (data->ttls_version == 0 && ret->methodState == METHOD_DONE) {
+ if (ret->methodState == METHOD_DONE) {
ret->allowNotifications = FALSE;
if (ret->decision == DECISION_UNCOND_SUCC ||
ret->decision == DECISION_COND_SUCC) {
@@ -1779,8 +1439,7 @@ static void eap_ttls_check_auth_status(struct eap_sm *sm,
}
#endif /* EAP_TNC */
}
- } else if (data->ttls_version == 0 &&
- ret->methodState == METHOD_MAY_CONT &&
+ } else if (ret->methodState == METHOD_MAY_CONT &&
(ret->decision == DECISION_UNCOND_SUCC ||
ret->decision == DECISION_COND_SUCC)) {
wpa_printf(MSG_DEBUG, "EAP-TTLS: Authentication "
@@ -1808,8 +1467,9 @@ static struct wpabuf * eap_ttls_process(struct eap_sm *sm, void *priv,
id = eap_get_id(reqData);
if (flags & EAP_TLS_FLAGS_START) {
- if (eap_ttls_process_start(sm, data, flags, ret) < 0)
- return NULL;
+ wpa_printf(MSG_DEBUG, "EAP-TTLS: Start (server ver=%d, own "
+ "ver=%d)", flags & EAP_TLS_VERSION_MASK,
+ data->ttls_version);
/* RFC 5281, Ch. 9.2:
* "This packet MAY contain additional information in the form
@@ -1817,13 +1477,6 @@ static struct wpabuf * eap_ttls_process(struct eap_sm *sm, void *priv,
* For now, ignore any potential extra data.
*/
left = 0;
- } else if (!data->ssl_initialized) {
- wpa_printf(MSG_DEBUG, "EAP-TTLS: First message did not "
- "include Start flag");
- ret->methodState = METHOD_DONE;
- ret->decision = DECISION_FAIL;
- ret->allowNotifications = FALSE;
- return NULL;
}
resp = NULL;
diff --git a/contrib/wpa/src/eap_peer/eap_vendor_test.c b/contrib/wpa/src/eap_peer/eap_vendor_test.c
index 3e114c1..040d1e7 100644
--- a/contrib/wpa/src/eap_peer/eap_vendor_test.c
+++ b/contrib/wpa/src/eap_peer/eap_vendor_test.c
@@ -2,14 +2,8 @@
* EAP peer method: Test method for vendor specific (expanded) EAP type
* Copyright (c) 2005-2006, Jouni Malinen <j@w1.fi>
*
- * This program is free software; you can redistribute it and/or modify
- * it under the terms of the GNU General Public License version 2 as
- * published by the Free Software Foundation.
- *
- * Alternatively, this software may be distributed under the terms of BSD
- * license.
- *
- * See README and COPYING for more details.
+ * This software may be distributed under the terms of the BSD license.
+ * See README for more details.
*
* This file implements a vendor specific test method using EAP expanded types.
* This is only for test use and must not be used for authentication since no
@@ -25,7 +19,7 @@
#endif /* TEST_PENDING_REQUEST */
-#define EAP_VENDOR_ID 0xfffefd
+#define EAP_VENDOR_ID EAP_VENDOR_HOSTAP
#define EAP_VENDOR_TYPE 0xfcfbfaf9
diff --git a/contrib/wpa/src/eap_peer/eap_wsc.c b/contrib/wpa/src/eap_peer/eap_wsc.c
index 8317f72..d007a57 100644
--- a/contrib/wpa/src/eap_peer/eap_wsc.c
+++ b/contrib/wpa/src/eap_peer/eap_wsc.c
@@ -1,15 +1,9 @@
/*
* EAP-WSC peer for Wi-Fi Protected Setup
- * Copyright (c) 2007-2009, Jouni Malinen <j@w1.fi>
+ * Copyright (c) 2007-2009, 2012, Jouni Malinen <j@w1.fi>
*
- * This program is free software; you can redistribute it and/or modify
- * it under the terms of the GNU General Public License version 2 as
- * published by the Free Software Foundation.
- *
- * Alternatively, this software may be distributed under the terms of BSD
- * license.
- *
- * See README and COPYING for more details.
+ * This software may be distributed under the terms of the BSD license.
+ * See README for more details.
*/
#include "includes.h"
@@ -143,6 +137,8 @@ static void * eap_wsc_init(struct eap_sm *sm)
struct wps_context *wps;
struct wps_credential new_ap_settings;
int res;
+ u8 dev_pw[WPS_OOB_DEVICE_PASSWORD_LEN];
+ int nfc = 0;
wps = sm->wps;
if (wps == NULL) {
@@ -190,19 +186,36 @@ static void * eap_wsc_init(struct eap_sm *sm)
while (*pos != '\0' && *pos != ' ')
pos++;
cfg.pin_len = pos - (const char *) cfg.pin;
+ if (cfg.pin_len >= WPS_OOB_DEVICE_PASSWORD_MIN_LEN * 2 &&
+ cfg.pin_len <= WPS_OOB_DEVICE_PASSWORD_LEN * 2 &&
+ hexstr2bin((const char *) cfg.pin, dev_pw,
+ cfg.pin_len / 2) == 0) {
+ /* Convert OOB Device Password to binary */
+ cfg.pin = dev_pw;
+ cfg.pin_len /= 2;
+ }
+ if (cfg.pin_len == 6 && os_strncmp(pos, "nfc-pw", 6) == 0) {
+ cfg.pin = NULL;
+ cfg.pin_len = 0;
+ nfc = 1;
+ }
} else {
pos = os_strstr(phase1, "pbc=1");
if (pos)
cfg.pbc = 1;
}
- if (cfg.pin == NULL && !cfg.pbc) {
+ if (cfg.pin == NULL && !cfg.pbc && !nfc) {
wpa_printf(MSG_INFO, "EAP-WSC: PIN or PBC not set in phase1 "
"configuration data");
os_free(data);
return NULL;
}
+ pos = os_strstr(phase1, "dev_pw_id=");
+ if (pos && cfg.pin)
+ cfg.dev_pw_id = atoi(pos + 10);
+
res = eap_wsc_new_ap_settings(&new_ap_settings, phase1);
if (res < 0) {
os_free(data);
@@ -219,10 +232,16 @@ static void * eap_wsc_init(struct eap_sm *sm)
os_free(data);
return NULL;
}
- data->fragment_size = WSC_FRAGMENT_SIZE;
+ res = eap_get_config_fragment_size(sm);
+ if (res > 0)
+ data->fragment_size = res;
+ else
+ data->fragment_size = WSC_FRAGMENT_SIZE;
+ wpa_printf(MSG_DEBUG, "EAP-WSC: Fragment size limit %u",
+ (unsigned int) data->fragment_size);
if (registrar && cfg.pin) {
- wps_registrar_add_pin(data->wps_ctx->registrar, NULL,
+ wps_registrar_add_pin(data->wps_ctx->registrar, NULL, NULL,
cfg.pin, cfg.pin_len, 0);
}
diff --git a/contrib/wpa/src/eap_peer/ikev2.c b/contrib/wpa/src/eap_peer/ikev2.c
index 309a331..fcf4712 100644
--- a/contrib/wpa/src/eap_peer/ikev2.c
+++ b/contrib/wpa/src/eap_peer/ikev2.c
@@ -2,20 +2,15 @@
* IKEv2 responder (RFC 4306) for EAP-IKEV2
* Copyright (c) 2007, Jouni Malinen <j@w1.fi>
*
- * This program is free software; you can redistribute it and/or modify
- * it under the terms of the GNU General Public License version 2 as
- * published by the Free Software Foundation.
- *
- * Alternatively, this software may be distributed under the terms of BSD
- * license.
- *
- * See README and COPYING for more details.
+ * This software may be distributed under the terms of the BSD license.
+ * See README for more details.
*/
#include "includes.h"
#include "common.h"
#include "crypto/dh_groups.h"
+#include "crypto/random.h"
#include "ikev2.h"
@@ -424,7 +419,7 @@ static int ikev2_process_kei(struct ikev2_responder_data *data,
}
/* RFC 4306, Section 3.4:
- * The length of DH public value MUST be equal to the lenght of the
+ * The length of DH public value MUST be equal to the length of the
* prime modulus.
*/
if (kei_len - 4 != data->dh->prime_len) {
@@ -1133,7 +1128,7 @@ static struct wpabuf * ikev2_build_sa_init(struct ikev2_responder_data *data)
data->r_spi, IKEV2_SPI_LEN);
data->r_nonce_len = IKEV2_NONCE_MIN_LEN;
- if (os_get_random(data->r_nonce, data->r_nonce_len))
+ if (random_get_bytes(data->r_nonce, data->r_nonce_len))
return NULL;
#ifdef CCNS_PL
/* Zeros are removed incorrectly from the beginning of the nonces in
diff --git a/contrib/wpa/src/eap_peer/ikev2.h b/contrib/wpa/src/eap_peer/ikev2.h
index 9ca0ca5..627a2cb 100644
--- a/contrib/wpa/src/eap_peer/ikev2.h
+++ b/contrib/wpa/src/eap_peer/ikev2.h
@@ -2,14 +2,8 @@
* IKEv2 responder (RFC 4306) for EAP-IKEV2
* Copyright (c) 2007, Jouni Malinen <j@w1.fi>
*
- * This program is free software; you can redistribute it and/or modify
- * it under the terms of the GNU General Public License version 2 as
- * published by the Free Software Foundation.
- *
- * Alternatively, this software may be distributed under the terms of BSD
- * license.
- *
- * See README and COPYING for more details.
+ * This software may be distributed under the terms of the BSD license.
+ * See README for more details.
*/
#ifndef IKEV2_H
diff --git a/contrib/wpa/src/eap_peer/mschapv2.c b/contrib/wpa/src/eap_peer/mschapv2.c
index b8fb075..37e6735 100644
--- a/contrib/wpa/src/eap_peer/mschapv2.c
+++ b/contrib/wpa/src/eap_peer/mschapv2.c
@@ -2,14 +2,8 @@
* MSCHAPV2 (RFC 2759)
* Copyright (c) 2004-2008, Jouni Malinen <j@w1.fi>
*
- * This program is free software; you can redistribute it and/or modify
- * it under the terms of the GNU General Public License version 2 as
- * published by the Free Software Foundation.
- *
- * Alternatively, this software may be distributed under the terms of BSD
- * license.
- *
- * See README and COPYING for more details.
+ * This software may be distributed under the terms of the BSD license.
+ * See README for more details.
*/
#include "includes.h"
@@ -69,22 +63,28 @@ int mschapv2_derive_response(const u8 *identity, size_t identity_len,
if (pwhash) {
wpa_hexdump_key(MSG_DEBUG, "MSCHAPV2: password hash",
password, password_len);
- generate_nt_response_pwhash(auth_challenge, peer_challenge,
- username, username_len,
- password, nt_response);
- generate_authenticator_response_pwhash(
- password, peer_challenge, auth_challenge,
- username, username_len, nt_response, auth_response);
+ if (generate_nt_response_pwhash(auth_challenge, peer_challenge,
+ username, username_len,
+ password, nt_response) ||
+ generate_authenticator_response_pwhash(
+ password, peer_challenge, auth_challenge,
+ username, username_len, nt_response,
+ auth_response))
+ return -1;
} else {
wpa_hexdump_ascii_key(MSG_DEBUG, "MSCHAPV2: password",
password, password_len);
- generate_nt_response(auth_challenge, peer_challenge,
- username, username_len,
- password, password_len, nt_response);
- generate_authenticator_response(password, password_len,
- peer_challenge, auth_challenge,
- username, username_len,
- nt_response, auth_response);
+ if (generate_nt_response(auth_challenge, peer_challenge,
+ username, username_len,
+ password, password_len,
+ nt_response) ||
+ generate_authenticator_response(password, password_len,
+ peer_challenge,
+ auth_challenge,
+ username, username_len,
+ nt_response,
+ auth_response))
+ return -1;
}
wpa_hexdump(MSG_DEBUG, "MSCHAPV2: NT Response",
nt_response, MSCHAPV2_NT_RESPONSE_LEN);
@@ -100,7 +100,8 @@ int mschapv2_derive_response(const u8 *identity, size_t identity_len,
hash_nt_password_hash(password_hash, password_hash_hash))
return -1;
}
- get_master_key(password_hash_hash, nt_response, master_key);
+ if (get_master_key(password_hash_hash, nt_response, master_key))
+ return -1;
wpa_hexdump_key(MSG_DEBUG, "MSCHAPV2: Master Key",
master_key, MSCHAPV2_MASTER_KEY_LEN);
diff --git a/contrib/wpa/src/eap_peer/mschapv2.h b/contrib/wpa/src/eap_peer/mschapv2.h
index 90dad31..edd458b 100644
--- a/contrib/wpa/src/eap_peer/mschapv2.h
+++ b/contrib/wpa/src/eap_peer/mschapv2.h
@@ -2,14 +2,8 @@
* MSCHAPV2 (RFC 2759)
* Copyright (c) 2004-2008, Jouni Malinen <j@w1.fi>
*
- * This program is free software; you can redistribute it and/or modify
- * it under the terms of the GNU General Public License version 2 as
- * published by the Free Software Foundation.
- *
- * Alternatively, this software may be distributed under the terms of BSD
- * license.
- *
- * See README and COPYING for more details.
+ * This software may be distributed under the terms of the BSD license.
+ * See README for more details.
*/
#ifndef MSCHAPV2_H
diff --git a/contrib/wpa/src/eap_peer/tncc.c b/contrib/wpa/src/eap_peer/tncc.c
index eaaa168..f5edfd5 100644
--- a/contrib/wpa/src/eap_peer/tncc.c
+++ b/contrib/wpa/src/eap_peer/tncc.c
@@ -2,14 +2,8 @@
* EAP-TNC - TNCC (IF-IMC and IF-TNCCS)
* Copyright (c) 2007, Jouni Malinen <j@w1.fi>
*
- * This program is free software; you can redistribute it and/or modify
- * it under the terms of the GNU General Public License version 2 as
- * published by the Free Software Foundation.
- *
- * Alternatively, this software may be distributed under the terms of BSD
- * license.
- *
- * See README and COPYING for more details.
+ * This software may be distributed under the terms of the BSD license.
+ * See README for more details.
*/
#include "includes.h"
@@ -180,11 +174,11 @@ TNC_Result TNC_TNCC_ReportMessageTypes(
imc = tnc_imc[imcID];
os_free(imc->supported_types);
imc->supported_types =
- os_malloc(typeCount * sizeof(TNC_MessageTypeList));
+ os_malloc(typeCount * sizeof(TNC_MessageType));
if (imc->supported_types == NULL)
return TNC_RESULT_FATAL;
os_memcpy(imc->supported_types, supportedTypes,
- typeCount * sizeof(TNC_MessageTypeList));
+ typeCount * sizeof(TNC_MessageType));
imc->num_supported_types = typeCount;
return TNC_RESULT_SUCCESS;
diff --git a/contrib/wpa/src/eap_peer/tncc.h b/contrib/wpa/src/eap_peer/tncc.h
index 4d42a05..df2a287 100644
--- a/contrib/wpa/src/eap_peer/tncc.h
+++ b/contrib/wpa/src/eap_peer/tncc.h
@@ -2,14 +2,8 @@
* EAP-TNC - TNCC (IF-IMC and IF-TNCCS)
* Copyright (c) 2007, Jouni Malinen <j@w1.fi>
*
- * This program is free software; you can redistribute it and/or modify
- * it under the terms of the GNU General Public License version 2 as
- * published by the Free Software Foundation.
- *
- * Alternatively, this software may be distributed under the terms of BSD
- * license.
- *
- * See README and COPYING for more details.
+ * This software may be distributed under the terms of the BSD license.
+ * See README for more details.
*/
#ifndef TNCC_H
diff --git a/contrib/wpa/src/eap_server/Makefile b/contrib/wpa/src/eap_server/Makefile
deleted file mode 100644
index 9c41962..0000000
--- a/contrib/wpa/src/eap_server/Makefile
+++ /dev/null
@@ -1,8 +0,0 @@
-all:
- @echo Nothing to be made.
-
-clean:
- rm -f *~ *.o *.d
-
-install:
- @echo Nothing to be made.
diff --git a/contrib/wpa/src/eap_server/eap.h b/contrib/wpa/src/eap_server/eap.h
index 92400a5..f2a7cd7 100644
--- a/contrib/wpa/src/eap_server/eap.h
+++ b/contrib/wpa/src/eap_server/eap.h
@@ -2,14 +2,8 @@
* hostapd / EAP Full Authenticator state machine (RFC 4137)
* Copyright (c) 2004-2007, Jouni Malinen <j@w1.fi>
*
- * This program is free software; you can redistribute it and/or modify
- * it under the terms of the GNU General Public License version 2 as
- * published by the Free Software Foundation.
- *
- * Alternatively, this software may be distributed under the terms of BSD
- * license.
- *
- * See README and COPYING for more details.
+ * This software may be distributed under the terms of the BSD license.
+ * See README for more details.
*/
#ifndef EAP_H
@@ -22,8 +16,6 @@
struct eap_sm;
-#define EAP_MAX_METHODS 8
-
#define EAP_TTLS_AUTH_PAP 1
#define EAP_TTLS_AUTH_CHAP 2
#define EAP_TTLS_AUTH_MSCHAP 4
@@ -95,6 +87,7 @@ struct eap_config {
void *eap_sim_db_priv;
Boolean backend_auth;
int eap_server;
+ u16 pwd_group;
u8 *pac_opaque_encr_key;
u8 *eap_fast_a_id;
size_t eap_fast_a_id_len;
@@ -106,7 +99,11 @@ struct eap_config {
int tnc;
struct wps_context *wps;
const struct wpabuf *assoc_wps_ie;
+ const struct wpabuf *assoc_p2p_ie;
const u8 *peer_addr;
+ int fragment_size;
+
+ int pbc_in_m1;
};
@@ -120,5 +117,6 @@ void eap_sm_pending_cb(struct eap_sm *sm);
int eap_sm_method_pending(struct eap_sm *sm);
const u8 * eap_get_identity(struct eap_sm *sm, size_t *len);
struct eap_eapol_interface * eap_get_interface(struct eap_sm *sm);
+void eap_server_clear_identity(struct eap_sm *sm);
#endif /* EAP_H */
diff --git a/contrib/wpa/src/eap_server/eap_i.h b/contrib/wpa/src/eap_server/eap_i.h
index 4269a8c..f92704a 100644
--- a/contrib/wpa/src/eap_server/eap_i.h
+++ b/contrib/wpa/src/eap_server/eap_i.h
@@ -2,14 +2,8 @@
* hostapd / EAP Authenticator state machine internal structures (RFC 4137)
* Copyright (c) 2004-2007, Jouni Malinen <j@w1.fi>
*
- * This program is free software; you can redistribute it and/or modify
- * it under the terms of the GNU General Public License version 2 as
- * published by the Free Software Foundation.
- *
- * Alternatively, this software may be distributed under the terms of BSD
- * license.
- *
- * See README and COPYING for more details.
+ * This software may be distributed under the terms of the BSD license.
+ * See README for more details.
*/
#ifndef EAP_I_H
@@ -119,7 +113,7 @@ struct eap_sm {
/* Full authenticator state machine local variables */
- /* Long-term (maintained betwen packets) */
+ /* Long-term (maintained between packets) */
EapType currentMethod;
int currentId;
enum {
@@ -157,7 +151,7 @@ struct eap_sm {
int user_eap_method_index;
int init_phase2;
void *ssl_ctx;
- void *eap_sim_db_priv;
+ struct eap_sim_db_data *eap_sim_db_priv;
Boolean backend_auth;
Boolean update_user;
int eap_server;
@@ -181,12 +175,19 @@ struct eap_sm {
int pac_key_refresh_time;
int eap_sim_aka_result_ind;
int tnc;
+ u16 pwd_group;
struct wps_context *wps;
struct wpabuf *assoc_wps_ie;
+ struct wpabuf *assoc_p2p_ie;
Boolean start_reauth;
u8 peer_addr[ETH_ALEN];
+
+ /* Fragmentation size for EAP method init() handler */
+ int fragment_size;
+
+ int pbc_in_m1;
};
int eap_user_get(struct eap_sm *sm, const u8 *identity, size_t identity_len,
diff --git a/contrib/wpa/src/eap_server/eap_methods.h b/contrib/wpa/src/eap_server/eap_methods.h
index 5d4d92c..bc810a9 100644
--- a/contrib/wpa/src/eap_server/eap_methods.h
+++ b/contrib/wpa/src/eap_server/eap_methods.h
@@ -2,14 +2,8 @@
* EAP server method registration
* Copyright (c) 2004-2009, Jouni Malinen <j@w1.fi>
*
- * This program is free software; you can redistribute it and/or modify
- * it under the terms of the GNU General Public License version 2 as
- * published by the Free Software Foundation.
- *
- * Alternatively, this software may be distributed under the terms of BSD
- * license.
- *
- * See README and COPYING for more details.
+ * This software may be distributed under the terms of the BSD license.
+ * See README for more details.
*/
#ifndef EAP_SERVER_METHODS_H
@@ -32,6 +26,7 @@ const char * eap_server_get_name(int vendor, EapType type);
int eap_server_identity_register(void);
int eap_server_md5_register(void);
int eap_server_tls_register(void);
+int eap_server_unauth_tls_register(void);
int eap_server_mschapv2_register(void);
int eap_server_peap_register(void);
int eap_server_tlv_register(void);
@@ -49,5 +44,6 @@ int eap_server_fast_register(void);
int eap_server_wsc_register(void);
int eap_server_ikev2_register(void);
int eap_server_tnc_register(void);
+int eap_server_pwd_register(void);
#endif /* EAP_SERVER_METHODS_H */
diff --git a/contrib/wpa/src/eap_server/eap_server.c b/contrib/wpa/src/eap_server/eap_server.c
index fdc26f9..15f7e22 100644
--- a/contrib/wpa/src/eap_server/eap_server.c
+++ b/contrib/wpa/src/eap_server/eap_server.c
@@ -2,14 +2,8 @@
* hostapd / EAP Full Authenticator state machine (RFC 4137)
* Copyright (c) 2004-2007, Jouni Malinen <j@w1.fi>
*
- * This program is free software; you can redistribute it and/or modify
- * it under the terms of the GNU General Public License version 2 as
- * published by the Free Software Foundation.
- *
- * Alternatively, this software may be distributed under the terms of BSD
- * license.
- *
- * See README and COPYING for more details.
+ * This software may be distributed under the terms of the BSD license.
+ * See README for more details.
*
* This state machine is based on the full authenticator state machine defined
* in RFC 4137. However, to support backend authentication in RADIUS
@@ -136,6 +130,14 @@ SM_STATE(EAP, INITIALIZE)
{
SM_ENTRY(EAP, INITIALIZE);
+ if (sm->eap_if.eapRestart && !sm->eap_server && sm->identity) {
+ /*
+ * Need to allow internal Identity method to be used instead
+ * of passthrough at the beginning of reauthentication.
+ */
+ eap_server_clear_identity(sm);
+ }
+
sm->currentId = -1;
sm->eap_if.eapSuccess = FALSE;
sm->eap_if.eapFail = FALSE;
@@ -273,6 +275,11 @@ SM_STATE(EAP, INTEGRITY_CHECK)
{
SM_ENTRY(EAP, INTEGRITY_CHECK);
+ if (!eap_hdr_len_valid(sm->eap_if.eapRespData, 1)) {
+ sm->ignore = TRUE;
+ return;
+ }
+
if (sm->m->check) {
sm->ignore = sm->m->check(sm, sm->eap_method_priv,
sm->eap_if.eapRespData);
@@ -307,6 +314,9 @@ SM_STATE(EAP, METHOD_RESPONSE)
{
SM_ENTRY(EAP, METHOD_RESPONSE);
+ if (!eap_hdr_len_valid(sm->eap_if.eapRespData, 1))
+ return;
+
sm->m->process(sm, sm->eap_method_priv, sm->eap_if.eapRespData);
if (sm->m->isDone(sm, sm->eap_method_priv)) {
eap_sm_Policy_update(sm, NULL, 0);
@@ -378,6 +388,9 @@ SM_STATE(EAP, NAK)
}
sm->m = NULL;
+ if (!eap_hdr_len_valid(sm->eap_if.eapRespData, 1))
+ return;
+
nak = wpabuf_head(sm->eap_if.eapRespData);
if (nak && wpabuf_len(sm->eap_if.eapRespData) > sizeof(*nak)) {
len = be_to_host16(nak->length);
@@ -1028,9 +1041,12 @@ void eap_sm_process_nak(struct eap_sm *sm, const u8 *nak_list, size_t len)
not_found:
/* not found - remove from the list */
- os_memmove(&sm->user->methods[i], &sm->user->methods[i + 1],
- (EAP_MAX_METHODS - i - 1) *
- sizeof(sm->user->methods[0]));
+ if (i + 1 < EAP_MAX_METHODS) {
+ os_memmove(&sm->user->methods[i],
+ &sm->user->methods[i + 1],
+ (EAP_MAX_METHODS - i - 1) *
+ sizeof(sm->user->methods[0]));
+ }
sm->user->methods[EAP_MAX_METHODS - 1].vendor =
EAP_VENDOR_IETF;
sm->user->methods[EAP_MAX_METHODS - 1].method = EAP_TYPE_NONE;
@@ -1255,8 +1271,13 @@ struct eap_sm * eap_server_sm_init(void *eapol_ctx,
sm->wps = conf->wps;
if (conf->assoc_wps_ie)
sm->assoc_wps_ie = wpabuf_dup(conf->assoc_wps_ie);
+ if (conf->assoc_p2p_ie)
+ sm->assoc_p2p_ie = wpabuf_dup(conf->assoc_p2p_ie);
if (conf->peer_addr)
os_memcpy(sm->peer_addr, conf->peer_addr, ETH_ALEN);
+ sm->fragment_size = conf->fragment_size;
+ sm->pwd_group = conf->pwd_group;
+ sm->pbc_in_m1 = conf->pbc_in_m1;
wpa_printf(MSG_DEBUG, "EAP: Server state machine created");
@@ -1291,6 +1312,7 @@ void eap_server_sm_deinit(struct eap_sm *sm)
os_free(sm->eap_if.aaaEapKeyData);
eap_user_free(sm->user);
wpabuf_free(sm->assoc_wps_ie);
+ wpabuf_free(sm->assoc_p2p_ie);
os_free(sm);
}
@@ -1362,3 +1384,18 @@ struct eap_eapol_interface * eap_get_interface(struct eap_sm *sm)
{
return &sm->eap_if;
}
+
+
+/**
+ * eap_server_clear_identity - Clear EAP identity information
+ * @sm: Pointer to EAP state machine allocated with eap_server_sm_init()
+ *
+ * This function can be used to clear the EAP identity information in the EAP
+ * server context. This allows the EAP/Identity method to be used again after
+ * EAPOL-Start or EAPOL-Logoff.
+ */
+void eap_server_clear_identity(struct eap_sm *sm)
+{
+ os_free(sm->identity);
+ sm->identity = NULL;
+}
diff --git a/contrib/wpa/src/eap_server/eap_server_aka.c b/contrib/wpa/src/eap_server/eap_server_aka.c
index 4e7db48..469b9a0 100644
--- a/contrib/wpa/src/eap_server/eap_server_aka.c
+++ b/contrib/wpa/src/eap_server/eap_server_aka.c
@@ -1,15 +1,9 @@
/*
- * hostapd / EAP-AKA (RFC 4187) and EAP-AKA' (draft-arkko-eap-aka-kdf)
- * Copyright (c) 2005-2008, Jouni Malinen <j@w1.fi>
+ * hostapd / EAP-AKA (RFC 4187) and EAP-AKA' (RFC 5448)
+ * Copyright (c) 2005-2012, Jouni Malinen <j@w1.fi>
*
- * This program is free software; you can redistribute it and/or modify
- * it under the terms of the GNU General Public License version 2 as
- * published by the Free Software Foundation.
- *
- * Alternatively, this software may be distributed under the terms of BSD
- * license.
- *
- * See README and COPYING for more details.
+ * This software may be distributed under the terms of the BSD license.
+ * See README for more details.
*/
#include "includes.h"
@@ -17,6 +11,7 @@
#include "common.h"
#include "crypto/sha256.h"
#include "crypto/crypto.h"
+#include "crypto/random.h"
#include "eap_common/eap_sim_common.h"
#include "eap_server/eap_i.h"
#include "eap_server/eap_sim_db.h"
@@ -54,12 +49,12 @@ struct eap_aka_data {
u8 *network_name;
size_t network_name_len;
u16 kdf;
+ int identity_round;
+ char permanent[20]; /* Permanent username */
};
-static void eap_aka_determine_identity(struct eap_sm *sm,
- struct eap_aka_data *data,
- int before_identity, int after_reauth);
+static void eap_aka_fullauth(struct eap_sm *sm, struct eap_aka_data *data);
static const char * eap_aka_state_txt(int state)
@@ -92,6 +87,96 @@ static void eap_aka_state(struct eap_aka_data *data, int state)
}
+static int eap_aka_check_identity_reauth(struct eap_sm *sm,
+ struct eap_aka_data *data,
+ const char *username)
+{
+ if (data->eap_method == EAP_TYPE_AKA_PRIME &&
+ username[0] != EAP_AKA_PRIME_REAUTH_ID_PREFIX)
+ return 0;
+ if (data->eap_method == EAP_TYPE_AKA &&
+ username[0] != EAP_AKA_REAUTH_ID_PREFIX)
+ return 0;
+
+ wpa_printf(MSG_DEBUG, "EAP-AKA: Reauth username '%s'", username);
+ data->reauth = eap_sim_db_get_reauth_entry(sm->eap_sim_db_priv,
+ username);
+ if (data->reauth == NULL) {
+ wpa_printf(MSG_DEBUG, "EAP-AKA: Unknown reauth identity - "
+ "request full auth identity");
+ /* Remain in IDENTITY state for another round */
+ return 0;
+ }
+
+ wpa_printf(MSG_DEBUG, "EAP-AKA: Using fast re-authentication");
+ os_strlcpy(data->permanent, data->reauth->permanent,
+ sizeof(data->permanent));
+ data->counter = data->reauth->counter;
+ if (data->eap_method == EAP_TYPE_AKA_PRIME) {
+ os_memcpy(data->k_encr, data->reauth->k_encr,
+ EAP_SIM_K_ENCR_LEN);
+ os_memcpy(data->k_aut, data->reauth->k_aut,
+ EAP_AKA_PRIME_K_AUT_LEN);
+ os_memcpy(data->k_re, data->reauth->k_re,
+ EAP_AKA_PRIME_K_RE_LEN);
+ } else {
+ os_memcpy(data->mk, data->reauth->mk, EAP_SIM_MK_LEN);
+ }
+
+ eap_aka_state(data, REAUTH);
+ return 1;
+}
+
+
+static void eap_aka_check_identity(struct eap_sm *sm,
+ struct eap_aka_data *data)
+{
+ char *username;
+
+ /* Check if we already know the identity from EAP-Response/Identity */
+
+ username = sim_get_username(sm->identity, sm->identity_len);
+ if (username == NULL)
+ return;
+
+ if (eap_aka_check_identity_reauth(sm, data, username) > 0) {
+ os_free(username);
+ /*
+ * Since re-auth username was recognized, skip AKA/Identity
+ * exchange.
+ */
+ return;
+ }
+
+ if ((data->eap_method == EAP_TYPE_AKA_PRIME &&
+ username[0] == EAP_AKA_PRIME_PSEUDONYM_PREFIX) ||
+ (data->eap_method == EAP_TYPE_AKA &&
+ username[0] == EAP_AKA_PSEUDONYM_PREFIX)) {
+ const char *permanent;
+ wpa_printf(MSG_DEBUG, "EAP-AKA: Pseudonym username '%s'",
+ username);
+ permanent = eap_sim_db_get_permanent(
+ sm->eap_sim_db_priv, username);
+ if (permanent == NULL) {
+ os_free(username);
+ wpa_printf(MSG_DEBUG, "EAP-AKA: Unknown pseudonym "
+ "identity - request permanent identity");
+ /* Remain in IDENTITY state for another round */
+ return;
+ }
+ os_strlcpy(data->permanent, permanent,
+ sizeof(data->permanent));
+ /*
+ * Since pseudonym username was recognized, skip AKA/Identity
+ * exchange.
+ */
+ eap_aka_fullauth(sm, data);
+ }
+
+ os_free(username);
+}
+
+
static void * eap_aka_init(struct eap_sm *sm)
{
struct eap_aka_data *data;
@@ -108,8 +193,8 @@ static void * eap_aka_init(struct eap_sm *sm)
data->eap_method = EAP_TYPE_AKA;
data->state = IDENTITY;
- eap_aka_determine_identity(sm, data, 1, 0);
data->pending_id = -1;
+ eap_aka_check_identity(sm, data);
return data;
}
@@ -132,18 +217,17 @@ static void * eap_aka_prime_init(struct eap_sm *sm)
return NULL;
data->eap_method = EAP_TYPE_AKA_PRIME;
- data->network_name = os_malloc(os_strlen(network_name));
+ data->network_name = (u8 *) os_strdup(network_name);
if (data->network_name == NULL) {
os_free(data);
return NULL;
}
data->network_name_len = os_strlen(network_name);
- os_memcpy(data->network_name, network_name, data->network_name_len);
data->state = IDENTITY;
- eap_aka_determine_identity(sm, data, 1, 0);
data->pending_id = -1;
+ eap_aka_check_identity(sm, data);
return data;
}
@@ -270,11 +354,8 @@ static struct wpabuf * eap_aka_build_identity(struct eap_sm *sm,
wpa_printf(MSG_DEBUG, "EAP-AKA: Generating Identity");
msg = eap_sim_msg_init(EAP_CODE_REQUEST, id, data->eap_method,
EAP_AKA_SUBTYPE_IDENTITY);
- if (eap_sim_db_identity_known(sm->eap_sim_db_priv, sm->identity,
- sm->identity_len)) {
- wpa_printf(MSG_DEBUG, " AT_PERMANENT_ID_REQ");
- eap_sim_msg_add(msg, EAP_SIM_AT_PERMANENT_ID_REQ, 0, NULL, 0);
- } else {
+ data->identity_round++;
+ if (data->identity_round == 1) {
/*
* RFC 4187, Chap. 4.1.4 recommends that identity from EAP is
* ignored and the AKA/Identity is used to request the
@@ -282,6 +363,19 @@ static struct wpabuf * eap_aka_build_identity(struct eap_sm *sm,
*/
wpa_printf(MSG_DEBUG, " AT_ANY_ID_REQ");
eap_sim_msg_add(msg, EAP_SIM_AT_ANY_ID_REQ, 0, NULL, 0);
+ } else if (data->identity_round > 3) {
+ /* Cannot use more than three rounds of Identity messages */
+ eap_sim_msg_free(msg);
+ return NULL;
+ } else if (sm->identity && sm->identity_len > 0 &&
+ (sm->identity[0] == EAP_AKA_REAUTH_ID_PREFIX ||
+ sm->identity[0] == EAP_AKA_PRIME_REAUTH_ID_PREFIX)) {
+ /* Reauth id may have expired - try fullauth */
+ wpa_printf(MSG_DEBUG, " AT_FULLAUTH_ID_REQ");
+ eap_sim_msg_add(msg, EAP_SIM_AT_FULLAUTH_ID_REQ, 0, NULL, 0);
+ } else {
+ wpa_printf(MSG_DEBUG, " AT_PERMANENT_ID_REQ");
+ eap_sim_msg_add(msg, EAP_SIM_AT_PERMANENT_ID_REQ, 0, NULL, 0);
}
buf = eap_sim_msg_finish(msg, NULL, NULL, 0);
if (eap_aka_add_id_msg(data, buf) < 0) {
@@ -298,12 +392,23 @@ static int eap_aka_build_encr(struct eap_sm *sm, struct eap_aka_data *data,
const u8 *nonce_s)
{
os_free(data->next_pseudonym);
- data->next_pseudonym =
- eap_sim_db_get_next_pseudonym(sm->eap_sim_db_priv, 1);
+ if (nonce_s == NULL) {
+ data->next_pseudonym =
+ eap_sim_db_get_next_pseudonym(
+ sm->eap_sim_db_priv,
+ data->eap_method == EAP_TYPE_AKA_PRIME ?
+ EAP_SIM_DB_AKA_PRIME : EAP_SIM_DB_AKA);
+ } else {
+ /* Do not update pseudonym during re-authentication */
+ data->next_pseudonym = NULL;
+ }
os_free(data->next_reauth_id);
if (data->counter <= EAP_AKA_MAX_FAST_REAUTHS) {
data->next_reauth_id =
- eap_sim_db_get_next_reauth_id(sm->eap_sim_db_priv, 1);
+ eap_sim_db_get_next_reauth_id(
+ sm->eap_sim_db_priv,
+ data->eap_method == EAP_TYPE_AKA_PRIME ?
+ EAP_SIM_DB_AKA_PRIME : EAP_SIM_DB_AKA);
} else {
wpa_printf(MSG_DEBUG, "EAP-AKA: Max fast re-authentication "
"count exceeded - force full authentication");
@@ -440,7 +545,7 @@ static struct wpabuf * eap_aka_build_reauth(struct eap_sm *sm,
wpa_printf(MSG_DEBUG, "EAP-AKA: Generating Re-authentication");
- if (os_get_random(data->nonce_s, EAP_SIM_NONCE_S_LEN))
+ if (random_get_bytes(data->nonce_s, EAP_SIM_NONCE_S_LEN))
return NULL;
wpa_hexdump_key(MSG_MSGDUMP, "EAP-AKA: NONCE_S",
data->nonce_s, EAP_SIM_NONCE_S_LEN);
@@ -607,92 +712,83 @@ static Boolean eap_aka_subtype_ok(struct eap_aka_data *data, u8 subtype)
static void eap_aka_determine_identity(struct eap_sm *sm,
- struct eap_aka_data *data,
- int before_identity, int after_reauth)
+ struct eap_aka_data *data)
{
- const u8 *identity;
- size_t identity_len;
- int res;
+ char *username;
- identity = NULL;
- identity_len = 0;
+ wpa_hexdump_ascii(MSG_DEBUG, "EAP-AKA: Identity",
+ sm->identity, sm->identity_len);
- if (after_reauth && data->reauth) {
- identity = data->reauth->identity;
- identity_len = data->reauth->identity_len;
- } else if (sm->identity && sm->identity_len > 0 &&
- sm->identity[0] == EAP_AKA_PERMANENT_PREFIX) {
- identity = sm->identity;
- identity_len = sm->identity_len;
- } else {
- identity = eap_sim_db_get_permanent(sm->eap_sim_db_priv,
- sm->identity,
- sm->identity_len,
- &identity_len);
- if (identity == NULL) {
- data->reauth = eap_sim_db_get_reauth_entry(
- sm->eap_sim_db_priv, sm->identity,
- sm->identity_len);
- if (data->reauth &&
- data->reauth->aka_prime !=
- (data->eap_method == EAP_TYPE_AKA_PRIME)) {
- wpa_printf(MSG_DEBUG, "EAP-AKA: Reauth data "
- "was for different AKA version");
- data->reauth = NULL;
- }
- if (data->reauth) {
- wpa_printf(MSG_DEBUG, "EAP-AKA: Using fast "
- "re-authentication");
- identity = data->reauth->identity;
- identity_len = data->reauth->identity_len;
- data->counter = data->reauth->counter;
- if (data->eap_method == EAP_TYPE_AKA_PRIME) {
- os_memcpy(data->k_encr,
- data->reauth->k_encr,
- EAP_SIM_K_ENCR_LEN);
- os_memcpy(data->k_aut,
- data->reauth->k_aut,
- EAP_AKA_PRIME_K_AUT_LEN);
- os_memcpy(data->k_re,
- data->reauth->k_re,
- EAP_AKA_PRIME_K_RE_LEN);
- } else {
- os_memcpy(data->mk, data->reauth->mk,
- EAP_SIM_MK_LEN);
- }
- }
- }
+ username = sim_get_username(sm->identity, sm->identity_len);
+ if (username == NULL) {
+ data->notification = EAP_SIM_GENERAL_FAILURE_BEFORE_AUTH;
+ eap_aka_state(data, NOTIFICATION);
+ return;
}
- if (identity == NULL ||
- eap_sim_db_identity_known(sm->eap_sim_db_priv, sm->identity,
- sm->identity_len) < 0) {
- if (before_identity) {
- wpa_printf(MSG_DEBUG, "EAP-AKA: Permanent user name "
- "not known - send AKA-Identity request");
- eap_aka_state(data, IDENTITY);
- return;
- } else {
- wpa_printf(MSG_DEBUG, "EAP-AKA: Unknown whether the "
- "permanent user name is known; try to use "
- "it");
- /* eap_sim_db_get_aka_auth() will report failure, if
- * this identity is not known. */
- }
+ if (eap_aka_check_identity_reauth(sm, data, username) > 0) {
+ os_free(username);
+ return;
}
- wpa_hexdump_ascii(MSG_DEBUG, "EAP-AKA: Identity",
- identity, identity_len);
+ if (((data->eap_method == EAP_TYPE_AKA_PRIME &&
+ username[0] == EAP_AKA_PRIME_REAUTH_ID_PREFIX) ||
+ (data->eap_method == EAP_TYPE_AKA &&
+ username[0] == EAP_AKA_REAUTH_ID_PREFIX)) &&
+ data->identity_round == 1) {
+ /* Remain in IDENTITY state for another round to request full
+ * auth identity since we did not recognize reauth id */
+ os_free(username);
+ return;
+ }
- if (!after_reauth && data->reauth) {
- eap_aka_state(data, REAUTH);
+ if ((data->eap_method == EAP_TYPE_AKA_PRIME &&
+ username[0] == EAP_AKA_PRIME_PSEUDONYM_PREFIX) ||
+ (data->eap_method == EAP_TYPE_AKA &&
+ username[0] == EAP_AKA_PSEUDONYM_PREFIX)) {
+ const char *permanent;
+ wpa_printf(MSG_DEBUG, "EAP-AKA: Pseudonym username '%s'",
+ username);
+ permanent = eap_sim_db_get_permanent(
+ sm->eap_sim_db_priv, username);
+ os_free(username);
+ if (permanent == NULL) {
+ wpa_printf(MSG_DEBUG, "EAP-AKA: Unknown pseudonym "
+ "identity - request permanent identity");
+ /* Remain in IDENTITY state for another round */
+ return;
+ }
+ os_strlcpy(data->permanent, permanent,
+ sizeof(data->permanent));
+ } else if ((data->eap_method == EAP_TYPE_AKA_PRIME &&
+ username[0] == EAP_AKA_PRIME_PERMANENT_PREFIX) ||
+ (data->eap_method == EAP_TYPE_AKA &&
+ username[0] == EAP_AKA_PERMANENT_PREFIX)) {
+ wpa_printf(MSG_DEBUG, "EAP-AKA: Permanent username '%s'",
+ username);
+ os_strlcpy(data->permanent, username, sizeof(data->permanent));
+ os_free(username);
+ } else {
+ wpa_printf(MSG_DEBUG, "EAP-AKA: Unrecognized username '%s'",
+ username);
+ os_free(username);
+ data->notification = EAP_SIM_GENERAL_FAILURE_BEFORE_AUTH;
+ eap_aka_state(data, NOTIFICATION);
return;
}
- res = eap_sim_db_get_aka_auth(sm->eap_sim_db_priv, identity,
- identity_len, data->rand, data->autn,
- data->ik, data->ck, data->res,
- &data->res_len, sm);
+ eap_aka_fullauth(sm, data);
+}
+
+
+static void eap_aka_fullauth(struct eap_sm *sm, struct eap_aka_data *data)
+{
+ size_t identity_len;
+ int res;
+
+ res = eap_sim_db_get_aka_auth(sm->eap_sim_db_priv, data->permanent,
+ data->rand, data->autn, data->ik,
+ data->ck, data->res, &data->res_len, sm);
if (res == EAP_SIM_DB_PENDING) {
wpa_printf(MSG_DEBUG, "EAP-AKA: AKA authentication data "
"not yet available - pending request");
@@ -737,7 +833,7 @@ static void eap_aka_determine_identity(struct eap_sm *sm,
sm->identity, identity_len);
if (data->eap_method == EAP_TYPE_AKA_PRIME) {
- eap_aka_prime_derive_keys(identity, identity_len, data->ik,
+ eap_aka_prime_derive_keys(sm->identity, identity_len, data->ik,
data->ck, data->k_encr, data->k_aut,
data->k_re, data->msk, data->emsk);
} else {
@@ -756,6 +852,8 @@ static void eap_aka_process_identity(struct eap_sm *sm,
struct wpabuf *respData,
struct eap_sim_attrs *attr)
{
+ u8 *new_identity;
+
wpa_printf(MSG_DEBUG, "EAP-AKA: Processing Identity");
if (attr->mac || attr->iv || attr->encr_data) {
@@ -766,17 +864,30 @@ static void eap_aka_process_identity(struct eap_sm *sm,
return;
}
- if (attr->identity) {
- os_free(sm->identity);
- sm->identity = os_malloc(attr->identity_len);
- if (sm->identity) {
- os_memcpy(sm->identity, attr->identity,
- attr->identity_len);
- sm->identity_len = attr->identity_len;
- }
+ /*
+ * We always request identity with AKA/Identity, so the peer is
+ * required to have replied with one.
+ */
+ if (!attr->identity || attr->identity_len == 0) {
+ wpa_printf(MSG_DEBUG, "EAP-AKA: Peer did not provide any "
+ "identity");
+ data->notification = EAP_SIM_GENERAL_FAILURE_BEFORE_AUTH;
+ eap_aka_state(data, NOTIFICATION);
+ return;
+ }
+
+ new_identity = os_malloc(attr->identity_len);
+ if (new_identity == NULL) {
+ data->notification = EAP_SIM_GENERAL_FAILURE_BEFORE_AUTH;
+ eap_aka_state(data, NOTIFICATION);
+ return;
}
+ os_free(sm->identity);
+ sm->identity = new_identity;
+ os_memcpy(sm->identity, attr->identity, attr->identity_len);
+ sm->identity_len = attr->identity_len;
- eap_aka_determine_identity(sm, data, 0, 0);
+ eap_aka_determine_identity(sm, data);
if (eap_get_id(respData) == data->pending_id) {
data->pending_id = -1;
eap_aka_add_id_msg(data, respData);
@@ -801,9 +912,6 @@ static void eap_aka_process_challenge(struct eap_sm *sm,
struct wpabuf *respData,
struct eap_sim_attrs *attr)
{
- const u8 *identity;
- size_t identity_len;
-
wpa_printf(MSG_DEBUG, "EAP-AKA: Processing Challenge");
#ifdef EAP_SERVER_AKA_PRIME
@@ -876,16 +984,8 @@ static void eap_aka_process_challenge(struct eap_sm *sm,
} else
eap_aka_state(data, SUCCESS);
- identity = eap_sim_db_get_permanent(sm->eap_sim_db_priv, sm->identity,
- sm->identity_len, &identity_len);
- if (identity == NULL) {
- identity = sm->identity;
- identity_len = sm->identity_len;
- }
-
if (data->next_pseudonym) {
- eap_sim_db_add_pseudonym(sm->eap_sim_db_priv, identity,
- identity_len,
+ eap_sim_db_add_pseudonym(sm->eap_sim_db_priv, data->permanent,
data->next_pseudonym);
data->next_pseudonym = NULL;
}
@@ -893,16 +993,15 @@ static void eap_aka_process_challenge(struct eap_sm *sm,
if (data->eap_method == EAP_TYPE_AKA_PRIME) {
#ifdef EAP_SERVER_AKA_PRIME
eap_sim_db_add_reauth_prime(sm->eap_sim_db_priv,
- identity,
- identity_len,
+ data->permanent,
data->next_reauth_id,
data->counter + 1,
data->k_encr, data->k_aut,
data->k_re);
#endif /* EAP_SERVER_AKA_PRIME */
} else {
- eap_sim_db_add_reauth(sm->eap_sim_db_priv, identity,
- identity_len,
+ eap_sim_db_add_reauth(sm->eap_sim_db_priv,
+ data->permanent,
data->next_reauth_id,
data->counter + 1,
data->mk);
@@ -931,9 +1030,8 @@ static void eap_aka_process_sync_failure(struct eap_sm *sm,
* maintaining a local flag stating whether this AUTS has already been
* reported. */
if (!data->auts_reported &&
- eap_sim_db_resynchronize(sm->eap_sim_db_priv, sm->identity,
- sm->identity_len, attr->auts,
- data->rand)) {
+ eap_sim_db_resynchronize(sm->eap_sim_db_priv, data->permanent,
+ attr->auts, data->rand)) {
wpa_printf(MSG_WARNING, "EAP-AKA: Resynchronization failed");
data->notification = EAP_SIM_GENERAL_FAILURE_BEFORE_AUTH;
eap_aka_state(data, NOTIFICATION);
@@ -941,8 +1039,7 @@ static void eap_aka_process_sync_failure(struct eap_sm *sm,
}
data->auts_reported = 1;
- /* Try again after resynchronization */
- eap_aka_determine_identity(sm, data, 0, 0);
+ /* Remain in CHALLENGE state to re-try after resynchronization */
}
@@ -953,8 +1050,6 @@ static void eap_aka_process_reauth(struct eap_sm *sm,
{
struct eap_sim_attrs eattr;
u8 *decrypted = NULL;
- const u8 *identity, *id2;
- size_t identity_len, id2_len;
wpa_printf(MSG_DEBUG, "EAP-AKA: Processing Reauthentication");
@@ -997,7 +1092,7 @@ static void eap_aka_process_reauth(struct eap_sm *sm,
wpa_printf(MSG_DEBUG, "EAP-AKA: Re-authentication response "
"included AT_COUNTER_TOO_SMALL - starting full "
"authentication");
- eap_aka_determine_identity(sm, data, 0, 1);
+ eap_aka_fullauth(sm, data);
return;
}
@@ -1008,40 +1103,19 @@ static void eap_aka_process_reauth(struct eap_sm *sm,
} else
eap_aka_state(data, SUCCESS);
- if (data->reauth) {
- identity = data->reauth->identity;
- identity_len = data->reauth->identity_len;
- } else {
- identity = sm->identity;
- identity_len = sm->identity_len;
- }
-
- id2 = eap_sim_db_get_permanent(sm->eap_sim_db_priv, identity,
- identity_len, &id2_len);
- if (id2) {
- identity = id2;
- identity_len = id2_len;
- }
-
- if (data->next_pseudonym) {
- eap_sim_db_add_pseudonym(sm->eap_sim_db_priv, identity,
- identity_len, data->next_pseudonym);
- data->next_pseudonym = NULL;
- }
if (data->next_reauth_id) {
if (data->eap_method == EAP_TYPE_AKA_PRIME) {
#ifdef EAP_SERVER_AKA_PRIME
eap_sim_db_add_reauth_prime(sm->eap_sim_db_priv,
- identity,
- identity_len,
+ data->permanent,
data->next_reauth_id,
data->counter + 1,
data->k_encr, data->k_aut,
data->k_re);
#endif /* EAP_SERVER_AKA_PRIME */
} else {
- eap_sim_db_add_reauth(sm->eap_sim_db_priv, identity,
- identity_len,
+ eap_sim_db_add_reauth(sm->eap_sim_db_priv,
+ data->permanent,
data->next_reauth_id,
data->counter + 1,
data->mk);
diff --git a/contrib/wpa/src/eap_server/eap_server_fast.c b/contrib/wpa/src/eap_server/eap_server_fast.c
index 39beb33..fcb80dc 100644
--- a/contrib/wpa/src/eap_server/eap_server_fast.c
+++ b/contrib/wpa/src/eap_server/eap_server_fast.c
@@ -2,14 +2,8 @@
* EAP-FAST server (RFC 4851)
* Copyright (c) 2004-2008, Jouni Malinen <j@w1.fi>
*
- * This program is free software; you can redistribute it and/or modify
- * it under the terms of the GNU General Public License version 2 as
- * published by the Free Software Foundation.
- *
- * Alternatively, this software may be distributed under the terms of BSD
- * license.
- *
- * See README and COPYING for more details.
+ * This software may be distributed under the terms of the BSD license.
+ * See README for more details.
*/
#include "includes.h"
@@ -18,6 +12,7 @@
#include "crypto/aes_wrap.h"
#include "crypto/sha1.h"
#include "crypto/tls.h"
+#include "crypto/random.h"
#include "eap_common/eap_tlv_common.h"
#include "eap_common/eap_fast_common.h"
#include "eap_i.h"
@@ -642,7 +637,7 @@ static struct wpabuf * eap_fast_build_crypto_binding(
binding->version = EAP_FAST_VERSION;
binding->received_version = data->peer_version;
binding->subtype = EAP_TLV_CRYPTO_BINDING_SUBTYPE_REQUEST;
- if (os_get_random(binding->nonce, sizeof(binding->nonce)) < 0) {
+ if (random_get_bytes(binding->nonce, sizeof(binding->nonce)) < 0) {
wpabuf_free(buf);
return NULL;
}
@@ -692,7 +687,7 @@ static struct wpabuf * eap_fast_build_pac(struct eap_sm *sm,
struct eap_tlv_result_tlv *result;
struct os_time now;
- if (os_get_random(pac_key, EAP_FAST_PAC_KEY_LEN) < 0 ||
+ if (random_get_bytes(pac_key, EAP_FAST_PAC_KEY_LEN) < 0 ||
os_get_time(&now) < 0)
return NULL;
wpa_hexdump_key(MSG_DEBUG, "EAP-FAST: Generated PAC-Key",
diff --git a/contrib/wpa/src/eap_server/eap_server_gpsk.c b/contrib/wpa/src/eap_server/eap_server_gpsk.c
index d0c7559..2853c48 100644
--- a/contrib/wpa/src/eap_server/eap_server_gpsk.c
+++ b/contrib/wpa/src/eap_server/eap_server_gpsk.c
@@ -2,19 +2,14 @@
* hostapd / EAP-GPSK (RFC 5433) server
* Copyright (c) 2006-2007, Jouni Malinen <j@w1.fi>
*
- * This program is free software; you can redistribute it and/or modify
- * it under the terms of the GNU General Public License version 2 as
- * published by the Free Software Foundation.
- *
- * Alternatively, this software may be distributed under the terms of BSD
- * license.
- *
- * See README and COPYING for more details.
+ * This software may be distributed under the terms of the BSD license.
+ * See README for more details.
*/
#include "includes.h"
#include "common.h"
+#include "crypto/random.h"
#include "eap_server/eap_i.h"
#include "eap_common/eap_gpsk_common.h"
@@ -120,7 +115,7 @@ static struct wpabuf * eap_gpsk_build_gpsk_1(struct eap_sm *sm,
wpa_printf(MSG_DEBUG, "EAP-GPSK: Request/GPSK-1");
- if (os_get_random(data->rand_server, EAP_GPSK_RAND_LEN)) {
+ if (random_get_bytes(data->rand_server, EAP_GPSK_RAND_LEN)) {
wpa_printf(MSG_ERROR, "EAP-GPSK: Failed to get random data");
eap_gpsk_state(data, FAILURE);
return NULL;
diff --git a/contrib/wpa/src/eap_server/eap_server_gtc.c b/contrib/wpa/src/eap_server/eap_server_gtc.c
index 79b9696..f423106 100644
--- a/contrib/wpa/src/eap_server/eap_server_gtc.c
+++ b/contrib/wpa/src/eap_server/eap_server_gtc.c
@@ -2,14 +2,8 @@
* hostapd / EAP-GTC (RFC 3748)
* Copyright (c) 2004-2006, Jouni Malinen <j@w1.fi>
*
- * This program is free software; you can redistribute it and/or modify
- * it under the terms of the GNU General Public License version 2 as
- * published by the Free Software Foundation.
- *
- * Alternatively, this software may be distributed under the terms of BSD
- * license.
- *
- * See README and COPYING for more details.
+ * This software may be distributed under the terms of the BSD license.
+ * See README for more details.
*/
#include "includes.h"
diff --git a/contrib/wpa/src/eap_server/eap_server_identity.c b/contrib/wpa/src/eap_server/eap_server_identity.c
index cd8da2a..51dc4e8 100644
--- a/contrib/wpa/src/eap_server/eap_server_identity.c
+++ b/contrib/wpa/src/eap_server/eap_server_identity.c
@@ -2,14 +2,8 @@
* hostapd / EAP-Identity
* Copyright (c) 2004-2006, Jouni Malinen <j@w1.fi>
*
- * This program is free software; you can redistribute it and/or modify
- * it under the terms of the GNU General Public License version 2 as
- * published by the Free Software Foundation.
- *
- * Alternatively, this software may be distributed under the terms of BSD
- * license.
- *
- * See README and COPYING for more details.
+ * This software may be distributed under the terms of the BSD license.
+ * See README for more details.
*/
#include "includes.h"
diff --git a/contrib/wpa/src/eap_server/eap_server_ikev2.c b/contrib/wpa/src/eap_server/eap_server_ikev2.c
index 06074ee..42aaca2 100644
--- a/contrib/wpa/src/eap_server/eap_server_ikev2.c
+++ b/contrib/wpa/src/eap_server/eap_server_ikev2.c
@@ -2,14 +2,8 @@
* EAP-IKEv2 server (RFC 5106)
* Copyright (c) 2007, Jouni Malinen <j@w1.fi>
*
- * This program is free software; you can redistribute it and/or modify
- * it under the terms of the GNU General Public License version 2 as
- * published by the Free Software Foundation.
- *
- * Alternatively, this software may be distributed under the terms of BSD
- * license.
- *
- * See README and COPYING for more details.
+ * This software may be distributed under the terms of the BSD license.
+ * See README for more details.
*/
#include "includes.h"
@@ -93,7 +87,8 @@ static void * eap_ikev2_init(struct eap_sm *sm)
if (data == NULL)
return NULL;
data->state = MSG;
- data->fragment_size = IKEV2_FRAGMENT_SIZE;
+ data->fragment_size = sm->fragment_size > 0 ? sm->fragment_size :
+ IKEV2_FRAGMENT_SIZE;
data->ikev2.state = SA_INIT;
data->ikev2.peer_auth = PEER_AUTH_SECRET;
data->ikev2.key_pad = (u8 *) os_strdup("Key Pad for EAP-IKEv2");
diff --git a/contrib/wpa/src/eap_server/eap_server_md5.c b/contrib/wpa/src/eap_server/eap_server_md5.c
index dee2dc5..5a5e290 100644
--- a/contrib/wpa/src/eap_server/eap_server_md5.c
+++ b/contrib/wpa/src/eap_server/eap_server_md5.c
@@ -1,20 +1,15 @@
/*
* hostapd / EAP-MD5 server
- * Copyright (c) 2004-2007, Jouni Malinen <j@w1.fi>
+ * Copyright (c) 2004-2012, Jouni Malinen <j@w1.fi>
*
- * This program is free software; you can redistribute it and/or modify
- * it under the terms of the GNU General Public License version 2 as
- * published by the Free Software Foundation.
- *
- * Alternatively, this software may be distributed under the terms of BSD
- * license.
- *
- * See README and COPYING for more details.
+ * This software may be distributed under the terms of the BSD license.
+ * See README for more details.
*/
#include "includes.h"
#include "common.h"
+#include "crypto/random.h"
#include "eap_i.h"
#include "eap_common/chap.h"
@@ -52,7 +47,7 @@ static struct wpabuf * eap_md5_buildReq(struct eap_sm *sm, void *priv, u8 id)
struct eap_md5_data *data = priv;
struct wpabuf *req;
- if (os_get_random(data->challenge, CHALLENGE_LEN)) {
+ if (random_get_bytes(data->challenge, CHALLENGE_LEN)) {
wpa_printf(MSG_ERROR, "EAP-MD5: Failed to get random data");
data->state = FAILURE;
return NULL;
@@ -124,8 +119,12 @@ static void eap_md5_process(struct eap_sm *sm, void *priv,
wpa_hexdump(MSG_MSGDUMP, "EAP-MD5: Response", pos, CHAP_MD5_LEN);
id = eap_get_id(respData);
- chap_md5(id, sm->user->password, sm->user->password_len,
- data->challenge, CHALLENGE_LEN, hash);
+ if (chap_md5(id, sm->user->password, sm->user->password_len,
+ data->challenge, CHALLENGE_LEN, hash)) {
+ wpa_printf(MSG_INFO, "EAP-MD5: CHAP MD5 operation failed");
+ data->state = FAILURE;
+ return;
+ }
if (os_memcmp(hash, pos, CHAP_MD5_LEN) == 0) {
wpa_printf(MSG_DEBUG, "EAP-MD5: Done - Success");
diff --git a/contrib/wpa/src/eap_server/eap_server_methods.c b/contrib/wpa/src/eap_server/eap_server_methods.c
index 900a5dd..0209fad 100644
--- a/contrib/wpa/src/eap_server/eap_server_methods.c
+++ b/contrib/wpa/src/eap_server/eap_server_methods.c
@@ -2,14 +2,8 @@
* EAP server method registration
* Copyright (c) 2004-2009, Jouni Malinen <j@w1.fi>
*
- * This program is free software; you can redistribute it and/or modify
- * it under the terms of the GNU General Public License version 2 as
- * published by the Free Software Foundation.
- *
- * Alternatively, this software may be distributed under the terms of BSD
- * license.
- *
- * See README and COPYING for more details.
+ * This software may be distributed under the terms of the BSD license.
+ * See README for more details.
*/
#include "includes.h"
@@ -167,6 +161,8 @@ void eap_server_unregister_methods(void)
const char * eap_server_get_name(int vendor, EapType type)
{
struct eap_method *m;
+ if (vendor == EAP_VENDOR_IETF && type == EAP_TYPE_EXPANDED)
+ return "expanded";
for (m = eap_methods; m; m = m->next) {
if (m->vendor == vendor && m->method == type)
return m->name;
diff --git a/contrib/wpa/src/eap_server/eap_server_mschapv2.c b/contrib/wpa/src/eap_server/eap_server_mschapv2.c
index 39d1c6e..8d3dd52 100644
--- a/contrib/wpa/src/eap_server/eap_server_mschapv2.c
+++ b/contrib/wpa/src/eap_server/eap_server_mschapv2.c
@@ -2,20 +2,15 @@
* hostapd / EAP-MSCHAPv2 (draft-kamath-pppext-eap-mschapv2-00.txt) server
* Copyright (c) 2004-2007, Jouni Malinen <j@w1.fi>
*
- * This program is free software; you can redistribute it and/or modify
- * it under the terms of the GNU General Public License version 2 as
- * published by the Free Software Foundation.
- *
- * Alternatively, this software may be distributed under the terms of BSD
- * license.
- *
- * See README and COPYING for more details.
+ * This software may be distributed under the terms of the BSD license.
+ * See README for more details.
*/
#include "includes.h"
#include "common.h"
#include "crypto/ms_funcs.h"
+#include "crypto/random.h"
#include "eap_i.h"
@@ -109,7 +104,7 @@ static struct wpabuf * eap_mschapv2_build_challenge(
size_t ms_len;
if (!data->auth_challenge_from_tls &&
- os_get_random(data->auth_challenge, CHALLENGE_LEN)) {
+ random_get_bytes(data->auth_challenge, CHALLENGE_LEN)) {
wpa_printf(MSG_ERROR, "EAP-MSCHAPV2: Failed to get random "
"data");
data->state = FAILURE;
@@ -404,9 +399,12 @@ static void eap_mschapv2_process_response(struct eap_sm *sm,
if (sm->user->password_hash) {
pw_hash = sm->user->password;
} else {
- nt_password_hash(sm->user->password,
- sm->user->password_len,
- pw_hash_buf);
+ if (nt_password_hash(sm->user->password,
+ sm->user->password_len,
+ pw_hash_buf) < 0) {
+ data->state = FAILURE;
+ return;
+ }
pw_hash = pw_hash_buf;
}
generate_authenticator_response_pwhash(
diff --git a/contrib/wpa/src/eap_server/eap_server_pax.c b/contrib/wpa/src/eap_server/eap_server_pax.c
index 1dc023b..35a42ad 100644
--- a/contrib/wpa/src/eap_server/eap_server_pax.c
+++ b/contrib/wpa/src/eap_server/eap_server_pax.c
@@ -2,19 +2,14 @@
* hostapd / EAP-PAX (RFC 4746) server
* Copyright (c) 2005-2007, Jouni Malinen <j@w1.fi>
*
- * This program is free software; you can redistribute it and/or modify
- * it under the terms of the GNU General Public License version 2 as
- * published by the Free Software Foundation.
- *
- * Alternatively, this software may be distributed under the terms of BSD
- * license.
- *
- * See README and COPYING for more details.
+ * This software may be distributed under the terms of the BSD license.
+ * See README for more details.
*/
#include "includes.h"
#include "common.h"
+#include "crypto/random.h"
#include "eap_server/eap_i.h"
#include "eap_common/eap_pax_common.h"
@@ -82,7 +77,7 @@ static struct wpabuf * eap_pax_build_std_1(struct eap_sm *sm,
wpa_printf(MSG_DEBUG, "EAP-PAX: PAX_STD-1 (sending)");
- if (os_get_random(data->rand.r.x, EAP_PAX_RAND_LEN)) {
+ if (random_get_bytes(data->rand.r.x, EAP_PAX_RAND_LEN)) {
wpa_printf(MSG_ERROR, "EAP-PAX: Failed to get random data");
data->state = FAILURE;
return NULL;
diff --git a/contrib/wpa/src/eap_server/eap_server_peap.c b/contrib/wpa/src/eap_server/eap_server_peap.c
index 674ecd2..68253c4 100644
--- a/contrib/wpa/src/eap_server/eap_server_peap.c
+++ b/contrib/wpa/src/eap_server/eap_server_peap.c
@@ -2,14 +2,8 @@
* hostapd / EAP-PEAP (draft-josefsson-pppext-eap-tls-eap-10.txt)
* Copyright (c) 2004-2008, Jouni Malinen <j@w1.fi>
*
- * This program is free software; you can redistribute it and/or modify
- * it under the terms of the GNU General Public License version 2 as
- * published by the Free Software Foundation.
- *
- * Alternatively, this software may be distributed under the terms of BSD
- * license.
- *
- * See README and COPYING for more details.
+ * This software may be distributed under the terms of the BSD license.
+ * See README for more details.
*/
#include "includes.h"
@@ -17,6 +11,7 @@
#include "common.h"
#include "crypto/sha1.h"
#include "crypto/tls.h"
+#include "crypto/random.h"
#include "eap_i.h"
#include "eap_tls_common.h"
#include "eap_common/eap_tlv_common.h"
@@ -350,8 +345,12 @@ static int eap_peap_derive_cmk(struct eap_sm *sm, struct eap_peap_data *data)
* in the end of the label just before ISK; is that just a typo?)
*/
wpa_hexdump_key(MSG_DEBUG, "EAP-PEAP: TempKey", tk, 40);
- peap_prfplus(data->peap_version, tk, 40, "Inner Methods Compound Keys",
- isk, sizeof(isk), imck, sizeof(imck));
+ if (peap_prfplus(data->peap_version, tk, 40,
+ "Inner Methods Compound Keys",
+ isk, sizeof(isk), imck, sizeof(imck)) < 0) {
+ os_free(tk);
+ return -1;
+ }
wpa_hexdump_key(MSG_DEBUG, "EAP-PEAP: IMCK (IPMKj)",
imck, sizeof(imck));
@@ -414,7 +413,7 @@ static struct wpabuf * eap_peap_build_phase2_tlv(struct eap_sm *sm,
#endif /* EAP_SERVER_TNC */
if (eap_peap_derive_cmk(sm, data) < 0 ||
- os_get_random(data->binding_nonce, 32)) {
+ random_get_bytes(data->binding_nonce, 32)) {
wpabuf_free(buf);
return NULL;
}
@@ -1059,8 +1058,6 @@ static void eap_peap_process_phase2(struct eap_sm *sm,
wpa_hexdump_buf_key(MSG_DEBUG, "EAP-PEAP: Decrypted Phase 2 EAP",
in_decrypted);
- hdr = wpabuf_head(in_decrypted);
-
if (data->peap_version == 0 && data->state != PHASE2_TLV) {
const struct eap_hdr *resp;
struct eap_hdr *nhdr;
@@ -1319,9 +1316,10 @@ static u8 * eap_peap_getKey(struct eap_sm *sm, void *priv, size_t *len)
* termination for this label while the one used for deriving
* IPMK|CMK did not use null termination.
*/
- peap_prfplus(data->peap_version, data->ipmk, 40,
- "Session Key Generating Function",
- (u8 *) "\00", 1, csk, sizeof(csk));
+ if (peap_prfplus(data->peap_version, data->ipmk, 40,
+ "Session Key Generating Function",
+ (u8 *) "\00", 1, csk, sizeof(csk)) < 0)
+ return NULL;
wpa_hexdump_key(MSG_DEBUG, "EAP-PEAP: CSK", csk, sizeof(csk));
eapKeyData = os_malloc(EAP_TLS_KEY_LEN);
if (eapKeyData) {
diff --git a/contrib/wpa/src/eap_server/eap_server_psk.c b/contrib/wpa/src/eap_server/eap_server_psk.c
index 4c30346..0cd9799 100644
--- a/contrib/wpa/src/eap_server/eap_server_psk.c
+++ b/contrib/wpa/src/eap_server/eap_server_psk.c
@@ -2,14 +2,8 @@
* hostapd / EAP-PSK (RFC 4764) server
* Copyright (c) 2005-2007, Jouni Malinen <j@w1.fi>
*
- * This program is free software; you can redistribute it and/or modify
- * it under the terms of the GNU General Public License version 2 as
- * published by the Free Software Foundation.
- *
- * Alternatively, this software may be distributed under the terms of BSD
- * license.
- *
- * See README and COPYING for more details.
+ * This software may be distributed under the terms of the BSD license.
+ * See README for more details.
*
* Note: EAP-PSK is an EAP authentication method and as such, completely
* different from WPA-PSK. This file is not needed for WPA-PSK functionality.
@@ -19,6 +13,7 @@
#include "common.h"
#include "crypto/aes_wrap.h"
+#include "crypto/random.h"
#include "eap_common/eap_psk_common.h"
#include "eap_server/eap_i.h"
@@ -66,7 +61,7 @@ static struct wpabuf * eap_psk_build_1(struct eap_sm *sm,
wpa_printf(MSG_DEBUG, "EAP-PSK: PSK-1 (sending)");
- if (os_get_random(data->rand_s, EAP_PSK_RAND_LEN)) {
+ if (random_get_bytes(data->rand_s, EAP_PSK_RAND_LEN)) {
wpa_printf(MSG_ERROR, "EAP-PSK: Failed to get random data");
data->state = FAILURE;
return NULL;
@@ -124,8 +119,10 @@ static struct wpabuf * eap_psk_build_3(struct eap_sm *sm,
os_memcpy(buf, data->id_s, data->id_s_len);
os_memcpy(buf + data->id_s_len, data->rand_p, EAP_PSK_RAND_LEN);
- if (omac1_aes_128(data->ak, buf, buflen, psk->mac_s))
+ if (omac1_aes_128(data->ak, buf, buflen, psk->mac_s)) {
+ os_free(buf);
goto fail;
+ }
os_free(buf);
if (eap_psk_derive_keys(data->kdk, data->rand_p, data->tek, data->msk,
diff --git a/contrib/wpa/src/eap_server/eap_server_pwd.c b/contrib/wpa/src/eap_server/eap_server_pwd.c
new file mode 100644
index 0000000..b61061b
--- /dev/null
+++ b/contrib/wpa/src/eap_server/eap_server_pwd.c
@@ -0,0 +1,1045 @@
+/*
+ * hostapd / EAP-pwd (RFC 5931) server
+ * Copyright (c) 2010, Dan Harkins <dharkins@lounge.org>
+ *
+ * This software may be distributed under the terms of the BSD license.
+ * See README for more details.
+ */
+
+#include "includes.h"
+
+#include "common.h"
+#include "crypto/sha256.h"
+#include "eap_server/eap_i.h"
+#include "eap_common/eap_pwd_common.h"
+
+
+struct eap_pwd_data {
+ enum {
+ PWD_ID_Req, PWD_Commit_Req, PWD_Confirm_Req, SUCCESS, FAILURE
+ } state;
+ u8 *id_peer;
+ size_t id_peer_len;
+ u8 *id_server;
+ size_t id_server_len;
+ u8 *password;
+ size_t password_len;
+ u32 token;
+ u16 group_num;
+ EAP_PWD_group *grp;
+
+ struct wpabuf *inbuf;
+ size_t in_frag_pos;
+ struct wpabuf *outbuf;
+ size_t out_frag_pos;
+ size_t mtu;
+
+ BIGNUM *k;
+ BIGNUM *private_value;
+ BIGNUM *peer_scalar;
+ BIGNUM *my_scalar;
+ EC_POINT *my_element;
+ EC_POINT *peer_element;
+
+ u8 my_confirm[SHA256_MAC_LEN];
+
+ u8 msk[EAP_MSK_LEN];
+ u8 emsk[EAP_EMSK_LEN];
+
+ BN_CTX *bnctx;
+};
+
+
+static const char * eap_pwd_state_txt(int state)
+{
+ switch (state) {
+ case PWD_ID_Req:
+ return "PWD-ID-Req";
+ case PWD_Commit_Req:
+ return "PWD-Commit-Req";
+ case PWD_Confirm_Req:
+ return "PWD-Confirm-Req";
+ case SUCCESS:
+ return "SUCCESS";
+ case FAILURE:
+ return "FAILURE";
+ default:
+ return "PWD-Unk";
+ }
+}
+
+
+static void eap_pwd_state(struct eap_pwd_data *data, int state)
+{
+ wpa_printf(MSG_DEBUG, "EAP-pwd: %s -> %s",
+ eap_pwd_state_txt(data->state), eap_pwd_state_txt(state));
+ data->state = state;
+}
+
+
+static void * eap_pwd_init(struct eap_sm *sm)
+{
+ struct eap_pwd_data *data;
+
+ if (sm->user == NULL || sm->user->password == NULL ||
+ sm->user->password_len == 0) {
+ wpa_printf(MSG_INFO, "EAP-PWD (server): Password is not "
+ "configured");
+ return NULL;
+ }
+
+ data = os_zalloc(sizeof(*data));
+ if (data == NULL)
+ return NULL;
+
+ data->group_num = sm->pwd_group;
+ wpa_printf(MSG_DEBUG, "EAP-pwd: Selected group number %d",
+ data->group_num);
+ data->state = PWD_ID_Req;
+
+ data->id_server = (u8 *) os_strdup("server");
+ if (data->id_server)
+ data->id_server_len = os_strlen((char *) data->id_server);
+
+ data->password = os_malloc(sm->user->password_len);
+ if (data->password == NULL) {
+ wpa_printf(MSG_INFO, "EAP-PWD: Memory allocation password "
+ "fail");
+ os_free(data->id_server);
+ os_free(data);
+ return NULL;
+ }
+ data->password_len = sm->user->password_len;
+ os_memcpy(data->password, sm->user->password, data->password_len);
+
+ data->bnctx = BN_CTX_new();
+ if (data->bnctx == NULL) {
+ wpa_printf(MSG_INFO, "EAP-PWD: bn context allocation fail");
+ os_free(data->password);
+ os_free(data->id_server);
+ os_free(data);
+ return NULL;
+ }
+
+ data->in_frag_pos = data->out_frag_pos = 0;
+ data->inbuf = data->outbuf = NULL;
+ data->mtu = 1020; /* default from RFC 5931, make it configurable! */
+
+ return data;
+}
+
+
+static void eap_pwd_reset(struct eap_sm *sm, void *priv)
+{
+ struct eap_pwd_data *data = priv;
+
+ BN_free(data->private_value);
+ BN_free(data->peer_scalar);
+ BN_free(data->my_scalar);
+ BN_free(data->k);
+ BN_CTX_free(data->bnctx);
+ EC_POINT_free(data->my_element);
+ EC_POINT_free(data->peer_element);
+ os_free(data->id_peer);
+ os_free(data->id_server);
+ os_free(data->password);
+ if (data->grp) {
+ EC_GROUP_free(data->grp->group);
+ EC_POINT_free(data->grp->pwe);
+ BN_free(data->grp->order);
+ BN_free(data->grp->prime);
+ os_free(data->grp);
+ }
+ os_free(data);
+}
+
+
+static void eap_pwd_build_id_req(struct eap_sm *sm, struct eap_pwd_data *data,
+ u8 id)
+{
+ wpa_printf(MSG_DEBUG, "EAP-pwd: ID/Request");
+ /*
+ * if we're fragmenting then we already have an id request, just return
+ */
+ if (data->out_frag_pos)
+ return;
+
+ data->outbuf = wpabuf_alloc(sizeof(struct eap_pwd_id) +
+ data->id_server_len);
+ if (data->outbuf == NULL) {
+ eap_pwd_state(data, FAILURE);
+ return;
+ }
+
+ /* an lfsr is good enough to generate unpredictable tokens */
+ data->token = os_random();
+ wpabuf_put_be16(data->outbuf, data->group_num);
+ wpabuf_put_u8(data->outbuf, EAP_PWD_DEFAULT_RAND_FUNC);
+ wpabuf_put_u8(data->outbuf, EAP_PWD_DEFAULT_PRF);
+ wpabuf_put_data(data->outbuf, &data->token, sizeof(data->token));
+ wpabuf_put_u8(data->outbuf, EAP_PWD_PREP_NONE);
+ wpabuf_put_data(data->outbuf, data->id_server, data->id_server_len);
+}
+
+
+static void eap_pwd_build_commit_req(struct eap_sm *sm,
+ struct eap_pwd_data *data, u8 id)
+{
+ BIGNUM *mask = NULL, *x = NULL, *y = NULL;
+ u8 *scalar = NULL, *element = NULL;
+ u16 offset;
+
+ wpa_printf(MSG_DEBUG, "EAP-pwd: Commit/Request");
+ /*
+ * if we're fragmenting then we already have an commit request, just
+ * return
+ */
+ if (data->out_frag_pos)
+ return;
+
+ if (((data->private_value = BN_new()) == NULL) ||
+ ((data->my_element = EC_POINT_new(data->grp->group)) == NULL) ||
+ ((data->my_scalar = BN_new()) == NULL) ||
+ ((mask = BN_new()) == NULL)) {
+ wpa_printf(MSG_INFO, "EAP-PWD (server): scalar allocation "
+ "fail");
+ goto fin;
+ }
+
+ BN_rand_range(data->private_value, data->grp->order);
+ BN_rand_range(mask, data->grp->order);
+ BN_add(data->my_scalar, data->private_value, mask);
+ BN_mod(data->my_scalar, data->my_scalar, data->grp->order,
+ data->bnctx);
+
+ if (!EC_POINT_mul(data->grp->group, data->my_element, NULL,
+ data->grp->pwe, mask, data->bnctx)) {
+ wpa_printf(MSG_INFO, "EAP-PWD (server): element allocation "
+ "fail");
+ eap_pwd_state(data, FAILURE);
+ goto fin;
+ }
+
+ if (!EC_POINT_invert(data->grp->group, data->my_element, data->bnctx))
+ {
+ wpa_printf(MSG_INFO, "EAP-PWD (server): element inversion "
+ "fail");
+ goto fin;
+ }
+ BN_free(mask);
+
+ if (((x = BN_new()) == NULL) ||
+ ((y = BN_new()) == NULL)) {
+ wpa_printf(MSG_INFO, "EAP-PWD (server): point allocation "
+ "fail");
+ goto fin;
+ }
+ if (!EC_POINT_get_affine_coordinates_GFp(data->grp->group,
+ data->my_element, x, y,
+ data->bnctx)) {
+ wpa_printf(MSG_INFO, "EAP-PWD (server): point assignment "
+ "fail");
+ goto fin;
+ }
+
+ if (((scalar = os_malloc(BN_num_bytes(data->grp->order))) == NULL) ||
+ ((element = os_malloc(BN_num_bytes(data->grp->prime) * 2)) ==
+ NULL)) {
+ wpa_printf(MSG_INFO, "EAP-PWD (server): data allocation fail");
+ goto fin;
+ }
+
+ /*
+ * bignums occupy as little memory as possible so one that is
+ * sufficiently smaller than the prime or order might need pre-pending
+ * with zeros.
+ */
+ os_memset(scalar, 0, BN_num_bytes(data->grp->order));
+ os_memset(element, 0, BN_num_bytes(data->grp->prime) * 2);
+ offset = BN_num_bytes(data->grp->order) -
+ BN_num_bytes(data->my_scalar);
+ BN_bn2bin(data->my_scalar, scalar + offset);
+
+ offset = BN_num_bytes(data->grp->prime) - BN_num_bytes(x);
+ BN_bn2bin(x, element + offset);
+ offset = BN_num_bytes(data->grp->prime) - BN_num_bytes(y);
+ BN_bn2bin(y, element + BN_num_bytes(data->grp->prime) + offset);
+
+ data->outbuf = wpabuf_alloc(2 * BN_num_bytes(data->grp->prime) +
+ BN_num_bytes(data->grp->order));
+ if (data->outbuf == NULL)
+ goto fin;
+
+ /* We send the element as (x,y) followed by the scalar */
+ wpabuf_put_data(data->outbuf, element,
+ 2 * BN_num_bytes(data->grp->prime));
+ wpabuf_put_data(data->outbuf, scalar, BN_num_bytes(data->grp->order));
+
+fin:
+ os_free(scalar);
+ os_free(element);
+ BN_free(x);
+ BN_free(y);
+ if (data->outbuf == NULL)
+ eap_pwd_state(data, FAILURE);
+}
+
+
+static void eap_pwd_build_confirm_req(struct eap_sm *sm,
+ struct eap_pwd_data *data, u8 id)
+{
+ BIGNUM *x = NULL, *y = NULL;
+ struct crypto_hash *hash;
+ u8 conf[SHA256_MAC_LEN], *cruft = NULL, *ptr;
+ u16 grp;
+ int offset;
+
+ wpa_printf(MSG_DEBUG, "EAP-pwd: Confirm/Request");
+ /*
+ * if we're fragmenting then we already have an confirm request, just
+ * return
+ */
+ if (data->out_frag_pos)
+ return;
+
+ /* Each component of the cruft will be at most as big as the prime */
+ if (((cruft = os_malloc(BN_num_bytes(data->grp->prime))) == NULL) ||
+ ((x = BN_new()) == NULL) || ((y = BN_new()) == NULL)) {
+ wpa_printf(MSG_INFO, "EAP-PWD (server): debug allocation "
+ "fail");
+ goto fin;
+ }
+
+ /*
+ * commit is H(k | server_element | server_scalar | peer_element |
+ * peer_scalar | ciphersuite)
+ */
+ hash = eap_pwd_h_init();
+ if (hash == NULL)
+ goto fin;
+
+ /*
+ * Zero the memory each time because this is mod prime math and some
+ * value may start with a few zeros and the previous one did not.
+ *
+ * First is k
+ */
+ os_memset(cruft, 0, BN_num_bytes(data->grp->prime));
+ offset = BN_num_bytes(data->grp->prime) - BN_num_bytes(data->k);
+ BN_bn2bin(data->k, cruft + offset);
+ eap_pwd_h_update(hash, cruft, BN_num_bytes(data->grp->prime));
+
+ /* server element: x, y */
+ if (!EC_POINT_get_affine_coordinates_GFp(data->grp->group,
+ data->my_element, x, y,
+ data->bnctx)) {
+ wpa_printf(MSG_INFO, "EAP-PWD (server): confirm point "
+ "assignment fail");
+ goto fin;
+ }
+
+ os_memset(cruft, 0, BN_num_bytes(data->grp->prime));
+ offset = BN_num_bytes(data->grp->prime) - BN_num_bytes(x);
+ BN_bn2bin(x, cruft + offset);
+ eap_pwd_h_update(hash, cruft, BN_num_bytes(data->grp->prime));
+ os_memset(cruft, 0, BN_num_bytes(data->grp->prime));
+ offset = BN_num_bytes(data->grp->prime) - BN_num_bytes(y);
+ BN_bn2bin(y, cruft + offset);
+ eap_pwd_h_update(hash, cruft, BN_num_bytes(data->grp->prime));
+
+ /* server scalar */
+ os_memset(cruft, 0, BN_num_bytes(data->grp->prime));
+ offset = BN_num_bytes(data->grp->order) -
+ BN_num_bytes(data->my_scalar);
+ BN_bn2bin(data->my_scalar, cruft + offset);
+ eap_pwd_h_update(hash, cruft, BN_num_bytes(data->grp->order));
+
+ /* peer element: x, y */
+ if (!EC_POINT_get_affine_coordinates_GFp(data->grp->group,
+ data->peer_element, x, y,
+ data->bnctx)) {
+ wpa_printf(MSG_INFO, "EAP-PWD (server): confirm point "
+ "assignment fail");
+ goto fin;
+ }
+
+ os_memset(cruft, 0, BN_num_bytes(data->grp->prime));
+ offset = BN_num_bytes(data->grp->prime) - BN_num_bytes(x);
+ BN_bn2bin(x, cruft + offset);
+ eap_pwd_h_update(hash, cruft, BN_num_bytes(data->grp->prime));
+ os_memset(cruft, 0, BN_num_bytes(data->grp->prime));
+ offset = BN_num_bytes(data->grp->prime) - BN_num_bytes(y);
+ BN_bn2bin(y, cruft + offset);
+ eap_pwd_h_update(hash, cruft, BN_num_bytes(data->grp->prime));
+
+ /* peer scalar */
+ os_memset(cruft, 0, BN_num_bytes(data->grp->prime));
+ offset = BN_num_bytes(data->grp->order) -
+ BN_num_bytes(data->peer_scalar);
+ BN_bn2bin(data->peer_scalar, cruft + offset);
+ eap_pwd_h_update(hash, cruft, BN_num_bytes(data->grp->order));
+
+ /* ciphersuite */
+ grp = htons(data->group_num);
+ os_memset(cruft, 0, BN_num_bytes(data->grp->prime));
+ ptr = cruft;
+ os_memcpy(ptr, &grp, sizeof(u16));
+ ptr += sizeof(u16);
+ *ptr = EAP_PWD_DEFAULT_RAND_FUNC;
+ ptr += sizeof(u8);
+ *ptr = EAP_PWD_DEFAULT_PRF;
+ ptr += sizeof(u8);
+ eap_pwd_h_update(hash, cruft, ptr - cruft);
+
+ /* all done with the random function */
+ eap_pwd_h_final(hash, conf);
+ os_memcpy(data->my_confirm, conf, SHA256_MAC_LEN);
+
+ data->outbuf = wpabuf_alloc(SHA256_MAC_LEN);
+ if (data->outbuf == NULL)
+ goto fin;
+
+ wpabuf_put_data(data->outbuf, conf, SHA256_MAC_LEN);
+
+fin:
+ os_free(cruft);
+ BN_free(x);
+ BN_free(y);
+ if (data->outbuf == NULL)
+ eap_pwd_state(data, FAILURE);
+}
+
+
+static struct wpabuf *
+eap_pwd_build_req(struct eap_sm *sm, void *priv, u8 id)
+{
+ struct eap_pwd_data *data = priv;
+ struct wpabuf *req;
+ u8 lm_exch;
+ const u8 *buf;
+ u16 totlen = 0;
+ size_t len;
+
+ /*
+ * if we're buffering response fragments then just ACK
+ */
+ if (data->in_frag_pos) {
+ wpa_printf(MSG_DEBUG, "EAP-pwd: ACKing a fragment!!");
+ req = eap_msg_alloc(EAP_VENDOR_IETF, EAP_TYPE_PWD,
+ EAP_PWD_HDR_SIZE, EAP_CODE_REQUEST, id);
+ if (req == NULL) {
+ eap_pwd_state(data, FAILURE);
+ return NULL;
+ }
+ switch (data->state) {
+ case PWD_ID_Req:
+ wpabuf_put_u8(req, EAP_PWD_OPCODE_ID_EXCH);
+ break;
+ case PWD_Commit_Req:
+ wpabuf_put_u8(req, EAP_PWD_OPCODE_COMMIT_EXCH);
+ break;
+ case PWD_Confirm_Req:
+ wpabuf_put_u8(req, EAP_PWD_OPCODE_CONFIRM_EXCH);
+ break;
+ default:
+ eap_pwd_state(data, FAILURE); /* just to be sure */
+ wpabuf_free(req);
+ return NULL;
+ }
+ return req;
+ }
+
+ /*
+ * build the data portion of a request
+ */
+ switch (data->state) {
+ case PWD_ID_Req:
+ eap_pwd_build_id_req(sm, data, id);
+ lm_exch = EAP_PWD_OPCODE_ID_EXCH;
+ break;
+ case PWD_Commit_Req:
+ eap_pwd_build_commit_req(sm, data, id);
+ lm_exch = EAP_PWD_OPCODE_COMMIT_EXCH;
+ break;
+ case PWD_Confirm_Req:
+ eap_pwd_build_confirm_req(sm, data, id);
+ lm_exch = EAP_PWD_OPCODE_CONFIRM_EXCH;
+ break;
+ default:
+ wpa_printf(MSG_INFO, "EAP-pwd: Unknown state %d in build_req",
+ data->state);
+ eap_pwd_state(data, FAILURE);
+ lm_exch = 0; /* hush now, sweet compiler */
+ break;
+ }
+
+ if (data->state == FAILURE)
+ return NULL;
+
+ /*
+ * determine whether that data needs to be fragmented
+ */
+ len = wpabuf_len(data->outbuf) - data->out_frag_pos;
+ if ((len + EAP_PWD_HDR_SIZE) > data->mtu) {
+ len = data->mtu - EAP_PWD_HDR_SIZE;
+ EAP_PWD_SET_MORE_BIT(lm_exch);
+ /*
+ * if this is the first fragment, need to set the M bit
+ * and add the total length to the eap_pwd_hdr
+ */
+ if (data->out_frag_pos == 0) {
+ EAP_PWD_SET_LENGTH_BIT(lm_exch);
+ totlen = wpabuf_len(data->outbuf) +
+ EAP_PWD_HDR_SIZE + sizeof(u16);
+ len -= sizeof(u16);
+ wpa_printf(MSG_DEBUG, "EAP-pwd: Fragmenting output, "
+ "total length = %d", totlen);
+ }
+ wpa_printf(MSG_DEBUG, "EAP-pwd: Send a %d byte fragment",
+ (int) len);
+ }
+
+ /*
+ * alloc an eap request and populate it with the data
+ */
+ req = eap_msg_alloc(EAP_VENDOR_IETF, EAP_TYPE_PWD,
+ EAP_PWD_HDR_SIZE + len +
+ (totlen ? sizeof(u16) : 0),
+ EAP_CODE_REQUEST, id);
+ if (req == NULL) {
+ eap_pwd_state(data, FAILURE);
+ return NULL;
+ }
+
+ wpabuf_put_u8(req, lm_exch);
+ if (EAP_PWD_GET_LENGTH_BIT(lm_exch))
+ wpabuf_put_be16(req, totlen);
+
+ buf = wpabuf_head_u8(data->outbuf);
+ wpabuf_put_data(req, buf + data->out_frag_pos, len);
+ data->out_frag_pos += len;
+ /*
+ * either not fragged or last fragment, either way free up the data
+ */
+ if (data->out_frag_pos >= wpabuf_len(data->outbuf)) {
+ wpabuf_free(data->outbuf);
+ data->out_frag_pos = 0;
+ }
+
+ return req;
+}
+
+
+static Boolean eap_pwd_check(struct eap_sm *sm, void *priv,
+ struct wpabuf *respData)
+{
+ struct eap_pwd_data *data = priv;
+ const u8 *pos;
+ size_t len;
+
+ pos = eap_hdr_validate(EAP_VENDOR_IETF, EAP_TYPE_PWD, respData, &len);
+ if (pos == NULL || len < 1) {
+ wpa_printf(MSG_INFO, "EAP-pwd: Invalid frame");
+ return TRUE;
+ }
+
+ wpa_printf(MSG_DEBUG, "EAP-pwd: Received frame: exch = %d, len = %d",
+ EAP_PWD_GET_EXCHANGE(*pos), (int) len);
+
+ if (data->state == PWD_ID_Req &&
+ ((EAP_PWD_GET_EXCHANGE(*pos)) == EAP_PWD_OPCODE_ID_EXCH))
+ return FALSE;
+
+ if (data->state == PWD_Commit_Req &&
+ ((EAP_PWD_GET_EXCHANGE(*pos)) == EAP_PWD_OPCODE_COMMIT_EXCH))
+ return FALSE;
+
+ if (data->state == PWD_Confirm_Req &&
+ ((EAP_PWD_GET_EXCHANGE(*pos)) == EAP_PWD_OPCODE_CONFIRM_EXCH))
+ return FALSE;
+
+ wpa_printf(MSG_INFO, "EAP-pwd: Unexpected opcode=%d in state=%d",
+ *pos, data->state);
+
+ return TRUE;
+}
+
+
+static void eap_pwd_process_id_resp(struct eap_sm *sm,
+ struct eap_pwd_data *data,
+ const u8 *payload, size_t payload_len)
+{
+ struct eap_pwd_id *id;
+
+ if (payload_len < sizeof(struct eap_pwd_id)) {
+ wpa_printf(MSG_INFO, "EAP-pwd: Invalid ID response");
+ return;
+ }
+
+ id = (struct eap_pwd_id *) payload;
+ if ((data->group_num != be_to_host16(id->group_num)) ||
+ (id->random_function != EAP_PWD_DEFAULT_RAND_FUNC) ||
+ (os_memcmp(id->token, (u8 *)&data->token, sizeof(data->token))) ||
+ (id->prf != EAP_PWD_DEFAULT_PRF)) {
+ wpa_printf(MSG_INFO, "EAP-pwd: peer changed parameters");
+ eap_pwd_state(data, FAILURE);
+ return;
+ }
+ data->id_peer = os_malloc(payload_len - sizeof(struct eap_pwd_id));
+ if (data->id_peer == NULL) {
+ wpa_printf(MSG_INFO, "EAP-PWD: memory allocation id fail");
+ return;
+ }
+ data->id_peer_len = payload_len - sizeof(struct eap_pwd_id);
+ os_memcpy(data->id_peer, id->identity, data->id_peer_len);
+ wpa_hexdump_ascii(MSG_DEBUG, "EAP-PWD (server): peer sent id of",
+ data->id_peer, data->id_peer_len);
+
+ if ((data->grp = os_malloc(sizeof(EAP_PWD_group))) == NULL) {
+ wpa_printf(MSG_INFO, "EAP-PWD: failed to allocate memory for "
+ "group");
+ return;
+ }
+ if (compute_password_element(data->grp, data->group_num,
+ data->password, data->password_len,
+ data->id_server, data->id_server_len,
+ data->id_peer, data->id_peer_len,
+ (u8 *) &data->token)) {
+ wpa_printf(MSG_INFO, "EAP-PWD (server): unable to compute "
+ "PWE");
+ return;
+ }
+ wpa_printf(MSG_DEBUG, "EAP-PWD (server): computed %d bit PWE...",
+ BN_num_bits(data->grp->prime));
+
+ eap_pwd_state(data, PWD_Commit_Req);
+}
+
+
+static void
+eap_pwd_process_commit_resp(struct eap_sm *sm, struct eap_pwd_data *data,
+ const u8 *payload, size_t payload_len)
+{
+ u8 *ptr;
+ BIGNUM *x = NULL, *y = NULL, *cofactor = NULL;
+ EC_POINT *K = NULL, *point = NULL;
+ int res = 0;
+
+ wpa_printf(MSG_DEBUG, "EAP-pwd: Received commit response");
+
+ if (((data->peer_scalar = BN_new()) == NULL) ||
+ ((data->k = BN_new()) == NULL) ||
+ ((cofactor = BN_new()) == NULL) ||
+ ((x = BN_new()) == NULL) ||
+ ((y = BN_new()) == NULL) ||
+ ((point = EC_POINT_new(data->grp->group)) == NULL) ||
+ ((K = EC_POINT_new(data->grp->group)) == NULL) ||
+ ((data->peer_element = EC_POINT_new(data->grp->group)) == NULL)) {
+ wpa_printf(MSG_INFO, "EAP-PWD (server): peer data allocation "
+ "fail");
+ goto fin;
+ }
+
+ if (!EC_GROUP_get_cofactor(data->grp->group, cofactor, NULL)) {
+ wpa_printf(MSG_INFO, "EAP-PWD (server): unable to get "
+ "cofactor for curve");
+ goto fin;
+ }
+
+ /* element, x then y, followed by scalar */
+ ptr = (u8 *) payload;
+ BN_bin2bn(ptr, BN_num_bytes(data->grp->prime), x);
+ ptr += BN_num_bytes(data->grp->prime);
+ BN_bin2bn(ptr, BN_num_bytes(data->grp->prime), y);
+ ptr += BN_num_bytes(data->grp->prime);
+ BN_bin2bn(ptr, BN_num_bytes(data->grp->order), data->peer_scalar);
+ if (!EC_POINT_set_affine_coordinates_GFp(data->grp->group,
+ data->peer_element, x, y,
+ data->bnctx)) {
+ wpa_printf(MSG_INFO, "EAP-PWD (server): setting peer element "
+ "fail");
+ goto fin;
+ }
+
+ /* check to ensure peer's element is not in a small sub-group */
+ if (BN_cmp(cofactor, BN_value_one())) {
+ if (!EC_POINT_mul(data->grp->group, point, NULL,
+ data->peer_element, cofactor, NULL)) {
+ wpa_printf(MSG_INFO, "EAP-PWD (server): cannot "
+ "multiply peer element by order");
+ goto fin;
+ }
+ if (EC_POINT_is_at_infinity(data->grp->group, point)) {
+ wpa_printf(MSG_INFO, "EAP-PWD (server): peer element "
+ "is at infinity!\n");
+ goto fin;
+ }
+ }
+
+ /* compute the shared key, k */
+ if ((!EC_POINT_mul(data->grp->group, K, NULL, data->grp->pwe,
+ data->peer_scalar, data->bnctx)) ||
+ (!EC_POINT_add(data->grp->group, K, K, data->peer_element,
+ data->bnctx)) ||
+ (!EC_POINT_mul(data->grp->group, K, NULL, K, data->private_value,
+ data->bnctx))) {
+ wpa_printf(MSG_INFO, "EAP-PWD (server): computing shared key "
+ "fail");
+ goto fin;
+ }
+
+ /* ensure that the shared key isn't in a small sub-group */
+ if (BN_cmp(cofactor, BN_value_one())) {
+ if (!EC_POINT_mul(data->grp->group, K, NULL, K, cofactor,
+ NULL)) {
+ wpa_printf(MSG_INFO, "EAP-PWD (server): cannot "
+ "multiply shared key point by order!\n");
+ goto fin;
+ }
+ }
+
+ /*
+ * This check is strictly speaking just for the case above where
+ * co-factor > 1 but it was suggested that even though this is probably
+ * never going to happen it is a simple and safe check "just to be
+ * sure" so let's be safe.
+ */
+ if (EC_POINT_is_at_infinity(data->grp->group, K)) {
+ wpa_printf(MSG_INFO, "EAP-PWD (server): shared key point is "
+ "at infinity");
+ goto fin;
+ }
+ if (!EC_POINT_get_affine_coordinates_GFp(data->grp->group, K, data->k,
+ NULL, data->bnctx)) {
+ wpa_printf(MSG_INFO, "EAP-PWD (server): unable to extract "
+ "shared secret from secret point");
+ goto fin;
+ }
+ res = 1;
+
+fin:
+ EC_POINT_free(K);
+ EC_POINT_free(point);
+ BN_free(cofactor);
+ BN_free(x);
+ BN_free(y);
+
+ if (res)
+ eap_pwd_state(data, PWD_Confirm_Req);
+ else
+ eap_pwd_state(data, FAILURE);
+}
+
+
+static void
+eap_pwd_process_confirm_resp(struct eap_sm *sm, struct eap_pwd_data *data,
+ const u8 *payload, size_t payload_len)
+{
+ BIGNUM *x = NULL, *y = NULL;
+ struct crypto_hash *hash;
+ u32 cs;
+ u16 grp;
+ u8 conf[SHA256_MAC_LEN], *cruft = NULL, *ptr;
+ int offset;
+
+ /* build up the ciphersuite: group | random_function | prf */
+ grp = htons(data->group_num);
+ ptr = (u8 *) &cs;
+ os_memcpy(ptr, &grp, sizeof(u16));
+ ptr += sizeof(u16);
+ *ptr = EAP_PWD_DEFAULT_RAND_FUNC;
+ ptr += sizeof(u8);
+ *ptr = EAP_PWD_DEFAULT_PRF;
+
+ /* each component of the cruft will be at most as big as the prime */
+ if (((cruft = os_malloc(BN_num_bytes(data->grp->prime))) == NULL) ||
+ ((x = BN_new()) == NULL) || ((y = BN_new()) == NULL)) {
+ wpa_printf(MSG_INFO, "EAP-PWD (peer): allocation fail");
+ goto fin;
+ }
+
+ /*
+ * commit is H(k | peer_element | peer_scalar | server_element |
+ * server_scalar | ciphersuite)
+ */
+ hash = eap_pwd_h_init();
+ if (hash == NULL)
+ goto fin;
+
+ /* k */
+ os_memset(cruft, 0, BN_num_bytes(data->grp->prime));
+ offset = BN_num_bytes(data->grp->prime) - BN_num_bytes(data->k);
+ BN_bn2bin(data->k, cruft + offset);
+ eap_pwd_h_update(hash, cruft, BN_num_bytes(data->grp->prime));
+
+ /* peer element: x, y */
+ if (!EC_POINT_get_affine_coordinates_GFp(data->grp->group,
+ data->peer_element, x, y,
+ data->bnctx)) {
+ wpa_printf(MSG_INFO, "EAP-PWD (server): confirm point "
+ "assignment fail");
+ goto fin;
+ }
+ os_memset(cruft, 0, BN_num_bytes(data->grp->prime));
+ offset = BN_num_bytes(data->grp->prime) - BN_num_bytes(x);
+ BN_bn2bin(x, cruft + offset);
+ eap_pwd_h_update(hash, cruft, BN_num_bytes(data->grp->prime));
+ os_memset(cruft, 0, BN_num_bytes(data->grp->prime));
+ offset = BN_num_bytes(data->grp->prime) - BN_num_bytes(y);
+ BN_bn2bin(y, cruft + offset);
+ eap_pwd_h_update(hash, cruft, BN_num_bytes(data->grp->prime));
+
+ /* peer scalar */
+ os_memset(cruft, 0, BN_num_bytes(data->grp->prime));
+ offset = BN_num_bytes(data->grp->order) -
+ BN_num_bytes(data->peer_scalar);
+ BN_bn2bin(data->peer_scalar, cruft + offset);
+ eap_pwd_h_update(hash, cruft, BN_num_bytes(data->grp->order));
+
+ /* server element: x, y */
+ if (!EC_POINT_get_affine_coordinates_GFp(data->grp->group,
+ data->my_element, x, y,
+ data->bnctx)) {
+ wpa_printf(MSG_INFO, "EAP-PWD (server): confirm point "
+ "assignment fail");
+ goto fin;
+ }
+
+ os_memset(cruft, 0, BN_num_bytes(data->grp->prime));
+ offset = BN_num_bytes(data->grp->prime) - BN_num_bytes(x);
+ BN_bn2bin(x, cruft + offset);
+ eap_pwd_h_update(hash, cruft, BN_num_bytes(data->grp->prime));
+ os_memset(cruft, 0, BN_num_bytes(data->grp->prime));
+ offset = BN_num_bytes(data->grp->prime) - BN_num_bytes(y);
+ BN_bn2bin(y, cruft + offset);
+ eap_pwd_h_update(hash, cruft, BN_num_bytes(data->grp->prime));
+
+ /* server scalar */
+ os_memset(cruft, 0, BN_num_bytes(data->grp->prime));
+ offset = BN_num_bytes(data->grp->order) -
+ BN_num_bytes(data->my_scalar);
+ BN_bn2bin(data->my_scalar, cruft + offset);
+ eap_pwd_h_update(hash, cruft, BN_num_bytes(data->grp->order));
+
+ /* ciphersuite */
+ os_memset(cruft, 0, BN_num_bytes(data->grp->prime));
+ eap_pwd_h_update(hash, (u8 *) &cs, sizeof(u32));
+
+ /* all done */
+ eap_pwd_h_final(hash, conf);
+
+ ptr = (u8 *) payload;
+ if (os_memcmp(conf, ptr, SHA256_MAC_LEN)) {
+ wpa_printf(MSG_INFO, "EAP-PWD (server): confirm did not "
+ "verify");
+ goto fin;
+ }
+
+ wpa_printf(MSG_DEBUG, "EAP-pwd (server): confirm verified");
+ if (compute_keys(data->grp, data->bnctx, data->k,
+ data->peer_scalar, data->my_scalar, conf,
+ data->my_confirm, &cs, data->msk, data->emsk) < 0)
+ eap_pwd_state(data, FAILURE);
+ else
+ eap_pwd_state(data, SUCCESS);
+
+fin:
+ os_free(cruft);
+ BN_free(x);
+ BN_free(y);
+}
+
+
+static void eap_pwd_process(struct eap_sm *sm, void *priv,
+ struct wpabuf *respData)
+{
+ struct eap_pwd_data *data = priv;
+ const u8 *pos;
+ size_t len;
+ u8 lm_exch;
+ u16 tot_len;
+
+ pos = eap_hdr_validate(EAP_VENDOR_IETF, EAP_TYPE_PWD, respData, &len);
+ if ((pos == NULL) || (len < 1)) {
+ wpa_printf(MSG_INFO, "Bad EAP header! pos %s and len = %d",
+ (pos == NULL) ? "is NULL" : "is not NULL",
+ (int) len);
+ return;
+ }
+
+ lm_exch = *pos;
+ pos++; /* skip over the bits and the exch */
+ len--;
+
+ /*
+ * if we're fragmenting then this should be an ACK with no data,
+ * just return and continue fragmenting in the "build" section above
+ */
+ if (data->out_frag_pos) {
+ if (len > 1)
+ wpa_printf(MSG_INFO, "EAP-pwd: Bad response! "
+ "Fragmenting but not an ACK");
+ else
+ wpa_printf(MSG_DEBUG, "EAP-pwd: received ACK from "
+ "peer");
+ return;
+ }
+ /*
+ * if we're receiving fragmented packets then we need to buffer...
+ *
+ * the first fragment has a total length
+ */
+ if (EAP_PWD_GET_LENGTH_BIT(lm_exch)) {
+ tot_len = WPA_GET_BE16(pos);
+ wpa_printf(MSG_DEBUG, "EAP-pwd: Incoming fragments, total "
+ "length = %d", tot_len);
+ data->inbuf = wpabuf_alloc(tot_len);
+ if (data->inbuf == NULL) {
+ wpa_printf(MSG_INFO, "EAP-pwd: Out of memory to "
+ "buffer fragments!");
+ return;
+ }
+ pos += sizeof(u16);
+ len -= sizeof(u16);
+ }
+ /*
+ * the first and all intermediate fragments have the M bit set
+ */
+ if (EAP_PWD_GET_MORE_BIT(lm_exch)) {
+ if ((data->in_frag_pos + len) > wpabuf_size(data->inbuf)) {
+ wpa_printf(MSG_DEBUG, "EAP-pwd: Buffer overflow "
+ "attack detected! (%d+%d > %d)",
+ (int) data->in_frag_pos, (int) len,
+ (int) wpabuf_size(data->inbuf));
+ eap_pwd_state(data, FAILURE);
+ return;
+ }
+ wpabuf_put_data(data->inbuf, pos, len);
+ data->in_frag_pos += len;
+ wpa_printf(MSG_DEBUG, "EAP-pwd: Got a %d byte fragment",
+ (int) len);
+ return;
+ }
+ /*
+ * last fragment won't have the M bit set (but we're obviously
+ * buffering fragments so that's how we know it's the last)
+ */
+ if (data->in_frag_pos) {
+ wpabuf_put_data(data->inbuf, pos, len);
+ data->in_frag_pos += len;
+ pos = wpabuf_head_u8(data->inbuf);
+ len = data->in_frag_pos;
+ wpa_printf(MSG_DEBUG, "EAP-pwd: Last fragment, %d bytes",
+ (int) len);
+ }
+ switch (EAP_PWD_GET_EXCHANGE(lm_exch)) {
+ case EAP_PWD_OPCODE_ID_EXCH:
+ eap_pwd_process_id_resp(sm, data, pos, len);
+ break;
+ case EAP_PWD_OPCODE_COMMIT_EXCH:
+ eap_pwd_process_commit_resp(sm, data, pos, len);
+ break;
+ case EAP_PWD_OPCODE_CONFIRM_EXCH:
+ eap_pwd_process_confirm_resp(sm, data, pos, len);
+ break;
+ }
+ /*
+ * if we had been buffering fragments, here's a great place
+ * to clean up
+ */
+ if (data->in_frag_pos) {
+ wpabuf_free(data->inbuf);
+ data->in_frag_pos = 0;
+ }
+}
+
+
+static u8 * eap_pwd_getkey(struct eap_sm *sm, void *priv, size_t *len)
+{
+ struct eap_pwd_data *data = priv;
+ u8 *key;
+
+ if (data->state != SUCCESS)
+ return NULL;
+
+ key = os_malloc(EAP_MSK_LEN);
+ if (key == NULL)
+ return NULL;
+
+ os_memcpy(key, data->msk, EAP_MSK_LEN);
+ *len = EAP_MSK_LEN;
+
+ return key;
+}
+
+
+static u8 * eap_pwd_get_emsk(struct eap_sm *sm, void *priv, size_t *len)
+{
+ struct eap_pwd_data *data = priv;
+ u8 *key;
+
+ if (data->state != SUCCESS)
+ return NULL;
+
+ key = os_malloc(EAP_EMSK_LEN);
+ if (key == NULL)
+ return NULL;
+
+ os_memcpy(key, data->emsk, EAP_EMSK_LEN);
+ *len = EAP_EMSK_LEN;
+
+ return key;
+}
+
+
+static Boolean eap_pwd_is_success(struct eap_sm *sm, void *priv)
+{
+ struct eap_pwd_data *data = priv;
+ return data->state == SUCCESS;
+}
+
+
+static Boolean eap_pwd_is_done(struct eap_sm *sm, void *priv)
+{
+ struct eap_pwd_data *data = priv;
+ return (data->state == SUCCESS) || (data->state == FAILURE);
+}
+
+
+int eap_server_pwd_register(void)
+{
+ struct eap_method *eap;
+ int ret;
+ struct timeval tp;
+ struct timezone tz;
+ u32 sr;
+
+ EVP_add_digest(EVP_sha256());
+
+ sr = 0xdeaddada;
+ (void) gettimeofday(&tp, &tz);
+ sr ^= (tp.tv_sec ^ tp.tv_usec);
+ srandom(sr);
+
+ eap = eap_server_method_alloc(EAP_SERVER_METHOD_INTERFACE_VERSION,
+ EAP_VENDOR_IETF, EAP_TYPE_PWD,
+ "PWD");
+ if (eap == NULL)
+ return -1;
+
+ eap->init = eap_pwd_init;
+ eap->reset = eap_pwd_reset;
+ eap->buildReq = eap_pwd_build_req;
+ eap->check = eap_pwd_check;
+ eap->process = eap_pwd_process;
+ eap->isDone = eap_pwd_is_done;
+ eap->getKey = eap_pwd_getkey;
+ eap->get_emsk = eap_pwd_get_emsk;
+ eap->isSuccess = eap_pwd_is_success;
+
+ ret = eap_server_method_register(eap);
+ if (ret)
+ eap_server_method_free(eap);
+ return ret;
+}
+
diff --git a/contrib/wpa/src/eap_server/eap_server_sake.c b/contrib/wpa/src/eap_server/eap_server_sake.c
index ce4848f..f72e1bf 100644
--- a/contrib/wpa/src/eap_server/eap_server_sake.c
+++ b/contrib/wpa/src/eap_server/eap_server_sake.c
@@ -2,19 +2,14 @@
* hostapd / EAP-SAKE (RFC 4763) server
* Copyright (c) 2006-2007, Jouni Malinen <j@w1.fi>
*
- * This program is free software; you can redistribute it and/or modify
- * it under the terms of the GNU General Public License version 2 as
- * published by the Free Software Foundation.
- *
- * Alternatively, this software may be distributed under the terms of BSD
- * license.
- *
- * See README and COPYING for more details.
+ * This software may be distributed under the terms of the BSD license.
+ * See README for more details.
*/
#include "includes.h"
#include "common.h"
+#include "crypto/random.h"
#include "eap_server/eap_i.h"
#include "eap_common/eap_sake_common.h"
@@ -166,7 +161,7 @@ static struct wpabuf * eap_sake_build_challenge(struct eap_sm *sm,
wpa_printf(MSG_DEBUG, "EAP-SAKE: Request/Challenge");
- if (os_get_random(data->rand_s, EAP_SAKE_RAND_LEN)) {
+ if (random_get_bytes(data->rand_s, EAP_SAKE_RAND_LEN)) {
wpa_printf(MSG_ERROR, "EAP-SAKE: Failed to get random data");
data->state = FAILURE;
return NULL;
diff --git a/contrib/wpa/src/eap_server/eap_server_sim.c b/contrib/wpa/src/eap_server/eap_server_sim.c
index 436c655..b531241 100644
--- a/contrib/wpa/src/eap_server/eap_server_sim.c
+++ b/contrib/wpa/src/eap_server/eap_server_sim.c
@@ -1,20 +1,15 @@
/*
* hostapd / EAP-SIM (RFC 4186)
- * Copyright (c) 2005-2008, Jouni Malinen <j@w1.fi>
+ * Copyright (c) 2005-2012, Jouni Malinen <j@w1.fi>
*
- * This program is free software; you can redistribute it and/or modify
- * it under the terms of the GNU General Public License version 2 as
- * published by the Free Software Foundation.
- *
- * Alternatively, this software may be distributed under the terms of BSD
- * license.
- *
- * See README and COPYING for more details.
+ * This software may be distributed under the terms of the BSD license.
+ * See README for more details.
*/
#include "includes.h"
#include "common.h"
+#include "crypto/random.h"
#include "eap_server/eap_i.h"
#include "eap_common/eap_sim_common.h"
#include "eap_server/eap_sim_db.h"
@@ -41,6 +36,8 @@ struct eap_sim_data {
struct eap_sim_reauth *reauth;
u16 notification;
int use_result_ind;
+ int start_round;
+ char permanent[20]; /* Permanent username */
};
@@ -110,17 +107,33 @@ static struct wpabuf * eap_sim_build_start(struct eap_sm *sm,
wpa_printf(MSG_DEBUG, "EAP-SIM: Generating Start");
msg = eap_sim_msg_init(EAP_CODE_REQUEST, id, EAP_TYPE_SIM,
EAP_SIM_SUBTYPE_START);
- if (eap_sim_db_identity_known(sm->eap_sim_db_priv, sm->identity,
- sm->identity_len)) {
- wpa_printf(MSG_DEBUG, " AT_PERMANENT_ID_REQ");
- eap_sim_msg_add(msg, EAP_SIM_AT_PERMANENT_ID_REQ, 0, NULL, 0);
- } else {
+ data->start_round++;
+ if (data->start_round == 1) {
/*
* RFC 4186, Chap. 4.2.4 recommends that identity from EAP is
* ignored and the SIM/Start is used to request the identity.
*/
wpa_printf(MSG_DEBUG, " AT_ANY_ID_REQ");
eap_sim_msg_add(msg, EAP_SIM_AT_ANY_ID_REQ, 0, NULL, 0);
+ } else if (data->start_round > 3) {
+ /* Cannot use more than three rounds of Start messages */
+ eap_sim_msg_free(msg);
+ return NULL;
+ } else if (data->start_round == 0) {
+ /*
+ * This is a special case that is used to recover from
+ * AT_COUNTER_TOO_SMALL during re-authentication. Since we
+ * already know the identity of the peer, there is no need to
+ * request any identity in this case.
+ */
+ } else if (sm->identity && sm->identity_len > 0 &&
+ sm->identity[0] == EAP_SIM_REAUTH_ID_PREFIX) {
+ /* Reauth id may have expired - try fullauth */
+ wpa_printf(MSG_DEBUG, " AT_FULLAUTH_ID_REQ");
+ eap_sim_msg_add(msg, EAP_SIM_AT_FULLAUTH_ID_REQ, 0, NULL, 0);
+ } else {
+ wpa_printf(MSG_DEBUG, " AT_PERMANENT_ID_REQ");
+ eap_sim_msg_add(msg, EAP_SIM_AT_PERMANENT_ID_REQ, 0, NULL, 0);
}
wpa_printf(MSG_DEBUG, " AT_VERSION_LIST");
ver[0] = 0;
@@ -136,12 +149,19 @@ static int eap_sim_build_encr(struct eap_sm *sm, struct eap_sim_data *data,
const u8 *nonce_s)
{
os_free(data->next_pseudonym);
- data->next_pseudonym =
- eap_sim_db_get_next_pseudonym(sm->eap_sim_db_priv, 0);
+ if (nonce_s == NULL) {
+ data->next_pseudonym =
+ eap_sim_db_get_next_pseudonym(sm->eap_sim_db_priv,
+ EAP_SIM_DB_SIM);
+ } else {
+ /* Do not update pseudonym during re-authentication */
+ data->next_pseudonym = NULL;
+ }
os_free(data->next_reauth_id);
if (data->counter <= EAP_SIM_MAX_FAST_REAUTHS) {
data->next_reauth_id =
- eap_sim_db_get_next_reauth_id(sm->eap_sim_db_priv, 0);
+ eap_sim_db_get_next_reauth_id(sm->eap_sim_db_priv,
+ EAP_SIM_DB_SIM);
} else {
wpa_printf(MSG_DEBUG, "EAP-SIM: Max fast re-authentication "
"count exceeded - force full authentication");
@@ -232,7 +252,7 @@ static struct wpabuf * eap_sim_build_reauth(struct eap_sm *sm,
wpa_printf(MSG_DEBUG, "EAP-SIM: Generating Re-authentication");
- if (os_get_random(data->nonce_s, EAP_SIM_NONCE_S_LEN))
+ if (random_get_bytes(data->nonce_s, EAP_SIM_NONCE_S_LEN))
return NULL;
wpa_hexdump_key(MSG_MSGDUMP, "EAP-SIM: NONCE_S",
data->nonce_s, EAP_SIM_NONCE_S_LEN);
@@ -326,18 +346,22 @@ static struct wpabuf * eap_sim_buildReq(struct eap_sm *sm, void *priv, u8 id)
static Boolean eap_sim_check(struct eap_sm *sm, void *priv,
struct wpabuf *respData)
{
- struct eap_sim_data *data = priv;
const u8 *pos;
size_t len;
- u8 subtype;
pos = eap_hdr_validate(EAP_VENDOR_IETF, EAP_TYPE_SIM, respData, &len);
if (pos == NULL || len < 3) {
wpa_printf(MSG_INFO, "EAP-SIM: Invalid frame");
return TRUE;
}
- subtype = *pos;
+ return FALSE;
+}
+
+
+static Boolean eap_sim_unexpected_subtype(struct eap_sim_data *data,
+ u8 subtype)
+{
if (subtype == EAP_SIM_SUBTYPE_CLIENT_ERROR)
return FALSE;
@@ -391,85 +415,113 @@ static void eap_sim_process_start(struct eap_sm *sm,
struct wpabuf *respData,
struct eap_sim_attrs *attr)
{
- const u8 *identity;
size_t identity_len;
u8 ver_list[2];
+ u8 *new_identity;
+ char *username;
wpa_printf(MSG_DEBUG, "EAP-SIM: Receive start response");
- if (attr->identity) {
- os_free(sm->identity);
- sm->identity = os_malloc(attr->identity_len);
- if (sm->identity) {
- os_memcpy(sm->identity, attr->identity,
- attr->identity_len);
- sm->identity_len = attr->identity_len;
- }
+ if (data->start_round == 0) {
+ /*
+ * Special case for AT_COUNTER_TOO_SMALL recovery - no identity
+ * was requested since we already know it.
+ */
+ goto skip_id_update;
}
- identity = NULL;
- identity_len = 0;
-
- if (sm->identity && sm->identity_len > 0 &&
- sm->identity[0] == EAP_SIM_PERMANENT_PREFIX) {
- identity = sm->identity;
- identity_len = sm->identity_len;
- } else {
- identity = eap_sim_db_get_permanent(sm->eap_sim_db_priv,
- sm->identity,
- sm->identity_len,
- &identity_len);
- if (identity == NULL) {
- data->reauth = eap_sim_db_get_reauth_entry(
- sm->eap_sim_db_priv, sm->identity,
- sm->identity_len);
- if (data->reauth) {
- wpa_printf(MSG_DEBUG, "EAP-SIM: Using fast "
- "re-authentication");
- identity = data->reauth->identity;
- identity_len = data->reauth->identity_len;
- data->counter = data->reauth->counter;
- os_memcpy(data->mk, data->reauth->mk,
- EAP_SIM_MK_LEN);
- }
- }
+ /*
+ * We always request identity in SIM/Start, so the peer is required to
+ * have replied with one.
+ */
+ if (!attr->identity || attr->identity_len == 0) {
+ wpa_printf(MSG_DEBUG, "EAP-SIM: Peer did not provide any "
+ "identity");
+ goto failed;
}
- if (identity == NULL) {
- wpa_printf(MSG_DEBUG, "EAP-SIM: Could not get proper permanent"
- " user name");
- eap_sim_state(data, FAILURE);
- return;
- }
+ new_identity = os_malloc(attr->identity_len);
+ if (new_identity == NULL)
+ goto failed;
+ os_free(sm->identity);
+ sm->identity = new_identity;
+ os_memcpy(sm->identity, attr->identity, attr->identity_len);
+ sm->identity_len = attr->identity_len;
wpa_hexdump_ascii(MSG_DEBUG, "EAP-SIM: Identity",
- identity, identity_len);
-
- if (data->reauth) {
+ sm->identity, sm->identity_len);
+ username = sim_get_username(sm->identity, sm->identity_len);
+ if (username == NULL)
+ goto failed;
+
+ if (username[0] == EAP_SIM_REAUTH_ID_PREFIX) {
+ wpa_printf(MSG_DEBUG, "EAP-SIM: Reauth username '%s'",
+ username);
+ data->reauth = eap_sim_db_get_reauth_entry(
+ sm->eap_sim_db_priv, username);
+ os_free(username);
+ if (data->reauth == NULL) {
+ wpa_printf(MSG_DEBUG, "EAP-SIM: Unknown reauth "
+ "identity - request full auth identity");
+ /* Remain in START state for another round */
+ return;
+ }
+ wpa_printf(MSG_DEBUG, "EAP-SIM: Using fast re-authentication");
+ os_strlcpy(data->permanent, data->reauth->permanent,
+ sizeof(data->permanent));
+ data->counter = data->reauth->counter;
+ os_memcpy(data->mk, data->reauth->mk, EAP_SIM_MK_LEN);
eap_sim_state(data, REAUTH);
return;
}
+ if (username[0] == EAP_SIM_PSEUDONYM_PREFIX) {
+ const char *permanent;
+ wpa_printf(MSG_DEBUG, "EAP-SIM: Pseudonym username '%s'",
+ username);
+ permanent = eap_sim_db_get_permanent(
+ sm->eap_sim_db_priv, username);
+ os_free(username);
+ if (permanent == NULL) {
+ wpa_printf(MSG_DEBUG, "EAP-SIM: Unknown pseudonym "
+ "identity - request permanent identity");
+ /* Remain in START state for another round */
+ return;
+ }
+ os_strlcpy(data->permanent, permanent,
+ sizeof(data->permanent));
+ } else if (username[0] == EAP_SIM_PERMANENT_PREFIX) {
+ wpa_printf(MSG_DEBUG, "EAP-SIM: Permanent username '%s'",
+ username);
+ os_strlcpy(data->permanent, username, sizeof(data->permanent));
+ os_free(username);
+ } else {
+ wpa_printf(MSG_DEBUG, "EAP-SIM: Unrecognized username '%s'",
+ username);
+ os_free(username);
+ goto failed;
+ }
+
+skip_id_update:
+ /* Full authentication */
+
if (attr->nonce_mt == NULL || attr->selected_version < 0) {
wpa_printf(MSG_DEBUG, "EAP-SIM: Start/Response missing "
"required attributes");
- eap_sim_state(data, FAILURE);
- return;
+ goto failed;
}
if (!eap_sim_supported_ver(data, attr->selected_version)) {
wpa_printf(MSG_DEBUG, "EAP-SIM: Peer selected unsupported "
"version %d", attr->selected_version);
- eap_sim_state(data, FAILURE);
- return;
+ goto failed;
}
data->counter = 0; /* reset re-auth counter since this is full auth */
data->reauth = NULL;
data->num_chal = eap_sim_db_get_gsm_triplets(
- sm->eap_sim_db_priv, identity, identity_len,
- EAP_SIM_MAX_CHAL,
+ sm->eap_sim_db_priv, data->permanent, EAP_SIM_MAX_CHAL,
(u8 *) data->rand, (u8 *) data->kc, (u8 *) data->sres, sm);
if (data->num_chal == EAP_SIM_DB_PENDING) {
wpa_printf(MSG_DEBUG, "EAP-SIM: GSM authentication triplets "
@@ -480,8 +532,7 @@ static void eap_sim_process_start(struct eap_sm *sm,
if (data->num_chal < 2) {
wpa_printf(MSG_INFO, "EAP-SIM: Failed to get GSM "
"authentication triplets for the peer");
- eap_sim_state(data, FAILURE);
- return;
+ goto failed;
}
identity_len = sm->identity_len;
@@ -502,6 +553,11 @@ static void eap_sim_process_start(struct eap_sm *sm,
data->emsk);
eap_sim_state(data, CHALLENGE);
+ return;
+
+failed:
+ data->notification = EAP_SIM_GENERAL_FAILURE_BEFORE_AUTH;
+ eap_sim_state(data, NOTIFICATION);
}
@@ -510,16 +566,14 @@ static void eap_sim_process_challenge(struct eap_sm *sm,
struct wpabuf *respData,
struct eap_sim_attrs *attr)
{
- const u8 *identity;
- size_t identity_len;
-
if (attr->mac == NULL ||
eap_sim_verify_mac(data->k_aut, respData, attr->mac,
(u8 *) data->sres,
data->num_chal * EAP_SIM_SRES_LEN)) {
wpa_printf(MSG_WARNING, "EAP-SIM: Challenge message "
"did not include valid AT_MAC");
- eap_sim_state(data, FAILURE);
+ data->notification = EAP_SIM_GENERAL_FAILURE_BEFORE_AUTH;
+ eap_sim_state(data, NOTIFICATION);
return;
}
@@ -532,22 +586,13 @@ static void eap_sim_process_challenge(struct eap_sm *sm,
} else
eap_sim_state(data, SUCCESS);
- identity = eap_sim_db_get_permanent(sm->eap_sim_db_priv, sm->identity,
- sm->identity_len, &identity_len);
- if (identity == NULL) {
- identity = sm->identity;
- identity_len = sm->identity_len;
- }
-
if (data->next_pseudonym) {
- eap_sim_db_add_pseudonym(sm->eap_sim_db_priv, identity,
- identity_len,
+ eap_sim_db_add_pseudonym(sm->eap_sim_db_priv, data->permanent,
data->next_pseudonym);
data->next_pseudonym = NULL;
}
if (data->next_reauth_id) {
- eap_sim_db_add_reauth(sm->eap_sim_db_priv, identity,
- identity_len,
+ eap_sim_db_add_reauth(sm->eap_sim_db_priv, data->permanent,
data->next_reauth_id, data->counter + 1,
data->mk);
data->next_reauth_id = NULL;
@@ -562,8 +607,6 @@ static void eap_sim_process_reauth(struct eap_sm *sm,
{
struct eap_sim_attrs eattr;
u8 *decrypted = NULL;
- const u8 *identity, *id2;
- size_t identity_len, id2_len;
if (attr->mac == NULL ||
eap_sim_verify_mac(data->k_aut, respData, attr->mac, data->nonce_s,
@@ -599,6 +642,16 @@ static void eap_sim_process_reauth(struct eap_sm *sm,
wpa_printf(MSG_DEBUG, "EAP-SIM: Re-authentication response includes "
"the correct AT_MAC");
+
+ if (eattr.counter_too_small) {
+ wpa_printf(MSG_DEBUG, "EAP-AKA: Re-authentication response "
+ "included AT_COUNTER_TOO_SMALL - starting full "
+ "authentication");
+ data->start_round = -1;
+ eap_sim_state(data, START);
+ return;
+ }
+
if (sm->eap_sim_aka_result_ind && attr->result_ind) {
data->use_result_ind = 1;
data->notification = EAP_SIM_SUCCESS;
@@ -606,29 +659,9 @@ static void eap_sim_process_reauth(struct eap_sm *sm,
} else
eap_sim_state(data, SUCCESS);
- if (data->reauth) {
- identity = data->reauth->identity;
- identity_len = data->reauth->identity_len;
- } else {
- identity = sm->identity;
- identity_len = sm->identity_len;
- }
-
- id2 = eap_sim_db_get_permanent(sm->eap_sim_db_priv, identity,
- identity_len, &id2_len);
- if (id2) {
- identity = id2;
- identity_len = id2_len;
- }
-
- if (data->next_pseudonym) {
- eap_sim_db_add_pseudonym(sm->eap_sim_db_priv, identity,
- identity_len, data->next_pseudonym);
- data->next_pseudonym = NULL;
- }
if (data->next_reauth_id) {
- eap_sim_db_add_reauth(sm->eap_sim_db_priv, identity,
- identity_len, data->next_reauth_id,
+ eap_sim_db_add_reauth(sm->eap_sim_db_priv, data->permanent,
+ data->next_reauth_id,
data->counter + 1, data->mk);
data->next_reauth_id = NULL;
} else {
@@ -639,7 +672,8 @@ static void eap_sim_process_reauth(struct eap_sm *sm,
return;
fail:
- eap_sim_state(data, FAILURE);
+ data->notification = EAP_SIM_GENERAL_FAILURE_BEFORE_AUTH;
+ eap_sim_state(data, NOTIFICATION);
eap_sim_db_remove_reauth(sm->eap_sim_db_priv, data->reauth);
data->reauth = NULL;
os_free(decrypted);
@@ -690,8 +724,24 @@ static void eap_sim_process(struct eap_sm *sm, void *priv,
subtype = *pos;
pos += 3;
+ if (eap_sim_unexpected_subtype(data, subtype)) {
+ wpa_printf(MSG_DEBUG, "EAP-SIM: Unrecognized or unexpected "
+ "EAP-SIM Subtype in EAP Response");
+ data->notification = EAP_SIM_GENERAL_FAILURE_BEFORE_AUTH;
+ eap_sim_state(data, NOTIFICATION);
+ return;
+ }
+
if (eap_sim_parse_attr(pos, end, &attr, 0, 0)) {
wpa_printf(MSG_DEBUG, "EAP-SIM: Failed to parse attributes");
+ if (subtype != EAP_SIM_SUBTYPE_CLIENT_ERROR &&
+ (data->state == START || data->state == CHALLENGE ||
+ data->state == REAUTH)) {
+ data->notification =
+ EAP_SIM_GENERAL_FAILURE_BEFORE_AUTH;
+ eap_sim_state(data, NOTIFICATION);
+ return;
+ }
eap_sim_state(data, FAILURE);
return;
}
diff --git a/contrib/wpa/src/eap_server/eap_server_tls.c b/contrib/wpa/src/eap_server/eap_server_tls.c
index c98fa18..447f47c 100644
--- a/contrib/wpa/src/eap_server/eap_server_tls.c
+++ b/contrib/wpa/src/eap_server/eap_server_tls.c
@@ -2,14 +2,8 @@
* hostapd / EAP-TLS (RFC 2716)
* Copyright (c) 2004-2008, Jouni Malinen <j@w1.fi>
*
- * This program is free software; you can redistribute it and/or modify
- * it under the terms of the GNU General Public License version 2 as
- * published by the Free Software Foundation.
- *
- * Alternatively, this software may be distributed under the terms of BSD
- * license.
- *
- * See README and COPYING for more details.
+ * This software may be distributed under the terms of the BSD license.
+ * See README for more details.
*/
#include "includes.h"
@@ -27,6 +21,7 @@ struct eap_tls_data {
struct eap_ssl_data ssl;
enum { START, CONTINUE, SUCCESS, FAILURE } state;
int established;
+ u8 eap_type;
};
@@ -71,10 +66,34 @@ static void * eap_tls_init(struct eap_sm *sm)
return NULL;
}
+ data->eap_type = EAP_TYPE_TLS;
+
return data;
}
+#ifdef EAP_SERVER_UNAUTH_TLS
+static void * eap_unauth_tls_init(struct eap_sm *sm)
+{
+ struct eap_tls_data *data;
+
+ data = os_zalloc(sizeof(*data));
+ if (data == NULL)
+ return NULL;
+ data->state = START;
+
+ if (eap_server_tls_ssl_init(sm, &data->ssl, 0)) {
+ wpa_printf(MSG_INFO, "EAP-TLS: Failed to initialize SSL.");
+ eap_tls_reset(sm, data);
+ return NULL;
+ }
+
+ data->eap_type = EAP_UNAUTH_TLS_TYPE;
+ return data;
+}
+#endif /* EAP_SERVER_UNAUTH_TLS */
+
+
static void eap_tls_reset(struct eap_sm *sm, void *priv)
{
struct eap_tls_data *data = priv;
@@ -90,8 +109,7 @@ static struct wpabuf * eap_tls_build_start(struct eap_sm *sm,
{
struct wpabuf *req;
- req = eap_msg_alloc(EAP_VENDOR_IETF, EAP_TYPE_TLS, 1, EAP_CODE_REQUEST,
- id);
+ req = eap_tls_msg_alloc(data->eap_type, 1, EAP_CODE_REQUEST, id);
if (req == NULL) {
wpa_printf(MSG_ERROR, "EAP-TLS: Failed to allocate memory for "
"request");
@@ -113,11 +131,11 @@ static struct wpabuf * eap_tls_buildReq(struct eap_sm *sm, void *priv, u8 id)
struct wpabuf *res;
if (data->ssl.state == FRAG_ACK) {
- return eap_server_tls_build_ack(id, EAP_TYPE_TLS, 0);
+ return eap_server_tls_build_ack(id, data->eap_type, 0);
}
if (data->ssl.state == WAIT_FRAG_ACK) {
- res = eap_server_tls_build_msg(&data->ssl, EAP_TYPE_TLS, 0,
+ res = eap_server_tls_build_msg(&data->ssl, data->eap_type, 0,
id);
goto check_established;
}
@@ -135,7 +153,7 @@ static struct wpabuf * eap_tls_buildReq(struct eap_sm *sm, void *priv, u8 id)
return NULL;
}
- res = eap_server_tls_build_msg(&data->ssl, EAP_TYPE_TLS, 0, id);
+ res = eap_server_tls_build_msg(&data->ssl, data->eap_type, 0, id);
check_established:
if (data->established && data->ssl.state != WAIT_FRAG_ACK) {
@@ -152,10 +170,17 @@ check_established:
static Boolean eap_tls_check(struct eap_sm *sm, void *priv,
struct wpabuf *respData)
{
+ struct eap_tls_data *data = priv;
const u8 *pos;
size_t len;
- pos = eap_hdr_validate(EAP_VENDOR_IETF, EAP_TYPE_TLS, respData, &len);
+ if (data->eap_type == EAP_UNAUTH_TLS_TYPE)
+ pos = eap_hdr_validate(EAP_VENDOR_UNAUTH_TLS,
+ EAP_VENDOR_TYPE_UNAUTH_TLS, respData,
+ &len);
+ else
+ pos = eap_hdr_validate(EAP_VENDOR_IETF, data->eap_type,
+ respData, &len);
if (pos == NULL || len < 1) {
wpa_printf(MSG_INFO, "EAP-TLS: Invalid frame");
return TRUE;
@@ -184,7 +209,7 @@ static void eap_tls_process(struct eap_sm *sm, void *priv,
{
struct eap_tls_data *data = priv;
if (eap_server_tls_process(sm, &data->ssl, respData, data,
- EAP_TYPE_TLS, NULL, eap_tls_process_msg) <
+ data->eap_type, NULL, eap_tls_process_msg) <
0)
eap_tls_state(data, FAILURE);
}
@@ -284,3 +309,34 @@ int eap_server_tls_register(void)
eap_server_method_free(eap);
return ret;
}
+
+
+#ifdef EAP_SERVER_UNAUTH_TLS
+int eap_server_unauth_tls_register(void)
+{
+ struct eap_method *eap;
+ int ret;
+
+ eap = eap_server_method_alloc(EAP_SERVER_METHOD_INTERFACE_VERSION,
+ EAP_VENDOR_UNAUTH_TLS,
+ EAP_VENDOR_TYPE_UNAUTH_TLS,
+ "UNAUTH-TLS");
+ if (eap == NULL)
+ return -1;
+
+ eap->init = eap_unauth_tls_init;
+ eap->reset = eap_tls_reset;
+ eap->buildReq = eap_tls_buildReq;
+ eap->check = eap_tls_check;
+ eap->process = eap_tls_process;
+ eap->isDone = eap_tls_isDone;
+ eap->getKey = eap_tls_getKey;
+ eap->isSuccess = eap_tls_isSuccess;
+ eap->get_emsk = eap_tls_get_emsk;
+
+ ret = eap_server_method_register(eap);
+ if (ret)
+ eap_server_method_free(eap);
+ return ret;
+}
+#endif /* EAP_SERVER_UNAUTH_TLS */
diff --git a/contrib/wpa/src/eap_server/eap_server_tls_common.c b/contrib/wpa/src/eap_server/eap_server_tls_common.c
index 625ff52..9efb5b2 100644
--- a/contrib/wpa/src/eap_server/eap_server_tls_common.c
+++ b/contrib/wpa/src/eap_server/eap_server_tls_common.c
@@ -2,14 +2,8 @@
* EAP-TLS/PEAP/TTLS/FAST server common functions
* Copyright (c) 2004-2009, Jouni Malinen <j@w1.fi>
*
- * This program is free software; you can redistribute it and/or modify
- * it under the terms of the GNU General Public License version 2 as
- * published by the Free Software Foundation.
- *
- * Alternatively, this software may be distributed under the terms of BSD
- * license.
- *
- * See README and COPYING for more details.
+ * This software may be distributed under the terms of the BSD license.
+ * See README for more details.
*/
#include "includes.h"
@@ -24,6 +18,18 @@
static void eap_server_tls_free_in_buf(struct eap_ssl_data *data);
+struct wpabuf * eap_tls_msg_alloc(EapType type, size_t payload_len,
+ u8 code, u8 identifier)
+{
+ if (type == EAP_UNAUTH_TLS_TYPE)
+ return eap_msg_alloc(EAP_VENDOR_UNAUTH_TLS,
+ EAP_VENDOR_TYPE_UNAUTH_TLS, payload_len,
+ code, identifier);
+ return eap_msg_alloc(EAP_VENDOR_IETF, type, payload_len, code,
+ identifier);
+}
+
+
int eap_server_tls_ssl_init(struct eap_sm *sm, struct eap_ssl_data *data,
int verify_peer)
{
@@ -45,8 +51,7 @@ int eap_server_tls_ssl_init(struct eap_sm *sm, struct eap_ssl_data *data,
return -1;
}
- /* TODO: make this configurable */
- data->tls_out_limit = 1398;
+ data->tls_out_limit = sm->fragment_size > 0 ? sm->fragment_size : 1398;
if (data->phase2) {
/* Limit the fragment size in the inner TLS authentication
* since the outer authentication with EAP-PEAP does not yet
@@ -95,9 +100,9 @@ u8 * eap_server_tls_derive_key(struct eap_sm *sm, struct eap_ssl_data *data,
os_memcpy(rnd + keys.client_random_len, keys.server_random,
keys.server_random_len);
- if (tls_prf(keys.master_key, keys.master_key_len,
- label, rnd, keys.client_random_len +
- keys.server_random_len, out, len))
+ if (tls_prf_sha1_md5(keys.master_key, keys.master_key_len,
+ label, rnd, keys.client_random_len +
+ keys.server_random_len, out, len))
goto fail;
os_free(rnd);
@@ -138,8 +143,7 @@ struct wpabuf * eap_server_tls_build_msg(struct eap_ssl_data *data,
if (flags & EAP_TLS_FLAGS_LENGTH_INCLUDED)
plen += 4;
- req = eap_msg_alloc(EAP_VENDOR_IETF, eap_type, plen,
- EAP_CODE_REQUEST, id);
+ req = eap_tls_msg_alloc(eap_type, plen, EAP_CODE_REQUEST, id);
if (req == NULL)
return NULL;
@@ -175,8 +179,7 @@ struct wpabuf * eap_server_tls_build_ack(u8 id, int eap_type, int version)
{
struct wpabuf *req;
- req = eap_msg_alloc(EAP_VENDOR_IETF, eap_type, 1, EAP_CODE_REQUEST,
- id);
+ req = eap_tls_msg_alloc(eap_type, 1, EAP_CODE_REQUEST, id);
if (req == NULL)
return NULL;
wpa_printf(MSG_DEBUG, "SSL: Building ACK");
@@ -294,6 +297,13 @@ static int eap_server_tls_reassemble(struct eap_ssl_data *data, u8 flags,
tls_msg_len);
*pos += 4;
*left -= 4;
+
+ if (*left > tls_msg_len) {
+ wpa_printf(MSG_INFO, "SSL: TLS Message Length (%d "
+ "bytes) smaller than this fragment (%d "
+ "bytes)", (int) tls_msg_len, (int) *left);
+ return -1;
+ }
}
wpa_printf(MSG_DEBUG, "SSL: Received packet: Flags 0x%x "
@@ -374,7 +384,13 @@ int eap_server_tls_process(struct eap_sm *sm, struct eap_ssl_data *data,
size_t left;
int ret, res = 0;
- pos = eap_hdr_validate(EAP_VENDOR_IETF, eap_type, respData, &left);
+ if (eap_type == EAP_UNAUTH_TLS_TYPE)
+ pos = eap_hdr_validate(EAP_VENDOR_UNAUTH_TLS,
+ EAP_VENDOR_TYPE_UNAUTH_TLS, respData,
+ &left);
+ else
+ pos = eap_hdr_validate(EAP_VENDOR_IETF, eap_type, respData,
+ &left);
if (pos == NULL || left < 1)
return 0; /* Should not happen - frame already validated */
flags = *pos++;
diff --git a/contrib/wpa/src/eap_server/eap_server_tnc.c b/contrib/wpa/src/eap_server/eap_server_tnc.c
index f3b70ed..67a3dfa 100644
--- a/contrib/wpa/src/eap_server/eap_server_tnc.c
+++ b/contrib/wpa/src/eap_server/eap_server_tnc.c
@@ -2,20 +2,13 @@
* EAP server method: EAP-TNC (Trusted Network Connect)
* Copyright (c) 2007-2010, Jouni Malinen <j@w1.fi>
*
- * This program is free software; you can redistribute it and/or modify
- * it under the terms of the GNU General Public License version 2 as
- * published by the Free Software Foundation.
- *
- * Alternatively, this software may be distributed under the terms of BSD
- * license.
- *
- * See README and COPYING for more details.
+ * This software may be distributed under the terms of the BSD license.
+ * See README for more details.
*/
#include "includes.h"
#include "common.h"
-#include "base64.h"
#include "eap_i.h"
#include "tncs.h"
@@ -91,7 +84,8 @@ static void * eap_tnc_init(struct eap_sm *sm)
return NULL;
}
- data->fragment_size = 1300;
+ data->fragment_size = sm->fragment_size > 100 ?
+ sm->fragment_size - 98 : 1300;
return data;
}
diff --git a/contrib/wpa/src/eap_server/eap_server_ttls.c b/contrib/wpa/src/eap_server/eap_server_ttls.c
index 702c50c..647bd2f 100644
--- a/contrib/wpa/src/eap_server/eap_server_ttls.c
+++ b/contrib/wpa/src/eap_server/eap_server_ttls.c
@@ -1,15 +1,9 @@
/*
* hostapd / EAP-TTLS (RFC 5281)
- * Copyright (c) 2004-2008, Jouni Malinen <j@w1.fi>
+ * Copyright (c) 2004-2011, Jouni Malinen <j@w1.fi>
*
- * This program is free software; you can redistribute it and/or modify
- * it under the terms of the GNU General Public License version 2 as
- * published by the Free Software Foundation.
- *
- * Alternatively, this software may be distributed under the terms of BSD
- * license.
- *
- * See README and COPYING for more details.
+ * This software may be distributed under the terms of the BSD license.
+ * See README for more details.
*/
#include "includes.h"
@@ -24,16 +18,7 @@
#include "eap_common/eap_ttls.h"
-/* Maximum supported TTLS version
- * 0 = RFC 5281
- * 1 = draft-funk-eap-ttls-v1-00.txt
- */
-#ifndef EAP_TTLS_VERSION
-#define EAP_TTLS_VERSION 0 /* TTLSv1 implementation is not yet complete */
-#endif /* EAP_TTLS_VERSION */
-
-
-#define MSCHAPV2_KEY_LEN 16
+#define EAP_TTLS_VERSION 0
static void eap_ttls_reset(struct eap_sm *sm, void *priv);
@@ -43,17 +28,15 @@ struct eap_ttls_data {
struct eap_ssl_data ssl;
enum {
START, PHASE1, PHASE2_START, PHASE2_METHOD,
- PHASE2_MSCHAPV2_RESP, PHASE_FINISHED, SUCCESS, FAILURE
+ PHASE2_MSCHAPV2_RESP, SUCCESS, FAILURE
} state;
int ttls_version;
- int force_version;
const struct eap_method *phase2_method;
void *phase2_priv;
int mschapv2_resp_ok;
u8 mschapv2_auth_response[20];
u8 mschapv2_ident;
- int tls_ia_configured;
struct wpabuf *pending_phase2_eap_resp;
int tnc_started;
};
@@ -72,8 +55,6 @@ static const char * eap_ttls_state_txt(int state)
return "PHASE2_METHOD";
case PHASE2_MSCHAPV2_RESP:
return "PHASE2_MSCHAPV2_RESP";
- case PHASE_FINISHED:
- return "PHASE_FINISHED";
case SUCCESS:
return "SUCCESS";
case FAILURE:
@@ -111,7 +92,8 @@ static u8 * eap_ttls_avp_hdr(u8 *avphdr, u32 avp_code, u32 vendor_id,
}
avp->avp_code = host_to_be32(avp_code);
- avp->avp_length = host_to_be32((flags << 24) | (hdrlen + len));
+ avp->avp_length = host_to_be32(((u32) flags << 24) |
+ ((u32) (hdrlen + len)));
return avphdr + hdrlen;
}
@@ -320,54 +302,8 @@ fail:
static u8 * eap_ttls_implicit_challenge(struct eap_sm *sm,
struct eap_ttls_data *data, size_t len)
{
- struct tls_keys keys;
- u8 *challenge, *rnd;
-
- if (data->ttls_version == 0) {
- return eap_server_tls_derive_key(sm, &data->ssl,
- "ttls challenge", len);
- }
-
- os_memset(&keys, 0, sizeof(keys));
- if (tls_connection_get_keys(sm->ssl_ctx, data->ssl.conn, &keys) ||
- keys.client_random == NULL || keys.server_random == NULL ||
- keys.inner_secret == NULL) {
- wpa_printf(MSG_INFO, "EAP-TTLS: Could not get inner secret, "
- "client random, or server random to derive "
- "implicit challenge");
- return NULL;
- }
-
- rnd = os_malloc(keys.client_random_len + keys.server_random_len);
- challenge = os_malloc(len);
- if (rnd == NULL || challenge == NULL) {
- wpa_printf(MSG_INFO, "EAP-TTLS: No memory for implicit "
- "challenge derivation");
- os_free(rnd);
- os_free(challenge);
- return NULL;
- }
- os_memcpy(rnd, keys.server_random, keys.server_random_len);
- os_memcpy(rnd + keys.server_random_len, keys.client_random,
- keys.client_random_len);
-
- if (tls_prf(keys.inner_secret, keys.inner_secret_len,
- "inner application challenge", rnd,
- keys.client_random_len + keys.server_random_len,
- challenge, len)) {
- wpa_printf(MSG_DEBUG, "EAP-TTLS: Failed to derive implicit "
- "challenge");
- os_free(rnd);
- os_free(challenge);
- return NULL;
- }
-
- os_free(rnd);
-
- wpa_hexdump_key(MSG_DEBUG, "EAP-TTLS: Derived implicit challenge",
- challenge, len);
-
- return challenge;
+ return eap_server_tls_derive_key(sm, &data->ssl, "ttls challenge",
+ len);
}
@@ -379,27 +315,8 @@ static void * eap_ttls_init(struct eap_sm *sm)
if (data == NULL)
return NULL;
data->ttls_version = EAP_TTLS_VERSION;
- data->force_version = -1;
- if (sm->user && sm->user->force_version >= 0) {
- data->force_version = sm->user->force_version;
- wpa_printf(MSG_DEBUG, "EAP-TTLS: forcing version %d",
- data->force_version);
- data->ttls_version = data->force_version;
- }
data->state = START;
- if (!(tls_capabilities(sm->ssl_ctx) & TLS_CAPABILITY_IA) &&
- data->ttls_version > 0) {
- if (data->force_version > 0) {
- wpa_printf(MSG_INFO, "EAP-TTLS: Forced TTLSv%d and "
- "TLS library does not support TLS/IA.",
- data->force_version);
- eap_ttls_reset(sm, data);
- return NULL;
- }
- data->ttls_version = 0;
- }
-
if (eap_server_tls_ssl_init(sm, &data->ssl, 0)) {
wpa_printf(MSG_INFO, "EAP-TTLS: Failed to initialize SSL.");
eap_ttls_reset(sm, data);
@@ -516,14 +433,6 @@ static struct wpabuf * eap_ttls_build_phase2_mschapv2(
}
-static struct wpabuf * eap_ttls_build_phase_finished(
- struct eap_sm *sm, struct eap_ttls_data *data, int final)
-{
- return tls_connection_ia_send_phase_finished(sm->ssl_ctx,
- data->ssl.conn, final);
-}
-
-
static struct wpabuf * eap_ttls_buildReq(struct eap_sm *sm, void *priv, u8 id)
{
struct eap_ttls_data *data = priv;
@@ -559,11 +468,6 @@ static struct wpabuf * eap_ttls_buildReq(struct eap_sm *sm, void *priv, u8 id)
data->ssl.tls_out_pos = 0;
data->ssl.tls_out = eap_ttls_build_phase2_mschapv2(sm, data);
break;
- case PHASE_FINISHED:
- wpabuf_free(data->ssl.tls_out);
- data->ssl.tls_out_pos = 0;
- data->ssl.tls_out = eap_ttls_build_phase_finished(sm, data, 1);
- break;
default:
wpa_printf(MSG_DEBUG, "EAP-TTLS: %s - unexpected state %d",
__func__, data->state);
@@ -591,37 +495,6 @@ static Boolean eap_ttls_check(struct eap_sm *sm, void *priv,
}
-static int eap_ttls_ia_permute_inner_secret(struct eap_sm *sm,
- struct eap_ttls_data *data,
- const u8 *key, size_t key_len)
-{
- u8 *buf;
- size_t buf_len;
- int ret;
-
- if (key) {
- buf_len = 2 + key_len;
- buf = os_malloc(buf_len);
- if (buf == NULL)
- return -1;
- WPA_PUT_BE16(buf, key_len);
- os_memcpy(buf + 2, key, key_len);
- } else {
- buf = NULL;
- buf_len = 0;
- }
-
- wpa_hexdump_key(MSG_DEBUG, "EAP-TTLS: Session keys for TLS/IA inner "
- "secret permutation", buf, buf_len);
- ret = tls_connection_ia_permute_inner_secret(sm->ssl_ctx,
- data->ssl.conn,
- buf, buf_len);
- os_free(buf);
-
- return ret;
-}
-
-
static void eap_ttls_process_phase2_pap(struct eap_sm *sm,
struct eap_ttls_data *data,
const u8 *user_password,
@@ -644,8 +517,7 @@ static void eap_ttls_process_phase2_pap(struct eap_sm *sm,
}
wpa_printf(MSG_DEBUG, "EAP-TTLS/PAP: Correct user password");
- eap_ttls_state(data, data->ttls_version > 0 ? PHASE_FINISHED :
- SUCCESS);
+ eap_ttls_state(data, SUCCESS);
}
@@ -701,8 +573,7 @@ static void eap_ttls_process_phase2_chap(struct eap_sm *sm,
if (os_memcmp(hash, password + 1, EAP_TTLS_CHAP_PASSWORD_LEN) == 0) {
wpa_printf(MSG_DEBUG, "EAP-TTLS/CHAP: Correct user password");
- eap_ttls_state(data, data->ttls_version > 0 ? PHASE_FINISHED :
- SUCCESS);
+ eap_ttls_state(data, SUCCESS);
} else {
wpa_printf(MSG_DEBUG, "EAP-TTLS/CHAP: Invalid user password");
eap_ttls_state(data, FAILURE);
@@ -762,8 +633,7 @@ static void eap_ttls_process_phase2_mschap(struct eap_sm *sm,
if (os_memcmp(nt_response, response + 2 + 24, 24) == 0) {
wpa_printf(MSG_DEBUG, "EAP-TTLS/MSCHAP: Correct response");
- eap_ttls_state(data, data->ttls_version > 0 ? PHASE_FINISHED :
- SUCCESS);
+ eap_ttls_state(data, SUCCESS);
} else {
wpa_printf(MSG_DEBUG, "EAP-TTLS/MSCHAP: Invalid NT-Response");
wpa_hexdump(MSG_MSGDUMP, "EAP-TTLS/MSCHAP: Received",
@@ -804,6 +674,13 @@ static void eap_ttls_process_phase2_mschapv2(struct eap_sm *sm,
return;
}
+ if (sm->identity == NULL) {
+ wpa_printf(MSG_DEBUG, "EAP-TTLS/MSCHAPV2: No user identity "
+ "known");
+ eap_ttls_state(data, FAILURE);
+ return;
+ }
+
/* MSCHAPv2 does not include optional domain name in the
* challenge-response calculation, so remove domain prefix
* (if present). */
@@ -863,30 +740,6 @@ static void eap_ttls_process_phase2_mschapv2(struct eap_sm *sm,
wpa_printf(MSG_DEBUG, "EAP-TTLS/MSCHAPV2: Correct "
"NT-Response");
data->mschapv2_resp_ok = 1;
- if (data->ttls_version > 0) {
- const u8 *pw_hash;
- u8 pw_hash_buf[16], pw_hash_hash[16], master_key[16];
- u8 session_key[2 * MSCHAPV2_KEY_LEN];
-
- if (sm->user->password_hash)
- pw_hash = sm->user->password;
- else {
- nt_password_hash(sm->user->password,
- sm->user->password_len,
- pw_hash_buf);
- pw_hash = pw_hash_buf;
- }
- hash_nt_password_hash(pw_hash, pw_hash_hash);
- get_master_key(pw_hash_hash, nt_response, master_key);
- get_asymetric_start_key(master_key, session_key,
- MSCHAPV2_KEY_LEN, 0, 0);
- get_asymetric_start_key(master_key,
- session_key + MSCHAPV2_KEY_LEN,
- MSCHAPV2_KEY_LEN, 1, 0);
- eap_ttls_ia_permute_inner_secret(sm, data,
- session_key,
- sizeof(session_key));
- }
if (sm->user->password_hash) {
generate_authenticator_response_pwhash(
@@ -1030,17 +883,7 @@ static void eap_ttls_process_phase2_eap_response(struct eap_sm *sm,
}
break;
case PHASE2_METHOD:
- if (data->ttls_version > 0) {
- if (m->getKey) {
- u8 *key;
- size_t key_len;
- key = m->getKey(sm, priv, &key_len);
- eap_ttls_ia_permute_inner_secret(sm, data,
- key, key_len);
- }
- eap_ttls_state(data, PHASE_FINISHED);
- } else
- eap_ttls_state(data, SUCCESS);
+ eap_ttls_state(data, SUCCESS);
break;
case FAILURE:
break;
@@ -1130,23 +973,6 @@ static void eap_ttls_process_phase2(struct eap_sm *sm,
return;
}
- if (data->state == PHASE_FINISHED) {
- if (wpabuf_len(in_decrypted) == 0 &&
- tls_connection_ia_final_phase_finished(sm->ssl_ctx,
- data->ssl.conn)) {
- wpa_printf(MSG_DEBUG, "EAP-TTLS: FinalPhaseFinished "
- "received");
- eap_ttls_state(data, SUCCESS);
- } else {
- wpa_printf(MSG_INFO, "EAP-TTLS: Did not receive valid "
- "FinalPhaseFinished");
- eap_ttls_state(data, FAILURE);
- }
-
- wpabuf_free(in_decrypted);
- return;
- }
-
wpa_hexdump_buf_key(MSG_DEBUG, "EAP-TTLS: Decrypted Phase 2 EAP",
in_decrypted);
@@ -1160,11 +986,12 @@ static void eap_ttls_process_phase2(struct eap_sm *sm,
if (parse.user_name) {
os_free(sm->identity);
sm->identity = os_malloc(parse.user_name_len);
- if (sm->identity) {
- os_memcpy(sm->identity, parse.user_name,
- parse.user_name_len);
- sm->identity_len = parse.user_name_len;
+ if (sm->identity == NULL) {
+ eap_ttls_state(data, FAILURE);
+ goto done;
}
+ os_memcpy(sm->identity, parse.user_name, parse.user_name_len);
+ sm->identity_len = parse.user_name_len;
if (eap_user_get(sm, parse.user_name, parse.user_name_len, 1)
!= 0) {
wpa_printf(MSG_DEBUG, "EAP-TTLS: Phase2 Identity not "
@@ -1245,15 +1072,6 @@ static int eap_ttls_process_version(struct eap_sm *sm, void *priv,
data->ttls_version = peer_version;
}
- if (data->ttls_version > 0 && !data->tls_ia_configured) {
- if (tls_connection_set_ia(sm->ssl_ctx, data->ssl.conn, 1)) {
- wpa_printf(MSG_INFO, "EAP-TTLS: Failed to enable "
- "TLS/IA");
- return -1;
- }
- data->tls_ia_configured = 1;
- }
-
return 0;
}
@@ -1270,7 +1088,6 @@ static void eap_ttls_process_msg(struct eap_sm *sm, void *priv,
break;
case PHASE2_START:
case PHASE2_METHOD:
- case PHASE_FINISHED:
eap_ttls_process_phase2(sm, data, data->ssl.tls_in);
eap_ttls_start_tnc(sm, data);
break;
@@ -1279,8 +1096,7 @@ static void eap_ttls_process_msg(struct eap_sm *sm, void *priv,
0) {
wpa_printf(MSG_DEBUG, "EAP-TTLS/MSCHAPV2: Peer "
"acknowledged response");
- eap_ttls_state(data, data->ttls_version > 0 ?
- PHASE_FINISHED : SUCCESS);
+ eap_ttls_state(data, SUCCESS);
} else if (!data->mschapv2_resp_ok) {
wpa_printf(MSG_DEBUG, "EAP-TTLS/MSCHAPV2: Peer "
"acknowledged error");
@@ -1321,54 +1137,6 @@ static Boolean eap_ttls_isDone(struct eap_sm *sm, void *priv)
}
-static u8 * eap_ttls_v1_derive_key(struct eap_sm *sm,
- struct eap_ttls_data *data)
-{
- struct tls_keys keys;
- u8 *rnd, *key;
-
- os_memset(&keys, 0, sizeof(keys));
- if (tls_connection_get_keys(sm->ssl_ctx, data->ssl.conn, &keys) ||
- keys.client_random == NULL || keys.server_random == NULL ||
- keys.inner_secret == NULL) {
- wpa_printf(MSG_INFO, "EAP-TTLS: Could not get inner secret, "
- "client random, or server random to derive keying "
- "material");
- return NULL;
- }
-
- rnd = os_malloc(keys.client_random_len + keys.server_random_len);
- key = os_malloc(EAP_TLS_KEY_LEN);
- if (rnd == NULL || key == NULL) {
- wpa_printf(MSG_INFO, "EAP-TTLS: No memory for key derivation");
- os_free(rnd);
- os_free(key);
- return NULL;
- }
- os_memcpy(rnd, keys.client_random, keys.client_random_len);
- os_memcpy(rnd + keys.client_random_len, keys.server_random,
- keys.server_random_len);
-
- if (tls_prf(keys.inner_secret, keys.inner_secret_len,
- "ttls v1 keying material", rnd, keys.client_random_len +
- keys.server_random_len, key, EAP_TLS_KEY_LEN)) {
- wpa_printf(MSG_DEBUG, "EAP-TTLS: Failed to derive key");
- os_free(rnd);
- os_free(key);
- return NULL;
- }
-
- wpa_hexdump(MSG_DEBUG, "EAP-TTLS: client/server random",
- rnd, keys.client_random_len + keys.server_random_len);
- wpa_hexdump_key(MSG_DEBUG, "EAP-TTLS: TLS/IA inner secret",
- keys.inner_secret, keys.inner_secret_len);
-
- os_free(rnd);
-
- return key;
-}
-
-
static u8 * eap_ttls_getKey(struct eap_sm *sm, void *priv, size_t *len)
{
struct eap_ttls_data *data = priv;
@@ -1377,14 +1145,9 @@ static u8 * eap_ttls_getKey(struct eap_sm *sm, void *priv, size_t *len)
if (data->state != SUCCESS)
return NULL;
- if (data->ttls_version == 0) {
- eapKeyData = eap_server_tls_derive_key(sm, &data->ssl,
- "ttls keying material",
- EAP_TLS_KEY_LEN);
- } else {
- eapKeyData = eap_ttls_v1_derive_key(sm, data);
- }
-
+ eapKeyData = eap_server_tls_derive_key(sm, &data->ssl,
+ "ttls keying material",
+ EAP_TLS_KEY_LEN);
if (eapKeyData) {
*len = EAP_TLS_KEY_LEN;
wpa_hexdump_key(MSG_DEBUG, "EAP-TTLS: Derived key",
diff --git a/contrib/wpa/src/eap_server/eap_server_vendor_test.c b/contrib/wpa/src/eap_server/eap_server_vendor_test.c
index 0dd0aca..30f600d 100644
--- a/contrib/wpa/src/eap_server/eap_server_vendor_test.c
+++ b/contrib/wpa/src/eap_server/eap_server_vendor_test.c
@@ -2,14 +2,8 @@
* hostapd / Test method for vendor specific (expanded) EAP type
* Copyright (c) 2005-2007, Jouni Malinen <j@w1.fi>
*
- * This program is free software; you can redistribute it and/or modify
- * it under the terms of the GNU General Public License version 2 as
- * published by the Free Software Foundation.
- *
- * Alternatively, this software may be distributed under the terms of BSD
- * license.
- *
- * See README and COPYING for more details.
+ * This software may be distributed under the terms of the BSD license.
+ * See README for more details.
*/
#include "includes.h"
@@ -18,7 +12,7 @@
#include "eap_i.h"
-#define EAP_VENDOR_ID 0xfffefd
+#define EAP_VENDOR_ID EAP_VENDOR_HOSTAP
#define EAP_VENDOR_TYPE 0xfcfbfaf9
diff --git a/contrib/wpa/src/eap_server/eap_server_wsc.c b/contrib/wpa/src/eap_server/eap_server_wsc.c
index 77cf9e2..97ec0c0 100644
--- a/contrib/wpa/src/eap_server/eap_server_wsc.c
+++ b/contrib/wpa/src/eap_server/eap_server_wsc.c
@@ -2,14 +2,8 @@
* EAP-WSC server for Wi-Fi Protected Setup
* Copyright (c) 2007-2008, Jouni Malinen <j@w1.fi>
*
- * This program is free software; you can redistribute it and/or modify
- * it under the terms of the GNU General Public License version 2 as
- * published by the Free Software Foundation.
- *
- * Alternatively, this software may be distributed under the terms of BSD
- * license.
- *
- * See README and COPYING for more details.
+ * This software may be distributed under the terms of the BSD license.
+ * See README for more details.
*/
#include "includes.h"
@@ -18,6 +12,7 @@
#include "eloop.h"
#include "eap_i.h"
#include "eap_common/eap_wsc_common.h"
+#include "p2p/p2p.h"
#include "wps/wps.h"
@@ -135,14 +130,22 @@ static void * eap_wsc_init(struct eap_sm *sm)
}
cfg.assoc_wps_ie = sm->assoc_wps_ie;
cfg.peer_addr = sm->peer_addr;
- if (0 /* TODO: could provide option for forcing PSK format */)
- cfg.use_psk_key = 1;
+#ifdef CONFIG_P2P
+ if (sm->assoc_p2p_ie) {
+ wpa_printf(MSG_DEBUG, "EAP-WSC: Prefer PSK format for P2P "
+ "client");
+ cfg.use_psk_key = 1;
+ cfg.p2p_dev_addr = p2p_get_go_dev_addr(sm->assoc_p2p_ie);
+ }
+#endif /* CONFIG_P2P */
+ cfg.pbc_in_m1 = sm->pbc_in_m1;
data->wps = wps_init(&cfg);
if (data->wps == NULL) {
os_free(data);
return NULL;
}
- data->fragment_size = WSC_FRAGMENT_SIZE;
+ data->fragment_size = sm->fragment_size > 0 ? sm->fragment_size :
+ WSC_FRAGMENT_SIZE;
return data;
}
diff --git a/contrib/wpa/src/eap_server/eap_sim_db.c b/contrib/wpa/src/eap_server/eap_sim_db.c
index aba919a..257013e 100644
--- a/contrib/wpa/src/eap_server/eap_sim_db.c
+++ b/contrib/wpa/src/eap_server/eap_sim_db.c
@@ -1,15 +1,9 @@
/*
* hostapd / EAP-SIM database/authenticator gateway
- * Copyright (c) 2005-2007, Jouni Malinen <j@w1.fi>
+ * Copyright (c) 2005-2010, 2012, Jouni Malinen <j@w1.fi>
*
- * This program is free software; you can redistribute it and/or modify
- * it under the terms of the GNU General Public License version 2 as
- * published by the Free Software Foundation.
- *
- * Alternatively, this software may be distributed under the terms of BSD
- * license.
- *
- * See README and COPYING for more details.
+ * This software may be distributed under the terms of the BSD license.
+ * See README for more details.
*
* This is an example implementation of the EAP-SIM/AKA database/authentication
* gateway interface that is using an external program as an SS7 gateway to
@@ -23,23 +17,25 @@
#include "includes.h"
#include <sys/un.h>
+#ifdef CONFIG_SQLITE
+#include <sqlite3.h>
+#endif /* CONFIG_SQLITE */
#include "common.h"
+#include "crypto/random.h"
#include "eap_common/eap_sim_common.h"
#include "eap_server/eap_sim_db.h"
#include "eloop.h"
struct eap_sim_pseudonym {
struct eap_sim_pseudonym *next;
- u8 *identity;
- size_t identity_len;
- char *pseudonym;
+ char *permanent; /* permanent username */
+ char *pseudonym; /* pseudonym username */
};
struct eap_sim_db_pending {
struct eap_sim_db_pending *next;
- u8 imsi[20];
- size_t imsi_len;
+ char imsi[20];
enum { PENDING, SUCCESS, FAILURE } state;
void *cb_session_ctx;
struct os_time timestamp;
@@ -71,19 +67,316 @@ struct eap_sim_db_data {
struct eap_sim_pseudonym *pseudonyms;
struct eap_sim_reauth *reauths;
struct eap_sim_db_pending *pending;
+#ifdef CONFIG_SQLITE
+ sqlite3 *sqlite_db;
+ char db_tmp_identity[100];
+ char db_tmp_pseudonym_str[100];
+ struct eap_sim_pseudonym db_tmp_pseudonym;
+ struct eap_sim_reauth db_tmp_reauth;
+#endif /* CONFIG_SQLITE */
};
+#ifdef CONFIG_SQLITE
+
+static int db_table_exists(sqlite3 *db, const char *name)
+{
+ char cmd[128];
+ os_snprintf(cmd, sizeof(cmd), "SELECT 1 FROM %s;", name);
+ return sqlite3_exec(db, cmd, NULL, NULL, NULL) == SQLITE_OK;
+}
+
+
+static int db_table_create_pseudonym(sqlite3 *db)
+{
+ char *err = NULL;
+ const char *sql =
+ "CREATE TABLE pseudonyms("
+ " permanent CHAR(21) PRIMARY KEY,"
+ " pseudonym CHAR(21) NOT NULL"
+ ");";
+
+ wpa_printf(MSG_DEBUG, "EAP-SIM DB: Adding database table for "
+ "pseudonym information");
+ if (sqlite3_exec(db, sql, NULL, NULL, &err) != SQLITE_OK) {
+ wpa_printf(MSG_ERROR, "EAP-SIM DB: SQLite error: %s", err);
+ sqlite3_free(err);
+ return -1;
+ }
+
+ return 0;
+}
+
+
+static int db_table_create_reauth(sqlite3 *db)
+{
+ char *err = NULL;
+ const char *sql =
+ "CREATE TABLE reauth("
+ " permanent CHAR(21) PRIMARY KEY,"
+ " reauth_id CHAR(21) NOT NULL,"
+ " counter INTEGER,"
+ " mk CHAR(40),"
+ " k_encr CHAR(32),"
+ " k_aut CHAR(64),"
+ " k_re CHAR(64)"
+ ");";
+
+ wpa_printf(MSG_DEBUG, "EAP-SIM DB: Adding database table for "
+ "reauth information");
+ if (sqlite3_exec(db, sql, NULL, NULL, &err) != SQLITE_OK) {
+ wpa_printf(MSG_ERROR, "EAP-SIM DB: SQLite error: %s", err);
+ sqlite3_free(err);
+ return -1;
+ }
+
+ return 0;
+}
+
+
+static sqlite3 * db_open(const char *db_file)
+{
+ sqlite3 *db;
+
+ if (sqlite3_open(db_file, &db)) {
+ wpa_printf(MSG_ERROR, "EAP-SIM DB: Failed to open database "
+ "%s: %s", db_file, sqlite3_errmsg(db));
+ sqlite3_close(db);
+ return NULL;
+ }
+
+ if (!db_table_exists(db, "pseudonyms") &&
+ db_table_create_pseudonym(db) < 0) {
+ sqlite3_close(db);
+ return NULL;
+ }
+
+ if (!db_table_exists(db, "reauth") &&
+ db_table_create_reauth(db) < 0) {
+ sqlite3_close(db);
+ return NULL;
+ }
+
+ return db;
+}
+
+
+static int valid_db_string(const char *str)
+{
+ const char *pos = str;
+ while (*pos) {
+ if ((*pos < '0' || *pos > '9') &&
+ (*pos < 'a' || *pos > 'f'))
+ return 0;
+ pos++;
+ }
+ return 1;
+}
+
+
+static int db_add_pseudonym(struct eap_sim_db_data *data,
+ const char *permanent, char *pseudonym)
+{
+ char cmd[128];
+ char *err = NULL;
+
+ if (!valid_db_string(permanent) || !valid_db_string(pseudonym)) {
+ os_free(pseudonym);
+ return -1;
+ }
+
+ os_snprintf(cmd, sizeof(cmd), "INSERT OR REPLACE INTO pseudonyms "
+ "(permanent, pseudonym) VALUES ('%s', '%s');",
+ permanent, pseudonym);
+ os_free(pseudonym);
+ if (sqlite3_exec(data->sqlite_db, cmd, NULL, NULL, &err) != SQLITE_OK)
+ {
+ wpa_printf(MSG_ERROR, "EAP-SIM DB: SQLite error: %s", err);
+ sqlite3_free(err);
+ return -1;
+ }
+
+ return 0;
+}
+
+
+static int get_pseudonym_cb(void *ctx, int argc, char *argv[], char *col[])
+{
+ struct eap_sim_db_data *data = ctx;
+ int i;
+
+ for (i = 0; i < argc; i++) {
+ if (os_strcmp(col[i], "permanent") == 0 && argv[i]) {
+ os_strlcpy(data->db_tmp_identity, argv[i],
+ sizeof(data->db_tmp_identity));
+ }
+ }
+
+ return 0;
+}
+
+
+static char *
+db_get_pseudonym(struct eap_sim_db_data *data, const char *pseudonym)
+{
+ char cmd[128];
+
+ if (!valid_db_string(pseudonym))
+ return NULL;
+ os_memset(&data->db_tmp_identity, 0, sizeof(data->db_tmp_identity));
+ os_snprintf(cmd, sizeof(cmd),
+ "SELECT permanent FROM pseudonyms WHERE pseudonym='%s';",
+ pseudonym);
+ if (sqlite3_exec(data->sqlite_db, cmd, get_pseudonym_cb, data, NULL) !=
+ SQLITE_OK)
+ return NULL;
+ if (data->db_tmp_identity[0] == '\0')
+ return NULL;
+ return data->db_tmp_identity;
+}
+
+
+static int db_add_reauth(struct eap_sim_db_data *data, const char *permanent,
+ char *reauth_id, u16 counter, const u8 *mk,
+ const u8 *k_encr, const u8 *k_aut, const u8 *k_re)
+{
+ char cmd[2000], *pos, *end;
+ char *err = NULL;
+
+ if (!valid_db_string(permanent) || !valid_db_string(reauth_id)) {
+ os_free(reauth_id);
+ return -1;
+ }
+
+ pos = cmd;
+ end = pos + sizeof(cmd);
+ pos += os_snprintf(pos, end - pos, "INSERT OR REPLACE INTO reauth "
+ "(permanent, reauth_id, counter%s%s%s%s) "
+ "VALUES ('%s', '%s', %u",
+ mk ? ", mk" : "",
+ k_encr ? ", k_encr" : "",
+ k_aut ? ", k_aut" : "",
+ k_re ? ", k_re" : "",
+ permanent, reauth_id, counter);
+ os_free(reauth_id);
+
+ if (mk) {
+ pos += os_snprintf(pos, end - pos, ", '");
+ pos += wpa_snprintf_hex(pos, end - pos, mk, EAP_SIM_MK_LEN);
+ pos += os_snprintf(pos, end - pos, "'");
+ }
+
+ if (k_encr) {
+ pos += os_snprintf(pos, end - pos, ", '");
+ pos += wpa_snprintf_hex(pos, end - pos, k_encr,
+ EAP_SIM_K_ENCR_LEN);
+ pos += os_snprintf(pos, end - pos, "'");
+ }
+
+ if (k_aut) {
+ pos += os_snprintf(pos, end - pos, ", '");
+ pos += wpa_snprintf_hex(pos, end - pos, k_aut,
+ EAP_AKA_PRIME_K_AUT_LEN);
+ pos += os_snprintf(pos, end - pos, "'");
+ }
+
+ if (k_re) {
+ pos += os_snprintf(pos, end - pos, ", '");
+ pos += wpa_snprintf_hex(pos, end - pos, k_re,
+ EAP_AKA_PRIME_K_RE_LEN);
+ pos += os_snprintf(pos, end - pos, "'");
+ }
+
+ os_snprintf(pos, end - pos, ");");
+
+ if (sqlite3_exec(data->sqlite_db, cmd, NULL, NULL, &err) != SQLITE_OK)
+ {
+ wpa_printf(MSG_ERROR, "EAP-SIM DB: SQLite error: %s", err);
+ sqlite3_free(err);
+ return -1;
+ }
+
+ return 0;
+}
+
+
+static int get_reauth_cb(void *ctx, int argc, char *argv[], char *col[])
+{
+ struct eap_sim_db_data *data = ctx;
+ int i;
+ struct eap_sim_reauth *reauth = &data->db_tmp_reauth;
+
+ for (i = 0; i < argc; i++) {
+ if (os_strcmp(col[i], "permanent") == 0 && argv[i]) {
+ os_strlcpy(data->db_tmp_identity, argv[i],
+ sizeof(data->db_tmp_identity));
+ reauth->permanent = data->db_tmp_identity;
+ } else if (os_strcmp(col[i], "counter") == 0 && argv[i]) {
+ reauth->counter = atoi(argv[i]);
+ } else if (os_strcmp(col[i], "mk") == 0 && argv[i]) {
+ hexstr2bin(argv[i], reauth->mk, sizeof(reauth->mk));
+ } else if (os_strcmp(col[i], "k_encr") == 0 && argv[i]) {
+ hexstr2bin(argv[i], reauth->k_encr,
+ sizeof(reauth->k_encr));
+ } else if (os_strcmp(col[i], "k_aut") == 0 && argv[i]) {
+ hexstr2bin(argv[i], reauth->k_aut,
+ sizeof(reauth->k_aut));
+ } else if (os_strcmp(col[i], "k_re") == 0 && argv[i]) {
+ hexstr2bin(argv[i], reauth->k_re,
+ sizeof(reauth->k_re));
+ }
+ }
+
+ return 0;
+}
+
+
+static struct eap_sim_reauth *
+db_get_reauth(struct eap_sim_db_data *data, const char *reauth_id)
+{
+ char cmd[256];
+
+ if (!valid_db_string(reauth_id))
+ return NULL;
+ os_memset(&data->db_tmp_reauth, 0, sizeof(data->db_tmp_reauth));
+ os_strlcpy(data->db_tmp_pseudonym_str, reauth_id,
+ sizeof(data->db_tmp_pseudonym_str));
+ data->db_tmp_reauth.reauth_id = data->db_tmp_pseudonym_str;
+ os_snprintf(cmd, sizeof(cmd),
+ "SELECT * FROM reauth WHERE reauth_id='%s';", reauth_id);
+ if (sqlite3_exec(data->sqlite_db, cmd, get_reauth_cb, data, NULL) !=
+ SQLITE_OK)
+ return NULL;
+ if (data->db_tmp_reauth.permanent == NULL)
+ return NULL;
+ return &data->db_tmp_reauth;
+}
+
+
+static void db_remove_reauth(struct eap_sim_db_data *data,
+ struct eap_sim_reauth *reauth)
+{
+ char cmd[256];
+
+ if (!valid_db_string(reauth->permanent))
+ return;
+ os_snprintf(cmd, sizeof(cmd),
+ "DELETE FROM reauth WHERE permanent='%s';",
+ reauth->permanent);
+ sqlite3_exec(data->sqlite_db, cmd, NULL, NULL, NULL);
+}
+
+#endif /* CONFIG_SQLITE */
+
+
static struct eap_sim_db_pending *
-eap_sim_db_get_pending(struct eap_sim_db_data *data, const u8 *imsi,
- size_t imsi_len, int aka)
+eap_sim_db_get_pending(struct eap_sim_db_data *data, const char *imsi, int aka)
{
struct eap_sim_db_pending *entry, *prev = NULL;
entry = data->pending;
while (entry) {
- if (entry->aka == aka && entry->imsi_len == imsi_len &&
- os_memcmp(entry->imsi, imsi, imsi_len) == 0) {
+ if (entry->aka == aka && os_strcmp(entry->imsi, imsi) == 0) {
if (prev)
prev->next = entry->next;
else
@@ -118,7 +411,7 @@ static void eap_sim_db_sim_resp_auth(struct eap_sim_db_data *data,
* (IMSI = ASCII string, Kc/SRES/RAND = hex string)
*/
- entry = eap_sim_db_get_pending(data, (u8 *) imsi, os_strlen(imsi), 0);
+ entry = eap_sim_db_get_pending(data, imsi, 0);
if (entry == NULL) {
wpa_printf(MSG_DEBUG, "EAP-SIM DB: No pending entry for the "
"received message found");
@@ -196,7 +489,7 @@ static void eap_sim_db_aka_resp_auth(struct eap_sim_db_data *data,
* (IMSI = ASCII string, RAND/AUTN/IK/CK/RES = hex string)
*/
- entry = eap_sim_db_get_pending(data, (u8 *) imsi, os_strlen(imsi), 1);
+ entry = eap_sim_db_get_pending(data, imsi, 1);
if (entry == NULL) {
wpa_printf(MSG_DEBUG, "EAP-SIM DB: No pending entry for the "
"received message found");
@@ -345,6 +638,7 @@ static int eap_sim_db_open_socket(struct eap_sim_db_data *data)
addr.sun_family = AF_UNIX;
os_snprintf(addr.sun_path, sizeof(addr.sun_path),
"/tmp/eap_sim_db_%d-%d", getpid(), counter++);
+ os_free(data->local_sock);
data->local_sock = os_strdup(addr.sun_path);
if (bind(data->sock, (struct sockaddr *) &addr, sizeof(addr)) < 0) {
perror("bind(eap_sim_db)");
@@ -394,11 +688,13 @@ static void eap_sim_db_close_socket(struct eap_sim_db_data *data)
* @ctx: Context pointer for get_complete_cb
* Returns: Pointer to a private data structure or %NULL on failure
*/
-void * eap_sim_db_init(const char *config,
- void (*get_complete_cb)(void *ctx, void *session_ctx),
- void *ctx)
+struct eap_sim_db_data *
+eap_sim_db_init(const char *config,
+ void (*get_complete_cb)(void *ctx, void *session_ctx),
+ void *ctx)
{
struct eap_sim_db_data *data;
+ char *pos;
data = os_zalloc(sizeof(*data));
if (data == NULL)
@@ -410,10 +706,23 @@ void * eap_sim_db_init(const char *config,
data->fname = os_strdup(config);
if (data->fname == NULL)
goto fail;
+ pos = os_strstr(data->fname, " db=");
+ if (pos) {
+ *pos = '\0';
+#ifdef CONFIG_SQLITE
+ pos += 4;
+ data->sqlite_db = db_open(pos);
+ if (data->sqlite_db == NULL)
+ goto fail;
+#endif /* CONFIG_SQLITE */
+ }
if (os_strncmp(data->fname, "unix:", 5) == 0) {
- if (eap_sim_db_open_socket(data))
- goto fail;
+ if (eap_sim_db_open_socket(data)) {
+ wpa_printf(MSG_DEBUG, "EAP-SIM DB: External database "
+ "connection not available - will retry "
+ "later");
+ }
}
return data;
@@ -428,7 +737,7 @@ fail:
static void eap_sim_db_free_pseudonym(struct eap_sim_pseudonym *p)
{
- os_free(p->identity);
+ os_free(p->permanent);
os_free(p->pseudonym);
os_free(p);
}
@@ -436,7 +745,7 @@ static void eap_sim_db_free_pseudonym(struct eap_sim_pseudonym *p)
static void eap_sim_db_free_reauth(struct eap_sim_reauth *r)
{
- os_free(r->identity);
+ os_free(r->permanent);
os_free(r->reauth_id);
os_free(r);
}
@@ -453,6 +762,13 @@ void eap_sim_db_deinit(void *priv)
struct eap_sim_reauth *r, *prevr;
struct eap_sim_db_pending *pending, *prev_pending;
+#ifdef CONFIG_SQLITE
+ if (data->sqlite_db) {
+ sqlite3_close(data->sqlite_db);
+ data->sqlite_db = NULL;
+ }
+#endif /* CONFIG_SQLITE */
+
eap_sim_db_close_socket(data);
os_free(data->fname);
@@ -519,9 +835,8 @@ static void eap_sim_db_expire_pending(struct eap_sim_db_data *data)
/**
* eap_sim_db_get_gsm_triplets - Get GSM triplets
- * @priv: Private data pointer from eap_sim_db_init()
- * @identity: User name identity
- * @identity_len: Length of identity in bytes
+ * @data: Private data pointer from eap_sim_db_init()
+ * @username: Permanent username (prefix | IMSI)
* @max_chal: Maximum number of triplets
* @_rand: Buffer for RAND values
* @kc: Buffer for Kc values
@@ -533,9 +848,6 @@ static void eap_sim_db_expire_pending(struct eap_sim_db_data *data)
* callback function registered with eap_sim_db_init() will be called once the
* results become available.
*
- * In most cases, the user name is '1' | IMSI, i.e., 1 followed by the IMSI in
- * ASCII format.
- *
* When using an external server for GSM triplets, this function can always
* start a request and return EAP_SIM_DB_PENDING immediately if authentication
* triplets are not available. Once the triplets are received, callback
@@ -544,39 +856,28 @@ static void eap_sim_db_expire_pending(struct eap_sim_db_data *data)
* function will then be called again and the newly received triplets will then
* be given to the caller.
*/
-int eap_sim_db_get_gsm_triplets(void *priv, const u8 *identity,
- size_t identity_len, int max_chal,
+int eap_sim_db_get_gsm_triplets(struct eap_sim_db_data *data,
+ const char *username, int max_chal,
u8 *_rand, u8 *kc, u8 *sres,
void *cb_session_ctx)
{
- struct eap_sim_db_data *data = priv;
struct eap_sim_db_pending *entry;
int len, ret;
- size_t i;
char msg[40];
+ const char *imsi;
+ size_t imsi_len;
- if (identity_len < 2 || identity[0] != EAP_SIM_PERMANENT_PREFIX) {
- wpa_hexdump_ascii(MSG_DEBUG, "EAP-SIM DB: unexpected identity",
- identity, identity_len);
+ if (username == NULL || username[0] != EAP_SIM_PERMANENT_PREFIX ||
+ username[1] == '\0' || os_strlen(username) > sizeof(entry->imsi)) {
+ wpa_printf(MSG_DEBUG, "EAP-SIM DB: unexpected username '%s'",
+ username);
return EAP_SIM_DB_FAILURE;
}
- identity++;
- identity_len--;
- for (i = 0; i < identity_len; i++) {
- if (identity[i] == '@') {
- identity_len = i;
- break;
- }
- }
- if (identity_len + 1 > sizeof(entry->imsi)) {
- wpa_hexdump_ascii(MSG_DEBUG, "EAP-SIM DB: unexpected identity",
- identity, identity_len);
- return EAP_SIM_DB_FAILURE;
- }
- wpa_hexdump_ascii(MSG_DEBUG, "EAP-SIM DB: Get GSM triplets for IMSI",
- identity, identity_len);
+ imsi = username + 1;
+ wpa_printf(MSG_DEBUG, "EAP-SIM DB: Get GSM triplets for IMSI '%s'",
+ imsi);
- entry = eap_sim_db_get_pending(data, identity, identity_len, 0);
+ entry = eap_sim_db_get_pending(data, imsi, 0);
if (entry) {
int num_chal;
if (entry->state == FAILURE) {
@@ -611,18 +912,19 @@ int eap_sim_db_get_gsm_triplets(void *priv, const u8 *identity,
return EAP_SIM_DB_FAILURE;
}
+ imsi_len = os_strlen(imsi);
len = os_snprintf(msg, sizeof(msg), "SIM-REQ-AUTH ");
- if (len < 0 || len + identity_len >= sizeof(msg))
+ if (len < 0 || len + imsi_len >= sizeof(msg))
return EAP_SIM_DB_FAILURE;
- os_memcpy(msg + len, identity, identity_len);
- len += identity_len;
+ os_memcpy(msg + len, imsi, imsi_len);
+ len += imsi_len;
ret = os_snprintf(msg + len, sizeof(msg) - len, " %d", max_chal);
if (ret < 0 || (size_t) ret >= sizeof(msg) - len)
return EAP_SIM_DB_FAILURE;
len += ret;
- wpa_hexdump(MSG_DEBUG, "EAP-SIM DB: requesting SIM authentication "
- "data for IMSI", identity, identity_len);
+ wpa_printf(MSG_DEBUG, "EAP-SIM DB: requesting SIM authentication "
+ "data for IMSI '%s'", imsi);
if (eap_sim_db_send(data, msg, len) < 0)
return EAP_SIM_DB_FAILURE;
@@ -631,8 +933,7 @@ int eap_sim_db_get_gsm_triplets(void *priv, const u8 *identity,
return EAP_SIM_DB_FAILURE;
os_get_time(&entry->timestamp);
- os_memcpy(entry->imsi, identity, identity_len);
- entry->imsi_len = identity_len;
+ os_strlcpy(entry->imsi, imsi, sizeof(entry->imsi));
entry->cb_session_ctx = cb_session_ctx;
entry->state = PENDING;
eap_sim_db_add_pending(data, entry);
@@ -642,195 +943,12 @@ int eap_sim_db_get_gsm_triplets(void *priv, const u8 *identity,
}
-static struct eap_sim_pseudonym *
-eap_sim_db_get_pseudonym(struct eap_sim_db_data *data, const u8 *identity,
- size_t identity_len)
-{
- char *pseudonym;
- size_t len;
- struct eap_sim_pseudonym *p;
-
- if (identity_len == 0 ||
- (identity[0] != EAP_SIM_PSEUDONYM_PREFIX &&
- identity[0] != EAP_AKA_PSEUDONYM_PREFIX))
- return NULL;
-
- /* Remove possible realm from identity */
- len = 0;
- while (len < identity_len) {
- if (identity[len] == '@')
- break;
- len++;
- }
-
- pseudonym = os_malloc(len + 1);
- if (pseudonym == NULL)
- return NULL;
- os_memcpy(pseudonym, identity, len);
- pseudonym[len] = '\0';
-
- p = data->pseudonyms;
- while (p) {
- if (os_strcmp(p->pseudonym, pseudonym) == 0)
- break;
- p = p->next;
- }
-
- os_free(pseudonym);
-
- return p;
-}
-
-
-static struct eap_sim_pseudonym *
-eap_sim_db_get_pseudonym_id(struct eap_sim_db_data *data, const u8 *identity,
- size_t identity_len)
-{
- struct eap_sim_pseudonym *p;
-
- if (identity_len == 0 ||
- (identity[0] != EAP_SIM_PERMANENT_PREFIX &&
- identity[0] != EAP_AKA_PERMANENT_PREFIX))
- return NULL;
-
- p = data->pseudonyms;
- while (p) {
- if (identity_len == p->identity_len &&
- os_memcmp(p->identity, identity, identity_len) == 0)
- break;
- p = p->next;
- }
-
- return p;
-}
-
-
-static struct eap_sim_reauth *
-eap_sim_db_get_reauth(struct eap_sim_db_data *data, const u8 *identity,
- size_t identity_len)
-{
- char *reauth_id;
- size_t len;
- struct eap_sim_reauth *r;
-
- if (identity_len == 0 ||
- (identity[0] != EAP_SIM_REAUTH_ID_PREFIX &&
- identity[0] != EAP_AKA_REAUTH_ID_PREFIX))
- return NULL;
-
- /* Remove possible realm from identity */
- len = 0;
- while (len < identity_len) {
- if (identity[len] == '@')
- break;
- len++;
- }
-
- reauth_id = os_malloc(len + 1);
- if (reauth_id == NULL)
- return NULL;
- os_memcpy(reauth_id, identity, len);
- reauth_id[len] = '\0';
-
- r = data->reauths;
- while (r) {
- if (os_strcmp(r->reauth_id, reauth_id) == 0)
- break;
- r = r->next;
- }
-
- os_free(reauth_id);
-
- return r;
-}
-
-
-static struct eap_sim_reauth *
-eap_sim_db_get_reauth_id(struct eap_sim_db_data *data, const u8 *identity,
- size_t identity_len)
-{
- struct eap_sim_pseudonym *p;
- struct eap_sim_reauth *r;
-
- if (identity_len == 0)
- return NULL;
-
- p = eap_sim_db_get_pseudonym(data, identity, identity_len);
- if (p == NULL)
- p = eap_sim_db_get_pseudonym_id(data, identity, identity_len);
- if (p) {
- identity = p->identity;
- identity_len = p->identity_len;
- }
-
- r = data->reauths;
- while (r) {
- if (identity_len == r->identity_len &&
- os_memcmp(r->identity, identity, identity_len) == 0)
- break;
- r = r->next;
- }
-
- return r;
-}
-
-
-/**
- * eap_sim_db_identity_known - Verify whether the given identity is known
- * @priv: Private data pointer from eap_sim_db_init()
- * @identity: User name identity
- * @identity_len: Length of identity in bytes
- * Returns: 0 if the user is found or -1 on failure
- *
- * In most cases, the user name is ['0','1'] | IMSI, i.e., 1 followed by the
- * IMSI in ASCII format, ['2','3'] | pseudonym, or ['4','5'] | reauth_id.
- */
-int eap_sim_db_identity_known(void *priv, const u8 *identity,
- size_t identity_len)
-{
- struct eap_sim_db_data *data = priv;
-
- if (identity == NULL || identity_len < 2)
- return -1;
-
- if (identity[0] == EAP_SIM_PSEUDONYM_PREFIX ||
- identity[0] == EAP_AKA_PSEUDONYM_PREFIX) {
- struct eap_sim_pseudonym *p =
- eap_sim_db_get_pseudonym(data, identity, identity_len);
- return p ? 0 : -1;
- }
-
- if (identity[0] == EAP_SIM_REAUTH_ID_PREFIX ||
- identity[0] == EAP_AKA_REAUTH_ID_PREFIX) {
- struct eap_sim_reauth *r =
- eap_sim_db_get_reauth(data, identity, identity_len);
- return r ? 0 : -1;
- }
-
- if (identity[0] != EAP_SIM_PERMANENT_PREFIX &&
- identity[0] != EAP_AKA_PERMANENT_PREFIX) {
- /* Unknown identity prefix */
- return -1;
- }
-
- /* TODO: Should consider asking HLR/AuC gateway whether this permanent
- * identity is known. If it is, EAP-SIM/AKA can skip identity request.
- * In case of EAP-AKA, this would reduce number of needed round-trips.
- * Ideally, this would be done with one wait, i.e., just request
- * authentication data and store it for the next use. This would then
- * need to use similar pending-request functionality as the normal
- * request for authentication data at later phase.
- */
- return -1;
-}
-
-
static char * eap_sim_db_get_next(struct eap_sim_db_data *data, char prefix)
{
char *id, *pos, *end;
u8 buf[10];
- if (os_get_random(buf, sizeof(buf)))
+ if (random_get_bytes(buf, sizeof(buf)))
return NULL;
id = os_malloc(sizeof(buf) * 2 + 2);
if (id == NULL)
@@ -847,8 +965,8 @@ static char * eap_sim_db_get_next(struct eap_sim_db_data *data, char prefix)
/**
* eap_sim_db_get_next_pseudonym - EAP-SIM DB: Get next pseudonym
- * @priv: Private data pointer from eap_sim_db_init()
- * @aka: Using EAP-AKA instead of EAP-SIM
+ * @data: Private data pointer from eap_sim_db_init()
+ * @method: EAP method (SIM/AKA/AKA')
* Returns: Next pseudonym (allocated string) or %NULL on failure
*
* This function is used to generate a pseudonym for EAP-SIM. The returned
@@ -856,18 +974,31 @@ static char * eap_sim_db_get_next(struct eap_sim_db_data *data, char prefix)
* with eap_sim_db_add_pseudonym() once the authentication has been completed
* successfully. Caller is responsible for freeing the returned buffer.
*/
-char * eap_sim_db_get_next_pseudonym(void *priv, int aka)
+char * eap_sim_db_get_next_pseudonym(struct eap_sim_db_data *data,
+ enum eap_sim_db_method method)
{
- struct eap_sim_db_data *data = priv;
- return eap_sim_db_get_next(data, aka ? EAP_AKA_PSEUDONYM_PREFIX :
- EAP_SIM_PSEUDONYM_PREFIX);
+ char prefix = EAP_SIM_REAUTH_ID_PREFIX;
+
+ switch (method) {
+ case EAP_SIM_DB_SIM:
+ prefix = EAP_SIM_PSEUDONYM_PREFIX;
+ break;
+ case EAP_SIM_DB_AKA:
+ prefix = EAP_AKA_PSEUDONYM_PREFIX;
+ break;
+ case EAP_SIM_DB_AKA_PRIME:
+ prefix = EAP_AKA_PRIME_PSEUDONYM_PREFIX;
+ break;
+ }
+
+ return eap_sim_db_get_next(data, prefix);
}
/**
* eap_sim_db_get_next_reauth_id - EAP-SIM DB: Get next reauth_id
- * @priv: Private data pointer from eap_sim_db_init()
- * @aka: Using EAP-AKA instead of EAP-SIM
+ * @data: Private data pointer from eap_sim_db_init()
+ * @method: EAP method (SIM/AKA/AKA')
* Returns: Next reauth_id (allocated string) or %NULL on failure
*
* This function is used to generate a fast re-authentication identity for
@@ -876,19 +1007,31 @@ char * eap_sim_db_get_next_pseudonym(void *priv, int aka)
* has been completed successfully. Caller is responsible for freeing the
* returned buffer.
*/
-char * eap_sim_db_get_next_reauth_id(void *priv, int aka)
+char * eap_sim_db_get_next_reauth_id(struct eap_sim_db_data *data,
+ enum eap_sim_db_method method)
{
- struct eap_sim_db_data *data = priv;
- return eap_sim_db_get_next(data, aka ? EAP_AKA_REAUTH_ID_PREFIX :
- EAP_SIM_REAUTH_ID_PREFIX);
+ char prefix = EAP_SIM_REAUTH_ID_PREFIX;
+
+ switch (method) {
+ case EAP_SIM_DB_SIM:
+ prefix = EAP_SIM_REAUTH_ID_PREFIX;
+ break;
+ case EAP_SIM_DB_AKA:
+ prefix = EAP_AKA_REAUTH_ID_PREFIX;
+ break;
+ case EAP_SIM_DB_AKA_PRIME:
+ prefix = EAP_AKA_PRIME_REAUTH_ID_PREFIX;
+ break;
+ }
+
+ return eap_sim_db_get_next(data, prefix);
}
/**
* eap_sim_db_add_pseudonym - EAP-SIM DB: Add new pseudonym
- * @priv: Private data pointer from eap_sim_db_init()
- * @identity: Identity of the user (may be permanent identity or pseudonym)
- * @identity_len: Length of identity
+ * @data: Private data pointer from eap_sim_db_init()
+ * @permanent: Permanent username
* @pseudonym: Pseudonym for this user. This needs to be an allocated buffer,
* e.g., return value from eap_sim_db_get_next_pseudonym(). Caller must not
* free it.
@@ -897,20 +1040,22 @@ char * eap_sim_db_get_next_reauth_id(void *priv, int aka)
* This function adds a new pseudonym for EAP-SIM user. EAP-SIM DB is
* responsible of freeing pseudonym buffer once it is not needed anymore.
*/
-int eap_sim_db_add_pseudonym(void *priv, const u8 *identity,
- size_t identity_len, char *pseudonym)
+int eap_sim_db_add_pseudonym(struct eap_sim_db_data *data,
+ const char *permanent, char *pseudonym)
{
- struct eap_sim_db_data *data = priv;
struct eap_sim_pseudonym *p;
- wpa_hexdump_ascii(MSG_DEBUG, "EAP-SIM DB: Add pseudonym for identity",
- identity, identity_len);
- wpa_printf(MSG_DEBUG, "EAP-SIM DB: Pseudonym: %s", pseudonym);
+ wpa_printf(MSG_DEBUG, "EAP-SIM DB: Add pseudonym '%s' for permanent "
+ "username '%s'", pseudonym, permanent);
/* TODO: could store last two pseudonyms */
- p = eap_sim_db_get_pseudonym(data, identity, identity_len);
- if (p == NULL)
- p = eap_sim_db_get_pseudonym_id(data, identity, identity_len);
-
+#ifdef CONFIG_SQLITE
+ if (data->sqlite_db)
+ return db_add_pseudonym(data, permanent, pseudonym);
+#endif /* CONFIG_SQLITE */
+ for (p = data->pseudonyms; p; p = p->next) {
+ if (os_strcmp(permanent, p->permanent) == 0)
+ break;
+ }
if (p) {
wpa_printf(MSG_DEBUG, "EAP-SIM DB: Replacing previous "
"pseudonym: %s", p->pseudonym);
@@ -926,14 +1071,12 @@ int eap_sim_db_add_pseudonym(void *priv, const u8 *identity,
}
p->next = data->pseudonyms;
- p->identity = os_malloc(identity_len);
- if (p->identity == NULL) {
+ p->permanent = os_strdup(permanent);
+ if (p->permanent == NULL) {
os_free(p);
os_free(pseudonym);
return -1;
}
- os_memcpy(p->identity, identity, identity_len);
- p->identity_len = identity_len;
p->pseudonym = pseudonym;
data->pseudonyms = p;
@@ -943,18 +1086,16 @@ int eap_sim_db_add_pseudonym(void *priv, const u8 *identity,
static struct eap_sim_reauth *
-eap_sim_db_add_reauth_data(struct eap_sim_db_data *data, const u8 *identity,
- size_t identity_len, char *reauth_id, u16 counter)
+eap_sim_db_add_reauth_data(struct eap_sim_db_data *data,
+ const char *permanent,
+ char *reauth_id, u16 counter)
{
struct eap_sim_reauth *r;
- wpa_hexdump_ascii(MSG_DEBUG, "EAP-SIM DB: Add reauth_id for identity",
- identity, identity_len);
- wpa_printf(MSG_DEBUG, "EAP-SIM DB: reauth_id: %s", reauth_id);
-
- r = eap_sim_db_get_reauth(data, identity, identity_len);
- if (r == NULL)
- r = eap_sim_db_get_reauth_id(data, identity, identity_len);
+ for (r = data->reauths; r; r = r->next) {
+ if (os_strcmp(r->permanent, permanent) == 0)
+ break;
+ }
if (r) {
wpa_printf(MSG_DEBUG, "EAP-SIM DB: Replacing previous "
@@ -969,14 +1110,12 @@ eap_sim_db_add_reauth_data(struct eap_sim_db_data *data, const u8 *identity,
}
r->next = data->reauths;
- r->identity = os_malloc(identity_len);
- if (r->identity == NULL) {
+ r->permanent = os_strdup(permanent);
+ if (r->permanent == NULL) {
os_free(r);
os_free(reauth_id);
return NULL;
}
- os_memcpy(r->identity, identity, identity_len);
- r->identity_len = identity_len;
r->reauth_id = reauth_id;
data->reauths = r;
wpa_printf(MSG_DEBUG, "EAP-SIM DB: Added new reauth entry");
@@ -991,7 +1130,7 @@ eap_sim_db_add_reauth_data(struct eap_sim_db_data *data, const u8 *identity,
/**
* eap_sim_db_add_reauth - EAP-SIM DB: Add new re-authentication entry
* @priv: Private data pointer from eap_sim_db_init()
- * @identity: Identity of the user (may be permanent identity or pseudonym)
+ * @permanent: Permanent username
* @identity_len: Length of identity
* @reauth_id: reauth_id for this user. This needs to be an allocated buffer,
* e.g., return value from eap_sim_db_get_next_reauth_id(). Caller must not
@@ -1004,20 +1143,24 @@ eap_sim_db_add_reauth_data(struct eap_sim_db_data *data, const u8 *identity,
* EAP-SIM DB is responsible of freeing reauth_id buffer once it is not needed
* anymore.
*/
-int eap_sim_db_add_reauth(void *priv, const u8 *identity,
- size_t identity_len, char *reauth_id, u16 counter,
- const u8 *mk)
+int eap_sim_db_add_reauth(struct eap_sim_db_data *data, const char *permanent,
+ char *reauth_id, u16 counter, const u8 *mk)
{
- struct eap_sim_db_data *data = priv;
struct eap_sim_reauth *r;
- r = eap_sim_db_add_reauth_data(data, identity, identity_len, reauth_id,
- counter);
+ wpa_printf(MSG_DEBUG, "EAP-SIM DB: Add reauth_id '%s' for permanent "
+ "identity '%s'", reauth_id, permanent);
+
+#ifdef CONFIG_SQLITE
+ if (data->sqlite_db)
+ return db_add_reauth(data, permanent, reauth_id, counter, mk,
+ NULL, NULL, NULL);
+#endif /* CONFIG_SQLITE */
+ r = eap_sim_db_add_reauth_data(data, permanent, reauth_id, counter);
if (r == NULL)
return -1;
os_memcpy(r->mk, mk, EAP_SIM_MK_LEN);
- r->aka_prime = 0;
return 0;
}
@@ -1026,9 +1169,8 @@ int eap_sim_db_add_reauth(void *priv, const u8 *identity,
#ifdef EAP_SERVER_AKA_PRIME
/**
* eap_sim_db_add_reauth_prime - EAP-AKA' DB: Add new re-authentication entry
- * @priv: Private data pointer from eap_sim_db_init()
- * @identity: Identity of the user (may be permanent identity or pseudonym)
- * @identity_len: Length of identity
+ * @data: Private data pointer from eap_sim_db_init()
+ * @permanent: Permanent username
* @reauth_id: reauth_id for this user. This needs to be an allocated buffer,
* e.g., return value from eap_sim_db_get_next_reauth_id(). Caller must not
* free it.
@@ -1042,20 +1184,25 @@ int eap_sim_db_add_reauth(void *priv, const u8 *identity,
* EAP-SIM DB is responsible of freeing reauth_id buffer once it is not needed
* anymore.
*/
-int eap_sim_db_add_reauth_prime(void *priv, const u8 *identity,
- size_t identity_len, char *reauth_id,
- u16 counter, const u8 *k_encr, const u8 *k_aut,
- const u8 *k_re)
+int eap_sim_db_add_reauth_prime(struct eap_sim_db_data *data,
+ const char *permanent, char *reauth_id,
+ u16 counter, const u8 *k_encr,
+ const u8 *k_aut, const u8 *k_re)
{
- struct eap_sim_db_data *data = priv;
struct eap_sim_reauth *r;
- r = eap_sim_db_add_reauth_data(data, identity, identity_len, reauth_id,
- counter);
+ wpa_printf(MSG_DEBUG, "EAP-SIM DB: Add reauth_id '%s' for permanent "
+ "identity '%s'", reauth_id, permanent);
+
+#ifdef CONFIG_SQLITE
+ if (data->sqlite_db)
+ return db_add_reauth(data, permanent, reauth_id, counter, NULL,
+ k_encr, k_aut, k_re);
+#endif /* CONFIG_SQLITE */
+ r = eap_sim_db_add_reauth_data(data, permanent, reauth_id, counter);
if (r == NULL)
return -1;
- r->aka_prime = 1;
os_memcpy(r->k_encr, k_encr, EAP_SIM_K_ENCR_LEN);
os_memcpy(r->k_aut, k_aut, EAP_AKA_PRIME_K_AUT_LEN);
os_memcpy(r->k_re, k_re, EAP_AKA_PRIME_K_RE_LEN);
@@ -1067,66 +1214,75 @@ int eap_sim_db_add_reauth_prime(void *priv, const u8 *identity,
/**
* eap_sim_db_get_permanent - EAP-SIM DB: Get permanent identity
- * @priv: Private data pointer from eap_sim_db_init()
- * @identity: Identity of the user (may be permanent identity or pseudonym)
- * @identity_len: Length of identity
- * @len: Buffer for length of the returned permanent identity
- * Returns: Pointer to the permanent identity, or %NULL if not found
+ * @data: Private data pointer from eap_sim_db_init()
+ * @pseudonym: Pseudonym username
+ * Returns: Pointer to permanent username or %NULL if not found
*/
-const u8 * eap_sim_db_get_permanent(void *priv, const u8 *identity,
- size_t identity_len, size_t *len)
+const char *
+eap_sim_db_get_permanent(struct eap_sim_db_data *data, const char *pseudonym)
{
- struct eap_sim_db_data *data = priv;
struct eap_sim_pseudonym *p;
- if (identity == NULL)
- return NULL;
+#ifdef CONFIG_SQLITE
+ if (data->sqlite_db)
+ return db_get_pseudonym(data, pseudonym);
+#endif /* CONFIG_SQLITE */
- p = eap_sim_db_get_pseudonym(data, identity, identity_len);
- if (p == NULL)
- p = eap_sim_db_get_pseudonym_id(data, identity, identity_len);
- if (p == NULL)
- return NULL;
+ p = data->pseudonyms;
+ while (p) {
+ if (os_strcmp(p->pseudonym, pseudonym) == 0)
+ return p->permanent;
+ p = p->next;
+ }
- *len = p->identity_len;
- return p->identity;
+ return NULL;
}
/**
* eap_sim_db_get_reauth_entry - EAP-SIM DB: Get re-authentication entry
- * @priv: Private data pointer from eap_sim_db_init()
- * @identity: Identity of the user (may be permanent identity, pseudonym, or
- * reauth_id)
- * @identity_len: Length of identity
+ * @data: Private data pointer from eap_sim_db_init()
+ * @reauth_id: Fast re-authentication username
* Returns: Pointer to the re-auth entry, or %NULL if not found
*/
struct eap_sim_reauth *
-eap_sim_db_get_reauth_entry(void *priv, const u8 *identity,
- size_t identity_len)
+eap_sim_db_get_reauth_entry(struct eap_sim_db_data *data,
+ const char *reauth_id)
{
- struct eap_sim_db_data *data = priv;
struct eap_sim_reauth *r;
- if (identity == NULL)
- return NULL;
- r = eap_sim_db_get_reauth(data, identity, identity_len);
- if (r == NULL)
- r = eap_sim_db_get_reauth_id(data, identity, identity_len);
+#ifdef CONFIG_SQLITE
+ if (data->sqlite_db)
+ return db_get_reauth(data, reauth_id);
+#endif /* CONFIG_SQLITE */
+
+ r = data->reauths;
+ while (r) {
+ if (os_strcmp(r->reauth_id, reauth_id) == 0)
+ break;
+ r = r->next;
+ }
+
return r;
}
/**
* eap_sim_db_remove_reauth - EAP-SIM DB: Remove re-authentication entry
- * @priv: Private data pointer from eap_sim_db_init()
+ * @data: Private data pointer from eap_sim_db_init()
* @reauth: Pointer to re-authentication entry from
* eap_sim_db_get_reauth_entry()
*/
-void eap_sim_db_remove_reauth(void *priv, struct eap_sim_reauth *reauth)
+void eap_sim_db_remove_reauth(struct eap_sim_db_data *data,
+ struct eap_sim_reauth *reauth)
{
- struct eap_sim_db_data *data = priv;
struct eap_sim_reauth *r, *prev = NULL;
+#ifdef CONFIG_SQLITE
+ if (data->sqlite_db) {
+ db_remove_reauth(data, reauth);
+ return;
+ }
+#endif /* CONFIG_SQLITE */
r = data->reauths;
while (r) {
if (r == reauth) {
@@ -1145,9 +1301,8 @@ void eap_sim_db_remove_reauth(void *priv, struct eap_sim_reauth *reauth)
/**
* eap_sim_db_get_aka_auth - Get AKA authentication values
- * @priv: Private data pointer from eap_sim_db_init()
- * @identity: User name identity
- * @identity_len: Length of identity in bytes
+ * @data: Private data pointer from eap_sim_db_init()
+ * @username: Permanent username (prefix | IMSI)
* @_rand: Buffer for RAND value
* @autn: Buffer for AUTN value
* @ik: Buffer for IK value
@@ -1160,9 +1315,6 @@ void eap_sim_db_remove_reauth(void *priv, struct eap_sim_reauth *reauth)
* case, the callback function registered with eap_sim_db_init() will be
* called once the results become available.
*
- * In most cases, the user name is '0' | IMSI, i.e., 0 followed by the IMSI in
- * ASCII format.
- *
* When using an external server for AKA authentication, this function can
* always start a request and return EAP_SIM_DB_PENDING immediately if
* authentication triplets are not available. Once the authentication data are
@@ -1171,40 +1323,29 @@ void eap_sim_db_remove_reauth(void *priv, struct eap_sim_reauth *reauth)
* eap_sim_db_get_aka_auth() function will then be called again and the newly
* received triplets will then be given to the caller.
*/
-int eap_sim_db_get_aka_auth(void *priv, const u8 *identity,
- size_t identity_len, u8 *_rand, u8 *autn, u8 *ik,
- u8 *ck, u8 *res, size_t *res_len,
- void *cb_session_ctx)
+int eap_sim_db_get_aka_auth(struct eap_sim_db_data *data, const char *username,
+ u8 *_rand, u8 *autn, u8 *ik, u8 *ck,
+ u8 *res, size_t *res_len, void *cb_session_ctx)
{
- struct eap_sim_db_data *data = priv;
struct eap_sim_db_pending *entry;
int len;
- size_t i;
char msg[40];
+ const char *imsi;
+ size_t imsi_len;
- if (identity_len < 2 || identity == NULL ||
- identity[0] != EAP_AKA_PERMANENT_PREFIX) {
- wpa_hexdump_ascii(MSG_DEBUG, "EAP-SIM DB: unexpected identity",
- identity, identity_len);
- return EAP_SIM_DB_FAILURE;
- }
- identity++;
- identity_len--;
- for (i = 0; i < identity_len; i++) {
- if (identity[i] == '@') {
- identity_len = i;
- break;
- }
- }
- if (identity_len + 1 > sizeof(entry->imsi)) {
- wpa_hexdump_ascii(MSG_DEBUG, "EAP-SIM DB: unexpected identity",
- identity, identity_len);
+ if (username == NULL ||
+ (username[0] != EAP_AKA_PERMANENT_PREFIX &&
+ username[0] != EAP_AKA_PRIME_PERMANENT_PREFIX) ||
+ username[1] == '\0' || os_strlen(username) > sizeof(entry->imsi)) {
+ wpa_printf(MSG_DEBUG, "EAP-SIM DB: unexpected username '%s'",
+ username);
return EAP_SIM_DB_FAILURE;
}
- wpa_hexdump_ascii(MSG_DEBUG, "EAP-SIM DB: Get AKA auth for IMSI",
- identity, identity_len);
+ imsi = username + 1;
+ wpa_printf(MSG_DEBUG, "EAP-SIM DB: Get AKA auth for IMSI '%s'",
+ imsi);
- entry = eap_sim_db_get_pending(data, identity, identity_len, 1);
+ entry = eap_sim_db_get_pending(data, imsi, 1);
if (entry) {
if (entry->state == FAILURE) {
os_free(entry);
@@ -1235,14 +1376,15 @@ int eap_sim_db_get_aka_auth(void *priv, const u8 *identity,
return EAP_SIM_DB_FAILURE;
}
+ imsi_len = os_strlen(imsi);
len = os_snprintf(msg, sizeof(msg), "AKA-REQ-AUTH ");
- if (len < 0 || len + identity_len >= sizeof(msg))
+ if (len < 0 || len + imsi_len >= sizeof(msg))
return EAP_SIM_DB_FAILURE;
- os_memcpy(msg + len, identity, identity_len);
- len += identity_len;
+ os_memcpy(msg + len, imsi, imsi_len);
+ len += imsi_len;
- wpa_hexdump(MSG_DEBUG, "EAP-SIM DB: requesting AKA authentication "
- "data for IMSI", identity, identity_len);
+ wpa_printf(MSG_DEBUG, "EAP-SIM DB: requesting AKA authentication "
+ "data for IMSI '%s'", imsi);
if (eap_sim_db_send(data, msg, len) < 0)
return EAP_SIM_DB_FAILURE;
@@ -1252,8 +1394,7 @@ int eap_sim_db_get_aka_auth(void *priv, const u8 *identity,
os_get_time(&entry->timestamp);
entry->aka = 1;
- os_memcpy(entry->imsi, identity, identity_len);
- entry->imsi_len = identity_len;
+ os_strlcpy(entry->imsi, imsi, sizeof(entry->imsi));
entry->cb_session_ctx = cb_session_ctx;
entry->state = PENDING;
eap_sim_db_add_pending(data, entry);
@@ -1265,9 +1406,8 @@ int eap_sim_db_get_aka_auth(void *priv, const u8 *identity,
/**
* eap_sim_db_resynchronize - Resynchronize AKA AUTN
- * @priv: Private data pointer from eap_sim_db_init()
- * @identity: User name identity
- * @identity_len: Length of identity in bytes
+ * @data: Private data pointer from eap_sim_db_init()
+ * @username: Permanent username
* @auts: AUTS value from the peer
* @_rand: RAND value used in the rejected message
* Returns: 0 on success, -1 on failure
@@ -1278,42 +1418,35 @@ int eap_sim_db_get_aka_auth(void *priv, const u8 *identity,
* eap_sim_db_get_aka_auth() will be called again to to fetch updated
* RAND/AUTN values for the next challenge.
*/
-int eap_sim_db_resynchronize(void *priv, const u8 *identity,
- size_t identity_len, const u8 *auts,
- const u8 *_rand)
+int eap_sim_db_resynchronize(struct eap_sim_db_data *data,
+ const char *username,
+ const u8 *auts, const u8 *_rand)
{
- struct eap_sim_db_data *data = priv;
- size_t i;
+ const char *imsi;
+ size_t imsi_len;
- if (identity_len < 2 || identity == NULL ||
- identity[0] != EAP_AKA_PERMANENT_PREFIX) {
- wpa_hexdump_ascii(MSG_DEBUG, "EAP-SIM DB: unexpected identity",
- identity, identity_len);
- return -1;
- }
- identity++;
- identity_len--;
- for (i = 0; i < identity_len; i++) {
- if (identity[i] == '@') {
- identity_len = i;
- break;
- }
- }
- if (identity_len > 20) {
- wpa_hexdump_ascii(MSG_DEBUG, "EAP-SIM DB: unexpected identity",
- identity, identity_len);
+ if (username == NULL ||
+ (username[0] != EAP_AKA_PERMANENT_PREFIX &&
+ username[0] != EAP_AKA_PRIME_PERMANENT_PREFIX) ||
+ username[1] == '\0' || os_strlen(username) > 20) {
+ wpa_printf(MSG_DEBUG, "EAP-SIM DB: unexpected username '%s'",
+ username);
return -1;
}
+ imsi = username + 1;
+ wpa_printf(MSG_DEBUG, "EAP-SIM DB: Get AKA auth for IMSI '%s'",
+ imsi);
if (data->sock >= 0) {
char msg[100];
int len, ret;
+ imsi_len = os_strlen(imsi);
len = os_snprintf(msg, sizeof(msg), "AKA-AUTS ");
- if (len < 0 || len + identity_len >= sizeof(msg))
+ if (len < 0 || len + imsi_len >= sizeof(msg))
return -1;
- os_memcpy(msg + len, identity, identity_len);
- len += identity_len;
+ os_memcpy(msg + len, imsi, imsi_len);
+ len += imsi_len;
ret = os_snprintf(msg + len, sizeof(msg) - len, " ");
if (ret < 0 || (size_t) ret >= sizeof(msg) - len)
@@ -1327,11 +1460,42 @@ int eap_sim_db_resynchronize(void *priv, const u8 *identity,
len += ret;
len += wpa_snprintf_hex(msg + len, sizeof(msg) - len,
_rand, EAP_AKA_RAND_LEN);
- wpa_hexdump(MSG_DEBUG, "EAP-SIM DB: reporting AKA AUTS for "
- "IMSI", identity, identity_len);
+ wpa_printf(MSG_DEBUG, "EAP-SIM DB: reporting AKA AUTS for "
+ "IMSI '%s'", imsi);
if (eap_sim_db_send(data, msg, len) < 0)
return -1;
}
return 0;
}
+
+
+/**
+ * sim_get_username - Extract username from SIM identity
+ * @identity: Identity
+ * @identity_len: Identity length
+ * Returns: Allocated buffer with the username part of the identity
+ *
+ * Caller is responsible for freeing the returned buffer with os_free().
+ */
+char * sim_get_username(const u8 *identity, size_t identity_len)
+{
+ char *username;
+ size_t pos;
+
+ if (identity == NULL)
+ return NULL;
+
+ for (pos = 0; pos < identity_len; pos++) {
+ if (identity[pos] == '@' || identity[pos] == '\0')
+ break;
+ }
+
+ username = os_malloc(pos + 1);
+ if (username == NULL)
+ return NULL;
+ os_memcpy(username, identity, pos);
+ username[pos] = '\0';
+
+ return username;
+}
diff --git a/contrib/wpa/src/eap_server/eap_sim_db.h b/contrib/wpa/src/eap_server/eap_sim_db.h
index ab89ae9..53a1a7c 100644
--- a/contrib/wpa/src/eap_server/eap_sim_db.h
+++ b/contrib/wpa/src/eap_server/eap_sim_db.h
@@ -1,15 +1,9 @@
/*
* hostapd / EAP-SIM database/authenticator gateway
- * Copyright (c) 2005-2007, Jouni Malinen <j@w1.fi>
+ * Copyright (c) 2005-2008, 2012, Jouni Malinen <j@w1.fi>
*
- * This program is free software; you can redistribute it and/or modify
- * it under the terms of the GNU General Public License version 2 as
- * published by the Free Software Foundation.
- *
- * Alternatively, this software may be distributed under the terms of BSD
- * license.
- *
- * See README and COPYING for more details.
+ * This software may be distributed under the terms of the BSD license.
+ * See README for more details.
*/
#ifndef EAP_SIM_DB_H
@@ -24,49 +18,57 @@
#define EAP_AKA_PERMANENT_PREFIX '0'
#define EAP_AKA_PSEUDONYM_PREFIX '2'
#define EAP_AKA_REAUTH_ID_PREFIX '4'
+#define EAP_AKA_PRIME_PERMANENT_PREFIX '6'
+#define EAP_AKA_PRIME_PSEUDONYM_PREFIX '7'
+#define EAP_AKA_PRIME_REAUTH_ID_PREFIX '8'
+
+enum eap_sim_db_method {
+ EAP_SIM_DB_SIM,
+ EAP_SIM_DB_AKA,
+ EAP_SIM_DB_AKA_PRIME
+};
-void * eap_sim_db_init(const char *config,
- void (*get_complete_cb)(void *ctx, void *session_ctx),
- void *ctx);
+struct eap_sim_db_data;
+
+struct eap_sim_db_data *
+eap_sim_db_init(const char *config,
+ void (*get_complete_cb)(void *ctx, void *session_ctx),
+ void *ctx);
void eap_sim_db_deinit(void *priv);
-int eap_sim_db_get_gsm_triplets(void *priv, const u8 *identity,
- size_t identity_len, int max_chal,
+int eap_sim_db_get_gsm_triplets(struct eap_sim_db_data *data,
+ const char *username, int max_chal,
u8 *_rand, u8 *kc, u8 *sres,
void *cb_session_ctx);
#define EAP_SIM_DB_FAILURE -1
#define EAP_SIM_DB_PENDING -2
-int eap_sim_db_identity_known(void *priv, const u8 *identity,
- size_t identity_len);
-
-char * eap_sim_db_get_next_pseudonym(void *priv, int aka);
+char * eap_sim_db_get_next_pseudonym(struct eap_sim_db_data *data,
+ enum eap_sim_db_method method);
-char * eap_sim_db_get_next_reauth_id(void *priv, int aka);
+char * eap_sim_db_get_next_reauth_id(struct eap_sim_db_data *data,
+ enum eap_sim_db_method method);
-int eap_sim_db_add_pseudonym(void *priv, const u8 *identity,
- size_t identity_len, char *pseudonym);
+int eap_sim_db_add_pseudonym(struct eap_sim_db_data *data,
+ const char *permanent, char *pseudonym);
-int eap_sim_db_add_reauth(void *priv, const u8 *identity,
- size_t identity_len, char *reauth_id, u16 counter,
- const u8 *mk);
-int eap_sim_db_add_reauth_prime(void *priv, const u8 *identity,
- size_t identity_len, char *reauth_id,
- u16 counter, const u8 *k_encr, const u8 *k_aut,
- const u8 *k_re);
+int eap_sim_db_add_reauth(struct eap_sim_db_data *data, const char *permanent,
+ char *reauth_id, u16 counter, const u8 *mk);
+int eap_sim_db_add_reauth_prime(struct eap_sim_db_data *data,
+ const char *permanent,
+ char *reauth_id, u16 counter, const u8 *k_encr,
+ const u8 *k_aut, const u8 *k_re);
-const u8 * eap_sim_db_get_permanent(void *priv, const u8 *identity,
- size_t identity_len, size_t *len);
+const char * eap_sim_db_get_permanent(struct eap_sim_db_data *data,
+ const char *pseudonym);
struct eap_sim_reauth {
struct eap_sim_reauth *next;
- u8 *identity;
- size_t identity_len;
- char *reauth_id;
+ char *permanent; /* Permanent username */
+ char *reauth_id; /* Fast re-authentication username */
u16 counter;
- int aka_prime;
u8 mk[EAP_SIM_MK_LEN];
u8 k_encr[EAP_SIM_K_ENCR_LEN];
u8 k_aut[EAP_AKA_PRIME_K_AUT_LEN];
@@ -74,18 +76,20 @@ struct eap_sim_reauth {
};
struct eap_sim_reauth *
-eap_sim_db_get_reauth_entry(void *priv, const u8 *identity,
- size_t identity_len);
+eap_sim_db_get_reauth_entry(struct eap_sim_db_data *data,
+ const char *reauth_id);
-void eap_sim_db_remove_reauth(void *priv, struct eap_sim_reauth *reauth);
+void eap_sim_db_remove_reauth(struct eap_sim_db_data *data,
+ struct eap_sim_reauth *reauth);
-int eap_sim_db_get_aka_auth(void *priv, const u8 *identity,
- size_t identity_len, u8 *_rand, u8 *autn, u8 *ik,
- u8 *ck, u8 *res, size_t *res_len,
- void *cb_session_ctx);
+int eap_sim_db_get_aka_auth(struct eap_sim_db_data *data, const char *username,
+ u8 *_rand, u8 *autn, u8 *ik, u8 *ck,
+ u8 *res, size_t *res_len, void *cb_session_ctx);
-int eap_sim_db_resynchronize(void *priv, const u8 *identity,
- size_t identity_len, const u8 *auts,
+int eap_sim_db_resynchronize(struct eap_sim_db_data *data,
+ const char *username, const u8 *auts,
const u8 *_rand);
+char * sim_get_username(const u8 *identity, size_t identity_len);
+
#endif /* EAP_SIM_DB_H */
diff --git a/contrib/wpa/src/eap_server/eap_tls_common.h b/contrib/wpa/src/eap_server/eap_tls_common.h
index c34c401..11f5827 100644
--- a/contrib/wpa/src/eap_server/eap_tls_common.h
+++ b/contrib/wpa/src/eap_server/eap_tls_common.h
@@ -2,14 +2,8 @@
* EAP-TLS/PEAP/TTLS/FAST server common functions
* Copyright (c) 2004-2009, Jouni Malinen <j@w1.fi>
*
- * This program is free software; you can redistribute it and/or modify
- * it under the terms of the GNU General Public License version 2 as
- * published by the Free Software Foundation.
- *
- * Alternatively, this software may be distributed under the terms of BSD
- * license.
- *
- * See README and COPYING for more details.
+ * This software may be distributed under the terms of the BSD license.
+ * See README for more details.
*/
#ifndef EAP_TLS_COMMON_H
@@ -68,7 +62,12 @@ struct eap_ssl_data {
/* could be up to 128 bytes, but only the first 64 bytes are used */
#define EAP_TLS_KEY_LEN 64
+/* dummy type used as a flag for UNAUTH-TLS */
+#define EAP_UNAUTH_TLS_TYPE 255
+
+struct wpabuf * eap_tls_msg_alloc(EapType type, size_t payload_len,
+ u8 code, u8 identifier);
int eap_server_tls_ssl_init(struct eap_sm *sm, struct eap_ssl_data *data,
int verify_peer);
void eap_server_tls_ssl_deinit(struct eap_sm *sm, struct eap_ssl_data *data);
diff --git a/contrib/wpa/src/eap_server/ikev2.c b/contrib/wpa/src/eap_server/ikev2.c
index 435ba26..0e77efb 100644
--- a/contrib/wpa/src/eap_server/ikev2.c
+++ b/contrib/wpa/src/eap_server/ikev2.c
@@ -2,20 +2,15 @@
* IKEv2 initiator (RFC 4306) for EAP-IKEV2
* Copyright (c) 2007, Jouni Malinen <j@w1.fi>
*
- * This program is free software; you can redistribute it and/or modify
- * it under the terms of the GNU General Public License version 2 as
- * published by the Free Software Foundation.
- *
- * Alternatively, this software may be distributed under the terms of BSD
- * license.
- *
- * See README and COPYING for more details.
+ * This software may be distributed under the terms of the BSD license.
+ * See README for more details.
*/
#include "includes.h"
#include "common.h"
#include "crypto/dh_groups.h"
+#include "crypto/random.h"
#include "ikev2.h"
@@ -403,7 +398,7 @@ static int ikev2_process_ker(struct ikev2_initiator_data *data,
}
/* RFC 4306, Section 3.4:
- * The length of DH public value MUST be equal to the lenght of the
+ * The length of DH public value MUST be equal to the length of the
* prime modulus.
*/
if (ker_len - 4 != data->dh->prime_len) {
@@ -1100,7 +1095,7 @@ static struct wpabuf * ikev2_build_sa_init(struct ikev2_initiator_data *data)
data->i_spi, IKEV2_SPI_LEN);
data->i_nonce_len = IKEV2_NONCE_MIN_LEN;
- if (os_get_random(data->i_nonce, data->i_nonce_len))
+ if (random_get_bytes(data->i_nonce, data->i_nonce_len))
return NULL;
wpa_hexdump(MSG_DEBUG, "IKEV2: Ni", data->i_nonce, data->i_nonce_len);
@@ -1148,7 +1143,7 @@ static struct wpabuf * ikev2_build_sa_auth(struct ikev2_initiator_data *data)
if (data->shared_secret == NULL)
return NULL;
data->shared_secret_len = 16;
- if (os_get_random(data->shared_secret, 16))
+ if (random_get_bytes(data->shared_secret, 16))
return NULL;
} else {
os_free(data->shared_secret);
diff --git a/contrib/wpa/src/eap_server/ikev2.h b/contrib/wpa/src/eap_server/ikev2.h
index 8349fbe..051a938 100644
--- a/contrib/wpa/src/eap_server/ikev2.h
+++ b/contrib/wpa/src/eap_server/ikev2.h
@@ -2,14 +2,8 @@
* IKEv2 initiator (RFC 4306) for EAP-IKEV2
* Copyright (c) 2007, Jouni Malinen <j@w1.fi>
*
- * This program is free software; you can redistribute it and/or modify
- * it under the terms of the GNU General Public License version 2 as
- * published by the Free Software Foundation.
- *
- * Alternatively, this software may be distributed under the terms of BSD
- * license.
- *
- * See README and COPYING for more details.
+ * This software may be distributed under the terms of the BSD license.
+ * See README for more details.
*/
#ifndef IKEV2_H
diff --git a/contrib/wpa/src/eap_server/tncs.c b/contrib/wpa/src/eap_server/tncs.c
index 497b51a..5e332ae 100644
--- a/contrib/wpa/src/eap_server/tncs.c
+++ b/contrib/wpa/src/eap_server/tncs.c
@@ -2,14 +2,8 @@
* EAP-TNC - TNCS (IF-IMV, IF-TNCCS, and IF-TNCCS-SOH)
* Copyright (c) 2007-2008, Jouni Malinen <j@w1.fi>
*
- * This program is free software; you can redistribute it and/or modify
- * it under the terms of the GNU General Public License version 2 as
- * published by the Free Software Foundation.
- *
- * Alternatively, this software may be distributed under the terms of BSD
- * license.
- *
- * See README and COPYING for more details.
+ * This software may be distributed under the terms of the BSD license.
+ * See README for more details.
*/
#include "includes.h"
@@ -234,11 +228,11 @@ TNC_Result TNC_TNCS_ReportMessageTypes(
return TNC_RESULT_INVALID_PARAMETER;
os_free(imv->supported_types);
imv->supported_types =
- os_malloc(typeCount * sizeof(TNC_MessageTypeList));
+ os_malloc(typeCount * sizeof(TNC_MessageType));
if (imv->supported_types == NULL)
return TNC_RESULT_FATAL;
os_memcpy(imv->supported_types, supportedTypes,
- typeCount * sizeof(TNC_MessageTypeList));
+ typeCount * sizeof(TNC_MessageType));
imv->num_supported_types = typeCount;
return TNC_RESULT_SUCCESS;
diff --git a/contrib/wpa/src/eap_server/tncs.h b/contrib/wpa/src/eap_server/tncs.h
index 18a3a1f..ac7251b 100644
--- a/contrib/wpa/src/eap_server/tncs.h
+++ b/contrib/wpa/src/eap_server/tncs.h
@@ -2,14 +2,8 @@
* EAP-TNC - TNCS (IF-IMV, IF-TNCCS, and IF-TNCCS-SOH)
* Copyright (c) 2007-2008, Jouni Malinen <j@w1.fi>
*
- * This program is free software; you can redistribute it and/or modify
- * it under the terms of the GNU General Public License version 2 as
- * published by the Free Software Foundation.
- *
- * Alternatively, this software may be distributed under the terms of BSD
- * license.
- *
- * See README and COPYING for more details.
+ * This software may be distributed under the terms of the BSD license.
+ * See README for more details.
*/
#ifndef TNCS_H
diff --git a/contrib/wpa/src/eapol_auth/Makefile b/contrib/wpa/src/eapol_auth/Makefile
deleted file mode 100644
index 9c41962..0000000
--- a/contrib/wpa/src/eapol_auth/Makefile
+++ /dev/null
@@ -1,8 +0,0 @@
-all:
- @echo Nothing to be made.
-
-clean:
- rm -f *~ *.o *.d
-
-install:
- @echo Nothing to be made.
diff --git a/contrib/wpa/src/eapol_auth/eapol_auth_dump.c b/contrib/wpa/src/eapol_auth/eapol_auth_dump.c
index a0f0e8d..b6e0b13 100644
--- a/contrib/wpa/src/eapol_auth/eapol_auth_dump.c
+++ b/contrib/wpa/src/eapol_auth/eapol_auth_dump.c
@@ -2,14 +2,8 @@
* IEEE 802.1X-2004 Authenticator - State dump
* Copyright (c) 2002-2009, Jouni Malinen <j@w1.fi>
*
- * This program is free software; you can redistribute it and/or modify
- * it under the terms of the GNU General Public License version 2 as
- * published by the Free Software Foundation.
- *
- * Alternatively, this software may be distributed under the terms of BSD
- * license.
- *
- * See README and COPYING for more details.
+ * This software may be distributed under the terms of the BSD license.
+ * See README for more details.
*/
#include "includes.h"
diff --git a/contrib/wpa/src/eapol_auth/eapol_auth_sm.c b/contrib/wpa/src/eapol_auth/eapol_auth_sm.c
index a1976e8..c3ccb46 100644
--- a/contrib/wpa/src/eapol_auth/eapol_auth_sm.c
+++ b/contrib/wpa/src/eapol_auth/eapol_auth_sm.c
@@ -2,14 +2,8 @@
* IEEE 802.1X-2004 Authenticator - EAPOL state machine
* Copyright (c) 2002-2009, Jouni Malinen <j@w1.fi>
*
- * This program is free software; you can redistribute it and/or modify
- * it under the terms of the GNU General Public License version 2 as
- * published by the Free Software Foundation.
- *
- * Alternatively, this software may be distributed under the terms of BSD
- * license.
- *
- * See README and COPYING for more details.
+ * This software may be distributed under the terms of the BSD license.
+ * See README for more details.
*/
#include "includes.h"
@@ -762,7 +756,9 @@ SM_STEP(CTRL_DIR)
struct eapol_state_machine *
eapol_auth_alloc(struct eapol_authenticator *eapol, const u8 *addr,
- int flags, const struct wpabuf *assoc_wps_ie, void *sta_ctx)
+ int flags, const struct wpabuf *assoc_wps_ie,
+ const struct wpabuf *assoc_p2p_ie, void *sta_ctx,
+ const char *identity, const char *radius_cui)
{
struct eapol_state_machine *sm;
struct eap_config eap_conf;
@@ -829,7 +825,11 @@ eapol_auth_alloc(struct eapol_authenticator *eapol, const u8 *addr,
eap_conf.tnc = eapol->conf.tnc;
eap_conf.wps = eapol->conf.wps;
eap_conf.assoc_wps_ie = assoc_wps_ie;
+ eap_conf.assoc_p2p_ie = assoc_p2p_ie;
eap_conf.peer_addr = addr;
+ eap_conf.fragment_size = eapol->conf.fragment_size;
+ eap_conf.pwd_group = eapol->conf.pwd_group;
+ eap_conf.pbc_in_m1 = eapol->conf.pbc_in_m1;
sm->eap = eap_server_sm_init(sm, &eapol_cb, &eap_conf);
if (sm->eap == NULL) {
eapol_auth_free(sm);
@@ -839,6 +839,15 @@ eapol_auth_alloc(struct eapol_authenticator *eapol, const u8 *addr,
eapol_auth_initialize(sm);
+ if (identity) {
+ sm->identity = (u8 *) os_strdup(identity);
+ if (sm->identity)
+ sm->identity_len = os_strlen(identity);
+ }
+ if (radius_cui)
+ sm->radius_cui = wpabuf_alloc_copy(radius_cui,
+ os_strlen(radius_cui));
+
return sm;
}
@@ -1012,7 +1021,7 @@ static struct eapol_callbacks eapol_cb =
int eapol_auth_eap_pending_cb(struct eapol_state_machine *sm, void *ctx)
{
- if (sm == NULL || ctx != sm->eap)
+ if (sm == NULL || ctx == NULL || ctx != sm->eap)
return -1;
eap_sm_pending_cb(sm->eap);
@@ -1034,6 +1043,8 @@ static int eapol_auth_conf_clone(struct eapol_auth_config *dst,
dst->msg_ctx = src->msg_ctx;
dst->eap_sim_db_priv = src->eap_sim_db_priv;
os_free(dst->eap_req_id_text);
+ dst->pwd_group = src->pwd_group;
+ dst->pbc_in_m1 = src->pbc_in_m1;
if (src->eap_req_id_text) {
dst->eap_req_id_text = os_malloc(src->eap_req_id_text_len);
if (dst->eap_req_id_text == NULL)
@@ -1077,6 +1088,7 @@ static int eapol_auth_conf_clone(struct eapol_auth_config *dst,
dst->eap_sim_aka_result_ind = src->eap_sim_aka_result_ind;
dst->tnc = src->tnc;
dst->wps = src->wps;
+ dst->fragment_size = src->fragment_size;
return 0;
}
diff --git a/contrib/wpa/src/eapol_auth/eapol_auth_sm.h b/contrib/wpa/src/eapol_auth/eapol_auth_sm.h
index ef943ad..b50bbdd 100644
--- a/contrib/wpa/src/eapol_auth/eapol_auth_sm.h
+++ b/contrib/wpa/src/eapol_auth/eapol_auth_sm.h
@@ -2,14 +2,8 @@
* IEEE 802.1X-2004 Authenticator - EAPOL state machine
* Copyright (c) 2002-2009, Jouni Malinen <j@w1.fi>
*
- * This program is free software; you can redistribute it and/or modify
- * it under the terms of the GNU General Public License version 2 as
- * published by the Free Software Foundation.
- *
- * Alternatively, this software may be distributed under the terms of BSD
- * license.
- *
- * See README and COPYING for more details.
+ * This software may be distributed under the terms of the BSD license.
+ * See README for more details.
*/
#ifndef EAPOL_AUTH_SM_H
@@ -40,6 +34,9 @@ struct eapol_auth_config {
int eap_sim_aka_result_ind;
int tnc;
struct wps_context *wps;
+ int fragment_size;
+ u16 pwd_group;
+ int pbc_in_m1;
/* Opaque context pointer to owner data for callback functions */
void *ctx;
@@ -79,7 +76,9 @@ struct eapol_authenticator * eapol_auth_init(struct eapol_auth_config *conf,
void eapol_auth_deinit(struct eapol_authenticator *eapol);
struct eapol_state_machine *
eapol_auth_alloc(struct eapol_authenticator *eapol, const u8 *addr,
- int flags, const struct wpabuf *assoc_wps_ie, void *sta_ctx);
+ int flags, const struct wpabuf *assoc_wps_ie,
+ const struct wpabuf *assoc_p2p_ie, void *sta_ctx,
+ const char *identity, const char *radius_cui);
void eapol_auth_free(struct eapol_state_machine *sm);
void eapol_auth_step(struct eapol_state_machine *sm);
void eapol_auth_dump_state(FILE *f, const char *prefix,
diff --git a/contrib/wpa/src/eapol_auth/eapol_auth_sm_i.h b/contrib/wpa/src/eapol_auth/eapol_auth_sm_i.h
index 1000da4..d7f893a 100644
--- a/contrib/wpa/src/eapol_auth/eapol_auth_sm_i.h
+++ b/contrib/wpa/src/eapol_auth/eapol_auth_sm_i.h
@@ -2,14 +2,8 @@
* IEEE 802.1X-2004 Authenticator - EAPOL state machine (internal definitions)
* Copyright (c) 2002-2009, Jouni Malinen <j@w1.fi>
*
- * This program is free software; you can redistribute it and/or modify
- * it under the terms of the GNU General Public License version 2 as
- * published by the Free Software Foundation.
- *
- * Alternatively, this software may be distributed under the terms of BSD
- * license.
- *
- * See README and COPYING for more details.
+ * This software may be distributed under the terms of the BSD license.
+ * See README for more details.
*/
#ifndef EAPOL_AUTH_SM_I_H
@@ -163,6 +157,7 @@ struct eapol_state_machine {
* Authentication server */
u8 eap_type_supp; /* EAP type of the last EAP packet from Supplicant */
struct radius_class_data radius_class;
+ struct wpabuf *radius_cui; /* Chargeable-User-Identity */
/* Keys for encrypting and signing EAPOL-Key frames */
u8 *eapol_key_sign;
diff --git a/contrib/wpa/src/eapol_supp/Makefile b/contrib/wpa/src/eapol_supp/Makefile
deleted file mode 100644
index 9c41962..0000000
--- a/contrib/wpa/src/eapol_supp/Makefile
+++ /dev/null
@@ -1,8 +0,0 @@
-all:
- @echo Nothing to be made.
-
-clean:
- rm -f *~ *.o *.d
-
-install:
- @echo Nothing to be made.
diff --git a/contrib/wpa/src/eapol_supp/eapol_supp_sm.c b/contrib/wpa/src/eapol_supp/eapol_supp_sm.c
index 77cd564..f90fb62 100644
--- a/contrib/wpa/src/eapol_supp/eapol_supp_sm.c
+++ b/contrib/wpa/src/eapol_supp/eapol_supp_sm.c
@@ -1,15 +1,9 @@
/*
* EAPOL supplicant state machines
- * Copyright (c) 2004-2008, Jouni Malinen <j@w1.fi>
+ * Copyright (c) 2004-2012, Jouni Malinen <j@w1.fi>
*
- * This program is free software; you can redistribute it and/or modify
- * it under the terms of the GNU General Public License version 2 as
- * published by the Free Software Foundation.
- *
- * Alternatively, this software may be distributed under the terms of BSD
- * license.
- *
- * See README and COPYING for more details.
+ * This software may be distributed under the terms of the BSD license.
+ * See README for more details.
*/
#include "includes.h"
@@ -145,46 +139,6 @@ struct eapol_sm {
};
-#define IEEE8021X_REPLAY_COUNTER_LEN 8
-#define IEEE8021X_KEY_SIGN_LEN 16
-#define IEEE8021X_KEY_IV_LEN 16
-
-#define IEEE8021X_KEY_INDEX_FLAG 0x80
-#define IEEE8021X_KEY_INDEX_MASK 0x03
-
-#ifdef _MSC_VER
-#pragma pack(push, 1)
-#endif /* _MSC_VER */
-
-struct ieee802_1x_eapol_key {
- u8 type;
- /* Note: key_length is unaligned */
- u8 key_length[2];
- /* does not repeat within the life of the keying material used to
- * encrypt the Key field; 64-bit NTP timestamp MAY be used here */
- u8 replay_counter[IEEE8021X_REPLAY_COUNTER_LEN];
- u8 key_iv[IEEE8021X_KEY_IV_LEN]; /* cryptographically random number */
- u8 key_index; /* key flag in the most significant bit:
- * 0 = broadcast (default key),
- * 1 = unicast (key mapping key); key index is in the
- * 7 least significant bits */
- /* HMAC-MD5 message integrity check computed with MS-MPPE-Send-Key as
- * the key */
- u8 key_signature[IEEE8021X_KEY_SIGN_LEN];
-
- /* followed by key: if packet body length = 44 + key length, then the
- * key field (of key_length bytes) contains the key in encrypted form;
- * if packet body length = 44, key field is absent and key_length
- * represents the number of least significant octets from
- * MS-MPPE-Send-Key attribute to be used as the keying material;
- * RC4 key used in encryption = Key-IV + MS-MPPE-Recv-Key */
-} STRUCT_PACKED;
-
-#ifdef _MSC_VER
-#pragma pack(pop)
-#endif /* _MSC_VER */
-
-
static void eapol_sm_txLogoff(struct eapol_sm *sm);
static void eapol_sm_txStart(struct eapol_sm *sm);
static void eapol_sm_processKey(struct eapol_sm *sm);
@@ -268,6 +222,15 @@ SM_STATE(SUPP_PAE, DISCONNECTED)
sm->unicast_key_received = FALSE;
sm->broadcast_key_received = FALSE;
+
+ /*
+ * IEEE Std 802.1X-2004 does not clear heldWhile here, but doing so
+ * allows the timer tick to be stopped more quickly when the port is
+ * not enabled. Since this variable is used only within HELD state,
+ * clearing it on initialization does not change actual state machine
+ * behavior.
+ */
+ sm->heldWhile = 0;
}
@@ -535,6 +498,15 @@ SM_STATE(SUPP_BE, INITIALIZE)
SM_ENTRY(SUPP_BE, INITIALIZE);
eapol_sm_abortSupp(sm);
sm->suppAbort = FALSE;
+
+ /*
+ * IEEE Std 802.1X-2004 does not clear authWhile here, but doing so
+ * allows the timer tick to be stopped more quickly when the port is
+ * not enabled. Since this variable is used only within RECEIVE state,
+ * clearing it on initialization does not change actual state machine
+ * behavior.
+ */
+ sm->authWhile = 0;
}
@@ -561,7 +533,7 @@ SM_STEP(SUPP_BE)
* IEEE Std 802.1X-2004 has transitions from REQUEST to FAIL
* and SUCCESS based on eapFail and eapSuccess, respectively.
* However, IEEE Std 802.1X-2004 is also specifying that
- * eapNoResp should be set in conjuction with eapSuccess and
+ * eapNoResp should be set in conjunction with eapSuccess and
* eapFail which would mean that more than one of the
* transitions here would be activated at the same time.
* Skipping RESPONSE and/or RECEIVE states in these cases can
@@ -652,6 +624,7 @@ struct eap_key_data {
static void eapol_sm_processKey(struct eapol_sm *sm)
{
+#ifndef CONFIG_FIPS
struct ieee802_1x_hdr *hdr;
struct ieee802_1x_eapol_key *key;
struct eap_key_data keydata;
@@ -659,6 +632,7 @@ static void eapol_sm_processKey(struct eapol_sm *sm)
u8 ekey[IEEE8021X_KEY_IV_LEN + IEEE8021X_ENCR_KEY_LEN];
int key_len, res, sign_key_len, encr_key_len;
u16 rx_key_length;
+ size_t plen;
wpa_printf(MSG_DEBUG, "EAPOL: processKey");
if (sm->last_rx_key == NULL)
@@ -671,9 +645,12 @@ static void eapol_sm_processKey(struct eapol_sm *sm)
return;
}
+ if (sm->last_rx_key_len < sizeof(*hdr) + sizeof(*key))
+ return;
hdr = (struct ieee802_1x_hdr *) sm->last_rx_key;
key = (struct ieee802_1x_eapol_key *) (hdr + 1);
- if (sizeof(*hdr) + be_to_host16(hdr->length) > sm->last_rx_key_len) {
+ plen = be_to_host16(hdr->length);
+ if (sizeof(*hdr) + plen > sm->last_rx_key_len || plen < sizeof(*key)) {
wpa_printf(MSG_WARNING, "EAPOL: Too short EAPOL-Key frame");
return;
}
@@ -739,7 +716,7 @@ static void eapol_sm_processKey(struct eapol_sm *sm)
}
wpa_printf(MSG_DEBUG, "EAPOL: EAPOL-Key key signature verified");
- key_len = be_to_host16(hdr->length) - sizeof(*key);
+ key_len = plen - sizeof(*key);
if (key_len > 32 || rx_key_length > 32) {
wpa_printf(MSG_WARNING, "EAPOL: Too long key data length %d",
key_len ? key_len : rx_key_length);
@@ -810,6 +787,7 @@ static void eapol_sm_processKey(struct eapol_sm *sm)
sm->ctx->eapol_done_cb(sm->ctx->ctx);
}
}
+#endif /* CONFIG_FIPS */
}
@@ -1029,6 +1007,21 @@ void eapol_sm_configure(struct eapol_sm *sm, int heldPeriod, int authPeriod,
}
+/**
+ * eapol_sm_get_method_name - Get EAPOL method name
+ * @sm: Pointer to EAPOL state machine allocated with eapol_sm_init()
+ * Returns: Static string containing name of current eap method or NULL
+ */
+const char * eapol_sm_get_method_name(struct eapol_sm *sm)
+{
+ if (sm->SUPP_PAE_state != SUPP_PAE_AUTHENTICATED ||
+ sm->suppPortStatus != Authorized)
+ return NULL;
+
+ return eap_sm_get_method_name(sm->eap);
+}
+
+
#ifdef CONFIG_CTRL_IFACE
/**
* eapol_sm_get_status - Get EAPOL state machine status
@@ -1476,10 +1469,7 @@ void eapol_sm_notify_cached(struct eapol_sm *sm)
if (sm == NULL)
return;
wpa_printf(MSG_DEBUG, "EAPOL: PMKSA caching was used - skip EAPOL");
- sm->SUPP_PAE_state = SUPP_PAE_AUTHENTICATED;
- sm->suppPortStatus = Authorized;
- eapol_sm_set_port_authorized(sm);
- sm->portValid = TRUE;
+ sm->eapSuccess = TRUE;
eap_notify_success(sm->eap);
eapol_sm_step(sm);
}
@@ -1751,7 +1741,8 @@ static void eapol_sm_set_int(void *ctx, enum eapol_int_var variable,
switch (variable) {
case EAPOL_idleWhile:
sm->idleWhile = value;
- eapol_enable_timer_tick(sm);
+ if (sm->idleWhile > 0)
+ eapol_enable_timer_tick(sm);
break;
}
}
@@ -1798,7 +1789,7 @@ static void eapol_sm_notify_pending(void *ctx)
#if defined(CONFIG_CTRL_IFACE) || !defined(CONFIG_NO_STDOUT_DEBUG)
-static void eapol_sm_eap_param_needed(void *ctx, const char *field,
+static void eapol_sm_eap_param_needed(void *ctx, enum wpa_ctrl_req_type field,
const char *txt)
{
struct eapol_sm *sm = ctx;
@@ -1810,6 +1801,35 @@ static void eapol_sm_eap_param_needed(void *ctx, const char *field,
#define eapol_sm_eap_param_needed NULL
#endif /* CONFIG_CTRL_IFACE || !CONFIG_NO_STDOUT_DEBUG */
+static void eapol_sm_notify_cert(void *ctx, int depth, const char *subject,
+ const char *cert_hash,
+ const struct wpabuf *cert)
+{
+ struct eapol_sm *sm = ctx;
+ if (sm->ctx->cert_cb)
+ sm->ctx->cert_cb(sm->ctx->ctx, depth, subject,
+ cert_hash, cert);
+}
+
+
+static void eapol_sm_notify_status(void *ctx, const char *status,
+ const char *parameter)
+{
+ struct eapol_sm *sm = ctx;
+
+ if (sm->ctx->status_cb)
+ sm->ctx->status_cb(sm->ctx->ctx, status, parameter);
+}
+
+
+static void eapol_sm_set_anon_id(void *ctx, const u8 *id, size_t len)
+{
+ struct eapol_sm *sm = ctx;
+
+ if (sm->ctx->set_anon_id)
+ sm->ctx->set_anon_id(sm->ctx->ctx, id, len);
+}
+
static struct eapol_callbacks eapol_cb =
{
@@ -1822,7 +1842,10 @@ static struct eapol_callbacks eapol_cb =
eapol_sm_set_config_blob,
eapol_sm_get_config_blob,
eapol_sm_notify_pending,
- eapol_sm_eap_param_needed
+ eapol_sm_eap_param_needed,
+ eapol_sm_notify_cert,
+ eapol_sm_notify_status,
+ eapol_sm_set_anon_id
};
@@ -1858,6 +1881,7 @@ struct eapol_sm *eapol_sm_init(struct eapol_ctx *ctx)
conf.pkcs11_engine_path = ctx->pkcs11_engine_path;
conf.pkcs11_module_path = ctx->pkcs11_module_path;
conf.wps = ctx->wps;
+ conf.cert_in_cb = ctx->cert_in_cb;
sm->eap = eap_peer_sm_init(sm, &eapol_cb, sm->ctx->msg_ctx, &conf);
if (sm->eap == NULL) {
@@ -1896,3 +1920,19 @@ void eapol_sm_deinit(struct eapol_sm *sm)
os_free(sm->ctx);
os_free(sm);
}
+
+
+void eapol_sm_set_ext_pw_ctx(struct eapol_sm *sm,
+ struct ext_password_data *ext)
+{
+ if (sm && sm->eap)
+ eap_sm_set_ext_pw_ctx(sm->eap, ext);
+}
+
+
+int eapol_sm_failed(struct eapol_sm *sm)
+{
+ if (sm == NULL)
+ return 0;
+ return !sm->eapSuccess && sm->eapFail;
+}
diff --git a/contrib/wpa/src/eapol_supp/eapol_supp_sm.h b/contrib/wpa/src/eapol_supp/eapol_supp_sm.h
index 1d2a32b..c4b87da 100644
--- a/contrib/wpa/src/eapol_supp/eapol_supp_sm.h
+++ b/contrib/wpa/src/eapol_supp/eapol_supp_sm.h
@@ -1,15 +1,9 @@
/*
* EAPOL supplicant state machines
- * Copyright (c) 2004-2008, Jouni Malinen <j@w1.fi>
+ * Copyright (c) 2004-2012, Jouni Malinen <j@w1.fi>
*
- * This program is free software; you can redistribute it and/or modify
- * it under the terms of the GNU General Public License version 2 as
- * published by the Free Software Foundation.
- *
- * Alternatively, this software may be distributed under the terms of BSD
- * license.
- *
- * See README and COPYING for more details.
+ * This software may be distributed under the terms of the BSD license.
+ * See README for more details.
*/
#ifndef EAPOL_SUPP_SM_H
@@ -208,10 +202,10 @@ struct eapol_ctx {
/**
* eap_param_needed - Notify that EAP parameter is needed
* @ctx: Callback context (ctx)
- * @field: Field name (e.g., "IDENTITY")
+ * @field: Field indicator (e.g., WPA_CTRL_REQ_EAP_IDENTITY)
* @txt: User readable text describing the required parameter
*/
- void (*eap_param_needed)(void *ctx, const char *field,
+ void (*eap_param_needed)(void *ctx, enum wpa_ctrl_req_type field,
const char *txt);
/**
@@ -220,10 +214,44 @@ struct eapol_ctx {
* @authorized: Whether the supplicant port is now in authorized state
*/
void (*port_cb)(void *ctx, int authorized);
+
+ /**
+ * cert_cb - Notification of a peer certificate
+ * @ctx: Callback context (ctx)
+ * @depth: Depth in certificate chain (0 = server)
+ * @subject: Subject of the peer certificate
+ * @cert_hash: SHA-256 hash of the certificate
+ * @cert: Peer certificate
+ */
+ void (*cert_cb)(void *ctx, int depth, const char *subject,
+ const char *cert_hash, const struct wpabuf *cert);
+
+ /**
+ * cert_in_cb - Include server certificates in callback
+ */
+ int cert_in_cb;
+
+ /**
+ * status_cb - Notification of a change in EAP status
+ * @ctx: Callback context (ctx)
+ * @status: Step in the process of EAP authentication
+ * @parameter: Step-specific parameter, e.g., EAP method name
+ */
+ void (*status_cb)(void *ctx, const char *status,
+ const char *parameter);
+
+ /**
+ * set_anon_id - Set or add anonymous identity
+ * @ctx: eapol_ctx from eap_peer_sm_init() call
+ * @id: Anonymous identity (e.g., EAP-SIM pseudonym)
+ * @len: Length of anonymous identity in octets
+ */
+ void (*set_anon_id)(void *ctx, const u8 *id, size_t len);
};
struct eap_peer_config;
+struct ext_password_data;
#ifdef IEEE8021X_EAPOL
struct eapol_sm *eapol_sm_init(struct eapol_ctx *ctx);
@@ -255,6 +283,10 @@ void eapol_sm_notify_ctrl_response(struct eapol_sm *sm);
void eapol_sm_request_reauth(struct eapol_sm *sm);
void eapol_sm_notify_lower_layer_success(struct eapol_sm *sm, int in_eapol_sm);
void eapol_sm_invalidate_cached_session(struct eapol_sm *sm);
+const char * eapol_sm_get_method_name(struct eapol_sm *sm);
+void eapol_sm_set_ext_pw_ctx(struct eapol_sm *sm,
+ struct ext_password_data *ext);
+int eapol_sm_failed(struct eapol_sm *sm);
#else /* IEEE8021X_EAPOL */
static inline struct eapol_sm *eapol_sm_init(struct eapol_ctx *ctx)
{
@@ -342,6 +374,18 @@ static inline void eapol_sm_notify_lower_layer_success(struct eapol_sm *sm,
static inline void eapol_sm_invalidate_cached_session(struct eapol_sm *sm)
{
}
+static inline const char * eapol_sm_get_method_name(struct eapol_sm *sm)
+{
+ return NULL;
+}
+static inline void eapol_sm_set_ext_pw_ctx(struct eapol_sm *sm,
+ struct ext_password_data *ext)
+{
+}
+static inline int eapol_sm_failed(struct eapol_sm *sm)
+{
+ return 0;
+}
#endif /* IEEE8021X_EAPOL */
#endif /* EAPOL_SUPP_SM_H */
diff --git a/contrib/wpa/src/l2_packet/Makefile b/contrib/wpa/src/l2_packet/Makefile
deleted file mode 100644
index 9c41962..0000000
--- a/contrib/wpa/src/l2_packet/Makefile
+++ /dev/null
@@ -1,8 +0,0 @@
-all:
- @echo Nothing to be made.
-
-clean:
- rm -f *~ *.o *.d
-
-install:
- @echo Nothing to be made.
diff --git a/contrib/wpa/src/l2_packet/l2_packet.h b/contrib/wpa/src/l2_packet/l2_packet.h
index c7b5014..dd825b5 100644
--- a/contrib/wpa/src/l2_packet/l2_packet.h
+++ b/contrib/wpa/src/l2_packet/l2_packet.h
@@ -2,14 +2,8 @@
* WPA Supplicant - Layer2 packet interface definition
* Copyright (c) 2003-2005, Jouni Malinen <j@w1.fi>
*
- * This program is free software; you can redistribute it and/or modify
- * it under the terms of the GNU General Public License version 2 as
- * published by the Free Software Foundation.
- *
- * Alternatively, this software may be distributed under the terms of BSD
- * license.
- *
- * See README and COPYING for more details.
+ * This software may be distributed under the terms of the BSD license.
+ * See README for more details.
*
* This file defines an interface for layer 2 (link layer) packet sending and
* receiving. l2_packet_linux.c is one implementation for such a layer 2
diff --git a/contrib/wpa/src/l2_packet/l2_packet_freebsd.c b/contrib/wpa/src/l2_packet/l2_packet_freebsd.c
index 009e02c..2e9a04c 100644
--- a/contrib/wpa/src/l2_packet/l2_packet_freebsd.c
+++ b/contrib/wpa/src/l2_packet/l2_packet_freebsd.c
@@ -3,14 +3,8 @@
* Copyright (c) 2003-2005, Jouni Malinen <j@w1.fi>
* Copyright (c) 2005, Sam Leffler <sam@errno.com>
*
- * This program is free software; you can redistribute it and/or modify
- * it under the terms of the GNU General Public License version 2 as
- * published by the Free Software Foundation.
- *
- * Alternatively, this software may be distributed under the terms of BSD
- * license.
- *
- * See README and COPYING for more details.
+ * This software may be distributed under the terms of the BSD license.
+ * See README for more details.
*/
#include "includes.h"
@@ -20,7 +14,11 @@
#include <pcap.h>
#include <sys/ioctl.h>
+#ifdef __sun__
+#include <libdlpi.h>
+#else /* __sun__ */
#include <sys/sysctl.h>
+#endif /* __sun__ */
#include <net/if.h>
#include <net/if_dl.h>
@@ -139,6 +137,7 @@ static int l2_packet_init_libpcap(struct l2_packet_data *l2,
}
pcap_freecode(&pcap_fp);
+#ifndef __sun__
/*
* When libpcap uses BPF we must enable "immediate mode" to
* receive frames right away; otherwise the system may
@@ -153,6 +152,7 @@ static int l2_packet_init_libpcap(struct l2_packet_data *l2,
/* XXX should we fail? */
}
}
+#endif /* __sun__ */
eloop_register_read_sock(pcap_get_selectable_fd(l2->pcap),
l2_packet_receive, l2, l2->pcap);
@@ -163,6 +163,30 @@ static int l2_packet_init_libpcap(struct l2_packet_data *l2,
static int eth_get(const char *device, u8 ea[ETH_ALEN])
{
+#ifdef __sun__
+ dlpi_handle_t dh;
+ u32 physaddrlen = DLPI_PHYSADDR_MAX;
+ u8 physaddr[DLPI_PHYSADDR_MAX];
+ int retval;
+
+ retval = dlpi_open(device, &dh, 0);
+ if (retval != DLPI_SUCCESS) {
+ wpa_printf(MSG_ERROR, "dlpi_open error: %s",
+ dlpi_strerror(retval));
+ return -1;
+ }
+
+ retval = dlpi_get_physaddr(dh, DL_CURR_PHYS_ADDR, physaddr,
+ &physaddrlen);
+ if (retval != DLPI_SUCCESS) {
+ wpa_printf(MSG_ERROR, "dlpi_get_physaddr error: %s",
+ dlpi_strerror(retval));
+ dlpi_close(dh);
+ return -1;
+ }
+ os_memcpy(ea, physaddr, ETH_ALEN);
+ dlpi_close(dh);
+#else /* __sun__ */
struct if_msghdr *ifm;
struct sockaddr_dl *sdl;
u_char *p, *buf;
@@ -195,6 +219,7 @@ static int eth_get(const char *device, u8 ea[ETH_ALEN])
errno = ESRCH;
return -1;
}
+#endif /* __sun__ */
return 0;
}
diff --git a/contrib/wpa/src/l2_packet/l2_packet_ndis.c b/contrib/wpa/src/l2_packet/l2_packet_ndis.c
index 6ce29aa..23b8ddc 100644
--- a/contrib/wpa/src/l2_packet/l2_packet_ndis.c
+++ b/contrib/wpa/src/l2_packet/l2_packet_ndis.c
@@ -2,14 +2,8 @@
* WPA Supplicant - Layer2 packet handling with Microsoft NDISUIO
* Copyright (c) 2003-2006, Jouni Malinen <j@w1.fi>
*
- * This program is free software; you can redistribute it and/or modify
- * it under the terms of the GNU General Public License version 2 as
- * published by the Free Software Foundation.
- *
- * Alternatively, this software may be distributed under the terms of BSD
- * license.
- *
- * See README and COPYING for more details.
+ * This software may be distributed under the terms of the BSD license.
+ * See README for more details.
*
* This implementation requires Windows specific event loop implementation,
* i.e., eloop_win.c. In addition, the NDISUIO connection is shared with
diff --git a/contrib/wpa/src/l2_packet/l2_packet_none.c b/contrib/wpa/src/l2_packet/l2_packet_none.c
index 5e3f6e9..b01e830 100644
--- a/contrib/wpa/src/l2_packet/l2_packet_none.c
+++ b/contrib/wpa/src/l2_packet/l2_packet_none.c
@@ -2,14 +2,8 @@
* WPA Supplicant - Layer2 packet handling example with dummy functions
* Copyright (c) 2003-2005, Jouni Malinen <j@w1.fi>
*
- * This program is free software; you can redistribute it and/or modify
- * it under the terms of the GNU General Public License version 2 as
- * published by the Free Software Foundation.
- *
- * Alternatively, this software may be distributed under the terms of BSD
- * license.
- *
- * See README and COPYING for more details.
+ * This software may be distributed under the terms of the BSD license.
+ * See README for more details.
*
* This file can be used as a starting point for layer2 packet implementation.
*/
diff --git a/contrib/wpa/src/l2_packet/l2_packet_privsep.c b/contrib/wpa/src/l2_packet/l2_packet_privsep.c
new file mode 100644
index 0000000..6b117ca
--- /dev/null
+++ b/contrib/wpa/src/l2_packet/l2_packet_privsep.c
@@ -0,0 +1,261 @@
+/*
+ * WPA Supplicant - Layer2 packet handling with privilege separation
+ * Copyright (c) 2007, Jouni Malinen <j@w1.fi>
+ *
+ * This software may be distributed under the terms of the BSD license.
+ * See README for more details.
+ */
+
+#include "includes.h"
+#include <sys/un.h>
+
+#include "common.h"
+#include "eloop.h"
+#include "l2_packet.h"
+#include "common/privsep_commands.h"
+
+
+struct l2_packet_data {
+ int fd; /* UNIX domain socket for privsep access */
+ void (*rx_callback)(void *ctx, const u8 *src_addr,
+ const u8 *buf, size_t len);
+ void *rx_callback_ctx;
+ u8 own_addr[ETH_ALEN];
+ char *own_socket_path;
+ struct sockaddr_un priv_addr;
+};
+
+
+static int wpa_priv_cmd(struct l2_packet_data *l2, int cmd,
+ const void *data, size_t data_len)
+{
+ struct msghdr msg;
+ struct iovec io[2];
+
+ io[0].iov_base = &cmd;
+ io[0].iov_len = sizeof(cmd);
+ io[1].iov_base = (u8 *) data;
+ io[1].iov_len = data_len;
+
+ os_memset(&msg, 0, sizeof(msg));
+ msg.msg_iov = io;
+ msg.msg_iovlen = data ? 2 : 1;
+ msg.msg_name = &l2->priv_addr;
+ msg.msg_namelen = sizeof(l2->priv_addr);
+
+ if (sendmsg(l2->fd, &msg, 0) < 0) {
+ perror("L2: sendmsg(cmd)");
+ return -1;
+ }
+
+ return 0;
+}
+
+
+int l2_packet_get_own_addr(struct l2_packet_data *l2, u8 *addr)
+{
+ os_memcpy(addr, l2->own_addr, ETH_ALEN);
+ return 0;
+}
+
+
+int l2_packet_send(struct l2_packet_data *l2, const u8 *dst_addr, u16 proto,
+ const u8 *buf, size_t len)
+{
+ struct msghdr msg;
+ struct iovec io[4];
+ int cmd = PRIVSEP_CMD_L2_SEND;
+
+ io[0].iov_base = &cmd;
+ io[0].iov_len = sizeof(cmd);
+ io[1].iov_base = &dst_addr;
+ io[1].iov_len = ETH_ALEN;
+ io[2].iov_base = &proto;
+ io[2].iov_len = 2;
+ io[3].iov_base = (u8 *) buf;
+ io[3].iov_len = len;
+
+ os_memset(&msg, 0, sizeof(msg));
+ msg.msg_iov = io;
+ msg.msg_iovlen = 4;
+ msg.msg_name = &l2->priv_addr;
+ msg.msg_namelen = sizeof(l2->priv_addr);
+
+ if (sendmsg(l2->fd, &msg, 0) < 0) {
+ perror("L2: sendmsg(packet_send)");
+ return -1;
+ }
+
+ return 0;
+}
+
+
+static void l2_packet_receive(int sock, void *eloop_ctx, void *sock_ctx)
+{
+ struct l2_packet_data *l2 = eloop_ctx;
+ u8 buf[2300];
+ int res;
+ struct sockaddr_un from;
+ socklen_t fromlen = sizeof(from);
+
+ os_memset(&from, 0, sizeof(from));
+ res = recvfrom(sock, buf, sizeof(buf), 0, (struct sockaddr *) &from,
+ &fromlen);
+ if (res < 0) {
+ perror("l2_packet_receive - recvfrom");
+ return;
+ }
+ if (res < ETH_ALEN) {
+ wpa_printf(MSG_DEBUG, "L2: Too show packet received");
+ return;
+ }
+
+ if (from.sun_family != AF_UNIX ||
+ os_strncmp(from.sun_path, l2->priv_addr.sun_path,
+ sizeof(from.sun_path)) != 0) {
+ wpa_printf(MSG_DEBUG, "L2: Received message from unexpected "
+ "source");
+ return;
+ }
+
+ l2->rx_callback(l2->rx_callback_ctx, buf, buf + ETH_ALEN,
+ res - ETH_ALEN);
+}
+
+
+struct l2_packet_data * l2_packet_init(
+ const char *ifname, const u8 *own_addr, unsigned short protocol,
+ void (*rx_callback)(void *ctx, const u8 *src_addr,
+ const u8 *buf, size_t len),
+ void *rx_callback_ctx, int l2_hdr)
+{
+ struct l2_packet_data *l2;
+ char *own_dir = "/tmp";
+ char *priv_dir = "/var/run/wpa_priv";
+ size_t len;
+ static unsigned int counter = 0;
+ struct sockaddr_un addr;
+ fd_set rfds;
+ struct timeval tv;
+ int res;
+ u8 reply[ETH_ALEN + 1];
+ int reg_cmd[2];
+
+ l2 = os_zalloc(sizeof(struct l2_packet_data));
+ if (l2 == NULL)
+ return NULL;
+ l2->rx_callback = rx_callback;
+ l2->rx_callback_ctx = rx_callback_ctx;
+
+ len = os_strlen(own_dir) + 50;
+ l2->own_socket_path = os_malloc(len);
+ if (l2->own_socket_path == NULL) {
+ os_free(l2);
+ return NULL;
+ }
+ os_snprintf(l2->own_socket_path, len, "%s/wpa_privsep-l2-%d-%d",
+ own_dir, getpid(), counter++);
+
+ l2->priv_addr.sun_family = AF_UNIX;
+ os_snprintf(l2->priv_addr.sun_path, sizeof(l2->priv_addr.sun_path),
+ "%s/%s", priv_dir, ifname);
+
+ l2->fd = socket(PF_UNIX, SOCK_DGRAM, 0);
+ if (l2->fd < 0) {
+ perror("socket(PF_UNIX)");
+ os_free(l2->own_socket_path);
+ l2->own_socket_path = NULL;
+ os_free(l2);
+ return NULL;
+ }
+
+ os_memset(&addr, 0, sizeof(addr));
+ addr.sun_family = AF_UNIX;
+ os_strlcpy(addr.sun_path, l2->own_socket_path, sizeof(addr.sun_path));
+ if (bind(l2->fd, (struct sockaddr *) &addr, sizeof(addr)) < 0) {
+ perror("l2-pkt-privsep: bind(PF_UNIX)");
+ goto fail;
+ }
+
+ reg_cmd[0] = protocol;
+ reg_cmd[1] = l2_hdr;
+ if (wpa_priv_cmd(l2, PRIVSEP_CMD_L2_REGISTER, reg_cmd, sizeof(reg_cmd))
+ < 0) {
+ wpa_printf(MSG_ERROR, "L2: Failed to register with wpa_priv");
+ goto fail;
+ }
+
+ FD_ZERO(&rfds);
+ FD_SET(l2->fd, &rfds);
+ tv.tv_sec = 5;
+ tv.tv_usec = 0;
+ res = select(l2->fd + 1, &rfds, NULL, NULL, &tv);
+ if (res < 0 && errno != EINTR) {
+ perror("select");
+ goto fail;
+ }
+
+ if (FD_ISSET(l2->fd, &rfds)) {
+ res = recv(l2->fd, reply, sizeof(reply), 0);
+ if (res < 0) {
+ perror("recv");
+ goto fail;
+ }
+ } else {
+ wpa_printf(MSG_DEBUG, "L2: Timeout while waiting for "
+ "registration reply");
+ goto fail;
+ }
+
+ if (res != ETH_ALEN) {
+ wpa_printf(MSG_DEBUG, "L2: Unexpected registration reply "
+ "(len=%d)", res);
+ }
+ os_memcpy(l2->own_addr, reply, ETH_ALEN);
+
+ eloop_register_read_sock(l2->fd, l2_packet_receive, l2, NULL);
+
+ return l2;
+
+fail:
+ close(l2->fd);
+ l2->fd = -1;
+ unlink(l2->own_socket_path);
+ os_free(l2->own_socket_path);
+ l2->own_socket_path = NULL;
+ os_free(l2);
+ return NULL;
+}
+
+
+void l2_packet_deinit(struct l2_packet_data *l2)
+{
+ if (l2 == NULL)
+ return;
+
+ if (l2->fd >= 0) {
+ wpa_priv_cmd(l2, PRIVSEP_CMD_L2_UNREGISTER, NULL, 0);
+ eloop_unregister_read_sock(l2->fd);
+ close(l2->fd);
+ }
+
+ if (l2->own_socket_path) {
+ unlink(l2->own_socket_path);
+ os_free(l2->own_socket_path);
+ }
+
+ os_free(l2);
+}
+
+
+int l2_packet_get_ip_addr(struct l2_packet_data *l2, char *buf, size_t len)
+{
+ /* TODO */
+ return -1;
+}
+
+
+void l2_packet_notify_auth_start(struct l2_packet_data *l2)
+{
+ wpa_priv_cmd(l2, PRIVSEP_CMD_L2_NOTIFY_AUTH_START, NULL, 0);
+}
diff --git a/contrib/wpa/src/lib.rules b/contrib/wpa/src/lib.rules
deleted file mode 100644
index b260d25..0000000
--- a/contrib/wpa/src/lib.rules
+++ /dev/null
@@ -1,21 +0,0 @@
-ifndef CC
-CC=gcc
-endif
-
-ifndef CFLAGS
-CFLAGS = -MMD -O2 -Wall -g
-endif
-
-CFLAGS += -I.. -I../utils
-
-
-Q=@
-E=echo
-ifeq ($(V), 1)
-Q=
-E=true
-endif
-
-%.o: %.c
- $(Q)$(CC) -c -o $@ $(CFLAGS) $<
- @$(E) " CC " $<
diff --git a/contrib/wpa/src/p2p/p2p.c b/contrib/wpa/src/p2p/p2p.c
new file mode 100644
index 0000000..b994a44
--- /dev/null
+++ b/contrib/wpa/src/p2p/p2p.c
@@ -0,0 +1,4320 @@
+/*
+ * Wi-Fi Direct - P2P module
+ * Copyright (c) 2009-2010, Atheros Communications
+ *
+ * This software may be distributed under the terms of the BSD license.
+ * See README for more details.
+ */
+
+#include "includes.h"
+
+#include "common.h"
+#include "eloop.h"
+#include "common/ieee802_11_defs.h"
+#include "common/ieee802_11_common.h"
+#include "common/wpa_ctrl.h"
+#include "wps/wps_i.h"
+#include "p2p_i.h"
+#include "p2p.h"
+
+
+static void p2p_state_timeout(void *eloop_ctx, void *timeout_ctx);
+static void p2p_device_free(struct p2p_data *p2p, struct p2p_device *dev);
+static void p2p_process_presence_req(struct p2p_data *p2p, const u8 *da,
+ const u8 *sa, const u8 *data, size_t len,
+ int rx_freq);
+static void p2p_process_presence_resp(struct p2p_data *p2p, const u8 *da,
+ const u8 *sa, const u8 *data,
+ size_t len);
+static void p2p_ext_listen_timeout(void *eloop_ctx, void *timeout_ctx);
+static void p2p_scan_timeout(void *eloop_ctx, void *timeout_ctx);
+
+
+/*
+ * p2p_scan recovery timeout
+ *
+ * Many drivers are using 30 second timeout on scan results. Allow a bit larger
+ * timeout for this to avoid hitting P2P timeout unnecessarily.
+ */
+#define P2P_SCAN_TIMEOUT 35
+
+/**
+ * P2P_PEER_EXPIRATION_AGE - Number of seconds after which inactive peer
+ * entries will be removed
+ */
+#define P2P_PEER_EXPIRATION_AGE 300
+
+#define P2P_PEER_EXPIRATION_INTERVAL (P2P_PEER_EXPIRATION_AGE / 2)
+
+static void p2p_expire_peers(struct p2p_data *p2p)
+{
+ struct p2p_device *dev, *n;
+ struct os_time now;
+ size_t i;
+
+ os_get_time(&now);
+ dl_list_for_each_safe(dev, n, &p2p->devices, struct p2p_device, list) {
+ if (dev->last_seen.sec + P2P_PEER_EXPIRATION_AGE >= now.sec)
+ continue;
+
+ if (p2p->cfg->go_connected &&
+ p2p->cfg->go_connected(p2p->cfg->cb_ctx,
+ dev->info.p2p_device_addr)) {
+ /*
+ * We are connected as a client to a group in which the
+ * peer is the GO, so do not expire the peer entry.
+ */
+ os_get_time(&dev->last_seen);
+ continue;
+ }
+
+ for (i = 0; i < p2p->num_groups; i++) {
+ if (p2p_group_is_client_connected(
+ p2p->groups[i], dev->info.p2p_device_addr))
+ break;
+ }
+ if (i < p2p->num_groups) {
+ /*
+ * The peer is connected as a client in a group where
+ * we are the GO, so do not expire the peer entry.
+ */
+ os_get_time(&dev->last_seen);
+ continue;
+ }
+
+ wpa_msg(p2p->cfg->msg_ctx, MSG_DEBUG, "P2P: Expiring old peer "
+ "entry " MACSTR, MAC2STR(dev->info.p2p_device_addr));
+ dl_list_del(&dev->list);
+ p2p_device_free(p2p, dev);
+ }
+}
+
+
+static void p2p_expiration_timeout(void *eloop_ctx, void *timeout_ctx)
+{
+ struct p2p_data *p2p = eloop_ctx;
+ p2p_expire_peers(p2p);
+ eloop_register_timeout(P2P_PEER_EXPIRATION_INTERVAL, 0,
+ p2p_expiration_timeout, p2p, NULL);
+}
+
+
+static const char * p2p_state_txt(int state)
+{
+ switch (state) {
+ case P2P_IDLE:
+ return "IDLE";
+ case P2P_SEARCH:
+ return "SEARCH";
+ case P2P_CONNECT:
+ return "CONNECT";
+ case P2P_CONNECT_LISTEN:
+ return "CONNECT_LISTEN";
+ case P2P_GO_NEG:
+ return "GO_NEG";
+ case P2P_LISTEN_ONLY:
+ return "LISTEN_ONLY";
+ case P2P_WAIT_PEER_CONNECT:
+ return "WAIT_PEER_CONNECT";
+ case P2P_WAIT_PEER_IDLE:
+ return "WAIT_PEER_IDLE";
+ case P2P_SD_DURING_FIND:
+ return "SD_DURING_FIND";
+ case P2P_PROVISIONING:
+ return "PROVISIONING";
+ case P2P_PD_DURING_FIND:
+ return "PD_DURING_FIND";
+ case P2P_INVITE:
+ return "INVITE";
+ case P2P_INVITE_LISTEN:
+ return "INVITE_LISTEN";
+ case P2P_SEARCH_WHEN_READY:
+ return "SEARCH_WHEN_READY";
+ case P2P_CONTINUE_SEARCH_WHEN_READY:
+ return "CONTINUE_SEARCH_WHEN_READY";
+ default:
+ return "?";
+ }
+}
+
+
+u16 p2p_get_provisioning_info(struct p2p_data *p2p, const u8 *addr)
+{
+ struct p2p_device *dev = NULL;
+
+ if (!addr || !p2p)
+ return 0;
+
+ dev = p2p_get_device(p2p, addr);
+ if (dev)
+ return dev->wps_prov_info;
+ else
+ return 0;
+}
+
+
+void p2p_clear_provisioning_info(struct p2p_data *p2p, const u8 *addr)
+{
+ struct p2p_device *dev = NULL;
+
+ if (!addr || !p2p)
+ return;
+
+ dev = p2p_get_device(p2p, addr);
+ if (dev)
+ dev->wps_prov_info = 0;
+}
+
+
+void p2p_set_state(struct p2p_data *p2p, int new_state)
+{
+ wpa_msg(p2p->cfg->msg_ctx, MSG_DEBUG, "P2P: State %s -> %s",
+ p2p_state_txt(p2p->state), p2p_state_txt(new_state));
+ p2p->state = new_state;
+}
+
+
+void p2p_set_timeout(struct p2p_data *p2p, unsigned int sec, unsigned int usec)
+{
+ wpa_msg(p2p->cfg->msg_ctx, MSG_DEBUG,
+ "P2P: Set timeout (state=%s): %u.%06u sec",
+ p2p_state_txt(p2p->state), sec, usec);
+ eloop_cancel_timeout(p2p_state_timeout, p2p, NULL);
+ eloop_register_timeout(sec, usec, p2p_state_timeout, p2p, NULL);
+}
+
+
+void p2p_clear_timeout(struct p2p_data *p2p)
+{
+ wpa_msg(p2p->cfg->msg_ctx, MSG_DEBUG, "P2P: Clear timeout (state=%s)",
+ p2p_state_txt(p2p->state));
+ eloop_cancel_timeout(p2p_state_timeout, p2p, NULL);
+}
+
+
+void p2p_go_neg_failed(struct p2p_data *p2p, struct p2p_device *peer,
+ int status)
+{
+ struct p2p_go_neg_results res;
+ p2p_clear_timeout(p2p);
+ p2p_set_state(p2p, P2P_IDLE);
+ if (p2p->go_neg_peer)
+ p2p->go_neg_peer->wps_method = WPS_NOT_READY;
+ p2p->go_neg_peer = NULL;
+
+ os_memset(&res, 0, sizeof(res));
+ res.status = status;
+ if (peer) {
+ os_memcpy(res.peer_device_addr, peer->info.p2p_device_addr,
+ ETH_ALEN);
+ os_memcpy(res.peer_interface_addr, peer->intended_addr,
+ ETH_ALEN);
+ }
+ p2p->cfg->go_neg_completed(p2p->cfg->cb_ctx, &res);
+}
+
+
+static void p2p_listen_in_find(struct p2p_data *p2p, int dev_disc)
+{
+ unsigned int r, tu;
+ int freq;
+ struct wpabuf *ies;
+
+ wpa_msg(p2p->cfg->msg_ctx, MSG_DEBUG,
+ "P2P: Starting short listen state (state=%s)",
+ p2p_state_txt(p2p->state));
+
+ freq = p2p_channel_to_freq(p2p->cfg->country, p2p->cfg->reg_class,
+ p2p->cfg->channel);
+ if (freq < 0) {
+ wpa_msg(p2p->cfg->msg_ctx, MSG_DEBUG,
+ "P2P: Unknown regulatory class/channel");
+ return;
+ }
+
+ os_get_random((u8 *) &r, sizeof(r));
+ tu = (r % ((p2p->max_disc_int - p2p->min_disc_int) + 1) +
+ p2p->min_disc_int) * 100;
+ if (p2p->max_disc_tu >= 0 && tu > (unsigned int) p2p->max_disc_tu)
+ tu = p2p->max_disc_tu;
+ if (!dev_disc && tu < 100)
+ tu = 100; /* Need to wait in non-device discovery use cases */
+ if (p2p->cfg->max_listen && 1024 * tu / 1000 > p2p->cfg->max_listen)
+ tu = p2p->cfg->max_listen * 1000 / 1024;
+
+ if (tu == 0) {
+ wpa_msg(p2p->cfg->msg_ctx, MSG_DEBUG, "P2P: Skip listen state "
+ "since duration was 0 TU");
+ p2p_set_timeout(p2p, 0, 0);
+ return;
+ }
+
+ p2p->pending_listen_freq = freq;
+ p2p->pending_listen_sec = 0;
+ p2p->pending_listen_usec = 1024 * tu;
+
+ ies = p2p_build_probe_resp_ies(p2p);
+ if (ies == NULL)
+ return;
+
+ if (p2p->cfg->start_listen(p2p->cfg->cb_ctx, freq, 1024 * tu / 1000,
+ ies) < 0) {
+ wpa_msg(p2p->cfg->msg_ctx, MSG_DEBUG,
+ "P2P: Failed to start listen mode");
+ p2p->pending_listen_freq = 0;
+ }
+ wpabuf_free(ies);
+}
+
+
+int p2p_listen(struct p2p_data *p2p, unsigned int timeout)
+{
+ int freq;
+ struct wpabuf *ies;
+
+ wpa_msg(p2p->cfg->msg_ctx, MSG_DEBUG,
+ "P2P: Going to listen(only) state");
+
+ freq = p2p_channel_to_freq(p2p->cfg->country, p2p->cfg->reg_class,
+ p2p->cfg->channel);
+ if (freq < 0) {
+ wpa_msg(p2p->cfg->msg_ctx, MSG_DEBUG,
+ "P2P: Unknown regulatory class/channel");
+ return -1;
+ }
+
+ p2p->pending_listen_freq = freq;
+ p2p->pending_listen_sec = timeout / 1000;
+ p2p->pending_listen_usec = (timeout % 1000) * 1000;
+
+ if (p2p->p2p_scan_running) {
+ if (p2p->start_after_scan == P2P_AFTER_SCAN_CONNECT) {
+ wpa_msg(p2p->cfg->msg_ctx, MSG_DEBUG,
+ "P2P: p2p_scan running - connect is already "
+ "pending - skip listen");
+ return 0;
+ }
+ wpa_msg(p2p->cfg->msg_ctx, MSG_DEBUG,
+ "P2P: p2p_scan running - delay start of listen state");
+ p2p->start_after_scan = P2P_AFTER_SCAN_LISTEN;
+ return 0;
+ }
+
+ ies = p2p_build_probe_resp_ies(p2p);
+ if (ies == NULL)
+ return -1;
+
+ if (p2p->cfg->start_listen(p2p->cfg->cb_ctx, freq, timeout, ies) < 0) {
+ wpa_msg(p2p->cfg->msg_ctx, MSG_DEBUG,
+ "P2P: Failed to start listen mode");
+ p2p->pending_listen_freq = 0;
+ wpabuf_free(ies);
+ return -1;
+ }
+ wpabuf_free(ies);
+
+ p2p_set_state(p2p, P2P_LISTEN_ONLY);
+
+ return 0;
+}
+
+
+static void p2p_device_clear_reported(struct p2p_data *p2p)
+{
+ struct p2p_device *dev;
+ dl_list_for_each(dev, &p2p->devices, struct p2p_device, list)
+ dev->flags &= ~P2P_DEV_REPORTED;
+}
+
+
+/**
+ * p2p_get_device - Fetch a peer entry
+ * @p2p: P2P module context from p2p_init()
+ * @addr: P2P Device Address of the peer
+ * Returns: Pointer to the device entry or %NULL if not found
+ */
+struct p2p_device * p2p_get_device(struct p2p_data *p2p, const u8 *addr)
+{
+ struct p2p_device *dev;
+ dl_list_for_each(dev, &p2p->devices, struct p2p_device, list) {
+ if (os_memcmp(dev->info.p2p_device_addr, addr, ETH_ALEN) == 0)
+ return dev;
+ }
+ return NULL;
+}
+
+
+/**
+ * p2p_get_device_interface - Fetch a peer entry based on P2P Interface Address
+ * @p2p: P2P module context from p2p_init()
+ * @addr: P2P Interface Address of the peer
+ * Returns: Pointer to the device entry or %NULL if not found
+ */
+struct p2p_device * p2p_get_device_interface(struct p2p_data *p2p,
+ const u8 *addr)
+{
+ struct p2p_device *dev;
+ dl_list_for_each(dev, &p2p->devices, struct p2p_device, list) {
+ if (os_memcmp(dev->interface_addr, addr, ETH_ALEN) == 0)
+ return dev;
+ }
+ return NULL;
+}
+
+
+/**
+ * p2p_create_device - Create a peer entry
+ * @p2p: P2P module context from p2p_init()
+ * @addr: P2P Device Address of the peer
+ * Returns: Pointer to the device entry or %NULL on failure
+ *
+ * If there is already an entry for the peer, it will be returned instead of
+ * creating a new one.
+ */
+static struct p2p_device * p2p_create_device(struct p2p_data *p2p,
+ const u8 *addr)
+{
+ struct p2p_device *dev, *oldest = NULL;
+ size_t count = 0;
+
+ dev = p2p_get_device(p2p, addr);
+ if (dev)
+ return dev;
+
+ dl_list_for_each(dev, &p2p->devices, struct p2p_device, list) {
+ count++;
+ if (oldest == NULL ||
+ os_time_before(&dev->last_seen, &oldest->last_seen))
+ oldest = dev;
+ }
+ if (count + 1 > p2p->cfg->max_peers && oldest) {
+ wpa_msg(p2p->cfg->msg_ctx, MSG_DEBUG,
+ "P2P: Remove oldest peer entry to make room for a new "
+ "peer");
+ dl_list_del(&oldest->list);
+ p2p_device_free(p2p, oldest);
+ }
+
+ dev = os_zalloc(sizeof(*dev));
+ if (dev == NULL)
+ return NULL;
+ dl_list_add(&p2p->devices, &dev->list);
+ os_memcpy(dev->info.p2p_device_addr, addr, ETH_ALEN);
+
+ return dev;
+}
+
+
+static void p2p_copy_client_info(struct p2p_device *dev,
+ struct p2p_client_info *cli)
+{
+ os_memcpy(dev->info.device_name, cli->dev_name, cli->dev_name_len);
+ dev->info.device_name[cli->dev_name_len] = '\0';
+ dev->info.dev_capab = cli->dev_capab;
+ dev->info.config_methods = cli->config_methods;
+ os_memcpy(dev->info.pri_dev_type, cli->pri_dev_type, 8);
+ dev->info.wps_sec_dev_type_list_len = 8 * cli->num_sec_dev_types;
+ os_memcpy(dev->info.wps_sec_dev_type_list, cli->sec_dev_types,
+ dev->info.wps_sec_dev_type_list_len);
+}
+
+
+static int p2p_add_group_clients(struct p2p_data *p2p, const u8 *go_dev_addr,
+ const u8 *go_interface_addr, int freq,
+ const u8 *gi, size_t gi_len)
+{
+ struct p2p_group_info info;
+ size_t c;
+ struct p2p_device *dev;
+
+ if (gi == NULL)
+ return 0;
+
+ if (p2p_group_info_parse(gi, gi_len, &info) < 0)
+ return -1;
+
+ /*
+ * Clear old data for this group; if the devices are still in the
+ * group, the information will be restored in the loop following this.
+ */
+ dl_list_for_each(dev, &p2p->devices, struct p2p_device, list) {
+ if (os_memcmp(dev->member_in_go_iface, go_interface_addr,
+ ETH_ALEN) == 0) {
+ os_memset(dev->member_in_go_iface, 0, ETH_ALEN);
+ os_memset(dev->member_in_go_dev, 0, ETH_ALEN);
+ }
+ }
+
+ for (c = 0; c < info.num_clients; c++) {
+ struct p2p_client_info *cli = &info.client[c];
+ if (os_memcmp(cli->p2p_device_addr, p2p->cfg->dev_addr,
+ ETH_ALEN) == 0)
+ continue; /* ignore our own entry */
+ dev = p2p_get_device(p2p, cli->p2p_device_addr);
+ if (dev) {
+ if (dev->flags & (P2P_DEV_GROUP_CLIENT_ONLY |
+ P2P_DEV_PROBE_REQ_ONLY)) {
+ /*
+ * Update information since we have not
+ * received this directly from the client.
+ */
+ p2p_copy_client_info(dev, cli);
+ } else {
+ /*
+ * Need to update P2P Client Discoverability
+ * flag since it is valid only in P2P Group
+ * Info attribute.
+ */
+ dev->info.dev_capab &=
+ ~P2P_DEV_CAPAB_CLIENT_DISCOVERABILITY;
+ dev->info.dev_capab |=
+ cli->dev_capab &
+ P2P_DEV_CAPAB_CLIENT_DISCOVERABILITY;
+ }
+ if (dev->flags & P2P_DEV_PROBE_REQ_ONLY) {
+ dev->flags &= ~P2P_DEV_PROBE_REQ_ONLY;
+ }
+ } else {
+ dev = p2p_create_device(p2p, cli->p2p_device_addr);
+ if (dev == NULL)
+ continue;
+ dev->flags |= P2P_DEV_GROUP_CLIENT_ONLY;
+ p2p_copy_client_info(dev, cli);
+ dev->oper_freq = freq;
+ p2p->cfg->dev_found(p2p->cfg->cb_ctx,
+ dev->info.p2p_device_addr,
+ &dev->info, 1);
+ dev->flags |= P2P_DEV_REPORTED | P2P_DEV_REPORTED_ONCE;
+ }
+
+ os_memcpy(dev->interface_addr, cli->p2p_interface_addr,
+ ETH_ALEN);
+ os_get_time(&dev->last_seen);
+ os_memcpy(dev->member_in_go_dev, go_dev_addr, ETH_ALEN);
+ os_memcpy(dev->member_in_go_iface, go_interface_addr,
+ ETH_ALEN);
+ }
+
+ return 0;
+}
+
+
+static void p2p_copy_wps_info(struct p2p_device *dev, int probe_req,
+ const struct p2p_message *msg)
+{
+ os_memcpy(dev->info.device_name, msg->device_name,
+ sizeof(dev->info.device_name));
+
+ if (msg->manufacturer &&
+ msg->manufacturer_len < sizeof(dev->info.manufacturer)) {
+ os_memset(dev->info.manufacturer, 0,
+ sizeof(dev->info.manufacturer));
+ os_memcpy(dev->info.manufacturer, msg->manufacturer,
+ msg->manufacturer_len);
+ }
+
+ if (msg->model_name &&
+ msg->model_name_len < sizeof(dev->info.model_name)) {
+ os_memset(dev->info.model_name, 0,
+ sizeof(dev->info.model_name));
+ os_memcpy(dev->info.model_name, msg->model_name,
+ msg->model_name_len);
+ }
+
+ if (msg->model_number &&
+ msg->model_number_len < sizeof(dev->info.model_number)) {
+ os_memset(dev->info.model_number, 0,
+ sizeof(dev->info.model_number));
+ os_memcpy(dev->info.model_number, msg->model_number,
+ msg->model_number_len);
+ }
+
+ if (msg->serial_number &&
+ msg->serial_number_len < sizeof(dev->info.serial_number)) {
+ os_memset(dev->info.serial_number, 0,
+ sizeof(dev->info.serial_number));
+ os_memcpy(dev->info.serial_number, msg->serial_number,
+ msg->serial_number_len);
+ }
+
+ if (msg->pri_dev_type)
+ os_memcpy(dev->info.pri_dev_type, msg->pri_dev_type,
+ sizeof(dev->info.pri_dev_type));
+ else if (msg->wps_pri_dev_type)
+ os_memcpy(dev->info.pri_dev_type, msg->wps_pri_dev_type,
+ sizeof(dev->info.pri_dev_type));
+
+ if (msg->wps_sec_dev_type_list) {
+ os_memcpy(dev->info.wps_sec_dev_type_list,
+ msg->wps_sec_dev_type_list,
+ msg->wps_sec_dev_type_list_len);
+ dev->info.wps_sec_dev_type_list_len =
+ msg->wps_sec_dev_type_list_len;
+ }
+
+ if (msg->capability) {
+ /*
+ * P2P Client Discoverability bit is reserved in all frames
+ * that use this function, so do not change its value here.
+ */
+ dev->info.dev_capab &= P2P_DEV_CAPAB_CLIENT_DISCOVERABILITY;
+ dev->info.dev_capab |= msg->capability[0] &
+ ~P2P_DEV_CAPAB_CLIENT_DISCOVERABILITY;
+ dev->info.group_capab = msg->capability[1];
+ }
+
+ if (msg->ext_listen_timing) {
+ dev->ext_listen_period = WPA_GET_LE16(msg->ext_listen_timing);
+ dev->ext_listen_interval =
+ WPA_GET_LE16(msg->ext_listen_timing + 2);
+ }
+
+ if (!probe_req) {
+ dev->info.config_methods = msg->config_methods ?
+ msg->config_methods : msg->wps_config_methods;
+ }
+}
+
+
+/**
+ * p2p_add_device - Add peer entries based on scan results or P2P frames
+ * @p2p: P2P module context from p2p_init()
+ * @addr: Source address of Beacon or Probe Response frame (may be either
+ * P2P Device Address or P2P Interface Address)
+ * @level: Signal level (signal strength of the received frame from the peer)
+ * @freq: Frequency on which the Beacon or Probe Response frame was received
+ * @age_ms: Age of the information in milliseconds
+ * @ies: IEs from the Beacon or Probe Response frame
+ * @ies_len: Length of ies buffer in octets
+ * @scan_res: Whether this was based on scan results
+ * Returns: 0 on success, -1 on failure
+ *
+ * If the scan result is for a GO, the clients in the group will also be added
+ * to the peer table. This function can also be used with some other frames
+ * like Provision Discovery Request that contains P2P Capability and P2P Device
+ * Info attributes.
+ */
+int p2p_add_device(struct p2p_data *p2p, const u8 *addr, int freq,
+ unsigned int age_ms, int level, const u8 *ies,
+ size_t ies_len, int scan_res)
+{
+ struct p2p_device *dev;
+ struct p2p_message msg;
+ const u8 *p2p_dev_addr;
+ int i;
+ struct os_time time_now, time_tmp_age, entry_ts;
+
+ os_memset(&msg, 0, sizeof(msg));
+ if (p2p_parse_ies(ies, ies_len, &msg)) {
+ wpa_msg(p2p->cfg->msg_ctx, MSG_DEBUG,
+ "P2P: Failed to parse P2P IE for a device entry");
+ p2p_parse_free(&msg);
+ return -1;
+ }
+
+ if (msg.p2p_device_addr)
+ p2p_dev_addr = msg.p2p_device_addr;
+ else if (msg.device_id)
+ p2p_dev_addr = msg.device_id;
+ else {
+ wpa_msg(p2p->cfg->msg_ctx, MSG_DEBUG,
+ "P2P: Ignore scan data without P2P Device Info or "
+ "P2P Device Id");
+ p2p_parse_free(&msg);
+ return -1;
+ }
+
+ if (!is_zero_ether_addr(p2p->peer_filter) &&
+ os_memcmp(p2p_dev_addr, p2p->peer_filter, ETH_ALEN) != 0) {
+ wpa_msg(p2p->cfg->msg_ctx, MSG_DEBUG, "P2P: Do not add peer "
+ "filter for " MACSTR " due to peer filter",
+ MAC2STR(p2p_dev_addr));
+ p2p_parse_free(&msg);
+ return 0;
+ }
+
+ dev = p2p_create_device(p2p, p2p_dev_addr);
+ if (dev == NULL) {
+ p2p_parse_free(&msg);
+ return -1;
+ }
+
+ os_get_time(&time_now);
+ time_tmp_age.sec = age_ms / 1000;
+ time_tmp_age.usec = (age_ms % 1000) * 1000;
+ os_time_sub(&time_now, &time_tmp_age, &entry_ts);
+
+ /*
+ * Update the device entry only if the new peer
+ * entry is newer than the one previously stored.
+ */
+ if (dev->last_seen.usec > 0 &&
+ os_time_before(&entry_ts, &dev->last_seen)) {
+ p2p_parse_free(&msg);
+ return -1;
+ }
+
+ os_memcpy(&dev->last_seen, &entry_ts, sizeof(struct os_time));
+
+ dev->flags &= ~(P2P_DEV_PROBE_REQ_ONLY | P2P_DEV_GROUP_CLIENT_ONLY);
+
+ if (os_memcmp(addr, p2p_dev_addr, ETH_ALEN) != 0)
+ os_memcpy(dev->interface_addr, addr, ETH_ALEN);
+ if (msg.ssid &&
+ (msg.ssid[1] != P2P_WILDCARD_SSID_LEN ||
+ os_memcmp(msg.ssid + 2, P2P_WILDCARD_SSID, P2P_WILDCARD_SSID_LEN)
+ != 0)) {
+ os_memcpy(dev->oper_ssid, msg.ssid + 2, msg.ssid[1]);
+ dev->oper_ssid_len = msg.ssid[1];
+ }
+
+ if (freq >= 2412 && freq <= 2484 && msg.ds_params &&
+ *msg.ds_params >= 1 && *msg.ds_params <= 14) {
+ int ds_freq;
+ if (*msg.ds_params == 14)
+ ds_freq = 2484;
+ else
+ ds_freq = 2407 + *msg.ds_params * 5;
+ if (freq != ds_freq) {
+ wpa_msg(p2p->cfg->msg_ctx, MSG_DEBUG,
+ "P2P: Update Listen frequency based on DS "
+ "Parameter Set IE: %d -> %d MHz",
+ freq, ds_freq);
+ freq = ds_freq;
+ }
+ }
+
+ if (dev->listen_freq && dev->listen_freq != freq && scan_res) {
+ wpa_msg(p2p->cfg->msg_ctx, MSG_DEBUG,
+ "P2P: Update Listen frequency based on scan "
+ "results (" MACSTR " %d -> %d MHz (DS param %d)",
+ MAC2STR(dev->info.p2p_device_addr), dev->listen_freq,
+ freq, msg.ds_params ? *msg.ds_params : -1);
+ }
+ if (scan_res) {
+ dev->listen_freq = freq;
+ if (msg.group_info)
+ dev->oper_freq = freq;
+ }
+ dev->info.level = level;
+
+ p2p_copy_wps_info(dev, 0, &msg);
+
+ for (i = 0; i < P2P_MAX_WPS_VENDOR_EXT; i++) {
+ wpabuf_free(dev->info.wps_vendor_ext[i]);
+ dev->info.wps_vendor_ext[i] = NULL;
+ }
+
+ for (i = 0; i < P2P_MAX_WPS_VENDOR_EXT; i++) {
+ if (msg.wps_vendor_ext[i] == NULL)
+ break;
+ dev->info.wps_vendor_ext[i] = wpabuf_alloc_copy(
+ msg.wps_vendor_ext[i], msg.wps_vendor_ext_len[i]);
+ if (dev->info.wps_vendor_ext[i] == NULL)
+ break;
+ }
+
+ if (msg.wfd_subelems) {
+ wpabuf_free(dev->info.wfd_subelems);
+ dev->info.wfd_subelems = wpabuf_dup(msg.wfd_subelems);
+ }
+
+ if (scan_res) {
+ p2p_add_group_clients(p2p, p2p_dev_addr, addr, freq,
+ msg.group_info, msg.group_info_len);
+ }
+
+ p2p_parse_free(&msg);
+
+ if (p2p_pending_sd_req(p2p, dev))
+ dev->flags |= P2P_DEV_SD_SCHEDULE;
+
+ if (dev->flags & P2P_DEV_REPORTED)
+ return 0;
+
+ wpa_msg(p2p->cfg->msg_ctx, MSG_DEBUG,
+ "P2P: Peer found with Listen frequency %d MHz", freq);
+ if (dev->flags & P2P_DEV_USER_REJECTED) {
+ wpa_msg(p2p->cfg->msg_ctx, MSG_DEBUG,
+ "P2P: Do not report rejected device");
+ return 0;
+ }
+
+ p2p->cfg->dev_found(p2p->cfg->cb_ctx, addr, &dev->info,
+ !(dev->flags & P2P_DEV_REPORTED_ONCE));
+ dev->flags |= P2P_DEV_REPORTED | P2P_DEV_REPORTED_ONCE;
+
+ return 0;
+}
+
+
+static void p2p_device_free(struct p2p_data *p2p, struct p2p_device *dev)
+{
+ int i;
+
+ if (p2p->go_neg_peer == dev) {
+ /*
+ * If GO Negotiation is in progress, report that it has failed.
+ */
+ p2p_go_neg_failed(p2p, dev, -1);
+ p2p->go_neg_peer = NULL;
+ }
+ if (p2p->invite_peer == dev)
+ p2p->invite_peer = NULL;
+ if (p2p->sd_peer == dev)
+ p2p->sd_peer = NULL;
+ if (p2p->pending_client_disc_go == dev)
+ p2p->pending_client_disc_go = NULL;
+
+ /* dev_lost() device, but only if it was previously dev_found() */
+ if (dev->flags & P2P_DEV_REPORTED_ONCE)
+ p2p->cfg->dev_lost(p2p->cfg->cb_ctx,
+ dev->info.p2p_device_addr);
+
+ for (i = 0; i < P2P_MAX_WPS_VENDOR_EXT; i++) {
+ wpabuf_free(dev->info.wps_vendor_ext[i]);
+ dev->info.wps_vendor_ext[i] = NULL;
+ }
+
+ wpabuf_free(dev->info.wfd_subelems);
+
+ os_free(dev);
+}
+
+
+static int p2p_get_next_prog_freq(struct p2p_data *p2p)
+{
+ struct p2p_channels *c;
+ struct p2p_reg_class *cla;
+ size_t cl, ch;
+ int found = 0;
+ u8 reg_class;
+ u8 channel;
+ int freq;
+
+ c = &p2p->cfg->channels;
+ for (cl = 0; cl < c->reg_classes; cl++) {
+ cla = &c->reg_class[cl];
+ if (cla->reg_class != p2p->last_prog_scan_class)
+ continue;
+ for (ch = 0; ch < cla->channels; ch++) {
+ if (cla->channel[ch] == p2p->last_prog_scan_chan) {
+ found = 1;
+ break;
+ }
+ }
+ if (found)
+ break;
+ }
+
+ if (!found) {
+ /* Start from beginning */
+ reg_class = c->reg_class[0].reg_class;
+ channel = c->reg_class[0].channel[0];
+ } else {
+ /* Pick the next channel */
+ ch++;
+ if (ch == cla->channels) {
+ cl++;
+ if (cl == c->reg_classes)
+ cl = 0;
+ ch = 0;
+ }
+ reg_class = c->reg_class[cl].reg_class;
+ channel = c->reg_class[cl].channel[ch];
+ }
+
+ freq = p2p_channel_to_freq(p2p->cfg->country, reg_class, channel);
+ wpa_msg(p2p->cfg->msg_ctx, MSG_DEBUG, "P2P: Next progressive search "
+ "channel: reg_class %u channel %u -> %d MHz",
+ reg_class, channel, freq);
+ p2p->last_prog_scan_class = reg_class;
+ p2p->last_prog_scan_chan = channel;
+
+ if (freq == 2412 || freq == 2437 || freq == 2462)
+ return 0; /* No need to add social channels */
+ return freq;
+}
+
+
+static void p2p_search(struct p2p_data *p2p)
+{
+ int freq = 0;
+ enum p2p_scan_type type;
+ u16 pw_id = DEV_PW_DEFAULT;
+ int res;
+
+ if (p2p->drv_in_listen) {
+ wpa_msg(p2p->cfg->msg_ctx, MSG_DEBUG, "P2P: Driver is still "
+ "in Listen state - wait for it to end before "
+ "continuing");
+ return;
+ }
+ p2p->cfg->stop_listen(p2p->cfg->cb_ctx);
+
+ if (p2p->find_type == P2P_FIND_PROGRESSIVE &&
+ (freq = p2p_get_next_prog_freq(p2p)) > 0) {
+ type = P2P_SCAN_SOCIAL_PLUS_ONE;
+ wpa_msg(p2p->cfg->msg_ctx, MSG_DEBUG, "P2P: Starting search "
+ "(+ freq %u)", freq);
+ } else {
+ type = P2P_SCAN_SOCIAL;
+ wpa_msg(p2p->cfg->msg_ctx, MSG_DEBUG, "P2P: Starting search");
+ }
+
+ res = p2p->cfg->p2p_scan(p2p->cfg->cb_ctx, type, freq,
+ p2p->num_req_dev_types, p2p->req_dev_types,
+ p2p->find_dev_id, pw_id);
+ if (res < 0) {
+ wpa_msg(p2p->cfg->msg_ctx, MSG_DEBUG,
+ "P2P: Scan request failed");
+ p2p_continue_find(p2p);
+ } else if (res == 1) {
+ wpa_msg(p2p->cfg->msg_ctx, MSG_DEBUG, "P2P: Could not start "
+ "p2p_scan at this point - will try again after "
+ "previous scan completes");
+ p2p_set_state(p2p, P2P_CONTINUE_SEARCH_WHEN_READY);
+ } else {
+ wpa_msg(p2p->cfg->msg_ctx, MSG_DEBUG, "P2P: Running p2p_scan");
+ p2p->p2p_scan_running = 1;
+ eloop_cancel_timeout(p2p_scan_timeout, p2p, NULL);
+ eloop_register_timeout(P2P_SCAN_TIMEOUT, 0, p2p_scan_timeout,
+ p2p, NULL);
+ }
+}
+
+
+static void p2p_find_timeout(void *eloop_ctx, void *timeout_ctx)
+{
+ struct p2p_data *p2p = eloop_ctx;
+ wpa_msg(p2p->cfg->msg_ctx, MSG_DEBUG, "P2P: Find timeout -> stop");
+ p2p_stop_find(p2p);
+}
+
+
+static int p2p_run_after_scan(struct p2p_data *p2p)
+{
+ struct p2p_device *dev;
+ enum p2p_after_scan op;
+
+ if (p2p->after_scan_tx) {
+ /* TODO: schedule p2p_run_after_scan to be called from TX
+ * status callback(?) */
+ wpa_msg(p2p->cfg->msg_ctx, MSG_DEBUG, "P2P: Send pending "
+ "Action frame at p2p_scan completion");
+ p2p->cfg->send_action(p2p->cfg->cb_ctx,
+ p2p->after_scan_tx->freq,
+ p2p->after_scan_tx->dst,
+ p2p->after_scan_tx->src,
+ p2p->after_scan_tx->bssid,
+ (u8 *) (p2p->after_scan_tx + 1),
+ p2p->after_scan_tx->len,
+ p2p->after_scan_tx->wait_time);
+ os_free(p2p->after_scan_tx);
+ p2p->after_scan_tx = NULL;
+ return 1;
+ }
+
+ op = p2p->start_after_scan;
+ p2p->start_after_scan = P2P_AFTER_SCAN_NOTHING;
+ switch (op) {
+ case P2P_AFTER_SCAN_NOTHING:
+ break;
+ case P2P_AFTER_SCAN_LISTEN:
+ wpa_msg(p2p->cfg->msg_ctx, MSG_DEBUG, "P2P: Start previously "
+ "requested Listen state");
+ p2p_listen(p2p, p2p->pending_listen_sec * 1000 +
+ p2p->pending_listen_usec / 1000);
+ return 1;
+ case P2P_AFTER_SCAN_CONNECT:
+ wpa_msg(p2p->cfg->msg_ctx, MSG_DEBUG, "P2P: Start previously "
+ "requested connect with " MACSTR,
+ MAC2STR(p2p->after_scan_peer));
+ dev = p2p_get_device(p2p, p2p->after_scan_peer);
+ if (dev == NULL) {
+ wpa_msg(p2p->cfg->msg_ctx, MSG_DEBUG, "P2P: Peer not "
+ "known anymore");
+ break;
+ }
+ p2p_connect_send(p2p, dev);
+ return 1;
+ }
+
+ return 0;
+}
+
+
+static void p2p_scan_timeout(void *eloop_ctx, void *timeout_ctx)
+{
+ struct p2p_data *p2p = eloop_ctx;
+ int running;
+ wpa_msg(p2p->cfg->msg_ctx, MSG_DEBUG, "P2P: p2p_scan timeout "
+ "(running=%d)", p2p->p2p_scan_running);
+ running = p2p->p2p_scan_running;
+ /* Make sure we recover from missed scan results callback */
+ p2p->p2p_scan_running = 0;
+
+ if (running)
+ p2p_run_after_scan(p2p);
+}
+
+
+static void p2p_free_req_dev_types(struct p2p_data *p2p)
+{
+ p2p->num_req_dev_types = 0;
+ os_free(p2p->req_dev_types);
+ p2p->req_dev_types = NULL;
+}
+
+
+int p2p_find(struct p2p_data *p2p, unsigned int timeout,
+ enum p2p_discovery_type type,
+ unsigned int num_req_dev_types, const u8 *req_dev_types,
+ const u8 *dev_id, unsigned int search_delay)
+{
+ int res;
+
+ wpa_msg(p2p->cfg->msg_ctx, MSG_DEBUG, "P2P: Starting find (type=%d)",
+ type);
+ if (p2p->p2p_scan_running) {
+ wpa_msg(p2p->cfg->msg_ctx, MSG_DEBUG, "P2P: p2p_scan is "
+ "already running");
+ }
+
+ p2p_free_req_dev_types(p2p);
+ if (req_dev_types && num_req_dev_types) {
+ p2p->req_dev_types = os_malloc(num_req_dev_types *
+ WPS_DEV_TYPE_LEN);
+ if (p2p->req_dev_types == NULL)
+ return -1;
+ os_memcpy(p2p->req_dev_types, req_dev_types,
+ num_req_dev_types * WPS_DEV_TYPE_LEN);
+ p2p->num_req_dev_types = num_req_dev_types;
+ }
+
+ if (dev_id) {
+ os_memcpy(p2p->find_dev_id_buf, dev_id, ETH_ALEN);
+ p2p->find_dev_id = p2p->find_dev_id_buf;
+ } else
+ p2p->find_dev_id = NULL;
+
+ p2p->start_after_scan = P2P_AFTER_SCAN_NOTHING;
+ p2p_clear_timeout(p2p);
+ p2p->cfg->stop_listen(p2p->cfg->cb_ctx);
+ p2p->find_type = type;
+ p2p_device_clear_reported(p2p);
+ p2p_set_state(p2p, P2P_SEARCH);
+ p2p->search_delay = search_delay;
+ p2p->in_search_delay = 0;
+ eloop_cancel_timeout(p2p_find_timeout, p2p, NULL);
+ p2p->last_p2p_find_timeout = timeout;
+ if (timeout)
+ eloop_register_timeout(timeout, 0, p2p_find_timeout,
+ p2p, NULL);
+ switch (type) {
+ case P2P_FIND_START_WITH_FULL:
+ case P2P_FIND_PROGRESSIVE:
+ res = p2p->cfg->p2p_scan(p2p->cfg->cb_ctx, P2P_SCAN_FULL, 0,
+ p2p->num_req_dev_types,
+ p2p->req_dev_types, dev_id,
+ DEV_PW_DEFAULT);
+ break;
+ case P2P_FIND_ONLY_SOCIAL:
+ res = p2p->cfg->p2p_scan(p2p->cfg->cb_ctx, P2P_SCAN_SOCIAL, 0,
+ p2p->num_req_dev_types,
+ p2p->req_dev_types, dev_id,
+ DEV_PW_DEFAULT);
+ break;
+ default:
+ return -1;
+ }
+
+ if (res == 0) {
+ wpa_msg(p2p->cfg->msg_ctx, MSG_DEBUG, "P2P: Running p2p_scan");
+ p2p->p2p_scan_running = 1;
+ eloop_cancel_timeout(p2p_scan_timeout, p2p, NULL);
+ eloop_register_timeout(P2P_SCAN_TIMEOUT, 0, p2p_scan_timeout,
+ p2p, NULL);
+ } else if (res == 1) {
+ wpa_msg(p2p->cfg->msg_ctx, MSG_DEBUG, "P2P: Could not start "
+ "p2p_scan at this point - will try again after "
+ "previous scan completes");
+ res = 0;
+ p2p_set_state(p2p, P2P_SEARCH_WHEN_READY);
+ eloop_cancel_timeout(p2p_find_timeout, p2p, NULL);
+ } else {
+ wpa_msg(p2p->cfg->msg_ctx, MSG_DEBUG, "P2P: Failed to start "
+ "p2p_scan");
+ p2p_set_state(p2p, P2P_IDLE);
+ eloop_cancel_timeout(p2p_find_timeout, p2p, NULL);
+ }
+
+ return res;
+}
+
+
+int p2p_other_scan_completed(struct p2p_data *p2p)
+{
+ if (p2p->state == P2P_CONTINUE_SEARCH_WHEN_READY) {
+ p2p_set_state(p2p, P2P_SEARCH);
+ p2p_search(p2p);
+ return 1;
+ }
+ if (p2p->state != P2P_SEARCH_WHEN_READY)
+ return 0;
+ wpa_msg(p2p->cfg->msg_ctx, MSG_DEBUG, "P2P: Starting pending P2P find "
+ "now that previous scan was completed");
+ if (p2p_find(p2p, p2p->last_p2p_find_timeout, p2p->find_type,
+ p2p->num_req_dev_types, p2p->req_dev_types,
+ p2p->find_dev_id, p2p->search_delay) < 0)
+ return 0;
+ return 1;
+}
+
+
+void p2p_stop_find_for_freq(struct p2p_data *p2p, int freq)
+{
+ wpa_msg(p2p->cfg->msg_ctx, MSG_DEBUG, "P2P: Stopping find");
+ eloop_cancel_timeout(p2p_find_timeout, p2p, NULL);
+ p2p_clear_timeout(p2p);
+ if (p2p->state == P2P_SEARCH)
+ wpa_msg(p2p->cfg->msg_ctx, MSG_INFO, P2P_EVENT_FIND_STOPPED);
+ p2p_set_state(p2p, P2P_IDLE);
+ p2p_free_req_dev_types(p2p);
+ p2p->start_after_scan = P2P_AFTER_SCAN_NOTHING;
+ p2p->go_neg_peer = NULL;
+ p2p->sd_peer = NULL;
+ p2p->invite_peer = NULL;
+ p2p_stop_listen_for_freq(p2p, freq);
+}
+
+
+void p2p_stop_listen_for_freq(struct p2p_data *p2p, int freq)
+{
+ if (freq > 0 && p2p->drv_in_listen == freq && p2p->in_listen) {
+ wpa_msg(p2p->cfg->msg_ctx, MSG_DEBUG, "P2P: Skip stop_listen "
+ "since we are on correct channel for response");
+ return;
+ }
+ if (p2p->in_listen) {
+ p2p->in_listen = 0;
+ p2p_clear_timeout(p2p);
+ }
+ if (p2p->drv_in_listen) {
+ /*
+ * The driver may not deliver callback to p2p_listen_end()
+ * when the operation gets canceled, so clear the internal
+ * variable that is tracking driver state.
+ */
+ wpa_msg(p2p->cfg->msg_ctx, MSG_DEBUG, "P2P: Clear "
+ "drv_in_listen (%d)", p2p->drv_in_listen);
+ p2p->drv_in_listen = 0;
+ }
+ p2p->cfg->stop_listen(p2p->cfg->cb_ctx);
+}
+
+
+void p2p_stop_find(struct p2p_data *p2p)
+{
+ p2p_stop_find_for_freq(p2p, 0);
+}
+
+
+static int p2p_prepare_channel_pref(struct p2p_data *p2p,
+ unsigned int force_freq,
+ unsigned int pref_freq)
+{
+ u8 op_class, op_channel;
+ unsigned int freq = force_freq ? force_freq : pref_freq;
+
+ if (p2p_freq_to_channel(p2p->cfg->country, freq,
+ &op_class, &op_channel) < 0) {
+ wpa_msg(p2p->cfg->msg_ctx, MSG_DEBUG,
+ "P2P: Unsupported frequency %u MHz", freq);
+ return -1;
+ }
+
+ if (!p2p_channels_includes(&p2p->cfg->channels, op_class, op_channel)) {
+ wpa_msg(p2p->cfg->msg_ctx, MSG_DEBUG,
+ "P2P: Frequency %u MHz (oper_class %u channel %u) not "
+ "allowed for P2P", freq, op_class, op_channel);
+ return -1;
+ }
+
+ p2p->op_reg_class = op_class;
+ p2p->op_channel = op_channel;
+
+ if (force_freq) {
+ p2p->channels.reg_classes = 1;
+ p2p->channels.reg_class[0].channels = 1;
+ p2p->channels.reg_class[0].reg_class = p2p->op_reg_class;
+ p2p->channels.reg_class[0].channel[0] = p2p->op_channel;
+ } else {
+ os_memcpy(&p2p->channels, &p2p->cfg->channels,
+ sizeof(struct p2p_channels));
+ }
+
+ return 0;
+}
+
+
+static void p2p_prepare_channel_best(struct p2p_data *p2p)
+{
+ u8 op_class, op_channel;
+
+ if (!p2p->cfg->cfg_op_channel && p2p->best_freq_overall > 0 &&
+ p2p_supported_freq(p2p, p2p->best_freq_overall) &&
+ p2p_freq_to_channel(p2p->cfg->country, p2p->best_freq_overall,
+ &op_class, &op_channel) == 0) {
+ wpa_msg(p2p->cfg->msg_ctx, MSG_DEBUG, "P2P: Select best "
+ "overall channel as operating channel preference");
+ p2p->op_reg_class = op_class;
+ p2p->op_channel = op_channel;
+ } else if (!p2p->cfg->cfg_op_channel && p2p->best_freq_5 > 0 &&
+ p2p_supported_freq(p2p, p2p->best_freq_5) &&
+ p2p_freq_to_channel(p2p->cfg->country, p2p->best_freq_5,
+ &op_class, &op_channel) == 0) {
+ wpa_msg(p2p->cfg->msg_ctx, MSG_DEBUG, "P2P: Select best 5 GHz "
+ "channel as operating channel preference");
+ p2p->op_reg_class = op_class;
+ p2p->op_channel = op_channel;
+ } else if (!p2p->cfg->cfg_op_channel && p2p->best_freq_24 > 0 &&
+ p2p_supported_freq(p2p, p2p->best_freq_24) &&
+ p2p_freq_to_channel(p2p->cfg->country, p2p->best_freq_24,
+ &op_class, &op_channel) == 0) {
+ wpa_msg(p2p->cfg->msg_ctx, MSG_DEBUG, "P2P: Select best 2.4 "
+ "GHz channel as operating channel preference");
+ p2p->op_reg_class = op_class;
+ p2p->op_channel = op_channel;
+ } else {
+ p2p->op_reg_class = p2p->cfg->op_reg_class;
+ p2p->op_channel = p2p->cfg->op_channel;
+ }
+
+ os_memcpy(&p2p->channels, &p2p->cfg->channels,
+ sizeof(struct p2p_channels));
+}
+
+
+/**
+ * p2p_prepare_channel - Select operating channel for GO Negotiation
+ * @p2p: P2P module context from p2p_init()
+ * @dev: Selected peer device
+ * @force_freq: Forced frequency in MHz or 0 if not forced
+ * @pref_freq: Preferred frequency in MHz or 0 if no preference
+ * Returns: 0 on success, -1 on failure (channel not supported for P2P)
+ *
+ * This function is used to do initial operating channel selection for GO
+ * Negotiation prior to having received peer information. The selected channel
+ * may be further optimized in p2p_reselect_channel() once the peer information
+ * is available.
+ */
+static int p2p_prepare_channel(struct p2p_data *p2p, struct p2p_device *dev,
+ unsigned int force_freq, unsigned int pref_freq)
+{
+ if (force_freq || pref_freq) {
+ if (p2p_prepare_channel_pref(p2p, force_freq, pref_freq) < 0)
+ return -1;
+ } else {
+ p2p_prepare_channel_best(p2p);
+ }
+ wpa_msg(p2p->cfg->msg_ctx, MSG_DEBUG,
+ "P2P: Own preference for operation channel: "
+ "Operating Class %u Channel %u%s",
+ p2p->op_reg_class, p2p->op_channel,
+ force_freq ? " (forced)" : "");
+
+ if (force_freq)
+ dev->flags |= P2P_DEV_FORCE_FREQ;
+ else
+ dev->flags &= ~P2P_DEV_FORCE_FREQ;
+
+ return 0;
+}
+
+
+static void p2p_set_dev_persistent(struct p2p_device *dev,
+ int persistent_group)
+{
+ switch (persistent_group) {
+ case 0:
+ dev->flags &= ~(P2P_DEV_PREFER_PERSISTENT_GROUP |
+ P2P_DEV_PREFER_PERSISTENT_RECONN);
+ break;
+ case 1:
+ dev->flags |= P2P_DEV_PREFER_PERSISTENT_GROUP;
+ dev->flags &= ~P2P_DEV_PREFER_PERSISTENT_RECONN;
+ break;
+ case 2:
+ dev->flags |= P2P_DEV_PREFER_PERSISTENT_GROUP |
+ P2P_DEV_PREFER_PERSISTENT_RECONN;
+ break;
+ }
+}
+
+
+int p2p_connect(struct p2p_data *p2p, const u8 *peer_addr,
+ enum p2p_wps_method wps_method,
+ int go_intent, const u8 *own_interface_addr,
+ unsigned int force_freq, int persistent_group,
+ const u8 *force_ssid, size_t force_ssid_len,
+ int pd_before_go_neg, unsigned int pref_freq)
+{
+ struct p2p_device *dev;
+
+ wpa_msg(p2p->cfg->msg_ctx, MSG_DEBUG,
+ "P2P: Request to start group negotiation - peer=" MACSTR
+ " GO Intent=%d Intended Interface Address=" MACSTR
+ " wps_method=%d persistent_group=%d pd_before_go_neg=%d",
+ MAC2STR(peer_addr), go_intent, MAC2STR(own_interface_addr),
+ wps_method, persistent_group, pd_before_go_neg);
+
+ dev = p2p_get_device(p2p, peer_addr);
+ if (dev == NULL || (dev->flags & P2P_DEV_PROBE_REQ_ONLY)) {
+ wpa_msg(p2p->cfg->msg_ctx, MSG_DEBUG,
+ "P2P: Cannot connect to unknown P2P Device " MACSTR,
+ MAC2STR(peer_addr));
+ return -1;
+ }
+
+ if (p2p_prepare_channel(p2p, dev, force_freq, pref_freq) < 0)
+ return -1;
+
+ if (dev->flags & P2P_DEV_GROUP_CLIENT_ONLY) {
+ if (!(dev->info.dev_capab &
+ P2P_DEV_CAPAB_CLIENT_DISCOVERABILITY)) {
+ wpa_msg(p2p->cfg->msg_ctx, MSG_DEBUG,
+ "P2P: Cannot connect to P2P Device " MACSTR
+ " that is in a group and is not discoverable",
+ MAC2STR(peer_addr));
+ return -1;
+ }
+ if (dev->oper_freq <= 0) {
+ wpa_msg(p2p->cfg->msg_ctx, MSG_DEBUG,
+ "P2P: Cannot connect to P2P Device " MACSTR
+ " with incomplete information",
+ MAC2STR(peer_addr));
+ return -1;
+ }
+
+ /*
+ * First, try to connect directly. If the peer does not
+ * acknowledge frames, assume it is sleeping and use device
+ * discoverability via the GO at that point.
+ */
+ }
+
+ p2p->ssid_set = 0;
+ if (force_ssid) {
+ wpa_hexdump_ascii(MSG_DEBUG, "P2P: Forced SSID",
+ force_ssid, force_ssid_len);
+ os_memcpy(p2p->ssid, force_ssid, force_ssid_len);
+ p2p->ssid_len = force_ssid_len;
+ p2p->ssid_set = 1;
+ }
+
+ dev->flags &= ~P2P_DEV_NOT_YET_READY;
+ dev->flags &= ~P2P_DEV_USER_REJECTED;
+ dev->flags &= ~P2P_DEV_WAIT_GO_NEG_RESPONSE;
+ dev->flags &= ~P2P_DEV_WAIT_GO_NEG_CONFIRM;
+ if (pd_before_go_neg)
+ dev->flags |= P2P_DEV_PD_BEFORE_GO_NEG;
+ else
+ dev->flags &= ~P2P_DEV_PD_BEFORE_GO_NEG;
+ dev->connect_reqs = 0;
+ dev->go_neg_req_sent = 0;
+ dev->go_state = UNKNOWN_GO;
+ p2p_set_dev_persistent(dev, persistent_group);
+ p2p->go_intent = go_intent;
+ os_memcpy(p2p->intended_addr, own_interface_addr, ETH_ALEN);
+
+ if (p2p->state != P2P_IDLE)
+ p2p_stop_find(p2p);
+
+ if (p2p->after_scan_tx) {
+ /*
+ * We need to drop the pending frame to avoid issues with the
+ * new GO Negotiation, e.g., when the pending frame was from a
+ * previous attempt at starting a GO Negotiation.
+ */
+ wpa_msg(p2p->cfg->msg_ctx, MSG_DEBUG, "P2P: Dropped "
+ "previous pending Action frame TX that was waiting "
+ "for p2p_scan completion");
+ os_free(p2p->after_scan_tx);
+ p2p->after_scan_tx = NULL;
+ }
+
+ dev->wps_method = wps_method;
+ dev->status = P2P_SC_SUCCESS;
+
+ if (p2p->p2p_scan_running) {
+ wpa_msg(p2p->cfg->msg_ctx, MSG_DEBUG,
+ "P2P: p2p_scan running - delay connect send");
+ p2p->start_after_scan = P2P_AFTER_SCAN_CONNECT;
+ os_memcpy(p2p->after_scan_peer, peer_addr, ETH_ALEN);
+ return 0;
+ }
+ p2p->start_after_scan = P2P_AFTER_SCAN_NOTHING;
+
+ return p2p_connect_send(p2p, dev);
+}
+
+
+int p2p_authorize(struct p2p_data *p2p, const u8 *peer_addr,
+ enum p2p_wps_method wps_method,
+ int go_intent, const u8 *own_interface_addr,
+ unsigned int force_freq, int persistent_group,
+ const u8 *force_ssid, size_t force_ssid_len,
+ unsigned int pref_freq)
+{
+ struct p2p_device *dev;
+
+ wpa_msg(p2p->cfg->msg_ctx, MSG_DEBUG,
+ "P2P: Request to authorize group negotiation - peer=" MACSTR
+ " GO Intent=%d Intended Interface Address=" MACSTR
+ " wps_method=%d persistent_group=%d",
+ MAC2STR(peer_addr), go_intent, MAC2STR(own_interface_addr),
+ wps_method, persistent_group);
+
+ dev = p2p_get_device(p2p, peer_addr);
+ if (dev == NULL) {
+ wpa_msg(p2p->cfg->msg_ctx, MSG_DEBUG,
+ "P2P: Cannot authorize unknown P2P Device " MACSTR,
+ MAC2STR(peer_addr));
+ return -1;
+ }
+
+ if (p2p_prepare_channel(p2p, dev, force_freq, pref_freq) < 0)
+ return -1;
+
+ p2p->ssid_set = 0;
+ if (force_ssid) {
+ wpa_hexdump_ascii(MSG_DEBUG, "P2P: Forced SSID",
+ force_ssid, force_ssid_len);
+ os_memcpy(p2p->ssid, force_ssid, force_ssid_len);
+ p2p->ssid_len = force_ssid_len;
+ p2p->ssid_set = 1;
+ }
+
+ dev->flags &= ~P2P_DEV_NOT_YET_READY;
+ dev->flags &= ~P2P_DEV_USER_REJECTED;
+ dev->go_neg_req_sent = 0;
+ dev->go_state = UNKNOWN_GO;
+ p2p_set_dev_persistent(dev, persistent_group);
+ p2p->go_intent = go_intent;
+ os_memcpy(p2p->intended_addr, own_interface_addr, ETH_ALEN);
+
+ dev->wps_method = wps_method;
+ dev->status = P2P_SC_SUCCESS;
+
+ return 0;
+}
+
+
+void p2p_add_dev_info(struct p2p_data *p2p, const u8 *addr,
+ struct p2p_device *dev, struct p2p_message *msg)
+{
+ os_get_time(&dev->last_seen);
+
+ p2p_copy_wps_info(dev, 0, msg);
+
+ if (msg->listen_channel) {
+ int freq;
+ freq = p2p_channel_to_freq((char *) msg->listen_channel,
+ msg->listen_channel[3],
+ msg->listen_channel[4]);
+ if (freq < 0) {
+ wpa_msg(p2p->cfg->msg_ctx, MSG_DEBUG,
+ "P2P: Unknown peer Listen channel: "
+ "country=%c%c(0x%02x) reg_class=%u channel=%u",
+ msg->listen_channel[0],
+ msg->listen_channel[1],
+ msg->listen_channel[2],
+ msg->listen_channel[3],
+ msg->listen_channel[4]);
+ } else {
+ wpa_msg(p2p->cfg->msg_ctx, MSG_DEBUG, "P2P: Update "
+ "peer " MACSTR " Listen channel: %u -> %u MHz",
+ MAC2STR(dev->info.p2p_device_addr),
+ dev->listen_freq, freq);
+ dev->listen_freq = freq;
+ }
+ }
+
+ if (msg->wfd_subelems) {
+ wpabuf_free(dev->info.wfd_subelems);
+ dev->info.wfd_subelems = wpabuf_dup(msg->wfd_subelems);
+ }
+
+ if (dev->flags & P2P_DEV_PROBE_REQ_ONLY) {
+ dev->flags &= ~P2P_DEV_PROBE_REQ_ONLY;
+ wpa_msg(p2p->cfg->msg_ctx, MSG_DEBUG,
+ "P2P: Completed device entry based on data from "
+ "GO Negotiation Request");
+ } else {
+ wpa_msg(p2p->cfg->msg_ctx, MSG_DEBUG,
+ "P2P: Created device entry based on GO Neg Req: "
+ MACSTR " dev_capab=0x%x group_capab=0x%x name='%s' "
+ "listen_freq=%d",
+ MAC2STR(dev->info.p2p_device_addr),
+ dev->info.dev_capab, dev->info.group_capab,
+ dev->info.device_name, dev->listen_freq);
+ }
+
+ dev->flags &= ~P2P_DEV_GROUP_CLIENT_ONLY;
+
+ if (dev->flags & P2P_DEV_USER_REJECTED) {
+ wpa_msg(p2p->cfg->msg_ctx, MSG_DEBUG,
+ "P2P: Do not report rejected device");
+ return;
+ }
+
+ p2p->cfg->dev_found(p2p->cfg->cb_ctx, addr, &dev->info,
+ !(dev->flags & P2P_DEV_REPORTED_ONCE));
+ dev->flags |= P2P_DEV_REPORTED | P2P_DEV_REPORTED_ONCE;
+}
+
+
+void p2p_build_ssid(struct p2p_data *p2p, u8 *ssid, size_t *ssid_len)
+{
+ os_memcpy(ssid, P2P_WILDCARD_SSID, P2P_WILDCARD_SSID_LEN);
+ p2p_random((char *) &ssid[P2P_WILDCARD_SSID_LEN], 2);
+ os_memcpy(&ssid[P2P_WILDCARD_SSID_LEN + 2],
+ p2p->cfg->ssid_postfix, p2p->cfg->ssid_postfix_len);
+ *ssid_len = P2P_WILDCARD_SSID_LEN + 2 + p2p->cfg->ssid_postfix_len;
+}
+
+
+int p2p_go_params(struct p2p_data *p2p, struct p2p_go_neg_results *params)
+{
+ p2p_build_ssid(p2p, params->ssid, &params->ssid_len);
+ p2p_random(params->passphrase, 8);
+ return 0;
+}
+
+
+void p2p_go_complete(struct p2p_data *p2p, struct p2p_device *peer)
+{
+ struct p2p_go_neg_results res;
+ int go = peer->go_state == LOCAL_GO;
+ struct p2p_channels intersection;
+ int freqs;
+ size_t i, j;
+
+ wpa_msg(p2p->cfg->msg_ctx, MSG_DEBUG,
+ "P2P: GO Negotiation with " MACSTR " completed (%s will be "
+ "GO)", MAC2STR(peer->info.p2p_device_addr),
+ go ? "local end" : "peer");
+
+ os_memset(&res, 0, sizeof(res));
+ res.role_go = go;
+ os_memcpy(res.peer_device_addr, peer->info.p2p_device_addr, ETH_ALEN);
+ os_memcpy(res.peer_interface_addr, peer->intended_addr, ETH_ALEN);
+ res.wps_method = peer->wps_method;
+ if (peer->flags & P2P_DEV_PREFER_PERSISTENT_GROUP) {
+ if (peer->flags & P2P_DEV_PREFER_PERSISTENT_RECONN)
+ res.persistent_group = 2;
+ else
+ res.persistent_group = 1;
+ }
+
+ if (go) {
+ /* Setup AP mode for WPS provisioning */
+ res.freq = p2p_channel_to_freq(p2p->cfg->country,
+ p2p->op_reg_class,
+ p2p->op_channel);
+ os_memcpy(res.ssid, p2p->ssid, p2p->ssid_len);
+ res.ssid_len = p2p->ssid_len;
+ p2p_random(res.passphrase, 8);
+ } else {
+ res.freq = peer->oper_freq;
+ if (p2p->ssid_len) {
+ os_memcpy(res.ssid, p2p->ssid, p2p->ssid_len);
+ res.ssid_len = p2p->ssid_len;
+ }
+ }
+
+ p2p_channels_intersect(&p2p->channels, &peer->channels,
+ &intersection);
+ freqs = 0;
+ for (i = 0; i < intersection.reg_classes; i++) {
+ struct p2p_reg_class *c = &intersection.reg_class[i];
+ if (freqs + 1 == P2P_MAX_CHANNELS)
+ break;
+ for (j = 0; j < c->channels; j++) {
+ int freq;
+ if (freqs + 1 == P2P_MAX_CHANNELS)
+ break;
+ freq = p2p_channel_to_freq(peer->country, c->reg_class,
+ c->channel[j]);
+ if (freq < 0)
+ continue;
+ res.freq_list[freqs++] = freq;
+ }
+ }
+
+ res.peer_config_timeout = go ? peer->client_timeout : peer->go_timeout;
+
+ p2p_clear_timeout(p2p);
+ p2p->ssid_set = 0;
+ peer->go_neg_req_sent = 0;
+ peer->wps_method = WPS_NOT_READY;
+
+ p2p_set_state(p2p, P2P_PROVISIONING);
+ p2p->cfg->go_neg_completed(p2p->cfg->cb_ctx, &res);
+}
+
+
+static void p2p_rx_p2p_action(struct p2p_data *p2p, const u8 *sa,
+ const u8 *data, size_t len, int rx_freq)
+{
+ wpa_msg(p2p->cfg->msg_ctx, MSG_DEBUG,
+ "P2P: RX P2P Public Action from " MACSTR, MAC2STR(sa));
+ wpa_hexdump(MSG_MSGDUMP, "P2P: P2P Public Action contents", data, len);
+
+ if (len < 1)
+ return;
+
+ switch (data[0]) {
+ case P2P_GO_NEG_REQ:
+ p2p_process_go_neg_req(p2p, sa, data + 1, len - 1, rx_freq);
+ break;
+ case P2P_GO_NEG_RESP:
+ p2p_process_go_neg_resp(p2p, sa, data + 1, len - 1, rx_freq);
+ break;
+ case P2P_GO_NEG_CONF:
+ p2p_process_go_neg_conf(p2p, sa, data + 1, len - 1);
+ break;
+ case P2P_INVITATION_REQ:
+ p2p_process_invitation_req(p2p, sa, data + 1, len - 1,
+ rx_freq);
+ break;
+ case P2P_INVITATION_RESP:
+ p2p_process_invitation_resp(p2p, sa, data + 1, len - 1);
+ break;
+ case P2P_PROV_DISC_REQ:
+ p2p_process_prov_disc_req(p2p, sa, data + 1, len - 1, rx_freq);
+ break;
+ case P2P_PROV_DISC_RESP:
+ p2p_process_prov_disc_resp(p2p, sa, data + 1, len - 1);
+ break;
+ case P2P_DEV_DISC_REQ:
+ p2p_process_dev_disc_req(p2p, sa, data + 1, len - 1, rx_freq);
+ break;
+ case P2P_DEV_DISC_RESP:
+ p2p_process_dev_disc_resp(p2p, sa, data + 1, len - 1);
+ break;
+ default:
+ wpa_msg(p2p->cfg->msg_ctx, MSG_DEBUG,
+ "P2P: Unsupported P2P Public Action frame type %d",
+ data[0]);
+ break;
+ }
+}
+
+
+static void p2p_rx_action_public(struct p2p_data *p2p, const u8 *da,
+ const u8 *sa, const u8 *bssid, const u8 *data,
+ size_t len, int freq)
+{
+ if (len < 1)
+ return;
+
+ switch (data[0]) {
+ case WLAN_PA_VENDOR_SPECIFIC:
+ data++;
+ len--;
+ if (len < 3)
+ return;
+ if (WPA_GET_BE24(data) != OUI_WFA)
+ return;
+
+ data += 3;
+ len -= 3;
+ if (len < 1)
+ return;
+
+ if (*data != P2P_OUI_TYPE)
+ return;
+
+ p2p_rx_p2p_action(p2p, sa, data + 1, len - 1, freq);
+ break;
+ case WLAN_PA_GAS_INITIAL_REQ:
+ p2p_rx_gas_initial_req(p2p, sa, data + 1, len - 1, freq);
+ break;
+ case WLAN_PA_GAS_INITIAL_RESP:
+ p2p_rx_gas_initial_resp(p2p, sa, data + 1, len - 1, freq);
+ break;
+ case WLAN_PA_GAS_COMEBACK_REQ:
+ p2p_rx_gas_comeback_req(p2p, sa, data + 1, len - 1, freq);
+ break;
+ case WLAN_PA_GAS_COMEBACK_RESP:
+ p2p_rx_gas_comeback_resp(p2p, sa, data + 1, len - 1, freq);
+ break;
+ }
+}
+
+
+void p2p_rx_action(struct p2p_data *p2p, const u8 *da, const u8 *sa,
+ const u8 *bssid, u8 category,
+ const u8 *data, size_t len, int freq)
+{
+ if (category == WLAN_ACTION_PUBLIC) {
+ p2p_rx_action_public(p2p, da, sa, bssid, data, len, freq);
+ return;
+ }
+
+ if (category != WLAN_ACTION_VENDOR_SPECIFIC)
+ return;
+
+ if (len < 4)
+ return;
+
+ if (WPA_GET_BE24(data) != OUI_WFA)
+ return;
+ data += 3;
+ len -= 3;
+
+ if (*data != P2P_OUI_TYPE)
+ return;
+ data++;
+ len--;
+
+ /* P2P action frame */
+ wpa_msg(p2p->cfg->msg_ctx, MSG_DEBUG,
+ "P2P: RX P2P Action from " MACSTR, MAC2STR(sa));
+ wpa_hexdump(MSG_MSGDUMP, "P2P: P2P Action contents", data, len);
+
+ if (len < 1)
+ return;
+ switch (data[0]) {
+ case P2P_NOA:
+ wpa_msg(p2p->cfg->msg_ctx, MSG_DEBUG,
+ "P2P: Received P2P Action - Notice of Absence");
+ /* TODO */
+ break;
+ case P2P_PRESENCE_REQ:
+ p2p_process_presence_req(p2p, da, sa, data + 1, len - 1, freq);
+ break;
+ case P2P_PRESENCE_RESP:
+ p2p_process_presence_resp(p2p, da, sa, data + 1, len - 1);
+ break;
+ case P2P_GO_DISC_REQ:
+ p2p_process_go_disc_req(p2p, da, sa, data + 1, len - 1, freq);
+ break;
+ default:
+ wpa_msg(p2p->cfg->msg_ctx, MSG_DEBUG,
+ "P2P: Received P2P Action - unknown type %u", data[0]);
+ break;
+ }
+}
+
+
+static void p2p_go_neg_start(void *eloop_ctx, void *timeout_ctx)
+{
+ struct p2p_data *p2p = eloop_ctx;
+ if (p2p->go_neg_peer == NULL)
+ return;
+ p2p->cfg->stop_listen(p2p->cfg->cb_ctx);
+ p2p->go_neg_peer->status = P2P_SC_SUCCESS;
+ p2p_connect_send(p2p, p2p->go_neg_peer);
+}
+
+
+static void p2p_invite_start(void *eloop_ctx, void *timeout_ctx)
+{
+ struct p2p_data *p2p = eloop_ctx;
+ if (p2p->invite_peer == NULL)
+ return;
+ p2p->cfg->stop_listen(p2p->cfg->cb_ctx);
+ p2p_invite_send(p2p, p2p->invite_peer, p2p->invite_go_dev_addr);
+}
+
+
+static void p2p_add_dev_from_probe_req(struct p2p_data *p2p, const u8 *addr,
+ const u8 *ie, size_t ie_len)
+{
+ struct p2p_message msg;
+ struct p2p_device *dev;
+
+ os_memset(&msg, 0, sizeof(msg));
+ if (p2p_parse_ies(ie, ie_len, &msg) < 0 || msg.p2p_attributes == NULL)
+ {
+ p2p_parse_free(&msg);
+ return; /* not a P2P probe */
+ }
+
+ if (msg.ssid == NULL || msg.ssid[1] != P2P_WILDCARD_SSID_LEN ||
+ os_memcmp(msg.ssid + 2, P2P_WILDCARD_SSID, P2P_WILDCARD_SSID_LEN)
+ != 0) {
+ /* The Probe Request is not part of P2P Device Discovery. It is
+ * not known whether the source address of the frame is the P2P
+ * Device Address or P2P Interface Address. Do not add a new
+ * peer entry based on this frames.
+ */
+ p2p_parse_free(&msg);
+ return;
+ }
+
+ dev = p2p_get_device(p2p, addr);
+ if (dev) {
+ if (dev->country[0] == 0 && msg.listen_channel)
+ os_memcpy(dev->country, msg.listen_channel, 3);
+ os_get_time(&dev->last_seen);
+ p2p_parse_free(&msg);
+ return; /* already known */
+ }
+
+ dev = p2p_create_device(p2p, addr);
+ if (dev == NULL) {
+ p2p_parse_free(&msg);
+ return;
+ }
+
+ os_get_time(&dev->last_seen);
+ dev->flags |= P2P_DEV_PROBE_REQ_ONLY;
+
+ if (msg.listen_channel) {
+ os_memcpy(dev->country, msg.listen_channel, 3);
+ dev->listen_freq = p2p_channel_to_freq(dev->country,
+ msg.listen_channel[3],
+ msg.listen_channel[4]);
+ }
+
+ p2p_copy_wps_info(dev, 1, &msg);
+
+ if (msg.wfd_subelems) {
+ wpabuf_free(dev->info.wfd_subelems);
+ dev->info.wfd_subelems = wpabuf_dup(msg.wfd_subelems);
+ }
+
+ p2p_parse_free(&msg);
+
+ wpa_msg(p2p->cfg->msg_ctx, MSG_DEBUG,
+ "P2P: Created device entry based on Probe Req: " MACSTR
+ " dev_capab=0x%x group_capab=0x%x name='%s' listen_freq=%d",
+ MAC2STR(dev->info.p2p_device_addr), dev->info.dev_capab,
+ dev->info.group_capab, dev->info.device_name,
+ dev->listen_freq);
+}
+
+
+struct p2p_device * p2p_add_dev_from_go_neg_req(struct p2p_data *p2p,
+ const u8 *addr,
+ struct p2p_message *msg)
+{
+ struct p2p_device *dev;
+
+ dev = p2p_get_device(p2p, addr);
+ if (dev) {
+ os_get_time(&dev->last_seen);
+ return dev; /* already known */
+ }
+
+ dev = p2p_create_device(p2p, addr);
+ if (dev == NULL)
+ return NULL;
+
+ p2p_add_dev_info(p2p, addr, dev, msg);
+
+ return dev;
+}
+
+
+static int dev_type_match(const u8 *dev_type, const u8 *req_dev_type)
+{
+ if (os_memcmp(dev_type, req_dev_type, WPS_DEV_TYPE_LEN) == 0)
+ return 1;
+ if (os_memcmp(dev_type, req_dev_type, 2) == 0 &&
+ WPA_GET_BE32(&req_dev_type[2]) == 0 &&
+ WPA_GET_BE16(&req_dev_type[6]) == 0)
+ return 1; /* Category match with wildcard OUI/sub-category */
+ return 0;
+}
+
+
+int dev_type_list_match(const u8 *dev_type, const u8 *req_dev_type[],
+ size_t num_req_dev_type)
+{
+ size_t i;
+ for (i = 0; i < num_req_dev_type; i++) {
+ if (dev_type_match(dev_type, req_dev_type[i]))
+ return 1;
+ }
+ return 0;
+}
+
+
+/**
+ * p2p_match_dev_type - Match local device type with requested type
+ * @p2p: P2P module context from p2p_init()
+ * @wps: WPS TLVs from Probe Request frame (concatenated WPS IEs)
+ * Returns: 1 on match, 0 on mismatch
+ *
+ * This function can be used to match the Requested Device Type attribute in
+ * WPS IE with the local device types for deciding whether to reply to a Probe
+ * Request frame.
+ */
+int p2p_match_dev_type(struct p2p_data *p2p, struct wpabuf *wps)
+{
+ struct wps_parse_attr attr;
+ size_t i;
+
+ if (wps_parse_msg(wps, &attr))
+ return 1; /* assume no Requested Device Type attributes */
+
+ if (attr.num_req_dev_type == 0)
+ return 1; /* no Requested Device Type attributes -> match */
+
+ if (dev_type_list_match(p2p->cfg->pri_dev_type, attr.req_dev_type,
+ attr.num_req_dev_type))
+ return 1; /* Own Primary Device Type matches */
+
+ for (i = 0; i < p2p->cfg->num_sec_dev_types; i++)
+ if (dev_type_list_match(p2p->cfg->sec_dev_type[i],
+ attr.req_dev_type,
+ attr.num_req_dev_type))
+ return 1; /* Own Secondary Device Type matches */
+
+ /* No matching device type found */
+ return 0;
+}
+
+
+struct wpabuf * p2p_build_probe_resp_ies(struct p2p_data *p2p)
+{
+ struct wpabuf *buf;
+ u8 *len;
+ int pw_id = -1;
+ size_t extra = 0;
+
+#ifdef CONFIG_WIFI_DISPLAY
+ if (p2p->wfd_ie_probe_resp)
+ extra = wpabuf_len(p2p->wfd_ie_probe_resp);
+#endif /* CONFIG_WIFI_DISPLAY */
+
+ buf = wpabuf_alloc(1000 + extra);
+ if (buf == NULL)
+ return NULL;
+
+ if (p2p->go_neg_peer) {
+ /* Advertise immediate availability of WPS credential */
+ pw_id = p2p_wps_method_pw_id(p2p->go_neg_peer->wps_method);
+ }
+
+ p2p_build_wps_ie(p2p, buf, pw_id, 1);
+
+#ifdef CONFIG_WIFI_DISPLAY
+ if (p2p->wfd_ie_probe_resp)
+ wpabuf_put_buf(buf, p2p->wfd_ie_probe_resp);
+#endif /* CONFIG_WIFI_DISPLAY */
+
+ /* P2P IE */
+ len = p2p_buf_add_ie_hdr(buf);
+ p2p_buf_add_capability(buf, p2p->dev_capab &
+ ~P2P_DEV_CAPAB_CLIENT_DISCOVERABILITY, 0);
+ if (p2p->ext_listen_interval)
+ p2p_buf_add_ext_listen_timing(buf, p2p->ext_listen_period,
+ p2p->ext_listen_interval);
+ p2p_buf_add_device_info(buf, p2p, NULL);
+ p2p_buf_update_ie_hdr(buf, len);
+
+ return buf;
+}
+
+
+static int is_11b(u8 rate)
+{
+ return rate == 0x02 || rate == 0x04 || rate == 0x0b || rate == 0x16;
+}
+
+
+static int supp_rates_11b_only(struct ieee802_11_elems *elems)
+{
+ int num_11b = 0, num_others = 0;
+ int i;
+
+ if (elems->supp_rates == NULL && elems->ext_supp_rates == NULL)
+ return 0;
+
+ for (i = 0; elems->supp_rates && i < elems->supp_rates_len; i++) {
+ if (is_11b(elems->supp_rates[i]))
+ num_11b++;
+ else
+ num_others++;
+ }
+
+ for (i = 0; elems->ext_supp_rates && i < elems->ext_supp_rates_len;
+ i++) {
+ if (is_11b(elems->ext_supp_rates[i]))
+ num_11b++;
+ else
+ num_others++;
+ }
+
+ return num_11b > 0 && num_others == 0;
+}
+
+
+static enum p2p_probe_req_status
+p2p_reply_probe(struct p2p_data *p2p, const u8 *addr, const u8 *dst,
+ const u8 *bssid, const u8 *ie, size_t ie_len)
+{
+ struct ieee802_11_elems elems;
+ struct wpabuf *buf;
+ struct ieee80211_mgmt *resp;
+ struct p2p_message msg;
+ struct wpabuf *ies;
+
+ if (!p2p->in_listen || !p2p->drv_in_listen) {
+ /* not in Listen state - ignore Probe Request */
+ return P2P_PREQ_NOT_LISTEN;
+ }
+
+ if (ieee802_11_parse_elems((u8 *) ie, ie_len, &elems, 0) ==
+ ParseFailed) {
+ /* Ignore invalid Probe Request frames */
+ return P2P_PREQ_MALFORMED;
+ }
+
+ if (elems.p2p == NULL) {
+ /* not a P2P probe - ignore it */
+ return P2P_PREQ_NOT_P2P;
+ }
+
+ if (dst && !is_broadcast_ether_addr(dst) &&
+ os_memcmp(dst, p2p->cfg->dev_addr, ETH_ALEN) != 0) {
+ /* Not sent to the broadcast address or our P2P Device Address
+ */
+ return P2P_PREQ_NOT_PROCESSED;
+ }
+
+ if (bssid && !is_broadcast_ether_addr(bssid)) {
+ /* Not sent to the Wildcard BSSID */
+ return P2P_PREQ_NOT_PROCESSED;
+ }
+
+ if (elems.ssid == NULL || elems.ssid_len != P2P_WILDCARD_SSID_LEN ||
+ os_memcmp(elems.ssid, P2P_WILDCARD_SSID, P2P_WILDCARD_SSID_LEN) !=
+ 0) {
+ /* not using P2P Wildcard SSID - ignore */
+ return P2P_PREQ_NOT_PROCESSED;
+ }
+
+ if (supp_rates_11b_only(&elems)) {
+ /* Indicates support for 11b rates only */
+ return P2P_PREQ_NOT_P2P;
+ }
+
+ os_memset(&msg, 0, sizeof(msg));
+ if (p2p_parse_ies(ie, ie_len, &msg) < 0) {
+ /* Could not parse P2P attributes */
+ return P2P_PREQ_NOT_P2P;
+ }
+
+ if (msg.device_id &&
+ os_memcmp(msg.device_id, p2p->cfg->dev_addr, ETH_ALEN) != 0) {
+ /* Device ID did not match */
+ p2p_parse_free(&msg);
+ return P2P_PREQ_NOT_PROCESSED;
+ }
+
+ /* Check Requested Device Type match */
+ if (msg.wps_attributes &&
+ !p2p_match_dev_type(p2p, msg.wps_attributes)) {
+ /* No match with Requested Device Type */
+ p2p_parse_free(&msg);
+ return P2P_PREQ_NOT_PROCESSED;
+ }
+ p2p_parse_free(&msg);
+
+ if (!p2p->cfg->send_probe_resp) {
+ /* Response generated elsewhere */
+ return P2P_PREQ_NOT_PROCESSED;
+ }
+
+ wpa_msg(p2p->cfg->msg_ctx, MSG_DEBUG,
+ "P2P: Reply to P2P Probe Request in Listen state");
+
+ /*
+ * We do not really have a specific BSS that this frame is advertising,
+ * so build a frame that has some information in valid format. This is
+ * really only used for discovery purposes, not to learn exact BSS
+ * parameters.
+ */
+ ies = p2p_build_probe_resp_ies(p2p);
+ if (ies == NULL)
+ return P2P_PREQ_NOT_PROCESSED;
+
+ buf = wpabuf_alloc(200 + wpabuf_len(ies));
+ if (buf == NULL) {
+ wpabuf_free(ies);
+ return P2P_PREQ_NOT_PROCESSED;
+ }
+
+ resp = NULL;
+ resp = wpabuf_put(buf, resp->u.probe_resp.variable - (u8 *) resp);
+
+ resp->frame_control = host_to_le16((WLAN_FC_TYPE_MGMT << 2) |
+ (WLAN_FC_STYPE_PROBE_RESP << 4));
+ os_memcpy(resp->da, addr, ETH_ALEN);
+ os_memcpy(resp->sa, p2p->cfg->dev_addr, ETH_ALEN);
+ os_memcpy(resp->bssid, p2p->cfg->dev_addr, ETH_ALEN);
+ resp->u.probe_resp.beacon_int = host_to_le16(100);
+ /* hardware or low-level driver will setup seq_ctrl and timestamp */
+ resp->u.probe_resp.capab_info =
+ host_to_le16(WLAN_CAPABILITY_SHORT_PREAMBLE |
+ WLAN_CAPABILITY_PRIVACY |
+ WLAN_CAPABILITY_SHORT_SLOT_TIME);
+
+ wpabuf_put_u8(buf, WLAN_EID_SSID);
+ wpabuf_put_u8(buf, P2P_WILDCARD_SSID_LEN);
+ wpabuf_put_data(buf, P2P_WILDCARD_SSID, P2P_WILDCARD_SSID_LEN);
+
+ wpabuf_put_u8(buf, WLAN_EID_SUPP_RATES);
+ wpabuf_put_u8(buf, 8);
+ wpabuf_put_u8(buf, (60 / 5) | 0x80);
+ wpabuf_put_u8(buf, 90 / 5);
+ wpabuf_put_u8(buf, (120 / 5) | 0x80);
+ wpabuf_put_u8(buf, 180 / 5);
+ wpabuf_put_u8(buf, (240 / 5) | 0x80);
+ wpabuf_put_u8(buf, 360 / 5);
+ wpabuf_put_u8(buf, 480 / 5);
+ wpabuf_put_u8(buf, 540 / 5);
+
+ wpabuf_put_u8(buf, WLAN_EID_DS_PARAMS);
+ wpabuf_put_u8(buf, 1);
+ wpabuf_put_u8(buf, p2p->cfg->channel);
+
+ wpabuf_put_buf(buf, ies);
+ wpabuf_free(ies);
+
+ p2p->cfg->send_probe_resp(p2p->cfg->cb_ctx, buf);
+
+ wpabuf_free(buf);
+
+ return P2P_PREQ_NOT_PROCESSED;
+}
+
+
+enum p2p_probe_req_status
+p2p_probe_req_rx(struct p2p_data *p2p, const u8 *addr, const u8 *dst,
+ const u8 *bssid, const u8 *ie, size_t ie_len)
+{
+ enum p2p_probe_req_status res;
+
+ p2p_add_dev_from_probe_req(p2p, addr, ie, ie_len);
+
+ res = p2p_reply_probe(p2p, addr, dst, bssid, ie, ie_len);
+
+ if ((p2p->state == P2P_CONNECT || p2p->state == P2P_CONNECT_LISTEN) &&
+ p2p->go_neg_peer &&
+ os_memcmp(addr, p2p->go_neg_peer->info.p2p_device_addr, ETH_ALEN)
+ == 0) {
+ /* Received a Probe Request from GO Negotiation peer */
+ wpa_msg(p2p->cfg->msg_ctx, MSG_DEBUG,
+ "P2P: Found GO Negotiation peer - try to start GO "
+ "negotiation from timeout");
+ eloop_register_timeout(0, 0, p2p_go_neg_start, p2p, NULL);
+ return P2P_PREQ_PROCESSED;
+ }
+
+ if ((p2p->state == P2P_INVITE || p2p->state == P2P_INVITE_LISTEN) &&
+ p2p->invite_peer &&
+ os_memcmp(addr, p2p->invite_peer->info.p2p_device_addr, ETH_ALEN)
+ == 0) {
+ /* Received a Probe Request from Invite peer */
+ wpa_msg(p2p->cfg->msg_ctx, MSG_DEBUG,
+ "P2P: Found Invite peer - try to start Invite from "
+ "timeout");
+ eloop_register_timeout(0, 0, p2p_invite_start, p2p, NULL);
+ return P2P_PREQ_PROCESSED;
+ }
+
+ return res;
+}
+
+
+static int p2p_assoc_req_ie_wlan_ap(struct p2p_data *p2p, const u8 *bssid,
+ u8 *buf, size_t len, struct wpabuf *p2p_ie)
+{
+ struct wpabuf *tmp;
+ u8 *lpos;
+ size_t tmplen;
+ int res;
+ u8 group_capab;
+
+ if (p2p_ie == NULL)
+ return 0; /* WLAN AP is not a P2P manager */
+
+ /*
+ * (Re)Association Request - P2P IE
+ * P2P Capability attribute (shall be present)
+ * P2P Interface attribute (present if concurrent device and
+ * P2P Management is enabled)
+ */
+ tmp = wpabuf_alloc(200);
+ if (tmp == NULL)
+ return -1;
+
+ lpos = p2p_buf_add_ie_hdr(tmp);
+ group_capab = 0;
+ if (p2p->num_groups > 0) {
+ group_capab |= P2P_GROUP_CAPAB_GROUP_OWNER;
+ if ((p2p->dev_capab & P2P_DEV_CAPAB_CONCURRENT_OPER) &&
+ (p2p->dev_capab & P2P_DEV_CAPAB_INFRA_MANAGED) &&
+ p2p->cross_connect)
+ group_capab |= P2P_GROUP_CAPAB_CROSS_CONN;
+ }
+ p2p_buf_add_capability(tmp, p2p->dev_capab, group_capab);
+ if ((p2p->dev_capab & P2P_DEV_CAPAB_CONCURRENT_OPER) &&
+ (p2p->dev_capab & P2P_DEV_CAPAB_INFRA_MANAGED))
+ p2p_buf_add_p2p_interface(tmp, p2p);
+ p2p_buf_update_ie_hdr(tmp, lpos);
+
+ tmplen = wpabuf_len(tmp);
+ if (tmplen > len)
+ res = -1;
+ else {
+ os_memcpy(buf, wpabuf_head(tmp), tmplen);
+ res = tmplen;
+ }
+ wpabuf_free(tmp);
+
+ return res;
+}
+
+
+int p2p_assoc_req_ie(struct p2p_data *p2p, const u8 *bssid, u8 *buf,
+ size_t len, int p2p_group, struct wpabuf *p2p_ie)
+{
+ struct wpabuf *tmp;
+ u8 *lpos;
+ struct p2p_device *peer;
+ size_t tmplen;
+ int res;
+ size_t extra = 0;
+
+ if (!p2p_group)
+ return p2p_assoc_req_ie_wlan_ap(p2p, bssid, buf, len, p2p_ie);
+
+#ifdef CONFIG_WIFI_DISPLAY
+ if (p2p->wfd_ie_assoc_req)
+ extra = wpabuf_len(p2p->wfd_ie_assoc_req);
+#endif /* CONFIG_WIFI_DISPLAY */
+
+ /*
+ * (Re)Association Request - P2P IE
+ * P2P Capability attribute (shall be present)
+ * Extended Listen Timing (may be present)
+ * P2P Device Info attribute (shall be present)
+ */
+ tmp = wpabuf_alloc(200 + extra);
+ if (tmp == NULL)
+ return -1;
+
+#ifdef CONFIG_WIFI_DISPLAY
+ if (p2p->wfd_ie_assoc_req)
+ wpabuf_put_buf(tmp, p2p->wfd_ie_assoc_req);
+#endif /* CONFIG_WIFI_DISPLAY */
+
+ peer = bssid ? p2p_get_device(p2p, bssid) : NULL;
+
+ lpos = p2p_buf_add_ie_hdr(tmp);
+ p2p_buf_add_capability(tmp, p2p->dev_capab, 0);
+ if (p2p->ext_listen_interval)
+ p2p_buf_add_ext_listen_timing(tmp, p2p->ext_listen_period,
+ p2p->ext_listen_interval);
+ p2p_buf_add_device_info(tmp, p2p, peer);
+ p2p_buf_update_ie_hdr(tmp, lpos);
+
+ tmplen = wpabuf_len(tmp);
+ if (tmplen > len)
+ res = -1;
+ else {
+ os_memcpy(buf, wpabuf_head(tmp), tmplen);
+ res = tmplen;
+ }
+ wpabuf_free(tmp);
+
+ return res;
+}
+
+
+int p2p_scan_result_text(const u8 *ies, size_t ies_len, char *buf, char *end)
+{
+ struct wpabuf *p2p_ie;
+ int ret;
+
+ p2p_ie = ieee802_11_vendor_ie_concat(ies, ies_len, P2P_IE_VENDOR_TYPE);
+ if (p2p_ie == NULL)
+ return 0;
+
+ ret = p2p_attr_text(p2p_ie, buf, end);
+ wpabuf_free(p2p_ie);
+ return ret;
+}
+
+
+int p2p_parse_dev_addr_in_p2p_ie(struct wpabuf *p2p_ie, u8 *dev_addr)
+{
+ struct p2p_message msg;
+
+ os_memset(&msg, 0, sizeof(msg));
+ if (p2p_parse_p2p_ie(p2p_ie, &msg))
+ return -1;
+
+ if (msg.p2p_device_addr) {
+ os_memcpy(dev_addr, msg.p2p_device_addr, ETH_ALEN);
+ return 0;
+ } else if (msg.device_id) {
+ os_memcpy(dev_addr, msg.device_id, ETH_ALEN);
+ return 0;
+ }
+ return -1;
+}
+
+
+int p2p_parse_dev_addr(const u8 *ies, size_t ies_len, u8 *dev_addr)
+{
+ struct wpabuf *p2p_ie;
+ int ret;
+
+ p2p_ie = ieee802_11_vendor_ie_concat(ies, ies_len,
+ P2P_IE_VENDOR_TYPE);
+ if (p2p_ie == NULL)
+ return -1;
+ ret = p2p_parse_dev_addr_in_p2p_ie(p2p_ie, dev_addr);
+ wpabuf_free(p2p_ie);
+ return ret;
+}
+
+
+static void p2p_clear_go_neg(struct p2p_data *p2p)
+{
+ p2p->go_neg_peer = NULL;
+ p2p_clear_timeout(p2p);
+ p2p_set_state(p2p, P2P_IDLE);
+}
+
+
+void p2p_wps_success_cb(struct p2p_data *p2p, const u8 *mac_addr)
+{
+ if (p2p->go_neg_peer == NULL) {
+ wpa_msg(p2p->cfg->msg_ctx, MSG_DEBUG,
+ "P2P: No pending Group Formation - "
+ "ignore WPS registration success notification");
+ return; /* No pending Group Formation */
+ }
+
+ if (os_memcmp(mac_addr, p2p->go_neg_peer->intended_addr, ETH_ALEN) !=
+ 0) {
+ wpa_msg(p2p->cfg->msg_ctx, MSG_DEBUG,
+ "P2P: Ignore WPS registration success notification "
+ "for " MACSTR " (GO Negotiation peer " MACSTR ")",
+ MAC2STR(mac_addr),
+ MAC2STR(p2p->go_neg_peer->intended_addr));
+ return; /* Ignore unexpected peer address */
+ }
+
+ wpa_msg(p2p->cfg->msg_ctx, MSG_DEBUG,
+ "P2P: Group Formation completed successfully with " MACSTR,
+ MAC2STR(mac_addr));
+
+ p2p_clear_go_neg(p2p);
+}
+
+
+void p2p_group_formation_failed(struct p2p_data *p2p)
+{
+ if (p2p->go_neg_peer == NULL) {
+ wpa_msg(p2p->cfg->msg_ctx, MSG_DEBUG,
+ "P2P: No pending Group Formation - "
+ "ignore group formation failure notification");
+ return; /* No pending Group Formation */
+ }
+
+ wpa_msg(p2p->cfg->msg_ctx, MSG_DEBUG,
+ "P2P: Group Formation failed with " MACSTR,
+ MAC2STR(p2p->go_neg_peer->intended_addr));
+
+ p2p_clear_go_neg(p2p);
+}
+
+
+struct p2p_data * p2p_init(const struct p2p_config *cfg)
+{
+ struct p2p_data *p2p;
+
+ if (cfg->max_peers < 1)
+ return NULL;
+
+ p2p = os_zalloc(sizeof(*p2p) + sizeof(*cfg));
+ if (p2p == NULL)
+ return NULL;
+ p2p->cfg = (struct p2p_config *) (p2p + 1);
+ os_memcpy(p2p->cfg, cfg, sizeof(*cfg));
+ if (cfg->dev_name)
+ p2p->cfg->dev_name = os_strdup(cfg->dev_name);
+ if (cfg->manufacturer)
+ p2p->cfg->manufacturer = os_strdup(cfg->manufacturer);
+ if (cfg->model_name)
+ p2p->cfg->model_name = os_strdup(cfg->model_name);
+ if (cfg->model_number)
+ p2p->cfg->model_number = os_strdup(cfg->model_number);
+ if (cfg->serial_number)
+ p2p->cfg->serial_number = os_strdup(cfg->serial_number);
+ if (cfg->pref_chan) {
+ p2p->cfg->pref_chan = os_malloc(cfg->num_pref_chan *
+ sizeof(struct p2p_channel));
+ if (p2p->cfg->pref_chan) {
+ os_memcpy(p2p->cfg->pref_chan, cfg->pref_chan,
+ cfg->num_pref_chan *
+ sizeof(struct p2p_channel));
+ } else
+ p2p->cfg->num_pref_chan = 0;
+ }
+
+ p2p->min_disc_int = 1;
+ p2p->max_disc_int = 3;
+ p2p->max_disc_tu = -1;
+
+ os_get_random(&p2p->next_tie_breaker, 1);
+ p2p->next_tie_breaker &= 0x01;
+ if (cfg->sd_request)
+ p2p->dev_capab |= P2P_DEV_CAPAB_SERVICE_DISCOVERY;
+ p2p->dev_capab |= P2P_DEV_CAPAB_INVITATION_PROCEDURE;
+ if (cfg->concurrent_operations)
+ p2p->dev_capab |= P2P_DEV_CAPAB_CONCURRENT_OPER;
+ p2p->dev_capab |= P2P_DEV_CAPAB_CLIENT_DISCOVERABILITY;
+
+ dl_list_init(&p2p->devices);
+
+ eloop_register_timeout(P2P_PEER_EXPIRATION_INTERVAL, 0,
+ p2p_expiration_timeout, p2p, NULL);
+
+ p2p->go_timeout = 100;
+ p2p->client_timeout = 20;
+
+ return p2p;
+}
+
+
+void p2p_deinit(struct p2p_data *p2p)
+{
+#ifdef CONFIG_WIFI_DISPLAY
+ wpabuf_free(p2p->wfd_ie_beacon);
+ wpabuf_free(p2p->wfd_ie_probe_req);
+ wpabuf_free(p2p->wfd_ie_probe_resp);
+ wpabuf_free(p2p->wfd_ie_assoc_req);
+ wpabuf_free(p2p->wfd_ie_invitation);
+ wpabuf_free(p2p->wfd_ie_prov_disc_req);
+ wpabuf_free(p2p->wfd_ie_prov_disc_resp);
+ wpabuf_free(p2p->wfd_ie_go_neg);
+ wpabuf_free(p2p->wfd_dev_info);
+ wpabuf_free(p2p->wfd_assoc_bssid);
+ wpabuf_free(p2p->wfd_coupled_sink_info);
+#endif /* CONFIG_WIFI_DISPLAY */
+
+ eloop_cancel_timeout(p2p_expiration_timeout, p2p, NULL);
+ eloop_cancel_timeout(p2p_ext_listen_timeout, p2p, NULL);
+ eloop_cancel_timeout(p2p_scan_timeout, p2p, NULL);
+ p2p_flush(p2p);
+ p2p_free_req_dev_types(p2p);
+ os_free(p2p->cfg->dev_name);
+ os_free(p2p->cfg->manufacturer);
+ os_free(p2p->cfg->model_name);
+ os_free(p2p->cfg->model_number);
+ os_free(p2p->cfg->serial_number);
+ os_free(p2p->cfg->pref_chan);
+ os_free(p2p->groups);
+ wpabuf_free(p2p->sd_resp);
+ os_free(p2p->after_scan_tx);
+ p2p_remove_wps_vendor_extensions(p2p);
+ os_free(p2p);
+}
+
+
+void p2p_flush(struct p2p_data *p2p)
+{
+ struct p2p_device *dev, *prev;
+ p2p_stop_find(p2p);
+ dl_list_for_each_safe(dev, prev, &p2p->devices, struct p2p_device,
+ list) {
+ dl_list_del(&dev->list);
+ p2p_device_free(p2p, dev);
+ }
+ p2p_free_sd_queries(p2p);
+ os_free(p2p->after_scan_tx);
+ p2p->after_scan_tx = NULL;
+}
+
+
+int p2p_unauthorize(struct p2p_data *p2p, const u8 *addr)
+{
+ struct p2p_device *dev;
+
+ dev = p2p_get_device(p2p, addr);
+ if (dev == NULL)
+ return -1;
+
+ wpa_msg(p2p->cfg->msg_ctx, MSG_DEBUG, "P2P: Unauthorizing " MACSTR,
+ MAC2STR(addr));
+
+ if (p2p->go_neg_peer == dev)
+ p2p->go_neg_peer = NULL;
+
+ dev->wps_method = WPS_NOT_READY;
+ dev->flags &= ~P2P_DEV_WAIT_GO_NEG_RESPONSE;
+ dev->flags &= ~P2P_DEV_WAIT_GO_NEG_CONFIRM;
+
+ /* Check if after_scan_tx is for this peer. If so free it */
+ if (p2p->after_scan_tx &&
+ os_memcmp(addr, p2p->after_scan_tx->dst, ETH_ALEN) == 0) {
+ os_free(p2p->after_scan_tx);
+ p2p->after_scan_tx = NULL;
+ }
+
+ return 0;
+}
+
+
+int p2p_set_dev_name(struct p2p_data *p2p, const char *dev_name)
+{
+ os_free(p2p->cfg->dev_name);
+ if (dev_name) {
+ p2p->cfg->dev_name = os_strdup(dev_name);
+ if (p2p->cfg->dev_name == NULL)
+ return -1;
+ } else
+ p2p->cfg->dev_name = NULL;
+ return 0;
+}
+
+
+int p2p_set_manufacturer(struct p2p_data *p2p, const char *manufacturer)
+{
+ os_free(p2p->cfg->manufacturer);
+ p2p->cfg->manufacturer = NULL;
+ if (manufacturer) {
+ p2p->cfg->manufacturer = os_strdup(manufacturer);
+ if (p2p->cfg->manufacturer == NULL)
+ return -1;
+ }
+
+ return 0;
+}
+
+
+int p2p_set_model_name(struct p2p_data *p2p, const char *model_name)
+{
+ os_free(p2p->cfg->model_name);
+ p2p->cfg->model_name = NULL;
+ if (model_name) {
+ p2p->cfg->model_name = os_strdup(model_name);
+ if (p2p->cfg->model_name == NULL)
+ return -1;
+ }
+
+ return 0;
+}
+
+
+int p2p_set_model_number(struct p2p_data *p2p, const char *model_number)
+{
+ os_free(p2p->cfg->model_number);
+ p2p->cfg->model_number = NULL;
+ if (model_number) {
+ p2p->cfg->model_number = os_strdup(model_number);
+ if (p2p->cfg->model_number == NULL)
+ return -1;
+ }
+
+ return 0;
+}
+
+
+int p2p_set_serial_number(struct p2p_data *p2p, const char *serial_number)
+{
+ os_free(p2p->cfg->serial_number);
+ p2p->cfg->serial_number = NULL;
+ if (serial_number) {
+ p2p->cfg->serial_number = os_strdup(serial_number);
+ if (p2p->cfg->serial_number == NULL)
+ return -1;
+ }
+
+ return 0;
+}
+
+
+void p2p_set_config_methods(struct p2p_data *p2p, u16 config_methods)
+{
+ p2p->cfg->config_methods = config_methods;
+}
+
+
+void p2p_set_uuid(struct p2p_data *p2p, const u8 *uuid)
+{
+ os_memcpy(p2p->cfg->uuid, uuid, 16);
+}
+
+
+int p2p_set_pri_dev_type(struct p2p_data *p2p, const u8 *pri_dev_type)
+{
+ os_memcpy(p2p->cfg->pri_dev_type, pri_dev_type, 8);
+ return 0;
+}
+
+
+int p2p_set_sec_dev_types(struct p2p_data *p2p, const u8 dev_types[][8],
+ size_t num_dev_types)
+{
+ if (num_dev_types > P2P_SEC_DEVICE_TYPES)
+ num_dev_types = P2P_SEC_DEVICE_TYPES;
+ p2p->cfg->num_sec_dev_types = num_dev_types;
+ os_memcpy(p2p->cfg->sec_dev_type, dev_types, num_dev_types * 8);
+ return 0;
+}
+
+
+void p2p_remove_wps_vendor_extensions(struct p2p_data *p2p)
+{
+ int i;
+
+ for (i = 0; i < P2P_MAX_WPS_VENDOR_EXT; i++) {
+ wpabuf_free(p2p->wps_vendor_ext[i]);
+ p2p->wps_vendor_ext[i] = NULL;
+ }
+}
+
+
+int p2p_add_wps_vendor_extension(struct p2p_data *p2p,
+ const struct wpabuf *vendor_ext)
+{
+ int i;
+
+ if (vendor_ext == NULL)
+ return -1;
+
+ for (i = 0; i < P2P_MAX_WPS_VENDOR_EXT; i++) {
+ if (p2p->wps_vendor_ext[i] == NULL)
+ break;
+ }
+ if (i >= P2P_MAX_WPS_VENDOR_EXT)
+ return -1;
+
+ p2p->wps_vendor_ext[i] = wpabuf_dup(vendor_ext);
+ if (p2p->wps_vendor_ext[i] == NULL)
+ return -1;
+
+ return 0;
+}
+
+
+int p2p_set_country(struct p2p_data *p2p, const char *country)
+{
+ os_memcpy(p2p->cfg->country, country, 3);
+ return 0;
+}
+
+
+void p2p_continue_find(struct p2p_data *p2p)
+{
+ struct p2p_device *dev;
+ p2p_set_state(p2p, P2P_SEARCH);
+ dl_list_for_each(dev, &p2p->devices, struct p2p_device, list) {
+ if (dev->flags & P2P_DEV_SD_SCHEDULE) {
+ if (p2p_start_sd(p2p, dev) == 0)
+ return;
+ else
+ break;
+ } else if (dev->req_config_methods &&
+ !(dev->flags & P2P_DEV_PD_FOR_JOIN)) {
+ wpa_msg(p2p->cfg->msg_ctx, MSG_DEBUG, "P2P: Send "
+ "pending Provision Discovery Request to "
+ MACSTR " (config methods 0x%x)",
+ MAC2STR(dev->info.p2p_device_addr),
+ dev->req_config_methods);
+ if (p2p_send_prov_disc_req(p2p, dev, 0, 0) == 0)
+ return;
+ }
+ }
+
+ p2p_listen_in_find(p2p, 1);
+}
+
+
+static void p2p_sd_cb(struct p2p_data *p2p, int success)
+{
+ wpa_msg(p2p->cfg->msg_ctx, MSG_DEBUG,
+ "P2P: Service Discovery Query TX callback: success=%d",
+ success);
+ p2p->pending_action_state = P2P_NO_PENDING_ACTION;
+
+ if (!success) {
+ if (p2p->sd_peer) {
+ p2p->sd_peer->flags &= ~P2P_DEV_SD_SCHEDULE;
+ p2p->sd_peer = NULL;
+ }
+ p2p_continue_find(p2p);
+ return;
+ }
+
+ if (p2p->sd_peer == NULL) {
+ wpa_msg(p2p->cfg->msg_ctx, MSG_DEBUG,
+ "P2P: No SD peer entry known");
+ p2p_continue_find(p2p);
+ return;
+ }
+
+ /* Wait for response from the peer */
+ p2p_set_state(p2p, P2P_SD_DURING_FIND);
+ p2p_set_timeout(p2p, 0, 200000);
+}
+
+
+/**
+ * p2p_retry_pd - Retry any pending provision disc requests in IDLE state
+ * @p2p: P2P module context from p2p_init()
+ */
+static void p2p_retry_pd(struct p2p_data *p2p)
+{
+ struct p2p_device *dev;
+
+ if (p2p->state != P2P_IDLE)
+ return;
+
+ /*
+ * Retry the prov disc req attempt only for the peer that the user had
+ * requested.
+ */
+
+ dl_list_for_each(dev, &p2p->devices, struct p2p_device, list) {
+ if (os_memcmp(p2p->pending_pd_devaddr,
+ dev->info.p2p_device_addr, ETH_ALEN) != 0)
+ continue;
+ if (!dev->req_config_methods)
+ continue;
+
+ wpa_msg(p2p->cfg->msg_ctx, MSG_DEBUG, "P2P: Send "
+ "pending Provision Discovery Request to "
+ MACSTR " (config methods 0x%x)",
+ MAC2STR(dev->info.p2p_device_addr),
+ dev->req_config_methods);
+ p2p_send_prov_disc_req(p2p, dev,
+ dev->flags & P2P_DEV_PD_FOR_JOIN, 0);
+ return;
+ }
+}
+
+
+static void p2p_prov_disc_cb(struct p2p_data *p2p, int success)
+{
+ wpa_msg(p2p->cfg->msg_ctx, MSG_DEBUG,
+ "P2P: Provision Discovery Request TX callback: success=%d",
+ success);
+
+ /*
+ * Postpone resetting the pending action state till after we actually
+ * time out. This allows us to take some action like notifying any
+ * interested parties about no response to the request.
+ *
+ * When the timer (below) goes off we check in IDLE, SEARCH, or
+ * LISTEN_ONLY state, which are the only allowed states to issue a PD
+ * requests in, if this was still pending and then raise notification.
+ */
+
+ if (!success) {
+ p2p->pending_action_state = P2P_NO_PENDING_ACTION;
+
+ if (p2p->user_initiated_pd &&
+ (p2p->state == P2P_SEARCH || p2p->state == P2P_LISTEN_ONLY))
+ {
+ /* Retry request from timeout to avoid busy loops */
+ p2p->pending_action_state = P2P_PENDING_PD;
+ p2p_set_timeout(p2p, 0, 50000);
+ } else if (p2p->state != P2P_IDLE)
+ p2p_continue_find(p2p);
+ else if (p2p->user_initiated_pd) {
+ p2p->pending_action_state = P2P_PENDING_PD;
+ p2p_set_timeout(p2p, 0, 300000);
+ }
+ return;
+ }
+
+ /*
+ * This postponing, of resetting pending_action_state, needs to be
+ * done only for user initiated PD requests and not internal ones.
+ */
+ if (p2p->user_initiated_pd)
+ p2p->pending_action_state = P2P_PENDING_PD;
+ else
+ p2p->pending_action_state = P2P_NO_PENDING_ACTION;
+
+ /* Wait for response from the peer */
+ if (p2p->state == P2P_SEARCH)
+ p2p_set_state(p2p, P2P_PD_DURING_FIND);
+ p2p_set_timeout(p2p, 0, 200000);
+}
+
+
+int p2p_scan_res_handler(struct p2p_data *p2p, const u8 *bssid, int freq,
+ unsigned int age, int level, const u8 *ies,
+ size_t ies_len)
+{
+ p2p_add_device(p2p, bssid, freq, age, level, ies, ies_len, 1);
+
+ return 0;
+}
+
+
+void p2p_scan_res_handled(struct p2p_data *p2p)
+{
+ if (!p2p->p2p_scan_running) {
+ wpa_msg(p2p->cfg->msg_ctx, MSG_DEBUG, "P2P: p2p_scan was not "
+ "running, but scan results received");
+ }
+ p2p->p2p_scan_running = 0;
+ eloop_cancel_timeout(p2p_scan_timeout, p2p, NULL);
+
+ if (p2p_run_after_scan(p2p))
+ return;
+ if (p2p->state == P2P_SEARCH)
+ p2p_continue_find(p2p);
+}
+
+
+void p2p_scan_ie(struct p2p_data *p2p, struct wpabuf *ies, const u8 *dev_id)
+{
+ u8 *len;
+
+#ifdef CONFIG_WIFI_DISPLAY
+ if (p2p->wfd_ie_probe_req)
+ wpabuf_put_buf(ies, p2p->wfd_ie_probe_req);
+#endif /* CONFIG_WIFI_DISPLAY */
+
+ len = p2p_buf_add_ie_hdr(ies);
+ p2p_buf_add_capability(ies, p2p->dev_capab &
+ ~P2P_DEV_CAPAB_CLIENT_DISCOVERABILITY, 0);
+ if (dev_id)
+ p2p_buf_add_device_id(ies, dev_id);
+ if (p2p->cfg->reg_class && p2p->cfg->channel)
+ p2p_buf_add_listen_channel(ies, p2p->cfg->country,
+ p2p->cfg->reg_class,
+ p2p->cfg->channel);
+ if (p2p->ext_listen_interval)
+ p2p_buf_add_ext_listen_timing(ies, p2p->ext_listen_period,
+ p2p->ext_listen_interval);
+ /* TODO: p2p_buf_add_operating_channel() if GO */
+ p2p_buf_update_ie_hdr(ies, len);
+}
+
+
+size_t p2p_scan_ie_buf_len(struct p2p_data *p2p)
+{
+ size_t len = 100;
+
+#ifdef CONFIG_WIFI_DISPLAY
+ if (p2p && p2p->wfd_ie_probe_req)
+ len += wpabuf_len(p2p->wfd_ie_probe_req);
+#endif /* CONFIG_WIFI_DISPLAY */
+
+ return len;
+}
+
+
+int p2p_ie_text(struct wpabuf *p2p_ie, char *buf, char *end)
+{
+ return p2p_attr_text(p2p_ie, buf, end);
+}
+
+
+static void p2p_go_neg_req_cb(struct p2p_data *p2p, int success)
+{
+ struct p2p_device *dev = p2p->go_neg_peer;
+
+ wpa_msg(p2p->cfg->msg_ctx, MSG_DEBUG,
+ "P2P: GO Negotiation Request TX callback: success=%d",
+ success);
+
+ if (dev == NULL) {
+ wpa_msg(p2p->cfg->msg_ctx, MSG_DEBUG,
+ "P2P: No pending GO Negotiation");
+ return;
+ }
+
+ if (success) {
+ if (dev->flags & P2P_DEV_USER_REJECTED) {
+ p2p_set_state(p2p, P2P_IDLE);
+ return;
+ }
+ } else if (dev->go_neg_req_sent) {
+ /* Cancel the increment from p2p_connect_send() on failure */
+ dev->go_neg_req_sent--;
+ }
+
+ if (!success &&
+ (dev->info.dev_capab & P2P_DEV_CAPAB_CLIENT_DISCOVERABILITY) &&
+ !is_zero_ether_addr(dev->member_in_go_dev)) {
+ wpa_msg(p2p->cfg->msg_ctx, MSG_DEBUG,
+ "P2P: Peer " MACSTR " did not acknowledge request - "
+ "try to use device discoverability through its GO",
+ MAC2STR(dev->info.p2p_device_addr));
+ p2p->cfg->send_action_done(p2p->cfg->cb_ctx);
+ p2p_send_dev_disc_req(p2p, dev);
+ return;
+ }
+
+ /*
+ * Use P2P find, if needed, to find the other device from its listen
+ * channel.
+ */
+ p2p_set_state(p2p, P2P_CONNECT);
+ p2p_set_timeout(p2p, 0, success ? 200000 : 100000);
+}
+
+
+static void p2p_go_neg_resp_cb(struct p2p_data *p2p, int success)
+{
+ wpa_msg(p2p->cfg->msg_ctx, MSG_DEBUG,
+ "P2P: GO Negotiation Response TX callback: success=%d",
+ success);
+ if (!p2p->go_neg_peer && p2p->state == P2P_PROVISIONING) {
+ wpa_msg(p2p->cfg->msg_ctx, MSG_DEBUG,
+ "P2P: Ignore TX callback event - GO Negotiation is "
+ "not running anymore");
+ return;
+ }
+ p2p_set_state(p2p, P2P_CONNECT);
+ p2p_set_timeout(p2p, 0, 250000);
+}
+
+
+static void p2p_go_neg_resp_failure_cb(struct p2p_data *p2p, int success)
+{
+ wpa_msg(p2p->cfg->msg_ctx, MSG_DEBUG,
+ "P2P: GO Negotiation Response (failure) TX callback: "
+ "success=%d", success);
+ if (p2p->go_neg_peer && p2p->go_neg_peer->status != P2P_SC_SUCCESS) {
+ p2p_go_neg_failed(p2p, p2p->go_neg_peer,
+ p2p->go_neg_peer->status);
+ }
+}
+
+
+static void p2p_go_neg_conf_cb(struct p2p_data *p2p,
+ enum p2p_send_action_result result)
+{
+ struct p2p_device *dev;
+
+ wpa_msg(p2p->cfg->msg_ctx, MSG_DEBUG,
+ "P2P: GO Negotiation Confirm TX callback: result=%d",
+ result);
+ p2p->cfg->send_action_done(p2p->cfg->cb_ctx);
+ if (result == P2P_SEND_ACTION_FAILED) {
+ p2p_go_neg_failed(p2p, p2p->go_neg_peer, -1);
+ return;
+ }
+ if (result == P2P_SEND_ACTION_NO_ACK) {
+ /*
+ * It looks like the TX status for GO Negotiation Confirm is
+ * often showing failure even when the peer has actually
+ * received the frame. Since the peer may change channels
+ * immediately after having received the frame, we may not see
+ * an Ack for retries, so just dropping a single frame may
+ * trigger this. To allow the group formation to succeed if the
+ * peer did indeed receive the frame, continue regardless of
+ * the TX status.
+ */
+ wpa_msg(p2p->cfg->msg_ctx, MSG_DEBUG,
+ "P2P: Assume GO Negotiation Confirm TX was actually "
+ "received by the peer even though Ack was not "
+ "reported");
+ }
+
+ dev = p2p->go_neg_peer;
+ if (dev == NULL)
+ return;
+
+ p2p_go_complete(p2p, dev);
+}
+
+
+void p2p_send_action_cb(struct p2p_data *p2p, unsigned int freq, const u8 *dst,
+ const u8 *src, const u8 *bssid,
+ enum p2p_send_action_result result)
+{
+ enum p2p_pending_action_state state;
+ int success;
+
+ wpa_msg(p2p->cfg->msg_ctx, MSG_DEBUG,
+ "P2P: Action frame TX callback (state=%d freq=%u dst=" MACSTR
+ " src=" MACSTR " bssid=" MACSTR " result=%d",
+ p2p->pending_action_state, freq, MAC2STR(dst), MAC2STR(src),
+ MAC2STR(bssid), result);
+ success = result == P2P_SEND_ACTION_SUCCESS;
+ state = p2p->pending_action_state;
+ p2p->pending_action_state = P2P_NO_PENDING_ACTION;
+ switch (state) {
+ case P2P_NO_PENDING_ACTION:
+ break;
+ case P2P_PENDING_GO_NEG_REQUEST:
+ p2p_go_neg_req_cb(p2p, success);
+ break;
+ case P2P_PENDING_GO_NEG_RESPONSE:
+ p2p_go_neg_resp_cb(p2p, success);
+ break;
+ case P2P_PENDING_GO_NEG_RESPONSE_FAILURE:
+ p2p_go_neg_resp_failure_cb(p2p, success);
+ break;
+ case P2P_PENDING_GO_NEG_CONFIRM:
+ p2p_go_neg_conf_cb(p2p, result);
+ break;
+ case P2P_PENDING_SD:
+ p2p_sd_cb(p2p, success);
+ break;
+ case P2P_PENDING_PD:
+ p2p_prov_disc_cb(p2p, success);
+ break;
+ case P2P_PENDING_INVITATION_REQUEST:
+ p2p_invitation_req_cb(p2p, success);
+ break;
+ case P2P_PENDING_INVITATION_RESPONSE:
+ p2p_invitation_resp_cb(p2p, success);
+ break;
+ case P2P_PENDING_DEV_DISC_REQUEST:
+ p2p_dev_disc_req_cb(p2p, success);
+ break;
+ case P2P_PENDING_DEV_DISC_RESPONSE:
+ p2p_dev_disc_resp_cb(p2p, success);
+ break;
+ case P2P_PENDING_GO_DISC_REQ:
+ p2p_go_disc_req_cb(p2p, success);
+ break;
+ }
+}
+
+
+void p2p_listen_cb(struct p2p_data *p2p, unsigned int freq,
+ unsigned int duration)
+{
+ if (freq == p2p->pending_client_disc_freq) {
+ wpa_msg(p2p->cfg->msg_ctx, MSG_DEBUG,
+ "P2P: Client discoverability remain-awake completed");
+ p2p->pending_client_disc_freq = 0;
+ return;
+ }
+
+ if (freq != p2p->pending_listen_freq) {
+ wpa_msg(p2p->cfg->msg_ctx, MSG_DEBUG,
+ "P2P: Unexpected listen callback for freq=%u "
+ "duration=%u (pending_listen_freq=%u)",
+ freq, duration, p2p->pending_listen_freq);
+ return;
+ }
+
+ wpa_msg(p2p->cfg->msg_ctx, MSG_DEBUG,
+ "P2P: Starting Listen timeout(%u,%u) on freq=%u based on "
+ "callback",
+ p2p->pending_listen_sec, p2p->pending_listen_usec,
+ p2p->pending_listen_freq);
+ p2p->in_listen = 1;
+ p2p->drv_in_listen = freq;
+ if (p2p->pending_listen_sec || p2p->pending_listen_usec) {
+ /*
+ * Add 20 msec extra wait to avoid race condition with driver
+ * remain-on-channel end event, i.e., give driver more time to
+ * complete the operation before our timeout expires.
+ */
+ p2p_set_timeout(p2p, p2p->pending_listen_sec,
+ p2p->pending_listen_usec + 20000);
+ }
+
+ p2p->pending_listen_freq = 0;
+}
+
+
+int p2p_listen_end(struct p2p_data *p2p, unsigned int freq)
+{
+ wpa_msg(p2p->cfg->msg_ctx, MSG_DEBUG, "P2P: Driver ended Listen "
+ "state (freq=%u)", freq);
+ p2p->drv_in_listen = 0;
+ if (p2p->in_listen)
+ return 0; /* Internal timeout will trigger the next step */
+
+ if (p2p->state == P2P_CONNECT_LISTEN && p2p->go_neg_peer) {
+ if (p2p->go_neg_peer->connect_reqs >= 120) {
+ wpa_msg(p2p->cfg->msg_ctx, MSG_DEBUG,
+ "P2P: Timeout on sending GO Negotiation "
+ "Request without getting response");
+ p2p_go_neg_failed(p2p, p2p->go_neg_peer, -1);
+ return 0;
+ }
+
+ p2p_set_state(p2p, P2P_CONNECT);
+ p2p_connect_send(p2p, p2p->go_neg_peer);
+ return 1;
+ } else if (p2p->state == P2P_SEARCH) {
+ if (p2p->p2p_scan_running) {
+ /*
+ * Search is already in progress. This can happen if
+ * an Action frame RX is reported immediately after
+ * the end of a remain-on-channel operation and the
+ * response frame to that is sent using an offchannel
+ * operation while in p2p_find. Avoid an attempt to
+ * restart a scan here.
+ */
+ wpa_msg(p2p->cfg->msg_ctx, MSG_DEBUG, "P2P: p2p_scan "
+ "already in progress - do not try to start a "
+ "new one");
+ return 1;
+ }
+ if (p2p->pending_listen_freq) {
+ /*
+ * Better wait a bit if the driver is unable to start
+ * offchannel operation for some reason. p2p_search()
+ * will be started from internal timeout.
+ */
+ wpa_msg(p2p->cfg->msg_ctx, MSG_DEBUG, "P2P: Listen "
+ "operation did not seem to start - delay "
+ "search phase to avoid busy loop");
+ p2p_set_timeout(p2p, 0, 100000);
+ return 1;
+ }
+ if (p2p->search_delay) {
+ wpa_msg(p2p->cfg->msg_ctx, MSG_DEBUG, "P2P: Delay "
+ "search operation by %u ms",
+ p2p->search_delay);
+ p2p_set_timeout(p2p, p2p->search_delay / 1000,
+ (p2p->search_delay % 1000) * 1000);
+ return 1;
+ }
+ p2p_search(p2p);
+ return 1;
+ }
+
+ return 0;
+}
+
+
+static void p2p_timeout_connect(struct p2p_data *p2p)
+{
+ p2p->cfg->send_action_done(p2p->cfg->cb_ctx);
+ if (p2p->go_neg_peer &&
+ (p2p->go_neg_peer->flags & P2P_DEV_WAIT_GO_NEG_CONFIRM)) {
+ wpa_msg(p2p->cfg->msg_ctx, MSG_DEBUG, "P2P: Wait for GO "
+ "Negotiation Confirm timed out - assume GO "
+ "Negotiation failed");
+ p2p_go_neg_failed(p2p, p2p->go_neg_peer, -1);
+ return;
+ }
+ p2p_set_state(p2p, P2P_CONNECT_LISTEN);
+ p2p_listen_in_find(p2p, 0);
+}
+
+
+static void p2p_timeout_connect_listen(struct p2p_data *p2p)
+{
+ if (p2p->go_neg_peer) {
+ if (p2p->drv_in_listen) {
+ wpa_msg(p2p->cfg->msg_ctx, MSG_DEBUG, "P2P: Driver is "
+ "still in Listen state; wait for it to "
+ "complete");
+ return;
+ }
+
+ if (p2p->go_neg_peer->connect_reqs >= 120) {
+ wpa_msg(p2p->cfg->msg_ctx, MSG_DEBUG,
+ "P2P: Timeout on sending GO Negotiation "
+ "Request without getting response");
+ p2p_go_neg_failed(p2p, p2p->go_neg_peer, -1);
+ return;
+ }
+
+ p2p_set_state(p2p, P2P_CONNECT);
+ p2p_connect_send(p2p, p2p->go_neg_peer);
+ } else
+ p2p_set_state(p2p, P2P_IDLE);
+}
+
+
+static void p2p_timeout_wait_peer_connect(struct p2p_data *p2p)
+{
+ /*
+ * TODO: could remain constantly in Listen state for some time if there
+ * are no other concurrent uses for the radio. For now, go to listen
+ * state once per second to give other uses a chance to use the radio.
+ */
+ p2p_set_state(p2p, P2P_WAIT_PEER_IDLE);
+ p2p_set_timeout(p2p, 0, 500000);
+}
+
+
+static void p2p_timeout_wait_peer_idle(struct p2p_data *p2p)
+{
+ struct p2p_device *dev = p2p->go_neg_peer;
+
+ if (dev == NULL) {
+ wpa_msg(p2p->cfg->msg_ctx, MSG_DEBUG,
+ "P2P: Unknown GO Neg peer - stop GO Neg wait");
+ return;
+ }
+
+ dev->wait_count++;
+ if (dev->wait_count >= 120) {
+ wpa_msg(p2p->cfg->msg_ctx, MSG_DEBUG,
+ "P2P: Timeout on waiting peer to become ready for GO "
+ "Negotiation");
+ p2p_go_neg_failed(p2p, dev, -1);
+ return;
+ }
+
+ wpa_msg(p2p->cfg->msg_ctx, MSG_DEBUG,
+ "P2P: Go to Listen state while waiting for the peer to become "
+ "ready for GO Negotiation");
+ p2p_set_state(p2p, P2P_WAIT_PEER_CONNECT);
+ p2p_listen_in_find(p2p, 0);
+}
+
+
+static void p2p_timeout_sd_during_find(struct p2p_data *p2p)
+{
+ wpa_msg(p2p->cfg->msg_ctx, MSG_DEBUG,
+ "P2P: Service Discovery Query timeout");
+ if (p2p->sd_peer) {
+ p2p->cfg->send_action_done(p2p->cfg->cb_ctx);
+ p2p->sd_peer->flags &= ~P2P_DEV_SD_SCHEDULE;
+ p2p->sd_peer = NULL;
+ }
+ p2p_continue_find(p2p);
+}
+
+
+static void p2p_timeout_prov_disc_during_find(struct p2p_data *p2p)
+{
+ wpa_msg(p2p->cfg->msg_ctx, MSG_DEBUG,
+ "P2P: Provision Discovery Request timeout");
+ p2p->cfg->send_action_done(p2p->cfg->cb_ctx);
+ p2p_continue_find(p2p);
+}
+
+
+static void p2p_timeout_prov_disc_req(struct p2p_data *p2p)
+{
+ p2p->pending_action_state = P2P_NO_PENDING_ACTION;
+
+ /*
+ * For user initiated PD requests that we have not gotten any responses
+ * for while in IDLE state, we retry them a couple of times before
+ * giving up.
+ */
+ if (!p2p->user_initiated_pd)
+ return;
+
+ wpa_msg(p2p->cfg->msg_ctx, MSG_DEBUG,
+ "P2P: User initiated Provision Discovery Request timeout");
+
+ if (p2p->pd_retries) {
+ p2p->pd_retries--;
+ p2p_retry_pd(p2p);
+ } else {
+ struct p2p_device *dev;
+ int for_join = 0;
+
+ dl_list_for_each(dev, &p2p->devices, struct p2p_device, list) {
+ if (os_memcmp(p2p->pending_pd_devaddr,
+ dev->info.p2p_device_addr, ETH_ALEN) != 0)
+ continue;
+ if (dev->req_config_methods &&
+ (dev->flags & P2P_DEV_PD_FOR_JOIN))
+ for_join = 1;
+ }
+
+ if (p2p->cfg->prov_disc_fail)
+ p2p->cfg->prov_disc_fail(p2p->cfg->cb_ctx,
+ p2p->pending_pd_devaddr,
+ for_join ?
+ P2P_PROV_DISC_TIMEOUT_JOIN :
+ P2P_PROV_DISC_TIMEOUT);
+ p2p_reset_pending_pd(p2p);
+ }
+}
+
+
+static void p2p_timeout_invite(struct p2p_data *p2p)
+{
+ p2p->cfg->send_action_done(p2p->cfg->cb_ctx);
+ p2p_set_state(p2p, P2P_INVITE_LISTEN);
+ if (p2p->inv_role == P2P_INVITE_ROLE_ACTIVE_GO) {
+ /*
+ * Better remain on operating channel instead of listen channel
+ * when running a group.
+ */
+ wpa_msg(p2p->cfg->msg_ctx, MSG_DEBUG, "P2P: Inviting in "
+ "active GO role - wait on operating channel");
+ p2p_set_timeout(p2p, 0, 100000);
+ return;
+ }
+ p2p_listen_in_find(p2p, 0);
+}
+
+
+static void p2p_timeout_invite_listen(struct p2p_data *p2p)
+{
+ if (p2p->invite_peer && p2p->invite_peer->invitation_reqs < 100) {
+ p2p_set_state(p2p, P2P_INVITE);
+ p2p_invite_send(p2p, p2p->invite_peer,
+ p2p->invite_go_dev_addr);
+ } else {
+ if (p2p->invite_peer) {
+ wpa_msg(p2p->cfg->msg_ctx, MSG_DEBUG,
+ "P2P: Invitation Request retry limit reached");
+ if (p2p->cfg->invitation_result)
+ p2p->cfg->invitation_result(
+ p2p->cfg->cb_ctx, -1, NULL);
+ }
+ p2p_set_state(p2p, P2P_IDLE);
+ }
+}
+
+
+static void p2p_state_timeout(void *eloop_ctx, void *timeout_ctx)
+{
+ struct p2p_data *p2p = eloop_ctx;
+
+ wpa_msg(p2p->cfg->msg_ctx, MSG_DEBUG, "P2P: Timeout (state=%s)",
+ p2p_state_txt(p2p->state));
+
+ p2p->in_listen = 0;
+
+ switch (p2p->state) {
+ case P2P_IDLE:
+ /* Check if we timed out waiting for PD req */
+ if (p2p->pending_action_state == P2P_PENDING_PD)
+ p2p_timeout_prov_disc_req(p2p);
+ break;
+ case P2P_SEARCH:
+ /* Check if we timed out waiting for PD req */
+ if (p2p->pending_action_state == P2P_PENDING_PD)
+ p2p_timeout_prov_disc_req(p2p);
+ if (p2p->search_delay && !p2p->in_search_delay) {
+ wpa_msg(p2p->cfg->msg_ctx, MSG_DEBUG, "P2P: Delay "
+ "search operation by %u ms",
+ p2p->search_delay);
+ p2p->in_search_delay = 1;
+ p2p_set_timeout(p2p, p2p->search_delay / 1000,
+ (p2p->search_delay % 1000) * 1000);
+ break;
+ }
+ p2p->in_search_delay = 0;
+ p2p_search(p2p);
+ break;
+ case P2P_CONNECT:
+ p2p_timeout_connect(p2p);
+ break;
+ case P2P_CONNECT_LISTEN:
+ p2p_timeout_connect_listen(p2p);
+ break;
+ case P2P_GO_NEG:
+ break;
+ case P2P_LISTEN_ONLY:
+ /* Check if we timed out waiting for PD req */
+ if (p2p->pending_action_state == P2P_PENDING_PD)
+ p2p_timeout_prov_disc_req(p2p);
+
+ if (p2p->ext_listen_only) {
+ wpa_msg(p2p->cfg->msg_ctx, MSG_DEBUG,
+ "P2P: Extended Listen Timing - Listen State "
+ "completed");
+ p2p->ext_listen_only = 0;
+ p2p_set_state(p2p, P2P_IDLE);
+ }
+ break;
+ case P2P_WAIT_PEER_CONNECT:
+ p2p_timeout_wait_peer_connect(p2p);
+ break;
+ case P2P_WAIT_PEER_IDLE:
+ p2p_timeout_wait_peer_idle(p2p);
+ break;
+ case P2P_SD_DURING_FIND:
+ p2p_timeout_sd_during_find(p2p);
+ break;
+ case P2P_PROVISIONING:
+ break;
+ case P2P_PD_DURING_FIND:
+ p2p_timeout_prov_disc_during_find(p2p);
+ break;
+ case P2P_INVITE:
+ p2p_timeout_invite(p2p);
+ break;
+ case P2P_INVITE_LISTEN:
+ p2p_timeout_invite_listen(p2p);
+ break;
+ case P2P_SEARCH_WHEN_READY:
+ break;
+ case P2P_CONTINUE_SEARCH_WHEN_READY:
+ break;
+ }
+}
+
+
+int p2p_reject(struct p2p_data *p2p, const u8 *peer_addr)
+{
+ struct p2p_device *dev;
+
+ dev = p2p_get_device(p2p, peer_addr);
+ wpa_msg(p2p->cfg->msg_ctx, MSG_DEBUG, "P2P: Local request to reject "
+ "connection attempts by peer " MACSTR, MAC2STR(peer_addr));
+ if (dev == NULL) {
+ wpa_msg(p2p->cfg->msg_ctx, MSG_DEBUG, "P2P: Peer " MACSTR
+ " unknown", MAC2STR(peer_addr));
+ return -1;
+ }
+ dev->status = P2P_SC_FAIL_REJECTED_BY_USER;
+ dev->flags |= P2P_DEV_USER_REJECTED;
+ return 0;
+}
+
+
+const char * p2p_wps_method_text(enum p2p_wps_method method)
+{
+ switch (method) {
+ case WPS_NOT_READY:
+ return "not-ready";
+ case WPS_PIN_DISPLAY:
+ return "Display";
+ case WPS_PIN_KEYPAD:
+ return "Keypad";
+ case WPS_PBC:
+ return "PBC";
+ }
+
+ return "??";
+}
+
+
+static const char * p2p_go_state_text(enum p2p_go_state go_state)
+{
+ switch (go_state) {
+ case UNKNOWN_GO:
+ return "unknown";
+ case LOCAL_GO:
+ return "local";
+ case REMOTE_GO:
+ return "remote";
+ }
+
+ return "??";
+}
+
+
+const struct p2p_peer_info * p2p_get_peer_info(struct p2p_data *p2p,
+ const u8 *addr, int next)
+{
+ struct p2p_device *dev;
+
+ if (addr)
+ dev = p2p_get_device(p2p, addr);
+ else
+ dev = dl_list_first(&p2p->devices, struct p2p_device, list);
+
+ if (dev && next) {
+ dev = dl_list_first(&dev->list, struct p2p_device, list);
+ if (&dev->list == &p2p->devices)
+ dev = NULL;
+ }
+
+ if (dev == NULL)
+ return NULL;
+
+ return &dev->info;
+}
+
+
+int p2p_get_peer_info_txt(const struct p2p_peer_info *info,
+ char *buf, size_t buflen)
+{
+ struct p2p_device *dev;
+ int res;
+ char *pos, *end;
+ struct os_time now;
+
+ if (info == NULL)
+ return -1;
+
+ dev = (struct p2p_device *) (((u8 *) info) -
+ offsetof(struct p2p_device, info));
+
+ pos = buf;
+ end = buf + buflen;
+
+ os_get_time(&now);
+ res = os_snprintf(pos, end - pos,
+ "age=%d\n"
+ "listen_freq=%d\n"
+ "wps_method=%s\n"
+ "interface_addr=" MACSTR "\n"
+ "member_in_go_dev=" MACSTR "\n"
+ "member_in_go_iface=" MACSTR "\n"
+ "go_neg_req_sent=%d\n"
+ "go_state=%s\n"
+ "dialog_token=%u\n"
+ "intended_addr=" MACSTR "\n"
+ "country=%c%c\n"
+ "oper_freq=%d\n"
+ "req_config_methods=0x%x\n"
+ "flags=%s%s%s%s%s%s%s%s%s%s%s%s%s%s%s\n"
+ "status=%d\n"
+ "wait_count=%u\n"
+ "invitation_reqs=%u\n",
+ (int) (now.sec - dev->last_seen.sec),
+ dev->listen_freq,
+ p2p_wps_method_text(dev->wps_method),
+ MAC2STR(dev->interface_addr),
+ MAC2STR(dev->member_in_go_dev),
+ MAC2STR(dev->member_in_go_iface),
+ dev->go_neg_req_sent,
+ p2p_go_state_text(dev->go_state),
+ dev->dialog_token,
+ MAC2STR(dev->intended_addr),
+ dev->country[0] ? dev->country[0] : '_',
+ dev->country[1] ? dev->country[1] : '_',
+ dev->oper_freq,
+ dev->req_config_methods,
+ dev->flags & P2P_DEV_PROBE_REQ_ONLY ?
+ "[PROBE_REQ_ONLY]" : "",
+ dev->flags & P2P_DEV_REPORTED ? "[REPORTED]" : "",
+ dev->flags & P2P_DEV_NOT_YET_READY ?
+ "[NOT_YET_READY]" : "",
+ dev->flags & P2P_DEV_SD_INFO ? "[SD_INFO]" : "",
+ dev->flags & P2P_DEV_SD_SCHEDULE ? "[SD_SCHEDULE]" :
+ "",
+ dev->flags & P2P_DEV_PD_PEER_DISPLAY ?
+ "[PD_PEER_DISPLAY]" : "",
+ dev->flags & P2P_DEV_PD_PEER_KEYPAD ?
+ "[PD_PEER_KEYPAD]" : "",
+ dev->flags & P2P_DEV_USER_REJECTED ?
+ "[USER_REJECTED]" : "",
+ dev->flags & P2P_DEV_PEER_WAITING_RESPONSE ?
+ "[PEER_WAITING_RESPONSE]" : "",
+ dev->flags & P2P_DEV_PREFER_PERSISTENT_GROUP ?
+ "[PREFER_PERSISTENT_GROUP]" : "",
+ dev->flags & P2P_DEV_WAIT_GO_NEG_RESPONSE ?
+ "[WAIT_GO_NEG_RESPONSE]" : "",
+ dev->flags & P2P_DEV_WAIT_GO_NEG_CONFIRM ?
+ "[WAIT_GO_NEG_CONFIRM]" : "",
+ dev->flags & P2P_DEV_GROUP_CLIENT_ONLY ?
+ "[GROUP_CLIENT_ONLY]" : "",
+ dev->flags & P2P_DEV_FORCE_FREQ ?
+ "[FORCE_FREQ]" : "",
+ dev->flags & P2P_DEV_PD_FOR_JOIN ?
+ "[PD_FOR_JOIN]" : "",
+ dev->status,
+ dev->wait_count,
+ dev->invitation_reqs);
+ if (res < 0 || res >= end - pos)
+ return pos - buf;
+ pos += res;
+
+ if (dev->ext_listen_period) {
+ res = os_snprintf(pos, end - pos,
+ "ext_listen_period=%u\n"
+ "ext_listen_interval=%u\n",
+ dev->ext_listen_period,
+ dev->ext_listen_interval);
+ if (res < 0 || res >= end - pos)
+ return pos - buf;
+ pos += res;
+ }
+
+ if (dev->oper_ssid_len) {
+ res = os_snprintf(pos, end - pos,
+ "oper_ssid=%s\n",
+ wpa_ssid_txt(dev->oper_ssid,
+ dev->oper_ssid_len));
+ if (res < 0 || res >= end - pos)
+ return pos - buf;
+ pos += res;
+ }
+
+#ifdef CONFIG_WIFI_DISPLAY
+ if (dev->info.wfd_subelems) {
+ res = os_snprintf(pos, end - pos, "wfd_subelems=");
+ if (res < 0 || res >= end - pos)
+ return pos - buf;
+ pos += res;
+
+ pos += wpa_snprintf_hex(pos, end - pos,
+ wpabuf_head(dev->info.wfd_subelems),
+ wpabuf_len(dev->info.wfd_subelems));
+
+ res = os_snprintf(pos, end - pos, "\n");
+ if (res < 0 || res >= end - pos)
+ return pos - buf;
+ pos += res;
+ }
+#endif /* CONFIG_WIFI_DISPLAY */
+
+ return pos - buf;
+}
+
+
+int p2p_peer_known(struct p2p_data *p2p, const u8 *addr)
+{
+ return p2p_get_device(p2p, addr) != NULL;
+}
+
+
+void p2p_set_client_discoverability(struct p2p_data *p2p, int enabled)
+{
+ if (enabled) {
+ wpa_msg(p2p->cfg->msg_ctx, MSG_DEBUG, "P2P: Client "
+ "discoverability enabled");
+ p2p->dev_capab |= P2P_DEV_CAPAB_CLIENT_DISCOVERABILITY;
+ } else {
+ wpa_msg(p2p->cfg->msg_ctx, MSG_DEBUG, "P2P: Client "
+ "discoverability disabled");
+ p2p->dev_capab &= ~P2P_DEV_CAPAB_CLIENT_DISCOVERABILITY;
+ }
+}
+
+
+static struct wpabuf * p2p_build_presence_req(u32 duration1, u32 interval1,
+ u32 duration2, u32 interval2)
+{
+ struct wpabuf *req;
+ struct p2p_noa_desc desc1, desc2, *ptr1 = NULL, *ptr2 = NULL;
+ u8 *len;
+
+ req = wpabuf_alloc(100);
+ if (req == NULL)
+ return NULL;
+
+ if (duration1 || interval1) {
+ os_memset(&desc1, 0, sizeof(desc1));
+ desc1.count_type = 1;
+ desc1.duration = duration1;
+ desc1.interval = interval1;
+ ptr1 = &desc1;
+
+ if (duration2 || interval2) {
+ os_memset(&desc2, 0, sizeof(desc2));
+ desc2.count_type = 2;
+ desc2.duration = duration2;
+ desc2.interval = interval2;
+ ptr2 = &desc2;
+ }
+ }
+
+ p2p_buf_add_action_hdr(req, P2P_PRESENCE_REQ, 1);
+ len = p2p_buf_add_ie_hdr(req);
+ p2p_buf_add_noa(req, 0, 0, 0, ptr1, ptr2);
+ p2p_buf_update_ie_hdr(req, len);
+
+ return req;
+}
+
+
+int p2p_presence_req(struct p2p_data *p2p, const u8 *go_interface_addr,
+ const u8 *own_interface_addr, unsigned int freq,
+ u32 duration1, u32 interval1, u32 duration2,
+ u32 interval2)
+{
+ struct wpabuf *req;
+
+ wpa_msg(p2p->cfg->msg_ctx, MSG_DEBUG, "P2P: Send Presence Request to "
+ "GO " MACSTR " (own interface " MACSTR ") freq=%u dur1=%u "
+ "int1=%u dur2=%u int2=%u",
+ MAC2STR(go_interface_addr), MAC2STR(own_interface_addr),
+ freq, duration1, interval1, duration2, interval2);
+
+ req = p2p_build_presence_req(duration1, interval1, duration2,
+ interval2);
+ if (req == NULL)
+ return -1;
+
+ p2p->pending_action_state = P2P_NO_PENDING_ACTION;
+ if (p2p_send_action(p2p, freq, go_interface_addr, own_interface_addr,
+ go_interface_addr,
+ wpabuf_head(req), wpabuf_len(req), 200) < 0) {
+ wpa_msg(p2p->cfg->msg_ctx, MSG_DEBUG,
+ "P2P: Failed to send Action frame");
+ }
+ wpabuf_free(req);
+
+ return 0;
+}
+
+
+static struct wpabuf * p2p_build_presence_resp(u8 status, const u8 *noa,
+ size_t noa_len, u8 dialog_token)
+{
+ struct wpabuf *resp;
+ u8 *len;
+
+ resp = wpabuf_alloc(100 + noa_len);
+ if (resp == NULL)
+ return NULL;
+
+ p2p_buf_add_action_hdr(resp, P2P_PRESENCE_RESP, dialog_token);
+ len = p2p_buf_add_ie_hdr(resp);
+ p2p_buf_add_status(resp, status);
+ if (noa) {
+ wpabuf_put_u8(resp, P2P_ATTR_NOTICE_OF_ABSENCE);
+ wpabuf_put_le16(resp, noa_len);
+ wpabuf_put_data(resp, noa, noa_len);
+ } else
+ p2p_buf_add_noa(resp, 0, 0, 0, NULL, NULL);
+ p2p_buf_update_ie_hdr(resp, len);
+
+ return resp;
+}
+
+
+static void p2p_process_presence_req(struct p2p_data *p2p, const u8 *da,
+ const u8 *sa, const u8 *data, size_t len,
+ int rx_freq)
+{
+ struct p2p_message msg;
+ u8 status;
+ struct wpabuf *resp;
+ size_t g;
+ struct p2p_group *group = NULL;
+ int parsed = 0;
+ u8 noa[50];
+ int noa_len;
+
+ wpa_msg(p2p->cfg->msg_ctx, MSG_DEBUG,
+ "P2P: Received P2P Action - P2P Presence Request");
+
+ for (g = 0; g < p2p->num_groups; g++) {
+ if (os_memcmp(da, p2p_group_get_interface_addr(p2p->groups[g]),
+ ETH_ALEN) == 0) {
+ group = p2p->groups[g];
+ break;
+ }
+ }
+ if (group == NULL) {
+ wpa_msg(p2p->cfg->msg_ctx, MSG_DEBUG,
+ "P2P: Ignore P2P Presence Request for unknown group "
+ MACSTR, MAC2STR(da));
+ return;
+ }
+
+ if (p2p_parse(data, len, &msg) < 0) {
+ wpa_msg(p2p->cfg->msg_ctx, MSG_DEBUG,
+ "P2P: Failed to parse P2P Presence Request");
+ status = P2P_SC_FAIL_INVALID_PARAMS;
+ goto fail;
+ }
+ parsed = 1;
+
+ if (msg.noa == NULL) {
+ wpa_msg(p2p->cfg->msg_ctx, MSG_DEBUG,
+ "P2P: No NoA attribute in P2P Presence Request");
+ status = P2P_SC_FAIL_INVALID_PARAMS;
+ goto fail;
+ }
+
+ status = p2p_group_presence_req(group, sa, msg.noa, msg.noa_len);
+
+fail:
+ if (p2p->cfg->get_noa)
+ noa_len = p2p->cfg->get_noa(p2p->cfg->cb_ctx, da, noa,
+ sizeof(noa));
+ else
+ noa_len = -1;
+ resp = p2p_build_presence_resp(status, noa_len > 0 ? noa : NULL,
+ noa_len > 0 ? noa_len : 0,
+ msg.dialog_token);
+ if (parsed)
+ p2p_parse_free(&msg);
+ if (resp == NULL)
+ return;
+
+ p2p->pending_action_state = P2P_NO_PENDING_ACTION;
+ if (p2p_send_action(p2p, rx_freq, sa, da, da,
+ wpabuf_head(resp), wpabuf_len(resp), 200) < 0) {
+ wpa_msg(p2p->cfg->msg_ctx, MSG_DEBUG,
+ "P2P: Failed to send Action frame");
+ }
+ wpabuf_free(resp);
+}
+
+
+static void p2p_process_presence_resp(struct p2p_data *p2p, const u8 *da,
+ const u8 *sa, const u8 *data, size_t len)
+{
+ struct p2p_message msg;
+
+ wpa_msg(p2p->cfg->msg_ctx, MSG_DEBUG,
+ "P2P: Received P2P Action - P2P Presence Response");
+
+ if (p2p_parse(data, len, &msg) < 0) {
+ wpa_msg(p2p->cfg->msg_ctx, MSG_DEBUG,
+ "P2P: Failed to parse P2P Presence Response");
+ return;
+ }
+
+ if (msg.status == NULL || msg.noa == NULL) {
+ wpa_msg(p2p->cfg->msg_ctx, MSG_DEBUG,
+ "P2P: No Status or NoA attribute in P2P Presence "
+ "Response");
+ p2p_parse_free(&msg);
+ return;
+ }
+
+ if (*msg.status) {
+ wpa_msg(p2p->cfg->msg_ctx, MSG_DEBUG,
+ "P2P: P2P Presence Request was rejected: status %u",
+ *msg.status);
+ p2p_parse_free(&msg);
+ return;
+ }
+
+ wpa_msg(p2p->cfg->msg_ctx, MSG_DEBUG,
+ "P2P: P2P Presence Request was accepted");
+ wpa_hexdump(MSG_DEBUG, "P2P: P2P Presence Response - NoA",
+ msg.noa, msg.noa_len);
+ /* TODO: process NoA */
+ p2p_parse_free(&msg);
+}
+
+
+static void p2p_ext_listen_timeout(void *eloop_ctx, void *timeout_ctx)
+{
+ struct p2p_data *p2p = eloop_ctx;
+
+ if (p2p->ext_listen_interval) {
+ /* Schedule next extended listen timeout */
+ eloop_register_timeout(p2p->ext_listen_interval_sec,
+ p2p->ext_listen_interval_usec,
+ p2p_ext_listen_timeout, p2p, NULL);
+ }
+
+ if (p2p->state == P2P_LISTEN_ONLY && p2p->ext_listen_only) {
+ /*
+ * This should not really happen, but it looks like the Listen
+ * command may fail is something else (e.g., a scan) was
+ * running at an inconvenient time. As a workaround, allow new
+ * Extended Listen operation to be started.
+ */
+ wpa_msg(p2p->cfg->msg_ctx, MSG_DEBUG, "P2P: Previous "
+ "Extended Listen operation had not been completed - "
+ "try again");
+ p2p->ext_listen_only = 0;
+ p2p_set_state(p2p, P2P_IDLE);
+ }
+
+ if (p2p->state != P2P_IDLE) {
+ wpa_msg(p2p->cfg->msg_ctx, MSG_DEBUG, "P2P: Skip Extended "
+ "Listen timeout in active state (%s)",
+ p2p_state_txt(p2p->state));
+ return;
+ }
+
+ wpa_msg(p2p->cfg->msg_ctx, MSG_DEBUG, "P2P: Extended Listen timeout");
+ p2p->ext_listen_only = 1;
+ if (p2p_listen(p2p, p2p->ext_listen_period) < 0) {
+ wpa_msg(p2p->cfg->msg_ctx, MSG_DEBUG, "P2P: Failed to start "
+ "Listen state for Extended Listen Timing");
+ p2p->ext_listen_only = 0;
+ }
+}
+
+
+int p2p_ext_listen(struct p2p_data *p2p, unsigned int period,
+ unsigned int interval)
+{
+ if (period > 65535 || interval > 65535 || period > interval ||
+ (period == 0 && interval > 0) || (period > 0 && interval == 0)) {
+ wpa_msg(p2p->cfg->msg_ctx, MSG_DEBUG,
+ "P2P: Invalid Extended Listen Timing request: "
+ "period=%u interval=%u", period, interval);
+ return -1;
+ }
+
+ eloop_cancel_timeout(p2p_ext_listen_timeout, p2p, NULL);
+
+ if (interval == 0) {
+ wpa_msg(p2p->cfg->msg_ctx, MSG_DEBUG,
+ "P2P: Disabling Extended Listen Timing");
+ p2p->ext_listen_period = 0;
+ p2p->ext_listen_interval = 0;
+ return 0;
+ }
+
+ wpa_msg(p2p->cfg->msg_ctx, MSG_DEBUG,
+ "P2P: Enabling Extended Listen Timing: period %u msec, "
+ "interval %u msec", period, interval);
+ p2p->ext_listen_period = period;
+ p2p->ext_listen_interval = interval;
+ p2p->ext_listen_interval_sec = interval / 1000;
+ p2p->ext_listen_interval_usec = (interval % 1000) * 1000;
+
+ eloop_register_timeout(p2p->ext_listen_interval_sec,
+ p2p->ext_listen_interval_usec,
+ p2p_ext_listen_timeout, p2p, NULL);
+
+ return 0;
+}
+
+
+void p2p_deauth_notif(struct p2p_data *p2p, const u8 *bssid, u16 reason_code,
+ const u8 *ie, size_t ie_len)
+{
+ struct p2p_message msg;
+
+ if (bssid == NULL || ie == NULL)
+ return;
+
+ os_memset(&msg, 0, sizeof(msg));
+ if (p2p_parse_ies(ie, ie_len, &msg))
+ return;
+ if (msg.minor_reason_code == NULL)
+ return;
+
+ wpa_msg(p2p->cfg->msg_ctx, MSG_INFO,
+ "P2P: Deauthentication notification BSSID " MACSTR
+ " reason_code=%u minor_reason_code=%u",
+ MAC2STR(bssid), reason_code, *msg.minor_reason_code);
+
+ p2p_parse_free(&msg);
+}
+
+
+void p2p_disassoc_notif(struct p2p_data *p2p, const u8 *bssid, u16 reason_code,
+ const u8 *ie, size_t ie_len)
+{
+ struct p2p_message msg;
+
+ if (bssid == NULL || ie == NULL)
+ return;
+
+ os_memset(&msg, 0, sizeof(msg));
+ if (p2p_parse_ies(ie, ie_len, &msg))
+ return;
+ if (msg.minor_reason_code == NULL)
+ return;
+
+ wpa_msg(p2p->cfg->msg_ctx, MSG_INFO,
+ "P2P: Disassociation notification BSSID " MACSTR
+ " reason_code=%u minor_reason_code=%u",
+ MAC2STR(bssid), reason_code, *msg.minor_reason_code);
+
+ p2p_parse_free(&msg);
+}
+
+
+void p2p_set_managed_oper(struct p2p_data *p2p, int enabled)
+{
+ if (enabled) {
+ wpa_msg(p2p->cfg->msg_ctx, MSG_DEBUG, "P2P: Managed P2P "
+ "Device operations enabled");
+ p2p->dev_capab |= P2P_DEV_CAPAB_INFRA_MANAGED;
+ } else {
+ wpa_msg(p2p->cfg->msg_ctx, MSG_DEBUG, "P2P: Managed P2P "
+ "Device operations disabled");
+ p2p->dev_capab &= ~P2P_DEV_CAPAB_INFRA_MANAGED;
+ }
+}
+
+
+int p2p_set_listen_channel(struct p2p_data *p2p, u8 reg_class, u8 channel)
+{
+ if (p2p_channel_to_freq(p2p->cfg->country, reg_class, channel) < 0)
+ return -1;
+
+ wpa_msg(p2p->cfg->msg_ctx, MSG_DEBUG, "P2P: Set Listen channel: "
+ "reg_class %u channel %u", reg_class, channel);
+ p2p->cfg->reg_class = reg_class;
+ p2p->cfg->channel = channel;
+
+ return 0;
+}
+
+
+int p2p_set_ssid_postfix(struct p2p_data *p2p, const u8 *postfix, size_t len)
+{
+ wpa_hexdump_ascii(MSG_DEBUG, "P2P: New SSID postfix", postfix, len);
+ if (postfix == NULL) {
+ p2p->cfg->ssid_postfix_len = 0;
+ return 0;
+ }
+ if (len > sizeof(p2p->cfg->ssid_postfix))
+ return -1;
+ os_memcpy(p2p->cfg->ssid_postfix, postfix, len);
+ p2p->cfg->ssid_postfix_len = len;
+ return 0;
+}
+
+
+int p2p_set_oper_channel(struct p2p_data *p2p, u8 op_reg_class, u8 op_channel,
+ int cfg_op_channel)
+{
+ if (p2p_channel_to_freq(p2p->cfg->country, op_reg_class, op_channel)
+ < 0)
+ return -1;
+
+ wpa_msg(p2p->cfg->msg_ctx, MSG_INFO, "P2P: Set Operating channel: "
+ "reg_class %u channel %u", op_reg_class, op_channel);
+ p2p->cfg->op_reg_class = op_reg_class;
+ p2p->cfg->op_channel = op_channel;
+ p2p->cfg->cfg_op_channel = cfg_op_channel;
+ return 0;
+}
+
+
+int p2p_set_pref_chan(struct p2p_data *p2p, unsigned int num_pref_chan,
+ const struct p2p_channel *pref_chan)
+{
+ struct p2p_channel *n;
+
+ if (pref_chan) {
+ n = os_malloc(num_pref_chan * sizeof(struct p2p_channel));
+ if (n == NULL)
+ return -1;
+ os_memcpy(n, pref_chan,
+ num_pref_chan * sizeof(struct p2p_channel));
+ } else
+ n = NULL;
+
+ os_free(p2p->cfg->pref_chan);
+ p2p->cfg->pref_chan = n;
+ p2p->cfg->num_pref_chan = num_pref_chan;
+
+ return 0;
+}
+
+
+int p2p_get_interface_addr(struct p2p_data *p2p, const u8 *dev_addr,
+ u8 *iface_addr)
+{
+ struct p2p_device *dev = p2p_get_device(p2p, dev_addr);
+ if (dev == NULL || is_zero_ether_addr(dev->interface_addr))
+ return -1;
+ os_memcpy(iface_addr, dev->interface_addr, ETH_ALEN);
+ return 0;
+}
+
+
+int p2p_get_dev_addr(struct p2p_data *p2p, const u8 *iface_addr,
+ u8 *dev_addr)
+{
+ struct p2p_device *dev = p2p_get_device_interface(p2p, iface_addr);
+ if (dev == NULL)
+ return -1;
+ os_memcpy(dev_addr, dev->info.p2p_device_addr, ETH_ALEN);
+ return 0;
+}
+
+
+void p2p_set_peer_filter(struct p2p_data *p2p, const u8 *addr)
+{
+ os_memcpy(p2p->peer_filter, addr, ETH_ALEN);
+ if (is_zero_ether_addr(p2p->peer_filter))
+ wpa_msg(p2p->cfg->msg_ctx, MSG_DEBUG, "P2P: Disable peer "
+ "filter");
+ else
+ wpa_msg(p2p->cfg->msg_ctx, MSG_DEBUG, "P2P: Enable peer "
+ "filter for " MACSTR, MAC2STR(p2p->peer_filter));
+}
+
+
+void p2p_set_cross_connect(struct p2p_data *p2p, int enabled)
+{
+ wpa_msg(p2p->cfg->msg_ctx, MSG_DEBUG, "P2P: Cross connection %s",
+ enabled ? "enabled" : "disabled");
+ if (p2p->cross_connect == enabled)
+ return;
+ p2p->cross_connect = enabled;
+ /* TODO: may need to tear down any action group where we are GO(?) */
+}
+
+
+int p2p_get_oper_freq(struct p2p_data *p2p, const u8 *iface_addr)
+{
+ struct p2p_device *dev = p2p_get_device_interface(p2p, iface_addr);
+ if (dev == NULL)
+ return -1;
+ if (dev->oper_freq <= 0)
+ return -1;
+ return dev->oper_freq;
+}
+
+
+void p2p_set_intra_bss_dist(struct p2p_data *p2p, int enabled)
+{
+ wpa_msg(p2p->cfg->msg_ctx, MSG_DEBUG, "P2P: Intra BSS distribution %s",
+ enabled ? "enabled" : "disabled");
+ p2p->cfg->p2p_intra_bss = enabled;
+}
+
+
+void p2p_update_channel_list(struct p2p_data *p2p, struct p2p_channels *chan)
+{
+ wpa_msg(p2p->cfg->msg_ctx, MSG_DEBUG, "P2P: Update channel list");
+ os_memcpy(&p2p->cfg->channels, chan, sizeof(struct p2p_channels));
+}
+
+
+int p2p_send_action(struct p2p_data *p2p, unsigned int freq, const u8 *dst,
+ const u8 *src, const u8 *bssid, const u8 *buf,
+ size_t len, unsigned int wait_time)
+{
+ if (p2p->p2p_scan_running) {
+ wpa_msg(p2p->cfg->msg_ctx, MSG_DEBUG, "P2P: Delay Action "
+ "frame TX until p2p_scan completes");
+ if (p2p->after_scan_tx) {
+ wpa_msg(p2p->cfg->msg_ctx, MSG_DEBUG, "P2P: Dropped "
+ "previous pending Action frame TX");
+ os_free(p2p->after_scan_tx);
+ }
+ p2p->after_scan_tx = os_malloc(sizeof(*p2p->after_scan_tx) +
+ len);
+ if (p2p->after_scan_tx == NULL)
+ return -1;
+ p2p->after_scan_tx->freq = freq;
+ os_memcpy(p2p->after_scan_tx->dst, dst, ETH_ALEN);
+ os_memcpy(p2p->after_scan_tx->src, src, ETH_ALEN);
+ os_memcpy(p2p->after_scan_tx->bssid, bssid, ETH_ALEN);
+ p2p->after_scan_tx->len = len;
+ p2p->after_scan_tx->wait_time = wait_time;
+ os_memcpy(p2p->after_scan_tx + 1, buf, len);
+ return 0;
+ }
+
+ return p2p->cfg->send_action(p2p->cfg->cb_ctx, freq, dst, src, bssid,
+ buf, len, wait_time);
+}
+
+
+void p2p_set_best_channels(struct p2p_data *p2p, int freq_24, int freq_5,
+ int freq_overall)
+{
+ wpa_msg(p2p->cfg->msg_ctx, MSG_DEBUG, "P2P: Best channel: 2.4 GHz: %d,"
+ " 5 GHz: %d, overall: %d", freq_24, freq_5, freq_overall);
+ p2p->best_freq_24 = freq_24;
+ p2p->best_freq_5 = freq_5;
+ p2p->best_freq_overall = freq_overall;
+}
+
+
+const u8 * p2p_get_go_neg_peer(struct p2p_data *p2p)
+{
+ if (p2p == NULL || p2p->go_neg_peer == NULL)
+ return NULL;
+ return p2p->go_neg_peer->info.p2p_device_addr;
+}
+
+
+const struct p2p_peer_info *
+p2p_get_peer_found(struct p2p_data *p2p, const u8 *addr, int next)
+{
+ struct p2p_device *dev;
+
+ if (addr) {
+ dev = p2p_get_device(p2p, addr);
+ if (!dev)
+ return NULL;
+
+ if (!next) {
+ if (dev->flags & P2P_DEV_PROBE_REQ_ONLY)
+ return NULL;
+
+ return &dev->info;
+ } else {
+ do {
+ dev = dl_list_first(&dev->list,
+ struct p2p_device,
+ list);
+ if (&dev->list == &p2p->devices)
+ return NULL;
+ } while (dev->flags & P2P_DEV_PROBE_REQ_ONLY);
+ }
+ } else {
+ dev = dl_list_first(&p2p->devices, struct p2p_device, list);
+ if (!dev)
+ return NULL;
+ while (dev->flags & P2P_DEV_PROBE_REQ_ONLY) {
+ dev = dl_list_first(&dev->list,
+ struct p2p_device,
+ list);
+ if (&dev->list == &p2p->devices)
+ return NULL;
+ }
+ }
+
+ return &dev->info;
+}
+
+
+int p2p_in_progress(struct p2p_data *p2p)
+{
+ if (p2p == NULL)
+ return 0;
+ if (p2p->state == P2P_SEARCH || p2p->state == P2P_SEARCH_WHEN_READY ||
+ p2p->state == P2P_CONTINUE_SEARCH_WHEN_READY)
+ return 2;
+ return p2p->state != P2P_IDLE && p2p->state != P2P_PROVISIONING;
+}
+
+
+void p2p_set_config_timeout(struct p2p_data *p2p, u8 go_timeout,
+ u8 client_timeout)
+{
+ if (p2p) {
+ p2p->go_timeout = go_timeout;
+ p2p->client_timeout = client_timeout;
+ }
+}
+
+
+void p2p_increase_search_delay(struct p2p_data *p2p, unsigned int delay)
+{
+ if (p2p && p2p->search_delay < delay)
+ p2p->search_delay = delay;
+}
+
+
+#ifdef CONFIG_WIFI_DISPLAY
+
+static void p2p_update_wfd_ie_groups(struct p2p_data *p2p)
+{
+ size_t g;
+ struct p2p_group *group;
+
+ for (g = 0; g < p2p->num_groups; g++) {
+ group = p2p->groups[g];
+ p2p_group_update_ies(group);
+ }
+}
+
+
+int p2p_set_wfd_ie_beacon(struct p2p_data *p2p, struct wpabuf *ie)
+{
+ wpabuf_free(p2p->wfd_ie_beacon);
+ p2p->wfd_ie_beacon = ie;
+ p2p_update_wfd_ie_groups(p2p);
+ return 0;
+}
+
+
+int p2p_set_wfd_ie_probe_req(struct p2p_data *p2p, struct wpabuf *ie)
+{
+ wpabuf_free(p2p->wfd_ie_probe_req);
+ p2p->wfd_ie_probe_req = ie;
+ return 0;
+}
+
+
+int p2p_set_wfd_ie_probe_resp(struct p2p_data *p2p, struct wpabuf *ie)
+{
+ wpabuf_free(p2p->wfd_ie_probe_resp);
+ p2p->wfd_ie_probe_resp = ie;
+ p2p_update_wfd_ie_groups(p2p);
+ return 0;
+}
+
+
+int p2p_set_wfd_ie_assoc_req(struct p2p_data *p2p, struct wpabuf *ie)
+{
+ wpabuf_free(p2p->wfd_ie_assoc_req);
+ p2p->wfd_ie_assoc_req = ie;
+ return 0;
+}
+
+
+int p2p_set_wfd_ie_invitation(struct p2p_data *p2p, struct wpabuf *ie)
+{
+ wpabuf_free(p2p->wfd_ie_invitation);
+ p2p->wfd_ie_invitation = ie;
+ return 0;
+}
+
+
+int p2p_set_wfd_ie_prov_disc_req(struct p2p_data *p2p, struct wpabuf *ie)
+{
+ wpabuf_free(p2p->wfd_ie_prov_disc_req);
+ p2p->wfd_ie_prov_disc_req = ie;
+ return 0;
+}
+
+
+int p2p_set_wfd_ie_prov_disc_resp(struct p2p_data *p2p, struct wpabuf *ie)
+{
+ wpabuf_free(p2p->wfd_ie_prov_disc_resp);
+ p2p->wfd_ie_prov_disc_resp = ie;
+ return 0;
+}
+
+
+int p2p_set_wfd_ie_go_neg(struct p2p_data *p2p, struct wpabuf *ie)
+{
+ wpabuf_free(p2p->wfd_ie_go_neg);
+ p2p->wfd_ie_go_neg = ie;
+ return 0;
+}
+
+
+int p2p_set_wfd_dev_info(struct p2p_data *p2p, const struct wpabuf *elem)
+{
+ wpabuf_free(p2p->wfd_dev_info);
+ if (elem) {
+ p2p->wfd_dev_info = wpabuf_dup(elem);
+ if (p2p->wfd_dev_info == NULL)
+ return -1;
+ } else
+ p2p->wfd_dev_info = NULL;
+
+ return 0;
+}
+
+
+int p2p_set_wfd_assoc_bssid(struct p2p_data *p2p, const struct wpabuf *elem)
+{
+ wpabuf_free(p2p->wfd_assoc_bssid);
+ if (elem) {
+ p2p->wfd_assoc_bssid = wpabuf_dup(elem);
+ if (p2p->wfd_assoc_bssid == NULL)
+ return -1;
+ } else
+ p2p->wfd_assoc_bssid = NULL;
+
+ return 0;
+}
+
+
+int p2p_set_wfd_coupled_sink_info(struct p2p_data *p2p,
+ const struct wpabuf *elem)
+{
+ wpabuf_free(p2p->wfd_coupled_sink_info);
+ if (elem) {
+ p2p->wfd_coupled_sink_info = wpabuf_dup(elem);
+ if (p2p->wfd_coupled_sink_info == NULL)
+ return -1;
+ } else
+ p2p->wfd_coupled_sink_info = NULL;
+
+ return 0;
+}
+
+#endif /* CONFIG_WIFI_DISPLAY */
+
+
+int p2p_set_disc_int(struct p2p_data *p2p, int min_disc_int, int max_disc_int,
+ int max_disc_tu)
+{
+ if (min_disc_int > max_disc_int || min_disc_int < 0 || max_disc_int < 0)
+ return -1;
+
+ p2p->min_disc_int = min_disc_int;
+ p2p->max_disc_int = max_disc_int;
+ p2p->max_disc_tu = max_disc_tu;
+ wpa_msg(p2p->cfg->msg_ctx, MSG_DEBUG, "P2P: Set discoverable interval: "
+ "min=%d max=%d max_tu=%d", min_disc_int, max_disc_int,
+ max_disc_tu);
+
+ return 0;
+}
diff --git a/contrib/wpa/src/p2p/p2p.h b/contrib/wpa/src/p2p/p2p.h
new file mode 100644
index 0000000..2d8b2c2
--- /dev/null
+++ b/contrib/wpa/src/p2p/p2p.h
@@ -0,0 +1,1783 @@
+/*
+ * Wi-Fi Direct - P2P module
+ * Copyright (c) 2009-2010, Atheros Communications
+ *
+ * This software may be distributed under the terms of the BSD license.
+ * See README for more details.
+ */
+
+#ifndef P2P_H
+#define P2P_H
+
+/**
+ * P2P_MAX_REG_CLASSES - Maximum number of regulatory classes
+ */
+#define P2P_MAX_REG_CLASSES 10
+
+/**
+ * P2P_MAX_REG_CLASS_CHANNELS - Maximum number of channels per regulatory class
+ */
+#define P2P_MAX_REG_CLASS_CHANNELS 20
+
+/**
+ * struct p2p_channels - List of supported channels
+ */
+struct p2p_channels {
+ /**
+ * struct p2p_reg_class - Supported regulatory class
+ */
+ struct p2p_reg_class {
+ /**
+ * reg_class - Regulatory class (IEEE 802.11-2007, Annex J)
+ */
+ u8 reg_class;
+
+ /**
+ * channel - Supported channels
+ */
+ u8 channel[P2P_MAX_REG_CLASS_CHANNELS];
+
+ /**
+ * channels - Number of channel entries in use
+ */
+ size_t channels;
+ } reg_class[P2P_MAX_REG_CLASSES];
+
+ /**
+ * reg_classes - Number of reg_class entries in use
+ */
+ size_t reg_classes;
+};
+
+enum p2p_wps_method {
+ WPS_NOT_READY, WPS_PIN_DISPLAY, WPS_PIN_KEYPAD, WPS_PBC
+};
+
+/**
+ * struct p2p_go_neg_results - P2P Group Owner Negotiation results
+ */
+struct p2p_go_neg_results {
+ /**
+ * status - Negotiation result (Status Code)
+ *
+ * 0 (P2P_SC_SUCCESS) indicates success. Non-zero values indicate
+ * failed negotiation.
+ */
+ int status;
+
+ /**
+ * role_go - Whether local end is Group Owner
+ */
+ int role_go;
+
+ /**
+ * freq - Frequency of the group operational channel in MHz
+ */
+ int freq;
+
+ int ht40;
+
+ /**
+ * ssid - SSID of the group
+ */
+ u8 ssid[32];
+
+ /**
+ * ssid_len - Length of SSID in octets
+ */
+ size_t ssid_len;
+
+ /**
+ * psk - WPA pre-shared key (256 bits) (GO only)
+ */
+ u8 psk[32];
+
+ /**
+ * psk_set - Whether PSK field is configured (GO only)
+ */
+ int psk_set;
+
+ /**
+ * passphrase - WPA2-Personal passphrase for the group (GO only)
+ */
+ char passphrase[64];
+
+ /**
+ * peer_device_addr - P2P Device Address of the peer
+ */
+ u8 peer_device_addr[ETH_ALEN];
+
+ /**
+ * peer_interface_addr - P2P Interface Address of the peer
+ */
+ u8 peer_interface_addr[ETH_ALEN];
+
+ /**
+ * wps_method - WPS method to be used during provisioning
+ */
+ enum p2p_wps_method wps_method;
+
+#define P2P_MAX_CHANNELS 50
+
+ /**
+ * freq_list - Zero-terminated list of possible operational channels
+ */
+ int freq_list[P2P_MAX_CHANNELS];
+
+ /**
+ * persistent_group - Whether the group should be made persistent
+ * 0 = not persistent
+ * 1 = persistent group without persistent reconnect
+ * 2 = persistent group with persistent reconnect
+ */
+ int persistent_group;
+
+ /**
+ * peer_config_timeout - Peer configuration timeout (in 10 msec units)
+ */
+ unsigned int peer_config_timeout;
+};
+
+struct p2p_data;
+
+enum p2p_scan_type {
+ P2P_SCAN_SOCIAL,
+ P2P_SCAN_FULL,
+ P2P_SCAN_SOCIAL_PLUS_ONE
+};
+
+#define P2P_MAX_WPS_VENDOR_EXT 10
+
+/**
+ * struct p2p_peer_info - P2P peer information
+ */
+struct p2p_peer_info {
+ /**
+ * p2p_device_addr - P2P Device Address of the peer
+ */
+ u8 p2p_device_addr[ETH_ALEN];
+
+ /**
+ * pri_dev_type - Primary Device Type
+ */
+ u8 pri_dev_type[8];
+
+ /**
+ * device_name - Device Name (0..32 octets encoded in UTF-8)
+ */
+ char device_name[33];
+
+ /**
+ * manufacturer - Manufacturer (0..64 octets encoded in UTF-8)
+ */
+ char manufacturer[65];
+
+ /**
+ * model_name - Model Name (0..32 octets encoded in UTF-8)
+ */
+ char model_name[33];
+
+ /**
+ * model_number - Model Number (0..32 octets encoded in UTF-8)
+ */
+ char model_number[33];
+
+ /**
+ * serial_number - Serial Number (0..32 octets encoded in UTF-8)
+ */
+ char serial_number[33];
+
+ /**
+ * level - Signal level
+ */
+ int level;
+
+ /**
+ * config_methods - WPS Configuration Methods
+ */
+ u16 config_methods;
+
+ /**
+ * dev_capab - Device Capabilities
+ */
+ u8 dev_capab;
+
+ /**
+ * group_capab - Group Capabilities
+ */
+ u8 group_capab;
+
+ /**
+ * wps_sec_dev_type_list - WPS secondary device type list
+ *
+ * This list includes from 0 to 16 Secondary Device Types as indicated
+ * by wps_sec_dev_type_list_len (8 * number of types).
+ */
+ u8 wps_sec_dev_type_list[128];
+
+ /**
+ * wps_sec_dev_type_list_len - Length of secondary device type list
+ */
+ size_t wps_sec_dev_type_list_len;
+
+ struct wpabuf *wps_vendor_ext[P2P_MAX_WPS_VENDOR_EXT];
+
+ /**
+ * wfd_subelems - Wi-Fi Display subelements from WFD IE(s)
+ */
+ struct wpabuf *wfd_subelems;
+};
+
+enum p2p_prov_disc_status {
+ P2P_PROV_DISC_SUCCESS,
+ P2P_PROV_DISC_TIMEOUT,
+ P2P_PROV_DISC_REJECTED,
+ P2P_PROV_DISC_TIMEOUT_JOIN,
+};
+
+struct p2p_channel {
+ u8 op_class;
+ u8 chan;
+};
+
+/**
+ * struct p2p_config - P2P configuration
+ *
+ * This configuration is provided to the P2P module during initialization with
+ * p2p_init().
+ */
+struct p2p_config {
+ /**
+ * country - Country code to use in P2P operations
+ */
+ char country[3];
+
+ /**
+ * reg_class - Regulatory class for own listen channel
+ */
+ u8 reg_class;
+
+ /**
+ * channel - Own listen channel
+ */
+ u8 channel;
+
+ /**
+ * Regulatory class for own operational channel
+ */
+ u8 op_reg_class;
+
+ /**
+ * op_channel - Own operational channel
+ */
+ u8 op_channel;
+
+ /**
+ * cfg_op_channel - Whether op_channel is hardcoded in configuration
+ */
+ u8 cfg_op_channel;
+
+ /**
+ * channels - Own supported regulatory classes and channels
+ *
+ * List of supposerted channels per regulatory class. The regulatory
+ * classes are defined in IEEE Std 802.11-2007 Annex J and the
+ * numbering of the clases depends on the configured country code.
+ */
+ struct p2p_channels channels;
+
+ /**
+ * num_pref_chan - Number of pref_chan entries
+ */
+ unsigned int num_pref_chan;
+
+ /**
+ * pref_chan - Preferred channels for GO Negotiation
+ */
+ struct p2p_channel *pref_chan;
+
+ /**
+ * pri_dev_type - Primary Device Type (see WPS)
+ */
+ u8 pri_dev_type[8];
+
+ /**
+ * P2P_SEC_DEVICE_TYPES - Maximum number of secondary device types
+ */
+#define P2P_SEC_DEVICE_TYPES 5
+
+ /**
+ * sec_dev_type - Optional secondary device types
+ */
+ u8 sec_dev_type[P2P_SEC_DEVICE_TYPES][8];
+
+ /**
+ * num_sec_dev_types - Number of sec_dev_type entries
+ */
+ size_t num_sec_dev_types;
+
+ /**
+ * dev_addr - P2P Device Address
+ */
+ u8 dev_addr[ETH_ALEN];
+
+ /**
+ * dev_name - Device Name
+ */
+ char *dev_name;
+
+ char *manufacturer;
+ char *model_name;
+ char *model_number;
+ char *serial_number;
+
+ u8 uuid[16];
+ u16 config_methods;
+
+ /**
+ * concurrent_operations - Whether concurrent operations are supported
+ */
+ int concurrent_operations;
+
+ /**
+ * max_peers - Maximum number of discovered peers to remember
+ *
+ * If more peers are discovered, older entries will be removed to make
+ * room for the new ones.
+ */
+ size_t max_peers;
+
+ /**
+ * p2p_intra_bss - Intra BSS communication is supported
+ */
+ int p2p_intra_bss;
+
+ /**
+ * ssid_postfix - Postfix data to add to the SSID
+ *
+ * This data will be added to the end of the SSID after the
+ * DIRECT-<random two octets> prefix.
+ */
+ u8 ssid_postfix[32 - 9];
+
+ /**
+ * ssid_postfix_len - Length of the ssid_postfix data
+ */
+ size_t ssid_postfix_len;
+
+ /**
+ * max_listen - Maximum listen duration in ms
+ */
+ unsigned int max_listen;
+
+ /**
+ * msg_ctx - Context to use with wpa_msg() calls
+ */
+ void *msg_ctx;
+
+ /**
+ * cb_ctx - Context to use with callback functions
+ */
+ void *cb_ctx;
+
+
+ /* Callbacks to request lower layer driver operations */
+
+ /**
+ * p2p_scan - Request a P2P scan/search
+ * @ctx: Callback context from cb_ctx
+ * @type: Scan type
+ * @freq: Specific frequency (MHz) to scan or 0 for no restriction
+ * @num_req_dev_types: Number of requested device types
+ * @req_dev_types: Array containing requested device types
+ * @dev_id: Device ID to search for or %NULL to find all devices
+ * @pw_id: Device Password ID
+ * Returns: 0 on success, -1 on failure
+ *
+ * This callback function is used to request a P2P scan or search
+ * operation to be completed. Type type argument specifies which type
+ * of scan is to be done. @P2P_SCAN_SOCIAL indicates that only the
+ * social channels (1, 6, 11) should be scanned. @P2P_SCAN_FULL
+ * indicates that all channels are to be scanned.
+ * @P2P_SCAN_SOCIAL_PLUS_ONE request scan of all the social channels
+ * plus one extra channel specified by freq.
+ *
+ * The full scan is used for the initial scan to find group owners from
+ * all. The other types are used during search phase scan of the social
+ * channels (with potential variation if the Listen channel of the
+ * target peer is known or if other channels are scanned in steps).
+ *
+ * The scan results are returned after this call by calling
+ * p2p_scan_res_handler() for each scan result that has a P2P IE and
+ * then calling p2p_scan_res_handled() to indicate that all scan
+ * results have been indicated.
+ */
+ int (*p2p_scan)(void *ctx, enum p2p_scan_type type, int freq,
+ unsigned int num_req_dev_types,
+ const u8 *req_dev_types, const u8 *dev_id, u16 pw_id);
+
+ /**
+ * send_probe_resp - Transmit a Probe Response frame
+ * @ctx: Callback context from cb_ctx
+ * @buf: Probe Response frame (including the header and body)
+ * Returns: 0 on success, -1 on failure
+ *
+ * This function is used to reply to Probe Request frames that were
+ * indicated with a call to p2p_probe_req_rx(). The response is to be
+ * sent on the same channel or to be dropped if the driver is not
+ * anymore listening to Probe Request frames.
+ *
+ * Alternatively, the responsibility for building the Probe Response
+ * frames in Listen state may be in another system component in which
+ * case this function need to be implemented (i.e., the function
+ * pointer can be %NULL). The WPS and P2P IEs to be added for Probe
+ * Response frames in such a case are available from the
+ * start_listen() callback. It should be noted that the received Probe
+ * Request frames must be indicated by calling p2p_probe_req_rx() even
+ * if this send_probe_resp() is not used.
+ */
+ int (*send_probe_resp)(void *ctx, const struct wpabuf *buf);
+
+ /**
+ * send_action - Transmit an Action frame
+ * @ctx: Callback context from cb_ctx
+ * @freq: Frequency in MHz for the channel on which to transmit
+ * @dst: Destination MAC address (Address 1)
+ * @src: Source MAC address (Address 2)
+ * @bssid: BSSID (Address 3)
+ * @buf: Frame body (starting from Category field)
+ * @len: Length of buf in octets
+ * @wait_time: How many msec to wait for a response frame
+ * Returns: 0 on success, -1 on failure
+ *
+ * The Action frame may not be transmitted immediately and the status
+ * of the transmission must be reported by calling
+ * p2p_send_action_cb() once the frame has either been transmitted or
+ * it has been dropped due to excessive retries or other failure to
+ * transmit.
+ */
+ int (*send_action)(void *ctx, unsigned int freq, const u8 *dst,
+ const u8 *src, const u8 *bssid, const u8 *buf,
+ size_t len, unsigned int wait_time);
+
+ /**
+ * send_action_done - Notify that Action frame sequence was completed
+ * @ctx: Callback context from cb_ctx
+ *
+ * This function is called when the Action frame sequence that was
+ * started with send_action() has been completed, i.e., when there is
+ * no need to wait for a response from the destination peer anymore.
+ */
+ void (*send_action_done)(void *ctx);
+
+ /**
+ * start_listen - Start Listen state
+ * @ctx: Callback context from cb_ctx
+ * @freq: Frequency of the listen channel in MHz
+ * @duration: Duration for the Listen state in milliseconds
+ * @probe_resp_ie: IE(s) to be added to Probe Response frames
+ * Returns: 0 on success, -1 on failure
+ *
+ * This Listen state may not start immediately since the driver may
+ * have other pending operations to complete first. Once the Listen
+ * state has started, p2p_listen_cb() must be called to notify the P2P
+ * module. Once the Listen state is stopped, p2p_listen_end() must be
+ * called to notify the P2P module that the driver is not in the Listen
+ * state anymore.
+ *
+ * If the send_probe_resp() is not used for generating the response,
+ * the IEs from probe_resp_ie need to be added to the end of the Probe
+ * Response frame body. If send_probe_resp() is used, the probe_resp_ie
+ * information can be ignored.
+ */
+ int (*start_listen)(void *ctx, unsigned int freq,
+ unsigned int duration,
+ const struct wpabuf *probe_resp_ie);
+ /**
+ * stop_listen - Stop Listen state
+ * @ctx: Callback context from cb_ctx
+ *
+ * This callback can be used to stop a Listen state operation that was
+ * previously requested with start_listen().
+ */
+ void (*stop_listen)(void *ctx);
+
+ /**
+ * get_noa - Get current Notice of Absence attribute payload
+ * @ctx: Callback context from cb_ctx
+ * @interface_addr: P2P Interface Address of the GO
+ * @buf: Buffer for returning NoA
+ * @buf_len: Buffer length in octets
+ * Returns: Number of octets used in buf, 0 to indicate no NoA is being
+ * advertized, or -1 on failure
+ *
+ * This function is used to fetch the current Notice of Absence
+ * attribute value from GO.
+ */
+ int (*get_noa)(void *ctx, const u8 *interface_addr, u8 *buf,
+ size_t buf_len);
+
+ /* Callbacks to notify events to upper layer management entity */
+
+ /**
+ * dev_found - Notification of a found P2P Device
+ * @ctx: Callback context from cb_ctx
+ * @addr: Source address of the message triggering this notification
+ * @info: P2P peer information
+ * @new_device: Inform if the peer is newly found
+ *
+ * This callback is used to notify that a new P2P Device has been
+ * found. This may happen, e.g., during Search state based on scan
+ * results or during Listen state based on receive Probe Request and
+ * Group Owner Negotiation Request.
+ */
+ void (*dev_found)(void *ctx, const u8 *addr,
+ const struct p2p_peer_info *info,
+ int new_device);
+
+ /**
+ * dev_lost - Notification of a lost P2P Device
+ * @ctx: Callback context from cb_ctx
+ * @dev_addr: P2P Device Address of the lost P2P Device
+ *
+ * This callback is used to notify that a P2P Device has been deleted.
+ */
+ void (*dev_lost)(void *ctx, const u8 *dev_addr);
+
+ /**
+ * go_neg_req_rx - Notification of a receive GO Negotiation Request
+ * @ctx: Callback context from cb_ctx
+ * @src: Source address of the message triggering this notification
+ * @dev_passwd_id: WPS Device Password ID
+ *
+ * This callback is used to notify that a P2P Device is requesting
+ * group owner negotiation with us, but we do not have all the
+ * necessary information to start GO Negotiation. This indicates that
+ * the local user has not authorized the connection yet by providing a
+ * PIN or PBC button press. This information can be provided with a
+ * call to p2p_connect().
+ */
+ void (*go_neg_req_rx)(void *ctx, const u8 *src, u16 dev_passwd_id);
+
+ /**
+ * go_neg_completed - Notification of GO Negotiation results
+ * @ctx: Callback context from cb_ctx
+ * @res: GO Negotiation results
+ *
+ * This callback is used to notify that Group Owner Negotiation has
+ * been completed. Non-zero struct p2p_go_neg_results::status indicates
+ * failed negotiation. In case of success, this function is responsible
+ * for creating a new group interface (or using the existing interface
+ * depending on driver features), setting up the group interface in
+ * proper mode based on struct p2p_go_neg_results::role_go and
+ * initializing WPS provisioning either as a Registrar (if GO) or as an
+ * Enrollee. Successful WPS provisioning must be indicated by calling
+ * p2p_wps_success_cb(). The callee is responsible for timing out group
+ * formation if WPS provisioning cannot be completed successfully
+ * within 15 seconds.
+ */
+ void (*go_neg_completed)(void *ctx, struct p2p_go_neg_results *res);
+
+ /**
+ * sd_request - Callback on Service Discovery Request
+ * @ctx: Callback context from cb_ctx
+ * @freq: Frequency (in MHz) of the channel
+ * @sa: Source address of the request
+ * @dialog_token: Dialog token
+ * @update_indic: Service Update Indicator from the source of request
+ * @tlvs: P2P Service Request TLV(s)
+ * @tlvs_len: Length of tlvs buffer in octets
+ *
+ * This callback is used to indicate reception of a service discovery
+ * request. Response to the query must be indicated by calling
+ * p2p_sd_response() with the context information from the arguments to
+ * this callback function.
+ *
+ * This callback handler can be set to %NULL to indicate that service
+ * discovery is not supported.
+ */
+ void (*sd_request)(void *ctx, int freq, const u8 *sa, u8 dialog_token,
+ u16 update_indic, const u8 *tlvs, size_t tlvs_len);
+
+ /**
+ * sd_response - Callback on Service Discovery Response
+ * @ctx: Callback context from cb_ctx
+ * @sa: Source address of the request
+ * @update_indic: Service Update Indicator from the source of response
+ * @tlvs: P2P Service Response TLV(s)
+ * @tlvs_len: Length of tlvs buffer in octets
+ *
+ * This callback is used to indicate reception of a service discovery
+ * response. This callback handler can be set to %NULL if no service
+ * discovery requests are used. The information provided with this call
+ * is replies to the queries scheduled with p2p_sd_request().
+ */
+ void (*sd_response)(void *ctx, const u8 *sa, u16 update_indic,
+ const u8 *tlvs, size_t tlvs_len);
+
+ /**
+ * prov_disc_req - Callback on Provisiong Discovery Request
+ * @ctx: Callback context from cb_ctx
+ * @peer: Source address of the request
+ * @config_methods: Requested WPS Config Method
+ * @dev_addr: P2P Device Address of the found P2P Device
+ * @pri_dev_type: Primary Device Type
+ * @dev_name: Device Name
+ * @supp_config_methods: Supported configuration Methods
+ * @dev_capab: Device Capabilities
+ * @group_capab: Group Capabilities
+ * @group_id: P2P Group ID (or %NULL if not included)
+ * @group_id_len: Length of P2P Group ID
+ *
+ * This callback is used to indicate reception of a Provision Discovery
+ * Request frame that the P2P module accepted.
+ */
+ void (*prov_disc_req)(void *ctx, const u8 *peer, u16 config_methods,
+ const u8 *dev_addr, const u8 *pri_dev_type,
+ const char *dev_name, u16 supp_config_methods,
+ u8 dev_capab, u8 group_capab,
+ const u8 *group_id, size_t group_id_len);
+
+ /**
+ * prov_disc_resp - Callback on Provisiong Discovery Response
+ * @ctx: Callback context from cb_ctx
+ * @peer: Source address of the response
+ * @config_methods: Value from p2p_prov_disc_req() or 0 on failure
+ *
+ * This callback is used to indicate reception of a Provision Discovery
+ * Response frame for a pending request scheduled with
+ * p2p_prov_disc_req(). This callback handler can be set to %NULL if
+ * provision discovery is not used.
+ */
+ void (*prov_disc_resp)(void *ctx, const u8 *peer, u16 config_methods);
+
+ /**
+ * prov_disc_fail - Callback on Provision Discovery failure
+ * @ctx: Callback context from cb_ctx
+ * @peer: Source address of the response
+ * @status: Cause of failure, will not be %P2P_PROV_DISC_SUCCESS
+ *
+ * This callback is used to indicate either a failure or no response
+ * to an earlier provision discovery request.
+ *
+ * This callback handler can be set to %NULL if provision discovery
+ * is not used or failures do not need to be indicated.
+ */
+ void (*prov_disc_fail)(void *ctx, const u8 *peer,
+ enum p2p_prov_disc_status status);
+
+ /**
+ * invitation_process - Optional callback for processing Invitations
+ * @ctx: Callback context from cb_ctx
+ * @sa: Source address of the Invitation Request
+ * @bssid: P2P Group BSSID from the request or %NULL if not included
+ * @go_dev_addr: GO Device Address from P2P Group ID
+ * @ssid: SSID from P2P Group ID
+ * @ssid_len: Length of ssid buffer in octets
+ * @go: Variable for returning whether the local end is GO in the group
+ * @group_bssid: Buffer for returning P2P Group BSSID (if local end GO)
+ * @force_freq: Variable for returning forced frequency for the group
+ * @persistent_group: Whether this is an invitation to reinvoke a
+ * persistent group (instead of invitation to join an active
+ * group)
+ * Returns: Status code (P2P_SC_*)
+ *
+ * This optional callback can be used to implement persistent reconnect
+ * by allowing automatic restarting of persistent groups without user
+ * interaction. If this callback is not implemented (i.e., is %NULL),
+ * the received Invitation Request frames are replied with
+ * %P2P_SC_REQ_RECEIVED status and indicated to upper layer with the
+ * invitation_result() callback.
+ *
+ * If the requested parameters are acceptable and the group is known,
+ * %P2P_SC_SUCCESS may be returned. If the requested group is unknown,
+ * %P2P_SC_FAIL_UNKNOWN_GROUP should be returned. %P2P_SC_REQ_RECEIVED
+ * can be returned if there is not enough data to provide immediate
+ * response, i.e., if some sort of user interaction is needed. The
+ * invitation_received() callback will be called in that case
+ * immediately after this call.
+ */
+ u8 (*invitation_process)(void *ctx, const u8 *sa, const u8 *bssid,
+ const u8 *go_dev_addr, const u8 *ssid,
+ size_t ssid_len, int *go, u8 *group_bssid,
+ int *force_freq, int persistent_group);
+
+ /**
+ * invitation_received - Callback on Invitation Request RX
+ * @ctx: Callback context from cb_ctx
+ * @sa: Source address of the Invitation Request
+ * @bssid: P2P Group BSSID or %NULL if not received
+ * @ssid: SSID of the group
+ * @ssid_len: Length of ssid in octets
+ * @go_dev_addr: GO Device Address
+ * @status: Response Status
+ * @op_freq: Operational frequency for the group
+ *
+ * This callback is used to indicate sending of an Invitation Response
+ * for a received Invitation Request. If status == 0 (success), the
+ * upper layer code is responsible for starting the group. status == 1
+ * indicates need to get user authorization for the group. Other status
+ * values indicate that the invitation request was rejected.
+ */
+ void (*invitation_received)(void *ctx, const u8 *sa, const u8 *bssid,
+ const u8 *ssid, size_t ssid_len,
+ const u8 *go_dev_addr, u8 status,
+ int op_freq);
+
+ /**
+ * invitation_result - Callback on Invitation result
+ * @ctx: Callback context from cb_ctx
+ * @status: Negotiation result (Status Code)
+ * @bssid: P2P Group BSSID or %NULL if not received
+ *
+ * This callback is used to indicate result of an Invitation procedure
+ * started with a call to p2p_invite(). The indicated status code is
+ * the value received from the peer in Invitation Response with 0
+ * (P2P_SC_SUCCESS) indicating success or -1 to indicate a timeout or a
+ * local failure in transmitting the Invitation Request.
+ */
+ void (*invitation_result)(void *ctx, int status, const u8 *bssid);
+
+ /**
+ * go_connected - Check whether we are connected to a GO
+ * @ctx: Callback context from cb_ctx
+ * @dev_addr: P2P Device Address of a GO
+ * Returns: 1 if we are connected as a P2P client to the specified GO
+ * or 0 if not.
+ */
+ int (*go_connected)(void *ctx, const u8 *dev_addr);
+};
+
+
+/* P2P module initialization/deinitialization */
+
+/**
+ * p2p_init - Initialize P2P module
+ * @cfg: P2P module configuration
+ * Returns: Pointer to private data or %NULL on failure
+ *
+ * This function is used to initialize global P2P module context (one per
+ * device). The P2P module will keep a copy of the configuration data, so the
+ * caller does not need to maintain this structure. However, the callback
+ * functions and the context parameters to them must be kept available until
+ * the P2P module is deinitialized with p2p_deinit().
+ */
+struct p2p_data * p2p_init(const struct p2p_config *cfg);
+
+/**
+ * p2p_deinit - Deinitialize P2P module
+ * @p2p: P2P module context from p2p_init()
+ */
+void p2p_deinit(struct p2p_data *p2p);
+
+/**
+ * p2p_flush - Flush P2P module state
+ * @p2p: P2P module context from p2p_init()
+ *
+ * This command removes the P2P module state like peer device entries.
+ */
+void p2p_flush(struct p2p_data *p2p);
+
+/**
+ * p2p_unauthorize - Unauthorize the specified peer device
+ * @p2p: P2P module context from p2p_init()
+ * @addr: P2P peer entry to be unauthorized
+ * Returns: 0 on success, -1 on failure
+ *
+ * This command removes any connection authorization from the specified P2P
+ * peer device address. This can be used, e.g., to cancel effect of a previous
+ * p2p_authorize() or p2p_connect() call that has not yet resulted in completed
+ * GO Negotiation.
+ */
+int p2p_unauthorize(struct p2p_data *p2p, const u8 *addr);
+
+/**
+ * p2p_set_dev_name - Set device name
+ * @p2p: P2P module context from p2p_init()
+ * Returns: 0 on success, -1 on failure
+ *
+ * This function can be used to update the P2P module configuration with
+ * information that was not available at the time of the p2p_init() call.
+ */
+int p2p_set_dev_name(struct p2p_data *p2p, const char *dev_name);
+
+int p2p_set_manufacturer(struct p2p_data *p2p, const char *manufacturer);
+int p2p_set_model_name(struct p2p_data *p2p, const char *model_name);
+int p2p_set_model_number(struct p2p_data *p2p, const char *model_number);
+int p2p_set_serial_number(struct p2p_data *p2p, const char *serial_number);
+
+void p2p_set_config_methods(struct p2p_data *p2p, u16 config_methods);
+void p2p_set_uuid(struct p2p_data *p2p, const u8 *uuid);
+
+/**
+ * p2p_set_pri_dev_type - Set primary device type
+ * @p2p: P2P module context from p2p_init()
+ * Returns: 0 on success, -1 on failure
+ *
+ * This function can be used to update the P2P module configuration with
+ * information that was not available at the time of the p2p_init() call.
+ */
+int p2p_set_pri_dev_type(struct p2p_data *p2p, const u8 *pri_dev_type);
+
+/**
+ * p2p_set_sec_dev_types - Set secondary device types
+ * @p2p: P2P module context from p2p_init()
+ * Returns: 0 on success, -1 on failure
+ *
+ * This function can be used to update the P2P module configuration with
+ * information that was not available at the time of the p2p_init() call.
+ */
+int p2p_set_sec_dev_types(struct p2p_data *p2p, const u8 dev_types[][8],
+ size_t num_dev_types);
+
+int p2p_set_country(struct p2p_data *p2p, const char *country);
+
+
+/* Commands from upper layer management entity */
+
+enum p2p_discovery_type {
+ P2P_FIND_START_WITH_FULL,
+ P2P_FIND_ONLY_SOCIAL,
+ P2P_FIND_PROGRESSIVE
+};
+
+/**
+ * p2p_find - Start P2P Find (Device Discovery)
+ * @p2p: P2P module context from p2p_init()
+ * @timeout: Timeout for find operation in seconds or 0 for no timeout
+ * @type: Device Discovery type
+ * @num_req_dev_types: Number of requested device types
+ * @req_dev_types: Requested device types array, must be an array
+ * containing num_req_dev_types * WPS_DEV_TYPE_LEN bytes; %NULL if no
+ * requested device types.
+ * @dev_id: Device ID to search for or %NULL to find all devices
+ * @search_delay: Extra delay in milliseconds between search iterations
+ * Returns: 0 on success, -1 on failure
+ */
+int p2p_find(struct p2p_data *p2p, unsigned int timeout,
+ enum p2p_discovery_type type,
+ unsigned int num_req_dev_types, const u8 *req_dev_types,
+ const u8 *dev_id, unsigned int search_delay);
+
+/**
+ * p2p_stop_find - Stop P2P Find (Device Discovery)
+ * @p2p: P2P module context from p2p_init()
+ */
+void p2p_stop_find(struct p2p_data *p2p);
+
+/**
+ * p2p_stop_find_for_freq - Stop P2P Find for next oper on specific freq
+ * @p2p: P2P module context from p2p_init()
+ * @freq: Frequency in MHz for next operation
+ *
+ * This is like p2p_stop_find(), but Listen state is not stopped if we are
+ * already on the same frequency.
+ */
+void p2p_stop_find_for_freq(struct p2p_data *p2p, int freq);
+
+/**
+ * p2p_listen - Start P2P Listen state for specified duration
+ * @p2p: P2P module context from p2p_init()
+ * @timeout: Listen state duration in milliseconds
+ * Returns: 0 on success, -1 on failure
+ *
+ * This function can be used to request the P2P module to keep the device
+ * discoverable on the listen channel for an extended set of time. At least in
+ * its current form, this is mainly used for testing purposes and may not be of
+ * much use for normal P2P operations.
+ */
+int p2p_listen(struct p2p_data *p2p, unsigned int timeout);
+
+/**
+ * p2p_connect - Start P2P group formation (GO negotiation)
+ * @p2p: P2P module context from p2p_init()
+ * @peer_addr: MAC address of the peer P2P client
+ * @wps_method: WPS method to be used in provisioning
+ * @go_intent: Local GO intent value (1..15)
+ * @own_interface_addr: Intended interface address to use with the group
+ * @force_freq: The only allowed channel frequency in MHz or 0
+ * @persistent_group: Whether to create a persistent group (0 = no, 1 =
+ * persistent group without persistent reconnect, 2 = persistent group with
+ * persistent reconnect)
+ * @force_ssid: Forced SSID for the group if we become GO or %NULL to generate
+ * a new SSID
+ * @force_ssid_len: Length of $force_ssid buffer
+ * @pd_before_go_neg: Whether to send Provision Discovery prior to GO
+ * Negotiation as an interoperability workaround when initiating group
+ * formation
+ * @pref_freq: Preferred operating frequency in MHz or 0 (this is only used if
+ * force_freq == 0)
+ * Returns: 0 on success, -1 on failure
+ */
+int p2p_connect(struct p2p_data *p2p, const u8 *peer_addr,
+ enum p2p_wps_method wps_method,
+ int go_intent, const u8 *own_interface_addr,
+ unsigned int force_freq, int persistent_group,
+ const u8 *force_ssid, size_t force_ssid_len,
+ int pd_before_go_neg, unsigned int pref_freq);
+
+/**
+ * p2p_authorize - Authorize P2P group formation (GO negotiation)
+ * @p2p: P2P module context from p2p_init()
+ * @peer_addr: MAC address of the peer P2P client
+ * @wps_method: WPS method to be used in provisioning
+ * @go_intent: Local GO intent value (1..15)
+ * @own_interface_addr: Intended interface address to use with the group
+ * @force_freq: The only allowed channel frequency in MHz or 0
+ * @persistent_group: Whether to create a persistent group (0 = no, 1 =
+ * persistent group without persistent reconnect, 2 = persistent group with
+ * persistent reconnect)
+ * @force_ssid: Forced SSID for the group if we become GO or %NULL to generate
+ * a new SSID
+ * @force_ssid_len: Length of $force_ssid buffer
+ * @pref_freq: Preferred operating frequency in MHz or 0 (this is only used if
+ * force_freq == 0)
+ * Returns: 0 on success, -1 on failure
+ *
+ * This is like p2p_connect(), but the actual group negotiation is not
+ * initiated automatically, i.e., the other end is expected to do that.
+ */
+int p2p_authorize(struct p2p_data *p2p, const u8 *peer_addr,
+ enum p2p_wps_method wps_method,
+ int go_intent, const u8 *own_interface_addr,
+ unsigned int force_freq, int persistent_group,
+ const u8 *force_ssid, size_t force_ssid_len,
+ unsigned int pref_freq);
+
+/**
+ * p2p_reject - Reject peer device (explicitly block connection attempts)
+ * @p2p: P2P module context from p2p_init()
+ * @peer_addr: MAC address of the peer P2P client
+ * Returns: 0 on success, -1 on failure
+ */
+int p2p_reject(struct p2p_data *p2p, const u8 *peer_addr);
+
+/**
+ * p2p_prov_disc_req - Send Provision Discovery Request
+ * @p2p: P2P module context from p2p_init()
+ * @peer_addr: MAC address of the peer P2P client
+ * @config_methods: WPS Config Methods value (only one bit set)
+ * @join: Whether this is used by a client joining an active group
+ * @force_freq: Forced TX frequency for the frame (mainly for the join case)
+ * @user_initiated_pd: Flag to indicate if initiated by user or not
+ * Returns: 0 on success, -1 on failure
+ *
+ * This function can be used to request a discovered P2P peer to display a PIN
+ * (config_methods = WPS_CONFIG_DISPLAY) or be prepared to enter a PIN from us
+ * (config_methods = WPS_CONFIG_KEYPAD). The Provision Discovery Request frame
+ * is transmitted once immediately and if no response is received, the frame
+ * will be sent again whenever the target device is discovered during device
+ * dsicovery (start with a p2p_find() call). Response from the peer is
+ * indicated with the p2p_config::prov_disc_resp() callback.
+ */
+int p2p_prov_disc_req(struct p2p_data *p2p, const u8 *peer_addr,
+ u16 config_methods, int join, int force_freq,
+ int user_initiated_pd);
+
+/**
+ * p2p_sd_request - Schedule a service discovery query
+ * @p2p: P2P module context from p2p_init()
+ * @dst: Destination peer or %NULL to apply for all peers
+ * @tlvs: P2P Service Query TLV(s)
+ * Returns: Reference to the query or %NULL on failure
+ *
+ * Response to the query is indicated with the p2p_config::sd_response()
+ * callback.
+ */
+void * p2p_sd_request(struct p2p_data *p2p, const u8 *dst,
+ const struct wpabuf *tlvs);
+
+#ifdef CONFIG_WIFI_DISPLAY
+void * p2p_sd_request_wfd(struct p2p_data *p2p, const u8 *dst,
+ const struct wpabuf *tlvs);
+#endif /* CONFIG_WIFI_DISPLAY */
+
+/**
+ * p2p_sd_cancel_request - Cancel a pending service discovery query
+ * @p2p: P2P module context from p2p_init()
+ * @req: Query reference from p2p_sd_request()
+ * Returns: 0 if request for cancelled; -1 if not found
+ */
+int p2p_sd_cancel_request(struct p2p_data *p2p, void *req);
+
+/**
+ * p2p_sd_response - Send response to a service discovery query
+ * @p2p: P2P module context from p2p_init()
+ * @freq: Frequency from p2p_config::sd_request() callback
+ * @dst: Destination address from p2p_config::sd_request() callback
+ * @dialog_token: Dialog token from p2p_config::sd_request() callback
+ * @resp_tlvs: P2P Service Response TLV(s)
+ *
+ * This function is called as a response to the request indicated with
+ * p2p_config::sd_request() callback.
+ */
+void p2p_sd_response(struct p2p_data *p2p, int freq, const u8 *dst,
+ u8 dialog_token, const struct wpabuf *resp_tlvs);
+
+/**
+ * p2p_sd_service_update - Indicate a change in local services
+ * @p2p: P2P module context from p2p_init()
+ *
+ * This function needs to be called whenever there is a change in availability
+ * of the local services. This will increment the Service Update Indicator
+ * value which will be used in SD Request and Response frames.
+ */
+void p2p_sd_service_update(struct p2p_data *p2p);
+
+
+enum p2p_invite_role {
+ P2P_INVITE_ROLE_GO,
+ P2P_INVITE_ROLE_ACTIVE_GO,
+ P2P_INVITE_ROLE_CLIENT
+};
+
+/**
+ * p2p_invite - Invite a P2P Device into a group
+ * @p2p: P2P module context from p2p_init()
+ * @peer: Device Address of the peer P2P Device
+ * @role: Local role in the group
+ * @bssid: Group BSSID or %NULL if not known
+ * @ssid: Group SSID
+ * @ssid_len: Length of ssid in octets
+ * @force_freq: The only allowed channel frequency in MHz or 0
+ * @go_dev_addr: Forced GO Device Address or %NULL if none
+ * @persistent_group: Whether this is to reinvoke a persistent group
+ * Returns: 0 on success, -1 on failure
+ */
+int p2p_invite(struct p2p_data *p2p, const u8 *peer, enum p2p_invite_role role,
+ const u8 *bssid, const u8 *ssid, size_t ssid_len,
+ unsigned int force_freq, const u8 *go_dev_addr,
+ int persistent_group);
+
+/**
+ * p2p_presence_req - Request GO presence
+ * @p2p: P2P module context from p2p_init()
+ * @go_interface_addr: GO P2P Interface Address
+ * @own_interface_addr: Own P2P Interface Address for this group
+ * @freq: Group operating frequence (in MHz)
+ * @duration1: Preferred presence duration in microseconds
+ * @interval1: Preferred presence interval in microseconds
+ * @duration2: Acceptable presence duration in microseconds
+ * @interval2: Acceptable presence interval in microseconds
+ * Returns: 0 on success, -1 on failure
+ *
+ * If both duration and interval values are zero, the parameter pair is not
+ * specified (i.e., to remove Presence Request, use duration1 = interval1 = 0).
+ */
+int p2p_presence_req(struct p2p_data *p2p, const u8 *go_interface_addr,
+ const u8 *own_interface_addr, unsigned int freq,
+ u32 duration1, u32 interval1, u32 duration2,
+ u32 interval2);
+
+/**
+ * p2p_ext_listen - Set Extended Listen Timing
+ * @p2p: P2P module context from p2p_init()
+ * @freq: Group operating frequence (in MHz)
+ * @period: Availability period in milliseconds (1-65535; 0 to disable)
+ * @interval: Availability interval in milliseconds (1-65535; 0 to disable)
+ * Returns: 0 on success, -1 on failure
+ *
+ * This function can be used to enable or disable (period = interval = 0)
+ * Extended Listen Timing. When enabled, the P2P Device will become
+ * discoverable (go into Listen State) every @interval milliseconds for at
+ * least @period milliseconds.
+ */
+int p2p_ext_listen(struct p2p_data *p2p, unsigned int period,
+ unsigned int interval);
+
+/* Event notifications from upper layer management operations */
+
+/**
+ * p2p_wps_success_cb - Report successfully completed WPS provisioning
+ * @p2p: P2P module context from p2p_init()
+ * @mac_addr: Peer address
+ *
+ * This function is used to report successfully completed WPS provisioning
+ * during group formation in both GO/Registrar and client/Enrollee roles.
+ */
+void p2p_wps_success_cb(struct p2p_data *p2p, const u8 *mac_addr);
+
+/**
+ * p2p_group_formation_failed - Report failed WPS provisioning
+ * @p2p: P2P module context from p2p_init()
+ *
+ * This function is used to report failed group formation. This can happen
+ * either due to failed WPS provisioning or due to 15 second timeout during
+ * the provisioning phase.
+ */
+void p2p_group_formation_failed(struct p2p_data *p2p);
+
+/**
+ * p2p_get_provisioning_info - Get any stored provisioning info
+ * @p2p: P2P module context from p2p_init()
+ * @addr: Peer P2P Device Address
+ * Returns: WPS provisioning information (WPS config method) or 0 if no
+ * information is available
+ *
+ * This function is used to retrieve stored WPS provisioning info for the given
+ * peer.
+ */
+u16 p2p_get_provisioning_info(struct p2p_data *p2p, const u8 *addr);
+
+/**
+ * p2p_clear_provisioning_info - Clear any stored provisioning info
+ * @p2p: P2P module context from p2p_init()
+ * @iface_addr: Peer P2P Device Address
+ *
+ * This function is used to clear stored WPS provisioning info for the given
+ * peer.
+ */
+void p2p_clear_provisioning_info(struct p2p_data *p2p, const u8 *addr);
+
+
+/* Event notifications from lower layer driver operations */
+
+/**
+ * enum p2p_probe_req_status
+ *
+ * @P2P_PREQ_MALFORMED: frame was not well-formed
+ * @P2P_PREQ_NOT_LISTEN: device isn't in listen state, frame ignored
+ * @P2P_PREQ_NOT_P2P: frame was not a P2P probe request
+ * @P2P_PREQ_P2P_NOT_PROCESSED: frame was P2P but wasn't processed
+ * @P2P_PREQ_P2P_PROCESSED: frame has been processed by P2P
+ */
+enum p2p_probe_req_status {
+ P2P_PREQ_MALFORMED,
+ P2P_PREQ_NOT_LISTEN,
+ P2P_PREQ_NOT_P2P,
+ P2P_PREQ_NOT_PROCESSED,
+ P2P_PREQ_PROCESSED
+};
+
+/**
+ * p2p_probe_req_rx - Report reception of a Probe Request frame
+ * @p2p: P2P module context from p2p_init()
+ * @addr: Source MAC address
+ * @dst: Destination MAC address if available or %NULL
+ * @bssid: BSSID if available or %NULL
+ * @ie: Information elements from the Probe Request frame body
+ * @ie_len: Length of ie buffer in octets
+ * Returns: value indicating the type and status of the probe request
+ */
+enum p2p_probe_req_status
+p2p_probe_req_rx(struct p2p_data *p2p, const u8 *addr, const u8 *dst,
+ const u8 *bssid, const u8 *ie, size_t ie_len);
+
+/**
+ * p2p_rx_action - Report received Action frame
+ * @p2p: P2P module context from p2p_init()
+ * @da: Destination address of the received Action frame
+ * @sa: Source address of the received Action frame
+ * @bssid: Address 3 of the received Action frame
+ * @category: Category of the received Action frame
+ * @data: Action frame body after the Category field
+ * @len: Length of the data buffer in octets
+ * @freq: Frequency (in MHz) on which the frame was received
+ */
+void p2p_rx_action(struct p2p_data *p2p, const u8 *da, const u8 *sa,
+ const u8 *bssid, u8 category,
+ const u8 *data, size_t len, int freq);
+
+/**
+ * p2p_scan_res_handler - Indicate a P2P scan results
+ * @p2p: P2P module context from p2p_init()
+ * @bssid: BSSID of the scan result
+ * @freq: Frequency of the channel on which the device was found in MHz
+ * @age: Age of the scan result in milliseconds
+ * @level: Signal level (signal strength of the received Beacon/Probe Response
+ * frame)
+ * @ies: Pointer to IEs from the scan result
+ * @ies_len: Length of the ies buffer
+ * Returns: 0 to continue or 1 to stop scan result indication
+ *
+ * This function is called to indicate a scan result entry with P2P IE from a
+ * scan requested with struct p2p_config::p2p_scan(). This can be called during
+ * the actual scan process (i.e., whenever a new device is found) or as a
+ * sequence of calls after the full scan has been completed. The former option
+ * can result in optimized operations, but may not be supported by all
+ * driver/firmware designs. The ies buffer need to include at least the P2P IE,
+ * but it is recommended to include all IEs received from the device. The
+ * caller does not need to check that the IEs contain a P2P IE before calling
+ * this function since frames will be filtered internally if needed.
+ *
+ * This function will return 1 if it wants to stop scan result iteration (and
+ * scan in general if it is still in progress). This is used to allow faster
+ * start of a pending operation, e.g., to start a pending GO negotiation.
+ */
+int p2p_scan_res_handler(struct p2p_data *p2p, const u8 *bssid, int freq,
+ unsigned int age, int level, const u8 *ies,
+ size_t ies_len);
+
+/**
+ * p2p_scan_res_handled - Indicate end of scan results
+ * @p2p: P2P module context from p2p_init()
+ *
+ * This function is called to indicate that all P2P scan results from a scan
+ * have been reported with zero or more calls to p2p_scan_res_handler(). This
+ * function must be called as a response to successful
+ * struct p2p_config::p2p_scan() call if none of the p2p_scan_res_handler()
+ * calls stopped iteration.
+ */
+void p2p_scan_res_handled(struct p2p_data *p2p);
+
+enum p2p_send_action_result {
+ P2P_SEND_ACTION_SUCCESS /* Frame was send and acknowledged */,
+ P2P_SEND_ACTION_NO_ACK /* Frame was sent, but not acknowledged */,
+ P2P_SEND_ACTION_FAILED /* Frame was not sent due to a failure */
+};
+
+/**
+ * p2p_send_action_cb - Notify TX status of an Action frame
+ * @p2p: P2P module context from p2p_init()
+ * @freq: Channel frequency in MHz
+ * @dst: Destination MAC address (Address 1)
+ * @src: Source MAC address (Address 2)
+ * @bssid: BSSID (Address 3)
+ * @result: Result of the transmission attempt
+ *
+ * This function is used to indicate the result of an Action frame transmission
+ * that was requested with struct p2p_config::send_action() callback.
+ */
+void p2p_send_action_cb(struct p2p_data *p2p, unsigned int freq, const u8 *dst,
+ const u8 *src, const u8 *bssid,
+ enum p2p_send_action_result result);
+
+/**
+ * p2p_listen_cb - Indicate the start of a requested Listen state
+ * @p2p: P2P module context from p2p_init()
+ * @freq: Listen channel frequency in MHz
+ * @duration: Duration for the Listen state in milliseconds
+ *
+ * This function is used to indicate that a Listen state requested with
+ * struct p2p_config::start_listen() callback has started.
+ */
+void p2p_listen_cb(struct p2p_data *p2p, unsigned int freq,
+ unsigned int duration);
+
+/**
+ * p2p_listen_end - Indicate the end of a requested Listen state
+ * @p2p: P2P module context from p2p_init()
+ * @freq: Listen channel frequency in MHz
+ * Returns: 0 if no operations were started, 1 if an operation was started
+ *
+ * This function is used to indicate that a Listen state requested with
+ * struct p2p_config::start_listen() callback has ended.
+ */
+int p2p_listen_end(struct p2p_data *p2p, unsigned int freq);
+
+void p2p_deauth_notif(struct p2p_data *p2p, const u8 *bssid, u16 reason_code,
+ const u8 *ie, size_t ie_len);
+
+void p2p_disassoc_notif(struct p2p_data *p2p, const u8 *bssid, u16 reason_code,
+ const u8 *ie, size_t ie_len);
+
+
+/* Per-group P2P state for GO */
+
+struct p2p_group;
+
+/**
+ * struct p2p_group_config - P2P group configuration
+ *
+ * This configuration is provided to the P2P module during initialization of
+ * the per-group information with p2p_group_init().
+ */
+struct p2p_group_config {
+ /**
+ * persistent_group - Whether the group is persistent
+ * 0 = not a persistent group
+ * 1 = persistent group without persistent reconnect
+ * 2 = persistent group with persistent reconnect
+ */
+ int persistent_group;
+
+ /**
+ * interface_addr - P2P Interface Address of the group
+ */
+ u8 interface_addr[ETH_ALEN];
+
+ /**
+ * max_clients - Maximum number of clients in the group
+ */
+ unsigned int max_clients;
+
+ /**
+ * ssid - Group SSID
+ */
+ u8 ssid[32];
+
+ /**
+ * ssid_len - Length of SSID
+ */
+ size_t ssid_len;
+
+ /**
+ * cb_ctx - Context to use with callback functions
+ */
+ void *cb_ctx;
+
+ /**
+ * ie_update - Notification of IE update
+ * @ctx: Callback context from cb_ctx
+ * @beacon_ies: P2P IE for Beacon frames or %NULL if no change
+ * @proberesp_ies: P2P Ie for Probe Response frames
+ *
+ * P2P module uses this callback function to notify whenever the P2P IE
+ * in Beacon or Probe Response frames should be updated based on group
+ * events.
+ *
+ * The callee is responsible for freeing the returned buffer(s) with
+ * wpabuf_free().
+ */
+ void (*ie_update)(void *ctx, struct wpabuf *beacon_ies,
+ struct wpabuf *proberesp_ies);
+
+ /**
+ * idle_update - Notification of changes in group idle state
+ * @ctx: Callback context from cb_ctx
+ * @idle: Whether the group is idle (no associated stations)
+ */
+ void (*idle_update)(void *ctx, int idle);
+};
+
+/**
+ * p2p_group_init - Initialize P2P group
+ * @p2p: P2P module context from p2p_init()
+ * @config: P2P group configuration (will be freed by p2p_group_deinit())
+ * Returns: Pointer to private data or %NULL on failure
+ *
+ * This function is used to initialize per-group P2P module context. Currently,
+ * this is only used to manage GO functionality and P2P clients do not need to
+ * create an instance of this per-group information.
+ */
+struct p2p_group * p2p_group_init(struct p2p_data *p2p,
+ struct p2p_group_config *config);
+
+/**
+ * p2p_group_deinit - Deinitialize P2P group
+ * @group: P2P group context from p2p_group_init()
+ */
+void p2p_group_deinit(struct p2p_group *group);
+
+/**
+ * p2p_group_notif_assoc - Notification of P2P client association with GO
+ * @group: P2P group context from p2p_group_init()
+ * @addr: Interface address of the P2P client
+ * @ie: IEs from the (Re)association Request frame
+ * @len: Length of the ie buffer in octets
+ * Returns: 0 on success, -1 on failure
+ */
+int p2p_group_notif_assoc(struct p2p_group *group, const u8 *addr,
+ const u8 *ie, size_t len);
+
+/**
+ * p2p_group_assoc_resp_ie - Build P2P IE for (re)association response
+ * @group: P2P group context from p2p_group_init()
+ * @status: Status value (P2P_SC_SUCCESS if association succeeded)
+ * Returns: P2P IE for (Re)association Response or %NULL on failure
+ *
+ * The caller is responsible for freeing the returned buffer with
+ * wpabuf_free().
+ */
+struct wpabuf * p2p_group_assoc_resp_ie(struct p2p_group *group, u8 status);
+
+/**
+ * p2p_group_notif_disassoc - Notification of P2P client disassociation from GO
+ * @group: P2P group context from p2p_group_init()
+ * @addr: Interface address of the P2P client
+ */
+void p2p_group_notif_disassoc(struct p2p_group *group, const u8 *addr);
+
+/**
+ * p2p_group_notif_formation_done - Notification of completed group formation
+ * @group: P2P group context from p2p_group_init()
+ */
+void p2p_group_notif_formation_done(struct p2p_group *group);
+
+/**
+ * p2p_group_notif_noa - Notification of NoA change
+ * @group: P2P group context from p2p_group_init()
+ * @noa: Notice of Absence attribute payload, %NULL if none
+ * @noa_len: Length of noa buffer in octets
+ * Returns: 0 on success, -1 on failure
+ *
+ * Notify the P2P group management about a new NoA contents. This will be
+ * inserted into the P2P IEs in Beacon and Probe Response frames with rest of
+ * the group information.
+ */
+int p2p_group_notif_noa(struct p2p_group *group, const u8 *noa,
+ size_t noa_len);
+
+/**
+ * p2p_group_match_dev_type - Match device types in group with requested type
+ * @group: P2P group context from p2p_group_init()
+ * @wps: WPS TLVs from Probe Request frame (concatenated WPS IEs)
+ * Returns: 1 on match, 0 on mismatch
+ *
+ * This function can be used to match the Requested Device Type attribute in
+ * WPS IE with the device types of a group member for deciding whether a GO
+ * should reply to a Probe Request frame. Match will be reported if the WPS IE
+ * is not requested any specific device type.
+ */
+int p2p_group_match_dev_type(struct p2p_group *group, struct wpabuf *wps);
+
+/**
+ * p2p_group_match_dev_id - Match P2P Device Address in group with requested device id
+ */
+int p2p_group_match_dev_id(struct p2p_group *group, struct wpabuf *p2p);
+
+/**
+ * p2p_group_go_discover - Send GO Discoverability Request to a group client
+ * @group: P2P group context from p2p_group_init()
+ * Returns: 0 on success (frame scheduled); -1 if client was not found
+ */
+int p2p_group_go_discover(struct p2p_group *group, const u8 *dev_id,
+ const u8 *searching_dev, int rx_freq);
+
+
+/* Generic helper functions */
+
+/**
+ * p2p_ie_text - Build text format description of P2P IE
+ * @p2p_ie: P2P IE
+ * @buf: Buffer for returning text
+ * @end: Pointer to the end of the buf area
+ * Returns: Number of octets written to the buffer or -1 on failure
+ *
+ * This function can be used to parse P2P IE contents into text format
+ * field=value lines.
+ */
+int p2p_ie_text(struct wpabuf *p2p_ie, char *buf, char *end);
+
+/**
+ * p2p_scan_result_text - Build text format description of P2P IE
+ * @ies: Information elements from scan results
+ * @ies_len: ies buffer length in octets
+ * @buf: Buffer for returning text
+ * @end: Pointer to the end of the buf area
+ * Returns: Number of octets written to the buffer or -1 on failure
+ *
+ * This function can be used to parse P2P IE contents into text format
+ * field=value lines.
+ */
+int p2p_scan_result_text(const u8 *ies, size_t ies_len, char *buf, char *end);
+
+/**
+ * p2p_parse_dev_addr_in_p2p_ie - Parse P2P Device Address from a concatenated
+ * P2P IE
+ * @p2p_ie: P2P IE
+ * @dev_addr: Buffer for returning P2P Device Address
+ * Returns: 0 on success or -1 if P2P Device Address could not be parsed
+ */
+int p2p_parse_dev_addr_in_p2p_ie(struct wpabuf *p2p_ie, u8 *dev_addr);
+
+/**
+ * p2p_parse_dev_addr - Parse P2P Device Address from P2P IE(s)
+ * @ies: Information elements from scan results
+ * @ies_len: ies buffer length in octets
+ * @dev_addr: Buffer for returning P2P Device Address
+ * Returns: 0 on success or -1 if P2P Device Address could not be parsed
+ */
+int p2p_parse_dev_addr(const u8 *ies, size_t ies_len, u8 *dev_addr);
+
+/**
+ * p2p_assoc_req_ie - Build P2P IE for (Re)Association Request frame
+ * @p2p: P2P module context from p2p_init()
+ * @bssid: BSSID
+ * @buf: Buffer for writing the P2P IE
+ * @len: Maximum buf length in octets
+ * @p2p_group: Whether this is for association with a P2P GO
+ * @p2p_ie: Reassembled P2P IE data from scan results or %NULL if none
+ * Returns: Number of octets written into buf or -1 on failure
+ */
+int p2p_assoc_req_ie(struct p2p_data *p2p, const u8 *bssid, u8 *buf,
+ size_t len, int p2p_group, struct wpabuf *p2p_ie);
+
+/**
+ * p2p_scan_ie - Build P2P IE for Probe Request
+ * @p2p: P2P module context from p2p_init()
+ * @ies: Buffer for writing P2P IE
+ * @dev_id: Device ID to search for or %NULL for any
+ */
+void p2p_scan_ie(struct p2p_data *p2p, struct wpabuf *ies, const u8 *dev_id);
+
+/**
+ * p2p_scan_ie_buf_len - Get maximum buffer length needed for p2p_scan_ie
+ * @p2p: P2P module context from p2p_init()
+ * Returns: Number of octets that p2p_scan_ie() may add to the buffer
+ */
+size_t p2p_scan_ie_buf_len(struct p2p_data *p2p);
+
+/**
+ * p2p_go_params - Generate random P2P group parameters
+ * @p2p: P2P module context from p2p_init()
+ * @params: Buffer for parameters
+ * Returns: 0 on success, -1 on failure
+ */
+int p2p_go_params(struct p2p_data *p2p, struct p2p_go_neg_results *params);
+
+/**
+ * p2p_get_group_capab - Get Group Capability from P2P IE data
+ * @p2p_ie: P2P IE(s) contents
+ * Returns: Group Capability
+ */
+u8 p2p_get_group_capab(const struct wpabuf *p2p_ie);
+
+/**
+ * p2p_get_cross_connect_disallowed - Does WLAN AP disallows cross connection
+ * @p2p_ie: P2P IE(s) contents
+ * Returns: 0 if cross connection is allow, 1 if not
+ */
+int p2p_get_cross_connect_disallowed(const struct wpabuf *p2p_ie);
+
+/**
+ * p2p_get_go_dev_addr - Get P2P Device Address from P2P IE data
+ * @p2p_ie: P2P IE(s) contents
+ * Returns: Pointer to P2P Device Address or %NULL if not included
+ */
+const u8 * p2p_get_go_dev_addr(const struct wpabuf *p2p_ie);
+
+/**
+ * p2p_get_peer_info - Get P2P peer information
+ * @p2p: P2P module context from p2p_init()
+ * @addr: P2P Device Address of the peer or %NULL to indicate the first peer
+ * @next: Whether to select the peer entry following the one indicated by addr
+ * Returns: Pointer to peer info or %NULL if not found
+ */
+const struct p2p_peer_info * p2p_get_peer_info(struct p2p_data *p2p,
+ const u8 *addr, int next);
+
+/**
+ * p2p_get_peer_info_txt - Get internal P2P peer information in text format
+ * @info: Pointer to P2P peer info from p2p_get_peer_info()
+ * @buf: Buffer for returning text
+ * @buflen: Maximum buffer length
+ * Returns: Number of octets written to the buffer or -1 on failure
+ *
+ * Note: This information is internal to the P2P module and subject to change.
+ * As such, this should not really be used by external programs for purposes
+ * other than debugging.
+ */
+int p2p_get_peer_info_txt(const struct p2p_peer_info *info,
+ char *buf, size_t buflen);
+
+/**
+ * p2p_peer_known - Check whether P2P peer is known
+ * @p2p: P2P module context from p2p_init()
+ * @addr: P2P Device Address of the peer
+ * Returns: 1 if the specified device is in the P2P peer table or 0 if not
+ */
+int p2p_peer_known(struct p2p_data *p2p, const u8 *addr);
+
+/**
+ * p2p_set_client_discoverability - Set client discoverability capability
+ * @p2p: P2P module context from p2p_init()
+ * @enabled: Whether client discoverability will be enabled
+ *
+ * This function can be used to disable (and re-enable) client discoverability.
+ * This capability is enabled by default and should not be disabled in normal
+ * use cases, i.e., this is mainly for testing purposes.
+ */
+void p2p_set_client_discoverability(struct p2p_data *p2p, int enabled);
+
+/**
+ * p2p_set_managed_oper - Set managed P2P Device operations capability
+ * @p2p: P2P module context from p2p_init()
+ * @enabled: Whether managed P2P Device operations will be enabled
+ */
+void p2p_set_managed_oper(struct p2p_data *p2p, int enabled);
+
+int p2p_set_listen_channel(struct p2p_data *p2p, u8 reg_class, u8 channel);
+
+int p2p_set_ssid_postfix(struct p2p_data *p2p, const u8 *postfix, size_t len);
+
+int p2p_get_interface_addr(struct p2p_data *p2p, const u8 *dev_addr,
+ u8 *iface_addr);
+int p2p_get_dev_addr(struct p2p_data *p2p, const u8 *iface_addr,
+ u8 *dev_addr);
+
+void p2p_set_peer_filter(struct p2p_data *p2p, const u8 *addr);
+
+/**
+ * p2p_set_cross_connect - Set cross connection capability
+ * @p2p: P2P module context from p2p_init()
+ * @enabled: Whether cross connection will be enabled
+ */
+void p2p_set_cross_connect(struct p2p_data *p2p, int enabled);
+
+int p2p_get_oper_freq(struct p2p_data *p2p, const u8 *iface_addr);
+
+/**
+ * p2p_set_intra_bss_dist - Set intra BSS distribution
+ * @p2p: P2P module context from p2p_init()
+ * @enabled: Whether intra BSS distribution will be enabled
+ */
+void p2p_set_intra_bss_dist(struct p2p_data *p2p, int enabled);
+
+/**
+ * p2p_supported_freq - Check whether channel is supported for P2P
+ * @p2p: P2P module context from p2p_init()
+ * @freq: Channel frequency in MHz
+ * Returns: 0 if channel not usable for P2P, 1 if usable for P2P
+ */
+int p2p_supported_freq(struct p2p_data *p2p, unsigned int freq);
+
+void p2p_update_channel_list(struct p2p_data *p2p, struct p2p_channels *chan);
+
+/**
+ * p2p_set_best_channels - Update best channel information
+ * @p2p: P2P module context from p2p_init()
+ * @freq_24: Frequency (MHz) of best channel in 2.4 GHz band
+ * @freq_5: Frequency (MHz) of best channel in 5 GHz band
+ * @freq_overall: Frequency (MHz) of best channel overall
+ */
+void p2p_set_best_channels(struct p2p_data *p2p, int freq_24, int freq_5,
+ int freq_overall);
+
+const u8 * p2p_get_go_neg_peer(struct p2p_data *p2p);
+
+/**
+ * p2p_get_group_num_members - Get number of members in group
+ * @group: P2P group context from p2p_group_init()
+ * Returns: Number of members in the group
+ */
+unsigned int p2p_get_group_num_members(struct p2p_group *group);
+
+/**
+ * p2p_iterate_group_members - Iterate group members
+ * @group: P2P group context from p2p_group_init()
+ * @next: iteration pointer, must be a pointer to a void * that is set to %NULL
+ * on the first call and not modified later
+ * Returns: A P2P Interface Address for each call and %NULL for no more members
+ */
+const u8 * p2p_iterate_group_members(struct p2p_group *group, void **next);
+
+/**
+ * p2p_group_get_dev_addr - Get a P2P Device Address of a client in a group
+ * @group: P2P group context from p2p_group_init()
+ * @addr: P2P Interface Address of the client
+ * Returns: P2P Device Address of the client if found or %NULL if no match
+ * found
+ */
+const u8 * p2p_group_get_dev_addr(struct p2p_group *group, const u8 *addr);
+
+/**
+ * p2p_group_is_client_connected - Check whether a specific client is connected
+ * @group: P2P group context from p2p_group_init()
+ * @addr: P2P Device Address of the client
+ * Returns: 1 if client is connected or 0 if not
+ */
+int p2p_group_is_client_connected(struct p2p_group *group, const u8 *dev_addr);
+
+/**
+ * p2p_get_peer_found - Get P2P peer info structure of a found peer
+ * @p2p: P2P module context from p2p_init()
+ * @addr: P2P Device Address of the peer or %NULL to indicate the first peer
+ * @next: Whether to select the peer entry following the one indicated by addr
+ * Returns: The first P2P peer info available or %NULL if no such peer exists
+ */
+const struct p2p_peer_info *
+p2p_get_peer_found(struct p2p_data *p2p, const u8 *addr, int next);
+
+/**
+ * p2p_remove_wps_vendor_extensions - Remove WPS vendor extensions
+ * @p2p: P2P module context from p2p_init()
+ */
+void p2p_remove_wps_vendor_extensions(struct p2p_data *p2p);
+
+/**
+ * p2p_add_wps_vendor_extension - Add a WPS vendor extension
+ * @p2p: P2P module context from p2p_init()
+ * @vendor_ext: The vendor extensions to add
+ * Returns: 0 on success, -1 on failure
+ *
+ * The wpabuf structures in the array are owned by the P2P
+ * module after this call.
+ */
+int p2p_add_wps_vendor_extension(struct p2p_data *p2p,
+ const struct wpabuf *vendor_ext);
+
+/**
+ * p2p_set_oper_channel - Set the P2P operating channel
+ * @p2p: P2P module context from p2p_init()
+ * @op_reg_class: Operating regulatory class to set
+ * @op_channel: operating channel to set
+ * @cfg_op_channel : Whether op_channel is hardcoded in configuration
+ * Returns: 0 on success, -1 on failure
+ */
+int p2p_set_oper_channel(struct p2p_data *p2p, u8 op_reg_class, u8 op_channel,
+ int cfg_op_channel);
+
+/**
+ * p2p_set_pref_chan - Set P2P preferred channel list
+ * @p2p: P2P module context from p2p_init()
+ * @num_pref_chan: Number of entries in pref_chan list
+ * @pref_chan: Preferred channels or %NULL to remove preferences
+ * Returns: 0 on success, -1 on failure
+ */
+int p2p_set_pref_chan(struct p2p_data *p2p, unsigned int num_pref_chan,
+ const struct p2p_channel *pref_chan);
+
+/**
+ * p2p_in_progress - Check whether a P2P operation is progress
+ * @p2p: P2P module context from p2p_init()
+ * Returns: 0 if P2P module is idle or 1 if an operation is in progress
+ */
+int p2p_in_progress(struct p2p_data *p2p);
+
+/**
+ * p2p_other_scan_completed - Notify completion of non-P2P scan
+ * @p2p: P2P module context from p2p_init()
+ * Returns: 0 if P2P module is idle or 1 if an operation was started
+ */
+int p2p_other_scan_completed(struct p2p_data *p2p);
+
+const char * p2p_wps_method_text(enum p2p_wps_method method);
+
+/**
+ * p2p_set_config_timeout - Set local config timeouts
+ * @p2p: P2P module context from p2p_init()
+ * @go_timeout: Time in 10 ms units it takes to start the GO mode
+ * @client_timeout: Time in 10 ms units it takes to start the client mode
+ */
+void p2p_set_config_timeout(struct p2p_data *p2p, u8 go_timeout,
+ u8 client_timeout);
+
+void p2p_increase_search_delay(struct p2p_data *p2p, unsigned int delay);
+
+int p2p_set_wfd_ie_beacon(struct p2p_data *p2p, struct wpabuf *ie);
+int p2p_set_wfd_ie_probe_req(struct p2p_data *p2p, struct wpabuf *ie);
+int p2p_set_wfd_ie_probe_resp(struct p2p_data *p2p, struct wpabuf *ie);
+int p2p_set_wfd_ie_assoc_req(struct p2p_data *p2p, struct wpabuf *ie);
+int p2p_set_wfd_ie_invitation(struct p2p_data *p2p, struct wpabuf *ie);
+int p2p_set_wfd_ie_prov_disc_req(struct p2p_data *p2p, struct wpabuf *ie);
+int p2p_set_wfd_ie_prov_disc_resp(struct p2p_data *p2p, struct wpabuf *ie);
+int p2p_set_wfd_ie_go_neg(struct p2p_data *p2p, struct wpabuf *ie);
+int p2p_set_wfd_dev_info(struct p2p_data *p2p, const struct wpabuf *elem);
+int p2p_set_wfd_assoc_bssid(struct p2p_data *p2p, const struct wpabuf *elem);
+int p2p_set_wfd_coupled_sink_info(struct p2p_data *p2p,
+ const struct wpabuf *elem);
+struct wpabuf * wifi_display_encaps(struct wpabuf *subelems);
+
+/**
+ * p2p_set_disc_int - Set min/max discoverable interval for p2p_find
+ * @p2p: P2P module context from p2p_init()
+ * @min_disc_int: minDiscoverableInterval (in units of 100 TU); default 1
+ * @max_disc_int: maxDiscoverableInterval (in units of 100 TU); default 3
+ * @max_disc_tu: Maximum number of TUs (1.024 ms) for discoverable interval; or
+ * -1 not to limit
+ * Returns: 0 on success, or -1 on failure
+ *
+ * This function can be used to configure minDiscoverableInterval and
+ * maxDiscoverableInterval parameters for the Listen state during device
+ * discovery (p2p_find). A random number of 100 TU units is picked for each
+ * Listen state iteration from [min_disc_int,max_disc_int] range.
+ *
+ * max_disc_tu can be used to futher limit the discoverable duration. However,
+ * it should be noted that use of this parameter is not recommended since it
+ * would not be compliant with the P2P specification.
+ */
+int p2p_set_disc_int(struct p2p_data *p2p, int min_disc_int, int max_disc_int,
+ int max_disc_tu);
+
+#endif /* P2P_H */
diff --git a/contrib/wpa/src/p2p/p2p_build.c b/contrib/wpa/src/p2p/p2p_build.c
new file mode 100644
index 0000000..5838d35
--- /dev/null
+++ b/contrib/wpa/src/p2p/p2p_build.c
@@ -0,0 +1,431 @@
+/*
+ * P2P - IE builder
+ * Copyright (c) 2009-2010, Atheros Communications
+ *
+ * This software may be distributed under the terms of the BSD license.
+ * See README for more details.
+ */
+
+#include "includes.h"
+
+#include "common.h"
+#include "common/ieee802_11_defs.h"
+#include "wps/wps_i.h"
+#include "p2p_i.h"
+
+
+void p2p_buf_add_action_hdr(struct wpabuf *buf, u8 subtype, u8 dialog_token)
+{
+ wpabuf_put_u8(buf, WLAN_ACTION_VENDOR_SPECIFIC);
+ wpabuf_put_be24(buf, OUI_WFA);
+ wpabuf_put_u8(buf, P2P_OUI_TYPE);
+
+ wpabuf_put_u8(buf, subtype); /* OUI Subtype */
+ wpabuf_put_u8(buf, dialog_token);
+ wpa_printf(MSG_DEBUG, "P2P: * Dialog Token: %d", dialog_token);
+}
+
+
+void p2p_buf_add_public_action_hdr(struct wpabuf *buf, u8 subtype,
+ u8 dialog_token)
+{
+ wpabuf_put_u8(buf, WLAN_ACTION_PUBLIC);
+ wpabuf_put_u8(buf, WLAN_PA_VENDOR_SPECIFIC);
+ wpabuf_put_be24(buf, OUI_WFA);
+ wpabuf_put_u8(buf, P2P_OUI_TYPE);
+
+ wpabuf_put_u8(buf, subtype); /* OUI Subtype */
+ wpabuf_put_u8(buf, dialog_token);
+ wpa_printf(MSG_DEBUG, "P2P: * Dialog Token: %d", dialog_token);
+}
+
+
+u8 * p2p_buf_add_ie_hdr(struct wpabuf *buf)
+{
+ u8 *len;
+
+ /* P2P IE header */
+ wpabuf_put_u8(buf, WLAN_EID_VENDOR_SPECIFIC);
+ len = wpabuf_put(buf, 1); /* IE length to be filled */
+ wpabuf_put_be24(buf, OUI_WFA);
+ wpabuf_put_u8(buf, P2P_OUI_TYPE);
+ wpa_printf(MSG_DEBUG, "P2P: * P2P IE header");
+ return len;
+}
+
+
+void p2p_buf_update_ie_hdr(struct wpabuf *buf, u8 *len)
+{
+ /* Update P2P IE Length */
+ *len = (u8 *) wpabuf_put(buf, 0) - len - 1;
+}
+
+
+void p2p_buf_add_capability(struct wpabuf *buf, u8 dev_capab, u8 group_capab)
+{
+ /* P2P Capability */
+ wpabuf_put_u8(buf, P2P_ATTR_CAPABILITY);
+ wpabuf_put_le16(buf, 2);
+ wpabuf_put_u8(buf, dev_capab); /* Device Capabilities */
+ wpabuf_put_u8(buf, group_capab); /* Group Capabilities */
+ wpa_printf(MSG_DEBUG, "P2P: * Capability dev=%02x group=%02x",
+ dev_capab, group_capab);
+}
+
+
+void p2p_buf_add_go_intent(struct wpabuf *buf, u8 go_intent)
+{
+ /* Group Owner Intent */
+ wpabuf_put_u8(buf, P2P_ATTR_GROUP_OWNER_INTENT);
+ wpabuf_put_le16(buf, 1);
+ wpabuf_put_u8(buf, go_intent);
+ wpa_printf(MSG_DEBUG, "P2P: * GO Intent: Intent %u Tie breaker %u",
+ go_intent >> 1, go_intent & 0x01);
+}
+
+
+void p2p_buf_add_listen_channel(struct wpabuf *buf, const char *country,
+ u8 reg_class, u8 channel)
+{
+ /* Listen Channel */
+ wpabuf_put_u8(buf, P2P_ATTR_LISTEN_CHANNEL);
+ wpabuf_put_le16(buf, 5);
+ wpabuf_put_data(buf, country, 3);
+ wpabuf_put_u8(buf, reg_class); /* Regulatory Class */
+ wpabuf_put_u8(buf, channel); /* Channel Number */
+ wpa_printf(MSG_DEBUG, "P2P: * Listen Channel: Regulatory Class %u "
+ "Channel %u", reg_class, channel);
+}
+
+
+void p2p_buf_add_operating_channel(struct wpabuf *buf, const char *country,
+ u8 reg_class, u8 channel)
+{
+ /* Operating Channel */
+ wpabuf_put_u8(buf, P2P_ATTR_OPERATING_CHANNEL);
+ wpabuf_put_le16(buf, 5);
+ wpabuf_put_data(buf, country, 3);
+ wpabuf_put_u8(buf, reg_class); /* Regulatory Class */
+ wpabuf_put_u8(buf, channel); /* Channel Number */
+ wpa_printf(MSG_DEBUG, "P2P: * Operating Channel: Regulatory Class %u "
+ "Channel %u", reg_class, channel);
+}
+
+
+void p2p_buf_add_channel_list(struct wpabuf *buf, const char *country,
+ struct p2p_channels *chan)
+{
+ u8 *len;
+ size_t i;
+
+ /* Channel List */
+ wpabuf_put_u8(buf, P2P_ATTR_CHANNEL_LIST);
+ len = wpabuf_put(buf, 2); /* IE length to be filled */
+ wpabuf_put_data(buf, country, 3); /* Country String */
+
+ for (i = 0; i < chan->reg_classes; i++) {
+ struct p2p_reg_class *c = &chan->reg_class[i];
+ wpabuf_put_u8(buf, c->reg_class);
+ wpabuf_put_u8(buf, c->channels);
+ wpabuf_put_data(buf, c->channel, c->channels);
+ }
+
+ /* Update attribute length */
+ WPA_PUT_LE16(len, (u8 *) wpabuf_put(buf, 0) - len - 2);
+ wpa_hexdump(MSG_DEBUG, "P2P: * Channel List",
+ len + 2, (u8 *) wpabuf_put(buf, 0) - len - 2);
+}
+
+
+void p2p_buf_add_status(struct wpabuf *buf, u8 status)
+{
+ /* Status */
+ wpabuf_put_u8(buf, P2P_ATTR_STATUS);
+ wpabuf_put_le16(buf, 1);
+ wpabuf_put_u8(buf, status);
+ wpa_printf(MSG_DEBUG, "P2P: * Status: %d", status);
+}
+
+
+void p2p_buf_add_device_info(struct wpabuf *buf, struct p2p_data *p2p,
+ struct p2p_device *peer)
+{
+ u8 *len;
+ u16 methods;
+ size_t nlen, i;
+
+ /* P2P Device Info */
+ wpabuf_put_u8(buf, P2P_ATTR_DEVICE_INFO);
+ len = wpabuf_put(buf, 2); /* IE length to be filled */
+
+ /* P2P Device address */
+ wpabuf_put_data(buf, p2p->cfg->dev_addr, ETH_ALEN);
+
+ /* Config Methods */
+ methods = 0;
+ if (peer && peer->wps_method != WPS_NOT_READY) {
+ if (peer->wps_method == WPS_PBC)
+ methods |= WPS_CONFIG_PUSHBUTTON;
+ else if (peer->wps_method == WPS_PIN_DISPLAY ||
+ peer->wps_method == WPS_PIN_KEYPAD)
+ methods |= WPS_CONFIG_DISPLAY | WPS_CONFIG_KEYPAD;
+ } else if (p2p->cfg->config_methods) {
+ methods |= p2p->cfg->config_methods &
+ (WPS_CONFIG_PUSHBUTTON | WPS_CONFIG_DISPLAY |
+ WPS_CONFIG_KEYPAD);
+ } else {
+ methods |= WPS_CONFIG_PUSHBUTTON;
+ methods |= WPS_CONFIG_DISPLAY | WPS_CONFIG_KEYPAD;
+ }
+ wpabuf_put_be16(buf, methods);
+
+ /* Primary Device Type */
+ wpabuf_put_data(buf, p2p->cfg->pri_dev_type,
+ sizeof(p2p->cfg->pri_dev_type));
+
+ /* Number of Secondary Device Types */
+ wpabuf_put_u8(buf, p2p->cfg->num_sec_dev_types);
+
+ /* Secondary Device Type List */
+ for (i = 0; i < p2p->cfg->num_sec_dev_types; i++)
+ wpabuf_put_data(buf, p2p->cfg->sec_dev_type[i],
+ WPS_DEV_TYPE_LEN);
+
+ /* Device Name */
+ nlen = p2p->cfg->dev_name ? os_strlen(p2p->cfg->dev_name) : 0;
+ wpabuf_put_be16(buf, ATTR_DEV_NAME);
+ wpabuf_put_be16(buf, nlen);
+ wpabuf_put_data(buf, p2p->cfg->dev_name, nlen);
+
+ /* Update attribute length */
+ WPA_PUT_LE16(len, (u8 *) wpabuf_put(buf, 0) - len - 2);
+ wpa_printf(MSG_DEBUG, "P2P: * Device Info");
+}
+
+
+void p2p_buf_add_device_id(struct wpabuf *buf, const u8 *dev_addr)
+{
+ /* P2P Device ID */
+ wpabuf_put_u8(buf, P2P_ATTR_DEVICE_ID);
+ wpabuf_put_le16(buf, ETH_ALEN);
+ wpabuf_put_data(buf, dev_addr, ETH_ALEN);
+ wpa_printf(MSG_DEBUG, "P2P: * Device ID: " MACSTR, MAC2STR(dev_addr));
+}
+
+
+void p2p_buf_add_config_timeout(struct wpabuf *buf, u8 go_timeout,
+ u8 client_timeout)
+{
+ /* Configuration Timeout */
+ wpabuf_put_u8(buf, P2P_ATTR_CONFIGURATION_TIMEOUT);
+ wpabuf_put_le16(buf, 2);
+ wpabuf_put_u8(buf, go_timeout);
+ wpabuf_put_u8(buf, client_timeout);
+ wpa_printf(MSG_DEBUG, "P2P: * Configuration Timeout: GO %d (*10ms) "
+ "client %d (*10ms)", go_timeout, client_timeout);
+}
+
+
+void p2p_buf_add_intended_addr(struct wpabuf *buf, const u8 *interface_addr)
+{
+ /* Intended P2P Interface Address */
+ wpabuf_put_u8(buf, P2P_ATTR_INTENDED_INTERFACE_ADDR);
+ wpabuf_put_le16(buf, ETH_ALEN);
+ wpabuf_put_data(buf, interface_addr, ETH_ALEN);
+ wpa_printf(MSG_DEBUG, "P2P: * Intended P2P Interface Address " MACSTR,
+ MAC2STR(interface_addr));
+}
+
+
+void p2p_buf_add_group_bssid(struct wpabuf *buf, const u8 *bssid)
+{
+ /* P2P Group BSSID */
+ wpabuf_put_u8(buf, P2P_ATTR_GROUP_BSSID);
+ wpabuf_put_le16(buf, ETH_ALEN);
+ wpabuf_put_data(buf, bssid, ETH_ALEN);
+ wpa_printf(MSG_DEBUG, "P2P: * P2P Group BSSID " MACSTR,
+ MAC2STR(bssid));
+}
+
+
+void p2p_buf_add_group_id(struct wpabuf *buf, const u8 *dev_addr,
+ const u8 *ssid, size_t ssid_len)
+{
+ /* P2P Group ID */
+ wpabuf_put_u8(buf, P2P_ATTR_GROUP_ID);
+ wpabuf_put_le16(buf, ETH_ALEN + ssid_len);
+ wpabuf_put_data(buf, dev_addr, ETH_ALEN);
+ wpabuf_put_data(buf, ssid, ssid_len);
+ wpa_printf(MSG_DEBUG, "P2P: * P2P Group ID " MACSTR,
+ MAC2STR(dev_addr));
+}
+
+
+void p2p_buf_add_invitation_flags(struct wpabuf *buf, u8 flags)
+{
+ /* Invitation Flags */
+ wpabuf_put_u8(buf, P2P_ATTR_INVITATION_FLAGS);
+ wpabuf_put_le16(buf, 1);
+ wpabuf_put_u8(buf, flags);
+ wpa_printf(MSG_DEBUG, "P2P: * Invitation Flags: bitmap 0x%x", flags);
+}
+
+
+static void p2p_buf_add_noa_desc(struct wpabuf *buf, struct p2p_noa_desc *desc)
+{
+ if (desc == NULL)
+ return;
+
+ wpabuf_put_u8(buf, desc->count_type);
+ wpabuf_put_le32(buf, desc->duration);
+ wpabuf_put_le32(buf, desc->interval);
+ wpabuf_put_le32(buf, desc->start_time);
+}
+
+
+void p2p_buf_add_noa(struct wpabuf *buf, u8 noa_index, u8 opp_ps, u8 ctwindow,
+ struct p2p_noa_desc *desc1, struct p2p_noa_desc *desc2)
+{
+ /* Notice of Absence */
+ wpabuf_put_u8(buf, P2P_ATTR_NOTICE_OF_ABSENCE);
+ wpabuf_put_le16(buf, 2 + (desc1 ? 13 : 0) + (desc2 ? 13 : 0));
+ wpabuf_put_u8(buf, noa_index);
+ wpabuf_put_u8(buf, (opp_ps ? 0x80 : 0) | (ctwindow & 0x7f));
+ p2p_buf_add_noa_desc(buf, desc1);
+ p2p_buf_add_noa_desc(buf, desc2);
+ wpa_printf(MSG_DEBUG, "P2P: * Notice of Absence");
+}
+
+
+void p2p_buf_add_ext_listen_timing(struct wpabuf *buf, u16 period,
+ u16 interval)
+{
+ /* Extended Listen Timing */
+ wpabuf_put_u8(buf, P2P_ATTR_EXT_LISTEN_TIMING);
+ wpabuf_put_le16(buf, 4);
+ wpabuf_put_le16(buf, period);
+ wpabuf_put_le16(buf, interval);
+ wpa_printf(MSG_DEBUG, "P2P: * Extended Listen Timing (period %u msec "
+ "interval %u msec)", period, interval);
+}
+
+
+void p2p_buf_add_p2p_interface(struct wpabuf *buf, struct p2p_data *p2p)
+{
+ /* P2P Interface */
+ wpabuf_put_u8(buf, P2P_ATTR_INTERFACE);
+ wpabuf_put_le16(buf, ETH_ALEN + 1 + ETH_ALEN);
+ /* P2P Device address */
+ wpabuf_put_data(buf, p2p->cfg->dev_addr, ETH_ALEN);
+ /*
+ * FIX: Fetch interface address list from driver. Do not include
+ * the P2P Device address if it is never used as interface address.
+ */
+ /* P2P Interface Address Count */
+ wpabuf_put_u8(buf, 1);
+ wpabuf_put_data(buf, p2p->cfg->dev_addr, ETH_ALEN);
+}
+
+
+static void p2p_add_wps_string(struct wpabuf *buf, enum wps_attribute attr,
+ const char *val)
+{
+ size_t len;
+
+ wpabuf_put_be16(buf, attr);
+ len = val ? os_strlen(val) : 0;
+#ifndef CONFIG_WPS_STRICT
+ if (len == 0) {
+ /*
+ * Some deployed WPS implementations fail to parse zeor-length
+ * attributes. As a workaround, send a space character if the
+ * device attribute string is empty.
+ */
+ wpabuf_put_be16(buf, 1);
+ wpabuf_put_u8(buf, ' ');
+ return;
+ }
+#endif /* CONFIG_WPS_STRICT */
+ wpabuf_put_be16(buf, len);
+ if (val)
+ wpabuf_put_data(buf, val, len);
+}
+
+
+void p2p_build_wps_ie(struct p2p_data *p2p, struct wpabuf *buf, int pw_id,
+ int all_attr)
+{
+ u8 *len;
+ int i;
+
+ wpabuf_put_u8(buf, WLAN_EID_VENDOR_SPECIFIC);
+ len = wpabuf_put(buf, 1);
+ wpabuf_put_be32(buf, WPS_DEV_OUI_WFA);
+
+ wps_build_version(buf);
+
+ if (all_attr) {
+ wpabuf_put_be16(buf, ATTR_WPS_STATE);
+ wpabuf_put_be16(buf, 1);
+ wpabuf_put_u8(buf, WPS_STATE_NOT_CONFIGURED);
+ }
+
+ if (pw_id >= 0) {
+ /* Device Password ID */
+ wpabuf_put_be16(buf, ATTR_DEV_PASSWORD_ID);
+ wpabuf_put_be16(buf, 2);
+ wpa_printf(MSG_DEBUG, "P2P: WPS IE Device Password ID: %d",
+ pw_id);
+ wpabuf_put_be16(buf, pw_id);
+ }
+
+ if (all_attr) {
+ wpabuf_put_be16(buf, ATTR_RESPONSE_TYPE);
+ wpabuf_put_be16(buf, 1);
+ wpabuf_put_u8(buf, WPS_RESP_ENROLLEE_INFO);
+
+ wps_build_uuid_e(buf, p2p->cfg->uuid);
+ p2p_add_wps_string(buf, ATTR_MANUFACTURER,
+ p2p->cfg->manufacturer);
+ p2p_add_wps_string(buf, ATTR_MODEL_NAME, p2p->cfg->model_name);
+ p2p_add_wps_string(buf, ATTR_MODEL_NUMBER,
+ p2p->cfg->model_number);
+ p2p_add_wps_string(buf, ATTR_SERIAL_NUMBER,
+ p2p->cfg->serial_number);
+
+ wpabuf_put_be16(buf, ATTR_PRIMARY_DEV_TYPE);
+ wpabuf_put_be16(buf, WPS_DEV_TYPE_LEN);
+ wpabuf_put_data(buf, p2p->cfg->pri_dev_type, WPS_DEV_TYPE_LEN);
+
+ p2p_add_wps_string(buf, ATTR_DEV_NAME, p2p->cfg->dev_name);
+
+ wpabuf_put_be16(buf, ATTR_CONFIG_METHODS);
+ wpabuf_put_be16(buf, 2);
+ wpabuf_put_be16(buf, p2p->cfg->config_methods);
+ }
+
+ wps_build_wfa_ext(buf, 0, NULL, 0);
+
+ if (all_attr && p2p->cfg->num_sec_dev_types) {
+ wpabuf_put_be16(buf, ATTR_SECONDARY_DEV_TYPE_LIST);
+ wpabuf_put_be16(buf, WPS_DEV_TYPE_LEN *
+ p2p->cfg->num_sec_dev_types);
+ wpabuf_put_data(buf, p2p->cfg->sec_dev_type,
+ WPS_DEV_TYPE_LEN *
+ p2p->cfg->num_sec_dev_types);
+ }
+
+ /* Add the WPS vendor extensions */
+ for (i = 0; i < P2P_MAX_WPS_VENDOR_EXT; i++) {
+ if (p2p->wps_vendor_ext[i] == NULL)
+ break;
+ if (wpabuf_tailroom(buf) <
+ 4 + wpabuf_len(p2p->wps_vendor_ext[i]))
+ continue;
+ wpabuf_put_be16(buf, ATTR_VENDOR_EXT);
+ wpabuf_put_be16(buf, wpabuf_len(p2p->wps_vendor_ext[i]));
+ wpabuf_put_buf(buf, p2p->wps_vendor_ext[i]);
+ }
+
+ p2p_buf_update_ie_hdr(buf, len);
+}
diff --git a/contrib/wpa/src/p2p/p2p_dev_disc.c b/contrib/wpa/src/p2p/p2p_dev_disc.c
new file mode 100644
index 0000000..c976b7c
--- /dev/null
+++ b/contrib/wpa/src/p2p/p2p_dev_disc.c
@@ -0,0 +1,359 @@
+/*
+ * Wi-Fi Direct - P2P Device Discoverability procedure
+ * Copyright (c) 2010, Atheros Communications
+ *
+ * This software may be distributed under the terms of the BSD license.
+ * See README for more details.
+ */
+
+#include "includes.h"
+
+#include "common.h"
+#include "common/ieee802_11_defs.h"
+#include "p2p_i.h"
+#include "p2p.h"
+
+
+static struct wpabuf * p2p_build_dev_disc_req(struct p2p_data *p2p,
+ struct p2p_device *go,
+ const u8 *dev_id)
+{
+ struct wpabuf *buf;
+ u8 *len;
+
+ buf = wpabuf_alloc(100);
+ if (buf == NULL)
+ return NULL;
+
+ go->dialog_token++;
+ if (go->dialog_token == 0)
+ go->dialog_token = 1;
+ p2p_buf_add_public_action_hdr(buf, P2P_DEV_DISC_REQ, go->dialog_token);
+
+ len = p2p_buf_add_ie_hdr(buf);
+ p2p_buf_add_device_id(buf, dev_id);
+ p2p_buf_add_group_id(buf, go->info.p2p_device_addr, go->oper_ssid,
+ go->oper_ssid_len);
+ p2p_buf_update_ie_hdr(buf, len);
+
+ return buf;
+}
+
+
+void p2p_dev_disc_req_cb(struct p2p_data *p2p, int success)
+{
+ wpa_msg(p2p->cfg->msg_ctx, MSG_DEBUG,
+ "P2P: Device Discoverability Request TX callback: success=%d",
+ success);
+
+ if (!success) {
+ /*
+ * Use P2P find, if needed, to find the other device or to
+ * retry device discoverability.
+ */
+ p2p_set_state(p2p, P2P_CONNECT);
+ p2p_set_timeout(p2p, 0, 100000);
+ return;
+ }
+
+ wpa_msg(p2p->cfg->msg_ctx, MSG_DEBUG,
+ "P2P: GO acknowledged Device Discoverability Request - wait "
+ "for response");
+ /*
+ * TODO: is the remain-on-channel from Action frame TX long enough for
+ * most cases or should we try to increase its duration and/or start
+ * another remain-on-channel if needed once the previous one expires?
+ */
+}
+
+
+int p2p_send_dev_disc_req(struct p2p_data *p2p, struct p2p_device *dev)
+{
+ struct p2p_device *go;
+ struct wpabuf *req;
+
+ go = p2p_get_device(p2p, dev->member_in_go_dev);
+ if (go == NULL || dev->oper_freq <= 0) {
+ wpa_msg(p2p->cfg->msg_ctx, MSG_DEBUG,
+ "P2P: Could not find peer entry for GO and frequency "
+ "to send Device Discoverability Request");
+ return -1;
+ }
+
+ req = p2p_build_dev_disc_req(p2p, go, dev->info.p2p_device_addr);
+ if (req == NULL)
+ return -1;
+
+ wpa_msg(p2p->cfg->msg_ctx, MSG_DEBUG,
+ "P2P: Sending Device Discoverability Request to GO " MACSTR
+ " for client " MACSTR,
+ MAC2STR(go->info.p2p_device_addr),
+ MAC2STR(dev->info.p2p_device_addr));
+
+ p2p->pending_client_disc_go = go;
+ os_memcpy(p2p->pending_client_disc_addr, dev->info.p2p_device_addr,
+ ETH_ALEN);
+ p2p->pending_action_state = P2P_PENDING_DEV_DISC_REQUEST;
+ if (p2p_send_action(p2p, dev->oper_freq, go->info.p2p_device_addr,
+ p2p->cfg->dev_addr, go->info.p2p_device_addr,
+ wpabuf_head(req), wpabuf_len(req), 1000) < 0) {
+ wpa_msg(p2p->cfg->msg_ctx, MSG_DEBUG,
+ "P2P: Failed to send Action frame");
+ wpabuf_free(req);
+ /* TODO: how to recover from failure? */
+ return -1;
+ }
+
+ wpabuf_free(req);
+
+ return 0;
+}
+
+
+static struct wpabuf * p2p_build_dev_disc_resp(u8 dialog_token, u8 status)
+{
+ struct wpabuf *buf;
+ u8 *len;
+
+ buf = wpabuf_alloc(100);
+ if (buf == NULL)
+ return NULL;
+
+ p2p_buf_add_public_action_hdr(buf, P2P_DEV_DISC_RESP, dialog_token);
+
+ len = p2p_buf_add_ie_hdr(buf);
+ p2p_buf_add_status(buf, status);
+ p2p_buf_update_ie_hdr(buf, len);
+
+ return buf;
+}
+
+
+void p2p_dev_disc_resp_cb(struct p2p_data *p2p, int success)
+{
+ wpa_msg(p2p->cfg->msg_ctx, MSG_DEBUG,
+ "P2P: Device Discoverability Response TX callback: success=%d",
+ success);
+ p2p->cfg->send_action_done(p2p->cfg->cb_ctx);
+}
+
+
+static void p2p_send_dev_disc_resp(struct p2p_data *p2p, u8 dialog_token,
+ const u8 *addr, int freq, u8 status)
+{
+ struct wpabuf *resp;
+
+ resp = p2p_build_dev_disc_resp(dialog_token, status);
+ if (resp == NULL)
+ return;
+
+ wpa_msg(p2p->cfg->msg_ctx, MSG_DEBUG,
+ "P2P: Sending Device Discoverability Response to " MACSTR
+ " (status %u freq %d)",
+ MAC2STR(addr), status, freq);
+
+ p2p->pending_action_state = P2P_PENDING_DEV_DISC_RESPONSE;
+ if (p2p_send_action(p2p, freq, addr, p2p->cfg->dev_addr,
+ p2p->cfg->dev_addr,
+ wpabuf_head(resp), wpabuf_len(resp), 200) < 0) {
+ wpa_msg(p2p->cfg->msg_ctx, MSG_DEBUG,
+ "P2P: Failed to send Action frame");
+ }
+
+ wpabuf_free(resp);
+}
+
+
+void p2p_process_dev_disc_req(struct p2p_data *p2p, const u8 *sa,
+ const u8 *data, size_t len, int rx_freq)
+{
+ struct p2p_message msg;
+ size_t g;
+
+ wpa_msg(p2p->cfg->msg_ctx, MSG_DEBUG,
+ "P2P: Received Device Discoverability Request from " MACSTR
+ " (freq=%d)", MAC2STR(sa), rx_freq);
+
+ if (p2p_parse(data, len, &msg))
+ return;
+
+ if (msg.dialog_token == 0) {
+ wpa_msg(p2p->cfg->msg_ctx, MSG_DEBUG,
+ "P2P: Invalid Dialog Token 0 (must be nonzero) in "
+ "Device Discoverability Request");
+ p2p_send_dev_disc_resp(p2p, msg.dialog_token, sa, rx_freq,
+ P2P_SC_FAIL_INVALID_PARAMS);
+ p2p_parse_free(&msg);
+ return;
+ }
+
+ if (msg.device_id == NULL) {
+ wpa_msg(p2p->cfg->msg_ctx, MSG_DEBUG,
+ "P2P: P2P Device ID attribute missing from Device "
+ "Discoverability Request");
+ p2p_send_dev_disc_resp(p2p, msg.dialog_token, sa, rx_freq,
+ P2P_SC_FAIL_INVALID_PARAMS);
+ p2p_parse_free(&msg);
+ return;
+ }
+
+ for (g = 0; g < p2p->num_groups; g++) {
+ if (p2p_group_go_discover(p2p->groups[g], msg.device_id, sa,
+ rx_freq) == 0) {
+ wpa_msg(p2p->cfg->msg_ctx, MSG_DEBUG, "P2P: Scheduled "
+ "GO Discoverability Request for the target "
+ "device");
+ /*
+ * P2P group code will use a callback to indicate TX
+ * status, so that we can reply to the request once the
+ * target client has acknowledged the request or it has
+ * timed out.
+ */
+ p2p->pending_dev_disc_dialog_token = msg.dialog_token;
+ os_memcpy(p2p->pending_dev_disc_addr, sa, ETH_ALEN);
+ p2p->pending_dev_disc_freq = rx_freq;
+ p2p_parse_free(&msg);
+ return;
+ }
+ }
+
+ wpa_msg(p2p->cfg->msg_ctx, MSG_DEBUG, "P2P: Requested client "
+ "was not found in any group or did not support client "
+ "discoverability");
+ p2p_send_dev_disc_resp(p2p, msg.dialog_token, sa, rx_freq,
+ P2P_SC_FAIL_UNABLE_TO_ACCOMMODATE);
+ p2p_parse_free(&msg);
+}
+
+
+void p2p_process_dev_disc_resp(struct p2p_data *p2p, const u8 *sa,
+ const u8 *data, size_t len)
+{
+ struct p2p_message msg;
+ struct p2p_device *go;
+ u8 status;
+
+ wpa_msg(p2p->cfg->msg_ctx, MSG_DEBUG,
+ "P2P: Received Device Discoverability Response from " MACSTR,
+ MAC2STR(sa));
+
+ go = p2p->pending_client_disc_go;
+ if (go == NULL ||
+ os_memcmp(sa, go->info.p2p_device_addr, ETH_ALEN) != 0) {
+ wpa_msg(p2p->cfg->msg_ctx, MSG_DEBUG, "P2P: Ignore unexpected "
+ "Device Discoverability Response");
+ return;
+ }
+
+ if (p2p_parse(data, len, &msg))
+ return;
+
+ if (msg.status == NULL) {
+ p2p_parse_free(&msg);
+ return;
+ }
+
+ if (msg.dialog_token != go->dialog_token) {
+ wpa_msg(p2p->cfg->msg_ctx, MSG_DEBUG, "P2P: Ignore Device "
+ "Discoverability Response with unexpected dialog "
+ "token %u (expected %u)",
+ msg.dialog_token, go->dialog_token);
+ p2p_parse_free(&msg);
+ return;
+ }
+
+ status = *msg.status;
+ p2p_parse_free(&msg);
+
+ wpa_msg(p2p->cfg->msg_ctx, MSG_DEBUG,
+ "P2P: Device Discoverability Response status %u", status);
+
+ if (p2p->go_neg_peer == NULL ||
+ os_memcmp(p2p->pending_client_disc_addr,
+ p2p->go_neg_peer->info.p2p_device_addr, ETH_ALEN) != 0 ||
+ os_memcmp(p2p->go_neg_peer->member_in_go_dev,
+ go->info.p2p_device_addr, ETH_ALEN) != 0) {
+ wpa_msg(p2p->cfg->msg_ctx, MSG_DEBUG, "P2P: No pending "
+ "operation with the client discoverability peer "
+ "anymore");
+ return;
+ }
+
+ if (status == 0) {
+ /*
+ * Peer is expected to be awake for at least 100 TU; try to
+ * connect immediately.
+ */
+ wpa_msg(p2p->cfg->msg_ctx, MSG_DEBUG,
+ "P2P: Client discoverability request succeeded");
+ if (p2p->state == P2P_CONNECT) {
+ /*
+ * Change state to force the timeout to start in
+ * P2P_CONNECT again without going through the short
+ * Listen state.
+ */
+ p2p_set_state(p2p, P2P_CONNECT_LISTEN);
+ p2p->cfg->send_action_done(p2p->cfg->cb_ctx);
+ }
+ p2p_set_timeout(p2p, 0, 0);
+ } else {
+ /*
+ * Client discoverability request failed; try to connect from
+ * timeout.
+ */
+ wpa_msg(p2p->cfg->msg_ctx, MSG_DEBUG,
+ "P2P: Client discoverability request failed");
+ p2p_set_timeout(p2p, 0, 500000);
+ }
+
+}
+
+
+void p2p_go_disc_req_cb(struct p2p_data *p2p, int success)
+{
+ wpa_msg(p2p->cfg->msg_ctx, MSG_DEBUG,
+ "P2P: GO Discoverability Request TX callback: success=%d",
+ success);
+ p2p->cfg->send_action_done(p2p->cfg->cb_ctx);
+
+ if (p2p->pending_dev_disc_dialog_token == 0) {
+ wpa_msg(p2p->cfg->msg_ctx, MSG_DEBUG, "P2P: No pending Device "
+ "Discoverability Request");
+ return;
+ }
+
+ p2p_send_dev_disc_resp(p2p, p2p->pending_dev_disc_dialog_token,
+ p2p->pending_dev_disc_addr,
+ p2p->pending_dev_disc_freq,
+ success ? P2P_SC_SUCCESS :
+ P2P_SC_FAIL_UNABLE_TO_ACCOMMODATE);
+
+ p2p->pending_dev_disc_dialog_token = 0;
+}
+
+
+void p2p_process_go_disc_req(struct p2p_data *p2p, const u8 *da, const u8 *sa,
+ const u8 *data, size_t len, int rx_freq)
+{
+ unsigned int tu;
+ struct wpabuf *ies;
+
+ wpa_msg(p2p->cfg->msg_ctx, MSG_DEBUG,
+ "P2P: Received GO Discoverability Request - remain awake for "
+ "100 TU");
+
+ ies = p2p_build_probe_resp_ies(p2p);
+ if (ies == NULL)
+ return;
+
+ /* Remain awake 100 TU on operating channel */
+ p2p->pending_client_disc_freq = rx_freq;
+ tu = 100;
+ if (p2p->cfg->start_listen(p2p->cfg->cb_ctx, rx_freq, 1024 * tu / 1000,
+ ies) < 0) {
+ wpa_msg(p2p->cfg->msg_ctx, MSG_DEBUG,
+ "P2P: Failed to start listen mode for client "
+ "discoverability");
+ }
+ wpabuf_free(ies);
+}
diff --git a/contrib/wpa/src/p2p/p2p_go_neg.c b/contrib/wpa/src/p2p/p2p_go_neg.c
new file mode 100644
index 0000000..2fdc47f
--- /dev/null
+++ b/contrib/wpa/src/p2p/p2p_go_neg.c
@@ -0,0 +1,1242 @@
+/*
+ * Wi-Fi Direct - P2P Group Owner Negotiation
+ * Copyright (c) 2009-2010, Atheros Communications
+ *
+ * This software may be distributed under the terms of the BSD license.
+ * See README for more details.
+ */
+
+#include "includes.h"
+
+#include "common.h"
+#include "common/ieee802_11_defs.h"
+#include "wps/wps_defs.h"
+#include "p2p_i.h"
+#include "p2p.h"
+
+
+static int p2p_go_det(u8 own_intent, u8 peer_value)
+{
+ u8 peer_intent = peer_value >> 1;
+ if (own_intent == peer_intent) {
+ if (own_intent == P2P_MAX_GO_INTENT)
+ return -1; /* both devices want to become GO */
+
+ /* Use tie breaker bit to determine GO */
+ return (peer_value & 0x01) ? 0 : 1;
+ }
+
+ return own_intent > peer_intent;
+}
+
+
+int p2p_peer_channels_check(struct p2p_data *p2p, struct p2p_channels *own,
+ struct p2p_device *dev,
+ const u8 *channel_list, size_t channel_list_len)
+{
+ const u8 *pos, *end;
+ struct p2p_channels *ch;
+ size_t channels;
+ struct p2p_channels intersection;
+
+ ch = &dev->channels;
+ os_memset(ch, 0, sizeof(*ch));
+ pos = channel_list;
+ end = channel_list + channel_list_len;
+
+ if (end - pos < 3)
+ return -1;
+ os_memcpy(dev->country, pos, 3);
+ wpa_hexdump_ascii(MSG_DEBUG, "P2P: Peer country", pos, 3);
+ if (pos[2] != 0x04 && os_memcmp(pos, p2p->cfg->country, 2) != 0) {
+ wpa_msg(p2p->cfg->msg_ctx, MSG_INFO,
+ "P2P: Mismatching country (ours=%c%c peer's=%c%c)",
+ p2p->cfg->country[0], p2p->cfg->country[1],
+ pos[0], pos[1]);
+ return -1;
+ }
+ pos += 3;
+
+ while (pos + 2 < end) {
+ struct p2p_reg_class *cl = &ch->reg_class[ch->reg_classes];
+ cl->reg_class = *pos++;
+ if (pos + 1 + pos[0] > end) {
+ wpa_msg(p2p->cfg->msg_ctx, MSG_INFO,
+ "P2P: Invalid peer Channel List");
+ return -1;
+ }
+ channels = *pos++;
+ cl->channels = channels > P2P_MAX_REG_CLASS_CHANNELS ?
+ P2P_MAX_REG_CLASS_CHANNELS : channels;
+ os_memcpy(cl->channel, pos, cl->channels);
+ pos += channels;
+ ch->reg_classes++;
+ if (ch->reg_classes == P2P_MAX_REG_CLASSES)
+ break;
+ }
+
+ p2p_channels_intersect(own, &dev->channels, &intersection);
+ wpa_msg(p2p->cfg->msg_ctx, MSG_DEBUG, "P2P: Own reg_classes %d "
+ "peer reg_classes %d intersection reg_classes %d",
+ (int) own->reg_classes,
+ (int) dev->channels.reg_classes,
+ (int) intersection.reg_classes);
+ if (intersection.reg_classes == 0) {
+ wpa_msg(p2p->cfg->msg_ctx, MSG_INFO,
+ "P2P: No common channels found");
+ return -1;
+ }
+ return 0;
+}
+
+
+static int p2p_peer_channels(struct p2p_data *p2p, struct p2p_device *dev,
+ const u8 *channel_list, size_t channel_list_len)
+{
+ return p2p_peer_channels_check(p2p, &p2p->channels, dev,
+ channel_list, channel_list_len);
+}
+
+
+u16 p2p_wps_method_pw_id(enum p2p_wps_method wps_method)
+{
+ switch (wps_method) {
+ case WPS_PIN_DISPLAY:
+ return DEV_PW_REGISTRAR_SPECIFIED;
+ case WPS_PIN_KEYPAD:
+ return DEV_PW_USER_SPECIFIED;
+ case WPS_PBC:
+ return DEV_PW_PUSHBUTTON;
+ default:
+ return DEV_PW_DEFAULT;
+ }
+}
+
+
+static const char * p2p_wps_method_str(enum p2p_wps_method wps_method)
+{
+ switch (wps_method) {
+ case WPS_PIN_DISPLAY:
+ return "Display";
+ case WPS_PIN_KEYPAD:
+ return "Keypad";
+ case WPS_PBC:
+ return "PBC";
+ default:
+ return "??";
+ }
+}
+
+
+static struct wpabuf * p2p_build_go_neg_req(struct p2p_data *p2p,
+ struct p2p_device *peer)
+{
+ struct wpabuf *buf;
+ u8 *len;
+ u8 group_capab;
+ size_t extra = 0;
+
+#ifdef CONFIG_WIFI_DISPLAY
+ if (p2p->wfd_ie_go_neg)
+ extra = wpabuf_len(p2p->wfd_ie_go_neg);
+#endif /* CONFIG_WIFI_DISPLAY */
+
+ buf = wpabuf_alloc(1000 + extra);
+ if (buf == NULL)
+ return NULL;
+
+ peer->dialog_token++;
+ if (peer->dialog_token == 0)
+ peer->dialog_token = 1;
+ p2p_buf_add_public_action_hdr(buf, P2P_GO_NEG_REQ, peer->dialog_token);
+
+ len = p2p_buf_add_ie_hdr(buf);
+ group_capab = 0;
+ if (peer->flags & P2P_DEV_PREFER_PERSISTENT_GROUP) {
+ group_capab |= P2P_GROUP_CAPAB_PERSISTENT_GROUP;
+ if (peer->flags & P2P_DEV_PREFER_PERSISTENT_RECONN)
+ group_capab |= P2P_GROUP_CAPAB_PERSISTENT_RECONN;
+ }
+ if (p2p->cross_connect)
+ group_capab |= P2P_GROUP_CAPAB_CROSS_CONN;
+ if (p2p->cfg->p2p_intra_bss)
+ group_capab |= P2P_GROUP_CAPAB_INTRA_BSS_DIST;
+ p2p_buf_add_capability(buf, p2p->dev_capab &
+ ~P2P_DEV_CAPAB_CLIENT_DISCOVERABILITY,
+ group_capab);
+ p2p_buf_add_go_intent(buf, (p2p->go_intent << 1) |
+ p2p->next_tie_breaker);
+ p2p->next_tie_breaker = !p2p->next_tie_breaker;
+ p2p_buf_add_config_timeout(buf, p2p->go_timeout, p2p->client_timeout);
+ p2p_buf_add_listen_channel(buf, p2p->cfg->country, p2p->cfg->reg_class,
+ p2p->cfg->channel);
+ if (p2p->ext_listen_interval)
+ p2p_buf_add_ext_listen_timing(buf, p2p->ext_listen_period,
+ p2p->ext_listen_interval);
+ p2p_buf_add_intended_addr(buf, p2p->intended_addr);
+ p2p_buf_add_channel_list(buf, p2p->cfg->country, &p2p->channels);
+ p2p_buf_add_device_info(buf, p2p, peer);
+ p2p_buf_add_operating_channel(buf, p2p->cfg->country,
+ p2p->op_reg_class, p2p->op_channel);
+ p2p_buf_update_ie_hdr(buf, len);
+
+ /* WPS IE with Device Password ID attribute */
+ p2p_build_wps_ie(p2p, buf, p2p_wps_method_pw_id(peer->wps_method), 0);
+
+#ifdef CONFIG_WIFI_DISPLAY
+ if (p2p->wfd_ie_go_neg)
+ wpabuf_put_buf(buf, p2p->wfd_ie_go_neg);
+#endif /* CONFIG_WIFI_DISPLAY */
+
+ return buf;
+}
+
+
+int p2p_connect_send(struct p2p_data *p2p, struct p2p_device *dev)
+{
+ struct wpabuf *req;
+ int freq;
+
+ if (dev->flags & P2P_DEV_PD_BEFORE_GO_NEG) {
+ u16 config_method;
+ wpa_msg(p2p->cfg->msg_ctx, MSG_DEBUG,
+ "P2P: Use PD-before-GO-Neg workaround for " MACSTR,
+ MAC2STR(dev->info.p2p_device_addr));
+ if (dev->wps_method == WPS_PIN_DISPLAY)
+ config_method = WPS_CONFIG_KEYPAD;
+ else if (dev->wps_method == WPS_PIN_KEYPAD)
+ config_method = WPS_CONFIG_DISPLAY;
+ else if (dev->wps_method == WPS_PBC)
+ config_method = WPS_CONFIG_PUSHBUTTON;
+ else
+ return -1;
+ return p2p_prov_disc_req(p2p, dev->info.p2p_device_addr,
+ config_method, 0, 0, 1);
+ }
+
+ freq = dev->listen_freq > 0 ? dev->listen_freq : dev->oper_freq;
+ if (freq <= 0) {
+ wpa_msg(p2p->cfg->msg_ctx, MSG_DEBUG,
+ "P2P: No Listen/Operating frequency known for the "
+ "peer " MACSTR " to send GO Negotiation Request",
+ MAC2STR(dev->info.p2p_device_addr));
+ return -1;
+ }
+
+ req = p2p_build_go_neg_req(p2p, dev);
+ if (req == NULL)
+ return -1;
+ wpa_msg(p2p->cfg->msg_ctx, MSG_DEBUG,
+ "P2P: Sending GO Negotiation Request");
+ p2p_set_state(p2p, P2P_CONNECT);
+ p2p->pending_action_state = P2P_PENDING_GO_NEG_REQUEST;
+ p2p->go_neg_peer = dev;
+ dev->flags |= P2P_DEV_WAIT_GO_NEG_RESPONSE;
+ dev->connect_reqs++;
+ if (p2p_send_action(p2p, freq, dev->info.p2p_device_addr,
+ p2p->cfg->dev_addr, dev->info.p2p_device_addr,
+ wpabuf_head(req), wpabuf_len(req), 200) < 0) {
+ wpa_msg(p2p->cfg->msg_ctx, MSG_DEBUG,
+ "P2P: Failed to send Action frame");
+ /* Use P2P find to recover and retry */
+ p2p_set_timeout(p2p, 0, 0);
+ } else
+ dev->go_neg_req_sent++;
+
+ wpabuf_free(req);
+
+ return 0;
+}
+
+
+static struct wpabuf * p2p_build_go_neg_resp(struct p2p_data *p2p,
+ struct p2p_device *peer,
+ u8 dialog_token, u8 status,
+ u8 tie_breaker)
+{
+ struct wpabuf *buf;
+ u8 *len;
+ u8 group_capab;
+ size_t extra = 0;
+
+ wpa_msg(p2p->cfg->msg_ctx, MSG_DEBUG,
+ "P2P: Building GO Negotiation Response");
+
+#ifdef CONFIG_WIFI_DISPLAY
+ if (p2p->wfd_ie_go_neg)
+ extra = wpabuf_len(p2p->wfd_ie_go_neg);
+#endif /* CONFIG_WIFI_DISPLAY */
+
+ buf = wpabuf_alloc(1000 + extra);
+ if (buf == NULL)
+ return NULL;
+
+ p2p_buf_add_public_action_hdr(buf, P2P_GO_NEG_RESP, dialog_token);
+
+ len = p2p_buf_add_ie_hdr(buf);
+ p2p_buf_add_status(buf, status);
+ group_capab = 0;
+ if (peer && peer->go_state == LOCAL_GO) {
+ if (peer->flags & P2P_DEV_PREFER_PERSISTENT_GROUP) {
+ group_capab |= P2P_GROUP_CAPAB_PERSISTENT_GROUP;
+ if (peer->flags & P2P_DEV_PREFER_PERSISTENT_RECONN)
+ group_capab |=
+ P2P_GROUP_CAPAB_PERSISTENT_RECONN;
+ }
+ if (p2p->cross_connect)
+ group_capab |= P2P_GROUP_CAPAB_CROSS_CONN;
+ if (p2p->cfg->p2p_intra_bss)
+ group_capab |= P2P_GROUP_CAPAB_INTRA_BSS_DIST;
+ }
+ p2p_buf_add_capability(buf, p2p->dev_capab &
+ ~P2P_DEV_CAPAB_CLIENT_DISCOVERABILITY,
+ group_capab);
+ p2p_buf_add_go_intent(buf, (p2p->go_intent << 1) | tie_breaker);
+ p2p_buf_add_config_timeout(buf, p2p->go_timeout, p2p->client_timeout);
+ if (peer && peer->go_state == REMOTE_GO) {
+ wpa_msg(p2p->cfg->msg_ctx, MSG_DEBUG, "P2P: Omit Operating "
+ "Channel attribute");
+ } else {
+ p2p_buf_add_operating_channel(buf, p2p->cfg->country,
+ p2p->op_reg_class,
+ p2p->op_channel);
+ }
+ p2p_buf_add_intended_addr(buf, p2p->intended_addr);
+ if (status || peer == NULL) {
+ p2p_buf_add_channel_list(buf, p2p->cfg->country,
+ &p2p->channels);
+ } else if (peer->go_state == REMOTE_GO) {
+ p2p_buf_add_channel_list(buf, p2p->cfg->country,
+ &p2p->channels);
+ } else {
+ struct p2p_channels res;
+ p2p_channels_intersect(&p2p->channels, &peer->channels,
+ &res);
+ p2p_buf_add_channel_list(buf, p2p->cfg->country, &res);
+ }
+ p2p_buf_add_device_info(buf, p2p, peer);
+ if (peer && peer->go_state == LOCAL_GO) {
+ p2p_buf_add_group_id(buf, p2p->cfg->dev_addr, p2p->ssid,
+ p2p->ssid_len);
+ }
+ p2p_buf_update_ie_hdr(buf, len);
+
+ /* WPS IE with Device Password ID attribute */
+ p2p_build_wps_ie(p2p, buf,
+ p2p_wps_method_pw_id(peer ? peer->wps_method :
+ WPS_NOT_READY), 0);
+
+#ifdef CONFIG_WIFI_DISPLAY
+ if (p2p->wfd_ie_go_neg)
+ wpabuf_put_buf(buf, p2p->wfd_ie_go_neg);
+#endif /* CONFIG_WIFI_DISPLAY */
+
+
+ return buf;
+}
+
+
+/**
+ * p2p_reselect_channel - Re-select operating channel based on peer information
+ * @p2p: P2P module context from p2p_init()
+ * @intersection: Support channel list intersection from local and peer
+ *
+ * This function is used to re-select the best channel after having received
+ * information from the peer to allow supported channel lists to be intersected.
+ * This can be used to improve initial channel selection done in
+ * p2p_prepare_channel() prior to the start of GO Negotiation. In addition, this
+ * can be used for Invitation case.
+ */
+void p2p_reselect_channel(struct p2p_data *p2p,
+ struct p2p_channels *intersection)
+{
+ struct p2p_reg_class *cl;
+ int freq;
+ u8 op_reg_class, op_channel;
+ unsigned int i;
+
+ /* First, try to pick the best channel from another band */
+ freq = p2p_channel_to_freq(p2p->cfg->country, p2p->op_reg_class,
+ p2p->op_channel);
+ if (freq >= 2400 && freq < 2500 && p2p->best_freq_5 > 0 &&
+ p2p_freq_to_channel(p2p->cfg->country, p2p->best_freq_5,
+ &op_reg_class, &op_channel) == 0 &&
+ p2p_channels_includes(intersection, op_reg_class, op_channel)) {
+ wpa_msg(p2p->cfg->msg_ctx, MSG_DEBUG, "P2P: Pick best 5 GHz "
+ "channel (reg_class %u channel %u) from intersection",
+ op_reg_class, op_channel);
+ p2p->op_reg_class = op_reg_class;
+ p2p->op_channel = op_channel;
+ return;
+ }
+
+ if (freq >= 4900 && freq < 6000 && p2p->best_freq_24 > 0 &&
+ p2p_freq_to_channel(p2p->cfg->country, p2p->best_freq_24,
+ &op_reg_class, &op_channel) == 0 &&
+ p2p_channels_includes(intersection, op_reg_class, op_channel)) {
+ wpa_msg(p2p->cfg->msg_ctx, MSG_DEBUG, "P2P: Pick best 2.4 GHz "
+ "channel (reg_class %u channel %u) from intersection",
+ op_reg_class, op_channel);
+ p2p->op_reg_class = op_reg_class;
+ p2p->op_channel = op_channel;
+ return;
+ }
+
+ /* Select channel with highest preference if the peer supports it */
+ for (i = 0; p2p->cfg->pref_chan && i < p2p->cfg->num_pref_chan; i++) {
+ if (p2p_channels_includes(intersection,
+ p2p->cfg->pref_chan[i].op_class,
+ p2p->cfg->pref_chan[i].chan)) {
+ p2p->op_reg_class = p2p->cfg->pref_chan[i].op_class;
+ p2p->op_channel = p2p->cfg->pref_chan[i].chan;
+ wpa_msg(p2p->cfg->msg_ctx, MSG_DEBUG, "P2P: Pick "
+ "highest preferred chnnel (op_class %u "
+ "channel %u) from intersection",
+ p2p->op_reg_class, p2p->op_channel);
+ return;
+ }
+ }
+
+ /* Try a channel where we might be able to use HT40 */
+ for (i = 0; i < intersection->reg_classes; i++) {
+ struct p2p_reg_class *c = &intersection->reg_class[i];
+ if (c->reg_class == 116 || c->reg_class == 117 ||
+ c->reg_class == 126 || c->reg_class == 127) {
+ wpa_msg(p2p->cfg->msg_ctx, MSG_DEBUG,
+ "P2P: Pick possible HT40 channel (reg_class "
+ "%u channel %u) from intersection",
+ c->reg_class, c->channel[0]);
+ p2p->op_reg_class = c->reg_class;
+ p2p->op_channel = c->channel[0];
+ return;
+ }
+ }
+
+ /*
+ * Try to see if the original channel is in the intersection. If
+ * so, no need to change anything, as it already contains some
+ * randomness.
+ */
+ if (p2p_channels_includes(intersection, p2p->op_reg_class,
+ p2p->op_channel)) {
+ wpa_msg(p2p->cfg->msg_ctx, MSG_DEBUG,
+ "P2P: Using original operating class and channel "
+ "(op_class %u channel %u) from intersection",
+ p2p->op_reg_class, p2p->op_channel);
+ return;
+ }
+
+ /*
+ * Fall back to whatever is included in the channel intersection since
+ * no better options seems to be available.
+ */
+ cl = &intersection->reg_class[0];
+ wpa_msg(p2p->cfg->msg_ctx, MSG_DEBUG, "P2P: Pick another channel "
+ "(reg_class %u channel %u) from intersection",
+ cl->reg_class, cl->channel[0]);
+ p2p->op_reg_class = cl->reg_class;
+ p2p->op_channel = cl->channel[0];
+}
+
+
+static int p2p_go_select_channel(struct p2p_data *p2p, struct p2p_device *dev,
+ u8 *status)
+{
+ struct p2p_channels intersection;
+ size_t i;
+
+ p2p_channels_intersect(&p2p->channels, &dev->channels, &intersection);
+ if (intersection.reg_classes == 0 ||
+ intersection.reg_class[0].channels == 0) {
+ *status = P2P_SC_FAIL_NO_COMMON_CHANNELS;
+ wpa_msg(p2p->cfg->msg_ctx, MSG_DEBUG,
+ "P2P: No common channels found");
+ return -1;
+ }
+
+ for (i = 0; i < intersection.reg_classes; i++) {
+ struct p2p_reg_class *c;
+ c = &intersection.reg_class[i];
+ wpa_printf(MSG_DEBUG, "P2P: reg_class %u", c->reg_class);
+ wpa_hexdump(MSG_DEBUG, "P2P: channels",
+ c->channel, c->channels);
+ }
+
+ if (!p2p_channels_includes(&intersection, p2p->op_reg_class,
+ p2p->op_channel)) {
+ if (dev->flags & P2P_DEV_FORCE_FREQ) {
+ *status = P2P_SC_FAIL_NO_COMMON_CHANNELS;
+ wpa_msg(p2p->cfg->msg_ctx, MSG_DEBUG, "P2P: Peer does "
+ "not support the forced channel");
+ return -1;
+ }
+
+ wpa_msg(p2p->cfg->msg_ctx, MSG_DEBUG, "P2P: Selected operating "
+ "channel (op_class %u channel %u) not acceptable to "
+ "the peer", p2p->op_reg_class, p2p->op_channel);
+ p2p_reselect_channel(p2p, &intersection);
+ } else if (!(dev->flags & P2P_DEV_FORCE_FREQ) &&
+ !p2p->cfg->cfg_op_channel) {
+ wpa_msg(p2p->cfg->msg_ctx, MSG_DEBUG, "P2P: Try to optimize "
+ "channel selection with peer information received; "
+ "previously selected op_class %u channel %u",
+ p2p->op_reg_class, p2p->op_channel);
+ p2p_reselect_channel(p2p, &intersection);
+ }
+
+ if (!p2p->ssid_set) {
+ p2p_build_ssid(p2p, p2p->ssid, &p2p->ssid_len);
+ p2p->ssid_set = 1;
+ }
+
+ return 0;
+}
+
+
+void p2p_process_go_neg_req(struct p2p_data *p2p, const u8 *sa,
+ const u8 *data, size_t len, int rx_freq)
+{
+ struct p2p_device *dev = NULL;
+ struct wpabuf *resp;
+ struct p2p_message msg;
+ u8 status = P2P_SC_FAIL_INVALID_PARAMS;
+ int tie_breaker = 0;
+ int freq;
+
+ wpa_msg(p2p->cfg->msg_ctx, MSG_DEBUG,
+ "P2P: Received GO Negotiation Request from " MACSTR
+ "(freq=%d)", MAC2STR(sa), rx_freq);
+
+ if (p2p_parse(data, len, &msg))
+ return;
+
+ if (!msg.capability) {
+ wpa_msg(p2p->cfg->msg_ctx, MSG_DEBUG,
+ "P2P: Mandatory Capability attribute missing from GO "
+ "Negotiation Request");
+#ifdef CONFIG_P2P_STRICT
+ goto fail;
+#endif /* CONFIG_P2P_STRICT */
+ }
+
+ if (msg.go_intent)
+ tie_breaker = *msg.go_intent & 0x01;
+ else {
+ wpa_msg(p2p->cfg->msg_ctx, MSG_DEBUG,
+ "P2P: Mandatory GO Intent attribute missing from GO "
+ "Negotiation Request");
+#ifdef CONFIG_P2P_STRICT
+ goto fail;
+#endif /* CONFIG_P2P_STRICT */
+ }
+
+ if (!msg.config_timeout) {
+ wpa_msg(p2p->cfg->msg_ctx, MSG_DEBUG,
+ "P2P: Mandatory Configuration Timeout attribute "
+ "missing from GO Negotiation Request");
+#ifdef CONFIG_P2P_STRICT
+ goto fail;
+#endif /* CONFIG_P2P_STRICT */
+ }
+
+ if (!msg.listen_channel) {
+ wpa_msg(p2p->cfg->msg_ctx, MSG_DEBUG,
+ "P2P: No Listen Channel attribute received");
+ goto fail;
+ }
+ if (!msg.operating_channel) {
+ wpa_msg(p2p->cfg->msg_ctx, MSG_DEBUG,
+ "P2P: No Operating Channel attribute received");
+ goto fail;
+ }
+ if (!msg.channel_list) {
+ wpa_msg(p2p->cfg->msg_ctx, MSG_DEBUG,
+ "P2P: No Channel List attribute received");
+ goto fail;
+ }
+ if (!msg.intended_addr) {
+ wpa_msg(p2p->cfg->msg_ctx, MSG_DEBUG,
+ "P2P: No Intended P2P Interface Address attribute "
+ "received");
+ goto fail;
+ }
+ if (!msg.p2p_device_info) {
+ wpa_msg(p2p->cfg->msg_ctx, MSG_DEBUG,
+ "P2P: No P2P Device Info attribute received");
+ goto fail;
+ }
+
+ if (os_memcmp(msg.p2p_device_addr, sa, ETH_ALEN) != 0) {
+ wpa_msg(p2p->cfg->msg_ctx, MSG_DEBUG,
+ "P2P: Unexpected GO Negotiation Request SA=" MACSTR
+ " != dev_addr=" MACSTR,
+ MAC2STR(sa), MAC2STR(msg.p2p_device_addr));
+ goto fail;
+ }
+
+ dev = p2p_get_device(p2p, sa);
+
+ if (msg.status && *msg.status) {
+ wpa_msg(p2p->cfg->msg_ctx, MSG_DEBUG,
+ "P2P: Unexpected Status attribute (%d) in GO "
+ "Negotiation Request", *msg.status);
+ goto fail;
+ }
+
+ if (dev == NULL)
+ dev = p2p_add_dev_from_go_neg_req(p2p, sa, &msg);
+ else if (dev->flags & P2P_DEV_PROBE_REQ_ONLY)
+ p2p_add_dev_info(p2p, sa, dev, &msg);
+ if (dev && dev->flags & P2P_DEV_USER_REJECTED) {
+ wpa_msg(p2p->cfg->msg_ctx, MSG_DEBUG,
+ "P2P: User has rejected this peer");
+ status = P2P_SC_FAIL_REJECTED_BY_USER;
+ } else if (dev == NULL || dev->wps_method == WPS_NOT_READY) {
+ wpa_msg(p2p->cfg->msg_ctx, MSG_DEBUG,
+ "P2P: Not ready for GO negotiation with " MACSTR,
+ MAC2STR(sa));
+ status = P2P_SC_FAIL_INFO_CURRENTLY_UNAVAILABLE;
+ if (dev)
+ dev->flags |= P2P_DEV_PEER_WAITING_RESPONSE;
+ p2p->cfg->go_neg_req_rx(p2p->cfg->cb_ctx, sa,
+ msg.dev_password_id);
+ } else if (p2p->go_neg_peer && p2p->go_neg_peer != dev) {
+ wpa_msg(p2p->cfg->msg_ctx, MSG_DEBUG,
+ "P2P: Already in Group Formation with another peer");
+ status = P2P_SC_FAIL_UNABLE_TO_ACCOMMODATE;
+ } else {
+ int go;
+
+ if (!p2p->go_neg_peer) {
+ wpa_msg(p2p->cfg->msg_ctx, MSG_DEBUG, "P2P: Starting "
+ "GO Negotiation with previously authorized "
+ "peer");
+ if (!(dev->flags & P2P_DEV_FORCE_FREQ)) {
+ wpa_msg(p2p->cfg->msg_ctx, MSG_DEBUG,
+ "P2P: Use default channel settings");
+ p2p->op_reg_class = p2p->cfg->op_reg_class;
+ p2p->op_channel = p2p->cfg->op_channel;
+ os_memcpy(&p2p->channels, &p2p->cfg->channels,
+ sizeof(struct p2p_channels));
+ } else {
+ wpa_msg(p2p->cfg->msg_ctx, MSG_DEBUG,
+ "P2P: Use previously configured "
+ "forced channel settings");
+ }
+ }
+
+ dev->flags &= ~P2P_DEV_NOT_YET_READY;
+
+ if (!msg.go_intent) {
+ wpa_msg(p2p->cfg->msg_ctx, MSG_DEBUG,
+ "P2P: No GO Intent attribute received");
+ goto fail;
+ }
+ if ((*msg.go_intent >> 1) > P2P_MAX_GO_INTENT) {
+ wpa_msg(p2p->cfg->msg_ctx, MSG_DEBUG,
+ "P2P: Invalid GO Intent value (%u) received",
+ *msg.go_intent >> 1);
+ goto fail;
+ }
+
+ if (dev->go_neg_req_sent &&
+ os_memcmp(sa, p2p->cfg->dev_addr, ETH_ALEN) > 0) {
+ wpa_msg(p2p->cfg->msg_ctx, MSG_DEBUG,
+ "P2P: Do not reply since peer has higher "
+ "address and GO Neg Request already sent");
+ p2p_parse_free(&msg);
+ return;
+ }
+
+ go = p2p_go_det(p2p->go_intent, *msg.go_intent);
+ if (go < 0) {
+ wpa_msg(p2p->cfg->msg_ctx, MSG_DEBUG,
+ "P2P: Incompatible GO Intent");
+ status = P2P_SC_FAIL_BOTH_GO_INTENT_15;
+ goto fail;
+ }
+
+ if (p2p_peer_channels(p2p, dev, msg.channel_list,
+ msg.channel_list_len) < 0) {
+ wpa_msg(p2p->cfg->msg_ctx, MSG_DEBUG,
+ "P2P: No common channels found");
+ status = P2P_SC_FAIL_NO_COMMON_CHANNELS;
+ goto fail;
+ }
+
+ switch (msg.dev_password_id) {
+ case DEV_PW_REGISTRAR_SPECIFIED:
+ wpa_msg(p2p->cfg->msg_ctx, MSG_DEBUG,
+ "P2P: PIN from peer Display");
+ if (dev->wps_method != WPS_PIN_KEYPAD) {
+ wpa_msg(p2p->cfg->msg_ctx, MSG_DEBUG,
+ "P2P: We have wps_method=%s -> "
+ "incompatible",
+ p2p_wps_method_str(dev->wps_method));
+ status = P2P_SC_FAIL_INCOMPATIBLE_PROV_METHOD;
+ goto fail;
+ }
+ break;
+ case DEV_PW_USER_SPECIFIED:
+ wpa_msg(p2p->cfg->msg_ctx, MSG_DEBUG,
+ "P2P: Peer entered PIN on Keypad");
+ if (dev->wps_method != WPS_PIN_DISPLAY) {
+ wpa_msg(p2p->cfg->msg_ctx, MSG_DEBUG,
+ "P2P: We have wps_method=%s -> "
+ "incompatible",
+ p2p_wps_method_str(dev->wps_method));
+ status = P2P_SC_FAIL_INCOMPATIBLE_PROV_METHOD;
+ goto fail;
+ }
+ break;
+ case DEV_PW_PUSHBUTTON:
+ wpa_msg(p2p->cfg->msg_ctx, MSG_DEBUG,
+ "P2P: Peer using pushbutton");
+ if (dev->wps_method != WPS_PBC) {
+ wpa_msg(p2p->cfg->msg_ctx, MSG_DEBUG,
+ "P2P: We have wps_method=%s -> "
+ "incompatible",
+ p2p_wps_method_str(dev->wps_method));
+ status = P2P_SC_FAIL_INCOMPATIBLE_PROV_METHOD;
+ goto fail;
+ }
+ break;
+ default:
+ wpa_msg(p2p->cfg->msg_ctx, MSG_DEBUG,
+ "P2P: Unsupported Device Password ID %d",
+ msg.dev_password_id);
+ status = P2P_SC_FAIL_INCOMPATIBLE_PROV_METHOD;
+ goto fail;
+ }
+
+ if (go && p2p_go_select_channel(p2p, dev, &status) < 0)
+ goto fail;
+
+ dev->go_state = go ? LOCAL_GO : REMOTE_GO;
+ dev->oper_freq = p2p_channel_to_freq((const char *)
+ msg.operating_channel,
+ msg.operating_channel[3],
+ msg.operating_channel[4]);
+ wpa_msg(p2p->cfg->msg_ctx, MSG_DEBUG, "P2P: Peer operating "
+ "channel preference: %d MHz", dev->oper_freq);
+
+ if (msg.config_timeout) {
+ dev->go_timeout = msg.config_timeout[0];
+ dev->client_timeout = msg.config_timeout[1];
+ }
+
+ wpa_msg(p2p->cfg->msg_ctx, MSG_DEBUG,
+ "P2P: GO Negotiation with " MACSTR, MAC2STR(sa));
+ if (p2p->state != P2P_IDLE)
+ p2p_stop_find_for_freq(p2p, rx_freq);
+ p2p_set_state(p2p, P2P_GO_NEG);
+ p2p_clear_timeout(p2p);
+ dev->dialog_token = msg.dialog_token;
+ os_memcpy(dev->intended_addr, msg.intended_addr, ETH_ALEN);
+ p2p->go_neg_peer = dev;
+ status = P2P_SC_SUCCESS;
+ }
+
+fail:
+ if (dev)
+ dev->status = status;
+ resp = p2p_build_go_neg_resp(p2p, dev, msg.dialog_token, status,
+ !tie_breaker);
+ p2p_parse_free(&msg);
+ if (resp == NULL)
+ return;
+ wpa_msg(p2p->cfg->msg_ctx, MSG_DEBUG,
+ "P2P: Sending GO Negotiation Response");
+ if (rx_freq > 0)
+ freq = rx_freq;
+ else
+ freq = p2p_channel_to_freq(p2p->cfg->country,
+ p2p->cfg->reg_class,
+ p2p->cfg->channel);
+ if (freq < 0) {
+ wpa_msg(p2p->cfg->msg_ctx, MSG_DEBUG,
+ "P2P: Unknown regulatory class/channel");
+ wpabuf_free(resp);
+ return;
+ }
+ if (status == P2P_SC_SUCCESS) {
+ p2p->pending_action_state = P2P_PENDING_GO_NEG_RESPONSE;
+ dev->flags |= P2P_DEV_WAIT_GO_NEG_CONFIRM;
+ if (os_memcmp(sa, p2p->cfg->dev_addr, ETH_ALEN) < 0) {
+ /*
+ * Peer has smaller address, so the GO Negotiation
+ * Response from us is expected to complete
+ * negotiation. Ignore a GO Negotiation Response from
+ * the peer if it happens to be received after this
+ * point due to a race condition in GO Negotiation
+ * Request transmission and processing.
+ */
+ dev->flags &= ~P2P_DEV_WAIT_GO_NEG_RESPONSE;
+ }
+ } else
+ p2p->pending_action_state =
+ P2P_PENDING_GO_NEG_RESPONSE_FAILURE;
+ if (p2p_send_action(p2p, freq, sa, p2p->cfg->dev_addr,
+ p2p->cfg->dev_addr,
+ wpabuf_head(resp), wpabuf_len(resp), 250) < 0) {
+ wpa_msg(p2p->cfg->msg_ctx, MSG_DEBUG,
+ "P2P: Failed to send Action frame");
+ }
+
+ wpabuf_free(resp);
+}
+
+
+static struct wpabuf * p2p_build_go_neg_conf(struct p2p_data *p2p,
+ struct p2p_device *peer,
+ u8 dialog_token, u8 status,
+ const u8 *resp_chan, int go)
+{
+ struct wpabuf *buf;
+ u8 *len;
+ struct p2p_channels res;
+ u8 group_capab;
+ size_t extra = 0;
+
+ wpa_msg(p2p->cfg->msg_ctx, MSG_DEBUG,
+ "P2P: Building GO Negotiation Confirm");
+
+#ifdef CONFIG_WIFI_DISPLAY
+ if (p2p->wfd_ie_go_neg)
+ extra = wpabuf_len(p2p->wfd_ie_go_neg);
+#endif /* CONFIG_WIFI_DISPLAY */
+
+ buf = wpabuf_alloc(1000 + extra);
+ if (buf == NULL)
+ return NULL;
+
+ p2p_buf_add_public_action_hdr(buf, P2P_GO_NEG_CONF, dialog_token);
+
+ len = p2p_buf_add_ie_hdr(buf);
+ p2p_buf_add_status(buf, status);
+ group_capab = 0;
+ if (peer->go_state == LOCAL_GO) {
+ if (peer->flags & P2P_DEV_PREFER_PERSISTENT_GROUP) {
+ group_capab |= P2P_GROUP_CAPAB_PERSISTENT_GROUP;
+ if (peer->flags & P2P_DEV_PREFER_PERSISTENT_RECONN)
+ group_capab |=
+ P2P_GROUP_CAPAB_PERSISTENT_RECONN;
+ }
+ if (p2p->cross_connect)
+ group_capab |= P2P_GROUP_CAPAB_CROSS_CONN;
+ if (p2p->cfg->p2p_intra_bss)
+ group_capab |= P2P_GROUP_CAPAB_INTRA_BSS_DIST;
+ }
+ p2p_buf_add_capability(buf, p2p->dev_capab &
+ ~P2P_DEV_CAPAB_CLIENT_DISCOVERABILITY,
+ group_capab);
+ if (go || resp_chan == NULL)
+ p2p_buf_add_operating_channel(buf, p2p->cfg->country,
+ p2p->op_reg_class,
+ p2p->op_channel);
+ else
+ p2p_buf_add_operating_channel(buf, (const char *) resp_chan,
+ resp_chan[3], resp_chan[4]);
+ p2p_channels_intersect(&p2p->channels, &peer->channels, &res);
+ p2p_buf_add_channel_list(buf, p2p->cfg->country, &res);
+ if (go) {
+ p2p_buf_add_group_id(buf, p2p->cfg->dev_addr, p2p->ssid,
+ p2p->ssid_len);
+ }
+ p2p_buf_update_ie_hdr(buf, len);
+
+#ifdef CONFIG_WIFI_DISPLAY
+ if (p2p->wfd_ie_go_neg)
+ wpabuf_put_buf(buf, p2p->wfd_ie_go_neg);
+#endif /* CONFIG_WIFI_DISPLAY */
+
+ return buf;
+}
+
+
+void p2p_process_go_neg_resp(struct p2p_data *p2p, const u8 *sa,
+ const u8 *data, size_t len, int rx_freq)
+{
+ struct p2p_device *dev;
+ struct wpabuf *conf;
+ int go = -1;
+ struct p2p_message msg;
+ u8 status = P2P_SC_SUCCESS;
+ int freq;
+
+ wpa_msg(p2p->cfg->msg_ctx, MSG_DEBUG,
+ "P2P: Received GO Negotiation Response from " MACSTR
+ " (freq=%d)", MAC2STR(sa), rx_freq);
+ dev = p2p_get_device(p2p, sa);
+ if (dev == NULL || dev->wps_method == WPS_NOT_READY ||
+ dev != p2p->go_neg_peer) {
+ wpa_msg(p2p->cfg->msg_ctx, MSG_DEBUG,
+ "P2P: Not ready for GO negotiation with " MACSTR,
+ MAC2STR(sa));
+ return;
+ }
+
+ if (p2p_parse(data, len, &msg))
+ return;
+
+ if (!(dev->flags & P2P_DEV_WAIT_GO_NEG_RESPONSE)) {
+ wpa_msg(p2p->cfg->msg_ctx, MSG_DEBUG,
+ "P2P: Was not expecting GO Negotiation Response - "
+ "ignore");
+ p2p_parse_free(&msg);
+ return;
+ }
+ dev->flags &= ~P2P_DEV_WAIT_GO_NEG_RESPONSE;
+
+ if (msg.dialog_token != dev->dialog_token) {
+ wpa_msg(p2p->cfg->msg_ctx, MSG_DEBUG,
+ "P2P: Unexpected Dialog Token %u (expected %u)",
+ msg.dialog_token, dev->dialog_token);
+ p2p_parse_free(&msg);
+ return;
+ }
+
+ if (!msg.status) {
+ wpa_msg(p2p->cfg->msg_ctx, MSG_DEBUG,
+ "P2P: No Status attribute received");
+ status = P2P_SC_FAIL_INVALID_PARAMS;
+ goto fail;
+ }
+ if (*msg.status) {
+ wpa_msg(p2p->cfg->msg_ctx, MSG_DEBUG,
+ "P2P: GO Negotiation rejected: status %d",
+ *msg.status);
+ dev->go_neg_req_sent = 0;
+ if (*msg.status == P2P_SC_FAIL_INFO_CURRENTLY_UNAVAILABLE) {
+ wpa_msg(p2p->cfg->msg_ctx, MSG_DEBUG,
+ "P2P: Wait for the peer to become ready for "
+ "GO Negotiation");
+ dev->flags |= P2P_DEV_NOT_YET_READY;
+ dev->wait_count = 0;
+ p2p_set_state(p2p, P2P_WAIT_PEER_IDLE);
+ p2p_set_timeout(p2p, 0, 0);
+ } else {
+ wpa_msg(p2p->cfg->msg_ctx, MSG_DEBUG,
+ "P2P: Stop GO Negotiation attempt");
+ p2p_go_neg_failed(p2p, dev, *msg.status);
+ }
+ p2p->cfg->send_action_done(p2p->cfg->cb_ctx);
+ p2p_parse_free(&msg);
+ return;
+ }
+
+ if (!msg.capability) {
+ wpa_msg(p2p->cfg->msg_ctx, MSG_DEBUG,
+ "P2P: Mandatory Capability attribute missing from GO "
+ "Negotiation Response");
+#ifdef CONFIG_P2P_STRICT
+ status = P2P_SC_FAIL_INVALID_PARAMS;
+ goto fail;
+#endif /* CONFIG_P2P_STRICT */
+ }
+
+ if (!msg.p2p_device_info) {
+ wpa_msg(p2p->cfg->msg_ctx, MSG_DEBUG,
+ "P2P: Mandatory P2P Device Info attribute missing "
+ "from GO Negotiation Response");
+#ifdef CONFIG_P2P_STRICT
+ status = P2P_SC_FAIL_INVALID_PARAMS;
+ goto fail;
+#endif /* CONFIG_P2P_STRICT */
+ }
+
+ if (!msg.intended_addr) {
+ wpa_msg(p2p->cfg->msg_ctx, MSG_DEBUG,
+ "P2P: No Intended P2P Interface Address attribute "
+ "received");
+ status = P2P_SC_FAIL_INVALID_PARAMS;
+ goto fail;
+ }
+
+ if (!msg.go_intent) {
+ wpa_msg(p2p->cfg->msg_ctx, MSG_DEBUG,
+ "P2P: No GO Intent attribute received");
+ status = P2P_SC_FAIL_INVALID_PARAMS;
+ goto fail;
+ }
+ if ((*msg.go_intent >> 1) > P2P_MAX_GO_INTENT) {
+ wpa_msg(p2p->cfg->msg_ctx, MSG_DEBUG,
+ "P2P: Invalid GO Intent value (%u) received",
+ *msg.go_intent >> 1);
+ status = P2P_SC_FAIL_INVALID_PARAMS;
+ goto fail;
+ }
+
+ go = p2p_go_det(p2p->go_intent, *msg.go_intent);
+ if (go < 0) {
+ wpa_msg(p2p->cfg->msg_ctx, MSG_DEBUG,
+ "P2P: Incompatible GO Intent");
+ status = P2P_SC_FAIL_INCOMPATIBLE_PARAMS;
+ goto fail;
+ }
+
+ if (!go && msg.group_id) {
+ /* Store SSID for Provisioning step */
+ p2p->ssid_len = msg.group_id_len - ETH_ALEN;
+ os_memcpy(p2p->ssid, msg.group_id + ETH_ALEN, p2p->ssid_len);
+ } else if (!go) {
+ wpa_msg(p2p->cfg->msg_ctx, MSG_DEBUG,
+ "P2P: Mandatory P2P Group ID attribute missing from "
+ "GO Negotiation Response");
+ p2p->ssid_len = 0;
+#ifdef CONFIG_P2P_STRICT
+ status = P2P_SC_FAIL_INVALID_PARAMS;
+ goto fail;
+#endif /* CONFIG_P2P_STRICT */
+ }
+
+ if (!msg.config_timeout) {
+ wpa_msg(p2p->cfg->msg_ctx, MSG_DEBUG,
+ "P2P: Mandatory Configuration Timeout attribute "
+ "missing from GO Negotiation Response");
+#ifdef CONFIG_P2P_STRICT
+ status = P2P_SC_FAIL_INVALID_PARAMS;
+ goto fail;
+#endif /* CONFIG_P2P_STRICT */
+ } else {
+ dev->go_timeout = msg.config_timeout[0];
+ dev->client_timeout = msg.config_timeout[1];
+ }
+
+ if (!msg.operating_channel && !go) {
+ /*
+ * Note: P2P Client may omit Operating Channel attribute to
+ * indicate it does not have a preference.
+ */
+ wpa_msg(p2p->cfg->msg_ctx, MSG_DEBUG,
+ "P2P: No Operating Channel attribute received");
+ status = P2P_SC_FAIL_INVALID_PARAMS;
+ goto fail;
+ }
+ if (!msg.channel_list) {
+ wpa_msg(p2p->cfg->msg_ctx, MSG_DEBUG,
+ "P2P: No Channel List attribute received");
+ status = P2P_SC_FAIL_INVALID_PARAMS;
+ goto fail;
+ }
+
+ if (p2p_peer_channels(p2p, dev, msg.channel_list,
+ msg.channel_list_len) < 0) {
+ wpa_msg(p2p->cfg->msg_ctx, MSG_DEBUG,
+ "P2P: No common channels found");
+ status = P2P_SC_FAIL_NO_COMMON_CHANNELS;
+ goto fail;
+ }
+
+ if (msg.operating_channel) {
+ dev->oper_freq = p2p_channel_to_freq((const char *)
+ msg.operating_channel,
+ msg.operating_channel[3],
+ msg.operating_channel[4]);
+ wpa_msg(p2p->cfg->msg_ctx, MSG_DEBUG, "P2P: Peer operating "
+ "channel preference: %d MHz", dev->oper_freq);
+ } else
+ dev->oper_freq = 0;
+
+ switch (msg.dev_password_id) {
+ case DEV_PW_REGISTRAR_SPECIFIED:
+ wpa_msg(p2p->cfg->msg_ctx, MSG_DEBUG,
+ "P2P: PIN from peer Display");
+ if (dev->wps_method != WPS_PIN_KEYPAD) {
+ wpa_msg(p2p->cfg->msg_ctx, MSG_DEBUG,
+ "P2P: We have wps_method=%s -> "
+ "incompatible",
+ p2p_wps_method_str(dev->wps_method));
+ status = P2P_SC_FAIL_INCOMPATIBLE_PROV_METHOD;
+ goto fail;
+ }
+ break;
+ case DEV_PW_USER_SPECIFIED:
+ wpa_msg(p2p->cfg->msg_ctx, MSG_DEBUG,
+ "P2P: Peer entered PIN on Keypad");
+ if (dev->wps_method != WPS_PIN_DISPLAY) {
+ wpa_msg(p2p->cfg->msg_ctx, MSG_DEBUG,
+ "P2P: We have wps_method=%s -> "
+ "incompatible",
+ p2p_wps_method_str(dev->wps_method));
+ status = P2P_SC_FAIL_INCOMPATIBLE_PROV_METHOD;
+ goto fail;
+ }
+ break;
+ case DEV_PW_PUSHBUTTON:
+ wpa_msg(p2p->cfg->msg_ctx, MSG_DEBUG,
+ "P2P: Peer using pushbutton");
+ if (dev->wps_method != WPS_PBC) {
+ wpa_msg(p2p->cfg->msg_ctx, MSG_DEBUG,
+ "P2P: We have wps_method=%s -> "
+ "incompatible",
+ p2p_wps_method_str(dev->wps_method));
+ status = P2P_SC_FAIL_INCOMPATIBLE_PROV_METHOD;
+ goto fail;
+ }
+ break;
+ default:
+ wpa_msg(p2p->cfg->msg_ctx, MSG_DEBUG,
+ "P2P: Unsupported Device Password ID %d",
+ msg.dev_password_id);
+ status = P2P_SC_FAIL_INCOMPATIBLE_PROV_METHOD;
+ goto fail;
+ }
+
+ if (go && p2p_go_select_channel(p2p, dev, &status) < 0)
+ goto fail;
+
+ p2p_set_state(p2p, P2P_GO_NEG);
+ p2p_clear_timeout(p2p);
+
+ wpa_msg(p2p->cfg->msg_ctx, MSG_DEBUG,
+ "P2P: GO Negotiation with " MACSTR, MAC2STR(sa));
+ os_memcpy(dev->intended_addr, msg.intended_addr, ETH_ALEN);
+
+fail:
+ conf = p2p_build_go_neg_conf(p2p, dev, msg.dialog_token, status,
+ msg.operating_channel, go);
+ p2p_parse_free(&msg);
+ if (conf == NULL)
+ return;
+ wpa_msg(p2p->cfg->msg_ctx, MSG_DEBUG,
+ "P2P: Sending GO Negotiation Confirm");
+ if (status == P2P_SC_SUCCESS) {
+ p2p->pending_action_state = P2P_PENDING_GO_NEG_CONFIRM;
+ dev->go_state = go ? LOCAL_GO : REMOTE_GO;
+ } else
+ p2p->pending_action_state = P2P_NO_PENDING_ACTION;
+ if (rx_freq > 0)
+ freq = rx_freq;
+ else
+ freq = dev->listen_freq;
+ if (p2p_send_action(p2p, freq, sa, p2p->cfg->dev_addr, sa,
+ wpabuf_head(conf), wpabuf_len(conf), 0) < 0) {
+ wpa_msg(p2p->cfg->msg_ctx, MSG_DEBUG,
+ "P2P: Failed to send Action frame");
+ p2p_go_neg_failed(p2p, dev, -1);
+ }
+ wpabuf_free(conf);
+}
+
+
+void p2p_process_go_neg_conf(struct p2p_data *p2p, const u8 *sa,
+ const u8 *data, size_t len)
+{
+ struct p2p_device *dev;
+ struct p2p_message msg;
+
+ wpa_msg(p2p->cfg->msg_ctx, MSG_DEBUG,
+ "P2P: Received GO Negotiation Confirm from " MACSTR,
+ MAC2STR(sa));
+ dev = p2p_get_device(p2p, sa);
+ if (dev == NULL || dev->wps_method == WPS_NOT_READY ||
+ dev != p2p->go_neg_peer) {
+ wpa_msg(p2p->cfg->msg_ctx, MSG_DEBUG,
+ "P2P: Not ready for GO negotiation with " MACSTR,
+ MAC2STR(sa));
+ return;
+ }
+
+ if (p2p->pending_action_state == P2P_PENDING_GO_NEG_RESPONSE) {
+ wpa_msg(p2p->cfg->msg_ctx, MSG_DEBUG, "P2P: Stopped waiting "
+ "for TX status on GO Negotiation Response since we "
+ "already received Confirmation");
+ p2p->pending_action_state = P2P_NO_PENDING_ACTION;
+ }
+
+ if (p2p_parse(data, len, &msg))
+ return;
+
+ if (!(dev->flags & P2P_DEV_WAIT_GO_NEG_CONFIRM)) {
+ wpa_msg(p2p->cfg->msg_ctx, MSG_DEBUG,
+ "P2P: Was not expecting GO Negotiation Confirm - "
+ "ignore");
+ return;
+ }
+ dev->flags &= ~P2P_DEV_WAIT_GO_NEG_CONFIRM;
+
+ if (msg.dialog_token != dev->dialog_token) {
+ wpa_msg(p2p->cfg->msg_ctx, MSG_DEBUG,
+ "P2P: Unexpected Dialog Token %u (expected %u)",
+ msg.dialog_token, dev->dialog_token);
+ p2p_parse_free(&msg);
+ return;
+ }
+
+ if (!msg.status) {
+ wpa_msg(p2p->cfg->msg_ctx, MSG_DEBUG,
+ "P2P: No Status attribute received");
+ p2p_parse_free(&msg);
+ return;
+ }
+ if (*msg.status) {
+ wpa_msg(p2p->cfg->msg_ctx, MSG_DEBUG,
+ "P2P: GO Negotiation rejected: status %d",
+ *msg.status);
+ p2p_parse_free(&msg);
+ return;
+ }
+
+ if (dev->go_state == REMOTE_GO && msg.group_id) {
+ /* Store SSID for Provisioning step */
+ p2p->ssid_len = msg.group_id_len - ETH_ALEN;
+ os_memcpy(p2p->ssid, msg.group_id + ETH_ALEN, p2p->ssid_len);
+ } else if (dev->go_state == REMOTE_GO) {
+ wpa_msg(p2p->cfg->msg_ctx, MSG_DEBUG,
+ "P2P: Mandatory P2P Group ID attribute missing from "
+ "GO Negotiation Confirmation");
+ p2p->ssid_len = 0;
+#ifdef CONFIG_P2P_STRICT
+ p2p_parse_free(&msg);
+ return;
+#endif /* CONFIG_P2P_STRICT */
+ }
+
+ if (!msg.operating_channel) {
+ wpa_msg(p2p->cfg->msg_ctx, MSG_DEBUG,
+ "P2P: Mandatory Operating Channel attribute missing "
+ "from GO Negotiation Confirmation");
+#ifdef CONFIG_P2P_STRICT
+ p2p_parse_free(&msg);
+ return;
+#endif /* CONFIG_P2P_STRICT */
+ }
+
+ if (!msg.channel_list) {
+ wpa_msg(p2p->cfg->msg_ctx, MSG_DEBUG,
+ "P2P: Mandatory Operating Channel attribute missing "
+ "from GO Negotiation Confirmation");
+#ifdef CONFIG_P2P_STRICT
+ p2p_parse_free(&msg);
+ return;
+#endif /* CONFIG_P2P_STRICT */
+ }
+
+ p2p_parse_free(&msg);
+
+ if (dev->go_state == UNKNOWN_GO) {
+ /*
+ * This should not happen since GO negotiation has already
+ * been completed.
+ */
+ wpa_msg(p2p->cfg->msg_ctx, MSG_DEBUG,
+ "P2P: Unexpected GO Neg state - do not know which end "
+ "becomes GO");
+ return;
+ }
+
+ /*
+ * The peer could have missed our ctrl::ack frame for GO Negotiation
+ * Confirm and continue retransmitting the frame. To reduce the
+ * likelihood of the peer not getting successful TX status for the
+ * GO Negotiation Confirm frame, wait a short time here before starting
+ * the group so that we will remain on the current channel to
+ * acknowledge any possible retransmission from the peer.
+ */
+ wpa_msg(p2p->cfg->msg_ctx, MSG_DEBUG, "P2P: 20 ms wait on current "
+ "channel before starting group");
+ os_sleep(0, 20000);
+
+ p2p_go_complete(p2p, dev);
+}
diff --git a/contrib/wpa/src/p2p/p2p_group.c b/contrib/wpa/src/p2p/p2p_group.c
new file mode 100644
index 0000000..8687320
--- /dev/null
+++ b/contrib/wpa/src/p2p/p2p_group.c
@@ -0,0 +1,953 @@
+/*
+ * Wi-Fi Direct - P2P group operations
+ * Copyright (c) 2009-2010, Atheros Communications
+ *
+ * This software may be distributed under the terms of the BSD license.
+ * See README for more details.
+ */
+
+#include "includes.h"
+
+#include "common.h"
+#include "common/ieee802_11_defs.h"
+#include "common/ieee802_11_common.h"
+#include "wps/wps_defs.h"
+#include "wps/wps_i.h"
+#include "p2p_i.h"
+#include "p2p.h"
+
+
+struct p2p_group_member {
+ struct p2p_group_member *next;
+ u8 addr[ETH_ALEN]; /* P2P Interface Address */
+ u8 dev_addr[ETH_ALEN]; /* P2P Device Address */
+ struct wpabuf *p2p_ie;
+ struct wpabuf *wfd_ie;
+ struct wpabuf *client_info;
+ u8 dev_capab;
+};
+
+/**
+ * struct p2p_group - Internal P2P module per-group data
+ */
+struct p2p_group {
+ struct p2p_data *p2p;
+ struct p2p_group_config *cfg;
+ struct p2p_group_member *members;
+ unsigned int num_members;
+ int group_formation;
+ int beacon_update;
+ struct wpabuf *noa;
+ struct wpabuf *wfd_ie;
+};
+
+
+struct p2p_group * p2p_group_init(struct p2p_data *p2p,
+ struct p2p_group_config *config)
+{
+ struct p2p_group *group, **groups;
+
+ group = os_zalloc(sizeof(*group));
+ if (group == NULL)
+ return NULL;
+
+ groups = os_realloc_array(p2p->groups, p2p->num_groups + 1,
+ sizeof(struct p2p_group *));
+ if (groups == NULL) {
+ os_free(group);
+ return NULL;
+ }
+ groups[p2p->num_groups++] = group;
+ p2p->groups = groups;
+
+ group->p2p = p2p;
+ group->cfg = config;
+ group->group_formation = 1;
+ group->beacon_update = 1;
+ p2p_group_update_ies(group);
+ group->cfg->idle_update(group->cfg->cb_ctx, 1);
+
+ return group;
+}
+
+
+static void p2p_group_free_member(struct p2p_group_member *m)
+{
+ wpabuf_free(m->wfd_ie);
+ wpabuf_free(m->p2p_ie);
+ wpabuf_free(m->client_info);
+ os_free(m);
+}
+
+
+static void p2p_group_free_members(struct p2p_group *group)
+{
+ struct p2p_group_member *m, *prev;
+ m = group->members;
+ group->members = NULL;
+ group->num_members = 0;
+ while (m) {
+ prev = m;
+ m = m->next;
+ p2p_group_free_member(prev);
+ }
+}
+
+
+void p2p_group_deinit(struct p2p_group *group)
+{
+ size_t g;
+ struct p2p_data *p2p;
+
+ if (group == NULL)
+ return;
+
+ p2p = group->p2p;
+
+ for (g = 0; g < p2p->num_groups; g++) {
+ if (p2p->groups[g] == group) {
+ while (g + 1 < p2p->num_groups) {
+ p2p->groups[g] = p2p->groups[g + 1];
+ g++;
+ }
+ p2p->num_groups--;
+ break;
+ }
+ }
+
+ p2p_group_free_members(group);
+ os_free(group->cfg);
+ wpabuf_free(group->noa);
+ wpabuf_free(group->wfd_ie);
+ os_free(group);
+}
+
+
+static void p2p_client_info(struct wpabuf *ie, struct p2p_group_member *m)
+{
+ if (m->client_info == NULL)
+ return;
+ if (wpabuf_tailroom(ie) < wpabuf_len(m->client_info) + 1)
+ return;
+ wpabuf_put_buf(ie, m->client_info);
+}
+
+
+static void p2p_group_add_common_ies(struct p2p_group *group,
+ struct wpabuf *ie)
+{
+ u8 dev_capab = group->p2p->dev_capab, group_capab = 0;
+
+ /* P2P Capability */
+ dev_capab &= ~P2P_DEV_CAPAB_CLIENT_DISCOVERABILITY;
+ group_capab |= P2P_GROUP_CAPAB_GROUP_OWNER;
+ if (group->cfg->persistent_group) {
+ group_capab |= P2P_GROUP_CAPAB_PERSISTENT_GROUP;
+ if (group->cfg->persistent_group == 2)
+ group_capab |= P2P_GROUP_CAPAB_PERSISTENT_RECONN;
+ }
+ if (group->p2p->cfg->p2p_intra_bss)
+ group_capab |= P2P_GROUP_CAPAB_INTRA_BSS_DIST;
+ if (group->group_formation)
+ group_capab |= P2P_GROUP_CAPAB_GROUP_FORMATION;
+ if (group->p2p->cross_connect)
+ group_capab |= P2P_GROUP_CAPAB_CROSS_CONN;
+ if (group->num_members >= group->cfg->max_clients)
+ group_capab |= P2P_GROUP_CAPAB_GROUP_LIMIT;
+ p2p_buf_add_capability(ie, dev_capab, group_capab);
+}
+
+
+static void p2p_group_add_noa(struct wpabuf *ie, struct wpabuf *noa)
+{
+ if (noa == NULL)
+ return;
+ /* Notice of Absence */
+ wpabuf_put_u8(ie, P2P_ATTR_NOTICE_OF_ABSENCE);
+ wpabuf_put_le16(ie, wpabuf_len(noa));
+ wpabuf_put_buf(ie, noa);
+}
+
+
+static struct wpabuf * p2p_group_build_beacon_ie(struct p2p_group *group)
+{
+ struct wpabuf *ie;
+ u8 *len;
+ size_t extra = 0;
+
+#ifdef CONFIG_WIFI_DISPLAY
+ if (group->p2p->wfd_ie_beacon)
+ extra = wpabuf_len(group->p2p->wfd_ie_beacon);
+#endif /* CONFIG_WIFI_DISPLAY */
+
+ ie = wpabuf_alloc(257 + extra);
+ if (ie == NULL)
+ return NULL;
+
+#ifdef CONFIG_WIFI_DISPLAY
+ if (group->p2p->wfd_ie_beacon)
+ wpabuf_put_buf(ie, group->p2p->wfd_ie_beacon);
+#endif /* CONFIG_WIFI_DISPLAY */
+
+ len = p2p_buf_add_ie_hdr(ie);
+ p2p_group_add_common_ies(group, ie);
+ p2p_buf_add_device_id(ie, group->p2p->cfg->dev_addr);
+ p2p_group_add_noa(ie, group->noa);
+ p2p_buf_update_ie_hdr(ie, len);
+
+ return ie;
+}
+
+
+#ifdef CONFIG_WIFI_DISPLAY
+
+struct wpabuf * p2p_group_get_wfd_ie(struct p2p_group *g)
+{
+ return g->wfd_ie;
+}
+
+
+struct wpabuf * wifi_display_encaps(struct wpabuf *subelems)
+{
+ struct wpabuf *ie;
+ const u8 *pos, *end;
+
+ if (subelems == NULL)
+ return NULL;
+
+ ie = wpabuf_alloc(wpabuf_len(subelems) + 100);
+ if (ie == NULL)
+ return NULL;
+
+ pos = wpabuf_head(subelems);
+ end = pos + wpabuf_len(subelems);
+
+ while (end > pos) {
+ size_t frag_len = end - pos;
+ if (frag_len > 251)
+ frag_len = 251;
+ wpabuf_put_u8(ie, WLAN_EID_VENDOR_SPECIFIC);
+ wpabuf_put_u8(ie, 4 + frag_len);
+ wpabuf_put_be32(ie, WFD_IE_VENDOR_TYPE);
+ wpabuf_put_data(ie, pos, frag_len);
+ pos += frag_len;
+ }
+
+ return ie;
+}
+
+
+static int wifi_display_add_dev_info_descr(struct wpabuf *buf,
+ struct p2p_group_member *m)
+{
+ const u8 *pos, *end;
+ const u8 *dev_info = NULL;
+ const u8 *assoc_bssid = NULL;
+ const u8 *coupled_sink = NULL;
+ u8 zero_addr[ETH_ALEN];
+
+ if (m->wfd_ie == NULL)
+ return 0;
+
+ os_memset(zero_addr, 0, ETH_ALEN);
+ pos = wpabuf_head_u8(m->wfd_ie);
+ end = pos + wpabuf_len(m->wfd_ie);
+ while (pos + 1 < end) {
+ u8 id;
+ u16 len;
+
+ id = *pos++;
+ len = WPA_GET_BE16(pos);
+ pos += 2;
+ if (pos + len > end)
+ break;
+
+ switch (id) {
+ case WFD_SUBELEM_DEVICE_INFO:
+ if (len < 6)
+ break;
+ dev_info = pos;
+ break;
+ case WFD_SUBELEM_ASSOCIATED_BSSID:
+ if (len < ETH_ALEN)
+ break;
+ assoc_bssid = pos;
+ break;
+ case WFD_SUBELEM_COUPLED_SINK:
+ if (len < 1 + ETH_ALEN)
+ break;
+ coupled_sink = pos;
+ break;
+ }
+
+ pos += len;
+ }
+
+ if (dev_info == NULL)
+ return 0;
+
+ wpabuf_put_u8(buf, 23);
+ wpabuf_put_data(buf, m->dev_addr, ETH_ALEN);
+ if (assoc_bssid)
+ wpabuf_put_data(buf, assoc_bssid, ETH_ALEN);
+ else
+ wpabuf_put_data(buf, zero_addr, ETH_ALEN);
+ wpabuf_put_data(buf, dev_info, 2); /* WFD Device Info */
+ wpabuf_put_data(buf, dev_info + 4, 2); /* WFD Device Max Throughput */
+ if (coupled_sink) {
+ wpabuf_put_data(buf, coupled_sink, 1 + ETH_ALEN);
+ } else {
+ wpabuf_put_u8(buf, 0);
+ wpabuf_put_data(buf, zero_addr, ETH_ALEN);
+ }
+
+ return 1;
+}
+
+
+static struct wpabuf *
+wifi_display_build_go_ie(struct p2p_group *group)
+{
+ struct wpabuf *wfd_subelems, *wfd_ie;
+ struct p2p_group_member *m;
+ u8 *len;
+ unsigned int count = 0;
+
+ if (!group->p2p->wfd_ie_probe_resp)
+ return NULL;
+
+ wfd_subelems = wpabuf_alloc(wpabuf_len(group->p2p->wfd_ie_probe_resp) +
+ group->num_members * 24 + 100);
+ if (wfd_subelems == NULL)
+ return NULL;
+ if (group->p2p->wfd_dev_info)
+ wpabuf_put_buf(wfd_subelems, group->p2p->wfd_dev_info);
+ if (group->p2p->wfd_assoc_bssid)
+ wpabuf_put_buf(wfd_subelems,
+ group->p2p->wfd_assoc_bssid);
+ if (group->p2p->wfd_coupled_sink_info)
+ wpabuf_put_buf(wfd_subelems,
+ group->p2p->wfd_coupled_sink_info);
+
+ /* Build WFD Session Info */
+ wpabuf_put_u8(wfd_subelems, WFD_SUBELEM_SESSION_INFO);
+ len = wpabuf_put(wfd_subelems, 2);
+ m = group->members;
+ while (m) {
+ if (wifi_display_add_dev_info_descr(wfd_subelems, m))
+ count++;
+ m = m->next;
+ }
+
+ if (count == 0) {
+ /* No Wi-Fi Display clients - do not include subelement */
+ wfd_subelems->used -= 3;
+ } else {
+ WPA_PUT_BE16(len, (u8 *) wpabuf_put(wfd_subelems, 0) - len -
+ 2);
+ wpa_printf(MSG_DEBUG, "WFD: WFD Session Info: %u descriptors",
+ count);
+ }
+
+ wfd_ie = wifi_display_encaps(wfd_subelems);
+ wpabuf_free(wfd_subelems);
+
+ return wfd_ie;
+}
+
+static void wifi_display_group_update(struct p2p_group *group)
+{
+ wpabuf_free(group->wfd_ie);
+ group->wfd_ie = wifi_display_build_go_ie(group);
+}
+
+#endif /* CONFIG_WIFI_DISPLAY */
+
+
+static struct wpabuf * p2p_group_build_probe_resp_ie(struct p2p_group *group)
+{
+ u8 *group_info;
+ struct wpabuf *ie;
+ struct p2p_group_member *m;
+ u8 *len;
+ size_t extra = 0;
+
+#ifdef CONFIG_WIFI_DISPLAY
+ if (group->wfd_ie)
+ extra += wpabuf_len(group->wfd_ie);
+#endif /* CONFIG_WIFI_DISPLAY */
+
+ ie = wpabuf_alloc(257 + extra);
+ if (ie == NULL)
+ return NULL;
+
+#ifdef CONFIG_WIFI_DISPLAY
+ if (group->wfd_ie)
+ wpabuf_put_buf(ie, group->wfd_ie);
+#endif /* CONFIG_WIFI_DISPLAY */
+
+ len = p2p_buf_add_ie_hdr(ie);
+
+ p2p_group_add_common_ies(group, ie);
+ p2p_group_add_noa(ie, group->noa);
+
+ /* P2P Device Info */
+ p2p_buf_add_device_info(ie, group->p2p, NULL);
+
+ /* P2P Group Info */
+ group_info = wpabuf_put(ie, 0);
+ wpabuf_put_u8(ie, P2P_ATTR_GROUP_INFO);
+ wpabuf_put_le16(ie, 0); /* Length to be filled */
+ for (m = group->members; m; m = m->next)
+ p2p_client_info(ie, m);
+ WPA_PUT_LE16(group_info + 1,
+ (u8 *) wpabuf_put(ie, 0) - group_info - 3);
+
+ p2p_buf_update_ie_hdr(ie, len);
+
+ return ie;
+}
+
+
+void p2p_group_update_ies(struct p2p_group *group)
+{
+ struct wpabuf *beacon_ie;
+ struct wpabuf *probe_resp_ie;
+
+#ifdef CONFIG_WIFI_DISPLAY
+ wifi_display_group_update(group);
+#endif /* CONFIG_WIFI_DISPLAY */
+
+ probe_resp_ie = p2p_group_build_probe_resp_ie(group);
+ if (probe_resp_ie == NULL)
+ return;
+ wpa_hexdump_buf(MSG_MSGDUMP, "P2P: Update GO Probe Response P2P IE",
+ probe_resp_ie);
+
+ if (group->beacon_update) {
+ beacon_ie = p2p_group_build_beacon_ie(group);
+ if (beacon_ie)
+ group->beacon_update = 0;
+ wpa_hexdump_buf(MSG_MSGDUMP, "P2P: Update GO Beacon P2P IE",
+ beacon_ie);
+ } else
+ beacon_ie = NULL;
+
+ group->cfg->ie_update(group->cfg->cb_ctx, beacon_ie, probe_resp_ie);
+}
+
+
+/**
+ * p2p_build_client_info - Build P2P Client Info Descriptor
+ * @addr: MAC address of the peer device
+ * @p2p_ie: P2P IE from (Re)Association Request
+ * @dev_capab: Buffer for returning Device Capability
+ * @dev_addr: Buffer for returning P2P Device Address
+ * Returns: P2P Client Info Descriptor or %NULL on failure
+ *
+ * This function builds P2P Client Info Descriptor based on the information
+ * available from (Re)Association Request frame. Group owner can use this to
+ * build the P2P Group Info attribute for Probe Response frames.
+ */
+static struct wpabuf * p2p_build_client_info(const u8 *addr,
+ struct wpabuf *p2p_ie,
+ u8 *dev_capab, u8 *dev_addr)
+{
+ const u8 *spos;
+ struct p2p_message msg;
+ u8 *len_pos;
+ struct wpabuf *buf;
+
+ if (p2p_ie == NULL)
+ return NULL;
+
+ os_memset(&msg, 0, sizeof(msg));
+ if (p2p_parse_p2p_ie(p2p_ie, &msg) ||
+ msg.capability == NULL || msg.p2p_device_info == NULL)
+ return NULL;
+
+ buf = wpabuf_alloc(ETH_ALEN + 1 + 1 + msg.p2p_device_info_len);
+ if (buf == NULL)
+ return NULL;
+
+ *dev_capab = msg.capability[0];
+ os_memcpy(dev_addr, msg.p2p_device_addr, ETH_ALEN);
+
+ spos = msg.p2p_device_info; /* P2P Device address */
+
+ /* P2P Client Info Descriptor */
+ /* Length to be set */
+ len_pos = wpabuf_put(buf, 1);
+ /* P2P Device address */
+ wpabuf_put_data(buf, spos, ETH_ALEN);
+ /* P2P Interface address */
+ wpabuf_put_data(buf, addr, ETH_ALEN);
+ /* Device Capability Bitmap */
+ wpabuf_put_u8(buf, msg.capability[0]);
+ /*
+ * Config Methods, Primary Device Type, Number of Secondary Device
+ * Types, Secondary Device Type List, Device Name copied from
+ * Device Info
+ */
+ wpabuf_put_data(buf, spos + ETH_ALEN,
+ msg.p2p_device_info_len - ETH_ALEN);
+
+ *len_pos = wpabuf_len(buf) - 1;
+
+
+ return buf;
+}
+
+
+static int p2p_group_remove_member(struct p2p_group *group, const u8 *addr)
+{
+ struct p2p_group_member *m, *prev;
+
+ if (group == NULL)
+ return 0;
+
+ m = group->members;
+ prev = NULL;
+ while (m) {
+ if (os_memcmp(m->addr, addr, ETH_ALEN) == 0)
+ break;
+ prev = m;
+ m = m->next;
+ }
+
+ if (m == NULL)
+ return 0;
+
+ if (prev)
+ prev->next = m->next;
+ else
+ group->members = m->next;
+ p2p_group_free_member(m);
+ group->num_members--;
+
+ return 1;
+}
+
+
+int p2p_group_notif_assoc(struct p2p_group *group, const u8 *addr,
+ const u8 *ie, size_t len)
+{
+ struct p2p_group_member *m;
+
+ if (group == NULL)
+ return -1;
+
+ m = os_zalloc(sizeof(*m));
+ if (m == NULL)
+ return -1;
+ os_memcpy(m->addr, addr, ETH_ALEN);
+ m->p2p_ie = ieee802_11_vendor_ie_concat(ie, len, P2P_IE_VENDOR_TYPE);
+ if (m->p2p_ie) {
+ m->client_info = p2p_build_client_info(addr, m->p2p_ie,
+ &m->dev_capab,
+ m->dev_addr);
+ }
+#ifdef CONFIG_WIFI_DISPLAY
+ m->wfd_ie = ieee802_11_vendor_ie_concat(ie, len, WFD_IE_VENDOR_TYPE);
+#endif /* CONFIG_WIFI_DISPLAY */
+
+ p2p_group_remove_member(group, addr);
+
+ m->next = group->members;
+ group->members = m;
+ group->num_members++;
+ wpa_msg(group->p2p->cfg->msg_ctx, MSG_DEBUG, "P2P: Add client " MACSTR
+ " to group (p2p=%d wfd=%d client_info=%d); num_members=%u/%u",
+ MAC2STR(addr), m->p2p_ie ? 1 : 0, m->wfd_ie ? 1 : 0,
+ m->client_info ? 1 : 0,
+ group->num_members, group->cfg->max_clients);
+ if (group->num_members == group->cfg->max_clients)
+ group->beacon_update = 1;
+ p2p_group_update_ies(group);
+ if (group->num_members == 1)
+ group->cfg->idle_update(group->cfg->cb_ctx, 0);
+
+ return 0;
+}
+
+
+struct wpabuf * p2p_group_assoc_resp_ie(struct p2p_group *group, u8 status)
+{
+ struct wpabuf *resp;
+ u8 *rlen;
+ size_t extra = 0;
+
+#ifdef CONFIG_WIFI_DISPLAY
+ if (group->wfd_ie)
+ extra = wpabuf_len(group->wfd_ie);
+#endif /* CONFIG_WIFI_DISPLAY */
+
+ /*
+ * (Re)Association Response - P2P IE
+ * Status attribute (shall be present when association request is
+ * denied)
+ * Extended Listen Timing (may be present)
+ */
+ resp = wpabuf_alloc(20 + extra);
+ if (resp == NULL)
+ return NULL;
+
+#ifdef CONFIG_WIFI_DISPLAY
+ if (group->wfd_ie)
+ wpabuf_put_buf(resp, group->wfd_ie);
+#endif /* CONFIG_WIFI_DISPLAY */
+
+ rlen = p2p_buf_add_ie_hdr(resp);
+ if (status != P2P_SC_SUCCESS)
+ p2p_buf_add_status(resp, status);
+ p2p_buf_update_ie_hdr(resp, rlen);
+
+ return resp;
+}
+
+
+void p2p_group_notif_disassoc(struct p2p_group *group, const u8 *addr)
+{
+ if (p2p_group_remove_member(group, addr)) {
+ wpa_msg(group->p2p->cfg->msg_ctx, MSG_DEBUG, "P2P: Remove "
+ "client " MACSTR " from group; num_members=%u/%u",
+ MAC2STR(addr), group->num_members,
+ group->cfg->max_clients);
+ if (group->num_members == group->cfg->max_clients - 1)
+ group->beacon_update = 1;
+ p2p_group_update_ies(group);
+ if (group->num_members == 0)
+ group->cfg->idle_update(group->cfg->cb_ctx, 1);
+ }
+}
+
+
+/**
+ * p2p_match_dev_type_member - Match client device type with requested type
+ * @m: Group member
+ * @wps: WPS TLVs from Probe Request frame (concatenated WPS IEs)
+ * Returns: 1 on match, 0 on mismatch
+ *
+ * This function can be used to match the Requested Device Type attribute in
+ * WPS IE with the device types of a group member for deciding whether a GO
+ * should reply to a Probe Request frame.
+ */
+static int p2p_match_dev_type_member(struct p2p_group_member *m,
+ struct wpabuf *wps)
+{
+ const u8 *pos, *end;
+ struct wps_parse_attr attr;
+ u8 num_sec;
+
+ if (m->client_info == NULL || wps == NULL)
+ return 0;
+
+ pos = wpabuf_head(m->client_info);
+ end = pos + wpabuf_len(m->client_info);
+
+ pos += 1 + 2 * ETH_ALEN + 1 + 2;
+ if (end - pos < WPS_DEV_TYPE_LEN + 1)
+ return 0;
+
+ if (wps_parse_msg(wps, &attr))
+ return 1; /* assume no Requested Device Type attributes */
+
+ if (attr.num_req_dev_type == 0)
+ return 1; /* no Requested Device Type attributes -> match */
+
+ if (dev_type_list_match(pos, attr.req_dev_type, attr.num_req_dev_type))
+ return 1; /* Match with client Primary Device Type */
+
+ pos += WPS_DEV_TYPE_LEN;
+ num_sec = *pos++;
+ if (end - pos < num_sec * WPS_DEV_TYPE_LEN)
+ return 0;
+ while (num_sec > 0) {
+ num_sec--;
+ if (dev_type_list_match(pos, attr.req_dev_type,
+ attr.num_req_dev_type))
+ return 1; /* Match with client Secondary Device Type */
+ pos += WPS_DEV_TYPE_LEN;
+ }
+
+ /* No matching device type found */
+ return 0;
+}
+
+
+int p2p_group_match_dev_type(struct p2p_group *group, struct wpabuf *wps)
+{
+ struct p2p_group_member *m;
+
+ if (p2p_match_dev_type(group->p2p, wps))
+ return 1; /* Match with own device type */
+
+ for (m = group->members; m; m = m->next) {
+ if (p2p_match_dev_type_member(m, wps))
+ return 1; /* Match with group client device type */
+ }
+
+ /* No match with Requested Device Type */
+ return 0;
+}
+
+
+int p2p_group_match_dev_id(struct p2p_group *group, struct wpabuf *p2p)
+{
+ struct p2p_group_member *m;
+ struct p2p_message msg;
+
+ os_memset(&msg, 0, sizeof(msg));
+ if (p2p_parse_p2p_ie(p2p, &msg))
+ return 1; /* Failed to parse - assume no filter on Device ID */
+
+ if (!msg.device_id)
+ return 1; /* No filter on Device ID */
+
+ if (os_memcmp(msg.device_id, group->p2p->cfg->dev_addr, ETH_ALEN) == 0)
+ return 1; /* Match with our P2P Device Address */
+
+ for (m = group->members; m; m = m->next) {
+ if (os_memcmp(msg.device_id, m->dev_addr, ETH_ALEN) == 0)
+ return 1; /* Match with group client P2P Device Address */
+ }
+
+ /* No match with Device ID */
+ return 0;
+}
+
+
+void p2p_group_notif_formation_done(struct p2p_group *group)
+{
+ if (group == NULL)
+ return;
+ group->group_formation = 0;
+ group->beacon_update = 1;
+ p2p_group_update_ies(group);
+}
+
+
+int p2p_group_notif_noa(struct p2p_group *group, const u8 *noa,
+ size_t noa_len)
+{
+ if (noa == NULL) {
+ wpabuf_free(group->noa);
+ group->noa = NULL;
+ } else {
+ if (group->noa) {
+ if (wpabuf_size(group->noa) >= noa_len) {
+ group->noa->used = 0;
+ wpabuf_put_data(group->noa, noa, noa_len);
+ } else {
+ wpabuf_free(group->noa);
+ group->noa = NULL;
+ }
+ }
+
+ if (!group->noa) {
+ group->noa = wpabuf_alloc_copy(noa, noa_len);
+ if (group->noa == NULL)
+ return -1;
+ }
+ }
+
+ group->beacon_update = 1;
+ p2p_group_update_ies(group);
+ return 0;
+}
+
+
+static struct p2p_group_member * p2p_group_get_client(struct p2p_group *group,
+ const u8 *dev_id)
+{
+ struct p2p_group_member *m;
+
+ for (m = group->members; m; m = m->next) {
+ if (os_memcmp(dev_id, m->dev_addr, ETH_ALEN) == 0)
+ return m;
+ }
+
+ return NULL;
+}
+
+
+static struct p2p_group_member * p2p_group_get_client_iface(
+ struct p2p_group *group, const u8 *interface_addr)
+{
+ struct p2p_group_member *m;
+
+ for (m = group->members; m; m = m->next) {
+ if (os_memcmp(interface_addr, m->addr, ETH_ALEN) == 0)
+ return m;
+ }
+
+ return NULL;
+}
+
+
+const u8 * p2p_group_get_dev_addr(struct p2p_group *group, const u8 *addr)
+{
+ struct p2p_group_member *m;
+
+ if (group == NULL)
+ return NULL;
+ m = p2p_group_get_client_iface(group, addr);
+ if (m && !is_zero_ether_addr(m->dev_addr))
+ return m->dev_addr;
+ return NULL;
+}
+
+
+static struct wpabuf * p2p_build_go_disc_req(void)
+{
+ struct wpabuf *buf;
+
+ buf = wpabuf_alloc(100);
+ if (buf == NULL)
+ return NULL;
+
+ p2p_buf_add_action_hdr(buf, P2P_GO_DISC_REQ, 0);
+
+ return buf;
+}
+
+
+int p2p_group_go_discover(struct p2p_group *group, const u8 *dev_id,
+ const u8 *searching_dev, int rx_freq)
+{
+ struct p2p_group_member *m;
+ struct wpabuf *req;
+ struct p2p_data *p2p = group->p2p;
+ int freq;
+
+ m = p2p_group_get_client(group, dev_id);
+ if (m == NULL || m->client_info == NULL) {
+ wpa_printf(MSG_DEBUG, "P2P: Requested client was not in this "
+ "group " MACSTR,
+ MAC2STR(group->cfg->interface_addr));
+ return -1;
+ }
+
+ if (!(m->dev_capab & P2P_DEV_CAPAB_CLIENT_DISCOVERABILITY)) {
+ wpa_printf(MSG_DEBUG, "P2P: Requested client does not support "
+ "client discoverability");
+ return -1;
+ }
+
+ wpa_printf(MSG_DEBUG, "P2P: Schedule GO Discoverability Request to be "
+ "sent to " MACSTR, MAC2STR(dev_id));
+
+ req = p2p_build_go_disc_req();
+ if (req == NULL)
+ return -1;
+
+ /* TODO: Should really use group operating frequency here */
+ freq = rx_freq;
+
+ p2p->pending_action_state = P2P_PENDING_GO_DISC_REQ;
+ if (p2p->cfg->send_action(p2p->cfg->cb_ctx, freq, m->addr,
+ group->cfg->interface_addr,
+ group->cfg->interface_addr,
+ wpabuf_head(req), wpabuf_len(req), 200) < 0)
+ {
+ wpa_msg(p2p->cfg->msg_ctx, MSG_DEBUG,
+ "P2P: Failed to send Action frame");
+ }
+
+ wpabuf_free(req);
+
+ return 0;
+}
+
+
+const u8 * p2p_group_get_interface_addr(struct p2p_group *group)
+{
+ return group->cfg->interface_addr;
+}
+
+
+u8 p2p_group_presence_req(struct p2p_group *group,
+ const u8 *client_interface_addr,
+ const u8 *noa, size_t noa_len)
+{
+ struct p2p_group_member *m;
+ u8 curr_noa[50];
+ int curr_noa_len;
+
+ m = p2p_group_get_client_iface(group, client_interface_addr);
+ if (m == NULL || m->client_info == NULL) {
+ wpa_printf(MSG_DEBUG, "P2P: Client was not in this group");
+ return P2P_SC_FAIL_UNABLE_TO_ACCOMMODATE;
+ }
+
+ wpa_hexdump(MSG_DEBUG, "P2P: Presence Request NoA", noa, noa_len);
+
+ if (group->p2p->cfg->get_noa)
+ curr_noa_len = group->p2p->cfg->get_noa(
+ group->p2p->cfg->cb_ctx, group->cfg->interface_addr,
+ curr_noa, sizeof(curr_noa));
+ else
+ curr_noa_len = -1;
+ if (curr_noa_len < 0)
+ wpa_printf(MSG_DEBUG, "P2P: Failed to fetch current NoA");
+ else if (curr_noa_len == 0)
+ wpa_printf(MSG_DEBUG, "P2P: No NoA being advertized");
+ else
+ wpa_hexdump(MSG_DEBUG, "P2P: Current NoA", curr_noa,
+ curr_noa_len);
+
+ /* TODO: properly process request and store copy */
+ if (curr_noa_len > 0 || curr_noa_len == -1)
+ return P2P_SC_FAIL_UNABLE_TO_ACCOMMODATE;
+
+ return P2P_SC_SUCCESS;
+}
+
+
+unsigned int p2p_get_group_num_members(struct p2p_group *group)
+{
+ return group->num_members;
+}
+
+
+const u8 * p2p_iterate_group_members(struct p2p_group *group, void **next)
+{
+ struct p2p_group_member *iter = *next;
+
+ if (!iter)
+ iter = group->members;
+ else
+ iter = iter->next;
+
+ *next = iter;
+
+ if (!iter)
+ return NULL;
+
+ return iter->addr;
+}
+
+
+int p2p_group_is_client_connected(struct p2p_group *group, const u8 *dev_addr)
+{
+ struct p2p_group_member *m;
+
+ for (m = group->members; m; m = m->next) {
+ if (os_memcmp(m->dev_addr, dev_addr, ETH_ALEN) == 0)
+ return 1;
+ }
+
+ return 0;
+}
+
+
+int p2p_group_is_group_id_match(struct p2p_group *group, const u8 *group_id,
+ size_t group_id_len)
+{
+ if (group_id_len != ETH_ALEN + group->cfg->ssid_len)
+ return 0;
+ if (os_memcmp(group_id, group->p2p->cfg->dev_addr, ETH_ALEN) != 0)
+ return 0;
+ return os_memcmp(group_id + ETH_ALEN, group->cfg->ssid,
+ group->cfg->ssid_len) == 0;
+}
diff --git a/contrib/wpa/src/p2p/p2p_i.h b/contrib/wpa/src/p2p/p2p_i.h
new file mode 100644
index 0000000..27fef01
--- /dev/null
+++ b/contrib/wpa/src/p2p/p2p_i.h
@@ -0,0 +1,714 @@
+/*
+ * P2P - Internal definitions for P2P module
+ * Copyright (c) 2009-2010, Atheros Communications
+ *
+ * This software may be distributed under the terms of the BSD license.
+ * See README for more details.
+ */
+
+#ifndef P2P_I_H
+#define P2P_I_H
+
+#include "utils/list.h"
+#include "p2p.h"
+
+enum p2p_go_state {
+ UNKNOWN_GO,
+ LOCAL_GO,
+ REMOTE_GO
+};
+
+/**
+ * struct p2p_device - P2P Device data (internal to P2P module)
+ */
+struct p2p_device {
+ struct dl_list list;
+ struct os_time last_seen;
+ int listen_freq;
+ enum p2p_wps_method wps_method;
+
+ struct p2p_peer_info info;
+
+ /*
+ * If the peer was discovered based on an interface address (e.g., GO
+ * from Beacon/Probe Response), the interface address is stored here.
+ * p2p_device_addr must still be set in such a case to the unique
+ * identifier for the P2P Device.
+ */
+ u8 interface_addr[ETH_ALEN];
+
+ /*
+ * P2P Device Address of the GO in whose group this P2P Device is a
+ * client.
+ */
+ u8 member_in_go_dev[ETH_ALEN];
+
+ /*
+ * P2P Interface Address of the GO in whose group this P2P Device is a
+ * client.
+ */
+ u8 member_in_go_iface[ETH_ALEN];
+
+ int go_neg_req_sent;
+ enum p2p_go_state go_state;
+ u8 dialog_token;
+ u8 intended_addr[ETH_ALEN];
+
+ char country[3];
+ struct p2p_channels channels;
+ int oper_freq;
+ u8 oper_ssid[32];
+ size_t oper_ssid_len;
+
+ /**
+ * req_config_methods - Pending provision discovery methods
+ */
+ u16 req_config_methods;
+
+ /**
+ * wps_prov_info - Stored provisioning WPS config method
+ *
+ * This is used to store pending WPS config method between Provisioning
+ * Discovery and connection to a running group.
+ */
+ u16 wps_prov_info;
+
+#define P2P_DEV_PROBE_REQ_ONLY BIT(0)
+#define P2P_DEV_REPORTED BIT(1)
+#define P2P_DEV_NOT_YET_READY BIT(2)
+#define P2P_DEV_SD_INFO BIT(3)
+#define P2P_DEV_SD_SCHEDULE BIT(4)
+#define P2P_DEV_PD_PEER_DISPLAY BIT(5)
+#define P2P_DEV_PD_PEER_KEYPAD BIT(6)
+#define P2P_DEV_USER_REJECTED BIT(7)
+#define P2P_DEV_PEER_WAITING_RESPONSE BIT(8)
+#define P2P_DEV_PREFER_PERSISTENT_GROUP BIT(9)
+#define P2P_DEV_WAIT_GO_NEG_RESPONSE BIT(10)
+#define P2P_DEV_WAIT_GO_NEG_CONFIRM BIT(11)
+#define P2P_DEV_GROUP_CLIENT_ONLY BIT(12)
+#define P2P_DEV_FORCE_FREQ BIT(13)
+#define P2P_DEV_PD_FOR_JOIN BIT(14)
+#define P2P_DEV_REPORTED_ONCE BIT(15)
+#define P2P_DEV_PREFER_PERSISTENT_RECONN BIT(16)
+#define P2P_DEV_PD_BEFORE_GO_NEG BIT(17)
+ unsigned int flags;
+
+ int status; /* enum p2p_status_code */
+ unsigned int wait_count;
+ unsigned int connect_reqs;
+ unsigned int invitation_reqs;
+
+ u16 ext_listen_period;
+ u16 ext_listen_interval;
+
+ u8 go_timeout;
+ u8 client_timeout;
+};
+
+struct p2p_sd_query {
+ struct p2p_sd_query *next;
+ u8 peer[ETH_ALEN];
+ int for_all_peers;
+ int wsd; /* Wi-Fi Display Service Discovery Request */
+ struct wpabuf *tlvs;
+};
+
+struct p2p_pending_action_tx {
+ unsigned int freq;
+ u8 dst[ETH_ALEN];
+ u8 src[ETH_ALEN];
+ u8 bssid[ETH_ALEN];
+ size_t len;
+ unsigned int wait_time;
+ /* Followed by len octets of the frame */
+};
+
+/**
+ * struct p2p_data - P2P module data (internal to P2P module)
+ */
+struct p2p_data {
+ /**
+ * cfg - P2P module configuration
+ *
+ * This is included in the same memory allocation with the
+ * struct p2p_data and as such, must not be freed separately.
+ */
+ struct p2p_config *cfg;
+
+ /**
+ * state - The current P2P state
+ */
+ enum p2p_state {
+ /**
+ * P2P_IDLE - Idle
+ */
+ P2P_IDLE,
+
+ /**
+ * P2P_SEARCH - Search (Device Discovery)
+ */
+ P2P_SEARCH,
+
+ /**
+ * P2P_CONNECT - Trying to start GO Negotiation
+ */
+ P2P_CONNECT,
+
+ /**
+ * P2P_CONNECT_LISTEN - Listen during GO Negotiation start
+ */
+ P2P_CONNECT_LISTEN,
+
+ /**
+ * P2P_GO_NEG - In GO Negotiation
+ */
+ P2P_GO_NEG,
+
+ /**
+ * P2P_LISTEN_ONLY - Listen only
+ */
+ P2P_LISTEN_ONLY,
+
+ /**
+ * P2P_WAIT_PEER_CONNECT - Waiting peer in List for GO Neg
+ */
+ P2P_WAIT_PEER_CONNECT,
+
+ /**
+ * P2P_WAIT_PEER_IDLE - Waiting peer idle for GO Neg
+ */
+ P2P_WAIT_PEER_IDLE,
+
+ /**
+ * P2P_SD_DURING_FIND - Service Discovery during find
+ */
+ P2P_SD_DURING_FIND,
+
+ /**
+ * P2P_PROVISIONING - Provisioning (during group formation)
+ */
+ P2P_PROVISIONING,
+
+ /**
+ * P2P_PD_DURING_FIND - Provision Discovery during find
+ */
+ P2P_PD_DURING_FIND,
+
+ /**
+ * P2P_INVITE - Trying to start Invite
+ */
+ P2P_INVITE,
+
+ /**
+ * P2P_INVITE_LISTEN - Listen during Invite
+ */
+ P2P_INVITE_LISTEN,
+
+ /**
+ * P2P_SEARCH_WHEN_READY - Waiting to start Search
+ */
+ P2P_SEARCH_WHEN_READY,
+
+ /**
+ * P2P_CONTINUE_SEARCH_WHEN_READY - Waiting to continue Search
+ */
+ P2P_CONTINUE_SEARCH_WHEN_READY,
+ } state;
+
+ /**
+ * min_disc_int - minDiscoverableInterval
+ */
+ int min_disc_int;
+
+ /**
+ * max_disc_int - maxDiscoverableInterval
+ */
+ int max_disc_int;
+
+ /**
+ * max_disc_tu - Maximum number of TUs for discoverable interval
+ */
+ int max_disc_tu;
+
+ /**
+ * devices - List of known P2P Device peers
+ */
+ struct dl_list devices;
+
+ /**
+ * go_neg_peer - Pointer to GO Negotiation peer
+ */
+ struct p2p_device *go_neg_peer;
+
+ /**
+ * invite_peer - Pointer to Invite peer
+ */
+ struct p2p_device *invite_peer;
+
+ const u8 *invite_go_dev_addr;
+ u8 invite_go_dev_addr_buf[ETH_ALEN];
+
+ /**
+ * sd_peer - Pointer to Service Discovery peer
+ */
+ struct p2p_device *sd_peer;
+
+ /**
+ * sd_query - Pointer to Service Discovery query
+ */
+ struct p2p_sd_query *sd_query;
+
+ /* GO Negotiation data */
+
+ /**
+ * intended_addr - Local Intended P2P Interface Address
+ *
+ * This address is used during group owner negotiation as the Intended
+ * P2P Interface Address and the group interface will be created with
+ * address as the local address in case of successfully completed
+ * negotiation.
+ */
+ u8 intended_addr[ETH_ALEN];
+
+ /**
+ * go_intent - Local GO Intent to be used during GO Negotiation
+ */
+ u8 go_intent;
+
+ /**
+ * next_tie_breaker - Next tie-breaker value to use in GO Negotiation
+ */
+ u8 next_tie_breaker;
+
+ /**
+ * ssid - Selected SSID for GO Negotiation (if local end will be GO)
+ */
+ u8 ssid[32];
+
+ /**
+ * ssid_len - ssid length in octets
+ */
+ size_t ssid_len;
+
+ /**
+ * ssid_set - Whether SSID is already set for GO Negotiation
+ */
+ int ssid_set;
+
+ /**
+ * Regulatory class for own operational channel
+ */
+ u8 op_reg_class;
+
+ /**
+ * op_channel - Own operational channel
+ */
+ u8 op_channel;
+
+ /**
+ * channels - Own supported regulatory classes and channels
+ *
+ * List of supposerted channels per regulatory class. The regulatory
+ * classes are defined in IEEE Std 802.11-2007 Annex J and the
+ * numbering of the clases depends on the configured country code.
+ */
+ struct p2p_channels channels;
+
+ enum p2p_pending_action_state {
+ P2P_NO_PENDING_ACTION,
+ P2P_PENDING_GO_NEG_REQUEST,
+ P2P_PENDING_GO_NEG_RESPONSE,
+ P2P_PENDING_GO_NEG_RESPONSE_FAILURE,
+ P2P_PENDING_GO_NEG_CONFIRM,
+ P2P_PENDING_SD,
+ P2P_PENDING_PD,
+ P2P_PENDING_INVITATION_REQUEST,
+ P2P_PENDING_INVITATION_RESPONSE,
+ P2P_PENDING_DEV_DISC_REQUEST,
+ P2P_PENDING_DEV_DISC_RESPONSE,
+ P2P_PENDING_GO_DISC_REQ
+ } pending_action_state;
+
+ unsigned int pending_listen_freq;
+ unsigned int pending_listen_sec;
+ unsigned int pending_listen_usec;
+
+ u8 dev_capab;
+
+ int in_listen;
+ int drv_in_listen;
+
+ /**
+ * sd_queries - Pending service discovery queries
+ */
+ struct p2p_sd_query *sd_queries;
+
+ /**
+ * srv_update_indic - Service Update Indicator for local services
+ */
+ u16 srv_update_indic;
+
+ struct wpabuf *sd_resp; /* Fragmented SD response */
+ u8 sd_resp_addr[ETH_ALEN];
+ u8 sd_resp_dialog_token;
+ size_t sd_resp_pos; /* Offset in sd_resp */
+ u8 sd_frag_id;
+
+ struct wpabuf *sd_rx_resp; /* Reassembled SD response */
+ u16 sd_rx_update_indic;
+
+ /* P2P Invitation data */
+ enum p2p_invite_role inv_role;
+ u8 inv_bssid[ETH_ALEN];
+ int inv_bssid_set;
+ u8 inv_ssid[32];
+ size_t inv_ssid_len;
+ u8 inv_sa[ETH_ALEN];
+ u8 inv_group_bssid[ETH_ALEN];
+ u8 *inv_group_bssid_ptr;
+ u8 inv_go_dev_addr[ETH_ALEN];
+ u8 inv_status;
+ int inv_op_freq;
+ int inv_persistent;
+
+ enum p2p_discovery_type find_type;
+ unsigned int last_p2p_find_timeout;
+ u8 last_prog_scan_class;
+ u8 last_prog_scan_chan;
+ int p2p_scan_running;
+ enum p2p_after_scan {
+ P2P_AFTER_SCAN_NOTHING,
+ P2P_AFTER_SCAN_LISTEN,
+ P2P_AFTER_SCAN_CONNECT
+ } start_after_scan;
+ u8 after_scan_peer[ETH_ALEN];
+ struct p2p_pending_action_tx *after_scan_tx;
+
+ /* Requested device types for find/search */
+ unsigned int num_req_dev_types;
+ u8 *req_dev_types;
+ u8 *find_dev_id;
+ u8 find_dev_id_buf[ETH_ALEN];
+
+ struct p2p_group **groups;
+ size_t num_groups;
+
+ struct p2p_device *pending_client_disc_go;
+ u8 pending_client_disc_addr[ETH_ALEN];
+ u8 pending_dev_disc_dialog_token;
+ u8 pending_dev_disc_addr[ETH_ALEN];
+ int pending_dev_disc_freq;
+ unsigned int pending_client_disc_freq;
+
+ int ext_listen_only;
+ unsigned int ext_listen_period;
+ unsigned int ext_listen_interval;
+ unsigned int ext_listen_interval_sec;
+ unsigned int ext_listen_interval_usec;
+
+ u8 peer_filter[ETH_ALEN];
+
+ int cross_connect;
+
+ int best_freq_24;
+ int best_freq_5;
+ int best_freq_overall;
+
+ /**
+ * wps_vendor_ext - WPS Vendor Extensions to add
+ */
+ struct wpabuf *wps_vendor_ext[P2P_MAX_WPS_VENDOR_EXT];
+
+ /*
+ * user_initiated_pd - Whether a PD request is user initiated or not.
+ */
+ u8 user_initiated_pd;
+
+ /*
+ * Keep track of which peer a given PD request was sent to.
+ * Used to raise a timeout alert in case there is no response.
+ */
+ u8 pending_pd_devaddr[ETH_ALEN];
+
+ /*
+ * Retry counter for provision discovery requests when issued
+ * in IDLE state.
+ */
+ int pd_retries;
+
+ u8 go_timeout;
+ u8 client_timeout;
+
+ /* Extra delay in milliseconds between search iterations */
+ unsigned int search_delay;
+ int in_search_delay;
+
+#ifdef CONFIG_WIFI_DISPLAY
+ struct wpabuf *wfd_ie_beacon;
+ struct wpabuf *wfd_ie_probe_req;
+ struct wpabuf *wfd_ie_probe_resp;
+ struct wpabuf *wfd_ie_assoc_req;
+ struct wpabuf *wfd_ie_invitation;
+ struct wpabuf *wfd_ie_prov_disc_req;
+ struct wpabuf *wfd_ie_prov_disc_resp;
+ struct wpabuf *wfd_ie_go_neg;
+ struct wpabuf *wfd_dev_info;
+ struct wpabuf *wfd_assoc_bssid;
+ struct wpabuf *wfd_coupled_sink_info;
+#endif /* CONFIG_WIFI_DISPLAY */
+};
+
+/**
+ * struct p2p_message - Parsed P2P message (or P2P IE)
+ */
+struct p2p_message {
+ struct wpabuf *p2p_attributes;
+ struct wpabuf *wps_attributes;
+ struct wpabuf *wfd_subelems;
+
+ u8 dialog_token;
+
+ const u8 *capability;
+ const u8 *go_intent;
+ const u8 *status;
+ const u8 *listen_channel;
+ const u8 *operating_channel;
+ const u8 *channel_list;
+ u8 channel_list_len;
+ const u8 *config_timeout;
+ const u8 *intended_addr;
+ const u8 *group_bssid;
+ const u8 *invitation_flags;
+
+ const u8 *group_info;
+ size_t group_info_len;
+
+ const u8 *group_id;
+ size_t group_id_len;
+
+ const u8 *device_id;
+
+ const u8 *manageability;
+
+ const u8 *noa;
+ size_t noa_len;
+
+ const u8 *ext_listen_timing;
+
+ const u8 *minor_reason_code;
+
+ /* P2P Device Info */
+ const u8 *p2p_device_info;
+ size_t p2p_device_info_len;
+ const u8 *p2p_device_addr;
+ const u8 *pri_dev_type;
+ u8 num_sec_dev_types;
+ char device_name[33];
+ u16 config_methods;
+
+ /* WPS IE */
+ u16 dev_password_id;
+ u16 wps_config_methods;
+ const u8 *wps_pri_dev_type;
+ const u8 *wps_sec_dev_type_list;
+ size_t wps_sec_dev_type_list_len;
+ const u8 *wps_vendor_ext[P2P_MAX_WPS_VENDOR_EXT];
+ size_t wps_vendor_ext_len[P2P_MAX_WPS_VENDOR_EXT];
+ const u8 *manufacturer;
+ size_t manufacturer_len;
+ const u8 *model_name;
+ size_t model_name_len;
+ const u8 *model_number;
+ size_t model_number_len;
+ const u8 *serial_number;
+ size_t serial_number_len;
+
+ /* DS Parameter Set IE */
+ const u8 *ds_params;
+
+ /* SSID IE */
+ const u8 *ssid;
+};
+
+
+#define P2P_MAX_GROUP_ENTRIES 50
+
+struct p2p_group_info {
+ unsigned int num_clients;
+ struct p2p_client_info {
+ const u8 *p2p_device_addr;
+ const u8 *p2p_interface_addr;
+ u8 dev_capab;
+ u16 config_methods;
+ const u8 *pri_dev_type;
+ u8 num_sec_dev_types;
+ const u8 *sec_dev_types;
+ const char *dev_name;
+ size_t dev_name_len;
+ } client[P2P_MAX_GROUP_ENTRIES];
+};
+
+
+/* p2p_utils.c */
+int p2p_random(char *buf, size_t len);
+int p2p_channel_to_freq(const char *country, int reg_class, int channel);
+int p2p_freq_to_channel(const char *country, unsigned int freq, u8 *reg_class,
+ u8 *channel);
+void p2p_channels_intersect(const struct p2p_channels *a,
+ const struct p2p_channels *b,
+ struct p2p_channels *res);
+int p2p_channels_includes(const struct p2p_channels *channels, u8 reg_class,
+ u8 channel);
+
+/* p2p_parse.c */
+int p2p_parse_p2p_ie(const struct wpabuf *buf, struct p2p_message *msg);
+int p2p_parse_ies(const u8 *data, size_t len, struct p2p_message *msg);
+int p2p_parse(const u8 *data, size_t len, struct p2p_message *msg);
+void p2p_parse_free(struct p2p_message *msg);
+int p2p_attr_text(struct wpabuf *data, char *buf, char *end);
+int p2p_group_info_parse(const u8 *gi, size_t gi_len,
+ struct p2p_group_info *info);
+
+/* p2p_build.c */
+
+struct p2p_noa_desc {
+ u8 count_type;
+ u32 duration;
+ u32 interval;
+ u32 start_time;
+};
+
+/* p2p_group.c */
+const u8 * p2p_group_get_interface_addr(struct p2p_group *group);
+u8 p2p_group_presence_req(struct p2p_group *group,
+ const u8 *client_interface_addr,
+ const u8 *noa, size_t noa_len);
+int p2p_group_is_group_id_match(struct p2p_group *group, const u8 *group_id,
+ size_t group_id_len);
+void p2p_group_update_ies(struct p2p_group *group);
+struct wpabuf * p2p_group_get_wfd_ie(struct p2p_group *g);
+
+
+void p2p_buf_add_action_hdr(struct wpabuf *buf, u8 subtype, u8 dialog_token);
+void p2p_buf_add_public_action_hdr(struct wpabuf *buf, u8 subtype,
+ u8 dialog_token);
+u8 * p2p_buf_add_ie_hdr(struct wpabuf *buf);
+void p2p_buf_add_status(struct wpabuf *buf, u8 status);
+void p2p_buf_add_device_info(struct wpabuf *buf, struct p2p_data *p2p,
+ struct p2p_device *peer);
+void p2p_buf_add_device_id(struct wpabuf *buf, const u8 *dev_addr);
+void p2p_buf_update_ie_hdr(struct wpabuf *buf, u8 *len);
+void p2p_buf_add_capability(struct wpabuf *buf, u8 dev_capab, u8 group_capab);
+void p2p_buf_add_go_intent(struct wpabuf *buf, u8 go_intent);
+void p2p_buf_add_listen_channel(struct wpabuf *buf, const char *country,
+ u8 reg_class, u8 channel);
+void p2p_buf_add_operating_channel(struct wpabuf *buf, const char *country,
+ u8 reg_class, u8 channel);
+void p2p_buf_add_channel_list(struct wpabuf *buf, const char *country,
+ struct p2p_channels *chan);
+void p2p_buf_add_config_timeout(struct wpabuf *buf, u8 go_timeout,
+ u8 client_timeout);
+void p2p_buf_add_intended_addr(struct wpabuf *buf, const u8 *interface_addr);
+void p2p_buf_add_group_bssid(struct wpabuf *buf, const u8 *bssid);
+void p2p_buf_add_group_id(struct wpabuf *buf, const u8 *dev_addr,
+ const u8 *ssid, size_t ssid_len);
+void p2p_buf_add_invitation_flags(struct wpabuf *buf, u8 flags);
+void p2p_buf_add_noa(struct wpabuf *buf, u8 noa_index, u8 opp_ps, u8 ctwindow,
+ struct p2p_noa_desc *desc1, struct p2p_noa_desc *desc2);
+void p2p_buf_add_ext_listen_timing(struct wpabuf *buf, u16 period,
+ u16 interval);
+void p2p_buf_add_p2p_interface(struct wpabuf *buf, struct p2p_data *p2p);
+void p2p_build_wps_ie(struct p2p_data *p2p, struct wpabuf *buf, int pw_id,
+ int all_attr);
+
+/* p2p_sd.c */
+struct p2p_sd_query * p2p_pending_sd_req(struct p2p_data *p2p,
+ struct p2p_device *dev);
+void p2p_free_sd_queries(struct p2p_data *p2p);
+void p2p_rx_gas_initial_req(struct p2p_data *p2p, const u8 *sa,
+ const u8 *data, size_t len, int rx_freq);
+void p2p_rx_gas_initial_resp(struct p2p_data *p2p, const u8 *sa,
+ const u8 *data, size_t len, int rx_freq);
+void p2p_rx_gas_comeback_req(struct p2p_data *p2p, const u8 *sa,
+ const u8 *data, size_t len, int rx_freq);
+void p2p_rx_gas_comeback_resp(struct p2p_data *p2p, const u8 *sa,
+ const u8 *data, size_t len, int rx_freq);
+int p2p_start_sd(struct p2p_data *p2p, struct p2p_device *dev);
+
+/* p2p_go_neg.c */
+int p2p_peer_channels_check(struct p2p_data *p2p, struct p2p_channels *own,
+ struct p2p_device *dev,
+ const u8 *channel_list, size_t channel_list_len);
+void p2p_process_go_neg_req(struct p2p_data *p2p, const u8 *sa,
+ const u8 *data, size_t len, int rx_freq);
+void p2p_process_go_neg_resp(struct p2p_data *p2p, const u8 *sa,
+ const u8 *data, size_t len, int rx_freq);
+void p2p_process_go_neg_conf(struct p2p_data *p2p, const u8 *sa,
+ const u8 *data, size_t len);
+int p2p_connect_send(struct p2p_data *p2p, struct p2p_device *dev);
+u16 p2p_wps_method_pw_id(enum p2p_wps_method wps_method);
+void p2p_reselect_channel(struct p2p_data *p2p,
+ struct p2p_channels *intersection);
+
+/* p2p_pd.c */
+void p2p_process_prov_disc_req(struct p2p_data *p2p, const u8 *sa,
+ const u8 *data, size_t len, int rx_freq);
+void p2p_process_prov_disc_resp(struct p2p_data *p2p, const u8 *sa,
+ const u8 *data, size_t len);
+int p2p_send_prov_disc_req(struct p2p_data *p2p, struct p2p_device *dev,
+ int join, int force_freq);
+void p2p_reset_pending_pd(struct p2p_data *p2p);
+
+/* p2p_invitation.c */
+void p2p_process_invitation_req(struct p2p_data *p2p, const u8 *sa,
+ const u8 *data, size_t len, int rx_freq);
+void p2p_process_invitation_resp(struct p2p_data *p2p, const u8 *sa,
+ const u8 *data, size_t len);
+int p2p_invite_send(struct p2p_data *p2p, struct p2p_device *dev,
+ const u8 *go_dev_addr);
+void p2p_invitation_req_cb(struct p2p_data *p2p, int success);
+void p2p_invitation_resp_cb(struct p2p_data *p2p, int success);
+
+/* p2p_dev_disc.c */
+void p2p_process_dev_disc_req(struct p2p_data *p2p, const u8 *sa,
+ const u8 *data, size_t len, int rx_freq);
+void p2p_dev_disc_req_cb(struct p2p_data *p2p, int success);
+int p2p_send_dev_disc_req(struct p2p_data *p2p, struct p2p_device *dev);
+void p2p_dev_disc_resp_cb(struct p2p_data *p2p, int success);
+void p2p_process_dev_disc_resp(struct p2p_data *p2p, const u8 *sa,
+ const u8 *data, size_t len);
+void p2p_go_disc_req_cb(struct p2p_data *p2p, int success);
+void p2p_process_go_disc_req(struct p2p_data *p2p, const u8 *da, const u8 *sa,
+ const u8 *data, size_t len, int rx_freq);
+
+/* p2p.c */
+void p2p_set_state(struct p2p_data *p2p, int new_state);
+void p2p_set_timeout(struct p2p_data *p2p, unsigned int sec,
+ unsigned int usec);
+void p2p_clear_timeout(struct p2p_data *p2p);
+void p2p_continue_find(struct p2p_data *p2p);
+struct p2p_device * p2p_add_dev_from_go_neg_req(struct p2p_data *p2p,
+ const u8 *addr,
+ struct p2p_message *msg);
+void p2p_add_dev_info(struct p2p_data *p2p, const u8 *addr,
+ struct p2p_device *dev, struct p2p_message *msg);
+int p2p_add_device(struct p2p_data *p2p, const u8 *addr, int freq,
+ unsigned int age_ms, int level, const u8 *ies,
+ size_t ies_len, int scan_res);
+struct p2p_device * p2p_get_device(struct p2p_data *p2p, const u8 *addr);
+struct p2p_device * p2p_get_device_interface(struct p2p_data *p2p,
+ const u8 *addr);
+void p2p_go_neg_failed(struct p2p_data *p2p, struct p2p_device *peer,
+ int status);
+void p2p_go_complete(struct p2p_data *p2p, struct p2p_device *peer);
+int p2p_match_dev_type(struct p2p_data *p2p, struct wpabuf *wps);
+int dev_type_list_match(const u8 *dev_type, const u8 *req_dev_type[],
+ size_t num_req_dev_type);
+struct wpabuf * p2p_build_probe_resp_ies(struct p2p_data *p2p);
+void p2p_build_ssid(struct p2p_data *p2p, u8 *ssid, size_t *ssid_len);
+int p2p_send_action(struct p2p_data *p2p, unsigned int freq, const u8 *dst,
+ const u8 *src, const u8 *bssid, const u8 *buf,
+ size_t len, unsigned int wait_time);
+void p2p_stop_listen_for_freq(struct p2p_data *p2p, int freq);
+
+#endif /* P2P_I_H */
diff --git a/contrib/wpa/src/p2p/p2p_invitation.c b/contrib/wpa/src/p2p/p2p_invitation.c
new file mode 100644
index 0000000..983dd6b
--- /dev/null
+++ b/contrib/wpa/src/p2p/p2p_invitation.c
@@ -0,0 +1,608 @@
+/*
+ * Wi-Fi Direct - P2P Invitation procedure
+ * Copyright (c) 2010, Atheros Communications
+ *
+ * This software may be distributed under the terms of the BSD license.
+ * See README for more details.
+ */
+
+#include "includes.h"
+
+#include "common.h"
+#include "common/ieee802_11_defs.h"
+#include "p2p_i.h"
+#include "p2p.h"
+
+
+static struct wpabuf * p2p_build_invitation_req(struct p2p_data *p2p,
+ struct p2p_device *peer,
+ const u8 *go_dev_addr)
+{
+ struct wpabuf *buf;
+ u8 *len;
+ const u8 *dev_addr;
+ size_t extra = 0;
+
+#ifdef CONFIG_WIFI_DISPLAY
+ struct wpabuf *wfd_ie = p2p->wfd_ie_invitation;
+ if (wfd_ie && p2p->inv_role == P2P_INVITE_ROLE_ACTIVE_GO) {
+ size_t i;
+ for (i = 0; i < p2p->num_groups; i++) {
+ struct p2p_group *g = p2p->groups[i];
+ struct wpabuf *ie;
+ if (os_memcmp(p2p_group_get_interface_addr(g),
+ p2p->inv_bssid, ETH_ALEN) != 0)
+ continue;
+ ie = p2p_group_get_wfd_ie(g);
+ if (ie) {
+ wfd_ie = ie;
+ break;
+ }
+ }
+ }
+ if (wfd_ie)
+ extra = wpabuf_len(wfd_ie);
+#endif /* CONFIG_WIFI_DISPLAY */
+
+ buf = wpabuf_alloc(1000 + extra);
+ if (buf == NULL)
+ return NULL;
+
+ peer->dialog_token++;
+ if (peer->dialog_token == 0)
+ peer->dialog_token = 1;
+ p2p_buf_add_public_action_hdr(buf, P2P_INVITATION_REQ,
+ peer->dialog_token);
+
+ len = p2p_buf_add_ie_hdr(buf);
+ if (p2p->inv_role == P2P_INVITE_ROLE_ACTIVE_GO || !p2p->inv_persistent)
+ p2p_buf_add_config_timeout(buf, 0, 0);
+ else
+ p2p_buf_add_config_timeout(buf, p2p->go_timeout,
+ p2p->client_timeout);
+ p2p_buf_add_invitation_flags(buf, p2p->inv_persistent ?
+ P2P_INVITATION_FLAGS_TYPE : 0);
+ p2p_buf_add_operating_channel(buf, p2p->cfg->country,
+ p2p->op_reg_class, p2p->op_channel);
+ if (p2p->inv_bssid_set)
+ p2p_buf_add_group_bssid(buf, p2p->inv_bssid);
+ p2p_buf_add_channel_list(buf, p2p->cfg->country, &p2p->channels);
+ if (go_dev_addr)
+ dev_addr = go_dev_addr;
+ else if (p2p->inv_role == P2P_INVITE_ROLE_CLIENT)
+ dev_addr = peer->info.p2p_device_addr;
+ else
+ dev_addr = p2p->cfg->dev_addr;
+ p2p_buf_add_group_id(buf, dev_addr, p2p->inv_ssid, p2p->inv_ssid_len);
+ p2p_buf_add_device_info(buf, p2p, peer);
+ p2p_buf_update_ie_hdr(buf, len);
+
+#ifdef CONFIG_WIFI_DISPLAY
+ if (wfd_ie)
+ wpabuf_put_buf(buf, wfd_ie);
+#endif /* CONFIG_WIFI_DISPLAY */
+
+ return buf;
+}
+
+
+static struct wpabuf * p2p_build_invitation_resp(struct p2p_data *p2p,
+ struct p2p_device *peer,
+ u8 dialog_token, u8 status,
+ const u8 *group_bssid,
+ u8 reg_class, u8 channel,
+ struct p2p_channels *channels)
+{
+ struct wpabuf *buf;
+ u8 *len;
+ size_t extra = 0;
+
+#ifdef CONFIG_WIFI_DISPLAY
+ struct wpabuf *wfd_ie = p2p->wfd_ie_invitation;
+ if (wfd_ie && group_bssid) {
+ size_t i;
+ for (i = 0; i < p2p->num_groups; i++) {
+ struct p2p_group *g = p2p->groups[i];
+ struct wpabuf *ie;
+ if (os_memcmp(p2p_group_get_interface_addr(g),
+ group_bssid, ETH_ALEN) != 0)
+ continue;
+ ie = p2p_group_get_wfd_ie(g);
+ if (ie) {
+ wfd_ie = ie;
+ break;
+ }
+ }
+ }
+ if (wfd_ie)
+ extra = wpabuf_len(wfd_ie);
+#endif /* CONFIG_WIFI_DISPLAY */
+
+ buf = wpabuf_alloc(1000 + extra);
+ if (buf == NULL)
+ return NULL;
+
+ p2p_buf_add_public_action_hdr(buf, P2P_INVITATION_RESP,
+ dialog_token);
+
+ len = p2p_buf_add_ie_hdr(buf);
+ p2p_buf_add_status(buf, status);
+ p2p_buf_add_config_timeout(buf, 0, 0); /* FIX */
+ if (reg_class && channel)
+ p2p_buf_add_operating_channel(buf, p2p->cfg->country,
+ reg_class, channel);
+ if (group_bssid)
+ p2p_buf_add_group_bssid(buf, group_bssid);
+ if (channels)
+ p2p_buf_add_channel_list(buf, p2p->cfg->country, channels);
+ p2p_buf_update_ie_hdr(buf, len);
+
+#ifdef CONFIG_WIFI_DISPLAY
+ if (wfd_ie)
+ wpabuf_put_buf(buf, wfd_ie);
+#endif /* CONFIG_WIFI_DISPLAY */
+
+ return buf;
+}
+
+
+void p2p_process_invitation_req(struct p2p_data *p2p, const u8 *sa,
+ const u8 *data, size_t len, int rx_freq)
+{
+ struct p2p_device *dev;
+ struct p2p_message msg;
+ struct wpabuf *resp = NULL;
+ u8 status = P2P_SC_FAIL_INFO_CURRENTLY_UNAVAILABLE;
+ int freq;
+ int go = 0;
+ u8 group_bssid[ETH_ALEN], *bssid;
+ int op_freq = 0;
+ u8 reg_class = 0, channel = 0;
+ struct p2p_channels intersection, *channels = NULL;
+ int persistent;
+
+ os_memset(group_bssid, 0, sizeof(group_bssid));
+
+ wpa_msg(p2p->cfg->msg_ctx, MSG_DEBUG,
+ "P2P: Received Invitation Request from " MACSTR " (freq=%d)",
+ MAC2STR(sa), rx_freq);
+
+ if (p2p_parse(data, len, &msg))
+ return;
+
+ dev = p2p_get_device(p2p, sa);
+ if (dev == NULL || (dev->flags & P2P_DEV_PROBE_REQ_ONLY)) {
+ wpa_msg(p2p->cfg->msg_ctx, MSG_DEBUG,
+ "P2P: Invitation Request from unknown peer "
+ MACSTR, MAC2STR(sa));
+
+ if (p2p_add_device(p2p, sa, rx_freq, 0, 0, data + 1, len - 1,
+ 0)) {
+ wpa_msg(p2p->cfg->msg_ctx, MSG_DEBUG,
+ "P2P: Invitation Request add device failed "
+ MACSTR, MAC2STR(sa));
+ status = P2P_SC_FAIL_INFO_CURRENTLY_UNAVAILABLE;
+ goto fail;
+ }
+
+ dev = p2p_get_device(p2p, sa);
+ if (dev == NULL) {
+ wpa_msg(p2p->cfg->msg_ctx, MSG_DEBUG,
+ "P2P: Reject Invitation Request from unknown "
+ "peer " MACSTR, MAC2STR(sa));
+ status = P2P_SC_FAIL_INFO_CURRENTLY_UNAVAILABLE;
+ goto fail;
+ }
+ }
+
+ if (!msg.group_id || !msg.channel_list) {
+ wpa_msg(p2p->cfg->msg_ctx, MSG_DEBUG,
+ "P2P: Mandatory attribute missing in Invitation "
+ "Request from " MACSTR, MAC2STR(sa));
+ status = P2P_SC_FAIL_INVALID_PARAMS;
+ goto fail;
+ }
+
+ if (msg.invitation_flags)
+ persistent = *msg.invitation_flags & P2P_INVITATION_FLAGS_TYPE;
+ else {
+ /* Invitation Flags is a mandatory attribute starting from P2P
+ * spec 1.06. As a backwards compatibility mechanism, assume
+ * the request was for a persistent group if the attribute is
+ * missing.
+ */
+ wpa_printf(MSG_DEBUG, "P2P: Mandatory Invitation Flags "
+ "attribute missing from Invitation Request");
+ persistent = 1;
+ }
+
+ if (p2p_peer_channels_check(p2p, &p2p->cfg->channels, dev,
+ msg.channel_list, msg.channel_list_len) <
+ 0) {
+ wpa_msg(p2p->cfg->msg_ctx, MSG_DEBUG,
+ "P2P: No common channels found");
+ status = P2P_SC_FAIL_NO_COMMON_CHANNELS;
+ goto fail;
+ }
+
+ if (p2p->cfg->invitation_process) {
+ status = p2p->cfg->invitation_process(
+ p2p->cfg->cb_ctx, sa, msg.group_bssid, msg.group_id,
+ msg.group_id + ETH_ALEN, msg.group_id_len - ETH_ALEN,
+ &go, group_bssid, &op_freq, persistent);
+ }
+
+ if (op_freq) {
+ wpa_msg(p2p->cfg->msg_ctx, MSG_DEBUG, "P2P: Invitation "
+ "processing forced frequency %d MHz", op_freq);
+ if (p2p_freq_to_channel(p2p->cfg->country, op_freq,
+ &reg_class, &channel) < 0) {
+ wpa_msg(p2p->cfg->msg_ctx, MSG_DEBUG,
+ "P2P: Unknown forced freq %d MHz from "
+ "invitation_process()", op_freq);
+ status = P2P_SC_FAIL_NO_COMMON_CHANNELS;
+ goto fail;
+ }
+
+ p2p_channels_intersect(&p2p->cfg->channels, &dev->channels,
+ &intersection);
+ if (!p2p_channels_includes(&intersection, reg_class, channel))
+ {
+ wpa_msg(p2p->cfg->msg_ctx, MSG_DEBUG,
+ "P2P: forced freq %d MHz not in the supported "
+ "channels interaction", op_freq);
+ status = P2P_SC_FAIL_NO_COMMON_CHANNELS;
+ goto fail;
+ }
+
+ if (status == P2P_SC_SUCCESS)
+ channels = &intersection;
+ } else {
+ wpa_msg(p2p->cfg->msg_ctx, MSG_DEBUG,
+ "P2P: No forced channel from invitation processing - "
+ "figure out best one to use");
+
+ p2p_channels_intersect(&p2p->cfg->channels, &dev->channels,
+ &intersection);
+ /* Default to own configuration as a starting point */
+ p2p->op_reg_class = p2p->cfg->op_reg_class;
+ p2p->op_channel = p2p->cfg->op_channel;
+ wpa_msg(p2p->cfg->msg_ctx, MSG_DEBUG, "P2P: Own default "
+ "op_class %d channel %d",
+ p2p->op_reg_class, p2p->op_channel);
+
+ /* Use peer preference if specified and compatible */
+ if (msg.operating_channel) {
+ int req_freq;
+ req_freq = p2p_channel_to_freq(
+ (const char *) msg.operating_channel,
+ msg.operating_channel[3],
+ msg.operating_channel[4]);
+ wpa_msg(p2p->cfg->msg_ctx, MSG_DEBUG, "P2P: Peer "
+ "operating channel preference: %d MHz",
+ req_freq);
+ if (req_freq > 0 &&
+ p2p_channels_includes(&intersection,
+ msg.operating_channel[3],
+ msg.operating_channel[4])) {
+ p2p->op_reg_class = msg.operating_channel[3];
+ p2p->op_channel = msg.operating_channel[4];
+ wpa_msg(p2p->cfg->msg_ctx, MSG_DEBUG,
+ "P2P: Use peer preference op_class %d "
+ "channel %d",
+ p2p->op_reg_class, p2p->op_channel);
+ } else {
+ wpa_msg(p2p->cfg->msg_ctx, MSG_DEBUG,
+ "P2P: Cannot use peer channel "
+ "preference");
+ }
+ }
+
+ if (!p2p_channels_includes(&intersection, p2p->op_reg_class,
+ p2p->op_channel)) {
+ wpa_msg(p2p->cfg->msg_ctx, MSG_DEBUG,
+ "P2P: Initially selected channel (op_class %d "
+ "channel %d) not in channel intersection - try "
+ "to reselect",
+ p2p->op_reg_class, p2p->op_channel);
+ p2p_reselect_channel(p2p, &intersection);
+ wpa_msg(p2p->cfg->msg_ctx, MSG_DEBUG,
+ "P2P: Re-selection result: op_class %d "
+ "channel %d",
+ p2p->op_reg_class, p2p->op_channel);
+ if (!p2p_channels_includes(&intersection,
+ p2p->op_reg_class,
+ p2p->op_channel)) {
+ wpa_msg(p2p->cfg->msg_ctx, MSG_DEBUG,
+ "P2P: Peer does not support selected "
+ "operating channel (reg_class=%u "
+ "channel=%u)",
+ p2p->op_reg_class, p2p->op_channel);
+ status = P2P_SC_FAIL_NO_COMMON_CHANNELS;
+ goto fail;
+ }
+ }
+
+ op_freq = p2p_channel_to_freq(p2p->cfg->country,
+ p2p->op_reg_class,
+ p2p->op_channel);
+ if (op_freq < 0) {
+ wpa_msg(p2p->cfg->msg_ctx, MSG_DEBUG,
+ "P2P: Unknown operational channel "
+ "(country=%c%c reg_class=%u channel=%u)",
+ p2p->cfg->country[0], p2p->cfg->country[1],
+ p2p->op_reg_class, p2p->op_channel);
+ status = P2P_SC_FAIL_NO_COMMON_CHANNELS;
+ goto fail;
+ }
+ wpa_msg(p2p->cfg->msg_ctx, MSG_DEBUG, "P2P: Selected operating "
+ "channel - %d MHz", op_freq);
+
+ if (status == P2P_SC_SUCCESS) {
+ reg_class = p2p->op_reg_class;
+ channel = p2p->op_channel;
+ channels = &intersection;
+ }
+ }
+
+fail:
+ if (go && status == P2P_SC_SUCCESS && !is_zero_ether_addr(group_bssid))
+ bssid = group_bssid;
+ else
+ bssid = NULL;
+ resp = p2p_build_invitation_resp(p2p, dev, msg.dialog_token, status,
+ bssid, reg_class, channel, channels);
+
+ if (resp == NULL)
+ goto out;
+
+ if (rx_freq > 0)
+ freq = rx_freq;
+ else
+ freq = p2p_channel_to_freq(p2p->cfg->country,
+ p2p->cfg->reg_class,
+ p2p->cfg->channel);
+ if (freq < 0) {
+ wpa_msg(p2p->cfg->msg_ctx, MSG_DEBUG,
+ "P2P: Unknown regulatory class/channel");
+ goto out;
+ }
+
+ /*
+ * Store copy of invitation data to be used when processing TX status
+ * callback for the Acton frame.
+ */
+ os_memcpy(p2p->inv_sa, sa, ETH_ALEN);
+ if (msg.group_bssid) {
+ os_memcpy(p2p->inv_group_bssid, msg.group_bssid, ETH_ALEN);
+ p2p->inv_group_bssid_ptr = p2p->inv_group_bssid;
+ } else
+ p2p->inv_group_bssid_ptr = NULL;
+ if (msg.group_id_len - ETH_ALEN <= 32) {
+ os_memcpy(p2p->inv_ssid, msg.group_id + ETH_ALEN,
+ msg.group_id_len - ETH_ALEN);
+ p2p->inv_ssid_len = msg.group_id_len - ETH_ALEN;
+ }
+ os_memcpy(p2p->inv_go_dev_addr, msg.group_id, ETH_ALEN);
+ p2p->inv_status = status;
+ p2p->inv_op_freq = op_freq;
+
+ p2p->pending_action_state = P2P_PENDING_INVITATION_RESPONSE;
+ if (p2p_send_action(p2p, freq, sa, p2p->cfg->dev_addr,
+ p2p->cfg->dev_addr,
+ wpabuf_head(resp), wpabuf_len(resp), 200) < 0) {
+ wpa_msg(p2p->cfg->msg_ctx, MSG_DEBUG,
+ "P2P: Failed to send Action frame");
+ }
+
+out:
+ wpabuf_free(resp);
+ p2p_parse_free(&msg);
+}
+
+
+void p2p_process_invitation_resp(struct p2p_data *p2p, const u8 *sa,
+ const u8 *data, size_t len)
+{
+ struct p2p_device *dev;
+ struct p2p_message msg;
+
+ wpa_msg(p2p->cfg->msg_ctx, MSG_DEBUG,
+ "P2P: Received Invitation Response from " MACSTR,
+ MAC2STR(sa));
+
+ dev = p2p_get_device(p2p, sa);
+ if (dev == NULL) {
+ wpa_msg(p2p->cfg->msg_ctx, MSG_DEBUG,
+ "P2P: Ignore Invitation Response from unknown peer "
+ MACSTR, MAC2STR(sa));
+ return;
+ }
+
+ if (dev != p2p->invite_peer) {
+ wpa_msg(p2p->cfg->msg_ctx, MSG_DEBUG,
+ "P2P: Ignore unexpected Invitation Response from peer "
+ MACSTR, MAC2STR(sa));
+ return;
+ }
+
+ if (p2p_parse(data, len, &msg))
+ return;
+
+ if (!msg.status) {
+ wpa_msg(p2p->cfg->msg_ctx, MSG_DEBUG,
+ "P2P: Mandatory Status attribute missing in "
+ "Invitation Response from " MACSTR, MAC2STR(sa));
+ p2p_parse_free(&msg);
+ return;
+ }
+
+ if (p2p->cfg->invitation_result)
+ p2p->cfg->invitation_result(p2p->cfg->cb_ctx, *msg.status,
+ msg.group_bssid);
+
+ p2p_parse_free(&msg);
+
+ p2p_clear_timeout(p2p);
+ p2p_set_state(p2p, P2P_IDLE);
+ p2p->invite_peer = NULL;
+}
+
+
+int p2p_invite_send(struct p2p_data *p2p, struct p2p_device *dev,
+ const u8 *go_dev_addr)
+{
+ struct wpabuf *req;
+ int freq;
+
+ freq = dev->listen_freq > 0 ? dev->listen_freq : dev->oper_freq;
+ if (freq <= 0) {
+ wpa_msg(p2p->cfg->msg_ctx, MSG_DEBUG,
+ "P2P: No Listen/Operating frequency known for the "
+ "peer " MACSTR " to send Invitation Request",
+ MAC2STR(dev->info.p2p_device_addr));
+ return -1;
+ }
+
+ req = p2p_build_invitation_req(p2p, dev, go_dev_addr);
+ if (req == NULL)
+ return -1;
+ if (p2p->state != P2P_IDLE)
+ p2p_stop_listen_for_freq(p2p, freq);
+ wpa_msg(p2p->cfg->msg_ctx, MSG_DEBUG,
+ "P2P: Sending Invitation Request");
+ p2p_set_state(p2p, P2P_INVITE);
+ p2p->pending_action_state = P2P_PENDING_INVITATION_REQUEST;
+ p2p->invite_peer = dev;
+ dev->invitation_reqs++;
+ if (p2p_send_action(p2p, freq, dev->info.p2p_device_addr,
+ p2p->cfg->dev_addr, dev->info.p2p_device_addr,
+ wpabuf_head(req), wpabuf_len(req), 200) < 0) {
+ wpa_msg(p2p->cfg->msg_ctx, MSG_DEBUG,
+ "P2P: Failed to send Action frame");
+ /* Use P2P find to recover and retry */
+ p2p_set_timeout(p2p, 0, 0);
+ }
+
+ wpabuf_free(req);
+
+ return 0;
+}
+
+
+void p2p_invitation_req_cb(struct p2p_data *p2p, int success)
+{
+ wpa_msg(p2p->cfg->msg_ctx, MSG_DEBUG,
+ "P2P: Invitation Request TX callback: success=%d", success);
+
+ if (p2p->invite_peer == NULL) {
+ wpa_msg(p2p->cfg->msg_ctx, MSG_DEBUG,
+ "P2P: No pending Invite");
+ return;
+ }
+
+ /*
+ * Use P2P find, if needed, to find the other device from its listen
+ * channel.
+ */
+ p2p_set_state(p2p, P2P_INVITE);
+ p2p_set_timeout(p2p, 0, 100000);
+}
+
+
+void p2p_invitation_resp_cb(struct p2p_data *p2p, int success)
+{
+ wpa_msg(p2p->cfg->msg_ctx, MSG_DEBUG,
+ "P2P: Invitation Response TX callback: success=%d", success);
+ p2p->cfg->send_action_done(p2p->cfg->cb_ctx);
+
+ if (success && p2p->cfg->invitation_received) {
+ p2p->cfg->invitation_received(p2p->cfg->cb_ctx,
+ p2p->inv_sa,
+ p2p->inv_group_bssid_ptr,
+ p2p->inv_ssid, p2p->inv_ssid_len,
+ p2p->inv_go_dev_addr,
+ p2p->inv_status,
+ p2p->inv_op_freq);
+ }
+}
+
+
+int p2p_invite(struct p2p_data *p2p, const u8 *peer, enum p2p_invite_role role,
+ const u8 *bssid, const u8 *ssid, size_t ssid_len,
+ unsigned int force_freq, const u8 *go_dev_addr,
+ int persistent_group)
+{
+ struct p2p_device *dev;
+
+ wpa_msg(p2p->cfg->msg_ctx, MSG_DEBUG,
+ "P2P: Request to invite peer " MACSTR " role=%d persistent=%d "
+ "force_freq=%u",
+ MAC2STR(peer), role, persistent_group, force_freq);
+ if (bssid)
+ wpa_msg(p2p->cfg->msg_ctx, MSG_DEBUG,
+ "P2P: Invitation for BSSID " MACSTR, MAC2STR(bssid));
+ if (go_dev_addr) {
+ wpa_msg(p2p->cfg->msg_ctx, MSG_DEBUG,
+ "P2P: Invitation for GO Device Address " MACSTR,
+ MAC2STR(go_dev_addr));
+ os_memcpy(p2p->invite_go_dev_addr_buf, go_dev_addr, ETH_ALEN);
+ p2p->invite_go_dev_addr = p2p->invite_go_dev_addr_buf;
+ } else
+ p2p->invite_go_dev_addr = NULL;
+ wpa_hexdump_ascii(MSG_DEBUG, "P2P: Invitation for SSID",
+ ssid, ssid_len);
+
+ dev = p2p_get_device(p2p, peer);
+ if (dev == NULL || (dev->listen_freq <= 0 && dev->oper_freq <= 0)) {
+ wpa_msg(p2p->cfg->msg_ctx, MSG_DEBUG,
+ "P2P: Cannot invite unknown P2P Device " MACSTR,
+ MAC2STR(peer));
+ return -1;
+ }
+
+ if (dev->flags & P2P_DEV_GROUP_CLIENT_ONLY) {
+ if (!(dev->info.dev_capab &
+ P2P_DEV_CAPAB_CLIENT_DISCOVERABILITY)) {
+ wpa_msg(p2p->cfg->msg_ctx, MSG_DEBUG,
+ "P2P: Cannot invite a P2P Device " MACSTR
+ " that is in a group and is not discoverable",
+ MAC2STR(peer));
+ }
+ /* TODO: use device discoverability request through GO */
+ }
+
+ dev->invitation_reqs = 0;
+
+ if (force_freq) {
+ if (p2p_freq_to_channel(p2p->cfg->country, force_freq,
+ &p2p->op_reg_class, &p2p->op_channel) <
+ 0) {
+ wpa_msg(p2p->cfg->msg_ctx, MSG_DEBUG,
+ "P2P: Unsupported frequency %u MHz",
+ force_freq);
+ return -1;
+ }
+ p2p->channels.reg_classes = 1;
+ p2p->channels.reg_class[0].channels = 1;
+ p2p->channels.reg_class[0].reg_class = p2p->op_reg_class;
+ p2p->channels.reg_class[0].channel[0] = p2p->op_channel;
+ } else {
+ p2p->op_reg_class = p2p->cfg->op_reg_class;
+ p2p->op_channel = p2p->cfg->op_channel;
+ os_memcpy(&p2p->channels, &p2p->cfg->channels,
+ sizeof(struct p2p_channels));
+ }
+
+ if (p2p->state != P2P_IDLE)
+ p2p_stop_find(p2p);
+
+ p2p->inv_role = role;
+ p2p->inv_bssid_set = bssid != NULL;
+ if (bssid)
+ os_memcpy(p2p->inv_bssid, bssid, ETH_ALEN);
+ os_memcpy(p2p->inv_ssid, ssid, ssid_len);
+ p2p->inv_ssid_len = ssid_len;
+ p2p->inv_persistent = persistent_group;
+ return p2p_invite_send(p2p, dev, go_dev_addr);
+}
diff --git a/contrib/wpa/src/p2p/p2p_parse.c b/contrib/wpa/src/p2p/p2p_parse.c
new file mode 100644
index 0000000..097a31d
--- /dev/null
+++ b/contrib/wpa/src/p2p/p2p_parse.c
@@ -0,0 +1,723 @@
+/*
+ * P2P - IE parser
+ * Copyright (c) 2009-2010, Atheros Communications
+ *
+ * This software may be distributed under the terms of the BSD license.
+ * See README for more details.
+ */
+
+#include "includes.h"
+
+#include "common.h"
+#include "common/ieee802_11_defs.h"
+#include "common/ieee802_11_common.h"
+#include "wps/wps_i.h"
+#include "p2p_i.h"
+
+
+static int p2p_parse_attribute(u8 id, const u8 *data, u16 len,
+ struct p2p_message *msg)
+{
+ const u8 *pos;
+ size_t i, nlen;
+ char devtype[WPS_DEV_TYPE_BUFSIZE];
+
+ switch (id) {
+ case P2P_ATTR_CAPABILITY:
+ if (len < 2) {
+ wpa_printf(MSG_DEBUG, "P2P: Too short Capability "
+ "attribute (length %d)", len);
+ return -1;
+ }
+ msg->capability = data;
+ wpa_printf(MSG_DEBUG, "P2P: * Device Capability %02x "
+ "Group Capability %02x",
+ data[0], data[1]);
+ break;
+ case P2P_ATTR_DEVICE_ID:
+ if (len < ETH_ALEN) {
+ wpa_printf(MSG_DEBUG, "P2P: Too short Device ID "
+ "attribute (length %d)", len);
+ return -1;
+ }
+ msg->device_id = data;
+ wpa_printf(MSG_DEBUG, "P2P: * Device ID " MACSTR,
+ MAC2STR(msg->device_id));
+ break;
+ case P2P_ATTR_GROUP_OWNER_INTENT:
+ if (len < 1) {
+ wpa_printf(MSG_DEBUG, "P2P: Too short GO Intent "
+ "attribute (length %d)", len);
+ return -1;
+ }
+ msg->go_intent = data;
+ wpa_printf(MSG_DEBUG, "P2P: * GO Intent: Intent %u "
+ "Tie breaker %u", data[0] >> 1, data[0] & 0x01);
+ break;
+ case P2P_ATTR_STATUS:
+ if (len < 1) {
+ wpa_printf(MSG_DEBUG, "P2P: Too short Status "
+ "attribute (length %d)", len);
+ return -1;
+ }
+ msg->status = data;
+ wpa_printf(MSG_DEBUG, "P2P: * Status: %d", data[0]);
+ break;
+ case P2P_ATTR_LISTEN_CHANNEL:
+ if (len == 0) {
+ wpa_printf(MSG_DEBUG, "P2P: * Listen Channel: Ignore "
+ "null channel");
+ break;
+ }
+ if (len < 5) {
+ wpa_printf(MSG_DEBUG, "P2P: Too short Listen Channel "
+ "attribute (length %d)", len);
+ return -1;
+ }
+ msg->listen_channel = data;
+ wpa_printf(MSG_DEBUG, "P2P: * Listen Channel: "
+ "Country %c%c(0x%02x) Regulatory "
+ "Class %d Channel Number %d", data[0], data[1],
+ data[2], data[3], data[4]);
+ break;
+ case P2P_ATTR_OPERATING_CHANNEL:
+ if (len == 0) {
+ wpa_printf(MSG_DEBUG, "P2P: * Operating Channel: "
+ "Ignore null channel");
+ break;
+ }
+ if (len < 5) {
+ wpa_printf(MSG_DEBUG, "P2P: Too short Operating "
+ "Channel attribute (length %d)", len);
+ return -1;
+ }
+ msg->operating_channel = data;
+ wpa_printf(MSG_DEBUG, "P2P: * Operating Channel: "
+ "Country %c%c(0x%02x) Regulatory "
+ "Class %d Channel Number %d", data[0], data[1],
+ data[2], data[3], data[4]);
+ break;
+ case P2P_ATTR_CHANNEL_LIST:
+ if (len < 3) {
+ wpa_printf(MSG_DEBUG, "P2P: Too short Channel List "
+ "attribute (length %d)", len);
+ return -1;
+ }
+ msg->channel_list = data;
+ msg->channel_list_len = len;
+ wpa_printf(MSG_DEBUG, "P2P: * Channel List: Country String "
+ "'%c%c(0x%02x)'", data[0], data[1], data[2]);
+ wpa_hexdump(MSG_MSGDUMP, "P2P: Channel List",
+ msg->channel_list, msg->channel_list_len);
+ break;
+ case P2P_ATTR_GROUP_INFO:
+ msg->group_info = data;
+ msg->group_info_len = len;
+ wpa_printf(MSG_DEBUG, "P2P: * Group Info");
+ break;
+ case P2P_ATTR_DEVICE_INFO:
+ if (len < ETH_ALEN + 2 + 8 + 1) {
+ wpa_printf(MSG_DEBUG, "P2P: Too short Device Info "
+ "attribute (length %d)", len);
+ return -1;
+ }
+ msg->p2p_device_info = data;
+ msg->p2p_device_info_len = len;
+ pos = data;
+ msg->p2p_device_addr = pos;
+ pos += ETH_ALEN;
+ msg->config_methods = WPA_GET_BE16(pos);
+ pos += 2;
+ msg->pri_dev_type = pos;
+ pos += 8;
+ msg->num_sec_dev_types = *pos++;
+ if (msg->num_sec_dev_types * 8 > data + len - pos) {
+ wpa_printf(MSG_DEBUG, "P2P: Device Info underflow");
+ return -1;
+ }
+ pos += msg->num_sec_dev_types * 8;
+ if (data + len - pos < 4) {
+ wpa_printf(MSG_DEBUG, "P2P: Invalid Device Name "
+ "length %d", (int) (data + len - pos));
+ return -1;
+ }
+ if (WPA_GET_BE16(pos) != ATTR_DEV_NAME) {
+ wpa_hexdump(MSG_DEBUG, "P2P: Unexpected Device Name "
+ "header", pos, 4);
+ return -1;
+ }
+ pos += 2;
+ nlen = WPA_GET_BE16(pos);
+ pos += 2;
+ if (data + len - pos < (int) nlen || nlen > 32) {
+ wpa_printf(MSG_DEBUG, "P2P: Invalid Device Name "
+ "length %d (buf len %d)", (int) nlen,
+ (int) (data + len - pos));
+ return -1;
+ }
+ os_memcpy(msg->device_name, pos, nlen);
+ msg->device_name[nlen] = '\0';
+ for (i = 0; i < nlen; i++) {
+ if (msg->device_name[i] == '\0')
+ break;
+ if (msg->device_name[i] > 0 &&
+ msg->device_name[i] < 32)
+ msg->device_name[i] = '_';
+ }
+ wpa_printf(MSG_DEBUG, "P2P: * Device Info: addr " MACSTR
+ " primary device type %s device name '%s' "
+ "config methods 0x%x",
+ MAC2STR(msg->p2p_device_addr),
+ wps_dev_type_bin2str(msg->pri_dev_type, devtype,
+ sizeof(devtype)),
+ msg->device_name, msg->config_methods);
+ break;
+ case P2P_ATTR_CONFIGURATION_TIMEOUT:
+ if (len < 2) {
+ wpa_printf(MSG_DEBUG, "P2P: Too short Configuration "
+ "Timeout attribute (length %d)", len);
+ return -1;
+ }
+ msg->config_timeout = data;
+ wpa_printf(MSG_DEBUG, "P2P: * Configuration Timeout");
+ break;
+ case P2P_ATTR_INTENDED_INTERFACE_ADDR:
+ if (len < ETH_ALEN) {
+ wpa_printf(MSG_DEBUG, "P2P: Too short Intended P2P "
+ "Interface Address attribute (length %d)",
+ len);
+ return -1;
+ }
+ msg->intended_addr = data;
+ wpa_printf(MSG_DEBUG, "P2P: * Intended P2P Interface Address: "
+ MACSTR, MAC2STR(msg->intended_addr));
+ break;
+ case P2P_ATTR_GROUP_BSSID:
+ if (len < ETH_ALEN) {
+ wpa_printf(MSG_DEBUG, "P2P: Too short P2P Group BSSID "
+ "attribute (length %d)", len);
+ return -1;
+ }
+ msg->group_bssid = data;
+ wpa_printf(MSG_DEBUG, "P2P: * P2P Group BSSID: " MACSTR,
+ MAC2STR(msg->group_bssid));
+ break;
+ case P2P_ATTR_GROUP_ID:
+ if (len < ETH_ALEN || len > ETH_ALEN + 32) {
+ wpa_printf(MSG_DEBUG, "P2P: Invalid P2P Group ID "
+ "attribute length %d", len);
+ return -1;
+ }
+ msg->group_id = data;
+ msg->group_id_len = len;
+ wpa_printf(MSG_DEBUG, "P2P: * P2P Group ID: Device Address "
+ MACSTR, MAC2STR(msg->group_id));
+ wpa_hexdump_ascii(MSG_DEBUG, "P2P: * P2P Group ID: SSID",
+ msg->group_id + ETH_ALEN,
+ msg->group_id_len - ETH_ALEN);
+ break;
+ case P2P_ATTR_INVITATION_FLAGS:
+ if (len < 1) {
+ wpa_printf(MSG_DEBUG, "P2P: Too short Invitation "
+ "Flag attribute (length %d)", len);
+ return -1;
+ }
+ msg->invitation_flags = data;
+ wpa_printf(MSG_DEBUG, "P2P: * Invitation Flags: bitmap 0x%x",
+ data[0]);
+ break;
+ case P2P_ATTR_MANAGEABILITY:
+ if (len < 1) {
+ wpa_printf(MSG_DEBUG, "P2P: Too short Manageability "
+ "attribute (length %d)", len);
+ return -1;
+ }
+ msg->manageability = data;
+ wpa_printf(MSG_DEBUG, "P2P: * Manageability: bitmap 0x%x",
+ data[0]);
+ break;
+ case P2P_ATTR_NOTICE_OF_ABSENCE:
+ if (len < 2) {
+ wpa_printf(MSG_DEBUG, "P2P: Too short Notice of "
+ "Absence attribute (length %d)", len);
+ return -1;
+ }
+ msg->noa = data;
+ msg->noa_len = len;
+ wpa_printf(MSG_DEBUG, "P2P: * Notice of Absence");
+ break;
+ case P2P_ATTR_EXT_LISTEN_TIMING:
+ if (len < 4) {
+ wpa_printf(MSG_DEBUG, "P2P: Too short Extended Listen "
+ "Timing attribute (length %d)", len);
+ return -1;
+ }
+ msg->ext_listen_timing = data;
+ wpa_printf(MSG_DEBUG, "P2P: * Extended Listen Timing "
+ "(period %u msec interval %u msec)",
+ WPA_GET_LE16(msg->ext_listen_timing),
+ WPA_GET_LE16(msg->ext_listen_timing + 2));
+ break;
+ case P2P_ATTR_MINOR_REASON_CODE:
+ if (len < 1) {
+ wpa_printf(MSG_DEBUG, "P2P: Too short Minor Reason "
+ "Code attribute (length %d)", len);
+ return -1;
+ }
+ msg->minor_reason_code = data;
+ wpa_printf(MSG_DEBUG, "P2P: * Minor Reason Code: %u",
+ *msg->minor_reason_code);
+ break;
+ default:
+ wpa_printf(MSG_DEBUG, "P2P: Skipped unknown attribute %d "
+ "(length %d)", id, len);
+ break;
+ }
+
+ return 0;
+}
+
+
+/**
+ * p2p_parse_p2p_ie - Parse P2P IE
+ * @buf: Concatenated P2P IE(s) payload
+ * @msg: Buffer for returning parsed attributes
+ * Returns: 0 on success, -1 on failure
+ *
+ * Note: Caller is responsible for clearing the msg data structure before
+ * calling this function.
+ */
+int p2p_parse_p2p_ie(const struct wpabuf *buf, struct p2p_message *msg)
+{
+ const u8 *pos = wpabuf_head_u8(buf);
+ const u8 *end = pos + wpabuf_len(buf);
+
+ wpa_printf(MSG_DEBUG, "P2P: Parsing P2P IE");
+
+ while (pos < end) {
+ u16 attr_len;
+ if (pos + 2 >= end) {
+ wpa_printf(MSG_DEBUG, "P2P: Invalid P2P attribute");
+ return -1;
+ }
+ attr_len = WPA_GET_LE16(pos + 1);
+ wpa_printf(MSG_DEBUG, "P2P: Attribute %d length %u",
+ pos[0], attr_len);
+ if (pos + 3 + attr_len > end) {
+ wpa_printf(MSG_DEBUG, "P2P: Attribute underflow "
+ "(len=%u left=%d)",
+ attr_len, (int) (end - pos - 3));
+ wpa_hexdump(MSG_MSGDUMP, "P2P: Data", pos, end - pos);
+ return -1;
+ }
+ if (p2p_parse_attribute(pos[0], pos + 3, attr_len, msg))
+ return -1;
+ pos += 3 + attr_len;
+ }
+
+ return 0;
+}
+
+
+static int p2p_parse_wps_ie(const struct wpabuf *buf, struct p2p_message *msg)
+{
+ struct wps_parse_attr attr;
+ int i;
+
+ wpa_printf(MSG_DEBUG, "P2P: Parsing WPS IE");
+ if (wps_parse_msg(buf, &attr))
+ return -1;
+ if (attr.dev_name && attr.dev_name_len < sizeof(msg->device_name) &&
+ !msg->device_name[0])
+ os_memcpy(msg->device_name, attr.dev_name, attr.dev_name_len);
+ if (attr.config_methods) {
+ msg->wps_config_methods =
+ WPA_GET_BE16(attr.config_methods);
+ wpa_printf(MSG_DEBUG, "P2P: Config Methods (WPS): 0x%x",
+ msg->wps_config_methods);
+ }
+ if (attr.dev_password_id) {
+ msg->dev_password_id = WPA_GET_BE16(attr.dev_password_id);
+ wpa_printf(MSG_DEBUG, "P2P: Device Password ID: %d",
+ msg->dev_password_id);
+ }
+ if (attr.primary_dev_type) {
+ char devtype[WPS_DEV_TYPE_BUFSIZE];
+ msg->wps_pri_dev_type = attr.primary_dev_type;
+ wpa_printf(MSG_DEBUG, "P2P: Primary Device Type (WPS): %s",
+ wps_dev_type_bin2str(msg->wps_pri_dev_type, devtype,
+ sizeof(devtype)));
+ }
+ if (attr.sec_dev_type_list) {
+ msg->wps_sec_dev_type_list = attr.sec_dev_type_list;
+ msg->wps_sec_dev_type_list_len = attr.sec_dev_type_list_len;
+ }
+
+ for (i = 0; i < P2P_MAX_WPS_VENDOR_EXT; i++) {
+ msg->wps_vendor_ext[i] = attr.vendor_ext[i];
+ msg->wps_vendor_ext_len[i] = attr.vendor_ext_len[i];
+ }
+
+ msg->manufacturer = attr.manufacturer;
+ msg->manufacturer_len = attr.manufacturer_len;
+ msg->model_name = attr.model_name;
+ msg->model_name_len = attr.model_name_len;
+ msg->model_number = attr.model_number;
+ msg->model_number_len = attr.model_number_len;
+ msg->serial_number = attr.serial_number;
+ msg->serial_number_len = attr.serial_number_len;
+
+ return 0;
+}
+
+
+/**
+ * p2p_parse_ies - Parse P2P message IEs (both WPS and P2P IE)
+ * @data: IEs from the message
+ * @len: Length of data buffer in octets
+ * @msg: Buffer for returning parsed attributes
+ * Returns: 0 on success, -1 on failure
+ *
+ * Note: Caller is responsible for clearing the msg data structure before
+ * calling this function.
+ *
+ * Note: Caller must free temporary memory allocations by calling
+ * p2p_parse_free() when the parsed data is not needed anymore.
+ */
+int p2p_parse_ies(const u8 *data, size_t len, struct p2p_message *msg)
+{
+ struct ieee802_11_elems elems;
+
+ ieee802_11_parse_elems(data, len, &elems, 0);
+ if (elems.ds_params && elems.ds_params_len >= 1)
+ msg->ds_params = elems.ds_params;
+ if (elems.ssid)
+ msg->ssid = elems.ssid - 2;
+
+ msg->wps_attributes = ieee802_11_vendor_ie_concat(data, len,
+ WPS_DEV_OUI_WFA);
+ if (msg->wps_attributes &&
+ p2p_parse_wps_ie(msg->wps_attributes, msg)) {
+ p2p_parse_free(msg);
+ return -1;
+ }
+
+ msg->p2p_attributes = ieee802_11_vendor_ie_concat(data, len,
+ P2P_IE_VENDOR_TYPE);
+ if (msg->p2p_attributes &&
+ p2p_parse_p2p_ie(msg->p2p_attributes, msg)) {
+ wpa_printf(MSG_DEBUG, "P2P: Failed to parse P2P IE data");
+ if (msg->p2p_attributes)
+ wpa_hexdump_buf(MSG_MSGDUMP, "P2P: P2P IE data",
+ msg->p2p_attributes);
+ p2p_parse_free(msg);
+ return -1;
+ }
+
+#ifdef CONFIG_WIFI_DISPLAY
+ if (elems.wfd) {
+ msg->wfd_subelems = ieee802_11_vendor_ie_concat(
+ data, len, WFD_IE_VENDOR_TYPE);
+ }
+#endif /* CONFIG_WIFI_DISPLAY */
+
+ return 0;
+}
+
+
+/**
+ * p2p_parse - Parse a P2P Action frame contents
+ * @data: Action frame payload after Category and Code fields
+ * @len: Length of data buffer in octets
+ * @msg: Buffer for returning parsed attributes
+ * Returns: 0 on success, -1 on failure
+ *
+ * Note: Caller must free temporary memory allocations by calling
+ * p2p_parse_free() when the parsed data is not needed anymore.
+ */
+int p2p_parse(const u8 *data, size_t len, struct p2p_message *msg)
+{
+ os_memset(msg, 0, sizeof(*msg));
+ wpa_printf(MSG_DEBUG, "P2P: Parsing the received message");
+ if (len < 1) {
+ wpa_printf(MSG_DEBUG, "P2P: No Dialog Token in the message");
+ return -1;
+ }
+ msg->dialog_token = data[0];
+ wpa_printf(MSG_DEBUG, "P2P: * Dialog Token: %d", msg->dialog_token);
+
+ return p2p_parse_ies(data + 1, len - 1, msg);
+}
+
+
+/**
+ * p2p_parse_free - Free temporary data from P2P parsing
+ * @msg: Parsed attributes
+ */
+void p2p_parse_free(struct p2p_message *msg)
+{
+ wpabuf_free(msg->p2p_attributes);
+ msg->p2p_attributes = NULL;
+ wpabuf_free(msg->wps_attributes);
+ msg->wps_attributes = NULL;
+#ifdef CONFIG_WIFI_DISPLAY
+ wpabuf_free(msg->wfd_subelems);
+ msg->wfd_subelems = NULL;
+#endif /* CONFIG_WIFI_DISPLAY */
+}
+
+
+int p2p_group_info_parse(const u8 *gi, size_t gi_len,
+ struct p2p_group_info *info)
+{
+ const u8 *g, *gend;
+
+ os_memset(info, 0, sizeof(*info));
+ if (gi == NULL)
+ return 0;
+
+ g = gi;
+ gend = gi + gi_len;
+ while (g < gend) {
+ struct p2p_client_info *cli;
+ const u8 *t, *cend;
+ int count;
+
+ cli = &info->client[info->num_clients];
+ cend = g + 1 + g[0];
+ if (cend > gend)
+ return -1; /* invalid data */
+ /* g at start of P2P Client Info Descriptor */
+ /* t at Device Capability Bitmap */
+ t = g + 1 + 2 * ETH_ALEN;
+ if (t > cend)
+ return -1; /* invalid data */
+ cli->p2p_device_addr = g + 1;
+ cli->p2p_interface_addr = g + 1 + ETH_ALEN;
+ cli->dev_capab = t[0];
+
+ if (t + 1 + 2 + 8 + 1 > cend)
+ return -1; /* invalid data */
+
+ cli->config_methods = WPA_GET_BE16(&t[1]);
+ cli->pri_dev_type = &t[3];
+
+ t += 1 + 2 + 8;
+ /* t at Number of Secondary Device Types */
+ cli->num_sec_dev_types = *t++;
+ if (t + 8 * cli->num_sec_dev_types > cend)
+ return -1; /* invalid data */
+ cli->sec_dev_types = t;
+ t += 8 * cli->num_sec_dev_types;
+
+ /* t at Device Name in WPS TLV format */
+ if (t + 2 + 2 > cend)
+ return -1; /* invalid data */
+ if (WPA_GET_BE16(t) != ATTR_DEV_NAME)
+ return -1; /* invalid Device Name TLV */
+ t += 2;
+ count = WPA_GET_BE16(t);
+ t += 2;
+ if (count > cend - t)
+ return -1; /* invalid Device Name TLV */
+ if (count >= 32)
+ count = 32;
+ cli->dev_name = (const char *) t;
+ cli->dev_name_len = count;
+
+ g = cend;
+
+ info->num_clients++;
+ if (info->num_clients == P2P_MAX_GROUP_ENTRIES)
+ return -1;
+ }
+
+ return 0;
+}
+
+
+static int p2p_group_info_text(const u8 *gi, size_t gi_len, char *buf,
+ char *end)
+{
+ char *pos = buf;
+ int ret;
+ struct p2p_group_info info;
+ unsigned int i;
+
+ if (p2p_group_info_parse(gi, gi_len, &info) < 0)
+ return 0;
+
+ for (i = 0; i < info.num_clients; i++) {
+ struct p2p_client_info *cli;
+ char name[33];
+ char devtype[WPS_DEV_TYPE_BUFSIZE];
+ u8 s;
+ int count;
+
+ cli = &info.client[i];
+ ret = os_snprintf(pos, end - pos, "p2p_group_client: "
+ "dev=" MACSTR " iface=" MACSTR,
+ MAC2STR(cli->p2p_device_addr),
+ MAC2STR(cli->p2p_interface_addr));
+ if (ret < 0 || ret >= end - pos)
+ return pos - buf;
+ pos += ret;
+
+ ret = os_snprintf(pos, end - pos,
+ " dev_capab=0x%x config_methods=0x%x "
+ "dev_type=%s",
+ cli->dev_capab, cli->config_methods,
+ wps_dev_type_bin2str(cli->pri_dev_type,
+ devtype,
+ sizeof(devtype)));
+ if (ret < 0 || ret >= end - pos)
+ return pos - buf;
+ pos += ret;
+
+ for (s = 0; s < cli->num_sec_dev_types; s++) {
+ ret = os_snprintf(pos, end - pos, " dev_type=%s",
+ wps_dev_type_bin2str(
+ &cli->sec_dev_types[s * 8],
+ devtype, sizeof(devtype)));
+ if (ret < 0 || ret >= end - pos)
+ return pos - buf;
+ pos += ret;
+ }
+
+ os_memcpy(name, cli->dev_name, cli->dev_name_len);
+ name[cli->dev_name_len] = '\0';
+ count = (int) cli->dev_name_len - 1;
+ while (count >= 0) {
+ if (name[count] > 0 && name[count] < 32)
+ name[count] = '_';
+ count--;
+ }
+
+ ret = os_snprintf(pos, end - pos, " dev_name='%s'\n", name);
+ if (ret < 0 || ret >= end - pos)
+ return pos - buf;
+ pos += ret;
+ }
+
+ return pos - buf;
+}
+
+
+/**
+ * p2p_attr_text - Build text format description of P2P IE attributes
+ * @data: P2P IE contents
+ * @buf: Buffer for returning text
+ * @end: Pointer to the end of the buf area
+ * Returns: Number of octets written to the buffer or -1 on faikure
+ *
+ * This function can be used to parse P2P IE contents into text format
+ * field=value lines.
+ */
+int p2p_attr_text(struct wpabuf *data, char *buf, char *end)
+{
+ struct p2p_message msg;
+ char *pos = buf;
+ int ret;
+
+ os_memset(&msg, 0, sizeof(msg));
+ if (p2p_parse_p2p_ie(data, &msg))
+ return -1;
+
+ if (msg.capability) {
+ ret = os_snprintf(pos, end - pos,
+ "p2p_dev_capab=0x%x\n"
+ "p2p_group_capab=0x%x\n",
+ msg.capability[0], msg.capability[1]);
+ if (ret < 0 || ret >= end - pos)
+ return pos - buf;
+ pos += ret;
+ }
+
+ if (msg.pri_dev_type) {
+ char devtype[WPS_DEV_TYPE_BUFSIZE];
+ ret = os_snprintf(pos, end - pos,
+ "p2p_primary_device_type=%s\n",
+ wps_dev_type_bin2str(msg.pri_dev_type,
+ devtype,
+ sizeof(devtype)));
+ if (ret < 0 || ret >= end - pos)
+ return pos - buf;
+ pos += ret;
+ }
+
+ ret = os_snprintf(pos, end - pos, "p2p_device_name=%s\n",
+ msg.device_name);
+ if (ret < 0 || ret >= end - pos)
+ return pos - buf;
+ pos += ret;
+
+ if (msg.p2p_device_addr) {
+ ret = os_snprintf(pos, end - pos, "p2p_device_addr=" MACSTR
+ "\n",
+ MAC2STR(msg.p2p_device_addr));
+ if (ret < 0 || ret >= end - pos)
+ return pos - buf;
+ pos += ret;
+ }
+
+ ret = os_snprintf(pos, end - pos, "p2p_config_methods=0x%x\n",
+ msg.config_methods);
+ if (ret < 0 || ret >= end - pos)
+ return pos - buf;
+ pos += ret;
+
+ ret = p2p_group_info_text(msg.group_info, msg.group_info_len,
+ pos, end);
+ if (ret < 0)
+ return pos - buf;
+ pos += ret;
+
+ return pos - buf;
+}
+
+
+int p2p_get_cross_connect_disallowed(const struct wpabuf *p2p_ie)
+{
+ struct p2p_message msg;
+
+ os_memset(&msg, 0, sizeof(msg));
+ if (p2p_parse_p2p_ie(p2p_ie, &msg))
+ return 0;
+
+ if (!msg.manageability)
+ return 0;
+
+ return !(msg.manageability[0] & P2P_MAN_CROSS_CONNECTION_PERMITTED);
+}
+
+
+u8 p2p_get_group_capab(const struct wpabuf *p2p_ie)
+{
+ struct p2p_message msg;
+
+ os_memset(&msg, 0, sizeof(msg));
+ if (p2p_parse_p2p_ie(p2p_ie, &msg))
+ return 0;
+
+ if (!msg.capability)
+ return 0;
+
+ return msg.capability[1];
+}
+
+
+const u8 * p2p_get_go_dev_addr(const struct wpabuf *p2p_ie)
+{
+ struct p2p_message msg;
+
+ os_memset(&msg, 0, sizeof(msg));
+ if (p2p_parse_p2p_ie(p2p_ie, &msg))
+ return NULL;
+
+ if (msg.p2p_device_addr)
+ return msg.p2p_device_addr;
+ if (msg.device_id)
+ return msg.device_id;
+
+ return NULL;
+}
diff --git a/contrib/wpa/src/p2p/p2p_pd.c b/contrib/wpa/src/p2p/p2p_pd.c
new file mode 100644
index 0000000..ca33f17
--- /dev/null
+++ b/contrib/wpa/src/p2p/p2p_pd.c
@@ -0,0 +1,484 @@
+/*
+ * Wi-Fi Direct - P2P provision discovery
+ * Copyright (c) 2009-2010, Atheros Communications
+ *
+ * This software may be distributed under the terms of the BSD license.
+ * See README for more details.
+ */
+
+#include "includes.h"
+
+#include "common.h"
+#include "common/ieee802_11_defs.h"
+#include "wps/wps_defs.h"
+#include "p2p_i.h"
+#include "p2p.h"
+
+
+/*
+ * Number of retries to attempt for provision discovery requests
+ * in case the peer is not listening.
+ */
+#define MAX_PROV_DISC_REQ_RETRIES 120
+
+
+static void p2p_build_wps_ie_config_methods(struct wpabuf *buf,
+ u16 config_methods)
+{
+ u8 *len;
+ wpabuf_put_u8(buf, WLAN_EID_VENDOR_SPECIFIC);
+ len = wpabuf_put(buf, 1);
+ wpabuf_put_be32(buf, WPS_DEV_OUI_WFA);
+
+ /* Config Methods */
+ wpabuf_put_be16(buf, ATTR_CONFIG_METHODS);
+ wpabuf_put_be16(buf, 2);
+ wpabuf_put_be16(buf, config_methods);
+
+ p2p_buf_update_ie_hdr(buf, len);
+}
+
+
+static struct wpabuf * p2p_build_prov_disc_req(struct p2p_data *p2p,
+ u8 dialog_token,
+ u16 config_methods,
+ struct p2p_device *go)
+{
+ struct wpabuf *buf;
+ u8 *len;
+ size_t extra = 0;
+
+#ifdef CONFIG_WIFI_DISPLAY
+ if (p2p->wfd_ie_prov_disc_req)
+ extra = wpabuf_len(p2p->wfd_ie_prov_disc_req);
+#endif /* CONFIG_WIFI_DISPLAY */
+
+ buf = wpabuf_alloc(1000 + extra);
+ if (buf == NULL)
+ return NULL;
+
+ p2p_buf_add_public_action_hdr(buf, P2P_PROV_DISC_REQ, dialog_token);
+
+ len = p2p_buf_add_ie_hdr(buf);
+ p2p_buf_add_capability(buf, p2p->dev_capab &
+ ~P2P_DEV_CAPAB_CLIENT_DISCOVERABILITY, 0);
+ p2p_buf_add_device_info(buf, p2p, NULL);
+ if (go) {
+ p2p_buf_add_group_id(buf, go->info.p2p_device_addr,
+ go->oper_ssid, go->oper_ssid_len);
+ }
+ p2p_buf_update_ie_hdr(buf, len);
+
+ /* WPS IE with Config Methods attribute */
+ p2p_build_wps_ie_config_methods(buf, config_methods);
+
+#ifdef CONFIG_WIFI_DISPLAY
+ if (p2p->wfd_ie_prov_disc_req)
+ wpabuf_put_buf(buf, p2p->wfd_ie_prov_disc_req);
+#endif /* CONFIG_WIFI_DISPLAY */
+
+ return buf;
+}
+
+
+static struct wpabuf * p2p_build_prov_disc_resp(struct p2p_data *p2p,
+ u8 dialog_token,
+ u16 config_methods,
+ const u8 *group_id,
+ size_t group_id_len)
+{
+ struct wpabuf *buf;
+ size_t extra = 0;
+
+#ifdef CONFIG_WIFI_DISPLAY
+ struct wpabuf *wfd_ie = p2p->wfd_ie_prov_disc_resp;
+ if (wfd_ie && group_id) {
+ size_t i;
+ for (i = 0; i < p2p->num_groups; i++) {
+ struct p2p_group *g = p2p->groups[i];
+ struct wpabuf *ie;
+ if (!p2p_group_is_group_id_match(g, group_id,
+ group_id_len))
+ continue;
+ ie = p2p_group_get_wfd_ie(g);
+ if (ie) {
+ wfd_ie = ie;
+ break;
+ }
+ }
+ }
+ if (wfd_ie)
+ extra = wpabuf_len(wfd_ie);
+#endif /* CONFIG_WIFI_DISPLAY */
+
+ buf = wpabuf_alloc(100 + extra);
+ if (buf == NULL)
+ return NULL;
+
+ p2p_buf_add_public_action_hdr(buf, P2P_PROV_DISC_RESP, dialog_token);
+
+ /* WPS IE with Config Methods attribute */
+ p2p_build_wps_ie_config_methods(buf, config_methods);
+
+#ifdef CONFIG_WIFI_DISPLAY
+ if (wfd_ie)
+ wpabuf_put_buf(buf, wfd_ie);
+#endif /* CONFIG_WIFI_DISPLAY */
+
+ return buf;
+}
+
+
+void p2p_process_prov_disc_req(struct p2p_data *p2p, const u8 *sa,
+ const u8 *data, size_t len, int rx_freq)
+{
+ struct p2p_message msg;
+ struct p2p_device *dev;
+ int freq;
+ int reject = 1;
+ struct wpabuf *resp;
+
+ if (p2p_parse(data, len, &msg))
+ return;
+
+ wpa_msg(p2p->cfg->msg_ctx, MSG_DEBUG,
+ "P2P: Received Provision Discovery Request from " MACSTR
+ " with config methods 0x%x (freq=%d)",
+ MAC2STR(sa), msg.wps_config_methods, rx_freq);
+
+ dev = p2p_get_device(p2p, sa);
+ if (dev == NULL || (dev->flags & P2P_DEV_PROBE_REQ_ONLY)) {
+ wpa_msg(p2p->cfg->msg_ctx, MSG_DEBUG,
+ "P2P: Provision Discovery Request from "
+ "unknown peer " MACSTR, MAC2STR(sa));
+
+ if (p2p_add_device(p2p, sa, rx_freq, 0, 0, data + 1, len - 1,
+ 0)) {
+ wpa_msg(p2p->cfg->msg_ctx, MSG_DEBUG,
+ "P2P: Provision Discovery Request add device "
+ "failed " MACSTR, MAC2STR(sa));
+ }
+ } else if (msg.wfd_subelems) {
+ wpabuf_free(dev->info.wfd_subelems);
+ dev->info.wfd_subelems = wpabuf_dup(msg.wfd_subelems);
+ }
+
+ if (!(msg.wps_config_methods &
+ (WPS_CONFIG_DISPLAY | WPS_CONFIG_KEYPAD |
+ WPS_CONFIG_PUSHBUTTON))) {
+ wpa_msg(p2p->cfg->msg_ctx, MSG_DEBUG, "P2P: Unsupported "
+ "Config Methods in Provision Discovery Request");
+ goto out;
+ }
+
+ if (msg.group_id) {
+ size_t i;
+ for (i = 0; i < p2p->num_groups; i++) {
+ if (p2p_group_is_group_id_match(p2p->groups[i],
+ msg.group_id,
+ msg.group_id_len))
+ break;
+ }
+ if (i == p2p->num_groups) {
+ wpa_msg(p2p->cfg->msg_ctx, MSG_DEBUG, "P2P: PD "
+ "request for unknown P2P Group ID - reject");
+ goto out;
+ }
+ }
+
+ if (dev)
+ dev->flags &= ~(P2P_DEV_PD_PEER_DISPLAY |
+ P2P_DEV_PD_PEER_KEYPAD);
+ if (msg.wps_config_methods & WPS_CONFIG_DISPLAY) {
+ wpa_msg(p2p->cfg->msg_ctx, MSG_DEBUG, "P2P: Peer " MACSTR
+ " requested us to show a PIN on display", MAC2STR(sa));
+ if (dev)
+ dev->flags |= P2P_DEV_PD_PEER_KEYPAD;
+ } else if (msg.wps_config_methods & WPS_CONFIG_KEYPAD) {
+ wpa_msg(p2p->cfg->msg_ctx, MSG_DEBUG, "P2P: Peer " MACSTR
+ " requested us to write its PIN using keypad",
+ MAC2STR(sa));
+ if (dev)
+ dev->flags |= P2P_DEV_PD_PEER_DISPLAY;
+ }
+
+ reject = 0;
+
+out:
+ resp = p2p_build_prov_disc_resp(p2p, msg.dialog_token,
+ reject ? 0 : msg.wps_config_methods,
+ msg.group_id, msg.group_id_len);
+ if (resp == NULL) {
+ p2p_parse_free(&msg);
+ return;
+ }
+ wpa_msg(p2p->cfg->msg_ctx, MSG_DEBUG,
+ "P2P: Sending Provision Discovery Response");
+ if (rx_freq > 0)
+ freq = rx_freq;
+ else
+ freq = p2p_channel_to_freq(p2p->cfg->country,
+ p2p->cfg->reg_class,
+ p2p->cfg->channel);
+ if (freq < 0) {
+ wpa_msg(p2p->cfg->msg_ctx, MSG_DEBUG,
+ "P2P: Unknown regulatory class/channel");
+ wpabuf_free(resp);
+ p2p_parse_free(&msg);
+ return;
+ }
+ p2p->pending_action_state = P2P_NO_PENDING_ACTION;
+ if (p2p_send_action(p2p, freq, sa, p2p->cfg->dev_addr,
+ p2p->cfg->dev_addr,
+ wpabuf_head(resp), wpabuf_len(resp), 200) < 0) {
+ wpa_msg(p2p->cfg->msg_ctx, MSG_DEBUG,
+ "P2P: Failed to send Action frame");
+ }
+
+ wpabuf_free(resp);
+
+ if (!reject && p2p->cfg->prov_disc_req) {
+ const u8 *dev_addr = sa;
+ if (msg.p2p_device_addr)
+ dev_addr = msg.p2p_device_addr;
+ p2p->cfg->prov_disc_req(p2p->cfg->cb_ctx, sa,
+ msg.wps_config_methods,
+ dev_addr, msg.pri_dev_type,
+ msg.device_name, msg.config_methods,
+ msg.capability ? msg.capability[0] : 0,
+ msg.capability ? msg.capability[1] :
+ 0,
+ msg.group_id, msg.group_id_len);
+ }
+ p2p_parse_free(&msg);
+}
+
+
+void p2p_process_prov_disc_resp(struct p2p_data *p2p, const u8 *sa,
+ const u8 *data, size_t len)
+{
+ struct p2p_message msg;
+ struct p2p_device *dev;
+ u16 report_config_methods = 0;
+ int success = 0;
+
+ if (p2p_parse(data, len, &msg))
+ return;
+
+ wpa_msg(p2p->cfg->msg_ctx, MSG_DEBUG,
+ "P2P: Received Provision Discovery Response from " MACSTR
+ " with config methods 0x%x",
+ MAC2STR(sa), msg.wps_config_methods);
+
+ dev = p2p_get_device(p2p, sa);
+ if (dev == NULL || !dev->req_config_methods) {
+ wpa_msg(p2p->cfg->msg_ctx, MSG_DEBUG,
+ "P2P: Ignore Provision Discovery Response from "
+ MACSTR " with no pending request", MAC2STR(sa));
+ p2p_parse_free(&msg);
+ return;
+ }
+
+ if (dev->dialog_token != msg.dialog_token) {
+ wpa_msg(p2p->cfg->msg_ctx, MSG_DEBUG,
+ "P2P: Ignore Provision Discovery Response with "
+ "unexpected Dialog Token %u (expected %u)",
+ msg.dialog_token, dev->dialog_token);
+ p2p_parse_free(&msg);
+ return;
+ }
+
+ if (p2p->pending_action_state == P2P_PENDING_PD) {
+ os_memset(p2p->pending_pd_devaddr, 0, ETH_ALEN);
+ p2p->pending_action_state = P2P_NO_PENDING_ACTION;
+ }
+
+ /*
+ * If the response is from the peer to whom a user initiated request
+ * was sent earlier, we reset that state info here.
+ */
+ if (p2p->user_initiated_pd &&
+ os_memcmp(p2p->pending_pd_devaddr, sa, ETH_ALEN) == 0)
+ p2p_reset_pending_pd(p2p);
+
+ if (msg.wps_config_methods != dev->req_config_methods) {
+ wpa_msg(p2p->cfg->msg_ctx, MSG_DEBUG, "P2P: Peer rejected "
+ "our Provision Discovery Request");
+ if (p2p->cfg->prov_disc_fail)
+ p2p->cfg->prov_disc_fail(p2p->cfg->cb_ctx, sa,
+ P2P_PROV_DISC_REJECTED);
+ p2p_parse_free(&msg);
+ goto out;
+ }
+
+ report_config_methods = dev->req_config_methods;
+ dev->flags &= ~(P2P_DEV_PD_PEER_DISPLAY |
+ P2P_DEV_PD_PEER_KEYPAD);
+ if (dev->req_config_methods & WPS_CONFIG_DISPLAY) {
+ wpa_msg(p2p->cfg->msg_ctx, MSG_DEBUG, "P2P: Peer " MACSTR
+ " accepted to show a PIN on display", MAC2STR(sa));
+ dev->flags |= P2P_DEV_PD_PEER_DISPLAY;
+ } else if (msg.wps_config_methods & WPS_CONFIG_KEYPAD) {
+ wpa_msg(p2p->cfg->msg_ctx, MSG_DEBUG, "P2P: Peer " MACSTR
+ " accepted to write our PIN using keypad",
+ MAC2STR(sa));
+ dev->flags |= P2P_DEV_PD_PEER_KEYPAD;
+ }
+
+ /* Store the provisioning info */
+ dev->wps_prov_info = msg.wps_config_methods;
+
+ p2p_parse_free(&msg);
+ success = 1;
+
+out:
+ dev->req_config_methods = 0;
+ p2p->cfg->send_action_done(p2p->cfg->cb_ctx);
+ if (dev->flags & P2P_DEV_PD_BEFORE_GO_NEG) {
+ wpa_msg(p2p->cfg->msg_ctx, MSG_DEBUG,
+ "P2P: Start GO Neg after the PD-before-GO-Neg "
+ "workaround with " MACSTR,
+ MAC2STR(dev->info.p2p_device_addr));
+ dev->flags &= ~P2P_DEV_PD_BEFORE_GO_NEG;
+ p2p_connect_send(p2p, dev);
+ return;
+ }
+ if (success && p2p->cfg->prov_disc_resp)
+ p2p->cfg->prov_disc_resp(p2p->cfg->cb_ctx, sa,
+ report_config_methods);
+}
+
+
+int p2p_send_prov_disc_req(struct p2p_data *p2p, struct p2p_device *dev,
+ int join, int force_freq)
+{
+ struct wpabuf *req;
+ int freq;
+
+ if (force_freq > 0)
+ freq = force_freq;
+ else
+ freq = dev->listen_freq > 0 ? dev->listen_freq :
+ dev->oper_freq;
+ if (freq <= 0) {
+ wpa_msg(p2p->cfg->msg_ctx, MSG_DEBUG,
+ "P2P: No Listen/Operating frequency known for the "
+ "peer " MACSTR " to send Provision Discovery Request",
+ MAC2STR(dev->info.p2p_device_addr));
+ return -1;
+ }
+
+ if (dev->flags & P2P_DEV_GROUP_CLIENT_ONLY) {
+ if (!(dev->info.dev_capab &
+ P2P_DEV_CAPAB_CLIENT_DISCOVERABILITY)) {
+ wpa_msg(p2p->cfg->msg_ctx, MSG_DEBUG,
+ "P2P: Cannot use PD with P2P Device " MACSTR
+ " that is in a group and is not discoverable",
+ MAC2STR(dev->info.p2p_device_addr));
+ return -1;
+ }
+ /* TODO: use device discoverability request through GO */
+ }
+
+ req = p2p_build_prov_disc_req(p2p, dev->dialog_token,
+ dev->req_config_methods,
+ join ? dev : NULL);
+ if (req == NULL)
+ return -1;
+
+ if (p2p->state != P2P_IDLE)
+ p2p_stop_listen_for_freq(p2p, freq);
+ p2p->pending_action_state = P2P_PENDING_PD;
+ if (p2p_send_action(p2p, freq, dev->info.p2p_device_addr,
+ p2p->cfg->dev_addr, dev->info.p2p_device_addr,
+ wpabuf_head(req), wpabuf_len(req), 200) < 0) {
+ wpa_msg(p2p->cfg->msg_ctx, MSG_DEBUG,
+ "P2P: Failed to send Action frame");
+ wpabuf_free(req);
+ return -1;
+ }
+
+ os_memcpy(p2p->pending_pd_devaddr, dev->info.p2p_device_addr, ETH_ALEN);
+
+ wpabuf_free(req);
+ return 0;
+}
+
+
+int p2p_prov_disc_req(struct p2p_data *p2p, const u8 *peer_addr,
+ u16 config_methods, int join, int force_freq,
+ int user_initiated_pd)
+{
+ struct p2p_device *dev;
+
+ dev = p2p_get_device(p2p, peer_addr);
+ if (dev == NULL)
+ dev = p2p_get_device_interface(p2p, peer_addr);
+ if (dev == NULL || (dev->flags & P2P_DEV_PROBE_REQ_ONLY)) {
+ wpa_msg(p2p->cfg->msg_ctx, MSG_DEBUG, "P2P: Provision "
+ "Discovery Request destination " MACSTR
+ " not yet known", MAC2STR(peer_addr));
+ return -1;
+ }
+
+ wpa_msg(p2p->cfg->msg_ctx, MSG_DEBUG, "P2P: Provision Discovery "
+ "Request with " MACSTR " (config methods 0x%x)",
+ MAC2STR(peer_addr), config_methods);
+ if (config_methods == 0)
+ return -1;
+
+ /* Reset provisioning info */
+ dev->wps_prov_info = 0;
+
+ dev->req_config_methods = config_methods;
+ if (join)
+ dev->flags |= P2P_DEV_PD_FOR_JOIN;
+ else
+ dev->flags &= ~P2P_DEV_PD_FOR_JOIN;
+
+ if (p2p->state != P2P_IDLE && p2p->state != P2P_SEARCH &&
+ p2p->state != P2P_LISTEN_ONLY) {
+ wpa_msg(p2p->cfg->msg_ctx, MSG_DEBUG, "P2P: Busy with other "
+ "operations; postpone Provision Discovery Request "
+ "with " MACSTR " (config methods 0x%x)",
+ MAC2STR(peer_addr), config_methods);
+ return 0;
+ }
+
+ p2p->user_initiated_pd = user_initiated_pd;
+
+ if (p2p->user_initiated_pd)
+ p2p->pd_retries = MAX_PROV_DISC_REQ_RETRIES;
+
+ /*
+ * Assign dialog token here to use the same value in each retry within
+ * the same PD exchange.
+ */
+ dev->dialog_token++;
+ if (dev->dialog_token == 0)
+ dev->dialog_token = 1;
+
+ return p2p_send_prov_disc_req(p2p, dev, join, force_freq);
+}
+
+
+void p2p_reset_pending_pd(struct p2p_data *p2p)
+{
+ struct p2p_device *dev;
+
+ dl_list_for_each(dev, &p2p->devices, struct p2p_device, list) {
+ if (os_memcmp(p2p->pending_pd_devaddr,
+ dev->info.p2p_device_addr, ETH_ALEN))
+ continue;
+ if (!dev->req_config_methods)
+ continue;
+ if (dev->flags & P2P_DEV_PD_FOR_JOIN)
+ continue;
+ /* Reset the config methods of the device */
+ dev->req_config_methods = 0;
+ }
+
+ p2p->user_initiated_pd = 0;
+ os_memset(p2p->pending_pd_devaddr, 0, ETH_ALEN);
+ p2p->pd_retries = 0;
+}
diff --git a/contrib/wpa/src/p2p/p2p_sd.c b/contrib/wpa/src/p2p/p2p_sd.c
new file mode 100644
index 0000000..abe1d6b
--- /dev/null
+++ b/contrib/wpa/src/p2p/p2p_sd.c
@@ -0,0 +1,947 @@
+/*
+ * Wi-Fi Direct - P2P service discovery
+ * Copyright (c) 2009, Atheros Communications
+ *
+ * This software may be distributed under the terms of the BSD license.
+ * See README for more details.
+ */
+
+#include "includes.h"
+
+#include "common.h"
+#include "common/ieee802_11_defs.h"
+#include "common/gas.h"
+#include "p2p_i.h"
+#include "p2p.h"
+
+
+#ifdef CONFIG_WIFI_DISPLAY
+static int wfd_wsd_supported(struct wpabuf *wfd)
+{
+ const u8 *pos, *end;
+ u8 subelem;
+ u16 len;
+
+ if (wfd == NULL)
+ return 0;
+
+ pos = wpabuf_head(wfd);
+ end = pos + wpabuf_len(wfd);
+
+ while (pos + 3 <= end) {
+ subelem = *pos++;
+ len = WPA_GET_BE16(pos);
+ pos += 2;
+ if (pos + len > end)
+ break;
+
+ if (subelem == WFD_SUBELEM_DEVICE_INFO && len >= 6) {
+ u16 info = WPA_GET_BE16(pos);
+ return !!(info & 0x0040);
+ }
+
+ pos += len;
+ }
+
+ return 0;
+}
+#endif /* CONFIG_WIFI_DISPLAY */
+
+struct p2p_sd_query * p2p_pending_sd_req(struct p2p_data *p2p,
+ struct p2p_device *dev)
+{
+ struct p2p_sd_query *q;
+ int wsd = 0;
+
+ if (!(dev->info.dev_capab & P2P_DEV_CAPAB_SERVICE_DISCOVERY))
+ return NULL; /* peer does not support SD */
+#ifdef CONFIG_WIFI_DISPLAY
+ if (wfd_wsd_supported(dev->info.wfd_subelems))
+ wsd = 1;
+#endif /* CONFIG_WIFI_DISPLAY */
+
+ for (q = p2p->sd_queries; q; q = q->next) {
+ /* Use WSD only if the peer indicates support or it */
+ if (q->wsd && !wsd)
+ continue;
+ if (q->for_all_peers && !(dev->flags & P2P_DEV_SD_INFO))
+ return q;
+ if (!q->for_all_peers &&
+ os_memcmp(q->peer, dev->info.p2p_device_addr, ETH_ALEN) ==
+ 0)
+ return q;
+ }
+
+ return NULL;
+}
+
+
+static int p2p_unlink_sd_query(struct p2p_data *p2p,
+ struct p2p_sd_query *query)
+{
+ struct p2p_sd_query *q, *prev;
+ q = p2p->sd_queries;
+ prev = NULL;
+ while (q) {
+ if (q == query) {
+ if (prev)
+ prev->next = q->next;
+ else
+ p2p->sd_queries = q->next;
+ if (p2p->sd_query == query)
+ p2p->sd_query = NULL;
+ return 1;
+ }
+ prev = q;
+ q = q->next;
+ }
+ return 0;
+}
+
+
+static void p2p_free_sd_query(struct p2p_sd_query *q)
+{
+ if (q == NULL)
+ return;
+ wpabuf_free(q->tlvs);
+ os_free(q);
+}
+
+
+void p2p_free_sd_queries(struct p2p_data *p2p)
+{
+ struct p2p_sd_query *q, *prev;
+ q = p2p->sd_queries;
+ p2p->sd_queries = NULL;
+ while (q) {
+ prev = q;
+ q = q->next;
+ p2p_free_sd_query(prev);
+ }
+}
+
+
+static struct wpabuf * p2p_build_sd_query(u16 update_indic,
+ struct wpabuf *tlvs)
+{
+ struct wpabuf *buf;
+ u8 *len_pos;
+
+ buf = gas_anqp_build_initial_req(0, 100 + wpabuf_len(tlvs));
+ if (buf == NULL)
+ return NULL;
+
+ /* ANQP Query Request Frame */
+ len_pos = gas_anqp_add_element(buf, ANQP_VENDOR_SPECIFIC);
+ wpabuf_put_be24(buf, OUI_WFA);
+ wpabuf_put_u8(buf, P2P_OUI_TYPE);
+ wpabuf_put_le16(buf, update_indic); /* Service Update Indicator */
+ wpabuf_put_buf(buf, tlvs);
+ gas_anqp_set_element_len(buf, len_pos);
+
+ gas_anqp_set_len(buf);
+
+ return buf;
+}
+
+
+static void p2p_send_gas_comeback_req(struct p2p_data *p2p, const u8 *dst,
+ u8 dialog_token, int freq)
+{
+ struct wpabuf *req;
+
+ req = gas_build_comeback_req(dialog_token);
+ if (req == NULL)
+ return;
+
+ p2p->pending_action_state = P2P_NO_PENDING_ACTION;
+ if (p2p_send_action(p2p, freq, dst, p2p->cfg->dev_addr, dst,
+ wpabuf_head(req), wpabuf_len(req), 200) < 0)
+ wpa_msg(p2p->cfg->msg_ctx, MSG_DEBUG,
+ "P2P: Failed to send Action frame");
+
+ wpabuf_free(req);
+}
+
+
+static struct wpabuf * p2p_build_sd_response(u8 dialog_token, u16 status_code,
+ u16 comeback_delay,
+ u16 update_indic,
+ const struct wpabuf *tlvs)
+{
+ struct wpabuf *buf;
+ u8 *len_pos;
+
+ buf = gas_anqp_build_initial_resp(dialog_token, status_code,
+ comeback_delay,
+ 100 + (tlvs ? wpabuf_len(tlvs) : 0));
+ if (buf == NULL)
+ return NULL;
+
+ if (tlvs) {
+ /* ANQP Query Response Frame */
+ len_pos = gas_anqp_add_element(buf, ANQP_VENDOR_SPECIFIC);
+ wpabuf_put_be24(buf, OUI_WFA);
+ wpabuf_put_u8(buf, P2P_OUI_TYPE);
+ /* Service Update Indicator */
+ wpabuf_put_le16(buf, update_indic);
+ wpabuf_put_buf(buf, tlvs);
+ gas_anqp_set_element_len(buf, len_pos);
+ }
+
+ gas_anqp_set_len(buf);
+
+ return buf;
+}
+
+
+static struct wpabuf * p2p_build_gas_comeback_resp(u8 dialog_token,
+ u16 status_code,
+ u16 update_indic,
+ const u8 *data, size_t len,
+ u8 frag_id, u8 more,
+ u16 total_len)
+{
+ struct wpabuf *buf;
+
+ buf = gas_anqp_build_comeback_resp(dialog_token, status_code, frag_id,
+ more, 0, 100 + len);
+ if (buf == NULL)
+ return NULL;
+
+ if (frag_id == 0) {
+ /* ANQP Query Response Frame */
+ wpabuf_put_le16(buf, ANQP_VENDOR_SPECIFIC); /* Info ID */
+ wpabuf_put_le16(buf, 3 + 1 + 2 + total_len);
+ wpabuf_put_be24(buf, OUI_WFA);
+ wpabuf_put_u8(buf, P2P_OUI_TYPE);
+ /* Service Update Indicator */
+ wpabuf_put_le16(buf, update_indic);
+ }
+
+ wpabuf_put_data(buf, data, len);
+ gas_anqp_set_len(buf);
+
+ return buf;
+}
+
+
+int p2p_start_sd(struct p2p_data *p2p, struct p2p_device *dev)
+{
+ struct wpabuf *req;
+ int ret = 0;
+ struct p2p_sd_query *query;
+ int freq;
+
+ freq = dev->listen_freq > 0 ? dev->listen_freq : dev->oper_freq;
+ if (freq <= 0) {
+ wpa_msg(p2p->cfg->msg_ctx, MSG_DEBUG,
+ "P2P: No Listen/Operating frequency known for the "
+ "peer " MACSTR " to send SD Request",
+ MAC2STR(dev->info.p2p_device_addr));
+ return -1;
+ }
+
+ query = p2p_pending_sd_req(p2p, dev);
+ if (query == NULL)
+ return -1;
+
+ wpa_msg(p2p->cfg->msg_ctx, MSG_DEBUG,
+ "P2P: Start Service Discovery with " MACSTR,
+ MAC2STR(dev->info.p2p_device_addr));
+
+ req = p2p_build_sd_query(p2p->srv_update_indic, query->tlvs);
+ if (req == NULL)
+ return -1;
+
+ p2p->sd_peer = dev;
+ p2p->sd_query = query;
+ p2p->pending_action_state = P2P_PENDING_SD;
+
+ if (p2p_send_action(p2p, freq, dev->info.p2p_device_addr,
+ p2p->cfg->dev_addr, dev->info.p2p_device_addr,
+ wpabuf_head(req), wpabuf_len(req), 5000) < 0) {
+ wpa_msg(p2p->cfg->msg_ctx, MSG_DEBUG,
+ "P2P: Failed to send Action frame");
+ ret = -1;
+ }
+
+ wpabuf_free(req);
+
+ return ret;
+}
+
+
+void p2p_rx_gas_initial_req(struct p2p_data *p2p, const u8 *sa,
+ const u8 *data, size_t len, int rx_freq)
+{
+ const u8 *pos = data;
+ const u8 *end = data + len;
+ const u8 *next;
+ u8 dialog_token;
+ u16 slen;
+ int freq;
+ u16 update_indic;
+
+
+ if (p2p->cfg->sd_request == NULL)
+ return;
+
+ if (rx_freq > 0)
+ freq = rx_freq;
+ else
+ freq = p2p_channel_to_freq(p2p->cfg->country,
+ p2p->cfg->reg_class,
+ p2p->cfg->channel);
+ if (freq < 0)
+ return;
+
+ if (len < 1 + 2)
+ return;
+
+ dialog_token = *pos++;
+ wpa_msg(p2p->cfg->msg_ctx, MSG_DEBUG,
+ "P2P: GAS Initial Request from " MACSTR " (dialog token %u, "
+ "freq %d)",
+ MAC2STR(sa), dialog_token, rx_freq);
+
+ if (*pos != WLAN_EID_ADV_PROTO) {
+ wpa_msg(p2p->cfg->msg_ctx, MSG_DEBUG,
+ "P2P: Unexpected IE in GAS Initial Request: %u", *pos);
+ return;
+ }
+ pos++;
+
+ slen = *pos++;
+ next = pos + slen;
+ if (next > end || slen < 2) {
+ wpa_msg(p2p->cfg->msg_ctx, MSG_DEBUG,
+ "P2P: Invalid IE in GAS Initial Request");
+ return;
+ }
+ pos++; /* skip QueryRespLenLimit and PAME-BI */
+
+ if (*pos != ACCESS_NETWORK_QUERY_PROTOCOL) {
+ wpa_msg(p2p->cfg->msg_ctx, MSG_DEBUG,
+ "P2P: Unsupported GAS advertisement protocol id %u",
+ *pos);
+ return;
+ }
+
+ pos = next;
+ /* Query Request */
+ if (pos + 2 > end)
+ return;
+ slen = WPA_GET_LE16(pos);
+ pos += 2;
+ if (pos + slen > end)
+ return;
+ end = pos + slen;
+
+ /* ANQP Query Request */
+ if (pos + 4 > end)
+ return;
+ if (WPA_GET_LE16(pos) != ANQP_VENDOR_SPECIFIC) {
+ wpa_msg(p2p->cfg->msg_ctx, MSG_DEBUG,
+ "P2P: Unsupported ANQP Info ID %u", WPA_GET_LE16(pos));
+ return;
+ }
+ pos += 2;
+
+ slen = WPA_GET_LE16(pos);
+ pos += 2;
+ if (pos + slen > end || slen < 3 + 1) {
+ wpa_msg(p2p->cfg->msg_ctx, MSG_DEBUG,
+ "P2P: Invalid ANQP Query Request length");
+ return;
+ }
+
+ if (WPA_GET_BE24(pos) != OUI_WFA) {
+ wpa_msg(p2p->cfg->msg_ctx, MSG_DEBUG,
+ "P2P: Unsupported ANQP OUI %06x", WPA_GET_BE24(pos));
+ return;
+ }
+ pos += 3;
+
+ if (*pos != P2P_OUI_TYPE) {
+ wpa_msg(p2p->cfg->msg_ctx, MSG_DEBUG,
+ "P2P: Unsupported ANQP vendor type %u", *pos);
+ return;
+ }
+ pos++;
+
+ if (pos + 2 > end)
+ return;
+ update_indic = WPA_GET_LE16(pos);
+ wpa_msg(p2p->cfg->msg_ctx, MSG_DEBUG,
+ "P2P: Service Update Indicator: %u", update_indic);
+ pos += 2;
+
+ p2p->cfg->sd_request(p2p->cfg->cb_ctx, freq, sa, dialog_token,
+ update_indic, pos, end - pos);
+ /* the response will be indicated with a call to p2p_sd_response() */
+}
+
+
+void p2p_sd_response(struct p2p_data *p2p, int freq, const u8 *dst,
+ u8 dialog_token, const struct wpabuf *resp_tlvs)
+{
+ struct wpabuf *resp;
+
+ /* TODO: fix the length limit to match with the maximum frame length */
+ if (wpabuf_len(resp_tlvs) > 1400) {
+ wpa_msg(p2p->cfg->msg_ctx, MSG_DEBUG, "P2P: SD response long "
+ "enough to require fragmentation");
+ if (p2p->sd_resp) {
+ /*
+ * TODO: Could consider storing the fragmented response
+ * separately for each peer to avoid having to drop old
+ * one if there is more than one pending SD query.
+ * Though, that would eat more memory, so there are
+ * also benefits to just using a single buffer.
+ */
+ wpa_msg(p2p->cfg->msg_ctx, MSG_DEBUG, "P2P: Drop "
+ "previous SD response");
+ wpabuf_free(p2p->sd_resp);
+ }
+ p2p->sd_resp = wpabuf_dup(resp_tlvs);
+ if (p2p->sd_resp == NULL) {
+ wpa_msg(p2p->cfg->msg_ctx, MSG_ERROR, "P2P: Failed to "
+ "allocate SD response fragmentation area");
+ return;
+ }
+ os_memcpy(p2p->sd_resp_addr, dst, ETH_ALEN);
+ p2p->sd_resp_dialog_token = dialog_token;
+ p2p->sd_resp_pos = 0;
+ p2p->sd_frag_id = 0;
+ resp = p2p_build_sd_response(dialog_token, WLAN_STATUS_SUCCESS,
+ 1, p2p->srv_update_indic, NULL);
+ } else {
+ wpa_msg(p2p->cfg->msg_ctx, MSG_DEBUG, "P2P: SD response fits "
+ "in initial response");
+ resp = p2p_build_sd_response(dialog_token,
+ WLAN_STATUS_SUCCESS, 0,
+ p2p->srv_update_indic, resp_tlvs);
+ }
+ if (resp == NULL)
+ return;
+
+ p2p->pending_action_state = P2P_NO_PENDING_ACTION;
+ if (p2p_send_action(p2p, freq, dst, p2p->cfg->dev_addr,
+ p2p->cfg->dev_addr,
+ wpabuf_head(resp), wpabuf_len(resp), 200) < 0)
+ wpa_msg(p2p->cfg->msg_ctx, MSG_DEBUG,
+ "P2P: Failed to send Action frame");
+
+ wpabuf_free(resp);
+}
+
+
+void p2p_rx_gas_initial_resp(struct p2p_data *p2p, const u8 *sa,
+ const u8 *data, size_t len, int rx_freq)
+{
+ const u8 *pos = data;
+ const u8 *end = data + len;
+ const u8 *next;
+ u8 dialog_token;
+ u16 status_code;
+ u16 comeback_delay;
+ u16 slen;
+ u16 update_indic;
+
+ if (p2p->state != P2P_SD_DURING_FIND || p2p->sd_peer == NULL ||
+ os_memcmp(sa, p2p->sd_peer->info.p2p_device_addr, ETH_ALEN) != 0) {
+ wpa_msg(p2p->cfg->msg_ctx, MSG_DEBUG,
+ "P2P: Ignore unexpected GAS Initial Response from "
+ MACSTR, MAC2STR(sa));
+ return;
+ }
+ p2p->cfg->send_action_done(p2p->cfg->cb_ctx);
+ p2p_clear_timeout(p2p);
+
+ wpa_msg(p2p->cfg->msg_ctx, MSG_DEBUG,
+ "P2P: Received GAS Initial Response from " MACSTR " (len=%d)",
+ MAC2STR(sa), (int) len);
+
+ if (len < 5 + 2) {
+ wpa_msg(p2p->cfg->msg_ctx, MSG_DEBUG,
+ "P2P: Too short GAS Initial Response frame");
+ return;
+ }
+
+ dialog_token = *pos++;
+ /* TODO: check dialog_token match */
+ status_code = WPA_GET_LE16(pos);
+ pos += 2;
+ comeback_delay = WPA_GET_LE16(pos);
+ pos += 2;
+ wpa_msg(p2p->cfg->msg_ctx, MSG_DEBUG,
+ "P2P: dialog_token=%u status_code=%u comeback_delay=%u",
+ dialog_token, status_code, comeback_delay);
+ if (status_code) {
+ wpa_msg(p2p->cfg->msg_ctx, MSG_DEBUG,
+ "P2P: Service Discovery failed: status code %u",
+ status_code);
+ return;
+ }
+
+ if (*pos != WLAN_EID_ADV_PROTO) {
+ wpa_msg(p2p->cfg->msg_ctx, MSG_DEBUG,
+ "P2P: Unexpected IE in GAS Initial Response: %u",
+ *pos);
+ return;
+ }
+ pos++;
+
+ slen = *pos++;
+ next = pos + slen;
+ if (next > end || slen < 2) {
+ wpa_msg(p2p->cfg->msg_ctx, MSG_DEBUG,
+ "P2P: Invalid IE in GAS Initial Response");
+ return;
+ }
+ pos++; /* skip QueryRespLenLimit and PAME-BI */
+
+ if (*pos != ACCESS_NETWORK_QUERY_PROTOCOL) {
+ wpa_msg(p2p->cfg->msg_ctx, MSG_DEBUG,
+ "P2P: Unsupported GAS advertisement protocol id %u",
+ *pos);
+ return;
+ }
+
+ pos = next;
+ /* Query Response */
+ if (pos + 2 > end) {
+ wpa_msg(p2p->cfg->msg_ctx, MSG_DEBUG, "P2P: Too short Query "
+ "Response");
+ return;
+ }
+ slen = WPA_GET_LE16(pos);
+ pos += 2;
+ wpa_msg(p2p->cfg->msg_ctx, MSG_DEBUG, "P2P: Query Response Length: %d",
+ slen);
+ if (pos + slen > end) {
+ wpa_msg(p2p->cfg->msg_ctx, MSG_DEBUG, "P2P: Not enough Query "
+ "Response data");
+ return;
+ }
+ end = pos + slen;
+
+ if (comeback_delay) {
+ wpa_msg(p2p->cfg->msg_ctx, MSG_DEBUG, "P2P: Fragmented "
+ "response - request fragments");
+ if (p2p->sd_rx_resp) {
+ wpa_msg(p2p->cfg->msg_ctx, MSG_DEBUG, "P2P: Drop "
+ "old SD reassembly buffer");
+ wpabuf_free(p2p->sd_rx_resp);
+ p2p->sd_rx_resp = NULL;
+ }
+ p2p_send_gas_comeback_req(p2p, sa, dialog_token, rx_freq);
+ return;
+ }
+
+ /* ANQP Query Response */
+ if (pos + 4 > end)
+ return;
+ if (WPA_GET_LE16(pos) != ANQP_VENDOR_SPECIFIC) {
+ wpa_msg(p2p->cfg->msg_ctx, MSG_DEBUG,
+ "P2P: Unsupported ANQP Info ID %u", WPA_GET_LE16(pos));
+ return;
+ }
+ pos += 2;
+
+ slen = WPA_GET_LE16(pos);
+ pos += 2;
+ if (pos + slen > end || slen < 3 + 1) {
+ wpa_msg(p2p->cfg->msg_ctx, MSG_DEBUG,
+ "P2P: Invalid ANQP Query Response length");
+ return;
+ }
+
+ if (WPA_GET_BE24(pos) != OUI_WFA) {
+ wpa_msg(p2p->cfg->msg_ctx, MSG_DEBUG,
+ "P2P: Unsupported ANQP OUI %06x", WPA_GET_BE24(pos));
+ return;
+ }
+ pos += 3;
+
+ if (*pos != P2P_OUI_TYPE) {
+ wpa_msg(p2p->cfg->msg_ctx, MSG_DEBUG,
+ "P2P: Unsupported ANQP vendor type %u", *pos);
+ return;
+ }
+ pos++;
+
+ if (pos + 2 > end)
+ return;
+ update_indic = WPA_GET_LE16(pos);
+ wpa_msg(p2p->cfg->msg_ctx, MSG_DEBUG,
+ "P2P: Service Update Indicator: %u", update_indic);
+ pos += 2;
+
+ p2p->sd_peer->flags |= P2P_DEV_SD_INFO;
+ p2p->sd_peer->flags &= ~P2P_DEV_SD_SCHEDULE;
+ p2p->sd_peer = NULL;
+
+ if (p2p->sd_query) {
+ if (!p2p->sd_query->for_all_peers) {
+ struct p2p_sd_query *q;
+ wpa_msg(p2p->cfg->msg_ctx, MSG_DEBUG,
+ "P2P: Remove completed SD query %p",
+ p2p->sd_query);
+ q = p2p->sd_query;
+ p2p_unlink_sd_query(p2p, p2p->sd_query);
+ p2p_free_sd_query(q);
+ }
+ p2p->sd_query = NULL;
+ }
+
+ if (p2p->cfg->sd_response)
+ p2p->cfg->sd_response(p2p->cfg->cb_ctx, sa, update_indic,
+ pos, end - pos);
+ p2p_continue_find(p2p);
+}
+
+
+void p2p_rx_gas_comeback_req(struct p2p_data *p2p, const u8 *sa,
+ const u8 *data, size_t len, int rx_freq)
+{
+ struct wpabuf *resp;
+ u8 dialog_token;
+ size_t frag_len;
+ int more = 0;
+
+ wpa_hexdump(MSG_DEBUG, "P2P: RX GAS Comeback Request", data, len);
+ if (len < 1)
+ return;
+ dialog_token = *data;
+ wpa_msg(p2p->cfg->msg_ctx, MSG_DEBUG, "P2P: Dialog Token: %u",
+ dialog_token);
+ if (dialog_token != p2p->sd_resp_dialog_token) {
+ wpa_msg(p2p->cfg->msg_ctx, MSG_DEBUG, "P2P: No pending SD "
+ "response fragment for dialog token %u", dialog_token);
+ return;
+ }
+
+ if (p2p->sd_resp == NULL) {
+ wpa_msg(p2p->cfg->msg_ctx, MSG_DEBUG, "P2P: No pending SD "
+ "response fragment available");
+ return;
+ }
+ if (os_memcmp(sa, p2p->sd_resp_addr, ETH_ALEN) != 0) {
+ wpa_msg(p2p->cfg->msg_ctx, MSG_DEBUG, "P2P: No pending SD "
+ "response fragment for " MACSTR, MAC2STR(sa));
+ return;
+ }
+
+ frag_len = wpabuf_len(p2p->sd_resp) - p2p->sd_resp_pos;
+ if (frag_len > 1400) {
+ frag_len = 1400;
+ more = 1;
+ }
+ resp = p2p_build_gas_comeback_resp(dialog_token, WLAN_STATUS_SUCCESS,
+ p2p->srv_update_indic,
+ wpabuf_head_u8(p2p->sd_resp) +
+ p2p->sd_resp_pos, frag_len,
+ p2p->sd_frag_id, more,
+ wpabuf_len(p2p->sd_resp));
+ if (resp == NULL)
+ return;
+ wpa_msg(p2p->cfg->msg_ctx, MSG_DEBUG, "P2P: Send GAS Comeback "
+ "Response (frag_id %d more=%d frag_len=%d)",
+ p2p->sd_frag_id, more, (int) frag_len);
+ p2p->sd_frag_id++;
+ p2p->sd_resp_pos += frag_len;
+
+ if (more) {
+ wpa_msg(p2p->cfg->msg_ctx, MSG_DEBUG, "P2P: %d more bytes "
+ "remain to be sent",
+ (int) (wpabuf_len(p2p->sd_resp) - p2p->sd_resp_pos));
+ } else {
+ wpa_msg(p2p->cfg->msg_ctx, MSG_DEBUG, "P2P: All fragments of "
+ "SD response sent");
+ wpabuf_free(p2p->sd_resp);
+ p2p->sd_resp = NULL;
+ }
+
+ p2p->pending_action_state = P2P_NO_PENDING_ACTION;
+ if (p2p_send_action(p2p, rx_freq, sa, p2p->cfg->dev_addr,
+ p2p->cfg->dev_addr,
+ wpabuf_head(resp), wpabuf_len(resp), 200) < 0)
+ wpa_msg(p2p->cfg->msg_ctx, MSG_DEBUG,
+ "P2P: Failed to send Action frame");
+
+ wpabuf_free(resp);
+}
+
+
+void p2p_rx_gas_comeback_resp(struct p2p_data *p2p, const u8 *sa,
+ const u8 *data, size_t len, int rx_freq)
+{
+ const u8 *pos = data;
+ const u8 *end = data + len;
+ const u8 *next;
+ u8 dialog_token;
+ u16 status_code;
+ u8 frag_id;
+ u8 more_frags;
+ u16 comeback_delay;
+ u16 slen;
+
+ wpa_hexdump(MSG_DEBUG, "P2P: RX GAS Comeback Response", data, len);
+
+ if (p2p->state != P2P_SD_DURING_FIND || p2p->sd_peer == NULL ||
+ os_memcmp(sa, p2p->sd_peer->info.p2p_device_addr, ETH_ALEN) != 0) {
+ wpa_msg(p2p->cfg->msg_ctx, MSG_DEBUG,
+ "P2P: Ignore unexpected GAS Comeback Response from "
+ MACSTR, MAC2STR(sa));
+ return;
+ }
+ p2p->cfg->send_action_done(p2p->cfg->cb_ctx);
+ p2p_clear_timeout(p2p);
+
+ wpa_msg(p2p->cfg->msg_ctx, MSG_DEBUG,
+ "P2P: Received GAS Comeback Response from " MACSTR " (len=%d)",
+ MAC2STR(sa), (int) len);
+
+ if (len < 6 + 2) {
+ wpa_msg(p2p->cfg->msg_ctx, MSG_DEBUG,
+ "P2P: Too short GAS Comeback Response frame");
+ return;
+ }
+
+ dialog_token = *pos++;
+ /* TODO: check dialog_token match */
+ status_code = WPA_GET_LE16(pos);
+ pos += 2;
+ frag_id = *pos & 0x7f;
+ more_frags = (*pos & 0x80) >> 7;
+ pos++;
+ comeback_delay = WPA_GET_LE16(pos);
+ pos += 2;
+ wpa_msg(p2p->cfg->msg_ctx, MSG_DEBUG,
+ "P2P: dialog_token=%u status_code=%u frag_id=%d more_frags=%d "
+ "comeback_delay=%u",
+ dialog_token, status_code, frag_id, more_frags,
+ comeback_delay);
+ /* TODO: check frag_id match */
+ if (status_code) {
+ wpa_msg(p2p->cfg->msg_ctx, MSG_DEBUG,
+ "P2P: Service Discovery failed: status code %u",
+ status_code);
+ return;
+ }
+
+ if (*pos != WLAN_EID_ADV_PROTO) {
+ wpa_msg(p2p->cfg->msg_ctx, MSG_DEBUG,
+ "P2P: Unexpected IE in GAS Comeback Response: %u",
+ *pos);
+ return;
+ }
+ pos++;
+
+ slen = *pos++;
+ next = pos + slen;
+ if (next > end || slen < 2) {
+ wpa_msg(p2p->cfg->msg_ctx, MSG_DEBUG,
+ "P2P: Invalid IE in GAS Comeback Response");
+ return;
+ }
+ pos++; /* skip QueryRespLenLimit and PAME-BI */
+
+ if (*pos != ACCESS_NETWORK_QUERY_PROTOCOL) {
+ wpa_msg(p2p->cfg->msg_ctx, MSG_DEBUG,
+ "P2P: Unsupported GAS advertisement protocol id %u",
+ *pos);
+ return;
+ }
+
+ pos = next;
+ /* Query Response */
+ if (pos + 2 > end) {
+ wpa_msg(p2p->cfg->msg_ctx, MSG_DEBUG, "P2P: Too short Query "
+ "Response");
+ return;
+ }
+ slen = WPA_GET_LE16(pos);
+ pos += 2;
+ wpa_msg(p2p->cfg->msg_ctx, MSG_DEBUG, "P2P: Query Response Length: %d",
+ slen);
+ if (pos + slen > end) {
+ wpa_msg(p2p->cfg->msg_ctx, MSG_DEBUG, "P2P: Not enough Query "
+ "Response data");
+ return;
+ }
+ if (slen == 0) {
+ wpa_msg(p2p->cfg->msg_ctx, MSG_DEBUG, "P2P: No Query Response "
+ "data");
+ return;
+ }
+ end = pos + slen;
+
+ if (p2p->sd_rx_resp) {
+ /*
+ * ANQP header is only included in the first fragment; rest of
+ * the fragments start with continue TLVs.
+ */
+ goto skip_nqp_header;
+ }
+
+ /* ANQP Query Response */
+ if (pos + 4 > end)
+ return;
+ if (WPA_GET_LE16(pos) != ANQP_VENDOR_SPECIFIC) {
+ wpa_msg(p2p->cfg->msg_ctx, MSG_DEBUG,
+ "P2P: Unsupported ANQP Info ID %u", WPA_GET_LE16(pos));
+ return;
+ }
+ pos += 2;
+
+ slen = WPA_GET_LE16(pos);
+ pos += 2;
+ wpa_msg(p2p->cfg->msg_ctx, MSG_DEBUG, "P2P: ANQP Query Response "
+ "length: %u", slen);
+ if (slen < 3 + 1) {
+ wpa_msg(p2p->cfg->msg_ctx, MSG_DEBUG,
+ "P2P: Invalid ANQP Query Response length");
+ return;
+ }
+ if (pos + 4 > end)
+ return;
+
+ if (WPA_GET_BE24(pos) != OUI_WFA) {
+ wpa_msg(p2p->cfg->msg_ctx, MSG_DEBUG,
+ "P2P: Unsupported ANQP OUI %06x", WPA_GET_BE24(pos));
+ return;
+ }
+ pos += 3;
+
+ if (*pos != P2P_OUI_TYPE) {
+ wpa_msg(p2p->cfg->msg_ctx, MSG_DEBUG,
+ "P2P: Unsupported ANQP vendor type %u", *pos);
+ return;
+ }
+ pos++;
+
+ if (pos + 2 > end)
+ return;
+ p2p->sd_rx_update_indic = WPA_GET_LE16(pos);
+ wpa_msg(p2p->cfg->msg_ctx, MSG_DEBUG,
+ "P2P: Service Update Indicator: %u", p2p->sd_rx_update_indic);
+ pos += 2;
+
+skip_nqp_header:
+ if (wpabuf_resize(&p2p->sd_rx_resp, end - pos) < 0)
+ return;
+ wpabuf_put_data(p2p->sd_rx_resp, pos, end - pos);
+ wpa_msg(p2p->cfg->msg_ctx, MSG_DEBUG, "P2P: Current SD reassembly "
+ "buffer length: %u",
+ (unsigned int) wpabuf_len(p2p->sd_rx_resp));
+
+ if (more_frags) {
+ wpa_msg(p2p->cfg->msg_ctx, MSG_DEBUG, "P2P: More fragments "
+ "remains");
+ /* TODO: what would be a good size limit? */
+ if (wpabuf_len(p2p->sd_rx_resp) > 64000) {
+ wpabuf_free(p2p->sd_rx_resp);
+ p2p->sd_rx_resp = NULL;
+ wpa_msg(p2p->cfg->msg_ctx, MSG_DEBUG, "P2P: Too long "
+ "SD response - drop it");
+ return;
+ }
+ p2p_send_gas_comeback_req(p2p, sa, dialog_token, rx_freq);
+ return;
+ }
+
+ p2p->sd_peer->flags |= P2P_DEV_SD_INFO;
+ p2p->sd_peer->flags &= ~P2P_DEV_SD_SCHEDULE;
+ p2p->sd_peer = NULL;
+
+ if (p2p->sd_query) {
+ if (!p2p->sd_query->for_all_peers) {
+ struct p2p_sd_query *q;
+ wpa_msg(p2p->cfg->msg_ctx, MSG_DEBUG,
+ "P2P: Remove completed SD query %p",
+ p2p->sd_query);
+ q = p2p->sd_query;
+ p2p_unlink_sd_query(p2p, p2p->sd_query);
+ p2p_free_sd_query(q);
+ }
+ p2p->sd_query = NULL;
+ }
+
+ if (p2p->cfg->sd_response)
+ p2p->cfg->sd_response(p2p->cfg->cb_ctx, sa,
+ p2p->sd_rx_update_indic,
+ wpabuf_head(p2p->sd_rx_resp),
+ wpabuf_len(p2p->sd_rx_resp));
+ wpabuf_free(p2p->sd_rx_resp);
+ p2p->sd_rx_resp = NULL;
+
+ p2p_continue_find(p2p);
+}
+
+
+void * p2p_sd_request(struct p2p_data *p2p, const u8 *dst,
+ const struct wpabuf *tlvs)
+{
+ struct p2p_sd_query *q;
+
+ q = os_zalloc(sizeof(*q));
+ if (q == NULL)
+ return NULL;
+
+ if (dst)
+ os_memcpy(q->peer, dst, ETH_ALEN);
+ else
+ q->for_all_peers = 1;
+
+ q->tlvs = wpabuf_dup(tlvs);
+ if (q->tlvs == NULL) {
+ p2p_free_sd_query(q);
+ return NULL;
+ }
+
+ q->next = p2p->sd_queries;
+ p2p->sd_queries = q;
+ wpa_msg(p2p->cfg->msg_ctx, MSG_DEBUG, "P2P: Added SD Query %p", q);
+
+ if (dst == NULL) {
+ struct p2p_device *dev;
+ dl_list_for_each(dev, &p2p->devices, struct p2p_device, list)
+ dev->flags &= ~P2P_DEV_SD_INFO;
+ }
+
+ return q;
+}
+
+
+#ifdef CONFIG_WIFI_DISPLAY
+void * p2p_sd_request_wfd(struct p2p_data *p2p, const u8 *dst,
+ const struct wpabuf *tlvs)
+{
+ struct p2p_sd_query *q;
+ q = p2p_sd_request(p2p, dst, tlvs);
+ if (q)
+ q->wsd = 1;
+ return q;
+}
+#endif /* CONFIG_WIFI_DISPLAY */
+
+
+void p2p_sd_service_update(struct p2p_data *p2p)
+{
+ p2p->srv_update_indic++;
+}
+
+
+int p2p_sd_cancel_request(struct p2p_data *p2p, void *req)
+{
+ if (p2p_unlink_sd_query(p2p, req)) {
+ wpa_msg(p2p->cfg->msg_ctx, MSG_DEBUG,
+ "P2P: Cancel pending SD query %p", req);
+ p2p_free_sd_query(req);
+ return 0;
+ }
+ return -1;
+}
diff --git a/contrib/wpa/src/p2p/p2p_utils.c b/contrib/wpa/src/p2p/p2p_utils.c
new file mode 100644
index 0000000..bcc690d
--- /dev/null
+++ b/contrib/wpa/src/p2p/p2p_utils.c
@@ -0,0 +1,265 @@
+/*
+ * P2P - generic helper functions
+ * Copyright (c) 2009, Atheros Communications
+ *
+ * This software may be distributed under the terms of the BSD license.
+ * See README for more details.
+ */
+
+#include "includes.h"
+
+#include "common.h"
+#include "p2p_i.h"
+
+
+/**
+ * p2p_random - Generate random string for SSID and passphrase
+ * @buf: Buffer for returning the result
+ * @len: Number of octets to write to the buffer
+ * Returns: 0 on success, -1 on failure
+ *
+ * This function generates a random string using the following character set:
+ * 'A'-'Z', 'a'-'z', '0'-'9'.
+ */
+int p2p_random(char *buf, size_t len)
+{
+ u8 val;
+ size_t i;
+ u8 letters = 'Z' - 'A' + 1;
+ u8 numbers = 10;
+
+ if (os_get_random((unsigned char *) buf, len))
+ return -1;
+ /* Character set: 'A'-'Z', 'a'-'z', '0'-'9' */
+ for (i = 0; i < len; i++) {
+ val = buf[i];
+ val %= 2 * letters + numbers;
+ if (val < letters)
+ buf[i] = 'A' + val;
+ else if (val < 2 * letters)
+ buf[i] = 'a' + (val - letters);
+ else
+ buf[i] = '0' + (val - 2 * letters);
+ }
+
+ return 0;
+}
+
+
+static int p2p_channel_to_freq_j4(int reg_class, int channel)
+{
+ /* Table J-4 in P802.11REVmb/D4.0 - Global operating classes */
+ /* TODO: more regulatory classes */
+ switch (reg_class) {
+ case 81:
+ /* channels 1..13 */
+ if (channel < 1 || channel > 13)
+ return -1;
+ return 2407 + 5 * channel;
+ case 82:
+ /* channel 14 */
+ if (channel != 14)
+ return -1;
+ return 2414 + 5 * channel;
+ case 83: /* channels 1..9; 40 MHz */
+ case 84: /* channels 5..13; 40 MHz */
+ if (channel < 1 || channel > 13)
+ return -1;
+ return 2407 + 5 * channel;
+ case 115: /* channels 36,40,44,48; indoor only */
+ case 118: /* channels 52,56,60,64; dfs */
+ if (channel < 36 || channel > 64)
+ return -1;
+ return 5000 + 5 * channel;
+ case 124: /* channels 149,153,157,161 */
+ case 125: /* channels 149,153,157,161,165,169 */
+ if (channel < 149 || channel > 161)
+ return -1;
+ return 5000 + 5 * channel;
+ case 116: /* channels 36,44; 40 MHz; indoor only */
+ case 117: /* channels 40,48; 40 MHz; indoor only */
+ case 119: /* channels 52,60; 40 MHz; dfs */
+ case 120: /* channels 56,64; 40 MHz; dfs */
+ if (channel < 36 || channel > 64)
+ return -1;
+ return 5000 + 5 * channel;
+ case 126: /* channels 149,157; 40 MHz */
+ case 127: /* channels 153,161; 40 MHz */
+ if (channel < 149 || channel > 161)
+ return -1;
+ return 5000 + 5 * channel;
+ }
+ return -1;
+}
+
+
+/**
+ * p2p_channel_to_freq - Convert channel info to frequency
+ * @country: Country code
+ * @reg_class: Regulatory class
+ * @channel: Channel number
+ * Returns: Frequency in MHz or -1 if the specified channel is unknown
+ */
+int p2p_channel_to_freq(const char *country, int reg_class, int channel)
+{
+ if (country[2] == 0x04)
+ return p2p_channel_to_freq_j4(reg_class, channel);
+
+ /* These are mainly for backwards compatibility; to be removed */
+ switch (reg_class) {
+ case 1: /* US/1, EU/1, JP/1 = 5 GHz, channels 36,40,44,48 */
+ if (channel < 36 || channel > 48)
+ return -1;
+ return 5000 + 5 * channel;
+ case 3: /* US/3 = 5 GHz, channels 149,153,157,161 */
+ case 5: /* US/5 = 5 GHz, channels 149,153,157,161 */
+ if (channel < 149 || channel > 161)
+ return -1;
+ return 5000 + 5 * channel;
+ case 4: /* EU/4 = 2.407 GHz, channels 1..13 */
+ case 12: /* US/12 = 2.407 GHz, channels 1..11 */
+ case 30: /* JP/30 = 2.407 GHz, channels 1..13 */
+ if (channel < 1 || channel > 13)
+ return -1;
+ return 2407 + 5 * channel;
+ case 31: /* JP/31 = 2.414 GHz, channel 14 */
+ if (channel != 14)
+ return -1;
+ return 2414 + 5 * channel;
+ }
+
+ return -1;
+}
+
+
+/**
+ * p2p_freq_to_channel - Convert frequency into channel info
+ * @country: Country code
+ * @reg_class: Buffer for returning regulatory class
+ * @channel: Buffer for returning channel number
+ * Returns: 0 on success, -1 if the specified frequency is unknown
+ */
+int p2p_freq_to_channel(const char *country, unsigned int freq, u8 *reg_class,
+ u8 *channel)
+{
+ /* TODO: more operating classes */
+ if (freq >= 2412 && freq <= 2472) {
+ *reg_class = 81; /* 2.407 GHz, channels 1..13 */
+ *channel = (freq - 2407) / 5;
+ return 0;
+ }
+
+ if (freq == 2484) {
+ *reg_class = 82; /* channel 14 */
+ *channel = 14;
+ return 0;
+ }
+
+ if (freq >= 5180 && freq <= 5240) {
+ *reg_class = 115; /* 5 GHz, channels 36..48 */
+ *channel = (freq - 5000) / 5;
+ return 0;
+ }
+
+ if (freq >= 5745 && freq <= 5805) {
+ *reg_class = 124; /* 5 GHz, channels 149..161 */
+ *channel = (freq - 5000) / 5;
+ return 0;
+ }
+
+ return -1;
+}
+
+
+static void p2p_reg_class_intersect(const struct p2p_reg_class *a,
+ const struct p2p_reg_class *b,
+ struct p2p_reg_class *res)
+{
+ size_t i, j;
+
+ res->reg_class = a->reg_class;
+
+ for (i = 0; i < a->channels; i++) {
+ for (j = 0; j < b->channels; j++) {
+ if (a->channel[i] != b->channel[j])
+ continue;
+ res->channel[res->channels] = a->channel[i];
+ res->channels++;
+ if (res->channels == P2P_MAX_REG_CLASS_CHANNELS)
+ return;
+ }
+ }
+}
+
+
+/**
+ * p2p_channels_intersect - Intersection of supported channel lists
+ * @a: First set of supported channels
+ * @b: Second set of supported channels
+ * @res: Data structure for returning the intersection of support channels
+ *
+ * This function can be used to find a common set of supported channels. Both
+ * input channels sets are assumed to use the same country code. If different
+ * country codes are used, the regulatory class numbers may not be matched
+ * correctly and results are undefined.
+ */
+void p2p_channels_intersect(const struct p2p_channels *a,
+ const struct p2p_channels *b,
+ struct p2p_channels *res)
+{
+ size_t i, j;
+
+ os_memset(res, 0, sizeof(*res));
+
+ for (i = 0; i < a->reg_classes; i++) {
+ const struct p2p_reg_class *a_reg = &a->reg_class[i];
+ for (j = 0; j < b->reg_classes; j++) {
+ const struct p2p_reg_class *b_reg = &b->reg_class[j];
+ if (a_reg->reg_class != b_reg->reg_class)
+ continue;
+ p2p_reg_class_intersect(
+ a_reg, b_reg,
+ &res->reg_class[res->reg_classes]);
+ if (res->reg_class[res->reg_classes].channels) {
+ res->reg_classes++;
+ if (res->reg_classes == P2P_MAX_REG_CLASSES)
+ return;
+ }
+ }
+ }
+}
+
+
+/**
+ * p2p_channels_includes - Check whether a channel is included in the list
+ * @channels: List of supported channels
+ * @reg_class: Regulatory class of the channel to search
+ * @channel: Channel number of the channel to search
+ * Returns: 1 if channel was found or 0 if not
+ */
+int p2p_channels_includes(const struct p2p_channels *channels, u8 reg_class,
+ u8 channel)
+{
+ size_t i, j;
+ for (i = 0; i < channels->reg_classes; i++) {
+ const struct p2p_reg_class *reg = &channels->reg_class[i];
+ if (reg->reg_class != reg_class)
+ continue;
+ for (j = 0; j < reg->channels; j++) {
+ if (reg->channel[j] == channel)
+ return 1;
+ }
+ }
+ return 0;
+}
+
+
+int p2p_supported_freq(struct p2p_data *p2p, unsigned int freq)
+{
+ u8 op_reg_class, op_channel;
+ if (p2p_freq_to_channel(p2p->cfg->country, freq,
+ &op_reg_class, &op_channel) < 0)
+ return 0;
+ return p2p_channels_includes(&p2p->cfg->channels, op_reg_class,
+ op_channel);
+}
diff --git a/contrib/wpa/src/radius/.gitignore b/contrib/wpa/src/radius/.gitignore
deleted file mode 100644
index a89a1f9..0000000
--- a/contrib/wpa/src/radius/.gitignore
+++ /dev/null
@@ -1 +0,0 @@
-libradius.a
diff --git a/contrib/wpa/src/radius/Makefile b/contrib/wpa/src/radius/Makefile
deleted file mode 100644
index b199be8..0000000
--- a/contrib/wpa/src/radius/Makefile
+++ /dev/null
@@ -1,22 +0,0 @@
-all: libradius.a
-
-clean:
- rm -f *~ *.o *.d libradius.a
-
-install:
- @echo Nothing to be made.
-
-
-include ../lib.rules
-
-CFLAGS += -DCONFIG_IPV6
-
-LIB_OBJS= \
- radius.o \
- radius_client.o \
- radius_server.o
-
-libradius.a: $(LIB_OBJS)
- $(AR) crT $@ $?
-
--include $(OBJS:%.o=%.d)
diff --git a/contrib/wpa/src/radius/radius.c b/contrib/wpa/src/radius/radius.c
index 70754ef..d1feec9 100644
--- a/contrib/wpa/src/radius/radius.c
+++ b/contrib/wpa/src/radius/radius.c
@@ -1,15 +1,9 @@
/*
* RADIUS message processing
- * Copyright (c) 2002-2009, Jouni Malinen <j@w1.fi>
+ * Copyright (c) 2002-2009, 2011-2012, Jouni Malinen <j@w1.fi>
*
- * This program is free software; you can redistribute it and/or modify
- * it under the terms of the GNU General Public License version 2 as
- * published by the Free Software Foundation.
- *
- * Alternatively, this software may be distributed under the terms of BSD
- * license.
- *
- * See README and COPYING for more details.
+ * This software may be distributed under the terms of the BSD license.
+ * See README for more details.
*/
#include "utils/includes.h"
@@ -84,8 +78,8 @@ static void radius_msg_set_hdr(struct radius_msg *msg, u8 code, u8 identifier)
static int radius_msg_initialize(struct radius_msg *msg)
{
- msg->attr_pos =
- os_zalloc(RADIUS_DEFAULT_ATTR_COUNT * sizeof(*msg->attr_pos));
+ msg->attr_pos = os_calloc(RADIUS_DEFAULT_ATTR_COUNT,
+ sizeof(*msg->attr_pos));
if (msg->attr_pos == NULL)
return -1;
@@ -153,6 +147,12 @@ static const char *radius_code_string(u8 code)
case RADIUS_CODE_STATUS_SERVER: return "Status-Server";
case RADIUS_CODE_STATUS_CLIENT: return "Status-Client";
case RADIUS_CODE_RESERVED: return "Reserved";
+ case RADIUS_CODE_DISCONNECT_REQUEST: return "Disconnect-Request";
+ case RADIUS_CODE_DISCONNECT_ACK: return "Disconnect-ACK";
+ case RADIUS_CODE_DISCONNECT_NAK: return "Disconnect-NAK";
+ case RADIUS_CODE_COA_REQUEST: return "CoA-Request";
+ case RADIUS_CODE_COA_ACK: return "CoA-ACK";
+ case RADIUS_CODE_COA_NAK: return "CoA-NAK";
default: return "?Unknown?";
}
}
@@ -218,6 +218,8 @@ static struct radius_attr_type radius_attrs[] =
{ RADIUS_ATTR_TUNNEL_TYPE, "Tunnel-Type", RADIUS_ATTR_HEXDUMP },
{ RADIUS_ATTR_TUNNEL_MEDIUM_TYPE, "Tunnel-Medium-Type",
RADIUS_ATTR_HEXDUMP },
+ { RADIUS_ATTR_TUNNEL_PASSWORD, "Tunnel-Password",
+ RADIUS_ATTR_UNDIST },
{ RADIUS_ATTR_CONNECT_INFO, "Connect-Info", RADIUS_ATTR_TEXT },
{ RADIUS_ATTR_EAP_MESSAGE, "EAP-Message", RADIUS_ATTR_UNDIST },
{ RADIUS_ATTR_MESSAGE_AUTHENTICATOR, "Message-Authenticator",
@@ -226,9 +228,10 @@ static struct radius_attr_type radius_attrs[] =
RADIUS_ATTR_HEXDUMP },
{ RADIUS_ATTR_ACCT_INTERIM_INTERVAL, "Acct-Interim-Interval",
RADIUS_ATTR_INT32 },
- { RADIUS_ATTR_CHARGEABLE_USER_IDENTITY, "Chargable-User-Identity",
+ { RADIUS_ATTR_CHARGEABLE_USER_IDENTITY, "Chargeable-User-Identity",
RADIUS_ATTR_TEXT },
{ RADIUS_ATTR_NAS_IPV6_ADDRESS, "NAS-IPv6-Address", RADIUS_ATTR_IPV6 },
+ { RADIUS_ATTR_ERROR_CAUSE, "Error-Cause", RADIUS_ATTR_INT32 }
};
#define RADIUS_ATTRS (sizeof(radius_attrs) / sizeof(radius_attrs[0]))
@@ -266,7 +269,7 @@ static void radius_msg_dump_attr(struct radius_attr_hdr *hdr)
printf(" Attribute %d (%s) length=%d\n",
hdr->type, attr ? attr->name : "?Unknown?", hdr->length);
- if (attr == NULL)
+ if (attr == NULL || hdr->length < sizeof(struct radius_attr_hdr))
return;
len = hdr->length - sizeof(struct radius_attr_hdr);
@@ -329,7 +332,7 @@ void radius_msg_dump(struct radius_msg *msg)
printf("RADIUS message: code=%d (%s) identifier=%d length=%d\n",
msg->hdr->code, radius_code_string(msg->hdr->code),
- msg->hdr->identifier, ntohs(msg->hdr->length));
+ msg->hdr->identifier, be_to_host16(msg->hdr->length));
for (i = 0; i < msg->attr_used; i++) {
struct radius_attr_hdr *attr = radius_get_attr_hdr(msg, i);
@@ -354,11 +357,11 @@ int radius_msg_finish(struct radius_msg *msg, const u8 *secret,
"Message-Authenticator");
return -1;
}
- msg->hdr->length = htons(wpabuf_len(msg->buf));
+ msg->hdr->length = host_to_be16(wpabuf_len(msg->buf));
hmac_md5(secret, secret_len, wpabuf_head(msg->buf),
wpabuf_len(msg->buf), (u8 *) (attr + 1));
} else
- msg->hdr->length = htons(wpabuf_len(msg->buf));
+ msg->hdr->length = host_to_be16(wpabuf_len(msg->buf));
if (wpabuf_len(msg->buf) > 0xffff) {
wpa_printf(MSG_WARNING, "RADIUS: Too long message (%lu)",
@@ -384,7 +387,7 @@ int radius_msg_finish_srv(struct radius_msg *msg, const u8 *secret,
printf("WARNING: Could not add Message-Authenticator\n");
return -1;
}
- msg->hdr->length = htons(wpabuf_len(msg->buf));
+ msg->hdr->length = host_to_be16(wpabuf_len(msg->buf));
os_memcpy(msg->hdr->authenticator, req_authenticator,
sizeof(msg->hdr->authenticator));
hmac_md5(secret, secret_len, wpabuf_head(msg->buf),
@@ -410,13 +413,52 @@ int radius_msg_finish_srv(struct radius_msg *msg, const u8 *secret,
}
+int radius_msg_finish_das_resp(struct radius_msg *msg, const u8 *secret,
+ size_t secret_len,
+ const struct radius_hdr *req_hdr)
+{
+ const u8 *addr[2];
+ size_t len[2];
+ u8 auth[MD5_MAC_LEN];
+ struct radius_attr_hdr *attr;
+
+ os_memset(auth, 0, MD5_MAC_LEN);
+ attr = radius_msg_add_attr(msg, RADIUS_ATTR_MESSAGE_AUTHENTICATOR,
+ auth, MD5_MAC_LEN);
+ if (attr == NULL) {
+ wpa_printf(MSG_WARNING, "Could not add Message-Authenticator");
+ return -1;
+ }
+
+ msg->hdr->length = host_to_be16(wpabuf_len(msg->buf));
+ os_memcpy(msg->hdr->authenticator, req_hdr->authenticator, 16);
+ hmac_md5(secret, secret_len, wpabuf_head(msg->buf),
+ wpabuf_len(msg->buf), (u8 *) (attr + 1));
+
+ /* ResponseAuth = MD5(Code+ID+Length+RequestAuth+Attributes+Secret) */
+ addr[0] = wpabuf_head_u8(msg->buf);
+ len[0] = wpabuf_len(msg->buf);
+ addr[1] = secret;
+ len[1] = secret_len;
+ if (md5_vector(2, addr, len, msg->hdr->authenticator) < 0)
+ return -1;
+
+ if (wpabuf_len(msg->buf) > 0xffff) {
+ wpa_printf(MSG_WARNING, "RADIUS: Too long message (%lu)",
+ (unsigned long) wpabuf_len(msg->buf));
+ return -1;
+ }
+ return 0;
+}
+
+
void radius_msg_finish_acct(struct radius_msg *msg, const u8 *secret,
size_t secret_len)
{
const u8 *addr[2];
size_t len[2];
- msg->hdr->length = htons(wpabuf_len(msg->buf));
+ msg->hdr->length = host_to_be16(wpabuf_len(msg->buf));
os_memset(msg->hdr->authenticator, 0, MD5_MAC_LEN);
addr[0] = wpabuf_head(msg->buf);
len[0] = wpabuf_len(msg->buf);
@@ -431,6 +473,88 @@ void radius_msg_finish_acct(struct radius_msg *msg, const u8 *secret,
}
+int radius_msg_verify_acct_req(struct radius_msg *msg, const u8 *secret,
+ size_t secret_len)
+{
+ const u8 *addr[4];
+ size_t len[4];
+ u8 zero[MD5_MAC_LEN];
+ u8 hash[MD5_MAC_LEN];
+
+ os_memset(zero, 0, sizeof(zero));
+ addr[0] = (u8 *) msg->hdr;
+ len[0] = sizeof(struct radius_hdr) - MD5_MAC_LEN;
+ addr[1] = zero;
+ len[1] = MD5_MAC_LEN;
+ addr[2] = (u8 *) (msg->hdr + 1);
+ len[2] = wpabuf_len(msg->buf) - sizeof(struct radius_hdr);
+ addr[3] = secret;
+ len[3] = secret_len;
+ md5_vector(4, addr, len, hash);
+ return os_memcmp(msg->hdr->authenticator, hash, MD5_MAC_LEN) != 0;
+}
+
+
+int radius_msg_verify_das_req(struct radius_msg *msg, const u8 *secret,
+ size_t secret_len)
+{
+ const u8 *addr[4];
+ size_t len[4];
+ u8 zero[MD5_MAC_LEN];
+ u8 hash[MD5_MAC_LEN];
+ u8 auth[MD5_MAC_LEN], orig[MD5_MAC_LEN];
+ u8 orig_authenticator[16];
+
+ struct radius_attr_hdr *attr = NULL, *tmp;
+ size_t i;
+
+ os_memset(zero, 0, sizeof(zero));
+ addr[0] = (u8 *) msg->hdr;
+ len[0] = sizeof(struct radius_hdr) - MD5_MAC_LEN;
+ addr[1] = zero;
+ len[1] = MD5_MAC_LEN;
+ addr[2] = (u8 *) (msg->hdr + 1);
+ len[2] = wpabuf_len(msg->buf) - sizeof(struct radius_hdr);
+ addr[3] = secret;
+ len[3] = secret_len;
+ md5_vector(4, addr, len, hash);
+ if (os_memcmp(msg->hdr->authenticator, hash, MD5_MAC_LEN) != 0)
+ return 1;
+
+ for (i = 0; i < msg->attr_used; i++) {
+ tmp = radius_get_attr_hdr(msg, i);
+ if (tmp->type == RADIUS_ATTR_MESSAGE_AUTHENTICATOR) {
+ if (attr != NULL) {
+ wpa_printf(MSG_WARNING, "Multiple "
+ "Message-Authenticator attributes "
+ "in RADIUS message");
+ return 1;
+ }
+ attr = tmp;
+ }
+ }
+
+ if (attr == NULL) {
+ /* Message-Authenticator is MAY; not required */
+ return 0;
+ }
+
+ os_memcpy(orig, attr + 1, MD5_MAC_LEN);
+ os_memset(attr + 1, 0, MD5_MAC_LEN);
+ os_memcpy(orig_authenticator, msg->hdr->authenticator,
+ sizeof(orig_authenticator));
+ os_memset(msg->hdr->authenticator, 0,
+ sizeof(msg->hdr->authenticator));
+ hmac_md5(secret, secret_len, wpabuf_head(msg->buf),
+ wpabuf_len(msg->buf), auth);
+ os_memcpy(attr + 1, orig, MD5_MAC_LEN);
+ os_memcpy(msg->hdr->authenticator, orig_authenticator,
+ sizeof(orig_authenticator));
+
+ return os_memcmp(orig, auth, MD5_MAC_LEN) != 0;
+}
+
+
static int radius_msg_add_attr_to_array(struct radius_msg *msg,
struct radius_attr_hdr *attr)
{
@@ -438,8 +562,8 @@ static int radius_msg_add_attr_to_array(struct radius_msg *msg,
size_t *nattr_pos;
int nlen = msg->attr_size * 2;
- nattr_pos = os_realloc(msg->attr_pos,
- nlen * sizeof(*msg->attr_pos));
+ nattr_pos = os_realloc_array(msg->attr_pos, nlen,
+ sizeof(*msg->attr_pos));
if (nattr_pos == NULL)
return -1;
@@ -509,7 +633,7 @@ struct radius_msg * radius_msg_parse(const u8 *data, size_t len)
hdr = (struct radius_hdr *) data;
- msg_len = ntohs(hdr->length);
+ msg_len = be_to_host16(hdr->length);
if (msg_len < sizeof(*hdr) || msg_len > len) {
wpa_printf(MSG_INFO, "RADIUS: Invalid message length");
return NULL;
@@ -583,9 +707,9 @@ int radius_msg_add_eap(struct radius_msg *msg, const u8 *data, size_t data_len)
}
-u8 *radius_msg_get_eap(struct radius_msg *msg, size_t *eap_len)
+struct wpabuf * radius_msg_get_eap(struct radius_msg *msg)
{
- u8 *eap, *pos;
+ struct wpabuf *eap;
size_t len, i;
struct radius_attr_hdr *attr;
@@ -595,30 +719,27 @@ u8 *radius_msg_get_eap(struct radius_msg *msg, size_t *eap_len)
len = 0;
for (i = 0; i < msg->attr_used; i++) {
attr = radius_get_attr_hdr(msg, i);
- if (attr->type == RADIUS_ATTR_EAP_MESSAGE)
+ if (attr->type == RADIUS_ATTR_EAP_MESSAGE &&
+ attr->length > sizeof(struct radius_attr_hdr))
len += attr->length - sizeof(struct radius_attr_hdr);
}
if (len == 0)
return NULL;
- eap = os_malloc(len);
+ eap = wpabuf_alloc(len);
if (eap == NULL)
return NULL;
- pos = eap;
for (i = 0; i < msg->attr_used; i++) {
attr = radius_get_attr_hdr(msg, i);
- if (attr->type == RADIUS_ATTR_EAP_MESSAGE) {
+ if (attr->type == RADIUS_ATTR_EAP_MESSAGE &&
+ attr->length > sizeof(struct radius_attr_hdr)) {
int flen = attr->length - sizeof(*attr);
- os_memcpy(pos, attr + 1, flen);
- pos += flen;
+ wpabuf_put_data(eap, attr + 1, flen);
}
}
- if (eap_len)
- *eap_len = len;
-
return eap;
}
@@ -719,7 +840,7 @@ int radius_msg_copy_attr(struct radius_msg *dst, struct radius_msg *src,
for (i = 0; i < src->attr_used; i++) {
attr = radius_get_attr_hdr(src, i);
- if (attr->type == type) {
+ if (attr->type == type && attr->length >= sizeof(*attr)) {
if (!radius_msg_add_attr(dst, type, (u8 *) (attr + 1),
attr->length - sizeof(*attr)))
return -1;
@@ -776,7 +897,8 @@ static u8 *radius_msg_get_vendor_attr(struct radius_msg *msg, u32 vendor,
u32 vendor_id;
struct radius_attr_vendor *vhdr;
- if (attr->type != RADIUS_ATTR_VENDOR_SPECIFIC)
+ if (attr->type != RADIUS_ATTR_VENDOR_SPECIFIC ||
+ attr->length < sizeof(*attr))
continue;
left = attr->length - sizeof(*attr);
@@ -1090,8 +1212,7 @@ radius_msg_add_attr_user_password(struct radius_msg *msg,
const u8 *secret, size_t secret_len)
{
u8 buf[128];
- int padlen, i;
- size_t buf_len, pos;
+ size_t padlen, i, buf_len, pos;
const u8 *addr[2];
size_t len[2];
u8 hash[16];
@@ -1103,7 +1224,7 @@ radius_msg_add_attr_user_password(struct radius_msg *msg,
buf_len = data_len;
padlen = data_len % 16;
- if (padlen) {
+ if (padlen && data_len < sizeof(buf)) {
padlen = 16 - padlen;
os_memset(buf + data_len, 0, padlen);
buf_len += padlen;
@@ -1150,7 +1271,7 @@ int radius_msg_get_attr(struct radius_msg *msg, u8 type, u8 *buf, size_t len)
}
}
- if (!attr)
+ if (!attr || attr->length < sizeof(*attr))
return -1;
dlen = attr->length - sizeof(*attr);
@@ -1175,7 +1296,7 @@ int radius_msg_get_attr_ptr(struct radius_msg *msg, u8 type, u8 **buf,
}
}
- if (!attr)
+ if (!attr || attr->length < sizeof(*attr))
return -1;
*buf = (u8 *) (attr + 1);
@@ -1226,6 +1347,8 @@ int radius_msg_get_vlanid(struct radius_msg *msg)
for (i = 0; i < msg->attr_used; i++) {
attr = radius_get_attr_hdr(msg, i);
+ if (attr->length < sizeof(*attr))
+ return -1;
data = (const u8 *) (attr + 1);
dlen = attr->length - sizeof(*attr);
if (attr->length < 3)
@@ -1276,6 +1399,123 @@ int radius_msg_get_vlanid(struct radius_msg *msg)
}
+/**
+ * radius_msg_get_tunnel_password - Parse RADIUS attribute Tunnel-Password
+ * @msg: Received RADIUS message
+ * @keylen: Length of returned password
+ * @secret: RADIUS shared secret
+ * @secret_len: Length of secret
+ * @sent_msg: Sent RADIUS message
+ * @n: Number of password attribute to return (starting with 0)
+ * Returns: Pointer to n-th password (free with os_free) or %NULL
+ */
+char * radius_msg_get_tunnel_password(struct radius_msg *msg, int *keylen,
+ const u8 *secret, size_t secret_len,
+ struct radius_msg *sent_msg, size_t n)
+{
+ u8 *buf = NULL;
+ size_t buflen;
+ const u8 *salt;
+ u8 *str;
+ const u8 *addr[3];
+ size_t len[3];
+ u8 hash[16];
+ u8 *pos;
+ size_t i, j = 0;
+ struct radius_attr_hdr *attr;
+ const u8 *data;
+ size_t dlen;
+ const u8 *fdata = NULL; /* points to found item */
+ size_t fdlen = -1;
+ char *ret = NULL;
+
+ /* find n-th valid Tunnel-Password attribute */
+ for (i = 0; i < msg->attr_used; i++) {
+ attr = radius_get_attr_hdr(msg, i);
+ if (attr == NULL ||
+ attr->type != RADIUS_ATTR_TUNNEL_PASSWORD) {
+ continue;
+ }
+ if (attr->length <= 5)
+ continue;
+ data = (const u8 *) (attr + 1);
+ dlen = attr->length - sizeof(*attr);
+ if (dlen <= 3 || dlen % 16 != 3)
+ continue;
+ j++;
+ if (j <= n)
+ continue;
+
+ fdata = data;
+ fdlen = dlen;
+ break;
+ }
+ if (fdata == NULL)
+ goto out;
+
+ /* alloc writable memory for decryption */
+ buf = os_malloc(fdlen);
+ if (buf == NULL)
+ goto out;
+ os_memcpy(buf, fdata, fdlen);
+ buflen = fdlen;
+
+ /* init pointers */
+ salt = buf + 1;
+ str = buf + 3;
+
+ /* decrypt blocks */
+ pos = buf + buflen - 16; /* last block */
+ while (pos >= str + 16) { /* all but the first block */
+ addr[0] = secret;
+ len[0] = secret_len;
+ addr[1] = pos - 16;
+ len[1] = 16;
+ md5_vector(2, addr, len, hash);
+
+ for (i = 0; i < 16; i++)
+ pos[i] ^= hash[i];
+
+ pos -= 16;
+ }
+
+ /* decrypt first block */
+ if (str != pos)
+ goto out;
+ addr[0] = secret;
+ len[0] = secret_len;
+ addr[1] = sent_msg->hdr->authenticator;
+ len[1] = 16;
+ addr[2] = salt;
+ len[2] = 2;
+ md5_vector(3, addr, len, hash);
+
+ for (i = 0; i < 16; i++)
+ pos[i] ^= hash[i];
+
+ /* derive plaintext length from first subfield */
+ *keylen = (unsigned char) str[0];
+ if ((u8 *) (str + *keylen) >= (u8 *) (buf + buflen)) {
+ /* decryption error - invalid key length */
+ goto out;
+ }
+ if (*keylen == 0) {
+ /* empty password */
+ goto out;
+ }
+
+ /* copy passphrase into new buffer */
+ ret = os_malloc(*keylen);
+ if (ret)
+ os_memcpy(ret, str + 1, *keylen);
+
+out:
+ /* return new buffer */
+ os_free(buf);
+ return ret;
+}
+
+
void radius_free_class(struct radius_class_data *c)
{
size_t i;
@@ -1297,7 +1537,7 @@ int radius_copy_class(struct radius_class_data *dst,
if (src->attr == NULL)
return 0;
- dst->attr = os_zalloc(src->count * sizeof(struct radius_attr_data));
+ dst->attr = os_calloc(src->count, sizeof(struct radius_attr_data));
if (dst->attr == NULL)
return -1;
@@ -1315,3 +1555,24 @@ int radius_copy_class(struct radius_class_data *dst,
return 0;
}
+
+
+u8 radius_msg_find_unlisted_attr(struct radius_msg *msg, u8 *attrs)
+{
+ size_t i, j;
+ struct radius_attr_hdr *attr;
+
+ for (i = 0; i < msg->attr_used; i++) {
+ attr = radius_get_attr_hdr(msg, i);
+
+ for (j = 0; attrs[j]; j++) {
+ if (attr->type == attrs[j])
+ break;
+ }
+
+ if (attrs[j] == 0)
+ return attr->type; /* unlisted attr */
+ }
+
+ return 0;
+}
diff --git a/contrib/wpa/src/radius/radius.h b/contrib/wpa/src/radius/radius.h
index a3cdac0..2031054 100644
--- a/contrib/wpa/src/radius/radius.h
+++ b/contrib/wpa/src/radius/radius.h
@@ -1,15 +1,9 @@
/*
* RADIUS message processing
- * Copyright (c) 2002-2009, Jouni Malinen <j@w1.fi>
+ * Copyright (c) 2002-2009, 2012, Jouni Malinen <j@w1.fi>
*
- * This program is free software; you can redistribute it and/or modify
- * it under the terms of the GNU General Public License version 2 as
- * published by the Free Software Foundation.
- *
- * Alternatively, this software may be distributed under the terms of BSD
- * license.
- *
- * See README and COPYING for more details.
+ * This software may be distributed under the terms of the BSD license.
+ * See README for more details.
*/
#ifndef RADIUS_H
@@ -24,7 +18,7 @@
struct radius_hdr {
u8 code;
u8 identifier;
- u16 length; /* including this header */
+ be16 length; /* including this header */
u8 authenticator[16];
/* followed by length-20 octets of attributes */
} STRUCT_PACKED;
@@ -37,6 +31,12 @@ enum { RADIUS_CODE_ACCESS_REQUEST = 1,
RADIUS_CODE_ACCESS_CHALLENGE = 11,
RADIUS_CODE_STATUS_SERVER = 12,
RADIUS_CODE_STATUS_CLIENT = 13,
+ RADIUS_CODE_DISCONNECT_REQUEST = 40,
+ RADIUS_CODE_DISCONNECT_ACK = 41,
+ RADIUS_CODE_DISCONNECT_NAK = 42,
+ RADIUS_CODE_COA_REQUEST = 43,
+ RADIUS_CODE_COA_ACK = 44,
+ RADIUS_CODE_COA_NAK = 45,
RADIUS_CODE_RESERVED = 255
};
@@ -82,13 +82,15 @@ enum { RADIUS_ATTR_USER_NAME = 1,
RADIUS_ATTR_NAS_PORT_TYPE = 61,
RADIUS_ATTR_TUNNEL_TYPE = 64,
RADIUS_ATTR_TUNNEL_MEDIUM_TYPE = 65,
+ RADIUS_ATTR_TUNNEL_PASSWORD = 69,
RADIUS_ATTR_CONNECT_INFO = 77,
RADIUS_ATTR_EAP_MESSAGE = 79,
RADIUS_ATTR_MESSAGE_AUTHENTICATOR = 80,
RADIUS_ATTR_TUNNEL_PRIVATE_GROUP_ID = 81,
RADIUS_ATTR_ACCT_INTERIM_INTERVAL = 85,
RADIUS_ATTR_CHARGEABLE_USER_IDENTITY = 89,
- RADIUS_ATTR_NAS_IPV6_ADDRESS = 95
+ RADIUS_ATTR_NAS_IPV6_ADDRESS = 95,
+ RADIUS_ATTR_ERROR_CAUSE = 101
};
@@ -197,14 +199,21 @@ int radius_msg_finish(struct radius_msg *msg, const u8 *secret,
size_t secret_len);
int radius_msg_finish_srv(struct radius_msg *msg, const u8 *secret,
size_t secret_len, const u8 *req_authenticator);
+int radius_msg_finish_das_resp(struct radius_msg *msg, const u8 *secret,
+ size_t secret_len,
+ const struct radius_hdr *req_hdr);
void radius_msg_finish_acct(struct radius_msg *msg, const u8 *secret,
size_t secret_len);
+int radius_msg_verify_acct_req(struct radius_msg *msg, const u8 *secret,
+ size_t secret_len);
+int radius_msg_verify_das_req(struct radius_msg *msg, const u8 *secret,
+ size_t secret_len);
struct radius_attr_hdr * radius_msg_add_attr(struct radius_msg *msg, u8 type,
const u8 *data, size_t data_len);
struct radius_msg * radius_msg_parse(const u8 *data, size_t len);
int radius_msg_add_eap(struct radius_msg *msg, const u8 *data,
size_t data_len);
-u8 *radius_msg_get_eap(struct radius_msg *msg, size_t *len);
+struct wpabuf * radius_msg_get_eap(struct radius_msg *msg);
int radius_msg_verify(struct radius_msg *msg, const u8 *secret,
size_t secret_len, struct radius_msg *sent_msg,
int auth);
@@ -231,6 +240,9 @@ radius_msg_add_attr_user_password(struct radius_msg *msg,
const u8 *secret, size_t secret_len);
int radius_msg_get_attr(struct radius_msg *msg, u8 type, u8 *buf, size_t len);
int radius_msg_get_vlanid(struct radius_msg *msg);
+char * radius_msg_get_tunnel_password(struct radius_msg *msg, int *keylen,
+ const u8 *secret, size_t secret_len,
+ struct radius_msg *sent_msg, size_t n);
static inline int radius_msg_add_attr_int32(struct radius_msg *msg, u8 type,
u32 value)
@@ -270,4 +282,6 @@ void radius_free_class(struct radius_class_data *c);
int radius_copy_class(struct radius_class_data *dst,
const struct radius_class_data *src);
+u8 radius_msg_find_unlisted_attr(struct radius_msg *msg, u8 *attrs);
+
#endif /* RADIUS_H */
diff --git a/contrib/wpa/src/radius/radius_client.c b/contrib/wpa/src/radius/radius_client.c
index 171af29..425ad93 100644
--- a/contrib/wpa/src/radius/radius_client.c
+++ b/contrib/wpa/src/radius/radius_client.c
@@ -2,14 +2,8 @@
* RADIUS client
* Copyright (c) 2002-2009, Jouni Malinen <j@w1.fi>
*
- * This program is free software; you can redistribute it and/or modify
- * it under the terms of the GNU General Public License version 2 as
- * published by the Free Software Foundation.
- *
- * Alternatively, this software may be distributed under the terms of BSD
- * license.
- *
- * See README and COPYING for more details.
+ * This software may be distributed under the terms of the BSD license.
+ * See README for more details.
*/
#include "includes.h"
@@ -287,8 +281,8 @@ int radius_client_register(struct radius_client_data *radius,
num = &radius->num_auth_handlers;
}
- newh = os_realloc(*handlers,
- (*num + 1) * sizeof(struct radius_rx_handler));
+ newh = os_realloc_array(*handlers, *num + 1,
+ sizeof(struct radius_rx_handler));
if (newh == NULL)
return -1;
@@ -511,7 +505,7 @@ static void radius_client_update_timeout(struct radius_client_data *radius)
NULL);
hostapd_logger(radius->ctx, NULL, HOSTAPD_MODULE_RADIUS,
HOSTAPD_LEVEL_DEBUG, "Next RADIUS client retransmit in"
- " %ld seconds\n", (long int) (first - now.sec));
+ " %ld seconds", (long int) (first - now.sec));
}
@@ -684,7 +678,7 @@ int radius_client_send(struct radius_client_data *radius,
radius_client_list_add(radius, msg, msg_type, shared_secret,
shared_secret_len, addr);
- return res;
+ return 0;
}
@@ -1489,3 +1483,11 @@ int radius_client_get_mib(struct radius_client_data *radius, char *buf,
return count;
}
+
+
+void radius_client_reconfig(struct radius_client_data *radius,
+ struct hostapd_radius_servers *conf)
+{
+ if (radius)
+ radius->conf = conf;
+}
diff --git a/contrib/wpa/src/radius/radius_client.h b/contrib/wpa/src/radius/radius_client.h
index 644ea23..3db16aa 100644
--- a/contrib/wpa/src/radius/radius_client.h
+++ b/contrib/wpa/src/radius/radius_client.h
@@ -2,14 +2,8 @@
* RADIUS client
* Copyright (c) 2002-2009, Jouni Malinen <j@w1.fi>
*
- * This program is free software; you can redistribute it and/or modify
- * it under the terms of the GNU General Public License version 2 as
- * published by the Free Software Foundation.
- *
- * Alternatively, this software may be distributed under the terms of BSD
- * license.
- *
- * See README and COPYING for more details.
+ * This software may be distributed under the terms of the BSD license.
+ * See README for more details.
*/
#ifndef RADIUS_CLIENT_H
@@ -259,5 +253,7 @@ void radius_client_flush_auth(struct radius_client_data *radius,
const u8 *addr);
int radius_client_get_mib(struct radius_client_data *radius, char *buf,
size_t buflen);
+void radius_client_reconfig(struct radius_client_data *radius,
+ struct hostapd_radius_servers *conf);
#endif /* RADIUS_CLIENT_H */
diff --git a/contrib/wpa/src/radius/radius_das.c b/contrib/wpa/src/radius/radius_das.c
new file mode 100644
index 0000000..bded965
--- /dev/null
+++ b/contrib/wpa/src/radius/radius_das.c
@@ -0,0 +1,364 @@
+/*
+ * RADIUS Dynamic Authorization Server (DAS) (RFC 5176)
+ * Copyright (c) 2012, Jouni Malinen <j@w1.fi>
+ *
+ * This software may be distributed under the terms of the BSD license.
+ * See README for more details.
+ */
+
+#include "includes.h"
+#include <net/if.h>
+
+#include "utils/common.h"
+#include "utils/eloop.h"
+#include "utils/ip_addr.h"
+#include "radius.h"
+#include "radius_das.h"
+
+
+extern int wpa_debug_level;
+
+
+struct radius_das_data {
+ int sock;
+ u8 *shared_secret;
+ size_t shared_secret_len;
+ struct hostapd_ip_addr client_addr;
+ unsigned int time_window;
+ int require_event_timestamp;
+ void *ctx;
+ enum radius_das_res (*disconnect)(void *ctx,
+ struct radius_das_attrs *attr);
+};
+
+
+static struct radius_msg * radius_das_disconnect(struct radius_das_data *das,
+ struct radius_msg *msg,
+ const char *abuf,
+ int from_port)
+{
+ struct radius_hdr *hdr;
+ struct radius_msg *reply;
+ u8 allowed[] = {
+ RADIUS_ATTR_USER_NAME,
+ RADIUS_ATTR_CALLING_STATION_ID,
+ RADIUS_ATTR_ACCT_SESSION_ID,
+ RADIUS_ATTR_EVENT_TIMESTAMP,
+ RADIUS_ATTR_MESSAGE_AUTHENTICATOR,
+ RADIUS_ATTR_CHARGEABLE_USER_IDENTITY,
+ 0
+ };
+ int error = 405;
+ u8 attr;
+ enum radius_das_res res;
+ struct radius_das_attrs attrs;
+ u8 *buf;
+ size_t len;
+ char tmp[100];
+ u8 sta_addr[ETH_ALEN];
+
+ hdr = radius_msg_get_hdr(msg);
+
+ attr = radius_msg_find_unlisted_attr(msg, allowed);
+ if (attr) {
+ wpa_printf(MSG_INFO, "DAS: Unsupported attribute %u in "
+ "Disconnect-Request from %s:%d", attr,
+ abuf, from_port);
+ error = 401;
+ goto fail;
+ }
+
+ os_memset(&attrs, 0, sizeof(attrs));
+
+ if (radius_msg_get_attr_ptr(msg, RADIUS_ATTR_CALLING_STATION_ID,
+ &buf, &len, NULL) == 0) {
+ if (len >= sizeof(tmp))
+ len = sizeof(tmp) - 1;
+ os_memcpy(tmp, buf, len);
+ tmp[len] = '\0';
+ if (hwaddr_aton2(tmp, sta_addr) < 0) {
+ wpa_printf(MSG_INFO, "DAS: Invalid Calling-Station-Id "
+ "'%s' from %s:%d", tmp, abuf, from_port);
+ error = 407;
+ goto fail;
+ }
+ attrs.sta_addr = sta_addr;
+ }
+
+ if (radius_msg_get_attr_ptr(msg, RADIUS_ATTR_USER_NAME,
+ &buf, &len, NULL) == 0) {
+ attrs.user_name = buf;
+ attrs.user_name_len = len;
+ }
+
+ if (radius_msg_get_attr_ptr(msg, RADIUS_ATTR_ACCT_SESSION_ID,
+ &buf, &len, NULL) == 0) {
+ attrs.acct_session_id = buf;
+ attrs.acct_session_id_len = len;
+ }
+
+ if (radius_msg_get_attr_ptr(msg, RADIUS_ATTR_CHARGEABLE_USER_IDENTITY,
+ &buf, &len, NULL) == 0) {
+ attrs.cui = buf;
+ attrs.cui_len = len;
+ }
+
+ res = das->disconnect(das->ctx, &attrs);
+ switch (res) {
+ case RADIUS_DAS_NAS_MISMATCH:
+ wpa_printf(MSG_INFO, "DAS: NAS mismatch from %s:%d",
+ abuf, from_port);
+ error = 403;
+ break;
+ case RADIUS_DAS_SESSION_NOT_FOUND:
+ wpa_printf(MSG_INFO, "DAS: Session not found for request from "
+ "%s:%d", abuf, from_port);
+ error = 503;
+ break;
+ case RADIUS_DAS_SUCCESS:
+ error = 0;
+ break;
+ }
+
+fail:
+ reply = radius_msg_new(error ? RADIUS_CODE_DISCONNECT_NAK :
+ RADIUS_CODE_DISCONNECT_ACK, hdr->identifier);
+ if (reply == NULL)
+ return NULL;
+
+ if (error) {
+ if (!radius_msg_add_attr_int32(reply, RADIUS_ATTR_ERROR_CAUSE,
+ error)) {
+ radius_msg_free(reply);
+ return NULL;
+ }
+ }
+
+ return reply;
+}
+
+
+static void radius_das_receive(int sock, void *eloop_ctx, void *sock_ctx)
+{
+ struct radius_das_data *das = eloop_ctx;
+ u8 buf[1500];
+ union {
+ struct sockaddr_storage ss;
+ struct sockaddr_in sin;
+#ifdef CONFIG_IPV6
+ struct sockaddr_in6 sin6;
+#endif /* CONFIG_IPV6 */
+ } from;
+ char abuf[50];
+ int from_port = 0;
+ socklen_t fromlen;
+ int len;
+ struct radius_msg *msg, *reply = NULL;
+ struct radius_hdr *hdr;
+ struct wpabuf *rbuf;
+ u32 val;
+ int res;
+ struct os_time now;
+
+ fromlen = sizeof(from);
+ len = recvfrom(sock, buf, sizeof(buf), 0,
+ (struct sockaddr *) &from.ss, &fromlen);
+ if (len < 0) {
+ wpa_printf(MSG_ERROR, "DAS: recvfrom: %s", strerror(errno));
+ return;
+ }
+
+ os_strlcpy(abuf, inet_ntoa(from.sin.sin_addr), sizeof(abuf));
+ from_port = ntohs(from.sin.sin_port);
+
+ wpa_printf(MSG_DEBUG, "DAS: Received %d bytes from %s:%d",
+ len, abuf, from_port);
+ if (das->client_addr.u.v4.s_addr != from.sin.sin_addr.s_addr) {
+ wpa_printf(MSG_DEBUG, "DAS: Drop message from unknown client");
+ return;
+ }
+
+ msg = radius_msg_parse(buf, len);
+ if (msg == NULL) {
+ wpa_printf(MSG_DEBUG, "DAS: Parsing incoming RADIUS packet "
+ "from %s:%d failed", abuf, from_port);
+ return;
+ }
+
+ if (wpa_debug_level <= MSG_MSGDUMP)
+ radius_msg_dump(msg);
+
+ if (radius_msg_verify_das_req(msg, das->shared_secret,
+ das->shared_secret_len)) {
+ wpa_printf(MSG_DEBUG, "DAS: Invalid authenticator in packet "
+ "from %s:%d - drop", abuf, from_port);
+ goto fail;
+ }
+
+ os_get_time(&now);
+ res = radius_msg_get_attr(msg, RADIUS_ATTR_EVENT_TIMESTAMP,
+ (u8 *) &val, 4);
+ if (res == 4) {
+ u32 timestamp = ntohl(val);
+ if (abs(now.sec - timestamp) > das->time_window) {
+ wpa_printf(MSG_DEBUG, "DAS: Unacceptable "
+ "Event-Timestamp (%u; local time %u) in "
+ "packet from %s:%d - drop",
+ timestamp, (unsigned int) now.sec,
+ abuf, from_port);
+ goto fail;
+ }
+ } else if (das->require_event_timestamp) {
+ wpa_printf(MSG_DEBUG, "DAS: Missing Event-Timestamp in packet "
+ "from %s:%d - drop", abuf, from_port);
+ goto fail;
+ }
+
+ hdr = radius_msg_get_hdr(msg);
+
+ switch (hdr->code) {
+ case RADIUS_CODE_DISCONNECT_REQUEST:
+ reply = radius_das_disconnect(das, msg, abuf, from_port);
+ break;
+ case RADIUS_CODE_COA_REQUEST:
+ /* TODO */
+ reply = radius_msg_new(RADIUS_CODE_COA_NAK,
+ hdr->identifier);
+ if (reply == NULL)
+ break;
+
+ /* Unsupported Service */
+ if (!radius_msg_add_attr_int32(reply, RADIUS_ATTR_ERROR_CAUSE,
+ 405)) {
+ radius_msg_free(reply);
+ reply = NULL;
+ break;
+ }
+ break;
+ default:
+ wpa_printf(MSG_DEBUG, "DAS: Unexpected RADIUS code %u in "
+ "packet from %s:%d",
+ hdr->code, abuf, from_port);
+ }
+
+ if (reply) {
+ wpa_printf(MSG_DEBUG, "DAS: Reply to %s:%d", abuf, from_port);
+
+ if (!radius_msg_add_attr_int32(reply,
+ RADIUS_ATTR_EVENT_TIMESTAMP,
+ now.sec)) {
+ wpa_printf(MSG_DEBUG, "DAS: Failed to add "
+ "Event-Timestamp attribute");
+ }
+
+ if (radius_msg_finish_das_resp(reply, das->shared_secret,
+ das->shared_secret_len, hdr) <
+ 0) {
+ wpa_printf(MSG_DEBUG, "DAS: Failed to add "
+ "Message-Authenticator attribute");
+ }
+
+ if (wpa_debug_level <= MSG_MSGDUMP)
+ radius_msg_dump(reply);
+
+ rbuf = radius_msg_get_buf(reply);
+ res = sendto(das->sock, wpabuf_head(rbuf),
+ wpabuf_len(rbuf), 0,
+ (struct sockaddr *) &from.ss, fromlen);
+ if (res < 0) {
+ wpa_printf(MSG_ERROR, "DAS: sendto(to %s:%d): %s",
+ abuf, from_port, strerror(errno));
+ }
+ }
+
+fail:
+ radius_msg_free(msg);
+ radius_msg_free(reply);
+}
+
+
+static int radius_das_open_socket(int port)
+{
+ int s;
+ struct sockaddr_in addr;
+
+ s = socket(PF_INET, SOCK_DGRAM, 0);
+ if (s < 0) {
+ perror("socket");
+ return -1;
+ }
+
+ os_memset(&addr, 0, sizeof(addr));
+ addr.sin_family = AF_INET;
+ addr.sin_port = htons(port);
+ if (bind(s, (struct sockaddr *) &addr, sizeof(addr)) < 0) {
+ perror("bind");
+ close(s);
+ return -1;
+ }
+
+ return s;
+}
+
+
+struct radius_das_data *
+radius_das_init(struct radius_das_conf *conf)
+{
+ struct radius_das_data *das;
+
+ if (conf->port == 0 || conf->shared_secret == NULL ||
+ conf->client_addr == NULL)
+ return NULL;
+
+ das = os_zalloc(sizeof(*das));
+ if (das == NULL)
+ return NULL;
+
+ das->time_window = conf->time_window;
+ das->require_event_timestamp = conf->require_event_timestamp;
+ das->ctx = conf->ctx;
+ das->disconnect = conf->disconnect;
+
+ os_memcpy(&das->client_addr, conf->client_addr,
+ sizeof(das->client_addr));
+
+ das->shared_secret = os_malloc(conf->shared_secret_len);
+ if (das->shared_secret == NULL) {
+ radius_das_deinit(das);
+ return NULL;
+ }
+ os_memcpy(das->shared_secret, conf->shared_secret,
+ conf->shared_secret_len);
+ das->shared_secret_len = conf->shared_secret_len;
+
+ das->sock = radius_das_open_socket(conf->port);
+ if (das->sock < 0) {
+ wpa_printf(MSG_ERROR, "Failed to open UDP socket for RADIUS "
+ "DAS");
+ radius_das_deinit(das);
+ return NULL;
+ }
+
+ if (eloop_register_read_sock(das->sock, radius_das_receive, das, NULL))
+ {
+ radius_das_deinit(das);
+ return NULL;
+ }
+
+ return das;
+}
+
+
+void radius_das_deinit(struct radius_das_data *das)
+{
+ if (das == NULL)
+ return;
+
+ if (das->sock >= 0) {
+ eloop_unregister_read_sock(das->sock);
+ close(das->sock);
+ }
+
+ os_free(das->shared_secret);
+ os_free(das);
+}
diff --git a/contrib/wpa/src/radius/radius_das.h b/contrib/wpa/src/radius/radius_das.h
new file mode 100644
index 0000000..738b18b
--- /dev/null
+++ b/contrib/wpa/src/radius/radius_das.h
@@ -0,0 +1,47 @@
+/*
+ * RADIUS Dynamic Authorization Server (DAS)
+ * Copyright (c) 2012, Jouni Malinen <j@w1.fi>
+ *
+ * This software may be distributed under the terms of the BSD license.
+ * See README for more details.
+ */
+
+#ifndef RADIUS_DAS_H
+#define RADIUS_DAS_H
+
+struct radius_das_data;
+
+enum radius_das_res {
+ RADIUS_DAS_SUCCESS,
+ RADIUS_DAS_NAS_MISMATCH,
+ RADIUS_DAS_SESSION_NOT_FOUND
+};
+
+struct radius_das_attrs {
+ const u8 *sta_addr;
+ const u8 *user_name;
+ size_t user_name_len;
+ const u8 *acct_session_id;
+ size_t acct_session_id_len;
+ const u8 *cui;
+ size_t cui_len;
+};
+
+struct radius_das_conf {
+ int port;
+ const u8 *shared_secret;
+ size_t shared_secret_len;
+ const struct hostapd_ip_addr *client_addr;
+ unsigned int time_window;
+ int require_event_timestamp;
+ void *ctx;
+ enum radius_das_res (*disconnect)(void *ctx,
+ struct radius_das_attrs *attr);
+};
+
+struct radius_das_data *
+radius_das_init(struct radius_das_conf *conf);
+
+void radius_das_deinit(struct radius_das_data *data);
+
+#endif /* RADIUS_DAS_H */
diff --git a/contrib/wpa/src/radius/radius_server.c b/contrib/wpa/src/radius/radius_server.c
index f8780a6..5b2d711 100644
--- a/contrib/wpa/src/radius/radius_server.c
+++ b/contrib/wpa/src/radius/radius_server.c
@@ -1,15 +1,9 @@
/*
* RADIUS authentication server
- * Copyright (c) 2005-2009, Jouni Malinen <j@w1.fi>
+ * Copyright (c) 2005-2009, 2011, Jouni Malinen <j@w1.fi>
*
- * This program is free software; you can redistribute it and/or modify
- * it under the terms of the GNU General Public License version 2 as
- * published by the Free Software Foundation.
- *
- * Alternatively, this software may be distributed under the terms of BSD
- * license.
- *
- * See README and COPYING for more details.
+ * This software may be distributed under the terms of the BSD license.
+ * See README for more details.
*/
#include "includes.h"
@@ -222,6 +216,13 @@ struct radius_server_data {
int tnc;
/**
+ * pwd_group - The D-H group assigned for EAP-pwd
+ *
+ * If EAP-pwd is not used it can be set to zero.
+ */
+ u16 pwd_group;
+
+ /**
* wps - Wi-Fi Protected Setup context
*
* If WPS is used with an external RADIUS server (which is quite
@@ -285,6 +286,10 @@ struct radius_server_data {
* msg_ctx - Context data for wpa_msg() calls
*/
void *msg_ctx;
+
+#ifdef CONFIG_RADIUS_TEST
+ char *dump_msk_file;
+#endif /* CONFIG_RADIUS_TEST */
};
@@ -505,6 +510,7 @@ radius_server_get_new_session(struct radius_server_data *data,
eap_conf.eap_sim_aka_result_ind = data->eap_sim_aka_result_ind;
eap_conf.tnc = data->tnc;
eap_conf.wps = data->wps;
+ eap_conf.pwd_group = data->pwd_group;
sess->eap = eap_server_sm_init(sess, &radius_server_eapol_cb,
&eap_conf);
if (sess->eap == NULL) {
@@ -566,6 +572,24 @@ radius_server_encapsulate_eap(struct radius_server_data *data,
if (code == RADIUS_CODE_ACCESS_ACCEPT && sess->eap_if->eapKeyData) {
int len;
+#ifdef CONFIG_RADIUS_TEST
+ if (data->dump_msk_file) {
+ FILE *f;
+ char buf[2 * 64 + 1];
+ f = fopen(data->dump_msk_file, "a");
+ if (f) {
+ len = sess->eap_if->eapKeyDataLen;
+ if (len > 64)
+ len = 64;
+ len = wpa_snprintf_hex(
+ buf, sizeof(buf),
+ sess->eap_if->eapKeyData, len);
+ buf[len] = '\0';
+ fprintf(f, "%s\n", buf);
+ fclose(f);
+ }
+ }
+#endif /* CONFIG_RADIUS_TEST */
if (sess->eap_if->eapKeyDataLen > 64) {
len = 32;
} else {
@@ -665,8 +689,7 @@ static int radius_server_request(struct radius_server_data *data,
const char *from_addr, int from_port,
struct radius_session *force_sess)
{
- u8 *eap = NULL;
- size_t eap_len;
+ struct wpabuf *eap = NULL;
int res, state_included = 0;
u8 statebuf[4];
unsigned int state;
@@ -730,7 +753,7 @@ static int radius_server_request(struct radius_server_data *data,
return -1;
}
- eap = radius_msg_get_eap(msg, &eap_len);
+ eap = radius_msg_get_eap(msg);
if (eap == NULL) {
RADIUS_DEBUG("No EAP-Message in RADIUS packet from %s",
from_addr);
@@ -739,7 +762,7 @@ static int radius_server_request(struct radius_server_data *data,
return -1;
}
- RADIUS_DUMP("Received EAP data", eap, eap_len);
+ RADIUS_DUMP("Received EAP data", wpabuf_head(eap), wpabuf_len(eap));
/* FIX: if Code is Request, Success, or Failure, send Access-Reject;
* RFC3579 Sect. 2.6.2.
@@ -749,10 +772,7 @@ static int radius_server_request(struct radius_server_data *data,
* Or is this already done by the EAP state machine? */
wpabuf_free(sess->eap_if->eapRespData);
- sess->eap_if->eapRespData = wpabuf_alloc_ext_data(eap, eap_len);
- if (sess->eap_if->eapRespData == NULL)
- os_free(eap);
- eap = NULL;
+ sess->eap_if->eapRespData = eap;
sess->eap_if->eapResp = TRUE;
eap_server_sm_step(sess->eap);
@@ -1259,6 +1279,7 @@ radius_server_init(struct radius_server_conf *conf)
data->eap_sim_aka_result_ind = conf->eap_sim_aka_result_ind;
data->tnc = conf->tnc;
data->wps = conf->wps;
+ data->pwd_group = conf->pwd_group;
if (conf->eap_req_id_text) {
data->eap_req_id_text = os_malloc(conf->eap_req_id_text_len);
if (data->eap_req_id_text) {
@@ -1268,6 +1289,11 @@ radius_server_init(struct radius_server_conf *conf)
}
}
+#ifdef CONFIG_RADIUS_TEST
+ if (conf->dump_msk_file)
+ data->dump_msk_file = os_strdup(conf->dump_msk_file);
+#endif /* CONFIG_RADIUS_TEST */
+
data->clients = radius_server_read_clients(conf->client_file,
conf->ipv6);
if (data->clients == NULL) {
@@ -1319,6 +1345,9 @@ void radius_server_deinit(struct radius_server_data *data)
os_free(data->eap_fast_a_id);
os_free(data->eap_fast_a_id_info);
os_free(data->eap_req_id_text);
+#ifdef CONFIG_RADIUS_TEST
+ os_free(data->dump_msk_file);
+#endif /* CONFIG_RADIUS_TEST */
os_free(data);
}
diff --git a/contrib/wpa/src/radius/radius_server.h b/contrib/wpa/src/radius/radius_server.h
index f9c951d..82466c3 100644
--- a/contrib/wpa/src/radius/radius_server.h
+++ b/contrib/wpa/src/radius/radius_server.h
@@ -1,15 +1,9 @@
/*
* RADIUS authentication server
- * Copyright (c) 2005-2009, Jouni Malinen <j@w1.fi>
+ * Copyright (c) 2005-2009, 2011, Jouni Malinen <j@w1.fi>
*
- * This program is free software; you can redistribute it and/or modify
- * it under the terms of the GNU General Public License version 2 as
- * published by the Free Software Foundation.
- *
- * Alternatively, this software may be distributed under the terms of BSD
- * license.
- *
- * See README and COPYING for more details.
+ * This software may be distributed under the terms of the BSD license.
+ * See README for more details.
*/
#ifndef RADIUS_SERVER_H
@@ -143,6 +137,13 @@ struct radius_server_conf {
int tnc;
/**
+ * pwd_group - EAP-pwd D-H group
+ *
+ * This is used to select which D-H group to use with EAP-pwd.
+ */
+ u16 pwd_group;
+
+ /**
* wps - Wi-Fi Protected Setup context
*
* If WPS is used with an external RADIUS server (which is quite
@@ -194,6 +195,10 @@ struct radius_server_conf {
* msg_ctx - Context data for wpa_msg() calls
*/
void *msg_ctx;
+
+#ifdef CONFIG_RADIUS_TEST
+ const char *dump_msk_file;
+#endif /* CONFIG_RADIUS_TEST */
};
diff --git a/contrib/wpa/src/rsn_supp/Makefile b/contrib/wpa/src/rsn_supp/Makefile
deleted file mode 100644
index 9c41962..0000000
--- a/contrib/wpa/src/rsn_supp/Makefile
+++ /dev/null
@@ -1,8 +0,0 @@
-all:
- @echo Nothing to be made.
-
-clean:
- rm -f *~ *.o *.d
-
-install:
- @echo Nothing to be made.
diff --git a/contrib/wpa/src/rsn_supp/peerkey.c b/contrib/wpa/src/rsn_supp/peerkey.c
index 9d60d4a..f2bac34 100644
--- a/contrib/wpa/src/rsn_supp/peerkey.c
+++ b/contrib/wpa/src/rsn_supp/peerkey.c
@@ -2,14 +2,8 @@
* WPA Supplicant - PeerKey for Direct Link Setup (DLS)
* Copyright (c) 2006-2008, Jouni Malinen <j@w1.fi>
*
- * This program is free software; you can redistribute it and/or modify
- * it under the terms of the GNU General Public License version 2 as
- * published by the Free Software Foundation.
- *
- * Alternatively, this software may be distributed under the terms of BSD
- * license.
- *
- * See README and COPYING for more details.
+ * This software may be distributed under the terms of the BSD license.
+ * See README for more details.
*/
#include "includes.h"
@@ -20,6 +14,7 @@
#include "eloop.h"
#include "crypto/sha1.h"
#include "crypto/sha256.h"
+#include "crypto/random.h"
#include "common/ieee802_11_defs.h"
#include "wpa.h"
#include "wpa_i.h"
@@ -226,6 +221,9 @@ static int wpa_supplicant_process_smk_m2(
if (cipher & WPA_CIPHER_CCMP) {
wpa_printf(MSG_DEBUG, "RSN: Using CCMP for PeerKey");
cipher = WPA_CIPHER_CCMP;
+ } else if (cipher & WPA_CIPHER_GCMP) {
+ wpa_printf(MSG_DEBUG, "RSN: Using GCMP for PeerKey");
+ cipher = WPA_CIPHER_GCMP;
} else if (cipher & WPA_CIPHER_TKIP) {
wpa_printf(MSG_DEBUG, "RSN: Using TKIP for PeerKey");
cipher = WPA_CIPHER_TKIP;
@@ -254,7 +252,7 @@ static int wpa_supplicant_process_smk_m2(
peerkey->use_sha256 = 1;
#endif /* CONFIG_IEEE80211W */
- if (os_get_random(peerkey->pnonce, WPA_NONCE_LEN)) {
+ if (random_get_bytes(peerkey->pnonce, WPA_NONCE_LEN)) {
wpa_msg(sm->ctx->msg_ctx, MSG_WARNING,
"WPA: Failed to get random data for PNonce");
wpa_supplicant_peerkey_free(sm, peerkey);
@@ -272,10 +270,7 @@ static int wpa_supplicant_process_smk_m2(
/* Include only the selected cipher in pairwise cipher suite */
WPA_PUT_LE16(pos, 1);
pos += 2;
- if (cipher == WPA_CIPHER_CCMP)
- RSN_SELECTOR_PUT(pos, RSN_CIPHER_SUITE_CCMP);
- else if (cipher == WPA_CIPHER_TKIP)
- RSN_SELECTOR_PUT(pos, RSN_CIPHER_SUITE_TKIP);
+ RSN_SELECTOR_PUT(pos, wpa_cipher_to_suite(WPA_PROTO_RSN, cipher));
pos += RSN_SELECTOR_LEN;
hdr->len = (pos - peerkey->rsnie_p) - 2;
@@ -349,7 +344,7 @@ static void wpa_supplicant_send_stk_1_of_4(struct wpa_sm *sm,
msg->type = EAPOL_KEY_TYPE_RSN;
- if (peerkey->cipher == WPA_CIPHER_CCMP)
+ if (peerkey->cipher != WPA_CIPHER_TKIP)
ver = WPA_KEY_INFO_TYPE_HMAC_SHA1_AES;
else
ver = WPA_KEY_INFO_TYPE_HMAC_MD5_RC4;
@@ -357,7 +352,7 @@ static void wpa_supplicant_send_stk_1_of_4(struct wpa_sm *sm,
key_info = ver | WPA_KEY_INFO_KEY_TYPE | WPA_KEY_INFO_ACK;
WPA_PUT_BE16(msg->key_info, key_info);
- if (peerkey->cipher == WPA_CIPHER_CCMP)
+ if (peerkey->cipher != WPA_CIPHER_TKIP)
WPA_PUT_BE16(msg->key_length, 16);
else
WPA_PUT_BE16(msg->key_length, 32);
@@ -370,7 +365,7 @@ static void wpa_supplicant_send_stk_1_of_4(struct wpa_sm *sm,
wpa_add_kde((u8 *) (msg + 1), RSN_KEY_DATA_PMKID,
peerkey->smkid, PMKID_LEN);
- if (os_get_random(peerkey->inonce, WPA_NONCE_LEN)) {
+ if (random_get_bytes(peerkey->inonce, WPA_NONCE_LEN)) {
wpa_msg(sm->ctx->msg_ctx, MSG_WARNING,
"RSN: Failed to get random data for INonce (STK)");
os_free(mbuf);
@@ -408,7 +403,7 @@ static void wpa_supplicant_send_stk_3_of_4(struct wpa_sm *sm,
msg->type = EAPOL_KEY_TYPE_RSN;
- if (peerkey->cipher == WPA_CIPHER_CCMP)
+ if (peerkey->cipher != WPA_CIPHER_TKIP)
ver = WPA_KEY_INFO_TYPE_HMAC_SHA1_AES;
else
ver = WPA_KEY_INFO_TYPE_HMAC_MD5_RC4;
@@ -417,7 +412,7 @@ static void wpa_supplicant_send_stk_3_of_4(struct wpa_sm *sm,
WPA_KEY_INFO_MIC | WPA_KEY_INFO_SECURE;
WPA_PUT_BE16(msg->key_info, key_info);
- if (peerkey->cipher == WPA_CIPHER_CCMP)
+ if (peerkey->cipher != WPA_CIPHER_TKIP)
WPA_PUT_BE16(msg->key_length, 16);
else
WPA_PUT_BE16(msg->key_length, 32);
@@ -505,6 +500,9 @@ static int wpa_supplicant_process_smk_m5(struct wpa_sm *sm,
if (cipher & WPA_CIPHER_CCMP) {
wpa_printf(MSG_DEBUG, "RSN: Using CCMP for PeerKey");
peerkey->cipher = WPA_CIPHER_CCMP;
+ } else if (cipher & WPA_CIPHER_GCMP) {
+ wpa_printf(MSG_DEBUG, "RSN: Using GCMP for PeerKey");
+ peerkey->cipher = WPA_CIPHER_GCMP;
} else if (cipher & WPA_CIPHER_TKIP) {
wpa_printf(MSG_DEBUG, "RSN: Using TKIP for PeerKey");
peerkey->cipher = WPA_CIPHER_TKIP;
@@ -697,7 +695,7 @@ static void wpa_supplicant_process_stk_1_of_4(struct wpa_sm *sm,
return;
}
- if (os_get_random(peerkey->pnonce, WPA_NONCE_LEN)) {
+ if (random_get_bytes(peerkey->pnonce, WPA_NONCE_LEN)) {
wpa_msg(sm->ctx->msg_ctx, MSG_WARNING,
"RSN: Failed to get random data for PNonce");
return;
@@ -1021,7 +1019,7 @@ int wpa_sm_stkstart(struct wpa_sm *sm, const u8 *peer)
return -1;
}
- if (sm->pairwise_cipher == WPA_CIPHER_CCMP)
+ if (sm->pairwise_cipher != WPA_CIPHER_TKIP)
ver = WPA_KEY_INFO_TYPE_HMAC_SHA1_AES;
else
ver = WPA_KEY_INFO_TYPE_HMAC_MD5_RC4;
@@ -1060,17 +1058,8 @@ int wpa_sm_stkstart(struct wpa_sm *sm, const u8 *peer)
count_pos = pos;
pos += 2;
- count = 0;
- if (sm->allowed_pairwise_cipher & WPA_CIPHER_CCMP) {
- RSN_SELECTOR_PUT(pos, RSN_CIPHER_SUITE_CCMP);
- pos += RSN_SELECTOR_LEN;
- count++;
- }
- if (sm->allowed_pairwise_cipher & WPA_CIPHER_TKIP) {
- RSN_SELECTOR_PUT(pos, RSN_CIPHER_SUITE_TKIP);
- pos += RSN_SELECTOR_LEN;
- count++;
- }
+ count = rsn_cipher_put_suites(pos, sm->allowed_pairwise_cipher);
+ pos += count * RSN_SELECTOR_LEN;
WPA_PUT_LE16(count_pos, count);
hdr->len = (pos - peerkey->rsnie_i) - 2;
@@ -1097,7 +1086,7 @@ int wpa_sm_stkstart(struct wpa_sm *sm, const u8 *peer)
WPA_REPLAY_COUNTER_LEN);
inc_byte_array(sm->request_counter, WPA_REPLAY_COUNTER_LEN);
- if (os_get_random(peerkey->inonce, WPA_NONCE_LEN)) {
+ if (random_get_bytes(peerkey->inonce, WPA_NONCE_LEN)) {
wpa_msg(sm->ctx->msg_ctx, MSG_WARNING,
"WPA: Failed to get random data for INonce");
os_free(rbuf);
@@ -1140,6 +1129,7 @@ void peerkey_deinit(struct wpa_sm *sm)
peerkey = peerkey->next;
os_free(prev);
}
+ sm->peerkey = NULL;
}
diff --git a/contrib/wpa/src/rsn_supp/peerkey.h b/contrib/wpa/src/rsn_supp/peerkey.h
index 2613127..b8845f7 100644
--- a/contrib/wpa/src/rsn_supp/peerkey.h
+++ b/contrib/wpa/src/rsn_supp/peerkey.h
@@ -2,14 +2,8 @@
* WPA Supplicant - PeerKey for Direct Link Setup (DLS)
* Copyright (c) 2006-2008, Jouni Malinen <j@w1.fi>
*
- * This program is free software; you can redistribute it and/or modify
- * it under the terms of the GNU General Public License version 2 as
- * published by the Free Software Foundation.
- *
- * Alternatively, this software may be distributed under the terms of BSD
- * license.
- *
- * See README and COPYING for more details.
+ * This software may be distributed under the terms of the BSD license.
+ * See README for more details.
*/
#ifndef PEERKEY_H
diff --git a/contrib/wpa/src/rsn_supp/pmksa_cache.c b/contrib/wpa/src/rsn_supp/pmksa_cache.c
index cac8c83..df67583 100644
--- a/contrib/wpa/src/rsn_supp/pmksa_cache.c
+++ b/contrib/wpa/src/rsn_supp/pmksa_cache.c
@@ -1,15 +1,9 @@
/*
* WPA Supplicant - RSN PMKSA cache
- * Copyright (c) 2004-2008, Jouni Malinen <j@w1.fi>
+ * Copyright (c) 2004-2009, 2011-2012, Jouni Malinen <j@w1.fi>
*
- * This program is free software; you can redistribute it and/or modify
- * it under the terms of the GNU General Public License version 2 as
- * published by the Free Software Foundation.
- *
- * Alternatively, this software may be distributed under the terms of BSD
- * license.
- *
- * See README and COPYING for more details.
+ * This software may be distributed under the terms of the BSD license.
+ * See README for more details.
*/
#include "includes.h"
@@ -31,7 +25,7 @@ struct rsn_pmksa_cache {
struct wpa_sm *sm; /* TODO: get rid of this reference(?) */
void (*free_cb)(struct rsn_pmksa_cache_entry *entry, void *ctx,
- int replace);
+ enum pmksa_free_reason reason);
void *ctx;
};
@@ -47,10 +41,11 @@ static void _pmksa_cache_free_entry(struct rsn_pmksa_cache_entry *entry)
static void pmksa_cache_free_entry(struct rsn_pmksa_cache *pmksa,
struct rsn_pmksa_cache_entry *entry,
- int replace)
+ enum pmksa_free_reason reason)
{
+ wpa_sm_remove_pmkid(pmksa->sm, entry->aa, entry->pmkid);
pmksa->pmksa_count--;
- pmksa->free_cb(entry, pmksa->ctx, replace);
+ pmksa->free_cb(entry, pmksa->ctx, reason);
_pmksa_cache_free_entry(entry);
}
@@ -66,7 +61,7 @@ static void pmksa_cache_expire(void *eloop_ctx, void *timeout_ctx)
pmksa->pmksa = entry->next;
wpa_printf(MSG_DEBUG, "RSN: expired PMKSA cache entry for "
MACSTR, MAC2STR(entry->aa));
- pmksa_cache_free_entry(pmksa, entry, 0);
+ pmksa_cache_free_entry(pmksa, entry, PMKSA_EXPIRE);
}
pmksa_cache_set_expiration(pmksa);
@@ -98,7 +93,7 @@ static void pmksa_cache_set_expiration(struct rsn_pmksa_cache *pmksa)
eloop_register_timeout(sec + 1, 0, pmksa_cache_expire, pmksa, NULL);
entry = pmksa->sm->cur_pmksa ? pmksa->sm->cur_pmksa :
- pmksa_cache_get(pmksa, pmksa->sm->bssid, NULL);
+ pmksa_cache_get(pmksa, pmksa->sm->bssid, NULL, NULL);
if (entry) {
sec = pmksa->pmksa->reauth_time - now.sec;
if (sec < 0)
@@ -169,22 +164,17 @@ pmksa_cache_add(struct rsn_pmksa_cache *pmksa, const u8 *pmk, size_t pmk_len,
pmksa->pmksa = pos->next;
else
prev->next = pos->next;
- if (pos == pmksa->sm->cur_pmksa) {
- /* We are about to replace the current PMKSA
- * cache entry. This happens when the PMKSA
- * caching attempt fails, so we don't want to
- * force pmksa_cache_free_entry() to disconnect
- * at this point. Let's just make sure the old
- * PMKSA cache entry will not be used in the
- * future.
- */
- wpa_printf(MSG_DEBUG, "RSN: replacing current "
- "PMKSA entry");
- pmksa->sm->cur_pmksa = NULL;
- }
wpa_printf(MSG_DEBUG, "RSN: Replace PMKSA entry for "
"the current AP");
- pmksa_cache_free_entry(pmksa, pos, 1);
+ pmksa_cache_free_entry(pmksa, pos, PMKSA_REPLACE);
+
+ /*
+ * If OKC is used, there may be other PMKSA cache
+ * entries based on the same PMK. These needs to be
+ * flushed so that a new entry can be created based on
+ * the new PMK.
+ */
+ pmksa_cache_flush(pmksa, network_ctx);
break;
}
prev = pos;
@@ -194,12 +184,25 @@ pmksa_cache_add(struct rsn_pmksa_cache *pmksa, const u8 *pmk, size_t pmk_len,
if (pmksa->pmksa_count >= pmksa_cache_max_entries && pmksa->pmksa) {
/* Remove the oldest entry to make room for the new entry */
pos = pmksa->pmksa;
- pmksa->pmksa = pos->next;
- wpa_printf(MSG_DEBUG, "RSN: removed the oldest PMKSA cache "
- "entry (for " MACSTR ") to make room for new one",
- MAC2STR(pos->aa));
- wpa_sm_remove_pmkid(pmksa->sm, pos->aa, pos->pmkid);
- pmksa_cache_free_entry(pmksa, pos, 0);
+
+ if (pos == pmksa->sm->cur_pmksa) {
+ /*
+ * Never remove the current PMKSA cache entry, since
+ * it's in use, and removing it triggers a needless
+ * deauthentication.
+ */
+ pos = pos->next;
+ pmksa->pmksa->next = pos ? pos->next : NULL;
+ } else
+ pmksa->pmksa = pos->next;
+
+ if (pos) {
+ wpa_printf(MSG_DEBUG, "RSN: removed the oldest idle "
+ "PMKSA cache entry (for " MACSTR ") to "
+ "make room for new one",
+ MAC2STR(pos->aa));
+ pmksa_cache_free_entry(pmksa, pos, PMKSA_FREE);
+ }
}
/* Add the new entry; order by expiration time */
@@ -220,8 +223,8 @@ pmksa_cache_add(struct rsn_pmksa_cache *pmksa, const u8 *pmk, size_t pmk_len,
prev->next = entry;
}
pmksa->pmksa_count++;
- wpa_printf(MSG_DEBUG, "RSN: added PMKSA cache entry for " MACSTR,
- MAC2STR(entry->aa));
+ wpa_printf(MSG_DEBUG, "RSN: Added PMKSA cache entry for " MACSTR
+ " network_ctx=%p", MAC2STR(entry->aa), network_ctx);
wpa_sm_add_pmkid(pmksa->sm, entry->aa, entry->pmkid);
return entry;
@@ -229,6 +232,39 @@ pmksa_cache_add(struct rsn_pmksa_cache *pmksa, const u8 *pmk, size_t pmk_len,
/**
+ * pmksa_cache_flush - Flush PMKSA cache entries for a specific network
+ * @pmksa: Pointer to PMKSA cache data from pmksa_cache_init()
+ * @network_ctx: Network configuration context or %NULL to flush all entries
+ */
+void pmksa_cache_flush(struct rsn_pmksa_cache *pmksa, void *network_ctx)
+{
+ struct rsn_pmksa_cache_entry *entry, *prev = NULL, *tmp;
+ int removed = 0;
+
+ entry = pmksa->pmksa;
+ while (entry) {
+ if (entry->network_ctx == network_ctx || network_ctx == NULL) {
+ wpa_printf(MSG_DEBUG, "RSN: Flush PMKSA cache entry "
+ "for " MACSTR, MAC2STR(entry->aa));
+ if (prev)
+ prev->next = entry->next;
+ else
+ pmksa->pmksa = entry->next;
+ tmp = entry;
+ entry = entry->next;
+ pmksa_cache_free_entry(pmksa, tmp, PMKSA_FREE);
+ removed++;
+ } else {
+ prev = entry;
+ entry = entry->next;
+ }
+ }
+ if (removed)
+ pmksa_cache_set_expiration(pmksa);
+}
+
+
+/**
* pmksa_cache_deinit - Free all entries in PMKSA cache
* @pmksa: Pointer to PMKSA cache data from pmksa_cache_init()
*/
@@ -256,16 +292,19 @@ void pmksa_cache_deinit(struct rsn_pmksa_cache *pmksa)
* @pmksa: Pointer to PMKSA cache data from pmksa_cache_init()
* @aa: Authenticator address or %NULL to match any
* @pmkid: PMKID or %NULL to match any
+ * @network_ctx: Network context or %NULL to match any
* Returns: Pointer to PMKSA cache entry or %NULL if no match was found
*/
struct rsn_pmksa_cache_entry * pmksa_cache_get(struct rsn_pmksa_cache *pmksa,
- const u8 *aa, const u8 *pmkid)
+ const u8 *aa, const u8 *pmkid,
+ const void *network_ctx)
{
struct rsn_pmksa_cache_entry *entry = pmksa->pmksa;
while (entry) {
if ((aa == NULL || os_memcmp(entry->aa, aa, ETH_ALEN) == 0) &&
(pmkid == NULL ||
- os_memcmp(entry->pmkid, pmkid, PMKID_LEN) == 0))
+ os_memcmp(entry->pmkid, pmkid, PMKID_LEN) == 0) &&
+ (network_ctx == NULL || network_ctx == entry->network_ctx))
return entry;
entry = entry->next;
}
@@ -273,22 +312,6 @@ struct rsn_pmksa_cache_entry * pmksa_cache_get(struct rsn_pmksa_cache *pmksa,
}
-/**
- * pmksa_cache_notify_reconfig - Reconfiguration notification for PMKSA cache
- * @pmksa: Pointer to PMKSA cache data from pmksa_cache_init()
- *
- * Clear references to old data structures when wpa_supplicant is reconfigured.
- */
-void pmksa_cache_notify_reconfig(struct rsn_pmksa_cache *pmksa)
-{
- struct rsn_pmksa_cache_entry *entry = pmksa->pmksa;
- while (entry) {
- entry->network_ctx = NULL;
- entry = entry->next;
- }
-}
-
-
static struct rsn_pmksa_cache_entry *
pmksa_cache_clone_entry(struct rsn_pmksa_cache *pmksa,
const struct rsn_pmksa_cache_entry *old_entry,
@@ -327,6 +350,7 @@ pmksa_cache_get_opportunistic(struct rsn_pmksa_cache *pmksa, void *network_ctx,
{
struct rsn_pmksa_cache_entry *entry = pmksa->pmksa;
+ wpa_printf(MSG_DEBUG, "RSN: Consider " MACSTR " for OKC", MAC2STR(aa));
if (network_ctx == NULL)
return NULL;
while (entry) {
@@ -384,20 +408,32 @@ int pmksa_cache_set_current(struct wpa_sm *sm, const u8 *pmkid,
int try_opportunistic)
{
struct rsn_pmksa_cache *pmksa = sm->pmksa;
+ wpa_printf(MSG_DEBUG, "RSN: PMKSA cache search - network_ctx=%p "
+ "try_opportunistic=%d", network_ctx, try_opportunistic);
+ if (pmkid)
+ wpa_hexdump(MSG_DEBUG, "RSN: Search for PMKID",
+ pmkid, PMKID_LEN);
+ if (bssid)
+ wpa_printf(MSG_DEBUG, "RSN: Search for BSSID " MACSTR,
+ MAC2STR(bssid));
+
sm->cur_pmksa = NULL;
if (pmkid)
- sm->cur_pmksa = pmksa_cache_get(pmksa, NULL, pmkid);
+ sm->cur_pmksa = pmksa_cache_get(pmksa, NULL, pmkid,
+ network_ctx);
if (sm->cur_pmksa == NULL && bssid)
- sm->cur_pmksa = pmksa_cache_get(pmksa, bssid, NULL);
+ sm->cur_pmksa = pmksa_cache_get(pmksa, bssid, NULL,
+ network_ctx);
if (sm->cur_pmksa == NULL && try_opportunistic && bssid)
sm->cur_pmksa = pmksa_cache_get_opportunistic(pmksa,
network_ctx,
bssid);
if (sm->cur_pmksa) {
- wpa_hexdump(MSG_DEBUG, "RSN: PMKID",
+ wpa_hexdump(MSG_DEBUG, "RSN: PMKSA cache entry found - PMKID",
sm->cur_pmksa->pmkid, PMKID_LEN);
return 0;
}
+ wpa_printf(MSG_DEBUG, "RSN: No PMKSA cache entry found");
return -1;
}
@@ -458,7 +494,7 @@ int pmksa_cache_list(struct rsn_pmksa_cache *pmksa, char *buf, size_t len)
*/
struct rsn_pmksa_cache *
pmksa_cache_init(void (*free_cb)(struct rsn_pmksa_cache_entry *entry,
- void *ctx, int replace),
+ void *ctx, enum pmksa_free_reason reason),
void *ctx, struct wpa_sm *sm)
{
struct rsn_pmksa_cache *pmksa;
diff --git a/contrib/wpa/src/rsn_supp/pmksa_cache.h b/contrib/wpa/src/rsn_supp/pmksa_cache.h
index a1447e5..f318c52 100644
--- a/contrib/wpa/src/rsn_supp/pmksa_cache.h
+++ b/contrib/wpa/src/rsn_supp/pmksa_cache.h
@@ -1,15 +1,9 @@
/*
* wpa_supplicant - WPA2/RSN PMKSA cache functions
- * Copyright (c) 2003-2008, Jouni Malinen <j@w1.fi>
+ * Copyright (c) 2003-2009, 2011-2012, Jouni Malinen <j@w1.fi>
*
- * This program is free software; you can redistribute it and/or modify
- * it under the terms of the GNU General Public License version 2 as
- * published by the Free Software Foundation.
- *
- * Alternatively, this software may be distributed under the terms of BSD
- * license.
- *
- * See README and COPYING for more details.
+ * This software may be distributed under the terms of the BSD license.
+ * See README for more details.
*/
#ifndef PMKSA_CACHE_H
@@ -44,20 +38,26 @@ struct rsn_pmksa_cache_entry {
struct rsn_pmksa_cache;
+enum pmksa_free_reason {
+ PMKSA_FREE,
+ PMKSA_REPLACE,
+ PMKSA_EXPIRE,
+};
+
#if defined(IEEE8021X_EAPOL) && !defined(CONFIG_NO_WPA2)
struct rsn_pmksa_cache *
pmksa_cache_init(void (*free_cb)(struct rsn_pmksa_cache_entry *entry,
- void *ctx, int replace),
+ void *ctx, enum pmksa_free_reason reason),
void *ctx, struct wpa_sm *sm);
void pmksa_cache_deinit(struct rsn_pmksa_cache *pmksa);
struct rsn_pmksa_cache_entry * pmksa_cache_get(struct rsn_pmksa_cache *pmksa,
- const u8 *aa, const u8 *pmkid);
+ const u8 *aa, const u8 *pmkid,
+ const void *network_ctx);
int pmksa_cache_list(struct rsn_pmksa_cache *pmksa, char *buf, size_t len);
struct rsn_pmksa_cache_entry *
pmksa_cache_add(struct rsn_pmksa_cache *pmksa, const u8 *pmk, size_t pmk_len,
const u8 *aa, const u8 *spa, void *network_ctx, int akmp);
-void pmksa_cache_notify_reconfig(struct rsn_pmksa_cache *pmksa);
struct rsn_pmksa_cache_entry * pmksa_cache_get_current(struct wpa_sm *sm);
void pmksa_cache_clear_current(struct wpa_sm *sm);
int pmksa_cache_set_current(struct wpa_sm *sm, const u8 *pmkid,
@@ -66,12 +66,13 @@ int pmksa_cache_set_current(struct wpa_sm *sm, const u8 *pmkid,
struct rsn_pmksa_cache_entry *
pmksa_cache_get_opportunistic(struct rsn_pmksa_cache *pmksa,
void *network_ctx, const u8 *aa);
+void pmksa_cache_flush(struct rsn_pmksa_cache *pmksa, void *network_ctx);
#else /* IEEE8021X_EAPOL and !CONFIG_NO_WPA2 */
static inline struct rsn_pmksa_cache *
pmksa_cache_init(void (*free_cb)(struct rsn_pmksa_cache_entry *entry,
- void *ctx, int replace),
+ void *ctx, int reason),
void *ctx, struct wpa_sm *sm)
{
return (void *) -1;
@@ -82,7 +83,8 @@ static inline void pmksa_cache_deinit(struct rsn_pmksa_cache *pmksa)
}
static inline struct rsn_pmksa_cache_entry *
-pmksa_cache_get(struct rsn_pmksa_cache *pmksa, const u8 *aa, const u8 *pmkid)
+pmksa_cache_get(struct rsn_pmksa_cache *pmksa, const u8 *aa, const u8 *pmkid,
+ const void *network_ctx)
{
return NULL;
}
@@ -106,10 +108,6 @@ pmksa_cache_add(struct rsn_pmksa_cache *pmksa, const u8 *pmk, size_t pmk_len,
return NULL;
}
-static inline void pmksa_cache_notify_reconfig(struct rsn_pmksa_cache *pmksa)
-{
-}
-
static inline void pmksa_cache_clear_current(struct wpa_sm *sm)
{
}
@@ -122,6 +120,11 @@ static inline int pmksa_cache_set_current(struct wpa_sm *sm, const u8 *pmkid,
return -1;
}
+static inline void pmksa_cache_flush(struct rsn_pmksa_cache *pmksa,
+ void *network_ctx)
+{
+}
+
#endif /* IEEE8021X_EAPOL and !CONFIG_NO_WPA2 */
#endif /* PMKSA_CACHE_H */
diff --git a/contrib/wpa/src/rsn_supp/preauth.c b/contrib/wpa/src/rsn_supp/preauth.c
index 6109f5e..ab61867 100644
--- a/contrib/wpa/src/rsn_supp/preauth.c
+++ b/contrib/wpa/src/rsn_supp/preauth.c
@@ -1,15 +1,9 @@
/*
* RSN pre-authentication (supplicant)
- * Copyright (c) 2003-2010, Jouni Malinen <j@w1.fi>
+ * Copyright (c) 2003-2012, Jouni Malinen <j@w1.fi>
*
- * This program is free software; you can redistribute it and/or modify
- * it under the terms of the GNU General Public License version 2 as
- * published by the Free Software Foundation.
- *
- * Alternatively, this software may be distributed under the terms of BSD
- * license.
- *
- * See README and COPYING for more details.
+ * This software may be distributed under the terms of the BSD license.
+ * See README for more details.
*/
#include "includes.h"
@@ -22,7 +16,6 @@
#include "preauth.h"
#include "pmksa_cache.h"
#include "wpa_i.h"
-#include "common/ieee802_11_defs.h"
#if defined(IEEE8021X_EAPOL) && !defined(CONFIG_NO_WPA2)
@@ -312,7 +305,7 @@ void rsn_preauth_candidate_process(struct wpa_sm *sm)
dl_list_for_each_safe(candidate, n, &sm->pmksa_candidates,
struct rsn_pmksa_candidate, list) {
struct rsn_pmksa_cache_entry *p = NULL;
- p = pmksa_cache_get(sm->pmksa, candidate->bssid, NULL);
+ p = pmksa_cache_get(sm->pmksa, candidate->bssid, NULL, NULL);
if (os_memcmp(sm->bssid, candidate->bssid, ETH_ALEN) != 0 &&
(p == NULL || p->opportunistic)) {
wpa_msg(sm->ctx->msg_ctx, MSG_DEBUG, "RSN: PMKSA "
@@ -459,7 +452,7 @@ void rsn_preauth_scan_result(struct wpa_sm *sm, const u8 *bssid,
if (wpa_parse_wpa_ie(rsn, 2 + rsn[1], &ie))
return;
- pmksa = pmksa_cache_get(sm->pmksa, bssid, NULL);
+ pmksa = pmksa_cache_get(sm->pmksa, bssid, NULL, NULL);
if (pmksa && (!pmksa->opportunistic ||
!(ie.capabilities & WPA_CAPABILITY_PREAUTH)))
return;
diff --git a/contrib/wpa/src/rsn_supp/preauth.h b/contrib/wpa/src/rsn_supp/preauth.h
index f8240ab..27d3112c 100644
--- a/contrib/wpa/src/rsn_supp/preauth.h
+++ b/contrib/wpa/src/rsn_supp/preauth.h
@@ -2,14 +2,8 @@
* wpa_supplicant - WPA2/RSN pre-authentication functions
* Copyright (c) 2003-2009, Jouni Malinen <j@w1.fi>
*
- * This program is free software; you can redistribute it and/or modify
- * it under the terms of the GNU General Public License version 2 as
- * published by the Free Software Foundation.
- *
- * Alternatively, this software may be distributed under the terms of BSD
- * license.
- *
- * See README and COPYING for more details.
+ * This software may be distributed under the terms of the BSD license.
+ * See README for more details.
*/
#ifndef PREAUTH_H
diff --git a/contrib/wpa/src/rsn_supp/tdls.c b/contrib/wpa/src/rsn_supp/tdls.c
new file mode 100644
index 0000000..7646ca8
--- /dev/null
+++ b/contrib/wpa/src/rsn_supp/tdls.c
@@ -0,0 +1,2334 @@
+/*
+ * wpa_supplicant - TDLS
+ * Copyright (c) 2010-2011, Atheros Communications
+ *
+ * This software may be distributed under the terms of the BSD license.
+ * See README for more details.
+ */
+
+#include "utils/includes.h"
+
+#include "utils/common.h"
+#include "utils/eloop.h"
+#include "utils/os.h"
+#include "common/ieee802_11_defs.h"
+#include "crypto/sha256.h"
+#include "crypto/crypto.h"
+#include "crypto/aes_wrap.h"
+#include "rsn_supp/wpa.h"
+#include "rsn_supp/wpa_ie.h"
+#include "rsn_supp/wpa_i.h"
+#include "drivers/driver.h"
+#include "l2_packet/l2_packet.h"
+
+#ifdef CONFIG_TDLS_TESTING
+#define TDLS_TESTING_LONG_FRAME BIT(0)
+#define TDLS_TESTING_ALT_RSN_IE BIT(1)
+#define TDLS_TESTING_DIFF_BSSID BIT(2)
+#define TDLS_TESTING_SHORT_LIFETIME BIT(3)
+#define TDLS_TESTING_WRONG_LIFETIME_RESP BIT(4)
+#define TDLS_TESTING_WRONG_LIFETIME_CONF BIT(5)
+#define TDLS_TESTING_LONG_LIFETIME BIT(6)
+#define TDLS_TESTING_CONCURRENT_INIT BIT(7)
+#define TDLS_TESTING_NO_TPK_EXPIRATION BIT(8)
+#define TDLS_TESTING_DECLINE_RESP BIT(9)
+#define TDLS_TESTING_IGNORE_AP_PROHIBIT BIT(10)
+unsigned int tdls_testing = 0;
+#endif /* CONFIG_TDLS_TESTING */
+
+#define TPK_LIFETIME 43200 /* 12 hours */
+#define TPK_RETRY_COUNT 3
+#define TPK_TIMEOUT 5000 /* in milliseconds */
+
+#define TDLS_MIC_LEN 16
+
+#define TDLS_TIMEOUT_LEN 4
+
+struct wpa_tdls_ftie {
+ u8 ie_type; /* FTIE */
+ u8 ie_len;
+ u8 mic_ctrl[2];
+ u8 mic[TDLS_MIC_LEN];
+ u8 Anonce[WPA_NONCE_LEN]; /* Responder Nonce in TDLS */
+ u8 Snonce[WPA_NONCE_LEN]; /* Initiator Nonce in TDLS */
+ /* followed by optional elements */
+} STRUCT_PACKED;
+
+struct wpa_tdls_timeoutie {
+ u8 ie_type; /* Timeout IE */
+ u8 ie_len;
+ u8 interval_type;
+ u8 value[TDLS_TIMEOUT_LEN];
+} STRUCT_PACKED;
+
+struct wpa_tdls_lnkid {
+ u8 ie_type; /* Link Identifier IE */
+ u8 ie_len;
+ u8 bssid[ETH_ALEN];
+ u8 init_sta[ETH_ALEN];
+ u8 resp_sta[ETH_ALEN];
+} STRUCT_PACKED;
+
+/* TDLS frame headers as per IEEE Std 802.11z-2010 */
+struct wpa_tdls_frame {
+ u8 payloadtype; /* IEEE80211_TDLS_RFTYPE */
+ u8 category; /* Category */
+ u8 action; /* Action (enum tdls_frame_type) */
+} STRUCT_PACKED;
+
+static u8 * wpa_add_tdls_timeoutie(u8 *pos, u8 *ie, size_t ie_len, u32 tsecs);
+static void wpa_tdls_tpk_retry_timeout(void *eloop_ctx, void *timeout_ctx);
+static void wpa_tdls_peer_free(struct wpa_sm *sm, struct wpa_tdls_peer *peer);
+
+
+#define TDLS_MAX_IE_LEN 80
+#define IEEE80211_MAX_SUPP_RATES 32
+
+struct wpa_tdls_peer {
+ struct wpa_tdls_peer *next;
+ int initiator; /* whether this end was initiator for TDLS setup */
+ u8 addr[ETH_ALEN]; /* other end MAC address */
+ u8 inonce[WPA_NONCE_LEN]; /* Initiator Nonce */
+ u8 rnonce[WPA_NONCE_LEN]; /* Responder Nonce */
+ u8 rsnie_i[TDLS_MAX_IE_LEN]; /* Initiator RSN IE */
+ size_t rsnie_i_len;
+ u8 rsnie_p[TDLS_MAX_IE_LEN]; /* Peer RSN IE */
+ size_t rsnie_p_len;
+ u32 lifetime;
+ int cipher; /* Selected cipher (WPA_CIPHER_*) */
+ u8 dtoken;
+
+ struct tpk {
+ u8 kck[16]; /* TPK-KCK */
+ u8 tk[16]; /* TPK-TK; assuming only CCMP will be used */
+ } tpk;
+ int tpk_set;
+ int tpk_success;
+
+ struct tpk_timer {
+ u8 dest[ETH_ALEN];
+ int count; /* Retry Count */
+ int timer; /* Timeout in milliseconds */
+ u8 action_code; /* TDLS frame type */
+ u8 dialog_token;
+ u16 status_code;
+ int buf_len; /* length of TPK message for retransmission */
+ u8 *buf; /* buffer for TPK message */
+ } sm_tmr;
+
+ u16 capability;
+
+ u8 supp_rates[IEEE80211_MAX_SUPP_RATES];
+ size_t supp_rates_len;
+};
+
+
+static int wpa_tdls_get_privacy(struct wpa_sm *sm)
+{
+ /*
+ * Get info needed from supplicant to check if the current BSS supports
+ * security. Other than OPEN mode, rest are considered secured
+ * WEP/WPA/WPA2 hence TDLS frames are processed for TPK handshake.
+ */
+ return sm->pairwise_cipher != WPA_CIPHER_NONE;
+}
+
+
+static u8 * wpa_add_ie(u8 *pos, const u8 *ie, size_t ie_len)
+{
+ os_memcpy(pos, ie, ie_len);
+ return pos + ie_len;
+}
+
+
+static int wpa_tdls_del_key(struct wpa_sm *sm, struct wpa_tdls_peer *peer)
+{
+ if (wpa_sm_set_key(sm, WPA_ALG_NONE, peer->addr,
+ 0, 0, NULL, 0, NULL, 0) < 0) {
+ wpa_printf(MSG_WARNING, "TDLS: Failed to delete TPK-TK from "
+ "the driver");
+ return -1;
+ }
+
+ return 0;
+}
+
+
+static int wpa_tdls_set_key(struct wpa_sm *sm, struct wpa_tdls_peer *peer)
+{
+ u8 key_len;
+ u8 rsc[6];
+ enum wpa_alg alg;
+
+ os_memset(rsc, 0, 6);
+
+ switch (peer->cipher) {
+ case WPA_CIPHER_CCMP:
+ alg = WPA_ALG_CCMP;
+ key_len = 16;
+ break;
+ case WPA_CIPHER_NONE:
+ wpa_printf(MSG_DEBUG, "TDLS: Pairwise Cipher Suite: "
+ "NONE - do not use pairwise keys");
+ return -1;
+ default:
+ wpa_printf(MSG_WARNING, "TDLS: Unsupported pairwise cipher %d",
+ sm->pairwise_cipher);
+ return -1;
+ }
+
+ if (wpa_sm_set_key(sm, alg, peer->addr, -1, 1,
+ rsc, sizeof(rsc), peer->tpk.tk, key_len) < 0) {
+ wpa_printf(MSG_WARNING, "TDLS: Failed to set TPK to the "
+ "driver");
+ return -1;
+ }
+ return 0;
+}
+
+
+static int wpa_tdls_send_tpk_msg(struct wpa_sm *sm, const u8 *dst,
+ u8 action_code, u8 dialog_token,
+ u16 status_code, const u8 *buf, size_t len)
+{
+ return wpa_sm_send_tdls_mgmt(sm, dst, action_code, dialog_token,
+ status_code, buf, len);
+}
+
+
+static int wpa_tdls_tpk_send(struct wpa_sm *sm, const u8 *dest, u8 action_code,
+ u8 dialog_token, u16 status_code,
+ const u8 *msg, size_t msg_len)
+{
+ struct wpa_tdls_peer *peer;
+
+ wpa_printf(MSG_DEBUG, "TDLS: TPK send dest=" MACSTR " action_code=%u "
+ "dialog_token=%u status_code=%u msg_len=%u",
+ MAC2STR(dest), action_code, dialog_token, status_code,
+ (unsigned int) msg_len);
+
+ if (wpa_tdls_send_tpk_msg(sm, dest, action_code, dialog_token,
+ status_code, msg, msg_len)) {
+ wpa_printf(MSG_INFO, "TDLS: Failed to send message "
+ "(action_code=%u)", action_code);
+ return -1;
+ }
+
+ if (action_code == WLAN_TDLS_SETUP_CONFIRM ||
+ action_code == WLAN_TDLS_TEARDOWN ||
+ action_code == WLAN_TDLS_DISCOVERY_REQUEST ||
+ action_code == WLAN_TDLS_DISCOVERY_RESPONSE)
+ return 0; /* No retries */
+
+ for (peer = sm->tdls; peer; peer = peer->next) {
+ if (os_memcmp(peer->addr, dest, ETH_ALEN) == 0)
+ break;
+ }
+
+ if (peer == NULL) {
+ wpa_printf(MSG_INFO, "TDLS: No matching entry found for "
+ "retry " MACSTR, MAC2STR(dest));
+ return 0;
+ }
+
+ eloop_cancel_timeout(wpa_tdls_tpk_retry_timeout, sm, peer);
+
+ peer->sm_tmr.count = TPK_RETRY_COUNT;
+ peer->sm_tmr.timer = TPK_TIMEOUT;
+
+ /* Copy message to resend on timeout */
+ os_memcpy(peer->sm_tmr.dest, dest, ETH_ALEN);
+ peer->sm_tmr.action_code = action_code;
+ peer->sm_tmr.dialog_token = dialog_token;
+ peer->sm_tmr.status_code = status_code;
+ peer->sm_tmr.buf_len = msg_len;
+ os_free(peer->sm_tmr.buf);
+ peer->sm_tmr.buf = os_malloc(msg_len);
+ if (peer->sm_tmr.buf == NULL)
+ return -1;
+ os_memcpy(peer->sm_tmr.buf, msg, msg_len);
+
+ wpa_printf(MSG_DEBUG, "TDLS: Retry timeout registered "
+ "(action_code=%u)", action_code);
+ eloop_register_timeout(peer->sm_tmr.timer / 1000, 0,
+ wpa_tdls_tpk_retry_timeout, sm, peer);
+ return 0;
+}
+
+
+static int wpa_tdls_do_teardown(struct wpa_sm *sm, struct wpa_tdls_peer *peer,
+ u16 reason_code, int free_peer)
+{
+ int ret;
+
+ if (sm->tdls_external_setup) {
+ ret = wpa_tdls_send_teardown(sm, peer->addr, reason_code);
+
+ /* disable the link after teardown was sent */
+ wpa_sm_tdls_oper(sm, TDLS_DISABLE_LINK, peer->addr);
+ } else {
+ ret = wpa_sm_tdls_oper(sm, TDLS_TEARDOWN, peer->addr);
+ }
+
+ if (sm->tdls_external_setup || free_peer)
+ wpa_tdls_peer_free(sm, peer);
+
+ return ret;
+}
+
+
+static void wpa_tdls_tpk_retry_timeout(void *eloop_ctx, void *timeout_ctx)
+{
+
+ struct wpa_sm *sm = eloop_ctx;
+ struct wpa_tdls_peer *peer = timeout_ctx;
+
+ if (peer->sm_tmr.count) {
+ peer->sm_tmr.count--;
+ peer->sm_tmr.timer = TPK_TIMEOUT;
+
+ wpa_printf(MSG_INFO, "TDLS: Retrying sending of message "
+ "(action_code=%u)",
+ peer->sm_tmr.action_code);
+
+ if (peer->sm_tmr.buf == NULL) {
+ wpa_printf(MSG_INFO, "TDLS: No retry buffer available "
+ "for action_code=%u",
+ peer->sm_tmr.action_code);
+ eloop_cancel_timeout(wpa_tdls_tpk_retry_timeout, sm,
+ peer);
+ return;
+ }
+
+ /* resend TPK Handshake Message to Peer */
+ if (wpa_tdls_send_tpk_msg(sm, peer->sm_tmr.dest,
+ peer->sm_tmr.action_code,
+ peer->sm_tmr.dialog_token,
+ peer->sm_tmr.status_code,
+ peer->sm_tmr.buf,
+ peer->sm_tmr.buf_len)) {
+ wpa_printf(MSG_INFO, "TDLS: Failed to retry "
+ "transmission");
+ }
+
+ eloop_cancel_timeout(wpa_tdls_tpk_retry_timeout, sm, peer);
+ eloop_register_timeout(peer->sm_tmr.timer / 1000, 0,
+ wpa_tdls_tpk_retry_timeout, sm, peer);
+ } else {
+ eloop_cancel_timeout(wpa_tdls_tpk_retry_timeout, sm, peer);
+
+ wpa_printf(MSG_DEBUG, "TDLS: Sending Teardown Request");
+ wpa_tdls_do_teardown(sm, peer,
+ WLAN_REASON_TDLS_TEARDOWN_UNSPECIFIED, 1);
+ }
+}
+
+
+static void wpa_tdls_tpk_retry_timeout_cancel(struct wpa_sm *sm,
+ struct wpa_tdls_peer *peer,
+ u8 action_code)
+{
+ if (action_code == peer->sm_tmr.action_code) {
+ wpa_printf(MSG_DEBUG, "TDLS: Retry timeout cancelled for "
+ "action_code=%u", action_code);
+
+ /* Cancel Timeout registered */
+ eloop_cancel_timeout(wpa_tdls_tpk_retry_timeout, sm, peer);
+
+ /* free all resources meant for retry */
+ os_free(peer->sm_tmr.buf);
+ peer->sm_tmr.buf = NULL;
+
+ peer->sm_tmr.count = 0;
+ peer->sm_tmr.timer = 0;
+ peer->sm_tmr.buf_len = 0;
+ peer->sm_tmr.action_code = 0xff;
+ } else {
+ wpa_printf(MSG_INFO, "TDLS: Error in cancelling retry timeout "
+ "(Unknown action_code=%u)", action_code);
+ }
+}
+
+
+static void wpa_tdls_generate_tpk(struct wpa_tdls_peer *peer,
+ const u8 *own_addr, const u8 *bssid)
+{
+ u8 key_input[SHA256_MAC_LEN];
+ const u8 *nonce[2];
+ size_t len[2];
+ u8 data[3 * ETH_ALEN];
+
+ /* IEEE Std 802.11z-2010 8.5.9.1:
+ * TPK-Key-Input = SHA-256(min(SNonce, ANonce) || max(SNonce, ANonce))
+ */
+ len[0] = WPA_NONCE_LEN;
+ len[1] = WPA_NONCE_LEN;
+ if (os_memcmp(peer->inonce, peer->rnonce, WPA_NONCE_LEN) < 0) {
+ nonce[0] = peer->inonce;
+ nonce[1] = peer->rnonce;
+ } else {
+ nonce[0] = peer->rnonce;
+ nonce[1] = peer->inonce;
+ }
+ wpa_hexdump(MSG_DEBUG, "TDLS: min(Nonce)", nonce[0], WPA_NONCE_LEN);
+ wpa_hexdump(MSG_DEBUG, "TDLS: max(Nonce)", nonce[1], WPA_NONCE_LEN);
+ sha256_vector(2, nonce, len, key_input);
+ wpa_hexdump_key(MSG_DEBUG, "TDLS: TPK-Key-Input",
+ key_input, SHA256_MAC_LEN);
+
+ /*
+ * TPK-Key-Data = KDF-N_KEY(TPK-Key-Input, "TDLS PMK",
+ * min(MAC_I, MAC_R) || max(MAC_I, MAC_R) || BSSID || N_KEY)
+ * TODO: is N_KEY really included in KDF Context and if so, in which
+ * presentation format (little endian 16-bit?) is it used? It gets
+ * added by the KDF anyway..
+ */
+
+ if (os_memcmp(own_addr, peer->addr, ETH_ALEN) < 0) {
+ os_memcpy(data, own_addr, ETH_ALEN);
+ os_memcpy(data + ETH_ALEN, peer->addr, ETH_ALEN);
+ } else {
+ os_memcpy(data, peer->addr, ETH_ALEN);
+ os_memcpy(data + ETH_ALEN, own_addr, ETH_ALEN);
+ }
+ os_memcpy(data + 2 * ETH_ALEN, bssid, ETH_ALEN);
+ wpa_hexdump(MSG_DEBUG, "TDLS: KDF Context", data, sizeof(data));
+
+ sha256_prf(key_input, SHA256_MAC_LEN, "TDLS PMK", data, sizeof(data),
+ (u8 *) &peer->tpk, sizeof(peer->tpk));
+ wpa_hexdump_key(MSG_DEBUG, "TDLS: TPK-KCK",
+ peer->tpk.kck, sizeof(peer->tpk.kck));
+ wpa_hexdump_key(MSG_DEBUG, "TDLS: TPK-TK",
+ peer->tpk.tk, sizeof(peer->tpk.tk));
+ peer->tpk_set = 1;
+}
+
+
+/**
+ * wpa_tdls_ftie_mic - Calculate TDLS FTIE MIC
+ * @kck: TPK-KCK
+ * @lnkid: Pointer to the beginning of Link Identifier IE
+ * @rsnie: Pointer to the beginning of RSN IE used for handshake
+ * @timeoutie: Pointer to the beginning of Timeout IE used for handshake
+ * @ftie: Pointer to the beginning of FT IE
+ * @mic: Pointer for writing MIC
+ *
+ * Calculate MIC for TDLS frame.
+ */
+static int wpa_tdls_ftie_mic(const u8 *kck, u8 trans_seq, const u8 *lnkid,
+ const u8 *rsnie, const u8 *timeoutie,
+ const u8 *ftie, u8 *mic)
+{
+ u8 *buf, *pos;
+ struct wpa_tdls_ftie *_ftie;
+ const struct wpa_tdls_lnkid *_lnkid;
+ int ret;
+ int len = 2 * ETH_ALEN + 1 + 2 + lnkid[1] + 2 + rsnie[1] +
+ 2 + timeoutie[1] + 2 + ftie[1];
+ buf = os_zalloc(len);
+ if (!buf) {
+ wpa_printf(MSG_WARNING, "TDLS: No memory for MIC calculation");
+ return -1;
+ }
+
+ pos = buf;
+ _lnkid = (const struct wpa_tdls_lnkid *) lnkid;
+ /* 1) TDLS initiator STA MAC address */
+ os_memcpy(pos, _lnkid->init_sta, ETH_ALEN);
+ pos += ETH_ALEN;
+ /* 2) TDLS responder STA MAC address */
+ os_memcpy(pos, _lnkid->resp_sta, ETH_ALEN);
+ pos += ETH_ALEN;
+ /* 3) Transaction Sequence number */
+ *pos++ = trans_seq;
+ /* 4) Link Identifier IE */
+ os_memcpy(pos, lnkid, 2 + lnkid[1]);
+ pos += 2 + lnkid[1];
+ /* 5) RSN IE */
+ os_memcpy(pos, rsnie, 2 + rsnie[1]);
+ pos += 2 + rsnie[1];
+ /* 6) Timeout Interval IE */
+ os_memcpy(pos, timeoutie, 2 + timeoutie[1]);
+ pos += 2 + timeoutie[1];
+ /* 7) FTIE, with the MIC field of the FTIE set to 0 */
+ os_memcpy(pos, ftie, 2 + ftie[1]);
+ _ftie = (struct wpa_tdls_ftie *) pos;
+ os_memset(_ftie->mic, 0, TDLS_MIC_LEN);
+ pos += 2 + ftie[1];
+
+ wpa_hexdump(MSG_DEBUG, "TDLS: Data for FTIE MIC", buf, pos - buf);
+ wpa_hexdump_key(MSG_DEBUG, "TDLS: KCK", kck, 16);
+ ret = omac1_aes_128(kck, buf, pos - buf, mic);
+ os_free(buf);
+ wpa_hexdump(MSG_DEBUG, "TDLS: FTIE MIC", mic, 16);
+ return ret;
+}
+
+
+/**
+ * wpa_tdls_key_mic_teardown - Calculate TDLS FTIE MIC for Teardown frame
+ * @kck: TPK-KCK
+ * @trans_seq: Transaction Sequence Number (4 - Teardown)
+ * @rcode: Reason code for Teardown
+ * @dtoken: Dialog Token used for that particular link
+ * @lnkid: Pointer to the beginning of Link Identifier IE
+ * @ftie: Pointer to the beginning of FT IE
+ * @mic: Pointer for writing MIC
+ *
+ * Calculate MIC for TDLS frame.
+ */
+static int wpa_tdls_key_mic_teardown(const u8 *kck, u8 trans_seq, u16 rcode,
+ u8 dtoken, const u8 *lnkid,
+ const u8 *ftie, u8 *mic)
+{
+ u8 *buf, *pos;
+ struct wpa_tdls_ftie *_ftie;
+ int ret;
+ int len;
+
+ if (lnkid == NULL)
+ return -1;
+
+ len = 2 + lnkid[1] + sizeof(rcode) + sizeof(dtoken) +
+ sizeof(trans_seq) + 2 + ftie[1];
+
+ buf = os_zalloc(len);
+ if (!buf) {
+ wpa_printf(MSG_WARNING, "TDLS: No memory for MIC calculation");
+ return -1;
+ }
+
+ pos = buf;
+ /* 1) Link Identifier IE */
+ os_memcpy(pos, lnkid, 2 + lnkid[1]);
+ pos += 2 + lnkid[1];
+ /* 2) Reason Code */
+ WPA_PUT_LE16(pos, rcode);
+ pos += sizeof(rcode);
+ /* 3) Dialog token */
+ *pos++ = dtoken;
+ /* 4) Transaction Sequence number */
+ *pos++ = trans_seq;
+ /* 7) FTIE, with the MIC field of the FTIE set to 0 */
+ os_memcpy(pos, ftie, 2 + ftie[1]);
+ _ftie = (struct wpa_tdls_ftie *) pos;
+ os_memset(_ftie->mic, 0, TDLS_MIC_LEN);
+ pos += 2 + ftie[1];
+
+ wpa_hexdump(MSG_DEBUG, "TDLS: Data for FTIE MIC", buf, pos - buf);
+ wpa_hexdump_key(MSG_DEBUG, "TDLS: KCK", kck, 16);
+ ret = omac1_aes_128(kck, buf, pos - buf, mic);
+ os_free(buf);
+ wpa_hexdump(MSG_DEBUG, "TDLS: FTIE MIC", mic, 16);
+ return ret;
+}
+
+
+static int wpa_supplicant_verify_tdls_mic(u8 trans_seq,
+ struct wpa_tdls_peer *peer,
+ const u8 *lnkid, const u8 *timeoutie,
+ const struct wpa_tdls_ftie *ftie)
+{
+ u8 mic[16];
+
+ if (peer->tpk_set) {
+ wpa_tdls_ftie_mic(peer->tpk.kck, trans_seq, lnkid,
+ peer->rsnie_p, timeoutie, (u8 *) ftie,
+ mic);
+ if (os_memcmp(mic, ftie->mic, 16) != 0) {
+ wpa_printf(MSG_INFO, "TDLS: Invalid MIC in FTIE - "
+ "dropping packet");
+ wpa_hexdump(MSG_DEBUG, "TDLS: Received MIC",
+ ftie->mic, 16);
+ wpa_hexdump(MSG_DEBUG, "TDLS: Calculated MIC",
+ mic, 16);
+ return -1;
+ }
+ } else {
+ wpa_printf(MSG_WARNING, "TDLS: Could not verify TDLS MIC, "
+ "TPK not set - dropping packet");
+ return -1;
+ }
+ return 0;
+}
+
+
+static int wpa_supplicant_verify_tdls_mic_teardown(
+ u8 trans_seq, u16 rcode, u8 dtoken, struct wpa_tdls_peer *peer,
+ const u8 *lnkid, const struct wpa_tdls_ftie *ftie)
+{
+ u8 mic[16];
+
+ if (peer->tpk_set) {
+ wpa_tdls_key_mic_teardown(peer->tpk.kck, trans_seq, rcode,
+ dtoken, lnkid, (u8 *) ftie, mic);
+ if (os_memcmp(mic, ftie->mic, 16) != 0) {
+ wpa_printf(MSG_INFO, "TDLS: Invalid MIC in Teardown - "
+ "dropping packet");
+ return -1;
+ }
+ } else {
+ wpa_printf(MSG_INFO, "TDLS: Could not verify TDLS Teardown "
+ "MIC, TPK not set - dropping packet");
+ return -1;
+ }
+ return 0;
+}
+
+
+static void wpa_tdls_tpk_timeout(void *eloop_ctx, void *timeout_ctx)
+{
+ struct wpa_sm *sm = eloop_ctx;
+ struct wpa_tdls_peer *peer = timeout_ctx;
+
+ /*
+ * On TPK lifetime expiration, we have an option of either tearing down
+ * the direct link or trying to re-initiate it. The selection of what
+ * to do is not strictly speaking controlled by our role in the expired
+ * link, but for now, use that to select whether to renew or tear down
+ * the link.
+ */
+
+ if (peer->initiator) {
+ wpa_printf(MSG_DEBUG, "TDLS: TPK lifetime expired for " MACSTR
+ " - try to renew", MAC2STR(peer->addr));
+ wpa_tdls_start(sm, peer->addr);
+ } else {
+ wpa_printf(MSG_DEBUG, "TDLS: TPK lifetime expired for " MACSTR
+ " - tear down", MAC2STR(peer->addr));
+ wpa_tdls_do_teardown(sm, peer,
+ WLAN_REASON_TDLS_TEARDOWN_UNSPECIFIED, 1);
+ }
+}
+
+
+static void wpa_tdls_peer_free(struct wpa_sm *sm, struct wpa_tdls_peer *peer)
+{
+ wpa_printf(MSG_DEBUG, "TDLS: Clear state for peer " MACSTR,
+ MAC2STR(peer->addr));
+ eloop_cancel_timeout(wpa_tdls_tpk_timeout, sm, peer);
+ eloop_cancel_timeout(wpa_tdls_tpk_retry_timeout, sm, peer);
+ peer->initiator = 0;
+ os_free(peer->sm_tmr.buf);
+ peer->sm_tmr.buf = NULL;
+ peer->rsnie_i_len = peer->rsnie_p_len = 0;
+ peer->cipher = 0;
+ peer->tpk_set = peer->tpk_success = 0;
+ os_memset(&peer->tpk, 0, sizeof(peer->tpk));
+ os_memset(peer->inonce, 0, WPA_NONCE_LEN);
+ os_memset(peer->rnonce, 0, WPA_NONCE_LEN);
+}
+
+
+static void wpa_tdls_linkid(struct wpa_sm *sm, struct wpa_tdls_peer *peer,
+ struct wpa_tdls_lnkid *lnkid)
+{
+ lnkid->ie_type = WLAN_EID_LINK_ID;
+ lnkid->ie_len = 3 * ETH_ALEN;
+ os_memcpy(lnkid->bssid, sm->bssid, ETH_ALEN);
+ if (peer->initiator) {
+ os_memcpy(lnkid->init_sta, sm->own_addr, ETH_ALEN);
+ os_memcpy(lnkid->resp_sta, peer->addr, ETH_ALEN);
+ } else {
+ os_memcpy(lnkid->init_sta, peer->addr, ETH_ALEN);
+ os_memcpy(lnkid->resp_sta, sm->own_addr, ETH_ALEN);
+ }
+}
+
+
+int wpa_tdls_send_teardown(struct wpa_sm *sm, const u8 *addr, u16 reason_code)
+{
+ struct wpa_tdls_peer *peer;
+ struct wpa_tdls_ftie *ftie;
+ struct wpa_tdls_lnkid lnkid;
+ u8 dialog_token;
+ u8 *rbuf, *pos;
+ int ielen;
+
+ if (sm->tdls_disabled || !sm->tdls_supported)
+ return -1;
+
+ /* Find the node and free from the list */
+ for (peer = sm->tdls; peer; peer = peer->next) {
+ if (os_memcmp(peer->addr, addr, ETH_ALEN) == 0)
+ break;
+ }
+
+ if (peer == NULL) {
+ wpa_printf(MSG_INFO, "TDLS: No matching entry found for "
+ "Teardown " MACSTR, MAC2STR(addr));
+ return 0;
+ }
+
+ dialog_token = peer->dtoken;
+
+ wpa_printf(MSG_DEBUG, "TDLS: TDLS Teardown for " MACSTR,
+ MAC2STR(addr));
+
+ ielen = 0;
+ if (wpa_tdls_get_privacy(sm) && peer->tpk_set && peer->tpk_success) {
+ /* To add FTIE for Teardown request and compute MIC */
+ ielen += sizeof(*ftie);
+#ifdef CONFIG_TDLS_TESTING
+ if (tdls_testing & TDLS_TESTING_LONG_FRAME)
+ ielen += 170;
+#endif /* CONFIG_TDLS_TESTING */
+ }
+
+ rbuf = os_zalloc(ielen + 1);
+ if (rbuf == NULL)
+ return -1;
+ pos = rbuf;
+
+ if (!wpa_tdls_get_privacy(sm) || !peer->tpk_set || !peer->tpk_success)
+ goto skip_ies;
+
+ ftie = (struct wpa_tdls_ftie *) pos;
+ ftie->ie_type = WLAN_EID_FAST_BSS_TRANSITION;
+ /* Using the recent nonce which should be for CONFIRM frame */
+ os_memcpy(ftie->Anonce, peer->rnonce, WPA_NONCE_LEN);
+ os_memcpy(ftie->Snonce, peer->inonce, WPA_NONCE_LEN);
+ ftie->ie_len = sizeof(struct wpa_tdls_ftie) - 2;
+ pos = (u8 *) (ftie + 1);
+#ifdef CONFIG_TDLS_TESTING
+ if (tdls_testing & TDLS_TESTING_LONG_FRAME) {
+ wpa_printf(MSG_DEBUG, "TDLS: Testing - add extra subelem to "
+ "FTIE");
+ ftie->ie_len += 170;
+ *pos++ = 255; /* FTIE subelem */
+ *pos++ = 168; /* FTIE subelem length */
+ pos += 168;
+ }
+#endif /* CONFIG_TDLS_TESTING */
+ wpa_hexdump(MSG_DEBUG, "TDLS: FTIE for TDLS Teardown handshake",
+ (u8 *) ftie, pos - (u8 *) ftie);
+
+ /* compute MIC before sending */
+ wpa_tdls_linkid(sm, peer, &lnkid);
+ wpa_tdls_key_mic_teardown(peer->tpk.kck, 4, reason_code,
+ dialog_token, (u8 *) &lnkid, (u8 *) ftie,
+ ftie->mic);
+
+skip_ies:
+ /* TODO: register for a Timeout handler, if Teardown is not received at
+ * the other end, then try again another time */
+
+ /* request driver to send Teardown using this FTIE */
+ wpa_tdls_tpk_send(sm, addr, WLAN_TDLS_TEARDOWN, 0,
+ WLAN_REASON_TDLS_TEARDOWN_UNSPECIFIED, rbuf,
+ pos - rbuf);
+ os_free(rbuf);
+
+ /* clear the Peerkey statemachine */
+ wpa_tdls_peer_free(sm, peer);
+
+ return 0;
+}
+
+
+int wpa_tdls_teardown_link(struct wpa_sm *sm, const u8 *addr, u16 reason_code)
+{
+ struct wpa_tdls_peer *peer;
+
+ if (sm->tdls_disabled || !sm->tdls_supported)
+ return -1;
+
+ for (peer = sm->tdls; peer; peer = peer->next) {
+ if (os_memcmp(peer->addr, addr, ETH_ALEN) == 0)
+ break;
+ }
+
+ if (peer == NULL) {
+ wpa_printf(MSG_DEBUG, "TDLS: Could not find peer " MACSTR
+ " for link Teardown", MAC2STR(addr));
+ return -1;
+ }
+
+ if (!peer->tpk_success) {
+ wpa_printf(MSG_DEBUG, "TDLS: Peer " MACSTR
+ " not connected - cannot Teardown link", MAC2STR(addr));
+ return -1;
+ }
+
+ return wpa_tdls_do_teardown(sm, peer, reason_code, 0);
+}
+
+
+void wpa_tdls_disable_link(struct wpa_sm *sm, const u8 *addr)
+{
+ struct wpa_tdls_peer *peer;
+
+ for (peer = sm->tdls; peer; peer = peer->next) {
+ if (os_memcmp(peer->addr, addr, ETH_ALEN) == 0)
+ break;
+ }
+
+ if (peer) {
+ wpa_sm_tdls_oper(sm, TDLS_DISABLE_LINK, addr);
+ wpa_tdls_peer_free(sm, peer);
+ }
+}
+
+
+static int wpa_tdls_recv_teardown(struct wpa_sm *sm, const u8 *src_addr,
+ const u8 *buf, size_t len)
+{
+ struct wpa_tdls_peer *peer = NULL;
+ struct wpa_tdls_ftie *ftie;
+ struct wpa_tdls_lnkid *lnkid;
+ struct wpa_eapol_ie_parse kde;
+ u16 reason_code;
+ const u8 *pos;
+ int ielen;
+
+ /* Find the node and free from the list */
+ for (peer = sm->tdls; peer; peer = peer->next) {
+ if (os_memcmp(peer->addr, src_addr, ETH_ALEN) == 0)
+ break;
+ }
+
+ if (peer == NULL) {
+ wpa_printf(MSG_INFO, "TDLS: No matching entry found for "
+ "Teardown " MACSTR, MAC2STR(src_addr));
+ return 0;
+ }
+
+ pos = buf;
+ pos += 1 /* pkt_type */ + 1 /* Category */ + 1 /* Action */;
+
+ reason_code = WPA_GET_LE16(pos);
+ pos += 2;
+
+ wpa_printf(MSG_DEBUG, "TDLS: TDLS Teardown Request from " MACSTR
+ " (reason code %u)", MAC2STR(src_addr), reason_code);
+
+ ielen = len - (pos - buf); /* start of IE in buf */
+ if (wpa_supplicant_parse_ies((const u8 *) pos, ielen, &kde) < 0) {
+ wpa_printf(MSG_INFO, "TDLS: Failed to parse IEs in Teardown");
+ return -1;
+ }
+
+ if (kde.lnkid == NULL || kde.lnkid_len < 3 * ETH_ALEN) {
+ wpa_printf(MSG_INFO, "TDLS: No Link Identifier IE in TDLS "
+ "Teardown");
+ return -1;
+ }
+ lnkid = (struct wpa_tdls_lnkid *) kde.lnkid;
+
+ if (!wpa_tdls_get_privacy(sm) || !peer->tpk_set || !peer->tpk_success)
+ goto skip_ftie;
+
+ if (kde.ftie == NULL || kde.ftie_len < sizeof(*ftie)) {
+ wpa_printf(MSG_INFO, "TDLS: No FTIE in TDLS Teardown");
+ return -1;
+ }
+
+ ftie = (struct wpa_tdls_ftie *) kde.ftie;
+
+ /* Process MIC check to see if TDLS Teardown is right */
+ if (wpa_supplicant_verify_tdls_mic_teardown(4, reason_code,
+ peer->dtoken, peer,
+ (u8 *) lnkid, ftie) < 0) {
+ wpa_printf(MSG_DEBUG, "TDLS: MIC failure for TDLS "
+ "Teardown Request from " MACSTR, MAC2STR(src_addr));
+ return -1;
+ }
+
+skip_ftie:
+ /*
+ * Request the driver to disable the direct link and clear associated
+ * keys.
+ */
+ wpa_sm_tdls_oper(sm, TDLS_DISABLE_LINK, src_addr);
+
+ /* clear the Peerkey statemachine */
+ wpa_tdls_peer_free(sm, peer);
+
+ return 0;
+}
+
+
+/**
+ * wpa_tdls_send_error - To send suitable TDLS status response with
+ * appropriate status code mentioning reason for error/failure.
+ * @dst - MAC addr of Peer station
+ * @tdls_action - TDLS frame type for which error code is sent
+ * @status - status code mentioning reason
+ */
+
+static int wpa_tdls_send_error(struct wpa_sm *sm, const u8 *dst,
+ u8 tdls_action, u8 dialog_token, u16 status)
+{
+ wpa_printf(MSG_DEBUG, "TDLS: Sending error to " MACSTR
+ " (action=%u status=%u)",
+ MAC2STR(dst), tdls_action, status);
+ return wpa_tdls_tpk_send(sm, dst, tdls_action, dialog_token, status,
+ NULL, 0);
+}
+
+
+static struct wpa_tdls_peer *
+wpa_tdls_add_peer(struct wpa_sm *sm, const u8 *addr)
+{
+ struct wpa_tdls_peer *peer;
+
+ wpa_printf(MSG_INFO, "TDLS: Creating peer entry for " MACSTR,
+ MAC2STR(addr));
+
+ peer = os_zalloc(sizeof(*peer));
+ if (peer == NULL)
+ return NULL;
+
+ os_memcpy(peer->addr, addr, ETH_ALEN);
+ peer->next = sm->tdls;
+ sm->tdls = peer;
+
+ return peer;
+}
+
+
+static int wpa_tdls_send_tpk_m1(struct wpa_sm *sm,
+ struct wpa_tdls_peer *peer)
+{
+ size_t buf_len;
+ struct wpa_tdls_timeoutie timeoutie;
+ u16 rsn_capab;
+ struct wpa_tdls_ftie *ftie;
+ u8 *rbuf, *pos, *count_pos;
+ u16 count;
+ struct rsn_ie_hdr *hdr;
+
+ if (!wpa_tdls_get_privacy(sm)) {
+ wpa_printf(MSG_DEBUG, "TDLS: No security used on the link");
+ peer->rsnie_i_len = 0;
+ goto skip_rsnie;
+ }
+
+ /*
+ * TPK Handshake Message 1:
+ * FTIE: ANonce=0, SNonce=initiator nonce MIC=0, DataKDs=(RSNIE_I,
+ * Timeout Interval IE))
+ */
+
+ /* Filling RSN IE */
+ hdr = (struct rsn_ie_hdr *) peer->rsnie_i;
+ hdr->elem_id = WLAN_EID_RSN;
+ WPA_PUT_LE16(hdr->version, RSN_VERSION);
+
+ pos = (u8 *) (hdr + 1);
+ RSN_SELECTOR_PUT(pos, RSN_CIPHER_SUITE_NO_GROUP_ADDRESSED);
+ pos += RSN_SELECTOR_LEN;
+ count_pos = pos;
+ pos += 2;
+
+ count = 0;
+
+ /*
+ * AES-CCMP is the default Encryption preferred for TDLS, so
+ * RSN IE is filled only with CCMP CIPHER
+ * Note: TKIP is not used to encrypt TDLS link.
+ *
+ * Regardless of the cipher used on the AP connection, select CCMP
+ * here.
+ */
+ RSN_SELECTOR_PUT(pos, RSN_CIPHER_SUITE_CCMP);
+ pos += RSN_SELECTOR_LEN;
+ count++;
+
+ WPA_PUT_LE16(count_pos, count);
+
+ WPA_PUT_LE16(pos, 1);
+ pos += 2;
+ RSN_SELECTOR_PUT(pos, RSN_AUTH_KEY_MGMT_TPK_HANDSHAKE);
+ pos += RSN_SELECTOR_LEN;
+
+ rsn_capab = WPA_CAPABILITY_PEERKEY_ENABLED;
+ rsn_capab |= RSN_NUM_REPLAY_COUNTERS_16 << 2;
+#ifdef CONFIG_TDLS_TESTING
+ if (tdls_testing & TDLS_TESTING_ALT_RSN_IE) {
+ wpa_printf(MSG_DEBUG, "TDLS: Use alternative RSN IE for "
+ "testing");
+ rsn_capab = WPA_CAPABILITY_PEERKEY_ENABLED;
+ }
+#endif /* CONFIG_TDLS_TESTING */
+ WPA_PUT_LE16(pos, rsn_capab);
+ pos += 2;
+#ifdef CONFIG_TDLS_TESTING
+ if (tdls_testing & TDLS_TESTING_ALT_RSN_IE) {
+ /* Number of PMKIDs */
+ *pos++ = 0x00;
+ *pos++ = 0x00;
+ }
+#endif /* CONFIG_TDLS_TESTING */
+
+ hdr->len = (pos - peer->rsnie_i) - 2;
+ peer->rsnie_i_len = pos - peer->rsnie_i;
+ wpa_hexdump(MSG_DEBUG, "TDLS: RSN IE for TPK handshake",
+ peer->rsnie_i, peer->rsnie_i_len);
+
+skip_rsnie:
+ buf_len = 0;
+ if (wpa_tdls_get_privacy(sm))
+ buf_len += peer->rsnie_i_len + sizeof(struct wpa_tdls_ftie) +
+ sizeof(struct wpa_tdls_timeoutie);
+#ifdef CONFIG_TDLS_TESTING
+ if (wpa_tdls_get_privacy(sm) &&
+ (tdls_testing & TDLS_TESTING_LONG_FRAME))
+ buf_len += 170;
+ if (tdls_testing & TDLS_TESTING_DIFF_BSSID)
+ buf_len += sizeof(struct wpa_tdls_lnkid);
+#endif /* CONFIG_TDLS_TESTING */
+ rbuf = os_zalloc(buf_len + 1);
+ if (rbuf == NULL) {
+ wpa_tdls_peer_free(sm, peer);
+ return -1;
+ }
+ pos = rbuf;
+
+ if (!wpa_tdls_get_privacy(sm))
+ goto skip_ies;
+
+ /* Initiator RSN IE */
+ pos = wpa_add_ie(pos, peer->rsnie_i, peer->rsnie_i_len);
+
+ ftie = (struct wpa_tdls_ftie *) pos;
+ ftie->ie_type = WLAN_EID_FAST_BSS_TRANSITION;
+ ftie->ie_len = sizeof(struct wpa_tdls_ftie) - 2;
+
+ if (os_get_random(peer->inonce, WPA_NONCE_LEN)) {
+ wpa_msg(sm->ctx->msg_ctx, MSG_WARNING,
+ "TDLS: Failed to get random data for initiator Nonce");
+ os_free(rbuf);
+ wpa_tdls_peer_free(sm, peer);
+ return -1;
+ }
+ wpa_hexdump(MSG_DEBUG, "TDLS: Initiator Nonce for TPK handshake",
+ peer->inonce, WPA_NONCE_LEN);
+ os_memcpy(ftie->Snonce, peer->inonce, WPA_NONCE_LEN);
+
+ wpa_hexdump(MSG_DEBUG, "TDLS: FTIE for TPK Handshake M1",
+ (u8 *) ftie, sizeof(struct wpa_tdls_ftie));
+
+ pos = (u8 *) (ftie + 1);
+
+#ifdef CONFIG_TDLS_TESTING
+ if (tdls_testing & TDLS_TESTING_LONG_FRAME) {
+ wpa_printf(MSG_DEBUG, "TDLS: Testing - add extra subelem to "
+ "FTIE");
+ ftie->ie_len += 170;
+ *pos++ = 255; /* FTIE subelem */
+ *pos++ = 168; /* FTIE subelem length */
+ pos += 168;
+ }
+#endif /* CONFIG_TDLS_TESTING */
+
+ /* Lifetime */
+ peer->lifetime = TPK_LIFETIME;
+#ifdef CONFIG_TDLS_TESTING
+ if (tdls_testing & TDLS_TESTING_SHORT_LIFETIME) {
+ wpa_printf(MSG_DEBUG, "TDLS: Testing - use short TPK "
+ "lifetime");
+ peer->lifetime = 301;
+ }
+ if (tdls_testing & TDLS_TESTING_LONG_LIFETIME) {
+ wpa_printf(MSG_DEBUG, "TDLS: Testing - use long TPK "
+ "lifetime");
+ peer->lifetime = 0xffffffff;
+ }
+#endif /* CONFIG_TDLS_TESTING */
+ pos = wpa_add_tdls_timeoutie(pos, (u8 *) &timeoutie,
+ sizeof(timeoutie), peer->lifetime);
+ wpa_printf(MSG_DEBUG, "TDLS: TPK lifetime %u seconds", peer->lifetime);
+
+skip_ies:
+
+#ifdef CONFIG_TDLS_TESTING
+ if (tdls_testing & TDLS_TESTING_DIFF_BSSID) {
+ wpa_printf(MSG_DEBUG, "TDLS: Testing - use incorrect BSSID in "
+ "Link Identifier");
+ struct wpa_tdls_lnkid *l = (struct wpa_tdls_lnkid *) pos;
+ wpa_tdls_linkid(sm, peer, l);
+ l->bssid[5] ^= 0x01;
+ pos += sizeof(*l);
+ }
+#endif /* CONFIG_TDLS_TESTING */
+
+ wpa_printf(MSG_DEBUG, "TDLS: Sending TDLS Setup Request / TPK "
+ "Handshake Message 1 (peer " MACSTR ")",
+ MAC2STR(peer->addr));
+
+ wpa_tdls_tpk_send(sm, peer->addr, WLAN_TDLS_SETUP_REQUEST, 1, 0,
+ rbuf, pos - rbuf);
+ os_free(rbuf);
+
+ return 0;
+}
+
+
+static int wpa_tdls_send_tpk_m2(struct wpa_sm *sm,
+ const unsigned char *src_addr, u8 dtoken,
+ struct wpa_tdls_lnkid *lnkid,
+ const struct wpa_tdls_peer *peer)
+{
+ u8 *rbuf, *pos;
+ size_t buf_len;
+ u32 lifetime;
+ struct wpa_tdls_timeoutie timeoutie;
+ struct wpa_tdls_ftie *ftie;
+
+ buf_len = 0;
+ if (wpa_tdls_get_privacy(sm)) {
+ /* Peer RSN IE, FTIE(Initiator Nonce, Responder Nonce),
+ * Lifetime */
+ buf_len += peer->rsnie_i_len + sizeof(struct wpa_tdls_ftie) +
+ sizeof(struct wpa_tdls_timeoutie);
+#ifdef CONFIG_TDLS_TESTING
+ if (tdls_testing & TDLS_TESTING_LONG_FRAME)
+ buf_len += 170;
+#endif /* CONFIG_TDLS_TESTING */
+ }
+
+ rbuf = os_zalloc(buf_len + 1);
+ if (rbuf == NULL)
+ return -1;
+ pos = rbuf;
+
+ if (!wpa_tdls_get_privacy(sm))
+ goto skip_ies;
+
+ /* Peer RSN IE */
+ pos = wpa_add_ie(pos, peer->rsnie_p, peer->rsnie_p_len);
+
+ ftie = (struct wpa_tdls_ftie *) pos;
+ ftie->ie_type = WLAN_EID_FAST_BSS_TRANSITION;
+ /* TODO: ftie->mic_control to set 2-RESPONSE */
+ os_memcpy(ftie->Anonce, peer->rnonce, WPA_NONCE_LEN);
+ os_memcpy(ftie->Snonce, peer->inonce, WPA_NONCE_LEN);
+ ftie->ie_len = sizeof(struct wpa_tdls_ftie) - 2;
+ wpa_hexdump(MSG_DEBUG, "TDLS: FTIE for TPK M2",
+ (u8 *) ftie, sizeof(*ftie));
+
+ pos = (u8 *) (ftie + 1);
+
+#ifdef CONFIG_TDLS_TESTING
+ if (tdls_testing & TDLS_TESTING_LONG_FRAME) {
+ wpa_printf(MSG_DEBUG, "TDLS: Testing - add extra subelem to "
+ "FTIE");
+ ftie->ie_len += 170;
+ *pos++ = 255; /* FTIE subelem */
+ *pos++ = 168; /* FTIE subelem length */
+ pos += 168;
+ }
+#endif /* CONFIG_TDLS_TESTING */
+
+ /* Lifetime */
+ lifetime = peer->lifetime;
+#ifdef CONFIG_TDLS_TESTING
+ if (tdls_testing & TDLS_TESTING_WRONG_LIFETIME_RESP) {
+ wpa_printf(MSG_DEBUG, "TDLS: Testing - use wrong TPK "
+ "lifetime in response");
+ lifetime++;
+ }
+#endif /* CONFIG_TDLS_TESTING */
+ pos = wpa_add_tdls_timeoutie(pos, (u8 *) &timeoutie,
+ sizeof(timeoutie), lifetime);
+ wpa_printf(MSG_DEBUG, "TDLS: TPK lifetime %u seconds from initiator",
+ lifetime);
+
+ /* compute MIC before sending */
+ wpa_tdls_ftie_mic(peer->tpk.kck, 2, (u8 *) lnkid, peer->rsnie_p,
+ (u8 *) &timeoutie, (u8 *) ftie, ftie->mic);
+
+skip_ies:
+ wpa_tdls_tpk_send(sm, src_addr, WLAN_TDLS_SETUP_RESPONSE, dtoken, 0,
+ rbuf, pos - rbuf);
+ os_free(rbuf);
+
+ return 0;
+}
+
+
+static int wpa_tdls_send_tpk_m3(struct wpa_sm *sm,
+ const unsigned char *src_addr, u8 dtoken,
+ struct wpa_tdls_lnkid *lnkid,
+ const struct wpa_tdls_peer *peer)
+{
+ u8 *rbuf, *pos;
+ size_t buf_len;
+ struct wpa_tdls_ftie *ftie;
+ struct wpa_tdls_timeoutie timeoutie;
+ u32 lifetime;
+
+ buf_len = 0;
+ if (wpa_tdls_get_privacy(sm)) {
+ /* Peer RSN IE, FTIE(Initiator Nonce, Responder Nonce),
+ * Lifetime */
+ buf_len += peer->rsnie_i_len + sizeof(struct wpa_tdls_ftie) +
+ sizeof(struct wpa_tdls_timeoutie);
+#ifdef CONFIG_TDLS_TESTING
+ if (tdls_testing & TDLS_TESTING_LONG_FRAME)
+ buf_len += 170;
+#endif /* CONFIG_TDLS_TESTING */
+ }
+
+ rbuf = os_zalloc(buf_len + 1);
+ if (rbuf == NULL)
+ return -1;
+ pos = rbuf;
+
+ if (!wpa_tdls_get_privacy(sm))
+ goto skip_ies;
+
+ /* Peer RSN IE */
+ pos = wpa_add_ie(pos, peer->rsnie_p, peer->rsnie_p_len);
+
+ ftie = (struct wpa_tdls_ftie *) pos;
+ ftie->ie_type = WLAN_EID_FAST_BSS_TRANSITION;
+ /*TODO: ftie->mic_control to set 3-CONFIRM */
+ os_memcpy(ftie->Anonce, peer->rnonce, WPA_NONCE_LEN);
+ os_memcpy(ftie->Snonce, peer->inonce, WPA_NONCE_LEN);
+ ftie->ie_len = sizeof(struct wpa_tdls_ftie) - 2;
+
+ pos = (u8 *) (ftie + 1);
+
+#ifdef CONFIG_TDLS_TESTING
+ if (tdls_testing & TDLS_TESTING_LONG_FRAME) {
+ wpa_printf(MSG_DEBUG, "TDLS: Testing - add extra subelem to "
+ "FTIE");
+ ftie->ie_len += 170;
+ *pos++ = 255; /* FTIE subelem */
+ *pos++ = 168; /* FTIE subelem length */
+ pos += 168;
+ }
+#endif /* CONFIG_TDLS_TESTING */
+
+ /* Lifetime */
+ lifetime = peer->lifetime;
+#ifdef CONFIG_TDLS_TESTING
+ if (tdls_testing & TDLS_TESTING_WRONG_LIFETIME_CONF) {
+ wpa_printf(MSG_DEBUG, "TDLS: Testing - use wrong TPK "
+ "lifetime in confirm");
+ lifetime++;
+ }
+#endif /* CONFIG_TDLS_TESTING */
+ pos = wpa_add_tdls_timeoutie(pos, (u8 *) &timeoutie,
+ sizeof(timeoutie), lifetime);
+ wpa_printf(MSG_DEBUG, "TDLS: TPK lifetime %u seconds",
+ lifetime);
+
+ /* compute MIC before sending */
+ wpa_tdls_ftie_mic(peer->tpk.kck, 3, (u8 *) lnkid, peer->rsnie_p,
+ (u8 *) &timeoutie, (u8 *) ftie, ftie->mic);
+
+skip_ies:
+ wpa_tdls_tpk_send(sm, src_addr, WLAN_TDLS_SETUP_CONFIRM, dtoken, 0,
+ rbuf, pos - rbuf);
+ os_free(rbuf);
+
+ return 0;
+}
+
+
+static int wpa_tdls_send_discovery_response(struct wpa_sm *sm,
+ struct wpa_tdls_peer *peer,
+ u8 dialog_token)
+{
+ wpa_printf(MSG_DEBUG, "TDLS: Sending TDLS Discovery Response "
+ "(peer " MACSTR ")", MAC2STR(peer->addr));
+
+ return wpa_tdls_tpk_send(sm, peer->addr, WLAN_TDLS_DISCOVERY_RESPONSE,
+ dialog_token, 0, NULL, 0);
+}
+
+
+static int
+wpa_tdls_process_discovery_request(struct wpa_sm *sm, const u8 *addr,
+ const u8 *buf, size_t len)
+{
+ struct wpa_eapol_ie_parse kde;
+ const struct wpa_tdls_lnkid *lnkid;
+ struct wpa_tdls_peer *peer;
+ size_t min_req_len = sizeof(struct wpa_tdls_frame) +
+ 1 /* dialog token */ + sizeof(struct wpa_tdls_lnkid);
+ u8 dialog_token;
+
+ wpa_printf(MSG_DEBUG, "TDLS: Discovery Request from " MACSTR,
+ MAC2STR(addr));
+
+ if (len < min_req_len) {
+ wpa_printf(MSG_DEBUG, "TDLS Discovery Request is too short: "
+ "%d", (int) len);
+ return -1;
+ }
+
+ dialog_token = buf[sizeof(struct wpa_tdls_frame)];
+
+ if (wpa_supplicant_parse_ies(buf + sizeof(struct wpa_tdls_frame) + 1,
+ len - (sizeof(struct wpa_tdls_frame) + 1),
+ &kde) < 0)
+ return -1;
+
+ if (!kde.lnkid) {
+ wpa_printf(MSG_DEBUG, "TDLS: Link ID not found in Discovery "
+ "Request");
+ return -1;
+ }
+
+ lnkid = (const struct wpa_tdls_lnkid *) kde.lnkid;
+
+ if (os_memcmp(sm->bssid, lnkid->bssid, ETH_ALEN) != 0) {
+ wpa_printf(MSG_DEBUG, "TDLS: Discovery Request from different "
+ " BSS " MACSTR, MAC2STR(lnkid->bssid));
+ return -1;
+ }
+
+ peer = wpa_tdls_add_peer(sm, addr);
+ if (peer == NULL)
+ return -1;
+
+ return wpa_tdls_send_discovery_response(sm, peer, dialog_token);
+}
+
+
+int wpa_tdls_send_discovery_request(struct wpa_sm *sm, const u8 *addr)
+{
+ if (sm->tdls_disabled || !sm->tdls_supported)
+ return -1;
+
+ wpa_printf(MSG_DEBUG, "TDLS: Sending Discovery Request to peer "
+ MACSTR, MAC2STR(addr));
+ return wpa_tdls_tpk_send(sm, addr, WLAN_TDLS_DISCOVERY_REQUEST,
+ 1, 0, NULL, 0);
+}
+
+
+static int copy_supp_rates(const struct wpa_eapol_ie_parse *kde,
+ struct wpa_tdls_peer *peer)
+{
+ if (!kde->supp_rates) {
+ wpa_printf(MSG_DEBUG, "TDLS: No supported rates received");
+ return -1;
+ }
+
+ peer->supp_rates_len = kde->supp_rates_len - 2;
+ if (peer->supp_rates_len > IEEE80211_MAX_SUPP_RATES)
+ peer->supp_rates_len = IEEE80211_MAX_SUPP_RATES;
+ os_memcpy(peer->supp_rates, kde->supp_rates + 2, peer->supp_rates_len);
+
+ if (kde->ext_supp_rates) {
+ int clen = kde->ext_supp_rates_len - 2;
+ if (peer->supp_rates_len + clen > IEEE80211_MAX_SUPP_RATES)
+ clen = IEEE80211_MAX_SUPP_RATES - peer->supp_rates_len;
+ os_memcpy(peer->supp_rates + peer->supp_rates_len,
+ kde->ext_supp_rates + 2, clen);
+ peer->supp_rates_len += clen;
+ }
+
+ return 0;
+}
+
+
+static int wpa_tdls_process_tpk_m1(struct wpa_sm *sm, const u8 *src_addr,
+ const u8 *buf, size_t len)
+{
+ struct wpa_tdls_peer *peer;
+ struct wpa_eapol_ie_parse kde;
+ struct wpa_ie_data ie;
+ int cipher;
+ const u8 *cpos;
+ struct wpa_tdls_ftie *ftie = NULL;
+ struct wpa_tdls_timeoutie *timeoutie;
+ struct wpa_tdls_lnkid *lnkid;
+ u32 lifetime = 0;
+#if 0
+ struct rsn_ie_hdr *hdr;
+ u8 *pos;
+ u16 rsn_capab;
+ u16 rsn_ver;
+#endif
+ u8 dtoken;
+ u16 ielen;
+ u16 status = WLAN_STATUS_UNSPECIFIED_FAILURE;
+ int tdls_prohibited = sm->tdls_prohibited;
+ int existing_peer = 0;
+
+ if (len < 3 + 3)
+ return -1;
+
+ cpos = buf;
+ cpos += 1 /* pkt_type */ + 1 /* Category */ + 1 /* Action */;
+
+ /* driver had already verified the frame format */
+ dtoken = *cpos++; /* dialog token */
+
+ wpa_printf(MSG_INFO, "TDLS: Dialog Token in TPK M1 %d", dtoken);
+
+ for (peer = sm->tdls; peer; peer = peer->next) {
+ if (os_memcmp(peer->addr, src_addr, ETH_ALEN) == 0) {
+ existing_peer = 1;
+ break;
+ }
+ }
+
+ if (peer == NULL) {
+ peer = wpa_tdls_add_peer(sm, src_addr);
+ if (peer == NULL)
+ goto error;
+ }
+
+ /* capability information */
+ peer->capability = WPA_GET_LE16(cpos);
+ cpos += 2;
+
+ ielen = len - (cpos - buf); /* start of IE in buf */
+ if (wpa_supplicant_parse_ies(cpos, ielen, &kde) < 0) {
+ wpa_printf(MSG_INFO, "TDLS: Failed to parse IEs in TPK M1");
+ goto error;
+ }
+
+ if (kde.lnkid == NULL || kde.lnkid_len < 3 * ETH_ALEN) {
+ wpa_printf(MSG_INFO, "TDLS: No valid Link Identifier IE in "
+ "TPK M1");
+ goto error;
+ }
+ wpa_hexdump(MSG_DEBUG, "TDLS: Link ID Received from TPK M1",
+ kde.lnkid, kde.lnkid_len);
+ lnkid = (struct wpa_tdls_lnkid *) kde.lnkid;
+ if (os_memcmp(sm->bssid, lnkid->bssid, ETH_ALEN) != 0) {
+ wpa_printf(MSG_INFO, "TDLS: TPK M1 from diff BSS");
+ status = WLAN_STATUS_NOT_IN_SAME_BSS;
+ goto error;
+ }
+
+ wpa_printf(MSG_DEBUG, "TDLS: TPK M1 - TPK initiator " MACSTR,
+ MAC2STR(src_addr));
+
+ if (copy_supp_rates(&kde, peer) < 0)
+ goto error;
+
+#ifdef CONFIG_TDLS_TESTING
+ if (tdls_testing & TDLS_TESTING_CONCURRENT_INIT) {
+ for (peer = sm->tdls; peer; peer = peer->next) {
+ if (os_memcmp(peer->addr, src_addr, ETH_ALEN) == 0)
+ break;
+ }
+ if (peer == NULL) {
+ peer = wpa_tdls_add_peer(sm, src_addr);
+ if (peer == NULL)
+ goto error;
+ }
+ wpa_printf(MSG_DEBUG, "TDLS: Testing concurrent initiation of "
+ "TDLS setup - send own request");
+ peer->initiator = 1;
+ wpa_tdls_send_tpk_m1(sm, peer);
+ }
+
+ if ((tdls_testing & TDLS_TESTING_IGNORE_AP_PROHIBIT) &&
+ tdls_prohibited) {
+ wpa_printf(MSG_DEBUG, "TDLS: Testing - ignore AP prohibition "
+ "on TDLS");
+ tdls_prohibited = 0;
+ }
+#endif /* CONFIG_TDLS_TESTING */
+
+ if (tdls_prohibited) {
+ wpa_printf(MSG_INFO, "TDLS: TDLS prohibited in this BSS");
+ status = WLAN_STATUS_REQUEST_DECLINED;
+ goto error;
+ }
+
+ if (!wpa_tdls_get_privacy(sm)) {
+ if (kde.rsn_ie) {
+ wpa_printf(MSG_INFO, "TDLS: RSN IE in TPK M1 while "
+ "security is disabled");
+ status = WLAN_STATUS_SECURITY_DISABLED;
+ goto error;
+ }
+ goto skip_rsn;
+ }
+
+ if (kde.ftie == NULL || kde.ftie_len < sizeof(*ftie) ||
+ kde.rsn_ie == NULL) {
+ wpa_printf(MSG_INFO, "TDLS: No FTIE or RSN IE in TPK M1");
+ status = WLAN_STATUS_INVALID_PARAMETERS;
+ goto error;
+ }
+
+ if (kde.rsn_ie_len > TDLS_MAX_IE_LEN) {
+ wpa_printf(MSG_INFO, "TDLS: Too long Initiator RSN IE in "
+ "TPK M1");
+ status = WLAN_STATUS_INVALID_RSNIE;
+ goto error;
+ }
+
+ if (wpa_parse_wpa_ie_rsn(kde.rsn_ie, kde.rsn_ie_len, &ie) < 0) {
+ wpa_printf(MSG_INFO, "TDLS: Failed to parse RSN IE in TPK M1");
+ status = WLAN_STATUS_INVALID_RSNIE;
+ goto error;
+ }
+
+ cipher = ie.pairwise_cipher;
+ if (cipher & WPA_CIPHER_CCMP) {
+ wpa_printf(MSG_DEBUG, "TDLS: Using CCMP for direct link");
+ cipher = WPA_CIPHER_CCMP;
+ } else {
+ wpa_printf(MSG_INFO, "TDLS: No acceptable cipher in TPK M1");
+ status = WLAN_STATUS_PAIRWISE_CIPHER_NOT_VALID;
+ goto error;
+ }
+
+ if ((ie.capabilities &
+ (WPA_CAPABILITY_NO_PAIRWISE | WPA_CAPABILITY_PEERKEY_ENABLED)) !=
+ WPA_CAPABILITY_PEERKEY_ENABLED) {
+ wpa_printf(MSG_INFO, "TDLS: Invalid RSN Capabilities in "
+ "TPK M1");
+ status = WLAN_STATUS_INVALID_RSN_IE_CAPAB;
+ goto error;
+ }
+
+ /* Lifetime */
+ if (kde.key_lifetime == NULL) {
+ wpa_printf(MSG_INFO, "TDLS: No Key Lifetime IE in TPK M1");
+ status = WLAN_STATUS_UNACCEPTABLE_LIFETIME;
+ goto error;
+ }
+ timeoutie = (struct wpa_tdls_timeoutie *) kde.key_lifetime;
+ lifetime = WPA_GET_LE32(timeoutie->value);
+ wpa_printf(MSG_DEBUG, "TDLS: TPK lifetime %u seconds", lifetime);
+ if (lifetime < 300) {
+ wpa_printf(MSG_INFO, "TDLS: Too short TPK lifetime");
+ status = WLAN_STATUS_UNACCEPTABLE_LIFETIME;
+ goto error;
+ }
+
+skip_rsn:
+ /* If found, use existing entry instead of adding a new one;
+ * how to handle the case where both ends initiate at the
+ * same time? */
+ if (existing_peer) {
+ if (peer->tpk_success) {
+ wpa_printf(MSG_DEBUG, "TDLS: TDLS Setup Request while "
+ "direct link is enabled - tear down the "
+ "old link first");
+#if 0
+ /* TODO: Disabling the link would be more proper
+ * operation here, but it seems to trigger a race with
+ * some drivers handling the new request frame. */
+ wpa_sm_tdls_oper(sm, TDLS_DISABLE_LINK, src_addr);
+#else
+ if (sm->tdls_external_setup)
+ wpa_sm_tdls_oper(sm, TDLS_DISABLE_LINK,
+ src_addr);
+ else
+ wpa_tdls_del_key(sm, peer);
+#endif
+ wpa_tdls_peer_free(sm, peer);
+ }
+
+ /*
+ * An entry is already present, so check if we already sent a
+ * TDLS Setup Request. If so, compare MAC addresses and let the
+ * STA with the lower MAC address continue as the initiator.
+ * The other negotiation is terminated.
+ */
+ if (peer->initiator) {
+ if (os_memcmp(sm->own_addr, src_addr, ETH_ALEN) < 0) {
+ wpa_printf(MSG_DEBUG, "TDLS: Discard request "
+ "from peer with higher address "
+ MACSTR, MAC2STR(src_addr));
+ return -1;
+ } else {
+ wpa_printf(MSG_DEBUG, "TDLS: Accept request "
+ "from peer with lower address "
+ MACSTR " (terminate previously "
+ "initiated negotiation",
+ MAC2STR(src_addr));
+ wpa_tdls_peer_free(sm, peer);
+ }
+ }
+ }
+
+#ifdef CONFIG_TDLS_TESTING
+ if (tdls_testing & TDLS_TESTING_CONCURRENT_INIT) {
+ if (os_memcmp(sm->own_addr, peer->addr, ETH_ALEN) < 0) {
+ /*
+ * The request frame from us is going to win, so do not
+ * replace information based on this request frame from
+ * the peer.
+ */
+ goto skip_rsn_check;
+ }
+ }
+#endif /* CONFIG_TDLS_TESTING */
+
+ peer->initiator = 0; /* Need to check */
+ peer->dtoken = dtoken;
+
+ if (!wpa_tdls_get_privacy(sm)) {
+ peer->rsnie_i_len = 0;
+ peer->rsnie_p_len = 0;
+ peer->cipher = WPA_CIPHER_NONE;
+ goto skip_rsn_check;
+ }
+
+ ftie = (struct wpa_tdls_ftie *) kde.ftie;
+ os_memcpy(peer->inonce, ftie->Snonce, WPA_NONCE_LEN);
+ os_memcpy(peer->rsnie_i, kde.rsn_ie, kde.rsn_ie_len);
+ peer->rsnie_i_len = kde.rsn_ie_len;
+ peer->cipher = cipher;
+
+ if (os_get_random(peer->rnonce, WPA_NONCE_LEN)) {
+ wpa_msg(sm->ctx->ctx, MSG_WARNING,
+ "TDLS: Failed to get random data for responder nonce");
+ wpa_tdls_peer_free(sm, peer);
+ goto error;
+ }
+
+#if 0
+ /* get version info from RSNIE received from Peer */
+ hdr = (struct rsn_ie_hdr *) kde.rsn_ie;
+ rsn_ver = WPA_GET_LE16(hdr->version);
+
+ /* use min(peer's version, out version) */
+ if (rsn_ver > RSN_VERSION)
+ rsn_ver = RSN_VERSION;
+
+ hdr = (struct rsn_ie_hdr *) peer->rsnie_p;
+
+ hdr->elem_id = WLAN_EID_RSN;
+ WPA_PUT_LE16(hdr->version, rsn_ver);
+ pos = (u8 *) (hdr + 1);
+
+ RSN_SELECTOR_PUT(pos, RSN_CIPHER_SUITE_NO_GROUP_ADDRESSED);
+ pos += RSN_SELECTOR_LEN;
+ /* Include only the selected cipher in pairwise cipher suite */
+ WPA_PUT_LE16(pos, 1);
+ pos += 2;
+ if (cipher == WPA_CIPHER_CCMP)
+ RSN_SELECTOR_PUT(pos, RSN_CIPHER_SUITE_CCMP);
+ pos += RSN_SELECTOR_LEN;
+
+ WPA_PUT_LE16(pos, 1);
+ pos += 2;
+ RSN_SELECTOR_PUT(pos, RSN_AUTH_KEY_MGMT_TPK_HANDSHAKE);
+ pos += RSN_SELECTOR_LEN;
+
+ rsn_capab = WPA_CAPABILITY_PEERKEY_ENABLED;
+ rsn_capab |= RSN_NUM_REPLAY_COUNTERS_16 << 2;
+ WPA_PUT_LE16(pos, rsn_capab);
+ pos += 2;
+
+ hdr->len = (pos - peer->rsnie_p) - 2;
+ peer->rsnie_p_len = pos - peer->rsnie_p;
+#endif
+
+ /* temp fix: validation of RSNIE later */
+ os_memcpy(peer->rsnie_p, peer->rsnie_i, peer->rsnie_i_len);
+ peer->rsnie_p_len = peer->rsnie_i_len;
+
+ wpa_hexdump(MSG_DEBUG, "TDLS: RSN IE for TPK handshake",
+ peer->rsnie_p, peer->rsnie_p_len);
+
+ peer->lifetime = lifetime;
+
+ wpa_tdls_generate_tpk(peer, sm->own_addr, sm->bssid);
+
+skip_rsn_check:
+ /* add the peer to the driver as a "setup in progress" peer */
+ wpa_sm_tdls_peer_addset(sm, peer->addr, 1, 0, NULL, 0);
+
+ wpa_printf(MSG_DEBUG, "TDLS: Sending TDLS Setup Response / TPK M2");
+ if (wpa_tdls_send_tpk_m2(sm, src_addr, dtoken, lnkid, peer) < 0) {
+ wpa_tdls_disable_link(sm, peer->addr);
+ goto error;
+ }
+
+ return 0;
+
+error:
+ wpa_tdls_send_error(sm, src_addr, WLAN_TDLS_SETUP_RESPONSE, dtoken,
+ status);
+ return -1;
+}
+
+
+static void wpa_tdls_enable_link(struct wpa_sm *sm, struct wpa_tdls_peer *peer)
+{
+ peer->tpk_success = 1;
+ eloop_cancel_timeout(wpa_tdls_tpk_timeout, sm, peer);
+ if (wpa_tdls_get_privacy(sm)) {
+ u32 lifetime = peer->lifetime;
+ /*
+ * Start the initiator process a bit earlier to avoid race
+ * condition with the responder sending teardown request.
+ */
+ if (lifetime > 3 && peer->initiator)
+ lifetime -= 3;
+ eloop_register_timeout(lifetime, 0, wpa_tdls_tpk_timeout,
+ sm, peer);
+#ifdef CONFIG_TDLS_TESTING
+ if (tdls_testing & TDLS_TESTING_NO_TPK_EXPIRATION) {
+ wpa_printf(MSG_DEBUG, "TDLS: Testing - disable TPK "
+ "expiration");
+ eloop_cancel_timeout(wpa_tdls_tpk_timeout, sm, peer);
+ }
+#endif /* CONFIG_TDLS_TESTING */
+ }
+
+ /* add supported rates and capabilities to the TDLS peer */
+ wpa_sm_tdls_peer_addset(sm, peer->addr, 0, peer->capability,
+ peer->supp_rates, peer->supp_rates_len);
+
+ wpa_sm_tdls_oper(sm, TDLS_ENABLE_LINK, peer->addr);
+}
+
+
+static int wpa_tdls_process_tpk_m2(struct wpa_sm *sm, const u8 *src_addr,
+ const u8 *buf, size_t len)
+{
+ struct wpa_tdls_peer *peer;
+ struct wpa_eapol_ie_parse kde;
+ struct wpa_ie_data ie;
+ int cipher;
+ struct wpa_tdls_ftie *ftie;
+ struct wpa_tdls_timeoutie *timeoutie;
+ struct wpa_tdls_lnkid *lnkid;
+ u32 lifetime;
+ u8 dtoken;
+ int ielen;
+ u16 status;
+ const u8 *pos;
+
+ wpa_printf(MSG_DEBUG, "TDLS: Received TDLS Setup Response / TPK M2 "
+ "(Peer " MACSTR ")", MAC2STR(src_addr));
+ for (peer = sm->tdls; peer; peer = peer->next) {
+ if (os_memcmp(peer->addr, src_addr, ETH_ALEN) == 0)
+ break;
+ }
+ if (peer == NULL) {
+ wpa_printf(MSG_INFO, "TDLS: No matching peer found for "
+ "TPK M2: " MACSTR, MAC2STR(src_addr));
+ return -1;
+ }
+ wpa_tdls_tpk_retry_timeout_cancel(sm, peer, WLAN_TDLS_SETUP_REQUEST);
+
+ if (len < 3 + 2 + 1)
+ return -1;
+ pos = buf;
+ pos += 1 /* pkt_type */ + 1 /* Category */ + 1 /* Action */;
+ status = WPA_GET_LE16(pos);
+ pos += 2 /* status code */;
+
+ if (status != WLAN_STATUS_SUCCESS) {
+ wpa_printf(MSG_INFO, "TDLS: Status code in TPK M2: %u",
+ status);
+ if (sm->tdls_external_setup)
+ wpa_sm_tdls_oper(sm, TDLS_DISABLE_LINK, src_addr);
+ return -1;
+ }
+
+ status = WLAN_STATUS_UNSPECIFIED_FAILURE;
+
+ /* TODO: need to verify dialog token matches here or in kernel */
+ dtoken = *pos++; /* dialog token */
+
+ wpa_printf(MSG_DEBUG, "TDLS: Dialog Token in TPK M2 %d", dtoken);
+
+ if (len < 3 + 2 + 1 + 2)
+ return -1;
+
+ /* capability information */
+ peer->capability = WPA_GET_LE16(pos);
+ pos += 2;
+
+ ielen = len - (pos - buf); /* start of IE in buf */
+ if (wpa_supplicant_parse_ies(pos, ielen, &kde) < 0) {
+ wpa_printf(MSG_INFO, "TDLS: Failed to parse IEs in TPK M2");
+ goto error;
+ }
+
+#ifdef CONFIG_TDLS_TESTING
+ if (tdls_testing & TDLS_TESTING_DECLINE_RESP) {
+ wpa_printf(MSG_DEBUG, "TDLS: Testing - decline response");
+ status = WLAN_STATUS_REQUEST_DECLINED;
+ goto error;
+ }
+#endif /* CONFIG_TDLS_TESTING */
+
+ if (kde.lnkid == NULL || kde.lnkid_len < 3 * ETH_ALEN) {
+ wpa_printf(MSG_INFO, "TDLS: No valid Link Identifier IE in "
+ "TPK M2");
+ goto error;
+ }
+ wpa_hexdump(MSG_DEBUG, "TDLS: Link ID Received from TPK M2",
+ kde.lnkid, kde.lnkid_len);
+ lnkid = (struct wpa_tdls_lnkid *) kde.lnkid;
+
+ if (os_memcmp(sm->bssid, lnkid->bssid, ETH_ALEN) != 0) {
+ wpa_printf(MSG_INFO, "TDLS: TPK M2 from different BSS");
+ status = WLAN_STATUS_NOT_IN_SAME_BSS;
+ goto error;
+ }
+
+ if (copy_supp_rates(&kde, peer) < 0)
+ goto error;
+
+ if (!wpa_tdls_get_privacy(sm)) {
+ peer->rsnie_p_len = 0;
+ peer->cipher = WPA_CIPHER_NONE;
+ goto skip_rsn;
+ }
+
+ if (kde.ftie == NULL || kde.ftie_len < sizeof(*ftie) ||
+ kde.rsn_ie == NULL) {
+ wpa_printf(MSG_INFO, "TDLS: No FTIE or RSN IE in TPK M2");
+ status = WLAN_STATUS_INVALID_PARAMETERS;
+ goto error;
+ }
+ wpa_hexdump(MSG_DEBUG, "TDLS: RSN IE Received from TPK M2",
+ kde.rsn_ie, kde.rsn_ie_len);
+
+ /*
+ * FIX: bitwise comparison of RSN IE is not the correct way of
+ * validation this. It can be different, but certain fields must
+ * match. Since we list only a single pairwise cipher in TPK M1, the
+ * memcmp is likely to work in most cases, though.
+ */
+ if (kde.rsn_ie_len != peer->rsnie_i_len ||
+ os_memcmp(peer->rsnie_i, kde.rsn_ie, peer->rsnie_i_len) != 0) {
+ wpa_printf(MSG_INFO, "TDLS: RSN IE in TPK M2 does "
+ "not match with RSN IE used in TPK M1");
+ wpa_hexdump(MSG_DEBUG, "TDLS: RSN IE Sent in TPK M1",
+ peer->rsnie_i, peer->rsnie_i_len);
+ wpa_hexdump(MSG_DEBUG, "TDLS: RSN IE Received from TPK M2",
+ kde.rsn_ie, kde.rsn_ie_len);
+ status = WLAN_STATUS_INVALID_RSNIE;
+ goto error;
+ }
+
+ if (wpa_parse_wpa_ie_rsn(kde.rsn_ie, kde.rsn_ie_len, &ie) < 0) {
+ wpa_printf(MSG_INFO, "TDLS: Failed to parse RSN IE in TPK M2");
+ status = WLAN_STATUS_INVALID_RSNIE;
+ goto error;
+ }
+
+ cipher = ie.pairwise_cipher;
+ if (cipher == WPA_CIPHER_CCMP) {
+ wpa_printf(MSG_DEBUG, "TDLS: Using CCMP for direct link");
+ cipher = WPA_CIPHER_CCMP;
+ } else {
+ wpa_printf(MSG_INFO, "TDLS: No acceptable cipher in TPK M2");
+ status = WLAN_STATUS_PAIRWISE_CIPHER_NOT_VALID;
+ goto error;
+ }
+
+ wpa_hexdump(MSG_DEBUG, "TDLS: FTIE Received from TPK M2",
+ kde.ftie, sizeof(*ftie));
+ ftie = (struct wpa_tdls_ftie *) kde.ftie;
+
+ if (!os_memcmp(peer->inonce, ftie->Snonce, WPA_NONCE_LEN) == 0) {
+ wpa_printf(MSG_INFO, "TDLS: FTIE SNonce in TPK M2 does "
+ "not match with FTIE SNonce used in TPK M1");
+ /* Silently discard the frame */
+ return -1;
+ }
+
+ /* Responder Nonce and RSN IE */
+ os_memcpy(peer->rnonce, ftie->Anonce, WPA_NONCE_LEN);
+ os_memcpy(peer->rsnie_p, kde.rsn_ie, kde.rsn_ie_len);
+ peer->rsnie_p_len = kde.rsn_ie_len;
+ peer->cipher = cipher;
+
+ /* Lifetime */
+ if (kde.key_lifetime == NULL) {
+ wpa_printf(MSG_INFO, "TDLS: No Key Lifetime IE in TPK M2");
+ status = WLAN_STATUS_UNACCEPTABLE_LIFETIME;
+ goto error;
+ }
+ timeoutie = (struct wpa_tdls_timeoutie *) kde.key_lifetime;
+ lifetime = WPA_GET_LE32(timeoutie->value);
+ wpa_printf(MSG_DEBUG, "TDLS: TPK lifetime %u seconds in TPK M2",
+ lifetime);
+ if (lifetime != peer->lifetime) {
+ wpa_printf(MSG_INFO, "TDLS: Unexpected TPK lifetime %u in "
+ "TPK M2 (expected %u)", lifetime, peer->lifetime);
+ status = WLAN_STATUS_UNACCEPTABLE_LIFETIME;
+ goto error;
+ }
+
+ wpa_tdls_generate_tpk(peer, sm->own_addr, sm->bssid);
+
+ /* Process MIC check to see if TPK M2 is right */
+ if (wpa_supplicant_verify_tdls_mic(2, peer, (u8 *) lnkid,
+ (u8 *) timeoutie, ftie) < 0) {
+ /* Discard the frame */
+ wpa_tdls_del_key(sm, peer);
+ wpa_tdls_peer_free(sm, peer);
+ if (sm->tdls_external_setup)
+ wpa_sm_tdls_oper(sm, TDLS_DISABLE_LINK, src_addr);
+ return -1;
+ }
+
+ wpa_tdls_set_key(sm, peer);
+
+skip_rsn:
+ peer->dtoken = dtoken;
+
+ wpa_printf(MSG_DEBUG, "TDLS: Sending TDLS Setup Confirm / "
+ "TPK Handshake Message 3");
+ wpa_tdls_send_tpk_m3(sm, src_addr, dtoken, lnkid, peer);
+
+ wpa_tdls_enable_link(sm, peer);
+
+ return 0;
+
+error:
+ wpa_tdls_send_error(sm, src_addr, WLAN_TDLS_SETUP_CONFIRM, dtoken,
+ status);
+ if (sm->tdls_external_setup)
+ wpa_sm_tdls_oper(sm, TDLS_DISABLE_LINK, src_addr);
+ return -1;
+}
+
+
+static int wpa_tdls_process_tpk_m3(struct wpa_sm *sm, const u8 *src_addr,
+ const u8 *buf, size_t len)
+{
+ struct wpa_tdls_peer *peer;
+ struct wpa_eapol_ie_parse kde;
+ struct wpa_tdls_ftie *ftie;
+ struct wpa_tdls_timeoutie *timeoutie;
+ struct wpa_tdls_lnkid *lnkid;
+ int ielen;
+ u16 status;
+ const u8 *pos;
+ u32 lifetime;
+
+ wpa_printf(MSG_DEBUG, "TDLS: Received TDLS Setup Confirm / TPK M3 "
+ "(Peer " MACSTR ")", MAC2STR(src_addr));
+ for (peer = sm->tdls; peer; peer = peer->next) {
+ if (os_memcmp(peer->addr, src_addr, ETH_ALEN) == 0)
+ break;
+ }
+ if (peer == NULL) {
+ wpa_printf(MSG_INFO, "TDLS: No matching peer found for "
+ "TPK M3: " MACSTR, MAC2STR(src_addr));
+ return -1;
+ }
+ wpa_tdls_tpk_retry_timeout_cancel(sm, peer, WLAN_TDLS_SETUP_RESPONSE);
+
+ if (len < 3 + 3)
+ return -1;
+ pos = buf;
+ pos += 1 /* pkt_type */ + 1 /* Category */ + 1 /* Action */;
+
+ status = WPA_GET_LE16(pos);
+
+ if (status != 0) {
+ wpa_printf(MSG_INFO, "TDLS: Status code in TPK M3: %u",
+ status);
+ if (sm->tdls_external_setup)
+ wpa_sm_tdls_oper(sm, TDLS_DISABLE_LINK, src_addr);
+ return -1;
+ }
+ pos += 2 /* status code */ + 1 /* dialog token */;
+
+ ielen = len - (pos - buf); /* start of IE in buf */
+ if (wpa_supplicant_parse_ies((const u8 *) pos, ielen, &kde) < 0) {
+ wpa_printf(MSG_INFO, "TDLS: Failed to parse KDEs in TPK M3");
+ return -1;
+ }
+
+ if (kde.lnkid == NULL || kde.lnkid_len < 3 * ETH_ALEN) {
+ wpa_printf(MSG_INFO, "TDLS: No Link Identifier IE in TPK M3");
+ return -1;
+ }
+ wpa_hexdump(MSG_DEBUG, "TDLS: Link ID Received from TPK M3",
+ (u8 *) kde.lnkid, kde.lnkid_len);
+ lnkid = (struct wpa_tdls_lnkid *) kde.lnkid;
+
+ if (os_memcmp(sm->bssid, lnkid->bssid, ETH_ALEN) != 0) {
+ wpa_printf(MSG_INFO, "TDLS: TPK M3 from diff BSS");
+ return -1;
+ }
+
+ if (!wpa_tdls_get_privacy(sm))
+ goto skip_rsn;
+
+ if (kde.ftie == NULL || kde.ftie_len < sizeof(*ftie)) {
+ wpa_printf(MSG_INFO, "TDLS: No FTIE in TPK M3");
+ return -1;
+ }
+ wpa_hexdump(MSG_DEBUG, "TDLS: FTIE Received from TPK M3",
+ kde.ftie, sizeof(*ftie));
+ ftie = (struct wpa_tdls_ftie *) kde.ftie;
+
+ if (kde.rsn_ie == NULL) {
+ wpa_printf(MSG_INFO, "TDLS: No RSN IE in TPK M3");
+ return -1;
+ }
+ wpa_hexdump(MSG_DEBUG, "TDLS: RSN IE Received from TPK M3",
+ kde.rsn_ie, kde.rsn_ie_len);
+ if (kde.rsn_ie_len != peer->rsnie_p_len ||
+ os_memcmp(kde.rsn_ie, peer->rsnie_p, peer->rsnie_p_len) != 0) {
+ wpa_printf(MSG_INFO, "TDLS: RSN IE in TPK M3 does not match "
+ "with the one sent in TPK M2");
+ return -1;
+ }
+
+ if (!os_memcmp(peer->rnonce, ftie->Anonce, WPA_NONCE_LEN) == 0) {
+ wpa_printf(MSG_INFO, "TDLS: FTIE ANonce in TPK M3 does "
+ "not match with FTIE ANonce used in TPK M2");
+ return -1;
+ }
+
+ if (!os_memcmp(peer->inonce, ftie->Snonce, WPA_NONCE_LEN) == 0) {
+ wpa_printf(MSG_INFO, "TDLS: FTIE SNonce in TPK M3 does not "
+ "match with FTIE SNonce used in TPK M1");
+ return -1;
+ }
+
+ if (kde.key_lifetime == NULL) {
+ wpa_printf(MSG_INFO, "TDLS: No Key Lifetime IE in TPK M3");
+ return -1;
+ }
+ timeoutie = (struct wpa_tdls_timeoutie *) kde.key_lifetime;
+ wpa_hexdump(MSG_DEBUG, "TDLS: Timeout IE Received from TPK M3",
+ (u8 *) timeoutie, sizeof(*timeoutie));
+ lifetime = WPA_GET_LE32(timeoutie->value);
+ wpa_printf(MSG_DEBUG, "TDLS: TPK lifetime %u seconds in TPK M3",
+ lifetime);
+ if (lifetime != peer->lifetime) {
+ wpa_printf(MSG_INFO, "TDLS: Unexpected TPK lifetime %u in "
+ "TPK M3 (expected %u)", lifetime, peer->lifetime);
+ if (sm->tdls_external_setup)
+ wpa_sm_tdls_oper(sm, TDLS_DISABLE_LINK, src_addr);
+ return -1;
+ }
+
+ if (wpa_supplicant_verify_tdls_mic(3, peer, (u8 *) lnkid,
+ (u8 *) timeoutie, ftie) < 0) {
+ wpa_tdls_del_key(sm, peer);
+ wpa_tdls_peer_free(sm, peer);
+ return -1;
+ }
+
+ if (wpa_tdls_set_key(sm, peer) < 0)
+ return -1;
+
+skip_rsn:
+ wpa_tdls_enable_link(sm, peer);
+
+ return 0;
+}
+
+
+static u8 * wpa_add_tdls_timeoutie(u8 *pos, u8 *ie, size_t ie_len, u32 tsecs)
+{
+ struct wpa_tdls_timeoutie *lifetime = (struct wpa_tdls_timeoutie *) ie;
+
+ os_memset(lifetime, 0, ie_len);
+ lifetime->ie_type = WLAN_EID_TIMEOUT_INTERVAL;
+ lifetime->ie_len = sizeof(struct wpa_tdls_timeoutie) - 2;
+ lifetime->interval_type = WLAN_TIMEOUT_KEY_LIFETIME;
+ WPA_PUT_LE32(lifetime->value, tsecs);
+ os_memcpy(pos, ie, ie_len);
+ return pos + ie_len;
+}
+
+
+/**
+ * wpa_tdls_start - Initiate TDLS handshake (send TPK Handshake Message 1)
+ * @sm: Pointer to WPA state machine data from wpa_sm_init()
+ * @peer: MAC address of the peer STA
+ * Returns: 0 on success, or -1 on failure
+ *
+ * Send TPK Handshake Message 1 info to driver to start TDLS
+ * handshake with the peer.
+ */
+int wpa_tdls_start(struct wpa_sm *sm, const u8 *addr)
+{
+ struct wpa_tdls_peer *peer;
+ int tdls_prohibited = sm->tdls_prohibited;
+
+ if (sm->tdls_disabled || !sm->tdls_supported)
+ return -1;
+
+#ifdef CONFIG_TDLS_TESTING
+ if ((tdls_testing & TDLS_TESTING_IGNORE_AP_PROHIBIT) &&
+ tdls_prohibited) {
+ wpa_printf(MSG_DEBUG, "TDLS: Testing - ignore AP prohibition "
+ "on TDLS");
+ tdls_prohibited = 0;
+ }
+#endif /* CONFIG_TDLS_TESTING */
+
+ if (tdls_prohibited) {
+ wpa_printf(MSG_DEBUG, "TDLS: TDLS is prohibited in this BSS - "
+ "reject request to start setup");
+ return -1;
+ }
+
+ /* Find existing entry and if found, use that instead of adding
+ * a new one */
+ for (peer = sm->tdls; peer; peer = peer->next) {
+ if (os_memcmp(peer->addr, addr, ETH_ALEN) == 0)
+ break;
+ }
+
+ if (peer == NULL) {
+ peer = wpa_tdls_add_peer(sm, addr);
+ if (peer == NULL)
+ return -1;
+ }
+
+ peer->initiator = 1;
+
+ /* add the peer to the driver as a "setup in progress" peer */
+ wpa_sm_tdls_peer_addset(sm, peer->addr, 1, 0, NULL, 0);
+
+ if (wpa_tdls_send_tpk_m1(sm, peer) < 0) {
+ wpa_tdls_disable_link(sm, peer->addr);
+ return -1;
+ }
+
+ return 0;
+}
+
+
+int wpa_tdls_reneg(struct wpa_sm *sm, const u8 *addr)
+{
+ struct wpa_tdls_peer *peer;
+
+ if (sm->tdls_disabled || !sm->tdls_supported)
+ return -1;
+
+ for (peer = sm->tdls; peer; peer = peer->next) {
+ if (os_memcmp(peer->addr, addr, ETH_ALEN) == 0)
+ break;
+ }
+
+ if (peer == NULL || !peer->tpk_success)
+ return -1;
+
+ if (sm->tdls_external_setup) {
+ /*
+ * Disable previous link to allow renegotiation to be completed
+ * on AP path.
+ */
+ wpa_sm_tdls_oper(sm, TDLS_DISABLE_LINK, peer->addr);
+ }
+
+ return wpa_tdls_start(sm, addr);
+}
+
+
+/**
+ * wpa_supplicant_rx_tdls - Receive TDLS data frame
+ *
+ * This function is called to receive TDLS (ethertype = 0x890d) data frames.
+ */
+static void wpa_supplicant_rx_tdls(void *ctx, const u8 *src_addr,
+ const u8 *buf, size_t len)
+{
+ struct wpa_sm *sm = ctx;
+ struct wpa_tdls_frame *tf;
+
+ wpa_hexdump(MSG_DEBUG, "TDLS: Received Data frame encapsulation",
+ buf, len);
+
+ if (sm->tdls_disabled || !sm->tdls_supported) {
+ wpa_printf(MSG_DEBUG, "TDLS: Discard message - TDLS disabled "
+ "or unsupported by driver");
+ return;
+ }
+
+ if (os_memcmp(src_addr, sm->own_addr, ETH_ALEN) == 0) {
+ wpa_printf(MSG_DEBUG, "TDLS: Discard copy of own message");
+ return;
+ }
+
+ if (len < sizeof(*tf)) {
+ wpa_printf(MSG_INFO, "TDLS: Drop too short frame");
+ return;
+ }
+
+ /* Check to make sure its a valid encapsulated TDLS frame */
+ tf = (struct wpa_tdls_frame *) buf;
+ if (tf->payloadtype != 2 /* TDLS_RFTYPE */ ||
+ tf->category != WLAN_ACTION_TDLS) {
+ wpa_printf(MSG_INFO, "TDLS: Invalid frame - payloadtype=%u "
+ "category=%u action=%u",
+ tf->payloadtype, tf->category, tf->action);
+ return;
+ }
+
+ switch (tf->action) {
+ case WLAN_TDLS_SETUP_REQUEST:
+ wpa_tdls_process_tpk_m1(sm, src_addr, buf, len);
+ break;
+ case WLAN_TDLS_SETUP_RESPONSE:
+ wpa_tdls_process_tpk_m2(sm, src_addr, buf, len);
+ break;
+ case WLAN_TDLS_SETUP_CONFIRM:
+ wpa_tdls_process_tpk_m3(sm, src_addr, buf, len);
+ break;
+ case WLAN_TDLS_TEARDOWN:
+ wpa_tdls_recv_teardown(sm, src_addr, buf, len);
+ break;
+ case WLAN_TDLS_DISCOVERY_REQUEST:
+ wpa_tdls_process_discovery_request(sm, src_addr, buf, len);
+ break;
+ default:
+ /* Kernel code will process remaining frames */
+ wpa_printf(MSG_DEBUG, "TDLS: Ignore TDLS frame action code %u",
+ tf->action);
+ break;
+ }
+}
+
+
+/**
+ * wpa_tdls_init - Initialize driver interface parameters for TDLS
+ * @wpa_s: Pointer to wpa_supplicant data
+ * Returns: 0 on success, -1 on failure
+ *
+ * This function is called to initialize driver interface parameters for TDLS.
+ * wpa_drv_init() must have been called before this function to initialize the
+ * driver interface.
+ */
+int wpa_tdls_init(struct wpa_sm *sm)
+{
+ if (sm == NULL)
+ return -1;
+
+ sm->l2_tdls = l2_packet_init(sm->bridge_ifname ? sm->bridge_ifname :
+ sm->ifname,
+ sm->own_addr,
+ ETH_P_80211_ENCAP, wpa_supplicant_rx_tdls,
+ sm, 0);
+ if (sm->l2_tdls == NULL) {
+ wpa_printf(MSG_ERROR, "TDLS: Failed to open l2_packet "
+ "connection");
+ return -1;
+ }
+
+ /*
+ * Drivers that support TDLS but don't implement the get_capa callback
+ * are assumed to perform everything internally
+ */
+ if (wpa_sm_tdls_get_capa(sm, &sm->tdls_supported,
+ &sm->tdls_external_setup) < 0) {
+ sm->tdls_supported = 1;
+ sm->tdls_external_setup = 0;
+ }
+
+ wpa_printf(MSG_DEBUG, "TDLS: TDLS operation%s supported by "
+ "driver", sm->tdls_supported ? "" : " not");
+ wpa_printf(MSG_DEBUG, "TDLS: Driver uses %s link setup",
+ sm->tdls_external_setup ? "external" : "internal");
+
+ return 0;
+}
+
+
+static void wpa_tdls_remove_peers(struct wpa_sm *sm)
+{
+ struct wpa_tdls_peer *peer, *tmp;
+
+ peer = sm->tdls;
+ sm->tdls = NULL;
+
+ while (peer) {
+ int res;
+ tmp = peer->next;
+ res = wpa_sm_tdls_oper(sm, TDLS_DISABLE_LINK, peer->addr);
+ wpa_printf(MSG_DEBUG, "TDLS: Remove peer " MACSTR " (res=%d)",
+ MAC2STR(peer->addr), res);
+ wpa_tdls_peer_free(sm, peer);
+ os_free(peer);
+ peer = tmp;
+ }
+}
+
+
+/**
+ * wpa_tdls_deinit - Deinitialize driver interface parameters for TDLS
+ *
+ * This function is called to recover driver interface parameters for TDLS
+ * and frees resources allocated for it.
+ */
+void wpa_tdls_deinit(struct wpa_sm *sm)
+{
+ if (sm == NULL)
+ return;
+
+ if (sm->l2_tdls)
+ l2_packet_deinit(sm->l2_tdls);
+ sm->l2_tdls = NULL;
+
+ wpa_tdls_remove_peers(sm);
+}
+
+
+void wpa_tdls_assoc(struct wpa_sm *sm)
+{
+ wpa_printf(MSG_DEBUG, "TDLS: Remove peers on association");
+ wpa_tdls_remove_peers(sm);
+}
+
+
+void wpa_tdls_disassoc(struct wpa_sm *sm)
+{
+ wpa_printf(MSG_DEBUG, "TDLS: Remove peers on disassociation");
+ wpa_tdls_remove_peers(sm);
+}
+
+
+static int wpa_tdls_prohibited(const u8 *ies, size_t len)
+{
+ struct wpa_eapol_ie_parse elems;
+
+ if (ies == NULL)
+ return 0;
+
+ if (wpa_supplicant_parse_ies(ies, len, &elems) < 0)
+ return 0;
+
+ if (elems.ext_capab == NULL || elems.ext_capab_len < 2 + 5)
+ return 0;
+
+ /* bit 38 - TDLS Prohibited */
+ return !!(elems.ext_capab[2 + 4] & 0x40);
+}
+
+
+void wpa_tdls_ap_ies(struct wpa_sm *sm, const u8 *ies, size_t len)
+{
+ sm->tdls_prohibited = wpa_tdls_prohibited(ies, len);
+ wpa_printf(MSG_DEBUG, "TDLS: TDLS is %s in the target BSS",
+ sm->tdls_prohibited ? "prohibited" : "allowed");
+}
+
+
+void wpa_tdls_assoc_resp_ies(struct wpa_sm *sm, const u8 *ies, size_t len)
+{
+ if (!sm->tdls_prohibited && wpa_tdls_prohibited(ies, len)) {
+ wpa_printf(MSG_DEBUG, "TDLS: TDLS prohibited based on "
+ "(Re)Association Response IEs");
+ sm->tdls_prohibited = 1;
+ }
+}
+
+
+void wpa_tdls_enable(struct wpa_sm *sm, int enabled)
+{
+ wpa_printf(MSG_DEBUG, "TDLS: %s", enabled ? "enabled" : "disabled");
+ sm->tdls_disabled = !enabled;
+}
+
+
+int wpa_tdls_is_external_setup(struct wpa_sm *sm)
+{
+ return sm->tdls_external_setup;
+}
diff --git a/contrib/wpa/src/rsn_supp/wpa.c b/contrib/wpa/src/rsn_supp/wpa.c
index 9439f97..e50404c 100644
--- a/contrib/wpa/src/rsn_supp/wpa.c
+++ b/contrib/wpa/src/rsn_supp/wpa.c
@@ -1,15 +1,9 @@
/*
* WPA Supplicant - WPA state machine and EAPOL-Key processing
- * Copyright (c) 2003-2010, Jouni Malinen <j@w1.fi>
+ * Copyright (c) 2003-2012, Jouni Malinen <j@w1.fi>
*
- * This program is free software; you can redistribute it and/or modify
- * it under the terms of the GNU General Public License version 2 as
- * published by the Free Software Foundation.
- *
- * Alternatively, this software may be distributed under the terms of BSD
- * license.
- *
- * See README and COPYING for more details.
+ * This software may be distributed under the terms of the BSD license.
+ * See README for more details.
*/
#include "includes.h"
@@ -17,6 +11,7 @@
#include "common.h"
#include "crypto/aes_wrap.h"
#include "crypto/crypto.h"
+#include "crypto/random.h"
#include "common/ieee802_11_defs.h"
#include "eapol_supp/eapol_supp_sm.h"
#include "wpa.h"
@@ -49,21 +44,26 @@ void wpa_eapol_key_send(struct wpa_sm *sm, const u8 *kck,
* BSSID from the driver.
*/
if (wpa_sm_get_bssid(sm, sm->bssid) < 0) {
- wpa_printf(MSG_DEBUG, "WPA: Failed to read BSSID for "
- "EAPOL-Key destination address");
+ wpa_dbg(sm->ctx->msg_ctx, MSG_DEBUG,
+ "WPA: Failed to read BSSID for "
+ "EAPOL-Key destination address");
} else {
dest = sm->bssid;
- wpa_printf(MSG_DEBUG, "WPA: Use BSSID (" MACSTR
- ") as the destination for EAPOL-Key",
- MAC2STR(dest));
+ wpa_dbg(sm->ctx->msg_ctx, MSG_DEBUG,
+ "WPA: Use BSSID (" MACSTR
+ ") as the destination for EAPOL-Key",
+ MAC2STR(dest));
}
}
if (key_mic &&
wpa_eapol_key_mic(kck, ver, msg, msg_len, key_mic)) {
- wpa_printf(MSG_ERROR, "WPA: Failed to generate EAPOL-Key "
- "version %d MIC", ver);
+ wpa_msg(sm->ctx->msg_ctx, MSG_ERROR,
+ "WPA: Failed to generate EAPOL-Key "
+ "version %d MIC", ver);
goto out;
}
+ wpa_hexdump_key(MSG_DEBUG, "WPA: KCK", kck, 16);
+ wpa_hexdump(MSG_DEBUG, "WPA: Derived Key MIC", key_mic, 16);
wpa_hexdump(MSG_MSGDUMP, "WPA: TX EAPOL-Key", msg, msg_len);
wpa_sm_ether_send(sm, dest, proto, msg, msg_len);
eapol_sm_notify_tx_eapol_key(sm->eapol);
@@ -91,14 +91,14 @@ void wpa_sm_key_request(struct wpa_sm *sm, int error, int pairwise)
if (wpa_key_mgmt_ft(sm->key_mgmt) || wpa_key_mgmt_sha256(sm->key_mgmt))
ver = WPA_KEY_INFO_TYPE_AES_128_CMAC;
- else if (sm->pairwise_cipher == WPA_CIPHER_CCMP)
+ else if (sm->pairwise_cipher != WPA_CIPHER_TKIP)
ver = WPA_KEY_INFO_TYPE_HMAC_SHA1_AES;
else
ver = WPA_KEY_INFO_TYPE_HMAC_MD5_RC4;
if (wpa_sm_get_bssid(sm, bssid) < 0) {
- wpa_printf(MSG_WARNING, "Failed to read BSSID for EAPOL-Key "
- "request");
+ wpa_msg(sm->ctx->msg_ctx, MSG_WARNING,
+ "Failed to read BSSID for EAPOL-Key request");
return;
}
@@ -124,9 +124,10 @@ void wpa_sm_key_request(struct wpa_sm *sm, int error, int pairwise)
WPA_PUT_BE16(reply->key_data_length, 0);
- wpa_printf(MSG_INFO, "WPA: Sending EAPOL-Key Request (error=%d "
- "pairwise=%d ptk_set=%d len=%lu)",
- error, pairwise, sm->ptk_set, (unsigned long) rlen);
+ wpa_msg(sm->ctx->msg_ctx, MSG_INFO,
+ "WPA: Sending EAPOL-Key Request (error=%d "
+ "pairwise=%d ptk_set=%d len=%lu)",
+ error, pairwise, sm->ptk_set, (unsigned long) rlen);
wpa_eapol_key_send(sm, sm->ptk.kck, ver, bssid, ETH_P_EAPOL,
rbuf, rlen, key_info & WPA_KEY_INFO_MIC ?
reply->key_mic : NULL);
@@ -144,12 +145,14 @@ static int wpa_supplicant_get_pmk(struct wpa_sm *sm,
* not have enough time to get the association information
* event before receiving this 1/4 message, so try to find a
* matching PMKSA cache entry here. */
- sm->cur_pmksa = pmksa_cache_get(sm->pmksa, src_addr, pmkid);
+ sm->cur_pmksa = pmksa_cache_get(sm->pmksa, src_addr, pmkid,
+ NULL);
if (sm->cur_pmksa) {
- wpa_printf(MSG_DEBUG, "RSN: found matching PMKID from "
- "PMKSA cache");
+ wpa_dbg(sm->ctx->msg_ctx, MSG_DEBUG,
+ "RSN: found matching PMKID from PMKSA cache");
} else {
- wpa_printf(MSG_DEBUG, "RSN: no matching PMKID found");
+ wpa_dbg(sm->ctx->msg_ctx, MSG_DEBUG,
+ "RSN: no matching PMKID found");
abort_cached = 1;
}
}
@@ -187,29 +190,38 @@ static int wpa_supplicant_get_pmk(struct wpa_sm *sm,
#endif /* CONFIG_IEEE80211R */
}
if (res == 0) {
+ struct rsn_pmksa_cache_entry *sa = NULL;
wpa_hexdump_key(MSG_DEBUG, "WPA: PMK from EAPOL state "
"machines", sm->pmk, pmk_len);
sm->pmk_len = pmk_len;
- if (sm->proto == WPA_PROTO_RSN) {
- pmksa_cache_add(sm->pmksa, sm->pmk, pmk_len,
- src_addr, sm->own_addr,
- sm->network_ctx, sm->key_mgmt);
+ if (sm->proto == WPA_PROTO_RSN &&
+ !wpa_key_mgmt_ft(sm->key_mgmt)) {
+ sa = pmksa_cache_add(sm->pmksa,
+ sm->pmk, pmk_len,
+ src_addr, sm->own_addr,
+ sm->network_ctx,
+ sm->key_mgmt);
}
if (!sm->cur_pmksa && pmkid &&
- pmksa_cache_get(sm->pmksa, src_addr, pmkid)) {
- wpa_printf(MSG_DEBUG, "RSN: the new PMK "
- "matches with the PMKID");
+ pmksa_cache_get(sm->pmksa, src_addr, pmkid, NULL))
+ {
+ wpa_dbg(sm->ctx->msg_ctx, MSG_DEBUG,
+ "RSN: the new PMK matches with the "
+ "PMKID");
abort_cached = 0;
}
+
+ if (!sm->cur_pmksa)
+ sm->cur_pmksa = sa;
} else {
wpa_msg(sm->ctx->msg_ctx, MSG_WARNING,
"WPA: Failed to get master session key from "
- "EAPOL state machines");
- wpa_msg(sm->ctx->msg_ctx, MSG_WARNING,
- "WPA: Key handshake aborted");
+ "EAPOL state machines - key handshake "
+ "aborted");
if (sm->cur_pmksa) {
- wpa_printf(MSG_DEBUG, "RSN: Cancelled PMKSA "
- "caching attempt");
+ wpa_dbg(sm->ctx->msg_ctx, MSG_DEBUG,
+ "RSN: Cancelled PMKSA caching "
+ "attempt");
sm->cur_pmksa = NULL;
abort_cached = 1;
} else if (!abort_cached) {
@@ -218,13 +230,15 @@ static int wpa_supplicant_get_pmk(struct wpa_sm *sm,
}
}
- if (abort_cached && wpa_key_mgmt_wpa_ieee8021x(sm->key_mgmt)) {
+ if (abort_cached && wpa_key_mgmt_wpa_ieee8021x(sm->key_mgmt) &&
+ !wpa_key_mgmt_ft(sm->key_mgmt)) {
/* Send EAPOL-Start to trigger full EAP authentication. */
u8 *buf;
size_t buflen;
- wpa_printf(MSG_DEBUG, "RSN: no PMKSA entry found - trigger "
- "full EAP authentication");
+ wpa_dbg(sm->ctx->msg_ctx, MSG_DEBUG,
+ "RSN: no PMKSA entry found - trigger "
+ "full EAP authentication");
buf = wpa_sm_alloc_eapol(sm, IEEE802_1X_TYPE_EAPOL_START,
NULL, 0, &buflen, NULL);
if (buf) {
@@ -265,8 +279,8 @@ int wpa_supplicant_send_2_of_4(struct wpa_sm *sm, const unsigned char *dst,
u8 *rsn_ie_buf = NULL;
if (wpa_ie == NULL) {
- wpa_printf(MSG_WARNING, "WPA: No wpa_ie set - cannot "
- "generate msg 2/4");
+ wpa_msg(sm->ctx->msg_ctx, MSG_WARNING, "WPA: No wpa_ie set - "
+ "cannot generate msg 2/4");
return -1;
}
@@ -321,6 +335,8 @@ int wpa_supplicant_send_2_of_4(struct wpa_sm *sm, const unsigned char *dst,
os_memcpy(reply->key_length, key->key_length, 2);
os_memcpy(reply->replay_counter, key->replay_counter,
WPA_REPLAY_COUNTER_LEN);
+ wpa_hexdump(MSG_DEBUG, "WPA: Replay Counter", reply->replay_counter,
+ WPA_REPLAY_COUNTER_LEN);
WPA_PUT_BE16(reply->key_data_length, wpa_ie_len);
os_memcpy(reply + 1, wpa_ie, wpa_ie_len);
@@ -328,7 +344,7 @@ int wpa_supplicant_send_2_of_4(struct wpa_sm *sm, const unsigned char *dst,
os_memcpy(reply->key_nonce, nonce, WPA_NONCE_LEN);
- wpa_printf(MSG_DEBUG, "WPA: Sending EAPOL-Key 2/4");
+ wpa_dbg(sm->ctx->msg_ctx, MSG_DEBUG, "WPA: Sending EAPOL-Key 2/4");
wpa_eapol_key_send(sm, ptk->kck, ver, dst, ETH_P_EAPOL,
rbuf, rlen, reply->key_mic);
@@ -340,7 +356,7 @@ static int wpa_derive_ptk(struct wpa_sm *sm, const unsigned char *src_addr,
const struct wpa_eapol_key *key,
struct wpa_ptk *ptk)
{
- size_t ptk_len = sm->pairwise_cipher == WPA_CIPHER_CCMP ? 48 : 64;
+ size_t ptk_len = sm->pairwise_cipher != WPA_CIPHER_TKIP ? 48 : 64;
#ifdef CONFIG_IEEE80211R
if (wpa_key_mgmt_ft(sm->key_mgmt))
return wpa_derive_ptk_ft(sm, src_addr, key, ptk, ptk_len);
@@ -365,14 +381,14 @@ static void wpa_supplicant_process_1_of_4(struct wpa_sm *sm,
int res;
if (wpa_sm_get_network_ctx(sm) == NULL) {
- wpa_printf(MSG_WARNING, "WPA: No SSID info found (msg 1 of "
- "4).");
+ wpa_msg(sm->ctx->msg_ctx, MSG_WARNING, "WPA: No SSID info "
+ "found (msg 1 of 4)");
return;
}
wpa_sm_set_state(sm, WPA_4WAY_HANDSHAKE);
- wpa_printf(MSG_DEBUG, "WPA: RX message 1 of 4-Way Handshake from "
- MACSTR " (ver=%d)", MAC2STR(src_addr), ver);
+ wpa_dbg(sm->ctx->msg_ctx, MSG_DEBUG, "WPA: RX message 1 of 4-Way "
+ "Handshake from " MACSTR " (ver=%d)", MAC2STR(src_addr), ver);
os_memset(&ie, 0, sizeof(ie));
@@ -382,7 +398,8 @@ static void wpa_supplicant_process_1_of_4(struct wpa_sm *sm,
const u8 *_buf = (const u8 *) (key + 1);
size_t len = WPA_GET_BE16(key->key_data_length);
wpa_hexdump(MSG_DEBUG, "RSN: msg 1/4 key data", _buf, len);
- wpa_supplicant_parse_ies(_buf, len, &ie);
+ if (wpa_supplicant_parse_ies(_buf, len, &ie) < 0)
+ goto failed;
if (ie.pmkid) {
wpa_hexdump(MSG_DEBUG, "RSN: PMKID from "
"Authenticator", ie.pmkid, PMKID_LEN);
@@ -392,15 +409,15 @@ static void wpa_supplicant_process_1_of_4(struct wpa_sm *sm,
res = wpa_supplicant_get_pmk(sm, src_addr, ie.pmkid);
if (res == -2) {
- wpa_printf(MSG_DEBUG, "RSN: Do not reply to msg 1/4 - "
- "requesting full EAP authentication");
+ wpa_dbg(sm->ctx->msg_ctx, MSG_DEBUG, "RSN: Do not reply to "
+ "msg 1/4 - requesting full EAP authentication");
return;
}
if (res)
goto failed;
if (sm->renew_snonce) {
- if (os_get_random(sm->snonce, WPA_NONCE_LEN)) {
+ if (random_get_bytes(sm->snonce, WPA_NONCE_LEN)) {
wpa_msg(sm->ctx->msg_ctx, MSG_WARNING,
"WPA: Failed to get random data for SNonce");
goto failed;
@@ -462,15 +479,16 @@ static void wpa_supplicant_key_neg_complete(struct wpa_sm *sm,
* Start preauthentication after a short wait to avoid a
* possible race condition between the data receive and key
* configuration after the 4-Way Handshake. This increases the
- * likelyhood of the first preauth EAPOL-Start frame getting to
+ * likelihood of the first preauth EAPOL-Start frame getting to
* the target AP.
*/
eloop_register_timeout(1, 0, wpa_sm_start_preauth, sm, NULL);
}
if (sm->cur_pmksa && sm->cur_pmksa->opportunistic) {
- wpa_printf(MSG_DEBUG, "RSN: Authenticator accepted "
- "opportunistic PMKSA entry - marking it valid");
+ wpa_dbg(sm->ctx->msg_ctx, MSG_DEBUG,
+ "RSN: Authenticator accepted "
+ "opportunistic PMKSA entry - marking it valid");
sm->cur_pmksa->opportunistic = 0;
}
@@ -486,7 +504,7 @@ static void wpa_supplicant_key_neg_complete(struct wpa_sm *sm,
static void wpa_sm_rekey_ptk(void *eloop_ctx, void *timeout_ctx)
{
struct wpa_sm *sm = eloop_ctx;
- wpa_printf(MSG_DEBUG, "WPA: Request PTK rekeying");
+ wpa_dbg(sm->ctx->msg_ctx, MSG_DEBUG, "WPA: Request PTK rekeying");
wpa_sm_key_request(sm, 0, 1);
}
@@ -499,29 +517,26 @@ static int wpa_supplicant_install_ptk(struct wpa_sm *sm,
const u8 *key_rsc;
u8 null_rsc[8] = { 0, 0, 0, 0, 0, 0, 0, 0 };
- wpa_printf(MSG_DEBUG, "WPA: Installing PTK to the driver.");
+ wpa_dbg(sm->ctx->msg_ctx, MSG_DEBUG,
+ "WPA: Installing PTK to the driver");
- switch (sm->pairwise_cipher) {
- case WPA_CIPHER_CCMP:
- alg = WPA_ALG_CCMP;
- keylen = 16;
- rsclen = 6;
- break;
- case WPA_CIPHER_TKIP:
- alg = WPA_ALG_TKIP;
- keylen = 32;
- rsclen = 6;
- break;
- case WPA_CIPHER_NONE:
- wpa_printf(MSG_DEBUG, "WPA: Pairwise Cipher Suite: "
- "NONE - do not use pairwise keys");
+ if (sm->pairwise_cipher == WPA_CIPHER_NONE) {
+ wpa_dbg(sm->ctx->msg_ctx, MSG_DEBUG, "WPA: Pairwise Cipher "
+ "Suite: NONE - do not use pairwise keys");
return 0;
- default:
- wpa_printf(MSG_WARNING, "WPA: Unsupported pairwise cipher %d",
- sm->pairwise_cipher);
+ }
+
+ if (!wpa_cipher_valid_pairwise(sm->pairwise_cipher)) {
+ wpa_msg(sm->ctx->msg_ctx, MSG_WARNING,
+ "WPA: Unsupported pairwise cipher %d",
+ sm->pairwise_cipher);
return -1;
}
+ alg = wpa_cipher_to_alg(sm->pairwise_cipher);
+ keylen = wpa_cipher_key_len(sm->pairwise_cipher);
+ rsclen = wpa_cipher_rsc_len(sm->pairwise_cipher);
+
if (sm->proto == WPA_PROTO_RSN) {
key_rsc = null_rsc;
} else {
@@ -531,9 +546,10 @@ static int wpa_supplicant_install_ptk(struct wpa_sm *sm,
if (wpa_sm_set_key(sm, alg, sm->bssid, 0, 1, key_rsc, rsclen,
(u8 *) sm->ptk.tk1, keylen) < 0) {
- wpa_printf(MSG_WARNING, "WPA: Failed to set PTK to the "
- "driver (alg=%d keylen=%d bssid=" MACSTR ")",
- alg, keylen, MAC2STR(sm->bssid));
+ wpa_msg(sm->ctx->msg_ctx, MSG_WARNING,
+ "WPA: Failed to set PTK to the "
+ "driver (alg=%d keylen=%d bssid=" MACSTR ")",
+ alg, keylen, MAC2STR(sm->bssid));
return -1;
}
@@ -547,59 +563,31 @@ static int wpa_supplicant_install_ptk(struct wpa_sm *sm,
}
-static int wpa_supplicant_check_group_cipher(int group_cipher,
+static int wpa_supplicant_check_group_cipher(struct wpa_sm *sm,
+ int group_cipher,
int keylen, int maxkeylen,
int *key_rsc_len,
enum wpa_alg *alg)
{
- int ret = 0;
+ int klen;
- switch (group_cipher) {
- case WPA_CIPHER_CCMP:
- if (keylen != 16 || maxkeylen < 16) {
- ret = -1;
- break;
- }
- *key_rsc_len = 6;
- *alg = WPA_ALG_CCMP;
- break;
- case WPA_CIPHER_TKIP:
- if (keylen != 32 || maxkeylen < 32) {
- ret = -1;
- break;
- }
- *key_rsc_len = 6;
- *alg = WPA_ALG_TKIP;
- break;
- case WPA_CIPHER_WEP104:
- if (keylen != 13 || maxkeylen < 13) {
- ret = -1;
- break;
- }
- *key_rsc_len = 0;
- *alg = WPA_ALG_WEP;
- break;
- case WPA_CIPHER_WEP40:
- if (keylen != 5 || maxkeylen < 5) {
- ret = -1;
- break;
- }
- *key_rsc_len = 0;
- *alg = WPA_ALG_WEP;
- break;
- default:
- wpa_printf(MSG_WARNING, "WPA: Unsupported Group Cipher %d",
- group_cipher);
+ *alg = wpa_cipher_to_alg(group_cipher);
+ if (*alg == WPA_ALG_NONE) {
+ wpa_msg(sm->ctx->msg_ctx, MSG_WARNING,
+ "WPA: Unsupported Group Cipher %d",
+ group_cipher);
return -1;
}
+ *key_rsc_len = wpa_cipher_rsc_len(group_cipher);
- if (ret < 0 ) {
- wpa_printf(MSG_WARNING, "WPA: Unsupported %s Group Cipher key "
- "length %d (%d).",
- wpa_cipher_txt(group_cipher), keylen, maxkeylen);
+ klen = wpa_cipher_key_len(group_cipher);
+ if (keylen != klen || maxkeylen < klen) {
+ wpa_msg(sm->ctx->msg_ctx, MSG_WARNING,
+ "WPA: Unsupported %s Group Cipher key length %d (%d)",
+ wpa_cipher_txt(group_cipher), keylen, maxkeylen);
+ return -1;
}
-
- return ret;
+ return 0;
}
@@ -619,9 +607,9 @@ static int wpa_supplicant_install_gtk(struct wpa_sm *sm,
u8 gtk_buf[32];
wpa_hexdump_key(MSG_DEBUG, "WPA: Group Key", gd->gtk, gd->gtk_len);
- wpa_printf(MSG_DEBUG, "WPA: Installing GTK to the driver "
- "(keyidx=%d tx=%d len=%d).", gd->keyidx, gd->tx,
- gd->gtk_len);
+ wpa_dbg(sm->ctx->msg_ctx, MSG_DEBUG,
+ "WPA: Installing GTK to the driver (keyidx=%d tx=%d len=%d)",
+ gd->keyidx, gd->tx, gd->gtk_len);
wpa_hexdump(MSG_DEBUG, "WPA: RSC", key_rsc, gd->key_rsc_len);
if (sm->group_cipher == WPA_CIPHER_TKIP) {
/* Swap Tx/Rx keys for Michael MIC */
@@ -631,21 +619,21 @@ static int wpa_supplicant_install_gtk(struct wpa_sm *sm,
_gtk = gtk_buf;
}
if (sm->pairwise_cipher == WPA_CIPHER_NONE) {
- if (wpa_sm_set_key(sm, gd->alg,
- (u8 *) "\xff\xff\xff\xff\xff\xff",
+ if (wpa_sm_set_key(sm, gd->alg, NULL,
gd->keyidx, 1, key_rsc, gd->key_rsc_len,
_gtk, gd->gtk_len) < 0) {
- wpa_printf(MSG_WARNING, "WPA: Failed to set "
- "GTK to the driver (Group only).");
+ wpa_msg(sm->ctx->msg_ctx, MSG_WARNING,
+ "WPA: Failed to set GTK to the driver "
+ "(Group only)");
return -1;
}
- } else if (wpa_sm_set_key(sm, gd->alg,
- (u8 *) "\xff\xff\xff\xff\xff\xff",
+ } else if (wpa_sm_set_key(sm, gd->alg, broadcast_ether_addr,
gd->keyidx, gd->tx, key_rsc, gd->key_rsc_len,
_gtk, gd->gtk_len) < 0) {
- wpa_printf(MSG_WARNING, "WPA: Failed to set GTK to "
- "the driver (alg=%d keylen=%d keyidx=%d)",
- gd->alg, gd->gtk_len, gd->keyidx);
+ wpa_msg(sm->ctx->msg_ctx, MSG_WARNING,
+ "WPA: Failed to set GTK to "
+ "the driver (alg=%d keylen=%d keyidx=%d)",
+ gd->alg, gd->gtk_len, gd->keyidx);
return -1;
}
@@ -662,8 +650,9 @@ static int wpa_supplicant_gtk_tx_bit_workaround(const struct wpa_sm *sm,
* doing Group Key only APs) and without this workaround, the
* data connection does not work because wpa_supplicant
* configured non-zero keyidx to be used for unicast. */
- wpa_printf(MSG_INFO, "WPA: Tx bit set for GTK, but pairwise "
- "keys are used - ignore Tx bit");
+ wpa_msg(sm->ctx->msg_ctx, MSG_INFO,
+ "WPA: Tx bit set for GTK, but pairwise "
+ "keys are used - ignore Tx bit");
return 0;
}
return tx;
@@ -702,11 +691,12 @@ static int wpa_supplicant_pairwise_gtk(struct wpa_sm *sm,
os_memcpy(gd.gtk, gtk, gtk_len);
gd.gtk_len = gtk_len;
- if (wpa_supplicant_check_group_cipher(sm->group_cipher,
+ if (wpa_supplicant_check_group_cipher(sm, sm->group_cipher,
gtk_len, gtk_len,
&gd.key_rsc_len, &gd.alg) ||
wpa_supplicant_install_gtk(sm, &gd, key->key_rsc)) {
- wpa_printf(MSG_DEBUG, "RSN: Failed to install GTK");
+ wpa_dbg(sm->ctx->msg_ctx, MSG_DEBUG,
+ "RSN: Failed to install GTK");
return -1;
}
@@ -733,22 +723,21 @@ static int ieee80211w_set_keys(struct wpa_sm *sm,
return -1;
igtk = (const struct wpa_igtk_kde *) ie->igtk;
keyidx = WPA_GET_LE16(igtk->keyid);
- wpa_printf(MSG_DEBUG, "WPA: IGTK keyid %d "
- "pn %02x%02x%02x%02x%02x%02x",
- keyidx, MAC2STR(igtk->pn));
+ wpa_dbg(sm->ctx->msg_ctx, MSG_DEBUG, "WPA: IGTK keyid %d "
+ "pn %02x%02x%02x%02x%02x%02x",
+ keyidx, MAC2STR(igtk->pn));
wpa_hexdump_key(MSG_DEBUG, "WPA: IGTK",
igtk->igtk, WPA_IGTK_LEN);
if (keyidx > 4095) {
- wpa_printf(MSG_WARNING, "WPA: Invalid IGTK KeyID %d",
- keyidx);
+ wpa_msg(sm->ctx->msg_ctx, MSG_WARNING,
+ "WPA: Invalid IGTK KeyID %d", keyidx);
return -1;
}
- if (wpa_sm_set_key(sm, WPA_ALG_IGTK,
- (u8 *) "\xff\xff\xff\xff\xff\xff",
+ if (wpa_sm_set_key(sm, WPA_ALG_IGTK, broadcast_ether_addr,
keyidx, 0, igtk->pn, sizeof(igtk->pn),
igtk->igtk, WPA_IGTK_LEN) < 0) {
- wpa_printf(MSG_WARNING, "WPA: Failed to configure IGTK"
- " to the driver");
+ wpa_msg(sm->ctx->msg_ctx, MSG_WARNING,
+ "WPA: Failed to configure IGTK to the driver");
return -1;
}
}
@@ -774,8 +763,8 @@ static void wpa_report_ie_mismatch(struct wpa_sm *sm,
}
if (wpa_ie) {
if (!sm->ap_wpa_ie) {
- wpa_printf(MSG_INFO, "WPA: No WPA IE in "
- "Beacon/ProbeResp");
+ wpa_msg(sm->ctx->msg_ctx, MSG_INFO,
+ "WPA: No WPA IE in Beacon/ProbeResp");
}
wpa_hexdump(MSG_INFO, "WPA: WPA IE in 3/4 msg",
wpa_ie, wpa_ie_len);
@@ -787,14 +776,14 @@ static void wpa_report_ie_mismatch(struct wpa_sm *sm,
}
if (rsn_ie) {
if (!sm->ap_rsn_ie) {
- wpa_printf(MSG_INFO, "WPA: No RSN IE in "
- "Beacon/ProbeResp");
+ wpa_msg(sm->ctx->msg_ctx, MSG_INFO,
+ "WPA: No RSN IE in Beacon/ProbeResp");
}
wpa_hexdump(MSG_INFO, "WPA: RSN IE in 3/4 msg",
rsn_ie, rsn_ie_len);
}
- wpa_sm_disassociate(sm, WLAN_REASON_IE_IN_4WAY_DIFFERS);
+ wpa_sm_deauthenticate(sm, WLAN_REASON_IE_IN_4WAY_DIFFERS);
}
@@ -811,15 +800,15 @@ static int ft_validate_mdie(struct wpa_sm *sm,
if (ie->mdie == NULL || ie->mdie_len < 2 + sizeof(*mdie) ||
os_memcmp(mdie->mobility_domain, sm->mobility_domain,
MOBILITY_DOMAIN_ID_LEN) != 0) {
- wpa_printf(MSG_DEBUG, "FT: MDIE in msg 3/4 did not "
- "match with the current mobility domain");
+ wpa_dbg(sm->ctx->msg_ctx, MSG_DEBUG, "FT: MDIE in msg 3/4 did "
+ "not match with the current mobility domain");
return -1;
}
if (assoc_resp_mdie &&
(assoc_resp_mdie[1] != ie->mdie[1] ||
os_memcmp(assoc_resp_mdie, ie->mdie, 2 + ie->mdie[1]) != 0)) {
- wpa_printf(MSG_DEBUG, "FT: MDIE mismatch");
+ wpa_dbg(sm->ctx->msg_ctx, MSG_DEBUG, "FT: MDIE mismatch");
wpa_hexdump(MSG_DEBUG, "FT: MDIE in EAPOL-Key msg 3/4",
ie->mdie, 2 + ie->mdie[1]);
wpa_hexdump(MSG_DEBUG, "FT: MDIE in (Re)Association Response",
@@ -837,7 +826,8 @@ static int ft_validate_ftie(struct wpa_sm *sm,
const u8 *assoc_resp_ftie)
{
if (ie->ftie == NULL) {
- wpa_printf(MSG_DEBUG, "FT: No FTIE in EAPOL-Key msg 3/4");
+ wpa_dbg(sm->ctx->msg_ctx, MSG_DEBUG,
+ "FT: No FTIE in EAPOL-Key msg 3/4");
return -1;
}
@@ -846,7 +836,7 @@ static int ft_validate_ftie(struct wpa_sm *sm,
if (assoc_resp_ftie[1] != ie->ftie[1] ||
os_memcmp(assoc_resp_ftie, ie->ftie, 2 + ie->ftie[1]) != 0) {
- wpa_printf(MSG_DEBUG, "FT: FTIE mismatch");
+ wpa_dbg(sm->ctx->msg_ctx, MSG_DEBUG, "FT: FTIE mismatch");
wpa_hexdump(MSG_DEBUG, "FT: FTIE in EAPOL-Key msg 3/4",
ie->ftie, 2 + ie->ftie[1]);
wpa_hexdump(MSG_DEBUG, "FT: FTIE in (Re)Association Response",
@@ -873,14 +863,15 @@ static int ft_validate_rsnie(struct wpa_sm *sm,
*/
if (wpa_parse_wpa_ie_rsn(ie->rsn_ie, ie->rsn_ie_len, &rsn) < 0 ||
rsn.num_pmkid != 1 || rsn.pmkid == NULL) {
- wpa_printf(MSG_DEBUG, "FT: No PMKR1Name in "
- "FT 4-way handshake message 3/4");
+ wpa_dbg(sm->ctx->msg_ctx, MSG_DEBUG, "FT: No PMKR1Name in "
+ "FT 4-way handshake message 3/4");
return -1;
}
if (os_memcmp(rsn.pmkid, sm->pmk_r1_name, WPA_PMK_NAME_LEN) != 0) {
- wpa_printf(MSG_DEBUG, "FT: PMKR1Name mismatch in "
- "FT 4-way handshake message 3/4");
+ wpa_dbg(sm->ctx->msg_ctx, MSG_DEBUG,
+ "FT: PMKR1Name mismatch in "
+ "FT 4-way handshake message 3/4");
wpa_hexdump(MSG_DEBUG, "FT: PMKR1Name from Authenticator",
rsn.pmkid, WPA_PMK_NAME_LEN);
wpa_hexdump(MSG_DEBUG, "FT: Derived PMKR1Name",
@@ -932,14 +923,17 @@ static int wpa_supplicant_validate_ie(struct wpa_sm *sm,
struct wpa_eapol_ie_parse *ie)
{
if (sm->ap_wpa_ie == NULL && sm->ap_rsn_ie == NULL) {
- wpa_printf(MSG_DEBUG, "WPA: No WPA/RSN IE for this AP known. "
- "Trying to get from scan results");
+ wpa_dbg(sm->ctx->msg_ctx, MSG_DEBUG,
+ "WPA: No WPA/RSN IE for this AP known. "
+ "Trying to get from scan results");
if (wpa_sm_get_beacon_ie(sm) < 0) {
- wpa_printf(MSG_WARNING, "WPA: Could not find AP from "
- "the scan results");
+ wpa_msg(sm->ctx->msg_ctx, MSG_WARNING,
+ "WPA: Could not find AP from "
+ "the scan results");
} else {
- wpa_printf(MSG_DEBUG, "WPA: Found the current AP from "
- "updated scan results");
+ wpa_msg(sm->ctx->msg_ctx, MSG_DEBUG,
+ "WPA: Found the current AP from "
+ "updated scan results");
}
}
@@ -1034,7 +1028,7 @@ int wpa_supplicant_send_4_of_4(struct wpa_sm *sm, const unsigned char *dst,
if (kde)
os_memcpy(reply + 1, kde, kde_len);
- wpa_printf(MSG_DEBUG, "WPA: Sending EAPOL-Key 4/4");
+ wpa_dbg(sm->ctx->msg_ctx, MSG_DEBUG, "WPA: Sending EAPOL-Key 4/4");
wpa_eapol_key_send(sm, ptk->kck, ver, dst, ETH_P_EAPOL,
rbuf, rlen, reply->key_mic);
@@ -1051,29 +1045,32 @@ static void wpa_supplicant_process_3_of_4(struct wpa_sm *sm,
struct wpa_eapol_ie_parse ie;
wpa_sm_set_state(sm, WPA_4WAY_HANDSHAKE);
- wpa_printf(MSG_DEBUG, "WPA: RX message 3 of 4-Way Handshake from "
- MACSTR " (ver=%d)", MAC2STR(sm->bssid), ver);
+ wpa_dbg(sm->ctx->msg_ctx, MSG_DEBUG, "WPA: RX message 3 of 4-Way "
+ "Handshake from " MACSTR " (ver=%d)", MAC2STR(sm->bssid), ver);
key_info = WPA_GET_BE16(key->key_info);
pos = (const u8 *) (key + 1);
len = WPA_GET_BE16(key->key_data_length);
wpa_hexdump(MSG_DEBUG, "WPA: IE KeyData", pos, len);
- wpa_supplicant_parse_ies(pos, len, &ie);
+ if (wpa_supplicant_parse_ies(pos, len, &ie) < 0)
+ goto failed;
if (ie.gtk && !(key_info & WPA_KEY_INFO_ENCR_KEY_DATA)) {
- wpa_printf(MSG_WARNING, "WPA: GTK IE in unencrypted key data");
+ wpa_msg(sm->ctx->msg_ctx, MSG_WARNING,
+ "WPA: GTK IE in unencrypted key data");
goto failed;
}
#ifdef CONFIG_IEEE80211W
if (ie.igtk && !(key_info & WPA_KEY_INFO_ENCR_KEY_DATA)) {
- wpa_printf(MSG_WARNING, "WPA: IGTK KDE in unencrypted key "
- "data");
+ wpa_msg(sm->ctx->msg_ctx, MSG_WARNING,
+ "WPA: IGTK KDE in unencrypted key data");
goto failed;
}
if (ie.igtk && ie.igtk_len != sizeof(struct wpa_igtk_kde)) {
- wpa_printf(MSG_WARNING, "WPA: Invalid IGTK KDE length %lu",
- (unsigned long) ie.igtk_len);
+ wpa_msg(sm->ctx->msg_ctx, MSG_WARNING,
+ "WPA: Invalid IGTK KDE length %lu",
+ (unsigned long) ie.igtk_len);
goto failed;
}
#endif /* CONFIG_IEEE80211W */
@@ -1082,30 +1079,20 @@ static void wpa_supplicant_process_3_of_4(struct wpa_sm *sm,
goto failed;
if (os_memcmp(sm->anonce, key->key_nonce, WPA_NONCE_LEN) != 0) {
- wpa_printf(MSG_WARNING, "WPA: ANonce from message 1 of 4-Way "
- "Handshake differs from 3 of 4-Way Handshake - drop"
- " packet (src=" MACSTR ")", MAC2STR(sm->bssid));
+ wpa_msg(sm->ctx->msg_ctx, MSG_WARNING,
+ "WPA: ANonce from message 1 of 4-Way Handshake "
+ "differs from 3 of 4-Way Handshake - drop packet (src="
+ MACSTR ")", MAC2STR(sm->bssid));
goto failed;
}
keylen = WPA_GET_BE16(key->key_length);
- switch (sm->pairwise_cipher) {
- case WPA_CIPHER_CCMP:
- if (keylen != 16) {
- wpa_printf(MSG_WARNING, "WPA: Invalid CCMP key length "
- "%d (src=" MACSTR ")",
- keylen, MAC2STR(sm->bssid));
- goto failed;
- }
- break;
- case WPA_CIPHER_TKIP:
- if (keylen != 32) {
- wpa_printf(MSG_WARNING, "WPA: Invalid TKIP key length "
- "%d (src=" MACSTR ")",
- keylen, MAC2STR(sm->bssid));
- goto failed;
- }
- break;
+ if (keylen != wpa_cipher_key_len(sm->pairwise_cipher)) {
+ wpa_msg(sm->ctx->msg_ctx, MSG_WARNING,
+ "WPA: Invalid %s key length %d (src=" MACSTR
+ ")", wpa_cipher_txt(sm->pairwise_cipher), keylen,
+ MAC2STR(sm->bssid));
+ goto failed;
}
if (wpa_supplicant_send_4_of_4(sm, sm->bssid, key, ver, key_info,
@@ -1134,15 +1121,19 @@ static void wpa_supplicant_process_3_of_4(struct wpa_sm *sm,
if (ie.gtk &&
wpa_supplicant_pairwise_gtk(sm, key,
ie.gtk, ie.gtk_len, key_info) < 0) {
- wpa_printf(MSG_INFO, "RSN: Failed to configure GTK");
+ wpa_msg(sm->ctx->msg_ctx, MSG_INFO,
+ "RSN: Failed to configure GTK");
goto failed;
}
if (ieee80211w_set_keys(sm, &ie) < 0) {
- wpa_printf(MSG_INFO, "RSN: Failed to configure IGTK");
+ wpa_msg(sm->ctx->msg_ctx, MSG_INFO,
+ "RSN: Failed to configure IGTK");
goto failed;
}
+ wpa_sm_set_rekey_offload(sm);
+
return;
failed:
@@ -1160,18 +1151,21 @@ static int wpa_supplicant_process_1_of_2_rsn(struct wpa_sm *sm,
struct wpa_eapol_ie_parse ie;
wpa_hexdump(MSG_DEBUG, "RSN: msg 1/2 key data", keydata, keydatalen);
- wpa_supplicant_parse_ies(keydata, keydatalen, &ie);
+ if (wpa_supplicant_parse_ies(keydata, keydatalen, &ie) < 0)
+ return -1;
if (ie.gtk && !(key_info & WPA_KEY_INFO_ENCR_KEY_DATA)) {
- wpa_printf(MSG_WARNING, "WPA: GTK IE in unencrypted key data");
+ wpa_msg(sm->ctx->msg_ctx, MSG_WARNING,
+ "WPA: GTK IE in unencrypted key data");
return -1;
}
if (ie.gtk == NULL) {
- wpa_printf(MSG_INFO, "WPA: No GTK IE in Group Key msg 1/2");
+ wpa_msg(sm->ctx->msg_ctx, MSG_INFO,
+ "WPA: No GTK IE in Group Key msg 1/2");
return -1;
}
maxkeylen = gd->gtk_len = ie.gtk_len - 2;
- if (wpa_supplicant_check_group_cipher(sm->group_cipher,
+ if (wpa_supplicant_check_group_cipher(sm, sm->group_cipher,
gd->gtk_len, maxkeylen,
&gd->key_rsc_len, &gd->alg))
return -1;
@@ -1182,14 +1176,16 @@ static int wpa_supplicant_process_1_of_2_rsn(struct wpa_sm *sm,
gd->tx = wpa_supplicant_gtk_tx_bit_workaround(sm,
!!(ie.gtk[0] & BIT(2)));
if (ie.gtk_len - 2 > sizeof(gd->gtk)) {
- wpa_printf(MSG_INFO, "RSN: Too long GTK in GTK IE "
- "(len=%lu)", (unsigned long) ie.gtk_len - 2);
+ wpa_msg(sm->ctx->msg_ctx, MSG_INFO,
+ "RSN: Too long GTK in GTK IE (len=%lu)",
+ (unsigned long) ie.gtk_len - 2);
return -1;
}
os_memcpy(gd->gtk, ie.gtk + 2, ie.gtk_len - 2);
if (ieee80211w_set_keys(sm, &ie) < 0)
- wpa_printf(MSG_INFO, "RSN: Failed to configure IGTK");
+ wpa_msg(sm->ctx->msg_ctx, MSG_INFO,
+ "RSN: Failed to configure IGTK");
return 0;
}
@@ -1207,22 +1203,23 @@ static int wpa_supplicant_process_1_of_2_wpa(struct wpa_sm *sm,
gd->gtk_len = WPA_GET_BE16(key->key_length);
maxkeylen = keydatalen;
if (keydatalen > extra_len) {
- wpa_printf(MSG_INFO, "WPA: Truncated EAPOL-Key packet:"
- " key_data_length=%lu > extra_len=%lu",
- (unsigned long) keydatalen,
- (unsigned long) extra_len);
+ wpa_msg(sm->ctx->msg_ctx, MSG_INFO,
+ "WPA: Truncated EAPOL-Key packet: "
+ "key_data_length=%lu > extra_len=%lu",
+ (unsigned long) keydatalen, (unsigned long) extra_len);
return -1;
}
if (ver == WPA_KEY_INFO_TYPE_HMAC_SHA1_AES) {
if (maxkeylen < 8) {
- wpa_printf(MSG_INFO, "WPA: Too short maxkeylen (%lu)",
- (unsigned long) maxkeylen);
+ wpa_msg(sm->ctx->msg_ctx, MSG_INFO,
+ "WPA: Too short maxkeylen (%lu)",
+ (unsigned long) maxkeylen);
return -1;
}
maxkeylen -= 8;
}
- if (wpa_supplicant_check_group_cipher(sm->group_cipher,
+ if (wpa_supplicant_check_group_cipher(sm, sm->group_cipher,
gd->gtk_len, maxkeylen,
&gd->key_rsc_len, &gd->alg))
return -1;
@@ -1233,38 +1230,42 @@ static int wpa_supplicant_process_1_of_2_wpa(struct wpa_sm *sm,
os_memcpy(ek, key->key_iv, 16);
os_memcpy(ek + 16, sm->ptk.kek, 16);
if (keydatalen > sizeof(gd->gtk)) {
- wpa_printf(MSG_WARNING, "WPA: RC4 key data "
- "too long (%lu)",
- (unsigned long) keydatalen);
+ wpa_msg(sm->ctx->msg_ctx, MSG_WARNING,
+ "WPA: RC4 key data too long (%lu)",
+ (unsigned long) keydatalen);
return -1;
}
os_memcpy(gd->gtk, key + 1, keydatalen);
if (rc4_skip(ek, 32, 256, gd->gtk, keydatalen)) {
- wpa_printf(MSG_ERROR, "WPA: RC4 failed");
+ wpa_msg(sm->ctx->msg_ctx, MSG_ERROR,
+ "WPA: RC4 failed");
return -1;
}
} else if (ver == WPA_KEY_INFO_TYPE_HMAC_SHA1_AES) {
if (keydatalen % 8) {
- wpa_printf(MSG_WARNING, "WPA: Unsupported AES-WRAP "
- "len %lu", (unsigned long) keydatalen);
+ wpa_msg(sm->ctx->msg_ctx, MSG_WARNING,
+ "WPA: Unsupported AES-WRAP len %lu",
+ (unsigned long) keydatalen);
return -1;
}
if (maxkeylen > sizeof(gd->gtk)) {
- wpa_printf(MSG_WARNING, "WPA: AES-WRAP key data "
- "too long (keydatalen=%lu maxkeylen=%lu)",
- (unsigned long) keydatalen,
- (unsigned long) maxkeylen);
+ wpa_msg(sm->ctx->msg_ctx, MSG_WARNING,
+ "WPA: AES-WRAP key data "
+ "too long (keydatalen=%lu maxkeylen=%lu)",
+ (unsigned long) keydatalen,
+ (unsigned long) maxkeylen);
return -1;
}
if (aes_unwrap(sm->ptk.kek, maxkeylen / 8,
(const u8 *) (key + 1), gd->gtk)) {
- wpa_printf(MSG_WARNING, "WPA: AES unwrap "
- "failed - could not decrypt GTK");
+ wpa_msg(sm->ctx->msg_ctx, MSG_WARNING,
+ "WPA: AES unwrap failed - could not decrypt "
+ "GTK");
return -1;
}
} else {
- wpa_printf(MSG_WARNING, "WPA: Unsupported key_info type %d",
- ver);
+ wpa_msg(sm->ctx->msg_ctx, MSG_WARNING,
+ "WPA: Unsupported key_info type %d", ver);
return -1;
}
gd->tx = wpa_supplicant_gtk_tx_bit_workaround(
@@ -1300,7 +1301,7 @@ static int wpa_supplicant_send_2_of_2(struct wpa_sm *sm,
WPA_PUT_BE16(reply->key_data_length, 0);
- wpa_printf(MSG_DEBUG, "WPA: Sending EAPOL-Key 2/2");
+ wpa_dbg(sm->ctx->msg_ctx, MSG_DEBUG, "WPA: Sending EAPOL-Key 2/2");
wpa_eapol_key_send(sm, sm->ptk.kck, ver, sm->bssid, ETH_P_EAPOL,
rbuf, rlen, reply->key_mic);
@@ -1320,8 +1321,8 @@ static void wpa_supplicant_process_1_of_2(struct wpa_sm *sm,
os_memset(&gd, 0, sizeof(gd));
rekey = wpa_sm_get_state(sm) == WPA_COMPLETED;
- wpa_printf(MSG_DEBUG, "WPA: RX message 1 of Group Key Handshake from "
- MACSTR " (ver=%d)", MAC2STR(src_addr), ver);
+ wpa_dbg(sm->ctx->msg_ctx, MSG_DEBUG, "WPA: RX message 1 of Group Key "
+ "Handshake from " MACSTR " (ver=%d)", MAC2STR(src_addr), ver);
key_info = WPA_GET_BE16(key->key_info);
keydatalen = WPA_GET_BE16(key->key_data_length);
@@ -1352,6 +1353,8 @@ static void wpa_supplicant_process_1_of_2(struct wpa_sm *sm,
MAC2STR(sm->bssid), wpa_cipher_txt(sm->group_cipher));
wpa_sm_cancel_auth_timeout(sm);
wpa_sm_set_state(sm, WPA_COMPLETED);
+
+ wpa_sm_set_rekey_offload(sm);
} else {
wpa_supplicant_key_neg_complete(sm, sm->bssid,
key_info &
@@ -1378,8 +1381,9 @@ static int wpa_supplicant_verify_eapol_key_mic(struct wpa_sm *sm,
wpa_eapol_key_mic(sm->tptk.kck, ver, buf, len,
key->key_mic);
if (os_memcmp(mic, key->key_mic, 16) != 0) {
- wpa_printf(MSG_WARNING, "WPA: Invalid EAPOL-Key MIC "
- "when using TPTK - ignoring TPTK");
+ wpa_msg(sm->ctx->msg_ctx, MSG_WARNING,
+ "WPA: Invalid EAPOL-Key MIC "
+ "when using TPTK - ignoring TPTK");
} else {
ok = 1;
sm->tptk_set = 0;
@@ -1393,16 +1397,18 @@ static int wpa_supplicant_verify_eapol_key_mic(struct wpa_sm *sm,
wpa_eapol_key_mic(sm->ptk.kck, ver, buf, len,
key->key_mic);
if (os_memcmp(mic, key->key_mic, 16) != 0) {
- wpa_printf(MSG_WARNING, "WPA: Invalid EAPOL-Key MIC "
- "- dropping packet");
+ wpa_msg(sm->ctx->msg_ctx, MSG_WARNING,
+ "WPA: Invalid EAPOL-Key MIC - "
+ "dropping packet");
return -1;
}
ok = 1;
}
if (!ok) {
- wpa_printf(MSG_WARNING, "WPA: Could not verify EAPOL-Key MIC "
- "- dropping packet");
+ wpa_msg(sm->ctx->msg_ctx, MSG_WARNING,
+ "WPA: Could not verify EAPOL-Key MIC - "
+ "dropping packet");
return -1;
}
@@ -1422,8 +1428,9 @@ static int wpa_supplicant_decrypt_key_data(struct wpa_sm *sm,
wpa_hexdump(MSG_DEBUG, "RSN: encrypted key data",
(u8 *) (key + 1), keydatalen);
if (!sm->ptk_set) {
- wpa_printf(MSG_WARNING, "WPA: PTK not available, "
- "cannot decrypt EAPOL-Key key data.");
+ wpa_msg(sm->ctx->msg_ctx, MSG_WARNING,
+ "WPA: PTK not available, cannot decrypt EAPOL-Key Key "
+ "Data");
return -1;
}
@@ -1434,37 +1441,40 @@ static int wpa_supplicant_decrypt_key_data(struct wpa_sm *sm,
os_memcpy(ek, key->key_iv, 16);
os_memcpy(ek + 16, sm->ptk.kek, 16);
if (rc4_skip(ek, 32, 256, (u8 *) (key + 1), keydatalen)) {
- wpa_printf(MSG_ERROR, "WPA: RC4 failed");
+ wpa_msg(sm->ctx->msg_ctx, MSG_ERROR,
+ "WPA: RC4 failed");
return -1;
}
} else if (ver == WPA_KEY_INFO_TYPE_HMAC_SHA1_AES ||
ver == WPA_KEY_INFO_TYPE_AES_128_CMAC) {
u8 *buf;
if (keydatalen % 8) {
- wpa_printf(MSG_WARNING, "WPA: Unsupported "
- "AES-WRAP len %d", keydatalen);
+ wpa_msg(sm->ctx->msg_ctx, MSG_WARNING,
+ "WPA: Unsupported AES-WRAP len %d",
+ keydatalen);
return -1;
}
keydatalen -= 8; /* AES-WRAP adds 8 bytes */
buf = os_malloc(keydatalen);
if (buf == NULL) {
- wpa_printf(MSG_WARNING, "WPA: No memory for "
- "AES-UNWRAP buffer");
+ wpa_msg(sm->ctx->msg_ctx, MSG_WARNING,
+ "WPA: No memory for AES-UNWRAP buffer");
return -1;
}
if (aes_unwrap(sm->ptk.kek, keydatalen / 8,
(u8 *) (key + 1), buf)) {
os_free(buf);
- wpa_printf(MSG_WARNING, "WPA: AES unwrap failed - "
- "could not decrypt EAPOL-Key key data");
+ wpa_msg(sm->ctx->msg_ctx, MSG_WARNING,
+ "WPA: AES unwrap failed - "
+ "could not decrypt EAPOL-Key key data");
return -1;
}
os_memcpy(key + 1, buf, keydatalen);
os_free(buf);
WPA_PUT_BE16(key->key_data_length, keydatalen);
} else {
- wpa_printf(MSG_WARNING, "WPA: Unsupported key_info type %d",
- ver);
+ wpa_msg(sm->ctx->msg_ctx, MSG_WARNING,
+ "WPA: Unsupported key_info type %d", ver);
return -1;
}
wpa_hexdump_key(MSG_DEBUG, "WPA: decrypted EAPOL-Key key data",
@@ -1480,35 +1490,38 @@ static int wpa_supplicant_decrypt_key_data(struct wpa_sm *sm,
void wpa_sm_aborted_cached(struct wpa_sm *sm)
{
if (sm && sm->cur_pmksa) {
- wpa_printf(MSG_DEBUG, "RSN: Cancelling PMKSA caching attempt");
+ wpa_dbg(sm->ctx->msg_ctx, MSG_DEBUG,
+ "RSN: Cancelling PMKSA caching attempt");
sm->cur_pmksa = NULL;
}
}
-static void wpa_eapol_key_dump(const struct wpa_eapol_key *key)
+static void wpa_eapol_key_dump(struct wpa_sm *sm,
+ const struct wpa_eapol_key *key)
{
#ifndef CONFIG_NO_STDOUT_DEBUG
u16 key_info = WPA_GET_BE16(key->key_info);
- wpa_printf(MSG_DEBUG, " EAPOL-Key type=%d", key->type);
- wpa_printf(MSG_DEBUG, " key_info 0x%x (ver=%d keyidx=%d rsvd=%d %s"
- "%s%s%s%s%s%s%s)",
- key_info, key_info & WPA_KEY_INFO_TYPE_MASK,
- (key_info & WPA_KEY_INFO_KEY_INDEX_MASK) >>
- WPA_KEY_INFO_KEY_INDEX_SHIFT,
- (key_info & (BIT(13) | BIT(14) | BIT(15))) >> 13,
- key_info & WPA_KEY_INFO_KEY_TYPE ? "Pairwise" : "Group",
- key_info & WPA_KEY_INFO_INSTALL ? " Install" : "",
- key_info & WPA_KEY_INFO_ACK ? " Ack" : "",
- key_info & WPA_KEY_INFO_MIC ? " MIC" : "",
- key_info & WPA_KEY_INFO_SECURE ? " Secure" : "",
- key_info & WPA_KEY_INFO_ERROR ? " Error" : "",
- key_info & WPA_KEY_INFO_REQUEST ? " Request" : "",
- key_info & WPA_KEY_INFO_ENCR_KEY_DATA ? " Encr" : "");
- wpa_printf(MSG_DEBUG, " key_length=%u key_data_length=%u",
- WPA_GET_BE16(key->key_length),
- WPA_GET_BE16(key->key_data_length));
+ wpa_dbg(sm->ctx->msg_ctx, MSG_DEBUG, " EAPOL-Key type=%d", key->type);
+ wpa_dbg(sm->ctx->msg_ctx, MSG_DEBUG,
+ " key_info 0x%x (ver=%d keyidx=%d rsvd=%d %s%s%s%s%s%s%s%s)",
+ key_info, key_info & WPA_KEY_INFO_TYPE_MASK,
+ (key_info & WPA_KEY_INFO_KEY_INDEX_MASK) >>
+ WPA_KEY_INFO_KEY_INDEX_SHIFT,
+ (key_info & (BIT(13) | BIT(14) | BIT(15))) >> 13,
+ key_info & WPA_KEY_INFO_KEY_TYPE ? "Pairwise" : "Group",
+ key_info & WPA_KEY_INFO_INSTALL ? " Install" : "",
+ key_info & WPA_KEY_INFO_ACK ? " Ack" : "",
+ key_info & WPA_KEY_INFO_MIC ? " MIC" : "",
+ key_info & WPA_KEY_INFO_SECURE ? " Secure" : "",
+ key_info & WPA_KEY_INFO_ERROR ? " Error" : "",
+ key_info & WPA_KEY_INFO_REQUEST ? " Request" : "",
+ key_info & WPA_KEY_INFO_ENCR_KEY_DATA ? " Encr" : "");
+ wpa_dbg(sm->ctx->msg_ctx, MSG_DEBUG,
+ " key_length=%u key_data_length=%u",
+ WPA_GET_BE16(key->key_length),
+ WPA_GET_BE16(key->key_data_length));
wpa_hexdump(MSG_DEBUG, " replay_counter",
key->replay_counter, WPA_REPLAY_COUNTER_LEN);
wpa_hexdump(MSG_DEBUG, " key_nonce", key->key_nonce, WPA_NONCE_LEN);
@@ -1552,10 +1565,11 @@ int wpa_sm_rx_eapol(struct wpa_sm *sm, const u8 *src_addr,
#endif /* CONFIG_IEEE80211R */
if (len < sizeof(*hdr) + sizeof(*key)) {
- wpa_printf(MSG_DEBUG, "WPA: EAPOL frame too short to be a WPA "
- "EAPOL-Key (len %lu, expecting at least %lu)",
- (unsigned long) len,
- (unsigned long) sizeof(*hdr) + sizeof(*key));
+ wpa_dbg(sm->ctx->msg_ctx, MSG_DEBUG,
+ "WPA: EAPOL frame too short to be a WPA "
+ "EAPOL-Key (len %lu, expecting at least %lu)",
+ (unsigned long) len,
+ (unsigned long) sizeof(*hdr) + sizeof(*key));
return 0;
}
@@ -1568,40 +1582,45 @@ int wpa_sm_rx_eapol(struct wpa_sm *sm, const u8 *src_addr,
key = (struct wpa_eapol_key *) (hdr + 1);
plen = be_to_host16(hdr->length);
data_len = plen + sizeof(*hdr);
- wpa_printf(MSG_DEBUG, "IEEE 802.1X RX: version=%d type=%d length=%lu",
- hdr->version, hdr->type, (unsigned long) plen);
+ wpa_dbg(sm->ctx->msg_ctx, MSG_DEBUG,
+ "IEEE 802.1X RX: version=%d type=%d length=%lu",
+ hdr->version, hdr->type, (unsigned long) plen);
if (hdr->version < EAPOL_VERSION) {
/* TODO: backwards compatibility */
}
if (hdr->type != IEEE802_1X_TYPE_EAPOL_KEY) {
- wpa_printf(MSG_DEBUG, "WPA: EAPOL frame (type %u) discarded, "
+ wpa_dbg(sm->ctx->msg_ctx, MSG_DEBUG,
+ "WPA: EAPOL frame (type %u) discarded, "
"not a Key frame", hdr->type);
ret = 0;
goto out;
}
if (plen > len - sizeof(*hdr) || plen < sizeof(*key)) {
- wpa_printf(MSG_DEBUG, "WPA: EAPOL frame payload size %lu "
- "invalid (frame size %lu)",
- (unsigned long) plen, (unsigned long) len);
+ wpa_dbg(sm->ctx->msg_ctx, MSG_DEBUG,
+ "WPA: EAPOL frame payload size %lu "
+ "invalid (frame size %lu)",
+ (unsigned long) plen, (unsigned long) len);
ret = 0;
goto out;
}
if (key->type != EAPOL_KEY_TYPE_WPA && key->type != EAPOL_KEY_TYPE_RSN)
{
- wpa_printf(MSG_DEBUG, "WPA: EAPOL-Key type (%d) unknown, "
- "discarded", key->type);
+ wpa_dbg(sm->ctx->msg_ctx, MSG_DEBUG,
+ "WPA: EAPOL-Key type (%d) unknown, discarded",
+ key->type);
ret = 0;
goto out;
}
- wpa_eapol_key_dump(key);
+ wpa_eapol_key_dump(sm, key);
eapol_sm_notify_lower_layer_success(sm->eapol, 0);
wpa_hexdump(MSG_MSGDUMP, "WPA: RX EAPOL-Key", tmp, len);
if (data_len < len) {
- wpa_printf(MSG_DEBUG, "WPA: ignoring %lu bytes after the IEEE "
- "802.1X data", (unsigned long) len - data_len);
+ wpa_dbg(sm->ctx->msg_ctx, MSG_DEBUG,
+ "WPA: ignoring %lu bytes after the IEEE 802.1X data",
+ (unsigned long) len - data_len);
}
key_info = WPA_GET_BE16(key->key_info);
ver = key_info & WPA_KEY_INFO_TYPE_MASK;
@@ -1610,8 +1629,9 @@ int wpa_sm_rx_eapol(struct wpa_sm *sm, const u8 *src_addr,
ver != WPA_KEY_INFO_TYPE_AES_128_CMAC &&
#endif /* CONFIG_IEEE80211R || CONFIG_IEEE80211W */
ver != WPA_KEY_INFO_TYPE_HMAC_SHA1_AES) {
- wpa_printf(MSG_INFO, "WPA: Unsupported EAPOL-Key descriptor "
- "version %d.", ver);
+ wpa_msg(sm->ctx->msg_ctx, MSG_INFO,
+ "WPA: Unsupported EAPOL-Key descriptor version %d",
+ ver);
goto out;
}
@@ -1619,8 +1639,8 @@ int wpa_sm_rx_eapol(struct wpa_sm *sm, const u8 *src_addr,
if (wpa_key_mgmt_ft(sm->key_mgmt)) {
/* IEEE 802.11r uses a new key_info type (AES-128-CMAC). */
if (ver != WPA_KEY_INFO_TYPE_AES_128_CMAC) {
- wpa_printf(MSG_INFO, "FT: AP did not use "
- "AES-128-CMAC.");
+ wpa_msg(sm->ctx->msg_ctx, MSG_INFO,
+ "FT: AP did not use AES-128-CMAC");
goto out;
}
} else
@@ -1628,28 +1648,37 @@ int wpa_sm_rx_eapol(struct wpa_sm *sm, const u8 *src_addr,
#ifdef CONFIG_IEEE80211W
if (wpa_key_mgmt_sha256(sm->key_mgmt)) {
if (ver != WPA_KEY_INFO_TYPE_AES_128_CMAC) {
- wpa_printf(MSG_INFO, "WPA: AP did not use the "
- "negotiated AES-128-CMAC.");
+ wpa_msg(sm->ctx->msg_ctx, MSG_INFO,
+ "WPA: AP did not use the "
+ "negotiated AES-128-CMAC");
goto out;
}
} else
#endif /* CONFIG_IEEE80211W */
if (sm->pairwise_cipher == WPA_CIPHER_CCMP &&
ver != WPA_KEY_INFO_TYPE_HMAC_SHA1_AES) {
- wpa_printf(MSG_INFO, "WPA: CCMP is used, but EAPOL-Key "
- "descriptor version (%d) is not 2.", ver);
+ wpa_msg(sm->ctx->msg_ctx, MSG_INFO,
+ "WPA: CCMP is used, but EAPOL-Key "
+ "descriptor version (%d) is not 2", ver);
if (sm->group_cipher != WPA_CIPHER_CCMP &&
!(key_info & WPA_KEY_INFO_KEY_TYPE)) {
/* Earlier versions of IEEE 802.11i did not explicitly
* require version 2 descriptor for all EAPOL-Key
* packets, so allow group keys to use version 1 if
* CCMP is not used for them. */
- wpa_printf(MSG_INFO, "WPA: Backwards compatibility: "
- "allow invalid version for non-CCMP group "
- "keys");
+ wpa_msg(sm->ctx->msg_ctx, MSG_INFO,
+ "WPA: Backwards compatibility: allow invalid "
+ "version for non-CCMP group keys");
} else
goto out;
}
+ if (sm->pairwise_cipher == WPA_CIPHER_GCMP &&
+ ver != WPA_KEY_INFO_TYPE_HMAC_SHA1_AES) {
+ wpa_msg(sm->ctx->msg_ctx, MSG_INFO,
+ "WPA: GCMP is used, but EAPOL-Key "
+ "descriptor version (%d) is not 2", ver);
+ goto out;
+ }
#ifdef CONFIG_PEERKEY
for (peerkey = sm->peerkey; peerkey; peerkey = peerkey->next) {
@@ -1661,9 +1690,9 @@ int wpa_sm_rx_eapol(struct wpa_sm *sm, const u8 *src_addr,
if (!peerkey->initiator && peerkey->replay_counter_set &&
os_memcmp(key->replay_counter, peerkey->replay_counter,
WPA_REPLAY_COUNTER_LEN) <= 0) {
- wpa_printf(MSG_WARNING, "RSN: EAPOL-Key Replay "
- "Counter did not increase (STK) - dropping "
- "packet");
+ wpa_msg(sm->ctx->msg_ctx, MSG_WARNING,
+ "RSN: EAPOL-Key Replay Counter did not "
+ "increase (STK) - dropping packet");
goto out;
} else if (peerkey->initiator) {
u8 _tmp[WPA_REPLAY_COUNTER_LEN];
@@ -1672,16 +1701,18 @@ int wpa_sm_rx_eapol(struct wpa_sm *sm, const u8 *src_addr,
inc_byte_array(_tmp, WPA_REPLAY_COUNTER_LEN);
if (os_memcmp(_tmp, peerkey->replay_counter,
WPA_REPLAY_COUNTER_LEN) != 0) {
- wpa_printf(MSG_DEBUG, "RSN: EAPOL-Key Replay "
- "Counter did not match (STK) - "
- "dropping packet");
+ wpa_dbg(sm->ctx->msg_ctx, MSG_DEBUG,
+ "RSN: EAPOL-Key Replay "
+ "Counter did not match (STK) - "
+ "dropping packet");
goto out;
}
}
}
if (peerkey && peerkey->initiator && (key_info & WPA_KEY_INFO_ACK)) {
- wpa_printf(MSG_INFO, "RSN: Ack bit in key_info from STK peer");
+ wpa_msg(sm->ctx->msg_ctx, MSG_INFO,
+ "RSN: Ack bit in key_info from STK peer");
goto out;
}
#endif /* CONFIG_PEERKEY */
@@ -1689,8 +1720,9 @@ int wpa_sm_rx_eapol(struct wpa_sm *sm, const u8 *src_addr,
if (!peerkey && sm->rx_replay_counter_set &&
os_memcmp(key->replay_counter, sm->rx_replay_counter,
WPA_REPLAY_COUNTER_LEN) <= 0) {
- wpa_printf(MSG_WARNING, "WPA: EAPOL-Key Replay Counter did not"
- " increase - dropping packet");
+ wpa_msg(sm->ctx->msg_ctx, MSG_WARNING,
+ "WPA: EAPOL-Key Replay Counter did not increase - "
+ "dropping packet");
goto out;
}
@@ -1699,13 +1731,14 @@ int wpa_sm_rx_eapol(struct wpa_sm *sm, const u8 *src_addr,
&& (peerkey == NULL || !peerkey->initiator)
#endif /* CONFIG_PEERKEY */
) {
- wpa_printf(MSG_INFO, "WPA: No Ack bit in key_info");
+ wpa_msg(sm->ctx->msg_ctx, MSG_INFO,
+ "WPA: No Ack bit in key_info");
goto out;
}
if (key_info & WPA_KEY_INFO_REQUEST) {
- wpa_printf(MSG_INFO, "WPA: EAPOL-Key with Request bit - "
- "dropped");
+ wpa_msg(sm->ctx->msg_ctx, MSG_INFO,
+ "WPA: EAPOL-Key with Request bit - dropped");
goto out;
}
@@ -1739,8 +1772,9 @@ int wpa_sm_rx_eapol(struct wpa_sm *sm, const u8 *src_addr,
if (key_info & WPA_KEY_INFO_KEY_TYPE) {
if (key_info & WPA_KEY_INFO_KEY_INDEX_MASK) {
- wpa_printf(MSG_WARNING, "WPA: Ignored EAPOL-Key "
- "(Pairwise) with non-zero key index");
+ wpa_msg(sm->ctx->msg_ctx, MSG_WARNING,
+ "WPA: Ignored EAPOL-Key (Pairwise) with "
+ "non-zero key index");
goto out;
}
if (peerkey) {
@@ -1764,8 +1798,9 @@ int wpa_sm_rx_eapol(struct wpa_sm *sm, const u8 *src_addr,
wpa_supplicant_process_1_of_2(sm, src_addr, key,
extra_len, ver);
} else {
- wpa_printf(MSG_WARNING, "WPA: EAPOL-Key (Group) "
- "without Mic bit - dropped");
+ wpa_msg(sm->ctx->msg_ctx, MSG_WARNING,
+ "WPA: EAPOL-Key (Group) without Mic bit - "
+ "dropped");
}
}
@@ -1778,23 +1813,6 @@ out:
#ifdef CONFIG_CTRL_IFACE
-static int wpa_cipher_bits(int cipher)
-{
- switch (cipher) {
- case WPA_CIPHER_CCMP:
- return 128;
- case WPA_CIPHER_TKIP:
- return 256;
- case WPA_CIPHER_WEP104:
- return 104;
- case WPA_CIPHER_WEP40:
- return 40;
- default:
- return 0;
- }
-}
-
-
static u32 wpa_key_mgmt_suite(struct wpa_sm *sm)
{
switch (sm->key_mgmt) {
@@ -1818,6 +1836,10 @@ static u32 wpa_key_mgmt_suite(struct wpa_sm *sm)
case WPA_KEY_MGMT_PSK_SHA256:
return RSN_AUTH_KEY_MGMT_PSK_SHA256;
#endif /* CONFIG_IEEE80211W */
+ case WPA_KEY_MGMT_CCKM:
+ return (sm->proto == WPA_PROTO_RSN ?
+ RSN_AUTH_KEY_MGMT_CCKM:
+ WPA_AUTH_KEY_MGMT_CCKM);
case WPA_KEY_MGMT_WPA_NONE:
return WPA_AUTH_KEY_MGMT_NONE;
default:
@@ -1826,30 +1848,6 @@ static u32 wpa_key_mgmt_suite(struct wpa_sm *sm)
}
-static u32 wpa_cipher_suite(struct wpa_sm *sm, int cipher)
-{
- switch (cipher) {
- case WPA_CIPHER_CCMP:
- return (sm->proto == WPA_PROTO_RSN ?
- RSN_CIPHER_SUITE_CCMP : WPA_CIPHER_SUITE_CCMP);
- case WPA_CIPHER_TKIP:
- return (sm->proto == WPA_PROTO_RSN ?
- RSN_CIPHER_SUITE_TKIP : WPA_CIPHER_SUITE_TKIP);
- case WPA_CIPHER_WEP104:
- return (sm->proto == WPA_PROTO_RSN ?
- RSN_CIPHER_SUITE_WEP104 : WPA_CIPHER_SUITE_WEP104);
- case WPA_CIPHER_WEP40:
- return (sm->proto == WPA_PROTO_RSN ?
- RSN_CIPHER_SUITE_WEP40 : WPA_CIPHER_SUITE_WEP40);
- case WPA_CIPHER_NONE:
- return (sm->proto == WPA_PROTO_RSN ?
- RSN_CIPHER_SUITE_NONE : WPA_CIPHER_SUITE_NONE);
- default:
- return 0;
- }
-}
-
-
#define RSN_SUITE "%02x-%02x-%02x-%d"
#define RSN_SUITE_ARG(s) \
((s) >> 24) & 0xff, ((s) >> 16) & 0xff, ((s) >> 8) & 0xff, (s) & 0xff
@@ -1897,7 +1895,7 @@ int wpa_sm_get_mib(struct wpa_sm *sm, char *buf, size_t buflen)
rsna ? "TRUE" : "FALSE",
rsna ? "TRUE" : "FALSE",
RSN_VERSION,
- wpa_cipher_bits(sm->group_cipher),
+ wpa_cipher_key_len(sm->group_cipher) * 8,
sm->dot11RSNAConfigPMKLifetime,
sm->dot11RSNAConfigPMKReauthThreshold,
sm->dot11RSNAConfigSATimeout);
@@ -1917,12 +1915,16 @@ int wpa_sm_get_mib(struct wpa_sm *sm, char *buf, size_t buflen)
"dot11RSNAConfigNumberOfGTKSAReplayCounters=0\n"
"dot11RSNA4WayHandshakeFailures=%u\n",
RSN_SUITE_ARG(wpa_key_mgmt_suite(sm)),
- RSN_SUITE_ARG(wpa_cipher_suite(sm, sm->pairwise_cipher)),
- RSN_SUITE_ARG(wpa_cipher_suite(sm, sm->group_cipher)),
+ RSN_SUITE_ARG(wpa_cipher_to_suite(sm->proto,
+ sm->pairwise_cipher)),
+ RSN_SUITE_ARG(wpa_cipher_to_suite(sm->proto,
+ sm->group_cipher)),
pmkid_txt,
RSN_SUITE_ARG(wpa_key_mgmt_suite(sm)),
- RSN_SUITE_ARG(wpa_cipher_suite(sm, sm->pairwise_cipher)),
- RSN_SUITE_ARG(wpa_cipher_suite(sm, sm->group_cipher)),
+ RSN_SUITE_ARG(wpa_cipher_to_suite(sm->proto,
+ sm->pairwise_cipher)),
+ RSN_SUITE_ARG(wpa_cipher_to_suite(sm->proto,
+ sm->group_cipher)),
sm->dot11RSNA4WayHandshakeFailures);
if (ret >= 0 && (size_t) ret < buflen)
len += ret;
@@ -1933,24 +1935,40 @@ int wpa_sm_get_mib(struct wpa_sm *sm, char *buf, size_t buflen)
static void wpa_sm_pmksa_free_cb(struct rsn_pmksa_cache_entry *entry,
- void *ctx, int replace)
+ void *ctx, enum pmksa_free_reason reason)
{
struct wpa_sm *sm = ctx;
+ int deauth = 0;
+
+ wpa_dbg(sm->ctx->msg_ctx, MSG_DEBUG, "RSN: PMKSA cache entry free_cb: "
+ MACSTR " reason=%d", MAC2STR(entry->aa), reason);
+
+ if (sm->cur_pmksa == entry) {
+ wpa_dbg(sm->ctx->msg_ctx, MSG_DEBUG,
+ "RSN: %s current PMKSA entry",
+ reason == PMKSA_REPLACE ? "replaced" : "removed");
+ pmksa_cache_clear_current(sm);
+
+ /*
+ * If an entry is simply being replaced, there's no need to
+ * deauthenticate because it will be immediately re-added.
+ * This happens when EAP authentication is completed again
+ * (reauth or failed PMKSA caching attempt).
+ */
+ if (reason != PMKSA_REPLACE)
+ deauth = 1;
+ }
- if (sm->cur_pmksa == entry ||
+ if (reason == PMKSA_EXPIRE &&
(sm->pmk_len == entry->pmk_len &&
os_memcmp(sm->pmk, entry->pmk, sm->pmk_len) == 0)) {
- wpa_printf(MSG_DEBUG, "RSN: removed current PMKSA entry");
- sm->cur_pmksa = NULL;
-
- if (replace) {
- /* A new entry is being added, so no need to
- * deauthenticate in this case. This happens when EAP
- * authentication is completed again (reauth or failed
- * PMKSA caching attempt). */
- return;
- }
+ wpa_dbg(sm->ctx->msg_ctx, MSG_DEBUG,
+ "RSN: deauthenticating due to expired PMK");
+ pmksa_cache_clear_current(sm);
+ deauth = 1;
+ }
+ if (deauth) {
os_memset(sm->pmk, 0, sizeof(sm->pmk));
wpa_sm_deauthenticate(sm, WLAN_REASON_UNSPECIFIED);
}
@@ -1982,8 +2000,8 @@ struct wpa_sm * wpa_sm_init(struct wpa_sm_ctx *ctx)
sm->pmksa = pmksa_cache_init(wpa_sm_pmksa_free_cb, sm, sm);
if (sm->pmksa == NULL) {
- wpa_printf(MSG_ERROR, "RSN: PMKSA cache initialization "
- "failed");
+ wpa_msg(sm->ctx->msg_ctx, MSG_ERROR,
+ "RSN: PMKSA cache initialization failed");
os_free(sm);
return NULL;
}
@@ -2030,7 +2048,8 @@ void wpa_sm_notify_assoc(struct wpa_sm *sm, const u8 *bssid)
if (sm == NULL)
return;
- wpa_printf(MSG_DEBUG, "WPA: Association event - clear replay counter");
+ wpa_dbg(sm->ctx->msg_ctx, MSG_DEBUG,
+ "WPA: Association event - clear replay counter");
os_memcpy(sm->bssid, bssid, ETH_ALEN);
os_memset(sm->rx_replay_counter, 0, WPA_REPLAY_COUNTER_LEN);
sm->rx_replay_counter_set = 0;
@@ -2059,10 +2078,14 @@ void wpa_sm_notify_assoc(struct wpa_sm *sm, const u8 *bssid)
* IEEE 802.11, 8.4.10: Delete PTK SA on (re)association if
* this is not part of a Fast BSS Transition.
*/
- wpa_printf(MSG_DEBUG, "WPA: Clear old PTK");
+ wpa_dbg(sm->ctx->msg_ctx, MSG_DEBUG, "WPA: Clear old PTK");
sm->ptk_set = 0;
sm->tptk_set = 0;
}
+
+#ifdef CONFIG_TDLS
+ wpa_tdls_assoc(sm);
+#endif /* CONFIG_TDLS */
}
@@ -2076,8 +2099,12 @@ void wpa_sm_notify_assoc(struct wpa_sm *sm, const u8 *bssid)
void wpa_sm_notify_disassoc(struct wpa_sm *sm)
{
rsn_preauth_deinit(sm);
+ pmksa_cache_clear_current(sm);
if (wpa_sm_get_state(sm) == WPA_4WAY_HANDSHAKE)
sm->dot11RSNA4WayHandshakeFailures++;
+#ifdef CONFIG_TDLS
+ wpa_tdls_disassoc(sm);
+#endif /* CONFIG_TDLS */
}
@@ -2191,8 +2218,6 @@ void wpa_sm_set_config(struct wpa_sm *sm, struct rsn_supp_config *config)
sm->ssid_len = 0;
sm->wpa_ptk_rekey = 0;
}
- if (config == NULL || config->network_ctx != sm->network_ctx)
- pmksa_cache_notify_reconfig(sm->pmksa);
}
@@ -2367,6 +2392,22 @@ int wpa_sm_get_status(struct wpa_sm *sm, char *buf, size_t buflen,
if (ret < 0 || ret >= end - pos)
return pos - buf;
pos += ret;
+
+ if (sm->mfp != NO_MGMT_FRAME_PROTECTION && sm->ap_rsn_ie) {
+ struct wpa_ie_data rsn;
+ if (wpa_parse_wpa_ie_rsn(sm->ap_rsn_ie, sm->ap_rsn_ie_len, &rsn)
+ >= 0 &&
+ rsn.capabilities & (WPA_CAPABILITY_MFPR |
+ WPA_CAPABILITY_MFPC)) {
+ ret = os_snprintf(pos, end - pos, "pmf=%d\n",
+ (rsn.capabilities &
+ WPA_CAPABILITY_MFPR) ? 2 : 1);
+ if (ret < 0 || ret >= end - pos)
+ return pos - buf;
+ pos += ret;
+ }
+ }
+
return pos - buf;
}
@@ -2430,7 +2471,8 @@ int wpa_sm_set_assoc_wpa_ie(struct wpa_sm *sm, const u8 *ie, size_t len)
os_free(sm->assoc_wpa_ie);
if (ie == NULL || len == 0) {
- wpa_printf(MSG_DEBUG, "WPA: clearing own WPA/RSN IE");
+ wpa_dbg(sm->ctx->msg_ctx, MSG_DEBUG,
+ "WPA: clearing own WPA/RSN IE");
sm->assoc_wpa_ie = NULL;
sm->assoc_wpa_ie_len = 0;
} else {
@@ -2464,7 +2506,8 @@ int wpa_sm_set_ap_wpa_ie(struct wpa_sm *sm, const u8 *ie, size_t len)
os_free(sm->ap_wpa_ie);
if (ie == NULL || len == 0) {
- wpa_printf(MSG_DEBUG, "WPA: clearing AP WPA IE");
+ wpa_dbg(sm->ctx->msg_ctx, MSG_DEBUG,
+ "WPA: clearing AP WPA IE");
sm->ap_wpa_ie = NULL;
sm->ap_wpa_ie_len = 0;
} else {
@@ -2498,7 +2541,8 @@ int wpa_sm_set_ap_rsn_ie(struct wpa_sm *sm, const u8 *ie, size_t len)
os_free(sm->ap_rsn_ie);
if (ie == NULL || len == 0) {
- wpa_printf(MSG_DEBUG, "WPA: clearing AP RSN IE");
+ wpa_dbg(sm->ctx->msg_ctx, MSG_DEBUG,
+ "WPA: clearing AP RSN IE");
sm->ap_rsn_ie = NULL;
sm->ap_rsn_ie_len = 0;
} else {
@@ -2526,9 +2570,12 @@ int wpa_sm_set_ap_rsn_ie(struct wpa_sm *sm, const u8 *ie, size_t len)
*/
int wpa_sm_parse_own_wpa_ie(struct wpa_sm *sm, struct wpa_ie_data *data)
{
- if (sm == NULL || sm->assoc_wpa_ie == NULL) {
- wpa_printf(MSG_DEBUG, "WPA: No WPA/RSN IE available from "
- "association info");
+ if (sm == NULL)
+ return -1;
+
+ if (sm->assoc_wpa_ie == NULL) {
+ wpa_dbg(sm->ctx->msg_ctx, MSG_DEBUG,
+ "WPA: No WPA/RSN IE available from association info");
return -1;
}
if (wpa_parse_wpa_ie(sm->assoc_wpa_ie, sm->assoc_wpa_ie_len, data))
@@ -2549,7 +2596,7 @@ int wpa_sm_pmksa_cache_list(struct wpa_sm *sm, char *buf, size_t len)
void wpa_sm_drop_sa(struct wpa_sm *sm)
{
- wpa_printf(MSG_DEBUG, "WPA: Clear old PMK and PTK");
+ wpa_dbg(sm->ctx->msg_ctx, MSG_DEBUG, "WPA: Clear old PMK and PTK");
sm->ptk_set = 0;
sm->tptk_set = 0;
os_memset(sm->pmk, 0, sizeof(sm->pmk));
@@ -2564,3 +2611,92 @@ int wpa_sm_has_ptk(struct wpa_sm *sm)
return 0;
return sm->ptk_set;
}
+
+
+void wpa_sm_update_replay_ctr(struct wpa_sm *sm, const u8 *replay_ctr)
+{
+ os_memcpy(sm->rx_replay_counter, replay_ctr, WPA_REPLAY_COUNTER_LEN);
+}
+
+
+void wpa_sm_pmksa_cache_flush(struct wpa_sm *sm, void *network_ctx)
+{
+#ifndef CONFIG_NO_WPA2
+ pmksa_cache_flush(sm->pmksa, network_ctx);
+#endif /* CONFIG_NO_WPA2 */
+}
+
+
+#ifdef CONFIG_WNM
+int wpa_wnmsleep_install_key(struct wpa_sm *sm, u8 subelem_id, u8 *buf)
+{
+ struct wpa_gtk_data gd;
+#ifdef CONFIG_IEEE80211W
+ struct wpa_igtk_kde igd;
+ u16 keyidx;
+#endif /* CONFIG_IEEE80211W */
+ u16 keyinfo;
+ u8 keylen; /* plaintext key len */
+ u8 *key_rsc;
+
+ os_memset(&gd, 0, sizeof(gd));
+#ifdef CONFIG_IEEE80211W
+ os_memset(&igd, 0, sizeof(igd));
+#endif /* CONFIG_IEEE80211W */
+
+ keylen = wpa_cipher_key_len(sm->group_cipher);
+ gd.key_rsc_len = wpa_cipher_rsc_len(sm->group_cipher);
+ gd.alg = wpa_cipher_to_alg(sm->group_cipher);
+ if (gd.alg == WPA_ALG_NONE) {
+ wpa_printf(MSG_DEBUG, "Unsupported group cipher suite");
+ return -1;
+ }
+
+ if (subelem_id == WNM_SLEEP_SUBELEM_GTK) {
+ key_rsc = buf + 5;
+ keyinfo = WPA_GET_LE16(buf + 2);
+ gd.gtk_len = keylen;
+ if (gd.gtk_len != buf[4]) {
+ wpa_printf(MSG_DEBUG, "GTK len mismatch len %d vs %d",
+ gd.gtk_len, buf[4]);
+ return -1;
+ }
+ gd.keyidx = keyinfo & 0x03; /* B0 - B1 */
+ gd.tx = wpa_supplicant_gtk_tx_bit_workaround(
+ sm, !!(keyinfo & WPA_KEY_INFO_TXRX));
+
+ os_memcpy(gd.gtk, buf + 13, gd.gtk_len);
+
+ wpa_hexdump_key(MSG_DEBUG, "Install GTK (WNM SLEEP)",
+ gd.gtk, gd.gtk_len);
+ if (wpa_supplicant_install_gtk(sm, &gd, key_rsc)) {
+ wpa_printf(MSG_DEBUG, "Failed to install the GTK in "
+ "WNM mode");
+ return -1;
+ }
+#ifdef CONFIG_IEEE80211W
+ } else if (subelem_id == WNM_SLEEP_SUBELEM_IGTK) {
+ os_memcpy(igd.keyid, buf + 2, 2);
+ os_memcpy(igd.pn, buf + 4, 6);
+
+ keyidx = WPA_GET_LE16(igd.keyid);
+ os_memcpy(igd.igtk, buf + 10, WPA_IGTK_LEN);
+
+ wpa_hexdump_key(MSG_DEBUG, "Install IGTK (WNM SLEEP)",
+ igd.igtk, WPA_IGTK_LEN);
+ if (wpa_sm_set_key(sm, WPA_ALG_IGTK, broadcast_ether_addr,
+ keyidx, 0, igd.pn, sizeof(igd.pn),
+ igd.igtk, WPA_IGTK_LEN) < 0) {
+ wpa_printf(MSG_DEBUG, "Failed to install the IGTK in "
+ "WNM mode");
+ return -1;
+ }
+#endif /* CONFIG_IEEE80211W */
+ } else {
+ wpa_printf(MSG_DEBUG, "Unknown element id");
+ return -1;
+ }
+
+ return 0;
+}
+#endif /* CONFIG_WNM */
diff --git a/contrib/wpa/src/rsn_supp/wpa.h b/contrib/wpa/src/rsn_supp/wpa.h
index f1a5554..791974c 100644
--- a/contrib/wpa/src/rsn_supp/wpa.h
+++ b/contrib/wpa/src/rsn_supp/wpa.h
@@ -2,14 +2,8 @@
* wpa_supplicant - WPA definitions
* Copyright (c) 2003-2007, Jouni Malinen <j@w1.fi>
*
- * This program is free software; you can redistribute it and/or modify
- * it under the terms of the GNU General Public License version 2 as
- * published by the Free Software Foundation.
- *
- * Alternatively, this software may be distributed under the terms of BSD
- * license.
- *
- * See README and COPYING for more details.
+ * This software may be distributed under the terms of the BSD license.
+ * See README for more details.
*/
#ifndef WPA_H
@@ -30,7 +24,6 @@ struct wpa_sm_ctx {
void (*set_state)(void *ctx, enum wpa_states state);
enum wpa_states (*get_state)(void *ctx);
void (*deauthenticate)(void * ctx, int reason_code);
- void (*disassociate)(void *ctx, int reason_code);
int (*set_key)(void *ctx, enum wpa_alg alg,
const u8 *addr, int key_idx, int set_tx,
const u8 *seq, size_t seq_len,
@@ -55,6 +48,19 @@ struct wpa_sm_ctx {
int (*send_ft_action)(void *ctx, u8 action, const u8 *target_ap,
const u8 *ies, size_t ies_len);
int (*mark_authenticated)(void *ctx, const u8 *target_ap);
+#ifdef CONFIG_TDLS
+ int (*tdls_get_capa)(void *ctx, int *tdls_supported,
+ int *tdls_ext_setup);
+ int (*send_tdls_mgmt)(void *ctx, const u8 *dst,
+ u8 action_code, u8 dialog_token,
+ u16 status_code, const u8 *buf, size_t len);
+ int (*tdls_oper)(void *ctx, int oper, const u8 *peer);
+ int (*tdls_peer_addset)(void *ctx, const u8 *addr, int add,
+ u16 capability, const u8 *supp_rates,
+ size_t supp_rates_len);
+#endif /* CONFIG_TDLS */
+ void (*set_rekey_offload)(void *ctx, const u8 *kek, const u8 *kck,
+ const u8 *replay_ctr);
};
@@ -126,6 +132,10 @@ int wpa_sm_pmksa_cache_list(struct wpa_sm *sm, char *buf, size_t len);
void wpa_sm_drop_sa(struct wpa_sm *sm);
int wpa_sm_has_ptk(struct wpa_sm *sm);
+void wpa_sm_update_replay_ctr(struct wpa_sm *sm, const u8 *replay_ctr);
+
+void wpa_sm_pmksa_cache_flush(struct wpa_sm *sm, void *network_ctx);
+
#else /* CONFIG_NO_WPA */
static inline struct wpa_sm * wpa_sm_init(struct wpa_sm_ctx *ctx)
@@ -271,6 +281,16 @@ static inline int wpa_sm_has_ptk(struct wpa_sm *sm)
return 0;
}
+static inline void wpa_sm_update_replay_ctr(struct wpa_sm *sm,
+ const u8 *replay_ctr)
+{
+}
+
+static inline void wpa_sm_pmksa_cache_flush(struct wpa_sm *sm,
+ void *network_ctx)
+{
+}
+
#endif /* CONFIG_NO_WPA */
#ifdef CONFIG_PEERKEY
@@ -330,4 +350,21 @@ wpa_ft_validate_reassoc_resp(struct wpa_sm *sm, const u8 *ies, size_t ies_len,
#endif /* CONFIG_IEEE80211R */
+
+/* tdls.c */
+void wpa_tdls_ap_ies(struct wpa_sm *sm, const u8 *ies, size_t len);
+void wpa_tdls_assoc_resp_ies(struct wpa_sm *sm, const u8 *ies, size_t len);
+int wpa_tdls_start(struct wpa_sm *sm, const u8 *addr);
+int wpa_tdls_reneg(struct wpa_sm *sm, const u8 *addr);
+int wpa_tdls_send_teardown(struct wpa_sm *sm, const u8 *addr, u16 reason_code);
+int wpa_tdls_teardown_link(struct wpa_sm *sm, const u8 *addr, u16 reason_code);
+int wpa_tdls_send_discovery_request(struct wpa_sm *sm, const u8 *addr);
+int wpa_tdls_init(struct wpa_sm *sm);
+void wpa_tdls_deinit(struct wpa_sm *sm);
+void wpa_tdls_enable(struct wpa_sm *sm, int enabled);
+void wpa_tdls_disable_link(struct wpa_sm *sm, const u8 *addr);
+int wpa_tdls_is_external_setup(struct wpa_sm *sm);
+
+int wpa_wnmsleep_install_key(struct wpa_sm *sm, u8 subelem_id, u8 *buf);
+
#endif /* WPA_H */
diff --git a/contrib/wpa/src/rsn_supp/wpa_ft.c b/contrib/wpa/src/rsn_supp/wpa_ft.c
index 23063bc..2df060c 100644
--- a/contrib/wpa/src/rsn_supp/wpa_ft.c
+++ b/contrib/wpa/src/rsn_supp/wpa_ft.c
@@ -2,53 +2,22 @@
* WPA Supplicant - IEEE 802.11r - Fast BSS Transition
* Copyright (c) 2006-2007, Jouni Malinen <j@w1.fi>
*
- * This program is free software; you can redistribute it and/or modify
- * it under the terms of the GNU General Public License version 2 as
- * published by the Free Software Foundation.
- *
- * Alternatively, this software may be distributed under the terms of BSD
- * license.
- *
- * See README and COPYING for more details.
+ * This software may be distributed under the terms of the BSD license.
+ * See README for more details.
*/
#include "includes.h"
#include "common.h"
#include "crypto/aes_wrap.h"
+#include "crypto/random.h"
#include "common/ieee802_11_defs.h"
#include "common/ieee802_11_common.h"
#include "wpa.h"
#include "wpa_i.h"
-#include "wpa_ie.h"
#ifdef CONFIG_IEEE80211R
-struct wpa_ft_ies {
- const u8 *mdie;
- size_t mdie_len;
- const u8 *ftie;
- size_t ftie_len;
- const u8 *r1kh_id;
- const u8 *gtk;
- size_t gtk_len;
- const u8 *r0kh_id;
- size_t r0kh_id_len;
- const u8 *rsn;
- size_t rsn_len;
- const u8 *rsn_pmkid;
- const u8 *tie;
- size_t tie_len;
- const u8 *igtk;
- size_t igtk_len;
- const u8 *ric;
- size_t ric_len;
-};
-
-static int wpa_ft_parse_ies(const u8 *ies, size_t ies_len,
- struct wpa_ft_ies *parse);
-
-
int wpa_derive_ptk_ft(struct wpa_sm *sm, const unsigned char *src_addr,
const struct wpa_eapol_key *key,
struct wpa_ptk *ptk, size_t ptk_len)
@@ -202,16 +171,16 @@ static u8 * wpa_ft_gen_req_ies(struct wpa_sm *sm, size_t *len,
pos = (u8 *) (rsnie + 1);
/* Group Suite Selector */
- if (sm->group_cipher == WPA_CIPHER_CCMP)
- RSN_SELECTOR_PUT(pos, RSN_CIPHER_SUITE_CCMP);
- else if (sm->group_cipher == WPA_CIPHER_TKIP)
- RSN_SELECTOR_PUT(pos, RSN_CIPHER_SUITE_TKIP);
- else {
+ if (sm->group_cipher != WPA_CIPHER_CCMP &&
+ sm->group_cipher != WPA_CIPHER_GCMP &&
+ sm->group_cipher != WPA_CIPHER_TKIP) {
wpa_printf(MSG_WARNING, "FT: Invalid group cipher (%d)",
sm->group_cipher);
os_free(buf);
return NULL;
}
+ RSN_SELECTOR_PUT(pos, wpa_cipher_to_suite(WPA_PROTO_RSN,
+ sm->group_cipher));
pos += RSN_SELECTOR_LEN;
/* Pairwise Suite Count */
@@ -219,16 +188,14 @@ static u8 * wpa_ft_gen_req_ies(struct wpa_sm *sm, size_t *len,
pos += 2;
/* Pairwise Suite List */
- if (sm->pairwise_cipher == WPA_CIPHER_CCMP)
- RSN_SELECTOR_PUT(pos, RSN_CIPHER_SUITE_CCMP);
- else if (sm->pairwise_cipher == WPA_CIPHER_TKIP)
- RSN_SELECTOR_PUT(pos, RSN_CIPHER_SUITE_TKIP);
- else {
+ if (!wpa_cipher_valid_pairwise(sm->pairwise_cipher)) {
wpa_printf(MSG_WARNING, "FT: Invalid pairwise cipher (%d)",
sm->pairwise_cipher);
os_free(buf);
return NULL;
}
+ RSN_SELECTOR_PUT(pos, wpa_cipher_to_suite(WPA_PROTO_RSN,
+ sm->pairwise_cipher));
pos += RSN_SELECTOR_LEN;
/* Authenticated Key Management Suite Count */
@@ -346,155 +313,6 @@ static u8 * wpa_ft_gen_req_ies(struct wpa_sm *sm, size_t *len,
}
-static int wpa_ft_parse_ftie(const u8 *ie, size_t ie_len,
- struct wpa_ft_ies *parse)
-{
- const u8 *end, *pos;
-
- parse->ftie = ie;
- parse->ftie_len = ie_len;
-
- pos = ie + sizeof(struct rsn_ftie);
- end = ie + ie_len;
-
- while (pos + 2 <= end && pos + 2 + pos[1] <= end) {
- switch (pos[0]) {
- case FTIE_SUBELEM_R1KH_ID:
- if (pos[1] != FT_R1KH_ID_LEN) {
- wpa_printf(MSG_DEBUG, "FT: Invalid R1KH-ID "
- "length in FTIE: %d", pos[1]);
- return -1;
- }
- parse->r1kh_id = pos + 2;
- break;
- case FTIE_SUBELEM_GTK:
- parse->gtk = pos + 2;
- parse->gtk_len = pos[1];
- break;
- case FTIE_SUBELEM_R0KH_ID:
- if (pos[1] < 1 || pos[1] > FT_R0KH_ID_MAX_LEN) {
- wpa_printf(MSG_DEBUG, "FT: Invalid R0KH-ID "
- "length in FTIE: %d", pos[1]);
- return -1;
- }
- parse->r0kh_id = pos + 2;
- parse->r0kh_id_len = pos[1];
- break;
-#ifdef CONFIG_IEEE80211W
- case FTIE_SUBELEM_IGTK:
- parse->igtk = pos + 2;
- parse->igtk_len = pos[1];
- break;
-#endif /* CONFIG_IEEE80211W */
- }
-
- pos += 2 + pos[1];
- }
-
- return 0;
-}
-
-
-static int wpa_ft_parse_ies(const u8 *ies, size_t ies_len,
- struct wpa_ft_ies *parse)
-{
- const u8 *end, *pos;
- struct wpa_ie_data data;
- int ret;
- const struct rsn_ftie *ftie;
- int prot_ie_count = 0;
-
- os_memset(parse, 0, sizeof(*parse));
- if (ies == NULL)
- return 0;
-
- pos = ies;
- end = ies + ies_len;
- while (pos + 2 <= end && pos + 2 + pos[1] <= end) {
- switch (pos[0]) {
- case WLAN_EID_RSN:
- parse->rsn = pos + 2;
- parse->rsn_len = pos[1];
- ret = wpa_parse_wpa_ie_rsn(parse->rsn - 2,
- parse->rsn_len + 2,
- &data);
- if (ret < 0) {
- wpa_printf(MSG_DEBUG, "FT: Failed to parse "
- "RSN IE: %d", ret);
- return -1;
- }
- if (data.num_pmkid == 1 && data.pmkid)
- parse->rsn_pmkid = data.pmkid;
- break;
- case WLAN_EID_MOBILITY_DOMAIN:
- parse->mdie = pos + 2;
- parse->mdie_len = pos[1];
- break;
- case WLAN_EID_FAST_BSS_TRANSITION:
- if (pos[1] < sizeof(*ftie))
- return -1;
- ftie = (const struct rsn_ftie *) (pos + 2);
- prot_ie_count = ftie->mic_control[1];
- if (wpa_ft_parse_ftie(pos + 2, pos[1], parse) < 0)
- return -1;
- break;
- case WLAN_EID_TIMEOUT_INTERVAL:
- parse->tie = pos + 2;
- parse->tie_len = pos[1];
- break;
- case WLAN_EID_RIC_DATA:
- if (parse->ric == NULL)
- parse->ric = pos;
- }
-
- pos += 2 + pos[1];
- }
-
- if (prot_ie_count == 0)
- return 0; /* no MIC */
-
- /*
- * Check that the protected IE count matches with IEs included in the
- * frame.
- */
- if (parse->rsn)
- prot_ie_count--;
- if (parse->mdie)
- prot_ie_count--;
- if (parse->ftie)
- prot_ie_count--;
- if (parse->tie)
- prot_ie_count--;
- if (prot_ie_count < 0) {
- wpa_printf(MSG_DEBUG, "FT: Some required IEs not included in "
- "the protected IE count");
- return -1;
- }
-
- if (prot_ie_count == 0 && parse->ric) {
- wpa_printf(MSG_DEBUG, "FT: RIC IE(s) in the frame, but not "
- "included in protected IE count");
- return -1;
- }
-
- /* Determine the end of the RIC IE(s) */
- pos = parse->ric;
- while (pos && pos + 2 <= end && pos + 2 + pos[1] <= end &&
- prot_ie_count) {
- prot_ie_count--;
- pos += 2 + pos[1];
- }
- parse->ric_len = pos - parse->ric;
- if (prot_ie_count) {
- wpa_printf(MSG_DEBUG, "FT: %d protected IEs missing from "
- "frame", (int) prot_ie_count);
- return -1;
- }
-
- return 0;
-}
-
-
static int wpa_ft_install_ptk(struct wpa_sm *sm, const u8 *bssid)
{
int keylen;
@@ -503,21 +321,15 @@ static int wpa_ft_install_ptk(struct wpa_sm *sm, const u8 *bssid)
wpa_printf(MSG_DEBUG, "FT: Installing PTK to the driver.");
- switch (sm->pairwise_cipher) {
- case WPA_CIPHER_CCMP:
- alg = WPA_ALG_CCMP;
- keylen = 16;
- break;
- case WPA_CIPHER_TKIP:
- alg = WPA_ALG_TKIP;
- keylen = 32;
- break;
- default:
+ if (!wpa_cipher_valid_pairwise(sm->pairwise_cipher)) {
wpa_printf(MSG_WARNING, "FT: Unsupported pairwise cipher %d",
sm->pairwise_cipher);
return -1;
}
+ alg = wpa_cipher_to_alg(sm->pairwise_cipher);
+ keylen = wpa_cipher_key_len(sm->pairwise_cipher);
+
if (wpa_sm_set_key(sm, alg, bssid, 0, 1, null_rsc,
sizeof(null_rsc), (u8 *) sm->ptk.tk1, keylen) < 0) {
wpa_printf(MSG_WARNING, "FT: Failed to set PTK to the driver");
@@ -540,7 +352,7 @@ int wpa_ft_prepare_auth_request(struct wpa_sm *sm, const u8 *mdie)
size_t ft_ies_len;
/* Generate a new SNonce */
- if (os_get_random(sm->snonce, WPA_NONCE_LEN)) {
+ if (random_get_bytes(sm->snonce, WPA_NONCE_LEN)) {
wpa_printf(MSG_INFO, "FT: Failed to generate a new SNonce");
return -1;
}
@@ -663,7 +475,7 @@ int wpa_ft_process_response(struct wpa_sm *sm, const u8 *ies, size_t ies_len,
sm->pmk_r1_name, WPA_PMK_NAME_LEN);
bssid = target_ap;
- ptk_len = sm->pairwise_cipher == WPA_CIPHER_CCMP ? 48 : 64;
+ ptk_len = sm->pairwise_cipher != WPA_CIPHER_TKIP ? 48 : 64;
wpa_pmk_r1_to_ptk(sm->pmk_r1, sm->snonce, ftie->anonce, sm->own_addr,
bssid, sm->pmk_r1_name,
(u8 *) &sm->ptk, ptk_len, ptk_name);
@@ -751,28 +563,10 @@ static int wpa_ft_process_gtk_subelem(struct wpa_sm *sm, const u8 *gtk_elem,
return -1;
}
- switch (sm->group_cipher) {
- case WPA_CIPHER_CCMP:
- keylen = 16;
- rsc_len = 6;
- alg = WPA_ALG_CCMP;
- break;
- case WPA_CIPHER_TKIP:
- keylen = 32;
- rsc_len = 6;
- alg = WPA_ALG_TKIP;
- break;
- case WPA_CIPHER_WEP104:
- keylen = 13;
- rsc_len = 0;
- alg = WPA_ALG_WEP;
- break;
- case WPA_CIPHER_WEP40:
- keylen = 5;
- rsc_len = 0;
- alg = WPA_ALG_WEP;
- break;
- default:
+ keylen = wpa_cipher_key_len(sm->group_cipher);
+ rsc_len = wpa_cipher_rsc_len(sm->group_cipher);
+ alg = wpa_cipher_to_alg(sm->group_cipher);
+ if (alg == WPA_ALG_NONE) {
wpa_printf(MSG_WARNING, "WPA: Unsupported Group Cipher %d",
sm->group_cipher);
return -1;
@@ -795,9 +589,8 @@ static int wpa_ft_process_gtk_subelem(struct wpa_sm *sm, const u8 *gtk_elem,
}
wpa_hexdump_key(MSG_DEBUG, "FT: GTK from Reassoc Resp", gtk, keylen);
- if (wpa_sm_set_key(sm, alg, (u8 *) "\xff\xff\xff\xff\xff\xff",
- keyidx, 0, gtk_elem + 3, rsc_len, gtk, keylen) <
- 0) {
+ if (wpa_sm_set_key(sm, alg, broadcast_ether_addr, keyidx, 0,
+ gtk_elem + 3, rsc_len, gtk, keylen) < 0) {
wpa_printf(MSG_WARNING, "WPA: Failed to set GTK to the "
"driver.");
return -1;
@@ -848,9 +641,8 @@ static int wpa_ft_process_igtk_subelem(struct wpa_sm *sm, const u8 *igtk_elem,
wpa_hexdump_key(MSG_DEBUG, "FT: IGTK from Reassoc Resp", igtk,
WPA_IGTK_LEN);
- if (wpa_sm_set_key(sm, WPA_ALG_IGTK, (u8 *) "\xff\xff\xff\xff\xff\xff",
- keyidx, 0, igtk_elem + 2, 6, igtk, WPA_IGTK_LEN) <
- 0) {
+ if (wpa_sm_set_key(sm, WPA_ALG_IGTK, broadcast_ether_addr, keyidx, 0,
+ igtk_elem + 2, 6, igtk, WPA_IGTK_LEN) < 0) {
wpa_printf(MSG_WARNING, "WPA: Failed to set IGTK to the "
"driver.");
return -1;
@@ -951,8 +743,8 @@ int wpa_ft_validate_reassoc_resp(struct wpa_sm *sm, const u8 *ies,
}
count = 3;
- if (parse.tie)
- count++;
+ if (parse.ric)
+ count += ieee802_11_ie_count(parse.ric, parse.ric_len);
if (ftie->mic_control[1] != count) {
wpa_printf(MSG_DEBUG, "FT: Unexpected IE count in MIC "
"Control: received %u expected %u",
@@ -1020,7 +812,7 @@ int wpa_ft_start_over_ds(struct wpa_sm *sm, const u8 *target_ap,
MAC2STR(target_ap));
/* Generate a new SNonce */
- if (os_get_random(sm->snonce, WPA_NONCE_LEN)) {
+ if (random_get_bytes(sm->snonce, WPA_NONCE_LEN)) {
wpa_printf(MSG_INFO, "FT: Failed to generate a new SNonce");
return -1;
}
diff --git a/contrib/wpa/src/rsn_supp/wpa_i.h b/contrib/wpa/src/rsn_supp/wpa_i.h
index 618c090..9f9e641 100644
--- a/contrib/wpa/src/rsn_supp/wpa_i.h
+++ b/contrib/wpa/src/rsn_supp/wpa_i.h
@@ -2,14 +2,8 @@
* Internal WPA/RSN supplicant state machine definitions
* Copyright (c) 2004-2010, Jouni Malinen <j@w1.fi>
*
- * This program is free software; you can redistribute it and/or modify
- * it under the terms of the GNU General Public License version 2 as
- * published by the Free Software Foundation.
- *
- * Alternatively, this software may be distributed under the terms of BSD
- * license.
- *
- * See README and COPYING for more details.
+ * This software may be distributed under the terms of the BSD license.
+ * See README for more details.
*/
#ifndef WPA_I_H
@@ -18,6 +12,7 @@
#include "utils/list.h"
struct wpa_peerkey;
+struct wpa_tdls_peer;
struct wpa_eapol_key;
/**
@@ -43,6 +38,7 @@ struct wpa_sm {
struct l2_packet_data *l2_preauth;
struct l2_packet_data *l2_preauth_br;
+ struct l2_packet_data *l2_tdls;
u8 preauth_bssid[ETH_ALEN]; /* current RSN pre-auth peer or
* 00:00:00:00:00:00 if no pre-auth is
* in progress */
@@ -92,6 +88,20 @@ struct wpa_sm {
#ifdef CONFIG_PEERKEY
struct wpa_peerkey *peerkey;
#endif /* CONFIG_PEERKEY */
+#ifdef CONFIG_TDLS
+ struct wpa_tdls_peer *tdls;
+ int tdls_prohibited;
+ int tdls_disabled;
+
+ /* The driver supports TDLS */
+ int tdls_supported;
+
+ /*
+ * The driver requires explicit discovery/setup/teardown frames sent
+ * to it via tdls_mgmt.
+ */
+ int tdls_external_setup;
+#endif /* CONFIG_TDLS */
#ifdef CONFIG_IEEE80211R
u8 xxkey[PMK_LEN]; /* PSK or the second 256 bits of MSK */
@@ -133,12 +143,6 @@ static inline void wpa_sm_deauthenticate(struct wpa_sm *sm, int reason_code)
sm->ctx->deauthenticate(sm->ctx->ctx, reason_code);
}
-static inline void wpa_sm_disassociate(struct wpa_sm *sm, int reason_code)
-{
- WPA_ASSERT(sm->ctx->disassociate);
- sm->ctx->disassociate(sm->ctx->ctx, reason_code);
-}
-
static inline int wpa_sm_set_key(struct wpa_sm *sm, enum wpa_alg alg,
const u8 *addr, int key_idx, int set_tx,
const u8 *seq, size_t seq_len,
@@ -237,6 +241,57 @@ static inline int wpa_sm_mark_authenticated(struct wpa_sm *sm,
return -1;
}
+static inline void wpa_sm_set_rekey_offload(struct wpa_sm *sm)
+{
+ if (!sm->ctx->set_rekey_offload)
+ return;
+ sm->ctx->set_rekey_offload(sm->ctx->ctx, sm->ptk.kek,
+ sm->ptk.kck, sm->rx_replay_counter);
+}
+
+#ifdef CONFIG_TDLS
+static inline int wpa_sm_tdls_get_capa(struct wpa_sm *sm,
+ int *tdls_supported,
+ int *tdls_ext_setup)
+{
+ if (sm->ctx->tdls_get_capa)
+ return sm->ctx->tdls_get_capa(sm->ctx->ctx, tdls_supported,
+ tdls_ext_setup);
+ return -1;
+}
+
+static inline int wpa_sm_send_tdls_mgmt(struct wpa_sm *sm, const u8 *dst,
+ u8 action_code, u8 dialog_token,
+ u16 status_code, const u8 *buf,
+ size_t len)
+{
+ if (sm->ctx->send_tdls_mgmt)
+ return sm->ctx->send_tdls_mgmt(sm->ctx->ctx, dst, action_code,
+ dialog_token, status_code,
+ buf, len);
+ return -1;
+}
+
+static inline int wpa_sm_tdls_oper(struct wpa_sm *sm, int oper,
+ const u8 *peer)
+{
+ if (sm->ctx->tdls_oper)
+ return sm->ctx->tdls_oper(sm->ctx->ctx, oper, peer);
+ return -1;
+}
+
+static inline int
+wpa_sm_tdls_peer_addset(struct wpa_sm *sm, const u8 *addr, int add,
+ u16 capability, const u8 *supp_rates,
+ size_t supp_rates_len)
+{
+ if (sm->ctx->tdls_peer_addset)
+ return sm->ctx->tdls_peer_addset(sm->ctx->ctx, addr, add,
+ capability, supp_rates,
+ supp_rates_len);
+ return -1;
+}
+#endif /* CONFIG_TDLS */
void wpa_eapol_key_send(struct wpa_sm *sm, const u8 *kck,
int ver, const u8 *dest, u16 proto,
@@ -256,4 +311,7 @@ int wpa_derive_ptk_ft(struct wpa_sm *sm, const unsigned char *src_addr,
const struct wpa_eapol_key *key,
struct wpa_ptk *ptk, size_t ptk_len);
+void wpa_tdls_assoc(struct wpa_sm *sm);
+void wpa_tdls_disassoc(struct wpa_sm *sm);
+
#endif /* WPA_I_H */
diff --git a/contrib/wpa/src/rsn_supp/wpa_ie.c b/contrib/wpa/src/rsn_supp/wpa_ie.c
index f447223..3d75365 100644
--- a/contrib/wpa/src/rsn_supp/wpa_ie.c
+++ b/contrib/wpa/src/rsn_supp/wpa_ie.c
@@ -2,14 +2,8 @@
* wpa_supplicant - WPA/RSN IE and KDE processing
* Copyright (c) 2003-2008, Jouni Malinen <j@w1.fi>
*
- * This program is free software; you can redistribute it and/or modify
- * it under the terms of the GNU General Public License version 2 as
- * published by the Free Software Foundation.
- *
- * Alternatively, this software may be distributed under the terms of BSD
- * license.
- *
- * See README and COPYING for more details.
+ * This software may be distributed under the terms of the BSD license.
+ * See README for more details.
*/
#include "includes.h"
@@ -22,144 +16,6 @@
#include "wpa_ie.h"
-static int wpa_selector_to_bitfield(const u8 *s)
-{
- if (RSN_SELECTOR_GET(s) == WPA_CIPHER_SUITE_NONE)
- return WPA_CIPHER_NONE;
- if (RSN_SELECTOR_GET(s) == WPA_CIPHER_SUITE_WEP40)
- return WPA_CIPHER_WEP40;
- if (RSN_SELECTOR_GET(s) == WPA_CIPHER_SUITE_TKIP)
- return WPA_CIPHER_TKIP;
- if (RSN_SELECTOR_GET(s) == WPA_CIPHER_SUITE_CCMP)
- return WPA_CIPHER_CCMP;
- if (RSN_SELECTOR_GET(s) == WPA_CIPHER_SUITE_WEP104)
- return WPA_CIPHER_WEP104;
- return 0;
-}
-
-
-static int wpa_key_mgmt_to_bitfield(const u8 *s)
-{
- if (RSN_SELECTOR_GET(s) == WPA_AUTH_KEY_MGMT_UNSPEC_802_1X)
- return WPA_KEY_MGMT_IEEE8021X;
- if (RSN_SELECTOR_GET(s) == WPA_AUTH_KEY_MGMT_PSK_OVER_802_1X)
- return WPA_KEY_MGMT_PSK;
- if (RSN_SELECTOR_GET(s) == WPA_AUTH_KEY_MGMT_NONE)
- return WPA_KEY_MGMT_WPA_NONE;
- return 0;
-}
-
-
-static int wpa_parse_wpa_ie_wpa(const u8 *wpa_ie, size_t wpa_ie_len,
- struct wpa_ie_data *data)
-{
- const struct wpa_ie_hdr *hdr;
- const u8 *pos;
- int left;
- int i, count;
-
- os_memset(data, 0, sizeof(*data));
- data->proto = WPA_PROTO_WPA;
- data->pairwise_cipher = WPA_CIPHER_TKIP;
- data->group_cipher = WPA_CIPHER_TKIP;
- data->key_mgmt = WPA_KEY_MGMT_IEEE8021X;
- data->capabilities = 0;
- data->pmkid = NULL;
- data->num_pmkid = 0;
- data->mgmt_group_cipher = 0;
-
- if (wpa_ie_len == 0) {
- /* No WPA IE - fail silently */
- return -1;
- }
-
- if (wpa_ie_len < sizeof(struct wpa_ie_hdr)) {
- wpa_printf(MSG_DEBUG, "%s: ie len too short %lu",
- __func__, (unsigned long) wpa_ie_len);
- return -1;
- }
-
- hdr = (const struct wpa_ie_hdr *) wpa_ie;
-
- if (hdr->elem_id != WLAN_EID_VENDOR_SPECIFIC ||
- hdr->len != wpa_ie_len - 2 ||
- RSN_SELECTOR_GET(hdr->oui) != WPA_OUI_TYPE ||
- WPA_GET_LE16(hdr->version) != WPA_VERSION) {
- wpa_printf(MSG_DEBUG, "%s: malformed ie or unknown version",
- __func__);
- return -1;
- }
-
- pos = (const u8 *) (hdr + 1);
- left = wpa_ie_len - sizeof(*hdr);
-
- if (left >= WPA_SELECTOR_LEN) {
- data->group_cipher = wpa_selector_to_bitfield(pos);
- pos += WPA_SELECTOR_LEN;
- left -= WPA_SELECTOR_LEN;
- } else if (left > 0) {
- wpa_printf(MSG_DEBUG, "%s: ie length mismatch, %u too much",
- __func__, left);
- return -1;
- }
-
- if (left >= 2) {
- data->pairwise_cipher = 0;
- count = WPA_GET_LE16(pos);
- pos += 2;
- left -= 2;
- if (count == 0 || left < count * WPA_SELECTOR_LEN) {
- wpa_printf(MSG_DEBUG, "%s: ie count botch (pairwise), "
- "count %u left %u", __func__, count, left);
- return -1;
- }
- for (i = 0; i < count; i++) {
- data->pairwise_cipher |= wpa_selector_to_bitfield(pos);
- pos += WPA_SELECTOR_LEN;
- left -= WPA_SELECTOR_LEN;
- }
- } else if (left == 1) {
- wpa_printf(MSG_DEBUG, "%s: ie too short (for key mgmt)",
- __func__);
- return -1;
- }
-
- if (left >= 2) {
- data->key_mgmt = 0;
- count = WPA_GET_LE16(pos);
- pos += 2;
- left -= 2;
- if (count == 0 || left < count * WPA_SELECTOR_LEN) {
- wpa_printf(MSG_DEBUG, "%s: ie count botch (key mgmt), "
- "count %u left %u", __func__, count, left);
- return -1;
- }
- for (i = 0; i < count; i++) {
- data->key_mgmt |= wpa_key_mgmt_to_bitfield(pos);
- pos += WPA_SELECTOR_LEN;
- left -= WPA_SELECTOR_LEN;
- }
- } else if (left == 1) {
- wpa_printf(MSG_DEBUG, "%s: ie too short (for capabilities)",
- __func__);
- return -1;
- }
-
- if (left >= 2) {
- data->capabilities = WPA_GET_LE16(pos);
- pos += 2;
- left -= 2;
- }
-
- if (left > 0) {
- wpa_printf(MSG_DEBUG, "%s: ie has %u trailing bytes - ignored",
- __func__, left);
- }
-
- return 0;
-}
-
-
/**
* wpa_parse_wpa_ie - Parse WPA/RSN IE
* @wpa_ie: Pointer to WPA or RSN IE
@@ -185,6 +41,7 @@ static int wpa_gen_wpa_ie_wpa(u8 *wpa_ie, size_t wpa_ie_len,
{
u8 *pos;
struct wpa_ie_hdr *hdr;
+ u32 suite;
if (wpa_ie_len < sizeof(*hdr) + WPA_SELECTOR_LEN +
2 + WPA_SELECTOR_LEN + 2 + WPA_SELECTOR_LEN)
@@ -196,34 +53,26 @@ static int wpa_gen_wpa_ie_wpa(u8 *wpa_ie, size_t wpa_ie_len,
WPA_PUT_LE16(hdr->version, WPA_VERSION);
pos = (u8 *) (hdr + 1);
- if (group_cipher == WPA_CIPHER_CCMP) {
- RSN_SELECTOR_PUT(pos, WPA_CIPHER_SUITE_CCMP);
- } else if (group_cipher == WPA_CIPHER_TKIP) {
- RSN_SELECTOR_PUT(pos, WPA_CIPHER_SUITE_TKIP);
- } else if (group_cipher == WPA_CIPHER_WEP104) {
- RSN_SELECTOR_PUT(pos, WPA_CIPHER_SUITE_WEP104);
- } else if (group_cipher == WPA_CIPHER_WEP40) {
- RSN_SELECTOR_PUT(pos, WPA_CIPHER_SUITE_WEP40);
- } else {
+ suite = wpa_cipher_to_suite(WPA_PROTO_WPA, group_cipher);
+ if (suite == 0) {
wpa_printf(MSG_WARNING, "Invalid group cipher (%d).",
group_cipher);
return -1;
}
+ RSN_SELECTOR_PUT(pos, suite);
pos += WPA_SELECTOR_LEN;
*pos++ = 1;
*pos++ = 0;
- if (pairwise_cipher == WPA_CIPHER_CCMP) {
- RSN_SELECTOR_PUT(pos, WPA_CIPHER_SUITE_CCMP);
- } else if (pairwise_cipher == WPA_CIPHER_TKIP) {
- RSN_SELECTOR_PUT(pos, WPA_CIPHER_SUITE_TKIP);
- } else if (pairwise_cipher == WPA_CIPHER_NONE) {
- RSN_SELECTOR_PUT(pos, WPA_CIPHER_SUITE_NONE);
- } else {
+ suite = wpa_cipher_to_suite(WPA_PROTO_WPA, pairwise_cipher);
+ if (suite == 0 ||
+ (!wpa_cipher_valid_pairwise(pairwise_cipher) &&
+ pairwise_cipher != WPA_CIPHER_NONE)) {
wpa_printf(MSG_WARNING, "Invalid pairwise cipher (%d).",
pairwise_cipher);
return -1;
}
+ RSN_SELECTOR_PUT(pos, suite);
pos += WPA_SELECTOR_LEN;
*pos++ = 1;
@@ -234,6 +83,8 @@ static int wpa_gen_wpa_ie_wpa(u8 *wpa_ie, size_t wpa_ie_len,
RSN_SELECTOR_PUT(pos, WPA_AUTH_KEY_MGMT_PSK_OVER_802_1X);
} else if (key_mgmt == WPA_KEY_MGMT_WPA_NONE) {
RSN_SELECTOR_PUT(pos, WPA_AUTH_KEY_MGMT_NONE);
+ } else if (key_mgmt == WPA_KEY_MGMT_CCKM) {
+ RSN_SELECTOR_PUT(pos, WPA_AUTH_KEY_MGMT_CCKM);
} else {
wpa_printf(MSG_WARNING, "Invalid key management type (%d).",
key_mgmt);
@@ -260,6 +111,7 @@ static int wpa_gen_wpa_ie_rsn(u8 *rsn_ie, size_t rsn_ie_len,
u8 *pos;
struct rsn_ie_hdr *hdr;
u16 capab;
+ u32 suite;
if (rsn_ie_len < sizeof(*hdr) + RSN_SELECTOR_LEN +
2 + RSN_SELECTOR_LEN + 2 + RSN_SELECTOR_LEN + 2 +
@@ -274,34 +126,26 @@ static int wpa_gen_wpa_ie_rsn(u8 *rsn_ie, size_t rsn_ie_len,
WPA_PUT_LE16(hdr->version, RSN_VERSION);
pos = (u8 *) (hdr + 1);
- if (group_cipher == WPA_CIPHER_CCMP) {
- RSN_SELECTOR_PUT(pos, RSN_CIPHER_SUITE_CCMP);
- } else if (group_cipher == WPA_CIPHER_TKIP) {
- RSN_SELECTOR_PUT(pos, RSN_CIPHER_SUITE_TKIP);
- } else if (group_cipher == WPA_CIPHER_WEP104) {
- RSN_SELECTOR_PUT(pos, RSN_CIPHER_SUITE_WEP104);
- } else if (group_cipher == WPA_CIPHER_WEP40) {
- RSN_SELECTOR_PUT(pos, RSN_CIPHER_SUITE_WEP40);
- } else {
+ suite = wpa_cipher_to_suite(WPA_PROTO_RSN, group_cipher);
+ if (suite == 0) {
wpa_printf(MSG_WARNING, "Invalid group cipher (%d).",
group_cipher);
return -1;
}
+ RSN_SELECTOR_PUT(pos, suite);
pos += RSN_SELECTOR_LEN;
*pos++ = 1;
*pos++ = 0;
- if (pairwise_cipher == WPA_CIPHER_CCMP) {
- RSN_SELECTOR_PUT(pos, RSN_CIPHER_SUITE_CCMP);
- } else if (pairwise_cipher == WPA_CIPHER_TKIP) {
- RSN_SELECTOR_PUT(pos, RSN_CIPHER_SUITE_TKIP);
- } else if (pairwise_cipher == WPA_CIPHER_NONE) {
- RSN_SELECTOR_PUT(pos, RSN_CIPHER_SUITE_NONE);
- } else {
+ suite = wpa_cipher_to_suite(WPA_PROTO_RSN, pairwise_cipher);
+ if (suite == 0 ||
+ (!wpa_cipher_valid_pairwise(pairwise_cipher) &&
+ pairwise_cipher != WPA_CIPHER_NONE)) {
wpa_printf(MSG_WARNING, "Invalid pairwise cipher (%d).",
pairwise_cipher);
return -1;
}
+ RSN_SELECTOR_PUT(pos, suite);
pos += RSN_SELECTOR_LEN;
*pos++ = 1;
@@ -310,6 +154,8 @@ static int wpa_gen_wpa_ie_rsn(u8 *rsn_ie, size_t rsn_ie_len,
RSN_SELECTOR_PUT(pos, RSN_AUTH_KEY_MGMT_UNSPEC_802_1X);
} else if (key_mgmt == WPA_KEY_MGMT_PSK) {
RSN_SELECTOR_PUT(pos, RSN_AUTH_KEY_MGMT_PSK_OVER_802_1X);
+ } else if (key_mgmt == WPA_KEY_MGMT_CCKM) {
+ RSN_SELECTOR_PUT(pos, RSN_AUTH_KEY_MGMT_CCKM);
#ifdef CONFIG_IEEE80211R
} else if (key_mgmt == WPA_KEY_MGMT_FT_IEEE8021X) {
RSN_SELECTOR_PUT(pos, RSN_AUTH_KEY_MGMT_FT_802_1X);
@@ -322,6 +168,12 @@ static int wpa_gen_wpa_ie_rsn(u8 *rsn_ie, size_t rsn_ie_len,
} else if (key_mgmt == WPA_KEY_MGMT_PSK_SHA256) {
RSN_SELECTOR_PUT(pos, RSN_AUTH_KEY_MGMT_PSK_SHA256);
#endif /* CONFIG_IEEE80211W */
+#ifdef CONFIG_SAE
+ } else if (key_mgmt == WPA_KEY_MGMT_SAE) {
+ RSN_SELECTOR_PUT(pos, RSN_AUTH_KEY_MGMT_SAE);
+ } else if (key_mgmt == WPA_KEY_MGMT_FT_SAE) {
+ RSN_SELECTOR_PUT(pos, RSN_AUTH_KEY_MGMT_FT_SAE);
+#endif /* CONFIG_SAE */
} else {
wpa_printf(MSG_WARNING, "Invalid key management type (%d).",
key_mgmt);
@@ -535,7 +387,6 @@ int wpa_supplicant_parse_ies(const u8 *buf, size_t len,
ie->rsn_ie_len = pos[1] + 2;
wpa_hexdump(MSG_DEBUG, "WPA: RSN IE in EAPOL-Key",
ie->rsn_ie, ie->rsn_ie_len);
-#ifdef CONFIG_IEEE80211R
} else if (*pos == WLAN_EID_MOBILITY_DOMAIN) {
ie->mdie = pos;
ie->mdie_len = pos[1] + 2;
@@ -562,7 +413,20 @@ int wpa_supplicant_parse_ies(const u8 *buf, size_t len,
"EAPOL-Key Key Data IE",
pos, 2 + pos[1]);
}
-#endif /* CONFIG_IEEE80211R */
+ } else if (*pos == WLAN_EID_LINK_ID) {
+ if (pos[1] >= 18) {
+ ie->lnkid = pos;
+ ie->lnkid_len = pos[1] + 2;
+ }
+ } else if (*pos == WLAN_EID_EXT_CAPAB) {
+ ie->ext_capab = pos;
+ ie->ext_capab_len = pos[1] + 2;
+ } else if (*pos == WLAN_EID_SUPP_RATES) {
+ ie->supp_rates = pos;
+ ie->supp_rates_len = pos[1] + 2;
+ } else if (*pos == WLAN_EID_EXT_SUPP_RATES) {
+ ie->ext_supp_rates = pos;
+ ie->ext_supp_rates_len = pos[1] + 2;
} else if (*pos == WLAN_EID_VENDOR_SPECIFIC) {
ret = wpa_parse_generic(pos, end, ie);
if (ret < 0)
diff --git a/contrib/wpa/src/rsn_supp/wpa_ie.h b/contrib/wpa/src/rsn_supp/wpa_ie.h
index 94518d8..5afdfe9 100644
--- a/contrib/wpa/src/rsn_supp/wpa_ie.h
+++ b/contrib/wpa/src/rsn_supp/wpa_ie.h
@@ -2,19 +2,15 @@
* wpa_supplicant - WPA/RSN IE and KDE definitions
* Copyright (c) 2004-2007, Jouni Malinen <j@w1.fi>
*
- * This program is free software; you can redistribute it and/or modify
- * it under the terms of the GNU General Public License version 2 as
- * published by the Free Software Foundation.
- *
- * Alternatively, this software may be distributed under the terms of BSD
- * license.
- *
- * See README and COPYING for more details.
+ * This software may be distributed under the terms of the BSD license.
+ * See README for more details.
*/
#ifndef WPA_IE_H
#define WPA_IE_H
+struct wpa_sm;
+
struct wpa_eapol_ie_parse {
const u8 *wpa_ie;
size_t wpa_ie_len;
@@ -39,14 +35,20 @@ struct wpa_eapol_ie_parse {
const u8 *igtk;
size_t igtk_len;
#endif /* CONFIG_IEEE80211W */
-#ifdef CONFIG_IEEE80211R
const u8 *mdie;
size_t mdie_len;
const u8 *ftie;
size_t ftie_len;
const u8 *reassoc_deadline;
const u8 *key_lifetime;
-#endif /* CONFIG_IEEE80211R */
+ const u8 *lnkid;
+ size_t lnkid_len;
+ const u8 *ext_capab;
+ size_t ext_capab_len;
+ const u8 *supp_rates;
+ size_t supp_rates_len;
+ const u8 *ext_supp_rates;
+ size_t ext_supp_rates_len;
};
int wpa_supplicant_parse_ies(const u8 *buf, size_t len,
diff --git a/contrib/wpa/src/tls/.gitignore b/contrib/wpa/src/tls/.gitignore
deleted file mode 100644
index d43242d..0000000
--- a/contrib/wpa/src/tls/.gitignore
+++ /dev/null
@@ -1 +0,0 @@
-libtls.a
diff --git a/contrib/wpa/src/tls/Makefile b/contrib/wpa/src/tls/Makefile
deleted file mode 100644
index a2da096..0000000
--- a/contrib/wpa/src/tls/Makefile
+++ /dev/null
@@ -1,37 +0,0 @@
-all: libtls.a
-
-clean:
- rm -f *~ *.o *.d libtls.a
-
-install:
- @echo Nothing to be made.
-
-
-include ../lib.rules
-
-CFLAGS += -DCONFIG_INTERNAL_LIBTOMMATH
-CFLAGS += -DCONFIG_CRYPTO_INTERNAL
-
-LIB_OBJS= \
- asn1.o \
- bignum.o \
- pkcs1.o \
- pkcs5.o \
- pkcs8.o \
- rsa.o \
- tlsv1_client.o \
- tlsv1_client_read.o \
- tlsv1_client_write.o \
- tlsv1_common.o \
- tlsv1_cred.o \
- tlsv1_record.o \
- tlsv1_server.o \
- tlsv1_server_read.o \
- tlsv1_server_write.o \
- x509v3.o
-
-
-libtls.a: $(LIB_OBJS)
- $(AR) crT $@ $?
-
--include $(OBJS:%.o=%.d)
diff --git a/contrib/wpa/src/tls/asn1.c b/contrib/wpa/src/tls/asn1.c
index 3391245..53acd53 100644
--- a/contrib/wpa/src/tls/asn1.c
+++ b/contrib/wpa/src/tls/asn1.c
@@ -2,14 +2,8 @@
* ASN.1 DER parsing
* Copyright (c) 2006, Jouni Malinen <j@w1.fi>
*
- * This program is free software; you can redistribute it and/or modify
- * it under the terms of the GNU General Public License version 2 as
- * published by the Free Software Foundation.
- *
- * Alternatively, this software may be distributed under the terms of BSD
- * license.
- *
- * See README and COPYING for more details.
+ * This software may be distributed under the terms of the BSD license.
+ * See README for more details.
*/
#include "includes.h"
diff --git a/contrib/wpa/src/tls/asn1.h b/contrib/wpa/src/tls/asn1.h
index 2ff571e..6342c4cc7 100644
--- a/contrib/wpa/src/tls/asn1.h
+++ b/contrib/wpa/src/tls/asn1.h
@@ -2,14 +2,8 @@
* ASN.1 DER parsing
* Copyright (c) 2006, Jouni Malinen <j@w1.fi>
*
- * This program is free software; you can redistribute it and/or modify
- * it under the terms of the GNU General Public License version 2 as
- * published by the Free Software Foundation.
- *
- * Alternatively, this software may be distributed under the terms of BSD
- * license.
- *
- * See README and COPYING for more details.
+ * This software may be distributed under the terms of the BSD license.
+ * See README for more details.
*/
#ifndef ASN1_H
diff --git a/contrib/wpa/src/tls/bignum.c b/contrib/wpa/src/tls/bignum.c
index 5c0fc62..f3baafe 100644
--- a/contrib/wpa/src/tls/bignum.c
+++ b/contrib/wpa/src/tls/bignum.c
@@ -2,14 +2,8 @@
* Big number math
* Copyright (c) 2006, Jouni Malinen <j@w1.fi>
*
- * This program is free software; you can redistribute it and/or modify
- * it under the terms of the GNU General Public License version 2 as
- * published by the Free Software Foundation.
- *
- * Alternatively, this software may be distributed under the terms of BSD
- * license.
- *
- * See README and COPYING for more details.
+ * This software may be distributed under the terms of the BSD license.
+ * See README for more details.
*/
#include "includes.h"
diff --git a/contrib/wpa/src/tls/bignum.h b/contrib/wpa/src/tls/bignum.h
index f25e267..24acdce 100644
--- a/contrib/wpa/src/tls/bignum.h
+++ b/contrib/wpa/src/tls/bignum.h
@@ -2,14 +2,8 @@
* Big number math
* Copyright (c) 2006, Jouni Malinen <j@w1.fi>
*
- * This program is free software; you can redistribute it and/or modify
- * it under the terms of the GNU General Public License version 2 as
- * published by the Free Software Foundation.
- *
- * Alternatively, this software may be distributed under the terms of BSD
- * license.
- *
- * See README and COPYING for more details.
+ * This software may be distributed under the terms of the BSD license.
+ * See README for more details.
*/
#ifndef BIGNUM_H
diff --git a/contrib/wpa/src/tls/libtommath.c b/contrib/wpa/src/tls/libtommath.c
index 1374264..741b442 100644
--- a/contrib/wpa/src/tls/libtommath.c
+++ b/contrib/wpa/src/tls/libtommath.c
@@ -66,11 +66,19 @@
#define OPT_CAST(x)
+#ifdef __x86_64__
+typedef unsigned long mp_digit;
+typedef unsigned long mp_word __attribute__((mode(TI)));
+
+#define DIGIT_BIT 60
+#define MP_64BIT
+#else
typedef unsigned long mp_digit;
typedef u64 mp_word;
#define DIGIT_BIT 28
#define MP_28BIT
+#endif
#define XMALLOC os_malloc
@@ -572,7 +580,7 @@ static int mp_mod (mp_int * a, mp_int * b, mp_int * c)
/* this is a shell function that calls either the normal or Montgomery
* exptmod functions. Originally the call to the montgomery code was
- * embedded in the normal function but that wasted alot of stack space
+ * embedded in the normal function but that wasted a lot of stack space
* for nothing (since 99% of the time the Montgomery code would be called)
*/
static int mp_exptmod (mp_int * G, mp_int * X, mp_int * P, mp_int * Y)
@@ -2207,7 +2215,7 @@ static int mp_2expt (mp_int * a, int b)
/* zero a as per default */
mp_zero (a);
- /* grow a to accomodate the single bit */
+ /* grow a to accommodate the single bit */
if ((res = mp_grow (a, b / DIGIT_BIT + 1)) != MP_OKAY) {
return res;
}
@@ -2319,7 +2327,7 @@ CLEANUP:
}
-/* multiplies |a| * |b| and only computes upto digs digits of result
+/* multiplies |a| * |b| and only computes up to digs digits of result
* HAC pp. 595, Algorithm 14.12 Modified so you can control how
* many digits of output are created.
*/
@@ -2678,7 +2686,7 @@ mp_montgomery_setup (mp_int * n, mp_digit * rho)
*
* Based on Algorithm 14.32 on pp.601 of HAC.
*/
-int fast_mp_montgomery_reduce (mp_int * x, mp_int * n, mp_digit rho)
+static int fast_mp_montgomery_reduce (mp_int * x, mp_int * n, mp_digit rho)
{
int ix, res, olduse;
mp_word W[MP_WARRAY];
@@ -2829,7 +2837,7 @@ static int mp_mul_2(mp_int * a, mp_int * b)
{
int x, res, oldused;
- /* grow to accomodate result */
+ /* grow to accommodate result */
if (b->alloc < a->used + 1) {
if ((res = mp_grow (b, a->used + 1)) != MP_OKAY) {
return res;
@@ -2891,8 +2899,8 @@ static int mp_mul_2(mp_int * a, mp_int * b)
/*
* shifts with subtractions when the result is greater than b.
*
- * The method is slightly modified to shift B unconditionally upto just under
- * the leading bit of b. This saves alot of multiple precision shifting.
+ * The method is slightly modified to shift B unconditionally up to just under
+ * the leading bit of b. This saves a lot of multiple precision shifting.
*/
static int mp_montgomery_calc_normalization (mp_int * a, mp_int * b)
{
diff --git a/contrib/wpa/src/tls/pkcs1.c b/contrib/wpa/src/tls/pkcs1.c
index 72ebd87..b6fde5e 100644
--- a/contrib/wpa/src/tls/pkcs1.c
+++ b/contrib/wpa/src/tls/pkcs1.c
@@ -2,14 +2,8 @@
* PKCS #1 (RSA Encryption)
* Copyright (c) 2006-2009, Jouni Malinen <j@w1.fi>
*
- * This program is free software; you can redistribute it and/or modify
- * it under the terms of the GNU General Public License version 2 as
- * published by the Free Software Foundation.
- *
- * Alternatively, this software may be distributed under the terms of BSD
- * license.
- *
- * See README and COPYING for more details.
+ * This software may be distributed under the terms of the BSD license.
+ * See README for more details.
*/
#include "includes.h"
diff --git a/contrib/wpa/src/tls/pkcs1.h b/contrib/wpa/src/tls/pkcs1.h
index 68872b1..ed64def 100644
--- a/contrib/wpa/src/tls/pkcs1.h
+++ b/contrib/wpa/src/tls/pkcs1.h
@@ -2,14 +2,8 @@
* PKCS #1 (RSA Encryption)
* Copyright (c) 2006-2009, Jouni Malinen <j@w1.fi>
*
- * This program is free software; you can redistribute it and/or modify
- * it under the terms of the GNU General Public License version 2 as
- * published by the Free Software Foundation.
- *
- * Alternatively, this software may be distributed under the terms of BSD
- * license.
- *
- * See README and COPYING for more details.
+ * This software may be distributed under the terms of the BSD license.
+ * See README for more details.
*/
#ifndef PKCS1_H
diff --git a/contrib/wpa/src/tls/pkcs5.c b/contrib/wpa/src/tls/pkcs5.c
index 4291b84..8a93483 100644
--- a/contrib/wpa/src/tls/pkcs5.c
+++ b/contrib/wpa/src/tls/pkcs5.c
@@ -2,14 +2,8 @@
* PKCS #5 (Password-based Encryption)
* Copyright (c) 2009, Jouni Malinen <j@w1.fi>
*
- * This program is free software; you can redistribute it and/or modify
- * it under the terms of the GNU General Public License version 2 as
- * published by the Free Software Foundation.
- *
- * Alternatively, this software may be distributed under the terms of BSD
- * license.
- *
- * See README and COPYING for more details.
+ * This software may be distributed under the terms of the BSD license.
+ * See README for more details.
*/
#include "includes.h"
@@ -32,7 +26,7 @@ struct pkcs5_params {
};
-enum pkcs5_alg pkcs5_get_alg(struct asn1_oid *oid)
+static enum pkcs5_alg pkcs5_get_alg(struct asn1_oid *oid)
{
if (oid->len == 7 &&
oid->oid[0] == 1 /* iso */ &&
diff --git a/contrib/wpa/src/tls/pkcs5.h b/contrib/wpa/src/tls/pkcs5.h
index 6ed3923..20ddadc 100644
--- a/contrib/wpa/src/tls/pkcs5.h
+++ b/contrib/wpa/src/tls/pkcs5.h
@@ -2,14 +2,8 @@
* PKCS #5 (Password-based Encryption)
* Copyright (c) 2009, Jouni Malinen <j@w1.fi>
*
- * This program is free software; you can redistribute it and/or modify
- * it under the terms of the GNU General Public License version 2 as
- * published by the Free Software Foundation.
- *
- * Alternatively, this software may be distributed under the terms of BSD
- * license.
- *
- * See README and COPYING for more details.
+ * This software may be distributed under the terms of the BSD license.
+ * See README for more details.
*/
#ifndef PKCS5_H
diff --git a/contrib/wpa/src/tls/pkcs8.c b/contrib/wpa/src/tls/pkcs8.c
index 69ab262..52e43a4 100644
--- a/contrib/wpa/src/tls/pkcs8.c
+++ b/contrib/wpa/src/tls/pkcs8.c
@@ -2,14 +2,8 @@
* PKCS #8 (Private-key information syntax)
* Copyright (c) 2006-2009, Jouni Malinen <j@w1.fi>
*
- * This program is free software; you can redistribute it and/or modify
- * it under the terms of the GNU General Public License version 2 as
- * published by the Free Software Foundation.
- *
- * Alternatively, this software may be distributed under the terms of BSD
- * license.
- *
- * See README and COPYING for more details.
+ * This software may be distributed under the terms of the BSD license.
+ * See README for more details.
*/
#include "includes.h"
diff --git a/contrib/wpa/src/tls/pkcs8.h b/contrib/wpa/src/tls/pkcs8.h
index dac517c..bebf840 100644
--- a/contrib/wpa/src/tls/pkcs8.h
+++ b/contrib/wpa/src/tls/pkcs8.h
@@ -2,14 +2,8 @@
* PKCS #8 (Private-key information syntax)
* Copyright (c) 2006-2009, Jouni Malinen <j@w1.fi>
*
- * This program is free software; you can redistribute it and/or modify
- * it under the terms of the GNU General Public License version 2 as
- * published by the Free Software Foundation.
- *
- * Alternatively, this software may be distributed under the terms of BSD
- * license.
- *
- * See README and COPYING for more details.
+ * This software may be distributed under the terms of the BSD license.
+ * See README for more details.
*/
#ifndef PKCS8_H
diff --git a/contrib/wpa/src/tls/rsa.c b/contrib/wpa/src/tls/rsa.c
index 3084adc..125c420 100644
--- a/contrib/wpa/src/tls/rsa.c
+++ b/contrib/wpa/src/tls/rsa.c
@@ -2,14 +2,8 @@
* RSA
* Copyright (c) 2006, Jouni Malinen <j@w1.fi>
*
- * This program is free software; you can redistribute it and/or modify
- * it under the terms of the GNU General Public License version 2 as
- * published by the Free Software Foundation.
- *
- * Alternatively, this software may be distributed under the terms of BSD
- * license.
- *
- * See README and COPYING for more details.
+ * This software may be distributed under the terms of the BSD license.
+ * See README for more details.
*/
#include "includes.h"
diff --git a/contrib/wpa/src/tls/rsa.h b/contrib/wpa/src/tls/rsa.h
index ac50dfd..c236a9d 100644
--- a/contrib/wpa/src/tls/rsa.h
+++ b/contrib/wpa/src/tls/rsa.h
@@ -2,14 +2,8 @@
* RSA
* Copyright (c) 2006, Jouni Malinen <j@w1.fi>
*
- * This program is free software; you can redistribute it and/or modify
- * it under the terms of the GNU General Public License version 2 as
- * published by the Free Software Foundation.
- *
- * Alternatively, this software may be distributed under the terms of BSD
- * license.
- *
- * See README and COPYING for more details.
+ * This software may be distributed under the terms of the BSD license.
+ * See README for more details.
*/
#ifndef RSA_H
diff --git a/contrib/wpa/src/tls/tlsv1_client.c b/contrib/wpa/src/tls/tlsv1_client.c
index afb6031..12148b6 100644
--- a/contrib/wpa/src/tls/tlsv1_client.c
+++ b/contrib/wpa/src/tls/tlsv1_client.c
@@ -1,15 +1,9 @@
/*
- * TLSv1 client (RFC 2246)
- * Copyright (c) 2006-2007, Jouni Malinen <j@w1.fi>
+ * TLS v1.0/v1.1/v1.2 client (RFC 2246, RFC 4346, RFC 5246)
+ * Copyright (c) 2006-2011, Jouni Malinen <j@w1.fi>
*
- * This program is free software; you can redistribute it and/or modify
- * it under the terms of the GNU General Public License version 2 as
- * published by the Free Software Foundation.
- *
- * Alternatively, this software may be distributed under the terms of BSD
- * license.
- *
- * See README and COPYING for more details.
+ * This software may be distributed under the terms of the BSD license.
+ * See README for more details.
*/
#include "includes.h"
@@ -67,7 +61,8 @@ int tls_derive_keys(struct tlsv1_client *conn,
os_memcpy(seed, conn->client_random, TLS_RANDOM_LEN);
os_memcpy(seed + TLS_RANDOM_LEN, conn->server_random,
TLS_RANDOM_LEN);
- if (tls_prf(pre_master_secret, pre_master_secret_len,
+ if (tls_prf(conn->rl.tls_version,
+ pre_master_secret, pre_master_secret_len,
"master secret", seed, 2 * TLS_RANDOM_LEN,
conn->master_secret, TLS_MASTER_SECRET_LEN)) {
wpa_printf(MSG_DEBUG, "TLSv1: Failed to derive "
@@ -80,9 +75,11 @@ int tls_derive_keys(struct tlsv1_client *conn,
os_memcpy(seed, conn->server_random, TLS_RANDOM_LEN);
os_memcpy(seed + TLS_RANDOM_LEN, conn->client_random, TLS_RANDOM_LEN);
- key_block_len = 2 * (conn->rl.hash_size + conn->rl.key_material_len +
- conn->rl.iv_size);
- if (tls_prf(conn->master_secret, TLS_MASTER_SECRET_LEN,
+ key_block_len = 2 * (conn->rl.hash_size + conn->rl.key_material_len);
+ if (conn->rl.tls_version == TLS_VERSION_1)
+ key_block_len += 2 * conn->rl.iv_size;
+ if (tls_prf(conn->rl.tls_version,
+ conn->master_secret, TLS_MASTER_SECRET_LEN,
"key expansion", seed, 2 * TLS_RANDOM_LEN,
key_block, key_block_len)) {
wpa_printf(MSG_DEBUG, "TLSv1: Failed to derive key_block");
@@ -107,12 +104,21 @@ int tls_derive_keys(struct tlsv1_client *conn,
os_memcpy(conn->rl.read_key, pos, conn->rl.key_material_len);
pos += conn->rl.key_material_len;
- /* client_write_IV */
- os_memcpy(conn->rl.write_iv, pos, conn->rl.iv_size);
- pos += conn->rl.iv_size;
- /* server_write_IV */
- os_memcpy(conn->rl.read_iv, pos, conn->rl.iv_size);
- pos += conn->rl.iv_size;
+ if (conn->rl.tls_version == TLS_VERSION_1) {
+ /* client_write_IV */
+ os_memcpy(conn->rl.write_iv, pos, conn->rl.iv_size);
+ pos += conn->rl.iv_size;
+ /* server_write_IV */
+ os_memcpy(conn->rl.read_iv, pos, conn->rl.iv_size);
+ pos += conn->rl.iv_size;
+ } else {
+ /*
+ * Use IV field to set the mask value for TLS v1.1. A fixed
+ * mask of zero is used per the RFC 4346, 6.2.3.2 CBC Block
+ * Cipher option 2a.
+ */
+ os_memset(conn->rl.write_iv, 0, conn->rl.iv_size);
+ }
return 0;
}
@@ -126,17 +132,23 @@ int tls_derive_keys(struct tlsv1_client *conn,
* @out_len: Length of the output buffer.
* @appl_data: Pointer to application data pointer, or %NULL if dropped
* @appl_data_len: Pointer to variable that is set to appl_data length
+ * @need_more_data: Set to 1 if more data would be needed to complete
+ * processing
* Returns: Pointer to output data, %NULL on failure
*/
u8 * tlsv1_client_handshake(struct tlsv1_client *conn,
const u8 *in_data, size_t in_len,
size_t *out_len, u8 **appl_data,
- size_t *appl_data_len)
+ size_t *appl_data_len, int *need_more_data)
{
const u8 *pos, *end;
- u8 *msg = NULL, *in_msg, *in_pos, *in_end, alert, ct;
+ u8 *msg = NULL, *in_msg = NULL, *in_pos, *in_end, alert, ct;
size_t in_msg_len;
int no_appl_data;
+ int used;
+
+ if (need_more_data)
+ *need_more_data = 0;
if (conn->state == CLIENT_HELLO) {
if (in_len)
@@ -144,6 +156,19 @@ u8 * tlsv1_client_handshake(struct tlsv1_client *conn,
return tls_send_client_hello(conn, out_len);
}
+ if (conn->partial_input) {
+ if (wpabuf_resize(&conn->partial_input, in_len) < 0) {
+ wpa_printf(MSG_DEBUG, "TLSv1: Failed to allocate "
+ "memory for pending record");
+ tls_alert(conn, TLS_ALERT_LEVEL_FATAL,
+ TLS_ALERT_INTERNAL_ERROR);
+ goto failed;
+ }
+ wpabuf_put_data(conn->partial_input, in_data, in_len);
+ in_data = wpabuf_head(conn->partial_input);
+ in_len = wpabuf_len(conn->partial_input);
+ }
+
if (in_data == NULL || in_len == 0)
return NULL;
@@ -156,13 +181,33 @@ u8 * tlsv1_client_handshake(struct tlsv1_client *conn,
/* Each received packet may include multiple records */
while (pos < end) {
in_msg_len = in_len;
- if (tlsv1_record_receive(&conn->rl, pos, end - pos,
- in_msg, &in_msg_len, &alert)) {
+ used = tlsv1_record_receive(&conn->rl, pos, end - pos,
+ in_msg, &in_msg_len, &alert);
+ if (used < 0) {
wpa_printf(MSG_DEBUG, "TLSv1: Processing received "
"record failed");
tls_alert(conn, TLS_ALERT_LEVEL_FATAL, alert);
goto failed;
}
+ if (used == 0) {
+ struct wpabuf *partial;
+ wpa_printf(MSG_DEBUG, "TLSv1: Need more data");
+ partial = wpabuf_alloc_copy(pos, end - pos);
+ wpabuf_free(conn->partial_input);
+ conn->partial_input = partial;
+ if (conn->partial_input == NULL) {
+ wpa_printf(MSG_DEBUG, "TLSv1: Failed to "
+ "allocate memory for pending "
+ "record");
+ tls_alert(conn, TLS_ALERT_LEVEL_FATAL,
+ TLS_ALERT_INTERNAL_ERROR);
+ goto failed;
+ }
+ os_free(in_msg);
+ if (need_more_data)
+ *need_more_data = 1;
+ return NULL;
+ }
ct = pos[0];
in_pos = in_msg;
@@ -180,7 +225,7 @@ u8 * tlsv1_client_handshake(struct tlsv1_client *conn,
in_pos += in_msg_len;
}
- pos += TLS_RECORD_HEADER_LEN + WPA_GET_BE16(pos + 3);
+ pos += used;
}
os_free(in_msg);
@@ -192,6 +237,8 @@ u8 * tlsv1_client_handshake(struct tlsv1_client *conn,
failed:
os_free(in_msg);
if (conn->alert_level) {
+ wpabuf_free(conn->partial_input);
+ conn->partial_input = NULL;
conn->state = FAILED;
os_free(msg);
msg = tlsv1_client_send_alert(conn, conn->alert_level,
@@ -202,6 +249,11 @@ failed:
*out_len = 0;
}
+ if (need_more_data == NULL || !(*need_more_data)) {
+ wpabuf_free(conn->partial_input);
+ conn->partial_input = NULL;
+ }
+
return msg;
}
@@ -227,10 +279,8 @@ int tlsv1_client_encrypt(struct tlsv1_client *conn,
wpa_hexdump_key(MSG_MSGDUMP, "TLSv1: Plaintext AppData",
in_data, in_len);
- os_memcpy(out_data + TLS_RECORD_HEADER_LEN, in_data, in_len);
-
if (tlsv1_record_send(&conn->rl, TLS_CONTENT_TYPE_APPLICATION_DATA,
- out_data, out_len, in_len, &rlen) < 0) {
+ out_data, out_len, in_data, in_len, &rlen) < 0) {
wpa_printf(MSG_DEBUG, "TLSv1: Failed to create a record");
tls_alert(conn, TLS_ALERT_LEVEL_FATAL,
TLS_ALERT_INTERNAL_ERROR);
@@ -246,58 +296,116 @@ int tlsv1_client_encrypt(struct tlsv1_client *conn,
* @conn: TLSv1 client connection data from tlsv1_client_init()
* @in_data: Pointer to input buffer (encrypted TLS data)
* @in_len: Input buffer length
- * @out_data: Pointer to output buffer (decrypted data from TLS tunnel)
- * @out_len: Maximum out_data length
- * Returns: Number of bytes written to out_data, -1 on failure
+ * @need_more_data: Set to 1 if more data would be needed to complete
+ * processing
+ * Returns: Decrypted data or %NULL on failure
*
* This function is used after TLS handshake has been completed successfully to
* receive data from the encrypted tunnel.
*/
-int tlsv1_client_decrypt(struct tlsv1_client *conn,
- const u8 *in_data, size_t in_len,
- u8 *out_data, size_t out_len)
+struct wpabuf * tlsv1_client_decrypt(struct tlsv1_client *conn,
+ const u8 *in_data, size_t in_len,
+ int *need_more_data)
{
const u8 *in_end, *pos;
- int res;
- u8 alert, *out_end, *out_pos;
+ int used;
+ u8 alert, *out_pos, ct;
size_t olen;
+ struct wpabuf *buf = NULL;
+
+ if (need_more_data)
+ *need_more_data = 0;
+
+ if (conn->partial_input) {
+ if (wpabuf_resize(&conn->partial_input, in_len) < 0) {
+ wpa_printf(MSG_DEBUG, "TLSv1: Failed to allocate "
+ "memory for pending record");
+ alert = TLS_ALERT_INTERNAL_ERROR;
+ goto fail;
+ }
+ wpabuf_put_data(conn->partial_input, in_data, in_len);
+ in_data = wpabuf_head(conn->partial_input);
+ in_len = wpabuf_len(conn->partial_input);
+ }
pos = in_data;
in_end = in_data + in_len;
- out_pos = out_data;
- out_end = out_data + out_len;
while (pos < in_end) {
- if (pos[0] != TLS_CONTENT_TYPE_APPLICATION_DATA) {
- wpa_printf(MSG_DEBUG, "TLSv1: Unexpected content type "
- "0x%x", pos[0]);
- tls_alert(conn, TLS_ALERT_LEVEL_FATAL,
- TLS_ALERT_UNEXPECTED_MESSAGE);
- return -1;
+ ct = pos[0];
+ if (wpabuf_resize(&buf, in_end - pos) < 0) {
+ alert = TLS_ALERT_INTERNAL_ERROR;
+ goto fail;
}
-
- olen = out_end - out_pos;
- res = tlsv1_record_receive(&conn->rl, pos, in_end - pos,
- out_pos, &olen, &alert);
- if (res < 0) {
+ out_pos = wpabuf_put(buf, 0);
+ olen = wpabuf_tailroom(buf);
+ used = tlsv1_record_receive(&conn->rl, pos, in_end - pos,
+ out_pos, &olen, &alert);
+ if (used < 0) {
wpa_printf(MSG_DEBUG, "TLSv1: Record layer processing "
"failed");
- tls_alert(conn, TLS_ALERT_LEVEL_FATAL, alert);
- return -1;
+ goto fail;
}
- out_pos += olen;
- if (out_pos > out_end) {
- wpa_printf(MSG_DEBUG, "TLSv1: Buffer not large enough "
- "for processing the received record");
- tls_alert(conn, TLS_ALERT_LEVEL_FATAL,
- TLS_ALERT_INTERNAL_ERROR);
- return -1;
+ if (used == 0) {
+ struct wpabuf *partial;
+ wpa_printf(MSG_DEBUG, "TLSv1: Need more data");
+ partial = wpabuf_alloc_copy(pos, in_end - pos);
+ wpabuf_free(conn->partial_input);
+ conn->partial_input = partial;
+ if (conn->partial_input == NULL) {
+ wpa_printf(MSG_DEBUG, "TLSv1: Failed to "
+ "allocate memory for pending "
+ "record");
+ alert = TLS_ALERT_INTERNAL_ERROR;
+ goto fail;
+ }
+ if (need_more_data)
+ *need_more_data = 1;
+ return buf;
}
- pos += TLS_RECORD_HEADER_LEN + WPA_GET_BE16(pos + 3);
+ if (ct == TLS_CONTENT_TYPE_ALERT) {
+ if (olen < 2) {
+ wpa_printf(MSG_DEBUG, "TLSv1: Alert "
+ "underflow");
+ alert = TLS_ALERT_DECODE_ERROR;
+ goto fail;
+ }
+ wpa_printf(MSG_DEBUG, "TLSv1: Received alert %d:%d",
+ out_pos[0], out_pos[1]);
+ if (out_pos[0] == TLS_ALERT_LEVEL_WARNING) {
+ /* Continue processing */
+ pos += used;
+ continue;
+ }
+
+ alert = out_pos[1];
+ goto fail;
+ }
+
+ if (ct != TLS_CONTENT_TYPE_APPLICATION_DATA) {
+ wpa_printf(MSG_DEBUG, "TLSv1: Unexpected content type "
+ "0x%x when decrypting application data",
+ pos[0]);
+ alert = TLS_ALERT_UNEXPECTED_MESSAGE;
+ goto fail;
+ }
+
+ wpabuf_put(buf, olen);
+
+ pos += used;
}
- return out_pos - out_data;
+ wpabuf_free(conn->partial_input);
+ conn->partial_input = NULL;
+ return buf;
+
+fail:
+ wpabuf_free(buf);
+ wpabuf_free(conn->partial_input);
+ conn->partial_input = NULL;
+ tls_alert(conn, TLS_ALERT_LEVEL_FATAL, alert);
+ return NULL;
}
@@ -351,15 +459,17 @@ struct tlsv1_client * tlsv1_client_init(void)
count = 0;
suites = conn->cipher_suites;
-#ifndef CONFIG_CRYPTO_INTERNAL
+ suites[count++] = TLS_RSA_WITH_AES_256_CBC_SHA256;
suites[count++] = TLS_RSA_WITH_AES_256_CBC_SHA;
-#endif /* CONFIG_CRYPTO_INTERNAL */
+ suites[count++] = TLS_RSA_WITH_AES_128_CBC_SHA256;
suites[count++] = TLS_RSA_WITH_AES_128_CBC_SHA;
suites[count++] = TLS_RSA_WITH_3DES_EDE_CBC_SHA;
suites[count++] = TLS_RSA_WITH_RC4_128_SHA;
suites[count++] = TLS_RSA_WITH_RC4_128_MD5;
conn->num_cipher_suites = count;
+ conn->rl.tls_version = TLS_VERSION;
+
return conn;
}
@@ -378,6 +488,7 @@ void tlsv1_client_deinit(struct tlsv1_client *conn)
os_free(conn->client_hello_ext);
tlsv1_client_free_dh(conn);
tlsv1_cred_free(conn->cred);
+ wpabuf_free(conn->partial_input);
os_free(conn);
}
@@ -421,7 +532,8 @@ int tlsv1_client_prf(struct tlsv1_client *conn, const char *label,
TLS_RANDOM_LEN);
}
- return tls_prf(conn->master_secret, TLS_MASTER_SECRET_LEN,
+ return tls_prf(conn->rl.tls_version,
+ conn->master_secret, TLS_MASTER_SECRET_LEN,
label, seed, 2 * TLS_RANDOM_LEN, out, out_len);
}
@@ -453,15 +565,24 @@ int tlsv1_client_get_cipher(struct tlsv1_client *conn, char *buf,
case TLS_RSA_WITH_3DES_EDE_CBC_SHA:
cipher = "DES-CBC3-SHA";
break;
+ case TLS_DH_anon_WITH_AES_128_CBC_SHA256:
+ cipher = "ADH-AES-128-SHA256";
+ break;
case TLS_DH_anon_WITH_AES_128_CBC_SHA:
cipher = "ADH-AES-128-SHA";
break;
case TLS_RSA_WITH_AES_256_CBC_SHA:
cipher = "AES-256-SHA";
break;
+ case TLS_RSA_WITH_AES_256_CBC_SHA256:
+ cipher = "AES-256-SHA256";
+ break;
case TLS_RSA_WITH_AES_128_CBC_SHA:
cipher = "AES-128-SHA";
break;
+ case TLS_RSA_WITH_AES_128_CBC_SHA256:
+ cipher = "AES-128-SHA256";
+ break;
default:
return -1;
}
@@ -612,9 +733,9 @@ int tlsv1_client_set_cipher_list(struct tlsv1_client *conn, u8 *ciphers)
if (ciphers[0] == TLS_CIPHER_ANON_DH_AES128_SHA) {
count = 0;
suites = conn->cipher_suites;
-#ifndef CONFIG_CRYPTO_INTERNAL
+ suites[count++] = TLS_DH_anon_WITH_AES_256_CBC_SHA256;
suites[count++] = TLS_DH_anon_WITH_AES_256_CBC_SHA;
-#endif /* CONFIG_CRYPTO_INTERNAL */
+ suites[count++] = TLS_DH_anon_WITH_AES_128_CBC_SHA256;
suites[count++] = TLS_DH_anon_WITH_AES_128_CBC_SHA;
suites[count++] = TLS_DH_anon_WITH_3DES_EDE_CBC_SHA;
suites[count++] = TLS_DH_anon_WITH_RC4_128_MD5;
@@ -656,6 +777,12 @@ int tlsv1_client_set_cred(struct tlsv1_client *conn,
}
+void tlsv1_client_set_time_checks(struct tlsv1_client *conn, int enabled)
+{
+ conn->disable_time_checks = !enabled;
+}
+
+
void tlsv1_client_set_session_ticket_cb(struct tlsv1_client *conn,
tlsv1_client_session_ticket_cb cb,
void *ctx)
diff --git a/contrib/wpa/src/tls/tlsv1_client.h b/contrib/wpa/src/tls/tlsv1_client.h
index 16ad57d..8ec85f1 100644
--- a/contrib/wpa/src/tls/tlsv1_client.h
+++ b/contrib/wpa/src/tls/tlsv1_client.h
@@ -1,15 +1,9 @@
/*
- * TLSv1 client (RFC 2246)
- * Copyright (c) 2006-2007, Jouni Malinen <j@w1.fi>
+ * TLS v1.0/v1.1/v1.2 client (RFC 2246, RFC 4346, RFC 5246)
+ * Copyright (c) 2006-2011, Jouni Malinen <j@w1.fi>
*
- * This program is free software; you can redistribute it and/or modify
- * it under the terms of the GNU General Public License version 2 as
- * published by the Free Software Foundation.
- *
- * Alternatively, this software may be distributed under the terms of BSD
- * license.
- *
- * See README and COPYING for more details.
+ * This software may be distributed under the terms of the BSD license.
+ * See README for more details.
*/
#ifndef TLSV1_CLIENT_H
@@ -29,13 +23,13 @@ int tlsv1_client_prf(struct tlsv1_client *conn, const char *label,
u8 * tlsv1_client_handshake(struct tlsv1_client *conn,
const u8 *in_data, size_t in_len,
size_t *out_len, u8 **appl_data,
- size_t *appl_data_len);
+ size_t *appl_data_len, int *need_more_data);
int tlsv1_client_encrypt(struct tlsv1_client *conn,
const u8 *in_data, size_t in_len,
u8 *out_data, size_t out_len);
-int tlsv1_client_decrypt(struct tlsv1_client *conn,
- const u8 *in_data, size_t in_len,
- u8 *out_data, size_t out_len);
+struct wpabuf * tlsv1_client_decrypt(struct tlsv1_client *conn,
+ const u8 *in_data, size_t in_len,
+ int *need_more_data);
int tlsv1_client_get_cipher(struct tlsv1_client *conn, char *buf,
size_t buflen);
int tlsv1_client_shutdown(struct tlsv1_client *conn);
@@ -47,6 +41,7 @@ int tlsv1_client_get_keyblock_size(struct tlsv1_client *conn);
int tlsv1_client_set_cipher_list(struct tlsv1_client *conn, u8 *ciphers);
int tlsv1_client_set_cred(struct tlsv1_client *conn,
struct tlsv1_credentials *cred);
+void tlsv1_client_set_time_checks(struct tlsv1_client *conn, int enabled);
typedef int (*tlsv1_client_session_ticket_cb)
(void *ctx, const u8 *ticket, size_t len, const u8 *client_random,
diff --git a/contrib/wpa/src/tls/tlsv1_client_i.h b/contrib/wpa/src/tls/tlsv1_client_i.h
index 7fe179f..55fdcf8 100644
--- a/contrib/wpa/src/tls/tlsv1_client_i.h
+++ b/contrib/wpa/src/tls/tlsv1_client_i.h
@@ -1,15 +1,9 @@
/*
* TLSv1 client - internal structures
- * Copyright (c) 2006-2007, Jouni Malinen <j@w1.fi>
+ * Copyright (c) 2006-2011, Jouni Malinen <j@w1.fi>
*
- * This program is free software; you can redistribute it and/or modify
- * it under the terms of the GNU General Public License version 2 as
- * published by the Free Software Foundation.
- *
- * Alternatively, this software may be distributed under the terms of BSD
- * license.
- *
- * See README and COPYING for more details.
+ * This software may be distributed under the terms of the BSD license.
+ * See README for more details.
*/
#ifndef TLSV1_CLIENT_I_H
@@ -39,6 +33,7 @@ struct tlsv1_client {
unsigned int session_resumed:1;
unsigned int session_ticket_included:1;
unsigned int use_session_ticket:1;
+ unsigned int disable_time_checks:1;
struct crypto_public_key *server_rsa_key;
@@ -67,6 +62,8 @@ struct tlsv1_client {
tlsv1_client_session_ticket_cb session_ticket_cb;
void *session_ticket_cb_ctx;
+
+ struct wpabuf *partial_input;
};
diff --git a/contrib/wpa/src/tls/tlsv1_client_read.c b/contrib/wpa/src/tls/tlsv1_client_read.c
index ed3f260..3269ecf 100644
--- a/contrib/wpa/src/tls/tlsv1_client_read.c
+++ b/contrib/wpa/src/tls/tlsv1_client_read.c
@@ -1,15 +1,9 @@
/*
* TLSv1 client - read handshake message
- * Copyright (c) 2006-2007, Jouni Malinen <j@w1.fi>
+ * Copyright (c) 2006-2011, Jouni Malinen <j@w1.fi>
*
- * This program is free software; you can redistribute it and/or modify
- * it under the terms of the GNU General Public License version 2 as
- * published by the Free Software Foundation.
- *
- * Alternatively, this software may be distributed under the terms of BSD
- * license.
- *
- * See README and COPYING for more details.
+ * This software may be distributed under the terms of the BSD license.
+ * See README for more details.
*/
#include "includes.h"
@@ -17,6 +11,7 @@
#include "common.h"
#include "crypto/md5.h"
#include "crypto/sha1.h"
+#include "crypto/sha256.h"
#include "crypto/tls.h"
#include "x509v3.h"
#include "tlsv1_common.h"
@@ -38,6 +33,7 @@ static int tls_process_server_hello(struct tlsv1_client *conn, u8 ct,
const u8 *pos, *end;
size_t left, len, i;
u16 cipher_suite;
+ u16 tls_version;
if (ct != TLS_CONTENT_TYPE_HANDSHAKE) {
wpa_printf(MSG_DEBUG, "TLSv1: Expected Handshake; "
@@ -79,15 +75,20 @@ static int tls_process_server_hello(struct tlsv1_client *conn, u8 ct,
/* ProtocolVersion server_version */
if (end - pos < 2)
goto decode_error;
- if (WPA_GET_BE16(pos) != TLS_VERSION) {
+ tls_version = WPA_GET_BE16(pos);
+ if (!tls_version_ok(tls_version)) {
wpa_printf(MSG_DEBUG, "TLSv1: Unexpected protocol version in "
- "ServerHello");
+ "ServerHello %u.%u", pos[0], pos[1]);
tls_alert(conn, TLS_ALERT_LEVEL_FATAL,
TLS_ALERT_PROTOCOL_VERSION);
return -1;
}
pos += 2;
+ wpa_printf(MSG_DEBUG, "TLSv1: Using TLS v%s",
+ tls_version_str(tls_version));
+ conn->rl.tls_version = tls_version;
+
/* Random random */
if (end - pos < TLS_RANDOM_LEN)
goto decode_error;
@@ -365,7 +366,8 @@ static int tls_process_certificate(struct tlsv1_client *conn, u8 ct,
if (conn->cred &&
x509_certificate_chain_validate(conn->cred->trusted_certs, chain,
- &reason) < 0) {
+ &reason, conn->disable_time_checks)
+ < 0) {
int tls_reason;
wpa_printf(MSG_DEBUG, "TLSv1: Server certificate chain "
"validation failed (reason=%d)", reason);
@@ -815,6 +817,21 @@ static int tls_process_server_finished(struct tlsv1_client *conn, u8 ct,
wpa_hexdump(MSG_MSGDUMP, "TLSv1: verify_data in Finished",
pos, TLS_VERIFY_DATA_LEN);
+#ifdef CONFIG_TLSV12
+ if (conn->rl.tls_version >= TLS_VERSION_1_2) {
+ hlen = SHA256_MAC_LEN;
+ if (conn->verify.sha256_server == NULL ||
+ crypto_hash_finish(conn->verify.sha256_server, hash, &hlen)
+ < 0) {
+ tls_alert(conn, TLS_ALERT_LEVEL_FATAL,
+ TLS_ALERT_INTERNAL_ERROR);
+ conn->verify.sha256_server = NULL;
+ return -1;
+ }
+ conn->verify.sha256_server = NULL;
+ } else {
+#endif /* CONFIG_TLSV12 */
+
hlen = MD5_MAC_LEN;
if (conn->verify.md5_server == NULL ||
crypto_hash_finish(conn->verify.md5_server, hash, &hlen) < 0) {
@@ -836,9 +853,15 @@ static int tls_process_server_finished(struct tlsv1_client *conn, u8 ct,
return -1;
}
conn->verify.sha1_server = NULL;
+ hlen = MD5_MAC_LEN + SHA1_MAC_LEN;
+
+#ifdef CONFIG_TLSV12
+ }
+#endif /* CONFIG_TLSV12 */
- if (tls_prf(conn->master_secret, TLS_MASTER_SECRET_LEN,
- "server finished", hash, MD5_MAC_LEN + SHA1_MAC_LEN,
+ if (tls_prf(conn->rl.tls_version,
+ conn->master_secret, TLS_MASTER_SECRET_LEN,
+ "server finished", hash, hlen,
verify_data, TLS_VERIFY_DATA_LEN)) {
wpa_printf(MSG_DEBUG, "TLSv1: Failed to derive verify_data");
tls_alert(conn, TLS_ALERT_LEVEL_FATAL,
diff --git a/contrib/wpa/src/tls/tlsv1_client_write.c b/contrib/wpa/src/tls/tlsv1_client_write.c
index b47425f..d789efb 100644
--- a/contrib/wpa/src/tls/tlsv1_client_write.c
+++ b/contrib/wpa/src/tls/tlsv1_client_write.c
@@ -1,15 +1,9 @@
/*
* TLSv1 client - write handshake message
- * Copyright (c) 2006-2007, Jouni Malinen <j@w1.fi>
+ * Copyright (c) 2006-2011, Jouni Malinen <j@w1.fi>
*
- * This program is free software; you can redistribute it and/or modify
- * it under the terms of the GNU General Public License version 2 as
- * published by the Free Software Foundation.
- *
- * Alternatively, this software may be distributed under the terms of BSD
- * license.
- *
- * See README and COPYING for more details.
+ * This software may be distributed under the terms of the BSD license.
+ * See README for more details.
*/
#include "includes.h"
@@ -17,7 +11,9 @@
#include "common.h"
#include "crypto/md5.h"
#include "crypto/sha1.h"
+#include "crypto/sha256.h"
#include "crypto/tls.h"
+#include "crypto/random.h"
#include "x509v3.h"
#include "tlsv1_common.h"
#include "tlsv1_record.h"
@@ -57,7 +53,7 @@ u8 * tls_send_client_hello(struct tlsv1_client *conn, size_t *out_len)
os_get_time(&now);
WPA_PUT_BE32(conn->client_random, now.sec);
- if (os_get_random(conn->client_random + 4, TLS_RANDOM_LEN - 4)) {
+ if (random_get_bytes(conn->client_random + 4, TLS_RANDOM_LEN - 4)) {
wpa_printf(MSG_ERROR, "TLSv1: Could not generate "
"client_random");
return NULL;
@@ -115,7 +111,8 @@ u8 * tls_send_client_hello(struct tlsv1_client *conn, size_t *out_len)
tls_verify_hash_add(&conn->verify, hs_start, pos - hs_start);
if (tlsv1_record_send(&conn->rl, TLS_CONTENT_TYPE_HANDSHAKE,
- rhdr, end - rhdr, pos - hs_start, out_len) < 0) {
+ rhdr, end - rhdr, hs_start, pos - hs_start,
+ out_len) < 0) {
wpa_printf(MSG_DEBUG, "TLSv1: Failed to create TLS record");
tls_alert(conn, TLS_ALERT_LEVEL_FATAL,
TLS_ALERT_INTERNAL_ERROR);
@@ -191,7 +188,8 @@ static int tls_write_client_certificate(struct tlsv1_client *conn,
WPA_PUT_BE24(hs_length, pos - hs_length - 3);
if (tlsv1_record_send(&conn->rl, TLS_CONTENT_TYPE_HANDSHAKE,
- rhdr, end - rhdr, pos - hs_start, &rlen) < 0) {
+ rhdr, end - rhdr, hs_start, pos - hs_start,
+ &rlen) < 0) {
wpa_printf(MSG_DEBUG, "TLSv1: Failed to generate a record");
tls_alert(conn, TLS_ALERT_LEVEL_FATAL,
TLS_ALERT_INTERNAL_ERROR);
@@ -222,7 +220,7 @@ static int tlsv1_key_x_anon_dh(struct tlsv1_client *conn, u8 **pos, u8 *end)
TLS_ALERT_INTERNAL_ERROR);
return -1;
}
- if (os_get_random(csecret, csecret_len)) {
+ if (random_get_bytes(csecret, csecret_len)) {
wpa_printf(MSG_DEBUG, "TLSv1: Failed to get random "
"data for Diffie-Hellman");
tls_alert(conn, TLS_ALERT_LEVEL_FATAL,
@@ -412,7 +410,8 @@ static int tls_write_client_key_exchange(struct tlsv1_client *conn,
WPA_PUT_BE24(hs_length, pos - hs_length - 3);
if (tlsv1_record_send(&conn->rl, TLS_CONTENT_TYPE_HANDSHAKE,
- rhdr, end - rhdr, pos - hs_start, &rlen) < 0) {
+ rhdr, end - rhdr, hs_start, pos - hs_start,
+ &rlen) < 0) {
wpa_printf(MSG_DEBUG, "TLSv1: Failed to create a record");
tls_alert(conn, TLS_ALERT_LEVEL_FATAL,
TLS_ALERT_INTERNAL_ERROR);
@@ -432,7 +431,7 @@ static int tls_write_client_certificate_verify(struct tlsv1_client *conn,
{
u8 *pos, *rhdr, *hs_start, *hs_length, *signed_start;
size_t rlen, hlen, clen;
- u8 hash[MD5_MAC_LEN + SHA1_MAC_LEN], *hpos;
+ u8 hash[100], *hpos;
enum { SIGN_ALG_RSA, SIGN_ALG_DSA } alg = SIGN_ALG_RSA;
pos = *msgpos;
@@ -472,6 +471,40 @@ static int tls_write_client_certificate_verify(struct tlsv1_client *conn,
hpos = hash;
+#ifdef CONFIG_TLSV12
+ if (conn->rl.tls_version == TLS_VERSION_1_2) {
+ hlen = SHA256_MAC_LEN;
+ if (conn->verify.sha256_cert == NULL ||
+ crypto_hash_finish(conn->verify.sha256_cert, hpos, &hlen) <
+ 0) {
+ conn->verify.sha256_cert = NULL;
+ tls_alert(conn, TLS_ALERT_LEVEL_FATAL,
+ TLS_ALERT_INTERNAL_ERROR);
+ return -1;
+ }
+ conn->verify.sha256_cert = NULL;
+
+ /*
+ * RFC 3447, A.2.4 RSASSA-PKCS1-v1_5
+ *
+ * DigestInfo ::= SEQUENCE {
+ * digestAlgorithm DigestAlgorithm,
+ * digest OCTET STRING
+ * }
+ *
+ * SHA-256 OID: sha256WithRSAEncryption ::= {pkcs-1 11}
+ *
+ * DER encoded DigestInfo for SHA256 per RFC 3447:
+ * 30 31 30 0d 06 09 60 86 48 01 65 03 04 02 01 05 00 04 20 ||
+ * H
+ */
+ os_memmove(hash + 19, hash, hlen);
+ hlen += 19;
+ os_memcpy(hash, "\x30\x31\x30\x0d\x06\x09\x60\x86\x48\x01\x65"
+ "\x03\x04\x02\x01\x05\x00\x04\x20", 19);
+ } else {
+#endif /* CONFIG_TLSV12 */
+
if (alg == SIGN_ALG_RSA) {
hlen = MD5_MAC_LEN;
if (conn->verify.md5_cert == NULL ||
@@ -502,8 +535,29 @@ static int tls_write_client_certificate_verify(struct tlsv1_client *conn,
if (alg == SIGN_ALG_RSA)
hlen += MD5_MAC_LEN;
+#ifdef CONFIG_TLSV12
+ }
+#endif /* CONFIG_TLSV12 */
+
wpa_hexdump(MSG_MSGDUMP, "TLSv1: CertificateVerify hash", hash, hlen);
+#ifdef CONFIG_TLSV12
+ if (conn->rl.tls_version >= TLS_VERSION_1_2) {
+ /*
+ * RFC 5246, 4.7:
+ * TLS v1.2 adds explicit indication of the used signature and
+ * hash algorithms.
+ *
+ * struct {
+ * HashAlgorithm hash;
+ * SignatureAlgorithm signature;
+ * } SignatureAndHashAlgorithm;
+ */
+ *pos++ = TLS_HASH_ALG_SHA256;
+ *pos++ = TLS_SIGN_ALG_RSA;
+ }
+#endif /* CONFIG_TLSV12 */
+
/*
* RFC 2246, 4.7:
* In digital signing, one-way hash functions are used as input for a
@@ -533,7 +587,8 @@ static int tls_write_client_certificate_verify(struct tlsv1_client *conn,
WPA_PUT_BE24(hs_length, pos - hs_length - 3);
if (tlsv1_record_send(&conn->rl, TLS_CONTENT_TYPE_HANDSHAKE,
- rhdr, end - rhdr, pos - hs_start, &rlen) < 0) {
+ rhdr, end - rhdr, hs_start, pos - hs_start,
+ &rlen) < 0) {
wpa_printf(MSG_DEBUG, "TLSv1: Failed to generate a record");
tls_alert(conn, TLS_ALERT_LEVEL_FATAL,
TLS_ALERT_INTERNAL_ERROR);
@@ -552,17 +607,16 @@ static int tls_write_client_certificate_verify(struct tlsv1_client *conn,
static int tls_write_client_change_cipher_spec(struct tlsv1_client *conn,
u8 **msgpos, u8 *end)
{
- u8 *pos, *rhdr;
size_t rlen;
-
- pos = *msgpos;
+ u8 payload[1];
wpa_printf(MSG_DEBUG, "TLSv1: Send ChangeCipherSpec");
- rhdr = pos;
- pos += TLS_RECORD_HEADER_LEN;
- *pos = TLS_CHANGE_CIPHER_SPEC;
+
+ payload[0] = TLS_CHANGE_CIPHER_SPEC;
+
if (tlsv1_record_send(&conn->rl, TLS_CONTENT_TYPE_CHANGE_CIPHER_SPEC,
- rhdr, end - rhdr, 1, &rlen) < 0) {
+ *msgpos, end - *msgpos, payload, sizeof(payload),
+ &rlen) < 0) {
wpa_printf(MSG_DEBUG, "TLSv1: Failed to create a record");
tls_alert(conn, TLS_ALERT_LEVEL_FATAL,
TLS_ALERT_INTERNAL_ERROR);
@@ -577,7 +631,7 @@ static int tls_write_client_change_cipher_spec(struct tlsv1_client *conn,
return -1;
}
- *msgpos = rhdr + rlen;
+ *msgpos += rlen;
return 0;
}
@@ -586,17 +640,30 @@ static int tls_write_client_change_cipher_spec(struct tlsv1_client *conn,
static int tls_write_client_finished(struct tlsv1_client *conn,
u8 **msgpos, u8 *end)
{
- u8 *pos, *rhdr, *hs_start, *hs_length;
+ u8 *pos, *hs_start;
size_t rlen, hlen;
- u8 verify_data[TLS_VERIFY_DATA_LEN];
+ u8 verify_data[1 + 3 + TLS_VERIFY_DATA_LEN];
u8 hash[MD5_MAC_LEN + SHA1_MAC_LEN];
- pos = *msgpos;
-
wpa_printf(MSG_DEBUG, "TLSv1: Send Finished");
/* Encrypted Handshake Message: Finished */
+#ifdef CONFIG_TLSV12
+ if (conn->rl.tls_version >= TLS_VERSION_1_2) {
+ hlen = SHA256_MAC_LEN;
+ if (conn->verify.sha256_client == NULL ||
+ crypto_hash_finish(conn->verify.sha256_client, hash, &hlen)
+ < 0) {
+ tls_alert(conn, TLS_ALERT_LEVEL_FATAL,
+ TLS_ALERT_INTERNAL_ERROR);
+ conn->verify.sha256_client = NULL;
+ return -1;
+ }
+ conn->verify.sha256_client = NULL;
+ } else {
+#endif /* CONFIG_TLSV12 */
+
hlen = MD5_MAC_LEN;
if (conn->verify.md5_client == NULL ||
crypto_hash_finish(conn->verify.md5_client, hash, &hlen) < 0) {
@@ -618,43 +685,44 @@ static int tls_write_client_finished(struct tlsv1_client *conn,
return -1;
}
conn->verify.sha1_client = NULL;
+ hlen = MD5_MAC_LEN + SHA1_MAC_LEN;
+
+#ifdef CONFIG_TLSV12
+ }
+#endif /* CONFIG_TLSV12 */
- if (tls_prf(conn->master_secret, TLS_MASTER_SECRET_LEN,
- "client finished", hash, MD5_MAC_LEN + SHA1_MAC_LEN,
- verify_data, TLS_VERIFY_DATA_LEN)) {
+ if (tls_prf(conn->rl.tls_version,
+ conn->master_secret, TLS_MASTER_SECRET_LEN,
+ "client finished", hash, hlen,
+ verify_data + 1 + 3, TLS_VERIFY_DATA_LEN)) {
wpa_printf(MSG_DEBUG, "TLSv1: Failed to generate verify_data");
tls_alert(conn, TLS_ALERT_LEVEL_FATAL,
TLS_ALERT_INTERNAL_ERROR);
return -1;
}
wpa_hexdump_key(MSG_DEBUG, "TLSv1: verify_data (client)",
- verify_data, TLS_VERIFY_DATA_LEN);
+ verify_data + 1 + 3, TLS_VERIFY_DATA_LEN);
- rhdr = pos;
- pos += TLS_RECORD_HEADER_LEN;
/* Handshake */
- hs_start = pos;
+ pos = hs_start = verify_data;
/* HandshakeType msg_type */
*pos++ = TLS_HANDSHAKE_TYPE_FINISHED;
- /* uint24 length (to be filled) */
- hs_length = pos;
+ /* uint24 length */
+ WPA_PUT_BE24(pos, TLS_VERIFY_DATA_LEN);
pos += 3;
- os_memcpy(pos, verify_data, TLS_VERIFY_DATA_LEN);
- pos += TLS_VERIFY_DATA_LEN;
- WPA_PUT_BE24(hs_length, pos - hs_length - 3);
+ pos += TLS_VERIFY_DATA_LEN; /* verify_data already in place */
tls_verify_hash_add(&conn->verify, hs_start, pos - hs_start);
if (tlsv1_record_send(&conn->rl, TLS_CONTENT_TYPE_HANDSHAKE,
- rhdr, end - rhdr, pos - hs_start, &rlen) < 0) {
+ *msgpos, end - *msgpos, hs_start, pos - hs_start,
+ &rlen) < 0) {
wpa_printf(MSG_DEBUG, "TLSv1: Failed to create a record");
tls_alert(conn, TLS_ALERT_LEVEL_FATAL,
TLS_ALERT_INTERNAL_ERROR);
return -1;
}
- pos = rhdr + rlen;
-
- *msgpos = pos;
+ *msgpos += rlen;
return 0;
}
@@ -668,7 +736,7 @@ static u8 * tls_send_client_key_exchange(struct tlsv1_client *conn,
*out_len = 0;
- msglen = 1000;
+ msglen = 2000;
if (conn->certificate_requested)
msglen += tls_client_cert_chain_der_len(conn);
@@ -777,7 +845,8 @@ u8 * tlsv1_client_send_alert(struct tlsv1_client *conn, u8 level,
/* ContentType type */
*pos++ = TLS_CONTENT_TYPE_ALERT;
/* ProtocolVersion version */
- WPA_PUT_BE16(pos, TLS_VERSION);
+ WPA_PUT_BE16(pos, conn->rl.tls_version ? conn->rl.tls_version :
+ TLS_VERSION);
pos += 2;
/* uint16 length (to be filled) */
length = pos;
diff --git a/contrib/wpa/src/tls/tlsv1_common.c b/contrib/wpa/src/tls/tlsv1_common.c
index 2f9dd0f..d212862 100644
--- a/contrib/wpa/src/tls/tlsv1_common.c
+++ b/contrib/wpa/src/tls/tlsv1_common.c
@@ -1,20 +1,16 @@
/*
* TLSv1 common routines
- * Copyright (c) 2006-2007, Jouni Malinen <j@w1.fi>
+ * Copyright (c) 2006-2011, Jouni Malinen <j@w1.fi>
*
- * This program is free software; you can redistribute it and/or modify
- * it under the terms of the GNU General Public License version 2 as
- * published by the Free Software Foundation.
- *
- * Alternatively, this software may be distributed under the terms of BSD
- * license.
- *
- * See README and COPYING for more details.
+ * This software may be distributed under the terms of the BSD license.
+ * See README for more details.
*/
#include "includes.h"
#include "common.h"
+#include "crypto/sha1.h"
+#include "crypto/sha256.h"
#include "x509v3.h"
#include "tlsv1_common.h"
@@ -50,7 +46,15 @@ static const struct tls_cipher_suite tls_cipher_suites[] = {
{ TLS_RSA_WITH_AES_256_CBC_SHA, TLS_KEY_X_RSA, TLS_CIPHER_AES_256_CBC,
TLS_HASH_SHA },
{ TLS_DH_anon_WITH_AES_256_CBC_SHA, TLS_KEY_X_DH_anon,
- TLS_CIPHER_AES_256_CBC, TLS_HASH_SHA }
+ TLS_CIPHER_AES_256_CBC, TLS_HASH_SHA },
+ { TLS_RSA_WITH_AES_128_CBC_SHA256, TLS_KEY_X_RSA,
+ TLS_CIPHER_AES_128_CBC, TLS_HASH_SHA256 },
+ { TLS_RSA_WITH_AES_256_CBC_SHA256, TLS_KEY_X_RSA,
+ TLS_CIPHER_AES_256_CBC, TLS_HASH_SHA256 },
+ { TLS_DH_anon_WITH_AES_128_CBC_SHA256, TLS_KEY_X_DH_anon,
+ TLS_CIPHER_AES_128_CBC, TLS_HASH_SHA256 },
+ { TLS_DH_anon_WITH_AES_256_CBC_SHA256, TLS_KEY_X_DH_anon,
+ TLS_CIPHER_AES_256_CBC, TLS_HASH_SHA256 }
};
#define NUM_ELEMS(a) (sizeof(a) / sizeof((a)[0]))
@@ -202,6 +206,19 @@ int tls_verify_hash_init(struct tls_verify_hash *verify)
tls_verify_hash_free(verify);
return -1;
}
+#ifdef CONFIG_TLSV12
+ verify->sha256_client = crypto_hash_init(CRYPTO_HASH_ALG_SHA256, NULL,
+ 0);
+ verify->sha256_server = crypto_hash_init(CRYPTO_HASH_ALG_SHA256, NULL,
+ 0);
+ verify->sha256_cert = crypto_hash_init(CRYPTO_HASH_ALG_SHA256, NULL,
+ 0);
+ if (verify->sha256_client == NULL || verify->sha256_server == NULL ||
+ verify->sha256_cert == NULL) {
+ tls_verify_hash_free(verify);
+ return -1;
+ }
+#endif /* CONFIG_TLSV12 */
return 0;
}
@@ -221,6 +238,14 @@ void tls_verify_hash_add(struct tls_verify_hash *verify, const u8 *buf,
crypto_hash_update(verify->md5_cert, buf, len);
crypto_hash_update(verify->sha1_cert, buf, len);
}
+#ifdef CONFIG_TLSV12
+ if (verify->sha256_client)
+ crypto_hash_update(verify->sha256_client, buf, len);
+ if (verify->sha256_server)
+ crypto_hash_update(verify->sha256_server, buf, len);
+ if (verify->sha256_cert)
+ crypto_hash_update(verify->sha256_cert, buf, len);
+#endif /* CONFIG_TLSV12 */
}
@@ -238,4 +263,60 @@ void tls_verify_hash_free(struct tls_verify_hash *verify)
verify->sha1_client = NULL;
verify->sha1_server = NULL;
verify->sha1_cert = NULL;
+#ifdef CONFIG_TLSV12
+ crypto_hash_finish(verify->sha256_client, NULL, NULL);
+ crypto_hash_finish(verify->sha256_server, NULL, NULL);
+ crypto_hash_finish(verify->sha256_cert, NULL, NULL);
+ verify->sha256_client = NULL;
+ verify->sha256_server = NULL;
+ verify->sha256_cert = NULL;
+#endif /* CONFIG_TLSV12 */
+}
+
+
+int tls_version_ok(u16 ver)
+{
+ if (ver == TLS_VERSION_1)
+ return 1;
+#ifdef CONFIG_TLSV11
+ if (ver == TLS_VERSION_1_1)
+ return 1;
+#endif /* CONFIG_TLSV11 */
+#ifdef CONFIG_TLSV12
+ if (ver == TLS_VERSION_1_2)
+ return 1;
+#endif /* CONFIG_TLSV12 */
+
+ return 0;
+}
+
+
+const char * tls_version_str(u16 ver)
+{
+ switch (ver) {
+ case TLS_VERSION_1:
+ return "1.0";
+ case TLS_VERSION_1_1:
+ return "1.1";
+ case TLS_VERSION_1_2:
+ return "1.2";
+ }
+
+ return "?";
+}
+
+
+int tls_prf(u16 ver, const u8 *secret, size_t secret_len, const char *label,
+ const u8 *seed, size_t seed_len, u8 *out, size_t outlen)
+{
+#ifdef CONFIG_TLSV12
+ if (ver >= TLS_VERSION_1_2) {
+ tls_prf_sha256(secret, secret_len, label, seed, seed_len,
+ out, outlen);
+ return 0;
+ }
+#endif /* CONFIG_TLSV12 */
+
+ return tls_prf_sha1_md5(secret, secret_len, label, seed, seed_len, out,
+ outlen);
}
diff --git a/contrib/wpa/src/tls/tlsv1_common.h b/contrib/wpa/src/tls/tlsv1_common.h
index 763a4af..f28c0cd 100644
--- a/contrib/wpa/src/tls/tlsv1_common.h
+++ b/contrib/wpa/src/tls/tlsv1_common.h
@@ -1,15 +1,9 @@
/*
* TLSv1 common definitions
- * Copyright (c) 2006-2007, Jouni Malinen <j@w1.fi>
+ * Copyright (c) 2006-2011, Jouni Malinen <j@w1.fi>
*
- * This program is free software; you can redistribute it and/or modify
- * it under the terms of the GNU General Public License version 2 as
- * published by the Free Software Foundation.
- *
- * Alternatively, this software may be distributed under the terms of BSD
- * license.
- *
- * See README and COPYING for more details.
+ * This software may be distributed under the terms of the BSD license.
+ * See README for more details.
*/
#ifndef TLSV1_COMMON_H
@@ -17,7 +11,18 @@
#include "crypto/crypto.h"
-#define TLS_VERSION 0x0301 /* TLSv1 */
+#define TLS_VERSION_1 0x0301 /* TLSv1 */
+#define TLS_VERSION_1_1 0x0302 /* TLSv1.1 */
+#define TLS_VERSION_1_2 0x0303 /* TLSv1.2 */
+#ifdef CONFIG_TLSV12
+#define TLS_VERSION TLS_VERSION_1_2
+#else /* CONFIG_TLSV12 */
+#ifdef CONFIG_TLSV11
+#define TLS_VERSION TLS_VERSION_1_1
+#else /* CONFIG_TLSV11 */
+#define TLS_VERSION TLS_VERSION_1
+#endif /* CONFIG_TLSV11 */
+#endif /* CONFIG_TLSV12 */
#define TLS_RANDOM_LEN 32
#define TLS_PRE_MASTER_SECRET_LEN 48
#define TLS_MASTER_SECRET_LEN 48
@@ -82,10 +87,42 @@ enum {
#define TLS_DHE_DSS_WITH_AES_256_CBC_SHA 0x0038 /* RFC 3268 */
#define TLS_DHE_RSA_WITH_AES_256_CBC_SHA 0x0039 /* RFC 3268 */
#define TLS_DH_anon_WITH_AES_256_CBC_SHA 0x003A /* RFC 3268 */
+#define TLS_RSA_WITH_NULL_SHA256 0x003B /* RFC 5246 */
+#define TLS_RSA_WITH_AES_128_CBC_SHA256 0x003C /* RFC 5246 */
+#define TLS_RSA_WITH_AES_256_CBC_SHA256 0x003D /* RFC 5246 */
+#define TLS_DH_DSS_WITH_AES_128_CBC_SHA256 0x003E /* RFC 5246 */
+#define TLS_DH_RSA_WITH_AES_128_CBC_SHA256 0x003F /* RFC 5246 */
+#define TLS_DHE_DSS_WITH_AES_128_CBC_SHA256 0x0040 /* RFC 5246 */
+#define TLS_DHE_RSA_WITH_AES_128_CBC_SHA256 0x0067 /* RFC 5246 */
+#define TLS_DH_DSS_WITH_AES_256_CBC_SHA256 0x0068 /* RFC 5246 */
+#define TLS_DH_RSA_WITH_AES_256_CBC_SHA256 0x0069 /* RFC 5246 */
+#define TLS_DHE_DSS_WITH_AES_256_CBC_SHA256 0x006A /* RFC 5246 */
+#define TLS_DHE_RSA_WITH_AES_256_CBC_SHA256 0x006B /* RFC 5246 */
+#define TLS_DH_anon_WITH_AES_128_CBC_SHA256 0x006C /* RFC 5246 */
+#define TLS_DH_anon_WITH_AES_256_CBC_SHA256 0x006D /* RFC 5246 */
/* CompressionMethod */
#define TLS_COMPRESSION_NULL 0
+/* HashAlgorithm */
+enum {
+ TLS_HASH_ALG_NONE = 0,
+ TLS_HASH_ALG_MD5 = 1,
+ TLS_HASH_ALG_SHA1 = 2,
+ TLS_HASH_ALG_SHA224 = 3,
+ TLS_HASH_ALG_SHA256 = 4,
+ TLS_HASH_ALG_SHA384 = 5,
+ TLS_HASH_ALG_SHA512 = 6
+};
+
+/* SignatureAlgorithm */
+enum {
+ TLS_SIGN_ALG_ANONYMOUS = 0,
+ TLS_SIGN_ALG_RSA = 1,
+ TLS_SIGN_ALG_DSA = 2,
+ TLS_SIGN_ALG_ECDSA = 3,
+};
+
/* AlertLevel */
#define TLS_ALERT_LEVEL_WARNING 1
#define TLS_ALERT_LEVEL_FATAL 2
@@ -169,7 +206,8 @@ typedef enum {
typedef enum {
TLS_HASH_NULL,
TLS_HASH_MD5,
- TLS_HASH_SHA
+ TLS_HASH_SHA,
+ TLS_HASH_SHA256
} tls_hash;
struct tls_cipher_suite {
@@ -197,10 +235,13 @@ struct tls_cipher_data {
struct tls_verify_hash {
struct crypto_hash *md5_client;
struct crypto_hash *sha1_client;
+ struct crypto_hash *sha256_client;
struct crypto_hash *md5_server;
struct crypto_hash *sha1_server;
+ struct crypto_hash *sha256_server;
struct crypto_hash *md5_cert;
struct crypto_hash *sha1_cert;
+ struct crypto_hash *sha256_cert;
};
@@ -212,5 +253,9 @@ int tls_verify_hash_init(struct tls_verify_hash *verify);
void tls_verify_hash_add(struct tls_verify_hash *verify, const u8 *buf,
size_t len);
void tls_verify_hash_free(struct tls_verify_hash *verify);
+int tls_version_ok(u16 ver);
+const char * tls_version_str(u16 ver);
+int tls_prf(u16 ver, const u8 *secret, size_t secret_len, const char *label,
+ const u8 *seed, size_t seed_len, u8 *out, size_t outlen);
#endif /* TLSV1_COMMON_H */
diff --git a/contrib/wpa/src/tls/tlsv1_cred.c b/contrib/wpa/src/tls/tlsv1_cred.c
index aa467ef..1ea6827 100644
--- a/contrib/wpa/src/tls/tlsv1_cred.c
+++ b/contrib/wpa/src/tls/tlsv1_cred.c
@@ -2,14 +2,8 @@
* TLSv1 credentials
* Copyright (c) 2006-2009, Jouni Malinen <j@w1.fi>
*
- * This program is free software; you can redistribute it and/or modify
- * it under the terms of the GNU General Public License version 2 as
- * published by the Free Software Foundation.
- *
- * Alternatively, this software may be distributed under the terms of BSD
- * license.
- *
- * See README and COPYING for more details.
+ * This software may be distributed under the terms of the BSD license.
+ * See README for more details.
*/
#include "includes.h"
@@ -46,7 +40,7 @@ void tlsv1_cred_free(struct tlsv1_credentials *cred)
static int tlsv1_add_cert_der(struct x509_certificate **chain,
const u8 *buf, size_t len)
{
- struct x509_certificate *cert;
+ struct x509_certificate *cert, *p;
char name[128];
cert = x509_certificate_parse(buf, len);
@@ -56,8 +50,20 @@ static int tlsv1_add_cert_der(struct x509_certificate **chain,
return -1;
}
- cert->next = *chain;
- *chain = cert;
+ p = *chain;
+ while (p && p->next)
+ p = p->next;
+ if (p && x509_name_compare(&cert->subject, &p->issuer) == 0) {
+ /*
+ * The new certificate is the issuer of the last certificate in
+ * the chain - add the new certificate to the end.
+ */
+ p->next = cert;
+ } else {
+ /* Add to the beginning of the chain */
+ cert->next = *chain;
+ *chain = cert;
+ }
x509_name_string(&cert->subject, name, sizeof(name));
wpa_printf(MSG_DEBUG, "TLSv1: Added certificate: %s", name);
@@ -232,10 +238,17 @@ static struct crypto_private_key * tlsv1_set_key_pem(const u8 *key, size_t len)
if (!end)
return NULL;
} else {
+ const u8 *pos2;
pos += os_strlen(pem_key_begin);
end = search_tag(pem_key_end, pos, key + len - pos);
if (!end)
return NULL;
+ pos2 = search_tag("Proc-Type: 4,ENCRYPTED", pos, end - pos);
+ if (pos2) {
+ wpa_printf(MSG_DEBUG, "TLSv1: Unsupported private key "
+ "format (Proc-Type/DEK-Info)");
+ return NULL;
+ }
}
der = base64_decode(pos, end - pos, &der_len);
diff --git a/contrib/wpa/src/tls/tlsv1_cred.h b/contrib/wpa/src/tls/tlsv1_cred.h
index 8425fe4..68fbdc9 100644
--- a/contrib/wpa/src/tls/tlsv1_cred.h
+++ b/contrib/wpa/src/tls/tlsv1_cred.h
@@ -2,14 +2,8 @@
* TLSv1 credentials
* Copyright (c) 2006-2007, Jouni Malinen <j@w1.fi>
*
- * This program is free software; you can redistribute it and/or modify
- * it under the terms of the GNU General Public License version 2 as
- * published by the Free Software Foundation.
- *
- * Alternatively, this software may be distributed under the terms of BSD
- * license.
- *
- * See README and COPYING for more details.
+ * This software may be distributed under the terms of the BSD license.
+ * See README for more details.
*/
#ifndef TLSV1_CRED_H
diff --git a/contrib/wpa/src/tls/tlsv1_record.c b/contrib/wpa/src/tls/tlsv1_record.c
index e811f0e..3bec3be 100644
--- a/contrib/wpa/src/tls/tlsv1_record.c
+++ b/contrib/wpa/src/tls/tlsv1_record.c
@@ -1,15 +1,9 @@
/*
* TLSv1 Record Protocol
- * Copyright (c) 2006-2007, Jouni Malinen <j@w1.fi>
+ * Copyright (c) 2006-2011, Jouni Malinen <j@w1.fi>
*
- * This program is free software; you can redistribute it and/or modify
- * it under the terms of the GNU General Public License version 2 as
- * published by the Free Software Foundation.
- *
- * Alternatively, this software may be distributed under the terms of BSD
- * license.
- *
- * See README and COPYING for more details.
+ * This software may be distributed under the terms of the BSD license.
+ * See README for more details.
*/
#include "includes.h"
@@ -17,6 +11,7 @@
#include "common.h"
#include "crypto/md5.h"
#include "crypto/sha1.h"
+#include "crypto/sha256.h"
#include "tlsv1_common.h"
#include "tlsv1_record.h"
@@ -52,6 +47,9 @@ int tlsv1_record_set_cipher_suite(struct tlsv1_record_layer *rl,
} else if (suite->hash == TLS_HASH_SHA) {
rl->hash_alg = CRYPTO_HASH_ALG_HMAC_SHA1;
rl->hash_size = SHA1_MAC_LEN;
+ } else if (suite->hash == TLS_HASH_SHA256) {
+ rl->hash_alg = CRYPTO_HASH_ALG_HMAC_SHA256;
+ rl->hash_size = SHA256_MAC_LEN;
}
data = tls_get_cipher_data(suite->cipher);
@@ -138,10 +136,10 @@ int tlsv1_record_change_read_cipher(struct tlsv1_record_layer *rl)
* tlsv1_record_send - TLS record layer: Send a message
* @rl: Pointer to TLS record layer data
* @content_type: Content type (TLS_CONTENT_TYPE_*)
- * @buf: Buffer to send (with TLS_RECORD_HEADER_LEN octets reserved in the
- * beginning for record layer to fill in; payload filled in after this and
- * extra space in the end for HMAC).
+ * @buf: Buffer for the generated TLS message (needs to have extra space for
+ * header, IV (TLS v1.1), and HMAC)
* @buf_size: Maximum buf size
+ * @payload: Payload to be sent
* @payload_len: Length of the payload
* @out_len: Buffer for returning the used buf length
* Returns: 0 on success, -1 on failure
@@ -150,29 +148,62 @@ int tlsv1_record_change_read_cipher(struct tlsv1_record_layer *rl)
* the data using the current write cipher.
*/
int tlsv1_record_send(struct tlsv1_record_layer *rl, u8 content_type, u8 *buf,
- size_t buf_size, size_t payload_len, size_t *out_len)
+ size_t buf_size, const u8 *payload, size_t payload_len,
+ size_t *out_len)
{
- u8 *pos, *ct_start, *length, *payload;
+ u8 *pos, *ct_start, *length, *cpayload;
struct crypto_hash *hmac;
size_t clen;
+ int explicit_iv;
pos = buf;
+ if (pos + TLS_RECORD_HEADER_LEN > buf + buf_size)
+ return -1;
+
/* ContentType type */
ct_start = pos;
*pos++ = content_type;
/* ProtocolVersion version */
- WPA_PUT_BE16(pos, TLS_VERSION);
+ WPA_PUT_BE16(pos, rl->tls_version);
pos += 2;
/* uint16 length */
length = pos;
WPA_PUT_BE16(length, payload_len);
pos += 2;
- /* opaque fragment[TLSPlaintext.length] */
- payload = pos;
+ cpayload = pos;
+ explicit_iv = rl->write_cipher_suite != TLS_NULL_WITH_NULL_NULL &&
+ rl->iv_size && rl->tls_version >= TLS_VERSION_1_1;
+ if (explicit_iv) {
+ /* opaque IV[Cipherspec.block_length] */
+ if (pos + rl->iv_size > buf + buf_size)
+ return -1;
+
+ /*
+ * Use random number R per the RFC 4346, 6.2.3.2 CBC Block
+ * Cipher option 2a.
+ */
+
+ if (os_get_random(pos, rl->iv_size))
+ return -1;
+ pos += rl->iv_size;
+ }
+
+ /*
+ * opaque fragment[TLSPlaintext.length]
+ * (opaque content[TLSCompressed.length] in GenericBlockCipher)
+ */
+ if (pos + payload_len > buf + buf_size)
+ return -1;
+ os_memmove(pos, payload, payload_len);
pos += payload_len;
if (rl->write_cipher_suite != TLS_NULL_WITH_NULL_NULL) {
+ /*
+ * MAC calculated over seq_num + TLSCompressed.type +
+ * TLSCompressed.version + TLSCompressed.length +
+ * TLSCompressed.fragment
+ */
hmac = crypto_hash_init(rl->hash_alg, rl->write_mac_secret,
rl->hash_size);
if (hmac == NULL) {
@@ -182,7 +213,8 @@ int tlsv1_record_send(struct tlsv1_record_layer *rl, u8 content_type, u8 *buf,
}
crypto_hash_update(hmac, rl->write_seq_num, TLS_SEQ_NUM_LEN);
/* type + version + length + fragment */
- crypto_hash_update(hmac, ct_start, pos - ct_start);
+ crypto_hash_update(hmac, ct_start, TLS_RECORD_HEADER_LEN);
+ crypto_hash_update(hmac, payload, payload_len);
clen = buf + buf_size - pos;
if (clen < rl->hash_size) {
wpa_printf(MSG_DEBUG, "TLSv1: Record Layer - Not "
@@ -200,7 +232,7 @@ int tlsv1_record_send(struct tlsv1_record_layer *rl, u8 content_type, u8 *buf,
pos, clen);
pos += clen;
if (rl->iv_size) {
- size_t len = pos - payload;
+ size_t len = pos - cpayload;
size_t pad;
pad = (len + 1) % rl->iv_size;
if (pad)
@@ -214,8 +246,8 @@ int tlsv1_record_send(struct tlsv1_record_layer *rl, u8 content_type, u8 *buf,
pos += pad + 1;
}
- if (crypto_cipher_encrypt(rl->write_cbc, payload,
- payload, pos - payload) < 0)
+ if (crypto_cipher_encrypt(rl->write_cbc, cpayload,
+ cpayload, pos - cpayload) < 0)
return -1;
}
@@ -237,7 +269,8 @@ int tlsv1_record_send(struct tlsv1_record_layer *rl, u8 content_type, u8 *buf,
* @out_len: Set to maximum out_data length by caller; used to return the
* length of the used data
* @alert: Buffer for returning an alert value on failure
- * Returns: 0 on success, -1 on failure
+ * Returns: Number of bytes used from in_data on success, 0 if record was not
+ * complete (more data needed), or -1 on failure
*
* This function decrypts the received message, verifies HMAC and TLS record
* layer header.
@@ -250,40 +283,35 @@ int tlsv1_record_receive(struct tlsv1_record_layer *rl,
u8 padlen;
struct crypto_hash *hmac;
u8 len[2], hash[100];
-
- wpa_hexdump(MSG_MSGDUMP, "TLSv1: Record Layer - Received",
- in_data, in_len);
+ int force_mac_error = 0;
+ u8 ct;
if (in_len < TLS_RECORD_HEADER_LEN) {
- wpa_printf(MSG_DEBUG, "TLSv1: Too short record (in_len=%lu)",
+ wpa_printf(MSG_DEBUG, "TLSv1: Too short record (in_len=%lu) - "
+ "need more data",
(unsigned long) in_len);
- *alert = TLS_ALERT_DECODE_ERROR;
- return -1;
+ wpa_hexdump(MSG_MSGDUMP, "TLSv1: Record Layer - Received",
+ in_data, in_len);
+ return 0;
}
+ ct = in_data[0];
+ rlen = WPA_GET_BE16(in_data + 3);
wpa_printf(MSG_DEBUG, "TLSv1: Received content type %d version %d.%d "
- "length %d", in_data[0], in_data[1], in_data[2],
- WPA_GET_BE16(in_data + 3));
-
- if (in_data[0] != TLS_CONTENT_TYPE_HANDSHAKE &&
- in_data[0] != TLS_CONTENT_TYPE_CHANGE_CIPHER_SPEC &&
- in_data[0] != TLS_CONTENT_TYPE_ALERT &&
- in_data[0] != TLS_CONTENT_TYPE_APPLICATION_DATA) {
- wpa_printf(MSG_DEBUG, "TLSv1: Unexpected content type 0x%x",
- in_data[0]);
- *alert = TLS_ALERT_UNEXPECTED_MESSAGE;
- return -1;
- }
-
- if (WPA_GET_BE16(in_data + 1) != TLS_VERSION) {
+ "length %d", ct, in_data[1], in_data[2], (int) rlen);
+
+ /*
+ * TLS v1.0 and v1.1 RFCs were not exactly clear on the use of the
+ * protocol version in record layer. As such, accept any {03,xx} value
+ * to remain compatible with existing implementations.
+ */
+ if (in_data[1] != 0x03) {
wpa_printf(MSG_DEBUG, "TLSv1: Unexpected protocol version "
- "%d.%d", in_data[1], in_data[2]);
+ "%u.%u", in_data[1], in_data[2]);
*alert = TLS_ALERT_PROTOCOL_VERSION;
return -1;
}
- rlen = WPA_GET_BE16(in_data + 3);
-
/* TLSCiphertext must not be more than 2^14+2048 bytes */
if (TLS_RECORD_HEADER_LEN + rlen > 18432) {
wpa_printf(MSG_DEBUG, "TLSv1: Record overflow (len=%lu)",
@@ -299,7 +327,19 @@ int tlsv1_record_receive(struct tlsv1_record_layer *rl,
wpa_printf(MSG_DEBUG, "TLSv1: Not all record data included "
"(rlen=%lu > in_len=%lu)",
(unsigned long) rlen, (unsigned long) in_len);
- *alert = TLS_ALERT_DECODE_ERROR;
+ return 0;
+ }
+
+ wpa_hexdump(MSG_MSGDUMP, "TLSv1: Record Layer - Received",
+ in_data, rlen);
+
+ if (ct != TLS_CONTENT_TYPE_HANDSHAKE &&
+ ct != TLS_CONTENT_TYPE_CHANGE_CIPHER_SPEC &&
+ ct != TLS_CONTENT_TYPE_ALERT &&
+ ct != TLS_CONTENT_TYPE_APPLICATION_DATA) {
+ wpa_printf(MSG_DEBUG, "TLSv1: Ignore record with unknown "
+ "content type 0x%x", ct);
+ *alert = TLS_ALERT_UNEXPECTED_MESSAGE;
return -1;
}
@@ -312,58 +352,86 @@ int tlsv1_record_receive(struct tlsv1_record_layer *rl,
return -1;
}
- os_memcpy(out_data, in_data, in_len);
- *out_len = in_len;
-
if (rl->read_cipher_suite != TLS_NULL_WITH_NULL_NULL) {
- if (crypto_cipher_decrypt(rl->read_cbc, out_data,
+ size_t plen;
+ if (crypto_cipher_decrypt(rl->read_cbc, in_data,
out_data, in_len) < 0) {
*alert = TLS_ALERT_DECRYPTION_FAILED;
return -1;
}
+ plen = in_len;
+ wpa_hexdump_key(MSG_MSGDUMP, "TLSv1: Record Layer - Decrypted "
+ "data", out_data, plen);
+
if (rl->iv_size) {
- if (in_len == 0) {
+ /*
+ * TLS v1.0 defines different alert values for various
+ * failures. That may information to aid in attacks, so
+ * use the same bad_record_mac alert regardless of the
+ * issues.
+ *
+ * In addition, instead of returning immediately on
+ * error, run through the MAC check to make timing
+ * attacks more difficult.
+ */
+
+ if (rl->tls_version >= TLS_VERSION_1_1) {
+ /* Remove opaque IV[Cipherspec.block_length] */
+ if (plen < rl->iv_size) {
+ wpa_printf(MSG_DEBUG, "TLSv1.1: Not "
+ "enough room for IV");
+ force_mac_error = 1;
+ goto check_mac;
+ }
+ os_memmove(out_data, out_data + rl->iv_size,
+ plen - rl->iv_size);
+ plen -= rl->iv_size;
+ }
+
+ /* Verify and remove padding */
+ if (plen == 0) {
wpa_printf(MSG_DEBUG, "TLSv1: Too short record"
" (no pad)");
- *alert = TLS_ALERT_DECODE_ERROR;
- return -1;
+ force_mac_error = 1;
+ goto check_mac;
}
- padlen = out_data[in_len - 1];
- if (padlen >= in_len) {
+ padlen = out_data[plen - 1];
+ if (padlen >= plen) {
wpa_printf(MSG_DEBUG, "TLSv1: Incorrect pad "
- "length (%u, in_len=%lu) in "
+ "length (%u, plen=%lu) in "
"received record",
- padlen, (unsigned long) in_len);
- *alert = TLS_ALERT_DECRYPTION_FAILED;
- return -1;
+ padlen, (unsigned long) plen);
+ force_mac_error = 1;
+ goto check_mac;
}
- for (i = in_len - padlen; i < in_len; i++) {
+ for (i = plen - padlen - 1; i < plen - 1; i++) {
if (out_data[i] != padlen) {
wpa_hexdump(MSG_DEBUG,
"TLSv1: Invalid pad in "
"received record",
- out_data + in_len - padlen,
- padlen);
- *alert = TLS_ALERT_DECRYPTION_FAILED;
- return -1;
+ out_data + plen - padlen -
+ 1, padlen + 1);
+ force_mac_error = 1;
+ goto check_mac;
}
}
- *out_len -= padlen + 1;
- }
+ plen -= padlen + 1;
- wpa_hexdump(MSG_MSGDUMP,
- "TLSv1: Record Layer - Decrypted data",
- out_data, in_len);
+ wpa_hexdump_key(MSG_MSGDUMP, "TLSv1: Record Layer - "
+ "Decrypted data with IV and padding "
+ "removed", out_data, plen);
+ }
- if (*out_len < rl->hash_size) {
+ check_mac:
+ if (plen < rl->hash_size) {
wpa_printf(MSG_DEBUG, "TLSv1: Too short record; no "
"hash value");
- *alert = TLS_ALERT_INTERNAL_ERROR;
+ *alert = TLS_ALERT_BAD_RECORD_MAC;
return -1;
}
- *out_len -= rl->hash_size;
+ plen -= rl->hash_size;
hmac = crypto_hash_init(rl->hash_alg, rl->read_mac_secret,
rl->hash_size);
@@ -377,22 +445,30 @@ int tlsv1_record_receive(struct tlsv1_record_layer *rl,
crypto_hash_update(hmac, rl->read_seq_num, TLS_SEQ_NUM_LEN);
/* type + version + length + fragment */
crypto_hash_update(hmac, in_data - TLS_RECORD_HEADER_LEN, 3);
- WPA_PUT_BE16(len, *out_len);
+ WPA_PUT_BE16(len, plen);
crypto_hash_update(hmac, len, 2);
- crypto_hash_update(hmac, out_data, *out_len);
+ crypto_hash_update(hmac, out_data, plen);
hlen = sizeof(hash);
if (crypto_hash_finish(hmac, hash, &hlen) < 0) {
wpa_printf(MSG_DEBUG, "TLSv1: Record Layer - Failed "
"to calculate HMAC");
+ *alert = TLS_ALERT_INTERNAL_ERROR;
return -1;
}
if (hlen != rl->hash_size ||
- os_memcmp(hash, out_data + *out_len, hlen) != 0) {
+ os_memcmp(hash, out_data + plen, hlen) != 0 ||
+ force_mac_error) {
wpa_printf(MSG_DEBUG, "TLSv1: Invalid HMAC value in "
- "received message");
+ "received message (force_mac_error=%d)",
+ force_mac_error);
*alert = TLS_ALERT_BAD_RECORD_MAC;
return -1;
}
+
+ *out_len = plen;
+ } else {
+ os_memcpy(out_data, in_data, in_len);
+ *out_len = in_len;
}
/* TLSCompressed must not be more than 2^14+1024 bytes */
@@ -405,5 +481,5 @@ int tlsv1_record_receive(struct tlsv1_record_layer *rl,
inc_byte_array(rl->read_seq_num, TLS_SEQ_NUM_LEN);
- return 0;
+ return TLS_RECORD_HEADER_LEN + rlen;
}
diff --git a/contrib/wpa/src/tls/tlsv1_record.h b/contrib/wpa/src/tls/tlsv1_record.h
index 9c7c0a4..48abcb0 100644
--- a/contrib/wpa/src/tls/tlsv1_record.h
+++ b/contrib/wpa/src/tls/tlsv1_record.h
@@ -1,15 +1,9 @@
/*
* TLSv1 Record Protocol
- * Copyright (c) 2006-2007, Jouni Malinen <j@w1.fi>
+ * Copyright (c) 2006-2011, Jouni Malinen <j@w1.fi>
*
- * This program is free software; you can redistribute it and/or modify
- * it under the terms of the GNU General Public License version 2 as
- * published by the Free Software Foundation.
- *
- * Alternatively, this software may be distributed under the terms of BSD
- * license.
- *
- * See README and COPYING for more details.
+ * This software may be distributed under the terms of the BSD license.
+ * See README for more details.
*/
#ifndef TLSV1_RECORD_H
@@ -17,7 +11,7 @@
#include "crypto/crypto.h"
-#define TLS_MAX_WRITE_MAC_SECRET_LEN 20
+#define TLS_MAX_WRITE_MAC_SECRET_LEN 32
#define TLS_MAX_WRITE_KEY_LEN 32
#define TLS_MAX_IV_LEN 16
#define TLS_MAX_KEY_BLOCK_LEN (2 * (TLS_MAX_WRITE_MAC_SECRET_LEN + \
@@ -35,6 +29,8 @@ enum {
};
struct tlsv1_record_layer {
+ u16 tls_version;
+
u8 write_mac_secret[TLS_MAX_WRITE_MAC_SECRET_LEN];
u8 read_mac_secret[TLS_MAX_WRITE_MAC_SECRET_LEN];
u8 write_key[TLS_MAX_WRITE_KEY_LEN];
@@ -66,7 +62,8 @@ int tlsv1_record_set_cipher_suite(struct tlsv1_record_layer *rl,
int tlsv1_record_change_write_cipher(struct tlsv1_record_layer *rl);
int tlsv1_record_change_read_cipher(struct tlsv1_record_layer *rl);
int tlsv1_record_send(struct tlsv1_record_layer *rl, u8 content_type, u8 *buf,
- size_t buf_size, size_t payload_len, size_t *out_len);
+ size_t buf_size, const u8 *payload, size_t payload_len,
+ size_t *out_len);
int tlsv1_record_receive(struct tlsv1_record_layer *rl,
const u8 *in_data, size_t in_len,
u8 *out_data, size_t *out_len, u8 *alert);
diff --git a/contrib/wpa/src/tls/tlsv1_server.c b/contrib/wpa/src/tls/tlsv1_server.c
index 6a61235..2880309 100644
--- a/contrib/wpa/src/tls/tlsv1_server.c
+++ b/contrib/wpa/src/tls/tlsv1_server.c
@@ -1,15 +1,9 @@
/*
- * TLSv1 server (RFC 2246)
- * Copyright (c) 2006-2007, Jouni Malinen <j@w1.fi>
+ * TLS v1.0/v1.1/v1.2 server (RFC 2246, RFC 4346, RFC 5246)
+ * Copyright (c) 2006-2011, Jouni Malinen <j@w1.fi>
*
- * This program is free software; you can redistribute it and/or modify
- * it under the terms of the GNU General Public License version 2 as
- * published by the Free Software Foundation.
- *
- * Alternatively, this software may be distributed under the terms of BSD
- * license.
- *
- * See README and COPYING for more details.
+ * This software may be distributed under the terms of the BSD license.
+ * See README for more details.
*/
#include "includes.h"
@@ -49,7 +43,8 @@ int tlsv1_server_derive_keys(struct tlsv1_server *conn,
os_memcpy(seed, conn->client_random, TLS_RANDOM_LEN);
os_memcpy(seed + TLS_RANDOM_LEN, conn->server_random,
TLS_RANDOM_LEN);
- if (tls_prf(pre_master_secret, pre_master_secret_len,
+ if (tls_prf(conn->rl.tls_version,
+ pre_master_secret, pre_master_secret_len,
"master secret", seed, 2 * TLS_RANDOM_LEN,
conn->master_secret, TLS_MASTER_SECRET_LEN)) {
wpa_printf(MSG_DEBUG, "TLSv1: Failed to derive "
@@ -64,7 +59,8 @@ int tlsv1_server_derive_keys(struct tlsv1_server *conn,
os_memcpy(seed + TLS_RANDOM_LEN, conn->client_random, TLS_RANDOM_LEN);
key_block_len = 2 * (conn->rl.hash_size + conn->rl.key_material_len +
conn->rl.iv_size);
- if (tls_prf(conn->master_secret, TLS_MASTER_SECRET_LEN,
+ if (tls_prf(conn->rl.tls_version,
+ conn->master_secret, TLS_MASTER_SECRET_LEN,
"key expansion", seed, 2 * TLS_RANDOM_LEN,
key_block, key_block_len)) {
wpa_printf(MSG_DEBUG, "TLSv1: Failed to derive key_block");
@@ -115,6 +111,7 @@ u8 * tlsv1_server_handshake(struct tlsv1_server *conn,
const u8 *pos, *end;
u8 *msg = NULL, *in_msg, *in_pos, *in_end, alert, ct;
size_t in_msg_len;
+ int used;
if (in_data == NULL || in_len == 0) {
wpa_printf(MSG_DEBUG, "TLSv1: No input data to server");
@@ -130,13 +127,21 @@ u8 * tlsv1_server_handshake(struct tlsv1_server *conn,
/* Each received packet may include multiple records */
while (pos < end) {
in_msg_len = in_len;
- if (tlsv1_record_receive(&conn->rl, pos, end - pos,
- in_msg, &in_msg_len, &alert)) {
+ used = tlsv1_record_receive(&conn->rl, pos, end - pos,
+ in_msg, &in_msg_len, &alert);
+ if (used < 0) {
wpa_printf(MSG_DEBUG, "TLSv1: Processing received "
"record failed");
tlsv1_server_alert(conn, TLS_ALERT_LEVEL_FATAL, alert);
goto failed;
}
+ if (used == 0) {
+ /* need more data */
+ wpa_printf(MSG_DEBUG, "TLSv1: Partial processing not "
+ "yet supported");
+ tlsv1_server_alert(conn, TLS_ALERT_LEVEL_FATAL, alert);
+ goto failed;
+ }
ct = pos[0];
in_pos = in_msg;
@@ -152,7 +157,7 @@ u8 * tlsv1_server_handshake(struct tlsv1_server *conn,
in_pos += in_msg_len;
}
- pos += TLS_RECORD_HEADER_LEN + WPA_GET_BE16(pos + 3);
+ pos += used;
}
os_free(in_msg);
@@ -201,10 +206,8 @@ int tlsv1_server_encrypt(struct tlsv1_server *conn,
wpa_hexdump_key(MSG_MSGDUMP, "TLSv1: Plaintext AppData",
in_data, in_len);
- os_memcpy(out_data + TLS_RECORD_HEADER_LEN, in_data, in_len);
-
if (tlsv1_record_send(&conn->rl, TLS_CONTENT_TYPE_APPLICATION_DATA,
- out_data, out_len, in_len, &rlen) < 0) {
+ out_data, out_len, in_data, in_len, &rlen) < 0) {
wpa_printf(MSG_DEBUG, "TLSv1: Failed to create a record");
tlsv1_server_alert(conn, TLS_ALERT_LEVEL_FATAL,
TLS_ALERT_INTERNAL_ERROR);
@@ -232,8 +235,8 @@ int tlsv1_server_decrypt(struct tlsv1_server *conn,
u8 *out_data, size_t out_len)
{
const u8 *in_end, *pos;
- int res;
- u8 alert, *out_end, *out_pos;
+ int used;
+ u8 alert, *out_end, *out_pos, ct;
size_t olen;
pos = in_data;
@@ -242,7 +245,46 @@ int tlsv1_server_decrypt(struct tlsv1_server *conn,
out_end = out_data + out_len;
while (pos < in_end) {
- if (pos[0] != TLS_CONTENT_TYPE_APPLICATION_DATA) {
+ ct = pos[0];
+ olen = out_end - out_pos;
+ used = tlsv1_record_receive(&conn->rl, pos, in_end - pos,
+ out_pos, &olen, &alert);
+ if (used < 0) {
+ wpa_printf(MSG_DEBUG, "TLSv1: Record layer processing "
+ "failed");
+ tlsv1_server_alert(conn, TLS_ALERT_LEVEL_FATAL, alert);
+ return -1;
+ }
+ if (used == 0) {
+ /* need more data */
+ wpa_printf(MSG_DEBUG, "TLSv1: Partial processing not "
+ "yet supported");
+ tlsv1_server_alert(conn, TLS_ALERT_LEVEL_FATAL, alert);
+ return -1;
+ }
+
+ if (ct == TLS_CONTENT_TYPE_ALERT) {
+ if (olen < 2) {
+ wpa_printf(MSG_DEBUG, "TLSv1: Alert "
+ "underflow");
+ tlsv1_server_alert(conn, TLS_ALERT_LEVEL_FATAL,
+ TLS_ALERT_DECODE_ERROR);
+ return -1;
+ }
+ wpa_printf(MSG_DEBUG, "TLSv1: Received alert %d:%d",
+ out_pos[0], out_pos[1]);
+ if (out_pos[0] == TLS_ALERT_LEVEL_WARNING) {
+ /* Continue processing */
+ pos += used;
+ continue;
+ }
+
+ tlsv1_server_alert(conn, TLS_ALERT_LEVEL_FATAL,
+ out_pos[1]);
+ return -1;
+ }
+
+ if (ct != TLS_CONTENT_TYPE_APPLICATION_DATA) {
wpa_printf(MSG_DEBUG, "TLSv1: Unexpected content type "
"0x%x", pos[0]);
tlsv1_server_alert(conn, TLS_ALERT_LEVEL_FATAL,
@@ -250,15 +292,6 @@ int tlsv1_server_decrypt(struct tlsv1_server *conn,
return -1;
}
- olen = out_end - out_pos;
- res = tlsv1_record_receive(&conn->rl, pos, in_end - pos,
- out_pos, &olen, &alert);
- if (res < 0) {
- wpa_printf(MSG_DEBUG, "TLSv1: Record layer processing "
- "failed");
- tlsv1_server_alert(conn, TLS_ALERT_LEVEL_FATAL, alert);
- return -1;
- }
out_pos += olen;
if (out_pos > out_end) {
wpa_printf(MSG_DEBUG, "TLSv1: Buffer not large enough "
@@ -268,7 +301,7 @@ int tlsv1_server_decrypt(struct tlsv1_server *conn,
return -1;
}
- pos += TLS_RECORD_HEADER_LEN + WPA_GET_BE16(pos + 3);
+ pos += used;
}
return out_pos - out_data;
@@ -328,9 +361,7 @@ struct tlsv1_server * tlsv1_server_init(struct tlsv1_credentials *cred)
count = 0;
suites = conn->cipher_suites;
-#ifndef CONFIG_CRYPTO_INTERNAL
suites[count++] = TLS_RSA_WITH_AES_256_CBC_SHA;
-#endif /* CONFIG_CRYPTO_INTERNAL */
suites[count++] = TLS_RSA_WITH_AES_128_CBC_SHA;
suites[count++] = TLS_RSA_WITH_3DES_EDE_CBC_SHA;
suites[count++] = TLS_RSA_WITH_RC4_128_SHA;
@@ -412,7 +443,8 @@ int tlsv1_server_prf(struct tlsv1_server *conn, const char *label,
TLS_RANDOM_LEN);
}
- return tls_prf(conn->master_secret, TLS_MASTER_SECRET_LEN,
+ return tls_prf(conn->rl.tls_version,
+ conn->master_secret, TLS_MASTER_SECRET_LEN,
label, seed, 2 * TLS_RANDOM_LEN, out, out_len);
}
@@ -553,16 +585,12 @@ int tlsv1_server_set_cipher_list(struct tlsv1_server *conn, u8 *ciphers)
if (ciphers[0] == TLS_CIPHER_ANON_DH_AES128_SHA) {
count = 0;
suites = conn->cipher_suites;
-#ifndef CONFIG_CRYPTO_INTERNAL
suites[count++] = TLS_RSA_WITH_AES_256_CBC_SHA;
-#endif /* CONFIG_CRYPTO_INTERNAL */
suites[count++] = TLS_RSA_WITH_AES_128_CBC_SHA;
suites[count++] = TLS_RSA_WITH_3DES_EDE_CBC_SHA;
suites[count++] = TLS_RSA_WITH_RC4_128_SHA;
suites[count++] = TLS_RSA_WITH_RC4_128_MD5;
-#ifndef CONFIG_CRYPTO_INTERNAL
suites[count++] = TLS_DH_anon_WITH_AES_256_CBC_SHA;
-#endif /* CONFIG_CRYPTO_INTERNAL */
suites[count++] = TLS_DH_anon_WITH_AES_128_CBC_SHA;
suites[count++] = TLS_DH_anon_WITH_3DES_EDE_CBC_SHA;
suites[count++] = TLS_DH_anon_WITH_RC4_128_MD5;
diff --git a/contrib/wpa/src/tls/tlsv1_server.h b/contrib/wpa/src/tls/tlsv1_server.h
index 00c536c..a18c69e 100644
--- a/contrib/wpa/src/tls/tlsv1_server.h
+++ b/contrib/wpa/src/tls/tlsv1_server.h
@@ -1,15 +1,9 @@
/*
- * TLSv1 server (RFC 2246)
- * Copyright (c) 2006-2007, Jouni Malinen <j@w1.fi>
+ * TLS v1.0/v1.1/v1.2 server (RFC 2246, RFC 4346, RFC 5246)
+ * Copyright (c) 2006-2011, Jouni Malinen <j@w1.fi>
*
- * This program is free software; you can redistribute it and/or modify
- * it under the terms of the GNU General Public License version 2 as
- * published by the Free Software Foundation.
- *
- * Alternatively, this software may be distributed under the terms of BSD
- * license.
- *
- * See README and COPYING for more details.
+ * This software may be distributed under the terms of the BSD license.
+ * See README for more details.
*/
#ifndef TLSV1_SERVER_H
diff --git a/contrib/wpa/src/tls/tlsv1_server_i.h b/contrib/wpa/src/tls/tlsv1_server_i.h
index d11ea75..1f61533 100644
--- a/contrib/wpa/src/tls/tlsv1_server_i.h
+++ b/contrib/wpa/src/tls/tlsv1_server_i.h
@@ -2,14 +2,8 @@
* TLSv1 server - internal structures
* Copyright (c) 2006-2007, Jouni Malinen <j@w1.fi>
*
- * This program is free software; you can redistribute it and/or modify
- * it under the terms of the GNU General Public License version 2 as
- * published by the Free Software Foundation.
- *
- * Alternatively, this software may be distributed under the terms of BSD
- * license.
- *
- * See README and COPYING for more details.
+ * This software may be distributed under the terms of the BSD license.
+ * See README for more details.
*/
#ifndef TLSV1_SERVER_I_H
diff --git a/contrib/wpa/src/tls/tlsv1_server_read.c b/contrib/wpa/src/tls/tlsv1_server_read.c
index 49e811f..6f6539b 100644
--- a/contrib/wpa/src/tls/tlsv1_server_read.c
+++ b/contrib/wpa/src/tls/tlsv1_server_read.c
@@ -1,15 +1,9 @@
/*
* TLSv1 server - read handshake message
- * Copyright (c) 2006-2007, Jouni Malinen <j@w1.fi>
+ * Copyright (c) 2006-2011, Jouni Malinen <j@w1.fi>
*
- * This program is free software; you can redistribute it and/or modify
- * it under the terms of the GNU General Public License version 2 as
- * published by the Free Software Foundation.
- *
- * Alternatively, this software may be distributed under the terms of BSD
- * license.
- *
- * See README and COPYING for more details.
+ * This software may be distributed under the terms of the BSD license.
+ * See README for more details.
*/
#include "includes.h"
@@ -17,6 +11,7 @@
#include "common.h"
#include "crypto/md5.h"
#include "crypto/sha1.h"
+#include "crypto/sha256.h"
#include "crypto/tls.h"
#include "x509v3.h"
#include "tlsv1_common.h"
@@ -85,15 +80,30 @@ static int tls_process_client_hello(struct tlsv1_server *conn, u8 ct,
conn->client_version = WPA_GET_BE16(pos);
wpa_printf(MSG_DEBUG, "TLSv1: Client version %d.%d",
conn->client_version >> 8, conn->client_version & 0xff);
- if (conn->client_version < TLS_VERSION) {
+ if (conn->client_version < TLS_VERSION_1) {
wpa_printf(MSG_DEBUG, "TLSv1: Unexpected protocol version in "
- "ClientHello");
+ "ClientHello %u.%u",
+ conn->client_version >> 8,
+ conn->client_version & 0xff);
tlsv1_server_alert(conn, TLS_ALERT_LEVEL_FATAL,
TLS_ALERT_PROTOCOL_VERSION);
return -1;
}
pos += 2;
+ if (TLS_VERSION == TLS_VERSION_1)
+ conn->rl.tls_version = TLS_VERSION_1;
+#ifdef CONFIG_TLSV12
+ else if (conn->client_version >= TLS_VERSION_1_2)
+ conn->rl.tls_version = TLS_VERSION_1_2;
+#endif /* CONFIG_TLSV12 */
+ else if (conn->client_version > TLS_VERSION_1_1)
+ conn->rl.tls_version = TLS_VERSION_1_1;
+ else
+ conn->rl.tls_version = conn->client_version;
+ wpa_printf(MSG_DEBUG, "TLSv1: Using TLS v%s",
+ tls_version_str(conn->rl.tls_version));
+
/* Random random */
if (end - pos < TLS_RANDOM_LEN)
goto decode_error;
@@ -424,7 +434,7 @@ static int tls_process_certificate(struct tlsv1_server *conn, u8 ct,
}
if (x509_certificate_chain_validate(conn->cred->trusted_certs, chain,
- &reason) < 0) {
+ &reason, 0) < 0) {
int tls_reason;
wpa_printf(MSG_DEBUG, "TLSv1: Server certificate chain "
"validation failed (reason=%d)", reason);
@@ -483,6 +493,14 @@ static int tls_process_client_key_exchange_rsa(
encr_len = WPA_GET_BE16(pos);
pos += 2;
+ if (pos + encr_len > end) {
+ wpa_printf(MSG_DEBUG, "TLSv1: Invalid ClientKeyExchange "
+ "format: encr_len=%u left=%u",
+ encr_len, (unsigned int) (end - pos));
+ tlsv1_server_alert(conn, TLS_ALERT_LEVEL_FATAL,
+ TLS_ALERT_DECODE_ERROR);
+ return -1;
+ }
outbuflen = outlen = end - pos;
out = os_malloc(outlen >= TLS_PRE_MASTER_SECRET_LEN ?
@@ -512,21 +530,21 @@ static int tls_process_client_key_exchange_rsa(
*/
if (crypto_private_key_decrypt_pkcs1_v15(conn->cred->key,
- pos, end - pos,
+ pos, encr_len,
out, &outlen) < 0) {
wpa_printf(MSG_DEBUG, "TLSv1: Failed to decrypt "
- "PreMasterSecret (encr_len=%d outlen=%lu)",
- (int) (end - pos), (unsigned long) outlen);
+ "PreMasterSecret (encr_len=%u outlen=%lu)",
+ encr_len, (unsigned long) outlen);
use_random = 1;
}
- if (outlen != TLS_PRE_MASTER_SECRET_LEN) {
+ if (!use_random && outlen != TLS_PRE_MASTER_SECRET_LEN) {
wpa_printf(MSG_DEBUG, "TLSv1: Unexpected PreMasterSecret "
"length %lu", (unsigned long) outlen);
use_random = 1;
}
- if (WPA_GET_BE16(out) != conn->client_version) {
+ if (!use_random && WPA_GET_BE16(out) != conn->client_version) {
wpa_printf(MSG_DEBUG, "TLSv1: Client version in "
"ClientKeyExchange does not match with version in "
"ClientHello");
@@ -822,6 +840,47 @@ static int tls_process_certificate_verify(struct tlsv1_server *conn, u8 ct,
hpos = hash;
+#ifdef CONFIG_TLSV12
+ if (conn->rl.tls_version == TLS_VERSION_1_2) {
+ /*
+ * RFC 5246, 4.7:
+ * TLS v1.2 adds explicit indication of the used signature and
+ * hash algorithms.
+ *
+ * struct {
+ * HashAlgorithm hash;
+ * SignatureAlgorithm signature;
+ * } SignatureAndHashAlgorithm;
+ */
+ if (end - pos < 2) {
+ tlsv1_server_alert(conn, TLS_ALERT_LEVEL_FATAL,
+ TLS_ALERT_DECODE_ERROR);
+ return -1;
+ }
+ if (pos[0] != TLS_HASH_ALG_SHA256 ||
+ pos[1] != TLS_SIGN_ALG_RSA) {
+ wpa_printf(MSG_DEBUG, "TLSv1.2: Unsupported hash(%u)/"
+ "signature(%u) algorithm",
+ pos[0], pos[1]);
+ tlsv1_server_alert(conn, TLS_ALERT_LEVEL_FATAL,
+ TLS_ALERT_INTERNAL_ERROR);
+ return -1;
+ }
+ pos += 2;
+
+ hlen = SHA256_MAC_LEN;
+ if (conn->verify.sha256_cert == NULL ||
+ crypto_hash_finish(conn->verify.sha256_cert, hpos, &hlen) <
+ 0) {
+ conn->verify.sha256_cert = NULL;
+ tlsv1_server_alert(conn, TLS_ALERT_LEVEL_FATAL,
+ TLS_ALERT_INTERNAL_ERROR);
+ return -1;
+ }
+ conn->verify.sha256_cert = NULL;
+ } else {
+#endif /* CONFIG_TLSV12 */
+
if (alg == SIGN_ALG_RSA) {
hlen = MD5_MAC_LEN;
if (conn->verify.md5_cert == NULL ||
@@ -852,6 +911,10 @@ static int tls_process_certificate_verify(struct tlsv1_server *conn, u8 ct,
if (alg == SIGN_ALG_RSA)
hlen += MD5_MAC_LEN;
+#ifdef CONFIG_TLSV12
+ }
+#endif /* CONFIG_TLSV12 */
+
wpa_hexdump(MSG_MSGDUMP, "TLSv1: CertificateVerify hash", hash, hlen);
if (end - pos < 2) {
@@ -891,6 +954,41 @@ static int tls_process_certificate_verify(struct tlsv1_server *conn, u8 ct,
wpa_hexdump_key(MSG_MSGDUMP, "TLSv1: Decrypted Signature",
buf, buflen);
+#ifdef CONFIG_TLSV12
+ if (conn->rl.tls_version >= TLS_VERSION_1_2) {
+ /*
+ * RFC 3447, A.2.4 RSASSA-PKCS1-v1_5
+ *
+ * DigestInfo ::= SEQUENCE {
+ * digestAlgorithm DigestAlgorithm,
+ * digest OCTET STRING
+ * }
+ *
+ * SHA-256 OID: sha256WithRSAEncryption ::= {pkcs-1 11}
+ *
+ * DER encoded DigestInfo for SHA256 per RFC 3447:
+ * 30 31 30 0d 06 09 60 86 48 01 65 03 04 02 01 05 00 04 20 ||
+ * H
+ */
+ if (buflen >= 19 + 32 &&
+ os_memcmp(buf, "\x30\x31\x30\x0d\x06\x09\x60\x86\x48\x01"
+ "\x65\x03\x04\x02\x01\x05\x00\x04\x20", 19) == 0)
+ {
+ wpa_printf(MSG_DEBUG, "TLSv1.2: DigestAlgorithn = "
+ "SHA-256");
+ os_memmove(buf, buf + 19, buflen - 19);
+ buflen -= 19;
+ } else {
+ wpa_printf(MSG_DEBUG, "TLSv1.2: Unrecognized "
+ "DigestInfo");
+ os_free(buf);
+ tlsv1_server_alert(conn, TLS_ALERT_LEVEL_FATAL,
+ TLS_ALERT_DECRYPT_ERROR);
+ return -1;
+ }
+ }
+#endif /* CONFIG_TLSV12 */
+
if (buflen != hlen || os_memcmp(buf, hash, buflen) != 0) {
wpa_printf(MSG_DEBUG, "TLSv1: Invalid Signature in "
"CertificateVerify - did not match with calculated "
@@ -1022,6 +1120,21 @@ static int tls_process_client_finished(struct tlsv1_server *conn, u8 ct,
wpa_hexdump(MSG_MSGDUMP, "TLSv1: verify_data in Finished",
pos, TLS_VERIFY_DATA_LEN);
+#ifdef CONFIG_TLSV12
+ if (conn->rl.tls_version >= TLS_VERSION_1_2) {
+ hlen = SHA256_MAC_LEN;
+ if (conn->verify.sha256_client == NULL ||
+ crypto_hash_finish(conn->verify.sha256_client, hash, &hlen)
+ < 0) {
+ tlsv1_server_alert(conn, TLS_ALERT_LEVEL_FATAL,
+ TLS_ALERT_INTERNAL_ERROR);
+ conn->verify.sha256_client = NULL;
+ return -1;
+ }
+ conn->verify.sha256_client = NULL;
+ } else {
+#endif /* CONFIG_TLSV12 */
+
hlen = MD5_MAC_LEN;
if (conn->verify.md5_client == NULL ||
crypto_hash_finish(conn->verify.md5_client, hash, &hlen) < 0) {
@@ -1043,9 +1156,15 @@ static int tls_process_client_finished(struct tlsv1_server *conn, u8 ct,
return -1;
}
conn->verify.sha1_client = NULL;
+ hlen = MD5_MAC_LEN + SHA1_MAC_LEN;
+
+#ifdef CONFIG_TLSV12
+ }
+#endif /* CONFIG_TLSV12 */
- if (tls_prf(conn->master_secret, TLS_MASTER_SECRET_LEN,
- "client finished", hash, MD5_MAC_LEN + SHA1_MAC_LEN,
+ if (tls_prf(conn->rl.tls_version,
+ conn->master_secret, TLS_MASTER_SECRET_LEN,
+ "client finished", hash, hlen,
verify_data, TLS_VERIFY_DATA_LEN)) {
wpa_printf(MSG_DEBUG, "TLSv1: Failed to derive verify_data");
tlsv1_server_alert(conn, TLS_ALERT_LEVEL_FATAL,
diff --git a/contrib/wpa/src/tls/tlsv1_server_write.c b/contrib/wpa/src/tls/tlsv1_server_write.c
index 6d1df7f..6d8e55ed 100644
--- a/contrib/wpa/src/tls/tlsv1_server_write.c
+++ b/contrib/wpa/src/tls/tlsv1_server_write.c
@@ -1,15 +1,9 @@
/*
* TLSv1 server - write handshake message
- * Copyright (c) 2006-2007, Jouni Malinen <j@w1.fi>
+ * Copyright (c) 2006-2011, Jouni Malinen <j@w1.fi>
*
- * This program is free software; you can redistribute it and/or modify
- * it under the terms of the GNU General Public License version 2 as
- * published by the Free Software Foundation.
- *
- * Alternatively, this software may be distributed under the terms of BSD
- * license.
- *
- * See README and COPYING for more details.
+ * This software may be distributed under the terms of the BSD license.
+ * See README for more details.
*/
#include "includes.h"
@@ -17,7 +11,9 @@
#include "common.h"
#include "crypto/md5.h"
#include "crypto/sha1.h"
+#include "crypto/sha256.h"
#include "crypto/tls.h"
+#include "crypto/random.h"
#include "x509v3.h"
#include "tlsv1_common.h"
#include "tlsv1_record.h"
@@ -58,7 +54,7 @@ static int tls_write_server_hello(struct tlsv1_server *conn,
os_get_time(&now);
WPA_PUT_BE32(conn->server_random, now.sec);
- if (os_get_random(conn->server_random + 4, TLS_RANDOM_LEN - 4)) {
+ if (random_get_bytes(conn->server_random + 4, TLS_RANDOM_LEN - 4)) {
wpa_printf(MSG_ERROR, "TLSv1: Could not generate "
"server_random");
return -1;
@@ -67,7 +63,7 @@ static int tls_write_server_hello(struct tlsv1_server *conn,
conn->server_random, TLS_RANDOM_LEN);
conn->session_id_len = TLS_SESSION_ID_MAX_LEN;
- if (os_get_random(conn->session_id, conn->session_id_len)) {
+ if (random_get_bytes(conn->session_id, conn->session_id_len)) {
wpa_printf(MSG_ERROR, "TLSv1: Could not generate "
"session_id");
return -1;
@@ -86,7 +82,7 @@ static int tls_write_server_hello(struct tlsv1_server *conn,
pos += 3;
/* body - ServerHello */
/* ProtocolVersion server_version */
- WPA_PUT_BE16(pos, TLS_VERSION);
+ WPA_PUT_BE16(pos, conn->rl.tls_version);
pos += 2;
/* Random random: uint32 gmt_unix_time, opaque random_bytes */
os_memcpy(pos, conn->server_random, TLS_RANDOM_LEN);
@@ -142,7 +138,8 @@ static int tls_write_server_hello(struct tlsv1_server *conn,
tls_verify_hash_add(&conn->verify, hs_start, pos - hs_start);
if (tlsv1_record_send(&conn->rl, TLS_CONTENT_TYPE_HANDSHAKE,
- rhdr, end - rhdr, pos - hs_start, &rlen) < 0) {
+ rhdr, end - rhdr, hs_start, pos - hs_start,
+ &rlen) < 0) {
wpa_printf(MSG_DEBUG, "TLSv1: Failed to create TLS record");
tlsv1_server_alert(conn, TLS_ALERT_LEVEL_FATAL,
TLS_ALERT_INTERNAL_ERROR);
@@ -226,7 +223,8 @@ static int tls_write_server_certificate(struct tlsv1_server *conn,
WPA_PUT_BE24(hs_length, pos - hs_length - 3);
if (tlsv1_record_send(&conn->rl, TLS_CONTENT_TYPE_HANDSHAKE,
- rhdr, end - rhdr, pos - hs_start, &rlen) < 0) {
+ rhdr, end - rhdr, hs_start, pos - hs_start,
+ &rlen) < 0) {
wpa_printf(MSG_DEBUG, "TLSv1: Failed to generate a record");
tlsv1_server_alert(conn, TLS_ALERT_LEVEL_FATAL,
TLS_ALERT_INTERNAL_ERROR);
@@ -287,7 +285,7 @@ static int tls_write_server_key_exchange(struct tlsv1_server *conn,
TLS_ALERT_INTERNAL_ERROR);
return -1;
}
- if (os_get_random(conn->dh_secret, conn->dh_secret_len)) {
+ if (random_get_bytes(conn->dh_secret, conn->dh_secret_len)) {
wpa_printf(MSG_DEBUG, "TLSv1: Failed to get random "
"data for Diffie-Hellman");
tlsv1_server_alert(conn, TLS_ALERT_LEVEL_FATAL,
@@ -417,7 +415,8 @@ static int tls_write_server_key_exchange(struct tlsv1_server *conn,
WPA_PUT_BE24(hs_length, pos - hs_length - 3);
if (tlsv1_record_send(&conn->rl, TLS_CONTENT_TYPE_HANDSHAKE,
- rhdr, end - rhdr, pos - hs_start, &rlen) < 0) {
+ rhdr, end - rhdr, hs_start, pos - hs_start,
+ &rlen) < 0) {
wpa_printf(MSG_DEBUG, "TLSv1: Failed to generate a record");
tlsv1_server_alert(conn, TLS_ALERT_LEVEL_FATAL,
TLS_ALERT_INTERNAL_ERROR);
@@ -482,7 +481,8 @@ static int tls_write_server_certificate_request(struct tlsv1_server *conn,
WPA_PUT_BE24(hs_length, pos - hs_length - 3);
if (tlsv1_record_send(&conn->rl, TLS_CONTENT_TYPE_HANDSHAKE,
- rhdr, end - rhdr, pos - hs_start, &rlen) < 0) {
+ rhdr, end - rhdr, hs_start, pos - hs_start,
+ &rlen) < 0) {
wpa_printf(MSG_DEBUG, "TLSv1: Failed to generate a record");
tlsv1_server_alert(conn, TLS_ALERT_LEVEL_FATAL,
TLS_ALERT_INTERNAL_ERROR);
@@ -501,40 +501,35 @@ static int tls_write_server_certificate_request(struct tlsv1_server *conn,
static int tls_write_server_hello_done(struct tlsv1_server *conn,
u8 **msgpos, u8 *end)
{
- u8 *pos, *rhdr, *hs_start, *hs_length;
+ u8 *pos;
size_t rlen;
-
- pos = *msgpos;
+ u8 payload[4];
wpa_printf(MSG_DEBUG, "TLSv1: Send ServerHelloDone");
- rhdr = pos;
- pos += TLS_RECORD_HEADER_LEN;
/* opaque fragment[TLSPlaintext.length] */
/* Handshake */
- hs_start = pos;
+ pos = payload;
/* HandshakeType msg_type */
*pos++ = TLS_HANDSHAKE_TYPE_SERVER_HELLO_DONE;
- /* uint24 length (to be filled) */
- hs_length = pos;
+ /* uint24 length */
+ WPA_PUT_BE24(pos, 0);
pos += 3;
/* body - ServerHelloDone (empty) */
- WPA_PUT_BE24(hs_length, pos - hs_length - 3);
-
if (tlsv1_record_send(&conn->rl, TLS_CONTENT_TYPE_HANDSHAKE,
- rhdr, end - rhdr, pos - hs_start, &rlen) < 0) {
+ *msgpos, end - *msgpos, payload, pos - payload,
+ &rlen) < 0) {
wpa_printf(MSG_DEBUG, "TLSv1: Failed to generate a record");
tlsv1_server_alert(conn, TLS_ALERT_LEVEL_FATAL,
TLS_ALERT_INTERNAL_ERROR);
return -1;
}
- pos = rhdr + rlen;
- tls_verify_hash_add(&conn->verify, hs_start, pos - hs_start);
+ tls_verify_hash_add(&conn->verify, payload, pos - payload);
- *msgpos = pos;
+ *msgpos += rlen;
return 0;
}
@@ -543,17 +538,16 @@ static int tls_write_server_hello_done(struct tlsv1_server *conn,
static int tls_write_server_change_cipher_spec(struct tlsv1_server *conn,
u8 **msgpos, u8 *end)
{
- u8 *pos, *rhdr;
size_t rlen;
-
- pos = *msgpos;
+ u8 payload[1];
wpa_printf(MSG_DEBUG, "TLSv1: Send ChangeCipherSpec");
- rhdr = pos;
- pos += TLS_RECORD_HEADER_LEN;
- *pos = TLS_CHANGE_CIPHER_SPEC;
+
+ payload[0] = TLS_CHANGE_CIPHER_SPEC;
+
if (tlsv1_record_send(&conn->rl, TLS_CONTENT_TYPE_CHANGE_CIPHER_SPEC,
- rhdr, end - rhdr, 1, &rlen) < 0) {
+ *msgpos, end - *msgpos, payload, sizeof(payload),
+ &rlen) < 0) {
wpa_printf(MSG_DEBUG, "TLSv1: Failed to create a record");
tlsv1_server_alert(conn, TLS_ALERT_LEVEL_FATAL,
TLS_ALERT_INTERNAL_ERROR);
@@ -568,7 +562,7 @@ static int tls_write_server_change_cipher_spec(struct tlsv1_server *conn,
return -1;
}
- *msgpos = rhdr + rlen;
+ *msgpos += rlen;
return 0;
}
@@ -577,9 +571,9 @@ static int tls_write_server_change_cipher_spec(struct tlsv1_server *conn,
static int tls_write_server_finished(struct tlsv1_server *conn,
u8 **msgpos, u8 *end)
{
- u8 *pos, *rhdr, *hs_start, *hs_length;
+ u8 *pos, *hs_start;
size_t rlen, hlen;
- u8 verify_data[TLS_VERIFY_DATA_LEN];
+ u8 verify_data[1 + 3 + TLS_VERIFY_DATA_LEN];
u8 hash[MD5_MAC_LEN + SHA1_MAC_LEN];
pos = *msgpos;
@@ -588,6 +582,21 @@ static int tls_write_server_finished(struct tlsv1_server *conn,
/* Encrypted Handshake Message: Finished */
+#ifdef CONFIG_TLSV12
+ if (conn->rl.tls_version >= TLS_VERSION_1_2) {
+ hlen = SHA256_MAC_LEN;
+ if (conn->verify.sha256_server == NULL ||
+ crypto_hash_finish(conn->verify.sha256_server, hash, &hlen)
+ < 0) {
+ conn->verify.sha256_server = NULL;
+ tlsv1_server_alert(conn, TLS_ALERT_LEVEL_FATAL,
+ TLS_ALERT_INTERNAL_ERROR);
+ return -1;
+ }
+ conn->verify.sha256_server = NULL;
+ } else {
+#endif /* CONFIG_TLSV12 */
+
hlen = MD5_MAC_LEN;
if (conn->verify.md5_server == NULL ||
crypto_hash_finish(conn->verify.md5_server, hash, &hlen) < 0) {
@@ -609,43 +618,44 @@ static int tls_write_server_finished(struct tlsv1_server *conn,
return -1;
}
conn->verify.sha1_server = NULL;
+ hlen = MD5_MAC_LEN + SHA1_MAC_LEN;
+
+#ifdef CONFIG_TLSV12
+ }
+#endif /* CONFIG_TLSV12 */
- if (tls_prf(conn->master_secret, TLS_MASTER_SECRET_LEN,
- "server finished", hash, MD5_MAC_LEN + SHA1_MAC_LEN,
- verify_data, TLS_VERIFY_DATA_LEN)) {
+ if (tls_prf(conn->rl.tls_version,
+ conn->master_secret, TLS_MASTER_SECRET_LEN,
+ "server finished", hash, hlen,
+ verify_data + 1 + 3, TLS_VERIFY_DATA_LEN)) {
wpa_printf(MSG_DEBUG, "TLSv1: Failed to generate verify_data");
tlsv1_server_alert(conn, TLS_ALERT_LEVEL_FATAL,
TLS_ALERT_INTERNAL_ERROR);
return -1;
}
wpa_hexdump_key(MSG_DEBUG, "TLSv1: verify_data (server)",
- verify_data, TLS_VERIFY_DATA_LEN);
+ verify_data + 1 + 3, TLS_VERIFY_DATA_LEN);
- rhdr = pos;
- pos += TLS_RECORD_HEADER_LEN;
/* Handshake */
- hs_start = pos;
+ pos = hs_start = verify_data;
/* HandshakeType msg_type */
*pos++ = TLS_HANDSHAKE_TYPE_FINISHED;
- /* uint24 length (to be filled) */
- hs_length = pos;
+ /* uint24 length */
+ WPA_PUT_BE24(pos, TLS_VERIFY_DATA_LEN);
pos += 3;
- os_memcpy(pos, verify_data, TLS_VERIFY_DATA_LEN);
pos += TLS_VERIFY_DATA_LEN;
- WPA_PUT_BE24(hs_length, pos - hs_length - 3);
tls_verify_hash_add(&conn->verify, hs_start, pos - hs_start);
if (tlsv1_record_send(&conn->rl, TLS_CONTENT_TYPE_HANDSHAKE,
- rhdr, end - rhdr, pos - hs_start, &rlen) < 0) {
+ *msgpos, end - *msgpos, hs_start, pos - hs_start,
+ &rlen) < 0) {
wpa_printf(MSG_DEBUG, "TLSv1: Failed to create a record");
tlsv1_server_alert(conn, TLS_ALERT_LEVEL_FATAL,
TLS_ALERT_INTERNAL_ERROR);
return -1;
}
- pos = rhdr + rlen;
-
- *msgpos = pos;
+ *msgpos += rlen;
return 0;
}
@@ -770,7 +780,8 @@ u8 * tlsv1_server_send_alert(struct tlsv1_server *conn, u8 level,
/* ContentType type */
*pos++ = TLS_CONTENT_TYPE_ALERT;
/* ProtocolVersion version */
- WPA_PUT_BE16(pos, TLS_VERSION);
+ WPA_PUT_BE16(pos, conn->rl.tls_version ? conn->rl.tls_version :
+ TLS_VERSION);
pos += 2;
/* uint16 length (to be filled) */
length = pos;
diff --git a/contrib/wpa/src/tls/x509v3.c b/contrib/wpa/src/tls/x509v3.c
index bc93df6..87c5178 100644
--- a/contrib/wpa/src/tls/x509v3.c
+++ b/contrib/wpa/src/tls/x509v3.c
@@ -1,15 +1,9 @@
/*
* X.509v3 certificate parsing and processing (RFC 3280 profile)
- * Copyright (c) 2006-2007, Jouni Malinen <j@w1.fi>
+ * Copyright (c) 2006-2011, Jouni Malinen <j@w1.fi>
*
- * This program is free software; you can redistribute it and/or modify
- * it under the terms of the GNU General Public License version 2 as
- * published by the Free Software Foundation.
- *
- * Alternatively, this software may be distributed under the terms of BSD
- * license.
- *
- * See README and COPYING for more details.
+ * This software may be distributed under the terms of the BSD license.
+ * See README for more details.
*/
#include "includes.h"
@@ -1834,7 +1828,7 @@ static int x509_valid_issuer(const struct x509_certificate *cert)
*/
int x509_certificate_chain_validate(struct x509_certificate *trusted,
struct x509_certificate *chain,
- int *reason)
+ int *reason, int disable_time_checks)
{
long unsigned idx;
int chain_trusted = 0;
@@ -1854,10 +1848,11 @@ int x509_certificate_chain_validate(struct x509_certificate *trusted,
if (chain_trusted)
continue;
- if ((unsigned long) now.sec <
- (unsigned long) cert->not_before ||
- (unsigned long) now.sec >
- (unsigned long) cert->not_after) {
+ if (!disable_time_checks &&
+ ((unsigned long) now.sec <
+ (unsigned long) cert->not_before ||
+ (unsigned long) now.sec >
+ (unsigned long) cert->not_after)) {
wpa_printf(MSG_INFO, "X509: Certificate not valid "
"(now=%lu not_before=%lu not_after=%lu)",
now.sec, cert->not_before, cert->not_after);
diff --git a/contrib/wpa/src/tls/x509v3.h b/contrib/wpa/src/tls/x509v3.h
index 37292d7..91a35ba 100644
--- a/contrib/wpa/src/tls/x509v3.h
+++ b/contrib/wpa/src/tls/x509v3.h
@@ -1,15 +1,9 @@
/*
* X.509v3 certificate parsing and processing
- * Copyright (c) 2006, Jouni Malinen <j@w1.fi>
+ * Copyright (c) 2006-2011, Jouni Malinen <j@w1.fi>
*
- * This program is free software; you can redistribute it and/or modify
- * it under the terms of the GNU General Public License version 2 as
- * published by the Free Software Foundation.
- *
- * Alternatively, this software may be distributed under the terms of BSD
- * license.
- *
- * See README and COPYING for more details.
+ * This software may be distributed under the terms of the BSD license.
+ * See README for more details.
*/
#ifndef X509V3_H
@@ -120,7 +114,7 @@ int x509_certificate_check_signature(struct x509_certificate *issuer,
struct x509_certificate *cert);
int x509_certificate_chain_validate(struct x509_certificate *trusted,
struct x509_certificate *chain,
- int *reason);
+ int *reason, int disable_time_checks);
struct x509_certificate *
x509_certificate_get_subject(struct x509_certificate *chain,
struct x509_name *name);
diff --git a/contrib/wpa/src/utils/.gitignore b/contrib/wpa/src/utils/.gitignore
deleted file mode 100644
index 833734f..0000000
--- a/contrib/wpa/src/utils/.gitignore
+++ /dev/null
@@ -1 +0,0 @@
-libutils.a
diff --git a/contrib/wpa/src/utils/Makefile b/contrib/wpa/src/utils/Makefile
deleted file mode 100644
index 527cf3e..0000000
--- a/contrib/wpa/src/utils/Makefile
+++ /dev/null
@@ -1,36 +0,0 @@
-all: libutils.a
-
-clean:
- rm -f *~ *.o *.d libutils.a
-
-install:
- @echo Nothing to be made.
-
-
-include ../lib.rules
-
-#CFLAGS += -DWPA_TRACE
-CFLAGS += -DCONFIG_IPV6
-
-LIB_OBJS= \
- base64.o \
- common.o \
- ip_addr.o \
- radiotap.o \
- trace.o \
- uuid.o \
- wpa_debug.o \
- wpabuf.o
-
-# Pick correct OS wrapper implementation
-LIB_OBJS += os_unix.o
-
-# Pick correct event loop implementation
-LIB_OBJS += eloop.o
-
-#LIB_OBJS += pcsc_funcs.o
-
-libutils.a: $(LIB_OBJS)
- $(AR) crT $@ $?
-
--include $(OBJS:%.o=%.d)
diff --git a/contrib/wpa/src/utils/base64.c b/contrib/wpa/src/utils/base64.c
index 155bfce..af1307f 100644
--- a/contrib/wpa/src/utils/base64.c
+++ b/contrib/wpa/src/utils/base64.c
@@ -1,15 +1,9 @@
/*
* Base64 encoding/decoding (RFC1341)
- * Copyright (c) 2005, Jouni Malinen <j@w1.fi>
+ * Copyright (c) 2005-2011, Jouni Malinen <j@w1.fi>
*
- * This program is free software; you can redistribute it and/or modify
- * it under the terms of the GNU General Public License version 2 as
- * published by the Free Software Foundation.
- *
- * Alternatively, this software may be distributed under the terms of BSD
- * license.
- *
- * See README and COPYING for more details.
+ * This software may be distributed under the terms of the BSD license.
+ * See README for more details.
*/
#include "includes.h"
@@ -103,8 +97,9 @@ unsigned char * base64_encode(const unsigned char *src, size_t len,
unsigned char * base64_decode(const unsigned char *src, size_t len,
size_t *out_len)
{
- unsigned char dtable[256], *out, *pos, in[4], block[4], tmp;
+ unsigned char dtable[256], *out, *pos, block[4], tmp;
size_t i, count, olen;
+ int pad = 0;
os_memset(dtable, 0x80, 256);
for (i = 0; i < sizeof(base64_table) - 1; i++)
@@ -131,7 +126,8 @@ unsigned char * base64_decode(const unsigned char *src, size_t len,
if (tmp == 0x80)
continue;
- in[count] = src[i];
+ if (src[i] == '=')
+ pad++;
block[count] = tmp;
count++;
if (count == 4) {
@@ -139,16 +135,21 @@ unsigned char * base64_decode(const unsigned char *src, size_t len,
*pos++ = (block[1] << 4) | (block[2] >> 2);
*pos++ = (block[2] << 6) | block[3];
count = 0;
+ if (pad) {
+ if (pad == 1)
+ pos--;
+ else if (pad == 2)
+ pos -= 2;
+ else {
+ /* Invalid padding */
+ os_free(out);
+ return NULL;
+ }
+ break;
+ }
}
}
- if (pos > out) {
- if (in[2] == '=')
- pos -= 2;
- else if (in[3] == '=')
- pos--;
- }
-
*out_len = pos - out;
return out;
}
diff --git a/contrib/wpa/src/utils/base64.h b/contrib/wpa/src/utils/base64.h
index b87a168..aa21fd0 100644
--- a/contrib/wpa/src/utils/base64.h
+++ b/contrib/wpa/src/utils/base64.h
@@ -2,14 +2,8 @@
* Base64 encoding/decoding (RFC1341)
* Copyright (c) 2005, Jouni Malinen <j@w1.fi>
*
- * This program is free software; you can redistribute it and/or modify
- * it under the terms of the GNU General Public License version 2 as
- * published by the Free Software Foundation.
- *
- * Alternatively, this software may be distributed under the terms of BSD
- * license.
- *
- * See README and COPYING for more details.
+ * This software may be distributed under the terms of the BSD license.
+ * See README for more details.
*/
#ifndef BASE64_H
diff --git a/contrib/wpa/src/utils/build_config.h b/contrib/wpa/src/utils/build_config.h
index 3666778..f947388 100644
--- a/contrib/wpa/src/utils/build_config.h
+++ b/contrib/wpa/src/utils/build_config.h
@@ -2,14 +2,8 @@
* wpa_supplicant/hostapd - Build time configuration defines
* Copyright (c) 2005-2006, Jouni Malinen <j@w1.fi>
*
- * This program is free software; you can redistribute it and/or modify
- * it under the terms of the GNU General Public License version 2 as
- * published by the Free Software Foundation.
- *
- * Alternatively, this software may be distributed under the terms of BSD
- * license.
- *
- * See README and COPYING for more details.
+ * This software may be distributed under the terms of the BSD license.
+ * See README for more details.
*
* This header file can be used to define configuration defines that were
* originally defined in Makefile. This is mainly meant for IDE use or for
@@ -53,28 +47,6 @@
#endif /* USE_INTERNAL_CRYPTO */
#endif /* CONFIG_WIN32_DEFAULTS */
-#ifdef __SYMBIAN32__
-#define OS_NO_C_LIB_DEFINES
-#define CONFIG_ANSI_C_EXTRA
-#define CONFIG_NO_WPA_MSG
-#define CONFIG_NO_HOSTAPD_LOGGER
-#define CONFIG_NO_STDOUT_DEBUG
-#define CONFIG_BACKEND_FILE
-#define CONFIG_INTERNAL_LIBTOMMATH
-#define CONFIG_CRYPTO_INTERNAL
-#define IEEE8021X_EAPOL
-#define PKCS12_FUNCS
-#define EAP_MD5
-#define EAP_TLS
-#define EAP_MSCHAPv2
-#define EAP_PEAP
-#define EAP_TTLS
-#define EAP_GTC
-#define EAP_OTP
-#define EAP_LEAP
-#define EAP_FAST
-#endif /* __SYMBIAN32__ */
-
#ifdef CONFIG_XCODE_DEFAULTS
#define CONFIG_DRIVER_OSX
#define CONFIG_BACKEND_FILE
diff --git a/contrib/wpa/src/utils/common.c b/contrib/wpa/src/utils/common.c
index 1b8ea80..e636984 100644
--- a/contrib/wpa/src/utils/common.c
+++ b/contrib/wpa/src/utils/common.c
@@ -2,14 +2,8 @@
* wpa_supplicant/hostapd / common helper functions, etc.
* Copyright (c) 2002-2007, Jouni Malinen <j@w1.fi>
*
- * This program is free software; you can redistribute it and/or modify
- * it under the terms of the GNU General Public License version 2 as
- * published by the Free Software Foundation.
- *
- * Alternatively, this software may be distributed under the terms of BSD
- * license.
- *
- * See README and COPYING for more details.
+ * This software may be distributed under the terms of the BSD license.
+ * See README for more details.
*/
#include "includes.h"
@@ -29,7 +23,7 @@ static int hex2num(char c)
}
-static int hex2byte(const char *hex)
+int hex2byte(const char *hex)
{
int a, b;
a = hex2num(*hex++);
@@ -69,6 +63,30 @@ int hwaddr_aton(const char *txt, u8 *addr)
return 0;
}
+/**
+ * hwaddr_compact_aton - Convert ASCII string to MAC address (no colon delimitors format)
+ * @txt: MAC address as a string (e.g., "001122334455")
+ * @addr: Buffer for the MAC address (ETH_ALEN = 6 bytes)
+ * Returns: 0 on success, -1 on failure (e.g., string not a MAC address)
+ */
+int hwaddr_compact_aton(const char *txt, u8 *addr)
+{
+ int i;
+
+ for (i = 0; i < 6; i++) {
+ int a, b;
+
+ a = hex2num(*txt++);
+ if (a < 0)
+ return -1;
+ b = hex2num(*txt++);
+ if (b < 0)
+ return -1;
+ *addr++ = (a << 4) | b;
+ }
+
+ return 0;
+}
/**
* hwaddr_aton2 - Convert ASCII string to MAC address (in any known format)
@@ -326,6 +344,135 @@ TCHAR * wpa_strdup_tchar(const char *str)
#endif /* CONFIG_NATIVE_WINDOWS */
+void printf_encode(char *txt, size_t maxlen, const u8 *data, size_t len)
+{
+ char *end = txt + maxlen;
+ size_t i;
+
+ for (i = 0; i < len; i++) {
+ if (txt + 4 > end)
+ break;
+
+ switch (data[i]) {
+ case '\"':
+ *txt++ = '\\';
+ *txt++ = '\"';
+ break;
+ case '\\':
+ *txt++ = '\\';
+ *txt++ = '\\';
+ break;
+ case '\e':
+ *txt++ = '\\';
+ *txt++ = 'e';
+ break;
+ case '\n':
+ *txt++ = '\\';
+ *txt++ = 'n';
+ break;
+ case '\r':
+ *txt++ = '\\';
+ *txt++ = 'r';
+ break;
+ case '\t':
+ *txt++ = '\\';
+ *txt++ = 't';
+ break;
+ default:
+ if (data[i] >= 32 && data[i] <= 127) {
+ *txt++ = data[i];
+ } else {
+ txt += os_snprintf(txt, end - txt, "\\x%02x",
+ data[i]);
+ }
+ break;
+ }
+ }
+
+ *txt = '\0';
+}
+
+
+size_t printf_decode(u8 *buf, size_t maxlen, const char *str)
+{
+ const char *pos = str;
+ size_t len = 0;
+ int val;
+
+ while (*pos) {
+ if (len == maxlen)
+ break;
+ switch (*pos) {
+ case '\\':
+ pos++;
+ switch (*pos) {
+ case '\\':
+ buf[len++] = '\\';
+ pos++;
+ break;
+ case '"':
+ buf[len++] = '"';
+ pos++;
+ break;
+ case 'n':
+ buf[len++] = '\n';
+ pos++;
+ break;
+ case 'r':
+ buf[len++] = '\r';
+ pos++;
+ break;
+ case 't':
+ buf[len++] = '\t';
+ pos++;
+ break;
+ case 'e':
+ buf[len++] = '\e';
+ pos++;
+ break;
+ case 'x':
+ pos++;
+ val = hex2byte(pos);
+ if (val < 0) {
+ val = hex2num(*pos);
+ if (val < 0)
+ break;
+ buf[len++] = val;
+ pos++;
+ } else {
+ buf[len++] = val;
+ pos += 2;
+ }
+ break;
+ case '0':
+ case '1':
+ case '2':
+ case '3':
+ case '4':
+ case '5':
+ case '6':
+ case '7':
+ val = *pos++ - '0';
+ if (*pos >= '0' && *pos <= '7')
+ val = val * 8 + (*pos++ - '0');
+ if (*pos >= '0' && *pos <= '7')
+ val = val * 8 + (*pos++ - '0');
+ buf[len++] = val;
+ break;
+ default:
+ break;
+ }
+ break;
+ default:
+ buf[len++] = *pos++;
+ break;
+ }
+ }
+
+ return len;
+}
+
+
/**
* wpa_ssid_txt - Convert SSID to a printable string
* @ssid: SSID (32-octet string)
@@ -342,17 +489,14 @@ TCHAR * wpa_strdup_tchar(const char *str)
*/
const char * wpa_ssid_txt(const u8 *ssid, size_t ssid_len)
{
- static char ssid_txt[33];
- char *pos;
-
- if (ssid_len > 32)
- ssid_len = 32;
- os_memcpy(ssid_txt, ssid, ssid_len);
- ssid_txt[ssid_len] = '\0';
- for (pos = ssid_txt; *pos != '\0'; pos++) {
- if ((u8) *pos < 32 || (u8) *pos >= 127)
- *pos = '_';
+ static char ssid_txt[32 * 4 + 1];
+
+ if (ssid == NULL) {
+ ssid_txt[0] = '\0';
+ return ssid_txt;
}
+
+ printf_encode(ssid_txt, sizeof(ssid_txt), ssid, ssid_len);
return ssid_txt;
}
@@ -361,3 +505,108 @@ void * __hide_aliasing_typecast(void *foo)
{
return foo;
}
+
+
+char * wpa_config_parse_string(const char *value, size_t *len)
+{
+ if (*value == '"') {
+ const char *pos;
+ char *str;
+ value++;
+ pos = os_strrchr(value, '"');
+ if (pos == NULL || pos[1] != '\0')
+ return NULL;
+ *len = pos - value;
+ str = os_malloc(*len + 1);
+ if (str == NULL)
+ return NULL;
+ os_memcpy(str, value, *len);
+ str[*len] = '\0';
+ return str;
+ } else if (*value == 'P' && value[1] == '"') {
+ const char *pos;
+ char *tstr, *str;
+ size_t tlen;
+ value += 2;
+ pos = os_strrchr(value, '"');
+ if (pos == NULL || pos[1] != '\0')
+ return NULL;
+ tlen = pos - value;
+ tstr = os_malloc(tlen + 1);
+ if (tstr == NULL)
+ return NULL;
+ os_memcpy(tstr, value, tlen);
+ tstr[tlen] = '\0';
+
+ str = os_malloc(tlen + 1);
+ if (str == NULL) {
+ os_free(tstr);
+ return NULL;
+ }
+
+ *len = printf_decode((u8 *) str, tlen + 1, tstr);
+ os_free(tstr);
+
+ return str;
+ } else {
+ u8 *str;
+ size_t tlen, hlen = os_strlen(value);
+ if (hlen & 1)
+ return NULL;
+ tlen = hlen / 2;
+ str = os_malloc(tlen + 1);
+ if (str == NULL)
+ return NULL;
+ if (hexstr2bin(value, str, tlen)) {
+ os_free(str);
+ return NULL;
+ }
+ str[tlen] = '\0';
+ *len = tlen;
+ return (char *) str;
+ }
+}
+
+
+int is_hex(const u8 *data, size_t len)
+{
+ size_t i;
+
+ for (i = 0; i < len; i++) {
+ if (data[i] < 32 || data[i] >= 127)
+ return 1;
+ }
+ return 0;
+}
+
+
+size_t merge_byte_arrays(u8 *res, size_t res_len,
+ const u8 *src1, size_t src1_len,
+ const u8 *src2, size_t src2_len)
+{
+ size_t len = 0;
+
+ os_memset(res, 0, res_len);
+
+ if (src1) {
+ if (src1_len >= res_len) {
+ os_memcpy(res, src1, res_len);
+ return res_len;
+ }
+
+ os_memcpy(res, src1, src1_len);
+ len += src1_len;
+ }
+
+ if (src2) {
+ if (len + src2_len >= res_len) {
+ os_memcpy(res + len, src2, res_len - len);
+ return res_len;
+ }
+
+ os_memcpy(res + len, src2, src2_len);
+ len += src2_len;
+ }
+
+ return len;
+}
diff --git a/contrib/wpa/src/utils/common.h b/contrib/wpa/src/utils/common.h
index f17bf69..5fc916c 100644
--- a/contrib/wpa/src/utils/common.h
+++ b/contrib/wpa/src/utils/common.h
@@ -2,14 +2,8 @@
* wpa_supplicant/hostapd / common helper functions, etc.
* Copyright (c) 2002-2007, Jouni Malinen <j@w1.fi>
*
- * This program is free software; you can redistribute it and/or modify
- * it under the terms of the GNU General Public License version 2 as
- * published by the Free Software Foundation.
- *
- * Alternatively, this software may be distributed under the terms of BSD
- * license.
- *
- * See README and COPYING for more details.
+ * This software may be distributed under the terms of the BSD license.
+ * See README for more details.
*/
#ifndef COMMON_H
@@ -69,12 +63,6 @@ static inline unsigned int bswap_32(unsigned int v)
#endif
#endif /* CONFIG_TI_COMPILER */
-#ifdef __SYMBIAN32__
-#define __BIG_ENDIAN 4321
-#define __LITTLE_ENDIAN 1234
-#define __BYTE_ORDER __LITTLE_ENDIAN
-#endif /* __SYMBIAN32__ */
-
#ifdef CONFIG_NATIVE_WINDOWS
#include <winsock.h>
@@ -138,16 +126,6 @@ typedef unsigned char u8;
#define WPA_TYPES_DEFINED
#endif /* CONFIG_TI_COMPILER */
-#ifdef __SYMBIAN32__
-#define __REMOVE_PLATSEC_DIAGNOSTICS__
-#include <e32def.h>
-typedef TUint64 u64;
-typedef TUint32 u32;
-typedef TUint16 u16;
-typedef TUint8 u8;
-#define WPA_TYPES_DEFINED
-#endif /* __SYMBIAN32__ */
-
#ifndef WPA_TYPES_DEFINED
#ifdef CONFIG_USE_INTTYPES_H
#include <inttypes.h>
@@ -320,6 +298,9 @@ static inline unsigned int wpa_swap_32(unsigned int v)
#ifndef ETH_P_ALL
#define ETH_P_ALL 0x0003
#endif
+#ifndef ETH_P_80211_ENCAP
+#define ETH_P_80211_ENCAP 0x890d /* TDLS comes under this category */
+#endif
#ifndef ETH_P_PAE
#define ETH_P_PAE 0x888E /* Port Access Entity (IEEE 802.1X) */
#endif /* ETH_P_PAE */
@@ -402,6 +383,12 @@ void perror(const char *s);
#ifndef MAC2STR
#define MAC2STR(a) (a)[0], (a)[1], (a)[2], (a)[3], (a)[4], (a)[5]
#define MACSTR "%02x:%02x:%02x:%02x:%02x:%02x"
+
+/*
+ * Compact form for string representation of MAC address
+ * To be used, e.g., for constructing dbus paths for P2P Devices
+ */
+#define COMPACT_MACSTR "%02x%02x%02x%02x%02x%02x"
#endif
#ifndef BIT
@@ -436,7 +423,9 @@ typedef u64 __bitwise le64;
#endif /* __must_check */
int hwaddr_aton(const char *txt, u8 *addr);
+int hwaddr_compact_aton(const char *txt, u8 *addr);
int hwaddr_aton2(const char *txt, u8 *addr);
+int hex2byte(const char *hex);
int hexstr2bin(const char *hex, u8 *buf, size_t len);
void inc_byte_array(u8 *counter, size_t len);
void wpa_get_ntp_timestamp(u8 *buf);
@@ -452,13 +441,29 @@ TCHAR * wpa_strdup_tchar(const char *str);
#define wpa_strdup_tchar(s) strdup((s))
#endif /* CONFIG_NATIVE_WINDOWS */
+void printf_encode(char *txt, size_t maxlen, const u8 *data, size_t len);
+size_t printf_decode(u8 *buf, size_t maxlen, const char *str);
+
const char * wpa_ssid_txt(const u8 *ssid, size_t ssid_len);
+char * wpa_config_parse_string(const char *value, size_t *len);
+int is_hex(const u8 *data, size_t len);
+size_t merge_byte_arrays(u8 *res, size_t res_len,
+ const u8 *src1, size_t src1_len,
+ const u8 *src2, size_t src2_len);
+
static inline int is_zero_ether_addr(const u8 *a)
{
return !(a[0] | a[1] | a[2] | a[3] | a[4] | a[5]);
}
+static inline int is_broadcast_ether_addr(const u8 *a)
+{
+ return (a[0] & a[1] & a[2] & a[3] & a[4] & a[5]) == 0xff;
+}
+
+#define broadcast_ether_addr (const u8 *) "\xff\xff\xff\xff\xff\xff"
+
#include "wpa_debug.h"
@@ -474,4 +479,11 @@ static inline int is_zero_ether_addr(const u8 *a)
void * __hide_aliasing_typecast(void *foo);
#define aliasing_hide_typecast(a,t) (t *) __hide_aliasing_typecast((a))
+#ifdef CONFIG_VALGRIND
+#include <valgrind/memcheck.h>
+#define WPA_MEM_DEFINED(ptr, len) VALGRIND_MAKE_MEM_DEFINED((ptr), (len))
+#else /* CONFIG_VALGRIND */
+#define WPA_MEM_DEFINED(ptr, len) do { } while (0)
+#endif /* CONFIG_VALGRIND */
+
#endif /* COMMON_H */
diff --git a/contrib/wpa/src/utils/edit.c b/contrib/wpa/src/utils/edit.c
new file mode 100644
index 0000000..b01e08d
--- /dev/null
+++ b/contrib/wpa/src/utils/edit.c
@@ -0,0 +1,1174 @@
+/*
+ * Command line editing and history
+ * Copyright (c) 2010-2011, Jouni Malinen <j@w1.fi>
+ *
+ * This software may be distributed under the terms of the BSD license.
+ * See README for more details.
+ */
+
+#include "includes.h"
+#include <termios.h>
+
+#include "common.h"
+#include "eloop.h"
+#include "list.h"
+#include "edit.h"
+
+#define CMD_BUF_LEN 256
+static char cmdbuf[CMD_BUF_LEN];
+static int cmdbuf_pos = 0;
+static int cmdbuf_len = 0;
+static char currbuf[CMD_BUF_LEN];
+static int currbuf_valid = 0;
+static const char *ps2 = NULL;
+
+#define HISTORY_MAX 100
+
+struct edit_history {
+ struct dl_list list;
+ char str[1];
+};
+
+static struct dl_list history_list;
+static struct edit_history *history_curr;
+
+static void *edit_cb_ctx;
+static void (*edit_cmd_cb)(void *ctx, char *cmd);
+static void (*edit_eof_cb)(void *ctx);
+static char ** (*edit_completion_cb)(void *ctx, const char *cmd, int pos) =
+ NULL;
+
+static struct termios prevt, newt;
+
+
+#define CLEAR_END_LINE "\e[K"
+
+
+void edit_clear_line(void)
+{
+ int i;
+ putchar('\r');
+ for (i = 0; i < cmdbuf_len + 2 + (ps2 ? (int) os_strlen(ps2) : 0); i++)
+ putchar(' ');
+}
+
+
+static void move_start(void)
+{
+ cmdbuf_pos = 0;
+ edit_redraw();
+}
+
+
+static void move_end(void)
+{
+ cmdbuf_pos = cmdbuf_len;
+ edit_redraw();
+}
+
+
+static void move_left(void)
+{
+ if (cmdbuf_pos > 0) {
+ cmdbuf_pos--;
+ edit_redraw();
+ }
+}
+
+
+static void move_right(void)
+{
+ if (cmdbuf_pos < cmdbuf_len) {
+ cmdbuf_pos++;
+ edit_redraw();
+ }
+}
+
+
+static void move_word_left(void)
+{
+ while (cmdbuf_pos > 0 && cmdbuf[cmdbuf_pos - 1] == ' ')
+ cmdbuf_pos--;
+ while (cmdbuf_pos > 0 && cmdbuf[cmdbuf_pos - 1] != ' ')
+ cmdbuf_pos--;
+ edit_redraw();
+}
+
+
+static void move_word_right(void)
+{
+ while (cmdbuf_pos < cmdbuf_len && cmdbuf[cmdbuf_pos] == ' ')
+ cmdbuf_pos++;
+ while (cmdbuf_pos < cmdbuf_len && cmdbuf[cmdbuf_pos] != ' ')
+ cmdbuf_pos++;
+ edit_redraw();
+}
+
+
+static void delete_left(void)
+{
+ if (cmdbuf_pos == 0)
+ return;
+
+ edit_clear_line();
+ os_memmove(cmdbuf + cmdbuf_pos - 1, cmdbuf + cmdbuf_pos,
+ cmdbuf_len - cmdbuf_pos);
+ cmdbuf_pos--;
+ cmdbuf_len--;
+ edit_redraw();
+}
+
+
+static void delete_current(void)
+{
+ if (cmdbuf_pos == cmdbuf_len)
+ return;
+
+ edit_clear_line();
+ os_memmove(cmdbuf + cmdbuf_pos, cmdbuf + cmdbuf_pos + 1,
+ cmdbuf_len - cmdbuf_pos);
+ cmdbuf_len--;
+ edit_redraw();
+}
+
+
+static void delete_word(void)
+{
+ int pos;
+
+ edit_clear_line();
+ pos = cmdbuf_pos;
+ while (pos > 0 && cmdbuf[pos - 1] == ' ')
+ pos--;
+ while (pos > 0 && cmdbuf[pos - 1] != ' ')
+ pos--;
+ os_memmove(cmdbuf + pos, cmdbuf + cmdbuf_pos, cmdbuf_len - cmdbuf_pos);
+ cmdbuf_len -= cmdbuf_pos - pos;
+ cmdbuf_pos = pos;
+ edit_redraw();
+}
+
+
+static void clear_left(void)
+{
+ if (cmdbuf_pos == 0)
+ return;
+
+ edit_clear_line();
+ os_memmove(cmdbuf, cmdbuf + cmdbuf_pos, cmdbuf_len - cmdbuf_pos);
+ cmdbuf_len -= cmdbuf_pos;
+ cmdbuf_pos = 0;
+ edit_redraw();
+}
+
+
+static void clear_right(void)
+{
+ if (cmdbuf_pos == cmdbuf_len)
+ return;
+
+ edit_clear_line();
+ cmdbuf_len = cmdbuf_pos;
+ edit_redraw();
+}
+
+
+static void history_add(const char *str)
+{
+ struct edit_history *h, *match = NULL, *last = NULL;
+ size_t len, count = 0;
+
+ if (str[0] == '\0')
+ return;
+
+ dl_list_for_each(h, &history_list, struct edit_history, list) {
+ if (os_strcmp(str, h->str) == 0) {
+ match = h;
+ break;
+ }
+ last = h;
+ count++;
+ }
+
+ if (match) {
+ dl_list_del(&h->list);
+ dl_list_add(&history_list, &h->list);
+ history_curr = h;
+ return;
+ }
+
+ if (count >= HISTORY_MAX && last) {
+ dl_list_del(&last->list);
+ os_free(last);
+ }
+
+ len = os_strlen(str);
+ h = os_zalloc(sizeof(*h) + len);
+ if (h == NULL)
+ return;
+ dl_list_add(&history_list, &h->list);
+ os_strlcpy(h->str, str, len + 1);
+ history_curr = h;
+}
+
+
+static void history_use(void)
+{
+ edit_clear_line();
+ cmdbuf_len = cmdbuf_pos = os_strlen(history_curr->str);
+ os_memcpy(cmdbuf, history_curr->str, cmdbuf_len);
+ edit_redraw();
+}
+
+
+static void history_prev(void)
+{
+ if (history_curr == NULL)
+ return;
+
+ if (history_curr ==
+ dl_list_first(&history_list, struct edit_history, list)) {
+ if (!currbuf_valid) {
+ cmdbuf[cmdbuf_len] = '\0';
+ os_memcpy(currbuf, cmdbuf, cmdbuf_len + 1);
+ currbuf_valid = 1;
+ history_use();
+ return;
+ }
+ }
+
+ if (history_curr ==
+ dl_list_last(&history_list, struct edit_history, list))
+ return;
+
+ history_curr = dl_list_entry(history_curr->list.next,
+ struct edit_history, list);
+ history_use();
+}
+
+
+static void history_next(void)
+{
+ if (history_curr == NULL ||
+ history_curr ==
+ dl_list_first(&history_list, struct edit_history, list)) {
+ if (currbuf_valid) {
+ currbuf_valid = 0;
+ edit_clear_line();
+ cmdbuf_len = cmdbuf_pos = os_strlen(currbuf);
+ os_memcpy(cmdbuf, currbuf, cmdbuf_len);
+ edit_redraw();
+ }
+ return;
+ }
+
+ history_curr = dl_list_entry(history_curr->list.prev,
+ struct edit_history, list);
+ history_use();
+}
+
+
+static void history_read(const char *fname)
+{
+ FILE *f;
+ char buf[CMD_BUF_LEN], *pos;
+
+ f = fopen(fname, "r");
+ if (f == NULL)
+ return;
+
+ while (fgets(buf, CMD_BUF_LEN, f)) {
+ for (pos = buf; *pos; pos++) {
+ if (*pos == '\r' || *pos == '\n') {
+ *pos = '\0';
+ break;
+ }
+ }
+ history_add(buf);
+ }
+
+ fclose(f);
+}
+
+
+static void history_write(const char *fname,
+ int (*filter_cb)(void *ctx, const char *cmd))
+{
+ FILE *f;
+ struct edit_history *h;
+
+ f = fopen(fname, "w");
+ if (f == NULL)
+ return;
+
+ dl_list_for_each_reverse(h, &history_list, struct edit_history, list) {
+ if (filter_cb && filter_cb(edit_cb_ctx, h->str))
+ continue;
+ fprintf(f, "%s\n", h->str);
+ }
+
+ fclose(f);
+}
+
+
+static void history_debug_dump(void)
+{
+ struct edit_history *h;
+ edit_clear_line();
+ printf("\r");
+ dl_list_for_each_reverse(h, &history_list, struct edit_history, list)
+ printf("%s%s\n", h == history_curr ? "[C]" : "", h->str);
+ if (currbuf_valid)
+ printf("{%s}\n", currbuf);
+ edit_redraw();
+}
+
+
+static void insert_char(int c)
+{
+ if (cmdbuf_len >= (int) sizeof(cmdbuf) - 1)
+ return;
+ if (cmdbuf_len == cmdbuf_pos) {
+ cmdbuf[cmdbuf_pos++] = c;
+ cmdbuf_len++;
+ putchar(c);
+ fflush(stdout);
+ } else {
+ os_memmove(cmdbuf + cmdbuf_pos + 1, cmdbuf + cmdbuf_pos,
+ cmdbuf_len - cmdbuf_pos);
+ cmdbuf[cmdbuf_pos++] = c;
+ cmdbuf_len++;
+ edit_redraw();
+ }
+}
+
+
+static void process_cmd(void)
+{
+
+ if (cmdbuf_len == 0) {
+ printf("\n%s> ", ps2 ? ps2 : "");
+ fflush(stdout);
+ return;
+ }
+ printf("\n");
+ cmdbuf[cmdbuf_len] = '\0';
+ history_add(cmdbuf);
+ cmdbuf_pos = 0;
+ cmdbuf_len = 0;
+ edit_cmd_cb(edit_cb_ctx, cmdbuf);
+ printf("%s> ", ps2 ? ps2 : "");
+ fflush(stdout);
+}
+
+
+static void free_completions(char **c)
+{
+ int i;
+ if (c == NULL)
+ return;
+ for (i = 0; c[i]; i++)
+ os_free(c[i]);
+ os_free(c);
+}
+
+
+static int filter_strings(char **c, char *str, size_t len)
+{
+ int i, j;
+
+ for (i = 0, j = 0; c[j]; j++) {
+ if (os_strncasecmp(c[j], str, len) == 0) {
+ if (i != j) {
+ c[i] = c[j];
+ c[j] = NULL;
+ }
+ i++;
+ } else {
+ os_free(c[j]);
+ c[j] = NULL;
+ }
+ }
+ c[i] = NULL;
+ return i;
+}
+
+
+static int common_len(const char *a, const char *b)
+{
+ int len = 0;
+ while (a[len] && a[len] == b[len])
+ len++;
+ return len;
+}
+
+
+static int max_common_length(char **c)
+{
+ int len, i;
+
+ len = os_strlen(c[0]);
+ for (i = 1; c[i]; i++) {
+ int same = common_len(c[0], c[i]);
+ if (same < len)
+ len = same;
+ }
+
+ return len;
+}
+
+
+static int cmp_str(const void *a, const void *b)
+{
+ return os_strcmp(* (const char **) a, * (const char **) b);
+}
+
+static void complete(int list)
+{
+ char **c;
+ int i, len, count;
+ int start, end;
+ int room, plen, add_space;
+
+ if (edit_completion_cb == NULL)
+ return;
+
+ cmdbuf[cmdbuf_len] = '\0';
+ c = edit_completion_cb(edit_cb_ctx, cmdbuf, cmdbuf_pos);
+ if (c == NULL)
+ return;
+
+ end = cmdbuf_pos;
+ start = end;
+ while (start > 0 && cmdbuf[start - 1] != ' ')
+ start--;
+ plen = end - start;
+
+ count = filter_strings(c, &cmdbuf[start], plen);
+ if (count == 0) {
+ free_completions(c);
+ return;
+ }
+
+ len = max_common_length(c);
+ if (len <= plen && count > 1) {
+ if (list) {
+ qsort(c, count, sizeof(char *), cmp_str);
+ edit_clear_line();
+ printf("\r");
+ for (i = 0; c[i]; i++)
+ printf("%s%s", i > 0 ? " " : "", c[i]);
+ printf("\n");
+ edit_redraw();
+ }
+ free_completions(c);
+ return;
+ }
+ len -= plen;
+
+ room = sizeof(cmdbuf) - 1 - cmdbuf_len;
+ if (room < len)
+ len = room;
+ add_space = count == 1 && len < room;
+
+ os_memmove(cmdbuf + cmdbuf_pos + len + add_space, cmdbuf + cmdbuf_pos,
+ cmdbuf_len - cmdbuf_pos);
+ os_memcpy(&cmdbuf[cmdbuf_pos - plen], c[0], plen + len);
+ if (add_space)
+ cmdbuf[cmdbuf_pos + len] = ' ';
+
+ cmdbuf_pos += len + add_space;
+ cmdbuf_len += len + add_space;
+
+ edit_redraw();
+
+ free_completions(c);
+}
+
+
+enum edit_key_code {
+ EDIT_KEY_NONE = 256,
+ EDIT_KEY_TAB,
+ EDIT_KEY_UP,
+ EDIT_KEY_DOWN,
+ EDIT_KEY_RIGHT,
+ EDIT_KEY_LEFT,
+ EDIT_KEY_ENTER,
+ EDIT_KEY_BACKSPACE,
+ EDIT_KEY_INSERT,
+ EDIT_KEY_DELETE,
+ EDIT_KEY_HOME,
+ EDIT_KEY_END,
+ EDIT_KEY_PAGE_UP,
+ EDIT_KEY_PAGE_DOWN,
+ EDIT_KEY_F1,
+ EDIT_KEY_F2,
+ EDIT_KEY_F3,
+ EDIT_KEY_F4,
+ EDIT_KEY_F5,
+ EDIT_KEY_F6,
+ EDIT_KEY_F7,
+ EDIT_KEY_F8,
+ EDIT_KEY_F9,
+ EDIT_KEY_F10,
+ EDIT_KEY_F11,
+ EDIT_KEY_F12,
+ EDIT_KEY_CTRL_UP,
+ EDIT_KEY_CTRL_DOWN,
+ EDIT_KEY_CTRL_RIGHT,
+ EDIT_KEY_CTRL_LEFT,
+ EDIT_KEY_CTRL_A,
+ EDIT_KEY_CTRL_B,
+ EDIT_KEY_CTRL_D,
+ EDIT_KEY_CTRL_E,
+ EDIT_KEY_CTRL_F,
+ EDIT_KEY_CTRL_G,
+ EDIT_KEY_CTRL_H,
+ EDIT_KEY_CTRL_J,
+ EDIT_KEY_CTRL_K,
+ EDIT_KEY_CTRL_L,
+ EDIT_KEY_CTRL_N,
+ EDIT_KEY_CTRL_O,
+ EDIT_KEY_CTRL_P,
+ EDIT_KEY_CTRL_R,
+ EDIT_KEY_CTRL_T,
+ EDIT_KEY_CTRL_U,
+ EDIT_KEY_CTRL_V,
+ EDIT_KEY_CTRL_W,
+ EDIT_KEY_ALT_UP,
+ EDIT_KEY_ALT_DOWN,
+ EDIT_KEY_ALT_RIGHT,
+ EDIT_KEY_ALT_LEFT,
+ EDIT_KEY_SHIFT_UP,
+ EDIT_KEY_SHIFT_DOWN,
+ EDIT_KEY_SHIFT_RIGHT,
+ EDIT_KEY_SHIFT_LEFT,
+ EDIT_KEY_ALT_SHIFT_UP,
+ EDIT_KEY_ALT_SHIFT_DOWN,
+ EDIT_KEY_ALT_SHIFT_RIGHT,
+ EDIT_KEY_ALT_SHIFT_LEFT,
+ EDIT_KEY_EOF
+};
+
+static void show_esc_buf(const char *esc_buf, char c, int i)
+{
+ edit_clear_line();
+ printf("\rESC buffer '%s' c='%c' [%d]\n", esc_buf, c, i);
+ edit_redraw();
+}
+
+
+static enum edit_key_code esc_seq_to_key1_no(char last)
+{
+ switch (last) {
+ case 'A':
+ return EDIT_KEY_UP;
+ case 'B':
+ return EDIT_KEY_DOWN;
+ case 'C':
+ return EDIT_KEY_RIGHT;
+ case 'D':
+ return EDIT_KEY_LEFT;
+ default:
+ return EDIT_KEY_NONE;
+ }
+}
+
+
+static enum edit_key_code esc_seq_to_key1_shift(char last)
+{
+ switch (last) {
+ case 'A':
+ return EDIT_KEY_SHIFT_UP;
+ case 'B':
+ return EDIT_KEY_SHIFT_DOWN;
+ case 'C':
+ return EDIT_KEY_SHIFT_RIGHT;
+ case 'D':
+ return EDIT_KEY_SHIFT_LEFT;
+ default:
+ return EDIT_KEY_NONE;
+ }
+}
+
+
+static enum edit_key_code esc_seq_to_key1_alt(char last)
+{
+ switch (last) {
+ case 'A':
+ return EDIT_KEY_ALT_UP;
+ case 'B':
+ return EDIT_KEY_ALT_DOWN;
+ case 'C':
+ return EDIT_KEY_ALT_RIGHT;
+ case 'D':
+ return EDIT_KEY_ALT_LEFT;
+ default:
+ return EDIT_KEY_NONE;
+ }
+}
+
+
+static enum edit_key_code esc_seq_to_key1_alt_shift(char last)
+{
+ switch (last) {
+ case 'A':
+ return EDIT_KEY_ALT_SHIFT_UP;
+ case 'B':
+ return EDIT_KEY_ALT_SHIFT_DOWN;
+ case 'C':
+ return EDIT_KEY_ALT_SHIFT_RIGHT;
+ case 'D':
+ return EDIT_KEY_ALT_SHIFT_LEFT;
+ default:
+ return EDIT_KEY_NONE;
+ }
+}
+
+
+static enum edit_key_code esc_seq_to_key1_ctrl(char last)
+{
+ switch (last) {
+ case 'A':
+ return EDIT_KEY_CTRL_UP;
+ case 'B':
+ return EDIT_KEY_CTRL_DOWN;
+ case 'C':
+ return EDIT_KEY_CTRL_RIGHT;
+ case 'D':
+ return EDIT_KEY_CTRL_LEFT;
+ default:
+ return EDIT_KEY_NONE;
+ }
+}
+
+
+static enum edit_key_code esc_seq_to_key1(int param1, int param2, char last)
+{
+ /* ESC-[<param1>;<param2><last> */
+
+ if (param1 < 0 && param2 < 0)
+ return esc_seq_to_key1_no(last);
+
+ if (param1 == 1 && param2 == 2)
+ return esc_seq_to_key1_shift(last);
+
+ if (param1 == 1 && param2 == 3)
+ return esc_seq_to_key1_alt(last);
+
+ if (param1 == 1 && param2 == 4)
+ return esc_seq_to_key1_alt_shift(last);
+
+ if (param1 == 1 && param2 == 5)
+ return esc_seq_to_key1_ctrl(last);
+
+ if (param2 < 0) {
+ if (last != '~')
+ return EDIT_KEY_NONE;
+ switch (param1) {
+ case 2:
+ return EDIT_KEY_INSERT;
+ case 3:
+ return EDIT_KEY_DELETE;
+ case 5:
+ return EDIT_KEY_PAGE_UP;
+ case 6:
+ return EDIT_KEY_PAGE_DOWN;
+ case 15:
+ return EDIT_KEY_F5;
+ case 17:
+ return EDIT_KEY_F6;
+ case 18:
+ return EDIT_KEY_F7;
+ case 19:
+ return EDIT_KEY_F8;
+ case 20:
+ return EDIT_KEY_F9;
+ case 21:
+ return EDIT_KEY_F10;
+ case 23:
+ return EDIT_KEY_F11;
+ case 24:
+ return EDIT_KEY_F12;
+ }
+ }
+
+ return EDIT_KEY_NONE;
+}
+
+
+static enum edit_key_code esc_seq_to_key2(int param1, int param2, char last)
+{
+ /* ESC-O<param1>;<param2><last> */
+
+ if (param1 >= 0 || param2 >= 0)
+ return EDIT_KEY_NONE;
+
+ switch (last) {
+ case 'F':
+ return EDIT_KEY_END;
+ case 'H':
+ return EDIT_KEY_HOME;
+ case 'P':
+ return EDIT_KEY_F1;
+ case 'Q':
+ return EDIT_KEY_F2;
+ case 'R':
+ return EDIT_KEY_F3;
+ case 'S':
+ return EDIT_KEY_F4;
+ default:
+ return EDIT_KEY_NONE;
+ }
+}
+
+
+static enum edit_key_code esc_seq_to_key(char *seq)
+{
+ char last, *pos;
+ int param1 = -1, param2 = -1;
+ enum edit_key_code ret = EDIT_KEY_NONE;
+
+ last = '\0';
+ for (pos = seq; *pos; pos++)
+ last = *pos;
+
+ if (seq[1] >= '0' && seq[1] <= '9') {
+ param1 = atoi(&seq[1]);
+ pos = os_strchr(seq, ';');
+ if (pos)
+ param2 = atoi(pos + 1);
+ }
+
+ if (seq[0] == '[')
+ ret = esc_seq_to_key1(param1, param2, last);
+ else if (seq[0] == 'O')
+ ret = esc_seq_to_key2(param1, param2, last);
+
+ if (ret != EDIT_KEY_NONE)
+ return ret;
+
+ edit_clear_line();
+ printf("\rUnknown escape sequence '%s'\n", seq);
+ edit_redraw();
+ return EDIT_KEY_NONE;
+}
+
+
+static enum edit_key_code edit_read_key(int sock)
+{
+ int c;
+ unsigned char buf[1];
+ int res;
+ static int esc = -1;
+ static char esc_buf[7];
+
+ res = read(sock, buf, 1);
+ if (res < 0)
+ perror("read");
+ if (res <= 0)
+ return EDIT_KEY_EOF;
+
+ c = buf[0];
+
+ if (esc >= 0) {
+ if (c == 27 /* ESC */) {
+ esc = 0;
+ return EDIT_KEY_NONE;
+ }
+
+ if (esc == 6) {
+ show_esc_buf(esc_buf, c, 0);
+ esc = -1;
+ } else {
+ esc_buf[esc++] = c;
+ esc_buf[esc] = '\0';
+ }
+ }
+
+ if (esc == 1) {
+ if (esc_buf[0] != '[' && esc_buf[0] != 'O') {
+ show_esc_buf(esc_buf, c, 1);
+ esc = -1;
+ return EDIT_KEY_NONE;
+ } else
+ return EDIT_KEY_NONE; /* Escape sequence continues */
+ }
+
+ if (esc > 1) {
+ if ((c >= '0' && c <= '9') || c == ';')
+ return EDIT_KEY_NONE; /* Escape sequence continues */
+
+ if (c == '~' || (c >= 'A' && c <= 'Z')) {
+ esc = -1;
+ return esc_seq_to_key(esc_buf);
+ }
+
+ show_esc_buf(esc_buf, c, 2);
+ esc = -1;
+ return EDIT_KEY_NONE;
+ }
+
+ switch (c) {
+ case 1:
+ return EDIT_KEY_CTRL_A;
+ case 2:
+ return EDIT_KEY_CTRL_B;
+ case 4:
+ return EDIT_KEY_CTRL_D;
+ case 5:
+ return EDIT_KEY_CTRL_E;
+ case 6:
+ return EDIT_KEY_CTRL_F;
+ case 7:
+ return EDIT_KEY_CTRL_G;
+ case 8:
+ return EDIT_KEY_CTRL_H;
+ case 9:
+ return EDIT_KEY_TAB;
+ case 10:
+ return EDIT_KEY_CTRL_J;
+ case 13: /* CR */
+ return EDIT_KEY_ENTER;
+ case 11:
+ return EDIT_KEY_CTRL_K;
+ case 12:
+ return EDIT_KEY_CTRL_L;
+ case 14:
+ return EDIT_KEY_CTRL_N;
+ case 15:
+ return EDIT_KEY_CTRL_O;
+ case 16:
+ return EDIT_KEY_CTRL_P;
+ case 18:
+ return EDIT_KEY_CTRL_R;
+ case 20:
+ return EDIT_KEY_CTRL_T;
+ case 21:
+ return EDIT_KEY_CTRL_U;
+ case 22:
+ return EDIT_KEY_CTRL_V;
+ case 23:
+ return EDIT_KEY_CTRL_W;
+ case 27: /* ESC */
+ esc = 0;
+ return EDIT_KEY_NONE;
+ case 127:
+ return EDIT_KEY_BACKSPACE;
+ default:
+ return c;
+ }
+}
+
+
+static char search_buf[21];
+static int search_skip;
+
+static char * search_find(void)
+{
+ struct edit_history *h;
+ size_t len = os_strlen(search_buf);
+ int skip = search_skip;
+
+ if (len == 0)
+ return NULL;
+
+ dl_list_for_each(h, &history_list, struct edit_history, list) {
+ if (os_strstr(h->str, search_buf)) {
+ if (skip == 0)
+ return h->str;
+ skip--;
+ }
+ }
+
+ search_skip = 0;
+ return NULL;
+}
+
+
+static void search_redraw(void)
+{
+ char *match = search_find();
+ printf("\rsearch '%s': %s" CLEAR_END_LINE,
+ search_buf, match ? match : "");
+ printf("\rsearch '%s", search_buf);
+ fflush(stdout);
+}
+
+
+static void search_start(void)
+{
+ edit_clear_line();
+ search_buf[0] = '\0';
+ search_skip = 0;
+ search_redraw();
+}
+
+
+static void search_clear(void)
+{
+ search_redraw();
+ printf("\r" CLEAR_END_LINE);
+}
+
+
+static void search_stop(void)
+{
+ char *match = search_find();
+ search_buf[0] = '\0';
+ search_clear();
+ if (match) {
+ os_strlcpy(cmdbuf, match, CMD_BUF_LEN);
+ cmdbuf_len = os_strlen(cmdbuf);
+ cmdbuf_pos = cmdbuf_len;
+ }
+ edit_redraw();
+}
+
+
+static void search_cancel(void)
+{
+ search_buf[0] = '\0';
+ search_clear();
+ edit_redraw();
+}
+
+
+static void search_backspace(void)
+{
+ size_t len;
+ len = os_strlen(search_buf);
+ if (len == 0)
+ return;
+ search_buf[len - 1] = '\0';
+ search_skip = 0;
+ search_redraw();
+}
+
+
+static void search_next(void)
+{
+ search_skip++;
+ search_find();
+ search_redraw();
+}
+
+
+static void search_char(char c)
+{
+ size_t len;
+ len = os_strlen(search_buf);
+ if (len == sizeof(search_buf) - 1)
+ return;
+ search_buf[len] = c;
+ search_buf[len + 1] = '\0';
+ search_skip = 0;
+ search_redraw();
+}
+
+
+static enum edit_key_code search_key(enum edit_key_code c)
+{
+ switch (c) {
+ case EDIT_KEY_ENTER:
+ case EDIT_KEY_CTRL_J:
+ case EDIT_KEY_LEFT:
+ case EDIT_KEY_RIGHT:
+ case EDIT_KEY_HOME:
+ case EDIT_KEY_END:
+ case EDIT_KEY_CTRL_A:
+ case EDIT_KEY_CTRL_E:
+ search_stop();
+ return c;
+ case EDIT_KEY_DOWN:
+ case EDIT_KEY_UP:
+ search_cancel();
+ return EDIT_KEY_EOF;
+ case EDIT_KEY_CTRL_H:
+ case EDIT_KEY_BACKSPACE:
+ search_backspace();
+ break;
+ case EDIT_KEY_CTRL_R:
+ search_next();
+ break;
+ default:
+ if (c >= 32 && c <= 255)
+ search_char(c);
+ break;
+ }
+
+ return EDIT_KEY_NONE;
+}
+
+
+static void edit_read_char(int sock, void *eloop_ctx, void *sock_ctx)
+{
+ static int last_tab = 0;
+ static int search = 0;
+ enum edit_key_code c;
+
+ c = edit_read_key(sock);
+
+ if (search) {
+ c = search_key(c);
+ if (c == EDIT_KEY_NONE)
+ return;
+ search = 0;
+ if (c == EDIT_KEY_EOF)
+ return;
+ }
+
+ if (c != EDIT_KEY_TAB && c != EDIT_KEY_NONE)
+ last_tab = 0;
+
+ switch (c) {
+ case EDIT_KEY_NONE:
+ break;
+ case EDIT_KEY_EOF:
+ edit_eof_cb(edit_cb_ctx);
+ break;
+ case EDIT_KEY_TAB:
+ complete(last_tab);
+ last_tab = 1;
+ break;
+ case EDIT_KEY_UP:
+ case EDIT_KEY_CTRL_P:
+ history_prev();
+ break;
+ case EDIT_KEY_DOWN:
+ case EDIT_KEY_CTRL_N:
+ history_next();
+ break;
+ case EDIT_KEY_RIGHT:
+ case EDIT_KEY_CTRL_F:
+ move_right();
+ break;
+ case EDIT_KEY_LEFT:
+ case EDIT_KEY_CTRL_B:
+ move_left();
+ break;
+ case EDIT_KEY_CTRL_RIGHT:
+ move_word_right();
+ break;
+ case EDIT_KEY_CTRL_LEFT:
+ move_word_left();
+ break;
+ case EDIT_KEY_DELETE:
+ delete_current();
+ break;
+ case EDIT_KEY_END:
+ move_end();
+ break;
+ case EDIT_KEY_HOME:
+ case EDIT_KEY_CTRL_A:
+ move_start();
+ break;
+ case EDIT_KEY_F2:
+ history_debug_dump();
+ break;
+ case EDIT_KEY_CTRL_D:
+ if (cmdbuf_len > 0) {
+ delete_current();
+ return;
+ }
+ printf("\n");
+ edit_eof_cb(edit_cb_ctx);
+ break;
+ case EDIT_KEY_CTRL_E:
+ move_end();
+ break;
+ case EDIT_KEY_CTRL_H:
+ case EDIT_KEY_BACKSPACE:
+ delete_left();
+ break;
+ case EDIT_KEY_ENTER:
+ case EDIT_KEY_CTRL_J:
+ process_cmd();
+ break;
+ case EDIT_KEY_CTRL_K:
+ clear_right();
+ break;
+ case EDIT_KEY_CTRL_L:
+ edit_clear_line();
+ edit_redraw();
+ break;
+ case EDIT_KEY_CTRL_R:
+ search = 1;
+ search_start();
+ break;
+ case EDIT_KEY_CTRL_U:
+ clear_left();
+ break;
+ case EDIT_KEY_CTRL_W:
+ delete_word();
+ break;
+ default:
+ if (c >= 32 && c <= 255)
+ insert_char(c);
+ break;
+ }
+}
+
+
+int edit_init(void (*cmd_cb)(void *ctx, char *cmd),
+ void (*eof_cb)(void *ctx),
+ char ** (*completion_cb)(void *ctx, const char *cmd, int pos),
+ void *ctx, const char *history_file, const char *ps)
+{
+ currbuf[0] = '\0';
+ dl_list_init(&history_list);
+ history_curr = NULL;
+ if (history_file)
+ history_read(history_file);
+
+ edit_cb_ctx = ctx;
+ edit_cmd_cb = cmd_cb;
+ edit_eof_cb = eof_cb;
+ edit_completion_cb = completion_cb;
+
+ tcgetattr(STDIN_FILENO, &prevt);
+ newt = prevt;
+ newt.c_lflag &= ~(ICANON | ECHO);
+ tcsetattr(STDIN_FILENO, TCSANOW, &newt);
+
+ eloop_register_read_sock(STDIN_FILENO, edit_read_char, NULL, NULL);
+
+ ps2 = ps;
+ printf("%s> ", ps2 ? ps2 : "");
+ fflush(stdout);
+
+ return 0;
+}
+
+
+void edit_deinit(const char *history_file,
+ int (*filter_cb)(void *ctx, const char *cmd))
+{
+ struct edit_history *h;
+ if (history_file)
+ history_write(history_file, filter_cb);
+ while ((h = dl_list_first(&history_list, struct edit_history, list))) {
+ dl_list_del(&h->list);
+ os_free(h);
+ }
+ edit_clear_line();
+ putchar('\r');
+ fflush(stdout);
+ eloop_unregister_read_sock(STDIN_FILENO);
+ tcsetattr(STDIN_FILENO, TCSANOW, &prevt);
+}
+
+
+void edit_redraw(void)
+{
+ char tmp;
+ cmdbuf[cmdbuf_len] = '\0';
+ printf("\r%s> %s", ps2 ? ps2 : "", cmdbuf);
+ if (cmdbuf_pos != cmdbuf_len) {
+ tmp = cmdbuf[cmdbuf_pos];
+ cmdbuf[cmdbuf_pos] = '\0';
+ printf("\r%s> %s", ps2 ? ps2 : "", cmdbuf);
+ cmdbuf[cmdbuf_pos] = tmp;
+ }
+ fflush(stdout);
+}
diff --git a/contrib/wpa/src/utils/edit.h b/contrib/wpa/src/utils/edit.h
new file mode 100644
index 0000000..ad27f1c
--- /dev/null
+++ b/contrib/wpa/src/utils/edit.h
@@ -0,0 +1,21 @@
+/*
+ * Command line editing and history
+ * Copyright (c) 2010, Jouni Malinen <j@w1.fi>
+ *
+ * This software may be distributed under the terms of the BSD license.
+ * See README for more details.
+ */
+
+#ifndef EDIT_H
+#define EDIT_H
+
+int edit_init(void (*cmd_cb)(void *ctx, char *cmd),
+ void (*eof_cb)(void *ctx),
+ char ** (*completion_cb)(void *ctx, const char *cmd, int pos),
+ void *ctx, const char *history_file, const char *ps);
+void edit_deinit(const char *history_file,
+ int (*filter_cb)(void *ctx, const char *cmd));
+void edit_clear_line(void);
+void edit_redraw(void);
+
+#endif /* EDIT_H */
diff --git a/contrib/wpa/src/utils/edit_readline.c b/contrib/wpa/src/utils/edit_readline.c
new file mode 100644
index 0000000..add26fa
--- /dev/null
+++ b/contrib/wpa/src/utils/edit_readline.c
@@ -0,0 +1,192 @@
+/*
+ * Command line editing and history wrapper for readline
+ * Copyright (c) 2010, Jouni Malinen <j@w1.fi>
+ *
+ * This software may be distributed under the terms of the BSD license.
+ * See README for more details.
+ */
+
+#include "includes.h"
+#include <readline/readline.h>
+#include <readline/history.h>
+
+#include "common.h"
+#include "eloop.h"
+#include "edit.h"
+
+
+static void *edit_cb_ctx;
+static void (*edit_cmd_cb)(void *ctx, char *cmd);
+static void (*edit_eof_cb)(void *ctx);
+static char ** (*edit_completion_cb)(void *ctx, const char *cmd, int pos) =
+ NULL;
+
+static char **pending_completions = NULL;
+
+
+static void readline_free_completions(void)
+{
+ int i;
+ if (pending_completions == NULL)
+ return;
+ for (i = 0; pending_completions[i]; i++)
+ os_free(pending_completions[i]);
+ os_free(pending_completions);
+ pending_completions = NULL;
+}
+
+
+static char * readline_completion_func(const char *text, int state)
+{
+ static int pos = 0;
+ static size_t len = 0;
+
+ if (pending_completions == NULL) {
+ rl_attempted_completion_over = 1;
+ return NULL;
+ }
+
+ if (state == 0) {
+ pos = 0;
+ len = os_strlen(text);
+ }
+ for (; pending_completions[pos]; pos++) {
+ if (strncmp(pending_completions[pos], text, len) == 0)
+ return strdup(pending_completions[pos++]);
+ }
+
+ rl_attempted_completion_over = 1;
+ return NULL;
+}
+
+
+static char ** readline_completion(const char *text, int start, int end)
+{
+ readline_free_completions();
+ if (edit_completion_cb)
+ pending_completions = edit_completion_cb(edit_cb_ctx,
+ rl_line_buffer, end);
+ return rl_completion_matches(text, readline_completion_func);
+}
+
+
+static void edit_read_char(int sock, void *eloop_ctx, void *sock_ctx)
+{
+ rl_callback_read_char();
+}
+
+
+static void trunc_nl(char *str)
+{
+ char *pos = str;
+ while (*pos != '\0') {
+ if (*pos == '\n') {
+ *pos = '\0';
+ break;
+ }
+ pos++;
+ }
+}
+
+
+static void readline_cmd_handler(char *cmd)
+{
+ if (cmd && *cmd) {
+ HIST_ENTRY *h;
+ while (next_history())
+ ;
+ h = previous_history();
+ if (h == NULL || os_strcmp(cmd, h->line) != 0)
+ add_history(cmd);
+ next_history();
+ }
+ if (cmd == NULL) {
+ edit_eof_cb(edit_cb_ctx);
+ return;
+ }
+ trunc_nl(cmd);
+ edit_cmd_cb(edit_cb_ctx, cmd);
+}
+
+
+int edit_init(void (*cmd_cb)(void *ctx, char *cmd),
+ void (*eof_cb)(void *ctx),
+ char ** (*completion_cb)(void *ctx, const char *cmd, int pos),
+ void *ctx, const char *history_file, const char *ps)
+{
+ edit_cb_ctx = ctx;
+ edit_cmd_cb = cmd_cb;
+ edit_eof_cb = eof_cb;
+ edit_completion_cb = completion_cb;
+
+ rl_attempted_completion_function = readline_completion;
+ if (history_file) {
+ read_history(history_file);
+ stifle_history(100);
+ }
+
+ eloop_register_read_sock(STDIN_FILENO, edit_read_char, NULL, NULL);
+
+ if (ps) {
+ size_t blen = os_strlen(ps) + 3;
+ char *ps2 = os_malloc(blen);
+ if (ps2) {
+ os_snprintf(ps2, blen, "%s> ", ps);
+ rl_callback_handler_install(ps2, readline_cmd_handler);
+ os_free(ps2);
+ return 0;
+ }
+ }
+
+ rl_callback_handler_install("> ", readline_cmd_handler);
+
+ return 0;
+}
+
+
+void edit_deinit(const char *history_file,
+ int (*filter_cb)(void *ctx, const char *cmd))
+{
+ rl_set_prompt("");
+ rl_replace_line("", 0);
+ rl_redisplay();
+ rl_callback_handler_remove();
+ readline_free_completions();
+
+ eloop_unregister_read_sock(STDIN_FILENO);
+
+ if (history_file) {
+ /* Save command history, excluding lines that may contain
+ * passwords. */
+ HIST_ENTRY *h;
+ history_set_pos(0);
+ while ((h = current_history())) {
+ char *p = h->line;
+ while (*p == ' ' || *p == '\t')
+ p++;
+ if (filter_cb && filter_cb(edit_cb_ctx, p)) {
+ h = remove_history(where_history());
+ if (h) {
+ os_free(h->line);
+ free(h->data);
+ os_free(h);
+ } else
+ next_history();
+ } else
+ next_history();
+ }
+ write_history(history_file);
+ }
+}
+
+
+void edit_clear_line(void)
+{
+}
+
+
+void edit_redraw(void)
+{
+ rl_on_new_line();
+ rl_redisplay();
+}
diff --git a/contrib/wpa/src/utils/edit_simple.c b/contrib/wpa/src/utils/edit_simple.c
new file mode 100644
index 0000000..a095ea6
--- /dev/null
+++ b/contrib/wpa/src/utils/edit_simple.c
@@ -0,0 +1,92 @@
+/*
+ * Minimal command line editing
+ * Copyright (c) 2010, Jouni Malinen <j@w1.fi>
+ *
+ * This software may be distributed under the terms of the BSD license.
+ * See README for more details.
+ */
+
+#include "includes.h"
+
+#include "common.h"
+#include "eloop.h"
+#include "edit.h"
+
+
+#define CMD_BUF_LEN 256
+static char cmdbuf[CMD_BUF_LEN];
+static int cmdbuf_pos = 0;
+static const char *ps2 = NULL;
+
+static void *edit_cb_ctx;
+static void (*edit_cmd_cb)(void *ctx, char *cmd);
+static void (*edit_eof_cb)(void *ctx);
+
+
+static void edit_read_char(int sock, void *eloop_ctx, void *sock_ctx)
+{
+ int c;
+ unsigned char buf[1];
+ int res;
+
+ res = read(sock, buf, 1);
+ if (res < 0)
+ perror("read");
+ if (res <= 0) {
+ edit_eof_cb(edit_cb_ctx);
+ return;
+ }
+ c = buf[0];
+
+ if (c == '\r' || c == '\n') {
+ cmdbuf[cmdbuf_pos] = '\0';
+ cmdbuf_pos = 0;
+ edit_cmd_cb(edit_cb_ctx, cmdbuf);
+ printf("%s> ", ps2 ? ps2 : "");
+ fflush(stdout);
+ return;
+ }
+
+ if (c >= 32 && c <= 255) {
+ if (cmdbuf_pos < (int) sizeof(cmdbuf) - 1) {
+ cmdbuf[cmdbuf_pos++] = c;
+ }
+ }
+}
+
+
+int edit_init(void (*cmd_cb)(void *ctx, char *cmd),
+ void (*eof_cb)(void *ctx),
+ char ** (*completion_cb)(void *ctx, const char *cmd, int pos),
+ void *ctx, const char *history_file, const char *ps)
+{
+ edit_cb_ctx = ctx;
+ edit_cmd_cb = cmd_cb;
+ edit_eof_cb = eof_cb;
+ eloop_register_read_sock(STDIN_FILENO, edit_read_char, NULL, NULL);
+ ps2 = ps;
+
+ printf("%s> ", ps2 ? ps2 : "");
+ fflush(stdout);
+
+ return 0;
+}
+
+
+void edit_deinit(const char *history_file,
+ int (*filter_cb)(void *ctx, const char *cmd))
+{
+ eloop_unregister_read_sock(STDIN_FILENO);
+}
+
+
+void edit_clear_line(void)
+{
+}
+
+
+void edit_redraw(void)
+{
+ cmdbuf[cmdbuf_pos] = '\0';
+ printf("\r> %s", cmdbuf);
+}
diff --git a/contrib/wpa/src/utils/eloop.c b/contrib/wpa/src/utils/eloop.c
index 4b61598..d01ae64 100644
--- a/contrib/wpa/src/utils/eloop.c
+++ b/contrib/wpa/src/utils/eloop.c
@@ -2,14 +2,8 @@
* Event loop based on select() loop
* Copyright (c) 2002-2009, Jouni Malinen <j@w1.fi>
*
- * This program is free software; you can redistribute it and/or modify
- * it under the terms of the GNU General Public License version 2 as
- * published by the Free Software Foundation.
- *
- * Alternatively, this software may be distributed under the terms of BSD
- * license.
- *
- * See README and COPYING for more details.
+ * This software may be distributed under the terms of the BSD license.
+ * See README for more details.
*/
#include "includes.h"
@@ -19,6 +13,11 @@
#include "list.h"
#include "eloop.h"
+#ifdef CONFIG_ELOOP_POLL
+#include <assert.h>
+#include <poll.h>
+#endif /* CONFIG_ELOOP_POLL */
+
struct eloop_sock {
int sock;
@@ -57,6 +56,13 @@ struct eloop_sock_table {
struct eloop_data {
int max_sock;
+ int count; /* sum of all table counts */
+#ifdef CONFIG_ELOOP_POLL
+ int max_pollfd_map; /* number of pollfds_map currently allocated */
+ int max_poll_fds; /* number of pollfds currently allocated */
+ struct pollfd *pollfds;
+ struct pollfd **pollfds_map;
+#endif /* CONFIG_ELOOP_POLL */
struct eloop_sock_table readers;
struct eloop_sock_table writers;
struct eloop_sock_table exceptions;
@@ -134,14 +140,44 @@ static int eloop_sock_table_add_sock(struct eloop_sock_table *table,
void *eloop_data, void *user_data)
{
struct eloop_sock *tmp;
+ int new_max_sock;
+
+ if (sock > eloop.max_sock)
+ new_max_sock = sock;
+ else
+ new_max_sock = eloop.max_sock;
if (table == NULL)
return -1;
+#ifdef CONFIG_ELOOP_POLL
+ if (new_max_sock >= eloop.max_pollfd_map) {
+ struct pollfd **nmap;
+ nmap = os_realloc_array(eloop.pollfds_map, new_max_sock + 50,
+ sizeof(struct pollfd *));
+ if (nmap == NULL)
+ return -1;
+
+ eloop.max_pollfd_map = new_max_sock + 50;
+ eloop.pollfds_map = nmap;
+ }
+
+ if (eloop.count + 1 > eloop.max_poll_fds) {
+ struct pollfd *n;
+ int nmax = eloop.count + 1 + 50;
+ n = os_realloc_array(eloop.pollfds, nmax,
+ sizeof(struct pollfd));
+ if (n == NULL)
+ return -1;
+
+ eloop.max_poll_fds = nmax;
+ eloop.pollfds = n;
+ }
+#endif /* CONFIG_ELOOP_POLL */
+
eloop_trace_sock_remove_ref(table);
- tmp = (struct eloop_sock *)
- os_realloc(table->table,
- (table->count + 1) * sizeof(struct eloop_sock));
+ tmp = os_realloc_array(table->table, table->count + 1,
+ sizeof(struct eloop_sock));
if (tmp == NULL)
return -1;
@@ -152,8 +188,8 @@ static int eloop_sock_table_add_sock(struct eloop_sock_table *table,
wpa_trace_record(&tmp[table->count]);
table->count++;
table->table = tmp;
- if (sock > eloop.max_sock)
- eloop.max_sock = sock;
+ eloop.max_sock = new_max_sock;
+ eloop.count++;
table->changed = 1;
eloop_trace_sock_add_ref(table);
@@ -182,11 +218,152 @@ static void eloop_sock_table_remove_sock(struct eloop_sock_table *table,
sizeof(struct eloop_sock));
}
table->count--;
+ eloop.count--;
table->changed = 1;
eloop_trace_sock_add_ref(table);
}
+#ifdef CONFIG_ELOOP_POLL
+
+static struct pollfd * find_pollfd(struct pollfd **pollfds_map, int fd, int mx)
+{
+ if (fd < mx && fd >= 0)
+ return pollfds_map[fd];
+ return NULL;
+}
+
+
+static int eloop_sock_table_set_fds(struct eloop_sock_table *readers,
+ struct eloop_sock_table *writers,
+ struct eloop_sock_table *exceptions,
+ struct pollfd *pollfds,
+ struct pollfd **pollfds_map,
+ int max_pollfd_map)
+{
+ int i;
+ int nxt = 0;
+ int fd;
+ struct pollfd *pfd;
+
+ /* Clear pollfd lookup map. It will be re-populated below. */
+ os_memset(pollfds_map, 0, sizeof(struct pollfd *) * max_pollfd_map);
+
+ if (readers && readers->table) {
+ for (i = 0; i < readers->count; i++) {
+ fd = readers->table[i].sock;
+ assert(fd >= 0 && fd < max_pollfd_map);
+ pollfds[nxt].fd = fd;
+ pollfds[nxt].events = POLLIN;
+ pollfds[nxt].revents = 0;
+ pollfds_map[fd] = &(pollfds[nxt]);
+ nxt++;
+ }
+ }
+
+ if (writers && writers->table) {
+ for (i = 0; i < writers->count; i++) {
+ /*
+ * See if we already added this descriptor, update it
+ * if so.
+ */
+ fd = writers->table[i].sock;
+ assert(fd >= 0 && fd < max_pollfd_map);
+ pfd = pollfds_map[fd];
+ if (!pfd) {
+ pfd = &(pollfds[nxt]);
+ pfd->events = 0;
+ pfd->fd = fd;
+ pollfds[i].revents = 0;
+ pollfds_map[fd] = pfd;
+ nxt++;
+ }
+ pfd->events |= POLLOUT;
+ }
+ }
+
+ /*
+ * Exceptions are always checked when using poll, but I suppose it's
+ * possible that someone registered a socket *only* for exception
+ * handling. Set the POLLIN bit in this case.
+ */
+ if (exceptions && exceptions->table) {
+ for (i = 0; i < exceptions->count; i++) {
+ /*
+ * See if we already added this descriptor, just use it
+ * if so.
+ */
+ fd = exceptions->table[i].sock;
+ assert(fd >= 0 && fd < max_pollfd_map);
+ pfd = pollfds_map[fd];
+ if (!pfd) {
+ pfd = &(pollfds[nxt]);
+ pfd->events = POLLIN;
+ pfd->fd = fd;
+ pollfds[i].revents = 0;
+ pollfds_map[fd] = pfd;
+ nxt++;
+ }
+ }
+ }
+
+ return nxt;
+}
+
+
+static int eloop_sock_table_dispatch_table(struct eloop_sock_table *table,
+ struct pollfd **pollfds_map,
+ int max_pollfd_map,
+ short int revents)
+{
+ int i;
+ struct pollfd *pfd;
+
+ if (!table || !table->table)
+ return 0;
+
+ table->changed = 0;
+ for (i = 0; i < table->count; i++) {
+ pfd = find_pollfd(pollfds_map, table->table[i].sock,
+ max_pollfd_map);
+ if (!pfd)
+ continue;
+
+ if (!(pfd->revents & revents))
+ continue;
+
+ table->table[i].handler(table->table[i].sock,
+ table->table[i].eloop_data,
+ table->table[i].user_data);
+ if (table->changed)
+ return 1;
+ }
+
+ return 0;
+}
+
+
+static void eloop_sock_table_dispatch(struct eloop_sock_table *readers,
+ struct eloop_sock_table *writers,
+ struct eloop_sock_table *exceptions,
+ struct pollfd **pollfds_map,
+ int max_pollfd_map)
+{
+ if (eloop_sock_table_dispatch_table(readers, pollfds_map,
+ max_pollfd_map, POLLIN | POLLERR |
+ POLLHUP))
+ return; /* pollfds may be invalid at this point */
+
+ if (eloop_sock_table_dispatch_table(writers, pollfds_map,
+ max_pollfd_map, POLLOUT))
+ return; /* pollfds may be invalid at this point */
+
+ eloop_sock_table_dispatch_table(exceptions, pollfds_map,
+ max_pollfd_map, POLLERR | POLLHUP);
+}
+
+#else /* CONFIG_ELOOP_POLL */
+
static void eloop_sock_table_set_fds(struct eloop_sock_table *table,
fd_set *fds)
{
@@ -222,6 +399,8 @@ static void eloop_sock_table_dispatch(struct eloop_sock_table *table,
}
}
+#endif /* CONFIG_ELOOP_POLL */
+
static void eloop_sock_table_destroy(struct eloop_sock_table *table)
{
@@ -300,6 +479,7 @@ int eloop_register_timeout(unsigned int secs, unsigned int usecs,
void *eloop_data, void *user_data)
{
struct eloop_timeout *timeout, *tmp;
+ os_time_t now_sec;
timeout = os_zalloc(sizeof(*timeout));
if (timeout == NULL)
@@ -308,7 +488,18 @@ int eloop_register_timeout(unsigned int secs, unsigned int usecs,
os_free(timeout);
return -1;
}
+ now_sec = timeout->time.sec;
timeout->time.sec += secs;
+ if (timeout->time.sec < now_sec) {
+ /*
+ * Integer overflow - assume long enough timeout to be assumed
+ * to be infinite, i.e., the timeout would never happen.
+ */
+ wpa_printf(MSG_DEBUG, "ELOOP: Too long timeout (secs=%u) to "
+ "ever happen - ignore it", secs);
+ os_free(timeout);
+ return 0;
+ }
timeout->time.usec += usecs;
while (timeout->time.usec >= 1000000) {
timeout->time.sec++;
@@ -448,10 +639,8 @@ int eloop_register_signal(int sig, eloop_signal_handler handler,
{
struct eloop_signal *tmp;
- tmp = (struct eloop_signal *)
- os_realloc(eloop.signals,
- (eloop.signal_count + 1) *
- sizeof(struct eloop_signal));
+ tmp = os_realloc_array(eloop.signals, eloop.signal_count + 1,
+ sizeof(struct eloop_signal));
if (tmp == NULL)
return -1;
@@ -490,16 +679,23 @@ int eloop_register_signal_reconfig(eloop_signal_handler handler,
void eloop_run(void)
{
+#ifdef CONFIG_ELOOP_POLL
+ int num_poll_fds;
+ int timeout_ms = 0;
+#else /* CONFIG_ELOOP_POLL */
fd_set *rfds, *wfds, *efds;
- int res;
struct timeval _tv;
+#endif /* CONFIG_ELOOP_POLL */
+ int res;
struct os_time tv, now;
+#ifndef CONFIG_ELOOP_POLL
rfds = os_malloc(sizeof(*rfds));
wfds = os_malloc(sizeof(*wfds));
efds = os_malloc(sizeof(*efds));
if (rfds == NULL || wfds == NULL || efds == NULL)
goto out;
+#endif /* CONFIG_ELOOP_POLL */
while (!eloop.terminate &&
(!dl_list_empty(&eloop.timeout) || eloop.readers.count > 0 ||
@@ -513,10 +709,27 @@ void eloop_run(void)
os_time_sub(&timeout->time, &now, &tv);
else
tv.sec = tv.usec = 0;
+#ifdef CONFIG_ELOOP_POLL
+ timeout_ms = tv.sec * 1000 + tv.usec / 1000;
+#else /* CONFIG_ELOOP_POLL */
_tv.tv_sec = tv.sec;
_tv.tv_usec = tv.usec;
+#endif /* CONFIG_ELOOP_POLL */
}
+#ifdef CONFIG_ELOOP_POLL
+ num_poll_fds = eloop_sock_table_set_fds(
+ &eloop.readers, &eloop.writers, &eloop.exceptions,
+ eloop.pollfds, eloop.pollfds_map,
+ eloop.max_pollfd_map);
+ res = poll(eloop.pollfds, num_poll_fds,
+ timeout ? timeout_ms : -1);
+
+ if (res < 0 && errno != EINTR && errno != 0) {
+ perror("poll");
+ goto out;
+ }
+#else /* CONFIG_ELOOP_POLL */
eloop_sock_table_set_fds(&eloop.readers, rfds);
eloop_sock_table_set_fds(&eloop.writers, wfds);
eloop_sock_table_set_fds(&eloop.exceptions, efds);
@@ -526,6 +739,7 @@ void eloop_run(void)
perror("select");
goto out;
}
+#endif /* CONFIG_ELOOP_POLL */
eloop_process_pending_signals();
/* check if some registered timeouts have occurred */
@@ -547,15 +761,24 @@ void eloop_run(void)
if (res <= 0)
continue;
+#ifdef CONFIG_ELOOP_POLL
+ eloop_sock_table_dispatch(&eloop.readers, &eloop.writers,
+ &eloop.exceptions, eloop.pollfds_map,
+ eloop.max_pollfd_map);
+#else /* CONFIG_ELOOP_POLL */
eloop_sock_table_dispatch(&eloop.readers, rfds);
eloop_sock_table_dispatch(&eloop.writers, wfds);
eloop_sock_table_dispatch(&eloop.exceptions, efds);
+#endif /* CONFIG_ELOOP_POLL */
}
out:
+#ifndef CONFIG_ELOOP_POLL
os_free(rfds);
os_free(wfds);
os_free(efds);
+#endif /* CONFIG_ELOOP_POLL */
+ return;
}
@@ -593,6 +816,11 @@ void eloop_destroy(void)
eloop_sock_table_destroy(&eloop.writers);
eloop_sock_table_destroy(&eloop.exceptions);
os_free(eloop.signals);
+
+#ifdef CONFIG_ELOOP_POLL
+ os_free(eloop.pollfds);
+ os_free(eloop.pollfds_map);
+#endif /* CONFIG_ELOOP_POLL */
}
@@ -604,6 +832,18 @@ int eloop_terminated(void)
void eloop_wait_for_read_sock(int sock)
{
+#ifdef CONFIG_ELOOP_POLL
+ struct pollfd pfd;
+
+ if (sock < 0)
+ return;
+
+ os_memset(&pfd, 0, sizeof(pfd));
+ pfd.fd = sock;
+ pfd.events = POLLIN;
+
+ poll(&pfd, 1, -1);
+#else /* CONFIG_ELOOP_POLL */
fd_set rfds;
if (sock < 0)
@@ -612,4 +852,5 @@ void eloop_wait_for_read_sock(int sock)
FD_ZERO(&rfds);
FD_SET(sock, &rfds);
select(sock + 1, &rfds, NULL, NULL, NULL);
+#endif /* CONFIG_ELOOP_POLL */
}
diff --git a/contrib/wpa/src/utils/eloop.h b/contrib/wpa/src/utils/eloop.h
index 1228f24..db03a73 100644
--- a/contrib/wpa/src/utils/eloop.h
+++ b/contrib/wpa/src/utils/eloop.h
@@ -2,14 +2,8 @@
* Event loop
* Copyright (c) 2002-2006, Jouni Malinen <j@w1.fi>
*
- * This program is free software; you can redistribute it and/or modify
- * it under the terms of the GNU General Public License version 2 as
- * published by the Free Software Foundation.
- *
- * Alternatively, this software may be distributed under the terms of BSD
- * license.
- *
- * See README and COPYING for more details.
+ * This software may be distributed under the terms of the BSD license.
+ * See README for more details.
*
* This file defines an event loop interface that supports processing events
* from registered timeouts (i.e., do something after N seconds), sockets
@@ -144,7 +138,7 @@ void eloop_unregister_sock(int sock, eloop_event_type type);
* Returns: 0 on success, -1 on failure
*
* Register an event handler for the given event. This function is used to
- * register eloop implementation specific events which are mainly targetted for
+ * register eloop implementation specific events which are mainly targeted for
* operating system specific code (driver interface and l2_packet) since the
* portable code will not be able to use such an OS-specific call. The handler
* function will be called whenever the event is triggered. The handler
diff --git a/contrib/wpa/src/utils/eloop_none.c b/contrib/wpa/src/utils/eloop_none.c
index 18eae4e..c67ece4 100644
--- a/contrib/wpa/src/utils/eloop_none.c
+++ b/contrib/wpa/src/utils/eloop_none.c
@@ -2,14 +2,8 @@
* Event loop - empty template (basic structure, but no OS specific operations)
* Copyright (c) 2002-2005, Jouni Malinen <j@w1.fi>
*
- * This program is free software; you can redistribute it and/or modify
- * it under the terms of the GNU General Public License version 2 as
- * published by the Free Software Foundation.
- *
- * Alternatively, this software may be distributed under the terms of BSD
- * license.
- *
- * See README and COPYING for more details.
+ * This software may be distributed under the terms of the BSD license.
+ * See README for more details.
*/
#include "includes.h"
diff --git a/contrib/wpa/src/utils/eloop_win.c b/contrib/wpa/src/utils/eloop_win.c
index 94cc72d..1fafeb2 100644
--- a/contrib/wpa/src/utils/eloop_win.c
+++ b/contrib/wpa/src/utils/eloop_win.c
@@ -2,14 +2,8 @@
* Event loop based on Windows events and WaitForMultipleObjects
* Copyright (c) 2002-2006, Jouni Malinen <j@w1.fi>
*
- * This program is free software; you can redistribute it and/or modify
- * it under the terms of the GNU General Public License version 2 as
- * published by the Free Software Foundation.
- *
- * Alternatively, this software may be distributed under the terms of BSD
- * license.
- *
- * See README and COPYING for more details.
+ * This software may be distributed under the terms of the BSD license.
+ * See README for more details.
*/
#include "includes.h"
@@ -104,8 +98,8 @@ static int eloop_prepare_handles(void)
if (eloop.num_handles > eloop.reader_count + eloop.event_count + 8)
return 0;
- n = os_realloc(eloop.handles,
- eloop.num_handles * 2 * sizeof(eloop.handles[0]));
+ n = os_realloc_array(eloop.handles, eloop.num_handles * 2,
+ sizeof(eloop.handles[0]));
if (n == NULL)
return -1;
eloop.handles = n;
@@ -134,8 +128,8 @@ int eloop_register_read_sock(int sock, eloop_sock_handler handler,
WSACloseEvent(event);
return -1;
}
- tmp = os_realloc(eloop.readers,
- (eloop.reader_count + 1) * sizeof(struct eloop_sock));
+ tmp = os_realloc_array(eloop.readers, eloop.reader_count + 1,
+ sizeof(struct eloop_sock));
if (tmp == NULL) {
WSAEventSelect(sock, event, 0);
WSACloseEvent(event);
@@ -197,8 +191,8 @@ int eloop_register_event(void *event, size_t event_size,
if (eloop_prepare_handles())
return -1;
- tmp = os_realloc(eloop.events,
- (eloop.event_count + 1) * sizeof(struct eloop_event));
+ tmp = os_realloc_array(eloop.events, eloop.event_count + 1,
+ sizeof(struct eloop_event));
if (tmp == NULL)
return -1;
@@ -243,12 +237,24 @@ int eloop_register_timeout(unsigned int secs, unsigned int usecs,
void *eloop_data, void *user_data)
{
struct eloop_timeout *timeout, *tmp, *prev;
+ os_time_t now_sec;
timeout = os_malloc(sizeof(*timeout));
if (timeout == NULL)
return -1;
os_get_time(&timeout->time);
+ now_sec = timeout->time.sec;
timeout->time.sec += secs;
+ if (timeout->time.sec < now_sec) {
+ /*
+ * Integer overflow - assume long enough timeout to be assumed
+ * to be infinite, i.e., the timeout would never happen.
+ */
+ wpa_printf(MSG_DEBUG, "ELOOP: Too long timeout (secs=%u) to "
+ "ever happen - ignore it", secs);
+ os_free(timeout);
+ return 0;
+ }
timeout->time.usec += usecs;
while (timeout->time.usec >= 1000000) {
timeout->time.sec++;
@@ -386,9 +392,8 @@ int eloop_register_signal(int sig, eloop_signal_handler handler,
{
struct eloop_signal *tmp;
- tmp = os_realloc(eloop.signals,
- (eloop.signal_count + 1) *
- sizeof(struct eloop_signal));
+ tmp = os_realloc_array(eloop.signals, eloop.signal_count + 1,
+ sizeof(struct eloop_signal));
if (tmp == NULL)
return -1;
diff --git a/contrib/wpa/src/utils/ext_password.c b/contrib/wpa/src/utils/ext_password.c
new file mode 100644
index 0000000..0613119
--- /dev/null
+++ b/contrib/wpa/src/utils/ext_password.c
@@ -0,0 +1,116 @@
+/*
+ * External password backend
+ * Copyright (c) 2012, Jouni Malinen <j@w1.fi>
+ *
+ * This software may be distributed under the terms of the BSD license.
+ * See README for more details.
+ */
+
+#include "includes.h"
+
+#ifdef __linux__
+#include <sys/mman.h>
+#endif /* __linux__ */
+
+#include "common.h"
+#include "ext_password_i.h"
+
+
+#ifdef CONFIG_EXT_PASSWORD_TEST
+extern struct ext_password_backend ext_password_test;
+#endif /* CONFIG_EXT_PASSWORD_TEST */
+
+static const struct ext_password_backend *backends[] = {
+#ifdef CONFIG_EXT_PASSWORD_TEST
+ &ext_password_test,
+#endif /* CONFIG_EXT_PASSWORD_TEST */
+ NULL
+};
+
+struct ext_password_data {
+ const struct ext_password_backend *backend;
+ void *priv;
+};
+
+
+struct ext_password_data * ext_password_init(const char *backend,
+ const char *params)
+{
+ struct ext_password_data *data;
+ int i;
+
+ data = os_zalloc(sizeof(*data));
+ if (data == NULL)
+ return NULL;
+
+ for (i = 0; backends[i]; i++) {
+ if (os_strcmp(backends[i]->name, backend) == 0) {
+ data->backend = backends[i];
+ break;
+ }
+ }
+
+ if (!data->backend) {
+ os_free(data);
+ return NULL;
+ }
+
+ data->priv = data->backend->init(params);
+ if (data->priv == NULL) {
+ os_free(data);
+ return NULL;
+ }
+
+ return data;
+}
+
+
+void ext_password_deinit(struct ext_password_data *data)
+{
+ if (data && data->backend && data->priv)
+ data->backend->deinit(data->priv);
+ os_free(data);
+}
+
+
+struct wpabuf * ext_password_get(struct ext_password_data *data,
+ const char *name)
+{
+ if (data == NULL)
+ return NULL;
+ return data->backend->get(data->priv, name);
+}
+
+
+struct wpabuf * ext_password_alloc(size_t len)
+{
+ struct wpabuf *buf;
+
+ buf = wpabuf_alloc(len);
+ if (buf == NULL)
+ return NULL;
+
+#ifdef __linux__
+ if (mlock(wpabuf_head(buf), wpabuf_len(buf)) < 0) {
+ wpa_printf(MSG_ERROR, "EXT PW: mlock failed: %s",
+ strerror(errno));
+ }
+#endif /* __linux__ */
+
+ return buf;
+}
+
+
+void ext_password_free(struct wpabuf *pw)
+{
+ if (pw == NULL)
+ return;
+ os_memset(wpabuf_mhead(pw), 0, wpabuf_len(pw));
+#ifdef __linux__
+ if (munlock(wpabuf_head(pw), wpabuf_len(pw)) < 0) {
+ wpa_printf(MSG_ERROR, "EXT PW: munlock failed: %s",
+ strerror(errno));
+ }
+#endif /* __linux__ */
+ wpabuf_free(pw);
+}
diff --git a/contrib/wpa/src/utils/ext_password.h b/contrib/wpa/src/utils/ext_password.h
new file mode 100644
index 0000000..e3e46ea
--- /dev/null
+++ b/contrib/wpa/src/utils/ext_password.h
@@ -0,0 +1,33 @@
+/*
+ * External password backend
+ * Copyright (c) 2012, Jouni Malinen <j@w1.fi>
+ *
+ * This software may be distributed under the terms of the BSD license.
+ * See README for more details.
+ */
+
+#ifndef EXT_PASSWORD_H
+#define EXT_PASSWORD_H
+
+struct ext_password_data;
+
+#ifdef CONFIG_EXT_PASSWORD
+
+struct ext_password_data * ext_password_init(const char *backend,
+ const char *params);
+void ext_password_deinit(struct ext_password_data *data);
+
+struct wpabuf * ext_password_get(struct ext_password_data *data,
+ const char *name);
+void ext_password_free(struct wpabuf *pw);
+
+#else /* CONFIG_EXT_PASSWORD */
+
+#define ext_password_init(b, p) ((void *) 1)
+#define ext_password_deinit(d) do { } while (0)
+#define ext_password_get(d, n) (NULL)
+#define ext_password_free(p) do { } while (0)
+
+#endif /* CONFIG_EXT_PASSWORD */
+
+#endif /* EXT_PASSWORD_H */
diff --git a/contrib/wpa/src/utils/ext_password_i.h b/contrib/wpa/src/utils/ext_password_i.h
new file mode 100644
index 0000000..043e731
--- /dev/null
+++ b/contrib/wpa/src/utils/ext_password_i.h
@@ -0,0 +1,23 @@
+/*
+ * External password backend - internal definitions
+ * Copyright (c) 2012, Jouni Malinen <j@w1.fi>
+ *
+ * This software may be distributed under the terms of the BSD license.
+ * See README for more details.
+ */
+
+#ifndef EXT_PASSWORD_I_H
+#define EXT_PASSWORD_I_H
+
+#include "ext_password.h"
+
+struct ext_password_backend {
+ const char *name;
+ void * (*init)(const char *params);
+ void (*deinit)(void *ctx);
+ struct wpabuf * (*get)(void *ctx, const char *name);
+};
+
+struct wpabuf * ext_password_alloc(size_t len);
+
+#endif /* EXT_PASSWORD_I_H */
diff --git a/contrib/wpa/src/utils/ext_password_test.c b/contrib/wpa/src/utils/ext_password_test.c
new file mode 100644
index 0000000..3801bb8
--- /dev/null
+++ b/contrib/wpa/src/utils/ext_password_test.c
@@ -0,0 +1,90 @@
+/*
+ * External password backend
+ * Copyright (c) 2012, Jouni Malinen <j@w1.fi>
+ *
+ * This software may be distributed under the terms of the BSD license.
+ * See README for more details.
+ */
+
+#include "includes.h"
+
+#include "common.h"
+#include "ext_password_i.h"
+
+
+struct ext_password_test_data {
+ char *params;
+};
+
+
+static void * ext_password_test_init(const char *params)
+{
+ struct ext_password_test_data *data;
+
+ data = os_zalloc(sizeof(*data));
+ if (data == NULL)
+ return NULL;
+
+ if (params)
+ data->params = os_strdup(params);
+
+ return data;
+}
+
+
+static void ext_password_test_deinit(void *ctx)
+{
+ struct ext_password_test_data *data = ctx;
+
+ os_free(data->params);
+ os_free(data);
+}
+
+
+static struct wpabuf * ext_password_test_get(void *ctx, const char *name)
+{
+ struct ext_password_test_data *data = ctx;
+ char *pos, *pos2;
+ size_t nlen;
+
+ wpa_printf(MSG_DEBUG, "EXT PW TEST: get(%s)", name);
+
+ pos = data->params;
+ if (pos == NULL)
+ return NULL;
+ nlen = os_strlen(name);
+
+ while (pos && *pos) {
+ if (os_strncmp(pos, name, nlen) == 0 && pos[nlen] == '=') {
+ struct wpabuf *buf;
+ pos += nlen + 1;
+ pos2 = pos;
+ while (*pos2 != '|' && *pos2 != '\0')
+ pos2++;
+ buf = ext_password_alloc(pos2 - pos);
+ if (buf == NULL)
+ return NULL;
+ wpabuf_put_data(buf, pos, pos2 - pos);
+ wpa_hexdump_ascii_key(MSG_DEBUG, "EXT PW TEST: value",
+ wpabuf_head(buf),
+ wpabuf_len(buf));
+ return buf;
+ }
+
+ pos = os_strchr(pos + 1, '|');
+ if (pos)
+ pos++;
+ }
+
+ wpa_printf(MSG_DEBUG, "EXT PW TEST: get(%s) - not found", name);
+
+ return NULL;
+}
+
+
+const struct ext_password_backend ext_password_test = {
+ .name = "test",
+ .init = ext_password_test_init,
+ .deinit = ext_password_test_deinit,
+ .get = ext_password_test_get,
+};
diff --git a/contrib/wpa/src/utils/includes.h b/contrib/wpa/src/utils/includes.h
index 63b5c23..6c6ec87 100644
--- a/contrib/wpa/src/utils/includes.h
+++ b/contrib/wpa/src/utils/includes.h
@@ -2,14 +2,8 @@
* wpa_supplicant/hostapd - Default include files
* Copyright (c) 2005-2006, Jouni Malinen <j@w1.fi>
*
- * This program is free software; you can redistribute it and/or modify
- * it under the terms of the GNU General Public License version 2 as
- * published by the Free Software Foundation.
- *
- * Alternatively, this software may be distributed under the terms of BSD
- * license.
- *
- * See README and COPYING for more details.
+ * This software may be distributed under the terms of the BSD license.
+ * See README for more details.
*
* This header file is included into all C files so that commonly used header
* files can be selected with OS specific ifdef blocks in one place instead of
@@ -34,7 +28,6 @@
#include <errno.h>
#endif /* _WIN32_WCE */
#include <ctype.h>
-#include <time.h>
#ifndef CONFIG_TI_COMPILER
#ifndef _MSC_VER
@@ -48,9 +41,7 @@
#include <netinet/in.h>
#include <arpa/inet.h>
#ifndef __vxworks
-#ifndef __SYMBIAN32__
#include <sys/uio.h>
-#endif /* __SYMBIAN32__ */
#include <sys/time.h>
#endif /* __vxworks */
#endif /* CONFIG_TI_COMPILER */
diff --git a/contrib/wpa/src/utils/ip_addr.c b/contrib/wpa/src/utils/ip_addr.c
index 158fd57..3647c76 100644
--- a/contrib/wpa/src/utils/ip_addr.c
+++ b/contrib/wpa/src/utils/ip_addr.c
@@ -2,14 +2,8 @@
* IP address processing
* Copyright (c) 2003-2006, Jouni Malinen <j@w1.fi>
*
- * This program is free software; you can redistribute it and/or modify
- * it under the terms of the GNU General Public License version 2 as
- * published by the Free Software Foundation.
- *
- * Alternatively, this software may be distributed under the terms of BSD
- * license.
- *
- * See README and COPYING for more details.
+ * This software may be distributed under the terms of the BSD license.
+ * See README for more details.
*/
#include "includes.h"
diff --git a/contrib/wpa/src/utils/ip_addr.h b/contrib/wpa/src/utils/ip_addr.h
index 28ccaef..79ac20c 100644
--- a/contrib/wpa/src/utils/ip_addr.h
+++ b/contrib/wpa/src/utils/ip_addr.h
@@ -2,14 +2,8 @@
* IP address processing
* Copyright (c) 2003-2006, Jouni Malinen <j@w1.fi>
*
- * This program is free software; you can redistribute it and/or modify
- * it under the terms of the GNU General Public License version 2 as
- * published by the Free Software Foundation.
- *
- * Alternatively, this software may be distributed under the terms of BSD
- * license.
- *
- * See README and COPYING for more details.
+ * This software may be distributed under the terms of the BSD license.
+ * See README for more details.
*/
#ifndef IP_ADDR_H
diff --git a/contrib/wpa/src/utils/list.h b/contrib/wpa/src/utils/list.h
index ed7c022..6881130 100644
--- a/contrib/wpa/src/utils/list.h
+++ b/contrib/wpa/src/utils/list.h
@@ -2,14 +2,8 @@
* Doubly-linked list
* Copyright (c) 2009, Jouni Malinen <j@w1.fi>
*
- * This program is free software; you can redistribute it and/or modify
- * it under the terms of the GNU General Public License version 2 as
- * published by the Free Software Foundation.
- *
- * Alternatively, this software may be distributed under the terms of BSD
- * license.
- *
- * See README and COPYING for more details.
+ * This software may be distributed under the terms of the BSD license.
+ * See README for more details.
*/
#ifndef LIST_H
@@ -75,6 +69,10 @@ static inline unsigned int dl_list_len(struct dl_list *list)
(dl_list_empty((list)) ? NULL : \
dl_list_entry((list)->next, type, member))
+#define dl_list_last(list, type, member) \
+ (dl_list_empty((list)) ? NULL : \
+ dl_list_entry((list)->prev, type, member))
+
#define dl_list_for_each(item, list, type, member) \
for (item = dl_list_entry((list)->next, type, member); \
&item->member != (list); \
@@ -86,4 +84,12 @@ static inline unsigned int dl_list_len(struct dl_list *list)
&item->member != (list); \
item = n, n = dl_list_entry(n->member.next, type, member))
+#define dl_list_for_each_reverse(item, list, type, member) \
+ for (item = dl_list_entry((list)->prev, type, member); \
+ &item->member != (list); \
+ item = dl_list_entry(item->member.prev, type, member))
+
+#define DEFINE_DL_LIST(name) \
+ struct dl_list name = { &(name), &(name) }
+
#endif /* LIST_H */
diff --git a/contrib/wpa/src/utils/os.h b/contrib/wpa/src/utils/os.h
index f4723d8..ad20834 100644
--- a/contrib/wpa/src/utils/os.h
+++ b/contrib/wpa/src/utils/os.h
@@ -2,14 +2,8 @@
* OS specific functions
* Copyright (c) 2005-2009, Jouni Malinen <j@w1.fi>
*
- * This program is free software; you can redistribute it and/or modify
- * it under the terms of the GNU General Public License version 2 as
- * published by the Free Software Foundation.
- *
- * Alternatively, this software may be distributed under the terms of BSD
- * license.
- *
- * See README and COPYING for more details.
+ * This software may be distributed under the terms of the BSD license.
+ * See README for more details.
*/
#ifndef OS_H
@@ -70,6 +64,16 @@ int os_get_time(struct os_time *t);
int os_mktime(int year, int month, int day, int hour, int min, int sec,
os_time_t *t);
+struct os_tm {
+ int sec; /* 0..59 or 60 for leap seconds */
+ int min; /* 0..59 */
+ int hour; /* 0..23 */
+ int day; /* 1..31 */
+ int month; /* 1..12 */
+ int year; /* Four digit year */
+};
+
+int os_gmtime(os_time_t t, struct os_tm *tm);
/**
* os_daemonize - Run in the background (detach from the controlling terminal)
@@ -176,6 +180,25 @@ char * os_readfile(const char *name, size_t *len);
*/
void * os_zalloc(size_t size);
+/**
+ * os_calloc - Allocate and zero memory for an array
+ * @nmemb: Number of members in the array
+ * @size: Number of bytes in each member
+ * Returns: Pointer to allocated and zeroed memory or %NULL on failure
+ *
+ * This function can be used as a wrapper for os_zalloc(nmemb * size) when an
+ * allocation is used for an array. The main benefit over os_zalloc() is in
+ * having an extra check to catch integer overflows in multiplication.
+ *
+ * Caller is responsible for freeing the returned buffer with os_free().
+ */
+static inline void * os_calloc(size_t nmemb, size_t size)
+{
+ if (size && nmemb > (~(size_t) 0) / size)
+ return NULL;
+ return os_zalloc(nmemb * size);
+}
+
/*
* The following functions are wrapper for standard ANSI C or POSIX functions.
@@ -463,6 +486,14 @@ char * os_strdup(const char *s);
#endif /* OS_NO_C_LIB_DEFINES */
+static inline void * os_realloc_array(void *ptr, size_t nmemb, size_t size)
+{
+ if (size && nmemb > (~(size_t) 0) / size)
+ return NULL;
+ return os_realloc(ptr, nmemb * size);
+}
+
+
/**
* os_strlcpy - Copy a string with size bound and NUL-termination
* @dest: Destination
diff --git a/contrib/wpa/src/utils/os_internal.c b/contrib/wpa/src/utils/os_internal.c
index 5260e23..e4b7fdb 100644
--- a/contrib/wpa/src/utils/os_internal.c
+++ b/contrib/wpa/src/utils/os_internal.c
@@ -2,14 +2,8 @@
* wpa_supplicant/hostapd / Internal implementation of OS specific functions
* Copyright (c) 2005-2006, Jouni Malinen <j@w1.fi>
*
- * This program is free software; you can redistribute it and/or modify
- * it under the terms of the GNU General Public License version 2 as
- * published by the Free Software Foundation.
- *
- * Alternatively, this software may be distributed under the terms of BSD
- * license.
- *
- * See README and COPYING for more details.
+ * This software may be distributed under the terms of the BSD license.
+ * See README for more details.
*
* This file is an example of operating system specific wrapper functions.
* This version implements many of the functions internally, so it can be used
@@ -70,6 +64,24 @@ int os_mktime(int year, int month, int day, int hour, int min, int sec,
}
+int os_gmtime(os_time_t t, struct os_tm *tm)
+{
+ struct tm *tm2;
+ time_t t2 = t;
+
+ tm2 = gmtime(&t2);
+ if (tm2 == NULL)
+ return -1;
+ tm->sec = tm2->tm_sec;
+ tm->min = tm2->tm_min;
+ tm->hour = tm2->tm_hour;
+ tm->day = tm2->tm_mday;
+ tm->month = tm2->tm_mon + 1;
+ tm->year = tm2->tm_year + 1900;
+ return 0;
+}
+
+
int os_daemonize(const char *pid_file)
{
if (daemon(0, 0)) {
diff --git a/contrib/wpa/src/utils/os_none.c b/contrib/wpa/src/utils/os_none.c
index bab8f17..cabf73b 100644
--- a/contrib/wpa/src/utils/os_none.c
+++ b/contrib/wpa/src/utils/os_none.c
@@ -2,14 +2,8 @@
* wpa_supplicant/hostapd / Empty OS specific functions
* Copyright (c) 2005-2006, Jouni Malinen <j@w1.fi>
*
- * This program is free software; you can redistribute it and/or modify
- * it under the terms of the GNU General Public License version 2 as
- * published by the Free Software Foundation.
- *
- * Alternatively, this software may be distributed under the terms of BSD
- * license.
- *
- * See README and COPYING for more details.
+ * This software may be distributed under the terms of the BSD license.
+ * See README for more details.
*
* This file can be used as a starting point when adding a new OS target. The
* functions here do not really work as-is since they are just empty or only
@@ -38,6 +32,11 @@ int os_mktime(int year, int month, int day, int hour, int min, int sec,
return -1;
}
+int os_gmtime(os_time_t t, struct os_tm *tm)
+{
+ return -1;
+}
+
int os_daemonize(const char *pid_file)
{
diff --git a/contrib/wpa/src/utils/os_unix.c b/contrib/wpa/src/utils/os_unix.c
index 6f58fa4..23a93be 100644
--- a/contrib/wpa/src/utils/os_unix.c
+++ b/contrib/wpa/src/utils/os_unix.c
@@ -2,26 +2,28 @@
* OS specific functions for UNIX/POSIX systems
* Copyright (c) 2005-2009, Jouni Malinen <j@w1.fi>
*
- * This program is free software; you can redistribute it and/or modify
- * it under the terms of the GNU General Public License version 2 as
- * published by the Free Software Foundation.
- *
- * Alternatively, this software may be distributed under the terms of BSD
- * license.
- *
- * See README and COPYING for more details.
+ * This software may be distributed under the terms of the BSD license.
+ * See README for more details.
*/
#include "includes.h"
+#include <time.h>
+
+#ifdef ANDROID
+#include <linux/capability.h>
+#include <linux/prctl.h>
+#include <private/android_filesystem_config.h>
+#endif /* ANDROID */
+
#include "os.h"
#ifdef WPA_TRACE
#include "common.h"
-#include "list.h"
#include "wpa_debug.h"
#include "trace.h"
+#include "list.h"
static struct dl_list alloc_list;
@@ -98,6 +100,24 @@ int os_mktime(int year, int month, int day, int hour, int min, int sec,
}
+int os_gmtime(os_time_t t, struct os_tm *tm)
+{
+ struct tm *tm2;
+ time_t t2 = t;
+
+ tm2 = gmtime(&t2);
+ if (tm2 == NULL)
+ return -1;
+ tm->sec = tm2->tm_sec;
+ tm->min = tm2->tm_min;
+ tm->hour = tm2->tm_hour;
+ tm->day = tm2->tm_mday;
+ tm->month = tm2->tm_mon + 1;
+ tm->year = tm2->tm_year + 1900;
+ return 0;
+}
+
+
#ifdef __APPLE__
#include <fcntl.h>
static int os_daemon(int nochdir, int noclose)
@@ -135,9 +155,9 @@ static int os_daemon(int nochdir, int noclose)
int os_daemonize(const char *pid_file)
{
-#ifdef __uClinux__
+#if defined(__uClinux__) || defined(__sun__)
return -1;
-#else /* __uClinux__ */
+#else /* defined(__uClinux__) || defined(__sun__) */
if (os_daemon(0, 0)) {
perror("daemon");
return -1;
@@ -152,7 +172,7 @@ int os_daemonize(const char *pid_file)
}
return -0;
-#endif /* __uClinux__ */
+#endif /* defined(__uClinux__) || defined(__sun__) */
}
@@ -232,6 +252,30 @@ char * os_rel2abs_path(const char *rel_path)
int os_program_init(void)
{
+#ifdef ANDROID
+ /*
+ * We ignore errors here since errors are normal if we
+ * are already running as non-root.
+ */
+ gid_t groups[] = { AID_INET, AID_WIFI, AID_KEYSTORE };
+ struct __user_cap_header_struct header;
+ struct __user_cap_data_struct cap;
+
+ setgroups(sizeof(groups)/sizeof(groups[0]), groups);
+
+ prctl(PR_SET_KEEPCAPS, 1, 0, 0, 0);
+
+ setgid(AID_WIFI);
+ setuid(AID_WIFI);
+
+ header.version = _LINUX_CAPABILITY_VERSION;
+ header.pid = 0;
+ cap.effective = cap.permitted =
+ (1 << CAP_NET_ADMIN) | (1 << CAP_NET_RAW);
+ cap.inheritable = 0;
+ capset(&header, &cap);
+#endif /* ANDROID */
+
#ifdef WPA_TRACE
dl_list_init(&alloc_list);
#endif /* WPA_TRACE */
@@ -285,14 +329,21 @@ char * os_readfile(const char *name, size_t *len)
{
FILE *f;
char *buf;
+ long pos;
f = fopen(name, "rb");
if (f == NULL)
return NULL;
- fseek(f, 0, SEEK_END);
- *len = ftell(f);
- fseek(f, 0, SEEK_SET);
+ if (fseek(f, 0, SEEK_END) < 0 || (pos = ftell(f)) < 0) {
+ fclose(f);
+ return NULL;
+ }
+ *len = pos;
+ if (fseek(f, 0, SEEK_SET) < 0) {
+ fclose(f);
+ return NULL;
+ }
buf = os_malloc(*len);
if (buf == NULL) {
diff --git a/contrib/wpa/src/utils/os_win32.c b/contrib/wpa/src/utils/os_win32.c
index 0740964..163cebe 100644
--- a/contrib/wpa/src/utils/os_win32.c
+++ b/contrib/wpa/src/utils/os_win32.c
@@ -2,17 +2,12 @@
* wpa_supplicant/hostapd / OS specific functions for Win32 systems
* Copyright (c) 2005-2006, Jouni Malinen <j@w1.fi>
*
- * This program is free software; you can redistribute it and/or modify
- * it under the terms of the GNU General Public License version 2 as
- * published by the Free Software Foundation.
- *
- * Alternatively, this software may be distributed under the terms of BSD
- * license.
- *
- * See README and COPYING for more details.
+ * This software may be distributed under the terms of the BSD license.
+ * See README for more details.
*/
#include "includes.h"
+#include <time.h>
#include <winsock2.h>
#include <wincrypt.h>
@@ -92,6 +87,24 @@ int os_mktime(int year, int month, int day, int hour, int min, int sec,
}
+int os_gmtime(os_time_t t, struct os_tm *tm)
+{
+ struct tm *tm2;
+ time_t t2 = t;
+
+ tm2 = gmtime(&t2);
+ if (tm2 == NULL)
+ return -1;
+ tm->sec = tm2->tm_sec;
+ tm->min = tm2->tm_min;
+ tm->hour = tm2->tm_hour;
+ tm->day = tm2->tm_mday;
+ tm->month = tm2->tm_mon + 1;
+ tm->year = tm2->tm_year + 1900;
+ return 0;
+}
+
+
int os_daemonize(const char *pid_file)
{
/* TODO */
diff --git a/contrib/wpa/src/utils/pcsc_funcs.c b/contrib/wpa/src/utils/pcsc_funcs.c
index bf9f04a..08510d0 100644
--- a/contrib/wpa/src/utils/pcsc_funcs.c
+++ b/contrib/wpa/src/utils/pcsc_funcs.c
@@ -1,15 +1,9 @@
/*
* WPA Supplicant / PC/SC smartcard interface for USIM, GSM SIM
- * Copyright (c) 2004-2007, Jouni Malinen <j@w1.fi>
+ * Copyright (c) 2004-2007, 2012, Jouni Malinen <j@w1.fi>
*
- * This program is free software; you can redistribute it and/or modify
- * it under the terms of the GNU General Public License version 2 as
- * published by the Free Software Foundation.
- *
- * Alternatively, this software may be distributed under the terms of BSD
- * license.
- *
- * See README and COPYING for more details.
+ * This software may be distributed under the terms of the BSD license.
+ * See README for more details.
*
* This file implements wrapper functions for accessing GSM SIM and 3GPP USIM
* cards through PC/SC smartcard library. These functions are used to implement
@@ -76,6 +70,9 @@
#define USIM_TLV_TOTAL_FILE_SIZE 0x81
#define USIM_TLV_PIN_STATUS_TEMPLATE 0xC6
#define USIM_TLV_SHORT_FILE_ID 0x88
+#define USIM_TLV_SECURITY_ATTR_8B 0x8B
+#define USIM_TLV_SECURITY_ATTR_8C 0x8C
+#define USIM_TLV_SECURITY_ATTR_AB 0xAB
#define USIM_PS_DO_TAG 0x90
@@ -87,6 +84,27 @@
#define CK_LEN 16
+/* GSM files
+ * File type in first octet:
+ * 3F = Master File
+ * 7F = Dedicated File
+ * 2F = Elementary File under the Master File
+ * 6F = Elementary File under a Dedicated File
+ */
+#define SCARD_FILE_MF 0x3F00
+#define SCARD_FILE_GSM_DF 0x7F20
+#define SCARD_FILE_UMTS_DF 0x7F50
+#define SCARD_FILE_GSM_EF_IMSI 0x6F07
+#define SCARD_FILE_GSM_EF_AD 0x6FAD
+#define SCARD_FILE_EF_DIR 0x2F00
+#define SCARD_FILE_EF_ICCID 0x2FE2
+#define SCARD_FILE_EF_CK 0x6FE1
+#define SCARD_FILE_EF_IK 0x6FE2
+
+#define SCARD_CHV1_OFFSET 13
+#define SCARD_CHV1_FLAG 0x80
+
+
typedef enum { SCARD_GSM_SIM, SCARD_USIM } sim_types;
struct scard_data {
@@ -240,37 +258,60 @@ static int scard_read_record(struct scard_data *scard,
static int scard_parse_fsp_templ(unsigned char *buf, size_t buf_len,
int *ps_do, int *file_len)
{
- unsigned char *pos, *end;
-
- if (ps_do)
- *ps_do = -1;
- if (file_len)
- *file_len = -1;
-
- pos = buf;
- end = pos + buf_len;
- if (*pos != USIM_FSP_TEMPL_TAG) {
- wpa_printf(MSG_DEBUG, "SCARD: file header did not "
- "start with FSP template tag");
- return -1;
- }
- pos++;
- if (pos >= end)
- return -1;
- if ((pos + pos[0]) < end)
- end = pos + 1 + pos[0];
- pos++;
- wpa_hexdump(MSG_DEBUG, "SCARD: file header FSP template",
- pos, end - pos);
-
- while (pos + 1 < end) {
- wpa_printf(MSG_MSGDUMP, "SCARD: file header TLV "
- "0x%02x len=%d", pos[0], pos[1]);
- if (pos + 2 + pos[1] > end)
- break;
+ unsigned char *pos, *end;
+
+ if (ps_do)
+ *ps_do = -1;
+ if (file_len)
+ *file_len = -1;
+
+ pos = buf;
+ end = pos + buf_len;
+ if (*pos != USIM_FSP_TEMPL_TAG) {
+ wpa_printf(MSG_DEBUG, "SCARD: file header did not "
+ "start with FSP template tag");
+ return -1;
+ }
+ pos++;
+ if (pos >= end)
+ return -1;
+ if ((pos + pos[0]) < end)
+ end = pos + 1 + pos[0];
+ pos++;
+ wpa_hexdump(MSG_DEBUG, "SCARD: file header FSP template",
+ pos, end - pos);
+
+ while (pos + 1 < end) {
+ wpa_printf(MSG_MSGDUMP, "SCARD: file header TLV 0x%02x len=%d",
+ pos[0], pos[1]);
+ if (pos + 2 + pos[1] > end)
+ break;
- if (pos[0] == USIM_TLV_FILE_SIZE &&
- (pos[1] == 1 || pos[1] == 2) && file_len) {
+ switch (pos[0]) {
+ case USIM_TLV_FILE_DESC:
+ wpa_hexdump(MSG_MSGDUMP, "SCARD: File Descriptor TLV",
+ pos + 2, pos[1]);
+ break;
+ case USIM_TLV_FILE_ID:
+ wpa_hexdump(MSG_MSGDUMP, "SCARD: File Identifier TLV",
+ pos + 2, pos[1]);
+ break;
+ case USIM_TLV_DF_NAME:
+ wpa_hexdump(MSG_MSGDUMP, "SCARD: DF name (AID) TLV",
+ pos + 2, pos[1]);
+ break;
+ case USIM_TLV_PROPR_INFO:
+ wpa_hexdump(MSG_MSGDUMP, "SCARD: Proprietary "
+ "information TLV", pos + 2, pos[1]);
+ break;
+ case USIM_TLV_LIFE_CYCLE_STATUS:
+ wpa_hexdump(MSG_MSGDUMP, "SCARD: Life Cycle Status "
+ "Integer TLV", pos + 2, pos[1]);
+ break;
+ case USIM_TLV_FILE_SIZE:
+ wpa_hexdump(MSG_MSGDUMP, "SCARD: File size TLV",
+ pos + 2, pos[1]);
+ if ((pos[1] == 1 || pos[1] == 2) && file_len) {
if (pos[1] == 1)
*file_len = (int) pos[2];
else
@@ -279,21 +320,43 @@ static int scard_parse_fsp_templ(unsigned char *buf, size_t buf_len,
wpa_printf(MSG_DEBUG, "SCARD: file_size=%d",
*file_len);
}
-
- if (pos[0] == USIM_TLV_PIN_STATUS_TEMPLATE &&
- pos[1] >= 2 && pos[2] == USIM_PS_DO_TAG &&
+ break;
+ case USIM_TLV_TOTAL_FILE_SIZE:
+ wpa_hexdump(MSG_MSGDUMP, "SCARD: Total file size TLV",
+ pos + 2, pos[1]);
+ break;
+ case USIM_TLV_PIN_STATUS_TEMPLATE:
+ wpa_hexdump(MSG_MSGDUMP, "SCARD: PIN Status Template "
+ "DO TLV", pos + 2, pos[1]);
+ if (pos[1] >= 2 && pos[2] == USIM_PS_DO_TAG &&
pos[3] >= 1 && ps_do) {
wpa_printf(MSG_DEBUG, "SCARD: PS_DO=0x%02x",
pos[4]);
*ps_do = (int) pos[4];
}
+ break;
+ case USIM_TLV_SHORT_FILE_ID:
+ wpa_hexdump(MSG_MSGDUMP, "SCARD: Short File "
+ "Identifier (SFI) TLV", pos + 2, pos[1]);
+ break;
+ case USIM_TLV_SECURITY_ATTR_8B:
+ case USIM_TLV_SECURITY_ATTR_8C:
+ case USIM_TLV_SECURITY_ATTR_AB:
+ wpa_hexdump(MSG_MSGDUMP, "SCARD: Security attribute "
+ "TLV", pos + 2, pos[1]);
+ break;
+ default:
+ wpa_hexdump(MSG_MSGDUMP, "SCARD: Unrecognized TLV",
+ pos, 2 + pos[1]);
+ break;
+ }
- pos += 2 + pos[1];
+ pos += 2 + pos[1];
- if (pos == end)
- return 0;
- }
- return -1;
+ if (pos == end)
+ return 0;
+ }
+ return -1;
}
@@ -334,7 +397,7 @@ static int scard_get_aid(struct scard_data *scard, unsigned char *aid,
unsigned char rid[5];
unsigned char appl_code[2]; /* 0x1002 for 3G USIM */
} *efdir;
- unsigned char buf[100];
+ unsigned char buf[127];
size_t blen;
efdir = (struct efdir *) buf;
@@ -423,6 +486,7 @@ static int scard_get_aid(struct scard_data *scard, unsigned char *aid,
/**
* scard_init - Initialize SIM/USIM connection using PC/SC
* @sim_type: Allowed SIM types (SIM, USIM, or both)
+ * @reader: Reader name prefix to search for
* Returns: Pointer to private data structure, or %NULL on failure
*
* This function is used to initialize SIM/USIM connection. PC/SC is used to
@@ -431,10 +495,10 @@ static int scard_get_aid(struct scard_data *scard, unsigned char *aid,
* access some of the card functions. Once the connection is not needed
* anymore, scard_deinit() can be used to close it.
*/
-struct scard_data * scard_init(scard_sim_type sim_type)
+struct scard_data * scard_init(scard_sim_type sim_type, const char *reader)
{
long ret;
- unsigned long len;
+ unsigned long len, pos;
struct scard_data *scard;
#ifdef CONFIG_NATIVE_WINDOWS
TCHAR *readers = NULL;
@@ -488,18 +552,41 @@ struct scard_data * scard_init(scard_sim_type sim_type)
"available.");
goto failed;
}
- /* readers is a list of available reader. Last entry is terminated with
- * double NUL.
- * TODO: add support for selecting the reader; now just use the first
- * one.. */
+ wpa_hexdump_ascii(MSG_DEBUG, "SCARD: Readers", (u8 *) readers, len);
+ /*
+ * readers is a list of available readers. The last entry is terminated
+ * with double null.
+ */
+ pos = 0;
#ifdef UNICODE
- wpa_printf(MSG_DEBUG, "SCARD: Selected reader='%S'", readers);
+ /* TODO */
#else /* UNICODE */
- wpa_printf(MSG_DEBUG, "SCARD: Selected reader='%s'", readers);
+ while (pos < len) {
+ if (reader == NULL ||
+ os_strncmp(&readers[pos], reader, os_strlen(reader)) == 0)
+ break;
+ while (pos < len && readers[pos])
+ pos++;
+ pos++; /* skip separating null */
+ if (pos < len && readers[pos] == '\0')
+ pos = len; /* double null terminates list */
+ }
#endif /* UNICODE */
+ if (pos >= len) {
+ wpa_printf(MSG_WARNING, "SCARD: No reader with prefix '%s' "
+ "found", reader);
+ goto failed;
+ }
- ret = SCardConnect(scard->ctx, readers, SCARD_SHARE_SHARED,
- SCARD_PROTOCOL_T0, &scard->card, &scard->protocol);
+#ifdef UNICODE
+ wpa_printf(MSG_DEBUG, "SCARD: Selected reader='%S'", &readers[pos]);
+#else /* UNICODE */
+ wpa_printf(MSG_DEBUG, "SCARD: Selected reader='%s'", &readers[pos]);
+#endif /* UNICODE */
+
+ ret = SCardConnect(scard->ctx, &readers[pos], SCARD_SHARE_SHARED,
+ SCARD_PROTOCOL_T0 | SCARD_PROTOCOL_T1,
+ &scard->card, &scard->protocol);
if (ret != SCARD_S_SUCCESS) {
if (ret == (long) SCARD_E_NO_SMARTCARD)
wpa_printf(MSG_INFO, "No smart card inserted.");
@@ -588,7 +675,8 @@ struct scard_data * scard_init(scard_sim_type sim_type)
}
if (pin_needed) {
scard->pin1_required = 1;
- wpa_printf(MSG_DEBUG, "PIN1 needed for SIM access");
+ wpa_printf(MSG_DEBUG, "PIN1 needed for SIM access (retry "
+ "counter=%d)", scard_get_pin_retry_counter(scard));
}
ret = SCardEndTransaction(scard->card, SCARD_LEAVE_CARD);
@@ -812,7 +900,7 @@ static int scard_get_record_len(struct scard_data *scard, unsigned char recnum,
wpa_hexdump(MSG_DEBUG, "SCARD: file length determination response",
buf, blen);
- if (blen < 2 || buf[0] != 0x6c) {
+ if (blen < 2 || (buf[0] != 0x6c && buf[0] != 0x67)) {
wpa_printf(MSG_DEBUG, "SCARD: unexpected response to file "
"length determination");
return -1;
@@ -945,6 +1033,46 @@ static int scard_verify_pin(struct scard_data *scard, const char *pin)
}
+int scard_get_pin_retry_counter(struct scard_data *scard)
+{
+ long ret;
+ unsigned char resp[3];
+ unsigned char cmd[5] = { SIM_CMD_VERIFY_CHV1 };
+ size_t len;
+ u16 val;
+
+ wpa_printf(MSG_DEBUG, "SCARD: fetching PIN retry counter");
+
+ if (scard->sim_type == SCARD_USIM)
+ cmd[0] = USIM_CLA;
+ cmd[4] = 0; /* Empty data */
+
+ len = sizeof(resp);
+ ret = scard_transmit(scard, cmd, sizeof(cmd), resp, &len);
+ if (ret != SCARD_S_SUCCESS)
+ return -2;
+
+ if (len != 2) {
+ wpa_printf(MSG_WARNING, "SCARD: failed to fetch PIN retry "
+ "counter");
+ return -1;
+ }
+
+ val = WPA_GET_BE16(resp);
+ if (val == 0x63c0 || val == 0x6983) {
+ wpa_printf(MSG_DEBUG, "SCARD: PIN has been blocked");
+ return 0;
+ }
+
+ if (val >= 0x63c0 && val <= 0x63cf)
+ return val & 0x000f;
+
+ wpa_printf(MSG_DEBUG, "SCARD: Unexpected PIN retry counter response "
+ "value 0x%x", val);
+ return 0;
+}
+
+
/**
* scard_get_imsi - Read IMSI from SIM/USIM card
* @scard: Pointer to private data from scard_init()
@@ -1024,6 +1152,61 @@ int scard_get_imsi(struct scard_data *scard, char *imsi, size_t *len)
/**
+ * scard_get_mnc_len - Read length of MNC in the IMSI from SIM/USIM card
+ * @scard: Pointer to private data from scard_init()
+ * Returns: length (>0) on success, -1 if administrative data file cannot be
+ * selected, -2 if administrative data file selection returns invalid result
+ * code, -3 if parsing FSP template file fails (USIM only), -4 if length of
+ * the file is unexpected, -5 if reading file fails, -6 if MNC length is not
+ * in range (i.e. 2 or 3), -7 if MNC length is not available.
+ *
+ */
+int scard_get_mnc_len(struct scard_data *scard)
+{
+ unsigned char buf[100];
+ size_t blen;
+ int file_size;
+
+ wpa_printf(MSG_DEBUG, "SCARD: reading MNC len from (GSM) EF-AD");
+ blen = sizeof(buf);
+ if (scard_select_file(scard, SCARD_FILE_GSM_EF_AD, buf, &blen))
+ return -1;
+ if (blen < 4) {
+ wpa_printf(MSG_WARNING, "SCARD: too short (GSM) EF-AD "
+ "header (len=%ld)", (long) blen);
+ return -2;
+ }
+
+ if (scard->sim_type == SCARD_GSM_SIM) {
+ file_size = (buf[2] << 8) | buf[3];
+ } else {
+ if (scard_parse_fsp_templ(buf, blen, NULL, &file_size))
+ return -3;
+ }
+ if (file_size == 3) {
+ wpa_printf(MSG_DEBUG, "SCARD: MNC length not available");
+ return -7;
+ }
+ if (file_size < 4 || file_size > (int) sizeof(buf)) {
+ wpa_printf(MSG_DEBUG, "SCARD: invalid file length=%ld",
+ (long) file_size);
+ return -4;
+ }
+
+ if (scard_read_file(scard, buf, file_size))
+ return -5;
+ buf[3] = buf[3] & 0x0f; /* upper nibble reserved for future use */
+ if (buf[3] < 2 || buf[3] > 3) {
+ wpa_printf(MSG_DEBUG, "SCARD: invalid MNC length=%ld",
+ (long) buf[3]);
+ return -6;
+ }
+ wpa_printf(MSG_DEBUG, "SCARD: MNC length=%ld", (long) buf[3]);
+ return buf[3];
+}
+
+
+/**
* scard_gsm_auth - Run GSM authentication command on SIM card
* @scard: Pointer to private data from scard_init()
* @_rand: 16-byte RAND value from HLR/AuC
@@ -1236,3 +1419,9 @@ int scard_umts_auth(struct scard_data *scard, const unsigned char *_rand,
wpa_printf(MSG_DEBUG, "SCARD: Unrecognized response");
return -1;
}
+
+
+int scard_supports_umts(struct scard_data *scard)
+{
+ return scard->sim_type == SCARD_USIM;
+}
diff --git a/contrib/wpa/src/utils/pcsc_funcs.h b/contrib/wpa/src/utils/pcsc_funcs.h
index 543f7c5..b4ebc99 100644
--- a/contrib/wpa/src/utils/pcsc_funcs.h
+++ b/contrib/wpa/src/utils/pcsc_funcs.h
@@ -1,39 +1,14 @@
/*
* WPA Supplicant / PC/SC smartcard interface for USIM, GSM SIM
- * Copyright (c) 2004-2006, Jouni Malinen <j@w1.fi>
+ * Copyright (c) 2004-2006, 2012, Jouni Malinen <j@w1.fi>
*
- * This program is free software; you can redistribute it and/or modify
- * it under the terms of the GNU General Public License version 2 as
- * published by the Free Software Foundation.
- *
- * Alternatively, this software may be distributed under the terms of BSD
- * license.
- *
- * See README and COPYING for more details.
+ * This software may be distributed under the terms of the BSD license.
+ * See README for more details.
*/
#ifndef PCSC_FUNCS_H
#define PCSC_FUNCS_H
-/* GSM files
- * File type in first octet:
- * 3F = Master File
- * 7F = Dedicated File
- * 2F = Elementary File under the Master File
- * 6F = Elementary File under a Dedicated File
- */
-#define SCARD_FILE_MF 0x3F00
-#define SCARD_FILE_GSM_DF 0x7F20
-#define SCARD_FILE_UMTS_DF 0x7F50
-#define SCARD_FILE_GSM_EF_IMSI 0x6F07
-#define SCARD_FILE_EF_DIR 0x2F00
-#define SCARD_FILE_EF_ICCID 0x2FE2
-#define SCARD_FILE_EF_CK 0x6FE1
-#define SCARD_FILE_EF_IK 0x6FE2
-
-#define SCARD_CHV1_OFFSET 13
-#define SCARD_CHV1_FLAG 0x80
-
typedef enum {
SCARD_GSM_SIM_ONLY,
SCARD_USIM_ONLY,
@@ -42,26 +17,32 @@ typedef enum {
#ifdef PCSC_FUNCS
-struct scard_data * scard_init(scard_sim_type sim_type);
+struct scard_data * scard_init(scard_sim_type sim_type, const char *reader);
void scard_deinit(struct scard_data *scard);
int scard_set_pin(struct scard_data *scard, const char *pin);
int scard_get_imsi(struct scard_data *scard, char *imsi, size_t *len);
+int scard_get_mnc_len(struct scard_data *scard);
int scard_gsm_auth(struct scard_data *scard, const unsigned char *_rand,
unsigned char *sres, unsigned char *kc);
int scard_umts_auth(struct scard_data *scard, const unsigned char *_rand,
const unsigned char *autn,
unsigned char *res, size_t *res_len,
unsigned char *ik, unsigned char *ck, unsigned char *auts);
+int scard_get_pin_retry_counter(struct scard_data *scard);
+int scard_supports_umts(struct scard_data *scard);
#else /* PCSC_FUNCS */
-#define scard_init(s) NULL
+#define scard_init(s, r) NULL
#define scard_deinit(s) do { } while (0)
#define scard_set_pin(s, p) -1
#define scard_get_imsi(s, i, l) -1
+#define scard_get_mnc_len(s) -1
#define scard_gsm_auth(s, r, s2, k) -1
#define scard_umts_auth(s, r, a, r2, rl, i, c, a2) -1
+#define scard_get_pin_retry_counter(s) -1
+#define scard_supports_umts(s) 0
#endif /* PCSC_FUNCS */
diff --git a/contrib/wpa/src/utils/radiotap.h b/contrib/wpa/src/utils/radiotap.h
index ba23ed3..c888d36 100644
--- a/contrib/wpa/src/utils/radiotap.h
+++ b/contrib/wpa/src/utils/radiotap.h
@@ -1,4 +1,4 @@
-/* $FreeBSD$ */
+/* $FreeBSD: projects/hyperv/contrib/wpa/src/utils/radiotap.h 214734 2010-11-03 10:43:38Z rpaulo $ */
/* $NetBSD: ieee80211_radiotap.h,v 1.11 2005/06/22 06:16:02 dyoung Exp $ */
/*-
@@ -238,5 +238,6 @@ enum ieee80211_radiotap_type {
* retries */
#define IEEE80211_RADIOTAP_F_TX_CTS 0x0002 /* used cts 'protection' */
#define IEEE80211_RADIOTAP_F_TX_RTS 0x0004 /* used rts/cts handshake */
+#define IEEE80211_RADIOTAP_F_TX_NOACK 0x0008 /* don't expect an ACK */
#endif /* IEEE80211_RADIOTAP_H */
diff --git a/contrib/wpa/src/utils/radiotap_iter.h b/contrib/wpa/src/utils/radiotap_iter.h
index 92a798a..2e0e872 100644
--- a/contrib/wpa/src/utils/radiotap_iter.h
+++ b/contrib/wpa/src/utils/radiotap_iter.h
@@ -1,3 +1,18 @@
+/*
+ * Radiotap parser
+ *
+ * Copyright 2007 Andy Green <andy@warmcat.com>
+ *
+ * This program is free software; you can redistribute it and/or modify
+ * it under the terms of the GNU General Public License version 2 as
+ * published by the Free Software Foundation.
+ *
+ * Alternatively, this software may be distributed under the terms of BSD
+ * license.
+ *
+ * See README and COPYING for more details.
+ */
+
#ifndef __RADIOTAP_ITER_H
#define __RADIOTAP_ITER_H
diff --git a/contrib/wpa/src/utils/state_machine.h b/contrib/wpa/src/utils/state_machine.h
index 31f6672..a514315 100644
--- a/contrib/wpa/src/utils/state_machine.h
+++ b/contrib/wpa/src/utils/state_machine.h
@@ -2,14 +2,8 @@
* wpa_supplicant/hostapd - State machine definitions
* Copyright (c) 2002-2005, Jouni Malinen <j@w1.fi>
*
- * This program is free software; you can redistribute it and/or modify
- * it under the terms of the GNU General Public License version 2 as
- * published by the Free Software Foundation.
- *
- * Alternatively, this software may be distributed under the terms of BSD
- * license.
- *
- * See README and COPYING for more details.
+ * This software may be distributed under the terms of the BSD license.
+ * See README for more details.
*
* This file includes a set of pre-processor macros that can be used to
* implement a state machine. In addition to including this header file, each
diff --git a/contrib/wpa/src/utils/trace.c b/contrib/wpa/src/utils/trace.c
index bb3eb24..6795d41 100644
--- a/contrib/wpa/src/utils/trace.c
+++ b/contrib/wpa/src/utils/trace.c
@@ -2,14 +2,8 @@
* Backtrace debugging
* Copyright (c) 2009, Jouni Malinen <j@w1.fi>
*
- * This program is free software; you can redistribute it and/or modify
- * it under the terms of the GNU General Public License version 2 as
- * published by the Free Software Foundation.
- *
- * Alternatively, this software may be distributed under the terms of BSD
- * license.
- *
- * See README and COPYING for more details.
+ * This software may be distributed under the terms of the BSD license.
+ * See README for more details.
*/
#include "includes.h"
diff --git a/contrib/wpa/src/utils/trace.h b/contrib/wpa/src/utils/trace.h
index 22d3de0..38f43fb 100644
--- a/contrib/wpa/src/utils/trace.h
+++ b/contrib/wpa/src/utils/trace.h
@@ -2,14 +2,8 @@
* Backtrace debugging
* Copyright (c) 2009, Jouni Malinen <j@w1.fi>
*
- * This program is free software; you can redistribute it and/or modify
- * it under the terms of the GNU General Public License version 2 as
- * published by the Free Software Foundation.
- *
- * Alternatively, this software may be distributed under the terms of BSD
- * license.
- *
- * See README and COPYING for more details.
+ * This software may be distributed under the terms of the BSD license.
+ * See README for more details.
*/
#ifndef TRACE_H
diff --git a/contrib/wpa/src/utils/uuid.c b/contrib/wpa/src/utils/uuid.c
index d8cc267..2aa4bcb 100644
--- a/contrib/wpa/src/utils/uuid.c
+++ b/contrib/wpa/src/utils/uuid.c
@@ -2,14 +2,8 @@
* Universally Unique IDentifier (UUID)
* Copyright (c) 2008, Jouni Malinen <j@w1.fi>
*
- * This program is free software; you can redistribute it and/or modify
- * it under the terms of the GNU General Public License version 2 as
- * published by the Free Software Foundation.
- *
- * Alternatively, this software may be distributed under the terms of BSD
- * license.
- *
- * See README and COPYING for more details.
+ * This software may be distributed under the terms of the BSD license.
+ * See README for more details.
*/
#include "includes.h"
diff --git a/contrib/wpa/src/utils/uuid.h b/contrib/wpa/src/utils/uuid.h
index 0759165..5e860cb 100644
--- a/contrib/wpa/src/utils/uuid.h
+++ b/contrib/wpa/src/utils/uuid.h
@@ -2,14 +2,8 @@
* Universally Unique IDentifier (UUID)
* Copyright (c) 2008, Jouni Malinen <j@w1.fi>
*
- * This program is free software; you can redistribute it and/or modify
- * it under the terms of the GNU General Public License version 2 as
- * published by the Free Software Foundation.
- *
- * Alternatively, this software may be distributed under the terms of BSD
- * license.
- *
- * See README and COPYING for more details.
+ * This software may be distributed under the terms of the BSD license.
+ * See README for more details.
*/
#ifndef UUID_H
diff --git a/contrib/wpa/src/utils/wpa_debug.c b/contrib/wpa/src/utils/wpa_debug.c
index 6f6fc69..5511ef1 100644
--- a/contrib/wpa/src/utils/wpa_debug.c
+++ b/contrib/wpa/src/utils/wpa_debug.c
@@ -2,14 +2,8 @@
* wpa_supplicant/hostapd / Debug prints
* Copyright (c) 2002-2007, Jouni Malinen <j@w1.fi>
*
- * This program is free software; you can redistribute it and/or modify
- * it under the terms of the GNU General Public License version 2 as
- * published by the Free Software Foundation.
- *
- * Alternatively, this software may be distributed under the terms of BSD
- * license.
- *
- * See README and COPYING for more details.
+ * This software may be distributed under the terms of the BSD license.
+ * See README for more details.
*/
#include "includes.h"
@@ -22,19 +16,55 @@
static int wpa_debug_syslog = 0;
#endif /* CONFIG_DEBUG_SYSLOG */
+#ifdef CONFIG_DEBUG_LINUX_TRACING
+#include <sys/types.h>
+#include <sys/stat.h>
+#include <fcntl.h>
+#include <string.h>
+#include <stdio.h>
+
+static FILE *wpa_debug_tracing_file = NULL;
+
+#define WPAS_TRACE_PFX "wpas <%d>: "
+#endif /* CONFIG_DEBUG_LINUX_TRACING */
+
-#ifdef CONFIG_DEBUG_FILE
-static FILE *out_file = NULL;
-#endif /* CONFIG_DEBUG_FILE */
int wpa_debug_level = MSG_INFO;
int wpa_debug_show_keys = 0;
int wpa_debug_timestamp = 0;
+#ifdef CONFIG_ANDROID_LOG
+
+#include <android/log.h>
+
+#ifndef ANDROID_LOG_NAME
+#define ANDROID_LOG_NAME "wpa_supplicant"
+#endif /* ANDROID_LOG_NAME */
+
+static int wpa_to_android_level(int level)
+{
+ if (level == MSG_ERROR)
+ return ANDROID_LOG_ERROR;
+ if (level == MSG_WARNING)
+ return ANDROID_LOG_WARN;
+ if (level == MSG_INFO)
+ return ANDROID_LOG_INFO;
+ return ANDROID_LOG_DEBUG;
+}
+
+#endif /* CONFIG_ANDROID_LOG */
+
#ifndef CONFIG_NO_STDOUT_DEBUG
+#ifdef CONFIG_DEBUG_FILE
+static FILE *out_file = NULL;
+#endif /* CONFIG_DEBUG_FILE */
+
+
void wpa_debug_print_timestamp(void)
{
+#ifndef CONFIG_ANDROID_LOG
struct os_time tv;
if (!wpa_debug_timestamp)
@@ -48,13 +78,18 @@ void wpa_debug_print_timestamp(void)
} else
#endif /* CONFIG_DEBUG_FILE */
printf("%ld.%06u: ", (long) tv.sec, (unsigned int) tv.usec);
+#endif /* CONFIG_ANDROID_LOG */
}
#ifdef CONFIG_DEBUG_SYSLOG
+#ifndef LOG_HOSTAPD
+#define LOG_HOSTAPD LOG_DAEMON
+#endif /* LOG_HOSTAPD */
+
void wpa_debug_open_syslog(void)
{
- openlog("wpa_supplicant", LOG_PID | LOG_NDELAY, LOG_DAEMON);
+ openlog("wpa_supplicant", LOG_PID | LOG_NDELAY, LOG_HOSTAPD);
wpa_debug_syslog++;
}
@@ -84,6 +119,77 @@ static int syslog_priority(int level)
#endif /* CONFIG_DEBUG_SYSLOG */
+#ifdef CONFIG_DEBUG_LINUX_TRACING
+
+int wpa_debug_open_linux_tracing(void)
+{
+ int mounts, trace_fd;
+ char buf[4096] = {};
+ ssize_t buflen;
+ char *line, *tmp1, *path = NULL;
+
+ mounts = open("/proc/mounts", O_RDONLY);
+ if (mounts < 0) {
+ printf("no /proc/mounts\n");
+ return -1;
+ }
+
+ buflen = read(mounts, buf, sizeof(buf) - 1);
+ close(mounts);
+ if (buflen < 0) {
+ printf("failed to read /proc/mounts\n");
+ return -1;
+ }
+
+ line = strtok_r(buf, "\n", &tmp1);
+ while (line) {
+ char *tmp2, *tmp_path, *fstype;
+ /* "<dev> <mountpoint> <fs type> ..." */
+ strtok_r(line, " ", &tmp2);
+ tmp_path = strtok_r(NULL, " ", &tmp2);
+ fstype = strtok_r(NULL, " ", &tmp2);
+ if (strcmp(fstype, "debugfs") == 0) {
+ path = tmp_path;
+ break;
+ }
+
+ line = strtok_r(NULL, "\n", &tmp1);
+ }
+
+ if (path == NULL) {
+ printf("debugfs mountpoint not found\n");
+ return -1;
+ }
+
+ snprintf(buf, sizeof(buf) - 1, "%s/tracing/trace_marker", path);
+
+ trace_fd = open(buf, O_WRONLY);
+ if (trace_fd < 0) {
+ printf("failed to open trace_marker file\n");
+ return -1;
+ }
+ wpa_debug_tracing_file = fdopen(trace_fd, "w");
+ if (wpa_debug_tracing_file == NULL) {
+ close(trace_fd);
+ printf("failed to fdopen()\n");
+ return -1;
+ }
+
+ return 0;
+}
+
+
+void wpa_debug_close_linux_tracing(void)
+{
+ if (wpa_debug_tracing_file == NULL)
+ return;
+ fclose(wpa_debug_tracing_file);
+ wpa_debug_tracing_file = NULL;
+}
+
+#endif /* CONFIG_DEBUG_LINUX_TRACING */
+
+
/**
* wpa_printf - conditional printf
* @level: priority level (MSG_*) of the message
@@ -101,6 +207,10 @@ void wpa_printf(int level, const char *fmt, ...)
va_start(ap, fmt);
if (level >= wpa_debug_level) {
+#ifdef CONFIG_ANDROID_LOG
+ __android_log_vprint(wpa_to_android_level(level),
+ ANDROID_LOG_NAME, fmt, ap);
+#else /* CONFIG_ANDROID_LOG */
#ifdef CONFIG_DEBUG_SYSLOG
if (wpa_debug_syslog) {
vsyslog(syslog_priority(level), fmt, ap);
@@ -121,8 +231,20 @@ void wpa_printf(int level, const char *fmt, ...)
#ifdef CONFIG_DEBUG_SYSLOG
}
#endif /* CONFIG_DEBUG_SYSLOG */
+#endif /* CONFIG_ANDROID_LOG */
}
va_end(ap);
+
+#ifdef CONFIG_DEBUG_LINUX_TRACING
+ if (wpa_debug_tracing_file != NULL) {
+ va_start(ap, fmt);
+ fprintf(wpa_debug_tracing_file, WPAS_TRACE_PFX, level);
+ vfprintf(wpa_debug_tracing_file, fmt, ap);
+ fprintf(wpa_debug_tracing_file, "\n");
+ fflush(wpa_debug_tracing_file);
+ va_end(ap);
+ }
+#endif /* CONFIG_DEBUG_LINUX_TRACING */
}
@@ -130,8 +252,97 @@ static void _wpa_hexdump(int level, const char *title, const u8 *buf,
size_t len, int show)
{
size_t i;
+
+#ifdef CONFIG_DEBUG_LINUX_TRACING
+ if (wpa_debug_tracing_file != NULL) {
+ fprintf(wpa_debug_tracing_file,
+ WPAS_TRACE_PFX "%s - hexdump(len=%lu):",
+ level, title, (unsigned long) len);
+ if (buf == NULL) {
+ fprintf(wpa_debug_tracing_file, " [NULL]\n");
+ } else if (!show) {
+ fprintf(wpa_debug_tracing_file, " [REMOVED]\n");
+ } else {
+ for (i = 0; i < len; i++)
+ fprintf(wpa_debug_tracing_file,
+ " %02x", buf[i]);
+ }
+ fflush(wpa_debug_tracing_file);
+ }
+#endif /* CONFIG_DEBUG_LINUX_TRACING */
+
if (level < wpa_debug_level)
return;
+#ifdef CONFIG_ANDROID_LOG
+ {
+ const char *display;
+ char *strbuf = NULL;
+ size_t slen = len;
+ if (buf == NULL) {
+ display = " [NULL]";
+ } else if (len == 0) {
+ display = "";
+ } else if (show && len) {
+ /* Limit debug message length for Android log */
+ if (slen > 32)
+ slen = 32;
+ strbuf = os_malloc(1 + 3 * slen);
+ if (strbuf == NULL) {
+ wpa_printf(MSG_ERROR, "wpa_hexdump: Failed to "
+ "allocate message buffer");
+ return;
+ }
+
+ for (i = 0; i < slen; i++)
+ os_snprintf(&strbuf[i * 3], 4, " %02x",
+ buf[i]);
+
+ display = strbuf;
+ } else {
+ display = " [REMOVED]";
+ }
+
+ __android_log_print(wpa_to_android_level(level),
+ ANDROID_LOG_NAME,
+ "%s - hexdump(len=%lu):%s%s",
+ title, (long unsigned int) len, display,
+ len > slen ? " ..." : "");
+ os_free(strbuf);
+ return;
+ }
+#else /* CONFIG_ANDROID_LOG */
+#ifdef CONFIG_DEBUG_SYSLOG
+ if (wpa_debug_syslog) {
+ const char *display;
+ char *strbuf = NULL;
+
+ if (buf == NULL) {
+ display = " [NULL]";
+ } else if (len == 0) {
+ display = "";
+ } else if (show && len) {
+ strbuf = os_malloc(1 + 3 * len);
+ if (strbuf == NULL) {
+ wpa_printf(MSG_ERROR, "wpa_hexdump: Failed to "
+ "allocate message buffer");
+ return;
+ }
+
+ for (i = 0; i < len; i++)
+ os_snprintf(&strbuf[i * 3], 4, " %02x",
+ buf[i]);
+
+ display = strbuf;
+ } else {
+ display = " [REMOVED]";
+ }
+
+ syslog(syslog_priority(level), "%s - hexdump(len=%lu):%s",
+ title, (unsigned long) len, display);
+ os_free(strbuf);
+ return;
+ }
+#endif /* CONFIG_DEBUG_SYSLOG */
wpa_debug_print_timestamp();
#ifdef CONFIG_DEBUG_FILE
if (out_file) {
@@ -161,6 +372,7 @@ static void _wpa_hexdump(int level, const char *title, const u8 *buf,
#ifdef CONFIG_DEBUG_FILE
}
#endif /* CONFIG_DEBUG_FILE */
+#endif /* CONFIG_ANDROID_LOG */
}
void wpa_hexdump(int level, const char *title, const u8 *buf, size_t len)
@@ -182,8 +394,30 @@ static void _wpa_hexdump_ascii(int level, const char *title, const u8 *buf,
const u8 *pos = buf;
const size_t line_len = 16;
+#ifdef CONFIG_DEBUG_LINUX_TRACING
+ if (wpa_debug_tracing_file != NULL) {
+ fprintf(wpa_debug_tracing_file,
+ WPAS_TRACE_PFX "%s - hexdump_ascii(len=%lu):",
+ level, title, (unsigned long) len);
+ if (buf == NULL) {
+ fprintf(wpa_debug_tracing_file, " [NULL]\n");
+ } else if (!show) {
+ fprintf(wpa_debug_tracing_file, " [REMOVED]\n");
+ } else {
+ /* can do ascii processing in userspace */
+ for (i = 0; i < len; i++)
+ fprintf(wpa_debug_tracing_file,
+ " %02x", buf[i]);
+ }
+ fflush(wpa_debug_tracing_file);
+ }
+#endif /* CONFIG_DEBUG_LINUX_TRACING */
+
if (level < wpa_debug_level)
return;
+#ifdef CONFIG_ANDROID_LOG
+ _wpa_hexdump(level, title, buf, len, show);
+#else /* CONFIG_ANDROID_LOG */
wpa_debug_print_timestamp();
#ifdef CONFIG_DEBUG_FILE
if (out_file) {
@@ -257,6 +491,7 @@ static void _wpa_hexdump_ascii(int level, const char *title, const u8 *buf,
#ifdef CONFIG_DEBUG_FILE
}
#endif /* CONFIG_DEBUG_FILE */
+#endif /* CONFIG_ANDROID_LOG */
}
@@ -273,11 +508,43 @@ void wpa_hexdump_ascii_key(int level, const char *title, const u8 *buf,
}
+#ifdef CONFIG_DEBUG_FILE
+static char *last_path = NULL;
+#endif /* CONFIG_DEBUG_FILE */
+
+int wpa_debug_reopen_file(void)
+{
+#ifdef CONFIG_DEBUG_FILE
+ int rv;
+ if (last_path) {
+ char *tmp = os_strdup(last_path);
+ wpa_debug_close_file();
+ rv = wpa_debug_open_file(tmp);
+ os_free(tmp);
+ } else {
+ wpa_printf(MSG_ERROR, "Last-path was not set, cannot "
+ "re-open log file.");
+ rv = -1;
+ }
+ return rv;
+#else /* CONFIG_DEBUG_FILE */
+ return 0;
+#endif /* CONFIG_DEBUG_FILE */
+}
+
+
int wpa_debug_open_file(const char *path)
{
#ifdef CONFIG_DEBUG_FILE
if (!path)
return 0;
+
+ if (last_path == NULL || os_strcmp(last_path, path) != 0) {
+ /* Save our path to enable re-open */
+ os_free(last_path);
+ last_path = os_strdup(path);
+ }
+
out_file = fopen(path, "a");
if (out_file == NULL) {
wpa_printf(MSG_ERROR, "wpa_debug_open_file: Failed to open "
@@ -299,6 +566,8 @@ void wpa_debug_close_file(void)
return;
fclose(out_file);
out_file = NULL;
+ os_free(last_path);
+ last_path = NULL;
#endif /* CONFIG_DEBUG_FILE */
}
@@ -314,12 +583,21 @@ void wpa_msg_register_cb(wpa_msg_cb_func func)
}
+static wpa_msg_get_ifname_func wpa_msg_ifname_cb = NULL;
+
+void wpa_msg_register_ifname_cb(wpa_msg_get_ifname_func func)
+{
+ wpa_msg_ifname_cb = func;
+}
+
+
void wpa_msg(void *ctx, int level, const char *fmt, ...)
{
va_list ap;
char *buf;
const int buflen = 2048;
int len;
+ char prefix[130];
buf = os_malloc(buflen);
if (buf == NULL) {
@@ -328,9 +606,19 @@ void wpa_msg(void *ctx, int level, const char *fmt, ...)
return;
}
va_start(ap, fmt);
+ prefix[0] = '\0';
+ if (wpa_msg_ifname_cb) {
+ const char *ifname = wpa_msg_ifname_cb(ctx);
+ if (ifname) {
+ int res = os_snprintf(prefix, sizeof(prefix), "%s: ",
+ ifname);
+ if (res < 0 || res >= (int) sizeof(prefix))
+ prefix[0] = '\0';
+ }
+ }
len = vsnprintf(buf, buflen, fmt, ap);
va_end(ap);
- wpa_printf(level, "%s", buf);
+ wpa_printf(level, "%s%s", prefix, buf);
if (wpa_msg_cb)
wpa_msg_cb(ctx, level, buf, len);
os_free(buf);
diff --git a/contrib/wpa/src/utils/wpa_debug.h b/contrib/wpa/src/utils/wpa_debug.h
index 6e5e79e..339c749 100644
--- a/contrib/wpa/src/utils/wpa_debug.h
+++ b/contrib/wpa/src/utils/wpa_debug.h
@@ -2,14 +2,8 @@
* wpa_supplicant/hostapd / Debug prints
* Copyright (c) 2002-2007, Jouni Malinen <j@w1.fi>
*
- * This program is free software; you can redistribute it and/or modify
- * it under the terms of the GNU General Public License version 2 as
- * published by the Free Software Foundation.
- *
- * Alternatively, this software may be distributed under the terms of BSD
- * license.
- *
- * See README and COPYING for more details.
+ * This software may be distributed under the terms of the BSD license.
+ * See README for more details.
*/
#ifndef WPA_DEBUG_H
@@ -20,7 +14,9 @@
/* Debugging function - conditional printf and hex dump. Driver wrappers can
* use these for debugging purposes. */
-enum { MSG_MSGDUMP, MSG_DEBUG, MSG_INFO, MSG_WARNING, MSG_ERROR };
+enum {
+ MSG_EXCESSIVE, MSG_MSGDUMP, MSG_DEBUG, MSG_INFO, MSG_WARNING, MSG_ERROR
+};
#ifdef CONFIG_NO_STDOUT_DEBUG
@@ -34,10 +30,17 @@ enum { MSG_MSGDUMP, MSG_DEBUG, MSG_INFO, MSG_WARNING, MSG_ERROR };
#define wpa_hexdump_ascii_key(l,t,b,le) do { } while (0)
#define wpa_debug_open_file(p) do { } while (0)
#define wpa_debug_close_file() do { } while (0)
+#define wpa_dbg(args...) do { } while (0)
+
+static inline int wpa_debug_reopen_file(void)
+{
+ return 0;
+}
#else /* CONFIG_NO_STDOUT_DEBUG */
int wpa_debug_open_file(const char *path);
+int wpa_debug_reopen_file(void);
void wpa_debug_close_file(void);
/**
@@ -79,7 +82,8 @@ void wpa_hexdump(int level, const char *title, const u8 *buf, size_t len);
static inline void wpa_hexdump_buf(int level, const char *title,
const struct wpabuf *buf)
{
- wpa_hexdump(level, title, wpabuf_head(buf), wpabuf_len(buf));
+ wpa_hexdump(level, title, buf ? wpabuf_head(buf) : NULL,
+ buf ? wpabuf_len(buf) : 0);
}
/**
@@ -100,7 +104,8 @@ void wpa_hexdump_key(int level, const char *title, const u8 *buf, size_t len);
static inline void wpa_hexdump_buf_key(int level, const char *title,
const struct wpabuf *buf)
{
- wpa_hexdump_key(level, title, wpabuf_head(buf), wpabuf_len(buf));
+ wpa_hexdump_key(level, title, buf ? wpabuf_head(buf) : NULL,
+ buf ? wpabuf_len(buf) : 0);
}
/**
@@ -136,6 +141,14 @@ void wpa_hexdump_ascii(int level, const char *title, const u8 *buf,
void wpa_hexdump_ascii_key(int level, const char *title, const u8 *buf,
size_t len);
+/*
+ * wpa_dbg() behaves like wpa_msg(), but it can be removed from build to reduce
+ * binary size. As such, it should be used with debugging messages that are not
+ * needed in the control interface while wpa_msg() has to be used for anything
+ * that needs to shown to control interface monitors.
+ */
+#define wpa_dbg(args...) wpa_msg(args)
+
#endif /* CONFIG_NO_STDOUT_DEBUG */
@@ -143,6 +156,7 @@ void wpa_hexdump_ascii_key(int level, const char *title, const u8 *buf,
#define wpa_msg(args...) do { } while (0)
#define wpa_msg_ctrl(args...) do { } while (0)
#define wpa_msg_register_cb(f) do { } while (0)
+#define wpa_msg_register_ifname_cb(f) do { } while (0)
#else /* CONFIG_NO_WPA_MSG */
/**
* wpa_msg - Conditional printf for default target and ctrl_iface monitors
@@ -183,8 +197,11 @@ typedef void (*wpa_msg_cb_func)(void *ctx, int level, const char *txt,
* @func: Callback function (%NULL to unregister)
*/
void wpa_msg_register_cb(wpa_msg_cb_func func);
-#endif /* CONFIG_NO_WPA_MSG */
+typedef const char * (*wpa_msg_get_ifname_func)(void *ctx);
+void wpa_msg_register_ifname_cb(wpa_msg_get_ifname_func func);
+
+#endif /* CONFIG_NO_WPA_MSG */
#ifdef CONFIG_NO_HOSTAPD_LOGGER
#define hostapd_logger(args...) do { } while (0)
@@ -238,6 +255,24 @@ static inline void wpa_debug_close_syslog(void)
#endif /* CONFIG_DEBUG_SYSLOG */
+#ifdef CONFIG_DEBUG_LINUX_TRACING
+
+int wpa_debug_open_linux_tracing(void);
+void wpa_debug_close_linux_tracing(void);
+
+#else /* CONFIG_DEBUG_LINUX_TRACING */
+
+static inline int wpa_debug_open_linux_tracing(void)
+{
+ return 0;
+}
+
+static inline void wpa_debug_close_linux_tracing(void)
+{
+}
+
+#endif /* CONFIG_DEBUG_LINUX_TRACING */
+
#ifdef EAPOL_TEST
#define WPA_ASSERT(a) \
diff --git a/contrib/wpa/src/utils/wpabuf.c b/contrib/wpa/src/utils/wpabuf.c
index eda779e..b257b36 100644
--- a/contrib/wpa/src/utils/wpabuf.c
+++ b/contrib/wpa/src/utils/wpabuf.c
@@ -1,15 +1,9 @@
/*
* Dynamic data buffer
- * Copyright (c) 2007-2009, Jouni Malinen <j@w1.fi>
+ * Copyright (c) 2007-2012, Jouni Malinen <j@w1.fi>
*
- * This program is free software; you can redistribute it and/or modify
- * it under the terms of the GNU General Public License version 2 as
- * published by the Free Software Foundation.
- *
- * Alternatively, this software may be distributed under the terms of BSD
- * license.
- *
- * See README and COPYING for more details.
+ * This software may be distributed under the terms of the BSD license.
+ * See README for more details.
*/
#include "includes.h"
@@ -74,12 +68,12 @@ int wpabuf_resize(struct wpabuf **_buf, size_t add_len)
if (buf->used + add_len > buf->size) {
unsigned char *nbuf;
- if (buf->ext_data) {
- nbuf = os_realloc(buf->ext_data, buf->used + add_len);
+ if (buf->flags & WPABUF_FLAG_EXT_DATA) {
+ nbuf = os_realloc(buf->buf, buf->used + add_len);
if (nbuf == NULL)
return -1;
os_memset(nbuf + buf->used, 0, add_len);
- buf->ext_data = nbuf;
+ buf->buf = nbuf;
} else {
#ifdef WPA_TRACE
nbuf = os_realloc(trace, sizeof(struct wpabuf_trace) +
@@ -101,6 +95,7 @@ int wpabuf_resize(struct wpabuf **_buf, size_t add_len)
os_memset(nbuf + sizeof(struct wpabuf) + buf->used, 0,
add_len);
#endif /* WPA_TRACE */
+ buf->buf = (u8 *) (buf + 1);
*_buf = buf;
}
buf->size = buf->used + add_len;
@@ -132,6 +127,7 @@ struct wpabuf * wpabuf_alloc(size_t len)
#endif /* WPA_TRACE */
buf->size = len;
+ buf->buf = (u8 *) (buf + 1);
return buf;
}
@@ -154,7 +150,8 @@ struct wpabuf * wpabuf_alloc_ext_data(u8 *data, size_t len)
buf->size = len;
buf->used = len;
- buf->ext_data = data;
+ buf->buf = data;
+ buf->flags |= WPABUF_FLAG_EXT_DATA;
return buf;
}
@@ -195,12 +192,14 @@ void wpabuf_free(struct wpabuf *buf)
wpa_trace_show("wpabuf_free magic mismatch");
abort();
}
- os_free(buf->ext_data);
+ if (buf->flags & WPABUF_FLAG_EXT_DATA)
+ os_free(buf->buf);
os_free(trace);
#else /* WPA_TRACE */
if (buf == NULL)
return;
- os_free(buf->ext_data);
+ if (buf->flags & WPABUF_FLAG_EXT_DATA)
+ os_free(buf->buf);
os_free(buf);
#endif /* WPA_TRACE */
}
diff --git a/contrib/wpa/src/utils/wpabuf.h b/contrib/wpa/src/utils/wpabuf.h
index a150455..dbce925 100644
--- a/contrib/wpa/src/utils/wpabuf.h
+++ b/contrib/wpa/src/utils/wpabuf.h
@@ -1,20 +1,17 @@
/*
* Dynamic data buffer
- * Copyright (c) 2007-2009, Jouni Malinen <j@w1.fi>
+ * Copyright (c) 2007-2012, Jouni Malinen <j@w1.fi>
*
- * This program is free software; you can redistribute it and/or modify
- * it under the terms of the GNU General Public License version 2 as
- * published by the Free Software Foundation.
- *
- * Alternatively, this software may be distributed under the terms of BSD
- * license.
- *
- * See README and COPYING for more details.
+ * This software may be distributed under the terms of the BSD license.
+ * See README for more details.
*/
#ifndef WPABUF_H
#define WPABUF_H
+/* wpabuf::buf is a pointer to external data */
+#define WPABUF_FLAG_EXT_DATA BIT(0)
+
/*
* Internal data structure for wpabuf. Please do not touch this directly from
* elsewhere. This is only defined in header file to allow inline functions
@@ -23,8 +20,8 @@
struct wpabuf {
size_t size; /* total size of the allocated buffer */
size_t used; /* length of data in the buffer */
- u8 *ext_data; /* pointer to external data; NULL if data follows
- * struct wpabuf */
+ u8 *buf; /* pointer to the head of the buffer */
+ unsigned int flags;
/* optionally followed by the allocated buffer */
};
@@ -78,9 +75,7 @@ static inline size_t wpabuf_tailroom(const struct wpabuf *buf)
*/
static inline const void * wpabuf_head(const struct wpabuf *buf)
{
- if (buf->ext_data)
- return buf->ext_data;
- return buf + 1;
+ return buf->buf;
}
static inline const u8 * wpabuf_head_u8(const struct wpabuf *buf)
@@ -95,9 +90,7 @@ static inline const u8 * wpabuf_head_u8(const struct wpabuf *buf)
*/
static inline void * wpabuf_mhead(struct wpabuf *buf)
{
- if (buf->ext_data)
- return buf->ext_data;
- return buf + 1;
+ return buf->buf;
}
static inline u8 * wpabuf_mhead_u8(struct wpabuf *buf)
@@ -117,6 +110,12 @@ static inline void wpabuf_put_le16(struct wpabuf *buf, u16 data)
WPA_PUT_LE16(pos, data);
}
+static inline void wpabuf_put_le32(struct wpabuf *buf, u32 data)
+{
+ u8 *pos = wpabuf_put(buf, 4);
+ WPA_PUT_LE32(pos, data);
+}
+
static inline void wpabuf_put_be16(struct wpabuf *buf, u16 data)
{
u8 *pos = wpabuf_put(buf, 2);
@@ -150,7 +149,8 @@ static inline void wpabuf_put_buf(struct wpabuf *dst,
static inline void wpabuf_set(struct wpabuf *buf, const void *data, size_t len)
{
- buf->ext_data = (u8 *) data;
+ buf->buf = (u8 *) data;
+ buf->flags = WPABUF_FLAG_EXT_DATA;
buf->size = buf->used = len;
}
diff --git a/contrib/wpa/src/wps/Makefile b/contrib/wpa/src/wps/Makefile
deleted file mode 100644
index 9c41962..0000000
--- a/contrib/wpa/src/wps/Makefile
+++ /dev/null
@@ -1,8 +0,0 @@
-all:
- @echo Nothing to be made.
-
-clean:
- rm -f *~ *.o *.d
-
-install:
- @echo Nothing to be made.
diff --git a/contrib/wpa/src/wps/http_client.c b/contrib/wpa/src/wps/http_client.c
index fea2a04..c6d6c7f 100644
--- a/contrib/wpa/src/wps/http_client.c
+++ b/contrib/wpa/src/wps/http_client.c
@@ -2,14 +2,8 @@
* http_client - HTTP client
* Copyright (c) 2009, Jouni Malinen <j@w1.fi>
*
- * This program is free software; you can redistribute it and/or modify
- * it under the terms of the GNU General Public License version 2 as
- * published by the Free Software Foundation.
- *
- * Alternatively, this software may be distributed under the terms of BSD
- * license.
- *
- * See README and COPYING for more details.
+ * This software may be distributed under the terms of the BSD license.
+ * See README for more details.
*/
#include "includes.h"
@@ -21,7 +15,7 @@
#include "http_client.h"
-#define HTTP_CLIENT_TIMEOUT 30
+#define HTTP_CLIENT_TIMEOUT_SEC 30
struct http_client {
@@ -42,7 +36,7 @@ struct http_client {
static void http_client_timeout(void *eloop_data, void *user_ctx)
{
struct http_client *c = eloop_data;
- wpa_printf(MSG_DEBUG, "HTTP: Timeout");
+ wpa_printf(MSG_DEBUG, "HTTP: Timeout (c=%p)", c);
c->cb(c->cb_ctx, c, HTTP_CLIENT_TIMEOUT);
}
@@ -52,6 +46,9 @@ static void http_client_got_response(struct httpread *handle, void *cookie,
{
struct http_client *c = cookie;
+ wpa_printf(MSG_DEBUG, "HTTP: httpread callback: handle=%p cookie=%p "
+ "e=%d", handle, cookie, e);
+
eloop_cancel_timeout(http_client_timeout, c, NULL);
switch (e) {
case HTTPREAD_EVENT_FILE_READY:
@@ -122,7 +119,7 @@ static void http_client_tx_ready(int sock, void *eloop_ctx, void *sock_ctx)
c->req = NULL;
c->hread = httpread_create(c->sd, http_client_got_response, c,
- c->max_response, HTTP_CLIENT_TIMEOUT);
+ c->max_response, HTTP_CLIENT_TIMEOUT_SEC);
if (c->hread == NULL) {
c->cb(c->cb_ctx, c, HTTP_CLIENT_FAILED);
return;
@@ -181,8 +178,8 @@ struct http_client * http_client_addr(struct sockaddr_in *dst,
return NULL;
}
- if (eloop_register_timeout(HTTP_CLIENT_TIMEOUT, 0, http_client_timeout,
- c, NULL)) {
+ if (eloop_register_timeout(HTTP_CLIENT_TIMEOUT_SEC, 0,
+ http_client_timeout, c, NULL)) {
http_client_free(c);
return NULL;
}
diff --git a/contrib/wpa/src/wps/http_client.h b/contrib/wpa/src/wps/http_client.h
index 924d6ab..ddee2ad 100644
--- a/contrib/wpa/src/wps/http_client.h
+++ b/contrib/wpa/src/wps/http_client.h
@@ -2,14 +2,8 @@
* http_client - HTTP client
* Copyright (c) 2009, Jouni Malinen <j@w1.fi>
*
- * This program is free software; you can redistribute it and/or modify
- * it under the terms of the GNU General Public License version 2 as
- * published by the Free Software Foundation.
- *
- * Alternatively, this software may be distributed under the terms of BSD
- * license.
- *
- * See README and COPYING for more details.
+ * This software may be distributed under the terms of the BSD license.
+ * See README for more details.
*/
#ifndef HTTP_CLIENT_H
diff --git a/contrib/wpa/src/wps/http_server.c b/contrib/wpa/src/wps/http_server.c
index 356f599..6ca3214 100644
--- a/contrib/wpa/src/wps/http_server.c
+++ b/contrib/wpa/src/wps/http_server.c
@@ -2,14 +2,8 @@
* http_server - HTTP server
* Copyright (c) 2009, Jouni Malinen <j@w1.fi>
*
- * This program is free software; you can redistribute it and/or modify
- * it under the terms of the GNU General Public License version 2 as
- * published by the Free Software Foundation.
- *
- * Alternatively, this software may be distributed under the terms of BSD
- * license.
- *
- * See README and COPYING for more details.
+ * This software may be distributed under the terms of the BSD license.
+ * See README for more details.
*/
#include "includes.h"
diff --git a/contrib/wpa/src/wps/http_server.h b/contrib/wpa/src/wps/http_server.h
index 219941c..4b2b749 100644
--- a/contrib/wpa/src/wps/http_server.h
+++ b/contrib/wpa/src/wps/http_server.h
@@ -2,14 +2,8 @@
* http_server - HTTP server
* Copyright (c) 2009, Jouni Malinen <j@w1.fi>
*
- * This program is free software; you can redistribute it and/or modify
- * it under the terms of the GNU General Public License version 2 as
- * published by the Free Software Foundation.
- *
- * Alternatively, this software may be distributed under the terms of BSD
- * license.
- *
- * See README and COPYING for more details.
+ * This software may be distributed under the terms of the BSD license.
+ * See README for more details.
*/
#ifndef HTTP_SERVER_H
diff --git a/contrib/wpa/src/wps/httpread.c b/contrib/wpa/src/wps/httpread.c
index 40422e4..ad4f4a1 100644
--- a/contrib/wpa/src/wps/httpread.c
+++ b/contrib/wpa/src/wps/httpread.c
@@ -3,14 +3,8 @@
* Author: Ted Merrill
* Copyright 2008 Atheros Communications
*
- * This program is free software; you can redistribute it and/or modify
- * it under the terms of the GNU General Public License version 2 as
- * published by the Free Software Foundation.
- *
- * Alternatively, this software may be distributed under the terms of BSD
- * license.
- *
- * See README and COPYING for more details.
+ * This software may be distributed under the terms of the BSD license.
+ * See README for more details.
*
* The files are buffered via internal callbacks from eloop, then presented to
* an application callback routine when completely read into memory. May also
diff --git a/contrib/wpa/src/wps/httpread.h b/contrib/wpa/src/wps/httpread.h
index 51aa214..454b618 100644
--- a/contrib/wpa/src/wps/httpread.h
+++ b/contrib/wpa/src/wps/httpread.h
@@ -3,14 +3,8 @@
* Author: Ted Merrill
* Copyright 2008 Atheros Communications
*
- * This program is free software; you can redistribute it and/or modify
- * it under the terms of the GNU General Public License version 2 as
- * published by the Free Software Foundation.
- *
- * Alternatively, this software may be distributed under the terms of BSD
- * license.
- *
- * See README and COPYING for more details.
+ * This software may be distributed under the terms of the BSD license.
+ * See README for more details.
*/
#ifndef HTTPREAD_H
diff --git a/contrib/wpa/src/wps/ndef.c b/contrib/wpa/src/wps/ndef.c
index 9baec7f..a48a2d7 100644
--- a/contrib/wpa/src/wps/ndef.c
+++ b/contrib/wpa/src/wps/ndef.c
@@ -1,34 +1,28 @@
/*
* NDEF(NFC Data Exchange Format) routines for Wi-Fi Protected Setup
* Reference is "NFCForum-TS-NDEF_1.0 2006-07-24".
- * Copyright (c) 2009, Masashi Honma <honma@ictec.co.jp>
+ * Copyright (c) 2009-2012, Masashi Honma <masashi.honma@gmail.com>
*
- * This program is free software; you can redistribute it and/or modify
- * it under the terms of the GNU General Public License version 2 as
- * published by the Free Software Foundation.
- *
- * Alternatively, this software may be distributed under the terms of BSD
- * license.
- *
- * See README and COPYING for more details.
+ * This software may be distributed under the terms of the BSD license.
+ * See README for more details.
*/
#include "includes.h"
#include "common.h"
#include "wps/wps.h"
-#include "wps/wps_i.h"
#define FLAG_MESSAGE_BEGIN (1 << 7)
#define FLAG_MESSAGE_END (1 << 6)
#define FLAG_CHUNK (1 << 5)
#define FLAG_SHORT_RECORD (1 << 4)
#define FLAG_ID_LENGTH_PRESENT (1 << 3)
+#define FLAG_TNF_NFC_FORUM (0x01)
#define FLAG_TNF_RFC2046 (0x02)
struct ndef_record {
- u8 *type;
- u8 *id;
- u8 *payload;
+ const u8 *type;
+ const u8 *id;
+ const u8 *payload;
u8 type_length;
u8 id_length;
u32 payload_length;
@@ -37,9 +31,10 @@ struct ndef_record {
static char wifi_handover_type[] = "application/vnd.wfa.wsc";
-static int ndef_parse_record(u8 *data, u32 size, struct ndef_record *record)
+static int ndef_parse_record(const u8 *data, u32 size,
+ struct ndef_record *record)
{
- u8 *pos = data + 1;
+ const u8 *pos = data + 1;
if (size < 2)
return -1;
@@ -78,12 +73,12 @@ static int ndef_parse_record(u8 *data, u32 size, struct ndef_record *record)
}
-static struct wpabuf * ndef_parse_records(struct wpabuf *buf,
+static struct wpabuf * ndef_parse_records(const struct wpabuf *buf,
int (*filter)(struct ndef_record *))
{
struct ndef_record record;
int len = wpabuf_len(buf);
- u8 *data = wpabuf_mhead(buf);
+ const u8 *data = wpabuf_head(buf);
while (len > 0) {
if (ndef_parse_record(data, len, &record) < 0) {
@@ -103,13 +98,14 @@ static struct wpabuf * ndef_parse_records(struct wpabuf *buf,
static struct wpabuf * ndef_build_record(u8 flags, void *type,
u8 type_length, void *id,
- u8 id_length, void *payload,
- u32 payload_length)
+ u8 id_length,
+ const struct wpabuf *payload)
{
struct wpabuf *record;
size_t total_len;
int short_record;
u8 local_flag;
+ size_t payload_length = wpabuf_len(payload);
short_record = payload_length < 256 ? 1 : 0;
@@ -144,7 +140,7 @@ static struct wpabuf * ndef_build_record(u8 flags, void *type,
wpabuf_put_u8(record, id_length);
wpabuf_put_data(record, type, type_length);
wpabuf_put_data(record, id, id_length);
- wpabuf_put_data(record, payload, payload_length);
+ wpabuf_put_buf(record, payload);
return record;
}
@@ -160,16 +156,90 @@ static int wifi_filter(struct ndef_record *record)
}
-struct wpabuf * ndef_parse_wifi(struct wpabuf *buf)
+struct wpabuf * ndef_parse_wifi(const struct wpabuf *buf)
{
return ndef_parse_records(buf, wifi_filter);
}
-struct wpabuf * ndef_build_wifi(struct wpabuf *buf)
+struct wpabuf * ndef_build_wifi(const struct wpabuf *buf)
{
return ndef_build_record(FLAG_MESSAGE_BEGIN | FLAG_MESSAGE_END |
FLAG_TNF_RFC2046, wifi_handover_type,
- os_strlen(wifi_handover_type), NULL, 0,
- wpabuf_mhead(buf), wpabuf_len(buf));
+ os_strlen(wifi_handover_type), NULL, 0, buf);
+}
+
+
+struct wpabuf * ndef_build_wifi_hr(void)
+{
+ struct wpabuf *rn, *cr, *ac_payload, *ac, *hr_payload, *hr;
+ struct wpabuf *carrier, *hc;
+
+ rn = wpabuf_alloc(2);
+ if (rn == NULL)
+ return NULL;
+ wpabuf_put_be16(rn, os_random() & 0xffff);
+
+ cr = ndef_build_record(FLAG_MESSAGE_BEGIN | FLAG_TNF_NFC_FORUM, "cr", 2,
+ NULL, 0, rn);
+ wpabuf_free(rn);
+
+ if (cr == NULL)
+ return NULL;
+
+ ac_payload = wpabuf_alloc(4);
+ if (ac_payload == NULL) {
+ wpabuf_free(cr);
+ return NULL;
+ }
+ wpabuf_put_u8(ac_payload, 0x01); /* Carrier Flags: CRS=1 "active" */
+ wpabuf_put_u8(ac_payload, 0x01); /* Carrier Data Reference Length */
+ wpabuf_put_u8(ac_payload, '0'); /* Carrier Data Reference: "0" */
+ wpabuf_put_u8(ac_payload, 0); /* Aux Data Reference Count */
+
+ ac = ndef_build_record(FLAG_MESSAGE_END | FLAG_TNF_NFC_FORUM, "ac", 2,
+ NULL, 0, ac_payload);
+ wpabuf_free(ac_payload);
+ if (ac == NULL) {
+ wpabuf_free(cr);
+ return NULL;
+ }
+
+ hr_payload = wpabuf_alloc(1 + wpabuf_len(cr) + wpabuf_len(ac));
+ if (hr_payload == NULL) {
+ wpabuf_free(cr);
+ wpabuf_free(ac);
+ return NULL;
+ }
+
+ wpabuf_put_u8(hr_payload, 0x12); /* Connection Handover Version 1.2 */
+ wpabuf_put_buf(hr_payload, cr);
+ wpabuf_put_buf(hr_payload, ac);
+ wpabuf_free(cr);
+ wpabuf_free(ac);
+
+ hr = ndef_build_record(FLAG_MESSAGE_BEGIN | FLAG_TNF_NFC_FORUM, "Hr", 2,
+ NULL, 0, hr_payload);
+ wpabuf_free(hr_payload);
+ if (hr == NULL)
+ return NULL;
+
+ carrier = wpabuf_alloc(2 + os_strlen(wifi_handover_type));
+ if (carrier == NULL) {
+ wpabuf_free(hr);
+ return NULL;
+ }
+ wpabuf_put_u8(carrier, 0x02); /* Carrier Type Format */
+ wpabuf_put_u8(carrier, os_strlen(wifi_handover_type));
+ wpabuf_put_str(carrier, wifi_handover_type);
+
+ hc = ndef_build_record(FLAG_MESSAGE_END | FLAG_TNF_NFC_FORUM, "Hc", 2,
+ "0", 1, carrier);
+ wpabuf_free(carrier);
+ if (hc == NULL) {
+ wpabuf_free(hr);
+ return NULL;
+ }
+
+ return wpabuf_concat(hr, hc);
}
diff --git a/contrib/wpa/src/wps/upnp_xml.c b/contrib/wpa/src/wps/upnp_xml.c
index b1b1e2b..a9958ee 100644
--- a/contrib/wpa/src/wps/upnp_xml.c
+++ b/contrib/wpa/src/wps/upnp_xml.c
@@ -75,8 +75,8 @@
* Note that angle brackets present in the original data must have been encoded
* as &lt; and &gt; so they will not trouble us.
*/
-static int xml_next_tag(const char *in, const char **out,
- const char **out_tagname, const char **end)
+int xml_next_tag(const char *in, const char **out,
+ const char **out_tagname, const char **end)
{
while (*in && *in != '<')
in++;
diff --git a/contrib/wpa/src/wps/upnp_xml.h b/contrib/wpa/src/wps/upnp_xml.h
index 62dbe60..616af3d 100644
--- a/contrib/wpa/src/wps/upnp_xml.h
+++ b/contrib/wpa/src/wps/upnp_xml.h
@@ -16,6 +16,8 @@
void xml_data_encode(struct wpabuf *buf, const char *data, int len);
void xml_add_tagged_data(struct wpabuf *buf, const char *tag,
const char *data);
+int xml_next_tag(const char *in, const char **out,
+ const char **out_tagname, const char **end);
char * xml_get_first_item(const char *doc, const char *item);
struct wpabuf * xml_get_base64_item(const char *data, const char *name,
enum http_reply_code *ret);
diff --git a/contrib/wpa/src/wps/wps.c b/contrib/wpa/src/wps/wps.c
index 619af15..2575705 100644
--- a/contrib/wpa/src/wps/wps.c
+++ b/contrib/wpa/src/wps/wps.c
@@ -2,14 +2,8 @@
* Wi-Fi Protected Setup
* Copyright (c) 2007-2009, Jouni Malinen <j@w1.fi>
*
- * This program is free software; you can redistribute it and/or modify
- * it under the terms of the GNU General Public License version 2 as
- * published by the Free Software Foundation.
- *
- * Alternatively, this software may be distributed under the terms of BSD
- * license.
- *
- * See README and COPYING for more details.
+ * This software may be distributed under the terms of the BSD license.
+ * See README for more details.
*/
#include "includes.h"
@@ -21,6 +15,12 @@
#include "wps_dev_attr.h"
+#ifdef CONFIG_WPS_TESTING
+int wps_version_number = 0x20;
+int wps_testing_dummy_cred = 0;
+#endif /* CONFIG_WPS_TESTING */
+
+
/**
* wps_init - Initialize WPS Registration protocol data
* @cfg: WPS configuration
@@ -45,8 +45,7 @@ struct wps_data * wps_init(const struct wps_config *cfg)
os_memcpy(data->uuid_e, cfg->wps->uuid, WPS_UUID_LEN);
}
if (cfg->pin) {
- data->dev_pw_id = data->wps->oob_dev_pw_id == 0 ?
- DEV_PW_DEFAULT : data->wps->oob_dev_pw_id;
+ data->dev_pw_id = cfg->dev_pw_id;
data->dev_password = os_malloc(cfg->pin_len);
if (data->dev_password == NULL) {
os_free(data);
@@ -56,17 +55,33 @@ struct wps_data * wps_init(const struct wps_config *cfg)
data->dev_password_len = cfg->pin_len;
}
+#ifdef CONFIG_WPS_NFC
+ if (cfg->wps->ap && !cfg->registrar && cfg->wps->ap_nfc_dev_pw_id) {
+ data->dev_pw_id = cfg->wps->ap_nfc_dev_pw_id;
+ os_free(data->dev_password);
+ data->dev_password =
+ os_malloc(wpabuf_len(cfg->wps->ap_nfc_dev_pw));
+ if (data->dev_password == NULL) {
+ os_free(data);
+ return NULL;
+ }
+ os_memcpy(data->dev_password,
+ wpabuf_head(cfg->wps->ap_nfc_dev_pw),
+ wpabuf_len(cfg->wps->ap_nfc_dev_pw));
+ data->dev_password_len = wpabuf_len(cfg->wps->ap_nfc_dev_pw);
+ }
+#endif /* CONFIG_WPS_NFC */
+
data->pbc = cfg->pbc;
if (cfg->pbc) {
/* Use special PIN '00000000' for PBC */
data->dev_pw_id = DEV_PW_PUSHBUTTON;
os_free(data->dev_password);
- data->dev_password = os_malloc(8);
+ data->dev_password = (u8 *) os_strdup("00000000");
if (data->dev_password == NULL) {
os_free(data);
return NULL;
}
- os_memset(data->dev_password, '0', 8);
data->dev_password_len = 8;
}
@@ -94,6 +109,7 @@ struct wps_data * wps_init(const struct wps_config *cfg)
data->new_ap_settings =
os_malloc(sizeof(*data->new_ap_settings));
if (data->new_ap_settings == NULL) {
+ os_free(data->dev_password);
os_free(data);
return NULL;
}
@@ -103,8 +119,11 @@ struct wps_data * wps_init(const struct wps_config *cfg)
if (cfg->peer_addr)
os_memcpy(data->peer_dev.mac_addr, cfg->peer_addr, ETH_ALEN);
+ if (cfg->p2p_dev_addr)
+ os_memcpy(data->p2p_dev_addr, cfg->p2p_dev_addr, ETH_ALEN);
data->use_psk_key = cfg->use_psk_key;
+ data->pbc_in_m1 = cfg->pbc_in_m1;
return data;
}
@@ -116,6 +135,12 @@ struct wps_data * wps_init(const struct wps_config *cfg)
*/
void wps_deinit(struct wps_data *data)
{
+#ifdef CONFIG_WPS_NFC
+ if (data->registrar && data->nfc_pw_token)
+ wps_registrar_remove_nfc_pw_token(data->wps->registrar,
+ data->nfc_pw_token);
+#endif /* CONFIG_WPS_NFC */
+
if (data->wps_pin_revealed) {
wpa_printf(MSG_DEBUG, "WPS: Full PIN information revealed and "
"negotiation failed");
@@ -134,6 +159,7 @@ void wps_deinit(struct wps_data *data)
wps_device_data_free(&data->peer_dev);
os_free(data->new_ap_settings);
dh5_free(data->dh_ctx);
+ os_free(data->nfc_pw_token);
os_free(data);
}
@@ -201,19 +227,19 @@ int wps_is_selected_pbc_registrar(const struct wpabuf *msg)
WPA_GET_BE16(attr.dev_password_id) != DEV_PW_PUSHBUTTON)
return 0;
+#ifdef CONFIG_WPS_STRICT
+ if (!attr.sel_reg_config_methods ||
+ !(WPA_GET_BE16(attr.sel_reg_config_methods) &
+ WPS_CONFIG_PUSHBUTTON))
+ return 0;
+#endif /* CONFIG_WPS_STRICT */
+
return 1;
}
-/**
- * wps_is_selected_pin_registrar - Check whether WPS IE indicates active PIN
- * @msg: WPS IE contents from Beacon or Probe Response frame
- * Returns: 1 if PIN Registrar is active, 0 if not
- */
-int wps_is_selected_pin_registrar(const struct wpabuf *msg)
+static int is_selected_pin_registrar(struct wps_parse_attr *attr)
{
- struct wps_parse_attr attr;
-
/*
* In theory, this could also verify that attr.sel_reg_config_methods
* includes WPS_CONFIG_LABEL, WPS_CONFIG_DISPLAY, or WPS_CONFIG_KEYPAD,
@@ -222,21 +248,114 @@ int wps_is_selected_pin_registrar(const struct wpabuf *msg)
* Device Password ID here.
*/
- if (wps_parse_msg(msg, &attr) < 0)
+ if (!attr->selected_registrar || *attr->selected_registrar == 0)
return 0;
- if (!attr.selected_registrar || *attr.selected_registrar == 0)
+ if (attr->dev_password_id != NULL &&
+ WPA_GET_BE16(attr->dev_password_id) == DEV_PW_PUSHBUTTON)
return 0;
- if (attr.dev_password_id != NULL &&
- WPA_GET_BE16(attr.dev_password_id) == DEV_PW_PUSHBUTTON)
+#ifdef CONFIG_WPS_STRICT
+ if (!attr->sel_reg_config_methods ||
+ !(WPA_GET_BE16(attr->sel_reg_config_methods) &
+ (WPS_CONFIG_LABEL | WPS_CONFIG_DISPLAY | WPS_CONFIG_KEYPAD)))
return 0;
+#endif /* CONFIG_WPS_STRICT */
return 1;
}
/**
+ * wps_is_selected_pin_registrar - Check whether WPS IE indicates active PIN
+ * @msg: WPS IE contents from Beacon or Probe Response frame
+ * Returns: 1 if PIN Registrar is active, 0 if not
+ */
+int wps_is_selected_pin_registrar(const struct wpabuf *msg)
+{
+ struct wps_parse_attr attr;
+
+ if (wps_parse_msg(msg, &attr) < 0)
+ return 0;
+
+ return is_selected_pin_registrar(&attr);
+}
+
+
+/**
+ * wps_is_addr_authorized - Check whether WPS IE authorizes MAC address
+ * @msg: WPS IE contents from Beacon or Probe Response frame
+ * @addr: MAC address to search for
+ * @ver1_compat: Whether to use version 1 compatibility mode
+ * Returns: 2 if the specified address is explicit authorized, 1 if address is
+ * authorized (broadcast), 0 if not
+ */
+int wps_is_addr_authorized(const struct wpabuf *msg, const u8 *addr,
+ int ver1_compat)
+{
+ struct wps_parse_attr attr;
+ unsigned int i;
+ const u8 *pos;
+ const u8 bcast[ETH_ALEN] = { 0xff, 0xff, 0xff, 0xff, 0xff, 0xff };
+
+ if (wps_parse_msg(msg, &attr) < 0)
+ return 0;
+
+ if (!attr.version2 && ver1_compat) {
+ /*
+ * Version 1.0 AP - AuthorizedMACs not used, so revert back to
+ * old mechanism of using SelectedRegistrar.
+ */
+ return is_selected_pin_registrar(&attr);
+ }
+
+ if (!attr.authorized_macs)
+ return 0;
+
+ pos = attr.authorized_macs;
+ for (i = 0; i < attr.authorized_macs_len / ETH_ALEN; i++) {
+ if (os_memcmp(pos, addr, ETH_ALEN) == 0)
+ return 2;
+ if (os_memcmp(pos, bcast, ETH_ALEN) == 0)
+ return 1;
+ pos += ETH_ALEN;
+ }
+
+ return 0;
+}
+
+
+/**
+ * wps_ap_priority_compar - Prioritize WPS IE from two APs
+ * @wps_a: WPS IE contents from Beacon or Probe Response frame
+ * @wps_b: WPS IE contents from Beacon or Probe Response frame
+ * Returns: 1 if wps_b is considered more likely selection for WPS
+ * provisioning, -1 if wps_a is considered more like, or 0 if no preference
+ */
+int wps_ap_priority_compar(const struct wpabuf *wps_a,
+ const struct wpabuf *wps_b)
+{
+ struct wps_parse_attr attr_a, attr_b;
+ int sel_a, sel_b;
+
+ if (wps_a == NULL || wps_parse_msg(wps_a, &attr_a) < 0)
+ return 1;
+ if (wps_b == NULL || wps_parse_msg(wps_b, &attr_b) < 0)
+ return -1;
+
+ sel_a = attr_a.selected_registrar && *attr_a.selected_registrar != 0;
+ sel_b = attr_b.selected_registrar && *attr_b.selected_registrar != 0;
+
+ if (sel_a && !sel_b)
+ return -1;
+ if (!sel_a && sel_b)
+ return 1;
+
+ return 0;
+}
+
+
+/**
* wps_get_uuid_e - Get UUID-E from WPS IE
* @msg: WPS IE contents from Beacon or Probe Response frame
* Returns: Pointer to UUID-E or %NULL if not included
@@ -255,6 +374,19 @@ const u8 * wps_get_uuid_e(const struct wpabuf *msg)
/**
+ * wps_is_20 - Check whether WPS attributes claim support for WPS 2.0
+ */
+int wps_is_20(const struct wpabuf *msg)
+{
+ struct wps_parse_attr attr;
+
+ if (msg == NULL || wps_parse_msg(msg, &attr) < 0)
+ return 0;
+ return attr.version2 != NULL;
+}
+
+
+/**
* wps_build_assoc_req_ie - Build WPS IE for (Re)Association Request
* @req_type: Value for Request Type attribute
* Returns: WPS IE or %NULL on failure
@@ -277,7 +409,8 @@ struct wpabuf * wps_build_assoc_req_ie(enum wps_request_type req_type)
wpabuf_put_be32(ie, WPS_DEV_OUI_WFA);
if (wps_build_version(ie) ||
- wps_build_req_type(ie, req_type)) {
+ wps_build_req_type(ie, req_type) ||
+ wps_build_wfa_ext(ie, 0, NULL, 0)) {
wpabuf_free(ie);
return NULL;
}
@@ -310,7 +443,8 @@ struct wpabuf * wps_build_assoc_resp_ie(void)
wpabuf_put_be32(ie, WPS_DEV_OUI_WFA);
if (wps_build_version(ie) ||
- wps_build_resp_type(ie, WPS_RESP_AP)) {
+ wps_build_resp_type(ie, WPS_RESP_AP) ||
+ wps_build_wfa_ext(ie, 0, NULL, 0)) {
wpabuf_free(ie);
return NULL;
}
@@ -323,62 +457,64 @@ struct wpabuf * wps_build_assoc_resp_ie(void)
/**
* wps_build_probe_req_ie - Build WPS IE for Probe Request
- * @pbc: Whether searching for PBC mode APs
+ * @pw_id: Password ID (DEV_PW_PUSHBUTTON for active PBC and DEV_PW_DEFAULT for
+ * most other use cases)
* @dev: Device attributes
* @uuid: Own UUID
* @req_type: Value for Request Type attribute
+ * @num_req_dev_types: Number of requested device types
+ * @req_dev_types: Requested device types (8 * num_req_dev_types octets) or
+ * %NULL if none
* Returns: WPS IE or %NULL on failure
*
* The caller is responsible for freeing the buffer.
*/
-struct wpabuf * wps_build_probe_req_ie(int pbc, struct wps_device_data *dev,
+struct wpabuf * wps_build_probe_req_ie(u16 pw_id, struct wps_device_data *dev,
const u8 *uuid,
- enum wps_request_type req_type)
+ enum wps_request_type req_type,
+ unsigned int num_req_dev_types,
+ const u8 *req_dev_types)
{
struct wpabuf *ie;
- u8 *len;
- u16 methods;
wpa_printf(MSG_DEBUG, "WPS: Building WPS IE for Probe Request");
- ie = wpabuf_alloc(200);
+ ie = wpabuf_alloc(500);
if (ie == NULL)
return NULL;
- wpabuf_put_u8(ie, WLAN_EID_VENDOR_SPECIFIC);
- len = wpabuf_put(ie, 1);
- wpabuf_put_be32(ie, WPS_DEV_OUI_WFA);
-
- if (pbc)
- methods = WPS_CONFIG_PUSHBUTTON;
- else {
- methods = WPS_CONFIG_LABEL | WPS_CONFIG_DISPLAY |
- WPS_CONFIG_KEYPAD;
-#ifdef CONFIG_WPS_UFD
- methods |= WPS_CONFIG_USBA;
-#endif /* CONFIG_WPS_UFD */
-#ifdef CONFIG_WPS_NFC
- methods |= WPS_CONFIG_NFC_INTERFACE;
-#endif /* CONFIG_WPS_NFC */
- }
-
if (wps_build_version(ie) ||
wps_build_req_type(ie, req_type) ||
- wps_build_config_methods(ie, methods) ||
+ wps_build_config_methods(ie, dev->config_methods) ||
wps_build_uuid_e(ie, uuid) ||
wps_build_primary_dev_type(dev, ie) ||
wps_build_rf_bands(dev, ie) ||
wps_build_assoc_state(NULL, ie) ||
wps_build_config_error(ie, WPS_CFG_NO_ERROR) ||
- wps_build_dev_password_id(ie, pbc ? DEV_PW_PUSHBUTTON :
- DEV_PW_DEFAULT)) {
+ wps_build_dev_password_id(ie, pw_id) ||
+#ifdef CONFIG_WPS2
+ wps_build_manufacturer(dev, ie) ||
+ wps_build_model_name(dev, ie) ||
+ wps_build_model_number(dev, ie) ||
+ wps_build_dev_name(dev, ie) ||
+ wps_build_wfa_ext(ie, req_type == WPS_REQ_ENROLLEE, NULL, 0) ||
+#endif /* CONFIG_WPS2 */
+ wps_build_req_dev_type(dev, ie, num_req_dev_types, req_dev_types)
+ ||
+ wps_build_secondary_dev_type(dev, ie)
+ ) {
wpabuf_free(ie);
return NULL;
}
- *len = wpabuf_len(ie) - 2;
+#ifndef CONFIG_WPS2
+ if (dev->p2p && wps_build_dev_name(dev, ie)) {
+ wpabuf_free(ie);
+ return NULL;
+ }
+#endif /* CONFIG_WPS2 */
- return ie;
+ return wps_ie_encapsulate(ie);
}
diff --git a/contrib/wpa/src/wps/wps.h b/contrib/wpa/src/wps/wps.h
index 1fd1e52..c6b7099 100644
--- a/contrib/wpa/src/wps/wps.h
+++ b/contrib/wpa/src/wps/wps.h
@@ -1,15 +1,9 @@
/*
* Wi-Fi Protected Setup
- * Copyright (c) 2007-2009, Jouni Malinen <j@w1.fi>
+ * Copyright (c) 2007-2012, Jouni Malinen <j@w1.fi>
*
- * This program is free software; you can redistribute it and/or modify
- * it under the terms of the GNU General Public License version 2 as
- * published by the Free Software Foundation.
- *
- * Alternatively, this software may be distributed under the terms of BSD
- * license.
- *
- * See README and COPYING for more details.
+ * This software may be distributed under the terms of the BSD license.
+ * See README for more details.
*/
#ifndef WPS_H
@@ -33,6 +27,7 @@ enum wsc_op_code {
struct wps_registrar;
struct upnp_wps_device_sm;
struct wps_er;
+struct wps_parse_attr;
/**
* struct wps_credential - WPS Credential
@@ -47,6 +42,7 @@ struct wps_er;
* @cred_attr: Unparsed Credential attribute data (used only in cred_cb());
* this may be %NULL, if not used
* @cred_attr_len: Length of cred_attr in octets
+ * @ap_channel: AP channel
*/
struct wps_credential {
u8 ssid[32];
@@ -59,10 +55,18 @@ struct wps_credential {
u8 mac_addr[ETH_ALEN];
const u8 *cred_attr;
size_t cred_attr_len;
+ u16 ap_channel;
};
#define WPS_DEV_TYPE_LEN 8
#define WPS_DEV_TYPE_BUFSIZE 21
+#define WPS_SEC_DEV_TYPE_MAX_LEN 128
+/* maximum number of advertised WPS vendor extension attributes */
+#define MAX_WPS_VENDOR_EXTENSIONS 10
+/* maximum size of WPS Vendor extension attribute */
+#define WPS_MAX_VENDOR_EXT_LEN 1024
+/* maximum number of parsed WPS vendor extension attributes */
+#define MAX_WPS_PARSE_VENDOR_EXT 10
/**
* struct wps_device_data - WPS Device Data
@@ -73,8 +77,11 @@ struct wps_credential {
* @model_number: Model Number (0..32 octets encoded in UTF-8)
* @serial_number: Serial Number (0..32 octets encoded in UTF-8)
* @pri_dev_type: Primary Device Type
+ * @sec_dev_type: Array of secondary device types
+ * @num_sec_dev_type: Number of secondary device types
* @os_version: OS Version
* @rf_bands: RF bands (WPS_RF_24GHZ, WPS_RF_50GHZ flags)
+ * @p2p: Whether the device is a P2P device
*/
struct wps_device_data {
u8 mac_addr[ETH_ALEN];
@@ -84,19 +91,16 @@ struct wps_device_data {
char *model_number;
char *serial_number;
u8 pri_dev_type[WPS_DEV_TYPE_LEN];
+#define WPS_SEC_DEVICE_TYPES 5
+ u8 sec_dev_type[WPS_SEC_DEVICE_TYPES][WPS_DEV_TYPE_LEN];
+ u8 num_sec_dev_types;
u32 os_version;
u8 rf_bands;
-};
+ u16 config_methods;
+ struct wpabuf *vendor_ext_m1;
+ struct wpabuf *vendor_ext[MAX_WPS_VENDOR_EXTENSIONS];
-struct oob_conf_data {
- enum {
- OOB_METHOD_UNKNOWN = 0,
- OOB_METHOD_DEV_PWD_E,
- OOB_METHOD_DEV_PWD_R,
- OOB_METHOD_CRED,
- } oob_method;
- struct wpabuf *dev_password;
- struct wpabuf *pubkey_hash;
+ int p2p;
};
/**
@@ -156,6 +160,29 @@ struct wps_config {
* struct wpa_context::psk.
*/
int use_psk_key;
+
+ /**
+ * dev_pw_id - Device Password ID for Enrollee when PIN is used
+ */
+ u16 dev_pw_id;
+
+ /**
+ * p2p_dev_addr - P2P Device Address from (Re)Association Request
+ *
+ * On AP/GO, this is set to the P2P Device Address of the associating
+ * P2P client if a P2P IE is included in the (Re)Association Request
+ * frame and the P2P Device Address is included. Otherwise, this is set
+ * to %NULL to indicate the station does not have a P2P Device Address.
+ */
+ const u8 *p2p_dev_addr;
+
+ /**
+ * pbc_in_m1 - Do not remove PushButton config method in M1 (AP)
+ *
+ * This can be used to enable a workaround to allow Windows 7 to use
+ * PBC with the AP.
+ */
+ int pbc_in_m1;
};
struct wps_data * wps_init(const struct wps_config *cfg);
@@ -195,13 +222,20 @@ struct wpabuf * wps_get_msg(struct wps_data *wps, enum wsc_op_code *op_code);
int wps_is_selected_pbc_registrar(const struct wpabuf *msg);
int wps_is_selected_pin_registrar(const struct wpabuf *msg);
+int wps_ap_priority_compar(const struct wpabuf *wps_a,
+ const struct wpabuf *wps_b);
+int wps_is_addr_authorized(const struct wpabuf *msg, const u8 *addr,
+ int ver1_compat);
const u8 * wps_get_uuid_e(const struct wpabuf *msg);
+int wps_is_20(const struct wpabuf *msg);
struct wpabuf * wps_build_assoc_req_ie(enum wps_request_type req_type);
struct wpabuf * wps_build_assoc_resp_ie(void);
-struct wpabuf * wps_build_probe_req_ie(int pbc, struct wps_device_data *dev,
+struct wpabuf * wps_build_probe_req_ie(u16 pw_id, struct wps_device_data *dev,
const u8 *uuid,
- enum wps_request_type req_type);
+ enum wps_request_type req_type,
+ unsigned int num_req_dev_types,
+ const u8 *req_dev_types);
/**
@@ -253,12 +287,15 @@ struct wps_registrar_config {
* @ctx: Higher layer context data (cb_ctx)
* @mac_addr: MAC address of the Enrollee
* @uuid_e: UUID-E of the Enrollee
+ * @dev_pw: Device Password (PIN) used during registration
+ * @dev_pw_len: Length of dev_pw in octets
*
* This callback is called whenever an Enrollee completes registration
* successfully.
*/
void (*reg_success_cb)(void *ctx, const u8 *mac_addr,
- const u8 *uuid_e);
+ const u8 *uuid_e, const u8 *dev_pw,
+ size_t dev_pw_len);
/**
* set_sel_reg_cb - Callback for reporting selected registrar changes
@@ -340,6 +377,11 @@ struct wps_registrar_config {
* static_wep_only - Whether the BSS supports only static WEP
*/
int static_wep_only;
+
+ /**
+ * dualband - Whether this is a concurrent dualband AP
+ */
+ int dualband;
};
@@ -395,7 +437,22 @@ enum wps_event {
/**
* WPS_EV_ER_ENROLLEE_REMOVE - ER: Enrollee removed
*/
- WPS_EV_ER_ENROLLEE_REMOVE
+ WPS_EV_ER_ENROLLEE_REMOVE,
+
+ /**
+ * WPS_EV_ER_AP_SETTINGS - ER: AP Settings learned
+ */
+ WPS_EV_ER_AP_SETTINGS,
+
+ /**
+ * WPS_EV_ER_SET_SELECTED_REGISTRAR - ER: SetSelectedRegistrar event
+ */
+ WPS_EV_ER_SET_SELECTED_REGISTRAR,
+
+ /**
+ * WPS_EV_AP_PIN_SUCCESS - External Registrar used correct AP PIN
+ */
+ WPS_EV_AP_PIN_SUCCESS
};
/**
@@ -428,6 +485,8 @@ union wps_event_data {
*/
struct wps_event_fail {
int msg;
+ u16 config_error;
+ u16 error_indication;
} fail;
struct wps_event_pwd_auth_fail {
@@ -464,6 +523,23 @@ union wps_event_data {
const char *model_number;
const char *serial_number;
} enrollee;
+
+ struct wps_event_er_ap_settings {
+ const u8 *uuid;
+ const struct wps_credential *cred;
+ } ap_settings;
+
+ struct wps_event_er_set_selected_registrar {
+ const u8 *uuid;
+ int sel_reg;
+ u16 dev_passwd_id;
+ u16 sel_reg_config_methods;
+ enum {
+ WPS_ER_SET_SEL_REG_START,
+ WPS_ER_SET_SEL_REG_DONE,
+ WPS_ER_SET_SEL_REG_FAILED
+ } state;
+ } set_sel_reg;
};
/**
@@ -532,16 +608,6 @@ struct wps_context {
struct wps_device_data dev;
/**
- * oob_conf - OOB Config data
- */
- struct oob_conf_data oob_conf;
-
- /**
- * oob_dev_pw_id - OOB Device password id
- */
- u16 oob_dev_pw_id;
-
- /**
* dh_ctx - Context data for Diffie-Hellman operation
*/
void *dh_ctx;
@@ -672,53 +738,57 @@ struct wps_context {
/* Pending messages from UPnP PutWLANResponse */
struct upnp_pending_message *upnp_msgs;
-};
-
-struct oob_device_data {
- char *device_name;
- char *device_path;
- void * (*init_func)(struct wps_context *, struct oob_device_data *,
- int);
- struct wpabuf * (*read_func)(void *);
- int (*write_func)(void *, struct wpabuf *);
- void (*deinit_func)(void *);
-};
-struct oob_nfc_device_data {
- int (*init_func)(char *);
- void * (*read_func)(size_t *);
- int (*write_func)(void *, size_t);
- void (*deinit_func)(void);
+ u16 ap_nfc_dev_pw_id;
+ struct wpabuf *ap_nfc_dh_pubkey;
+ struct wpabuf *ap_nfc_dh_privkey;
+ struct wpabuf *ap_nfc_dev_pw;
};
struct wps_registrar *
wps_registrar_init(struct wps_context *wps,
const struct wps_registrar_config *cfg);
void wps_registrar_deinit(struct wps_registrar *reg);
-int wps_registrar_add_pin(struct wps_registrar *reg, const u8 *uuid,
- const u8 *pin, size_t pin_len, int timeout);
+int wps_registrar_add_pin(struct wps_registrar *reg, const u8 *addr,
+ const u8 *uuid, const u8 *pin, size_t pin_len,
+ int timeout);
int wps_registrar_invalidate_pin(struct wps_registrar *reg, const u8 *uuid);
+int wps_registrar_wps_cancel(struct wps_registrar *reg);
int wps_registrar_unlock_pin(struct wps_registrar *reg, const u8 *uuid);
-int wps_registrar_button_pushed(struct wps_registrar *reg);
+int wps_registrar_button_pushed(struct wps_registrar *reg,
+ const u8 *p2p_dev_addr);
+void wps_registrar_complete(struct wps_registrar *registrar, const u8 *uuid_e,
+ const u8 *dev_pw, size_t dev_pw_len);
void wps_registrar_probe_req_rx(struct wps_registrar *reg, const u8 *addr,
- const struct wpabuf *wps_data);
+ const struct wpabuf *wps_data,
+ int p2p_wildcard);
int wps_registrar_update_ie(struct wps_registrar *reg);
int wps_registrar_get_info(struct wps_registrar *reg, const u8 *addr,
char *buf, size_t buflen);
+int wps_registrar_config_ap(struct wps_registrar *reg,
+ struct wps_credential *cred);
+int wps_registrar_add_nfc_pw_token(struct wps_registrar *reg,
+ const u8 *pubkey_hash, u16 pw_id,
+ const u8 *dev_pw, size_t dev_pw_len);
+int wps_registrar_add_nfc_password_token(struct wps_registrar *reg,
+ const u8 *oob_dev_pw,
+ size_t oob_dev_pw_len);
+
+int wps_build_credential_wrap(struct wpabuf *msg,
+ const struct wps_credential *cred);
unsigned int wps_pin_checksum(unsigned int pin);
unsigned int wps_pin_valid(unsigned int pin);
unsigned int wps_generate_pin(void);
+int wps_pin_str_valid(const char *pin);
void wps_free_pending_msgs(struct upnp_pending_message *msgs);
-struct oob_device_data * wps_get_oob_device(char *device_type);
-struct oob_nfc_device_data * wps_get_oob_nfc_device(char *device_name);
-int wps_get_oob_method(char *method);
-int wps_process_oob(struct wps_context *wps, struct oob_device_data *oob_dev,
- int registrar);
+struct wpabuf * wps_get_oob_cred(struct wps_context *wps);
+int wps_oob_use_cred(struct wps_context *wps, struct wps_parse_attr *attr);
int wps_attr_text(struct wpabuf *data, char *buf, char *end);
-struct wps_er * wps_er_init(struct wps_context *wps, const char *ifname);
+struct wps_er * wps_er_init(struct wps_context *wps, const char *ifname,
+ const char *filter);
void wps_er_refresh(struct wps_er *er);
void wps_er_deinit(struct wps_er *er, void (*cb)(void *ctx), void *ctx);
void wps_er_set_sel_reg(struct wps_er *er, int sel_reg, u16 dev_passwd_id,
@@ -726,11 +796,173 @@ void wps_er_set_sel_reg(struct wps_er *er, int sel_reg, u16 dev_passwd_id,
int wps_er_pbc(struct wps_er *er, const u8 *uuid);
int wps_er_learn(struct wps_er *er, const u8 *uuid, const u8 *pin,
size_t pin_len);
+int wps_er_set_config(struct wps_er *er, const u8 *uuid,
+ const struct wps_credential *cred);
+int wps_er_config(struct wps_er *er, const u8 *uuid, const u8 *pin,
+ size_t pin_len, const struct wps_credential *cred);
+struct wpabuf * wps_er_nfc_config_token(struct wps_er *er, const u8 *uuid);
int wps_dev_type_str2bin(const char *str, u8 dev_type[WPS_DEV_TYPE_LEN]);
char * wps_dev_type_bin2str(const u8 dev_type[WPS_DEV_TYPE_LEN], char *buf,
size_t buf_len);
void uuid_gen_mac_addr(const u8 *mac_addr, u8 *uuid);
u16 wps_config_methods_str2bin(const char *str);
+struct wpabuf * wps_build_nfc_pw_token(u16 dev_pw_id,
+ const struct wpabuf *pubkey,
+ const struct wpabuf *dev_pw);
+struct wpabuf * wps_nfc_token_gen(int ndef, int *id, struct wpabuf **pubkey,
+ struct wpabuf **privkey,
+ struct wpabuf **dev_pw);
+
+/* ndef.c */
+struct wpabuf * ndef_parse_wifi(const struct wpabuf *buf);
+struct wpabuf * ndef_build_wifi(const struct wpabuf *buf);
+struct wpabuf * ndef_build_wifi_hr(void);
+
+#ifdef CONFIG_WPS_STRICT
+int wps_validate_beacon(const struct wpabuf *wps_ie);
+int wps_validate_beacon_probe_resp(const struct wpabuf *wps_ie, int probe,
+ const u8 *addr);
+int wps_validate_probe_req(const struct wpabuf *wps_ie, const u8 *addr);
+int wps_validate_assoc_req(const struct wpabuf *wps_ie);
+int wps_validate_assoc_resp(const struct wpabuf *wps_ie);
+int wps_validate_m1(const struct wpabuf *tlvs);
+int wps_validate_m2(const struct wpabuf *tlvs);
+int wps_validate_m2d(const struct wpabuf *tlvs);
+int wps_validate_m3(const struct wpabuf *tlvs);
+int wps_validate_m4(const struct wpabuf *tlvs);
+int wps_validate_m4_encr(const struct wpabuf *tlvs, int wps2);
+int wps_validate_m5(const struct wpabuf *tlvs);
+int wps_validate_m5_encr(const struct wpabuf *tlvs, int wps2);
+int wps_validate_m6(const struct wpabuf *tlvs);
+int wps_validate_m6_encr(const struct wpabuf *tlvs, int wps2);
+int wps_validate_m7(const struct wpabuf *tlvs);
+int wps_validate_m7_encr(const struct wpabuf *tlvs, int ap, int wps2);
+int wps_validate_m8(const struct wpabuf *tlvs);
+int wps_validate_m8_encr(const struct wpabuf *tlvs, int ap, int wps2);
+int wps_validate_wsc_ack(const struct wpabuf *tlvs);
+int wps_validate_wsc_nack(const struct wpabuf *tlvs);
+int wps_validate_wsc_done(const struct wpabuf *tlvs);
+int wps_validate_upnp_set_selected_registrar(const struct wpabuf *tlvs);
+#else /* CONFIG_WPS_STRICT */
+static inline int wps_validate_beacon(const struct wpabuf *wps_ie){
+ return 0;
+}
+
+static inline int wps_validate_beacon_probe_resp(const struct wpabuf *wps_ie,
+ int probe, const u8 *addr)
+{
+ return 0;
+}
+
+static inline int wps_validate_probe_req(const struct wpabuf *wps_ie,
+ const u8 *addr)
+{
+ return 0;
+}
+
+static inline int wps_validate_assoc_req(const struct wpabuf *wps_ie)
+{
+ return 0;
+}
+
+static inline int wps_validate_assoc_resp(const struct wpabuf *wps_ie)
+{
+ return 0;
+}
+
+static inline int wps_validate_m1(const struct wpabuf *tlvs)
+{
+ return 0;
+}
+
+static inline int wps_validate_m2(const struct wpabuf *tlvs)
+{
+ return 0;
+}
+
+static inline int wps_validate_m2d(const struct wpabuf *tlvs)
+{
+ return 0;
+}
+
+static inline int wps_validate_m3(const struct wpabuf *tlvs)
+{
+ return 0;
+}
+
+static inline int wps_validate_m4(const struct wpabuf *tlvs)
+{
+ return 0;
+}
+
+static inline int wps_validate_m4_encr(const struct wpabuf *tlvs, int wps2)
+{
+ return 0;
+}
+
+static inline int wps_validate_m5(const struct wpabuf *tlvs)
+{
+ return 0;
+}
+
+static inline int wps_validate_m5_encr(const struct wpabuf *tlvs, int wps2)
+{
+ return 0;
+}
+
+static inline int wps_validate_m6(const struct wpabuf *tlvs)
+{
+ return 0;
+}
+
+static inline int wps_validate_m6_encr(const struct wpabuf *tlvs, int wps2)
+{
+ return 0;
+}
+
+static inline int wps_validate_m7(const struct wpabuf *tlvs)
+{
+ return 0;
+}
+
+static inline int wps_validate_m7_encr(const struct wpabuf *tlvs, int ap,
+ int wps2)
+{
+ return 0;
+}
+
+static inline int wps_validate_m8(const struct wpabuf *tlvs)
+{
+ return 0;
+}
+
+static inline int wps_validate_m8_encr(const struct wpabuf *tlvs, int ap,
+ int wps2)
+{
+ return 0;
+}
+
+static inline int wps_validate_wsc_ack(const struct wpabuf *tlvs)
+{
+ return 0;
+}
+
+static inline int wps_validate_wsc_nack(const struct wpabuf *tlvs)
+{
+ return 0;
+}
+
+static inline int wps_validate_wsc_done(const struct wpabuf *tlvs)
+{
+ return 0;
+}
+
+static inline int wps_validate_upnp_set_selected_registrar(
+ const struct wpabuf *tlvs)
+{
+ return 0;
+}
+#endif /* CONFIG_WPS_STRICT */
#endif /* WPS_H */
diff --git a/contrib/wpa/src/wps/wps_attr_build.c b/contrib/wpa/src/wps/wps_attr_build.c
index 9da556a..29aee8e 100644
--- a/contrib/wpa/src/wps/wps_attr_build.c
+++ b/contrib/wpa/src/wps/wps_attr_build.c
@@ -2,14 +2,8 @@
* Wi-Fi Protected Setup - attribute building
* Copyright (c) 2008, Jouni Malinen <j@w1.fi>
*
- * This program is free software; you can redistribute it and/or modify
- * it under the terms of the GNU General Public License version 2 as
- * published by the Free Software Foundation.
- *
- * Alternatively, this software may be distributed under the terms of BSD
- * license.
- *
- * See README and COPYING for more details.
+ * This software may be distributed under the terms of the BSD license.
+ * See README for more details.
*/
#include "includes.h"
@@ -19,6 +13,8 @@
#include "crypto/crypto.h"
#include "crypto/dh_group5.h"
#include "crypto/sha256.h"
+#include "crypto/random.h"
+#include "common/ieee802_11_defs.h"
#include "wps_i.h"
@@ -34,6 +30,14 @@ int wps_build_public_key(struct wps_data *wps, struct wpabuf *msg)
wps->dh_ctx = wps->wps->dh_ctx;
wps->wps->dh_ctx = NULL;
pubkey = wpabuf_dup(wps->wps->dh_pubkey);
+#ifdef CONFIG_WPS_NFC
+ } else if (wps->dev_pw_id >= 0x10 && wps->wps->ap &&
+ wps->dev_pw_id == wps->wps->ap_nfc_dev_pw_id) {
+ wpa_printf(MSG_DEBUG, "WPS: Using NFC password token DH keys");
+ wps->dh_privkey = wpabuf_dup(wps->wps->ap_nfc_dh_privkey);
+ pubkey = wpabuf_dup(wps->wps->ap_nfc_dh_pubkey);
+ wps->dh_ctx = dh5_init_fixed(wps->dh_privkey, pubkey);
+#endif /* CONFIG_WPS_NFC */
} else {
wpa_printf(MSG_DEBUG, "WPS: Generate new DH keys");
wps->dh_privkey = NULL;
@@ -47,6 +51,8 @@ int wps_build_public_key(struct wps_data *wps, struct wpabuf *msg)
wpabuf_free(pubkey);
return -1;
}
+ wpa_hexdump_buf_key(MSG_DEBUG, "WPS: DH Private Key", wps->dh_privkey);
+ wpa_hexdump_buf(MSG_DEBUG, "WPS: DH own Public Key", pubkey);
wpabuf_put_be16(msg, ATTR_PUBLIC_KEY);
wpabuf_put_be16(msg, wpabuf_len(pubkey));
@@ -156,10 +162,65 @@ int wps_build_authenticator(struct wps_data *wps, struct wpabuf *msg)
int wps_build_version(struct wpabuf *msg)
{
- wpa_printf(MSG_DEBUG, "WPS: * Version");
+ /*
+ * Note: This attribute is deprecated and set to hardcoded 0x10 for
+ * backwards compatibility reasons. The real version negotiation is
+ * done with Version2.
+ */
+ wpa_printf(MSG_DEBUG, "WPS: * Version (hardcoded 0x10)");
wpabuf_put_be16(msg, ATTR_VERSION);
wpabuf_put_be16(msg, 1);
+ wpabuf_put_u8(msg, 0x10);
+ return 0;
+}
+
+
+int wps_build_wfa_ext(struct wpabuf *msg, int req_to_enroll,
+ const u8 *auth_macs, size_t auth_macs_count)
+{
+#ifdef CONFIG_WPS2
+ u8 *len;
+
+ wpabuf_put_be16(msg, ATTR_VENDOR_EXT);
+ len = wpabuf_put(msg, 2); /* to be filled */
+ wpabuf_put_be24(msg, WPS_VENDOR_ID_WFA);
+
+ wpa_printf(MSG_DEBUG, "WPS: * Version2 (0x%x)", WPS_VERSION);
+ wpabuf_put_u8(msg, WFA_ELEM_VERSION2);
+ wpabuf_put_u8(msg, 1);
wpabuf_put_u8(msg, WPS_VERSION);
+
+ if (req_to_enroll) {
+ wpa_printf(MSG_DEBUG, "WPS: * Request to Enroll (1)");
+ wpabuf_put_u8(msg, WFA_ELEM_REQUEST_TO_ENROLL);
+ wpabuf_put_u8(msg, 1);
+ wpabuf_put_u8(msg, 1);
+ }
+
+ if (auth_macs && auth_macs_count) {
+ size_t i;
+ wpa_printf(MSG_DEBUG, "WPS: * AuthorizedMACs (count=%d)",
+ (int) auth_macs_count);
+ wpabuf_put_u8(msg, WFA_ELEM_AUTHORIZEDMACS);
+ wpabuf_put_u8(msg, auth_macs_count * ETH_ALEN);
+ wpabuf_put_data(msg, auth_macs, auth_macs_count * ETH_ALEN);
+ for (i = 0; i < auth_macs_count; i++)
+ wpa_printf(MSG_DEBUG, "WPS: AuthorizedMAC: " MACSTR,
+ MAC2STR(&auth_macs[i * ETH_ALEN]));
+ }
+
+ WPA_PUT_BE16(len, (u8 *) wpabuf_put(msg, 0) - len - 2);
+#endif /* CONFIG_WPS2 */
+
+#ifdef CONFIG_WPS_TESTING
+ if (WPS_VERSION > 0x20) {
+ wpa_printf(MSG_DEBUG, "WPS: * Extensibility Testing - extra "
+ "attribute");
+ wpabuf_put_be16(msg, ATTR_EXTENSIBILITY_TEST);
+ wpabuf_put_be16(msg, 1);
+ wpabuf_put_u8(msg, 42);
+ }
+#endif /* CONFIG_WPS_TESTING */
return 0;
}
@@ -196,20 +257,28 @@ int wps_build_registrar_nonce(struct wps_data *wps, struct wpabuf *msg)
int wps_build_auth_type_flags(struct wps_data *wps, struct wpabuf *msg)
{
+ u16 auth_types = WPS_AUTH_TYPES;
+#ifdef CONFIG_WPS2
+ auth_types &= ~WPS_AUTH_SHARED;
+#endif /* CONFIG_WPS2 */
wpa_printf(MSG_DEBUG, "WPS: * Authentication Type Flags");
wpabuf_put_be16(msg, ATTR_AUTH_TYPE_FLAGS);
wpabuf_put_be16(msg, 2);
- wpabuf_put_be16(msg, WPS_AUTH_TYPES);
+ wpabuf_put_be16(msg, auth_types);
return 0;
}
int wps_build_encr_type_flags(struct wps_data *wps, struct wpabuf *msg)
{
+ u16 encr_types = WPS_ENCR_TYPES;
+#ifdef CONFIG_WPS2
+ encr_types &= ~WPS_ENCR_WEP;
+#endif /* CONFIG_WPS2 */
wpa_printf(MSG_DEBUG, "WPS: * Encryption Type Flags");
wpabuf_put_be16(msg, ATTR_ENCR_TYPE_FLAGS);
wpabuf_put_be16(msg, 2);
- wpabuf_put_be16(msg, WPS_ENCR_TYPES);
+ wpabuf_put_be16(msg, encr_types);
return 0;
}
@@ -266,7 +335,7 @@ int wps_build_encr_settings(struct wps_data *wps, struct wpabuf *msg,
wpabuf_put_be16(msg, block_size + wpabuf_len(plain));
iv = wpabuf_put(msg, block_size);
- if (os_get_random(iv, block_size) < 0)
+ if (random_get_bytes(iv, block_size) < 0)
return -1;
data = wpabuf_put(msg, 0);
@@ -279,44 +348,56 @@ int wps_build_encr_settings(struct wps_data *wps, struct wpabuf *msg,
#ifdef CONFIG_WPS_OOB
-int wps_build_oob_dev_password(struct wpabuf *msg, struct wps_context *wps)
+int wps_build_oob_dev_pw(struct wpabuf *msg, u16 dev_pw_id,
+ const struct wpabuf *pubkey, const u8 *dev_pw,
+ size_t dev_pw_len)
{
size_t hash_len;
const u8 *addr[1];
u8 pubkey_hash[WPS_HASH_LEN];
- u8 dev_password_bin[WPS_OOB_DEVICE_PASSWORD_LEN];
-
- wpa_printf(MSG_DEBUG, "WPS: * OOB Device Password");
- addr[0] = wpabuf_head(wps->dh_pubkey);
- hash_len = wpabuf_len(wps->dh_pubkey);
+ addr[0] = wpabuf_head(pubkey);
+ hash_len = wpabuf_len(pubkey);
sha256_vector(1, addr, &hash_len, pubkey_hash);
- if (os_get_random((u8 *) &wps->oob_dev_pw_id, sizeof(u16)) < 0) {
- wpa_printf(MSG_ERROR, "WPS: device password id "
- "generation error");
- return -1;
- }
- wps->oob_dev_pw_id |= 0x0010;
-
- if (os_get_random(dev_password_bin, WPS_OOB_DEVICE_PASSWORD_LEN) < 0) {
- wpa_printf(MSG_ERROR, "WPS: OOB device password "
- "generation error");
- return -1;
- }
-
wpabuf_put_be16(msg, ATTR_OOB_DEVICE_PASSWORD);
- wpabuf_put_be16(msg, WPS_OOB_DEVICE_PASSWORD_ATTR_LEN);
+ wpabuf_put_be16(msg, WPS_OOB_PUBKEY_HASH_LEN + 2 + dev_pw_len);
wpabuf_put_data(msg, pubkey_hash, WPS_OOB_PUBKEY_HASH_LEN);
- wpabuf_put_be16(msg, wps->oob_dev_pw_id);
- wpabuf_put_data(msg, dev_password_bin, WPS_OOB_DEVICE_PASSWORD_LEN);
-
- wpa_snprintf_hex_uppercase(
- wpabuf_put(wps->oob_conf.dev_password,
- wpabuf_size(wps->oob_conf.dev_password)),
- wpabuf_size(wps->oob_conf.dev_password),
- dev_password_bin, WPS_OOB_DEVICE_PASSWORD_LEN);
+ wpabuf_put_be16(msg, dev_pw_id);
+ wpabuf_put_data(msg, dev_pw, dev_pw_len);
return 0;
}
#endif /* CONFIG_WPS_OOB */
+
+
+/* Encapsulate WPS IE data with one (or more, if needed) IE headers */
+struct wpabuf * wps_ie_encapsulate(struct wpabuf *data)
+{
+ struct wpabuf *ie;
+ const u8 *pos, *end;
+
+ ie = wpabuf_alloc(wpabuf_len(data) + 100);
+ if (ie == NULL) {
+ wpabuf_free(data);
+ return NULL;
+ }
+
+ pos = wpabuf_head(data);
+ end = pos + wpabuf_len(data);
+
+ while (end > pos) {
+ size_t frag_len = end - pos;
+ if (frag_len > 251)
+ frag_len = 251;
+ wpabuf_put_u8(ie, WLAN_EID_VENDOR_SPECIFIC);
+ wpabuf_put_u8(ie, 4 + frag_len);
+ wpabuf_put_be32(ie, WPS_DEV_OUI_WFA);
+ wpabuf_put_data(ie, pos, frag_len);
+ pos += frag_len;
+ }
+
+ wpabuf_free(data);
+
+ return ie;
+}
diff --git a/contrib/wpa/src/wps/wps_attr_parse.c b/contrib/wpa/src/wps/wps_attr_parse.c
index 30b0e79..3999b1b8 100644
--- a/contrib/wpa/src/wps/wps_attr_parse.c
+++ b/contrib/wpa/src/wps/wps_attr_parse.c
@@ -2,22 +2,132 @@
* Wi-Fi Protected Setup - attribute parsing
* Copyright (c) 2008, Jouni Malinen <j@w1.fi>
*
- * This program is free software; you can redistribute it and/or modify
- * it under the terms of the GNU General Public License version 2 as
- * published by the Free Software Foundation.
- *
- * Alternatively, this software may be distributed under the terms of BSD
- * license.
- *
- * See README and COPYING for more details.
+ * This software may be distributed under the terms of the BSD license.
+ * See README for more details.
*/
#include "includes.h"
#include "common.h"
-#include "wps_i.h"
+#include "wps_defs.h"
+#include "wps_attr_parse.h"
+#ifndef CONFIG_WPS_STRICT
#define WPS_WORKAROUNDS
+#endif /* CONFIG_WPS_STRICT */
+
+
+static int wps_set_vendor_ext_wfa_subelem(struct wps_parse_attr *attr,
+ u8 id, u8 len, const u8 *pos)
+{
+ wpa_printf(MSG_EXCESSIVE, "WPS: WFA subelement id=%u len=%u",
+ id, len);
+ switch (id) {
+ case WFA_ELEM_VERSION2:
+ if (len != 1) {
+ wpa_printf(MSG_DEBUG, "WPS: Invalid Version2 length "
+ "%u", len);
+ return -1;
+ }
+ attr->version2 = pos;
+ break;
+ case WFA_ELEM_AUTHORIZEDMACS:
+ attr->authorized_macs = pos;
+ attr->authorized_macs_len = len;
+ break;
+ case WFA_ELEM_NETWORK_KEY_SHAREABLE:
+ if (len != 1) {
+ wpa_printf(MSG_DEBUG, "WPS: Invalid Network Key "
+ "Shareable length %u", len);
+ return -1;
+ }
+ attr->network_key_shareable = pos;
+ break;
+ case WFA_ELEM_REQUEST_TO_ENROLL:
+ if (len != 1) {
+ wpa_printf(MSG_DEBUG, "WPS: Invalid Request to Enroll "
+ "length %u", len);
+ return -1;
+ }
+ attr->request_to_enroll = pos;
+ break;
+ case WFA_ELEM_SETTINGS_DELAY_TIME:
+ if (len != 1) {
+ wpa_printf(MSG_DEBUG, "WPS: Invalid Settings Delay "
+ "Time length %u", len);
+ return -1;
+ }
+ attr->settings_delay_time = pos;
+ break;
+ default:
+ wpa_printf(MSG_MSGDUMP, "WPS: Skipped unknown WFA Vendor "
+ "Extension subelement %u", id);
+ break;
+ }
+
+ return 0;
+}
+
+
+static int wps_parse_vendor_ext_wfa(struct wps_parse_attr *attr, const u8 *pos,
+ u16 len)
+{
+ const u8 *end = pos + len;
+ u8 id, elen;
+
+ while (pos + 2 < end) {
+ id = *pos++;
+ elen = *pos++;
+ if (pos + elen > end)
+ break;
+ if (wps_set_vendor_ext_wfa_subelem(attr, id, elen, pos) < 0)
+ return -1;
+ pos += elen;
+ }
+
+ return 0;
+}
+
+
+static int wps_parse_vendor_ext(struct wps_parse_attr *attr, const u8 *pos,
+ u16 len)
+{
+ u32 vendor_id;
+
+ if (len < 3) {
+ wpa_printf(MSG_DEBUG, "WPS: Skip invalid Vendor Extension");
+ return 0;
+ }
+
+ vendor_id = WPA_GET_BE24(pos);
+ switch (vendor_id) {
+ case WPS_VENDOR_ID_WFA:
+ return wps_parse_vendor_ext_wfa(attr, pos + 3, len - 3);
+ }
+
+ /* Handle unknown vendor extensions */
+
+ wpa_printf(MSG_MSGDUMP, "WPS: Unknown Vendor Extension (Vendor ID %u)",
+ vendor_id);
+
+ if (len > WPS_MAX_VENDOR_EXT_LEN) {
+ wpa_printf(MSG_DEBUG, "WPS: Too long Vendor Extension (%u)",
+ len);
+ return -1;
+ }
+
+ if (attr->num_vendor_ext >= MAX_WPS_PARSE_VENDOR_EXT) {
+ wpa_printf(MSG_DEBUG, "WPS: Skipped Vendor Extension "
+ "attribute (max %d vendor extensions)",
+ MAX_WPS_PARSE_VENDOR_EXT);
+ return -1;
+ }
+ attr->vendor_ext[attr->num_vendor_ext] = pos;
+ attr->vendor_ext_len[attr->num_vendor_ext] = len;
+ attr->num_vendor_ext++;
+
+ return 0;
+}
static int wps_set_attr(struct wps_parse_attr *attr, u16 type,
@@ -153,12 +263,16 @@ static int wps_set_attr(struct wps_parse_attr *attr, u16 type,
attr->dev_password_id = pos;
break;
case ATTR_OOB_DEVICE_PASSWORD:
- if (len != WPS_OOB_DEVICE_PASSWORD_ATTR_LEN) {
+ if (len < WPS_OOB_PUBKEY_HASH_LEN + 2 +
+ WPS_OOB_DEVICE_PASSWORD_MIN_LEN ||
+ len > WPS_OOB_PUBKEY_HASH_LEN + 2 +
+ WPS_OOB_DEVICE_PASSWORD_LEN) {
wpa_printf(MSG_DEBUG, "WPS: Invalid OOB Device "
"Password length %u", len);
return -1;
}
attr->oob_dev_password = pos;
+ attr->oob_dev_password_len = len;
break;
case ATTR_OS_VERSION:
if (len != 4) {
@@ -399,6 +513,43 @@ static int wps_set_attr(struct wps_parse_attr *attr, u16 type,
}
attr->ap_setup_locked = pos;
break;
+ case ATTR_REQUESTED_DEV_TYPE:
+ if (len != WPS_DEV_TYPE_LEN) {
+ wpa_printf(MSG_DEBUG, "WPS: Invalid Requested Device "
+ "Type length %u", len);
+ return -1;
+ }
+ if (attr->num_req_dev_type >= MAX_REQ_DEV_TYPE_COUNT) {
+ wpa_printf(MSG_DEBUG, "WPS: Skipped Requested Device "
+ "Type attribute (max %u types)",
+ MAX_REQ_DEV_TYPE_COUNT);
+ break;
+ }
+ attr->req_dev_type[attr->num_req_dev_type] = pos;
+ attr->num_req_dev_type++;
+ break;
+ case ATTR_SECONDARY_DEV_TYPE_LIST:
+ if (len > WPS_SEC_DEV_TYPE_MAX_LEN ||
+ (len % WPS_DEV_TYPE_LEN) > 0) {
+ wpa_printf(MSG_DEBUG, "WPS: Invalid Secondary Device "
+ "Type length %u", len);
+ return -1;
+ }
+ attr->sec_dev_type_list = pos;
+ attr->sec_dev_type_list_len = len;
+ break;
+ case ATTR_VENDOR_EXT:
+ if (wps_parse_vendor_ext(attr, pos, len) < 0)
+ return -1;
+ break;
+ case ATTR_AP_CHANNEL:
+ if (len != 2) {
+ wpa_printf(MSG_DEBUG, "WPS: Invalid AP Channel "
+ "length %u", len);
+ return -1;
+ }
+ attr->ap_channel = pos;
+ break;
default:
wpa_printf(MSG_DEBUG, "WPS: Unsupported attribute type 0x%x "
"len=%u", type, len);
@@ -413,6 +564,9 @@ int wps_parse_msg(const struct wpabuf *msg, struct wps_parse_attr *attr)
{
const u8 *pos, *end;
u16 type, len;
+#ifdef WPS_WORKAROUNDS
+ u16 prev_type = 0;
+#endif /* WPS_WORKAROUNDS */
os_memset(attr, 0, sizeof(*attr));
pos = wpabuf_head(msg);
@@ -430,10 +584,28 @@ int wps_parse_msg(const struct wpabuf *msg, struct wps_parse_attr *attr)
pos += 2;
len = WPA_GET_BE16(pos);
pos += 2;
- wpa_printf(MSG_MSGDUMP, "WPS: attr type=0x%x len=%u",
+ wpa_printf(MSG_EXCESSIVE, "WPS: attr type=0x%x len=%u",
type, len);
if (len > end - pos) {
wpa_printf(MSG_DEBUG, "WPS: Attribute overflow");
+ wpa_hexdump_buf(MSG_MSGDUMP, "WPS: Message data", msg);
+#ifdef WPS_WORKAROUNDS
+ /*
+ * Some deployed APs seem to have a bug in encoding of
+ * Network Key attribute in the Credential attribute
+ * where they add an extra octet after the Network Key
+ * attribute at least when open network is being
+ * provisioned.
+ */
+ if ((type & 0xff00) != 0x1000 &&
+ prev_type == ATTR_NETWORK_KEY) {
+ wpa_printf(MSG_DEBUG, "WPS: Workaround - try "
+ "to skip unexpected octet after "
+ "Network Key");
+ pos -= 3;
+ continue;
+ }
+#endif /* WPS_WORKAROUNDS */
return -1;
}
@@ -459,6 +631,9 @@ int wps_parse_msg(const struct wpabuf *msg, struct wps_parse_attr *attr)
if (wps_set_attr(attr, type, pos, len) < 0)
return -1;
+#ifdef WPS_WORKAROUNDS
+ prev_type = type;
+#endif /* WPS_WORKAROUNDS */
pos += len;
}
diff --git a/contrib/wpa/src/wps/wps_attr_parse.h b/contrib/wpa/src/wps/wps_attr_parse.h
new file mode 100644
index 0000000..88e51a4
--- /dev/null
+++ b/contrib/wpa/src/wps/wps_attr_parse.h
@@ -0,0 +1,108 @@
+/*
+ * Wi-Fi Protected Setup - attribute parsing
+ * Copyright (c) 2008-2012, Jouni Malinen <j@w1.fi>
+ *
+ * This software may be distributed under the terms of the BSD license.
+ * See README for more details.
+ */
+
+#ifndef WPS_ATTR_PARSE_H
+#define WPS_ATTR_PARSE_H
+
+#include "wps.h"
+
+struct wps_parse_attr {
+ /* fixed length fields */
+ const u8 *version; /* 1 octet */
+ const u8 *version2; /* 1 octet */
+ const u8 *msg_type; /* 1 octet */
+ const u8 *enrollee_nonce; /* WPS_NONCE_LEN (16) octets */
+ const u8 *registrar_nonce; /* WPS_NONCE_LEN (16) octets */
+ const u8 *uuid_r; /* WPS_UUID_LEN (16) octets */
+ const u8 *uuid_e; /* WPS_UUID_LEN (16) octets */
+ const u8 *auth_type_flags; /* 2 octets */
+ const u8 *encr_type_flags; /* 2 octets */
+ const u8 *conn_type_flags; /* 1 octet */
+ const u8 *config_methods; /* 2 octets */
+ const u8 *sel_reg_config_methods; /* 2 octets */
+ const u8 *primary_dev_type; /* 8 octets */
+ const u8 *rf_bands; /* 1 octet */
+ const u8 *assoc_state; /* 2 octets */
+ const u8 *config_error; /* 2 octets */
+ const u8 *dev_password_id; /* 2 octets */
+ const u8 *os_version; /* 4 octets */
+ const u8 *wps_state; /* 1 octet */
+ const u8 *authenticator; /* WPS_AUTHENTICATOR_LEN (8) octets */
+ const u8 *r_hash1; /* WPS_HASH_LEN (32) octets */
+ const u8 *r_hash2; /* WPS_HASH_LEN (32) octets */
+ const u8 *e_hash1; /* WPS_HASH_LEN (32) octets */
+ const u8 *e_hash2; /* WPS_HASH_LEN (32) octets */
+ const u8 *r_snonce1; /* WPS_SECRET_NONCE_LEN (16) octets */
+ const u8 *r_snonce2; /* WPS_SECRET_NONCE_LEN (16) octets */
+ const u8 *e_snonce1; /* WPS_SECRET_NONCE_LEN (16) octets */
+ const u8 *e_snonce2; /* WPS_SECRET_NONCE_LEN (16) octets */
+ const u8 *key_wrap_auth; /* WPS_KWA_LEN (8) octets */
+ const u8 *auth_type; /* 2 octets */
+ const u8 *encr_type; /* 2 octets */
+ const u8 *network_idx; /* 1 octet */
+ const u8 *network_key_idx; /* 1 octet */
+ const u8 *mac_addr; /* ETH_ALEN (6) octets */
+ const u8 *key_prov_auto; /* 1 octet (Bool) */
+ const u8 *dot1x_enabled; /* 1 octet (Bool) */
+ const u8 *selected_registrar; /* 1 octet (Bool) */
+ const u8 *request_type; /* 1 octet */
+ const u8 *response_type; /* 1 octet */
+ const u8 *ap_setup_locked; /* 1 octet */
+ const u8 *settings_delay_time; /* 1 octet */
+ const u8 *network_key_shareable; /* 1 octet (Bool) */
+ const u8 *request_to_enroll; /* 1 octet (Bool) */
+ const u8 *ap_channel; /* 2 octets */
+
+ /* variable length fields */
+ const u8 *manufacturer;
+ size_t manufacturer_len;
+ const u8 *model_name;
+ size_t model_name_len;
+ const u8 *model_number;
+ size_t model_number_len;
+ const u8 *serial_number;
+ size_t serial_number_len;
+ const u8 *dev_name;
+ size_t dev_name_len;
+ const u8 *public_key;
+ size_t public_key_len;
+ const u8 *encr_settings;
+ size_t encr_settings_len;
+ const u8 *ssid; /* <= 32 octets */
+ size_t ssid_len;
+ const u8 *network_key; /* <= 64 octets */
+ size_t network_key_len;
+ const u8 *eap_type; /* <= 8 octets */
+ size_t eap_type_len;
+ const u8 *eap_identity; /* <= 64 octets */
+ size_t eap_identity_len;
+ const u8 *authorized_macs; /* <= 30 octets */
+ size_t authorized_macs_len;
+ const u8 *sec_dev_type_list; /* <= 128 octets */
+ size_t sec_dev_type_list_len;
+ const u8 *oob_dev_password; /* 38..54 octets */
+ size_t oob_dev_password_len;
+
+ /* attributes that can occur multiple times */
+#define MAX_CRED_COUNT 10
+ const u8 *cred[MAX_CRED_COUNT];
+ size_t cred_len[MAX_CRED_COUNT];
+ size_t num_cred;
+
+#define MAX_REQ_DEV_TYPE_COUNT 10
+ const u8 *req_dev_type[MAX_REQ_DEV_TYPE_COUNT];
+ size_t num_req_dev_type;
+
+ const u8 *vendor_ext[MAX_WPS_PARSE_VENDOR_EXT];
+ size_t vendor_ext_len[MAX_WPS_PARSE_VENDOR_EXT];
+ size_t num_vendor_ext;
+};
+
+int wps_parse_msg(const struct wpabuf *msg, struct wps_parse_attr *attr);
+
+#endif /* WPS_ATTR_PARSE_H */
diff --git a/contrib/wpa/src/wps/wps_attr_process.c b/contrib/wpa/src/wps/wps_attr_process.c
index 4751bbc..b81f106 100644
--- a/contrib/wpa/src/wps/wps_attr_process.c
+++ b/contrib/wpa/src/wps/wps_attr_process.c
@@ -2,14 +2,8 @@
* Wi-Fi Protected Setup - attribute processing
* Copyright (c) 2008, Jouni Malinen <j@w1.fi>
*
- * This program is free software; you can redistribute it and/or modify
- * it under the terms of the GNU General Public License version 2 as
- * published by the Free Software Foundation.
- *
- * Alternatively, this software may be distributed under the terms of BSD
- * license.
- *
- * See README and COPYING for more details.
+ * This software may be distributed under the terms of the BSD license.
+ * See README for more details.
*/
#include "includes.h"
@@ -264,11 +258,31 @@ static int wps_process_cred_802_1x_enabled(struct wps_credential *cred,
}
-static void wps_workaround_cred_key(struct wps_credential *cred)
+static int wps_process_cred_ap_channel(struct wps_credential *cred,
+ const u8 *ap_channel)
+{
+ if (ap_channel == NULL)
+ return 0; /* optional attribute */
+
+ cred->ap_channel = WPA_GET_BE16(ap_channel);
+ wpa_printf(MSG_DEBUG, "WPS: AP Channel: %u", cred->ap_channel);
+
+ return 0;
+}
+
+
+static int wps_workaround_cred_key(struct wps_credential *cred)
{
if (cred->auth_type & (WPS_AUTH_WPAPSK | WPS_AUTH_WPA2PSK) &&
cred->key_len > 8 && cred->key_len < 64 &&
cred->key[cred->key_len - 1] == 0) {
+#ifdef CONFIG_WPS_STRICT
+ wpa_printf(MSG_INFO, "WPS: WPA/WPA2-Personal passphrase uses "
+ "forbidden NULL termination");
+ wpa_hexdump_ascii_key(MSG_INFO, "WPS: Network Key",
+ cred->key, cred->key_len);
+ return -1;
+#else /* CONFIG_WPS_STRICT */
/*
* A deployed external registrar is known to encode ASCII
* passphrases incorrectly. Remove the extra NULL termination
@@ -277,7 +291,9 @@ static void wps_workaround_cred_key(struct wps_credential *cred)
wpa_printf(MSG_DEBUG, "WPS: Workaround - remove NULL "
"termination from ASCII passphrase");
cred->key_len--;
+#endif /* CONFIG_WPS_STRICT */
}
+ return 0;
}
@@ -300,12 +316,11 @@ int wps_process_cred(struct wps_parse_attr *attr,
wps_process_cred_eap_identity(cred, attr->eap_identity,
attr->eap_identity_len) ||
wps_process_cred_key_prov_auto(cred, attr->key_prov_auto) ||
- wps_process_cred_802_1x_enabled(cred, attr->dot1x_enabled))
+ wps_process_cred_802_1x_enabled(cred, attr->dot1x_enabled) ||
+ wps_process_cred_ap_channel(cred, attr->ap_channel))
return -1;
- wps_workaround_cred_key(cred);
-
- return 0;
+ return wps_workaround_cred_key(cred);
}
@@ -324,7 +339,5 @@ int wps_process_ap_settings(struct wps_parse_attr *attr,
wps_process_cred_mac_addr(cred, attr->mac_addr))
return -1;
- wps_workaround_cred_key(cred);
-
- return 0;
+ return wps_workaround_cred_key(cred);
}
diff --git a/contrib/wpa/src/wps/wps_common.c b/contrib/wpa/src/wps/wps_common.c
index 6ef14db..68d9f0a 100644
--- a/contrib/wpa/src/wps/wps_common.c
+++ b/contrib/wpa/src/wps/wps_common.c
@@ -1,15 +1,9 @@
/*
* Wi-Fi Protected Setup - common functionality
- * Copyright (c) 2008-2009, Jouni Malinen <j@w1.fi>
+ * Copyright (c) 2008-2012, Jouni Malinen <j@w1.fi>
*
- * This program is free software; you can redistribute it and/or modify
- * it under the terms of the GNU General Public License version 2 as
- * published by the Free Software Foundation.
- *
- * Alternatively, this software may be distributed under the terms of BSD
- * license.
- *
- * See README and COPYING for more details.
+ * This software may be distributed under the terms of the BSD license.
+ * See README for more details.
*/
#include "includes.h"
@@ -20,8 +14,8 @@
#include "crypto/dh_group5.h"
#include "crypto/sha1.h"
#include "crypto/sha256.h"
+#include "crypto/random.h"
#include "wps_i.h"
-#include "wps_dev_attr.h"
void wps_kdf(const u8 *key, const u8 *label_prefix, size_t label_prefix_len,
@@ -81,6 +75,8 @@ int wps_derive_keys(struct wps_data *wps)
return -1;
}
+ wpa_hexdump_buf_key(MSG_DEBUG, "WPS: DH Private Key", wps->dh_privkey);
+ wpa_hexdump_buf(MSG_DEBUG, "WPS: DH peer Public Key", pubkey);
dh_shared = dh5_derive_shared(wps->dh_ctx, pubkey, wps->dh_privkey);
dh5_free(wps->dh_ctx);
wps->dh_ctx = NULL;
@@ -241,7 +237,7 @@ unsigned int wps_generate_pin(void)
unsigned int val;
/* Generate seven random digits for the PIN */
- if (os_get_random((unsigned char *) &val, sizeof(val)) < 0) {
+ if (random_get_bytes((unsigned char *) &val, sizeof(val)) < 0) {
struct os_time now;
os_get_time(&now);
val = os_random() ^ now.sec ^ now.usec;
@@ -253,7 +249,24 @@ unsigned int wps_generate_pin(void)
}
-void wps_fail_event(struct wps_context *wps, enum wps_msg_type msg)
+int wps_pin_str_valid(const char *pin)
+{
+ const char *p;
+ size_t len;
+
+ p = pin;
+ while (*p >= '0' && *p <= '9')
+ p++;
+ if (*p != '\0')
+ return 0;
+
+ len = p - pin;
+ return len == 4 || len == 8;
+}
+
+
+void wps_fail_event(struct wps_context *wps, enum wps_msg_type msg,
+ u16 config_error, u16 error_indication)
{
union wps_event_data data;
@@ -262,6 +275,8 @@ void wps_fail_event(struct wps_context *wps, enum wps_msg_type msg)
os_memset(&data, 0, sizeof(data));
data.fail.msg = msg;
+ data.fail.config_error = config_error;
+ data.fail.error_indication = error_indication;
wps->event_cb(wps->cb_ctx, WPS_EV_FAIL, &data);
}
@@ -309,7 +324,7 @@ void wps_pbc_timeout_event(struct wps_context *wps)
#ifdef CONFIG_WPS_OOB
-static struct wpabuf * wps_get_oob_cred(struct wps_context *wps)
+struct wpabuf * wps_get_oob_cred(struct wps_context *wps)
{
struct wps_data data;
struct wpabuf *plain;
@@ -325,7 +340,9 @@ static struct wpabuf * wps_get_oob_cred(struct wps_context *wps)
data.wps = wps;
data.auth_type = wps->auth_types;
data.encr_type = wps->encr_types;
- if (wps_build_version(plain) || wps_build_cred(&data, plain)) {
+ if (wps_build_version(plain) ||
+ wps_build_cred(&data, plain) ||
+ wps_build_wfa_ext(plain, 0, NULL, 0)) {
wpabuf_free(plain);
return NULL;
}
@@ -334,31 +351,22 @@ static struct wpabuf * wps_get_oob_cred(struct wps_context *wps)
}
-static struct wpabuf * wps_get_oob_dev_pwd(struct wps_context *wps)
+struct wpabuf * wps_build_nfc_pw_token(u16 dev_pw_id,
+ const struct wpabuf *pubkey,
+ const struct wpabuf *dev_pw)
{
struct wpabuf *data;
- data = wpabuf_alloc(9 + WPS_OOB_DEVICE_PASSWORD_ATTR_LEN);
- if (data == NULL) {
- wpa_printf(MSG_ERROR, "WPS: Failed to allocate memory for OOB "
- "device password attribute");
+ data = wpabuf_alloc(200);
+ if (data == NULL)
return NULL;
- }
-
- wpabuf_free(wps->oob_conf.dev_password);
- wps->oob_conf.dev_password =
- wpabuf_alloc(WPS_OOB_DEVICE_PASSWORD_LEN * 2 + 1);
- if (wps->oob_conf.dev_password == NULL) {
- wpa_printf(MSG_ERROR, "WPS: Failed to allocate memory for OOB "
- "device password");
- wpabuf_free(data);
- return NULL;
- }
if (wps_build_version(data) ||
- wps_build_oob_dev_password(data, wps)) {
- wpa_printf(MSG_ERROR, "WPS: Build OOB device password "
- "attribute error");
+ wps_build_oob_dev_pw(data, dev_pw_id, pubkey,
+ wpabuf_head(dev_pw), wpabuf_len(dev_pw)) ||
+ wps_build_wfa_ext(data, 0, NULL, 0)) {
+ wpa_printf(MSG_ERROR, "WPS: Failed to build NFC password "
+ "token");
wpabuf_free(data);
return NULL;
}
@@ -367,66 +375,17 @@ static struct wpabuf * wps_get_oob_dev_pwd(struct wps_context *wps)
}
-static int wps_parse_oob_dev_pwd(struct wps_context *wps,
- struct wpabuf *data)
-{
- struct oob_conf_data *oob_conf = &wps->oob_conf;
- struct wps_parse_attr attr;
- const u8 *pos;
-
- if (wps_parse_msg(data, &attr) < 0 ||
- attr.oob_dev_password == NULL) {
- wpa_printf(MSG_ERROR, "WPS: OOB device password not found");
- return -1;
- }
-
- pos = attr.oob_dev_password;
-
- oob_conf->pubkey_hash =
- wpabuf_alloc_copy(pos, WPS_OOB_PUBKEY_HASH_LEN);
- if (oob_conf->pubkey_hash == NULL) {
- wpa_printf(MSG_ERROR, "WPS: Failed to allocate memory for OOB "
- "public key hash");
- return -1;
- }
- pos += WPS_OOB_PUBKEY_HASH_LEN;
-
- wps->oob_dev_pw_id = WPA_GET_BE16(pos);
- pos += sizeof(wps->oob_dev_pw_id);
-
- oob_conf->dev_password =
- wpabuf_alloc(WPS_OOB_DEVICE_PASSWORD_LEN * 2 + 1);
- if (oob_conf->dev_password == NULL) {
- wpa_printf(MSG_ERROR, "WPS: Failed to allocate memory for OOB "
- "device password");
- return -1;
- }
- wpa_snprintf_hex_uppercase(wpabuf_put(oob_conf->dev_password,
- wpabuf_size(oob_conf->dev_password)),
- wpabuf_size(oob_conf->dev_password), pos,
- WPS_OOB_DEVICE_PASSWORD_LEN);
-
- return 0;
-}
-
-
-static int wps_parse_oob_cred(struct wps_context *wps, struct wpabuf *data)
+int wps_oob_use_cred(struct wps_context *wps, struct wps_parse_attr *attr)
{
struct wpabuf msg;
- struct wps_parse_attr attr;
size_t i;
- if (wps_parse_msg(data, &attr) < 0 || attr.num_cred <= 0) {
- wpa_printf(MSG_ERROR, "WPS: OOB credential not found");
- return -1;
- }
-
- for (i = 0; i < attr.num_cred; i++) {
+ for (i = 0; i < attr->num_cred; i++) {
struct wps_credential local_cred;
struct wps_parse_attr cattr;
os_memset(&local_cred, 0, sizeof(local_cred));
- wpabuf_set(&msg, attr.cred[i], attr.cred_len[i]);
+ wpabuf_set(&msg, attr->cred[i], attr->cred_len[i]);
if (wps_parse_msg(&msg, &cattr) < 0 ||
wps_process_cred(&cattr, &local_cred)) {
wpa_printf(MSG_ERROR, "WPS: Failed to parse OOB "
@@ -440,94 +399,6 @@ static int wps_parse_oob_cred(struct wps_context *wps, struct wpabuf *data)
}
-int wps_process_oob(struct wps_context *wps, struct oob_device_data *oob_dev,
- int registrar)
-{
- struct wpabuf *data;
- int ret, write_f, oob_method = wps->oob_conf.oob_method;
- void *oob_priv;
-
- write_f = oob_method == OOB_METHOD_DEV_PWD_E ? !registrar : registrar;
-
- oob_priv = oob_dev->init_func(wps, oob_dev, registrar);
- if (oob_priv == NULL) {
- wpa_printf(MSG_ERROR, "WPS: Failed to initialize OOB device");
- return -1;
- }
-
- if (write_f) {
- if (oob_method == OOB_METHOD_CRED)
- data = wps_get_oob_cred(wps);
- else
- data = wps_get_oob_dev_pwd(wps);
-
- ret = 0;
- if (data == NULL || oob_dev->write_func(oob_priv, data) < 0)
- ret = -1;
- } else {
- data = oob_dev->read_func(oob_priv);
- if (data == NULL)
- ret = -1;
- else {
- if (oob_method == OOB_METHOD_CRED)
- ret = wps_parse_oob_cred(wps, data);
- else
- ret = wps_parse_oob_dev_pwd(wps, data);
- }
- }
- wpabuf_free(data);
- oob_dev->deinit_func(oob_priv);
-
- if (ret < 0) {
- wpa_printf(MSG_ERROR, "WPS: Failed to process OOB data");
- return -1;
- }
-
- return 0;
-}
-
-
-struct oob_device_data * wps_get_oob_device(char *device_type)
-{
-#ifdef CONFIG_WPS_UFD
- if (os_strstr(device_type, "ufd") != NULL)
- return &oob_ufd_device_data;
-#endif /* CONFIG_WPS_UFD */
-#ifdef CONFIG_WPS_NFC
- if (os_strstr(device_type, "nfc") != NULL)
- return &oob_nfc_device_data;
-#endif /* CONFIG_WPS_NFC */
-
- return NULL;
-}
-
-
-#ifdef CONFIG_WPS_NFC
-struct oob_nfc_device_data * wps_get_oob_nfc_device(char *device_name)
-{
- if (device_name == NULL)
- return NULL;
-#ifdef CONFIG_WPS_NFC_PN531
- if (os_strstr(device_name, "pn531") != NULL)
- return &oob_nfc_pn531_device_data;
-#endif /* CONFIG_WPS_NFC_PN531 */
-
- return NULL;
-}
-#endif /* CONFIG_WPS_NFC */
-
-
-int wps_get_oob_method(char *method)
-{
- if (os_strstr(method, "pin-e") != NULL)
- return OOB_METHOD_DEV_PWD_E;
- if (os_strstr(method, "pin-r") != NULL)
- return OOB_METHOD_DEV_PWD_R;
- if (os_strstr(method, "cred") != NULL)
- return OOB_METHOD_CRED;
- return OOB_METHOD_UNKNOWN;
-}
-
#endif /* CONFIG_WPS_OOB */
@@ -604,15 +475,13 @@ u16 wps_config_methods_str2bin(const char *str)
if (str == NULL) {
/* Default to enabling methods based on build configuration */
methods |= WPS_CONFIG_DISPLAY | WPS_CONFIG_KEYPAD;
-#ifdef CONFIG_WPS_UFD
- methods |= WPS_CONFIG_USBA;
-#endif /* CONFIG_WPS_UFD */
+#ifdef CONFIG_WPS2
+ methods |= WPS_CONFIG_VIRT_DISPLAY;
+#endif /* CONFIG_WPS2 */
#ifdef CONFIG_WPS_NFC
methods |= WPS_CONFIG_NFC_INTERFACE;
#endif /* CONFIG_WPS_NFC */
} else {
- if (os_strstr(str, "usba"))
- methods |= WPS_CONFIG_USBA;
if (os_strstr(str, "ethernet"))
methods |= WPS_CONFIG_ETHERNET;
if (os_strstr(str, "label"))
@@ -629,7 +498,114 @@ u16 wps_config_methods_str2bin(const char *str)
methods |= WPS_CONFIG_PUSHBUTTON;
if (os_strstr(str, "keypad"))
methods |= WPS_CONFIG_KEYPAD;
+#ifdef CONFIG_WPS2
+ if (os_strstr(str, "virtual_display"))
+ methods |= WPS_CONFIG_VIRT_DISPLAY;
+ if (os_strstr(str, "physical_display"))
+ methods |= WPS_CONFIG_PHY_DISPLAY;
+ if (os_strstr(str, "virtual_push_button"))
+ methods |= WPS_CONFIG_VIRT_PUSHBUTTON;
+ if (os_strstr(str, "physical_push_button"))
+ methods |= WPS_CONFIG_PHY_PUSHBUTTON;
+#endif /* CONFIG_WPS2 */
}
return methods;
}
+
+
+struct wpabuf * wps_build_wsc_ack(struct wps_data *wps)
+{
+ struct wpabuf *msg;
+
+ wpa_printf(MSG_DEBUG, "WPS: Building Message WSC_ACK");
+
+ msg = wpabuf_alloc(1000);
+ if (msg == NULL)
+ return NULL;
+
+ if (wps_build_version(msg) ||
+ wps_build_msg_type(msg, WPS_WSC_ACK) ||
+ wps_build_enrollee_nonce(wps, msg) ||
+ wps_build_registrar_nonce(wps, msg) ||
+ wps_build_wfa_ext(msg, 0, NULL, 0)) {
+ wpabuf_free(msg);
+ return NULL;
+ }
+
+ return msg;
+}
+
+
+struct wpabuf * wps_build_wsc_nack(struct wps_data *wps)
+{
+ struct wpabuf *msg;
+
+ wpa_printf(MSG_DEBUG, "WPS: Building Message WSC_NACK");
+
+ msg = wpabuf_alloc(1000);
+ if (msg == NULL)
+ return NULL;
+
+ if (wps_build_version(msg) ||
+ wps_build_msg_type(msg, WPS_WSC_NACK) ||
+ wps_build_enrollee_nonce(wps, msg) ||
+ wps_build_registrar_nonce(wps, msg) ||
+ wps_build_config_error(msg, wps->config_error) ||
+ wps_build_wfa_ext(msg, 0, NULL, 0)) {
+ wpabuf_free(msg);
+ return NULL;
+ }
+
+ return msg;
+}
+
+
+#ifdef CONFIG_WPS_NFC
+struct wpabuf * wps_nfc_token_gen(int ndef, int *id, struct wpabuf **pubkey,
+ struct wpabuf **privkey,
+ struct wpabuf **dev_pw)
+{
+ struct wpabuf *priv = NULL, *pub = NULL, *pw, *ret;
+ void *dh_ctx;
+ u16 val;
+
+ pw = wpabuf_alloc(WPS_OOB_DEVICE_PASSWORD_LEN);
+ if (pw == NULL)
+ return NULL;
+
+ if (random_get_bytes(wpabuf_put(pw, WPS_OOB_DEVICE_PASSWORD_LEN),
+ WPS_OOB_DEVICE_PASSWORD_LEN) ||
+ random_get_bytes((u8 *) &val, sizeof(val))) {
+ wpabuf_free(pw);
+ return NULL;
+ }
+
+ dh_ctx = dh5_init(&priv, &pub);
+ if (dh_ctx == NULL) {
+ wpabuf_free(pw);
+ return NULL;
+ }
+ dh5_free(dh_ctx);
+
+ *id = 0x10 + val % 0xfff0;
+ wpabuf_free(*pubkey);
+ *pubkey = pub;
+ wpabuf_free(*privkey);
+ *privkey = priv;
+ wpabuf_free(*dev_pw);
+ *dev_pw = pw;
+
+ ret = wps_build_nfc_pw_token(*id, *pubkey, *dev_pw);
+ if (ndef && ret) {
+ struct wpabuf *tmp;
+ tmp = ndef_build_wifi(ret);
+ wpabuf_free(ret);
+ if (tmp == NULL)
+ return NULL;
+ ret = tmp;
+ }
+
+ return ret;
+}
+#endif /* CONFIG_WPS_NFC */
diff --git a/contrib/wpa/src/wps/wps_defs.h b/contrib/wpa/src/wps/wps_defs.h
index 750ca41..2f42603 100644
--- a/contrib/wpa/src/wps/wps_defs.h
+++ b/contrib/wpa/src/wps/wps_defs.h
@@ -2,20 +2,28 @@
* Wi-Fi Protected Setup - message definitions
* Copyright (c) 2008, Jouni Malinen <j@w1.fi>
*
- * This program is free software; you can redistribute it and/or modify
- * it under the terms of the GNU General Public License version 2 as
- * published by the Free Software Foundation.
- *
- * Alternatively, this software may be distributed under the terms of BSD
- * license.
- *
- * See README and COPYING for more details.
+ * This software may be distributed under the terms of the BSD license.
+ * See README for more details.
*/
#ifndef WPS_DEFS_H
#define WPS_DEFS_H
+#ifdef CONFIG_WPS_TESTING
+
+extern int wps_version_number;
+extern int wps_testing_dummy_cred;
+#define WPS_VERSION wps_version_number
+
+#else /* CONFIG_WPS_TESTING */
+
+#ifdef CONFIG_WPS2
+#define WPS_VERSION 0x20
+#else /* CONFIG_WPS2 */
#define WPS_VERSION 0x10
+#endif /* CONFIG_WPS2 */
+
+#endif /* CONFIG_WPS_TESTING */
/* Diffie-Hellman 1536-bit MODP Group; RFC 3526, Group 5 */
#define WPS_DH_GROUP 5
@@ -33,7 +41,7 @@
#define WPS_MGMTAUTHKEY_LEN 32
#define WPS_MGMTENCKEY_LEN 16
#define WPS_MGMT_KEY_ID_LEN 16
-#define WPS_OOB_DEVICE_PASSWORD_ATTR_LEN 54
+#define WPS_OOB_DEVICE_PASSWORD_MIN_LEN 16
#define WPS_OOB_DEVICE_PASSWORD_LEN 32
#define WPS_OOB_PUBKEY_HASH_LEN 20
@@ -124,7 +132,20 @@ enum wps_attribute {
ATTR_KEY_PROVIDED_AUTO = 0x1061,
ATTR_802_1X_ENABLED = 0x1062,
ATTR_APPSESSIONKEY = 0x1063,
- ATTR_WEPTRANSMITKEY = 0x1064
+ ATTR_WEPTRANSMITKEY = 0x1064,
+ ATTR_REQUESTED_DEV_TYPE = 0x106a,
+ ATTR_EXTENSIBILITY_TEST = 0x10fa /* _NOT_ defined in the spec */
+};
+
+#define WPS_VENDOR_ID_WFA 14122
+
+/* WFA Vendor Extension subelements */
+enum {
+ WFA_ELEM_VERSION2 = 0x00,
+ WFA_ELEM_AUTHORIZEDMACS = 0x01,
+ WFA_ELEM_NETWORK_KEY_SHAREABLE = 0x02,
+ WFA_ELEM_REQUEST_TO_ENROLL = 0x03,
+ WFA_ELEM_SETTINGS_DELAY_TIME = 0x04
};
/* Device Password ID */
@@ -197,6 +218,14 @@ enum wps_config_error {
WPS_CFG_DEV_PASSWORD_AUTH_FAILURE = 18
};
+/* Vendor specific Error Indication for WPS event messages */
+enum wps_error_indication {
+ WPS_EI_NO_ERROR,
+ WPS_EI_SECURITY_TKIP_ONLY_PROHIBITED,
+ WPS_EI_SECURITY_WEP_PROHIBITED,
+ NUM_WPS_EI_VALUES
+};
+
/* RF Bands */
#define WPS_RF_24GHZ 0x01
#define WPS_RF_50GHZ 0x02
@@ -211,6 +240,12 @@ enum wps_config_error {
#define WPS_CONFIG_NFC_INTERFACE 0x0040
#define WPS_CONFIG_PUSHBUTTON 0x0080
#define WPS_CONFIG_KEYPAD 0x0100
+#ifdef CONFIG_WPS2
+#define WPS_CONFIG_VIRT_PUSHBUTTON 0x0280
+#define WPS_CONFIG_PHY_PUSHBUTTON 0x0480
+#define WPS_CONFIG_VIRT_DISPLAY 0x2008
+#define WPS_CONFIG_PHY_DISPLAY 0x4008
+#endif /* CONFIG_WPS2 */
/* Connection Type Flags */
#define WPS_CONN_ESS 0x01
@@ -290,4 +325,6 @@ enum wps_response_type {
/* Walk Time for push button configuration (in seconds) */
#define WPS_PBC_WALK_TIME 120
+#define WPS_MAX_AUTHORIZED_MACS 5
+
#endif /* WPS_DEFS_H */
diff --git a/contrib/wpa/src/wps/wps_dev_attr.c b/contrib/wpa/src/wps/wps_dev_attr.c
index 090bfa2..3c94a43 100644
--- a/contrib/wpa/src/wps/wps_dev_attr.c
+++ b/contrib/wpa/src/wps/wps_dev_attr.c
@@ -2,14 +2,8 @@
* Wi-Fi Protected Setup - device attributes
* Copyright (c) 2008, Jouni Malinen <j@w1.fi>
*
- * This program is free software; you can redistribute it and/or modify
- * it under the terms of the GNU General Public License version 2 as
- * published by the Free Software Foundation.
- *
- * Alternatively, this software may be distributed under the terms of BSD
- * license.
- *
- * See README and COPYING for more details.
+ * This software may be distributed under the terms of the BSD license.
+ * See README for more details.
*/
#include "includes.h"
@@ -19,71 +13,74 @@
#include "wps_dev_attr.h"
-static int wps_build_manufacturer(struct wps_device_data *dev,
- struct wpabuf *msg)
+int wps_build_manufacturer(struct wps_device_data *dev, struct wpabuf *msg)
{
size_t len;
wpa_printf(MSG_DEBUG, "WPS: * Manufacturer");
wpabuf_put_be16(msg, ATTR_MANUFACTURER);
len = dev->manufacturer ? os_strlen(dev->manufacturer) : 0;
+#ifndef CONFIG_WPS_STRICT
if (len == 0) {
/*
* Some deployed WPS implementations fail to parse zero-length
- * attributes. As a workaround, send a null character if the
+ * attributes. As a workaround, send a space character if the
* device attribute string is empty.
*/
wpabuf_put_be16(msg, 1);
- wpabuf_put_u8(msg, '\0');
- } else {
- wpabuf_put_be16(msg, len);
- wpabuf_put_data(msg, dev->manufacturer, len);
+ wpabuf_put_u8(msg, ' ');
+ return 0;
}
+#endif /* CONFIG_WPS_STRICT */
+ wpabuf_put_be16(msg, len);
+ wpabuf_put_data(msg, dev->manufacturer, len);
return 0;
}
-static int wps_build_model_name(struct wps_device_data *dev,
- struct wpabuf *msg)
+int wps_build_model_name(struct wps_device_data *dev, struct wpabuf *msg)
{
size_t len;
wpa_printf(MSG_DEBUG, "WPS: * Model Name");
wpabuf_put_be16(msg, ATTR_MODEL_NAME);
len = dev->model_name ? os_strlen(dev->model_name) : 0;
+#ifndef CONFIG_WPS_STRICT
if (len == 0) {
/*
* Some deployed WPS implementations fail to parse zero-length
- * attributes. As a workaround, send a null character if the
+ * attributes. As a workaround, send a space character if the
* device attribute string is empty.
*/
wpabuf_put_be16(msg, 1);
- wpabuf_put_u8(msg, '\0');
- } else {
- wpabuf_put_be16(msg, len);
- wpabuf_put_data(msg, dev->model_name, len);
+ wpabuf_put_u8(msg, ' ');
+ return 0;
}
+#endif /* CONFIG_WPS_STRICT */
+ wpabuf_put_be16(msg, len);
+ wpabuf_put_data(msg, dev->model_name, len);
return 0;
}
-static int wps_build_model_number(struct wps_device_data *dev,
- struct wpabuf *msg)
+int wps_build_model_number(struct wps_device_data *dev, struct wpabuf *msg)
{
size_t len;
wpa_printf(MSG_DEBUG, "WPS: * Model Number");
wpabuf_put_be16(msg, ATTR_MODEL_NUMBER);
len = dev->model_number ? os_strlen(dev->model_number) : 0;
+#ifndef CONFIG_WPS_STRICT
if (len == 0) {
/*
* Some deployed WPS implementations fail to parse zero-length
- * attributes. As a workaround, send a null character if the
+ * attributes. As a workaround, send a space character if the
* device attribute string is empty.
*/
wpabuf_put_be16(msg, 1);
- wpabuf_put_u8(msg, '\0');
- } else {
- wpabuf_put_be16(msg, len);
- wpabuf_put_data(msg, dev->model_number, len);
+ wpabuf_put_u8(msg, ' ');
+ return 0;
}
+#endif /* CONFIG_WPS_STRICT */
+ wpabuf_put_be16(msg, len);
+ wpabuf_put_data(msg, dev->model_number, len);
return 0;
}
@@ -95,18 +92,20 @@ static int wps_build_serial_number(struct wps_device_data *dev,
wpa_printf(MSG_DEBUG, "WPS: * Serial Number");
wpabuf_put_be16(msg, ATTR_SERIAL_NUMBER);
len = dev->serial_number ? os_strlen(dev->serial_number) : 0;
+#ifndef CONFIG_WPS_STRICT
if (len == 0) {
/*
* Some deployed WPS implementations fail to parse zero-length
- * attributes. As a workaround, send a null character if the
+ * attributes. As a workaround, send a space character if the
* device attribute string is empty.
*/
wpabuf_put_be16(msg, 1);
- wpabuf_put_u8(msg, '\0');
- } else {
- wpabuf_put_be16(msg, len);
- wpabuf_put_data(msg, dev->serial_number, len);
+ wpabuf_put_u8(msg, ' ');
+ return 0;
}
+#endif /* CONFIG_WPS_STRICT */
+ wpabuf_put_be16(msg, len);
+ wpabuf_put_data(msg, dev->serial_number, len);
return 0;
}
@@ -121,24 +120,62 @@ int wps_build_primary_dev_type(struct wps_device_data *dev, struct wpabuf *msg)
}
-static int wps_build_dev_name(struct wps_device_data *dev, struct wpabuf *msg)
+int wps_build_secondary_dev_type(struct wps_device_data *dev,
+ struct wpabuf *msg)
+{
+ if (!dev->num_sec_dev_types)
+ return 0;
+
+ wpa_printf(MSG_DEBUG, "WPS: * Secondary Device Type");
+ wpabuf_put_be16(msg, ATTR_SECONDARY_DEV_TYPE_LIST);
+ wpabuf_put_be16(msg, WPS_DEV_TYPE_LEN * dev->num_sec_dev_types);
+ wpabuf_put_data(msg, dev->sec_dev_type,
+ WPS_DEV_TYPE_LEN * dev->num_sec_dev_types);
+
+ return 0;
+}
+
+
+int wps_build_req_dev_type(struct wps_device_data *dev, struct wpabuf *msg,
+ unsigned int num_req_dev_types,
+ const u8 *req_dev_types)
+{
+ unsigned int i;
+
+ for (i = 0; i < num_req_dev_types; i++) {
+ wpa_hexdump(MSG_DEBUG, "WPS: * Requested Device Type",
+ req_dev_types + i * WPS_DEV_TYPE_LEN,
+ WPS_DEV_TYPE_LEN);
+ wpabuf_put_be16(msg, ATTR_REQUESTED_DEV_TYPE);
+ wpabuf_put_be16(msg, WPS_DEV_TYPE_LEN);
+ wpabuf_put_data(msg, req_dev_types + i * WPS_DEV_TYPE_LEN,
+ WPS_DEV_TYPE_LEN);
+ }
+
+ return 0;
+}
+
+
+int wps_build_dev_name(struct wps_device_data *dev, struct wpabuf *msg)
{
size_t len;
wpa_printf(MSG_DEBUG, "WPS: * Device Name");
wpabuf_put_be16(msg, ATTR_DEV_NAME);
len = dev->device_name ? os_strlen(dev->device_name) : 0;
+#ifndef CONFIG_WPS_STRICT
if (len == 0) {
/*
* Some deployed WPS implementations fail to parse zero-length
- * attributes. As a workaround, send a null character if the
+ * attributes. As a workaround, send a space character if the
* device attribute string is empty.
*/
wpabuf_put_be16(msg, 1);
- wpabuf_put_u8(msg, '\0');
- } else {
- wpabuf_put_be16(msg, len);
- wpabuf_put_data(msg, dev->device_name, len);
+ wpabuf_put_u8(msg, ' ');
+ return 0;
}
+#endif /* CONFIG_WPS_STRICT */
+ wpabuf_put_be16(msg, len);
+ wpabuf_put_data(msg, dev->device_name, len);
return 0;
}
@@ -166,6 +203,20 @@ int wps_build_os_version(struct wps_device_data *dev, struct wpabuf *msg)
}
+int wps_build_vendor_ext_m1(struct wps_device_data *dev, struct wpabuf *msg)
+{
+ if (dev->vendor_ext_m1 != NULL) {
+ wpa_hexdump(MSG_DEBUG, "WPS: * Vendor Extension M1",
+ wpabuf_head_u8(dev->vendor_ext_m1),
+ wpabuf_len(dev->vendor_ext_m1));
+ wpabuf_put_be16(msg, ATTR_VENDOR_EXT);
+ wpabuf_put_be16(msg, wpabuf_len(dev->vendor_ext_m1));
+ wpabuf_put_buf(msg, dev->vendor_ext_m1);
+ }
+ return 0;
+}
+
+
int wps_build_rf_bands(struct wps_device_data *dev, struct wpabuf *msg)
{
wpa_printf(MSG_DEBUG, "WPS: * RF Bands (%x)", dev->rf_bands);
@@ -176,6 +227,25 @@ int wps_build_rf_bands(struct wps_device_data *dev, struct wpabuf *msg)
}
+int wps_build_vendor_ext(struct wps_device_data *dev, struct wpabuf *msg)
+{
+ int i;
+
+ for (i = 0; i < MAX_WPS_VENDOR_EXTENSIONS; i++) {
+ if (dev->vendor_ext[i] == NULL)
+ continue;
+ wpa_hexdump(MSG_DEBUG, "WPS: * Vendor Extension",
+ wpabuf_head_u8(dev->vendor_ext[i]),
+ wpabuf_len(dev->vendor_ext[i]));
+ wpabuf_put_be16(msg, ATTR_VENDOR_EXT);
+ wpabuf_put_be16(msg, wpabuf_len(dev->vendor_ext[i]));
+ wpabuf_put_buf(msg, dev->vendor_ext[i]);
+ }
+
+ return 0;
+}
+
+
static int wps_process_manufacturer(struct wps_device_data *dev, const u8 *str,
size_t str_len)
{
diff --git a/contrib/wpa/src/wps/wps_dev_attr.h b/contrib/wpa/src/wps/wps_dev_attr.h
index a9c16ea..200c9c4 100644
--- a/contrib/wpa/src/wps/wps_dev_attr.h
+++ b/contrib/wpa/src/wps/wps_dev_attr.h
@@ -2,14 +2,8 @@
* Wi-Fi Protected Setup - device attributes
* Copyright (c) 2008, Jouni Malinen <j@w1.fi>
*
- * This program is free software; you can redistribute it and/or modify
- * it under the terms of the GNU General Public License version 2 as
- * published by the Free Software Foundation.
- *
- * Alternatively, this software may be distributed under the terms of BSD
- * license.
- *
- * See README and COPYING for more details.
+ * This software may be distributed under the terms of the BSD license.
+ * See README for more details.
*/
#ifndef WPS_DEV_ATTR_H
@@ -17,11 +11,19 @@
struct wps_parse_attr;
+int wps_build_manufacturer(struct wps_device_data *dev, struct wpabuf *msg);
+int wps_build_model_name(struct wps_device_data *dev, struct wpabuf *msg);
+int wps_build_model_number(struct wps_device_data *dev, struct wpabuf *msg);
+int wps_build_dev_name(struct wps_device_data *dev, struct wpabuf *msg);
int wps_build_device_attrs(struct wps_device_data *dev, struct wpabuf *msg);
int wps_build_os_version(struct wps_device_data *dev, struct wpabuf *msg);
+int wps_build_vendor_ext_m1(struct wps_device_data *dev, struct wpabuf *msg);
int wps_build_rf_bands(struct wps_device_data *dev, struct wpabuf *msg);
int wps_build_primary_dev_type(struct wps_device_data *dev,
struct wpabuf *msg);
+int wps_build_secondary_dev_type(struct wps_device_data *dev,
+ struct wpabuf *msg);
+int wps_build_dev_name(struct wps_device_data *dev, struct wpabuf *msg);
int wps_process_device_attrs(struct wps_device_data *dev,
struct wps_parse_attr *attr);
int wps_process_os_version(struct wps_device_data *dev, const u8 *ver);
@@ -29,5 +31,9 @@ int wps_process_rf_bands(struct wps_device_data *dev, const u8 *bands);
void wps_device_data_dup(struct wps_device_data *dst,
const struct wps_device_data *src);
void wps_device_data_free(struct wps_device_data *dev);
+int wps_build_vendor_ext(struct wps_device_data *dev, struct wpabuf *msg);
+int wps_build_req_dev_type(struct wps_device_data *dev, struct wpabuf *msg,
+ unsigned int num_req_dev_types,
+ const u8 *req_dev_types);
#endif /* WPS_DEV_ATTR_H */
diff --git a/contrib/wpa/src/wps/wps_enrollee.c b/contrib/wpa/src/wps/wps_enrollee.c
index dff24d4..837b941 100644
--- a/contrib/wpa/src/wps/wps_enrollee.c
+++ b/contrib/wpa/src/wps/wps_enrollee.c
@@ -2,14 +2,8 @@
* Wi-Fi Protected Setup - Enrollee
* Copyright (c) 2008, Jouni Malinen <j@w1.fi>
*
- * This program is free software; you can redistribute it and/or modify
- * it under the terms of the GNU General Public License version 2 as
- * published by the Free Software Foundation.
- *
- * Alternatively, this software may be distributed under the terms of BSD
- * license.
- *
- * See README and COPYING for more details.
+ * This software may be distributed under the terms of the BSD license.
+ * See README for more details.
*/
#include "includes.h"
@@ -17,6 +11,7 @@
#include "common.h"
#include "crypto/crypto.h"
#include "crypto/sha256.h"
+#include "crypto/random.h"
#include "wps_i.h"
#include "wps_dev_attr.h"
@@ -53,7 +48,7 @@ static int wps_build_e_hash(struct wps_data *wps, struct wpabuf *msg)
const u8 *addr[4];
size_t len[4];
- if (os_get_random(wps->snonce, 2 * WPS_SECRET_NONCE_LEN) < 0)
+ if (random_get_bytes(wps->snonce, 2 * WPS_SECRET_NONCE_LEN) < 0)
return -1;
wpa_hexdump(MSG_DEBUG, "WPS: E-S1", wps->snonce, WPS_SECRET_NONCE_LEN);
wpa_hexdump(MSG_DEBUG, "WPS: E-S2",
@@ -119,8 +114,9 @@ static int wps_build_e_snonce2(struct wps_data *wps, struct wpabuf *msg)
static struct wpabuf * wps_build_m1(struct wps_data *wps)
{
struct wpabuf *msg;
+ u16 config_methods;
- if (os_get_random(wps->nonce_e, WPS_NONCE_LEN) < 0)
+ if (random_get_bytes(wps->nonce_e, WPS_NONCE_LEN) < 0)
return NULL;
wpa_hexdump(MSG_DEBUG, "WPS: Enrollee Nonce",
wps->nonce_e, WPS_NONCE_LEN);
@@ -130,6 +126,26 @@ static struct wpabuf * wps_build_m1(struct wps_data *wps)
if (msg == NULL)
return NULL;
+ config_methods = wps->wps->config_methods;
+ if (wps->wps->ap && !wps->pbc_in_m1 &&
+ (wps->dev_password_len != 0 ||
+ (config_methods & WPS_CONFIG_DISPLAY))) {
+ /*
+ * These are the methods that the AP supports as an Enrollee
+ * for adding external Registrars, so remove PushButton.
+ *
+ * As a workaround for Windows 7 mechanism for probing WPS
+ * capabilities from M1, leave PushButton option if no PIN
+ * method is available or if WPS configuration enables PBC
+ * workaround.
+ */
+ config_methods &= ~WPS_CONFIG_PUSHBUTTON;
+#ifdef CONFIG_WPS2
+ config_methods &= ~(WPS_CONFIG_VIRT_PUSHBUTTON |
+ WPS_CONFIG_PHY_PUSHBUTTON);
+#endif /* CONFIG_WPS2 */
+ }
+
if (wps_build_version(msg) ||
wps_build_msg_type(msg, WPS_M1) ||
wps_build_uuid_e(msg, wps->uuid_e) ||
@@ -139,14 +155,16 @@ static struct wpabuf * wps_build_m1(struct wps_data *wps)
wps_build_auth_type_flags(wps, msg) ||
wps_build_encr_type_flags(wps, msg) ||
wps_build_conn_type_flags(wps, msg) ||
- wps_build_config_methods(msg, wps->wps->config_methods) ||
+ wps_build_config_methods(msg, config_methods) ||
wps_build_wps_state(wps, msg) ||
wps_build_device_attrs(&wps->wps->dev, msg) ||
wps_build_rf_bands(&wps->wps->dev, msg) ||
wps_build_assoc_state(wps, msg) ||
wps_build_dev_password_id(msg, wps->dev_pw_id) ||
wps_build_config_error(msg, WPS_CFG_NO_ERROR) ||
- wps_build_os_version(&wps->wps->dev, msg)) {
+ wps_build_os_version(&wps->wps->dev, msg) ||
+ wps_build_wfa_ext(msg, 0, NULL, 0) ||
+ wps_build_vendor_ext_m1(&wps->wps->dev, msg)) {
wpabuf_free(msg);
return NULL;
}
@@ -176,6 +194,7 @@ static struct wpabuf * wps_build_m3(struct wps_data *wps)
wps_build_msg_type(msg, WPS_M3) ||
wps_build_registrar_nonce(wps, msg) ||
wps_build_e_hash(wps, msg) ||
+ wps_build_wfa_ext(msg, 0, NULL, 0) ||
wps_build_authenticator(wps, msg)) {
wpabuf_free(msg);
return NULL;
@@ -208,6 +227,7 @@ static struct wpabuf * wps_build_m5(struct wps_data *wps)
wps_build_e_snonce1(wps, plain) ||
wps_build_key_wrap_auth(wps, plain) ||
wps_build_encr_settings(wps, msg, plain) ||
+ wps_build_wfa_ext(msg, 0, NULL, 0) ||
wps_build_authenticator(wps, msg)) {
wpabuf_free(plain);
wpabuf_free(msg);
@@ -232,20 +252,47 @@ static int wps_build_cred_ssid(struct wps_data *wps, struct wpabuf *msg)
static int wps_build_cred_auth_type(struct wps_data *wps, struct wpabuf *msg)
{
- wpa_printf(MSG_DEBUG, "WPS: * Authentication Type");
+ u16 auth_type = wps->wps->auth_types;
+
+ /* Select the best authentication type */
+ if (auth_type & WPS_AUTH_WPA2PSK)
+ auth_type = WPS_AUTH_WPA2PSK;
+ else if (auth_type & WPS_AUTH_WPAPSK)
+ auth_type = WPS_AUTH_WPAPSK;
+ else if (auth_type & WPS_AUTH_OPEN)
+ auth_type = WPS_AUTH_OPEN;
+ else if (auth_type & WPS_AUTH_SHARED)
+ auth_type = WPS_AUTH_SHARED;
+
+ wpa_printf(MSG_DEBUG, "WPS: * Authentication Type (0x%x)", auth_type);
wpabuf_put_be16(msg, ATTR_AUTH_TYPE);
wpabuf_put_be16(msg, 2);
- wpabuf_put_be16(msg, wps->wps->auth_types);
+ wpabuf_put_be16(msg, auth_type);
return 0;
}
static int wps_build_cred_encr_type(struct wps_data *wps, struct wpabuf *msg)
{
- wpa_printf(MSG_DEBUG, "WPS: * Encryption Type");
+ u16 encr_type = wps->wps->encr_types;
+
+ /* Select the best encryption type */
+ if (wps->wps->auth_types & (WPS_AUTH_WPA2PSK | WPS_AUTH_WPAPSK)) {
+ if (encr_type & WPS_ENCR_AES)
+ encr_type = WPS_ENCR_AES;
+ else if (encr_type & WPS_ENCR_TKIP)
+ encr_type = WPS_ENCR_TKIP;
+ } else {
+ if (encr_type & WPS_ENCR_WEP)
+ encr_type = WPS_ENCR_WEP;
+ else if (encr_type & WPS_ENCR_NONE)
+ encr_type = WPS_ENCR_NONE;
+ }
+
+ wpa_printf(MSG_DEBUG, "WPS: * Encryption Type (0x%x)", encr_type);
wpabuf_put_be16(msg, ATTR_ENCR_TYPE);
wpabuf_put_be16(msg, 2);
- wpabuf_put_be16(msg, wps->wps->encr_types);
+ wpabuf_put_be16(msg, encr_type);
return 0;
}
@@ -310,6 +357,7 @@ static struct wpabuf * wps_build_m7(struct wps_data *wps)
(wps->wps->ap && wps_build_ap_settings(wps, plain)) ||
wps_build_key_wrap_auth(wps, plain) ||
wps_build_encr_settings(wps, msg, plain) ||
+ wps_build_wfa_ext(msg, 0, NULL, 0) ||
wps_build_authenticator(wps, msg)) {
wpabuf_free(plain);
wpabuf_free(msg);
@@ -345,7 +393,8 @@ static struct wpabuf * wps_build_wsc_done(struct wps_data *wps)
if (wps_build_version(msg) ||
wps_build_msg_type(msg, WPS_WSC_DONE) ||
wps_build_enrollee_nonce(wps, msg) ||
- wps_build_registrar_nonce(wps, msg)) {
+ wps_build_registrar_nonce(wps, msg) ||
+ wps_build_wfa_ext(msg, 0, NULL, 0)) {
wpabuf_free(msg);
return NULL;
}
@@ -360,51 +409,6 @@ static struct wpabuf * wps_build_wsc_done(struct wps_data *wps)
}
-static struct wpabuf * wps_build_wsc_ack(struct wps_data *wps)
-{
- struct wpabuf *msg;
-
- wpa_printf(MSG_DEBUG, "WPS: Building Message WSC_ACK");
-
- msg = wpabuf_alloc(1000);
- if (msg == NULL)
- return NULL;
-
- if (wps_build_version(msg) ||
- wps_build_msg_type(msg, WPS_WSC_ACK) ||
- wps_build_enrollee_nonce(wps, msg) ||
- wps_build_registrar_nonce(wps, msg)) {
- wpabuf_free(msg);
- return NULL;
- }
-
- return msg;
-}
-
-
-static struct wpabuf * wps_build_wsc_nack(struct wps_data *wps)
-{
- struct wpabuf *msg;
-
- wpa_printf(MSG_DEBUG, "WPS: Building Message WSC_NACK");
-
- msg = wpabuf_alloc(1000);
- if (msg == NULL)
- return NULL;
-
- if (wps_build_version(msg) ||
- wps_build_msg_type(msg, WPS_WSC_NACK) ||
- wps_build_enrollee_nonce(wps, msg) ||
- wps_build_registrar_nonce(wps, msg) ||
- wps_build_config_error(msg, wps->config_error)) {
- wpabuf_free(msg);
- return NULL;
- }
-
- return msg;
-}
-
-
struct wpabuf * wps_enrollee_get_msg(struct wps_data *wps,
enum wsc_op_code *op_code)
{
@@ -519,23 +523,6 @@ static int wps_process_pubkey(struct wps_data *wps, const u8 *pk,
return -1;
}
-#ifdef CONFIG_WPS_OOB
- if (wps->dev_pw_id != DEV_PW_DEFAULT &&
- wps->wps->oob_conf.pubkey_hash) {
- const u8 *addr[1];
- u8 hash[WPS_HASH_LEN];
-
- addr[0] = pk;
- sha256_vector(1, addr, &pk_len, hash);
- if (os_memcmp(hash,
- wpabuf_head(wps->wps->oob_conf.pubkey_hash),
- WPS_OOB_PUBKEY_HASH_LEN) != 0) {
- wpa_printf(MSG_ERROR, "WPS: Public Key hash error");
- return -1;
- }
- }
-#endif /* CONFIG_WPS_OOB */
-
wpabuf_free(wps->dh_pubkey_r);
wps->dh_pubkey_r = wpabuf_alloc_copy(pk, pk_len);
if (wps->dh_pubkey_r == NULL)
@@ -657,10 +644,11 @@ static int wps_process_r_snonce2(struct wps_data *wps, const u8 *r_snonce2)
static int wps_process_cred_e(struct wps_data *wps, const u8 *cred,
- size_t cred_len)
+ size_t cred_len, int wps2)
{
struct wps_parse_attr attr;
struct wpabuf msg;
+ int ret = 0;
wpa_printf(MSG_DEBUG, "WPS: Received Credential");
os_memset(&wps->cred, 0, sizeof(wps->cred));
@@ -682,24 +670,48 @@ static int wps_process_cred_e(struct wps_data *wps, const u8 *cred,
* reasons, allow this to be processed since we do not really
* use the MAC Address information for anything.
*/
+#ifdef CONFIG_WPS_STRICT
+ if (wps2) {
+ wpa_printf(MSG_INFO, "WPS: Do not accept incorrect "
+ "MAC Address in AP Settings");
+ return -1;
+ }
+#endif /* CONFIG_WPS_STRICT */
}
+#ifdef CONFIG_WPS2
+ if (!(wps->cred.encr_type &
+ (WPS_ENCR_NONE | WPS_ENCR_TKIP | WPS_ENCR_AES))) {
+ if (wps->cred.encr_type & WPS_ENCR_WEP) {
+ wpa_printf(MSG_INFO, "WPS: Reject Credential "
+ "due to WEP configuration");
+ wps->error_indication = WPS_EI_SECURITY_WEP_PROHIBITED;
+ return -2;
+ }
+
+ wpa_printf(MSG_INFO, "WPS: Reject Credential due to "
+ "invalid encr_type 0x%x", wps->cred.encr_type);
+ return -1;
+ }
+#endif /* CONFIG_WPS2 */
+
if (wps->wps->cred_cb) {
wps->cred.cred_attr = cred - 4;
wps->cred.cred_attr_len = cred_len + 4;
- wps->wps->cred_cb(wps->wps->cb_ctx, &wps->cred);
+ ret = wps->wps->cred_cb(wps->wps->cb_ctx, &wps->cred);
wps->cred.cred_attr = NULL;
wps->cred.cred_attr_len = 0;
}
- return 0;
+ return ret;
}
static int wps_process_creds(struct wps_data *wps, const u8 *cred[],
- size_t cred_len[], size_t num_cred)
+ size_t cred_len[], size_t num_cred, int wps2)
{
size_t i;
+ int ok = 0;
if (wps->wps->ap)
return 0;
@@ -711,17 +723,29 @@ static int wps_process_creds(struct wps_data *wps, const u8 *cred[],
}
for (i = 0; i < num_cred; i++) {
- if (wps_process_cred_e(wps, cred[i], cred_len[i]))
+ int res;
+ res = wps_process_cred_e(wps, cred[i], cred_len[i], wps2);
+ if (res == 0)
+ ok++;
+ else if (res == -2)
+ wpa_printf(MSG_DEBUG, "WPS: WEP credential skipped");
+ else
return -1;
}
+ if (ok == 0) {
+ wpa_printf(MSG_DEBUG, "WPS: No valid Credential attribute "
+ "received");
+ return -1;
+ }
+
return 0;
}
static int wps_process_ap_settings_e(struct wps_data *wps,
struct wps_parse_attr *attr,
- struct wpabuf *attrs)
+ struct wpabuf *attrs, int wps2)
{
struct wps_credential cred;
@@ -747,7 +771,61 @@ static int wps_process_ap_settings_e(struct wps_data *wps,
* reasons, allow this to be processed since we do not really
* use the MAC Address information for anything.
*/
+#ifdef CONFIG_WPS_STRICT
+ if (wps2) {
+ wpa_printf(MSG_INFO, "WPS: Do not accept incorrect "
+ "MAC Address in AP Settings");
+ return -1;
+ }
+#endif /* CONFIG_WPS_STRICT */
+ }
+
+#ifdef CONFIG_WPS2
+ if (!(cred.encr_type & (WPS_ENCR_NONE | WPS_ENCR_TKIP | WPS_ENCR_AES)))
+ {
+ if (cred.encr_type & WPS_ENCR_WEP) {
+ wpa_printf(MSG_INFO, "WPS: Reject new AP settings "
+ "due to WEP configuration");
+ wps->error_indication = WPS_EI_SECURITY_WEP_PROHIBITED;
+ return -1;
+ }
+
+ wpa_printf(MSG_INFO, "WPS: Reject new AP settings due to "
+ "invalid encr_type 0x%x", cred.encr_type);
+ return -1;
+ }
+#endif /* CONFIG_WPS2 */
+
+#ifdef CONFIG_WPS_STRICT
+ if (wps2) {
+ if ((cred.encr_type & (WPS_ENCR_TKIP | WPS_ENCR_AES)) ==
+ WPS_ENCR_TKIP ||
+ (cred.auth_type & (WPS_AUTH_WPAPSK | WPS_AUTH_WPA2PSK)) ==
+ WPS_AUTH_WPAPSK) {
+ wpa_printf(MSG_INFO, "WPS-STRICT: Invalid WSC 2.0 "
+ "AP Settings: WPA-Personal/TKIP only");
+ wps->error_indication =
+ WPS_EI_SECURITY_TKIP_ONLY_PROHIBITED;
+ return -1;
+ }
+ }
+#endif /* CONFIG_WPS_STRICT */
+
+#ifdef CONFIG_WPS2
+ if ((cred.encr_type & (WPS_ENCR_TKIP | WPS_ENCR_AES)) == WPS_ENCR_TKIP)
+ {
+ wpa_printf(MSG_DEBUG, "WPS: Upgrade encr_type TKIP -> "
+ "TKIP+AES");
+ cred.encr_type |= WPS_ENCR_AES;
+ }
+
+ if ((cred.auth_type & (WPS_AUTH_WPAPSK | WPS_AUTH_WPA2PSK)) ==
+ WPS_AUTH_WPAPSK) {
+ wpa_printf(MSG_DEBUG, "WPS: Upgrade auth_type WPAPSK -> "
+ "WPAPSK+WPA2PSK");
+ cred.auth_type |= WPS_AUTH_WPA2PSK;
}
+#endif /* CONFIG_WPS2 */
if (wps->wps->cred_cb) {
cred.cred_attr = wpabuf_head(attrs);
@@ -779,8 +857,15 @@ static enum wps_process_res wps_process_m2(struct wps_data *wps,
return WPS_CONTINUE;
}
+ /*
+ * Stop here on an AP as an Enrollee if AP Setup is locked unless the
+ * special locked mode is used to allow protocol run up to M7 in order
+ * to support external Registrars that only learn the current AP
+ * configuration without changing it.
+ */
if (wps->wps->ap &&
- (wps->wps->ap_setup_locked || wps->dev_password == NULL)) {
+ ((wps->wps->ap_setup_locked && wps->wps->ap_setup_locked != 2) ||
+ wps->dev_password == NULL)) {
wpa_printf(MSG_DEBUG, "WPS: AP Setup is locked - refuse "
"registration of a new Registrar");
wps->config_error = WPS_CFG_SETUP_LOCKED;
@@ -888,6 +973,12 @@ static enum wps_process_res wps_process_m4(struct wps_data *wps,
return WPS_CONTINUE;
}
+ if (wps_validate_m4_encr(decrypted, attr->version2 != NULL) < 0) {
+ wpabuf_free(decrypted);
+ wps->state = SEND_WSC_NACK;
+ return WPS_CONTINUE;
+ }
+
wpa_printf(MSG_DEBUG, "WPS: Processing decrypted Encrypted Settings "
"attribute");
if (wps_parse_msg(decrypted, &eattr) < 0 ||
@@ -935,6 +1026,12 @@ static enum wps_process_res wps_process_m6(struct wps_data *wps,
return WPS_CONTINUE;
}
+ if (wps_validate_m6_encr(decrypted, attr->version2 != NULL) < 0) {
+ wpabuf_free(decrypted);
+ wps->state = SEND_WSC_NACK;
+ return WPS_CONTINUE;
+ }
+
wpa_printf(MSG_DEBUG, "WPS: Processing decrypted Encrypted Settings "
"attribute");
if (wps_parse_msg(decrypted, &eattr) < 0 ||
@@ -946,6 +1043,10 @@ static enum wps_process_res wps_process_m6(struct wps_data *wps,
}
wpabuf_free(decrypted);
+ if (wps->wps->ap)
+ wps->wps->event_cb(wps->wps->cb_ctx, WPS_EV_AP_PIN_SUCCESS,
+ NULL);
+
wps->state = SEND_M7;
return WPS_CONTINUE;
}
@@ -973,6 +1074,19 @@ static enum wps_process_res wps_process_m8(struct wps_data *wps,
return WPS_CONTINUE;
}
+ if (wps->wps->ap && wps->wps->ap_setup_locked) {
+ /*
+ * Stop here if special ap_setup_locked == 2 mode allowed the
+ * protocol to continue beyond M2. This allows ER to learn the
+ * current AP settings without changing them.
+ */
+ wpa_printf(MSG_DEBUG, "WPS: AP Setup is locked - refuse "
+ "registration of a new Registrar");
+ wps->config_error = WPS_CFG_SETUP_LOCKED;
+ wps->state = SEND_WSC_NACK;
+ return WPS_CONTINUE;
+ }
+
decrypted = wps_decrypt_encr_settings(wps, attr->encr_settings,
attr->encr_settings_len);
if (decrypted == NULL) {
@@ -982,13 +1096,21 @@ static enum wps_process_res wps_process_m8(struct wps_data *wps,
return WPS_CONTINUE;
}
+ if (wps_validate_m8_encr(decrypted, wps->wps->ap,
+ attr->version2 != NULL) < 0) {
+ wpabuf_free(decrypted);
+ wps->state = SEND_WSC_NACK;
+ return WPS_CONTINUE;
+ }
+
wpa_printf(MSG_DEBUG, "WPS: Processing decrypted Encrypted Settings "
"attribute");
if (wps_parse_msg(decrypted, &eattr) < 0 ||
wps_process_key_wrap_auth(wps, decrypted, eattr.key_wrap_auth) ||
wps_process_creds(wps, eattr.cred, eattr.cred_len,
- eattr.num_cred) ||
- wps_process_ap_settings_e(wps, &eattr, decrypted)) {
+ eattr.num_cred, attr->version2 != NULL) ||
+ wps_process_ap_settings_e(wps, &eattr, decrypted,
+ attr->version2 != NULL)) {
wpabuf_free(decrypted);
wps->state = SEND_WSC_NACK;
return WPS_CONTINUE;
@@ -1011,44 +1133,52 @@ static enum wps_process_res wps_process_wsc_msg(struct wps_data *wps,
if (wps_parse_msg(msg, &attr) < 0)
return WPS_FAILURE;
- if (!wps_version_supported(attr.version)) {
- wpa_printf(MSG_DEBUG, "WPS: Unsupported message version 0x%x",
- attr.version ? *attr.version : 0);
- return WPS_FAILURE;
- }
-
if (attr.enrollee_nonce == NULL ||
- os_memcmp(wps->nonce_e, attr.enrollee_nonce, WPS_NONCE_LEN != 0)) {
+ os_memcmp(wps->nonce_e, attr.enrollee_nonce, WPS_NONCE_LEN) != 0) {
wpa_printf(MSG_DEBUG, "WPS: Mismatch in enrollee nonce");
return WPS_FAILURE;
}
if (attr.msg_type == NULL) {
wpa_printf(MSG_DEBUG, "WPS: No Message Type attribute");
- return WPS_FAILURE;
+ wps->state = SEND_WSC_NACK;
+ return WPS_CONTINUE;
}
switch (*attr.msg_type) {
case WPS_M2:
+ if (wps_validate_m2(msg) < 0)
+ return WPS_FAILURE;
ret = wps_process_m2(wps, msg, &attr);
break;
case WPS_M2D:
+ if (wps_validate_m2d(msg) < 0)
+ return WPS_FAILURE;
ret = wps_process_m2d(wps, &attr);
break;
case WPS_M4:
+ if (wps_validate_m4(msg) < 0)
+ return WPS_FAILURE;
ret = wps_process_m4(wps, msg, &attr);
if (ret == WPS_FAILURE || wps->state == SEND_WSC_NACK)
- wps_fail_event(wps->wps, WPS_M4);
+ wps_fail_event(wps->wps, WPS_M4, wps->config_error,
+ wps->error_indication);
break;
case WPS_M6:
+ if (wps_validate_m6(msg) < 0)
+ return WPS_FAILURE;
ret = wps_process_m6(wps, msg, &attr);
if (ret == WPS_FAILURE || wps->state == SEND_WSC_NACK)
- wps_fail_event(wps->wps, WPS_M6);
+ wps_fail_event(wps->wps, WPS_M6, wps->config_error,
+ wps->error_indication);
break;
case WPS_M8:
+ if (wps_validate_m8(msg) < 0)
+ return WPS_FAILURE;
ret = wps_process_m8(wps, msg, &attr);
if (ret == WPS_FAILURE || wps->state == SEND_WSC_NACK)
- wps_fail_event(wps->wps, WPS_M8);
+ wps_fail_event(wps->wps, WPS_M8, wps->config_error,
+ wps->error_indication);
break;
default:
wpa_printf(MSG_DEBUG, "WPS: Unsupported Message Type %d",
@@ -1084,12 +1214,6 @@ static enum wps_process_res wps_process_wsc_ack(struct wps_data *wps,
if (wps_parse_msg(msg, &attr) < 0)
return WPS_FAILURE;
- if (!wps_version_supported(attr.version)) {
- wpa_printf(MSG_DEBUG, "WPS: Unsupported message version 0x%x",
- attr.version ? *attr.version : 0);
- return WPS_FAILURE;
- }
-
if (attr.msg_type == NULL) {
wpa_printf(MSG_DEBUG, "WPS: No Message Type attribute");
return WPS_FAILURE;
@@ -1102,14 +1226,14 @@ static enum wps_process_res wps_process_wsc_ack(struct wps_data *wps,
}
if (attr.registrar_nonce == NULL ||
- os_memcmp(wps->nonce_r, attr.registrar_nonce, WPS_NONCE_LEN != 0))
+ os_memcmp(wps->nonce_r, attr.registrar_nonce, WPS_NONCE_LEN) != 0)
{
wpa_printf(MSG_DEBUG, "WPS: Mismatch in registrar nonce");
return WPS_FAILURE;
}
if (attr.enrollee_nonce == NULL ||
- os_memcmp(wps->nonce_e, attr.enrollee_nonce, WPS_NONCE_LEN != 0)) {
+ os_memcmp(wps->nonce_e, attr.enrollee_nonce, WPS_NONCE_LEN) != 0) {
wpa_printf(MSG_DEBUG, "WPS: Mismatch in enrollee nonce");
return WPS_FAILURE;
}
@@ -1130,18 +1254,13 @@ static enum wps_process_res wps_process_wsc_nack(struct wps_data *wps,
const struct wpabuf *msg)
{
struct wps_parse_attr attr;
+ u16 config_error;
wpa_printf(MSG_DEBUG, "WPS: Received WSC_NACK");
if (wps_parse_msg(msg, &attr) < 0)
return WPS_FAILURE;
- if (!wps_version_supported(attr.version)) {
- wpa_printf(MSG_DEBUG, "WPS: Unsupported message version 0x%x",
- attr.version ? *attr.version : 0);
- return WPS_FAILURE;
- }
-
if (attr.msg_type == NULL) {
wpa_printf(MSG_DEBUG, "WPS: No Message Type attribute");
return WPS_FAILURE;
@@ -1154,7 +1273,7 @@ static enum wps_process_res wps_process_wsc_nack(struct wps_data *wps,
}
if (attr.registrar_nonce == NULL ||
- os_memcmp(wps->nonce_r, attr.registrar_nonce, WPS_NONCE_LEN != 0))
+ os_memcmp(wps->nonce_r, attr.registrar_nonce, WPS_NONCE_LEN) != 0)
{
wpa_printf(MSG_DEBUG, "WPS: Mismatch in registrar nonce");
wpa_hexdump(MSG_DEBUG, "WPS: Received Registrar Nonce",
@@ -1165,7 +1284,7 @@ static enum wps_process_res wps_process_wsc_nack(struct wps_data *wps,
}
if (attr.enrollee_nonce == NULL ||
- os_memcmp(wps->nonce_e, attr.enrollee_nonce, WPS_NONCE_LEN != 0)) {
+ os_memcmp(wps->nonce_e, attr.enrollee_nonce, WPS_NONCE_LEN) != 0) {
wpa_printf(MSG_DEBUG, "WPS: Mismatch in enrollee nonce");
wpa_hexdump(MSG_DEBUG, "WPS: Received Enrollee Nonce",
attr.enrollee_nonce, WPS_NONCE_LEN);
@@ -1180,18 +1299,22 @@ static enum wps_process_res wps_process_wsc_nack(struct wps_data *wps,
return WPS_FAILURE;
}
+ config_error = WPA_GET_BE16(attr.config_error);
wpa_printf(MSG_DEBUG, "WPS: Registrar terminated negotiation with "
- "Configuration Error %d", WPA_GET_BE16(attr.config_error));
+ "Configuration Error %d", config_error);
switch (wps->state) {
case RECV_M4:
- wps_fail_event(wps->wps, WPS_M3);
+ wps_fail_event(wps->wps, WPS_M3, config_error,
+ wps->error_indication);
break;
case RECV_M6:
- wps_fail_event(wps->wps, WPS_M5);
+ wps_fail_event(wps->wps, WPS_M5, config_error,
+ wps->error_indication);
break;
case RECV_M8:
- wps_fail_event(wps->wps, WPS_M7);
+ wps_fail_event(wps->wps, WPS_M7, config_error,
+ wps->error_indication);
break;
default:
break;
@@ -1230,8 +1353,12 @@ enum wps_process_res wps_enrollee_process_msg(struct wps_data *wps,
case WSC_UPnP:
return wps_process_wsc_msg(wps, msg);
case WSC_ACK:
+ if (wps_validate_wsc_ack(msg) < 0)
+ return WPS_FAILURE;
return wps_process_wsc_ack(wps, msg);
case WSC_NACK:
+ if (wps_validate_wsc_nack(msg) < 0)
+ return WPS_FAILURE;
return wps_process_wsc_nack(wps, msg);
default:
wpa_printf(MSG_DEBUG, "WPS: Unsupported op_code %d", op_code);
diff --git a/contrib/wpa/src/wps/wps_er.c b/contrib/wpa/src/wps/wps_er.c
index e0cdd1d..95a0dec 100644
--- a/contrib/wpa/src/wps/wps_er.c
+++ b/contrib/wpa/src/wps/wps_er.c
@@ -1,15 +1,9 @@
/*
* Wi-Fi Protected Setup - External Registrar
- * Copyright (c) 2009, Jouni Malinen <j@w1.fi>
+ * Copyright (c) 2009-2012, Jouni Malinen <j@w1.fi>
*
- * This program is free software; you can redistribute it and/or modify
- * it under the terms of the GNU General Public License version 2 as
- * published by the Free Software Foundation.
- *
- * Alternatively, this software may be distributed under the terms of BSD
- * license.
- *
- * See README and COPYING for more details.
+ * This software may be distributed under the terms of the BSD license.
+ * See README for more details.
*/
#include "includes.h"
@@ -62,11 +56,15 @@ static void wps_er_sta_event(struct wps_context *wps, struct wps_er_sta *sta,
}
-static struct wps_er_sta * wps_er_sta_get(struct wps_er_ap *ap, const u8 *addr)
+static struct wps_er_sta * wps_er_sta_get(struct wps_er_ap *ap, const u8 *addr,
+ const u8 *uuid)
{
struct wps_er_sta *sta;
dl_list_for_each(sta, &ap->sta, struct wps_er_sta, list) {
- if (os_memcmp(sta->addr, addr, ETH_ALEN) == 0)
+ if ((addr == NULL ||
+ os_memcmp(sta->addr, addr, ETH_ALEN) == 0) &&
+ (uuid == NULL ||
+ os_memcmp(uuid, sta->uuid, WPS_UUID_LEN) == 0))
return sta;
}
return NULL;
@@ -275,6 +273,64 @@ fail:
wps_er_ap_unsubscribed(ap->er, ap);
}
+
+static struct wps_er_ap_settings * wps_er_ap_get_settings(struct wps_er *er,
+ const u8 *uuid)
+{
+ struct wps_er_ap_settings *s;
+ dl_list_for_each(s, &er->ap_settings, struct wps_er_ap_settings, list)
+ if (os_memcmp(uuid, s->uuid, WPS_UUID_LEN) == 0)
+ return s;
+ return NULL;
+}
+
+
+int wps_er_ap_cache_settings(struct wps_er *er, struct in_addr *addr)
+{
+ struct wps_er_ap *ap;
+ struct wps_er_ap_settings *settings;
+
+ ap = wps_er_ap_get(er, addr, NULL);
+ if (ap == NULL || ap->ap_settings == NULL)
+ return -1;
+
+ settings = wps_er_ap_get_settings(er, ap->uuid);
+ if (!settings) {
+ settings = os_zalloc(sizeof(*settings));
+ if (settings == NULL)
+ return -1;
+ os_memcpy(settings->uuid, ap->uuid, WPS_UUID_LEN);
+ dl_list_add(&er->ap_settings, &settings->list);
+ }
+ os_memcpy(&settings->ap_settings, ap->ap_settings,
+ sizeof(struct wps_credential));
+
+ return 0;
+}
+
+
+static int wps_er_ap_use_cached_settings(struct wps_er *er,
+ struct wps_er_ap *ap)
+{
+ struct wps_er_ap_settings *s;
+
+ if (ap->ap_settings)
+ return 0;
+
+ s = wps_er_ap_get_settings(ap->er, ap->uuid);
+ if (!s)
+ return -1;
+
+ ap->ap_settings = os_malloc(sizeof(*ap->ap_settings));
+ if (ap->ap_settings == NULL)
+ return -1;
+
+ os_memcpy(ap->ap_settings, &s->ap_settings, sizeof(*ap->ap_settings));
+ wpa_printf(MSG_DEBUG, "WPS ER: Use cached AP settings");
+ return 0;
+}
+
+
static void wps_er_ap_remove_entry(struct wps_er *er, struct wps_er_ap *ap)
{
wpa_printf(MSG_DEBUG, "WPS ER: Removing AP entry for %s (%s)",
@@ -352,6 +408,7 @@ static void wps_er_http_subscribe_cb(void *ctx, struct http_client *c,
wpa_printf(MSG_DEBUG, "WPS ER: Subscribed to events");
ap->subscribed = 1;
wps_er_get_sid(ap, http_client_get_hdr_line(c, "SID"));
+ wps_er_ap_use_cached_settings(ap->er, ap);
wps_er_ap_event(ap->er->wps, ap, WPS_EV_ER_AP_ADD);
break;
case HTTP_CLIENT_FAILED:
@@ -439,16 +496,61 @@ static void wps_er_get_device_info(struct wps_er_ap *ap)
}
+static const char * wps_er_find_wfadevice(const char *data)
+{
+ const char *tag, *tagname, *end;
+ char *val;
+ int found = 0;
+
+ while (!found) {
+ /* Find next <device> */
+ for (;;) {
+ if (xml_next_tag(data, &tag, &tagname, &end))
+ return NULL;
+ data = end;
+ if (!os_strncasecmp(tagname, "device", 6) &&
+ *tag != '/' &&
+ (tagname[6] == '>' || !isgraph(tagname[6]))) {
+ break;
+ }
+ }
+
+ /* Check whether deviceType is WFADevice */
+ val = xml_get_first_item(data, "deviceType");
+ if (val == NULL)
+ return NULL;
+ wpa_printf(MSG_DEBUG, "WPS ER: Found deviceType '%s'", val);
+ found = os_strcasecmp(val, "urn:schemas-wifialliance-org:"
+ "device:WFADevice:1") == 0;
+ os_free(val);
+ }
+
+ return data;
+}
+
+
static void wps_er_parse_device_description(struct wps_er_ap *ap,
struct wpabuf *reply)
{
/* Note: reply includes null termination after the buffer data */
- const char *data = wpabuf_head(reply);
+ const char *tmp, *data = wpabuf_head(reply);
char *pos;
wpa_hexdump_ascii(MSG_MSGDUMP, "WPS ER: Device info",
wpabuf_head(reply), wpabuf_len(reply));
+ /*
+ * The root device description may include multiple devices, so first
+ * find the beginning of the WFADevice description to allow the
+ * simplistic parser to pick the correct entries.
+ */
+ tmp = wps_er_find_wfadevice(data);
+ if (tmp == NULL) {
+ wpa_printf(MSG_DEBUG, "WPS ER: WFADevice:1 device not found - "
+ "trying to parse invalid data");
+ } else
+ data = tmp;
+
ap->friendly_name = xml_get_first_item(data, "friendlyName");
wpa_printf(MSG_DEBUG, "WPS ER: friendlyName='%s'", ap->friendly_name);
@@ -583,8 +685,12 @@ void wps_er_ap_remove(struct wps_er *er, struct in_addr *addr)
static void wps_er_ap_remove_all(struct wps_er *er)
{
struct wps_er_ap *prev, *ap;
+ struct wps_er_ap_settings *prev_s, *s;
dl_list_for_each_safe(ap, prev, &er->ap, struct wps_er_ap, list)
wps_er_ap_remove_entry(er, ap);
+ dl_list_for_each_safe(s, prev_s, &er->ap_settings,
+ struct wps_er_ap_settings, list)
+ os_free(s);
}
@@ -649,7 +755,7 @@ static struct wps_er_sta * wps_er_add_sta_data(struct wps_er_ap *ap,
struct wps_parse_attr *attr,
int probe_req)
{
- struct wps_er_sta *sta = wps_er_sta_get(ap, addr);
+ struct wps_er_sta *sta = wps_er_sta_get(ap, addr, NULL);
int new_sta = 0;
int m1;
@@ -756,6 +862,12 @@ static void wps_er_process_wlanevent_probe_req(struct wps_er_ap *ap,
wpa_hexdump_buf(MSG_MSGDUMP, "WPS ER: WLANEvent - Enrollee's message "
"(TLVs from Probe Request)", msg);
+ if (wps_validate_probe_req(msg, addr) < 0) {
+ wpa_printf(MSG_INFO, "WPS-STRICT: ER: Ignore invalid proxied "
+ "Probe Request frame from " MACSTR, MAC2STR(addr));
+ return;
+ }
+
if (wps_parse_msg(msg, &attr) < 0) {
wpa_printf(MSG_DEBUG, "WPS ER: Failed to parse TLVs in "
"WLANEvent message");
@@ -763,6 +875,7 @@ static void wps_er_process_wlanevent_probe_req(struct wps_er_ap *ap,
}
wps_er_add_sta_data(ap, addr, &attr, 1);
+ wps_registrar_probe_req_rx(ap->er->wps->registrar, addr, msg, 0);
}
@@ -1151,7 +1264,7 @@ static void wps_er_http_req(void *ctx, struct http_request *req)
struct wps_er *
-wps_er_init(struct wps_context *wps, const char *ifname)
+wps_er_init(struct wps_context *wps, const char *ifname, const char *filter)
{
struct wps_er *er;
struct in_addr addr;
@@ -1161,6 +1274,7 @@ wps_er_init(struct wps_context *wps, const char *ifname)
return NULL;
dl_list_init(&er->ap);
dl_list_init(&er->ap_unsubscribing);
+ dl_list_init(&er->ap_settings);
er->multicast_sd = -1;
er->ssdp_sd = -1;
@@ -1175,6 +1289,16 @@ wps_er_init(struct wps_context *wps, const char *ifname)
/* Limit event_id to < 32 bits to avoid issues with atoi() */
er->event_id &= 0x0fffffff;
+ if (filter) {
+ if (inet_aton(filter, &er->filter_addr) == 0) {
+ wpa_printf(MSG_INFO, "WPS UPnP: Invalid filter "
+ "address %s", filter);
+ wps_er_deinit(er, NULL, NULL);
+ return NULL;
+ }
+ wpa_printf(MSG_DEBUG, "WPS UPnP: Only accepting connections "
+ "with %s", filter);
+ }
if (get_netif_info(ifname, &er->ip_addr, &er->ip_addr_text,
er->mac_addr)) {
wpa_printf(MSG_INFO, "WPS UPnP: Could not get IP/MAC address "
@@ -1184,6 +1308,7 @@ wps_er_init(struct wps_context *wps, const char *ifname)
}
if (wps_er_ssdp_init(er) < 0) {
+ wpa_printf(MSG_INFO, "WPS UPnP: SSDP initialization failed");
wps_er_deinit(er, NULL, NULL);
return NULL;
}
@@ -1191,6 +1316,7 @@ wps_er_init(struct wps_context *wps, const char *ifname)
addr.s_addr = er->ip_addr;
er->http_srv = http_server_init(&addr, -1, wps_er_http_req, er);
if (er->http_srv == NULL) {
+ wpa_printf(MSG_INFO, "WPS UPnP: HTTP initialization failed");
wps_er_deinit(er, NULL, NULL);
return NULL;
}
@@ -1256,19 +1382,30 @@ static void wps_er_http_set_sel_reg_cb(void *ctx, struct http_client *c,
enum http_client_event event)
{
struct wps_er_ap *ap = ctx;
+ union wps_event_data data;
+
+ os_memset(&data, 0, sizeof(data));
switch (event) {
case HTTP_CLIENT_OK:
wpa_printf(MSG_DEBUG, "WPS ER: SetSelectedRegistrar OK");
+ data.set_sel_reg.state = WPS_ER_SET_SEL_REG_DONE;
+ data.set_sel_reg.uuid = ap->uuid;
break;
case HTTP_CLIENT_FAILED:
case HTTP_CLIENT_INVALID_REPLY:
case HTTP_CLIENT_TIMEOUT:
wpa_printf(MSG_DEBUG, "WPS ER: SetSelectedRegistrar failed");
+ data.set_sel_reg.state = WPS_ER_SET_SEL_REG_FAILED;
+ data.set_sel_reg.uuid = ap->uuid;
break;
}
http_client_free(ap->http);
ap->http = NULL;
+
+ if (data.set_sel_reg.uuid)
+ ap->er->wps->event_cb(ap->er->wps->cb_ctx,
+ WPS_EV_ER_SET_SELECTED_REGISTRAR, &data);
}
@@ -1290,6 +1427,12 @@ static void wps_er_send_set_sel_reg(struct wps_er_ap *ap, struct wpabuf *msg)
return;
}
+ if (ap->wps) {
+ wpa_printf(MSG_DEBUG, "WPS ER: Pending WPS operation for AP - "
+ "skip SetSelectedRegistrar");
+ return;
+ }
+
url = http_client_url_parse(ap->control_url, &dst, &path);
if (url == NULL) {
wpa_printf(MSG_DEBUG, "WPS ER: Failed to parse controlURL");
@@ -1339,26 +1482,74 @@ static int wps_er_build_sel_reg_config_methods(struct wpabuf *msg,
}
+static int wps_er_build_uuid_r(struct wpabuf *msg, const u8 *uuid_r)
+{
+#ifdef CONFIG_WPS2
+ wpabuf_put_be16(msg, ATTR_UUID_R);
+ wpabuf_put_be16(msg, WPS_UUID_LEN);
+ wpabuf_put_data(msg, uuid_r, WPS_UUID_LEN);
+#endif /* CONFIG_WPS2 */
+ return 0;
+}
+
+
void wps_er_set_sel_reg(struct wps_er *er, int sel_reg, u16 dev_passwd_id,
u16 sel_reg_config_methods)
{
struct wpabuf *msg;
struct wps_er_ap *ap;
+ struct wps_registrar *reg = er->wps->registrar;
+ const u8 *auth_macs;
+#ifdef CONFIG_WPS2
+ u8 bcast[ETH_ALEN];
+#endif /* CONFIG_WPS2 */
+ size_t count;
+ union wps_event_data data;
+
+ if (er->skip_set_sel_reg) {
+ wpa_printf(MSG_DEBUG, "WPS ER: Skip SetSelectedRegistrar");
+ return;
+ }
msg = wpabuf_alloc(500);
if (msg == NULL)
return;
+ auth_macs = wps_authorized_macs(reg, &count);
+#ifdef CONFIG_WPS2
+ if (count == 0) {
+ os_memset(bcast, 0xff, ETH_ALEN);
+ auth_macs = bcast;
+ count = 1;
+ }
+#endif /* CONFIG_WPS2 */
+
if (wps_build_version(msg) ||
wps_er_build_selected_registrar(msg, sel_reg) ||
wps_er_build_dev_password_id(msg, dev_passwd_id) ||
- wps_er_build_sel_reg_config_methods(msg, sel_reg_config_methods)) {
+ wps_er_build_sel_reg_config_methods(msg, sel_reg_config_methods) ||
+ wps_build_wfa_ext(msg, 0, auth_macs, count) ||
+ wps_er_build_uuid_r(msg, er->wps->uuid)) {
wpabuf_free(msg);
return;
}
- dl_list_for_each(ap, &er->ap, struct wps_er_ap, list)
+ os_memset(&data, 0, sizeof(data));
+ data.set_sel_reg.sel_reg = sel_reg;
+ data.set_sel_reg.dev_passwd_id = dev_passwd_id;
+ data.set_sel_reg.sel_reg_config_methods = sel_reg_config_methods;
+ data.set_sel_reg.state = WPS_ER_SET_SEL_REG_START;
+
+ dl_list_for_each(ap, &er->ap, struct wps_er_ap, list) {
+ if (er->set_sel_reg_uuid_filter &&
+ os_memcmp(ap->uuid, er->set_sel_reg_uuid_filter,
+ WPS_UUID_LEN) != 0)
+ continue;
+ data.set_sel_reg.uuid = ap->uuid;
+ er->wps->event_cb(er->wps->cb_ctx,
+ WPS_EV_ER_SET_SELECTED_REGISTRAR, &data);
wps_er_send_set_sel_reg(ap, msg);
+ }
wpabuf_free(msg);
}
@@ -1366,16 +1557,41 @@ void wps_er_set_sel_reg(struct wps_er *er, int sel_reg, u16 dev_passwd_id,
int wps_er_pbc(struct wps_er *er, const u8 *uuid)
{
+ int res;
+ struct wps_er_ap *ap;
+
if (er == NULL || er->wps == NULL)
return -1;
- /*
- * TODO: Should enable PBC mode only in a single AP based on which AP
- * the Enrollee (uuid) is using. Now, we may end up enabling multiple
- * APs in PBC mode which could result in session overlap at the
- * Enrollee.
- */
- if (wps_registrar_button_pushed(er->wps->registrar))
+ if (wps_registrar_pbc_overlap(er->wps->registrar, NULL, NULL)) {
+ wpa_printf(MSG_DEBUG, "WPS ER: PBC overlap - do not start PBC "
+ "mode");
+ return -2;
+ }
+
+ ap = wps_er_ap_get(er, NULL, uuid);
+ if (ap == NULL) {
+ struct wps_er_sta *sta = NULL;
+ dl_list_for_each(ap, &er->ap, struct wps_er_ap, list) {
+ sta = wps_er_sta_get(ap, NULL, uuid);
+ if (sta) {
+ uuid = ap->uuid;
+ break;
+ }
+ }
+ if (sta == NULL)
+ return -3; /* Unknown UUID */
+ }
+
+ if (ap->ap_settings == NULL) {
+ wpa_printf(MSG_DEBUG, "WPS ER: AP settings not known");
+ return -4;
+ }
+
+ er->set_sel_reg_uuid_filter = uuid;
+ res = wps_registrar_button_pushed(er->wps->registrar, NULL);
+ er->set_sel_reg_uuid_filter = NULL;
+ if (res)
return -1;
return 0;
@@ -1385,6 +1601,8 @@ int wps_er_pbc(struct wps_er *er, const u8 *uuid)
static void wps_er_ap_settings_cb(void *ctx, const struct wps_credential *cred)
{
struct wps_er_ap *ap = ctx;
+ union wps_event_data data;
+
wpa_printf(MSG_DEBUG, "WPS ER: AP Settings received");
os_free(ap->ap_settings);
ap->ap_settings = os_malloc(sizeof(*cred));
@@ -1393,7 +1611,11 @@ static void wps_er_ap_settings_cb(void *ctx, const struct wps_credential *cred)
ap->ap_settings->cred_attr = NULL;
}
- /* TODO: send info through ctrl_iface */
+ os_memset(&data, 0, sizeof(data));
+ data.ap_settings.uuid = ap->uuid;
+ data.ap_settings.cred = cred;
+ ap->er->wps->event_cb(ap->er->wps->cb_ctx, WPS_EV_ER_AP_SETTINGS,
+ &data);
}
@@ -1436,6 +1658,8 @@ static void wps_er_http_put_message_cb(void *ctx, struct http_client *c,
if (buf == NULL) {
wpa_printf(MSG_DEBUG, "WPS ER: Could not extract "
"NewOutMessage from PutMessage response");
+ wps_deinit(ap->wps);
+ ap->wps = NULL;
return;
}
wps_er_ap_process(ap, buf);
@@ -1487,10 +1711,26 @@ static void wps_er_ap_put_message(struct wps_er_ap *ap,
static void wps_er_ap_process(struct wps_er_ap *ap, struct wpabuf *msg)
{
enum wps_process_res res;
+ struct wps_parse_attr attr;
+ enum wsc_op_code op_code;
- res = wps_process_msg(ap->wps, WSC_MSG, msg);
+ op_code = WSC_MSG;
+ if (wps_parse_msg(msg, &attr) == 0 && attr.msg_type) {
+ switch (*attr.msg_type) {
+ case WPS_WSC_ACK:
+ op_code = WSC_ACK;
+ break;
+ case WPS_WSC_NACK:
+ op_code = WSC_NACK;
+ break;
+ case WPS_WSC_DONE:
+ op_code = WSC_Done;
+ break;
+ }
+ }
+
+ res = wps_process_msg(ap->wps, op_code, msg);
if (res == WPS_CONTINUE) {
- enum wsc_op_code op_code;
struct wpabuf *next = wps_get_msg(ap->wps, &op_code);
if (next) {
wps_er_ap_put_message(ap, next);
@@ -1501,6 +1741,10 @@ static void wps_er_ap_process(struct wps_er_ap *ap, struct wpabuf *msg)
wps_deinit(ap->wps);
ap->wps = NULL;
}
+ } else if (res == WPS_DONE) {
+ wpa_printf(MSG_DEBUG, "WPS ER: Protocol run done");
+ wps_deinit(ap->wps);
+ ap->wps = NULL;
} else {
wpa_printf(MSG_DEBUG, "WPS ER: Failed to process message from "
"AP (res=%d)", res);
@@ -1656,8 +1900,137 @@ int wps_er_learn(struct wps_er *er, const u8 *uuid, const u8 *pin,
if (wps_er_send_get_device_info(ap, wps_er_ap_learn_m1) < 0)
return -1;
- /* TODO: add PIN without SetSelectedRegistrar trigger to all APs */
- wps_registrar_add_pin(er->wps->registrar, uuid, pin, pin_len, 0);
+ er->skip_set_sel_reg = 1;
+ wps_registrar_add_pin(er->wps->registrar, NULL, uuid, pin, pin_len, 0);
+ er->skip_set_sel_reg = 0;
+
+ return 0;
+}
+
+
+int wps_er_set_config(struct wps_er *er, const u8 *uuid,
+ const struct wps_credential *cred)
+{
+ struct wps_er_ap *ap;
+
+ if (er == NULL)
+ return -1;
+
+ ap = wps_er_ap_get(er, NULL, uuid);
+ if (ap == NULL) {
+ wpa_printf(MSG_DEBUG, "WPS ER: AP not found for set config "
+ "request");
+ return -1;
+ }
+
+ os_free(ap->ap_settings);
+ ap->ap_settings = os_malloc(sizeof(*cred));
+ if (ap->ap_settings == NULL)
+ return -1;
+ os_memcpy(ap->ap_settings, cred, sizeof(*cred));
+ ap->ap_settings->cred_attr = NULL;
+ wpa_printf(MSG_DEBUG, "WPS ER: Updated local AP settings based set "
+ "config request");
+
+ return 0;
+}
+
+
+static void wps_er_ap_config_m1(struct wps_er_ap *ap, struct wpabuf *m1)
+{
+ struct wps_config cfg;
+
+ if (ap->wps) {
+ wpa_printf(MSG_DEBUG, "WPS ER: Protocol run already in "
+ "progress with this AP");
+ return;
+ }
+
+ os_memset(&cfg, 0, sizeof(cfg));
+ cfg.wps = ap->er->wps;
+ cfg.registrar = 1;
+ cfg.new_ap_settings = ap->ap_settings;
+ ap->wps = wps_init(&cfg);
+ if (ap->wps == NULL)
+ return;
+ ap->wps->ap_settings_cb = NULL;
+ ap->wps->ap_settings_cb_ctx = NULL;
+
+ wps_er_ap_process(ap, m1);
+}
+
+
+int wps_er_config(struct wps_er *er, const u8 *uuid, const u8 *pin,
+ size_t pin_len, const struct wps_credential *cred)
+{
+ struct wps_er_ap *ap;
+
+ if (er == NULL)
+ return -1;
+
+ ap = wps_er_ap_get(er, NULL, uuid);
+ if (ap == NULL) {
+ wpa_printf(MSG_DEBUG, "WPS ER: AP not found for config "
+ "request");
+ return -1;
+ }
+ if (ap->wps) {
+ wpa_printf(MSG_DEBUG, "WPS ER: Pending operation ongoing "
+ "with the AP - cannot start config");
+ return -1;
+ }
+
+ os_free(ap->ap_settings);
+ ap->ap_settings = os_malloc(sizeof(*cred));
+ if (ap->ap_settings == NULL)
+ return -1;
+ os_memcpy(ap->ap_settings, cred, sizeof(*cred));
+ ap->ap_settings->cred_attr = NULL;
+
+ if (wps_er_send_get_device_info(ap, wps_er_ap_config_m1) < 0)
+ return -1;
+
+ er->skip_set_sel_reg = 1;
+ wps_registrar_add_pin(er->wps->registrar, NULL, uuid, pin, pin_len, 0);
+ er->skip_set_sel_reg = 0;
return 0;
}
+
+
+#ifdef CONFIG_WPS_NFC
+struct wpabuf * wps_er_nfc_config_token(struct wps_er *er, const u8 *uuid)
+{
+ struct wps_er_ap *ap;
+ struct wpabuf *ret;
+ struct wps_data data;
+
+ if (er == NULL)
+ return NULL;
+
+ ap = wps_er_ap_get(er, NULL, uuid);
+ if (ap == NULL)
+ return NULL;
+ if (ap->ap_settings == NULL) {
+ wpa_printf(MSG_DEBUG, "WPS ER: No settings known for the "
+ "selected AP");
+ return NULL;
+ }
+
+ ret = wpabuf_alloc(500);
+ if (ret == NULL)
+ return NULL;
+
+ os_memset(&data, 0, sizeof(data));
+ data.wps = er->wps;
+ data.use_cred = ap->ap_settings;
+ if (wps_build_version(ret) ||
+ wps_build_cred(&data, ret) ||
+ wps_build_wfa_ext(ret, 0, NULL, 0)) {
+ wpabuf_free(ret);
+ return NULL;
+ }
+
+ return ret;
+}
+#endif /* CONFIG_WPS_NFC */
diff --git a/contrib/wpa/src/wps/wps_er.h b/contrib/wpa/src/wps/wps_er.h
index b13b950..6119647 100644
--- a/contrib/wpa/src/wps/wps_er.h
+++ b/contrib/wpa/src/wps/wps_er.h
@@ -2,14 +2,8 @@
* Wi-Fi Protected Setup - External Registrar
* Copyright (c) 2009, Jouni Malinen <j@w1.fi>
*
- * This program is free software; you can redistribute it and/or modify
- * it under the terms of the GNU General Public License version 2 as
- * published by the Free Software Foundation.
- *
- * Alternatively, this software may be distributed under the terms of BSD
- * license.
- *
- * See README and COPYING for more details.
+ * This software may be distributed under the terms of the BSD license.
+ * See README for more details.
*/
#ifndef WPS_ER_H
@@ -73,6 +67,12 @@ struct wps_er_ap {
void (*m1_handler)(struct wps_er_ap *ap, struct wpabuf *m1);
};
+struct wps_er_ap_settings {
+ struct dl_list list;
+ u8 uuid[WPS_UUID_LEN];
+ struct wps_credential ap_settings;
+};
+
struct wps_er {
struct wps_context *wps;
char ifname[17];
@@ -83,6 +83,7 @@ struct wps_er {
int ssdp_sd;
struct dl_list ap;
struct dl_list ap_unsubscribing;
+ struct dl_list ap_settings;
struct http_server *http_srv;
int http_port;
unsigned int next_ap_id;
@@ -90,6 +91,9 @@ struct wps_er {
int deinitializing;
void (*deinit_done_cb)(void *ctx);
void *deinit_done_ctx;
+ struct in_addr filter_addr;
+ int skip_set_sel_reg;
+ const u8 *set_sel_reg_uuid_filter;
};
@@ -97,6 +101,7 @@ struct wps_er {
void wps_er_ap_add(struct wps_er *er, const u8 *uuid, struct in_addr *addr,
const char *location, int max_age);
void wps_er_ap_remove(struct wps_er *er, struct in_addr *addr);
+int wps_er_ap_cache_settings(struct wps_er *er, struct in_addr *addr);
/* wps_er_ssdp.c */
int wps_er_ssdp_init(struct wps_er *er);
diff --git a/contrib/wpa/src/wps/wps_er_ssdp.c b/contrib/wpa/src/wps/wps_er_ssdp.c
index f108435..f9f6e6c 100644
--- a/contrib/wpa/src/wps/wps_er_ssdp.c
+++ b/contrib/wpa/src/wps/wps_er_ssdp.c
@@ -2,14 +2,8 @@
* Wi-Fi Protected Setup - External Registrar (SSDP)
* Copyright (c) 2009, Jouni Malinen <j@w1.fi>
*
- * This program is free software; you can redistribute it and/or modify
- * it under the terms of the GNU General Public License version 2 as
- * published by the Free Software Foundation.
- *
- * Alternatively, this software may be distributed under the terms of BSD
- * license.
- *
- * See README and COPYING for more details.
+ * This software may be distributed under the terms of the BSD license.
+ * See README for more details.
*/
#include "includes.h"
@@ -41,6 +35,9 @@ static void wps_er_ssdp_rx(int sd, void *eloop_ctx, void *sock_ctx)
if (nread <= 0)
return;
buf[nread] = '\0';
+ if (er->filter_addr.s_addr &&
+ er->filter_addr.s_addr != addr.sin_addr.s_addr)
+ return;
wpa_printf(MSG_DEBUG, "WPS ER: Received SSDP from %s",
inet_ntoa(addr.sin_addr));
@@ -110,6 +107,7 @@ static void wps_er_ssdp_rx(int sd, void *eloop_ctx, void *sock_ctx)
return; /* Not WPS advertisement/reply */
if (byebye) {
+ wps_er_ap_cache_settings(er, &addr.sin_addr);
wps_er_ap_remove(er, &addr.sin_addr);
return;
}
@@ -162,16 +160,25 @@ void wps_er_send_ssdp_msearch(struct wps_er *er)
int wps_er_ssdp_init(struct wps_er *er)
{
- if (add_ssdp_network(er->ifname))
+ if (add_ssdp_network(er->ifname)) {
+ wpa_printf(MSG_INFO, "WPS ER: Failed to add routing entry for "
+ "SSDP");
return -1;
+ }
er->multicast_sd = ssdp_open_multicast_sock(er->ip_addr);
- if (er->multicast_sd < 0)
+ if (er->multicast_sd < 0) {
+ wpa_printf(MSG_INFO, "WPS ER: Failed to open multicast socket "
+ "for SSDP");
return -1;
+ }
er->ssdp_sd = ssdp_listener_open();
- if (er->ssdp_sd < 0)
+ if (er->ssdp_sd < 0) {
+ wpa_printf(MSG_INFO, "WPS ER: Failed to open SSDP listener "
+ "socket");
return -1;
+ }
if (eloop_register_sock(er->multicast_sd, EVENT_TYPE_READ,
wps_er_ssdp_rx, er, NULL) ||
diff --git a/contrib/wpa/src/wps/wps_i.h b/contrib/wpa/src/wps/wps_i.h
index 50e66f6..8110894 100644
--- a/contrib/wpa/src/wps/wps_i.h
+++ b/contrib/wpa/src/wps/wps_i.h
@@ -1,21 +1,18 @@
/*
* Wi-Fi Protected Setup - internal definitions
- * Copyright (c) 2008-2009, Jouni Malinen <j@w1.fi>
+ * Copyright (c) 2008-2012, Jouni Malinen <j@w1.fi>
*
- * This program is free software; you can redistribute it and/or modify
- * it under the terms of the GNU General Public License version 2 as
- * published by the Free Software Foundation.
- *
- * Alternatively, this software may be distributed under the terms of BSD
- * license.
- *
- * See README and COPYING for more details.
+ * This software may be distributed under the terms of the BSD license.
+ * See README for more details.
*/
#ifndef WPS_I_H
#define WPS_I_H
#include "wps.h"
+#include "wps_attr_parse.h"
+
+struct wps_nfc_pw_token;
/**
* struct wps_data - WPS registration protocol data
@@ -102,6 +99,7 @@ struct wps_data {
* config_error - Configuration Error value to be used in NACK
*/
u16 config_error;
+ u16 error_indication;
int ext_reg;
int int_reg;
@@ -116,84 +114,14 @@ struct wps_data {
struct wps_credential *use_cred;
int use_psk_key;
-};
-
+ u8 p2p_dev_addr[ETH_ALEN]; /* P2P Device Address of the client or
+ * 00:00:00:00:00:00 if not a P2p client */
+ int pbc_in_m1;
-struct wps_parse_attr {
- /* fixed length fields */
- const u8 *version; /* 1 octet */
- const u8 *msg_type; /* 1 octet */
- const u8 *enrollee_nonce; /* WPS_NONCE_LEN (16) octets */
- const u8 *registrar_nonce; /* WPS_NONCE_LEN (16) octets */
- const u8 *uuid_r; /* WPS_UUID_LEN (16) octets */
- const u8 *uuid_e; /* WPS_UUID_LEN (16) octets */
- const u8 *auth_type_flags; /* 2 octets */
- const u8 *encr_type_flags; /* 2 octets */
- const u8 *conn_type_flags; /* 1 octet */
- const u8 *config_methods; /* 2 octets */
- const u8 *sel_reg_config_methods; /* 2 octets */
- const u8 *primary_dev_type; /* 8 octets */
- const u8 *rf_bands; /* 1 octet */
- const u8 *assoc_state; /* 2 octets */
- const u8 *config_error; /* 2 octets */
- const u8 *dev_password_id; /* 2 octets */
- const u8 *oob_dev_password; /* WPS_OOB_DEVICE_PASSWORD_ATTR_LEN (54)
- * octets */
- const u8 *os_version; /* 4 octets */
- const u8 *wps_state; /* 1 octet */
- const u8 *authenticator; /* WPS_AUTHENTICATOR_LEN (8) octets */
- const u8 *r_hash1; /* WPS_HASH_LEN (32) octets */
- const u8 *r_hash2; /* WPS_HASH_LEN (32) octets */
- const u8 *e_hash1; /* WPS_HASH_LEN (32) octets */
- const u8 *e_hash2; /* WPS_HASH_LEN (32) octets */
- const u8 *r_snonce1; /* WPS_SECRET_NONCE_LEN (16) octets */
- const u8 *r_snonce2; /* WPS_SECRET_NONCE_LEN (16) octets */
- const u8 *e_snonce1; /* WPS_SECRET_NONCE_LEN (16) octets */
- const u8 *e_snonce2; /* WPS_SECRET_NONCE_LEN (16) octets */
- const u8 *key_wrap_auth; /* WPS_KWA_LEN (8) octets */
- const u8 *auth_type; /* 2 octets */
- const u8 *encr_type; /* 2 octets */
- const u8 *network_idx; /* 1 octet */
- const u8 *network_key_idx; /* 1 octet */
- const u8 *mac_addr; /* ETH_ALEN (6) octets */
- const u8 *key_prov_auto; /* 1 octet (Bool) */
- const u8 *dot1x_enabled; /* 1 octet (Bool) */
- const u8 *selected_registrar; /* 1 octet (Bool) */
- const u8 *request_type; /* 1 octet */
- const u8 *response_type; /* 1 octet */
- const u8 *ap_setup_locked; /* 1 octet */
-
- /* variable length fields */
- const u8 *manufacturer;
- size_t manufacturer_len;
- const u8 *model_name;
- size_t model_name_len;
- const u8 *model_number;
- size_t model_number_len;
- const u8 *serial_number;
- size_t serial_number_len;
- const u8 *dev_name;
- size_t dev_name_len;
- const u8 *public_key;
- size_t public_key_len;
- const u8 *encr_settings;
- size_t encr_settings_len;
- const u8 *ssid; /* <= 32 octets */
- size_t ssid_len;
- const u8 *network_key; /* <= 64 octets */
- size_t network_key_len;
- const u8 *eap_type; /* <= 8 octets */
- size_t eap_type_len;
- const u8 *eap_identity; /* <= 64 octets */
- size_t eap_identity_len;
-
- /* attributes that can occur multiple times */
-#define MAX_CRED_COUNT 10
- const u8 *cred[MAX_CRED_COUNT];
- size_t cred_len[MAX_CRED_COUNT];
- size_t num_cred;
+ struct wps_nfc_pw_token *nfc_pw_token;
};
+
/* wps_common.c */
void wps_kdf(const u8 *key, const u8 *label_prefix, size_t label_prefix_len,
const char *label, u8 *res, size_t res_len);
@@ -202,18 +130,15 @@ void wps_derive_psk(struct wps_data *wps, const u8 *dev_passwd,
size_t dev_passwd_len);
struct wpabuf * wps_decrypt_encr_settings(struct wps_data *wps, const u8 *encr,
size_t encr_len);
-void wps_fail_event(struct wps_context *wps, enum wps_msg_type msg);
+void wps_fail_event(struct wps_context *wps, enum wps_msg_type msg,
+ u16 config_error, u16 error_indication);
void wps_success_event(struct wps_context *wps);
void wps_pwd_auth_fail_event(struct wps_context *wps, int enrollee, int part);
void wps_pbc_overlap_event(struct wps_context *wps);
void wps_pbc_timeout_event(struct wps_context *wps);
-extern struct oob_device_data oob_ufd_device_data;
-extern struct oob_device_data oob_nfc_device_data;
-extern struct oob_nfc_device_data oob_nfc_pn531_device_data;
-
-/* wps_attr_parse.c */
-int wps_parse_msg(const struct wpabuf *msg, struct wps_parse_attr *attr);
+struct wpabuf * wps_build_wsc_ack(struct wps_data *wps);
+struct wpabuf * wps_build_wsc_nack(struct wps_data *wps);
/* wps_attr_build.c */
int wps_build_public_key(struct wps_data *wps, struct wpabuf *msg);
@@ -228,6 +153,8 @@ int wps_build_key_wrap_auth(struct wps_data *wps, struct wpabuf *msg);
int wps_build_encr_settings(struct wps_data *wps, struct wpabuf *msg,
struct wpabuf *plain);
int wps_build_version(struct wpabuf *msg);
+int wps_build_wfa_ext(struct wpabuf *msg, int req_to_enroll,
+ const u8 *auth_macs, size_t auth_macs_count);
int wps_build_msg_type(struct wpabuf *msg, enum wps_msg_type msg_type);
int wps_build_enrollee_nonce(struct wps_data *wps, struct wpabuf *msg);
int wps_build_registrar_nonce(struct wps_data *wps, struct wpabuf *msg);
@@ -235,7 +162,10 @@ int wps_build_auth_type_flags(struct wps_data *wps, struct wpabuf *msg);
int wps_build_encr_type_flags(struct wps_data *wps, struct wpabuf *msg);
int wps_build_conn_type_flags(struct wps_data *wps, struct wpabuf *msg);
int wps_build_assoc_state(struct wps_data *wps, struct wpabuf *msg);
-int wps_build_oob_dev_password(struct wpabuf *msg, struct wps_context *wps);
+int wps_build_oob_dev_pw(struct wpabuf *msg, u16 dev_pw_id,
+ const struct wpabuf *pubkey, const u8 *dev_pw,
+ size_t dev_pw_len);
+struct wpabuf * wps_ie_encapsulate(struct wpabuf *data);
/* wps_attr_process.c */
int wps_process_authenticator(struct wps_data *wps, const u8 *authenticator,
@@ -264,15 +194,10 @@ int wps_build_cred(struct wps_data *wps, struct wpabuf *msg);
int wps_device_store(struct wps_registrar *reg,
struct wps_device_data *dev, const u8 *uuid);
void wps_registrar_selected_registrar_changed(struct wps_registrar *reg);
-
-/* ndef.c */
-struct wpabuf * ndef_parse_wifi(struct wpabuf *buf);
-struct wpabuf * ndef_build_wifi(struct wpabuf *buf);
-
-static inline int wps_version_supported(const u8 *version)
-{
- /* Require major version match, but allow minor version differences */
- return version && (*version & 0xf0) == (WPS_VERSION & 0xf0);
-}
+const u8 * wps_authorized_macs(struct wps_registrar *reg, size_t *count);
+int wps_registrar_pbc_overlap(struct wps_registrar *reg,
+ const u8 *addr, const u8 *uuid_e);
+void wps_registrar_remove_nfc_pw_token(struct wps_registrar *reg,
+ struct wps_nfc_pw_token *token);
#endif /* WPS_I_H */
diff --git a/contrib/wpa/src/wps/wps_nfc.c b/contrib/wpa/src/wps/wps_nfc.c
deleted file mode 100644
index ff12000..0000000
--- a/contrib/wpa/src/wps/wps_nfc.c
+++ /dev/null
@@ -1,117 +0,0 @@
-/*
- * NFC routines for Wi-Fi Protected Setup
- * Copyright (c) 2009, Masashi Honma <honma@ictec.co.jp>
- *
- * This program is free software; you can redistribute it and/or modify
- * it under the terms of the GNU General Public License version 2 as
- * published by the Free Software Foundation.
- *
- * Alternatively, this software may be distributed under the terms of BSD
- * license.
- *
- * See README and COPYING for more details.
- */
-
-#include "includes.h"
-#include "common.h"
-
-#include "wps/wps.h"
-#include "wps_i.h"
-
-
-struct wps_nfc_data {
- struct oob_nfc_device_data *oob_nfc_dev;
-};
-
-
-static void * init_nfc(struct wps_context *wps,
- struct oob_device_data *oob_dev, int registrar)
-{
- struct oob_nfc_device_data *oob_nfc_dev;
- struct wps_nfc_data *data;
-
- oob_nfc_dev = wps_get_oob_nfc_device(oob_dev->device_name);
- if (oob_nfc_dev == NULL) {
- wpa_printf(MSG_ERROR, "WPS (NFC): Unknown NFC device (%s)",
- oob_dev->device_name);
- return NULL;
- }
-
- if (oob_nfc_dev->init_func(oob_dev->device_path) < 0)
- return NULL;
-
- data = os_zalloc(sizeof(*data));
- if (data == NULL) {
- wpa_printf(MSG_ERROR, "WPS (NFC): Failed to allocate "
- "nfc data area");
- return NULL;
- }
- data->oob_nfc_dev = oob_nfc_dev;
- return data;
-}
-
-
-static struct wpabuf * read_nfc(void *priv)
-{
- struct wps_nfc_data *data = priv;
- struct wpabuf *wifi, *buf;
- char *raw_data;
- size_t len;
-
- raw_data = data->oob_nfc_dev->read_func(&len);
- if (raw_data == NULL)
- return NULL;
-
- wifi = wpabuf_alloc_copy(raw_data, len);
- os_free(raw_data);
- if (wifi == NULL) {
- wpa_printf(MSG_ERROR, "WPS (NFC): Failed to allocate "
- "nfc read area");
- return NULL;
- }
-
- buf = ndef_parse_wifi(wifi);
- wpabuf_free(wifi);
- if (buf == NULL)
- wpa_printf(MSG_ERROR, "WPS (NFC): Failed to unwrap");
- return buf;
-}
-
-
-static int write_nfc(void *priv, struct wpabuf *buf)
-{
- struct wps_nfc_data *data = priv;
- struct wpabuf *wifi;
- int ret;
-
- wifi = ndef_build_wifi(buf);
- if (wifi == NULL) {
- wpa_printf(MSG_ERROR, "WPS (NFC): Failed to wrap");
- return -1;
- }
-
- ret = data->oob_nfc_dev->write_func(wpabuf_mhead(wifi),
- wpabuf_len(wifi));
- wpabuf_free(wifi);
- return ret;
-}
-
-
-static void deinit_nfc(void *priv)
-{
- struct wps_nfc_data *data = priv;
-
- data->oob_nfc_dev->deinit_func();
-
- os_free(data);
-}
-
-
-struct oob_device_data oob_nfc_device_data = {
- .device_name = NULL,
- .device_path = NULL,
- .init_func = init_nfc,
- .read_func = read_nfc,
- .write_func = write_nfc,
- .deinit_func = deinit_nfc,
-};
diff --git a/contrib/wpa/src/wps/wps_nfc_pn531.c b/contrib/wpa/src/wps/wps_nfc_pn531.c
deleted file mode 100644
index 7e05e4d..0000000
--- a/contrib/wpa/src/wps/wps_nfc_pn531.c
+++ /dev/null
@@ -1,113 +0,0 @@
-/*
- * NFC PN531 routines for Wi-Fi Protected Setup
- * Copyright (c) 2009, Masashi Honma <honma@ictec.co.jp>
- *
- * This program is free software; you can redistribute it and/or modify
- * it under the terms of the GNU General Public License version 2 as
- * published by the Free Software Foundation.
- *
- * Alternatively, this software may be distributed under the terms of BSD
- * license.
- *
- * See README and COPYING for more details.
- */
-
-#include "includes.h"
-#include "common.h"
-
-#include "wps/wps.h"
-#include "wps_i.h"
-
-#include "WpsNfcType.h"
-#include "WpsNfc.h"
-
-
-static int init_nfc_pn531(char *path)
-{
- u32 ret;
-
- ret = WpsNfcInit();
- if (ret != WPS_NFCLIB_ERR_SUCCESS) {
- wpa_printf(MSG_ERROR, "WPS (PN531): Failed to initialize "
- "NFC Library: 0x%08x", ret);
- return -1;
- }
-
- ret = WpsNfcOpenDevice((int8 *) path);
- if (ret != WPS_NFCLIB_ERR_SUCCESS) {
- wpa_printf(MSG_ERROR, "WPS (PN531): Failed to open "
- "NFC Device(%s): 0x%08x", path, ret);
- goto fail;
- }
-
- ret = WpsNfcTokenDiscovery();
- if (ret != WPS_NFCLIB_ERR_SUCCESS) {
- wpa_printf(MSG_ERROR, "WPS (PN531): Failed to discover "
- "token: 0x%08x", ret);
- WpsNfcCloseDevice();
- goto fail;
- }
-
- return 0;
-
-fail:
- WpsNfcDeinit();
- return -1;
-}
-
-
-static void * read_nfc_pn531(size_t *size)
-{
- uint32 len;
- u32 ret;
- int8 *data;
-
- ret = WpsNfcRawReadToken(&data, &len);
- if (ret != WPS_NFCLIB_ERR_SUCCESS) {
- wpa_printf(MSG_ERROR, "WPS (PN531): Failed to read: 0x%08x",
- ret);
- return NULL;
- }
-
- *size = len;
- return data;
-}
-
-
-static int write_nfc_pn531(void *data, size_t len)
-{
- u32 ret;
-
- ret = WpsNfcRawWriteToken(data, len);
- if (ret != WPS_NFCLIB_ERR_SUCCESS) {
- wpa_printf(MSG_ERROR, "WPS (PN531): Failed to write: 0x%08x",
- ret);
- return -1;
- }
-
- return 0;
-}
-
-
-static void deinit_nfc_pn531(void)
-{
- u32 ret;
-
- ret = WpsNfcCloseDevice();
- if (ret != WPS_NFCLIB_ERR_SUCCESS)
- wpa_printf(MSG_ERROR, "WPS (PN531): Failed to close "
- "NFC Device: 0x%08x", ret);
-
- ret = WpsNfcDeinit();
- if (ret != WPS_NFCLIB_ERR_SUCCESS)
- wpa_printf(MSG_ERROR, "WPS (PN531): Failed to deinitialize "
- "NFC Library: 0x%08x", ret);
-}
-
-
-struct oob_nfc_device_data oob_nfc_pn531_device_data = {
- .init_func = init_nfc_pn531,
- .read_func = read_nfc_pn531,
- .write_func = write_nfc_pn531,
- .deinit_func = deinit_nfc_pn531,
-};
diff --git a/contrib/wpa/src/wps/wps_registrar.c b/contrib/wpa/src/wps/wps_registrar.c
index 81ddf3a..b650a3c 100644
--- a/contrib/wpa/src/wps/wps_registrar.c
+++ b/contrib/wpa/src/wps/wps_registrar.c
@@ -1,15 +1,9 @@
/*
* Wi-Fi Protected Setup - Registrar
- * Copyright (c) 2008-2009, Jouni Malinen <j@w1.fi>
+ * Copyright (c) 2008-2012, Jouni Malinen <j@w1.fi>
*
- * This program is free software; you can redistribute it and/or modify
- * it under the terms of the GNU General Public License version 2 as
- * published by the Free Software Foundation.
- *
- * Alternatively, this software may be distributed under the terms of BSD
- * license.
- *
- * See README and COPYING for more details.
+ * This software may be distributed under the terms of the BSD license.
+ * See README for more details.
*/
#include "utils/includes.h"
@@ -21,13 +15,63 @@
#include "utils/list.h"
#include "crypto/crypto.h"
#include "crypto/sha256.h"
+#include "crypto/random.h"
#include "common/ieee802_11_defs.h"
#include "wps_i.h"
#include "wps_dev_attr.h"
#include "wps_upnp.h"
#include "wps_upnp_i.h"
+#ifndef CONFIG_WPS_STRICT
#define WPS_WORKAROUNDS
+#endif /* CONFIG_WPS_STRICT */
+
+#ifdef CONFIG_WPS_NFC
+
+struct wps_nfc_pw_token {
+ struct dl_list list;
+ u8 pubkey_hash[WPS_OOB_PUBKEY_HASH_LEN];
+ u16 pw_id;
+ u8 dev_pw[WPS_OOB_DEVICE_PASSWORD_LEN];
+ size_t dev_pw_len;
+};
+
+
+static void wps_remove_nfc_pw_token(struct wps_nfc_pw_token *token)
+{
+ dl_list_del(&token->list);
+ os_free(token);
+}
+
+
+static void wps_free_nfc_pw_tokens(struct dl_list *tokens, u16 pw_id)
+{
+ struct wps_nfc_pw_token *token, *prev;
+ dl_list_for_each_safe(token, prev, tokens, struct wps_nfc_pw_token,
+ list) {
+ if (pw_id == 0 || pw_id == token->pw_id)
+ wps_remove_nfc_pw_token(token);
+ }
+}
+
+
+static struct wps_nfc_pw_token * wps_get_nfc_pw_token(struct dl_list *tokens,
+ u16 pw_id)
+{
+ struct wps_nfc_pw_token *token;
+ dl_list_for_each(token, tokens, struct wps_nfc_pw_token, list) {
+ if (pw_id == token->pw_id)
+ return token;
+ }
+ return NULL;
+}
+
+#else /* CONFIG_WPS_NFC */
+
+#define wps_free_nfc_pw_tokens(t, p) do { } while (0)
+
+#endif /* CONFIG_WPS_NFC */
+
struct wps_uuid_pin {
struct dl_list list;
@@ -39,6 +83,7 @@ struct wps_uuid_pin {
#define PIN_EXPIRES BIT(1)
int flags;
struct os_time expiration;
+ u8 enrollee_addr[ETH_ALEN];
};
@@ -104,7 +149,8 @@ struct wps_registrar {
void (*pin_needed_cb)(void *ctx, const u8 *uuid_e,
const struct wps_device_data *dev);
void (*reg_success_cb)(void *ctx, const u8 *mac_addr,
- const u8 *uuid_e);
+ const u8 *uuid_e, const u8 *dev_pw,
+ size_t dev_pw_len);
void (*set_sel_reg_cb)(void *ctx, int sel_reg, u16 dev_passwd_id,
u16 sel_reg_config_methods);
void (*enrollee_seen_cb)(void *ctx, const u8 *addr, const u8 *uuid_e,
@@ -114,6 +160,7 @@ struct wps_registrar {
void *cb_ctx;
struct dl_list pins;
+ struct dl_list nfc_pw_tokens;
struct wps_pbc_session *pbc_sessions;
int skip_cred_build;
@@ -123,10 +170,19 @@ struct wps_registrar {
int sel_reg_dev_password_id_override;
int sel_reg_config_methods_override;
int static_wep_only;
+ int dualband;
struct wps_registrar_device *devices;
int force_pbc_overlap;
+
+ u8 authorized_macs[WPS_MAX_AUTHORIZED_MACS][ETH_ALEN];
+ u8 authorized_macs_union[WPS_MAX_AUTHORIZED_MACS][ETH_ALEN];
+
+ u8 p2p_dev_addr[ETH_ALEN];
+
+ u8 pbc_ignore_uuid[WPS_UUID_LEN];
+ struct os_time pbc_ignore_start;
};
@@ -134,6 +190,54 @@ static int wps_set_ie(struct wps_registrar *reg);
static void wps_registrar_pbc_timeout(void *eloop_ctx, void *timeout_ctx);
static void wps_registrar_set_selected_timeout(void *eloop_ctx,
void *timeout_ctx);
+static void wps_registrar_remove_pin(struct wps_registrar *reg,
+ struct wps_uuid_pin *pin);
+
+
+static void wps_registrar_add_authorized_mac(struct wps_registrar *reg,
+ const u8 *addr)
+{
+ int i;
+ wpa_printf(MSG_DEBUG, "WPS: Add authorized MAC " MACSTR,
+ MAC2STR(addr));
+ for (i = 0; i < WPS_MAX_AUTHORIZED_MACS; i++)
+ if (os_memcmp(reg->authorized_macs[i], addr, ETH_ALEN) == 0) {
+ wpa_printf(MSG_DEBUG, "WPS: Authorized MAC was "
+ "already in the list");
+ return; /* already in list */
+ }
+ for (i = WPS_MAX_AUTHORIZED_MACS - 1; i > 0; i--)
+ os_memcpy(reg->authorized_macs[i], reg->authorized_macs[i - 1],
+ ETH_ALEN);
+ os_memcpy(reg->authorized_macs[0], addr, ETH_ALEN);
+ wpa_hexdump(MSG_DEBUG, "WPS: Authorized MACs",
+ (u8 *) reg->authorized_macs, sizeof(reg->authorized_macs));
+}
+
+
+static void wps_registrar_remove_authorized_mac(struct wps_registrar *reg,
+ const u8 *addr)
+{
+ int i;
+ wpa_printf(MSG_DEBUG, "WPS: Remove authorized MAC " MACSTR,
+ MAC2STR(addr));
+ for (i = 0; i < WPS_MAX_AUTHORIZED_MACS; i++) {
+ if (os_memcmp(reg->authorized_macs, addr, ETH_ALEN) == 0)
+ break;
+ }
+ if (i == WPS_MAX_AUTHORIZED_MACS) {
+ wpa_printf(MSG_DEBUG, "WPS: Authorized MAC was not in the "
+ "list");
+ return; /* not in the list */
+ }
+ for (; i + 1 < WPS_MAX_AUTHORIZED_MACS; i++)
+ os_memcpy(reg->authorized_macs[i], reg->authorized_macs[i + 1],
+ ETH_ALEN);
+ os_memset(reg->authorized_macs[WPS_MAX_AUTHORIZED_MACS - 1], 0,
+ ETH_ALEN);
+ wpa_hexdump(MSG_DEBUG, "WPS: Authorized MACs",
+ (u8 *) reg->authorized_macs, sizeof(reg->authorized_macs));
+}
static void wps_free_devices(struct wps_registrar_device *dev)
@@ -254,20 +358,29 @@ static void wps_registrar_add_pbc_session(struct wps_registrar *reg,
static void wps_registrar_remove_pbc_session(struct wps_registrar *reg,
- const u8 *addr, const u8 *uuid_e)
+ const u8 *uuid_e,
+ const u8 *p2p_dev_addr)
{
- struct wps_pbc_session *pbc, *prev = NULL;
+ struct wps_pbc_session *pbc, *prev = NULL, *tmp;
pbc = reg->pbc_sessions;
while (pbc) {
- if (os_memcmp(pbc->addr, addr, ETH_ALEN) == 0 &&
- os_memcmp(pbc->uuid_e, uuid_e, WPS_UUID_LEN) == 0) {
+ if (os_memcmp(pbc->uuid_e, uuid_e, WPS_UUID_LEN) == 0 ||
+ (p2p_dev_addr && !is_zero_ether_addr(reg->p2p_dev_addr) &&
+ os_memcmp(reg->p2p_dev_addr, p2p_dev_addr, ETH_ALEN) ==
+ 0)) {
if (prev)
prev->next = pbc->next;
else
reg->pbc_sessions = pbc->next;
- os_free(pbc);
- break;
+ tmp = pbc;
+ pbc = pbc->next;
+ wpa_printf(MSG_DEBUG, "WPS: Removing PBC session for "
+ "addr=" MACSTR, MAC2STR(tmp->addr));
+ wpa_hexdump(MSG_DEBUG, "WPS: Removed UUID-E",
+ tmp->uuid_e, WPS_UUID_LEN);
+ os_free(tmp);
+ continue;
}
prev = pbc;
pbc = pbc->next;
@@ -275,26 +388,50 @@ static void wps_registrar_remove_pbc_session(struct wps_registrar *reg,
}
-static int wps_registrar_pbc_overlap(struct wps_registrar *reg,
- const u8 *addr, const u8 *uuid_e)
+int wps_registrar_pbc_overlap(struct wps_registrar *reg,
+ const u8 *addr, const u8 *uuid_e)
{
int count = 0;
struct wps_pbc_session *pbc;
+ struct wps_pbc_session *first = NULL;
struct os_time now;
os_get_time(&now);
+ wpa_printf(MSG_DEBUG, "WPS: Checking active PBC sessions for overlap");
+
+ if (uuid_e) {
+ wpa_printf(MSG_DEBUG, "WPS: Add one for the requested UUID");
+ wpa_hexdump(MSG_DEBUG, "WPS: Requested UUID",
+ uuid_e, WPS_UUID_LEN);
+ count++;
+ }
+
for (pbc = reg->pbc_sessions; pbc; pbc = pbc->next) {
- if (now.sec > pbc->timestamp.sec + WPS_PBC_WALK_TIME)
+ wpa_printf(MSG_DEBUG, "WPS: Consider PBC session with " MACSTR,
+ MAC2STR(pbc->addr));
+ wpa_hexdump(MSG_DEBUG, "WPS: UUID-E",
+ pbc->uuid_e, WPS_UUID_LEN);
+ if (now.sec > pbc->timestamp.sec + WPS_PBC_WALK_TIME) {
+ wpa_printf(MSG_DEBUG, "WPS: PBC walk time has "
+ "expired");
break;
- if (addr == NULL || os_memcmp(addr, pbc->addr, ETH_ALEN) ||
- uuid_e == NULL ||
- os_memcmp(uuid_e, pbc->uuid_e, WPS_UUID_LEN))
+ }
+ if (first &&
+ os_memcmp(pbc->uuid_e, first->uuid_e, WPS_UUID_LEN) == 0) {
+ wpa_printf(MSG_DEBUG, "WPS: Same Enrollee");
+ continue; /* same Enrollee */
+ }
+ if (uuid_e == NULL ||
+ os_memcmp(uuid_e, pbc->uuid_e, WPS_UUID_LEN)) {
+ wpa_printf(MSG_DEBUG, "WPS: New Enrollee");
count++;
+ }
+ if (first == NULL)
+ first = pbc;
}
- if (addr || uuid_e)
- count++;
+ wpa_printf(MSG_DEBUG, "WPS: %u active PBC session(s) found", count);
return count > 1 ? 1 : 0;
}
@@ -339,7 +476,7 @@ static void wps_registrar_free_pending_m2(struct wps_context *wps)
static int wps_build_ap_setup_locked(struct wps_context *wps,
struct wpabuf *msg)
{
- if (wps->ap_setup_locked) {
+ if (wps->ap_setup_locked && wps->ap_setup_locked != 2) {
wpa_printf(MSG_DEBUG, "WPS: * AP Setup Locked");
wpabuf_put_be16(msg, ATTR_AP_SETUP_LOCKED);
wpabuf_put_be16(msg, 1);
@@ -378,15 +515,59 @@ static int wps_build_sel_reg_dev_password_id(struct wps_registrar *reg,
}
+static int wps_build_sel_pbc_reg_uuid_e(struct wps_registrar *reg,
+ struct wpabuf *msg)
+{
+ u16 id = reg->pbc ? DEV_PW_PUSHBUTTON : DEV_PW_DEFAULT;
+ if (!reg->sel_reg_union)
+ return 0;
+ if (reg->sel_reg_dev_password_id_override >= 0)
+ id = reg->sel_reg_dev_password_id_override;
+ if (id != DEV_PW_PUSHBUTTON || !reg->dualband)
+ return 0;
+ return wps_build_uuid_e(msg, reg->wps->uuid);
+}
+
+
+static void wps_set_pushbutton(u16 *methods, u16 conf_methods)
+{
+ *methods |= WPS_CONFIG_PUSHBUTTON;
+#ifdef CONFIG_WPS2
+ if ((conf_methods & WPS_CONFIG_VIRT_PUSHBUTTON) ==
+ WPS_CONFIG_VIRT_PUSHBUTTON)
+ *methods |= WPS_CONFIG_VIRT_PUSHBUTTON;
+ if ((conf_methods & WPS_CONFIG_PHY_PUSHBUTTON) ==
+ WPS_CONFIG_PHY_PUSHBUTTON)
+ *methods |= WPS_CONFIG_PHY_PUSHBUTTON;
+ if ((*methods & WPS_CONFIG_VIRT_PUSHBUTTON) !=
+ WPS_CONFIG_VIRT_PUSHBUTTON &&
+ (*methods & WPS_CONFIG_PHY_PUSHBUTTON) !=
+ WPS_CONFIG_PHY_PUSHBUTTON) {
+ /*
+ * Required to include virtual/physical flag, but we were not
+ * configured with push button type, so have to default to one
+ * of them.
+ */
+ *methods |= WPS_CONFIG_PHY_PUSHBUTTON;
+ }
+#endif /* CONFIG_WPS2 */
+}
+
+
static int wps_build_sel_reg_config_methods(struct wps_registrar *reg,
struct wpabuf *msg)
{
u16 methods;
if (!reg->sel_reg_union)
return 0;
- methods = reg->wps->config_methods & ~WPS_CONFIG_PUSHBUTTON;
+ methods = reg->wps->config_methods;
+ methods &= ~WPS_CONFIG_PUSHBUTTON;
+#ifdef CONFIG_WPS2
+ methods &= ~(WPS_CONFIG_VIRT_PUSHBUTTON |
+ WPS_CONFIG_PHY_PUSHBUTTON);
+#endif /* CONFIG_WPS2 */
if (reg->pbc)
- methods |= WPS_CONFIG_PUSHBUTTON;
+ wps_set_pushbutton(&methods, reg->wps->config_methods);
if (reg->sel_reg_config_methods_override >= 0)
methods = reg->sel_reg_config_methods_override;
wpa_printf(MSG_DEBUG, "WPS: * Selected Registrar Config Methods (%x)",
@@ -407,6 +588,10 @@ static int wps_build_probe_config_methods(struct wps_registrar *reg,
* external Registrars.
*/
methods = reg->wps->config_methods & ~WPS_CONFIG_PUSHBUTTON;
+#ifdef CONFIG_WPS2
+ methods &= ~(WPS_CONFIG_VIRT_PUSHBUTTON |
+ WPS_CONFIG_PHY_PUSHBUTTON);
+#endif /* CONFIG_WPS2 */
wpa_printf(MSG_DEBUG, "WPS: * Config Methods (%x)", methods);
wpabuf_put_be16(msg, ATTR_CONFIG_METHODS);
wpabuf_put_be16(msg, 2);
@@ -418,11 +603,23 @@ static int wps_build_probe_config_methods(struct wps_registrar *reg,
static int wps_build_config_methods_r(struct wps_registrar *reg,
struct wpabuf *msg)
{
- u16 methods;
- methods = reg->wps->config_methods & ~WPS_CONFIG_PUSHBUTTON;
- if (reg->pbc)
- methods |= WPS_CONFIG_PUSHBUTTON;
- return wps_build_config_methods(msg, methods);
+ return wps_build_config_methods(msg, reg->wps->config_methods);
+}
+
+
+const u8 * wps_authorized_macs(struct wps_registrar *reg, size_t *count)
+{
+ *count = 0;
+
+#ifdef CONFIG_WPS2
+ while (*count < WPS_MAX_AUTHORIZED_MACS) {
+ if (is_zero_ether_addr(reg->authorized_macs_union[*count]))
+ break;
+ (*count)++;
+ }
+#endif /* CONFIG_WPS2 */
+
+ return (const u8 *) reg->authorized_macs_union;
}
@@ -447,6 +644,7 @@ wps_registrar_init(struct wps_context *wps,
return NULL;
dl_list_init(&reg->pins);
+ dl_list_init(&reg->nfc_pw_tokens);
reg->wps = wps;
reg->new_psk_cb = cfg->new_psk_cb;
reg->set_ie_cb = cfg->set_ie_cb;
@@ -468,6 +666,7 @@ wps_registrar_init(struct wps_context *wps,
reg->sel_reg_dev_password_id_override = -1;
reg->sel_reg_config_methods_override = -1;
reg->static_wep_only = cfg->static_wep_only;
+ reg->dualband = cfg->dualband;
if (wps_set_ie(reg)) {
wps_registrar_deinit(reg);
@@ -489,6 +688,7 @@ void wps_registrar_deinit(struct wps_registrar *reg)
eloop_cancel_timeout(wps_registrar_pbc_timeout, reg, NULL);
eloop_cancel_timeout(wps_registrar_set_selected_timeout, reg, NULL);
wps_free_pins(&reg->pins);
+ wps_free_nfc_pw_tokens(&reg->nfc_pw_tokens, 0);
wps_free_pbc_sessions(reg->pbc_sessions);
wpabuf_free(reg->extra_cred);
wps_free_devices(reg->devices);
@@ -496,23 +696,42 @@ void wps_registrar_deinit(struct wps_registrar *reg)
}
+static void wps_registrar_invalidate_unused(struct wps_registrar *reg)
+{
+ struct wps_uuid_pin *pin;
+
+ dl_list_for_each(pin, &reg->pins, struct wps_uuid_pin, list) {
+ if (pin->wildcard_uuid == 1 && !(pin->flags & PIN_LOCKED)) {
+ wpa_printf(MSG_DEBUG, "WPS: Invalidate previously "
+ "configured wildcard PIN");
+ wps_registrar_remove_pin(reg, pin);
+ break;
+ }
+ }
+}
+
+
/**
* wps_registrar_add_pin - Configure a new PIN for Registrar
* @reg: Registrar data from wps_registrar_init()
+ * @addr: Enrollee MAC address or %NULL if not known
* @uuid: UUID-E or %NULL for wildcard (any UUID)
* @pin: PIN (Device Password)
* @pin_len: Length of pin in octets
* @timeout: Time (in seconds) when the PIN will be invalidated; 0 = no timeout
* Returns: 0 on success, -1 on failure
*/
-int wps_registrar_add_pin(struct wps_registrar *reg, const u8 *uuid,
- const u8 *pin, size_t pin_len, int timeout)
+int wps_registrar_add_pin(struct wps_registrar *reg, const u8 *addr,
+ const u8 *uuid, const u8 *pin, size_t pin_len,
+ int timeout)
{
struct wps_uuid_pin *p;
p = os_zalloc(sizeof(*p));
if (p == NULL)
return -1;
+ if (addr)
+ os_memcpy(p->enrollee_addr, addr, ETH_ALEN);
if (uuid == NULL)
p->wildcard_uuid = 1;
else
@@ -531,6 +750,9 @@ int wps_registrar_add_pin(struct wps_registrar *reg, const u8 *uuid,
p->expiration.sec += timeout;
}
+ if (p->wildcard_uuid)
+ wps_registrar_invalidate_unused(reg);
+
dl_list_add(&reg->pins, &p->list);
wpa_printf(MSG_DEBUG, "WPS: A new PIN configured (timeout=%d)",
@@ -539,6 +761,11 @@ int wps_registrar_add_pin(struct wps_registrar *reg, const u8 *uuid,
wpa_hexdump_ascii_key(MSG_DEBUG, "WPS: PIN", pin, pin_len);
reg->selected_registrar = 1;
reg->pbc = 0;
+ if (addr)
+ wps_registrar_add_authorized_mac(reg, addr);
+ else
+ wps_registrar_add_authorized_mac(
+ reg, (u8 *) "\xff\xff\xff\xff\xff\xff");
wps_registrar_selected_registrar_changed(reg);
eloop_cancel_timeout(wps_registrar_set_selected_timeout, reg, NULL);
eloop_register_timeout(WPS_PBC_WALK_TIME, 0,
@@ -549,6 +776,22 @@ int wps_registrar_add_pin(struct wps_registrar *reg, const u8 *uuid,
}
+static void wps_registrar_remove_pin(struct wps_registrar *reg,
+ struct wps_uuid_pin *pin)
+{
+ u8 *addr;
+ u8 bcast[ETH_ALEN] = { 0xff, 0xff, 0xff, 0xff, 0xff, 0xff };
+
+ if (is_zero_ether_addr(pin->enrollee_addr))
+ addr = bcast;
+ else
+ addr = pin->enrollee_addr;
+ wps_registrar_remove_authorized_mac(reg, addr);
+ wps_remove_pin(pin);
+ wps_registrar_selected_registrar_changed(reg);
+}
+
+
static void wps_registrar_expire_pins(struct wps_registrar *reg)
{
struct wps_uuid_pin *pin, *prev;
@@ -561,9 +804,40 @@ static void wps_registrar_expire_pins(struct wps_registrar *reg)
os_time_before(&pin->expiration, &now)) {
wpa_hexdump(MSG_DEBUG, "WPS: Expired PIN for UUID",
pin->uuid, WPS_UUID_LEN);
- wps_remove_pin(pin);
+ wps_registrar_remove_pin(reg, pin);
+ }
+ }
+}
+
+
+/**
+ * wps_registrar_invalidate_wildcard_pin - Invalidate a wildcard PIN
+ * @reg: Registrar data from wps_registrar_init()
+ * @dev_pw: PIN to search for or %NULL to match any
+ * @dev_pw_len: Length of dev_pw in octets
+ * Returns: 0 on success, -1 if not wildcard PIN is enabled
+ */
+static int wps_registrar_invalidate_wildcard_pin(struct wps_registrar *reg,
+ const u8 *dev_pw,
+ size_t dev_pw_len)
+{
+ struct wps_uuid_pin *pin, *prev;
+
+ dl_list_for_each_safe(pin, prev, &reg->pins, struct wps_uuid_pin, list)
+ {
+ if (dev_pw && pin->pin &&
+ (dev_pw_len != pin->pin_len ||
+ os_memcmp(dev_pw, pin->pin, dev_pw_len) != 0))
+ continue; /* different PIN */
+ if (pin->wildcard_uuid) {
+ wpa_hexdump(MSG_DEBUG, "WPS: Invalidated PIN for UUID",
+ pin->uuid, WPS_UUID_LEN);
+ wps_registrar_remove_pin(reg, pin);
+ return 0;
}
}
+
+ return -1;
}
@@ -582,7 +856,7 @@ int wps_registrar_invalidate_pin(struct wps_registrar *reg, const u8 *uuid)
if (os_memcmp(pin->uuid, uuid, WPS_UUID_LEN) == 0) {
wpa_hexdump(MSG_DEBUG, "WPS: Invalidated PIN for UUID",
pin->uuid, WPS_UUID_LEN);
- wps_remove_pin(pin);
+ wps_registrar_remove_pin(reg, pin);
return 0;
}
}
@@ -610,10 +884,11 @@ static const u8 * wps_registrar_get_pin(struct wps_registrar *reg,
/* Check for wildcard UUIDs since none of the UUID-specific
* PINs matched */
dl_list_for_each(pin, &reg->pins, struct wps_uuid_pin, list) {
- if (pin->wildcard_uuid == 1) {
+ if (pin->wildcard_uuid == 1 ||
+ pin->wildcard_uuid == 2) {
wpa_printf(MSG_DEBUG, "WPS: Found a wildcard "
"PIN. Assigned it for this UUID-E");
- pin->wildcard_uuid = 2;
+ pin->wildcard_uuid++;
os_memcpy(pin->uuid, uuid, WPS_UUID_LEN);
found = pin;
break;
@@ -655,7 +930,7 @@ int wps_registrar_unlock_pin(struct wps_registrar *reg, const u8 *uuid)
dl_list_for_each(pin, &reg->pins, struct wps_uuid_pin, list) {
if (os_memcmp(pin->uuid, uuid, WPS_UUID_LEN) == 0) {
- if (pin->wildcard_uuid == 2) {
+ if (pin->wildcard_uuid == 3) {
wpa_printf(MSG_DEBUG, "WPS: Invalidating used "
"wildcard PIN");
return wps_registrar_invalidate_pin(reg, uuid);
@@ -673,6 +948,9 @@ static void wps_registrar_stop_pbc(struct wps_registrar *reg)
{
reg->selected_registrar = 0;
reg->pbc = 0;
+ os_memset(reg->p2p_dev_addr, 0, ETH_ALEN);
+ wps_registrar_remove_authorized_mac(reg,
+ (u8 *) "\xff\xff\xff\xff\xff\xff");
wps_registrar_selected_registrar_changed(reg);
}
@@ -690,26 +968,40 @@ static void wps_registrar_pbc_timeout(void *eloop_ctx, void *timeout_ctx)
/**
* wps_registrar_button_pushed - Notify Registrar that AP button was pushed
* @reg: Registrar data from wps_registrar_init()
- * Returns: 0 on success, -1 on failure
+ * @p2p_dev_addr: Limit allowed PBC devices to the specified P2P device, %NULL
+ * indicates no such filtering
+ * Returns: 0 on success, -1 on failure, -2 on session overlap
*
* This function is called on an AP when a push button is pushed to activate
* PBC mode. The PBC mode will be stopped after walk time (2 minutes) timeout
- * or when a PBC registration is completed.
+ * or when a PBC registration is completed. If more than one Enrollee in active
+ * PBC mode has been detected during the monitor time (previous 2 minutes), the
+ * PBC mode is not activated and -2 is returned to indicate session overlap.
+ * This is skipped if a specific Enrollee is selected.
*/
-int wps_registrar_button_pushed(struct wps_registrar *reg)
+int wps_registrar_button_pushed(struct wps_registrar *reg,
+ const u8 *p2p_dev_addr)
{
- if (wps_registrar_pbc_overlap(reg, NULL, NULL)) {
+ if (p2p_dev_addr == NULL &&
+ wps_registrar_pbc_overlap(reg, NULL, NULL)) {
wpa_printf(MSG_DEBUG, "WPS: PBC overlap - do not start PBC "
"mode");
wps_pbc_overlap_event(reg->wps);
- return -1;
+ return -2;
}
wpa_printf(MSG_DEBUG, "WPS: Button pushed - PBC mode started");
reg->force_pbc_overlap = 0;
reg->selected_registrar = 1;
reg->pbc = 1;
+ if (p2p_dev_addr)
+ os_memcpy(reg->p2p_dev_addr, p2p_dev_addr, ETH_ALEN);
+ else
+ os_memset(reg->p2p_dev_addr, 0, ETH_ALEN);
+ wps_registrar_add_authorized_mac(reg,
+ (u8 *) "\xff\xff\xff\xff\xff\xff");
wps_registrar_selected_registrar_changed(reg);
+ eloop_cancel_timeout(wps_registrar_set_selected_timeout, reg, NULL);
eloop_cancel_timeout(wps_registrar_pbc_timeout, reg, NULL);
eloop_register_timeout(WPS_PBC_WALK_TIME, 0, wps_registrar_pbc_timeout,
reg, NULL);
@@ -734,6 +1026,46 @@ static void wps_registrar_pin_completed(struct wps_registrar *reg)
}
+void wps_registrar_complete(struct wps_registrar *registrar, const u8 *uuid_e,
+ const u8 *dev_pw, size_t dev_pw_len)
+{
+ if (registrar->pbc) {
+ wps_registrar_remove_pbc_session(registrar,
+ uuid_e, NULL);
+ wps_registrar_pbc_completed(registrar);
+ os_get_time(&registrar->pbc_ignore_start);
+ os_memcpy(registrar->pbc_ignore_uuid, uuid_e, WPS_UUID_LEN);
+ } else {
+ wps_registrar_pin_completed(registrar);
+ }
+
+ if (dev_pw &&
+ wps_registrar_invalidate_wildcard_pin(registrar, dev_pw,
+ dev_pw_len) == 0) {
+ wpa_hexdump_key(MSG_DEBUG, "WPS: Invalidated wildcard PIN",
+ dev_pw, dev_pw_len);
+ }
+}
+
+
+int wps_registrar_wps_cancel(struct wps_registrar *reg)
+{
+ if (reg->pbc) {
+ wpa_printf(MSG_DEBUG, "WPS: PBC is set - cancelling it");
+ wps_registrar_pbc_timeout(reg, NULL);
+ eloop_cancel_timeout(wps_registrar_pbc_timeout, reg, NULL);
+ return 1;
+ } else if (reg->selected_registrar) {
+ /* PIN Method */
+ wpa_printf(MSG_DEBUG, "WPS: PIN is set - cancelling it");
+ wps_registrar_pin_completed(reg);
+ wps_registrar_invalidate_wildcard_pin(reg, NULL, 0);
+ return 1;
+ }
+ return 0;
+}
+
+
/**
* wps_registrar_probe_req_rx - Notify Registrar of Probe Request
* @reg: Registrar data from wps_registrar_init()
@@ -745,9 +1077,11 @@ static void wps_registrar_pin_completed(struct wps_registrar *reg)
* situation with other WPS APs.
*/
void wps_registrar_probe_req_rx(struct wps_registrar *reg, const u8 *addr,
- const struct wpabuf *wps_data)
+ const struct wpabuf *wps_data,
+ int p2p_wildcard)
{
struct wps_parse_attr attr;
+ int skip_add = 0;
wpa_hexdump_buf(MSG_MSGDUMP,
"WPS: Probe Request with WPS data received",
@@ -755,11 +1089,6 @@ void wps_registrar_probe_req_rx(struct wps_registrar *reg, const u8 *addr,
if (wps_parse_msg(wps_data, &attr) < 0)
return;
- if (!wps_version_supported(attr.version)) {
- wpa_printf(MSG_DEBUG, "WPS: Unsupported ProbeReq WPS IE "
- "version 0x%x", attr.version ? *attr.version : 0);
- return;
- }
if (attr.config_methods == NULL) {
wpa_printf(MSG_DEBUG, "WPS: No Config Methods attribute in "
@@ -774,7 +1103,7 @@ void wps_registrar_probe_req_rx(struct wps_registrar *reg, const u8 *addr,
}
if (reg->enrollee_seen_cb && attr.uuid_e &&
- attr.primary_dev_type && attr.request_type) {
+ attr.primary_dev_type && attr.request_type && !p2p_wildcard) {
char *dev_name = NULL;
if (attr.dev_name) {
dev_name = os_zalloc(attr.dev_name_len + 1);
@@ -801,8 +1130,27 @@ void wps_registrar_probe_req_rx(struct wps_registrar *reg, const u8 *addr,
"UUID-E included");
return;
}
+ wpa_hexdump(MSG_DEBUG, "WPS: UUID-E from Probe Request", attr.uuid_e,
+ WPS_UUID_LEN);
- wps_registrar_add_pbc_session(reg, addr, attr.uuid_e);
+#ifdef WPS_WORKAROUNDS
+ if (reg->pbc_ignore_start.sec &&
+ os_memcmp(attr.uuid_e, reg->pbc_ignore_uuid, WPS_UUID_LEN) == 0) {
+ struct os_time now, dur;
+ os_get_time(&now);
+ os_time_sub(&now, &reg->pbc_ignore_start, &dur);
+ if (dur.sec >= 0 && dur.sec < 5) {
+ wpa_printf(MSG_DEBUG, "WPS: Ignore PBC activation "
+ "based on Probe Request from the Enrollee "
+ "that just completed PBC provisioning");
+ skip_add = 1;
+ } else
+ reg->pbc_ignore_start.sec = 0;
+ }
+#endif /* WPS_WORKAROUNDS */
+
+ if (!skip_add)
+ wps_registrar_add_pbc_session(reg, addr, attr.uuid_e);
if (wps_registrar_pbc_overlap(reg, addr, attr.uuid_e)) {
wpa_printf(MSG_DEBUG, "WPS: PBC session overlap detected");
reg->force_pbc_overlap = 1;
@@ -832,12 +1180,13 @@ static void wps_cb_pin_needed(struct wps_registrar *reg, const u8 *uuid_e,
static void wps_cb_reg_success(struct wps_registrar *reg, const u8 *mac_addr,
- const u8 *uuid_e)
+ const u8 *uuid_e, const u8 *dev_pw,
+ size_t dev_pw_len)
{
if (reg->reg_success_cb == NULL)
return;
- reg->reg_success_cb(reg->cb_ctx, mac_addr, uuid_e);
+ reg->reg_success_cb(reg->cb_ctx, mac_addr, uuid_e, dev_pw, dev_pw_len);
}
@@ -856,74 +1205,84 @@ static void wps_cb_set_sel_reg(struct wps_registrar *reg)
if (reg->selected_registrar) {
methods = reg->wps->config_methods & ~WPS_CONFIG_PUSHBUTTON;
+#ifdef CONFIG_WPS2
+ methods &= ~(WPS_CONFIG_VIRT_PUSHBUTTON |
+ WPS_CONFIG_PHY_PUSHBUTTON);
+#endif /* CONFIG_WPS2 */
if (reg->pbc)
- methods |= WPS_CONFIG_PUSHBUTTON;
+ wps_set_pushbutton(&methods, reg->wps->config_methods);
}
+ wpa_printf(MSG_DEBUG, "WPS: wps_cb_set_sel_reg: sel_reg=%d "
+ "config_methods=0x%x pbc=%d methods=0x%x",
+ reg->selected_registrar, reg->wps->config_methods,
+ reg->pbc, methods);
+
reg->set_sel_reg_cb(reg->cb_ctx, reg->selected_registrar,
reg->pbc ? DEV_PW_PUSHBUTTON : DEV_PW_DEFAULT,
methods);
}
-/* Encapsulate WPS IE data with one (or more, if needed) IE headers */
-static struct wpabuf * wps_ie_encapsulate(struct wpabuf *data)
-{
- struct wpabuf *ie;
- const u8 *pos, *end;
-
- ie = wpabuf_alloc(wpabuf_len(data) + 100);
- if (ie == NULL) {
- wpabuf_free(data);
- return NULL;
- }
-
- pos = wpabuf_head(data);
- end = pos + wpabuf_len(data);
-
- while (end > pos) {
- size_t frag_len = end - pos;
- if (frag_len > 251)
- frag_len = 251;
- wpabuf_put_u8(ie, WLAN_EID_VENDOR_SPECIFIC);
- wpabuf_put_u8(ie, 4 + frag_len);
- wpabuf_put_be32(ie, WPS_DEV_OUI_WFA);
- wpabuf_put_data(ie, pos, frag_len);
- pos += frag_len;
- }
-
- wpabuf_free(data);
-
- return ie;
-}
-
-
static int wps_set_ie(struct wps_registrar *reg)
{
struct wpabuf *beacon;
struct wpabuf *probe;
+ const u8 *auth_macs;
+ size_t count;
+ size_t vendor_len = 0;
+ int i;
if (reg->set_ie_cb == NULL)
return 0;
- wpa_printf(MSG_DEBUG, "WPS: Build Beacon and Probe Response IEs");
+ for (i = 0; i < MAX_WPS_VENDOR_EXTENSIONS; i++) {
+ if (reg->wps->dev.vendor_ext[i]) {
+ vendor_len += 2 + 2;
+ vendor_len += wpabuf_len(reg->wps->dev.vendor_ext[i]);
+ }
+ }
- beacon = wpabuf_alloc(300);
+ beacon = wpabuf_alloc(400 + vendor_len);
if (beacon == NULL)
return -1;
- probe = wpabuf_alloc(400);
+ probe = wpabuf_alloc(500 + vendor_len);
if (probe == NULL) {
wpabuf_free(beacon);
return -1;
}
+ auth_macs = wps_authorized_macs(reg, &count);
+
+ wpa_printf(MSG_DEBUG, "WPS: Build Beacon IEs");
+
if (wps_build_version(beacon) ||
wps_build_wps_state(reg->wps, beacon) ||
wps_build_ap_setup_locked(reg->wps, beacon) ||
wps_build_selected_registrar(reg, beacon) ||
wps_build_sel_reg_dev_password_id(reg, beacon) ||
wps_build_sel_reg_config_methods(reg, beacon) ||
- wps_build_version(probe) ||
+ wps_build_sel_pbc_reg_uuid_e(reg, beacon) ||
+ (reg->dualband && wps_build_rf_bands(&reg->wps->dev, beacon)) ||
+ wps_build_wfa_ext(beacon, 0, auth_macs, count) ||
+ wps_build_vendor_ext(&reg->wps->dev, beacon)) {
+ wpabuf_free(beacon);
+ wpabuf_free(probe);
+ return -1;
+ }
+
+#ifdef CONFIG_P2P
+ if (wps_build_dev_name(&reg->wps->dev, beacon) ||
+ wps_build_primary_dev_type(&reg->wps->dev, beacon)) {
+ wpabuf_free(beacon);
+ wpabuf_free(probe);
+ return -1;
+ }
+#endif /* CONFIG_P2P */
+
+ wpa_printf(MSG_DEBUG, "WPS: Build Probe Response IEs");
+
+ if (wps_build_version(probe) ||
wps_build_wps_state(reg->wps, probe) ||
wps_build_ap_setup_locked(reg->wps, probe) ||
wps_build_selected_registrar(reg, probe) ||
@@ -934,7 +1293,9 @@ static int wps_set_ie(struct wps_registrar *reg)
wps_build_uuid_e(probe, reg->wps->uuid) ||
wps_build_device_attrs(&reg->wps->dev, probe) ||
wps_build_probe_config_methods(reg, probe) ||
- wps_build_rf_bands(&reg->wps->dev, probe)) {
+ (reg->dualband && wps_build_rf_bands(&reg->wps->dev, probe)) ||
+ wps_build_wfa_ext(probe, 0, auth_macs, count) ||
+ wps_build_vendor_ext(&reg->wps->dev, probe)) {
wpabuf_free(beacon);
wpabuf_free(probe);
return -1;
@@ -987,6 +1348,13 @@ static int wps_get_dev_password(struct wps_data *wps)
wpa_printf(MSG_DEBUG, "WPS: Use default PIN for PBC");
pin = (const u8 *) "00000000";
pin_len = 8;
+#ifdef CONFIG_WPS_NFC
+ } else if (wps->nfc_pw_token) {
+ wpa_printf(MSG_DEBUG, "WPS: Use OOB Device Password from NFC "
+ "Password Token");
+ pin = wps->nfc_pw_token->dev_pw;
+ pin_len = wps->nfc_pw_token->dev_pw_len;
+#endif /* CONFIG_WPS_NFC */
} else {
pin = wps_registrar_get_pin(wps->wps->registrar, wps->uuid_e,
&pin_len);
@@ -1025,7 +1393,7 @@ static int wps_build_r_hash(struct wps_data *wps, struct wpabuf *msg)
const u8 *addr[4];
size_t len[4];
- if (os_get_random(wps->snonce, 2 * WPS_SECRET_NONCE_LEN) < 0)
+ if (random_get_bytes(wps->snonce, 2 * WPS_SECRET_NONCE_LEN) < 0)
return -1;
wpa_hexdump(MSG_DEBUG, "WPS: R-S1", wps->snonce, WPS_SECRET_NONCE_LEN);
wpa_hexdump(MSG_DEBUG, "WPS: R-S2",
@@ -1091,7 +1459,7 @@ static int wps_build_r_snonce2(struct wps_data *wps, struct wpabuf *msg)
static int wps_build_cred_network_idx(struct wpabuf *msg,
const struct wps_credential *cred)
{
- wpa_printf(MSG_DEBUG, "WPS: * Network Index");
+ wpa_printf(MSG_DEBUG, "WPS: * Network Index (1)");
wpabuf_put_be16(msg, ATTR_NETWORK_INDEX);
wpabuf_put_be16(msg, 1);
wpabuf_put_u8(msg, 1);
@@ -1103,6 +1471,8 @@ static int wps_build_cred_ssid(struct wpabuf *msg,
const struct wps_credential *cred)
{
wpa_printf(MSG_DEBUG, "WPS: * SSID");
+ wpa_hexdump_ascii(MSG_DEBUG, "WPS: SSID for Credential",
+ cred->ssid, cred->ssid_len);
wpabuf_put_be16(msg, ATTR_SSID);
wpabuf_put_be16(msg, cred->ssid_len);
wpabuf_put_data(msg, cred->ssid, cred->ssid_len);
@@ -1139,6 +1509,8 @@ static int wps_build_cred_network_key(struct wpabuf *msg,
{
wpa_printf(MSG_DEBUG, "WPS: * Network Key (len=%d)",
(int) cred->key_len);
+ wpa_hexdump_key(MSG_DEBUG, "WPS: Network Key",
+ cred->key, cred->key_len);
wpabuf_put_be16(msg, ATTR_NETWORK_KEY);
wpabuf_put_be16(msg, cred->key_len);
wpabuf_put_data(msg, cred->key, cred->key_len);
@@ -1172,6 +1544,25 @@ static int wps_build_credential(struct wpabuf *msg,
}
+int wps_build_credential_wrap(struct wpabuf *msg,
+ const struct wps_credential *cred)
+{
+ struct wpabuf *wbuf;
+ wbuf = wpabuf_alloc(200);
+ if (wbuf == NULL)
+ return -1;
+ if (wps_build_credential(wbuf, cred)) {
+ wpabuf_free(wbuf);
+ return -1;
+ }
+ wpabuf_put_be16(msg, ATTR_CRED);
+ wpabuf_put_be16(msg, wpabuf_len(wbuf));
+ wpabuf_put_buf(msg, wbuf);
+ wpabuf_free(wbuf);
+ return 0;
+}
+
+
int wps_build_cred(struct wps_data *wps, struct wpabuf *msg)
{
struct wpabuf *cred;
@@ -1237,7 +1628,7 @@ int wps_build_cred(struct wps_data *wps, struct wpabuf *msg)
!wps->wps->registrar->disable_auto_conf) {
u8 r[16];
/* Generate a random passphrase */
- if (os_get_random(r, sizeof(r)) < 0)
+ if (random_get_bytes(r, sizeof(r)) < 0)
return -1;
os_free(wps->new_psk);
wps->new_psk = base64_encode(r, sizeof(r), &wps->new_psk_len);
@@ -1269,7 +1660,7 @@ int wps_build_cred(struct wps_data *wps, struct wpabuf *msg)
wps->new_psk = os_malloc(wps->new_psk_len);
if (wps->new_psk == NULL)
return -1;
- if (os_get_random(wps->new_psk, wps->new_psk_len) < 0) {
+ if (random_get_bytes(wps->new_psk, wps->new_psk_len) < 0) {
os_free(wps->new_psk);
wps->new_psk = NULL;
return -1;
@@ -1283,6 +1674,33 @@ int wps_build_cred(struct wps_data *wps, struct wpabuf *msg)
}
use_provided:
+#ifdef CONFIG_WPS_TESTING
+ if (wps_testing_dummy_cred)
+ cred = wpabuf_alloc(200);
+ else
+ cred = NULL;
+ if (cred) {
+ struct wps_credential dummy;
+ wpa_printf(MSG_DEBUG, "WPS: Add dummy credential");
+ os_memset(&dummy, 0, sizeof(dummy));
+ os_memcpy(dummy.ssid, "dummy", 5);
+ dummy.ssid_len = 5;
+ dummy.auth_type = WPS_AUTH_WPA2PSK;
+ dummy.encr_type = WPS_ENCR_AES;
+ os_memcpy(dummy.key, "dummy psk", 9);
+ dummy.key_len = 9;
+ os_memcpy(dummy.mac_addr, wps->mac_addr_e, ETH_ALEN);
+ wps_build_credential(cred, &dummy);
+ wpa_hexdump_buf(MSG_DEBUG, "WPS: Dummy Credential", cred);
+
+ wpabuf_put_be16(msg, ATTR_CRED);
+ wpabuf_put_be16(msg, wpabuf_len(cred));
+ wpabuf_put_buf(msg, cred);
+
+ wpabuf_free(cred);
+ }
+#endif /* CONFIG_WPS_TESTING */
+
cred = wpabuf_alloc(200);
if (cred == NULL)
return -1;
@@ -1318,11 +1736,40 @@ static int wps_build_ap_settings(struct wps_data *wps, struct wpabuf *msg)
}
+static struct wpabuf * wps_build_ap_cred(struct wps_data *wps)
+{
+ struct wpabuf *msg, *plain;
+
+ msg = wpabuf_alloc(1000);
+ if (msg == NULL)
+ return NULL;
+
+ plain = wpabuf_alloc(200);
+ if (plain == NULL) {
+ wpabuf_free(msg);
+ return NULL;
+ }
+
+ if (wps_build_ap_settings(wps, plain)) {
+ wpabuf_free(plain);
+ wpabuf_free(msg);
+ return NULL;
+ }
+
+ wpabuf_put_be16(msg, ATTR_CRED);
+ wpabuf_put_be16(msg, wpabuf_len(plain));
+ wpabuf_put_buf(msg, plain);
+ wpabuf_free(plain);
+
+ return msg;
+}
+
+
static struct wpabuf * wps_build_m2(struct wps_data *wps)
{
struct wpabuf *msg;
- if (os_get_random(wps->nonce_r, WPS_NONCE_LEN) < 0)
+ if (random_get_bytes(wps->nonce_r, WPS_NONCE_LEN) < 0)
return NULL;
wpa_hexdump(MSG_DEBUG, "WPS: Registrar Nonce",
wps->nonce_r, WPS_NONCE_LEN);
@@ -1350,6 +1797,7 @@ static struct wpabuf * wps_build_m2(struct wps_data *wps)
wps_build_config_error(msg, WPS_CFG_NO_ERROR) ||
wps_build_dev_password_id(msg, wps->dev_pw_id) ||
wps_build_os_version(&wps->wps->dev, msg) ||
+ wps_build_wfa_ext(msg, 0, NULL, 0) ||
wps_build_authenticator(wps, msg)) {
wpabuf_free(msg);
return NULL;
@@ -1388,7 +1836,8 @@ static struct wpabuf * wps_build_m2d(struct wps_data *wps)
wps_build_rf_bands(&wps->wps->dev, msg) ||
wps_build_assoc_state(wps, msg) ||
wps_build_config_error(msg, err) ||
- wps_build_os_version(&wps->wps->dev, msg)) {
+ wps_build_os_version(&wps->wps->dev, msg) ||
+ wps_build_wfa_ext(msg, 0, NULL, 0)) {
wpabuf_free(msg);
return NULL;
}
@@ -1423,6 +1872,7 @@ static struct wpabuf * wps_build_m4(struct wps_data *wps)
wps_build_r_snonce1(wps, plain) ||
wps_build_key_wrap_auth(wps, plain) ||
wps_build_encr_settings(wps, msg, plain) ||
+ wps_build_wfa_ext(msg, 0, NULL, 0) ||
wps_build_authenticator(wps, msg)) {
wpabuf_free(plain);
wpabuf_free(msg);
@@ -1457,6 +1907,7 @@ static struct wpabuf * wps_build_m6(struct wps_data *wps)
wps_build_r_snonce2(wps, plain) ||
wps_build_key_wrap_auth(wps, plain) ||
wps_build_encr_settings(wps, msg, plain) ||
+ wps_build_wfa_ext(msg, 0, NULL, 0) ||
wps_build_authenticator(wps, msg)) {
wpabuf_free(plain);
wpabuf_free(msg);
@@ -1493,6 +1944,7 @@ static struct wpabuf * wps_build_m8(struct wps_data *wps)
(!wps->wps->ap && !wps->er && wps_build_ap_settings(wps, plain)) ||
wps_build_key_wrap_auth(wps, plain) ||
wps_build_encr_settings(wps, msg, plain) ||
+ wps_build_wfa_ext(msg, 0, NULL, 0) ||
wps_build_authenticator(wps, msg)) {
wpabuf_free(plain);
wpabuf_free(msg);
@@ -1505,51 +1957,6 @@ static struct wpabuf * wps_build_m8(struct wps_data *wps)
}
-static struct wpabuf * wps_build_wsc_ack(struct wps_data *wps)
-{
- struct wpabuf *msg;
-
- wpa_printf(MSG_DEBUG, "WPS: Building Message WSC_ACK");
-
- msg = wpabuf_alloc(1000);
- if (msg == NULL)
- return NULL;
-
- if (wps_build_version(msg) ||
- wps_build_msg_type(msg, WPS_WSC_ACK) ||
- wps_build_enrollee_nonce(wps, msg) ||
- wps_build_registrar_nonce(wps, msg)) {
- wpabuf_free(msg);
- return NULL;
- }
-
- return msg;
-}
-
-
-static struct wpabuf * wps_build_wsc_nack(struct wps_data *wps)
-{
- struct wpabuf *msg;
-
- wpa_printf(MSG_DEBUG, "WPS: Building Message WSC_NACK");
-
- msg = wpabuf_alloc(1000);
- if (msg == NULL)
- return NULL;
-
- if (wps_build_version(msg) ||
- wps_build_msg_type(msg, WPS_WSC_NACK) ||
- wps_build_enrollee_nonce(wps, msg) ||
- wps_build_registrar_nonce(wps, msg) ||
- wps_build_config_error(msg, wps->config_error)) {
- wpabuf_free(msg);
- return NULL;
- }
-
- return msg;
-}
-
-
struct wpabuf * wps_registrar_get_msg(struct wps_data *wps,
enum wsc_op_code *op_code)
{
@@ -1814,6 +2221,13 @@ static int wps_process_e_snonce2(struct wps_data *wps, const u8 *e_snonce2)
wps->wps_pin_revealed = 0;
wps_registrar_unlock_pin(wps->wps->registrar, wps->uuid_e);
+ /*
+ * In case wildcard PIN is used and WPS handshake succeeds in the first
+ * attempt, wps_registrar_unlock_pin() would not free the PIN, so make
+ * sure the PIN gets invalidated here.
+ */
+ wps_registrar_invalidate_pin(wps->wps->registrar, wps->uuid_e);
+
return 0;
}
@@ -1842,22 +2256,6 @@ static int wps_process_pubkey(struct wps_data *wps, const u8 *pk,
return -1;
}
-#ifdef CONFIG_WPS_OOB
- if (wps->wps->oob_conf.pubkey_hash != NULL) {
- const u8 *addr[1];
- u8 hash[WPS_HASH_LEN];
-
- addr[0] = pk;
- sha256_vector(1, addr, &pk_len, hash);
- if (os_memcmp(hash,
- wpabuf_head(wps->wps->oob_conf.pubkey_hash),
- WPS_OOB_PUBKEY_HASH_LEN) != 0) {
- wpa_printf(MSG_ERROR, "WPS: Public Key hash error");
- return -1;
- }
- }
-#endif /* CONFIG_WPS_OOB */
-
wpabuf_free(wps->dh_pubkey_e);
wps->dh_pubkey_e = wpabuf_alloc_copy(pk, pk_len);
if (wps->dh_pubkey_e == NULL)
@@ -2047,6 +2445,45 @@ static int wps_process_config_error(struct wps_data *wps, const u8 *err)
}
+static int wps_registrar_p2p_dev_addr_match(struct wps_data *wps)
+{
+#ifdef CONFIG_P2P
+ struct wps_registrar *reg = wps->wps->registrar;
+
+ if (is_zero_ether_addr(reg->p2p_dev_addr))
+ return 1; /* no filtering in use */
+
+ if (os_memcmp(reg->p2p_dev_addr, wps->p2p_dev_addr, ETH_ALEN) != 0) {
+ wpa_printf(MSG_DEBUG, "WPS: No match on P2P Device Address "
+ "filtering for PBC: expected " MACSTR " was "
+ MACSTR " - indicate PBC session overlap",
+ MAC2STR(reg->p2p_dev_addr),
+ MAC2STR(wps->p2p_dev_addr));
+ return 0;
+ }
+#endif /* CONFIG_P2P */
+ return 1;
+}
+
+
+static int wps_registrar_skip_overlap(struct wps_data *wps)
+{
+#ifdef CONFIG_P2P
+ struct wps_registrar *reg = wps->wps->registrar;
+
+ if (is_zero_ether_addr(reg->p2p_dev_addr))
+ return 0; /* no specific Enrollee selected */
+
+ if (os_memcmp(reg->p2p_dev_addr, wps->p2p_dev_addr, ETH_ALEN) == 0) {
+ wpa_printf(MSG_DEBUG, "WPS: Skip PBC overlap due to selected "
+ "Enrollee match");
+ return 1;
+ }
+#endif /* CONFIG_P2P */
+ return 0;
+}
+
+
static enum wps_process_res wps_process_m1(struct wps_data *wps,
struct wps_parse_attr *attr)
{
@@ -2088,25 +2525,46 @@ static enum wps_process_res wps_process_m1(struct wps_data *wps,
return WPS_CONTINUE;
}
-#ifdef CONFIG_WPS_OOB
- if (wps->dev_pw_id >= 0x10 &&
- wps->dev_pw_id != wps->wps->oob_dev_pw_id) {
- wpa_printf(MSG_DEBUG, "WPS: OOB Device Password ID "
- "%d mismatch", wps->dev_pw_id);
- wps->state = SEND_M2D;
- return WPS_CONTINUE;
+#ifdef CONFIG_WPS_NFC
+ if (wps->dev_pw_id >= 0x10) {
+ struct wps_nfc_pw_token *token;
+ const u8 *addr[1];
+ u8 hash[WPS_HASH_LEN];
+
+ token = wps_get_nfc_pw_token(
+ &wps->wps->registrar->nfc_pw_tokens, wps->dev_pw_id);
+ if (token) {
+ wpa_printf(MSG_DEBUG, "WPS: Found matching NFC "
+ "Password Token");
+ dl_list_del(&token->list);
+ wps->nfc_pw_token = token;
+
+ addr[0] = attr->public_key;
+ sha256_vector(1, addr, &attr->public_key_len, hash);
+ if (os_memcmp(hash, wps->nfc_pw_token->pubkey_hash,
+ WPS_OOB_PUBKEY_HASH_LEN) != 0) {
+ wpa_printf(MSG_ERROR, "WPS: Public Key hash "
+ "mismatch");
+ return WPS_FAILURE;
+ }
+ }
}
-#endif /* CONFIG_WPS_OOB */
+#endif /* CONFIG_WPS_NFC */
if (wps->dev_pw_id == DEV_PW_PUSHBUTTON) {
- if (wps->wps->registrar->force_pbc_overlap ||
- wps_registrar_pbc_overlap(wps->wps->registrar,
- wps->mac_addr_e, wps->uuid_e)) {
+ if ((wps->wps->registrar->force_pbc_overlap ||
+ wps_registrar_pbc_overlap(wps->wps->registrar,
+ wps->mac_addr_e, wps->uuid_e) ||
+ !wps_registrar_p2p_dev_addr_match(wps)) &&
+ !wps_registrar_skip_overlap(wps)) {
wpa_printf(MSG_DEBUG, "WPS: PBC overlap - deny PBC "
"negotiation");
wps->state = SEND_M2D;
wps->config_error = WPS_CFG_MULTIPLE_PBC_DETECTED;
wps_pbc_overlap_event(wps->wps);
+ wps_fail_event(wps->wps, WPS_M1,
+ WPS_CFG_MULTIPLE_PBC_DETECTED,
+ WPS_EI_NO_ERROR);
wps->wps->registrar->force_pbc_overlap = 1;
return WPS_CONTINUE;
}
@@ -2150,7 +2608,8 @@ static enum wps_process_res wps_process_m3(struct wps_data *wps,
return WPS_CONTINUE;
}
- if (wps->pbc && wps->wps->registrar->force_pbc_overlap) {
+ if (wps->pbc && wps->wps->registrar->force_pbc_overlap &&
+ !wps_registrar_skip_overlap(wps)) {
wpa_printf(MSG_DEBUG, "WPS: Reject negotiation due to PBC "
"session overlap");
wps->state = SEND_WSC_NACK;
@@ -2187,7 +2646,8 @@ static enum wps_process_res wps_process_m5(struct wps_data *wps,
return WPS_CONTINUE;
}
- if (wps->pbc && wps->wps->registrar->force_pbc_overlap) {
+ if (wps->pbc && wps->wps->registrar->force_pbc_overlap &&
+ !wps_registrar_skip_overlap(wps)) {
wpa_printf(MSG_DEBUG, "WPS: Reject negotiation due to PBC "
"session overlap");
wps->state = SEND_WSC_NACK;
@@ -2210,6 +2670,12 @@ static enum wps_process_res wps_process_m5(struct wps_data *wps,
return WPS_CONTINUE;
}
+ if (wps_validate_m5_encr(decrypted, attr->version2 != NULL) < 0) {
+ wpabuf_free(decrypted);
+ wps->state = SEND_WSC_NACK;
+ return WPS_CONTINUE;
+ }
+
wpa_printf(MSG_DEBUG, "WPS: Processing decrypted Encrypted Settings "
"attribute");
if (wps_parse_msg(decrypted, &eattr) < 0 ||
@@ -2264,6 +2730,8 @@ static void wps_cred_update(struct wps_credential *dst,
static int wps_process_ap_settings_r(struct wps_data *wps,
struct wps_parse_attr *attr)
{
+ struct wpabuf *msg;
+
if (wps->wps->ap || wps->er)
return 0;
@@ -2283,12 +2751,31 @@ static int wps_process_ap_settings_r(struct wps_data *wps,
* Use the AP PIN only to receive the current AP settings, not
* to reconfigure the AP.
*/
+
+ /*
+ * Clear selected registrar here since we do not get to
+ * WSC_Done in this protocol run.
+ */
+ wps_registrar_pin_completed(wps->wps->registrar);
+
+ msg = wps_build_ap_cred(wps);
+ if (msg == NULL)
+ return -1;
+ wps->cred.cred_attr = wpabuf_head(msg);
+ wps->cred.cred_attr_len = wpabuf_len(msg);
+
if (wps->ap_settings_cb) {
wps->ap_settings_cb(wps->ap_settings_cb_ctx,
&wps->cred);
+ wpabuf_free(msg);
return 1;
}
wps_sta_cred_cb(wps);
+
+ wps->cred.cred_attr = NULL;
+ wps->cred.cred_attr_len = 0;
+ wpabuf_free(msg);
+
return 1;
}
}
@@ -2310,7 +2797,8 @@ static enum wps_process_res wps_process_m7(struct wps_data *wps,
return WPS_CONTINUE;
}
- if (wps->pbc && wps->wps->registrar->force_pbc_overlap) {
+ if (wps->pbc && wps->wps->registrar->force_pbc_overlap &&
+ !wps_registrar_skip_overlap(wps)) {
wpa_printf(MSG_DEBUG, "WPS: Reject negotiation due to PBC "
"session overlap");
wps->state = SEND_WSC_NACK;
@@ -2333,6 +2821,13 @@ static enum wps_process_res wps_process_m7(struct wps_data *wps,
return WPS_CONTINUE;
}
+ if (wps_validate_m7_encr(decrypted, wps->wps->ap || wps->er,
+ attr->version2 != NULL) < 0) {
+ wpabuf_free(decrypted);
+ wps->state = SEND_WSC_NACK;
+ return WPS_CONTINUE;
+ }
+
wpa_printf(MSG_DEBUG, "WPS: Processing decrypted Encrypted Settings "
"attribute");
if (wps_parse_msg(decrypted, &eattr) < 0 ||
@@ -2362,27 +2857,24 @@ static enum wps_process_res wps_process_wsc_msg(struct wps_data *wps,
if (wps_parse_msg(msg, &attr) < 0)
return WPS_FAILURE;
- if (!wps_version_supported(attr.version)) {
- wpa_printf(MSG_DEBUG, "WPS: Unsupported message version 0x%x",
- attr.version ? *attr.version : 0);
- return WPS_FAILURE;
- }
-
if (attr.msg_type == NULL) {
wpa_printf(MSG_DEBUG, "WPS: No Message Type attribute");
- return WPS_FAILURE;
+ wps->state = SEND_WSC_NACK;
+ return WPS_CONTINUE;
}
if (*attr.msg_type != WPS_M1 &&
(attr.registrar_nonce == NULL ||
os_memcmp(wps->nonce_r, attr.registrar_nonce,
- WPS_NONCE_LEN != 0))) {
+ WPS_NONCE_LEN) != 0)) {
wpa_printf(MSG_DEBUG, "WPS: Mismatch in registrar nonce");
return WPS_FAILURE;
}
switch (*attr.msg_type) {
case WPS_M1:
+ if (wps_validate_m1(msg) < 0)
+ return WPS_FAILURE;
#ifdef CONFIG_WPS_UPNP
if (wps->wps->wps_upnp && attr.mac_addr) {
/* Remove old pending messages when starting new run */
@@ -2397,19 +2889,28 @@ static enum wps_process_res wps_process_wsc_msg(struct wps_data *wps,
ret = wps_process_m1(wps, &attr);
break;
case WPS_M3:
+ if (wps_validate_m3(msg) < 0)
+ return WPS_FAILURE;
ret = wps_process_m3(wps, msg, &attr);
if (ret == WPS_FAILURE || wps->state == SEND_WSC_NACK)
- wps_fail_event(wps->wps, WPS_M3);
+ wps_fail_event(wps->wps, WPS_M3, wps->config_error,
+ wps->error_indication);
break;
case WPS_M5:
+ if (wps_validate_m5(msg) < 0)
+ return WPS_FAILURE;
ret = wps_process_m5(wps, msg, &attr);
if (ret == WPS_FAILURE || wps->state == SEND_WSC_NACK)
- wps_fail_event(wps->wps, WPS_M5);
+ wps_fail_event(wps->wps, WPS_M5, wps->config_error,
+ wps->error_indication);
break;
case WPS_M7:
+ if (wps_validate_m7(msg) < 0)
+ return WPS_FAILURE;
ret = wps_process_m7(wps, msg, &attr);
if (ret == WPS_FAILURE || wps->state == SEND_WSC_NACK)
- wps_fail_event(wps->wps, WPS_M7);
+ wps_fail_event(wps->wps, WPS_M7, wps->config_error,
+ wps->error_indication);
break;
default:
wpa_printf(MSG_DEBUG, "WPS: Unsupported Message Type %d",
@@ -2438,12 +2939,6 @@ static enum wps_process_res wps_process_wsc_ack(struct wps_data *wps,
if (wps_parse_msg(msg, &attr) < 0)
return WPS_FAILURE;
- if (!wps_version_supported(attr.version)) {
- wpa_printf(MSG_DEBUG, "WPS: Unsupported message version 0x%x",
- attr.version ? *attr.version : 0);
- return WPS_FAILURE;
- }
-
if (attr.msg_type == NULL) {
wpa_printf(MSG_DEBUG, "WPS: No Message Type attribute");
return WPS_FAILURE;
@@ -2467,14 +2962,14 @@ static enum wps_process_res wps_process_wsc_ack(struct wps_data *wps,
#endif /* CONFIG_WPS_UPNP */
if (attr.registrar_nonce == NULL ||
- os_memcmp(wps->nonce_r, attr.registrar_nonce, WPS_NONCE_LEN != 0))
+ os_memcmp(wps->nonce_r, attr.registrar_nonce, WPS_NONCE_LEN) != 0)
{
wpa_printf(MSG_DEBUG, "WPS: Mismatch in registrar nonce");
return WPS_FAILURE;
}
if (attr.enrollee_nonce == NULL ||
- os_memcmp(wps->nonce_e, attr.enrollee_nonce, WPS_NONCE_LEN != 0)) {
+ os_memcmp(wps->nonce_e, attr.enrollee_nonce, WPS_NONCE_LEN) != 0) {
wpa_printf(MSG_DEBUG, "WPS: Mismatch in enrollee nonce");
return WPS_FAILURE;
}
@@ -2506,6 +3001,7 @@ static enum wps_process_res wps_process_wsc_nack(struct wps_data *wps,
{
struct wps_parse_attr attr;
int old_state;
+ u16 config_error;
wpa_printf(MSG_DEBUG, "WPS: Received WSC_NACK");
@@ -2515,12 +3011,6 @@ static enum wps_process_res wps_process_wsc_nack(struct wps_data *wps,
if (wps_parse_msg(msg, &attr) < 0)
return WPS_FAILURE;
- if (!wps_version_supported(attr.version)) {
- wpa_printf(MSG_DEBUG, "WPS: Unsupported message version 0x%x",
- attr.version ? *attr.version : 0);
- return WPS_FAILURE;
- }
-
if (attr.msg_type == NULL) {
wpa_printf(MSG_DEBUG, "WPS: No Message Type attribute");
return WPS_FAILURE;
@@ -2541,14 +3031,14 @@ static enum wps_process_res wps_process_wsc_nack(struct wps_data *wps,
#endif /* CONFIG_WPS_UPNP */
if (attr.registrar_nonce == NULL ||
- os_memcmp(wps->nonce_r, attr.registrar_nonce, WPS_NONCE_LEN != 0))
+ os_memcmp(wps->nonce_r, attr.registrar_nonce, WPS_NONCE_LEN) != 0)
{
wpa_printf(MSG_DEBUG, "WPS: Mismatch in registrar nonce");
return WPS_FAILURE;
}
if (attr.enrollee_nonce == NULL ||
- os_memcmp(wps->nonce_e, attr.enrollee_nonce, WPS_NONCE_LEN != 0)) {
+ os_memcmp(wps->nonce_e, attr.enrollee_nonce, WPS_NONCE_LEN) != 0) {
wpa_printf(MSG_DEBUG, "WPS: Mismatch in enrollee nonce");
return WPS_FAILURE;
}
@@ -2559,21 +3049,26 @@ static enum wps_process_res wps_process_wsc_nack(struct wps_data *wps,
return WPS_FAILURE;
}
+ config_error = WPA_GET_BE16(attr.config_error);
wpa_printf(MSG_DEBUG, "WPS: Enrollee terminated negotiation with "
- "Configuration Error %d", WPA_GET_BE16(attr.config_error));
+ "Configuration Error %d", config_error);
switch (old_state) {
case RECV_M3:
- wps_fail_event(wps->wps, WPS_M2);
+ wps_fail_event(wps->wps, WPS_M2, config_error,
+ wps->error_indication);
break;
case RECV_M5:
- wps_fail_event(wps->wps, WPS_M4);
+ wps_fail_event(wps->wps, WPS_M4, config_error,
+ wps->error_indication);
break;
case RECV_M7:
- wps_fail_event(wps->wps, WPS_M6);
+ wps_fail_event(wps->wps, WPS_M6, config_error,
+ wps->error_indication);
break;
case RECV_DONE:
- wps_fail_event(wps->wps, WPS_M8);
+ wps_fail_event(wps->wps, WPS_M8, config_error,
+ wps->error_indication);
break;
default:
break;
@@ -2600,12 +3095,6 @@ static enum wps_process_res wps_process_wsc_done(struct wps_data *wps,
if (wps_parse_msg(msg, &attr) < 0)
return WPS_FAILURE;
- if (!wps_version_supported(attr.version)) {
- wpa_printf(MSG_DEBUG, "WPS: Unsupported message version 0x%x",
- attr.version ? *attr.version : 0);
- return WPS_FAILURE;
- }
-
if (attr.msg_type == NULL) {
wpa_printf(MSG_DEBUG, "WPS: No Message Type attribute");
return WPS_FAILURE;
@@ -2628,14 +3117,14 @@ static enum wps_process_res wps_process_wsc_done(struct wps_data *wps,
#endif /* CONFIG_WPS_UPNP */
if (attr.registrar_nonce == NULL ||
- os_memcmp(wps->nonce_r, attr.registrar_nonce, WPS_NONCE_LEN != 0))
+ os_memcmp(wps->nonce_r, attr.registrar_nonce, WPS_NONCE_LEN) != 0)
{
wpa_printf(MSG_DEBUG, "WPS: Mismatch in registrar nonce");
return WPS_FAILURE;
}
if (attr.enrollee_nonce == NULL ||
- os_memcmp(wps->nonce_e, attr.enrollee_nonce, WPS_NONCE_LEN != 0)) {
+ os_memcmp(wps->nonce_e, attr.enrollee_nonce, WPS_NONCE_LEN) != 0) {
wpa_printf(MSG_DEBUG, "WPS: Mismatch in enrollee nonce");
return WPS_FAILURE;
}
@@ -2683,15 +3172,22 @@ static enum wps_process_res wps_process_wsc_done(struct wps_data *wps,
wps->new_psk = NULL;
}
- wps_cb_reg_success(wps->wps->registrar, wps->mac_addr_e, wps->uuid_e);
+ wps_cb_reg_success(wps->wps->registrar, wps->mac_addr_e, wps->uuid_e,
+ wps->dev_password, wps->dev_password_len);
if (wps->pbc) {
wps_registrar_remove_pbc_session(wps->wps->registrar,
- wps->mac_addr_e, wps->uuid_e);
+ wps->uuid_e,
+ wps->p2p_dev_addr);
wps_registrar_pbc_completed(wps->wps->registrar);
+ os_get_time(&wps->wps->registrar->pbc_ignore_start);
+ os_memcpy(wps->wps->registrar->pbc_ignore_uuid, wps->uuid_e,
+ WPS_UUID_LEN);
} else {
wps_registrar_pin_completed(wps->wps->registrar);
}
+ /* TODO: maintain AuthorizedMACs somewhere separately for each ER and
+ * merge them into APs own list.. */
wps_success_event(wps->wps);
@@ -2747,14 +3243,22 @@ enum wps_process_res wps_registrar_process_msg(struct wps_data *wps,
case WSC_MSG:
return wps_process_wsc_msg(wps, msg);
case WSC_ACK:
+ if (wps_validate_wsc_ack(msg) < 0)
+ return WPS_FAILURE;
return wps_process_wsc_ack(wps, msg);
case WSC_NACK:
+ if (wps_validate_wsc_nack(msg) < 0)
+ return WPS_FAILURE;
return wps_process_wsc_nack(wps, msg);
case WSC_Done:
+ if (wps_validate_wsc_done(msg) < 0)
+ return WPS_FAILURE;
ret = wps_process_wsc_done(wps, msg);
if (ret == WPS_FAILURE) {
wps->state = SEND_WSC_NACK;
- wps_fail_event(wps->wps, WPS_WSC_DONE);
+ wps_fail_event(wps->wps, WPS_WSC_DONE,
+ wps->config_error,
+ wps->error_indication);
}
return ret;
default:
@@ -2787,6 +3291,7 @@ static void wps_registrar_set_selected_timeout(void *eloop_ctx,
static void wps_registrar_sel_reg_add(struct wps_registrar *reg,
struct subscription *s)
{
+ int i, j;
wpa_printf(MSG_DEBUG, "WPS: External Registrar selected (dev_pw_id=%d "
"config_methods=0x%x)",
s->dev_password_id, s->config_methods);
@@ -2796,6 +3301,22 @@ static void wps_registrar_sel_reg_add(struct wps_registrar *reg,
if (reg->sel_reg_config_methods_override == -1)
reg->sel_reg_config_methods_override = 0;
reg->sel_reg_config_methods_override |= s->config_methods;
+ for (i = 0; i < WPS_MAX_AUTHORIZED_MACS; i++)
+ if (is_zero_ether_addr(reg->authorized_macs_union[i]))
+ break;
+ for (j = 0; i < WPS_MAX_AUTHORIZED_MACS && j < WPS_MAX_AUTHORIZED_MACS;
+ j++) {
+ if (is_zero_ether_addr(s->authorized_macs[j]))
+ break;
+ wpa_printf(MSG_DEBUG, "WPS: Add authorized MAC into union: "
+ MACSTR, MAC2STR(s->authorized_macs[j]));
+ os_memcpy(reg->authorized_macs_union[i],
+ s->authorized_macs[j], ETH_ALEN);
+ i++;
+ }
+ wpa_hexdump(MSG_DEBUG, "WPS: Authorized MACs union",
+ (u8 *) reg->authorized_macs_union,
+ sizeof(reg->authorized_macs_union));
}
#endif /* CONFIG_WPS_UPNP */
@@ -2841,17 +3362,27 @@ void wps_registrar_selected_registrar_changed(struct wps_registrar *reg)
reg->sel_reg_union = reg->selected_registrar;
reg->sel_reg_dev_password_id_override = -1;
reg->sel_reg_config_methods_override = -1;
+ os_memcpy(reg->authorized_macs_union, reg->authorized_macs,
+ WPS_MAX_AUTHORIZED_MACS * ETH_ALEN);
+ wpa_hexdump(MSG_DEBUG, "WPS: Authorized MACs union (start with own)",
+ (u8 *) reg->authorized_macs_union,
+ sizeof(reg->authorized_macs_union));
if (reg->selected_registrar) {
- reg->sel_reg_config_methods_override =
- reg->wps->config_methods & ~WPS_CONFIG_PUSHBUTTON;
+ u16 methods;
+
+ methods = reg->wps->config_methods & ~WPS_CONFIG_PUSHBUTTON;
+#ifdef CONFIG_WPS2
+ methods &= ~(WPS_CONFIG_VIRT_PUSHBUTTON |
+ WPS_CONFIG_PHY_PUSHBUTTON);
+#endif /* CONFIG_WPS2 */
if (reg->pbc) {
reg->sel_reg_dev_password_id_override =
DEV_PW_PUSHBUTTON;
- reg->sel_reg_config_methods_override |=
- WPS_CONFIG_PUSHBUTTON;
+ wps_set_pushbutton(&methods, reg->wps->config_methods);
}
wpa_printf(MSG_DEBUG, "WPS: Internal Registrar selected "
"(pbc=%d)", reg->pbc);
+ reg->sel_reg_config_methods_override = methods;
} else
wpa_printf(MSG_DEBUG, "WPS: Internal Registrar not selected");
@@ -2898,3 +3429,124 @@ int wps_registrar_get_info(struct wps_registrar *reg, const u8 *addr,
return len;
}
+
+
+int wps_registrar_config_ap(struct wps_registrar *reg,
+ struct wps_credential *cred)
+{
+#ifdef CONFIG_WPS2
+ printf("encr_type=0x%x\n", cred->encr_type);
+ if (!(cred->encr_type & (WPS_ENCR_NONE | WPS_ENCR_TKIP |
+ WPS_ENCR_AES))) {
+ if (cred->encr_type & WPS_ENCR_WEP) {
+ wpa_printf(MSG_INFO, "WPS: Reject new AP settings "
+ "due to WEP configuration");
+ return -1;
+ }
+
+ wpa_printf(MSG_INFO, "WPS: Reject new AP settings due to "
+ "invalid encr_type 0x%x", cred->encr_type);
+ return -1;
+ }
+
+ if ((cred->encr_type & (WPS_ENCR_TKIP | WPS_ENCR_AES)) ==
+ WPS_ENCR_TKIP) {
+ wpa_printf(MSG_DEBUG, "WPS: Upgrade encr_type TKIP -> "
+ "TKIP+AES");
+ cred->encr_type |= WPS_ENCR_AES;
+ }
+
+ if ((cred->auth_type & (WPS_AUTH_WPAPSK | WPS_AUTH_WPA2PSK)) ==
+ WPS_AUTH_WPAPSK) {
+ wpa_printf(MSG_DEBUG, "WPS: Upgrade auth_type WPAPSK -> "
+ "WPAPSK+WPA2PSK");
+ cred->auth_type |= WPS_AUTH_WPA2PSK;
+ }
+#endif /* CONFIG_WPS2 */
+
+ if (reg->wps->cred_cb)
+ return reg->wps->cred_cb(reg->wps->cb_ctx, cred);
+
+ return -1;
+}
+
+
+#ifdef CONFIG_WPS_NFC
+
+int wps_registrar_add_nfc_pw_token(struct wps_registrar *reg,
+ const u8 *pubkey_hash, u16 pw_id,
+ const u8 *dev_pw, size_t dev_pw_len)
+{
+ struct wps_nfc_pw_token *token;
+
+ if (dev_pw_len > WPS_OOB_DEVICE_PASSWORD_LEN)
+ return -1;
+
+ wps_free_nfc_pw_tokens(&reg->nfc_pw_tokens, pw_id);
+
+ token = os_zalloc(sizeof(*token));
+ if (token == NULL)
+ return -1;
+
+ os_memcpy(token->pubkey_hash, pubkey_hash, WPS_OOB_PUBKEY_HASH_LEN);
+ token->pw_id = pw_id;
+ os_memcpy(token->dev_pw, dev_pw, dev_pw_len);
+ token->dev_pw_len = dev_pw_len;
+
+ dl_list_add(&reg->nfc_pw_tokens, &token->list);
+
+ reg->selected_registrar = 1;
+ reg->pbc = 0;
+ wps_registrar_add_authorized_mac(reg,
+ (u8 *) "\xff\xff\xff\xff\xff\xff");
+ wps_registrar_selected_registrar_changed(reg);
+ eloop_cancel_timeout(wps_registrar_set_selected_timeout, reg, NULL);
+ eloop_register_timeout(WPS_PBC_WALK_TIME, 0,
+ wps_registrar_set_selected_timeout,
+ reg, NULL);
+
+ return 0;
+}
+
+
+int wps_registrar_add_nfc_password_token(struct wps_registrar *reg,
+ const u8 *oob_dev_pw,
+ size_t oob_dev_pw_len)
+{
+ const u8 *pos, *hash, *dev_pw;
+ u16 id;
+ size_t dev_pw_len;
+
+ if (oob_dev_pw_len < WPS_OOB_PUBKEY_HASH_LEN + 2 +
+ WPS_OOB_DEVICE_PASSWORD_MIN_LEN ||
+ oob_dev_pw_len > WPS_OOB_PUBKEY_HASH_LEN + 2 +
+ WPS_OOB_DEVICE_PASSWORD_LEN)
+ return -1;
+
+ hash = oob_dev_pw;
+ pos = oob_dev_pw + WPS_OOB_PUBKEY_HASH_LEN;
+ id = WPA_GET_BE16(pos);
+ dev_pw = pos + 2;
+ dev_pw_len = oob_dev_pw + oob_dev_pw_len - dev_pw;
+
+ wpa_printf(MSG_DEBUG, "WPS: Add NFC Password Token for Password ID %u",
+ id);
+
+ wpa_hexdump(MSG_DEBUG, "WPS: Public Key Hash",
+ hash, WPS_OOB_PUBKEY_HASH_LEN);
+ wpa_hexdump_key(MSG_DEBUG, "WPS: Device Password", dev_pw, dev_pw_len);
+
+ return wps_registrar_add_nfc_pw_token(reg, hash, id, dev_pw,
+ dev_pw_len);
+}
+
+
+void wps_registrar_remove_nfc_pw_token(struct wps_registrar *reg,
+ struct wps_nfc_pw_token *token)
+{
+ wps_registrar_remove_authorized_mac(reg,
+ (u8 *) "\xff\xff\xff\xff\xff\xff");
+ wps_registrar_selected_registrar_changed(reg);
+}
+
+#endif /* CONFIG_WPS_NFC */
diff --git a/contrib/wpa/src/wps/wps_ufd.c b/contrib/wpa/src/wps/wps_ufd.c
deleted file mode 100644
index 1a911e1..0000000
--- a/contrib/wpa/src/wps/wps_ufd.c
+++ /dev/null
@@ -1,235 +0,0 @@
-/*
- * UFD routines for Wi-Fi Protected Setup
- * Copyright (c) 2009, Masashi Honma <honma@ictec.co.jp>
- *
- * This program is free software; you can redistribute it and/or modify
- * it under the terms of the GNU General Public License version 2 as
- * published by the Free Software Foundation.
- *
- * Alternatively, this software may be distributed under the terms of BSD
- * license.
- *
- * See README and COPYING for more details.
- */
-
-#include "includes.h"
-#include "common.h"
-#include <sys/types.h>
-#include <sys/stat.h>
-#include <sys/wait.h>
-#include <fcntl.h>
-#include <dirent.h>
-
-#include "wps/wps.h"
-#include "wps/wps_i.h"
-
-#ifdef CONFIG_NATIVE_WINDOWS
-#define UFD_DIR1 "%s\\SMRTNTKY"
-#define UFD_DIR2 UFD_DIR1 "\\WFAWSC"
-#define UFD_FILE UFD_DIR2 "\\%s"
-#else /* CONFIG_NATIVE_WINDOWS */
-#define UFD_DIR1 "%s/SMRTNTKY"
-#define UFD_DIR2 UFD_DIR1 "/WFAWSC"
-#define UFD_FILE UFD_DIR2 "/%s"
-#endif /* CONFIG_NATIVE_WINDOWS */
-
-
-struct wps_ufd_data {
- int ufd_fd;
-};
-
-
-static int dev_pwd_e_file_filter(const struct dirent *entry)
-{
- unsigned int prefix;
- char ext[5];
-
- if (sscanf(entry->d_name, "%8x.%4s", &prefix, ext) != 2)
- return 0;
- if (prefix == 0)
- return 0;
- if (os_strcasecmp(ext, "WFA") != 0)
- return 0;
-
- return 1;
-}
-
-
-static int wps_get_dev_pwd_e_file_name(char *path, char *file_name)
-{
- struct dirent **namelist;
- int i, file_num;
-
- file_num = scandir(path, &namelist, &dev_pwd_e_file_filter,
- alphasort);
- if (file_num < 0) {
- wpa_printf(MSG_ERROR, "WPS: OOB file not found: %d (%s)",
- errno, strerror(errno));
- return -1;
- }
- if (file_num == 0) {
- wpa_printf(MSG_ERROR, "WPS: OOB file not found");
- os_free(namelist);
- return -1;
- }
- os_strlcpy(file_name, namelist[0]->d_name, 13);
- for (i = 0; i < file_num; i++)
- os_free(namelist[i]);
- os_free(namelist);
- return 0;
-}
-
-
-static int get_file_name(struct wps_context *wps, int registrar,
- const char *path, char *file_name)
-{
- switch (wps->oob_conf.oob_method) {
- case OOB_METHOD_CRED:
- os_snprintf(file_name, 13, "00000000.WSC");
- break;
- case OOB_METHOD_DEV_PWD_E:
- if (registrar) {
- char temp[128];
- os_snprintf(temp, sizeof(temp), UFD_DIR2, path);
- if (wps_get_dev_pwd_e_file_name(temp, file_name) < 0)
- return -1;
- } else {
- u8 *mac_addr = wps->dev.mac_addr;
-
- os_snprintf(file_name, 13, "%02X%02X%02X%02X.WFA",
- mac_addr[2], mac_addr[3], mac_addr[4],
- mac_addr[5]);
- }
- break;
- case OOB_METHOD_DEV_PWD_R:
- os_snprintf(file_name, 13, "00000000.WFA");
- break;
- default:
- wpa_printf(MSG_ERROR, "WPS: Invalid USBA OOB method");
- return -1;
- }
- return 0;
-}
-
-
-static int ufd_mkdir(const char *path)
-{
- if (mkdir(path, S_IRWXU) < 0 && errno != EEXIST) {
- wpa_printf(MSG_ERROR, "WPS (UFD): Failed to create directory "
- "'%s': %d (%s)", path, errno, strerror(errno));
- return -1;
- }
- return 0;
-}
-
-
-static void * init_ufd(struct wps_context *wps,
- struct oob_device_data *oob_dev, int registrar)
-{
- int write_f;
- char temp[128];
- char *path = oob_dev->device_path;
- char filename[13];
- struct wps_ufd_data *data;
- int ufd_fd;
-
- if (path == NULL)
- return NULL;
-
- write_f = wps->oob_conf.oob_method == OOB_METHOD_DEV_PWD_E ?
- !registrar : registrar;
-
- if (get_file_name(wps, registrar, path, filename) < 0) {
- wpa_printf(MSG_ERROR, "WPS (UFD): Failed to get file name");
- return NULL;
- }
-
- if (write_f) {
- os_snprintf(temp, sizeof(temp), UFD_DIR1, path);
- if (ufd_mkdir(temp))
- return NULL;
- os_snprintf(temp, sizeof(temp), UFD_DIR2, path);
- if (ufd_mkdir(temp))
- return NULL;
- }
-
- os_snprintf(temp, sizeof(temp), UFD_FILE, path, filename);
- if (write_f)
- ufd_fd = open(temp, O_WRONLY | O_CREAT | O_TRUNC,
- S_IRUSR | S_IWUSR);
- else
- ufd_fd = open(temp, O_RDONLY);
- if (ufd_fd < 0) {
- wpa_printf(MSG_ERROR, "WPS (UFD): Failed to open %s: %s",
- temp, strerror(errno));
- return NULL;
- }
-
- data = os_zalloc(sizeof(*data));
- if (data == NULL)
- return NULL;
- data->ufd_fd = ufd_fd;
- return data;
-}
-
-
-static struct wpabuf * read_ufd(void *priv)
-{
- struct wps_ufd_data *data = priv;
- struct wpabuf *buf;
- struct stat s;
- size_t file_size;
-
- if (fstat(data->ufd_fd, &s) < 0) {
- wpa_printf(MSG_ERROR, "WPS (UFD): Failed to get file size");
- return NULL;
- }
-
- file_size = s.st_size;
- buf = wpabuf_alloc(file_size);
- if (buf == NULL) {
- wpa_printf(MSG_ERROR, "WPS (UFD): Failed to alloc read "
- "buffer");
- return NULL;
- }
-
- if (read(data->ufd_fd, wpabuf_mhead(buf), file_size) !=
- (int) file_size) {
- wpabuf_free(buf);
- wpa_printf(MSG_ERROR, "WPS (UFD): Failed to read");
- return NULL;
- }
- wpabuf_put(buf, file_size);
- return buf;
-}
-
-
-static int write_ufd(void *priv, struct wpabuf *buf)
-{
- struct wps_ufd_data *data = priv;
-
- if (write(data->ufd_fd, wpabuf_mhead(buf), wpabuf_len(buf)) !=
- (int) wpabuf_len(buf)) {
- wpa_printf(MSG_ERROR, "WPS (UFD): Failed to write");
- return -1;
- }
- return 0;
-}
-
-
-static void deinit_ufd(void *priv)
-{
- struct wps_ufd_data *data = priv;
- close(data->ufd_fd);
- os_free(data);
-}
-
-
-struct oob_device_data oob_ufd_device_data = {
- .device_name = NULL,
- .device_path = NULL,
- .init_func = init_ufd,
- .read_func = read_ufd,
- .write_func = write_ufd,
- .deinit_func = deinit_ufd,
-};
diff --git a/contrib/wpa/src/wps/wps_upnp.c b/contrib/wpa/src/wps/wps_upnp.c
index f99b859..09a46a2 100644
--- a/contrib/wpa/src/wps/wps_upnp.c
+++ b/contrib/wpa/src/wps/wps_upnp.c
@@ -3,7 +3,7 @@
* Copyright (c) 2000-2003 Intel Corporation
* Copyright (c) 2006-2007 Sony Corporation
* Copyright (c) 2008-2009 Atheros Communications
- * Copyright (c) 2009, Jouni Malinen <j@w1.fi>
+ * Copyright (c) 2009-2010, Jouni Malinen <j@w1.fi>
*
* See below for more details on licensing and code history.
*/
@@ -47,7 +47,7 @@
* -- Needs renaming with module prefix to avoid polluting the debugger
* namespace and causing possible collisions with other static fncs
* and structure declarations when using the debugger.
- * -- The http error code generation is pretty bogus, hopefully noone cares.
+ * -- The http error code generation is pretty bogus, hopefully no one cares.
*
* Author: Ted Merrill, Atheros Communications, based upon earlier work
* as explained above and below.
@@ -172,7 +172,7 @@
#include "includes.h"
-#include <assert.h>
+#include <time.h>
#include <net/if.h>
#include <netdb.h>
#include <sys/ioctl.h>
@@ -209,6 +209,12 @@
#define MAX_SUBSCRIPTIONS 4 /* how many subscribing clients we handle */
#define MAX_ADDR_PER_SUBSCRIPTION 8
+/* Maximum number of Probe Request events per second */
+#define MAX_EVENTS_PER_SEC 5
+
+
+static struct upnp_wps_device_sm *shared_upnp_device = NULL;
+
/* Write the current date/time per RFC */
void format_date(struct wpabuf *buf)
@@ -270,7 +276,7 @@ static void uuid_make(u8 uuid[UUID_LEN])
/* subscr_addr_delete -- delete single unlinked subscriber address
* (be sure to unlink first if need be)
*/
-static void subscr_addr_delete(struct subscr_addr *a)
+void subscr_addr_delete(struct subscr_addr *a)
{
/*
* Note: do NOT free domain_and_port or path because they point to
@@ -293,50 +299,46 @@ static void subscr_addr_free_all(struct subscription *s)
/* subscr_addr_add_url -- add address(es) for one url to subscription */
-static void subscr_addr_add_url(struct subscription *s, const char *url)
+static void subscr_addr_add_url(struct subscription *s, const char *url,
+ size_t url_len)
{
int alloc_len;
char *scratch_mem = NULL;
char *mem;
- char *domain_and_port;
+ char *host;
char *delim;
char *path;
- char *domain;
int port = 80; /* port to send to (default is port 80) */
struct addrinfo hints;
struct addrinfo *result = NULL;
struct addrinfo *rp;
int rerr;
- struct subscr_addr *a = NULL;
+ size_t host_len, path_len;
/* url MUST begin with http: */
- if (os_strncasecmp(url, "http://", 7))
+ if (url_len < 7 || os_strncasecmp(url, "http://", 7))
goto fail;
url += 7;
+ url_len -= 7;
- /* allocate memory for the extra stuff we need */
- alloc_len = (2 * (os_strlen(url) + 1));
- scratch_mem = os_zalloc(alloc_len);
+ /* Make a copy of the string to allow modification during parsing */
+ scratch_mem = os_malloc(url_len + 1);
if (scratch_mem == NULL)
goto fail;
- mem = scratch_mem;
- strcpy(mem, url);
- domain_and_port = mem;
- mem += 1 + os_strlen(mem);
- delim = os_strchr(domain_and_port, '/');
+ os_memcpy(scratch_mem, url, url_len);
+ scratch_mem[url_len] = '\0';
+ wpa_printf(MSG_DEBUG, "WPS UPnP: Adding URL '%s'", scratch_mem);
+ host = scratch_mem;
+ path = os_strchr(host, '/');
+ if (path)
+ *path++ = '\0'; /* null terminate host */
+
+ /* Process and remove optional port component */
+ delim = os_strchr(host, ':');
if (delim) {
- *delim++ = 0; /* null terminate domain and port */
- path = delim;
- } else {
- path = domain_and_port + os_strlen(domain_and_port);
- }
- domain = mem;
- strcpy(domain, domain_and_port);
- delim = strchr(domain, ':');
- if (delim) {
- *delim++ = 0; /* null terminate domain */
- if (isdigit(*delim))
- port = atol(delim);
+ *delim = '\0'; /* null terminate host name for now */
+ if (isdigit(delim[1]))
+ port = atol(delim + 1);
}
/*
@@ -359,14 +361,24 @@ static void subscr_addr_add_url(struct subscription *s, const char *url)
hints.ai_flags = 0;
#endif
hints.ai_protocol = 0; /* Any protocol? */
- rerr = getaddrinfo(domain, NULL /* fill in port ourselves */,
+ rerr = getaddrinfo(host, NULL /* fill in port ourselves */,
&hints, &result);
if (rerr) {
wpa_printf(MSG_INFO, "WPS UPnP: Resolve error %d (%s) on: %s",
- rerr, gai_strerror(rerr), domain);
+ rerr, gai_strerror(rerr), host);
goto fail;
}
+
+ if (delim)
+ *delim = ':'; /* Restore port */
+
+ host_len = os_strlen(host);
+ path_len = path ? os_strlen(path) : 0;
+ alloc_len = host_len + 1 + 1 + path_len + 1;
+
for (rp = result; rp; rp = rp->ai_next) {
+ struct subscr_addr *a;
+
/* Limit no. of address to avoid denial of service attack */
if (dl_list_len(&s->addr_list) >= MAX_ADDR_PER_SUBSCRIPTION) {
wpa_printf(MSG_INFO, "WPS UPnP: subscr_addr_add_url: "
@@ -376,28 +388,26 @@ static void subscr_addr_add_url(struct subscription *s, const char *url)
a = os_zalloc(sizeof(*a) + alloc_len);
if (a == NULL)
- continue;
- mem = (void *) (a + 1);
+ break;
+ mem = (char *) (a + 1);
a->domain_and_port = mem;
- strcpy(mem, domain_and_port);
- mem += 1 + strlen(mem);
+ os_memcpy(mem, host, host_len);
+ mem += host_len + 1;
a->path = mem;
- if (path[0] != '/')
+ if (path == NULL || path[0] != '/')
*mem++ = '/';
- strcpy(mem, path);
- mem += 1 + strlen(mem);
+ if (path)
+ os_memcpy(mem, path, path_len);
os_memcpy(&a->saddr, rp->ai_addr, sizeof(a->saddr));
a->saddr.sin_port = htons(port);
dl_list_add(&s->addr_list, &a->list);
- a = NULL; /* don't free it below */
}
fail:
if (result)
freeaddrinfo(result);
os_free(scratch_mem);
- os_free(a);
}
@@ -407,7 +417,8 @@ fail:
static void subscr_addr_list_create(struct subscription *s,
const char *url_list)
{
- char *end;
+ const char *end;
+ wpa_printf(MSG_DEBUG, "WPS UPnP: Parsing URL list '%s'", url_list);
for (;;) {
while (*url_list == ' ' || *url_list == '\t')
url_list++;
@@ -417,9 +428,8 @@ static void subscr_addr_list_create(struct subscription *s,
end = os_strchr(url_list, '>');
if (end == NULL)
break;
- *end++ = 0;
- subscr_addr_add_url(s, url_list);
- url_list = end;
+ subscr_addr_add_url(s, url_list, end - url_list);
+ url_list = end + 1;
}
}
@@ -472,12 +482,38 @@ static void upnp_wps_device_send_event(struct upnp_wps_device_sm *sm)
"<?xml version=\"1.0\" encoding=\"utf-8\"?>\n"
"<e:propertyset xmlns:e=\"urn:schemas-upnp-org:event-1-0\">\n";
const char *format_tail = "</e:propertyset>\n";
+ struct os_time now;
if (dl_list_empty(&sm->subscriptions)) {
/* optimize */
return;
}
+ if (os_get_time(&now) == 0) {
+ if (now.sec != sm->last_event_sec) {
+ sm->last_event_sec = now.sec;
+ sm->num_events_in_sec = 1;
+ } else {
+ sm->num_events_in_sec++;
+ /*
+ * In theory, this should apply to all WLANEvent
+ * notifications, but EAP messages are of much higher
+ * priority and Probe Request notifications should not
+ * be allowed to drop EAP messages, so only throttle
+ * Probe Request notifications.
+ */
+ if (sm->num_events_in_sec > MAX_EVENTS_PER_SEC &&
+ sm->wlanevent_type ==
+ UPNP_WPS_WLANEVENT_TYPE_PROBE) {
+ wpa_printf(MSG_DEBUG, "WPS UPnP: Throttle "
+ "event notifications (%u seen "
+ "during one second)",
+ sm->num_events_in_sec);
+ return;
+ }
+ }
+ }
+
/* Determine buffer size needed first */
buf_size += os_strlen(format_head);
buf_size += 50 + 2 * os_strlen("WLANEvent");
@@ -497,12 +533,8 @@ static void upnp_wps_device_send_event(struct upnp_wps_device_sm *sm)
dl_list_for_each_safe(s, tmp, &sm->subscriptions, struct subscription,
list) {
- if (event_add(s, buf)) {
- wpa_printf(MSG_INFO, "WPS UPnP: Dropping "
- "subscriber due to event backlog");
- dl_list_del(&s->list);
- subscription_destroy(s);
- }
+ event_add(s, buf,
+ sm->wlanevent_type == UPNP_WPS_WLANEVENT_TYPE_PROBE);
}
wpabuf_free(buf);
@@ -520,10 +552,13 @@ static void upnp_wps_device_send_event(struct upnp_wps_device_sm *sm)
*/
void subscription_destroy(struct subscription *s)
{
+ struct upnp_wps_device_interface *iface;
wpa_printf(MSG_DEBUG, "WPS UPnP: Destroy subscription %p", s);
subscr_addr_free_all(s);
event_delete_all(s);
- upnp_er_remove_notification(s);
+ dl_list_for_each(iface, &s->sm->interfaces,
+ struct upnp_wps_device_interface, list)
+ upnp_er_remove_notification(iface->wps->registrar, s);
os_free(s);
}
@@ -578,6 +613,7 @@ static struct wpabuf * build_fake_wsc_ack(void)
wpabuf_put_be16(msg, ATTR_REGISTRAR_NONCE);
wpabuf_put_be16(msg, WPS_NONCE_LEN);
wpabuf_put(msg, WPS_NONCE_LEN);
+ wps_build_wfa_ext(msg, 0, NULL, 0);
return msg;
}
@@ -605,6 +641,7 @@ static int subscription_first_event(struct subscription *s)
"<e:propertyset xmlns:e=\"urn:schemas-upnp-org:event-1-0\">\n";
const char *tail = "</e:propertyset>\n";
char txt[10];
+ int ret;
if (s->sm->wlanevent == NULL) {
/*
@@ -636,7 +673,7 @@ static int subscription_first_event(struct subscription *s)
}
buf = wpabuf_alloc(500 + os_strlen(wlan_event));
if (buf == NULL)
- return 1;
+ return -1;
wpabuf_put_str(buf, head);
wpabuf_put_property(buf, "STAStatus", "1");
@@ -646,9 +683,10 @@ static int subscription_first_event(struct subscription *s)
wpabuf_put_property(buf, "WLANEvent", wlan_event);
wpabuf_put_str(buf, tail);
- if (event_add(s, buf)) {
+ ret = event_add(s, buf, 0);
+ if (ret) {
wpabuf_free(buf);
- return 1;
+ return ret;
}
wpabuf_free(buf);
@@ -692,6 +730,13 @@ struct subscription * subscription_start(struct upnp_wps_device_sm *sm,
s->timeout_time = expire;
uuid_make(s->uuid);
subscr_addr_list_create(s, callback_urls);
+ if (dl_list_empty(&s->addr_list)) {
+ wpa_printf(MSG_DEBUG, "WPS UPnP: No valid callback URLs in "
+ "'%s' - drop subscription", callback_urls);
+ subscription_destroy(s);
+ return NULL;
+ }
+
/* Add to end of list, since it has the highest expiration time */
dl_list_add_tail(&sm->subscriptions, &s->list);
/* Queue up immediate event message (our last event)
@@ -786,6 +831,7 @@ int upnp_wps_device_send_wlan_event(struct upnp_wps_device_sm *sm,
os_free(sm->wlanevent);
sm->wlanevent = val;
+ sm->wlanevent_type = ev_type;
upnp_wps_device_send_event(sm);
ret = 0;
@@ -914,10 +960,13 @@ static void upnp_wps_free_msearchreply(struct dl_list *head)
}
-static void upnp_wps_free_subscriptions(struct dl_list *head)
+static void upnp_wps_free_subscriptions(struct dl_list *head,
+ struct wps_registrar *reg)
{
struct subscription *s, *tmp;
dl_list_for_each_safe(s, tmp, head, struct subscription, list) {
+ if (reg && s->reg != reg)
+ continue;
dl_list_del(&s->list);
subscription_destroy(s);
}
@@ -928,7 +977,7 @@ static void upnp_wps_free_subscriptions(struct dl_list *head)
* upnp_wps_device_stop - Stop WPS UPnP operations on an interface
* @sm: WPS UPnP state machine from upnp_wps_device_init()
*/
-void upnp_wps_device_stop(struct upnp_wps_device_sm *sm)
+static void upnp_wps_device_stop(struct upnp_wps_device_sm *sm)
{
if (!sm || !sm->started)
return;
@@ -936,7 +985,7 @@ void upnp_wps_device_stop(struct upnp_wps_device_sm *sm)
wpa_printf(MSG_DEBUG, "WPS UPnP: Stop device");
web_listener_stop(sm);
upnp_wps_free_msearchreply(&sm->msearch_replies);
- upnp_wps_free_subscriptions(&sm->subscriptions);
+ upnp_wps_free_subscriptions(&sm->subscriptions, NULL);
advertisement_state_machine_stop(sm, 1);
@@ -960,7 +1009,7 @@ void upnp_wps_device_stop(struct upnp_wps_device_sm *sm)
* @net_if: Selected network interface name
* Returns: 0 on success, -1 on failure
*/
-int upnp_wps_device_start(struct upnp_wps_device_sm *sm, char *net_if)
+static int upnp_wps_device_start(struct upnp_wps_device_sm *sm, char *net_if)
{
if (!sm || !net_if)
return -1;
@@ -1015,24 +1064,59 @@ fail:
}
+static struct upnp_wps_device_interface *
+upnp_wps_get_iface(struct upnp_wps_device_sm *sm, void *priv)
+{
+ struct upnp_wps_device_interface *iface;
+ dl_list_for_each(iface, &sm->interfaces,
+ struct upnp_wps_device_interface, list) {
+ if (iface->priv == priv)
+ return iface;
+ }
+ return NULL;
+}
+
+
/**
* upnp_wps_device_deinit - Deinitialize WPS UPnP
* @sm: WPS UPnP state machine from upnp_wps_device_init()
+ * @priv: External context data that was used in upnp_wps_device_init() call
*/
-void upnp_wps_device_deinit(struct upnp_wps_device_sm *sm)
+void upnp_wps_device_deinit(struct upnp_wps_device_sm *sm, void *priv)
{
+ struct upnp_wps_device_interface *iface;
+
if (!sm)
return;
- upnp_wps_device_stop(sm);
-
- if (sm->peer.wps)
- wps_deinit(sm->peer.wps);
- os_free(sm->root_dir);
- os_free(sm->desc_url);
- os_free(sm->ctx->ap_pin);
- os_free(sm->ctx);
- os_free(sm);
+ iface = upnp_wps_get_iface(sm, priv);
+ if (iface == NULL) {
+ wpa_printf(MSG_ERROR, "WPS UPnP: Could not find the interface "
+ "instance to deinit");
+ return;
+ }
+ wpa_printf(MSG_DEBUG, "WPS UPnP: Deinit interface instance %p", iface);
+ if (dl_list_len(&sm->interfaces) == 1) {
+ wpa_printf(MSG_DEBUG, "WPS UPnP: Deinitializing last instance "
+ "- free global device instance");
+ upnp_wps_device_stop(sm);
+ } else
+ upnp_wps_free_subscriptions(&sm->subscriptions,
+ iface->wps->registrar);
+ dl_list_del(&iface->list);
+
+ if (iface->peer.wps)
+ wps_deinit(iface->peer.wps);
+ os_free(iface->ctx->ap_pin);
+ os_free(iface->ctx);
+ os_free(iface);
+
+ if (dl_list_empty(&sm->interfaces)) {
+ os_free(sm->root_dir);
+ os_free(sm->desc_url);
+ os_free(sm);
+ shared_upnp_device = NULL;
+ }
}
@@ -1041,25 +1125,59 @@ void upnp_wps_device_deinit(struct upnp_wps_device_sm *sm)
* @ctx: callback table; we must eventually free it
* @wps: Pointer to longterm WPS context
* @priv: External context data that will be used in callbacks
+ * @net_if: Selected network interface name
* Returns: WPS UPnP state or %NULL on failure
*/
struct upnp_wps_device_sm *
upnp_wps_device_init(struct upnp_wps_device_ctx *ctx, struct wps_context *wps,
- void *priv)
+ void *priv, char *net_if)
{
struct upnp_wps_device_sm *sm;
+ struct upnp_wps_device_interface *iface;
+ int start = 0;
- sm = os_zalloc(sizeof(*sm));
- if (!sm) {
- wpa_printf(MSG_ERROR, "WPS UPnP: upnp_wps_device_init failed");
+ iface = os_zalloc(sizeof(*iface));
+ if (iface == NULL) {
+ os_free(ctx->ap_pin);
+ os_free(ctx);
+ return NULL;
+ }
+ wpa_printf(MSG_DEBUG, "WPS UPnP: Init interface instance %p", iface);
+
+ iface->ctx = ctx;
+ iface->wps = wps;
+ iface->priv = priv;
+
+ if (shared_upnp_device) {
+ wpa_printf(MSG_DEBUG, "WPS UPnP: Share existing device "
+ "context");
+ sm = shared_upnp_device;
+ } else {
+ wpa_printf(MSG_DEBUG, "WPS UPnP: Initialize device context");
+ sm = os_zalloc(sizeof(*sm));
+ if (!sm) {
+ wpa_printf(MSG_ERROR, "WPS UPnP: upnp_wps_device_init "
+ "failed");
+ os_free(iface);
+ os_free(ctx->ap_pin);
+ os_free(ctx);
+ return NULL;
+ }
+ shared_upnp_device = sm;
+
+ dl_list_init(&sm->msearch_replies);
+ dl_list_init(&sm->subscriptions);
+ dl_list_init(&sm->interfaces);
+ start = 1;
+ }
+
+ dl_list_add(&sm->interfaces, &iface->list);
+
+ if (start && upnp_wps_device_start(sm, net_if)) {
+ upnp_wps_device_deinit(sm, priv);
return NULL;
}
- sm->ctx = ctx;
- sm->wps = wps;
- sm->priv = priv;
- dl_list_init(&sm->msearch_replies);
- dl_list_init(&sm->subscriptions);
return sm;
}
@@ -1078,16 +1196,20 @@ int upnp_wps_subscribers(struct upnp_wps_device_sm *sm)
int upnp_wps_set_ap_pin(struct upnp_wps_device_sm *sm, const char *ap_pin)
{
+ struct upnp_wps_device_interface *iface;
if (sm == NULL)
return 0;
- os_free(sm->ctx->ap_pin);
- if (ap_pin) {
- sm->ctx->ap_pin = os_strdup(ap_pin);
- if (sm->ctx->ap_pin == NULL)
- return -1;
- } else
- sm->ctx->ap_pin = NULL;
+ dl_list_for_each(iface, &sm->interfaces,
+ struct upnp_wps_device_interface, list) {
+ os_free(iface->ctx->ap_pin);
+ if (ap_pin) {
+ iface->ctx->ap_pin = os_strdup(ap_pin);
+ if (iface->ctx->ap_pin == NULL)
+ return -1;
+ } else
+ iface->ctx->ap_pin = NULL;
+ }
return 0;
}
diff --git a/contrib/wpa/src/wps/wps_upnp.h b/contrib/wpa/src/wps/wps_upnp.h
index 06bc31f..87b7ab1 100644
--- a/contrib/wpa/src/wps/wps_upnp.h
+++ b/contrib/wpa/src/wps/wps_upnp.h
@@ -35,11 +35,8 @@ struct upnp_wps_device_ctx {
struct upnp_wps_device_sm *
upnp_wps_device_init(struct upnp_wps_device_ctx *ctx, struct wps_context *wps,
- void *priv);
-void upnp_wps_device_deinit(struct upnp_wps_device_sm *sm);
-
-int upnp_wps_device_start(struct upnp_wps_device_sm *sm, char *net_if);
-void upnp_wps_device_stop(struct upnp_wps_device_sm *sm);
+ void *priv, char *net_if);
+void upnp_wps_device_deinit(struct upnp_wps_device_sm *sm, void *priv);
int upnp_wps_device_send_wlan_event(struct upnp_wps_device_sm *sm,
const u8 from_mac_addr[ETH_ALEN],
diff --git a/contrib/wpa/src/wps/wps_upnp_ap.c b/contrib/wpa/src/wps/wps_upnp_ap.c
index 93746da..54ed98f 100644
--- a/contrib/wpa/src/wps/wps_upnp_ap.c
+++ b/contrib/wpa/src/wps/wps_upnp_ap.c
@@ -2,14 +2,8 @@
* Wi-Fi Protected Setup - UPnP AP functionality
* Copyright (c) 2009, Jouni Malinen <j@w1.fi>
*
- * This program is free software; you can redistribute it and/or modify
- * it under the terms of the GNU General Public License version 2 as
- * published by the Free Software Foundation.
- *
- * Alternatively, this software may be distributed under the terms of BSD
- * license.
- *
- * See README and COPYING for more details.
+ * This software may be distributed under the terms of the BSD license.
+ * See README for more details.
*/
#include "includes.h"
@@ -25,9 +19,10 @@
static void upnp_er_set_selected_timeout(void *eloop_ctx, void *timeout_ctx)
{
struct subscription *s = eloop_ctx;
+ struct wps_registrar *reg = timeout_ctx;
wpa_printf(MSG_DEBUG, "WPS: SetSelectedRegistrar from ER timed out");
s->selected_registrar = 0;
- wps_registrar_selected_registrar_changed(s->reg);
+ wps_registrar_selected_registrar_changed(reg);
}
@@ -39,18 +34,16 @@ int upnp_er_set_selected_registrar(struct wps_registrar *reg,
wpa_hexdump_buf(MSG_MSGDUMP, "WPS: SetSelectedRegistrar attributes",
msg);
+ if (wps_validate_upnp_set_selected_registrar(msg) < 0)
+ return -1;
if (wps_parse_msg(msg, &attr) < 0)
return -1;
- if (!wps_version_supported(attr.version)) {
- wpa_printf(MSG_DEBUG, "WPS: Unsupported SetSelectedRegistrar "
- "version 0x%x", attr.version ? *attr.version : 0);
- return -1;
- }
s->reg = reg;
- eloop_cancel_timeout(upnp_er_set_selected_timeout, s, NULL);
+ eloop_cancel_timeout(upnp_er_set_selected_timeout, s, reg);
+ os_memset(s->authorized_macs, 0, sizeof(s->authorized_macs));
if (attr.selected_registrar == NULL || *attr.selected_registrar == 0) {
wpa_printf(MSG_DEBUG, "WPS: SetSelectedRegistrar: Disable "
"Selected Registrar");
@@ -61,8 +54,21 @@ int upnp_er_set_selected_registrar(struct wps_registrar *reg,
WPA_GET_BE16(attr.dev_password_id) : DEV_PW_DEFAULT;
s->config_methods = attr.sel_reg_config_methods ?
WPA_GET_BE16(attr.sel_reg_config_methods) : -1;
+ if (attr.authorized_macs) {
+ int count = attr.authorized_macs_len / ETH_ALEN;
+ if (count > WPS_MAX_AUTHORIZED_MACS)
+ count = WPS_MAX_AUTHORIZED_MACS;
+ os_memcpy(s->authorized_macs, attr.authorized_macs,
+ count * ETH_ALEN);
+ } else if (!attr.version2) {
+#ifdef CONFIG_WPS2
+ wpa_printf(MSG_DEBUG, "WPS: Add broadcast "
+ "AuthorizedMACs for WPS 1.0 ER");
+ os_memset(s->authorized_macs, 0xff, ETH_ALEN);
+#endif /* CONFIG_WPS2 */
+ }
eloop_register_timeout(WPS_PBC_WALK_TIME, 0,
- upnp_er_set_selected_timeout, s, NULL);
+ upnp_er_set_selected_timeout, s, reg);
}
wps_registrar_selected_registrar_changed(reg);
@@ -71,10 +77,11 @@ int upnp_er_set_selected_registrar(struct wps_registrar *reg,
}
-void upnp_er_remove_notification(struct subscription *s)
+void upnp_er_remove_notification(struct wps_registrar *reg,
+ struct subscription *s)
{
s->selected_registrar = 0;
- eloop_cancel_timeout(upnp_er_set_selected_timeout, s, NULL);
- if (s->reg)
- wps_registrar_selected_registrar_changed(s->reg);
+ eloop_cancel_timeout(upnp_er_set_selected_timeout, s, reg);
+ if (reg)
+ wps_registrar_selected_registrar_changed(reg);
}
diff --git a/contrib/wpa/src/wps/wps_upnp_event.c b/contrib/wpa/src/wps/wps_upnp_event.c
index ae5efdb..2c8ed4f1 100644
--- a/contrib/wpa/src/wps/wps_upnp_event.c
+++ b/contrib/wpa/src/wps/wps_upnp_event.c
@@ -3,7 +3,7 @@
* Copyright (c) 2000-2003 Intel Corporation
* Copyright (c) 2006-2007 Sony Corporation
* Copyright (c) 2008-2009 Atheros Communications
- * Copyright (c) 2009, Jouni Malinen <j@w1.fi>
+ * Copyright (c) 2009-2010, Jouni Malinen <j@w1.fi>
*
* See wps_upnp.c for more details on licensing and code history.
*/
@@ -31,7 +31,7 @@
*/
#define MAX_EVENTS_QUEUED 20 /* How far behind queued events */
-#define EVENT_TIMEOUT_SEC 30 /* Drop sending event after timeout */
+#define MAX_FAILURES 10 /* Drop subscription after this many failures */
/* How long to wait before sending event */
#define EVENT_DELAY_SECONDS 0
@@ -73,6 +73,7 @@ static void event_clean(struct wps_event_ *e)
*/
static void event_delete(struct wps_event_ *e)
{
+ wpa_printf(MSG_DEBUG, "WPS UPnP: Delete event %p", e);
event_clean(e);
wpabuf_free(e->data);
os_free(e);
@@ -86,8 +87,11 @@ static struct wps_event_ *event_dequeue(struct subscription *s)
{
struct wps_event_ *e;
e = dl_list_first(&s->event_queue, struct wps_event_, list);
- if (e)
+ if (e) {
+ wpa_printf(MSG_DEBUG, "WPS UPnP: Dequeue event %p for "
+ "subscription %p", e, s);
dl_list_del(&e->list);
+ }
return e;
}
@@ -115,14 +119,22 @@ static void event_retry(struct wps_event_ *e, int do_next_address)
struct subscription *s = e->s;
struct upnp_wps_device_sm *sm = s->sm;
+ wpa_printf(MSG_DEBUG, "WPS UPnP: Retry event %p for subscription %p",
+ e, s);
event_clean(e);
/* will set: s->current_event = NULL; */
- if (do_next_address)
+ if (do_next_address) {
e->retry++;
+ wpa_printf(MSG_DEBUG, "WPS UPnP: Try address %d", e->retry);
+ }
if (e->retry >= dl_list_len(&s->addr_list)) {
wpa_printf(MSG_DEBUG, "WPS UPnP: Giving up on sending event "
"for %s", e->addr->domain_and_port);
+ event_delete(e);
+ s->last_event_failed = 1;
+ if (!dl_list_empty(&s->event_queue))
+ event_send_all_later(s->sm);
return;
}
dl_list_add(&s->event_queue, &e->list);
@@ -158,17 +170,60 @@ static struct wpabuf * event_build_message(struct wps_event_ *e)
}
+static void event_addr_failure(struct wps_event_ *e)
+{
+ struct subscription *s = e->s;
+
+ e->addr->num_failures++;
+ wpa_printf(MSG_DEBUG, "WPS UPnP: Failed to send event %p to %s "
+ "(num_failures=%u)",
+ e, e->addr->domain_and_port, e->addr->num_failures);
+
+ if (e->addr->num_failures < MAX_FAILURES) {
+ /* Try other addresses, if available */
+ event_retry(e, 1);
+ return;
+ }
+
+ /*
+ * If other side doesn't like what we say, forget about them.
+ * (There is no way to tell other side that we are dropping them...).
+ */
+ wpa_printf(MSG_DEBUG, "WPS UPnP: Deleting subscription %p "
+ "address %s due to errors", s, e->addr->domain_and_port);
+ dl_list_del(&e->addr->list);
+ subscr_addr_delete(e->addr);
+ e->addr = NULL;
+
+ if (dl_list_empty(&s->addr_list)) {
+ /* if we've given up on all addresses */
+ wpa_printf(MSG_DEBUG, "WPS UPnP: Removing subscription %p "
+ "with no addresses", s);
+ dl_list_del(&s->list);
+ subscription_destroy(s);
+ return;
+ }
+
+ /* Try other addresses, if available */
+ event_retry(e, 0);
+}
+
+
static void event_http_cb(void *ctx, struct http_client *c,
enum http_client_event event)
{
struct wps_event_ *e = ctx;
struct subscription *s = e->s;
+ wpa_printf(MSG_DEBUG, "WPS UPnP: HTTP client callback: e=%p c=%p "
+ "event=%d", e, c, event);
switch (event) {
case HTTP_CLIENT_OK:
wpa_printf(MSG_DEBUG,
- "WPS UPnP: Got event reply OK from "
- "%s", e->addr->domain_and_port);
+ "WPS UPnP: Got event %p reply OK from %s",
+ e, e->addr->domain_and_port);
+ e->addr->num_failures = 0;
+ s->last_event_failed = 0;
event_delete(e);
/* Schedule sending more if there is more to send */
@@ -176,24 +231,17 @@ static void event_http_cb(void *ctx, struct http_client *c,
event_send_all_later(s->sm);
break;
case HTTP_CLIENT_FAILED:
+ wpa_printf(MSG_DEBUG, "WPS UPnP: Event send failure");
+ event_addr_failure(e);
+ break;
case HTTP_CLIENT_INVALID_REPLY:
- wpa_printf(MSG_DEBUG, "WPS UPnP: Failed to send event to %s",
- e->addr->domain_and_port);
-
- /*
- * If other side doesn't like what we say, forget about them.
- * (There is no way to tell other side that we are dropping
- * them...).
- * Alternately, we could just do event_delete(e)
- */
- wpa_printf(MSG_DEBUG, "WPS UPnP: Deleting subscription due to "
- "errors");
- dl_list_del(&s->list);
- subscription_destroy(s);
+ wpa_printf(MSG_DEBUG, "WPS UPnP: Invalid reply");
+ event_addr_failure(e);
break;
case HTTP_CLIENT_TIMEOUT:
wpa_printf(MSG_DEBUG, "WPS UPnP: Event send timeout");
- event_retry(e, 1);
+ event_addr_failure(e);
+ break;
}
}
@@ -228,9 +276,12 @@ static int event_send_start(struct subscription *s)
* Assume we are called ONLY with no current event and ONLY with
* nonempty event queue and ONLY with at least one address to send to.
*/
- assert(!dl_list_empty(&s->addr_list));
- assert(s->current_event == NULL);
- assert(!dl_list_empty(&s->event_queue));
+ if (dl_list_empty(&s->addr_list))
+ return -1;
+ if (s->current_event)
+ return -1;
+ if (dl_list_empty(&s->event_queue))
+ return -1;
s->current_event = e = event_dequeue(s);
@@ -270,18 +321,10 @@ static void event_send_all_later_handler(void *eloop_data, void *user_ctx)
sm->event_send_all_queued = 0;
dl_list_for_each_safe(s, tmp, &sm->subscriptions, struct subscription,
list) {
- if (dl_list_empty(&s->addr_list)) {
- /* if we've given up on all addresses */
- wpa_printf(MSG_DEBUG, "WPS UPnP: Removing "
- "subscription with no addresses");
- dl_list_del(&s->list);
- subscription_destroy(s);
- } else {
- if (s->current_event == NULL /* not busy */ &&
- !dl_list_empty(&s->event_queue) /* more to do */) {
- if (event_send_start(s))
- nerrors++;
- }
+ if (s->current_event == NULL /* not busy */ &&
+ !dl_list_empty(&s->event_queue) /* more to do */) {
+ if (event_send_start(s))
+ nerrors++;
}
}
@@ -326,31 +369,54 @@ void event_send_stop_all(struct upnp_wps_device_sm *sm)
* event_add - Add a new event to a queue
* @s: Subscription
* @data: Event data (is copied; caller retains ownership)
- * Returns: 0 on success, 1 on error
+ * @probereq: Whether this is a Probe Request event
+ * Returns: 0 on success, -1 on error, 1 on max event queue limit reached
*/
-int event_add(struct subscription *s, const struct wpabuf *data)
+int event_add(struct subscription *s, const struct wpabuf *data, int probereq)
{
struct wps_event_ *e;
+ unsigned int len;
- if (dl_list_len(&s->event_queue) >= MAX_EVENTS_QUEUED) {
+ len = dl_list_len(&s->event_queue);
+ if (len >= MAX_EVENTS_QUEUED) {
wpa_printf(MSG_DEBUG, "WPS UPnP: Too many events queued for "
- "subscriber");
- return 1;
+ "subscriber %p", s);
+ if (probereq)
+ return 1;
+
+ /* Drop oldest entry to allow EAP event to be stored. */
+ e = event_dequeue(s);
+ if (!e)
+ return 1;
+ event_delete(e);
+ }
+
+ if (s->last_event_failed && probereq && len > 0) {
+ /*
+ * Avoid queuing frames for subscribers that may have left
+ * without unsubscribing.
+ */
+ wpa_printf(MSG_DEBUG, "WPS UPnP: Do not queue more Probe "
+ "Request frames for subscription %p since last "
+ "delivery failed", s);
+ return -1;
}
e = os_zalloc(sizeof(*e));
if (e == NULL)
- return 1;
+ return -1;
dl_list_init(&e->list);
e->s = s;
e->data = wpabuf_dup(data);
if (e->data == NULL) {
os_free(e);
- return 1;
+ return -1;
}
e->subscriber_sequence = s->next_subscriber_sequence++;
if (s->next_subscriber_sequence == 0)
s->next_subscriber_sequence++;
+ wpa_printf(MSG_DEBUG, "WPS UPnP: Queue event %p for subscriber %p "
+ "(queue len %u)", e, s, len + 1);
dl_list_add_tail(&s->event_queue, &e->list);
event_send_all_later(s->sm);
return 0;
diff --git a/contrib/wpa/src/wps/wps_upnp_i.h b/contrib/wpa/src/wps/wps_upnp_i.h
index b31875a..7f3c561 100644
--- a/contrib/wpa/src/wps/wps_upnp_i.h
+++ b/contrib/wpa/src/wps/wps_upnp_i.h
@@ -67,6 +67,7 @@ struct subscr_addr {
char *domain_and_port; /* domain and port part of url */
char *path; /* "filepath" part of url (from "mem") */
struct sockaddr_in saddr; /* address for doing connect */
+ unsigned num_failures;
};
@@ -91,25 +92,37 @@ struct subscription {
struct dl_list event_queue; /* Queued event messages. */
struct wps_event_ *current_event; /* non-NULL if being sent (not in q)
*/
+ int last_event_failed; /* Whether delivery of last event failed */
/* Information from SetSelectedRegistrar action */
u8 selected_registrar;
u16 dev_password_id;
u16 config_methods;
+ u8 authorized_macs[WPS_MAX_AUTHORIZED_MACS][ETH_ALEN];
struct wps_registrar *reg;
};
+struct upnp_wps_device_interface {
+ struct dl_list list;
+ struct upnp_wps_device_ctx *ctx; /* callback table */
+ struct wps_context *wps;
+ void *priv;
+
+ /* FIX: maintain separate structures for each UPnP peer */
+ struct upnp_wps_peer peer;
+};
+
/*
- * Our instance data corresponding to one WiFi network interface
- * (multiple might share the same wired network interface!).
+ * Our instance data corresponding to the AP device. Note that there may be
+ * multiple wireless interfaces sharing the same UPnP device instance. Each
+ * such interface is stored in the list of struct upnp_wps_device_interface
+ * instances.
*
* This is known as an opaque struct declaration to users of the WPS UPnP code.
*/
struct upnp_wps_device_sm {
- struct upnp_wps_device_ctx *ctx; /* callback table */
- struct wps_context *wps;
- void *priv;
+ struct dl_list interfaces; /* struct upnp_wps_device_interface */
char *root_dir;
char *desc_url;
int started; /* nonzero if we are active */
@@ -130,9 +143,9 @@ struct upnp_wps_device_sm {
*/
char *wlanevent; /* the last WLANEvent data */
-
- /* FIX: maintain separate structures for each UPnP peer */
- struct upnp_wps_peer peer;
+ enum upnp_wps_wlanevent_type wlanevent_type;
+ os_time_t last_event_sec;
+ unsigned int num_events_in_sec;
};
/* wps_upnp.c */
@@ -144,6 +157,7 @@ struct subscription * subscription_renew(struct upnp_wps_device_sm *sm,
void subscription_destroy(struct subscription *s);
struct subscription * subscription_find(struct upnp_wps_device_sm *sm,
const u8 uuid[UUID_LEN]);
+void subscr_addr_delete(struct subscr_addr *a);
int send_wpabuf(int fd, struct wpabuf *buf);
int get_netif_info(const char *net_if, unsigned *ip_addr, char **ip_addr_text,
u8 mac[ETH_ALEN]);
@@ -165,7 +179,7 @@ int web_listener_start(struct upnp_wps_device_sm *sm);
void web_listener_stop(struct upnp_wps_device_sm *sm);
/* wps_upnp_event.c */
-int event_add(struct subscription *s, const struct wpabuf *data);
+int event_add(struct subscription *s, const struct wpabuf *data, int probereq);
void event_delete_all(struct subscription *s);
void event_send_all_later(struct upnp_wps_device_sm *sm);
void event_send_stop_all(struct upnp_wps_device_sm *sm);
@@ -174,6 +188,7 @@ void event_send_stop_all(struct upnp_wps_device_sm *sm);
int upnp_er_set_selected_registrar(struct wps_registrar *reg,
struct subscription *s,
const struct wpabuf *msg);
-void upnp_er_remove_notification(struct subscription *s);
+void upnp_er_remove_notification(struct wps_registrar *reg,
+ struct subscription *s);
#endif /* WPS_UPNP_I_H */
diff --git a/contrib/wpa/src/wps/wps_upnp_ssdp.c b/contrib/wpa/src/wps/wps_upnp_ssdp.c
index 8505d05..17a8207 100644
--- a/contrib/wpa/src/wps/wps_upnp_ssdp.c
+++ b/contrib/wpa/src/wps/wps_upnp_ssdp.c
@@ -97,16 +97,6 @@ static int line_length(const char *l)
}
-/* No. of chars excluding trailing whitespace */
-static int line_length_stripped(const char *l)
-{
- const char *lp = l + line_length(l);
- while (lp > l && !isgraph(lp[-1]))
- lp--;
- return lp - l;
-}
-
-
static int str_starts(const char *str, const char *start)
{
return os_strncmp(str, start, os_strlen(start)) == 0;
@@ -136,9 +126,12 @@ next_advertisement(struct upnp_wps_device_sm *sm,
struct wpabuf *msg;
char *NTString = "";
char uuid_string[80];
+ struct upnp_wps_device_interface *iface;
*islast = 0;
- uuid_bin2str(sm->wps->uuid, uuid_string, sizeof(uuid_string));
+ iface = dl_list_first(&sm->interfaces,
+ struct upnp_wps_device_interface, list);
+ uuid_bin2str(iface->wps->uuid, uuid_string, sizeof(uuid_string));
msg = wpabuf_alloc(800); /* more than big enough */
if (msg == NULL)
goto fail;
@@ -527,7 +520,6 @@ static void ssdp_parse_msearch(struct upnp_wps_device_sm *sm,
#ifndef CONFIG_NO_STDOUT_DEBUG
const char *start = data;
#endif /* CONFIG_NO_STDOUT_DEBUG */
- const char *end;
int got_host = 0;
int got_st = 0, st_match = 0;
int got_man = 0;
@@ -542,7 +534,6 @@ static void ssdp_parse_msearch(struct upnp_wps_device_sm *sm,
/* Parse remaining lines */
for (; *data != '\0'; data += line_length(data)) {
- end = data + line_length_stripped(data);
if (token_eq(data, "host")) {
/* The host line indicates who the packet
* is addressed to... but do we really care?
@@ -588,8 +579,13 @@ static void ssdp_parse_msearch(struct upnp_wps_device_sm *sm,
}
if (str_starts(data, "uuid:")) {
char uuid_string[80];
+ struct upnp_wps_device_interface *iface;
+ iface = dl_list_first(
+ &sm->interfaces,
+ struct upnp_wps_device_interface,
+ list);
data += os_strlen("uuid:");
- uuid_bin2str(sm->wps->uuid, uuid_string,
+ uuid_bin2str(iface->wps->uuid, uuid_string,
sizeof(uuid_string));
if (str_starts(data, uuid_string))
st_match = 1;
@@ -870,16 +866,26 @@ int ssdp_open_multicast_sock(u32 ip_addr)
return -1;
#if 0 /* maybe ok if we sometimes block on writes */
- if (fcntl(sd, F_SETFL, O_NONBLOCK) != 0)
+ if (fcntl(sd, F_SETFL, O_NONBLOCK) != 0) {
+ close(sd);
return -1;
+ }
#endif
if (setsockopt(sd, IPPROTO_IP, IP_MULTICAST_IF,
- &ip_addr, sizeof(ip_addr)))
+ &ip_addr, sizeof(ip_addr))) {
+ wpa_printf(MSG_DEBUG, "WPS: setsockopt(IP_MULTICAST_IF) %x: "
+ "%d (%s)", ip_addr, errno, strerror(errno));
+ close(sd);
return -1;
+ }
if (setsockopt(sd, IPPROTO_IP, IP_MULTICAST_TTL,
- &ttl, sizeof(ttl)))
+ &ttl, sizeof(ttl))) {
+ wpa_printf(MSG_DEBUG, "WPS: setsockopt(IP_MULTICAST_TTL): "
+ "%d (%s)", errno, strerror(errno));
+ close(sd);
return -1;
+ }
#if 0 /* not needed, because we don't receive using multicast_sd */
{
@@ -896,6 +902,7 @@ int ssdp_open_multicast_sock(u32 ip_addr)
"WPS UPnP: setsockopt "
"IP_ADD_MEMBERSHIP errno %d (%s)",
errno, strerror(errno));
+ close(sd);
return -1;
}
}
diff --git a/contrib/wpa/src/wps/wps_upnp_web.c b/contrib/wpa/src/wps/wps_upnp_web.c
index 9a6b36e..ce0bede 100644
--- a/contrib/wpa/src/wps/wps_upnp_web.c
+++ b/contrib/wpa/src/wps/wps_upnp_web.c
@@ -184,6 +184,10 @@ static void format_wps_device_xml(struct upnp_wps_device_sm *sm,
{
const char *s;
char uuid_string[80];
+ struct upnp_wps_device_interface *iface;
+
+ iface = dl_list_first(&sm->interfaces,
+ struct upnp_wps_device_interface, list);
wpabuf_put_str(buf, wps_device_xml_prefix);
@@ -191,38 +195,38 @@ static void format_wps_device_xml(struct upnp_wps_device_sm *sm,
* Add required fields with default values if not configured. Add
* optional and recommended fields only if configured.
*/
- s = sm->wps->friendly_name;
+ s = iface->wps->friendly_name;
s = ((s && *s) ? s : "WPS Access Point");
xml_add_tagged_data(buf, "friendlyName", s);
- s = sm->wps->dev.manufacturer;
+ s = iface->wps->dev.manufacturer;
s = ((s && *s) ? s : "");
xml_add_tagged_data(buf, "manufacturer", s);
- if (sm->wps->manufacturer_url)
+ if (iface->wps->manufacturer_url)
xml_add_tagged_data(buf, "manufacturerURL",
- sm->wps->manufacturer_url);
+ iface->wps->manufacturer_url);
- if (sm->wps->model_description)
+ if (iface->wps->model_description)
xml_add_tagged_data(buf, "modelDescription",
- sm->wps->model_description);
+ iface->wps->model_description);
- s = sm->wps->dev.model_name;
+ s = iface->wps->dev.model_name;
s = ((s && *s) ? s : "");
xml_add_tagged_data(buf, "modelName", s);
- if (sm->wps->dev.model_number)
+ if (iface->wps->dev.model_number)
xml_add_tagged_data(buf, "modelNumber",
- sm->wps->dev.model_number);
+ iface->wps->dev.model_number);
- if (sm->wps->model_url)
- xml_add_tagged_data(buf, "modelURL", sm->wps->model_url);
+ if (iface->wps->model_url)
+ xml_add_tagged_data(buf, "modelURL", iface->wps->model_url);
- if (sm->wps->dev.serial_number)
+ if (iface->wps->dev.serial_number)
xml_add_tagged_data(buf, "serialNumber",
- sm->wps->dev.serial_number);
+ iface->wps->dev.serial_number);
- uuid_bin2str(sm->wps->uuid, uuid_string, sizeof(uuid_string));
+ uuid_bin2str(iface->wps->uuid, uuid_string, sizeof(uuid_string));
s = uuid_string;
/* Need "uuid:" prefix, thus we can't use xml_add_tagged_data()
* easily...
@@ -231,8 +235,8 @@ static void format_wps_device_xml(struct upnp_wps_device_sm *sm,
xml_data_encode(buf, s, os_strlen(s));
wpabuf_put_str(buf, "</UDN>\n");
- if (sm->wps->upc)
- xml_add_tagged_data(buf, "UPC", sm->wps->upc);
+ if (iface->wps->upc)
+ xml_add_tagged_data(buf, "UPC", iface->wps->upc);
wpabuf_put_str(buf, wps_device_xml_postfix);
}
@@ -311,6 +315,10 @@ static void web_connection_parse_get(struct upnp_wps_device_sm *sm,
size_t extra_len = 0;
int body_length;
char len_buf[10];
+ struct upnp_wps_device_interface *iface;
+
+ iface = dl_list_first(&sm->interfaces,
+ struct upnp_wps_device_interface, list);
/*
* It is not required that filenames be case insensitive but it is
@@ -322,16 +330,16 @@ static void web_connection_parse_get(struct upnp_wps_device_sm *sm,
wpa_printf(MSG_DEBUG, "WPS UPnP: HTTP GET for device XML");
req = GET_DEVICE_XML_FILE;
extra_len = 3000;
- if (sm->wps->friendly_name)
- extra_len += os_strlen(sm->wps->friendly_name);
- if (sm->wps->manufacturer_url)
- extra_len += os_strlen(sm->wps->manufacturer_url);
- if (sm->wps->model_description)
- extra_len += os_strlen(sm->wps->model_description);
- if (sm->wps->model_url)
- extra_len += os_strlen(sm->wps->model_url);
- if (sm->wps->upc)
- extra_len += os_strlen(sm->wps->upc);
+ if (iface->wps->friendly_name)
+ extra_len += os_strlen(iface->wps->friendly_name);
+ if (iface->wps->manufacturer_url)
+ extra_len += os_strlen(iface->wps->manufacturer_url);
+ if (iface->wps->model_description)
+ extra_len += os_strlen(iface->wps->model_description);
+ if (iface->wps->model_url)
+ extra_len += os_strlen(iface->wps->model_url);
+ if (iface->wps->upc)
+ extra_len += os_strlen(iface->wps->upc);
} else if (!os_strcasecmp(filename, UPNP_WPS_SCPD_XML_FILE)) {
wpa_printf(MSG_DEBUG, "WPS UPnP: HTTP GET for SCPD XML");
req = GET_SCPD_XML_FILE;
@@ -408,11 +416,16 @@ web_process_get_device_info(struct upnp_wps_device_sm *sm,
{
static const char *name = "NewDeviceInfo";
struct wps_config cfg;
- struct upnp_wps_peer *peer = &sm->peer;
+ struct upnp_wps_device_interface *iface;
+ struct upnp_wps_peer *peer;
+
+ iface = dl_list_first(&sm->interfaces,
+ struct upnp_wps_device_interface, list);
+ peer = &iface->peer;
wpa_printf(MSG_DEBUG, "WPS UPnP: GetDeviceInfo");
- if (sm->ctx->ap_pin == NULL)
+ if (iface->ctx->ap_pin == NULL)
return HTTP_INTERNAL_SERVER_ERROR;
/*
@@ -427,9 +440,9 @@ web_process_get_device_info(struct upnp_wps_device_sm *sm,
wps_deinit(peer->wps);
os_memset(&cfg, 0, sizeof(cfg));
- cfg.wps = sm->wps;
- cfg.pin = (u8 *) sm->ctx->ap_pin;
- cfg.pin_len = os_strlen(sm->ctx->ap_pin);
+ cfg.wps = iface->wps;
+ cfg.pin = (u8 *) iface->ctx->ap_pin;
+ cfg.pin_len = os_strlen(iface->ctx->ap_pin);
peer->wps = wps_init(&cfg);
if (peer->wps) {
enum wsc_op_code op_code;
@@ -458,6 +471,10 @@ web_process_put_message(struct upnp_wps_device_sm *sm, char *data,
enum http_reply_code ret;
enum wps_process_res res;
enum wsc_op_code op_code;
+ struct upnp_wps_device_interface *iface;
+
+ iface = dl_list_first(&sm->interfaces,
+ struct upnp_wps_device_interface, list);
/*
* PutMessage is used by external UPnP-based Registrar to perform WPS
@@ -468,11 +485,11 @@ web_process_put_message(struct upnp_wps_device_sm *sm, char *data,
msg = xml_get_base64_item(data, "NewInMessage", &ret);
if (msg == NULL)
return ret;
- res = wps_process_msg(sm->peer.wps, WSC_UPnP, msg);
+ res = wps_process_msg(iface->peer.wps, WSC_UPnP, msg);
if (res == WPS_FAILURE)
*reply = NULL;
else
- *reply = wps_get_msg(sm->peer.wps, &op_code);
+ *reply = wps_get_msg(iface->peer.wps, &op_code);
wpabuf_free(msg);
if (*reply == NULL)
return HTTP_INTERNAL_SERVER_ERROR;
@@ -491,6 +508,8 @@ web_process_put_wlan_response(struct upnp_wps_device_sm *sm, char *data,
int ev_type;
int type;
char *val;
+ struct upnp_wps_device_interface *iface;
+ int ok = 0;
/*
* External UPnP-based Registrar is passing us a message to be proxied
@@ -523,6 +542,16 @@ web_process_put_wlan_response(struct upnp_wps_device_sm *sm, char *data,
if (hwaddr_aton(val, macaddr)) {
wpa_printf(MSG_DEBUG, "WPS UPnP: Invalid NewWLANEventMAC in "
"PutWLANResponse: '%s'", val);
+#ifdef CONFIG_WPS_STRICT
+ {
+ struct wps_parse_attr attr;
+ if (wps_parse_msg(msg, &attr) < 0 || attr.version2) {
+ wpabuf_free(msg);
+ os_free(val);
+ return UPNP_ARG_VALUE_INVALID;
+ }
+ }
+#endif /* CONFIG_WPS_STRICT */
if (hwaddr_aton2(val, macaddr) > 0) {
/*
* At least some versions of Intel PROset seem to be
@@ -530,7 +559,8 @@ web_process_put_wlan_response(struct upnp_wps_device_sm *sm, char *data,
*/
wpa_printf(MSG_DEBUG, "WPS UPnP: Workaround - allow "
"incorrect MAC address format in "
- "NewWLANEventMAC");
+ "NewWLANEventMAC: %s -> " MACSTR,
+ val, MAC2STR(macaddr));
} else {
wpabuf_free(msg);
os_free(val);
@@ -548,9 +578,16 @@ web_process_put_wlan_response(struct upnp_wps_device_sm *sm, char *data,
wpa_printf(MSG_DEBUG, "WPS UPnP: Message Type %d", type);
} else
type = -1;
- if (!sm->ctx->rx_req_put_wlan_response ||
- sm->ctx->rx_req_put_wlan_response(sm->priv, ev_type, macaddr, msg,
- type)) {
+ dl_list_for_each(iface, &sm->interfaces,
+ struct upnp_wps_device_interface, list) {
+ if (iface->ctx->rx_req_put_wlan_response &&
+ iface->ctx->rx_req_put_wlan_response(iface->priv, ev_type,
+ macaddr, msg, type)
+ == 0)
+ ok = 1;
+ }
+
+ if (!ok) {
wpa_printf(MSG_INFO, "WPS UPnP: Fail: sm->ctx->"
"rx_req_put_wlan_response");
wpabuf_free(msg);
@@ -595,6 +632,8 @@ web_process_set_selected_registrar(struct upnp_wps_device_sm *sm,
struct wpabuf *msg;
enum http_reply_code ret;
struct subscription *s;
+ struct upnp_wps_device_interface *iface;
+ int err = 0;
wpa_printf(MSG_DEBUG, "WPS UPnP: SetSelectedRegistrar");
s = find_er(sm, cli);
@@ -606,11 +645,15 @@ web_process_set_selected_registrar(struct upnp_wps_device_sm *sm,
msg = xml_get_base64_item(data, "NewMessage", &ret);
if (msg == NULL)
return ret;
- if (upnp_er_set_selected_registrar(sm->wps->registrar, s, msg)) {
- wpabuf_free(msg);
- return HTTP_INTERNAL_SERVER_ERROR;
+ dl_list_for_each(iface, &sm->interfaces,
+ struct upnp_wps_device_interface, list) {
+ if (upnp_er_set_selected_registrar(iface->wps->registrar, s,
+ msg))
+ err = 1;
}
wpabuf_free(msg);
+ if (err)
+ return HTTP_INTERNAL_SERVER_ERROR;
*replyname = NULL;
*reply = NULL;
return HTTP_OK;
@@ -890,6 +933,9 @@ static void web_connection_parse_subscribe(struct upnp_wps_device_sm *sm,
return;
}
+ wpa_hexdump_ascii(MSG_DEBUG, "WPS UPnP: HTTP SUBSCRIBE",
+ (u8 *) hdr, os_strlen(hdr));
+
/* Parse/validate headers */
h = hdr;
/* First line: SUBSCRIBE /wps_event HTTP/1.1
@@ -910,7 +956,7 @@ static void web_connection_parse_subscribe(struct upnp_wps_device_sm *sm,
break; /* no unterminated lines allowed */
/* NT assures that it is our type of subscription;
- * not used for a renewl.
+ * not used for a renewal.
**/
match = "NT:";
match_len = os_strlen(match);
@@ -989,16 +1035,22 @@ static void web_connection_parse_subscribe(struct upnp_wps_device_sm *sm,
if (got_uuid) {
/* renewal */
+ wpa_printf(MSG_DEBUG, "WPS UPnP: Subscription renewal");
if (callback_urls) {
ret = HTTP_BAD_REQUEST;
goto error;
}
s = subscription_renew(sm, uuid);
if (s == NULL) {
+ char str[80];
+ uuid_bin2str(uuid, str, sizeof(str));
+ wpa_printf(MSG_DEBUG, "WPS UPnP: Could not find "
+ "SID %s", str);
ret = HTTP_PRECONDITION_FAILED;
goto error;
}
} else if (callback_urls) {
+ wpa_printf(MSG_DEBUG, "WPS UPnP: New subscription");
if (!got_nt) {
ret = HTTP_PRECONDITION_FAILED;
goto error;
@@ -1022,6 +1074,7 @@ static void web_connection_parse_subscribe(struct upnp_wps_device_sm *sm,
/* subscription id */
b = wpabuf_put(buf, 0);
uuid_bin2str(s->uuid, b, 80);
+ wpa_printf(MSG_DEBUG, "WPS UPnP: Assigned SID %s", b);
wpabuf_put(buf, os_strlen(b));
wpabuf_put_str(buf, "\r\n");
wpabuf_printf(buf, "Timeout: Second-%d\r\n", UPNP_SUBSCRIBE_SEC);
@@ -1055,6 +1108,7 @@ error:
* HTTP 500-series error code.
* 599 Too many subscriptions (not a standard HTTP error)
*/
+ wpa_printf(MSG_DEBUG, "WPS UPnP: SUBSCRIBE failed - return %d", ret);
http_put_empty(buf, ret);
http_request_send_and_deinit(req, buf);
os_free(callback_urls);
diff --git a/contrib/wpa/src/wps/wps_validate.c b/contrib/wpa/src/wps/wps_validate.c
new file mode 100644
index 0000000..e366256
--- /dev/null
+++ b/contrib/wpa/src/wps/wps_validate.c
@@ -0,0 +1,1975 @@
+/*
+ * Wi-Fi Protected Setup - Strict protocol validation routines
+ * Copyright (c) 2010, Atheros Communications, Inc.
+ *
+ * This software may be distributed under the terms of the BSD license.
+ * See README for more details.
+ */
+
+#include "utils/includes.h"
+
+#include "utils/common.h"
+#include "wps_i.h"
+#include "wps.h"
+
+
+#ifndef WPS_STRICT_ALL
+#define WPS_STRICT_WPS2
+#endif /* WPS_STRICT_ALL */
+
+
+static int wps_validate_version(const u8 *version, int mandatory)
+{
+ if (version == NULL) {
+ if (mandatory) {
+ wpa_printf(MSG_INFO, "WPS-STRICT: Version attribute "
+ "missing");
+ return -1;
+ }
+ return 0;
+ }
+ if (*version != 0x10) {
+ wpa_printf(MSG_INFO, "WPS-STRICT: Invalid Version attribute "
+ "value 0x%x", *version);
+ return -1;
+ }
+ return 0;
+}
+
+
+static int wps_validate_version2(const u8 *version2, int mandatory)
+{
+ if (version2 == NULL) {
+ if (mandatory) {
+ wpa_printf(MSG_INFO, "WPS-STRICT: Version2 attribute "
+ "missing");
+ return -1;
+ }
+ return 0;
+ }
+ if (*version2 < 0x20) {
+ wpa_printf(MSG_INFO, "WPS-STRICT: Invalid Version2 attribute "
+ "value 0x%x", *version2);
+ return -1;
+ }
+ return 0;
+}
+
+
+static int wps_validate_request_type(const u8 *request_type, int mandatory)
+{
+ if (request_type == NULL) {
+ if (mandatory) {
+ wpa_printf(MSG_INFO, "WPS-STRICT: Request Type "
+ "attribute missing");
+ return -1;
+ }
+ return 0;
+ }
+ if (*request_type > 0x03) {
+ wpa_printf(MSG_INFO, "WPS-STRICT: Invalid Request Type "
+ "attribute value 0x%x", *request_type);
+ return -1;
+ }
+ return 0;
+}
+
+
+static int wps_validate_response_type(const u8 *response_type, int mandatory)
+{
+ if (response_type == NULL) {
+ if (mandatory) {
+ wpa_printf(MSG_INFO, "WPS-STRICT: Response Type "
+ "attribute missing");
+ return -1;
+ }
+ return 0;
+ }
+ if (*response_type > 0x03) {
+ wpa_printf(MSG_INFO, "WPS-STRICT: Invalid Response Type "
+ "attribute value 0x%x", *response_type);
+ return -1;
+ }
+ return 0;
+}
+
+
+static int valid_config_methods(u16 val, int wps2)
+{
+ if (wps2) {
+ if ((val & 0x6000) && !(val & WPS_CONFIG_DISPLAY)) {
+ wpa_printf(MSG_INFO, "WPS-STRICT: Physical/Virtual "
+ "Display flag without old Display flag "
+ "set");
+ return 0;
+ }
+ if (!(val & 0x6000) && (val & WPS_CONFIG_DISPLAY)) {
+ wpa_printf(MSG_INFO, "WPS-STRICT: Display flag "
+ "without Physical/Virtual Display flag");
+ return 0;
+ }
+ if ((val & 0x0600) && !(val & WPS_CONFIG_PUSHBUTTON)) {
+ wpa_printf(MSG_INFO, "WPS-STRICT: Physical/Virtual "
+ "PushButton flag without old PushButton "
+ "flag set");
+ return 0;
+ }
+ if (!(val & 0x0600) && (val & WPS_CONFIG_PUSHBUTTON)) {
+ wpa_printf(MSG_INFO, "WPS-STRICT: PushButton flag "
+ "without Physical/Virtual PushButton flag");
+ return 0;
+ }
+ }
+
+ return 1;
+}
+
+
+static int wps_validate_config_methods(const u8 *config_methods, int wps2,
+ int mandatory)
+{
+ u16 val;
+
+ if (config_methods == NULL) {
+ if (mandatory) {
+ wpa_printf(MSG_INFO, "WPS-STRICT: Configuration "
+ "Methods attribute missing");
+ return -1;
+ }
+ return 0;
+ }
+
+ val = WPA_GET_BE16(config_methods);
+ if (!valid_config_methods(val, wps2)) {
+ wpa_printf(MSG_INFO, "WPS-STRICT: Invalid Configuration "
+ "Methods attribute value 0x%04x", val);
+ return -1;
+ }
+ return 0;
+}
+
+
+static int wps_validate_ap_config_methods(const u8 *config_methods, int wps2,
+ int mandatory)
+{
+ u16 val;
+
+ if (wps_validate_config_methods(config_methods, wps2, mandatory) < 0)
+ return -1;
+ if (config_methods == NULL)
+ return 0;
+ val = WPA_GET_BE16(config_methods);
+ if (val & WPS_CONFIG_PUSHBUTTON) {
+ wpa_printf(MSG_INFO, "WPS-STRICT: Invalid Configuration "
+ "Methods attribute value 0x%04x in AP info "
+ "(PushButton not allowed for registering new ER)",
+ val);
+ return -1;
+ }
+ return 0;
+}
+
+
+static int wps_validate_uuid_e(const u8 *uuid_e, int mandatory)
+{
+ if (uuid_e == NULL) {
+ if (mandatory) {
+ wpa_printf(MSG_INFO, "WPS-STRICT: UUID-E "
+ "attribute missing");
+ return -1;
+ }
+ return 0;
+ }
+ return 0;
+}
+
+
+static int wps_validate_uuid_r(const u8 *uuid_r, int mandatory)
+{
+ if (uuid_r == NULL) {
+ if (mandatory) {
+ wpa_printf(MSG_INFO, "WPS-STRICT: UUID-R "
+ "attribute missing");
+ return -1;
+ }
+ return 0;
+ }
+ return 0;
+}
+
+
+static int wps_validate_primary_dev_type(const u8 *primary_dev_type,
+ int mandatory)
+{
+ if (primary_dev_type == NULL) {
+ if (mandatory) {
+ wpa_printf(MSG_INFO, "WPS-STRICT: Primary Device Type "
+ "attribute missing");
+ return -1;
+ }
+ return 0;
+ }
+ return 0;
+}
+
+
+static int wps_validate_rf_bands(const u8 *rf_bands, int mandatory)
+{
+ if (rf_bands == NULL) {
+ if (mandatory) {
+ wpa_printf(MSG_INFO, "WPS-STRICT: RF Bands "
+ "attribute missing");
+ return -1;
+ }
+ return 0;
+ }
+ if (*rf_bands != WPS_RF_24GHZ && *rf_bands != WPS_RF_50GHZ &&
+ *rf_bands != (WPS_RF_24GHZ | WPS_RF_50GHZ)) {
+ wpa_printf(MSG_INFO, "WPS-STRICT: Invalid Rf Bands "
+ "attribute value 0x%x", *rf_bands);
+ return -1;
+ }
+ return 0;
+}
+
+
+static int wps_validate_assoc_state(const u8 *assoc_state, int mandatory)
+{
+ u16 val;
+ if (assoc_state == NULL) {
+ if (mandatory) {
+ wpa_printf(MSG_INFO, "WPS-STRICT: Association State "
+ "attribute missing");
+ return -1;
+ }
+ return 0;
+ }
+ val = WPA_GET_BE16(assoc_state);
+ if (val > 4) {
+ wpa_printf(MSG_INFO, "WPS-STRICT: Invalid Association State "
+ "attribute value 0x%04x", val);
+ return -1;
+ }
+ return 0;
+}
+
+
+static int wps_validate_config_error(const u8 *config_error, int mandatory)
+{
+ u16 val;
+
+ if (config_error == NULL) {
+ if (mandatory) {
+ wpa_printf(MSG_INFO, "WPS-STRICT: Configuration Error "
+ "attribute missing");
+ return -1;
+ }
+ return 0;
+ }
+ val = WPA_GET_BE16(config_error);
+ if (val > 18) {
+ wpa_printf(MSG_INFO, "WPS-STRICT: Invalid Configuration Error "
+ "attribute value 0x%04x", val);
+ return -1;
+ }
+ return 0;
+}
+
+
+static int wps_validate_dev_password_id(const u8 *dev_password_id,
+ int mandatory)
+{
+ u16 val;
+
+ if (dev_password_id == NULL) {
+ if (mandatory) {
+ wpa_printf(MSG_INFO, "WPS-STRICT: Device Password ID "
+ "attribute missing");
+ return -1;
+ }
+ return 0;
+ }
+ val = WPA_GET_BE16(dev_password_id);
+ if (val >= 0x0006 && val <= 0x000f) {
+ wpa_printf(MSG_INFO, "WPS-STRICT: Invalid Device Password ID "
+ "attribute value 0x%04x", val);
+ return -1;
+ }
+ return 0;
+}
+
+
+static int wps_validate_manufacturer(const u8 *manufacturer, size_t len,
+ int mandatory)
+{
+ if (manufacturer == NULL) {
+ if (mandatory) {
+ wpa_printf(MSG_INFO, "WPS-STRICT: Manufacturer "
+ "attribute missing");
+ return -1;
+ }
+ return 0;
+ }
+ if (len > 0 && manufacturer[len - 1] == 0) {
+ wpa_hexdump_ascii(MSG_INFO, "WPS-STRICT: Invalid Manufacturer "
+ "attribute value", manufacturer, len);
+ return -1;
+ }
+ return 0;
+}
+
+
+static int wps_validate_model_name(const u8 *model_name, size_t len,
+ int mandatory)
+{
+ if (model_name == NULL) {
+ if (mandatory) {
+ wpa_printf(MSG_INFO, "WPS-STRICT: Model Name "
+ "attribute missing");
+ return -1;
+ }
+ return 0;
+ }
+ if (len > 0 && model_name[len - 1] == 0) {
+ wpa_hexdump_ascii(MSG_INFO, "WPS-STRICT: Invalid Model Name "
+ "attribute value", model_name, len);
+ return -1;
+ }
+ return 0;
+}
+
+
+static int wps_validate_model_number(const u8 *model_number, size_t len,
+ int mandatory)
+{
+ if (model_number == NULL) {
+ if (mandatory) {
+ wpa_printf(MSG_INFO, "WPS-STRICT: Model Number "
+ "attribute missing");
+ return -1;
+ }
+ return 0;
+ }
+ if (len > 0 && model_number[len - 1] == 0) {
+ wpa_hexdump_ascii(MSG_INFO, "WPS-STRICT: Invalid Model Number "
+ "attribute value", model_number, len);
+ return -1;
+ }
+ return 0;
+}
+
+
+static int wps_validate_serial_number(const u8 *serial_number, size_t len,
+ int mandatory)
+{
+ if (serial_number == NULL) {
+ if (mandatory) {
+ wpa_printf(MSG_INFO, "WPS-STRICT: Serial Number "
+ "attribute missing");
+ return -1;
+ }
+ return 0;
+ }
+ if (len > 0 && serial_number[len - 1] == 0) {
+ wpa_hexdump_ascii(MSG_INFO, "WPS-STRICT: Invalid Serial "
+ "Number attribute value",
+ serial_number, len);
+ return -1;
+ }
+ return 0;
+}
+
+
+static int wps_validate_dev_name(const u8 *dev_name, size_t len,
+ int mandatory)
+{
+ if (dev_name == NULL) {
+ if (mandatory) {
+ wpa_printf(MSG_INFO, "WPS-STRICT: Device Name "
+ "attribute missing");
+ return -1;
+ }
+ return 0;
+ }
+ if (len > 0 && dev_name[len - 1] == 0) {
+ wpa_hexdump_ascii(MSG_INFO, "WPS-STRICT: Invalid Device Name "
+ "attribute value", dev_name, len);
+ return -1;
+ }
+ return 0;
+}
+
+
+static int wps_validate_request_to_enroll(const u8 *request_to_enroll,
+ int mandatory)
+{
+ if (request_to_enroll == NULL) {
+ if (mandatory) {
+ wpa_printf(MSG_INFO, "WPS-STRICT: Request to Enroll "
+ "attribute missing");
+ return -1;
+ }
+ return 0;
+ }
+ if (*request_to_enroll > 0x01) {
+ wpa_printf(MSG_INFO, "WPS-STRICT: Invalid Request to Enroll "
+ "attribute value 0x%x", *request_to_enroll);
+ return -1;
+ }
+ return 0;
+}
+
+
+static int wps_validate_req_dev_type(const u8 *req_dev_type[], size_t num,
+ int mandatory)
+{
+ if (num == 0) {
+ if (mandatory) {
+ wpa_printf(MSG_INFO, "WPS-STRICT: Requested Device "
+ "Type attribute missing");
+ return -1;
+ }
+ return 0;
+ }
+ return 0;
+}
+
+
+static int wps_validate_wps_state(const u8 *wps_state, int mandatory)
+{
+ if (wps_state == NULL) {
+ if (mandatory) {
+ wpa_printf(MSG_INFO, "WPS-STRICT: Wi-Fi Protected "
+ "Setup State attribute missing");
+ return -1;
+ }
+ return 0;
+ }
+ if (*wps_state != WPS_STATE_NOT_CONFIGURED &&
+ *wps_state != WPS_STATE_CONFIGURED) {
+ wpa_printf(MSG_INFO, "WPS-STRICT: Invalid Wi-Fi Protected "
+ "Setup State attribute value 0x%x", *wps_state);
+ return -1;
+ }
+ return 0;
+}
+
+
+static int wps_validate_ap_setup_locked(const u8 *ap_setup_locked,
+ int mandatory)
+{
+ if (ap_setup_locked == NULL) {
+ if (mandatory) {
+ wpa_printf(MSG_INFO, "WPS-STRICT: AP Setup Locked "
+ "attribute missing");
+ return -1;
+ }
+ return 0;
+ }
+ if (*ap_setup_locked > 1) {
+ wpa_printf(MSG_INFO, "WPS-STRICT: Invalid AP Setup Locked "
+ "attribute value 0x%x", *ap_setup_locked);
+ return -1;
+ }
+ return 0;
+}
+
+
+static int wps_validate_selected_registrar(const u8 *selected_registrar,
+ int mandatory)
+{
+ if (selected_registrar == NULL) {
+ if (mandatory) {
+ wpa_printf(MSG_INFO, "WPS-STRICT: Selected Registrar "
+ "attribute missing");
+ return -1;
+ }
+ return 0;
+ }
+ if (*selected_registrar > 1) {
+ wpa_printf(MSG_INFO, "WPS-STRICT: Invalid Selected Registrar "
+ "attribute value 0x%x", *selected_registrar);
+ return -1;
+ }
+ return 0;
+}
+
+
+static int wps_validate_sel_reg_config_methods(const u8 *config_methods,
+ int wps2, int mandatory)
+{
+ u16 val;
+
+ if (config_methods == NULL) {
+ if (mandatory) {
+ wpa_printf(MSG_INFO, "WPS-STRICT: Selected Registrar "
+ "Configuration Methods attribute missing");
+ return -1;
+ }
+ return 0;
+ }
+
+ val = WPA_GET_BE16(config_methods);
+ if (!valid_config_methods(val, wps2)) {
+ wpa_printf(MSG_INFO, "WPS-STRICT: Invalid Selected Registrar "
+ "Configuration Methods attribute value 0x%04x",
+ val);
+ return -1;
+ }
+ return 0;
+}
+
+
+static int wps_validate_authorized_macs(const u8 *authorized_macs, size_t len,
+ int mandatory)
+{
+ if (authorized_macs == NULL) {
+ if (mandatory) {
+ wpa_printf(MSG_INFO, "WPS-STRICT: Authorized MACs "
+ "attribute missing");
+ return -1;
+ }
+ return 0;
+ }
+ if (len > 30 && (len % ETH_ALEN) != 0) {
+ wpa_hexdump(MSG_INFO, "WPS-STRICT: Invalid Authorized "
+ "MACs attribute value", authorized_macs, len);
+ return -1;
+ }
+ return 0;
+}
+
+
+static int wps_validate_msg_type(const u8 *msg_type, int mandatory)
+{
+ if (msg_type == NULL) {
+ if (mandatory) {
+ wpa_printf(MSG_INFO, "WPS-STRICT: Message Type "
+ "attribute missing");
+ return -1;
+ }
+ return 0;
+ }
+ if (*msg_type < WPS_Beacon || *msg_type > WPS_WSC_DONE) {
+ wpa_printf(MSG_INFO, "WPS-STRICT: Invalid Message Type "
+ "attribute value 0x%x", *msg_type);
+ return -1;
+ }
+ return 0;
+}
+
+
+static int wps_validate_mac_addr(const u8 *mac_addr, int mandatory)
+{
+ if (mac_addr == NULL) {
+ if (mandatory) {
+ wpa_printf(MSG_INFO, "WPS-STRICT: MAC Address "
+ "attribute missing");
+ return -1;
+ }
+ return 0;
+ }
+ if (mac_addr[0] & 0x01) {
+ wpa_printf(MSG_INFO, "WPS-STRICT: Invalid MAC Address "
+ "attribute value " MACSTR, MAC2STR(mac_addr));
+ return -1;
+ }
+ return 0;
+}
+
+
+static int wps_validate_enrollee_nonce(const u8 *enrollee_nonce, int mandatory)
+{
+ if (enrollee_nonce == NULL) {
+ if (mandatory) {
+ wpa_printf(MSG_INFO, "WPS-STRICT: Enrollee Nonce "
+ "attribute missing");
+ return -1;
+ }
+ return 0;
+ }
+ return 0;
+}
+
+
+static int wps_validate_registrar_nonce(const u8 *registrar_nonce,
+ int mandatory)
+{
+ if (registrar_nonce == NULL) {
+ if (mandatory) {
+ wpa_printf(MSG_INFO, "WPS-STRICT: Registrar Nonce "
+ "attribute missing");
+ return -1;
+ }
+ return 0;
+ }
+ return 0;
+}
+
+
+static int wps_validate_public_key(const u8 *public_key, size_t len,
+ int mandatory)
+{
+ if (public_key == NULL) {
+ if (mandatory) {
+ wpa_printf(MSG_INFO, "WPS-STRICT: Public Key "
+ "attribute missing");
+ return -1;
+ }
+ return 0;
+ }
+ if (len != 192) {
+ wpa_printf(MSG_INFO, "WPS-STRICT: Invalid Public Key "
+ "attribute length %d", (int) len);
+ return -1;
+ }
+ return 0;
+}
+
+
+static int num_bits_set(u16 val)
+{
+ int c;
+ for (c = 0; val; c++)
+ val &= val - 1;
+ return c;
+}
+
+
+static int wps_validate_auth_type_flags(const u8 *flags, int mandatory)
+{
+ u16 val;
+
+ if (flags == NULL) {
+ if (mandatory) {
+ wpa_printf(MSG_INFO, "WPS-STRICT: Authentication Type "
+ "Flags attribute missing");
+ return -1;
+ }
+ return 0;
+ }
+ val = WPA_GET_BE16(flags);
+ if ((val & ~WPS_AUTH_TYPES) || !(val & WPS_AUTH_WPA2PSK)) {
+ wpa_printf(MSG_INFO, "WPS-STRICT: Invalid Authentication Type "
+ "Flags attribute value 0x%04x", val);
+ return -1;
+ }
+ return 0;
+}
+
+
+static int wps_validate_auth_type(const u8 *type, int mandatory)
+{
+ u16 val;
+
+ if (type == NULL) {
+ if (mandatory) {
+ wpa_printf(MSG_INFO, "WPS-STRICT: Authentication Type "
+ "attribute missing");
+ return -1;
+ }
+ return 0;
+ }
+ val = WPA_GET_BE16(type);
+ if ((val & ~WPS_AUTH_TYPES) || val == 0 ||
+ (num_bits_set(val) > 1 &&
+ val != (WPS_AUTH_WPAPSK | WPS_AUTH_WPA2PSK))) {
+ wpa_printf(MSG_INFO, "WPS-STRICT: Invalid Authentication Type "
+ "attribute value 0x%04x", val);
+ return -1;
+ }
+ return 0;
+}
+
+
+static int wps_validate_encr_type_flags(const u8 *flags, int mandatory)
+{
+ u16 val;
+
+ if (flags == NULL) {
+ if (mandatory) {
+ wpa_printf(MSG_INFO, "WPS-STRICT: Encryption Type "
+ "Flags attribute missing");
+ return -1;
+ }
+ return 0;
+ }
+ val = WPA_GET_BE16(flags);
+ if ((val & ~WPS_ENCR_TYPES) || !(val & WPS_ENCR_AES)) {
+ wpa_printf(MSG_INFO, "WPS-STRICT: Invalid Encryption Type "
+ "Flags attribute value 0x%04x", val);
+ return -1;
+ }
+ return 0;
+}
+
+
+static int wps_validate_encr_type(const u8 *type, int mandatory)
+{
+ u16 val;
+
+ if (type == NULL) {
+ if (mandatory) {
+ wpa_printf(MSG_INFO, "WPS-STRICT: Encryption Type "
+ "attribute missing");
+ return -1;
+ }
+ return 0;
+ }
+ val = WPA_GET_BE16(type);
+ if ((val & ~WPS_ENCR_TYPES) || val == 0 ||
+ (num_bits_set(val) > 1 && val != (WPS_ENCR_TKIP | WPS_ENCR_AES))) {
+ wpa_printf(MSG_INFO, "WPS-STRICT: Invalid Encryption Type "
+ "attribute value 0x%04x", val);
+ return -1;
+ }
+ return 0;
+}
+
+
+static int wps_validate_conn_type_flags(const u8 *flags, int mandatory)
+{
+ if (flags == NULL) {
+ if (mandatory) {
+ wpa_printf(MSG_INFO, "WPS-STRICT: Connection Type "
+ "Flags attribute missing");
+ return -1;
+ }
+ return 0;
+ }
+ if ((*flags & ~(WPS_CONN_ESS | WPS_CONN_IBSS)) ||
+ !(*flags & WPS_CONN_ESS)) {
+ wpa_printf(MSG_INFO, "WPS-STRICT: Invalid Connection Type "
+ "Flags attribute value 0x%02x", *flags);
+ return -1;
+ }
+ return 0;
+}
+
+
+static int wps_validate_os_version(const u8 *os_version, int mandatory)
+{
+ if (os_version == NULL) {
+ if (mandatory) {
+ wpa_printf(MSG_INFO, "WPS-STRICT: OS Version "
+ "attribute missing");
+ return -1;
+ }
+ return 0;
+ }
+ return 0;
+}
+
+
+static int wps_validate_authenticator(const u8 *authenticator, int mandatory)
+{
+ if (authenticator == NULL) {
+ if (mandatory) {
+ wpa_printf(MSG_INFO, "WPS-STRICT: Authenticator "
+ "attribute missing");
+ return -1;
+ }
+ return 0;
+ }
+ return 0;
+}
+
+
+static int wps_validate_e_hash1(const u8 *hash, int mandatory)
+{
+ if (hash == NULL) {
+ if (mandatory) {
+ wpa_printf(MSG_INFO, "WPS-STRICT: E-Hash1 "
+ "attribute missing");
+ return -1;
+ }
+ return 0;
+ }
+ return 0;
+}
+
+
+static int wps_validate_e_hash2(const u8 *hash, int mandatory)
+{
+ if (hash == NULL) {
+ if (mandatory) {
+ wpa_printf(MSG_INFO, "WPS-STRICT: E-Hash2 "
+ "attribute missing");
+ return -1;
+ }
+ return 0;
+ }
+ return 0;
+}
+
+
+static int wps_validate_r_hash1(const u8 *hash, int mandatory)
+{
+ if (hash == NULL) {
+ if (mandatory) {
+ wpa_printf(MSG_INFO, "WPS-STRICT: R-Hash1 "
+ "attribute missing");
+ return -1;
+ }
+ return 0;
+ }
+ return 0;
+}
+
+
+static int wps_validate_r_hash2(const u8 *hash, int mandatory)
+{
+ if (hash == NULL) {
+ if (mandatory) {
+ wpa_printf(MSG_INFO, "WPS-STRICT: R-Hash2 "
+ "attribute missing");
+ return -1;
+ }
+ return 0;
+ }
+ return 0;
+}
+
+
+static int wps_validate_encr_settings(const u8 *encr_settings, size_t len,
+ int mandatory)
+{
+ if (encr_settings == NULL) {
+ if (mandatory) {
+ wpa_printf(MSG_INFO, "WPS-STRICT: Encrypted Settings "
+ "attribute missing");
+ return -1;
+ }
+ return 0;
+ }
+ if (len < 16) {
+ wpa_printf(MSG_INFO, "WPS-STRICT: Invalid Encrypted Settings "
+ "attribute length %d", (int) len);
+ return -1;
+ }
+ return 0;
+}
+
+
+static int wps_validate_settings_delay_time(const u8 *delay, int mandatory)
+{
+ if (delay == NULL) {
+ if (mandatory) {
+ wpa_printf(MSG_INFO, "WPS-STRICT: Settings Delay Time "
+ "attribute missing");
+ return -1;
+ }
+ return 0;
+ }
+ return 0;
+}
+
+
+static int wps_validate_r_snonce1(const u8 *nonce, int mandatory)
+{
+ if (nonce == NULL) {
+ if (mandatory) {
+ wpa_printf(MSG_INFO, "WPS-STRICT: R-SNonce1 "
+ "attribute missing");
+ return -1;
+ }
+ return 0;
+ }
+ return 0;
+}
+
+
+static int wps_validate_r_snonce2(const u8 *nonce, int mandatory)
+{
+ if (nonce == NULL) {
+ if (mandatory) {
+ wpa_printf(MSG_INFO, "WPS-STRICT: R-SNonce2 "
+ "attribute missing");
+ return -1;
+ }
+ return 0;
+ }
+ return 0;
+}
+
+
+static int wps_validate_e_snonce1(const u8 *nonce, int mandatory)
+{
+ if (nonce == NULL) {
+ if (mandatory) {
+ wpa_printf(MSG_INFO, "WPS-STRICT: E-SNonce1 "
+ "attribute missing");
+ return -1;
+ }
+ return 0;
+ }
+ return 0;
+}
+
+
+static int wps_validate_e_snonce2(const u8 *nonce, int mandatory)
+{
+ if (nonce == NULL) {
+ if (mandatory) {
+ wpa_printf(MSG_INFO, "WPS-STRICT: E-SNonce2 "
+ "attribute missing");
+ return -1;
+ }
+ return 0;
+ }
+ return 0;
+}
+
+
+static int wps_validate_key_wrap_auth(const u8 *auth, int mandatory)
+{
+ if (auth == NULL) {
+ if (mandatory) {
+ wpa_printf(MSG_INFO, "WPS-STRICT: Key Wrap "
+ "Authenticator attribute missing");
+ return -1;
+ }
+ return 0;
+ }
+ return 0;
+}
+
+
+static int wps_validate_ssid(const u8 *ssid, size_t ssid_len, int mandatory)
+{
+ if (ssid == NULL) {
+ if (mandatory) {
+ wpa_printf(MSG_INFO, "WPS-STRICT: SSID "
+ "attribute missing");
+ return -1;
+ }
+ return 0;
+ }
+ if (ssid_len == 0 || ssid[ssid_len - 1] == 0) {
+ wpa_hexdump_ascii(MSG_INFO, "WPS-STRICT: Invalid SSID "
+ "attribute value", ssid, ssid_len);
+ return -1;
+ }
+ return 0;
+}
+
+
+static int wps_validate_network_key_index(const u8 *idx, int mandatory)
+{
+ if (idx == NULL) {
+ if (mandatory) {
+ wpa_printf(MSG_INFO, "WPS-STRICT: Network Key Index "
+ "attribute missing");
+ return -1;
+ }
+ return 0;
+ }
+ return 0;
+}
+
+
+static int wps_validate_network_idx(const u8 *idx, int mandatory)
+{
+ if (idx == NULL) {
+ if (mandatory) {
+ wpa_printf(MSG_INFO, "WPS-STRICT: Network Index "
+ "attribute missing");
+ return -1;
+ }
+ return 0;
+ }
+ return 0;
+}
+
+
+static int wps_validate_network_key(const u8 *key, size_t key_len,
+ const u8 *encr_type, int mandatory)
+{
+ if (key == NULL) {
+ if (mandatory) {
+ wpa_printf(MSG_INFO, "WPS-STRICT: Network Key "
+ "attribute missing");
+ return -1;
+ }
+ return 0;
+ }
+ if (((encr_type == NULL || WPA_GET_BE16(encr_type) != WPS_ENCR_WEP) &&
+ key_len > 8 && key_len < 64 && key[key_len - 1] == 0) ||
+ key_len > 64) {
+ wpa_hexdump_ascii_key(MSG_INFO, "WPS-STRICT: Invalid Network "
+ "Key attribute value", key, key_len);
+ return -1;
+ }
+ return 0;
+}
+
+
+static int wps_validate_network_key_shareable(const u8 *val, int mandatory)
+{
+ if (val == NULL) {
+ if (mandatory) {
+ wpa_printf(MSG_INFO, "WPS-STRICT: Network Key "
+ "Shareable attribute missing");
+ return -1;
+ }
+ return 0;
+ }
+ if (*val > 1) {
+ wpa_printf(MSG_INFO, "WPS-STRICT: Invalid Network Key "
+ "Shareable attribute value 0x%x", *val);
+ return -1;
+ }
+ return 0;
+}
+
+
+static int wps_validate_cred(const u8 *cred, size_t len)
+{
+ struct wps_parse_attr attr;
+ struct wpabuf buf;
+
+ if (cred == NULL)
+ return -1;
+ wpabuf_set(&buf, cred, len);
+ if (wps_parse_msg(&buf, &attr) < 0) {
+ wpa_printf(MSG_INFO, "WPS-STRICT: Failed to parse Credential");
+ return -1;
+ }
+
+ if (wps_validate_network_idx(attr.network_idx, 1) ||
+ wps_validate_ssid(attr.ssid, attr.ssid_len, 1) ||
+ wps_validate_auth_type(attr.auth_type, 1) ||
+ wps_validate_encr_type(attr.encr_type, 1) ||
+ wps_validate_network_key_index(attr.network_key_idx, 0) ||
+ wps_validate_network_key(attr.network_key, attr.network_key_len,
+ attr.encr_type, 1) ||
+ wps_validate_mac_addr(attr.mac_addr, 1) ||
+ wps_validate_network_key_shareable(attr.network_key_shareable, 0))
+ {
+ wpa_printf(MSG_INFO, "WPS-STRICT: Invalid Credential");
+ return -1;
+ }
+
+
+ return 0;
+}
+
+
+static int wps_validate_credential(const u8 *cred[], size_t len[], size_t num,
+ int mandatory)
+{
+ size_t i;
+
+ if (num == 0) {
+ if (mandatory) {
+ wpa_printf(MSG_INFO, "WPS-STRICT: Credential "
+ "attribute missing");
+ return -1;
+ }
+ return 0;
+ }
+
+ for (i = 0; i < num; i++) {
+ if (wps_validate_cred(cred[i], len[i]) < 0)
+ return -1;
+ }
+
+ return 0;
+}
+
+
+int wps_validate_beacon(const struct wpabuf *wps_ie)
+{
+ struct wps_parse_attr attr;
+ int wps2, sel_reg;
+
+ if (wps_ie == NULL) {
+ wpa_printf(MSG_INFO, "WPS-STRICT: No WPS IE in Beacon frame");
+ return -1;
+ }
+ if (wps_parse_msg(wps_ie, &attr) < 0) {
+ wpa_printf(MSG_INFO, "WPS-STRICT: Failed to parse WPS IE in "
+ "Beacon frame");
+ return -1;
+ }
+
+ wps2 = attr.version2 != NULL;
+ sel_reg = attr.selected_registrar != NULL &&
+ *attr.selected_registrar != 0;
+ if (wps_validate_version(attr.version, 1) ||
+ wps_validate_wps_state(attr.wps_state, 1) ||
+ wps_validate_ap_setup_locked(attr.ap_setup_locked, 0) ||
+ wps_validate_selected_registrar(attr.selected_registrar, 0) ||
+ wps_validate_dev_password_id(attr.dev_password_id, sel_reg) ||
+ wps_validate_sel_reg_config_methods(attr.sel_reg_config_methods,
+ wps2, sel_reg) ||
+ wps_validate_uuid_e(attr.uuid_e, 0) ||
+ wps_validate_rf_bands(attr.rf_bands, 0) ||
+ wps_validate_version2(attr.version2, wps2) ||
+ wps_validate_authorized_macs(attr.authorized_macs,
+ attr.authorized_macs_len, 0)) {
+ wpa_printf(MSG_INFO, "WPS-STRICT: Invalid Beacon frame");
+ return -1;
+ }
+
+ return 0;
+}
+
+
+int wps_validate_beacon_probe_resp(const struct wpabuf *wps_ie, int probe,
+ const u8 *addr)
+{
+ struct wps_parse_attr attr;
+ int wps2, sel_reg;
+
+ if (wps_ie == NULL) {
+ wpa_printf(MSG_INFO, "WPS-STRICT: No WPS IE in "
+ "%sProbe Response frame", probe ? "" : "Beacon/");
+ return -1;
+ }
+ if (wps_parse_msg(wps_ie, &attr) < 0) {
+ wpa_printf(MSG_INFO, "WPS-STRICT: Failed to parse WPS IE in "
+ "%sProbe Response frame", probe ? "" : "Beacon/");
+ return -1;
+ }
+
+ wps2 = attr.version2 != NULL;
+ sel_reg = attr.selected_registrar != NULL &&
+ *attr.selected_registrar != 0;
+ if (wps_validate_version(attr.version, 1) ||
+ wps_validate_wps_state(attr.wps_state, 1) ||
+ wps_validate_ap_setup_locked(attr.ap_setup_locked, 0) ||
+ wps_validate_selected_registrar(attr.selected_registrar, 0) ||
+ wps_validate_dev_password_id(attr.dev_password_id, sel_reg) ||
+ wps_validate_sel_reg_config_methods(attr.sel_reg_config_methods,
+ wps2, sel_reg) ||
+ wps_validate_response_type(attr.response_type, probe) ||
+ wps_validate_uuid_e(attr.uuid_e, probe) ||
+ wps_validate_manufacturer(attr.manufacturer, attr.manufacturer_len,
+ probe) ||
+ wps_validate_model_name(attr.model_name, attr.model_name_len,
+ probe) ||
+ wps_validate_model_number(attr.model_number, attr.model_number_len,
+ probe) ||
+ wps_validate_serial_number(attr.serial_number,
+ attr.serial_number_len, probe) ||
+ wps_validate_primary_dev_type(attr.primary_dev_type, probe) ||
+ wps_validate_dev_name(attr.dev_name, attr.dev_name_len, probe) ||
+ wps_validate_ap_config_methods(attr.config_methods, wps2, probe) ||
+ wps_validate_rf_bands(attr.rf_bands, 0) ||
+ wps_validate_version2(attr.version2, wps2) ||
+ wps_validate_authorized_macs(attr.authorized_macs,
+ attr.authorized_macs_len, 0)) {
+ wpa_printf(MSG_INFO, "WPS-STRICT: Invalid %sProbe Response "
+ "frame from " MACSTR, probe ? "" : "Beacon/",
+ MAC2STR(addr));
+#ifdef WPS_STRICT_WPS2
+ if (wps2)
+ return -1;
+#else /* WPS_STRICT_WPS2 */
+ return -1;
+#endif /* WPS_STRICT_WPS2 */
+ }
+
+ return 0;
+}
+
+
+int wps_validate_probe_req(const struct wpabuf *wps_ie, const u8 *addr)
+{
+ struct wps_parse_attr attr;
+ int wps2;
+
+ if (wps_ie == NULL) {
+ wpa_printf(MSG_INFO, "WPS-STRICT: No WPS IE in "
+ "Probe Request frame");
+ return -1;
+ }
+ if (wps_parse_msg(wps_ie, &attr) < 0) {
+ wpa_printf(MSG_INFO, "WPS-STRICT: Failed to parse WPS IE in "
+ "Probe Request frame");
+ return -1;
+ }
+
+ wps2 = attr.version2 != NULL;
+ if (wps_validate_version(attr.version, 1) ||
+ wps_validate_request_type(attr.request_type, 1) ||
+ wps_validate_config_methods(attr.config_methods, wps2, 1) ||
+ wps_validate_uuid_e(attr.uuid_e, attr.uuid_r == NULL) ||
+ wps_validate_uuid_r(attr.uuid_r, attr.uuid_e == NULL) ||
+ wps_validate_primary_dev_type(attr.primary_dev_type, 1) ||
+ wps_validate_rf_bands(attr.rf_bands, 1) ||
+ wps_validate_assoc_state(attr.assoc_state, 1) ||
+ wps_validate_config_error(attr.config_error, 1) ||
+ wps_validate_dev_password_id(attr.dev_password_id, 1) ||
+ wps_validate_version2(attr.version2, wps2) ||
+ wps_validate_manufacturer(attr.manufacturer, attr.manufacturer_len,
+ wps2) ||
+ wps_validate_model_name(attr.model_name, attr.model_name_len,
+ wps2) ||
+ wps_validate_model_number(attr.model_number, attr.model_number_len,
+ wps2) ||
+ wps_validate_dev_name(attr.dev_name, attr.dev_name_len, wps2) ||
+ wps_validate_request_to_enroll(attr.request_to_enroll, 0) ||
+ wps_validate_req_dev_type(attr.req_dev_type, attr.num_req_dev_type,
+ 0)) {
+ wpa_printf(MSG_INFO, "WPS-STRICT: Invalid Probe Request "
+ "frame from " MACSTR, MAC2STR(addr));
+ return -1;
+ }
+
+ return 0;
+}
+
+
+int wps_validate_assoc_req(const struct wpabuf *wps_ie)
+{
+ struct wps_parse_attr attr;
+ int wps2;
+
+ if (wps_ie == NULL) {
+ wpa_printf(MSG_INFO, "WPS-STRICT: No WPS IE in "
+ "(Re)Association Request frame");
+ return -1;
+ }
+ if (wps_parse_msg(wps_ie, &attr) < 0) {
+ wpa_printf(MSG_INFO, "WPS-STRICT: Failed to parse WPS IE in "
+ "(Re)Association Request frame");
+ return -1;
+ }
+
+ wps2 = attr.version2 != NULL;
+ if (wps_validate_version(attr.version, 1) ||
+ wps_validate_request_type(attr.request_type, 1) ||
+ wps_validate_version2(attr.version2, wps2)) {
+ wpa_printf(MSG_INFO, "WPS-STRICT: Invalid (Re)Association "
+ "Request frame");
+ return -1;
+ }
+
+ return 0;
+}
+
+
+int wps_validate_assoc_resp(const struct wpabuf *wps_ie)
+{
+ struct wps_parse_attr attr;
+ int wps2;
+
+ if (wps_ie == NULL) {
+ wpa_printf(MSG_INFO, "WPS-STRICT: No WPS IE in "
+ "(Re)Association Response frame");
+ return -1;
+ }
+ if (wps_parse_msg(wps_ie, &attr) < 0) {
+ wpa_printf(MSG_INFO, "WPS-STRICT: Failed to parse WPS IE in "
+ "(Re)Association Response frame");
+ return -1;
+ }
+
+ wps2 = attr.version2 != NULL;
+ if (wps_validate_version(attr.version, 1) ||
+ wps_validate_response_type(attr.response_type, 1) ||
+ wps_validate_version2(attr.version2, wps2)) {
+ wpa_printf(MSG_INFO, "WPS-STRICT: Invalid (Re)Association "
+ "Response frame");
+ return -1;
+ }
+
+ return 0;
+}
+
+
+int wps_validate_m1(const struct wpabuf *tlvs)
+{
+ struct wps_parse_attr attr;
+ int wps2;
+
+ if (tlvs == NULL) {
+ wpa_printf(MSG_INFO, "WPS-STRICT: No TLVs in M1");
+ return -1;
+ }
+ if (wps_parse_msg(tlvs, &attr) < 0) {
+ wpa_printf(MSG_INFO, "WPS-STRICT: Failed to parse attributes "
+ "in M1");
+ return -1;
+ }
+
+ wps2 = attr.version2 != NULL;
+ if (wps_validate_version(attr.version, 1) ||
+ wps_validate_msg_type(attr.msg_type, 1) ||
+ wps_validate_uuid_e(attr.uuid_e, 1) ||
+ wps_validate_mac_addr(attr.mac_addr, 1) ||
+ wps_validate_enrollee_nonce(attr.enrollee_nonce, 1) ||
+ wps_validate_public_key(attr.public_key, attr.public_key_len, 1) ||
+ wps_validate_auth_type_flags(attr.auth_type_flags, 1) ||
+ wps_validate_encr_type_flags(attr.encr_type_flags, 1) ||
+ wps_validate_conn_type_flags(attr.conn_type_flags, 1) ||
+ wps_validate_config_methods(attr.config_methods, wps2, 1) ||
+ wps_validate_wps_state(attr.wps_state, 1) ||
+ wps_validate_manufacturer(attr.manufacturer, attr.manufacturer_len,
+ 1) ||
+ wps_validate_model_name(attr.model_name, attr.model_name_len, 1) ||
+ wps_validate_model_number(attr.model_number, attr.model_number_len,
+ 1) ||
+ wps_validate_serial_number(attr.serial_number,
+ attr.serial_number_len, 1) ||
+ wps_validate_primary_dev_type(attr.primary_dev_type, 1) ||
+ wps_validate_dev_name(attr.dev_name, attr.dev_name_len, 1) ||
+ wps_validate_rf_bands(attr.rf_bands, 1) ||
+ wps_validate_assoc_state(attr.assoc_state, 1) ||
+ wps_validate_dev_password_id(attr.dev_password_id, 1) ||
+ wps_validate_config_error(attr.config_error, 1) ||
+ wps_validate_os_version(attr.os_version, 1) ||
+ wps_validate_version2(attr.version2, wps2) ||
+ wps_validate_request_to_enroll(attr.request_to_enroll, 0)) {
+ wpa_printf(MSG_INFO, "WPS-STRICT: Invalid M1");
+#ifdef WPS_STRICT_WPS2
+ if (wps2)
+ return -1;
+#else /* WPS_STRICT_WPS2 */
+ return -1;
+#endif /* WPS_STRICT_WPS2 */
+ }
+
+ return 0;
+}
+
+
+int wps_validate_m2(const struct wpabuf *tlvs)
+{
+ struct wps_parse_attr attr;
+ int wps2;
+
+ if (tlvs == NULL) {
+ wpa_printf(MSG_INFO, "WPS-STRICT: No TLVs in M2");
+ return -1;
+ }
+ if (wps_parse_msg(tlvs, &attr) < 0) {
+ wpa_printf(MSG_INFO, "WPS-STRICT: Failed to parse attributes "
+ "in M2");
+ return -1;
+ }
+
+ wps2 = attr.version2 != NULL;
+ if (wps_validate_version(attr.version, 1) ||
+ wps_validate_msg_type(attr.msg_type, 1) ||
+ wps_validate_enrollee_nonce(attr.enrollee_nonce, 1) ||
+ wps_validate_registrar_nonce(attr.registrar_nonce, 1) ||
+ wps_validate_uuid_r(attr.uuid_r, 1) ||
+ wps_validate_public_key(attr.public_key, attr.public_key_len, 1) ||
+ wps_validate_auth_type_flags(attr.auth_type_flags, 1) ||
+ wps_validate_encr_type_flags(attr.encr_type_flags, 1) ||
+ wps_validate_conn_type_flags(attr.conn_type_flags, 1) ||
+ wps_validate_config_methods(attr.config_methods, wps2, 1) ||
+ wps_validate_manufacturer(attr.manufacturer, attr.manufacturer_len,
+ 1) ||
+ wps_validate_model_name(attr.model_name, attr.model_name_len, 1) ||
+ wps_validate_model_number(attr.model_number, attr.model_number_len,
+ 1) ||
+ wps_validate_serial_number(attr.serial_number,
+ attr.serial_number_len, 1) ||
+ wps_validate_primary_dev_type(attr.primary_dev_type, 1) ||
+ wps_validate_dev_name(attr.dev_name, attr.dev_name_len, 1) ||
+ wps_validate_rf_bands(attr.rf_bands, 1) ||
+ wps_validate_assoc_state(attr.assoc_state, 1) ||
+ wps_validate_config_error(attr.config_error, 1) ||
+ wps_validate_dev_password_id(attr.dev_password_id, 1) ||
+ wps_validate_os_version(attr.os_version, 1) ||
+ wps_validate_version2(attr.version2, wps2) ||
+ wps_validate_authenticator(attr.authenticator, 1)) {
+ wpa_printf(MSG_INFO, "WPS-STRICT: Invalid M2");
+#ifdef WPS_STRICT_WPS2
+ if (wps2)
+ return -1;
+#else /* WPS_STRICT_WPS2 */
+ return -1;
+#endif /* WPS_STRICT_WPS2 */
+ }
+
+ return 0;
+}
+
+
+int wps_validate_m2d(const struct wpabuf *tlvs)
+{
+ struct wps_parse_attr attr;
+ int wps2;
+
+ if (tlvs == NULL) {
+ wpa_printf(MSG_INFO, "WPS-STRICT: No TLVs in M2D");
+ return -1;
+ }
+ if (wps_parse_msg(tlvs, &attr) < 0) {
+ wpa_printf(MSG_INFO, "WPS-STRICT: Failed to parse attributes "
+ "in M2D");
+ return -1;
+ }
+
+ wps2 = attr.version2 != NULL;
+ if (wps_validate_version(attr.version, 1) ||
+ wps_validate_msg_type(attr.msg_type, 1) ||
+ wps_validate_enrollee_nonce(attr.enrollee_nonce, 1) ||
+ wps_validate_registrar_nonce(attr.registrar_nonce, 1) ||
+ wps_validate_uuid_r(attr.uuid_r, 1) ||
+ wps_validate_auth_type_flags(attr.auth_type_flags, 1) ||
+ wps_validate_encr_type_flags(attr.encr_type_flags, 1) ||
+ wps_validate_conn_type_flags(attr.conn_type_flags, 1) ||
+ wps_validate_config_methods(attr.config_methods, wps2, 1) ||
+ wps_validate_manufacturer(attr.manufacturer, attr.manufacturer_len,
+ 1) ||
+ wps_validate_model_name(attr.model_name, attr.model_name_len, 1) ||
+ wps_validate_model_number(attr.model_number, attr.model_number_len,
+ 1) ||
+ wps_validate_serial_number(attr.serial_number,
+ attr.serial_number_len, 1) ||
+ wps_validate_primary_dev_type(attr.primary_dev_type, 1) ||
+ wps_validate_dev_name(attr.dev_name, attr.dev_name_len, 1) ||
+ wps_validate_rf_bands(attr.rf_bands, 1) ||
+ wps_validate_assoc_state(attr.assoc_state, 1) ||
+ wps_validate_config_error(attr.config_error, 1) ||
+ wps_validate_os_version(attr.os_version, 1) ||
+ wps_validate_version2(attr.version2, wps2)) {
+ wpa_printf(MSG_INFO, "WPS-STRICT: Invalid M2D");
+#ifdef WPS_STRICT_WPS2
+ if (wps2)
+ return -1;
+#else /* WPS_STRICT_WPS2 */
+ return -1;
+#endif /* WPS_STRICT_WPS2 */
+ }
+
+ return 0;
+}
+
+
+int wps_validate_m3(const struct wpabuf *tlvs)
+{
+ struct wps_parse_attr attr;
+ int wps2;
+
+ if (tlvs == NULL) {
+ wpa_printf(MSG_INFO, "WPS-STRICT: No TLVs in M3");
+ return -1;
+ }
+ if (wps_parse_msg(tlvs, &attr) < 0) {
+ wpa_printf(MSG_INFO, "WPS-STRICT: Failed to parse attributes "
+ "in M3");
+ return -1;
+ }
+
+ wps2 = attr.version2 != NULL;
+ if (wps_validate_version(attr.version, 1) ||
+ wps_validate_msg_type(attr.msg_type, 1) ||
+ wps_validate_registrar_nonce(attr.registrar_nonce, 1) ||
+ wps_validate_e_hash1(attr.e_hash1, 1) ||
+ wps_validate_e_hash2(attr.e_hash2, 1) ||
+ wps_validate_version2(attr.version2, wps2) ||
+ wps_validate_authenticator(attr.authenticator, 1)) {
+ wpa_printf(MSG_INFO, "WPS-STRICT: Invalid M3");
+#ifdef WPS_STRICT_WPS2
+ if (wps2)
+ return -1;
+#else /* WPS_STRICT_WPS2 */
+ return -1;
+#endif /* WPS_STRICT_WPS2 */
+ }
+
+ return 0;
+}
+
+
+int wps_validate_m4(const struct wpabuf *tlvs)
+{
+ struct wps_parse_attr attr;
+ int wps2;
+
+ if (tlvs == NULL) {
+ wpa_printf(MSG_INFO, "WPS-STRICT: No TLVs in M4");
+ return -1;
+ }
+ if (wps_parse_msg(tlvs, &attr) < 0) {
+ wpa_printf(MSG_INFO, "WPS-STRICT: Failed to parse attributes "
+ "in M4");
+ return -1;
+ }
+
+ wps2 = attr.version2 != NULL;
+ if (wps_validate_version(attr.version, 1) ||
+ wps_validate_msg_type(attr.msg_type, 1) ||
+ wps_validate_enrollee_nonce(attr.enrollee_nonce, 1) ||
+ wps_validate_r_hash1(attr.r_hash1, 1) ||
+ wps_validate_r_hash2(attr.r_hash2, 1) ||
+ wps_validate_encr_settings(attr.encr_settings,
+ attr.encr_settings_len, 1) ||
+ wps_validate_version2(attr.version2, wps2) ||
+ wps_validate_authenticator(attr.authenticator, 1)) {
+ wpa_printf(MSG_INFO, "WPS-STRICT: Invalid M4");
+#ifdef WPS_STRICT_WPS2
+ if (wps2)
+ return -1;
+#else /* WPS_STRICT_WPS2 */
+ return -1;
+#endif /* WPS_STRICT_WPS2 */
+ }
+
+ return 0;
+}
+
+
+int wps_validate_m4_encr(const struct wpabuf *tlvs, int wps2)
+{
+ struct wps_parse_attr attr;
+
+ if (tlvs == NULL) {
+ wpa_printf(MSG_INFO, "WPS-STRICT: No TLVs in M4 encrypted "
+ "settings");
+ return -1;
+ }
+ if (wps_parse_msg(tlvs, &attr) < 0) {
+ wpa_printf(MSG_INFO, "WPS-STRICT: Failed to parse attributes "
+ "in M4 encrypted settings");
+ return -1;
+ }
+
+ if (wps_validate_r_snonce1(attr.r_snonce1, 1) ||
+ wps_validate_key_wrap_auth(attr.key_wrap_auth, 1)) {
+ wpa_printf(MSG_INFO, "WPS-STRICT: Invalid M4 encrypted "
+ "settings");
+#ifdef WPS_STRICT_WPS2
+ if (wps2)
+ return -1;
+#else /* WPS_STRICT_WPS2 */
+ return -1;
+#endif /* WPS_STRICT_WPS2 */
+ }
+
+ return 0;
+}
+
+
+int wps_validate_m5(const struct wpabuf *tlvs)
+{
+ struct wps_parse_attr attr;
+ int wps2;
+
+ if (tlvs == NULL) {
+ wpa_printf(MSG_INFO, "WPS-STRICT: No TLVs in M5");
+ return -1;
+ }
+ if (wps_parse_msg(tlvs, &attr) < 0) {
+ wpa_printf(MSG_INFO, "WPS-STRICT: Failed to parse attributes "
+ "in M5");
+ return -1;
+ }
+
+ wps2 = attr.version2 != NULL;
+ if (wps_validate_version(attr.version, 1) ||
+ wps_validate_msg_type(attr.msg_type, 1) ||
+ wps_validate_registrar_nonce(attr.registrar_nonce, 1) ||
+ wps_validate_encr_settings(attr.encr_settings,
+ attr.encr_settings_len, 1) ||
+ wps_validate_version2(attr.version2, wps2) ||
+ wps_validate_authenticator(attr.authenticator, 1)) {
+ wpa_printf(MSG_INFO, "WPS-STRICT: Invalid M5");
+#ifdef WPS_STRICT_WPS2
+ if (wps2)
+ return -1;
+#else /* WPS_STRICT_WPS2 */
+ return -1;
+#endif /* WPS_STRICT_WPS2 */
+ }
+
+ return 0;
+}
+
+
+int wps_validate_m5_encr(const struct wpabuf *tlvs, int wps2)
+{
+ struct wps_parse_attr attr;
+
+ if (tlvs == NULL) {
+ wpa_printf(MSG_INFO, "WPS-STRICT: No TLVs in M5 encrypted "
+ "settings");
+ return -1;
+ }
+ if (wps_parse_msg(tlvs, &attr) < 0) {
+ wpa_printf(MSG_INFO, "WPS-STRICT: Failed to parse attributes "
+ "in M5 encrypted settings");
+ return -1;
+ }
+
+ if (wps_validate_e_snonce1(attr.e_snonce1, 1) ||
+ wps_validate_key_wrap_auth(attr.key_wrap_auth, 1)) {
+ wpa_printf(MSG_INFO, "WPS-STRICT: Invalid M5 encrypted "
+ "settings");
+#ifdef WPS_STRICT_WPS2
+ if (wps2)
+ return -1;
+#else /* WPS_STRICT_WPS2 */
+ return -1;
+#endif /* WPS_STRICT_WPS2 */
+ }
+
+ return 0;
+}
+
+
+int wps_validate_m6(const struct wpabuf *tlvs)
+{
+ struct wps_parse_attr attr;
+ int wps2;
+
+ if (tlvs == NULL) {
+ wpa_printf(MSG_INFO, "WPS-STRICT: No TLVs in M6");
+ return -1;
+ }
+ if (wps_parse_msg(tlvs, &attr) < 0) {
+ wpa_printf(MSG_INFO, "WPS-STRICT: Failed to parse attributes "
+ "in M6");
+ return -1;
+ }
+
+ wps2 = attr.version2 != NULL;
+ if (wps_validate_version(attr.version, 1) ||
+ wps_validate_msg_type(attr.msg_type, 1) ||
+ wps_validate_enrollee_nonce(attr.enrollee_nonce, 1) ||
+ wps_validate_encr_settings(attr.encr_settings,
+ attr.encr_settings_len, 1) ||
+ wps_validate_version2(attr.version2, wps2) ||
+ wps_validate_authenticator(attr.authenticator, 1)) {
+ wpa_printf(MSG_INFO, "WPS-STRICT: Invalid M6");
+#ifdef WPS_STRICT_WPS2
+ if (wps2)
+ return -1;
+#else /* WPS_STRICT_WPS2 */
+ return -1;
+#endif /* WPS_STRICT_WPS2 */
+ }
+
+ return 0;
+}
+
+
+int wps_validate_m6_encr(const struct wpabuf *tlvs, int wps2)
+{
+ struct wps_parse_attr attr;
+
+ if (tlvs == NULL) {
+ wpa_printf(MSG_INFO, "WPS-STRICT: No TLVs in M6 encrypted "
+ "settings");
+ return -1;
+ }
+ if (wps_parse_msg(tlvs, &attr) < 0) {
+ wpa_printf(MSG_INFO, "WPS-STRICT: Failed to parse attributes "
+ "in M6 encrypted settings");
+ return -1;
+ }
+
+ if (wps_validate_r_snonce2(attr.r_snonce2, 1) ||
+ wps_validate_key_wrap_auth(attr.key_wrap_auth, 1)) {
+ wpa_printf(MSG_INFO, "WPS-STRICT: Invalid M6 encrypted "
+ "settings");
+#ifdef WPS_STRICT_WPS2
+ if (wps2)
+ return -1;
+#else /* WPS_STRICT_WPS2 */
+ return -1;
+#endif /* WPS_STRICT_WPS2 */
+ }
+
+ return 0;
+}
+
+
+int wps_validate_m7(const struct wpabuf *tlvs)
+{
+ struct wps_parse_attr attr;
+ int wps2;
+
+ if (tlvs == NULL) {
+ wpa_printf(MSG_INFO, "WPS-STRICT: No TLVs in M7");
+ return -1;
+ }
+ if (wps_parse_msg(tlvs, &attr) < 0) {
+ wpa_printf(MSG_INFO, "WPS-STRICT: Failed to parse attributes "
+ "in M7");
+ return -1;
+ }
+
+ wps2 = attr.version2 != NULL;
+ if (wps_validate_version(attr.version, 1) ||
+ wps_validate_msg_type(attr.msg_type, 1) ||
+ wps_validate_registrar_nonce(attr.registrar_nonce, 1) ||
+ wps_validate_encr_settings(attr.encr_settings,
+ attr.encr_settings_len, 1) ||
+ wps_validate_settings_delay_time(attr.settings_delay_time, 0) ||
+ wps_validate_version2(attr.version2, wps2) ||
+ wps_validate_authenticator(attr.authenticator, 1)) {
+ wpa_printf(MSG_INFO, "WPS-STRICT: Invalid M7");
+#ifdef WPS_STRICT_WPS2
+ if (wps2)
+ return -1;
+#else /* WPS_STRICT_WPS2 */
+ return -1;
+#endif /* WPS_STRICT_WPS2 */
+ }
+
+ return 0;
+}
+
+
+int wps_validate_m7_encr(const struct wpabuf *tlvs, int ap, int wps2)
+{
+ struct wps_parse_attr attr;
+
+ if (tlvs == NULL) {
+ wpa_printf(MSG_INFO, "WPS-STRICT: No TLVs in M7 encrypted "
+ "settings");
+ return -1;
+ }
+ if (wps_parse_msg(tlvs, &attr) < 0) {
+ wpa_printf(MSG_INFO, "WPS-STRICT: Failed to parse attributes "
+ "in M7 encrypted settings");
+ return -1;
+ }
+
+ if (wps_validate_e_snonce2(attr.e_snonce2, 1) ||
+ wps_validate_ssid(attr.ssid, attr.ssid_len, !ap) ||
+ wps_validate_mac_addr(attr.mac_addr, !ap) ||
+ wps_validate_auth_type(attr.auth_type, !ap) ||
+ wps_validate_encr_type(attr.encr_type, !ap) ||
+ wps_validate_network_key_index(attr.network_key_idx, 0) ||
+ wps_validate_network_key(attr.network_key, attr.network_key_len,
+ attr.encr_type, !ap) ||
+ wps_validate_key_wrap_auth(attr.key_wrap_auth, 1)) {
+ wpa_printf(MSG_INFO, "WPS-STRICT: Invalid M7 encrypted "
+ "settings");
+#ifdef WPS_STRICT_WPS2
+ if (wps2)
+ return -1;
+#else /* WPS_STRICT_WPS2 */
+ return -1;
+#endif /* WPS_STRICT_WPS2 */
+ }
+
+ return 0;
+}
+
+
+int wps_validate_m8(const struct wpabuf *tlvs)
+{
+ struct wps_parse_attr attr;
+ int wps2;
+
+ if (tlvs == NULL) {
+ wpa_printf(MSG_INFO, "WPS-STRICT: No TLVs in M8");
+ return -1;
+ }
+ if (wps_parse_msg(tlvs, &attr) < 0) {
+ wpa_printf(MSG_INFO, "WPS-STRICT: Failed to parse attributes "
+ "in M8");
+ return -1;
+ }
+
+ wps2 = attr.version2 != NULL;
+ if (wps_validate_version(attr.version, 1) ||
+ wps_validate_msg_type(attr.msg_type, 1) ||
+ wps_validate_enrollee_nonce(attr.enrollee_nonce, 1) ||
+ wps_validate_encr_settings(attr.encr_settings,
+ attr.encr_settings_len, 1) ||
+ wps_validate_version2(attr.version2, wps2) ||
+ wps_validate_authenticator(attr.authenticator, 1)) {
+ wpa_printf(MSG_INFO, "WPS-STRICT: Invalid M8");
+#ifdef WPS_STRICT_WPS2
+ if (wps2)
+ return -1;
+#else /* WPS_STRICT_WPS2 */
+ return -1;
+#endif /* WPS_STRICT_WPS2 */
+ }
+
+ return 0;
+}
+
+
+int wps_validate_m8_encr(const struct wpabuf *tlvs, int ap, int wps2)
+{
+ struct wps_parse_attr attr;
+
+ if (tlvs == NULL) {
+ wpa_printf(MSG_INFO, "WPS-STRICT: No TLVs in M8 encrypted "
+ "settings");
+ return -1;
+ }
+ if (wps_parse_msg(tlvs, &attr) < 0) {
+ wpa_printf(MSG_INFO, "WPS-STRICT: Failed to parse attributes "
+ "in M8 encrypted settings");
+ return -1;
+ }
+
+ if (wps_validate_ssid(attr.ssid, attr.ssid_len, ap) ||
+ wps_validate_auth_type(attr.auth_type, ap) ||
+ wps_validate_encr_type(attr.encr_type, ap) ||
+ wps_validate_network_key_index(attr.network_key_idx, 0) ||
+ wps_validate_mac_addr(attr.mac_addr, ap) ||
+ wps_validate_credential(attr.cred, attr.cred_len, attr.num_cred,
+ !ap) ||
+ wps_validate_key_wrap_auth(attr.key_wrap_auth, 1)) {
+ wpa_printf(MSG_INFO, "WPS-STRICT: Invalid M8 encrypted "
+ "settings");
+#ifdef WPS_STRICT_WPS2
+ if (wps2)
+ return -1;
+#else /* WPS_STRICT_WPS2 */
+ return -1;
+#endif /* WPS_STRICT_WPS2 */
+ }
+
+ return 0;
+}
+
+
+int wps_validate_wsc_ack(const struct wpabuf *tlvs)
+{
+ struct wps_parse_attr attr;
+ int wps2;
+
+ if (tlvs == NULL) {
+ wpa_printf(MSG_INFO, "WPS-STRICT: No TLVs in WSC_ACK");
+ return -1;
+ }
+ if (wps_parse_msg(tlvs, &attr) < 0) {
+ wpa_printf(MSG_INFO, "WPS-STRICT: Failed to parse attributes "
+ "in WSC_ACK");
+ return -1;
+ }
+
+ wps2 = attr.version2 != NULL;
+ if (wps_validate_version(attr.version, 1) ||
+ wps_validate_msg_type(attr.msg_type, 1) ||
+ wps_validate_enrollee_nonce(attr.enrollee_nonce, 1) ||
+ wps_validate_registrar_nonce(attr.registrar_nonce, 1) ||
+ wps_validate_version2(attr.version2, wps2)) {
+ wpa_printf(MSG_INFO, "WPS-STRICT: Invalid WSC_ACK");
+#ifdef WPS_STRICT_WPS2
+ if (wps2)
+ return -1;
+#else /* WPS_STRICT_WPS2 */
+ return -1;
+#endif /* WPS_STRICT_WPS2 */
+ }
+
+ return 0;
+}
+
+
+int wps_validate_wsc_nack(const struct wpabuf *tlvs)
+{
+ struct wps_parse_attr attr;
+ int wps2;
+
+ if (tlvs == NULL) {
+ wpa_printf(MSG_INFO, "WPS-STRICT: No TLVs in WSC_NACK");
+ return -1;
+ }
+ if (wps_parse_msg(tlvs, &attr) < 0) {
+ wpa_printf(MSG_INFO, "WPS-STRICT: Failed to parse attributes "
+ "in WSC_NACK");
+ return -1;
+ }
+
+ wps2 = attr.version2 != NULL;
+ if (wps_validate_version(attr.version, 1) ||
+ wps_validate_msg_type(attr.msg_type, 1) ||
+ wps_validate_enrollee_nonce(attr.enrollee_nonce, 1) ||
+ wps_validate_registrar_nonce(attr.registrar_nonce, 1) ||
+ wps_validate_config_error(attr.config_error, 1) ||
+ wps_validate_version2(attr.version2, wps2)) {
+ wpa_printf(MSG_INFO, "WPS-STRICT: Invalid WSC_NACK");
+#ifdef WPS_STRICT_WPS2
+ if (wps2)
+ return -1;
+#else /* WPS_STRICT_WPS2 */
+ return -1;
+#endif /* WPS_STRICT_WPS2 */
+ }
+
+ return 0;
+}
+
+
+int wps_validate_wsc_done(const struct wpabuf *tlvs)
+{
+ struct wps_parse_attr attr;
+ int wps2;
+
+ if (tlvs == NULL) {
+ wpa_printf(MSG_INFO, "WPS-STRICT: No TLVs in WSC_Done");
+ return -1;
+ }
+ if (wps_parse_msg(tlvs, &attr) < 0) {
+ wpa_printf(MSG_INFO, "WPS-STRICT: Failed to parse attributes "
+ "in WSC_Done");
+ return -1;
+ }
+
+ wps2 = attr.version2 != NULL;
+ if (wps_validate_version(attr.version, 1) ||
+ wps_validate_msg_type(attr.msg_type, 1) ||
+ wps_validate_enrollee_nonce(attr.enrollee_nonce, 1) ||
+ wps_validate_registrar_nonce(attr.registrar_nonce, 1) ||
+ wps_validate_version2(attr.version2, wps2)) {
+ wpa_printf(MSG_INFO, "WPS-STRICT: Invalid WSC_Done");
+#ifdef WPS_STRICT_WPS2
+ if (wps2)
+ return -1;
+#else /* WPS_STRICT_WPS2 */
+ return -1;
+#endif /* WPS_STRICT_WPS2 */
+ }
+
+ return 0;
+}
+
+
+int wps_validate_upnp_set_selected_registrar(const struct wpabuf *tlvs)
+{
+ struct wps_parse_attr attr;
+ int wps2;
+ int sel_reg;
+
+ if (tlvs == NULL) {
+ wpa_printf(MSG_INFO, "WPS-STRICT: No TLVs in "
+ "SetSelectedRegistrar");
+ return -1;
+ }
+ if (wps_parse_msg(tlvs, &attr) < 0) {
+ wpa_printf(MSG_INFO, "WPS-STRICT: Failed to parse attributes "
+ "in SetSelectedRegistrar");
+ return -1;
+ }
+
+ wps2 = attr.version2 != NULL;
+ sel_reg = attr.selected_registrar != NULL &&
+ *attr.selected_registrar != 0;
+ if (wps_validate_version(attr.version, 1) ||
+ wps_validate_dev_password_id(attr.dev_password_id, sel_reg) ||
+ wps_validate_sel_reg_config_methods(attr.sel_reg_config_methods,
+ wps2, sel_reg) ||
+ wps_validate_version2(attr.version2, wps2) ||
+ wps_validate_authorized_macs(attr.authorized_macs,
+ attr.authorized_macs_len, wps2) ||
+ wps_validate_uuid_r(attr.uuid_r, wps2)) {
+ wpa_printf(MSG_INFO, "WPS-STRICT: Invalid "
+ "SetSelectedRegistrar");
+#ifdef WPS_STRICT_WPS2
+ if (wps2)
+ return -1;
+#else /* WPS_STRICT_WPS2 */
+ return -1;
+#endif /* WPS_STRICT_WPS2 */
+ }
+
+ return 0;
+}
diff --git a/contrib/wpa/wpa_supplicant/.gitignore b/contrib/wpa/wpa_supplicant/.gitignore
deleted file mode 100644
index e7e034c..0000000
--- a/contrib/wpa/wpa_supplicant/.gitignore
+++ /dev/null
@@ -1,8 +0,0 @@
-*.d
-.config
-eapol_test
-preauth_test
-wpa_cli
-wpa_passphrase
-wpa_supplicant
-wpa_priv
diff --git a/contrib/wpa/wpa_supplicant/ChangeLog b/contrib/wpa/wpa_supplicant/ChangeLog
index 56046c3..8abafb2 100644
--- a/contrib/wpa/wpa_supplicant/ChangeLog
+++ b/contrib/wpa/wpa_supplicant/ChangeLog
@@ -1,23 +1,408 @@
ChangeLog for wpa_supplicant
-2010-09-07 - v0.7.3
- * fixed fallback from failed PMKSA caching into full EAP authentication
- [Bug 355]
- * fixed issue with early D-Bus signals during initialization
- * fixed X.509 name handling in internal TLS
- * fixed WPS ER to use corrent Enrollee MAC Address in Credential
- * fixed scanning routines ot improve AP selection for WPS
- * added WPS workaround for open networks
- * fixed WPS Diffie-Hellman derivation to use correct public key length
- * fixed wpa_supplicant AP mode operations to ignore Supplicant and
- scan result events
- * improved SME operations with nl80211
- * fixed WPS ER event_id handling in some cases
- * fixed some issues with bgscan simple to avoid unnecessary scans
- * fixed issue with l2_packet_ndis overlapped writes corrupting stack
- [Bug 328]
- * updated WinPcap to the latest stable version 4.1.2 in Windows
- installer
+2013-01-12 - v2.0
+ * removed Qt3-based wpa_gui (obsoleted by wpa_qui-qt4)
+ * removed unmaintained driver wrappers broadcom, iphone, osx, ralink,
+ hostap, madwifi (hostap and madwifi remain available for hostapd;
+ their wpa_supplicant functionality is obsoleted by wext)
+ * improved debug logging (human readable event names, interface name
+ included in more entries)
+ * changed AP mode behavior to enable WPS only for open and
+ WPA/WPA2-Personal configuration
+ * improved P2P concurrency operations
+ - better coordination of concurrent scan and P2P search operations
+ - avoid concurrent remain-on-channel operation requests by canceling
+ previous operations prior to starting a new one
+ - reject operations that would require multi-channel concurrency if
+ the driver does not support it
+ - add parameter to select whether STA or P2P connection is preferred
+ if the driver cannot support both at the same time
+ - allow driver to indicate channel changes
+ - added optional delay=<search delay in milliseconds> parameter for
+ p2p_find to avoid taking all radio resources
+ - use 500 ms p2p_find search delay by default during concurrent
+ operations
+ - allow all channels in GO Negotiation if the driver supports
+ multi-channel concurrency
+ * added number of small changes to make it easier for static analyzers
+ to understand the implementation
+ * fixed number of small bugs (see git logs for more details)
+ * nl80211: number of updates to use new cfg80211/nl80211 functionality
+ - replace monitor interface with nl80211 commands for AP mode
+ - additional information for driver-based AP SME
+ - STA entry authorization in RSN IBSS
+ * EAP-pwd:
+ - fixed KDF for group 21 and zero-padding
+ - added support for fragmentation
+ - increased maximum number of hunting-and-pecking iterations
+ * avoid excessive Probe Response retries for broadcast Probe Request
+ frames (only with drivers using wpa_supplicant AP mode SME/MLME)
+ * added "GET country" ctrl_iface command
+ * do not save an invalid network block in wpa_supplicant.conf to avoid
+ problems reading the file on next start
+ * send STA connected/disconnected ctrl_iface events to both the P2P
+ group and parent interfaces
+ * added preliminary support for using TLS v1.2 (CONFIG_TLSV12=y)
+ * added "SET pno <1/0>" ctrl_iface command to start/stop preferred
+ network offload with sched_scan driver command
+ * merged in number of changes from Android repository for P2P, nl80211,
+ and build parameters
+ * changed P2P GO mode configuration to use driver capabilities to
+ automatically enable HT operations when supported
+ * added "wpa_cli status wps" command to fetch WPA2-Personal passhrase
+ for WPS use cases in AP mode
+ * EAP-AKA: keep pseudonym identity across EAP exchanges to match EAP-SIM
+ behavior
+ * improved reassociation behavior in cases where association is rejected
+ or when an AP disconnects us to handle common load balancing
+ mechanisms
+ - try to avoid extra scans when the needed information is available
+ * added optional "join" argument for p2p_prov_disc ctrl_iface command
+ * added group ifname to P2P-PROV-DISC-* events
+ * added P2P Device Address to AP-STA-DISCONNECTED event and use
+ p2p_dev_addr parameter name with AP-STA-CONNECTED
+ * added workarounds for WPS PBC overlap detection for some P2P use cases
+ where deployed stations work incorrectly
+ * optimize WPS connection speed by disconnecting prior to WPS scan and
+ by using single channel scans when AP channel is known
+ * PCSC and SIM/USIM improvements:
+ - accept 0x67 (Wrong length) as a response to READ RECORD to fix
+ issues with some USIM cards
+ - try to read MNC length from SIM/USIM
+ - build realm according to 3GPP TS 23.003 with identity from the SIM
+ - allow T1 protocol to be enabled
+ * added more WPS and P2P information available through D-Bus
+ * improve P2P negotiation robustness
+ - extra waits to get ACK frames through
+ - longer timeouts for cases where deployed devices have been
+ identified have issues meeting the specification requirements
+ - more retries for some P2P frames
+ - handle race conditions in GO Negotiation start by both devices
+ - ignore unexpected GO Negotiation Response frame
+ * added support for libnl 3.2 and newer
+ * added P2P persistent group info to P2P_PEER data
+ * maintain a list of P2P Clients for persistent group on GO
+ * AP: increased initial group key handshake retransmit timeout to 500 ms
+ * added optional dev_id parameter for p2p_find
+ * added P2P-FIND-STOPPED ctrl_iface event
+ * fixed issues in WPA/RSN element validation when roaming with ap_scan=1
+ and driver-based BSS selection
+ * do not expire P2P peer entries while connected with the peer in a
+ group
+ * fixed WSC element inclusion in cases where P2P is disabled
+ * AP: added a WPS workaround for mixed mode AP Settings with Windows 7
+ * EAP-SIM: fixed AT_COUNTER_TOO_SMALL use
+ * EAP-SIM/AKA: append realm to pseudonym identity
+ * EAP-SIM/AKA: store pseudonym identity in network configuration to
+ allow it to persist over multiple EAP sessions and wpa_supplicant
+ restarts
+ * EAP-AKA': updated to RFC 5448 (username prefixes changed); note: this
+ breaks interoperability with older versions
+ * added support for WFA Hotspot 2.0
+ - GAS/ANQP to fetch network information
+ - credential configuration and automatic network selections based on
+ credential match with ANQP information
+ * limited PMKSA cache entries to be used only with the network context
+ that was used to create them
+ * improved PMKSA cache expiration to avoid unnecessary disconnections
+ * adjusted bgscan_simple fast-scan backoff to avoid too frequent
+ background scans
+ * removed ctrl_iface event on P2P PD Response in join-group case
+ * added option to fetch BSS table entry based on P2P Device Address
+ ("BSS p2p_dev_addr=<P2P Device Address>")
+ * added BSS entry age to ctrl_iface BSS command output
+ * added optional MASK=0xH option for ctrl_iface BSS command to select
+ which fields are included in the response
+ * added optional RANGE=ALL|N1-N2 option for ctrl_iface BSS command to
+ fetch information about several BSSes in one call
+ * simplified licensing terms by selecting the BSD license as the only
+ alternative
+ * added "P2P_SET disallow_freq <freq list>" ctrl_iface command to
+ disable channels from P2P use
+ * added p2p_pref_chan configuration parameter to allow preferred P2P
+ channels to be specified
+ * added support for advertising immediate availability of a WPS
+ credential for P2P use cases
+ * optimized scan operations for P2P use cases (use single channel scan
+ for a specific SSID when possible)
+ * EAP-TTLS: fixed peer challenge generation for MSCHAPv2
+ * SME: do not use reassociation after explicit disconnection request
+ (local or a notification from an AP)
+ * added support for sending debug info to Linux tracing (-T on command
+ line)
+ * added support for using Deauthentication reason code 3 as an
+ indication of P2P group termination
+ * added wps_vendor_ext_m1 configuration parameter to allow vendor
+ specific attributes to be added to WPS M1
+ * started using separate TLS library context for tunneled TLS
+ (EAP-PEAP/TLS, EAP-TTLS/TLS, EAP-FAST/TLS) to support different CA
+ certificate configuration between Phase 1 and Phase 2
+ * added optional "auto" parameter for p2p_connect to request automatic
+ GO Negotiation vs. join-a-group selection
+ * added disabled_scan_offload parameter to disable automatic scan
+ offloading (sched_scan)
+ * added optional persistent=<network id> parameter for p2p_connect to
+ allow forcing of a specific SSID/passphrase for GO Negotiation
+ * added support for OBSS scan requests and 20/40 BSS coexistence reports
+ * reject PD Request for unknown group
+ * removed scripts and notes related to Windows binary releases (which
+ have not been used starting from 1.x)
+ * added initial support for WNM operations
+ - Keep-alive based on BSS max idle period
+ - WNM-Sleep Mode
+ - minimal BSS Transition Management processing
+ * added autoscan module to control scanning behavior while not connected
+ - autoscan_periodic and autoscan_exponential modules
+ * added new WPS NFC ctrl_iface mechanism
+ - added initial support NFC connection handover
+ - removed obsoleted WPS_OOB command (including support for deprecated
+ UFD config_method)
+ * added optional framework for external password storage ("ext:<name>")
+ * wpa_cli: added optional support for controlling wpa_supplicant
+ remotely over UDP (CONFIG_CTRL_IFACE=udp-remote) for testing purposes
+ * wpa_cli: extended tab completion to more commands
+ * changed SSID output to use printf-escaped strings instead of masking
+ of non-ASCII characters
+ - SSID can now be configured in the same format: ssid=P"abc\x00test"
+ * removed default ACM=1 from AC_VO and AC_VI
+ * added optional "ht40" argument for P2P ctrl_iface commands to allow
+ 40 MHz channels to be requested on the 5 GHz band
+ * added optional parameters for p2p_invite command to specify channel
+ when reinvoking a persistent group as the GO
+ * improved FIPS mode builds with OpenSSL
+ - "make fips" with CONFIG_FIPS=y to build wpa_supplicant with the
+ OpenSSL FIPS object module
+ - replace low level OpenSSL AES API calls to use EVP
+ - use OpenSSL keying material exporter when possible
+ - do not export TLS keys in FIPS mode
+ - remove MD5 from CONFIG_FIPS=y builds
+ - use OpenSSL function for PKBDF2 passphrase-to-PSK
+ - use OpenSSL HMAC implementation
+ - mix RAND_bytes() output into random_get_bytes() to force OpenSSL
+ DRBG to be used in FIPS mode
+ - use OpenSSL CMAC implementation
+ * added mechanism to disable TLS Session Ticket extension
+ - a workaround for servers that do not support TLS extensions that
+ was enabled by default in recent OpenSSL versions
+ - tls_disable_session_ticket=1
+ - automatically disable TLS Session Ticket extension by default when
+ using EAP-TLS/PEAP/TTLS (i.e., only use it with EAP-FAST)
+ * changed VENDOR-TEST EAP method to use proper private enterprise number
+ (this will not interoperate with older versions)
+ * disable network block temporarily on authentication failures
+ * improved WPS AP selection during WPS PIN iteration
+ * added support for configuring GCMP cipher for IEEE 802.11ad
+ * added support for Wi-Fi Display extensions
+ - WFD_SUBELEMENT_SET ctrl_iface command to configure WFD subelements
+ - SET wifi_display <0/1> to disable/enable WFD support
+ - WFD service discovery
+ - an external program is needed to manage the audio/video streaming
+ and codecs
+ * optimized scan result use for network selection
+ - use the internal BSS table instead of raw scan results
+ - allow unnecessary scans to be skipped if fresh information is
+ available (e.g., after GAS/ANQP round for Interworking)
+ * added support for 256-bit AES with internal TLS implementation
+ * allow peer to propose channel in P2P invitation process for a
+ persistent group
+ * added disallow_aps parameter to allow BSSIDs/SSIDs to be disallowed
+ from network selection
+ * re-enable the networks disabled during WPS operations
+ * allow P2P functionality to be disabled per interface (p2p_disabled=1)
+ * added secondary device types into P2P_PEER output
+ * added an option to disable use of a separate P2P group interface
+ (p2p_no_group_iface=1)
+ * fixed P2P Bonjour SD to match entries with both compressed and not
+ compressed domain name format and support multiple Bonjour PTR matches
+ for the same key
+ * use deauthentication instead of disassociation for all disconnection
+ operations; this removes the now unused disassociate() wpa_driver_ops
+ callback
+ * optimized PSK generation on P2P GO by caching results to avoid
+ multiple PBKDF2 operations
+ * added okc=1 global configuration parameter to allow OKC to be enabled
+ by default for all network blocks
+ * added a workaround for WPS PBC session overlap detection to avoid
+ interop issues with deployed station implementations that do not
+ remove active PBC indication from Probe Request frames properly
+ * added basic support for 60 GHz band
+ * extend EAPOL frames processing workaround for roaming cases
+ (postpone processing of unexpected EAPOL frame until association
+ event to handle reordered events)
+
+2012-05-10 - v1.0
+ * bsd: Add support for setting HT values in IFM_MMASK.
+ * Delay STA entry removal until Deauth/Disassoc TX status in AP mode.
+ This allows the driver to use PS buffering of Deauthentication and
+ Disassociation frames when the STA is in power save sleep. Only
+ available with drivers that provide TX status events for Deauth/
+ Disassoc frames (nl80211).
+ * Drop oldest unknown BSS table entries first. This makes it less
+ likely to hit connection issues in environments with huge number
+ of visible APs.
+ * Add systemd support.
+ * Add support for setting the syslog facility from the config file
+ at build time.
+ * atheros: Add support for IEEE 802.11w configuration.
+ * AP mode: Allow enable HT20 if driver supports it, by setting the
+ config parameter ieee80211n.
+ * Allow AP mode to disconnect STAs based on low ACK condition (when
+ the data connection is not working properly, e.g., due to the STA
+ going outside the range of the AP). Disabled by default, enable by
+ config option disassoc_low_ack.
+ * nl80211:
+ - Support GTK rekey offload.
+ - Support PMKSA candidate events. This adds support for RSN
+ pre-authentication with nl80211 interface and drivers that handle
+ roaming internally.
+ * dbus:
+ - Add a DBus signal for EAP SM requests, emitted on the Interface
+ object.
+ - Export max scan ssids supported by the driver as MaxScanSSID.
+ - Add signal Certification for information about server certification.
+ - Add BSSExpireAge and BSSExpireCount interface properties and
+ support set/get, which allows for setting BSS cache expiration age
+ and expiration scan count.
+ - Add ConfigFile to AddInterface properties.
+ - Add Interface.Country property and support to get/set the value.
+ - Add DBus property CurrentAuthMode.
+ - P2P DBus API added.
+ - Emit property changed events (for property BSSs) when adding/
+ removing BSSs.
+ - Treat '' in SSIDs of Interface.Scan as a request for broadcast
+ scan, instead of ignoring it.
+ - Add DBus getter/setter for FastReauth.
+ - Raise PropertiesChanged on org.freedesktop.DBus.Properties.
+ * wpa_cli:
+ - Send AP-STA-DISCONNECTED event when an AP disconnects a station
+ due to inactivity.
+ - Make second argument to set command optional. This can be used to
+ indicate a zero length value.
+ - Add signal_poll command.
+ - Add bss_expire_age and bss_expire_count commands to set/get BSS
+ cache expiration age and expiration scan count.
+ - Add ability to set scan interval (the time in seconds wpa_s waits
+ before requesting a new scan after failing to find a suitable
+ network in scan results) using scan_interval command.
+ - Add event CTRL-EVENT-ASSOC-REJECT for association rejected.
+ - Add command get version, that returns wpa_supplicant version string.
+ - Add command sta_autoconnect for disabling automatic reconnection
+ on receiving disconnection event.
+ - Setting bssid parameter to an empty string "" or any can now be
+ used to clear the bssid_set flag in a network block, i.e., to remove
+ bssid filtering.
+ - Add tdls_testing command to add a special testing feature for
+ changing TDLS behavior. Build param CONFIG_TDLS_TESTING must be
+ enabled as well.
+ - For interworking, add wpa_cli commands interworking_select,
+ interworking_connect, anqp_get, fetch_anqp, and stop_fetch_anqp.
+ - Many P2P commands were added. See README-P2P.
+ - Many WPS/WPS ER commands - see WPS/WPS ER sections for details.
+ - Allow set command to change global config parameters.
+ - Add log_level command, which can be used to display the current
+ debugging level and to change the log level during run time.
+ - Add note command, which can be used to insert notes to the debug
+ log.
+ - Add internal line edit implementation. CONFIG_WPA_CLI_EDIT=y
+ can now be used to build wpa_cli with internal implementation of
+ line editing and history support. This can be used as a replacement
+ for CONFIG_READLINE=y.
+ * AP mode: Add max_num_sta config option, which can be used to limit
+ the number of stations allowed to connect to the AP.
+ * Add WPA_IGNORE_CONFIG_ERRORS build option to continue in case of bad
+ config file.
+ * wext: Increase scan timeout from 5 to 10 seconds.
+ * Add blacklist command, allowing an external program to
+ manage the BSS blacklist and display its current contents.
+ * WPS:
+ - Add wpa_cli wps_pin get command for generating random PINs. This can
+ be used in a UI to generate a PIN without starting WPS (or P2P)
+ operation.
+ - Set RF bands based on driver capabilities, instead of hardcoding
+ them.
+ - Add mechanism for indicating non-standard WPS errors.
+ - Add CONFIG_WPS_REG_DISABLE_OPEN=y option to disable open networks
+ by default.
+ - Add wps_ap_pin cli command for wpa_supplicant AP mode.
+ - Add wps_check_pin cli command for processing PIN from user input.
+ UIs can use this command to process a PIN entered by a user and to
+ validate the checksum digit (if present).
+ - Cancel WPS operation on PBC session overlap detection.
+ - New wps_cancel command in wpa_cli will cancel a pending WPS
+ operation.
+ - wpa_cli action: Add WPS_EVENT_SUCCESS and WPS_EVENT_FAIL handlers.
+ - Trigger WPS config update on Manufacturer, Model Name, Model
+ Number, and Serial Number changes.
+ - Fragment size is now configurable for EAP-WSC peer. Use
+ wpa_cli set wps_fragment_size <val>.
+ - Disable AP PIN after 10 consecutive failures. Slow down attacks on
+ failures up to 10.
+ - Allow AP to start in Enrollee mode without AP PIN for probing, to
+ be compatible with Windows 7.
+ - Add Config Error into WPS-FAIL events to provide more info to the
+ user on how to resolve the issue.
+ - Label and Display config methods are not allowed to be enabled
+ at the same time, since it is unclear which PIN to use if both
+ methods are advertised.
+ - When controlling multiple interfaces:
+ - apply WPS commands to all interfaces configured to use WPS
+ - apply WPS config changes to all interfaces that use WPS
+ - when an attack is detected on any interface, disable AP PIN on
+ all interfaces
+ * WPS ER:
+ - Add special AP Setup Locked mode to allow read only ER.
+ ap_setup_locked=2 can now be used to enable a special mode where
+ WPS ER can learn the current AP settings, but cannot change them.
+ - Show SetSelectedRegistrar events as ctrl_iface events
+ - Add wps_er_set_config to enroll a network based on a local
+ network configuration block instead of having to (re-)learn the
+ current AP settings with wps_er_learn.
+ - Allow AP filtering based on IP address, add ctrl_iface event for
+ learned AP settings, add wps_er_config command to configure an AP.
+ * WPS 2.0: Add support for WPS 2.0 (CONFIG_WPS2)
+ - Add build option CONFIG_WPS_EXTENSIBILITY_TESTING to enable tool
+ for testing protocol extensibility.
+ - Add build option CONFIG_WPS_STRICT to allow disabling of WPS
+ workarounds.
+ - Add support for AuthorizedMACs attribute.
+ * TDLS:
+ - Propogate TDLS related nl80211 capability flags from kernel and
+ add them as driver capability flags. If the driver doesn't support
+ capabilities, assume TDLS is supported internally. When TDLS is
+ explicitly not supported, disable all user facing TDLS operations.
+ - Allow TDLS to be disabled at runtime (mostly for testing).
+ Use set tdls_disabled.
+ - Honor AP TDLS settings that prohibit/allow TDLS.
+ - Add a special testing feature for changing TDLS behavior. Use
+ CONFIG_TDLS_TESTING build param to enable. Configure at runtime
+ with tdls_testing cli command.
+ - Add support for TDLS 802.11z.
+ * wlantest: Add a tool wlantest for IEEE802.11 protocol testing.
+ wlantest can be used to capture frames from a monitor interface
+ for realtime capturing or from pcap files for offline analysis.
+ * Interworking: Support added for 802.11u. Enable in .config with
+ CONFIG_INTERWORKING. See wpa_supplicant.conf for config parameters
+ for interworking. wpa_cli commands added to support this are
+ interworking_select, interworking_connect, anqp_get, fetch_anqp,
+ and stop_fetch_anqp.
+ * Android: Add build and runtime support for Android wpa_supplicant.
+ * bgscan learn: Add new bgscan that learns BSS information based on
+ previous scans, and uses that information to dynamically generate
+ the list of channels for background scans.
+ * Add a new debug message level for excessive information. Use
+ -ddd to enable.
+ * TLS: Add support for tls_disable_time_checks=1 in client mode.
+ * Internal TLS:
+ - Add support for TLS v1.1 (RFC 4346). Enable with build parameter
+ CONFIG_TLSV11.
+ - Add domainComponent parser for X.509 names.
+ * Linux: Add RFKill support by adding an interface state "disabled".
+ * Reorder some IEs to get closer to IEEE 802.11 standard. Move
+ WMM into end of Beacon, Probe Resp and (Re)Assoc Resp frames.
+ Move HT IEs to be later in (Re)Assoc Resp.
+ * Solaris: Add support for wired 802.1X client.
+ * Wi-Fi Direct support. See README-P2P for more information.
+ * Many bugfixes.
2010-04-18 - v0.7.2
* nl80211: fixed number of issues with roaming
diff --git a/contrib/wpa/wpa_supplicant/Makefile b/contrib/wpa/wpa_supplicant/Makefile
deleted file mode 100644
index 1d25623..0000000
--- a/contrib/wpa/wpa_supplicant/Makefile
+++ /dev/null
@@ -1,1380 +0,0 @@
-ifndef CC
-CC=gcc
-endif
-
-ifndef CFLAGS
-CFLAGS = -MMD -O2 -Wall -g
-endif
-
-export LIBDIR ?= /usr/local/lib/
-export BINDIR ?= /usr/local/sbin/
-
-CFLAGS += -I../src
-CFLAGS += -I../src/utils
-
-ALL=wpa_supplicant wpa_passphrase wpa_cli
-
-all: verify_config $(ALL) dynamic_eap_methods
-
-verify_config:
- @if [ ! -r .config ]; then \
- echo 'Building wpa_supplicant requires a configuration file'; \
- echo '(.config). See README for more instructions. You can'; \
- echo 'run "cp defconfig .config" to create an example'; \
- echo 'configuration.'; \
- exit 1; \
- fi
-
-mkconfig:
- @if [ -f .config ]; then \
- echo '.config exists - did not replace it'; \
- exit 1; \
- fi
- echo CONFIG_DRIVER_HOSTAP=y >> .config
- echo CONFIG_DRIVER_WEXT=y >> .config
-
-install: all
- mkdir -p $(DESTDIR)$(BINDIR)
- for i in $(ALL); do cp $$i $(DESTDIR)$(BINDIR)/$$i; done
- $(MAKE) -C ../src install
-
-OBJS = config.o
-OBJS += notify.o
-OBJS += bss.o
-OBJS += eap_register.o
-OBJS += ../src/utils/common.o
-OBJS += ../src/utils/wpa_debug.o
-OBJS += ../src/utils/wpabuf.o
-OBJS_p = wpa_passphrase.o
-OBJS_p += ../src/utils/common.o
-OBJS_p += ../src/utils/wpa_debug.o
-OBJS_p += ../src/utils/wpabuf.o
-OBJS_c = wpa_cli.o ../src/common/wpa_ctrl.o
-
--include .config
-
-ifndef CONFIG_OS
-ifdef CONFIG_NATIVE_WINDOWS
-CONFIG_OS=win32
-else
-CONFIG_OS=unix
-endif
-endif
-
-ifeq ($(CONFIG_OS), internal)
-CFLAGS += -DOS_NO_C_LIB_DEFINES
-endif
-
-OBJS += ../src/utils/os_$(CONFIG_OS).o
-OBJS_p += ../src/utils/os_$(CONFIG_OS).o
-OBJS_c += ../src/utils/os_$(CONFIG_OS).o
-
-ifdef CONFIG_WPA_TRACE
-CFLAGS += -DWPA_TRACE
-OBJS += ../src/utils/trace.o
-OBJS_p += ../src/utils/trace.o
-OBJS_c += ../src/utils/trace.o
-OBJS_c += ../src/utils/wpa_debug.o
-LDFLAGS += -rdynamic
-CFLAGS += -funwind-tables
-ifdef CONFIG_WPA_TRACE_BFD
-CFLAGS += -DWPA_TRACE_BFD
-LIBS += -lbfd
-LIBS_p += -lbfd
-LIBS_c += -lbfd
-endif
-endif
-
-ifndef CONFIG_ELOOP
-CONFIG_ELOOP=eloop
-endif
-OBJS += ../src/utils/$(CONFIG_ELOOP).o
-
-
-ifdef CONFIG_EAPOL_TEST
-CFLAGS += -Werror -DEAPOL_TEST
-endif
-
-ifndef CONFIG_BACKEND
-CONFIG_BACKEND=file
-endif
-
-ifeq ($(CONFIG_BACKEND), file)
-OBJS += config_file.o
-ifndef CONFIG_NO_CONFIG_BLOBS
-NEED_BASE64=y
-endif
-CFLAGS += -DCONFIG_BACKEND_FILE
-endif
-
-ifeq ($(CONFIG_BACKEND), winreg)
-OBJS += config_winreg.o
-endif
-
-ifeq ($(CONFIG_BACKEND), none)
-OBJS += config_none.o
-endif
-
-ifdef CONFIG_NO_CONFIG_WRITE
-CFLAGS += -DCONFIG_NO_CONFIG_WRITE
-endif
-
-ifdef CONFIG_NO_CONFIG_BLOBS
-CFLAGS += -DCONFIG_NO_CONFIG_BLOBS
-endif
-
-ifdef CONFIG_NO_SCAN_PROCESSING
-CFLAGS += -DCONFIG_NO_SCAN_PROCESSING
-endif
-
-ifdef CONFIG_IEEE80211W
-CFLAGS += -DCONFIG_IEEE80211W
-NEED_SHA256=y
-NEED_AES_OMAC1=y
-endif
-
-ifdef CONFIG_IEEE80211R
-CFLAGS += -DCONFIG_IEEE80211R
-OBJS += ../src/rsn_supp/wpa_ft.o
-NEED_80211_COMMON=y
-NEED_SHA256=y
-NEED_AES_OMAC1=y
-endif
-
-ifdef CONFIG_PEERKEY
-CFLAGS += -DCONFIG_PEERKEY
-endif
-
-ifndef CONFIG_NO_WPA
-OBJS += ../src/rsn_supp/wpa.o
-OBJS += ../src/rsn_supp/preauth.o
-OBJS += ../src/rsn_supp/pmksa_cache.o
-OBJS += ../src/rsn_supp/peerkey.o
-OBJS += ../src/rsn_supp/wpa_ie.o
-OBJS += ../src/common/wpa_common.o
-NEED_AES=y
-NEED_SHA1=y
-NEED_MD5=y
-NEED_RC4=y
-else
-CFLAGS += -DCONFIG_NO_WPA -DCONFIG_NO_WPA2
-endif
-
-ifdef CONFIG_IBSS_RSN
-NEED_RSN_AUTHENTICATOR=y
-CFLAGS += -DCONFIG_IBSS_RSN
-OBJS += ibss_rsn.o
-endif
-
-ifdef CONFIG_NO_WPA2
-CFLAGS += -DCONFIG_NO_WPA2
-endif
-
-include ../src/drivers/drivers.mak
-ifdef CONFIG_AP
-OBJS_d += $(DRV_BOTH_OBJS)
-CFLAGS += $(DRV_BOTH_CFLAGS)
-LDFLAGS += $(DRV_BOTH_LDFLAGS)
-LIBS += $(DRV_BOTH_LIBS)
-else
-NEED_AP_MLME=
-OBJS_d += $(DRV_WPA_OBJS)
-CFLAGS += $(DRV_WPA_CFLAGS)
-LDFLAGS += $(DRV_WPA_LDFLAGS)
-LIBS += $(DRV_WPA_LIBS)
-endif
-
-ifndef CONFIG_L2_PACKET
-CONFIG_L2_PACKET=linux
-endif
-
-OBJS_l2 += ../src/l2_packet/l2_packet_$(CONFIG_L2_PACKET).o
-
-ifeq ($(CONFIG_L2_PACKET), pcap)
-ifdef CONFIG_WINPCAP
-CFLAGS += -DCONFIG_WINPCAP
-LIBS += -lwpcap -lpacket
-LIBS_w += -lwpcap
-else
-LIBS += -ldnet -lpcap
-endif
-endif
-
-ifeq ($(CONFIG_L2_PACKET), winpcap)
-LIBS += -lwpcap -lpacket
-LIBS_w += -lwpcap
-endif
-
-ifeq ($(CONFIG_L2_PACKET), freebsd)
-LIBS += -lpcap
-endif
-
-ifdef CONFIG_EAP_TLS
-# EAP-TLS
-ifeq ($(CONFIG_EAP_TLS), dyn)
-CFLAGS += -DEAP_TLS_DYNAMIC
-EAPDYN += ../src/eap_peer/eap_tls.so
-else
-CFLAGS += -DEAP_TLS
-OBJS += ../src/eap_peer/eap_tls.o
-OBJS_h += ../src/eap_server/eap_server_tls.o
-endif
-TLS_FUNCS=y
-CONFIG_IEEE8021X_EAPOL=y
-endif
-
-ifdef CONFIG_EAP_PEAP
-# EAP-PEAP
-ifeq ($(CONFIG_EAP_PEAP), dyn)
-CFLAGS += -DEAP_PEAP_DYNAMIC
-EAPDYN += ../src/eap_peer/eap_peap.so
-else
-CFLAGS += -DEAP_PEAP
-OBJS += ../src/eap_peer/eap_peap.o
-OBJS += ../src/eap_common/eap_peap_common.o
-OBJS_h += ../src/eap_server/eap_server_peap.o
-endif
-TLS_FUNCS=y
-CONFIG_IEEE8021X_EAPOL=y
-endif
-
-ifdef CONFIG_EAP_TTLS
-# EAP-TTLS
-ifeq ($(CONFIG_EAP_TTLS), dyn)
-CFLAGS += -DEAP_TTLS_DYNAMIC
-EAPDYN += ../src/eap_peer/eap_ttls.so
-else
-CFLAGS += -DEAP_TTLS
-OBJS += ../src/eap_peer/eap_ttls.o
-OBJS_h += ../src/eap_server/eap_server_ttls.o
-endif
-MS_FUNCS=y
-TLS_FUNCS=y
-CHAP=y
-CONFIG_IEEE8021X_EAPOL=y
-endif
-
-ifdef CONFIG_EAP_MD5
-# EAP-MD5
-ifeq ($(CONFIG_EAP_MD5), dyn)
-CFLAGS += -DEAP_MD5_DYNAMIC
-EAPDYN += ../src/eap_peer/eap_md5.so
-else
-CFLAGS += -DEAP_MD5
-OBJS += ../src/eap_peer/eap_md5.o
-OBJS_h += ../src/eap_server/eap_server_md5.o
-endif
-CHAP=y
-CONFIG_IEEE8021X_EAPOL=y
-endif
-
-# backwards compatibility for old spelling
-ifdef CONFIG_MSCHAPV2
-ifndef CONFIG_EAP_MSCHAPV2
-CONFIG_EAP_MSCHAPV2=y
-endif
-endif
-
-ifdef CONFIG_EAP_MSCHAPV2
-# EAP-MSCHAPv2
-ifeq ($(CONFIG_EAP_MSCHAPV2), dyn)
-CFLAGS += -DEAP_MSCHAPv2_DYNAMIC
-EAPDYN += ../src/eap_peer/eap_mschapv2.so
-EAPDYN += ../src/eap_peer/mschapv2.so
-else
-CFLAGS += -DEAP_MSCHAPv2
-OBJS += ../src/eap_peer/eap_mschapv2.o
-OBJS += ../src/eap_peer/mschapv2.o
-OBJS_h += ../src/eap_server/eap_server_mschapv2.o
-endif
-MS_FUNCS=y
-CONFIG_IEEE8021X_EAPOL=y
-endif
-
-ifdef CONFIG_EAP_GTC
-# EAP-GTC
-ifeq ($(CONFIG_EAP_GTC), dyn)
-CFLAGS += -DEAP_GTC_DYNAMIC
-EAPDYN += ../src/eap_peer/eap_gtc.so
-else
-CFLAGS += -DEAP_GTC
-OBJS += ../src/eap_peer/eap_gtc.o
-OBJS_h += ../src/eap_server/eap_server_gtc.o
-endif
-CONFIG_IEEE8021X_EAPOL=y
-endif
-
-ifdef CONFIG_EAP_OTP
-# EAP-OTP
-ifeq ($(CONFIG_EAP_OTP), dyn)
-CFLAGS += -DEAP_OTP_DYNAMIC
-EAPDYN += ../src/eap_peer/eap_otp.so
-else
-CFLAGS += -DEAP_OTP
-OBJS += ../src/eap_peer/eap_otp.o
-endif
-CONFIG_IEEE8021X_EAPOL=y
-endif
-
-ifdef CONFIG_EAP_SIM
-# EAP-SIM
-ifeq ($(CONFIG_EAP_SIM), dyn)
-CFLAGS += -DEAP_SIM_DYNAMIC
-EAPDYN += ../src/eap_peer/eap_sim.so
-else
-CFLAGS += -DEAP_SIM
-OBJS += ../src/eap_peer/eap_sim.o
-OBJS_h += ../src/eap_server/eap_server_sim.o
-endif
-CONFIG_IEEE8021X_EAPOL=y
-CONFIG_EAP_SIM_COMMON=y
-NEED_AES_CBC=y
-endif
-
-ifdef CONFIG_EAP_LEAP
-# EAP-LEAP
-ifeq ($(CONFIG_EAP_LEAP), dyn)
-CFLAGS += -DEAP_LEAP_DYNAMIC
-EAPDYN += ../src/eap_peer/eap_leap.so
-else
-CFLAGS += -DEAP_LEAP
-OBJS += ../src/eap_peer/eap_leap.o
-endif
-MS_FUNCS=y
-CONFIG_IEEE8021X_EAPOL=y
-endif
-
-ifdef CONFIG_EAP_PSK
-# EAP-PSK
-ifeq ($(CONFIG_EAP_PSK), dyn)
-CFLAGS += -DEAP_PSK_DYNAMIC
-EAPDYN += ../src/eap_peer/eap_psk.so
-else
-CFLAGS += -DEAP_PSK
-OBJS += ../src/eap_peer/eap_psk.o ../src/eap_common/eap_psk_common.o
-OBJS_h += ../src/eap_server/eap_server_psk.o
-endif
-CONFIG_IEEE8021X_EAPOL=y
-NEED_AES=y
-NEED_AES_OMAC1=y
-NEED_AES_ENCBLOCK=y
-NEED_AES_EAX=y
-endif
-
-ifdef CONFIG_EAP_AKA
-# EAP-AKA
-ifeq ($(CONFIG_EAP_AKA), dyn)
-CFLAGS += -DEAP_AKA_DYNAMIC
-EAPDYN += ../src/eap_peer/eap_aka.so
-else
-CFLAGS += -DEAP_AKA
-OBJS += ../src/eap_peer/eap_aka.o
-OBJS_h += ../src/eap_server/eap_server_aka.o
-endif
-CONFIG_IEEE8021X_EAPOL=y
-CONFIG_EAP_SIM_COMMON=y
-NEED_AES_CBC=y
-endif
-
-ifdef CONFIG_EAP_AKA_PRIME
-# EAP-AKA'
-ifeq ($(CONFIG_EAP_AKA_PRIME), dyn)
-CFLAGS += -DEAP_AKA_PRIME_DYNAMIC
-else
-CFLAGS += -DEAP_AKA_PRIME
-endif
-NEED_SHA256=y
-endif
-
-ifdef CONFIG_EAP_SIM_COMMON
-OBJS += ../src/eap_common/eap_sim_common.o
-OBJS_h += ../src/eap_server/eap_sim_db.o
-NEED_AES=y
-NEED_FIPS186_2_PRF=y
-endif
-
-ifdef CONFIG_EAP_FAST
-# EAP-FAST
-ifeq ($(CONFIG_EAP_FAST), dyn)
-CFLAGS += -DEAP_FAST_DYNAMIC
-EAPDYN += ../src/eap_peer/eap_fast.so
-EAPDYN += ../src/eap_common/eap_fast_common.o
-else
-CFLAGS += -DEAP_FAST
-OBJS += ../src/eap_peer/eap_fast.o ../src/eap_peer/eap_fast_pac.o
-OBJS += ../src/eap_common/eap_fast_common.o
-OBJS_h += ../src/eap_server/eap_server_fast.o
-endif
-TLS_FUNCS=y
-CONFIG_IEEE8021X_EAPOL=y
-NEED_T_PRF=y
-endif
-
-ifdef CONFIG_EAP_PAX
-# EAP-PAX
-ifeq ($(CONFIG_EAP_PAX), dyn)
-CFLAGS += -DEAP_PAX_DYNAMIC
-EAPDYN += ../src/eap_peer/eap_pax.so
-else
-CFLAGS += -DEAP_PAX
-OBJS += ../src/eap_peer/eap_pax.o ../src/eap_common/eap_pax_common.o
-OBJS_h += ../src/eap_server/eap_server_pax.o
-endif
-CONFIG_IEEE8021X_EAPOL=y
-endif
-
-ifdef CONFIG_EAP_SAKE
-# EAP-SAKE
-ifeq ($(CONFIG_EAP_SAKE), dyn)
-CFLAGS += -DEAP_SAKE_DYNAMIC
-EAPDYN += ../src/eap_peer/eap_sake.so
-else
-CFLAGS += -DEAP_SAKE
-OBJS += ../src/eap_peer/eap_sake.o ../src/eap_common/eap_sake_common.o
-OBJS_h += ../src/eap_server/eap_server_sake.o
-endif
-CONFIG_IEEE8021X_EAPOL=y
-endif
-
-ifdef CONFIG_EAP_GPSK
-# EAP-GPSK
-ifeq ($(CONFIG_EAP_GPSK), dyn)
-CFLAGS += -DEAP_GPSK_DYNAMIC
-EAPDYN += ../src/eap_peer/eap_gpsk.so
-else
-CFLAGS += -DEAP_GPSK
-OBJS += ../src/eap_peer/eap_gpsk.o ../src/eap_common/eap_gpsk_common.o
-OBJS_h += ../src/eap_server/eap_server_gpsk.o
-endif
-CONFIG_IEEE8021X_EAPOL=y
-ifdef CONFIG_EAP_GPSK_SHA256
-CFLAGS += -DEAP_GPSK_SHA256
-endif
-NEED_SHA256=y
-NEED_AES_OMAC1=y
-endif
-
-ifdef CONFIG_WPS
-# EAP-WSC
-CFLAGS += -DCONFIG_WPS -DEAP_WSC
-OBJS += wps_supplicant.o
-OBJS += ../src/utils/uuid.o
-OBJS += ../src/eap_peer/eap_wsc.o ../src/eap_common/eap_wsc_common.o
-OBJS += ../src/wps/wps.o
-OBJS += ../src/wps/wps_common.o
-OBJS += ../src/wps/wps_attr_parse.o
-OBJS += ../src/wps/wps_attr_build.o
-OBJS += ../src/wps/wps_attr_process.o
-OBJS += ../src/wps/wps_dev_attr.o
-OBJS += ../src/wps/wps_enrollee.o
-OBJS += ../src/wps/wps_registrar.o
-OBJS_h += ../src/eap_server/eap_server_wsc.o
-CONFIG_IEEE8021X_EAPOL=y
-NEED_DH_GROUPS=y
-NEED_SHA256=y
-NEED_BASE64=y
-NEED_80211_COMMON=y
-NEED_AES_CBC=y
-NEED_MODEXP=y
-
-ifdef CONFIG_WPS_UFD
-CFLAGS += -DCONFIG_WPS_UFD
-OBJS += ../src/wps/wps_ufd.o
-NEED_WPS_OOB=y
-endif
-
-ifdef CONFIG_WPS_NFC
-CFLAGS += -DCONFIG_WPS_NFC
-OBJS += ../src/wps/ndef.o
-OBJS += ../src/wps/wps_nfc.o
-NEED_WPS_OOB=y
-ifdef CONFIG_WPS_NFC_PN531
-PN531_PATH ?= /usr/local/src/nfc
-CFLAGS += -DCONFIG_WPS_NFC_PN531
-CFLAGS += -I${PN531_PATH}/inc
-OBJS += ../src/wps/wps_nfc_pn531.o
-LIBS += ${PN531_PATH}/lib/wpsnfc.dll
-LIBS += ${PN531_PATH}/lib/libnfc_mapping_pn53x.dll
-endif
-endif
-
-ifdef NEED_WPS_OOB
-CFLAGS += -DCONFIG_WPS_OOB
-endif
-
-ifdef CONFIG_WPS_ER
-CONFIG_WPS_UPNP=y
-CFLAGS += -DCONFIG_WPS_ER
-OBJS += ../src/wps/wps_er.o
-OBJS += ../src/wps/wps_er_ssdp.o
-endif
-
-ifdef CONFIG_WPS_UPNP
-CFLAGS += -DCONFIG_WPS_UPNP
-OBJS += ../src/wps/wps_upnp.o
-OBJS += ../src/wps/wps_upnp_ssdp.o
-OBJS += ../src/wps/wps_upnp_web.o
-OBJS += ../src/wps/wps_upnp_event.o
-OBJS += ../src/wps/wps_upnp_ap.o
-OBJS += ../src/wps/upnp_xml.o
-OBJS += ../src/wps/httpread.o
-OBJS += ../src/wps/http_client.o
-OBJS += ../src/wps/http_server.o
-endif
-
-endif
-
-ifdef CONFIG_EAP_IKEV2
-# EAP-IKEv2
-ifeq ($(CONFIG_EAP_IKEV2), dyn)
-CFLAGS += -DEAP_IKEV2_DYNAMIC
-EAPDYN += ../src/eap_peer/eap_ikev2.so ../src/eap_peer/ikev2.o
-EAPDYN += ../src/eap_common/eap_ikev2_common.o ../src/eap_common/ikev2_common.o
-else
-CFLAGS += -DEAP_IKEV2
-OBJS += ../src/eap_peer/eap_ikev2.o ../src/eap_peer/ikev2.o
-OBJS += ../src/eap_common/eap_ikev2_common.o ../src/eap_common/ikev2_common.o
-OBJS_h += ../src/eap_server/eap_server_ikev2.o
-OBJS_h += ../src/eap_server/ikev2.o
-endif
-CONFIG_IEEE8021X_EAPOL=y
-NEED_DH_GROUPS=y
-NEED_DH_GROUPS_ALL=y
-NEED_MODEXP=y
-NEED_CIPHER=y
-endif
-
-ifdef CONFIG_EAP_VENDOR_TEST
-ifeq ($(CONFIG_EAP_VENDOR_TEST), dyn)
-CFLAGS += -DEAP_VENDOR_TEST_DYNAMIC
-EAPDYN += ../src/eap_peer/eap_vendor_test.so
-else
-CFLAGS += -DEAP_VENDOR_TEST
-OBJS += ../src/eap_peer/eap_vendor_test.o
-OBJS_h += ../src/eap_server/eap_server_vendor_test.o
-endif
-CONFIG_IEEE8021X_EAPOL=y
-endif
-
-ifdef CONFIG_EAP_TNC
-# EAP-TNC
-CFLAGS += -DEAP_TNC
-OBJS += ../src/eap_peer/eap_tnc.o
-OBJS += ../src/eap_peer/tncc.o
-OBJS_h += ../src/eap_server/eap_server_tnc.o
-OBJS_h += ../src/eap_server/tncs.o
-NEED_BASE64=y
-ifndef CONFIG_NATIVE_WINDOWS
-ifndef CONFIG_DRIVER_BSD
-LIBS += -ldl
-endif
-endif
-endif
-
-ifdef CONFIG_IEEE8021X_EAPOL
-# IEEE 802.1X/EAPOL state machines (e.g., for RADIUS authentication)
-CFLAGS += -DIEEE8021X_EAPOL
-OBJS += ../src/eapol_supp/eapol_supp_sm.o
-OBJS += ../src/eap_peer/eap.o ../src/eap_peer/eap_methods.o
-NEED_EAP_COMMON=y
-ifdef CONFIG_DYNAMIC_EAP_METHODS
-CFLAGS += -DCONFIG_DYNAMIC_EAP_METHODS
-LIBS += -ldl -rdynamic
-endif
-endif
-
-ifdef CONFIG_AP
-NEED_80211_COMMON=y
-NEED_EAP_COMMON=y
-NEED_RSN_AUTHENTICATOR=y
-CFLAGS += -DCONFIG_AP
-OBJS += ap.o
-CFLAGS += -DCONFIG_NO_RADIUS
-CFLAGS += -DCONFIG_NO_ACCOUNTING
-CFLAGS += -DCONFIG_NO_VLAN
-OBJS += ../src/ap/hostapd.o
-OBJS += ../src/ap/wpa_auth_glue.o
-OBJS += ../src/ap/utils.o
-OBJS += ../src/ap/authsrv.o
-OBJS += ../src/ap/ap_config.o
-OBJS += ../src/utils/ip_addr.o
-OBJS += ../src/ap/sta_info.o
-OBJS += ../src/ap/tkip_countermeasures.o
-OBJS += ../src/ap/ap_mlme.o
-OBJS += ../src/ap/ieee802_1x.o
-OBJS += ../src/eapol_auth/eapol_auth_sm.o
-OBJS += ../src/ap/ieee802_11_auth.o
-OBJS += ../src/ap/drv_callbacks.o
-OBJS += ../src/ap/ap_drv_ops.o
-ifdef CONFIG_CTRL_IFACE
-OBJS += ../src/ap/ctrl_iface_ap.o
-endif
-
-CFLAGS += -DEAP_SERVER -DEAP_SERVER_IDENTITY
-OBJS += ../src/eap_server/eap_server.o
-OBJS += ../src/eap_server/eap_server_identity.o
-OBJS += ../src/eap_server/eap_server_methods.o
-
-ifdef CONFIG_IEEE80211N
-CFLAGS += -DCONFIG_IEEE80211N
-endif
-
-ifdef NEED_AP_MLME
-OBJS += ../src/ap/beacon.o
-OBJS += ../src/ap/wmm.o
-OBJS += ../src/ap/ap_list.o
-OBJS += ../src/ap/ieee802_11.o
-OBJS += ../src/ap/hw_features.o
-ifdef CONFIG_IEEE80211N
-OBJS += ../src/ap/ieee802_11_ht.o
-endif
-CFLAGS += -DNEED_AP_MLME
-endif
-ifdef CONFIG_WPS
-CFLAGS += -DEAP_SERVER_WSC
-OBJS += ../src/ap/wps_hostapd.o
-OBJS += ../src/eap_server/eap_server_wsc.o
-endif
-endif
-
-ifdef NEED_RSN_AUTHENTICATOR
-CFLAGS += -DCONFIG_NO_RADIUS
-NEED_AES_WRAP=y
-OBJS += ../src/ap/wpa_auth.o
-OBJS += ../src/ap/wpa_auth_ie.o
-OBJS += ../src/ap/pmksa_cache_auth.o
-ifdef CONFIG_IEEE80211R
-OBJS += ../src/ap/wpa_auth_ft.o
-endif
-ifdef CONFIG_PEERKEY
-OBJS += ../src/ap/peerkey_auth.o
-endif
-endif
-
-ifdef CONFIG_EAP_SERVER
-CFLAGS += -DEAP_SERVER
-OBJS_h += ../src/eap_server/eap_server.o
-OBJS_h += ../src/eap_server/eap_server_identity.o
-OBJS_h += ../src/eap_server/eap_server_methods.o
-endif
-
-ifdef CONFIG_RADIUS_CLIENT
-OBJS_h += ../src/utils/ip_addr.o
-OBJS_h += ../src/radius/radius.o
-OBJS_h += ../src/radius/radius_client.o
-endif
-
-ifdef CONFIG_AUTHENTICATOR
-OBJS_h += ../src/eapol_auth/eapol_auth_sm.o
-OBJS_h += ../src/ap/ieee802_1x.o
-endif
-
-ifdef CONFIG_WPA_AUTHENTICATOR
-OBJS_h += ../src/ap/wpa_auth.o
-OBJS_h += ../src/ap/wpa_auth_ie.o
-OBJS_h += ../src/ap/pmksa_cache_auth.o
-ifdef CONFIG_IEEE80211R
-OBJS_h += ../src/ap/wpa_auth_ft.o
-endif
-ifdef CONFIG_PEERKEY
-OBJS_h += ../src/ap/peerkey_auth.o
-endif
-endif
-
-ifdef CONFIG_PCSC
-# PC/SC interface for smartcards (USIM, GSM SIM)
-CFLAGS += -DPCSC_FUNCS -I/usr/include/PCSC
-OBJS += ../src/utils/pcsc_funcs.o
-# -lpthread may not be needed depending on how pcsc-lite was configured
-ifdef CONFIG_NATIVE_WINDOWS
-#Once MinGW gets support for WinScard, -lwinscard could be used instead of the
-#dynamic symbol loading that is now used in pcsc_funcs.c
-#LIBS += -lwinscard
-else
-LIBS += -lpcsclite -lpthread
-endif
-endif
-
-ifdef CONFIG_SIM_SIMULATOR
-CFLAGS += -DCONFIG_SIM_SIMULATOR
-NEED_MILENAGE=y
-endif
-
-ifdef CONFIG_USIM_SIMULATOR
-CFLAGS += -DCONFIG_USIM_SIMULATOR
-NEED_MILENAGE=y
-endif
-
-ifdef NEED_MILENAGE
-OBJS += ../src/crypto/milenage.o
-endif
-
-ifdef CONFIG_PKCS12
-CFLAGS += -DPKCS12_FUNCS
-endif
-
-ifdef CONFIG_SMARTCARD
-CFLAGS += -DCONFIG_SMARTCARD
-endif
-
-ifdef MS_FUNCS
-OBJS += ../src/crypto/ms_funcs.o
-NEED_DES=y
-NEED_MD4=y
-endif
-
-ifdef CHAP
-OBJS += ../src/eap_common/chap.o
-endif
-
-ifdef TLS_FUNCS
-NEED_DES=y
-# Shared TLS functions (needed for EAP_TLS, EAP_PEAP, EAP_TTLS, and EAP_FAST)
-OBJS += ../src/eap_peer/eap_tls_common.o
-OBJS_h += ../src/eap_server/eap_server_tls_common.o
-NEED_TLS_PRF=y
-endif
-
-ifndef CONFIG_TLS
-CONFIG_TLS=openssl
-endif
-
-ifeq ($(CONFIG_TLS), openssl)
-ifdef TLS_FUNCS
-CFLAGS += -DEAP_TLS_OPENSSL
-OBJS += ../src/crypto/tls_openssl.o
-LIBS += -lssl
-endif
-OBJS += ../src/crypto/crypto_openssl.o
-OBJS_p += ../src/crypto/crypto_openssl.o
-ifdef NEED_FIPS186_2_PRF
-OBJS += ../src/crypto/fips_prf_openssl.o
-endif
-LIBS += -lcrypto
-LIBS_p += -lcrypto
-endif
-
-ifeq ($(CONFIG_TLS), gnutls)
-ifdef TLS_FUNCS
-OBJS += ../src/crypto/tls_gnutls.o
-LIBS += -lgnutls -lgpg-error
-ifdef CONFIG_GNUTLS_EXTRA
-CFLAGS += -DCONFIG_GNUTLS_EXTRA
-LIBS += -lgnutls-extra
-endif
-endif
-OBJS += ../src/crypto/crypto_gnutls.o
-OBJS_p += ../src/crypto/crypto_gnutls.o
-ifdef NEED_FIPS186_2_PRF
-OBJS += ../src/crypto/fips_prf_gnutls.o
-endif
-LIBS += -lgcrypt
-LIBS_p += -lgcrypt
-CONFIG_INTERNAL_SHA256=y
-CONFIG_INTERNAL_RC4=y
-CONFIG_INTERNAL_DH_GROUP5=y
-endif
-
-ifeq ($(CONFIG_TLS), schannel)
-ifdef TLS_FUNCS
-OBJS += ../src/crypto/tls_schannel.o
-endif
-OBJS += ../src/crypto/crypto_cryptoapi.o
-OBJS_p += ../src/crypto/crypto_cryptoapi.o
-ifdef NEED_FIPS186_2_PRF
-OBJS += ../src/crypto/fips_prf_cryptoapi.o
-endif
-CONFIG_INTERNAL_SHA256=y
-CONFIG_INTERNAL_RC4=y
-CONFIG_INTERNAL_DH_GROUP5=y
-endif
-
-ifeq ($(CONFIG_TLS), nss)
-ifdef TLS_FUNCS
-OBJS += ../src/crypto/tls_nss.o
-LIBS += -lssl3
-endif
-OBJS += ../src/crypto/crypto_nss.o
-OBJS_p += ../src/crypto/crypto_nss.o
-ifdef NEED_FIPS186_2_PRF
-OBJS += ../src/crypto/fips_prf_nss.o
-endif
-LIBS += -lnss3
-LIBS_p += -lnss3
-CONFIG_INTERNAL_MD4=y
-CONFIG_INTERNAL_DH_GROUP5=y
-endif
-
-ifeq ($(CONFIG_TLS), internal)
-ifndef CONFIG_CRYPTO
-CONFIG_CRYPTO=internal
-endif
-ifdef TLS_FUNCS
-OBJS += ../src/crypto/crypto_internal-rsa.o
-OBJS += ../src/crypto/tls_internal.o
-OBJS += ../src/tls/tlsv1_common.o
-OBJS += ../src/tls/tlsv1_record.o
-OBJS += ../src/tls/tlsv1_cred.o
-OBJS += ../src/tls/tlsv1_client.o
-OBJS += ../src/tls/tlsv1_client_write.o
-OBJS += ../src/tls/tlsv1_client_read.o
-OBJS += ../src/tls/asn1.o
-OBJS += ../src/tls/rsa.o
-OBJS += ../src/tls/x509v3.o
-OBJS += ../src/tls/pkcs1.o
-OBJS += ../src/tls/pkcs5.o
-OBJS += ../src/tls/pkcs8.o
-NEED_SHA256=y
-NEED_BASE64=y
-NEED_TLS_PRF=y
-NEED_MODEXP=y
-NEED_CIPHER=y
-CFLAGS += -DCONFIG_TLS_INTERNAL_CLIENT
-endif
-ifdef NEED_CIPHER
-NEED_DES=y
-OBJS += ../src/crypto/crypto_internal-cipher.o
-endif
-ifdef NEED_MODEXP
-OBJS += ../src/crypto/crypto_internal-modexp.o
-OBJS += ../src/tls/bignum.o
-endif
-ifeq ($(CONFIG_CRYPTO), libtomcrypt)
-OBJS += ../src/crypto/crypto_libtomcrypt.o
-OBJS_p += ../src/crypto/crypto_libtomcrypt.o
-LIBS += -ltomcrypt -ltfm
-LIBS_p += -ltomcrypt -ltfm
-CONFIG_INTERNAL_SHA256=y
-CONFIG_INTERNAL_RC4=y
-CONFIG_INTERNAL_DH_GROUP5=y
-endif
-ifeq ($(CONFIG_CRYPTO), internal)
-OBJS += ../src/crypto/crypto_internal.o
-OBJS_p += ../src/crypto/crypto_internal.o
-NEED_AES_ENC=y
-CFLAGS += -DCONFIG_CRYPTO_INTERNAL
-ifdef CONFIG_INTERNAL_LIBTOMMATH
-CFLAGS += -DCONFIG_INTERNAL_LIBTOMMATH
-ifdef CONFIG_INTERNAL_LIBTOMMATH_FAST
-CFLAGS += -DLTM_FAST
-endif
-else
-LIBS += -ltommath
-LIBS_p += -ltommath
-endif
-CONFIG_INTERNAL_AES=y
-CONFIG_INTERNAL_DES=y
-CONFIG_INTERNAL_SHA1=y
-CONFIG_INTERNAL_MD4=y
-CONFIG_INTERNAL_MD5=y
-CONFIG_INTERNAL_SHA256=y
-CONFIG_INTERNAL_RC4=y
-CONFIG_INTERNAL_DH_GROUP5=y
-endif
-ifeq ($(CONFIG_CRYPTO), cryptoapi)
-OBJS += ../src/crypto/crypto_cryptoapi.o
-OBJS_p += ../src/crypto/crypto_cryptoapi.o
-CFLAGS += -DCONFIG_CRYPTO_CRYPTOAPI
-CONFIG_INTERNAL_SHA256=y
-CONFIG_INTERNAL_RC4=y
-endif
-endif
-
-ifeq ($(CONFIG_TLS), none)
-ifdef TLS_FUNCS
-OBJS += ../src/crypto/tls_none.o
-CFLAGS += -DEAP_TLS_NONE
-CONFIG_INTERNAL_AES=y
-CONFIG_INTERNAL_SHA1=y
-CONFIG_INTERNAL_MD5=y
-endif
-OBJS += ../src/crypto/crypto_none.o
-OBJS_p += ../src/crypto/crypto_none.o
-CONFIG_INTERNAL_SHA256=y
-CONFIG_INTERNAL_RC4=y
-endif
-
-ifdef TLS_FUNCS
-ifdef CONFIG_SMARTCARD
-ifndef CONFIG_NATIVE_WINDOWS
-ifneq ($(CONFIG_L2_PACKET), freebsd)
-LIBS += -ldl
-endif
-endif
-endif
-endif
-
-ifndef TLS_FUNCS
-OBJS += ../src/crypto/tls_none.o
-ifeq ($(CONFIG_TLS), internal)
-CONFIG_INTERNAL_AES=y
-CONFIG_INTERNAL_SHA1=y
-CONFIG_INTERNAL_MD5=y
-CONFIG_INTERNAL_RC4=y
-endif
-endif
-
-AESOBJS = # none so far (see below)
-ifdef CONFIG_INTERNAL_AES
-AESOBJS += ../src/crypto/aes-internal.o ../src/crypto/aes-internal-dec.o
-endif
-
-AESOBJS += ../src/crypto/aes-unwrap.o
-ifdef NEED_AES_EAX
-AESOBJS += ../src/crypto/aes-eax.o
-NEED_AES_CTR=y
-endif
-ifdef NEED_AES_CTR
-AESOBJS += ../src/crypto/aes-ctr.o
-endif
-ifdef NEED_AES_ENCBLOCK
-AESOBJS += ../src/crypto/aes-encblock.o
-endif
-ifdef NEED_AES_OMAC1
-NEED_AES_ENC=y
-AESOBJS += ../src/crypto/aes-omac1.o
-endif
-ifdef NEED_AES_WRAP
-NEED_AES_ENC=y
-AESOBJS += ../src/crypto/aes-wrap.o
-endif
-ifdef NEED_AES_CBC
-NEED_AES_ENC=y
-AESOBJS += ../src/crypto/aes-cbc.o
-endif
-ifdef NEED_AES_ENC
-ifdef CONFIG_INTERNAL_AES
-AESOBJS += ../src/crypto/aes-internal-enc.o
-endif
-endif
-ifdef NEED_AES
-OBJS += $(AESOBJS)
-endif
-
-ifdef NEED_SHA1
-SHA1OBJS += ../src/crypto/sha1.o
-ifdef CONFIG_INTERNAL_SHA1
-SHA1OBJS += ../src/crypto/sha1-internal.o
-ifdef NEED_FIPS186_2_PRF
-SHA1OBJS += ../src/crypto/fips_prf_internal.o
-endif
-endif
-ifndef CONFIG_NO_WPA_PASSPHRASE
-SHA1OBJS += ../src/crypto/sha1-pbkdf2.o
-endif
-ifdef NEED_T_PRF
-SHA1OBJS += ../src/crypto/sha1-tprf.o
-endif
-ifdef NEED_TLS_PRF
-SHA1OBJS += ../src/crypto/sha1-tlsprf.o
-endif
-endif
-
-MD5OBJS = ../src/crypto/md5.o
-ifdef NEED_MD5
-ifdef CONFIG_INTERNAL_MD5
-MD5OBJS += ../src/crypto/md5-internal.o
-endif
-ifdef CONFIG_FIPS
-MD5OBJS += ../src/crypto/md5-non-fips.o
-endif
-OBJS += $(MD5OBJS)
-OBJS_p += $(MD5OBJS)
-endif
-
-ifdef NEED_MD4
-ifdef CONFIG_INTERNAL_MD4
-OBJS += ../src/crypto/md4-internal.o
-endif
-endif
-
-DESOBJS = # none needed when not internal
-ifdef NEED_DES
-ifdef CONFIG_INTERNAL_DES
-DESOBJS += ../src/crypto/des-internal.o
-endif
-endif
-
-ifdef NEED_RC4
-ifdef CONFIG_INTERNAL_RC4
-OBJS += ../src/crypto/rc4.o
-endif
-endif
-
-SHA256OBJS = # none by default
-ifdef NEED_SHA256
-CFLAGS += -DCONFIG_SHA256
-SHA256OBJS += ../src/crypto/sha256.o
-ifdef CONFIG_INTERNAL_SHA256
-SHA256OBJS += ../src/crypto/sha256-internal.o
-endif
-OBJS += $(SHA256OBJS)
-endif
-
-ifdef NEED_DH_GROUPS
-OBJS += ../src/crypto/dh_groups.o
-endif
-ifdef NEED_DH_GROUPS_ALL
-CFLAGS += -DALL_DH_GROUPS
-endif
-ifdef CONFIG_INTERNAL_DH_GROUP5
-ifdef NEED_DH_GROUPS
-OBJS += ../src/crypto/dh_group5.o
-endif
-endif
-
-ifdef CONFIG_CTRL_IFACE
-ifeq ($(CONFIG_CTRL_IFACE), y)
-ifdef CONFIG_NATIVE_WINDOWS
-CONFIG_CTRL_IFACE=named_pipe
-else
-CONFIG_CTRL_IFACE=unix
-endif
-endif
-CFLAGS += -DCONFIG_CTRL_IFACE
-ifeq ($(CONFIG_CTRL_IFACE), unix)
-CFLAGS += -DCONFIG_CTRL_IFACE_UNIX
-endif
-ifeq ($(CONFIG_CTRL_IFACE), udp)
-CFLAGS += -DCONFIG_CTRL_IFACE_UDP
-endif
-ifeq ($(CONFIG_CTRL_IFACE), named_pipe)
-CFLAGS += -DCONFIG_CTRL_IFACE_NAMED_PIPE
-endif
-OBJS += ctrl_iface.o ctrl_iface_$(CONFIG_CTRL_IFACE).o
-endif
-
-ifdef CONFIG_CTRL_IFACE_DBUS
-DBUS=y
-DBUS_CFLAGS += -DCONFIG_CTRL_IFACE_DBUS -DDBUS_API_SUBJECT_TO_CHANGE
-DBUS_OBJS += dbus/dbus_old.o dbus/dbus_old_handlers.o
-ifdef CONFIG_WPS
-DBUS_OBJS += dbus/dbus_old_handlers_wps.o
-endif
-DBUS_OBJS += dbus/dbus_dict_helpers.o
-ifndef DBUS_LIBS
-DBUS_LIBS := $(shell pkg-config --libs dbus-1)
-endif
-ifndef DBUS_INCLUDE
-DBUS_INCLUDE := $(shell pkg-config --cflags dbus-1)
-endif
-dbus_version=$(subst ., ,$(shell pkg-config --modversion dbus-1))
-DBUS_VERSION_MAJOR=$(word 1,$(dbus_version))
-DBUS_VERSION_MINOR=$(word 2,$(dbus_version))
-ifeq ($(DBUS_VERSION_MAJOR),)
-DBUS_VERSION_MAJOR=0
-endif
-ifeq ($(DBUS_VERSION_MINOR),)
-DBUS_VERSION_MINOR=0
-endif
-DBUS_INCLUDE += -DDBUS_VERSION_MAJOR=$(DBUS_VERSION_MAJOR)
-DBUS_INCLUDE += -DDBUS_VERSION_MINOR=$(DBUS_VERSION_MINOR)
-DBUS_CFLAGS += $(DBUS_INCLUDE)
-endif
-
-ifdef CONFIG_CTRL_IFACE_DBUS_NEW
-DBUS=y
-DBUS_CFLAGS += -DCONFIG_CTRL_IFACE_DBUS_NEW
-DBUS_OBJS ?= dbus/dbus_dict_helpers.o
-DBUS_OBJS += dbus/dbus_new_helpers.o
-DBUS_OBJS += dbus/dbus_new.o dbus/dbus_new_handlers.o
-ifdef CONFIG_WPS
-DBUS_OBJS += dbus/dbus_new_handlers_wps.o
-endif
-ifndef DBUS_LIBS
-DBUS_LIBS := $(shell pkg-config --libs dbus-1)
-endif
-ifndef DBUS_INCLUDE
-DBUS_INCLUDE := $(shell pkg-config --cflags dbus-1)
-endif
-ifdef CONFIG_CTRL_IFACE_DBUS_INTRO
-DBUS_OBJS += dbus/dbus_new_introspect.o
-DBUS_CFLAGS += -DCONFIG_CTRL_IFACE_DBUS_INTRO
-endif
-DBUS_CFLAGS += $(DBUS_INCLUDE)
-endif
-
-ifdef DBUS
-DBUS_CFLAGS += -DCONFIG_DBUS
-DBUS_OBJS += dbus/dbus_common.o
-endif
-
-OBJS += $(DBUS_OBJS)
-CFLAGS += $(DBUS_CFLAGS)
-LIBS += $(DBUS_LIBS)
-
-ifdef CONFIG_READLINE
-CFLAGS += -DCONFIG_READLINE
-LIBS_c += -lncurses -lreadline
-endif
-
-ifdef CONFIG_NATIVE_WINDOWS
-CFLAGS += -DCONFIG_NATIVE_WINDOWS
-LIBS += -lws2_32 -lgdi32 -lcrypt32
-LIBS_c += -lws2_32
-LIBS_p += -lws2_32 -lgdi32
-ifeq ($(CONFIG_CRYPTO), cryptoapi)
-LIBS_p += -lcrypt32
-endif
-endif
-
-ifdef CONFIG_NO_STDOUT_DEBUG
-CFLAGS += -DCONFIG_NO_STDOUT_DEBUG
-ifndef CONFIG_CTRL_IFACE
-CFLAGS += -DCONFIG_NO_WPA_MSG
-endif
-endif
-
-ifdef CONFIG_IPV6
-# for eapol_test only
-CFLAGS += -DCONFIG_IPV6
-endif
-
-ifdef NEED_BASE64
-OBJS += ../src/utils/base64.o
-endif
-
-ifdef NEED_SME
-NEED_80211_COMMON=y
-OBJS += sme.o
-CFLAGS += -DCONFIG_SME
-endif
-
-ifdef CONFIG_CLIENT_MLME
-OBJS += mlme.o
-CFLAGS += -DCONFIG_CLIENT_MLME
-NEED_80211_COMMON=y
-endif
-
-ifdef NEED_80211_COMMON
-OBJS += ../src/common/ieee802_11_common.o
-endif
-
-ifdef NEED_EAP_COMMON
-OBJS += ../src/eap_common/eap_common.o
-endif
-
-ifndef CONFIG_MAIN
-CONFIG_MAIN=main
-endif
-
-ifdef CONFIG_DEBUG_SYSLOG
-CFLAGS += -DCONFIG_DEBUG_SYSLOG
-endif
-
-ifdef CONFIG_DEBUG_FILE
-CFLAGS += -DCONFIG_DEBUG_FILE
-endif
-
-ifdef CONFIG_DELAYED_MIC_ERROR_REPORT
-CFLAGS += -DCONFIG_DELAYED_MIC_ERROR_REPORT
-endif
-
-ifdef CONFIG_FIPS
-CFLAGS += -DCONFIG_FIPS
-endif
-
-OBJS += $(SHA1OBJS) $(DESOBJS)
-
-OBJS_p += $(SHA1OBJS)
-
-ifdef CONFIG_BGSCAN_SIMPLE
-CFLAGS += -DCONFIG_BGSCAN_SIMPLE
-OBJS += bgscan_simple.o
-NEED_BGSCAN=y
-endif
-
-ifdef NEED_BGSCAN
-CFLAGS += -DCONFIG_BGSCAN
-OBJS += bgscan.o
-endif
-
-OBJS_wpa_rm := ctrl_iface.o mlme.o ctrl_iface_unix.o
-OBJS_wpa := $(filter-out $(OBJS_wpa_rm),$(OBJS)) $(OBJS_h) tests/test_wpa.o
-ifdef CONFIG_AUTHENTICATOR
-OBJS_wpa += tests/link_test.o
-endif
-OBJS_wpa += $(OBJS_l2)
-OBJS += wpa_supplicant.o events.o blacklist.o wpas_glue.o scan.o
-OBJS_t := $(OBJS) $(OBJS_l2) eapol_test.o
-OBJS_t += ../src/radius/radius_client.o
-OBJS_t += ../src/radius/radius.o
-ifndef CONFIG_AP
-OBJS_t += ../src/utils/ip_addr.o
-endif
-OBJS_t2 := $(OBJS) $(OBJS_l2) preauth_test.o
-OBJS += $(CONFIG_MAIN).o
-
-ifdef CONFIG_PRIVSEP
-OBJS_priv += $(OBJS_d) ../src/drivers/drivers.o
-OBJS_priv += $(OBJS_l2)
-OBJS_priv += ../src/utils/os_$(CONFIG_OS).o
-OBJS_priv += ../src/utils/$(CONFIG_ELOOP).o
-OBJS_priv += ../src/utils/common.o
-OBJS_priv += ../src/utils/wpa_debug.o
-OBJS_priv += ../src/utils/wpabuf.o
-OBJS_priv += wpa_priv.o
-ifdef CONFIG_DRIVER_TEST
-OBJS_priv += $(SHA1OBJS)
-OBJS_priv += $(MD5OBJS)
-ifeq ($(CONFIG_TLS), openssl)
-OBJS_priv += ../src/crypto/crypto_openssl.o
-endif
-ifeq ($(CONFIG_TLS), gnutls)
-OBJS_priv += ../src/crypto/crypto_gnutls.o
-endif
-ifeq ($(CONFIG_TLS), nss)
-OBJS_priv += ../src/crypto/crypto_nss.o
-endif
-ifeq ($(CONFIG_TLS), internal)
-ifeq ($(CONFIG_CRYPTO), libtomcrypt)
-OBJS_priv += ../src/crypto/crypto_libtomcrypt.o
-else
-OBJS_priv += ../src/crypto/crypto_internal.o
-endif
-endif
-endif # CONFIG_DRIVER_TEST
-OBJS += ../src/l2_packet/l2_packet_privsep.o
-OBJS += ../src/drivers/driver_privsep.o
-EXTRA_progs += wpa_priv
-else
-OBJS += $(OBJS_d) ../src/drivers/drivers.o
-OBJS += $(OBJS_l2)
-endif
-
-ifdef CONFIG_NDIS_EVENTS_INTEGRATED
-CFLAGS += -DCONFIG_NDIS_EVENTS_INTEGRATED
-OBJS += ../src/drivers/ndis_events.o
-EXTRALIBS += -loleaut32 -lole32 -luuid
-ifdef PLATFORMSDKLIB
-EXTRALIBS += $(PLATFORMSDKLIB)/WbemUuid.Lib
-else
-EXTRALIBS += WbemUuid.Lib
-endif
-endif
-
-ifndef LDO
-LDO=$(CC)
-endif
-
-dynamic_eap_methods: $(EAPDYN)
-
-../src/drivers/build.wpa_supplicant:
- @if [ -f ../src/drivers/build.hostapd ]; then \
- $(MAKE) -C ../src/drivers clean; \
- fi
- @touch ../src/drivers/build.wpa_supplicant
-
-BCHECK=../src/drivers/build.wpa_supplicant
-
-wpa_priv: $(BCHECK) $(OBJS_priv)
- $(LDO) $(LDFLAGS) -o wpa_priv $(OBJS_priv) $(LIBS)
-
-wpa_supplicant: .config $(BCHECK) $(OBJS) $(EXTRA_progs)
- $(LDO) $(LDFLAGS) -o wpa_supplicant $(OBJS) $(LIBS) $(EXTRALIBS)
-
-eapol_test: .config $(OBJS_t)
- $(LDO) $(LDFLAGS) -o eapol_test $(OBJS_t) $(LIBS)
-
-preauth_test: .config $(OBJS_t2)
- $(LDO) $(LDFLAGS) -o preauth_test $(OBJS_t2) $(LIBS)
-
-wpa_passphrase: $(OBJS_p)
- $(LDO) $(LDFLAGS) -o wpa_passphrase $(OBJS_p) $(LIBS_p)
-
-wpa_cli: $(OBJS_c)
- $(LDO) $(LDFLAGS) -o wpa_cli $(OBJS_c) $(LIBS_c)
-
-link_test: $(OBJS) $(OBJS_h) tests/link_test.o
- $(LDO) $(LDFLAGS) -o link_test $(OBJS) $(OBJS_h) tests/link_test.o $(LIBS)
-
-test_wpa: $(OBJS_wpa) $(OBJS_h)
- $(LDO) $(LDFLAGS) -o test_wpa $(OBJS_wpa) $(LIBS)
-
-win_if_list: win_if_list.c
- $(LDO) $(LDFLAGS) -o $@ win_if_list.c $(CFLAGS) $(LIBS_w)
-
-eap_psk.so: ../src/eap_peer/eap_psk.c ../src/eap_common/eap_psk_common.c
- $(CC) $(LDFLAGS) -o $@ $(CFLAGS) -shared -rdynamic -fPIC $^ \
- -Deap_peer_psk_register=eap_peer_method_dynamic_init
-
-eap_pax.so: ../src/eap_peer/eap_pax.c ../src/eap_common/eap_pax_common.c
- $(CC) $(LDFLAGS) -o $@ $(CFLAGS) -shared -rdynamic -fPIC $^ \
- -Deap_peer_pax_register=eap_peer_method_dynamic_init
-
-eap_sake.so: ../src/eap_peer/eap_sake.c ../src/eap_common/eap_sake_common.c
- $(CC) $(LDFLAGS) -o $@ $(CFLAGS) -shared -rdynamic -fPIC $^ \
- -Deap_peer_sake_register=eap_peer_method_dynamic_init
-
-eap_wsc.so: ../src/eap_peer/eap_wsc.c ../src/eap_common/eap_wsc_common.c ../src/wps/wps.c
- $(CC) $(LDFLAGS) -o $@ $(CFLAGS) -shared -rdynamic -fPIC $^ \
- -Deap_peer_wsc_register=eap_peer_method_dynamic_init
-
-eap_ikev2.so: ../src/eap_peer/eap_ikev2.c ../src/eap_peer/ikev2.c ../src/eap_common/eap_ikev2_common.o ../src/eap_common/ikev2_common.c
- $(CC) $(LDFLAGS) -o $@ $(CFLAGS) -shared -rdynamic -fPIC $^ \
- -Deap_peer_ikev2_register=eap_peer_method_dynamic_init
-
-%.so: %.c
- $(CC) $(LDFLAGS) -o $@ $(CFLAGS) -shared -rdynamic -fPIC $< \
- -D$(*F:eap_%=eap_peer_%)_register=eap_peer_method_dynamic_init
-
-Q=@
-E=echo
-ifeq ($(V), 1)
-Q=
-E=true
-endif
-
-%.o: %.c
- $(Q)$(CC) -c -o $@ $(CFLAGS) $<
- @$(E) " CC " $<
-
-wpa_supplicant.exe: wpa_supplicant
- mv -f $< $@
-wpa_cli.exe: wpa_cli
- mv -f $< $@
-wpa_passphrase.exe: wpa_passphrase
- mv -f $< $@
-win_if_list.exe: win_if_list
- mv -f $< $@
-eapol_test.exe: eapol_test
- mv -f $< $@
-
-WINALL=wpa_supplicant.exe wpa_cli.exe wpa_passphrase.exe win_if_list.exe
-
-windows-bin: $(WINALL)
- $(STRIP) $(WINALL)
-
-wpa_gui/Makefile:
- qmake -o wpa_gui/Makefile wpa_gui/wpa_gui.pro
-
-wpa_gui: wpa_gui/Makefile
- $(MAKE) -C wpa_gui
-
-wpa_gui-qt4/Makefile:
- qmake -o wpa_gui-qt4/Makefile wpa_gui-qt4/wpa_gui.pro
-
-wpa_gui-qt4/lang/wpa_gui_de.qm: wpa_gui-qt4/lang/wpa_gui_de.ts
- lrelease wpa_gui-qt4/wpa_gui.pro
-
-wpa_gui-qt4: wpa_gui-qt4/Makefile wpa_gui-qt4/lang/wpa_gui_de.qm
- $(MAKE) -C wpa_gui-qt4
-
-TEST_EAP_SIM_COMMON_OBJS = $(SHA1OBJS) $(MD5OBJS) \
- ../src/utils/common.o ../src/utils/os_unix.o \
- ../src/utils/wpa_debug.o $(AESOBJS) \
- tests/test_eap_sim_common.o
-test-eap_sim_common: $(TEST_EAP_SIM_COMMON_OBJS)
- $(LDO) $(LDFLAGS) -o $@ $(TEST_EAP_SIM_COMMON_OBJS) $(LIBS)
- ./test-eap_sim_common
- rm test-eap_sim_common
-
-tests: test-eap_sim_common
-
-clean:
- $(MAKE) -C ../src clean
- $(MAKE) -C dbus clean
- rm -f core *~ *.o *.d eap_*.so $(ALL) $(WINALL) eapol_test preauth_test
- rm -f wpa_priv
-
--include $(OBJS:%.o=%.d)
diff --git a/contrib/wpa/wpa_supplicant/README b/contrib/wpa/wpa_supplicant/README
index 45c8bae..a06e5c1 100644
--- a/contrib/wpa/wpa_supplicant/README
+++ b/contrib/wpa/wpa_supplicant/README
@@ -1,37 +1,22 @@
WPA Supplicant
==============
-Copyright (c) 2003-2010, Jouni Malinen <j@w1.fi> and contributors
+Copyright (c) 2003-2012, Jouni Malinen <j@w1.fi> and contributors
All Rights Reserved.
-This program is dual-licensed under both the GPL version 2 and BSD
-license. Either license may be used at your option.
+This program is licensed under the BSD license (the one with
+advertisement clause removed).
+
+If you are submitting changes to the project, please see CONTRIBUTIONS
+file for more instructions.
License
-------
-GPL v2:
-
-This program is free software; you can redistribute it and/or modify
-it under the terms of the GNU General Public License version 2 as
-published by the Free Software Foundation.
-
-This program is distributed in the hope that it will be useful,
-but WITHOUT ANY WARRANTY; without even the implied warranty of
-MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the
-GNU General Public License for more details.
-
-You should have received a copy of the GNU General Public License
-along with this program; if not, write to the Free Software
-Foundation, Inc., 51 Franklin St, Fifth Floor, Boston, MA 02110-1301 USA
-
-(this copy of the license is in COPYING file)
-
-
-Alternatively, this software may be distributed, used, and modified
-under the terms of BSD license:
+This software may be distributed, used, and modified under the terms of
+BSD license:
Redistribution and use in source and binary forms, with or without
modification, are permitted provided that the following conditions are
@@ -138,55 +123,6 @@ Current hardware/software requirements:
default option to start with before falling back to driver specific
interface.
- Host AP driver for Prism2/2.5/3 (development snapshot/v0.2.x)
- (http://hostap.epitest.fi/)
- Driver need to be set in Managed mode ('iwconfig wlan0 mode managed').
- Please note that station firmware version needs to be 1.7.0 or newer
- to work in WPA mode.
-
- Linuxant DriverLoader (http://www.linuxant.com/driverloader/)
- with Windows NDIS driver for your wlan card supporting WPA.
-
- Agere Systems Inc. Linux Driver
- (http://www.agere.com/support/drivers/)
- Please note that the driver interface file (driver_hermes.c) and
- hardware specific include files are not included in the
- wpa_supplicant distribution. You will need to copy these from the
- source package of the Agere driver.
-
- madwifi driver for cards based on Atheros chip set (ar521x)
- (http://sourceforge.net/projects/madwifi/)
- Please note that you will need to modify the wpa_supplicant .config
- file to use the correct path for the madwifi driver root directory
- (CFLAGS += -I../madwifi/wpa line in example defconfig).
-
- ATMEL AT76C5XXx driver for USB and PCMCIA cards
- (http://atmelwlandriver.sourceforge.net/).
-
- Linux ndiswrapper (http://ndiswrapper.sourceforge.net/) with
- Windows NDIS driver.
-
- Broadcom wl.o driver (old version only)
- This is a generic Linux driver for Broadcom IEEE 802.11a/g cards.
- However, it is proprietary driver that is not publicly available
- except for couple of exceptions, mainly Broadcom-based APs/wireless
- routers that use Linux. The driver binary can be downloaded, e.g.,
- from Linksys support site (http://www.linksys.com/support/gpl.asp)
- for Linksys WRT54G. The GPL tarball includes cross-compiler and
- the needed header file, wlioctl.h, for compiling wpa_supplicant.
- This driver support in wpa_supplicant is expected to work also with
- other devices based on Broadcom driver (assuming the driver includes
- client mode support). Please note that the newer Broadcom driver
- ("hybrid Linux driver") supports Linux wireless extensions and does
- not need (or even work) with the specific driver wrapper. Use -Dwext
- with that driver.
-
- Intel ipw2100 driver
- (http://sourceforge.net/projects/ipw2100/)
-
- Intel ipw2200 driver
- (http://sourceforge.net/projects/ipw2200/)
-
In theory, any driver that supports Linux wireless extensions can be
used with IEEE 802.1X (i.e., not WPA) when using ap_scan=0 option in
configuration file.
@@ -363,7 +299,7 @@ and a list of available options and additional notes.
The build time configuration can be used to select only the needed
features and limit the binary size and requirements for external
libraries. The main configuration parts are the selection of which
-driver interfaces (e.g., hostap, madwifi, ..) and which authentication
+driver interfaces (e.g., nl80211, wext, ..) and which authentication
methods (e.g., EAP-TLS, EAP-PEAP, ..) are included.
Following build time configuration options are used to control IEEE
@@ -396,32 +332,18 @@ authentication algorithm (for EAP-SIM/EAP-AKA). This requires pcsc-lite
CONFIG_PCSC=y
Following options can be added to .config to select which driver
-interfaces are included. Hermes driver interface needs to be downloaded
-from Agere (see above).
+interfaces are included.
-CONFIG_DRIVER_HOSTAP=y
-CONFIG_DRIVER_HERMES=y
-CONFIG_DRIVER_MADWIFI=y
-CONFIG_DRIVER_ATMEL=y
+CONFIG_DRIVER_NL80211=y
CONFIG_DRIVER_WEXT=y
-CONFIG_DRIVER_RALINK=y
-CONFIG_DRIVER_NDISWRAPPER=y
-CONFIG_DRIVER_BROADCOM=y
-CONFIG_DRIVER_IPW=y
CONFIG_DRIVER_BSD=y
CONFIG_DRIVER_NDIS=y
-Following example includes all features and driver interfaces that are
-included in the wpa_supplicant package:
+Following example includes some more features and driver interfaces that
+are included in the wpa_supplicant package:
-CONFIG_DRIVER_HOSTAP=y
-CONFIG_DRIVER_HERMES=y
-CONFIG_DRIVER_MADWIFI=y
-CONFIG_DRIVER_ATMEL=y
+CONFIG_DRIVER_NL80211=y
CONFIG_DRIVER_WEXT=y
-CONFIG_DRIVER_NDISWRAPPER=y
-CONFIG_DRIVER_BROADCOM=y
-CONFIG_DRIVER_IPW=y
CONFIG_DRIVER_BSD=y
CONFIG_DRIVER_NDIS=y
CONFIG_IEEE8021X_EAPOL=y
@@ -503,7 +425,7 @@ options:
-K = include keys (passwords, etc.) in debug output
-t = include timestamp in debug messages
-h = show this help text
- -L = show license (GPL and BSD)
+ -L = show license (BSD)
-p = driver parameters
-P = PID file
-q = decrease debugging verbosity (-qq even less)
@@ -514,16 +436,7 @@ options:
-N = start describing new interface
drivers:
- hostap = Host AP driver (Intersil Prism2/2.5/3) [default]
- (this can also be used with Linuxant DriverLoader)
- hermes = Agere Systems Inc. driver (Hermes-I/Hermes-II)
- madwifi = MADWIFI 802.11 support (Atheros, etc.) (deprecated; use wext)
- atmel = ATMEL AT76C5XXx (USB, PCMCIA)
wext = Linux wireless extensions (generic)
- ralink = Ralink Client driver
- ndiswrapper = Linux ndiswrapper (deprecated; use wext)
- broadcom = Broadcom wl.o driver
- ipw = Intel ipw2100/2200 driver (old; use wext with Linux 2.6.13 or newer)
wired = wpa_supplicant wired Ethernet driver
roboswitch = wpa_supplicant Broadcom switch driver
bsd = BSD 802.11 support (Atheros, etc.)
@@ -556,15 +469,15 @@ separated with -N argument. As an example, following command would
start wpa_supplicant for two interfaces:
wpa_supplicant \
- -c wpa1.conf -i wlan0 -D hostap -N \
- -c wpa2.conf -i ath0 -D madwifi
+ -c wpa1.conf -i wlan0 -D nl80211 -N \
+ -c wpa2.conf -i wlan1 -D wext
If the interface is added in a Linux bridge (e.g., br0), the bridge
interface needs to be configured to wpa_supplicant in addition to the
main interface:
-wpa_supplicant -cw.conf -Dmadwifi -iath0 -bbr0
+wpa_supplicant -cw.conf -Dwext -iwlan0 -bbr0
Configuration file
@@ -894,13 +807,14 @@ script:
IFNAME=$1
CMD=$2
-if [ "$CMD" == "CONNECTED" ]; then
+if [ "$CMD" = "CONNECTED" ]; then
SSID=`wpa_cli -i$IFNAME status | grep ^ssid= | cut -f2- -d=`
# configure network, signal DHCP client, etc.
fi
-if [ "$CMD" == "DISCONNECTED" ]; then
+if [ "$CMD" = "DISCONNECTED" ]; then
# remove network configuration, if needed
+ SSID=
fi
diff --git a/contrib/wpa/wpa_supplicant/README-HS20 b/contrib/wpa/wpa_supplicant/README-HS20
new file mode 100644
index 0000000..5669c55
--- /dev/null
+++ b/contrib/wpa/wpa_supplicant/README-HS20
@@ -0,0 +1,475 @@
+wpa_supplicant and Hotspot 2.0
+==============================
+
+This document describe how the IEEE 802.11u Interworking and Wi-Fi
+Hotspot 2.0 (Release 1) implementation in wpa_supplicant can be
+configured and how an external component on the client e.g., management
+GUI or Wi-Fi framework) is used to manage this functionality.
+
+
+Introduction to Wi-Fi Hotspot 2.0
+---------------------------------
+
+Hotspot 2.0 is the name of the Wi-Fi Alliance specification that is used
+in the Wi-Fi CERTIFIED Passpoint<TM> program. More information about
+this is available in this white paper:
+
+http://www.wi-fi.org/knowledge-center/white-papers/wi-fi-certified-passpoint%E2%84%A2-new-program-wi-fi-alliance%C2%AE-enable-seamless
+
+The Hotspot 2.0 specification is also available from WFA:
+https://www.wi-fi.org/knowledge-center/published-specifications
+
+The core Interworking functionality (network selection, GAS/ANQP) were
+standardized in IEEE Std 802.11u-2011 which is now part of the IEEE Std
+802.11-2012.
+
+
+wpa_supplicant network selection
+--------------------------------
+
+Interworking support added option for configuring credentials that can
+work with multiple networks as an alternative to configuration of
+network blocks (e.g., per-SSID parameters). When requested to perform
+network selection, wpa_supplicant picks the highest priority enabled
+network block or credential. If a credential is picked (based on ANQP
+information from APs), a temporary network block is created
+automatically for the matching network. This temporary network block is
+used similarly to the network blocks that can be configured by the user,
+but it is not stored into the configuration file and is meant to be used
+only for temporary period of time since a new one can be created
+whenever needed based on ANQP information and the credential.
+
+By default, wpa_supplicant is not using automatic network selection
+unless requested explicitly with the interworking_select command. This
+can be changed with the auto_interworking=1 parameter to perform network
+selection automatically whenever trying to find a network for connection
+and none of the enabled network blocks match with the scan results. This
+case works similarly to "interworking_select auto", i.e., wpa_supplicant
+will internally determine which network or credential is going to be
+used based on configured priorities, scan results, and ANQP information.
+
+
+wpa_supplicant configuration
+----------------------------
+
+Interworking and Hotspot 2.0 functionality are optional components that
+need to be enabled in the wpa_supplicant build configuration
+(.config). This is done by adding following parameters into that file:
+
+CONFIG_INTERWORKING=y
+CONFIG_HS20=y
+
+It should be noted that this functionality requires a driver that
+supports GAS/ANQP operations. This uses the same design as P2P, i.e.,
+Action frame processing and building in user space within
+wpa_supplicant. The Linux nl80211 driver interface provides the needed
+functionality for this.
+
+
+There are number of run-time configuration parameters (e.g., in
+wpa_supplicant.conf when using the configuration file) that can be used
+to control Hotspot 2.0 operations.
+
+# Enable Interworking
+interworking=1
+
+# Enable Hotspot 2.0
+hs20=1
+
+# Parameters for controlling scanning
+
+# Homogenous ESS identifier
+# If this is set, scans will be used to request response only from BSSes
+# belonging to the specified Homogeneous ESS. This is used only if interworking
+# is enabled.
+#hessid=00:11:22:33:44:55
+
+# Access Network Type
+# When Interworking is enabled, scans can be limited to APs that advertise the
+# specified Access Network Type (0..15; with 15 indicating wildcard match).
+# This value controls the Access Network Type value in Probe Request frames.
+#access_network_type=15
+
+# Automatic network selection behavior
+# 0 = do not automatically go through Interworking network selection
+# (i.e., require explicit interworking_select command for this; default)
+# 1 = perform Interworking network selection if one or more
+# credentials have been configured and scan did not find a
+# matching network block
+#auto_interworking=0
+
+
+Credentials can be pre-configured for automatic network selection:
+
+# credential block
+#
+# Each credential used for automatic network selection is configured as a set
+# of parameters that are compared to the information advertised by the APs when
+# interworking_select and interworking_connect commands are used.
+#
+# credential fields:
+#
+# priority: Priority group
+# By default, all networks and credentials get the same priority group
+# (0). This field can be used to give higher priority for credentials
+# (and similarly in struct wpa_ssid for network blocks) to change the
+# Interworking automatic networking selection behavior. The matching
+# network (based on either an enabled network block or a credential)
+# with the highest priority value will be selected.
+#
+# pcsc: Use PC/SC and SIM/USIM card
+#
+# realm: Home Realm for Interworking
+#
+# username: Username for Interworking network selection
+#
+# password: Password for Interworking network selection
+#
+# ca_cert: CA certificate for Interworking network selection
+#
+# client_cert: File path to client certificate file (PEM/DER)
+# This field is used with Interworking networking selection for a case
+# where client certificate/private key is used for authentication
+# (EAP-TLS). Full path to the file should be used since working
+# directory may change when wpa_supplicant is run in the background.
+#
+# Alternatively, a named configuration blob can be used by setting
+# this to blob://blob_name.
+#
+# private_key: File path to client private key file (PEM/DER/PFX)
+# When PKCS#12/PFX file (.p12/.pfx) is used, client_cert should be
+# commented out. Both the private key and certificate will be read
+# from the PKCS#12 file in this case. Full path to the file should be
+# used since working directory may change when wpa_supplicant is run
+# in the background.
+#
+# Windows certificate store can be used by leaving client_cert out and
+# configuring private_key in one of the following formats:
+#
+# cert://substring_to_match
+#
+# hash://certificate_thumbprint_in_hex
+#
+# For example: private_key="hash://63093aa9c47f56ae88334c7b65a4"
+#
+# Note that when running wpa_supplicant as an application, the user
+# certificate store (My user account) is used, whereas computer store
+# (Computer account) is used when running wpasvc as a service.
+#
+# Alternatively, a named configuration blob can be used by setting
+# this to blob://blob_name.
+#
+# private_key_passwd: Password for private key file
+#
+# imsi: IMSI in <MCC> | <MNC> | '-' | <MSIN> format
+#
+# milenage: Milenage parameters for SIM/USIM simulator in <Ki>:<OPc>:<SQN>
+# format
+#
+# domain: Home service provider FQDN
+# This is used to compare against the Domain Name List to figure out
+# whether the AP is operated by the Home SP.
+#
+# roaming_consortium: Roaming Consortium OI
+# If roaming_consortium_len is non-zero, this field contains the
+# Roaming Consortium OI that can be used to determine which access
+# points support authentication with this credential. This is an
+# alternative to the use of the realm parameter. When using Roaming
+# Consortium to match the network, the EAP parameters need to be
+# pre-configured with the credential since the NAI Realm information
+# may not be available or fetched.
+#
+# eap: Pre-configured EAP method
+# This optional field can be used to specify which EAP method will be
+# used with this credential. If not set, the EAP method is selected
+# automatically based on ANQP information (e.g., NAI Realm).
+#
+# phase1: Pre-configure Phase 1 (outer authentication) parameters
+# This optional field is used with like the 'eap' parameter.
+#
+# phase2: Pre-configure Phase 2 (inner authentication) parameters
+# This optional field is used with like the 'eap' parameter.
+#
+# excluded_ssid: Excluded SSID
+# This optional field can be used to excluded specific SSID(s) from
+# matching with the network. Multiple entries can be used to specify more
+# than one SSID.
+#
+# for example:
+#
+#cred={
+# realm="example.com"
+# username="user@example.com"
+# password="password"
+# ca_cert="/etc/wpa_supplicant/ca.pem"
+# domain="example.com"
+#}
+#
+#cred={
+# imsi="310026-000000000"
+# milenage="90dca4eda45b53cf0f12d7c9c3bc6a89:cb9cccc4b9258e6dca4760379fb82"
+#}
+#
+#cred={
+# realm="example.com"
+# username="user"
+# password="password"
+# ca_cert="/etc/wpa_supplicant/ca.pem"
+# domain="example.com"
+# roaming_consortium=223344
+# eap=TTLS
+# phase2="auth=MSCHAPV2"
+#}
+
+
+Control interface
+-----------------
+
+wpa_supplicant provides a control interface that can be used from
+external programs to manage various operations. The included command
+line tool, wpa_cli, can be used for manual testing with this interface.
+
+Following wpa_cli interactive mode commands show some examples of manual
+operations related to Hotspot 2.0:
+
+Remove configured networks and credentials:
+
+> remove_network all
+OK
+> remove_cred all
+OK
+
+
+Add a username/password credential:
+
+> add_cred
+0
+> set_cred 0 realm "mail.example.com"
+OK
+> set_cred 0 username "username"
+OK
+> set_cred 0 password "password"
+OK
+> set_cred 0 priority 1
+OK
+
+Add a SIM credential using a simulated SIM/USIM card for testing:
+
+> add_cred
+1
+> set_cred 1 imsi "23456-0000000000"
+OK
+> set_cred 1 milenage "90dca4eda45b53cf0f12d7c9c3bc6a89:cb9cccc4b9258e6dca4760379fb82581:000000000123"
+OK
+> set_cred 1 priority 1
+OK
+
+Note: the return value of add_cred is used as the first argument to
+the following set_cred commands.
+
+
+Add a WPA2-Enterprise network:
+
+> add_network
+0
+> set_network 0 key_mgmt WPA-EAP
+OK
+> set_network 0 ssid "enterprise"
+OK
+> set_network 0 eap TTLS
+OK
+> set_network 0 anonymous_identity "anonymous"
+OK
+> set_network 0 identity "user"
+OK
+> set_network 0 password "password"
+OK
+> set_network 0 priority 0
+OK
+> enable_network 0 no-connect
+OK
+
+
+Add an open network:
+
+> add_network
+3
+> set_network 3 key_mgmt NONE
+OK
+> set_network 3 ssid "coffee-shop"
+OK
+> select_network 3
+OK
+
+Note: the return value of add_network is used as the first argument to
+the following set_network commands.
+
+The preferred credentials/networks can be indicated with the priority
+parameter (1 is higher priority than 0).
+
+
+Interworking network selection can be started with interworking_select
+command. This instructs wpa_supplicant to run a network scan and iterate
+through the discovered APs to request ANQP information from the APs that
+advertise support for Interworking/Hotspot 2.0:
+
+> interworking_select
+OK
+<3>Starting ANQP fetch for 02:00:00:00:01:00
+<3>RX-ANQP 02:00:00:00:01:00 ANQP Capability list
+<3>RX-ANQP 02:00:00:00:01:00 Roaming Consortium list
+<3>RX-HS20-ANQP 02:00:00:00:01:00 HS Capability List
+<3>ANQP fetch completed
+<3>INTERWORKING-AP 02:00:00:00:01:00 type=unknown
+
+
+INTERWORKING-AP event messages indicate the APs that support network
+selection and for which there is a matching
+credential. interworking_connect command can be used to select a network
+to connect with:
+
+
+> interworking_connect 02:00:00:00:01:00
+OK
+<3>CTRL-EVENT-SCAN-RESULTS
+<3>SME: Trying to authenticate with 02:00:00:00:01:00 (SSID='Example Network' freq=2412 MHz)
+<3>Trying to associate with 02:00:00:00:01:00 (SSID='Example Network' freq=2412 MHz)
+<3>Associated with 02:00:00:00:01:00
+<3>CTRL-EVENT-EAP-STARTED EAP authentication started
+<3>CTRL-EVENT-EAP-PROPOSED-METHOD vendor=0 method=21
+<3>CTRL-EVENT-EAP-METHOD EAP vendor 0 method 21 (TTLS) selected
+<3>CTRL-EVENT-EAP-SUCCESS EAP authentication completed successfully
+<3>WPA: Key negotiation completed with 02:00:00:00:01:00 [PTK=CCMP GTK=CCMP]
+<3>CTRL-EVENT-CONNECTED - Connection to 02:00:00:00:01:00 completed (auth) [id=0 id_str=]
+
+
+wpa_supplicant creates a temporary network block for the selected
+network based on the configured credential and ANQP information from the
+AP:
+
+> list_networks
+network id / ssid / bssid / flags
+0 Example Network any [CURRENT]
+> get_network 0 key_mgmt
+WPA-EAP
+> get_network 0 eap
+TTLS
+
+
+Alternatively to using an external program to select the network,
+"interworking_select auto" command can be used to request wpa_supplicant
+to select which network to use based on configured priorities:
+
+
+> remove_network all
+OK
+<3>CTRL-EVENT-DISCONNECTED bssid=02:00:00:00:01:00 reason=1 locally_generated=1
+> interworking_select auto
+OK
+<3>Starting ANQP fetch for 02:00:00:00:01:00
+<3>RX-ANQP 02:00:00:00:01:00 ANQP Capability list
+<3>RX-ANQP 02:00:00:00:01:00 Roaming Consortium list
+<3>RX-HS20-ANQP 02:00:00:00:01:00 HS Capability List
+<3>ANQP fetch completed
+<3>INTERWORKING-AP 02:00:00:00:01:00 type=unknown
+<3>CTRL-EVENT-SCAN-RESULTS
+<3>SME: Trying to authenticate with 02:00:00:00:01:00 (SSID='Example Network' freq=2412 MHz)
+<3>Trying to associate with 02:00:00:00:01:00 (SSID='Example Network' freq=2412 MHz)
+<3>Associated with 02:00:00:00:01:00
+<3>CTRL-EVENT-EAP-STARTED EAP authentication started
+<3>CTRL-EVENT-EAP-PROPOSED-METHOD vendor=0 method=21
+<3>CTRL-EVENT-EAP-METHOD EAP vendor 0 method 21 (TTLS) selected
+<3>CTRL-EVENT-EAP-SUCCESS EAP authentication completed successfully
+<3>WPA: Key negotiation completed with 02:00:00:00:01:00 [PTK=CCMP GTK=CCMP]
+<3>CTRL-EVENT-CONNECTED - Connection to 02:00:00:00:01:00 completed (reauth) [id=0 id_str=]
+
+
+The connection status can be shown with the status command:
+
+> status
+bssid=02:00:00:00:01:00
+ssid=Example Network
+id=0
+mode=station
+pairwise_cipher=CCMP <--- link layer security indication
+group_cipher=CCMP
+key_mgmt=WPA2/IEEE 802.1X/EAP
+wpa_state=COMPLETED
+p2p_device_address=02:00:00:00:00:00
+address=02:00:00:00:00:00
+hs20=1 <--- HS 2.0 indication
+Supplicant PAE state=AUTHENTICATED
+suppPortStatus=Authorized
+EAP state=SUCCESS
+selectedMethod=21 (EAP-TTLS)
+EAP TLS cipher=AES-128-SHA
+EAP-TTLSv0 Phase2 method=PAP
+
+
+> status
+bssid=02:00:00:00:02:00
+ssid=coffee-shop
+id=3
+mode=station
+pairwise_cipher=NONE
+group_cipher=NONE
+key_mgmt=NONE
+wpa_state=COMPLETED
+p2p_device_address=02:00:00:00:00:00
+address=02:00:00:00:00:00
+
+
+Note: The Hotspot 2.0 indication is shown as "hs20=1" in the status
+command output. Link layer security is indicated with the
+pairwise_cipher (CCMP = secure, NONE = no encryption used).
+
+
+Also the scan results include the Hotspot 2.0 indication:
+
+> scan_results
+bssid / frequency / signal level / flags / ssid
+02:00:00:00:01:00 2412 -30 [WPA2-EAP-CCMP][ESS][HS20] Example Network
+
+
+ANQP information for the BSS can be fetched using the BSS command:
+
+> bss 02:00:00:00:01:00
+id=1
+bssid=02:00:00:00:01:00
+freq=2412
+beacon_int=100
+capabilities=0x0411
+qual=0
+noise=-92
+level=-30
+tsf=1345573286517276
+age=105
+ie=000f4578616d706c65204e6574776f726b010882848b960c1218240301012a010432043048606c30140100000fac040100000fac040100000fac0100007f04000000806b091e07010203040506076c027f006f1001531122331020304050010203040506dd05506f9a1000
+flags=[WPA2-EAP-CCMP][ESS][HS20]
+ssid=Example Network
+anqp_roaming_consortium=031122330510203040500601020304050603fedcba
+
+
+ANQP queries can also be requested with the anqp_get and hs20_anqp_get
+commands:
+
+> anqp_get 02:00:00:00:01:00 261
+OK
+<3>RX-ANQP 02:00:00:00:01:00 Roaming Consortium list
+> hs20_anqp_get 02:00:00:00:01:00 2
+OK
+<3>RX-HS20-ANQP 02:00:00:00:01:00 HS Capability List
+
+In addition, fetch_anqp command can be used to request similar set of
+ANQP queries to be done as is run as part of interworking_select:
+
+> scan
+OK
+<3>CTRL-EVENT-SCAN-RESULTS
+> fetch_anqp
+OK
+<3>Starting ANQP fetch for 02:00:00:00:01:00
+<3>RX-ANQP 02:00:00:00:01:00 ANQP Capability list
+<3>RX-ANQP 02:00:00:00:01:00 Roaming Consortium list
+<3>RX-HS20-ANQP 02:00:00:00:01:00 HS Capability List
+<3>ANQP fetch completed
diff --git a/contrib/wpa/wpa_supplicant/README-P2P b/contrib/wpa/wpa_supplicant/README-P2P
new file mode 100644
index 0000000..5e98c95
--- /dev/null
+++ b/contrib/wpa/wpa_supplicant/README-P2P
@@ -0,0 +1,560 @@
+wpa_supplicant and Wi-Fi P2P
+============================
+
+This document describes how the Wi-Fi P2P implementation in
+wpa_supplicant can be configured and how an external component on the
+client (e.g., management GUI) is used to enable WPS enrollment and
+registrar registration.
+
+
+Introduction to Wi-Fi P2P
+-------------------------
+
+TODO
+
+More information about Wi-Fi P2P is available from Wi-Fi Alliance:
+http://www.wi-fi.org/Wi-Fi_Direct.php
+
+
+wpa_supplicant implementation
+-----------------------------
+
+TODO
+
+
+wpa_supplicant configuration
+----------------------------
+
+Wi-Fi P2P is an optional component that needs to be enabled in the
+wpa_supplicant build configuration (.config). Here is an example
+configuration that includes Wi-Fi P2P support and Linux nl80211
+-based driver interface:
+
+CONFIG_DRIVER_NL80211=y
+CONFIG_CTRL_IFACE=y
+CONFIG_P2P=y
+CONFIG_AP=y
+CONFIG_WPS=y
+
+
+In run-time configuration file (wpa_supplicant.conf), some parameters
+for P2P may be set. In order to make the devices easier to recognize,
+device_name and device_type should be specified. For example,
+something like this should be included:
+
+ctrl_interface=/var/run/wpa_supplicant
+device_name=My P2P Device
+device_type=1-0050F204-1
+
+
+wpa_cli
+-------
+
+Actual Wi-Fi P2P operations are requested during runtime. These can be
+done for example using wpa_cli (which is described below) or a GUI
+like wpa_gui-qt4.
+
+
+wpa_cli starts in interactive mode if no command string is included on
+the command line. By default, it will select the first network interface
+that it can find (and that wpa_supplicant controls). If more than one
+interface is in use, it may be necessary to select one of the explicitly
+by adding -i argument on the command line (e.g., 'wpa_cli -i wlan1').
+
+Most of the P2P operations are done on the main interface (e.g., the
+interface that is automatically added when the driver is loaded, e.g.,
+wlan0). When using a separate virtual interface for group operations
+(e.g., wlan1), the control interface for that group interface may need
+to be used for some operations (mainly WPS activation in GO). This may
+change in the future so that all the needed operations could be done
+over the main control interface.
+
+Device Discovery
+
+p2p_find [timeout in seconds] [type=<social|progressive>] \
+ [dev_id=<addr>] [delay=<search delay in ms>]
+
+The default behavior is to run a single full scan in the beginning and
+then scan only social channels. type=social will scan only social
+channels, i.e., it skips the initial full scan. type=progressive is
+like the default behavior, but it will scan through all the channels
+progressively one channel at the time in the Search state rounds. This
+will help in finding new groups or groups missed during the initial
+full scan.
+
+The optional dev_id option can be used to specify a single P2P peer to
+search for. The optional delay parameter can be used to request an extra
+delay to be used between search iterations (e.g., to free up radio
+resources for concurrent operations).
+
+p2p_listen [timeout in seconds]
+
+Start Listen-only state (become discoverable without searching for
+other devices). Optional parameter can be used to specify the duration
+for the Listen operation in seconds. This command may not be of that
+much use during normal operations and is mainly designed for
+testing. It can also be used to keep the device discoverable without
+having to maintain a group.
+
+p2p_stop_find
+
+Stop ongoing P2P device discovery or other operation (connect, listen
+mode).
+
+p2p_flush
+
+Flush P2P peer table and state.
+
+Group Formation
+
+p2p_prov_disc <peer device address> <display|keypad|pbc> [join|auto]
+
+Send P2P provision discovery request to the specified peer. The
+parameters for this command are the P2P device address of the peer and
+the desired configuration method. For example, "p2p_prov_disc
+02:01:02:03:04:05 display" would request the peer to display a PIN for
+us and "p2p_prov_disc 02:01:02:03:04:05 keypad" would request the peer
+to enter a PIN that we display.
+
+The optional "join" parameter can be used to indicate that this command
+is requesting an already running GO to prepare for a new client. This is
+mainly used with "display" to request it to display a PIN. The "auto"
+parameter can be used to request wpa_supplicant to automatically figure
+out whether the peer device is operating as a GO and if so, use
+join-a-group style PD instead of GO Negotiation style PD.
+
+p2p_connect <peer device address> <pbc|pin|PIN#> [display|keypad]
+ [persistent|persistent=<network id>] [join|auth]
+ [go_intent=<0..15>] [freq=<in MHz>] [ht40] [provdisc]
+
+Start P2P group formation with a discovered P2P peer. This includes
+optional group owner negotiation, group interface setup, provisioning,
+and establishing data connection.
+
+The <pbc|pin|PIN#> parameter specifies the WPS provisioning
+method. "pbc" string starts pushbutton method, "pin" string start PIN
+method using an automatically generated PIN (which will be returned as
+the command return code), PIN# means that a pre-selected PIN can be
+used (e.g., 12345670). [display|keypad] is used with PIN method
+to specify which PIN is used (display=dynamically generated random PIN
+from local display, keypad=PIN entered from peer display). "persistent"
+parameter can be used to request a persistent group to be formed. The
+"persistent=<network id>" alternative can be used to pre-populate
+SSID/passphrase configuration based on a previously used persistent
+group where this device was the GO. The previously used parameters will
+then be used if the local end becomes the GO in GO Negotiation (which
+can be forced with go_intent=15).
+
+"join" indicates that this is a command to join an existing group as a
+client. It skips the GO Negotiation part. This will send a Provision
+Discovery Request message to the target GO before associating for WPS
+provisioning.
+
+"auth" indicates that the WPS parameters are authorized for the peer
+device without actually starting GO Negotiation (i.e., the peer is
+expected to initiate GO Negotiation). This is mainly for testing
+purposes.
+
+"go_intent" can be used to override the default GO Intent for this GO
+Negotiation.
+
+"freq" can be used to set a forced operating channel (e.g., freq=2412
+to select 2.4 GHz channel 1).
+
+"provdisc" can be used to request a Provision Discovery exchange to be
+used prior to starting GO Negotiation as a workaround with some deployed
+P2P implementations that require this to allow the user to accept the
+connection.
+
+p2p_group_add [persistent|persistent=<network id>] [freq=<freq in MHz>] [ht40]
+
+Set up a P2P group owner manually (i.e., without group owner
+negotiation with a specific peer). This is also known as autonomous
+GO. Optional persistent=<network id> can be used to specify restart of
+a persistent group. Optional freq=<freq in MHz> can be used to force
+the GO to be started on a specific frequency. Special freq=2 or freq=5
+options can be used to request the best 2.4 GHz or 5 GHz band channel
+to be selected automatically.
+
+p2p_reject <peer device address>
+
+Reject connection attempt from a peer (specified with a device
+address). This is a mechanism to reject a pending GO Negotiation with
+a peer and request to automatically block any further connection or
+discovery of the peer.
+
+p2p_group_remove <group interface>
+
+Terminate a P2P group. If a new virtual network interface was used for
+the group, it will also be removed. The network interface name of the
+group interface is used as a parameter for this command.
+
+p2p_cancel
+
+Cancel an ongoing P2P group formation and joining-a-group related
+operation. This operations unauthorizes the specific peer device (if any
+had been authorized to start group formation), stops P2P find (if in
+progress), stops pending operations for join-a-group, and removes the
+P2P group interface (if one was used) that is in the WPS provisioning
+step. If the WPS provisioning step has been completed, the group is not
+terminated.
+
+Service Discovery
+
+p2p_serv_disc_req
+
+Schedule a P2P service discovery request. The parameters for this
+command are the device address of the peer device (or 00:00:00:00:00:00
+for wildcard query that is sent to every discovered P2P peer that
+supports service discovery) and P2P Service Query TLV(s) as hexdump. For
+example,
+
+p2p_serv_disc_req 00:00:00:00:00:00 02000001
+
+schedules a request for listing all available services of all service
+discovery protocols and requests this to be sent to all discovered
+peers (note: this can result in long response frames). The pending
+requests are sent during device discovery (see p2p_find).
+
+Only a single pending wildcard query is supported, but there can be
+multiple pending peer device specific queries (each will be sent in
+sequence whenever the peer is found).
+
+This command returns an identifier for the pending query (e.g.,
+"1f77628") that can be used to cancel the request. Directed requests
+will be automatically removed when the specified peer has replied to
+it.
+
+For UPnP, an alternative command format can be used to specify a
+single query TLV (i.e., a service discovery for a specific UPnP
+service):
+
+p2p_serv_disc_req 00:00:00:00:00:00 upnp <version hex> <ST: from M-SEARCH>
+
+For example:
+
+p2p_serv_disc_req 00:00:00:00:00:00 upnp 10 urn:schemas-upnp-org:device:InternetGatewayDevice:1
+
+Additional examples for queries:
+
+# list of all Bonjour services
+p2p_serv_disc_req 00:00:00:00:00:00 02000101
+
+# list of all UPnP services
+p2p_serv_disc_req 00:00:00:00:00:00 02000201
+
+# list of all WS-Discovery services
+p2p_serv_disc_req 00:00:00:00:00:00 02000301
+
+# list of all Bonjour and UPnP services
+p2p_serv_disc_req 00:00:00:00:00:00 0200010102000202
+
+# Apple File Sharing over TCP
+p2p_serv_disc_req 00:00:00:00:00:00 130001010b5f6166706f766572746370c00c000c01
+
+# Bonjour SSTH (supported service type hash)
+p2p_serv_disc_req 00:00:00:00:00:00 05000101000000
+
+# UPnP examples
+p2p_serv_disc_req 00:00:00:00:00:00 upnp 10 ssdp:all
+p2p_serv_disc_req 00:00:00:00:00:00 upnp 10 upnp:rootdevice
+p2p_serv_disc_req 00:00:00:00:00:00 upnp 10 urn:schemas-upnp-org:service:ContentDirectory:2
+p2p_serv_disc_req 00:00:00:00:00:00 upnp 10 uuid:6859dede-8574-59ab-9332-123456789012
+p2p_serv_disc_req 00:00:00:00:00:00 upnp 10 urn:schemas-upnp-org:device:InternetGatewayDevice:1
+
+# Wi-Fi Display examples
+# format: wifi-display <list of roles> <list of subelements>
+p2p_serv_disc_req 00:00:00:00:00:00 wifi-display [source] 2,3,4,5
+p2p_serv_disc_req 02:01:02:03:04:05 wifi-display [pri-sink] 3
+p2p_serv_disc_req 00:00:00:00:00:00 wifi-display [sec-source] 2
+p2p_serv_disc_req 00:00:00:00:00:00 wifi-display [source+sink] 2,3,4,5
+p2p_serv_disc_req 00:00:00:00:00:00 wifi-display [source][pri-sink] 2,3,4,5
+
+p2p_serv_disc_cancel_req <query identifier>
+
+Cancel a pending P2P service discovery request. This command takes a
+single parameter: identifier for the pending query (the value returned
+by p2p_serv_disc_req, e.g., "p2p_serv_disc_cancel_req 1f77628".
+
+p2p_serv_disc_resp
+
+Reply to a service discovery query. This command takes following
+parameters: frequency in MHz, destination address, dialog token,
+response TLV(s). The first three parameters are copied from the
+request event. For example, "p2p_serv_disc_resp 2437 02:40:61:c2:f3:b7
+1 0300000101". This command is used only if external program is used
+to process the request (see p2p_serv_disc_external).
+
+p2p_service_update
+
+Indicate that local services have changed. This is used to increment
+the P2P service indicator value so that peers know when previously
+cached information may have changed. This is only needed when external
+service discovery processing is enabled since the commands to
+pre-configure services for internal processing will increment the
+indicator automatically.
+
+p2p_serv_disc_external <0|1>
+
+Configure external processing of P2P service requests: 0 (default) =
+no external processing of requests (i.e., internal code will process
+each request based on pre-configured services), 1 = external
+processing of requests (external program is responsible for replying
+to service discovery requests with p2p_serv_disc_resp). Please note
+that there is quite strict limit on how quickly the response needs to
+be transmitted, so use of the internal processing is strongly
+recommended.
+
+p2p_service_add bonjour <query hexdump> <RDATA hexdump>
+
+Add a local Bonjour service for internal SD query processing.
+
+Examples:
+
+# AFP Over TCP (PTR)
+p2p_service_add bonjour 0b5f6166706f766572746370c00c000c01 074578616d706c65c027
+# AFP Over TCP (TXT) (RDATA=null)
+p2p_service_add bonjour 076578616d706c650b5f6166706f766572746370c00c001001 00
+
+# IP Printing over TCP (PTR) (RDATA=MyPrinter._ipp._tcp.local.)
+p2p_service_add bonjour 045f697070c00c000c01 094d795072696e746572c027
+# IP Printing over TCP (TXT) (RDATA=txtvers=1,pdl=application/postscript)
+p2p_service_add bonjour 096d797072696e746572045f697070c00c001001 09747874766572733d311a70646c3d6170706c69636174696f6e2f706f7374736372797074
+
+# Supported Service Type Hash (SSTH)
+p2p_service_add bonjour 000000 <32-byte bitfield as hexdump>
+(note: see P2P spec Annex E.4 for information on how to construct the bitfield)
+
+p2p_service_del bonjour <query hexdump>
+
+Remove a local Bonjour service from internal SD query processing.
+
+p2p_service_add upnp <version hex> <service>
+
+Add a local UPnP service for internal SD query processing.
+
+Examples:
+
+p2p_service_add upnp 10 uuid:6859dede-8574-59ab-9332-123456789012::upnp:rootdevice
+p2p_service_add upnp 10 uuid:5566d33e-9774-09ab-4822-333456785632::upnp:rootdevice
+p2p_service_add upnp 10 uuid:1122de4e-8574-59ab-9322-333456789044::urn:schemas-upnp-org:service:ContentDirectory:2
+p2p_service_add upnp 10 uuid:5566d33e-9774-09ab-4822-333456785632::urn:schemas-upnp-org:service:ContentDirectory:2
+p2p_service_add upnp 10 uuid:6859dede-8574-59ab-9332-123456789012::urn:schemas-upnp-org:device:InternetGatewayDevice:1
+
+p2p_service_del upnp <version hex> <service>
+
+Remove a local UPnP service from internal SD query processing.
+
+p2p_service_flush
+
+Remove all local services from internal SD query processing.
+
+Invitation
+
+p2p_invite [persistent=<network id>|group=<group ifname>] [peer=address]
+ [go_dev_addr=address] [freq=<freq in MHz>] [ht40]
+
+Invite a peer to join a group (e.g., group=wlan1) or to reinvoke a
+persistent group (e.g., persistent=4). If the peer device is the GO of
+the persistent group, the peer parameter is not needed. Otherwise it is
+used to specify which device to invite. go_dev_addr parameter can be
+used to override the GO device address for Invitation Request should
+it be not known for some reason (this should not be needed in most
+cases). When reinvoking a persistent group, the GO device can specify
+the frequency for the group with the freq parameter.
+
+Group Operations
+
+(These are used on the group interface.)
+
+wps_pin <any|address> <PIN>
+
+Start WPS PIN method. This allows a single WPS Enrollee to connect to
+the AP/GO. This is used on the GO when a P2P client joins an existing
+group. The second parameter is the address of the Enrollee or a string
+"any" to allow any station to use the entered PIN (which will restrict
+the PIN for one-time-use). PIN is the Enrollee PIN read either from a
+label or display on the P2P Client/WPS Enrollee.
+
+wps_pbc
+
+Start WPS PBC method (i.e., push the button). This allows a single WPS
+Enrollee to connect to the AP/GO. This is used on the GO when a P2P
+client joins an existing group.
+
+p2p_get_passphrase
+
+Get the passphrase for a group (only available when acting as a GO).
+
+p2p_presence_req [<duration> <interval>] [<duration> <interval>]
+
+Send a P2P Presence Request to the GO (this is only available when
+acting as a P2P client). If no duration/interval pairs are given, the
+request indicates that this client has no special needs for GO
+presence. the first parameter pair gives the preferred duration and
+interval values in microseconds. If the second pair is included, that
+indicates which value would be acceptable.
+
+Parameters
+
+p2p_ext_listen [<period> <interval>]
+
+Configure Extended Listen Timing. If the parameters are omitted, this
+feature is disabled. If the parameters are included, Listen State will
+be entered every interval msec for at least period msec. Both values
+have acceptable range of 1-65535 (with interval obviously having to be
+larger than or equal to duration). If the P2P module is not idle at
+the time the Extended Listen Timing timeout occurs, the Listen State
+operation will be skipped.
+
+The configured values will also be advertised to other P2P Devices. The
+received values are available in the p2p_peer command output:
+
+ext_listen_period=100 ext_listen_interval=5000
+
+p2p_set <field> <value>
+
+Change dynamic P2P parameters
+
+p2p_set discoverability <0/1>
+
+Disable/enable advertisement of client discoverability. This is
+enabled by default and this parameter is mainly used to allow testing
+of device discoverability.
+
+p2p_set managed <0/1>
+
+Disable/enable managed P2P Device operations. This is disabled by
+default.
+
+p2p_set listen_channel <1/6/11>
+
+Set P2P Listen channel. This is mainly meant for testing purposes and
+changing the Listen channel during normal operations can result in
+protocol failures.
+
+p2p_set ssid_postfix <postfix>
+
+Set postfix string to be added to the automatically generated P2P SSID
+(DIRECT-<two random characters>). For example, postfix of "-testing"
+could result in the SSID becoming DIRECT-ab-testing.
+
+set <field> <value>
+
+Set global configuration parameters which may also affect P2P
+operations. The format on these parameters is same as is used in
+wpa_supplicant.conf. Only the parameters listen here should be
+changed. Modifying other parameters may result in incorrect behavior
+since not all existing users of the parameters are updated.
+
+set uuid <UUID>
+
+Set WPS UUID (by default, this is generated based on the MAC address).
+
+set device_name <device name>
+
+Set WPS Device Name (also included in some P2P messages).
+
+set manufacturer <manufacturer>
+
+Set WPS Manufacturer.
+
+set model_name <model name>
+
+Set WPS Model Name.
+
+set model_number <model number>
+
+Set WPS Model Number.
+
+set serial_number <serial number>
+
+Set WPS Serial Number.
+
+set device_type <device type>
+
+Set WPS Device Type.
+
+set os_version <OS version>
+
+Set WPS OS Version.
+
+set config_methods <config methods>
+
+Set WPS Configuration Methods.
+
+set sec_device_type <device type>
+
+Add a new Secondary Device Type.
+
+set p2p_go_intent <GO intent>
+
+Set the default P2P GO Intent. Note: This value can be overridden in
+p2p_connect command and as such, there should be no need to change the
+default value here during normal operations.
+
+set p2p_ssid_postfix <P2P SSID postfix>
+
+Set P2P SSID postfix.
+
+set persistent_reconnect <0/1>
+
+Disable/enabled persistent reconnect for reinvocation of persistent
+groups. If enabled, invitations to reinvoke a persistent group will be
+accepted without separate authorization (e.g., user interaction).
+
+set country <two character country code>
+
+Set country code (this is included in some P2P messages).
+
+Status
+
+p2p_peers [discovered]
+
+List P2P Device Addresses of all the P2P peers we know. The optional
+"discovered" parameter filters out the peers that we have not fully
+discovered, i.e., which we have only seen in a received Probe Request
+frame.
+
+p2p_peer <P2P Device Address>
+
+Fetch information about a known P2P peer.
+
+Group Status
+
+(These are used on the group interface.)
+
+status
+
+Show status information (connection state, role, use encryption
+parameters, IP address, etc.).
+
+sta
+
+Show information about an associated station (when acting in AP/GO role).
+
+all_sta
+
+Lists the currently associated stations.
+
+Configuration data
+
+list_networks
+
+Lists the configured networks, including stored information for
+persistent groups. The identifier in this list is used with
+p2p_group_add and p2p_invite to indicate which persistent group is to
+be reinvoked.
+
+remove_network <network id>
+
+Remove a network entry from configuration.
+
+
+wpa_cli action script
+---------------------
+
+See examples/p2p-action.sh
+
+TODO: describe DHCP/DNS setup
+TODO: cross-connection
diff --git a/contrib/wpa/wpa_supplicant/README-WPS b/contrib/wpa/wpa_supplicant/README-WPS
index 8f0d0d6..1ea9843 100644
--- a/contrib/wpa/wpa_supplicant/README-WPS
+++ b/contrib/wpa/wpa_supplicant/README-WPS
@@ -47,9 +47,7 @@ wpa_supplicant implementation
wpa_supplicant includes an optional WPS component that can be used as
an Enrollee to enroll new network credential or as a Registrar to
-configure an AP. The current version of wpa_supplicant does not
-support operation as an external WLAN Management Registrar for adding
-new client devices or configuring the AP over UPnP.
+configure an AP.
wpa_supplicant configuration
@@ -57,11 +55,21 @@ wpa_supplicant configuration
WPS is an optional component that needs to be enabled in
wpa_supplicant build configuration (.config). Here is an example
-configuration that includes WPS support and Linux wireless extensions
--based driver interface:
+configuration that includes WPS support and Linux nl80211 -based
+driver interface:
-CONFIG_DRIVER_WEXT=y
+CONFIG_DRIVER_NL80211=y
CONFIG_WPS=y
+CONFIG_WPS2=y
+
+If you want to enable WPS external registrar (ER) functionality, you
+will also need to add following line:
+
+CONFIG_WPS_ER=y
+
+Following parameter can be used to enable support for NFC config method:
+
+CONFIG_WPS_NFC=y
WPS needs the Universally Unique IDentifier (UUID; see RFC 4122) for
@@ -93,6 +101,13 @@ pushbutton event (for PBC) to allow a new WPS Enrollee to join the
network. wpa_supplicant uses the control interface as an input channel
for these events.
+The PIN value used in the commands must be processed by an UI to
+remove non-digit characters and potentially, to verify the checksum
+digit. "wpa_cli wps_check_pin <PIN>" can be used to do such processing.
+It returns FAIL if the PIN is invalid, or FAIL-CHECKSUM if the checksum
+digit is incorrect, or the processed PIN (non-digit characters removed)
+if the PIN is valid.
+
If the client device has a display, a random PIN has to be generated
for each WPS registration session. wpa_supplicant can do this with a
control interface request, e.g., by calling wpa_cli:
@@ -115,6 +130,17 @@ wpa_cli wps_pin any 12345670
This starts the WPS negotiation in the same way as above with the
generated PIN.
+When the wps_pin command is issued for an AP (including P2P GO) mode
+interface, an optional timeout parameter can be used to specify
+expiration timeout for the PIN in seconds. For example:
+
+wpa_cli wps_pin any 12345670 300
+
+
+If a random PIN is needed for a user interface, "wpa_cli wps_pin get"
+can be used to generate a new PIN without starting WPS negotiation.
+This random PIN can then be passed as an argument to another wps_pin
+call when the actual operation should be started.
If the client design wants to support optional WPS PBC mode, this can
be enabled by either a physical button in the client device or a
@@ -198,3 +224,154 @@ Following control interface messages are sent out for external programs:
WPS-CRED-RECEIVED <hexdump of Credential attribute(s)>
For example:
<2>WPS-CRED-RECEIVED 100e006f10260001011045000c6a6b6d2d7770732d74657374100300020020100f000200081027004030653462303435366332363666653064333961643135353461316634626637313234333761636664623766333939653534663166316230323061643434386235102000060266a0ee1727
+
+
+wpa_supplicant as WPS External Registrar (ER)
+---------------------------------------------
+
+wpa_supplicant can be used as a WPS ER to configure an AP or enroll
+new Enrollee to join the network. This functionality uses UPnP and
+requires that a working IP connectivity is available with the AP (this
+can be either over a wired or wireless connection).
+
+Separate wpa_supplicant process can be started for WPS ER
+operations. A special "none" driver can be used in such a case to
+indicate that no local network interface is actually controlled. For
+example, following command could be used to start the ER:
+
+wpa_supplicant -Dnone -c er.conf -ieth0
+
+Sample er.conf:
+
+ctrl_interface=DIR=/var/run/wpa_supplicant GROUP=admin
+device_name=WPS External Registrar
+
+
+wpa_cli commands for ER functionality:
+
+wps_er_start [IP address]
+- start WPS ER functionality
+- the optional IP address parameter can be used to filter operations only
+ to include a single AP
+- if run again while ER is active, the stored information (discovered APs
+ and Enrollees) are shown again
+
+wps_er_stop
+- stop WPS ER functionality
+
+wps_er_learn <UUID> <AP PIN>
+- learn AP configuration
+
+wps_er_set_config <UUID> <network id>
+- use AP configuration from a locally configured network (e.g., from
+ wps_reg command); this does not change the AP's configuration, but
+ only prepares a configuration to be used when enrolling a new device
+ to the AP
+
+wps_er_config <UUID> <AP PIN> <new SSID> <auth> <encr> <new key>
+- examples:
+ wps_er_config 87654321-9abc-def0-1234-56789abc0002 12345670 testing WPA2PSK CCMP 12345678
+ wpa_er_config 87654321-9abc-def0-1234-56789abc0002 12345670 clear OPEN NONE ""
+
+<auth> must be one of the following: OPEN WPAPSK WPA2PSK
+<encr> must be one of the following: NONE WEP TKIP CCMP
+
+
+wps_er_pbc <Enrollee UUID>
+- accept an Enrollee PBC using External Registrar
+
+wps_er_pin <Enrollee UUID> <PIN> [Enrollee MAC address]
+- add an Enrollee PIN to External Registrar
+- if Enrollee UUID is not known, "any" can be used to add a wildcard PIN
+- if the MAC address of the enrollee is known, it should be configured
+ to allow the AP to advertise list of authorized enrollees
+
+
+WPS ER events:
+
+WPS_EVENT_ER_AP_ADD
+- WPS ER discovered an AP
+
+WPS-ER-AP-ADD 87654321-9abc-def0-1234-56789abc0002 02:11:22:33:44:55 pri_dev_type=6-0050F204-1 wps_state=1 |Very friendly name|Company|Long description of the model|WAP|http://w1.fi/|http://w1.fi/hostapd/
+
+WPS_EVENT_ER_AP_REMOVE
+- WPS ER removed an AP entry
+
+WPS-ER-AP-REMOVE 87654321-9abc-def0-1234-56789abc0002
+
+WPS_EVENT_ER_ENROLLEE_ADD
+- WPS ER discovered a new Enrollee
+
+WPS-ER-ENROLLEE-ADD 2b7093f1-d6fb-5108-adbb-bea66bb87333 02:66:a0:ee:17:27 M1=1 config_methods=0x14d dev_passwd_id=0 pri_dev_type=1-0050F204-1 |Wireless Client|Company|cmodel|123|12345|
+
+WPS_EVENT_ER_ENROLLEE_REMOVE
+- WPS ER removed an Enrollee entry
+
+WPS-ER-ENROLLEE-REMOVE 2b7093f1-d6fb-5108-adbb-bea66bb87333 02:66:a0:ee:17:27
+
+WPS-ER-AP-SETTINGS
+- WPS ER learned AP settings
+
+WPS-ER-AP-SETTINGS uuid=fd91b4ec-e3fa-5891-a57d-8c59efeed1d2 ssid=test-wps auth_type=0x0020 encr_type=0x0008 key=12345678
+
+
+WPS with NFC
+------------
+
+WPS can be used with NFC-based configuration method. An NFC tag
+containing a password token from the Enrollee can be used to
+authenticate the connection instead of the PIN. In addition, an NFC tag
+with a configuration token can be used to transfer AP settings without
+going through the WPS protocol.
+
+When the station acts as an Enrollee, a local NFC tag with a password
+token can be used by touching the NFC interface of a Registrar.
+
+"wps_nfc [BSSID]" command starts WPS protocol run with the local end as
+the Enrollee using the NFC password token that is either pre-configured
+in the configuration file (wps_nfc_dev_pw_id, wps_nfc_dh_pubkey,
+wps_nfc_dh_privkey, wps_nfc_dev_pw) or generated dynamically with
+"wps_nfc_token <WPS|NDEF>" command. The included nfc_pw_token tool
+(build with "make nfc_pw_token") can be used to generate NFC password
+tokens during manufacturing (each station needs to have its own random
+keys).
+
+If the station includes NFC interface and reads an NFC tag with a MIME
+media type "application/vnd.wfa.wsc", the NDEF message payload (with or
+without NDEF encapsulation) can be delivered to wpa_supplicant using the
+following wpa_cli command:
+
+wps_nfc_tag_read <hexdump of payload>
+
+If the NFC tag contains a configuration token, the network is added to
+wpa_supplicant configuration. If the NFC tag contains a password token,
+the token is added to the WPS Registrar component. This information can
+then be used with wps_reg command (when the NFC password token was from
+an AP) using a special value "nfc-pw" in place of the PIN parameter. If
+the ER functionality has been started (wps_er_start), the NFC password
+token is used to enable enrollment of a new station (that was the source
+of the NFC password token).
+
+"nfc_get_handover_req <NDEF> <WPS>" command can be used to build the
+contents of a Handover Request Message for connection handover. The
+first argument selects the format of the output data and the second
+argument selects which type of connection handover is requested (WPS =
+Wi-Fi handover as specified in WSC 2.0).
+
+"nfc_get_handover_sel <NDEF> <WPS>" command can be used to build the
+contents of a Handover Select Message for connection handover when this
+does not depend on the contents of the Handover Request Message. The
+first argument selects the format of the output data and the second
+argument selects which type of connection handover is requested (WPS =
+Wi-Fi handover as specified in WSC 2.0).
+
+"nfc_rx_handover_req <hexdump of payload>" is used to indicate receipt
+of NFC connection handover request. The payload may include multiple
+carriers the the applicable ones are matched based on the media
+type. The reply data is contents for the Handover Select Message
+(hexdump).
+
+"nfc_rx_handover_sel <hexdump of payload>" is used to indicate receipt
+of NFC connection handover select. The payload may include multiple
+carriers the the applicable ones are matched based on the media
+type.
diff --git a/contrib/wpa/wpa_supplicant/ap.c b/contrib/wpa/wpa_supplicant/ap.c
index 2b93984..c1e4acf 100644
--- a/contrib/wpa/wpa_supplicant/ap.c
+++ b/contrib/wpa/wpa_supplicant/ap.c
@@ -3,37 +3,42 @@
* Copyright (c) 2003-2009, Jouni Malinen <j@w1.fi>
* Copyright (c) 2009, Atheros Communications
*
- * This program is free software; you can redistribute it and/or modify
- * it under the terms of the GNU General Public License version 2 as
- * published by the Free Software Foundation.
- *
- * Alternatively, this software may be distributed under the terms of BSD
- * license.
- *
- * See README and COPYING for more details.
+ * This software may be distributed under the terms of the BSD license.
+ * See README for more details.
*/
#include "utils/includes.h"
#include "utils/common.h"
+#include "utils/eloop.h"
+#include "utils/uuid.h"
#include "common/ieee802_11_defs.h"
+#include "common/wpa_ctrl.h"
#include "ap/hostapd.h"
#include "ap/ap_config.h"
+#include "ap/ap_drv_ops.h"
#ifdef NEED_AP_MLME
#include "ap/ieee802_11.h"
#endif /* NEED_AP_MLME */
+#include "ap/beacon.h"
#include "ap/ieee802_1x.h"
#include "ap/wps_hostapd.h"
#include "ap/ctrl_iface_ap.h"
-#include "eap_common/eap_defs.h"
-#include "eap_server/eap_methods.h"
-#include "eap_common/eap_wsc_common.h"
#include "wps/wps.h"
+#include "common/ieee802_11_defs.h"
#include "config_ssid.h"
#include "config.h"
#include "wpa_supplicant_i.h"
#include "driver_i.h"
+#include "p2p_supplicant.h"
#include "ap.h"
+#include "ap/sta_info.h"
+#include "notify.h"
+
+
+#ifdef CONFIG_WPS
+static void wpas_wps_ap_pin_timeout(void *eloop_data, void *user_ctx);
+#endif /* CONFIG_WPS */
static int wpa_supplicant_conf_ap(struct wpa_supplicant *wpa_s,
@@ -58,39 +63,154 @@ static int wpa_supplicant_conf_ap(struct wpa_supplicant *wpa_s,
(ssid->frequency >= 5745 && ssid->frequency <= 5825)) {
conf->hw_mode = HOSTAPD_MODE_IEEE80211A;
conf->channel = (ssid->frequency - 5000) / 5;
+ } else if (ssid->frequency >= 56160 + 2160 * 1 &&
+ ssid->frequency <= 56160 + 2160 * 4) {
+ conf->hw_mode = HOSTAPD_MODE_IEEE80211AD;
+ conf->channel = (ssid->frequency - 56160) / 2160;
} else {
wpa_printf(MSG_ERROR, "Unsupported AP mode frequency: %d MHz",
ssid->frequency);
return -1;
}
- /* TODO: enable HT if driver supports it;
+ /* TODO: enable HT40 if driver supports it;
* drop to 11b if driver does not support 11g */
+#ifdef CONFIG_IEEE80211N
+ /*
+ * Enable HT20 if the driver supports it, by setting conf->ieee80211n
+ * and a mask of allowed capabilities within conf->ht_capab.
+ * Using default config settings for: conf->ht_op_mode_fixed,
+ * conf->secondary_channel, conf->require_ht
+ */
+ if (wpa_s->hw.modes) {
+ struct hostapd_hw_modes *mode = NULL;
+ int i, no_ht = 0;
+ for (i = 0; i < wpa_s->hw.num_modes; i++) {
+ if (wpa_s->hw.modes[i].mode == conf->hw_mode) {
+ mode = &wpa_s->hw.modes[i];
+ break;
+ }
+ }
+
+#ifdef CONFIG_HT_OVERRIDES
+ if (ssid->disable_ht) {
+ conf->ieee80211n = 0;
+ conf->ht_capab = 0;
+ no_ht = 1;
+ }
+#endif /* CONFIG_HT_OVERRIDES */
+
+ if (!no_ht && mode && mode->ht_capab) {
+ conf->ieee80211n = 1;
+#ifdef CONFIG_P2P
+ if (conf->hw_mode == HOSTAPD_MODE_IEEE80211A &&
+ (mode->ht_capab &
+ HT_CAP_INFO_SUPP_CHANNEL_WIDTH_SET) &&
+ ssid->ht40)
+ conf->secondary_channel =
+ wpas_p2p_get_ht40_mode(wpa_s, mode,
+ conf->channel);
+ if (conf->secondary_channel)
+ conf->ht_capab |=
+ HT_CAP_INFO_SUPP_CHANNEL_WIDTH_SET;
+#endif /* CONFIG_P2P */
+
+ /*
+ * white-list capabilities that won't cause issues
+ * to connecting stations, while leaving the current
+ * capabilities intact (currently disabled SMPS).
+ */
+ conf->ht_capab |= mode->ht_capab &
+ (HT_CAP_INFO_GREEN_FIELD |
+ HT_CAP_INFO_SHORT_GI20MHZ |
+ HT_CAP_INFO_SHORT_GI40MHZ |
+ HT_CAP_INFO_RX_STBC_MASK |
+ HT_CAP_INFO_MAX_AMSDU_SIZE);
+ }
+ }
+#endif /* CONFIG_IEEE80211N */
+
+#ifdef CONFIG_P2P
+ if (conf->hw_mode == HOSTAPD_MODE_IEEE80211G) {
+ /* Remove 802.11b rates from supported and basic rate sets */
+ int *list = os_malloc(4 * sizeof(int));
+ if (list) {
+ list[0] = 60;
+ list[1] = 120;
+ list[2] = 240;
+ list[3] = -1;
+ }
+ conf->basic_rates = list;
+
+ list = os_malloc(9 * sizeof(int));
+ if (list) {
+ list[0] = 60;
+ list[1] = 90;
+ list[2] = 120;
+ list[3] = 180;
+ list[4] = 240;
+ list[5] = 360;
+ list[6] = 480;
+ list[7] = 540;
+ list[8] = -1;
+ }
+ conf->supported_rates = list;
+ }
+
+ bss->isolate = !wpa_s->conf->p2p_intra_bss;
+#endif /* CONFIG_P2P */
+
if (ssid->ssid_len == 0) {
wpa_printf(MSG_ERROR, "No SSID configured for AP mode");
return -1;
}
os_memcpy(bss->ssid.ssid, ssid->ssid, ssid->ssid_len);
- bss->ssid.ssid[ssid->ssid_len] = '\0';
bss->ssid.ssid_len = ssid->ssid_len;
bss->ssid.ssid_set = 1;
+ bss->ignore_broadcast_ssid = ssid->ignore_broadcast_ssid;
+
+ if (ssid->auth_alg)
+ bss->auth_algs = ssid->auth_alg;
+
if (wpa_key_mgmt_wpa_psk(ssid->key_mgmt))
bss->wpa = ssid->proto;
bss->wpa_key_mgmt = ssid->key_mgmt;
bss->wpa_pairwise = ssid->pairwise_cipher;
- if (ssid->passphrase) {
- bss->ssid.wpa_passphrase = os_strdup(ssid->passphrase);
- } else if (ssid->psk_set) {
+ if (ssid->psk_set) {
os_free(bss->ssid.wpa_psk);
bss->ssid.wpa_psk = os_zalloc(sizeof(struct hostapd_wpa_psk));
if (bss->ssid.wpa_psk == NULL)
return -1;
os_memcpy(bss->ssid.wpa_psk->psk, ssid->psk, PMK_LEN);
bss->ssid.wpa_psk->group = 1;
+ } else if (ssid->passphrase) {
+ bss->ssid.wpa_passphrase = os_strdup(ssid->passphrase);
+ } else if (ssid->wep_key_len[0] || ssid->wep_key_len[1] ||
+ ssid->wep_key_len[2] || ssid->wep_key_len[3]) {
+ struct hostapd_wep_keys *wep = &bss->ssid.wep;
+ int i;
+ for (i = 0; i < NUM_WEP_KEYS; i++) {
+ if (ssid->wep_key_len[i] == 0)
+ continue;
+ wep->key[i] = os_malloc(ssid->wep_key_len[i]);
+ if (wep->key[i] == NULL)
+ return -1;
+ os_memcpy(wep->key[i], ssid->wep_key[i],
+ ssid->wep_key_len[i]);
+ wep->len[i] = ssid->wep_key_len[i];
+ }
+ wep->idx = ssid->wep_tx_keyidx;
+ wep->keys_set = 1;
}
+ if (ssid->ap_max_inactivity)
+ bss->ap_max_inactivity = ssid->ap_max_inactivity;
+
+ if (ssid->dtim_period)
+ bss->dtim_period = ssid->dtim_period;
+
/* Select group cipher based on the enabled pairwise cipher suites */
pairwise = 0;
if (bss->wpa & 1)
@@ -102,6 +222,9 @@ static int wpa_supplicant_conf_ap(struct wpa_supplicant *wpa_s,
}
if (pairwise & WPA_CIPHER_TKIP)
bss->wpa_group = WPA_CIPHER_TKIP;
+ else if ((pairwise & (WPA_CIPHER_CCMP | WPA_CIPHER_GCMP)) ==
+ WPA_CIPHER_GCMP)
+ bss->wpa_group = WPA_CIPHER_GCMP;
else
bss->wpa_group = WPA_CIPHER_CCMP;
@@ -110,46 +233,193 @@ static int wpa_supplicant_conf_ap(struct wpa_supplicant *wpa_s,
else if (bss->wpa)
bss->ssid.security_policy = SECURITY_WPA_PSK;
else if (bss->ieee802_1x) {
+ int cipher = WPA_CIPHER_NONE;
bss->ssid.security_policy = SECURITY_IEEE_802_1X;
bss->ssid.wep.default_len = bss->default_wep_key_len;
- } else if (bss->ssid.wep.keys_set)
+ if (bss->default_wep_key_len)
+ cipher = bss->default_wep_key_len >= 13 ?
+ WPA_CIPHER_WEP104 : WPA_CIPHER_WEP40;
+ bss->wpa_group = cipher;
+ bss->wpa_pairwise = cipher;
+ bss->rsn_pairwise = cipher;
+ } else if (bss->ssid.wep.keys_set) {
+ int cipher = WPA_CIPHER_WEP40;
+ if (bss->ssid.wep.len[0] >= 13)
+ cipher = WPA_CIPHER_WEP104;
bss->ssid.security_policy = SECURITY_STATIC_WEP;
- else
+ bss->wpa_group = cipher;
+ bss->wpa_pairwise = cipher;
+ bss->rsn_pairwise = cipher;
+ } else {
bss->ssid.security_policy = SECURITY_PLAINTEXT;
+ bss->wpa_group = WPA_CIPHER_NONE;
+ bss->wpa_pairwise = WPA_CIPHER_NONE;
+ bss->rsn_pairwise = WPA_CIPHER_NONE;
+ }
#ifdef CONFIG_WPS
/*
- * Enable WPS by default, but require user interaction to actually use
- * it. Only the internal Registrar is supported.
+ * Enable WPS by default for open and WPA/WPA2-Personal network, but
+ * require user interaction to actually use it. Only the internal
+ * Registrar is supported.
*/
+ if (bss->ssid.security_policy != SECURITY_WPA_PSK &&
+ bss->ssid.security_policy != SECURITY_PLAINTEXT)
+ goto no_wps;
+#ifdef CONFIG_WPS2
+ if (bss->ssid.security_policy == SECURITY_WPA_PSK &&
+ (!(pairwise & WPA_CIPHER_CCMP) || !(bss->wpa & 2)))
+ goto no_wps; /* WPS2 does not allow WPA/TKIP-only
+ * configuration */
+#endif /* CONFIG_WPS2 */
bss->eap_server = 1;
- bss->wps_state = 2;
- bss->ap_setup_locked = 1;
+
+ if (!ssid->ignore_broadcast_ssid)
+ bss->wps_state = 2;
+
+ bss->ap_setup_locked = 2;
if (wpa_s->conf->config_methods)
bss->config_methods = os_strdup(wpa_s->conf->config_methods);
- if (wpa_s->conf->device_type)
- bss->device_type = os_strdup(wpa_s->conf->device_type);
+ os_memcpy(bss->device_type, wpa_s->conf->device_type,
+ WPS_DEV_TYPE_LEN);
+ if (wpa_s->conf->device_name) {
+ bss->device_name = os_strdup(wpa_s->conf->device_name);
+ bss->friendly_name = os_strdup(wpa_s->conf->device_name);
+ }
+ if (wpa_s->conf->manufacturer)
+ bss->manufacturer = os_strdup(wpa_s->conf->manufacturer);
+ if (wpa_s->conf->model_name)
+ bss->model_name = os_strdup(wpa_s->conf->model_name);
+ if (wpa_s->conf->model_number)
+ bss->model_number = os_strdup(wpa_s->conf->model_number);
+ if (wpa_s->conf->serial_number)
+ bss->serial_number = os_strdup(wpa_s->conf->serial_number);
+ if (is_nil_uuid(wpa_s->conf->uuid))
+ os_memcpy(bss->uuid, wpa_s->wps->uuid, WPS_UUID_LEN);
+ else
+ os_memcpy(bss->uuid, wpa_s->conf->uuid, WPS_UUID_LEN);
+ os_memcpy(bss->os_version, wpa_s->conf->os_version, 4);
+ bss->pbc_in_m1 = wpa_s->conf->pbc_in_m1;
+no_wps:
#endif /* CONFIG_WPS */
+ if (wpa_s->max_stations &&
+ wpa_s->max_stations < wpa_s->conf->max_num_sta)
+ bss->max_num_sta = wpa_s->max_stations;
+ else
+ bss->max_num_sta = wpa_s->conf->max_num_sta;
+
+ bss->disassoc_low_ack = wpa_s->conf->disassoc_low_ack;
+
return 0;
}
static void ap_public_action_rx(void *ctx, const u8 *buf, size_t len, int freq)
{
+#ifdef CONFIG_P2P
+ struct wpa_supplicant *wpa_s = ctx;
+ const struct ieee80211_mgmt *mgmt;
+ size_t hdr_len;
+
+ mgmt = (const struct ieee80211_mgmt *) buf;
+ hdr_len = (const u8 *) &mgmt->u.action.u.vs_public_action.action - buf;
+ if (hdr_len > len)
+ return;
+ wpas_p2p_rx_action(wpa_s, mgmt->da, mgmt->sa, mgmt->bssid,
+ mgmt->u.action.category,
+ &mgmt->u.action.u.vs_public_action.action,
+ len - hdr_len, freq);
+#endif /* CONFIG_P2P */
+}
+
+
+static void ap_wps_event_cb(void *ctx, enum wps_event event,
+ union wps_event_data *data)
+{
+#ifdef CONFIG_P2P
+ struct wpa_supplicant *wpa_s = ctx;
+
+ if (event == WPS_EV_FAIL) {
+ struct wps_event_fail *fail = &data->fail;
+
+ if (wpa_s->parent && wpa_s->parent != wpa_s &&
+ wpa_s == wpa_s->global->p2p_group_formation) {
+ /*
+ * src/ap/wps_hostapd.c has already sent this on the
+ * main interface, so only send on the parent interface
+ * here if needed.
+ */
+ wpa_msg(wpa_s->parent, MSG_INFO, WPS_EVENT_FAIL
+ "msg=%d config_error=%d",
+ fail->msg, fail->config_error);
+ }
+ wpas_p2p_wps_failed(wpa_s, fail);
+ }
+#endif /* CONFIG_P2P */
+}
+
+
+static void ap_sta_authorized_cb(void *ctx, const u8 *mac_addr,
+ int authorized, const u8 *p2p_dev_addr)
+{
+ wpas_notify_sta_authorized(ctx, mac_addr, authorized, p2p_dev_addr);
+}
+
+
+static int ap_vendor_action_rx(void *ctx, const u8 *buf, size_t len, int freq)
+{
+#ifdef CONFIG_P2P
+ struct wpa_supplicant *wpa_s = ctx;
+ const struct ieee80211_mgmt *mgmt;
+ size_t hdr_len;
+
+ mgmt = (const struct ieee80211_mgmt *) buf;
+ hdr_len = (const u8 *) &mgmt->u.action.u.vs_public_action.action - buf;
+ if (hdr_len > len)
+ return -1;
+ wpas_p2p_rx_action(wpa_s, mgmt->da, mgmt->sa, mgmt->bssid,
+ mgmt->u.action.category,
+ &mgmt->u.action.u.vs_public_action.action,
+ len - hdr_len, freq);
+#endif /* CONFIG_P2P */
+ return 0;
}
-static int ap_probe_req_rx(void *ctx, const u8 *addr, const u8 *ie,
- size_t ie_len)
+static int ap_probe_req_rx(void *ctx, const u8 *sa, const u8 *da,
+ const u8 *bssid, const u8 *ie, size_t ie_len,
+ int ssi_signal)
{
+#ifdef CONFIG_P2P
+ struct wpa_supplicant *wpa_s = ctx;
+ return wpas_p2p_probe_req_rx(wpa_s, sa, da, bssid, ie, ie_len,
+ ssi_signal);
+#else /* CONFIG_P2P */
return 0;
+#endif /* CONFIG_P2P */
}
static void ap_wps_reg_success_cb(void *ctx, const u8 *mac_addr,
const u8 *uuid_e)
{
+#ifdef CONFIG_P2P
+ struct wpa_supplicant *wpa_s = ctx;
+ wpas_p2p_wps_success(wpa_s, mac_addr, 1);
+#endif /* CONFIG_P2P */
+}
+
+
+static void wpas_ap_configured_cb(void *ctx)
+{
+ struct wpa_supplicant *wpa_s = ctx;
+
+ wpa_supplicant_set_state(wpa_s, WPA_COMPLETED);
+
+ if (wpa_s->ap_configured_cb)
+ wpa_s->ap_configured_cb(wpa_s->ap_configured_cb_ctx,
+ wpa_s->ap_configured_cb_data);
}
@@ -182,11 +452,14 @@ int wpa_supplicant_create_ap(struct wpa_supplicant *wpa_s,
params.mode = IEEE80211_MODE_IBSS;
break;
case WPAS_MODE_AP:
+ case WPAS_MODE_P2P_GO:
+ case WPAS_MODE_P2P_GROUP_FORMATION:
params.mode = IEEE80211_MODE_AP;
break;
}
params.freq = ssid->frequency;
+ params.wpa_proto = ssid->proto;
if (ssid->key_mgmt & WPA_KEY_MGMT_PSK)
wpa_s->key_mgmt = WPA_KEY_MGMT_PSK;
else
@@ -195,6 +468,8 @@ int wpa_supplicant_create_ap(struct wpa_supplicant *wpa_s,
if (ssid->pairwise_cipher & WPA_CIPHER_CCMP)
wpa_s->pairwise_cipher = WPA_CIPHER_CCMP;
+ else if (ssid->pairwise_cipher & WPA_CIPHER_GCMP)
+ wpa_s->pairwise_cipher = WPA_CIPHER_GCMP;
else if (ssid->pairwise_cipher & WPA_CIPHER_TKIP)
wpa_s->pairwise_cipher = WPA_CIPHER_TKIP;
else if (ssid->pairwise_cipher & WPA_CIPHER_NONE)
@@ -207,6 +482,17 @@ int wpa_supplicant_create_ap(struct wpa_supplicant *wpa_s,
params.pairwise_suite = cipher_suite2driver(wpa_s->pairwise_cipher);
params.group_suite = params.pairwise_suite;
+#ifdef CONFIG_P2P
+ if (ssid->mode == WPAS_MODE_P2P_GO ||
+ ssid->mode == WPAS_MODE_P2P_GROUP_FORMATION)
+ params.p2p = 1;
+#endif /* CONFIG_P2P */
+
+ if (wpa_s->parent->set_ap_uapsd)
+ params.uapsd = wpa_s->parent->ap_uapsd;
+ else
+ params.uapsd = -1;
+
if (wpa_drv_associate(wpa_s, &params) < 0) {
wpa_msg(wpa_s, MSG_INFO, "Failed to start AP functionality");
return -1;
@@ -216,6 +502,8 @@ int wpa_supplicant_create_ap(struct wpa_supplicant *wpa_s,
if (hapd_iface == NULL)
return -1;
hapd_iface->owner = wpa_s;
+ hapd_iface->drv_flags = wpa_s->drv_flags;
+ hapd_iface->probe_resp_offloads = wpa_s->probe_resp_offloads;
wpa_s->ap_iface->conf = conf = hostapd_config_defaults();
if (conf == NULL) {
@@ -223,14 +511,31 @@ int wpa_supplicant_create_ap(struct wpa_supplicant *wpa_s,
return -1;
}
+ os_memcpy(wpa_s->ap_iface->conf->wmm_ac_params,
+ wpa_s->conf->wmm_ac_params,
+ sizeof(wpa_s->conf->wmm_ac_params));
+
+ if (params.uapsd > 0) {
+ conf->bss->wmm_enabled = 1;
+ conf->bss->wmm_uapsd = 1;
+ }
+
if (wpa_supplicant_conf_ap(wpa_s, ssid, conf)) {
wpa_printf(MSG_ERROR, "Failed to create AP configuration");
wpa_supplicant_ap_deinit(wpa_s);
return -1;
}
+#ifdef CONFIG_P2P
+ if (ssid->mode == WPAS_MODE_P2P_GO)
+ conf->bss[0].p2p = P2P_ENABLED | P2P_GROUP_OWNER;
+ else if (ssid->mode == WPAS_MODE_P2P_GROUP_FORMATION)
+ conf->bss[0].p2p = P2P_ENABLED | P2P_GROUP_OWNER |
+ P2P_GROUP_FORMATION;
+#endif /* CONFIG_P2P */
+
hapd_iface->num_bss = conf->num_bss;
- hapd_iface->bss = os_zalloc(conf->num_bss *
+ hapd_iface->bss = os_calloc(conf->num_bss,
sizeof(struct hostapd_data *));
if (hapd_iface->bss == NULL) {
wpa_supplicant_ap_deinit(wpa_s);
@@ -247,42 +552,62 @@ int wpa_supplicant_create_ap(struct wpa_supplicant *wpa_s,
}
hapd_iface->bss[i]->msg_ctx = wpa_s;
+ hapd_iface->bss[i]->msg_ctx_parent = wpa_s->parent;
hapd_iface->bss[i]->public_action_cb = ap_public_action_rx;
hapd_iface->bss[i]->public_action_cb_ctx = wpa_s;
+ hapd_iface->bss[i]->vendor_action_cb = ap_vendor_action_rx;
+ hapd_iface->bss[i]->vendor_action_cb_ctx = wpa_s;
hostapd_register_probereq_cb(hapd_iface->bss[i],
ap_probe_req_rx, wpa_s);
hapd_iface->bss[i]->wps_reg_success_cb = ap_wps_reg_success_cb;
hapd_iface->bss[i]->wps_reg_success_cb_ctx = wpa_s;
+ hapd_iface->bss[i]->wps_event_cb = ap_wps_event_cb;
+ hapd_iface->bss[i]->wps_event_cb_ctx = wpa_s;
+ hapd_iface->bss[i]->sta_authorized_cb = ap_sta_authorized_cb;
+ hapd_iface->bss[i]->sta_authorized_cb_ctx = wpa_s;
+#ifdef CONFIG_P2P
+ hapd_iface->bss[i]->p2p = wpa_s->global->p2p;
+ hapd_iface->bss[i]->p2p_group = wpas_p2p_group_init(wpa_s,
+ ssid);
+#endif /* CONFIG_P2P */
+ hapd_iface->bss[i]->setup_complete_cb = wpas_ap_configured_cb;
+ hapd_iface->bss[i]->setup_complete_cb_ctx = wpa_s;
}
os_memcpy(hapd_iface->bss[0]->own_addr, wpa_s->own_addr, ETH_ALEN);
hapd_iface->bss[0]->driver = wpa_s->driver;
hapd_iface->bss[0]->drv_priv = wpa_s->drv_priv;
+ wpa_s->current_ssid = ssid;
+ os_memcpy(wpa_s->bssid, wpa_s->own_addr, ETH_ALEN);
+ wpa_s->assoc_freq = ssid->frequency;
+
if (hostapd_setup_interface(wpa_s->ap_iface)) {
wpa_printf(MSG_ERROR, "Failed to initialize AP interface");
wpa_supplicant_ap_deinit(wpa_s);
return -1;
}
- wpa_s->current_ssid = ssid;
- os_memcpy(wpa_s->bssid, wpa_s->own_addr, ETH_ALEN);
- wpa_supplicant_set_state(wpa_s, WPA_COMPLETED);
-
- if (wpa_s->ap_configured_cb)
- wpa_s->ap_configured_cb(wpa_s->ap_configured_cb_ctx,
- wpa_s->ap_configured_cb_data);
-
return 0;
}
void wpa_supplicant_ap_deinit(struct wpa_supplicant *wpa_s)
{
+#ifdef CONFIG_WPS
+ eloop_cancel_timeout(wpas_wps_ap_pin_timeout, wpa_s, NULL);
+#endif /* CONFIG_WPS */
+
if (wpa_s->ap_iface == NULL)
return;
wpa_s->current_ssid = NULL;
+ wpa_s->assoc_freq = 0;
+#ifdef CONFIG_P2P
+ if (wpa_s->ap_iface->bss)
+ wpa_s->ap_iface->bss[0]->p2p_group = NULL;
+ wpas_p2p_group_deinit(wpa_s);
+#endif /* CONFIG_P2P */
hostapd_interface_deinit(wpa_s->ap_iface);
hostapd_interface_free(wpa_s->ap_iface);
wpa_s->ap_iface = NULL;
@@ -300,16 +625,31 @@ void ap_tx_status(void *ctx, const u8 *addr,
}
-void ap_rx_from_unknown_sta(void *ctx, const u8 *frame, size_t len)
+void ap_eapol_tx_status(void *ctx, const u8 *dst,
+ const u8 *data, size_t len, int ack)
{
#ifdef NEED_AP_MLME
struct wpa_supplicant *wpa_s = ctx;
- const struct ieee80211_hdr *hdr =
- (const struct ieee80211_hdr *) frame;
- u16 fc = le_to_host16(hdr->frame_control);
- ieee802_11_rx_from_unknown(wpa_s->ap_iface->bss[0], hdr->addr2,
- (fc & (WLAN_FC_TODS | WLAN_FC_FROMDS)) ==
- (WLAN_FC_TODS | WLAN_FC_FROMDS));
+ hostapd_tx_status(wpa_s->ap_iface->bss[0], dst, data, len, ack);
+#endif /* NEED_AP_MLME */
+}
+
+
+void ap_client_poll_ok(void *ctx, const u8 *addr)
+{
+#ifdef NEED_AP_MLME
+ struct wpa_supplicant *wpa_s = ctx;
+ if (wpa_s->ap_iface)
+ hostapd_client_poll_ok(wpa_s->ap_iface->bss[0], addr);
+#endif /* NEED_AP_MLME */
+}
+
+
+void ap_rx_from_unknown_sta(void *ctx, const u8 *addr, int wds)
+{
+#ifdef NEED_AP_MLME
+ struct wpa_supplicant *wpa_s = ctx;
+ ieee802_11_rx_from_unknown(wpa_s->ap_iface->bss[0], addr, wds);
#endif /* NEED_AP_MLME */
}
@@ -346,16 +686,49 @@ void wpa_supplicant_ap_rx_eapol(struct wpa_supplicant *wpa_s,
#ifdef CONFIG_WPS
-int wpa_supplicant_ap_wps_pbc(struct wpa_supplicant *wpa_s, const u8 *bssid)
+int wpa_supplicant_ap_wps_pbc(struct wpa_supplicant *wpa_s, const u8 *bssid,
+ const u8 *p2p_dev_addr)
{
if (!wpa_s->ap_iface)
return -1;
- return hostapd_wps_button_pushed(wpa_s->ap_iface->bss[0]);
+ return hostapd_wps_button_pushed(wpa_s->ap_iface->bss[0],
+ p2p_dev_addr);
+}
+
+
+int wpa_supplicant_ap_wps_cancel(struct wpa_supplicant *wpa_s)
+{
+ struct wps_registrar *reg;
+ int reg_sel = 0, wps_sta = 0;
+
+ if (!wpa_s->ap_iface || !wpa_s->ap_iface->bss[0]->wps)
+ return -1;
+
+ reg = wpa_s->ap_iface->bss[0]->wps->registrar;
+ reg_sel = wps_registrar_wps_cancel(reg);
+ wps_sta = ap_for_each_sta(wpa_s->ap_iface->bss[0],
+ ap_sta_wps_cancel, NULL);
+
+ if (!reg_sel && !wps_sta) {
+ wpa_printf(MSG_DEBUG, "No WPS operation in progress at this "
+ "time");
+ return -1;
+ }
+
+ /*
+ * There are 2 cases to return wps cancel as success:
+ * 1. When wps cancel was initiated but no connection has been
+ * established with client yet.
+ * 2. Client is in the middle of exchanging WPS messages.
+ */
+
+ return 0;
}
int wpa_supplicant_ap_wps_pin(struct wpa_supplicant *wpa_s, const u8 *bssid,
- const char *pin, char *buf, size_t buflen)
+ const char *pin, char *buf, size_t buflen,
+ int timeout)
{
int ret, ret_len = 0;
@@ -364,16 +737,135 @@ int wpa_supplicant_ap_wps_pin(struct wpa_supplicant *wpa_s, const u8 *bssid,
if (pin == NULL) {
unsigned int rpin = wps_generate_pin();
- ret_len = os_snprintf(buf, buflen, "%d", rpin);
+ ret_len = os_snprintf(buf, buflen, "%08d", rpin);
pin = buf;
- }
+ } else
+ ret_len = os_snprintf(buf, buflen, "%s", pin);
- ret = hostapd_wps_add_pin(wpa_s->ap_iface->bss[0], "any", pin, 0);
+ ret = hostapd_wps_add_pin(wpa_s->ap_iface->bss[0], bssid, "any", pin,
+ timeout);
if (ret)
return -1;
return ret_len;
}
+
+static void wpas_wps_ap_pin_timeout(void *eloop_data, void *user_ctx)
+{
+ struct wpa_supplicant *wpa_s = eloop_data;
+ wpa_printf(MSG_DEBUG, "WPS: AP PIN timed out");
+ wpas_wps_ap_pin_disable(wpa_s);
+}
+
+
+static void wpas_wps_ap_pin_enable(struct wpa_supplicant *wpa_s, int timeout)
+{
+ struct hostapd_data *hapd;
+
+ if (wpa_s->ap_iface == NULL)
+ return;
+ hapd = wpa_s->ap_iface->bss[0];
+ wpa_printf(MSG_DEBUG, "WPS: Enabling AP PIN (timeout=%d)", timeout);
+ hapd->ap_pin_failures = 0;
+ eloop_cancel_timeout(wpas_wps_ap_pin_timeout, wpa_s, NULL);
+ if (timeout > 0)
+ eloop_register_timeout(timeout, 0,
+ wpas_wps_ap_pin_timeout, wpa_s, NULL);
+}
+
+
+void wpas_wps_ap_pin_disable(struct wpa_supplicant *wpa_s)
+{
+ struct hostapd_data *hapd;
+
+ if (wpa_s->ap_iface == NULL)
+ return;
+ wpa_printf(MSG_DEBUG, "WPS: Disabling AP PIN");
+ hapd = wpa_s->ap_iface->bss[0];
+ os_free(hapd->conf->ap_pin);
+ hapd->conf->ap_pin = NULL;
+ eloop_cancel_timeout(wpas_wps_ap_pin_timeout, wpa_s, NULL);
+}
+
+
+const char * wpas_wps_ap_pin_random(struct wpa_supplicant *wpa_s, int timeout)
+{
+ struct hostapd_data *hapd;
+ unsigned int pin;
+ char pin_txt[9];
+
+ if (wpa_s->ap_iface == NULL)
+ return NULL;
+ hapd = wpa_s->ap_iface->bss[0];
+ pin = wps_generate_pin();
+ os_snprintf(pin_txt, sizeof(pin_txt), "%08u", pin);
+ os_free(hapd->conf->ap_pin);
+ hapd->conf->ap_pin = os_strdup(pin_txt);
+ if (hapd->conf->ap_pin == NULL)
+ return NULL;
+ wpas_wps_ap_pin_enable(wpa_s, timeout);
+
+ return hapd->conf->ap_pin;
+}
+
+
+const char * wpas_wps_ap_pin_get(struct wpa_supplicant *wpa_s)
+{
+ struct hostapd_data *hapd;
+ if (wpa_s->ap_iface == NULL)
+ return NULL;
+ hapd = wpa_s->ap_iface->bss[0];
+ return hapd->conf->ap_pin;
+}
+
+
+int wpas_wps_ap_pin_set(struct wpa_supplicant *wpa_s, const char *pin,
+ int timeout)
+{
+ struct hostapd_data *hapd;
+ char pin_txt[9];
+ int ret;
+
+ if (wpa_s->ap_iface == NULL)
+ return -1;
+ hapd = wpa_s->ap_iface->bss[0];
+ ret = os_snprintf(pin_txt, sizeof(pin_txt), "%s", pin);
+ if (ret < 0 || ret >= (int) sizeof(pin_txt))
+ return -1;
+ os_free(hapd->conf->ap_pin);
+ hapd->conf->ap_pin = os_strdup(pin_txt);
+ if (hapd->conf->ap_pin == NULL)
+ return -1;
+ wpas_wps_ap_pin_enable(wpa_s, timeout);
+
+ return 0;
+}
+
+
+void wpa_supplicant_ap_pwd_auth_fail(struct wpa_supplicant *wpa_s)
+{
+ struct hostapd_data *hapd;
+
+ if (wpa_s->ap_iface == NULL)
+ return;
+ hapd = wpa_s->ap_iface->bss[0];
+
+ /*
+ * Registrar failed to prove its knowledge of the AP PIN. Disable AP
+ * PIN if this happens multiple times to slow down brute force attacks.
+ */
+ hapd->ap_pin_failures++;
+ wpa_printf(MSG_DEBUG, "WPS: AP PIN authentication failure number %u",
+ hapd->ap_pin_failures);
+ if (hapd->ap_pin_failures < 3)
+ return;
+
+ wpa_printf(MSG_DEBUG, "WPS: Disable AP PIN");
+ hapd->ap_pin_failures = 0;
+ os_free(hapd->conf->ap_pin);
+ hapd->conf->ap_pin = NULL;
+}
+
#endif /* CONFIG_WPS */
@@ -409,6 +901,26 @@ int ap_ctrl_iface_sta_next(struct wpa_supplicant *wpa_s, const char *txtaddr,
}
+int ap_ctrl_iface_sta_disassociate(struct wpa_supplicant *wpa_s,
+ const char *txtaddr)
+{
+ if (wpa_s->ap_iface == NULL)
+ return -1;
+ return hostapd_ctrl_iface_disassociate(wpa_s->ap_iface->bss[0],
+ txtaddr);
+}
+
+
+int ap_ctrl_iface_sta_deauthenticate(struct wpa_supplicant *wpa_s,
+ const char *txtaddr)
+{
+ if (wpa_s->ap_iface == NULL)
+ return -1;
+ return hostapd_ctrl_iface_deauthenticate(wpa_s->ap_iface->bss[0],
+ txtaddr);
+}
+
+
int ap_ctrl_iface_wpa_get_status(struct wpa_supplicant *wpa_s, char *buf,
size_t buflen, int verbose)
{
@@ -440,6 +952,46 @@ int ap_ctrl_iface_wpa_get_status(struct wpa_supplicant *wpa_s, char *buf,
#endif /* CONFIG_CTRL_IFACE */
+int wpa_supplicant_ap_update_beacon(struct wpa_supplicant *wpa_s)
+{
+ struct hostapd_iface *iface = wpa_s->ap_iface;
+ struct wpa_ssid *ssid = wpa_s->current_ssid;
+ struct hostapd_data *hapd;
+
+ if (ssid == NULL || wpa_s->ap_iface == NULL ||
+ ssid->mode == WPAS_MODE_INFRA ||
+ ssid->mode == WPAS_MODE_IBSS)
+ return -1;
+
+#ifdef CONFIG_P2P
+ if (ssid->mode == WPAS_MODE_P2P_GO)
+ iface->conf->bss[0].p2p = P2P_ENABLED | P2P_GROUP_OWNER;
+ else if (ssid->mode == WPAS_MODE_P2P_GROUP_FORMATION)
+ iface->conf->bss[0].p2p = P2P_ENABLED | P2P_GROUP_OWNER |
+ P2P_GROUP_FORMATION;
+#endif /* CONFIG_P2P */
+
+ hapd = iface->bss[0];
+ if (hapd->drv_priv == NULL)
+ return -1;
+ ieee802_11_set_beacons(iface);
+ hostapd_set_ap_wps_ie(hapd);
+
+ return 0;
+}
+
+
+void wpas_ap_ch_switch(struct wpa_supplicant *wpa_s, int freq, int ht,
+ int offset)
+{
+ if (!wpa_s->ap_iface)
+ return;
+
+ wpa_s->assoc_freq = freq;
+ hostapd_event_ch_switch(wpa_s->ap_iface->bss[0], freq, ht, offset);
+}
+
+
int wpa_supplicant_ap_mac_addr_filter(struct wpa_supplicant *wpa_s,
const u8 *addr)
{
diff --git a/contrib/wpa/wpa_supplicant/ap.h b/contrib/wpa/wpa_supplicant/ap.h
index 381a432..536064f 100644
--- a/contrib/wpa/wpa_supplicant/ap.h
+++ b/contrib/wpa/wpa_supplicant/ap.h
@@ -3,14 +3,8 @@
* Copyright (c) 2003-2009, Jouni Malinen <j@w1.fi>
* Copyright (c) 2009, Atheros Communications
*
- * This program is free software; you can redistribute it and/or modify
- * it under the terms of the GNU General Public License version 2 as
- * published by the Free Software Foundation.
- *
- * Alternatively, this software may be distributed under the terms of BSD
- * license.
- *
- * See README and COPYING for more details.
+ * This software may be distributed under the terms of the BSD license.
+ * See README for more details.
*/
#ifndef AP_H
@@ -21,23 +15,42 @@ int wpa_supplicant_create_ap(struct wpa_supplicant *wpa_s,
void wpa_supplicant_ap_deinit(struct wpa_supplicant *wpa_s);
void wpa_supplicant_ap_rx_eapol(struct wpa_supplicant *wpa_s,
const u8 *src_addr, const u8 *buf, size_t len);
-int wpa_supplicant_ap_wps_pbc(struct wpa_supplicant *wpa_s, const u8 *bssid);
+int wpa_supplicant_ap_wps_pbc(struct wpa_supplicant *wpa_s, const u8 *bssid,
+ const u8 *p2p_dev_addr);
int wpa_supplicant_ap_wps_pin(struct wpa_supplicant *wpa_s, const u8 *bssid,
- const char *pin, char *buf, size_t buflen);
+ const char *pin, char *buf, size_t buflen,
+ int timeout);
+int wpa_supplicant_ap_wps_cancel(struct wpa_supplicant *wpa_s);
+void wpas_wps_ap_pin_disable(struct wpa_supplicant *wpa_s);
+const char * wpas_wps_ap_pin_random(struct wpa_supplicant *wpa_s, int timeout);
+const char * wpas_wps_ap_pin_get(struct wpa_supplicant *wpa_s);
+int wpas_wps_ap_pin_set(struct wpa_supplicant *wpa_s, const char *pin,
+ int timeout);
int ap_ctrl_iface_sta_first(struct wpa_supplicant *wpa_s,
char *buf, size_t buflen);
int ap_ctrl_iface_sta(struct wpa_supplicant *wpa_s, const char *txtaddr,
char *buf, size_t buflen);
int ap_ctrl_iface_sta_next(struct wpa_supplicant *wpa_s, const char *txtaddr,
char *buf, size_t buflen);
+int ap_ctrl_iface_sta_deauthenticate(struct wpa_supplicant *wpa_s,
+ const char *txtaddr);
+int ap_ctrl_iface_sta_disassociate(struct wpa_supplicant *wpa_s,
+ const char *txtaddr);
int ap_ctrl_iface_wpa_get_status(struct wpa_supplicant *wpa_s, char *buf,
size_t buflen, int verbose);
void ap_tx_status(void *ctx, const u8 *addr,
const u8 *buf, size_t len, int ack);
-void ap_rx_from_unknown_sta(void *ctx, const u8 *frame, size_t len);
+void ap_eapol_tx_status(void *ctx, const u8 *dst,
+ const u8 *data, size_t len, int ack);
+void ap_client_poll_ok(void *ctx, const u8 *addr);
+void ap_rx_from_unknown_sta(void *ctx, const u8 *addr, int wds);
void ap_mgmt_rx(void *ctx, struct rx_mgmt *rx_mgmt);
void ap_mgmt_tx_cb(void *ctx, const u8 *buf, size_t len, u16 stype, int ok);
+int wpa_supplicant_ap_update_beacon(struct wpa_supplicant *wpa_s);
int wpa_supplicant_ap_mac_addr_filter(struct wpa_supplicant *wpa_s,
const u8 *addr);
+void wpa_supplicant_ap_pwd_auth_fail(struct wpa_supplicant *wpa_s);
+void wpas_ap_ch_switch(struct wpa_supplicant *wpa_s, int freq, int ht,
+ int offset);
#endif /* AP_H */
diff --git a/contrib/wpa/wpa_supplicant/autoscan.c b/contrib/wpa/wpa_supplicant/autoscan.c
new file mode 100644
index 0000000..a2cf7a5
--- /dev/null
+++ b/contrib/wpa/wpa_supplicant/autoscan.c
@@ -0,0 +1,143 @@
+/*
+ * WPA Supplicant - auto scan
+ * Copyright (c) 2012, Intel Corporation. All rights reserved.
+ *
+ * This software may be distributed under the terms of the BSD license.
+ * See README for more details.
+ */
+
+#include "includes.h"
+
+#include "common.h"
+#include "config.h"
+#include "wpa_supplicant_i.h"
+#include "bss.h"
+#include "scan.h"
+#include "autoscan.h"
+
+#ifdef CONFIG_AUTOSCAN_EXPONENTIAL
+extern const struct autoscan_ops autoscan_exponential_ops;
+#endif /* CONFIG_AUTOSCAN_EXPONENTIAL */
+
+#ifdef CONFIG_AUTOSCAN_PERIODIC
+extern const struct autoscan_ops autoscan_periodic_ops;
+#endif /* CONFIG_AUTOSCAN_PERIODIC */
+
+static const struct autoscan_ops * autoscan_modules[] = {
+#ifdef CONFIG_AUTOSCAN_EXPONENTIAL
+ &autoscan_exponential_ops,
+#endif /* CONFIG_AUTOSCAN_EXPONENTIAL */
+#ifdef CONFIG_AUTOSCAN_PERIODIC
+ &autoscan_periodic_ops,
+#endif /* CONFIG_AUTOSCAN_PERIODIC */
+ NULL
+};
+
+
+static void request_scan(struct wpa_supplicant *wpa_s)
+{
+ wpa_s->scan_req = MANUAL_SCAN_REQ;
+
+ if (wpa_supplicant_req_sched_scan(wpa_s))
+ wpa_supplicant_req_scan(wpa_s, wpa_s->scan_interval, 0);
+}
+
+
+int autoscan_init(struct wpa_supplicant *wpa_s, int req_scan)
+{
+ const char *name = wpa_s->conf->autoscan;
+ const char *params;
+ size_t nlen;
+ int i;
+ const struct autoscan_ops *ops = NULL;
+
+ if (wpa_s->autoscan && wpa_s->autoscan_priv)
+ return 0;
+
+ if (name == NULL)
+ return 0;
+
+ params = os_strchr(name, ':');
+ if (params == NULL) {
+ params = "";
+ nlen = os_strlen(name);
+ } else {
+ nlen = params - name;
+ params++;
+ }
+
+ for (i = 0; autoscan_modules[i]; i++) {
+ if (os_strncmp(name, autoscan_modules[i]->name, nlen) == 0) {
+ ops = autoscan_modules[i];
+ break;
+ }
+ }
+
+ if (ops == NULL) {
+ wpa_printf(MSG_ERROR, "autoscan: Could not find module "
+ "matching the parameter '%s'", name);
+ return -1;
+ }
+
+ wpa_s->autoscan_params = NULL;
+
+ wpa_s->autoscan_priv = ops->init(wpa_s, params);
+ if (wpa_s->autoscan_priv == NULL)
+ return -1;
+ wpa_s->autoscan = ops;
+
+ wpa_printf(MSG_DEBUG, "autoscan: Initialized module '%s' with "
+ "parameters '%s'", ops->name, params);
+ if (!req_scan)
+ return 0;
+
+ /*
+ * Cancelling existing scan requests, if any.
+ */
+ wpa_supplicant_cancel_sched_scan(wpa_s);
+ wpa_supplicant_cancel_scan(wpa_s);
+
+ /*
+ * Firing first scan, which will lead to call autoscan_notify_scan.
+ */
+ request_scan(wpa_s);
+
+ return 0;
+}
+
+
+void autoscan_deinit(struct wpa_supplicant *wpa_s)
+{
+ if (wpa_s->autoscan && wpa_s->autoscan_priv) {
+ wpa_printf(MSG_DEBUG, "autoscan: Deinitializing module '%s'",
+ wpa_s->autoscan->name);
+ wpa_s->autoscan->deinit(wpa_s->autoscan_priv);
+ wpa_s->autoscan = NULL;
+ wpa_s->autoscan_priv = NULL;
+
+ wpa_s->scan_interval = 5;
+ wpa_s->sched_scan_interval = 0;
+ }
+}
+
+
+int autoscan_notify_scan(struct wpa_supplicant *wpa_s,
+ struct wpa_scan_results *scan_res)
+{
+ int interval;
+
+ if (wpa_s->autoscan && wpa_s->autoscan_priv) {
+ interval = wpa_s->autoscan->notify_scan(wpa_s->autoscan_priv,
+ scan_res);
+
+ if (interval <= 0)
+ return -1;
+
+ wpa_s->scan_interval = interval;
+ wpa_s->sched_scan_interval = interval;
+
+ request_scan(wpa_s);
+ }
+
+ return 0;
+}
diff --git a/contrib/wpa/wpa_supplicant/autoscan.h b/contrib/wpa/wpa_supplicant/autoscan.h
new file mode 100644
index 0000000..e2a7652
--- /dev/null
+++ b/contrib/wpa/wpa_supplicant/autoscan.h
@@ -0,0 +1,49 @@
+/*
+ * WPA Supplicant - auto scan
+ * Copyright (c) 2012, Intel Corporation. All rights reserved.
+ *
+ * This software may be distributed under the terms of the BSD license.
+ * See README for more details.
+ */
+
+#ifndef AUTOSCAN_H
+#define AUTOSCAN_H
+
+struct wpa_supplicant;
+
+struct autoscan_ops {
+ const char *name;
+
+ void * (*init)(struct wpa_supplicant *wpa_s, const char *params);
+ void (*deinit)(void *priv);
+
+ int (*notify_scan)(void *priv, struct wpa_scan_results *scan_res);
+};
+
+#ifdef CONFIG_AUTOSCAN
+
+int autoscan_init(struct wpa_supplicant *wpa_s, int req_scan);
+void autoscan_deinit(struct wpa_supplicant *wpa_s);
+int autoscan_notify_scan(struct wpa_supplicant *wpa_s,
+ struct wpa_scan_results *scan_res);
+
+#else /* CONFIG_AUTOSCAN */
+
+static inline int autoscan_init(struct wpa_supplicant *wpa_s, int req_scan)
+{
+ return 0;
+}
+
+static inline void autoscan_deinit(struct wpa_supplicant *wpa_s)
+{
+}
+
+static inline int autoscan_notify_scan(struct wpa_supplicant *wpa_s,
+ struct wpa_scan_results *scan_res)
+{
+ return 0;
+}
+
+#endif /* CONFIG_AUTOSCAN */
+
+#endif /* AUTOSCAN_H */
diff --git a/contrib/wpa/wpa_supplicant/autoscan_exponential.c b/contrib/wpa/wpa_supplicant/autoscan_exponential.c
new file mode 100644
index 0000000..424477b
--- /dev/null
+++ b/contrib/wpa/wpa_supplicant/autoscan_exponential.c
@@ -0,0 +1,104 @@
+/*
+ * WPA Supplicant - auto scan exponential module
+ * Copyright (c) 2012, Intel Corporation. All rights reserved.
+ *
+ * This software may be distributed under the terms of the BSD license.
+ * See README for more details.
+ */
+
+#include "includes.h"
+
+#include "common.h"
+#include "wpa_supplicant_i.h"
+#include "autoscan.h"
+
+struct autoscan_exponential_data {
+ struct wpa_supplicant *wpa_s;
+ int base;
+ int limit;
+ int interval;
+};
+
+
+static int
+autoscan_exponential_get_params(struct autoscan_exponential_data *data,
+ const char *params)
+{
+ const char *pos;
+
+ if (params == NULL)
+ return -1;
+
+ data->base = atoi(params);
+
+ pos = os_strchr(params, ':');
+ if (pos == NULL)
+ return -1;
+
+ pos++;
+ data->limit = atoi(pos);
+
+ return 0;
+}
+
+
+static void * autoscan_exponential_init(struct wpa_supplicant *wpa_s,
+ const char *params)
+{
+ struct autoscan_exponential_data *data;
+
+ data = os_zalloc(sizeof(struct autoscan_exponential_data));
+ if (data == NULL)
+ return NULL;
+
+ if (autoscan_exponential_get_params(data, params) < 0) {
+ os_free(data);
+ return NULL;
+ }
+
+ wpa_printf(MSG_DEBUG, "autoscan exponential: base exponential is %d "
+ "and limit is %d", data->base, data->limit);
+
+ data->wpa_s = wpa_s;
+
+ return data;
+}
+
+
+static void autoscan_exponential_deinit(void *priv)
+{
+ struct autoscan_exponential_data *data = priv;
+
+ os_free(data);
+}
+
+
+static int autoscan_exponential_notify_scan(void *priv,
+ struct wpa_scan_results *scan_res)
+{
+ struct autoscan_exponential_data *data = priv;
+
+ wpa_printf(MSG_DEBUG, "autoscan exponential: scan result "
+ "notification");
+
+ if (data->interval >= data->limit)
+ return data->limit;
+
+ if (data->interval <= 0)
+ data->interval = data->base;
+ else {
+ data->interval = data->interval * data->base;
+ if (data->interval > data->limit)
+ return data->limit;
+ }
+
+ return data->interval;
+}
+
+
+const struct autoscan_ops autoscan_exponential_ops = {
+ .name = "exponential",
+ .init = autoscan_exponential_init,
+ .deinit = autoscan_exponential_deinit,
+ .notify_scan = autoscan_exponential_notify_scan,
+};
diff --git a/contrib/wpa/wpa_supplicant/autoscan_periodic.c b/contrib/wpa/wpa_supplicant/autoscan_periodic.c
new file mode 100644
index 0000000..102d723
--- /dev/null
+++ b/contrib/wpa/wpa_supplicant/autoscan_periodic.c
@@ -0,0 +1,85 @@
+/*
+ * WPA Supplicant - auto scan periodic module
+ * Copyright (c) 2012, Intel Corporation. All rights reserved.
+ *
+ * This software may be distributed under the terms of the BSD license.
+ * See README for more details.
+ */
+
+#include "includes.h"
+
+#include "common.h"
+#include "wpa_supplicant_i.h"
+#include "autoscan.h"
+
+
+struct autoscan_periodic_data {
+ int periodic_interval;
+};
+
+
+static int autoscan_periodic_get_params(struct autoscan_periodic_data *data,
+ const char *params)
+{
+ int interval;
+
+ if (params == NULL)
+ return -1;
+
+ interval = atoi(params);
+
+ if (interval < 0)
+ return -1;
+
+ data->periodic_interval = interval;
+
+ return 0;
+}
+
+
+static void * autoscan_periodic_init(struct wpa_supplicant *wpa_s,
+ const char *params)
+{
+ struct autoscan_periodic_data *data;
+
+ data = os_zalloc(sizeof(struct autoscan_periodic_data));
+ if (data == NULL)
+ return NULL;
+
+ if (autoscan_periodic_get_params(data, params) < 0) {
+ os_free(data);
+ return NULL;
+ }
+
+ wpa_printf(MSG_DEBUG, "autoscan periodic: interval is %d",
+ data->periodic_interval);
+
+ return data;
+}
+
+
+static void autoscan_periodic_deinit(void *priv)
+{
+ struct autoscan_periodic_data *data = priv;
+
+ os_free(data);
+}
+
+
+static int autoscan_periodic_notify_scan(void *priv,
+ struct wpa_scan_results *scan_res)
+{
+ struct autoscan_periodic_data *data = priv;
+
+ wpa_printf(MSG_DEBUG, "autoscan periodic: scan result notification");
+
+ return data->periodic_interval;
+}
+
+
+const struct autoscan_ops autoscan_periodic_ops = {
+ .name = "periodic",
+ .init = autoscan_periodic_init,
+ .deinit = autoscan_periodic_deinit,
+ .notify_scan = autoscan_periodic_notify_scan,
+};
diff --git a/contrib/wpa/wpa_supplicant/bgscan.c b/contrib/wpa/wpa_supplicant/bgscan.c
index 31b5d27..9a9bd52 100644
--- a/contrib/wpa/wpa_supplicant/bgscan.c
+++ b/contrib/wpa/wpa_supplicant/bgscan.c
@@ -2,14 +2,8 @@
* WPA Supplicant - background scan and roaming interface
* Copyright (c) 2009-2010, Jouni Malinen <j@w1.fi>
*
- * This program is free software; you can redistribute it and/or modify
- * it under the terms of the GNU General Public License version 2 as
- * published by the Free Software Foundation.
- *
- * Alternatively, this software may be distributed under the terms of BSD
- * license.
- *
- * See README and COPYING for more details.
+ * This software may be distributed under the terms of the BSD license.
+ * See README for more details.
*/
#include "includes.h"
@@ -22,11 +16,17 @@
#ifdef CONFIG_BGSCAN_SIMPLE
extern const struct bgscan_ops bgscan_simple_ops;
#endif /* CONFIG_BGSCAN_SIMPLE */
+#ifdef CONFIG_BGSCAN_LEARN
+extern const struct bgscan_ops bgscan_learn_ops;
+#endif /* CONFIG_BGSCAN_LEARN */
static const struct bgscan_ops * bgscan_modules[] = {
#ifdef CONFIG_BGSCAN_SIMPLE
&bgscan_simple_ops,
#endif /* CONFIG_BGSCAN_SIMPLE */
+#ifdef CONFIG_BGSCAN_LEARN
+ &bgscan_learn_ops,
+#endif /* CONFIG_BGSCAN_LEARN */
NULL
};
@@ -88,10 +88,12 @@ void bgscan_deinit(struct wpa_supplicant *wpa_s)
}
-int bgscan_notify_scan(struct wpa_supplicant *wpa_s)
+int bgscan_notify_scan(struct wpa_supplicant *wpa_s,
+ struct wpa_scan_results *scan_res)
{
if (wpa_s->bgscan && wpa_s->bgscan_priv)
- return wpa_s->bgscan->notify_scan(wpa_s->bgscan_priv);
+ return wpa_s->bgscan->notify_scan(wpa_s->bgscan_priv,
+ scan_res);
return 0;
}
@@ -103,8 +105,13 @@ void bgscan_notify_beacon_loss(struct wpa_supplicant *wpa_s)
}
-void bgscan_notify_signal_change(struct wpa_supplicant *wpa_s, int above)
+void bgscan_notify_signal_change(struct wpa_supplicant *wpa_s, int above,
+ int current_signal, int current_noise,
+ int current_txrate)
{
if (wpa_s->bgscan && wpa_s->bgscan_priv)
- wpa_s->bgscan->notify_signal_change(wpa_s->bgscan_priv, above);
+ wpa_s->bgscan->notify_signal_change(wpa_s->bgscan_priv, above,
+ current_signal,
+ current_noise,
+ current_txrate);
}
diff --git a/contrib/wpa/wpa_supplicant/bgscan.h b/contrib/wpa/wpa_supplicant/bgscan.h
index 69e99b6..e9d15fc 100644
--- a/contrib/wpa/wpa_supplicant/bgscan.h
+++ b/contrib/wpa/wpa_supplicant/bgscan.h
@@ -2,14 +2,8 @@
* WPA Supplicant - background scan and roaming interface
* Copyright (c) 2009-2010, Jouni Malinen <j@w1.fi>
*
- * This program is free software; you can redistribute it and/or modify
- * it under the terms of the GNU General Public License version 2 as
- * published by the Free Software Foundation.
- *
- * Alternatively, this software may be distributed under the terms of BSD
- * license.
- *
- * See README and COPYING for more details.
+ * This software may be distributed under the terms of the BSD license.
+ * See README for more details.
*/
#ifndef BGSCAN_H
@@ -25,18 +19,24 @@ struct bgscan_ops {
const struct wpa_ssid *ssid);
void (*deinit)(void *priv);
- int (*notify_scan)(void *priv);
+ int (*notify_scan)(void *priv, struct wpa_scan_results *scan_res);
void (*notify_beacon_loss)(void *priv);
- void (*notify_signal_change)(void *priv, int above);
+ void (*notify_signal_change)(void *priv, int above,
+ int current_signal,
+ int current_noise,
+ int current_txrate);
};
#ifdef CONFIG_BGSCAN
int bgscan_init(struct wpa_supplicant *wpa_s, struct wpa_ssid *ssid);
void bgscan_deinit(struct wpa_supplicant *wpa_s);
-int bgscan_notify_scan(struct wpa_supplicant *wpa_s);
+int bgscan_notify_scan(struct wpa_supplicant *wpa_s,
+ struct wpa_scan_results *scan_res);
void bgscan_notify_beacon_loss(struct wpa_supplicant *wpa_s);
-void bgscan_notify_signal_change(struct wpa_supplicant *wpa_s, int above);
+void bgscan_notify_signal_change(struct wpa_supplicant *wpa_s, int above,
+ int current_signal, int current_noise,
+ int current_txrate);
#else /* CONFIG_BGSCAN */
@@ -50,7 +50,8 @@ static inline void bgscan_deinit(struct wpa_supplicant *wpa_s)
{
}
-static inline int bgscan_notify_scan(struct wpa_supplicant *wpa_s)
+static inline int bgscan_notify_scan(struct wpa_supplicant *wpa_s,
+ struct wpa_scan_results *scan_res)
{
return 0;
}
@@ -60,7 +61,9 @@ static inline void bgscan_notify_beacon_loss(struct wpa_supplicant *wpa_s)
}
static inline void bgscan_notify_signal_change(struct wpa_supplicant *wpa_s,
- int above)
+ int above, int current_signal,
+ int current_noise,
+ int current_txrate)
{
}
diff --git a/contrib/wpa/wpa_supplicant/bgscan_learn.c b/contrib/wpa/wpa_supplicant/bgscan_learn.c
new file mode 100644
index 0000000..07d31e4
--- /dev/null
+++ b/contrib/wpa/wpa_supplicant/bgscan_learn.c
@@ -0,0 +1,607 @@
+/*
+ * WPA Supplicant - background scan and roaming module: learn
+ * Copyright (c) 2009-2010, Jouni Malinen <j@w1.fi>
+ *
+ * This software may be distributed under the terms of the BSD license.
+ * See README for more details.
+ */
+
+#include "includes.h"
+
+#include "common.h"
+#include "eloop.h"
+#include "list.h"
+#include "common/ieee802_11_defs.h"
+#include "drivers/driver.h"
+#include "config_ssid.h"
+#include "wpa_supplicant_i.h"
+#include "driver_i.h"
+#include "scan.h"
+#include "bgscan.h"
+
+struct bgscan_learn_bss {
+ struct dl_list list;
+ u8 bssid[ETH_ALEN];
+ int freq;
+ u8 *neigh; /* num_neigh * ETH_ALEN buffer */
+ size_t num_neigh;
+};
+
+struct bgscan_learn_data {
+ struct wpa_supplicant *wpa_s;
+ const struct wpa_ssid *ssid;
+ int scan_interval;
+ int signal_threshold;
+ int short_interval; /* use if signal < threshold */
+ int long_interval; /* use if signal > threshold */
+ struct os_time last_bgscan;
+ char *fname;
+ struct dl_list bss;
+ int *supp_freqs;
+ int probe_idx;
+};
+
+
+static void bss_free(struct bgscan_learn_bss *bss)
+{
+ os_free(bss->neigh);
+ os_free(bss);
+}
+
+
+static int bssid_in_array(u8 *array, size_t array_len, const u8 *bssid)
+{
+ size_t i;
+
+ if (array == NULL || array_len == 0)
+ return 0;
+
+ for (i = 0; i < array_len; i++) {
+ if (os_memcmp(array + i * ETH_ALEN, bssid, ETH_ALEN) == 0)
+ return 1;
+ }
+
+ return 0;
+}
+
+
+static void bgscan_learn_add_neighbor(struct bgscan_learn_bss *bss,
+ const u8 *bssid)
+{
+ u8 *n;
+
+ if (os_memcmp(bss->bssid, bssid, ETH_ALEN) == 0)
+ return;
+ if (bssid_in_array(bss->neigh, bss->num_neigh, bssid))
+ return;
+
+ n = os_realloc_array(bss->neigh, bss->num_neigh + 1, ETH_ALEN);
+ if (n == NULL)
+ return;
+
+ os_memcpy(n + bss->num_neigh * ETH_ALEN, bssid, ETH_ALEN);
+ bss->neigh = n;
+ bss->num_neigh++;
+}
+
+
+static struct bgscan_learn_bss * bgscan_learn_get_bss(
+ struct bgscan_learn_data *data, const u8 *bssid)
+{
+ struct bgscan_learn_bss *bss;
+
+ dl_list_for_each(bss, &data->bss, struct bgscan_learn_bss, list) {
+ if (os_memcmp(bss->bssid, bssid, ETH_ALEN) == 0)
+ return bss;
+ }
+ return NULL;
+}
+
+
+static int bgscan_learn_load(struct bgscan_learn_data *data)
+{
+ FILE *f;
+ char buf[128];
+ struct bgscan_learn_bss *bss;
+
+ if (data->fname == NULL)
+ return 0;
+
+ f = fopen(data->fname, "r");
+ if (f == NULL)
+ return 0;
+
+ wpa_printf(MSG_DEBUG, "bgscan learn: Loading data from %s",
+ data->fname);
+
+ if (fgets(buf, sizeof(buf), f) == NULL ||
+ os_strncmp(buf, "wpa_supplicant-bgscan-learn\n", 28) != 0) {
+ wpa_printf(MSG_INFO, "bgscan learn: Invalid data file %s",
+ data->fname);
+ fclose(f);
+ return -1;
+ }
+
+ while (fgets(buf, sizeof(buf), f)) {
+ if (os_strncmp(buf, "BSS ", 4) == 0) {
+ bss = os_zalloc(sizeof(*bss));
+ if (!bss)
+ continue;
+ if (hwaddr_aton(buf + 4, bss->bssid) < 0) {
+ bss_free(bss);
+ continue;
+ }
+ bss->freq = atoi(buf + 4 + 18);
+ dl_list_add(&data->bss, &bss->list);
+ wpa_printf(MSG_DEBUG, "bgscan learn: Loaded BSS "
+ "entry: " MACSTR " freq=%d",
+ MAC2STR(bss->bssid), bss->freq);
+ }
+
+ if (os_strncmp(buf, "NEIGHBOR ", 9) == 0) {
+ u8 addr[ETH_ALEN];
+
+ if (hwaddr_aton(buf + 9, addr) < 0)
+ continue;
+ bss = bgscan_learn_get_bss(data, addr);
+ if (bss == NULL)
+ continue;
+ if (hwaddr_aton(buf + 9 + 18, addr) < 0)
+ continue;
+
+ bgscan_learn_add_neighbor(bss, addr);
+ }
+ }
+
+ fclose(f);
+ return 0;
+}
+
+
+static void bgscan_learn_save(struct bgscan_learn_data *data)
+{
+ FILE *f;
+ struct bgscan_learn_bss *bss;
+
+ if (data->fname == NULL)
+ return;
+
+ wpa_printf(MSG_DEBUG, "bgscan learn: Saving data to %s",
+ data->fname);
+
+ f = fopen(data->fname, "w");
+ if (f == NULL)
+ return;
+ fprintf(f, "wpa_supplicant-bgscan-learn\n");
+
+ dl_list_for_each(bss, &data->bss, struct bgscan_learn_bss, list) {
+ fprintf(f, "BSS " MACSTR " %d\n",
+ MAC2STR(bss->bssid), bss->freq);
+ }
+
+ dl_list_for_each(bss, &data->bss, struct bgscan_learn_bss, list) {
+ size_t i;
+ for (i = 0; i < bss->num_neigh; i++) {
+ fprintf(f, "NEIGHBOR " MACSTR " " MACSTR "\n",
+ MAC2STR(bss->bssid),
+ MAC2STR(bss->neigh + i * ETH_ALEN));
+ }
+ }
+
+ fclose(f);
+}
+
+
+static int in_array(int *array, int val)
+{
+ int i;
+
+ if (array == NULL)
+ return 0;
+
+ for (i = 0; array[i]; i++) {
+ if (array[i] == val)
+ return 1;
+ }
+
+ return 0;
+}
+
+
+static int * bgscan_learn_get_freqs(struct bgscan_learn_data *data,
+ size_t *count)
+{
+ struct bgscan_learn_bss *bss;
+ int *freqs = NULL, *n;
+
+ *count = 0;
+
+ dl_list_for_each(bss, &data->bss, struct bgscan_learn_bss, list) {
+ if (in_array(freqs, bss->freq))
+ continue;
+ n = os_realloc_array(freqs, *count + 2, sizeof(int));
+ if (n == NULL)
+ return freqs;
+ freqs = n;
+ freqs[*count] = bss->freq;
+ (*count)++;
+ freqs[*count] = 0;
+ }
+
+ return freqs;
+}
+
+
+static int * bgscan_learn_get_probe_freq(struct bgscan_learn_data *data,
+ int *freqs, size_t count)
+{
+ int idx, *n;
+
+ if (data->supp_freqs == NULL)
+ return freqs;
+
+ idx = data->probe_idx + 1;
+ while (idx != data->probe_idx) {
+ if (data->supp_freqs[idx] == 0) {
+ if (data->probe_idx == 0)
+ break;
+ idx = 0;
+ }
+ if (!in_array(freqs, data->supp_freqs[idx])) {
+ wpa_printf(MSG_DEBUG, "bgscan learn: Probe new freq "
+ "%u", data->supp_freqs[idx]);
+ data->probe_idx = idx;
+ n = os_realloc_array(freqs, count + 2, sizeof(int));
+ if (n == NULL)
+ return freqs;
+ freqs = n;
+ freqs[count] = data->supp_freqs[idx];
+ count++;
+ freqs[count] = 0;
+ break;
+ }
+
+ idx++;
+ }
+
+ return freqs;
+}
+
+
+static void bgscan_learn_timeout(void *eloop_ctx, void *timeout_ctx)
+{
+ struct bgscan_learn_data *data = eloop_ctx;
+ struct wpa_supplicant *wpa_s = data->wpa_s;
+ struct wpa_driver_scan_params params;
+ int *freqs = NULL;
+ size_t count, i;
+ char msg[100], *pos;
+
+ os_memset(&params, 0, sizeof(params));
+ params.num_ssids = 1;
+ params.ssids[0].ssid = data->ssid->ssid;
+ params.ssids[0].ssid_len = data->ssid->ssid_len;
+ if (data->ssid->scan_freq)
+ params.freqs = data->ssid->scan_freq;
+ else {
+ freqs = bgscan_learn_get_freqs(data, &count);
+ wpa_printf(MSG_DEBUG, "bgscan learn: BSSes in this ESS have "
+ "been seen on %u channels", (unsigned int) count);
+ freqs = bgscan_learn_get_probe_freq(data, freqs, count);
+
+ msg[0] = '\0';
+ pos = msg;
+ for (i = 0; freqs && freqs[i]; i++) {
+ int ret;
+ ret = os_snprintf(pos, msg + sizeof(msg) - pos, " %d",
+ freqs[i]);
+ if (ret < 0 || ret >= msg + sizeof(msg) - pos)
+ break;
+ pos += ret;
+ }
+ pos[0] = '\0';
+ wpa_printf(MSG_DEBUG, "bgscan learn: Scanning frequencies:%s",
+ msg);
+ params.freqs = freqs;
+ }
+
+ wpa_printf(MSG_DEBUG, "bgscan learn: Request a background scan");
+ if (wpa_supplicant_trigger_scan(wpa_s, &params)) {
+ wpa_printf(MSG_DEBUG, "bgscan learn: Failed to trigger scan");
+ eloop_register_timeout(data->scan_interval, 0,
+ bgscan_learn_timeout, data, NULL);
+ } else
+ os_get_time(&data->last_bgscan);
+ os_free(freqs);
+}
+
+
+static int bgscan_learn_get_params(struct bgscan_learn_data *data,
+ const char *params)
+{
+ const char *pos;
+
+ if (params == NULL)
+ return 0;
+
+ data->short_interval = atoi(params);
+
+ pos = os_strchr(params, ':');
+ if (pos == NULL)
+ return 0;
+ pos++;
+ data->signal_threshold = atoi(pos);
+ pos = os_strchr(pos, ':');
+ if (pos == NULL) {
+ wpa_printf(MSG_ERROR, "bgscan learn: Missing scan interval "
+ "for high signal");
+ return -1;
+ }
+ pos++;
+ data->long_interval = atoi(pos);
+ pos = os_strchr(pos, ':');
+ if (pos) {
+ pos++;
+ data->fname = os_strdup(pos);
+ }
+
+ return 0;
+}
+
+
+static int * bgscan_learn_get_supp_freqs(struct wpa_supplicant *wpa_s)
+{
+ struct hostapd_hw_modes *modes;
+ int i, j, *freqs = NULL, *n;
+ size_t count = 0;
+
+ modes = wpa_s->hw.modes;
+ if (modes == NULL)
+ return NULL;
+
+ for (i = 0; i < wpa_s->hw.num_modes; i++) {
+ for (j = 0; j < modes[i].num_channels; j++) {
+ if (modes[i].channels[j].flag & HOSTAPD_CHAN_DISABLED)
+ continue;
+ n = os_realloc_array(freqs, count + 2, sizeof(int));
+ if (n == NULL)
+ continue;
+
+ freqs = n;
+ freqs[count] = modes[i].channels[j].freq;
+ count++;
+ freqs[count] = 0;
+ }
+ }
+
+ return freqs;
+}
+
+
+static void * bgscan_learn_init(struct wpa_supplicant *wpa_s,
+ const char *params,
+ const struct wpa_ssid *ssid)
+{
+ struct bgscan_learn_data *data;
+
+ data = os_zalloc(sizeof(*data));
+ if (data == NULL)
+ return NULL;
+ dl_list_init(&data->bss);
+ data->wpa_s = wpa_s;
+ data->ssid = ssid;
+ if (bgscan_learn_get_params(data, params) < 0) {
+ os_free(data->fname);
+ os_free(data);
+ return NULL;
+ }
+ if (data->short_interval <= 0)
+ data->short_interval = 30;
+ if (data->long_interval <= 0)
+ data->long_interval = 30;
+
+ if (bgscan_learn_load(data) < 0) {
+ os_free(data->fname);
+ os_free(data);
+ return NULL;
+ }
+
+ wpa_printf(MSG_DEBUG, "bgscan learn: Signal strength threshold %d "
+ "Short bgscan interval %d Long bgscan interval %d",
+ data->signal_threshold, data->short_interval,
+ data->long_interval);
+
+ if (data->signal_threshold &&
+ wpa_drv_signal_monitor(wpa_s, data->signal_threshold, 4) < 0) {
+ wpa_printf(MSG_ERROR, "bgscan learn: Failed to enable "
+ "signal strength monitoring");
+ }
+
+ data->supp_freqs = bgscan_learn_get_supp_freqs(wpa_s);
+ data->scan_interval = data->short_interval;
+ eloop_register_timeout(data->scan_interval, 0, bgscan_learn_timeout,
+ data, NULL);
+
+ /*
+ * This function is called immediately after an association, so it is
+ * reasonable to assume that a scan was completed recently. This makes
+ * us skip an immediate new scan in cases where the current signal
+ * level is below the bgscan threshold.
+ */
+ os_get_time(&data->last_bgscan);
+
+ return data;
+}
+
+
+static void bgscan_learn_deinit(void *priv)
+{
+ struct bgscan_learn_data *data = priv;
+ struct bgscan_learn_bss *bss, *n;
+
+ bgscan_learn_save(data);
+ eloop_cancel_timeout(bgscan_learn_timeout, data, NULL);
+ if (data->signal_threshold)
+ wpa_drv_signal_monitor(data->wpa_s, 0, 0);
+ os_free(data->fname);
+ dl_list_for_each_safe(bss, n, &data->bss, struct bgscan_learn_bss,
+ list) {
+ dl_list_del(&bss->list);
+ bss_free(bss);
+ }
+ os_free(data->supp_freqs);
+ os_free(data);
+}
+
+
+static int bgscan_learn_bss_match(struct bgscan_learn_data *data,
+ struct wpa_scan_res *bss)
+{
+ const u8 *ie;
+
+ ie = wpa_scan_get_ie(bss, WLAN_EID_SSID);
+ if (ie == NULL)
+ return 0;
+
+ if (data->ssid->ssid_len != ie[1] ||
+ os_memcmp(data->ssid->ssid, ie + 2, ie[1]) != 0)
+ return 0; /* SSID mismatch */
+
+ return 1;
+}
+
+
+static int bgscan_learn_notify_scan(void *priv,
+ struct wpa_scan_results *scan_res)
+{
+ struct bgscan_learn_data *data = priv;
+ size_t i, j;
+#define MAX_BSS 50
+ u8 bssid[MAX_BSS * ETH_ALEN];
+ size_t num_bssid = 0;
+
+ wpa_printf(MSG_DEBUG, "bgscan learn: scan result notification");
+
+ eloop_cancel_timeout(bgscan_learn_timeout, data, NULL);
+ eloop_register_timeout(data->scan_interval, 0, bgscan_learn_timeout,
+ data, NULL);
+
+ for (i = 0; i < scan_res->num; i++) {
+ struct wpa_scan_res *res = scan_res->res[i];
+ if (!bgscan_learn_bss_match(data, res))
+ continue;
+
+ if (num_bssid < MAX_BSS) {
+ os_memcpy(bssid + num_bssid * ETH_ALEN, res->bssid,
+ ETH_ALEN);
+ num_bssid++;
+ }
+ }
+ wpa_printf(MSG_DEBUG, "bgscan learn: %u matching BSSes in scan "
+ "results", (unsigned int) num_bssid);
+
+ for (i = 0; i < scan_res->num; i++) {
+ struct wpa_scan_res *res = scan_res->res[i];
+ struct bgscan_learn_bss *bss;
+
+ if (!bgscan_learn_bss_match(data, res))
+ continue;
+
+ bss = bgscan_learn_get_bss(data, res->bssid);
+ if (bss && bss->freq != res->freq) {
+ wpa_printf(MSG_DEBUG, "bgscan learn: Update BSS "
+ MACSTR " freq %d -> %d",
+ MAC2STR(res->bssid), bss->freq, res->freq);
+ bss->freq = res->freq;
+ } else if (!bss) {
+ wpa_printf(MSG_DEBUG, "bgscan learn: Add BSS " MACSTR
+ " freq=%d", MAC2STR(res->bssid), res->freq);
+ bss = os_zalloc(sizeof(*bss));
+ if (!bss)
+ continue;
+ os_memcpy(bss->bssid, res->bssid, ETH_ALEN);
+ bss->freq = res->freq;
+ dl_list_add(&data->bss, &bss->list);
+ }
+
+ for (j = 0; j < num_bssid; j++) {
+ u8 *addr = bssid + j * ETH_ALEN;
+ bgscan_learn_add_neighbor(bss, addr);
+ }
+ }
+
+ /*
+ * A more advanced bgscan could process scan results internally, select
+ * the BSS and request roam if needed. This sample uses the existing
+ * BSS/ESS selection routine. Change this to return 1 if selection is
+ * done inside the bgscan module.
+ */
+
+ return 0;
+}
+
+
+static void bgscan_learn_notify_beacon_loss(void *priv)
+{
+ wpa_printf(MSG_DEBUG, "bgscan learn: beacon loss");
+ /* TODO: speed up background scanning */
+}
+
+
+static void bgscan_learn_notify_signal_change(void *priv, int above,
+ int current_signal,
+ int current_noise,
+ int current_txrate)
+{
+ struct bgscan_learn_data *data = priv;
+ int scan = 0;
+ struct os_time now;
+
+ if (data->short_interval == data->long_interval ||
+ data->signal_threshold == 0)
+ return;
+
+ wpa_printf(MSG_DEBUG, "bgscan learn: signal level changed "
+ "(above=%d current_signal=%d current_noise=%d "
+ "current_txrate=%d)", above, current_signal,
+ current_noise, current_txrate);
+ if (data->scan_interval == data->long_interval && !above) {
+ wpa_printf(MSG_DEBUG, "bgscan learn: Start using short bgscan "
+ "interval");
+ data->scan_interval = data->short_interval;
+ os_get_time(&now);
+ if (now.sec > data->last_bgscan.sec + 1)
+ scan = 1;
+ } else if (data->scan_interval == data->short_interval && above) {
+ wpa_printf(MSG_DEBUG, "bgscan learn: Start using long bgscan "
+ "interval");
+ data->scan_interval = data->long_interval;
+ eloop_cancel_timeout(bgscan_learn_timeout, data, NULL);
+ eloop_register_timeout(data->scan_interval, 0,
+ bgscan_learn_timeout, data, NULL);
+ } else if (!above) {
+ /*
+ * Signal dropped further 4 dB. Request a new scan if we have
+ * not yet scanned in a while.
+ */
+ os_get_time(&now);
+ if (now.sec > data->last_bgscan.sec + 10)
+ scan = 1;
+ }
+
+ if (scan) {
+ wpa_printf(MSG_DEBUG, "bgscan learn: Trigger immediate scan");
+ eloop_cancel_timeout(bgscan_learn_timeout, data, NULL);
+ eloop_register_timeout(0, 0, bgscan_learn_timeout, data, NULL);
+ }
+}
+
+
+const struct bgscan_ops bgscan_learn_ops = {
+ .name = "learn",
+ .init = bgscan_learn_init,
+ .deinit = bgscan_learn_deinit,
+ .notify_scan = bgscan_learn_notify_scan,
+ .notify_beacon_loss = bgscan_learn_notify_beacon_loss,
+ .notify_signal_change = bgscan_learn_notify_signal_change,
+};
diff --git a/contrib/wpa/wpa_supplicant/bgscan_simple.c b/contrib/wpa/wpa_supplicant/bgscan_simple.c
index 8e80b12..479f703 100644
--- a/contrib/wpa/wpa_supplicant/bgscan_simple.c
+++ b/contrib/wpa/wpa_supplicant/bgscan_simple.c
@@ -2,14 +2,8 @@
* WPA Supplicant - background scan and roaming module: simple
* Copyright (c) 2009-2010, Jouni Malinen <j@w1.fi>
*
- * This program is free software; you can redistribute it and/or modify
- * it under the terms of the GNU General Public License version 2 as
- * published by the Free Software Foundation.
- *
- * Alternatively, this software may be distributed under the terms of BSD
- * license.
- *
- * See README and COPYING for more details.
+ * This software may be distributed under the terms of the BSD license.
+ * See README for more details.
*/
#include "includes.h"
@@ -28,6 +22,8 @@ struct bgscan_simple_data {
const struct wpa_ssid *ssid;
int scan_interval;
int signal_threshold;
+ int short_scan_count; /* counter for scans using short scan interval */
+ int max_short_scans; /* maximum times we short-scan before back-off */
int short_interval; /* use if signal < threshold */
int long_interval; /* use if signal > threshold */
struct os_time last_bgscan;
@@ -57,8 +53,30 @@ static void bgscan_simple_timeout(void *eloop_ctx, void *timeout_ctx)
wpa_printf(MSG_DEBUG, "bgscan simple: Failed to trigger scan");
eloop_register_timeout(data->scan_interval, 0,
bgscan_simple_timeout, data, NULL);
- } else
+ } else {
+ if (data->scan_interval == data->short_interval) {
+ data->short_scan_count++;
+ /*
+ * Spend at most the duration of a long scan interval
+ * scanning at the short scan interval. After that,
+ * revert to the long scan interval.
+ */
+ if (data->short_scan_count > data->max_short_scans) {
+ data->scan_interval = data->long_interval;
+ wpa_printf(MSG_DEBUG, "bgscan simple: Backing "
+ "off to long scan interval");
+ }
+ } else if (data->short_scan_count > 0) {
+ /*
+ * If we lasted a long scan interval without any
+ * CQM triggers, decrease the short-scan count,
+ * which allows 1 more short-scan interval to
+ * occur in the future when CQM triggers.
+ */
+ data->short_scan_count--;
+ }
os_get_time(&data->last_bgscan);
+ }
}
@@ -122,6 +140,16 @@ static void * bgscan_simple_init(struct wpa_supplicant *wpa_s,
}
data->scan_interval = data->short_interval;
+ data->max_short_scans = data->long_interval / data->short_interval + 1;
+ if (data->signal_threshold) {
+ /* Poll for signal info to set initial scan interval */
+ struct wpa_signal_info siginfo;
+ if (wpa_drv_signal_poll(wpa_s, &siginfo) == 0 &&
+ siginfo.current_signal >= data->signal_threshold)
+ data->scan_interval = data->long_interval;
+ }
+ wpa_printf(MSG_DEBUG, "bgscan simple: Init scan interval: %d",
+ data->scan_interval);
eloop_register_timeout(data->scan_interval, 0, bgscan_simple_timeout,
data, NULL);
@@ -147,7 +175,8 @@ static void bgscan_simple_deinit(void *priv)
}
-static int bgscan_simple_notify_scan(void *priv)
+static int bgscan_simple_notify_scan(void *priv,
+ struct wpa_scan_results *scan_res)
{
struct bgscan_simple_data *data = priv;
@@ -175,7 +204,10 @@ static void bgscan_simple_notify_beacon_loss(void *priv)
}
-static void bgscan_simple_notify_signal_change(void *priv, int above)
+static void bgscan_simple_notify_signal_change(void *priv, int above,
+ int current_signal,
+ int current_noise,
+ int current_txrate)
{
struct bgscan_simple_data *data = priv;
int scan = 0;
@@ -186,14 +218,35 @@ static void bgscan_simple_notify_signal_change(void *priv, int above)
return;
wpa_printf(MSG_DEBUG, "bgscan simple: signal level changed "
- "(above=%d)", above);
+ "(above=%d current_signal=%d current_noise=%d "
+ "current_txrate=%d))", above, current_signal,
+ current_noise, current_txrate);
if (data->scan_interval == data->long_interval && !above) {
wpa_printf(MSG_DEBUG, "bgscan simple: Start using short "
"bgscan interval");
data->scan_interval = data->short_interval;
os_get_time(&now);
- if (now.sec > data->last_bgscan.sec + 1)
+ if (now.sec > data->last_bgscan.sec + 1 &&
+ data->short_scan_count <= data->max_short_scans)
+ /*
+ * If we haven't just previously (<1 second ago)
+ * performed a scan, and we haven't depleted our
+ * budget for short-scans, perform a scan
+ * immediately.
+ */
scan = 1;
+ else if (data->last_bgscan.sec + data->long_interval >
+ now.sec + data->scan_interval) {
+ /*
+ * Restart scan interval timer if currently scheduled
+ * scan is too far in the future.
+ */
+ eloop_cancel_timeout(bgscan_simple_timeout, data,
+ NULL);
+ eloop_register_timeout(data->scan_interval, 0,
+ bgscan_simple_timeout, data,
+ NULL);
+ }
} else if (data->scan_interval == data->short_interval && above) {
wpa_printf(MSG_DEBUG, "bgscan simple: Start using long bgscan "
"interval");
diff --git a/contrib/wpa/wpa_supplicant/blacklist.c b/contrib/wpa/wpa_supplicant/blacklist.c
index 4ffb220..e53dc38 100644
--- a/contrib/wpa/wpa_supplicant/blacklist.c
+++ b/contrib/wpa/wpa_supplicant/blacklist.c
@@ -2,14 +2,8 @@
* wpa_supplicant - Temporary BSSID blacklist
* Copyright (c) 2003-2007, Jouni Malinen <j@w1.fi>
*
- * This program is free software; you can redistribute it and/or modify
- * it under the terms of the GNU General Public License version 2 as
- * published by the Free Software Foundation.
- *
- * Alternatively, this software may be distributed under the terms of BSD
- * license.
- *
- * See README and COPYING for more details.
+ * This software may be distributed under the terms of the BSD license.
+ * See README for more details.
*/
#include "includes.h"
@@ -29,6 +23,9 @@ struct wpa_blacklist * wpa_blacklist_get(struct wpa_supplicant *wpa_s,
{
struct wpa_blacklist *e;
+ if (wpa_s == NULL || bssid == NULL)
+ return NULL;
+
e = wpa_s->blacklist;
while (e) {
if (os_memcmp(e->bssid, bssid, ETH_ALEN) == 0)
@@ -44,7 +41,7 @@ struct wpa_blacklist * wpa_blacklist_get(struct wpa_supplicant *wpa_s,
* wpa_blacklist_add - Add an BSSID to the blacklist
* @wpa_s: Pointer to wpa_supplicant data
* @bssid: BSSID to be added to the blacklist
- * Returns: 0 on success, -1 on failure
+ * Returns: Current blacklist count on success, -1 on failure
*
* This function adds the specified BSSID to the blacklist or increases the
* blacklist count if the BSSID was already listed. It should be called when
@@ -60,13 +57,16 @@ int wpa_blacklist_add(struct wpa_supplicant *wpa_s, const u8 *bssid)
{
struct wpa_blacklist *e;
+ if (wpa_s == NULL || bssid == NULL)
+ return -1;
+
e = wpa_blacklist_get(wpa_s, bssid);
if (e) {
e->count++;
wpa_printf(MSG_DEBUG, "BSSID " MACSTR " blacklist count "
"incremented to %d",
MAC2STR(bssid), e->count);
- return 0;
+ return e->count;
}
e = os_zalloc(sizeof(*e));
@@ -79,7 +79,7 @@ int wpa_blacklist_add(struct wpa_supplicant *wpa_s, const u8 *bssid)
wpa_printf(MSG_DEBUG, "Added BSSID " MACSTR " into blacklist",
MAC2STR(bssid));
- return 0;
+ return e->count;
}
@@ -93,6 +93,9 @@ int wpa_blacklist_del(struct wpa_supplicant *wpa_s, const u8 *bssid)
{
struct wpa_blacklist *e, *prev = NULL;
+ if (wpa_s == NULL || bssid == NULL)
+ return -1;
+
e = wpa_s->blacklist;
while (e) {
if (os_memcmp(e->bssid, bssid, ETH_ALEN) == 0) {
@@ -120,14 +123,19 @@ int wpa_blacklist_del(struct wpa_supplicant *wpa_s, const u8 *bssid)
void wpa_blacklist_clear(struct wpa_supplicant *wpa_s)
{
struct wpa_blacklist *e, *prev;
+ int max_count = 0;
e = wpa_s->blacklist;
wpa_s->blacklist = NULL;
while (e) {
+ if (e->count > max_count)
+ max_count = e->count;
prev = e;
e = e->next;
wpa_printf(MSG_DEBUG, "Removed BSSID " MACSTR " from "
"blacklist (clear)", MAC2STR(prev->bssid));
os_free(prev);
}
+
+ wpa_s->extra_blacklist_count += max_count;
}
diff --git a/contrib/wpa/wpa_supplicant/blacklist.h b/contrib/wpa/wpa_supplicant/blacklist.h
index de280cd..ae06986 100644
--- a/contrib/wpa/wpa_supplicant/blacklist.h
+++ b/contrib/wpa/wpa_supplicant/blacklist.h
@@ -2,14 +2,8 @@
* wpa_supplicant - Temporary BSSID blacklist
* Copyright (c) 2003-2007, Jouni Malinen <j@w1.fi>
*
- * This program is free software; you can redistribute it and/or modify
- * it under the terms of the GNU General Public License version 2 as
- * published by the Free Software Foundation.
- *
- * Alternatively, this software may be distributed under the terms of BSD
- * license.
- *
- * See README and COPYING for more details.
+ * This software may be distributed under the terms of the BSD license.
+ * See README for more details.
*/
#ifndef BLACKLIST_H
diff --git a/contrib/wpa/wpa_supplicant/bss.c b/contrib/wpa/wpa_supplicant/bss.c
index e2ac230..87b7db8 100644
--- a/contrib/wpa/wpa_supplicant/bss.c
+++ b/contrib/wpa/wpa_supplicant/bss.c
@@ -1,15 +1,9 @@
/*
* BSS table
- * Copyright (c) 2009-2010, Jouni Malinen <j@w1.fi>
+ * Copyright (c) 2009-2012, Jouni Malinen <j@w1.fi>
*
- * This program is free software; you can redistribute it and/or modify
- * it under the terms of the GNU General Public License version 2 as
- * published by the Free Software Foundation.
- *
- * Alternatively, this software may be distributed under the terms of BSD
- * license.
- *
- * See README and COPYING for more details.
+ * This software may be distributed under the terms of the BSD license.
+ * See README for more details.
*/
#include "utils/includes.h"
@@ -30,25 +24,6 @@
*/
#define WPA_BSS_EXPIRATION_PERIOD 10
-/**
- * WPA_BSS_EXPIRATION_AGE - BSS entry age after which it can be expired
- *
- * This value control the time in seconds after which a BSS entry gets removed
- * if it has not been updated or is not in use.
- */
-#define WPA_BSS_EXPIRATION_AGE 180
-
-/**
- * WPA_BSS_EXPIRATION_SCAN_COUNT - Expire BSS after number of scans
- *
- * If the BSS entry has not been seen in this many scans, it will be removed.
- * Value 1 means that the entry is removed after the first scan without the
- * BSSID being seen. Larger values can be used to avoid BSS entries
- * disappearing if they are not visible in every scan (e.g., low signal quality
- * or interference).
- */
-#define WPA_BSS_EXPIRATION_SCAN_COUNT 2
-
#define WPA_BSS_FREQ_CHANGED_FLAG BIT(0)
#define WPA_BSS_SIGNAL_CHANGED_FLAG BIT(1)
#define WPA_BSS_PRIVACY_CHANGED_FLAG BIT(2)
@@ -60,23 +35,185 @@
#define WPA_BSS_IES_CHANGED_FLAG BIT(8)
-static void wpa_bss_remove(struct wpa_supplicant *wpa_s, struct wpa_bss *bss)
+static void wpa_bss_set_hessid(struct wpa_bss *bss)
+{
+#ifdef CONFIG_INTERWORKING
+ const u8 *ie = wpa_bss_get_ie(bss, WLAN_EID_INTERWORKING);
+ if (ie == NULL || (ie[1] != 7 && ie[1] != 9)) {
+ os_memset(bss->hessid, 0, ETH_ALEN);
+ return;
+ }
+ if (ie[1] == 7)
+ os_memcpy(bss->hessid, ie + 3, ETH_ALEN);
+ else
+ os_memcpy(bss->hessid, ie + 5, ETH_ALEN);
+#endif /* CONFIG_INTERWORKING */
+}
+
+
+/**
+ * wpa_bss_anqp_alloc - Allocate ANQP data structure for a BSS entry
+ * Returns: Allocated ANQP data structure or %NULL on failure
+ *
+ * The allocated ANQP data structure has its users count set to 1. It may be
+ * shared by multiple BSS entries and each shared entry is freed with
+ * wpa_bss_anqp_free().
+ */
+struct wpa_bss_anqp * wpa_bss_anqp_alloc(void)
+{
+ struct wpa_bss_anqp *anqp;
+ anqp = os_zalloc(sizeof(*anqp));
+ if (anqp == NULL)
+ return NULL;
+ anqp->users = 1;
+ return anqp;
+}
+
+
+/**
+ * wpa_bss_anqp_clone - Clone an ANQP data structure
+ * @anqp: ANQP data structure from wpa_bss_anqp_alloc()
+ * Returns: Cloned ANQP data structure or %NULL on failure
+ */
+static struct wpa_bss_anqp * wpa_bss_anqp_clone(struct wpa_bss_anqp *anqp)
+{
+ struct wpa_bss_anqp *n;
+
+ n = os_zalloc(sizeof(*n));
+ if (n == NULL)
+ return NULL;
+
+#define ANQP_DUP(f) if (anqp->f) n->f = wpabuf_dup(anqp->f)
+#ifdef CONFIG_INTERWORKING
+ ANQP_DUP(venue_name);
+ ANQP_DUP(network_auth_type);
+ ANQP_DUP(roaming_consortium);
+ ANQP_DUP(ip_addr_type_availability);
+ ANQP_DUP(nai_realm);
+ ANQP_DUP(anqp_3gpp);
+ ANQP_DUP(domain_name);
+#endif /* CONFIG_INTERWORKING */
+#ifdef CONFIG_HS20
+ ANQP_DUP(hs20_operator_friendly_name);
+ ANQP_DUP(hs20_wan_metrics);
+ ANQP_DUP(hs20_connection_capability);
+ ANQP_DUP(hs20_operating_class);
+#endif /* CONFIG_HS20 */
+#undef ANQP_DUP
+
+ return n;
+}
+
+
+/**
+ * wpa_bss_anqp_unshare_alloc - Unshare ANQP data (if shared) in a BSS entry
+ * @bss: BSS entry
+ * Returns: 0 on success, -1 on failure
+ *
+ * This function ensures the specific BSS entry has an ANQP data structure that
+ * is not shared with any other BSS entry.
+ */
+int wpa_bss_anqp_unshare_alloc(struct wpa_bss *bss)
{
+ struct wpa_bss_anqp *anqp;
+
+ if (bss->anqp && bss->anqp->users > 1) {
+ /* allocated, but shared - clone an unshared copy */
+ anqp = wpa_bss_anqp_clone(bss->anqp);
+ if (anqp == NULL)
+ return -1;
+ anqp->users = 1;
+ bss->anqp->users--;
+ bss->anqp = anqp;
+ return 0;
+ }
+
+ if (bss->anqp)
+ return 0; /* already allocated and not shared */
+
+ /* not allocated - allocate a new storage area */
+ bss->anqp = wpa_bss_anqp_alloc();
+ return bss->anqp ? 0 : -1;
+}
+
+
+/**
+ * wpa_bss_anqp_free - Free an ANQP data structure
+ * @anqp: ANQP data structure from wpa_bss_anqp_alloc() or wpa_bss_anqp_clone()
+ */
+static void wpa_bss_anqp_free(struct wpa_bss_anqp *anqp)
+{
+ if (anqp == NULL)
+ return;
+
+ anqp->users--;
+ if (anqp->users > 0) {
+ /* Another BSS entry holds a pointer to this ANQP info */
+ return;
+ }
+
+#ifdef CONFIG_INTERWORKING
+ wpabuf_free(anqp->venue_name);
+ wpabuf_free(anqp->network_auth_type);
+ wpabuf_free(anqp->roaming_consortium);
+ wpabuf_free(anqp->ip_addr_type_availability);
+ wpabuf_free(anqp->nai_realm);
+ wpabuf_free(anqp->anqp_3gpp);
+ wpabuf_free(anqp->domain_name);
+#endif /* CONFIG_INTERWORKING */
+#ifdef CONFIG_HS20
+ wpabuf_free(anqp->hs20_operator_friendly_name);
+ wpabuf_free(anqp->hs20_wan_metrics);
+ wpabuf_free(anqp->hs20_connection_capability);
+ wpabuf_free(anqp->hs20_operating_class);
+#endif /* CONFIG_HS20 */
+
+ os_free(anqp);
+}
+
+
+static void wpa_bss_remove(struct wpa_supplicant *wpa_s, struct wpa_bss *bss,
+ const char *reason)
+{
+ if (wpa_s->last_scan_res) {
+ unsigned int i;
+ for (i = 0; i < wpa_s->last_scan_res_used; i++) {
+ if (wpa_s->last_scan_res[i] == bss) {
+ os_memmove(&wpa_s->last_scan_res[i],
+ &wpa_s->last_scan_res[i + 1],
+ (wpa_s->last_scan_res_used - i - 1)
+ * sizeof(struct wpa_bss *));
+ wpa_s->last_scan_res_used--;
+ break;
+ }
+ }
+ }
dl_list_del(&bss->list);
dl_list_del(&bss->list_id);
wpa_s->num_bss--;
- wpa_printf(MSG_DEBUG, "BSS: Remove id %u BSSID " MACSTR " SSID '%s'",
- bss->id, MAC2STR(bss->bssid),
- wpa_ssid_txt(bss->ssid, bss->ssid_len));
+ wpa_dbg(wpa_s, MSG_DEBUG, "BSS: Remove id %u BSSID " MACSTR
+ " SSID '%s' due to %s", bss->id, MAC2STR(bss->bssid),
+ wpa_ssid_txt(bss->ssid, bss->ssid_len), reason);
wpas_notify_bss_removed(wpa_s, bss->bssid, bss->id);
+ wpa_bss_anqp_free(bss->anqp);
os_free(bss);
}
+/**
+ * wpa_bss_get - Fetch a BSS table entry based on BSSID and SSID
+ * @wpa_s: Pointer to wpa_supplicant data
+ * @bssid: BSSID
+ * @ssid: SSID
+ * @ssid_len: Length of @ssid
+ * Returns: Pointer to the BSS entry or %NULL if not found
+ */
struct wpa_bss * wpa_bss_get(struct wpa_supplicant *wpa_s, const u8 *bssid,
const u8 *ssid, size_t ssid_len)
{
struct wpa_bss *bss;
+ if (!wpa_supplicant_filter_bssid_match(wpa_s, bssid))
+ return NULL;
dl_list_for_each(bss, &wpa_s->bss, struct wpa_bss, list) {
if (os_memcmp(bss->bssid, bssid, ETH_ALEN) == 0 &&
bss->ssid_len == ssid_len &&
@@ -112,15 +249,79 @@ static void wpa_bss_copy_res(struct wpa_bss *dst, struct wpa_scan_res *src)
}
-static void wpa_bss_add(struct wpa_supplicant *wpa_s,
- const u8 *ssid, size_t ssid_len,
- struct wpa_scan_res *res)
+static int wpa_bss_known(struct wpa_supplicant *wpa_s, struct wpa_bss *bss)
+{
+ struct wpa_ssid *ssid;
+
+ for (ssid = wpa_s->conf->ssid; ssid; ssid = ssid->next) {
+ if (ssid->ssid == NULL || ssid->ssid_len == 0)
+ continue;
+ if (ssid->ssid_len == bss->ssid_len &&
+ os_memcmp(ssid->ssid, bss->ssid, ssid->ssid_len) == 0)
+ return 1;
+ }
+
+ return 0;
+}
+
+
+static int wpa_bss_in_use(struct wpa_supplicant *wpa_s, struct wpa_bss *bss)
+{
+ return bss == wpa_s->current_bss ||
+ os_memcmp(bss->bssid, wpa_s->bssid, ETH_ALEN) == 0 ||
+ os_memcmp(bss->bssid, wpa_s->pending_bssid, ETH_ALEN) == 0;
+}
+
+
+static int wpa_bss_remove_oldest_unknown(struct wpa_supplicant *wpa_s)
+{
+ struct wpa_bss *bss;
+
+ dl_list_for_each(bss, &wpa_s->bss, struct wpa_bss, list) {
+ if (!wpa_bss_known(wpa_s, bss)) {
+ wpa_bss_remove(wpa_s, bss, __func__);
+ return 0;
+ }
+ }
+
+ return -1;
+}
+
+
+static int wpa_bss_remove_oldest(struct wpa_supplicant *wpa_s)
+{
+ struct wpa_bss *bss;
+
+ /*
+ * Remove the oldest entry that does not match with any configured
+ * network.
+ */
+ if (wpa_bss_remove_oldest_unknown(wpa_s) == 0)
+ return 0;
+
+ /*
+ * Remove the oldest entry that isn't currently in use.
+ */
+ dl_list_for_each(bss, &wpa_s->bss, struct wpa_bss, list) {
+ if (!wpa_bss_in_use(wpa_s, bss)) {
+ wpa_bss_remove(wpa_s, bss, __func__);
+ return 0;
+ }
+ }
+
+ return -1;
+}
+
+
+static struct wpa_bss * wpa_bss_add(struct wpa_supplicant *wpa_s,
+ const u8 *ssid, size_t ssid_len,
+ struct wpa_scan_res *res)
{
struct wpa_bss *bss;
bss = os_zalloc(sizeof(*bss) + res->ie_len + res->beacon_ie_len);
if (bss == NULL)
- return;
+ return NULL;
bss->id = wpa_s->bss_next_id++;
bss->last_update_idx = wpa_s->bss_update_idx;
wpa_bss_copy_res(bss, res);
@@ -129,18 +330,23 @@ static void wpa_bss_add(struct wpa_supplicant *wpa_s,
bss->ie_len = res->ie_len;
bss->beacon_ie_len = res->beacon_ie_len;
os_memcpy(bss + 1, res + 1, res->ie_len + res->beacon_ie_len);
+ wpa_bss_set_hessid(bss);
dl_list_add_tail(&wpa_s->bss, &bss->list);
dl_list_add_tail(&wpa_s->bss_id, &bss->list_id);
wpa_s->num_bss++;
- wpa_printf(MSG_DEBUG, "BSS: Add new id %u BSSID " MACSTR " SSID '%s'",
- bss->id, MAC2STR(bss->bssid), wpa_ssid_txt(ssid, ssid_len));
+ wpa_dbg(wpa_s, MSG_DEBUG, "BSS: Add new id %u BSSID " MACSTR
+ " SSID '%s'",
+ bss->id, MAC2STR(bss->bssid), wpa_ssid_txt(ssid, ssid_len));
wpas_notify_bss_added(wpa_s, bss->bssid, bss->id);
- if (wpa_s->num_bss > wpa_s->conf->bss_max_count) {
- /* Remove the oldest entry */
- wpa_bss_remove(wpa_s, dl_list_first(&wpa_s->bss,
- struct wpa_bss, list));
+ if (wpa_s->num_bss > wpa_s->conf->bss_max_count &&
+ wpa_bss_remove_oldest(wpa_s) != 0) {
+ wpa_printf(MSG_ERROR, "Increasing the MAX BSS count to %d "
+ "because all BSSes are in use. We should normally "
+ "not get here!", (int) wpa_s->num_bss);
+ wpa_s->conf->bss_max_count = wpa_s->num_bss;
}
+ return bss;
}
@@ -187,8 +393,11 @@ static int are_ies_equal(const struct wpa_bss *old,
new_ie_len = new_ie ? new_ie[1] + 2 : 0;
}
- ret = (old_ie_len == new_ie_len &&
- os_memcmp(old_ie, new_ie, old_ie_len) == 0);
+ if (!old_ie || !new_ie)
+ ret = !old_ie && !new_ie;
+ else
+ ret = (old_ie_len == new_ie_len &&
+ os_memcmp(old_ie, new_ie, old_ie_len) == 0);
wpabuf_free(old_ie_buff);
wpabuf_free(new_ie_buff);
@@ -269,8 +478,9 @@ static void notify_bss_changes(struct wpa_supplicant *wpa_s, u32 changes,
}
-static void wpa_bss_update(struct wpa_supplicant *wpa_s, struct wpa_bss *bss,
- struct wpa_scan_res *res)
+static struct wpa_bss *
+wpa_bss_update(struct wpa_supplicant *wpa_s, struct wpa_bss *bss,
+ struct wpa_scan_res *res)
{
u32 changes;
@@ -292,6 +502,15 @@ static void wpa_bss_update(struct wpa_supplicant *wpa_s, struct wpa_bss *bss,
nbss = os_realloc(bss, sizeof(*bss) + res->ie_len +
res->beacon_ie_len);
if (nbss) {
+ unsigned int i;
+ for (i = 0; i < wpa_s->last_scan_res_used; i++) {
+ if (wpa_s->last_scan_res[i] == bss) {
+ wpa_s->last_scan_res[i] = nbss;
+ break;
+ }
+ }
+ if (wpa_s->current_bss == bss)
+ wpa_s->current_bss = nbss;
bss = nbss;
os_memcpy(bss + 1, res + 1,
res->ie_len + res->beacon_ie_len);
@@ -300,53 +519,104 @@ static void wpa_bss_update(struct wpa_supplicant *wpa_s, struct wpa_bss *bss,
}
dl_list_add(prev, &bss->list_id);
}
+ if (changes & WPA_BSS_IES_CHANGED_FLAG)
+ wpa_bss_set_hessid(bss);
dl_list_add_tail(&wpa_s->bss, &bss->list);
notify_bss_changes(wpa_s, changes, bss);
-}
-
-static int wpa_bss_in_use(struct wpa_supplicant *wpa_s, struct wpa_bss *bss)
-{
- return bss == wpa_s->current_bss ||
- os_memcmp(bss->bssid, wpa_s->bssid, ETH_ALEN) == 0 ||
- os_memcmp(bss->bssid, wpa_s->pending_bssid, ETH_ALEN) == 0;
+ return bss;
}
+/**
+ * wpa_bss_update_start - Start a BSS table update from scan results
+ * @wpa_s: Pointer to wpa_supplicant data
+ *
+ * This function is called at the start of each BSS table update round for new
+ * scan results. The actual scan result entries are indicated with calls to
+ * wpa_bss_update_scan_res() and the update round is finished with a call to
+ * wpa_bss_update_end().
+ */
void wpa_bss_update_start(struct wpa_supplicant *wpa_s)
{
wpa_s->bss_update_idx++;
- wpa_printf(MSG_DEBUG, "BSS: Start scan result update %u",
- wpa_s->bss_update_idx);
+ wpa_dbg(wpa_s, MSG_DEBUG, "BSS: Start scan result update %u",
+ wpa_s->bss_update_idx);
+ wpa_s->last_scan_res_used = 0;
}
+/**
+ * wpa_bss_update_scan_res - Update a BSS table entry based on a scan result
+ * @wpa_s: Pointer to wpa_supplicant data
+ * @res: Scan result
+ *
+ * This function updates a BSS table entry (or adds one) based on a scan result.
+ * This is called separately for each scan result between the calls to
+ * wpa_bss_update_start() and wpa_bss_update_end().
+ */
void wpa_bss_update_scan_res(struct wpa_supplicant *wpa_s,
struct wpa_scan_res *res)
{
- const u8 *ssid;
+ const u8 *ssid, *p2p;
struct wpa_bss *bss;
ssid = wpa_scan_get_ie(res, WLAN_EID_SSID);
if (ssid == NULL) {
- wpa_printf(MSG_DEBUG, "BSS: No SSID IE included for " MACSTR,
- MAC2STR(res->bssid));
+ wpa_dbg(wpa_s, MSG_DEBUG, "BSS: No SSID IE included for "
+ MACSTR, MAC2STR(res->bssid));
return;
}
if (ssid[1] > 32) {
- wpa_printf(MSG_DEBUG, "BSS: Too long SSID IE included for "
- MACSTR, MAC2STR(res->bssid));
+ wpa_dbg(wpa_s, MSG_DEBUG, "BSS: Too long SSID IE included for "
+ MACSTR, MAC2STR(res->bssid));
return;
}
+ p2p = wpa_scan_get_vendor_ie(res, P2P_IE_VENDOR_TYPE);
+#ifdef CONFIG_P2P
+ if (p2p == NULL &&
+ wpa_s->p2p_group_interface != NOT_P2P_GROUP_INTERFACE) {
+ /*
+ * If it's a P2P specific interface, then don't update
+ * the scan result without a P2P IE.
+ */
+ wpa_printf(MSG_DEBUG, "BSS: No P2P IE - skipping BSS " MACSTR
+ " update for P2P interface", MAC2STR(res->bssid));
+ return;
+ }
+#endif /* CONFIG_P2P */
+ if (p2p && ssid[1] == P2P_WILDCARD_SSID_LEN &&
+ os_memcmp(ssid + 2, P2P_WILDCARD_SSID, P2P_WILDCARD_SSID_LEN) == 0)
+ return; /* Skip P2P listen discovery results here */
+
/* TODO: add option for ignoring BSSes we are not interested in
* (to save memory) */
bss = wpa_bss_get(wpa_s, res->bssid, ssid + 2, ssid[1]);
if (bss == NULL)
- wpa_bss_add(wpa_s, ssid + 2, ssid[1], res);
+ bss = wpa_bss_add(wpa_s, ssid + 2, ssid[1], res);
else
- wpa_bss_update(wpa_s, bss, res);
+ bss = wpa_bss_update(wpa_s, bss, res);
+
+ if (bss == NULL)
+ return;
+ if (wpa_s->last_scan_res_used >= wpa_s->last_scan_res_size) {
+ struct wpa_bss **n;
+ unsigned int siz;
+ if (wpa_s->last_scan_res_size == 0)
+ siz = 32;
+ else
+ siz = wpa_s->last_scan_res_size * 2;
+ n = os_realloc_array(wpa_s->last_scan_res, siz,
+ sizeof(struct wpa_bss *));
+ if (n == NULL)
+ return;
+ wpa_s->last_scan_res = n;
+ wpa_s->last_scan_res_size = siz;
+ }
+
+ wpa_s->last_scan_res[wpa_s->last_scan_res_used++] = bss;
}
@@ -391,14 +661,41 @@ static int wpa_bss_included_in_scan(const struct wpa_bss *bss,
}
+/**
+ * wpa_bss_update_end - End a BSS table update from scan results
+ * @wpa_s: Pointer to wpa_supplicant data
+ * @info: Information about scan parameters
+ * @new_scan: Whether this update round was based on a new scan
+ *
+ * This function is called at the end of each BSS table update round for new
+ * scan results. The start of the update was indicated with a call to
+ * wpa_bss_update_start().
+ */
void wpa_bss_update_end(struct wpa_supplicant *wpa_s, struct scan_info *info,
int new_scan)
{
struct wpa_bss *bss, *n;
+ wpa_s->last_scan_full = 0;
+ os_get_time(&wpa_s->last_scan);
if (!new_scan)
return; /* do not expire entries without new scan */
+ if (info && !info->aborted && !info->freqs) {
+ size_t i;
+ if (info->num_ssids == 0) {
+ wpa_s->last_scan_full = 1;
+ } else {
+ for (i = 0; i < info->num_ssids; i++) {
+ if (info->ssids[i].ssid == NULL ||
+ info->ssids[i].ssid_len == 0) {
+ wpa_s->last_scan_full = 1;
+ break;
+ }
+ }
+ }
+ }
+
dl_list_for_each_safe(bss, n, &wpa_s->bss, struct wpa_bss, list) {
if (wpa_bss_in_use(wpa_s, bss))
continue;
@@ -406,18 +703,28 @@ void wpa_bss_update_end(struct wpa_supplicant *wpa_s, struct scan_info *info,
continue; /* expire only BSSes that were scanned */
if (bss->last_update_idx < wpa_s->bss_update_idx)
bss->scan_miss_count++;
- if (bss->scan_miss_count >= WPA_BSS_EXPIRATION_SCAN_COUNT) {
- wpa_printf(MSG_DEBUG, "BSS: Expire BSS %u due to no "
- "match in scan", bss->id);
- wpa_bss_remove(wpa_s, bss);
+ if (bss->scan_miss_count >=
+ wpa_s->conf->bss_expiration_scan_count) {
+ wpa_bss_remove(wpa_s, bss, "no match in scan");
}
}
+
+ wpa_printf(MSG_DEBUG, "BSS: last_scan_res_used=%u/%u "
+ "last_scan_full=%d",
+ wpa_s->last_scan_res_used, wpa_s->last_scan_res_size,
+ wpa_s->last_scan_full);
}
-static void wpa_bss_timeout(void *eloop_ctx, void *timeout_ctx)
+/**
+ * wpa_bss_flush_by_age - Flush old BSS entries
+ * @wpa_s: Pointer to wpa_supplicant data
+ * @age: Maximum entry age in seconds
+ *
+ * Remove BSS entries that have not been updated during the last @age seconds.
+ */
+void wpa_bss_flush_by_age(struct wpa_supplicant *wpa_s, int age)
{
- struct wpa_supplicant *wpa_s = eloop_ctx;
struct wpa_bss *bss, *n;
struct os_time t;
@@ -425,24 +732,38 @@ static void wpa_bss_timeout(void *eloop_ctx, void *timeout_ctx)
return;
os_get_time(&t);
- t.sec -= WPA_BSS_EXPIRATION_AGE;
+ t.sec -= age;
dl_list_for_each_safe(bss, n, &wpa_s->bss, struct wpa_bss, list) {
if (wpa_bss_in_use(wpa_s, bss))
continue;
if (os_time_before(&bss->last_update, &t)) {
- wpa_printf(MSG_DEBUG, "BSS: Expire BSS %u due to age",
- bss->id);
- wpa_bss_remove(wpa_s, bss);
+ wpa_bss_remove(wpa_s, bss, __func__);
} else
break;
}
+}
+
+
+static void wpa_bss_timeout(void *eloop_ctx, void *timeout_ctx)
+{
+ struct wpa_supplicant *wpa_s = eloop_ctx;
+
+ wpa_bss_flush_by_age(wpa_s, wpa_s->conf->bss_expiration_age);
eloop_register_timeout(WPA_BSS_EXPIRATION_PERIOD, 0,
wpa_bss_timeout, wpa_s, NULL);
}
+/**
+ * wpa_bss_init - Initialize BSS table
+ * @wpa_s: Pointer to wpa_supplicant data
+ * Returns: 0 on success, -1 on failure
+ *
+ * This prepares BSS table lists and timer for periodic updates. The BSS table
+ * is deinitialized with wpa_bss_deinit() once not needed anymore.
+ */
int wpa_bss_init(struct wpa_supplicant *wpa_s)
{
dl_list_init(&wpa_s->bss);
@@ -453,22 +774,49 @@ int wpa_bss_init(struct wpa_supplicant *wpa_s)
}
-void wpa_bss_deinit(struct wpa_supplicant *wpa_s)
+/**
+ * wpa_bss_flush - Flush all unused BSS entries
+ * @wpa_s: Pointer to wpa_supplicant data
+ */
+void wpa_bss_flush(struct wpa_supplicant *wpa_s)
{
struct wpa_bss *bss, *n;
- eloop_cancel_timeout(wpa_bss_timeout, wpa_s, NULL);
+
if (wpa_s->bss.next == NULL)
return; /* BSS table not yet initialized */
- dl_list_for_each_safe(bss, n, &wpa_s->bss, struct wpa_bss, list)
- wpa_bss_remove(wpa_s, bss);
+
+ dl_list_for_each_safe(bss, n, &wpa_s->bss, struct wpa_bss, list) {
+ if (wpa_bss_in_use(wpa_s, bss))
+ continue;
+ wpa_bss_remove(wpa_s, bss, __func__);
+ }
+}
+
+
+/**
+ * wpa_bss_deinit - Deinitialize BSS table
+ * @wpa_s: Pointer to wpa_supplicant data
+ */
+void wpa_bss_deinit(struct wpa_supplicant *wpa_s)
+{
+ eloop_cancel_timeout(wpa_bss_timeout, wpa_s, NULL);
+ wpa_bss_flush(wpa_s);
}
+/**
+ * wpa_bss_get_bssid - Fetch a BSS table entry based on BSSID
+ * @wpa_s: Pointer to wpa_supplicant data
+ * @bssid: BSSID
+ * Returns: Pointer to the BSS entry or %NULL if not found
+ */
struct wpa_bss * wpa_bss_get_bssid(struct wpa_supplicant *wpa_s,
const u8 *bssid)
{
struct wpa_bss *bss;
- dl_list_for_each(bss, &wpa_s->bss, struct wpa_bss, list) {
+ if (!wpa_supplicant_filter_bssid_match(wpa_s, bssid))
+ return NULL;
+ dl_list_for_each_reverse(bss, &wpa_s->bss, struct wpa_bss, list) {
if (os_memcmp(bss->bssid, bssid, ETH_ALEN) == 0)
return bss;
}
@@ -476,6 +824,35 @@ struct wpa_bss * wpa_bss_get_bssid(struct wpa_supplicant *wpa_s,
}
+#ifdef CONFIG_P2P
+/**
+ * wpa_bss_get_p2p_dev_addr - Fetch a BSS table entry based on P2P Device Addr
+ * @wpa_s: Pointer to wpa_supplicant data
+ * @dev_addr: P2P Device Address of the GO
+ * Returns: Pointer to the BSS entry or %NULL if not found
+ */
+struct wpa_bss * wpa_bss_get_p2p_dev_addr(struct wpa_supplicant *wpa_s,
+ const u8 *dev_addr)
+{
+ struct wpa_bss *bss;
+ dl_list_for_each_reverse(bss, &wpa_s->bss, struct wpa_bss, list) {
+ u8 addr[ETH_ALEN];
+ if (p2p_parse_dev_addr((const u8 *) (bss + 1), bss->ie_len,
+ addr) == 0 &&
+ os_memcmp(addr, dev_addr, ETH_ALEN) == 0)
+ return bss;
+ }
+ return NULL;
+}
+#endif /* CONFIG_P2P */
+
+
+/**
+ * wpa_bss_get_id - Fetch a BSS table entry based on identifier
+ * @wpa_s: Pointer to wpa_supplicant data
+ * @id: Unique identifier (struct wpa_bss::id) assigned for the entry
+ * Returns: Pointer to the BSS entry or %NULL if not found
+ */
struct wpa_bss * wpa_bss_get_id(struct wpa_supplicant *wpa_s, unsigned int id)
{
struct wpa_bss *bss;
@@ -487,6 +864,15 @@ struct wpa_bss * wpa_bss_get_id(struct wpa_supplicant *wpa_s, unsigned int id)
}
+/**
+ * wpa_bss_get_ie - Fetch a specified information element from a BSS entry
+ * @bss: BSS table entry
+ * @ie: Information element identitifier (WLAN_EID_*)
+ * Returns: Pointer to the information element (id field) or %NULL if not found
+ *
+ * This function returns the first matching information element in the BSS
+ * entry.
+ */
const u8 * wpa_bss_get_ie(const struct wpa_bss *bss, u8 ie)
{
const u8 *end, *pos;
@@ -506,6 +892,15 @@ const u8 * wpa_bss_get_ie(const struct wpa_bss *bss, u8 ie)
}
+/**
+ * wpa_bss_get_vendor_ie - Fetch a vendor information element from a BSS entry
+ * @bss: BSS table entry
+ * @vendor_type: Vendor type (four octets starting the IE payload)
+ * Returns: Pointer to the information element (id field) or %NULL if not found
+ *
+ * This function returns the first matching information element in the BSS
+ * entry.
+ */
const u8 * wpa_bss_get_vendor_ie(const struct wpa_bss *bss, u32 vendor_type)
{
const u8 *end, *pos;
@@ -526,6 +921,16 @@ const u8 * wpa_bss_get_vendor_ie(const struct wpa_bss *bss, u32 vendor_type)
}
+/**
+ * wpa_bss_get_vendor_ie_multi - Fetch vendor IE data from a BSS entry
+ * @bss: BSS table entry
+ * @vendor_type: Vendor type (four octets starting the IE payload)
+ * Returns: Pointer to the information element payload or %NULL if not found
+ *
+ * This function returns concatenated payload of possibly fragmented vendor
+ * specific information elements in the BSS entry. The caller is responsible for
+ * freeing the returned buffer.
+ */
struct wpabuf * wpa_bss_get_vendor_ie_multi(const struct wpa_bss *bss,
u32 vendor_type)
{
@@ -557,6 +962,56 @@ struct wpabuf * wpa_bss_get_vendor_ie_multi(const struct wpa_bss *bss,
}
+/**
+ * wpa_bss_get_vendor_ie_multi_beacon - Fetch vendor IE data from a BSS entry
+ * @bss: BSS table entry
+ * @vendor_type: Vendor type (four octets starting the IE payload)
+ * Returns: Pointer to the information element payload or %NULL if not found
+ *
+ * This function returns concatenated payload of possibly fragmented vendor
+ * specific information elements in the BSS entry. The caller is responsible for
+ * freeing the returned buffer.
+ *
+ * This function is like wpa_bss_get_vendor_ie_multi(), but uses IE buffer only
+ * from Beacon frames instead of either Beacon or Probe Response frames.
+ */
+struct wpabuf * wpa_bss_get_vendor_ie_multi_beacon(const struct wpa_bss *bss,
+ u32 vendor_type)
+{
+ struct wpabuf *buf;
+ const u8 *end, *pos;
+
+ buf = wpabuf_alloc(bss->beacon_ie_len);
+ if (buf == NULL)
+ return NULL;
+
+ pos = (const u8 *) (bss + 1);
+ pos += bss->ie_len;
+ end = pos + bss->beacon_ie_len;
+
+ while (pos + 1 < end) {
+ if (pos + 2 + pos[1] > end)
+ break;
+ if (pos[0] == WLAN_EID_VENDOR_SPECIFIC && pos[1] >= 4 &&
+ vendor_type == WPA_GET_BE32(&pos[2]))
+ wpabuf_put_data(buf, pos + 2 + 4, pos[1] - 4);
+ pos += 2 + pos[1];
+ }
+
+ if (wpabuf_len(buf) == 0) {
+ wpabuf_free(buf);
+ buf = NULL;
+ }
+
+ return buf;
+}
+
+
+/**
+ * wpa_bss_get_max_rate - Get maximum legacy TX rate supported in a BSS
+ * @bss: BSS table entry
+ * Returns: Maximum legacy rate in units of 500 kbps
+ */
int wpa_bss_get_max_rate(const struct wpa_bss *bss)
{
int rate = 0;
@@ -579,6 +1034,15 @@ int wpa_bss_get_max_rate(const struct wpa_bss *bss)
}
+/**
+ * wpa_bss_get_bit_rates - Get legacy TX rates supported in a BSS
+ * @bss: BSS table entry
+ * @rates: Buffer for returning a pointer to the rates list (units of 500 kbps)
+ * Returns: number of legacy TX rates or -1 on failure
+ *
+ * The caller is responsible for freeing the returned buffer with os_free() in
+ * case of success.
+ */
int wpa_bss_get_bit_rates(const struct wpa_bss *bss, u8 **rates)
{
const u8 *ie, *ie2;
diff --git a/contrib/wpa/wpa_supplicant/bss.h b/contrib/wpa/wpa_supplicant/bss.h
index 1de4722..01f6c59 100644
--- a/contrib/wpa/wpa_supplicant/bss.h
+++ b/contrib/wpa/wpa_supplicant/bss.h
@@ -2,14 +2,8 @@
* BSS table
* Copyright (c) 2009-2010, Jouni Malinen <j@w1.fi>
*
- * This program is free software; you can redistribute it and/or modify
- * it under the terms of the GNU General Public License version 2 as
- * published by the Free Software Foundation.
- *
- * Alternatively, this software may be distributed under the terms of BSD
- * license.
- *
- * See README and COPYING for more details.
+ * This software may be distributed under the terms of the BSD license.
+ * See README for more details.
*/
#ifndef BSS_H
@@ -23,49 +17,79 @@ struct wpa_scan_res;
#define WPA_BSS_LEVEL_DBM BIT(3)
#define WPA_BSS_AUTHENTICATED BIT(4)
#define WPA_BSS_ASSOCIATED BIT(5)
+#define WPA_BSS_ANQP_FETCH_TRIED BIT(6)
+
+/**
+ * struct wpa_bss_anqp - ANQP data for a BSS entry (struct wpa_bss)
+ */
+struct wpa_bss_anqp {
+ /** Number of BSS entries referring to this ANQP data instance */
+ unsigned int users;
+#ifdef CONFIG_INTERWORKING
+ struct wpabuf *venue_name;
+ struct wpabuf *network_auth_type;
+ struct wpabuf *roaming_consortium;
+ struct wpabuf *ip_addr_type_availability;
+ struct wpabuf *nai_realm;
+ struct wpabuf *anqp_3gpp;
+ struct wpabuf *domain_name;
+#endif /* CONFIG_INTERWORKING */
+#ifdef CONFIG_HS20
+ struct wpabuf *hs20_operator_friendly_name;
+ struct wpabuf *hs20_wan_metrics;
+ struct wpabuf *hs20_connection_capability;
+ struct wpabuf *hs20_operating_class;
+#endif /* CONFIG_HS20 */
+};
/**
* struct wpa_bss - BSS table
- * @list: List entry for struct wpa_supplicant::bss
- * @list_id: List entry for struct wpa_supplicant::bss_id
- * @id: Unique identifier for this BSS entry
- * @scan_miss_count: Number of counts without seeing this BSS
- * @flags: information flags about the BSS/IBSS (WPA_BSS_*)
- * @last_update_idx: Index of the last scan update
- * @bssid: BSSID
- * @freq: frequency of the channel in MHz (e.g., 2412 = channel 1)
- * @beacon_int: beacon interval in TUs (host byte order)
- * @caps: capability information field in host byte order
- * @qual: signal quality
- * @noise: noise level
- * @level: signal level
- * @tsf: Timestamp of last Beacon/Probe Response frame
- * @last_update: Time of the last update (i.e., Beacon or Probe Response RX)
- * @ie_len: length of the following IE field in octets (from Probe Response)
- * @beacon_ie_len: length of the following Beacon IE field in octets
*
* This structure is used to store information about neighboring BSSes in
* generic format. It is mainly updated based on scan results from the driver.
*/
struct wpa_bss {
+ /** List entry for struct wpa_supplicant::bss */
struct dl_list list;
+ /** List entry for struct wpa_supplicant::bss_id */
struct dl_list list_id;
+ /** Unique identifier for this BSS entry */
unsigned int id;
+ /** Number of counts without seeing this BSS */
unsigned int scan_miss_count;
+ /** Index of the last scan update */
unsigned int last_update_idx;
+ /** Information flags about the BSS/IBSS (WPA_BSS_*) */
unsigned int flags;
+ /** BSSID */
u8 bssid[ETH_ALEN];
+ /** HESSID */
+ u8 hessid[ETH_ALEN];
+ /** SSID */
u8 ssid[32];
+ /** Length of SSID */
size_t ssid_len;
+ /** Frequency of the channel in MHz (e.g., 2412 = channel 1) */
int freq;
+ /** Beacon interval in TUs (host byte order) */
u16 beacon_int;
+ /** Capability information field in host byte order */
u16 caps;
+ /** Signal quality */
int qual;
+ /** Noise level */
int noise;
+ /** Signal level */
int level;
+ /** Timestamp of last Beacon/Probe Response frame */
u64 tsf;
+ /** Time of the last update (i.e., Beacon or Probe Response RX) */
struct os_time last_update;
+ /** ANQP data */
+ struct wpa_bss_anqp *anqp;
+ /** Length of the following IE field in octets (from Probe Response) */
size_t ie_len;
+ /** Length of the following Beacon IE field in octets */
size_t beacon_ie_len;
/* followed by ie_len octets of IEs */
/* followed by beacon_ie_len octets of IEs */
@@ -78,16 +102,24 @@ void wpa_bss_update_end(struct wpa_supplicant *wpa_s, struct scan_info *info,
int new_scan);
int wpa_bss_init(struct wpa_supplicant *wpa_s);
void wpa_bss_deinit(struct wpa_supplicant *wpa_s);
+void wpa_bss_flush(struct wpa_supplicant *wpa_s);
+void wpa_bss_flush_by_age(struct wpa_supplicant *wpa_s, int age);
struct wpa_bss * wpa_bss_get(struct wpa_supplicant *wpa_s, const u8 *bssid,
const u8 *ssid, size_t ssid_len);
struct wpa_bss * wpa_bss_get_bssid(struct wpa_supplicant *wpa_s,
const u8 *bssid);
+struct wpa_bss * wpa_bss_get_p2p_dev_addr(struct wpa_supplicant *wpa_s,
+ const u8 *dev_addr);
struct wpa_bss * wpa_bss_get_id(struct wpa_supplicant *wpa_s, unsigned int id);
const u8 * wpa_bss_get_ie(const struct wpa_bss *bss, u8 ie);
const u8 * wpa_bss_get_vendor_ie(const struct wpa_bss *bss, u32 vendor_type);
struct wpabuf * wpa_bss_get_vendor_ie_multi(const struct wpa_bss *bss,
u32 vendor_type);
+struct wpabuf * wpa_bss_get_vendor_ie_multi_beacon(const struct wpa_bss *bss,
+ u32 vendor_type);
int wpa_bss_get_max_rate(const struct wpa_bss *bss);
int wpa_bss_get_bit_rates(const struct wpa_bss *bss, u8 **rates);
+struct wpa_bss_anqp * wpa_bss_anqp_alloc(void);
+int wpa_bss_anqp_unshare_alloc(struct wpa_bss *bss);
#endif /* BSS_H */
diff --git a/contrib/wpa/wpa_supplicant/config.c b/contrib/wpa/wpa_supplicant/config.c
index 7e2a5b4..0fab07a 100644
--- a/contrib/wpa/wpa_supplicant/config.c
+++ b/contrib/wpa/wpa_supplicant/config.c
@@ -1,23 +1,19 @@
/*
* WPA Supplicant / Configuration parser and common functions
- * Copyright (c) 2003-2008, Jouni Malinen <j@w1.fi>
+ * Copyright (c) 2003-2012, Jouni Malinen <j@w1.fi>
*
- * This program is free software; you can redistribute it and/or modify
- * it under the terms of the GNU General Public License version 2 as
- * published by the Free Software Foundation.
- *
- * Alternatively, this software may be distributed under the terms of BSD
- * license.
- *
- * See README and COPYING for more details.
+ * This software may be distributed under the terms of the BSD license.
+ * See README for more details.
*/
#include "includes.h"
#include "common.h"
+#include "utils/uuid.h"
#include "crypto/sha1.h"
#include "rsn_supp/wpa.h"
#include "eap_peer/eap.h"
+#include "p2p/p2p.h"
#include "config.h"
@@ -57,42 +53,6 @@ struct parse_data {
};
-static char * wpa_config_parse_string(const char *value, size_t *len)
-{
- if (*value == '"') {
- const char *pos;
- char *str;
- value++;
- pos = os_strrchr(value, '"');
- if (pos == NULL || pos[1] != '\0')
- return NULL;
- *len = pos - value;
- str = os_malloc(*len + 1);
- if (str == NULL)
- return NULL;
- os_memcpy(str, value, *len);
- str[*len] = '\0';
- return str;
- } else {
- u8 *str;
- size_t tlen, hlen = os_strlen(value);
- if (hlen & 1)
- return NULL;
- tlen = hlen / 2;
- str = os_malloc(tlen + 1);
- if (str == NULL)
- return NULL;
- if (hexstr2bin(value, str, tlen)) {
- os_free(str);
- return NULL;
- }
- str[tlen] = '\0';
- *len = tlen;
- return (char *) str;
- }
-}
-
-
static int wpa_config_parse_str(const struct parse_data *data,
struct wpa_ssid *ssid,
int line, const char *value)
@@ -153,18 +113,6 @@ set:
#ifndef NO_CONFIG_WRITE
-static int is_hex(const u8 *data, size_t len)
-{
- size_t i;
-
- for (i = 0; i < len; i++) {
- if (data[i] < 32 || data[i] >= 127)
- return 1;
- }
- return 0;
-}
-
-
static char * wpa_config_write_string_ascii(const u8 *value, size_t len)
{
char *buf;
@@ -283,6 +231,12 @@ static int wpa_config_parse_bssid(const struct parse_data *data,
struct wpa_ssid *ssid, int line,
const char *value)
{
+ if (value[0] == '\0' || os_strcmp(value, "\"\"") == 0 ||
+ os_strcmp(value, "any") == 0) {
+ ssid->bssid_set = 0;
+ wpa_printf(MSG_MSGDUMP, "BSSID any");
+ return 0;
+ }
if (hwaddr_aton(value, ssid->bssid)) {
wpa_printf(MSG_ERROR, "Line %d: Invalid BSSID '%s'.",
line, value);
@@ -322,6 +276,21 @@ static int wpa_config_parse_psk(const struct parse_data *data,
struct wpa_ssid *ssid, int line,
const char *value)
{
+#ifdef CONFIG_EXT_PASSWORD
+ if (os_strncmp(value, "ext:", 4) == 0) {
+ os_free(ssid->passphrase);
+ ssid->passphrase = NULL;
+ ssid->psk_set = 0;
+ os_free(ssid->ext_psk);
+ ssid->ext_psk = os_strdup(value + 4);
+ if (ssid->ext_psk == NULL)
+ return -1;
+ wpa_printf(MSG_DEBUG, "PSK: External password '%s'",
+ ssid->ext_psk);
+ return 0;
+ }
+#endif /* CONFIG_EXT_PASSWORD */
+
if (*value == '"') {
#ifndef CONFIG_NO_PBKDF2
const char *pos;
@@ -379,6 +348,17 @@ static int wpa_config_parse_psk(const struct parse_data *data,
static char * wpa_config_write_psk(const struct parse_data *data,
struct wpa_ssid *ssid)
{
+#ifdef CONFIG_EXT_PASSWORD
+ if (ssid->ext_psk) {
+ size_t len = 4 + os_strlen(ssid->ext_psk) + 1;
+ char *buf = os_malloc(len);
+ if (buf == NULL)
+ return NULL;
+ os_snprintf(buf, len, "ext:%s", ssid->ext_psk);
+ return buf;
+ }
+#endif /* CONFIG_EXT_PASSWORD */
+
if (ssid->passphrase)
return wpa_config_write_string_ascii(
(const u8 *) ssid->passphrase,
@@ -524,6 +504,12 @@ static int wpa_config_parse_key_mgmt(const struct parse_data *data,
else if (os_strcmp(start, "WPS") == 0)
val |= WPA_KEY_MGMT_WPS;
#endif /* CONFIG_WPS */
+#ifdef CONFIG_SAE
+ else if (os_strcmp(start, "SAE") == 0)
+ val |= WPA_KEY_MGMT_SAE;
+ else if (os_strcmp(start, "FT-SAE") == 0)
+ val |= WPA_KEY_MGMT_FT_SAE;
+#endif /* CONFIG_SAE */
else {
wpa_printf(MSG_ERROR, "Line %d: invalid key_mgmt '%s'",
line, start);
@@ -663,6 +649,8 @@ static int wpa_config_parse_cipher(int line, const char *value)
*end = '\0';
if (os_strcmp(start, "CCMP") == 0)
val |= WPA_CIPHER_CCMP;
+ else if (os_strcmp(start, "GCMP") == 0)
+ val |= WPA_CIPHER_GCMP;
else if (os_strcmp(start, "TKIP") == 0)
val |= WPA_CIPHER_TKIP;
else if (os_strcmp(start, "WEP104") == 0)
@@ -714,6 +702,16 @@ static char * wpa_config_write_cipher(int cipher)
pos += ret;
}
+ if (cipher & WPA_CIPHER_GCMP) {
+ ret = os_snprintf(pos, end - pos, "%sGCMP",
+ pos == buf ? "" : " ");
+ if (ret < 0 || ret >= end - pos) {
+ end[-1] = '\0';
+ return buf;
+ }
+ pos += ret;
+ }
+
if (cipher & WPA_CIPHER_TKIP) {
ret = os_snprintf(pos, end - pos, "%sTKIP",
pos == buf ? "" : " ");
@@ -767,7 +765,8 @@ static int wpa_config_parse_pairwise(const struct parse_data *data,
val = wpa_config_parse_cipher(line, value);
if (val == -1)
return -1;
- if (val & ~(WPA_CIPHER_CCMP | WPA_CIPHER_TKIP | WPA_CIPHER_NONE)) {
+ if (val & ~(WPA_CIPHER_CCMP | WPA_CIPHER_GCMP | WPA_CIPHER_TKIP |
+ WPA_CIPHER_NONE)) {
wpa_printf(MSG_ERROR, "Line %d: not allowed pairwise cipher "
"(0x%x).", line, val);
return -1;
@@ -796,8 +795,8 @@ static int wpa_config_parse_group(const struct parse_data *data,
val = wpa_config_parse_cipher(line, value);
if (val == -1)
return -1;
- if (val & ~(WPA_CIPHER_CCMP | WPA_CIPHER_TKIP | WPA_CIPHER_WEP104 |
- WPA_CIPHER_WEP40)) {
+ if (val & ~(WPA_CIPHER_CCMP | WPA_CIPHER_GCMP | WPA_CIPHER_TKIP |
+ WPA_CIPHER_WEP104 | WPA_CIPHER_WEP40)) {
wpa_printf(MSG_ERROR, "Line %d: not allowed group cipher "
"(0x%x).", line, val);
return -1;
@@ -927,7 +926,7 @@ static int * wpa_config_parse_freqs(const struct parse_data *data,
used = 0;
len = 10;
- freqs = os_zalloc((len + 1) * sizeof(int));
+ freqs = os_calloc(len + 1, sizeof(int));
if (freqs == NULL)
return NULL;
@@ -938,7 +937,7 @@ static int * wpa_config_parse_freqs(const struct parse_data *data,
if (used == len) {
int *n;
size_t i;
- n = os_realloc(freqs, (len * 2 + 1) * sizeof(int));
+ n = os_realloc_array(freqs, len * 2 + 1, sizeof(int));
if (n == NULL) {
os_free(freqs);
return NULL;
@@ -1067,8 +1066,8 @@ static int wpa_config_parse_eap(const struct parse_data *data,
last = *end == '\0';
*end = '\0';
tmp = methods;
- methods = os_realloc(methods,
- (num_methods + 1) * sizeof(*methods));
+ methods = os_realloc_array(methods, num_methods + 1,
+ sizeof(*methods));
if (methods == NULL) {
os_free(tmp);
os_free(buf);
@@ -1098,7 +1097,7 @@ static int wpa_config_parse_eap(const struct parse_data *data,
os_free(buf);
tmp = methods;
- methods = os_realloc(methods, (num_methods + 1) * sizeof(*methods));
+ methods = os_realloc_array(methods, num_methods + 1, sizeof(*methods));
if (methods == NULL) {
os_free(tmp);
return -1;
@@ -1109,6 +1108,7 @@ static int wpa_config_parse_eap(const struct parse_data *data,
wpa_hexdump(MSG_MSGDUMP, "eap methods",
(u8 *) methods, num_methods * sizeof(*methods));
+ os_free(ssid->eap.eap_methods);
ssid->eap.eap_methods = methods;
return errors ? -1 : 0;
}
@@ -1163,6 +1163,20 @@ static int wpa_config_parse_password(const struct parse_data *data,
return 0;
}
+#ifdef CONFIG_EXT_PASSWORD
+ if (os_strncmp(value, "ext:", 4) == 0) {
+ char *name = os_strdup(value + 4);
+ if (name == NULL)
+ return -1;
+ os_free(ssid->eap.password);
+ ssid->eap.password = (u8 *) name;
+ ssid->eap.password_len = os_strlen(name);
+ ssid->eap.flags &= ~EAP_CONFIG_FLAGS_PASSWORD_NTHASH;
+ ssid->eap.flags |= EAP_CONFIG_FLAGS_EXT_PASSWORD;
+ return 0;
+ }
+#endif /* CONFIG_EXT_PASSWORD */
+
if (os_strncmp(value, "hash:", 5) != 0) {
char *tmp;
size_t res_len;
@@ -1180,6 +1194,7 @@ static int wpa_config_parse_password(const struct parse_data *data,
ssid->eap.password = (u8 *) tmp;
ssid->eap.password_len = res_len;
ssid->eap.flags &= ~EAP_CONFIG_FLAGS_PASSWORD_NTHASH;
+ ssid->eap.flags &= ~EAP_CONFIG_FLAGS_EXT_PASSWORD;
return 0;
}
@@ -1208,6 +1223,7 @@ static int wpa_config_parse_password(const struct parse_data *data,
ssid->eap.password = hash;
ssid->eap.password_len = 16;
ssid->eap.flags |= EAP_CONFIG_FLAGS_PASSWORD_NTHASH;
+ ssid->eap.flags &= ~EAP_CONFIG_FLAGS_EXT_PASSWORD;
return 0;
}
@@ -1221,6 +1237,17 @@ static char * wpa_config_write_password(const struct parse_data *data,
if (ssid->eap.password == NULL)
return NULL;
+#ifdef CONFIG_EXT_PASSWORD
+ if (ssid->eap.flags & EAP_CONFIG_FLAGS_EXT_PASSWORD) {
+ buf = os_zalloc(4 + ssid->eap.password_len + 1);
+ if (buf == NULL)
+ return NULL;
+ os_memcpy(buf, "ext:", 4);
+ os_memcpy(buf + 4, ssid->eap.password, ssid->eap.password_len);
+ return buf;
+ }
+#endif /* CONFIG_EXT_PASSWORD */
+
if (!(ssid->eap.flags & EAP_CONFIG_FLAGS_PASSWORD_NTHASH)) {
return wpa_config_write_string(
ssid->eap.password, ssid->eap.password_len);
@@ -1256,6 +1283,11 @@ static int wpa_config_parse_wep_key(u8 *key, size_t *len, int line,
os_free(buf);
return -1;
}
+ if (*len && *len != 5 && *len != 13 && *len != 16) {
+ wpa_printf(MSG_ERROR, "Line %d: Invalid WEP key length %u - "
+ "this network block will be ignored",
+ line, (unsigned int) *len);
+ }
os_memcpy(key, buf, *len);
os_free(buf);
res = os_snprintf(title, sizeof(title), "wep_key%d", idx);
@@ -1344,6 +1376,99 @@ static char * wpa_config_write_wep_key3(const struct parse_data *data,
#endif /* NO_CONFIG_WRITE */
+#ifdef CONFIG_P2P
+
+static int wpa_config_parse_p2p_client_list(const struct parse_data *data,
+ struct wpa_ssid *ssid, int line,
+ const char *value)
+{
+ const char *pos;
+ u8 *buf, *n, addr[ETH_ALEN];
+ size_t count;
+
+ buf = NULL;
+ count = 0;
+
+ pos = value;
+ while (pos && *pos) {
+ while (*pos == ' ')
+ pos++;
+
+ if (hwaddr_aton(pos, addr)) {
+ if (count == 0) {
+ wpa_printf(MSG_ERROR, "Line %d: Invalid "
+ "p2p_client_list address '%s'.",
+ line, value);
+ os_free(buf);
+ return -1;
+ }
+ /* continue anyway since this could have been from a
+ * truncated configuration file line */
+ wpa_printf(MSG_INFO, "Line %d: Ignore likely "
+ "truncated p2p_client_list address '%s'",
+ line, pos);
+ } else {
+ n = os_realloc_array(buf, count + 1, ETH_ALEN);
+ if (n == NULL) {
+ os_free(buf);
+ return -1;
+ }
+ buf = n;
+ os_memmove(buf + ETH_ALEN, buf, count * ETH_ALEN);
+ os_memcpy(buf, addr, ETH_ALEN);
+ count++;
+ wpa_hexdump(MSG_MSGDUMP, "p2p_client_list",
+ addr, ETH_ALEN);
+ }
+
+ pos = os_strchr(pos, ' ');
+ }
+
+ os_free(ssid->p2p_client_list);
+ ssid->p2p_client_list = buf;
+ ssid->num_p2p_clients = count;
+
+ return 0;
+}
+
+
+#ifndef NO_CONFIG_WRITE
+static char * wpa_config_write_p2p_client_list(const struct parse_data *data,
+ struct wpa_ssid *ssid)
+{
+ char *value, *end, *pos;
+ int res;
+ size_t i;
+
+ if (ssid->p2p_client_list == NULL || ssid->num_p2p_clients == 0)
+ return NULL;
+
+ value = os_malloc(20 * ssid->num_p2p_clients);
+ if (value == NULL)
+ return NULL;
+ pos = value;
+ end = value + 20 * ssid->num_p2p_clients;
+
+ for (i = ssid->num_p2p_clients; i > 0; i--) {
+ res = os_snprintf(pos, end - pos, MACSTR " ",
+ MAC2STR(ssid->p2p_client_list +
+ (i - 1) * ETH_ALEN));
+ if (res < 0 || res >= end - pos) {
+ os_free(value);
+ return NULL;
+ }
+ pos += res;
+ }
+
+ if (pos > value)
+ pos[-1] = '\0';
+
+ return value;
+}
+#endif /* NO_CONFIG_WRITE */
+
+#endif /* CONFIG_P2P */
+
/* Helper macros for network block parser */
#ifdef OFFSET
@@ -1438,6 +1563,7 @@ static const struct parse_data ssid_fields[] = {
{ FUNC_KEY(psk) },
{ FUNC(proto) },
{ FUNC(key_mgmt) },
+ { INT(bg_scan_period) },
{ FUNC(pairwise) },
{ FUNC(group) },
{ FUNC(auth_alg) },
@@ -1492,18 +1618,33 @@ static const struct parse_data ssid_fields[] = {
{ STRe(pac_file) },
{ INTe(fragment_size) },
#endif /* IEEE8021X_EAPOL */
- { INT_RANGE(mode, 0, 2) },
+ { INT_RANGE(mode, 0, 4) },
{ INT_RANGE(proactive_key_caching, 0, 1) },
- { INT_RANGE(disabled, 0, 1) },
+ { INT_RANGE(disabled, 0, 2) },
{ STR(id_str) },
#ifdef CONFIG_IEEE80211W
{ INT_RANGE(ieee80211w, 0, 2) },
#endif /* CONFIG_IEEE80211W */
{ INT_RANGE(peerkey, 0, 1) },
{ INT_RANGE(mixed_cell, 0, 1) },
- { INT_RANGE(frequency, 0, 10000) },
+ { INT_RANGE(frequency, 0, 65000) },
{ INT(wpa_ptk_rekey) },
{ STR(bgscan) },
+ { INT_RANGE(ignore_broadcast_ssid, 0, 2) },
+#ifdef CONFIG_P2P
+ { FUNC(p2p_client_list) },
+#endif /* CONFIG_P2P */
+#ifdef CONFIG_HT_OVERRIDES
+ { INT_RANGE(disable_ht, 0, 1) },
+ { INT_RANGE(disable_ht40, -1, 1) },
+ { INT_RANGE(disable_sgi, 0, 1) },
+ { INT_RANGE(disable_max_amsdu, -1, 1) },
+ { INT_RANGE(ampdu_factor, -1, 3) },
+ { INT_RANGE(ampdu_density, -1, 7) },
+ { STR(ht_mcs) },
+#endif /* CONFIG_HT_OVERRIDES */
+ { INT(ap_max_inactivity) },
+ { INT(dtim_period) },
};
#undef OFFSET
@@ -1557,19 +1698,20 @@ int wpa_config_add_prio_network(struct wpa_config *config,
}
/* First network for this priority - add a new priority list */
- nlist = os_realloc(config->pssid,
- (config->num_prio + 1) * sizeof(struct wpa_ssid *));
+ nlist = os_realloc_array(config->pssid, config->num_prio + 1,
+ sizeof(struct wpa_ssid *));
if (nlist == NULL)
return -1;
for (prio = 0; prio < config->num_prio; prio++) {
- if (nlist[prio]->priority < ssid->priority)
+ if (nlist[prio]->priority < ssid->priority) {
+ os_memmove(&nlist[prio + 1], &nlist[prio],
+ (config->num_prio - prio) *
+ sizeof(struct wpa_ssid *));
break;
+ }
}
- os_memmove(&nlist[prio + 1], &nlist[prio],
- (config->num_prio - prio) * sizeof(struct wpa_ssid *));
-
nlist[prio] = ssid;
config->num_prio++;
config->pssid = nlist;
@@ -1663,6 +1805,7 @@ void wpa_config_free_ssid(struct wpa_ssid *ssid)
{
os_free(ssid->ssid);
os_free(ssid->passphrase);
+ os_free(ssid->ext_psk);
#ifdef IEEE8021X_EAPOL
eap_peer_config_free(&ssid->eap);
#endif /* IEEE8021X_EAPOL */
@@ -1670,10 +1813,34 @@ void wpa_config_free_ssid(struct wpa_ssid *ssid)
os_free(ssid->scan_freq);
os_free(ssid->freq_list);
os_free(ssid->bgscan);
+ os_free(ssid->p2p_client_list);
+#ifdef CONFIG_HT_OVERRIDES
+ os_free(ssid->ht_mcs);
+#endif /* CONFIG_HT_OVERRIDES */
os_free(ssid);
}
+void wpa_config_free_cred(struct wpa_cred *cred)
+{
+ os_free(cred->realm);
+ os_free(cred->username);
+ os_free(cred->password);
+ os_free(cred->ca_cert);
+ os_free(cred->client_cert);
+ os_free(cred->private_key);
+ os_free(cred->private_key_passwd);
+ os_free(cred->imsi);
+ os_free(cred->milenage);
+ os_free(cred->domain);
+ os_free(cred->eap_method);
+ os_free(cred->phase1);
+ os_free(cred->phase2);
+ os_free(cred->excluded_ssid);
+ os_free(cred);
+}
+
+
/**
* wpa_config_free - Free configuration data
* @config: Configuration data from wpa_config_read()
@@ -1687,6 +1854,8 @@ void wpa_config_free(struct wpa_config *config)
struct wpa_config_blob *blob, *prevblob;
#endif /* CONFIG_NO_CONFIG_BLOBS */
struct wpa_ssid *ssid, *prev = NULL;
+ struct wpa_cred *cred, *cprev;
+
ssid = config->ssid;
while (ssid) {
prev = ssid;
@@ -1694,6 +1863,13 @@ void wpa_config_free(struct wpa_config *config)
wpa_config_free_ssid(prev);
}
+ cred = config->cred;
+ while (cred) {
+ cprev = cred;
+ cred = cred->next;
+ wpa_config_free_cred(cprev);
+ }
+
#ifndef CONFIG_NO_CONFIG_BLOBS
blob = config->blobs;
prevblob = NULL;
@@ -1704,25 +1880,59 @@ void wpa_config_free(struct wpa_config *config)
}
#endif /* CONFIG_NO_CONFIG_BLOBS */
+ wpabuf_free(config->wps_vendor_ext_m1);
os_free(config->ctrl_interface);
os_free(config->ctrl_interface_group);
os_free(config->opensc_engine_path);
os_free(config->pkcs11_engine_path);
os_free(config->pkcs11_module_path);
+ os_free(config->pcsc_reader);
+ os_free(config->pcsc_pin);
os_free(config->driver_param);
os_free(config->device_name);
os_free(config->manufacturer);
os_free(config->model_name);
os_free(config->model_number);
os_free(config->serial_number);
- os_free(config->device_type);
os_free(config->config_methods);
+ os_free(config->p2p_ssid_postfix);
os_free(config->pssid);
+ os_free(config->p2p_pref_chan);
+ os_free(config->autoscan);
+ wpabuf_free(config->wps_nfc_dh_pubkey);
+ wpabuf_free(config->wps_nfc_dh_privkey);
+ wpabuf_free(config->wps_nfc_dev_pw);
+ os_free(config->ext_password_backend);
os_free(config);
}
/**
+ * wpa_config_foreach_network - Iterate over each configured network
+ * @config: Configuration data from wpa_config_read()
+ * @func: Callback function to process each network
+ * @arg: Opaque argument to pass to callback function
+ *
+ * Iterate over the set of configured networks calling the specified
+ * function for each item. We guard against callbacks removing the
+ * supplied network.
+ */
+void wpa_config_foreach_network(struct wpa_config *config,
+ void (*func)(void *, struct wpa_ssid *),
+ void *arg)
+{
+ struct wpa_ssid *ssid, *next;
+
+ ssid = config->ssid;
+ while (ssid) {
+ next = ssid->next;
+ func(arg, ssid);
+ ssid = next;
+ }
+}
+
+
+/**
* wpa_config_get_network - Get configured network based on id
* @config: Configuration data from wpa_config_read()
* @id: Unique network id to search for
@@ -1820,11 +2030,24 @@ void wpa_config_set_network_defaults(struct wpa_ssid *ssid)
ssid->pairwise_cipher = DEFAULT_PAIRWISE;
ssid->group_cipher = DEFAULT_GROUP;
ssid->key_mgmt = DEFAULT_KEY_MGMT;
+ ssid->bg_scan_period = DEFAULT_BG_SCAN_PERIOD;
#ifdef IEEE8021X_EAPOL
ssid->eapol_flags = DEFAULT_EAPOL_FLAGS;
ssid->eap_workaround = DEFAULT_EAP_WORKAROUND;
ssid->eap.fragment_size = DEFAULT_FRAGMENT_SIZE;
#endif /* IEEE8021X_EAPOL */
+#ifdef CONFIG_HT_OVERRIDES
+ ssid->disable_ht = DEFAULT_DISABLE_HT;
+ ssid->disable_ht40 = DEFAULT_DISABLE_HT40;
+ ssid->disable_sgi = DEFAULT_DISABLE_SGI;
+ ssid->disable_max_amsdu = DEFAULT_DISABLE_MAX_AMSDU;
+ ssid->ampdu_factor = DEFAULT_AMPDU_FACTOR;
+ ssid->ampdu_density = DEFAULT_AMPDU_DENSITY;
+#endif /* CONFIG_HT_OVERRIDES */
+ ssid->proactive_key_caching = -1;
+#ifdef CONFIG_IEEE80211W
+ ssid->ieee80211w = MGMT_FRAME_PROTECTION_DEFAULT;
+#endif /* CONFIG_IEEE80211W */
}
@@ -1876,10 +2099,32 @@ int wpa_config_set(struct wpa_ssid *ssid, const char *var, const char *value,
}
+int wpa_config_set_quoted(struct wpa_ssid *ssid, const char *var,
+ const char *value)
+{
+ size_t len;
+ char *buf;
+ int ret;
+
+ len = os_strlen(value);
+ buf = os_malloc(len + 3);
+ if (buf == NULL)
+ return -1;
+ buf[0] = '"';
+ os_memcpy(buf + 1, value, len);
+ buf[len + 1] = '"';
+ buf[len + 2] = '\0';
+ ret = wpa_config_set(ssid, var, buf, 0);
+ os_free(buf);
+ return ret;
+}
+
+
/**
* wpa_config_get_all - Get all options from network configuration
* @ssid: Pointer to network configuration data
* @get_keys: Determines if keys/passwords will be included in returned list
+ * (if they may be exported)
* Returns: %NULL terminated list of all set keys and their values in the form
* of [key1, val1, key2, val2, ... , NULL]
*
@@ -1895,7 +2140,9 @@ char ** wpa_config_get_all(struct wpa_ssid *ssid, int get_keys)
char **props;
int fields_num;
- props = os_zalloc(sizeof(char *) * ((2 * NUM_SSID_FIELDS) + 1));
+ get_keys = get_keys && ssid->export_keys;
+
+ props = os_calloc(2 * NUM_SSID_FIELDS + 1, sizeof(char *));
if (!props)
return NULL;
@@ -2023,8 +2270,7 @@ char * wpa_config_get_no_key(struct wpa_ssid *ssid, const char *var)
void wpa_config_update_psk(struct wpa_ssid *ssid)
{
#ifndef CONFIG_NO_PBKDF2
- pbkdf2_sha1(ssid->passphrase,
- (char *) ssid->ssid, ssid->ssid_len, 4096,
+ pbkdf2_sha1(ssid->passphrase, ssid->ssid, ssid->ssid_len, 4096,
ssid->psk, PMK_LEN);
wpa_hexdump_key(MSG_MSGDUMP, "PSK (from passphrase)",
ssid->psk, PMK_LEN);
@@ -2033,6 +2279,248 @@ void wpa_config_update_psk(struct wpa_ssid *ssid)
}
+int wpa_config_set_cred(struct wpa_cred *cred, const char *var,
+ const char *value, int line)
+{
+ char *val;
+ size_t len;
+
+ if (os_strcmp(var, "priority") == 0) {
+ cred->priority = atoi(value);
+ return 0;
+ }
+
+ if (os_strcmp(var, "pcsc") == 0) {
+ cred->pcsc = atoi(value);
+ return 0;
+ }
+
+ if (os_strcmp(var, "eap") == 0) {
+ struct eap_method_type method;
+ method.method = eap_peer_get_type(value, &method.vendor);
+ if (method.vendor == EAP_VENDOR_IETF &&
+ method.method == EAP_TYPE_NONE) {
+ wpa_printf(MSG_ERROR, "Line %d: unknown EAP type '%s' "
+ "for a credential", line, value);
+ return -1;
+ }
+ os_free(cred->eap_method);
+ cred->eap_method = os_malloc(sizeof(*cred->eap_method));
+ if (cred->eap_method == NULL)
+ return -1;
+ os_memcpy(cred->eap_method, &method, sizeof(method));
+ return 0;
+ }
+
+ if (os_strcmp(var, "password") == 0 &&
+ os_strncmp(value, "ext:", 4) == 0) {
+ os_free(cred->password);
+ cred->password = os_strdup(value);
+ cred->ext_password = 1;
+ return 0;
+ }
+
+ val = wpa_config_parse_string(value, &len);
+ if (val == NULL) {
+ wpa_printf(MSG_ERROR, "Line %d: invalid field '%s' string "
+ "value '%s'.", line, var, value);
+ return -1;
+ }
+
+ if (os_strcmp(var, "realm") == 0) {
+ os_free(cred->realm);
+ cred->realm = val;
+ return 0;
+ }
+
+ if (os_strcmp(var, "username") == 0) {
+ os_free(cred->username);
+ cred->username = val;
+ return 0;
+ }
+
+ if (os_strcmp(var, "password") == 0) {
+ os_free(cred->password);
+ cred->password = val;
+ cred->ext_password = 0;
+ return 0;
+ }
+
+ if (os_strcmp(var, "ca_cert") == 0) {
+ os_free(cred->ca_cert);
+ cred->ca_cert = val;
+ return 0;
+ }
+
+ if (os_strcmp(var, "client_cert") == 0) {
+ os_free(cred->client_cert);
+ cred->client_cert = val;
+ return 0;
+ }
+
+ if (os_strcmp(var, "private_key") == 0) {
+ os_free(cred->private_key);
+ cred->private_key = val;
+ return 0;
+ }
+
+ if (os_strcmp(var, "private_key_passwd") == 0) {
+ os_free(cred->private_key_passwd);
+ cred->private_key_passwd = val;
+ return 0;
+ }
+
+ if (os_strcmp(var, "imsi") == 0) {
+ os_free(cred->imsi);
+ cred->imsi = val;
+ return 0;
+ }
+
+ if (os_strcmp(var, "milenage") == 0) {
+ os_free(cred->milenage);
+ cred->milenage = val;
+ return 0;
+ }
+
+ if (os_strcmp(var, "domain") == 0) {
+ os_free(cred->domain);
+ cred->domain = val;
+ return 0;
+ }
+
+ if (os_strcmp(var, "phase1") == 0) {
+ os_free(cred->phase1);
+ cred->phase1 = val;
+ return 0;
+ }
+
+ if (os_strcmp(var, "phase2") == 0) {
+ os_free(cred->phase2);
+ cred->phase2 = val;
+ return 0;
+ }
+
+ if (os_strcmp(var, "roaming_consortium") == 0) {
+ if (len < 3 || len > sizeof(cred->roaming_consortium)) {
+ wpa_printf(MSG_ERROR, "Line %d: invalid "
+ "roaming_consortium length %d (3..15 "
+ "expected)", line, (int) len);
+ os_free(val);
+ return -1;
+ }
+ os_memcpy(cred->roaming_consortium, val, len);
+ cred->roaming_consortium_len = len;
+ os_free(val);
+ return 0;
+ }
+
+ if (os_strcmp(var, "excluded_ssid") == 0) {
+ struct excluded_ssid *e;
+
+ if (len > MAX_SSID_LEN) {
+ wpa_printf(MSG_ERROR, "Line %d: invalid "
+ "excluded_ssid length %d", line, (int) len);
+ os_free(val);
+ return -1;
+ }
+
+ e = os_realloc_array(cred->excluded_ssid,
+ cred->num_excluded_ssid + 1,
+ sizeof(struct excluded_ssid));
+ if (e == NULL) {
+ os_free(val);
+ return -1;
+ }
+ cred->excluded_ssid = e;
+
+ e = &cred->excluded_ssid[cred->num_excluded_ssid++];
+ os_memcpy(e->ssid, val, len);
+ e->ssid_len = len;
+
+ os_free(val);
+
+ return 0;
+ }
+
+ if (line) {
+ wpa_printf(MSG_ERROR, "Line %d: unknown cred field '%s'.",
+ line, var);
+ }
+
+ os_free(val);
+
+ return -1;
+}
+
+
+struct wpa_cred * wpa_config_get_cred(struct wpa_config *config, int id)
+{
+ struct wpa_cred *cred;
+
+ cred = config->cred;
+ while (cred) {
+ if (id == cred->id)
+ break;
+ cred = cred->next;
+ }
+
+ return cred;
+}
+
+
+struct wpa_cred * wpa_config_add_cred(struct wpa_config *config)
+{
+ int id;
+ struct wpa_cred *cred, *last = NULL;
+
+ id = -1;
+ cred = config->cred;
+ while (cred) {
+ if (cred->id > id)
+ id = cred->id;
+ last = cred;
+ cred = cred->next;
+ }
+ id++;
+
+ cred = os_zalloc(sizeof(*cred));
+ if (cred == NULL)
+ return NULL;
+ cred->id = id;
+ if (last)
+ last->next = cred;
+ else
+ config->cred = cred;
+
+ return cred;
+}
+
+
+int wpa_config_remove_cred(struct wpa_config *config, int id)
+{
+ struct wpa_cred *cred, *prev = NULL;
+
+ cred = config->cred;
+ while (cred) {
+ if (id == cred->id)
+ break;
+ prev = cred;
+ cred = cred->next;
+ }
+
+ if (cred == NULL)
+ return -1;
+
+ if (prev)
+ prev->next = cred->next;
+ else
+ config->cred = cred->next;
+
+ wpa_config_free_cred(cred);
+ return 0;
+}
+
+
#ifndef CONFIG_NO_CONFIG_BLOBS
/**
* wpa_config_get_blob - Get a named configuration blob
@@ -2124,6 +2612,15 @@ struct wpa_config * wpa_config_alloc_empty(const char *ctrl_interface,
const char *driver_param)
{
struct wpa_config *config;
+ const int aCWmin = 4, aCWmax = 10;
+ const struct hostapd_wmm_ac_params ac_bk =
+ { aCWmin, aCWmax, 7, 0, 0 }; /* background traffic */
+ const struct hostapd_wmm_ac_params ac_be =
+ { aCWmin, aCWmax, 3, 0, 0 }; /* best effort traffic */
+ const struct hostapd_wmm_ac_params ac_vi = /* video traffic */
+ { aCWmin - 1, aCWmin, 2, 3000 / 32, 0 };
+ const struct hostapd_wmm_ac_params ac_vo = /* voice traffic */
+ { aCWmin - 2, aCWmin - 1, 2, 1500 / 32, 0 };
config = os_zalloc(sizeof(*config));
if (config == NULL)
@@ -2131,7 +2628,18 @@ struct wpa_config * wpa_config_alloc_empty(const char *ctrl_interface,
config->eapol_version = DEFAULT_EAPOL_VERSION;
config->ap_scan = DEFAULT_AP_SCAN;
config->fast_reauth = DEFAULT_FAST_REAUTH;
+ config->p2p_go_intent = DEFAULT_P2P_GO_INTENT;
+ config->p2p_intra_bss = DEFAULT_P2P_INTRA_BSS;
+ config->p2p_go_max_inactivity = DEFAULT_P2P_GO_MAX_INACTIVITY;
config->bss_max_count = DEFAULT_BSS_MAX_COUNT;
+ config->bss_expiration_age = DEFAULT_BSS_EXPIRATION_AGE;
+ config->bss_expiration_scan_count = DEFAULT_BSS_EXPIRATION_SCAN_COUNT;
+ config->max_num_sta = DEFAULT_MAX_NUM_STA;
+ config->access_network_type = DEFAULT_ACCESS_NETWORK_TYPE;
+ config->wmm_ac_params[0] = ac_be;
+ config->wmm_ac_params[1] = ac_bk;
+ config->wmm_ac_params[2] = ac_vi;
+ config->wmm_ac_params[3] = ac_vo;
if (ctrl_interface)
config->ctrl_interface = os_strdup(ctrl_interface);
@@ -2165,3 +2673,462 @@ void wpa_config_debug_dump_networks(struct wpa_config *config)
}
}
#endif /* CONFIG_NO_STDOUT_DEBUG */
+
+
+struct global_parse_data {
+ char *name;
+ int (*parser)(const struct global_parse_data *data,
+ struct wpa_config *config, int line, const char *value);
+ void *param1, *param2, *param3;
+ unsigned int changed_flag;
+};
+
+
+static int wpa_global_config_parse_int(const struct global_parse_data *data,
+ struct wpa_config *config, int line,
+ const char *pos)
+{
+ int *dst;
+ dst = (int *) (((u8 *) config) + (long) data->param1);
+ *dst = atoi(pos);
+ wpa_printf(MSG_DEBUG, "%s=%d", data->name, *dst);
+
+ if (data->param2 && *dst < (long) data->param2) {
+ wpa_printf(MSG_ERROR, "Line %d: too small %s (value=%d "
+ "min_value=%ld)", line, data->name, *dst,
+ (long) data->param2);
+ *dst = (long) data->param2;
+ return -1;
+ }
+
+ if (data->param3 && *dst > (long) data->param3) {
+ wpa_printf(MSG_ERROR, "Line %d: too large %s (value=%d "
+ "max_value=%ld)", line, data->name, *dst,
+ (long) data->param3);
+ *dst = (long) data->param3;
+ return -1;
+ }
+
+ return 0;
+}
+
+
+static int wpa_global_config_parse_str(const struct global_parse_data *data,
+ struct wpa_config *config, int line,
+ const char *pos)
+{
+ size_t len;
+ char **dst, *tmp;
+
+ len = os_strlen(pos);
+ if (data->param2 && len < (size_t) data->param2) {
+ wpa_printf(MSG_ERROR, "Line %d: too short %s (len=%lu "
+ "min_len=%ld)", line, data->name,
+ (unsigned long) len, (long) data->param2);
+ return -1;
+ }
+
+ if (data->param3 && len > (size_t) data->param3) {
+ wpa_printf(MSG_ERROR, "Line %d: too long %s (len=%lu "
+ "max_len=%ld)", line, data->name,
+ (unsigned long) len, (long) data->param3);
+ return -1;
+ }
+
+ tmp = os_strdup(pos);
+ if (tmp == NULL)
+ return -1;
+
+ dst = (char **) (((u8 *) config) + (long) data->param1);
+ os_free(*dst);
+ *dst = tmp;
+ wpa_printf(MSG_DEBUG, "%s='%s'", data->name, *dst);
+
+ return 0;
+}
+
+
+static int wpa_global_config_parse_bin(const struct global_parse_data *data,
+ struct wpa_config *config, int line,
+ const char *pos)
+{
+ size_t len;
+ struct wpabuf **dst, *tmp;
+
+ len = os_strlen(pos);
+ if (len & 0x01)
+ return -1;
+
+ tmp = wpabuf_alloc(len / 2);
+ if (tmp == NULL)
+ return -1;
+
+ if (hexstr2bin(pos, wpabuf_put(tmp, len / 2), len / 2)) {
+ wpabuf_free(tmp);
+ return -1;
+ }
+
+ dst = (struct wpabuf **) (((u8 *) config) + (long) data->param1);
+ wpabuf_free(*dst);
+ *dst = tmp;
+ wpa_printf(MSG_DEBUG, "%s", data->name);
+
+ return 0;
+}
+
+
+static int wpa_config_process_country(const struct global_parse_data *data,
+ struct wpa_config *config, int line,
+ const char *pos)
+{
+ if (!pos[0] || !pos[1]) {
+ wpa_printf(MSG_DEBUG, "Invalid country set");
+ return -1;
+ }
+ config->country[0] = pos[0];
+ config->country[1] = pos[1];
+ wpa_printf(MSG_DEBUG, "country='%c%c'",
+ config->country[0], config->country[1]);
+ return 0;
+}
+
+
+static int wpa_config_process_load_dynamic_eap(
+ const struct global_parse_data *data, struct wpa_config *config,
+ int line, const char *so)
+{
+ int ret;
+ wpa_printf(MSG_DEBUG, "load_dynamic_eap=%s", so);
+ ret = eap_peer_method_load(so);
+ if (ret == -2) {
+ wpa_printf(MSG_DEBUG, "This EAP type was already loaded - not "
+ "reloading.");
+ } else if (ret) {
+ wpa_printf(MSG_ERROR, "Line %d: Failed to load dynamic EAP "
+ "method '%s'.", line, so);
+ return -1;
+ }
+
+ return 0;
+}
+
+
+#ifdef CONFIG_WPS
+
+static int wpa_config_process_uuid(const struct global_parse_data *data,
+ struct wpa_config *config, int line,
+ const char *pos)
+{
+ char buf[40];
+ if (uuid_str2bin(pos, config->uuid)) {
+ wpa_printf(MSG_ERROR, "Line %d: invalid UUID", line);
+ return -1;
+ }
+ uuid_bin2str(config->uuid, buf, sizeof(buf));
+ wpa_printf(MSG_DEBUG, "uuid=%s", buf);
+ return 0;
+}
+
+
+static int wpa_config_process_device_type(
+ const struct global_parse_data *data,
+ struct wpa_config *config, int line, const char *pos)
+{
+ return wps_dev_type_str2bin(pos, config->device_type);
+}
+
+
+static int wpa_config_process_os_version(const struct global_parse_data *data,
+ struct wpa_config *config, int line,
+ const char *pos)
+{
+ if (hexstr2bin(pos, config->os_version, 4)) {
+ wpa_printf(MSG_ERROR, "Line %d: invalid os_version", line);
+ return -1;
+ }
+ wpa_printf(MSG_DEBUG, "os_version=%08x",
+ WPA_GET_BE32(config->os_version));
+ return 0;
+}
+
+
+static int wpa_config_process_wps_vendor_ext_m1(
+ const struct global_parse_data *data,
+ struct wpa_config *config, int line, const char *pos)
+{
+ struct wpabuf *tmp;
+ int len = os_strlen(pos) / 2;
+ u8 *p;
+
+ if (!len) {
+ wpa_printf(MSG_ERROR, "Line %d: "
+ "invalid wps_vendor_ext_m1", line);
+ return -1;
+ }
+
+ tmp = wpabuf_alloc(len);
+ if (tmp) {
+ p = wpabuf_put(tmp, len);
+
+ if (hexstr2bin(pos, p, len)) {
+ wpa_printf(MSG_ERROR, "Line %d: "
+ "invalid wps_vendor_ext_m1", line);
+ wpabuf_free(tmp);
+ return -1;
+ }
+
+ wpabuf_free(config->wps_vendor_ext_m1);
+ config->wps_vendor_ext_m1 = tmp;
+ } else {
+ wpa_printf(MSG_ERROR, "Can not allocate "
+ "memory for wps_vendor_ext_m1");
+ return -1;
+ }
+
+ return 0;
+}
+
+#endif /* CONFIG_WPS */
+
+#ifdef CONFIG_P2P
+static int wpa_config_process_sec_device_type(
+ const struct global_parse_data *data,
+ struct wpa_config *config, int line, const char *pos)
+{
+ int idx;
+
+ if (config->num_sec_device_types >= MAX_SEC_DEVICE_TYPES) {
+ wpa_printf(MSG_ERROR, "Line %d: too many sec_device_type "
+ "items", line);
+ return -1;
+ }
+
+ idx = config->num_sec_device_types;
+
+ if (wps_dev_type_str2bin(pos, config->sec_device_type[idx]))
+ return -1;
+
+ config->num_sec_device_types++;
+ return 0;
+}
+
+
+static int wpa_config_process_p2p_pref_chan(
+ const struct global_parse_data *data,
+ struct wpa_config *config, int line, const char *pos)
+{
+ struct p2p_channel *pref = NULL, *n;
+ unsigned int num = 0;
+ const char *pos2;
+ u8 op_class, chan;
+
+ /* format: class:chan,class:chan,... */
+
+ while (*pos) {
+ op_class = atoi(pos);
+ pos2 = os_strchr(pos, ':');
+ if (pos2 == NULL)
+ goto fail;
+ pos2++;
+ chan = atoi(pos2);
+
+ n = os_realloc_array(pref, num + 1,
+ sizeof(struct p2p_channel));
+ if (n == NULL)
+ goto fail;
+ pref = n;
+ pref[num].op_class = op_class;
+ pref[num].chan = chan;
+ num++;
+
+ pos = os_strchr(pos2, ',');
+ if (pos == NULL)
+ break;
+ pos++;
+ }
+
+ os_free(config->p2p_pref_chan);
+ config->p2p_pref_chan = pref;
+ config->num_p2p_pref_chan = num;
+ wpa_hexdump(MSG_DEBUG, "P2P: Preferred class/channel pairs",
+ (u8 *) config->p2p_pref_chan,
+ config->num_p2p_pref_chan * sizeof(struct p2p_channel));
+
+ return 0;
+
+fail:
+ os_free(pref);
+ wpa_printf(MSG_ERROR, "Line %d: Invalid p2p_pref_chan list", line);
+ return -1;
+}
+#endif /* CONFIG_P2P */
+
+
+static int wpa_config_process_hessid(
+ const struct global_parse_data *data,
+ struct wpa_config *config, int line, const char *pos)
+{
+ if (hwaddr_aton2(pos, config->hessid) < 0) {
+ wpa_printf(MSG_ERROR, "Line %d: Invalid hessid '%s'",
+ line, pos);
+ return -1;
+ }
+
+ return 0;
+}
+
+
+#ifdef OFFSET
+#undef OFFSET
+#endif /* OFFSET */
+/* OFFSET: Get offset of a variable within the wpa_config structure */
+#define OFFSET(v) ((void *) &((struct wpa_config *) 0)->v)
+
+#define FUNC(f) #f, wpa_config_process_ ## f, OFFSET(f), NULL, NULL
+#define FUNC_NO_VAR(f) #f, wpa_config_process_ ## f, NULL, NULL, NULL
+#define _INT(f) #f, wpa_global_config_parse_int, OFFSET(f)
+#define INT(f) _INT(f), NULL, NULL
+#define INT_RANGE(f, min, max) _INT(f), (void *) min, (void *) max
+#define _STR(f) #f, wpa_global_config_parse_str, OFFSET(f)
+#define STR(f) _STR(f), NULL, NULL
+#define STR_RANGE(f, min, max) _STR(f), (void *) min, (void *) max
+#define BIN(f) #f, wpa_global_config_parse_bin, OFFSET(f), NULL, NULL
+
+static const struct global_parse_data global_fields[] = {
+#ifdef CONFIG_CTRL_IFACE
+ { STR(ctrl_interface), 0 },
+ { STR(ctrl_interface_group), 0 } /* deprecated */,
+#endif /* CONFIG_CTRL_IFACE */
+ { INT_RANGE(eapol_version, 1, 2), 0 },
+ { INT(ap_scan), 0 },
+ { INT(disable_scan_offload), 0 },
+ { INT(fast_reauth), 0 },
+ { STR(opensc_engine_path), 0 },
+ { STR(pkcs11_engine_path), 0 },
+ { STR(pkcs11_module_path), 0 },
+ { STR(pcsc_reader), 0 },
+ { STR(pcsc_pin), 0 },
+ { STR(driver_param), 0 },
+ { INT(dot11RSNAConfigPMKLifetime), 0 },
+ { INT(dot11RSNAConfigPMKReauthThreshold), 0 },
+ { INT(dot11RSNAConfigSATimeout), 0 },
+#ifndef CONFIG_NO_CONFIG_WRITE
+ { INT(update_config), 0 },
+#endif /* CONFIG_NO_CONFIG_WRITE */
+ { FUNC_NO_VAR(load_dynamic_eap), 0 },
+#ifdef CONFIG_WPS
+ { FUNC(uuid), CFG_CHANGED_UUID },
+ { STR_RANGE(device_name, 0, 32), CFG_CHANGED_DEVICE_NAME },
+ { STR_RANGE(manufacturer, 0, 64), CFG_CHANGED_WPS_STRING },
+ { STR_RANGE(model_name, 0, 32), CFG_CHANGED_WPS_STRING },
+ { STR_RANGE(model_number, 0, 32), CFG_CHANGED_WPS_STRING },
+ { STR_RANGE(serial_number, 0, 32), CFG_CHANGED_WPS_STRING },
+ { FUNC(device_type), CFG_CHANGED_DEVICE_TYPE },
+ { FUNC(os_version), CFG_CHANGED_OS_VERSION },
+ { STR(config_methods), CFG_CHANGED_CONFIG_METHODS },
+ { INT_RANGE(wps_cred_processing, 0, 2), 0 },
+ { FUNC(wps_vendor_ext_m1), CFG_CHANGED_VENDOR_EXTENSION },
+#endif /* CONFIG_WPS */
+#ifdef CONFIG_P2P
+ { FUNC(sec_device_type), CFG_CHANGED_SEC_DEVICE_TYPE },
+ { INT(p2p_listen_reg_class), 0 },
+ { INT(p2p_listen_channel), 0 },
+ { INT(p2p_oper_reg_class), 0 },
+ { INT(p2p_oper_channel), 0 },
+ { INT_RANGE(p2p_go_intent, 0, 15), 0 },
+ { STR(p2p_ssid_postfix), CFG_CHANGED_P2P_SSID_POSTFIX },
+ { INT_RANGE(persistent_reconnect, 0, 1), 0 },
+ { INT_RANGE(p2p_intra_bss, 0, 1), CFG_CHANGED_P2P_INTRA_BSS },
+ { INT(p2p_group_idle), 0 },
+ { FUNC(p2p_pref_chan), CFG_CHANGED_P2P_PREF_CHAN },
+ { INT(p2p_go_ht40), 0 },
+ { INT(p2p_disabled), 0 },
+ { INT(p2p_no_group_iface), 0 },
+#endif /* CONFIG_P2P */
+ { FUNC(country), CFG_CHANGED_COUNTRY },
+ { INT(bss_max_count), 0 },
+ { INT(bss_expiration_age), 0 },
+ { INT(bss_expiration_scan_count), 0 },
+ { INT_RANGE(filter_ssids, 0, 1), 0 },
+ { INT_RANGE(filter_rssi, -100, 0), 0 },
+ { INT(max_num_sta), 0 },
+ { INT_RANGE(disassoc_low_ack, 0, 1), 0 },
+#ifdef CONFIG_HS20
+ { INT_RANGE(hs20, 0, 1), 0 },
+#endif /* CONFIG_HS20 */
+ { INT_RANGE(interworking, 0, 1), 0 },
+ { FUNC(hessid), 0 },
+ { INT_RANGE(access_network_type, 0, 15), 0 },
+ { INT_RANGE(pbc_in_m1, 0, 1), 0 },
+ { STR(autoscan), 0 },
+ { INT_RANGE(wps_nfc_dev_pw_id, 0x10, 0xffff), 0 },
+ { BIN(wps_nfc_dh_pubkey), 0 },
+ { BIN(wps_nfc_dh_privkey), 0 },
+ { BIN(wps_nfc_dev_pw), 0 },
+ { STR(ext_password_backend), CFG_CHANGED_EXT_PW_BACKEND },
+ { INT(p2p_go_max_inactivity), 0 },
+ { INT_RANGE(auto_interworking, 0, 1), 0 },
+ { INT(okc), 0 },
+ { INT(pmf), 0 },
+};
+
+#undef FUNC
+#undef _INT
+#undef INT
+#undef INT_RANGE
+#undef _STR
+#undef STR
+#undef STR_RANGE
+#undef BIN
+#define NUM_GLOBAL_FIELDS (sizeof(global_fields) / sizeof(global_fields[0]))
+
+
+int wpa_config_process_global(struct wpa_config *config, char *pos, int line)
+{
+ size_t i;
+ int ret = 0;
+
+ for (i = 0; i < NUM_GLOBAL_FIELDS; i++) {
+ const struct global_parse_data *field = &global_fields[i];
+ size_t flen = os_strlen(field->name);
+ if (os_strncmp(pos, field->name, flen) != 0 ||
+ pos[flen] != '=')
+ continue;
+
+ if (field->parser(field, config, line, pos + flen + 1)) {
+ wpa_printf(MSG_ERROR, "Line %d: failed to "
+ "parse '%s'.", line, pos);
+ ret = -1;
+ }
+ config->changed_parameters |= field->changed_flag;
+ break;
+ }
+ if (i == NUM_GLOBAL_FIELDS) {
+#ifdef CONFIG_AP
+ if (os_strncmp(pos, "wmm_ac_", 7) == 0) {
+ char *tmp = os_strchr(pos, '=');
+ if (tmp == NULL) {
+ if (line < 0)
+ return -1;
+ wpa_printf(MSG_ERROR, "Line %d: invalid line "
+ "'%s'", line, pos);
+ return -1;
+ }
+ *tmp++ = '\0';
+ if (hostapd_config_wmm_ac(config->wmm_ac_params, pos,
+ tmp)) {
+ wpa_printf(MSG_ERROR, "Line %d: invalid WMM "
+ "AC item", line);
+ return -1;
+ }
+ }
+#endif /* CONFIG_AP */
+ if (line < 0)
+ return -1;
+ wpa_printf(MSG_ERROR, "Line %d: unknown global field '%s'.",
+ line, pos);
+ ret = -1;
+ }
+
+ return ret;
+}
diff --git a/contrib/wpa/wpa_supplicant/config.h b/contrib/wpa/wpa_supplicant/config.h
index 754e4be..bb70b9c 100644
--- a/contrib/wpa/wpa_supplicant/config.h
+++ b/contrib/wpa/wpa_supplicant/config.h
@@ -1,15 +1,9 @@
/*
* WPA Supplicant / Configuration file structures
- * Copyright (c) 2003-2005, Jouni Malinen <j@w1.fi>
+ * Copyright (c) 2003-2012, Jouni Malinen <j@w1.fi>
*
- * This program is free software; you can redistribute it and/or modify
- * it under the terms of the GNU General Public License version 2 as
- * published by the Free Software Foundation.
- *
- * Alternatively, this software may be distributed under the terms of BSD
- * license.
- *
- * See README and COPYING for more details.
+ * This software may be distributed under the terms of the BSD license.
+ * See README for more details.
*/
#ifndef CONFIG_H
@@ -22,10 +16,210 @@
#define DEFAULT_AP_SCAN 1
#endif /* CONFIG_NO_SCAN_PROCESSING */
#define DEFAULT_FAST_REAUTH 1
+#define DEFAULT_P2P_GO_INTENT 7
+#define DEFAULT_P2P_INTRA_BSS 1
+#define DEFAULT_P2P_GO_MAX_INACTIVITY (5 * 60)
#define DEFAULT_BSS_MAX_COUNT 200
+#define DEFAULT_BSS_EXPIRATION_AGE 180
+#define DEFAULT_BSS_EXPIRATION_SCAN_COUNT 2
+#define DEFAULT_MAX_NUM_STA 128
+#define DEFAULT_ACCESS_NETWORK_TYPE 15
#include "config_ssid.h"
+#include "wps/wps.h"
+#include "common/ieee802_11_common.h"
+
+
+struct wpa_cred {
+ /**
+ * next - Next credential in the list
+ *
+ * This pointer can be used to iterate over all credentials. The head
+ * of this list is stored in the cred field of struct wpa_config.
+ */
+ struct wpa_cred *next;
+
+ /**
+ * id - Unique id for the credential
+ *
+ * This identifier is used as a unique identifier for each credential
+ * block when using the control interface. Each credential is allocated
+ * an id when it is being created, either when reading the
+ * configuration file or when a new credential is added through the
+ * control interface.
+ */
+ int id;
+
+ /**
+ * priority - Priority group
+ *
+ * By default, all networks and credentials get the same priority group
+ * (0). This field can be used to give higher priority for credentials
+ * (and similarly in struct wpa_ssid for network blocks) to change the
+ * Interworking automatic networking selection behavior. The matching
+ * network (based on either an enabled network block or a credential)
+ * with the highest priority value will be selected.
+ */
+ int priority;
+
+ /**
+ * pcsc - Use PC/SC and SIM/USIM card
+ */
+ int pcsc;
+
+ /**
+ * realm - Home Realm for Interworking
+ */
+ char *realm;
+
+ /**
+ * username - Username for Interworking network selection
+ */
+ char *username;
+
+ /**
+ * password - Password for Interworking network selection
+ */
+ char *password;
+
+ /**
+ * ext_password - Whether password is a name for external storage
+ */
+ int ext_password;
+
+ /**
+ * ca_cert - CA certificate for Interworking network selection
+ */
+ char *ca_cert;
+
+ /**
+ * client_cert - File path to client certificate file (PEM/DER)
+ *
+ * This field is used with Interworking networking selection for a case
+ * where client certificate/private key is used for authentication
+ * (EAP-TLS). Full path to the file should be used since working
+ * directory may change when wpa_supplicant is run in the background.
+ *
+ * Alternatively, a named configuration blob can be used by setting
+ * this to blob://blob_name.
+ */
+ char *client_cert;
+ /**
+ * private_key - File path to client private key file (PEM/DER/PFX)
+ *
+ * When PKCS#12/PFX file (.p12/.pfx) is used, client_cert should be
+ * commented out. Both the private key and certificate will be read
+ * from the PKCS#12 file in this case. Full path to the file should be
+ * used since working directory may change when wpa_supplicant is run
+ * in the background.
+ *
+ * Windows certificate store can be used by leaving client_cert out and
+ * configuring private_key in one of the following formats:
+ *
+ * cert://substring_to_match
+ *
+ * hash://certificate_thumbprint_in_hex
+ *
+ * For example: private_key="hash://63093aa9c47f56ae88334c7b65a4"
+ *
+ * Note that when running wpa_supplicant as an application, the user
+ * certificate store (My user account) is used, whereas computer store
+ * (Computer account) is used when running wpasvc as a service.
+ *
+ * Alternatively, a named configuration blob can be used by setting
+ * this to blob://blob_name.
+ */
+ char *private_key;
+
+ /**
+ * private_key_passwd - Password for private key file
+ */
+ char *private_key_passwd;
+
+ /**
+ * imsi - IMSI in <MCC> | <MNC> | '-' | <MSIN> format
+ */
+ char *imsi;
+
+ /**
+ * milenage - Milenage parameters for SIM/USIM simulator in
+ * <Ki>:<OPc>:<SQN> format
+ */
+ char *milenage;
+
+ /**
+ * domain - Home service provider FQDN
+ *
+ * This is used to compare against the Domain Name List to figure out
+ * whether the AP is operated by the Home SP.
+ */
+ char *domain;
+
+ /**
+ * roaming_consortium - Roaming Consortium OI
+ *
+ * If roaming_consortium_len is non-zero, this field contains the
+ * Roaming Consortium OI that can be used to determine which access
+ * points support authentication with this credential. This is an
+ * alternative to the use of the realm parameter. When using Roaming
+ * Consortium to match the network, the EAP parameters need to be
+ * pre-configured with the credential since the NAI Realm information
+ * may not be available or fetched.
+ */
+ u8 roaming_consortium[15];
+
+ /**
+ * roaming_consortium_len - Length of roaming_consortium
+ */
+ size_t roaming_consortium_len;
+
+ /**
+ * eap_method - EAP method to use
+ *
+ * Pre-configured EAP method to use with this credential or %NULL to
+ * indicate no EAP method is selected, i.e., the method will be
+ * selected automatically based on ANQP information.
+ */
+ struct eap_method_type *eap_method;
+
+ /**
+ * phase1 - Phase 1 (outer authentication) parameters
+ *
+ * Pre-configured EAP parameters or %NULL.
+ */
+ char *phase1;
+
+ /**
+ * phase2 - Phase 2 (inner authentication) parameters
+ *
+ * Pre-configured EAP parameters or %NULL.
+ */
+ char *phase2;
+
+ struct excluded_ssid {
+ u8 ssid[MAX_SSID_LEN];
+ size_t ssid_len;
+ } *excluded_ssid;
+ size_t num_excluded_ssid;
+};
+
+
+#define CFG_CHANGED_DEVICE_NAME BIT(0)
+#define CFG_CHANGED_CONFIG_METHODS BIT(1)
+#define CFG_CHANGED_DEVICE_TYPE BIT(2)
+#define CFG_CHANGED_OS_VERSION BIT(3)
+#define CFG_CHANGED_UUID BIT(4)
+#define CFG_CHANGED_COUNTRY BIT(5)
+#define CFG_CHANGED_SEC_DEVICE_TYPE BIT(6)
+#define CFG_CHANGED_P2P_SSID_POSTFIX BIT(7)
+#define CFG_CHANGED_WPS_STRING BIT(8)
+#define CFG_CHANGED_P2P_INTRA_BSS BIT(9)
+#define CFG_CHANGED_VENDOR_EXTENSION BIT(10)
+#define CFG_CHANGED_P2P_LISTEN_CHANNEL BIT(11)
+#define CFG_CHANGED_P2P_OPER_CHANNEL BIT(12)
+#define CFG_CHANGED_P2P_PREF_CHAN BIT(13)
+#define CFG_CHANGED_EXT_PW_BACKEND BIT(14)
/**
* struct wpa_config - wpa_supplicant configuration data
@@ -57,6 +251,13 @@ struct wpa_config {
int num_prio;
/**
+ * cred - Head of the credential list
+ *
+ * This is the head for the list of all the configured credentials.
+ */
+ struct wpa_cred *cred;
+
+ /**
* eapol_version - IEEE 802.1X/EAPOL version number
*
* wpa_supplicant is implemented based on IEEE Std 802.1X-2004 which
@@ -98,12 +299,21 @@ struct wpa_config {
int ap_scan;
/**
+ * disable_scan_offload - Disable automatic offloading of scan requests
+ *
+ * By default, %wpa_supplicant tries to offload scanning if the driver
+ * indicates support for this (sched_scan). This configuration
+ * parameter can be used to disable this offloading mechanism.
+ */
+ int disable_scan_offload;
+
+ /**
* ctrl_interface - Parameters for the control interface
*
* If this is specified, %wpa_supplicant will open a control interface
* that is available for external programs to manage %wpa_supplicant.
* The meaning of this string depends on which control interface
- * mechanism is used. For all cases, the existance of this parameter
+ * mechanism is used. For all cases, the existence of this parameter
* in configuration is used to determine whether the control interface
* is enabled.
*
@@ -196,6 +406,23 @@ struct wpa_config {
char *pkcs11_module_path;
/**
+ * pcsc_reader - PC/SC reader name prefix
+ *
+ * If not %NULL, PC/SC reader with a name that matches this prefix is
+ * initialized for SIM/USIM access. Empty string can be used to match
+ * the first available reader.
+ */
+ char *pcsc_reader;
+
+ /**
+ * pcsc_pin - PIN for USIM, GSM SIM, and smartcards
+ *
+ * This field is used to configure PIN for SIM/USIM for EAP-SIM and
+ * EAP-AKA. If left out, this will be asked through control interface.
+ */
+ char *pcsc_pin;
+
+ /**
* driver_param - Driver interface parameters
*
* This text string is passed to the selected driver interface with the
@@ -285,26 +512,19 @@ struct wpa_config {
/**
* device_type - Primary Device Type (WPS)
- * Used format: categ-OUI-subcateg
- * categ = Category as an integer value
- * OUI = OUI and type octet as a 4-octet hex-encoded value;
- * 0050F204 for default WPS OUI
- * subcateg = OUI-specific Sub Category as an integer value
- * Examples:
- * 1-0050F204-1 (Computer / PC)
- * 1-0050F204-2 (Computer / Server)
- * 5-0050F204-1 (Storage / NAS)
- * 6-0050F204-1 (Network Infrastructure / AP)
*/
- char *device_type;
+ u8 device_type[WPS_DEV_TYPE_LEN];
/**
* config_methods - Config Methods
*
* This is a space-separated list of supported WPS configuration
- * methods. For example, "label display push_button keypad".
+ * methods. For example, "label virtual_display virtual_push_button
+ * keypad".
* Available methods: usba ethernet label display ext_nfc_token
- * int_nfc_token nfc_interface push_button keypad.
+ * int_nfc_token nfc_interface push_button keypad
+ * virtual_display physical_display
+ * virtual_push_button physical_push_button.
*/
char *config_methods;
@@ -333,18 +553,250 @@ struct wpa_config {
*/
int wps_cred_processing;
+#define MAX_SEC_DEVICE_TYPES 5
+ /**
+ * sec_device_types - Secondary Device Types (P2P)
+ */
+ u8 sec_device_type[MAX_SEC_DEVICE_TYPES][WPS_DEV_TYPE_LEN];
+ int num_sec_device_types;
+
+ int p2p_listen_reg_class;
+ int p2p_listen_channel;
+ int p2p_oper_reg_class;
+ int p2p_oper_channel;
+ int p2p_go_intent;
+ char *p2p_ssid_postfix;
+ int persistent_reconnect;
+ int p2p_intra_bss;
+ unsigned int num_p2p_pref_chan;
+ struct p2p_channel *p2p_pref_chan;
+
+ struct wpabuf *wps_vendor_ext_m1;
+
+#define MAX_WPS_VENDOR_EXT 10
+ /**
+ * wps_vendor_ext - Vendor extension attributes in WPS
+ */
+ struct wpabuf *wps_vendor_ext[MAX_WPS_VENDOR_EXT];
+
+ /**
+ * p2p_group_idle - Maximum idle time in seconds for P2P group
+ *
+ * This value controls how long a P2P group is maintained after there
+ * is no other members in the group. As a GO, this means no associated
+ * stations in the group. As a P2P client, this means no GO seen in
+ * scan results. The maximum idle time is specified in seconds with 0
+ * indicating no time limit, i.e., the P2P group remains in active
+ * state indefinitely until explicitly removed. As a P2P client, the
+ * maximum idle time of P2P_MAX_CLIENT_IDLE seconds is enforced, i.e.,
+ * this parameter is mainly meant for GO use and for P2P client, it can
+ * only be used to reduce the default timeout to smaller value. A
+ * special value -1 can be used to configure immediate removal of the
+ * group for P2P client role on any disconnection after the data
+ * connection has been established.
+ */
+ int p2p_group_idle;
+
/**
* bss_max_count - Maximum number of BSS entries to keep in memory
*/
unsigned int bss_max_count;
/**
+ * bss_expiration_age - BSS entry age after which it can be expired
+ *
+ * This value controls the time in seconds after which a BSS entry
+ * gets removed if it has not been updated or is not in use.
+ */
+ unsigned int bss_expiration_age;
+
+ /**
+ * bss_expiration_scan_count - Expire BSS after number of scans
+ *
+ * If the BSS entry has not been seen in this many scans, it will be
+ * removed. A value of 1 means that entry is removed after the first
+ * scan in which the BSSID is not seen. Larger values can be used
+ * to avoid BSS entries disappearing if they are not visible in
+ * every scan (e.g., low signal quality or interference).
+ */
+ unsigned int bss_expiration_scan_count;
+
+ /**
* filter_ssids - SSID-based scan result filtering
*
* 0 = do not filter scan results
* 1 = only include configured SSIDs in scan results/BSS table
*/
int filter_ssids;
+
+ /**
+ * filter_rssi - RSSI-based scan result filtering
+ *
+ * 0 = do not filter scan results
+ * -n = filter scan results below -n dBm
+ */
+ int filter_rssi;
+
+ /**
+ * max_num_sta - Maximum number of STAs in an AP/P2P GO
+ */
+ unsigned int max_num_sta;
+
+ /**
+ * changed_parameters - Bitmap of changed parameters since last update
+ */
+ unsigned int changed_parameters;
+
+ /**
+ * disassoc_low_ack - Disassocicate stations with massive packet loss
+ */
+ int disassoc_low_ack;
+
+ /**
+ * interworking - Whether Interworking (IEEE 802.11u) is enabled
+ */
+ int interworking;
+
+ /**
+ * access_network_type - Access Network Type
+ *
+ * When Interworking is enabled, scans will be limited to APs that
+ * advertise the specified Access Network Type (0..15; with 15
+ * indicating wildcard match).
+ */
+ int access_network_type;
+
+ /**
+ * hessid - Homogenous ESS identifier
+ *
+ * If this is set (any octet is non-zero), scans will be used to
+ * request response only from BSSes belonging to the specified
+ * Homogeneous ESS. This is used only if interworking is enabled.
+ */
+ u8 hessid[ETH_ALEN];
+
+ /**
+ * hs20 - Hotspot 2.0
+ */
+ int hs20;
+
+ /**
+ * pbc_in_m1 - AP mode WPS probing workaround for PBC with Windows 7
+ *
+ * Windows 7 uses incorrect way of figuring out AP's WPS capabilities
+ * by acting as a Registrar and using M1 from the AP. The config
+ * methods attribute in that message is supposed to indicate only the
+ * configuration method supported by the AP in Enrollee role, i.e., to
+ * add an external Registrar. For that case, PBC shall not be used and
+ * as such, the PushButton config method is removed from M1 by default.
+ * If pbc_in_m1=1 is included in the configuration file, the PushButton
+ * config method is left in M1 (if included in config_methods
+ * parameter) to allow Windows 7 to use PBC instead of PIN (e.g., from
+ * a label in the AP).
+ */
+ int pbc_in_m1;
+
+ /**
+ * autoscan - Automatic scan parameters or %NULL if none
+ *
+ * This is an optional set of parameters for automatic scanning
+ * within an interface in following format:
+ * <autoscan module name>:<module parameters>
+ */
+ char *autoscan;
+
+ /**
+ * wps_nfc_dev_pw_id - NFC Device Password ID for password token
+ */
+ int wps_nfc_dev_pw_id;
+
+ /**
+ * wps_nfc_dh_pubkey - NFC DH Public Key for password token
+ */
+ struct wpabuf *wps_nfc_dh_pubkey;
+
+ /**
+ * wps_nfc_dh_privkey - NFC DH Private Key for password token
+ */
+ struct wpabuf *wps_nfc_dh_privkey;
+
+ /**
+ * wps_nfc_dev_pw - NFC Device Password for password token
+ */
+ struct wpabuf *wps_nfc_dev_pw;
+
+ /**
+ * ext_password_backend - External password backend or %NULL if none
+ *
+ * format: <backend name>[:<optional backend parameters>]
+ */
+ char *ext_password_backend;
+
+ /*
+ * p2p_go_max_inactivity - Timeout in seconds to detect STA inactivity
+ *
+ * This timeout value is used in P2P GO mode to clean up
+ * inactive stations.
+ * By default: 300 seconds.
+ */
+ int p2p_go_max_inactivity;
+
+ struct hostapd_wmm_ac_params wmm_ac_params[4];
+
+ /**
+ * auto_interworking - Whether to use network selection automatically
+ *
+ * 0 = do not automatically go through Interworking network selection
+ * (i.e., require explicit interworking_select command for this)
+ * 1 = perform Interworking network selection if one or more
+ * credentials have been configured and scan did not find a
+ * matching network block
+ */
+ int auto_interworking;
+
+ /**
+ * p2p_go_ht40 - Default mode for HT40 enable when operating as GO.
+ *
+ * This will take effect for p2p_group_add, p2p_connect, and p2p_invite.
+ * Note that regulatory constraints and driver capabilities are
+ * consulted anyway, so setting it to 1 can't do real harm.
+ * By default: 0 (disabled)
+ */
+ int p2p_go_ht40;
+
+ /**
+ * p2p_disabled - Whether P2P operations are disabled for this interface
+ */
+ int p2p_disabled;
+
+ /**
+ * p2p_no_group_iface - Whether group interfaces can be used
+ *
+ * By default, wpa_supplicant will create a separate interface for P2P
+ * group operations if the driver supports this. This functionality can
+ * be disabled by setting this parameter to 1. In that case, the same
+ * interface that was used for the P2P management operations is used
+ * also for the group operation.
+ */
+ int p2p_no_group_iface;
+
+ /**
+ * okc - Whether to enable opportunistic key caching by default
+ *
+ * By default, OKC is disabled unless enabled by the per-network
+ * proactive_key_caching=1 parameter. okc=1 can be used to change this
+ * default behavior.
+ */
+ int okc;
+
+ /**
+ * pmf - Whether to enable/require PMF by default
+ *
+ * By default, PMF is disabled unless enabled by the per-network
+ * ieee80211w=1 or ieee80211w=2 parameter. pmf=1/2 can be used to change
+ * this default behavior.
+ */
+ enum mfp_options pmf;
};
@@ -352,12 +804,17 @@ struct wpa_config {
void wpa_config_free(struct wpa_config *ssid);
void wpa_config_free_ssid(struct wpa_ssid *ssid);
+void wpa_config_foreach_network(struct wpa_config *config,
+ void (*func)(void *, struct wpa_ssid *),
+ void *arg);
struct wpa_ssid * wpa_config_get_network(struct wpa_config *config, int id);
struct wpa_ssid * wpa_config_add_network(struct wpa_config *config);
int wpa_config_remove_network(struct wpa_config *config, int id);
void wpa_config_set_network_defaults(struct wpa_ssid *ssid);
int wpa_config_set(struct wpa_ssid *ssid, const char *var, const char *value,
int line);
+int wpa_config_set_quoted(struct wpa_ssid *ssid, const char *var,
+ const char *value);
char ** wpa_config_get_all(struct wpa_ssid *ssid, int get_keys);
char * wpa_config_get(struct wpa_ssid *ssid, const char *var);
char * wpa_config_get_no_key(struct wpa_ssid *ssid, const char *var);
@@ -372,6 +829,13 @@ void wpa_config_set_blob(struct wpa_config *config,
void wpa_config_free_blob(struct wpa_config_blob *blob);
int wpa_config_remove_blob(struct wpa_config *config, const char *name);
+struct wpa_cred * wpa_config_get_cred(struct wpa_config *config, int id);
+struct wpa_cred * wpa_config_add_cred(struct wpa_config *config);
+int wpa_config_remove_cred(struct wpa_config *config, int id);
+void wpa_config_free_cred(struct wpa_cred *cred);
+int wpa_config_set_cred(struct wpa_cred *cred, const char *var,
+ const char *value, int line);
+
struct wpa_config * wpa_config_alloc_empty(const char *ctrl_interface,
const char *driver_param);
#ifndef CONFIG_NO_STDOUT_DEBUG
@@ -381,6 +845,10 @@ void wpa_config_debug_dump_networks(struct wpa_config *config);
#endif /* CONFIG_NO_STDOUT_DEBUG */
+/* Prototypes for common functions from config.c */
+int wpa_config_process_global(struct wpa_config *config, char *pos, int line);
+
+
/* Prototypes for backend specific functions from the selected config_*.c */
/**
diff --git a/contrib/wpa/wpa_supplicant/config_file.c b/contrib/wpa/wpa_supplicant/config_file.c
index 5f07045..8f32cc8 100644
--- a/contrib/wpa/wpa_supplicant/config_file.c
+++ b/contrib/wpa/wpa_supplicant/config_file.c
@@ -1,15 +1,9 @@
/*
* WPA Supplicant / Configuration backend: text file
- * Copyright (c) 2003-2008, Jouni Malinen <j@w1.fi>
+ * Copyright (c) 2003-2012, Jouni Malinen <j@w1.fi>
*
- * This program is free software; you can redistribute it and/or modify
- * it under the terms of the GNU General Public License version 2 as
- * published by the Free Software Foundation.
- *
- * Alternatively, this software may be distributed under the terms of BSD
- * license.
- *
- * See README and COPYING for more details.
+ * This software may be distributed under the terms of the BSD license.
+ * See README for more details.
*
* This file implements a configuration backend for text files. All the
* configuration information is stored in a text file that uses a format
@@ -22,7 +16,32 @@
#include "config.h"
#include "base64.h"
#include "uuid.h"
+#include "p2p/p2p.h"
#include "eap_peer/eap_methods.h"
+#include "eap_peer/eap.h"
+
+
+static int newline_terminated(const char *buf, size_t buflen)
+{
+ size_t len = os_strlen(buf);
+ if (len == 0)
+ return 0;
+ if (len == buflen - 1 && buf[buflen - 1] != '\r' &&
+ buf[len - 1] != '\n')
+ return 0;
+ return 1;
+}
+
+
+static void skip_line_end(FILE *stream)
+{
+ char buf[100];
+ while (fgets(buf, sizeof(buf), stream)) {
+ buf[sizeof(buf) - 1] = '\0';
+ if (newline_terminated(buf, sizeof(buf)))
+ return;
+ }
+}
/**
@@ -47,6 +66,15 @@ static char * wpa_config_get_line(char *s, int size, FILE *stream, int *line,
while (fgets(s, size, stream)) {
(*line)++;
s[size - 1] = '\0';
+ if (!newline_terminated(s, size)) {
+ /*
+ * The line was truncated - skip rest of it to avoid
+ * confusing error messages.
+ */
+ wpa_printf(MSG_INFO, "Long line in configuration file "
+ "truncated");
+ skip_line_end(stream);
+ }
pos = s;
/* Skip white space from the beginning of line. */
@@ -105,14 +133,6 @@ static int wpa_config_validate_network(struct wpa_ssid *ssid, int line)
wpa_config_update_psk(ssid);
}
- if ((ssid->key_mgmt & (WPA_KEY_MGMT_PSK | WPA_KEY_MGMT_FT_PSK |
- WPA_KEY_MGMT_PSK_SHA256)) &&
- !ssid->psk_set) {
- wpa_printf(MSG_ERROR, "Line %d: WPA-PSK accepted for key "
- "management, but no PSK configured.", line);
- errors++;
- }
-
if ((ssid->group_cipher & WPA_CIPHER_CCMP) &&
!(ssid->pairwise_cipher & WPA_CIPHER_CCMP) &&
!(ssid->pairwise_cipher & WPA_CIPHER_NONE)) {
@@ -131,7 +151,7 @@ static struct wpa_ssid * wpa_config_read_network(FILE *f, int *line, int id)
{
struct wpa_ssid *ssid;
int errors = 0, end = 0;
- char buf[256], *pos, *pos2;
+ char buf[2000], *pos, *pos2;
wpa_printf(MSG_MSGDUMP, "Line: %d - start of a new network block",
*line);
@@ -187,6 +207,61 @@ static struct wpa_ssid * wpa_config_read_network(FILE *f, int *line, int id)
}
+static struct wpa_cred * wpa_config_read_cred(FILE *f, int *line, int id)
+{
+ struct wpa_cred *cred;
+ int errors = 0, end = 0;
+ char buf[256], *pos, *pos2;
+
+ wpa_printf(MSG_MSGDUMP, "Line: %d - start of a new cred block", *line);
+ cred = os_zalloc(sizeof(*cred));
+ if (cred == NULL)
+ return NULL;
+ cred->id = id;
+
+ while (wpa_config_get_line(buf, sizeof(buf), f, line, &pos)) {
+ if (os_strcmp(pos, "}") == 0) {
+ end = 1;
+ break;
+ }
+
+ pos2 = os_strchr(pos, '=');
+ if (pos2 == NULL) {
+ wpa_printf(MSG_ERROR, "Line %d: Invalid cred line "
+ "'%s'.", *line, pos);
+ errors++;
+ continue;
+ }
+
+ *pos2++ = '\0';
+ if (*pos2 == '"') {
+ if (os_strchr(pos2 + 1, '"') == NULL) {
+ wpa_printf(MSG_ERROR, "Line %d: invalid "
+ "quotation '%s'.", *line, pos2);
+ errors++;
+ continue;
+ }
+ }
+
+ if (wpa_config_set_cred(cred, pos, pos2, *line) < 0)
+ errors++;
+ }
+
+ if (!end) {
+ wpa_printf(MSG_ERROR, "Line %d: cred block was not "
+ "terminated properly.", *line);
+ errors++;
+ }
+
+ if (errors) {
+ wpa_config_free_cred(cred);
+ cred = NULL;
+ }
+
+ return cred;
+}
+
+
#ifndef CONFIG_NO_CONFIG_BLOBS
static struct wpa_config_blob * wpa_config_read_blob(FILE *f, int *line,
const char *name)
@@ -270,253 +345,29 @@ static int wpa_config_process_blob(struct wpa_config *config, FILE *f,
#endif /* CONFIG_NO_CONFIG_BLOBS */
-struct global_parse_data {
- char *name;
- int (*parser)(const struct global_parse_data *data,
- struct wpa_config *config, int line, const char *value);
- void *param1, *param2, *param3;
-};
-
-
-static int wpa_config_parse_int(const struct global_parse_data *data,
- struct wpa_config *config, int line,
- const char *pos)
-{
- int *dst;
- dst = (int *) (((u8 *) config) + (long) data->param1);
- *dst = atoi(pos);
- wpa_printf(MSG_DEBUG, "%s=%d", data->name, *dst);
-
- if (data->param2 && *dst < (long) data->param2) {
- wpa_printf(MSG_ERROR, "Line %d: too small %s (value=%d "
- "min_value=%ld)", line, data->name, *dst,
- (long) data->param2);
- *dst = (long) data->param2;
- return -1;
- }
-
- if (data->param3 && *dst > (long) data->param3) {
- wpa_printf(MSG_ERROR, "Line %d: too large %s (value=%d "
- "max_value=%ld)", line, data->name, *dst,
- (long) data->param3);
- *dst = (long) data->param3;
- return -1;
- }
-
- return 0;
-}
-
-
-static int wpa_config_parse_str(const struct global_parse_data *data,
- struct wpa_config *config, int line,
- const char *pos)
-{
- size_t len;
- char **dst, *tmp;
-
- len = os_strlen(pos);
- if (data->param2 && len < (size_t) data->param2) {
- wpa_printf(MSG_ERROR, "Line %d: too short %s (len=%lu "
- "min_len=%ld)", line, data->name,
- (unsigned long) len, (long) data->param2);
- return -1;
- }
-
- if (data->param3 && len > (size_t) data->param3) {
- wpa_printf(MSG_ERROR, "Line %d: too long %s (len=%lu "
- "max_len=%ld)", line, data->name,
- (unsigned long) len, (long) data->param3);
- return -1;
- }
-
- tmp = os_strdup(pos);
- if (tmp == NULL)
- return -1;
-
- dst = (char **) (((u8 *) config) + (long) data->param1);
- os_free(*dst);
- *dst = tmp;
- wpa_printf(MSG_DEBUG, "%s='%s'", data->name, *dst);
-
- return 0;
-}
-
-
-static int wpa_config_process_country(const struct global_parse_data *data,
- struct wpa_config *config, int line,
- const char *pos)
-{
- if (!pos[0] || !pos[1]) {
- wpa_printf(MSG_DEBUG, "Invalid country set");
- return -1;
- }
- config->country[0] = pos[0];
- config->country[1] = pos[1];
- wpa_printf(MSG_DEBUG, "country='%c%c'",
- config->country[0], config->country[1]);
- return 0;
-}
-
-
-static int wpa_config_process_load_dynamic_eap(
- const struct global_parse_data *data, struct wpa_config *config,
- int line, const char *so)
-{
- int ret;
- wpa_printf(MSG_DEBUG, "load_dynamic_eap=%s", so);
- ret = eap_peer_method_load(so);
- if (ret == -2) {
- wpa_printf(MSG_DEBUG, "This EAP type was already loaded - not "
- "reloading.");
- } else if (ret) {
- wpa_printf(MSG_ERROR, "Line %d: Failed to load dynamic EAP "
- "method '%s'.", line, so);
- return -1;
- }
-
- return 0;
-}
-
-
-#ifdef CONFIG_WPS
-
-static int wpa_config_process_uuid(const struct global_parse_data *data,
- struct wpa_config *config, int line,
- const char *pos)
-{
- char buf[40];
- if (uuid_str2bin(pos, config->uuid)) {
- wpa_printf(MSG_ERROR, "Line %d: invalid UUID", line);
- return -1;
- }
- uuid_bin2str(config->uuid, buf, sizeof(buf));
- wpa_printf(MSG_DEBUG, "uuid=%s", buf);
- return 0;
-}
-
-
-static int wpa_config_process_os_version(const struct global_parse_data *data,
- struct wpa_config *config, int line,
- const char *pos)
-{
- if (hexstr2bin(pos, config->os_version, 4)) {
- wpa_printf(MSG_ERROR, "Line %d: invalid os_version", line);
- return -1;
- }
- wpa_printf(MSG_DEBUG, "os_version=%08x",
- WPA_GET_BE32(config->os_version));
- return 0;
-}
-
-#endif /* CONFIG_WPS */
-
-
-#ifdef OFFSET
-#undef OFFSET
-#endif /* OFFSET */
-/* OFFSET: Get offset of a variable within the wpa_config structure */
-#define OFFSET(v) ((void *) &((struct wpa_config *) 0)->v)
-
-#define FUNC(f) #f, wpa_config_process_ ## f, OFFSET(f), NULL, NULL
-#define FUNC_NO_VAR(f) #f, wpa_config_process_ ## f, NULL, NULL, NULL
-#define _INT(f) #f, wpa_config_parse_int, OFFSET(f)
-#define INT(f) _INT(f), NULL, NULL
-#define INT_RANGE(f, min, max) _INT(f), (void *) min, (void *) max
-#define _STR(f) #f, wpa_config_parse_str, OFFSET(f)
-#define STR(f) _STR(f), NULL, NULL
-#define STR_RANGE(f, min, max) _STR(f), (void *) min, (void *) max
-
-static const struct global_parse_data global_fields[] = {
-#ifdef CONFIG_CTRL_IFACE
- { STR(ctrl_interface) },
- { STR(ctrl_interface_group) } /* deprecated */,
-#endif /* CONFIG_CTRL_IFACE */
- { INT_RANGE(eapol_version, 1, 2) },
- { INT(ap_scan) },
- { INT(fast_reauth) },
- { STR(opensc_engine_path) },
- { STR(pkcs11_engine_path) },
- { STR(pkcs11_module_path) },
- { STR(driver_param) },
- { INT(dot11RSNAConfigPMKLifetime) },
- { INT(dot11RSNAConfigPMKReauthThreshold) },
- { INT(dot11RSNAConfigSATimeout) },
-#ifndef CONFIG_NO_CONFIG_WRITE
- { INT(update_config) },
-#endif /* CONFIG_NO_CONFIG_WRITE */
- { FUNC_NO_VAR(load_dynamic_eap) },
-#ifdef CONFIG_WPS
- { FUNC(uuid) },
- { STR_RANGE(device_name, 0, 32) },
- { STR_RANGE(manufacturer, 0, 64) },
- { STR_RANGE(model_name, 0, 32) },
- { STR_RANGE(model_number, 0, 32) },
- { STR_RANGE(serial_number, 0, 32) },
- { STR(device_type) },
- { FUNC(os_version) },
- { STR(config_methods) },
- { INT_RANGE(wps_cred_processing, 0, 2) },
-#endif /* CONFIG_WPS */
- { FUNC(country) },
- { INT(bss_max_count) },
- { INT_RANGE(filter_ssids, 0, 1) }
-};
-
-#undef FUNC
-#undef _INT
-#undef INT
-#undef INT_RANGE
-#undef _STR
-#undef STR
-#undef STR_RANGE
-#define NUM_GLOBAL_FIELDS (sizeof(global_fields) / sizeof(global_fields[0]))
-
-
-static int wpa_config_process_global(struct wpa_config *config, char *pos,
- int line)
-{
- size_t i;
- int ret = 0;
-
- for (i = 0; i < NUM_GLOBAL_FIELDS; i++) {
- const struct global_parse_data *field = &global_fields[i];
- size_t flen = os_strlen(field->name);
- if (os_strncmp(pos, field->name, flen) != 0 ||
- pos[flen] != '=')
- continue;
-
- if (field->parser(field, config, line, pos + flen + 1)) {
- wpa_printf(MSG_ERROR, "Line %d: failed to "
- "parse '%s'.", line, pos);
- ret = -1;
- }
- break;
- }
- if (i == NUM_GLOBAL_FIELDS) {
- wpa_printf(MSG_ERROR, "Line %d: unknown global field '%s'.",
- line, pos);
- ret = -1;
- }
-
- return ret;
-}
-
-
struct wpa_config * wpa_config_read(const char *name)
{
FILE *f;
- char buf[256], *pos;
+ char buf[512], *pos;
int errors = 0, line = 0;
struct wpa_ssid *ssid, *tail = NULL, *head = NULL;
+ struct wpa_cred *cred, *cred_tail = NULL, *cred_head = NULL;
struct wpa_config *config;
int id = 0;
+ int cred_id = 0;
config = wpa_config_alloc_empty(NULL, NULL);
- if (config == NULL)
+ if (config == NULL) {
+ wpa_printf(MSG_ERROR, "Failed to allocate config file "
+ "structure");
return NULL;
+ }
+
wpa_printf(MSG_DEBUG, "Reading configuration file '%s'", name);
f = fopen(name, "r");
if (f == NULL) {
+ wpa_printf(MSG_ERROR, "Failed to open config file '%s', "
+ "error: %s", name, strerror(errno));
os_free(config);
return NULL;
}
@@ -543,10 +394,26 @@ struct wpa_config * wpa_config_read(const char *name)
errors++;
continue;
}
+ } else if (os_strcmp(pos, "cred={") == 0) {
+ cred = wpa_config_read_cred(f, &line, cred_id++);
+ if (cred == NULL) {
+ wpa_printf(MSG_ERROR, "Line %d: failed to "
+ "parse cred block.", line);
+ errors++;
+ continue;
+ }
+ if (cred_head == NULL) {
+ cred_head = cred_tail = cred;
+ } else {
+ cred_tail->next = cred;
+ cred_tail = cred;
+ }
#ifndef CONFIG_NO_CONFIG_BLOBS
} else if (os_strncmp(pos, "blob-base64-", 12) == 0) {
if (wpa_config_process_blob(config, f, &line, pos + 12)
< 0) {
+ wpa_printf(MSG_ERROR, "Line %d: failed to "
+ "process blob.", line);
errors++;
continue;
}
@@ -563,12 +430,15 @@ struct wpa_config * wpa_config_read(const char *name)
config->ssid = head;
wpa_config_debug_dump_networks(config);
+ config->cred = cred_head;
+#ifndef WPA_IGNORE_CONFIG_ERRORS
if (errors) {
wpa_config_free(config);
config = NULL;
head = NULL;
}
+#endif /* WPA_IGNORE_CONFIG_ERRORS */
return config;
}
@@ -726,6 +596,18 @@ static void write_wep_key(FILE *f, int idx, struct wpa_ssid *ssid)
}
+#ifdef CONFIG_P2P
+static void write_p2p_client_list(FILE *f, struct wpa_ssid *ssid)
+{
+ char *value = wpa_config_get(ssid, "p2p_client_list");
+ if (value == NULL)
+ return;
+ fprintf(f, "\tp2p_client_list=%s\n", value);
+ os_free(value);
+}
+#endif /* CONFIG_P2P */
+
+
static void wpa_config_write_network(FILE *f, struct wpa_ssid *ssid)
{
int i;
@@ -742,9 +624,12 @@ static void wpa_config_write_network(FILE *f, struct wpa_ssid *ssid)
write_psk(f, ssid);
write_proto(f, ssid);
write_key_mgmt(f, ssid);
+ INT_DEF(bg_scan_period, DEFAULT_BG_SCAN_PERIOD);
write_pairwise(f, ssid);
write_group(f, ssid);
write_auth_alg(f, ssid);
+ STR(bgscan);
+ STR(autoscan);
#ifdef IEEE8021X_EAPOL
write_eap(f, ssid);
STR(identity);
@@ -793,13 +678,18 @@ static void wpa_config_write_network(FILE *f, struct wpa_ssid *ssid)
INT_DEFe(fragment_size, DEFAULT_FRAGMENT_SIZE);
#endif /* IEEE8021X_EAPOL */
INT(mode);
- INT(proactive_key_caching);
+ INT(frequency);
+ write_int(f, "proactive_key_caching", ssid->proactive_key_caching, -1);
INT(disabled);
INT(peerkey);
#ifdef CONFIG_IEEE80211W
- INT(ieee80211w);
+ write_int(f, "ieee80211w", ssid->ieee80211w,
+ MGMT_FRAME_PROTECTION_DEFAULT);
#endif /* CONFIG_IEEE80211W */
STR(id_str);
+#ifdef CONFIG_P2P
+ write_p2p_client_list(f, ssid);
+#endif /* CONFIG_P2P */
#undef STR
#undef INT
@@ -807,6 +697,65 @@ static void wpa_config_write_network(FILE *f, struct wpa_ssid *ssid)
}
+static void wpa_config_write_cred(FILE *f, struct wpa_cred *cred)
+{
+ if (cred->priority)
+ fprintf(f, "\tpriority=%d\n", cred->priority);
+ if (cred->pcsc)
+ fprintf(f, "\tpcsc=%d\n", cred->pcsc);
+ if (cred->realm)
+ fprintf(f, "\trealm=\"%s\"\n", cred->realm);
+ if (cred->username)
+ fprintf(f, "\tusername=\"%s\"\n", cred->username);
+ if (cred->password && cred->ext_password)
+ fprintf(f, "\tpassword=ext:%s\n", cred->password);
+ else if (cred->password)
+ fprintf(f, "\tpassword=\"%s\"\n", cred->password);
+ if (cred->ca_cert)
+ fprintf(f, "\tca_cert=\"%s\"\n", cred->ca_cert);
+ if (cred->client_cert)
+ fprintf(f, "\tclient_cert=\"%s\"\n", cred->client_cert);
+ if (cred->private_key)
+ fprintf(f, "\tprivate_key=\"%s\"\n", cred->private_key);
+ if (cred->private_key_passwd)
+ fprintf(f, "\tprivate_key_passwd=\"%s\"\n",
+ cred->private_key_passwd);
+ if (cred->imsi)
+ fprintf(f, "\timsi=\"%s\"\n", cred->imsi);
+ if (cred->milenage)
+ fprintf(f, "\tmilenage=\"%s\"\n", cred->milenage);
+ if (cred->domain)
+ fprintf(f, "\tdomain=\"%s\"\n", cred->domain);
+ if (cred->roaming_consortium_len) {
+ size_t i;
+ fprintf(f, "\troaming_consortium=");
+ for (i = 0; i < cred->roaming_consortium_len; i++)
+ fprintf(f, "%02x", cred->roaming_consortium[i]);
+ fprintf(f, "\n");
+ }
+ if (cred->eap_method) {
+ const char *name;
+ name = eap_get_name(cred->eap_method[0].vendor,
+ cred->eap_method[0].method);
+ fprintf(f, "\teap=%s\n", name);
+ }
+ if (cred->phase1)
+ fprintf(f, "\tphase1=\"%s\"\n", cred->phase1);
+ if (cred->phase2)
+ fprintf(f, "\tphase2=\"%s\"\n", cred->phase2);
+ if (cred->excluded_ssid) {
+ size_t i, j;
+ for (i = 0; i < cred->num_excluded_ssid; i++) {
+ struct excluded_ssid *e = &cred->excluded_ssid[i];
+ fprintf(f, "\texcluded_ssid=");
+ for (j = 0; j < e->ssid_len; j++)
+ fprintf(f, "%02x", e->ssid[j]);
+ fprintf(f, "\n");
+ }
+ }
+}
+
+
#ifndef CONFIG_NO_CONFIG_BLOBS
static int wpa_config_write_blob(FILE *f, struct wpa_config_blob *blob)
{
@@ -823,6 +772,23 @@ static int wpa_config_write_blob(FILE *f, struct wpa_config_blob *blob)
#endif /* CONFIG_NO_CONFIG_BLOBS */
+static void write_global_bin(FILE *f, const char *field,
+ const struct wpabuf *val)
+{
+ size_t i;
+ const u8 *pos;
+
+ if (val == NULL)
+ return;
+
+ fprintf(f, "%s=", field);
+ pos = wpabuf_head(val);
+ for (i = 0; i < wpabuf_len(val); i++)
+ fprintf(f, "%02X", *pos++);
+ fprintf(f, "\n");
+}
+
+
static void wpa_config_write_global(FILE *f, struct wpa_config *config)
{
#ifdef CONFIG_CTRL_IFACE
@@ -836,6 +802,9 @@ static void wpa_config_write_global(FILE *f, struct wpa_config *config)
fprintf(f, "eapol_version=%d\n", config->eapol_version);
if (config->ap_scan != DEFAULT_AP_SCAN)
fprintf(f, "ap_scan=%d\n", config->ap_scan);
+ if (config->disable_scan_offload)
+ fprintf(f, "disable_scan_offload=%d\n",
+ config->disable_scan_offload);
if (config->fast_reauth != DEFAULT_FAST_REAUTH)
fprintf(f, "fast_reauth=%d\n", config->fast_reauth);
if (config->opensc_engine_path)
@@ -847,6 +816,10 @@ static void wpa_config_write_global(FILE *f, struct wpa_config *config)
if (config->pkcs11_module_path)
fprintf(f, "pkcs11_module_path=%s\n",
config->pkcs11_module_path);
+ if (config->pcsc_reader)
+ fprintf(f, "pcsc_reader=%s\n", config->pcsc_reader);
+ if (config->pcsc_pin)
+ fprintf(f, "pcsc_pin=%s\n", config->pcsc_pin);
if (config->driver_param)
fprintf(f, "driver_param=%s\n", config->driver_param);
if (config->dot11RSNAConfigPMKLifetime)
@@ -876,8 +849,13 @@ static void wpa_config_write_global(FILE *f, struct wpa_config *config)
fprintf(f, "model_number=%s\n", config->model_number);
if (config->serial_number)
fprintf(f, "serial_number=%s\n", config->serial_number);
- if (config->device_type)
- fprintf(f, "device_type=%s\n", config->device_type);
+ {
+ char _buf[WPS_DEV_TYPE_BUFSIZE], *buf;
+ buf = wps_dev_type_bin2str(config->device_type,
+ _buf, sizeof(_buf));
+ if (os_strcmp(buf, "0-00000000-0") != 0)
+ fprintf(f, "device_type=%s\n", buf);
+ }
if (WPA_GET_BE32(config->os_version))
fprintf(f, "os_version=%08x\n",
WPA_GET_BE32(config->os_version));
@@ -886,15 +864,112 @@ static void wpa_config_write_global(FILE *f, struct wpa_config *config)
if (config->wps_cred_processing)
fprintf(f, "wps_cred_processing=%d\n",
config->wps_cred_processing);
+ if (config->wps_vendor_ext_m1) {
+ int i, len = wpabuf_len(config->wps_vendor_ext_m1);
+ const u8 *p = wpabuf_head_u8(config->wps_vendor_ext_m1);
+ if (len > 0) {
+ fprintf(f, "wps_vendor_ext_m1=");
+ for (i = 0; i < len; i++)
+ fprintf(f, "%02x", *p++);
+ fprintf(f, "\n");
+ }
+ }
#endif /* CONFIG_WPS */
+#ifdef CONFIG_P2P
+ if (config->p2p_listen_reg_class)
+ fprintf(f, "p2p_listen_reg_class=%u\n",
+ config->p2p_listen_reg_class);
+ if (config->p2p_listen_channel)
+ fprintf(f, "p2p_listen_channel=%u\n",
+ config->p2p_listen_channel);
+ if (config->p2p_oper_reg_class)
+ fprintf(f, "p2p_oper_reg_class=%u\n",
+ config->p2p_oper_reg_class);
+ if (config->p2p_oper_channel)
+ fprintf(f, "p2p_oper_channel=%u\n", config->p2p_oper_channel);
+ if (config->p2p_go_intent != DEFAULT_P2P_GO_INTENT)
+ fprintf(f, "p2p_go_intent=%u\n", config->p2p_go_intent);
+ if (config->p2p_ssid_postfix)
+ fprintf(f, "p2p_ssid_postfix=%s\n", config->p2p_ssid_postfix);
+ if (config->persistent_reconnect)
+ fprintf(f, "persistent_reconnect=%u\n",
+ config->persistent_reconnect);
+ if (config->p2p_intra_bss != DEFAULT_P2P_INTRA_BSS)
+ fprintf(f, "p2p_intra_bss=%u\n", config->p2p_intra_bss);
+ if (config->p2p_group_idle)
+ fprintf(f, "p2p_group_idle=%u\n", config->p2p_group_idle);
+ if (config->p2p_pref_chan) {
+ unsigned int i;
+ fprintf(f, "p2p_pref_chan=");
+ for (i = 0; i < config->num_p2p_pref_chan; i++) {
+ fprintf(f, "%s%u:%u", i > 0 ? "," : "",
+ config->p2p_pref_chan[i].op_class,
+ config->p2p_pref_chan[i].chan);
+ }
+ fprintf(f, "\n");
+ }
+ if (config->p2p_go_ht40)
+ fprintf(f, "p2p_go_ht40=%u\n", config->p2p_go_ht40);
+ if (config->p2p_disabled)
+ fprintf(f, "p2p_disabled=%u\n", config->p2p_disabled);
+ if (config->p2p_no_group_iface)
+ fprintf(f, "p2p_no_group_iface=%u\n",
+ config->p2p_no_group_iface);
+#endif /* CONFIG_P2P */
if (config->country[0] && config->country[1]) {
fprintf(f, "country=%c%c\n",
config->country[0], config->country[1]);
}
if (config->bss_max_count != DEFAULT_BSS_MAX_COUNT)
fprintf(f, "bss_max_count=%u\n", config->bss_max_count);
+ if (config->bss_expiration_age != DEFAULT_BSS_EXPIRATION_AGE)
+ fprintf(f, "bss_expiration_age=%u\n",
+ config->bss_expiration_age);
+ if (config->bss_expiration_scan_count !=
+ DEFAULT_BSS_EXPIRATION_SCAN_COUNT)
+ fprintf(f, "bss_expiration_scan_count=%u\n",
+ config->bss_expiration_scan_count);
if (config->filter_ssids)
fprintf(f, "filter_ssids=%d\n", config->filter_ssids);
+ if (config->max_num_sta != DEFAULT_MAX_NUM_STA)
+ fprintf(f, "max_num_sta=%u\n", config->max_num_sta);
+ if (config->disassoc_low_ack)
+ fprintf(f, "disassoc_low_ack=%u\n", config->disassoc_low_ack);
+#ifdef CONFIG_HS20
+ if (config->hs20)
+ fprintf(f, "hs20=1\n");
+#endif /* CONFIG_HS20 */
+#ifdef CONFIG_INTERWORKING
+ if (config->interworking)
+ fprintf(f, "interworking=%u\n", config->interworking);
+ if (!is_zero_ether_addr(config->hessid))
+ fprintf(f, "hessid=" MACSTR "\n", MAC2STR(config->hessid));
+ if (config->access_network_type != DEFAULT_ACCESS_NETWORK_TYPE)
+ fprintf(f, "access_network_type=%d\n",
+ config->access_network_type);
+#endif /* CONFIG_INTERWORKING */
+ if (config->pbc_in_m1)
+ fprintf(f, "pbc_in_m1=%u\n", config->pbc_in_m1);
+ if (config->wps_nfc_dev_pw_id)
+ fprintf(f, "wps_nfc_dev_pw_id=%d\n",
+ config->wps_nfc_dev_pw_id);
+ write_global_bin(f, "wps_nfc_dh_pubkey", config->wps_nfc_dh_pubkey);
+ write_global_bin(f, "wps_nfc_dh_privkey", config->wps_nfc_dh_privkey);
+ write_global_bin(f, "wps_nfc_dev_pw", config->wps_nfc_dev_pw);
+
+ if (config->ext_password_backend)
+ fprintf(f, "ext_password_backend=%s\n",
+ config->ext_password_backend);
+ if (config->p2p_go_max_inactivity != DEFAULT_P2P_GO_MAX_INACTIVITY)
+ fprintf(f, "p2p_go_max_inactivity=%d\n",
+ config->p2p_go_max_inactivity);
+ if (config->auto_interworking)
+ fprintf(f, "auto_interworking=%d\n",
+ config->auto_interworking);
+ if (config->okc)
+ fprintf(f, "okc=%d\n", config->okc);
+ if (config->pmf)
+ fprintf(f, "pmf=%d\n", config->pmf);
}
#endif /* CONFIG_NO_CONFIG_WRITE */
@@ -905,6 +980,7 @@ int wpa_config_write(const char *name, struct wpa_config *config)
#ifndef CONFIG_NO_CONFIG_WRITE
FILE *f;
struct wpa_ssid *ssid;
+ struct wpa_cred *cred;
#ifndef CONFIG_NO_CONFIG_BLOBS
struct wpa_config_blob *blob;
#endif /* CONFIG_NO_CONFIG_BLOBS */
@@ -920,9 +996,18 @@ int wpa_config_write(const char *name, struct wpa_config *config)
wpa_config_write_global(f, config);
+ for (cred = config->cred; cred; cred = cred->next) {
+ fprintf(f, "\ncred={\n");
+ wpa_config_write_cred(f, cred);
+ fprintf(f, "}\n");
+ }
+
for (ssid = config->ssid; ssid; ssid = ssid->next) {
- if (ssid->key_mgmt == WPA_KEY_MGMT_WPS)
- continue; /* do not save temporary WPS networks */
+ if (ssid->key_mgmt == WPA_KEY_MGMT_WPS || ssid->temporary)
+ continue; /* do not save temporary networks */
+ if (wpa_key_mgmt_wpa_psk(ssid->key_mgmt) && !ssid->psk_set &&
+ !ssid->passphrase)
+ continue; /* do not save invalid network */
fprintf(f, "\nnetwork={\n");
wpa_config_write_network(f, ssid);
fprintf(f, "}\n");
diff --git a/contrib/wpa/wpa_supplicant/config_none.c b/contrib/wpa/wpa_supplicant/config_none.c
index 2e9ccc0..589ea36 100644
--- a/contrib/wpa/wpa_supplicant/config_none.c
+++ b/contrib/wpa/wpa_supplicant/config_none.c
@@ -2,14 +2,8 @@
* WPA Supplicant / Configuration backend: empty starting point
* Copyright (c) 2003-2005, Jouni Malinen <j@w1.fi>
*
- * This program is free software; you can redistribute it and/or modify
- * it under the terms of the GNU General Public License version 2 as
- * published by the Free Software Foundation.
- *
- * Alternatively, this software may be distributed under the terms of BSD
- * license.
- *
- * See README and COPYING for more details.
+ * This software may be distributed under the terms of the BSD license.
+ * See README for more details.
*
* This file implements dummy example of a configuration backend. None of the
* functions are actually implemented so this can be used as a simple
diff --git a/contrib/wpa/wpa_supplicant/config_ssid.h b/contrib/wpa/wpa_supplicant/config_ssid.h
index 25e87aa..9ac67c7 100644
--- a/contrib/wpa/wpa_supplicant/config_ssid.h
+++ b/contrib/wpa/wpa_supplicant/config_ssid.h
@@ -2,14 +2,8 @@
* WPA Supplicant / Network configuration structures
* Copyright (c) 2003-2008, Jouni Malinen <j@w1.fi>
*
- * This program is free software; you can redistribute it and/or modify
- * it under the terms of the GNU General Public License version 2 as
- * published by the Free Software Foundation.
- *
- * Alternatively, this software may be distributed under the terms of BSD
- * license.
- *
- * See README and COPYING for more details.
+ * This software may be distributed under the terms of the BSD license.
+ * See README for more details.
*/
#ifndef CONFIG_SSID_H
@@ -31,6 +25,14 @@
WPA_CIPHER_WEP104 | WPA_CIPHER_WEP40)
#define DEFAULT_FRAGMENT_SIZE 1398
+#define DEFAULT_BG_SCAN_PERIOD -1
+#define DEFAULT_DISABLE_HT 0
+#define DEFAULT_DISABLE_HT40 0
+#define DEFAULT_DISABLE_SGI 0
+#define DEFAULT_DISABLE_MAX_AMSDU -1 /* no change */
+#define DEFAULT_AMPDU_FACTOR -1 /* no change */
+#define DEFAULT_AMPDU_DENSITY -1 /* no change */
+
/**
* struct wpa_ssid - Network configuration data
*
@@ -109,6 +111,9 @@ struct wpa_ssid {
*
* If set, this network block is used only when associating with the AP
* using the configured BSSID
+ *
+ * If this is a persistent P2P group (disabled == 2), this is the GO
+ * Device Address.
*/
u8 bssid[ETH_ALEN];
@@ -137,6 +142,14 @@ struct wpa_ssid {
char *passphrase;
/**
+ * ext_psk - PSK/passphrase name in external storage
+ *
+ * If this is set, PSK/passphrase will be fetched from external storage
+ * when requesting association with the network.
+ */
+ char *ext_psk;
+
+ /**
* pairwise_cipher - Bitfield of allowed pairwise ciphers, WPA_CIPHER_*
*/
int pairwise_cipher;
@@ -154,6 +167,12 @@ struct wpa_ssid {
int key_mgmt;
/**
+ * bg_scan_period - Background scan period in seconds, 0 to disable, or
+ * -1 to indicate no change to default driver configuration
+ */
+ int bg_scan_period;
+
+ /**
* proto - Bitfield of allowed protocols, WPA_PROTO_*
*/
int proto;
@@ -210,13 +229,18 @@ struct wpa_ssid {
*
* This field can be used to enable proactive key caching which is also
* known as opportunistic PMKSA caching for WPA2. This is disabled (0)
- * by default. Enable by setting this to 1.
+ * by default unless default value is changed with the global okc=1
+ * parameter. Enable by setting this to 1.
*
* Proactive key caching is used to make supplicant assume that the APs
* are using the same PMK and generate PMKSA cache entries without
* doing RSN pre-authentication. This requires support from the AP side
* and is normally used with wireless switches that co-locate the
* authenticator.
+ *
+ * Internally, special value -1 is used to indicate that the parameter
+ * was not specified in the configuration (i.e., default behavior is
+ * followed).
*/
int proactive_key_caching;
@@ -273,6 +297,11 @@ struct wpa_ssid {
*
* 2 = AP (access point)
*
+ * 3 = P2P Group Owner (can be set in the configuration file)
+ *
+ * 4 = P2P Group Formation (used internally; not in configuration
+ * files)
+ *
* Note: IBSS can only be used with key_mgmt NONE (plaintext and
* static WEP) and key_mgmt=WPA-NONE (fixed group key TKIP/CCMP). In
* addition, ap_scan has to be set to 2 for IBSS. WPA-None requires
@@ -284,6 +313,8 @@ struct wpa_ssid {
WPAS_MODE_INFRA = 0,
WPAS_MODE_IBSS = 1,
WPAS_MODE_AP = 2,
+ WPAS_MODE_P2P_GO = 3,
+ WPAS_MODE_P2P_GROUP_FORMATION = 4,
} mode;
/**
@@ -292,10 +323,20 @@ struct wpa_ssid {
* 0 = this network can be used (default).
* 1 = this network block is disabled (can be enabled through
* ctrl_iface, e.g., with wpa_cli or wpa_gui).
+ * 2 = this network block includes parameters for a persistent P2P
+ * group (can be used with P2P ctrl_iface commands)
*/
int disabled;
/**
+ * disabled_for_connect - Whether this network was temporarily disabled
+ *
+ * This flag is used to reenable all the temporarily disabled networks
+ * after either the success or failure of a WPS connection.
+ */
+ int disabled_for_connect;
+
+ /**
* peerkey - Whether PeerKey handshake for direct links is allowed
*
* This is only used when both RSN/WPA2 and IEEE 802.11e (QoS) are
@@ -321,6 +362,12 @@ struct wpa_ssid {
*
* This value is used to configure policy for management frame
* protection (IEEE 802.11w). 0 = disabled, 1 = optional, 2 = required.
+ * This is disabled by default unless the default value has been changed
+ * with the global pmf=1/2 parameter.
+ *
+ * Internally, special value 3 is used to indicate that the parameter
+ * was not specified in the configuration (i.e., default behavior is
+ * followed).
*/
enum mfp_options ieee80211w;
#endif /* CONFIG_IEEE80211W */
@@ -337,6 +384,8 @@ struct wpa_ssid {
*/
int frequency;
+ int ht40;
+
/**
* wpa_ptk_rekey - Maximum lifetime for PTK in seconds
*
@@ -365,6 +414,20 @@ struct wpa_ssid {
char *bgscan;
/**
+ * ignore_broadcast_ssid - Hide SSID in AP mode
+ *
+ * Send empty SSID in beacons and ignore probe request frames that do
+ * not specify full SSID, i.e., require stations to know SSID.
+ * default: disabled (0)
+ * 1 = send empty (length=0) SSID in beacon and ignore probe request
+ * for broadcast SSID
+ * 2 = clear SSID (ASCII 0), but keep the original length (this may be
+ * required with some clients that do not support empty SSID) and
+ * ignore probe requests for broadcast SSID
+ */
+ int ignore_broadcast_ssid;
+
+ /**
* freq_list - Array of allowed frequencies or %NULL for all
*
* This is an optional zero-terminated array of frequencies in
@@ -373,6 +436,136 @@ struct wpa_ssid {
* considered when selecting a BSS.
*/
int *freq_list;
+
+ /**
+ * p2p_client_list - List of P2P Clients in a persistent group (GO)
+ *
+ * This is a list of P2P Clients (P2P Device Address) that have joined
+ * the persistent group. This is maintained on the GO for persistent
+ * group entries (disabled == 2).
+ */
+ u8 *p2p_client_list;
+
+ /**
+ * num_p2p_clients - Number of entries in p2p_client_list
+ */
+ size_t num_p2p_clients;
+
+#ifndef P2P_MAX_STORED_CLIENTS
+#define P2P_MAX_STORED_CLIENTS 100
+#endif /* P2P_MAX_STORED_CLIENTS */
+
+ /**
+ * p2p_group - Network generated as a P2P group (used internally)
+ */
+ int p2p_group;
+
+ /**
+ * p2p_persistent_group - Whether this is a persistent group
+ */
+ int p2p_persistent_group;
+
+ /**
+ * temporary - Whether this network is temporary and not to be saved
+ */
+ int temporary;
+
+ /**
+ * export_keys - Whether keys may be exported
+ *
+ * This attribute will be set when keys are determined through
+ * WPS or similar so that they may be exported.
+ */
+ int export_keys;
+
+#ifdef CONFIG_HT_OVERRIDES
+ /**
+ * disable_ht - Disable HT (IEEE 802.11n) for this network
+ *
+ * By default, use it if it is available, but this can be configured
+ * to 1 to have it disabled.
+ */
+ int disable_ht;
+
+ /**
+ * disable_ht40 - Disable HT40 for this network
+ *
+ * By default, use it if it is available, but this can be configured
+ * to 1 to have it disabled.
+ */
+ int disable_ht40;
+
+ /**
+ * disable_sgi - Disable SGI (Short Guard Interval) for this network
+ *
+ * By default, use it if it is available, but this can be configured
+ * to 1 to have it disabled.
+ */
+ int disable_sgi;
+
+ /**
+ * disable_max_amsdu - Disable MAX A-MSDU
+ *
+ * A-MDSU will be 3839 bytes when disabled, or 7935
+ * when enabled (assuming it is otherwise supported)
+ * -1 (default) means do not apply any settings to the kernel.
+ */
+ int disable_max_amsdu;
+
+ /**
+ * ampdu_factor - Maximum A-MPDU Length Exponent
+ *
+ * Value: 0-3, see 7.3.2.56.3 in IEEE Std 802.11n-2009.
+ */
+ int ampdu_factor;
+
+ /**
+ * ampdu_density - Minimum A-MPDU Start Spacing
+ *
+ * Value: 0-7, see 7.3.2.56.3 in IEEE Std 802.11n-2009.
+ */
+ int ampdu_density;
+
+ /**
+ * ht_mcs - Allowed HT-MCS rates, in ASCII hex: ffff0000...
+ *
+ * By default (empty string): Use whatever the OS has configured.
+ */
+ char *ht_mcs;
+#endif /* CONFIG_HT_OVERRIDES */
+
+ /**
+ * ap_max_inactivity - Timeout in seconds to detect STA's inactivity
+ *
+ * This timeout value is used in AP mode to clean up inactive stations.
+ * By default: 300 seconds.
+ */
+ int ap_max_inactivity;
+
+ /**
+ * dtim_period - DTIM period in Beacon intervals
+ * By default: 2
+ */
+ int dtim_period;
+
+ /**
+ * auth_failures - Number of consecutive authentication failures
+ */
+ unsigned int auth_failures;
+
+ /**
+ * disabled_until - Network block disabled until this time if non-zero
+ */
+ struct os_time disabled_until;
+
+ /**
+ * parent_cred - Pointer to parent wpa_cred entry
+ *
+ * This pointer can be used to delete temporary networks when a wpa_cred
+ * that was used to create them is removed. This pointer should not be
+ * dereferences since it may not be updated in all cases.
+ */
+ void *parent_cred;
};
#endif /* CONFIG_SSID_H */
diff --git a/contrib/wpa/wpa_supplicant/ctrl_iface.c b/contrib/wpa/wpa_supplicant/ctrl_iface.c
index 19fea29..864dd7d 100644
--- a/contrib/wpa/wpa_supplicant/ctrl_iface.c
+++ b/contrib/wpa/wpa_supplicant/ctrl_iface.c
@@ -1,22 +1,18 @@
/*
* WPA Supplicant / Control interface (shared code for all backends)
- * Copyright (c) 2004-2010, Jouni Malinen <j@w1.fi>
+ * Copyright (c) 2004-2012, Jouni Malinen <j@w1.fi>
*
- * This program is free software; you can redistribute it and/or modify
- * it under the terms of the GNU General Public License version 2 as
- * published by the Free Software Foundation.
- *
- * Alternatively, this software may be distributed under the terms of BSD
- * license.
- *
- * See README and COPYING for more details.
+ * This software may be distributed under the terms of the BSD license.
+ * See README for more details.
*/
#include "utils/includes.h"
#include "utils/common.h"
#include "utils/eloop.h"
+#include "common/version.h"
#include "common/ieee802_11_defs.h"
+#include "common/ieee802_11_common.h"
#include "common/wpa_ctrl.h"
#include "eap_peer/eap.h"
#include "eapol_supp/eapol_supp_sm.h"
@@ -31,10 +27,18 @@
#include "wps_supplicant.h"
#include "ibss_rsn.h"
#include "ap.h"
+#include "p2p_supplicant.h"
+#include "p2p/p2p.h"
+#include "hs20_supplicant.h"
+#include "wifi_display.h"
#include "notify.h"
#include "bss.h"
#include "scan.h"
#include "ctrl_iface.h"
+#include "interworking.h"
+#include "blacklist.h"
+#include "autoscan.h"
+#include "wnm_sta.h"
extern struct wpa_driver_ops *wpa_drivers[];
@@ -44,6 +48,249 @@ static int wpa_supplicant_global_iface_interfaces(struct wpa_global *global,
char *buf, int len);
+static int pno_start(struct wpa_supplicant *wpa_s)
+{
+ int ret;
+ size_t i, num_ssid;
+ struct wpa_ssid *ssid;
+ struct wpa_driver_scan_params params;
+
+ if (wpa_s->pno)
+ return 0;
+
+ if (wpa_s->wpa_state == WPA_SCANNING) {
+ wpa_supplicant_cancel_sched_scan(wpa_s);
+ wpa_supplicant_cancel_scan(wpa_s);
+ }
+
+ os_memset(&params, 0, sizeof(params));
+
+ num_ssid = 0;
+ ssid = wpa_s->conf->ssid;
+ while (ssid) {
+ if (!wpas_network_disabled(wpa_s, ssid))
+ num_ssid++;
+ ssid = ssid->next;
+ }
+ if (num_ssid > WPAS_MAX_SCAN_SSIDS) {
+ wpa_printf(MSG_DEBUG, "PNO: Use only the first %u SSIDs from "
+ "%u", WPAS_MAX_SCAN_SSIDS, (unsigned int) num_ssid);
+ num_ssid = WPAS_MAX_SCAN_SSIDS;
+ }
+
+ if (num_ssid == 0) {
+ wpa_printf(MSG_DEBUG, "PNO: No configured SSIDs");
+ return -1;
+ }
+
+ params.filter_ssids = os_malloc(sizeof(struct wpa_driver_scan_filter) *
+ num_ssid);
+ if (params.filter_ssids == NULL)
+ return -1;
+ i = 0;
+ ssid = wpa_s->conf->ssid;
+ while (ssid) {
+ if (!wpas_network_disabled(wpa_s, ssid)) {
+ params.ssids[i].ssid = ssid->ssid;
+ params.ssids[i].ssid_len = ssid->ssid_len;
+ params.num_ssids++;
+ os_memcpy(params.filter_ssids[i].ssid, ssid->ssid,
+ ssid->ssid_len);
+ params.filter_ssids[i].ssid_len = ssid->ssid_len;
+ params.num_filter_ssids++;
+ i++;
+ if (i == num_ssid)
+ break;
+ }
+ ssid = ssid->next;
+ }
+
+ if (wpa_s->conf->filter_rssi)
+ params.filter_rssi = wpa_s->conf->filter_rssi;
+
+ ret = wpa_drv_sched_scan(wpa_s, &params, 10 * 1000);
+ os_free(params.filter_ssids);
+ if (ret == 0)
+ wpa_s->pno = 1;
+ return ret;
+}
+
+
+static int pno_stop(struct wpa_supplicant *wpa_s)
+{
+ int ret = 0;
+
+ if (wpa_s->pno) {
+ wpa_s->pno = 0;
+ ret = wpa_drv_stop_sched_scan(wpa_s);
+ }
+
+ if (wpa_s->wpa_state == WPA_SCANNING)
+ wpa_supplicant_req_scan(wpa_s, 0, 0);
+
+ return ret;
+}
+
+
+static int set_bssid_filter(struct wpa_supplicant *wpa_s, char *val)
+{
+ char *pos;
+ u8 addr[ETH_ALEN], *filter = NULL, *n;
+ size_t count = 0;
+
+ pos = val;
+ while (pos) {
+ if (*pos == '\0')
+ break;
+ if (hwaddr_aton(pos, addr)) {
+ os_free(filter);
+ return -1;
+ }
+ n = os_realloc_array(filter, count + 1, ETH_ALEN);
+ if (n == NULL) {
+ os_free(filter);
+ return -1;
+ }
+ filter = n;
+ os_memcpy(filter + count * ETH_ALEN, addr, ETH_ALEN);
+ count++;
+
+ pos = os_strchr(pos, ' ');
+ if (pos)
+ pos++;
+ }
+
+ wpa_hexdump(MSG_DEBUG, "bssid_filter", filter, count * ETH_ALEN);
+ os_free(wpa_s->bssid_filter);
+ wpa_s->bssid_filter = filter;
+ wpa_s->bssid_filter_count = count;
+
+ return 0;
+}
+
+
+static int set_disallow_aps(struct wpa_supplicant *wpa_s, char *val)
+{
+ char *pos;
+ u8 addr[ETH_ALEN], *bssid = NULL, *n;
+ struct wpa_ssid_value *ssid = NULL, *ns;
+ size_t count = 0, ssid_count = 0;
+ struct wpa_ssid *c;
+
+ /*
+ * disallow_list ::= <ssid_spec> | <bssid_spec> | <disallow_list> | “”
+ * SSID_SPEC ::= ssid <SSID_HEX>
+ * BSSID_SPEC ::= bssid <BSSID_HEX>
+ */
+
+ pos = val;
+ while (pos) {
+ if (*pos == '\0')
+ break;
+ if (os_strncmp(pos, "bssid ", 6) == 0) {
+ int res;
+ pos += 6;
+ res = hwaddr_aton2(pos, addr);
+ if (res < 0) {
+ os_free(ssid);
+ os_free(bssid);
+ wpa_printf(MSG_DEBUG, "Invalid disallow_aps "
+ "BSSID value '%s'", pos);
+ return -1;
+ }
+ pos += res;
+ n = os_realloc_array(bssid, count + 1, ETH_ALEN);
+ if (n == NULL) {
+ os_free(ssid);
+ os_free(bssid);
+ return -1;
+ }
+ bssid = n;
+ os_memcpy(bssid + count * ETH_ALEN, addr, ETH_ALEN);
+ count++;
+ } else if (os_strncmp(pos, "ssid ", 5) == 0) {
+ char *end;
+ pos += 5;
+
+ end = pos;
+ while (*end) {
+ if (*end == '\0' || *end == ' ')
+ break;
+ end++;
+ }
+
+ ns = os_realloc_array(ssid, ssid_count + 1,
+ sizeof(struct wpa_ssid_value));
+ if (ns == NULL) {
+ os_free(ssid);
+ os_free(bssid);
+ return -1;
+ }
+ ssid = ns;
+
+ if ((end - pos) & 0x01 || end - pos > 2 * 32 ||
+ hexstr2bin(pos, ssid[ssid_count].ssid,
+ (end - pos) / 2) < 0) {
+ os_free(ssid);
+ os_free(bssid);
+ wpa_printf(MSG_DEBUG, "Invalid disallow_aps "
+ "SSID value '%s'", pos);
+ return -1;
+ }
+ ssid[ssid_count].ssid_len = (end - pos) / 2;
+ wpa_hexdump_ascii(MSG_DEBUG, "disallow_aps SSID",
+ ssid[ssid_count].ssid,
+ ssid[ssid_count].ssid_len);
+ ssid_count++;
+ pos = end;
+ } else {
+ wpa_printf(MSG_DEBUG, "Unexpected disallow_aps value "
+ "'%s'", pos);
+ os_free(ssid);
+ os_free(bssid);
+ return -1;
+ }
+
+ pos = os_strchr(pos, ' ');
+ if (pos)
+ pos++;
+ }
+
+ wpa_hexdump(MSG_DEBUG, "disallow_aps_bssid", bssid, count * ETH_ALEN);
+ os_free(wpa_s->disallow_aps_bssid);
+ wpa_s->disallow_aps_bssid = bssid;
+ wpa_s->disallow_aps_bssid_count = count;
+
+ wpa_printf(MSG_DEBUG, "disallow_aps_ssid_count %d", (int) ssid_count);
+ os_free(wpa_s->disallow_aps_ssid);
+ wpa_s->disallow_aps_ssid = ssid;
+ wpa_s->disallow_aps_ssid_count = ssid_count;
+
+ if (!wpa_s->current_ssid || wpa_s->wpa_state < WPA_AUTHENTICATING)
+ return 0;
+
+ c = wpa_s->current_ssid;
+ if (c->mode != WPAS_MODE_INFRA && c->mode != WPAS_MODE_IBSS)
+ return 0;
+
+ if (!disallowed_bssid(wpa_s, wpa_s->bssid) &&
+ !disallowed_ssid(wpa_s, c->ssid, c->ssid_len))
+ return 0;
+
+ wpa_printf(MSG_DEBUG, "Disconnect and try to find another network "
+ "because current AP was marked disallowed");
+
+#ifdef CONFIG_SME
+ wpa_s->sme.prev_bssid_set = 0;
+#endif /* CONFIG_SME */
+ wpa_s->reassociate = 1;
+ wpa_supplicant_deauthenticate(wpa_s, WLAN_REASON_DEAUTH_LEAVING);
+ wpa_supplicant_req_scan(wpa_s, 0, 0);
+
+ return 0;
+}
+
+
static int wpa_supplicant_ctrl_iface_set(struct wpa_supplicant *wpa_s,
char *cmd)
{
@@ -80,13 +327,148 @@ static int wpa_supplicant_ctrl_iface_set(struct wpa_supplicant *wpa_s,
} else if (os_strcasecmp(cmd, "dot11RSNAConfigSATimeout") == 0) {
if (wpa_sm_set_param(wpa_s->wpa, RSNA_SA_TIMEOUT, atoi(value)))
ret = -1;
- } else
- ret = -1;
+ } else if (os_strcasecmp(cmd, "wps_fragment_size") == 0) {
+ wpa_s->wps_fragment_size = atoi(value);
+#ifdef CONFIG_WPS_TESTING
+ } else if (os_strcasecmp(cmd, "wps_version_number") == 0) {
+ long int val;
+ val = strtol(value, NULL, 0);
+ if (val < 0 || val > 0xff) {
+ ret = -1;
+ wpa_printf(MSG_DEBUG, "WPS: Invalid "
+ "wps_version_number %ld", val);
+ } else {
+ wps_version_number = val;
+ wpa_printf(MSG_DEBUG, "WPS: Testing - force WPS "
+ "version %u.%u",
+ (wps_version_number & 0xf0) >> 4,
+ wps_version_number & 0x0f);
+ }
+ } else if (os_strcasecmp(cmd, "wps_testing_dummy_cred") == 0) {
+ wps_testing_dummy_cred = atoi(value);
+ wpa_printf(MSG_DEBUG, "WPS: Testing - dummy_cred=%d",
+ wps_testing_dummy_cred);
+#endif /* CONFIG_WPS_TESTING */
+ } else if (os_strcasecmp(cmd, "ampdu") == 0) {
+ if (wpa_drv_ampdu(wpa_s, atoi(value)) < 0)
+ ret = -1;
+#ifdef CONFIG_TDLS_TESTING
+ } else if (os_strcasecmp(cmd, "tdls_testing") == 0) {
+ extern unsigned int tdls_testing;
+ tdls_testing = strtol(value, NULL, 0);
+ wpa_printf(MSG_DEBUG, "TDLS: tdls_testing=0x%x", tdls_testing);
+#endif /* CONFIG_TDLS_TESTING */
+#ifdef CONFIG_TDLS
+ } else if (os_strcasecmp(cmd, "tdls_disabled") == 0) {
+ int disabled = atoi(value);
+ wpa_printf(MSG_DEBUG, "TDLS: tdls_disabled=%d", disabled);
+ if (disabled) {
+ if (wpa_drv_tdls_oper(wpa_s, TDLS_DISABLE, NULL) < 0)
+ ret = -1;
+ } else if (wpa_drv_tdls_oper(wpa_s, TDLS_ENABLE, NULL) < 0)
+ ret = -1;
+ wpa_tdls_enable(wpa_s->wpa, !disabled);
+#endif /* CONFIG_TDLS */
+ } else if (os_strcasecmp(cmd, "pno") == 0) {
+ if (atoi(value))
+ ret = pno_start(wpa_s);
+ else
+ ret = pno_stop(wpa_s);
+ } else if (os_strcasecmp(cmd, "radio_disabled") == 0) {
+ int disabled = atoi(value);
+ if (wpa_drv_radio_disable(wpa_s, disabled) < 0)
+ ret = -1;
+ else if (disabled)
+ wpa_supplicant_set_state(wpa_s, WPA_INACTIVE);
+ } else if (os_strcasecmp(cmd, "uapsd") == 0) {
+ if (os_strcmp(value, "disable") == 0)
+ wpa_s->set_sta_uapsd = 0;
+ else {
+ int be, bk, vi, vo;
+ char *pos;
+ /* format: BE,BK,VI,VO;max SP Length */
+ be = atoi(value);
+ pos = os_strchr(value, ',');
+ if (pos == NULL)
+ return -1;
+ pos++;
+ bk = atoi(pos);
+ pos = os_strchr(pos, ',');
+ if (pos == NULL)
+ return -1;
+ pos++;
+ vi = atoi(pos);
+ pos = os_strchr(pos, ',');
+ if (pos == NULL)
+ return -1;
+ pos++;
+ vo = atoi(pos);
+ /* ignore max SP Length for now */
+
+ wpa_s->set_sta_uapsd = 1;
+ wpa_s->sta_uapsd = 0;
+ if (be)
+ wpa_s->sta_uapsd |= BIT(0);
+ if (bk)
+ wpa_s->sta_uapsd |= BIT(1);
+ if (vi)
+ wpa_s->sta_uapsd |= BIT(2);
+ if (vo)
+ wpa_s->sta_uapsd |= BIT(3);
+ }
+ } else if (os_strcasecmp(cmd, "ps") == 0) {
+ ret = wpa_drv_set_p2p_powersave(wpa_s, atoi(value), -1, -1);
+#ifdef CONFIG_WIFI_DISPLAY
+ } else if (os_strcasecmp(cmd, "wifi_display") == 0) {
+ wifi_display_enable(wpa_s->global, !!atoi(value));
+#endif /* CONFIG_WIFI_DISPLAY */
+ } else if (os_strcasecmp(cmd, "bssid_filter") == 0) {
+ ret = set_bssid_filter(wpa_s, value);
+ } else if (os_strcasecmp(cmd, "disallow_aps") == 0) {
+ ret = set_disallow_aps(wpa_s, value);
+ } else if (os_strcasecmp(cmd, "no_keep_alive") == 0) {
+ wpa_s->no_keep_alive = !!atoi(value);
+ } else {
+ value[-1] = '=';
+ ret = wpa_config_process_global(wpa_s->conf, cmd, -1);
+ if (ret == 0)
+ wpa_supplicant_update_config(wpa_s);
+ }
return ret;
}
+static int wpa_supplicant_ctrl_iface_get(struct wpa_supplicant *wpa_s,
+ char *cmd, char *buf, size_t buflen)
+{
+ int res = -1;
+
+ wpa_printf(MSG_DEBUG, "CTRL_IFACE GET '%s'", cmd);
+
+ if (os_strcmp(cmd, "version") == 0) {
+ res = os_snprintf(buf, buflen, "%s", VERSION_STR);
+ } else if (os_strcasecmp(cmd, "country") == 0) {
+ if (wpa_s->conf->country[0] && wpa_s->conf->country[1])
+ res = os_snprintf(buf, buflen, "%c%c",
+ wpa_s->conf->country[0],
+ wpa_s->conf->country[1]);
+#ifdef CONFIG_WIFI_DISPLAY
+ } else if (os_strcasecmp(cmd, "wifi_display") == 0) {
+ res = os_snprintf(buf, buflen, "%d",
+ wpa_s->global->wifi_display);
+ if (res < 0 || (unsigned int) res >= buflen)
+ return -1;
+ return res;
+#endif /* CONFIG_WIFI_DISPLAY */
+ }
+
+ if (res < 0 || (unsigned int) res >= buflen)
+ return -1;
+ return res;
+}
+
+
#ifdef IEEE8021X_EAPOL
static int wpa_supplicant_ctrl_iface_preauth(struct wpa_supplicant *wpa_s,
char *addr)
@@ -131,6 +513,80 @@ static int wpa_supplicant_ctrl_iface_stkstart(
#endif /* CONFIG_PEERKEY */
+#ifdef CONFIG_TDLS
+
+static int wpa_supplicant_ctrl_iface_tdls_discover(
+ struct wpa_supplicant *wpa_s, char *addr)
+{
+ u8 peer[ETH_ALEN];
+ int ret;
+
+ if (hwaddr_aton(addr, peer)) {
+ wpa_printf(MSG_DEBUG, "CTRL_IFACE TDLS_DISCOVER: invalid "
+ "address '%s'", addr);
+ return -1;
+ }
+
+ wpa_printf(MSG_DEBUG, "CTRL_IFACE TDLS_DISCOVER " MACSTR,
+ MAC2STR(peer));
+
+ if (wpa_tdls_is_external_setup(wpa_s->wpa))
+ ret = wpa_tdls_send_discovery_request(wpa_s->wpa, peer);
+ else
+ ret = wpa_drv_tdls_oper(wpa_s, TDLS_DISCOVERY_REQ, peer);
+
+ return ret;
+}
+
+
+static int wpa_supplicant_ctrl_iface_tdls_setup(
+ struct wpa_supplicant *wpa_s, char *addr)
+{
+ u8 peer[ETH_ALEN];
+ int ret;
+
+ if (hwaddr_aton(addr, peer)) {
+ wpa_printf(MSG_DEBUG, "CTRL_IFACE TDLS_SETUP: invalid "
+ "address '%s'", addr);
+ return -1;
+ }
+
+ wpa_printf(MSG_DEBUG, "CTRL_IFACE TDLS_SETUP " MACSTR,
+ MAC2STR(peer));
+
+ ret = wpa_tdls_reneg(wpa_s->wpa, peer);
+ if (ret) {
+ if (wpa_tdls_is_external_setup(wpa_s->wpa))
+ ret = wpa_tdls_start(wpa_s->wpa, peer);
+ else
+ ret = wpa_drv_tdls_oper(wpa_s, TDLS_SETUP, peer);
+ }
+
+ return ret;
+}
+
+
+static int wpa_supplicant_ctrl_iface_tdls_teardown(
+ struct wpa_supplicant *wpa_s, char *addr)
+{
+ u8 peer[ETH_ALEN];
+
+ if (hwaddr_aton(addr, peer)) {
+ wpa_printf(MSG_DEBUG, "CTRL_IFACE TDLS_TEARDOWN: invalid "
+ "address '%s'", addr);
+ return -1;
+ }
+
+ wpa_printf(MSG_DEBUG, "CTRL_IFACE TDLS_TEARDOWN " MACSTR,
+ MAC2STR(peer));
+
+ return wpa_tdls_teardown_link(wpa_s->wpa, peer,
+ WLAN_REASON_TDLS_TEARDOWN_UNSPECIFIED);
+}
+
+#endif /* CONFIG_TDLS */
+
+
#ifdef CONFIG_IEEE80211R
static int wpa_supplicant_ctrl_iface_ft_ds(
struct wpa_supplicant *wpa_s, char *addr)
@@ -163,10 +619,26 @@ static int wpa_supplicant_ctrl_iface_wps_pbc(struct wpa_supplicant *wpa_s,
char *cmd)
{
u8 bssid[ETH_ALEN], *_bssid = bssid;
+#ifdef CONFIG_P2P
+ u8 p2p_dev_addr[ETH_ALEN];
+#endif /* CONFIG_P2P */
+#ifdef CONFIG_AP
+ u8 *_p2p_dev_addr = NULL;
+#endif /* CONFIG_AP */
- if (cmd == NULL || os_strcmp(cmd, "any") == 0)
+ if (cmd == NULL || os_strcmp(cmd, "any") == 0) {
_bssid = NULL;
- else if (hwaddr_aton(cmd, bssid)) {
+#ifdef CONFIG_P2P
+ } else if (os_strncmp(cmd, "p2p_dev_addr=", 13) == 0) {
+ if (hwaddr_aton(cmd + 13, p2p_dev_addr)) {
+ wpa_printf(MSG_DEBUG, "CTRL_IFACE WPS_PBC: invalid "
+ "P2P Device Address '%s'",
+ cmd + 13);
+ return -1;
+ }
+ _p2p_dev_addr = p2p_dev_addr;
+#endif /* CONFIG_P2P */
+ } else if (hwaddr_aton(cmd, bssid)) {
wpa_printf(MSG_DEBUG, "CTRL_IFACE WPS_PBC: invalid BSSID '%s'",
cmd);
return -1;
@@ -174,10 +646,10 @@ static int wpa_supplicant_ctrl_iface_wps_pbc(struct wpa_supplicant *wpa_s,
#ifdef CONFIG_AP
if (wpa_s->ap_iface)
- return wpa_supplicant_ap_wps_pbc(wpa_s, _bssid);
+ return wpa_supplicant_ap_wps_pbc(wpa_s, _bssid, _p2p_dev_addr);
#endif /* CONFIG_AP */
- return wpas_wps_start_pbc(wpa_s, _bssid);
+ return wpas_wps_start_pbc(wpa_s, _bssid, 0);
}
@@ -195,20 +667,36 @@ static int wpa_supplicant_ctrl_iface_wps_pin(struct wpa_supplicant *wpa_s,
if (os_strcmp(cmd, "any") == 0)
_bssid = NULL;
- else if (hwaddr_aton(cmd, bssid)) {
+ else if (os_strcmp(cmd, "get") == 0) {
+ ret = wps_generate_pin();
+ goto done;
+ } else if (hwaddr_aton(cmd, bssid)) {
wpa_printf(MSG_DEBUG, "CTRL_IFACE WPS_PIN: invalid BSSID '%s'",
cmd);
return -1;
}
#ifdef CONFIG_AP
- if (wpa_s->ap_iface)
+ if (wpa_s->ap_iface) {
+ int timeout = 0;
+ char *pos;
+
+ if (pin) {
+ pos = os_strchr(pin, ' ');
+ if (pos) {
+ *pos++ = '\0';
+ timeout = atoi(pos);
+ }
+ }
+
return wpa_supplicant_ap_wps_pin(wpa_s, _bssid, pin,
- buf, buflen);
+ buf, buflen, timeout);
+ }
#endif /* CONFIG_AP */
if (pin) {
- ret = wpas_wps_start_pin(wpa_s, _bssid, pin);
+ ret = wpas_wps_start_pin(wpa_s, _bssid, pin, 0,
+ DEV_PW_DEFAULT);
if (ret < 0)
return -1;
ret = os_snprintf(buf, buflen, "%s", pin);
@@ -217,10 +705,11 @@ static int wpa_supplicant_ctrl_iface_wps_pin(struct wpa_supplicant *wpa_s,
return ret;
}
- ret = wpas_wps_start_pin(wpa_s, _bssid, NULL);
+ ret = wpas_wps_start_pin(wpa_s, _bssid, NULL, 0, DEV_PW_DEFAULT);
if (ret < 0)
return -1;
+done:
/* Return the generated PIN */
ret = os_snprintf(buf, buflen, "%08d", ret);
if (ret < 0 || (size_t) ret >= buflen)
@@ -229,35 +718,272 @@ static int wpa_supplicant_ctrl_iface_wps_pin(struct wpa_supplicant *wpa_s,
}
-#ifdef CONFIG_WPS_OOB
-static int wpa_supplicant_ctrl_iface_wps_oob(struct wpa_supplicant *wpa_s,
+static int wpa_supplicant_ctrl_iface_wps_check_pin(
+ struct wpa_supplicant *wpa_s, char *cmd, char *buf, size_t buflen)
+{
+ char pin[9];
+ size_t len;
+ char *pos;
+ int ret;
+
+ wpa_hexdump_ascii_key(MSG_DEBUG, "WPS_CHECK_PIN",
+ (u8 *) cmd, os_strlen(cmd));
+ for (pos = cmd, len = 0; *pos != '\0'; pos++) {
+ if (*pos < '0' || *pos > '9')
+ continue;
+ pin[len++] = *pos;
+ if (len == 9) {
+ wpa_printf(MSG_DEBUG, "WPS: Too long PIN");
+ return -1;
+ }
+ }
+ if (len != 4 && len != 8) {
+ wpa_printf(MSG_DEBUG, "WPS: Invalid PIN length %d", (int) len);
+ return -1;
+ }
+ pin[len] = '\0';
+
+ if (len == 8) {
+ unsigned int pin_val;
+ pin_val = atoi(pin);
+ if (!wps_pin_valid(pin_val)) {
+ wpa_printf(MSG_DEBUG, "WPS: Invalid checksum digit");
+ ret = os_snprintf(buf, buflen, "FAIL-CHECKSUM\n");
+ if (ret < 0 || (size_t) ret >= buflen)
+ return -1;
+ return ret;
+ }
+ }
+
+ ret = os_snprintf(buf, buflen, "%s", pin);
+ if (ret < 0 || (size_t) ret >= buflen)
+ return -1;
+
+ return ret;
+}
+
+
+#ifdef CONFIG_WPS_NFC
+
+static int wpa_supplicant_ctrl_iface_wps_nfc(struct wpa_supplicant *wpa_s,
char *cmd)
{
- char *path, *method, *name;
+ u8 bssid[ETH_ALEN], *_bssid = bssid;
+
+ if (cmd == NULL || cmd[0] == '\0')
+ _bssid = NULL;
+ else if (hwaddr_aton(cmd, bssid))
+ return -1;
+
+ return wpas_wps_start_nfc(wpa_s, _bssid);
+}
+
+
+static int wpa_supplicant_ctrl_iface_wps_nfc_token(
+ struct wpa_supplicant *wpa_s, char *cmd, char *reply, size_t max_len)
+{
+ int ndef;
+ struct wpabuf *buf;
+ int res;
+
+ if (os_strcmp(cmd, "WPS") == 0)
+ ndef = 0;
+ else if (os_strcmp(cmd, "NDEF") == 0)
+ ndef = 1;
+ else
+ return -1;
+
+ buf = wpas_wps_nfc_token(wpa_s, ndef);
+ if (buf == NULL)
+ return -1;
+
+ res = wpa_snprintf_hex_uppercase(reply, max_len, wpabuf_head(buf),
+ wpabuf_len(buf));
+ reply[res++] = '\n';
+ reply[res] = '\0';
+
+ wpabuf_free(buf);
+
+ return res;
+}
+
+
+static int wpa_supplicant_ctrl_iface_wps_nfc_tag_read(
+ struct wpa_supplicant *wpa_s, char *pos)
+{
+ size_t len;
+ struct wpabuf *buf;
+ int ret;
+
+ len = os_strlen(pos);
+ if (len & 0x01)
+ return -1;
+ len /= 2;
+
+ buf = wpabuf_alloc(len);
+ if (buf == NULL)
+ return -1;
+ if (hexstr2bin(pos, wpabuf_put(buf, len), len) < 0) {
+ wpabuf_free(buf);
+ return -1;
+ }
+
+ ret = wpas_wps_nfc_tag_read(wpa_s, buf);
+ wpabuf_free(buf);
+
+ return ret;
+}
+
+
+static int wpas_ctrl_nfc_get_handover_req_wps(struct wpa_supplicant *wpa_s,
+ char *reply, size_t max_len)
+{
+ struct wpabuf *buf;
+ int res;
+
+ buf = wpas_wps_nfc_handover_req(wpa_s);
+ if (buf == NULL)
+ return -1;
+
+ res = wpa_snprintf_hex_uppercase(reply, max_len, wpabuf_head(buf),
+ wpabuf_len(buf));
+ reply[res++] = '\n';
+ reply[res] = '\0';
+
+ wpabuf_free(buf);
+
+ return res;
+}
+
+
+static int wpas_ctrl_nfc_get_handover_req(struct wpa_supplicant *wpa_s,
+ char *cmd, char *reply,
+ size_t max_len)
+{
+ char *pos;
+
+ pos = os_strchr(cmd, ' ');
+ if (pos == NULL)
+ return -1;
+ *pos++ = '\0';
- path = os_strchr(cmd, ' ');
- if (path == NULL)
+ if (os_strcmp(cmd, "NDEF") != 0)
return -1;
- *path++ = '\0';
- method = os_strchr(path, ' ');
- if (method == NULL)
+ if (os_strcmp(pos, "WPS") == 0) {
+ return wpas_ctrl_nfc_get_handover_req_wps(wpa_s, reply,
+ max_len);
+ }
+
+ return -1;
+}
+
+
+static int wpas_ctrl_nfc_get_handover_sel_wps(struct wpa_supplicant *wpa_s,
+ char *reply, size_t max_len)
+{
+ struct wpabuf *buf;
+ int res;
+
+ buf = wpas_wps_nfc_handover_sel(wpa_s);
+ if (buf == NULL)
return -1;
- *method++ = '\0';
- name = os_strchr(method, ' ');
- if (name != NULL)
- *name++ = '\0';
+ res = wpa_snprintf_hex_uppercase(reply, max_len, wpabuf_head(buf),
+ wpabuf_len(buf));
+ reply[res++] = '\n';
+ reply[res] = '\0';
- return wpas_wps_start_oob(wpa_s, cmd, path, method, name);
+ wpabuf_free(buf);
+
+ return res;
}
-#endif /* CONFIG_WPS_OOB */
+
+
+static int wpas_ctrl_nfc_get_handover_sel(struct wpa_supplicant *wpa_s,
+ char *cmd, char *reply,
+ size_t max_len)
+{
+ char *pos;
+
+ pos = os_strchr(cmd, ' ');
+ if (pos == NULL)
+ return -1;
+ *pos++ = '\0';
+
+ if (os_strcmp(cmd, "NDEF") != 0)
+ return -1;
+
+ if (os_strcmp(pos, "WPS") == 0) {
+ return wpas_ctrl_nfc_get_handover_sel_wps(wpa_s, reply,
+ max_len);
+ }
+
+ return -1;
+}
+
+
+static int wpas_ctrl_nfc_rx_handover_req(struct wpa_supplicant *wpa_s,
+ char *cmd, char *reply,
+ size_t max_len)
+{
+ size_t len;
+ struct wpabuf *buf;
+ int ret;
+
+ len = os_strlen(cmd);
+ if (len & 0x01)
+ return -1;
+ len /= 2;
+
+ buf = wpabuf_alloc(len);
+ if (buf == NULL)
+ return -1;
+ if (hexstr2bin(cmd, wpabuf_put(buf, len), len) < 0) {
+ wpabuf_free(buf);
+ return -1;
+ }
+
+ ret = wpas_wps_nfc_rx_handover_req(wpa_s, buf);
+ wpabuf_free(buf);
+
+ return ret;
+}
+
+
+static int wpas_ctrl_nfc_rx_handover_sel(struct wpa_supplicant *wpa_s,
+ char *cmd)
+{
+ size_t len;
+ struct wpabuf *buf;
+ int ret;
+
+ len = os_strlen(cmd);
+ if (len & 0x01)
+ return -1;
+ len /= 2;
+
+ buf = wpabuf_alloc(len);
+ if (buf == NULL)
+ return -1;
+ if (hexstr2bin(cmd, wpabuf_put(buf, len), len) < 0) {
+ wpabuf_free(buf);
+ return -1;
+ }
+
+ ret = wpas_wps_nfc_rx_handover_sel(wpa_s, buf);
+ wpabuf_free(buf);
+
+ return ret;
+}
+
+#endif /* CONFIG_WPS_NFC */
static int wpa_supplicant_ctrl_iface_wps_reg(struct wpa_supplicant *wpa_s,
char *cmd)
{
- u8 bssid[ETH_ALEN], *_bssid = bssid;
+ u8 bssid[ETH_ALEN];
char *pin;
char *new_ssid;
char *new_auth;
@@ -270,9 +996,7 @@ static int wpa_supplicant_ctrl_iface_wps_reg(struct wpa_supplicant *wpa_s,
return -1;
*pin++ = '\0';
- if (os_strcmp(cmd, "any") == 0)
- _bssid = NULL;
- else if (hwaddr_aton(cmd, bssid)) {
+ if (hwaddr_aton(cmd, bssid)) {
wpa_printf(MSG_DEBUG, "CTRL_IFACE WPS_REG: invalid BSSID '%s'",
cmd);
return -1;
@@ -280,7 +1004,7 @@ static int wpa_supplicant_ctrl_iface_wps_reg(struct wpa_supplicant *wpa_s,
new_ssid = os_strchr(pin, ' ');
if (new_ssid == NULL)
- return wpas_wps_start_reg(wpa_s, _bssid, pin, NULL);
+ return wpas_wps_start_reg(wpa_s, bssid, pin, NULL);
*new_ssid++ = '\0';
new_auth = os_strchr(new_ssid, ' ');
@@ -303,20 +1027,86 @@ static int wpa_supplicant_ctrl_iface_wps_reg(struct wpa_supplicant *wpa_s,
ap.auth = new_auth;
ap.encr = new_encr;
ap.key_hex = new_key;
- return wpas_wps_start_reg(wpa_s, _bssid, pin, &ap);
+ return wpas_wps_start_reg(wpa_s, bssid, pin, &ap);
}
+#ifdef CONFIG_AP
+static int wpa_supplicant_ctrl_iface_wps_ap_pin(struct wpa_supplicant *wpa_s,
+ char *cmd, char *buf,
+ size_t buflen)
+{
+ int timeout = 300;
+ char *pos;
+ const char *pin_txt;
+
+ if (!wpa_s->ap_iface)
+ return -1;
+
+ pos = os_strchr(cmd, ' ');
+ if (pos)
+ *pos++ = '\0';
+
+ if (os_strcmp(cmd, "disable") == 0) {
+ wpas_wps_ap_pin_disable(wpa_s);
+ return os_snprintf(buf, buflen, "OK\n");
+ }
+
+ if (os_strcmp(cmd, "random") == 0) {
+ if (pos)
+ timeout = atoi(pos);
+ pin_txt = wpas_wps_ap_pin_random(wpa_s, timeout);
+ if (pin_txt == NULL)
+ return -1;
+ return os_snprintf(buf, buflen, "%s", pin_txt);
+ }
+
+ if (os_strcmp(cmd, "get") == 0) {
+ pin_txt = wpas_wps_ap_pin_get(wpa_s);
+ if (pin_txt == NULL)
+ return -1;
+ return os_snprintf(buf, buflen, "%s", pin_txt);
+ }
+
+ if (os_strcmp(cmd, "set") == 0) {
+ char *pin;
+ if (pos == NULL)
+ return -1;
+ pin = pos;
+ pos = os_strchr(pos, ' ');
+ if (pos) {
+ *pos++ = '\0';
+ timeout = atoi(pos);
+ }
+ if (os_strlen(pin) > buflen)
+ return -1;
+ if (wpas_wps_ap_pin_set(wpa_s, pin, timeout) < 0)
+ return -1;
+ return os_snprintf(buf, buflen, "%s", pin);
+ }
+
+ return -1;
+}
+#endif /* CONFIG_AP */
+
+
#ifdef CONFIG_WPS_ER
static int wpa_supplicant_ctrl_iface_wps_er_pin(struct wpa_supplicant *wpa_s,
char *cmd)
{
- char *uuid = cmd, *pin;
+ char *uuid = cmd, *pin, *pos;
+ u8 addr_buf[ETH_ALEN], *addr = NULL;
pin = os_strchr(uuid, ' ');
if (pin == NULL)
return -1;
*pin++ = '\0';
- return wpas_wps_er_add_pin(wpa_s, uuid, pin);
+ pos = os_strchr(pin, ' ');
+ if (pos) {
+ *pos++ = '\0';
+ if (hwaddr_aton(pos, addr_buf) == 0)
+ addr = addr_buf;
+ }
+ return wpas_wps_er_add_pin(wpa_s, addr, uuid, pin);
}
@@ -330,6 +1120,99 @@ static int wpa_supplicant_ctrl_iface_wps_er_learn(struct wpa_supplicant *wpa_s,
*pin++ = '\0';
return wpas_wps_er_learn(wpa_s, uuid, pin);
}
+
+
+static int wpa_supplicant_ctrl_iface_wps_er_set_config(
+ struct wpa_supplicant *wpa_s, char *cmd)
+{
+ char *uuid = cmd, *id;
+ id = os_strchr(uuid, ' ');
+ if (id == NULL)
+ return -1;
+ *id++ = '\0';
+ return wpas_wps_er_set_config(wpa_s, uuid, atoi(id));
+}
+
+
+static int wpa_supplicant_ctrl_iface_wps_er_config(
+ struct wpa_supplicant *wpa_s, char *cmd)
+{
+ char *pin;
+ char *new_ssid;
+ char *new_auth;
+ char *new_encr;
+ char *new_key;
+ struct wps_new_ap_settings ap;
+
+ pin = os_strchr(cmd, ' ');
+ if (pin == NULL)
+ return -1;
+ *pin++ = '\0';
+
+ new_ssid = os_strchr(pin, ' ');
+ if (new_ssid == NULL)
+ return -1;
+ *new_ssid++ = '\0';
+
+ new_auth = os_strchr(new_ssid, ' ');
+ if (new_auth == NULL)
+ return -1;
+ *new_auth++ = '\0';
+
+ new_encr = os_strchr(new_auth, ' ');
+ if (new_encr == NULL)
+ return -1;
+ *new_encr++ = '\0';
+
+ new_key = os_strchr(new_encr, ' ');
+ if (new_key == NULL)
+ return -1;
+ *new_key++ = '\0';
+
+ os_memset(&ap, 0, sizeof(ap));
+ ap.ssid_hex = new_ssid;
+ ap.auth = new_auth;
+ ap.encr = new_encr;
+ ap.key_hex = new_key;
+ return wpas_wps_er_config(wpa_s, cmd, pin, &ap);
+}
+
+
+#ifdef CONFIG_WPS_NFC
+static int wpa_supplicant_ctrl_iface_wps_er_nfc_config_token(
+ struct wpa_supplicant *wpa_s, char *cmd, char *reply, size_t max_len)
+{
+ int ndef;
+ struct wpabuf *buf;
+ int res;
+ char *uuid;
+
+ uuid = os_strchr(cmd, ' ');
+ if (uuid == NULL)
+ return -1;
+ *uuid++ = '\0';
+
+ if (os_strcmp(cmd, "WPS") == 0)
+ ndef = 0;
+ else if (os_strcmp(cmd, "NDEF") == 0)
+ ndef = 1;
+ else
+ return -1;
+
+ buf = wpas_wps_er_nfc_config_token(wpa_s, ndef, uuid);
+ if (buf == NULL)
+ return -1;
+
+ res = wpa_snprintf_hex_uppercase(reply, max_len, wpabuf_head(buf),
+ wpabuf_len(buf));
+ reply[res++] = '\n';
+ reply[res] = '\0';
+
+ wpabuf_free(buf);
+
+ return res;
+}
+#endif /* CONFIG_WPS_NFC */
#endif /* CONFIG_WPS_ER */
#endif /* CONFIG_WPS */
@@ -362,7 +1245,6 @@ static int wpa_supplicant_ctrl_iface_ctrl_rsp(struct wpa_supplicant *wpa_s,
char *pos, *id_pos;
int id;
struct wpa_ssid *ssid;
- struct eap_peer_config *eap;
pos = os_strchr(rsp, '-');
if (pos == NULL)
@@ -384,54 +1266,9 @@ static int wpa_supplicant_ctrl_iface_ctrl_rsp(struct wpa_supplicant *wpa_s,
"to update", id);
return -1;
}
- eap = &ssid->eap;
-
- if (os_strcmp(rsp, "IDENTITY") == 0) {
- os_free(eap->identity);
- eap->identity = (u8 *) os_strdup(pos);
- eap->identity_len = os_strlen(pos);
- eap->pending_req_identity = 0;
- if (ssid == wpa_s->current_ssid)
- wpa_s->reassociate = 1;
- } else if (os_strcmp(rsp, "PASSWORD") == 0) {
- os_free(eap->password);
- eap->password = (u8 *) os_strdup(pos);
- eap->password_len = os_strlen(pos);
- eap->pending_req_password = 0;
- if (ssid == wpa_s->current_ssid)
- wpa_s->reassociate = 1;
- } else if (os_strcmp(rsp, "NEW_PASSWORD") == 0) {
- os_free(eap->new_password);
- eap->new_password = (u8 *) os_strdup(pos);
- eap->new_password_len = os_strlen(pos);
- eap->pending_req_new_password = 0;
- if (ssid == wpa_s->current_ssid)
- wpa_s->reassociate = 1;
- } else if (os_strcmp(rsp, "PIN") == 0) {
- os_free(eap->pin);
- eap->pin = os_strdup(pos);
- eap->pending_req_pin = 0;
- if (ssid == wpa_s->current_ssid)
- wpa_s->reassociate = 1;
- } else if (os_strcmp(rsp, "OTP") == 0) {
- os_free(eap->otp);
- eap->otp = (u8 *) os_strdup(pos);
- eap->otp_len = os_strlen(pos);
- os_free(eap->pending_req_otp);
- eap->pending_req_otp = NULL;
- eap->pending_req_otp_len = 0;
- } else if (os_strcmp(rsp, "PASSPHRASE") == 0) {
- os_free(eap->private_key_passwd);
- eap->private_key_passwd = (u8 *) os_strdup(pos);
- eap->pending_req_passphrase = 0;
- if (ssid == wpa_s->current_ssid)
- wpa_s->reassociate = 1;
- } else {
- wpa_printf(MSG_DEBUG, "CTRL_IFACE: Unknown field '%s'", rsp);
- return -1;
- }
- return 0;
+ return wpa_supplicant_ctrl_iface_ctrl_rsp_handle(wpa_s, ssid, rsp,
+ pos);
#else /* IEEE8021X_EAPOL */
wpa_printf(MSG_DEBUG, "CTRL_IFACE: 802.1X not included");
return -1;
@@ -444,9 +1281,10 @@ static int wpa_supplicant_ctrl_iface_status(struct wpa_supplicant *wpa_s,
char *buf, size_t buflen)
{
char *pos, *end, tmp[30];
- int res, verbose, ret;
+ int res, verbose, wps, ret;
verbose = os_strcmp(params, "-VERBOSE") == 0;
+ wps = os_strcmp(params, "-WPS") == 0;
pos = buf;
end = buf + buflen;
if (wpa_s->wpa_state >= WPA_ASSOCIATED) {
@@ -475,6 +1313,17 @@ static int wpa_supplicant_ctrl_iface_status(struct wpa_supplicant *wpa_s,
return pos - buf;
pos += ret;
+ if (wps && ssid->passphrase &&
+ wpa_key_mgmt_wpa_psk(ssid->key_mgmt) &&
+ (ssid->mode == WPAS_MODE_AP ||
+ ssid->mode == WPAS_MODE_P2P_GO)) {
+ ret = os_snprintf(pos, end - pos,
+ "passphrase=%s\n",
+ ssid->passphrase);
+ if (ret < 0 || ret >= end - pos)
+ return pos - buf;
+ pos += ret;
+ }
if (ssid->id_str) {
ret = os_snprintf(pos, end - pos,
"id_str=%s\n",
@@ -497,6 +1346,15 @@ static int wpa_supplicant_ctrl_iface_status(struct wpa_supplicant *wpa_s,
ret = os_snprintf(pos, end - pos,
"mode=AP\n");
break;
+ case WPAS_MODE_P2P_GO:
+ ret = os_snprintf(pos, end - pos,
+ "mode=P2P GO\n");
+ break;
+ case WPAS_MODE_P2P_GROUP_FORMATION:
+ ret = os_snprintf(pos, end - pos,
+ "mode=P2P GO - group "
+ "formation\n");
+ break;
default:
ret = 0;
break;
@@ -529,6 +1387,73 @@ static int wpa_supplicant_ctrl_iface_status(struct wpa_supplicant *wpa_s,
pos += ret;
}
+#ifdef CONFIG_P2P
+ if (wpa_s->global->p2p) {
+ ret = os_snprintf(pos, end - pos, "p2p_device_address=" MACSTR
+ "\n", MAC2STR(wpa_s->global->p2p_dev_addr));
+ if (ret < 0 || ret >= end - pos)
+ return pos - buf;
+ pos += ret;
+ }
+#endif /* CONFIG_P2P */
+
+ ret = os_snprintf(pos, end - pos, "address=" MACSTR "\n",
+ MAC2STR(wpa_s->own_addr));
+ if (ret < 0 || ret >= end - pos)
+ return pos - buf;
+ pos += ret;
+
+#ifdef CONFIG_HS20
+ if (wpa_s->current_bss &&
+ wpa_bss_get_vendor_ie(wpa_s->current_bss, HS20_IE_VENDOR_TYPE) &&
+ wpa_s->wpa_proto == WPA_PROTO_RSN &&
+ wpa_key_mgmt_wpa_ieee8021x(wpa_s->key_mgmt)) {
+ ret = os_snprintf(pos, end - pos, "hs20=1\n");
+ if (ret < 0 || ret >= end - pos)
+ return pos - buf;
+ pos += ret;
+ }
+
+ if (wpa_s->current_ssid) {
+ struct wpa_cred *cred;
+ char *type;
+
+ for (cred = wpa_s->conf->cred; cred; cred = cred->next) {
+ if (wpa_s->current_ssid->parent_cred != cred)
+ continue;
+ if (!cred->domain)
+ continue;
+
+ ret = os_snprintf(pos, end - pos, "home_sp=%s\n",
+ cred->domain);
+ if (ret < 0 || ret >= end - pos)
+ return pos - buf;
+ pos += ret;
+
+ if (wpa_s->current_bss == NULL ||
+ wpa_s->current_bss->anqp == NULL)
+ res = -1;
+ else
+ res = interworking_home_sp_cred(
+ wpa_s, cred,
+ wpa_s->current_bss->anqp->domain_name);
+ if (res > 0)
+ type = "home";
+ else if (res == 0)
+ type = "roaming";
+ else
+ type = "unknown";
+
+ ret = os_snprintf(pos, end - pos, "sp_type=%s\n", type);
+ if (ret < 0 || ret >= end - pos)
+ return pos - buf;
+ pos += ret;
+
+ break;
+ }
+ }
+#endif /* CONFIG_HS20 */
+
if (wpa_key_mgmt_wpa_ieee8021x(wpa_s->key_mgmt) ||
wpa_s->key_mgmt == WPA_KEY_MGMT_IEEE8021X_NO_WPA) {
res = eapol_sm_get_status(wpa_s->eapol, pos, end - pos,
@@ -579,6 +1504,152 @@ static int wpa_supplicant_ctrl_iface_bssid(struct wpa_supplicant *wpa_s,
}
+static int wpa_supplicant_ctrl_iface_blacklist(struct wpa_supplicant *wpa_s,
+ char *cmd, char *buf,
+ size_t buflen)
+{
+ u8 bssid[ETH_ALEN];
+ struct wpa_blacklist *e;
+ char *pos, *end;
+ int ret;
+
+ /* cmd: "BLACKLIST [<BSSID>]" */
+ if (*cmd == '\0') {
+ pos = buf;
+ end = buf + buflen;
+ e = wpa_s->blacklist;
+ while (e) {
+ ret = os_snprintf(pos, end - pos, MACSTR "\n",
+ MAC2STR(e->bssid));
+ if (ret < 0 || ret >= end - pos)
+ return pos - buf;
+ pos += ret;
+ e = e->next;
+ }
+ return pos - buf;
+ }
+
+ cmd++;
+ if (os_strncmp(cmd, "clear", 5) == 0) {
+ wpa_blacklist_clear(wpa_s);
+ os_memcpy(buf, "OK\n", 3);
+ return 3;
+ }
+
+ wpa_printf(MSG_DEBUG, "CTRL_IFACE: BLACKLIST bssid='%s'", cmd);
+ if (hwaddr_aton(cmd, bssid)) {
+ wpa_printf(MSG_DEBUG, "CTRL_IFACE: invalid BSSID '%s'", cmd);
+ return -1;
+ }
+
+ /*
+ * Add the BSSID twice, so its count will be 2, causing it to be
+ * skipped when processing scan results.
+ */
+ ret = wpa_blacklist_add(wpa_s, bssid);
+ if (ret != 0)
+ return -1;
+ ret = wpa_blacklist_add(wpa_s, bssid);
+ if (ret != 0)
+ return -1;
+ os_memcpy(buf, "OK\n", 3);
+ return 3;
+}
+
+
+extern int wpa_debug_level;
+extern int wpa_debug_timestamp;
+
+static const char * debug_level_str(int level)
+{
+ switch (level) {
+ case MSG_EXCESSIVE:
+ return "EXCESSIVE";
+ case MSG_MSGDUMP:
+ return "MSGDUMP";
+ case MSG_DEBUG:
+ return "DEBUG";
+ case MSG_INFO:
+ return "INFO";
+ case MSG_WARNING:
+ return "WARNING";
+ case MSG_ERROR:
+ return "ERROR";
+ default:
+ return "?";
+ }
+}
+
+
+static int str_to_debug_level(const char *s)
+{
+ if (os_strcasecmp(s, "EXCESSIVE") == 0)
+ return MSG_EXCESSIVE;
+ if (os_strcasecmp(s, "MSGDUMP") == 0)
+ return MSG_MSGDUMP;
+ if (os_strcasecmp(s, "DEBUG") == 0)
+ return MSG_DEBUG;
+ if (os_strcasecmp(s, "INFO") == 0)
+ return MSG_INFO;
+ if (os_strcasecmp(s, "WARNING") == 0)
+ return MSG_WARNING;
+ if (os_strcasecmp(s, "ERROR") == 0)
+ return MSG_ERROR;
+ return -1;
+}
+
+
+static int wpa_supplicant_ctrl_iface_log_level(struct wpa_supplicant *wpa_s,
+ char *cmd, char *buf,
+ size_t buflen)
+{
+ char *pos, *end, *stamp;
+ int ret;
+
+ if (cmd == NULL) {
+ return -1;
+ }
+
+ /* cmd: "LOG_LEVEL [<level>]" */
+ if (*cmd == '\0') {
+ pos = buf;
+ end = buf + buflen;
+ ret = os_snprintf(pos, end - pos, "Current level: %s\n"
+ "Timestamp: %d\n",
+ debug_level_str(wpa_debug_level),
+ wpa_debug_timestamp);
+ if (ret < 0 || ret >= end - pos)
+ ret = 0;
+
+ return ret;
+ }
+
+ while (*cmd == ' ')
+ cmd++;
+
+ stamp = os_strchr(cmd, ' ');
+ if (stamp) {
+ *stamp++ = '\0';
+ while (*stamp == ' ') {
+ stamp++;
+ }
+ }
+
+ if (cmd && os_strlen(cmd)) {
+ int level = str_to_debug_level(cmd);
+ if (level < 0)
+ return -1;
+ wpa_debug_level = level;
+ }
+
+ if (stamp && os_strlen(stamp))
+ wpa_debug_timestamp = atoi(stamp);
+
+ os_memcpy(buf, "OK\n", 3);
+ return 3;
+}
+
+
static int wpa_supplicant_ctrl_iface_list_networks(
struct wpa_supplicant *wpa_s, char *buf, size_t buflen)
{
@@ -611,10 +1682,14 @@ static int wpa_supplicant_ctrl_iface_list_networks(
if (ret < 0 || ret >= end - pos)
return pos - buf;
pos += ret;
- ret = os_snprintf(pos, end - pos, "\t%s%s",
+ ret = os_snprintf(pos, end - pos, "\t%s%s%s%s",
ssid == wpa_s->current_ssid ?
"[CURRENT]" : "",
- ssid->disabled ? "[DISABLED]" : "");
+ ssid->disabled ? "[DISABLED]" : "",
+ ssid->disabled_until.sec ?
+ "[TEMP-DISABLED]" : "",
+ ssid->disabled == 2 ? "[P2P-PERSISTENT]" :
+ "");
if (ret < 0 || ret >= end - pos)
return pos - buf;
pos += ret;
@@ -673,6 +1748,13 @@ static char * wpa_supplicant_cipher_txt(char *pos, char *end, int cipher)
pos += ret;
first = 0;
}
+ if (cipher & WPA_CIPHER_GCMP) {
+ ret = os_snprintf(pos, end - pos, "%sGCMP", first ? "" : "+");
+ if (ret < 0 || ret >= end - pos)
+ return pos;
+ pos += ret;
+ first = 0;
+ }
return pos;
}
@@ -774,7 +1856,8 @@ static char * wpa_supplicant_ie_txt(char *pos, char *end, const char *proto,
#ifdef CONFIG_WPS
-static char * wpa_supplicant_wps_ie_txt_buf(char *pos, char *end,
+static char * wpa_supplicant_wps_ie_txt_buf(struct wpa_supplicant *wpa_s,
+ char *pos, char *end,
struct wpabuf *wps_ie)
{
int ret;
@@ -784,6 +1867,10 @@ static char * wpa_supplicant_wps_ie_txt_buf(char *pos, char *end,
return pos;
if (wps_is_selected_pbc_registrar(wps_ie))
txt = "[WPS-PBC]";
+#ifdef CONFIG_WPS2
+ else if (wps_is_addr_authorized(wps_ie, wpa_s->own_addr, 0))
+ txt = "[WPS-AUTH]";
+#endif /* CONFIG_WPS2 */
else if (wps_is_selected_pin_registrar(wps_ie))
txt = "[WPS-PIN]";
else
@@ -798,13 +1885,14 @@ static char * wpa_supplicant_wps_ie_txt_buf(char *pos, char *end,
#endif /* CONFIG_WPS */
-static char * wpa_supplicant_wps_ie_txt(char *pos, char *end,
+static char * wpa_supplicant_wps_ie_txt(struct wpa_supplicant *wpa_s,
+ char *pos, char *end,
const struct wpa_bss *bss)
{
#ifdef CONFIG_WPS
struct wpabuf *wps_ie;
wps_ie = wpa_bss_get_vendor_ie_multi(bss, WPS_IE_VENDOR_TYPE);
- return wpa_supplicant_wps_ie_txt_buf(pos, end, wps_ie);
+ return wpa_supplicant_wps_ie_txt_buf(wpa_s, pos, end, wps_ie);
#else /* CONFIG_WPS */
return pos;
#endif /* CONFIG_WPS */
@@ -813,11 +1901,18 @@ static char * wpa_supplicant_wps_ie_txt(char *pos, char *end,
/* Format one result on one text line into a buffer. */
static int wpa_supplicant_ctrl_iface_scan_result(
+ struct wpa_supplicant *wpa_s,
const struct wpa_bss *bss, char *buf, size_t buflen)
{
char *pos, *end;
int ret;
- const u8 *ie, *ie2;
+ const u8 *ie, *ie2, *p2p;
+
+ p2p = wpa_bss_get_vendor_ie(bss, P2P_IE_VENDOR_TYPE);
+ if (p2p && bss->ssid_len == P2P_WILDCARD_SSID_LEN &&
+ os_memcmp(bss->ssid, P2P_WILDCARD_SSID, P2P_WILDCARD_SSID_LEN) ==
+ 0)
+ return 0; /* Do not show P2P listen discovery results here */
pos = buf;
end = buf + buflen;
@@ -825,7 +1920,7 @@ static int wpa_supplicant_ctrl_iface_scan_result(
ret = os_snprintf(pos, end - pos, MACSTR "\t%d\t%d\t",
MAC2STR(bss->bssid), bss->freq, bss->level);
if (ret < 0 || ret >= end - pos)
- return pos - buf;
+ return -1;
pos += ret;
ie = wpa_bss_get_vendor_ie(bss, WPA_IE_VENDOR_TYPE);
if (ie)
@@ -833,35 +1928,49 @@ static int wpa_supplicant_ctrl_iface_scan_result(
ie2 = wpa_bss_get_ie(bss, WLAN_EID_RSN);
if (ie2)
pos = wpa_supplicant_ie_txt(pos, end, "WPA2", ie2, 2 + ie2[1]);
- pos = wpa_supplicant_wps_ie_txt(pos, end, bss);
+ pos = wpa_supplicant_wps_ie_txt(wpa_s, pos, end, bss);
if (!ie && !ie2 && bss->caps & IEEE80211_CAP_PRIVACY) {
ret = os_snprintf(pos, end - pos, "[WEP]");
if (ret < 0 || ret >= end - pos)
- return pos - buf;
+ return -1;
pos += ret;
}
if (bss->caps & IEEE80211_CAP_IBSS) {
ret = os_snprintf(pos, end - pos, "[IBSS]");
if (ret < 0 || ret >= end - pos)
- return pos - buf;
+ return -1;
pos += ret;
}
if (bss->caps & IEEE80211_CAP_ESS) {
ret = os_snprintf(pos, end - pos, "[ESS]");
if (ret < 0 || ret >= end - pos)
- return pos - buf;
+ return -1;
pos += ret;
}
+ if (p2p) {
+ ret = os_snprintf(pos, end - pos, "[P2P]");
+ if (ret < 0 || ret >= end - pos)
+ return -1;
+ pos += ret;
+ }
+#ifdef CONFIG_HS20
+ if (wpa_bss_get_vendor_ie(bss, HS20_IE_VENDOR_TYPE) && ie2) {
+ ret = os_snprintf(pos, end - pos, "[HS20]");
+ if (ret < 0 || ret >= end - pos)
+ return -1;
+ pos += ret;
+ }
+#endif /* CONFIG_HS20 */
ret = os_snprintf(pos, end - pos, "\t%s",
wpa_ssid_txt(bss->ssid, bss->ssid_len));
if (ret < 0 || ret >= end - pos)
- return pos - buf;
+ return -1;
pos += ret;
ret = os_snprintf(pos, end - pos, "\n");
if (ret < 0 || ret >= end - pos)
- return pos - buf;
+ return -1;
pos += ret;
return pos - buf;
@@ -884,7 +1993,7 @@ static int wpa_supplicant_ctrl_iface_scan_results(
pos += ret;
dl_list_for_each(bss, &wpa_s->bss_id, struct wpa_bss, list_id) {
- ret = wpa_supplicant_ctrl_iface_scan_result(bss, pos,
+ ret = wpa_supplicant_ctrl_iface_scan_result(wpa_s, bss, pos,
end - pos);
if (ret < 0 || ret >= end - pos)
return pos - buf;
@@ -915,6 +2024,11 @@ static int wpa_supplicant_ctrl_iface_select_network(
"network id=%d", id);
return -1;
}
+ if (ssid->disabled == 2) {
+ wpa_printf(MSG_DEBUG, "CTRL_IFACE: Cannot use "
+ "SELECT_NETWORK with persistent P2P group");
+ return -1;
+ }
}
wpa_supplicant_select_network(wpa_s, ssid);
@@ -943,6 +2057,16 @@ static int wpa_supplicant_ctrl_iface_enable_network(
"network id=%d", id);
return -1;
}
+ if (ssid->disabled == 2) {
+ wpa_printf(MSG_DEBUG, "CTRL_IFACE: Cannot use "
+ "ENABLE_NETWORK with persistent P2P group");
+ return -1;
+ }
+
+ if (os_strstr(cmd, " no-connect")) {
+ ssid->disabled = 0;
+ return 0;
+ }
}
wpa_supplicant_enable_network(wpa_s, ssid);
@@ -970,6 +2094,12 @@ static int wpa_supplicant_ctrl_iface_disable_network(
"network id=%d", id);
return -1;
}
+ if (ssid->disabled == 2) {
+ wpa_printf(MSG_DEBUG, "CTRL_IFACE: Cannot use "
+ "DISABLE_NETWORK with persistent P2P "
+ "group");
+ return -1;
+ }
}
wpa_supplicant_disable_network(wpa_s, ssid);
@@ -1018,10 +2148,15 @@ static int wpa_supplicant_ctrl_iface_remove_network(
wpas_notify_network_removed(wpa_s, remove_ssid);
wpa_config_remove_network(wpa_s->conf, id);
}
+ eapol_sm_invalidate_cached_session(wpa_s->eapol);
if (wpa_s->current_ssid) {
- eapol_sm_invalidate_cached_session(wpa_s->eapol);
- wpa_supplicant_disassociate(wpa_s,
- WLAN_REASON_DEAUTH_LEAVING);
+#ifdef CONFIG_SME
+ wpa_s->sme.prev_bssid_set = 0;
+#endif /* CONFIG_SME */
+ wpa_sm_set_config(wpa_s->wpa, NULL);
+ eapol_sm_notify_config(wpa_s->eapol, NULL, NULL);
+ wpa_supplicant_deauthenticate(
+ wpa_s, WLAN_REASON_DEAUTH_LEAVING);
}
return 0;
}
@@ -1030,21 +2165,37 @@ static int wpa_supplicant_ctrl_iface_remove_network(
wpa_printf(MSG_DEBUG, "CTRL_IFACE: REMOVE_NETWORK id=%d", id);
ssid = wpa_config_get_network(wpa_s->conf, id);
- if (ssid == NULL ||
- wpa_config_remove_network(wpa_s->conf, id) < 0) {
+ if (ssid)
+ wpas_notify_network_removed(wpa_s, ssid);
+ if (ssid == NULL) {
wpa_printf(MSG_DEBUG, "CTRL_IFACE: Could not find network "
"id=%d", id);
return -1;
}
- if (ssid == wpa_s->current_ssid) {
+ if (ssid == wpa_s->current_ssid || wpa_s->current_ssid == NULL) {
+#ifdef CONFIG_SME
+ wpa_s->sme.prev_bssid_set = 0;
+#endif /* CONFIG_SME */
/*
- * Invalidate the EAP session cache if the current network is
- * removed.
+ * Invalidate the EAP session cache if the current or
+ * previously used network is removed.
*/
eapol_sm_invalidate_cached_session(wpa_s->eapol);
+ }
+
+ if (ssid == wpa_s->current_ssid) {
+ wpa_sm_set_config(wpa_s->wpa, NULL);
+ eapol_sm_notify_config(wpa_s->eapol, NULL, NULL);
- wpa_supplicant_disassociate(wpa_s, WLAN_REASON_DEAUTH_LEAVING);
+ wpa_supplicant_deauthenticate(wpa_s,
+ WLAN_REASON_DEAUTH_LEAVING);
+ }
+
+ if (wpa_config_remove_network(wpa_s->conf, id) < 0) {
+ wpa_printf(MSG_DEBUG, "CTRL_IFACE: Not able to remove the "
+ "network id=%d", id);
+ return -1;
}
return 0;
@@ -1088,10 +2239,14 @@ static int wpa_supplicant_ctrl_iface_set_network(
return -1;
}
- if (wpa_s->current_ssid == ssid) {
+ if (os_strcmp(name, "bssid") != 0 &&
+ os_strcmp(name, "priority") != 0)
+ wpa_sm_pmksa_cache_flush(wpa_s->wpa, ssid);
+
+ if (wpa_s->current_ssid == ssid || wpa_s->current_ssid == NULL) {
/*
* Invalidate the EAP session cache if anything in the current
- * configuration changes.
+ * or previously used configuration changes.
*/
eapol_sm_invalidate_cached_session(wpa_s->eapol);
}
@@ -1151,6 +2306,167 @@ static int wpa_supplicant_ctrl_iface_get_network(
}
+static int wpa_supplicant_ctrl_iface_list_creds(struct wpa_supplicant *wpa_s,
+ char *buf, size_t buflen)
+{
+ char *pos, *end;
+ struct wpa_cred *cred;
+ int ret;
+
+ pos = buf;
+ end = buf + buflen;
+ ret = os_snprintf(pos, end - pos,
+ "cred id / realm / username / domain / imsi\n");
+ if (ret < 0 || ret >= end - pos)
+ return pos - buf;
+ pos += ret;
+
+ cred = wpa_s->conf->cred;
+ while (cred) {
+ ret = os_snprintf(pos, end - pos, "%d\t%s\t%s\t%s\t%s\n",
+ cred->id, cred->realm ? cred->realm : "",
+ cred->username ? cred->username : "",
+ cred->domain ? cred->domain : "",
+ cred->imsi ? cred->imsi : "");
+ if (ret < 0 || ret >= end - pos)
+ return pos - buf;
+ pos += ret;
+
+ cred = cred->next;
+ }
+
+ return pos - buf;
+}
+
+
+static int wpa_supplicant_ctrl_iface_add_cred(struct wpa_supplicant *wpa_s,
+ char *buf, size_t buflen)
+{
+ struct wpa_cred *cred;
+ int ret;
+
+ wpa_printf(MSG_DEBUG, "CTRL_IFACE: ADD_CRED");
+
+ cred = wpa_config_add_cred(wpa_s->conf);
+ if (cred == NULL)
+ return -1;
+
+ ret = os_snprintf(buf, buflen, "%d\n", cred->id);
+ if (ret < 0 || (size_t) ret >= buflen)
+ return -1;
+ return ret;
+}
+
+
+static int wpas_ctrl_remove_cred(struct wpa_supplicant *wpa_s,
+ struct wpa_cred *cred)
+{
+ struct wpa_ssid *ssid;
+ char str[20];
+
+ if (cred == NULL || wpa_config_remove_cred(wpa_s->conf, cred->id) < 0) {
+ wpa_printf(MSG_DEBUG, "CTRL_IFACE: Could not find cred");
+ return -1;
+ }
+
+ /* Remove any network entry created based on the removed credential */
+ ssid = wpa_s->conf->ssid;
+ while (ssid) {
+ if (ssid->parent_cred == cred) {
+ wpa_printf(MSG_DEBUG, "Remove network id %d since it "
+ "used the removed credential", ssid->id);
+ os_snprintf(str, sizeof(str), "%d", ssid->id);
+ ssid = ssid->next;
+ wpa_supplicant_ctrl_iface_remove_network(wpa_s, str);
+ } else
+ ssid = ssid->next;
+ }
+
+ return 0;
+}
+
+
+static int wpa_supplicant_ctrl_iface_remove_cred(struct wpa_supplicant *wpa_s,
+ char *cmd)
+{
+ int id;
+ struct wpa_cred *cred, *prev;
+
+ /* cmd: "<cred id>", "all", or "sp_fqdn=<FQDN>" */
+ if (os_strcmp(cmd, "all") == 0) {
+ wpa_printf(MSG_DEBUG, "CTRL_IFACE: REMOVE_CRED all");
+ cred = wpa_s->conf->cred;
+ while (cred) {
+ prev = cred;
+ cred = cred->next;
+ wpas_ctrl_remove_cred(wpa_s, prev);
+ }
+ return 0;
+ }
+
+ if (os_strncmp(cmd, "sp_fqdn=", 8) == 0) {
+ wpa_printf(MSG_DEBUG, "CTRL_IFACE: REMOVE_CRED SP FQDN '%s'",
+ cmd + 8);
+ cred = wpa_s->conf->cred;
+ while (cred) {
+ prev = cred;
+ cred = cred->next;
+ if (prev->domain &&
+ os_strcmp(prev->domain, cmd + 8) == 0)
+ wpas_ctrl_remove_cred(wpa_s, prev);
+ }
+ return 0;
+ }
+
+ id = atoi(cmd);
+ wpa_printf(MSG_DEBUG, "CTRL_IFACE: REMOVE_CRED id=%d", id);
+
+ cred = wpa_config_get_cred(wpa_s->conf, id);
+ return wpas_ctrl_remove_cred(wpa_s, cred);
+}
+
+
+static int wpa_supplicant_ctrl_iface_set_cred(struct wpa_supplicant *wpa_s,
+ char *cmd)
+{
+ int id;
+ struct wpa_cred *cred;
+ char *name, *value;
+
+ /* cmd: "<cred id> <variable name> <value>" */
+ name = os_strchr(cmd, ' ');
+ if (name == NULL)
+ return -1;
+ *name++ = '\0';
+
+ value = os_strchr(name, ' ');
+ if (value == NULL)
+ return -1;
+ *value++ = '\0';
+
+ id = atoi(cmd);
+ wpa_printf(MSG_DEBUG, "CTRL_IFACE: SET_CRED id=%d name='%s'",
+ id, name);
+ wpa_hexdump_ascii_key(MSG_DEBUG, "CTRL_IFACE: value",
+ (u8 *) value, os_strlen(value));
+
+ cred = wpa_config_get_cred(wpa_s->conf, id);
+ if (cred == NULL) {
+ wpa_printf(MSG_DEBUG, "CTRL_IFACE: Could not find cred id=%d",
+ id);
+ return -1;
+ }
+
+ if (wpa_config_set_cred(cred, name, value, 0) < 0) {
+ wpa_printf(MSG_DEBUG, "CTRL_IFACE: Failed to set cred "
+ "variable '%s'", name);
+ return -1;
+ }
+
+ return 0;
+}
+
+
#ifndef CONFIG_NO_CONFIG_WRITE
static int wpa_supplicant_ctrl_iface_save_config(struct wpa_supplicant *wpa_s)
{
@@ -1204,6 +2520,14 @@ static int ctrl_iface_get_capability_pairwise(int res, char *strict,
first = 0;
}
+ if (capa->enc & WPA_DRIVER_CAPA_ENC_GCMP) {
+ ret = os_snprintf(pos, end - pos, "%sGCMP", first ? "" : " ");
+ if (ret < 0 || ret >= end - pos)
+ return pos - buf;
+ pos += ret;
+ first = 0;
+ }
+
if (capa->enc & WPA_DRIVER_CAPA_ENC_TKIP) {
ret = os_snprintf(pos, end - pos, "%sTKIP", first ? "" : " ");
if (ret < 0 || ret >= end - pos)
@@ -1252,6 +2576,14 @@ static int ctrl_iface_get_capability_group(int res, char *strict,
first = 0;
}
+ if (capa->enc & WPA_DRIVER_CAPA_ENC_GCMP) {
+ ret = os_snprintf(pos, end - pos, "%sGCMP", first ? "" : " ");
+ if (ret < 0 || ret >= end - pos)
+ return pos - buf;
+ pos += ret;
+ first = 0;
+ }
+
if (capa->enc & WPA_DRIVER_CAPA_ENC_TKIP) {
ret = os_snprintf(pos, end - pos, "%sTKIP", first ? "" : " ");
if (ret < 0 || ret >= end - pos)
@@ -1425,6 +2757,56 @@ static int ctrl_iface_get_capability_auth_alg(int res, char *strict,
}
+static int ctrl_iface_get_capability_channels(struct wpa_supplicant *wpa_s,
+ char *buf, size_t buflen)
+{
+ struct hostapd_channel_data *chnl;
+ int ret, i, j;
+ char *pos, *end, *hmode;
+
+ pos = buf;
+ end = pos + buflen;
+
+ for (j = 0; j < wpa_s->hw.num_modes; j++) {
+ switch (wpa_s->hw.modes[j].mode) {
+ case HOSTAPD_MODE_IEEE80211B:
+ hmode = "B";
+ break;
+ case HOSTAPD_MODE_IEEE80211G:
+ hmode = "G";
+ break;
+ case HOSTAPD_MODE_IEEE80211A:
+ hmode = "A";
+ break;
+ case HOSTAPD_MODE_IEEE80211AD:
+ hmode = "AD";
+ break;
+ default:
+ continue;
+ }
+ ret = os_snprintf(pos, end - pos, "Mode[%s] Channels:", hmode);
+ if (ret < 0 || ret >= end - pos)
+ return pos - buf;
+ pos += ret;
+ chnl = wpa_s->hw.modes[j].channels;
+ for (i = 0; i < wpa_s->hw.modes[j].num_channels; i++) {
+ if (chnl[i].flag & HOSTAPD_CHAN_DISABLED)
+ continue;
+ ret = os_snprintf(pos, end - pos, " %d", chnl[i].chan);
+ if (ret < 0 || ret >= end - pos)
+ return pos - buf;
+ pos += ret;
+ }
+ ret = os_snprintf(pos, end - pos, "\n");
+ if (ret < 0 || ret >= end - pos)
+ return pos - buf;
+ pos += ret;
+ }
+
+ return pos - buf;
+}
+
+
static int wpa_supplicant_ctrl_iface_get_capability(
struct wpa_supplicant *wpa_s, const char *_field, char *buf,
size_t buflen)
@@ -1475,6 +2857,9 @@ static int wpa_supplicant_ctrl_iface_get_capability(
return ctrl_iface_get_capability_auth_alg(res, strict, &capa,
buf, buflen);
+ if (os_strcmp(field, "channels") == 0)
+ return ctrl_iface_get_capability_channels(wpa_s, buf, buflen);
+
wpa_printf(MSG_DEBUG, "CTRL_IFACE: Unknown GET_CAPABILITY field '%s'",
field);
@@ -1482,6 +2867,288 @@ static int wpa_supplicant_ctrl_iface_get_capability(
}
+#ifdef CONFIG_INTERWORKING
+static char * anqp_add_hex(char *pos, char *end, const char *title,
+ struct wpabuf *data)
+{
+ char *start = pos;
+ size_t i;
+ int ret;
+ const u8 *d;
+
+ if (data == NULL)
+ return start;
+
+ ret = os_snprintf(pos, end - pos, "%s=", title);
+ if (ret < 0 || ret >= end - pos)
+ return start;
+ pos += ret;
+
+ d = wpabuf_head_u8(data);
+ for (i = 0; i < wpabuf_len(data); i++) {
+ ret = os_snprintf(pos, end - pos, "%02x", *d++);
+ if (ret < 0 || ret >= end - pos)
+ return start;
+ pos += ret;
+ }
+
+ ret = os_snprintf(pos, end - pos, "\n");
+ if (ret < 0 || ret >= end - pos)
+ return start;
+ pos += ret;
+
+ return pos;
+}
+#endif /* CONFIG_INTERWORKING */
+
+
+static int print_bss_info(struct wpa_supplicant *wpa_s, struct wpa_bss *bss,
+ unsigned long mask, char *buf, size_t buflen)
+{
+ size_t i;
+ int ret;
+ char *pos, *end;
+ const u8 *ie, *ie2;
+
+ pos = buf;
+ end = buf + buflen;
+
+ if (mask & WPA_BSS_MASK_ID) {
+ ret = os_snprintf(pos, end - pos, "id=%u\n", bss->id);
+ if (ret < 0 || ret >= end - pos)
+ return 0;
+ pos += ret;
+ }
+
+ if (mask & WPA_BSS_MASK_BSSID) {
+ ret = os_snprintf(pos, end - pos, "bssid=" MACSTR "\n",
+ MAC2STR(bss->bssid));
+ if (ret < 0 || ret >= end - pos)
+ return 0;
+ pos += ret;
+ }
+
+ if (mask & WPA_BSS_MASK_FREQ) {
+ ret = os_snprintf(pos, end - pos, "freq=%d\n", bss->freq);
+ if (ret < 0 || ret >= end - pos)
+ return 0;
+ pos += ret;
+ }
+
+ if (mask & WPA_BSS_MASK_BEACON_INT) {
+ ret = os_snprintf(pos, end - pos, "beacon_int=%d\n",
+ bss->beacon_int);
+ if (ret < 0 || ret >= end - pos)
+ return 0;
+ pos += ret;
+ }
+
+ if (mask & WPA_BSS_MASK_CAPABILITIES) {
+ ret = os_snprintf(pos, end - pos, "capabilities=0x%04x\n",
+ bss->caps);
+ if (ret < 0 || ret >= end - pos)
+ return 0;
+ pos += ret;
+ }
+
+ if (mask & WPA_BSS_MASK_QUAL) {
+ ret = os_snprintf(pos, end - pos, "qual=%d\n", bss->qual);
+ if (ret < 0 || ret >= end - pos)
+ return 0;
+ pos += ret;
+ }
+
+ if (mask & WPA_BSS_MASK_NOISE) {
+ ret = os_snprintf(pos, end - pos, "noise=%d\n", bss->noise);
+ if (ret < 0 || ret >= end - pos)
+ return 0;
+ pos += ret;
+ }
+
+ if (mask & WPA_BSS_MASK_LEVEL) {
+ ret = os_snprintf(pos, end - pos, "level=%d\n", bss->level);
+ if (ret < 0 || ret >= end - pos)
+ return 0;
+ pos += ret;
+ }
+
+ if (mask & WPA_BSS_MASK_TSF) {
+ ret = os_snprintf(pos, end - pos, "tsf=%016llu\n",
+ (unsigned long long) bss->tsf);
+ if (ret < 0 || ret >= end - pos)
+ return 0;
+ pos += ret;
+ }
+
+ if (mask & WPA_BSS_MASK_AGE) {
+ struct os_time now;
+
+ os_get_time(&now);
+ ret = os_snprintf(pos, end - pos, "age=%d\n",
+ (int) (now.sec - bss->last_update.sec));
+ if (ret < 0 || ret >= end - pos)
+ return 0;
+ pos += ret;
+ }
+
+ if (mask & WPA_BSS_MASK_IE) {
+ ret = os_snprintf(pos, end - pos, "ie=");
+ if (ret < 0 || ret >= end - pos)
+ return 0;
+ pos += ret;
+
+ ie = (const u8 *) (bss + 1);
+ for (i = 0; i < bss->ie_len; i++) {
+ ret = os_snprintf(pos, end - pos, "%02x", *ie++);
+ if (ret < 0 || ret >= end - pos)
+ return 0;
+ pos += ret;
+ }
+
+ ret = os_snprintf(pos, end - pos, "\n");
+ if (ret < 0 || ret >= end - pos)
+ return 0;
+ pos += ret;
+ }
+
+ if (mask & WPA_BSS_MASK_FLAGS) {
+ ret = os_snprintf(pos, end - pos, "flags=");
+ if (ret < 0 || ret >= end - pos)
+ return 0;
+ pos += ret;
+
+ ie = wpa_bss_get_vendor_ie(bss, WPA_IE_VENDOR_TYPE);
+ if (ie)
+ pos = wpa_supplicant_ie_txt(pos, end, "WPA", ie,
+ 2 + ie[1]);
+ ie2 = wpa_bss_get_ie(bss, WLAN_EID_RSN);
+ if (ie2)
+ pos = wpa_supplicant_ie_txt(pos, end, "WPA2", ie2,
+ 2 + ie2[1]);
+ pos = wpa_supplicant_wps_ie_txt(wpa_s, pos, end, bss);
+ if (!ie && !ie2 && bss->caps & IEEE80211_CAP_PRIVACY) {
+ ret = os_snprintf(pos, end - pos, "[WEP]");
+ if (ret < 0 || ret >= end - pos)
+ return 0;
+ pos += ret;
+ }
+ if (bss->caps & IEEE80211_CAP_IBSS) {
+ ret = os_snprintf(pos, end - pos, "[IBSS]");
+ if (ret < 0 || ret >= end - pos)
+ return 0;
+ pos += ret;
+ }
+ if (bss->caps & IEEE80211_CAP_ESS) {
+ ret = os_snprintf(pos, end - pos, "[ESS]");
+ if (ret < 0 || ret >= end - pos)
+ return 0;
+ pos += ret;
+ }
+ if (wpa_bss_get_vendor_ie(bss, P2P_IE_VENDOR_TYPE)) {
+ ret = os_snprintf(pos, end - pos, "[P2P]");
+ if (ret < 0 || ret >= end - pos)
+ return 0;
+ pos += ret;
+ }
+#ifdef CONFIG_HS20
+ if (wpa_bss_get_vendor_ie(bss, HS20_IE_VENDOR_TYPE)) {
+ ret = os_snprintf(pos, end - pos, "[HS20]");
+ if (ret < 0 || ret >= end - pos)
+ return -1;
+ pos += ret;
+ }
+#endif /* CONFIG_HS20 */
+
+ ret = os_snprintf(pos, end - pos, "\n");
+ if (ret < 0 || ret >= end - pos)
+ return 0;
+ pos += ret;
+ }
+
+ if (mask & WPA_BSS_MASK_SSID) {
+ ret = os_snprintf(pos, end - pos, "ssid=%s\n",
+ wpa_ssid_txt(bss->ssid, bss->ssid_len));
+ if (ret < 0 || ret >= end - pos)
+ return 0;
+ pos += ret;
+ }
+
+#ifdef CONFIG_WPS
+ if (mask & WPA_BSS_MASK_WPS_SCAN) {
+ ie = (const u8 *) (bss + 1);
+ ret = wpas_wps_scan_result_text(ie, bss->ie_len, pos, end);
+ if (ret < 0 || ret >= end - pos)
+ return 0;
+ pos += ret;
+ }
+#endif /* CONFIG_WPS */
+
+#ifdef CONFIG_P2P
+ if (mask & WPA_BSS_MASK_P2P_SCAN) {
+ ie = (const u8 *) (bss + 1);
+ ret = wpas_p2p_scan_result_text(ie, bss->ie_len, pos, end);
+ if (ret < 0 || ret >= end - pos)
+ return 0;
+ pos += ret;
+ }
+#endif /* CONFIG_P2P */
+
+#ifdef CONFIG_WIFI_DISPLAY
+ if (mask & WPA_BSS_MASK_WIFI_DISPLAY) {
+ struct wpabuf *wfd;
+ ie = (const u8 *) (bss + 1);
+ wfd = ieee802_11_vendor_ie_concat(ie, bss->ie_len,
+ WFD_IE_VENDOR_TYPE);
+ if (wfd) {
+ ret = os_snprintf(pos, end - pos, "wfd_subelems=");
+ if (ret < 0 || ret >= end - pos)
+ return pos - buf;
+ pos += ret;
+
+ pos += wpa_snprintf_hex(pos, end - pos,
+ wpabuf_head(wfd),
+ wpabuf_len(wfd));
+ wpabuf_free(wfd);
+
+ ret = os_snprintf(pos, end - pos, "\n");
+ if (ret < 0 || ret >= end - pos)
+ return pos - buf;
+ pos += ret;
+ }
+ }
+#endif /* CONFIG_WIFI_DISPLAY */
+
+#ifdef CONFIG_INTERWORKING
+ if ((mask & WPA_BSS_MASK_INTERNETW) && bss->anqp) {
+ struct wpa_bss_anqp *anqp = bss->anqp;
+ pos = anqp_add_hex(pos, end, "anqp_venue_name",
+ anqp->venue_name);
+ pos = anqp_add_hex(pos, end, "anqp_network_auth_type",
+ anqp->network_auth_type);
+ pos = anqp_add_hex(pos, end, "anqp_roaming_consortium",
+ anqp->roaming_consortium);
+ pos = anqp_add_hex(pos, end, "anqp_ip_addr_type_availability",
+ anqp->ip_addr_type_availability);
+ pos = anqp_add_hex(pos, end, "anqp_nai_realm",
+ anqp->nai_realm);
+ pos = anqp_add_hex(pos, end, "anqp_3gpp", anqp->anqp_3gpp);
+ pos = anqp_add_hex(pos, end, "anqp_domain_name",
+ anqp->domain_name);
+#ifdef CONFIG_HS20
+ pos = anqp_add_hex(pos, end, "hs20_operator_friendly_name",
+ anqp->hs20_operator_friendly_name);
+ pos = anqp_add_hex(pos, end, "hs20_wan_metrics",
+ anqp->hs20_wan_metrics);
+ pos = anqp_add_hex(pos, end, "hs20_connection_capability",
+ anqp->hs20_connection_capability);
+#endif /* CONFIG_HS20 */
+ }
+#endif /* CONFIG_INTERWORKING */
+
+ return pos - buf;
+}
+
+
static int wpa_supplicant_ctrl_iface_bss(struct wpa_supplicant *wpa_s,
const char *cmd, char *buf,
size_t buflen)
@@ -1489,12 +3156,55 @@ static int wpa_supplicant_ctrl_iface_bss(struct wpa_supplicant *wpa_s,
u8 bssid[ETH_ALEN];
size_t i;
struct wpa_bss *bss;
- int ret;
- char *pos, *end;
- const u8 *ie, *ie2;
+ struct wpa_bss *bsslast = NULL;
+ struct dl_list *next;
+ int ret = 0;
+ int len;
+ char *ctmp;
+ unsigned long mask = WPA_BSS_MASK_ALL;
+
+ if (os_strncmp(cmd, "RANGE=", 6) == 0) {
+ if (os_strncmp(cmd + 6, "ALL", 3) == 0) {
+ bss = dl_list_first(&wpa_s->bss_id, struct wpa_bss,
+ list_id);
+ bsslast = dl_list_last(&wpa_s->bss_id, struct wpa_bss,
+ list_id);
+ } else { /* N1-N2 */
+ unsigned int id1, id2;
+
+ if ((ctmp = os_strchr(cmd + 6, '-')) == NULL) {
+ wpa_printf(MSG_INFO, "Wrong BSS range "
+ "format");
+ return 0;
+ }
- if (os_strcmp(cmd, "FIRST") == 0)
- bss = dl_list_first(&wpa_s->bss, struct wpa_bss, list);
+ id1 = atoi(cmd + 6);
+ bss = wpa_bss_get_id(wpa_s, id1);
+ id2 = atoi(ctmp + 1);
+ if (id2 == 0)
+ bsslast = dl_list_last(&wpa_s->bss_id,
+ struct wpa_bss,
+ list_id);
+ else {
+ bsslast = wpa_bss_get_id(wpa_s, id2);
+ if (bsslast == NULL && bss && id2 > id1) {
+ struct wpa_bss *tmp = bss;
+ for (;;) {
+ next = tmp->list_id.next;
+ if (next == &wpa_s->bss_id)
+ break;
+ tmp = dl_list_entry(
+ next, struct wpa_bss,
+ list_id);
+ if (tmp->id > id2)
+ break;
+ bsslast = tmp;
+ }
+ }
+ }
+ }
+ } else if (os_strcmp(cmd, "FIRST") == 0)
+ bss = dl_list_first(&wpa_s->bss_id, struct wpa_bss, list_id);
else if (os_strncmp(cmd, "ID-", 3) == 0) {
i = atoi(cmd + 3);
bss = wpa_bss_get_id(wpa_s, i);
@@ -1502,13 +3212,20 @@ static int wpa_supplicant_ctrl_iface_bss(struct wpa_supplicant *wpa_s,
i = atoi(cmd + 5);
bss = wpa_bss_get_id(wpa_s, i);
if (bss) {
- struct dl_list *next = bss->list_id.next;
+ next = bss->list_id.next;
if (next == &wpa_s->bss_id)
bss = NULL;
else
bss = dl_list_entry(next, struct wpa_bss,
list_id);
}
+#ifdef CONFIG_P2P
+ } else if (os_strncmp(cmd, "p2p_dev_addr=", 13) == 0) {
+ if (hwaddr_aton(cmd + 13, bssid) == 0)
+ bss = wpa_bss_get_p2p_dev_addr(wpa_s, bssid);
+ else
+ bss = NULL;
+#endif /* CONFIG_P2P */
} else if (hwaddr_aton(cmd, bssid) == 0)
bss = wpa_bss_get_bssid(wpa_s, bssid);
else {
@@ -1524,118 +3241,90 @@ static int wpa_supplicant_ctrl_iface_bss(struct wpa_supplicant *wpa_s,
}
}
+ if ((ctmp = os_strstr(cmd, "MASK=")) != NULL) {
+ mask = strtoul(ctmp + 5, NULL, 0x10);
+ if (mask == 0)
+ mask = WPA_BSS_MASK_ALL;
+ }
+
if (bss == NULL)
return 0;
- pos = buf;
- end = buf + buflen;
- ret = os_snprintf(pos, end - pos,
- "id=%u\n"
- "bssid=" MACSTR "\n"
- "freq=%d\n"
- "beacon_int=%d\n"
- "capabilities=0x%04x\n"
- "qual=%d\n"
- "noise=%d\n"
- "level=%d\n"
- "tsf=%016llu\n"
- "ie=",
- bss->id,
- MAC2STR(bss->bssid), bss->freq, bss->beacon_int,
- bss->caps, bss->qual, bss->noise, bss->level,
- (unsigned long long) bss->tsf);
- if (ret < 0 || ret >= end - pos)
- return pos - buf;
- pos += ret;
+ if (bsslast == NULL)
+ bsslast = bss;
+ do {
+ len = print_bss_info(wpa_s, bss, mask, buf, buflen);
+ ret += len;
+ buf += len;
+ buflen -= len;
+ if (bss == bsslast)
+ break;
+ next = bss->list_id.next;
+ if (next == &wpa_s->bss_id)
+ break;
+ bss = dl_list_entry(next, struct wpa_bss, list_id);
+ } while (bss && len);
- ie = (const u8 *) (bss + 1);
- for (i = 0; i < bss->ie_len; i++) {
- ret = os_snprintf(pos, end - pos, "%02x", *ie++);
- if (ret < 0 || ret >= end - pos)
- return pos - buf;
- pos += ret;
- }
+ return ret;
+}
- ret = os_snprintf(pos, end - pos, "\n");
- if (ret < 0 || ret >= end - pos)
- return pos - buf;
- pos += ret;
- ret = os_snprintf(pos, end - pos, "flags=");
- if (ret < 0 || ret >= end - pos)
- return pos - buf;
- pos += ret;
+static int wpa_supplicant_ctrl_iface_ap_scan(
+ struct wpa_supplicant *wpa_s, char *cmd)
+{
+ int ap_scan = atoi(cmd);
+ return wpa_supplicant_set_ap_scan(wpa_s, ap_scan);
+}
- ie = wpa_bss_get_vendor_ie(bss, WPA_IE_VENDOR_TYPE);
- if (ie)
- pos = wpa_supplicant_ie_txt(pos, end, "WPA", ie, 2 + ie[1]);
- ie2 = wpa_bss_get_ie(bss, WLAN_EID_RSN);
- if (ie2)
- pos = wpa_supplicant_ie_txt(pos, end, "WPA2", ie2, 2 + ie2[1]);
- pos = wpa_supplicant_wps_ie_txt(pos, end, bss);
- if (!ie && !ie2 && bss->caps & IEEE80211_CAP_PRIVACY) {
- ret = os_snprintf(pos, end - pos, "[WEP]");
- if (ret < 0 || ret >= end - pos)
- return pos - buf;
- pos += ret;
- }
- if (bss->caps & IEEE80211_CAP_IBSS) {
- ret = os_snprintf(pos, end - pos, "[IBSS]");
- if (ret < 0 || ret >= end - pos)
- return pos - buf;
- pos += ret;
- }
- if (bss->caps & IEEE80211_CAP_ESS) {
- ret = os_snprintf(pos, end - pos, "[ESS]");
- if (ret < 0 || ret >= end - pos)
- return pos - buf;
- pos += ret;
- }
- ret = os_snprintf(pos, end - pos, "\n");
- if (ret < 0 || ret >= end - pos)
- return pos - buf;
- pos += ret;
+static int wpa_supplicant_ctrl_iface_scan_interval(
+ struct wpa_supplicant *wpa_s, char *cmd)
+{
+ int scan_int = atoi(cmd);
+ return wpa_supplicant_set_scan_interval(wpa_s, scan_int);
+}
- ret = os_snprintf(pos, end - pos, "ssid=%s\n",
- wpa_ssid_txt(bss->ssid, bss->ssid_len));
- if (ret < 0 || ret >= end - pos)
- return pos - buf;
- pos += ret;
-#ifdef CONFIG_WPS
- ie = (const u8 *) (bss + 1);
- ret = wpas_wps_scan_result_text(ie, bss->ie_len, pos, end);
- if (ret < 0 || ret >= end - pos)
- return pos - buf;
- pos += ret;
-#endif /* CONFIG_WPS */
+static int wpa_supplicant_ctrl_iface_bss_expire_age(
+ struct wpa_supplicant *wpa_s, char *cmd)
+{
+ int expire_age = atoi(cmd);
+ return wpa_supplicant_set_bss_expiration_age(wpa_s, expire_age);
+}
- return pos - buf;
+
+static int wpa_supplicant_ctrl_iface_bss_expire_count(
+ struct wpa_supplicant *wpa_s, char *cmd)
+{
+ int expire_count = atoi(cmd);
+ return wpa_supplicant_set_bss_expiration_count(wpa_s, expire_count);
}
-static int wpa_supplicant_ctrl_iface_ap_scan(
+static int wpa_supplicant_ctrl_iface_bss_flush(
struct wpa_supplicant *wpa_s, char *cmd)
{
- int ap_scan = atoi(cmd);
- return wpa_supplicant_set_ap_scan(wpa_s, ap_scan);
+ int flush_age = atoi(cmd);
+
+ if (flush_age == 0)
+ wpa_bss_flush(wpa_s);
+ else
+ wpa_bss_flush_by_age(wpa_s, flush_age);
+ return 0;
}
static void wpa_supplicant_ctrl_iface_drop_sa(struct wpa_supplicant *wpa_s)
{
- u8 *bcast = (u8 *) "\xff\xff\xff\xff\xff\xff";
-
wpa_printf(MSG_DEBUG, "Dropping SA without deauthentication");
/* MLME-DELETEKEYS.request */
- wpa_drv_set_key(wpa_s, WPA_ALG_NONE, bcast, 0, 0, NULL, 0, NULL, 0);
- wpa_drv_set_key(wpa_s, WPA_ALG_NONE, bcast, 1, 0, NULL, 0, NULL, 0);
- wpa_drv_set_key(wpa_s, WPA_ALG_NONE, bcast, 2, 0, NULL, 0, NULL, 0);
- wpa_drv_set_key(wpa_s, WPA_ALG_NONE, bcast, 3, 0, NULL, 0, NULL, 0);
+ wpa_drv_set_key(wpa_s, WPA_ALG_NONE, NULL, 0, 0, NULL, 0, NULL, 0);
+ wpa_drv_set_key(wpa_s, WPA_ALG_NONE, NULL, 1, 0, NULL, 0, NULL, 0);
+ wpa_drv_set_key(wpa_s, WPA_ALG_NONE, NULL, 2, 0, NULL, 0, NULL, 0);
+ wpa_drv_set_key(wpa_s, WPA_ALG_NONE, NULL, 3, 0, NULL, 0, NULL, 0);
#ifdef CONFIG_IEEE80211W
- wpa_drv_set_key(wpa_s, WPA_ALG_NONE, bcast, 4, 0, NULL, 0, NULL, 0);
- wpa_drv_set_key(wpa_s, WPA_ALG_NONE, bcast, 5, 0, NULL, 0, NULL, 0);
+ wpa_drv_set_key(wpa_s, WPA_ALG_NONE, NULL, 4, 0, NULL, 0, NULL, 0);
+ wpa_drv_set_key(wpa_s, WPA_ALG_NONE, NULL, 5, 0, NULL, 0, NULL, 0);
#endif /* CONFIG_IEEE80211W */
wpa_drv_set_key(wpa_s, WPA_ALG_NONE, wpa_s->bssid, 0, 0, NULL, 0, NULL,
@@ -1651,6 +3340,9 @@ static void wpa_supplicant_ctrl_iface_drop_sa(struct wpa_supplicant *wpa_s)
static int wpa_supplicant_ctrl_iface_roam(struct wpa_supplicant *wpa_s,
char *addr)
{
+#ifdef CONFIG_NO_SCAN_PROCESSING
+ return -1;
+#else /* CONFIG_NO_SCAN_PROCESSING */
u8 bssid[ETH_ALEN];
struct wpa_bss *bss;
struct wpa_ssid *ssid = wpa_s->current_ssid;
@@ -1685,6 +3377,1406 @@ static int wpa_supplicant_ctrl_iface_roam(struct wpa_supplicant *wpa_s,
wpa_supplicant_connect(wpa_s, bss, ssid);
return 0;
+#endif /* CONFIG_NO_SCAN_PROCESSING */
+}
+
+
+#ifdef CONFIG_P2P
+static int p2p_ctrl_find(struct wpa_supplicant *wpa_s, char *cmd)
+{
+ unsigned int timeout = atoi(cmd);
+ enum p2p_discovery_type type = P2P_FIND_START_WITH_FULL;
+ u8 dev_id[ETH_ALEN], *_dev_id = NULL;
+ char *pos;
+ unsigned int search_delay;
+
+ if (os_strstr(cmd, "type=social"))
+ type = P2P_FIND_ONLY_SOCIAL;
+ else if (os_strstr(cmd, "type=progressive"))
+ type = P2P_FIND_PROGRESSIVE;
+
+ pos = os_strstr(cmd, "dev_id=");
+ if (pos) {
+ pos += 7;
+ if (hwaddr_aton(pos, dev_id))
+ return -1;
+ _dev_id = dev_id;
+ }
+
+ pos = os_strstr(cmd, "delay=");
+ if (pos) {
+ pos += 6;
+ search_delay = atoi(pos);
+ } else
+ search_delay = wpas_p2p_search_delay(wpa_s);
+
+ return wpas_p2p_find(wpa_s, timeout, type, 0, NULL, _dev_id,
+ search_delay);
+}
+
+
+static int p2p_ctrl_connect(struct wpa_supplicant *wpa_s, char *cmd,
+ char *buf, size_t buflen)
+{
+ u8 addr[ETH_ALEN];
+ char *pos, *pos2;
+ char *pin = NULL;
+ enum p2p_wps_method wps_method;
+ int new_pin;
+ int ret;
+ int persistent_group, persistent_id = -1;
+ int join;
+ int auth;
+ int automatic;
+ int go_intent = -1;
+ int freq = 0;
+ int pd;
+ int ht40;
+
+ /* <addr> <"pbc" | "pin" | PIN> [label|display|keypad]
+ * [persistent|persistent=<network id>]
+ * [join] [auth] [go_intent=<0..15>] [freq=<in MHz>] [provdisc]
+ * [ht40] */
+
+ if (hwaddr_aton(cmd, addr))
+ return -1;
+
+ pos = cmd + 17;
+ if (*pos != ' ')
+ return -1;
+ pos++;
+
+ persistent_group = os_strstr(pos, " persistent") != NULL;
+ pos2 = os_strstr(pos, " persistent=");
+ if (pos2) {
+ struct wpa_ssid *ssid;
+ persistent_id = atoi(pos2 + 12);
+ ssid = wpa_config_get_network(wpa_s->conf, persistent_id);
+ if (ssid == NULL || ssid->disabled != 2 ||
+ ssid->mode != WPAS_MODE_P2P_GO) {
+ wpa_printf(MSG_DEBUG, "CTRL_IFACE: Could not find "
+ "SSID id=%d for persistent P2P group (GO)",
+ persistent_id);
+ return -1;
+ }
+ }
+ join = os_strstr(pos, " join") != NULL;
+ auth = os_strstr(pos, " auth") != NULL;
+ automatic = os_strstr(pos, " auto") != NULL;
+ pd = os_strstr(pos, " provdisc") != NULL;
+ ht40 = (os_strstr(cmd, " ht40") != NULL) || wpa_s->conf->p2p_go_ht40;
+
+ pos2 = os_strstr(pos, " go_intent=");
+ if (pos2) {
+ pos2 += 11;
+ go_intent = atoi(pos2);
+ if (go_intent < 0 || go_intent > 15)
+ return -1;
+ }
+
+ pos2 = os_strstr(pos, " freq=");
+ if (pos2) {
+ pos2 += 6;
+ freq = atoi(pos2);
+ if (freq <= 0)
+ return -1;
+ }
+
+ if (os_strncmp(pos, "pin", 3) == 0) {
+ /* Request random PIN (to be displayed) and enable the PIN */
+ wps_method = WPS_PIN_DISPLAY;
+ } else if (os_strncmp(pos, "pbc", 3) == 0) {
+ wps_method = WPS_PBC;
+ } else {
+ pin = pos;
+ pos = os_strchr(pin, ' ');
+ wps_method = WPS_PIN_KEYPAD;
+ if (pos) {
+ *pos++ = '\0';
+ if (os_strncmp(pos, "display", 7) == 0)
+ wps_method = WPS_PIN_DISPLAY;
+ }
+ if (!wps_pin_str_valid(pin)) {
+ os_memcpy(buf, "FAIL-INVALID-PIN\n", 17);
+ return 17;
+ }
+ }
+
+ new_pin = wpas_p2p_connect(wpa_s, addr, pin, wps_method,
+ persistent_group, automatic, join,
+ auth, go_intent, freq, persistent_id, pd,
+ ht40);
+ if (new_pin == -2) {
+ os_memcpy(buf, "FAIL-CHANNEL-UNAVAILABLE\n", 25);
+ return 25;
+ }
+ if (new_pin == -3) {
+ os_memcpy(buf, "FAIL-CHANNEL-UNSUPPORTED\n", 25);
+ return 25;
+ }
+ if (new_pin < 0)
+ return -1;
+ if (wps_method == WPS_PIN_DISPLAY && pin == NULL) {
+ ret = os_snprintf(buf, buflen, "%08d", new_pin);
+ if (ret < 0 || (size_t) ret >= buflen)
+ return -1;
+ return ret;
+ }
+
+ os_memcpy(buf, "OK\n", 3);
+ return 3;
+}
+
+
+static int p2p_ctrl_listen(struct wpa_supplicant *wpa_s, char *cmd)
+{
+ unsigned int timeout = atoi(cmd);
+ return wpas_p2p_listen(wpa_s, timeout);
+}
+
+
+static int p2p_ctrl_prov_disc(struct wpa_supplicant *wpa_s, char *cmd)
+{
+ u8 addr[ETH_ALEN];
+ char *pos;
+ enum wpas_p2p_prov_disc_use use = WPAS_P2P_PD_FOR_GO_NEG;
+
+ /* <addr> <config method> [join|auto] */
+
+ if (hwaddr_aton(cmd, addr))
+ return -1;
+
+ pos = cmd + 17;
+ if (*pos != ' ')
+ return -1;
+ pos++;
+
+ if (os_strstr(pos, " join") != NULL)
+ use = WPAS_P2P_PD_FOR_JOIN;
+ else if (os_strstr(pos, " auto") != NULL)
+ use = WPAS_P2P_PD_AUTO;
+
+ return wpas_p2p_prov_disc(wpa_s, addr, pos, use);
+}
+
+
+static int p2p_get_passphrase(struct wpa_supplicant *wpa_s, char *buf,
+ size_t buflen)
+{
+ struct wpa_ssid *ssid = wpa_s->current_ssid;
+
+ if (ssid == NULL || ssid->mode != WPAS_MODE_P2P_GO ||
+ ssid->passphrase == NULL)
+ return -1;
+
+ os_strlcpy(buf, ssid->passphrase, buflen);
+ return os_strlen(buf);
+}
+
+
+static int p2p_ctrl_serv_disc_req(struct wpa_supplicant *wpa_s, char *cmd,
+ char *buf, size_t buflen)
+{
+ u64 ref;
+ int res;
+ u8 dst_buf[ETH_ALEN], *dst;
+ struct wpabuf *tlvs;
+ char *pos;
+ size_t len;
+
+ if (hwaddr_aton(cmd, dst_buf))
+ return -1;
+ dst = dst_buf;
+ if (dst[0] == 0 && dst[1] == 0 && dst[2] == 0 &&
+ dst[3] == 0 && dst[4] == 0 && dst[5] == 0)
+ dst = NULL;
+ pos = cmd + 17;
+ if (*pos != ' ')
+ return -1;
+ pos++;
+
+ if (os_strncmp(pos, "upnp ", 5) == 0) {
+ u8 version;
+ pos += 5;
+ if (hexstr2bin(pos, &version, 1) < 0)
+ return -1;
+ pos += 2;
+ if (*pos != ' ')
+ return -1;
+ pos++;
+ ref = wpas_p2p_sd_request_upnp(wpa_s, dst, version, pos);
+#ifdef CONFIG_WIFI_DISPLAY
+ } else if (os_strncmp(pos, "wifi-display ", 13) == 0) {
+ ref = wpas_p2p_sd_request_wifi_display(wpa_s, dst, pos + 13);
+#endif /* CONFIG_WIFI_DISPLAY */
+ } else {
+ len = os_strlen(pos);
+ if (len & 1)
+ return -1;
+ len /= 2;
+ tlvs = wpabuf_alloc(len);
+ if (tlvs == NULL)
+ return -1;
+ if (hexstr2bin(pos, wpabuf_put(tlvs, len), len) < 0) {
+ wpabuf_free(tlvs);
+ return -1;
+ }
+
+ ref = wpas_p2p_sd_request(wpa_s, dst, tlvs);
+ wpabuf_free(tlvs);
+ }
+ if (ref == 0)
+ return -1;
+ res = os_snprintf(buf, buflen, "%llx", (long long unsigned) ref);
+ if (res < 0 || (unsigned) res >= buflen)
+ return -1;
+ return res;
+}
+
+
+static int p2p_ctrl_serv_disc_cancel_req(struct wpa_supplicant *wpa_s,
+ char *cmd)
+{
+ long long unsigned val;
+ u64 req;
+ if (sscanf(cmd, "%llx", &val) != 1)
+ return -1;
+ req = val;
+ return wpas_p2p_sd_cancel_request(wpa_s, req);
+}
+
+
+static int p2p_ctrl_serv_disc_resp(struct wpa_supplicant *wpa_s, char *cmd)
+{
+ int freq;
+ u8 dst[ETH_ALEN];
+ u8 dialog_token;
+ struct wpabuf *resp_tlvs;
+ char *pos, *pos2;
+ size_t len;
+
+ pos = os_strchr(cmd, ' ');
+ if (pos == NULL)
+ return -1;
+ *pos++ = '\0';
+ freq = atoi(cmd);
+ if (freq == 0)
+ return -1;
+
+ if (hwaddr_aton(pos, dst))
+ return -1;
+ pos += 17;
+ if (*pos != ' ')
+ return -1;
+ pos++;
+
+ pos2 = os_strchr(pos, ' ');
+ if (pos2 == NULL)
+ return -1;
+ *pos2++ = '\0';
+ dialog_token = atoi(pos);
+
+ len = os_strlen(pos2);
+ if (len & 1)
+ return -1;
+ len /= 2;
+ resp_tlvs = wpabuf_alloc(len);
+ if (resp_tlvs == NULL)
+ return -1;
+ if (hexstr2bin(pos2, wpabuf_put(resp_tlvs, len), len) < 0) {
+ wpabuf_free(resp_tlvs);
+ return -1;
+ }
+
+ wpas_p2p_sd_response(wpa_s, freq, dst, dialog_token, resp_tlvs);
+ wpabuf_free(resp_tlvs);
+ return 0;
+}
+
+
+static int p2p_ctrl_serv_disc_external(struct wpa_supplicant *wpa_s,
+ char *cmd)
+{
+ if (os_strcmp(cmd, "0") && os_strcmp(cmd, "1"))
+ return -1;
+ wpa_s->p2p_sd_over_ctrl_iface = atoi(cmd);
+ return 0;
+}
+
+
+static int p2p_ctrl_service_add_bonjour(struct wpa_supplicant *wpa_s,
+ char *cmd)
+{
+ char *pos;
+ size_t len;
+ struct wpabuf *query, *resp;
+
+ pos = os_strchr(cmd, ' ');
+ if (pos == NULL)
+ return -1;
+ *pos++ = '\0';
+
+ len = os_strlen(cmd);
+ if (len & 1)
+ return -1;
+ len /= 2;
+ query = wpabuf_alloc(len);
+ if (query == NULL)
+ return -1;
+ if (hexstr2bin(cmd, wpabuf_put(query, len), len) < 0) {
+ wpabuf_free(query);
+ return -1;
+ }
+
+ len = os_strlen(pos);
+ if (len & 1) {
+ wpabuf_free(query);
+ return -1;
+ }
+ len /= 2;
+ resp = wpabuf_alloc(len);
+ if (resp == NULL) {
+ wpabuf_free(query);
+ return -1;
+ }
+ if (hexstr2bin(pos, wpabuf_put(resp, len), len) < 0) {
+ wpabuf_free(query);
+ wpabuf_free(resp);
+ return -1;
+ }
+
+ if (wpas_p2p_service_add_bonjour(wpa_s, query, resp) < 0) {
+ wpabuf_free(query);
+ wpabuf_free(resp);
+ return -1;
+ }
+ return 0;
+}
+
+
+static int p2p_ctrl_service_add_upnp(struct wpa_supplicant *wpa_s, char *cmd)
+{
+ char *pos;
+ u8 version;
+
+ pos = os_strchr(cmd, ' ');
+ if (pos == NULL)
+ return -1;
+ *pos++ = '\0';
+
+ if (hexstr2bin(cmd, &version, 1) < 0)
+ return -1;
+
+ return wpas_p2p_service_add_upnp(wpa_s, version, pos);
+}
+
+
+static int p2p_ctrl_service_add(struct wpa_supplicant *wpa_s, char *cmd)
+{
+ char *pos;
+
+ pos = os_strchr(cmd, ' ');
+ if (pos == NULL)
+ return -1;
+ *pos++ = '\0';
+
+ if (os_strcmp(cmd, "bonjour") == 0)
+ return p2p_ctrl_service_add_bonjour(wpa_s, pos);
+ if (os_strcmp(cmd, "upnp") == 0)
+ return p2p_ctrl_service_add_upnp(wpa_s, pos);
+ wpa_printf(MSG_DEBUG, "Unknown service '%s'", cmd);
+ return -1;
+}
+
+
+static int p2p_ctrl_service_del_bonjour(struct wpa_supplicant *wpa_s,
+ char *cmd)
+{
+ size_t len;
+ struct wpabuf *query;
+ int ret;
+
+ len = os_strlen(cmd);
+ if (len & 1)
+ return -1;
+ len /= 2;
+ query = wpabuf_alloc(len);
+ if (query == NULL)
+ return -1;
+ if (hexstr2bin(cmd, wpabuf_put(query, len), len) < 0) {
+ wpabuf_free(query);
+ return -1;
+ }
+
+ ret = wpas_p2p_service_del_bonjour(wpa_s, query);
+ wpabuf_free(query);
+ return ret;
+}
+
+
+static int p2p_ctrl_service_del_upnp(struct wpa_supplicant *wpa_s, char *cmd)
+{
+ char *pos;
+ u8 version;
+
+ pos = os_strchr(cmd, ' ');
+ if (pos == NULL)
+ return -1;
+ *pos++ = '\0';
+
+ if (hexstr2bin(cmd, &version, 1) < 0)
+ return -1;
+
+ return wpas_p2p_service_del_upnp(wpa_s, version, pos);
+}
+
+
+static int p2p_ctrl_service_del(struct wpa_supplicant *wpa_s, char *cmd)
+{
+ char *pos;
+
+ pos = os_strchr(cmd, ' ');
+ if (pos == NULL)
+ return -1;
+ *pos++ = '\0';
+
+ if (os_strcmp(cmd, "bonjour") == 0)
+ return p2p_ctrl_service_del_bonjour(wpa_s, pos);
+ if (os_strcmp(cmd, "upnp") == 0)
+ return p2p_ctrl_service_del_upnp(wpa_s, pos);
+ wpa_printf(MSG_DEBUG, "Unknown service '%s'", cmd);
+ return -1;
+}
+
+
+static int p2p_ctrl_reject(struct wpa_supplicant *wpa_s, char *cmd)
+{
+ u8 addr[ETH_ALEN];
+
+ /* <addr> */
+
+ if (hwaddr_aton(cmd, addr))
+ return -1;
+
+ return wpas_p2p_reject(wpa_s, addr);
+}
+
+
+static int p2p_ctrl_invite_persistent(struct wpa_supplicant *wpa_s, char *cmd)
+{
+ char *pos;
+ int id;
+ struct wpa_ssid *ssid;
+ u8 *_peer = NULL, peer[ETH_ALEN];
+ int freq = 0;
+ int ht40;
+
+ id = atoi(cmd);
+ pos = os_strstr(cmd, " peer=");
+ if (pos) {
+ pos += 6;
+ if (hwaddr_aton(pos, peer))
+ return -1;
+ _peer = peer;
+ }
+ ssid = wpa_config_get_network(wpa_s->conf, id);
+ if (ssid == NULL || ssid->disabled != 2) {
+ wpa_printf(MSG_DEBUG, "CTRL_IFACE: Could not find SSID id=%d "
+ "for persistent P2P group",
+ id);
+ return -1;
+ }
+
+ pos = os_strstr(cmd, " freq=");
+ if (pos) {
+ pos += 6;
+ freq = atoi(pos);
+ if (freq <= 0)
+ return -1;
+ }
+
+ ht40 = (os_strstr(cmd, " ht40") != NULL) || wpa_s->conf->p2p_go_ht40;
+
+ return wpas_p2p_invite(wpa_s, _peer, ssid, NULL, freq, ht40);
+}
+
+
+static int p2p_ctrl_invite_group(struct wpa_supplicant *wpa_s, char *cmd)
+{
+ char *pos;
+ u8 peer[ETH_ALEN], go_dev_addr[ETH_ALEN], *go_dev = NULL;
+
+ pos = os_strstr(cmd, " peer=");
+ if (!pos)
+ return -1;
+
+ *pos = '\0';
+ pos += 6;
+ if (hwaddr_aton(pos, peer)) {
+ wpa_printf(MSG_DEBUG, "P2P: Invalid MAC address '%s'", pos);
+ return -1;
+ }
+
+ pos = os_strstr(pos, " go_dev_addr=");
+ if (pos) {
+ pos += 13;
+ if (hwaddr_aton(pos, go_dev_addr)) {
+ wpa_printf(MSG_DEBUG, "P2P: Invalid MAC address '%s'",
+ pos);
+ return -1;
+ }
+ go_dev = go_dev_addr;
+ }
+
+ return wpas_p2p_invite_group(wpa_s, cmd, peer, go_dev);
+}
+
+
+static int p2p_ctrl_invite(struct wpa_supplicant *wpa_s, char *cmd)
+{
+ if (os_strncmp(cmd, "persistent=", 11) == 0)
+ return p2p_ctrl_invite_persistent(wpa_s, cmd + 11);
+ if (os_strncmp(cmd, "group=", 6) == 0)
+ return p2p_ctrl_invite_group(wpa_s, cmd + 6);
+
+ return -1;
+}
+
+
+static int p2p_ctrl_group_add_persistent(struct wpa_supplicant *wpa_s,
+ char *cmd, int freq, int ht40)
+{
+ int id;
+ struct wpa_ssid *ssid;
+
+ id = atoi(cmd);
+ ssid = wpa_config_get_network(wpa_s->conf, id);
+ if (ssid == NULL || ssid->disabled != 2) {
+ wpa_printf(MSG_DEBUG, "CTRL_IFACE: Could not find SSID id=%d "
+ "for persistent P2P group",
+ id);
+ return -1;
+ }
+
+ return wpas_p2p_group_add_persistent(wpa_s, ssid, 0, freq, ht40);
+}
+
+
+static int p2p_ctrl_group_add(struct wpa_supplicant *wpa_s, char *cmd)
+{
+ int freq = 0, ht40;
+ char *pos;
+
+ pos = os_strstr(cmd, "freq=");
+ if (pos)
+ freq = atoi(pos + 5);
+
+ ht40 = (os_strstr(cmd, "ht40") != NULL) || wpa_s->conf->p2p_go_ht40;
+
+ if (os_strncmp(cmd, "persistent=", 11) == 0)
+ return p2p_ctrl_group_add_persistent(wpa_s, cmd + 11, freq,
+ ht40);
+ if (os_strcmp(cmd, "persistent") == 0 ||
+ os_strncmp(cmd, "persistent ", 11) == 0)
+ return wpas_p2p_group_add(wpa_s, 1, freq, ht40);
+ if (os_strncmp(cmd, "freq=", 5) == 0)
+ return wpas_p2p_group_add(wpa_s, 0, freq, ht40);
+ if (ht40)
+ return wpas_p2p_group_add(wpa_s, 0, freq, ht40);
+
+ wpa_printf(MSG_DEBUG, "CTRL: Invalid P2P_GROUP_ADD parameters '%s'",
+ cmd);
+ return -1;
+}
+
+
+static int p2p_ctrl_peer(struct wpa_supplicant *wpa_s, char *cmd,
+ char *buf, size_t buflen)
+{
+ u8 addr[ETH_ALEN], *addr_ptr;
+ int next, res;
+ const struct p2p_peer_info *info;
+ char *pos, *end;
+ char devtype[WPS_DEV_TYPE_BUFSIZE];
+ struct wpa_ssid *ssid;
+ size_t i;
+
+ if (!wpa_s->global->p2p)
+ return -1;
+
+ if (os_strcmp(cmd, "FIRST") == 0) {
+ addr_ptr = NULL;
+ next = 0;
+ } else if (os_strncmp(cmd, "NEXT-", 5) == 0) {
+ if (hwaddr_aton(cmd + 5, addr) < 0)
+ return -1;
+ addr_ptr = addr;
+ next = 1;
+ } else {
+ if (hwaddr_aton(cmd, addr) < 0)
+ return -1;
+ addr_ptr = addr;
+ next = 0;
+ }
+
+ info = p2p_get_peer_info(wpa_s->global->p2p, addr_ptr, next);
+ if (info == NULL)
+ return -1;
+
+ pos = buf;
+ end = buf + buflen;
+
+ res = os_snprintf(pos, end - pos, MACSTR "\n"
+ "pri_dev_type=%s\n"
+ "device_name=%s\n"
+ "manufacturer=%s\n"
+ "model_name=%s\n"
+ "model_number=%s\n"
+ "serial_number=%s\n"
+ "config_methods=0x%x\n"
+ "dev_capab=0x%x\n"
+ "group_capab=0x%x\n"
+ "level=%d\n",
+ MAC2STR(info->p2p_device_addr),
+ wps_dev_type_bin2str(info->pri_dev_type,
+ devtype, sizeof(devtype)),
+ info->device_name,
+ info->manufacturer,
+ info->model_name,
+ info->model_number,
+ info->serial_number,
+ info->config_methods,
+ info->dev_capab,
+ info->group_capab,
+ info->level);
+ if (res < 0 || res >= end - pos)
+ return pos - buf;
+ pos += res;
+
+ for (i = 0; i < info->wps_sec_dev_type_list_len / WPS_DEV_TYPE_LEN; i++)
+ {
+ const u8 *t;
+ t = &info->wps_sec_dev_type_list[i * WPS_DEV_TYPE_LEN];
+ res = os_snprintf(pos, end - pos, "sec_dev_type=%s\n",
+ wps_dev_type_bin2str(t, devtype,
+ sizeof(devtype)));
+ if (res < 0 || res >= end - pos)
+ return pos - buf;
+ pos += res;
+ }
+
+ ssid = wpas_p2p_get_persistent(wpa_s, info->p2p_device_addr, NULL, 0);
+ if (ssid) {
+ res = os_snprintf(pos, end - pos, "persistent=%d\n", ssid->id);
+ if (res < 0 || res >= end - pos)
+ return pos - buf;
+ pos += res;
+ }
+
+ res = p2p_get_peer_info_txt(info, pos, end - pos);
+ if (res < 0)
+ return pos - buf;
+ pos += res;
+
+ return pos - buf;
+}
+
+
+static int p2p_ctrl_disallow_freq(struct wpa_supplicant *wpa_s,
+ const char *param)
+{
+ struct wpa_freq_range *freq = NULL, *n;
+ unsigned int count = 0, i;
+ const char *pos, *pos2, *pos3;
+
+ if (wpa_s->global->p2p == NULL)
+ return -1;
+
+ /*
+ * param includes comma separated frequency range.
+ * For example: 2412-2432,2462,5000-6000
+ */
+ pos = param;
+ while (pos && pos[0]) {
+ n = os_realloc_array(freq, count + 1,
+ sizeof(struct wpa_freq_range));
+ if (n == NULL) {
+ os_free(freq);
+ return -1;
+ }
+ freq = n;
+ freq[count].min = atoi(pos);
+ pos2 = os_strchr(pos, '-');
+ pos3 = os_strchr(pos, ',');
+ if (pos2 && (!pos3 || pos2 < pos3)) {
+ pos2++;
+ freq[count].max = atoi(pos2);
+ } else
+ freq[count].max = freq[count].min;
+ pos = pos3;
+ if (pos)
+ pos++;
+ count++;
+ }
+
+ for (i = 0; i < count; i++) {
+ wpa_printf(MSG_DEBUG, "P2P: Disallowed frequency range %u-%u",
+ freq[i].min, freq[i].max);
+ }
+
+ os_free(wpa_s->global->p2p_disallow_freq);
+ wpa_s->global->p2p_disallow_freq = freq;
+ wpa_s->global->num_p2p_disallow_freq = count;
+ wpas_p2p_update_channel_list(wpa_s);
+ return 0;
+}
+
+
+static int p2p_ctrl_set(struct wpa_supplicant *wpa_s, char *cmd)
+{
+ char *param;
+
+ if (wpa_s->global->p2p == NULL)
+ return -1;
+
+ param = os_strchr(cmd, ' ');
+ if (param == NULL)
+ return -1;
+ *param++ = '\0';
+
+ if (os_strcmp(cmd, "discoverability") == 0) {
+ p2p_set_client_discoverability(wpa_s->global->p2p,
+ atoi(param));
+ return 0;
+ }
+
+ if (os_strcmp(cmd, "managed") == 0) {
+ p2p_set_managed_oper(wpa_s->global->p2p, atoi(param));
+ return 0;
+ }
+
+ if (os_strcmp(cmd, "listen_channel") == 0) {
+ return p2p_set_listen_channel(wpa_s->global->p2p, 81,
+ atoi(param));
+ }
+
+ if (os_strcmp(cmd, "ssid_postfix") == 0) {
+ return p2p_set_ssid_postfix(wpa_s->global->p2p, (u8 *) param,
+ os_strlen(param));
+ }
+
+ if (os_strcmp(cmd, "noa") == 0) {
+ char *pos;
+ int count, start, duration;
+ /* GO NoA parameters: count,start_offset(ms),duration(ms) */
+ count = atoi(param);
+ pos = os_strchr(param, ',');
+ if (pos == NULL)
+ return -1;
+ pos++;
+ start = atoi(pos);
+ pos = os_strchr(pos, ',');
+ if (pos == NULL)
+ return -1;
+ pos++;
+ duration = atoi(pos);
+ if (count < 0 || count > 255 || start < 0 || duration < 0)
+ return -1;
+ if (count == 0 && duration > 0)
+ return -1;
+ wpa_printf(MSG_DEBUG, "CTRL_IFACE: P2P_SET GO NoA: count=%d "
+ "start=%d duration=%d", count, start, duration);
+ return wpas_p2p_set_noa(wpa_s, count, start, duration);
+ }
+
+ if (os_strcmp(cmd, "ps") == 0)
+ return wpa_drv_set_p2p_powersave(wpa_s, atoi(param), -1, -1);
+
+ if (os_strcmp(cmd, "oppps") == 0)
+ return wpa_drv_set_p2p_powersave(wpa_s, -1, atoi(param), -1);
+
+ if (os_strcmp(cmd, "ctwindow") == 0)
+ return wpa_drv_set_p2p_powersave(wpa_s, -1, -1, atoi(param));
+
+ if (os_strcmp(cmd, "disabled") == 0) {
+ wpa_s->global->p2p_disabled = atoi(param);
+ wpa_printf(MSG_DEBUG, "P2P functionality %s",
+ wpa_s->global->p2p_disabled ?
+ "disabled" : "enabled");
+ if (wpa_s->global->p2p_disabled) {
+ wpas_p2p_stop_find(wpa_s);
+ os_memset(wpa_s->p2p_auth_invite, 0, ETH_ALEN);
+ p2p_flush(wpa_s->global->p2p);
+ }
+ return 0;
+ }
+
+ if (os_strcmp(cmd, "conc_pref") == 0) {
+ if (os_strcmp(param, "sta") == 0)
+ wpa_s->global->conc_pref = WPA_CONC_PREF_STA;
+ else if (os_strcmp(param, "p2p") == 0)
+ wpa_s->global->conc_pref = WPA_CONC_PREF_P2P;
+ else {
+ wpa_printf(MSG_INFO, "Invalid conc_pref value");
+ return -1;
+ }
+ wpa_printf(MSG_DEBUG, "Single channel concurrency preference: "
+ "%s", param);
+ return 0;
+ }
+
+ if (os_strcmp(cmd, "force_long_sd") == 0) {
+ wpa_s->force_long_sd = atoi(param);
+ return 0;
+ }
+
+ if (os_strcmp(cmd, "peer_filter") == 0) {
+ u8 addr[ETH_ALEN];
+ if (hwaddr_aton(param, addr))
+ return -1;
+ p2p_set_peer_filter(wpa_s->global->p2p, addr);
+ return 0;
+ }
+
+ if (os_strcmp(cmd, "cross_connect") == 0)
+ return wpas_p2p_set_cross_connect(wpa_s, atoi(param));
+
+ if (os_strcmp(cmd, "go_apsd") == 0) {
+ if (os_strcmp(param, "disable") == 0)
+ wpa_s->set_ap_uapsd = 0;
+ else {
+ wpa_s->set_ap_uapsd = 1;
+ wpa_s->ap_uapsd = atoi(param);
+ }
+ return 0;
+ }
+
+ if (os_strcmp(cmd, "client_apsd") == 0) {
+ if (os_strcmp(param, "disable") == 0)
+ wpa_s->set_sta_uapsd = 0;
+ else {
+ int be, bk, vi, vo;
+ char *pos;
+ /* format: BE,BK,VI,VO;max SP Length */
+ be = atoi(param);
+ pos = os_strchr(param, ',');
+ if (pos == NULL)
+ return -1;
+ pos++;
+ bk = atoi(pos);
+ pos = os_strchr(pos, ',');
+ if (pos == NULL)
+ return -1;
+ pos++;
+ vi = atoi(pos);
+ pos = os_strchr(pos, ',');
+ if (pos == NULL)
+ return -1;
+ pos++;
+ vo = atoi(pos);
+ /* ignore max SP Length for now */
+
+ wpa_s->set_sta_uapsd = 1;
+ wpa_s->sta_uapsd = 0;
+ if (be)
+ wpa_s->sta_uapsd |= BIT(0);
+ if (bk)
+ wpa_s->sta_uapsd |= BIT(1);
+ if (vi)
+ wpa_s->sta_uapsd |= BIT(2);
+ if (vo)
+ wpa_s->sta_uapsd |= BIT(3);
+ }
+ return 0;
+ }
+
+ if (os_strcmp(cmd, "disallow_freq") == 0)
+ return p2p_ctrl_disallow_freq(wpa_s, param);
+
+ if (os_strcmp(cmd, "disc_int") == 0) {
+ int min_disc_int, max_disc_int, max_disc_tu;
+ char *pos;
+
+ pos = param;
+
+ min_disc_int = atoi(pos);
+ pos = os_strchr(pos, ' ');
+ if (pos == NULL)
+ return -1;
+ *pos++ = '\0';
+
+ max_disc_int = atoi(pos);
+ pos = os_strchr(pos, ' ');
+ if (pos == NULL)
+ return -1;
+ *pos++ = '\0';
+
+ max_disc_tu = atoi(pos);
+
+ return p2p_set_disc_int(wpa_s->global->p2p, min_disc_int,
+ max_disc_int, max_disc_tu);
+ }
+
+ wpa_printf(MSG_DEBUG, "CTRL_IFACE: Unknown P2P_SET field value '%s'",
+ cmd);
+
+ return -1;
+}
+
+
+static int p2p_ctrl_presence_req(struct wpa_supplicant *wpa_s, char *cmd)
+{
+ char *pos, *pos2;
+ unsigned int dur1 = 0, int1 = 0, dur2 = 0, int2 = 0;
+
+ if (cmd[0]) {
+ pos = os_strchr(cmd, ' ');
+ if (pos == NULL)
+ return -1;
+ *pos++ = '\0';
+ dur1 = atoi(cmd);
+
+ pos2 = os_strchr(pos, ' ');
+ if (pos2)
+ *pos2++ = '\0';
+ int1 = atoi(pos);
+ } else
+ pos2 = NULL;
+
+ if (pos2) {
+ pos = os_strchr(pos2, ' ');
+ if (pos == NULL)
+ return -1;
+ *pos++ = '\0';
+ dur2 = atoi(pos2);
+ int2 = atoi(pos);
+ }
+
+ return wpas_p2p_presence_req(wpa_s, dur1, int1, dur2, int2);
+}
+
+
+static int p2p_ctrl_ext_listen(struct wpa_supplicant *wpa_s, char *cmd)
+{
+ char *pos;
+ unsigned int period = 0, interval = 0;
+
+ if (cmd[0]) {
+ pos = os_strchr(cmd, ' ');
+ if (pos == NULL)
+ return -1;
+ *pos++ = '\0';
+ period = atoi(cmd);
+ interval = atoi(pos);
+ }
+
+ return wpas_p2p_ext_listen(wpa_s, period, interval);
+}
+
+#endif /* CONFIG_P2P */
+
+
+#ifdef CONFIG_INTERWORKING
+static int ctrl_interworking_connect(struct wpa_supplicant *wpa_s, char *dst)
+{
+ u8 bssid[ETH_ALEN];
+ struct wpa_bss *bss;
+
+ if (hwaddr_aton(dst, bssid)) {
+ wpa_printf(MSG_DEBUG, "Invalid BSSID '%s'", dst);
+ return -1;
+ }
+
+ bss = wpa_bss_get_bssid(wpa_s, bssid);
+ if (bss == NULL) {
+ wpa_printf(MSG_DEBUG, "Could not find BSS " MACSTR,
+ MAC2STR(bssid));
+ return -1;
+ }
+
+ return interworking_connect(wpa_s, bss);
+}
+
+
+static int get_anqp(struct wpa_supplicant *wpa_s, char *dst)
+{
+ u8 dst_addr[ETH_ALEN];
+ int used;
+ char *pos;
+#define MAX_ANQP_INFO_ID 100
+ u16 id[MAX_ANQP_INFO_ID];
+ size_t num_id = 0;
+
+ used = hwaddr_aton2(dst, dst_addr);
+ if (used < 0)
+ return -1;
+ pos = dst + used;
+ while (num_id < MAX_ANQP_INFO_ID) {
+ id[num_id] = atoi(pos);
+ if (id[num_id])
+ num_id++;
+ pos = os_strchr(pos + 1, ',');
+ if (pos == NULL)
+ break;
+ pos++;
+ }
+
+ if (num_id == 0)
+ return -1;
+
+ return anqp_send_req(wpa_s, dst_addr, id, num_id);
+}
+
+
+static int gas_request(struct wpa_supplicant *wpa_s, char *cmd)
+{
+ u8 dst_addr[ETH_ALEN];
+ struct wpabuf *advproto, *query = NULL;
+ int used, ret = -1;
+ char *pos, *end;
+ size_t len;
+
+ used = hwaddr_aton2(cmd, dst_addr);
+ if (used < 0)
+ return -1;
+
+ pos = cmd + used;
+ while (*pos == ' ')
+ pos++;
+
+ /* Advertisement Protocol ID */
+ end = os_strchr(pos, ' ');
+ if (end)
+ len = end - pos;
+ else
+ len = os_strlen(pos);
+ if (len & 0x01)
+ return -1;
+ len /= 2;
+ if (len == 0)
+ return -1;
+ advproto = wpabuf_alloc(len);
+ if (advproto == NULL)
+ return -1;
+ if (hexstr2bin(pos, wpabuf_put(advproto, len), len) < 0)
+ goto fail;
+
+ if (end) {
+ /* Optional Query Request */
+ pos = end + 1;
+ while (*pos == ' ')
+ pos++;
+
+ len = os_strlen(pos);
+ if (len) {
+ if (len & 0x01)
+ goto fail;
+ len /= 2;
+ if (len == 0)
+ goto fail;
+ query = wpabuf_alloc(len);
+ if (query == NULL)
+ goto fail;
+ if (hexstr2bin(pos, wpabuf_put(query, len), len) < 0)
+ goto fail;
+ }
+ }
+
+ ret = gas_send_request(wpa_s, dst_addr, advproto, query);
+
+fail:
+ wpabuf_free(advproto);
+ wpabuf_free(query);
+
+ return ret;
+}
+
+
+static int gas_response_get(struct wpa_supplicant *wpa_s, char *cmd, char *buf,
+ size_t buflen)
+{
+ u8 addr[ETH_ALEN];
+ int dialog_token;
+ int used;
+ char *pos;
+ size_t resp_len, start, requested_len;
+
+ if (!wpa_s->last_gas_resp)
+ return -1;
+
+ used = hwaddr_aton2(cmd, addr);
+ if (used < 0)
+ return -1;
+
+ pos = cmd + used;
+ while (*pos == ' ')
+ pos++;
+ dialog_token = atoi(pos);
+
+ if (os_memcmp(addr, wpa_s->last_gas_addr, ETH_ALEN) != 0 ||
+ dialog_token != wpa_s->last_gas_dialog_token)
+ return -1;
+
+ resp_len = wpabuf_len(wpa_s->last_gas_resp);
+ start = 0;
+ requested_len = resp_len;
+
+ pos = os_strchr(pos, ' ');
+ if (pos) {
+ start = atoi(pos);
+ if (start > resp_len)
+ return os_snprintf(buf, buflen, "FAIL-Invalid range");
+ pos = os_strchr(pos, ',');
+ if (pos == NULL)
+ return -1;
+ pos++;
+ requested_len = atoi(pos);
+ if (start + requested_len > resp_len)
+ return os_snprintf(buf, buflen, "FAIL-Invalid range");
+ }
+
+ if (requested_len * 2 + 1 > buflen)
+ return os_snprintf(buf, buflen, "FAIL-Too long response");
+
+ return wpa_snprintf_hex(buf, buflen,
+ wpabuf_head_u8(wpa_s->last_gas_resp) + start,
+ requested_len);
+}
+#endif /* CONFIG_INTERWORKING */
+
+
+#ifdef CONFIG_HS20
+
+static int get_hs20_anqp(struct wpa_supplicant *wpa_s, char *dst)
+{
+ u8 dst_addr[ETH_ALEN];
+ int used;
+ char *pos;
+ u32 subtypes = 0;
+
+ used = hwaddr_aton2(dst, dst_addr);
+ if (used < 0)
+ return -1;
+ pos = dst + used;
+ for (;;) {
+ int num = atoi(pos);
+ if (num <= 0 || num > 31)
+ return -1;
+ subtypes |= BIT(num);
+ pos = os_strchr(pos + 1, ',');
+ if (pos == NULL)
+ break;
+ pos++;
+ }
+
+ if (subtypes == 0)
+ return -1;
+
+ return hs20_anqp_send_req(wpa_s, dst_addr, subtypes, NULL, 0);
+}
+
+
+static int hs20_nai_home_realm_list(struct wpa_supplicant *wpa_s,
+ const u8 *addr, const char *realm)
+{
+ u8 *buf;
+ size_t rlen, len;
+ int ret;
+
+ rlen = os_strlen(realm);
+ len = 3 + rlen;
+ buf = os_malloc(len);
+ if (buf == NULL)
+ return -1;
+ buf[0] = 1; /* NAI Home Realm Count */
+ buf[1] = 0; /* Formatted in accordance with RFC 4282 */
+ buf[2] = rlen;
+ os_memcpy(buf + 3, realm, rlen);
+
+ ret = hs20_anqp_send_req(wpa_s, addr,
+ BIT(HS20_STYPE_NAI_HOME_REALM_QUERY),
+ buf, len);
+
+ os_free(buf);
+
+ return ret;
+}
+
+
+static int hs20_get_nai_home_realm_list(struct wpa_supplicant *wpa_s,
+ char *dst)
+{
+ struct wpa_cred *cred = wpa_s->conf->cred;
+ u8 dst_addr[ETH_ALEN];
+ int used;
+ u8 *buf;
+ size_t len;
+ int ret;
+
+ used = hwaddr_aton2(dst, dst_addr);
+ if (used < 0)
+ return -1;
+
+ while (dst[used] == ' ')
+ used++;
+ if (os_strncmp(dst + used, "realm=", 6) == 0)
+ return hs20_nai_home_realm_list(wpa_s, dst_addr,
+ dst + used + 6);
+
+ len = os_strlen(dst + used);
+
+ if (len == 0 && cred && cred->realm)
+ return hs20_nai_home_realm_list(wpa_s, dst_addr, cred->realm);
+
+ if (len % 1)
+ return -1;
+ len /= 2;
+ buf = os_malloc(len);
+ if (buf == NULL)
+ return -1;
+ if (hexstr2bin(dst + used, buf, len) < 0) {
+ os_free(buf);
+ return -1;
+ }
+
+ ret = hs20_anqp_send_req(wpa_s, dst_addr,
+ BIT(HS20_STYPE_NAI_HOME_REALM_QUERY),
+ buf, len);
+ os_free(buf);
+
+ return ret;
+}
+
+#endif /* CONFIG_HS20 */
+
+
+static int wpa_supplicant_ctrl_iface_sta_autoconnect(
+ struct wpa_supplicant *wpa_s, char *cmd)
+{
+ wpa_s->auto_reconnect_disabled = atoi(cmd) == 0 ? 1 : 0;
+ return 0;
+}
+
+
+#ifdef CONFIG_AUTOSCAN
+
+static int wpa_supplicant_ctrl_iface_autoscan(struct wpa_supplicant *wpa_s,
+ char *cmd)
+{
+ enum wpa_states state = wpa_s->wpa_state;
+ char *new_params = NULL;
+
+ if (os_strlen(cmd) > 0) {
+ new_params = os_strdup(cmd);
+ if (new_params == NULL)
+ return -1;
+ }
+
+ os_free(wpa_s->conf->autoscan);
+ wpa_s->conf->autoscan = new_params;
+
+ if (wpa_s->conf->autoscan == NULL)
+ autoscan_deinit(wpa_s);
+ else if (state == WPA_DISCONNECTED || state == WPA_INACTIVE)
+ autoscan_init(wpa_s, 1);
+ else if (state == WPA_SCANNING)
+ wpa_supplicant_reinit_autoscan(wpa_s);
+
+ return 0;
+}
+
+#endif /* CONFIG_AUTOSCAN */
+
+
+#ifdef CONFIG_WNM
+
+static int wpas_ctrl_iface_wnm_sleep(struct wpa_supplicant *wpa_s, char *cmd)
+{
+ int enter;
+ int intval = 0;
+ char *pos;
+ int ret;
+ struct wpabuf *tfs_req = NULL;
+
+ if (os_strncmp(cmd, "enter", 5) == 0)
+ enter = 1;
+ else if (os_strncmp(cmd, "exit", 4) == 0)
+ enter = 0;
+ else
+ return -1;
+
+ pos = os_strstr(cmd, " interval=");
+ if (pos)
+ intval = atoi(pos + 10);
+
+ pos = os_strstr(cmd, " tfs_req=");
+ if (pos) {
+ char *end;
+ size_t len;
+ pos += 9;
+ end = os_strchr(pos, ' ');
+ if (end)
+ len = end - pos;
+ else
+ len = os_strlen(pos);
+ if (len & 1)
+ return -1;
+ len /= 2;
+ tfs_req = wpabuf_alloc(len);
+ if (tfs_req == NULL)
+ return -1;
+ if (hexstr2bin(pos, wpabuf_put(tfs_req, len), len) < 0) {
+ wpabuf_free(tfs_req);
+ return -1;
+ }
+ }
+
+ ret = ieee802_11_send_wnmsleep_req(wpa_s, enter ? WNM_SLEEP_MODE_ENTER :
+ WNM_SLEEP_MODE_EXIT, intval,
+ tfs_req);
+ wpabuf_free(tfs_req);
+
+ return ret;
+}
+
+#endif /* CONFIG_WNM */
+
+
+static int wpa_supplicant_signal_poll(struct wpa_supplicant *wpa_s, char *buf,
+ size_t buflen)
+{
+ struct wpa_signal_info si;
+ int ret;
+
+ ret = wpa_drv_signal_poll(wpa_s, &si);
+ if (ret)
+ return -1;
+
+ ret = os_snprintf(buf, buflen, "RSSI=%d\nLINKSPEED=%d\n"
+ "NOISE=%d\nFREQUENCY=%u\n",
+ si.current_signal, si.current_txrate / 1000,
+ si.current_noise, si.frequency);
+ if (ret < 0 || (unsigned int) ret > buflen)
+ return -1;
+ return ret;
+}
+
+
+static int wpa_supplicant_pktcnt_poll(struct wpa_supplicant *wpa_s, char *buf,
+ size_t buflen)
+{
+ struct hostap_sta_driver_data sta;
+ int ret;
+
+ ret = wpa_drv_pktcnt_poll(wpa_s, &sta);
+ if (ret)
+ return -1;
+
+ ret = os_snprintf(buf, buflen, "TXGOOD=%lu\nTXBAD=%lu\nRXGOOD=%lu\n",
+ sta.tx_packets, sta.tx_retry_failed, sta.rx_packets);
+ if (ret < 0 || (size_t) ret > buflen)
+ return -1;
+ return ret;
}
@@ -1692,17 +4784,23 @@ char * wpa_supplicant_ctrl_iface_process(struct wpa_supplicant *wpa_s,
char *buf, size_t *resp_len)
{
char *reply;
- const int reply_size = 2048;
+ const int reply_size = 4096;
int ctrl_rsp = 0;
int reply_len;
if (os_strncmp(buf, WPA_CTRL_RSP, os_strlen(WPA_CTRL_RSP)) == 0 ||
- os_strncmp(buf, "SET_NETWORK ", 12) == 0) {
+ os_strncmp(buf, "SET_NETWORK ", 12) == 0 ||
+ os_strncmp(buf, "WPS_NFC_TAG_READ", 16) == 0 ||
+ os_strncmp(buf, "NFC_RX_HANDOVER_SEL", 19) == 0) {
wpa_hexdump_ascii_key(MSG_DEBUG, "RX ctrl_iface",
(const u8 *) buf, os_strlen(buf));
} else {
- wpa_hexdump_ascii(MSG_DEBUG, "RX ctrl_iface",
+ int level = MSG_DEBUG;
+ if (os_strcmp(buf, "PING") == 0)
+ level = MSG_EXCESSIVE;
+ wpa_hexdump_ascii(level, "RX ctrl_iface",
(const u8 *) buf, os_strlen(buf));
+ wpa_dbg(wpa_s, level, "Control interface command '%s'", buf);
}
reply = os_malloc(reply_size);
@@ -1717,6 +4815,14 @@ char * wpa_supplicant_ctrl_iface_process(struct wpa_supplicant *wpa_s,
if (os_strcmp(buf, "PING") == 0) {
os_memcpy(reply, "PONG\n", 5);
reply_len = 5;
+ } else if (os_strcmp(buf, "IFNAME") == 0) {
+ reply_len = os_strlen(wpa_s->ifname);
+ os_memcpy(reply, wpa_s->ifname, reply_len);
+ } else if (os_strncmp(buf, "RELOG", 5) == 0) {
+ if (wpa_debug_reopen_file() < 0)
+ reply_len = -1;
+ } else if (os_strncmp(buf, "NOTE ", 5) == 0) {
+ wpa_printf(MSG_INFO, "NOTE: %s", buf + 5);
} else if (os_strcmp(buf, "MIB") == 0) {
reply_len = wpa_sm_get_mib(wpa_s->wpa, reply, reply_size);
if (reply_len >= 0) {
@@ -1737,20 +4843,23 @@ char * wpa_supplicant_ctrl_iface_process(struct wpa_supplicant *wpa_s,
} else if (os_strncmp(buf, "SET ", 4) == 0) {
if (wpa_supplicant_ctrl_iface_set(wpa_s, buf + 4))
reply_len = -1;
+ } else if (os_strncmp(buf, "GET ", 4) == 0) {
+ reply_len = wpa_supplicant_ctrl_iface_get(wpa_s, buf + 4,
+ reply, reply_size);
} else if (os_strcmp(buf, "LOGON") == 0) {
eapol_sm_notify_logoff(wpa_s->eapol, FALSE);
} else if (os_strcmp(buf, "LOGOFF") == 0) {
eapol_sm_notify_logoff(wpa_s->eapol, TRUE);
} else if (os_strcmp(buf, "REASSOCIATE") == 0) {
- wpa_s->disconnected = 0;
- wpa_s->reassociate = 1;
- wpa_supplicant_req_scan(wpa_s, 0, 0);
+ if (wpa_s->wpa_state == WPA_INTERFACE_DISABLED)
+ reply_len = -1;
+ else
+ wpas_request_connection(wpa_s);
} else if (os_strcmp(buf, "RECONNECT") == 0) {
- if (wpa_s->disconnected) {
- wpa_s->disconnected = 0;
- wpa_s->reassociate = 1;
- wpa_supplicant_req_scan(wpa_s, 0, 0);
- }
+ if (wpa_s->wpa_state == WPA_INTERFACE_DISABLED)
+ reply_len = -1;
+ else if (wpa_s->disconnected)
+ wpas_request_connection(wpa_s);
#ifdef IEEE8021X_EAPOL
} else if (os_strncmp(buf, "PREAUTH ", 8) == 0) {
if (wpa_supplicant_ctrl_iface_preauth(wpa_s, buf + 8))
@@ -1768,26 +4877,70 @@ char * wpa_supplicant_ctrl_iface_process(struct wpa_supplicant *wpa_s,
#endif /* CONFIG_IEEE80211R */
#ifdef CONFIG_WPS
} else if (os_strcmp(buf, "WPS_PBC") == 0) {
- if (wpa_supplicant_ctrl_iface_wps_pbc(wpa_s, NULL))
+ int res = wpa_supplicant_ctrl_iface_wps_pbc(wpa_s, NULL);
+ if (res == -2) {
+ os_memcpy(reply, "FAIL-PBC-OVERLAP\n", 17);
+ reply_len = 17;
+ } else if (res)
reply_len = -1;
} else if (os_strncmp(buf, "WPS_PBC ", 8) == 0) {
- if (wpa_supplicant_ctrl_iface_wps_pbc(wpa_s, buf + 8))
+ int res = wpa_supplicant_ctrl_iface_wps_pbc(wpa_s, buf + 8);
+ if (res == -2) {
+ os_memcpy(reply, "FAIL-PBC-OVERLAP\n", 17);
+ reply_len = 17;
+ } else if (res)
reply_len = -1;
} else if (os_strncmp(buf, "WPS_PIN ", 8) == 0) {
reply_len = wpa_supplicant_ctrl_iface_wps_pin(wpa_s, buf + 8,
reply,
reply_size);
-#ifdef CONFIG_WPS_OOB
- } else if (os_strncmp(buf, "WPS_OOB ", 8) == 0) {
- if (wpa_supplicant_ctrl_iface_wps_oob(wpa_s, buf + 8))
+ } else if (os_strncmp(buf, "WPS_CHECK_PIN ", 14) == 0) {
+ reply_len = wpa_supplicant_ctrl_iface_wps_check_pin(
+ wpa_s, buf + 14, reply, reply_size);
+ } else if (os_strcmp(buf, "WPS_CANCEL") == 0) {
+ if (wpas_wps_cancel(wpa_s))
+ reply_len = -1;
+#ifdef CONFIG_WPS_NFC
+ } else if (os_strcmp(buf, "WPS_NFC") == 0) {
+ if (wpa_supplicant_ctrl_iface_wps_nfc(wpa_s, NULL))
+ reply_len = -1;
+ } else if (os_strncmp(buf, "WPS_NFC ", 8) == 0) {
+ if (wpa_supplicant_ctrl_iface_wps_nfc(wpa_s, buf + 8))
reply_len = -1;
-#endif /* CONFIG_WPS_OOB */
+ } else if (os_strncmp(buf, "WPS_NFC_TOKEN ", 14) == 0) {
+ reply_len = wpa_supplicant_ctrl_iface_wps_nfc_token(
+ wpa_s, buf + 14, reply, reply_size);
+ } else if (os_strncmp(buf, "WPS_NFC_TAG_READ ", 17) == 0) {
+ if (wpa_supplicant_ctrl_iface_wps_nfc_tag_read(wpa_s,
+ buf + 17))
+ reply_len = -1;
+ } else if (os_strncmp(buf, "NFC_GET_HANDOVER_REQ ", 21) == 0) {
+ reply_len = wpas_ctrl_nfc_get_handover_req(
+ wpa_s, buf + 21, reply, reply_size);
+ } else if (os_strncmp(buf, "NFC_GET_HANDOVER_SEL ", 21) == 0) {
+ reply_len = wpas_ctrl_nfc_get_handover_sel(
+ wpa_s, buf + 21, reply, reply_size);
+ } else if (os_strncmp(buf, "NFC_RX_HANDOVER_REQ ", 20) == 0) {
+ reply_len = wpas_ctrl_nfc_rx_handover_req(
+ wpa_s, buf + 20, reply, reply_size);
+ } else if (os_strncmp(buf, "NFC_RX_HANDOVER_SEL ", 20) == 0) {
+ if (wpas_ctrl_nfc_rx_handover_sel(wpa_s, buf + 20))
+ reply_len = -1;
+#endif /* CONFIG_WPS_NFC */
} else if (os_strncmp(buf, "WPS_REG ", 8) == 0) {
if (wpa_supplicant_ctrl_iface_wps_reg(wpa_s, buf + 8))
reply_len = -1;
+#ifdef CONFIG_AP
+ } else if (os_strncmp(buf, "WPS_AP_PIN ", 11) == 0) {
+ reply_len = wpa_supplicant_ctrl_iface_wps_ap_pin(
+ wpa_s, buf + 11, reply, reply_size);
+#endif /* CONFIG_AP */
#ifdef CONFIG_WPS_ER
} else if (os_strcmp(buf, "WPS_ER_START") == 0) {
- if (wpas_wps_er_start(wpa_s))
+ if (wpas_wps_er_start(wpa_s, NULL))
+ reply_len = -1;
+ } else if (os_strncmp(buf, "WPS_ER_START ", 13) == 0) {
+ if (wpas_wps_er_start(wpa_s, buf + 13))
reply_len = -1;
} else if (os_strcmp(buf, "WPS_ER_STOP") == 0) {
if (wpas_wps_er_stop(wpa_s))
@@ -1796,11 +4949,33 @@ char * wpa_supplicant_ctrl_iface_process(struct wpa_supplicant *wpa_s,
if (wpa_supplicant_ctrl_iface_wps_er_pin(wpa_s, buf + 11))
reply_len = -1;
} else if (os_strncmp(buf, "WPS_ER_PBC ", 11) == 0) {
- if (wpas_wps_er_pbc(wpa_s, buf + 11))
+ int ret = wpas_wps_er_pbc(wpa_s, buf + 11);
+ if (ret == -2) {
+ os_memcpy(reply, "FAIL-PBC-OVERLAP\n", 17);
+ reply_len = 17;
+ } else if (ret == -3) {
+ os_memcpy(reply, "FAIL-UNKNOWN-UUID\n", 18);
+ reply_len = 18;
+ } else if (ret == -4) {
+ os_memcpy(reply, "FAIL-NO-AP-SETTINGS\n", 20);
+ reply_len = 20;
+ } else if (ret)
reply_len = -1;
} else if (os_strncmp(buf, "WPS_ER_LEARN ", 13) == 0) {
if (wpa_supplicant_ctrl_iface_wps_er_learn(wpa_s, buf + 13))
reply_len = -1;
+ } else if (os_strncmp(buf, "WPS_ER_SET_CONFIG ", 18) == 0) {
+ if (wpa_supplicant_ctrl_iface_wps_er_set_config(wpa_s,
+ buf + 18))
+ reply_len = -1;
+ } else if (os_strncmp(buf, "WPS_ER_CONFIG ", 14) == 0) {
+ if (wpa_supplicant_ctrl_iface_wps_er_config(wpa_s, buf + 14))
+ reply_len = -1;
+#ifdef CONFIG_WPS_NFC
+ } else if (os_strncmp(buf, "WPS_ER_NFC_CONFIG_TOKEN ", 24) == 0) {
+ reply_len = wpa_supplicant_ctrl_iface_wps_er_nfc_config_token(
+ wpa_s, buf + 24, reply, reply_size);
+#endif /* CONFIG_WPS_NFC */
#endif /* CONFIG_WPS_ER */
#endif /* CONFIG_WPS */
#ifdef CONFIG_IBSS_RSN
@@ -1808,6 +4983,135 @@ char * wpa_supplicant_ctrl_iface_process(struct wpa_supplicant *wpa_s,
if (wpa_supplicant_ctrl_iface_ibss_rsn(wpa_s, buf + 9))
reply_len = -1;
#endif /* CONFIG_IBSS_RSN */
+#ifdef CONFIG_P2P
+ } else if (os_strncmp(buf, "P2P_FIND ", 9) == 0) {
+ if (p2p_ctrl_find(wpa_s, buf + 9))
+ reply_len = -1;
+ } else if (os_strcmp(buf, "P2P_FIND") == 0) {
+ if (p2p_ctrl_find(wpa_s, ""))
+ reply_len = -1;
+ } else if (os_strcmp(buf, "P2P_STOP_FIND") == 0) {
+ wpas_p2p_stop_find(wpa_s);
+ } else if (os_strncmp(buf, "P2P_CONNECT ", 12) == 0) {
+ reply_len = p2p_ctrl_connect(wpa_s, buf + 12, reply,
+ reply_size);
+ } else if (os_strncmp(buf, "P2P_LISTEN ", 11) == 0) {
+ if (p2p_ctrl_listen(wpa_s, buf + 11))
+ reply_len = -1;
+ } else if (os_strcmp(buf, "P2P_LISTEN") == 0) {
+ if (p2p_ctrl_listen(wpa_s, ""))
+ reply_len = -1;
+ } else if (os_strncmp(buf, "P2P_GROUP_REMOVE ", 17) == 0) {
+ if (wpas_p2p_group_remove(wpa_s, buf + 17))
+ reply_len = -1;
+ } else if (os_strcmp(buf, "P2P_GROUP_ADD") == 0) {
+ if (wpas_p2p_group_add(wpa_s, 0, 0, 0))
+ reply_len = -1;
+ } else if (os_strncmp(buf, "P2P_GROUP_ADD ", 14) == 0) {
+ if (p2p_ctrl_group_add(wpa_s, buf + 14))
+ reply_len = -1;
+ } else if (os_strncmp(buf, "P2P_PROV_DISC ", 14) == 0) {
+ if (p2p_ctrl_prov_disc(wpa_s, buf + 14))
+ reply_len = -1;
+ } else if (os_strcmp(buf, "P2P_GET_PASSPHRASE") == 0) {
+ reply_len = p2p_get_passphrase(wpa_s, reply, reply_size);
+ } else if (os_strncmp(buf, "P2P_SERV_DISC_REQ ", 18) == 0) {
+ reply_len = p2p_ctrl_serv_disc_req(wpa_s, buf + 18, reply,
+ reply_size);
+ } else if (os_strncmp(buf, "P2P_SERV_DISC_CANCEL_REQ ", 25) == 0) {
+ if (p2p_ctrl_serv_disc_cancel_req(wpa_s, buf + 25) < 0)
+ reply_len = -1;
+ } else if (os_strncmp(buf, "P2P_SERV_DISC_RESP ", 19) == 0) {
+ if (p2p_ctrl_serv_disc_resp(wpa_s, buf + 19) < 0)
+ reply_len = -1;
+ } else if (os_strcmp(buf, "P2P_SERVICE_UPDATE") == 0) {
+ wpas_p2p_sd_service_update(wpa_s);
+ } else if (os_strncmp(buf, "P2P_SERV_DISC_EXTERNAL ", 23) == 0) {
+ if (p2p_ctrl_serv_disc_external(wpa_s, buf + 23) < 0)
+ reply_len = -1;
+ } else if (os_strcmp(buf, "P2P_SERVICE_FLUSH") == 0) {
+ wpas_p2p_service_flush(wpa_s);
+ } else if (os_strncmp(buf, "P2P_SERVICE_ADD ", 16) == 0) {
+ if (p2p_ctrl_service_add(wpa_s, buf + 16) < 0)
+ reply_len = -1;
+ } else if (os_strncmp(buf, "P2P_SERVICE_DEL ", 16) == 0) {
+ if (p2p_ctrl_service_del(wpa_s, buf + 16) < 0)
+ reply_len = -1;
+ } else if (os_strncmp(buf, "P2P_REJECT ", 11) == 0) {
+ if (p2p_ctrl_reject(wpa_s, buf + 11) < 0)
+ reply_len = -1;
+ } else if (os_strncmp(buf, "P2P_INVITE ", 11) == 0) {
+ if (p2p_ctrl_invite(wpa_s, buf + 11) < 0)
+ reply_len = -1;
+ } else if (os_strncmp(buf, "P2P_PEER ", 9) == 0) {
+ reply_len = p2p_ctrl_peer(wpa_s, buf + 9, reply,
+ reply_size);
+ } else if (os_strncmp(buf, "P2P_SET ", 8) == 0) {
+ if (p2p_ctrl_set(wpa_s, buf + 8) < 0)
+ reply_len = -1;
+ } else if (os_strcmp(buf, "P2P_FLUSH") == 0) {
+ os_memset(wpa_s->p2p_auth_invite, 0, ETH_ALEN);
+ wpa_s->force_long_sd = 0;
+ if (wpa_s->global->p2p)
+ p2p_flush(wpa_s->global->p2p);
+ } else if (os_strncmp(buf, "P2P_UNAUTHORIZE ", 16) == 0) {
+ if (wpas_p2p_unauthorize(wpa_s, buf + 16) < 0)
+ reply_len = -1;
+ } else if (os_strcmp(buf, "P2P_CANCEL") == 0) {
+ if (wpas_p2p_cancel(wpa_s))
+ reply_len = -1;
+ } else if (os_strncmp(buf, "P2P_PRESENCE_REQ ", 17) == 0) {
+ if (p2p_ctrl_presence_req(wpa_s, buf + 17) < 0)
+ reply_len = -1;
+ } else if (os_strcmp(buf, "P2P_PRESENCE_REQ") == 0) {
+ if (p2p_ctrl_presence_req(wpa_s, "") < 0)
+ reply_len = -1;
+ } else if (os_strncmp(buf, "P2P_EXT_LISTEN ", 15) == 0) {
+ if (p2p_ctrl_ext_listen(wpa_s, buf + 15) < 0)
+ reply_len = -1;
+ } else if (os_strcmp(buf, "P2P_EXT_LISTEN") == 0) {
+ if (p2p_ctrl_ext_listen(wpa_s, "") < 0)
+ reply_len = -1;
+#endif /* CONFIG_P2P */
+#ifdef CONFIG_WIFI_DISPLAY
+ } else if (os_strncmp(buf, "WFD_SUBELEM_SET ", 16) == 0) {
+ if (wifi_display_subelem_set(wpa_s->global, buf + 16) < 0)
+ reply_len = -1;
+ } else if (os_strncmp(buf, "WFD_SUBELEM_GET ", 16) == 0) {
+ reply_len = wifi_display_subelem_get(wpa_s->global, buf + 16,
+ reply, reply_size);
+#endif /* CONFIG_WIFI_DISPLAY */
+#ifdef CONFIG_INTERWORKING
+ } else if (os_strcmp(buf, "FETCH_ANQP") == 0) {
+ if (interworking_fetch_anqp(wpa_s) < 0)
+ reply_len = -1;
+ } else if (os_strcmp(buf, "STOP_FETCH_ANQP") == 0) {
+ interworking_stop_fetch_anqp(wpa_s);
+ } else if (os_strncmp(buf, "INTERWORKING_SELECT", 19) == 0) {
+ if (interworking_select(wpa_s, os_strstr(buf + 19, "auto") !=
+ NULL) < 0)
+ reply_len = -1;
+ } else if (os_strncmp(buf, "INTERWORKING_CONNECT ", 21) == 0) {
+ if (ctrl_interworking_connect(wpa_s, buf + 21) < 0)
+ reply_len = -1;
+ } else if (os_strncmp(buf, "ANQP_GET ", 9) == 0) {
+ if (get_anqp(wpa_s, buf + 9) < 0)
+ reply_len = -1;
+ } else if (os_strncmp(buf, "GAS_REQUEST ", 12) == 0) {
+ if (gas_request(wpa_s, buf + 12) < 0)
+ reply_len = -1;
+ } else if (os_strncmp(buf, "GAS_RESPONSE_GET ", 17) == 0) {
+ reply_len = gas_response_get(wpa_s, buf + 17, reply,
+ reply_size);
+#endif /* CONFIG_INTERWORKING */
+#ifdef CONFIG_HS20
+ } else if (os_strncmp(buf, "HS20_ANQP_GET ", 14) == 0) {
+ if (get_hs20_anqp(wpa_s, buf + 14) < 0)
+ reply_len = -1;
+ } else if (os_strncmp(buf, "HS20_GET_NAI_HOME_REALM_LIST ", 29) == 0) {
+ if (hs20_get_nai_home_realm_list(wpa_s, buf + 29) < 0)
+ reply_len = -1;
+#endif /* CONFIG_HS20 */
} else if (os_strncmp(buf, WPA_CTRL_RSP, os_strlen(WPA_CTRL_RSP)) == 0)
{
if (wpa_supplicant_ctrl_iface_ctrl_rsp(
@@ -1823,17 +5127,49 @@ char * wpa_supplicant_ctrl_iface_process(struct wpa_supplicant *wpa_s,
} else if (os_strncmp(buf, "BSSID ", 6) == 0) {
if (wpa_supplicant_ctrl_iface_bssid(wpa_s, buf + 6))
reply_len = -1;
+ } else if (os_strncmp(buf, "BLACKLIST", 9) == 0) {
+ reply_len = wpa_supplicant_ctrl_iface_blacklist(
+ wpa_s, buf + 9, reply, reply_size);
+ } else if (os_strncmp(buf, "LOG_LEVEL", 9) == 0) {
+ reply_len = wpa_supplicant_ctrl_iface_log_level(
+ wpa_s, buf + 9, reply, reply_size);
} else if (os_strcmp(buf, "LIST_NETWORKS") == 0) {
reply_len = wpa_supplicant_ctrl_iface_list_networks(
wpa_s, reply, reply_size);
} else if (os_strcmp(buf, "DISCONNECT") == 0) {
+#ifdef CONFIG_SME
+ wpa_s->sme.prev_bssid_set = 0;
+#endif /* CONFIG_SME */
wpa_s->reassociate = 0;
wpa_s->disconnected = 1;
+ wpa_supplicant_cancel_sched_scan(wpa_s);
+ wpa_supplicant_cancel_scan(wpa_s);
wpa_supplicant_deauthenticate(wpa_s,
WLAN_REASON_DEAUTH_LEAVING);
} else if (os_strcmp(buf, "SCAN") == 0) {
- wpa_s->scan_req = 2;
- wpa_supplicant_req_scan(wpa_s, 0, 0);
+ if (wpa_s->wpa_state == WPA_INTERFACE_DISABLED)
+ reply_len = -1;
+ else {
+ if (!wpa_s->sched_scanning && !wpa_s->scanning &&
+ ((wpa_s->wpa_state <= WPA_SCANNING) ||
+ (wpa_s->wpa_state == WPA_COMPLETED))) {
+ wpa_s->normal_scans = 0;
+ wpa_s->scan_req = MANUAL_SCAN_REQ;
+ wpa_supplicant_req_scan(wpa_s, 0, 0);
+ } else if (wpa_s->sched_scanning) {
+ wpa_printf(MSG_DEBUG, "Stop ongoing "
+ "sched_scan to allow requested "
+ "full scan to proceed");
+ wpa_supplicant_cancel_sched_scan(wpa_s);
+ wpa_s->scan_req = MANUAL_SCAN_REQ;
+ wpa_supplicant_req_scan(wpa_s, 0, 0);
+ } else {
+ wpa_printf(MSG_DEBUG, "Ongoing scan action - "
+ "reject new request");
+ reply_len = os_snprintf(reply, reply_size,
+ "FAIL-BUSY\n");
+ }
+ }
} else if (os_strcmp(buf, "SCAN_RESULTS") == 0) {
reply_len = wpa_supplicant_ctrl_iface_scan_results(
wpa_s, reply, reply_size);
@@ -1858,6 +5194,18 @@ char * wpa_supplicant_ctrl_iface_process(struct wpa_supplicant *wpa_s,
} else if (os_strncmp(buf, "GET_NETWORK ", 12) == 0) {
reply_len = wpa_supplicant_ctrl_iface_get_network(
wpa_s, buf + 12, reply, reply_size);
+ } else if (os_strcmp(buf, "LIST_CREDS") == 0) {
+ reply_len = wpa_supplicant_ctrl_iface_list_creds(
+ wpa_s, reply, reply_size);
+ } else if (os_strcmp(buf, "ADD_CRED") == 0) {
+ reply_len = wpa_supplicant_ctrl_iface_add_cred(
+ wpa_s, reply, reply_size);
+ } else if (os_strncmp(buf, "REMOVE_CRED ", 12) == 0) {
+ if (wpa_supplicant_ctrl_iface_remove_cred(wpa_s, buf + 12))
+ reply_len = -1;
+ } else if (os_strncmp(buf, "SET_CRED ", 9) == 0) {
+ if (wpa_supplicant_ctrl_iface_set_cred(wpa_s, buf + 9))
+ reply_len = -1;
#ifndef CONFIG_NO_CONFIG_WRITE
} else if (os_strcmp(buf, "SAVE_CONFIG") == 0) {
if (wpa_supplicant_ctrl_iface_save_config(wpa_s))
@@ -1869,6 +5217,9 @@ char * wpa_supplicant_ctrl_iface_process(struct wpa_supplicant *wpa_s,
} else if (os_strncmp(buf, "AP_SCAN ", 8) == 0) {
if (wpa_supplicant_ctrl_iface_ap_scan(wpa_s, buf + 8))
reply_len = -1;
+ } else if (os_strncmp(buf, "SCAN_INTERVAL ", 14) == 0) {
+ if (wpa_supplicant_ctrl_iface_scan_interval(wpa_s, buf + 14))
+ reply_len = -1;
} else if (os_strcmp(buf, "INTERFACE_LIST") == 0) {
reply_len = wpa_supplicant_global_iface_list(
wpa_s->global, reply, reply_size);
@@ -1887,6 +5238,12 @@ char * wpa_supplicant_ctrl_iface_process(struct wpa_supplicant *wpa_s,
} else if (os_strncmp(buf, "STA-NEXT ", 9) == 0) {
reply_len = ap_ctrl_iface_sta_next(wpa_s, buf + 9, reply,
reply_size);
+ } else if (os_strncmp(buf, "DEAUTHENTICATE ", 15) == 0) {
+ if (ap_ctrl_iface_sta_deauthenticate(wpa_s, buf + 15))
+ reply_len = -1;
+ } else if (os_strncmp(buf, "DISASSOCIATE ", 13) == 0) {
+ if (ap_ctrl_iface_sta_disassociate(wpa_s, buf + 13))
+ reply_len = -1;
#endif /* CONFIG_AP */
} else if (os_strcmp(buf, "SUSPEND") == 0) {
wpas_notify_suspend(wpa_s->global);
@@ -1897,6 +5254,49 @@ char * wpa_supplicant_ctrl_iface_process(struct wpa_supplicant *wpa_s,
} else if (os_strncmp(buf, "ROAM ", 5) == 0) {
if (wpa_supplicant_ctrl_iface_roam(wpa_s, buf + 5))
reply_len = -1;
+ } else if (os_strncmp(buf, "STA_AUTOCONNECT ", 16) == 0) {
+ if (wpa_supplicant_ctrl_iface_sta_autoconnect(wpa_s, buf + 16))
+ reply_len = -1;
+ } else if (os_strncmp(buf, "BSS_EXPIRE_AGE ", 15) == 0) {
+ if (wpa_supplicant_ctrl_iface_bss_expire_age(wpa_s, buf + 15))
+ reply_len = -1;
+ } else if (os_strncmp(buf, "BSS_EXPIRE_COUNT ", 17) == 0) {
+ if (wpa_supplicant_ctrl_iface_bss_expire_count(wpa_s,
+ buf + 17))
+ reply_len = -1;
+ } else if (os_strncmp(buf, "BSS_FLUSH ", 10) == 0) {
+ if (wpa_supplicant_ctrl_iface_bss_flush(wpa_s, buf + 10))
+ reply_len = -1;
+#ifdef CONFIG_TDLS
+ } else if (os_strncmp(buf, "TDLS_DISCOVER ", 14) == 0) {
+ if (wpa_supplicant_ctrl_iface_tdls_discover(wpa_s, buf + 14))
+ reply_len = -1;
+ } else if (os_strncmp(buf, "TDLS_SETUP ", 11) == 0) {
+ if (wpa_supplicant_ctrl_iface_tdls_setup(wpa_s, buf + 11))
+ reply_len = -1;
+ } else if (os_strncmp(buf, "TDLS_TEARDOWN ", 14) == 0) {
+ if (wpa_supplicant_ctrl_iface_tdls_teardown(wpa_s, buf + 14))
+ reply_len = -1;
+#endif /* CONFIG_TDLS */
+ } else if (os_strncmp(buf, "SIGNAL_POLL", 11) == 0) {
+ reply_len = wpa_supplicant_signal_poll(wpa_s, reply,
+ reply_size);
+ } else if (os_strncmp(buf, "PKTCNT_POLL", 11) == 0) {
+ reply_len = wpa_supplicant_pktcnt_poll(wpa_s, reply,
+ reply_size);
+#ifdef CONFIG_AUTOSCAN
+ } else if (os_strncmp(buf, "AUTOSCAN ", 9) == 0) {
+ if (wpa_supplicant_ctrl_iface_autoscan(wpa_s, buf + 9))
+ reply_len = -1;
+#endif /* CONFIG_AUTOSCAN */
+ } else if (os_strcmp(buf, "REAUTHENTICATE") == 0) {
+ pmksa_cache_clear_current(wpa_s->wpa);
+ eapol_sm_request_reauth(wpa_s->eapol);
+#ifdef CONFIG_WNM
+ } else if (os_strncmp(buf, "WNM_SLEEP ", 10) == 0) {
+ if (wpas_ctrl_iface_wnm_sleep(wpa_s, buf + 10))
+ reply_len = -1;
+#endif /* CONFIG_WNM */
} else {
os_memcpy(reply, "UNKNOWN COMMAND\n", 16);
reply_len = 16;
@@ -2002,7 +5402,7 @@ static int wpa_supplicant_global_iface_remove(struct wpa_global *global,
wpa_s = wpa_supplicant_get_iface(global, cmd);
if (wpa_s == NULL)
return -1;
- return wpa_supplicant_remove_iface(global, wpa_s);
+ return wpa_supplicant_remove_iface(global, wpa_s, 0);
}
@@ -2093,8 +5493,11 @@ char * wpa_supplicant_global_ctrl_iface_process(struct wpa_global *global,
char *reply;
const int reply_size = 2048;
int reply_len;
+ int level = MSG_DEBUG;
- wpa_hexdump_ascii(MSG_DEBUG, "RX global ctrl_iface",
+ if (os_strcmp(buf, "PING") == 0)
+ level = MSG_EXCESSIVE;
+ wpa_hexdump_ascii(level, "RX global ctrl_iface",
(const u8 *) buf, os_strlen(buf));
reply = os_malloc(reply_size);
diff --git a/contrib/wpa/wpa_supplicant/ctrl_iface.h b/contrib/wpa/wpa_supplicant/ctrl_iface.h
index 051d99a..a329ef3 100644
--- a/contrib/wpa/wpa_supplicant/ctrl_iface.h
+++ b/contrib/wpa/wpa_supplicant/ctrl_iface.h
@@ -2,14 +2,8 @@
* WPA Supplicant / UNIX domain socket -based control interface
* Copyright (c) 2004-2005, Jouni Malinen <j@w1.fi>
*
- * This program is free software; you can redistribute it and/or modify
- * it under the terms of the GNU General Public License version 2 as
- * published by the Free Software Foundation.
- *
- * Alternatively, this software may be distributed under the terms of BSD
- * license.
- *
- * See README and COPYING for more details.
+ * This software may be distributed under the terms of the BSD license.
+ * See README for more details.
*/
#ifndef CTRL_IFACE_H
diff --git a/contrib/wpa/wpa_supplicant/ctrl_iface_named_pipe.c b/contrib/wpa/wpa_supplicant/ctrl_iface_named_pipe.c
index 5f7e24d..fd417ff 100644
--- a/contrib/wpa/wpa_supplicant/ctrl_iface_named_pipe.c
+++ b/contrib/wpa/wpa_supplicant/ctrl_iface_named_pipe.c
@@ -2,14 +2,8 @@
* WPA Supplicant / Windows Named Pipe -based control interface
* Copyright (c) 2004-2006, Jouni Malinen <j@w1.fi>
*
- * This program is free software; you can redistribute it and/or modify
- * it under the terms of the GNU General Public License version 2 as
- * published by the Free Software Foundation.
- *
- * Alternatively, this software may be distributed under the terms of BSD
- * license.
- *
- * See README and COPYING for more details.
+ * This software may be distributed under the terms of the BSD license.
+ * See README for more details.
*/
#include "includes.h"
diff --git a/contrib/wpa/wpa_supplicant/ctrl_iface_udp.c b/contrib/wpa/wpa_supplicant/ctrl_iface_udp.c
index 110ca4f..994f9b1 100644
--- a/contrib/wpa/wpa_supplicant/ctrl_iface_udp.c
+++ b/contrib/wpa/wpa_supplicant/ctrl_iface_udp.c
@@ -2,14 +2,8 @@
* WPA Supplicant / UDP socket -based control interface
* Copyright (c) 2004-2005, Jouni Malinen <j@w1.fi>
*
- * This program is free software; you can redistribute it and/or modify
- * it under the terms of the GNU General Public License version 2 as
- * published by the Free Software Foundation.
- *
- * Alternatively, this software may be distributed under the terms of BSD
- * license.
- *
- * See README and COPYING for more details.
+ * This software may be distributed under the terms of the BSD license.
+ * See README for more details.
*/
#include "includes.h"
@@ -169,6 +163,8 @@ static void wpa_supplicant_ctrl_iface_receive(int sock, void *eloop_ctx,
perror("recvfrom(ctrl_iface)");
return;
}
+
+#ifndef CONFIG_CTRL_IFACE_UDP_REMOTE
if (from.sin_addr.s_addr != htonl((127 << 24) | 1)) {
/*
* The OS networking stack is expected to drop this kind of
@@ -180,6 +176,8 @@ static void wpa_supplicant_ctrl_iface_receive(int sock, void *eloop_ctx,
"source %s", inet_ntoa(from.sin_addr));
return;
}
+#endif /* CONFIG_CTRL_IFACE_UDP_REMOTE */
+
buf[res] = '\0';
if (os_strcmp(buf, "GET_COOKIE") == 0) {
@@ -272,6 +270,7 @@ wpa_supplicant_ctrl_iface_init(struct wpa_supplicant *wpa_s)
{
struct ctrl_iface_priv *priv;
struct sockaddr_in addr;
+ int port = WPA_CTRL_IFACE_PORT;
priv = os_zalloc(sizeof(*priv));
if (priv == NULL)
@@ -291,13 +290,25 @@ wpa_supplicant_ctrl_iface_init(struct wpa_supplicant *wpa_s)
os_memset(&addr, 0, sizeof(addr));
addr.sin_family = AF_INET;
+#ifdef CONFIG_CTRL_IFACE_UDP_REMOTE
+ addr.sin_addr.s_addr = INADDR_ANY;
+#else /* CONFIG_CTRL_IFACE_UDP_REMOTE */
addr.sin_addr.s_addr = htonl((127 << 24) | 1);
- addr.sin_port = htons(WPA_CTRL_IFACE_PORT);
+#endif /* CONFIG_CTRL_IFACE_UDP_REMOTE */
+try_again:
+ addr.sin_port = htons(port);
if (bind(priv->sock, (struct sockaddr *) &addr, sizeof(addr)) < 0) {
+ port--;
+ if ((WPA_CTRL_IFACE_PORT - port) < WPA_CTRL_IFACE_PORT_LIMIT)
+ goto try_again;
perror("bind(AF_INET)");
goto fail;
}
+#ifdef CONFIG_CTRL_IFACE_UDP_REMOTE
+ wpa_msg(wpa_s, MSG_DEBUG, "ctrl_iface_init UDP port: %d", port);
+#endif /* CONFIG_CTRL_IFACE_UDP_REMOTE */
+
eloop_register_read_sock(priv->sock, wpa_supplicant_ctrl_iface_receive,
wpa_s, priv);
wpa_msg_register_cb(wpa_supplicant_ctrl_iface_msg_cb);
@@ -448,6 +459,8 @@ static void wpa_supplicant_global_ctrl_iface_receive(int sock, void *eloop_ctx,
perror("recvfrom(ctrl_iface)");
return;
}
+
+#ifndef CONFIG_CTRL_IFACE_UDP_REMOTE
if (from.sin_addr.s_addr != htonl((127 << 24) | 1)) {
/*
* The OS networking stack is expected to drop this kind of
@@ -459,6 +472,8 @@ static void wpa_supplicant_global_ctrl_iface_receive(int sock, void *eloop_ctx,
"source %s", inet_ntoa(from.sin_addr));
return;
}
+#endif /* CONFIG_CTRL_IFACE_UDP_REMOTE */
+
buf[res] = '\0';
if (os_strcmp(buf, "GET_COOKIE") == 0) {
@@ -508,6 +523,7 @@ wpa_supplicant_global_ctrl_iface_init(struct wpa_global *global)
{
struct ctrl_iface_global_priv *priv;
struct sockaddr_in addr;
+ int port = WPA_GLOBAL_CTRL_IFACE_PORT;
priv = os_zalloc(sizeof(*priv));
if (priv == NULL)
@@ -529,13 +545,26 @@ wpa_supplicant_global_ctrl_iface_init(struct wpa_global *global)
os_memset(&addr, 0, sizeof(addr));
addr.sin_family = AF_INET;
+#ifdef CONFIG_CTRL_IFACE_UDP_REMOTE
+ addr.sin_addr.s_addr = INADDR_ANY;
+#else /* CONFIG_CTRL_IFACE_UDP_REMOTE */
addr.sin_addr.s_addr = htonl((127 << 24) | 1);
- addr.sin_port = htons(WPA_GLOBAL_CTRL_IFACE_PORT);
+#endif /* CONFIG_CTRL_IFACE_UDP_REMOTE */
+try_again:
+ addr.sin_port = htons(port);
if (bind(priv->sock, (struct sockaddr *) &addr, sizeof(addr)) < 0) {
+ port++;
+ if ((port - WPA_GLOBAL_CTRL_IFACE_PORT) <
+ WPA_GLOBAL_CTRL_IFACE_PORT_LIMIT)
+ goto try_again;
perror("bind(AF_INET)");
goto fail;
}
+#ifdef CONFIG_CTRL_IFACE_UDP_REMOTE
+ wpa_printf(MSG_DEBUG, "global_ctrl_iface_init UDP port: %d", port);
+#endif /* CONFIG_CTRL_IFACE_UDP_REMOTE */
+
eloop_register_read_sock(priv->sock,
wpa_supplicant_global_ctrl_iface_receive,
global, priv);
diff --git a/contrib/wpa/wpa_supplicant/ctrl_iface_unix.c b/contrib/wpa/wpa_supplicant/ctrl_iface_unix.c
index 84ac760..f792863 100644
--- a/contrib/wpa/wpa_supplicant/ctrl_iface_unix.c
+++ b/contrib/wpa/wpa_supplicant/ctrl_iface_unix.c
@@ -2,14 +2,8 @@
* WPA Supplicant / UNIX domain socket -based control interface
* Copyright (c) 2004-2009, Jouni Malinen <j@w1.fi>
*
- * This program is free software; you can redistribute it and/or modify
- * it under the terms of the GNU General Public License version 2 as
- * published by the Free Software Foundation.
- *
- * Alternatively, this software may be distributed under the terms of BSD
- * license.
- *
- * See README and COPYING for more details.
+ * This software may be distributed under the terms of the BSD license.
+ * See README for more details.
*/
#include "includes.h"
@@ -17,6 +11,11 @@
#include <sys/stat.h>
#include <grp.h>
#include <stddef.h>
+#include <unistd.h>
+#include <fcntl.h>
+#ifdef ANDROID
+#include <cutils/sockets.h>
+#endif /* ANDROID */
#include "utils/common.h"
#include "utils/eloop.h"
@@ -132,7 +131,7 @@ static void wpa_supplicant_ctrl_iface_receive(int sock, void *eloop_ctx,
{
struct wpa_supplicant *wpa_s = eloop_ctx;
struct ctrl_iface_priv *priv = sock_ctx;
- char buf[256];
+ char buf[4096];
int res;
struct sockaddr_un from;
socklen_t fromlen = sizeof(from);
@@ -262,6 +261,7 @@ wpa_supplicant_ctrl_iface_init(struct wpa_supplicant *wpa_s)
char *buf, *dir = NULL, *gid_str = NULL;
struct group *grp;
char *endp;
+ int flags;
priv = os_zalloc(sizeof(*priv));
if (priv == NULL)
@@ -276,6 +276,13 @@ wpa_supplicant_ctrl_iface_init(struct wpa_supplicant *wpa_s)
buf = os_strdup(wpa_s->conf->ctrl_interface);
if (buf == NULL)
goto fail;
+#ifdef ANDROID
+ os_snprintf(addr.sun_path, sizeof(addr.sun_path), "wpa_%s",
+ wpa_s->conf->ctrl_interface);
+ priv->sock = android_get_control_socket(addr.sun_path);
+ if (priv->sock >= 0)
+ goto havesock;
+#endif /* ANDROID */
if (os_strncmp(buf, "DIR=", 4) == 0) {
dir = buf + 4;
gid_str = os_strstr(dir, " GROUP=");
@@ -298,6 +305,22 @@ wpa_supplicant_ctrl_iface_init(struct wpa_supplicant *wpa_s)
}
}
+#ifdef ANDROID
+ /*
+ * wpa_supplicant is started from /init.*.rc on Android and that seems
+ * to be using umask 0077 which would leave the control interface
+ * directory without group access. This breaks things since Wi-Fi
+ * framework assumes that this directory can be accessed by other
+ * applications in the wifi group. Fix this by adding group access even
+ * if umask value would prevent this.
+ */
+ if (chmod(dir, S_IRWXU | S_IRWXG) < 0) {
+ wpa_printf(MSG_ERROR, "CTRL: Could not chmod directory: %s",
+ strerror(errno));
+ /* Try to continue anyway */
+ }
+#endif /* ANDROID */
+
if (gid_str) {
grp = getgrnam(gid_str);
if (grp) {
@@ -371,7 +394,7 @@ wpa_supplicant_ctrl_iface_init(struct wpa_supplicant *wpa_s)
}
if (bind(priv->sock, (struct sockaddr *) &addr,
sizeof(addr)) < 0) {
- perror("bind(PF_UNIX)");
+ perror("supp-ctrl-iface-init: bind(PF_UNIX)");
goto fail;
}
wpa_printf(MSG_DEBUG, "Successfully replaced leftover "
@@ -398,6 +421,23 @@ wpa_supplicant_ctrl_iface_init(struct wpa_supplicant *wpa_s)
}
os_free(fname);
+#ifdef ANDROID
+havesock:
+#endif /* ANDROID */
+
+ /*
+ * Make socket non-blocking so that we don't hang forever if
+ * target dies unexpectedly.
+ */
+ flags = fcntl(priv->sock, F_GETFL);
+ if (flags >= 0) {
+ flags |= O_NONBLOCK;
+ if (fcntl(priv->sock, F_SETFL, flags) < 0) {
+ perror("fcntl(ctrl, O_NONBLOCK)");
+ /* Not fatal, continue on.*/
+ }
+ }
+
eloop_register_read_sock(priv->sock, wpa_supplicant_ctrl_iface_receive,
wpa_s, priv);
wpa_msg_register_cb(wpa_supplicant_ctrl_iface_msg_cb);
@@ -637,6 +677,12 @@ wpa_supplicant_global_ctrl_iface_init(struct wpa_global *global)
if (global->params.ctrl_interface == NULL)
return priv;
+#ifdef ANDROID
+ priv->sock = android_get_control_socket(global->params.ctrl_interface);
+ if (priv->sock >= 0)
+ goto havesock;
+#endif /* ANDROID */
+
wpa_printf(MSG_DEBUG, "Global control interface '%s'",
global->params.ctrl_interface);
@@ -654,7 +700,8 @@ wpa_supplicant_global_ctrl_iface_init(struct wpa_global *global)
os_strlcpy(addr.sun_path, global->params.ctrl_interface,
sizeof(addr.sun_path));
if (bind(priv->sock, (struct sockaddr *) &addr, sizeof(addr)) < 0) {
- perror("bind(PF_UNIX)");
+ perror("supp-global-ctrl-iface-init (will try fixup): "
+ "bind(PF_UNIX)");
if (connect(priv->sock, (struct sockaddr *) &addr,
sizeof(addr)) < 0) {
wpa_printf(MSG_DEBUG, "ctrl_iface exists, but does not"
@@ -669,7 +716,7 @@ wpa_supplicant_global_ctrl_iface_init(struct wpa_global *global)
}
if (bind(priv->sock, (struct sockaddr *) &addr,
sizeof(addr)) < 0) {
- perror("bind(PF_UNIX)");
+ perror("supp-glb-iface-init: bind(PF_UNIX)");
goto fail;
}
wpa_printf(MSG_DEBUG, "Successfully replaced leftover "
@@ -685,6 +732,9 @@ wpa_supplicant_global_ctrl_iface_init(struct wpa_global *global)
}
}
+#ifdef ANDROID
+havesock:
+#endif /* ANDROID */
eloop_register_read_sock(priv->sock,
wpa_supplicant_global_ctrl_iface_receive,
global, NULL);
diff --git a/contrib/wpa/wpa_supplicant/dbus/.gitignore b/contrib/wpa/wpa_supplicant/dbus/.gitignore
deleted file mode 100644
index 6db2468..0000000
--- a/contrib/wpa/wpa_supplicant/dbus/.gitignore
+++ /dev/null
@@ -1 +0,0 @@
-libwpadbus.a
diff --git a/contrib/wpa/wpa_supplicant/dbus/Makefile b/contrib/wpa/wpa_supplicant/dbus/Makefile
index cfaf58d..d64c65c 100644
--- a/contrib/wpa/wpa_supplicant/dbus/Makefile
+++ b/contrib/wpa/wpa_supplicant/dbus/Makefile
@@ -15,6 +15,7 @@ ifndef CFLAGS
CFLAGS = -MMD -O2 -Wall -g
endif
+PKG_CONFIG ?= pkg-config
CFLAGS += -I../../src -I../../src/utils
@@ -38,10 +39,10 @@ CFLAGS += -DCONFIG_CTRL_IFACE_DBUS_NEW
CFLAGS += -DCONFIG_CTRL_IFACE_DBUS
ifndef DBUS_LIBS
-DBUS_LIBS := $(shell pkg-config --libs dbus-1)
+DBUS_LIBS := $(shell $(PKG_CONFIG) --libs dbus-1)
endif
ifndef DBUS_INCLUDE
-DBUS_INCLUDE := $(shell pkg-config --cflags dbus-1)
+DBUS_INCLUDE := $(shell $(PKG_CONFIG) --cflags dbus-1)
endif
ifdef CONFIG_CTRL_IFACE_DBUS_INTRO
CFLAGS += -DCONFIG_CTRL_IFACE_DBUS_INTRO
@@ -49,18 +50,6 @@ DBUS_INCLUDE += $(shell xml2-config --cflags)
DBUS_LIBS += $(shell xml2-config --libs)
endif
-dbus_version=$(subst ., ,$(shell pkg-config --modversion dbus-1))
-DBUS_VERSION_MAJOR=$(word 1,$(dbus_version))
-DBUS_VERSION_MINOR=$(word 2,$(dbus_version))
-ifeq ($(DBUS_VERSION_MAJOR),)
-DBUS_VERSION_MAJOR=0
-endif
-ifeq ($(DBUS_VERSION_MINOR),)
-DBUS_VERSION_MINOR=0
-endif
-DBUS_INCLUDE += -DDBUS_VERSION_MAJOR=$(DBUS_VERSION_MAJOR)
-DBUS_INCLUDE += -DDBUS_VERSION_MINOR=$(DBUS_VERSION_MINOR)
-
CFLAGS += $(DBUS_INCLUDE)
LIB_OBJS= \
diff --git a/contrib/wpa/wpa_supplicant/dbus/dbus_common.c b/contrib/wpa/wpa_supplicant/dbus/dbus_common.c
index 5850636..5d0e31e 100644
--- a/contrib/wpa/wpa_supplicant/dbus/dbus_common.c
+++ b/contrib/wpa/wpa_supplicant/dbus/dbus_common.c
@@ -4,14 +4,8 @@
* Copyright (c) 2009, Witold Sowa <witold.sowa@gmail.com>
* Copyright (c) 2009, Jouni Malinen <j@w1.fi>
*
- * This program is free software; you can redistribute it and/or modify
- * it under the terms of the GNU General Public License version 2 as
- * published by the Free Software Foundation.
- *
- * Alternatively, this software may be distributed under the terms of BSD
- * license.
- *
- * See README and COPYING for more details.
+ * This software may be distributed under the terms of the BSD license.
+ * See README for more details.
*/
#include "utils/includes.h"
diff --git a/contrib/wpa/wpa_supplicant/dbus/dbus_common.h b/contrib/wpa/wpa_supplicant/dbus/dbus_common.h
index 50da09b..aea7db7 100644
--- a/contrib/wpa/wpa_supplicant/dbus/dbus_common.h
+++ b/contrib/wpa/wpa_supplicant/dbus/dbus_common.h
@@ -4,14 +4,8 @@
* Copyright (c) 2009, Witold Sowa <witold.sowa@gmail.com>
* Copyright (c) 2009, Jouni Malinen <j@w1.fi>
*
- * This program is free software; you can redistribute it and/or modify
- * it under the terms of the GNU General Public License version 2 as
- * published by the Free Software Foundation.
- *
- * Alternatively, this software may be distributed under the terms of BSD
- * license.
- *
- * See README and COPYING for more details.
+ * This software may be distributed under the terms of the BSD license.
+ * See README for more details.
*/
#ifndef DBUS_COMMON_H
diff --git a/contrib/wpa/wpa_supplicant/dbus/dbus_common_i.h b/contrib/wpa/wpa_supplicant/dbus/dbus_common_i.h
index 9dab1ee..a551ccd 100644
--- a/contrib/wpa/wpa_supplicant/dbus/dbus_common_i.h
+++ b/contrib/wpa/wpa_supplicant/dbus/dbus_common_i.h
@@ -4,14 +4,8 @@
* Copyright (c) 2009, Witold Sowa <witold.sowa@gmail.com>
* Copyright (c) 2009, Jouni Malinen <j@w1.fi>
*
- * This program is free software; you can redistribute it and/or modify
- * it under the terms of the GNU General Public License version 2 as
- * published by the Free Software Foundation.
- *
- * Alternatively, this software may be distributed under the terms of BSD
- * license.
- *
- * See README and COPYING for more details.
+ * This software may be distributed under the terms of the BSD license.
+ * See README for more details.
*/
#ifndef DBUS_COMMON_I_H
@@ -25,6 +19,10 @@ struct wpas_dbus_priv {
struct wpa_global *global;
u32 next_objid;
int dbus_new_initialized;
+
+#if defined(CONFIG_CTRL_IFACE_DBUS_NEW) && defined(CONFIG_AP)
+ int dbus_noc_refcnt;
+#endif /* CONFIG_CTRL_IFACE_DBUS_NEW && CONFIG_AP */
};
#endif /* DBUS_COMMON_I_H */
diff --git a/contrib/wpa/wpa_supplicant/dbus/dbus_dict_helpers.c b/contrib/wpa/wpa_supplicant/dbus/dbus_dict_helpers.c
index b3aff40..61a9430 100644
--- a/contrib/wpa/wpa_supplicant/dbus/dbus_dict_helpers.c
+++ b/contrib/wpa/wpa_supplicant/dbus/dbus_dict_helpers.c
@@ -2,20 +2,15 @@
* WPA Supplicant / dbus-based control interface
* Copyright (c) 2006, Dan Williams <dcbw@redhat.com> and Red Hat, Inc.
*
- * This program is free software; you can redistribute it and/or modify
- * it under the terms of the GNU General Public License version 2 as
- * published by the Free Software Foundation.
- *
- * Alternatively, this software may be distributed under the terms of BSD
- * license.
- *
- * See README and COPYING for more details.
+ * This software may be distributed under the terms of the BSD license.
+ * See README for more details.
*/
#include "includes.h"
#include <dbus/dbus.h>
#include "common.h"
+#include "wpabuf.h"
#include "dbus_dict_helpers.h"
@@ -443,11 +438,12 @@ dbus_bool_t wpa_dbus_dict_append_byte_array(DBusMessageIter *iter_dict,
/**
- * Begin a string array entry in the dict
+ * Begin an array entry in the dict
*
* @param iter_dict A valid DBusMessageIter returned from
* wpa_dbus_dict_open_write()
* @param key The key of the dict item
+ * @param type The type of the contained data
* @param iter_dict_entry A private DBusMessageIter provided by the caller to
* be passed to wpa_dbus_dict_end_string_array()
* @param iter_dict_val A private DBusMessageIter provided by the caller to
@@ -457,12 +453,21 @@ dbus_bool_t wpa_dbus_dict_append_byte_array(DBusMessageIter *iter_dict,
* @return TRUE on success, FALSE on failure
*
*/
-dbus_bool_t wpa_dbus_dict_begin_string_array(DBusMessageIter *iter_dict,
- const char *key,
- DBusMessageIter *iter_dict_entry,
- DBusMessageIter *iter_dict_val,
- DBusMessageIter *iter_array)
+dbus_bool_t wpa_dbus_dict_begin_array(DBusMessageIter *iter_dict,
+ const char *key, const char *type,
+ DBusMessageIter *iter_dict_entry,
+ DBusMessageIter *iter_dict_val,
+ DBusMessageIter *iter_array)
{
+ char array_type[10];
+ int err;
+
+ err = os_snprintf(array_type, sizeof(array_type),
+ DBUS_TYPE_ARRAY_AS_STRING "%s",
+ type);
+ if (err < 0 || err > (int) sizeof(array_type))
+ return FALSE;
+
if (!iter_dict || !iter_dict_entry || !iter_dict_val || !iter_array)
return FALSE;
@@ -472,20 +477,31 @@ dbus_bool_t wpa_dbus_dict_begin_string_array(DBusMessageIter *iter_dict,
if (!dbus_message_iter_open_container(iter_dict_entry,
DBUS_TYPE_VARIANT,
- DBUS_TYPE_ARRAY_AS_STRING
- DBUS_TYPE_STRING_AS_STRING,
+ array_type,
iter_dict_val))
return FALSE;
if (!dbus_message_iter_open_container(iter_dict_val, DBUS_TYPE_ARRAY,
- DBUS_TYPE_BYTE_AS_STRING,
- iter_array))
+ type, iter_array))
return FALSE;
return TRUE;
}
+dbus_bool_t wpa_dbus_dict_begin_string_array(DBusMessageIter *iter_dict,
+ const char *key,
+ DBusMessageIter *iter_dict_entry,
+ DBusMessageIter *iter_dict_val,
+ DBusMessageIter *iter_array)
+{
+ return wpa_dbus_dict_begin_array(
+ iter_dict, key,
+ DBUS_TYPE_STRING_AS_STRING,
+ iter_dict_entry, iter_dict_val, iter_array);
+}
+
+
/**
* Add a single string element to a string array dict entry
*
@@ -508,23 +524,67 @@ dbus_bool_t wpa_dbus_dict_string_array_add_element(DBusMessageIter *iter_array,
/**
- * End a string array dict entry
+ * Add a single byte array element to a string array dict entry
+ *
+ * @param iter_array A valid DBusMessageIter returned from
+ * wpa_dbus_dict_begin_array()'s iter_array
+ * parameter -- note that wpa_dbus_dict_begin_array()
+ * must have been called with "ay" as the type
+ * @param value The data to be added to the dict entry's array
+ * @param value_len The length of the data
+ * @return TRUE on success, FALSE on failure
+ *
+ */
+dbus_bool_t wpa_dbus_dict_bin_array_add_element(DBusMessageIter *iter_array,
+ const u8 *value,
+ size_t value_len)
+{
+ DBusMessageIter iter_bytes;
+ size_t i;
+
+ if (!iter_array || !value)
+ return FALSE;
+
+ if (!dbus_message_iter_open_container(iter_array, DBUS_TYPE_ARRAY,
+ DBUS_TYPE_BYTE_AS_STRING,
+ &iter_bytes))
+ return FALSE;
+
+ for (i = 0; i < value_len; i++) {
+ if (!dbus_message_iter_append_basic(&iter_bytes,
+ DBUS_TYPE_BYTE,
+ &(value[i])))
+ return FALSE;
+ }
+
+ if (!dbus_message_iter_close_container(iter_array, &iter_bytes))
+ return FALSE;
+
+ return TRUE;
+}
+
+
+/**
+ * End an array dict entry
*
* @param iter_dict A valid DBusMessageIter returned from
* wpa_dbus_dict_open_write()
* @param iter_dict_entry A private DBusMessageIter returned from
- * wpa_dbus_dict_end_string_array()
+ * wpa_dbus_dict_begin_string_array() or
+ * wpa_dbus_dict_begin_array()
* @param iter_dict_val A private DBusMessageIter returned from
- * wpa_dbus_dict_end_string_array()
+ * wpa_dbus_dict_begin_string_array() or
+ * wpa_dbus_dict_begin_array()
* @param iter_array A DBusMessageIter returned from
- * wpa_dbus_dict_end_string_array()
+ * wpa_dbus_dict_begin_string_array() or
+ * wpa_dbus_dict_begin_array()
* @return TRUE on success, FALSE on failure
*
*/
-dbus_bool_t wpa_dbus_dict_end_string_array(DBusMessageIter *iter_dict,
- DBusMessageIter *iter_dict_entry,
- DBusMessageIter *iter_dict_val,
- DBusMessageIter *iter_array)
+dbus_bool_t wpa_dbus_dict_end_array(DBusMessageIter *iter_dict,
+ DBusMessageIter *iter_dict_entry,
+ DBusMessageIter *iter_dict_val,
+ DBusMessageIter *iter_array)
{
if (!iter_dict || !iter_dict_entry || !iter_dict_val || !iter_array)
return FALSE;
@@ -583,6 +643,52 @@ dbus_bool_t wpa_dbus_dict_append_string_array(DBusMessageIter *iter_dict,
}
+/**
+ * Convenience function to add an wpabuf binary array to the dict.
+ *
+ * @param iter_dict A valid DBusMessageIter returned from
+ * wpa_dbus_dict_open_write()
+ * @param key The key of the dict item
+ * @param items The array of wpabuf structures
+ * @param num_items The number of strings in the array
+ * @return TRUE on success, FALSE on failure
+ *
+ */
+dbus_bool_t wpa_dbus_dict_append_wpabuf_array(DBusMessageIter *iter_dict,
+ const char *key,
+ const struct wpabuf **items,
+ const dbus_uint32_t num_items)
+{
+ DBusMessageIter iter_dict_entry, iter_dict_val, iter_array;
+ dbus_uint32_t i;
+
+ if (!key)
+ return FALSE;
+ if (!items && (num_items != 0))
+ return FALSE;
+
+ if (!wpa_dbus_dict_begin_array(iter_dict, key,
+ DBUS_TYPE_ARRAY_AS_STRING
+ DBUS_TYPE_BYTE_AS_STRING,
+ &iter_dict_entry, &iter_dict_val,
+ &iter_array))
+ return FALSE;
+
+ for (i = 0; i < num_items; i++) {
+ if (!wpa_dbus_dict_bin_array_add_element(&iter_array,
+ wpabuf_head(items[i]),
+ wpabuf_len(items[i])))
+ return FALSE;
+ }
+
+ if (!wpa_dbus_dict_end_array(iter_dict, &iter_dict_entry,
+ &iter_dict_val, &iter_array))
+ return FALSE;
+
+ return TRUE;
+}
+
+
/*****************************************************/
/* Stuff for reading dicts */
/*****************************************************/
@@ -593,18 +699,26 @@ dbus_bool_t wpa_dbus_dict_append_string_array(DBusMessageIter *iter_dict,
* @param iter A valid DBusMessageIter pointing to the start of the dict
* @param iter_dict (out) A DBusMessageIter to be passed to
* wpa_dbus_dict_read_next_entry()
+ * @error on failure a descriptive error
* @return TRUE on success, FALSE on failure
*
*/
dbus_bool_t wpa_dbus_dict_open_read(DBusMessageIter *iter,
- DBusMessageIter *iter_dict)
+ DBusMessageIter *iter_dict,
+ DBusError *error)
{
- if (!iter || !iter_dict)
+ if (!iter || !iter_dict) {
+ dbus_set_error_const(error, DBUS_ERROR_FAILED,
+ "[internal] missing message iterators");
return FALSE;
+ }
if (dbus_message_iter_get_arg_type(iter) != DBUS_TYPE_ARRAY ||
- dbus_message_iter_get_element_type(iter) != DBUS_TYPE_DICT_ENTRY)
+ dbus_message_iter_get_element_type(iter) != DBUS_TYPE_DICT_ENTRY) {
+ dbus_set_error_const(error, DBUS_ERROR_INVALID_ARGS,
+ "unexpected message argument types");
return FALSE;
+ }
dbus_message_iter_recurse(iter, iter_dict);
return TRUE;
@@ -615,17 +729,16 @@ dbus_bool_t wpa_dbus_dict_open_read(DBusMessageIter *iter,
#define BYTE_ARRAY_ITEM_SIZE (sizeof(char))
static dbus_bool_t _wpa_dbus_dict_entry_get_byte_array(
- DBusMessageIter *iter, int array_type,
- struct wpa_dbus_dict_entry *entry)
+ DBusMessageIter *iter, struct wpa_dbus_dict_entry *entry)
{
dbus_uint32_t count = 0;
dbus_bool_t success = FALSE;
- char *buffer, *nbuffer;;
+ char *buffer, *nbuffer;
entry->bytearray_value = NULL;
entry->array_type = DBUS_TYPE_BYTE;
- buffer = os_zalloc(BYTE_ARRAY_ITEM_SIZE * BYTE_ARRAY_CHUNK_SIZE);
+ buffer = os_calloc(BYTE_ARRAY_CHUNK_SIZE, BYTE_ARRAY_ITEM_SIZE);
if (!buffer)
return FALSE;
@@ -635,8 +748,9 @@ static dbus_bool_t _wpa_dbus_dict_entry_get_byte_array(
char byte;
if ((count % BYTE_ARRAY_CHUNK_SIZE) == 0 && count != 0) {
- nbuffer = os_realloc(buffer, BYTE_ARRAY_ITEM_SIZE *
- (count + BYTE_ARRAY_CHUNK_SIZE));
+ nbuffer = os_realloc_array(
+ buffer, count + BYTE_ARRAY_CHUNK_SIZE,
+ BYTE_ARRAY_ITEM_SIZE);
if (nbuffer == NULL) {
os_free(buffer);
wpa_printf(MSG_ERROR, "dbus: _wpa_dbus_dict_"
@@ -682,7 +796,7 @@ static dbus_bool_t _wpa_dbus_dict_entry_get_string_array(
entry->strarray_value = NULL;
entry->array_type = DBUS_TYPE_STRING;
- buffer = os_zalloc(STR_ARRAY_ITEM_SIZE * STR_ARRAY_CHUNK_SIZE);
+ buffer = os_calloc(STR_ARRAY_CHUNK_SIZE, STR_ARRAY_ITEM_SIZE);
if (buffer == NULL)
return FALSE;
@@ -693,8 +807,9 @@ static dbus_bool_t _wpa_dbus_dict_entry_get_string_array(
char *str;
if ((count % STR_ARRAY_CHUNK_SIZE) == 0 && count != 0) {
- nbuffer = os_realloc(buffer, STR_ARRAY_ITEM_SIZE *
- (count + STR_ARRAY_CHUNK_SIZE));
+ nbuffer = os_realloc_array(
+ buffer, count + STR_ARRAY_CHUNK_SIZE,
+ STR_ARRAY_ITEM_SIZE);
if (nbuffer == NULL) {
os_free(buffer);
wpa_printf(MSG_ERROR, "dbus: _wpa_dbus_dict_"
@@ -733,6 +848,66 @@ done:
}
+#define BIN_ARRAY_CHUNK_SIZE 10
+#define BIN_ARRAY_ITEM_SIZE (sizeof(struct wpabuf *))
+
+static dbus_bool_t _wpa_dbus_dict_entry_get_binarray(
+ DBusMessageIter *iter, struct wpa_dbus_dict_entry *entry)
+{
+ struct wpa_dbus_dict_entry tmpentry;
+ size_t buflen = 0;
+ int i;
+
+ if (dbus_message_iter_get_element_type(iter) != DBUS_TYPE_BYTE)
+ return FALSE;
+
+ entry->array_type = WPAS_DBUS_TYPE_BINARRAY;
+ entry->array_len = 0;
+ entry->binarray_value = NULL;
+
+ while (dbus_message_iter_get_arg_type(iter) == DBUS_TYPE_ARRAY) {
+ DBusMessageIter iter_array;
+
+ if (entry->array_len == buflen) {
+ struct wpabuf **newbuf;
+
+ buflen += BIN_ARRAY_CHUNK_SIZE;
+
+ newbuf = os_realloc_array(entry->binarray_value,
+ buflen, BIN_ARRAY_ITEM_SIZE);
+ if (!newbuf)
+ goto cleanup;
+ entry->binarray_value = newbuf;
+ }
+
+ dbus_message_iter_recurse(iter, &iter_array);
+ if (_wpa_dbus_dict_entry_get_byte_array(&iter_array, &tmpentry)
+ == FALSE)
+ goto cleanup;
+
+ entry->binarray_value[entry->array_len] =
+ wpabuf_alloc_ext_data((u8 *) tmpentry.bytearray_value,
+ tmpentry.array_len);
+ if (entry->binarray_value[entry->array_len] == NULL) {
+ wpa_dbus_dict_entry_clear(&tmpentry);
+ goto cleanup;
+ }
+ entry->array_len++;
+ dbus_message_iter_next(iter);
+ }
+
+ return TRUE;
+
+ cleanup:
+ for (i = 0; i < (int) entry->array_len; i++)
+ wpabuf_free(entry->binarray_value[i]);
+ os_free(entry->binarray_value);
+ entry->array_len = 0;
+ entry->binarray_value = NULL;
+ return FALSE;
+}
+
+
static dbus_bool_t _wpa_dbus_dict_entry_get_array(
DBusMessageIter *iter_dict_val, struct wpa_dbus_dict_entry *entry)
{
@@ -748,7 +923,6 @@ static dbus_bool_t _wpa_dbus_dict_entry_get_array(
switch (array_type) {
case DBUS_TYPE_BYTE:
success = _wpa_dbus_dict_entry_get_byte_array(&iter_array,
- array_type,
entry);
break;
case DBUS_TYPE_STRING:
@@ -756,6 +930,8 @@ static dbus_bool_t _wpa_dbus_dict_entry_get_array(
array_type,
entry);
break;
+ case DBUS_TYPE_ARRAY:
+ success = _wpa_dbus_dict_entry_get_binarray(&iter_array, entry);
default:
break;
}
@@ -915,9 +1091,14 @@ void wpa_dbus_dict_entry_clear(struct wpa_dbus_dict_entry *entry)
os_free(entry->strarray_value[i]);
os_free(entry->strarray_value);
break;
+ case WPAS_DBUS_TYPE_BINARRAY:
+ for (i = 0; i < entry->array_len; i++)
+ wpabuf_free(entry->binarray_value[i]);
+ os_free(entry->binarray_value);
+ break;
}
break;
}
- memset(entry, 0, sizeof(struct wpa_dbus_dict_entry));
+ os_memset(entry, 0, sizeof(struct wpa_dbus_dict_entry));
}
diff --git a/contrib/wpa/wpa_supplicant/dbus/dbus_dict_helpers.h b/contrib/wpa/wpa_supplicant/dbus/dbus_dict_helpers.h
index eb31575..9666349 100644
--- a/contrib/wpa/wpa_supplicant/dbus/dbus_dict_helpers.h
+++ b/contrib/wpa/wpa_supplicant/dbus/dbus_dict_helpers.h
@@ -2,19 +2,15 @@
* WPA Supplicant / dbus-based control interface
* Copyright (c) 2006, Dan Williams <dcbw@redhat.com> and Red Hat, Inc.
*
- * This program is free software; you can redistribute it and/or modify
- * it under the terms of the GNU General Public License version 2 as
- * published by the Free Software Foundation.
- *
- * Alternatively, this software may be distributed under the terms of BSD
- * license.
- *
- * See README and COPYING for more details.
+ * This software may be distributed under the terms of the BSD license.
+ * See README for more details.
*/
#ifndef DBUS_DICT_HELPERS_H
#define DBUS_DICT_HELPERS_H
+#include "wpabuf.h"
+
/*
* Adding a dict to a DBusMessage
*/
@@ -74,7 +70,13 @@ dbus_bool_t wpa_dbus_dict_append_byte_array(DBusMessageIter *iter_dict,
const char *value,
const dbus_uint32_t value_len);
-/* Manual construction and addition of string array elements */
+/* Manual construction and addition of array elements */
+dbus_bool_t wpa_dbus_dict_begin_array(DBusMessageIter *iter_dict,
+ const char *key, const char *type,
+ DBusMessageIter *iter_dict_entry,
+ DBusMessageIter *iter_dict_val,
+ DBusMessageIter *iter_array);
+
dbus_bool_t wpa_dbus_dict_begin_string_array(DBusMessageIter *iter_dict,
const char *key,
DBusMessageIter *iter_dict_entry,
@@ -84,10 +86,24 @@ dbus_bool_t wpa_dbus_dict_begin_string_array(DBusMessageIter *iter_dict,
dbus_bool_t wpa_dbus_dict_string_array_add_element(DBusMessageIter *iter_array,
const char *elem);
-dbus_bool_t wpa_dbus_dict_end_string_array(DBusMessageIter *iter_dict,
- DBusMessageIter *iter_dict_entry,
- DBusMessageIter *iter_dict_val,
- DBusMessageIter *iter_array);
+dbus_bool_t wpa_dbus_dict_bin_array_add_element(DBusMessageIter *iter_array,
+ const u8 *value,
+ size_t value_len);
+
+dbus_bool_t wpa_dbus_dict_end_array(DBusMessageIter *iter_dict,
+ DBusMessageIter *iter_dict_entry,
+ DBusMessageIter *iter_dict_val,
+ DBusMessageIter *iter_array);
+
+static inline dbus_bool_t
+wpa_dbus_dict_end_string_array(DBusMessageIter *iter_dict,
+ DBusMessageIter *iter_dict_entry,
+ DBusMessageIter *iter_dict_val,
+ DBusMessageIter *iter_array)
+{
+ return wpa_dbus_dict_end_array(iter_dict, iter_dict_entry,
+ iter_dict_val, iter_array);
+}
/* Convenience function to add a whole string list */
dbus_bool_t wpa_dbus_dict_append_string_array(DBusMessageIter *iter_dict,
@@ -95,14 +111,22 @@ dbus_bool_t wpa_dbus_dict_append_string_array(DBusMessageIter *iter_dict,
const char **items,
const dbus_uint32_t num_items);
+dbus_bool_t wpa_dbus_dict_append_wpabuf_array(DBusMessageIter *iter_dict,
+ const char *key,
+ const struct wpabuf **items,
+ const dbus_uint32_t num_items);
+
/*
* Reading a dict from a DBusMessage
*/
+#define WPAS_DBUS_TYPE_BINARRAY (DBUS_NUMBER_OF_TYPES + 100)
+
struct wpa_dbus_dict_entry {
int type; /** the dbus type of the dict entry's value */
int array_type; /** the dbus type of the array elements if the dict
- entry value contains an array */
+ entry value contains an array, or the special
+ WPAS_DBUS_TYPE_BINARRAY */
const char *key; /** key of the dict entry */
/** Possible values of the property */
@@ -119,13 +143,15 @@ struct wpa_dbus_dict_entry {
double double_value;
char *bytearray_value;
char **strarray_value;
+ struct wpabuf **binarray_value;
};
dbus_uint32_t array_len; /** length of the array if the dict entry's
value contains an array */
};
dbus_bool_t wpa_dbus_dict_open_read(DBusMessageIter *iter,
- DBusMessageIter *iter_dict);
+ DBusMessageIter *iter_dict,
+ DBusError *error);
dbus_bool_t wpa_dbus_dict_get_entry(DBusMessageIter *iter_dict,
struct wpa_dbus_dict_entry *entry);
diff --git a/contrib/wpa/wpa_supplicant/dbus/dbus_new.c b/contrib/wpa/wpa_supplicant/dbus/dbus_new.c
index bdfbbac..8bc6618 100644
--- a/contrib/wpa/wpa_supplicant/dbus/dbus_new.c
+++ b/contrib/wpa/wpa_supplicant/dbus/dbus_new.c
@@ -4,29 +4,119 @@
* Copyright (c) 2009-2010, Witold Sowa <witold.sowa@gmail.com>
* Copyright (c) 2009, Jouni Malinen <j@w1.fi>
*
- * This program is free software; you can redistribute it and/or modify
- * it under the terms of the GNU General Public License version 2 as
- * published by the Free Software Foundation.
- *
- * Alternatively, this software may be distributed under the terms of BSD
- * license.
- *
- * See README and COPYING for more details.
+ * This software may be distributed under the terms of the BSD license.
+ * See README for more details.
*/
#include "includes.h"
#include "common.h"
+#include "common/ieee802_11_defs.h"
#include "wps/wps.h"
#include "../config.h"
#include "../wpa_supplicant_i.h"
#include "../bss.h"
+#include "../wpas_glue.h"
#include "dbus_new_helpers.h"
#include "dbus_dict_helpers.h"
#include "dbus_new.h"
#include "dbus_new_handlers.h"
-#include "dbus_common.h"
#include "dbus_common_i.h"
+#include "dbus_new_handlers_p2p.h"
+#include "p2p/p2p.h"
+
+#ifdef CONFIG_AP /* until needed by something else */
+
+/*
+ * NameOwnerChanged handling
+ *
+ * Some services we provide allow an application to register for
+ * a signal that it needs. While it can also unregister, we must
+ * be prepared for the case where the application simply crashes
+ * and thus doesn't clean up properly. The way to handle this in
+ * DBus is to register for the NameOwnerChanged signal which will
+ * signal an owner change to NULL if the peer closes the socket
+ * for whatever reason.
+ *
+ * Handle this signal via a filter function whenever necessary.
+ * The code below also handles refcounting in case in the future
+ * there will be multiple instances of this subscription scheme.
+ */
+static const char wpas_dbus_noc_filter_str[] =
+ "interface=org.freedesktop.DBus,member=NameOwnerChanged";
+
+
+static DBusHandlerResult noc_filter(DBusConnection *conn,
+ DBusMessage *message, void *data)
+{
+ struct wpas_dbus_priv *priv = data;
+
+ if (dbus_message_get_type(message) != DBUS_MESSAGE_TYPE_SIGNAL)
+ return DBUS_HANDLER_RESULT_NOT_YET_HANDLED;
+
+ if (dbus_message_is_signal(message, DBUS_INTERFACE_DBUS,
+ "NameOwnerChanged")) {
+ const char *name;
+ const char *prev_owner;
+ const char *new_owner;
+ DBusError derr;
+ struct wpa_supplicant *wpa_s;
+
+ dbus_error_init(&derr);
+
+ if (!dbus_message_get_args(message, &derr,
+ DBUS_TYPE_STRING, &name,
+ DBUS_TYPE_STRING, &prev_owner,
+ DBUS_TYPE_STRING, &new_owner,
+ DBUS_TYPE_INVALID)) {
+ /* Ignore this error */
+ dbus_error_free(&derr);
+ return DBUS_HANDLER_RESULT_NOT_YET_HANDLED;
+ }
+
+ for (wpa_s = priv->global->ifaces; wpa_s; wpa_s = wpa_s->next)
+ {
+ if (wpa_s->preq_notify_peer != NULL &&
+ os_strcmp(name, wpa_s->preq_notify_peer) == 0 &&
+ (new_owner == NULL || os_strlen(new_owner) == 0)) {
+ /* probe request owner disconnected */
+ os_free(wpa_s->preq_notify_peer);
+ wpa_s->preq_notify_peer = NULL;
+ wpas_dbus_unsubscribe_noc(priv);
+ }
+ }
+ }
+
+ return DBUS_HANDLER_RESULT_NOT_YET_HANDLED;
+}
+
+
+void wpas_dbus_subscribe_noc(struct wpas_dbus_priv *priv)
+{
+ priv->dbus_noc_refcnt++;
+ if (priv->dbus_noc_refcnt > 1)
+ return;
+
+ if (!dbus_connection_add_filter(priv->con, noc_filter, priv, NULL)) {
+ wpa_printf(MSG_ERROR, "dbus: failed to add filter");
+ return;
+ }
+
+ dbus_bus_add_match(priv->con, wpas_dbus_noc_filter_str, NULL);
+}
+
+
+void wpas_dbus_unsubscribe_noc(struct wpas_dbus_priv *priv)
+{
+ priv->dbus_noc_refcnt--;
+ if (priv->dbus_noc_refcnt > 0)
+ return;
+
+ dbus_bus_remove_match(priv->con, wpas_dbus_noc_filter_str, NULL);
+ dbus_connection_remove_filter(priv->con, noc_filter, priv);
+}
+
+#endif /* CONFIG_AP */
/**
@@ -42,7 +132,7 @@ static void wpas_dbus_signal_interface(struct wpa_supplicant *wpa_s,
{
struct wpas_dbus_priv *iface;
DBusMessage *msg;
- DBusMessageIter iter, iter_dict;
+ DBusMessageIter iter;
iface = wpa_s->global->dbus;
@@ -61,14 +151,9 @@ static void wpas_dbus_signal_interface(struct wpa_supplicant *wpa_s,
goto err;
if (properties) {
- if (!wpa_dbus_dict_open_write(&iter, &iter_dict))
- goto err;
-
- wpa_dbus_get_object_properties(iface, wpa_s->dbus_new_path,
- WPAS_DBUS_NEW_IFACE_INTERFACE,
- &iter_dict);
-
- if (!wpa_dbus_dict_close_write(&iter, &iter_dict))
+ if (!wpa_dbus_get_object_properties(
+ iface, wpa_s->dbus_new_path,
+ WPAS_DBUS_NEW_IFACE_INTERFACE, &iter))
goto err;
}
@@ -157,7 +242,7 @@ static void wpas_dbus_signal_bss(struct wpa_supplicant *wpa_s,
{
struct wpas_dbus_priv *iface;
DBusMessage *msg;
- DBusMessageIter iter, iter_dict;
+ DBusMessageIter iter;
iface = wpa_s->global->dbus;
@@ -177,14 +262,9 @@ static void wpas_dbus_signal_bss(struct wpa_supplicant *wpa_s,
goto err;
if (properties) {
- if (!wpa_dbus_dict_open_write(&iter, &iter_dict))
- goto err;
-
- wpa_dbus_get_object_properties(iface, bss_obj_path,
- WPAS_DBUS_NEW_IFACE_BSS,
- &iter_dict);
-
- if (!wpa_dbus_dict_close_write(&iter, &iter_dict))
+ if (!wpa_dbus_get_object_properties(iface, bss_obj_path,
+ WPAS_DBUS_NEW_IFACE_BSS,
+ &iter))
goto err;
}
@@ -304,7 +384,7 @@ static void wpas_dbus_signal_network(struct wpa_supplicant *wpa_s,
{
struct wpas_dbus_priv *iface;
DBusMessage *msg;
- DBusMessageIter iter, iter_dict;
+ DBusMessageIter iter;
char net_obj_path[WPAS_DBUS_OBJECT_PATH_MAX], *path;
iface = wpa_s->global->dbus;
@@ -330,14 +410,9 @@ static void wpas_dbus_signal_network(struct wpa_supplicant *wpa_s,
goto err;
if (properties) {
- if (!wpa_dbus_dict_open_write(&iter, &iter_dict))
- goto err;
-
- wpa_dbus_get_object_properties(iface, net_obj_path,
- WPAS_DBUS_NEW_IFACE_NETWORK,
- &iter_dict);
-
- if (!wpa_dbus_dict_close_write(&iter, &iter_dict))
+ if (!wpa_dbus_get_object_properties(
+ iface, net_obj_path, WPAS_DBUS_NEW_IFACE_NETWORK,
+ &iter))
goto err;
}
@@ -394,6 +469,67 @@ void wpas_dbus_signal_network_selected(struct wpa_supplicant *wpa_s, int id)
/**
+ * wpas_dbus_signal_network_request - Indicate that additional information
+ * (EAP password, etc.) is required to complete the association to this SSID
+ * @wpa_s: %wpa_supplicant network interface data
+ * @rtype: The specific additional information required
+ * @default_text: Optional description of required information
+ *
+ * Request additional information or passwords to complete an association
+ * request.
+ */
+void wpas_dbus_signal_network_request(struct wpa_supplicant *wpa_s,
+ struct wpa_ssid *ssid,
+ enum wpa_ctrl_req_type rtype,
+ const char *default_txt)
+{
+ struct wpas_dbus_priv *iface;
+ DBusMessage *msg;
+ DBusMessageIter iter;
+ char net_obj_path[WPAS_DBUS_OBJECT_PATH_MAX];
+ const char *field, *txt = NULL, *net_ptr;
+
+ iface = wpa_s->global->dbus;
+
+ /* Do nothing if the control interface is not turned on */
+ if (iface == NULL)
+ return;
+
+ field = wpa_supplicant_ctrl_req_to_string(rtype, default_txt, &txt);
+ if (field == NULL)
+ return;
+
+ msg = dbus_message_new_signal(wpa_s->dbus_new_path,
+ WPAS_DBUS_NEW_IFACE_INTERFACE,
+ "NetworkRequest");
+ if (msg == NULL)
+ return;
+
+ os_snprintf(net_obj_path, WPAS_DBUS_OBJECT_PATH_MAX,
+ "%s/" WPAS_DBUS_NEW_NETWORKS_PART "/%u",
+ wpa_s->dbus_new_path, ssid->id);
+ net_ptr = &net_obj_path[0];
+
+ dbus_message_iter_init_append(msg, &iter);
+ if (!dbus_message_iter_append_basic(&iter, DBUS_TYPE_OBJECT_PATH,
+ &net_ptr))
+ goto err;
+ if (!dbus_message_iter_append_basic(&iter, DBUS_TYPE_STRING, &field))
+ goto err;
+ if (!dbus_message_iter_append_basic(&iter, DBUS_TYPE_STRING, &txt))
+ goto err;
+
+ dbus_connection_send(iface->con, msg, NULL);
+ dbus_message_unref(msg);
+ return;
+
+err:
+ wpa_printf(MSG_ERROR, "dbus: Failed to construct signal");
+ dbus_message_unref(msg);
+}
+
+
+/**
* wpas_dbus_signal_network_enabled_changed - Signals Enabled property changes
* @wpa_s: %wpa_supplicant network interface data
* @ssid: configured network which Enabled property has changed
@@ -650,47 +786,974 @@ nomem:
#endif /* CONFIG_WPS */
+void wpas_dbus_signal_certification(struct wpa_supplicant *wpa_s,
+ int depth, const char *subject,
+ const char *cert_hash,
+ const struct wpabuf *cert)
+{
+ struct wpas_dbus_priv *iface;
+ DBusMessage *msg;
+ DBusMessageIter iter, dict_iter;
+
+ iface = wpa_s->global->dbus;
+
+ /* Do nothing if the control interface is not turned on */
+ if (iface == NULL)
+ return;
+
+ msg = dbus_message_new_signal(wpa_s->dbus_new_path,
+ WPAS_DBUS_NEW_IFACE_INTERFACE,
+ "Certification");
+ if (msg == NULL)
+ return;
+
+ dbus_message_iter_init_append(msg, &iter);
+ if (!wpa_dbus_dict_open_write(&iter, &dict_iter))
+ goto nomem;
+
+ if (!wpa_dbus_dict_append_uint32(&dict_iter, "depth", depth) ||
+ !wpa_dbus_dict_append_string(&dict_iter, "subject", subject))
+ goto nomem;
+
+ if (cert_hash &&
+ !wpa_dbus_dict_append_string(&dict_iter, "cert_hash", cert_hash))
+ goto nomem;
+
+ if (cert &&
+ !wpa_dbus_dict_append_byte_array(&dict_iter, "cert",
+ wpabuf_head(cert),
+ wpabuf_len(cert)))
+ goto nomem;
+
+ if (!wpa_dbus_dict_close_write(&iter, &dict_iter))
+ goto nomem;
+
+ dbus_connection_send(iface->con, msg, NULL);
+
+nomem:
+ dbus_message_unref(msg);
+}
+
+
+void wpas_dbus_signal_eap_status(struct wpa_supplicant *wpa_s,
+ const char *status, const char *parameter)
+{
+ struct wpas_dbus_priv *iface;
+ DBusMessage *msg;
+ DBusMessageIter iter;
+
+ iface = wpa_s->global->dbus;
+
+ /* Do nothing if the control interface is not turned on */
+ if (iface == NULL)
+ return;
+
+ msg = dbus_message_new_signal(wpa_s->dbus_new_path,
+ WPAS_DBUS_NEW_IFACE_INTERFACE,
+ "EAP");
+ if (msg == NULL)
+ return;
+
+ dbus_message_iter_init_append(msg, &iter);
+
+ if (!dbus_message_iter_append_basic(&iter, DBUS_TYPE_STRING, &status)
+ ||
+ !dbus_message_iter_append_basic(&iter, DBUS_TYPE_STRING,
+ &parameter))
+ goto nomem;
+
+ dbus_connection_send(iface->con, msg, NULL);
+
+nomem:
+ dbus_message_unref(msg);
+}
+
+
+#ifdef CONFIG_P2P
+
+/**
+ * wpas_dbus_signal_p2p_group_removed - Signals P2P group was removed
+ * @wpa_s: %wpa_supplicant network interface data
+ * @role: role of this device (client or GO)
+ * Sends signal with i/f name and role as string arguments
+ */
+void wpas_dbus_signal_p2p_group_removed(struct wpa_supplicant *wpa_s,
+ const char *role)
+{
+
+ DBusMessage *msg;
+ DBusMessageIter iter;
+ struct wpas_dbus_priv *iface = wpa_s->global->dbus;
+ char *ifname = wpa_s->ifname;
+
+ /* Do nothing if the control interface is not turned on */
+ if (iface == NULL)
+ return;
+
+ msg = dbus_message_new_signal(wpa_s->dbus_new_path,
+ WPAS_DBUS_NEW_IFACE_P2PDEVICE,
+ "GroupFinished");
+ if (msg == NULL)
+ return;
+
+ dbus_message_iter_init_append(msg, &iter);
+
+ if (!dbus_message_iter_append_basic(&iter, DBUS_TYPE_STRING, &ifname)) {
+ wpa_printf(MSG_ERROR, "dbus: Failed to construct GroupFinished"
+ "signal -not enough memory for ifname ");
+ goto err;
+ }
+
+ if (!dbus_message_iter_append_basic(&iter, DBUS_TYPE_STRING, &role))
+ wpa_printf(MSG_ERROR, "dbus: Failed to construct GroupFinished"
+ "signal -not enough memory for role ");
+ else
+ dbus_connection_send(iface->con, msg, NULL);
+
+err:
+ dbus_message_unref(msg);
+}
+
+
+/**
+ * wpas_dbus_signal_p2p_provision_discovery - Signals various PD events
+ *
+ * @dev_addr - who sent the request or responded to our request.
+ * @request - Will be 1 if request, 0 for response.
+ * @status - valid only in case of response
+ * @config_methods - wps config methods
+ * @generated_pin - pin to be displayed in case of WPS_CONFIG_DISPLAY method
+ *
+ * Sends following provision discovery related events:
+ * ProvisionDiscoveryRequestDisplayPin
+ * ProvisionDiscoveryResponseDisplayPin
+ * ProvisionDiscoveryRequestEnterPin
+ * ProvisionDiscoveryResponseEnterPin
+ * ProvisionDiscoveryPBCRequest
+ * ProvisionDiscoveryPBCResponse
+ *
+ * TODO::
+ * ProvisionDiscoveryFailure (timeout case)
+ */
+void wpas_dbus_signal_p2p_provision_discovery(struct wpa_supplicant *wpa_s,
+ const u8 *dev_addr, int request,
+ enum p2p_prov_disc_status status,
+ u16 config_methods,
+ unsigned int generated_pin)
+{
+ DBusMessage *msg;
+ DBusMessageIter iter;
+ struct wpas_dbus_priv *iface;
+ char *_signal;
+ int add_pin = 0;
+ char peer_obj_path[WPAS_DBUS_OBJECT_PATH_MAX], *path;
+ int error_ret = 1;
+ char pin[9], *p_pin = NULL;
+
+ iface = wpa_s->global->dbus;
+
+ /* Do nothing if the control interface is not turned on */
+ if (iface == NULL)
+ return;
+
+ if (request || !status) {
+ if (config_methods & WPS_CONFIG_DISPLAY)
+ _signal = request ?
+ "ProvisionDiscoveryRequestDisplayPin" :
+ "ProvisionDiscoveryResponseEnterPin";
+ else if (config_methods & WPS_CONFIG_KEYPAD)
+ _signal = request ?
+ "ProvisionDiscoveryRequestEnterPin" :
+ "ProvisionDiscoveryResponseDisplayPin";
+ else if (config_methods & WPS_CONFIG_PUSHBUTTON)
+ _signal = request ? "ProvisionDiscoveryPBCRequest" :
+ "ProvisionDiscoveryPBCResponse";
+ else
+ return; /* Unknown or un-supported method */
+ } else if (!request && status)
+ /* Explicit check for failure response */
+ _signal = "ProvisionDiscoveryFailure";
+
+ add_pin = ((request && (config_methods & WPS_CONFIG_DISPLAY)) ||
+ (!request && !status &&
+ (config_methods & WPS_CONFIG_KEYPAD)));
+
+ if (add_pin) {
+ os_snprintf(pin, sizeof(pin), "%08d", generated_pin);
+ p_pin = pin;
+ }
+
+ msg = dbus_message_new_signal(wpa_s->dbus_new_path,
+ WPAS_DBUS_NEW_IFACE_P2PDEVICE, _signal);
+ if (msg == NULL)
+ return;
+
+ /* Check if this is a known peer */
+ if (!p2p_peer_known(wpa_s->global->p2p, dev_addr))
+ goto error;
+
+ os_snprintf(peer_obj_path, WPAS_DBUS_OBJECT_PATH_MAX,
+ "%s/" WPAS_DBUS_NEW_P2P_PEERS_PART "/"
+ COMPACT_MACSTR,
+ wpa_s->dbus_new_path, MAC2STR(dev_addr));
+
+ path = peer_obj_path;
+
+ dbus_message_iter_init_append(msg, &iter);
+
+ if (!dbus_message_iter_append_basic(&iter,
+ DBUS_TYPE_OBJECT_PATH,
+ &path))
+ goto error;
+
+ if (!request && status)
+ /* Attach status to ProvisionDiscoveryFailure */
+ error_ret = !dbus_message_iter_append_basic(&iter,
+ DBUS_TYPE_INT32,
+ &status);
+ else
+ error_ret = (add_pin &&
+ !dbus_message_iter_append_basic(&iter,
+ DBUS_TYPE_STRING,
+ &p_pin));
+
+error:
+ if (!error_ret)
+ dbus_connection_send(iface->con, msg, NULL);
+ else
+ wpa_printf(MSG_ERROR, "dbus: Failed to construct signal");
+
+ dbus_message_unref(msg);
+}
+
+
+void wpas_dbus_signal_p2p_go_neg_req(struct wpa_supplicant *wpa_s,
+ const u8 *src, u16 dev_passwd_id)
+{
+ DBusMessage *msg;
+ DBusMessageIter iter;
+ struct wpas_dbus_priv *iface;
+ char peer_obj_path[WPAS_DBUS_OBJECT_PATH_MAX], *path;
+
+ iface = wpa_s->global->dbus;
+
+ /* Do nothing if the control interface is not turned on */
+ if (iface == NULL)
+ return;
+
+ os_snprintf(peer_obj_path, WPAS_DBUS_OBJECT_PATH_MAX,
+ "%s/" WPAS_DBUS_NEW_P2P_PEERS_PART "/" COMPACT_MACSTR,
+ wpa_s->dbus_new_path, MAC2STR(src));
+ path = peer_obj_path;
+
+ msg = dbus_message_new_signal(wpa_s->dbus_new_path,
+ WPAS_DBUS_NEW_IFACE_P2PDEVICE,
+ "GONegotiationRequest");
+ if (msg == NULL)
+ return;
+
+ dbus_message_iter_init_append(msg, &iter);
+
+ if (!dbus_message_iter_append_basic(&iter, DBUS_TYPE_OBJECT_PATH,
+ &path) ||
+ !dbus_message_iter_append_basic(&iter, DBUS_TYPE_UINT16,
+ &dev_passwd_id))
+ wpa_printf(MSG_ERROR, "dbus: Failed to construct signal");
+ else
+ dbus_connection_send(iface->con, msg, NULL);
+
+ dbus_message_unref(msg);
+}
+
+
+static int wpas_dbus_get_group_obj_path(struct wpa_supplicant *wpa_s,
+ const struct wpa_ssid *ssid,
+ char *group_obj_path)
+{
+ char group_name[3];
+
+ if (os_memcmp(ssid->ssid, P2P_WILDCARD_SSID, P2P_WILDCARD_SSID_LEN))
+ return -1;
+
+ os_memcpy(group_name, ssid->ssid + P2P_WILDCARD_SSID_LEN, 2);
+ group_name[2] = '\0';
+
+ os_snprintf(group_obj_path, WPAS_DBUS_OBJECT_PATH_MAX,
+ "%s/" WPAS_DBUS_NEW_P2P_GROUPS_PART "/%s",
+ wpa_s->dbus_new_path, group_name);
+
+ return 0;
+}
+
+
+/**
+ * wpas_dbus_signal_p2p_group_started - Signals P2P group has
+ * started. Emitted when a group is successfully started
+ * irrespective of the role (client/GO) of the current device
+ *
+ * @wpa_s: %wpa_supplicant network interface data
+ * @ssid: SSID object
+ * @client: this device is P2P client
+ * @network_id: network id of the group started, use instead of ssid->id
+ * to account for persistent groups
+ */
+void wpas_dbus_signal_p2p_group_started(struct wpa_supplicant *wpa_s,
+ const struct wpa_ssid *ssid,
+ int client, int network_id)
+{
+ DBusMessage *msg;
+ DBusMessageIter iter, dict_iter;
+ struct wpas_dbus_priv *iface;
+ char group_obj_path[WPAS_DBUS_OBJECT_PATH_MAX];
+
+ iface = wpa_s->parent->global->dbus;
+
+ /* Do nothing if the control interface is not turned on */
+ if (iface == NULL)
+ return;
+
+ if (wpas_dbus_get_group_obj_path(wpa_s, ssid, group_obj_path) < 0)
+ return;
+
+ /* New interface has been created for this group */
+ msg = dbus_message_new_signal(wpa_s->parent->dbus_new_path,
+ WPAS_DBUS_NEW_IFACE_P2PDEVICE,
+ "GroupStarted");
+
+ if (msg == NULL)
+ return;
+
+ dbus_message_iter_init_append(msg, &iter);
+ if (!wpa_dbus_dict_open_write(&iter, &dict_iter))
+ goto nomem;
+
+ /*
+ * In case the device supports creating a separate interface the
+ * DBus client will need to know the object path for the interface
+ * object this group was created on, so include it here.
+ */
+ if (!wpa_dbus_dict_append_object_path(&dict_iter,
+ "interface_object",
+ wpa_s->dbus_new_path))
+ goto nomem;
+
+ if (!wpa_dbus_dict_append_string(&dict_iter, "role",
+ client ? "client" : "GO"))
+ goto nomem;
+
+ if (!wpa_dbus_dict_append_object_path(&dict_iter, "group_object",
+ group_obj_path) ||
+ !wpa_dbus_dict_close_write(&iter, &dict_iter))
+ goto nomem;
+
+ dbus_connection_send(iface->con, msg, NULL);
+
+nomem:
+ dbus_message_unref(msg);
+}
+
+
+/**
+ *
+ * Method to emit GONeogtiation Success or Failure signals based
+ * on status.
+ * @status: Status of the GO neg request. 0 for success, other for errors.
+ */
+void wpas_dbus_signal_p2p_go_neg_resp(struct wpa_supplicant *wpa_s,
+ struct p2p_go_neg_results *res)
+{
+ DBusMessage *msg;
+ DBusMessageIter iter, dict_iter;
+ DBusMessageIter iter_dict_entry, iter_dict_val, iter_dict_array;
+ struct wpas_dbus_priv *iface;
+ char peer_obj_path[WPAS_DBUS_OBJECT_PATH_MAX], *path;
+ dbus_int32_t freqs[P2P_MAX_CHANNELS];
+ dbus_int32_t *f_array = freqs;
+
+
+ iface = wpa_s->global->dbus;
+
+ os_memset(freqs, 0, sizeof(freqs));
+ /* Do nothing if the control interface is not turned on */
+ if (iface == NULL)
+ return;
+
+ os_snprintf(peer_obj_path, WPAS_DBUS_OBJECT_PATH_MAX,
+ "%s/" WPAS_DBUS_NEW_P2P_PEERS_PART "/" COMPACT_MACSTR,
+ wpa_s->dbus_new_path, MAC2STR(res->peer_device_addr));
+ path = peer_obj_path;
+
+ msg = dbus_message_new_signal(wpa_s->dbus_new_path,
+ WPAS_DBUS_NEW_IFACE_P2PDEVICE,
+ res->status ? "GONegotiationFailure" :
+ "GONegotiationSuccess");
+ if (msg == NULL)
+ return;
+
+ dbus_message_iter_init_append(msg, &iter);
+ if (!wpa_dbus_dict_open_write(&iter, &dict_iter))
+ goto err;
+ if (!wpa_dbus_dict_append_object_path(&dict_iter, "peer_object",
+ path) ||
+ !wpa_dbus_dict_append_int32(&dict_iter, "status", res->status))
+ goto err;
+
+ if (!res->status) {
+ int i = 0;
+ int freq_list_num = 0;
+
+ if (res->role_go) {
+ if (!wpa_dbus_dict_append_byte_array(
+ &dict_iter, "passphrase",
+ (const char *) res->passphrase,
+ sizeof(res->passphrase)))
+ goto err;
+ }
+
+ if (!wpa_dbus_dict_append_string(&dict_iter, "role_go",
+ res->role_go ? "GO" :
+ "client") ||
+ !wpa_dbus_dict_append_int32(&dict_iter, "frequency",
+ res->freq) ||
+ !wpa_dbus_dict_append_byte_array(&dict_iter, "ssid",
+ (const char *) res->ssid,
+ res->ssid_len) ||
+ !wpa_dbus_dict_append_byte_array(&dict_iter,
+ "peer_device_addr",
+ (const char *)
+ res->peer_device_addr,
+ ETH_ALEN) ||
+ !wpa_dbus_dict_append_byte_array(&dict_iter,
+ "peer_interface_addr",
+ (const char *)
+ res->peer_interface_addr,
+ ETH_ALEN) ||
+ !wpa_dbus_dict_append_string(&dict_iter, "wps_method",
+ p2p_wps_method_text(
+ res->wps_method)))
+ goto err;
+
+ for (i = 0; i < P2P_MAX_CHANNELS; i++) {
+ if (res->freq_list[i]) {
+ freqs[i] = res->freq_list[i];
+ freq_list_num++;
+ }
+ }
+
+ if (!wpa_dbus_dict_begin_array(&dict_iter,
+ "frequency_list",
+ DBUS_TYPE_INT32_AS_STRING,
+ &iter_dict_entry,
+ &iter_dict_val,
+ &iter_dict_array))
+ goto err;
+
+ if (!dbus_message_iter_append_fixed_array(&iter_dict_array,
+ DBUS_TYPE_INT32,
+ &f_array,
+ freq_list_num))
+ goto err;
+
+ if (!wpa_dbus_dict_end_array(&dict_iter,
+ &iter_dict_entry,
+ &iter_dict_val,
+ &iter_dict_array))
+ goto err;
+
+ if (!wpa_dbus_dict_append_int32(&dict_iter, "persistent_group",
+ res->persistent_group) ||
+ !wpa_dbus_dict_append_uint32(&dict_iter,
+ "peer_config_timeout",
+ res->peer_config_timeout))
+ goto err;
+ }
+
+ if (!wpa_dbus_dict_close_write(&iter, &dict_iter))
+ goto err;
+
+ dbus_connection_send(iface->con, msg, NULL);
+err:
+ dbus_message_unref(msg);
+}
+
+
+/**
+ *
+ * Method to emit Invitation Result signal based on status and
+ * bssid
+ * @status: Status of the Invite request. 0 for success, other
+ * for errors
+ * @bssid : Basic Service Set Identifier
+ */
+void wpas_dbus_signal_p2p_invitation_result(struct wpa_supplicant *wpa_s,
+ int status, const u8 *bssid)
+{
+ DBusMessage *msg;
+ DBusMessageIter iter, dict_iter;
+ struct wpas_dbus_priv *iface;
+
+ wpa_printf(MSG_INFO, "%s\n", __func__);
+
+ iface = wpa_s->global->dbus;
+ /* Do nothing if the control interface is not turned on */
+ if (iface == NULL)
+ return;
+
+ msg = dbus_message_new_signal(wpa_s->dbus_new_path,
+ WPAS_DBUS_NEW_IFACE_P2PDEVICE,
+ "InvitationResult");
+
+ if (msg == NULL)
+ return;
+
+ dbus_message_iter_init_append(msg, &iter);
+ if (!wpa_dbus_dict_open_write(&iter, &dict_iter))
+ goto nomem;
+
+ if (!wpa_dbus_dict_append_int32(&dict_iter, "status", status))
+ goto nomem;
+ if (bssid) {
+ if (!wpa_dbus_dict_append_byte_array(&dict_iter, "BSSID",
+ (const char *) bssid,
+ ETH_ALEN))
+ goto nomem;
+ }
+ if (!wpa_dbus_dict_close_write(&iter, &dict_iter))
+ goto nomem;
+
+ dbus_connection_send(iface->con, msg, NULL);
+
+nomem:
+ dbus_message_unref(msg);
+}
+
+
+/**
+ *
+ * Method to emit a signal for a peer joining the group.
+ * The signal will carry path to the group member object
+ * constructed using p2p i/f addr used for connecting.
+ *
+ * @wpa_s: %wpa_supplicant network interface data
+ * @member_addr: addr (p2p i/f) of the peer joining the group
+ */
+void wpas_dbus_signal_p2p_peer_joined(struct wpa_supplicant *wpa_s,
+ const u8 *member)
+{
+ struct wpas_dbus_priv *iface;
+ DBusMessage *msg;
+ DBusMessageIter iter;
+ char groupmember_obj_path[WPAS_DBUS_OBJECT_PATH_MAX], *path;
+
+ iface = wpa_s->global->dbus;
+
+ /* Do nothing if the control interface is not turned on */
+ if (iface == NULL)
+ return;
+
+ if (!wpa_s->dbus_groupobj_path)
+ return;
+
+ os_snprintf(groupmember_obj_path, WPAS_DBUS_OBJECT_PATH_MAX,
+ "%s/" WPAS_DBUS_NEW_P2P_GROUPMEMBERS_PART "/"
+ COMPACT_MACSTR,
+ wpa_s->dbus_groupobj_path, MAC2STR(member));
+
+ msg = dbus_message_new_signal(wpa_s->dbus_groupobj_path,
+ WPAS_DBUS_NEW_IFACE_P2P_GROUP,
+ "PeerJoined");
+ if (msg == NULL)
+ return;
+
+ dbus_message_iter_init_append(msg, &iter);
+ path = groupmember_obj_path;
+ if (!dbus_message_iter_append_basic(&iter, DBUS_TYPE_OBJECT_PATH,
+ &path))
+ goto err;
+
+ dbus_connection_send(iface->con, msg, NULL);
+
+ dbus_message_unref(msg);
+ return;
+
+err:
+ wpa_printf(MSG_ERROR, "dbus: Failed to construct signal");
+ dbus_message_unref(msg);
+}
+
+
+/**
+ *
+ * Method to emit a signal for a peer disconnecting the group.
+ * The signal will carry path to the group member object
+ * constructed using p2p i/f addr used for connecting.
+ *
+ * @wpa_s: %wpa_supplicant network interface data
+ * @member_addr: addr (p2p i/f) of the peer joining the group
+ */
+void wpas_dbus_signal_p2p_peer_disconnected(struct wpa_supplicant *wpa_s,
+ const u8 *member)
+{
+ struct wpas_dbus_priv *iface;
+ DBusMessage *msg;
+ DBusMessageIter iter;
+ char groupmember_obj_path[WPAS_DBUS_OBJECT_PATH_MAX], *path;
+
+ iface = wpa_s->global->dbus;
+
+ /* Do nothing if the control interface is not turned on */
+ if (iface == NULL)
+ return;
+
+ if (!wpa_s->dbus_groupobj_path)
+ return;
+
+ os_snprintf(groupmember_obj_path, WPAS_DBUS_OBJECT_PATH_MAX,
+ "%s/" WPAS_DBUS_NEW_P2P_GROUPMEMBERS_PART "/"
+ COMPACT_MACSTR,
+ wpa_s->dbus_groupobj_path, MAC2STR(member));
+
+ msg = dbus_message_new_signal(wpa_s->dbus_groupobj_path,
+ WPAS_DBUS_NEW_IFACE_P2P_GROUP,
+ "PeerDisconnected");
+ if (msg == NULL)
+ return;
+
+ dbus_message_iter_init_append(msg, &iter);
+ path = groupmember_obj_path;
+ if (!dbus_message_iter_append_basic(&iter, DBUS_TYPE_OBJECT_PATH,
+ &path))
+ goto err;
+
+ dbus_connection_send(iface->con, msg, NULL);
+
+ dbus_message_unref(msg);
+ return;
+
+err:
+ wpa_printf(MSG_ERROR, "dbus: Failed to construct PeerDisconnected "
+ "signal");
+ dbus_message_unref(msg);
+}
+
+
+/**
+ *
+ * Method to emit a signal for a service discovery request.
+ * The signal will carry station address, frequency, dialog token,
+ * update indicator and it tlvs
+ *
+ * @wpa_s: %wpa_supplicant network interface data
+ * @sa: station addr (p2p i/f) of the peer
+ * @dialog_token: service discovery request dialog token
+ * @update_indic: service discovery request update indicator
+ * @tlvs: service discovery request genrated byte array of tlvs
+ * @tlvs_len: service discovery request tlvs length
+ */
+void wpas_dbus_signal_p2p_sd_request(struct wpa_supplicant *wpa_s,
+ int freq, const u8 *sa, u8 dialog_token,
+ u16 update_indic, const u8 *tlvs,
+ size_t tlvs_len)
+{
+ DBusMessage *msg;
+ DBusMessageIter iter, dict_iter;
+ struct wpas_dbus_priv *iface;
+ char peer_obj_path[WPAS_DBUS_OBJECT_PATH_MAX], *path;
+ iface = wpa_s->global->dbus;
+
+ /* Do nothing if the control interface is not turned on */
+ if (iface == NULL)
+ return;
+
+ msg = dbus_message_new_signal(wpa_s->dbus_new_path,
+ WPAS_DBUS_NEW_IFACE_P2PDEVICE,
+ "ServiceDiscoveryRequest");
+ if (msg == NULL)
+ return;
+
+ /* Check if this is a known peer */
+ if (!p2p_peer_known(wpa_s->global->p2p, sa))
+ goto error;
+
+ os_snprintf(peer_obj_path, WPAS_DBUS_OBJECT_PATH_MAX,
+ "%s/" WPAS_DBUS_NEW_P2P_PEERS_PART "/"
+ COMPACT_MACSTR, wpa_s->dbus_new_path, MAC2STR(sa));
+
+ path = peer_obj_path;
+
+ dbus_message_iter_init_append(msg, &iter);
+ if (!wpa_dbus_dict_open_write(&iter, &dict_iter))
+ goto error;
+
+
+ if (!wpa_dbus_dict_append_object_path(&dict_iter, "peer_object",
+ path) ||
+ !wpa_dbus_dict_append_int32(&dict_iter, "frequency", freq) ||
+ !wpa_dbus_dict_append_int32(&dict_iter, "dialog_token",
+ dialog_token) ||
+ !wpa_dbus_dict_append_uint16(&dict_iter, "update_indicator",
+ update_indic) ||
+ !wpa_dbus_dict_append_byte_array(&dict_iter, "tlvs",
+ (const char *) tlvs,
+ tlvs_len) ||
+ !wpa_dbus_dict_close_write(&iter, &dict_iter))
+ goto error;
+
+ dbus_connection_send(iface->con, msg, NULL);
+ dbus_message_unref(msg);
+ return;
+error:
+ wpa_printf(MSG_ERROR, "dbus: Failed to construct signal");
+ dbus_message_unref(msg);
+}
+
+
+/**
+ *
+ * Method to emit a signal for a service discovery response.
+ * The signal will carry station address, update indicator and it
+ * tlvs
+ *
+ * @wpa_s: %wpa_supplicant network interface data
+ * @sa: station addr (p2p i/f) of the peer
+ * @update_indic: service discovery request update indicator
+ * @tlvs: service discovery request genrated byte array of tlvs
+ * @tlvs_len: service discovery request tlvs length
+ */
+void wpas_dbus_signal_p2p_sd_response(struct wpa_supplicant *wpa_s,
+ const u8 *sa, u16 update_indic,
+ const u8 *tlvs, size_t tlvs_len)
+{
+ DBusMessage *msg;
+ DBusMessageIter iter, dict_iter;
+ struct wpas_dbus_priv *iface;
+ char peer_obj_path[WPAS_DBUS_OBJECT_PATH_MAX], *path;
+ iface = wpa_s->global->dbus;
+
+ /* Do nothing if the control interface is not turned on */
+ if (iface == NULL)
+ return;
+
+ msg = dbus_message_new_signal(wpa_s->dbus_new_path,
+ WPAS_DBUS_NEW_IFACE_P2PDEVICE,
+ "ServiceDiscoveryResponse");
+ if (msg == NULL)
+ return;
+
+ /* Check if this is a known peer */
+ if (!p2p_peer_known(wpa_s->global->p2p, sa))
+ goto error;
+
+ os_snprintf(peer_obj_path, WPAS_DBUS_OBJECT_PATH_MAX,
+ "%s/" WPAS_DBUS_NEW_P2P_PEERS_PART "/"
+ COMPACT_MACSTR, wpa_s->dbus_new_path, MAC2STR(sa));
+
+ path = peer_obj_path;
+
+ dbus_message_iter_init_append(msg, &iter);
+ if (!wpa_dbus_dict_open_write(&iter, &dict_iter))
+ goto error;
+
+ if (!wpa_dbus_dict_append_object_path(&dict_iter, "peer_object",
+ path) ||
+ !wpa_dbus_dict_append_uint16(&dict_iter, "update_indicator",
+ update_indic) ||
+ !wpa_dbus_dict_append_byte_array(&dict_iter, "tlvs",
+ (const char *) tlvs,
+ tlvs_len) ||
+ !wpa_dbus_dict_close_write(&iter, &dict_iter))
+ goto error;
+
+
+ dbus_connection_send(iface->con, msg, NULL);
+ dbus_message_unref(msg);
+ return;
+error:
+ wpa_printf(MSG_ERROR, "dbus: Failed to construct signal");
+ dbus_message_unref(msg);
+}
+
+/**
+ * wpas_dbus_signal_persistent_group - Send a persistent group related
+ * event signal
+ * @wpa_s: %wpa_supplicant network interface data
+ * @id: new persistent group id
+ * @sig_name: signal name - PersistentGroupAdded, PersistentGroupRemoved
+ * @properties: determines if add second argument with object properties
+ *
+ * Notify listeners about an event related to persistent groups.
+ */
+static void wpas_dbus_signal_persistent_group(struct wpa_supplicant *wpa_s,
+ int id, const char *sig_name,
+ int properties)
+{
+ struct wpas_dbus_priv *iface;
+ DBusMessage *msg;
+ DBusMessageIter iter;
+ char pgrp_obj_path[WPAS_DBUS_OBJECT_PATH_MAX], *path;
+
+ iface = wpa_s->global->dbus;
+
+ /* Do nothing if the control interface is not turned on */
+ if (iface == NULL)
+ return;
+
+ os_snprintf(pgrp_obj_path, WPAS_DBUS_OBJECT_PATH_MAX,
+ "%s/" WPAS_DBUS_NEW_PERSISTENT_GROUPS_PART "/%u",
+ wpa_s->dbus_new_path, id);
+
+ msg = dbus_message_new_signal(wpa_s->dbus_new_path,
+ WPAS_DBUS_NEW_IFACE_P2PDEVICE,
+ sig_name);
+ if (msg == NULL)
+ return;
+
+ dbus_message_iter_init_append(msg, &iter);
+ path = pgrp_obj_path;
+ if (!dbus_message_iter_append_basic(&iter, DBUS_TYPE_OBJECT_PATH,
+ &path))
+ goto err;
+
+ if (properties) {
+ if (!wpa_dbus_get_object_properties(
+ iface, pgrp_obj_path,
+ WPAS_DBUS_NEW_IFACE_PERSISTENT_GROUP, &iter))
+ goto err;
+ }
+
+ dbus_connection_send(iface->con, msg, NULL);
+
+ dbus_message_unref(msg);
+ return;
+
+err:
+ wpa_printf(MSG_ERROR, "dbus: Failed to construct signal");
+ dbus_message_unref(msg);
+}
+
+
+/**
+ * wpas_dbus_signal_persistent_group_added - Send a persistent_group
+ * added signal
+ * @wpa_s: %wpa_supplicant network interface data
+ * @id: new persistent group id
+ *
+ * Notify listeners about addition of a new persistent group.
+ */
+static void wpas_dbus_signal_persistent_group_added(
+ struct wpa_supplicant *wpa_s, int id)
+{
+ wpas_dbus_signal_persistent_group(wpa_s, id, "PersistentGroupAdded",
+ TRUE);
+}
+
+
+/**
+ * wpas_dbus_signal_persistent_group_removed - Send a persistent_group
+ * removed signal
+ * @wpa_s: %wpa_supplicant network interface data
+ * @id: persistent group id
+ *
+ * Notify listeners about removal of a persistent group.
+ */
+static void wpas_dbus_signal_persistent_group_removed(
+ struct wpa_supplicant *wpa_s, int id)
+{
+ wpas_dbus_signal_persistent_group(wpa_s, id, "PersistentGroupRemoved",
+ FALSE);
+}
+
+
+/**
+ * wpas_dbus_signal_p2p_wps_failed - Signals WpsFailed event
+ * @wpa_s: %wpa_supplicant network interface data
+ *
+ * Sends Event dbus signal with name "fail" and dictionary containing
+ * "msg" field with fail message number (int32) as arguments
+ */
+void wpas_dbus_signal_p2p_wps_failed(struct wpa_supplicant *wpa_s,
+ struct wps_event_fail *fail)
+{
+
+ DBusMessage *msg;
+ DBusMessageIter iter, dict_iter;
+ struct wpas_dbus_priv *iface;
+ char *key = "fail";
+
+ iface = wpa_s->global->dbus;
+
+ /* Do nothing if the control interface is not turned on */
+ if (iface == NULL)
+ return;
+
+ msg = dbus_message_new_signal(wpa_s->dbus_new_path,
+ WPAS_DBUS_NEW_IFACE_P2PDEVICE,
+ "WpsFailed");
+ if (msg == NULL)
+ return;
+
+ dbus_message_iter_init_append(msg, &iter);
+
+ if (!dbus_message_iter_append_basic(&iter, DBUS_TYPE_STRING, &key) ||
+ !wpa_dbus_dict_open_write(&iter, &dict_iter) ||
+ !wpa_dbus_dict_append_int32(&dict_iter, "msg", fail->msg) ||
+ !wpa_dbus_dict_append_int16(&dict_iter, "config_error",
+ fail->config_error) ||
+ !wpa_dbus_dict_close_write(&iter, &dict_iter))
+ wpa_printf(MSG_ERROR, "dbus: Failed to construct signal");
+ else
+ dbus_connection_send(iface->con, msg, NULL);
+
+ dbus_message_unref(msg);
+}
+
+#endif /*CONFIG_P2P*/
+
/**
* wpas_dbus_signal_prop_changed - Signals change of property
* @wpa_s: %wpa_supplicant network interface data
* @property: indicates which property has changed
*
- * Sends ProertyChanged signals with path, interface and arguments
+ * Sends PropertyChanged signals with path, interface and arguments
* depending on which property has changed.
*/
void wpas_dbus_signal_prop_changed(struct wpa_supplicant *wpa_s,
enum wpas_dbus_prop property)
{
- WPADBusPropertyAccessor getter;
char *prop;
+ dbus_bool_t flush;
if (wpa_s->dbus_new_path == NULL)
return; /* Skip signal since D-Bus setup is not yet ready */
+ flush = FALSE;
switch (property) {
case WPAS_DBUS_PROP_AP_SCAN:
- getter = (WPADBusPropertyAccessor) wpas_dbus_getter_ap_scan;
prop = "ApScan";
break;
case WPAS_DBUS_PROP_SCANNING:
- getter = (WPADBusPropertyAccessor) wpas_dbus_getter_scanning;
prop = "Scanning";
break;
case WPAS_DBUS_PROP_STATE:
- getter = (WPADBusPropertyAccessor) wpas_dbus_getter_state;
prop = "State";
break;
case WPAS_DBUS_PROP_CURRENT_BSS:
- getter = (WPADBusPropertyAccessor)
- wpas_dbus_getter_current_bss;
prop = "CurrentBSS";
break;
case WPAS_DBUS_PROP_CURRENT_NETWORK:
- getter = (WPADBusPropertyAccessor)
- wpas_dbus_getter_current_network;
prop = "CurrentNetwork";
break;
+ case WPAS_DBUS_PROP_BSSS:
+ prop = "BSSs";
+ break;
+ case WPAS_DBUS_PROP_CURRENT_AUTH_MODE:
+ prop = "CurrentAuthMode";
+ break;
+ case WPAS_DBUS_PROP_DISCONNECT_REASON:
+ prop = "DisconnectReason";
+ flush = TRUE;
+ break;
default:
wpa_printf(MSG_ERROR, "dbus: %s: Unknown Property value %d",
__func__, property);
@@ -700,6 +1763,10 @@ void wpas_dbus_signal_prop_changed(struct wpa_supplicant *wpa_s,
wpa_dbus_mark_property_changed(wpa_s->global->dbus,
wpa_s->dbus_new_path,
WPAS_DBUS_NEW_IFACE_INTERFACE, prop);
+ if (flush) {
+ wpa_dbus_flush_object_changed_properties(
+ wpa_s->global->dbus->con, wpa_s->dbus_new_path);
+ }
}
@@ -763,7 +1830,7 @@ void wpas_dbus_bss_signal_prop_changed(struct wpa_supplicant *wpa_s,
* wpas_dbus_signal_debug_level_changed - Signals change of debug param
* @global: wpa_global structure
*
- * Sends ProertyChanged signals informing that debug level has changed.
+ * Sends PropertyChanged signals informing that debug level has changed.
*/
void wpas_dbus_signal_debug_level_changed(struct wpa_global *global)
{
@@ -777,7 +1844,7 @@ void wpas_dbus_signal_debug_level_changed(struct wpa_global *global)
* wpas_dbus_signal_debug_timestamp_changed - Signals change of debug param
* @global: wpa_global structure
*
- * Sends ProertyChanged signals informing that debug timestamp has changed.
+ * Sends PropertyChanged signals informing that debug timestamp has changed.
*/
void wpas_dbus_signal_debug_timestamp_changed(struct wpa_global *global)
{
@@ -791,7 +1858,7 @@ void wpas_dbus_signal_debug_timestamp_changed(struct wpa_global *global)
* wpas_dbus_signal_debug_show_keys_changed - Signals change of debug param
* @global: wpa_global structure
*
- * Sends ProertyChanged signals informing that debug show_keys has changed.
+ * Sends PropertyChanged signals informing that debug show_keys has changed.
*/
void wpas_dbus_signal_debug_show_keys_changed(struct wpa_global *global)
{
@@ -850,36 +1917,44 @@ static const struct wpa_dbus_method_desc wpas_dbus_global_methods[] = {
END_ARGS
}
},
+#ifdef CONFIG_AUTOSCAN
+ { "AutoScan", WPAS_DBUS_NEW_IFACE_INTERFACE,
+ (WPADBusMethodHandler) &wpas_dbus_handler_autoscan,
+ {
+ { "arg", "s", ARG_IN },
+ END_ARGS
+ }
+ },
+#endif /* CONFIG_AUTOSCAN */
{ NULL, NULL, NULL, { END_ARGS } }
};
static const struct wpa_dbus_property_desc wpas_dbus_global_properties[] = {
{ "DebugLevel", WPAS_DBUS_NEW_INTERFACE, "s",
- (WPADBusPropertyAccessor) wpas_dbus_getter_debug_level,
- (WPADBusPropertyAccessor) wpas_dbus_setter_debug_level,
- RW
+ wpas_dbus_getter_debug_level,
+ wpas_dbus_setter_debug_level
},
{ "DebugTimestamp", WPAS_DBUS_NEW_INTERFACE, "b",
- (WPADBusPropertyAccessor) wpas_dbus_getter_debug_timestamp,
- (WPADBusPropertyAccessor) wpas_dbus_setter_debug_timestamp,
- RW
+ wpas_dbus_getter_debug_timestamp,
+ wpas_dbus_setter_debug_timestamp
},
{ "DebugShowKeys", WPAS_DBUS_NEW_INTERFACE, "b",
- (WPADBusPropertyAccessor) wpas_dbus_getter_debug_show_keys,
- (WPADBusPropertyAccessor) wpas_dbus_setter_debug_show_keys,
- RW
+ wpas_dbus_getter_debug_show_keys,
+ wpas_dbus_setter_debug_show_keys
},
{ "Interfaces", WPAS_DBUS_NEW_INTERFACE, "ao",
- (WPADBusPropertyAccessor) &wpas_dbus_getter_interfaces,
- NULL,
- R
+ wpas_dbus_getter_interfaces,
+ NULL
},
{ "EapMethods", WPAS_DBUS_NEW_INTERFACE, "as",
- (WPADBusPropertyAccessor) wpas_dbus_getter_eap_methods,
- NULL,
- R
+ wpas_dbus_getter_eap_methods,
+ NULL
+ },
+ { "Capabilities", WPAS_DBUS_NEW_INTERFACE, "as",
+ wpas_dbus_getter_global_capabilities,
+ NULL
},
- { NULL, NULL, NULL, NULL, NULL, 0 }
+ { NULL, NULL, NULL, NULL, NULL }
};
static const struct wpa_dbus_signal_desc wpas_dbus_global_signals[] = {
@@ -896,6 +1971,15 @@ static const struct wpa_dbus_signal_desc wpas_dbus_global_signals[] = {
END_ARGS
}
},
+ { "NetworkRequest", WPAS_DBUS_NEW_IFACE_INTERFACE,
+ {
+ { "path", "o", ARG_OUT },
+ { "field", "s", ARG_OUT },
+ { "text", "s", ARG_OUT },
+ END_ARGS
+ }
+ },
+ /* Deprecated: use org.freedesktop.DBus.Properties.PropertiesChanged */
{ "PropertiesChanged", WPAS_DBUS_NEW_INTERFACE,
{
{ "properties", "a{sv}", ARG_OUT },
@@ -972,20 +2056,19 @@ static void wpa_dbus_free(void *ptr)
static const struct wpa_dbus_property_desc wpas_dbus_network_properties[] = {
{ "Properties", WPAS_DBUS_NEW_IFACE_NETWORK, "a{sv}",
- (WPADBusPropertyAccessor) wpas_dbus_getter_network_properties,
- (WPADBusPropertyAccessor) wpas_dbus_setter_network_properties,
- RW
+ wpas_dbus_getter_network_properties,
+ wpas_dbus_setter_network_properties
},
{ "Enabled", WPAS_DBUS_NEW_IFACE_NETWORK, "b",
- (WPADBusPropertyAccessor) wpas_dbus_getter_enabled,
- (WPADBusPropertyAccessor) wpas_dbus_setter_enabled,
- RW
+ wpas_dbus_getter_enabled,
+ wpas_dbus_setter_enabled
},
- { NULL, NULL, NULL, NULL, NULL, 0 }
+ { NULL, NULL, NULL, NULL, NULL }
};
static const struct wpa_dbus_signal_desc wpas_dbus_network_signals[] = {
+ /* Deprecated: use org.freedesktop.DBus.Properties.PropertiesChanged */
{ "PropertiesChanged", WPAS_DBUS_NEW_IFACE_NETWORK,
{
{ "properties", "a{sv}", ARG_OUT },
@@ -1012,6 +2095,16 @@ int wpas_dbus_register_network(struct wpa_supplicant *wpa_s,
struct network_handler_args *arg;
char net_obj_path[WPAS_DBUS_OBJECT_PATH_MAX];
+#ifdef CONFIG_P2P
+ /*
+ * If it is a persistent group register it as such.
+ * This is to handle cases where an interface is being initialized
+ * with a list of networks read from config.
+ */
+ if (network_is_persistent_group(ssid))
+ return wpas_dbus_register_persistent_group(wpa_s, ssid);
+#endif /* CONFIG_P2P */
+
/* Do nothing if the control interface is not turned on */
if (wpa_s == NULL || wpa_s->global == NULL)
return 0;
@@ -1074,10 +2167,18 @@ int wpas_dbus_unregister_network(struct wpa_supplicant *wpa_s, int nid)
struct wpas_dbus_priv *ctrl_iface;
char net_obj_path[WPAS_DBUS_OBJECT_PATH_MAX];
int ret;
+#ifdef CONFIG_P2P
+ struct wpa_ssid *ssid;
+
+ ssid = wpa_config_get_network(wpa_s->conf, nid);
+
+ /* If it is a persistent group unregister it as such */
+ if (ssid && network_is_persistent_group(ssid))
+ return wpas_dbus_unregister_persistent_group(wpa_s, nid);
+#endif /* CONFIG_P2P */
/* Do nothing if the control interface is not turned on */
- if (wpa_s == NULL || wpa_s->global == NULL ||
- wpa_s->dbus_new_path == NULL)
+ if (wpa_s->global == NULL || wpa_s->dbus_new_path == NULL)
return 0;
ctrl_iface = wpa_s->global->dbus;
if (ctrl_iface == NULL)
@@ -1100,60 +2201,55 @@ int wpas_dbus_unregister_network(struct wpa_supplicant *wpa_s, int nid)
static const struct wpa_dbus_property_desc wpas_dbus_bss_properties[] = {
{ "SSID", WPAS_DBUS_NEW_IFACE_BSS, "ay",
- (WPADBusPropertyAccessor) wpas_dbus_getter_bss_ssid,
- NULL,
- R
+ wpas_dbus_getter_bss_ssid,
+ NULL
},
{ "BSSID", WPAS_DBUS_NEW_IFACE_BSS, "ay",
- (WPADBusPropertyAccessor) wpas_dbus_getter_bss_bssid,
- NULL,
- R
+ wpas_dbus_getter_bss_bssid,
+ NULL
},
{ "Privacy", WPAS_DBUS_NEW_IFACE_BSS, "b",
- (WPADBusPropertyAccessor) wpas_dbus_getter_bss_privacy,
- NULL,
- R
+ wpas_dbus_getter_bss_privacy,
+ NULL
},
{ "Mode", WPAS_DBUS_NEW_IFACE_BSS, "s",
- (WPADBusPropertyAccessor) wpas_dbus_getter_bss_mode,
- NULL,
- R
+ wpas_dbus_getter_bss_mode,
+ NULL
},
{ "Signal", WPAS_DBUS_NEW_IFACE_BSS, "n",
- (WPADBusPropertyAccessor) wpas_dbus_getter_bss_signal,
- NULL,
- R
+ wpas_dbus_getter_bss_signal,
+ NULL
},
{ "Frequency", WPAS_DBUS_NEW_IFACE_BSS, "q",
- (WPADBusPropertyAccessor) wpas_dbus_getter_bss_frequency,
- NULL,
- R
+ wpas_dbus_getter_bss_frequency,
+ NULL
},
{ "Rates", WPAS_DBUS_NEW_IFACE_BSS, "au",
- (WPADBusPropertyAccessor) wpas_dbus_getter_bss_rates,
- NULL,
- R
+ wpas_dbus_getter_bss_rates,
+ NULL
},
{ "WPA", WPAS_DBUS_NEW_IFACE_BSS, "a{sv}",
- (WPADBusPropertyAccessor) wpas_dbus_getter_bss_wpa,
- NULL,
- R
+ wpas_dbus_getter_bss_wpa,
+ NULL
},
{ "RSN", WPAS_DBUS_NEW_IFACE_BSS, "a{sv}",
- (WPADBusPropertyAccessor) wpas_dbus_getter_bss_rsn,
- NULL,
- R
+ wpas_dbus_getter_bss_rsn,
+ NULL
+ },
+ { "WPS", WPAS_DBUS_NEW_IFACE_BSS, "a{sv}",
+ wpas_dbus_getter_bss_wps,
+ NULL
},
{ "IEs", WPAS_DBUS_NEW_IFACE_BSS, "ay",
- (WPADBusPropertyAccessor) wpas_dbus_getter_bss_ies,
- NULL,
- R
+ wpas_dbus_getter_bss_ies,
+ NULL
},
- { NULL, NULL, NULL, NULL, NULL, 0 }
+ { NULL, NULL, NULL, NULL, NULL }
};
static const struct wpa_dbus_signal_desc wpas_dbus_bss_signals[] = {
+ /* Deprecated: use org.freedesktop.DBus.Properties.PropertiesChanged */
{ "PropertiesChanged", WPAS_DBUS_NEW_IFACE_BSS,
{
{ "properties", "a{sv}", ARG_OUT },
@@ -1199,6 +2295,7 @@ int wpas_dbus_unregister_bss(struct wpa_supplicant *wpa_s,
}
wpas_dbus_signal_bss_removed(wpa_s, bss_obj_path);
+ wpas_dbus_signal_prop_changed(wpa_s, WPAS_DBUS_PROP_BSSS);
return 0;
}
@@ -1263,6 +2360,7 @@ int wpas_dbus_register_bss(struct wpa_supplicant *wpa_s,
}
wpas_dbus_signal_bss_added(wpa_s, bss_obj_path);
+ wpas_dbus_signal_prop_changed(wpa_s, WPAS_DBUS_PROP_BSSS);
return 0;
@@ -1294,6 +2392,12 @@ static const struct wpa_dbus_method_desc wpas_dbus_interface_methods[] = {
END_ARGS
}
},
+ { "Reassociate", WPAS_DBUS_NEW_IFACE_INTERFACE,
+ (WPADBusMethodHandler) &wpas_dbus_handler_reassociate,
+ {
+ END_ARGS
+ }
+ },
{ "RemoveNetwork", WPAS_DBUS_NEW_IFACE_INTERFACE,
(WPADBusMethodHandler) &wpas_dbus_handler_remove_network,
{
@@ -1301,6 +2405,12 @@ static const struct wpa_dbus_method_desc wpas_dbus_interface_methods[] = {
END_ARGS
}
},
+ { "RemoveAllNetworks", WPAS_DBUS_NEW_IFACE_INTERFACE,
+ (WPADBusMethodHandler) &wpas_dbus_handler_remove_all_networks,
+ {
+ END_ARGS
+ }
+ },
{ "SelectNetwork", WPAS_DBUS_NEW_IFACE_INTERFACE,
(WPADBusMethodHandler) &wpas_dbus_handler_select_network,
{
@@ -1308,6 +2418,15 @@ static const struct wpa_dbus_method_desc wpas_dbus_interface_methods[] = {
END_ARGS
}
},
+ { "NetworkReply", WPAS_DBUS_NEW_IFACE_INTERFACE,
+ (WPADBusMethodHandler) &wpas_dbus_handler_network_reply,
+ {
+ { "path", "o", ARG_IN },
+ { "field", "s", ARG_IN },
+ { "value", "s", ARG_IN },
+ END_ARGS
+ }
+ },
{ "AddBlob", WPAS_DBUS_NEW_IFACE_INTERFACE,
(WPADBusMethodHandler) &wpas_dbus_handler_add_blob,
{
@@ -1341,67 +2460,308 @@ static const struct wpa_dbus_method_desc wpas_dbus_interface_methods[] = {
}
},
#endif /* CONFIG_WPS */
+#ifdef CONFIG_P2P
+ { "Find", WPAS_DBUS_NEW_IFACE_P2PDEVICE,
+ (WPADBusMethodHandler)wpas_dbus_handler_p2p_find,
+ {
+ { "args", "a{sv}", ARG_IN },
+ END_ARGS
+ }
+ },
+ { "StopFind", WPAS_DBUS_NEW_IFACE_P2PDEVICE,
+ (WPADBusMethodHandler)wpas_dbus_handler_p2p_stop_find,
+ {
+ END_ARGS
+ }
+ },
+ { "Listen", WPAS_DBUS_NEW_IFACE_P2PDEVICE,
+ (WPADBusMethodHandler)wpas_dbus_handler_p2p_listen,
+ {
+ { "timeout", "i", ARG_IN },
+ END_ARGS
+ }
+ },
+ { "ExtendedListen", WPAS_DBUS_NEW_IFACE_P2PDEVICE,
+ (WPADBusMethodHandler)wpas_dbus_handler_p2p_extendedlisten,
+ {
+ { "args", "a{sv}", ARG_IN },
+ END_ARGS
+ }
+ },
+ { "PresenceRequest", WPAS_DBUS_NEW_IFACE_P2PDEVICE,
+ (WPADBusMethodHandler)wpas_dbus_handler_p2p_presence_request,
+ {
+ { "args", "a{sv}", ARG_IN },
+ END_ARGS
+ }
+ },
+ { "ProvisionDiscoveryRequest", WPAS_DBUS_NEW_IFACE_P2PDEVICE,
+ (WPADBusMethodHandler)wpas_dbus_handler_p2p_prov_disc_req,
+ {
+ { "peer", "o", ARG_IN },
+ { "config_method", "s", ARG_IN },
+ END_ARGS
+ }
+ },
+ { "Connect", WPAS_DBUS_NEW_IFACE_P2PDEVICE,
+ (WPADBusMethodHandler)wpas_dbus_handler_p2p_connect,
+ {
+ { "args", "a{sv}", ARG_IN },
+ { "generated_pin", "s", ARG_OUT },
+ END_ARGS
+ }
+ },
+ { "GroupAdd", WPAS_DBUS_NEW_IFACE_P2PDEVICE,
+ (WPADBusMethodHandler)wpas_dbus_handler_p2p_group_add,
+ {
+ { "args", "a{sv}", ARG_IN },
+ END_ARGS
+ }
+ },
+ { "Invite", WPAS_DBUS_NEW_IFACE_P2PDEVICE,
+ (WPADBusMethodHandler)wpas_dbus_handler_p2p_invite,
+ {
+ { "args", "a{sv}", ARG_IN },
+ END_ARGS
+ }
+ },
+ { "Disconnect", WPAS_DBUS_NEW_IFACE_P2PDEVICE,
+ (WPADBusMethodHandler)wpas_dbus_handler_p2p_disconnect,
+ {
+ END_ARGS
+ }
+ },
+ { "RejectPeer", WPAS_DBUS_NEW_IFACE_P2PDEVICE,
+ (WPADBusMethodHandler)wpas_dbus_handler_p2p_rejectpeer,
+ {
+ { "peer", "o", ARG_IN },
+ END_ARGS
+ }
+ },
+ { "Flush", WPAS_DBUS_NEW_IFACE_P2PDEVICE,
+ (WPADBusMethodHandler)wpas_dbus_handler_p2p_flush,
+ {
+ END_ARGS
+ }
+ },
+ { "AddService", WPAS_DBUS_NEW_IFACE_P2PDEVICE,
+ (WPADBusMethodHandler)wpas_dbus_handler_p2p_add_service,
+ {
+ { "args", "a{sv}", ARG_IN },
+ END_ARGS
+ }
+ },
+ { "DeleteService", WPAS_DBUS_NEW_IFACE_P2PDEVICE,
+ (WPADBusMethodHandler)wpas_dbus_handler_p2p_delete_service,
+ {
+ { "args", "a{sv}", ARG_IN },
+ END_ARGS
+ }
+ },
+ { "FlushService", WPAS_DBUS_NEW_IFACE_P2PDEVICE,
+ (WPADBusMethodHandler)wpas_dbus_handler_p2p_flush_service,
+ {
+ END_ARGS
+ }
+ },
+ { "ServiceDiscoveryRequest", WPAS_DBUS_NEW_IFACE_P2PDEVICE,
+ (WPADBusMethodHandler)wpas_dbus_handler_p2p_service_sd_req,
+ {
+ { "args", "a{sv}", ARG_IN },
+ END_ARGS
+ }
+ },
+ { "ServiceDiscoveryResponse", WPAS_DBUS_NEW_IFACE_P2PDEVICE,
+ (WPADBusMethodHandler)wpas_dbus_handler_p2p_service_sd_res,
+ {
+ { "args", "a{sv}", ARG_IN },
+ END_ARGS
+ }
+ },
+ { "ServiceDiscoveryCancelRequest", WPAS_DBUS_NEW_IFACE_P2PDEVICE,
+ (WPADBusMethodHandler)wpas_dbus_handler_p2p_service_sd_cancel_req,
+ {
+ { "args", "t", ARG_IN },
+ END_ARGS
+ }
+ },
+ { "ServiceUpdate", WPAS_DBUS_NEW_IFACE_P2PDEVICE,
+ (WPADBusMethodHandler)wpas_dbus_handler_p2p_service_update,
+ {
+ END_ARGS
+ }
+ },
+ { "ServiceDiscoveryExternal", WPAS_DBUS_NEW_IFACE_P2PDEVICE,
+ (WPADBusMethodHandler)wpas_dbus_handler_p2p_serv_disc_external,
+ {
+ { "arg", "i", ARG_IN },
+ END_ARGS
+ }
+ },
+ { "ServiceDiscoveryExternal", WPAS_DBUS_NEW_IFACE_P2PDEVICE,
+ (WPADBusMethodHandler)wpas_dbus_handler_p2p_serv_disc_external,
+ {
+ { "arg", "i", ARG_IN },
+ END_ARGS
+ }
+ },
+ { "AddPersistentGroup", WPAS_DBUS_NEW_IFACE_P2PDEVICE,
+ (WPADBusMethodHandler) wpas_dbus_handler_add_persistent_group,
+ {
+ { "args", "a{sv}", ARG_IN },
+ { "path", "o", ARG_OUT },
+ END_ARGS
+ }
+ },
+ { "RemovePersistentGroup", WPAS_DBUS_NEW_IFACE_P2PDEVICE,
+ (WPADBusMethodHandler) wpas_dbus_handler_remove_persistent_group,
+ {
+ { "path", "o", ARG_IN },
+ END_ARGS
+ }
+ },
+ { "RemoveAllPersistentGroups", WPAS_DBUS_NEW_IFACE_P2PDEVICE,
+ (WPADBusMethodHandler)
+ wpas_dbus_handler_remove_all_persistent_groups,
+ {
+ END_ARGS
+ }
+ },
+#endif /* CONFIG_P2P */
+ { "FlushBSS", WPAS_DBUS_NEW_IFACE_INTERFACE,
+ (WPADBusMethodHandler) &wpas_dbus_handler_flush_bss,
+ {
+ { "age", "u", ARG_IN },
+ END_ARGS
+ }
+ },
+#ifdef CONFIG_AP
+ { "SubscribeProbeReq", WPAS_DBUS_NEW_IFACE_INTERFACE,
+ (WPADBusMethodHandler) wpas_dbus_handler_subscribe_preq,
+ {
+ END_ARGS
+ }
+ },
+ { "UnsubscribeProbeReq", WPAS_DBUS_NEW_IFACE_INTERFACE,
+ (WPADBusMethodHandler) wpas_dbus_handler_unsubscribe_preq,
+ {
+ END_ARGS
+ }
+ },
+#endif /* CONFIG_AP */
{ NULL, NULL, NULL, { END_ARGS } }
};
static const struct wpa_dbus_property_desc wpas_dbus_interface_properties[] = {
{ "Capabilities", WPAS_DBUS_NEW_IFACE_INTERFACE, "a{sv}",
- (WPADBusPropertyAccessor) wpas_dbus_getter_capabilities,
- NULL, R
+ wpas_dbus_getter_capabilities,
+ NULL
},
{ "State", WPAS_DBUS_NEW_IFACE_INTERFACE, "s",
- (WPADBusPropertyAccessor) wpas_dbus_getter_state,
- NULL, R
+ wpas_dbus_getter_state,
+ NULL
},
{ "Scanning", WPAS_DBUS_NEW_IFACE_INTERFACE, "b",
- (WPADBusPropertyAccessor) wpas_dbus_getter_scanning,
- NULL, R
+ wpas_dbus_getter_scanning,
+ NULL
},
{ "ApScan", WPAS_DBUS_NEW_IFACE_INTERFACE, "u",
- (WPADBusPropertyAccessor) wpas_dbus_getter_ap_scan,
- (WPADBusPropertyAccessor) wpas_dbus_setter_ap_scan,
- RW
+ wpas_dbus_getter_ap_scan,
+ wpas_dbus_setter_ap_scan
+ },
+ { "BSSExpireAge", WPAS_DBUS_NEW_IFACE_INTERFACE, "u",
+ wpas_dbus_getter_bss_expire_age,
+ wpas_dbus_setter_bss_expire_age
+ },
+ { "BSSExpireCount", WPAS_DBUS_NEW_IFACE_INTERFACE, "u",
+ wpas_dbus_getter_bss_expire_count,
+ wpas_dbus_setter_bss_expire_count
+ },
+ { "Country", WPAS_DBUS_NEW_IFACE_INTERFACE, "s",
+ wpas_dbus_getter_country,
+ wpas_dbus_setter_country
},
{ "Ifname", WPAS_DBUS_NEW_IFACE_INTERFACE, "s",
- (WPADBusPropertyAccessor) wpas_dbus_getter_ifname,
- NULL, R
+ wpas_dbus_getter_ifname,
+ NULL
},
{ "Driver", WPAS_DBUS_NEW_IFACE_INTERFACE, "s",
- (WPADBusPropertyAccessor) wpas_dbus_getter_driver,
- NULL, R
+ wpas_dbus_getter_driver,
+ NULL
},
{ "BridgeIfname", WPAS_DBUS_NEW_IFACE_INTERFACE, "s",
- (WPADBusPropertyAccessor) wpas_dbus_getter_bridge_ifname,
- NULL, R
+ wpas_dbus_getter_bridge_ifname,
+ NULL
},
{ "CurrentBSS", WPAS_DBUS_NEW_IFACE_INTERFACE, "o",
- (WPADBusPropertyAccessor) wpas_dbus_getter_current_bss,
- NULL, R
+ wpas_dbus_getter_current_bss,
+ NULL
},
{ "CurrentNetwork", WPAS_DBUS_NEW_IFACE_INTERFACE, "o",
- (WPADBusPropertyAccessor) wpas_dbus_getter_current_network,
- NULL, R
+ wpas_dbus_getter_current_network,
+ NULL
+ },
+ { "CurrentAuthMode", WPAS_DBUS_NEW_IFACE_INTERFACE, "s",
+ wpas_dbus_getter_current_auth_mode,
+ NULL
},
{ "Blobs", WPAS_DBUS_NEW_IFACE_INTERFACE, "a{say}",
- (WPADBusPropertyAccessor) wpas_dbus_getter_blobs,
- NULL, R
+ wpas_dbus_getter_blobs,
+ NULL
},
{ "BSSs", WPAS_DBUS_NEW_IFACE_INTERFACE, "ao",
- (WPADBusPropertyAccessor) wpas_dbus_getter_bsss,
- NULL, R
+ wpas_dbus_getter_bsss,
+ NULL
},
{ "Networks", WPAS_DBUS_NEW_IFACE_INTERFACE, "ao",
- (WPADBusPropertyAccessor) wpas_dbus_getter_networks,
- NULL, R
+ wpas_dbus_getter_networks,
+ NULL
+ },
+ { "FastReauth", WPAS_DBUS_NEW_IFACE_INTERFACE, "b",
+ wpas_dbus_getter_fast_reauth,
+ wpas_dbus_setter_fast_reauth
+ },
+ { "ScanInterval", WPAS_DBUS_NEW_IFACE_INTERFACE, "i",
+ wpas_dbus_getter_scan_interval,
+ wpas_dbus_setter_scan_interval
},
#ifdef CONFIG_WPS
{ "ProcessCredentials", WPAS_DBUS_NEW_IFACE_WPS, "b",
- (WPADBusPropertyAccessor) wpas_dbus_getter_process_credentials,
- (WPADBusPropertyAccessor) wpas_dbus_setter_process_credentials,
- RW
+ wpas_dbus_getter_process_credentials,
+ wpas_dbus_setter_process_credentials
},
#endif /* CONFIG_WPS */
- { NULL, NULL, NULL, NULL, NULL, 0 }
+#ifdef CONFIG_P2P
+ { "P2PDeviceConfig", WPAS_DBUS_NEW_IFACE_P2PDEVICE, "a{sv}",
+ wpas_dbus_getter_p2p_device_config,
+ wpas_dbus_setter_p2p_device_config
+ },
+ { "Peers", WPAS_DBUS_NEW_IFACE_P2PDEVICE, "ao",
+ wpas_dbus_getter_p2p_peers,
+ NULL
+ },
+ { "Role", WPAS_DBUS_NEW_IFACE_P2PDEVICE, "s",
+ wpas_dbus_getter_p2p_role,
+ NULL
+ },
+ { "Group", WPAS_DBUS_NEW_IFACE_P2PDEVICE, "o",
+ wpas_dbus_getter_p2p_group,
+ NULL
+ },
+ { "PeerGO", WPAS_DBUS_NEW_IFACE_P2PDEVICE, "o",
+ wpas_dbus_getter_p2p_peergo,
+ NULL
+ },
+ { "PersistentGroups", WPAS_DBUS_NEW_IFACE_P2PDEVICE, "ao",
+ wpas_dbus_getter_persistent_groups,
+ NULL
+ },
+#endif /* CONFIG_P2P */
+ { "DisconnectReason", WPAS_DBUS_NEW_IFACE_INTERFACE, "i",
+ wpas_dbus_getter_disconnect_reason,
+ NULL
+ },
+ { NULL, NULL, NULL, NULL, NULL }
};
static const struct wpa_dbus_signal_desc wpas_dbus_interface_signals[] = {
@@ -1455,6 +2815,7 @@ static const struct wpa_dbus_signal_desc wpas_dbus_interface_signals[] = {
END_ARGS
}
},
+ /* Deprecated: use org.freedesktop.DBus.Properties.PropertiesChanged */
{ "PropertiesChanged", WPAS_DBUS_NEW_IFACE_INTERFACE,
{
{ "properties", "a{sv}", ARG_OUT },
@@ -1475,6 +2836,7 @@ static const struct wpa_dbus_signal_desc wpas_dbus_interface_signals[] = {
END_ARGS
}
},
+ /* Deprecated: use org.freedesktop.DBus.Properties.PropertiesChanged */
{ "PropertiesChanged", WPAS_DBUS_NEW_IFACE_WPS,
{
{ "properties", "a{sv}", ARG_OUT },
@@ -1482,6 +2844,162 @@ static const struct wpa_dbus_signal_desc wpas_dbus_interface_signals[] = {
}
},
#endif /* CONFIG_WPS */
+#ifdef CONFIG_P2P
+ { "P2PStateChanged", WPAS_DBUS_NEW_IFACE_P2PDEVICE,
+ {
+ { "states", "a{ss}", ARG_OUT },
+ END_ARGS
+ }
+ },
+ { "DeviceFound", WPAS_DBUS_NEW_IFACE_P2PDEVICE,
+ {
+ { "path", "o", ARG_OUT },
+ { "properties", "a{sv}", ARG_OUT },
+ END_ARGS
+ }
+ },
+ { "DeviceLost", WPAS_DBUS_NEW_IFACE_P2PDEVICE,
+ {
+ { "path", "o", ARG_OUT },
+ END_ARGS
+ }
+ },
+ { "ProvisionDiscoveryRequestDisplayPin", WPAS_DBUS_NEW_IFACE_P2PDEVICE,
+ {
+ { "peer_object", "o", ARG_OUT },
+ { "pin", "s", ARG_OUT },
+ END_ARGS
+ }
+ },
+ { "ProvisionDiscoveryResponseDisplayPin", WPAS_DBUS_NEW_IFACE_P2PDEVICE,
+ {
+ { "peer_object", "o", ARG_OUT },
+ { "pin", "s", ARG_OUT },
+ END_ARGS
+ }
+ },
+ { "ProvisionDiscoveryRequestEnterPin", WPAS_DBUS_NEW_IFACE_P2PDEVICE,
+ {
+ { "peer_object", "o", ARG_OUT },
+ END_ARGS
+ }
+ },
+ { "ProvisionDiscoveryResponseEnterPin", WPAS_DBUS_NEW_IFACE_P2PDEVICE,
+ {
+ { "peer_object", "o", ARG_OUT },
+ END_ARGS
+ }
+ },
+ { "ProvisionDiscoveryPBCRequest", WPAS_DBUS_NEW_IFACE_P2PDEVICE,
+ {
+ { "peer_object", "o", ARG_OUT },
+ END_ARGS
+ }
+ },
+ { "ProvisionDiscoveryPBCResponse", WPAS_DBUS_NEW_IFACE_P2PDEVICE,
+ {
+ { "peer_object", "o", ARG_OUT },
+ END_ARGS
+ }
+ },
+ { "ProvisionDiscoveryFailure", WPAS_DBUS_NEW_IFACE_P2PDEVICE,
+ {
+ { "peer_object", "o", ARG_OUT },
+ { "status", "i", ARG_OUT },
+ END_ARGS
+ }
+ },
+ { "GroupStarted", WPAS_DBUS_NEW_IFACE_P2PDEVICE,
+ {
+ { "properties", "a{sv}", ARG_OUT },
+ END_ARGS
+ }
+ },
+ { "GONegotiationSuccess", WPAS_DBUS_NEW_IFACE_P2PDEVICE,
+ {
+ END_ARGS
+ }
+ },
+ { "GONegotiationFailure", WPAS_DBUS_NEW_IFACE_P2PDEVICE,
+ {
+ { "status", "i", ARG_OUT },
+ END_ARGS
+ }
+ },
+ { "GONegotiationRequest", WPAS_DBUS_NEW_IFACE_P2PDEVICE,
+ {
+ { "path", "o", ARG_OUT },
+ { "dev_passwd_id", "i", ARG_OUT },
+ END_ARGS
+ }
+ },
+ { "InvitationResult", WPAS_DBUS_NEW_IFACE_P2PDEVICE,
+ {
+ { "invite_result", "a{sv}", ARG_OUT },
+ END_ARGS
+ }
+ },
+ { "GroupFinished", WPAS_DBUS_NEW_IFACE_P2PDEVICE,
+ {
+ { "ifname", "s", ARG_OUT },
+ { "role", "s", ARG_OUT },
+ END_ARGS
+ }
+ },
+ { "ServiceDiscoveryRequest", WPAS_DBUS_NEW_IFACE_P2PDEVICE,
+ {
+ { "sd_request", "a{sv}", ARG_OUT },
+ END_ARGS
+ }
+ },
+ { "ServiceDiscoveryResponse", WPAS_DBUS_NEW_IFACE_P2PDEVICE,
+ {
+ { "sd_response", "a{sv}", ARG_OUT },
+ END_ARGS
+ }
+ },
+ { "PersistentGroupAdded", WPAS_DBUS_NEW_IFACE_P2PDEVICE,
+ {
+ { "path", "o", ARG_OUT },
+ { "properties", "a{sv}", ARG_OUT },
+ END_ARGS
+ }
+ },
+ { "PersistentGroupRemoved", WPAS_DBUS_NEW_IFACE_P2PDEVICE,
+ {
+ { "path", "o", ARG_OUT },
+ END_ARGS
+ }
+ },
+ { "WpsFailed", WPAS_DBUS_NEW_IFACE_P2PDEVICE,
+ {
+ { "name", "s", ARG_OUT },
+ { "args", "a{sv}", ARG_OUT },
+ END_ARGS
+ }
+ },
+#endif /* CONFIG_P2P */
+#ifdef CONFIG_AP
+ { "ProbeRequest", WPAS_DBUS_NEW_IFACE_INTERFACE,
+ {
+ { "args", "a{sv}", ARG_OUT },
+ END_ARGS
+ }
+ },
+#endif /* CONFIG_AP */
+ { "Certification", WPAS_DBUS_NEW_IFACE_INTERFACE,
+ {
+ { "certification", "a{sv}", ARG_OUT },
+ END_ARGS
+ }
+ },
+ { "EAP", WPAS_DBUS_NEW_IFACE_INTERFACE,
+ {
+ { "status", "s", ARG_OUT },
+ { "parameter", "s", ARG_OUT },
+ END_ARGS
+ }
+ },
{ NULL, NULL, { END_ARGS } }
};
@@ -1549,6 +3067,15 @@ int wpas_dbus_unregister_interface(struct wpa_supplicant *wpa_s)
wpa_printf(MSG_DEBUG, "dbus: Unregister interface object '%s'",
wpa_s->dbus_new_path);
+
+#ifdef CONFIG_AP
+ if (wpa_s->preq_notify_peer) {
+ wpas_dbus_unsubscribe_noc(ctrl_iface);
+ os_free(wpa_s->preq_notify_peer);
+ wpa_s->preq_notify_peer = NULL;
+ }
+#endif /* CONFIG_AP */
+
if (wpa_dbus_unregister_object_per_iface(ctrl_iface,
wpa_s->dbus_new_path))
return -1;
@@ -1560,3 +3087,624 @@ int wpas_dbus_unregister_interface(struct wpa_supplicant *wpa_s)
return 0;
}
+
+#ifdef CONFIG_P2P
+
+static const struct wpa_dbus_property_desc wpas_dbus_p2p_peer_properties[] = {
+ { "DeviceName", WPAS_DBUS_NEW_IFACE_P2P_PEER, "s",
+ wpas_dbus_getter_p2p_peer_device_name,
+ NULL
+ },
+ { "PrimaryDeviceType", WPAS_DBUS_NEW_IFACE_P2P_PEER, "ay",
+ wpas_dbus_getter_p2p_peer_primary_device_type,
+ NULL
+ },
+ { "config_method", WPAS_DBUS_NEW_IFACE_P2P_PEER, "q",
+ wpas_dbus_getter_p2p_peer_config_method,
+ NULL
+ },
+ { "level", WPAS_DBUS_NEW_IFACE_P2P_PEER, "i",
+ wpas_dbus_getter_p2p_peer_level,
+ NULL
+ },
+ { "devicecapability", WPAS_DBUS_NEW_IFACE_P2P_PEER, "y",
+ wpas_dbus_getter_p2p_peer_device_capability,
+ NULL
+ },
+ { "groupcapability", WPAS_DBUS_NEW_IFACE_P2P_PEER, "y",
+ wpas_dbus_getter_p2p_peer_group_capability,
+ NULL
+ },
+ { "SecondaryDeviceTypes", WPAS_DBUS_NEW_IFACE_P2P_PEER, "aay",
+ wpas_dbus_getter_p2p_peer_secondary_device_types,
+ NULL
+ },
+ { "VendorExtension", WPAS_DBUS_NEW_IFACE_P2P_PEER, "aay",
+ wpas_dbus_getter_p2p_peer_vendor_extension,
+ NULL
+ },
+ { "IEs", WPAS_DBUS_NEW_IFACE_P2P_PEER, "ay",
+ wpas_dbus_getter_p2p_peer_ies,
+ NULL
+ },
+ { NULL, NULL, NULL, NULL, NULL }
+};
+
+static const struct wpa_dbus_signal_desc wpas_dbus_p2p_peer_signals[] = {
+
+ { NULL, NULL, { END_ARGS } }
+};
+
+/**
+ * wpas_dbus_signal_peer - Send a peer related event signal
+ * @wpa_s: %wpa_supplicant network interface data
+ * @dev: peer device object
+ * @interface: name of the interface emitting this signal.
+ * In case of peer objects, it would be emitted by either
+ * the "interface object" or by "peer objects"
+ * @sig_name: signal name - DeviceFound
+ *
+ * Notify listeners about event related with newly found p2p peer device
+ */
+static void wpas_dbus_signal_peer(struct wpa_supplicant *wpa_s,
+ const u8 *dev_addr, const char *interface,
+ const char *sig_name)
+{
+ struct wpas_dbus_priv *iface;
+ DBusMessage *msg;
+ DBusMessageIter iter;
+ char peer_obj_path[WPAS_DBUS_OBJECT_PATH_MAX], *path;
+
+ iface = wpa_s->global->dbus;
+
+ /* Do nothing if the control interface is not turned on */
+ if (iface == NULL)
+ return;
+
+ os_snprintf(peer_obj_path, WPAS_DBUS_OBJECT_PATH_MAX,
+ "%s/" WPAS_DBUS_NEW_P2P_PEERS_PART "/" COMPACT_MACSTR,
+ wpa_s->dbus_new_path, MAC2STR(dev_addr));
+
+ msg = dbus_message_new_signal(wpa_s->dbus_new_path, interface,
+ sig_name);
+ if (msg == NULL)
+ return;
+
+ dbus_message_iter_init_append(msg, &iter);
+ path = peer_obj_path;
+ if (!dbus_message_iter_append_basic(&iter, DBUS_TYPE_OBJECT_PATH,
+ &path))
+ goto err;
+
+ dbus_connection_send(iface->con, msg, NULL);
+
+ dbus_message_unref(msg);
+ return;
+
+err:
+ wpa_printf(MSG_ERROR, "dbus: Failed to construct signal");
+ dbus_message_unref(msg);
+}
+
+
+/**
+ * wpas_dbus_signal_peer_found - Send a peer found signal
+ * @wpa_s: %wpa_supplicant network interface data
+ * @dev: peer device object
+ *
+ * Notify listeners about find a p2p peer device found
+ */
+void wpas_dbus_signal_peer_device_found(struct wpa_supplicant *wpa_s,
+ const u8 *dev_addr)
+{
+ wpas_dbus_signal_peer(wpa_s, dev_addr,
+ WPAS_DBUS_NEW_IFACE_P2PDEVICE,
+ "DeviceFound");
+}
+
+/**
+ * wpas_dbus_signal_peer_lost - Send a peer lost signal
+ * @wpa_s: %wpa_supplicant network interface data
+ * @dev: peer device object
+ *
+ * Notify listeners about lost a p2p peer device
+ */
+void wpas_dbus_signal_peer_device_lost(struct wpa_supplicant *wpa_s,
+ const u8 *dev_addr)
+{
+ wpas_dbus_signal_peer(wpa_s, dev_addr,
+ WPAS_DBUS_NEW_IFACE_P2PDEVICE,
+ "DeviceLost");
+}
+
+/**
+ * wpas_dbus_register_peer - Register a discovered peer object with dbus
+ * @wpa_s: wpa_supplicant interface structure
+ * @ssid: network configuration data
+ * Returns: 0 on success, -1 on failure
+ *
+ * Registers network representing object with dbus
+ */
+int wpas_dbus_register_peer(struct wpa_supplicant *wpa_s, const u8 *dev_addr)
+{
+ struct wpas_dbus_priv *ctrl_iface;
+ struct wpa_dbus_object_desc *obj_desc;
+ struct peer_handler_args *arg;
+ char peer_obj_path[WPAS_DBUS_OBJECT_PATH_MAX];
+
+ /* Do nothing if the control interface is not turned on */
+ if (wpa_s == NULL || wpa_s->global == NULL)
+ return 0;
+
+ ctrl_iface = wpa_s->global->dbus;
+ if (ctrl_iface == NULL)
+ return 0;
+
+ os_snprintf(peer_obj_path, WPAS_DBUS_OBJECT_PATH_MAX,
+ "%s/" WPAS_DBUS_NEW_P2P_PEERS_PART "/" COMPACT_MACSTR,
+ wpa_s->dbus_new_path, MAC2STR(dev_addr));
+
+ wpa_printf(MSG_INFO, "dbus: Register peer object '%s'",
+ peer_obj_path);
+ obj_desc = os_zalloc(sizeof(struct wpa_dbus_object_desc));
+ if (!obj_desc) {
+ wpa_printf(MSG_ERROR, "Not enough memory "
+ "to create object description");
+ goto err;
+ }
+
+ /* allocate memory for handlers arguments */
+ arg = os_zalloc(sizeof(struct peer_handler_args));
+ if (!arg) {
+ wpa_printf(MSG_ERROR, "Not enough memory "
+ "to create arguments for method");
+ goto err;
+ }
+
+ arg->wpa_s = wpa_s;
+ os_memcpy(arg->p2p_device_addr, dev_addr, ETH_ALEN);
+
+ wpas_dbus_register(obj_desc, arg, wpa_dbus_free,
+ NULL,
+ wpas_dbus_p2p_peer_properties,
+ wpas_dbus_p2p_peer_signals);
+
+ if (wpa_dbus_register_object_per_iface(ctrl_iface, peer_obj_path,
+ wpa_s->ifname, obj_desc))
+ goto err;
+
+ return 0;
+
+err:
+ free_dbus_object_desc(obj_desc);
+ return -1;
+}
+
+/**
+ * wpas_dbus_unregister_peer - Unregister a peer object with dbus
+ * @wpa_s: wpa_supplicant interface structure
+ * @dev_addr: p2p device addr
+ * Returns: 0 on success, -1 on failure
+ *
+ * Registers network representing object with dbus
+ */
+int wpas_dbus_unregister_peer(struct wpa_supplicant *wpa_s,
+ const u8 *dev_addr)
+{
+ struct wpas_dbus_priv *ctrl_iface;
+ char peer_obj_path[WPAS_DBUS_OBJECT_PATH_MAX];
+ int ret;
+
+ /* Do nothing if the control interface is not turned on */
+ if (wpa_s == NULL || wpa_s->global == NULL ||
+ wpa_s->dbus_new_path == NULL)
+ return 0;
+ ctrl_iface = wpa_s->global->dbus;
+ if (ctrl_iface == NULL)
+ return 0;
+
+ os_snprintf(peer_obj_path, WPAS_DBUS_OBJECT_PATH_MAX,
+ "%s/" WPAS_DBUS_NEW_P2P_PEERS_PART "/" COMPACT_MACSTR,
+ wpa_s->dbus_new_path, MAC2STR(dev_addr));
+
+ wpa_printf(MSG_INFO, "dbus: Unregister peer object '%s'",
+ peer_obj_path);
+ ret = wpa_dbus_unregister_object_per_iface(ctrl_iface, peer_obj_path);
+
+ return ret;
+}
+
+
+static const struct wpa_dbus_property_desc wpas_dbus_p2p_group_properties[] = {
+ { "Members", WPAS_DBUS_NEW_IFACE_P2P_GROUP, "ao",
+ wpas_dbus_getter_p2p_group_members,
+ NULL
+ },
+ { "Group", WPAS_DBUS_NEW_IFACE_P2P_GROUP, "o",
+ wpas_dbus_getter_p2p_group,
+ NULL
+ },
+ { "Role", WPAS_DBUS_NEW_IFACE_P2P_GROUP, "s",
+ wpas_dbus_getter_p2p_role,
+ NULL
+ },
+ { "SSID", WPAS_DBUS_NEW_IFACE_P2P_GROUP, "ay",
+ wpas_dbus_getter_p2p_group_ssid,
+ NULL
+ },
+ { "BSSID", WPAS_DBUS_NEW_IFACE_P2P_GROUP, "ay",
+ wpas_dbus_getter_p2p_group_bssid,
+ NULL
+ },
+ { "Frequency", WPAS_DBUS_NEW_IFACE_P2P_GROUP, "q",
+ wpas_dbus_getter_p2p_group_frequency,
+ NULL
+ },
+ { "Passphrase", WPAS_DBUS_NEW_IFACE_P2P_GROUP, "s",
+ wpas_dbus_getter_p2p_group_passphrase,
+ NULL
+ },
+ { "PSK", WPAS_DBUS_NEW_IFACE_P2P_GROUP, "ay",
+ wpas_dbus_getter_p2p_group_psk,
+ NULL
+ },
+ { "WPSVendorExtensions", WPAS_DBUS_NEW_IFACE_P2P_GROUP, "aay",
+ wpas_dbus_getter_p2p_group_vendor_ext,
+ wpas_dbus_setter_p2p_group_vendor_ext
+ },
+ { NULL, NULL, NULL, NULL, NULL }
+};
+
+static const struct wpa_dbus_signal_desc wpas_dbus_p2p_group_signals[] = {
+ { "PeerJoined", WPAS_DBUS_NEW_IFACE_P2P_GROUP,
+ {
+ { "peer", "o", ARG_OUT },
+ END_ARGS
+ }
+ },
+ { "PeerDisconnected", WPAS_DBUS_NEW_IFACE_P2P_GROUP,
+ {
+ { "peer", "o", ARG_OUT },
+ END_ARGS
+ }
+ },
+ { NULL, NULL, { END_ARGS } }
+};
+
+/**
+ * wpas_dbus_register_p2p_group - Register a p2p group object with dbus
+ * @wpa_s: wpa_supplicant interface structure
+ * @ssid: SSID struct
+ * Returns: 0 on success, -1 on failure
+ *
+ * Registers p2p group representing object with dbus
+ */
+void wpas_dbus_register_p2p_group(struct wpa_supplicant *wpa_s,
+ struct wpa_ssid *ssid)
+{
+ struct wpas_dbus_priv *ctrl_iface;
+ struct wpa_dbus_object_desc *obj_desc;
+ char group_obj_path[WPAS_DBUS_OBJECT_PATH_MAX];
+
+ /* Do nothing if the control interface is not turned on */
+ if (wpa_s == NULL || wpa_s->global == NULL)
+ return;
+
+ ctrl_iface = wpa_s->global->dbus;
+ if (ctrl_iface == NULL)
+ return;
+
+ if (wpa_s->dbus_groupobj_path) {
+ wpa_printf(MSG_INFO, "%s: Group object '%s' already exists",
+ __func__, wpa_s->dbus_groupobj_path);
+ return;
+ }
+
+ if (wpas_dbus_get_group_obj_path(wpa_s, ssid, group_obj_path) < 0)
+ return;
+
+ wpa_s->dbus_groupobj_path = os_strdup(group_obj_path);
+ if (wpa_s->dbus_groupobj_path == NULL)
+ return;
+
+ wpa_printf(MSG_INFO, "dbus: Register group object '%s'",
+ group_obj_path);
+ obj_desc = os_zalloc(sizeof(struct wpa_dbus_object_desc));
+ if (!obj_desc) {
+ wpa_printf(MSG_ERROR, "Not enough memory "
+ "to create object description");
+ goto err;
+ }
+
+ wpas_dbus_register(obj_desc, wpa_s, NULL, NULL,
+ wpas_dbus_p2p_group_properties,
+ wpas_dbus_p2p_group_signals);
+
+ if (wpa_dbus_register_object_per_iface(ctrl_iface, group_obj_path,
+ wpa_s->ifname, obj_desc))
+ goto err;
+
+ return;
+
+err:
+ if (wpa_s->dbus_groupobj_path) {
+ os_free(wpa_s->dbus_groupobj_path);
+ wpa_s->dbus_groupobj_path = NULL;
+ }
+
+ free_dbus_object_desc(obj_desc);
+}
+
+/**
+ * wpas_dbus_unregister_p2p_group - Unregister a p2p group object from dbus
+ * @wpa_s: wpa_supplicant interface structure
+ * @ssid: network name of the p2p group started
+ */
+void wpas_dbus_unregister_p2p_group(struct wpa_supplicant *wpa_s,
+ const struct wpa_ssid *ssid)
+{
+ struct wpas_dbus_priv *ctrl_iface;
+
+ /* Do nothing if the control interface is not turned on */
+ if (wpa_s == NULL || wpa_s->global == NULL)
+ return;
+
+ ctrl_iface = wpa_s->global->dbus;
+ if (ctrl_iface == NULL)
+ return;
+
+ if (!wpa_s->dbus_groupobj_path) {
+ wpa_printf(MSG_DEBUG,
+ "%s: Group object '%s' already unregistered",
+ __func__, wpa_s->dbus_groupobj_path);
+ return;
+ }
+
+ wpa_printf(MSG_DEBUG, "dbus: Unregister group object '%s'",
+ wpa_s->dbus_groupobj_path);
+
+ wpa_dbus_unregister_object_per_iface(ctrl_iface,
+ wpa_s->dbus_groupobj_path);
+
+ os_free(wpa_s->dbus_groupobj_path);
+ wpa_s->dbus_groupobj_path = NULL;
+}
+
+static const struct wpa_dbus_property_desc
+wpas_dbus_p2p_groupmember_properties[] = {
+ { NULL, NULL, NULL, NULL, NULL }
+};
+
+/**
+ * wpas_dbus_register_p2p_groupmember - Register a p2p groupmember
+ * object with dbus
+ * @wpa_s: wpa_supplicant interface structure
+ * @p2p_if_addr: i/f addr of the device joining this group
+ *
+ * Registers p2p groupmember representing object with dbus
+ */
+void wpas_dbus_register_p2p_groupmember(struct wpa_supplicant *wpa_s,
+ const u8 *p2p_if_addr)
+{
+ struct wpas_dbus_priv *ctrl_iface;
+ struct wpa_dbus_object_desc *obj_desc = NULL;
+ struct groupmember_handler_args *arg;
+ char groupmember_obj_path[WPAS_DBUS_OBJECT_PATH_MAX];
+
+ /* Do nothing if the control interface is not turned on */
+ if (wpa_s == NULL || wpa_s->global == NULL)
+ return;
+
+ ctrl_iface = wpa_s->global->dbus;
+ if (ctrl_iface == NULL)
+ return;
+
+ if (!wpa_s->dbus_groupobj_path)
+ return;
+
+ os_snprintf(groupmember_obj_path, WPAS_DBUS_OBJECT_PATH_MAX,
+ "%s/" WPAS_DBUS_NEW_P2P_GROUPMEMBERS_PART "/" COMPACT_MACSTR,
+ wpa_s->dbus_groupobj_path, MAC2STR(p2p_if_addr));
+
+ obj_desc = os_zalloc(sizeof(struct wpa_dbus_object_desc));
+ if (!obj_desc) {
+ wpa_printf(MSG_ERROR, "Not enough memory "
+ "to create object description");
+ goto err;
+ }
+
+ /* allocate memory for handlers arguments */
+ arg = os_zalloc(sizeof(struct groupmember_handler_args));
+ if (!arg) {
+ wpa_printf(MSG_ERROR, "Not enough memory "
+ "to create arguments for method");
+ goto err;
+ }
+
+ arg->wpa_s = wpa_s;
+ os_memcpy(arg->member_addr, p2p_if_addr, ETH_ALEN);
+
+ wpas_dbus_register(obj_desc, arg, wpa_dbus_free, NULL,
+ wpas_dbus_p2p_groupmember_properties, NULL);
+
+ if (wpa_dbus_register_object_per_iface(ctrl_iface, groupmember_obj_path,
+ wpa_s->ifname, obj_desc))
+ goto err;
+
+ wpa_printf(MSG_INFO,
+ "dbus: Registered group member object '%s' successfully",
+ groupmember_obj_path);
+ return;
+
+err:
+ free_dbus_object_desc(obj_desc);
+}
+
+/**
+ * wpas_dbus_unregister_p2p_groupmember - Unregister a p2p groupmember
+ * object with dbus
+ * @wpa_s: wpa_supplicant interface structure
+ * @p2p_if_addr: i/f addr of the device joining this group
+ *
+ * Unregisters p2p groupmember representing object with dbus
+ */
+void wpas_dbus_unregister_p2p_groupmember(struct wpa_supplicant *wpa_s,
+ const u8 *p2p_if_addr)
+{
+ struct wpas_dbus_priv *ctrl_iface;
+ char groupmember_obj_path[WPAS_DBUS_OBJECT_PATH_MAX];
+
+ /* Do nothing if the control interface is not turned on */
+ if (wpa_s == NULL || wpa_s->global == NULL)
+ return;
+
+ ctrl_iface = wpa_s->global->dbus;
+ if (ctrl_iface == NULL)
+ return;
+
+ if (!wpa_s->dbus_groupobj_path)
+ return;
+
+ os_snprintf(groupmember_obj_path, WPAS_DBUS_OBJECT_PATH_MAX,
+ "%s/" WPAS_DBUS_NEW_P2P_GROUPMEMBERS_PART "/" COMPACT_MACSTR,
+ wpa_s->dbus_groupobj_path, MAC2STR(p2p_if_addr));
+
+ wpa_dbus_unregister_object_per_iface(ctrl_iface, groupmember_obj_path);
+}
+
+
+static const struct wpa_dbus_property_desc
+ wpas_dbus_persistent_group_properties[] = {
+ { "Properties", WPAS_DBUS_NEW_IFACE_PERSISTENT_GROUP, "a{sv}",
+ wpas_dbus_getter_persistent_group_properties,
+ wpas_dbus_setter_persistent_group_properties
+ },
+ { NULL, NULL, NULL, NULL, NULL }
+};
+
+/* No signals intended for persistent group objects */
+
+/**
+ * wpas_dbus_register_persistent_group - Register a configured(saved)
+ * persistent group with dbus
+ * @wpa_s: wpa_supplicant interface structure
+ * @ssid: persistent group (still represented as a network within wpa)
+ * configuration data
+ * Returns: 0 on success, -1 on failure
+ *
+ * Registers a persistent group representing object with dbus.
+ */
+int wpas_dbus_register_persistent_group(struct wpa_supplicant *wpa_s,
+ struct wpa_ssid *ssid)
+{
+ struct wpas_dbus_priv *ctrl_iface;
+ struct wpa_dbus_object_desc *obj_desc;
+ struct network_handler_args *arg;
+ char pgrp_obj_path[WPAS_DBUS_OBJECT_PATH_MAX];
+
+ /* Do nothing if the control interface is not turned on */
+ if (wpa_s == NULL || wpa_s->global == NULL)
+ return 0;
+
+ /* Make sure ssid is a persistent group */
+ if (ssid->disabled != 2 && !ssid->p2p_persistent_group)
+ return -1; /* should we return w/o complaining? */
+
+ ctrl_iface = wpa_s->global->dbus;
+ if (ctrl_iface == NULL)
+ return 0;
+
+ /*
+ * Intentionally not coming up with different numbering scheme
+ * for persistent groups.
+ */
+ os_snprintf(pgrp_obj_path, WPAS_DBUS_OBJECT_PATH_MAX,
+ "%s/" WPAS_DBUS_NEW_PERSISTENT_GROUPS_PART "/%u",
+ wpa_s->dbus_new_path, ssid->id);
+
+ wpa_printf(MSG_DEBUG, "dbus: Register persistent group object '%s'",
+ pgrp_obj_path);
+ obj_desc = os_zalloc(sizeof(struct wpa_dbus_object_desc));
+ if (!obj_desc) {
+ wpa_printf(MSG_ERROR, "dbus: Not enough memory to create "
+ "object description");
+ goto err;
+ }
+
+ /*
+ * Reusing the same context structure as that for networks
+ * since these are represented using same data structure.
+ */
+ /* allocate memory for handlers arguments */
+ arg = os_zalloc(sizeof(struct network_handler_args));
+ if (!arg) {
+ wpa_printf(MSG_ERROR, "dbus: Not enough memory to create "
+ "arguments for method");
+ goto err;
+ }
+
+ arg->wpa_s = wpa_s;
+ arg->ssid = ssid;
+
+ wpas_dbus_register(obj_desc, arg, wpa_dbus_free, NULL,
+ wpas_dbus_persistent_group_properties,
+ NULL);
+
+ if (wpa_dbus_register_object_per_iface(ctrl_iface, pgrp_obj_path,
+ wpa_s->ifname, obj_desc))
+ goto err;
+
+ wpas_dbus_signal_persistent_group_added(wpa_s, ssid->id);
+
+ return 0;
+
+err:
+ free_dbus_object_desc(obj_desc);
+ return -1;
+}
+
+
+/**
+ * wpas_dbus_unregister_persistent_group - Unregister a persistent_group
+ * from dbus
+ * @wpa_s: wpa_supplicant interface structure
+ * @nid: network id
+ * Returns: 0 on success, -1 on failure
+ *
+ * Unregisters persistent group representing object from dbus
+ *
+ * NOTE: There is a slight issue with the semantics here. While the
+ * implementation simply means the persistent group is unloaded from memory,
+ * it should not get interpreted as the group is actually being erased/removed
+ * from persistent storage as well.
+ */
+int wpas_dbus_unregister_persistent_group(struct wpa_supplicant *wpa_s,
+ int nid)
+{
+ struct wpas_dbus_priv *ctrl_iface;
+ char pgrp_obj_path[WPAS_DBUS_OBJECT_PATH_MAX];
+ int ret;
+
+ /* Do nothing if the control interface is not turned on */
+ if (wpa_s == NULL || wpa_s->global == NULL ||
+ wpa_s->dbus_new_path == NULL)
+ return 0;
+ ctrl_iface = wpa_s->global->dbus;
+ if (ctrl_iface == NULL)
+ return 0;
+
+ os_snprintf(pgrp_obj_path, WPAS_DBUS_OBJECT_PATH_MAX,
+ "%s/" WPAS_DBUS_NEW_PERSISTENT_GROUPS_PART "/%u",
+ wpa_s->dbus_new_path, nid);
+
+ wpa_printf(MSG_DEBUG, "dbus: Unregister persistent group object '%s'",
+ pgrp_obj_path);
+ ret = wpa_dbus_unregister_object_per_iface(ctrl_iface, pgrp_obj_path);
+
+ if (!ret)
+ wpas_dbus_signal_persistent_group_removed(wpa_s, nid);
+
+ return ret;
+}
+
+#endif /* CONFIG_P2P */
diff --git a/contrib/wpa/wpa_supplicant/dbus/dbus_new.h b/contrib/wpa/wpa_supplicant/dbus/dbus_new.h
index 80ea98c..363a7e5 100644
--- a/contrib/wpa/wpa_supplicant/dbus/dbus_new.h
+++ b/contrib/wpa/wpa_supplicant/dbus/dbus_new.h
@@ -3,26 +3,22 @@
* Copyright (c) 2006, Dan Williams <dcbw@redhat.com> and Red Hat, Inc.
* Copyright (c) 2009-2010, Witold Sowa <witold.sowa@gmail.com>
*
- * This program is free software; you can redistribute it and/or modify
- * it under the terms of the GNU General Public License version 2 as
- * published by the Free Software Foundation.
- *
- * Alternatively, this software may be distributed under the terms of BSD
- * license.
- *
- * See README and COPYING for more details.
+ * This software may be distributed under the terms of the BSD license.
+ * See README for more details.
*/
#ifndef CTRL_IFACE_DBUS_NEW_H
#define CTRL_IFACE_DBUS_NEW_H
+#include "common/defs.h"
+#include "p2p/p2p.h"
+
struct wpa_global;
struct wpa_supplicant;
struct wpa_ssid;
struct wps_event_m2d;
struct wps_event_fail;
struct wps_credential;
-enum wpa_states;
enum wpas_dbus_prop {
WPAS_DBUS_PROP_AP_SCAN,
@@ -30,6 +26,9 @@ enum wpas_dbus_prop {
WPAS_DBUS_PROP_STATE,
WPAS_DBUS_PROP_CURRENT_BSS,
WPAS_DBUS_PROP_CURRENT_NETWORK,
+ WPAS_DBUS_PROP_CURRENT_AUTH_MODE,
+ WPAS_DBUS_PROP_BSSS,
+ WPAS_DBUS_PROP_DISCONNECT_REASON,
};
enum wpas_dbus_bss_prop {
@@ -40,6 +39,7 @@ enum wpas_dbus_bss_prop {
WPAS_DBUS_BSS_PROP_RATES,
WPAS_DBUS_BSS_PROP_WPA,
WPAS_DBUS_BSS_PROP_RSN,
+ WPAS_DBUS_BSS_PROP_WPS,
WPAS_DBUS_BSS_PROP_IES,
};
@@ -59,6 +59,29 @@ enum wpas_dbus_bss_prop {
#define WPAS_DBUS_NEW_BSSIDS_PART "BSSs"
#define WPAS_DBUS_NEW_IFACE_BSS WPAS_DBUS_NEW_INTERFACE ".BSS"
+#define WPAS_DBUS_NEW_IFACE_P2PDEVICE \
+ WPAS_DBUS_NEW_IFACE_INTERFACE ".P2PDevice"
+
+/*
+ * Groups correspond to P2P groups where this device is a GO (owner)
+ */
+#define WPAS_DBUS_NEW_P2P_GROUPS_PART "Groups"
+#define WPAS_DBUS_NEW_IFACE_P2P_GROUP WPAS_DBUS_NEW_INTERFACE ".Group"
+
+/*
+ * Different dbus object for persistent groups so they do not get confused
+ * with regular (configured) network objects.
+ */
+#define WPAS_DBUS_NEW_PERSISTENT_GROUPS_PART "PersistentGroups"
+#define WPAS_DBUS_NEW_IFACE_PERSISTENT_GROUP \
+ WPAS_DBUS_NEW_INTERFACE ".PersistentGroup"
+
+#define WPAS_DBUS_NEW_P2P_PEERS_PART "Peers"
+#define WPAS_DBUS_NEW_IFACE_P2P_PEER WPAS_DBUS_NEW_INTERFACE ".Peer"
+
+#define WPAS_DBUS_NEW_P2P_GROUPMEMBERS_PART "Members"
+#define WPAS_DBUS_NEW_IFACE_P2P_GROUPMEMBER \
+ WPAS_DBUS_NEW_INTERFACE ".GroupMember"
/* Errors */
#define WPAS_DBUS_ERROR_UNKNOWN_ERROR \
@@ -76,11 +99,29 @@ enum wpas_dbus_bss_prop {
#define WPAS_DBUS_ERROR_NETWORK_UNKNOWN \
WPAS_DBUS_NEW_INTERFACE ".NetworkUnknown"
+#define WPAS_DBUS_ERROR_CONNECT_CHANNEL_UNAVAILABLE \
+ WPAS_DBUS_NEW_INTERFACE ".ConnectChannelUnavailable"
+#define WPAS_DBUS_ERROR_CONNECT_CHANNEL_UNSUPPORTED \
+ WPAS_DBUS_NEW_INTERFACE ".ConnectChannelUnsupported"
+#define WPAS_DBUS_ERROR_CONNECT_UNSPECIFIED_ERROR \
+ WPAS_DBUS_NEW_INTERFACE ".ConnectUnspecifiedError"
+
#define WPAS_DBUS_ERROR_BLOB_EXISTS \
WPAS_DBUS_NEW_INTERFACE ".BlobExists"
#define WPAS_DBUS_ERROR_BLOB_UNKNOWN \
WPAS_DBUS_NEW_INTERFACE ".BlobUnknown"
+#define WPAS_DBUS_ERROR_SUBSCRIPTION_IN_USE \
+ WPAS_DBUS_NEW_INTERFACE ".SubscriptionInUse"
+#define WPAS_DBUS_ERROR_NO_SUBSCRIPTION \
+ WPAS_DBUS_NEW_INTERFACE ".NoSubscription"
+#define WPAS_DBUS_ERROR_SUBSCRIPTION_EPERM \
+ WPAS_DBUS_NEW_INTERFACE ".SubscriptionNotYou"
+
+
+void wpas_dbus_subscribe_noc(struct wpas_dbus_priv *priv);
+void wpas_dbus_unsubscribe_noc(struct wpas_dbus_priv *priv);
+
#ifdef CONFIG_CTRL_IFACE_DBUS_NEW
@@ -97,6 +138,10 @@ void wpas_dbus_bss_signal_prop_changed(struct wpa_supplicant *wpa_s,
void wpas_dbus_signal_network_enabled_changed(struct wpa_supplicant *wpa_s,
struct wpa_ssid *ssid);
void wpas_dbus_signal_network_selected(struct wpa_supplicant *wpa_s, int id);
+void wpas_dbus_signal_network_request(struct wpa_supplicant *wpa_s,
+ struct wpa_ssid *ssid,
+ enum wpa_ctrl_req_type rtype,
+ const char *default_text);
void wpas_dbus_signal_scan_done(struct wpa_supplicant *wpa_s, int success);
void wpas_dbus_signal_wps_cred(struct wpa_supplicant *wpa_s,
const struct wps_credential *cred);
@@ -120,6 +165,64 @@ void wpas_dbus_signal_debug_level_changed(struct wpa_global *global);
void wpas_dbus_signal_debug_timestamp_changed(struct wpa_global *global);
void wpas_dbus_signal_debug_show_keys_changed(struct wpa_global *global);
+int wpas_dbus_register_peer(struct wpa_supplicant *wpa_s, const u8 *dev_addr);
+void wpas_dbus_signal_peer_device_found(struct wpa_supplicant *wpa_s,
+ const u8 *dev_addr);
+int wpas_dbus_unregister_peer(struct wpa_supplicant *wpa_s,
+ const u8 *dev_addr);
+void wpas_dbus_signal_peer_device_lost(struct wpa_supplicant *wpa_s,
+ const u8 *dev_addr);
+void wpas_dbus_signal_p2p_group_removed(struct wpa_supplicant *wpa_s,
+ const char *role);
+void wpas_dbus_signal_p2p_provision_discovery(struct wpa_supplicant *wpa_s,
+ const u8 *dev_addr, int request,
+ enum p2p_prov_disc_status status,
+ u16 config_methods,
+ unsigned int generated_pin);
+void wpas_dbus_signal_p2p_go_neg_req(struct wpa_supplicant *wpa_s,
+ const u8 *src, u16 dev_passwd_id);
+void wpas_dbus_signal_p2p_group_started(struct wpa_supplicant *wpa_s,
+ const struct wpa_ssid *ssid,
+ int client, int network_id);
+void wpas_dbus_register_p2p_group(struct wpa_supplicant *wpa_s,
+ struct wpa_ssid *ssid);
+void wpas_dbus_signal_p2p_go_neg_resp(struct wpa_supplicant *wpa_s,
+ struct p2p_go_neg_results *res);
+void wpas_dbus_unregister_p2p_group(struct wpa_supplicant *wpa_s,
+ const struct wpa_ssid *ssid);
+int wpas_dbus_register_persistent_group(struct wpa_supplicant *wpa_s,
+ struct wpa_ssid *ssid);
+int wpas_dbus_unregister_persistent_group(struct wpa_supplicant *wpa_s,
+ int nid);
+void wpas_dbus_signal_p2p_invitation_result(struct wpa_supplicant *wpa_s,
+ int status, const u8 *bssid);
+void wpas_dbus_register_p2p_groupmember(struct wpa_supplicant *wpa_s,
+ const u8 *p2p_if_addr);
+void wpas_dbus_unregister_p2p_groupmember(struct wpa_supplicant *wpa_s,
+ const u8 *p2p_if_addr);
+void wpas_dbus_signal_p2p_peer_disconnected(struct wpa_supplicant *wpa_s,
+ const u8 *member);
+void wpas_dbus_signal_p2p_sd_request(struct wpa_supplicant *wpa_s,
+ int freq, const u8 *sa, u8 dialog_token,
+ u16 update_indic, const u8 *tlvs,
+ size_t tlvs_len);
+void wpas_dbus_signal_p2p_sd_response(struct wpa_supplicant *wpa_s,
+ const u8 *sa, u16 update_indic,
+ const u8 *tlvs, size_t tlvs_len);
+void wpas_dbus_signal_p2p_peer_joined(struct wpa_supplicant *wpa_s,
+ const u8 *member);
+void wpas_dbus_signal_p2p_wps_failed(struct wpa_supplicant *wpa_s,
+ struct wps_event_fail *fail);
+void wpas_dbus_signal_certification(struct wpa_supplicant *wpa_s,
+ int depth, const char *subject,
+ const char *cert_hash,
+ const struct wpabuf *cert);
+void wpas_dbus_signal_preq(struct wpa_supplicant *wpa_s,
+ const u8 *addr, const u8 *dst, const u8 *bssid,
+ const u8 *ie, size_t ie_len, u32 ssi_signal);
+void wpas_dbus_signal_eap_status(struct wpa_supplicant *wpa_s,
+ const char *status, const char *parameter);
+
#else /* CONFIG_CTRL_IFACE_DBUS_NEW */
static inline int wpas_dbus_register_interface(struct wpa_supplicant *wpa_s)
@@ -155,6 +258,12 @@ static inline void wpas_dbus_signal_network_selected(
{
}
+static inline void wpas_dbus_signal_network_request(
+ struct wpa_supplicant *wpa_s, struct wpa_ssid *ssid,
+ enum wpa_ctrl_req_type rtype, const char *default_txt)
+{
+}
+
static inline void wpas_dbus_signal_scan_done(struct wpa_supplicant *wpa_s,
int success)
{
@@ -229,6 +338,161 @@ static inline void wpas_dbus_signal_debug_show_keys_changed(
{
}
+static inline int wpas_dbus_register_peer(struct wpa_supplicant *wpa_s,
+ const u8 *dev_addr)
+{
+ return 0;
+}
+
+static inline int wpas_dbus_unregister_peer(struct wpa_supplicant *wpa_s,
+ const u8 *dev_addr)
+{
+ return 0;
+}
+
+static inline void
+wpas_dbus_signal_p2p_group_removed(struct wpa_supplicant *wpa_s,
+ const char *role)
+{
+}
+
+static inline void
+wpas_dbus_signal_p2p_provision_discovery(struct wpa_supplicant *wpa_s,
+ const u8 *dev_addr, int request,
+ enum p2p_prov_disc_status status,
+ u16 config_methods,
+ unsigned int generated_pin)
+{
+}
+
+static inline void wpas_dbus_signal_p2p_go_neg_req(
+ struct wpa_supplicant *wpa_s,
+ const u8 *src,
+ u16 dev_passwd_id)
+{
+}
+
+static inline void
+wpas_dbus_signal_p2p_group_started(struct wpa_supplicant *wpa_s,
+ const struct wpa_ssid *ssid,
+ int client, int network_id)
+{
+}
+
+static inline void
+wpas_dbus_register_p2p_group(struct wpa_supplicant *wpa_s,
+ struct wpa_ssid *ssid)
+{
+}
+
+static inline int wpas_dbus_register_persistent_group(
+ struct wpa_supplicant *wpa_s, struct wpa_ssid *ssid)
+{
+ return 0;
+}
+
+static inline int wpas_dbus_unregister_persistent_group(
+ struct wpa_supplicant *wpa_s, int nid)
+{
+ return 0;
+}
+
+static inline void
+wpas_dbus_signal_p2p_go_neg_resp(struct wpa_supplicant *wpa_s,
+ struct p2p_go_neg_results *res)
+{
+}
+
+static inline void
+wpas_dbus_unregister_p2p_group(struct wpa_supplicant *wpa_s,
+ const struct wpa_ssid *ssid)
+{
+}
+
+static inline void wpas_dbus_signal_p2p_invitation_result(
+ struct wpa_supplicant *wpa_s, int status,
+ const u8 *bssid)
+{
+}
+
+static inline void
+wpas_dbus_register_p2p_groupmember(struct wpa_supplicant *wpa_s,
+ const u8 *p2p_if_addr)
+{
+}
+
+static inline void
+wpas_dbus_signal_p2p_sd_request(struct wpa_supplicant *wpa_s, int freq,
+ const u8 *sa, u8 dialog_token, u16 update_indic,
+ const u8 *tlvs, size_t tlvs_len)
+{
+}
+
+static inline void
+wpas_dbus_signal_p2p_sd_response(struct wpa_supplicant *wpa_s,
+ const u8 *sa, u16 update_indic,
+ const u8 *tlvs, size_t tlvs_len)
+{
+}
+
+static inline void
+wpas_dbus_unregister_p2p_groupmember(struct wpa_supplicant *wpa_s,
+ const u8 *p2p_if_addr)
+{
+}
+
+static inline void
+wpas_dbus_signal_p2p_peer_joined(struct wpa_supplicant *wpa_s,
+ const u8 *member)
+{
+}
+
+static inline void
+wpas_dbus_signal_peer_device_found(struct wpa_supplicant *wpa_s,
+ const u8 *dev_addr)
+{
+}
+
+static inline void
+wpas_dbus_signal_peer_device_lost(struct wpa_supplicant *wpa_s,
+ const u8 *dev_addr)
+{
+}
+
+static inline void
+wpas_dbus_signal_p2p_peer_disconnected(struct wpa_supplicant *wpa_s,
+ const u8 *member)
+{
+}
+
+static inline void
+wpas_dbus_signal_p2p_wps_failed(struct wpa_supplicant *wpa_s,
+ struct wps_event_fail *fail)
+{
+}
+
+static inline void wpas_dbus_signal_certification(struct wpa_supplicant *wpa_s,
+ int depth,
+ const char *subject,
+ const char *cert_hash,
+ const struct wpabuf *cert)
+{
+}
+
+static inline void wpas_dbus_signal_preq(struct wpa_supplicant *wpa_s,
+ const u8 *addr, const u8 *dst,
+ const u8 *bssid,
+ const u8 *ie, size_t ie_len,
+ u32 ssi_signal)
+{
+}
+
+static inline void wpas_dbus_signal_eap_status(struct wpa_supplicant *wpa_s,
+ const char *status,
+ const char *parameter)
+{
+}
+
#endif /* CONFIG_CTRL_IFACE_DBUS_NEW */
#endif /* CTRL_IFACE_DBUS_H_NEW */
diff --git a/contrib/wpa/wpa_supplicant/dbus/dbus_new_handlers.c b/contrib/wpa/wpa_supplicant/dbus/dbus_new_handlers.c
index e2b5e50..5e06932 100644
--- a/contrib/wpa/wpa_supplicant/dbus/dbus_new_handlers.c
+++ b/contrib/wpa/wpa_supplicant/dbus/dbus_new_handlers.c
@@ -4,14 +4,8 @@
* Copyright (c) 2009-2010, Witold Sowa <witold.sowa@gmail.com>
* Copyright (c) 2009, Jouni Malinen <j@w1.fi>
*
- * This program is free software; you can redistribute it and/or modify
- * it under the terms of the GNU General Public License version 2 as
- * published by the Free Software Foundation.
- *
- * Alternatively, this software may be distributed under the terms of BSD
- * license.
- *
- * See README and COPYING for more details.
+ * This software may be distributed under the terms of the BSD license.
+ * See README for more details.
*/
#include "includes.h"
@@ -25,88 +19,25 @@
#include "../wpa_supplicant_i.h"
#include "../driver_i.h"
#include "../notify.h"
-#include "../wpas_glue.h"
#include "../bss.h"
#include "../scan.h"
+#include "../autoscan.h"
#include "dbus_new_helpers.h"
#include "dbus_new.h"
#include "dbus_new_handlers.h"
#include "dbus_dict_helpers.h"
+#include "dbus_common_i.h"
extern int wpa_debug_level;
extern int wpa_debug_show_keys;
extern int wpa_debug_timestamp;
static const char *debug_strings[] = {
- "msgdump", "debug", "info", "warning", "error", NULL
+ "excessive", "msgdump", "debug", "info", "warning", "error", NULL
};
/**
- * wpas_dbus_new_decompose_object_path - Decompose an interface object path into parts
- * @path: The dbus object path
- * @network: (out) the configured network this object path refers to, if any
- * @bssid: (out) the scanned bssid this object path refers to, if any
- * Returns: The object path of the network interface this path refers to
- *
- * For a given object path, decomposes the object path into object id, network,
- * and BSSID parts, if those parts exist.
- */
-static char * wpas_dbus_new_decompose_object_path(const char *path,
- char **network,
- char **bssid)
-{
- const unsigned int dev_path_prefix_len =
- strlen(WPAS_DBUS_NEW_PATH_INTERFACES "/");
- char *obj_path_only;
- char *next_sep;
-
- /* Be a bit paranoid about path */
- if (!path || os_strncmp(path, WPAS_DBUS_NEW_PATH_INTERFACES "/",
- dev_path_prefix_len))
- return NULL;
-
- /* Ensure there's something at the end of the path */
- if ((path + dev_path_prefix_len)[0] == '\0')
- return NULL;
-
- obj_path_only = os_strdup(path);
- if (obj_path_only == NULL)
- return NULL;
-
- next_sep = os_strchr(obj_path_only + dev_path_prefix_len, '/');
- if (next_sep != NULL) {
- const char *net_part = os_strstr(
- next_sep, WPAS_DBUS_NEW_NETWORKS_PART "/");
- const char *bssid_part = os_strstr(
- next_sep, WPAS_DBUS_NEW_BSSIDS_PART "/");
-
- if (network && net_part) {
- /* Deal with a request for a configured network */
- const char *net_name = net_part +
- os_strlen(WPAS_DBUS_NEW_NETWORKS_PART "/");
- *network = NULL;
- if (os_strlen(net_name))
- *network = os_strdup(net_name);
- } else if (bssid && bssid_part) {
- /* Deal with a request for a scanned BSSID */
- const char *bssid_name = bssid_part +
- os_strlen(WPAS_DBUS_NEW_BSSIDS_PART "/");
- if (strlen(bssid_name))
- *bssid = os_strdup(bssid_name);
- else
- *bssid = NULL;
- }
-
- /* Cut off interface object path before "/" */
- *next_sep = '\0';
- }
-
- return obj_path_only;
-}
-
-
-/**
* wpas_dbus_error_unknown_error - Return a new InvalidArgs error message
* @message: Pointer to incoming dbus message this error refers to
* @arg: Optional string appended to error message
@@ -117,6 +48,20 @@ static char * wpas_dbus_new_decompose_object_path(const char *path,
DBusMessage * wpas_dbus_error_unknown_error(DBusMessage *message,
const char *arg)
{
+ /*
+ * This function can be called as a result of a failure
+ * within internal getter calls, which will call this function
+ * with a NULL message parameter. However, dbus_message_new_error
+ * looks very unkindly (i.e, abort()) on a NULL message, so
+ * in this case, we should not call it.
+ */
+ if (message == NULL) {
+ wpa_printf(MSG_INFO, "dbus: wpas_dbus_error_unknown_error "
+ "called with NULL message (arg=%s)",
+ arg ? arg : "N/A");
+ return NULL;
+ }
+
return dbus_message_new_error(message, WPAS_DBUS_ERROR_UNKNOWN_ERROR,
arg);
}
@@ -178,7 +123,7 @@ DBusMessage * wpas_dbus_error_invalid_args(DBusMessage *message,
static const char *dont_quote[] = {
"key_mgmt", "proto", "pairwise", "auth_alg", "group", "eap",
"opensc_engine_path", "pkcs11_engine_path", "pkcs11_module_path",
- "bssid", NULL
+ "bssid", "scan_freq", "freq_list", NULL
};
static dbus_bool_t should_quote_opt(const char *key)
@@ -213,36 +158,35 @@ static struct wpa_supplicant * get_iface_by_dbus_path(
/**
* set_network_properties - Set properties of a configured network
- * @message: Pointer to incoming dbus message
* @wpa_s: wpa_supplicant structure for a network interface
* @ssid: wpa_ssid structure for a configured network
* @iter: DBus message iterator containing dictionary of network
* properties to set.
- * Returns: NULL when succeed or DBus error on failure
+ * @error: On failure, an error describing the failure
+ * Returns: TRUE if the request succeeds, FALSE if it failed
*
* Sets network configuration with parameters given id DBus dictionary
*/
-static DBusMessage * set_network_properties(DBusMessage *message,
- struct wpa_supplicant *wpa_s,
- struct wpa_ssid *ssid,
- DBusMessageIter *iter)
+dbus_bool_t set_network_properties(struct wpa_supplicant *wpa_s,
+ struct wpa_ssid *ssid,
+ DBusMessageIter *iter,
+ DBusError *error)
{
-
struct wpa_dbus_dict_entry entry = { .type = DBUS_TYPE_STRING };
- DBusMessage *reply = NULL;
DBusMessageIter iter_dict;
+ char *value = NULL;
- if (!wpa_dbus_dict_open_read(iter, &iter_dict))
- return wpas_dbus_error_invalid_args(message, NULL);
+ if (!wpa_dbus_dict_open_read(iter, &iter_dict, error))
+ return FALSE;
while (wpa_dbus_dict_has_dict_entry(&iter_dict)) {
- char *value = NULL;
size_t size = 50;
int ret;
- if (!wpa_dbus_dict_get_entry(&iter_dict, &entry)) {
- reply = wpas_dbus_error_invalid_args(message, NULL);
- break;
- }
+
+ if (!wpa_dbus_dict_get_entry(&iter_dict, &entry))
+ goto error;
+
+ value = NULL;
if (entry.type == DBUS_TYPE_ARRAY &&
entry.array_type == DBUS_TYPE_BYTE) {
if (entry.array_len <= 0)
@@ -304,78 +248,66 @@ static DBusMessage * set_network_properties(DBusMessage *message,
if ((os_strcmp(entry.key, "psk") == 0 &&
value[0] == '"' && ssid->ssid_len) ||
- (strcmp(entry.key, "ssid") == 0 && ssid->passphrase))
+ (os_strcmp(entry.key, "ssid") == 0 && ssid->passphrase))
wpa_config_update_psk(ssid);
else if (os_strcmp(entry.key, "priority") == 0)
wpa_config_update_prio_list(wpa_s->conf);
os_free(value);
wpa_dbus_dict_entry_clear(&entry);
- continue;
-
- error:
- os_free(value);
- reply = wpas_dbus_error_invalid_args(message, entry.key);
- wpa_dbus_dict_entry_clear(&entry);
- break;
}
- return reply;
+ return TRUE;
+
+error:
+ os_free(value);
+ wpa_dbus_dict_entry_clear(&entry);
+ dbus_set_error_const(error, DBUS_ERROR_INVALID_ARGS,
+ "invalid message format");
+ return FALSE;
}
/**
* wpas_dbus_simple_property_getter - Get basic type property
- * @message: Pointer to incoming dbus message
+ * @iter: Message iter to use when appending arguments
* @type: DBus type of property (must be basic type)
* @val: pointer to place holding property value
- * Returns: The DBus message containing response for Properties.Get call
- * or DBus error message if error occurred.
+ * @error: On failure an error describing the failure
+ * Returns: TRUE if the request was successful, FALSE if it failed
*
* Generic getter for basic type properties. Type is required to be basic.
*/
-DBusMessage * wpas_dbus_simple_property_getter(DBusMessage *message,
- const int type, const void *val)
+dbus_bool_t wpas_dbus_simple_property_getter(DBusMessageIter *iter,
+ const int type,
+ const void *val,
+ DBusError *error)
{
- DBusMessage *reply = NULL;
- DBusMessageIter iter, variant_iter;
+ DBusMessageIter variant_iter;
if (!dbus_type_is_basic(type)) {
- wpa_printf(MSG_ERROR, "dbus: wpas_dbus_simple_property_getter:"
- " given type is not basic");
- return wpas_dbus_error_unknown_error(message, NULL);
+ dbus_set_error(error, DBUS_ERROR_FAILED,
+ "%s: given type is not basic", __func__);
+ return FALSE;
}
- if (message == NULL)
- reply = dbus_message_new(DBUS_MESSAGE_TYPE_SIGNAL);
- else
- reply = dbus_message_new_method_return(message);
-
- if (reply != NULL) {
- dbus_message_iter_init_append(reply, &iter);
- if (!dbus_message_iter_open_container(
- &iter, DBUS_TYPE_VARIANT,
- wpa_dbus_type_as_string(type), &variant_iter) ||
- !dbus_message_iter_append_basic(&variant_iter, type,
- val) ||
- !dbus_message_iter_close_container(&iter, &variant_iter)) {
- wpa_printf(MSG_ERROR, "dbus: "
- "wpas_dbus_simple_property_getter: out of "
- "memory to put property value into "
- "message");
- dbus_message_unref(reply);
- reply = dbus_message_new_error(message,
- DBUS_ERROR_NO_MEMORY,
- NULL);
- }
- } else {
- wpa_printf(MSG_ERROR, "dbus: wpas_dbus_simple_property_getter:"
- " out of memory to return property value");
- reply = dbus_message_new_error(message, DBUS_ERROR_NO_MEMORY,
- NULL);
- }
+ if (!dbus_message_iter_open_container(iter, DBUS_TYPE_VARIANT,
+ wpa_dbus_type_as_string(type),
+ &variant_iter))
+ goto error;
- return reply;
+ if (!dbus_message_iter_append_basic(&variant_iter, type, val))
+ goto error;
+
+ if (!dbus_message_iter_close_container(iter, &variant_iter))
+ goto error;
+
+ return TRUE;
+
+error:
+ dbus_set_error(error, DBUS_ERROR_FAILED,
+ "%s: error constructing reply", __func__);
+ return FALSE;
}
@@ -384,102 +316,79 @@ DBusMessage * wpas_dbus_simple_property_getter(DBusMessage *message,
* @message: Pointer to incoming dbus message
* @type: DBus type of property (must be basic type)
* @val: pointer to place where value being set will be stored
- * Returns: NULL or DBus error message if error occurred.
+ * Returns: TRUE if the request was successful, FALSE if it failed
*
* Generic setter for basic type properties. Type is required to be basic.
*/
-DBusMessage * wpas_dbus_simple_property_setter(DBusMessage *message,
- const int type, void *val)
+dbus_bool_t wpas_dbus_simple_property_setter(DBusMessageIter *iter,
+ DBusError *error,
+ const int type, void *val)
{
- DBusMessageIter iter, variant_iter;
+ DBusMessageIter variant_iter;
if (!dbus_type_is_basic(type)) {
- wpa_printf(MSG_ERROR, "dbus: wpas_dbus_simple_property_setter:"
- " given type is not basic");
- return wpas_dbus_error_unknown_error(message, NULL);
- }
-
- if (!dbus_message_iter_init(message, &iter)) {
- wpa_printf(MSG_ERROR, "dbus: wpas_dbus_simple_property_setter:"
- " out of memory to return scanning state");
- return dbus_message_new_error(message, DBUS_ERROR_NO_MEMORY,
- NULL);
+ dbus_set_error(error, DBUS_ERROR_FAILED,
+ "%s: given type is not basic", __func__);
+ return FALSE;
}
- /* omit first and second argument and get value from third */
- dbus_message_iter_next(&iter);
- dbus_message_iter_next(&iter);
- dbus_message_iter_recurse(&iter, &variant_iter);
-
+ /* Look at the new value */
+ dbus_message_iter_recurse(iter, &variant_iter);
if (dbus_message_iter_get_arg_type(&variant_iter) != type) {
- wpa_printf(MSG_DEBUG, "dbus: wpas_dbus_simple_property_setter:"
- " wrong property type");
- return wpas_dbus_error_invalid_args(message,
- "wrong property type");
+ dbus_set_error_const(error, DBUS_ERROR_FAILED,
+ "wrong property type");
+ return FALSE;
}
dbus_message_iter_get_basic(&variant_iter, val);
- return NULL;
+ return TRUE;
}
/**
* wpas_dbus_simple_array_property_getter - Get array type property
- * @message: Pointer to incoming dbus message
+ * @iter: Pointer to incoming dbus message iterator
* @type: DBus type of property array elements (must be basic type)
* @array: pointer to array of elements to put into response message
* @array_len: length of above array
- * Returns: The DBus message containing response for Properties.Get call
- * or DBus error message if error occurred.
+ * @error: a pointer to an error to fill on failure
+ * Returns: TRUE if the request succeeded, FALSE if it failed
*
* Generic getter for array type properties. Array elements type is
* required to be basic.
*/
-DBusMessage * wpas_dbus_simple_array_property_getter(DBusMessage *message,
- const int type,
- const void *array,
- size_t array_len)
+dbus_bool_t wpas_dbus_simple_array_property_getter(DBusMessageIter *iter,
+ const int type,
+ const void *array,
+ size_t array_len,
+ DBusError *error)
{
- DBusMessage *reply = NULL;
- DBusMessageIter iter, variant_iter, array_iter;
+ DBusMessageIter variant_iter, array_iter;
char type_str[] = "a?"; /* ? will be replaced with subtype letter; */
const char *sub_type_str;
size_t element_size, i;
if (!dbus_type_is_basic(type)) {
- wpa_printf(MSG_ERROR, "dbus: "
- "wpas_dbus_simple_array_property_getter: given "
- "type is not basic");
- return wpas_dbus_error_unknown_error(message, NULL);
+ dbus_set_error(error, DBUS_ERROR_FAILED,
+ "%s: given type is not basic", __func__);
+ return FALSE;
}
sub_type_str = wpa_dbus_type_as_string(type);
type_str[1] = sub_type_str[0];
- if (message == NULL)
- reply = dbus_message_new(DBUS_MESSAGE_TYPE_SIGNAL);
- else
- reply = dbus_message_new_method_return(message);
- if (reply == NULL) {
- wpa_printf(MSG_ERROR, "dbus: "
- "wpas_dbus_simple_array_property_getter: out of "
- "memory to create return message");
- return dbus_message_new_error(message, DBUS_ERROR_NO_MEMORY,
- NULL);
+ if (!dbus_message_iter_open_container(iter, DBUS_TYPE_VARIANT,
+ type_str, &variant_iter)) {
+ dbus_set_error(error, DBUS_ERROR_FAILED,
+ "%s: failed to construct message 1", __func__);
+ return FALSE;
}
- dbus_message_iter_init_append(reply, &iter);
-
- if (!dbus_message_iter_open_container(&iter, DBUS_TYPE_VARIANT,
- type_str, &variant_iter) ||
- !dbus_message_iter_open_container(&variant_iter, DBUS_TYPE_ARRAY,
+ if (!dbus_message_iter_open_container(&variant_iter, DBUS_TYPE_ARRAY,
sub_type_str, &array_iter)) {
- wpa_printf(MSG_ERROR, "dbus: "
- "wpas_dbus_simple_array_property_getter: out of "
- "memory to open container");
- dbus_message_unref(reply);
- return dbus_message_new_error(message, DBUS_ERROR_NO_MEMORY,
- NULL);
+ dbus_set_error(error, DBUS_ERROR_FAILED,
+ "%s: failed to construct message 2", __func__);
+ return FALSE;
}
switch(type) {
@@ -507,11 +416,9 @@ DBusMessage * wpas_dbus_simple_array_property_getter(DBusMessage *message,
element_size = sizeof(char *);
break;
default:
- wpa_printf(MSG_ERROR, "dbus: "
- "wpas_dbus_simple_array_property_getter: "
- "fatal: unknown element type");
- element_size = 1;
- break;
+ dbus_set_error(error, DBUS_ERROR_FAILED,
+ "%s: unknown element type %d", __func__, type);
+ return FALSE;
}
for (i = 0; i < array_len; i++) {
@@ -519,17 +426,89 @@ DBusMessage * wpas_dbus_simple_array_property_getter(DBusMessage *message,
array + i * element_size);
}
- if (!dbus_message_iter_close_container(&variant_iter, &array_iter) ||
- !dbus_message_iter_close_container(&iter, &variant_iter)) {
- wpa_printf(MSG_ERROR, "dbus: "
- "wpas_dbus_simple_array_property_getter: out of "
- "memory to close container");
- dbus_message_unref(reply);
- return dbus_message_new_error(message, DBUS_ERROR_NO_MEMORY,
- NULL);
+ if (!dbus_message_iter_close_container(&variant_iter, &array_iter)) {
+ dbus_set_error(error, DBUS_ERROR_FAILED,
+ "%s: failed to construct message 3", __func__);
+ return FALSE;
}
- return reply;
+ if (!dbus_message_iter_close_container(iter, &variant_iter)) {
+ dbus_set_error(error, DBUS_ERROR_FAILED,
+ "%s: failed to construct message 4", __func__);
+ return FALSE;
+ }
+
+ return TRUE;
+}
+
+
+/**
+ * wpas_dbus_simple_array_array_property_getter - Get array array type property
+ * @iter: Pointer to incoming dbus message iterator
+ * @type: DBus type of property array elements (must be basic type)
+ * @array: pointer to array of elements to put into response message
+ * @array_len: length of above array
+ * @error: a pointer to an error to fill on failure
+ * Returns: TRUE if the request succeeded, FALSE if it failed
+ *
+ * Generic getter for array type properties. Array elements type is
+ * required to be basic.
+ */
+dbus_bool_t wpas_dbus_simple_array_array_property_getter(DBusMessageIter *iter,
+ const int type,
+ struct wpabuf **array,
+ size_t array_len,
+ DBusError *error)
+{
+ DBusMessageIter variant_iter, array_iter;
+ char type_str[] = "aa?";
+ char inner_type_str[] = "a?";
+ const char *sub_type_str;
+ size_t i;
+
+ if (!dbus_type_is_basic(type)) {
+ dbus_set_error(error, DBUS_ERROR_FAILED,
+ "%s: given type is not basic", __func__);
+ return FALSE;
+ }
+
+ sub_type_str = wpa_dbus_type_as_string(type);
+ type_str[2] = sub_type_str[0];
+ inner_type_str[1] = sub_type_str[0];
+
+ if (!dbus_message_iter_open_container(iter, DBUS_TYPE_VARIANT,
+ type_str, &variant_iter)) {
+ dbus_set_error(error, DBUS_ERROR_FAILED,
+ "%s: failed to construct message 1", __func__);
+ return FALSE;
+ }
+ if (!dbus_message_iter_open_container(&variant_iter, DBUS_TYPE_ARRAY,
+ inner_type_str, &array_iter)) {
+ dbus_set_error(error, DBUS_ERROR_FAILED,
+ "%s: failed to construct message 2", __func__);
+ return FALSE;
+ }
+
+ for (i = 0; i < array_len; i++) {
+ wpa_dbus_dict_bin_array_add_element(&array_iter,
+ wpabuf_head(array[i]),
+ wpabuf_len(array[i]));
+
+ }
+
+ if (!dbus_message_iter_close_container(&variant_iter, &array_iter)) {
+ dbus_set_error(error, DBUS_ERROR_FAILED,
+ "%s: failed to close message 2", __func__);
+ return FALSE;
+ }
+
+ if (!dbus_message_iter_close_container(iter, &variant_iter)) {
+ dbus_set_error(error, DBUS_ERROR_FAILED,
+ "%s: failed to close message 1", __func__);
+ return FALSE;
+ }
+
+ return TRUE;
}
@@ -553,28 +532,35 @@ DBusMessage * wpas_dbus_handler_create_interface(DBusMessage *message,
struct wpa_dbus_dict_entry entry;
char *driver = NULL;
char *ifname = NULL;
+ char *confname = NULL;
char *bridge_ifname = NULL;
dbus_message_iter_init(message, &iter);
- if (!wpa_dbus_dict_open_read(&iter, &iter_dict))
+ if (!wpa_dbus_dict_open_read(&iter, &iter_dict, NULL))
goto error;
while (wpa_dbus_dict_has_dict_entry(&iter_dict)) {
if (!wpa_dbus_dict_get_entry(&iter_dict, &entry))
goto error;
- if (!strcmp(entry.key, "Driver") &&
+ if (!os_strcmp(entry.key, "Driver") &&
(entry.type == DBUS_TYPE_STRING)) {
driver = os_strdup(entry.str_value);
wpa_dbus_dict_entry_clear(&entry);
if (driver == NULL)
goto error;
- } else if (!strcmp(entry.key, "Ifname") &&
+ } else if (!os_strcmp(entry.key, "Ifname") &&
(entry.type == DBUS_TYPE_STRING)) {
ifname = os_strdup(entry.str_value);
wpa_dbus_dict_entry_clear(&entry);
if (ifname == NULL)
goto error;
- } else if (!strcmp(entry.key, "BridgeIfname") &&
+ } else if (!os_strcmp(entry.key, "ConfigFile") &&
+ (entry.type == DBUS_TYPE_STRING)) {
+ confname = os_strdup(entry.str_value);
+ wpa_dbus_dict_entry_clear(&entry);
+ if (confname == NULL)
+ goto error;
+ } else if (!os_strcmp(entry.key, "BridgeIfname") &&
(entry.type == DBUS_TYPE_STRING)) {
bridge_ifname = os_strdup(entry.str_value);
wpa_dbus_dict_entry_clear(&entry);
@@ -604,6 +590,7 @@ DBusMessage * wpas_dbus_handler_create_interface(DBusMessage *message,
os_memset(&iface, 0, sizeof(iface));
iface.driver = driver;
iface.ifname = ifname;
+ iface.confname = confname;
iface.bridge_ifname = bridge_ifname;
/* Otherwise, have wpa_supplicant attach to it. */
if ((wpa_s = wpa_supplicant_add_iface(global, &iface))) {
@@ -621,6 +608,7 @@ DBusMessage * wpas_dbus_handler_create_interface(DBusMessage *message,
out:
os_free(driver);
os_free(ifname);
+ os_free(confname);
os_free(bridge_ifname);
return reply;
@@ -654,7 +642,7 @@ DBusMessage * wpas_dbus_handler_remove_interface(DBusMessage *message,
wpa_s = get_iface_by_dbus_path(global, path);
if (wpa_s == NULL)
reply = wpas_dbus_error_iface_unknown(message);
- else if (wpa_supplicant_remove_iface(global, wpa_s)) {
+ else if (wpa_supplicant_remove_iface(global, wpa_s, 0)) {
reply = wpas_dbus_error_unknown_error(
message, "wpa_supplicant couldn't remove this "
"interface.");
@@ -706,79 +694,86 @@ DBusMessage * wpas_dbus_handler_get_interface(DBusMessage *message,
/**
* wpas_dbus_getter_debug_level - Get debug level
- * @message: Pointer to incoming dbus message
- * @global: %wpa_supplicant global data structure
- * Returns: DBus message with value of debug level
+ * @iter: Pointer to incoming dbus message iter
+ * @error: Location to store error on failure
+ * @user_data: Function specific data
+ * Returns: TRUE on success, FALSE on failure
*
* Getter for "DebugLevel" property.
*/
-DBusMessage * wpas_dbus_getter_debug_level(DBusMessage *message,
- struct wpa_global *global)
+dbus_bool_t wpas_dbus_getter_debug_level(DBusMessageIter *iter,
+ DBusError *error,
+ void *user_data)
{
const char *str;
int idx = wpa_debug_level;
+
if (idx < 0)
idx = 0;
- if (idx > 4)
- idx = 4;
+ if (idx > 5)
+ idx = 5;
str = debug_strings[idx];
- return wpas_dbus_simple_property_getter(message, DBUS_TYPE_STRING,
- &str);
+ return wpas_dbus_simple_property_getter(iter, DBUS_TYPE_STRING,
+ &str, error);
}
/**
* wpas_dbus_getter_debug_timestamp - Get debug timestamp
- * @message: Pointer to incoming dbus message
- * @global: %wpa_supplicant global data structure
- * Returns: DBus message with value of debug timestamp
+ * @iter: Pointer to incoming dbus message iter
+ * @error: Location to store error on failure
+ * @user_data: Function specific data
+ * Returns: TRUE on success, FALSE on failure
*
* Getter for "DebugTimestamp" property.
*/
-DBusMessage * wpas_dbus_getter_debug_timestamp(DBusMessage *message,
- struct wpa_global *global)
+dbus_bool_t wpas_dbus_getter_debug_timestamp(DBusMessageIter *iter,
+ DBusError *error,
+ void *user_data)
{
- return wpas_dbus_simple_property_getter(message, DBUS_TYPE_BOOLEAN,
- &wpa_debug_timestamp);
+ return wpas_dbus_simple_property_getter(iter, DBUS_TYPE_BOOLEAN,
+ &wpa_debug_timestamp, error);
}
/**
* wpas_dbus_getter_debug_show_keys - Get debug show keys
- * @message: Pointer to incoming dbus message
- * @global: %wpa_supplicant global data structure
- * Returns: DBus message with value of debug show_keys
+ * @iter: Pointer to incoming dbus message iter
+ * @error: Location to store error on failure
+ * @user_data: Function specific data
+ * Returns: TRUE on success, FALSE on failure
*
* Getter for "DebugShowKeys" property.
*/
-DBusMessage * wpas_dbus_getter_debug_show_keys(DBusMessage *message,
- struct wpa_global *global)
+dbus_bool_t wpas_dbus_getter_debug_show_keys(DBusMessageIter *iter,
+ DBusError *error,
+ void *user_data)
{
- return wpas_dbus_simple_property_getter(message, DBUS_TYPE_BOOLEAN,
- &wpa_debug_show_keys);
+ return wpas_dbus_simple_property_getter(iter, DBUS_TYPE_BOOLEAN,
+ &wpa_debug_show_keys, error);
}
/**
* wpas_dbus_setter_debug_level - Set debug level
- * @message: Pointer to incoming dbus message
- * @global: %wpa_supplicant global data structure
- * Returns: %NULL or DBus error message
+ * @iter: Pointer to incoming dbus message iter
+ * @error: Location to store error on failure
+ * @user_data: Function specific data
+ * Returns: TRUE on success, FALSE on failure
*
* Setter for "DebugLevel" property.
*/
-DBusMessage * wpas_dbus_setter_debug_level(DBusMessage *message,
- struct wpa_global *global)
+dbus_bool_t wpas_dbus_setter_debug_level(DBusMessageIter *iter,
+ DBusError *error, void *user_data)
{
- DBusMessage *reply;
+ struct wpa_global *global = user_data;
const char *str = NULL;
int i, val = -1;
- reply = wpas_dbus_simple_property_setter(message, DBUS_TYPE_STRING,
- &str);
- if (reply)
- return reply;
+ if (!wpas_dbus_simple_property_setter(iter, error, DBUS_TYPE_STRING,
+ &str))
+ return FALSE;
for (i = 0; debug_strings[i]; i++)
if (os_strcmp(debug_strings[i], str) == 0) {
@@ -789,138 +784,180 @@ DBusMessage * wpas_dbus_setter_debug_level(DBusMessage *message,
if (val < 0 ||
wpa_supplicant_set_debug_params(global, val, wpa_debug_timestamp,
wpa_debug_show_keys)) {
- dbus_message_unref(reply);
- return wpas_dbus_error_invalid_args(
- message, "Wrong debug level value");
+ dbus_set_error_const(error, DBUS_ERROR_FAILED, "wrong debug "
+ "level value");
+ return FALSE;
}
- return NULL;
+ return TRUE;
}
/**
* wpas_dbus_setter_debug_timestamp - Set debug timestamp
- * @message: Pointer to incoming dbus message
- * @global: %wpa_supplicant global data structure
- * Returns: %NULL or DBus error message
+ * @iter: Pointer to incoming dbus message iter
+ * @error: Location to store error on failure
+ * @user_data: Function specific data
+ * Returns: TRUE on success, FALSE on failure
*
* Setter for "DebugTimestamp" property.
*/
-DBusMessage * wpas_dbus_setter_debug_timestamp(DBusMessage *message,
- struct wpa_global *global)
+dbus_bool_t wpas_dbus_setter_debug_timestamp(DBusMessageIter *iter,
+ DBusError *error,
+ void *user_data)
{
- DBusMessage *reply;
+ struct wpa_global *global = user_data;
dbus_bool_t val;
- reply = wpas_dbus_simple_property_setter(message, DBUS_TYPE_BOOLEAN,
- &val);
- if (reply)
- return reply;
+ if (!wpas_dbus_simple_property_setter(iter, error, DBUS_TYPE_BOOLEAN,
+ &val))
+ return FALSE;
wpa_supplicant_set_debug_params(global, wpa_debug_level, val ? 1 : 0,
wpa_debug_show_keys);
-
- return NULL;
+ return TRUE;
}
/**
* wpas_dbus_setter_debug_show_keys - Set debug show keys
- * @message: Pointer to incoming dbus message
- * @global: %wpa_supplicant global data structure
- * Returns: %NULL or DBus error message
+ * @iter: Pointer to incoming dbus message iter
+ * @error: Location to store error on failure
+ * @user_data: Function specific data
+ * Returns: TRUE on success, FALSE on failure
*
* Setter for "DebugShowKeys" property.
*/
-DBusMessage * wpas_dbus_setter_debug_show_keys(DBusMessage *message,
- struct wpa_global *global)
+dbus_bool_t wpas_dbus_setter_debug_show_keys(DBusMessageIter *iter,
+ DBusError *error,
+ void *user_data)
{
- DBusMessage *reply;
+ struct wpa_global *global = user_data;
dbus_bool_t val;
- reply = wpas_dbus_simple_property_setter(message, DBUS_TYPE_BOOLEAN,
- &val);
- if (reply)
- return reply;
+ if (!wpas_dbus_simple_property_setter(iter, error, DBUS_TYPE_BOOLEAN,
+ &val))
+ return FALSE;
wpa_supplicant_set_debug_params(global, wpa_debug_level,
wpa_debug_timestamp,
val ? 1 : 0);
-
- return NULL;
+ return TRUE;
}
/**
* wpas_dbus_getter_interfaces - Request registered interfaces list
- * @message: Pointer to incoming dbus message
- * @global: %wpa_supplicant global data structure
- * Returns: The object paths array containing registered interfaces
- * objects paths or DBus error on failure
+ * @iter: Pointer to incoming dbus message iter
+ * @error: Location to store error on failure
+ * @user_data: Function specific data
+ * Returns: TRUE on success, FALSE on failure
*
* Getter for "Interfaces" property. Handles requests
* by dbus clients to return list of registered interfaces objects
* paths
*/
-DBusMessage * wpas_dbus_getter_interfaces(DBusMessage *message,
- struct wpa_global *global)
+dbus_bool_t wpas_dbus_getter_interfaces(DBusMessageIter *iter,
+ DBusError *error,
+ void *user_data)
{
- DBusMessage *reply = NULL;
+ struct wpa_global *global = user_data;
struct wpa_supplicant *wpa_s;
const char **paths;
unsigned int i = 0, num = 0;
+ dbus_bool_t success;
for (wpa_s = global->ifaces; wpa_s; wpa_s = wpa_s->next)
num++;
- paths = os_zalloc(num * sizeof(char*));
+ paths = os_calloc(num, sizeof(char *));
if (!paths) {
- return dbus_message_new_error(message, DBUS_ERROR_NO_MEMORY,
- NULL);
+ dbus_set_error_const(error, DBUS_ERROR_NO_MEMORY, "no memory");
+ return FALSE;
}
for (wpa_s = global->ifaces; wpa_s; wpa_s = wpa_s->next)
- paths[i] = wpa_s->dbus_new_path;
+ paths[i++] = wpa_s->dbus_new_path;
- reply = wpas_dbus_simple_array_property_getter(message,
- DBUS_TYPE_OBJECT_PATH,
- paths, num);
+ success = wpas_dbus_simple_array_property_getter(iter,
+ DBUS_TYPE_OBJECT_PATH,
+ paths, num, error);
os_free(paths);
- return reply;
+ return success;
}
/**
* wpas_dbus_getter_eap_methods - Request supported EAP methods list
- * @message: Pointer to incoming dbus message
- * @nothing: not used argument. may be NULL or anything else
- * Returns: The object paths array containing supported EAP methods
- * represented by strings or DBus error on failure
+ * @iter: Pointer to incoming dbus message iter
+ * @error: Location to store error on failure
+ * @user_data: Function specific data
+ * Returns: TRUE on success, FALSE on failure
*
* Getter for "EapMethods" property. Handles requests
* by dbus clients to return list of strings with supported EAP methods
*/
-DBusMessage * wpas_dbus_getter_eap_methods(DBusMessage *message, void *nothing)
+dbus_bool_t wpas_dbus_getter_eap_methods(DBusMessageIter *iter,
+ DBusError *error, void *user_data)
{
- DBusMessage *reply = NULL;
char **eap_methods;
size_t num_items = 0;
+ dbus_bool_t success;
eap_methods = eap_get_names_as_string_array(&num_items);
if (!eap_methods) {
- return dbus_message_new_error(message, DBUS_ERROR_NO_MEMORY,
- NULL);
+ dbus_set_error_const(error, DBUS_ERROR_NO_MEMORY, "no memory");
+ return FALSE;
}
- reply = wpas_dbus_simple_array_property_getter(message,
- DBUS_TYPE_STRING,
- eap_methods, num_items);
+ success = wpas_dbus_simple_array_property_getter(iter,
+ DBUS_TYPE_STRING,
+ eap_methods,
+ num_items, error);
while (num_items)
os_free(eap_methods[--num_items]);
os_free(eap_methods);
- return reply;
+ return success;
+}
+
+
+/**
+ * wpas_dbus_getter_global_capabilities - Request supported global capabilities
+ * @iter: Pointer to incoming dbus message iter
+ * @error: Location to store error on failure
+ * @user_data: Function specific data
+ * Returns: TRUE on success, FALSE on failure
+ *
+ * Getter for "Capabilities" property. Handles requests by dbus clients to
+ * return a list of strings with supported capabilities like AP, RSN IBSS,
+ * and P2P that are determined at compile time.
+ */
+dbus_bool_t wpas_dbus_getter_global_capabilities(DBusMessageIter *iter,
+ DBusError *error,
+ void *user_data)
+{
+ const char *capabilities[5] = { NULL, NULL, NULL, NULL, NULL };
+ size_t num_items = 0;
+
+#ifdef CONFIG_AP
+ capabilities[num_items++] = "ap";
+#endif /* CONFIG_AP */
+#ifdef CONFIG_IBSS_RSN
+ capabilities[num_items++] = "ibss-rsn";
+#endif /* CONFIG_IBSS_RSN */
+#ifdef CONFIG_P2P
+ capabilities[num_items++] = "p2p";
+#endif /* CONFIG_P2P */
+#ifdef CONFIG_INTERWORKING
+ capabilities[num_items++] = "interworking";
+#endif /* CONFIG_INTERWORKING */
+
+ return wpas_dbus_simple_array_property_getter(iter,
+ DBUS_TYPE_STRING,
+ capabilities,
+ num_items, error);
}
@@ -987,21 +1024,34 @@ static int wpas_dbus_get_scan_ssids(DBusMessage *message, DBusMessageIter *var,
dbus_message_iter_recurse(&array_iter, &sub_array_iter);
dbus_message_iter_get_fixed_array(&sub_array_iter, &val, &len);
- if (len == 0) {
- dbus_message_iter_next(&array_iter);
- continue;
- }
- ssid = os_malloc(len);
- if (ssid == NULL) {
- wpa_printf(MSG_DEBUG, "wpas_dbus_handler_scan[dbus]: "
- "out of memory. Cannot allocate memory for "
- "SSID");
- *reply = dbus_message_new_error(
- message, DBUS_ERROR_NO_MEMORY, NULL);
+ if (len > MAX_SSID_LEN) {
+ wpa_printf(MSG_DEBUG,
+ "wpas_dbus_handler_scan[dbus]: "
+ "SSID too long (len=%d max_len=%d)",
+ len, MAX_SSID_LEN);
+ *reply = wpas_dbus_error_invalid_args(
+ message, "Invalid SSID: too long");
return -1;
}
- os_memcpy(ssid, val, len);
+
+ if (len != 0) {
+ ssid = os_malloc(len);
+ if (ssid == NULL) {
+ wpa_printf(MSG_DEBUG,
+ "wpas_dbus_handler_scan[dbus]: "
+ "out of memory. Cannot allocate "
+ "memory for SSID");
+ *reply = dbus_message_new_error(
+ message, DBUS_ERROR_NO_MEMORY, NULL);
+ return -1;
+ }
+ os_memcpy(ssid, val, len);
+ } else {
+ /* Allow zero-length SSIDs */
+ ssid = NULL;
+ }
+
ssids[ssids_num].ssid = ssid;
ssids[ssids_num].ssid_len = len;
@@ -1146,8 +1196,9 @@ static int wpas_dbus_get_scan_channels(DBusMessage *message,
#define FREQS_ALLOC_CHUNK 32
if (freqs_num % FREQS_ALLOC_CHUNK == 0) {
- nfreqs = os_realloc(freqs, sizeof(int) *
- (freqs_num + FREQS_ALLOC_CHUNK));
+ nfreqs = os_realloc_array(
+ freqs, freqs_num + FREQS_ALLOC_CHUNK,
+ sizeof(int));
if (nfreqs == NULL)
os_free(freqs);
freqs = nfreqs;
@@ -1167,8 +1218,7 @@ static int wpas_dbus_get_scan_channels(DBusMessage *message,
dbus_message_iter_next(&array_iter);
}
- nfreqs = os_realloc(freqs,
- sizeof(int) * (freqs_num + 1));
+ nfreqs = os_realloc_array(freqs, freqs_num + 1, sizeof(int));
if (nfreqs == NULL)
os_free(freqs);
freqs = nfreqs;
@@ -1260,14 +1310,19 @@ DBusMessage * wpas_dbus_handler_scan(DBusMessage *message,
"passive scan");
goto out;
} else if (params.freqs && params.freqs[0]) {
- /* wildcard ssid */
- params.num_ssids++;
wpa_supplicant_trigger_scan(wpa_s, &params);
} else {
- wpa_s->scan_req = 2;
+ wpa_s->scan_req = MANUAL_SCAN_REQ;
wpa_supplicant_req_scan(wpa_s, 0, 0);
}
} else if (!os_strcmp(type, "active")) {
+ if (!params.num_ssids) {
+ /* Add wildcard ssid */
+ params.num_ssids++;
+ }
+#ifdef CONFIG_AUTOSCAN
+ autoscan_deinit(wpa_s);
+#endif /* CONFIG_AUTOSCAN */
wpa_supplicant_trigger_scan(wpa_s, &params);
} else {
wpa_printf(MSG_DEBUG, "wpas_dbus_handler_scan[dbus]: "
@@ -1326,6 +1381,7 @@ DBusMessage * wpas_dbus_handler_add_network(DBusMessage *message,
DBusMessageIter iter;
struct wpa_ssid *ssid = NULL;
char path_buf[WPAS_DBUS_OBJECT_PATH_MAX], *path = path_buf;
+ DBusError error;
dbus_message_iter_init(message, &iter);
@@ -1343,11 +1399,15 @@ DBusMessage * wpas_dbus_handler_add_network(DBusMessage *message,
ssid->disabled = 1;
wpa_config_set_network_defaults(ssid);
- reply = set_network_properties(message, wpa_s, ssid, &iter);
- if (reply) {
+ dbus_error_init(&error);
+ if (!set_network_properties(wpa_s, ssid, &iter, &error)) {
wpa_printf(MSG_DEBUG, "wpas_dbus_handler_add_network[dbus]:"
"control interface couldn't set network "
"properties");
+ reply = wpas_dbus_reply_new_from_error(message, &error,
+ DBUS_ERROR_INVALID_ARGS,
+ "Failed to add network");
+ dbus_error_free(&error);
goto err;
}
@@ -1382,6 +1442,28 @@ err:
/**
+ * wpas_dbus_handler_reassociate - Reassociate to current AP
+ * @message: Pointer to incoming dbus message
+ * @wpa_s: wpa_supplicant structure for a network interface
+ * Returns: NotConnected DBus error message if not connected
+ * or NULL otherwise.
+ *
+ * Handler function for "Reassociate" method call of network interface.
+ */
+DBusMessage * wpas_dbus_handler_reassociate(DBusMessage *message,
+ struct wpa_supplicant *wpa_s)
+{
+ if (wpa_s->current_ssid != NULL) {
+ wpas_request_connection(wpa_s);
+ return NULL;
+ }
+
+ return dbus_message_new_error(message, WPAS_DBUS_ERROR_NOT_CONNECTED,
+ "This interface is not connected");
+}
+
+
+/**
* wpas_dbus_handler_remove_network - Remove a configured network
* @message: Pointer to incoming dbus message
* @wpa_s: wpa_supplicant structure for a network interface
@@ -1403,14 +1485,16 @@ DBusMessage * wpas_dbus_handler_remove_network(DBusMessage *message,
/* Extract the network ID and ensure the network */
/* is actually a child of this interface */
- iface = wpas_dbus_new_decompose_object_path(op, &net_id, NULL);
- if (iface == NULL || os_strcmp(iface, wpa_s->dbus_new_path) != 0) {
+ iface = wpas_dbus_new_decompose_object_path(op, 0, &net_id, NULL);
+ if (iface == NULL || net_id == NULL ||
+ os_strcmp(iface, wpa_s->dbus_new_path) != 0) {
reply = wpas_dbus_error_invalid_args(message, op);
goto out;
}
+ errno = 0;
id = strtoul(net_id, NULL, 10);
- if (errno == EINVAL) {
+ if (errno != 0) {
reply = wpas_dbus_error_invalid_args(message, op);
goto out;
}
@@ -1444,6 +1528,43 @@ out:
}
+static void remove_network(void *arg, struct wpa_ssid *ssid)
+{
+ struct wpa_supplicant *wpa_s = arg;
+
+ wpas_notify_network_removed(wpa_s, ssid);
+
+ if (wpa_config_remove_network(wpa_s->conf, ssid->id) < 0) {
+ wpa_printf(MSG_ERROR,
+ "wpas_dbus_handler_remove_all_networks[dbus]: "
+ "error occurred when removing network %d",
+ ssid->id);
+ return;
+ }
+
+ if (ssid == wpa_s->current_ssid)
+ wpa_supplicant_deauthenticate(wpa_s,
+ WLAN_REASON_DEAUTH_LEAVING);
+}
+
+
+/**
+ * wpas_dbus_handler_remove_all_networks - Remove all configured networks
+ * @message: Pointer to incoming dbus message
+ * @wpa_s: wpa_supplicant structure for a network interface
+ * Returns: NULL on success or dbus error on failure
+ *
+ * Handler function for "RemoveAllNetworks" method call of a network interface.
+ */
+DBusMessage * wpas_dbus_handler_remove_all_networks(
+ DBusMessage *message, struct wpa_supplicant *wpa_s)
+{
+ /* NB: could check for failure and return an error */
+ wpa_config_foreach_network(wpa_s->conf, remove_network, wpa_s);
+ return NULL;
+}
+
+
/**
* wpas_dbus_handler_select_network - Attempt association with a network
* @message: Pointer to incoming dbus message
@@ -1466,14 +1587,16 @@ DBusMessage * wpas_dbus_handler_select_network(DBusMessage *message,
/* Extract the network ID and ensure the network */
/* is actually a child of this interface */
- iface = wpas_dbus_new_decompose_object_path(op, &net_id, NULL);
- if (iface == NULL || os_strcmp(iface, wpa_s->dbus_new_path) != 0) {
+ iface = wpas_dbus_new_decompose_object_path(op, 0, &net_id, NULL);
+ if (iface == NULL || net_id == NULL ||
+ os_strcmp(iface, wpa_s->dbus_new_path) != 0) {
reply = wpas_dbus_error_invalid_args(message, op);
goto out;
}
+ errno = 0;
id = strtoul(net_id, NULL, 10);
- if (errno == EINVAL) {
+ if (errno != 0) {
reply = wpas_dbus_error_invalid_args(message, op);
goto out;
}
@@ -1495,6 +1618,72 @@ out:
/**
+ * wpas_dbus_handler_network_reply - Reply to a NetworkRequest signal
+ * @message: Pointer to incoming dbus message
+ * @wpa_s: wpa_supplicant structure for a network interface
+ * Returns: NULL on success or dbus error on failure
+ *
+ * Handler function for "NetworkReply" method call of network interface.
+ */
+DBusMessage * wpas_dbus_handler_network_reply(DBusMessage *message,
+ struct wpa_supplicant *wpa_s)
+{
+#ifdef IEEE8021X_EAPOL
+ DBusMessage *reply = NULL;
+ const char *op, *field, *value;
+ char *iface = NULL, *net_id = NULL;
+ int id;
+ struct wpa_ssid *ssid;
+
+ if (!dbus_message_get_args(message, NULL,
+ DBUS_TYPE_OBJECT_PATH, &op,
+ DBUS_TYPE_STRING, &field,
+ DBUS_TYPE_STRING, &value,
+ DBUS_TYPE_INVALID))
+ return wpas_dbus_error_invalid_args(message, NULL);
+
+ /* Extract the network ID and ensure the network */
+ /* is actually a child of this interface */
+ iface = wpas_dbus_new_decompose_object_path(op, 0, &net_id, NULL);
+ if (iface == NULL || net_id == NULL ||
+ os_strcmp(iface, wpa_s->dbus_new_path) != 0) {
+ reply = wpas_dbus_error_invalid_args(message, op);
+ goto out;
+ }
+
+ errno = 0;
+ id = strtoul(net_id, NULL, 10);
+ if (errno != 0) {
+ reply = wpas_dbus_error_invalid_args(message, net_id);
+ goto out;
+ }
+
+ ssid = wpa_config_get_network(wpa_s->conf, id);
+ if (ssid == NULL) {
+ reply = wpas_dbus_error_network_unknown(message);
+ goto out;
+ }
+
+ if (wpa_supplicant_ctrl_iface_ctrl_rsp_handle(wpa_s, ssid,
+ field, value) < 0)
+ reply = wpas_dbus_error_invalid_args(message, field);
+ else {
+ /* Tell EAP to retry immediately */
+ eapol_sm_notify_ctrl_response(wpa_s->eapol);
+ }
+
+out:
+ os_free(iface);
+ os_free(net_id);
+ return reply;
+#else /* IEEE8021X_EAPOL */
+ wpa_printf(MSG_DEBUG, "CTRL_IFACE: 802.1X not included");
+ return wpas_dbus_error_unknown_error(message, "802.1X not included");
+#endif /* IEEE8021X_EAPOL */
+}
+
+
+/**
* wpas_dbus_handler_add_blob - Store named binary blob (ie, for certificates)
* @message: Pointer to incoming dbus message
* @wpa_s: %wpa_supplicant data structure
@@ -1658,37 +1847,99 @@ DBusMessage * wpas_dbus_handler_remove_blob(DBusMessage *message,
}
+/*
+ * wpas_dbus_handler_flush_bss - Flush the BSS cache
+ * @message: Pointer to incoming dbus message
+ * @wpa_s: wpa_supplicant structure for a network interface
+ * Returns: NULL
+ *
+ * Handler function for "FlushBSS" method call of network interface.
+ */
+DBusMessage * wpas_dbus_handler_flush_bss(DBusMessage *message,
+ struct wpa_supplicant *wpa_s)
+{
+ dbus_uint32_t age;
+
+ dbus_message_get_args(message, NULL, DBUS_TYPE_UINT32, &age,
+ DBUS_TYPE_INVALID);
+
+ if (age == 0)
+ wpa_bss_flush(wpa_s);
+ else
+ wpa_bss_flush_by_age(wpa_s, age);
+
+ return NULL;
+}
+
+#ifdef CONFIG_AUTOSCAN
/**
- * wpas_dbus_getter_capabilities - Return interface capabilities
+ * wpas_dbus_handler_autoscan - Set autoscan parameters for the interface
* @message: Pointer to incoming dbus message
* @wpa_s: wpa_supplicant structure for a network interface
- * Returns: A dbus message containing a dict of strings
+ * Returns: NULL
*
- * Getter for "Capabilities" property of an interface.
+ * Handler function for "AutoScan" method call of network interface.
*/
-DBusMessage * wpas_dbus_getter_capabilities(DBusMessage *message,
- struct wpa_supplicant *wpa_s)
+DBusMessage * wpas_dbus_handler_autoscan(DBusMessage *message,
+ struct wpa_supplicant *wpa_s)
{
DBusMessage *reply = NULL;
+ enum wpa_states state = wpa_s->wpa_state;
+ char *arg;
+
+ dbus_message_get_args(message, NULL, DBUS_TYPE_STRING, &arg,
+ DBUS_TYPE_INVALID);
+
+ if (arg != NULL && os_strlen(arg) > 0) {
+ char *tmp;
+ tmp = os_strdup(arg);
+ if (tmp == NULL) {
+ reply = dbus_message_new_error(message,
+ DBUS_ERROR_NO_MEMORY,
+ NULL);
+ } else {
+ os_free(wpa_s->conf->autoscan);
+ wpa_s->conf->autoscan = tmp;
+ if (state == WPA_DISCONNECTED || state == WPA_INACTIVE)
+ autoscan_init(wpa_s, 1);
+ else if (state == WPA_SCANNING)
+ wpa_supplicant_reinit_autoscan(wpa_s);
+ }
+ } else if (arg != NULL && os_strlen(arg) == 0) {
+ os_free(wpa_s->conf->autoscan);
+ wpa_s->conf->autoscan = NULL;
+ autoscan_deinit(wpa_s);
+ } else
+ reply = dbus_message_new_error(message,
+ DBUS_ERROR_INVALID_ARGS,
+ NULL);
+
+ return reply;
+}
+#endif /* CONFIG_AUTOSCAN */
+
+
+/**
+ * wpas_dbus_getter_capabilities - Return interface capabilities
+ * @iter: Pointer to incoming dbus message iter
+ * @error: Location to store error on failure
+ * @user_data: Function specific data
+ * Returns: TRUE on success, FALSE on failure
+ *
+ * Getter for "Capabilities" property of an interface.
+ */
+dbus_bool_t wpas_dbus_getter_capabilities(DBusMessageIter *iter,
+ DBusError *error, void *user_data)
+{
+ struct wpa_supplicant *wpa_s = user_data;
struct wpa_driver_capa capa;
int res;
- DBusMessageIter iter, iter_dict;
- DBusMessageIter iter_dict_entry, iter_dict_val, iter_array,
+ DBusMessageIter iter_dict, iter_dict_entry, iter_dict_val, iter_array,
variant_iter;
const char *scans[] = { "active", "passive", "ssid" };
- const char *modes[] = { "infrastructure", "ad-hoc", "ap" };
- int n = sizeof(modes) / sizeof(char *);
-
- if (message == NULL)
- reply = dbus_message_new(DBUS_MESSAGE_TYPE_SIGNAL);
- else
- reply = dbus_message_new_method_return(message);
- if (!reply)
- goto nomem;
- dbus_message_iter_init_append(reply, &iter);
- if (!dbus_message_iter_open_container(&iter, DBUS_TYPE_VARIANT,
+ if (!dbus_message_iter_open_container(iter, DBUS_TYPE_VARIANT,
"a{sv}", &variant_iter))
goto nomem;
@@ -1717,6 +1968,12 @@ DBusMessage * wpas_dbus_getter_capabilities(DBusMessage *message,
goto nomem;
}
+ if (capa.enc & WPA_DRIVER_CAPA_ENC_GCMP) {
+ if (!wpa_dbus_dict_string_array_add_element(
+ &iter_array, "gcmp"))
+ goto nomem;
+ }
+
if (capa.enc & WPA_DRIVER_CAPA_ENC_TKIP) {
if (!wpa_dbus_dict_string_array_add_element(
&iter_array, "tkip"))
@@ -1758,6 +2015,12 @@ DBusMessage * wpas_dbus_getter_capabilities(DBusMessage *message,
goto nomem;
}
+ if (capa.enc & WPA_DRIVER_CAPA_ENC_GCMP) {
+ if (!wpa_dbus_dict_string_array_add_element(
+ &iter_array, "gcmp"))
+ goto nomem;
+ }
+
if (capa.enc & WPA_DRIVER_CAPA_ENC_TKIP) {
if (!wpa_dbus_dict_string_array_add_element(
&iter_array, "tkip"))
@@ -1949,41 +2212,78 @@ DBusMessage * wpas_dbus_getter_capabilities(DBusMessage *message,
goto nomem;
/***** Modes */
- if (res < 0 || !(capa.flags & WPA_DRIVER_FLAGS_AP))
- n--; /* exclude ap mode if it is not supported by the driver */
- if (!wpa_dbus_dict_append_string_array(&iter_dict, "Modes", modes, n))
+ if (!wpa_dbus_dict_begin_string_array(&iter_dict, "Modes",
+ &iter_dict_entry,
+ &iter_dict_val,
+ &iter_array))
+ goto nomem;
+
+ if (!wpa_dbus_dict_string_array_add_element(
+ &iter_array, "infrastructure"))
+ goto nomem;
+
+ if (!wpa_dbus_dict_string_array_add_element(
+ &iter_array, "ad-hoc"))
+ goto nomem;
+
+ if (res >= 0) {
+ if (capa.flags & (WPA_DRIVER_FLAGS_AP)) {
+ if (!wpa_dbus_dict_string_array_add_element(
+ &iter_array, "ap"))
+ goto nomem;
+ }
+
+ if (capa.flags & (WPA_DRIVER_FLAGS_P2P_CAPABLE)) {
+ if (!wpa_dbus_dict_string_array_add_element(
+ &iter_array, "p2p"))
+ goto nomem;
+ }
+ }
+
+ if (!wpa_dbus_dict_end_string_array(&iter_dict,
+ &iter_dict_entry,
+ &iter_dict_val,
+ &iter_array))
goto nomem;
+ /***** Modes end */
+
+ if (res >= 0) {
+ dbus_int32_t max_scan_ssid = capa.max_scan_ssids;
+
+ if (!wpa_dbus_dict_append_int32(&iter_dict, "MaxScanSSID",
+ max_scan_ssid))
+ goto nomem;
+ }
if (!wpa_dbus_dict_close_write(&variant_iter, &iter_dict))
goto nomem;
- if (!dbus_message_iter_close_container(&iter, &variant_iter))
+ if (!dbus_message_iter_close_container(iter, &variant_iter))
goto nomem;
- return reply;
+ return TRUE;
nomem:
- if (reply)
- dbus_message_unref(reply);
-
- return dbus_message_new_error(message, DBUS_ERROR_NO_MEMORY, NULL);
+ dbus_set_error_const(error, DBUS_ERROR_NO_MEMORY, "no memory");
+ return FALSE;
}
/**
* wpas_dbus_getter_state - Get interface state
- * @message: Pointer to incoming dbus message
- * @wpa_s: wpa_supplicant structure for a network interface
- * Returns: A dbus message containing a STRING representing the current
- * interface state
+ * @iter: Pointer to incoming dbus message iter
+ * @error: Location to store error on failure
+ * @user_data: Function specific data
+ * Returns: TRUE on success, FALSE on failure
*
* Getter for "State" property.
*/
-DBusMessage * wpas_dbus_getter_state(DBusMessage *message,
- struct wpa_supplicant *wpa_s)
+dbus_bool_t wpas_dbus_getter_state(DBusMessageIter *iter, DBusError *error,
+ void *user_data)
{
- DBusMessage *reply = NULL;
+ struct wpa_supplicant *wpa_s = user_data;
const char *str_state;
char *state_ls, *tmp;
+ dbus_bool_t success = FALSE;
str_state = wpa_supplicant_state_txt(wpa_s->wpa_state);
@@ -1991,141 +2291,436 @@ DBusMessage * wpas_dbus_getter_state(DBusMessage *message,
*/
state_ls = tmp = os_strdup(str_state);
if (!tmp) {
- return dbus_message_new_error(message, DBUS_ERROR_NO_MEMORY,
- NULL);
+ dbus_set_error_const(error, DBUS_ERROR_NO_MEMORY, "no memory");
+ return FALSE;
}
while (*tmp) {
*tmp = tolower(*tmp);
tmp++;
}
- reply = wpas_dbus_simple_property_getter(message, DBUS_TYPE_STRING,
- &state_ls);
+ success = wpas_dbus_simple_property_getter(iter, DBUS_TYPE_STRING,
+ &state_ls, error);
os_free(state_ls);
- return reply;
+ return success;
}
/**
* wpas_dbus_new_iface_get_scanning - Get interface scanning state
- * @message: Pointer to incoming dbus message
- * @wpa_s: wpa_supplicant structure for a network interface
- * Returns: A dbus message containing whether the interface is scanning
+ * @iter: Pointer to incoming dbus message iter
+ * @error: Location to store error on failure
+ * @user_data: Function specific data
+ * Returns: TRUE on success, FALSE on failure
*
* Getter for "scanning" property.
*/
-DBusMessage * wpas_dbus_getter_scanning(DBusMessage *message,
- struct wpa_supplicant *wpa_s)
+dbus_bool_t wpas_dbus_getter_scanning(DBusMessageIter *iter, DBusError *error,
+ void *user_data)
{
+ struct wpa_supplicant *wpa_s = user_data;
dbus_bool_t scanning = wpa_s->scanning ? TRUE : FALSE;
- return wpas_dbus_simple_property_getter(message, DBUS_TYPE_BOOLEAN,
- &scanning);
+
+ return wpas_dbus_simple_property_getter(iter, DBUS_TYPE_BOOLEAN,
+ &scanning, error);
}
/**
* wpas_dbus_getter_ap_scan - Control roaming mode
- * @message: Pointer to incoming dbus message
- * @wpa_s: wpa_supplicant structure for a network interface
- * Returns: A message containong value of ap_scan variable
+ * @iter: Pointer to incoming dbus message iter
+ * @error: Location to store error on failure
+ * @user_data: Function specific data
+ * Returns: TRUE on success, FALSE on failure
*
* Getter function for "ApScan" property.
*/
-DBusMessage * wpas_dbus_getter_ap_scan(DBusMessage *message,
- struct wpa_supplicant *wpa_s)
+dbus_bool_t wpas_dbus_getter_ap_scan(DBusMessageIter *iter, DBusError *error,
+ void *user_data)
{
+ struct wpa_supplicant *wpa_s = user_data;
dbus_uint32_t ap_scan = wpa_s->conf->ap_scan;
- return wpas_dbus_simple_property_getter(message, DBUS_TYPE_UINT32,
- &ap_scan);
+
+ return wpas_dbus_simple_property_getter(iter, DBUS_TYPE_UINT32,
+ &ap_scan, error);
}
/**
* wpas_dbus_setter_ap_scan - Control roaming mode
- * @message: Pointer to incoming dbus message
- * @wpa_s: wpa_supplicant structure for a network interface
- * Returns: NULL
+ * @iter: Pointer to incoming dbus message iter
+ * @error: Location to store error on failure
+ * @user_data: Function specific data
+ * Returns: TRUE on success, FALSE on failure
*
* Setter function for "ApScan" property.
*/
-DBusMessage * wpas_dbus_setter_ap_scan(DBusMessage *message,
- struct wpa_supplicant *wpa_s)
+dbus_bool_t wpas_dbus_setter_ap_scan(DBusMessageIter *iter, DBusError *error,
+ void *user_data)
{
- DBusMessage *reply = NULL;
+ struct wpa_supplicant *wpa_s = user_data;
dbus_uint32_t ap_scan;
- reply = wpas_dbus_simple_property_setter(message, DBUS_TYPE_UINT32,
- &ap_scan);
- if (reply)
- return reply;
+ if (!wpas_dbus_simple_property_setter(iter, error, DBUS_TYPE_UINT32,
+ &ap_scan))
+ return FALSE;
if (wpa_supplicant_set_ap_scan(wpa_s, ap_scan)) {
- return wpas_dbus_error_invalid_args(
- message, "ap_scan must equal 0, 1 or 2");
+ dbus_set_error_const(error, DBUS_ERROR_FAILED,
+ "ap_scan must be 0, 1, or 2");
+ return FALSE;
}
- return NULL;
+ return TRUE;
+}
+
+
+/**
+ * wpas_dbus_getter_fast_reauth - Control fast
+ * reauthentication (TLS session resumption)
+ * @iter: Pointer to incoming dbus message iter
+ * @error: Location to store error on failure
+ * @user_data: Function specific data
+ * Returns: TRUE on success, FALSE on failure
+ *
+ * Getter function for "FastReauth" property.
+ */
+dbus_bool_t wpas_dbus_getter_fast_reauth(DBusMessageIter *iter,
+ DBusError *error,
+ void *user_data)
+{
+ struct wpa_supplicant *wpa_s = user_data;
+ dbus_bool_t fast_reauth = wpa_s->conf->fast_reauth ? TRUE : FALSE;
+
+ return wpas_dbus_simple_property_getter(iter, DBUS_TYPE_BOOLEAN,
+ &fast_reauth, error);
+}
+
+
+/**
+ * wpas_dbus_setter_fast_reauth - Control fast
+ * reauthentication (TLS session resumption)
+ * @iter: Pointer to incoming dbus message iter
+ * @error: Location to store error on failure
+ * @user_data: Function specific data
+ * Returns: TRUE on success, FALSE on failure
+ *
+ * Setter function for "FastReauth" property.
+ */
+dbus_bool_t wpas_dbus_setter_fast_reauth(DBusMessageIter *iter,
+ DBusError *error,
+ void *user_data)
+{
+ struct wpa_supplicant *wpa_s = user_data;
+ dbus_bool_t fast_reauth;
+
+ if (!wpas_dbus_simple_property_setter(iter, error, DBUS_TYPE_BOOLEAN,
+ &fast_reauth))
+ return FALSE;
+
+ wpa_s->conf->fast_reauth = fast_reauth;
+ return TRUE;
+}
+
+
+/**
+ * wpas_dbus_getter_disconnect_reason - Get most recent reason for disconnect
+ * @iter: Pointer to incoming dbus message iter
+ * @error: Location to store error on failure
+ * @user_data: Function specific data
+ * Returns: TRUE on success, FALSE on failure
+ *
+ * Getter for "DisconnectReason" property. The reason is negative if it is
+ * locally generated.
+ */
+dbus_bool_t wpas_dbus_getter_disconnect_reason(DBusMessageIter *iter,
+ DBusError *error,
+ void *user_data)
+{
+ struct wpa_supplicant *wpa_s = user_data;
+ dbus_int32_t reason = wpa_s->disconnect_reason;
+ return wpas_dbus_simple_property_getter(iter, DBUS_TYPE_INT32,
+ &reason, error);
+}
+
+
+/**
+ * wpas_dbus_getter_bss_expire_age - Get BSS entry expiration age
+ * @iter: Pointer to incoming dbus message iter
+ * @error: Location to store error on failure
+ * @user_data: Function specific data
+ * Returns: TRUE on success, FALSE on failure
+ *
+ * Getter function for "BSSExpireAge" property.
+ */
+dbus_bool_t wpas_dbus_getter_bss_expire_age(DBusMessageIter *iter,
+ DBusError *error,
+ void *user_data)
+{
+ struct wpa_supplicant *wpa_s = user_data;
+ dbus_uint32_t expire_age = wpa_s->conf->bss_expiration_age;
+
+ return wpas_dbus_simple_property_getter(iter, DBUS_TYPE_UINT32,
+ &expire_age, error);
+}
+
+
+/**
+ * wpas_dbus_setter_bss_expire_age - Control BSS entry expiration age
+ * @iter: Pointer to incoming dbus message iter
+ * @error: Location to store error on failure
+ * @user_data: Function specific data
+ * Returns: TRUE on success, FALSE on failure
+ *
+ * Setter function for "BSSExpireAge" property.
+ */
+dbus_bool_t wpas_dbus_setter_bss_expire_age(DBusMessageIter *iter,
+ DBusError *error,
+ void *user_data)
+{
+ struct wpa_supplicant *wpa_s = user_data;
+ dbus_uint32_t expire_age;
+
+ if (!wpas_dbus_simple_property_setter(iter, error, DBUS_TYPE_UINT32,
+ &expire_age))
+ return FALSE;
+
+ if (wpa_supplicant_set_bss_expiration_age(wpa_s, expire_age)) {
+ dbus_set_error_const(error, DBUS_ERROR_FAILED,
+ "BSSExpireAge must be >= 10");
+ return FALSE;
+ }
+ return TRUE;
+}
+
+
+/**
+ * wpas_dbus_getter_bss_expire_count - Get BSS entry expiration scan count
+ * @iter: Pointer to incoming dbus message iter
+ * @error: Location to store error on failure
+ * @user_data: Function specific data
+ * Returns: TRUE on success, FALSE on failure
+ *
+ * Getter function for "BSSExpireCount" property.
+ */
+dbus_bool_t wpas_dbus_getter_bss_expire_count(DBusMessageIter *iter,
+ DBusError *error,
+ void *user_data)
+{
+ struct wpa_supplicant *wpa_s = user_data;
+ dbus_uint32_t expire_count = wpa_s->conf->bss_expiration_scan_count;
+
+ return wpas_dbus_simple_property_getter(iter, DBUS_TYPE_UINT32,
+ &expire_count, error);
+}
+
+
+/**
+ * wpas_dbus_setter_bss_expire_count - Control BSS entry expiration scan count
+ * @iter: Pointer to incoming dbus message iter
+ * @error: Location to store error on failure
+ * @user_data: Function specific data
+ * Returns: TRUE on success, FALSE on failure
+ *
+ * Setter function for "BSSExpireCount" property.
+ */
+dbus_bool_t wpas_dbus_setter_bss_expire_count(DBusMessageIter *iter,
+ DBusError *error,
+ void *user_data)
+{
+ struct wpa_supplicant *wpa_s = user_data;
+ dbus_uint32_t expire_count;
+
+ if (!wpas_dbus_simple_property_setter(iter, error, DBUS_TYPE_UINT32,
+ &expire_count))
+ return FALSE;
+
+ if (wpa_supplicant_set_bss_expiration_count(wpa_s, expire_count)) {
+ dbus_set_error_const(error, DBUS_ERROR_FAILED,
+ "BSSExpireCount must be > 0");
+ return FALSE;
+ }
+ return TRUE;
+}
+
+
+/**
+ * wpas_dbus_getter_country - Control country code
+ * @iter: Pointer to incoming dbus message iter
+ * @error: Location to store error on failure
+ * @user_data: Function specific data
+ * Returns: TRUE on success, FALSE on failure
+ *
+ * Getter function for "Country" property.
+ */
+dbus_bool_t wpas_dbus_getter_country(DBusMessageIter *iter, DBusError *error,
+ void *user_data)
+{
+ struct wpa_supplicant *wpa_s = user_data;
+ char country[3];
+ char *str = country;
+
+ country[0] = wpa_s->conf->country[0];
+ country[1] = wpa_s->conf->country[1];
+ country[2] = '\0';
+
+ return wpas_dbus_simple_property_getter(iter, DBUS_TYPE_STRING,
+ &str, error);
+}
+
+
+/**
+ * wpas_dbus_setter_country - Control country code
+ * @iter: Pointer to incoming dbus message iter
+ * @error: Location to store error on failure
+ * @user_data: Function specific data
+ * Returns: TRUE on success, FALSE on failure
+ *
+ * Setter function for "Country" property.
+ */
+dbus_bool_t wpas_dbus_setter_country(DBusMessageIter *iter, DBusError *error,
+ void *user_data)
+{
+ struct wpa_supplicant *wpa_s = user_data;
+ const char *country;
+
+ if (!wpas_dbus_simple_property_setter(iter, error, DBUS_TYPE_STRING,
+ &country))
+ return FALSE;
+
+ if (!country[0] || !country[1]) {
+ dbus_set_error_const(error, DBUS_ERROR_FAILED,
+ "invalid country code");
+ return FALSE;
+ }
+
+ if (wpa_s->drv_priv != NULL && wpa_drv_set_country(wpa_s, country)) {
+ wpa_printf(MSG_DEBUG, "Failed to set country");
+ dbus_set_error_const(error, DBUS_ERROR_FAILED,
+ "failed to set country code");
+ return FALSE;
+ }
+
+ wpa_s->conf->country[0] = country[0];
+ wpa_s->conf->country[1] = country[1];
+ return TRUE;
+}
+
+
+/**
+ * wpas_dbus_getter_scan_interval - Get scan interval
+ * @iter: Pointer to incoming dbus message iter
+ * @error: Location to store error on failure
+ * @user_data: Function specific data
+ * Returns: TRUE on success, FALSE on failure
+ *
+ * Getter function for "ScanInterval" property.
+ */
+dbus_bool_t wpas_dbus_getter_scan_interval(DBusMessageIter *iter,
+ DBusError *error,
+ void *user_data)
+{
+ struct wpa_supplicant *wpa_s = user_data;
+ dbus_int32_t scan_interval = wpa_s->scan_interval;
+
+ return wpas_dbus_simple_property_getter(iter, DBUS_TYPE_INT32,
+ &scan_interval, error);
+}
+
+
+/**
+ * wpas_dbus_setter_scan_interval - Control scan interval
+ * @iter: Pointer to incoming dbus message iter
+ * @error: Location to store error on failure
+ * @user_data: Function specific data
+ * Returns: TRUE on success, FALSE on failure
+ *
+ * Setter function for "ScanInterval" property.
+ */
+dbus_bool_t wpas_dbus_setter_scan_interval(DBusMessageIter *iter,
+ DBusError *error,
+ void *user_data)
+{
+ struct wpa_supplicant *wpa_s = user_data;
+ dbus_int32_t scan_interval;
+
+ if (!wpas_dbus_simple_property_setter(iter, error, DBUS_TYPE_INT32,
+ &scan_interval))
+ return FALSE;
+
+ if (wpa_supplicant_set_scan_interval(wpa_s, scan_interval)) {
+ dbus_set_error_const(error, DBUS_ERROR_FAILED,
+ "scan_interval must be >= 0");
+ return FALSE;
+ }
+ return TRUE;
}
/**
* wpas_dbus_getter_ifname - Get interface name
- * @message: Pointer to incoming dbus message
- * @wpa_s: wpa_supplicant structure for a network interface
- * Returns: A dbus message containing a name of network interface
- * associated with with wpa_s
+ * @iter: Pointer to incoming dbus message iter
+ * @error: Location to store error on failure
+ * @user_data: Function specific data
+ * Returns: TRUE on success, FALSE on failure
*
* Getter for "Ifname" property.
*/
-DBusMessage * wpas_dbus_getter_ifname(DBusMessage *message,
- struct wpa_supplicant *wpa_s)
+dbus_bool_t wpas_dbus_getter_ifname(DBusMessageIter *iter, DBusError *error,
+ void *user_data)
{
+ struct wpa_supplicant *wpa_s = user_data;
const char *ifname = wpa_s->ifname;
- return wpas_dbus_simple_property_getter(message, DBUS_TYPE_STRING,
- &ifname);
+
+ return wpas_dbus_simple_property_getter(iter, DBUS_TYPE_STRING,
+ &ifname, error);
}
/**
* wpas_dbus_getter_driver - Get interface name
- * @message: Pointer to incoming dbus message
- * @wpa_s: wpa_supplicant structure for a network interface
- * Returns: A dbus message containing a name of network interface
- * driver associated with with wpa_s
+ * @iter: Pointer to incoming dbus message iter
+ * @error: Location to store error on failure
+ * @user_data: Function specific data
+ * Returns: TRUE on success, FALSE on failure
*
* Getter for "Driver" property.
*/
-DBusMessage * wpas_dbus_getter_driver(DBusMessage *message,
- struct wpa_supplicant *wpa_s)
+dbus_bool_t wpas_dbus_getter_driver(DBusMessageIter *iter, DBusError *error,
+ void *user_data)
{
+ struct wpa_supplicant *wpa_s = user_data;
const char *driver;
if (wpa_s->driver == NULL || wpa_s->driver->name == NULL) {
wpa_printf(MSG_DEBUG, "wpas_dbus_getter_driver[dbus]: "
"wpa_s has no driver set");
- return wpas_dbus_error_unknown_error(message, NULL);
+ dbus_set_error(error, DBUS_ERROR_FAILED, "%s: no driver set",
+ __func__);
+ return FALSE;
}
driver = wpa_s->driver->name;
- return wpas_dbus_simple_property_getter(message, DBUS_TYPE_STRING,
- &driver);
+ return wpas_dbus_simple_property_getter(iter, DBUS_TYPE_STRING,
+ &driver, error);
}
/**
* wpas_dbus_getter_current_bss - Get current bss object path
- * @message: Pointer to incoming dbus message
- * @wpa_s: wpa_supplicant structure for a network interface
- * Returns: A dbus message containing a DBus object path to
- * current BSS
+ * @iter: Pointer to incoming dbus message iter
+ * @error: Location to store error on failure
+ * @user_data: Function specific data
+ * Returns: TRUE on success, FALSE on failure
*
* Getter for "CurrentBSS" property.
*/
-DBusMessage * wpas_dbus_getter_current_bss(DBusMessage *message,
- struct wpa_supplicant *wpa_s)
+dbus_bool_t wpas_dbus_getter_current_bss(DBusMessageIter *iter,
+ DBusError *error,
+ void *user_data)
{
- DBusMessage *reply;
+ struct wpa_supplicant *wpa_s = user_data;
char path_buf[WPAS_DBUS_OBJECT_PATH_MAX], *bss_obj_path = path_buf;
if (wpa_s->current_bss)
@@ -2135,27 +2730,25 @@ DBusMessage * wpas_dbus_getter_current_bss(DBusMessage *message,
else
os_snprintf(bss_obj_path, WPAS_DBUS_OBJECT_PATH_MAX, "/");
- reply = wpas_dbus_simple_property_getter(message,
- DBUS_TYPE_OBJECT_PATH,
- &bss_obj_path);
-
- return reply;
+ return wpas_dbus_simple_property_getter(iter, DBUS_TYPE_OBJECT_PATH,
+ &bss_obj_path, error);
}
/**
* wpas_dbus_getter_current_network - Get current network object path
- * @message: Pointer to incoming dbus message
- * @wpa_s: wpa_supplicant structure for a network interface
- * Returns: A dbus message containing a DBus object path to
- * current network
+ * @iter: Pointer to incoming dbus message iter
+ * @error: Location to store error on failure
+ * @user_data: Function specific data
+ * Returns: TRUE on success, FALSE on failure
*
* Getter for "CurrentNetwork" property.
*/
-DBusMessage * wpas_dbus_getter_current_network(DBusMessage *message,
- struct wpa_supplicant *wpa_s)
+dbus_bool_t wpas_dbus_getter_current_network(DBusMessageIter *iter,
+ DBusError *error,
+ void *user_data)
{
- DBusMessage *reply;
+ struct wpa_supplicant *wpa_s = user_data;
char path_buf[WPAS_DBUS_OBJECT_PATH_MAX], *net_obj_path = path_buf;
if (wpa_s->current_ssid)
@@ -2165,70 +2758,98 @@ DBusMessage * wpas_dbus_getter_current_network(DBusMessage *message,
else
os_snprintf(net_obj_path, WPAS_DBUS_OBJECT_PATH_MAX, "/");
- reply = wpas_dbus_simple_property_getter(message,
- DBUS_TYPE_OBJECT_PATH,
- &net_obj_path);
-
- return reply;
+ return wpas_dbus_simple_property_getter(iter, DBUS_TYPE_OBJECT_PATH,
+ &net_obj_path, error);
}
/**
- * wpas_dbus_getter_bridge_ifname - Get interface name
- * @message: Pointer to incoming dbus message
- * @wpa_s: wpa_supplicant structure for a network interface
- * Returns: A dbus message containing a name of bridge network
- * interface associated with with wpa_s
+ * wpas_dbus_getter_current_auth_mode - Get current authentication type
+ * @iter: Pointer to incoming dbus message iter
+ * @error: Location to store error on failure
+ * @user_data: Function specific data
+ * Returns: TRUE on success, FALSE on failure
*
- * Getter for "BridgeIfname" property.
+ * Getter for "CurrentAuthMode" property.
*/
-DBusMessage * wpas_dbus_getter_bridge_ifname(DBusMessage *message,
- struct wpa_supplicant *wpa_s)
+dbus_bool_t wpas_dbus_getter_current_auth_mode(DBusMessageIter *iter,
+ DBusError *error,
+ void *user_data)
{
- const char *bridge_ifname = NULL;
+ struct wpa_supplicant *wpa_s = user_data;
+ const char *eap_mode;
+ const char *auth_mode;
+ char eap_mode_buf[WPAS_DBUS_AUTH_MODE_MAX];
+
+ if (wpa_s->wpa_state != WPA_COMPLETED) {
+ auth_mode = "INACTIVE";
+ } else if (wpa_s->key_mgmt == WPA_KEY_MGMT_IEEE8021X ||
+ wpa_s->key_mgmt == WPA_KEY_MGMT_IEEE8021X_NO_WPA) {
+ eap_mode = wpa_supplicant_get_eap_mode(wpa_s);
+ os_snprintf(eap_mode_buf, WPAS_DBUS_AUTH_MODE_MAX,
+ "EAP-%s", eap_mode);
+ auth_mode = eap_mode_buf;
- bridge_ifname = wpa_s->bridge_ifname;
- if (bridge_ifname == NULL) {
- wpa_printf(MSG_ERROR, "wpas_dbus_getter_bridge_ifname[dbus]: "
- "wpa_s has no bridge interface name set");
- return wpas_dbus_error_unknown_error(message, NULL);
+ } else {
+ auth_mode = wpa_key_mgmt_txt(wpa_s->key_mgmt,
+ wpa_s->current_ssid->proto);
}
- return wpas_dbus_simple_property_getter(message, DBUS_TYPE_STRING,
- &bridge_ifname);
+ return wpas_dbus_simple_property_getter(iter, DBUS_TYPE_STRING,
+ &auth_mode, error);
+}
+
+
+/**
+ * wpas_dbus_getter_bridge_ifname - Get interface name
+ * @iter: Pointer to incoming dbus message iter
+ * @error: Location to store error on failure
+ * @user_data: Function specific data
+ * Returns: TRUE on success, FALSE on failure
+ *
+ * Getter for "BridgeIfname" property.
+ */
+dbus_bool_t wpas_dbus_getter_bridge_ifname(DBusMessageIter *iter,
+ DBusError *error,
+ void *user_data)
+{
+ struct wpa_supplicant *wpa_s = user_data;
+ const char *bridge_ifname = wpa_s->bridge_ifname;
+ return wpas_dbus_simple_property_getter(iter, DBUS_TYPE_STRING,
+ &bridge_ifname, error);
}
/**
* wpas_dbus_getter_bsss - Get array of BSSs objects
- * @message: Pointer to incoming dbus message
- * @wpa_s: wpa_supplicant structure for a network interface
- * Returns: a dbus message containing an array of all known BSS objects
- * dbus paths
+ * @iter: Pointer to incoming dbus message iter
+ * @error: Location to store error on failure
+ * @user_data: Function specific data
+ * Returns: TRUE on success, FALSE on failure
*
* Getter for "BSSs" property.
*/
-DBusMessage * wpas_dbus_getter_bsss(DBusMessage *message,
- struct wpa_supplicant *wpa_s)
+dbus_bool_t wpas_dbus_getter_bsss(DBusMessageIter *iter, DBusError *error,
+ void *user_data)
{
- DBusMessage *reply = NULL;
+ struct wpa_supplicant *wpa_s = user_data;
struct wpa_bss *bss;
char **paths;
unsigned int i = 0;
+ dbus_bool_t success = FALSE;
- paths = os_zalloc(wpa_s->num_bss * sizeof(char *));
+ paths = os_calloc(wpa_s->num_bss, sizeof(char *));
if (!paths) {
- return dbus_message_new_error(message, DBUS_ERROR_NO_MEMORY,
- NULL);
+ dbus_set_error_const(error, DBUS_ERROR_NO_MEMORY, "no memory");
+ return FALSE;
}
/* Loop through scan results and append each result's object path */
dl_list_for_each(bss, &wpa_s->bss_id, struct wpa_bss, list_id) {
paths[i] = os_zalloc(WPAS_DBUS_OBJECT_PATH_MAX);
if (paths[i] == NULL) {
- reply = dbus_message_new_error(message,
- DBUS_ERROR_NO_MEMORY,
- NULL);
+ dbus_set_error_const(error, DBUS_ERROR_NO_MEMORY,
+ "no memory");
goto out;
}
/* Construct the object path for this BSS. */
@@ -2237,57 +2858,62 @@ DBusMessage * wpas_dbus_getter_bsss(DBusMessage *message,
wpa_s->dbus_new_path, bss->id);
}
- reply = wpas_dbus_simple_array_property_getter(message,
- DBUS_TYPE_OBJECT_PATH,
- paths, wpa_s->num_bss);
+ success = wpas_dbus_simple_array_property_getter(iter,
+ DBUS_TYPE_OBJECT_PATH,
+ paths, wpa_s->num_bss,
+ error);
out:
while (i)
os_free(paths[--i]);
os_free(paths);
- return reply;
+ return success;
}
/**
* wpas_dbus_getter_networks - Get array of networks objects
- * @message: Pointer to incoming dbus message
- * @wpa_s: wpa_supplicant structure for a network interface
- * Returns: a dbus message containing an array of all configured
- * networks dbus object paths.
+ * @iter: Pointer to incoming dbus message iter
+ * @error: Location to store error on failure
+ * @user_data: Function specific data
+ * Returns: TRUE on success, FALSE on failure
*
* Getter for "Networks" property.
*/
-DBusMessage * wpas_dbus_getter_networks(DBusMessage *message,
- struct wpa_supplicant *wpa_s)
+dbus_bool_t wpas_dbus_getter_networks(DBusMessageIter *iter, DBusError *error,
+ void *user_data)
{
- DBusMessage *reply = NULL;
+ struct wpa_supplicant *wpa_s = user_data;
struct wpa_ssid *ssid;
char **paths;
unsigned int i = 0, num = 0;
+ dbus_bool_t success = FALSE;
if (wpa_s->conf == NULL) {
- wpa_printf(MSG_ERROR, "wpas_dbus_getter_networks[dbus]: "
- "An error occurred getting networks list.");
- return wpas_dbus_error_unknown_error(message, NULL);
+ wpa_printf(MSG_ERROR, "%s[dbus]: An error occurred getting "
+ "networks list.", __func__);
+ dbus_set_error(error, DBUS_ERROR_FAILED, "%s: an error "
+ "occurred getting the networks list", __func__);
+ return FALSE;
}
for (ssid = wpa_s->conf->ssid; ssid; ssid = ssid->next)
- num++;
+ if (!network_is_persistent_group(ssid))
+ num++;
- paths = os_zalloc(num * sizeof(char *));
+ paths = os_calloc(num, sizeof(char *));
if (!paths) {
- return dbus_message_new_error(message, DBUS_ERROR_NO_MEMORY,
- NULL);
+ dbus_set_error(error, DBUS_ERROR_NO_MEMORY, "no memory");
+ return FALSE;
}
/* Loop through configured networks and append object path of each */
for (ssid = wpa_s->conf->ssid; ssid; ssid = ssid->next) {
+ if (network_is_persistent_group(ssid))
+ continue;
paths[i] = os_zalloc(WPAS_DBUS_OBJECT_PATH_MAX);
if (paths[i] == NULL) {
- reply = dbus_message_new_error(message,
- DBUS_ERROR_NO_MEMORY,
- NULL);
+ dbus_set_error(error, DBUS_ERROR_NO_MEMORY, "no memory");
goto out;
}
@@ -2297,50 +2923,40 @@ DBusMessage * wpas_dbus_getter_networks(DBusMessage *message,
wpa_s->dbus_new_path, ssid->id);
}
- reply = wpas_dbus_simple_array_property_getter(message,
- DBUS_TYPE_OBJECT_PATH,
- paths, num);
+ success = wpas_dbus_simple_array_property_getter(iter,
+ DBUS_TYPE_OBJECT_PATH,
+ paths, num, error);
out:
while (i)
os_free(paths[--i]);
os_free(paths);
- return reply;
+ return success;
}
/**
* wpas_dbus_getter_blobs - Get all blobs defined for this interface
- * @message: Pointer to incoming dbus message
- * @wpa_s: wpa_supplicant structure for a network interface
- * Returns: a dbus message containing a dictionary of pairs (blob_name, blob)
+ * @iter: Pointer to incoming dbus message iter
+ * @error: Location to store error on failure
+ * @user_data: Function specific data
+ * Returns: TRUE on success, FALSE on failure
*
* Getter for "Blobs" property.
*/
-DBusMessage * wpas_dbus_getter_blobs(DBusMessage *message,
- struct wpa_supplicant *wpa_s)
+dbus_bool_t wpas_dbus_getter_blobs(DBusMessageIter *iter, DBusError *error,
+ void *user_data)
{
- DBusMessage *reply = NULL;
- DBusMessageIter iter, variant_iter, dict_iter, entry_iter, array_iter;
+ struct wpa_supplicant *wpa_s = user_data;
+ DBusMessageIter variant_iter, dict_iter, entry_iter, array_iter;
struct wpa_config_blob *blob;
- if (message == NULL)
- reply = dbus_message_new(DBUS_MESSAGE_TYPE_SIGNAL);
- else
- reply = dbus_message_new_method_return(message);
- if (!reply)
- return dbus_message_new_error(message, DBUS_ERROR_NO_MEMORY,
- NULL);
-
- dbus_message_iter_init_append(reply, &iter);
-
- if (!dbus_message_iter_open_container(&iter, DBUS_TYPE_VARIANT,
+ if (!dbus_message_iter_open_container(iter, DBUS_TYPE_VARIANT,
"a{say}", &variant_iter) ||
!dbus_message_iter_open_container(&variant_iter, DBUS_TYPE_ARRAY,
"{say}", &dict_iter)) {
- dbus_message_unref(reply);
- return dbus_message_new_error(message, DBUS_ERROR_NO_MEMORY,
- NULL);
+ dbus_set_error_const(error, DBUS_ERROR_NO_MEMORY, "no memory");
+ return FALSE;
}
blob = wpa_s->conf->blobs;
@@ -2363,176 +2979,196 @@ DBusMessage * wpas_dbus_getter_blobs(DBusMessage *message,
&array_iter) ||
!dbus_message_iter_close_container(&dict_iter,
&entry_iter)) {
- dbus_message_unref(reply);
- return dbus_message_new_error(message,
- DBUS_ERROR_NO_MEMORY,
- NULL);
+ dbus_set_error_const(error, DBUS_ERROR_NO_MEMORY,
+ "no memory");
+ return FALSE;
}
blob = blob->next;
}
if (!dbus_message_iter_close_container(&variant_iter, &dict_iter) ||
- !dbus_message_iter_close_container(&iter, &variant_iter)) {
- dbus_message_unref(reply);
- return dbus_message_new_error(message, DBUS_ERROR_NO_MEMORY,
- NULL);
+ !dbus_message_iter_close_container(iter, &variant_iter)) {
+ dbus_set_error_const(error, DBUS_ERROR_NO_MEMORY, "no memory");
+ return FALSE;
}
- return reply;
+ return TRUE;
+}
+
+
+static struct wpa_bss * get_bss_helper(struct bss_handler_args *args,
+ DBusError *error, const char *func_name)
+{
+ struct wpa_bss *res = wpa_bss_get_id(args->wpa_s, args->id);
+
+ if (!res) {
+ wpa_printf(MSG_ERROR, "%s[dbus]: no bss with id %d found",
+ func_name, args->id);
+ dbus_set_error(error, DBUS_ERROR_FAILED,
+ "%s: BSS %d not found",
+ func_name, args->id);
+ }
+
+ return res;
}
/**
* wpas_dbus_getter_bss_bssid - Return the BSSID of a BSS
- * @message: Pointer to incoming dbus message
- * @bss: a pair of interface describing structure and bss's id
- * Returns: a dbus message containing the bssid for the requested bss
+ * @iter: Pointer to incoming dbus message iter
+ * @error: Location to store error on failure
+ * @user_data: Function specific data
+ * Returns: TRUE on success, FALSE on failure
*
* Getter for "BSSID" property.
*/
-DBusMessage * wpas_dbus_getter_bss_bssid(DBusMessage *message,
- struct bss_handler_args *bss)
+dbus_bool_t wpas_dbus_getter_bss_bssid(DBusMessageIter *iter, DBusError *error,
+ void *user_data)
{
- struct wpa_bss *res = wpa_bss_get_id(bss->wpa_s, bss->id);
+ struct bss_handler_args *args = user_data;
+ struct wpa_bss *res;
- if (!res) {
- wpa_printf(MSG_ERROR, "wpas_dbus_getter_bss_bssid[dbus]: no "
- "bss with id %d found", bss->id);
- return NULL;
- }
+ res = get_bss_helper(args, error, __func__);
+ if (!res)
+ return FALSE;
- return wpas_dbus_simple_array_property_getter(message, DBUS_TYPE_BYTE,
- res->bssid, ETH_ALEN);
+ return wpas_dbus_simple_array_property_getter(iter, DBUS_TYPE_BYTE,
+ res->bssid, ETH_ALEN,
+ error);
}
/**
* wpas_dbus_getter_bss_ssid - Return the SSID of a BSS
- * @message: Pointer to incoming dbus message
- * @bss: a pair of interface describing structure and bss's id
- * Returns: a dbus message containing the ssid for the requested bss
+ * @iter: Pointer to incoming dbus message iter
+ * @error: Location to store error on failure
+ * @user_data: Function specific data
+ * Returns: TRUE on success, FALSE on failure
*
* Getter for "SSID" property.
*/
-DBusMessage * wpas_dbus_getter_bss_ssid(DBusMessage *message,
- struct bss_handler_args *bss)
+dbus_bool_t wpas_dbus_getter_bss_ssid(DBusMessageIter *iter, DBusError *error,
+ void *user_data)
{
- struct wpa_bss *res = wpa_bss_get_id(bss->wpa_s, bss->id);
+ struct bss_handler_args *args = user_data;
+ struct wpa_bss *res;
- if (!res) {
- wpa_printf(MSG_ERROR, "wpas_dbus_getter_bss_ssid[dbus]: no "
- "bss with id %d found", bss->id);
- return NULL;
- }
+ res = get_bss_helper(args, error, __func__);
+ if (!res)
+ return FALSE;
- return wpas_dbus_simple_array_property_getter(message, DBUS_TYPE_BYTE,
- res->ssid,
- res->ssid_len);
+ return wpas_dbus_simple_array_property_getter(iter, DBUS_TYPE_BYTE,
+ res->ssid, res->ssid_len,
+ error);
}
/**
* wpas_dbus_getter_bss_privacy - Return the privacy flag of a BSS
- * @message: Pointer to incoming dbus message
- * @bss: a pair of interface describing structure and bss's id
- * Returns: a dbus message containing the privacy flag value of requested bss
+ * @iter: Pointer to incoming dbus message iter
+ * @error: Location to store error on failure
+ * @user_data: Function specific data
+ * Returns: TRUE on success, FALSE on failure
*
* Getter for "Privacy" property.
*/
-DBusMessage * wpas_dbus_getter_bss_privacy(DBusMessage *message,
- struct bss_handler_args *bss)
+dbus_bool_t wpas_dbus_getter_bss_privacy(DBusMessageIter *iter,
+ DBusError *error, void *user_data)
{
- struct wpa_bss *res = wpa_bss_get_id(bss->wpa_s, bss->id);
+ struct bss_handler_args *args = user_data;
+ struct wpa_bss *res;
dbus_bool_t privacy;
- if (!res) {
- wpa_printf(MSG_ERROR, "wpas_dbus_getter_bss_privacy[dbus]: no "
- "bss with id %d found", bss->id);
- return NULL;
- }
+ res = get_bss_helper(args, error, __func__);
+ if (!res)
+ return FALSE;
privacy = (res->caps & IEEE80211_CAP_PRIVACY) ? TRUE : FALSE;
- return wpas_dbus_simple_property_getter(message, DBUS_TYPE_BOOLEAN,
- &privacy);
+ return wpas_dbus_simple_property_getter(iter, DBUS_TYPE_BOOLEAN,
+ &privacy, error);
}
/**
* wpas_dbus_getter_bss_mode - Return the mode of a BSS
- * @message: Pointer to incoming dbus message
- * @bss: a pair of interface describing structure and bss's id
- * Returns: a dbus message containing the mode of requested bss
+ * @iter: Pointer to incoming dbus message iter
+ * @error: Location to store error on failure
+ * @user_data: Function specific data
+ * Returns: TRUE on success, FALSE on failure
*
* Getter for "Mode" property.
*/
-DBusMessage * wpas_dbus_getter_bss_mode(DBusMessage *message,
- struct bss_handler_args *bss)
+dbus_bool_t wpas_dbus_getter_bss_mode(DBusMessageIter *iter, DBusError *error,
+ void *user_data)
{
- struct wpa_bss *res = wpa_bss_get_id(bss->wpa_s, bss->id);
+ struct bss_handler_args *args = user_data;
+ struct wpa_bss *res;
const char *mode;
- if (!res) {
- wpa_printf(MSG_ERROR, "wpas_dbus_getter_bss_mode[dbus]: no "
- "bss with id %d found", bss->id);
- return NULL;
- }
+ res = get_bss_helper(args, error, __func__);
+ if (!res)
+ return FALSE;
if (res->caps & IEEE80211_CAP_IBSS)
mode = "ad-hoc";
else
mode = "infrastructure";
- return wpas_dbus_simple_property_getter(message, DBUS_TYPE_STRING,
- &mode);
+ return wpas_dbus_simple_property_getter(iter, DBUS_TYPE_STRING,
+ &mode, error);
}
/**
* wpas_dbus_getter_bss_level - Return the signal strength of a BSS
- * @message: Pointer to incoming dbus message
- * @bss: a pair of interface describing structure and bss's id
- * Returns: a dbus message containing the signal strength of requested bss
+ * @iter: Pointer to incoming dbus message iter
+ * @error: Location to store error on failure
+ * @user_data: Function specific data
+ * Returns: TRUE on success, FALSE on failure
*
* Getter for "Level" property.
*/
-DBusMessage * wpas_dbus_getter_bss_signal(DBusMessage *message,
- struct bss_handler_args *bss)
+dbus_bool_t wpas_dbus_getter_bss_signal(DBusMessageIter *iter,
+ DBusError *error, void *user_data)
{
- struct wpa_bss *res = wpa_bss_get_id(bss->wpa_s, bss->id);
+ struct bss_handler_args *args = user_data;
+ struct wpa_bss *res;
+ s16 level;
- if (!res) {
- wpa_printf(MSG_ERROR, "wpas_dbus_getter_bss_signal[dbus]: no "
- "bss with id %d found", bss->id);
- return NULL;
- }
+ res = get_bss_helper(args, error, __func__);
+ if (!res)
+ return FALSE;
- return wpas_dbus_simple_property_getter(message, DBUS_TYPE_INT16,
- &res->level);
+ level = (s16) res->level;
+ return wpas_dbus_simple_property_getter(iter, DBUS_TYPE_INT16,
+ &level, error);
}
/**
* wpas_dbus_getter_bss_frequency - Return the frequency of a BSS
- * @message: Pointer to incoming dbus message
- * @bss: a pair of interface describing structure and bss's id
- * Returns: a dbus message containing the frequency of requested bss
+ * @iter: Pointer to incoming dbus message iter
+ * @error: Location to store error on failure
+ * @user_data: Function specific data
+ * Returns: TRUE on success, FALSE on failure
*
* Getter for "Frequency" property.
*/
-DBusMessage * wpas_dbus_getter_bss_frequency(DBusMessage *message,
- struct bss_handler_args *bss)
+dbus_bool_t wpas_dbus_getter_bss_frequency(DBusMessageIter *iter,
+ DBusError *error, void *user_data)
{
- struct wpa_bss *res = wpa_bss_get_id(bss->wpa_s, bss->id);
+ struct bss_handler_args *args = user_data;
+ struct wpa_bss *res;
+ u16 freq;
- if (!res) {
- wpa_printf(MSG_ERROR, "wpas_dbus_getter_bss_frequency[dbus]: "
- "no bss with id %d found", bss->id);
- return NULL;
- }
+ res = get_bss_helper(args, error, __func__);
+ if (!res)
+ return FALSE;
- return wpas_dbus_simple_property_getter(message, DBUS_TYPE_UINT16,
- &res->freq);
+ freq = (u16) res->freq;
+ return wpas_dbus_simple_property_getter(iter, DBUS_TYPE_UINT16,
+ &freq, error);
}
@@ -2544,72 +3180,64 @@ static int cmp_u8s_desc(const void *a, const void *b)
/**
* wpas_dbus_getter_bss_rates - Return available bit rates of a BSS
- * @message: Pointer to incoming dbus message
- * @bss: a pair of interface describing structure and bss's id
- * Returns: a dbus message containing sorted array of bit rates
+ * @iter: Pointer to incoming dbus message iter
+ * @error: Location to store error on failure
+ * @user_data: Function specific data
+ * Returns: TRUE on success, FALSE on failure
*
* Getter for "Rates" property.
*/
-DBusMessage * wpas_dbus_getter_bss_rates(DBusMessage *message,
- struct bss_handler_args *bss)
+dbus_bool_t wpas_dbus_getter_bss_rates(DBusMessageIter *iter,
+ DBusError *error, void *user_data)
{
- DBusMessage *reply;
- struct wpa_bss *res = wpa_bss_get_id(bss->wpa_s, bss->id);
+ struct bss_handler_args *args = user_data;
+ struct wpa_bss *res;
u8 *ie_rates = NULL;
u32 *real_rates;
int rates_num, i;
+ dbus_bool_t success = FALSE;
- if (!res) {
- wpa_printf(MSG_ERROR, "wpas_dbus_getter_bss_rates[dbus]: "
- "no bss with id %d found", bss->id);
- return NULL;
- }
+ res = get_bss_helper(args, error, __func__);
+ if (!res)
+ return FALSE;
rates_num = wpa_bss_get_bit_rates(res, &ie_rates);
if (rates_num < 0)
- return NULL;
+ return FALSE;
qsort(ie_rates, rates_num, 1, cmp_u8s_desc);
real_rates = os_malloc(sizeof(u32) * rates_num);
if (!real_rates) {
os_free(ie_rates);
- return dbus_message_new_error(message, DBUS_ERROR_NO_MEMORY,
- NULL);
+ dbus_set_error_const(error, DBUS_ERROR_NO_MEMORY, "no memory");
+ return FALSE;
}
for (i = 0; i < rates_num; i++)
real_rates[i] = ie_rates[i] * 500000;
- reply = wpas_dbus_simple_array_property_getter(message,
- DBUS_TYPE_UINT32,
- real_rates, rates_num);
+ success = wpas_dbus_simple_array_property_getter(iter, DBUS_TYPE_UINT32,
+ real_rates, rates_num,
+ error);
os_free(ie_rates);
os_free(real_rates);
- return reply;
+ return success;
}
-static DBusMessage * wpas_dbus_get_bss_security_prop(
- DBusMessage *message, struct wpa_ie_data *ie_data)
+static dbus_bool_t wpas_dbus_get_bss_security_prop(DBusMessageIter *iter,
+ struct wpa_ie_data *ie_data,
+ DBusError *error)
{
- DBusMessage *reply;
- DBusMessageIter iter, iter_dict, variant_iter;
+ DBusMessageIter iter_dict, variant_iter;
const char *group;
- const char *pairwise[2]; /* max 2 pairwise ciphers is supported */
+ const char *pairwise[3]; /* max 3 pairwise ciphers is supported */
const char *key_mgmt[7]; /* max 7 key managements may be supported */
int n;
- if (message == NULL)
- reply = dbus_message_new(DBUS_MESSAGE_TYPE_SIGNAL);
- else
- reply = dbus_message_new_method_return(message);
- if (!reply)
- goto nomem;
-
- dbus_message_iter_init_append(reply, &iter);
- if (!dbus_message_iter_open_container(&iter, DBUS_TYPE_VARIANT,
+ if (!dbus_message_iter_open_container(iter, DBUS_TYPE_VARIANT,
"a{sv}", &variant_iter))
goto nomem;
@@ -2648,6 +3276,9 @@ static DBusMessage * wpas_dbus_get_bss_security_prop(
case WPA_CIPHER_CCMP:
group = "ccmp";
break;
+ case WPA_CIPHER_GCMP:
+ group = "gcmp";
+ break;
case WPA_CIPHER_WEP104:
group = "wep104";
break;
@@ -2665,6 +3296,8 @@ static DBusMessage * wpas_dbus_get_bss_security_prop(
pairwise[n++] = "tkip";
if (ie_data->pairwise_cipher & WPA_CIPHER_CCMP)
pairwise[n++] = "ccmp";
+ if (ie_data->pairwise_cipher & WPA_CIPHER_GCMP)
+ pairwise[n++] = "gcmp";
if (!wpa_dbus_dict_append_string_array(&iter_dict, "Pairwise",
pairwise, n))
@@ -2690,152 +3323,209 @@ static DBusMessage * wpas_dbus_get_bss_security_prop(
if (!wpa_dbus_dict_close_write(&variant_iter, &iter_dict))
goto nomem;
- if (!dbus_message_iter_close_container(&iter, &variant_iter))
+ if (!dbus_message_iter_close_container(iter, &variant_iter))
goto nomem;
- return reply;
+ return TRUE;
nomem:
- if (reply)
- dbus_message_unref(reply);
-
- return dbus_message_new_error(message, DBUS_ERROR_NO_MEMORY, NULL);
+ dbus_set_error_const(error, DBUS_ERROR_NO_MEMORY, "no memory");
+ return FALSE;
}
/**
* wpas_dbus_getter_bss_wpa - Return the WPA options of a BSS
- * @message: Pointer to incoming dbus message
- * @bss: a pair of interface describing structure and bss's id
- * Returns: a dbus message containing the WPA options of requested bss
+ * @iter: Pointer to incoming dbus message iter
+ * @error: Location to store error on failure
+ * @user_data: Function specific data
+ * Returns: TRUE on success, FALSE on failure
*
* Getter for "WPA" property.
*/
-DBusMessage * wpas_dbus_getter_bss_wpa(DBusMessage *message,
- struct bss_handler_args *bss)
+dbus_bool_t wpas_dbus_getter_bss_wpa(DBusMessageIter *iter, DBusError *error,
+ void *user_data)
{
- struct wpa_bss *res = wpa_bss_get_id(bss->wpa_s, bss->id);
+ struct bss_handler_args *args = user_data;
+ struct wpa_bss *res;
struct wpa_ie_data wpa_data;
const u8 *ie;
- if (!res) {
- wpa_printf(MSG_ERROR, "wpas_dbus_getter_bss_wpa[dbus]: no "
- "bss with id %d found", bss->id);
- return NULL;
- }
+ res = get_bss_helper(args, error, __func__);
+ if (!res)
+ return FALSE;
os_memset(&wpa_data, 0, sizeof(wpa_data));
ie = wpa_bss_get_vendor_ie(res, WPA_IE_VENDOR_TYPE);
if (ie) {
- if (wpa_parse_wpa_ie(ie, 2 + ie[1], &wpa_data) < 0)
- return wpas_dbus_error_unknown_error(message,
- "invalid WPA IE");
+ if (wpa_parse_wpa_ie(ie, 2 + ie[1], &wpa_data) < 0) {
+ dbus_set_error_const(error, DBUS_ERROR_FAILED,
+ "failed to parse WPA IE");
+ return FALSE;
+ }
}
- return wpas_dbus_get_bss_security_prop(message, &wpa_data);
+ return wpas_dbus_get_bss_security_prop(iter, &wpa_data, error);
}
/**
* wpas_dbus_getter_bss_rsn - Return the RSN options of a BSS
- * @message: Pointer to incoming dbus message
- * @bss: a pair of interface describing structure and bss's id
- * Returns: a dbus message containing the RSN options of requested bss
+ * @iter: Pointer to incoming dbus message iter
+ * @error: Location to store error on failure
+ * @user_data: Function specific data
+ * Returns: TRUE on success, FALSE on failure
*
* Getter for "RSN" property.
*/
-DBusMessage * wpas_dbus_getter_bss_rsn(DBusMessage *message,
- struct bss_handler_args *bss)
+dbus_bool_t wpas_dbus_getter_bss_rsn(DBusMessageIter *iter, DBusError *error,
+ void *user_data)
{
- struct wpa_bss *res = wpa_bss_get_id(bss->wpa_s, bss->id);
+ struct bss_handler_args *args = user_data;
+ struct wpa_bss *res;
struct wpa_ie_data wpa_data;
const u8 *ie;
- if (!res) {
- wpa_printf(MSG_ERROR, "wpas_dbus_getter_bss_rsn[dbus]: no "
- "bss with id %d found", bss->id);
- return NULL;
- }
+ res = get_bss_helper(args, error, __func__);
+ if (!res)
+ return FALSE;
os_memset(&wpa_data, 0, sizeof(wpa_data));
ie = wpa_bss_get_ie(res, WLAN_EID_RSN);
if (ie) {
- if (wpa_parse_wpa_ie(ie, 2 + ie[1], &wpa_data) < 0)
- return wpas_dbus_error_unknown_error(message,
- "invalid RSN IE");
+ if (wpa_parse_wpa_ie(ie, 2 + ie[1], &wpa_data) < 0) {
+ dbus_set_error_const(error, DBUS_ERROR_FAILED,
+ "failed to parse RSN IE");
+ return FALSE;
+ }
+ }
+
+ return wpas_dbus_get_bss_security_prop(iter, &wpa_data, error);
+}
+
+
+/**
+ * wpas_dbus_getter_bss_wps - Return the WPS options of a BSS
+ * @iter: Pointer to incoming dbus message iter
+ * @error: Location to store error on failure
+ * @user_data: Function specific data
+ * Returns: TRUE on success, FALSE on failure
+ *
+ * Getter for "WPS" property.
+ */
+dbus_bool_t wpas_dbus_getter_bss_wps(DBusMessageIter *iter, DBusError *error,
+ void *user_data)
+{
+ struct bss_handler_args *args = user_data;
+ struct wpa_bss *res;
+#ifdef CONFIG_WPS
+ struct wpabuf *wps_ie;
+#endif /* CONFIG_WPS */
+ DBusMessageIter iter_dict, variant_iter;
+ const char *type = "";
+
+ res = get_bss_helper(args, error, __func__);
+ if (!res)
+ return FALSE;
+
+ if (!dbus_message_iter_open_container(iter, DBUS_TYPE_VARIANT,
+ "a{sv}", &variant_iter))
+ goto nomem;
+
+ if (!wpa_dbus_dict_open_write(&variant_iter, &iter_dict))
+ goto nomem;
+
+#ifdef CONFIG_WPS
+ wps_ie = wpa_bss_get_vendor_ie_multi(res, WPS_IE_VENDOR_TYPE);
+ if (wps_ie) {
+ if (wps_is_selected_pbc_registrar(wps_ie))
+ type = "pbc";
+ else if (wps_is_selected_pin_registrar(wps_ie))
+ type = "pin";
}
+#endif /* CONFIG_WPS */
+
+ if (!wpa_dbus_dict_append_string(&iter_dict, "Type", type))
+ goto nomem;
+
+ if (!wpa_dbus_dict_close_write(&variant_iter, &iter_dict))
+ goto nomem;
+ if (!dbus_message_iter_close_container(iter, &variant_iter))
+ goto nomem;
+
+ return TRUE;
- return wpas_dbus_get_bss_security_prop(message, &wpa_data);
+nomem:
+ dbus_set_error_const(error, DBUS_ERROR_NO_MEMORY, "no memory");
+ return FALSE;
}
/**
* wpas_dbus_getter_bss_ies - Return all IEs of a BSS
- * @message: Pointer to incoming dbus message
- * @bss: a pair of interface describing structure and bss's id
- * Returns: a dbus message containing IEs byte array
+ * @iter: Pointer to incoming dbus message iter
+ * @error: Location to store error on failure
+ * @user_data: Function specific data
+ * Returns: TRUE on success, FALSE on failure
*
* Getter for "IEs" property.
*/
-DBusMessage * wpas_dbus_getter_bss_ies(DBusMessage *message,
- struct bss_handler_args *bss)
+dbus_bool_t wpas_dbus_getter_bss_ies(DBusMessageIter *iter, DBusError *error,
+ void *user_data)
{
- struct wpa_bss *res = wpa_bss_get_id(bss->wpa_s, bss->id);
+ struct bss_handler_args *args = user_data;
+ struct wpa_bss *res;
- if (!res) {
- wpa_printf(MSG_ERROR, "wpas_dbus_getter_bss_ies[dbus]: no "
- "bss with id %d found", bss->id);
- return NULL;
- }
+ res = get_bss_helper(args, error, __func__);
+ if (!res)
+ return FALSE;
- return wpas_dbus_simple_array_property_getter(message, DBUS_TYPE_BYTE,
- res + 1, res->ie_len);
+ return wpas_dbus_simple_array_property_getter(iter, DBUS_TYPE_BYTE,
+ res + 1, res->ie_len,
+ error);
}
/**
* wpas_dbus_getter_enabled - Check whether network is enabled or disabled
- * @message: Pointer to incoming dbus message
- * @wpas_dbus_setter_enabled: wpa_supplicant structure for a network interface
- * and wpa_ssid structure for a configured network
- * Returns: DBus message with boolean indicating state of configured network
- * or DBus error on failure
+ * @iter: Pointer to incoming dbus message iter
+ * @error: Location to store error on failure
+ * @user_data: Function specific data
+ * Returns: TRUE on success, FALSE on failure
*
* Getter for "enabled" property of a configured network.
*/
-DBusMessage * wpas_dbus_getter_enabled(DBusMessage *message,
- struct network_handler_args *net)
+dbus_bool_t wpas_dbus_getter_enabled(DBusMessageIter *iter, DBusError *error,
+ void *user_data)
{
+ struct network_handler_args *net = user_data;
dbus_bool_t enabled = net->ssid->disabled ? FALSE : TRUE;
- return wpas_dbus_simple_property_getter(message, DBUS_TYPE_BOOLEAN,
- &enabled);
+
+ return wpas_dbus_simple_property_getter(iter, DBUS_TYPE_BOOLEAN,
+ &enabled, error);
}
/**
* wpas_dbus_setter_enabled - Mark a configured network as enabled or disabled
- * @message: Pointer to incoming dbus message
- * @wpas_dbus_setter_enabled: wpa_supplicant structure for a network interface
- * and wpa_ssid structure for a configured network
- * Returns: NULL indicating success or DBus error on failure
+ * @iter: Pointer to incoming dbus message iter
+ * @error: Location to store error on failure
+ * @user_data: Function specific data
+ * Returns: TRUE on success, FALSE on failure
*
* Setter for "Enabled" property of a configured network.
*/
-DBusMessage * wpas_dbus_setter_enabled(DBusMessage *message,
- struct network_handler_args *net)
+dbus_bool_t wpas_dbus_setter_enabled(DBusMessageIter *iter, DBusError *error,
+ void *user_data)
{
- DBusMessage *reply = NULL;
-
+ struct network_handler_args *net = user_data;
struct wpa_supplicant *wpa_s;
struct wpa_ssid *ssid;
-
dbus_bool_t enable;
- reply = wpas_dbus_simple_property_setter(message, DBUS_TYPE_BOOLEAN,
- &enable);
-
- if (reply)
- return reply;
+ if (!wpas_dbus_simple_property_setter(iter, error, DBUS_TYPE_BOOLEAN,
+ &enable))
+ return FALSE;
wpa_s = net->wpa_s;
ssid = net->ssid;
@@ -2845,48 +3535,38 @@ DBusMessage * wpas_dbus_setter_enabled(DBusMessage *message,
else
wpa_supplicant_disable_network(wpa_s, ssid);
- return NULL;
+ return TRUE;
}
/**
* wpas_dbus_getter_network_properties - Get options for a configured network
- * @message: Pointer to incoming dbus message
- * @net: wpa_supplicant structure for a network interface and
- * wpa_ssid structure for a configured network
- * Returns: DBus message with network properties or DBus error on failure
+ * @iter: Pointer to incoming dbus message iter
+ * @error: Location to store error on failure
+ * @user_data: Function specific data
+ * Returns: TRUE on success, FALSE on failure
*
* Getter for "Properties" property of a configured network.
*/
-DBusMessage * wpas_dbus_getter_network_properties(
- DBusMessage *message, struct network_handler_args *net)
+dbus_bool_t wpas_dbus_getter_network_properties(DBusMessageIter *iter,
+ DBusError *error,
+ void *user_data)
{
- DBusMessage *reply = NULL;
- DBusMessageIter iter, variant_iter, dict_iter;
+ struct network_handler_args *net = user_data;
+ DBusMessageIter variant_iter, dict_iter;
char **iterator;
- char **props = wpa_config_get_all(net->ssid, 0);
- if (!props)
- return dbus_message_new_error(message, DBUS_ERROR_NO_MEMORY,
- NULL);
+ char **props = wpa_config_get_all(net->ssid, 1);
+ dbus_bool_t success = FALSE;
- if (message == NULL)
- reply = dbus_message_new(DBUS_MESSAGE_TYPE_SIGNAL);
- else
- reply = dbus_message_new_method_return(message);
- if (!reply) {
- reply = dbus_message_new_error(message, DBUS_ERROR_NO_MEMORY,
- NULL);
- goto out;
+ if (!props) {
+ dbus_set_error_const(error, DBUS_ERROR_NO_MEMORY, "no memory");
+ return FALSE;
}
- dbus_message_iter_init_append(reply, &iter);
-
- if (!dbus_message_iter_open_container(&iter, DBUS_TYPE_VARIANT,
- "a{sv}", &variant_iter) ||
+ if (!dbus_message_iter_open_container(iter, DBUS_TYPE_VARIANT, "a{sv}",
+ &variant_iter) ||
!wpa_dbus_dict_open_write(&variant_iter, &dict_iter)) {
- dbus_message_unref(reply);
- reply = dbus_message_new_error(message, DBUS_ERROR_NO_MEMORY,
- NULL);
+ dbus_set_error_const(error, DBUS_ERROR_NO_MEMORY, "no memory");
goto out;
}
@@ -2894,10 +3574,8 @@ DBusMessage * wpas_dbus_getter_network_properties(
while (*iterator) {
if (!wpa_dbus_dict_append_string(&dict_iter, *iterator,
*(iterator + 1))) {
- dbus_message_unref(reply);
- reply = dbus_message_new_error(message,
- DBUS_ERROR_NO_MEMORY,
- NULL);
+ dbus_set_error_const(error, DBUS_ERROR_NO_MEMORY,
+ "no memory");
goto out;
}
iterator += 2;
@@ -2905,13 +3583,13 @@ DBusMessage * wpas_dbus_getter_network_properties(
if (!wpa_dbus_dict_close_write(&variant_iter, &dict_iter) ||
- !dbus_message_iter_close_container(&iter, &variant_iter)) {
- dbus_message_unref(reply);
- reply = dbus_message_new_error(message, DBUS_ERROR_NO_MEMORY,
- NULL);
+ !dbus_message_iter_close_container(iter, &variant_iter)) {
+ dbus_set_error_const(error, DBUS_ERROR_NO_MEMORY, "no memory");
goto out;
}
+ success = TRUE;
+
out:
iterator = props;
while (*iterator) {
@@ -2919,39 +3597,163 @@ out:
iterator++;
}
os_free(props);
- return reply;
+ return success;
}
/**
* wpas_dbus_setter_network_properties - Set options for a configured network
- * @message: Pointer to incoming dbus message
- * @net: wpa_supplicant structure for a network interface and
- * wpa_ssid structure for a configured network
- * Returns: NULL indicating success or DBus error on failure
+ * @iter: Pointer to incoming dbus message iter
+ * @error: Location to store error on failure
+ * @user_data: Function specific data
+ * Returns: TRUE on success, FALSE on failure
*
* Setter for "Properties" property of a configured network.
*/
-DBusMessage * wpas_dbus_setter_network_properties(
- DBusMessage *message, struct network_handler_args *net)
+dbus_bool_t wpas_dbus_setter_network_properties(DBusMessageIter *iter,
+ DBusError *error,
+ void *user_data)
{
+ struct network_handler_args *net = user_data;
struct wpa_ssid *ssid = net->ssid;
+ DBusMessageIter variant_iter;
- DBusMessage *reply = NULL;
- DBusMessageIter iter, variant_iter;
+ dbus_message_iter_recurse(iter, &variant_iter);
+ return set_network_properties(net->wpa_s, ssid, &variant_iter, error);
+}
- dbus_message_iter_init(message, &iter);
- dbus_message_iter_next(&iter);
- dbus_message_iter_next(&iter);
+#ifdef CONFIG_AP
- dbus_message_iter_recurse(&iter, &variant_iter);
+DBusMessage * wpas_dbus_handler_subscribe_preq(
+ DBusMessage *message, struct wpa_supplicant *wpa_s)
+{
+ struct wpas_dbus_priv *priv = wpa_s->global->dbus;
+ char *name;
- reply = set_network_properties(message, net->wpa_s, ssid,
- &variant_iter);
- if (reply)
- wpa_printf(MSG_DEBUG, "dbus control interface couldn't set "
- "network properties");
+ if (wpa_s->preq_notify_peer != NULL) {
+ if (os_strcmp(dbus_message_get_sender(message),
+ wpa_s->preq_notify_peer) == 0)
+ return NULL;
- return reply;
+ return dbus_message_new_error(message,
+ WPAS_DBUS_ERROR_SUBSCRIPTION_IN_USE,
+ "Another application is already subscribed");
+ }
+
+ name = os_strdup(dbus_message_get_sender(message));
+ if (!name)
+ return dbus_message_new_error(message, DBUS_ERROR_NO_MEMORY,
+ "out of memory");
+
+ wpa_s->preq_notify_peer = name;
+
+ /* Subscribe to clean up if application closes socket */
+ wpas_dbus_subscribe_noc(priv);
+
+ /*
+ * Double-check it's still alive to make sure that we didn't
+ * miss the NameOwnerChanged signal, e.g. while strdup'ing.
+ */
+ if (!dbus_bus_name_has_owner(priv->con, name, NULL)) {
+ /*
+ * Application no longer exists, clean up.
+ * The return value is irrelevant now.
+ *
+ * Need to check if the NameOwnerChanged handling
+ * already cleaned up because we have processed
+ * DBus messages while checking if the name still
+ * has an owner.
+ */
+ if (!wpa_s->preq_notify_peer)
+ return NULL;
+ os_free(wpa_s->preq_notify_peer);
+ wpa_s->preq_notify_peer = NULL;
+ wpas_dbus_unsubscribe_noc(priv);
+ }
+
+ return NULL;
+}
+
+
+DBusMessage * wpas_dbus_handler_unsubscribe_preq(
+ DBusMessage *message, struct wpa_supplicant *wpa_s)
+{
+ struct wpas_dbus_priv *priv = wpa_s->global->dbus;
+
+ if (!wpa_s->preq_notify_peer)
+ return dbus_message_new_error(message,
+ WPAS_DBUS_ERROR_NO_SUBSCRIPTION,
+ "Not subscribed");
+
+ if (os_strcmp(wpa_s->preq_notify_peer,
+ dbus_message_get_sender(message)))
+ return dbus_message_new_error(message,
+ WPAS_DBUS_ERROR_SUBSCRIPTION_EPERM,
+ "Can't unsubscribe others");
+
+ os_free(wpa_s->preq_notify_peer);
+ wpa_s->preq_notify_peer = NULL;
+ wpas_dbus_unsubscribe_noc(priv);
+ return NULL;
}
+
+
+void wpas_dbus_signal_preq(struct wpa_supplicant *wpa_s,
+ const u8 *addr, const u8 *dst, const u8 *bssid,
+ const u8 *ie, size_t ie_len, u32 ssi_signal)
+{
+ DBusMessage *msg;
+ DBusMessageIter iter, dict_iter;
+ struct wpas_dbus_priv *priv = wpa_s->global->dbus;
+
+ /* Do nothing if the control interface is not turned on */
+ if (priv == NULL)
+ return;
+
+ if (wpa_s->preq_notify_peer == NULL)
+ return;
+
+ msg = dbus_message_new_signal(wpa_s->dbus_new_path,
+ WPAS_DBUS_NEW_IFACE_INTERFACE,
+ "ProbeRequest");
+ if (msg == NULL)
+ return;
+
+ dbus_message_set_destination(msg, wpa_s->preq_notify_peer);
+
+ dbus_message_iter_init_append(msg, &iter);
+
+ if (!wpa_dbus_dict_open_write(&iter, &dict_iter))
+ goto fail;
+ if (addr && !wpa_dbus_dict_append_byte_array(&dict_iter, "addr",
+ (const char *) addr,
+ ETH_ALEN))
+ goto fail;
+ if (dst && !wpa_dbus_dict_append_byte_array(&dict_iter, "dst",
+ (const char *) dst,
+ ETH_ALEN))
+ goto fail;
+ if (bssid && !wpa_dbus_dict_append_byte_array(&dict_iter, "bssid",
+ (const char *) bssid,
+ ETH_ALEN))
+ goto fail;
+ if (ie && ie_len && !wpa_dbus_dict_append_byte_array(&dict_iter, "ies",
+ (const char *) ie,
+ ie_len))
+ goto fail;
+ if (ssi_signal && !wpa_dbus_dict_append_int32(&dict_iter, "signal",
+ ssi_signal))
+ goto fail;
+ if (!wpa_dbus_dict_close_write(&iter, &dict_iter))
+ goto fail;
+
+ dbus_connection_send(priv->con, msg, NULL);
+ goto out;
+fail:
+ wpa_printf(MSG_ERROR, "dbus: Failed to construct signal");
+out:
+ dbus_message_unref(msg);
+}
+
+#endif /* CONFIG_AP */
diff --git a/contrib/wpa/wpa_supplicant/dbus/dbus_new_handlers.h b/contrib/wpa/wpa_supplicant/dbus/dbus_new_handlers.h
index 3cdf9cb..aa56550 100644
--- a/contrib/wpa/wpa_supplicant/dbus/dbus_new_handlers.h
+++ b/contrib/wpa/wpa_supplicant/dbus/dbus_new_handlers.h
@@ -3,14 +3,8 @@
* Copyright (c) 2006, Dan Williams <dcbw@redhat.com> and Red Hat, Inc.
* Copyright (c) 2009-2010, Witold Sowa <witold.sowa@gmail.com>
*
- * This program is free software; you can redistribute it and/or modify
- * it under the terms of the GNU General Public License version 2 as
- * published by the Free Software Foundation.
- *
- * Alternatively, this software may be distributed under the terms of BSD
- * license.
- *
- * See README and COPYING for more details.
+ * This software may be distributed under the terms of the BSD license.
+ * See README for more details.
*/
#ifndef CTRL_IFACE_DBUS_NEW_HANDLERS_H
@@ -26,17 +20,26 @@ struct bss_handler_args {
unsigned int id;
};
-DBusMessage * wpas_dbus_simple_property_getter(DBusMessage *message,
- const int type,
- const void *val);
+dbus_bool_t wpas_dbus_simple_property_getter(DBusMessageIter *iter,
+ const int type,
+ const void *val,
+ DBusError *error);
+
+dbus_bool_t wpas_dbus_simple_property_setter(DBusMessageIter *iter,
+ DBusError *error,
+ const int type, void *val);
-DBusMessage * wpas_dbus_simple_property_setter(DBusMessage *message,
- const int type, void *val);
+dbus_bool_t wpas_dbus_simple_array_property_getter(DBusMessageIter *iter,
+ const int type,
+ const void *array,
+ size_t array_len,
+ DBusError *error);
-DBusMessage * wpas_dbus_simple_array_property_getter(DBusMessage *message,
- const int type,
- const void *array,
- size_t array_len);
+dbus_bool_t wpas_dbus_simple_array_array_property_getter(DBusMessageIter *iter,
+ const int type,
+ struct wpabuf **array,
+ size_t array_len,
+ DBusError *error);
DBusMessage * wpas_dbus_handler_create_interface(DBusMessage *message,
struct wpa_global *global);
@@ -47,29 +50,39 @@ DBusMessage * wpas_dbus_handler_remove_interface(DBusMessage *message,
DBusMessage * wpas_dbus_handler_get_interface(DBusMessage *message,
struct wpa_global *global);
-DBusMessage * wpas_dbus_getter_debug_level(DBusMessage *message,
- struct wpa_global *global);
+dbus_bool_t wpas_dbus_getter_debug_level(DBusMessageIter *iter,
+ DBusError *error,
+ void *user_data);
-DBusMessage * wpas_dbus_getter_debug_timestamp(DBusMessage *message,
- struct wpa_global *global);
+dbus_bool_t wpas_dbus_getter_debug_timestamp(DBusMessageIter *iter,
+ DBusError *error,
+ void *user_data);
-DBusMessage * wpas_dbus_getter_debug_show_keys(DBusMessage *message,
- struct wpa_global *global);
+dbus_bool_t wpas_dbus_getter_debug_show_keys(DBusMessageIter *iter,
+ DBusError *error,
+ void *user_data);
-DBusMessage * wpas_dbus_setter_debug_level(DBusMessage *message,
- struct wpa_global *global);
+dbus_bool_t wpas_dbus_setter_debug_level(DBusMessageIter *iter,
+ DBusError *error, void *user_data);
-DBusMessage * wpas_dbus_setter_debug_timestamp(DBusMessage *message,
- struct wpa_global *global);
+dbus_bool_t wpas_dbus_setter_debug_timestamp(DBusMessageIter *iter,
+ DBusError *error,
+ void *user_data);
-DBusMessage * wpas_dbus_setter_debug_show_keys(DBusMessage *message,
- struct wpa_global *global);
+dbus_bool_t wpas_dbus_setter_debug_show_keys(DBusMessageIter *iter,
+ DBusError *error,
+ void *user_data);
-DBusMessage * wpas_dbus_getter_interfaces(DBusMessage *message,
- struct wpa_global *global);
+dbus_bool_t wpas_dbus_getter_interfaces(DBusMessageIter *iter,
+ DBusError *error,
+ void *user_data);
-DBusMessage * wpas_dbus_getter_eap_methods(DBusMessage *message,
- void *nothing);
+dbus_bool_t wpas_dbus_getter_eap_methods(DBusMessageIter *iter,
+ DBusError *error, void *user_data);
+
+dbus_bool_t wpas_dbus_getter_global_capabilities(DBusMessageIter *iter,
+ DBusError *error,
+ void *user_data);
DBusMessage * wpas_dbus_handler_scan(DBusMessage *message,
struct wpa_supplicant *wpa_s);
@@ -77,15 +90,29 @@ DBusMessage * wpas_dbus_handler_scan(DBusMessage *message,
DBusMessage * wpas_dbus_handler_disconnect(DBusMessage *message,
struct wpa_supplicant *wpa_s);
+dbus_bool_t set_network_properties(struct wpa_supplicant *wpa_s,
+ struct wpa_ssid *ssid,
+ DBusMessageIter *iter,
+ DBusError *error);
+
DBusMessage * wpas_dbus_handler_add_network(DBusMessage *message,
struct wpa_supplicant *wpa_s);
+DBusMessage * wpas_dbus_handler_reassociate(DBusMessage *message,
+ struct wpa_supplicant *wpa_s);
+
DBusMessage * wpas_dbus_handler_remove_network(DBusMessage *message,
struct wpa_supplicant *wpa_s);
+DBusMessage * wpas_dbus_handler_remove_all_networks(
+ DBusMessage *message, struct wpa_supplicant *wpa_s);
+
DBusMessage * wpas_dbus_handler_select_network(DBusMessage *message,
struct wpa_supplicant *wpa_s);
+DBusMessage * wpas_dbus_handler_network_reply(DBusMessage *message,
+ struct wpa_supplicant *wpa_s);
+
DBusMessage * wpas_dbus_handler_add_blob(DBusMessage *message,
struct wpa_supplicant *wpa_s);
@@ -95,102 +122,164 @@ DBusMessage * wpas_dbus_handler_get_blob(DBusMessage *message,
DBusMessage * wpas_dbus_handler_remove_blob(DBusMessage *message,
struct wpa_supplicant *wpa_s);
-DBusMessage * wpas_dbus_getter_capabilities(DBusMessage *message,
- struct wpa_supplicant *wpa_s);
+DBusMessage * wpas_dbus_handler_flush_bss(DBusMessage *message,
+ struct wpa_supplicant *wpa_s);
-DBusMessage * wpas_dbus_getter_state(DBusMessage *message,
- struct wpa_supplicant *wpa_s);
+DBusMessage * wpas_dbus_handler_autoscan(DBusMessage *message,
+ struct wpa_supplicant *wpa_s);
-DBusMessage * wpas_dbus_getter_scanning(DBusMessage *message,
- struct wpa_supplicant *wpa_s);
+dbus_bool_t wpas_dbus_getter_capabilities(DBusMessageIter *iter,
+ DBusError *error, void *user_data);
-DBusMessage * wpas_dbus_getter_ap_scan(DBusMessage *message,
- struct wpa_supplicant *wpa_s);
+dbus_bool_t wpas_dbus_getter_state(DBusMessageIter *iter, DBusError *error,
+ void *user_data);
-DBusMessage * wpas_dbus_setter_ap_scan(DBusMessage *message,
- struct wpa_supplicant *wpa_s);
+dbus_bool_t wpas_dbus_getter_scanning(DBusMessageIter *iter, DBusError *error,
+ void *user_data);
-DBusMessage * wpas_dbus_getter_ifname(DBusMessage *message,
- struct wpa_supplicant *wpa_s);
+dbus_bool_t wpas_dbus_getter_ap_scan(DBusMessageIter *iter, DBusError *error,
+ void *user_data);
-DBusMessage * wpas_dbus_getter_driver(DBusMessage *message,
- struct wpa_supplicant *wpa_s);
+dbus_bool_t wpas_dbus_setter_ap_scan(DBusMessageIter *iter, DBusError *error,
+ void *user_data);
-DBusMessage * wpas_dbus_getter_bridge_ifname(DBusMessage *message,
- struct wpa_supplicant *wpa_s);
+dbus_bool_t wpas_dbus_getter_fast_reauth(DBusMessageIter *iter,
+ DBusError *error,
+ void *user_data);
-DBusMessage * wpas_dbus_getter_current_bss(DBusMessage *message,
- struct wpa_supplicant *wpa_s);
+dbus_bool_t wpas_dbus_setter_fast_reauth(DBusMessageIter *iter,
+ DBusError *error,
+ void *user_data);
-DBusMessage * wpas_dbus_getter_current_network(DBusMessage *message,
- struct wpa_supplicant *wpa_s);
+dbus_bool_t wpas_dbus_getter_disconnect_reason(DBusMessageIter *iter,
+ DBusError *error,
+ void *user_data);
+
+dbus_bool_t wpas_dbus_getter_bss_expire_age(DBusMessageIter *iter,
+ DBusError *error, void *user_data);
+
+dbus_bool_t wpas_dbus_setter_bss_expire_age(DBusMessageIter *iter,
+ DBusError *error,
+ void *user_data);
+
+dbus_bool_t wpas_dbus_getter_bss_expire_count(DBusMessageIter *iter,
+ DBusError *error,
+ void *user_data);
-DBusMessage * wpas_dbus_getter_bsss(DBusMessage *message,
- struct wpa_supplicant *wpa_s);
+dbus_bool_t wpas_dbus_setter_bss_expire_count(DBusMessageIter *iter,
+ DBusError *error,
+ void *user_data);
-DBusMessage * wpas_dbus_getter_networks(DBusMessage *message,
- struct wpa_supplicant *wpa_s);
+dbus_bool_t wpas_dbus_getter_country(DBusMessageIter *iter, DBusError *error,
+ void *user_data);
-DBusMessage * wpas_dbus_getter_blobs(DBusMessage *message,
- struct wpa_supplicant *bss);
+dbus_bool_t wpas_dbus_setter_country(DBusMessageIter *iter, DBusError *error,
+ void *user_data);
-DBusMessage * wpas_dbus_getter_bss_bssid(DBusMessage *message,
- struct bss_handler_args *bss);
+dbus_bool_t wpas_dbus_getter_scan_interval(DBusMessageIter *iter,
+ DBusError *error,
+ void *user_data);
-DBusMessage * wpas_dbus_getter_bss_ssid(DBusMessage *message,
- struct bss_handler_args *bss);
+dbus_bool_t wpas_dbus_setter_scan_interval(DBusMessageIter *iter,
+ DBusError *error,
+ void *user_data);
-DBusMessage * wpas_dbus_getter_bss_privacy(DBusMessage *message,
- struct bss_handler_args *bss);
+dbus_bool_t wpas_dbus_getter_ifname(DBusMessageIter *iter, DBusError *error,
+ void *user_data);
-DBusMessage * wpas_dbus_getter_bss_mode(DBusMessage *message,
- struct bss_handler_args *bss);
+dbus_bool_t wpas_dbus_getter_driver(DBusMessageIter *iter, DBusError *error,
+ void *user_data);
-DBusMessage * wpas_dbus_getter_bss_signal(DBusMessage *message,
- struct bss_handler_args *bss);
+dbus_bool_t wpas_dbus_getter_bridge_ifname(DBusMessageIter *iter,
+ DBusError *error,
+ void *user_data);
-DBusMessage * wpas_dbus_getter_bss_frequency(DBusMessage *message,
- struct bss_handler_args *bss);
+dbus_bool_t wpas_dbus_getter_current_bss(DBusMessageIter *iter,
+ DBusError *error,
+ void *user_data);
-DBusMessage * wpas_dbus_getter_bss_rates(DBusMessage *message,
- struct bss_handler_args *bss);
+dbus_bool_t wpas_dbus_getter_current_network(DBusMessageIter *iter,
+ DBusError *error,
+ void *user_data);
-DBusMessage * wpas_dbus_getter_bss_wpa(DBusMessage *message,
- struct bss_handler_args *bss);
+dbus_bool_t wpas_dbus_getter_current_auth_mode(DBusMessageIter *iter,
+ DBusError *error,
+ void *user_data);
-DBusMessage * wpas_dbus_getter_bss_rsn(DBusMessage *message,
- struct bss_handler_args *bss);
+dbus_bool_t wpas_dbus_getter_bsss(DBusMessageIter *iter, DBusError *error,
+ void *user_data);
-DBusMessage * wpas_dbus_getter_bss_ies(DBusMessage *message,
- struct bss_handler_args *bss);
+dbus_bool_t wpas_dbus_getter_networks(DBusMessageIter *iter, DBusError *error,
+ void *user_data);
-DBusMessage * wpas_dbus_getter_enabled(DBusMessage *message,
- struct network_handler_args *net);
+dbus_bool_t wpas_dbus_getter_blobs(DBusMessageIter *iter, DBusError *error,
+ void *user_data);
-DBusMessage * wpas_dbus_setter_enabled(DBusMessage *message,
- struct network_handler_args *net);
+dbus_bool_t wpas_dbus_getter_bss_bssid(DBusMessageIter *iter, DBusError *error,
+ void *user_data);
-DBusMessage * wpas_dbus_getter_network_properties(
- DBusMessage *message, struct network_handler_args *net);
+dbus_bool_t wpas_dbus_getter_bss_ssid(DBusMessageIter *iter, DBusError *error,
+ void *user_data);
-DBusMessage * wpas_dbus_setter_network_properties(
- DBusMessage *message, struct network_handler_args *net);
+dbus_bool_t wpas_dbus_getter_bss_privacy(DBusMessageIter *iter,
+ DBusError *error, void *user_data);
+
+dbus_bool_t wpas_dbus_getter_bss_mode(DBusMessageIter *iter, DBusError *error,
+ void *user_data);
+
+dbus_bool_t wpas_dbus_getter_bss_signal(DBusMessageIter *iter,
+ DBusError *error, void *user_data);
+
+dbus_bool_t wpas_dbus_getter_bss_frequency(DBusMessageIter *iter,
+ DBusError *error, void *user_data);
+
+dbus_bool_t wpas_dbus_getter_bss_rates(DBusMessageIter *iter,
+ DBusError *error, void *user_data);
+
+dbus_bool_t wpas_dbus_getter_bss_wpa(DBusMessageIter *iter, DBusError *error,
+ void *user_data);
+
+dbus_bool_t wpas_dbus_getter_bss_rsn(DBusMessageIter *iter, DBusError *error,
+ void *user_data);
+
+dbus_bool_t wpas_dbus_getter_bss_wps(DBusMessageIter *iter, DBusError *error,
+ void *user_data);
+
+dbus_bool_t wpas_dbus_getter_bss_ies(DBusMessageIter *iter, DBusError *error,
+ void *user_data);
+
+dbus_bool_t wpas_dbus_getter_enabled(DBusMessageIter *iter, DBusError *error,
+ void *user_data);
+
+dbus_bool_t wpas_dbus_setter_enabled(DBusMessageIter *iter, DBusError *error,
+ void *user_data);
+
+dbus_bool_t wpas_dbus_getter_network_properties(DBusMessageIter *iter,
+ DBusError *error,
+ void *user_data);
+
+dbus_bool_t wpas_dbus_setter_network_properties(DBusMessageIter *iter,
+ DBusError *error,
+ void *user_data);
DBusMessage * wpas_dbus_handler_wps_start(DBusMessage *message,
struct wpa_supplicant *wpa_s);
-DBusMessage * wpas_dbus_getter_process_credentials(
- DBusMessage *message, struct wpa_supplicant *wpa_s);
+dbus_bool_t wpas_dbus_getter_process_credentials(DBusMessageIter *iter,
+ DBusError *error, void *user_data);
-DBusMessage * wpas_dbus_setter_process_credentials(
- DBusMessage *message, struct wpa_supplicant *wpa_s);
-
-DBusMessage * wpas_dbus_getter_credentials(DBusMessage *message,
- struct wpa_supplicant *wpa_s);
+dbus_bool_t wpas_dbus_setter_process_credentials(DBusMessageIter *iter,
+ DBusError *error,
+ void *user_data);
DBusMessage * wpas_dbus_error_invalid_args(DBusMessage *message,
const char *arg);
DBusMessage * wpas_dbus_error_unknown_error(DBusMessage *message,
const char *arg);
+DBusMessage * wpas_dbus_handler_subscribe_preq(
+ DBusMessage *message, struct wpa_supplicant *wpa_s);
+DBusMessage * wpas_dbus_handler_unsubscribe_preq(
+ DBusMessage *message, struct wpa_supplicant *wpa_s);
+
#endif /* CTRL_IFACE_DBUS_HANDLERS_NEW_H */
diff --git a/contrib/wpa/wpa_supplicant/dbus/dbus_new_handlers_p2p.c b/contrib/wpa/wpa_supplicant/dbus/dbus_new_handlers_p2p.c
new file mode 100644
index 0000000..30e0eb3
--- /dev/null
+++ b/contrib/wpa/wpa_supplicant/dbus/dbus_new_handlers_p2p.c
@@ -0,0 +1,2438 @@
+/*
+ * WPA Supplicant / dbus-based control interface (P2P)
+ * Copyright (c) 2011-2012, Intel Corporation
+ *
+ * This software may be distributed under the terms of the BSD license.
+ * See README for more details.
+ */
+
+#include "includes.h"
+
+#include "utils/includes.h"
+#include "common.h"
+#include "../config.h"
+#include "../wpa_supplicant_i.h"
+#include "../wps_supplicant.h"
+#include "../notify.h"
+#include "dbus_new_helpers.h"
+#include "dbus_new.h"
+#include "dbus_new_handlers.h"
+#include "dbus_new_handlers_p2p.h"
+#include "dbus_dict_helpers.h"
+#include "p2p/p2p.h"
+#include "common/ieee802_11_defs.h"
+#include "ap/hostapd.h"
+#include "ap/ap_config.h"
+#include "ap/wps_hostapd.h"
+
+#include "../p2p_supplicant.h"
+
+/**
+ * Parses out the mac address from the peer object path.
+ * @peer_path - object path of the form
+ * /fi/w1/wpa_supplicant1/Interfaces/n/Peers/00112233445566 (no colons)
+ * @addr - out param must be of ETH_ALEN size
+ * Returns 0 if valid (including MAC), -1 otherwise
+ */
+static int parse_peer_object_path(char *peer_path, u8 addr[ETH_ALEN])
+{
+ char *p;
+
+ if (!peer_path)
+ return -1;
+ p = os_strrchr(peer_path, '/');
+ if (!p)
+ return -1;
+ p++;
+ return hwaddr_compact_aton(p, addr);
+}
+
+
+/**
+ * wpas_dbus_error_persistent_group_unknown - Return a new PersistentGroupUnknown
+ * error message
+ * @message: Pointer to incoming dbus message this error refers to
+ * Returns: a dbus error message
+ *
+ * Convenience function to create and return an invalid persistent group error.
+ */
+static DBusMessage * wpas_dbus_error_persistent_group_unknown(
+ DBusMessage *message)
+{
+ return dbus_message_new_error(message, WPAS_DBUS_ERROR_NETWORK_UNKNOWN,
+ "There is no such persistent group in "
+ "this P2P device.");
+}
+
+
+DBusMessage * wpas_dbus_handler_p2p_find(DBusMessage *message,
+ struct wpa_supplicant *wpa_s)
+{
+ struct wpa_dbus_dict_entry entry;
+ DBusMessage *reply = NULL;
+ DBusMessageIter iter;
+ DBusMessageIter iter_dict;
+ unsigned int timeout = 0;
+ enum p2p_discovery_type type = P2P_FIND_ONLY_SOCIAL;
+ int num_req_dev_types = 0;
+ unsigned int i;
+ u8 *req_dev_types = NULL;
+
+ dbus_message_iter_init(message, &iter);
+ entry.key = NULL;
+
+ if (!wpa_dbus_dict_open_read(&iter, &iter_dict, NULL))
+ goto error;
+
+ while (wpa_dbus_dict_has_dict_entry(&iter_dict)) {
+ if (!wpa_dbus_dict_get_entry(&iter_dict, &entry))
+ goto error;
+
+ if (!os_strcmp(entry.key, "Timeout") &&
+ (entry.type == DBUS_TYPE_INT32)) {
+ timeout = entry.uint32_value;
+ } else if (os_strcmp(entry.key, "RequestedDeviceTypes") == 0) {
+ if ((entry.type != DBUS_TYPE_ARRAY) ||
+ (entry.array_type != WPAS_DBUS_TYPE_BINARRAY))
+ goto error_clear;
+
+ os_free(req_dev_types);
+ req_dev_types =
+ os_malloc(WPS_DEV_TYPE_LEN * entry.array_len);
+ if (!req_dev_types)
+ goto error_clear;
+
+ for (i = 0; i < entry.array_len; i++) {
+ if (wpabuf_len(entry.binarray_value[i]) !=
+ WPS_DEV_TYPE_LEN)
+ goto error_clear;
+ os_memcpy(req_dev_types + i * WPS_DEV_TYPE_LEN,
+ wpabuf_head(entry.binarray_value[i]),
+ WPS_DEV_TYPE_LEN);
+ }
+ num_req_dev_types = entry.array_len;
+ } else if (!os_strcmp(entry.key, "DiscoveryType") &&
+ (entry.type == DBUS_TYPE_STRING)) {
+ if (!os_strcmp(entry.str_value, "start_with_full"))
+ type = P2P_FIND_START_WITH_FULL;
+ else if (!os_strcmp(entry.str_value, "social"))
+ type = P2P_FIND_ONLY_SOCIAL;
+ else if (!os_strcmp(entry.str_value, "progressive"))
+ type = P2P_FIND_PROGRESSIVE;
+ else
+ goto error_clear;
+ } else
+ goto error_clear;
+ wpa_dbus_dict_entry_clear(&entry);
+ }
+
+ wpas_p2p_find(wpa_s, timeout, type, num_req_dev_types, req_dev_types,
+ NULL, 0);
+ os_free(req_dev_types);
+ return reply;
+
+error_clear:
+ wpa_dbus_dict_entry_clear(&entry);
+error:
+ os_free(req_dev_types);
+ reply = wpas_dbus_error_invalid_args(message, entry.key);
+ return reply;
+}
+
+
+DBusMessage * wpas_dbus_handler_p2p_stop_find(DBusMessage *message,
+ struct wpa_supplicant *wpa_s)
+{
+ wpas_p2p_stop_find(wpa_s);
+ return NULL;
+}
+
+
+DBusMessage * wpas_dbus_handler_p2p_rejectpeer(DBusMessage *message,
+ struct wpa_supplicant *wpa_s)
+{
+ DBusMessageIter iter;
+ char *peer_object_path = NULL;
+ u8 peer_addr[ETH_ALEN];
+
+ dbus_message_iter_init(message, &iter);
+ dbus_message_iter_get_basic(&iter, &peer_object_path);
+
+ if (parse_peer_object_path(peer_object_path, peer_addr) < 0)
+ return wpas_dbus_error_invalid_args(message, NULL);
+
+ if (wpas_p2p_reject(wpa_s, peer_addr) < 0)
+ return wpas_dbus_error_unknown_error(message,
+ "Failed to call wpas_p2p_reject method.");
+
+ return NULL;
+}
+
+
+DBusMessage * wpas_dbus_handler_p2p_listen(DBusMessage *message,
+ struct wpa_supplicant *wpa_s)
+{
+ dbus_int32_t timeout = 0;
+
+ if (!dbus_message_get_args(message, NULL, DBUS_TYPE_INT32, &timeout,
+ DBUS_TYPE_INVALID))
+ return dbus_message_new_error(message, DBUS_ERROR_NO_MEMORY,
+ NULL);
+
+ if (wpas_p2p_listen(wpa_s, (unsigned int)timeout))
+ return dbus_message_new_error(message, DBUS_ERROR_NO_MEMORY,
+ NULL);
+
+ return NULL;
+}
+
+
+DBusMessage * wpas_dbus_handler_p2p_extendedlisten(
+ DBusMessage *message, struct wpa_supplicant *wpa_s)
+{
+ unsigned int period = 0, interval = 0;
+ struct wpa_dbus_dict_entry entry;
+ DBusMessageIter iter;
+ DBusMessageIter iter_dict;
+
+ dbus_message_iter_init(message, &iter);
+ entry.key = NULL;
+
+ if (!wpa_dbus_dict_open_read(&iter, &iter_dict, NULL))
+ goto error;
+
+ while (wpa_dbus_dict_has_dict_entry(&iter_dict)) {
+ if (!wpa_dbus_dict_get_entry(&iter_dict, &entry))
+ goto error;
+
+ if (!os_strcmp(entry.key, "period") &&
+ (entry.type == DBUS_TYPE_INT32))
+ period = entry.uint32_value;
+ else if (!os_strcmp(entry.key, "interval") &&
+ (entry.type == DBUS_TYPE_INT32))
+ interval = entry.uint32_value;
+ else
+ goto error_clear;
+ wpa_dbus_dict_entry_clear(&entry);
+ }
+
+ if (wpas_p2p_ext_listen(wpa_s, period, interval))
+ return wpas_dbus_error_unknown_error(
+ message, "failed to initiate a p2p_ext_listen.");
+
+ return NULL;
+
+error_clear:
+ wpa_dbus_dict_entry_clear(&entry);
+error:
+ return wpas_dbus_error_invalid_args(message, entry.key);
+}
+
+
+DBusMessage * wpas_dbus_handler_p2p_presence_request(
+ DBusMessage *message, struct wpa_supplicant *wpa_s)
+{
+ unsigned int dur1 = 0, int1 = 0, dur2 = 0, int2 = 0;
+ struct wpa_dbus_dict_entry entry;
+ DBusMessageIter iter;
+ DBusMessageIter iter_dict;
+
+ dbus_message_iter_init(message, &iter);
+ entry.key = NULL;
+
+ if (!wpa_dbus_dict_open_read(&iter, &iter_dict, NULL))
+ goto error;
+
+ while (wpa_dbus_dict_has_dict_entry(&iter_dict)) {
+ if (!wpa_dbus_dict_get_entry(&iter_dict, &entry))
+ goto error;
+
+ if (!os_strcmp(entry.key, "duration1") &&
+ (entry.type == DBUS_TYPE_INT32))
+ dur1 = entry.uint32_value;
+ else if (!os_strcmp(entry.key, "interval1") &&
+ entry.type == DBUS_TYPE_INT32)
+ int1 = entry.uint32_value;
+ else if (!os_strcmp(entry.key, "duration2") &&
+ entry.type == DBUS_TYPE_INT32)
+ dur2 = entry.uint32_value;
+ else if (!os_strcmp(entry.key, "interval2") &&
+ entry.type == DBUS_TYPE_INT32)
+ int2 = entry.uint32_value;
+ else
+ goto error_clear;
+
+ wpa_dbus_dict_entry_clear(&entry);
+ }
+ if (wpas_p2p_presence_req(wpa_s, dur1, int1, dur2, int2) < 0)
+ return wpas_dbus_error_unknown_error(message,
+ "Failed to invoke presence request.");
+
+ return NULL;
+
+error_clear:
+ wpa_dbus_dict_entry_clear(&entry);
+error:
+ return wpas_dbus_error_invalid_args(message, entry.key);
+}
+
+
+DBusMessage * wpas_dbus_handler_p2p_group_add(DBusMessage *message,
+ struct wpa_supplicant *wpa_s)
+{
+ DBusMessageIter iter_dict;
+ DBusMessage *reply = NULL;
+ DBusMessageIter iter;
+ struct wpa_dbus_dict_entry entry;
+ char *pg_object_path = NULL;
+ int persistent_group = 0;
+ int freq = 0;
+ char *iface = NULL;
+ char *net_id_str = NULL;
+ unsigned int group_id = 0;
+ struct wpa_ssid *ssid;
+
+ dbus_message_iter_init(message, &iter);
+
+ if (!wpa_dbus_dict_open_read(&iter, &iter_dict, NULL))
+ goto inv_args;
+
+ while (wpa_dbus_dict_has_dict_entry(&iter_dict)) {
+ if (!wpa_dbus_dict_get_entry(&iter_dict, &entry))
+ goto inv_args;
+
+ if (!os_strcmp(entry.key, "persistent") &&
+ (entry.type == DBUS_TYPE_BOOLEAN)) {
+ persistent_group = (entry.bool_value == TRUE) ? 1 : 0;
+ } else if (!os_strcmp(entry.key, "frequency") &&
+ (entry.type == DBUS_TYPE_INT32)) {
+ freq = entry.int32_value;
+ if (freq <= 0)
+ goto inv_args_clear;
+ } else if (!os_strcmp(entry.key, "persistent_group_object") &&
+ entry.type == DBUS_TYPE_OBJECT_PATH)
+ pg_object_path = os_strdup(entry.str_value);
+ else
+ goto inv_args_clear;
+
+ wpa_dbus_dict_entry_clear(&entry);
+ }
+
+ if (pg_object_path != NULL) {
+ /*
+ * A persistent group Object Path is defined meaning we want
+ * to re-invoke a persistent group.
+ */
+
+ iface = wpas_dbus_new_decompose_object_path(pg_object_path, 1,
+ &net_id_str, NULL);
+ if (iface == NULL ||
+ os_strcmp(iface, wpa_s->dbus_new_path) != 0) {
+ reply =
+ wpas_dbus_error_invalid_args(message,
+ pg_object_path);
+ goto out;
+ }
+
+ group_id = strtoul(net_id_str, NULL, 10);
+ if (errno == EINVAL) {
+ reply = wpas_dbus_error_invalid_args(
+ message, pg_object_path);
+ goto out;
+ }
+
+ /* Get the SSID structure from the persistent group id */
+ ssid = wpa_config_get_network(wpa_s->conf, group_id);
+ if (ssid == NULL || ssid->disabled != 2)
+ goto inv_args;
+
+ if (wpas_p2p_group_add_persistent(wpa_s, ssid, 0, freq, 0)) {
+ reply = wpas_dbus_error_unknown_error(
+ message,
+ "Failed to reinvoke a persistent group");
+ goto out;
+ }
+ } else if (wpas_p2p_group_add(wpa_s, persistent_group, freq, 0))
+ goto inv_args;
+
+out:
+ os_free(pg_object_path);
+ os_free(net_id_str);
+ os_free(iface);
+ return reply;
+inv_args_clear:
+ wpa_dbus_dict_entry_clear(&entry);
+inv_args:
+ reply = wpas_dbus_error_invalid_args(message, NULL);
+ goto out;
+}
+
+
+DBusMessage * wpas_dbus_handler_p2p_disconnect(DBusMessage *message,
+ struct wpa_supplicant *wpa_s)
+{
+ if (wpas_p2p_disconnect(wpa_s))
+ return wpas_dbus_error_unknown_error(message,
+ "failed to disconnect");
+
+ return NULL;
+}
+
+
+static dbus_bool_t wpa_dbus_p2p_check_enabled(struct wpa_supplicant *wpa_s,
+ DBusMessage *message,
+ DBusMessage **out_reply,
+ DBusError *error)
+{
+ /* Return an error message or an error if P2P isn't available */
+ if (wpa_s->global->p2p_disabled || wpa_s->global->p2p == NULL) {
+ if (out_reply) {
+ *out_reply = dbus_message_new_error(
+ message, DBUS_ERROR_FAILED,
+ "P2P is not available for this interface");
+ }
+ dbus_set_error_const(error, DBUS_ERROR_FAILED,
+ "P2P is not available for this "
+ "interface");
+ return FALSE;
+ }
+ return TRUE;
+}
+
+
+DBusMessage * wpas_dbus_handler_p2p_flush(DBusMessage *message,
+ struct wpa_supplicant *wpa_s)
+{
+ DBusMessage *reply = NULL;
+
+ if (!wpa_dbus_p2p_check_enabled(wpa_s, message, &reply, NULL))
+ return reply;
+
+ os_memset(wpa_s->p2p_auth_invite, 0, ETH_ALEN);
+ wpa_s->force_long_sd = 0;
+ p2p_flush(wpa_s->global->p2p);
+
+ return NULL;
+}
+
+
+DBusMessage * wpas_dbus_handler_p2p_connect(DBusMessage *message,
+ struct wpa_supplicant *wpa_s)
+{
+ DBusMessageIter iter_dict;
+ DBusMessage *reply = NULL;
+ DBusMessageIter iter;
+ struct wpa_dbus_dict_entry entry;
+ char *peer_object_path = NULL;
+ int persistent_group = 0;
+ int join = 0;
+ int authorize_only = 0;
+ int go_intent = -1;
+ int freq = 0;
+ u8 addr[ETH_ALEN];
+ char *pin = NULL;
+ enum p2p_wps_method wps_method = WPS_NOT_READY;
+ int new_pin;
+ char *err_msg = NULL;
+ char *iface = NULL;
+
+ if (!wpa_dbus_p2p_check_enabled(wpa_s, message, &reply, NULL))
+ return reply;
+
+ dbus_message_iter_init(message, &iter);
+
+ if (!wpa_dbus_dict_open_read(&iter, &iter_dict, NULL))
+ goto inv_args;
+
+ while (wpa_dbus_dict_has_dict_entry(&iter_dict)) {
+ if (!wpa_dbus_dict_get_entry(&iter_dict, &entry))
+ goto inv_args;
+
+ if (!os_strcmp(entry.key, "peer") &&
+ (entry.type == DBUS_TYPE_OBJECT_PATH)) {
+ peer_object_path = os_strdup(entry.str_value);
+ } else if (!os_strcmp(entry.key, "persistent") &&
+ (entry.type == DBUS_TYPE_BOOLEAN)) {
+ persistent_group = (entry.bool_value == TRUE) ? 1 : 0;
+ } else if (!os_strcmp(entry.key, "join") &&
+ (entry.type == DBUS_TYPE_BOOLEAN)) {
+ join = (entry.bool_value == TRUE) ? 1 : 0;
+ } else if (!os_strcmp(entry.key, "authorize_only") &&
+ (entry.type == DBUS_TYPE_BOOLEAN)) {
+ authorize_only = (entry.bool_value == TRUE) ? 1 : 0;
+ } else if (!os_strcmp(entry.key, "frequency") &&
+ (entry.type == DBUS_TYPE_INT32)) {
+ freq = entry.int32_value;
+ if (freq <= 0)
+ goto inv_args_clear;
+ } else if (!os_strcmp(entry.key, "go_intent") &&
+ (entry.type == DBUS_TYPE_INT32)) {
+ go_intent = entry.int32_value;
+ if ((go_intent < 0) || (go_intent > 15))
+ goto inv_args_clear;
+ } else if (!os_strcmp(entry.key, "wps_method") &&
+ (entry.type == DBUS_TYPE_STRING)) {
+ if (!os_strcmp(entry.str_value, "pbc"))
+ wps_method = WPS_PBC;
+ else if (!os_strcmp(entry.str_value, "pin"))
+ wps_method = WPS_PIN_DISPLAY;
+ else if (!os_strcmp(entry.str_value, "display"))
+ wps_method = WPS_PIN_DISPLAY;
+ else if (!os_strcmp(entry.str_value, "keypad"))
+ wps_method = WPS_PIN_KEYPAD;
+ else
+ goto inv_args_clear;
+ } else if (!os_strcmp(entry.key, "pin") &&
+ (entry.type == DBUS_TYPE_STRING)) {
+ pin = os_strdup(entry.str_value);
+ } else
+ goto inv_args_clear;
+
+ wpa_dbus_dict_entry_clear(&entry);
+ }
+
+ if (!peer_object_path || (wps_method == WPS_NOT_READY) ||
+ (parse_peer_object_path(peer_object_path, addr) < 0) ||
+ !p2p_peer_known(wpa_s->global->p2p, addr))
+ goto inv_args;
+
+ /*
+ * Validate the wps_method specified and the pin value.
+ */
+ if ((!pin || !pin[0]) && (wps_method == WPS_PIN_KEYPAD))
+ goto inv_args;
+
+ new_pin = wpas_p2p_connect(wpa_s, addr, pin, wps_method,
+ persistent_group, 0, join, authorize_only,
+ go_intent, freq, -1, 0, 0);
+
+ if (new_pin >= 0) {
+ char npin[9];
+ char *generated_pin;
+ os_snprintf(npin, sizeof(npin), "%08d", new_pin);
+ generated_pin = npin;
+ reply = dbus_message_new_method_return(message);
+ dbus_message_append_args(reply, DBUS_TYPE_STRING,
+ &generated_pin, DBUS_TYPE_INVALID);
+ } else {
+ switch (new_pin) {
+ case -2:
+ err_msg = "connect failed due to channel "
+ "unavailability.";
+ iface = WPAS_DBUS_ERROR_CONNECT_CHANNEL_UNAVAILABLE;
+ break;
+
+ case -3:
+ err_msg = "connect failed due to unsupported channel.";
+ iface = WPAS_DBUS_ERROR_CONNECT_CHANNEL_UNSUPPORTED;
+ break;
+
+ default:
+ err_msg = "connect failed due to unspecified error.";
+ iface = WPAS_DBUS_ERROR_CONNECT_UNSPECIFIED_ERROR;
+ break;
+ }
+
+ /*
+ * TODO:
+ * Do we need specialized errors corresponding to above
+ * error conditions as against just returning a different
+ * error message?
+ */
+ reply = dbus_message_new_error(message, iface, err_msg);
+ }
+
+out:
+ os_free(peer_object_path);
+ os_free(pin);
+ return reply;
+inv_args_clear:
+ wpa_dbus_dict_entry_clear(&entry);
+inv_args:
+ reply = wpas_dbus_error_invalid_args(message, NULL);
+ goto out;
+}
+
+
+DBusMessage * wpas_dbus_handler_p2p_invite(DBusMessage *message,
+ struct wpa_supplicant *wpa_s)
+{
+ DBusMessageIter iter_dict;
+ DBusMessage *reply = NULL;
+ DBusMessageIter iter;
+ struct wpa_dbus_dict_entry entry;
+ char *peer_object_path = NULL;
+ char *pg_object_path = NULL;
+ char *iface = NULL;
+ char *net_id_str = NULL;
+ u8 peer_addr[ETH_ALEN];
+ unsigned int group_id = 0;
+ int persistent = 0;
+ struct wpa_ssid *ssid;
+
+ if (!wpa_dbus_p2p_check_enabled(wpa_s, message, &reply, NULL))
+ return reply;
+
+ dbus_message_iter_init(message, &iter);
+
+ if (!wpa_dbus_dict_open_read(&iter, &iter_dict, NULL))
+ goto err;
+
+ while (wpa_dbus_dict_has_dict_entry(&iter_dict)) {
+ if (!wpa_dbus_dict_get_entry(&iter_dict, &entry))
+ goto err;
+
+ if (!os_strcmp(entry.key, "peer") &&
+ (entry.type == DBUS_TYPE_OBJECT_PATH)) {
+ peer_object_path = os_strdup(entry.str_value);
+ wpa_dbus_dict_entry_clear(&entry);
+ } else if (!os_strcmp(entry.key, "persistent_group_object") &&
+ (entry.type == DBUS_TYPE_OBJECT_PATH)) {
+ pg_object_path = os_strdup(entry.str_value);
+ persistent = 1;
+ wpa_dbus_dict_entry_clear(&entry);
+ } else {
+ wpa_dbus_dict_entry_clear(&entry);
+ goto err;
+ }
+ }
+
+ if (!peer_object_path ||
+ (parse_peer_object_path(peer_object_path, peer_addr) < 0) ||
+ !p2p_peer_known(wpa_s->global->p2p, peer_addr)) {
+ goto err;
+ }
+
+ if (persistent) {
+ /*
+ * A group ID is defined meaning we want to re-invoke a
+ * persistent group
+ */
+
+ iface = wpas_dbus_new_decompose_object_path(pg_object_path, 1,
+ &net_id_str, NULL);
+ if (iface == NULL ||
+ os_strcmp(iface, wpa_s->dbus_new_path) != 0) {
+ reply = wpas_dbus_error_invalid_args(message,
+ pg_object_path);
+ goto out;
+ }
+
+ group_id = strtoul(net_id_str, NULL, 10);
+ if (errno == EINVAL) {
+ reply = wpas_dbus_error_invalid_args(
+ message, pg_object_path);
+ goto out;
+ }
+
+ /* Get the SSID structure from the persistent group id */
+ ssid = wpa_config_get_network(wpa_s->conf, group_id);
+ if (ssid == NULL || ssid->disabled != 2)
+ goto err;
+
+ if (wpas_p2p_invite(wpa_s, peer_addr, ssid, NULL, 0, 0) < 0) {
+ reply = wpas_dbus_error_unknown_error(
+ message,
+ "Failed to reinvoke a persistent group");
+ goto out;
+ }
+ } else {
+ /*
+ * No group ID means propose to a peer to join my active group
+ */
+ if (wpas_p2p_invite_group(wpa_s, wpa_s->ifname,
+ peer_addr, NULL)) {
+ reply = wpas_dbus_error_unknown_error(
+ message, "Failed to join to an active group");
+ goto out;
+ }
+ }
+
+out:
+ os_free(pg_object_path);
+ os_free(peer_object_path);
+ return reply;
+
+err:
+ reply = wpas_dbus_error_invalid_args(message, NULL);
+ goto out;
+}
+
+
+DBusMessage * wpas_dbus_handler_p2p_prov_disc_req(DBusMessage *message,
+ struct wpa_supplicant *wpa_s)
+{
+ DBusMessageIter iter;
+ char *peer_object_path = NULL;
+ char *config_method = NULL;
+ u8 peer_addr[ETH_ALEN];
+
+ dbus_message_iter_init(message, &iter);
+ dbus_message_iter_get_basic(&iter, &peer_object_path);
+
+ if (parse_peer_object_path(peer_object_path, peer_addr) < 0)
+ return wpas_dbus_error_invalid_args(message, NULL);
+
+ dbus_message_iter_next(&iter);
+ dbus_message_iter_get_basic(&iter, &config_method);
+
+ /*
+ * Validation checks on config_method are being duplicated here
+ * to be able to return invalid args reply since the error code
+ * from p2p module are not granular enough (yet).
+ */
+ if (os_strcmp(config_method, "display") &&
+ os_strcmp(config_method, "keypad") &&
+ os_strcmp(config_method, "pbc") &&
+ os_strcmp(config_method, "pushbutton"))
+ return wpas_dbus_error_invalid_args(message, NULL);
+
+ if (wpas_p2p_prov_disc(wpa_s, peer_addr, config_method,
+ WPAS_P2P_PD_FOR_GO_NEG) < 0)
+ return wpas_dbus_error_unknown_error(message,
+ "Failed to send provision discovery request");
+
+ return NULL;
+}
+
+
+/*
+ * P2P Device property accessor methods.
+ */
+
+dbus_bool_t wpas_dbus_getter_p2p_device_config(DBusMessageIter *iter,
+ DBusError *error,
+ void *user_data)
+{
+ struct wpa_supplicant *wpa_s = user_data;
+ DBusMessageIter variant_iter, dict_iter;
+ DBusMessageIter iter_secdev_dict_entry, iter_secdev_dict_val,
+ iter_secdev_dict_array;
+ const char *dev_name;
+ int num_vendor_extensions = 0;
+ int i;
+ const struct wpabuf *vendor_ext[P2P_MAX_WPS_VENDOR_EXT];
+
+ if (!wpa_dbus_p2p_check_enabled(wpa_s, NULL, NULL, error))
+ return FALSE;
+
+ if (!dbus_message_iter_open_container(iter, DBUS_TYPE_VARIANT,
+ "a{sv}", &variant_iter) ||
+ !wpa_dbus_dict_open_write(&variant_iter, &dict_iter))
+ goto err_no_mem;
+
+ /* DeviceName */
+ dev_name = wpa_s->conf->device_name;
+ if (dev_name &&
+ !wpa_dbus_dict_append_string(&dict_iter, "DeviceName", dev_name))
+ goto err_no_mem;
+
+ /* Primary device type */
+ if (!wpa_dbus_dict_append_byte_array(&dict_iter, "PrimaryDeviceType",
+ (char *)wpa_s->conf->device_type,
+ WPS_DEV_TYPE_LEN))
+ goto err_no_mem;
+
+ /* Secondary device types */
+ if (wpa_s->conf->num_sec_device_types) {
+ if (!wpa_dbus_dict_begin_array(&dict_iter,
+ "SecondaryDeviceTypes",
+ DBUS_TYPE_ARRAY_AS_STRING
+ DBUS_TYPE_BYTE_AS_STRING,
+ &iter_secdev_dict_entry,
+ &iter_secdev_dict_val,
+ &iter_secdev_dict_array))
+ goto err_no_mem;
+
+ for (i = 0; i < wpa_s->conf->num_sec_device_types; i++)
+ wpa_dbus_dict_bin_array_add_element(
+ &iter_secdev_dict_array,
+ wpa_s->conf->sec_device_type[i],
+ WPS_DEV_TYPE_LEN);
+
+ if (!wpa_dbus_dict_end_array(&dict_iter,
+ &iter_secdev_dict_entry,
+ &iter_secdev_dict_val,
+ &iter_secdev_dict_array))
+ goto err_no_mem;
+ }
+
+ /* Vendor Extensions */
+ for (i = 0; i < P2P_MAX_WPS_VENDOR_EXT; i++) {
+ if (wpa_s->conf->wps_vendor_ext[i] == NULL)
+ continue;
+ vendor_ext[num_vendor_extensions++] =
+ wpa_s->conf->wps_vendor_ext[i];
+ }
+
+ if (num_vendor_extensions &&
+ !wpa_dbus_dict_append_wpabuf_array(&dict_iter,
+ "VendorExtension",
+ vendor_ext,
+ num_vendor_extensions))
+ goto err_no_mem;
+
+ /* GO Intent */
+ if (!wpa_dbus_dict_append_uint32(&dict_iter, "GOIntent",
+ wpa_s->conf->p2p_go_intent))
+ goto err_no_mem;
+
+ /* Persistent Reconnect */
+ if (!wpa_dbus_dict_append_bool(&dict_iter, "PersistentReconnect",
+ wpa_s->conf->persistent_reconnect))
+ goto err_no_mem;
+
+ /* Listen Reg Class */
+ if (!wpa_dbus_dict_append_uint32(&dict_iter, "ListenRegClass",
+ wpa_s->conf->p2p_listen_reg_class))
+ goto err_no_mem;
+
+ /* Listen Channel */
+ if (!wpa_dbus_dict_append_uint32(&dict_iter, "ListenChannel",
+ wpa_s->conf->p2p_listen_channel))
+ goto err_no_mem;
+
+ /* Oper Reg Class */
+ if (!wpa_dbus_dict_append_uint32(&dict_iter, "OperRegClass",
+ wpa_s->conf->p2p_oper_reg_class))
+ goto err_no_mem;
+
+ /* Oper Channel */
+ if (!wpa_dbus_dict_append_uint32(&dict_iter, "OperChannel",
+ wpa_s->conf->p2p_oper_channel))
+ goto err_no_mem;
+
+ /* SSID Postfix */
+ if (wpa_s->conf->p2p_ssid_postfix &&
+ !wpa_dbus_dict_append_string(&dict_iter, "SsidPostfix",
+ wpa_s->conf->p2p_ssid_postfix))
+ goto err_no_mem;
+
+ /* Intra Bss */
+ if (!wpa_dbus_dict_append_bool(&dict_iter, "IntraBss",
+ wpa_s->conf->p2p_intra_bss))
+ goto err_no_mem;
+
+ /* Group Idle */
+ if (!wpa_dbus_dict_append_uint32(&dict_iter, "GroupIdle",
+ wpa_s->conf->p2p_group_idle))
+ goto err_no_mem;
+
+ /* Dissasociation low ack */
+ if (!wpa_dbus_dict_append_uint32(&dict_iter, "disassoc_low_ack",
+ wpa_s->conf->disassoc_low_ack))
+ goto err_no_mem;
+
+ if (!wpa_dbus_dict_close_write(&variant_iter, &dict_iter) ||
+ !dbus_message_iter_close_container(iter, &variant_iter))
+ goto err_no_mem;
+
+ return TRUE;
+
+err_no_mem:
+ dbus_set_error_const(error, DBUS_ERROR_NO_MEMORY, "no memory");
+ return FALSE;
+}
+
+
+dbus_bool_t wpas_dbus_setter_p2p_device_config(DBusMessageIter *iter,
+ DBusError *error,
+ void *user_data)
+{
+ struct wpa_supplicant *wpa_s = user_data;
+ DBusMessageIter variant_iter, iter_dict;
+ struct wpa_dbus_dict_entry entry = {.type = DBUS_TYPE_STRING };
+ unsigned int i;
+
+ if (!wpa_dbus_p2p_check_enabled(wpa_s, NULL, NULL, error))
+ return FALSE;
+
+ dbus_message_iter_recurse(iter, &variant_iter);
+ if (!wpa_dbus_dict_open_read(&variant_iter, &iter_dict, error))
+ return FALSE;
+
+ while (wpa_dbus_dict_has_dict_entry(&iter_dict)) {
+ if (!wpa_dbus_dict_get_entry(&iter_dict, &entry)) {
+ dbus_set_error_const(error, DBUS_ERROR_INVALID_ARGS,
+ "invalid message format");
+ return FALSE;
+ }
+
+ if (os_strcmp(entry.key, "DeviceName") == 0) {
+ char *devname;
+
+ if (entry.type != DBUS_TYPE_STRING)
+ goto error;
+
+ devname = os_strdup(entry.str_value);
+ if (devname == NULL)
+ goto err_no_mem_clear;
+
+ os_free(wpa_s->conf->device_name);
+ wpa_s->conf->device_name = devname;
+
+ wpa_s->conf->changed_parameters |=
+ CFG_CHANGED_DEVICE_NAME;
+ } else if (os_strcmp(entry.key, "PrimaryDeviceType") == 0) {
+ if (entry.type != DBUS_TYPE_ARRAY ||
+ entry.array_type != DBUS_TYPE_BYTE ||
+ entry.array_len != WPS_DEV_TYPE_LEN)
+ goto error;
+
+ os_memcpy(wpa_s->conf->device_type,
+ entry.bytearray_value,
+ WPS_DEV_TYPE_LEN);
+ wpa_s->conf->changed_parameters |=
+ CFG_CHANGED_DEVICE_TYPE;
+ } else if (os_strcmp(entry.key, "SecondaryDeviceTypes") == 0) {
+ if (entry.type != DBUS_TYPE_ARRAY ||
+ entry.array_type != WPAS_DBUS_TYPE_BINARRAY ||
+ entry.array_len > MAX_SEC_DEVICE_TYPES)
+ goto error;
+
+ for (i = 0; i < entry.array_len; i++)
+ if (wpabuf_len(entry.binarray_value[i]) !=
+ WPS_DEV_TYPE_LEN)
+ goto err_no_mem_clear;
+ for (i = 0; i < entry.array_len; i++)
+ os_memcpy(wpa_s->conf->sec_device_type[i],
+ wpabuf_head(entry.binarray_value[i]),
+ WPS_DEV_TYPE_LEN);
+ wpa_s->conf->num_sec_device_types = entry.array_len;
+ wpa_s->conf->changed_parameters |=
+ CFG_CHANGED_SEC_DEVICE_TYPE;
+ } else if (os_strcmp(entry.key, "VendorExtension") == 0) {
+ if ((entry.type != DBUS_TYPE_ARRAY) ||
+ (entry.array_type != WPAS_DBUS_TYPE_BINARRAY) ||
+ (entry.array_len > P2P_MAX_WPS_VENDOR_EXT))
+ goto error;
+
+ wpa_s->conf->changed_parameters |=
+ CFG_CHANGED_VENDOR_EXTENSION;
+
+ for (i = 0; i < P2P_MAX_WPS_VENDOR_EXT; i++) {
+ wpabuf_free(wpa_s->conf->wps_vendor_ext[i]);
+ if (i < entry.array_len) {
+ wpa_s->conf->wps_vendor_ext[i] =
+ entry.binarray_value[i];
+ entry.binarray_value[i] = NULL;
+ } else
+ wpa_s->conf->wps_vendor_ext[i] = NULL;
+ }
+ } else if ((os_strcmp(entry.key, "GOIntent") == 0) &&
+ (entry.type == DBUS_TYPE_UINT32) &&
+ (entry.uint32_value <= 15))
+ wpa_s->conf->p2p_go_intent = entry.uint32_value;
+ else if ((os_strcmp(entry.key, "PersistentReconnect") == 0) &&
+ (entry.type == DBUS_TYPE_BOOLEAN))
+ wpa_s->conf->persistent_reconnect = entry.bool_value;
+ else if ((os_strcmp(entry.key, "ListenRegClass") == 0) &&
+ (entry.type == DBUS_TYPE_UINT32)) {
+ wpa_s->conf->p2p_listen_reg_class = entry.uint32_value;
+ wpa_s->conf->changed_parameters |=
+ CFG_CHANGED_P2P_LISTEN_CHANNEL;
+ } else if ((os_strcmp(entry.key, "ListenChannel") == 0) &&
+ (entry.type == DBUS_TYPE_UINT32)) {
+ wpa_s->conf->p2p_listen_channel = entry.uint32_value;
+ wpa_s->conf->changed_parameters |=
+ CFG_CHANGED_P2P_LISTEN_CHANNEL;
+ } else if ((os_strcmp(entry.key, "OperRegClass") == 0) &&
+ (entry.type == DBUS_TYPE_UINT32)) {
+ wpa_s->conf->p2p_oper_reg_class = entry.uint32_value;
+ wpa_s->conf->changed_parameters |=
+ CFG_CHANGED_P2P_OPER_CHANNEL;
+ } else if ((os_strcmp(entry.key, "OperChannel") == 0) &&
+ (entry.type == DBUS_TYPE_UINT32)) {
+ wpa_s->conf->p2p_oper_channel = entry.uint32_value;
+ wpa_s->conf->changed_parameters |=
+ CFG_CHANGED_P2P_OPER_CHANNEL;
+ } else if (os_strcmp(entry.key, "SsidPostfix") == 0) {
+ char *postfix;
+
+ if (entry.type != DBUS_TYPE_STRING)
+ goto error;
+
+ postfix = os_strdup(entry.str_value);
+ if (!postfix)
+ goto err_no_mem_clear;
+
+ os_free(wpa_s->conf->p2p_ssid_postfix);
+ wpa_s->conf->p2p_ssid_postfix = postfix;
+
+ wpa_s->conf->changed_parameters |=
+ CFG_CHANGED_P2P_SSID_POSTFIX;
+ } else if ((os_strcmp(entry.key, "IntraBss") == 0) &&
+ (entry.type == DBUS_TYPE_BOOLEAN)) {
+ wpa_s->conf->p2p_intra_bss = entry.bool_value;
+ wpa_s->conf->changed_parameters |=
+ CFG_CHANGED_P2P_INTRA_BSS;
+ } else if ((os_strcmp(entry.key, "GroupIdle") == 0) &&
+ (entry.type == DBUS_TYPE_UINT32))
+ wpa_s->conf->p2p_group_idle = entry.uint32_value;
+ else if (os_strcmp(entry.key, "disassoc_low_ack") == 0 &&
+ entry.type == DBUS_TYPE_UINT32)
+ wpa_s->conf->disassoc_low_ack = entry.uint32_value;
+ else
+ goto error;
+
+ wpa_dbus_dict_entry_clear(&entry);
+ }
+
+ if (wpa_s->conf->changed_parameters) {
+ /* Some changed parameters requires to update config*/
+ wpa_supplicant_update_config(wpa_s);
+ }
+
+ return TRUE;
+
+ error:
+ dbus_set_error_const(error, DBUS_ERROR_INVALID_ARGS,
+ "invalid message format");
+ wpa_dbus_dict_entry_clear(&entry);
+ return FALSE;
+
+ err_no_mem_clear:
+ dbus_set_error_const(error, DBUS_ERROR_NO_MEMORY, "no memory");
+ wpa_dbus_dict_entry_clear(&entry);
+ return FALSE;
+}
+
+
+dbus_bool_t wpas_dbus_getter_p2p_peers(DBusMessageIter *iter, DBusError *error,
+ void *user_data)
+{
+ struct wpa_supplicant *wpa_s = user_data;
+ struct p2p_data *p2p = wpa_s->global->p2p;
+ int next = 0, i = 0;
+ int num = 0, out_of_mem = 0;
+ const u8 *addr;
+ const struct p2p_peer_info *peer_info = NULL;
+ dbus_bool_t success = FALSE;
+
+ struct dl_list peer_objpath_list;
+ struct peer_objpath_node {
+ struct dl_list list;
+ char path[WPAS_DBUS_OBJECT_PATH_MAX];
+ } *node, *tmp;
+
+ char **peer_obj_paths = NULL;
+
+ if (!wpa_dbus_p2p_check_enabled(wpa_s, NULL, NULL, error))
+ return FALSE;
+
+ dl_list_init(&peer_objpath_list);
+
+ /* Get the first peer info */
+ peer_info = p2p_get_peer_found(p2p, NULL, next);
+
+ /* Get next and accumulate them */
+ next = 1;
+ while (peer_info != NULL) {
+ node = os_zalloc(sizeof(struct peer_objpath_node));
+ if (!node) {
+ out_of_mem = 1;
+ goto error;
+ }
+
+ addr = peer_info->p2p_device_addr;
+ os_snprintf(node->path, WPAS_DBUS_OBJECT_PATH_MAX,
+ "%s/" WPAS_DBUS_NEW_P2P_PEERS_PART
+ "/" COMPACT_MACSTR,
+ wpa_s->dbus_new_path, MAC2STR(addr));
+ dl_list_add_tail(&peer_objpath_list, &node->list);
+ num++;
+
+ peer_info = p2p_get_peer_found(p2p, addr, next);
+ }
+
+ /*
+ * Now construct the peer object paths in a form suitable for
+ * array_property_getter helper below.
+ */
+ peer_obj_paths = os_calloc(num, sizeof(char *));
+
+ if (!peer_obj_paths) {
+ out_of_mem = 1;
+ goto error;
+ }
+
+ dl_list_for_each_safe(node, tmp, &peer_objpath_list,
+ struct peer_objpath_node, list)
+ peer_obj_paths[i++] = node->path;
+
+ success = wpas_dbus_simple_array_property_getter(iter,
+ DBUS_TYPE_OBJECT_PATH,
+ peer_obj_paths, num,
+ error);
+
+error:
+ if (peer_obj_paths)
+ os_free(peer_obj_paths);
+
+ dl_list_for_each_safe(node, tmp, &peer_objpath_list,
+ struct peer_objpath_node, list) {
+ dl_list_del(&node->list);
+ os_free(node);
+ }
+ if (out_of_mem)
+ dbus_set_error_const(error, DBUS_ERROR_NO_MEMORY, "no memory");
+
+ return success;
+}
+
+
+enum wpas_p2p_role {
+ WPAS_P2P_ROLE_DEVICE,
+ WPAS_P2P_ROLE_GO,
+ WPAS_P2P_ROLE_CLIENT,
+};
+
+static enum wpas_p2p_role wpas_get_p2p_role(struct wpa_supplicant *wpa_s)
+{
+ struct wpa_ssid *ssid = wpa_s->current_ssid;
+
+ if (!ssid)
+ return WPAS_P2P_ROLE_DEVICE;
+ if (wpa_s->wpa_state != WPA_COMPLETED)
+ return WPAS_P2P_ROLE_DEVICE;
+
+ switch (ssid->mode) {
+ case WPAS_MODE_P2P_GO:
+ case WPAS_MODE_P2P_GROUP_FORMATION:
+ return WPAS_P2P_ROLE_GO;
+ case WPAS_MODE_INFRA:
+ if (ssid->p2p_group)
+ return WPAS_P2P_ROLE_CLIENT;
+ return WPAS_P2P_ROLE_DEVICE;
+ default:
+ return WPAS_P2P_ROLE_DEVICE;
+ }
+}
+
+
+dbus_bool_t wpas_dbus_getter_p2p_role(DBusMessageIter *iter, DBusError *error,
+ void *user_data)
+{
+ struct wpa_supplicant *wpa_s = user_data;
+ char *str;
+
+ switch (wpas_get_p2p_role(wpa_s)) {
+ case WPAS_P2P_ROLE_GO:
+ str = "GO";
+ break;
+ case WPAS_P2P_ROLE_CLIENT:
+ str = "client";
+ break;
+ default:
+ str = "device";
+ }
+
+ return wpas_dbus_simple_property_getter(iter, DBUS_TYPE_STRING, &str,
+ error);
+}
+
+
+dbus_bool_t wpas_dbus_getter_p2p_group(DBusMessageIter *iter, DBusError *error,
+ void *user_data)
+{
+ struct wpa_supplicant *wpa_s = user_data;
+ char path_buf[WPAS_DBUS_OBJECT_PATH_MAX];
+ char *dbus_groupobj_path = path_buf;
+
+ if (wpa_s->dbus_groupobj_path == NULL)
+ os_snprintf(dbus_groupobj_path, WPAS_DBUS_OBJECT_PATH_MAX,
+ "/");
+ else
+ os_snprintf(dbus_groupobj_path, WPAS_DBUS_OBJECT_PATH_MAX,
+ "%s", wpa_s->dbus_groupobj_path);
+
+ return wpas_dbus_simple_property_getter(iter, DBUS_TYPE_OBJECT_PATH,
+ &dbus_groupobj_path, error);
+}
+
+
+dbus_bool_t wpas_dbus_getter_p2p_peergo(DBusMessageIter *iter,
+ DBusError *error, void *user_data)
+{
+ struct wpa_supplicant *wpa_s = user_data;
+ char go_peer_obj_path[WPAS_DBUS_OBJECT_PATH_MAX], *path;
+
+ if (wpas_get_p2p_role(wpa_s) != WPAS_P2P_ROLE_CLIENT)
+ os_snprintf(go_peer_obj_path, WPAS_DBUS_OBJECT_PATH_MAX, "/");
+ else
+ os_snprintf(go_peer_obj_path, WPAS_DBUS_OBJECT_PATH_MAX,
+ "%s/" WPAS_DBUS_NEW_P2P_PEERS_PART "/"
+ COMPACT_MACSTR,
+ wpa_s->dbus_new_path, MAC2STR(wpa_s->go_dev_addr));
+
+ path = go_peer_obj_path;
+ return wpas_dbus_simple_property_getter(iter, DBUS_TYPE_OBJECT_PATH,
+ &path, error);
+}
+
+
+/*
+ * Peer object properties accessor methods
+ */
+
+dbus_bool_t wpas_dbus_getter_p2p_peer_device_name(DBusMessageIter *iter,
+ DBusError *error,
+ void *user_data)
+{
+ struct peer_handler_args *peer_args = user_data;
+ const struct p2p_peer_info *info;
+ char *tmp;
+
+ if (!wpa_dbus_p2p_check_enabled(peer_args->wpa_s, NULL, NULL, error))
+ return FALSE;
+
+ /* get the peer info */
+ info = p2p_get_peer_found(peer_args->wpa_s->global->p2p,
+ peer_args->p2p_device_addr, 0);
+ if (info == NULL) {
+ dbus_set_error(error, DBUS_ERROR_FAILED,
+ "failed to find peer");
+ return FALSE;
+ }
+
+ tmp = os_strdup(info->device_name);
+ if (!tmp) {
+ dbus_set_error_const(error, DBUS_ERROR_NO_MEMORY, "no memory");
+ return FALSE;
+ }
+
+ if (!wpas_dbus_simple_property_getter(iter, DBUS_TYPE_STRING, &tmp,
+ error)) {
+ dbus_set_error_const(error, DBUS_ERROR_NO_MEMORY, "no memory");
+ os_free(tmp);
+ return FALSE;
+ }
+
+ os_free(tmp);
+ return TRUE;
+}
+
+
+dbus_bool_t wpas_dbus_getter_p2p_peer_primary_device_type(
+ DBusMessageIter *iter, DBusError *error, void *user_data)
+{
+ struct peer_handler_args *peer_args = user_data;
+ const struct p2p_peer_info *info;
+
+ info = p2p_get_peer_found(peer_args->wpa_s->global->p2p,
+ peer_args->p2p_device_addr, 0);
+ if (info == NULL) {
+ dbus_set_error(error, DBUS_ERROR_FAILED,
+ "failed to find peer");
+ return FALSE;
+ }
+
+ if (!wpas_dbus_simple_array_property_getter(iter, DBUS_TYPE_BYTE,
+ (char *)
+ info->pri_dev_type,
+ WPS_DEV_TYPE_LEN, error)) {
+ dbus_set_error_const(error, DBUS_ERROR_NO_MEMORY, "no memory");
+ return FALSE;
+ }
+
+ return TRUE;
+}
+
+
+dbus_bool_t wpas_dbus_getter_p2p_peer_config_method(DBusMessageIter *iter,
+ DBusError *error,
+ void *user_data)
+{
+ struct peer_handler_args *peer_args = user_data;
+ const struct p2p_peer_info *info;
+
+ info = p2p_get_peer_found(peer_args->wpa_s->global->p2p,
+ peer_args->p2p_device_addr, 0);
+ if (info == NULL) {
+ dbus_set_error(error, DBUS_ERROR_FAILED,
+ "failed to find peer");
+ return FALSE;
+ }
+
+ if (!wpas_dbus_simple_property_getter(iter, DBUS_TYPE_UINT16,
+ &info->config_methods, error)) {
+ dbus_set_error_const(error, DBUS_ERROR_NO_MEMORY, "no memory");
+ return FALSE;
+ }
+
+ return TRUE;
+}
+
+
+dbus_bool_t wpas_dbus_getter_p2p_peer_level(DBusMessageIter *iter,
+ DBusError *error,
+ void *user_data)
+{
+ struct peer_handler_args *peer_args = user_data;
+ const struct p2p_peer_info *info;
+
+ info = p2p_get_peer_found(peer_args->wpa_s->global->p2p,
+ peer_args->p2p_device_addr, 0);
+ if (info == NULL) {
+ dbus_set_error(error, DBUS_ERROR_FAILED,
+ "failed to find peer");
+ return FALSE;
+ }
+
+ if (!wpas_dbus_simple_property_getter(iter, DBUS_TYPE_INT32,
+ &info->level, error)) {
+ dbus_set_error_const(error, DBUS_ERROR_NO_MEMORY, "no memory");
+ return FALSE;
+ }
+
+ return TRUE;
+}
+
+
+dbus_bool_t wpas_dbus_getter_p2p_peer_device_capability(DBusMessageIter *iter,
+ DBusError *error,
+ void *user_data)
+{
+ struct peer_handler_args *peer_args = user_data;
+ const struct p2p_peer_info *info;
+
+ info = p2p_get_peer_found(peer_args->wpa_s->global->p2p,
+ peer_args->p2p_device_addr, 0);
+ if (info == NULL) {
+ dbus_set_error(error, DBUS_ERROR_FAILED,
+ "failed to find peer");
+ return FALSE;
+ }
+
+ if (!wpas_dbus_simple_property_getter(iter, DBUS_TYPE_BYTE,
+ &info->dev_capab, error)) {
+ dbus_set_error_const(error, DBUS_ERROR_NO_MEMORY, "no memory");
+ return FALSE;
+ }
+
+ return TRUE;
+}
+
+
+dbus_bool_t wpas_dbus_getter_p2p_peer_group_capability(DBusMessageIter *iter,
+ DBusError *error,
+ void *user_data)
+{
+ struct peer_handler_args *peer_args = user_data;
+ const struct p2p_peer_info *info;
+
+ info = p2p_get_peer_found(peer_args->wpa_s->global->p2p,
+ peer_args->p2p_device_addr, 0);
+ if (info == NULL) {
+ dbus_set_error(error, DBUS_ERROR_FAILED,
+ "failed to find peer");
+ return FALSE;
+ }
+
+ if (!wpas_dbus_simple_property_getter(iter, DBUS_TYPE_BYTE,
+ &info->group_capab, error)) {
+ dbus_set_error_const(error, DBUS_ERROR_NO_MEMORY, "no memory");
+ return FALSE;
+ }
+
+ return TRUE;
+}
+
+
+dbus_bool_t wpas_dbus_getter_p2p_peer_secondary_device_types(
+ DBusMessageIter *iter, DBusError *error, void *user_data)
+{
+ struct peer_handler_args *peer_args = user_data;
+ const struct p2p_peer_info *info;
+ DBusMessageIter variant_iter, array_iter;
+
+ info = p2p_get_peer_found(peer_args->wpa_s->global->p2p,
+ peer_args->p2p_device_addr, 0);
+ if (info == NULL) {
+ dbus_set_error(error, DBUS_ERROR_FAILED,
+ "failed to find peer");
+ return FALSE;
+ }
+
+ if (!dbus_message_iter_open_container(iter, DBUS_TYPE_VARIANT,
+ DBUS_TYPE_ARRAY_AS_STRING
+ DBUS_TYPE_ARRAY_AS_STRING
+ DBUS_TYPE_BYTE_AS_STRING,
+ &variant_iter)) {
+ dbus_set_error(error, DBUS_ERROR_FAILED,
+ "%s: failed to construct message 1", __func__);
+ return FALSE;
+ }
+
+ if (!dbus_message_iter_open_container(&variant_iter, DBUS_TYPE_ARRAY,
+ DBUS_TYPE_ARRAY_AS_STRING
+ DBUS_TYPE_BYTE_AS_STRING,
+ &array_iter)) {
+ dbus_set_error(error, DBUS_ERROR_FAILED,
+ "%s: failed to construct message 2", __func__);
+ return FALSE;
+ }
+
+ if (info->wps_sec_dev_type_list_len) {
+ const u8 *sec_dev_type_list = info->wps_sec_dev_type_list;
+ int num_sec_device_types =
+ info->wps_sec_dev_type_list_len / WPS_DEV_TYPE_LEN;
+ int i;
+ DBusMessageIter inner_array_iter;
+
+ for (i = 0; i < num_sec_device_types; i++) {
+ if (!dbus_message_iter_open_container(
+ &array_iter, DBUS_TYPE_ARRAY,
+ DBUS_TYPE_BYTE_AS_STRING,
+ &inner_array_iter)) {
+ dbus_set_error(error, DBUS_ERROR_FAILED,
+ "%s: failed to construct "
+ "message 3 (%d)",
+ __func__, i);
+ return FALSE;
+ }
+
+ if (!dbus_message_iter_append_fixed_array(
+ &inner_array_iter, DBUS_TYPE_BYTE,
+ &sec_dev_type_list, WPS_DEV_TYPE_LEN)) {
+ dbus_set_error(error, DBUS_ERROR_FAILED,
+ "%s: failed to construct "
+ "message 4 (%d)",
+ __func__, i);
+ return FALSE;
+ }
+
+ if (!dbus_message_iter_close_container(
+ &array_iter, &inner_array_iter)) {
+ dbus_set_error(error, DBUS_ERROR_FAILED,
+ "%s: failed to construct "
+ "message 5 (%d)",
+ __func__, i);
+ return FALSE;
+ }
+
+ sec_dev_type_list += WPS_DEV_TYPE_LEN;
+ }
+ }
+
+ if (!dbus_message_iter_close_container(&variant_iter, &array_iter)) {
+ dbus_set_error(error, DBUS_ERROR_FAILED,
+ "%s: failed to construct message 6", __func__);
+ return FALSE;
+ }
+
+ if (!dbus_message_iter_close_container(iter, &variant_iter)) {
+ dbus_set_error(error, DBUS_ERROR_FAILED,
+ "%s: failed to construct message 7", __func__);
+ return FALSE;
+ }
+
+ return TRUE;
+}
+
+
+dbus_bool_t wpas_dbus_getter_p2p_peer_vendor_extension(DBusMessageIter *iter,
+ DBusError *error,
+ void *user_data)
+{
+ struct wpabuf *vendor_extension[P2P_MAX_WPS_VENDOR_EXT];
+ int i, num;
+ struct peer_handler_args *peer_args = user_data;
+ const struct p2p_peer_info *info;
+
+ info = p2p_get_peer_found(peer_args->wpa_s->global->p2p,
+ peer_args->p2p_device_addr, 0);
+ if (info == NULL) {
+ dbus_set_error(error, DBUS_ERROR_FAILED,
+ "failed to find peer");
+ return FALSE;
+ }
+
+ /* Add WPS vendor extensions attribute */
+ for (i = 0, num = 0; i < P2P_MAX_WPS_VENDOR_EXT; i++) {
+ if (info->wps_vendor_ext[i] == NULL)
+ continue;
+ vendor_extension[num] = info->wps_vendor_ext[i];
+ num++;
+ }
+
+ if (!wpas_dbus_simple_array_array_property_getter(iter, DBUS_TYPE_BYTE,
+ vendor_extension,
+ num, error))
+ return FALSE;
+
+ return TRUE;
+}
+
+
+dbus_bool_t wpas_dbus_getter_p2p_peer_ies(DBusMessageIter *iter,
+ DBusError *error, void *user_data)
+{
+ dbus_bool_t success;
+ /* struct peer_handler_args *peer_args = user_data; */
+
+ success = wpas_dbus_simple_array_property_getter(iter, DBUS_TYPE_BYTE,
+ NULL, 0, error);
+ return success;
+}
+
+
+/**
+ * wpas_dbus_getter_persistent_groups - Get array of persistent group objects
+ * @iter: Pointer to incoming dbus message iter
+ * @error: Location to store error on failure
+ * @user_data: Function specific data
+ * Returns: TRUE on success, FALSE on failure
+ *
+ * Getter for "PersistentGroups" property.
+ */
+dbus_bool_t wpas_dbus_getter_persistent_groups(DBusMessageIter *iter,
+ DBusError *error,
+ void *user_data)
+{
+ struct wpa_supplicant *wpa_s = user_data;
+ struct wpa_ssid *ssid;
+ char **paths;
+ unsigned int i = 0, num = 0;
+ dbus_bool_t success = FALSE;
+
+ if (wpa_s->conf == NULL) {
+ wpa_printf(MSG_ERROR, "dbus: %s: "
+ "An error occurred getting persistent groups list",
+ __func__);
+ dbus_set_error_const(error, DBUS_ERROR_FAILED, "an error "
+ "occurred getting persistent groups list");
+ return FALSE;
+ }
+
+ for (ssid = wpa_s->conf->ssid; ssid; ssid = ssid->next)
+ if (network_is_persistent_group(ssid))
+ num++;
+
+ paths = os_calloc(num, sizeof(char *));
+ if (!paths) {
+ dbus_set_error_const(error, DBUS_ERROR_NO_MEMORY, "no memory");
+ return FALSE;
+ }
+
+ /* Loop through configured networks and append object path of each */
+ for (ssid = wpa_s->conf->ssid; ssid; ssid = ssid->next) {
+ if (!network_is_persistent_group(ssid))
+ continue;
+ paths[i] = os_zalloc(WPAS_DBUS_OBJECT_PATH_MAX);
+ if (paths[i] == NULL) {
+ dbus_set_error_const(error, DBUS_ERROR_NO_MEMORY,
+ "no memory");
+ goto out;
+ }
+ /* Construct the object path for this network. */
+ os_snprintf(paths[i++], WPAS_DBUS_OBJECT_PATH_MAX,
+ "%s/" WPAS_DBUS_NEW_PERSISTENT_GROUPS_PART "/%d",
+ wpa_s->dbus_new_path, ssid->id);
+ }
+
+ success = wpas_dbus_simple_array_property_getter(iter,
+ DBUS_TYPE_OBJECT_PATH,
+ paths, num, error);
+
+out:
+ while (i)
+ os_free(paths[--i]);
+ os_free(paths);
+ return success;
+}
+
+
+/**
+ * wpas_dbus_getter_persistent_group_properties - Get options for a persistent
+ * group
+ * @iter: Pointer to incoming dbus message iter
+ * @error: Location to store error on failure
+ * @user_data: Function specific data
+ * Returns: TRUE on success, FALSE on failure
+ *
+ * Getter for "Properties" property of a persistent group.
+ */
+dbus_bool_t wpas_dbus_getter_persistent_group_properties(DBusMessageIter *iter,
+ DBusError *error,
+ void *user_data)
+{
+ struct network_handler_args *net = user_data;
+
+ /* Leveraging the fact that persistent group object is still
+ * represented in same manner as network within.
+ */
+ return wpas_dbus_getter_network_properties(iter, error, net);
+}
+
+
+/**
+ * wpas_dbus_setter_persistent_group_properties - Get options for a persistent
+ * group
+ * @iter: Pointer to incoming dbus message iter
+ * @error: Location to store error on failure
+ * @user_data: Function specific data
+ * Returns: TRUE on success, FALSE on failure
+ *
+ * Setter for "Properties" property of a persistent group.
+ */
+dbus_bool_t wpas_dbus_setter_persistent_group_properties(DBusMessageIter *iter,
+ DBusError *error,
+ void *user_data)
+{
+ struct network_handler_args *net = user_data;
+ struct wpa_ssid *ssid = net->ssid;
+ DBusMessageIter variant_iter;
+
+ /*
+ * Leveraging the fact that persistent group object is still
+ * represented in same manner as network within.
+ */
+ dbus_message_iter_recurse(iter, &variant_iter);
+ return set_network_properties(net->wpa_s, ssid, &variant_iter, error);
+}
+
+
+/**
+ * wpas_dbus_new_iface_add_persistent_group - Add a new configured
+ * persistent_group
+ * @message: Pointer to incoming dbus message
+ * @wpa_s: wpa_supplicant structure for a network interface
+ * Returns: A dbus message containing the object path of the new
+ * persistent group
+ *
+ * Handler function for "AddPersistentGroup" method call of a P2P Device
+ * interface.
+ */
+DBusMessage * wpas_dbus_handler_add_persistent_group(
+ DBusMessage *message, struct wpa_supplicant *wpa_s)
+{
+ DBusMessage *reply = NULL;
+ DBusMessageIter iter;
+ struct wpa_ssid *ssid = NULL;
+ char path_buf[WPAS_DBUS_OBJECT_PATH_MAX], *path = path_buf;
+ DBusError error;
+
+ dbus_message_iter_init(message, &iter);
+
+ ssid = wpa_config_add_network(wpa_s->conf);
+ if (ssid == NULL) {
+ wpa_printf(MSG_ERROR, "dbus: %s: "
+ "Cannot add new persistent group", __func__);
+ reply = wpas_dbus_error_unknown_error(
+ message,
+ "wpa_supplicant could not add "
+ "a persistent group on this interface.");
+ goto err;
+ }
+
+ /* Mark the ssid as being a persistent group before the notification */
+ ssid->disabled = 2;
+ ssid->p2p_persistent_group = 1;
+ wpas_notify_persistent_group_added(wpa_s, ssid);
+
+ wpa_config_set_network_defaults(ssid);
+
+ dbus_error_init(&error);
+ if (!set_network_properties(wpa_s, ssid, &iter, &error)) {
+ wpa_printf(MSG_DEBUG, "dbus: %s: "
+ "Control interface could not set persistent group "
+ "properties", __func__);
+ reply = wpas_dbus_reply_new_from_error(message, &error,
+ DBUS_ERROR_INVALID_ARGS,
+ "Failed to set network "
+ "properties");
+ dbus_error_free(&error);
+ goto err;
+ }
+
+ /* Construct the object path for this network. */
+ os_snprintf(path, WPAS_DBUS_OBJECT_PATH_MAX,
+ "%s/" WPAS_DBUS_NEW_PERSISTENT_GROUPS_PART "/%d",
+ wpa_s->dbus_new_path, ssid->id);
+
+ reply = dbus_message_new_method_return(message);
+ if (reply == NULL) {
+ reply = dbus_message_new_error(message, DBUS_ERROR_NO_MEMORY,
+ NULL);
+ goto err;
+ }
+ if (!dbus_message_append_args(reply, DBUS_TYPE_OBJECT_PATH, &path,
+ DBUS_TYPE_INVALID)) {
+ dbus_message_unref(reply);
+ reply = dbus_message_new_error(message, DBUS_ERROR_NO_MEMORY,
+ NULL);
+ goto err;
+ }
+
+ return reply;
+
+err:
+ if (ssid) {
+ wpas_notify_persistent_group_removed(wpa_s, ssid);
+ wpa_config_remove_network(wpa_s->conf, ssid->id);
+ }
+ return reply;
+}
+
+
+/**
+ * wpas_dbus_handler_remove_persistent_group - Remove a configured persistent
+ * group
+ * @message: Pointer to incoming dbus message
+ * @wpa_s: wpa_supplicant structure for a network interface
+ * Returns: NULL on success or dbus error on failure
+ *
+ * Handler function for "RemovePersistentGroup" method call of a P2P Device
+ * interface.
+ */
+DBusMessage * wpas_dbus_handler_remove_persistent_group(
+ DBusMessage *message, struct wpa_supplicant *wpa_s)
+{
+ DBusMessage *reply = NULL;
+ const char *op;
+ char *iface = NULL, *persistent_group_id = NULL;
+ int id;
+ struct wpa_ssid *ssid;
+
+ dbus_message_get_args(message, NULL, DBUS_TYPE_OBJECT_PATH, &op,
+ DBUS_TYPE_INVALID);
+
+ /*
+ * Extract the network ID and ensure the network is actually a child of
+ * this interface.
+ */
+ iface = wpas_dbus_new_decompose_object_path(op, 1,
+ &persistent_group_id,
+ NULL);
+ if (iface == NULL || os_strcmp(iface, wpa_s->dbus_new_path) != 0) {
+ reply = wpas_dbus_error_invalid_args(message, op);
+ goto out;
+ }
+
+ id = strtoul(persistent_group_id, NULL, 10);
+ if (errno == EINVAL) {
+ reply = wpas_dbus_error_invalid_args(message, op);
+ goto out;
+ }
+
+ ssid = wpa_config_get_network(wpa_s->conf, id);
+ if (ssid == NULL) {
+ reply = wpas_dbus_error_persistent_group_unknown(message);
+ goto out;
+ }
+
+ wpas_notify_persistent_group_removed(wpa_s, ssid);
+
+ if (wpa_config_remove_network(wpa_s->conf, id) < 0) {
+ wpa_printf(MSG_ERROR, "dbus: %s: "
+ "error occurred when removing persistent group %d",
+ __func__, id);
+ reply = wpas_dbus_error_unknown_error(
+ message,
+ "error removing the specified persistent group on "
+ "this interface.");
+ goto out;
+ }
+
+out:
+ os_free(iface);
+ os_free(persistent_group_id);
+ return reply;
+}
+
+
+static void remove_persistent_group(struct wpa_supplicant *wpa_s,
+ struct wpa_ssid *ssid)
+{
+ wpas_notify_persistent_group_removed(wpa_s, ssid);
+
+ if (wpa_config_remove_network(wpa_s->conf, ssid->id) < 0) {
+ wpa_printf(MSG_ERROR, "dbus: %s: "
+ "error occurred when removing persistent group %d",
+ __func__, ssid->id);
+ return;
+ }
+}
+
+
+/**
+ * wpas_dbus_handler_remove_all_persistent_groups - Remove all configured
+ * persistent groups
+ * @message: Pointer to incoming dbus message
+ * @wpa_s: wpa_supplicant structure for a network interface
+ * Returns: NULL on success or dbus error on failure
+ *
+ * Handler function for "RemoveAllPersistentGroups" method call of a
+ * P2P Device interface.
+ */
+DBusMessage * wpas_dbus_handler_remove_all_persistent_groups(
+ DBusMessage *message, struct wpa_supplicant *wpa_s)
+{
+ struct wpa_ssid *ssid, *next;
+ struct wpa_config *config;
+
+ config = wpa_s->conf;
+ ssid = config->ssid;
+ while (ssid) {
+ next = ssid->next;
+ if (network_is_persistent_group(ssid))
+ remove_persistent_group(wpa_s, ssid);
+ ssid = next;
+ }
+ return NULL;
+}
+
+
+/*
+ * Group object properties accessor methods
+ */
+
+dbus_bool_t wpas_dbus_getter_p2p_group_members(DBusMessageIter *iter,
+ DBusError *error,
+ void *user_data)
+{
+ struct wpa_supplicant *wpa_s = user_data;
+ struct wpa_ssid *ssid;
+ unsigned int num_members;
+ char **paths;
+ unsigned int i;
+ void *next = NULL;
+ const u8 *addr;
+ dbus_bool_t success = FALSE;
+
+ /* Verify correct role for this property */
+ if (wpas_get_p2p_role(wpa_s) != WPAS_P2P_ROLE_GO) {
+ return wpas_dbus_simple_array_property_getter(
+ iter, DBUS_TYPE_OBJECT_PATH, NULL, 0, error);
+ }
+
+ ssid = wpa_s->conf->ssid;
+ /* At present WPAS P2P_GO mode only applicable for p2p_go */
+ if (ssid->mode != WPAS_MODE_P2P_GO &&
+ ssid->mode != WPAS_MODE_AP &&
+ ssid->mode != WPAS_MODE_P2P_GROUP_FORMATION)
+ return FALSE;
+
+ num_members = p2p_get_group_num_members(wpa_s->p2p_group);
+
+ paths = os_calloc(num_members, sizeof(char *));
+ if (!paths)
+ goto out_of_memory;
+
+ i = 0;
+ while ((addr = p2p_iterate_group_members(wpa_s->p2p_group, &next))) {
+ paths[i] = os_zalloc(WPAS_DBUS_OBJECT_PATH_MAX);
+ if (!paths[i])
+ goto out_of_memory;
+ os_snprintf(paths[i], WPAS_DBUS_OBJECT_PATH_MAX,
+ "%s/" WPAS_DBUS_NEW_P2P_GROUPMEMBERS_PART
+ "/" COMPACT_MACSTR,
+ wpa_s->dbus_groupobj_path, MAC2STR(addr));
+ i++;
+ }
+
+ success = wpas_dbus_simple_array_property_getter(iter,
+ DBUS_TYPE_OBJECT_PATH,
+ paths, num_members,
+ error);
+
+ for (i = 0; i < num_members; i++)
+ os_free(paths[i]);
+ os_free(paths);
+ return success;
+
+out_of_memory:
+ dbus_set_error_const(error, DBUS_ERROR_NO_MEMORY, "no memory");
+ if (paths) {
+ for (i = 0; i < num_members; i++)
+ os_free(paths[i]);
+ os_free(paths);
+ }
+ return FALSE;
+}
+
+
+dbus_bool_t wpas_dbus_getter_p2p_group_ssid(DBusMessageIter *iter,
+ DBusError *error, void *user_data)
+{
+ struct wpa_supplicant *wpa_s = user_data;
+ if (wpa_s->current_ssid == NULL)
+ return FALSE;
+ return wpas_dbus_simple_array_property_getter(
+ iter, DBUS_TYPE_BYTE, wpa_s->current_ssid->ssid,
+ wpa_s->current_ssid->ssid_len, error);
+}
+
+
+dbus_bool_t wpas_dbus_getter_p2p_group_bssid(DBusMessageIter *iter,
+ DBusError *error,
+ void *user_data)
+{
+ struct wpa_supplicant *wpa_s = user_data;
+ u8 role = wpas_get_p2p_role(wpa_s);
+ u8 *p_bssid;
+
+ if (role == WPAS_P2P_ROLE_CLIENT) {
+ if (wpa_s->current_ssid == NULL)
+ return FALSE;
+ p_bssid = wpa_s->current_ssid->bssid;
+ } else {
+ if (wpa_s->ap_iface == NULL)
+ return FALSE;
+ p_bssid = wpa_s->ap_iface->bss[0]->own_addr;
+ }
+
+ return wpas_dbus_simple_array_property_getter(iter, DBUS_TYPE_BYTE,
+ p_bssid, ETH_ALEN,
+ error);
+}
+
+
+dbus_bool_t wpas_dbus_getter_p2p_group_frequency(DBusMessageIter *iter,
+ DBusError *error,
+ void *user_data)
+{
+ struct wpa_supplicant *wpa_s = user_data;
+ u16 op_freq;
+ u8 role = wpas_get_p2p_role(wpa_s);
+
+ if (role == WPAS_P2P_ROLE_CLIENT) {
+ if (wpa_s->go_params == NULL)
+ return FALSE;
+ op_freq = wpa_s->go_params->freq;
+ } else {
+ if (wpa_s->ap_iface == NULL)
+ return FALSE;
+ op_freq = wpa_s->ap_iface->freq;
+ }
+
+ return wpas_dbus_simple_property_getter(iter, DBUS_TYPE_UINT16,
+ &op_freq, error);
+}
+
+
+dbus_bool_t wpas_dbus_getter_p2p_group_passphrase(DBusMessageIter *iter,
+ DBusError *error,
+ void *user_data)
+{
+ struct wpa_supplicant *wpa_s = user_data;
+ u8 role = wpas_get_p2p_role(wpa_s);
+ char *p_pass = NULL;
+
+ /* Verify correct role for this property */
+ if (role == WPAS_P2P_ROLE_GO) {
+ if (wpa_s->current_ssid == NULL)
+ return FALSE;
+ p_pass = wpa_s->current_ssid->passphrase;
+ } else
+ p_pass = "";
+
+ return wpas_dbus_simple_property_getter(iter, DBUS_TYPE_STRING,
+ &p_pass, error);
+
+}
+
+
+dbus_bool_t wpas_dbus_getter_p2p_group_psk(DBusMessageIter *iter,
+ DBusError *error, void *user_data)
+{
+ struct wpa_supplicant *wpa_s = user_data;
+ u8 role = wpas_get_p2p_role(wpa_s);
+ u8 *p_psk = NULL;
+ u8 psk_len = 0;
+
+ /* Verify correct role for this property */
+ if (role == WPAS_P2P_ROLE_CLIENT) {
+ if (wpa_s->current_ssid == NULL)
+ return FALSE;
+ p_psk = wpa_s->current_ssid->psk;
+ psk_len = 32;
+ }
+
+ return wpas_dbus_simple_array_property_getter(iter, DBUS_TYPE_BYTE,
+ &p_psk, psk_len, error);
+}
+
+
+dbus_bool_t wpas_dbus_getter_p2p_group_vendor_ext(DBusMessageIter *iter,
+ DBusError *error,
+ void *user_data)
+{
+ struct wpa_supplicant *wpa_s = user_data;
+ struct hostapd_data *hapd;
+ struct wpabuf *vendor_ext[MAX_WPS_VENDOR_EXTENSIONS];
+ int num_vendor_ext = 0;
+ int i;
+
+ /* Verify correct role for this property */
+ if (wpas_get_p2p_role(wpa_s) == WPAS_P2P_ROLE_GO) {
+ if (wpa_s->ap_iface == NULL)
+ return FALSE;
+ hapd = wpa_s->ap_iface->bss[0];
+
+ /* Parse WPS Vendor Extensions sent in Beacon/Probe Response */
+ for (i = 0; i < MAX_WPS_VENDOR_EXTENSIONS; i++) {
+ if (hapd->conf->wps_vendor_ext[i] == NULL)
+ vendor_ext[i] = NULL;
+ else {
+ vendor_ext[num_vendor_ext++] =
+ hapd->conf->wps_vendor_ext[i];
+ }
+ }
+ }
+
+ /* Return vendor extensions or no data */
+ return wpas_dbus_simple_array_array_property_getter(iter,
+ DBUS_TYPE_BYTE,
+ vendor_ext,
+ num_vendor_ext,
+ error);
+}
+
+
+dbus_bool_t wpas_dbus_setter_p2p_group_vendor_ext(DBusMessageIter *iter,
+ DBusError *error,
+ void *user_data)
+{
+ struct wpa_supplicant *wpa_s = user_data;
+ DBusMessageIter variant_iter, iter_dict;
+ struct wpa_dbus_dict_entry entry = { .type = DBUS_TYPE_STRING };
+ unsigned int i;
+ struct hostapd_data *hapd = NULL;
+
+ if (wpas_get_p2p_role(wpa_s) == WPAS_P2P_ROLE_GO &&
+ wpa_s->ap_iface != NULL)
+ hapd = wpa_s->ap_iface->bss[0];
+ else
+ return FALSE;
+
+ dbus_message_iter_recurse(iter, &variant_iter);
+ if (!wpa_dbus_dict_open_read(&variant_iter, &iter_dict, error))
+ return FALSE;
+
+ while (wpa_dbus_dict_has_dict_entry(&iter_dict)) {
+ if (!wpa_dbus_dict_get_entry(&iter_dict, &entry)) {
+ dbus_set_error_const(error, DBUS_ERROR_INVALID_ARGS,
+ "invalid message format");
+ return FALSE;
+ }
+
+ if (os_strcmp(entry.key, "WPSVendorExtensions") == 0) {
+ if (entry.type != DBUS_TYPE_ARRAY ||
+ entry.array_type != WPAS_DBUS_TYPE_BINARRAY ||
+ entry.array_len > MAX_WPS_VENDOR_EXTENSIONS)
+ goto error;
+
+ for (i = 0; i < MAX_WPS_VENDOR_EXTENSIONS; i++) {
+ if (i < entry.array_len) {
+ hapd->conf->wps_vendor_ext[i] =
+ entry.binarray_value[i];
+ entry.binarray_value[i] = NULL;
+ } else
+ hapd->conf->wps_vendor_ext[i] = NULL;
+ }
+
+ hostapd_update_wps(hapd);
+ } else
+ goto error;
+
+ wpa_dbus_dict_entry_clear(&entry);
+ }
+
+ return TRUE;
+
+error:
+ wpa_dbus_dict_entry_clear(&entry);
+ dbus_set_error_const(error, DBUS_ERROR_INVALID_ARGS,
+ "invalid message format");
+ return FALSE;
+}
+
+
+DBusMessage * wpas_dbus_handler_p2p_add_service(DBusMessage *message,
+ struct wpa_supplicant *wpa_s)
+{
+ DBusMessageIter iter_dict;
+ DBusMessage *reply = NULL;
+ DBusMessageIter iter;
+ struct wpa_dbus_dict_entry entry;
+ int upnp = 0;
+ int bonjour = 0;
+ char *service = NULL;
+ struct wpabuf *query = NULL;
+ struct wpabuf *resp = NULL;
+ u8 version = 0;
+
+ dbus_message_iter_init(message, &iter);
+
+ if (!wpa_dbus_dict_open_read(&iter, &iter_dict, NULL))
+ goto error;
+
+ while (wpa_dbus_dict_has_dict_entry(&iter_dict)) {
+ if (!wpa_dbus_dict_get_entry(&iter_dict, &entry))
+ goto error;
+
+ if (!os_strcmp(entry.key, "service_type") &&
+ (entry.type == DBUS_TYPE_STRING)) {
+ if (!os_strcmp(entry.str_value, "upnp"))
+ upnp = 1;
+ else if (!os_strcmp(entry.str_value, "bonjour"))
+ bonjour = 1;
+ else
+ goto error_clear;
+ } else if (!os_strcmp(entry.key, "version") &&
+ entry.type == DBUS_TYPE_INT32) {
+ version = entry.uint32_value;
+ } else if (!os_strcmp(entry.key, "service") &&
+ (entry.type == DBUS_TYPE_STRING)) {
+ service = os_strdup(entry.str_value);
+ } else if (!os_strcmp(entry.key, "query")) {
+ if ((entry.type != DBUS_TYPE_ARRAY) ||
+ (entry.array_type != DBUS_TYPE_BYTE))
+ goto error_clear;
+ query = wpabuf_alloc_copy(
+ entry.bytearray_value,
+ entry.array_len);
+ } else if (!os_strcmp(entry.key, "response")) {
+ if ((entry.type != DBUS_TYPE_ARRAY) ||
+ (entry.array_type != DBUS_TYPE_BYTE))
+ goto error_clear;
+ resp = wpabuf_alloc_copy(entry.bytearray_value,
+ entry.array_len);
+ }
+ wpa_dbus_dict_entry_clear(&entry);
+ }
+
+ if (upnp == 1) {
+ if (version <= 0 || service == NULL)
+ goto error;
+
+ if (wpas_p2p_service_add_upnp(wpa_s, version, service) != 0)
+ goto error;
+
+ os_free(service);
+ service = NULL;
+ } else if (bonjour == 1) {
+ if (query == NULL || resp == NULL)
+ goto error;
+
+ if (wpas_p2p_service_add_bonjour(wpa_s, query, resp) < 0)
+ goto error;
+ query = NULL;
+ resp = NULL;
+ } else
+ goto error;
+
+ return reply;
+error_clear:
+ wpa_dbus_dict_entry_clear(&entry);
+error:
+ os_free(service);
+ wpabuf_free(query);
+ wpabuf_free(resp);
+ return wpas_dbus_error_invalid_args(message, NULL);
+}
+
+
+DBusMessage * wpas_dbus_handler_p2p_delete_service(
+ DBusMessage *message, struct wpa_supplicant *wpa_s)
+{
+ DBusMessageIter iter_dict;
+ DBusMessage *reply = NULL;
+ DBusMessageIter iter;
+ struct wpa_dbus_dict_entry entry;
+ int upnp = 0;
+ int bonjour = 0;
+ int ret = 0;
+ char *service = NULL;
+ struct wpabuf *query = NULL;
+ u8 version = 0;
+
+ dbus_message_iter_init(message, &iter);
+
+ if (!wpa_dbus_dict_open_read(&iter, &iter_dict, NULL))
+ goto error;
+
+ if (wpa_dbus_dict_has_dict_entry(&iter_dict)) {
+ if (!wpa_dbus_dict_get_entry(&iter_dict, &entry))
+ goto error;
+
+ if (!os_strcmp(entry.key, "service_type") &&
+ (entry.type == DBUS_TYPE_STRING)) {
+ if (!os_strcmp(entry.str_value, "upnp"))
+ upnp = 1;
+ else if (!os_strcmp(entry.str_value, "bonjour"))
+ bonjour = 1;
+ else
+ goto error_clear;
+ wpa_dbus_dict_entry_clear(&entry);
+ }
+ }
+ if (upnp == 1) {
+ while (wpa_dbus_dict_has_dict_entry(&iter_dict)) {
+ if (!wpa_dbus_dict_get_entry(&iter_dict, &entry))
+ goto error;
+ if (!os_strcmp(entry.key, "version") &&
+ entry.type == DBUS_TYPE_INT32)
+ version = entry.uint32_value;
+ else if (!os_strcmp(entry.key, "service") &&
+ entry.type == DBUS_TYPE_STRING)
+ service = os_strdup(entry.str_value);
+ else
+ goto error_clear;
+
+ wpa_dbus_dict_entry_clear(&entry);
+ }
+
+ if (version <= 0 || service == NULL)
+ goto error;
+
+ ret = wpas_p2p_service_del_upnp(wpa_s, version, service);
+ os_free(service);
+ if (ret != 0)
+ goto error;
+ } else if (bonjour == 1) {
+ while (wpa_dbus_dict_has_dict_entry(&iter_dict)) {
+ if (!wpa_dbus_dict_get_entry(&iter_dict, &entry))
+ goto error;
+
+ if (!os_strcmp(entry.key, "query")) {
+ if ((entry.type != DBUS_TYPE_ARRAY) ||
+ (entry.array_type != DBUS_TYPE_BYTE))
+ goto error_clear;
+ query = wpabuf_alloc_copy(
+ entry.bytearray_value,
+ entry.array_len);
+ } else
+ goto error_clear;
+
+ wpa_dbus_dict_entry_clear(&entry);
+ }
+
+ if (query == NULL)
+ goto error;
+
+ ret = wpas_p2p_service_del_bonjour(wpa_s, query);
+ if (ret != 0)
+ goto error;
+ wpabuf_free(query);
+ } else
+ goto error;
+
+ return reply;
+error_clear:
+ wpa_dbus_dict_entry_clear(&entry);
+error:
+ return wpas_dbus_error_invalid_args(message, NULL);
+}
+
+
+DBusMessage * wpas_dbus_handler_p2p_flush_service(DBusMessage *message,
+ struct wpa_supplicant *wpa_s)
+{
+ wpas_p2p_service_flush(wpa_s);
+ return NULL;
+}
+
+
+DBusMessage * wpas_dbus_handler_p2p_service_sd_req(
+ DBusMessage *message, struct wpa_supplicant *wpa_s)
+{
+ DBusMessageIter iter_dict;
+ DBusMessage *reply = NULL;
+ DBusMessageIter iter;
+ struct wpa_dbus_dict_entry entry;
+ int upnp = 0;
+ char *service = NULL;
+ char *peer_object_path = NULL;
+ struct wpabuf *tlv = NULL;
+ u8 version = 0;
+ u64 ref = 0;
+ u8 addr_buf[ETH_ALEN], *addr;
+
+ dbus_message_iter_init(message, &iter);
+
+ if (!wpa_dbus_dict_open_read(&iter, &iter_dict, NULL))
+ goto error;
+
+ while (wpa_dbus_dict_has_dict_entry(&iter_dict)) {
+ if (!wpa_dbus_dict_get_entry(&iter_dict, &entry))
+ goto error;
+ if (!os_strcmp(entry.key, "peer_object") &&
+ entry.type == DBUS_TYPE_OBJECT_PATH) {
+ peer_object_path = os_strdup(entry.str_value);
+ } else if (!os_strcmp(entry.key, "service_type") &&
+ entry.type == DBUS_TYPE_STRING) {
+ if (!os_strcmp(entry.str_value, "upnp"))
+ upnp = 1;
+ else
+ goto error_clear;
+ } else if (!os_strcmp(entry.key, "version") &&
+ entry.type == DBUS_TYPE_INT32) {
+ version = entry.uint32_value;
+ } else if (!os_strcmp(entry.key, "service") &&
+ entry.type == DBUS_TYPE_STRING) {
+ service = os_strdup(entry.str_value);
+ } else if (!os_strcmp(entry.key, "tlv")) {
+ if (entry.type != DBUS_TYPE_ARRAY ||
+ entry.array_type != DBUS_TYPE_BYTE)
+ goto error_clear;
+ tlv = wpabuf_alloc_copy(entry.bytearray_value,
+ entry.array_len);
+ } else
+ goto error_clear;
+
+ wpa_dbus_dict_entry_clear(&entry);
+ }
+
+ if (!peer_object_path) {
+ addr = NULL;
+ } else {
+ if (parse_peer_object_path(peer_object_path, addr_buf) < 0 ||
+ !p2p_peer_known(wpa_s->global->p2p, addr_buf))
+ goto error;
+
+ addr = addr_buf;
+ }
+
+ if (upnp == 1) {
+ if (version <= 0 || service == NULL)
+ goto error;
+
+ ref = wpas_p2p_sd_request_upnp(wpa_s, addr, version, service);
+ } else {
+ if (tlv == NULL)
+ goto error;
+ ref = wpas_p2p_sd_request(wpa_s, addr, tlv);
+ wpabuf_free(tlv);
+ }
+
+ if (ref != 0) {
+ reply = dbus_message_new_method_return(message);
+ dbus_message_append_args(reply, DBUS_TYPE_UINT64,
+ &ref, DBUS_TYPE_INVALID);
+ } else {
+ reply = wpas_dbus_error_unknown_error(
+ message, "Unable to send SD request");
+ }
+out:
+ os_free(service);
+ os_free(peer_object_path);
+ return reply;
+error_clear:
+ wpa_dbus_dict_entry_clear(&entry);
+error:
+ if (tlv)
+ wpabuf_free(tlv);
+ reply = wpas_dbus_error_invalid_args(message, NULL);
+ goto out;
+}
+
+
+DBusMessage * wpas_dbus_handler_p2p_service_sd_res(
+ DBusMessage *message, struct wpa_supplicant *wpa_s)
+{
+ DBusMessageIter iter_dict;
+ DBusMessage *reply = NULL;
+ DBusMessageIter iter;
+ struct wpa_dbus_dict_entry entry;
+ char *peer_object_path = NULL;
+ struct wpabuf *tlv = NULL;
+ int freq = 0;
+ int dlg_tok = 0;
+ u8 addr[ETH_ALEN];
+
+ dbus_message_iter_init(message, &iter);
+
+ if (!wpa_dbus_dict_open_read(&iter, &iter_dict, NULL))
+ goto error;
+
+ while (wpa_dbus_dict_has_dict_entry(&iter_dict)) {
+ if (!wpa_dbus_dict_get_entry(&iter_dict, &entry))
+ goto error;
+
+ if (!os_strcmp(entry.key, "peer_object") &&
+ entry.type == DBUS_TYPE_OBJECT_PATH) {
+ peer_object_path = os_strdup(entry.str_value);
+ } else if (!os_strcmp(entry.key, "frequency") &&
+ entry.type == DBUS_TYPE_INT32) {
+ freq = entry.uint32_value;
+ } else if (!os_strcmp(entry.key, "dialog_token") &&
+ entry.type == DBUS_TYPE_UINT32) {
+ dlg_tok = entry.uint32_value;
+ } else if (!os_strcmp(entry.key, "tlvs")) {
+ if (entry.type != DBUS_TYPE_ARRAY ||
+ entry.array_type != DBUS_TYPE_BYTE)
+ goto error_clear;
+ tlv = wpabuf_alloc_copy(entry.bytearray_value,
+ entry.array_len);
+ } else
+ goto error_clear;
+
+ wpa_dbus_dict_entry_clear(&entry);
+ }
+ if (!peer_object_path ||
+ (parse_peer_object_path(peer_object_path, addr) < 0) ||
+ !p2p_peer_known(wpa_s->global->p2p, addr))
+ goto error;
+
+ if (tlv == NULL)
+ goto error;
+
+ wpas_p2p_sd_response(wpa_s, freq, addr, (u8) dlg_tok, tlv);
+ wpabuf_free(tlv);
+out:
+ os_free(peer_object_path);
+ return reply;
+error_clear:
+ wpa_dbus_dict_entry_clear(&entry);
+error:
+ reply = wpas_dbus_error_invalid_args(message, NULL);
+ goto out;
+}
+
+
+DBusMessage * wpas_dbus_handler_p2p_service_sd_cancel_req(
+ DBusMessage *message, struct wpa_supplicant *wpa_s)
+{
+ DBusMessageIter iter;
+ u64 req = 0;
+
+ dbus_message_iter_init(message, &iter);
+ dbus_message_iter_get_basic(&iter, &req);
+
+ if (req == 0)
+ goto error;
+
+ if (!wpas_p2p_sd_cancel_request(wpa_s, req))
+ goto error;
+
+ return NULL;
+error:
+ return wpas_dbus_error_invalid_args(message, NULL);
+}
+
+
+DBusMessage * wpas_dbus_handler_p2p_service_update(
+ DBusMessage *message, struct wpa_supplicant *wpa_s)
+{
+ wpas_p2p_sd_service_update(wpa_s);
+ return NULL;
+}
+
+
+DBusMessage * wpas_dbus_handler_p2p_serv_disc_external(
+ DBusMessage *message, struct wpa_supplicant *wpa_s)
+{
+ DBusMessageIter iter;
+ int ext = 0;
+
+ dbus_message_iter_init(message, &iter);
+ dbus_message_iter_get_basic(&iter, &ext);
+
+ wpa_s->p2p_sd_over_ctrl_iface = ext;
+
+ return NULL;
+
+}
diff --git a/contrib/wpa/wpa_supplicant/dbus/dbus_new_handlers_p2p.h b/contrib/wpa/wpa_supplicant/dbus/dbus_new_handlers_p2p.h
new file mode 100644
index 0000000..a11b3c8
--- /dev/null
+++ b/contrib/wpa/wpa_supplicant/dbus/dbus_new_handlers_p2p.h
@@ -0,0 +1,211 @@
+/*
+ * WPA Supplicant / dbus-based control interface for p2p
+ * Copyright (c) 2011-2012, Intel Corporation
+ *
+ * This software may be distributed under the terms of the BSD license.
+ * See README for more details.
+ */
+
+#ifndef DBUS_NEW_HANDLERS_P2P_H
+#define DBUS_NEW_HANDLERS_P2P_H
+
+struct peer_handler_args {
+ struct wpa_supplicant *wpa_s;
+ u8 p2p_device_addr[ETH_ALEN];
+};
+
+struct groupmember_handler_args {
+ struct wpa_supplicant *wpa_s;
+ u8 member_addr[ETH_ALEN];
+};
+
+/*
+ * P2P Device methods
+ */
+
+DBusMessage *wpas_dbus_handler_p2p_find(
+ DBusMessage *message, struct wpa_supplicant *wpa_s);
+
+DBusMessage *wpas_dbus_handler_p2p_stop_find(
+ DBusMessage *message, struct wpa_supplicant *wpa_s);
+
+DBusMessage *wpas_dbus_handler_p2p_rejectpeer(
+ DBusMessage *message, struct wpa_supplicant *wpa_s);
+
+DBusMessage *wpas_dbus_handler_p2p_listen(
+ DBusMessage *message, struct wpa_supplicant *wpa_s);
+
+DBusMessage *wpas_dbus_handler_p2p_extendedlisten(
+ DBusMessage *message, struct wpa_supplicant *wpa_s);
+
+DBusMessage *wpas_dbus_handler_p2p_presence_request(
+ DBusMessage *message, struct wpa_supplicant *wpa_s);
+
+DBusMessage *wpas_dbus_handler_p2p_prov_disc_req(
+ DBusMessage *message, struct wpa_supplicant *wpa_s);
+
+DBusMessage *wpas_dbus_handler_p2p_group_add(
+ DBusMessage *message, struct wpa_supplicant *wpa_s);
+
+DBusMessage *wpas_dbus_handler_p2p_connect(
+ DBusMessage *message,
+ struct wpa_supplicant *wpa_s);
+
+DBusMessage *wpas_dbus_handler_p2p_invite(
+ DBusMessage *message,
+ struct wpa_supplicant *wpa_s);
+
+DBusMessage *wpas_dbus_handler_p2p_disconnect(
+ DBusMessage *message, struct wpa_supplicant *wpa_s);
+
+DBusMessage *wpas_dbus_handler_p2p_flush(
+ DBusMessage *message, struct wpa_supplicant *wpa_s);
+
+DBusMessage *wpas_dbus_handler_p2p_add_service(
+ DBusMessage *message, struct wpa_supplicant *wpa_s);
+
+DBusMessage *wpas_dbus_handler_p2p_delete_service(
+ DBusMessage *message, struct wpa_supplicant *wpa_s);
+
+DBusMessage *wpas_dbus_handler_p2p_flush_service(
+ DBusMessage *message, struct wpa_supplicant *wpa_s);
+
+DBusMessage *wpas_dbus_handler_p2p_service_sd_req(
+ DBusMessage *message, struct wpa_supplicant *wpa_s);
+
+DBusMessage *wpas_dbus_handler_p2p_service_sd_res(
+ DBusMessage *message, struct wpa_supplicant *wpa_s);
+
+DBusMessage *wpas_dbus_handler_p2p_service_sd_cancel_req(
+ DBusMessage *message, struct wpa_supplicant *wpa_s);
+
+DBusMessage *wpas_dbus_handler_p2p_service_update(
+ DBusMessage *message, struct wpa_supplicant *wpa_s);
+
+DBusMessage *wpas_dbus_handler_p2p_serv_disc_external(
+ DBusMessage *message, struct wpa_supplicant *wpa_s);
+
+/*
+ * P2P Device property accessor methods.
+ */
+dbus_bool_t wpas_dbus_setter_p2p_device_config(DBusMessageIter *iter,
+ DBusError *error,
+ void *user_data);
+
+dbus_bool_t wpas_dbus_getter_p2p_device_config(DBusMessageIter *iter,
+ DBusError *error,
+ void *user_data);
+
+dbus_bool_t wpas_dbus_getter_p2p_peers(DBusMessageIter *iter, DBusError *error,
+ void *user_data);
+
+dbus_bool_t wpas_dbus_getter_p2p_role(DBusMessageIter *iter, DBusError *error,
+ void *user_data);
+
+dbus_bool_t wpas_dbus_getter_p2p_group(DBusMessageIter *iter, DBusError *error,
+ void *user_data);
+
+dbus_bool_t wpas_dbus_getter_p2p_peergo(DBusMessageIter *iter,
+ DBusError *error,
+ void *user_data);
+
+/*
+ * P2P Peer properties.
+ */
+
+dbus_bool_t wpas_dbus_getter_p2p_peer_device_name(DBusMessageIter *iter,
+ DBusError *error,
+ void *user_data);
+
+dbus_bool_t wpas_dbus_getter_p2p_peer_primary_device_type(
+ DBusMessageIter *iter, DBusError *error, void *user_data);
+
+dbus_bool_t wpas_dbus_getter_p2p_peer_config_method(DBusMessageIter *iter,
+ DBusError *error,
+ void *user_data);
+
+dbus_bool_t wpas_dbus_getter_p2p_peer_level(DBusMessageIter *iter,
+ DBusError *error,
+ void *user_data);
+
+dbus_bool_t wpas_dbus_getter_p2p_peer_device_capability(DBusMessageIter *iter,
+ DBusError *error,
+ void *user_data);
+
+dbus_bool_t wpas_dbus_getter_p2p_peer_group_capability(DBusMessageIter *iter,
+ DBusError *error,
+ void *user_data);
+
+dbus_bool_t wpas_dbus_getter_p2p_peer_secondary_device_types(
+ DBusMessageIter *iter, DBusError *error, void *user_data);
+
+dbus_bool_t wpas_dbus_getter_p2p_peer_vendor_extension(DBusMessageIter *iter,
+ DBusError *error,
+ void *user_data);
+
+dbus_bool_t wpas_dbus_getter_p2p_peer_ies(DBusMessageIter *iter,
+ DBusError *error,
+ void *user_data);
+
+/*
+ * P2P Group properties
+ */
+
+dbus_bool_t wpas_dbus_getter_p2p_group_members(DBusMessageIter *iter,
+ DBusError *error,
+ void *user_data);
+
+dbus_bool_t wpas_dbus_getter_p2p_group_ssid(DBusMessageIter *iter,
+ DBusError *error,
+ void *user_data);
+
+dbus_bool_t wpas_dbus_getter_p2p_group_bssid(DBusMessageIter *iter,
+ DBusError *error,
+ void *user_data);
+
+dbus_bool_t wpas_dbus_getter_p2p_group_frequency(DBusMessageIter *iter,
+ DBusError *error,
+ void *user_data);
+
+dbus_bool_t wpas_dbus_getter_p2p_group_passphrase(DBusMessageIter *iter,
+ DBusError *error,
+ void *user_data);
+
+dbus_bool_t wpas_dbus_getter_p2p_group_psk(DBusMessageIter *iter,
+ DBusError *error,
+ void *user_data);
+
+dbus_bool_t wpas_dbus_getter_p2p_group_vendor_ext(DBusMessageIter *iter,
+ DBusError *error,
+ void *user_data);
+
+dbus_bool_t wpas_dbus_setter_p2p_group_vendor_ext(DBusMessageIter *iter,
+ DBusError *error,
+ void *user_data);
+
+/*
+ * P2P Persistent Groups and properties
+ */
+
+dbus_bool_t wpas_dbus_getter_persistent_groups(DBusMessageIter *iter,
+ DBusError *error,
+ void *user_data);
+
+dbus_bool_t wpas_dbus_getter_persistent_group_properties(DBusMessageIter *iter,
+ DBusError *error, void *user_data);
+
+dbus_bool_t wpas_dbus_setter_persistent_group_properties(DBusMessageIter *iter,
+ DBusError *error,
+ void *user_data);
+
+DBusMessage * wpas_dbus_handler_add_persistent_group(
+ DBusMessage *message, struct wpa_supplicant *wpa_s);
+
+DBusMessage * wpas_dbus_handler_remove_persistent_group(
+ DBusMessage *message, struct wpa_supplicant *wpa_s);
+
+DBusMessage * wpas_dbus_handler_remove_all_persistent_groups(
+ DBusMessage *message, struct wpa_supplicant *wpa_s);
+
+
+#endif /* DBUS_NEW_HANDLERS_P2P_H */
diff --git a/contrib/wpa/wpa_supplicant/dbus/dbus_new_handlers_wps.c b/contrib/wpa/wpa_supplicant/dbus/dbus_new_handlers_wps.c
index dc44a59..4ad5e7e 100644
--- a/contrib/wpa/wpa_supplicant/dbus/dbus_new_handlers_wps.c
+++ b/contrib/wpa/wpa_supplicant/dbus/dbus_new_handlers_wps.c
@@ -3,14 +3,8 @@
* Copyright (c) 2006, Dan Williams <dcbw@redhat.com> and Red Hat, Inc.
* Copyright (c) 2009, Witold Sowa <witold.sowa@gmail.com>
*
- * This program is free software; you can redistribute it and/or modify
- * it under the terms of the GNU General Public License version 2 as
- * published by the Free Software Foundation.
- *
- * Alternatively, this software may be distributed under the terms of BSD
- * license.
- *
- * See README and COPYING for more details.
+ * This software may be distributed under the terms of the BSD license.
+ * See README for more details.
*/
#include "includes.h"
@@ -19,6 +13,8 @@
#include "../config.h"
#include "../wpa_supplicant_i.h"
#include "../wps_supplicant.h"
+#include "../driver_i.h"
+#include "../ap.h"
#include "dbus_new_helpers.h"
#include "dbus_new.h"
#include "dbus_new_handlers.h"
@@ -30,6 +26,7 @@ struct wps_start_params {
int type; /* 0 - not set, 1 - pin, 2 - pbc */
u8 *bssid;
char *pin;
+ u8 *p2p_dev_addr;
};
@@ -107,7 +104,7 @@ static int wpas_dbus_handler_wps_bssid(DBusMessage *message,
dbus_message_iter_recurse(entry_iter, &variant_iter);
if (dbus_message_iter_get_arg_type(&variant_iter) != DBUS_TYPE_ARRAY ||
dbus_message_iter_get_element_type(&variant_iter) !=
- DBUS_TYPE_ARRAY) {
+ DBUS_TYPE_BYTE) {
wpa_printf(MSG_DEBUG, "dbus: WPS.Start - Wrong Bssid type, "
"byte array required");
*reply = wpas_dbus_error_invalid_args(
@@ -148,6 +145,41 @@ static int wpas_dbus_handler_wps_pin(DBusMessage *message,
}
+#ifdef CONFIG_P2P
+static int wpas_dbus_handler_wps_p2p_dev_addr(DBusMessage *message,
+ DBusMessageIter *entry_iter,
+ struct wps_start_params *params,
+ DBusMessage **reply)
+{
+ DBusMessageIter variant_iter, array_iter;
+ int len;
+
+ dbus_message_iter_recurse(entry_iter, &variant_iter);
+ if (dbus_message_iter_get_arg_type(&variant_iter) != DBUS_TYPE_ARRAY ||
+ dbus_message_iter_get_element_type(&variant_iter) !=
+ DBUS_TYPE_BYTE) {
+ wpa_printf(MSG_DEBUG, "dbus: WPS.Start - Wrong "
+ "P2PDeviceAddress type, byte array required");
+ *reply = wpas_dbus_error_invalid_args(
+ message, "P2PDeviceAddress must be a byte array");
+ return -1;
+ }
+ dbus_message_iter_recurse(&variant_iter, &array_iter);
+ dbus_message_iter_get_fixed_array(&array_iter, &params->p2p_dev_addr,
+ &len);
+ if (len != ETH_ALEN) {
+ wpa_printf(MSG_DEBUG, "dbus: WPS.Start - Wrong "
+ "P2PDeviceAddress length %d", len);
+ *reply = wpas_dbus_error_invalid_args(message,
+ "P2PDeviceAddress "
+ "has wrong length");
+ return -1;
+ }
+ return 0;
+}
+#endif /* CONFIG_P2P */
+
+
static int wpas_dbus_handler_wps_start_entry(DBusMessage *message, char *key,
DBusMessageIter *entry_iter,
struct wps_start_params *params,
@@ -165,6 +197,11 @@ static int wpas_dbus_handler_wps_start_entry(DBusMessage *message, char *key,
else if (os_strcmp(key, "Pin") == 0)
return wpas_dbus_handler_wps_pin(message, entry_iter,
params, reply);
+#ifdef CONFIG_P2P
+ else if (os_strcmp(key, "P2PDeviceAddress") == 0)
+ return wpas_dbus_handler_wps_p2p_dev_addr(message, entry_iter,
+ params, reply);
+#endif /* CONFIG_P2P */
wpa_printf(MSG_DEBUG, "dbus: WPS.Start - unknown key %s", key);
*reply = wpas_dbus_error_invalid_args(message, key);
@@ -231,11 +268,31 @@ DBusMessage * wpas_dbus_handler_wps_start(DBusMessage *message,
ret = wpas_wps_start_reg(wpa_s, params.bssid, params.pin,
NULL);
else if (params.type == 1) {
- ret = wpas_wps_start_pin(wpa_s, params.bssid, params.pin);
- if (ret > 0)
- os_snprintf(npin, sizeof(npin), "%08d", ret);
- } else
- ret = wpas_wps_start_pbc(wpa_s, params.bssid);
+#ifdef CONFIG_AP
+ if (wpa_s->ap_iface)
+ ret = wpa_supplicant_ap_wps_pin(wpa_s,
+ params.bssid,
+ params.pin,
+ npin, sizeof(npin), 0);
+ else
+#endif /* CONFIG_AP */
+ {
+ ret = wpas_wps_start_pin(wpa_s, params.bssid,
+ params.pin, 0,
+ DEV_PW_DEFAULT);
+ if (ret > 0)
+ os_snprintf(npin, sizeof(npin), "%08d", ret);
+ }
+ } else {
+#ifdef CONFIG_AP
+ if (wpa_s->ap_iface)
+ ret = wpa_supplicant_ap_wps_pbc(wpa_s,
+ params.bssid,
+ params.p2p_dev_addr);
+ else
+#endif /* CONFIG_AP */
+ ret = wpas_wps_start_pbc(wpa_s, params.bssid, 0);
+ }
if (ret < 0) {
wpa_printf(MSG_DEBUG, "dbus: WPS.Start wpas_wps_failed in "
@@ -283,40 +340,43 @@ DBusMessage * wpas_dbus_handler_wps_start(DBusMessage *message,
* wpas_dbus_getter_process_credentials - Check if credentials are processed
* @message: Pointer to incoming dbus message
* @wpa_s: %wpa_supplicant data structure
- * Returns: DBus message with a boolean on success or DBus error on failure
+ * Returns: TRUE on success, FALSE on failure
*
* Getter for "ProcessCredentials" property. Returns returned boolean will be
* true if wps_cred_processing configuration field is not equal to 1 or false
* if otherwise.
*/
-DBusMessage * wpas_dbus_getter_process_credentials(
- DBusMessage *message, struct wpa_supplicant *wpa_s)
+dbus_bool_t wpas_dbus_getter_process_credentials(DBusMessageIter *iter,
+ DBusError *error,
+ void *user_data)
{
+ struct wpa_supplicant *wpa_s = user_data;
dbus_bool_t process = (wpa_s->conf->wps_cred_processing != 1);
- return wpas_dbus_simple_property_getter(message, DBUS_TYPE_BOOLEAN,
- &process);
+ return wpas_dbus_simple_property_getter(iter, DBUS_TYPE_BOOLEAN,
+ &process, error);
}
/**
* wpas_dbus_setter_process_credentials - Set credentials_processed conf param
- * @message: Pointer to incoming dbus message
- * @wpa_s: %wpa_supplicant data structure
- * Returns: NULL on success or DBus error on failure
+ * @iter: Pointer to incoming dbus message iter
+ * @error: Location to store error on failure
+ * @user_data: Function specific data
+ * Returns: TRUE on success, FALSE on failure
*
* Setter for "ProcessCredentials" property. Sets credentials_processed on 2
* if boolean argument is true or on 1 if otherwise.
*/
-DBusMessage * wpas_dbus_setter_process_credentials(
- DBusMessage *message, struct wpa_supplicant *wpa_s)
+dbus_bool_t wpas_dbus_setter_process_credentials(DBusMessageIter *iter,
+ DBusError *error,
+ void *user_data)
{
- DBusMessage *reply = NULL;
+ struct wpa_supplicant *wpa_s = user_data;
dbus_bool_t process_credentials, old_pc;
- reply = wpas_dbus_simple_property_setter(message, DBUS_TYPE_BOOLEAN,
- &process_credentials);
- if (reply)
- return reply;
+ if (!wpas_dbus_simple_property_setter(iter, error, DBUS_TYPE_BOOLEAN,
+ &process_credentials))
+ return FALSE;
old_pc = (wpa_s->conf->wps_cred_processing != 1);
wpa_s->conf->wps_cred_processing = (process_credentials ? 2 : 1);
@@ -327,5 +387,5 @@ DBusMessage * wpas_dbus_setter_process_credentials(
WPAS_DBUS_NEW_IFACE_WPS,
"ProcessCredentials");
- return NULL;
+ return TRUE;
}
diff --git a/contrib/wpa/wpa_supplicant/dbus/dbus_new_helpers.c b/contrib/wpa/wpa_supplicant/dbus/dbus_new_helpers.c
index 06749db..cfa6a15 100644
--- a/contrib/wpa/wpa_supplicant/dbus/dbus_new_helpers.c
+++ b/contrib/wpa/wpa_supplicant/dbus/dbus_new_helpers.c
@@ -3,14 +3,8 @@
* Copyright (c) 2006, Dan Williams <dcbw@redhat.com> and Red Hat, Inc.
* Copyright (c) 2009, Witold Sowa <witold.sowa@gmail.com>
*
- * This program is free software; you can redistribute it and/or modify
- * it under the terms of the GNU General Public License version 2 as
- * published by the Free Software Foundation.
- *
- * Alternatively, this software may be distributed under the terms of BSD
- * license.
- *
- * See README and COPYING for more details.
+ * This software may be distributed under the terms of the BSD license.
+ * See README for more details.
*/
#include "utils/includes.h"
@@ -21,112 +15,50 @@
#include "dbus_common_i.h"
#include "dbus_new.h"
#include "dbus_new_helpers.h"
+#include "dbus_dict_helpers.h"
-/**
- * recursive_iter_copy - Reads arguments from one iterator and
- * writes to another recursively
- * @from: iterator to read from
- * @to: iterator to write to
- *
- * Copies one iterator's elements to another. If any element in
- * iterator is of container type, its content is copied recursively
- */
-static void recursive_iter_copy(DBusMessageIter *from, DBusMessageIter *to)
+static dbus_bool_t fill_dict_with_properties(
+ DBusMessageIter *dict_iter,
+ const struct wpa_dbus_property_desc *props,
+ const char *interface, void *user_data, DBusError *error)
{
-
- char *subtype = NULL;
- int type;
-
- /* iterate over iterator to copy */
- while ((type = dbus_message_iter_get_arg_type(from)) !=
- DBUS_TYPE_INVALID) {
-
- /* simply copy basic type entries */
- if (dbus_type_is_basic(type)) {
- if (dbus_type_is_fixed(type)) {
- /*
- * According to DBus documentation all
- * fixed-length types are guaranteed to fit
- * 8 bytes
- */
- dbus_uint64_t v;
- dbus_message_iter_get_basic(from, &v);
- dbus_message_iter_append_basic(to, type, &v);
- } else {
- char *v;
- dbus_message_iter_get_basic(from, &v);
- dbus_message_iter_append_basic(to, type, &v);
- }
- } else {
- /* recursively copy container type entries */
- DBusMessageIter write_subiter, read_subiter;
-
- dbus_message_iter_recurse(from, &read_subiter);
-
- if (type == DBUS_TYPE_VARIANT ||
- type == DBUS_TYPE_ARRAY) {
- subtype = dbus_message_iter_get_signature(
- &read_subiter);
- }
-
- dbus_message_iter_open_container(to, type, subtype,
- &write_subiter);
-
- recursive_iter_copy(&read_subiter, &write_subiter);
-
- dbus_message_iter_close_container(to, &write_subiter);
- if (subtype)
- dbus_free(subtype);
- }
-
- dbus_message_iter_next(from);
- }
-}
-
-
-static unsigned int fill_dict_with_properties(
- DBusMessageIter *dict_iter, const struct wpa_dbus_property_desc *props,
- const char *interface, const void *user_data)
-{
- DBusMessage *reply;
- DBusMessageIter entry_iter, ret_iter;
- unsigned int counter = 0;
+ DBusMessageIter entry_iter;
const struct wpa_dbus_property_desc *dsc;
for (dsc = props; dsc && dsc->dbus_property; dsc++) {
- if (!os_strncmp(dsc->dbus_interface, interface,
- WPAS_DBUS_INTERFACE_MAX) &&
- dsc->access != W && dsc->getter) {
- reply = dsc->getter(NULL, user_data);
- if (!reply)
- continue;
-
- if (dbus_message_get_type(reply) ==
- DBUS_MESSAGE_TYPE_ERROR) {
- dbus_message_unref(reply);
- continue;
- }
+ /* Only return properties for the requested D-Bus interface */
+ if (os_strncmp(dsc->dbus_interface, interface,
+ WPAS_DBUS_INTERFACE_MAX) != 0)
+ continue;
- dbus_message_iter_init(reply, &ret_iter);
+ /* Skip write-only properties */
+ if (dsc->getter == NULL)
+ continue;
- dbus_message_iter_open_container(dict_iter,
- DBUS_TYPE_DICT_ENTRY,
- NULL, &entry_iter);
- dbus_message_iter_append_basic(
- &entry_iter, DBUS_TYPE_STRING,
- &dsc->dbus_property);
+ if (!dbus_message_iter_open_container(dict_iter,
+ DBUS_TYPE_DICT_ENTRY,
+ NULL, &entry_iter)) {
+ dbus_set_error_const(error, DBUS_ERROR_NO_MEMORY,
+ "no memory");
+ return FALSE;
+ }
+ if (!dbus_message_iter_append_basic(&entry_iter,
+ DBUS_TYPE_STRING,
+ &dsc->dbus_property)) {
+ dbus_set_error_const(error, DBUS_ERROR_NO_MEMORY,
+ "no memory");
+ return FALSE;
+ }
- recursive_iter_copy(&ret_iter, &entry_iter);
+ /* An error getting a property fails the request entirely */
+ if (!dsc->getter(&entry_iter, error, user_data))
+ return FALSE;
- dbus_message_iter_close_container(dict_iter,
- &entry_iter);
- dbus_message_unref(reply);
- counter++;
- }
+ dbus_message_iter_close_container(dict_iter, &entry_iter);
}
- return counter;
+ return TRUE;
}
@@ -142,37 +74,44 @@ static unsigned int fill_dict_with_properties(
* specified as argument. Returned message contains one dict argument
* with properties names as keys and theirs values as values.
*/
-static DBusMessage * get_all_properties(
- DBusMessage *message, char *interface,
- struct wpa_dbus_object_desc *obj_dsc)
+static DBusMessage * get_all_properties(DBusMessage *message, char *interface,
+ struct wpa_dbus_object_desc *obj_dsc)
{
- /* Create and initialize the return message */
- DBusMessage *reply = dbus_message_new_method_return(message);
+ DBusMessage *reply;
DBusMessageIter iter, dict_iter;
- int props_num;
-
- dbus_message_iter_init_append(reply, &iter);
-
- dbus_message_iter_open_container(&iter, DBUS_TYPE_ARRAY,
- DBUS_DICT_ENTRY_BEGIN_CHAR_AS_STRING
- DBUS_TYPE_STRING_AS_STRING
- DBUS_TYPE_VARIANT_AS_STRING
- DBUS_DICT_ENTRY_END_CHAR_AS_STRING,
- &dict_iter);
+ DBusError error;
- props_num = fill_dict_with_properties(&dict_iter, obj_dsc->properties,
- interface, obj_dsc->user_data);
+ reply = dbus_message_new_method_return(message);
+ if (reply == NULL) {
+ wpa_printf(MSG_ERROR, "%s: out of memory creating dbus reply",
+ __func__);
+ return NULL;
+ }
- dbus_message_iter_close_container(&iter, &dict_iter);
+ dbus_message_iter_init_append(reply, &iter);
+ if (!wpa_dbus_dict_open_write(&iter, &dict_iter)) {
+ wpa_printf(MSG_ERROR, "%s: out of memory creating reply",
+ __func__);
+ dbus_message_unref(reply);
+ reply = dbus_message_new_error(message, DBUS_ERROR_NO_MEMORY,
+ "out of memory");
+ return reply;
+ }
- if (props_num == 0) {
+ dbus_error_init(&error);
+ if (!fill_dict_with_properties(&dict_iter, obj_dsc->properties,
+ interface, obj_dsc->user_data, &error))
+ {
dbus_message_unref(reply);
- reply = dbus_message_new_error(message,
- DBUS_ERROR_INVALID_ARGS,
- "No readable properties in "
- "this interface");
+ reply = wpas_dbus_reply_new_from_error(message, &error,
+ DBUS_ERROR_INVALID_ARGS,
+ "No readable properties"
+ " in this interface");
+ dbus_error_free(&error);
+ return reply;
}
+ wpa_dbus_dict_close_write(&iter, &dict_iter);
return reply;
}
@@ -219,15 +158,33 @@ static DBusMessage * properties_get(DBusMessage *message,
const struct wpa_dbus_property_desc *dsc,
void *user_data)
{
- if (os_strcmp(dbus_message_get_signature(message), "ss"))
+ DBusMessage *reply;
+ DBusMessageIter iter;
+ DBusError error;
+
+ if (os_strcmp(dbus_message_get_signature(message), "ss")) {
return dbus_message_new_error(message, DBUS_ERROR_INVALID_ARGS,
NULL);
+ }
+
+ if (dsc->getter == NULL) {
+ return dbus_message_new_error(message, DBUS_ERROR_INVALID_ARGS,
+ "Property is write-only");
+ }
+
+ reply = dbus_message_new_method_return(message);
+ dbus_message_iter_init_append(reply, &iter);
- if (dsc->access != W && dsc->getter)
- return dsc->getter(message, user_data);
+ dbus_error_init(&error);
+ if (dsc->getter(&iter, &error, user_data) == FALSE) {
+ dbus_message_unref(reply);
+ reply = wpas_dbus_reply_new_from_error(
+ message, &error, DBUS_ERROR_FAILED,
+ "Failed to read property");
+ dbus_error_free(&error);
+ }
- return dbus_message_new_error(message, DBUS_ERROR_INVALID_ARGS,
- "Property is write-only");
+ return reply;
}
@@ -235,15 +192,38 @@ static DBusMessage * properties_set(DBusMessage *message,
const struct wpa_dbus_property_desc *dsc,
void *user_data)
{
- if (os_strcmp(dbus_message_get_signature(message), "ssv"))
+ DBusMessage *reply;
+ DBusMessageIter iter;
+ DBusError error;
+
+ if (os_strcmp(dbus_message_get_signature(message), "ssv")) {
return dbus_message_new_error(message, DBUS_ERROR_INVALID_ARGS,
NULL);
+ }
+
+ if (dsc->setter == NULL) {
+ return dbus_message_new_error(message, DBUS_ERROR_INVALID_ARGS,
+ "Property is read-only");
+ }
- if (dsc->access != R && dsc->setter)
- return dsc->setter(message, user_data);
+ dbus_message_iter_init(message, &iter);
+ /* Skip the interface name and the property name */
+ dbus_message_iter_next(&iter);
+ dbus_message_iter_next(&iter);
+
+ /* Iter will now point to the property's new value */
+ dbus_error_init(&error);
+ if (dsc->setter(&iter, &error, user_data) == TRUE) {
+ /* Success */
+ reply = dbus_message_new_method_return(message);
+ } else {
+ reply = wpas_dbus_reply_new_from_error(
+ message, &error, DBUS_ERROR_FAILED,
+ "Failed to set property");
+ dbus_error_free(&error);
+ }
- return dbus_message_new_error(message, DBUS_ERROR_INVALID_ARGS,
- "Property is read-only");
+ return reply;
}
@@ -552,6 +532,7 @@ int wpa_dbus_register_object_per_iface(
struct wpa_dbus_object_desc *obj_desc)
{
DBusConnection *con;
+ DBusError error;
DBusObjectPathVTable vtable = {
&free_dbus_object_desc_cb, &message_handler,
@@ -566,14 +547,24 @@ int wpa_dbus_register_object_per_iface(
obj_desc->connection = con;
obj_desc->path = os_strdup(path);
+ dbus_error_init(&error);
/* Register the message handler for the interface functions */
- if (!dbus_connection_register_object_path(con, path, &vtable,
- obj_desc)) {
- wpa_printf(MSG_ERROR, "dbus: Could not set up message "
- "handler for interface %s object %s", ifname, path);
+ if (!dbus_connection_try_register_object_path(con, path, &vtable,
+ obj_desc, &error)) {
+ if (!os_strcmp(error.name, DBUS_ERROR_OBJECT_PATH_IN_USE)) {
+ wpa_printf(MSG_DEBUG, "dbus: %s", error.message);
+ } else {
+ wpa_printf(MSG_ERROR, "dbus: Could not set up message "
+ "handler for interface %s object %s",
+ ifname, path);
+ wpa_printf(MSG_ERROR, "dbus error: %s", error.name);
+ wpa_printf(MSG_ERROR, "dbus: %s", error.message);
+ }
+ dbus_error_free(&error);
return -1;
}
+ dbus_error_free(&error);
return 0;
}
@@ -611,14 +602,14 @@ int wpa_dbus_unregister_object_per_iface(
}
-static void put_changed_properties(const struct wpa_dbus_object_desc *obj_dsc,
- const char *interface,
- DBusMessageIter *dict_iter)
+static dbus_bool_t put_changed_properties(
+ const struct wpa_dbus_object_desc *obj_dsc, const char *interface,
+ DBusMessageIter *dict_iter, int clear_changed)
{
- DBusMessage *getter_reply;
- DBusMessageIter prop_iter, entry_iter;
+ DBusMessageIter entry_iter;
const struct wpa_dbus_property_desc *dsc;
int i;
+ DBusError error;
for (dsc = obj_dsc->properties, i = 0; dsc && dsc->dbus_property;
dsc++, i++) {
@@ -627,43 +618,94 @@ static void put_changed_properties(const struct wpa_dbus_object_desc *obj_dsc,
continue;
if (os_strcmp(dsc->dbus_interface, interface) != 0)
continue;
- obj_dsc->prop_changed_flags[i] = 0;
-
- getter_reply = dsc->getter(NULL, obj_dsc->user_data);
- if (!getter_reply ||
- dbus_message_get_type(getter_reply) ==
- DBUS_MESSAGE_TYPE_ERROR) {
- wpa_printf(MSG_ERROR, "dbus: %s: Cannot get new value "
- "of property %s", __func__,
- dsc->dbus_property);
- continue;
- }
+ if (clear_changed)
+ obj_dsc->prop_changed_flags[i] = 0;
- if (!dbus_message_iter_init(getter_reply, &prop_iter) ||
- !dbus_message_iter_open_container(dict_iter,
+ if (!dbus_message_iter_open_container(dict_iter,
DBUS_TYPE_DICT_ENTRY,
- NULL, &entry_iter) ||
- !dbus_message_iter_append_basic(&entry_iter,
+ NULL, &entry_iter))
+ return FALSE;
+
+ if (!dbus_message_iter_append_basic(&entry_iter,
DBUS_TYPE_STRING,
&dsc->dbus_property))
- goto err;
-
- recursive_iter_copy(&prop_iter, &entry_iter);
+ return FALSE;
+
+ dbus_error_init(&error);
+ if (!dsc->getter(&entry_iter, &error, obj_dsc->user_data)) {
+ if (dbus_error_is_set (&error)) {
+ wpa_printf(MSG_ERROR, "dbus: %s: Cannot get "
+ "new value of property %s: (%s) %s",
+ __func__, dsc->dbus_property,
+ error.name, error.message);
+ } else {
+ wpa_printf(MSG_ERROR, "dbus: %s: Cannot get "
+ "new value of property %s",
+ __func__, dsc->dbus_property);
+ }
+ dbus_error_free(&error);
+ return FALSE;
+ }
if (!dbus_message_iter_close_container(dict_iter, &entry_iter))
- goto err;
-
- dbus_message_unref(getter_reply);
+ return FALSE;
}
+ return TRUE;
+}
+
+
+static void do_send_prop_changed_signal(
+ DBusConnection *con, const char *path, const char *interface,
+ const struct wpa_dbus_object_desc *obj_dsc)
+{
+ DBusMessage *msg;
+ DBusMessageIter signal_iter, dict_iter;
+
+ msg = dbus_message_new_signal(path, DBUS_INTERFACE_PROPERTIES,
+ "PropertiesChanged");
+ if (msg == NULL)
+ return;
+
+ dbus_message_iter_init_append(msg, &signal_iter);
+
+ if (!dbus_message_iter_append_basic(&signal_iter, DBUS_TYPE_STRING,
+ &interface))
+ goto err;
+
+ /* Changed properties dict */
+ if (!dbus_message_iter_open_container(&signal_iter, DBUS_TYPE_ARRAY,
+ "{sv}", &dict_iter))
+ goto err;
+
+ if (!put_changed_properties(obj_dsc, interface, &dict_iter, 0))
+ goto err;
+
+ if (!dbus_message_iter_close_container(&signal_iter, &dict_iter))
+ goto err;
+
+ /* Invalidated properties array (empty) */
+ if (!dbus_message_iter_open_container(&signal_iter, DBUS_TYPE_ARRAY,
+ "s", &dict_iter))
+ goto err;
+
+ if (!dbus_message_iter_close_container(&signal_iter, &dict_iter))
+ goto err;
+
+ dbus_connection_send(con, msg, NULL);
+
+out:
+ dbus_message_unref(msg);
return;
err:
- wpa_printf(MSG_ERROR, "dbus: %s: Cannot construct signal", __func__);
+ wpa_printf(MSG_DEBUG, "dbus: %s: Failed to construct signal",
+ __func__);
+ goto out;
}
-static void send_prop_changed_signal(
+static void do_send_deprecated_prop_changed_signal(
DBusConnection *con, const char *path, const char *interface,
const struct wpa_dbus_object_desc *obj_dsc)
{
@@ -680,7 +722,8 @@ static void send_prop_changed_signal(
"{sv}", &dict_iter))
goto err;
- put_changed_properties(obj_dsc, interface, &dict_iter);
+ if (!put_changed_properties(obj_dsc, interface, &dict_iter, 1))
+ goto err;
if (!dbus_message_iter_close_container(&signal_iter, &dict_iter))
goto err;
@@ -698,6 +741,29 @@ err:
}
+static void send_prop_changed_signal(
+ DBusConnection *con, const char *path, const char *interface,
+ const struct wpa_dbus_object_desc *obj_dsc)
+{
+ /*
+ * First, send property change notification on the standardized
+ * org.freedesktop.DBus.Properties interface. This call will not
+ * clear the property change bits, so that they are preserved for
+ * the call that follows.
+ */
+ do_send_prop_changed_signal(con, path, interface, obj_dsc);
+
+ /*
+ * Now send PropertiesChanged on our own interface for backwards
+ * compatibility. This is deprecated and will be removed in a future
+ * release.
+ */
+ do_send_deprecated_prop_changed_signal(con, path, interface, obj_dsc);
+
+ /* Property change bits have now been cleared. */
+}
+
+
static void flush_object_timeout_handler(void *eloop_ctx, void *timeout_ctx)
{
DBusConnection *con = eloop_ctx;
@@ -849,27 +915,147 @@ void wpa_dbus_mark_property_changed(struct wpas_dbus_priv *iface,
* @iface: dbus priv struct
* @path: path to DBus object which properties will be obtained
* @interface: interface name which properties will be obtained
- * @dict_iter: correct, open DBus dictionary iterator.
+ * @iter: DBus message iter at which to append property dictionary.
*
* Iterates over all properties registered with object and execute getters
* of those, which are readable and which interface matches interface
* specified as argument. Obtained properties values are stored in
* dict_iter dictionary.
*/
-void wpa_dbus_get_object_properties(struct wpas_dbus_priv *iface,
- const char *path, const char *interface,
- DBusMessageIter *dict_iter)
+dbus_bool_t wpa_dbus_get_object_properties(struct wpas_dbus_priv *iface,
+ const char *path,
+ const char *interface,
+ DBusMessageIter *iter)
{
struct wpa_dbus_object_desc *obj_desc = NULL;
+ DBusMessageIter dict_iter;
+ DBusError error;
dbus_connection_get_object_path_data(iface->con, path,
(void **) &obj_desc);
if (!obj_desc) {
- wpa_printf(MSG_ERROR, "dbus: wpa_dbus_get_object_properties: "
- "could not obtain object's private data: %s", path);
- return;
+ wpa_printf(MSG_ERROR, "dbus: %s: could not obtain object's "
+ "private data: %s", __func__, path);
+ return FALSE;
+ }
+
+ if (!wpa_dbus_dict_open_write(iter, &dict_iter)) {
+ wpa_printf(MSG_ERROR, "dbus: %s: failed to open message dict",
+ __func__);
+ return FALSE;
+ }
+
+ dbus_error_init(&error);
+ if (!fill_dict_with_properties(&dict_iter, obj_desc->properties,
+ interface, obj_desc->user_data,
+ &error)) {
+ wpa_printf(MSG_ERROR, "dbus: %s: failed to get object"
+ " properties: (%s) %s", __func__,
+ dbus_error_is_set(&error) ? error.name : "none",
+ dbus_error_is_set(&error) ? error.message : "none");
+ dbus_error_free(&error);
+ return FALSE;
+ }
+
+ return wpa_dbus_dict_close_write(iter, &dict_iter);
+}
+
+/**
+ * wpas_dbus_new_decompose_object_path - Decompose an interface object path into parts
+ * @path: The dbus object path
+ * @p2p_persistent_group: indicates whether to parse the path as a P2P
+ * persistent group object
+ * @network: (out) the configured network this object path refers to, if any
+ * @bssid: (out) the scanned bssid this object path refers to, if any
+ * Returns: The object path of the network interface this path refers to
+ *
+ * For a given object path, decomposes the object path into object id, network,
+ * and BSSID parts, if those parts exist.
+ */
+char *wpas_dbus_new_decompose_object_path(const char *path,
+ int p2p_persistent_group,
+ char **network,
+ char **bssid)
+{
+ const unsigned int dev_path_prefix_len =
+ os_strlen(WPAS_DBUS_NEW_PATH_INTERFACES "/");
+ char *obj_path_only;
+ char *next_sep;
+
+ /* Be a bit paranoid about path */
+ if (!path || os_strncmp(path, WPAS_DBUS_NEW_PATH_INTERFACES "/",
+ dev_path_prefix_len))
+ return NULL;
+
+ /* Ensure there's something at the end of the path */
+ if ((path + dev_path_prefix_len)[0] == '\0')
+ return NULL;
+
+ obj_path_only = os_strdup(path);
+ if (obj_path_only == NULL)
+ return NULL;
+
+ next_sep = os_strchr(obj_path_only + dev_path_prefix_len, '/');
+ if (next_sep != NULL) {
+ const char *net_part = os_strstr(
+ next_sep, p2p_persistent_group ?
+ WPAS_DBUS_NEW_PERSISTENT_GROUPS_PART "/" :
+ WPAS_DBUS_NEW_NETWORKS_PART "/");
+ const char *bssid_part = os_strstr(
+ next_sep, WPAS_DBUS_NEW_BSSIDS_PART "/");
+
+ if (network && net_part) {
+ /* Deal with a request for a configured network */
+ const char *net_name = net_part +
+ os_strlen(p2p_persistent_group ?
+ WPAS_DBUS_NEW_PERSISTENT_GROUPS_PART
+ "/" :
+ WPAS_DBUS_NEW_NETWORKS_PART "/");
+ *network = NULL;
+ if (os_strlen(net_name))
+ *network = os_strdup(net_name);
+ } else if (bssid && bssid_part) {
+ /* Deal with a request for a scanned BSSID */
+ const char *bssid_name = bssid_part +
+ os_strlen(WPAS_DBUS_NEW_BSSIDS_PART "/");
+ if (os_strlen(bssid_name))
+ *bssid = os_strdup(bssid_name);
+ else
+ *bssid = NULL;
+ }
+
+ /* Cut off interface object path before "/" */
+ *next_sep = '\0';
}
- fill_dict_with_properties(dict_iter, obj_desc->properties,
- interface, obj_desc->user_data);
+ return obj_path_only;
+}
+
+
+/**
+ * wpas_dbus_reply_new_from_error - Create a new D-Bus error message from a
+ * dbus error structure
+ * @message: The original request message for which the error is a reply
+ * @error: The error containing a name and a descriptive error cause
+ * @fallback_name: A generic error name if @error was not set
+ * @fallback_string: A generic error string if @error was not set
+ * Returns: A new D-Bus error message
+ *
+ * Given a DBusMessage structure, creates a new D-Bus error message using
+ * the error name and string contained in that structure.
+ */
+DBusMessage * wpas_dbus_reply_new_from_error(DBusMessage *message,
+ DBusError *error,
+ const char *fallback_name,
+ const char *fallback_string)
+{
+ if (error && error->name && error->message) {
+ return dbus_message_new_error(message, error->name,
+ error->message);
+ }
+ if (fallback_name && fallback_string) {
+ return dbus_message_new_error(message, fallback_name,
+ fallback_string);
+ }
+ return NULL;
}
diff --git a/contrib/wpa/wpa_supplicant/dbus/dbus_new_helpers.h b/contrib/wpa/wpa_supplicant/dbus/dbus_new_helpers.h
index 8db7a37..6d31ad5 100644
--- a/contrib/wpa/wpa_supplicant/dbus/dbus_new_helpers.h
+++ b/contrib/wpa/wpa_supplicant/dbus/dbus_new_helpers.h
@@ -3,14 +3,8 @@
* Copyright (c) 2006, Dan Williams <dcbw@redhat.com> and Red Hat, Inc.
* Copyright (c) 2009, Witold Sowa <witold.sowa@gmail.com>
*
- * This program is free software; you can redistribute it and/or modify
- * it under the terms of the GNU General Public License version 2 as
- * published by the Free Software Foundation.
- *
- * Alternatively, this software may be distributed under the terms of BSD
- * license.
- *
- * See README and COPYING for more details.
+ * This software may be distributed under the terms of the BSD license.
+ * See README for more details.
*/
#ifndef WPA_DBUS_CTRL_H
@@ -22,8 +16,9 @@ typedef DBusMessage * (* WPADBusMethodHandler)(DBusMessage *message,
void *user_data);
typedef void (* WPADBusArgumentFreeFunction)(void *handler_arg);
-typedef DBusMessage * (* WPADBusPropertyAccessor)(DBusMessage *message,
- const void *user_data);
+typedef dbus_bool_t (* WPADBusPropertyAccessor)(DBusMessageIter *iter,
+ DBusError *error,
+ void *user_data);
struct wpa_dbus_object_desc {
DBusConnection *connection;
@@ -44,8 +39,6 @@ struct wpa_dbus_object_desc {
WPADBusArgumentFreeFunction user_data_free_func;
};
-enum dbus_prop_access { R, W, RW };
-
enum dbus_arg_direction { ARG_IN, ARG_OUT };
struct wpa_dbus_argument {
@@ -67,7 +60,7 @@ struct wpa_dbus_method_desc {
/* method handling function */
WPADBusMethodHandler method_handler;
/* array of arguments */
- struct wpa_dbus_argument args[3];
+ struct wpa_dbus_argument args[4];
};
/**
@@ -79,7 +72,7 @@ struct wpa_dbus_signal_desc {
/* signal interface */
const char *dbus_interface;
/* array of arguments */
- struct wpa_dbus_argument args[3];
+ struct wpa_dbus_argument args[4];
};
/**
@@ -96,14 +89,13 @@ struct wpa_dbus_property_desc {
WPADBusPropertyAccessor getter;
/* property setter function */
WPADBusPropertyAccessor setter;
- /* property access permissions */
- enum dbus_prop_access access;
};
#define WPAS_DBUS_OBJECT_PATH_MAX 150
#define WPAS_DBUS_INTERFACE_MAX 150
#define WPAS_DBUS_METHOD_SIGNAL_PROP_MAX 50
+#define WPAS_DBUS_AUTH_MODE_MAX 64
#define WPA_DBUS_INTROSPECTION_INTERFACE "org.freedesktop.DBus.Introspectable"
#define WPA_DBUS_INTROSPECTION_METHOD "Introspect"
@@ -127,9 +119,10 @@ int wpa_dbus_unregister_object_per_iface(
struct wpas_dbus_priv *ctrl_iface,
const char *path);
-void wpa_dbus_get_object_properties(struct wpas_dbus_priv *iface,
- const char *path, const char *interface,
- DBusMessageIter *dict_iter);
+dbus_bool_t wpa_dbus_get_object_properties(struct wpas_dbus_priv *iface,
+ const char *path,
+ const char *interface,
+ DBusMessageIter *iter);
void wpa_dbus_flush_all_changed_properties(DBusConnection *con);
@@ -144,4 +137,14 @@ void wpa_dbus_mark_property_changed(struct wpas_dbus_priv *iface,
DBusMessage * wpa_dbus_introspect(DBusMessage *message,
struct wpa_dbus_object_desc *obj_dsc);
+char *wpas_dbus_new_decompose_object_path(const char *path,
+ int p2p_persistent_group,
+ char **network,
+ char **bssid);
+
+DBusMessage *wpas_dbus_reply_new_from_error(DBusMessage *message,
+ DBusError *error,
+ const char *fallback_name,
+ const char *fallback_string);
+
#endif /* WPA_DBUS_CTRL_H */
diff --git a/contrib/wpa/wpa_supplicant/dbus/dbus_new_introspect.c b/contrib/wpa/wpa_supplicant/dbus/dbus_new_introspect.c
index c660c04..3b090c0 100644
--- a/contrib/wpa/wpa_supplicant/dbus/dbus_new_introspect.c
+++ b/contrib/wpa/wpa_supplicant/dbus/dbus_new_introspect.c
@@ -4,14 +4,8 @@
* Copyright (c) 2009, Witold Sowa <witold.sowa@gmail.com>
* Copyright (c) 2010, Jouni Malinen <j@w1.fi>
*
- * This program is free software; you can redistribute it and/or modify
- * it under the terms of the GNU General Public License version 2 as
- * published by the Free Software Foundation.
- *
- * Alternatively, this software may be distributed under the terms of BSD
- * license.
- *
- * See README and COPYING for more details.
+ * This software may be distributed under the terms of the BSD license.
+ * See README for more details.
*/
#include "utils/includes.h"
@@ -43,7 +37,7 @@ static struct interfaces * add_interface(struct dl_list *list,
iface = os_zalloc(sizeof(struct interfaces));
if (!iface)
return NULL;
- iface->xml = wpabuf_alloc(3000);
+ iface->xml = wpabuf_alloc(6000);
if (iface->xml == NULL) {
os_free(iface);
return NULL;
@@ -89,10 +83,11 @@ static void add_entry(struct wpabuf *xml, const char *type, const char *name,
static void add_property(struct wpabuf *xml,
const struct wpa_dbus_property_desc *dsc)
{
- wpabuf_printf(xml, "<property name=\"%s\" type=\"%s\" access=\"%s\"/>",
+ wpabuf_printf(xml, "<property name=\"%s\" type=\"%s\" "
+ "access=\"%s%s\"/>",
dsc->dbus_property, dsc->type,
- (dsc->access == R ? "read" :
- (dsc->access == W ? "write" : "readwrite")));
+ dsc->getter ? "read" : "",
+ dsc->setter ? "write" : "");
}
@@ -163,6 +158,12 @@ static void add_interfaces(struct dl_list *list, struct wpabuf *xml)
if (wpabuf_len(iface->xml) + 20 < wpabuf_tailroom(xml)) {
wpabuf_put_buf(xml, iface->xml);
wpabuf_put_str(xml, "</interface>");
+ } else {
+ wpa_printf(MSG_DEBUG, "dbus: Not enough room for "
+ "add_interfaces inspect data: tailroom %u, "
+ "add %u",
+ (unsigned int) wpabuf_tailroom(xml),
+ (unsigned int) wpabuf_len(iface->xml));
}
dl_list_del(&iface->list);
wpabuf_free(iface->xml);
@@ -250,7 +251,7 @@ DBusMessage * wpa_dbus_introspect(DBusMessage *message,
DBusMessage *reply;
struct wpabuf *xml;
- xml = wpabuf_alloc(4000);
+ xml = wpabuf_alloc(10000);
if (xml == NULL)
return NULL;
diff --git a/contrib/wpa/wpa_supplicant/dbus/dbus_old.c b/contrib/wpa/wpa_supplicant/dbus/dbus_old.c
index 7f25bf0..5f298e7 100644
--- a/contrib/wpa/wpa_supplicant/dbus/dbus_old.c
+++ b/contrib/wpa/wpa_supplicant/dbus/dbus_old.c
@@ -2,14 +2,8 @@
* WPA Supplicant / dbus-based control interface
* Copyright (c) 2006, Dan Williams <dcbw@redhat.com> and Red Hat, Inc.
*
- * This program is free software; you can redistribute it and/or modify
- * it under the terms of the GNU General Public License version 2 as
- * published by the Free Software Foundation.
- *
- * Alternatively, this software may be distributed under the terms of BSD
- * license.
- *
- * See README and COPYING for more details.
+ * This software may be distributed under the terms of the BSD license.
+ * See README for more details.
*/
#include "includes.h"
@@ -23,7 +17,6 @@
#include "../bss.h"
#include "dbus_old.h"
#include "dbus_old_handlers.h"
-#include "dbus_common.h"
#include "dbus_common_i.h"
@@ -287,6 +280,8 @@ static DBusHandlerResult wpas_iface_message_handler(DBusConnection *connection,
else if (!os_strcmp(method, "wpsReg"))
reply = wpas_dbus_iface_wps_reg(message, wpa_s);
#endif /* CONFIG_WPS */
+ else if (!os_strcmp(method, "flush"))
+ reply = wpas_dbus_iface_flush(message, wpa_s);
}
/* If the message was handled, send back the reply */
@@ -547,6 +542,59 @@ void wpa_supplicant_dbus_notify_wps_cred(struct wpa_supplicant *wpa_s,
}
#endif /* CONFIG_WPS */
+void wpa_supplicant_dbus_notify_certification(struct wpa_supplicant *wpa_s,
+ int depth, const char *subject,
+ const char *cert_hash,
+ const struct wpabuf *cert)
+{
+ struct wpas_dbus_priv *iface;
+ DBusMessage *_signal = NULL;
+ const char *hash;
+ const char *cert_hex;
+ int cert_hex_len;
+
+ /* Do nothing if the control interface is not turned on */
+ if (wpa_s->global == NULL)
+ return;
+ iface = wpa_s->global->dbus;
+ if (iface == NULL)
+ return;
+
+ _signal = dbus_message_new_signal(wpa_s->dbus_path,
+ WPAS_DBUS_IFACE_INTERFACE,
+ "Certification");
+ if (_signal == NULL) {
+ wpa_printf(MSG_ERROR,
+ "dbus: wpa_supplicant_dbus_notify_certification: "
+ "Could not create dbus signal; likely out of "
+ "memory");
+ return;
+ }
+
+ hash = cert_hash ? cert_hash : "";
+ cert_hex = cert ? wpabuf_head(cert) : "";
+ cert_hex_len = cert ? wpabuf_len(cert) : 0;
+
+ if (!dbus_message_append_args(_signal,
+ DBUS_TYPE_INT32,&depth,
+ DBUS_TYPE_STRING, &subject,
+ DBUS_TYPE_STRING, &hash,
+ DBUS_TYPE_ARRAY, DBUS_TYPE_BYTE,
+ &cert_hex, cert_hex_len,
+ DBUS_TYPE_INVALID)) {
+ wpa_printf(MSG_ERROR,
+ "dbus: wpa_supplicant_dbus_notify_certification: "
+ "Not enough memory to construct signal");
+ goto out;
+ }
+
+ dbus_connection_send(iface->con, _signal, NULL);
+
+out:
+ dbus_message_unref(_signal);
+
+}
+
/**
* wpa_supplicant_dbus_ctrl_iface_init - Initialize dbus control interface
diff --git a/contrib/wpa/wpa_supplicant/dbus/dbus_old.h b/contrib/wpa/wpa_supplicant/dbus/dbus_old.h
index a9840c2..e668231 100644
--- a/contrib/wpa/wpa_supplicant/dbus/dbus_old.h
+++ b/contrib/wpa/wpa_supplicant/dbus/dbus_old.h
@@ -2,14 +2,8 @@
* WPA Supplicant / dbus-based control interface
* Copyright (c) 2006, Dan Williams <dcbw@redhat.com> and Red Hat, Inc.
*
- * This program is free software; you can redistribute it and/or modify
- * it under the terms of the GNU General Public License version 2 as
- * published by the Free Software Foundation.
- *
- * Alternatively, this software may be distributed under the terms of BSD
- * license.
- *
- * See README and COPYING for more details.
+ * This software may be distributed under the terms of the BSD license.
+ * See README for more details.
*/
#ifndef CTRL_IFACE_DBUS_H
@@ -82,6 +76,10 @@ void wpa_supplicant_dbus_notify_state_change(struct wpa_supplicant *wpa_s,
enum wpa_states old_state);
void wpa_supplicant_dbus_notify_wps_cred(struct wpa_supplicant *wpa_s,
const struct wps_credential *cred);
+void wpa_supplicant_dbus_notify_certification(struct wpa_supplicant *wpa_s,
+ int depth, const char *subject,
+ const char *cert_hash,
+ const struct wpabuf *cert);
char * wpas_dbus_decompose_object_path(const char *path, char **network,
char **bssid);
@@ -114,6 +112,14 @@ wpa_supplicant_dbus_notify_wps_cred(struct wpa_supplicant *wpa_s,
{
}
+static inline void
+wpa_supplicant_dbus_notify_certification(struct wpa_supplicant *wpa_s,
+ int depth, const char *subject,
+ const char *cert_hash,
+ const struct wpabuf *cert)
+{
+}
+
static inline int
wpas_dbus_register_iface(struct wpa_supplicant *wpa_s)
{
diff --git a/contrib/wpa/wpa_supplicant/dbus/dbus_old_handlers.c b/contrib/wpa/wpa_supplicant/dbus/dbus_old_handlers.c
index d914697..68e5515 100644
--- a/contrib/wpa/wpa_supplicant/dbus/dbus_old_handlers.c
+++ b/contrib/wpa/wpa_supplicant/dbus/dbus_old_handlers.c
@@ -2,14 +2,8 @@
* WPA Supplicant / dbus-based control interface
* Copyright (c) 2006, Dan Williams <dcbw@redhat.com> and Red Hat, Inc.
*
- * This program is free software; you can redistribute it and/or modify
- * it under the terms of the GNU General Public License version 2 as
- * published by the Free Software Foundation.
- *
- * Alternatively, this software may be distributed under the terms of BSD
- * license.
- *
- * See README and COPYING for more details.
+ * This software may be distributed under the terms of the BSD license.
+ * See README for more details.
*/
#include "includes.h"
@@ -116,7 +110,7 @@ DBusMessage * wpas_dbus_global_add_interface(DBusMessage *message,
DBusMessageIter iter_dict;
struct wpa_dbus_dict_entry entry;
- if (!wpa_dbus_dict_open_read(&iter, &iter_dict))
+ if (!wpa_dbus_dict_open_read(&iter, &iter_dict, NULL))
goto error;
while (wpa_dbus_dict_has_dict_entry(&iter_dict)) {
if (!wpa_dbus_dict_get_entry(&iter_dict, &entry))
@@ -229,7 +223,7 @@ DBusMessage * wpas_dbus_global_remove_interface(DBusMessage *message,
goto out;
}
- if (!wpa_supplicant_remove_iface(global, wpa_s)) {
+ if (!wpa_supplicant_remove_iface(global, wpa_s, 0)) {
reply = wpas_dbus_new_success_reply(message);
} else {
reply = dbus_message_new_error(message,
@@ -337,7 +331,7 @@ DBusMessage * wpas_dbus_global_set_debugparams(DBusMessage *message,
DBusMessage * wpas_dbus_iface_scan(DBusMessage *message,
struct wpa_supplicant *wpa_s)
{
- wpa_s->scan_req = 2;
+ wpa_s->scan_req = MANUAL_SCAN_REQ;
wpa_supplicant_req_scan(wpa_s, 0, 0);
return wpas_dbus_new_success_reply(message);
}
@@ -922,7 +916,7 @@ DBusMessage * wpas_dbus_iface_set_network(DBusMessage *message,
dbus_message_iter_init(message, &iter);
- if (!wpa_dbus_dict_open_read(&iter, &iter_dict)) {
+ if (!wpa_dbus_dict_open_read(&iter, &iter_dict, NULL)) {
reply = wpas_dbus_new_invalid_opts_error(message, NULL);
goto out;
}
@@ -1202,7 +1196,7 @@ DBusMessage * wpas_dbus_iface_set_smartcard_modules(
if (!dbus_message_iter_init(message, &iter))
goto error;
- if (!wpa_dbus_dict_open_read(&iter, &iter_dict))
+ if (!wpa_dbus_dict_open_read(&iter, &iter_dict, NULL))
goto error;
while (wpa_dbus_dict_has_dict_entry(&iter_dict)) {
@@ -1324,7 +1318,7 @@ DBusMessage * wpas_dbus_iface_set_blobs(DBusMessage *message,
dbus_message_iter_init(message, &iter);
- if (!wpa_dbus_dict_open_read(&iter, &iter_dict))
+ if (!wpa_dbus_dict_open_read(&iter, &iter_dict, NULL))
return wpas_dbus_new_invalid_opts_error(message, NULL);
while (wpa_dbus_dict_has_dict_entry(&iter_dict)) {
@@ -1434,3 +1428,35 @@ DBusMessage * wpas_dbus_iface_remove_blobs(DBusMessage *message,
return wpas_dbus_new_success_reply(message);
}
+
+
+/**
+ * wpas_dbus_iface_flush - Clear BSS of old or all inactive entries
+ * @message: Pointer to incoming dbus message
+ * @wpa_s: %wpa_supplicant data structure
+ * Returns: a dbus message containing a UINT32 indicating success (1) or
+ * failure (0), or returns a dbus error message with more information
+ *
+ * Handler function for "flush" method call. Handles requests for an
+ * interface with an optional "age" parameter that specifies the minimum
+ * age of a BSS to be flushed.
+ */
+DBusMessage * wpas_dbus_iface_flush(DBusMessage *message,
+ struct wpa_supplicant *wpa_s)
+{
+ int flush_age = 0;
+
+ if (os_strlen(dbus_message_get_signature(message)) != 0 &&
+ !dbus_message_get_args(message, NULL,
+ DBUS_TYPE_INT32, &flush_age,
+ DBUS_TYPE_INVALID)) {
+ return wpas_dbus_new_invalid_opts_error(message, NULL);
+ }
+
+ if (flush_age == 0)
+ wpa_bss_flush(wpa_s);
+ else
+ wpa_bss_flush_by_age(wpa_s, flush_age);
+
+ return wpas_dbus_new_success_reply(message);
+}
diff --git a/contrib/wpa/wpa_supplicant/dbus/dbus_old_handlers.h b/contrib/wpa/wpa_supplicant/dbus/dbus_old_handlers.h
index 65e876f..825bc6d 100644
--- a/contrib/wpa/wpa_supplicant/dbus/dbus_old_handlers.h
+++ b/contrib/wpa/wpa_supplicant/dbus/dbus_old_handlers.h
@@ -2,14 +2,8 @@
* WPA Supplicant / dbus-based control interface
* Copyright (c) 2006, Dan Williams <dcbw@redhat.com> and Red Hat, Inc.
*
- * This program is free software; you can redistribute it and/or modify
- * it under the terms of the GNU General Public License version 2 as
- * published by the Free Software Foundation.
- *
- * Alternatively, this software may be distributed under the terms of BSD
- * license.
- *
- * See README and COPYING for more details.
+ * This software may be distributed under the terms of the BSD license.
+ * See README for more details.
*/
#ifndef CTRL_IFACE_DBUS_HANDLERS_H
@@ -96,6 +90,9 @@ DBusMessage * wpas_dbus_iface_wps_pin(DBusMessage *message,
DBusMessage * wpas_dbus_iface_wps_reg(DBusMessage *message,
struct wpa_supplicant *wpa_s);
+DBusMessage * wpas_dbus_iface_flush(DBusMessage *message,
+ struct wpa_supplicant *wpa_s);
+
DBusMessage * wpas_dbus_new_success_reply(DBusMessage *message);
DBusMessage * wpas_dbus_new_invalid_opts_error(DBusMessage *message,
const char *arg);
diff --git a/contrib/wpa/wpa_supplicant/dbus/dbus_old_handlers_wps.c b/contrib/wpa/wpa_supplicant/dbus/dbus_old_handlers_wps.c
index b5879f3..bb79382 100644
--- a/contrib/wpa/wpa_supplicant/dbus/dbus_old_handlers_wps.c
+++ b/contrib/wpa/wpa_supplicant/dbus/dbus_old_handlers_wps.c
@@ -2,14 +2,8 @@
* WPA Supplicant / dbus-based control interface (WPS)
* Copyright (c) 2006, Dan Williams <dcbw@redhat.com> and Red Hat, Inc.
*
- * This program is free software; you can redistribute it and/or modify
- * it under the terms of the GNU General Public License version 2 as
- * published by the Free Software Foundation.
- *
- * Alternatively, this software may be distributed under the terms of BSD
- * license.
- *
- * See README and COPYING for more details.
+ * This software may be distributed under the terms of the BSD license.
+ * See README for more details.
*/
#include "includes.h"
@@ -43,9 +37,9 @@ DBusMessage * wpas_dbus_iface_wps_pbc(DBusMessage *message,
return wpas_dbus_new_invalid_opts_error(message, NULL);
if (!os_strcmp(arg_bssid, "any"))
- ret = wpas_wps_start_pbc(wpa_s, NULL);
+ ret = wpas_wps_start_pbc(wpa_s, NULL, 0);
else if (!hwaddr_aton(arg_bssid, bssid))
- ret = wpas_wps_start_pbc(wpa_s, bssid);
+ ret = wpas_wps_start_pbc(wpa_s, bssid, 0);
else {
return wpas_dbus_new_invalid_opts_error(message,
"Invalid BSSID");
@@ -94,9 +88,11 @@ DBusMessage * wpas_dbus_iface_wps_pin(DBusMessage *message,
}
if (os_strlen(pin) > 0)
- ret = wpas_wps_start_pin(wpa_s, _bssid, pin);
+ ret = wpas_wps_start_pin(wpa_s, _bssid, pin, 0,
+ DEV_PW_DEFAULT);
else
- ret = wpas_wps_start_pin(wpa_s, _bssid, NULL);
+ ret = wpas_wps_start_pin(wpa_s, _bssid, NULL, 0,
+ DEV_PW_DEFAULT);
if (ret < 0) {
return dbus_message_new_error(message,
diff --git a/contrib/wpa/wpa_supplicant/dbus/fi.epitest.hostap.WPASupplicant.service b/contrib/wpa/wpa_supplicant/dbus/fi.epitest.hostap.WPASupplicant.service
deleted file mode 100644
index a9ce1ec..0000000
--- a/contrib/wpa/wpa_supplicant/dbus/fi.epitest.hostap.WPASupplicant.service
+++ /dev/null
@@ -1,4 +0,0 @@
-[D-BUS Service]
-Name=fi.epitest.hostap.WPASupplicant
-Exec=/sbin/wpa_supplicant -u
-User=root
diff --git a/contrib/wpa/wpa_supplicant/dbus/fi.epitest.hostap.WPASupplicant.service.in b/contrib/wpa/wpa_supplicant/dbus/fi.epitest.hostap.WPASupplicant.service.in
new file mode 100644
index 0000000..a75918f
--- /dev/null
+++ b/contrib/wpa/wpa_supplicant/dbus/fi.epitest.hostap.WPASupplicant.service.in
@@ -0,0 +1,5 @@
+[D-BUS Service]
+Name=fi.epitest.hostap.WPASupplicant
+Exec=@BINDIR@/wpa_supplicant -u
+User=root
+SystemdService=wpa_supplicant.service
diff --git a/contrib/wpa/wpa_supplicant/dbus/fi.w1.wpa_supplicant1.service b/contrib/wpa/wpa_supplicant/dbus/fi.w1.wpa_supplicant1.service
deleted file mode 100644
index df78471..0000000
--- a/contrib/wpa/wpa_supplicant/dbus/fi.w1.wpa_supplicant1.service
+++ /dev/null
@@ -1,4 +0,0 @@
-[D-BUS Service]
-Name=fi.w1.wpa_supplicant1
-Exec=/sbin/wpa_supplicant -u
-User=root
diff --git a/contrib/wpa/wpa_supplicant/dbus/fi.w1.wpa_supplicant1.service.in b/contrib/wpa/wpa_supplicant/dbus/fi.w1.wpa_supplicant1.service.in
new file mode 100644
index 0000000..d97ff39
--- /dev/null
+++ b/contrib/wpa/wpa_supplicant/dbus/fi.w1.wpa_supplicant1.service.in
@@ -0,0 +1,5 @@
+[D-BUS Service]
+Name=fi.w1.wpa_supplicant1
+Exec=@BINDIR@/wpa_supplicant -u
+User=root
+SystemdService=wpa_supplicant.service
diff --git a/contrib/wpa/wpa_supplicant/defconfig b/contrib/wpa/wpa_supplicant/defconfig
index 8c32cb3..711b407 100644
--- a/contrib/wpa/wpa_supplicant/defconfig
+++ b/contrib/wpa/wpa_supplicant/defconfig
@@ -78,10 +78,15 @@ CONFIG_DRIVER_ATMEL=y
#CONFIG_DRIVER_RALINK=y
# Driver interface for generic Linux wireless extensions
+# Note: WEXT is deprecated in the current Linux kernel version and no new
+# functionality is added to it. nl80211-based interface is the new
+# replacement for WEXT and its use allows wpa_supplicant to properly control
+# the driver to improve existing functionality like roaming and to support new
+# functionality.
CONFIG_DRIVER_WEXT=y
# Driver interface for Linux drivers using the nl80211 kernel interface
-#CONFIG_DRIVER_NL80211=y
+CONFIG_DRIVER_NL80211=y
# Driver interface for FreeBSD net80211 layer (e.g., Atheros driver)
#CONFIG_DRIVER_BSD=y
@@ -109,11 +114,6 @@ CONFIG_DRIVER_WEXT=y
# Driver interface for development testing
#CONFIG_DRIVER_TEST=y
-# Include client MLME (management frame processing) for test driver
-# This can be used to test MLME operations in hostapd with the test interface.
-# space.
-#CONFIG_CLIENT_MLME=y
-
# Driver interface for wired Ethernet drivers
CONFIG_DRIVER_WIRED=y
@@ -123,6 +123,10 @@ CONFIG_DRIVER_WIRED=y
# Driver interface for no driver (e.g., WPS ER only)
#CONFIG_DRIVER_NONE=y
+# Solaris libraries
+#LIBS += -lsocket -ldlpi -lnsl
+#LIBS_c += -lsocket
+
# Enable IEEE 802.1X Supplicant (automatically included if any EAP method is
# included)
CONFIG_IEEE8021X_EAPOL=y
@@ -161,6 +165,9 @@ CONFIG_EAP_OTP=y
# EAP-PSK (experimental; this is _not_ needed for WPA-PSK)
#CONFIG_EAP_PSK=y
+# EAP-pwd (secure authentication using only a password)
+#CONFIG_EAP_PWD=y
+
# EAP-PAX
#CONFIG_EAP_PAX=y
@@ -190,6 +197,15 @@ CONFIG_EAP_LEAP=y
# Wi-Fi Protected Setup (WPS)
#CONFIG_WPS=y
+# Enable WSC 2.0 support
+#CONFIG_WPS2=y
+# Enable WPS external registrar functionality
+#CONFIG_WPS_ER=y
+# Disable credentials for an open network by default when acting as a WPS
+# registrar.
+#CONFIG_WPS_REG_DISABLE_OPEN=y
+# Enable WPS support with NFC config method
+#CONFIG_WPS_NFC=y
# EAP-IKEv2
#CONFIG_EAP_IKEV2=y
@@ -206,6 +222,9 @@ CONFIG_SMARTCARD=y
# Enable this if EAP-SIM or EAP-AKA is included
#CONFIG_PCSC=y
+# Support HT overrides (disable HT/HT40, mask MCS rates, etc.)
+#CONFIG_HT_OVERRIDES=y
+
# Development testing
#CONFIG_EAPOL_TEST=y
@@ -213,6 +232,7 @@ CONFIG_SMARTCARD=y
# unix = UNIX domain sockets (default for Linux/*BSD)
# udp = UDP sockets using localhost (127.0.0.1)
# named_pipe = Windows Named Pipe (default for Windows)
+# udp-remote = UDP sockets with remote access (only for tests systems/purpose)
# y = use default (backwards compatibility)
# If this option is commented out, control interface is not included in the
# build.
@@ -224,6 +244,10 @@ CONFIG_CTRL_IFACE=y
# the resulting binary.
#CONFIG_READLINE=y
+# Include internal line edit mode in wpa_cli. This can be used as a replacement
+# for GNU Readline to provide limited command line editing and history support.
+#CONFIG_WPA_CLI_EDIT=y
+
# Remove debugging code that is printing out debug message to stdout.
# This can be used to reduce the size of the wpa_supplicant considerably
# if debugging code is not needed. The size reduction can be around 35%
@@ -285,6 +309,9 @@ CONFIG_BACKEND=file
# eloop_none = Empty template
#CONFIG_ELOOP=eloop
+# Should we use poll instead of select? Select is used by default.
+#CONFIG_ELOOP_POLL=y
+
# Select layer 2 packet implementation
# linux = Linux packet socket (default)
# pcap = libpcap/libdnet/WinPcap
@@ -297,26 +324,30 @@ CONFIG_BACKEND=file
# PeerKey handshake for Station to Station Link (IEEE 802.11e DLS)
CONFIG_PEERKEY=y
-# IEEE 802.11w (management frame protection)
-# This version is an experimental implementation based on IEEE 802.11w/D1.0
-# draft and is subject to change since the standard has not yet been finalized.
+# IEEE 802.11w (management frame protection), also known as PMF
# Driver support is also needed for IEEE 802.11w.
#CONFIG_IEEE80211W=y
# Select TLS implementation
# openssl = OpenSSL (default)
-# gnutls = GnuTLS (needed for TLS/IA, see also CONFIG_GNUTLS_EXTRA)
+# gnutls = GnuTLS
# internal = Internal TLSv1 implementation (experimental)
# none = Empty template
#CONFIG_TLS=openssl
-# Whether to enable TLS/IA support, which is required for EAP-TTLSv1.
-# You need CONFIG_TLS=gnutls for this to have any effect. Please note that
-# even though the core GnuTLS library is released under LGPL, this extra
-# library uses GPL and as such, the terms of GPL apply to the combination
-# of wpa_supplicant and GnuTLS if this option is enabled. BSD license may not
-# apply for distribution of the resulting binary.
-#CONFIG_GNUTLS_EXTRA=y
+# TLS-based EAP methods require at least TLS v1.0. Newer version of TLS (v1.1)
+# can be enabled to get a stronger construction of messages when block ciphers
+# are used. It should be noted that some existing TLS v1.0 -based
+# implementation may not be compatible with TLS v1.1 message (ClientHello is
+# sent prior to negotiating which version will be used)
+#CONFIG_TLSV11=y
+
+# TLS-based EAP methods require at least TLS v1.0. Newer version of TLS (v1.2)
+# can be enabled to enable use of stronger crypto algorithms. It should be
+# noted that some existing TLS v1.0 -based implementation may not be compatible
+# with TLS v1.2 message (ClientHello is sent prior to negotiating which version
+# will be used)
+#CONFIG_TLSV12=y
# If CONFIG_TLS=internal is used, additional library and include paths are
# needed for LibTomMath. Alternatively, an integrated, minimal version of
@@ -378,6 +409,17 @@ CONFIG_PEERKEY=y
# Add support for writing debug log to a file (/tmp/wpa_supplicant-log-#.txt)
#CONFIG_DEBUG_FILE=y
+# Send debug messages to syslog instead of stdout
+#CONFIG_DEBUG_SYSLOG=y
+# Set syslog facility for debug messages
+#CONFIG_DEBUG_SYSLOG_FACILITY=LOG_DAEMON
+
+# Add support for sending all debug messages (regardless of debug verbosity)
+# to the Linux kernel tracing facility. This helps debug the entire stack by
+# making it easy to record everything happening from the driver up into the
+# same file, e.g., using trace-cmd.
+#CONFIG_DEBUG_LINUX_TRACING=y
+
# Enable privilege separation (see README 'Privilege separation' for details)
#CONFIG_PRIVSEP=y
@@ -389,7 +431,7 @@ CONFIG_PEERKEY=y
# This tracks use of memory allocations and other registrations and reports
# incorrect use with a backtrace of call (or allocation) location.
#CONFIG_WPA_TRACE=y
-# For BSD, comment out these.
+# For BSD, uncomment these.
#LIBS += -lexecinfo
#LIBS_p += -lexecinfo
#LIBS_c += -lexecinfo
@@ -398,7 +440,84 @@ CONFIG_PEERKEY=y
# This enables use of libbfd to get more detailed symbols for the backtraces
# generated by CONFIG_WPA_TRACE=y.
#CONFIG_WPA_TRACE_BFD=y
-# For BSD, comment out these.
+# For BSD, uncomment these.
#LIBS += -lbfd -liberty -lz
#LIBS_p += -lbfd -liberty -lz
#LIBS_c += -lbfd -liberty -lz
+
+# wpa_supplicant depends on strong random number generation being available
+# from the operating system. os_get_random() function is used to fetch random
+# data when needed, e.g., for key generation. On Linux and BSD systems, this
+# works by reading /dev/urandom. It should be noted that the OS entropy pool
+# needs to be properly initialized before wpa_supplicant is started. This is
+# important especially on embedded devices that do not have a hardware random
+# number generator and may by default start up with minimal entropy available
+# for random number generation.
+#
+# As a safety net, wpa_supplicant is by default trying to internally collect
+# additional entropy for generating random data to mix in with the data fetched
+# from the OS. This by itself is not considered to be very strong, but it may
+# help in cases where the system pool is not initialized properly. However, it
+# is very strongly recommended that the system pool is initialized with enough
+# entropy either by using hardware assisted random number generator or by
+# storing state over device reboots.
+#
+# wpa_supplicant can be configured to maintain its own entropy store over
+# restarts to enhance random number generation. This is not perfect, but it is
+# much more secure than using the same sequence of random numbers after every
+# reboot. This can be enabled with -e<entropy file> command line option. The
+# specified file needs to be readable and writable by wpa_supplicant.
+#
+# If the os_get_random() is known to provide strong random data (e.g., on
+# Linux/BSD, the board in question is known to have reliable source of random
+# data from /dev/urandom), the internal wpa_supplicant random pool can be
+# disabled. This will save some in binary size and CPU use. However, this
+# should only be considered for builds that are known to be used on devices
+# that meet the requirements described above.
+#CONFIG_NO_RANDOM_POOL=y
+
+# IEEE 802.11n (High Throughput) support (mainly for AP mode)
+#CONFIG_IEEE80211N=y
+
+# Wireless Network Management (IEEE Std 802.11v-2011)
+# Note: This is experimental and not complete implementation.
+#CONFIG_WNM=y
+
+# Interworking (IEEE 802.11u)
+# This can be used to enable functionality to improve interworking with
+# external networks (GAS/ANQP to learn more about the networks and network
+# selection based on available credentials).
+#CONFIG_INTERWORKING=y
+
+# Hotspot 2.0
+#CONFIG_HS20=y
+
+# AP mode operations with wpa_supplicant
+# This can be used for controlling AP mode operations with wpa_supplicant. It
+# should be noted that this is mainly aimed at simple cases like
+# WPA2-Personal while more complex configurations like WPA2-Enterprise with an
+# external RADIUS server can be supported with hostapd.
+#CONFIG_AP=y
+
+# P2P (Wi-Fi Direct)
+# This can be used to enable P2P support in wpa_supplicant. See README-P2P for
+# more information on P2P operations.
+#CONFIG_P2P=y
+
+# Autoscan
+# This can be used to enable automatic scan support in wpa_supplicant.
+# See wpa_supplicant.conf for more information on autoscan usage.
+#
+# Enabling directly a module will enable autoscan support.
+# For exponential module:
+#CONFIG_AUTOSCAN_EXPONENTIAL=y
+# For periodic module:
+#CONFIG_AUTOSCAN_PERIODIC=y
+
+# Password (and passphrase, etc.) backend for external storage
+# These optional mechanisms can be used to add support for storing passwords
+# and other secrets in external (to wpa_supplicant) location. This allows, for
+# example, operating system specific key storage to be used
+#
+# External password backend for testing purposes (developer use)
+#CONFIG_EXT_PASSWORD_TEST=y
diff --git a/contrib/wpa/wpa_supplicant/doc/docbook/.gitignore b/contrib/wpa/wpa_supplicant/doc/docbook/.gitignore
deleted file mode 100644
index 8c3945c..0000000
--- a/contrib/wpa/wpa_supplicant/doc/docbook/.gitignore
+++ /dev/null
@@ -1,6 +0,0 @@
-manpage.links
-manpage.refs
-*.8
-*.5
-*.html
-*.pdf
diff --git a/contrib/wpa/wpa_supplicant/doc/docbook/Makefile b/contrib/wpa/wpa_supplicant/doc/docbook/Makefile
deleted file mode 100644
index aaeee2e..0000000
--- a/contrib/wpa/wpa_supplicant/doc/docbook/Makefile
+++ /dev/null
@@ -1,27 +0,0 @@
-all: man html pdf
-
-FILES += wpa_background
-FILES += wpa_cli
-FILES += wpa_gui
-FILES += wpa_passphrase
-FILES += wpa_priv
-FILES += wpa_supplicant.conf
-FILES += wpa_supplicant
-
-man:
- for i in $(FILES); do docbook2man $$i.sgml; done
-
-html:
- for i in $(FILES); do docbook2html $$i.sgml && \
- mv index.html $$i.html; done
-
-pdf:
- for i in $(FILES); do docbook2pdf $$i.sgml; done
-
-
-clean:
- rm -f wpa_background.8 wpa_cli.8 wpa_gui.8 wpa_passphrase.8 wpa_priv.8 wpa_supplicant.8
- rm -f wpa_supplicant.conf.5
- rm -f manpage.links manpage.refs
- rm -f $(FILES:%=%.pdf)
- rm -f $(FILES:%=%.html)
diff --git a/contrib/wpa/wpa_supplicant/doc/docbook/manpage.links b/contrib/wpa/wpa_supplicant/doc/docbook/manpage.links
deleted file mode 100644
index e69de29..0000000
--- a/contrib/wpa/wpa_supplicant/doc/docbook/manpage.links
+++ /dev/null
diff --git a/contrib/wpa/wpa_supplicant/doc/docbook/manpage.refs b/contrib/wpa/wpa_supplicant/doc/docbook/manpage.refs
deleted file mode 100644
index 16ffc79..0000000
--- a/contrib/wpa/wpa_supplicant/doc/docbook/manpage.refs
+++ /dev/null
@@ -1,4 +0,0 @@
-{
- '' => '',
- '' => ''
-}
diff --git a/contrib/wpa/wpa_supplicant/doc/docbook/wpa_background.8 b/contrib/wpa/wpa_supplicant/doc/docbook/wpa_background.8
deleted file mode 100644
index 19162a3..0000000
--- a/contrib/wpa/wpa_supplicant/doc/docbook/wpa_background.8
+++ /dev/null
@@ -1,84 +0,0 @@
-.\" This manpage has been automatically generated by docbook2man
-.\" from a DocBook document. This tool can be found at:
-.\" <http://shell.ipoline.com/~elmert/comp/docbook2X/>
-.\" Please send any bug reports, improvements, comments, patches,
-.\" etc. to Steve Cheng <steve@ggi-project.org>.
-.TH "WPA_BACKGROUND" "8" "07 September 2010" "" ""
-
-.SH NAME
-wpa_background \- Background information on Wi-Fi Protected Access and IEEE 802.11i
-.SH "WPA"
-.PP
-The original security mechanism of IEEE 802.11 standard was
-not designed to be strong and has proven to be insufficient for
-most networks that require some kind of security. Task group I
-(Security) of IEEE 802.11 working group
-(http://www.ieee802.org/11/) has worked to address the flaws of
-the base standard and has in practice completed its work in May
-2004. The IEEE 802.11i amendment to the IEEE 802.11 standard was
-approved in June 2004 and published in July 2004.
-.PP
-Wi-Fi Alliance (http://www.wi-fi.org/) used a draft version
-of the IEEE 802.11i work (draft 3.0) to define a subset of the
-security enhancements that can be implemented with existing wlan
-hardware. This is called Wi-Fi Protected Access<TM> (WPA). This
-has now become a mandatory component of interoperability testing
-and certification done by Wi-Fi Alliance. Wi-Fi provides
-information about WPA at its web site
-(http://www.wi-fi.org/OpenSection/protected_access.asp).
-.PP
-IEEE 802.11 standard defined wired equivalent privacy (WEP)
-algorithm for protecting wireless networks. WEP uses RC4 with
-40-bit keys, 24-bit initialization vector (IV), and CRC32 to
-protect against packet forgery. All these choices have proven to
-be insufficient: key space is too small against current attacks,
-RC4 key scheduling is insufficient (beginning of the pseudorandom
-stream should be skipped), IV space is too small and IV reuse
-makes attacks easier, there is no replay protection, and non-keyed
-authentication does not protect against bit flipping packet
-data.
-.PP
-WPA is an intermediate solution for the security issues. It
-uses Temporal Key Integrity Protocol (TKIP) to replace WEP. TKIP
-is a compromise on strong security and possibility to use existing
-hardware. It still uses RC4 for the encryption like WEP, but with
-per-packet RC4 keys. In addition, it implements replay protection,
-keyed packet authentication mechanism (Michael MIC).
-.PP
-Keys can be managed using two different mechanisms. WPA can
-either use an external authentication server (e.g., RADIUS) and
-EAP just like IEEE 802.1X is using or pre-shared keys without need
-for additional servers. Wi-Fi calls these "WPA-Enterprise" and
-"WPA-Personal", respectively. Both mechanisms will generate a
-master session key for the Authenticator (AP) and Supplicant
-(client station).
-.PP
-WPA implements a new key handshake (4-Way Handshake and
-Group Key Handshake) for generating and exchanging data encryption
-keys between the Authenticator and Supplicant. This handshake is
-also used to verify that both Authenticator and Supplicant know
-the master session key. These handshakes are identical regardless
-of the selected key management mechanism (only the method for
-generating master session key changes).
-.SH "IEEE 802.11I / WPA2"
-.PP
-The design for parts of IEEE 802.11i that were not included
-in WPA has finished (May 2004) and this amendment to IEEE 802.11
-was approved in June 2004. Wi-Fi Alliance is using the final IEEE
-802.11i as a new version of WPA called WPA2. This includes, e.g.,
-support for more robust encryption algorithm (CCMP: AES in Counter
-mode with CBC-MAC) to replace TKIP and optimizations for handoff
-(reduced number of messages in initial key handshake,
-pre-authentication, and PMKSA caching).
-.SH "SEE ALSO"
-.PP
-\fBwpa_supplicant\fR(8)
-.SH "LEGAL"
-.PP
-wpa_supplicant is copyright (c) 2003-2007,
-Jouni Malinen <j@w1.fi> and
-contributors.
-All Rights Reserved.
-.PP
-This program is dual-licensed under both the GPL version 2
-and BSD license. Either license may be used at your option.
diff --git a/contrib/wpa/wpa_supplicant/doc/docbook/wpa_background.sgml b/contrib/wpa/wpa_supplicant/doc/docbook/wpa_background.sgml
deleted file mode 100644
index f47235b..0000000
--- a/contrib/wpa/wpa_supplicant/doc/docbook/wpa_background.sgml
+++ /dev/null
@@ -1,101 +0,0 @@
-<!doctype refentry PUBLIC "-//OASIS//DTD DocBook V4.1//EN">
-
-<refentry>
- <refmeta>
- <refentrytitle>wpa_background</refentrytitle>
- <manvolnum>8</manvolnum>
- </refmeta>
- <refnamediv>
- <refname>wpa_background</refname>
- <refpurpose>Background information on Wi-Fi Protected Access and IEEE 802.11i</refpurpose>
- </refnamediv>
- <refsect1>
- <title>WPA</title>
-
- <para>The original security mechanism of IEEE 802.11 standard was
- not designed to be strong and has proven to be insufficient for
- most networks that require some kind of security. Task group I
- (Security) of IEEE 802.11 working group
- (http://www.ieee802.org/11/) has worked to address the flaws of
- the base standard and has in practice completed its work in May
- 2004. The IEEE 802.11i amendment to the IEEE 802.11 standard was
- approved in June 2004 and published in July 2004.</para>
-
- <para>Wi-Fi Alliance (http://www.wi-fi.org/) used a draft version
- of the IEEE 802.11i work (draft 3.0) to define a subset of the
- security enhancements that can be implemented with existing wlan
- hardware. This is called Wi-Fi Protected Access&lt;TM&gt; (WPA). This
- has now become a mandatory component of interoperability testing
- and certification done by Wi-Fi Alliance. Wi-Fi provides
- information about WPA at its web site
- (http://www.wi-fi.org/OpenSection/protected_access.asp).</para>
-
- <para>IEEE 802.11 standard defined wired equivalent privacy (WEP)
- algorithm for protecting wireless networks. WEP uses RC4 with
- 40-bit keys, 24-bit initialization vector (IV), and CRC32 to
- protect against packet forgery. All these choices have proven to
- be insufficient: key space is too small against current attacks,
- RC4 key scheduling is insufficient (beginning of the pseudorandom
- stream should be skipped), IV space is too small and IV reuse
- makes attacks easier, there is no replay protection, and non-keyed
- authentication does not protect against bit flipping packet
- data.</para>
-
- <para>WPA is an intermediate solution for the security issues. It
- uses Temporal Key Integrity Protocol (TKIP) to replace WEP. TKIP
- is a compromise on strong security and possibility to use existing
- hardware. It still uses RC4 for the encryption like WEP, but with
- per-packet RC4 keys. In addition, it implements replay protection,
- keyed packet authentication mechanism (Michael MIC).</para>
-
- <para>Keys can be managed using two different mechanisms. WPA can
- either use an external authentication server (e.g., RADIUS) and
- EAP just like IEEE 802.1X is using or pre-shared keys without need
- for additional servers. Wi-Fi calls these "WPA-Enterprise" and
- "WPA-Personal", respectively. Both mechanisms will generate a
- master session key for the Authenticator (AP) and Supplicant
- (client station).</para>
-
- <para>WPA implements a new key handshake (4-Way Handshake and
- Group Key Handshake) for generating and exchanging data encryption
- keys between the Authenticator and Supplicant. This handshake is
- also used to verify that both Authenticator and Supplicant know
- the master session key. These handshakes are identical regardless
- of the selected key management mechanism (only the method for
- generating master session key changes).</para>
- </refsect1>
-
- <refsect1>
- <title>IEEE 802.11i / WPA2</title>
-
- <para>The design for parts of IEEE 802.11i that were not included
- in WPA has finished (May 2004) and this amendment to IEEE 802.11
- was approved in June 2004. Wi-Fi Alliance is using the final IEEE
- 802.11i as a new version of WPA called WPA2. This includes, e.g.,
- support for more robust encryption algorithm (CCMP: AES in Counter
- mode with CBC-MAC) to replace TKIP and optimizations for handoff
- (reduced number of messages in initial key handshake,
- pre-authentication, and PMKSA caching).</para>
- </refsect1>
-
- <refsect1>
- <title>See Also</title>
- <para>
- <citerefentry>
- <refentrytitle>wpa_supplicant</refentrytitle>
- <manvolnum>8</manvolnum>
- </citerefentry>
- </para>
- </refsect1>
-
- <refsect1>
- <title>Legal</title>
- <para>wpa_supplicant is copyright (c) 2003-2007,
- Jouni Malinen <email>j@w1.fi</email> and
- contributors.
- All Rights Reserved.</para>
-
- <para>This program is dual-licensed under both the GPL version 2
- and BSD license. Either license may be used at your option.</para>
- </refsect1>
-</refentry>
diff --git a/contrib/wpa/wpa_supplicant/doc/docbook/wpa_cli.8 b/contrib/wpa/wpa_supplicant/doc/docbook/wpa_cli.8
deleted file mode 100644
index e22fc92..0000000
--- a/contrib/wpa/wpa_supplicant/doc/docbook/wpa_cli.8
+++ /dev/null
@@ -1,210 +0,0 @@
-.\" This manpage has been automatically generated by docbook2man
-.\" from a DocBook document. This tool can be found at:
-.\" <http://shell.ipoline.com/~elmert/comp/docbook2X/>
-.\" Please send any bug reports, improvements, comments, patches,
-.\" etc. to Steve Cheng <steve@ggi-project.org>.
-.TH "WPA_CLI" "8" "07 September 2010" "" ""
-
-.SH NAME
-wpa_cli \- WPA command line client
-.SH SYNOPSIS
-
-\fBwpa_cli\fR [ \fB-p \fIpath to ctrl sockets\fB\fR ] [ \fB-i \fIifname\fB\fR ] [ \fB-hvB\fR ] [ \fB-a \fIaction file\fB\fR ] [ \fB-P \fIpid file\fB\fR ] [ \fB\fIcommand ...\fB\fR ]
-
-.SH "OVERVIEW"
-.PP
-wpa_cli is a text-based frontend program for interacting
-with wpa_supplicant. It is used to query current status, change
-configuration, trigger events, and request interactive user
-input.
-.PP
-wpa_cli can show the current authentication status, selected
-security mode, dot11 and dot1x MIBs, etc. In addition, it can
-configure some variables like EAPOL state machine parameters and
-trigger events like reassociation and IEEE 802.1X
-logoff/logon. wpa_cli provides a user interface to request
-authentication information, like username and password, if these
-are not included in the configuration. This can be used to
-implement, e.g., one-time-passwords or generic token card
-authentication where the authentication is based on a
-challenge-response that uses an external device for generating the
-response.
-.PP
-The control interface of wpa_supplicant can be configured to
-allow non-root user access (ctrl_interface GROUP= parameter in the
-configuration file). This makes it possible to run wpa_cli with a
-normal user account.
-.PP
-wpa_cli supports two modes: interactive and command
-line. Both modes share the same command set and the main
-difference is in interactive mode providing access to unsolicited
-messages (event messages, username/password requests).
-.PP
-Interactive mode is started when wpa_cli is executed without
-including the command as a command line parameter. Commands are
-then entered on the wpa_cli prompt. In command line mode, the same
-commands are entered as command line arguments for wpa_cli.
-.SH "INTERACTIVE AUTHENTICATION PARAMETERS REQUEST"
-.PP
-When wpa_supplicant need authentication parameters, like
-username and password, which are not present in the configuration
-file, it sends a request message to all attached frontend programs,
-e.g., wpa_cli in interactive mode. wpa_cli shows these requests
-with "CTRL-REQ-<type>-<id>:<text>"
-prefix. <type> is IDENTITY, PASSWORD, or OTP
-(one-time-password). <id> is a unique identifier for the
-current network. <text> is description of the request. In
-case of OTP request, it includes the challenge from the
-authentication server.
-.PP
-The reply to these requests can be given with
-\fBidentity\fR, \fBpassword\fR, and
-\fBotp\fR commands. <id> needs to be copied from
-the matching request. \fBpassword\fR and
-\fBotp\fR commands can be used regardless of whether
-the request was for PASSWORD or OTP. The main difference between these
-two commands is that values given with \fBpassword\fR are
-remembered as long as wpa_supplicant is running whereas values given
-with \fBotp\fR are used only once and then forgotten,
-i.e., wpa_supplicant will ask frontend for a new value for every use.
-This can be used to implement one-time-password lists and generic token
-card -based authentication.
-.PP
-Example request for password and a matching reply:
-.sp
-.RS
-
-.nf
-CTRL-REQ-PASSWORD-1:Password needed for SSID foobar
-> password 1 mysecretpassword
-.fi
-.RE
-.PP
-Example request for generic token card challenge-response:
-.sp
-.RS
-
-.nf
-CTRL-REQ-OTP-2:Challenge 1235663 needed for SSID foobar
-> otp 2 9876
-.fi
-.RE
-.SH "COMMAND ARGUMENTS"
-.TP
-\fB-p path\fR
-Change the path where control sockets should
-be found.
-.TP
-\fB-i ifname\fR
-Specify the interface that is being
-configured. By default, choose the first interface found with
-a control socket in the socket path.
-.TP
-\fB-h\fR
-Help. Show a usage message.
-.TP
-\fB-v\fR
-Show version information.
-.TP
-\fB-B\fR
-Run as a daemon in the background.
-.TP
-\fB-a file\fR
-Run in daemon mode executing the action file
-based on events from wpa_supplicant. The specified file will
-be executed with the first argument set to interface name and
-second to "CONNECTED" or "DISCONNECTED" depending on the event.
-This can be used to execute networking tools required to configure
-the interface.
-
-Additionally, three environmental variables are available to
-the file: WPA_CTRL_DIR, WPA_ID, and WPA_ID_STR. WPA_CTRL_DIR
-contains the absolute path to the ctrl_interface socket. WPA_ID
-contains the unique network_id identifier assigned to the active
-network, and WPA_ID_STR contains the content of the id_str option.
-.TP
-\fB-P file\fR
-Set the location of the PID
-file.
-.TP
-\fBcommand\fR
-Run a command. The available commands are
-listed in the next section.
-.SH "COMMANDS"
-.PP
-The following commands are available:
-.TP
-\fBstatus\fR
-get current WPA/EAPOL/EAP status
-.TP
-\fBmib\fR
-get MIB variables (dot1x, dot11)
-.TP
-\fBhelp\fR
-show this usage help
-.TP
-\fBinterface [ifname]\fR
-show interfaces/select interface
-.TP
-\fBlevel <debug level>\fR
-change debug level
-.TP
-\fBlicense\fR
-show full wpa_cli license
-.TP
-\fBlogoff\fR
-IEEE 802.1X EAPOL state machine logoff
-.TP
-\fBlogon\fR
-IEEE 802.1X EAPOL state machine logon
-.TP
-\fBset\fR
-set variables (shows list of variables when run without arguments)
-.TP
-\fBpmksa\fR
-show PMKSA cache
-.TP
-\fBreassociate\fR
-force reassociation
-.TP
-\fBreconfigure\fR
-force wpa_supplicant to re-read its configuration file
-.TP
-\fBpreauthenticate <BSSID>\fR
-force preauthentication
-.TP
-\fBidentity <network id> <identity>\fR
-configure identity for an SSID
-.TP
-\fBpassword <network id> <password>\fR
-configure password for an SSID
-.TP
-\fBpin <network id> <pin>\fR
-configure pin for an SSID
-.TP
-\fBotp <network id> <password>\fR
-configure one-time-password for an SSID
-.TP
-\fBbssid <network id> <BSSID>\fR
-set preferred BSSID for an SSID
-.TP
-\fBlist_networks\fR
-list configured networks
-.TP
-\fBterminate\fR
-terminate \fBwpa_supplicant\fR
-.TP
-\fBquit\fR
-exit wpa_cli
-.SH "SEE ALSO"
-.PP
-\fBwpa_supplicant\fR(8)
-.SH "LEGAL"
-.PP
-wpa_supplicant is copyright (c) 2003-2007,
-Jouni Malinen <j@w1.fi> and
-contributors.
-All Rights Reserved.
-.PP
-This program is dual-licensed under both the GPL version 2
-and BSD license. Either license may be used at your option.
diff --git a/contrib/wpa/wpa_supplicant/doc/docbook/wpa_cli.sgml b/contrib/wpa/wpa_supplicant/doc/docbook/wpa_cli.sgml
deleted file mode 100644
index 1fe98f4..0000000
--- a/contrib/wpa/wpa_supplicant/doc/docbook/wpa_cli.sgml
+++ /dev/null
@@ -1,339 +0,0 @@
-<!doctype refentry PUBLIC "-//OASIS//DTD DocBook V4.1//EN">
-
-<refentry>
- <refmeta>
- <refentrytitle>wpa_cli</refentrytitle>
- <manvolnum>8</manvolnum>
- </refmeta>
- <refnamediv>
- <refname>wpa_cli</refname>
-
- <refpurpose>WPA command line client</refpurpose>
- </refnamediv>
-
- <refsynopsisdiv>
- <cmdsynopsis>
- <command>wpa_cli</command>
- <arg>-p <replaceable>path to ctrl sockets</replaceable></arg>
- <arg>-i <replaceable>ifname</replaceable></arg>
- <arg>-hvB</arg>
- <arg>-a <replaceable>action file</replaceable></arg>
- <arg>-P <replaceable>pid file</replaceable></arg>
- <arg><replaceable>command ...</replaceable></arg>
- </cmdsynopsis>
- </refsynopsisdiv>
-
- <refsect1>
- <title>Overview</title>
-
- <para>wpa_cli is a text-based frontend program for interacting
- with wpa_supplicant. It is used to query current status, change
- configuration, trigger events, and request interactive user
- input.</para>
-
- <para>wpa_cli can show the current authentication status, selected
- security mode, dot11 and dot1x MIBs, etc. In addition, it can
- configure some variables like EAPOL state machine parameters and
- trigger events like reassociation and IEEE 802.1X
- logoff/logon. wpa_cli provides a user interface to request
- authentication information, like username and password, if these
- are not included in the configuration. This can be used to
- implement, e.g., one-time-passwords or generic token card
- authentication where the authentication is based on a
- challenge-response that uses an external device for generating the
- response.</para>
-
- <para>The control interface of wpa_supplicant can be configured to
- allow non-root user access (ctrl_interface GROUP= parameter in the
- configuration file). This makes it possible to run wpa_cli with a
- normal user account.</para>
-
- <para>wpa_cli supports two modes: interactive and command
- line. Both modes share the same command set and the main
- difference is in interactive mode providing access to unsolicited
- messages (event messages, username/password requests).</para>
-
- <para>Interactive mode is started when wpa_cli is executed without
- including the command as a command line parameter. Commands are
- then entered on the wpa_cli prompt. In command line mode, the same
- commands are entered as command line arguments for wpa_cli.</para>
- </refsect1>
- <refsect1>
- <title>Interactive authentication parameters request</title>
-
- <para>When wpa_supplicant need authentication parameters, like
- username and password, which are not present in the configuration
- file, it sends a request message to all attached frontend programs,
- e.g., wpa_cli in interactive mode. wpa_cli shows these requests
- with "CTRL-REQ-&lt;type&gt;-&lt;id&gt;:&lt;text&gt;"
- prefix. &lt;type&gt; is IDENTITY, PASSWORD, or OTP
- (one-time-password). &lt;id&gt; is a unique identifier for the
- current network. &lt;text&gt; is description of the request. In
- case of OTP request, it includes the challenge from the
- authentication server.</para>
-
- <para>The reply to these requests can be given with
- <emphasis>identity</emphasis>, <emphasis>password</emphasis>, and
- <emphasis>otp</emphasis> commands. &lt;id&gt; needs to be copied from
- the matching request. <emphasis>password</emphasis> and
- <emphasis>otp</emphasis> commands can be used regardless of whether
- the request was for PASSWORD or OTP. The main difference between these
- two commands is that values given with <emphasis>password</emphasis> are
- remembered as long as wpa_supplicant is running whereas values given
- with <emphasis>otp</emphasis> are used only once and then forgotten,
- i.e., wpa_supplicant will ask frontend for a new value for every use.
- This can be used to implement one-time-password lists and generic token
- card -based authentication.</para>
-
- <para>Example request for password and a matching reply:</para>
-
-<blockquote><programlisting>
-CTRL-REQ-PASSWORD-1:Password needed for SSID foobar
-> password 1 mysecretpassword
-</programlisting></blockquote>
-
- <para>Example request for generic token card challenge-response:</para>
-
-<blockquote><programlisting>
-CTRL-REQ-OTP-2:Challenge 1235663 needed for SSID foobar
-> otp 2 9876
-</programlisting></blockquote>
-
- </refsect1>
- <refsect1>
- <title>Command Arguments</title>
- <variablelist>
- <varlistentry>
- <term>-p path</term>
-
- <listitem><para>Change the path where control sockets should
- be found.</para></listitem>
- </varlistentry>
-
- <varlistentry>
- <term>-i ifname</term>
-
- <listitem><para>Specify the interface that is being
- configured. By default, choose the first interface found with
- a control socket in the socket path.</para></listitem>
- </varlistentry>
-
- <varlistentry>
- <term>-h</term>
- <listitem><para>Help. Show a usage message.</para></listitem>
- </varlistentry>
-
-
- <varlistentry>
- <term>-v</term>
- <listitem><para>Show version information.</para></listitem>
- </varlistentry>
-
-
- <varlistentry>
- <term>-B</term>
- <listitem><para>Run as a daemon in the background.</para></listitem>
- </varlistentry>
-
- <varlistentry>
- <term>-a file</term>
-
- <listitem><para>Run in daemon mode executing the action file
- based on events from wpa_supplicant. The specified file will
- be executed with the first argument set to interface name and
- second to "CONNECTED" or "DISCONNECTED" depending on the event.
- This can be used to execute networking tools required to configure
- the interface.</para>
-
- <para>Additionally, three environmental variables are available to
- the file: WPA_CTRL_DIR, WPA_ID, and WPA_ID_STR. WPA_CTRL_DIR
- contains the absolute path to the ctrl_interface socket. WPA_ID
- contains the unique network_id identifier assigned to the active
- network, and WPA_ID_STR contains the content of the id_str option.
- </para></listitem>
- </varlistentry>
-
- <varlistentry>
- <term>-P file</term>
-
- <listitem><para>Set the location of the PID
- file.</para></listitem>
- </varlistentry>
-
- <varlistentry>
- <term>command</term>
-
- <listitem><para>Run a command. The available commands are
- listed in the next section.</para></listitem>
-
- </varlistentry>
- </variablelist>
- </refsect1>
- <refsect1>
- <title>Commands</title>
- <para>The following commands are available:</para>
-
- <variablelist>
- <varlistentry>
- <term>status</term>
- <listitem>
- <para>get current WPA/EAPOL/EAP status</para>
- </listitem>
- </varlistentry>
-
- <varlistentry>
- <term>mib</term>
- <listitem>
- <para>get MIB variables (dot1x, dot11)</para>
- </listitem>
- </varlistentry>
-
- <varlistentry>
- <term>help</term>
- <listitem>
- <para>show this usage help</para>
- </listitem>
- </varlistentry>
-
- <varlistentry>
- <term>interface [ifname]</term>
- <listitem>
- <para>show interfaces/select interface</para>
- </listitem>
- </varlistentry>
-
- <varlistentry>
- <term>level &lt;debug level&gt;</term>
- <listitem>
- <para>change debug level</para>
- </listitem>
- </varlistentry>
-
- <varlistentry>
- <term>license</term>
- <listitem>
- <para>show full wpa_cli license</para>
- </listitem>
- </varlistentry>
-
- <varlistentry>
- <term>logoff</term>
- <listitem>
- <para>IEEE 802.1X EAPOL state machine logoff</para>
- </listitem>
- </varlistentry>
-
- <varlistentry>
- <term>logon</term>
- <listitem>
- <para>IEEE 802.1X EAPOL state machine logon</para>
- </listitem>
- </varlistentry>
-
- <varlistentry>
- <term>set</term>
- <listitem>
- <para>set variables (shows list of variables when run without arguments)</para>
- </listitem>
- </varlistentry>
- <varlistentry>
- <term>pmksa</term>
- <listitem>
- <para>show PMKSA cache</para>
- </listitem>
- </varlistentry>
- <varlistentry>
- <term>reassociate</term>
- <listitem>
- <para>force reassociation</para>
- </listitem>
- </varlistentry>
- <varlistentry>
- <term>reconfigure</term>
- <listitem>
- <para>force wpa_supplicant to re-read its configuration file</para>
- </listitem>
- </varlistentry>
-
- <varlistentry>
- <term>preauthenticate &lt;BSSID&gt;</term>
- <listitem>
- <para>force preauthentication</para>
- </listitem>
- </varlistentry>
-
- <varlistentry>
- <term>identity &lt;network id&gt; &lt;identity&gt;</term>
- <listitem>
- <para>configure identity for an SSID</para>
- </listitem>
- </varlistentry>
-
- <varlistentry>
- <term>password &lt;network id&gt; &lt;password&gt;</term>
- <listitem>
- <para>configure password for an SSID</para>
- </listitem>
- </varlistentry>
-
- <varlistentry>
- <term>pin &lt;network id&gt; &lt;pin&gt;</term>
- <listitem>
- <para>configure pin for an SSID</para>
- </listitem>
- </varlistentry>
-
- <varlistentry>
- <term>otp &lt;network id&gt; &lt;password&gt;</term>
- <listitem>
- <para>configure one-time-password for an SSID</para>
- </listitem>
- </varlistentry>
-
- <varlistentry>
- <term>bssid &lt;network id&gt; &lt;BSSID&gt;</term>
- <listitem>
- <para>set preferred BSSID for an SSID</para>
- </listitem>
- </varlistentry>
-
- <varlistentry>
- <term>list_networks</term>
- <listitem>
- <para>list configured networks</para>
- </listitem>
- </varlistentry>
-
- <varlistentry>
- <term>terminate</term>
- <listitem>
- <para>terminate <command>wpa_supplicant</command></para>
- </listitem>
- </varlistentry>
-
- <varlistentry>
- <term>quit</term>
- <listitem><para>exit wpa_cli</para></listitem>
- </varlistentry>
- </variablelist>
- </refsect1>
- <refsect1>
- <title>See Also</title>
- <para>
- <citerefentry>
- <refentrytitle>wpa_supplicant</refentrytitle>
- <manvolnum>8</manvolnum>
- </citerefentry>
- </para>
- </refsect1>
- <refsect1>
- <title>Legal</title>
- <para>wpa_supplicant is copyright (c) 2003-2007,
- Jouni Malinen <email>j@w1.fi</email> and
- contributors.
- All Rights Reserved.</para>
-
- <para>This program is dual-licensed under both the GPL version 2
- and BSD license. Either license may be used at your option.</para>
- </refsect1>
-</refentry>
diff --git a/contrib/wpa/wpa_supplicant/doc/docbook/wpa_gui.8 b/contrib/wpa/wpa_supplicant/doc/docbook/wpa_gui.8
deleted file mode 100644
index f58a894..0000000
--- a/contrib/wpa/wpa_supplicant/doc/docbook/wpa_gui.8
+++ /dev/null
@@ -1,51 +0,0 @@
-.\" This manpage has been automatically generated by docbook2man
-.\" from a DocBook document. This tool can be found at:
-.\" <http://shell.ipoline.com/~elmert/comp/docbook2X/>
-.\" Please send any bug reports, improvements, comments, patches,
-.\" etc. to Steve Cheng <steve@ggi-project.org>.
-.TH "WPA_GUI" "8" "07 September 2010" "" ""
-
-.SH NAME
-wpa_gui \- WPA Graphical User Interface
-.SH SYNOPSIS
-
-\fBwpa_gui\fR [ \fB-p \fIpath to ctrl sockets\fB\fR ] [ \fB-i \fIifname\fB\fR ] [ \fB-t\fR ]
-
-.SH "OVERVIEW"
-.PP
-wpa_gui is a QT graphical frontend program for interacting
-with wpa_supplicant. It is used to query current status, change
-configuration and request interactive user input.
-.PP
-wpa_gui supports (almost) all of the interactive status and
-configuration features of the command line client, wpa_cli. Refer
-to the wpa_cli manpage for a comprehensive list of the
-interactive mode features.
-.SH "COMMAND ARGUMENTS"
-.TP
-\fB-p path\fR
-Change the path where control sockets should
-be found.
-.TP
-\fB-i ifname\fR
-Specify the interface that is being
-configured. By default, choose the first interface found with
-a control socket in the socket path.
-.TP
-\fB-t\fR
-Start program in the system tray only (if the window
-manager supports it). By default the main status window is
-shown.
-.SH "SEE ALSO"
-.PP
-\fBwpa_cli\fR(8)
-\fBwpa_supplicant\fR(8)
-.SH "LEGAL"
-.PP
-wpa_supplicant is copyright (c) 2003-2007,
-Jouni Malinen <j@w1.fi> and
-contributors.
-All Rights Reserved.
-.PP
-This program is dual-licensed under both the GPL version 2
-and BSD license. Either license may be used at your option.
diff --git a/contrib/wpa/wpa_supplicant/doc/docbook/wpa_gui.sgml b/contrib/wpa/wpa_supplicant/doc/docbook/wpa_gui.sgml
deleted file mode 100644
index 41b5849..0000000
--- a/contrib/wpa/wpa_supplicant/doc/docbook/wpa_gui.sgml
+++ /dev/null
@@ -1,85 +0,0 @@
-<!doctype refentry PUBLIC "-//OASIS//DTD DocBook V4.1//EN">
-
-<refentry>
- <refmeta>
- <refentrytitle>wpa_gui</refentrytitle>
- <manvolnum>8</manvolnum>
- </refmeta>
- <refnamediv>
- <refname>wpa_gui</refname>
-
- <refpurpose>WPA Graphical User Interface</refpurpose>
- </refnamediv>
-
- <refsynopsisdiv>
- <cmdsynopsis>
- <command>wpa_gui</command>
- <arg>-p <replaceable>path to ctrl sockets</replaceable></arg>
- <arg>-i <replaceable>ifname</replaceable></arg>
- <arg>-t</arg>
- </cmdsynopsis>
- </refsynopsisdiv>
-
- <refsect1>
- <title>Overview</title>
-
- <para>wpa_gui is a QT graphical frontend program for interacting
- with wpa_supplicant. It is used to query current status, change
- configuration and request interactive user input.</para>
-
- <para>wpa_gui supports (almost) all of the interactive status and
- configuration features of the command line client, wpa_cli. Refer
- to the wpa_cli manpage for a comprehensive list of the
- interactive mode features.</para>
- </refsect1>
- <refsect1>
- <title>Command Arguments</title>
- <variablelist>
- <varlistentry>
- <term>-p path</term>
-
- <listitem><para>Change the path where control sockets should
- be found.</para></listitem>
- </varlistentry>
-
- <varlistentry>
- <term>-i ifname</term>
-
- <listitem><para>Specify the interface that is being
- configured. By default, choose the first interface found with
- a control socket in the socket path.</para></listitem>
- </varlistentry>
-
- <varlistentry>
- <term>-t</term>
-
- <listitem><para>Start program in the system tray only (if the window
- manager supports it). By default the main status window is
- shown.</para></listitem>
- </varlistentry>
- </variablelist>
- </refsect1>
- <refsect1>
- <title>See Also</title>
- <para>
- <citerefentry>
- <refentrytitle>wpa_cli</refentrytitle>
- <manvolnum>8</manvolnum>
- </citerefentry>
- <citerefentry>
- <refentrytitle>wpa_supplicant</refentrytitle>
- <manvolnum>8</manvolnum>
- </citerefentry>
- </para>
- </refsect1>
- <refsect1>
- <title>Legal</title>
- <para>wpa_supplicant is copyright (c) 2003-2007,
- Jouni Malinen <email>j@w1.fi</email> and
- contributors.
- All Rights Reserved.</para>
-
- <para>This program is dual-licensed under both the GPL version 2
- and BSD license. Either license may be used at your option.</para>
- </refsect1>
-</refentry>
diff --git a/contrib/wpa/wpa_supplicant/doc/docbook/wpa_passphrase.8 b/contrib/wpa/wpa_supplicant/doc/docbook/wpa_passphrase.8
deleted file mode 100644
index 945c1c0..0000000
--- a/contrib/wpa/wpa_supplicant/doc/docbook/wpa_passphrase.8
+++ /dev/null
@@ -1,40 +0,0 @@
-.\" This manpage has been automatically generated by docbook2man
-.\" from a DocBook document. This tool can be found at:
-.\" <http://shell.ipoline.com/~elmert/comp/docbook2X/>
-.\" Please send any bug reports, improvements, comments, patches,
-.\" etc. to Steve Cheng <steve@ggi-project.org>.
-.TH "WPA_PASSPHRASE" "8" "07 September 2010" "" ""
-
-.SH NAME
-wpa_passphrase \- Generate a WPA PSK from an ASCII passphrase for a SSID
-.SH SYNOPSIS
-
-\fBwpa_passphrase\fR [ \fB\fIssid\fB\fR ] [ \fB\fIpassphrase\fB\fR ]
-
-.SH "OVERVIEW"
-.PP
-\fBwpa_passphrase\fR pre-computes PSK entries for
-network configuration blocks of a
-\fIwpa_supplicant.conf\fR file. An ASCII passphrase
-and SSID are used to generate a 256-bit PSK.
-.SH "OPTIONS"
-.TP
-\fBssid\fR
-The SSID whose passphrase should be derived.
-.TP
-\fBpassphrase\fR
-The passphrase to use. If not included on the command line,
-passphrase will be read from standard input.
-.SH "SEE ALSO"
-.PP
-\fBwpa_supplicant.conf\fR(5)
-\fBwpa_supplicant\fR(8)
-.SH "LEGAL"
-.PP
-wpa_supplicant is copyright (c) 2003-2007,
-Jouni Malinen <j@w1.fi> and
-contributors.
-All Rights Reserved.
-.PP
-This program is dual-licensed under both the GPL version 2
-and BSD license. Either license may be used at your option.
diff --git a/contrib/wpa/wpa_supplicant/doc/docbook/wpa_passphrase.sgml b/contrib/wpa/wpa_supplicant/doc/docbook/wpa_passphrase.sgml
deleted file mode 100644
index 402ea09..0000000
--- a/contrib/wpa/wpa_supplicant/doc/docbook/wpa_passphrase.sgml
+++ /dev/null
@@ -1,73 +0,0 @@
-<!doctype refentry PUBLIC "-//OASIS//DTD DocBook V4.1//EN">
-
-<refentry>
- <refmeta>
- <refentrytitle>wpa_passphrase</refentrytitle>
- <manvolnum>8</manvolnum>
- </refmeta>
- <refnamediv>
- <refname>wpa_passphrase</refname>
- <refpurpose>Generate a WPA PSK from an ASCII passphrase for a SSID</refpurpose>
- </refnamediv>
- <refsynopsisdiv>
- <cmdsynopsis>
- <command>wpa_passphrase</command>
- <arg><replaceable>ssid</replaceable></arg>
- <arg><replaceable>passphrase</replaceable></arg>
- </cmdsynopsis>
- </refsynopsisdiv>
-
- <refsect1>
- <title>Overview</title>
-
- <para><command>wpa_passphrase</command> pre-computes PSK entries for
- network configuration blocks of a
- <filename>wpa_supplicant.conf</filename> file. An ASCII passphrase
- and SSID are used to generate a 256-bit PSK.</para>
- </refsect1>
-
- <refsect1>
- <title>Options</title>
- <variablelist>
- <varlistentry>
- <term>ssid</term>
- <listitem>
- <para>The SSID whose passphrase should be derived.</para>
- </listitem>
- </varlistentry>
-
- <varlistentry>
- <term>passphrase</term>
- <listitem>
- <para>The passphrase to use. If not included on the command line,
- passphrase will be read from standard input.</para>
- </listitem>
- </varlistentry>
- </variablelist>
- </refsect1>
-
- <refsect1>
- <title>See Also</title>
- <para>
- <citerefentry>
- <refentrytitle>wpa_supplicant.conf</refentrytitle>
- <manvolnum>5</manvolnum>
- </citerefentry>
- <citerefentry>
- <refentrytitle>wpa_supplicant</refentrytitle>
- <manvolnum>8</manvolnum>
- </citerefentry>
- </para>
-
- </refsect1>
- <refsect1>
- <title>Legal</title>
- <para>wpa_supplicant is copyright (c) 2003-2007,
- Jouni Malinen <email>j@w1.fi</email> and
- contributors.
- All Rights Reserved.</para>
-
- <para>This program is dual-licensed under both the GPL version 2
- and BSD license. Either license may be used at your option.</para>
- </refsect1>
-</refentry>
diff --git a/contrib/wpa/wpa_supplicant/doc/docbook/wpa_priv.8 b/contrib/wpa/wpa_supplicant/doc/docbook/wpa_priv.8
deleted file mode 100644
index 05ad983..0000000
--- a/contrib/wpa/wpa_supplicant/doc/docbook/wpa_priv.8
+++ /dev/null
@@ -1,120 +0,0 @@
-.\" This manpage has been automatically generated by docbook2man
-.\" from a DocBook document. This tool can be found at:
-.\" <http://shell.ipoline.com/~elmert/comp/docbook2X/>
-.\" Please send any bug reports, improvements, comments, patches,
-.\" etc. to Steve Cheng <steve@ggi-project.org>.
-.TH "WPA_PRIV" "8" "07 September 2010" "" ""
-
-.SH NAME
-wpa_priv \- wpa_supplicant privilege separation helper
-.SH SYNOPSIS
-
-\fBwpa_priv\fR [ \fB-c \fIctrl path\fB\fR ] [ \fB-Bdd\fR ] [ \fB-P \fIpid file\fB\fR ] [ \fBdriver:ifname \fI[driver:ifname ...]\fB\fR ]
-
-.SH "OVERVIEW"
-.PP
-\fBwpa_priv\fR is a privilege separation helper that
-minimizes the size of \fBwpa_supplicant\fR code that needs
-to be run with root privileges.
-.PP
-If enabled, privileged operations are done in the wpa_priv process
-while leaving rest of the code (e.g., EAP authentication and WPA
-handshakes) to operate in an unprivileged process (wpa_supplicant) that
-can be run as non-root user. Privilege separation restricts the effects
-of potential software errors by containing the majority of the code in an
-unprivileged process to avoid the possibility of a full system
-compromise.
-.PP
-\fBwpa_priv\fR needs to be run with network admin
-privileges (usually, root user). It opens a UNIX domain socket for each
-interface that is included on the command line; any other interface will
-be off limits for \fBwpa_supplicant\fR in this kind of
-configuration. After this, \fBwpa_supplicant\fR can be run as
-a non-root user (e.g., all standard users on a laptop or as a special
-non-privileged user account created just for this purpose to limit access
-to user files even further).
-.SH "EXAMPLE CONFIGURATION"
-.PP
-The following steps are an example of how to configure
-\fBwpa_priv\fR to allow users in the
-\fBwpapriv\fR group to communicate with
-\fBwpa_supplicant\fR with privilege separation:
-.PP
-Create user group (e.g., wpapriv) and assign users that
-should be able to use wpa_supplicant into that group.
-.PP
-Create /var/run/wpa_priv directory for UNIX domain sockets and
-control user access by setting it accessible only for the wpapriv
-group:
-.sp
-.RS
-
-.nf
-mkdir /var/run/wpa_priv
-chown root:wpapriv /var/run/wpa_priv
-chmod 0750 /var/run/wpa_priv
-.fi
-.RE
-.PP
-Start \fBwpa_priv\fR as root (e.g., from system
-startup scripts) with the enabled interfaces configured on the
-command line:
-.sp
-.RS
-
-.nf
-wpa_priv -B -c /var/run/wpa_priv -P /var/run/wpa_priv.pid wext:wlan0
-.fi
-.RE
-.PP
-Run \fBwpa_supplicant\fR as non-root with a user
-that is in the wpapriv group:
-.sp
-.RS
-
-.nf
-wpa_supplicant -i ath0 -c wpa_supplicant.conf
-.fi
-.RE
-.SH "COMMAND ARGUMENTS"
-.TP
-\fB-c ctrl path\fR
-Specify the path to wpa_priv control directory
-(Default: /var/run/wpa_priv/).
-.TP
-\fB-B\fR
-Run as a daemon in the background.
-.TP
-\fB-P file\fR
-Set the location of the PID
-file.
-.TP
-\fBdriver:ifname [driver:ifname ...]\fR
-The <driver> string dictates which of the
-supported \fBwpa_supplicant\fR driver backends is to be
-used. To get a list of supported driver types see wpa_supplicant help
-(e.g, wpa_supplicant -h). The driver backend supported by most good
-drivers is \fBwext\fR\&.
-
-The <ifname> string specifies which network
-interface is to be managed by \fBwpa_supplicant\fR
-(e.g., wlan0 or ath0).
-
-\fBwpa_priv\fR does not use the network interface
-before \fBwpa_supplicant\fR is started, so it is fine to
-include network interfaces that are not available at the time wpa_priv
-is started. wpa_priv can control multiple interfaces with one process,
-but it is also possible to run multiple \fBwpa_priv\fR
-processes at the same time, if desired.
-.SH "SEE ALSO"
-.PP
-\fBwpa_supplicant\fR(8)
-.SH "LEGAL"
-.PP
-wpa_supplicant is copyright (c) 2003-2007,
-Jouni Malinen <j@w1.fi> and
-contributors.
-All Rights Reserved.
-.PP
-This program is dual-licensed under both the GPL version 2
-and BSD license. Either license may be used at your option.
diff --git a/contrib/wpa/wpa_supplicant/doc/docbook/wpa_priv.sgml b/contrib/wpa/wpa_supplicant/doc/docbook/wpa_priv.sgml
deleted file mode 100644
index 89b8a92..0000000
--- a/contrib/wpa/wpa_supplicant/doc/docbook/wpa_priv.sgml
+++ /dev/null
@@ -1,148 +0,0 @@
-<!doctype refentry PUBLIC "-//OASIS//DTD DocBook V4.1//EN">
-
-<refentry>
- <refmeta>
- <refentrytitle>wpa_priv</refentrytitle>
- <manvolnum>8</manvolnum>
- </refmeta>
- <refnamediv>
- <refname>wpa_priv</refname>
-
- <refpurpose>wpa_supplicant privilege separation helper</refpurpose>
- </refnamediv>
-
- <refsynopsisdiv>
- <cmdsynopsis>
- <command>wpa_priv</command>
- <arg>-c <replaceable>ctrl path</replaceable></arg>
- <arg>-Bdd</arg>
- <arg>-P <replaceable>pid file</replaceable></arg>
- <arg>driver:ifname <replaceable>[driver:ifname ...]</replaceable></arg>
- </cmdsynopsis>
- </refsynopsisdiv>
-
- <refsect1>
- <title>Overview</title>
-
- <para><command>wpa_priv</command> is a privilege separation helper that
- minimizes the size of <command>wpa_supplicant</command> code that needs
- to be run with root privileges.</para>
-
- <para>If enabled, privileged operations are done in the wpa_priv process
- while leaving rest of the code (e.g., EAP authentication and WPA
- handshakes) to operate in an unprivileged process (wpa_supplicant) that
- can be run as non-root user. Privilege separation restricts the effects
- of potential software errors by containing the majority of the code in an
- unprivileged process to avoid the possibility of a full system
- compromise.</para>
-
- <para><command>wpa_priv</command> needs to be run with network admin
- privileges (usually, root user). It opens a UNIX domain socket for each
- interface that is included on the command line; any other interface will
- be off limits for <command>wpa_supplicant</command> in this kind of
- configuration. After this, <command>wpa_supplicant</command> can be run as
- a non-root user (e.g., all standard users on a laptop or as a special
- non-privileged user account created just for this purpose to limit access
- to user files even further).</para>
- </refsect1>
- <refsect1>
- <title>Example configuration</title>
-
- <para>The following steps are an example of how to configure
- <command>wpa_priv</command> to allow users in the
- <emphasis>wpapriv</emphasis> group to communicate with
- <command>wpa_supplicant</command> with privilege separation:</para>
-
- <para>Create user group (e.g., wpapriv) and assign users that
- should be able to use wpa_supplicant into that group.</para>
-
- <para>Create /var/run/wpa_priv directory for UNIX domain sockets and
- control user access by setting it accessible only for the wpapriv
- group:</para>
-
-<blockquote><programlisting>
-mkdir /var/run/wpa_priv
-chown root:wpapriv /var/run/wpa_priv
-chmod 0750 /var/run/wpa_priv
-</programlisting></blockquote>
-
- <para>Start <command>wpa_priv</command> as root (e.g., from system
- startup scripts) with the enabled interfaces configured on the
- command line:</para>
-
-<blockquote><programlisting>
-wpa_priv -B -c /var/run/wpa_priv -P /var/run/wpa_priv.pid wext:wlan0
-</programlisting></blockquote>
-
- <para>Run <command>wpa_supplicant</command> as non-root with a user
- that is in the wpapriv group:</para>
-
-<blockquote><programlisting>
-wpa_supplicant -i ath0 -c wpa_supplicant.conf
-</programlisting></blockquote>
-
- </refsect1>
- <refsect1>
- <title>Command Arguments</title>
- <variablelist>
- <varlistentry>
- <term>-c ctrl path</term>
-
- <listitem><para>Specify the path to wpa_priv control directory
- (Default: /var/run/wpa_priv/).</para></listitem>
- </varlistentry>
-
- <varlistentry>
- <term>-B</term>
- <listitem><para>Run as a daemon in the background.</para></listitem>
- </varlistentry>
-
- <varlistentry>
- <term>-P file</term>
-
- <listitem><para>Set the location of the PID
- file.</para></listitem>
- </varlistentry>
-
- <varlistentry>
- <term>driver:ifname [driver:ifname ...]</term>
-
- <listitem><para>The &lt;driver&gt; string dictates which of the
- supported <command>wpa_supplicant</command> driver backends is to be
- used. To get a list of supported driver types see wpa_supplicant help
- (e.g, wpa_supplicant -h). The driver backend supported by most good
- drivers is <emphasis>wext</emphasis>.</para>
-
- <para>The &lt;ifname&gt; string specifies which network
- interface is to be managed by <command>wpa_supplicant</command>
- (e.g., wlan0 or ath0).</para>
-
- <para><command>wpa_priv</command> does not use the network interface
- before <command>wpa_supplicant</command> is started, so it is fine to
- include network interfaces that are not available at the time wpa_priv
- is started. wpa_priv can control multiple interfaces with one process,
- but it is also possible to run multiple <command>wpa_priv</command>
- processes at the same time, if desired.</para></listitem>
- </varlistentry>
- </variablelist>
- </refsect1>
- <refsect1>
- <title>See Also</title>
- <para>
- <citerefentry>
- <refentrytitle>wpa_supplicant</refentrytitle>
- <manvolnum>8</manvolnum>
- </citerefentry>
- </para>
- </refsect1>
- <refsect1>
- <title>Legal</title>
- <para>wpa_supplicant is copyright (c) 2003-2007,
- Jouni Malinen <email>j@w1.fi</email> and
- contributors.
- All Rights Reserved.</para>
-
- <para>This program is dual-licensed under both the GPL version 2
- and BSD license. Either license may be used at your option.</para>
- </refsect1>
-</refentry>
diff --git a/contrib/wpa/wpa_supplicant/doc/docbook/wpa_supplicant.8 b/contrib/wpa/wpa_supplicant/doc/docbook/wpa_supplicant.8
deleted file mode 100644
index 3334d0c..0000000
--- a/contrib/wpa/wpa_supplicant/doc/docbook/wpa_supplicant.8
+++ /dev/null
@@ -1,583 +0,0 @@
-.\" This manpage has been automatically generated by docbook2man
-.\" from a DocBook document. This tool can be found at:
-.\" <http://shell.ipoline.com/~elmert/comp/docbook2X/>
-.\" Please send any bug reports, improvements, comments, patches,
-.\" etc. to Steve Cheng <steve@ggi-project.org>.
-.TH "WPA_SUPPLICANT" "8" "07 September 2010" "" ""
-
-.SH NAME
-wpa_supplicant \- Wi-Fi Protected Access client and IEEE 802.1X supplicant
-.SH SYNOPSIS
-
-\fBwpa_supplicant\fR [ \fB-BddfhKLqqtuvW\fR ] [ \fB-i\fIifname\fB\fR ] [ \fB-c\fIconfig file\fB\fR ] [ \fB-D\fIdriver\fB\fR ] [ \fB-P\fIPID_file\fB\fR ] [ \fB-f\fIoutput file\fB\fR ]
-
-.SH "OVERVIEW"
-.PP
-Wireless networks do not require physical access to the network equipment
-in the same way as wired networks. This makes it easier for unauthorized
-users to passively monitor a network and capture all transmitted frames.
-In addition, unauthorized use of the network is much easier. In many cases,
-this can happen even without user's explicit knowledge since the wireless
-LAN adapter may have been configured to automatically join any available
-network.
-.PP
-Link-layer encryption can be used to provide a layer of security for
-wireless networks. The original wireless LAN standard, IEEE 802.11,
-included a simple encryption mechanism, WEP. However, that proved to
-be flawed in many areas and network protected with WEP cannot be consider
-secure. IEEE 802.1X authentication and frequently changed dynamic WEP keys
-can be used to improve the network security, but even that has inherited
-security issues due to the use of WEP for encryption. Wi-Fi Protected
-Access and IEEE 802.11i amendment to the wireless LAN standard introduce
-a much improvement mechanism for securing wireless networks. IEEE 802.11i
-enabled networks that are using CCMP (encryption mechanism based on strong
-cryptographic algorithm AES) can finally be called secure used for
-applications which require efficient protection against unauthorized
-access.
-.PP
-\fBwpa_supplicant\fR is an implementation of
-the WPA Supplicant component, i.e., the part that runs in the
-client stations. It implements WPA key negotiation with a WPA
-Authenticator and EAP authentication with Authentication
-Server. In addition, it controls the roaming and IEEE 802.11
-authentication/association of the wireless LAN driver.
-.PP
-\fBwpa_supplicant\fR is designed to be a
-"daemon" program that runs in the background and acts as the
-backend component controlling the wireless
-connection. \fBwpa_supplicant\fR supports separate
-frontend programs and an example text-based frontend,
-\fBwpa_cli\fR, is included with
-wpa_supplicant.
-.PP
-Before wpa_supplicant can do its work, the network interface
-must be available. That means that the physical device must be
-present and enabled, and the driver for the device must be
-loaded. The daemon will exit immediately if the device is not already
-available.
-.PP
-After \fBwpa_supplicant\fR has configured the
-network device, higher level configuration such as DHCP may
-proceed. There are a variety of ways to integrate wpa_supplicant
-into a machine's networking scripts, a few of which are described
-in sections below.
-.PP
-The following steps are used when associating with an AP
-using WPA:
-.TP 0.2i
-\(bu
-\fBwpa_supplicant\fR requests the kernel
-driver to scan neighboring BSSes
-.TP 0.2i
-\(bu
-\fBwpa_supplicant\fR selects a BSS based on
-its configuration
-.TP 0.2i
-\(bu
-\fBwpa_supplicant\fR requests the kernel
-driver to associate with the chosen BSS
-.TP 0.2i
-\(bu
-If WPA-EAP: integrated IEEE 802.1X Supplicant
-completes EAP authentication with the
-authentication server (proxied by the Authenticator in the
-AP)
-.TP 0.2i
-\(bu
-If WPA-EAP: master key is received from the IEEE 802.1X
-Supplicant
-.TP 0.2i
-\(bu
-If WPA-PSK: \fBwpa_supplicant\fR uses PSK
-as the master session key
-.TP 0.2i
-\(bu
-\fBwpa_supplicant\fR completes WPA 4-Way
-Handshake and Group Key Handshake with the Authenticator
-(AP)
-.TP 0.2i
-\(bu
-\fBwpa_supplicant\fR configures encryption
-keys for unicast and broadcast
-.TP 0.2i
-\(bu
-normal data packets can be transmitted and received
-.SH "SUPPORTED FEATURES"
-.PP
-Supported WPA/IEEE 802.11i features:
-.TP 0.2i
-\(bu
-WPA-PSK ("WPA-Personal")
-.TP 0.2i
-\(bu
-WPA with EAP (e.g., with RADIUS authentication server)
-("WPA-Enterprise") Following authentication methods are
-supported with an integrate IEEE 802.1X Supplicant:
-.RS
-.TP 0.2i
-\(bu
-EAP-TLS
-.RE
-.RS
-.TP 0.2i
-\(bu
-EAP-PEAP/MSCHAPv2 (both PEAPv0 and PEAPv1)
-.TP 0.2i
-\(bu
-EAP-PEAP/TLS (both PEAPv0 and PEAPv1)
-.TP 0.2i
-\(bu
-EAP-PEAP/GTC (both PEAPv0 and PEAPv1)
-.TP 0.2i
-\(bu
-EAP-PEAP/OTP (both PEAPv0 and PEAPv1)
-.TP 0.2i
-\(bu
-EAP-PEAP/MD5-Challenge (both PEAPv0 and PEAPv1)
-.TP 0.2i
-\(bu
-EAP-TTLS/EAP-MD5-Challenge
-.TP 0.2i
-\(bu
-EAP-TTLS/EAP-GTC
-.TP 0.2i
-\(bu
-EAP-TTLS/EAP-OTP
-.TP 0.2i
-\(bu
-EAP-TTLS/EAP-MSCHAPv2
-.TP 0.2i
-\(bu
-EAP-TTLS/EAP-TLS
-.TP 0.2i
-\(bu
-EAP-TTLS/MSCHAPv2
-.TP 0.2i
-\(bu
-EAP-TTLS/MSCHAP
-.TP 0.2i
-\(bu
-EAP-TTLS/PAP
-.TP 0.2i
-\(bu
-EAP-TTLS/CHAP
-.TP 0.2i
-\(bu
-EAP-SIM
-.TP 0.2i
-\(bu
-EAP-AKA
-.TP 0.2i
-\(bu
-EAP-PSK
-.TP 0.2i
-\(bu
-EAP-PAX
-.TP 0.2i
-\(bu
-LEAP (note: requires special support from
-the driver for IEEE 802.11 authentication)
-.TP 0.2i
-\(bu
-(following methods are supported, but since
-they do not generate keying material, they cannot be used
-with WPA or IEEE 802.1X WEP keying)
-.TP 0.2i
-\(bu
-EAP-MD5-Challenge
-.TP 0.2i
-\(bu
-EAP-MSCHAPv2
-.TP 0.2i
-\(bu
-EAP-GTC
-.TP 0.2i
-\(bu
-EAP-OTP
-.RE
-.TP 0.2i
-\(bu
-key management for CCMP, TKIP, WEP104, WEP40
-.TP 0.2i
-\(bu
-RSN/WPA2 (IEEE 802.11i)
-.RS
-.TP 0.2i
-\(bu
-pre-authentication
-.TP 0.2i
-\(bu
-PMKSA caching
-.RE
-.SH "AVAILABLE DRIVERS"
-.PP
-A summary of available driver backends is below. Support for each
-of the driver backends is chosen at wpa_supplicant compile time. For a
-list of supported driver backends that may be used with the -D option on
-your system, refer to the help output of wpa_supplicant
-(\fBwpa_supplicant -h\fR).
-.TP
-\fBhostap\fR
-(default) Host AP driver (Intersil Prism2/2.5/3).
-(this can also be used with Linuxant DriverLoader).
-.TP
-\fBhermes\fR
-Agere Systems Inc. driver (Hermes-I/Hermes-II).
-.TP
-\fBmadwifi\fR
-MADWIFI 802.11 support (Atheros, etc.).
-.TP
-\fBatmel\fR
-ATMEL AT76C5XXx (USB, PCMCIA).
-.TP
-\fBwext\fR
-Linux wireless extensions (generic).
-.TP
-\fBndiswrapper\fR
-Linux ndiswrapper.
-.TP
-\fBbroadcom\fR
-Broadcom wl.o driver.
-.TP
-\fBipw\fR
-Intel ipw2100/2200 driver.
-.TP
-\fBwired\fR
-wpa_supplicant wired Ethernet driver
-.TP
-\fBroboswitch\fR
-wpa_supplicant Broadcom switch driver
-.TP
-\fBbsd\fR
-BSD 802.11 support (Atheros, etc.).
-.TP
-\fBndis\fR
-Windows NDIS driver.
-.SH "COMMAND LINE OPTIONS"
-.PP
-Most command line options have global scope. Some are given per
-interface, and are only valid if at least one \fB-i\fR option
-is specified, otherwise they're ignored. Option groups for different
-interfaces must be separated by \fB-N\fR option.
-.TP
-\fB-b br_ifname\fR
-Optional bridge interface name. (Per interface)
-.TP
-\fB-B\fR
-Run daemon in the background.
-.TP
-\fB-c filename\fR
-Path to configuration file. (Per interface)
-.TP
-\fB-C ctrl_interface\fR
-Path to ctrl_interface socket (Per interface. Only used if
-\fB-c\fR is not).
-.TP
-\fB-i ifname\fR
-Interface to listen on. Multiple instances of this option can
-be present, one per interface, separated by \fB-N\fR
-option (see below).
-.TP
-\fB-d\fR
-Increase debugging verbosity (\fB-dd\fR even
-more).
-.TP
-\fB-D driver\fR
-Driver to use (can be multiple drivers: nl80211,wext).
-(Per interface, see the available options below.)
-.TP
-\fB-f output file\fR
-Log output to specified file instead of stdout.
-.TP
-\fB-g global ctrl_interface\fR
-Path to global ctrl_interface socket. If specified, interface
-definitions may be omitted.
-.TP
-\fB-K\fR
-Include keys (passwords, etc.) in debug output.
-.TP
-\fB-t\fR
-Include timestamp in debug messages.
-.TP
-\fB-h\fR
-Help. Show a usage message.
-.TP
-\fB-L\fR
-Show license (GPL and BSD).
-.TP
-\fB-p\fR
-Driver parameters. (Per interface)
-.TP
-\fB-P PID_file\fR
-Path to PID file.
-.TP
-\fB-q\fR
-Decrease debugging verbosity (\fB-qq\fR even
-less).
-.TP
-\fB-u\fR
-Enabled DBus control interface. If enabled, interface
-definitions may be omitted.
-.TP
-\fB-v\fR
-Show version.
-.TP
-\fB-W\fR
-Wait for a control interface monitor before starting.
-.TP
-\fB-N\fR
-Start describing new interface.
-.SH "EXAMPLES"
-.PP
-In most common cases, \fBwpa_supplicant\fR is
-started with:
-.sp
-.RS
-
-.nf
-wpa_supplicant -B -c/etc/wpa_supplicant.conf -iwlan0
-.fi
-.RE
-.PP
-This makes the process fork into background.
-.PP
-The easiest way to debug problems, and to get debug log for
-bug reports, is to start \fBwpa_supplicant\fR on
-foreground with debugging enabled:
-.sp
-.RS
-
-.nf
-wpa_supplicant -c/etc/wpa_supplicant.conf -iwlan0 -d
-.fi
-.RE
-.PP
-If the specific driver wrapper is not known beforehand, it is
-possible to specify multiple comma separated driver wrappers on the command
-line. \fBwpa_supplicant\fR will use the first driver
-wrapper that is able to initialize the interface.
-.sp
-.RS
-
-.nf
-wpa_supplicant -Dnl80211,wext -c/etc/wpa_supplicant.conf -iwlan0
-.fi
-.RE
-.PP
-\fBwpa_supplicant\fR can control multiple
-interfaces (radios) either by running one process for each
-interface separately or by running just one process and list of
-options at command line. Each interface is separated with -N
-argument. As an example, following command would start
-wpa_supplicant for two interfaces:
-.sp
-.RS
-
-.nf
-wpa_supplicant \\
- -c wpa1.conf -i wlan0 -D hostap -N \\
- -c wpa2.conf -i ath0 -D madwifi
-.fi
-.RE
-.SH "OS REQUIREMENTS"
-.PP
-Current hardware/software requirements:
-.TP 0.2i
-\(bu
-Linux kernel 2.4.x or 2.6.x with Linux Wireless
-Extensions v15 or newer
-.TP 0.2i
-\(bu
-FreeBSD 6-CURRENT
-.TP 0.2i
-\(bu
-Microsoft Windows with WinPcap (at least WinXP, may work
-with other versions)
-.SH "SUPPORTED DRIVERS"
-.TP
-\fBHost AP driver for Prism2/2.5/3 (development snapshot/v0.2.x)\fR
-(http://hostap.epitest.fi/) Driver needs to be set in
-Managed mode (\fBiwconfig wlan0 mode managed\fR).
-Please note that station firmware version needs to be 1.7.0 or
-newer to work in WPA mode.
-.TP
-\fBLinuxant DriverLoader\fR
-(http://www.linuxant.com/driverloader/)
-with Windows NDIS driver for your wlan card supporting WPA.
-.TP
-\fBAgere Systems Inc. Linux Driver\fR
-(http://www.agere.com/support/drivers/) Please note
-that the driver interface file (driver_hermes.c) and hardware
-specific include files are not included in the wpa_supplicant
-distribution. You will need to copy these from the source
-package of the Agere driver.
-.TP
-\fBmadwifi driver for cards based on Atheros chip set (ar521x)\fR
-(http://sourceforge.net/projects/madwifi/) Please
-note that you will need to modify the wpa_supplicant .config
-file to use the correct path for the madwifi driver root
-directory (CFLAGS += -I../madwifi/wpa line in example
-defconfig).
-.TP
-\fBATMEL AT76C5XXx driver for USB and PCMCIA cards\fR
-(http://atmelwlandriver.sourceforge.net/).
-.TP
-\fBLinux ndiswrapper\fR
-(http://ndiswrapper.sourceforge.net/) with Windows
-NDIS driver.
-.TP
-\fBBroadcom wl.o driver\fR
-This is a generic Linux driver for Broadcom IEEE
-802.11a/g cards. However, it is proprietary driver that is
-not publicly available except for couple of exceptions, mainly
-Broadcom-based APs/wireless routers that use Linux. The driver
-binary can be downloaded, e.g., from Linksys support site
-(http://www.linksys.com/support/gpl.asp) for Linksys
-WRT54G. The GPL tarball includes cross-compiler and the needed
-header file, wlioctl.h, for compiling wpa_supplicant. This
-driver support in wpa_supplicant is expected to work also with
-other devices based on Broadcom driver (assuming the driver
-includes client mode support).
-.TP
-\fB Intel ipw2100 driver\fR
-(http://sourceforge.net/projects/ipw2100/)
-.TP
-\fBIntel ipw2200 driver\fR
-(http://sourceforge.net/projects/ipw2200/)
-.TP
-\fBLinux wireless extensions\fR
-In theory, any driver that supports Linux wireless
-extensions can be used with IEEE 802.1X (i.e., not WPA) when
-using ap_scan=0 option in configuration file.
-.TP
-\fBWired Ethernet drivers\fR
-Use ap_scan=0.
-.TP
-\fBBSD net80211 layer (e.g., Atheros driver)\fR
-At the moment, this is for FreeBSD 6-CURRENT branch.
-.TP
-\fBWindows NDIS\fR
-The current Windows port requires WinPcap
-(http://winpcap.polito.it/). See README-Windows.txt for more
-information.
-.PP
-wpa_supplicant was designed to be portable for different
-drivers and operating systems. Hopefully, support for more wlan
-cards and OSes will be added in the future. See developer.txt for
-more information about the design of wpa_supplicant and porting to
-other drivers. One main goal is to add full WPA/WPA2 support to
-Linux wireless extensions to allow new drivers to be supported
-without having to implement new driver-specific interface code in
-wpa_supplicant.
-.SH "ARCHITECTURE"
-.PP
-The
-\fBwpa_supplicant\fR system consists of the following
-components:
-.TP
-\fB\fIwpa_supplicant.conf\fB \fR
-the configuration file describing all networks that the
-user wants the computer to connect to.
-.TP
-\fBwpa_supplicant\fR
-the program that directly interacts with the
-network interface.
-.TP
-\fBwpa_cli\fR
-the
-client program that provides a high-level interface to the
-functionality of the daemon.
-.TP
-\fBwpa_passphrase\fR
-a utility needed to construct
-\fIwpa_supplicant.conf\fR files that include
-encrypted passwords.
-.SH "QUICK START"
-.PP
-First, make a configuration file, e.g.
-\fI/etc/wpa_supplicant.conf\fR, that describes the networks
-you are interested in. See \fBwpa_supplicant.conf\fR(5)
-for details.
-.PP
-Once the configuration is ready, you can test whether the
-configuration works by running \fBwpa_supplicant\fR
-with following command to start it on foreground with debugging
-enabled:
-.sp
-.RS
-
-.nf
-wpa_supplicant -iwlan0 -c/etc/wpa_supplicant.conf -d
-
-.fi
-.RE
-.PP
-Assuming everything goes fine, you can start using following
-command to start \fBwpa_supplicant\fR on background
-without debugging:
-.sp
-.RS
-
-.nf
-wpa_supplicant -iwlan0 -c/etc/wpa_supplicant.conf -B
-
-.fi
-.RE
-.PP
-Please note that if you included more than one driver
-interface in the build time configuration (.config), you may need
-to specify which interface to use by including -D<driver
-name> option on the command line.
-.SH "INTERFACE TO PCMCIA-CS/CARDMRG"
-.PP
-For example, following small changes to pcmcia-cs scripts
-can be used to enable WPA support:
-.PP
-Add MODE="Managed" and WPA="y" to the network scheme in
-\fI/etc/pcmcia/wireless.opts\fR\&.
-.PP
-Add the following block to the end of \fBstart\fR
-action handler in \fI/etc/pcmcia/wireless\fR:
-.sp
-.RS
-
-.nf
-if [ "$WPA" = "y" -a -x /usr/local/bin/wpa_supplicant ]; then
- /usr/local/bin/wpa_supplicant -B -c/etc/wpa_supplicant.conf -i$DEVICE
-fi
-
-.fi
-.RE
-.PP
-Add the following block to the end of \fBstop\fR
-action handler (may need to be separated from other actions) in
-\fI/etc/pcmcia/wireless\fR:
-.sp
-.RS
-
-.nf
-if [ "$WPA" = "y" -a -x /usr/local/bin/wpa_supplicant ]; then
- killall wpa_supplicant
-fi
-
-.fi
-.RE
-.PP
-This will make \fBcardmgr\fR start
-\fBwpa_supplicant\fR when the card is plugged
-in.
-.SH "SEE ALSO"
-.PP
-\fBwpa_background\fR(8)
-\fBwpa_supplicant.conf\fR(5)
-\fBwpa_cli\fR(8)
-\fBwpa_passphrase\fR(8)
-.SH "LEGAL"
-.PP
-wpa_supplicant is copyright (c) 2003-2007,
-Jouni Malinen <j@w1.fi> and
-contributors.
-All Rights Reserved.
-.PP
-This program is dual-licensed under both the GPL version 2
-and BSD license. Either license may be used at your option.
diff --git a/contrib/wpa/wpa_supplicant/doc/docbook/wpa_supplicant.conf.5 b/contrib/wpa/wpa_supplicant/doc/docbook/wpa_supplicant.conf.5
deleted file mode 100644
index 6371965..0000000
--- a/contrib/wpa/wpa_supplicant/doc/docbook/wpa_supplicant.conf.5
+++ /dev/null
@@ -1,225 +0,0 @@
-.\" This manpage has been automatically generated by docbook2man
-.\" from a DocBook document. This tool can be found at:
-.\" <http://shell.ipoline.com/~elmert/comp/docbook2X/>
-.\" Please send any bug reports, improvements, comments, patches,
-.\" etc. to Steve Cheng <steve@ggi-project.org>.
-.TH "WPA_SUPPLICANT.CONF" "5" "07 September 2010" "" ""
-
-.SH NAME
-wpa_supplicant.conf \- configuration file for wpa_supplicant
-.SH "OVERVIEW"
-.PP
-\fBwpa_supplicant\fR is configured using a text
-file that lists all accepted networks and security policies,
-including pre-shared keys. See the example configuration file,
-probably in \fB/usr/share/doc/wpa_supplicant/\fR, for
-detailed information about the configuration format and supported
-fields.
-.PP
-All file paths in this configuration file should use full
-(absolute, not relative to working directory) path in order to allow
-working directory to be changed. This can happen if wpa_supplicant is
-run in the background.
-.PP
-Changes to configuration file can be reloaded be sending
-SIGHUP signal to \fBwpa_supplicant\fR ('killall -HUP
-wpa_supplicant'). Similarly, reloading can be triggered with
-the \fBwpa_cli reconfigure\fR command.
-.PP
-Configuration file can include one or more network blocks,
-e.g., one for each used SSID. wpa_supplicant will automatically
-select the best network based on the order of network blocks in
-the configuration file, network security level (WPA/WPA2 is
-preferred), and signal strength.
-.SH "QUICK EXAMPLES"
-.TP 3
-1.
-WPA-Personal (PSK) as home network and WPA-Enterprise with
-EAP-TLS as work network.
-.sp
-.RS
-
-.nf
-# allow frontend (e.g., wpa_cli) to be used by all users in 'wheel' group
-ctrl_interface=DIR=/var/run/wpa_supplicant GROUP=wheel
-#
-# home network; allow all valid ciphers
-network={
- ssid="home"
- scan_ssid=1
- key_mgmt=WPA-PSK
- psk="very secret passphrase"
-}
-#
-# work network; use EAP-TLS with WPA; allow only CCMP and TKIP ciphers
-network={
- ssid="work"
- scan_ssid=1
- key_mgmt=WPA-EAP
- pairwise=CCMP TKIP
- group=CCMP TKIP
- eap=TLS
- identity="user@example.com"
- ca_cert="/etc/cert/ca.pem"
- client_cert="/etc/cert/user.pem"
- private_key="/etc/cert/user.prv"
- private_key_passwd="password"
-}
-.fi
-.RE
-.TP 3
-2.
-WPA-RADIUS/EAP-PEAP/MSCHAPv2 with RADIUS servers that
-use old peaplabel (e.g., Funk Odyssey and SBR, Meetinghouse
-Aegis, Interlink RAD-Series)
-.sp
-.RS
-
-.nf
-ctrl_interface=DIR=/var/run/wpa_supplicant GROUP=wheel
-network={
- ssid="example"
- scan_ssid=1
- key_mgmt=WPA-EAP
- eap=PEAP
- identity="user@example.com"
- password="foobar"
- ca_cert="/etc/cert/ca.pem"
- phase1="peaplabel=0"
- phase2="auth=MSCHAPV2"
-}
-.fi
-.RE
-.TP 3
-3.
-EAP-TTLS/EAP-MD5-Challenge configuration with anonymous
-identity for the unencrypted use. Real identity is sent only
-within an encrypted TLS tunnel.
-.sp
-.RS
-
-.nf
-ctrl_interface=DIR=/var/run/wpa_supplicant GROUP=wheel
-network={
- ssid="example"
- scan_ssid=1
- key_mgmt=WPA-EAP
- eap=TTLS
- identity="user@example.com"
- anonymous_identity="anonymous@example.com"
- password="foobar"
- ca_cert="/etc/cert/ca.pem"
- phase2="auth=MD5"
-}
-.fi
-.RE
-.TP 3
-4.
-IEEE 802.1X (i.e., no WPA) with dynamic WEP keys
-(require both unicast and broadcast); use EAP-TLS for
-authentication
-.sp
-.RS
-
-.nf
-ctrl_interface=DIR=/var/run/wpa_supplicant GROUP=wheel
-network={
- ssid="1x-test"
- scan_ssid=1
- key_mgmt=IEEE8021X
- eap=TLS
- identity="user@example.com"
- ca_cert="/etc/cert/ca.pem"
- client_cert="/etc/cert/user.pem"
- private_key="/etc/cert/user.prv"
- private_key_passwd="password"
- eapol_flags=3
-}
-.fi
-.RE
-.TP 3
-5.
-Catch all example that allows more or less all
-configuration modes. The configuration options are used based
-on what security policy is used in the selected SSID. This is
-mostly for testing and is not recommended for normal
-use.
-.sp
-.RS
-
-.nf
-ctrl_interface=DIR=/var/run/wpa_supplicant GROUP=wheel
-network={
- ssid="example"
- scan_ssid=1
- key_mgmt=WPA-EAP WPA-PSK IEEE8021X NONE
- pairwise=CCMP TKIP
- group=CCMP TKIP WEP104 WEP40
- psk="very secret passphrase"
- eap=TTLS PEAP TLS
- identity="user@example.com"
- password="foobar"
- ca_cert="/etc/cert/ca.pem"
- client_cert="/etc/cert/user.pem"
- private_key="/etc/cert/user.prv"
- private_key_passwd="password"
- phase1="peaplabel=0"
- ca_cert2="/etc/cert/ca2.pem"
- client_cert2="/etc/cer/user.pem"
- private_key2="/etc/cer/user.prv"
- private_key2_passwd="password"
-}
-.fi
-.RE
-.TP 3
-6.
-Authentication for wired Ethernet. This can be used with
-\fBwired\fR or \fBroboswitch\fR interface
-(-Dwired or -Droboswitch on command line).
-.sp
-.RS
-
-.nf
-ctrl_interface=DIR=/var/run/wpa_supplicant GROUP=wheel
-ap_scan=0
-network={
- key_mgmt=IEEE8021X
- eap=MD5
- identity="user"
- password="password"
- eapol_flags=0
-}
-.fi
-.RE
-.SH "CERTIFICATES"
-.PP
-Some EAP authentication methods require use of
-certificates. EAP-TLS uses both server side and client
-certificates whereas EAP-PEAP and EAP-TTLS only require the server
-side certificate. When client certificate is used, a matching
-private key file has to also be included in configuration. If the
-private key uses a passphrase, this has to be configured in
-wpa_supplicant.conf ("private_key_passwd").
-.PP
-wpa_supplicant supports X.509 certificates in PEM and DER
-formats. User certificate and private key can be included in the
-same file.
-.PP
-If the user certificate and private key is received in
-PKCS#12/PFX format, they need to be converted to suitable PEM/DER
-format for wpa_supplicant. This can be done, e.g., with following
-commands:
-.sp
-.RS
-
-.nf
-# convert client certificate and private key to PEM format
-openssl pkcs12 -in example.pfx -out user.pem -clcerts
-# convert CA certificate (if included in PFX file) to PEM format
-openssl pkcs12 -in example.pfx -out ca.pem -cacerts -nokeys
-.fi
-.RE
-.SH "SEE ALSO"
-.PP
-\fBwpa_supplicant\fR(8)
-\fBopenssl\fR(1)
diff --git a/contrib/wpa/wpa_supplicant/doc/docbook/wpa_supplicant.conf.sgml b/contrib/wpa/wpa_supplicant/doc/docbook/wpa_supplicant.conf.sgml
deleted file mode 100644
index 462039d..0000000
--- a/contrib/wpa/wpa_supplicant/doc/docbook/wpa_supplicant.conf.sgml
+++ /dev/null
@@ -1,239 +0,0 @@
-<!doctype refentry PUBLIC "-//OASIS//DTD DocBook V4.1//EN">
-<refentry>
- <refmeta>
- <refentrytitle>wpa_supplicant.conf</refentrytitle>
- <manvolnum>5</manvolnum>
- </refmeta>
- <refnamediv>
- <refname>wpa_supplicant.conf</refname>
- <refpurpose>configuration file for wpa_supplicant</refpurpose>
- </refnamediv>
- <refsect1>
- <title>Overview</title>
-
- <para><command>wpa_supplicant</command> is configured using a text
- file that lists all accepted networks and security policies,
- including pre-shared keys. See the example configuration file,
- probably in <command>/usr/share/doc/wpa_supplicant/</command>, for
- detailed information about the configuration format and supported
- fields.</para>
-
- <para>All file paths in this configuration file should use full
- (absolute, not relative to working directory) path in order to allow
- working directory to be changed. This can happen if wpa_supplicant is
- run in the background.</para>
-
- <para>Changes to configuration file can be reloaded be sending
- SIGHUP signal to <command>wpa_supplicant</command> ('killall -HUP
- wpa_supplicant'). Similarly, reloading can be triggered with
- the <emphasis>wpa_cli reconfigure</emphasis> command.</para>
-
- <para>Configuration file can include one or more network blocks,
- e.g., one for each used SSID. wpa_supplicant will automatically
- select the best network based on the order of network blocks in
- the configuration file, network security level (WPA/WPA2 is
- preferred), and signal strength.</para>
- </refsect1>
-
- <refsect1>
- <title>Quick Examples</title>
-
- <orderedlist>
- <listitem>
-
- <para>WPA-Personal (PSK) as home network and WPA-Enterprise with
- EAP-TLS as work network.</para>
-
-<blockquote><programlisting>
-# allow frontend (e.g., wpa_cli) to be used by all users in 'wheel' group
-ctrl_interface=DIR=/var/run/wpa_supplicant GROUP=wheel
-#
-# home network; allow all valid ciphers
-network={
- ssid="home"
- scan_ssid=1
- key_mgmt=WPA-PSK
- psk="very secret passphrase"
-}
-#
-# work network; use EAP-TLS with WPA; allow only CCMP and TKIP ciphers
-network={
- ssid="work"
- scan_ssid=1
- key_mgmt=WPA-EAP
- pairwise=CCMP TKIP
- group=CCMP TKIP
- eap=TLS
- identity="user@example.com"
- ca_cert="/etc/cert/ca.pem"
- client_cert="/etc/cert/user.pem"
- private_key="/etc/cert/user.prv"
- private_key_passwd="password"
-}
-</programlisting></blockquote>
- </listitem>
-
- <listitem>
- <para>WPA-RADIUS/EAP-PEAP/MSCHAPv2 with RADIUS servers that
- use old peaplabel (e.g., Funk Odyssey and SBR, Meetinghouse
- Aegis, Interlink RAD-Series)</para>
-
-<blockquote><programlisting>
-ctrl_interface=DIR=/var/run/wpa_supplicant GROUP=wheel
-network={
- ssid="example"
- scan_ssid=1
- key_mgmt=WPA-EAP
- eap=PEAP
- identity="user@example.com"
- password="foobar"
- ca_cert="/etc/cert/ca.pem"
- phase1="peaplabel=0"
- phase2="auth=MSCHAPV2"
-}
-</programlisting></blockquote>
- </listitem>
-
- <listitem>
- <para>EAP-TTLS/EAP-MD5-Challenge configuration with anonymous
- identity for the unencrypted use. Real identity is sent only
- within an encrypted TLS tunnel.</para>
-
-
-<blockquote><programlisting>
-ctrl_interface=DIR=/var/run/wpa_supplicant GROUP=wheel
-network={
- ssid="example"
- scan_ssid=1
- key_mgmt=WPA-EAP
- eap=TTLS
- identity="user@example.com"
- anonymous_identity="anonymous@example.com"
- password="foobar"
- ca_cert="/etc/cert/ca.pem"
- phase2="auth=MD5"
-}
-</programlisting></blockquote>
-
- </listitem>
-
- <listitem>
- <para>IEEE 802.1X (i.e., no WPA) with dynamic WEP keys
- (require both unicast and broadcast); use EAP-TLS for
- authentication</para>
-
-<blockquote><programlisting>
-ctrl_interface=DIR=/var/run/wpa_supplicant GROUP=wheel
-network={
- ssid="1x-test"
- scan_ssid=1
- key_mgmt=IEEE8021X
- eap=TLS
- identity="user@example.com"
- ca_cert="/etc/cert/ca.pem"
- client_cert="/etc/cert/user.pem"
- private_key="/etc/cert/user.prv"
- private_key_passwd="password"
- eapol_flags=3
-}
-</programlisting></blockquote>
- </listitem>
-
-
- <listitem>
- <para>Catch all example that allows more or less all
- configuration modes. The configuration options are used based
- on what security policy is used in the selected SSID. This is
- mostly for testing and is not recommended for normal
- use.</para>
-
-<blockquote><programlisting>
-ctrl_interface=DIR=/var/run/wpa_supplicant GROUP=wheel
-network={
- ssid="example"
- scan_ssid=1
- key_mgmt=WPA-EAP WPA-PSK IEEE8021X NONE
- pairwise=CCMP TKIP
- group=CCMP TKIP WEP104 WEP40
- psk="very secret passphrase"
- eap=TTLS PEAP TLS
- identity="user@example.com"
- password="foobar"
- ca_cert="/etc/cert/ca.pem"
- client_cert="/etc/cert/user.pem"
- private_key="/etc/cert/user.prv"
- private_key_passwd="password"
- phase1="peaplabel=0"
- ca_cert2="/etc/cert/ca2.pem"
- client_cert2="/etc/cer/user.pem"
- private_key2="/etc/cer/user.prv"
- private_key2_passwd="password"
-}
-</programlisting></blockquote>
- </listitem>
-
- <listitem>
- <para>Authentication for wired Ethernet. This can be used with
- <emphasis>wired</emphasis> or <emphasis>roboswitch</emphasis> interface
- (-Dwired or -Droboswitch on command line).</para>
-
-<blockquote><programlisting>
-ctrl_interface=DIR=/var/run/wpa_supplicant GROUP=wheel
-ap_scan=0
-network={
- key_mgmt=IEEE8021X
- eap=MD5
- identity="user"
- password="password"
- eapol_flags=0
-}
-</programlisting></blockquote>
- </listitem>
- </orderedlist>
-
-
-
-
-
- </refsect1>
- <refsect1>
- <title>Certificates</title>
-
- <para>Some EAP authentication methods require use of
- certificates. EAP-TLS uses both server side and client
- certificates whereas EAP-PEAP and EAP-TTLS only require the server
- side certificate. When client certificate is used, a matching
- private key file has to also be included in configuration. If the
- private key uses a passphrase, this has to be configured in
- wpa_supplicant.conf ("private_key_passwd").</para>
-
- <para>wpa_supplicant supports X.509 certificates in PEM and DER
- formats. User certificate and private key can be included in the
- same file.</para>
-
- <para>If the user certificate and private key is received in
- PKCS#12/PFX format, they need to be converted to suitable PEM/DER
- format for wpa_supplicant. This can be done, e.g., with following
- commands:</para>
-<blockquote><programlisting>
-# convert client certificate and private key to PEM format
-openssl pkcs12 -in example.pfx -out user.pem -clcerts
-# convert CA certificate (if included in PFX file) to PEM format
-openssl pkcs12 -in example.pfx -out ca.pem -cacerts -nokeys
-</programlisting></blockquote>
- </refsect1>
-
- <refsect1>
- <title>See Also</title>
- <para>
- <citerefentry>
- <refentrytitle>wpa_supplicant</refentrytitle>
- <manvolnum>8</manvolnum>
- </citerefentry>
- <citerefentry>
- <refentrytitle>openssl</refentrytitle>
- <manvolnum>1</manvolnum>
- </citerefentry>
- </para>
- </refsect1>
-</refentry>
diff --git a/contrib/wpa/wpa_supplicant/doc/docbook/wpa_supplicant.sgml b/contrib/wpa/wpa_supplicant/doc/docbook/wpa_supplicant.sgml
deleted file mode 100644
index 3aae51b..0000000
--- a/contrib/wpa/wpa_supplicant/doc/docbook/wpa_supplicant.sgml
+++ /dev/null
@@ -1,827 +0,0 @@
-<!doctype refentry PUBLIC "-//OASIS//DTD DocBook V4.1//EN">
-
-<refentry>
- <refmeta>
- <refentrytitle>wpa_supplicant</refentrytitle>
- <manvolnum>8</manvolnum>
- </refmeta>
- <refnamediv>
- <refname>wpa_supplicant</refname>
- <refpurpose>Wi-Fi Protected Access client and IEEE 802.1X supplicant</refpurpose>
- </refnamediv>
- <refsynopsisdiv>
- <cmdsynopsis>
- <command>wpa_supplicant</command>
- <arg>-BddfhKLqqtuvW</arg>
- <arg>-i<replaceable>ifname</replaceable></arg>
- <arg>-c<replaceable>config file</replaceable></arg>
- <arg>-D<replaceable>driver</replaceable></arg>
- <arg>-P<replaceable>PID_file</replaceable></arg>
- <arg>-f<replaceable>output file</replaceable></arg>
- </cmdsynopsis>
- </refsynopsisdiv>
- <refsect1>
- <title>Overview</title>
-
- <para>
- Wireless networks do not require physical access to the network equipment
- in the same way as wired networks. This makes it easier for unauthorized
- users to passively monitor a network and capture all transmitted frames.
- In addition, unauthorized use of the network is much easier. In many cases,
- this can happen even without user's explicit knowledge since the wireless
- LAN adapter may have been configured to automatically join any available
- network.
- </para>
-
- <para>
- Link-layer encryption can be used to provide a layer of security for
- wireless networks. The original wireless LAN standard, IEEE 802.11,
- included a simple encryption mechanism, WEP. However, that proved to
- be flawed in many areas and network protected with WEP cannot be consider
- secure. IEEE 802.1X authentication and frequently changed dynamic WEP keys
- can be used to improve the network security, but even that has inherited
- security issues due to the use of WEP for encryption. Wi-Fi Protected
- Access and IEEE 802.11i amendment to the wireless LAN standard introduce
- a much improvement mechanism for securing wireless networks. IEEE 802.11i
- enabled networks that are using CCMP (encryption mechanism based on strong
- cryptographic algorithm AES) can finally be called secure used for
- applications which require efficient protection against unauthorized
- access.
- </para>
-
- <para><command>wpa_supplicant</command> is an implementation of
- the WPA Supplicant component, i.e., the part that runs in the
- client stations. It implements WPA key negotiation with a WPA
- Authenticator and EAP authentication with Authentication
- Server. In addition, it controls the roaming and IEEE 802.11
- authentication/association of the wireless LAN driver.</para>
-
- <para><command>wpa_supplicant</command> is designed to be a
- "daemon" program that runs in the background and acts as the
- backend component controlling the wireless
- connection. <command>wpa_supplicant</command> supports separate
- frontend programs and an example text-based frontend,
- <command>wpa_cli</command>, is included with
- wpa_supplicant.</para>
-
- <para>Before wpa_supplicant can do its work, the network interface
- must be available. That means that the physical device must be
- present and enabled, and the driver for the device must be
- loaded. The daemon will exit immediately if the device is not already
- available.</para>
-
- <para>After <command>wpa_supplicant</command> has configured the
- network device, higher level configuration such as DHCP may
- proceed. There are a variety of ways to integrate wpa_supplicant
- into a machine's networking scripts, a few of which are described
- in sections below.</para>
-
- <para>The following steps are used when associating with an AP
- using WPA:</para>
-
- <itemizedlist>
- <listitem>
- <para><command>wpa_supplicant</command> requests the kernel
- driver to scan neighboring BSSes</para>
- </listitem>
-
- <listitem>
- <para><command>wpa_supplicant</command> selects a BSS based on
- its configuration</para>
- </listitem>
-
- <listitem>
- <para><command>wpa_supplicant</command> requests the kernel
- driver to associate with the chosen BSS</para>
- </listitem>
-
- <listitem>
- <para>If WPA-EAP: integrated IEEE 802.1X Supplicant
- completes EAP authentication with the
- authentication server (proxied by the Authenticator in the
- AP)</para>
- </listitem>
-
- <listitem>
- <para>If WPA-EAP: master key is received from the IEEE 802.1X
- Supplicant</para>
- </listitem>
-
- <listitem>
- <para>If WPA-PSK: <command>wpa_supplicant</command> uses PSK
- as the master session key</para>
- </listitem>
-
- <listitem>
- <para><command>wpa_supplicant</command> completes WPA 4-Way
- Handshake and Group Key Handshake with the Authenticator
- (AP)</para>
- </listitem>
-
- <listitem>
- <para><command>wpa_supplicant</command> configures encryption
- keys for unicast and broadcast</para>
- </listitem>
-
- <listitem>
- <para>normal data packets can be transmitted and received</para>
- </listitem>
- </itemizedlist>
- </refsect1>
-
- <refsect1>
- <title>Supported Features</title>
- <para>Supported WPA/IEEE 802.11i features:</para>
- <itemizedlist>
- <listitem>
- <para>WPA-PSK ("WPA-Personal")</para>
- </listitem>
-
- <listitem>
- <para>WPA with EAP (e.g., with RADIUS authentication server)
- ("WPA-Enterprise") Following authentication methods are
- supported with an integrate IEEE 802.1X Supplicant:</para>
-
- <itemizedlist>
- <listitem>
- <para>EAP-TLS</para>
- </listitem>
- </itemizedlist>
-
- <itemizedlist>
- <listitem>
- <para>EAP-PEAP/MSCHAPv2 (both PEAPv0 and PEAPv1)</para>
- </listitem>
-
-
- <listitem>
- <para>EAP-PEAP/TLS (both PEAPv0 and PEAPv1)</para>
- </listitem>
-
- <listitem>
- <para>EAP-PEAP/GTC (both PEAPv0 and PEAPv1)</para>
- </listitem>
-
- <listitem>
- <para>EAP-PEAP/OTP (both PEAPv0 and PEAPv1)</para>
- </listitem>
-
- <listitem>
- <para>EAP-PEAP/MD5-Challenge (both PEAPv0 and PEAPv1)</para>
- </listitem>
-
- <listitem>
- <para>EAP-TTLS/EAP-MD5-Challenge</para>
- </listitem>
-
- <listitem>
- <para>EAP-TTLS/EAP-GTC</para>
- </listitem>
-
- <listitem><para>EAP-TTLS/EAP-OTP</para></listitem>
-
- <listitem><para>EAP-TTLS/EAP-MSCHAPv2</para></listitem>
-
- <listitem><para>EAP-TTLS/EAP-TLS</para></listitem>
-
- <listitem><para>EAP-TTLS/MSCHAPv2</para></listitem>
-
- <listitem><para>EAP-TTLS/MSCHAP</para></listitem>
-
- <listitem><para>EAP-TTLS/PAP</para></listitem>
-
- <listitem><para>EAP-TTLS/CHAP</para></listitem>
-
- <listitem><para>EAP-SIM</para></listitem>
-
- <listitem><para>EAP-AKA</para></listitem>
-
- <listitem><para>EAP-PSK</para></listitem>
-
- <listitem><para>EAP-PAX</para></listitem>
-
- <listitem><para>LEAP (note: requires special support from
- the driver for IEEE 802.11 authentication)</para></listitem>
-
- <listitem><para>(following methods are supported, but since
- they do not generate keying material, they cannot be used
- with WPA or IEEE 802.1X WEP keying)</para></listitem>
-
- <listitem><para>EAP-MD5-Challenge </para></listitem>
-
- <listitem><para>EAP-MSCHAPv2</para></listitem>
-
- <listitem><para>EAP-GTC</para></listitem>
-
- <listitem><para>EAP-OTP</para></listitem>
- </itemizedlist>
- </listitem>
-
- <listitem>
- <para>key management for CCMP, TKIP, WEP104, WEP40</para>
- </listitem>
-
- <listitem>
- <para>RSN/WPA2 (IEEE 802.11i)</para>
- <itemizedlist>
- <listitem>
- <para>pre-authentication</para>
- </listitem>
-
- <listitem>
- <para>PMKSA caching</para>
- </listitem>
- </itemizedlist>
- </listitem>
- </itemizedlist>
- </refsect1>
-
- <refsect1>
- <title>Available Drivers</title>
- <para>A summary of available driver backends is below. Support for each
- of the driver backends is chosen at wpa_supplicant compile time. For a
- list of supported driver backends that may be used with the -D option on
- your system, refer to the help output of wpa_supplicant
- (<emphasis>wpa_supplicant -h</emphasis>).</para>
-
- <variablelist>
- <varlistentry>
- <term>hostap</term>
- <listitem>
- <para>(default) Host AP driver (Intersil Prism2/2.5/3).
- (this can also be used with Linuxant DriverLoader).</para>
- </listitem>
- </varlistentry>
-
- <varlistentry>
- <term>hermes</term>
- <listitem>
- <para>Agere Systems Inc. driver (Hermes-I/Hermes-II).</para>
- </listitem>
- </varlistentry>
-
- <varlistentry>
- <term>madwifi</term>
- <listitem>
- <para>MADWIFI 802.11 support (Atheros, etc.).</para>
- </listitem>
- </varlistentry>
-
- <varlistentry>
- <term>atmel</term>
- <listitem>
- <para>ATMEL AT76C5XXx (USB, PCMCIA).</para>
- </listitem>
- </varlistentry>
-
- <varlistentry>
- <term>wext</term>
- <listitem>
- <para>Linux wireless extensions (generic).</para>
- </listitem>
- </varlistentry>
-
- <varlistentry>
- <term>ndiswrapper</term>
- <listitem>
- <para>Linux ndiswrapper.</para>
- </listitem>
- </varlistentry>
-
- <varlistentry>
- <term>broadcom</term>
- <listitem>
- <para>Broadcom wl.o driver.</para>
- </listitem>
- </varlistentry>
-
- <varlistentry>
- <term>ipw</term>
- <listitem>
- <para>Intel ipw2100/2200 driver.</para>
- </listitem>
- </varlistentry>
-
- <varlistentry>
- <term>wired</term>
- <listitem>
- <para>wpa_supplicant wired Ethernet driver</para>
- </listitem>
- </varlistentry>
-
- <varlistentry>
- <term>roboswitch</term>
- <listitem>
- <para>wpa_supplicant Broadcom switch driver</para>
- </listitem>
- </varlistentry>
-
- <varlistentry>
- <term>bsd</term>
- <listitem>
- <para>BSD 802.11 support (Atheros, etc.).</para>
- </listitem>
- </varlistentry>
-
- <varlistentry>
- <term>ndis</term>
- <listitem>
- <para>Windows NDIS driver.</para>
- </listitem>
- </varlistentry>
- </variablelist>
- </refsect1>
-
- <refsect1>
- <title>Command Line Options</title>
- <para>Most command line options have global scope. Some are given per
- interface, and are only valid if at least one <option>-i</option> option
- is specified, otherwise they're ignored. Option groups for different
- interfaces must be separated by <option>-N</option> option.</para>
- <variablelist>
- <varlistentry>
- <term>-b br_ifname</term>
- <listitem>
- <para>Optional bridge interface name. (Per interface)</para>
- </listitem>
- </varlistentry>
-
- <varlistentry>
- <term>-B</term>
- <listitem>
- <para>Run daemon in the background.</para>
- </listitem>
- </varlistentry>
-
- <varlistentry>
- <term>-c filename</term>
- <listitem>
- <para>Path to configuration file. (Per interface)</para>
- </listitem>
- </varlistentry>
-
- <varlistentry>
- <term>-C ctrl_interface</term>
- <listitem>
- <para>Path to ctrl_interface socket (Per interface. Only used if
- <option>-c</option> is not).</para>
- </listitem>
- </varlistentry>
-
- <varlistentry>
- <term>-i ifname</term>
- <listitem>
- <para>Interface to listen on. Multiple instances of this option can
- be present, one per interface, separated by <option>-N</option>
- option (see below).</para>
- </listitem>
- </varlistentry>
-
- <varlistentry>
- <term>-d</term>
- <listitem>
- <para>Increase debugging verbosity (<option>-dd</option> even
- more).</para>
- </listitem>
- </varlistentry>
-
- <varlistentry>
- <term>-D driver</term>
- <listitem>
- <para>Driver to use (can be multiple drivers: nl80211,wext).
- (Per interface, see the available options below.)</para>
- </listitem>
- </varlistentry>
-
- <varlistentry>
- <term>-f output file</term>
- <listitem>
- <para>Log output to specified file instead of stdout.</para>
- </listitem>
- </varlistentry>
-
- <varlistentry>
- <term>-g global ctrl_interface</term>
- <listitem>
- <para>Path to global ctrl_interface socket. If specified, interface
- definitions may be omitted.</para>
- </listitem>
- </varlistentry>
-
- <varlistentry>
- <term>-K</term>
- <listitem>
- <para>Include keys (passwords, etc.) in debug output.</para>
- </listitem>
- </varlistentry>
-
- <varlistentry>
- <term>-t</term>
- <listitem>
- <para>Include timestamp in debug messages.</para>
- </listitem>
- </varlistentry>
-
- <varlistentry>
- <term>-h</term>
- <listitem>
- <para>Help. Show a usage message.</para>
- </listitem>
- </varlistentry>
-
- <varlistentry>
- <term>-L</term>
- <listitem>
- <para>Show license (GPL and BSD).</para>
- </listitem>
- </varlistentry>
-
- <varlistentry>
- <term>-p</term>
- <listitem>
- <para>Driver parameters. (Per interface)</para>
- </listitem>
- </varlistentry>
-
- <varlistentry>
- <term>-P PID_file</term>
- <listitem>
- <para>Path to PID file.</para>
- </listitem>
- </varlistentry>
-
- <varlistentry>
- <term>-q</term>
- <listitem>
- <para>Decrease debugging verbosity (<option>-qq</option> even
- less).</para>
- </listitem>
- </varlistentry>
-
- <varlistentry>
- <term>-u</term>
- <listitem>
- <para>Enabled DBus control interface. If enabled, interface
- definitions may be omitted.</para>
- </listitem>
- </varlistentry>
-
- <varlistentry>
- <term>-v</term>
- <listitem>
- <para>Show version.</para>
- </listitem>
- </varlistentry>
-
- <varlistentry>
- <term>-W</term>
- <listitem>
- <para>Wait for a control interface monitor before starting.</para>
- </listitem>
- </varlistentry>
-
- <varlistentry>
- <term>-N</term>
- <listitem>
- <para>Start describing new interface.</para>
- </listitem>
- </varlistentry>
- </variablelist>
- </refsect1>
-
- <refsect1>
- <title>Examples</title>
-
- <para>In most common cases, <command>wpa_supplicant</command> is
- started with:</para>
-
-<blockquote><programlisting>
-wpa_supplicant -B -c/etc/wpa_supplicant.conf -iwlan0
-</programlisting></blockquote>
-
- <para>This makes the process fork into background.</para>
-
- <para>The easiest way to debug problems, and to get debug log for
- bug reports, is to start <command>wpa_supplicant</command> on
- foreground with debugging enabled:</para>
-
-<blockquote><programlisting>
-wpa_supplicant -c/etc/wpa_supplicant.conf -iwlan0 -d
-</programlisting></blockquote>
-
- <para>If the specific driver wrapper is not known beforehand, it is
- possible to specify multiple comma separated driver wrappers on the command
- line. <command>wpa_supplicant</command> will use the first driver
- wrapper that is able to initialize the interface.</para>
-
-<blockquote><programlisting>
-wpa_supplicant -Dnl80211,wext -c/etc/wpa_supplicant.conf -iwlan0
-</programlisting></blockquote>
-
- <para><command>wpa_supplicant</command> can control multiple
- interfaces (radios) either by running one process for each
- interface separately or by running just one process and list of
- options at command line. Each interface is separated with -N
- argument. As an example, following command would start
- wpa_supplicant for two interfaces:</para>
-
-<blockquote><programlisting>
-wpa_supplicant \
- -c wpa1.conf -i wlan0 -D hostap -N \
- -c wpa2.conf -i ath0 -D madwifi
-</programlisting></blockquote>
- </refsect1>
-
- <refsect1>
- <title>OS Requirements</title>
- <para>Current hardware/software requirements:</para>
-
- <itemizedlist>
- <listitem>
- <para>Linux kernel 2.4.x or 2.6.x with Linux Wireless
- Extensions v15 or newer</para>
- </listitem>
-
-
- <listitem>
- <para>FreeBSD 6-CURRENT</para>
- </listitem>
-
- <listitem>
- <para>Microsoft Windows with WinPcap (at least WinXP, may work
- with other versions)</para>
- </listitem>
- </itemizedlist>
- </refsect1>
-
- <refsect1>
- <title>Supported Drivers</title>
- <variablelist>
- <varlistentry>
- <term>Host AP driver for Prism2/2.5/3 (development
- snapshot/v0.2.x)</term>
- <listitem>
- <para> (http://hostap.epitest.fi/) Driver needs to be set in
- Managed mode (<emphasis>iwconfig wlan0 mode managed</emphasis>).
- Please note that station firmware version needs to be 1.7.0 or
- newer to work in WPA mode.</para>
- </listitem>
- </varlistentry>
-
- <varlistentry>
- <term>Linuxant DriverLoader</term>
- <listitem>
- <para>(http://www.linuxant.com/driverloader/)
- with Windows NDIS driver for your wlan card supporting WPA.</para>
- </listitem>
- </varlistentry>
-
- <varlistentry>
- <term>Agere Systems Inc. Linux Driver</term>
- <listitem>
- <para> (http://www.agere.com/support/drivers/) Please note
- that the driver interface file (driver_hermes.c) and hardware
- specific include files are not included in the wpa_supplicant
- distribution. You will need to copy these from the source
- package of the Agere driver.</para>
- </listitem>
- </varlistentry>
-
- <varlistentry>
- <term>madwifi driver for cards based on Atheros chip set (ar521x)</term>
- <listitem>
- <para> (http://sourceforge.net/projects/madwifi/) Please
- note that you will need to modify the wpa_supplicant .config
- file to use the correct path for the madwifi driver root
- directory (CFLAGS += -I../madwifi/wpa line in example
- defconfig).</para>
- </listitem>
- </varlistentry>
-
- <varlistentry>
- <term>ATMEL AT76C5XXx driver for USB and PCMCIA cards</term>
- <listitem>
- <para> (http://atmelwlandriver.sourceforge.net/).</para>
- </listitem>
- </varlistentry>
-
- <varlistentry>
- <term>Linux ndiswrapper</term>
- <listitem>
- <para> (http://ndiswrapper.sourceforge.net/) with Windows
- NDIS driver.</para>
- </listitem>
- </varlistentry>
-
- <varlistentry>
- <term>Broadcom wl.o driver</term>
- <listitem>
- <para> This is a generic Linux driver for Broadcom IEEE
- 802.11a/g cards. However, it is proprietary driver that is
- not publicly available except for couple of exceptions, mainly
- Broadcom-based APs/wireless routers that use Linux. The driver
- binary can be downloaded, e.g., from Linksys support site
- (http://www.linksys.com/support/gpl.asp) for Linksys
- WRT54G. The GPL tarball includes cross-compiler and the needed
- header file, wlioctl.h, for compiling wpa_supplicant. This
- driver support in wpa_supplicant is expected to work also with
- other devices based on Broadcom driver (assuming the driver
- includes client mode support).</para>
- </listitem>
- </varlistentry>
-
- <varlistentry>
- <term> Intel ipw2100 driver</term>
- <listitem>
- <para> (http://sourceforge.net/projects/ipw2100/)</para>
- </listitem>
- </varlistentry>
-
- <varlistentry>
- <term>Intel ipw2200 driver</term>
- <listitem>
- <para> (http://sourceforge.net/projects/ipw2200/)</para>
- </listitem>
- </varlistentry>
-
- <varlistentry>
- <term>Linux wireless extensions</term>
- <listitem>
- <para>In theory, any driver that supports Linux wireless
- extensions can be used with IEEE 802.1X (i.e., not WPA) when
- using ap_scan=0 option in configuration file.</para>
- </listitem>
- </varlistentry>
-
- <varlistentry>
- <term>Wired Ethernet drivers</term>
- <listitem>
- <para>Use ap_scan=0.</para>
- </listitem>
- </varlistentry>
-
- <varlistentry>
- <term>BSD net80211 layer (e.g., Atheros driver)</term>
- <listitem>
- <para>At the moment, this is for FreeBSD 6-CURRENT branch.</para>
- </listitem>
- </varlistentry>
-
- <varlistentry>
- <term>Windows NDIS</term>
- <listitem>
- <para>The current Windows port requires WinPcap
- (http://winpcap.polito.it/). See README-Windows.txt for more
- information.</para>
- </listitem>
- </varlistentry>
- </variablelist>
-
-
- <para>wpa_supplicant was designed to be portable for different
- drivers and operating systems. Hopefully, support for more wlan
- cards and OSes will be added in the future. See developer.txt for
- more information about the design of wpa_supplicant and porting to
- other drivers. One main goal is to add full WPA/WPA2 support to
- Linux wireless extensions to allow new drivers to be supported
- without having to implement new driver-specific interface code in
- wpa_supplicant.</para>
- </refsect1>
-
- <refsect1>
- <title>Architecture</title> <para>The
- <command>wpa_supplicant</command> system consists of the following
- components:</para>
-
- <variablelist>
- <varlistentry>
- <term><filename>wpa_supplicant.conf</filename> </term>
- <listitem>
- <para>the configuration file describing all networks that the
- user wants the computer to connect to. </para>
- </listitem>
- </varlistentry>
- <varlistentry>
- <term><command>wpa_supplicant</command></term>
- <listitem><para>the program that directly interacts with the
- network interface. </para></listitem>
- </varlistentry>
- <varlistentry>
- <term><command>wpa_cli</command></term> <listitem><para> the
- client program that provides a high-level interface to the
- functionality of the daemon. </para></listitem>
- </varlistentry>
- <varlistentry>
- <term><command>wpa_passphrase</command></term>
- <listitem><para>a utility needed to construct
- <filename>wpa_supplicant.conf</filename> files that include
- encrypted passwords.</para></listitem>
- </varlistentry>
- </variablelist>
- </refsect1>
-
- <refsect1>
- <title>Quick Start</title>
-
- <para>First, make a configuration file, e.g.
- <filename>/etc/wpa_supplicant.conf</filename>, that describes the networks
- you are interested in. See <citerefentry>
- <refentrytitle>wpa_supplicant.conf</refentrytitle>
- <manvolnum>5</manvolnum>
- </citerefentry>
- for details.</para>
-
- <para>Once the configuration is ready, you can test whether the
- configuration works by running <command>wpa_supplicant</command>
- with following command to start it on foreground with debugging
- enabled:</para>
-
- <blockquote><programlisting>
-wpa_supplicant -iwlan0 -c/etc/wpa_supplicant.conf -d
- </programlisting></blockquote>
-
- <para>Assuming everything goes fine, you can start using following
- command to start <command>wpa_supplicant</command> on background
- without debugging:</para>
-
- <blockquote><programlisting>
-wpa_supplicant -iwlan0 -c/etc/wpa_supplicant.conf -B
- </programlisting></blockquote>
-
- <para>Please note that if you included more than one driver
- interface in the build time configuration (.config), you may need
- to specify which interface to use by including -D&lt;driver
- name&gt; option on the command line.</para>
-
- <!-- XXX at this point, the page could include a little script
- based on wpa_cli to wait for a connection and then run
- dhclient -->
-
- </refsect1>
-
- <refsect1>
- <title>Interface to pcmcia-cs/cardmrg</title>
-
- <para>For example, following small changes to pcmcia-cs scripts
- can be used to enable WPA support:</para>
-
- <para>Add MODE="Managed" and WPA="y" to the network scheme in
- <filename>/etc/pcmcia/wireless.opts</filename>.</para>
-
- <para>Add the following block to the end of <emphasis>start</emphasis>
- action handler in <filename>/etc/pcmcia/wireless</filename>:</para>
-
- <blockquote><programlisting>
-if [ "$WPA" = "y" -a -x /usr/local/bin/wpa_supplicant ]; then
- /usr/local/bin/wpa_supplicant -B -c/etc/wpa_supplicant.conf -i$DEVICE
-fi
- </programlisting></blockquote>
-
-
- <para>Add the following block to the end of <emphasis>stop</emphasis>
- action handler (may need to be separated from other actions) in
- <filename>/etc/pcmcia/wireless</filename>:</para>
-
- <blockquote><programlisting>
-if [ "$WPA" = "y" -a -x /usr/local/bin/wpa_supplicant ]; then
- killall wpa_supplicant
-fi
- </programlisting></blockquote>
-
- <para>This will make <command>cardmgr</command> start
- <command>wpa_supplicant</command> when the card is plugged
- in.</para>
- </refsect1>
-
- <refsect1>
- <title>See Also</title>
- <para>
- <citerefentry>
- <refentrytitle>wpa_background</refentrytitle>
- <manvolnum>8</manvolnum>
- </citerefentry>
- <citerefentry>
- <refentrytitle>wpa_supplicant.conf</refentrytitle>
- <manvolnum>5</manvolnum>
- </citerefentry>
- <citerefentry>
- <refentrytitle>wpa_cli</refentrytitle>
- <manvolnum>8</manvolnum>
- </citerefentry>
- <citerefentry>
- <refentrytitle>wpa_passphrase</refentrytitle>
- <manvolnum>8</manvolnum>
- </citerefentry>
- </para>
- </refsect1>
- <refsect1>
- <title>Legal</title>
- <para>wpa_supplicant is copyright (c) 2003-2007,
- Jouni Malinen <email>j@w1.fi</email> and
- contributors.
- All Rights Reserved.</para>
-
- <para>This program is dual-licensed under both the GPL version 2
- and BSD license. Either license may be used at your option.</para>
- </refsect1>
-</refentry>
diff --git a/contrib/wpa/wpa_supplicant/driver_i.h b/contrib/wpa/wpa_supplicant/driver_i.h
index a70aa6a..847600d 100644
--- a/contrib/wpa/wpa_supplicant/driver_i.h
+++ b/contrib/wpa/wpa_supplicant/driver_i.h
@@ -2,14 +2,8 @@
* wpa_supplicant - Internal driver interface wrappers
* Copyright (c) 2003-2009, Jouni Malinen <j@w1.fi>
*
- * This program is free software; you can redistribute it and/or modify
- * it under the terms of the GNU General Public License version 2 as
- * published by the Free Software Foundation.
- *
- * Alternatively, this software may be distributed under the terms of BSD
- * license.
- *
- * See README and COPYING for more details.
+ * This software may be distributed under the terms of the BSD license.
+ * See README for more details.
*/
#ifndef DRIVER_I_H
@@ -79,6 +73,23 @@ static inline int wpa_drv_scan(struct wpa_supplicant *wpa_s,
return -1;
}
+static inline int wpa_drv_sched_scan(struct wpa_supplicant *wpa_s,
+ struct wpa_driver_scan_params *params,
+ u32 interval)
+{
+ if (wpa_s->driver->sched_scan)
+ return wpa_s->driver->sched_scan(wpa_s->drv_priv,
+ params, interval);
+ return -1;
+}
+
+static inline int wpa_drv_stop_sched_scan(struct wpa_supplicant *wpa_s)
+{
+ if (wpa_s->driver->stop_sched_scan)
+ return wpa_s->driver->stop_sched_scan(wpa_s->drv_priv);
+ return -1;
+}
+
static inline struct wpa_scan_results * wpa_drv_get_scan_results2(
struct wpa_supplicant *wpa_s)
{
@@ -128,16 +139,6 @@ static inline int wpa_drv_deauthenticate(struct wpa_supplicant *wpa_s,
return -1;
}
-static inline int wpa_drv_disassociate(struct wpa_supplicant *wpa_s,
- const u8 *addr, int reason_code)
-{
- if (wpa_s->driver->disassociate) {
- return wpa_s->driver->disassociate(wpa_s->drv_priv, addr,
- reason_code);
- }
- return -1;
-}
-
static inline int wpa_drv_add_pmkid(struct wpa_supplicant *wpa_s,
const u8 *bssid, const u8 *pmkid)
{
@@ -236,35 +237,6 @@ wpa_drv_get_hw_feature_data(struct wpa_supplicant *wpa_s, u16 *num_modes,
return NULL;
}
-static inline int wpa_drv_set_channel(struct wpa_supplicant *wpa_s,
- enum hostapd_hw_mode phymode, int chan,
- int freq)
-{
- if (wpa_s->driver->set_channel)
- return wpa_s->driver->set_channel(wpa_s->drv_priv, phymode,
- chan, freq);
- return -1;
-}
-
-static inline int wpa_drv_set_ssid(struct wpa_supplicant *wpa_s,
- const u8 *ssid, size_t ssid_len)
-{
- if (wpa_s->driver->set_ssid) {
- return wpa_s->driver->set_ssid(wpa_s->drv_priv, ssid,
- ssid_len);
- }
- return -1;
-}
-
-static inline int wpa_drv_set_bssid(struct wpa_supplicant *wpa_s,
- const u8 *bssid)
-{
- if (wpa_s->driver->set_bssid) {
- return wpa_s->driver->set_bssid(wpa_s->drv_priv, bssid);
- }
- return -1;
-}
-
static inline int wpa_drv_set_country(struct wpa_supplicant *wpa_s,
const char *alpha2)
{
@@ -274,29 +246,11 @@ static inline int wpa_drv_set_country(struct wpa_supplicant *wpa_s,
}
static inline int wpa_drv_send_mlme(struct wpa_supplicant *wpa_s,
- const u8 *data, size_t data_len)
+ const u8 *data, size_t data_len, int noack)
{
if (wpa_s->driver->send_mlme)
return wpa_s->driver->send_mlme(wpa_s->drv_priv,
- data, data_len);
- return -1;
-}
-
-static inline int wpa_drv_mlme_add_sta(struct wpa_supplicant *wpa_s,
- const u8 *addr, const u8 *supp_rates,
- size_t supp_rates_len)
-{
- if (wpa_s->driver->mlme_add_sta)
- return wpa_s->driver->mlme_add_sta(wpa_s->drv_priv, addr,
- supp_rates, supp_rates_len);
- return -1;
-}
-
-static inline int wpa_drv_mlme_remove_sta(struct wpa_supplicant *wpa_s,
- const u8 *addr)
-{
- if (wpa_s->driver->mlme_remove_sta)
- return wpa_s->driver->mlme_remove_sta(wpa_s->drv_priv, addr);
+ data, data_len, noack);
return -1;
}
@@ -320,15 +274,11 @@ static inline int wpa_drv_send_ft_action(struct wpa_supplicant *wpa_s,
return -1;
}
-static inline int wpa_drv_set_beacon(struct wpa_supplicant *wpa_s,
- const u8 *head, size_t head_len,
- const u8 *tail, size_t tail_len,
- int dtim_period, int beacon_int)
+static inline int wpa_drv_set_ap(struct wpa_supplicant *wpa_s,
+ struct wpa_driver_ap_params *params)
{
- if (wpa_s->driver->set_beacon)
- return wpa_s->driver->set_beacon(wpa_s->drv_priv, head,
- head_len, tail, tail_len,
- dtim_period, beacon_int);
+ if (wpa_s->driver->set_ap)
+ return wpa_s->driver->set_ap(wpa_s->drv_priv, params);
return -1;
}
@@ -351,12 +301,12 @@ static inline int wpa_drv_sta_remove(struct wpa_supplicant *wpa_s,
static inline int wpa_drv_hapd_send_eapol(struct wpa_supplicant *wpa_s,
const u8 *addr, const u8 *data,
size_t data_len, int encrypt,
- const u8 *own_addr)
+ const u8 *own_addr, u32 flags)
{
if (wpa_s->driver->hapd_send_eapol)
return wpa_s->driver->hapd_send_eapol(wpa_s->drv_priv, addr,
data, data_len, encrypt,
- own_addr);
+ own_addr, flags);
return -1;
}
@@ -383,14 +333,30 @@ static inline int wpa_drv_set_supp_port(struct wpa_supplicant *wpa_s,
static inline int wpa_drv_send_action(struct wpa_supplicant *wpa_s,
unsigned int freq,
+ unsigned int wait,
const u8 *dst, const u8 *src,
const u8 *bssid,
- const u8 *data, size_t data_len)
+ const u8 *data, size_t data_len,
+ int no_cck)
{
if (wpa_s->driver->send_action)
return wpa_s->driver->send_action(wpa_s->drv_priv, freq,
- dst, src, bssid, data,
- data_len);
+ wait, dst, src, bssid,
+ data, data_len, no_cck);
+ return -1;
+}
+
+static inline void wpa_drv_send_action_cancel_wait(struct wpa_supplicant *wpa_s)
+{
+ if (wpa_s->driver->send_action_cancel_wait)
+ wpa_s->driver->send_action_cancel_wait(wpa_s->drv_priv);
+}
+
+static inline int wpa_drv_set_freq(struct wpa_supplicant *wpa_s,
+ struct hostapd_freq_params *freq)
+{
+ if (wpa_s->driver->set_freq)
+ return wpa_s->driver->set_freq(wpa_s->drv_priv, freq);
return -1;
}
@@ -398,12 +364,12 @@ static inline int wpa_drv_if_add(struct wpa_supplicant *wpa_s,
enum wpa_driver_if_type type,
const char *ifname, const u8 *addr,
void *bss_ctx, char *force_ifname,
- u8 *if_addr)
+ u8 *if_addr, const char *bridge)
{
if (wpa_s->driver->if_add)
return wpa_s->driver->if_add(wpa_s->drv_priv, type, ifname,
addr, bss_ctx, NULL, force_ifname,
- if_addr);
+ if_addr, bridge);
return -1;
}
@@ -444,15 +410,6 @@ static inline int wpa_drv_probe_req_report(struct wpa_supplicant *wpa_s,
return -1;
}
-static inline int wpa_drv_disable_11b_rates(struct wpa_supplicant *wpa_s,
- int disabled)
-{
- if (wpa_s->driver->disable_11b_rates)
- return wpa_s->driver->disable_11b_rates(wpa_s->drv_priv,
- disabled);
- return -1;
-}
-
static inline int wpa_drv_deinit_ap(struct wpa_supplicant *wpa_s)
{
if (wpa_s->driver->deinit_ap)
@@ -460,6 +417,13 @@ static inline int wpa_drv_deinit_ap(struct wpa_supplicant *wpa_s)
return 0;
}
+static inline int wpa_drv_deinit_p2p_cli(struct wpa_supplicant *wpa_s)
+{
+ if (wpa_s->driver->deinit_p2p_cli)
+ return wpa_s->driver->deinit_p2p_cli(wpa_s->drv_priv);
+ return 0;
+}
+
static inline void wpa_drv_suspend(struct wpa_supplicant *wpa_s)
{
if (wpa_s->driver->suspend)
@@ -481,14 +445,248 @@ static inline int wpa_drv_signal_monitor(struct wpa_supplicant *wpa_s,
return -1;
}
+static inline int wpa_drv_signal_poll(struct wpa_supplicant *wpa_s,
+ struct wpa_signal_info *si)
+{
+ if (wpa_s->driver->signal_poll)
+ return wpa_s->driver->signal_poll(wpa_s->drv_priv, si);
+ return -1;
+}
+
+static inline int wpa_drv_pktcnt_poll(struct wpa_supplicant *wpa_s,
+ struct hostap_sta_driver_data *sta)
+{
+ if (wpa_s->driver->read_sta_data)
+ return wpa_s->driver->read_sta_data(wpa_s->drv_priv, sta,
+ wpa_s->bssid);
+ return -1;
+}
+
static inline int wpa_drv_set_ap_wps_ie(struct wpa_supplicant *wpa_s,
const struct wpabuf *beacon,
- const struct wpabuf *proberesp)
+ const struct wpabuf *proberesp,
+ const struct wpabuf *assocresp)
{
if (!wpa_s->driver->set_ap_wps_ie)
return -1;
return wpa_s->driver->set_ap_wps_ie(wpa_s->drv_priv, beacon,
- proberesp);
+ proberesp, assocresp);
+}
+
+static inline int wpa_drv_shared_freq(struct wpa_supplicant *wpa_s)
+{
+ if (!wpa_s->driver->shared_freq)
+ return -1;
+ return wpa_s->driver->shared_freq(wpa_s->drv_priv);
+}
+
+static inline int wpa_drv_get_noa(struct wpa_supplicant *wpa_s,
+ u8 *buf, size_t buf_len)
+{
+ if (!wpa_s->driver->get_noa)
+ return -1;
+ return wpa_s->driver->get_noa(wpa_s->drv_priv, buf, buf_len);
+}
+
+static inline int wpa_drv_set_p2p_powersave(struct wpa_supplicant *wpa_s,
+ int legacy_ps, int opp_ps,
+ int ctwindow)
+{
+ if (!wpa_s->driver->set_p2p_powersave)
+ return -1;
+ return wpa_s->driver->set_p2p_powersave(wpa_s->drv_priv, legacy_ps,
+ opp_ps, ctwindow);
+}
+
+static inline int wpa_drv_ampdu(struct wpa_supplicant *wpa_s, int ampdu)
+{
+ if (!wpa_s->driver->ampdu)
+ return -1;
+ return wpa_s->driver->ampdu(wpa_s->drv_priv, ampdu);
+}
+
+static inline int wpa_drv_p2p_find(struct wpa_supplicant *wpa_s,
+ unsigned int timeout, int type)
+{
+ if (!wpa_s->driver->p2p_find)
+ return -1;
+ return wpa_s->driver->p2p_find(wpa_s->drv_priv, timeout, type);
+}
+
+static inline int wpa_drv_p2p_stop_find(struct wpa_supplicant *wpa_s)
+{
+ if (!wpa_s->driver->p2p_stop_find)
+ return -1;
+ return wpa_s->driver->p2p_stop_find(wpa_s->drv_priv);
+}
+
+static inline int wpa_drv_p2p_listen(struct wpa_supplicant *wpa_s,
+ unsigned int timeout)
+{
+ if (!wpa_s->driver->p2p_listen)
+ return -1;
+ return wpa_s->driver->p2p_listen(wpa_s->drv_priv, timeout);
+}
+
+static inline int wpa_drv_p2p_connect(struct wpa_supplicant *wpa_s,
+ const u8 *peer_addr, int wps_method,
+ int go_intent,
+ const u8 *own_interface_addr,
+ unsigned int force_freq,
+ int persistent_group)
+{
+ if (!wpa_s->driver->p2p_connect)
+ return -1;
+ return wpa_s->driver->p2p_connect(wpa_s->drv_priv, peer_addr,
+ wps_method, go_intent,
+ own_interface_addr, force_freq,
+ persistent_group);
+}
+
+static inline int wpa_drv_wps_success_cb(struct wpa_supplicant *wpa_s,
+ const u8 *peer_addr)
+{
+ if (!wpa_s->driver->wps_success_cb)
+ return -1;
+ return wpa_s->driver->wps_success_cb(wpa_s->drv_priv, peer_addr);
+}
+
+static inline int
+wpa_drv_p2p_group_formation_failed(struct wpa_supplicant *wpa_s)
+{
+ if (!wpa_s->driver->p2p_group_formation_failed)
+ return -1;
+ return wpa_s->driver->p2p_group_formation_failed(wpa_s->drv_priv);
+}
+
+static inline int wpa_drv_p2p_set_params(struct wpa_supplicant *wpa_s,
+ const struct p2p_params *params)
+{
+ if (!wpa_s->driver->p2p_set_params)
+ return -1;
+ return wpa_s->driver->p2p_set_params(wpa_s->drv_priv, params);
+}
+
+static inline int wpa_drv_p2p_prov_disc_req(struct wpa_supplicant *wpa_s,
+ const u8 *peer_addr,
+ u16 config_methods, int join)
+{
+ if (!wpa_s->driver->p2p_prov_disc_req)
+ return -1;
+ return wpa_s->driver->p2p_prov_disc_req(wpa_s->drv_priv, peer_addr,
+ config_methods, join);
+}
+
+static inline u64 wpa_drv_p2p_sd_request(struct wpa_supplicant *wpa_s,
+ const u8 *dst,
+ const struct wpabuf *tlvs)
+{
+ if (!wpa_s->driver->p2p_sd_request)
+ return 0;
+ return wpa_s->driver->p2p_sd_request(wpa_s->drv_priv, dst, tlvs);
+}
+
+static inline int wpa_drv_p2p_sd_cancel_request(struct wpa_supplicant *wpa_s,
+ u64 req)
+{
+ if (!wpa_s->driver->p2p_sd_cancel_request)
+ return -1;
+ return wpa_s->driver->p2p_sd_cancel_request(wpa_s->drv_priv, req);
+}
+
+static inline int wpa_drv_p2p_sd_response(struct wpa_supplicant *wpa_s,
+ int freq, const u8 *dst,
+ u8 dialog_token,
+ const struct wpabuf *resp_tlvs)
+{
+ if (!wpa_s->driver->p2p_sd_response)
+ return -1;
+ return wpa_s->driver->p2p_sd_response(wpa_s->drv_priv, freq, dst,
+ dialog_token, resp_tlvs);
+}
+
+static inline int wpa_drv_p2p_service_update(struct wpa_supplicant *wpa_s)
+{
+ if (!wpa_s->driver->p2p_service_update)
+ return -1;
+ return wpa_s->driver->p2p_service_update(wpa_s->drv_priv);
+}
+
+static inline int wpa_drv_p2p_reject(struct wpa_supplicant *wpa_s,
+ const u8 *addr)
+{
+ if (!wpa_s->driver->p2p_reject)
+ return -1;
+ return wpa_s->driver->p2p_reject(wpa_s->drv_priv, addr);
+}
+
+static inline int wpa_drv_p2p_invite(struct wpa_supplicant *wpa_s,
+ const u8 *peer, int role, const u8 *bssid,
+ const u8 *ssid, size_t ssid_len,
+ const u8 *go_dev_addr,
+ int persistent_group)
+{
+ if (!wpa_s->driver->p2p_invite)
+ return -1;
+ return wpa_s->driver->p2p_invite(wpa_s->drv_priv, peer, role, bssid,
+ ssid, ssid_len, go_dev_addr,
+ persistent_group);
+}
+
+static inline int wpa_drv_send_tdls_mgmt(struct wpa_supplicant *wpa_s,
+ const u8 *dst, u8 action_code,
+ u8 dialog_token, u16 status_code,
+ const u8 *buf, size_t len)
+{
+ if (wpa_s->driver->send_tdls_mgmt) {
+ return wpa_s->driver->send_tdls_mgmt(wpa_s->drv_priv, dst,
+ action_code, dialog_token,
+ status_code, buf, len);
+ }
+ return -1;
+}
+
+static inline int wpa_drv_tdls_oper(struct wpa_supplicant *wpa_s,
+ enum tdls_oper oper, const u8 *peer)
+{
+ if (!wpa_s->driver->tdls_oper)
+ return -1;
+ return wpa_s->driver->tdls_oper(wpa_s->drv_priv, oper, peer);
+}
+
+static inline void wpa_drv_set_rekey_info(struct wpa_supplicant *wpa_s,
+ const u8 *kek, const u8 *kck,
+ const u8 *replay_ctr)
+{
+ if (!wpa_s->driver->set_rekey_info)
+ return;
+ wpa_s->driver->set_rekey_info(wpa_s->drv_priv, kek, kck, replay_ctr);
+}
+
+static inline int wpa_drv_radio_disable(struct wpa_supplicant *wpa_s,
+ int disabled)
+{
+ if (!wpa_s->driver->radio_disable)
+ return -1;
+ return wpa_s->driver->radio_disable(wpa_s->drv_priv, disabled);
+}
+
+static inline int wpa_drv_switch_channel(struct wpa_supplicant *wpa_s,
+ unsigned int freq)
+{
+ if (!wpa_s->driver->switch_channel)
+ return -1;
+ return wpa_s->driver->switch_channel(wpa_s->drv_priv, freq);
+}
+
+static inline int wpa_drv_wnm_oper(struct wpa_supplicant *wpa_s,
+ enum wnm_oper oper, const u8 *peer,
+ u8 *buf, u16 *buf_len)
+{
+ if (!wpa_s->driver->wnm_oper)
+ return -1;
+ return wpa_s->driver->wnm_oper(wpa_s->drv_priv, oper, peer, buf,
+ buf_len);
}
#endif /* DRIVER_I_H */
diff --git a/contrib/wpa/wpa_supplicant/eap_register.c b/contrib/wpa/wpa_supplicant/eap_register.c
index f668874..d1eb4ff 100644
--- a/contrib/wpa/wpa_supplicant/eap_register.c
+++ b/contrib/wpa/wpa_supplicant/eap_register.c
@@ -2,14 +2,8 @@
* EAP method registration
* Copyright (c) 2004-2009, Jouni Malinen <j@w1.fi>
*
- * This program is free software; you can redistribute it and/or modify
- * it under the terms of the GNU General Public License version 2 as
- * published by the Free Software Foundation.
- *
- * Alternatively, this software may be distributed under the terms of BSD
- * license.
- *
- * See README and COPYING for more details.
+ * This software may be distributed under the terms of the BSD license.
+ * See README for more details.
*/
#include "includes.h"
@@ -17,6 +11,7 @@
#include "common.h"
#include "eap_peer/eap_methods.h"
#include "eap_server/eap_methods.h"
+#include "wpa_supplicant_i.h"
/**
@@ -40,6 +35,11 @@ int eap_register_methods(void)
ret = eap_peer_tls_register();
#endif /* EAP_TLS */
+#ifdef EAP_UNAUTH_TLS
+ if (ret == 0)
+ ret = eap_peer_unauth_tls_register();
+#endif /* EAP_UNAUTH_TLS */
+
#ifdef EAP_MSCHAPv2
if (ret == 0)
ret = eap_peer_mschapv2_register();
@@ -130,6 +130,10 @@ int eap_register_methods(void)
ret = eap_peer_tnc_register();
#endif /* EAP_TNC */
+#ifdef EAP_PWD
+ if (ret == 0)
+ ret = eap_peer_pwd_register();
+#endif /* EAP_PWD */
#ifdef EAP_SERVER_IDENTITY
if (ret == 0)
@@ -146,6 +150,11 @@ int eap_register_methods(void)
ret = eap_server_tls_register();
#endif /* EAP_SERVER_TLS */
+#ifdef EAP_SERVER_UNAUTH_TLS
+ if (ret == 0)
+ ret = eap_server_unauth_tls_register();
+#endif /* EAP_SERVER_UNAUTH_TLS */
+
#ifdef EAP_SERVER_MSCHAPV2
if (ret == 0)
ret = eap_server_mschapv2_register();
@@ -231,5 +240,10 @@ int eap_register_methods(void)
ret = eap_server_tnc_register();
#endif /* EAP_SERVER_TNC */
+#ifdef EAP_SERVER_PWD
+ if (ret == 0)
+ ret = eap_server_pwd_register();
+#endif /* EAP_SERVER_PWD */
+
return ret;
}
diff --git a/contrib/wpa/wpa_supplicant/eapol_test.c b/contrib/wpa/wpa_supplicant/eapol_test.c
index 4eed854..80fe2c6 100644
--- a/contrib/wpa/wpa_supplicant/eapol_test.c
+++ b/contrib/wpa/wpa_supplicant/eapol_test.c
@@ -1,15 +1,9 @@
/*
* WPA Supplicant - test code
- * Copyright (c) 2003-2007, Jouni Malinen <j@w1.fi>
+ * Copyright (c) 2003-2012, Jouni Malinen <j@w1.fi>
*
- * This program is free software; you can redistribute it and/or modify
- * it under the terms of the GNU General Public License version 2 as
- * published by the Free Software Foundation.
- *
- * Alternatively, this software may be distributed under the terms of BSD
- * license.
- *
- * See README and COPYING for more details.
+ * This software may be distributed under the terms of the BSD license.
+ * See README for more details.
*
* IEEE 802.1X Supplicant test code (to be used in place of wpa_supplicant.c.
* Not used in production version.
@@ -19,15 +13,19 @@
#include <assert.h>
#include "common.h"
+#include "utils/ext_password.h"
#include "config.h"
#include "eapol_supp/eapol_supp_sm.h"
#include "eap_peer/eap.h"
+#include "eap_server/eap_methods.h"
#include "eloop.h"
+#include "utils/base64.h"
#include "rsn_supp/wpa.h"
#include "eap_peer/eap_i.h"
#include "wpa_supplicant_i.h"
#include "radius/radius.h"
#include "radius/radius_client.h"
+#include "common/wpa_ctrl.h"
#include "ctrl_iface.h"
#include "pcsc_funcs.h"
@@ -58,9 +56,8 @@ struct eapol_test_data {
struct radius_client_data *radius;
struct hostapd_radius_servers *radius_conf;
- u8 *last_eap_radius; /* last received EAP Response from Authentication
- * Server */
- size_t last_eap_radius_len;
+ /* last received EAP Response from Authentication Server */
+ struct wpabuf *last_eap_radius;
u8 authenticator_pmk[PMK_LEN];
size_t authenticator_pmk_len;
@@ -74,6 +71,8 @@ struct eapol_test_data {
char *connect_info;
u8 own_addr[ETH_ALEN];
struct extra_radius_attr *extra_attrs;
+
+ FILE *server_cert_file;
};
static struct eapol_test_data eapol_test;
@@ -99,7 +98,7 @@ static int add_extra_attr(struct radius_msg *msg,
size_t len;
char *pos;
u32 val;
- char buf[128];
+ char buf[RADIUS_MAX_ATTR_LEN + 1];
switch (attr->syntax) {
case 's':
@@ -115,7 +114,7 @@ static int add_extra_attr(struct radius_msg *msg,
if (pos[0] == '0' && pos[1] == 'x')
pos += 2;
len = os_strlen(pos);
- if ((len & 1) || (len / 2) > sizeof(buf)) {
+ if ((len & 1) || (len / 2) > RADIUS_MAX_ATTR_LEN) {
printf("Invalid extra attribute hexstring\n");
return -1;
}
@@ -172,7 +171,7 @@ static void ieee802_1x_encapsulate_radius(struct eapol_test_data *e,
const u8 *eap, size_t len)
{
struct radius_msg *msg;
- char buf[128];
+ char buf[RADIUS_MAX_ATTR_LEN + 1];
const struct eap_hdr *hdr;
const u8 *pos;
@@ -279,7 +278,9 @@ static void ieee802_1x_encapsulate_radius(struct eapol_test_data *e,
}
}
- radius_client_send(e->radius, msg, RADIUS_AUTH, e->wpa_s->own_addr);
+ if (radius_client_send(e->radius, msg, RADIUS_AUTH, e->wpa_s->own_addr)
+ < 0)
+ goto fail;
return;
fail:
@@ -290,7 +291,6 @@ static void ieee802_1x_encapsulate_radius(struct eapol_test_data *e,
static int eapol_test_eapol_send(void *ctx, int type, const u8 *buf,
size_t len)
{
- /* struct wpa_supplicant *wpa_s = ctx; */
printf("WPA: eapol_test_eapol_send(type=%d len=%lu)\n",
type, (unsigned long) len);
if (type == IEEE802_1X_TYPE_EAP_PACKET) {
@@ -304,16 +304,16 @@ static int eapol_test_eapol_send(void *ctx, int type, const u8 *buf,
static void eapol_test_set_config_blob(void *ctx,
struct wpa_config_blob *blob)
{
- struct wpa_supplicant *wpa_s = ctx;
- wpa_config_set_blob(wpa_s->conf, blob);
+ struct eapol_test_data *e = ctx;
+ wpa_config_set_blob(e->wpa_s->conf, blob);
}
static const struct wpa_config_blob *
eapol_test_get_config_blob(void *ctx, const char *name)
{
- struct wpa_supplicant *wpa_s = ctx;
- return wpa_config_get_blob(wpa_s->conf, name);
+ struct eapol_test_data *e = ctx;
+ return wpa_config_get_blob(e->wpa_s->conf, name);
}
@@ -382,6 +382,84 @@ static void eapol_sm_cb(struct eapol_sm *eapol, int success, void *ctx)
}
+static void eapol_test_write_cert(FILE *f, const char *subject,
+ const struct wpabuf *cert)
+{
+ unsigned char *encoded;
+
+ encoded = base64_encode(wpabuf_head(cert), wpabuf_len(cert), NULL);
+ if (encoded == NULL)
+ return;
+ fprintf(f, "%s\n-----BEGIN CERTIFICATE-----\n%s"
+ "-----END CERTIFICATE-----\n\n", subject, encoded);
+ os_free(encoded);
+}
+
+
+static void eapol_test_cert_cb(void *ctx, int depth, const char *subject,
+ const char *cert_hash,
+ const struct wpabuf *cert)
+{
+ struct eapol_test_data *e = ctx;
+
+ wpa_msg(e->wpa_s, MSG_INFO, WPA_EVENT_EAP_PEER_CERT
+ "depth=%d subject='%s'%s%s",
+ depth, subject,
+ cert_hash ? " hash=" : "",
+ cert_hash ? cert_hash : "");
+
+ if (cert) {
+ char *cert_hex;
+ size_t len = wpabuf_len(cert) * 2 + 1;
+ cert_hex = os_malloc(len);
+ if (cert_hex) {
+ wpa_snprintf_hex(cert_hex, len, wpabuf_head(cert),
+ wpabuf_len(cert));
+ wpa_msg_ctrl(e->wpa_s, MSG_INFO,
+ WPA_EVENT_EAP_PEER_CERT
+ "depth=%d subject='%s' cert=%s",
+ depth, subject, cert_hex);
+ os_free(cert_hex);
+ }
+
+ if (e->server_cert_file)
+ eapol_test_write_cert(e->server_cert_file,
+ subject, cert);
+ }
+}
+
+
+static void eapol_test_set_anon_id(void *ctx, const u8 *id, size_t len)
+{
+ struct eapol_test_data *e = ctx;
+ struct wpa_supplicant *wpa_s = e->wpa_s;
+ char *str;
+ int res;
+
+ wpa_hexdump_ascii(MSG_DEBUG, "EAP method updated anonymous_identity",
+ id, len);
+
+ if (wpa_s->current_ssid == NULL)
+ return;
+
+ if (id == NULL) {
+ if (wpa_config_set(wpa_s->current_ssid, "anonymous_identity",
+ "NULL", 0) < 0)
+ return;
+ } else {
+ str = os_malloc(len * 2 + 1);
+ if (str == NULL)
+ return;
+ wpa_snprintf_hex(str, len * 2 + 1, id, len);
+ res = wpa_config_set(wpa_s->current_ssid, "anonymous_identity",
+ str, 0);
+ os_free(str);
+ if (res < 0)
+ return;
+ }
+}
+
+
static int test_eapol(struct eapol_test_data *e, struct wpa_supplicant *wpa_s,
struct wpa_ssid *ssid)
{
@@ -393,7 +471,7 @@ static int test_eapol(struct eapol_test_data *e, struct wpa_supplicant *wpa_s,
printf("Failed to allocate EAPOL context.\n");
return -1;
}
- ctx->ctx = wpa_s;
+ ctx->ctx = e;
ctx->msg_ctx = wpa_s;
ctx->scard_ctx = wpa_s->scard;
ctx->cb = eapol_sm_cb;
@@ -407,6 +485,9 @@ static int test_eapol(struct eapol_test_data *e, struct wpa_supplicant *wpa_s,
ctx->opensc_engine_path = wpa_s->conf->opensc_engine_path;
ctx->pkcs11_engine_path = wpa_s->conf->pkcs11_engine_path;
ctx->pkcs11_module_path = wpa_s->conf->pkcs11_module_path;
+ ctx->cert_cb = eapol_test_cert_cb;
+ ctx->cert_in_cb = 1;
+ ctx->set_anon_id = eapol_test_set_anon_id;
wpa_s->eapol = eapol_sm_init(ctx);
if (wpa_s->eapol == NULL) {
@@ -439,7 +520,7 @@ static void test_eapol_clean(struct eapol_test_data *e,
struct extra_radius_attr *p, *prev;
radius_client_deinit(e->radius);
- os_free(e->last_eap_radius);
+ wpabuf_free(e->last_eap_radius);
radius_msg_free(e->last_recv_radius);
e->last_recv_radius = NULL;
os_free(e->eap_identity);
@@ -457,6 +538,10 @@ static void test_eapol_clean(struct eapol_test_data *e,
wpa_supplicant_ctrl_iface_deinit(wpa_s->ctrl_iface);
wpa_s->ctrl_iface = NULL;
}
+
+ ext_password_deinit(wpa_s->ext_pw);
+ wpa_s->ext_pw = NULL;
+
wpa_config_free(wpa_s->conf);
p = e->extra_attrs;
@@ -525,9 +610,8 @@ static char *eap_type_text(u8 type)
static void ieee802_1x_decapsulate_radius(struct eapol_test_data *e)
{
- u8 *eap;
- size_t len;
- struct eap_hdr *hdr;
+ struct wpabuf *eap;
+ const struct eap_hdr *hdr;
int eap_type = -1;
char buf[64];
struct radius_msg *msg;
@@ -537,30 +621,29 @@ static void ieee802_1x_decapsulate_radius(struct eapol_test_data *e)
msg = e->last_recv_radius;
- eap = radius_msg_get_eap(msg, &len);
+ eap = radius_msg_get_eap(msg);
if (eap == NULL) {
/* draft-aboba-radius-rfc2869bis-20.txt, Chap. 2.6.3:
* RADIUS server SHOULD NOT send Access-Reject/no EAP-Message
* attribute */
wpa_printf(MSG_DEBUG, "could not extract "
"EAP-Message from RADIUS message");
- os_free(e->last_eap_radius);
+ wpabuf_free(e->last_eap_radius);
e->last_eap_radius = NULL;
- e->last_eap_radius_len = 0;
return;
}
- if (len < sizeof(*hdr)) {
+ if (wpabuf_len(eap) < sizeof(*hdr)) {
wpa_printf(MSG_DEBUG, "too short EAP packet "
"received from authentication server");
- os_free(eap);
+ wpabuf_free(eap);
return;
}
- if (len > sizeof(*hdr))
- eap_type = eap[sizeof(*hdr)];
+ if (wpabuf_len(eap) > sizeof(*hdr))
+ eap_type = (wpabuf_head_u8(eap))[sizeof(*hdr)];
- hdr = (struct eap_hdr *) eap;
+ hdr = wpabuf_head(eap);
switch (hdr->code) {
case EAP_CODE_REQUEST:
os_snprintf(buf, sizeof(buf), "EAP-Request-%s (%d)",
@@ -583,7 +666,7 @@ static void ieee802_1x_decapsulate_radius(struct eapol_test_data *e)
break;
default:
os_strlcpy(buf, "unknown EAP code", sizeof(buf));
- wpa_hexdump(MSG_DEBUG, "Decapsulated EAP packet", eap, len);
+ wpa_hexdump_buf(MSG_DEBUG, "Decapsulated EAP packet", eap);
break;
}
wpa_printf(MSG_DEBUG, "decapsulated EAP packet (code=%d "
@@ -592,20 +675,21 @@ static void ieee802_1x_decapsulate_radius(struct eapol_test_data *e)
/* sta->eapol_sm->be_auth.idFromServer = hdr->identifier; */
- os_free(e->last_eap_radius);
+ wpabuf_free(e->last_eap_radius);
e->last_eap_radius = eap;
- e->last_eap_radius_len = len;
{
struct ieee802_1x_hdr *dot1x;
- dot1x = os_malloc(sizeof(*dot1x) + len);
+ dot1x = os_malloc(sizeof(*dot1x) + wpabuf_len(eap));
assert(dot1x != NULL);
dot1x->version = EAPOL_VERSION;
dot1x->type = IEEE802_1X_TYPE_EAP_PACKET;
- dot1x->length = htons(len);
- os_memcpy((u8 *) (dot1x + 1), eap, len);
+ dot1x->length = htons(wpabuf_len(eap));
+ os_memcpy((u8 *) (dot1x + 1), wpabuf_head(eap),
+ wpabuf_len(eap));
eapol_sm_rx_eapol(e->wpa_s->eapol, e->wpa_s->bssid,
- (u8 *) dot1x, sizeof(*dot1x) + len);
+ (u8 *) dot1x,
+ sizeof(*dot1x) + wpabuf_len(eap));
os_free(dot1x);
}
}
@@ -809,7 +893,7 @@ static int scard_test(void)
unsigned char aka_ik[IK_LEN];
unsigned char aka_ck[CK_LEN];
- scard = scard_init(SCARD_TRY_BOTH);
+ scard = scard_init(SCARD_TRY_BOTH, NULL);
if (scard == NULL)
return -1;
if (scard_set_pin(scard, "1234")) {
@@ -824,6 +908,9 @@ static int scard_test(void)
wpa_hexdump_ascii(MSG_DEBUG, "SCARD: IMSI", (u8 *) imsi, len);
/* NOTE: Permanent Username: 1 | IMSI */
+ wpa_printf(MSG_DEBUG, "SCARD: MNC length %d",
+ scard_get_mnc_len(scard));
+
os_memset(_rand, 0, sizeof(_rand));
if (scard_gsm_auth(scard, _rand, sres, kc))
goto failed;
@@ -906,7 +993,7 @@ static int scard_get_triplets(int argc, char *argv[])
wpa_debug_level = 99;
}
- scard = scard_init(SCARD_GSM_SIM_ONLY);
+ scard = scard_init(SCARD_GSM_SIM_ONLY, NULL);
if (scard == NULL) {
printf("Failed to open smartcard connection\n");
return -1;
@@ -963,7 +1050,7 @@ static void usage(void)
"eapol_test [-nWS] -c<conf> [-a<AS IP>] [-p<AS port>] "
"[-s<AS secret>]\\\n"
" [-r<count>] [-t<timeout>] [-C<Connect-Info>] \\\n"
- " [-M<client MAC address>] \\\n"
+ " [-M<client MAC address>] [-o<server cert file] \\\n"
" [-N<attr spec>] \\\n"
" [-A<client IP>]\n"
"eapol_test scard\n"
@@ -989,6 +1076,8 @@ static void usage(void)
" -M<client MAC address> = Set own MAC address "
"(Calling-Station-Id,\n"
" default: 02:00:00:00:00:01)\n"
+ " -o<server cert file> = Write received server certificate\n"
+ " chain to the specified file\n"
" -N<attr spec> = send arbitrary attribute specified by:\n"
" attr_id:syntax:value or attr_id\n"
" attr_id - number id of the attribute\n"
@@ -1030,7 +1119,7 @@ int main(int argc, char *argv[])
wpa_debug_show_keys = 1;
for (;;) {
- c = getopt(argc, argv, "a:A:c:C:M:nN:p:r:s:St:W");
+ c = getopt(argc, argv, "a:A:c:C:M:nN:o:p:r:s:St:W");
if (c < 0)
break;
switch (c) {
@@ -1055,6 +1144,16 @@ int main(int argc, char *argv[])
case 'n':
eapol_test.no_mppe_keys++;
break;
+ case 'o':
+ if (eapol_test.server_cert_file)
+ fclose(eapol_test.server_cert_file);
+ eapol_test.server_cert_file = fopen(optarg, "w");
+ if (eapol_test.server_cert_file == NULL) {
+ printf("Could not open '%s' for writing\n",
+ optarg);
+ return -1;
+ }
+ break;
case 'p':
as_port = atoi(optarg);
break;
@@ -1074,7 +1173,7 @@ int main(int argc, char *argv[])
wait_for_monitor++;
break;
case 'N':
- p1 = os_zalloc(sizeof(p1));
+ p1 = os_zalloc(sizeof(*p1));
if (p1 == NULL)
break;
if (!p)
@@ -1164,6 +1263,9 @@ int main(int argc, char *argv[])
if (test_eapol(&eapol_test, &wpa_s, wpa_s.conf->ssid))
return -1;
+ if (wpas_init_ext_pw(&wpa_s) < 0)
+ return -1;
+
if (wait_for_monitor)
wpa_supplicant_ctrl_iface_wait(wpa_s.ctrl_iface);
@@ -1191,9 +1293,15 @@ int main(int argc, char *argv[])
test_eapol_clean(&eapol_test, &wpa_s);
eap_peer_unregister_methods();
+#ifdef CONFIG_AP
+ eap_server_unregister_methods();
+#endif /* CONFIG_AP */
eloop_destroy();
+ if (eapol_test.server_cert_file)
+ fclose(eapol_test.server_cert_file);
+
printf("MPPE keys OK: %d mismatch: %d\n",
eapol_test.num_mppe_ok, eapol_test.num_mppe_mismatch);
if (eapol_test.num_mppe_mismatch)
diff --git a/contrib/wpa/wpa_supplicant/events.c b/contrib/wpa/wpa_supplicant/events.c
index 85dcfb2..baca363 100644
--- a/contrib/wpa/wpa_supplicant/events.c
+++ b/contrib/wpa/wpa_supplicant/events.c
@@ -1,15 +1,9 @@
/*
* WPA Supplicant - Driver event processing
- * Copyright (c) 2003-2010, Jouni Malinen <j@w1.fi>
+ * Copyright (c) 2003-2012, Jouni Malinen <j@w1.fi>
*
- * This program is free software; you can redistribute it and/or modify
- * it under the terms of the GNU General Public License version 2 as
- * published by the Free Software Foundation.
- *
- * Alternatively, this software may be distributed under the terms of BSD
- * license.
- *
- * See README and COPYING for more details.
+ * This software may be distributed under the terms of the BSD license.
+ * See README for more details.
*/
#include "includes.h"
@@ -28,48 +22,84 @@
#include "common/wpa_ctrl.h"
#include "eap_peer/eap.h"
#include "ap/hostapd.h"
+#include "p2p/p2p.h"
+#include "wnm_sta.h"
#include "notify.h"
#include "common/ieee802_11_defs.h"
+#include "common/ieee802_11_common.h"
+#include "crypto/random.h"
#include "blacklist.h"
#include "wpas_glue.h"
#include "wps_supplicant.h"
#include "ibss_rsn.h"
#include "sme.h"
+#include "gas_query.h"
+#include "p2p_supplicant.h"
#include "bgscan.h"
+#include "autoscan.h"
#include "ap.h"
#include "bss.h"
-#include "mlme.h"
#include "scan.h"
+#include "offchannel.h"
+#include "interworking.h"
+
+
+static int wpas_temp_disabled(struct wpa_supplicant *wpa_s,
+ struct wpa_ssid *ssid)
+{
+ struct os_time now;
+
+ if (ssid == NULL || ssid->disabled_until.sec == 0)
+ return 0;
+
+ os_get_time(&now);
+ if (ssid->disabled_until.sec > now.sec)
+ return ssid->disabled_until.sec - now.sec;
+
+ wpas_clear_temp_disabled(wpa_s, ssid, 0);
+
+ return 0;
+}
static int wpa_supplicant_select_config(struct wpa_supplicant *wpa_s)
{
struct wpa_ssid *ssid, *old_ssid;
+ int res;
if (wpa_s->conf->ap_scan == 1 && wpa_s->current_ssid)
return 0;
- wpa_printf(MSG_DEBUG, "Select network based on association "
- "information");
+ wpa_dbg(wpa_s, MSG_DEBUG, "Select network based on association "
+ "information");
ssid = wpa_supplicant_get_ssid(wpa_s);
if (ssid == NULL) {
- wpa_printf(MSG_INFO, "No network configuration found for the "
- "current AP");
+ wpa_msg(wpa_s, MSG_INFO,
+ "No network configuration found for the current AP");
+ return -1;
+ }
+
+ if (wpas_network_disabled(wpa_s, ssid)) {
+ wpa_dbg(wpa_s, MSG_DEBUG, "Selected network is disabled");
+ return -1;
+ }
+
+ if (disallowed_bssid(wpa_s, wpa_s->bssid) ||
+ disallowed_ssid(wpa_s, ssid->ssid, ssid->ssid_len)) {
+ wpa_dbg(wpa_s, MSG_DEBUG, "Selected BSS is disallowed");
return -1;
}
- if (ssid->disabled) {
- wpa_printf(MSG_DEBUG, "Selected network is disabled");
+ res = wpas_temp_disabled(wpa_s, ssid);
+ if (res > 0) {
+ wpa_dbg(wpa_s, MSG_DEBUG, "Selected network is temporarily "
+ "disabled for %d second(s)", res);
return -1;
}
- wpa_printf(MSG_DEBUG, "Network configuration found for the current "
- "AP");
- if (ssid->key_mgmt & (WPA_KEY_MGMT_PSK | WPA_KEY_MGMT_IEEE8021X |
- WPA_KEY_MGMT_WPA_NONE |
- WPA_KEY_MGMT_FT_PSK | WPA_KEY_MGMT_FT_IEEE8021X |
- WPA_KEY_MGMT_PSK_SHA256 |
- WPA_KEY_MGMT_IEEE8021X_SHA256)) {
+ wpa_dbg(wpa_s, MSG_DEBUG, "Network configuration found for the "
+ "current AP");
+ if (wpa_key_mgmt_wpa_any(ssid->key_mgmt)) {
u8 wpa_ie[80];
size_t wpa_ie_len = sizeof(wpa_ie);
wpa_supplicant_set_suites(wpa_s, NULL, ssid,
@@ -91,8 +121,7 @@ static int wpa_supplicant_select_config(struct wpa_supplicant *wpa_s)
}
-static void wpa_supplicant_stop_countermeasures(void *eloop_ctx,
- void *sock_ctx)
+void wpa_supplicant_stop_countermeasures(void *eloop_ctx, void *sock_ctx)
{
struct wpa_supplicant *wpa_s = eloop_ctx;
@@ -109,11 +138,39 @@ void wpa_supplicant_mark_disassoc(struct wpa_supplicant *wpa_s)
{
int bssid_changed;
+ wnm_bss_keep_alive_deinit(wpa_s);
+
+#ifdef CONFIG_IBSS_RSN
+ ibss_rsn_deinit(wpa_s->ibss_rsn);
+ wpa_s->ibss_rsn = NULL;
+#endif /* CONFIG_IBSS_RSN */
+
+#ifdef CONFIG_AP
+ wpa_supplicant_ap_deinit(wpa_s);
+#endif /* CONFIG_AP */
+
+ if (wpa_s->wpa_state == WPA_INTERFACE_DISABLED)
+ return;
+
wpa_supplicant_set_state(wpa_s, WPA_DISCONNECTED);
bssid_changed = !is_zero_ether_addr(wpa_s->bssid);
os_memset(wpa_s->bssid, 0, ETH_ALEN);
os_memset(wpa_s->pending_bssid, 0, ETH_ALEN);
+#ifdef CONFIG_SME
+ wpa_s->sme.prev_bssid_set = 0;
+#endif /* CONFIG_SME */
+#ifdef CONFIG_P2P
+ os_memset(wpa_s->go_dev_addr, 0, ETH_ALEN);
+#endif /* CONFIG_P2P */
wpa_s->current_bss = NULL;
+ wpa_s->assoc_freq = 0;
+#ifdef CONFIG_IEEE80211R
+#ifdef CONFIG_SME
+ if (wpa_s->sme.ft_ies)
+ sme_update_ft_ies(wpa_s, NULL, NULL, 0);
+#endif /* CONFIG_SME */
+#endif /* CONFIG_IEEE80211R */
+
if (bssid_changed)
wpas_notify_bssid_changed(wpa_s);
@@ -122,6 +179,8 @@ void wpa_supplicant_mark_disassoc(struct wpa_supplicant *wpa_s)
if (wpa_key_mgmt_wpa_psk(wpa_s->key_mgmt))
eapol_sm_notify_eap_success(wpa_s->eapol, FALSE);
wpa_s->ap_ies_from_associnfo = 0;
+ wpa_s->current_ssid = NULL;
+ wpa_s->key_mgmt = 0;
}
@@ -145,8 +204,8 @@ static void wpa_find_assoc_pmkid(struct wpa_supplicant *wpa_s)
}
}
- wpa_printf(MSG_DEBUG, "RSN: PMKID from assoc IE %sfound from PMKSA "
- "cache", pmksa_set == 0 ? "" : "not ");
+ wpa_dbg(wpa_s, MSG_DEBUG, "RSN: PMKID from assoc IE %sfound from "
+ "PMKSA cache", pmksa_set == 0 ? "" : "not ");
}
@@ -154,14 +213,15 @@ static void wpa_supplicant_event_pmkid_candidate(struct wpa_supplicant *wpa_s,
union wpa_event_data *data)
{
if (data == NULL) {
- wpa_printf(MSG_DEBUG, "RSN: No data in PMKID candidate event");
+ wpa_dbg(wpa_s, MSG_DEBUG, "RSN: No data in PMKID candidate "
+ "event");
return;
}
- wpa_printf(MSG_DEBUG, "RSN: PMKID candidate event - bssid=" MACSTR
- " index=%d preauth=%d",
- MAC2STR(data->pmkid_candidate.bssid),
- data->pmkid_candidate.index,
- data->pmkid_candidate.preauth);
+ wpa_dbg(wpa_s, MSG_DEBUG, "RSN: PMKID candidate event - bssid=" MACSTR
+ " index=%d preauth=%d",
+ MAC2STR(data->pmkid_candidate.bssid),
+ data->pmkid_candidate.index,
+ data->pmkid_candidate.preauth);
pmksa_candidate_add(wpa_s->wpa, data->pmkid_candidate.bssid,
data->pmkid_candidate.index,
@@ -204,6 +264,7 @@ int wpa_supplicant_scard_init(struct wpa_supplicant *wpa_s,
struct wpa_ssid *ssid)
{
#ifdef IEEE8021X_EAPOL
+#ifdef PCSC_FUNCS
int aka = 0, sim = 0, type;
if (ssid->eap.pcsc == NULL || wpa_s->scard != NULL)
@@ -219,7 +280,8 @@ int wpa_supplicant_scard_init(struct wpa_supplicant *wpa_s,
if (eap->vendor == EAP_VENDOR_IETF) {
if (eap->method == EAP_TYPE_SIM)
sim = 1;
- else if (eap->method == EAP_TYPE_AKA)
+ else if (eap->method == EAP_TYPE_AKA ||
+ eap->method == EAP_TYPE_AKA_PRIME)
aka = 1;
}
eap++;
@@ -228,17 +290,20 @@ int wpa_supplicant_scard_init(struct wpa_supplicant *wpa_s,
if (eap_peer_get_eap_method(EAP_VENDOR_IETF, EAP_TYPE_SIM) == NULL)
sim = 0;
- if (eap_peer_get_eap_method(EAP_VENDOR_IETF, EAP_TYPE_AKA) == NULL)
+ if (eap_peer_get_eap_method(EAP_VENDOR_IETF, EAP_TYPE_AKA) == NULL &&
+ eap_peer_get_eap_method(EAP_VENDOR_IETF, EAP_TYPE_AKA_PRIME) ==
+ NULL)
aka = 0;
if (!sim && !aka) {
- wpa_printf(MSG_DEBUG, "Selected network is configured to use "
- "SIM, but neither EAP-SIM nor EAP-AKA are enabled");
+ wpa_dbg(wpa_s, MSG_DEBUG, "Selected network is configured to "
+ "use SIM, but neither EAP-SIM nor EAP-AKA are "
+ "enabled");
return 0;
}
- wpa_printf(MSG_DEBUG, "Selected network is configured to use SIM "
- "(sim=%d aka=%d) - initialize PCSC", sim, aka);
+ wpa_dbg(wpa_s, MSG_DEBUG, "Selected network is configured to use SIM "
+ "(sim=%d aka=%d) - initialize PCSC", sim, aka);
if (sim && aka)
type = SCARD_TRY_BOTH;
else if (aka)
@@ -246,14 +311,15 @@ int wpa_supplicant_scard_init(struct wpa_supplicant *wpa_s,
else
type = SCARD_GSM_SIM_ONLY;
- wpa_s->scard = scard_init(type);
+ wpa_s->scard = scard_init(type, NULL);
if (wpa_s->scard == NULL) {
- wpa_printf(MSG_WARNING, "Failed to initialize SIM "
- "(pcsc-lite)");
+ wpa_msg(wpa_s, MSG_WARNING, "Failed to initialize SIM "
+ "(pcsc-lite)");
return -1;
}
wpa_sm_set_scard_ctx(wpa_s->wpa, wpa_s->scard);
eapol_sm_register_scard_ctx(wpa_s->eapol, wpa_s->scard);
+#endif /* PCSC_FUNCS */
#endif /* IEEE8021X_EAPOL */
return 0;
@@ -261,7 +327,7 @@ int wpa_supplicant_scard_init(struct wpa_supplicant *wpa_s,
#ifndef CONFIG_NO_SCAN_PROCESSING
-static int wpa_supplicant_match_privacy(struct wpa_scan_res *bss,
+static int wpa_supplicant_match_privacy(struct wpa_bss *bss,
struct wpa_ssid *ssid)
{
int i, privacy = 0;
@@ -287,6 +353,9 @@ static int wpa_supplicant_match_privacy(struct wpa_scan_res *bss,
privacy = 1;
#endif /* IEEE8021X_EAPOL */
+ if (wpa_key_mgmt_wpa(ssid->key_mgmt))
+ privacy = 1;
+
if (bss->caps & IEEE80211_CAP_PRIVACY)
return privacy;
return !privacy;
@@ -295,100 +364,146 @@ static int wpa_supplicant_match_privacy(struct wpa_scan_res *bss,
static int wpa_supplicant_ssid_bss_match(struct wpa_supplicant *wpa_s,
struct wpa_ssid *ssid,
- struct wpa_scan_res *bss)
+ struct wpa_bss *bss)
{
struct wpa_ie_data ie;
int proto_match = 0;
const u8 *rsn_ie, *wpa_ie;
int ret;
+ int wep_ok;
ret = wpas_wps_ssid_bss_match(wpa_s, ssid, bss);
if (ret >= 0)
return ret;
- rsn_ie = wpa_scan_get_ie(bss, WLAN_EID_RSN);
+ /* Allow TSN if local configuration accepts WEP use without WPA/WPA2 */
+ wep_ok = !wpa_key_mgmt_wpa(ssid->key_mgmt) &&
+ (((ssid->key_mgmt & WPA_KEY_MGMT_NONE) &&
+ ssid->wep_key_len[ssid->wep_tx_keyidx] > 0) ||
+ (ssid->key_mgmt & WPA_KEY_MGMT_IEEE8021X_NO_WPA));
+
+ rsn_ie = wpa_bss_get_ie(bss, WLAN_EID_RSN);
while ((ssid->proto & WPA_PROTO_RSN) && rsn_ie) {
proto_match++;
if (wpa_parse_wpa_ie(rsn_ie, 2 + rsn_ie[1], &ie)) {
- wpa_printf(MSG_DEBUG, " skip RSN IE - parse failed");
+ wpa_dbg(wpa_s, MSG_DEBUG, " skip RSN IE - parse "
+ "failed");
break;
}
+
+ if (wep_ok &&
+ (ie.group_cipher & (WPA_CIPHER_WEP40 | WPA_CIPHER_WEP104)))
+ {
+ wpa_dbg(wpa_s, MSG_DEBUG, " selected based on TSN "
+ "in RSN IE");
+ return 1;
+ }
+
if (!(ie.proto & ssid->proto)) {
- wpa_printf(MSG_DEBUG, " skip RSN IE - proto "
- "mismatch");
+ wpa_dbg(wpa_s, MSG_DEBUG, " skip RSN IE - proto "
+ "mismatch");
break;
}
if (!(ie.pairwise_cipher & ssid->pairwise_cipher)) {
- wpa_printf(MSG_DEBUG, " skip RSN IE - PTK cipher "
- "mismatch");
+ wpa_dbg(wpa_s, MSG_DEBUG, " skip RSN IE - PTK "
+ "cipher mismatch");
break;
}
if (!(ie.group_cipher & ssid->group_cipher)) {
- wpa_printf(MSG_DEBUG, " skip RSN IE - GTK cipher "
- "mismatch");
+ wpa_dbg(wpa_s, MSG_DEBUG, " skip RSN IE - GTK "
+ "cipher mismatch");
break;
}
if (!(ie.key_mgmt & ssid->key_mgmt)) {
- wpa_printf(MSG_DEBUG, " skip RSN IE - key mgmt "
- "mismatch");
+ wpa_dbg(wpa_s, MSG_DEBUG, " skip RSN IE - key mgmt "
+ "mismatch");
break;
}
#ifdef CONFIG_IEEE80211W
if (!(ie.capabilities & WPA_CAPABILITY_MFPC) &&
- ssid->ieee80211w == MGMT_FRAME_PROTECTION_REQUIRED) {
- wpa_printf(MSG_DEBUG, " skip RSN IE - no mgmt frame "
- "protection");
+ (ssid->ieee80211w == MGMT_FRAME_PROTECTION_DEFAULT ?
+ wpa_s->conf->pmf : ssid->ieee80211w) ==
+ MGMT_FRAME_PROTECTION_REQUIRED) {
+ wpa_dbg(wpa_s, MSG_DEBUG, " skip RSN IE - no mgmt "
+ "frame protection");
break;
}
#endif /* CONFIG_IEEE80211W */
- wpa_printf(MSG_DEBUG, " selected based on RSN IE");
+ wpa_dbg(wpa_s, MSG_DEBUG, " selected based on RSN IE");
return 1;
}
- wpa_ie = wpa_scan_get_vendor_ie(bss, WPA_IE_VENDOR_TYPE);
+ wpa_ie = wpa_bss_get_vendor_ie(bss, WPA_IE_VENDOR_TYPE);
while ((ssid->proto & WPA_PROTO_WPA) && wpa_ie) {
proto_match++;
if (wpa_parse_wpa_ie(wpa_ie, 2 + wpa_ie[1], &ie)) {
- wpa_printf(MSG_DEBUG, " skip WPA IE - parse failed");
+ wpa_dbg(wpa_s, MSG_DEBUG, " skip WPA IE - parse "
+ "failed");
break;
}
+
+ if (wep_ok &&
+ (ie.group_cipher & (WPA_CIPHER_WEP40 | WPA_CIPHER_WEP104)))
+ {
+ wpa_dbg(wpa_s, MSG_DEBUG, " selected based on TSN "
+ "in WPA IE");
+ return 1;
+ }
+
if (!(ie.proto & ssid->proto)) {
- wpa_printf(MSG_DEBUG, " skip WPA IE - proto "
- "mismatch");
+ wpa_dbg(wpa_s, MSG_DEBUG, " skip WPA IE - proto "
+ "mismatch");
break;
}
if (!(ie.pairwise_cipher & ssid->pairwise_cipher)) {
- wpa_printf(MSG_DEBUG, " skip WPA IE - PTK cipher "
- "mismatch");
+ wpa_dbg(wpa_s, MSG_DEBUG, " skip WPA IE - PTK "
+ "cipher mismatch");
break;
}
if (!(ie.group_cipher & ssid->group_cipher)) {
- wpa_printf(MSG_DEBUG, " skip WPA IE - GTK cipher "
- "mismatch");
+ wpa_dbg(wpa_s, MSG_DEBUG, " skip WPA IE - GTK "
+ "cipher mismatch");
break;
}
if (!(ie.key_mgmt & ssid->key_mgmt)) {
- wpa_printf(MSG_DEBUG, " skip WPA IE - key mgmt "
- "mismatch");
+ wpa_dbg(wpa_s, MSG_DEBUG, " skip WPA IE - key mgmt "
+ "mismatch");
break;
}
- wpa_printf(MSG_DEBUG, " selected based on WPA IE");
+ wpa_dbg(wpa_s, MSG_DEBUG, " selected based on WPA IE");
+ return 1;
+ }
+
+ if ((ssid->key_mgmt & WPA_KEY_MGMT_IEEE8021X_NO_WPA) && !wpa_ie &&
+ !rsn_ie) {
+ wpa_dbg(wpa_s, MSG_DEBUG, " allow for non-WPA IEEE 802.1X");
+ return 1;
+ }
+
+ if ((ssid->proto & (WPA_PROTO_WPA | WPA_PROTO_RSN)) &&
+ wpa_key_mgmt_wpa(ssid->key_mgmt) && proto_match == 0) {
+ wpa_dbg(wpa_s, MSG_DEBUG, " skip - no WPA/RSN proto match");
+ return 0;
+ }
+
+ if (!wpa_key_mgmt_wpa(ssid->key_mgmt)) {
+ wpa_dbg(wpa_s, MSG_DEBUG, " allow in non-WPA/WPA2");
return 1;
}
- if (proto_match == 0)
- wpa_printf(MSG_DEBUG, " skip - no WPA/RSN proto match");
+ wpa_dbg(wpa_s, MSG_DEBUG, " reject due to mismatch with "
+ "WPA/WPA2");
return 0;
}
@@ -408,285 +523,333 @@ static int freq_allowed(int *freqs, int freq)
}
-static struct wpa_bss *
-wpa_supplicant_select_bss_wpa(struct wpa_supplicant *wpa_s,
- struct wpa_scan_results *scan_res,
- struct wpa_ssid *group,
- struct wpa_ssid **selected_ssid)
+static int ht_supported(const struct hostapd_hw_modes *mode)
{
- struct wpa_ssid *ssid;
- struct wpa_scan_res *bss;
- size_t i;
- struct wpa_blacklist *e;
- const u8 *ie;
+ if (!(mode->flags & HOSTAPD_MODE_FLAG_HT_INFO_KNOWN)) {
+ /*
+ * The driver did not indicate whether it supports HT. Assume
+ * it does to avoid connection issues.
+ */
+ return 1;
+ }
- wpa_printf(MSG_DEBUG, "Try to find WPA-enabled AP");
- for (i = 0; i < scan_res->num; i++) {
- const u8 *ssid_;
- u8 wpa_ie_len, rsn_ie_len, ssid_len;
- bss = scan_res->res[i];
+ /*
+ * IEEE Std 802.11n-2009 20.1.1:
+ * An HT non-AP STA shall support all EQM rates for one spatial stream.
+ */
+ return mode->mcs_set[0] == 0xff;
+}
- ie = wpa_scan_get_ie(bss, WLAN_EID_SSID);
- ssid_ = ie ? ie + 2 : (u8 *) "";
- ssid_len = ie ? ie[1] : 0;
- ie = wpa_scan_get_vendor_ie(bss, WPA_IE_VENDOR_TYPE);
- wpa_ie_len = ie ? ie[1] : 0;
+static int rate_match(struct wpa_supplicant *wpa_s, struct wpa_bss *bss)
+{
+ const struct hostapd_hw_modes *mode = NULL, *modes;
+ const u8 scan_ie[2] = { WLAN_EID_SUPP_RATES, WLAN_EID_EXT_SUPP_RATES };
+ const u8 *rate_ie;
+ int i, j, k;
- ie = wpa_scan_get_ie(bss, WLAN_EID_RSN);
- rsn_ie_len = ie ? ie[1] : 0;
+ if (bss->freq == 0)
+ return 1; /* Cannot do matching without knowing band */
- wpa_printf(MSG_DEBUG, "%d: " MACSTR " ssid='%s' "
- "wpa_ie_len=%u rsn_ie_len=%u caps=0x%x",
- (int) i, MAC2STR(bss->bssid),
- wpa_ssid_txt(ssid_, ssid_len),
- wpa_ie_len, rsn_ie_len, bss->caps);
+ modes = wpa_s->hw.modes;
+ if (modes == NULL) {
+ /*
+ * The driver does not provide any additional information
+ * about the utilized hardware, so allow the connection attempt
+ * to continue.
+ */
+ return 1;
+ }
- e = wpa_blacklist_get(wpa_s, bss->bssid);
- if (e && e->count > 1) {
- wpa_printf(MSG_DEBUG, " skip - blacklisted");
- continue;
+ for (i = 0; i < wpa_s->hw.num_modes; i++) {
+ for (j = 0; j < modes[i].num_channels; j++) {
+ int freq = modes[i].channels[j].freq;
+ if (freq == bss->freq) {
+ if (mode &&
+ mode->mode == HOSTAPD_MODE_IEEE80211G)
+ break; /* do not allow 802.11b replace
+ * 802.11g */
+ mode = &modes[i];
+ break;
+ }
}
+ }
- if (ssid_len == 0) {
- wpa_printf(MSG_DEBUG, " skip - SSID not known");
- continue;
- }
+ if (mode == NULL)
+ return 0;
- if (wpa_ie_len == 0 && rsn_ie_len == 0) {
- wpa_printf(MSG_DEBUG, " skip - no WPA/RSN IE");
+ for (i = 0; i < (int) sizeof(scan_ie); i++) {
+ rate_ie = wpa_bss_get_ie(bss, scan_ie[i]);
+ if (rate_ie == NULL)
continue;
- }
- for (ssid = group; ssid; ssid = ssid->pnext) {
- int check_ssid = 1;
+ for (j = 2; j < rate_ie[1] + 2; j++) {
+ int flagged = !!(rate_ie[j] & 0x80);
+ int r = (rate_ie[j] & 0x7f) * 5;
- if (ssid->disabled) {
- wpa_printf(MSG_DEBUG, " skip - disabled");
+ /*
+ * IEEE Std 802.11n-2009 7.3.2.2:
+ * The new BSS Membership selector value is encoded
+ * like a legacy basic rate, but it is not a rate and
+ * only indicates if the BSS members are required to
+ * support the mandatory features of Clause 20 [HT PHY]
+ * in order to join the BSS.
+ */
+ if (flagged && ((rate_ie[j] & 0x7f) ==
+ BSS_MEMBERSHIP_SELECTOR_HT_PHY)) {
+ if (!ht_supported(mode)) {
+ wpa_dbg(wpa_s, MSG_DEBUG,
+ " hardware does not support "
+ "HT PHY");
+ return 0;
+ }
continue;
}
-#ifdef CONFIG_WPS
- if (ssid->ssid_len == 0 &&
- wpas_wps_ssid_wildcard_ok(wpa_s, ssid, bss))
- check_ssid = 0;
-#endif /* CONFIG_WPS */
-
- if (check_ssid &&
- (ssid_len != ssid->ssid_len ||
- os_memcmp(ssid_, ssid->ssid, ssid_len) != 0)) {
- wpa_printf(MSG_DEBUG, " skip - "
- "SSID mismatch");
+ if (!flagged)
continue;
- }
- if (ssid->bssid_set &&
- os_memcmp(bss->bssid, ssid->bssid, ETH_ALEN) != 0)
- {
- wpa_printf(MSG_DEBUG, " skip - "
- "BSSID mismatch");
- continue;
+ /* check for legacy basic rates */
+ for (k = 0; k < mode->num_rates; k++) {
+ if (mode->rates[k] == r)
+ break;
}
-
- if (!wpa_supplicant_ssid_bss_match(wpa_s, ssid, bss))
- continue;
-
- if (!freq_allowed(ssid->freq_list, bss->freq)) {
- wpa_printf(MSG_DEBUG, " skip - "
- "frequency not allowed");
- continue;
+ if (k == mode->num_rates) {
+ /*
+ * IEEE Std 802.11-2007 7.3.2.2 demands that in
+ * order to join a BSS all required rates
+ * have to be supported by the hardware.
+ */
+ wpa_dbg(wpa_s, MSG_DEBUG, " hardware does "
+ "not support required rate %d.%d Mbps",
+ r / 10, r % 10);
+ return 0;
}
-
- wpa_printf(MSG_DEBUG, " selected WPA AP "
- MACSTR " ssid='%s'",
- MAC2STR(bss->bssid),
- wpa_ssid_txt(ssid_, ssid_len));
- *selected_ssid = ssid;
- return wpa_bss_get(wpa_s, bss->bssid, ssid_, ssid_len);
}
}
- return NULL;
+ return 1;
}
-static struct wpa_bss *
-wpa_supplicant_select_bss_non_wpa(struct wpa_supplicant *wpa_s,
- struct wpa_scan_results *scan_res,
- struct wpa_ssid *group,
- struct wpa_ssid **selected_ssid)
+static struct wpa_ssid * wpa_scan_res_match(struct wpa_supplicant *wpa_s,
+ int i, struct wpa_bss *bss,
+ struct wpa_ssid *group)
{
- struct wpa_ssid *ssid;
- struct wpa_scan_res *bss;
- size_t i;
+ u8 wpa_ie_len, rsn_ie_len;
+ int wpa;
struct wpa_blacklist *e;
const u8 *ie;
+ struct wpa_ssid *ssid;
+
+ ie = wpa_bss_get_vendor_ie(bss, WPA_IE_VENDOR_TYPE);
+ wpa_ie_len = ie ? ie[1] : 0;
+
+ ie = wpa_bss_get_ie(bss, WLAN_EID_RSN);
+ rsn_ie_len = ie ? ie[1] : 0;
+
+ wpa_dbg(wpa_s, MSG_DEBUG, "%d: " MACSTR " ssid='%s' "
+ "wpa_ie_len=%u rsn_ie_len=%u caps=0x%x level=%d%s",
+ i, MAC2STR(bss->bssid), wpa_ssid_txt(bss->ssid, bss->ssid_len),
+ wpa_ie_len, rsn_ie_len, bss->caps, bss->level,
+ wpa_bss_get_vendor_ie(bss, WPS_IE_VENDOR_TYPE) ? " wps" : "");
+
+ e = wpa_blacklist_get(wpa_s, bss->bssid);
+ if (e) {
+ int limit = 1;
+ if (wpa_supplicant_enabled_networks(wpa_s) == 1) {
+ /*
+ * When only a single network is enabled, we can
+ * trigger blacklisting on the first failure. This
+ * should not be done with multiple enabled networks to
+ * avoid getting forced to move into a worse ESS on
+ * single error if there are no other BSSes of the
+ * current ESS.
+ */
+ limit = 0;
+ }
+ if (e->count > limit) {
+ wpa_dbg(wpa_s, MSG_DEBUG, " skip - blacklisted "
+ "(count=%d limit=%d)", e->count, limit);
+ return NULL;
+ }
+ }
- wpa_printf(MSG_DEBUG, "Try to find non-WPA AP");
- for (i = 0; i < scan_res->num; i++) {
- const u8 *ssid_;
- u8 wpa_ie_len, rsn_ie_len, ssid_len;
- bss = scan_res->res[i];
+ if (bss->ssid_len == 0) {
+ wpa_dbg(wpa_s, MSG_DEBUG, " skip - SSID not known");
+ return NULL;
+ }
- ie = wpa_scan_get_ie(bss, WLAN_EID_SSID);
- ssid_ = ie ? ie + 2 : (u8 *) "";
- ssid_len = ie ? ie[1] : 0;
+ if (disallowed_bssid(wpa_s, bss->bssid)) {
+ wpa_dbg(wpa_s, MSG_DEBUG, " skip - BSSID disallowed");
+ return NULL;
+ }
- ie = wpa_scan_get_vendor_ie(bss, WPA_IE_VENDOR_TYPE);
- wpa_ie_len = ie ? ie[1] : 0;
+ if (disallowed_ssid(wpa_s, bss->ssid, bss->ssid_len)) {
+ wpa_dbg(wpa_s, MSG_DEBUG, " skip - SSID disallowed");
+ return NULL;
+ }
- ie = wpa_scan_get_ie(bss, WLAN_EID_RSN);
- rsn_ie_len = ie ? ie[1] : 0;
+ wpa = wpa_ie_len > 0 || rsn_ie_len > 0;
- wpa_printf(MSG_DEBUG, "%d: " MACSTR " ssid='%s' "
- "wpa_ie_len=%u rsn_ie_len=%u caps=0x%x",
- (int) i, MAC2STR(bss->bssid),
- wpa_ssid_txt(ssid_, ssid_len),
- wpa_ie_len, rsn_ie_len, bss->caps);
+ for (ssid = group; ssid; ssid = ssid->pnext) {
+ int check_ssid = wpa ? 1 : (ssid->ssid_len != 0);
+ int res;
- e = wpa_blacklist_get(wpa_s, bss->bssid);
- if (e && e->count > 1) {
- wpa_printf(MSG_DEBUG, " skip - blacklisted");
+ if (wpas_network_disabled(wpa_s, ssid)) {
+ wpa_dbg(wpa_s, MSG_DEBUG, " skip - disabled");
continue;
}
- if (ssid_len == 0) {
- wpa_printf(MSG_DEBUG, " skip - SSID not known");
+ res = wpas_temp_disabled(wpa_s, ssid);
+ if (res > 0) {
+ wpa_dbg(wpa_s, MSG_DEBUG, " skip - disabled "
+ "temporarily for %d second(s)", res);
continue;
}
- for (ssid = group; ssid; ssid = ssid->pnext) {
- int check_ssid = ssid->ssid_len != 0;
+#ifdef CONFIG_WPS
+ if ((ssid->key_mgmt & WPA_KEY_MGMT_WPS) && e && e->count > 0) {
+ wpa_dbg(wpa_s, MSG_DEBUG, " skip - blacklisted "
+ "(WPS)");
+ continue;
+ }
- if (ssid->disabled) {
- wpa_printf(MSG_DEBUG, " skip - disabled");
- continue;
- }
+ if (wpa && ssid->ssid_len == 0 &&
+ wpas_wps_ssid_wildcard_ok(wpa_s, ssid, bss))
+ check_ssid = 0;
-#ifdef CONFIG_WPS
- if (ssid->key_mgmt & WPA_KEY_MGMT_WPS) {
- /* Only allow wildcard SSID match if an AP
- * advertises active WPS operation that matches
- * with our mode. */
- check_ssid = 1;
- if (ssid->ssid_len == 0 &&
- wpas_wps_ssid_wildcard_ok(wpa_s, ssid,
- bss))
- check_ssid = 0;
- }
+ if (!wpa && (ssid->key_mgmt & WPA_KEY_MGMT_WPS)) {
+ /* Only allow wildcard SSID match if an AP
+ * advertises active WPS operation that matches
+ * with our mode. */
+ check_ssid = 1;
+ if (ssid->ssid_len == 0 &&
+ wpas_wps_ssid_wildcard_ok(wpa_s, ssid, bss))
+ check_ssid = 0;
+ }
#endif /* CONFIG_WPS */
- if (check_ssid &&
- (ssid_len != ssid->ssid_len ||
- os_memcmp(ssid_, ssid->ssid, ssid_len) != 0)) {
- wpa_printf(MSG_DEBUG, " skip - "
- "SSID mismatch");
- continue;
- }
+ if (ssid->bssid_set && ssid->ssid_len == 0 &&
+ os_memcmp(bss->bssid, ssid->bssid, ETH_ALEN) == 0)
+ check_ssid = 0;
- if (ssid->bssid_set &&
- os_memcmp(bss->bssid, ssid->bssid, ETH_ALEN) != 0)
- {
- wpa_printf(MSG_DEBUG, " skip - "
- "BSSID mismatch");
- continue;
- }
+ if (check_ssid &&
+ (bss->ssid_len != ssid->ssid_len ||
+ os_memcmp(bss->ssid, ssid->ssid, bss->ssid_len) != 0)) {
+ wpa_dbg(wpa_s, MSG_DEBUG, " skip - SSID mismatch");
+ continue;
+ }
- if (!(ssid->key_mgmt & WPA_KEY_MGMT_NONE) &&
- !(ssid->key_mgmt & WPA_KEY_MGMT_WPS) &&
- !(ssid->key_mgmt & WPA_KEY_MGMT_IEEE8021X_NO_WPA))
- {
- wpa_printf(MSG_DEBUG, " skip - "
- "non-WPA network not allowed");
- continue;
- }
+ if (ssid->bssid_set &&
+ os_memcmp(bss->bssid, ssid->bssid, ETH_ALEN) != 0) {
+ wpa_dbg(wpa_s, MSG_DEBUG, " skip - BSSID mismatch");
+ continue;
+ }
- if ((ssid->key_mgmt &
- (WPA_KEY_MGMT_IEEE8021X | WPA_KEY_MGMT_PSK |
- WPA_KEY_MGMT_FT_IEEE8021X | WPA_KEY_MGMT_FT_PSK |
- WPA_KEY_MGMT_IEEE8021X_SHA256 |
- WPA_KEY_MGMT_PSK_SHA256)) &&
- (wpa_ie_len != 0 || rsn_ie_len != 0)) {
- wpa_printf(MSG_DEBUG, " skip - "
- "WPA network");
- continue;
- }
+ if (!wpa_supplicant_ssid_bss_match(wpa_s, ssid, bss))
+ continue;
- if (!wpa_supplicant_match_privacy(bss, ssid)) {
- wpa_printf(MSG_DEBUG, " skip - "
- "privacy mismatch");
- continue;
- }
+ if (!wpa &&
+ !(ssid->key_mgmt & WPA_KEY_MGMT_NONE) &&
+ !(ssid->key_mgmt & WPA_KEY_MGMT_WPS) &&
+ !(ssid->key_mgmt & WPA_KEY_MGMT_IEEE8021X_NO_WPA)) {
+ wpa_dbg(wpa_s, MSG_DEBUG, " skip - non-WPA network "
+ "not allowed");
+ continue;
+ }
- if (bss->caps & IEEE80211_CAP_IBSS) {
- wpa_printf(MSG_DEBUG, " skip - "
- "IBSS (adhoc) network");
- continue;
- }
+ if (!wpa_supplicant_match_privacy(bss, ssid)) {
+ wpa_dbg(wpa_s, MSG_DEBUG, " skip - privacy "
+ "mismatch");
+ continue;
+ }
- if (!freq_allowed(ssid->freq_list, bss->freq)) {
- wpa_printf(MSG_DEBUG, " skip - "
- "frequency not allowed");
- continue;
- }
+ if (bss->caps & IEEE80211_CAP_IBSS) {
+ wpa_dbg(wpa_s, MSG_DEBUG, " skip - IBSS (adhoc) "
+ "network");
+ continue;
+ }
+
+ if (!freq_allowed(ssid->freq_list, bss->freq)) {
+ wpa_dbg(wpa_s, MSG_DEBUG, " skip - frequency not "
+ "allowed");
+ continue;
+ }
- wpa_printf(MSG_DEBUG, " selected non-WPA AP "
- MACSTR " ssid='%s'",
- MAC2STR(bss->bssid),
- wpa_ssid_txt(ssid_, ssid_len));
- *selected_ssid = ssid;
- return wpa_bss_get(wpa_s, bss->bssid, ssid_, ssid_len);
+ if (!rate_match(wpa_s, bss)) {
+ wpa_dbg(wpa_s, MSG_DEBUG, " skip - rate sets do "
+ "not match");
+ continue;
}
+
+#ifdef CONFIG_P2P
+ /*
+ * TODO: skip the AP if its P2P IE has Group Formation
+ * bit set in the P2P Group Capability Bitmap and we
+ * are not in Group Formation with that device.
+ */
+#endif /* CONFIG_P2P */
+
+ /* Matching configuration found */
+ return ssid;
}
+ /* No matching configuration found */
return NULL;
}
static struct wpa_bss *
wpa_supplicant_select_bss(struct wpa_supplicant *wpa_s,
- struct wpa_scan_results *scan_res,
struct wpa_ssid *group,
struct wpa_ssid **selected_ssid)
{
- struct wpa_bss *selected;
+ unsigned int i;
- wpa_printf(MSG_DEBUG, "Selecting BSS from priority group %d",
- group->priority);
+ wpa_dbg(wpa_s, MSG_DEBUG, "Selecting BSS from priority group %d",
+ group->priority);
- /* First, try to find WPA-enabled AP */
- selected = wpa_supplicant_select_bss_wpa(wpa_s, scan_res, group,
- selected_ssid);
- if (selected)
- return selected;
+ for (i = 0; i < wpa_s->last_scan_res_used; i++) {
+ struct wpa_bss *bss = wpa_s->last_scan_res[i];
+ *selected_ssid = wpa_scan_res_match(wpa_s, i, bss, group);
+ if (!*selected_ssid)
+ continue;
+ wpa_dbg(wpa_s, MSG_DEBUG, " selected BSS " MACSTR
+ " ssid='%s'",
+ MAC2STR(bss->bssid),
+ wpa_ssid_txt(bss->ssid, bss->ssid_len));
+ return bss;
+ }
- /* If no WPA-enabled AP found, try to find non-WPA AP, if configuration
- * allows this. */
- return wpa_supplicant_select_bss_non_wpa(wpa_s, scan_res, group,
- selected_ssid);
+ return NULL;
}
static struct wpa_bss *
wpa_supplicant_pick_network(struct wpa_supplicant *wpa_s,
- struct wpa_scan_results *scan_res,
struct wpa_ssid **selected_ssid)
{
struct wpa_bss *selected = NULL;
int prio;
+ if (wpa_s->last_scan_res == NULL ||
+ wpa_s->last_scan_res_used == 0)
+ return NULL; /* no scan results from last update */
+
while (selected == NULL) {
for (prio = 0; prio < wpa_s->conf->num_prio; prio++) {
selected = wpa_supplicant_select_bss(
- wpa_s, scan_res, wpa_s->conf->pssid[prio],
+ wpa_s, wpa_s->conf->pssid[prio],
selected_ssid);
if (selected)
break;
}
- if (selected == NULL && wpa_s->blacklist) {
- wpa_printf(MSG_DEBUG, "No APs found - clear blacklist "
- "and try again");
+ if (selected == NULL && wpa_s->blacklist &&
+ !wpa_s->countermeasures) {
+ wpa_dbg(wpa_s, MSG_DEBUG, "No APs found - clear "
+ "blacklist and try again");
wpa_blacklist_clear(wpa_s);
wpa_s->blacklist_cleared++;
} else if (selected == NULL)
@@ -700,28 +863,42 @@ wpa_supplicant_pick_network(struct wpa_supplicant *wpa_s,
static void wpa_supplicant_req_new_scan(struct wpa_supplicant *wpa_s,
int timeout_sec, int timeout_usec)
{
- if (!wpa_supplicant_enabled_networks(wpa_s->conf)) {
+ if (!wpa_supplicant_enabled_networks(wpa_s)) {
/*
* No networks are enabled; short-circuit request so
* we don't wait timeout seconds before transitioning
* to INACTIVE state.
*/
+ wpa_dbg(wpa_s, MSG_DEBUG, "Short-circuit new scan request "
+ "since there are no enabled networks");
wpa_supplicant_set_state(wpa_s, WPA_INACTIVE);
+#ifdef CONFIG_P2P
+ wpa_s->sta_scan_pending = 0;
+#endif /* CONFIG_P2P */
return;
}
+
+ wpa_s->scan_for_connection = 1;
wpa_supplicant_req_scan(wpa_s, timeout_sec, timeout_usec);
}
-void wpa_supplicant_connect(struct wpa_supplicant *wpa_s,
- struct wpa_bss *selected,
- struct wpa_ssid *ssid)
+int wpa_supplicant_connect(struct wpa_supplicant *wpa_s,
+ struct wpa_bss *selected,
+ struct wpa_ssid *ssid)
{
if (wpas_wps_scan_pbc_overlap(wpa_s, selected, ssid)) {
wpa_msg(wpa_s, MSG_INFO, WPS_EVENT_OVERLAP
"PBC session overlap");
- wpa_supplicant_req_new_scan(wpa_s, 10, 0);
- return;
+#ifdef CONFIG_P2P
+ if (wpas_p2p_notif_pbc_overlap(wpa_s) == 1)
+ return -1;
+#endif /* CONFIG_P2P */
+
+#ifdef CONFIG_WPS
+ wpas_wps_cancel(wpa_s);
+#endif /* CONFIG_WPS */
+ return -1;
}
/*
@@ -731,18 +908,27 @@ void wpa_supplicant_connect(struct wpa_supplicant *wpa_s,
*/
if (wpa_s->reassociate ||
(os_memcmp(selected->bssid, wpa_s->bssid, ETH_ALEN) != 0 &&
- (wpa_s->wpa_state != WPA_ASSOCIATING ||
+ ((wpa_s->wpa_state != WPA_ASSOCIATING &&
+ wpa_s->wpa_state != WPA_AUTHENTICATING) ||
os_memcmp(selected->bssid, wpa_s->pending_bssid, ETH_ALEN) !=
0))) {
if (wpa_supplicant_scard_init(wpa_s, ssid)) {
wpa_supplicant_req_new_scan(wpa_s, 10, 0);
- return;
+ return 0;
}
+ wpa_msg(wpa_s, MSG_DEBUG, "Request association: "
+ "reassociate: %d selected: "MACSTR " bssid: " MACSTR
+ " pending: " MACSTR " wpa_state: %s",
+ wpa_s->reassociate, MAC2STR(selected->bssid),
+ MAC2STR(wpa_s->bssid), MAC2STR(wpa_s->pending_bssid),
+ wpa_supplicant_state_txt(wpa_s->wpa_state));
wpa_supplicant_associate(wpa_s, selected, ssid);
} else {
- wpa_printf(MSG_DEBUG, "Already associated with the selected "
- "AP");
+ wpa_dbg(wpa_s, MSG_DEBUG, "Already associated with the "
+ "selected AP");
}
+
+ return 0;
}
@@ -755,7 +941,7 @@ wpa_supplicant_pick_new_network(struct wpa_supplicant *wpa_s)
for (prio = 0; prio < wpa_s->conf->num_prio; prio++) {
for (ssid = wpa_s->conf->pssid[prio]; ssid; ssid = ssid->pnext)
{
- if (ssid->disabled)
+ if (wpas_network_disabled(wpa_s, ssid))
continue;
if (ssid->mode == IEEE80211_MODE_IBSS ||
ssid->mode == IEEE80211_MODE_AP)
@@ -769,28 +955,25 @@ wpa_supplicant_pick_new_network(struct wpa_supplicant *wpa_s)
/* TODO: move the rsn_preauth_scan_result*() to be called from notify.c based
* on BSS added and BSS changed events */
static void wpa_supplicant_rsn_preauth_scan_results(
- struct wpa_supplicant *wpa_s, struct wpa_scan_results *scan_res)
+ struct wpa_supplicant *wpa_s)
{
- int i;
+ struct wpa_bss *bss;
if (rsn_preauth_scan_results(wpa_s->wpa) < 0)
return;
- for (i = scan_res->num - 1; i >= 0; i--) {
+ dl_list_for_each(bss, &wpa_s->bss, struct wpa_bss, list) {
const u8 *ssid, *rsn;
- struct wpa_scan_res *r;
- r = scan_res->res[i];
-
- ssid = wpa_scan_get_ie(r, WLAN_EID_SSID);
+ ssid = wpa_bss_get_ie(bss, WLAN_EID_SSID);
if (ssid == NULL)
continue;
- rsn = wpa_scan_get_ie(r, WLAN_EID_RSN);
+ rsn = wpa_bss_get_ie(bss, WLAN_EID_RSN);
if (rsn == NULL)
continue;
- rsn_preauth_scan_result(wpa_s->wpa, r->bssid, ssid, rsn);
+ rsn_preauth_scan_result(wpa_s->wpa, bss->bssid, ssid, rsn);
}
}
@@ -798,11 +981,9 @@ static void wpa_supplicant_rsn_preauth_scan_results(
static int wpa_supplicant_need_to_roam(struct wpa_supplicant *wpa_s,
struct wpa_bss *selected,
- struct wpa_ssid *ssid,
- struct wpa_scan_results *scan_res)
+ struct wpa_ssid *ssid)
{
- size_t i;
- struct wpa_scan_res *current_bss = NULL;
+ struct wpa_bss *current_bss = NULL;
int min_diff;
if (wpa_s->reassociate)
@@ -814,39 +995,46 @@ static int wpa_supplicant_need_to_roam(struct wpa_supplicant *wpa_s,
if (wpa_s->current_ssid != ssid)
return 1; /* different network block */
- for (i = 0; i < scan_res->num; i++) {
- struct wpa_scan_res *res = scan_res->res[i];
- const u8 *ie;
- if (os_memcmp(res->bssid, wpa_s->bssid, ETH_ALEN) != 0)
- continue;
+ if (wpas_driver_bss_selection(wpa_s))
+ return 0; /* Driver-based roaming */
- ie = wpa_scan_get_ie(res, WLAN_EID_SSID);
- if (ie == NULL)
- continue;
- if (ie[1] != wpa_s->current_ssid->ssid_len ||
- os_memcmp(ie + 2, wpa_s->current_ssid->ssid, ie[1]) != 0)
- continue;
- current_bss = res;
- break;
- }
+ if (wpa_s->current_ssid->ssid)
+ current_bss = wpa_bss_get(wpa_s, wpa_s->bssid,
+ wpa_s->current_ssid->ssid,
+ wpa_s->current_ssid->ssid_len);
+ if (!current_bss)
+ current_bss = wpa_bss_get_bssid(wpa_s, wpa_s->bssid);
if (!current_bss)
return 1; /* current BSS not seen in scan results */
- wpa_printf(MSG_DEBUG, "Considering within-ESS reassociation");
- wpa_printf(MSG_DEBUG, "Current BSS: " MACSTR " level=%d",
- MAC2STR(current_bss->bssid), current_bss->level);
- wpa_printf(MSG_DEBUG, "Selected BSS: " MACSTR " level=%d",
- MAC2STR(selected->bssid), selected->level);
+ if (current_bss == selected)
+ return 0;
+
+ if (selected->last_update_idx > current_bss->last_update_idx)
+ return 1; /* current BSS not seen in the last scan */
+
+#ifndef CONFIG_NO_ROAMING
+ wpa_dbg(wpa_s, MSG_DEBUG, "Considering within-ESS reassociation");
+ wpa_dbg(wpa_s, MSG_DEBUG, "Current BSS: " MACSTR " level=%d",
+ MAC2STR(current_bss->bssid), current_bss->level);
+ wpa_dbg(wpa_s, MSG_DEBUG, "Selected BSS: " MACSTR " level=%d",
+ MAC2STR(selected->bssid), selected->level);
if (wpa_s->current_ssid->bssid_set &&
os_memcmp(selected->bssid, wpa_s->current_ssid->bssid, ETH_ALEN) ==
0) {
- wpa_printf(MSG_DEBUG, "Allow reassociation - selected BSS has "
- "preferred BSSID");
+ wpa_dbg(wpa_s, MSG_DEBUG, "Allow reassociation - selected BSS "
+ "has preferred BSSID");
return 1;
}
+ if (current_bss->level < 0 && current_bss->level > selected->level) {
+ wpa_dbg(wpa_s, MSG_DEBUG, "Skip roam - Current BSS has better "
+ "signal level");
+ return 0;
+ }
+
min_diff = 2;
if (current_bss->level < 0) {
if (current_bss->level < -85)
@@ -861,22 +1049,28 @@ static int wpa_supplicant_need_to_roam(struct wpa_supplicant *wpa_s,
min_diff = 5;
}
if (abs(current_bss->level - selected->level) < min_diff) {
- wpa_printf(MSG_DEBUG, "Skip roam - too small difference in "
- "signal level");
+ wpa_dbg(wpa_s, MSG_DEBUG, "Skip roam - too small difference "
+ "in signal level");
return 0;
}
return 1;
+#else /* CONFIG_NO_ROAMING */
+ return 0;
+#endif /* CONFIG_NO_ROAMING */
}
-static void wpa_supplicant_event_scan_results(struct wpa_supplicant *wpa_s,
+/* Return != 0 if no scan results could be fetched or if scan results should not
+ * be shared with other virtual interfaces. */
+static int _wpa_supplicant_event_scan_results(struct wpa_supplicant *wpa_s,
union wpa_event_data *data)
{
- struct wpa_bss *selected;
- struct wpa_ssid *ssid = NULL;
struct wpa_scan_results *scan_res;
int ap = 0;
+#ifndef CONFIG_NO_RANDOM_POOL
+ size_t i, num;
+#endif /* CONFIG_NO_RANDOM_POOL */
#ifdef CONFIG_AP
if (wpa_s->ap_iface)
@@ -885,102 +1079,343 @@ static void wpa_supplicant_event_scan_results(struct wpa_supplicant *wpa_s,
wpa_supplicant_notify_scanning(wpa_s, 0);
+#ifdef CONFIG_P2P
+ if (wpa_s->global->p2p_cb_on_scan_complete &&
+ !wpa_s->global->p2p_disabled &&
+ wpa_s->global->p2p != NULL && !wpa_s->sta_scan_pending &&
+ !wpa_s->scan_res_handler) {
+ wpa_s->global->p2p_cb_on_scan_complete = 0;
+ if (p2p_other_scan_completed(wpa_s->global->p2p) == 1) {
+ wpa_dbg(wpa_s, MSG_DEBUG, "P2P: Pending P2P operation "
+ "stopped scan processing");
+ wpa_s->sta_scan_pending = 1;
+ wpa_supplicant_req_scan(wpa_s, 5, 0);
+ return -1;
+ }
+ }
+ wpa_s->sta_scan_pending = 0;
+#endif /* CONFIG_P2P */
+
scan_res = wpa_supplicant_get_scan_results(wpa_s,
data ? &data->scan_info :
NULL, 1);
if (scan_res == NULL) {
if (wpa_s->conf->ap_scan == 2 || ap)
- return;
- wpa_printf(MSG_DEBUG, "Failed to get scan results - try "
- "scanning again");
+ return -1;
+ wpa_dbg(wpa_s, MSG_DEBUG, "Failed to get scan results - try "
+ "scanning again");
wpa_supplicant_req_new_scan(wpa_s, 1, 0);
- return;
+ return -1;
}
+#ifndef CONFIG_NO_RANDOM_POOL
+ num = scan_res->num;
+ if (num > 10)
+ num = 10;
+ for (i = 0; i < num; i++) {
+ u8 buf[5];
+ struct wpa_scan_res *res = scan_res->res[i];
+ buf[0] = res->bssid[5];
+ buf[1] = res->qual & 0xff;
+ buf[2] = res->noise & 0xff;
+ buf[3] = res->level & 0xff;
+ buf[4] = res->tsf & 0xff;
+ random_add_randomness(buf, sizeof(buf));
+ }
+#endif /* CONFIG_NO_RANDOM_POOL */
+
if (wpa_s->scan_res_handler) {
- wpa_s->scan_res_handler(wpa_s, scan_res);
+ void (*scan_res_handler)(struct wpa_supplicant *wpa_s,
+ struct wpa_scan_results *scan_res);
+
+ scan_res_handler = wpa_s->scan_res_handler;
wpa_s->scan_res_handler = NULL;
+ scan_res_handler(wpa_s, scan_res);
+
wpa_scan_results_free(scan_res);
- return;
+ return -2;
}
if (ap) {
- wpa_printf(MSG_DEBUG, "Ignore scan results in AP mode");
+ wpa_dbg(wpa_s, MSG_DEBUG, "Ignore scan results in AP mode");
+#ifdef CONFIG_AP
+ if (wpa_s->ap_iface->scan_cb)
+ wpa_s->ap_iface->scan_cb(wpa_s->ap_iface);
+#endif /* CONFIG_AP */
wpa_scan_results_free(scan_res);
- return;
+ return 0;
}
- wpa_printf(MSG_DEBUG, "New scan results available");
+ wpa_dbg(wpa_s, MSG_DEBUG, "New scan results available");
wpa_msg_ctrl(wpa_s, MSG_INFO, WPA_EVENT_SCAN_RESULTS);
wpas_notify_scan_results(wpa_s);
wpas_notify_scan_done(wpa_s, 1);
+ if (sme_proc_obss_scan(wpa_s) > 0) {
+ wpa_scan_results_free(scan_res);
+ return 0;
+ }
+
if ((wpa_s->conf->ap_scan == 2 && !wpas_wps_searching(wpa_s))) {
wpa_scan_results_free(scan_res);
- return;
+ return 0;
+ }
+
+ if (autoscan_notify_scan(wpa_s, scan_res)) {
+ wpa_scan_results_free(scan_res);
+ return 0;
}
if (wpa_s->disconnected) {
wpa_supplicant_set_state(wpa_s, WPA_DISCONNECTED);
wpa_scan_results_free(scan_res);
- return;
+ return 0;
}
- if (bgscan_notify_scan(wpa_s) == 1) {
+ if (!wpas_driver_bss_selection(wpa_s) &&
+ bgscan_notify_scan(wpa_s, scan_res) == 1) {
wpa_scan_results_free(scan_res);
- return;
+ return 0;
}
- wpa_supplicant_rsn_preauth_scan_results(wpa_s, scan_res);
+ wpas_wps_update_ap_info(wpa_s, scan_res);
- selected = wpa_supplicant_pick_network(wpa_s, scan_res, &ssid);
+ wpa_scan_results_free(scan_res);
+
+ return wpas_select_network_from_last_scan(wpa_s);
+}
+
+
+int wpas_select_network_from_last_scan(struct wpa_supplicant *wpa_s)
+{
+ struct wpa_bss *selected;
+ struct wpa_ssid *ssid = NULL;
+
+ selected = wpa_supplicant_pick_network(wpa_s, &ssid);
if (selected) {
int skip;
- skip = !wpa_supplicant_need_to_roam(wpa_s, selected, ssid,
- scan_res);
- wpa_scan_results_free(scan_res);
- if (skip)
- return;
- wpa_supplicant_connect(wpa_s, selected, ssid);
+ skip = !wpa_supplicant_need_to_roam(wpa_s, selected, ssid);
+ if (skip) {
+ wpa_supplicant_rsn_preauth_scan_results(wpa_s);
+ return 0;
+ }
+
+ if (wpa_supplicant_connect(wpa_s, selected, ssid) < 0) {
+ wpa_dbg(wpa_s, MSG_DEBUG, "Connect failed");
+ return -1;
+ }
+ wpa_supplicant_rsn_preauth_scan_results(wpa_s);
+ /*
+ * Do not notify other virtual radios of scan results since we do not
+ * want them to start other associations at the same time.
+ */
+ return 1;
} else {
- wpa_scan_results_free(scan_res);
- wpa_printf(MSG_DEBUG, "No suitable network found");
+ wpa_dbg(wpa_s, MSG_DEBUG, "No suitable network found");
ssid = wpa_supplicant_pick_new_network(wpa_s);
if (ssid) {
- wpa_printf(MSG_DEBUG, "Setup a new network");
+ wpa_dbg(wpa_s, MSG_DEBUG, "Setup a new network");
wpa_supplicant_associate(wpa_s, NULL, ssid);
+ wpa_supplicant_rsn_preauth_scan_results(wpa_s);
} else {
- int timeout_sec = 5;
+ int timeout_sec = wpa_s->scan_interval;
int timeout_usec = 0;
- wpa_supplicant_req_new_scan(wpa_s, timeout_sec,
- timeout_usec);
+#ifdef CONFIG_P2P
+ if (wpas_p2p_scan_no_go_seen(wpa_s) == 1)
+ return 0;
+
+ if (wpa_s->p2p_in_provisioning) {
+ /*
+ * Use shorter wait during P2P Provisioning
+ * state to speed up group formation.
+ */
+ timeout_sec = 0;
+ timeout_usec = 250000;
+ wpa_supplicant_req_new_scan(wpa_s, timeout_sec,
+ timeout_usec);
+ return 0;
+ }
+#endif /* CONFIG_P2P */
+#ifdef CONFIG_INTERWORKING
+ if (wpa_s->conf->auto_interworking &&
+ wpa_s->conf->interworking &&
+ wpa_s->conf->cred) {
+ wpa_dbg(wpa_s, MSG_DEBUG, "Interworking: "
+ "start ANQP fetch since no matching "
+ "networks found");
+ wpa_s->network_select = 1;
+ wpa_s->auto_network_select = 1;
+ interworking_start_fetch_anqp(wpa_s);
+ return 1;
+ }
+#endif /* CONFIG_INTERWORKING */
+ if (wpa_supplicant_req_sched_scan(wpa_s))
+ wpa_supplicant_req_new_scan(wpa_s, timeout_sec,
+ timeout_usec);
+ }
+ }
+ return 0;
+}
+
+
+static void wpa_supplicant_event_scan_results(struct wpa_supplicant *wpa_s,
+ union wpa_event_data *data)
+{
+ const char *rn, *rn2;
+ struct wpa_supplicant *ifs;
+
+ if (_wpa_supplicant_event_scan_results(wpa_s, data) != 0) {
+ /*
+ * If no scan results could be fetched, then no need to
+ * notify those interfaces that did not actually request
+ * this scan. Similarly, if scan results started a new operation on this
+ * interface, do not notify other interfaces to avoid concurrent
+ * operations during a connection attempt.
+ */
+ return;
+ }
+
+ /*
+ * Check other interfaces to see if they have the same radio-name. If
+ * so, they get updated with this same scan info.
+ */
+ if (!wpa_s->driver->get_radio_name)
+ return;
+
+ rn = wpa_s->driver->get_radio_name(wpa_s->drv_priv);
+ if (rn == NULL || rn[0] == '\0')
+ return;
+
+ wpa_dbg(wpa_s, MSG_DEBUG, "Checking for other virtual interfaces "
+ "sharing same radio (%s) in event_scan_results", rn);
+
+ for (ifs = wpa_s->global->ifaces; ifs; ifs = ifs->next) {
+ if (ifs == wpa_s || !ifs->driver->get_radio_name)
+ continue;
+
+ rn2 = ifs->driver->get_radio_name(ifs->drv_priv);
+ if (rn2 && os_strcmp(rn, rn2) == 0) {
+ wpa_printf(MSG_DEBUG, "%s: Updating scan results from "
+ "sibling", ifs->ifname);
+ _wpa_supplicant_event_scan_results(ifs, data);
}
}
}
+
#endif /* CONFIG_NO_SCAN_PROCESSING */
+#ifdef CONFIG_WNM
+
+static void wnm_bss_keep_alive(void *eloop_ctx, void *sock_ctx)
+{
+ struct wpa_supplicant *wpa_s = eloop_ctx;
+
+ if (wpa_s->wpa_state < WPA_ASSOCIATED)
+ return;
+
+ if (!wpa_s->no_keep_alive) {
+ wpa_printf(MSG_DEBUG, "WNM: Send keep-alive to AP " MACSTR,
+ MAC2STR(wpa_s->bssid));
+ /* TODO: could skip this if normal data traffic has been sent */
+ /* TODO: Consider using some more appropriate data frame for
+ * this */
+ if (wpa_s->l2)
+ l2_packet_send(wpa_s->l2, wpa_s->bssid, 0x0800,
+ (u8 *) "", 0);
+ }
+
+#ifdef CONFIG_SME
+ if (wpa_s->sme.bss_max_idle_period) {
+ unsigned int msec;
+ msec = wpa_s->sme.bss_max_idle_period * 1024; /* times 1000 */
+ if (msec > 100)
+ msec -= 100;
+ eloop_register_timeout(msec / 1000, msec % 1000 * 1000,
+ wnm_bss_keep_alive, wpa_s, NULL);
+ }
+#endif /* CONFIG_SME */
+}
+
+
+static void wnm_process_assoc_resp(struct wpa_supplicant *wpa_s,
+ const u8 *ies, size_t ies_len)
+{
+ struct ieee802_11_elems elems;
+
+ if (ies == NULL)
+ return;
+
+ if (ieee802_11_parse_elems(ies, ies_len, &elems, 1) == ParseFailed)
+ return;
+
+#ifdef CONFIG_SME
+ if (elems.bss_max_idle_period) {
+ unsigned int msec;
+ wpa_s->sme.bss_max_idle_period =
+ WPA_GET_LE16(elems.bss_max_idle_period);
+ wpa_printf(MSG_DEBUG, "WNM: BSS Max Idle Period: %u (* 1000 "
+ "TU)%s", wpa_s->sme.bss_max_idle_period,
+ (elems.bss_max_idle_period[2] & 0x01) ?
+ " (protected keep-live required)" : "");
+ if (wpa_s->sme.bss_max_idle_period == 0)
+ wpa_s->sme.bss_max_idle_period = 1;
+ if (wpa_s->drv_flags & WPA_DRIVER_FLAGS_SME) {
+ eloop_cancel_timeout(wnm_bss_keep_alive, wpa_s, NULL);
+ /* msec times 1000 */
+ msec = wpa_s->sme.bss_max_idle_period * 1024;
+ if (msec > 100)
+ msec -= 100;
+ eloop_register_timeout(msec / 1000, msec % 1000 * 1000,
+ wnm_bss_keep_alive, wpa_s,
+ NULL);
+ }
+ }
+#endif /* CONFIG_SME */
+}
+
+#endif /* CONFIG_WNM */
+
+
+void wnm_bss_keep_alive_deinit(struct wpa_supplicant *wpa_s)
+{
+#ifdef CONFIG_WNM
+ eloop_cancel_timeout(wnm_bss_keep_alive, wpa_s, NULL);
+#endif /* CONFIG_WNM */
+}
+
+
static int wpa_supplicant_event_associnfo(struct wpa_supplicant *wpa_s,
union wpa_event_data *data)
{
int l, len, found = 0, wpa_found, rsn_found;
const u8 *p;
- wpa_printf(MSG_DEBUG, "Association info event");
+ wpa_dbg(wpa_s, MSG_DEBUG, "Association info event");
if (data->assoc_info.req_ies)
wpa_hexdump(MSG_DEBUG, "req_ies", data->assoc_info.req_ies,
data->assoc_info.req_ies_len);
- if (data->assoc_info.resp_ies)
+ if (data->assoc_info.resp_ies) {
wpa_hexdump(MSG_DEBUG, "resp_ies", data->assoc_info.resp_ies,
data->assoc_info.resp_ies_len);
+#ifdef CONFIG_TDLS
+ wpa_tdls_assoc_resp_ies(wpa_s->wpa, data->assoc_info.resp_ies,
+ data->assoc_info.resp_ies_len);
+#endif /* CONFIG_TDLS */
+#ifdef CONFIG_WNM
+ wnm_process_assoc_resp(wpa_s, data->assoc_info.resp_ies,
+ data->assoc_info.resp_ies_len);
+#endif /* CONFIG_WNM */
+ }
if (data->assoc_info.beacon_ies)
wpa_hexdump(MSG_DEBUG, "beacon_ies",
data->assoc_info.beacon_ies,
data->assoc_info.beacon_ies_len);
if (data->assoc_info.freq)
- wpa_printf(MSG_DEBUG, "freq=%u MHz", data->assoc_info.freq);
+ wpa_dbg(wpa_s, MSG_DEBUG, "freq=%u MHz",
+ data->assoc_info.freq);
p = data->assoc_info.req_ies;
l = data->assoc_info.req_ies_len;
@@ -1017,8 +1452,8 @@ static int wpa_supplicant_event_associnfo(struct wpa_supplicant *wpa_s,
data->assoc_info.resp_ies,
data->assoc_info.resp_ies_len,
bssid) < 0) {
- wpa_printf(MSG_DEBUG, "FT: Validation of "
- "Reassociation Response failed");
+ wpa_dbg(wpa_s, MSG_DEBUG, "FT: Validation of "
+ "Reassociation Response failed");
wpa_supplicant_deauthenticate(
wpa_s, WLAN_REASON_INVALID_IE);
return -1;
@@ -1028,6 +1463,27 @@ static int wpa_supplicant_event_associnfo(struct wpa_supplicant *wpa_s,
p = data->assoc_info.resp_ies;
l = data->assoc_info.resp_ies_len;
+#ifdef CONFIG_WPS_STRICT
+ if (p && wpa_s->current_ssid &&
+ wpa_s->current_ssid->key_mgmt == WPA_KEY_MGMT_WPS) {
+ struct wpabuf *wps;
+ wps = ieee802_11_vendor_ie_concat(p, l, WPS_IE_VENDOR_TYPE);
+ if (wps == NULL) {
+ wpa_msg(wpa_s, MSG_INFO, "WPS-STRICT: AP did not "
+ "include WPS IE in (Re)Association Response");
+ return -1;
+ }
+
+ if (wps_validate_assoc_resp(wps) < 0) {
+ wpabuf_free(wps);
+ wpa_supplicant_deauthenticate(
+ wpa_s, WLAN_REASON_INVALID_IE);
+ return -1;
+ }
+ wpabuf_free(wps);
+ }
+#endif /* CONFIG_WPS_STRICT */
+
/* Go through the IEs and make a copy of the MDIE, if present. */
while (p && l >= 2) {
len = p[1] + 2;
@@ -1090,18 +1546,64 @@ static int wpa_supplicant_event_associnfo(struct wpa_supplicant *wpa_s,
if (wpa_found || rsn_found)
wpa_s->ap_ies_from_associnfo = 1;
+ if (wpa_s->assoc_freq && data->assoc_info.freq &&
+ wpa_s->assoc_freq != data->assoc_info.freq) {
+ wpa_printf(MSG_DEBUG, "Operating frequency changed from "
+ "%u to %u MHz",
+ wpa_s->assoc_freq, data->assoc_info.freq);
+ wpa_supplicant_update_scan_results(wpa_s);
+ }
+
wpa_s->assoc_freq = data->assoc_info.freq;
return 0;
}
+static struct wpa_bss * wpa_supplicant_get_new_bss(
+ struct wpa_supplicant *wpa_s, const u8 *bssid)
+{
+ struct wpa_bss *bss = NULL;
+ struct wpa_ssid *ssid = wpa_s->current_ssid;
+
+ if (ssid->ssid_len > 0)
+ bss = wpa_bss_get(wpa_s, bssid, ssid->ssid, ssid->ssid_len);
+ if (!bss)
+ bss = wpa_bss_get_bssid(wpa_s, bssid);
+
+ return bss;
+}
+
+
+static int wpa_supplicant_assoc_update_ie(struct wpa_supplicant *wpa_s)
+{
+ const u8 *bss_wpa = NULL, *bss_rsn = NULL;
+
+ if (!wpa_s->current_bss || !wpa_s->current_ssid)
+ return -1;
+
+ if (!wpa_key_mgmt_wpa_any(wpa_s->current_ssid->key_mgmt))
+ return 0;
+
+ bss_wpa = wpa_bss_get_vendor_ie(wpa_s->current_bss,
+ WPA_IE_VENDOR_TYPE);
+ bss_rsn = wpa_bss_get_ie(wpa_s->current_bss, WLAN_EID_RSN);
+
+ if (wpa_sm_set_ap_wpa_ie(wpa_s->wpa, bss_wpa,
+ bss_wpa ? 2 + bss_wpa[1] : 0) ||
+ wpa_sm_set_ap_rsn_ie(wpa_s->wpa, bss_rsn,
+ bss_rsn ? 2 + bss_rsn[1] : 0))
+ return -1;
+
+ return 0;
+}
+
+
static void wpa_supplicant_event_assoc(struct wpa_supplicant *wpa_s,
union wpa_event_data *data)
{
u8 bssid[ETH_ALEN];
int ft_completed;
- int bssid_changed;
struct wpa_driver_capa capa;
#ifdef CONFIG_AP
@@ -1109,7 +1611,8 @@ static void wpa_supplicant_event_assoc(struct wpa_supplicant *wpa_s,
hostapd_notif_assoc(wpa_s->ap_iface->bss[0],
data->assoc_info.addr,
data->assoc_info.req_ies,
- data->assoc_info.req_ies_len);
+ data->assoc_info.req_ies_len,
+ data->assoc_info.reassoc);
return;
}
#endif /* CONFIG_AP */
@@ -1118,39 +1621,51 @@ static void wpa_supplicant_event_assoc(struct wpa_supplicant *wpa_s,
if (data && wpa_supplicant_event_associnfo(wpa_s, data) < 0)
return;
+ if (wpa_drv_get_bssid(wpa_s, bssid) < 0) {
+ wpa_dbg(wpa_s, MSG_ERROR, "Failed to get BSSID");
+ wpa_supplicant_deauthenticate(
+ wpa_s, WLAN_REASON_DEAUTH_LEAVING);
+ return;
+ }
+
wpa_supplicant_set_state(wpa_s, WPA_ASSOCIATED);
- if (wpa_s->drv_flags & WPA_DRIVER_FLAGS_USER_SPACE_MLME)
- os_memcpy(bssid, wpa_s->bssid, ETH_ALEN);
- if ((wpa_s->drv_flags & WPA_DRIVER_FLAGS_USER_SPACE_MLME) ||
- (wpa_drv_get_bssid(wpa_s, bssid) >= 0 &&
- os_memcmp(bssid, wpa_s->bssid, ETH_ALEN) != 0)) {
- wpa_msg(wpa_s, MSG_DEBUG, "Associated to a new BSS: BSSID="
+ if (os_memcmp(bssid, wpa_s->bssid, ETH_ALEN) != 0) {
+ wpa_dbg(wpa_s, MSG_DEBUG, "Associated to a new BSS: BSSID="
MACSTR, MAC2STR(bssid));
- bssid_changed = os_memcmp(wpa_s->bssid, bssid, ETH_ALEN);
+ random_add_randomness(bssid, ETH_ALEN);
os_memcpy(wpa_s->bssid, bssid, ETH_ALEN);
os_memset(wpa_s->pending_bssid, 0, ETH_ALEN);
- if (bssid_changed)
- wpas_notify_bssid_changed(wpa_s);
+ wpas_notify_bssid_changed(wpa_s);
if (wpa_supplicant_dynamic_keys(wpa_s) && !ft_completed) {
wpa_clear_keys(wpa_s, bssid);
}
if (wpa_supplicant_select_config(wpa_s) < 0) {
- wpa_supplicant_disassociate(
+ wpa_supplicant_deauthenticate(
wpa_s, WLAN_REASON_DEAUTH_LEAVING);
return;
}
if (wpa_s->current_ssid) {
struct wpa_bss *bss = NULL;
- struct wpa_ssid *ssid = wpa_s->current_ssid;
- if (ssid->ssid_len > 0)
- bss = wpa_bss_get(wpa_s, bssid,
- ssid->ssid, ssid->ssid_len);
- if (!bss)
- bss = wpa_bss_get_bssid(wpa_s, bssid);
+
+ bss = wpa_supplicant_get_new_bss(wpa_s, bssid);
+ if (!bss) {
+ wpa_supplicant_update_scan_results(wpa_s);
+
+ /* Get the BSS from the new scan results */
+ bss = wpa_supplicant_get_new_bss(wpa_s, bssid);
+ }
+
if (bss)
wpa_s->current_bss = bss;
}
+
+ if (wpa_s->conf->ap_scan == 1 &&
+ wpa_s->drv_flags & WPA_DRIVER_FLAGS_BSS_SELECTION) {
+ if (wpa_supplicant_assoc_update_ie(wpa_s) < 0)
+ wpa_msg(wpa_s, MSG_WARNING,
+ "WPA/RSN IEs not updated");
+ }
}
#ifdef CONFIG_SME
@@ -1209,8 +1724,27 @@ static void wpa_supplicant_event_assoc(struct wpa_supplicant *wpa_s,
wpa_supplicant_set_state(wpa_s, WPA_COMPLETED);
eapol_sm_notify_portValid(wpa_s->eapol, TRUE);
eapol_sm_notify_eap_success(wpa_s->eapol, TRUE);
+ } else if ((wpa_s->drv_flags & WPA_DRIVER_FLAGS_4WAY_HANDSHAKE) &&
+ wpa_key_mgmt_wpa_ieee8021x(wpa_s->key_mgmt)) {
+ /*
+ * The driver will take care of RSN 4-way handshake, so we need
+ * to allow EAPOL supplicant to complete its work without
+ * waiting for WPA supplicant.
+ */
+ eapol_sm_notify_portValid(wpa_s->eapol, TRUE);
+ } else if (ft_completed) {
+ /*
+ * FT protocol completed - make sure EAPOL state machine ends
+ * up in authenticated.
+ */
+ wpa_supplicant_cancel_auth_timeout(wpa_s);
+ wpa_supplicant_set_state(wpa_s, WPA_COMPLETED);
+ eapol_sm_notify_portValid(wpa_s->eapol, TRUE);
+ eapol_sm_notify_eap_success(wpa_s->eapol, TRUE);
}
+ wpa_s->last_eapol_matches_bssid = 0;
+
if (wpa_s->pending_eapol_rx) {
struct os_time now, age;
os_get_time(&now);
@@ -1218,9 +1752,9 @@ static void wpa_supplicant_event_assoc(struct wpa_supplicant *wpa_s,
if (age.sec == 0 && age.usec < 100000 &&
os_memcmp(wpa_s->pending_eapol_rx_src, bssid, ETH_ALEN) ==
0) {
- wpa_printf(MSG_DEBUG, "Process pending EAPOL frame "
- "that was received just before association "
- "notification");
+ wpa_dbg(wpa_s, MSG_DEBUG, "Process pending EAPOL "
+ "frame that was received just before "
+ "association notification");
wpa_supplicant_rx_eapol(
wpa_s, wpa_s->pending_eapol_rx_src,
wpabuf_head(wpa_s->pending_eapol_rx),
@@ -1230,25 +1764,6 @@ static void wpa_supplicant_event_assoc(struct wpa_supplicant *wpa_s,
wpa_s->pending_eapol_rx = NULL;
}
-#ifdef CONFIG_BGSCAN
- if (wpa_s->current_ssid != wpa_s->bgscan_ssid) {
- bgscan_deinit(wpa_s);
- if (wpa_s->current_ssid && wpa_s->current_ssid->bgscan) {
- if (bgscan_init(wpa_s, wpa_s->current_ssid)) {
- wpa_printf(MSG_DEBUG, "Failed to initialize "
- "bgscan");
- /*
- * Live without bgscan; it is only used as a
- * roaming optimization, so the initial
- * connection is not affected.
- */
- } else
- wpa_s->bgscan_ssid = wpa_s->current_ssid;
- } else
- wpa_s->bgscan_ssid = NULL;
- }
-#endif /* CONFIG_BGSCAN */
-
if ((wpa_s->key_mgmt == WPA_KEY_MGMT_NONE ||
wpa_s->key_mgmt == WPA_KEY_MGMT_IEEE8021X_NO_WPA) &&
wpa_s->current_ssid && wpa_drv_get_capa(wpa_s, &capa) == 0 &&
@@ -1256,20 +1771,101 @@ static void wpa_supplicant_event_assoc(struct wpa_supplicant *wpa_s,
/* Set static WEP keys again */
wpa_set_wep_keys(wpa_s, wpa_s->current_ssid);
}
+
+#ifdef CONFIG_IBSS_RSN
+ if (wpa_s->current_ssid &&
+ wpa_s->current_ssid->mode == WPAS_MODE_IBSS &&
+ wpa_s->key_mgmt != WPA_KEY_MGMT_NONE &&
+ wpa_s->key_mgmt != WPA_KEY_MGMT_WPA_NONE &&
+ wpa_s->ibss_rsn == NULL) {
+ wpa_s->ibss_rsn = ibss_rsn_init(wpa_s);
+ if (!wpa_s->ibss_rsn) {
+ wpa_msg(wpa_s, MSG_INFO, "Failed to init IBSS RSN");
+ wpa_supplicant_deauthenticate(
+ wpa_s, WLAN_REASON_DEAUTH_LEAVING);
+ return;
+ }
+
+ ibss_rsn_set_psk(wpa_s->ibss_rsn, wpa_s->current_ssid->psk);
+ }
+#endif /* CONFIG_IBSS_RSN */
+
+ wpas_wps_notify_assoc(wpa_s, bssid);
+}
+
+
+static int disconnect_reason_recoverable(u16 reason_code)
+{
+ return reason_code == WLAN_REASON_DISASSOC_DUE_TO_INACTIVITY ||
+ reason_code == WLAN_REASON_CLASS2_FRAME_FROM_NONAUTH_STA ||
+ reason_code == WLAN_REASON_CLASS3_FRAME_FROM_NONASSOC_STA;
}
static void wpa_supplicant_event_disassoc(struct wpa_supplicant *wpa_s,
- u16 reason_code)
+ u16 reason_code,
+ int locally_generated)
+{
+ const u8 *bssid;
+
+ if (wpa_s->key_mgmt == WPA_KEY_MGMT_WPA_NONE) {
+ /*
+ * At least Host AP driver and a Prism3 card seemed to be
+ * generating streams of disconnected events when configuring
+ * IBSS for WPA-None. Ignore them for now.
+ */
+ return;
+ }
+
+ bssid = wpa_s->bssid;
+ if (is_zero_ether_addr(bssid))
+ bssid = wpa_s->pending_bssid;
+
+ if (!is_zero_ether_addr(bssid) ||
+ wpa_s->wpa_state >= WPA_AUTHENTICATING) {
+ wpa_msg(wpa_s, MSG_INFO, WPA_EVENT_DISCONNECTED "bssid=" MACSTR
+ " reason=%d%s",
+ MAC2STR(bssid), reason_code,
+ locally_generated ? " locally_generated=1" : "");
+ }
+}
+
+
+static int could_be_psk_mismatch(struct wpa_supplicant *wpa_s, u16 reason_code,
+ int locally_generated)
+{
+ if (wpa_s->wpa_state != WPA_4WAY_HANDSHAKE ||
+ !wpa_key_mgmt_wpa_psk(wpa_s->key_mgmt))
+ return 0; /* Not in 4-way handshake with PSK */
+
+ /*
+ * It looks like connection was lost while trying to go through PSK
+ * 4-way handshake. Filter out known disconnection cases that are caused
+ * by something else than PSK mismatch to avoid confusing reports.
+ */
+
+ if (locally_generated) {
+ if (reason_code == WLAN_REASON_IE_IN_4WAY_DIFFERS)
+ return 0;
+ }
+
+ return 1;
+}
+
+
+static void wpa_supplicant_event_disassoc_finish(struct wpa_supplicant *wpa_s,
+ u16 reason_code,
+ int locally_generated)
{
const u8 *bssid;
-#ifdef CONFIG_SME
int authenticating;
u8 prev_pending_bssid[ETH_ALEN];
+ struct wpa_bss *fast_reconnect = NULL;
+ struct wpa_ssid *fast_reconnect_ssid = NULL;
+ struct wpa_ssid *last_ssid;
authenticating = wpa_s->wpa_state == WPA_AUTHENTICATING;
os_memcpy(prev_pending_bssid, wpa_s->pending_bssid, ETH_ALEN);
-#endif /* CONFIG_SME */
if (wpa_s->key_mgmt == WPA_KEY_MGMT_WPA_NONE) {
/*
@@ -1277,61 +1873,93 @@ static void wpa_supplicant_event_disassoc(struct wpa_supplicant *wpa_s,
* generating streams of disconnected events when configuring
* IBSS for WPA-None. Ignore them for now.
*/
- wpa_printf(MSG_DEBUG, "Disconnect event - ignore in "
- "IBSS/WPA-None mode");
+ wpa_dbg(wpa_s, MSG_DEBUG, "Disconnect event - ignore in "
+ "IBSS/WPA-None mode");
return;
}
- if (wpa_s->wpa_state == WPA_4WAY_HANDSHAKE &&
- wpa_key_mgmt_wpa_psk(wpa_s->key_mgmt)) {
+ if (could_be_psk_mismatch(wpa_s, reason_code, locally_generated)) {
wpa_msg(wpa_s, MSG_INFO, "WPA: 4-Way Handshake failed - "
"pre-shared key may be incorrect");
+ wpas_auth_failed(wpa_s);
+ }
+ if (!wpa_s->auto_reconnect_disabled ||
+ wpa_s->key_mgmt == WPA_KEY_MGMT_WPS) {
+ wpa_dbg(wpa_s, MSG_DEBUG, "Auto connect enabled: try to "
+ "reconnect (wps=%d wpa_state=%d)",
+ wpa_s->key_mgmt == WPA_KEY_MGMT_WPS,
+ wpa_s->wpa_state);
+ if (wpa_s->wpa_state == WPA_COMPLETED &&
+ wpa_s->current_ssid &&
+ wpa_s->current_ssid->mode == WPAS_MODE_INFRA &&
+ !locally_generated &&
+ disconnect_reason_recoverable(reason_code)) {
+ /*
+ * It looks like the AP has dropped association with
+ * us, but could allow us to get back in. Try to
+ * reconnect to the same BSS without full scan to save
+ * time for some common cases.
+ */
+ fast_reconnect = wpa_s->current_bss;
+ fast_reconnect_ssid = wpa_s->current_ssid;
+ } else if (wpa_s->wpa_state >= WPA_ASSOCIATING)
+ wpa_supplicant_req_scan(wpa_s, 0, 100000);
+ else
+ wpa_dbg(wpa_s, MSG_DEBUG, "Do not request new "
+ "immediate scan");
+ } else {
+ wpa_dbg(wpa_s, MSG_DEBUG, "Auto connect disabled: do not "
+ "try to re-connect");
+ wpa_s->reassociate = 0;
+ wpa_s->disconnected = 1;
+ wpa_supplicant_cancel_sched_scan(wpa_s);
}
- if (wpa_s->wpa_state >= WPA_ASSOCIATED)
- wpa_supplicant_req_scan(wpa_s, 0, 100000);
bssid = wpa_s->bssid;
if (is_zero_ether_addr(bssid))
bssid = wpa_s->pending_bssid;
- wpa_blacklist_add(wpa_s, bssid);
+ if (wpa_s->wpa_state >= WPA_AUTHENTICATING)
+ wpas_connection_failed(wpa_s, bssid);
wpa_sm_notify_disassoc(wpa_s->wpa);
- wpa_msg(wpa_s, MSG_INFO, WPA_EVENT_DISCONNECTED "bssid=" MACSTR
- " reason=%d",
- MAC2STR(bssid), reason_code);
+ if (locally_generated)
+ wpa_s->disconnect_reason = -reason_code;
+ else
+ wpa_s->disconnect_reason = reason_code;
+ wpas_notify_disconnect_reason(wpa_s);
if (wpa_supplicant_dynamic_keys(wpa_s)) {
- wpa_printf(MSG_DEBUG, "Disconnect event - remove keys");
+ wpa_dbg(wpa_s, MSG_DEBUG, "Disconnect event - remove keys");
wpa_s->keys_cleared = 0;
wpa_clear_keys(wpa_s, wpa_s->bssid);
}
+ last_ssid = wpa_s->current_ssid;
wpa_supplicant_mark_disassoc(wpa_s);
- bgscan_deinit(wpa_s);
- wpa_s->bgscan_ssid = NULL;
-#ifdef CONFIG_SME
- if (authenticating &&
- (wpa_s->drv_flags & WPA_DRIVER_FLAGS_SME)) {
- /*
- * mac80211-workaround to force deauth on failed auth cmd,
- * requires us to remain in authenticating state to allow the
- * second authentication attempt to be continued properly.
- */
- wpa_printf(MSG_DEBUG, "SME: Allow pending authentication to "
- "proceed after disconnection event");
- wpa_supplicant_set_state(wpa_s, WPA_AUTHENTICATING);
- os_memcpy(wpa_s->pending_bssid, prev_pending_bssid, ETH_ALEN);
+
+ if (authenticating && (wpa_s->drv_flags & WPA_DRIVER_FLAGS_SME)) {
+ sme_disassoc_while_authenticating(wpa_s, prev_pending_bssid);
+ wpa_s->current_ssid = last_ssid;
+ }
+
+ if (fast_reconnect) {
+#ifndef CONFIG_NO_SCAN_PROCESSING
+ wpa_dbg(wpa_s, MSG_DEBUG, "Try to reconnect to the same BSS");
+ if (wpa_supplicant_connect(wpa_s, fast_reconnect,
+ fast_reconnect_ssid) < 0) {
+ /* Recover through full scan */
+ wpa_supplicant_req_scan(wpa_s, 0, 100000);
+ }
+#endif /* CONFIG_NO_SCAN_PROCESSING */
}
-#endif /* CONFIG_SME */
}
#ifdef CONFIG_DELAYED_MIC_ERROR_REPORT
-static void wpa_supplicant_delayed_mic_error_report(void *eloop_ctx,
- void *sock_ctx)
+void wpa_supplicant_delayed_mic_error_report(void *eloop_ctx, void *sock_ctx)
{
struct wpa_supplicant *wpa_s = eloop_ctx;
if (!wpa_s->pending_mic_error_report)
return;
- wpa_printf(MSG_DEBUG, "WPA: Sending pending MIC error report");
+ wpa_dbg(wpa_s, MSG_DEBUG, "WPA: Sending pending MIC error report");
wpa_sm_key_request(wpa_s->wpa, 1, wpa_s->pending_mic_error_pairwise);
wpa_s->pending_mic_error_report = 0;
}
@@ -1368,6 +1996,9 @@ wpa_supplicant_event_michael_mic_failure(struct wpa_supplicant *wpa_s,
/* initialize countermeasures */
wpa_s->countermeasures = 1;
+
+ wpa_blacklist_add(wpa_s, wpa_s->bssid);
+
wpa_msg(wpa_s, MSG_WARNING, "TKIP countermeasures started");
/*
@@ -1406,8 +2037,8 @@ wpa_supplicant_event_michael_mic_failure(struct wpa_supplicant *wpa_s,
sec = os_random() % 60;
else
sec = WPA_GET_BE32(rval) % 60;
- wpa_printf(MSG_DEBUG, "WPA: Delay MIC error report %d "
- "seconds", sec);
+ wpa_dbg(wpa_s, MSG_DEBUG, "WPA: Delay MIC error "
+ "report %d seconds", sec);
wpa_s->pending_mic_error_report = 1;
wpa_s->pending_mic_error_pairwise = pairwise;
eloop_cancel_timeout(
@@ -1454,18 +2085,24 @@ wpa_supplicant_event_interface_status(struct wpa_supplicant *wpa_s,
if (!wpa_s->interface_removed)
break;
wpa_s->interface_removed = 0;
- wpa_printf(MSG_DEBUG, "Configured interface was added.");
+ wpa_dbg(wpa_s, MSG_DEBUG, "Configured interface was added");
if (wpa_supplicant_driver_init(wpa_s) < 0) {
- wpa_printf(MSG_INFO, "Failed to initialize the driver "
- "after interface was added.");
+ wpa_msg(wpa_s, MSG_INFO, "Failed to initialize the "
+ "driver after interface was added");
}
+ wpa_supplicant_set_state(wpa_s, WPA_DISCONNECTED);
break;
case EVENT_INTERFACE_REMOVED:
- wpa_printf(MSG_DEBUG, "Configured interface was removed.");
+ wpa_dbg(wpa_s, MSG_DEBUG, "Configured interface was removed");
wpa_s->interface_removed = 1;
wpa_supplicant_mark_disassoc(wpa_s);
+ wpa_supplicant_set_state(wpa_s, WPA_INTERFACE_DISABLED);
l2_packet_deinit(wpa_s->l2);
wpa_s->l2 = NULL;
+#ifdef CONFIG_IBSS_RSN
+ ibss_rsn_deinit(wpa_s->ibss_rsn);
+ wpa_s->ibss_rsn = NULL;
+#endif /* CONFIG_IBSS_RSN */
#ifdef CONFIG_TERMINATE_ONLASTIF
/* check if last interface */
if (!any_interfaces(wpa_s->global->ifaces))
@@ -1488,6 +2125,44 @@ wpa_supplicant_event_stkstart(struct wpa_supplicant *wpa_s,
#endif /* CONFIG_PEERKEY */
+#ifdef CONFIG_TDLS
+static void wpa_supplicant_event_tdls(struct wpa_supplicant *wpa_s,
+ union wpa_event_data *data)
+{
+ if (data == NULL)
+ return;
+ switch (data->tdls.oper) {
+ case TDLS_REQUEST_SETUP:
+ wpa_tdls_start(wpa_s->wpa, data->tdls.peer);
+ break;
+ case TDLS_REQUEST_TEARDOWN:
+ wpa_tdls_send_teardown(wpa_s->wpa, data->tdls.peer,
+ data->tdls.reason_code);
+ break;
+ }
+}
+#endif /* CONFIG_TDLS */
+
+
+#ifdef CONFIG_WNM
+static void wpa_supplicant_event_wnm(struct wpa_supplicant *wpa_s,
+ union wpa_event_data *data)
+{
+ if (data == NULL)
+ return;
+ switch (data->wnm.oper) {
+ case WNM_OPER_SLEEP:
+ wpa_printf(MSG_DEBUG, "Start sending WNM-Sleep Request "
+ "(action=%d, intval=%d)",
+ data->wnm.sleep_action, data->wnm.sleep_intval);
+ ieee802_11_send_wnmsleep_req(wpa_s, data->wnm.sleep_action,
+ data->wnm.sleep_intval, NULL);
+ break;
+ }
+}
+#endif /* CONFIG_WNM */
+
+
#ifdef CONFIG_IEEE80211R
static void
wpa_supplicant_event_ft_response(struct wpa_supplicant *wpa_s,
@@ -1512,8 +2187,17 @@ wpa_supplicant_event_ft_response(struct wpa_supplicant *wpa_s,
static void wpa_supplicant_event_ibss_rsn_start(struct wpa_supplicant *wpa_s,
union wpa_event_data *data)
{
+ struct wpa_ssid *ssid;
+ if (wpa_s->wpa_state < WPA_ASSOCIATED)
+ return;
if (data == NULL)
return;
+ ssid = wpa_s->current_ssid;
+ if (ssid == NULL)
+ return;
+ if (ssid->mode != WPAS_MODE_IBSS || !wpa_key_mgmt_wpa(ssid->key_mgmt))
+ return;
+
ibss_rsn_start(wpa_s->ibss_rsn, data->ibss_rsn_start.peer);
}
#endif /* CONFIG_IBSS_RSN */
@@ -1536,19 +2220,19 @@ static void ft_rx_action(struct wpa_supplicant *wpa_s, const u8 *data,
sta_addr = data + 1;
target_ap_addr = data + 1 + ETH_ALEN;
status = WPA_GET_LE16(data + 1 + 2 * ETH_ALEN);
- wpa_printf(MSG_DEBUG, "FT: Received FT Action Response: STA " MACSTR
- " TargetAP " MACSTR " status %u",
- MAC2STR(sta_addr), MAC2STR(target_ap_addr), status);
+ wpa_dbg(wpa_s, MSG_DEBUG, "FT: Received FT Action Response: STA "
+ MACSTR " TargetAP " MACSTR " status %u",
+ MAC2STR(sta_addr), MAC2STR(target_ap_addr), status);
if (os_memcmp(sta_addr, wpa_s->own_addr, ETH_ALEN) != 0) {
- wpa_printf(MSG_DEBUG, "FT: Foreign STA Address " MACSTR
- " in FT Action Response", MAC2STR(sta_addr));
+ wpa_dbg(wpa_s, MSG_DEBUG, "FT: Foreign STA Address " MACSTR
+ " in FT Action Response", MAC2STR(sta_addr));
return;
}
if (status) {
- wpa_printf(MSG_DEBUG, "FT: FT Action Response indicates "
- "failure (status code %d)", status);
+ wpa_dbg(wpa_s, MSG_DEBUG, "FT: FT Action Response indicates "
+ "failure (status code %d)", status);
/* TODO: report error to FT code(?) */
return;
}
@@ -1573,11 +2257,67 @@ static void ft_rx_action(struct wpa_supplicant *wpa_s, const u8 *data,
#endif /* CONFIG_IEEE80211R */
+static void wpa_supplicant_event_unprot_deauth(struct wpa_supplicant *wpa_s,
+ struct unprot_deauth *e)
+{
+#ifdef CONFIG_IEEE80211W
+ wpa_printf(MSG_DEBUG, "Unprotected Deauthentication frame "
+ "dropped: " MACSTR " -> " MACSTR
+ " (reason code %u)",
+ MAC2STR(e->sa), MAC2STR(e->da), e->reason_code);
+ sme_event_unprot_disconnect(wpa_s, e->sa, e->da, e->reason_code);
+#endif /* CONFIG_IEEE80211W */
+}
+
+
+static void wpa_supplicant_event_unprot_disassoc(struct wpa_supplicant *wpa_s,
+ struct unprot_disassoc *e)
+{
+#ifdef CONFIG_IEEE80211W
+ wpa_printf(MSG_DEBUG, "Unprotected Disassociation frame "
+ "dropped: " MACSTR " -> " MACSTR
+ " (reason code %u)",
+ MAC2STR(e->sa), MAC2STR(e->da), e->reason_code);
+ sme_event_unprot_disconnect(wpa_s, e->sa, e->da, e->reason_code);
+#endif /* CONFIG_IEEE80211W */
+}
+
+
void wpa_supplicant_event(void *ctx, enum wpa_event_type event,
union wpa_event_data *data)
{
struct wpa_supplicant *wpa_s = ctx;
u16 reason_code = 0;
+ int locally_generated = 0;
+
+ if (wpa_s->wpa_state == WPA_INTERFACE_DISABLED &&
+ event != EVENT_INTERFACE_ENABLED &&
+ event != EVENT_INTERFACE_STATUS &&
+ event != EVENT_SCHED_SCAN_STOPPED) {
+ wpa_dbg(wpa_s, MSG_DEBUG,
+ "Ignore event %s (%d) while interface is disabled",
+ event_to_string(event), event);
+ return;
+ }
+
+#ifndef CONFIG_NO_STDOUT_DEBUG
+{
+ int level = MSG_DEBUG;
+
+ if (event == EVENT_RX_MGMT && data->rx_mgmt.frame_len >= 24) {
+ const struct ieee80211_hdr *hdr;
+ u16 fc;
+ hdr = (const struct ieee80211_hdr *) data->rx_mgmt.frame;
+ fc = le_to_host16(hdr->frame_control);
+ if (WLAN_FC_GET_TYPE(fc) == WLAN_FC_TYPE_MGMT &&
+ WLAN_FC_GET_STYPE(fc) == WLAN_FC_STYPE_BEACON)
+ level = MSG_EXCESSIVE;
+ }
+
+ wpa_dbg(wpa_s, level, "Event %s (%d) received",
+ event_to_string(event), event);
+}
+#endif /* CONFIG_NO_STDOUT_DEBUG */
switch (event) {
case EVENT_AUTH:
@@ -1587,24 +2327,69 @@ void wpa_supplicant_event(void *ctx, enum wpa_event_type event,
wpa_supplicant_event_assoc(wpa_s, data);
break;
case EVENT_DISASSOC:
- wpa_printf(MSG_DEBUG, "Disassociation notification");
+ wpa_dbg(wpa_s, MSG_DEBUG, "Disassociation notification");
+ if (data) {
+ wpa_dbg(wpa_s, MSG_DEBUG, " * reason %u%s",
+ data->disassoc_info.reason_code,
+ data->disassoc_info.locally_generated ?
+ " (locally generated)" : "");
+ if (data->disassoc_info.addr)
+ wpa_dbg(wpa_s, MSG_DEBUG, " * address " MACSTR,
+ MAC2STR(data->disassoc_info.addr));
+ }
#ifdef CONFIG_AP
if (wpa_s->ap_iface && data && data->disassoc_info.addr) {
hostapd_notif_disassoc(wpa_s->ap_iface->bss[0],
data->disassoc_info.addr);
break;
}
+ if (wpa_s->ap_iface) {
+ wpa_dbg(wpa_s, MSG_DEBUG, "Ignore disassoc event in "
+ "AP mode");
+ break;
+ }
#endif /* CONFIG_AP */
- if (data)
- reason_code = data->deauth_info.reason_code;
+ if (data) {
+ reason_code = data->disassoc_info.reason_code;
+ locally_generated =
+ data->disassoc_info.locally_generated;
+ wpa_hexdump(MSG_DEBUG, "Disassociation frame IE(s)",
+ data->disassoc_info.ie,
+ data->disassoc_info.ie_len);
+#ifdef CONFIG_P2P
+ wpas_p2p_disassoc_notif(
+ wpa_s, data->disassoc_info.addr, reason_code,
+ data->disassoc_info.ie,
+ data->disassoc_info.ie_len,
+ locally_generated);
+#endif /* CONFIG_P2P */
+ }
if (wpa_s->drv_flags & WPA_DRIVER_FLAGS_SME)
sme_event_disassoc(wpa_s, data);
/* fall through */
case EVENT_DEAUTH:
if (event == EVENT_DEAUTH) {
- wpa_printf(MSG_DEBUG, "Deauthentication notification");
- if (data)
+ wpa_dbg(wpa_s, MSG_DEBUG,
+ "Deauthentication notification");
+ if (data) {
reason_code = data->deauth_info.reason_code;
+ locally_generated =
+ data->deauth_info.locally_generated;
+ wpa_dbg(wpa_s, MSG_DEBUG, " * reason %u%s",
+ data->deauth_info.reason_code,
+ data->deauth_info.locally_generated ?
+ " (locally generated)" : "");
+ if (data->deauth_info.addr) {
+ wpa_dbg(wpa_s, MSG_DEBUG, " * address "
+ MACSTR,
+ MAC2STR(data->deauth_info.
+ addr));
+ }
+ wpa_hexdump(MSG_DEBUG,
+ "Deauthentication frame IE(s)",
+ data->deauth_info.ie,
+ data->deauth_info.ie_len);
+ }
}
#ifdef CONFIG_AP
if (wpa_s->ap_iface && data && data->deauth_info.addr) {
@@ -1612,8 +2397,38 @@ void wpa_supplicant_event(void *ctx, enum wpa_event_type event,
data->deauth_info.addr);
break;
}
+ if (wpa_s->ap_iface) {
+ wpa_dbg(wpa_s, MSG_DEBUG, "Ignore deauth event in "
+ "AP mode");
+ break;
+ }
#endif /* CONFIG_AP */
- wpa_supplicant_event_disassoc(wpa_s, reason_code);
+ wpa_supplicant_event_disassoc(wpa_s, reason_code,
+ locally_generated);
+ if (reason_code == WLAN_REASON_IEEE_802_1X_AUTH_FAILED ||
+ ((wpa_key_mgmt_wpa_ieee8021x(wpa_s->key_mgmt) ||
+ (wpa_s->key_mgmt & WPA_KEY_MGMT_IEEE8021X_NO_WPA)) &&
+ eapol_sm_failed(wpa_s->eapol)))
+ wpas_auth_failed(wpa_s);
+#ifdef CONFIG_P2P
+ if (event == EVENT_DEAUTH && data) {
+ if (wpas_p2p_deauth_notif(wpa_s,
+ data->deauth_info.addr,
+ reason_code,
+ data->deauth_info.ie,
+ data->deauth_info.ie_len,
+ locally_generated) > 0) {
+ /*
+ * The interface was removed, so cannot
+ * continue processing any additional
+ * operations after this.
+ */
+ break;
+ }
+ }
+#endif /* CONFIG_P2P */
+ wpa_supplicant_event_disassoc_finish(wpa_s, reason_code,
+ locally_generated);
break;
case EVENT_MICHAEL_MIC_FAILURE:
wpa_supplicant_event_michael_mic_failure(wpa_s, data);
@@ -1621,6 +2436,18 @@ void wpa_supplicant_event(void *ctx, enum wpa_event_type event,
#ifndef CONFIG_NO_SCAN_PROCESSING
case EVENT_SCAN_RESULTS:
wpa_supplicant_event_scan_results(wpa_s, data);
+#ifdef CONFIG_P2P
+ if (wpa_s->global->p2p_cb_on_scan_complete && !wpa_s->global->p2p_disabled &&
+ wpa_s->global->p2p != NULL &&
+ wpa_s->wpa_state != WPA_AUTHENTICATING &&
+ wpa_s->wpa_state != WPA_ASSOCIATING) {
+ wpa_s->global->p2p_cb_on_scan_complete = 0;
+ if (p2p_other_scan_completed(wpa_s->global->p2p) == 1) {
+ wpa_dbg(wpa_s, MSG_DEBUG, "P2P: Pending P2P operation "
+ "continued after scan result processing");
+ }
+ }
+#endif /* CONFIG_P2P */
break;
#endif /* CONFIG_NO_SCAN_PROCESSING */
case EVENT_ASSOCINFO:
@@ -1637,6 +2464,16 @@ void wpa_supplicant_event(void *ctx, enum wpa_event_type event,
wpa_supplicant_event_stkstart(wpa_s, data);
break;
#endif /* CONFIG_PEERKEY */
+#ifdef CONFIG_TDLS
+ case EVENT_TDLS:
+ wpa_supplicant_event_tdls(wpa_s, data);
+ break;
+#endif /* CONFIG_TDLS */
+#ifdef CONFIG_WNM
+ case EVENT_WNM:
+ wpa_supplicant_event_wnm(wpa_s, data);
+ break;
+#endif /* CONFIG_WNM */
#ifdef CONFIG_IEEE80211R
case EVENT_FT_RESPONSE:
wpa_supplicant_event_ft_response(wpa_s, data);
@@ -1648,18 +2485,76 @@ void wpa_supplicant_event(void *ctx, enum wpa_event_type event,
break;
#endif /* CONFIG_IBSS_RSN */
case EVENT_ASSOC_REJECT:
- sme_event_assoc_reject(wpa_s, data);
+ if (data->assoc_reject.bssid)
+ wpa_msg(wpa_s, MSG_INFO, WPA_EVENT_ASSOC_REJECT
+ "bssid=" MACSTR " status_code=%u",
+ MAC2STR(data->assoc_reject.bssid),
+ data->assoc_reject.status_code);
+ else
+ wpa_msg(wpa_s, MSG_INFO, WPA_EVENT_ASSOC_REJECT
+ "status_code=%u",
+ data->assoc_reject.status_code);
+ if (wpa_s->drv_flags & WPA_DRIVER_FLAGS_SME)
+ sme_event_assoc_reject(wpa_s, data);
+ else {
+ const u8 *bssid = data->assoc_reject.bssid;
+ if (bssid == NULL || is_zero_ether_addr(bssid))
+ bssid = wpa_s->pending_bssid;
+ wpas_connection_failed(wpa_s, bssid);
+ wpa_supplicant_mark_disassoc(wpa_s);
+ }
break;
case EVENT_AUTH_TIMED_OUT:
- sme_event_auth_timed_out(wpa_s, data);
+ if (wpa_s->drv_flags & WPA_DRIVER_FLAGS_SME)
+ sme_event_auth_timed_out(wpa_s, data);
break;
case EVENT_ASSOC_TIMED_OUT:
- sme_event_assoc_timed_out(wpa_s, data);
+ if (wpa_s->drv_flags & WPA_DRIVER_FLAGS_SME)
+ sme_event_assoc_timed_out(wpa_s, data);
break;
-#ifdef CONFIG_AP
case EVENT_TX_STATUS:
- if (wpa_s->ap_iface == NULL)
+ wpa_dbg(wpa_s, MSG_DEBUG, "EVENT_TX_STATUS dst=" MACSTR
+ " type=%d stype=%d",
+ MAC2STR(data->tx_status.dst),
+ data->tx_status.type, data->tx_status.stype);
+#ifdef CONFIG_AP
+ if (wpa_s->ap_iface == NULL) {
+#ifdef CONFIG_OFFCHANNEL
+ if (data->tx_status.type == WLAN_FC_TYPE_MGMT &&
+ data->tx_status.stype == WLAN_FC_STYPE_ACTION)
+ offchannel_send_action_tx_status(
+ wpa_s, data->tx_status.dst,
+ data->tx_status.data,
+ data->tx_status.data_len,
+ data->tx_status.ack ?
+ OFFCHANNEL_SEND_ACTION_SUCCESS :
+ OFFCHANNEL_SEND_ACTION_NO_ACK);
+#endif /* CONFIG_OFFCHANNEL */
break;
+ }
+#endif /* CONFIG_AP */
+#ifdef CONFIG_OFFCHANNEL
+ wpa_dbg(wpa_s, MSG_DEBUG, "EVENT_TX_STATUS pending_dst="
+ MACSTR, MAC2STR(wpa_s->parent->pending_action_dst));
+ /*
+ * Catch TX status events for Action frames we sent via group
+ * interface in GO mode.
+ */
+ if (data->tx_status.type == WLAN_FC_TYPE_MGMT &&
+ data->tx_status.stype == WLAN_FC_STYPE_ACTION &&
+ os_memcmp(wpa_s->parent->pending_action_dst,
+ data->tx_status.dst, ETH_ALEN) == 0) {
+ offchannel_send_action_tx_status(
+ wpa_s->parent, data->tx_status.dst,
+ data->tx_status.data,
+ data->tx_status.data_len,
+ data->tx_status.ack ?
+ OFFCHANNEL_SEND_ACTION_SUCCESS :
+ OFFCHANNEL_SEND_ACTION_NO_ACK);
+ break;
+ }
+#endif /* CONFIG_OFFCHANNEL */
+#ifdef CONFIG_AP
switch (data->tx_status.type) {
case WLAN_FC_TYPE_MGMT:
ap_mgmt_tx_cb(wpa_s, data->tx_status.data,
@@ -1674,25 +2569,91 @@ void wpa_supplicant_event(void *ctx, enum wpa_event_type event,
data->tx_status.ack);
break;
}
+#endif /* CONFIG_AP */
+ break;
+#ifdef CONFIG_AP
+ case EVENT_EAPOL_TX_STATUS:
+ ap_eapol_tx_status(wpa_s, data->eapol_tx_status.dst,
+ data->eapol_tx_status.data,
+ data->eapol_tx_status.data_len,
+ data->eapol_tx_status.ack);
+ break;
+ case EVENT_DRIVER_CLIENT_POLL_OK:
+ ap_client_poll_ok(wpa_s, data->client_poll.addr);
break;
case EVENT_RX_FROM_UNKNOWN:
if (wpa_s->ap_iface == NULL)
break;
- ap_rx_from_unknown_sta(wpa_s, data->rx_from_unknown.frame,
- data->rx_from_unknown.len);
+ ap_rx_from_unknown_sta(wpa_s, data->rx_from_unknown.addr,
+ data->rx_from_unknown.wds);
break;
- case EVENT_RX_MGMT:
- if (wpa_s->ap_iface == NULL)
+ case EVENT_CH_SWITCH:
+ if (!data)
+ break;
+ if (!wpa_s->ap_iface) {
+ wpa_dbg(wpa_s, MSG_DEBUG, "AP: Ignore channel switch "
+ "event in non-AP mode");
+ break;
+ }
+
+#ifdef CONFIG_AP
+ wpas_ap_ch_switch(wpa_s, data->ch_switch.freq,
+ data->ch_switch.ht_enabled,
+ data->ch_switch.ch_offset);
+#endif /* CONFIG_AP */
+ break;
+ case EVENT_RX_MGMT: {
+ u16 fc, stype;
+ const struct ieee80211_mgmt *mgmt;
+
+ mgmt = (const struct ieee80211_mgmt *)
+ data->rx_mgmt.frame;
+ fc = le_to_host16(mgmt->frame_control);
+ stype = WLAN_FC_GET_STYPE(fc);
+
+ if (wpa_s->ap_iface == NULL) {
+#ifdef CONFIG_P2P
+ if (stype == WLAN_FC_STYPE_PROBE_REQ &&
+ data->rx_mgmt.frame_len > 24) {
+ const u8 *src = mgmt->sa;
+ const u8 *ie = mgmt->u.probe_req.variable;
+ size_t ie_len = data->rx_mgmt.frame_len -
+ (mgmt->u.probe_req.variable -
+ data->rx_mgmt.frame);
+ wpas_p2p_probe_req_rx(
+ wpa_s, src, mgmt->da,
+ mgmt->bssid, ie, ie_len,
+ data->rx_mgmt.ssi_signal);
+ break;
+ }
+#endif /* CONFIG_P2P */
+ wpa_dbg(wpa_s, MSG_DEBUG, "AP: ignore received "
+ "management frame in non-AP mode");
break;
+ }
+
+ if (stype == WLAN_FC_STYPE_PROBE_REQ &&
+ data->rx_mgmt.frame_len > 24) {
+ const u8 *ie = mgmt->u.probe_req.variable;
+ size_t ie_len = data->rx_mgmt.frame_len -
+ (mgmt->u.probe_req.variable -
+ data->rx_mgmt.frame);
+
+ wpas_notify_preq(wpa_s, mgmt->sa, mgmt->da,
+ mgmt->bssid, ie, ie_len,
+ data->rx_mgmt.ssi_signal);
+ }
+
ap_mgmt_rx(wpa_s, &data->rx_mgmt);
break;
+ }
#endif /* CONFIG_AP */
case EVENT_RX_ACTION:
- wpa_printf(MSG_DEBUG, "Received Action frame: SA=" MACSTR
- " Category=%u DataLen=%d freq=%d MHz",
- MAC2STR(data->rx_action.sa),
- data->rx_action.category, (int) data->rx_action.len,
- data->rx_action.freq);
+ wpa_dbg(wpa_s, MSG_DEBUG, "Received Action frame: SA=" MACSTR
+ " Category=%u DataLen=%d freq=%d MHz",
+ MAC2STR(data->rx_action.sa),
+ data->rx_action.category, (int) data->rx_action.len,
+ data->rx_action.freq);
#ifdef CONFIG_IEEE80211R
if (data->rx_action.category == WLAN_ACTION_FT) {
ft_rx_action(wpa_s, data->rx_action.data,
@@ -1700,19 +2661,161 @@ void wpa_supplicant_event(void *ctx, enum wpa_event_type event,
break;
}
#endif /* CONFIG_IEEE80211R */
+#ifdef CONFIG_IEEE80211W
+#ifdef CONFIG_SME
+ if (data->rx_action.category == WLAN_ACTION_SA_QUERY) {
+ sme_sa_query_rx(wpa_s, data->rx_action.sa,
+ data->rx_action.data,
+ data->rx_action.len);
+ break;
+ }
+#endif /* CONFIG_SME */
+#endif /* CONFIG_IEEE80211W */
+#ifdef CONFIG_WNM
+ if (data->rx_action.category == WLAN_ACTION_WNM) {
+ ieee802_11_rx_wnm_action(wpa_s, &data->rx_action);
+ break;
+ }
+#endif /* CONFIG_WNM */
+#ifdef CONFIG_GAS
+ if (data->rx_action.category == WLAN_ACTION_PUBLIC &&
+ gas_query_rx(wpa_s->gas, data->rx_action.da,
+ data->rx_action.sa, data->rx_action.bssid,
+ data->rx_action.data, data->rx_action.len,
+ data->rx_action.freq) == 0)
+ break;
+#endif /* CONFIG_GAS */
+#ifdef CONFIG_TDLS
+ if (data->rx_action.category == WLAN_ACTION_PUBLIC &&
+ data->rx_action.len >= 4 &&
+ data->rx_action.data[0] == WLAN_TDLS_DISCOVERY_RESPONSE) {
+ wpa_dbg(wpa_s, MSG_DEBUG, "TDLS: Received Discovery "
+ "Response from " MACSTR,
+ MAC2STR(data->rx_action.sa));
+ break;
+ }
+#endif /* CONFIG_TDLS */
+#ifdef CONFIG_P2P
+ wpas_p2p_rx_action(wpa_s, data->rx_action.da,
+ data->rx_action.sa,
+ data->rx_action.bssid,
+ data->rx_action.category,
+ data->rx_action.data,
+ data->rx_action.len, data->rx_action.freq);
+#endif /* CONFIG_P2P */
break;
-#ifdef CONFIG_CLIENT_MLME
- case EVENT_MLME_RX: {
- struct ieee80211_rx_status rx_status;
- os_memset(&rx_status, 0, sizeof(rx_status));
- rx_status.freq = data->mlme_rx.freq;
- rx_status.channel = data->mlme_rx.channel;
- rx_status.ssi = data->mlme_rx.ssi;
- ieee80211_sta_rx(wpa_s, data->mlme_rx.buf, data->mlme_rx.len,
- &rx_status);
+ case EVENT_RX_PROBE_REQ:
+ if (data->rx_probe_req.sa == NULL ||
+ data->rx_probe_req.ie == NULL)
+ break;
+#ifdef CONFIG_AP
+ if (wpa_s->ap_iface) {
+ hostapd_probe_req_rx(wpa_s->ap_iface->bss[0],
+ data->rx_probe_req.sa,
+ data->rx_probe_req.da,
+ data->rx_probe_req.bssid,
+ data->rx_probe_req.ie,
+ data->rx_probe_req.ie_len,
+ data->rx_probe_req.ssi_signal);
+ break;
+ }
+#endif /* CONFIG_AP */
+#ifdef CONFIG_P2P
+ wpas_p2p_probe_req_rx(wpa_s, data->rx_probe_req.sa,
+ data->rx_probe_req.da,
+ data->rx_probe_req.bssid,
+ data->rx_probe_req.ie,
+ data->rx_probe_req.ie_len,
+ data->rx_probe_req.ssi_signal);
+#endif /* CONFIG_P2P */
break;
- }
-#endif /* CONFIG_CLIENT_MLME */
+ case EVENT_REMAIN_ON_CHANNEL:
+#ifdef CONFIG_OFFCHANNEL
+ offchannel_remain_on_channel_cb(
+ wpa_s, data->remain_on_channel.freq,
+ data->remain_on_channel.duration);
+#endif /* CONFIG_OFFCHANNEL */
+#ifdef CONFIG_P2P
+ wpas_p2p_remain_on_channel_cb(
+ wpa_s, data->remain_on_channel.freq,
+ data->remain_on_channel.duration);
+#endif /* CONFIG_P2P */
+ break;
+ case EVENT_CANCEL_REMAIN_ON_CHANNEL:
+#ifdef CONFIG_OFFCHANNEL
+ offchannel_cancel_remain_on_channel_cb(
+ wpa_s, data->remain_on_channel.freq);
+#endif /* CONFIG_OFFCHANNEL */
+#ifdef CONFIG_P2P
+ wpas_p2p_cancel_remain_on_channel_cb(
+ wpa_s, data->remain_on_channel.freq);
+#endif /* CONFIG_P2P */
+ break;
+#ifdef CONFIG_P2P
+ case EVENT_P2P_DEV_FOUND: {
+ struct p2p_peer_info peer_info;
+
+ os_memset(&peer_info, 0, sizeof(peer_info));
+ if (data->p2p_dev_found.dev_addr)
+ os_memcpy(peer_info.p2p_device_addr,
+ data->p2p_dev_found.dev_addr, ETH_ALEN);
+ if (data->p2p_dev_found.pri_dev_type)
+ os_memcpy(peer_info.pri_dev_type,
+ data->p2p_dev_found.pri_dev_type,
+ sizeof(peer_info.pri_dev_type));
+ if (data->p2p_dev_found.dev_name)
+ os_strlcpy(peer_info.device_name,
+ data->p2p_dev_found.dev_name,
+ sizeof(peer_info.device_name));
+ peer_info.config_methods = data->p2p_dev_found.config_methods;
+ peer_info.dev_capab = data->p2p_dev_found.dev_capab;
+ peer_info.group_capab = data->p2p_dev_found.group_capab;
+
+ /*
+ * FIX: new_device=1 is not necessarily correct. We should
+ * maintain a P2P peer database in wpa_supplicant and update
+ * this information based on whether the peer is truly new.
+ */
+ wpas_dev_found(wpa_s, data->p2p_dev_found.addr, &peer_info, 1);
+ break;
+ }
+ case EVENT_P2P_GO_NEG_REQ_RX:
+ wpas_go_neg_req_rx(wpa_s, data->p2p_go_neg_req_rx.src,
+ data->p2p_go_neg_req_rx.dev_passwd_id);
+ break;
+ case EVENT_P2P_GO_NEG_COMPLETED:
+ wpas_go_neg_completed(wpa_s, data->p2p_go_neg_completed.res);
+ break;
+ case EVENT_P2P_PROV_DISC_REQUEST:
+ wpas_prov_disc_req(wpa_s, data->p2p_prov_disc_req.peer,
+ data->p2p_prov_disc_req.config_methods,
+ data->p2p_prov_disc_req.dev_addr,
+ data->p2p_prov_disc_req.pri_dev_type,
+ data->p2p_prov_disc_req.dev_name,
+ data->p2p_prov_disc_req.supp_config_methods,
+ data->p2p_prov_disc_req.dev_capab,
+ data->p2p_prov_disc_req.group_capab,
+ NULL, 0);
+ break;
+ case EVENT_P2P_PROV_DISC_RESPONSE:
+ wpas_prov_disc_resp(wpa_s, data->p2p_prov_disc_resp.peer,
+ data->p2p_prov_disc_resp.config_methods);
+ break;
+ case EVENT_P2P_SD_REQUEST:
+ wpas_sd_request(wpa_s, data->p2p_sd_req.freq,
+ data->p2p_sd_req.sa,
+ data->p2p_sd_req.dialog_token,
+ data->p2p_sd_req.update_indic,
+ data->p2p_sd_req.tlvs,
+ data->p2p_sd_req.tlvs_len);
+ break;
+ case EVENT_P2P_SD_RESPONSE:
+ wpas_sd_response(wpa_s, data->p2p_sd_resp.sa,
+ data->p2p_sd_resp.update_indic,
+ data->p2p_sd_resp.tlvs,
+ data->p2p_sd_resp.tlvs_len);
+ break;
+#endif /* CONFIG_P2P */
case EVENT_EAPOL_RX:
wpa_supplicant_rx_eapol(wpa_s, data->eapol_rx.src,
data->eapol_rx.data,
@@ -1720,10 +2823,119 @@ void wpa_supplicant_event(void *ctx, enum wpa_event_type event,
break;
case EVENT_SIGNAL_CHANGE:
bgscan_notify_signal_change(
- wpa_s, data->signal_change.above_threshold);
+ wpa_s, data->signal_change.above_threshold,
+ data->signal_change.current_signal,
+ data->signal_change.current_noise,
+ data->signal_change.current_txrate);
+ break;
+ case EVENT_INTERFACE_ENABLED:
+ wpa_dbg(wpa_s, MSG_DEBUG, "Interface was enabled");
+ if (wpa_s->wpa_state == WPA_INTERFACE_DISABLED) {
+ wpa_supplicant_update_mac_addr(wpa_s);
+#ifdef CONFIG_AP
+ if (!wpa_s->ap_iface) {
+ wpa_supplicant_set_state(wpa_s,
+ WPA_DISCONNECTED);
+ wpa_supplicant_req_scan(wpa_s, 0, 0);
+ } else
+ wpa_supplicant_set_state(wpa_s,
+ WPA_COMPLETED);
+#else /* CONFIG_AP */
+ wpa_supplicant_set_state(wpa_s, WPA_DISCONNECTED);
+ wpa_supplicant_req_scan(wpa_s, 0, 0);
+#endif /* CONFIG_AP */
+ }
+ break;
+ case EVENT_INTERFACE_DISABLED:
+ wpa_dbg(wpa_s, MSG_DEBUG, "Interface was disabled");
+ wpa_supplicant_mark_disassoc(wpa_s);
+ wpa_supplicant_set_state(wpa_s, WPA_INTERFACE_DISABLED);
+ break;
+ case EVENT_CHANNEL_LIST_CHANGED:
+ if (wpa_s->drv_priv == NULL)
+ break; /* Ignore event during drv initialization */
+
+ free_hw_features(wpa_s);
+ wpa_s->hw.modes = wpa_drv_get_hw_feature_data(
+ wpa_s, &wpa_s->hw.num_modes, &wpa_s->hw.flags);
+
+#ifdef CONFIG_P2P
+ wpas_p2p_update_channel_list(wpa_s);
+#endif /* CONFIG_P2P */
+ break;
+ case EVENT_INTERFACE_UNAVAILABLE:
+#ifdef CONFIG_P2P
+ wpas_p2p_interface_unavailable(wpa_s);
+#endif /* CONFIG_P2P */
+ break;
+ case EVENT_BEST_CHANNEL:
+ wpa_dbg(wpa_s, MSG_DEBUG, "Best channel event received "
+ "(%d %d %d)",
+ data->best_chan.freq_24, data->best_chan.freq_5,
+ data->best_chan.freq_overall);
+ wpa_s->best_24_freq = data->best_chan.freq_24;
+ wpa_s->best_5_freq = data->best_chan.freq_5;
+ wpa_s->best_overall_freq = data->best_chan.freq_overall;
+#ifdef CONFIG_P2P
+ wpas_p2p_update_best_channels(wpa_s, data->best_chan.freq_24,
+ data->best_chan.freq_5,
+ data->best_chan.freq_overall);
+#endif /* CONFIG_P2P */
+ break;
+ case EVENT_UNPROT_DEAUTH:
+ wpa_supplicant_event_unprot_deauth(wpa_s,
+ &data->unprot_deauth);
+ break;
+ case EVENT_UNPROT_DISASSOC:
+ wpa_supplicant_event_unprot_disassoc(wpa_s,
+ &data->unprot_disassoc);
+ break;
+ case EVENT_STATION_LOW_ACK:
+#ifdef CONFIG_AP
+ if (wpa_s->ap_iface && data)
+ hostapd_event_sta_low_ack(wpa_s->ap_iface->bss[0],
+ data->low_ack.addr);
+#endif /* CONFIG_AP */
+#ifdef CONFIG_TDLS
+ if (data)
+ wpa_tdls_disable_link(wpa_s->wpa, data->low_ack.addr);
+#endif /* CONFIG_TDLS */
+ break;
+ case EVENT_IBSS_PEER_LOST:
+#ifdef CONFIG_IBSS_RSN
+ ibss_rsn_stop(wpa_s->ibss_rsn, data->ibss_peer_lost.peer);
+#endif /* CONFIG_IBSS_RSN */
+ break;
+ case EVENT_DRIVER_GTK_REKEY:
+ if (os_memcmp(data->driver_gtk_rekey.bssid,
+ wpa_s->bssid, ETH_ALEN))
+ break;
+ if (!wpa_s->wpa)
+ break;
+ wpa_sm_update_replay_ctr(wpa_s->wpa,
+ data->driver_gtk_rekey.replay_ctr);
+ break;
+ case EVENT_SCHED_SCAN_STOPPED:
+ wpa_s->sched_scanning = 0;
+ wpa_supplicant_notify_scanning(wpa_s, 0);
+
+ if (wpa_s->wpa_state == WPA_INTERFACE_DISABLED)
+ break;
+
+ /*
+ * If we timed out, start a new sched scan to continue
+ * searching for more SSIDs.
+ */
+ if (wpa_s->sched_scan_timed_out)
+ wpa_supplicant_req_sched_scan(wpa_s);
+ break;
+ case EVENT_WPS_BUTTON_PUSHED:
+#ifdef CONFIG_WPS
+ wpas_wps_start_pbc(wpa_s, NULL, 0);
+#endif /* CONFIG_WPS */
break;
default:
- wpa_printf(MSG_INFO, "Unknown event %d", event);
+ wpa_msg(wpa_s, MSG_INFO, "Unknown event %d", event);
break;
}
}
diff --git a/contrib/wpa/wpa_supplicant/examples/dbus-listen-preq.py b/contrib/wpa/wpa_supplicant/examples/dbus-listen-preq.py
new file mode 100755
index 0000000..5ac9859
--- /dev/null
+++ b/contrib/wpa/wpa_supplicant/examples/dbus-listen-preq.py
@@ -0,0 +1,62 @@
+#!/usr/bin/python
+
+import dbus
+import sys
+import time
+import gobject
+from dbus.mainloop.glib import DBusGMainLoop
+
+WPAS_DBUS_SERVICE = "fi.w1.wpa_supplicant1"
+WPAS_DBUS_INTERFACE = "fi.w1.wpa_supplicant1"
+WPAS_DBUS_OPATH = "/fi/w1/wpa_supplicant1"
+WPAS_DBUS_INTERFACES_INTERFACE = "fi.w1.wpa_supplicant1.Interface"
+
+def usage():
+ print "Usage: %s <ifname>" % sys.argv[0]
+ print "Press Ctrl-C to stop"
+
+def ProbeRequest(args):
+ if 'addr' in args:
+ print '%.2x:%.2x:%.2x:%.2x:%.2x:%.2x' % tuple(args['addr']),
+ if 'dst' in args:
+ print '-> %.2x:%.2x:%.2x:%.2x:%.2x:%.2x' % tuple(args['dst']),
+ if 'bssid' in args:
+ print '(bssid %.2x:%.2x:%.2x:%.2x:%.2x:%.2x)' % tuple(args['dst']),
+ if 'signal' in args:
+ print 'signal:%d' % args['signal'],
+ if 'ies' in args:
+ print 'have IEs (%d bytes)' % len(args['ies']),
+ print ''
+
+if __name__ == "__main__":
+ global bus
+ global wpas_obj
+ global if_obj
+ global p2p_iface
+
+ dbus.mainloop.glib.DBusGMainLoop(set_as_default=True)
+
+ bus = dbus.SystemBus()
+ wpas_obj = bus.get_object(WPAS_DBUS_SERVICE, WPAS_DBUS_OPATH)
+
+ # Print list of i/f if no one is specified
+ if (len(sys.argv) < 2) :
+ usage()
+ sys.exit(0)
+
+ wpas = dbus.Interface(wpas_obj, WPAS_DBUS_INTERFACE)
+
+ ifname = sys.argv[1]
+
+ path = wpas.GetInterface(ifname)
+
+ if_obj = bus.get_object(WPAS_DBUS_SERVICE, path)
+ iface = dbus.Interface(if_obj, WPAS_DBUS_INTERFACES_INTERFACE)
+
+ bus.add_signal_receiver(ProbeRequest,
+ dbus_interface=WPAS_DBUS_INTERFACES_INTERFACE,
+ signal_name="ProbeRequest")
+
+ iface.SubscribeProbeReq()
+
+ gobject.MainLoop().run()
diff --git a/contrib/wpa/wpa_supplicant/examples/p2p-action-udhcp.sh b/contrib/wpa/wpa_supplicant/examples/p2p-action-udhcp.sh
new file mode 100755
index 0000000..d7d0e79
--- /dev/null
+++ b/contrib/wpa/wpa_supplicant/examples/p2p-action-udhcp.sh
@@ -0,0 +1,69 @@
+#!/bin/sh
+
+IFNAME=$1
+CMD=$2
+
+kill_daemon() {
+ NAME=$1
+ PF=$2
+
+ if [ ! -r $PF ]; then
+ return
+ fi
+
+ PID=`cat $PF`
+ if [ $PID -gt 0 ]; then
+ if ps $PID | grep -q $NAME; then
+ kill $PID
+ fi
+ fi
+ rm $PF
+}
+
+if [ "$CMD" = "P2P-GROUP-STARTED" ]; then
+ GIFNAME=$3
+ if [ "$4" = "GO" ]; then
+ kill_daemon udhcpc /var/run/udhcpc-$GIFNAME.pid
+ ifconfig $GIFNAME 192.168.42.1 up
+ udhcpd /etc/udhcpd-p2p.conf
+ fi
+ if [ "$4" = "client" ]; then
+ kill_daemon udhcpc /var/run/udhcpc-$GIFNAME.pid
+ kill_daemon udhcpd /var/run/udhcpd-$GIFNAME.pid
+ udhcpc -i $GIFNAME -p /var/run/udhcpc-$GIFNAME.pid \
+ -s /etc/udhcpc.script
+ fi
+fi
+
+if [ "$CMD" = "P2P-GROUP-REMOVED" ]; then
+ GIFNAME=$3
+ if [ "$4" = "GO" ]; then
+ kill_daemon udhcpd /var/run/udhcpd-$GIFNAME.pid
+ ifconfig $GIFNAME 0.0.0.0
+ fi
+ if [ "$4" = "client" ]; then
+ kill_daemon udhcpc /var/run/udhcpc-$GIFNAME.pid
+ ifconfig $GIFNAME 0.0.0.0
+ fi
+fi
+
+if [ "$CMD" = "P2P-CROSS-CONNECT-ENABLE" ]; then
+ GIFNAME=$3
+ UPLINK=$4
+ # enable NAT/masquarade $GIFNAME -> $UPLINK
+ iptables -P FORWARD DROP
+ iptables -t nat -A POSTROUTING -o $UPLINK -j MASQUERADE
+ iptables -A FORWARD -i $UPLINK -o $GIFNAME -m state --state RELATED,ESTABLISHED -j ACCEPT
+ iptables -A FORWARD -i $GIFNAME -o $UPLINK -j ACCEPT
+ sysctl net.ipv4.ip_forward=1
+fi
+
+if [ "$CMD" = "P2P-CROSS-CONNECT-DISABLE" ]; then
+ GIFNAME=$3
+ UPLINK=$4
+ # disable NAT/masquarade $GIFNAME -> $UPLINK
+ sysctl net.ipv4.ip_forward=0
+ iptables -t nat -D POSTROUTING -o $UPLINK -j MASQUERADE
+ iptables -D FORWARD -i $UPLINK -o $GIFNAME -m state --state RELATED,ESTABLISHED -j ACCEPT
+ iptables -D FORWARD -i $GIFNAME -o $UPLINK -j ACCEPT
+fi
diff --git a/contrib/wpa/wpa_supplicant/examples/p2p-action.sh b/contrib/wpa/wpa_supplicant/examples/p2p-action.sh
new file mode 100755
index 0000000..8759f54
--- /dev/null
+++ b/contrib/wpa/wpa_supplicant/examples/p2p-action.sh
@@ -0,0 +1,83 @@
+#!/bin/sh
+
+IFNAME=$1
+CMD=$2
+
+kill_daemon() {
+ NAME=$1
+ PF=$2
+
+ if [ ! -r $PF ]; then
+ return
+ fi
+
+ PID=`cat $PF`
+ if [ $PID -gt 0 ]; then
+ if ps $PID | grep -q $NAME; then
+ kill $PID
+ fi
+ fi
+ rm $PF
+}
+
+if [ "$CMD" = "P2P-GROUP-STARTED" ]; then
+ GIFNAME=$3
+ if [ "$4" = "GO" ]; then
+ kill_daemon dhclient /var/run/dhclient-$GIFNAME.pid
+ rm /var/run/dhclient.leases-$GIFNAME
+ kill_daemon dnsmasq /var/run/dnsmasq.pid-$GIFNAME
+ ifconfig $GIFNAME 192.168.42.1 up
+ if ! dnsmasq -x /var/run/dnsmasq.pid-$GIFNAME \
+ -i $GIFNAME \
+ -F192.168.42.11,192.168.42.99; then
+ # another dnsmasq instance may be running and blocking us; try to
+ # start with -z to avoid that
+ dnsmasq -x /var/run/dnsmasq.pid-$GIFNAME \
+ -i $GIFNAME \
+ -F192.168.42.11,192.168.42.99 --listen-address 192.168.42.1 -z
+ fi
+ fi
+ if [ "$4" = "client" ]; then
+ kill_daemon dhclient /var/run/dhclient-$GIFNAME.pid
+ rm /var/run/dhclient.leases-$GIFNAME
+ kill_daemon dnsmasq /var/run/dnsmasq.pid-$GIFNAME
+ dhclient -pf /var/run/dhclient-$GIFNAME.pid \
+ -lf /var/run/dhclient.leases-$GIFNAME \
+ -nw \
+ $GIFNAME
+ fi
+fi
+
+if [ "$CMD" = "P2P-GROUP-REMOVED" ]; then
+ GIFNAME=$3
+ if [ "$4" = "GO" ]; then
+ kill_daemon dnsmasq /var/run/dnsmasq.pid-$GIFNAME
+ ifconfig $GIFNAME 0.0.0.0
+ fi
+ if [ "$4" = "client" ]; then
+ kill_daemon dhclient /var/run/dhclient-$GIFNAME.pid
+ rm /var/run/dhclient.leases-$GIFNAME
+ ifconfig $GIFNAME 0.0.0.0
+ fi
+fi
+
+if [ "$CMD" = "P2P-CROSS-CONNECT-ENABLE" ]; then
+ GIFNAME=$3
+ UPLINK=$4
+ # enable NAT/masquarade $GIFNAME -> $UPLINK
+ iptables -P FORWARD DROP
+ iptables -t nat -A POSTROUTING -o $UPLINK -j MASQUERADE
+ iptables -A FORWARD -i $UPLINK -o $GIFNAME -m state --state RELATED,ESTABLISHED -j ACCEPT
+ iptables -A FORWARD -i $GIFNAME -o $UPLINK -j ACCEPT
+ sysctl net.ipv4.ip_forward=1
+fi
+
+if [ "$CMD" = "P2P-CROSS-CONNECT-DISABLE" ]; then
+ GIFNAME=$3
+ UPLINK=$4
+ # disable NAT/masquarade $GIFNAME -> $UPLINK
+ sysctl net.ipv4.ip_forward=0
+ iptables -t nat -D POSTROUTING -o $UPLINK -j MASQUERADE
+ iptables -D FORWARD -i $UPLINK -o $GIFNAME -m state --state RELATED,ESTABLISHED -j ACCEPT
+ iptables -D FORWARD -i $GIFNAME -o $UPLINK -j ACCEPT
+fi
diff --git a/contrib/wpa/wpa_supplicant/examples/p2p/p2p_connect.py b/contrib/wpa/wpa_supplicant/examples/p2p/p2p_connect.py
new file mode 100644
index 0000000..59b0a9d
--- /dev/null
+++ b/contrib/wpa/wpa_supplicant/examples/p2p/p2p_connect.py
@@ -0,0 +1,299 @@
+#!/usr/bin/python
+# Tests p2p_connect
+# Will try to connect to another peer
+# and form a group
+######### MAY NEED TO RUN AS SUDO #############
+
+import dbus
+import sys, os
+import time
+import gobject
+import getopt
+from dbus.mainloop.glib import DBusGMainLoop
+
+
+def usage():
+ print "Usage:"
+ print " %s -i <interface_name> -m <wps_method> \ " \
+ % sys.argv[0]
+ print " -a <addr> [-p <pin>] [-g <go_intent>] \ "
+ print " [-w <wpas_dbus_interface>]"
+ print "Options:"
+ print " -i = interface name"
+ print " -m = wps method"
+ print " -a = peer address"
+ print " -p = pin number (8 digits)"
+ print " -g = group owner intent"
+ print " -w = wpas dbus interface = fi.w1.wpa_supplicant1"
+ print "Example:"
+ print " %s -i wlan0 -a 0015008352c0 -m display -p 12345670" % sys.argv[0]
+
+
+# Required Signals
+def GONegotiationSuccess(status):
+ print "Go Negotiation Success"
+
+def GONegotiationFailure(status):
+ print 'Go Negotiation Failed. Status:'
+ print format(status)
+ os._exit(0)
+
+def GroupStarted(properties):
+ if properties.has_key("group_object"):
+ print 'Group Formation Complete %s' \
+ % properties["group_object"]
+ os._exit(0)
+
+def WpsFailure(status, etc):
+ print "WPS Authentication Failure".format(status)
+ print etc
+ os._exit(0)
+
+class P2P_Connect():
+ # Needed Variables
+ global bus
+ global wpas_object
+ global interface_object
+ global p2p_interface
+ global ifname
+ global wpas
+ global wpas_dbus_interface
+ global timeout
+ global path
+ global wps_method
+ global go_intent
+ global addr
+ global pin
+
+ # Dbus Paths
+ global wpas_dbus_opath
+ global wpas_dbus_interfaces_opath
+ global wpas_dbus_interfaces_interface
+ global wpas_dbus_interfaces_p2pdevice
+
+ # Dictionary of Arguements
+ global p2p_connect_arguements
+
+ # Constructor
+ def __init__(self,ifname,wpas_dbus_interface,addr,
+ pin,wps_method,go_intent):
+ # Initializes variables and threads
+ self.ifname = ifname
+ self.wpas_dbus_interface = wpas_dbus_interface
+ self.wps_method = wps_method
+ self.go_intent = go_intent
+ self.addr = addr
+ self.pin = pin
+
+ # Generating interface/object paths
+ self.wpas_dbus_opath = \
+ "/" + self.wpas_dbus_interface.replace(".","/")
+ self.wpas_wpas_dbus_interfaces_opath = \
+ self.wpas_dbus_opath + "/Interfaces"
+ self.wpas_dbus_interfaces_interface = \
+ self.wpas_dbus_interface + ".Interface"
+ self.wpas_dbus_interfaces_p2pdevice = \
+ self.wpas_dbus_interfaces_interface + ".P2PDevice"
+
+ # Getting interfaces and objects
+ DBusGMainLoop(set_as_default=True)
+ self.bus = dbus.SystemBus()
+ self.wpas_object = self.bus.get_object(
+ self.wpas_dbus_interface,
+ self.wpas_dbus_opath)
+ self.wpas = dbus.Interface(
+ self.wpas_object, self.wpas_dbus_interface)
+
+ # See if wpa_supplicant already knows about this interface
+ self.path = None
+ try:
+ self.path = self.wpas.GetInterface(ifname)
+ except:
+ if not str(exc).startswith(
+ self.wpas_dbus_interface + \
+ ".InterfaceUnknown:"):
+ raise exc
+ try:
+ path = self.wpas.CreateInterface(
+ {'Ifname': ifname, 'Driver': 'test'})
+ time.sleep(1)
+
+ except dbus.DBusException, exc:
+ if not str(exc).startswith(
+ self.wpas_dbus_interface + \
+ ".InterfaceExists:"):
+ raise exc
+
+ # Get Interface and objects
+ self.interface_object = self.bus.get_object(
+ self.wpas_dbus_interface,self.path)
+ self.p2p_interface = dbus.Interface(
+ self.interface_object,
+ self.wpas_dbus_interfaces_p2pdevice)
+
+ # Add signals
+ self.bus.add_signal_receiver(GONegotiationSuccess,
+ dbus_interface=self.wpas_dbus_interfaces_p2pdevice,
+ signal_name="GONegotiationSuccess")
+ self.bus.add_signal_receiver(GONegotiationFailure,
+ dbus_interface=self.wpas_dbus_interfaces_p2pdevice,
+ signal_name="GONegotiationFailure")
+ self.bus.add_signal_receiver(GroupStarted,
+ dbus_interface=self.wpas_dbus_interfaces_p2pdevice,
+ signal_name="GroupStarted")
+ self.bus.add_signal_receiver(WpsFailure,
+ dbus_interface=self.wpas_dbus_interfaces_p2pdevice,
+ signal_name="WpsFailed")
+
+
+ #Constructing all the arguements needed to connect
+ def constructArguements(self):
+ # Adding required arguements
+ self.p2p_connect_arguements = {'wps_method':self.wps_method,
+ 'peer':dbus.ObjectPath(self.path+'/Peers/'+self.addr)}
+
+ # Display requires a pin, and a go intent of 15
+ if (self.wps_method == 'display'):
+ if (self.pin != None):
+ self.p2p_connect_arguements.update({'pin':self.pin})
+ else:
+ print "Error:\n Pin required for wps_method=display"
+ usage()
+ quit()
+
+ if (self.go_intent != None and int(self.go_intent) != 15):
+ print "go_intent overwritten to 15"
+
+ self.go_intent = '15'
+
+ # Keypad requires a pin, and a go intent of less than 15
+ elif (self.wps_method == 'keypad'):
+ if (self.pin != None):
+ self.p2p_connect_arguements.update({'pin':self.pin})
+ else:
+ print "Error:\n Pin required for wps_method=keypad"
+ usage()
+ quit()
+
+ if (self.go_intent != None and int(self.go_intent) == 15):
+ error = "Error :\n Group Owner intent cannot be" + \
+ " 15 for wps_method=keypad"
+ print error
+ usage()
+ quit()
+
+ # Doesn't require pin
+ # for ./wpa_cli, p2p_connect [mac] [pin#], wps_method=keypad
+ elif (self.wps_method == 'pin'):
+ if (self.pin != None):
+ print "pin ignored"
+
+ # No pin is required for pbc so it is ignored
+ elif (self.wps_method == 'pbc'):
+ if (self.pin != None):
+ print "pin ignored"
+
+ else:
+ print "Error:\n wps_method not supported or does not exist"
+ usage()
+ quit()
+
+ # Go_intent is optional for all arguements
+ if (self.go_intent != None):
+ self.p2p_connect_arguements.update(
+ {'go_intent':dbus.Int32(self.go_intent)})
+
+ # Running p2p_connect
+ def run(self):
+ try:
+ result_pin = self.p2p_interface.Connect(
+ self.p2p_connect_arguements)
+
+ except dbus.DBusException, exc:
+ raise exc
+
+ if (self.wps_method == 'pin' and \
+ not self.p2p_connect_arguements.has_key('pin') ):
+ print "Connect return with pin value of %d " % int(result_pin)
+ gobject.MainLoop().run()
+
+if __name__ == "__main__":
+
+ # Required
+ interface_name = None
+ wps_method = None
+ addr = None
+
+ # Conditionally optional
+ pin = None
+
+ # Optional
+ wpas_dbus_interface = 'fi.w1.wpa_supplicant1'
+ go_intent = None
+
+ # Using getopts to handle options
+ try:
+ options, args = getopt.getopt(sys.argv[1:],"hi:m:a:p:g:w:")
+
+ except getopt.GetoptError:
+ usage()
+ quit()
+
+ # If theres a switch, override default option
+ for key, value in options:
+ # Help
+ if (key == "-h"):
+ usage()
+ quit()
+ # Interface Name
+ elif (key == "-i"):
+ interface_name = value
+ # WPS Method
+ elif (key == "-m"):
+ wps_method = value
+ # Address
+ elif (key == "-a"):
+ addr = value
+ # Pin
+ elif (key == "-p"):
+ pin = value
+ # Group Owner Intent
+ elif (key == "-g"):
+ go_intent = value
+ # Dbus interface
+ elif (key == "-w"):
+ wpas_dbus_interface = value
+ else:
+ assert False, "unhandled option"
+
+ # Required Arguements check
+ if (interface_name == None or wps_method == None or addr == None):
+ print "Error:\n Required arguements not specified"
+ usage()
+ quit()
+
+ # Group Owner Intent Check
+ if (go_intent != None and (int(go_intent) > 15 or int(go_intent) < 0) ):
+ print "Error:\n Group Owner Intent must be between 0 and 15 inclusive"
+ usage()
+ quit()
+
+ # Pin Check
+ if (pin != None and len(pin) != 8):
+ print "Error:\n Pin is not 8 digits"
+ usage()
+ quit()
+
+ try:
+ p2p_connect_test = P2P_Connect(interface_name,wpas_dbus_interface,
+ addr,pin,wps_method,go_intent)
+
+ except:
+ print "Error:\n Invalid Arguements"
+ usage()
+ quit()
+
+ p2p_connect_test.constructArguements()
+ p2p_connect_test.run()
+
+ os._exit(0)
diff --git a/contrib/wpa/wpa_supplicant/examples/p2p/p2p_disconnect.py b/contrib/wpa/wpa_supplicant/examples/p2p/p2p_disconnect.py
new file mode 100644
index 0000000..c3e39b3
--- /dev/null
+++ b/contrib/wpa/wpa_supplicant/examples/p2p/p2p_disconnect.py
@@ -0,0 +1,169 @@
+#!/usr/bin/python
+# Tests P2P_Disconnect
+# Will perform disconnect on interface_name
+######### MAY NEED TO RUN AS SUDO #############
+
+import dbus
+import sys, os
+import time
+import gobject
+import threading
+import getopt
+from dbus.mainloop.glib import DBusGMainLoop
+
+def usage():
+ print "Usage:"
+ print " %s -i <interface_name> \ " \
+ % sys.argv[0]
+ print " [-w <wpas_dbus_interface>]"
+ print "Options:"
+ print " -i = interface name"
+ print " -w = wpas dbus interface = fi.w1.wpa_supplicant1"
+ print "Example:"
+ print " %s -i p2p-wlan0-0" % sys.argv[0]
+
+# Required Signals
+def GroupFinished(status, etc):
+ print "Disconnected"
+ os._exit(0)
+
+class P2P_Disconnect (threading.Thread):
+ # Needed Variables
+ global bus
+ global wpas_object
+ global interface_object
+ global p2p_interface
+ global interface_name
+ global wpas
+ global wpas_dbus_interface
+ global path
+ global timeout
+
+ # Dbus Paths
+ global wpas_dbus_opath
+ global wpas_dbus_interfaces_opath
+ global wpas_dbus_interfaces_interface
+ global wpas_dbus_interfaces_p2pdevice
+
+ # Constructor
+ def __init__(self,interface_name,wpas_dbus_interface,timeout):
+ # Initializes variables and threads
+ self.interface_name = interface_name
+ self.wpas_dbus_interface = wpas_dbus_interface
+ self.timeout = timeout
+
+ # Initializes thread and daemon allows for ctrl-c kill
+ threading.Thread.__init__(self)
+ self.daemon = True
+
+ # Generating interface/object paths
+ self.wpas_dbus_opath = "/" + \
+ self.wpas_dbus_interface.replace(".","/")
+ self.wpas_wpas_dbus_interfaces_opath = self.wpas_dbus_opath + \
+ "/Interfaces"
+ self.wpas_dbus_interfaces_interface = \
+ self.wpas_dbus_interface + ".Interface"
+ self.wpas_dbus_interfaces_p2pdevice = \
+ self.wpas_dbus_interfaces_interface \
+ + ".P2PDevice"
+
+ # Getting interfaces and objects
+ DBusGMainLoop(set_as_default=True)
+ self.bus = dbus.SystemBus()
+ self.wpas_object = self.bus.get_object(
+ self.wpas_dbus_interface,
+ self.wpas_dbus_opath)
+ self.wpas = dbus.Interface(self.wpas_object,
+ self.wpas_dbus_interface)
+
+ # Try to see if supplicant knows about interface
+ # If not, throw an exception
+ try:
+ self.path = self.wpas.GetInterface(
+ self.interface_name)
+ except dbus.DBusException, exc:
+ error = 'Error:\n Interface ' + self.interface_name \
+ + ' was not found'
+ print error
+ usage()
+ os._exit(0)
+
+ self.interface_object = self.bus.get_object(
+ self.wpas_dbus_interface, self.path)
+ self.p2p_interface = dbus.Interface(self.interface_object,
+ self.wpas_dbus_interfaces_p2pdevice)
+
+ # Signals
+ self.bus.add_signal_receiver(GroupFinished,
+ dbus_interface=self.wpas_dbus_interfaces_p2pdevice,
+ signal_name="GroupFinished")
+
+ # Runs p2p_disconnect
+ def run(self):
+ # Allows other threads to keep working while MainLoop runs
+ # Required for timeout implementation
+ gobject.MainLoop().get_context().iteration(True)
+ gobject.threads_init()
+ self.p2p_interface.Disconnect()
+ gobject.MainLoop().run()
+
+
+if __name__ == "__main__":
+
+ timeout = 5
+ # Defaults for optional inputs
+ wpas_dbus_interface = 'fi.w1.wpa_supplicant1'
+
+ # interface_name is required
+ interface_name = None
+
+ # Using getopts to handle options
+ try:
+ options, args = getopt.getopt(sys.argv[1:],"hi:w:")
+
+ except getopt.GetoptError:
+ usage()
+ quit()
+
+ # If theres a switch, override default option
+ for key, value in options:
+ # Help
+ if (key == "-h"):
+ usage()
+ quit()
+ # Interface Name
+ elif (key == "-i"):
+ interface_name = value
+ # Dbus interface
+ elif (key == "-w"):
+ wpas_dbus_interface = value
+ else:
+ assert False, "unhandled option"
+
+ # Interface name is required and was not given
+ if (interface_name == None):
+ print "Error:\n interface_name is required"
+ usage()
+ quit()
+
+ # Constructor
+ try:
+ p2p_disconnect_test = P2P_Disconnect(interface_name,
+ wpas_dbus_interface,timeout)
+
+ except:
+ print "Error:\n Invalid wpas_dbus_interface"
+ usage()
+ quit()
+
+ # Start P2P_Disconnect
+ p2p_disconnect_test.start()
+
+ try:
+ time.sleep(int(p2p_disconnect_test.timeout))
+
+ except:
+ pass
+
+ print "Disconnect timed out"
+ quit()
diff --git a/contrib/wpa/wpa_supplicant/examples/p2p/p2p_find.py b/contrib/wpa/wpa_supplicant/examples/p2p/p2p_find.py
new file mode 100644
index 0000000..973d46a
--- /dev/null
+++ b/contrib/wpa/wpa_supplicant/examples/p2p/p2p_find.py
@@ -0,0 +1,192 @@
+#!/usr/bin/python
+# Tests p2p_find
+# Will list all devices found/lost within a time frame (timeout)
+# Then Program will exit
+######### MAY NEED TO RUN AS SUDO #############
+
+import dbus
+import sys, os
+import time
+import gobject
+import threading
+import getopt
+from dbus.mainloop.glib import DBusGMainLoop
+
+def usage():
+ print "Usage:"
+ print " %s -i <interface_name> [-t <timeout>] \ " \
+ % sys.argv[0]
+ print " [-w <wpas_dbus_interface>]"
+ print "Options:"
+ print " -i = interface name"
+ print " -t = timeout = 0s (infinite)"
+ print " -w = wpas dbus interface = fi.w1.wpa_supplicant1"
+ print "Example:"
+ print " %s -i wlan0 -t 10" % sys.argv[0]
+
+# Required Signals
+def deviceFound(devicepath):
+ print "Device found: %s" % (devicepath)
+
+def deviceLost(devicepath):
+ print "Device lost: %s" % (devicepath)
+
+class P2P_Find (threading.Thread):
+ # Needed Variables
+ global bus
+ global wpas_object
+ global interface_object
+ global p2p_interface
+ global interface_name
+ global wpas
+ global wpas_dbus_interface
+ global timeout
+ global path
+
+ # Dbus Paths
+ global wpas_dbus_opath
+ global wpas_dbus_interfaces_opath
+ global wpas_dbus_interfaces_interface
+ global wpas_dbus_interfaces_p2pdevice
+
+ # Constructor
+ def __init__(self,interface_name,wpas_dbus_interface,timeout):
+ # Initializes variables and threads
+ self.timeout = int(timeout)
+ self.interface_name = interface_name
+ self.wpas_dbus_interface = wpas_dbus_interface
+
+ # Initializes thread and daemon allows for ctrl-c kill
+ threading.Thread.__init__(self)
+ self.daemon = True
+
+ # Generating interface/object paths
+ self.wpas_dbus_opath = "/" + \
+ self.wpas_dbus_interface.replace(".","/")
+ self.wpas_wpas_dbus_interfaces_opath = self.wpas_dbus_opath + \
+ "/Interfaces"
+ self.wpas_dbus_interfaces_interface = \
+ self.wpas_dbus_interface + ".Interface"
+ self.wpas_dbus_interfaces_p2pdevice = \
+ self.wpas_dbus_interfaces_interface \
+ + ".P2PDevice"
+
+ # Getting interfaces and objects
+ DBusGMainLoop(set_as_default=True)
+ self.bus = dbus.SystemBus()
+ self.wpas_object = self.bus.get_object(
+ self.wpas_dbus_interface,
+ self.wpas_dbus_opath)
+ self.wpas = dbus.Interface(self.wpas_object,
+ self.wpas_dbus_interface)
+
+ # Try to see if supplicant knows about interface
+ # If not, throw an exception
+ try:
+ self.path = self.wpas.GetInterface(
+ self.interface_name)
+ except dbus.DBusException, exc:
+ error = 'Error:\n Interface ' + self.interface_name \
+ + ' was not found'
+ print error
+ usage()
+ os._exit(0)
+
+ self.interface_object = self.bus.get_object(
+ self.wpas_dbus_interface, self.path)
+ self.p2p_interface = dbus.Interface(self.interface_object,
+ self.wpas_dbus_interfaces_p2pdevice)
+
+ #Adds listeners for find and lost
+ self.bus.add_signal_receiver(deviceFound,
+ dbus_interface=self.wpas_dbus_interfaces_p2pdevice,
+ signal_name="DeviceFound")
+ self.bus.add_signal_receiver(deviceLost,
+ dbus_interface=self.wpas_dbus_interfaces_p2pdevice,
+ signal_name="DeviceLost")
+
+
+ # Sets up p2p_find
+ P2PFindDict = dbus.Dictionary(
+ {'Timeout':int(self.timeout)})
+ self.p2p_interface.Find(P2PFindDict)
+
+ # Run p2p_find
+ def run(self):
+ # Allows other threads to keep working while MainLoop runs
+ # Required for timeout implementation
+ gobject.MainLoop().get_context().iteration(True)
+ gobject.threads_init()
+ gobject.MainLoop().run()
+
+if __name__ == "__main__":
+
+ # Defaults for optional inputs
+ timeout = 0
+ wpas_dbus_interface = 'fi.w1.wpa_supplicant1'
+
+ # interface_name is required
+ interface_name = None
+
+ # Using getopts to handle options
+ try:
+ options, args = getopt.getopt(sys.argv[1:],"hi:t:w:")
+
+ except getopt.GetoptError:
+ usage()
+ quit()
+
+ # If theres a switch, override default option
+ for key, value in options:
+ # Help
+ if (key == "-h"):
+ usage()
+ quit()
+ # Interface Name
+ elif (key == "-i"):
+ interface_name = value
+ # Timeout
+ elif (key == "-t"):
+ if ( int(value) >= 0):
+ timeout = value
+ else:
+ print "Error:\n Timeout cannot be negative"
+ usage()
+ quit()
+ # Dbus interface
+ elif (key == "-w"):
+ wpas_dbus_interface = value
+ else:
+ assert False, "unhandled option"
+
+ # Interface name is required and was not given
+ if (interface_name == None):
+ print "Error:\n interface_name is required"
+ usage()
+ quit()
+
+ # Constructor
+ try:
+ p2p_find_test = P2P_Find(interface_name, wpas_dbus_interface, timeout)
+
+ except:
+ print "Error:\n Invalid wpas_dbus_interface"
+ usage()
+ quit()
+
+ # Start P2P_Find
+ p2p_find_test.start()
+
+ try:
+ # If timeout is 0, then run forever
+ if (timeout == 0):
+ while(True):
+ pass
+ # Else sleep for (timeout)
+ else:
+ time.sleep(p2p_find_test.timeout)
+
+ except:
+ pass
+
+ quit()
diff --git a/contrib/wpa/wpa_supplicant/examples/p2p/p2p_flush.py b/contrib/wpa/wpa_supplicant/examples/p2p/p2p_flush.py
new file mode 100644
index 0000000..ff8509d
--- /dev/null
+++ b/contrib/wpa/wpa_supplicant/examples/p2p/p2p_flush.py
@@ -0,0 +1,168 @@
+#!/usr/bin/python
+# Tests P2P_Flush
+# Will flush the p2p interface
+# Then Program will exit
+######### MAY NEED TO RUN AS SUDO #############
+
+import dbus
+import sys, os
+import time
+import gobject
+import threading
+import getopt
+from dbus.mainloop.glib import DBusGMainLoop
+
+def usage():
+ print "Usage:"
+ print " %s -i <interface_name> \ " \
+ % sys.argv[0]
+ print " [-w <wpas_dbus_interface>]"
+ print "Options:"
+ print " -i = interface name"
+ print " -w = wpas dbus interface = fi.w1.wpa_supplicant1"
+ print "Example:"
+ print " %s -i wlan0" % sys.argv[0]
+
+# Required Signals\
+def deviceLost(devicepath):
+ print "Device lost: %s" % (devicepath)
+
+class P2P_Flush (threading.Thread):
+ # Needed Variables
+ global bus
+ global wpas_object
+ global interface_object
+ global p2p_interface
+ global interface_name
+ global wpas
+ global wpas_dbus_interface
+ global path
+ global timeout
+
+ # Dbus Paths
+ global wpas_dbus_opath
+ global wpas_dbus_interfaces_opath
+ global wpas_dbus_interfaces_interface
+ global wpas_dbus_interfaces_p2pdevice
+
+ # Constructor
+ def __init__(self,interface_name,wpas_dbus_interface,timeout):
+ # Initializes variables and threads
+ self.interface_name = interface_name
+ self.wpas_dbus_interface = wpas_dbus_interface
+ self.timeout = timeout
+
+ # Initializes thread and daemon allows for ctrl-c kill
+ threading.Thread.__init__(self)
+ self.daemon = True
+
+ # Generating interface/object paths
+ self.wpas_dbus_opath = "/" + \
+ self.wpas_dbus_interface.replace(".","/")
+ self.wpas_wpas_dbus_interfaces_opath = self.wpas_dbus_opath + \
+ "/Interfaces"
+ self.wpas_dbus_interfaces_interface = \
+ self.wpas_dbus_interface + ".Interface"
+ self.wpas_dbus_interfaces_p2pdevice = \
+ self.wpas_dbus_interfaces_interface \
+ + ".P2PDevice"
+
+ # Getting interfaces and objects
+ DBusGMainLoop(set_as_default=True)
+ self.bus = dbus.SystemBus()
+ self.wpas_object = self.bus.get_object(
+ self.wpas_dbus_interface,
+ self.wpas_dbus_opath)
+ self.wpas = dbus.Interface(self.wpas_object,
+ self.wpas_dbus_interface)
+
+ # Try to see if supplicant knows about interface
+ # If not, throw an exception
+ try:
+ self.path = self.wpas.GetInterface(
+ self.interface_name)
+ except dbus.DBusException, exc:
+ error = 'Error:\n Interface ' + self.interface_name \
+ + ' was not found'
+ print error
+ usage()
+ os._exit(0)
+
+ self.interface_object = self.bus.get_object(
+ self.wpas_dbus_interface, self.path)
+ self.p2p_interface = dbus.Interface(self.interface_object,
+ self.wpas_dbus_interfaces_p2pdevice)
+
+ # Signals
+ self.bus.add_signal_receiver(deviceLost,
+ dbus_interface=self.wpas_dbus_interfaces_p2pdevice,
+ signal_name="DeviceLost")
+
+ # Runs p2p_flush
+ def run(self):
+ # Allows other threads to keep working while MainLoop runs
+ # Required for timeout implementation
+ gobject.MainLoop().get_context().iteration(True)
+ gobject.threads_init()
+ self.p2p_interface.Flush()
+ gobject.MainLoop().run()
+
+
+if __name__ == "__main__":
+ # Needed to show which devices were lost
+ timeout = 5
+ # Defaults for optional inputs
+ wpas_dbus_interface = 'fi.w1.wpa_supplicant1'
+
+ # interface_name is required
+ interface_name = None
+
+ # Using getopts to handle options
+ try:
+ options, args = getopt.getopt(sys.argv[1:],"hi:w:")
+
+ except getopt.GetoptError:
+ usage()
+ quit()
+
+ # If theres a switch, override default option
+ for key, value in options:
+ # Help
+ if (key == "-h"):
+ usage()
+ quit()
+ # Interface Name
+ elif (key == "-i"):
+ interface_name = value
+ # Dbus interface
+ elif (key == "-w"):
+ wpas_dbus_interface = value
+ else:
+ assert False, "unhandled option"
+
+ # Interface name is required and was not given
+ if (interface_name == None):
+ print "Error:\n interface_name is required"
+ usage()
+ quit()
+
+ # Constructor
+ try:
+ p2p_flush_test = P2P_Flush(interface_name, wpas_dbus_interface,timeout)
+
+ except:
+ print "Error:\n Invalid wpas_dbus_interface"
+ usage()
+ quit()
+
+ # Start P2P_Find
+ p2p_flush_test.start()
+
+ try:
+ time.sleep(int(p2p_flush_test.timeout))
+
+ except:
+ pass
+
+ print "p2p_flush complete"
+ quit()
diff --git a/contrib/wpa/wpa_supplicant/examples/p2p/p2p_group_add.py b/contrib/wpa/wpa_supplicant/examples/p2p/p2p_group_add.py
new file mode 100644
index 0000000..5c8fdaf
--- /dev/null
+++ b/contrib/wpa/wpa_supplicant/examples/p2p/p2p_group_add.py
@@ -0,0 +1,222 @@
+#!/usr/bin/python
+# Tests p2p_group_add
+######### MAY NEED TO RUN AS SUDO #############
+
+import dbus
+import sys, os
+import time
+import gobject
+import getopt
+import threading
+from dbus.mainloop.glib import DBusGMainLoop
+
+def usage():
+ print "Usage:"
+ print " %s -i <interface_name> [-p <persistent>] \ " \
+ % sys.argv[0]
+ print " [-f <frequency>] [-o <group_object_path>] \ "
+ print " [-w <wpas_dbus_interface>]"
+ print "Options:"
+ print " -i = interface name"
+ print " -p = persistant group = 0 (0=false, 1=true)"
+ print " -f = frequency"
+ print " -o = persistent group object path"
+ print " -w = wpas dbus interface = fi.w1.wpa_supplicant1"
+ print "Example:"
+ print " %s -i wlan0" % sys.argv[0]
+
+# Required Signals
+def GroupStarted(properties):
+ if properties.has_key("group_object"):
+ print 'Group Formation Complete %s' \
+ % properties["group_object"]
+ os._exit(0)
+
+def WpsFailure(status, etc):
+ print "WPS Authentication Failure".format(status)
+ print etc
+ os._exit(0)
+
+class P2P_Group_Add (threading.Thread):
+ # Needed Variables
+ global bus
+ global wpas_object
+ global interface_object
+ global p2p_interface
+ global interface_name
+ global wpas
+ global wpas_dbus_interface
+ global path
+ global persistent
+ global frequency
+ global persistent_group_object
+
+ # Dbus Paths
+ global wpas_dbus_opath
+ global wpas_dbus_interfaces_opath
+ global wpas_dbus_interfaces_interface
+ global wpas_dbus_interfaces_p2pdevice
+
+ # Arguements
+ global P2PDictionary
+
+ # Constructor
+ def __init__(self,interface_name,wpas_dbus_interface,persistent,frequency,
+ persistent_group_object):
+ # Initializes variables and threads
+ self.interface_name = interface_name
+ self.wpas_dbus_interface = wpas_dbus_interface
+ self.persistent = persistent
+ self.frequency = frequency
+ self.persistent_group_object = persistent_group_object
+
+ # Initializes thread and daemon allows for ctrl-c kill
+ threading.Thread.__init__(self)
+ self.daemon = True
+
+ # Generating interface/object paths
+ self.wpas_dbus_opath = "/" + \
+ self.wpas_dbus_interface.replace(".","/")
+ self.wpas_wpas_dbus_interfaces_opath = self.wpas_dbus_opath + \
+ "/Interfaces"
+ self.wpas_dbus_interfaces_interface = \
+ self.wpas_dbus_interface + ".Interface"
+ self.wpas_dbus_interfaces_p2pdevice = \
+ self.wpas_dbus_interfaces_interface \
+ + ".P2PDevice"
+
+ # Getting interfaces and objects
+ DBusGMainLoop(set_as_default=True)
+ self.bus = dbus.SystemBus()
+ self.wpas_object = self.bus.get_object(
+ self.wpas_dbus_interface,
+ self.wpas_dbus_opath)
+ self.wpas = dbus.Interface(self.wpas_object,
+ self.wpas_dbus_interface)
+
+ # Try to see if supplicant knows about interface
+ # If not, throw an exception
+ try:
+ self.path = self.wpas.GetInterface(
+ self.interface_name)
+ except dbus.DBusException, exc:
+ error = 'Error:\n Interface ' + self.interface_name \
+ + ' was not found'
+ print error
+ usage()
+ os._exit(0)
+
+ self.interface_object = self.bus.get_object(
+ self.wpas_dbus_interface, self.path)
+ self.p2p_interface = dbus.Interface(self.interface_object,
+ self.wpas_dbus_interfaces_p2pdevice)
+
+ #Adds listeners
+ self.bus.add_signal_receiver(GroupStarted,
+ dbus_interface=self.wpas_dbus_interfaces_p2pdevice,
+ signal_name="GroupStarted")
+ self.bus.add_signal_receiver(WpsFailure,
+ dbus_interface=self.wpas_dbus_interfaces_p2pdevice,
+ signal_name="WpsFailed")
+
+ # Sets up p2p_group_add dictionary
+ def constructArguements(self):
+ self.P2PDictionary = {'persistent':self.persistent}
+
+ if (self.frequency != None):
+ if (int(self.frequency) > 0):
+ self.P2PDictionary.update({'frequency':int(self.frequency)})
+ else:
+ print "Error:\n Frequency must be greater than 0"
+ usage()
+ os._exit(0)
+
+ if (self.persistent_group_object != None):
+ self.P2PDictionary.update({'persistent_group_object':
+ self.persistent_group_object})
+
+ # Run p2p_group_remove
+ def run(self):
+ try:
+ self.p2p_interface.GroupAdd(self.P2PDictionary)
+
+ except:
+ print "Error:\n Could not preform group add"
+ usage()
+ os._exit(0)
+
+ # Allows other threads to keep working while MainLoop runs
+ # Required for timeout implementation
+ gobject.MainLoop().get_context().iteration(True)
+ gobject.threads_init()
+ gobject.MainLoop().run()
+
+
+if __name__ == "__main__":
+
+ # Defaults for optional inputs
+ # 0 = false, 1 = true
+ persistent = False
+ frequency = None
+ persistent_group_object = None
+ wpas_dbus_interface = 'fi.w1.wpa_supplicant1'
+
+ # interface_name is required
+ interface_name = None
+
+ # Using getopts to handle options
+ try:
+ options, args = getopt.getopt(sys.argv[1:],"hi:p:f:o:w:")
+
+ except getopt.GetoptError:
+ usage()
+ quit()
+
+ # If theres a switch, override default option
+ for key, value in options:
+ # Help
+ if (key == "-h"):
+ usage()
+ quit()
+ # Interface Name
+ elif (key == "-i"):
+ interface_name = value
+ # Timeout
+ elif (key == "-p"):
+ if (value == '0'):
+ persistent = False
+ elif (value == '1'):
+ persistent = True
+ else:
+ print "Error:\n Persistent can only be 1 or 0"
+ usage()
+ os._exit(0)
+ # Frequency
+ elif (key == "-f"):
+ frequency = value
+ # Persistent group object path
+ elif (key == "-o"):
+ persistent_group_object = value
+ # Dbus interface
+ elif (key == "-w"):
+ wpas_dbus_interface = value
+ else:
+ assert False, "unhandled option"
+
+ # Interface name is required and was not given
+ if (interface_name == None):
+ print "Error:\n interface_name is required"
+ usage()
+ quit()
+
+ try:
+ p2p_group_add_test = P2P_Group_Add(interface_name,wpas_dbus_interface,
+ persistent,frequency,persistent_group_object)
+ except:
+ print "Error:\n Invalid Arguements"
+
+ p2p_group_add_test.constructArguements()
+ p2p_group_add_test.start()
+ time.sleep(5)
+ print "Error:\n Group formation timed out"
+ os._exit(0)
diff --git a/contrib/wpa/wpa_supplicant/examples/p2p/p2p_invite.py b/contrib/wpa/wpa_supplicant/examples/p2p/p2p_invite.py
new file mode 100644
index 0000000..6deb397
--- /dev/null
+++ b/contrib/wpa/wpa_supplicant/examples/p2p/p2p_invite.py
@@ -0,0 +1,201 @@
+#!/usr/bin/python
+# Tests p2p_invite
+######### MAY NEED TO RUN AS SUDO #############
+
+import dbus
+import sys, os
+import time
+import gobject
+import getopt
+import threading
+from dbus.mainloop.glib import DBusGMainLoop
+
+def usage():
+ print "Usage:"
+ print " %s -i <interface_name> -a <addr> \ " \
+ % sys.argv[0]
+ print " [-o <persistent_group_object>] [-w <wpas_dbus_interface>]"
+ print "Options:"
+ print " -i = interface name"
+ print " -a = address of peer"
+ print " -o = persistent group object path"
+ print " -w = wpas dbus interface = fi.w1.wpa_supplicant1"
+ print "Example:"
+ print " %s -i p2p-wlan0-0 -a 00150083523c" % sys.argv[0]
+
+# Required Signals
+def InvitationResult(invite_result):
+ print "Inviation Result signal :"
+ status = invite_result['status']
+ print "status = ", status
+ if invite_result.has_key('BSSID'):
+ bssid = invite_result['BSSID']
+ print "BSSID = ", hex(bssid[0]) , ":" , \
+ hex(bssid[1]) , ":" , hex(bssid[2]) , ":", \
+ hex(bssid[3]) , ":" , hex(bssid[4]) , ":" , \
+ hex(bssid[5])
+ os._exit(0)
+
+class P2P_Invite (threading.Thread):
+ # Needed Variables
+ global bus
+ global wpas_object
+ global interface_object
+ global p2p_interface
+ global interface_name
+ global wpas
+ global wpas_dbus_interface
+ global path
+ global addr
+ global persistent_group_object
+
+ # Dbus Paths
+ global wpas_dbus_opath
+ global wpas_dbus_interfaces_opath
+ global wpas_dbus_interfaces_interface
+ global wpas_dbus_interfaces_p2pdevice
+
+ # Arguements
+ global P2PDictionary
+
+ # Constructor
+ def __init__(self,interface_name,wpas_dbus_interface,addr,
+ persistent_group_object):
+ # Initializes variables and threads
+ self.interface_name = interface_name
+ self.wpas_dbus_interface = wpas_dbus_interface
+ self.addr = addr
+ self.persistent_group_object = persistent_group_object
+
+ # Initializes thread and daemon allows for ctrl-c kill
+ threading.Thread.__init__(self)
+ self.daemon = True
+
+ # Generating interface/object paths
+ self.wpas_dbus_opath = "/" + \
+ self.wpas_dbus_interface.replace(".","/")
+ self.wpas_wpas_dbus_interfaces_opath = self.wpas_dbus_opath + \
+ "/Interfaces"
+ self.wpas_dbus_interfaces_interface = \
+ self.wpas_dbus_interface + ".Interface"
+ self.wpas_dbus_interfaces_p2pdevice = \
+ self.wpas_dbus_interfaces_interface \
+ + ".P2PDevice"
+
+ # Getting interfaces and objects
+ DBusGMainLoop(set_as_default=True)
+ self.bus = dbus.SystemBus()
+ self.wpas_object = self.bus.get_object(
+ self.wpas_dbus_interface,
+ self.wpas_dbus_opath)
+ self.wpas = dbus.Interface(self.wpas_object,
+ self.wpas_dbus_interface)
+
+ # Try to see if supplicant knows about interface
+ # If not, throw an exception
+ try:
+ self.path = self.wpas.GetInterface(
+ self.interface_name)
+ except dbus.DBusException, exc:
+ error = 'Error:\n Interface ' + self.interface_name \
+ + ' was not found'
+ print error
+ usage()
+ os._exit(0)
+
+ self.interface_object = self.bus.get_object(
+ self.wpas_dbus_interface, self.path)
+ self.p2p_interface = dbus.Interface(self.interface_object,
+ self.wpas_dbus_interfaces_p2pdevice)
+
+ #Adds listeners
+ self.bus.add_signal_receiver(InvitationResult,
+ dbus_interface=self.wpas_dbus_interfaces_p2pdevice,
+ signal_name="InvitationResult")
+
+ # Sets up p2p_invite dictionary
+ def constructArguements(self):
+ self.P2PDictionary = \
+ {'peer':dbus.ObjectPath(self.path+'/Peers/'+self.addr)}
+ if (self.persistent_group_object != None):
+ self.P2PDictionary.update({"persistent_group_object":
+ self.persistent_group_object})
+
+ # Run p2p_invite
+ def run(self):
+ try:
+ self.p2p_interface.Invite(self.P2PDictionary)
+
+ except:
+ print "Error:\n Invalid Arguements"
+ usage()
+ os._exit(0)
+
+ # Allows other threads to keep working while MainLoop runs
+ # Required for timeout implementation
+ gobject.MainLoop().get_context().iteration(True)
+ gobject.threads_init()
+ gobject.MainLoop().run()
+
+if __name__ == "__main__":
+ # Defaults for optional inputs
+ addr = None
+ persistent_group_object = None
+ wpas_dbus_interface = 'fi.w1.wpa_supplicant1'
+
+ # interface_name is required
+ interface_name = None
+
+ # Using getopts to handle options
+ try:
+ options, args = getopt.getopt(sys.argv[1:],"hi:o:w:a:")
+
+ except getopt.GetoptError:
+ usage()
+ quit()
+
+ # If theres a switch, override default option
+ for key, value in options:
+ # Help
+ if (key == "-h"):
+ usage()
+ quit()
+ # Interface Name
+ elif (key == "-i"):
+ interface_name = value
+ elif (key == "-a"):
+ addr = value
+ # Persistent group object path
+ elif (key == "-o"):
+ persistent_group_object = value
+ # Dbus interface
+ elif (key == "-w"):
+ wpas_dbus_interface = value
+ else:
+ assert False, "unhandled option"
+
+ # Interface name is required and was not given
+ if (interface_name == None):
+ print "Error:\n interface_name is required"
+ usage()
+ quit()
+
+ if (addr == None):
+ print "Error:\n peer address is required"
+ usage()
+ quit()
+
+ try:
+ p2p_invite_test = \
+ P2P_Invite(interface_name,wpas_dbus_interface,
+ addr,persistent_group_object)
+ except:
+ print "Error:\n Invalid Arguements"
+ usage()
+ os._exit(1)
+
+ p2p_invite_test.constructArguements()
+ p2p_invite_test.start()
+ time.sleep(10)
+ print "Error:\n p2p_invite timed out"
+ os._exit(0)
diff --git a/contrib/wpa/wpa_supplicant/examples/p2p/p2p_listen.py b/contrib/wpa/wpa_supplicant/examples/p2p/p2p_listen.py
new file mode 100644
index 0000000..bb3c1e4
--- /dev/null
+++ b/contrib/wpa/wpa_supplicant/examples/p2p/p2p_listen.py
@@ -0,0 +1,182 @@
+#!/usr/bin/python
+# Tests P2P_Find
+# Will listen
+# Then Program will exit
+######### MAY NEED TO RUN AS SUDO #############
+
+import dbus
+import sys, os
+import time
+import gobject
+import threading
+import getopt
+from dbus.mainloop.glib import DBusGMainLoop
+
+def usage():
+ print "Usage:"
+ print " %s -i <interface_name> [-t <timeout>] \ " \
+ % sys.argv[0]
+ print " [-w <wpas_dbus_interface>]"
+ print "Options:"
+ print " -i = interface name"
+ print " -t = timeout = 0s (infinite)"
+ print " -w = wpas dbus interface = fi.w1.wpa_supplicant1"
+ print "Example:"
+ print " %s -i wlan0 -t 5" % sys.argv[0]
+
+# Required Signals
+def p2pStateChange(status):
+ print status
+
+class P2P_Listen(threading.Thread):
+ # Needed Variables
+ global bus
+ global wpas_object
+ global interface_object
+ global p2p_interface
+ global interface_name
+ global wpas
+ global wpas_dbus_interface
+ global path
+ global timeout
+
+ # Dbus Paths
+ global wpas_dbus_opath
+ global wpas_dbus_interfaces_opath
+ global wpas_dbus_interfaces_interface
+ global wpas_dbus_interfaces_p2pdevice
+
+ # Constructor
+ def __init__(self,interface_name,wpas_dbus_interface,timeout):
+ # Initializes variables and threads
+ self.timeout = int(timeout)
+ self.interface_name = interface_name
+ self.wpas_dbus_interface = wpas_dbus_interface
+
+ # Initializes thread and daemon allows for ctrl-c kill
+ threading.Thread.__init__(self)
+ self.daemon = True
+
+ # Generating interface/object paths
+ self.wpas_dbus_opath = "/" + \
+ self.wpas_dbus_interface.replace(".","/")
+ self.wpas_wpas_dbus_interfaces_opath = self.wpas_dbus_opath + \
+ "/Interfaces"
+ self.wpas_dbus_interfaces_interface = \
+ self.wpas_dbus_interface + ".Interface"
+ self.wpas_dbus_interfaces_p2pdevice = \
+ self.wpas_dbus_interfaces_interface \
+ + ".P2PDevice"
+
+ # Getting interfaces and objects
+ DBusGMainLoop(set_as_default=True)
+ self.bus = dbus.SystemBus()
+ self.wpas_object = self.bus.get_object(
+ self.wpas_dbus_interface,
+ self.wpas_dbus_opath)
+ self.wpas = dbus.Interface(self.wpas_object,
+ self.wpas_dbus_interface)
+
+ # Try to see if supplicant knows about interface
+ # If not, throw an exception
+ try:
+ self.path = self.wpas.GetInterface(
+ self.interface_name)
+ except dbus.DBusException, exc:
+ error = 'Error:\n Interface ' + self.interface_name \
+ + ' was not found'
+ print error
+ usage()
+ os._exit(0)
+
+ self.interface_object = self.bus.get_object(
+ self.wpas_dbus_interface, self.path)
+ self.p2p_interface = dbus.Interface(self.interface_object,
+ self.wpas_dbus_interfaces_p2pdevice)
+
+ self.bus.add_signal_receiver(p2pStateChange,
+ dbus_interface=self.wpas_dbus_interfaces_p2pdevice,
+ signal_name="P2PStateChanged")
+
+ # Run p2p_find
+ def run(self):
+ # Sets up p2p_listen
+ self.p2p_interface.Listen(int(self.timeout))
+
+ # Allows other threads to keep working while MainLoop runs
+ # Required for timeout implementation
+ gobject.MainLoop().get_context().iteration(True)
+ gobject.threads_init()
+ gobject.MainLoop().run()
+
+if __name__ == "__main__":
+
+ # Defaults for optional inputs
+ timeout = 0
+ wpas_dbus_interface = 'fi.w1.wpa_supplicant1'
+
+ # interface_name is required
+ interface_name = None
+
+ # Using getopts to handle options
+ try:
+ options, args = getopt.getopt(sys.argv[1:],"hi:t:w:")
+
+ except getopt.GetoptError:
+ usage()
+ quit()
+
+ # If theres a switch, override default option
+ for key, value in options:
+ # Help
+ if (key == "-h"):
+ usage()
+ quit()
+ # Interface Name
+ elif (key == "-i"):
+ interface_name = value
+ # Timeout
+ elif (key == "-t"):
+ if ( int(value) >= 0):
+ timeout = value
+ else:
+ print "Error:\n Timeout cannot be negative"
+ usage()
+ quit()
+ # Dbus interface
+ elif (key == "-w"):
+ wpas_dbus_interface = value
+ else:
+ assert False, "unhandled option"
+
+ # Interface name is required and was not given
+ if (interface_name == None):
+ print "Error:\n interface_name is required"
+ usage()
+ quit()
+
+ # Constructor
+ try:
+ p2p_listen_test = P2P_Listen(interface_name, wpas_dbus_interface, timeout)
+
+ except:
+ print "Error:\n Invalid wpas_dbus_interface"
+ usage()
+ quit()
+
+ # Start P2P_Find
+ p2p_listen_test.start()
+
+ try:
+ # If timeout is 0, then run forever
+ if (int(p2p_listen_test.timeout) == 0):
+ while(True):
+ pass
+ # Else sleep for (timeout)
+ else:
+ time.sleep(int(p2p_listen_test.timeout))
+
+ except:
+ pass
+
+ quit()
diff --git a/contrib/wpa/wpa_supplicant/examples/p2p/p2p_stop_find.py b/contrib/wpa/wpa_supplicant/examples/p2p/p2p_stop_find.py
new file mode 100644
index 0000000..f6c03b0
--- /dev/null
+++ b/contrib/wpa/wpa_supplicant/examples/p2p/p2p_stop_find.py
@@ -0,0 +1,174 @@
+#!/usr/bin/python
+# Tests p2p_stop_find
+######### MAY NEED TO RUN AS SUDO #############
+
+import dbus
+import sys, os
+import time
+import gobject
+import threading
+import getopt
+from dbus.mainloop.glib import DBusGMainLoop
+
+def usage():
+ print "Usage:"
+ print " %s -i <interface_name> \ " \
+ % sys.argv[0]
+ print " [-w <wpas_dbus_interface>]"
+ print "Options:"
+ print " -i = interface name"
+ print " -w = wpas dbus interface = fi.w1.wpa_supplicant1"
+ print "Example:"
+ print " %s -i wlan0" % sys.argv[0]
+
+# Required Signals
+def deviceLost(devicepath):
+ print "Device lost: %s" % (devicepath)
+
+def p2pStateChange(status):
+ print status
+ os._exit(0)
+
+class P2P_Stop_Find (threading.Thread):
+ # Needed Variables
+ global bus
+ global wpas_object
+ global interface_object
+ global p2p_interface
+ global interface_name
+ global wpas
+ global wpas_dbus_interface
+ global path
+ global timeout
+
+ # Dbus Paths
+ global wpas_dbus_opath
+ global wpas_dbus_interfaces_opath
+ global wpas_dbus_interfaces_interface
+ global wpas_dbus_interfaces_p2pdevice
+
+ # Constructor
+ def __init__(self,interface_name,wpas_dbus_interface,timeout):
+ # Initializes variables and threads
+ self.interface_name = interface_name
+ self.wpas_dbus_interface = wpas_dbus_interface
+ self.timeout = timeout
+
+ # Initializes thread and daemon allows for ctrl-c kill
+ threading.Thread.__init__(self)
+ self.daemon = True
+
+ # Generating interface/object paths
+ self.wpas_dbus_opath = "/" + \
+ self.wpas_dbus_interface.replace(".","/")
+ self.wpas_wpas_dbus_interfaces_opath = self.wpas_dbus_opath + \
+ "/Interfaces"
+ self.wpas_dbus_interfaces_interface = \
+ self.wpas_dbus_interface + ".Interface"
+ self.wpas_dbus_interfaces_p2pdevice = \
+ self.wpas_dbus_interfaces_interface \
+ + ".P2PDevice"
+
+ # Getting interfaces and objects
+ DBusGMainLoop(set_as_default=True)
+ self.bus = dbus.SystemBus()
+ self.wpas_object = self.bus.get_object(
+ self.wpas_dbus_interface,
+ self.wpas_dbus_opath)
+ self.wpas = dbus.Interface(self.wpas_object,
+ self.wpas_dbus_interface)
+
+ # Try to see if supplicant knows about interface
+ # If not, throw an exception
+ try:
+ self.path = self.wpas.GetInterface(
+ self.interface_name)
+ except dbus.DBusException, exc:
+ error = 'Error:\n Interface ' + self.interface_name \
+ + ' was not found'
+ print error
+ usage()
+ os._exit(0)
+
+ self.interface_object = self.bus.get_object(
+ self.wpas_dbus_interface, self.path)
+ self.p2p_interface = dbus.Interface(self.interface_object,
+ self.wpas_dbus_interfaces_p2pdevice)
+
+ # Signals
+ self.bus.add_signal_receiver(deviceLost,
+ dbus_interface=self.wpas_dbus_interfaces_p2pdevice,
+ signal_name="DeviceLost")
+ self.bus.add_signal_receiver(p2pStateChange,
+ dbus_interface=self.wpas_dbus_interfaces_p2pdevice,
+ signal_name="P2PStateChanged")
+
+ # Runs p2p_stop_find
+ def run(self):
+ # Allows other threads to keep working while MainLoop runs
+ # Required for timeout implementation
+ gobject.MainLoop().get_context().iteration(True)
+ gobject.threads_init()
+ self.p2p_interface.StopFind()
+ gobject.MainLoop().run()
+
+
+if __name__ == "__main__":
+ # Needed because P2PStateChanged signal is not caught
+ timeout = 5
+ # Defaults for optional inputs
+ wpas_dbus_interface = 'fi.w1.wpa_supplicant1'
+
+ # interface_name is required
+ interface_name = None
+
+ # Using getopts to handle options
+ try:
+ options, args = getopt.getopt(sys.argv[1:],"ht:i:w:")
+
+ except getopt.GetoptError:
+ usage()
+ quit()
+
+ # If theres a switch, override default option
+ for key, value in options:
+ # Help
+ if (key == "-h"):
+ usage()
+ quit()
+ # Interface Name
+ elif (key == "-i"):
+ interface_name = value
+ # Dbus interface
+ elif (key == "-w"):
+ wpas_dbus_interface = value
+ else:
+ assert False, "unhandled option"
+
+ # Interface name is required and was not given
+ if (interface_name == None):
+ print "Error:\n interface_name is required"
+ usage()
+ quit()
+
+ # Constructor
+ try:
+ p2p_stop_find_test = P2P_Stop_Find(interface_name,
+ wpas_dbus_interface,timeout)
+
+ except:
+ print "Error:\n Invalid wpas_dbus_interface"
+ usage()
+ quit()
+
+ # Start P2P_Find
+ p2p_stop_find_test.start()
+
+ try:
+ time.sleep(int(p2p_stop_find_test.timeout))
+
+ except:
+ pass
+
+ print "p2p find stopped"
+ quit()
diff --git a/contrib/wpa/wpa_supplicant/examples/udhcpd-p2p.conf b/contrib/wpa/wpa_supplicant/examples/udhcpd-p2p.conf
new file mode 100644
index 0000000..df59094
--- /dev/null
+++ b/contrib/wpa/wpa_supplicant/examples/udhcpd-p2p.conf
@@ -0,0 +1,120 @@
+# Sample udhcpd configuration file (/etc/udhcpd.conf)
+
+# The start and end of the IP lease block
+
+start 192.168.42.20 #default: 192.168.0.20
+end 192.168.42.254 #default: 192.168.0.254
+
+
+# The interface that udhcpd will use
+
+interface wlan2 #default: eth0
+
+
+# The maximim number of leases (includes addressesd reserved
+# by OFFER's, DECLINE's, and ARP conficts
+
+#max_leases 254 #default: 254
+
+
+# If remaining is true (default), udhcpd will store the time
+# remaining for each lease in the udhcpd leases file. This is
+# for embedded systems that cannot keep time between reboots.
+# If you set remaining to no, the absolute time that the lease
+# expires at will be stored in the dhcpd.leases file.
+
+#remaining yes #default: yes
+
+
+# The time period at which udhcpd will write out a dhcpd.leases
+# file. If this is 0, udhcpd will never automatically write a
+# lease file. (specified in seconds)
+
+#auto_time 7200 #default: 7200 (2 hours)
+
+
+# The amount of time that an IP will be reserved (leased) for if a
+# DHCP decline message is received (seconds).
+
+#decline_time 3600 #default: 3600 (1 hour)
+
+
+# The amount of time that an IP will be reserved (leased) for if an
+# ARP conflct occurs. (seconds
+
+#conflict_time 3600 #default: 3600 (1 hour)
+
+
+# How long an offered address is reserved (leased) in seconds
+
+#offer_time 60 #default: 60 (1 minute)
+
+# If a lease to be given is below this value, the full lease time is
+# instead used (seconds).
+
+#min_lease 60 #defult: 60
+
+
+# The location of the leases file
+
+#lease_file /var/lib/misc/udhcpd.leases #defualt: /var/lib/misc/udhcpd.leases
+
+# The location of the pid file
+pidfile /var/run/udhcpd-wlan2.pid #default: /var/run/udhcpd.pid
+
+# Every time udhcpd writes a leases file, the below script will be called.
+# Useful for writing the lease file to flash every few hours.
+
+#notify_file #default: (no script)
+
+#notify_file dumpleases # <--- useful for debugging
+
+# The following are bootp specific options, setable by udhcpd.
+
+#siaddr 192.168.0.22 #default: 0.0.0.0
+
+#sname zorak #default: (none)
+
+#boot_file /var/nfs_root #default: (none)
+
+# The remainer of options are DHCP options and can be specifed with the
+# keyword 'opt' or 'option'. If an option can take multiple items, such
+# as the dns option, they can be listed on the same line, or multiple
+# lines. The only option with a default is 'lease'.
+
+#Examles
+opt dns 192.168.2.1
+option subnet 255.255.255.0
+option domain atherosowl.com
+option lease 864000 # 10 days of seconds
+
+
+# Currently supported options, for more info, see options.c
+#opt subnet
+#opt timezone
+#opt router
+#opt timesvr
+#opt namesvr
+#opt dns
+#opt logsvr
+#opt cookiesvr
+#opt lprsvr
+#opt bootsize
+#opt domain
+#opt swapsvr
+#opt rootpath
+#opt ipttl
+#opt mtu
+#opt broadcast
+#opt wins
+#opt lease
+#opt ntpsrv
+#opt tftp
+#opt bootfile
+
+
+# Static leases map
+#static_lease 00:60:08:11:CE:4E 192.168.0.54
+#static_lease 00:60:08:11:CE:3E 192.168.0.44
+
+
diff --git a/contrib/wpa/wpa_supplicant/examples/wpas-dbus-new-signals.py b/contrib/wpa/wpa_supplicant/examples/wpas-dbus-new-signals.py
index b040e0a..d90ef18 100755
--- a/contrib/wpa/wpa_supplicant/examples/wpas-dbus-new-signals.py
+++ b/contrib/wpa/wpa_supplicant/examples/wpas-dbus-new-signals.py
@@ -59,12 +59,12 @@ def showBss(bss):
dbus_interface=dbus.PROPERTIES_IFACE)
ssid = byte_array_to_string(val)
- val = net_obj.Get(WPAS_DBUS_BSS_INTERFACE, 'WPAIE',
+ val = net_obj.Get(WPAS_DBUS_BSS_INTERFACE, 'WPA',
dbus_interface=dbus.PROPERTIES_IFACE)
wpa = "no"
if val != None:
wpa = "yes"
- val = net_obj.Get(WPAS_DBUS_BSS_INTERFACE, 'RSNIE',
+ val = net_obj.Get(WPAS_DBUS_BSS_INTERFACE, 'RSN',
dbus_interface=dbus.PROPERTIES_IFACE)
wpa2 = "no"
if val != None:
diff --git a/contrib/wpa/wpa_supplicant/examples/wps-ap-cli b/contrib/wpa/wpa_supplicant/examples/wps-ap-cli
new file mode 100755
index 0000000..7c6b0aa
--- /dev/null
+++ b/contrib/wpa/wpa_supplicant/examples/wps-ap-cli
@@ -0,0 +1,78 @@
+#!/bin/sh
+
+CLI=wpa_cli
+
+pbc()
+{
+ echo "Starting PBC mode"
+ echo "Push button on the station within two minutes"
+ if ! $CLI wps_pbc | grep -q OK; then
+ echo "Failed to enable PBC mode"
+ fi
+}
+
+enter_pin()
+{
+ echo "Enter a PIN from a station to be enrolled to the network."
+ read -p "Enrollee PIN: " pin
+ cpin=`$CLI wps_check_pin "$pin" | tail -1`
+ if [ "$cpin" = "FAIL-CHECKSUM" ]; then
+ echo "Checksum digit is not valid"
+ read -p "Do you want to use this PIN (y/n)? " resp
+ case "$resp" in
+ y*)
+ cpin=`echo "$pin" | sed "s/[^1234567890]//g"`
+ ;;
+ *)
+ return 1
+ ;;
+ esac
+ fi
+ if [ "$cpin" = "FAIL" ]; then
+ echo "Invalid PIN: $pin"
+ return 1
+ fi
+ echo "Enabling Enrollee PIN: $cpin"
+ $CLI wps_pin any "$cpin"
+}
+
+show_config()
+{
+ $CLI status wps
+}
+
+main_menu()
+{
+ echo "WPS AP"
+ echo "------"
+ echo "1: Push button (activate PBC)"
+ echo "2: Enter Enrollee PIN"
+ echo "3: Show current configuration"
+ echo "0: Exit wps-ap-cli"
+
+ read -p "Command: " cmd
+
+ case "$cmd" in
+ 1)
+ pbc
+ ;;
+ 2)
+ enter_pin
+ ;;
+ 3)
+ show_config
+ ;;
+ 0)
+ exit 0
+ ;;
+ *)
+ echo "Unknown command: $cmd"
+ ;;
+ esac
+
+ echo
+ main_menu
+}
+
+
+main_menu
diff --git a/contrib/wpa/wpa_supplicant/examples/wps-nfc.py b/contrib/wpa/wpa_supplicant/examples/wps-nfc.py
new file mode 100755
index 0000000..0cfc1f6
--- /dev/null
+++ b/contrib/wpa/wpa_supplicant/examples/wps-nfc.py
@@ -0,0 +1,162 @@
+#!/usr/bin/python
+#
+# Example nfcpy to wpa_supplicant wrapper for WPS NFC operations
+# Copyright (c) 2012, Jouni Malinen <j@w1.fi>
+#
+# This software may be distributed under the terms of the BSD license.
+# See README for more details.
+
+import os
+import sys
+import time
+
+import nfc
+import nfc.ndef
+import nfc.llcp
+import nfc.handover
+
+import wpactrl
+
+wpas_ctrl = '/var/run/wpa_supplicant'
+
+def wpas_connect():
+ ifaces = []
+ if os.path.isdir(wpas_ctrl):
+ try:
+ ifaces = [os.path.join(wpas_ctrl, i) for i in os.listdir(wpas_ctrl)]
+ except OSError, error:
+ print "Could not find wpa_supplicant: ", error
+ return None
+
+ if len(ifaces) < 1:
+ print "No wpa_supplicant control interface found"
+ return None
+
+ for ctrl in ifaces:
+ try:
+ wpas = wpactrl.WPACtrl(ctrl)
+ return wpas
+ except wpactrl.error, error:
+ print "Error: ", error
+ pass
+ return None
+
+
+def wpas_tag_read(message):
+ wpas = wpas_connect()
+ if (wpas == None):
+ return
+ print wpas.request("WPS_NFC_TAG_READ " + message.encode("hex"))
+
+
+def wpas_get_handover_req():
+ wpas = wpas_connect()
+ if (wpas == None):
+ return None
+ return wpas.request("NFC_GET_HANDOVER_REQ NDEF WPS").rstrip().decode("hex")
+
+
+def wpas_put_handover_sel(message):
+ wpas = wpas_connect()
+ if (wpas == None):
+ return
+ print wpas.request("NFC_RX_HANDOVER_SEL " + str(message).encode("hex"))
+
+
+def wps_handover_init(peer):
+ print "Trying to initiate WPS handover"
+
+ data = wpas_get_handover_req()
+ if (data == None):
+ print "Could not get handover request message from wpa_supplicant"
+ return
+ print "Handover request from wpa_supplicant: " + data.encode("hex")
+ message = nfc.ndef.Message(data)
+ print "Parsed handover request: " + message.pretty()
+
+ nfc.llcp.activate(peer);
+ time.sleep(0.5)
+
+ client = nfc.handover.HandoverClient()
+ try:
+ print "Trying handover";
+ client.connect()
+ print "Connected for handover"
+ except nfc.llcp.ConnectRefused:
+ print "Handover connection refused"
+ nfc.llcp.shutdown()
+ client.close()
+ return
+
+ print "Sending handover request"
+
+ if not client.send(message):
+ print "Failed to send handover request"
+
+ print "Receiving handover response"
+ message = client._recv()
+ print "Handover select received"
+ print message.pretty()
+ wpas_put_handover_sel(message)
+
+ print "Remove peer"
+ nfc.llcp.shutdown()
+ client.close()
+ print "Done with handover"
+
+
+def wps_tag_read(tag):
+ if len(tag.ndef.message):
+ message = nfc.ndef.Message(tag.ndef.message)
+ print "message type " + message.type
+
+ for record in message:
+ print "record type " + record.type
+ if record.type == "application/vnd.wfa.wsc":
+ print "WPS tag - send to wpa_supplicant"
+ wpas_tag_read(tag.ndef.message)
+ break
+ else:
+ print "Empty tag"
+
+ print "Remove tag"
+ while tag.is_present:
+ time.sleep(0.1)
+
+
+def main():
+ clf = nfc.ContactlessFrontend()
+
+ try:
+ while True:
+ print "Waiting for a tag or peer to be touched"
+
+ while True:
+ general_bytes = nfc.llcp.startup({})
+ tag = clf.poll(general_bytes)
+ if tag == None:
+ continue
+
+ if isinstance(tag, nfc.DEP):
+ wps_handover_init(tag)
+ break
+
+ if tag.ndef:
+ wps_tag_read(tag)
+ break
+
+ if tag:
+ print "Not an NDEF tag - remove tag"
+ while tag.is_present:
+ time.sleep(0.1)
+ break
+
+ except KeyboardInterrupt:
+ raise SystemExit
+ finally:
+ clf.close()
+
+ raise SystemExit
+
+if __name__ == '__main__':
+ main()
diff --git a/contrib/wpa/wpa_supplicant/gas_query.c b/contrib/wpa/wpa_supplicant/gas_query.c
new file mode 100644
index 0000000..27bcc7a
--- /dev/null
+++ b/contrib/wpa/wpa_supplicant/gas_query.c
@@ -0,0 +1,519 @@
+/*
+ * Generic advertisement service (GAS) query
+ * Copyright (c) 2009, Atheros Communications
+ * Copyright (c) 2011, Qualcomm Atheros
+ *
+ * This software may be distributed under the terms of the BSD license.
+ * See README for more details.
+ */
+
+#include "includes.h"
+
+#include "common.h"
+#include "utils/eloop.h"
+#include "common/ieee802_11_defs.h"
+#include "common/gas.h"
+#include "wpa_supplicant_i.h"
+#include "driver_i.h"
+#include "offchannel.h"
+#include "gas_query.h"
+
+
+/** GAS query timeout in seconds */
+#define GAS_QUERY_TIMEOUT_PERIOD 5
+
+
+/**
+ * struct gas_query_pending - Pending GAS query
+ */
+struct gas_query_pending {
+ struct dl_list list;
+ u8 addr[ETH_ALEN];
+ u8 dialog_token;
+ u8 next_frag_id;
+ unsigned int wait_comeback:1;
+ unsigned int offchannel_tx_started:1;
+ int freq;
+ u16 status_code;
+ struct wpabuf *adv_proto;
+ struct wpabuf *resp;
+ void (*cb)(void *ctx, const u8 *dst, u8 dialog_token,
+ enum gas_query_result result,
+ const struct wpabuf *adv_proto,
+ const struct wpabuf *resp, u16 status_code);
+ void *ctx;
+};
+
+/**
+ * struct gas_query - Internal GAS query data
+ */
+struct gas_query {
+ struct wpa_supplicant *wpa_s;
+ struct dl_list pending; /* struct gas_query_pending */
+};
+
+
+static void gas_query_tx_comeback_timeout(void *eloop_data, void *user_ctx);
+static void gas_query_timeout(void *eloop_data, void *user_ctx);
+
+
+/**
+ * gas_query_init - Initialize GAS query component
+ * @wpa_s: Pointer to wpa_supplicant data
+ * Returns: Pointer to GAS query data or %NULL on failure
+ */
+struct gas_query * gas_query_init(struct wpa_supplicant *wpa_s)
+{
+ struct gas_query *gas;
+
+ gas = os_zalloc(sizeof(*gas));
+ if (gas == NULL)
+ return NULL;
+
+ gas->wpa_s = wpa_s;
+ dl_list_init(&gas->pending);
+
+ return gas;
+}
+
+
+static void gas_query_done(struct gas_query *gas,
+ struct gas_query_pending *query,
+ enum gas_query_result result)
+{
+ if (query->offchannel_tx_started)
+ offchannel_send_action_done(gas->wpa_s);
+ eloop_cancel_timeout(gas_query_tx_comeback_timeout, gas, query);
+ eloop_cancel_timeout(gas_query_timeout, gas, query);
+ dl_list_del(&query->list);
+ query->cb(query->ctx, query->addr, query->dialog_token, result,
+ query->adv_proto, query->resp, query->status_code);
+ wpabuf_free(query->adv_proto);
+ wpabuf_free(query->resp);
+ os_free(query);
+}
+
+
+/**
+ * gas_query_deinit - Deinitialize GAS query component
+ * @gas: GAS query data from gas_query_init()
+ */
+void gas_query_deinit(struct gas_query *gas)
+{
+ struct gas_query_pending *query, *next;
+
+ if (gas == NULL)
+ return;
+
+ dl_list_for_each_safe(query, next, &gas->pending,
+ struct gas_query_pending, list)
+ gas_query_done(gas, query, GAS_QUERY_DELETED_AT_DEINIT);
+
+ os_free(gas);
+}
+
+
+static struct gas_query_pending *
+gas_query_get_pending(struct gas_query *gas, const u8 *addr, u8 dialog_token)
+{
+ struct gas_query_pending *q;
+ dl_list_for_each(q, &gas->pending, struct gas_query_pending, list) {
+ if (os_memcmp(q->addr, addr, ETH_ALEN) == 0 &&
+ q->dialog_token == dialog_token)
+ return q;
+ }
+ return NULL;
+}
+
+
+static int gas_query_append(struct gas_query_pending *query, const u8 *data,
+ size_t len)
+{
+ if (wpabuf_resize(&query->resp, len) < 0) {
+ wpa_printf(MSG_DEBUG, "GAS: No memory to store the response");
+ return -1;
+ }
+ wpabuf_put_data(query->resp, data, len);
+ return 0;
+}
+
+
+static int gas_query_tx(struct gas_query *gas, struct gas_query_pending *query,
+ struct wpabuf *req)
+{
+ int res;
+ wpa_printf(MSG_DEBUG, "GAS: Send action frame to " MACSTR " len=%u "
+ "freq=%d", MAC2STR(query->addr),
+ (unsigned int) wpabuf_len(req), query->freq);
+ res = offchannel_send_action(gas->wpa_s, query->freq, query->addr,
+ gas->wpa_s->own_addr, query->addr,
+ wpabuf_head(req), wpabuf_len(req), 1000,
+ NULL, 0);
+ if (res == 0)
+ query->offchannel_tx_started = 1;
+ return res;
+}
+
+
+static void gas_query_tx_comeback_req(struct gas_query *gas,
+ struct gas_query_pending *query)
+{
+ struct wpabuf *req;
+
+ req = gas_build_comeback_req(query->dialog_token);
+ if (req == NULL) {
+ gas_query_done(gas, query, GAS_QUERY_INTERNAL_ERROR);
+ return;
+ }
+
+ if (gas_query_tx(gas, query, req) < 0) {
+ wpa_printf(MSG_DEBUG, "GAS: Failed to send Action frame to "
+ MACSTR, MAC2STR(query->addr));
+ gas_query_done(gas, query, GAS_QUERY_INTERNAL_ERROR);
+ }
+
+ wpabuf_free(req);
+}
+
+
+static void gas_query_tx_comeback_timeout(void *eloop_data, void *user_ctx)
+{
+ struct gas_query *gas = eloop_data;
+ struct gas_query_pending *query = user_ctx;
+
+ wpa_printf(MSG_DEBUG, "GAS: Comeback timeout for request to " MACSTR,
+ MAC2STR(query->addr));
+ gas_query_tx_comeback_req(gas, query);
+}
+
+
+static void gas_query_tx_comeback_req_delay(struct gas_query *gas,
+ struct gas_query_pending *query,
+ u16 comeback_delay)
+{
+ unsigned int secs, usecs;
+
+ secs = (comeback_delay * 1024) / 1000000;
+ usecs = comeback_delay * 1024 - secs * 1000000;
+ wpa_printf(MSG_DEBUG, "GAS: Send comeback request to " MACSTR
+ " in %u secs %u usecs", MAC2STR(query->addr), secs, usecs);
+ eloop_cancel_timeout(gas_query_tx_comeback_timeout, gas, query);
+ eloop_register_timeout(secs, usecs, gas_query_tx_comeback_timeout,
+ gas, query);
+}
+
+
+static void gas_query_rx_initial(struct gas_query *gas,
+ struct gas_query_pending *query,
+ const u8 *adv_proto, const u8 *resp,
+ size_t len, u16 comeback_delay)
+{
+ wpa_printf(MSG_DEBUG, "GAS: Received initial response from "
+ MACSTR " (dialog_token=%u comeback_delay=%u)",
+ MAC2STR(query->addr), query->dialog_token, comeback_delay);
+
+ query->adv_proto = wpabuf_alloc_copy(adv_proto, 2 + adv_proto[1]);
+ if (query->adv_proto == NULL) {
+ gas_query_done(gas, query, GAS_QUERY_INTERNAL_ERROR);
+ return;
+ }
+
+ if (comeback_delay) {
+ query->wait_comeback = 1;
+ gas_query_tx_comeback_req_delay(gas, query, comeback_delay);
+ return;
+ }
+
+ /* Query was completed without comeback mechanism */
+ if (gas_query_append(query, resp, len) < 0) {
+ gas_query_done(gas, query, GAS_QUERY_INTERNAL_ERROR);
+ return;
+ }
+
+ gas_query_done(gas, query, GAS_QUERY_SUCCESS);
+}
+
+
+static void gas_query_rx_comeback(struct gas_query *gas,
+ struct gas_query_pending *query,
+ const u8 *adv_proto, const u8 *resp,
+ size_t len, u8 frag_id, u8 more_frags,
+ u16 comeback_delay)
+{
+ wpa_printf(MSG_DEBUG, "GAS: Received comeback response from "
+ MACSTR " (dialog_token=%u frag_id=%u more_frags=%u "
+ "comeback_delay=%u)",
+ MAC2STR(query->addr), query->dialog_token, frag_id,
+ more_frags, comeback_delay);
+
+ if ((size_t) 2 + adv_proto[1] != wpabuf_len(query->adv_proto) ||
+ os_memcmp(adv_proto, wpabuf_head(query->adv_proto),
+ wpabuf_len(query->adv_proto)) != 0) {
+ wpa_printf(MSG_DEBUG, "GAS: Advertisement Protocol changed "
+ "between initial and comeback response from "
+ MACSTR, MAC2STR(query->addr));
+ gas_query_done(gas, query, GAS_QUERY_PEER_ERROR);
+ return;
+ }
+
+ if (comeback_delay) {
+ if (frag_id) {
+ wpa_printf(MSG_DEBUG, "GAS: Invalid comeback response "
+ "with non-zero frag_id and comeback_delay "
+ "from " MACSTR, MAC2STR(query->addr));
+ gas_query_done(gas, query, GAS_QUERY_PEER_ERROR);
+ return;
+ }
+ gas_query_tx_comeback_req_delay(gas, query, comeback_delay);
+ return;
+ }
+
+ if (frag_id != query->next_frag_id) {
+ wpa_printf(MSG_DEBUG, "GAS: Unexpected frag_id in response "
+ "from " MACSTR, MAC2STR(query->addr));
+ gas_query_done(gas, query, GAS_QUERY_PEER_ERROR);
+ return;
+ }
+ query->next_frag_id++;
+
+ if (gas_query_append(query, resp, len) < 0) {
+ gas_query_done(gas, query, GAS_QUERY_INTERNAL_ERROR);
+ return;
+ }
+
+ if (more_frags) {
+ gas_query_tx_comeback_req(gas, query);
+ return;
+ }
+
+ gas_query_done(gas, query, GAS_QUERY_SUCCESS);
+}
+
+
+/**
+ * gas_query_rx - Indicate reception of a Public Action frame
+ * @gas: GAS query data from gas_query_init()
+ * @da: Destination MAC address of the Action frame
+ * @sa: Source MAC address of the Action frame
+ * @bssid: BSSID of the Action frame
+ * @data: Payload of the Action frame
+ * @len: Length of @data
+ * @freq: Frequency (in MHz) on which the frame was received
+ * Returns: 0 if the Public Action frame was a GAS frame or -1 if not
+ */
+int gas_query_rx(struct gas_query *gas, const u8 *da, const u8 *sa,
+ const u8 *bssid, const u8 *data, size_t len, int freq)
+{
+ struct gas_query_pending *query;
+ u8 action, dialog_token, frag_id = 0, more_frags = 0;
+ u16 comeback_delay, resp_len;
+ const u8 *pos, *adv_proto;
+
+ if (gas == NULL || len < 4)
+ return -1;
+
+ pos = data;
+ action = *pos++;
+ dialog_token = *pos++;
+
+ if (action != WLAN_PA_GAS_INITIAL_RESP &&
+ action != WLAN_PA_GAS_COMEBACK_RESP)
+ return -1; /* Not a GAS response */
+
+ query = gas_query_get_pending(gas, sa, dialog_token);
+ if (query == NULL) {
+ wpa_printf(MSG_DEBUG, "GAS: No pending query found for " MACSTR
+ " dialog token %u", MAC2STR(sa), dialog_token);
+ return -1;
+ }
+
+ if (query->wait_comeback && action == WLAN_PA_GAS_INITIAL_RESP) {
+ wpa_printf(MSG_DEBUG, "GAS: Unexpected initial response from "
+ MACSTR " dialog token %u when waiting for comeback "
+ "response", MAC2STR(sa), dialog_token);
+ return 0;
+ }
+
+ if (!query->wait_comeback && action == WLAN_PA_GAS_COMEBACK_RESP) {
+ wpa_printf(MSG_DEBUG, "GAS: Unexpected comeback response from "
+ MACSTR " dialog token %u when waiting for initial "
+ "response", MAC2STR(sa), dialog_token);
+ return 0;
+ }
+
+ query->status_code = WPA_GET_LE16(pos);
+ pos += 2;
+
+ if (query->status_code != WLAN_STATUS_SUCCESS) {
+ wpa_printf(MSG_DEBUG, "GAS: Query to " MACSTR " dialog token "
+ "%u failed - status code %u",
+ MAC2STR(sa), dialog_token, query->status_code);
+ gas_query_done(gas, query, GAS_QUERY_FAILURE);
+ return 0;
+ }
+
+ if (action == WLAN_PA_GAS_COMEBACK_RESP) {
+ if (pos + 1 > data + len)
+ return 0;
+ frag_id = *pos & 0x7f;
+ more_frags = (*pos & 0x80) >> 7;
+ pos++;
+ }
+
+ /* Comeback Delay */
+ if (pos + 2 > data + len)
+ return 0;
+ comeback_delay = WPA_GET_LE16(pos);
+ pos += 2;
+
+ /* Advertisement Protocol element */
+ if (pos + 2 > data + len || pos + 2 + pos[1] > data + len) {
+ wpa_printf(MSG_DEBUG, "GAS: No room for Advertisement "
+ "Protocol element in the response from " MACSTR,
+ MAC2STR(sa));
+ return 0;
+ }
+
+ if (*pos != WLAN_EID_ADV_PROTO) {
+ wpa_printf(MSG_DEBUG, "GAS: Unexpected Advertisement "
+ "Protocol element ID %u in response from " MACSTR,
+ *pos, MAC2STR(sa));
+ return 0;
+ }
+
+ adv_proto = pos;
+ pos += 2 + pos[1];
+
+ /* Query Response Length */
+ if (pos + 2 > data + len) {
+ wpa_printf(MSG_DEBUG, "GAS: No room for GAS Response Length");
+ return 0;
+ }
+ resp_len = WPA_GET_LE16(pos);
+ pos += 2;
+
+ if (pos + resp_len > data + len) {
+ wpa_printf(MSG_DEBUG, "GAS: Truncated Query Response in "
+ "response from " MACSTR, MAC2STR(sa));
+ return 0;
+ }
+
+ if (pos + resp_len < data + len) {
+ wpa_printf(MSG_DEBUG, "GAS: Ignore %u octets of extra data "
+ "after Query Response from " MACSTR,
+ (unsigned int) (data + len - pos - resp_len),
+ MAC2STR(sa));
+ }
+
+ if (action == WLAN_PA_GAS_COMEBACK_RESP)
+ gas_query_rx_comeback(gas, query, adv_proto, pos, resp_len,
+ frag_id, more_frags, comeback_delay);
+ else
+ gas_query_rx_initial(gas, query, adv_proto, pos, resp_len,
+ comeback_delay);
+
+ return 0;
+}
+
+
+static void gas_query_timeout(void *eloop_data, void *user_ctx)
+{
+ struct gas_query *gas = eloop_data;
+ struct gas_query_pending *query = user_ctx;
+
+ wpa_printf(MSG_DEBUG, "GAS: No response received for query to " MACSTR,
+ MAC2STR(query->addr));
+ gas_query_done(gas, query, GAS_QUERY_TIMEOUT);
+}
+
+
+static int gas_query_dialog_token_available(struct gas_query *gas,
+ const u8 *dst, u8 dialog_token)
+{
+ struct gas_query_pending *q;
+ dl_list_for_each(q, &gas->pending, struct gas_query_pending, list) {
+ if (os_memcmp(dst, q->addr, ETH_ALEN) == 0 &&
+ dialog_token == q->dialog_token)
+ return 0;
+ }
+
+ return 1;
+}
+
+
+/**
+ * gas_query_req - Request a GAS query
+ * @gas: GAS query data from gas_query_init()
+ * @dst: Destination MAC address for the query
+ * @freq: Frequency (in MHz) for the channel on which to send the query
+ * @req: GAS query payload
+ * @cb: Callback function for reporting GAS query result and response
+ * @ctx: Context pointer to use with the @cb call
+ * Returns: dialog token (>= 0) on success or -1 on failure
+ */
+int gas_query_req(struct gas_query *gas, const u8 *dst, int freq,
+ struct wpabuf *req,
+ void (*cb)(void *ctx, const u8 *dst, u8 dialog_token,
+ enum gas_query_result result,
+ const struct wpabuf *adv_proto,
+ const struct wpabuf *resp, u16 status_code),
+ void *ctx)
+{
+ struct gas_query_pending *query;
+ int dialog_token;
+
+ if (wpabuf_len(req) < 3)
+ return -1;
+
+ for (dialog_token = 0; dialog_token < 256; dialog_token++) {
+ if (gas_query_dialog_token_available(gas, dst, dialog_token))
+ break;
+ }
+ if (dialog_token == 256)
+ return -1; /* Too many pending queries */
+
+ query = os_zalloc(sizeof(*query));
+ if (query == NULL)
+ return -1;
+
+ os_memcpy(query->addr, dst, ETH_ALEN);
+ query->dialog_token = dialog_token;
+ query->freq = freq;
+ query->cb = cb;
+ query->ctx = ctx;
+ dl_list_add(&gas->pending, &query->list);
+
+ *(wpabuf_mhead_u8(req) + 2) = dialog_token;
+
+ wpa_printf(MSG_DEBUG, "GAS: Starting request for " MACSTR
+ " dialog_token %u", MAC2STR(dst), dialog_token);
+ if (gas_query_tx(gas, query, req) < 0) {
+ wpa_printf(MSG_DEBUG, "GAS: Failed to send Action frame to "
+ MACSTR, MAC2STR(query->addr));
+ dl_list_del(&query->list);
+ os_free(query);
+ return -1;
+ }
+
+ eloop_register_timeout(GAS_QUERY_TIMEOUT_PERIOD, 0, gas_query_timeout,
+ gas, query);
+
+ return dialog_token;
+}
+
+
+/**
+ * gas_query_cancel - Cancel a pending GAS query
+ * @gas: GAS query data from gas_query_init()
+ * @dst: Destination MAC address for the query
+ * @dialog_token: Dialog token from gas_query_req()
+ */
+void gas_query_cancel(struct gas_query *gas, const u8 *dst, u8 dialog_token)
+{
+ struct gas_query_pending *query;
+
+ query = gas_query_get_pending(gas, dst, dialog_token);
+ if (query)
+ gas_query_done(gas, query, GAS_QUERY_CANCELLED);
+
+}
diff --git a/contrib/wpa/wpa_supplicant/gas_query.h b/contrib/wpa/wpa_supplicant/gas_query.h
new file mode 100644
index 0000000..5c3d161
--- /dev/null
+++ b/contrib/wpa/wpa_supplicant/gas_query.h
@@ -0,0 +1,58 @@
+/*
+ * Generic advertisement service (GAS) query
+ * Copyright (c) 2009, Atheros Communications
+ * Copyright (c) 2011, Qualcomm Atheros
+ *
+ * This software may be distributed under the terms of the BSD license.
+ * See README for more details.
+ */
+
+#ifndef GAS_QUERY_H
+#define GAS_QUERY_H
+
+struct gas_query;
+
+#ifdef CONFIG_GAS
+
+struct gas_query * gas_query_init(struct wpa_supplicant *wpa_s);
+void gas_query_deinit(struct gas_query *gas);
+int gas_query_rx(struct gas_query *gas, const u8 *da, const u8 *sa,
+ const u8 *bssid, const u8 *data, size_t len, int freq);
+
+/**
+ * enum gas_query_result - GAS query result
+ */
+enum gas_query_result {
+ GAS_QUERY_SUCCESS,
+ GAS_QUERY_FAILURE,
+ GAS_QUERY_TIMEOUT,
+ GAS_QUERY_PEER_ERROR,
+ GAS_QUERY_INTERNAL_ERROR,
+ GAS_QUERY_CANCELLED,
+ GAS_QUERY_DELETED_AT_DEINIT
+};
+
+int gas_query_req(struct gas_query *gas, const u8 *dst, int freq,
+ struct wpabuf *req,
+ void (*cb)(void *ctx, const u8 *dst, u8 dialog_token,
+ enum gas_query_result result,
+ const struct wpabuf *adv_proto,
+ const struct wpabuf *resp, u16 status_code),
+ void *ctx);
+void gas_query_cancel(struct gas_query *gas, const u8 *dst, u8 dialog_token);
+
+#else /* CONFIG_GAS */
+
+static inline struct gas_query * gas_query_init(struct wpa_supplicant *wpa_s)
+{
+ return (void *) 1;
+}
+
+static inline void gas_query_deinit(struct gas_query *gas)
+{
+}
+
+#endif /* CONFIG_GAS */
+
+
+#endif /* GAS_QUERY_H */
diff --git a/contrib/wpa/wpa_supplicant/hs20_supplicant.c b/contrib/wpa/wpa_supplicant/hs20_supplicant.c
new file mode 100644
index 0000000..1404241
--- /dev/null
+++ b/contrib/wpa/wpa_supplicant/hs20_supplicant.c
@@ -0,0 +1,185 @@
+/*
+ * Copyright (c) 2009, Atheros Communications, Inc.
+ * Copyright (c) 2011-2012, Qualcomm Atheros, Inc.
+ *
+ * This software may be distributed under the terms of the BSD license.
+ * See README for more details.
+ */
+
+#include "includes.h"
+
+#include "common.h"
+#include "eloop.h"
+#include "common/ieee802_11_common.h"
+#include "common/ieee802_11_defs.h"
+#include "common/gas.h"
+#include "common/wpa_ctrl.h"
+#include "wpa_supplicant_i.h"
+#include "driver_i.h"
+#include "config.h"
+#include "bss.h"
+#include "gas_query.h"
+#include "interworking.h"
+#include "hs20_supplicant.h"
+
+
+void wpas_hs20_add_indication(struct wpabuf *buf)
+{
+ wpabuf_put_u8(buf, WLAN_EID_VENDOR_SPECIFIC);
+ wpabuf_put_u8(buf, 5);
+ wpabuf_put_be24(buf, OUI_WFA);
+ wpabuf_put_u8(buf, HS20_INDICATION_OUI_TYPE);
+ wpabuf_put_u8(buf, 0x00); /* Hotspot Configuration */
+}
+
+
+struct wpabuf * hs20_build_anqp_req(u32 stypes, const u8 *payload,
+ size_t payload_len)
+{
+ struct wpabuf *buf;
+ u8 *len_pos;
+
+ buf = gas_anqp_build_initial_req(0, 100 + payload_len);
+ if (buf == NULL)
+ return NULL;
+
+ len_pos = gas_anqp_add_element(buf, ANQP_VENDOR_SPECIFIC);
+ wpabuf_put_be24(buf, OUI_WFA);
+ wpabuf_put_u8(buf, HS20_ANQP_OUI_TYPE);
+ if (stypes == BIT(HS20_STYPE_NAI_HOME_REALM_QUERY)) {
+ wpabuf_put_u8(buf, HS20_STYPE_NAI_HOME_REALM_QUERY);
+ wpabuf_put_u8(buf, 0); /* Reserved */
+ if (payload)
+ wpabuf_put_data(buf, payload, payload_len);
+ } else {
+ u8 i;
+ wpabuf_put_u8(buf, HS20_STYPE_QUERY_LIST);
+ wpabuf_put_u8(buf, 0); /* Reserved */
+ for (i = 0; i < 32; i++) {
+ if (stypes & BIT(i))
+ wpabuf_put_u8(buf, i);
+ }
+ }
+ gas_anqp_set_element_len(buf, len_pos);
+
+ gas_anqp_set_len(buf);
+
+ return buf;
+}
+
+
+int hs20_anqp_send_req(struct wpa_supplicant *wpa_s, const u8 *dst, u32 stypes,
+ const u8 *payload, size_t payload_len)
+{
+ struct wpabuf *buf;
+ int ret = 0;
+ int freq;
+ struct wpa_bss *bss;
+ int res;
+
+ freq = wpa_s->assoc_freq;
+ bss = wpa_bss_get_bssid(wpa_s, dst);
+ if (bss) {
+ wpa_bss_anqp_unshare_alloc(bss);
+ freq = bss->freq;
+ }
+ if (freq <= 0)
+ return -1;
+
+ wpa_printf(MSG_DEBUG, "HS20: ANQP Query Request to " MACSTR " for "
+ "subtypes 0x%x", MAC2STR(dst), stypes);
+
+ buf = hs20_build_anqp_req(stypes, payload, payload_len);
+ if (buf == NULL)
+ return -1;
+
+ res = gas_query_req(wpa_s->gas, dst, freq, buf, anqp_resp_cb, wpa_s);
+ if (res < 0) {
+ wpa_printf(MSG_DEBUG, "ANQP: Failed to send Query Request");
+ ret = -1;
+ } else
+ wpa_printf(MSG_DEBUG, "ANQP: Query started with dialog token "
+ "%u", res);
+
+ wpabuf_free(buf);
+ return ret;
+}
+
+
+void hs20_parse_rx_hs20_anqp_resp(struct wpa_supplicant *wpa_s,
+ const u8 *sa, const u8 *data, size_t slen)
+{
+ const u8 *pos = data;
+ u8 subtype;
+ struct wpa_bss *bss = wpa_bss_get_bssid(wpa_s, sa);
+ struct wpa_bss_anqp *anqp = NULL;
+
+ if (slen < 2)
+ return;
+
+ if (bss)
+ anqp = bss->anqp;
+
+ subtype = *pos++;
+ slen--;
+
+ pos++; /* Reserved */
+ slen--;
+
+ switch (subtype) {
+ case HS20_STYPE_CAPABILITY_LIST:
+ wpa_msg(wpa_s, MSG_INFO, "RX-HS20-ANQP " MACSTR
+ " HS Capability List", MAC2STR(sa));
+ wpa_hexdump_ascii(MSG_DEBUG, "HS Capability List", pos, slen);
+ break;
+ case HS20_STYPE_OPERATOR_FRIENDLY_NAME:
+ wpa_msg(wpa_s, MSG_INFO, "RX-HS20-ANQP " MACSTR
+ " Operator Friendly Name", MAC2STR(sa));
+ wpa_hexdump_ascii(MSG_DEBUG, "oper friendly name", pos, slen);
+ if (anqp) {
+ wpabuf_free(anqp->hs20_operator_friendly_name);
+ anqp->hs20_operator_friendly_name =
+ wpabuf_alloc_copy(pos, slen);
+ }
+ break;
+ case HS20_STYPE_WAN_METRICS:
+ wpa_hexdump(MSG_DEBUG, "WAN Metrics", pos, slen);
+ if (slen < 13) {
+ wpa_dbg(wpa_s, MSG_DEBUG, "HS 2.0: Too short WAN "
+ "Metrics value from " MACSTR, MAC2STR(sa));
+ break;
+ }
+ wpa_msg(wpa_s, MSG_INFO, "RX-HS20-ANQP " MACSTR
+ " WAN Metrics %02x:%u:%u:%u:%u:%u", MAC2STR(sa),
+ pos[0], WPA_GET_LE32(pos + 1), WPA_GET_LE32(pos + 5),
+ pos[9], pos[10], WPA_GET_LE16(pos + 11));
+ if (anqp) {
+ wpabuf_free(anqp->hs20_wan_metrics);
+ anqp->hs20_wan_metrics = wpabuf_alloc_copy(pos, slen);
+ }
+ break;
+ case HS20_STYPE_CONNECTION_CAPABILITY:
+ wpa_msg(wpa_s, MSG_INFO, "RX-HS20-ANQP " MACSTR
+ " Connection Capability", MAC2STR(sa));
+ wpa_hexdump_ascii(MSG_DEBUG, "conn capability", pos, slen);
+ if (anqp) {
+ wpabuf_free(anqp->hs20_connection_capability);
+ anqp->hs20_connection_capability =
+ wpabuf_alloc_copy(pos, slen);
+ }
+ break;
+ case HS20_STYPE_OPERATING_CLASS:
+ wpa_msg(wpa_s, MSG_INFO, "RX-HS20-ANQP " MACSTR
+ " Operating Class", MAC2STR(sa));
+ wpa_hexdump_ascii(MSG_DEBUG, "Operating Class", pos, slen);
+ if (anqp) {
+ wpabuf_free(anqp->hs20_operating_class);
+ anqp->hs20_operating_class =
+ wpabuf_alloc_copy(pos, slen);
+ }
+ break;
+ default:
+ wpa_printf(MSG_DEBUG, "HS20: Unsupported subtype %u", subtype);
+ break;
+ }
+}
diff --git a/contrib/wpa/wpa_supplicant/hs20_supplicant.h b/contrib/wpa/wpa_supplicant/hs20_supplicant.h
new file mode 100644
index 0000000..6eb3926
--- /dev/null
+++ b/contrib/wpa/wpa_supplicant/hs20_supplicant.h
@@ -0,0 +1,20 @@
+/*
+ * Copyright (c) 2011-2012, Qualcomm Atheros, Inc.
+ *
+ * This software may be distributed under the terms of the BSD license.
+ * See README for more details.
+ */
+
+#ifndef HS20_SUPPLICANT_H
+#define HS20_SUPPLICANT_H
+
+void wpas_hs20_add_indication(struct wpabuf *buf);
+
+int hs20_anqp_send_req(struct wpa_supplicant *wpa_s, const u8 *dst, u32 stypes,
+ const u8 *payload, size_t payload_len);
+struct wpabuf * hs20_build_anqp_req(u32 stypes, const u8 *payload,
+ size_t payload_len);
+void hs20_parse_rx_hs20_anqp_resp(struct wpa_supplicant *wpa_s,
+ const u8 *sa, const u8 *data, size_t slen);
+
+#endif /* HS20_SUPPLICANT_H */
diff --git a/contrib/wpa/wpa_supplicant/ibss_rsn.c b/contrib/wpa/wpa_supplicant/ibss_rsn.c
index 0e33253..046f181 100644
--- a/contrib/wpa/wpa_supplicant/ibss_rsn.c
+++ b/contrib/wpa/wpa_supplicant/ibss_rsn.c
@@ -2,14 +2,8 @@
* wpa_supplicant - IBSS RSN
* Copyright (c) 2009, Jouni Malinen <j@w1.fi>
*
- * This program is free software; you can redistribute it and/or modify
- * it under the terms of the GNU General Public License version 2 as
- * published by the Free Software Foundation.
- *
- * Alternatively, this software may be distributed under the terms of BSD
- * license.
- *
- * See README and COPYING for more details.
+ * This software may be distributed under the terms of the BSD license.
+ * See README for more details.
*/
#include "includes.h"
@@ -24,6 +18,18 @@
#include "ibss_rsn.h"
+static struct ibss_rsn_peer * ibss_rsn_get_peer(struct ibss_rsn *ibss_rsn,
+ const u8 *addr)
+{
+ struct ibss_rsn_peer *peer;
+
+ for (peer = ibss_rsn->peers; peer; peer = peer->next)
+ if (os_memcmp(addr, peer->addr, ETH_ALEN) == 0)
+ break;
+ return peer;
+}
+
+
static void ibss_rsn_free(struct ibss_rsn_peer *peer)
{
wpa_auth_sta_deinit(peer->auth);
@@ -39,6 +45,13 @@ static void supp_set_state(void *ctx, enum wpa_states state)
}
+static enum wpa_states supp_get_state(void *ctx)
+{
+ struct ibss_rsn_peer *peer = ctx;
+ return peer->supp_state;
+}
+
+
static int supp_ether_send(void *ctx, const u8 *dest, u16 proto, const u8 *buf,
size_t len)
{
@@ -125,6 +138,8 @@ static int supp_set_key(void *ctx, enum wpa_alg alg,
}
}
+ if (is_broadcast_ether_addr(addr))
+ addr = peer->addr;
return wpa_drv_set_key(peer->ibss_rsn->wpa_s, alg, addr, key_idx,
set_tx, seq, seq_len, key, key_len);
}
@@ -153,8 +168,14 @@ static void supp_cancel_auth_timeout(void *ctx)
}
-int ibss_rsn_supp_init(struct ibss_rsn_peer *peer, const u8 *own_addr,
- const u8 *psk)
+static void supp_deauthenticate(void * ctx, int reason_code)
+{
+ wpa_printf(MSG_DEBUG, "SUPP: %s (TODO)", __func__);
+}
+
+
+static int ibss_rsn_supp_init(struct ibss_rsn_peer *peer, const u8 *own_addr,
+ const u8 *psk)
{
struct wpa_sm_ctx *ctx = os_zalloc(sizeof(*ctx));
if (ctx == NULL)
@@ -163,6 +184,7 @@ int ibss_rsn_supp_init(struct ibss_rsn_peer *peer, const u8 *own_addr,
ctx->ctx = peer;
ctx->msg_ctx = peer->ibss_rsn->wpa_s;
ctx->set_state = supp_set_state;
+ ctx->get_state = supp_get_state;
ctx->ether_send = supp_ether_send;
ctx->get_beacon_ie = supp_get_beacon_ie;
ctx->alloc_eapol = supp_alloc_eapol;
@@ -170,6 +192,7 @@ int ibss_rsn_supp_init(struct ibss_rsn_peer *peer, const u8 *own_addr,
ctx->get_network_ctx = supp_get_network_ctx;
ctx->mlme_setprotection = supp_mlme_setprotection;
ctx->cancel_auth_timeout = supp_cancel_auth_timeout;
+ ctx->deauthenticate = supp_deauthenticate;
peer->supp = wpa_sm_init(ctx);
if (peer->supp == NULL) {
wpa_printf(MSG_DEBUG, "SUPP: wpa_sm_init() failed");
@@ -273,6 +296,71 @@ static int auth_set_key(void *ctx, int vlan_id, enum wpa_alg alg,
}
+static int auth_for_each_sta(void *ctx, int (*cb)(struct wpa_state_machine *sm,
+ void *ctx),
+ void *cb_ctx)
+{
+ struct ibss_rsn *ibss_rsn = ctx;
+ struct ibss_rsn_peer *peer;
+
+ wpa_printf(MSG_DEBUG, "AUTH: for_each_sta");
+
+ for (peer = ibss_rsn->peers; peer; peer = peer->next) {
+ if (peer->auth && cb(peer->auth, cb_ctx))
+ return 1;
+ }
+
+ return 0;
+}
+
+
+static void ibss_set_sta_authorized(struct ibss_rsn *ibss_rsn,
+ struct ibss_rsn_peer *peer, int authorized)
+{
+ int res;
+
+ if (authorized) {
+ res = wpa_drv_sta_set_flags(ibss_rsn->wpa_s, peer->addr,
+ WPA_STA_AUTHORIZED,
+ WPA_STA_AUTHORIZED, ~0);
+ wpa_printf(MSG_DEBUG, "AUTH: " MACSTR " authorizing port",
+ MAC2STR(peer->addr));
+ } else {
+ res = wpa_drv_sta_set_flags(ibss_rsn->wpa_s, peer->addr,
+ 0, 0, ~WPA_STA_AUTHORIZED);
+ wpa_printf(MSG_DEBUG, "AUTH: " MACSTR " unauthorizing port",
+ MAC2STR(peer->addr));
+ }
+
+ if (res && errno != ENOENT) {
+ wpa_printf(MSG_DEBUG, "Could not set station " MACSTR " flags "
+ "for kernel driver (errno=%d)",
+ MAC2STR(peer->addr), errno);
+ }
+}
+
+
+static void auth_set_eapol(void *ctx, const u8 *addr,
+ wpa_eapol_variable var, int value)
+{
+ struct ibss_rsn *ibss_rsn = ctx;
+ struct ibss_rsn_peer *peer = ibss_rsn_get_peer(ibss_rsn, addr);
+
+ if (peer == NULL)
+ return;
+
+ switch (var) {
+ case WPA_EAPOL_authorized:
+ ibss_set_sta_authorized(ibss_rsn, peer, value);
+ break;
+ default:
+ /* do not handle any other event */
+ wpa_printf(MSG_DEBUG, "AUTH: eapol event not handled %d", var);
+ break;
+ }
+}
+
+
static int ibss_rsn_auth_init_group(struct ibss_rsn *ibss_rsn,
const u8 *own_addr)
{
@@ -288,13 +376,16 @@ static int ibss_rsn_auth_init_group(struct ibss_rsn *ibss_rsn,
conf.rsn_pairwise = WPA_CIPHER_CCMP;
conf.wpa_group = WPA_CIPHER_CCMP;
conf.eapol_version = 2;
+ conf.wpa_group_rekey = 600;
os_memset(&cb, 0, sizeof(cb));
cb.ctx = ibss_rsn;
cb.logger = auth_logger;
+ cb.set_eapol = auth_set_eapol;
cb.send_eapol = auth_send_eapol;
cb.get_psk = auth_get_psk;
cb.set_key = auth_set_key;
+ cb.for_each_sta = auth_for_each_sta;
ibss_rsn->auth_group = wpa_init(own_addr, &conf, &cb);
if (ibss_rsn->auth_group == NULL) {
@@ -302,6 +393,8 @@ static int ibss_rsn_auth_init_group(struct ibss_rsn *ibss_rsn,
return -1;
}
+ wpa_init_keys(ibss_rsn->auth_group);
+
return 0;
}
@@ -341,6 +434,16 @@ int ibss_rsn_start(struct ibss_rsn *ibss_rsn, const u8 *addr)
{
struct ibss_rsn_peer *peer;
+ if (ibss_rsn == NULL)
+ return -1;
+
+ if (ibss_rsn_get_peer(ibss_rsn, addr)) {
+ wpa_printf(MSG_DEBUG, "RSN: IBSS Authenticator and Supplicant "
+ "for peer " MACSTR " already running",
+ MAC2STR(addr));
+ return 0;
+ }
+
wpa_printf(MSG_DEBUG, "RSN: Starting IBSS Authenticator and "
"Supplicant for peer " MACSTR, MAC2STR(addr));
@@ -369,6 +472,46 @@ int ibss_rsn_start(struct ibss_rsn *ibss_rsn, const u8 *addr)
}
+void ibss_rsn_stop(struct ibss_rsn *ibss_rsn, const u8 *peermac)
+{
+ struct ibss_rsn_peer *peer, *prev;
+
+ if (ibss_rsn == NULL)
+ return;
+
+ if (peermac == NULL) {
+ /* remove all peers */
+ wpa_printf(MSG_DEBUG, "%s: Remove all peers", __func__);
+ peer = ibss_rsn->peers;
+ while (peer) {
+ prev = peer;
+ peer = peer->next;
+ ibss_rsn_free(prev);
+ ibss_rsn->peers = peer;
+ }
+ } else {
+ /* remove specific peer */
+ wpa_printf(MSG_DEBUG, "%s: Remove specific peer " MACSTR,
+ __func__, MAC2STR(peermac));
+
+ for (prev = NULL, peer = ibss_rsn->peers; peer != NULL;
+ prev = peer, peer = peer->next) {
+ if (os_memcmp(peermac, peer->addr, ETH_ALEN) == 0) {
+ if (prev == NULL)
+ ibss_rsn->peers = peer->next;
+ else
+ prev->next = peer->next;
+ ibss_rsn_free(peer);
+ wpa_printf(MSG_DEBUG, "%s: Successfully "
+ "removed a specific peer",
+ __func__);
+ break;
+ }
+ }
+ }
+}
+
+
struct ibss_rsn * ibss_rsn_init(struct wpa_supplicant *wpa_s)
{
struct ibss_rsn *ibss_rsn;
@@ -483,11 +626,12 @@ int ibss_rsn_rx_eapol(struct ibss_rsn *ibss_rsn, const u8 *src_addr,
{
struct ibss_rsn_peer *peer;
- for (peer = ibss_rsn->peers; peer; peer = peer->next) {
- if (os_memcmp(src_addr, peer->addr, ETH_ALEN) == 0)
- return ibss_rsn_process_rx_eapol(ibss_rsn, peer,
- buf, len);
- }
+ if (ibss_rsn == NULL)
+ return -1;
+
+ peer = ibss_rsn_get_peer(ibss_rsn, src_addr);
+ if (peer)
+ return ibss_rsn_process_rx_eapol(ibss_rsn, peer, buf, len);
if (ibss_rsn_eapol_dst_supp(buf, len) > 0) {
/*
@@ -506,5 +650,7 @@ int ibss_rsn_rx_eapol(struct ibss_rsn *ibss_rsn, const u8 *src_addr,
void ibss_rsn_set_psk(struct ibss_rsn *ibss_rsn, const u8 *psk)
{
+ if (ibss_rsn == NULL)
+ return;
os_memcpy(ibss_rsn->psk, psk, PMK_LEN);
}
diff --git a/contrib/wpa/wpa_supplicant/ibss_rsn.h b/contrib/wpa/wpa_supplicant/ibss_rsn.h
index 11e63ad..1da94ab 100644
--- a/contrib/wpa/wpa_supplicant/ibss_rsn.h
+++ b/contrib/wpa/wpa_supplicant/ibss_rsn.h
@@ -2,14 +2,8 @@
* wpa_supplicant - IBSS RSN
* Copyright (c) 2009, Jouni Malinen <j@w1.fi>
*
- * This program is free software; you can redistribute it and/or modify
- * it under the terms of the GNU General Public License version 2 as
- * published by the Free Software Foundation.
- *
- * Alternatively, this software may be distributed under the terms of BSD
- * license.
- *
- * See README and COPYING for more details.
+ * This software may be distributed under the terms of the BSD license.
+ * See README for more details.
*/
#ifndef IBSS_RSN_H
@@ -42,6 +36,7 @@ struct ibss_rsn {
struct ibss_rsn * ibss_rsn_init(struct wpa_supplicant *wpa_s);
void ibss_rsn_deinit(struct ibss_rsn *ibss_rsn);
int ibss_rsn_start(struct ibss_rsn *ibss_rsn, const u8 *addr);
+void ibss_rsn_stop(struct ibss_rsn *ibss_rsn, const u8 *peermac);
int ibss_rsn_rx_eapol(struct ibss_rsn *ibss_rsn, const u8 *src_addr,
const u8 *buf, size_t len);
void ibss_rsn_set_psk(struct ibss_rsn *ibss_rsn, const u8 *psk);
diff --git a/contrib/wpa/wpa_supplicant/interworking.c b/contrib/wpa/wpa_supplicant/interworking.c
new file mode 100644
index 0000000..b8a8bb2
--- /dev/null
+++ b/contrib/wpa/wpa_supplicant/interworking.c
@@ -0,0 +1,2107 @@
+/*
+ * Interworking (IEEE 802.11u)
+ * Copyright (c) 2011-2012, Qualcomm Atheros, Inc.
+ *
+ * This software may be distributed under the terms of the BSD license.
+ * See README for more details.
+ */
+
+#include "includes.h"
+
+#include "common.h"
+#include "common/ieee802_11_defs.h"
+#include "common/gas.h"
+#include "common/wpa_ctrl.h"
+#include "utils/pcsc_funcs.h"
+#include "utils/eloop.h"
+#include "drivers/driver.h"
+#include "eap_common/eap_defs.h"
+#include "eap_peer/eap.h"
+#include "eap_peer/eap_methods.h"
+#include "wpa_supplicant_i.h"
+#include "config.h"
+#include "config_ssid.h"
+#include "bss.h"
+#include "scan.h"
+#include "notify.h"
+#include "gas_query.h"
+#include "hs20_supplicant.h"
+#include "interworking.h"
+
+
+#if defined(EAP_SIM) | defined(EAP_SIM_DYNAMIC)
+#define INTERWORKING_3GPP
+#else
+#if defined(EAP_AKA) | defined(EAP_AKA_DYNAMIC)
+#define INTERWORKING_3GPP
+#else
+#if defined(EAP_AKA_PRIME) | defined(EAP_AKA_PRIME_DYNAMIC)
+#define INTERWORKING_3GPP
+#endif
+#endif
+#endif
+
+static void interworking_next_anqp_fetch(struct wpa_supplicant *wpa_s);
+
+
+static void interworking_reconnect(struct wpa_supplicant *wpa_s)
+{
+ if (wpa_s->wpa_state >= WPA_AUTHENTICATING) {
+ wpa_supplicant_cancel_sched_scan(wpa_s);
+ wpa_supplicant_deauthenticate(wpa_s,
+ WLAN_REASON_DEAUTH_LEAVING);
+ }
+ wpa_s->disconnected = 0;
+ wpa_s->reassociate = 1;
+
+ if (wpa_s->last_scan_res_used > 0) {
+ struct os_time now;
+ os_get_time(&now);
+ if (now.sec - wpa_s->last_scan.sec <= 5) {
+ wpa_printf(MSG_DEBUG, "Interworking: Old scan results "
+ "are fresh - connect without new scan");
+ if (wpas_select_network_from_last_scan(wpa_s) >= 0)
+ return;
+ }
+ }
+
+ wpa_supplicant_req_scan(wpa_s, 0, 0);
+}
+
+
+static struct wpabuf * anqp_build_req(u16 info_ids[], size_t num_ids,
+ struct wpabuf *extra)
+{
+ struct wpabuf *buf;
+ size_t i;
+ u8 *len_pos;
+
+ buf = gas_anqp_build_initial_req(0, 4 + num_ids * 2 +
+ (extra ? wpabuf_len(extra) : 0));
+ if (buf == NULL)
+ return NULL;
+
+ len_pos = gas_anqp_add_element(buf, ANQP_QUERY_LIST);
+ for (i = 0; i < num_ids; i++)
+ wpabuf_put_le16(buf, info_ids[i]);
+ gas_anqp_set_element_len(buf, len_pos);
+ if (extra)
+ wpabuf_put_buf(buf, extra);
+
+ gas_anqp_set_len(buf);
+
+ return buf;
+}
+
+
+static void interworking_anqp_resp_cb(void *ctx, const u8 *dst,
+ u8 dialog_token,
+ enum gas_query_result result,
+ const struct wpabuf *adv_proto,
+ const struct wpabuf *resp,
+ u16 status_code)
+{
+ struct wpa_supplicant *wpa_s = ctx;
+
+ anqp_resp_cb(wpa_s, dst, dialog_token, result, adv_proto, resp,
+ status_code);
+ interworking_next_anqp_fetch(wpa_s);
+}
+
+
+static int cred_with_roaming_consortium(struct wpa_supplicant *wpa_s)
+{
+ struct wpa_cred *cred;
+
+ for (cred = wpa_s->conf->cred; cred; cred = cred->next) {
+ if (cred->roaming_consortium_len)
+ return 1;
+ }
+ return 0;
+}
+
+
+static int cred_with_3gpp(struct wpa_supplicant *wpa_s)
+{
+ struct wpa_cred *cred;
+
+ for (cred = wpa_s->conf->cred; cred; cred = cred->next) {
+ if (cred->pcsc || cred->imsi)
+ return 1;
+ }
+ return 0;
+}
+
+
+static int cred_with_nai_realm(struct wpa_supplicant *wpa_s)
+{
+ struct wpa_cred *cred;
+
+ for (cred = wpa_s->conf->cred; cred; cred = cred->next) {
+ if (cred->pcsc || cred->imsi)
+ continue;
+ if (!cred->eap_method)
+ return 1;
+ if (cred->realm && cred->roaming_consortium_len == 0)
+ return 1;
+ }
+ return 0;
+}
+
+
+static int cred_with_domain(struct wpa_supplicant *wpa_s)
+{
+ struct wpa_cred *cred;
+
+ for (cred = wpa_s->conf->cred; cred; cred = cred->next) {
+ if (cred->domain || cred->pcsc || cred->imsi)
+ return 1;
+ }
+ return 0;
+}
+
+
+static int additional_roaming_consortiums(struct wpa_bss *bss)
+{
+ const u8 *ie;
+ ie = wpa_bss_get_ie(bss, WLAN_EID_ROAMING_CONSORTIUM);
+ if (ie == NULL || ie[1] == 0)
+ return 0;
+ return ie[2]; /* Number of ANQP OIs */
+}
+
+
+static void interworking_continue_anqp(void *eloop_ctx, void *sock_ctx)
+{
+ struct wpa_supplicant *wpa_s = eloop_ctx;
+ interworking_next_anqp_fetch(wpa_s);
+}
+
+
+static int interworking_anqp_send_req(struct wpa_supplicant *wpa_s,
+ struct wpa_bss *bss)
+{
+ struct wpabuf *buf;
+ int ret = 0;
+ int res;
+ u16 info_ids[8];
+ size_t num_info_ids = 0;
+ struct wpabuf *extra = NULL;
+ int all = wpa_s->fetch_all_anqp;
+
+ wpa_printf(MSG_DEBUG, "Interworking: ANQP Query Request to " MACSTR,
+ MAC2STR(bss->bssid));
+
+ info_ids[num_info_ids++] = ANQP_CAPABILITY_LIST;
+ if (all) {
+ info_ids[num_info_ids++] = ANQP_VENUE_NAME;
+ info_ids[num_info_ids++] = ANQP_NETWORK_AUTH_TYPE;
+ }
+ if (all || (cred_with_roaming_consortium(wpa_s) &&
+ additional_roaming_consortiums(bss)))
+ info_ids[num_info_ids++] = ANQP_ROAMING_CONSORTIUM;
+ if (all)
+ info_ids[num_info_ids++] = ANQP_IP_ADDR_TYPE_AVAILABILITY;
+ if (all || cred_with_nai_realm(wpa_s))
+ info_ids[num_info_ids++] = ANQP_NAI_REALM;
+ if (all || cred_with_3gpp(wpa_s))
+ info_ids[num_info_ids++] = ANQP_3GPP_CELLULAR_NETWORK;
+ if (all || cred_with_domain(wpa_s))
+ info_ids[num_info_ids++] = ANQP_DOMAIN_NAME;
+ wpa_hexdump(MSG_DEBUG, "Interworking: ANQP Query info",
+ (u8 *) info_ids, num_info_ids * 2);
+
+#ifdef CONFIG_HS20
+ if (wpa_bss_get_vendor_ie(bss, HS20_IE_VENDOR_TYPE)) {
+ u8 *len_pos;
+
+ extra = wpabuf_alloc(100);
+ if (!extra)
+ return -1;
+
+ len_pos = gas_anqp_add_element(extra, ANQP_VENDOR_SPECIFIC);
+ wpabuf_put_be24(extra, OUI_WFA);
+ wpabuf_put_u8(extra, HS20_ANQP_OUI_TYPE);
+ wpabuf_put_u8(extra, HS20_STYPE_QUERY_LIST);
+ wpabuf_put_u8(extra, 0); /* Reserved */
+ wpabuf_put_u8(extra, HS20_STYPE_CAPABILITY_LIST);
+ if (all) {
+ wpabuf_put_u8(extra,
+ HS20_STYPE_OPERATOR_FRIENDLY_NAME);
+ wpabuf_put_u8(extra, HS20_STYPE_WAN_METRICS);
+ wpabuf_put_u8(extra, HS20_STYPE_CONNECTION_CAPABILITY);
+ wpabuf_put_u8(extra, HS20_STYPE_OPERATING_CLASS);
+ }
+ gas_anqp_set_element_len(extra, len_pos);
+ }
+#endif /* CONFIG_HS20 */
+
+ buf = anqp_build_req(info_ids, num_info_ids, extra);
+ wpabuf_free(extra);
+ if (buf == NULL)
+ return -1;
+
+ res = gas_query_req(wpa_s->gas, bss->bssid, bss->freq, buf,
+ interworking_anqp_resp_cb, wpa_s);
+ if (res < 0) {
+ wpa_printf(MSG_DEBUG, "ANQP: Failed to send Query Request");
+ ret = -1;
+ eloop_register_timeout(0, 0, interworking_continue_anqp, wpa_s,
+ NULL);
+ } else
+ wpa_printf(MSG_DEBUG, "ANQP: Query started with dialog token "
+ "%u", res);
+
+ wpabuf_free(buf);
+ return ret;
+}
+
+
+struct nai_realm_eap {
+ u8 method;
+ u8 inner_method;
+ enum nai_realm_eap_auth_inner_non_eap inner_non_eap;
+ u8 cred_type;
+ u8 tunneled_cred_type;
+};
+
+struct nai_realm {
+ u8 encoding;
+ char *realm;
+ u8 eap_count;
+ struct nai_realm_eap *eap;
+};
+
+
+static void nai_realm_free(struct nai_realm *realms, u16 count)
+{
+ u16 i;
+
+ if (realms == NULL)
+ return;
+ for (i = 0; i < count; i++) {
+ os_free(realms[i].eap);
+ os_free(realms[i].realm);
+ }
+ os_free(realms);
+}
+
+
+static const u8 * nai_realm_parse_eap(struct nai_realm_eap *e, const u8 *pos,
+ const u8 *end)
+{
+ u8 elen, auth_count, a;
+ const u8 *e_end;
+
+ if (pos + 3 > end) {
+ wpa_printf(MSG_DEBUG, "No room for EAP Method fixed fields");
+ return NULL;
+ }
+
+ elen = *pos++;
+ if (pos + elen > end || elen < 2) {
+ wpa_printf(MSG_DEBUG, "No room for EAP Method subfield");
+ return NULL;
+ }
+ e_end = pos + elen;
+ e->method = *pos++;
+ auth_count = *pos++;
+ wpa_printf(MSG_DEBUG, "EAP Method: len=%u method=%u auth_count=%u",
+ elen, e->method, auth_count);
+
+ for (a = 0; a < auth_count; a++) {
+ u8 id, len;
+
+ if (pos + 2 > end || pos + 2 + pos[1] > end) {
+ wpa_printf(MSG_DEBUG, "No room for Authentication "
+ "Parameter subfield");
+ return NULL;
+ }
+
+ id = *pos++;
+ len = *pos++;
+
+ switch (id) {
+ case NAI_REALM_EAP_AUTH_NON_EAP_INNER_AUTH:
+ if (len < 1)
+ break;
+ e->inner_non_eap = *pos;
+ if (e->method != EAP_TYPE_TTLS)
+ break;
+ switch (*pos) {
+ case NAI_REALM_INNER_NON_EAP_PAP:
+ wpa_printf(MSG_DEBUG, "EAP-TTLS/PAP");
+ break;
+ case NAI_REALM_INNER_NON_EAP_CHAP:
+ wpa_printf(MSG_DEBUG, "EAP-TTLS/CHAP");
+ break;
+ case NAI_REALM_INNER_NON_EAP_MSCHAP:
+ wpa_printf(MSG_DEBUG, "EAP-TTLS/MSCHAP");
+ break;
+ case NAI_REALM_INNER_NON_EAP_MSCHAPV2:
+ wpa_printf(MSG_DEBUG, "EAP-TTLS/MSCHAPV2");
+ break;
+ }
+ break;
+ case NAI_REALM_EAP_AUTH_INNER_AUTH_EAP_METHOD:
+ if (len < 1)
+ break;
+ e->inner_method = *pos;
+ wpa_printf(MSG_DEBUG, "Inner EAP method: %u",
+ e->inner_method);
+ break;
+ case NAI_REALM_EAP_AUTH_CRED_TYPE:
+ if (len < 1)
+ break;
+ e->cred_type = *pos;
+ wpa_printf(MSG_DEBUG, "Credential Type: %u",
+ e->cred_type);
+ break;
+ case NAI_REALM_EAP_AUTH_TUNNELED_CRED_TYPE:
+ if (len < 1)
+ break;
+ e->tunneled_cred_type = *pos;
+ wpa_printf(MSG_DEBUG, "Tunneled EAP Method Credential "
+ "Type: %u", e->tunneled_cred_type);
+ break;
+ default:
+ wpa_printf(MSG_DEBUG, "Unsupported Authentication "
+ "Parameter: id=%u len=%u", id, len);
+ wpa_hexdump(MSG_DEBUG, "Authentication Parameter "
+ "Value", pos, len);
+ break;
+ }
+
+ pos += len;
+ }
+
+ return e_end;
+}
+
+
+static const u8 * nai_realm_parse_realm(struct nai_realm *r, const u8 *pos,
+ const u8 *end)
+{
+ u16 len;
+ const u8 *f_end;
+ u8 realm_len, e;
+
+ if (end - pos < 4) {
+ wpa_printf(MSG_DEBUG, "No room for NAI Realm Data "
+ "fixed fields");
+ return NULL;
+ }
+
+ len = WPA_GET_LE16(pos); /* NAI Realm Data field Length */
+ pos += 2;
+ if (pos + len > end || len < 3) {
+ wpa_printf(MSG_DEBUG, "No room for NAI Realm Data "
+ "(len=%u; left=%u)",
+ len, (unsigned int) (end - pos));
+ return NULL;
+ }
+ f_end = pos + len;
+
+ r->encoding = *pos++;
+ realm_len = *pos++;
+ if (pos + realm_len > f_end) {
+ wpa_printf(MSG_DEBUG, "No room for NAI Realm "
+ "(len=%u; left=%u)",
+ realm_len, (unsigned int) (f_end - pos));
+ return NULL;
+ }
+ wpa_hexdump_ascii(MSG_DEBUG, "NAI Realm", pos, realm_len);
+ r->realm = os_malloc(realm_len + 1);
+ if (r->realm == NULL)
+ return NULL;
+ os_memcpy(r->realm, pos, realm_len);
+ r->realm[realm_len] = '\0';
+ pos += realm_len;
+
+ if (pos + 1 > f_end) {
+ wpa_printf(MSG_DEBUG, "No room for EAP Method Count");
+ return NULL;
+ }
+ r->eap_count = *pos++;
+ wpa_printf(MSG_DEBUG, "EAP Count: %u", r->eap_count);
+ if (pos + r->eap_count * 3 > f_end) {
+ wpa_printf(MSG_DEBUG, "No room for EAP Methods");
+ return NULL;
+ }
+ r->eap = os_calloc(r->eap_count, sizeof(struct nai_realm_eap));
+ if (r->eap == NULL)
+ return NULL;
+
+ for (e = 0; e < r->eap_count; e++) {
+ pos = nai_realm_parse_eap(&r->eap[e], pos, f_end);
+ if (pos == NULL)
+ return NULL;
+ }
+
+ return f_end;
+}
+
+
+static struct nai_realm * nai_realm_parse(struct wpabuf *anqp, u16 *count)
+{
+ struct nai_realm *realm;
+ const u8 *pos, *end;
+ u16 i, num;
+
+ if (anqp == NULL || wpabuf_len(anqp) < 2)
+ return NULL;
+
+ pos = wpabuf_head_u8(anqp);
+ end = pos + wpabuf_len(anqp);
+ num = WPA_GET_LE16(pos);
+ wpa_printf(MSG_DEBUG, "NAI Realm Count: %u", num);
+ pos += 2;
+
+ if (num * 5 > end - pos) {
+ wpa_printf(MSG_DEBUG, "Invalid NAI Realm Count %u - not "
+ "enough data (%u octets) for that many realms",
+ num, (unsigned int) (end - pos));
+ return NULL;
+ }
+
+ realm = os_calloc(num, sizeof(struct nai_realm));
+ if (realm == NULL)
+ return NULL;
+
+ for (i = 0; i < num; i++) {
+ pos = nai_realm_parse_realm(&realm[i], pos, end);
+ if (pos == NULL) {
+ nai_realm_free(realm, num);
+ return NULL;
+ }
+ }
+
+ *count = num;
+ return realm;
+}
+
+
+static int nai_realm_match(struct nai_realm *realm, const char *home_realm)
+{
+ char *tmp, *pos, *end;
+ int match = 0;
+
+ if (realm->realm == NULL || home_realm == NULL)
+ return 0;
+
+ if (os_strchr(realm->realm, ';') == NULL)
+ return os_strcasecmp(realm->realm, home_realm) == 0;
+
+ tmp = os_strdup(realm->realm);
+ if (tmp == NULL)
+ return 0;
+
+ pos = tmp;
+ while (*pos) {
+ end = os_strchr(pos, ';');
+ if (end)
+ *end = '\0';
+ if (os_strcasecmp(pos, home_realm) == 0) {
+ match = 1;
+ break;
+ }
+ if (end == NULL)
+ break;
+ pos = end + 1;
+ }
+
+ os_free(tmp);
+
+ return match;
+}
+
+
+static int nai_realm_cred_username(struct nai_realm_eap *eap)
+{
+ if (eap_get_name(EAP_VENDOR_IETF, eap->method) == NULL)
+ return 0; /* method not supported */
+
+ if (eap->method != EAP_TYPE_TTLS && eap->method != EAP_TYPE_PEAP) {
+ /* Only tunneled methods with username/password supported */
+ return 0;
+ }
+
+ if (eap->method == EAP_TYPE_PEAP) {
+ if (eap->inner_method &&
+ eap_get_name(EAP_VENDOR_IETF, eap->inner_method) == NULL)
+ return 0;
+ if (!eap->inner_method &&
+ eap_get_name(EAP_VENDOR_IETF, EAP_TYPE_MSCHAPV2) == NULL)
+ return 0;
+ }
+
+ if (eap->method == EAP_TYPE_TTLS) {
+ if (eap->inner_method == 0 && eap->inner_non_eap == 0)
+ return 1; /* Assume TTLS/MSCHAPv2 is used */
+ if (eap->inner_method &&
+ eap_get_name(EAP_VENDOR_IETF, eap->inner_method) == NULL)
+ return 0;
+ if (eap->inner_non_eap &&
+ eap->inner_non_eap != NAI_REALM_INNER_NON_EAP_PAP &&
+ eap->inner_non_eap != NAI_REALM_INNER_NON_EAP_CHAP &&
+ eap->inner_non_eap != NAI_REALM_INNER_NON_EAP_MSCHAP &&
+ eap->inner_non_eap != NAI_REALM_INNER_NON_EAP_MSCHAPV2)
+ return 0;
+ }
+
+ if (eap->inner_method &&
+ eap->inner_method != EAP_TYPE_GTC &&
+ eap->inner_method != EAP_TYPE_MSCHAPV2)
+ return 0;
+
+ return 1;
+}
+
+
+static int nai_realm_cred_cert(struct nai_realm_eap *eap)
+{
+ if (eap_get_name(EAP_VENDOR_IETF, eap->method) == NULL)
+ return 0; /* method not supported */
+
+ if (eap->method != EAP_TYPE_TLS) {
+ /* Only EAP-TLS supported for credential authentication */
+ return 0;
+ }
+
+ return 1;
+}
+
+
+static struct nai_realm_eap * nai_realm_find_eap(struct wpa_cred *cred,
+ struct nai_realm *realm)
+{
+ u8 e;
+
+ if (cred == NULL ||
+ cred->username == NULL ||
+ cred->username[0] == '\0' ||
+ ((cred->password == NULL ||
+ cred->password[0] == '\0') &&
+ (cred->private_key == NULL ||
+ cred->private_key[0] == '\0')))
+ return NULL;
+
+ for (e = 0; e < realm->eap_count; e++) {
+ struct nai_realm_eap *eap = &realm->eap[e];
+ if (cred->password && cred->password[0] &&
+ nai_realm_cred_username(eap))
+ return eap;
+ if (cred->private_key && cred->private_key[0] &&
+ nai_realm_cred_cert(eap))
+ return eap;
+ }
+
+ return NULL;
+}
+
+
+#ifdef INTERWORKING_3GPP
+
+static int plmn_id_match(struct wpabuf *anqp, const char *imsi, int mnc_len)
+{
+ u8 plmn[3];
+ const u8 *pos, *end;
+ u8 udhl;
+
+ /* See Annex A of 3GPP TS 24.234 v8.1.0 for description */
+ plmn[0] = (imsi[0] - '0') | ((imsi[1] - '0') << 4);
+ plmn[1] = imsi[2] - '0';
+ /* default to MNC length 3 if unknown */
+ if (mnc_len != 2)
+ plmn[1] |= (imsi[5] - '0') << 4;
+ else
+ plmn[1] |= 0xf0;
+ plmn[2] = (imsi[3] - '0') | ((imsi[4] - '0') << 4);
+
+ if (anqp == NULL)
+ return 0;
+ pos = wpabuf_head_u8(anqp);
+ end = pos + wpabuf_len(anqp);
+ if (pos + 2 > end)
+ return 0;
+ if (*pos != 0) {
+ wpa_printf(MSG_DEBUG, "Unsupported GUD version 0x%x", *pos);
+ return 0;
+ }
+ pos++;
+ udhl = *pos++;
+ if (pos + udhl > end) {
+ wpa_printf(MSG_DEBUG, "Invalid UDHL");
+ return 0;
+ }
+ end = pos + udhl;
+
+ while (pos + 2 <= end) {
+ u8 iei, len;
+ const u8 *l_end;
+ iei = *pos++;
+ len = *pos++ & 0x7f;
+ if (pos + len > end)
+ break;
+ l_end = pos + len;
+
+ if (iei == 0 && len > 0) {
+ /* PLMN List */
+ u8 num, i;
+ num = *pos++;
+ for (i = 0; i < num; i++) {
+ if (pos + 3 > end)
+ break;
+ if (os_memcmp(pos, plmn, 3) == 0)
+ return 1; /* Found matching PLMN */
+ pos += 3;
+ }
+ }
+
+ pos = l_end;
+ }
+
+ return 0;
+}
+
+
+static int build_root_nai(char *nai, size_t nai_len, const char *imsi,
+ size_t mnc_len, char prefix)
+{
+ const char *sep, *msin;
+ char *end, *pos;
+ size_t msin_len, plmn_len;
+
+ /*
+ * TS 23.003, Clause 14 (3GPP to WLAN Interworking)
+ * Root NAI:
+ * <aka:0|sim:1><IMSI>@wlan.mnc<MNC>.mcc<MCC>.3gppnetwork.org
+ * <MNC> is zero-padded to three digits in case two-digit MNC is used
+ */
+
+ if (imsi == NULL || os_strlen(imsi) > 16) {
+ wpa_printf(MSG_DEBUG, "No valid IMSI available");
+ return -1;
+ }
+ sep = os_strchr(imsi, '-');
+ if (sep) {
+ plmn_len = sep - imsi;
+ msin = sep + 1;
+ } else if (mnc_len && os_strlen(imsi) >= 3 + mnc_len) {
+ plmn_len = 3 + mnc_len;
+ msin = imsi + plmn_len;
+ } else
+ return -1;
+ if (plmn_len != 5 && plmn_len != 6)
+ return -1;
+ msin_len = os_strlen(msin);
+
+ pos = nai;
+ end = nai + nai_len;
+ if (prefix)
+ *pos++ = prefix;
+ os_memcpy(pos, imsi, plmn_len);
+ pos += plmn_len;
+ os_memcpy(pos, msin, msin_len);
+ pos += msin_len;
+ pos += os_snprintf(pos, end - pos, "@wlan.mnc");
+ if (plmn_len == 5) {
+ *pos++ = '0';
+ *pos++ = imsi[3];
+ *pos++ = imsi[4];
+ } else {
+ *pos++ = imsi[3];
+ *pos++ = imsi[4];
+ *pos++ = imsi[5];
+ }
+ pos += os_snprintf(pos, end - pos, ".mcc%c%c%c.3gppnetwork.org",
+ imsi[0], imsi[1], imsi[2]);
+
+ return 0;
+}
+
+
+static int set_root_nai(struct wpa_ssid *ssid, const char *imsi, char prefix)
+{
+ char nai[100];
+ if (build_root_nai(nai, sizeof(nai), imsi, 0, prefix) < 0)
+ return -1;
+ return wpa_config_set_quoted(ssid, "identity", nai);
+}
+
+#endif /* INTERWORKING_3GPP */
+
+
+static int interworking_set_hs20_params(struct wpa_supplicant *wpa_s,
+ struct wpa_ssid *ssid)
+{
+ if (wpa_config_set(ssid, "key_mgmt",
+ wpa_s->conf->pmf != NO_MGMT_FRAME_PROTECTION ?
+ "WPA-EAP WPA-EAP-SHA256" : "WPA-EAP", 0) < 0)
+ return -1;
+ if (wpa_config_set(ssid, "proto", "RSN", 0) < 0)
+ return -1;
+ if (wpa_config_set(ssid, "pairwise", "CCMP", 0) < 0)
+ return -1;
+ return 0;
+}
+
+
+static int interworking_connect_3gpp(struct wpa_supplicant *wpa_s,
+ struct wpa_bss *bss)
+{
+#ifdef INTERWORKING_3GPP
+ struct wpa_cred *cred;
+ struct wpa_ssid *ssid;
+ const u8 *ie;
+ int eap_type;
+ int res;
+ char prefix;
+
+ if (bss->anqp == NULL || bss->anqp->anqp_3gpp == NULL)
+ return -1;
+
+ for (cred = wpa_s->conf->cred; cred; cred = cred->next) {
+ char *sep;
+ const char *imsi;
+ int mnc_len;
+
+#ifdef PCSC_FUNCS
+ if (cred->pcsc && wpa_s->conf->pcsc_reader && wpa_s->scard &&
+ wpa_s->imsi[0]) {
+ imsi = wpa_s->imsi;
+ mnc_len = wpa_s->mnc_len;
+ goto compare;
+ }
+#endif /* PCSC_FUNCS */
+
+ if (cred->imsi == NULL || !cred->imsi[0] ||
+ cred->milenage == NULL || !cred->milenage[0])
+ continue;
+
+ sep = os_strchr(cred->imsi, '-');
+ if (sep == NULL ||
+ (sep - cred->imsi != 5 && sep - cred->imsi != 6))
+ continue;
+ mnc_len = sep - cred->imsi - 3;
+ imsi = cred->imsi;
+
+#ifdef PCSC_FUNCS
+ compare:
+#endif /* PCSC_FUNCS */
+ if (plmn_id_match(bss->anqp->anqp_3gpp, imsi, mnc_len))
+ break;
+ }
+ if (cred == NULL)
+ return -1;
+
+ ie = wpa_bss_get_ie(bss, WLAN_EID_SSID);
+ if (ie == NULL)
+ return -1;
+ wpa_printf(MSG_DEBUG, "Interworking: Connect with " MACSTR " (3GPP)",
+ MAC2STR(bss->bssid));
+
+ ssid = wpa_config_add_network(wpa_s->conf);
+ if (ssid == NULL)
+ return -1;
+ ssid->parent_cred = cred;
+
+ wpas_notify_network_added(wpa_s, ssid);
+ wpa_config_set_network_defaults(ssid);
+ ssid->priority = cred->priority;
+ ssid->temporary = 1;
+ ssid->ssid = os_zalloc(ie[1] + 1);
+ if (ssid->ssid == NULL)
+ goto fail;
+ os_memcpy(ssid->ssid, ie + 2, ie[1]);
+ ssid->ssid_len = ie[1];
+
+ if (interworking_set_hs20_params(wpa_s, ssid) < 0)
+ goto fail;
+
+ eap_type = EAP_TYPE_SIM;
+ if (cred->pcsc && wpa_s->scard && scard_supports_umts(wpa_s->scard))
+ eap_type = EAP_TYPE_AKA;
+ if (cred->eap_method && cred->eap_method[0].vendor == EAP_VENDOR_IETF) {
+ if (cred->eap_method[0].method == EAP_TYPE_SIM ||
+ cred->eap_method[0].method == EAP_TYPE_AKA ||
+ cred->eap_method[0].method == EAP_TYPE_AKA_PRIME)
+ eap_type = cred->eap_method[0].method;
+ }
+
+ switch (eap_type) {
+ case EAP_TYPE_SIM:
+ prefix = '1';
+ res = wpa_config_set(ssid, "eap", "SIM", 0);
+ break;
+ case EAP_TYPE_AKA:
+ prefix = '0';
+ res = wpa_config_set(ssid, "eap", "AKA", 0);
+ break;
+ case EAP_TYPE_AKA_PRIME:
+ prefix = '6';
+ res = wpa_config_set(ssid, "eap", "AKA'", 0);
+ break;
+ default:
+ res = -1;
+ break;
+ }
+ if (res < 0) {
+ wpa_printf(MSG_DEBUG, "Selected EAP method (%d) not supported",
+ eap_type);
+ goto fail;
+ }
+
+ if (!cred->pcsc && set_root_nai(ssid, cred->imsi, prefix) < 0) {
+ wpa_printf(MSG_DEBUG, "Failed to set Root NAI");
+ goto fail;
+ }
+
+ if (cred->milenage && cred->milenage[0]) {
+ if (wpa_config_set_quoted(ssid, "password",
+ cred->milenage) < 0)
+ goto fail;
+ } else if (cred->pcsc) {
+ if (wpa_config_set_quoted(ssid, "pcsc", "") < 0)
+ goto fail;
+ if (wpa_s->conf->pcsc_pin &&
+ wpa_config_set_quoted(ssid, "pin", wpa_s->conf->pcsc_pin)
+ < 0)
+ goto fail;
+ }
+
+ if (cred->password && cred->password[0] &&
+ wpa_config_set_quoted(ssid, "password", cred->password) < 0)
+ goto fail;
+
+ wpa_config_update_prio_list(wpa_s->conf);
+ interworking_reconnect(wpa_s);
+
+ return 0;
+
+fail:
+ wpas_notify_network_removed(wpa_s, ssid);
+ wpa_config_remove_network(wpa_s->conf, ssid->id);
+#endif /* INTERWORKING_3GPP */
+ return -1;
+}
+
+
+static int roaming_consortium_element_match(const u8 *ie, const u8 *rc_id,
+ size_t rc_len)
+{
+ const u8 *pos, *end;
+ u8 lens;
+
+ if (ie == NULL)
+ return 0;
+
+ pos = ie + 2;
+ end = ie + 2 + ie[1];
+
+ /* Roaming Consortium element:
+ * Number of ANQP OIs
+ * OI #1 and #2 lengths
+ * OI #1, [OI #2], [OI #3]
+ */
+
+ if (pos + 2 > end)
+ return 0;
+
+ pos++; /* skip Number of ANQP OIs */
+ lens = *pos++;
+ if (pos + (lens & 0x0f) + (lens >> 4) > end)
+ return 0;
+
+ if ((lens & 0x0f) == rc_len && os_memcmp(pos, rc_id, rc_len) == 0)
+ return 1;
+ pos += lens & 0x0f;
+
+ if ((lens >> 4) == rc_len && os_memcmp(pos, rc_id, rc_len) == 0)
+ return 1;
+ pos += lens >> 4;
+
+ if (pos < end && (size_t) (end - pos) == rc_len &&
+ os_memcmp(pos, rc_id, rc_len) == 0)
+ return 1;
+
+ return 0;
+}
+
+
+static int roaming_consortium_anqp_match(const struct wpabuf *anqp,
+ const u8 *rc_id, size_t rc_len)
+{
+ const u8 *pos, *end;
+ u8 len;
+
+ if (anqp == NULL)
+ return 0;
+
+ pos = wpabuf_head(anqp);
+ end = pos + wpabuf_len(anqp);
+
+ /* Set of <OI Length, OI> duples */
+ while (pos < end) {
+ len = *pos++;
+ if (pos + len > end)
+ break;
+ if (len == rc_len && os_memcmp(pos, rc_id, rc_len) == 0)
+ return 1;
+ pos += len;
+ }
+
+ return 0;
+}
+
+
+static int roaming_consortium_match(const u8 *ie, const struct wpabuf *anqp,
+ const u8 *rc_id, size_t rc_len)
+{
+ return roaming_consortium_element_match(ie, rc_id, rc_len) ||
+ roaming_consortium_anqp_match(anqp, rc_id, rc_len);
+}
+
+
+static int cred_excluded_ssid(struct wpa_cred *cred, struct wpa_bss *bss)
+{
+ size_t i;
+
+ if (!cred->excluded_ssid)
+ return 0;
+
+ for (i = 0; i < cred->num_excluded_ssid; i++) {
+ struct excluded_ssid *e = &cred->excluded_ssid[i];
+ if (bss->ssid_len == e->ssid_len &&
+ os_memcmp(bss->ssid, e->ssid, e->ssid_len) == 0)
+ return 1;
+ }
+
+ return 0;
+}
+
+
+static struct wpa_cred * interworking_credentials_available_roaming_consortium(
+ struct wpa_supplicant *wpa_s, struct wpa_bss *bss)
+{
+ struct wpa_cred *cred, *selected = NULL;
+ const u8 *ie;
+
+ ie = wpa_bss_get_ie(bss, WLAN_EID_ROAMING_CONSORTIUM);
+
+ if (ie == NULL &&
+ (bss->anqp == NULL || bss->anqp->roaming_consortium == NULL))
+ return NULL;
+
+ if (wpa_s->conf->cred == NULL)
+ return NULL;
+
+ for (cred = wpa_s->conf->cred; cred; cred = cred->next) {
+ if (cred->roaming_consortium_len == 0)
+ continue;
+
+ if (!roaming_consortium_match(ie,
+ bss->anqp ?
+ bss->anqp->roaming_consortium :
+ NULL,
+ cred->roaming_consortium,
+ cred->roaming_consortium_len))
+ continue;
+
+ if (cred_excluded_ssid(cred, bss))
+ continue;
+
+ if (selected == NULL ||
+ selected->priority < cred->priority)
+ selected = cred;
+ }
+
+ return selected;
+}
+
+
+static int interworking_set_eap_params(struct wpa_ssid *ssid,
+ struct wpa_cred *cred, int ttls)
+{
+ if (cred->eap_method) {
+ ttls = cred->eap_method->vendor == EAP_VENDOR_IETF &&
+ cred->eap_method->method == EAP_TYPE_TTLS;
+
+ os_free(ssid->eap.eap_methods);
+ ssid->eap.eap_methods =
+ os_malloc(sizeof(struct eap_method_type) * 2);
+ if (ssid->eap.eap_methods == NULL)
+ return -1;
+ os_memcpy(ssid->eap.eap_methods, cred->eap_method,
+ sizeof(*cred->eap_method));
+ ssid->eap.eap_methods[1].vendor = EAP_VENDOR_IETF;
+ ssid->eap.eap_methods[1].method = EAP_TYPE_NONE;
+ }
+
+ if (ttls && cred->username && cred->username[0]) {
+ const char *pos;
+ char *anon;
+ /* Use anonymous NAI in Phase 1 */
+ pos = os_strchr(cred->username, '@');
+ if (pos) {
+ size_t buflen = 9 + os_strlen(pos) + 1;
+ anon = os_malloc(buflen);
+ if (anon == NULL)
+ return -1;
+ os_snprintf(anon, buflen, "anonymous%s", pos);
+ } else if (cred->realm) {
+ size_t buflen = 10 + os_strlen(cred->realm) + 1;
+ anon = os_malloc(buflen);
+ if (anon == NULL)
+ return -1;
+ os_snprintf(anon, buflen, "anonymous@%s", cred->realm);
+ } else {
+ anon = os_strdup("anonymous");
+ if (anon == NULL)
+ return -1;
+ }
+ if (wpa_config_set_quoted(ssid, "anonymous_identity", anon) <
+ 0) {
+ os_free(anon);
+ return -1;
+ }
+ os_free(anon);
+ }
+
+ if (cred->username && cred->username[0] &&
+ wpa_config_set_quoted(ssid, "identity", cred->username) < 0)
+ return -1;
+
+ if (cred->password && cred->password[0]) {
+ if (cred->ext_password &&
+ wpa_config_set(ssid, "password", cred->password, 0) < 0)
+ return -1;
+ if (!cred->ext_password &&
+ wpa_config_set_quoted(ssid, "password", cred->password) <
+ 0)
+ return -1;
+ }
+
+ if (cred->client_cert && cred->client_cert[0] &&
+ wpa_config_set_quoted(ssid, "client_cert", cred->client_cert) < 0)
+ return -1;
+
+#ifdef ANDROID
+ if (cred->private_key &&
+ os_strncmp(cred->private_key, "keystore://", 11) == 0) {
+ /* Use OpenSSL engine configuration for Android keystore */
+ if (wpa_config_set_quoted(ssid, "engine_id", "keystore") < 0 ||
+ wpa_config_set_quoted(ssid, "key_id",
+ cred->private_key + 11) < 0 ||
+ wpa_config_set(ssid, "engine", "1", 0) < 0)
+ return -1;
+ } else
+#endif /* ANDROID */
+ if (cred->private_key && cred->private_key[0] &&
+ wpa_config_set_quoted(ssid, "private_key", cred->private_key) < 0)
+ return -1;
+
+ if (cred->private_key_passwd && cred->private_key_passwd[0] &&
+ wpa_config_set_quoted(ssid, "private_key_passwd",
+ cred->private_key_passwd) < 0)
+ return -1;
+
+ if (cred->phase1) {
+ os_free(ssid->eap.phase1);
+ ssid->eap.phase1 = os_strdup(cred->phase1);
+ }
+ if (cred->phase2) {
+ os_free(ssid->eap.phase2);
+ ssid->eap.phase2 = os_strdup(cred->phase2);
+ }
+
+ if (cred->ca_cert && cred->ca_cert[0] &&
+ wpa_config_set_quoted(ssid, "ca_cert", cred->ca_cert) < 0)
+ return -1;
+
+ return 0;
+}
+
+
+static int interworking_connect_roaming_consortium(
+ struct wpa_supplicant *wpa_s, struct wpa_cred *cred,
+ struct wpa_bss *bss, const u8 *ssid_ie)
+{
+ struct wpa_ssid *ssid;
+
+ wpa_printf(MSG_DEBUG, "Interworking: Connect with " MACSTR " based on "
+ "roaming consortium match", MAC2STR(bss->bssid));
+
+ ssid = wpa_config_add_network(wpa_s->conf);
+ if (ssid == NULL)
+ return -1;
+ ssid->parent_cred = cred;
+ wpas_notify_network_added(wpa_s, ssid);
+ wpa_config_set_network_defaults(ssid);
+ ssid->priority = cred->priority;
+ ssid->temporary = 1;
+ ssid->ssid = os_zalloc(ssid_ie[1] + 1);
+ if (ssid->ssid == NULL)
+ goto fail;
+ os_memcpy(ssid->ssid, ssid_ie + 2, ssid_ie[1]);
+ ssid->ssid_len = ssid_ie[1];
+
+ if (interworking_set_hs20_params(wpa_s, ssid) < 0)
+ goto fail;
+
+ if (cred->eap_method == NULL) {
+ wpa_printf(MSG_DEBUG, "Interworking: No EAP method set for "
+ "credential using roaming consortium");
+ goto fail;
+ }
+
+ if (interworking_set_eap_params(
+ ssid, cred,
+ cred->eap_method->vendor == EAP_VENDOR_IETF &&
+ cred->eap_method->method == EAP_TYPE_TTLS) < 0)
+ goto fail;
+
+ wpa_config_update_prio_list(wpa_s->conf);
+ interworking_reconnect(wpa_s);
+
+ return 0;
+
+fail:
+ wpas_notify_network_removed(wpa_s, ssid);
+ wpa_config_remove_network(wpa_s->conf, ssid->id);
+ return -1;
+}
+
+
+int interworking_connect(struct wpa_supplicant *wpa_s, struct wpa_bss *bss)
+{
+ struct wpa_cred *cred;
+ struct wpa_ssid *ssid;
+ struct nai_realm *realm;
+ struct nai_realm_eap *eap = NULL;
+ u16 count, i;
+ char buf[100];
+ const u8 *ie;
+
+ if (wpa_s->conf->cred == NULL || bss == NULL)
+ return -1;
+ ie = wpa_bss_get_ie(bss, WLAN_EID_SSID);
+ if (ie == NULL || ie[1] == 0) {
+ wpa_printf(MSG_DEBUG, "Interworking: No SSID known for "
+ MACSTR, MAC2STR(bss->bssid));
+ return -1;
+ }
+
+ if (!wpa_bss_get_ie(bss, WLAN_EID_RSN)) {
+ /*
+ * We currently support only HS 2.0 networks and those are
+ * required to use WPA2-Enterprise.
+ */
+ wpa_printf(MSG_DEBUG, "Interworking: Network does not use "
+ "RSN");
+ return -1;
+ }
+
+ cred = interworking_credentials_available_roaming_consortium(wpa_s,
+ bss);
+ if (cred)
+ return interworking_connect_roaming_consortium(wpa_s, cred,
+ bss, ie);
+
+ realm = nai_realm_parse(bss->anqp ? bss->anqp->nai_realm : NULL,
+ &count);
+ if (realm == NULL) {
+ wpa_printf(MSG_DEBUG, "Interworking: Could not parse NAI "
+ "Realm list from " MACSTR, MAC2STR(bss->bssid));
+ count = 0;
+ }
+
+ for (cred = wpa_s->conf->cred; cred; cred = cred->next) {
+ for (i = 0; i < count; i++) {
+ if (!nai_realm_match(&realm[i], cred->realm))
+ continue;
+ eap = nai_realm_find_eap(cred, &realm[i]);
+ if (eap)
+ break;
+ }
+ if (eap)
+ break;
+ }
+
+ if (!eap) {
+ if (interworking_connect_3gpp(wpa_s, bss) == 0) {
+ if (realm)
+ nai_realm_free(realm, count);
+ return 0;
+ }
+
+ wpa_printf(MSG_DEBUG, "Interworking: No matching credentials "
+ "and EAP method found for " MACSTR,
+ MAC2STR(bss->bssid));
+ nai_realm_free(realm, count);
+ return -1;
+ }
+
+ wpa_printf(MSG_DEBUG, "Interworking: Connect with " MACSTR,
+ MAC2STR(bss->bssid));
+
+ ssid = wpa_config_add_network(wpa_s->conf);
+ if (ssid == NULL) {
+ nai_realm_free(realm, count);
+ return -1;
+ }
+ ssid->parent_cred = cred;
+ wpas_notify_network_added(wpa_s, ssid);
+ wpa_config_set_network_defaults(ssid);
+ ssid->priority = cred->priority;
+ ssid->temporary = 1;
+ ssid->ssid = os_zalloc(ie[1] + 1);
+ if (ssid->ssid == NULL)
+ goto fail;
+ os_memcpy(ssid->ssid, ie + 2, ie[1]);
+ ssid->ssid_len = ie[1];
+
+ if (interworking_set_hs20_params(wpa_s, ssid) < 0)
+ goto fail;
+
+ if (wpa_config_set(ssid, "eap", eap_get_name(EAP_VENDOR_IETF,
+ eap->method), 0) < 0)
+ goto fail;
+
+ switch (eap->method) {
+ case EAP_TYPE_TTLS:
+ if (eap->inner_method) {
+ os_snprintf(buf, sizeof(buf), "\"autheap=%s\"",
+ eap_get_name(EAP_VENDOR_IETF,
+ eap->inner_method));
+ if (wpa_config_set(ssid, "phase2", buf, 0) < 0)
+ goto fail;
+ break;
+ }
+ switch (eap->inner_non_eap) {
+ case NAI_REALM_INNER_NON_EAP_PAP:
+ if (wpa_config_set(ssid, "phase2", "\"auth=PAP\"", 0) <
+ 0)
+ goto fail;
+ break;
+ case NAI_REALM_INNER_NON_EAP_CHAP:
+ if (wpa_config_set(ssid, "phase2", "\"auth=CHAP\"", 0)
+ < 0)
+ goto fail;
+ break;
+ case NAI_REALM_INNER_NON_EAP_MSCHAP:
+ if (wpa_config_set(ssid, "phase2", "\"auth=MSCHAP\"",
+ 0) < 0)
+ goto fail;
+ break;
+ case NAI_REALM_INNER_NON_EAP_MSCHAPV2:
+ if (wpa_config_set(ssid, "phase2", "\"auth=MSCHAPV2\"",
+ 0) < 0)
+ goto fail;
+ break;
+ default:
+ /* EAP params were not set - assume TTLS/MSCHAPv2 */
+ if (wpa_config_set(ssid, "phase2", "\"auth=MSCHAPV2\"",
+ 0) < 0)
+ goto fail;
+ break;
+ }
+ break;
+ case EAP_TYPE_PEAP:
+ os_snprintf(buf, sizeof(buf), "\"auth=%s\"",
+ eap_get_name(EAP_VENDOR_IETF,
+ eap->inner_method ?
+ eap->inner_method :
+ EAP_TYPE_MSCHAPV2));
+ if (wpa_config_set(ssid, "phase2", buf, 0) < 0)
+ goto fail;
+ break;
+ case EAP_TYPE_TLS:
+ break;
+ }
+
+ if (interworking_set_eap_params(ssid, cred,
+ eap->method == EAP_TYPE_TTLS) < 0)
+ goto fail;
+
+ nai_realm_free(realm, count);
+
+ wpa_config_update_prio_list(wpa_s->conf);
+ interworking_reconnect(wpa_s);
+
+ return 0;
+
+fail:
+ wpas_notify_network_removed(wpa_s, ssid);
+ wpa_config_remove_network(wpa_s->conf, ssid->id);
+ nai_realm_free(realm, count);
+ return -1;
+}
+
+
+static struct wpa_cred * interworking_credentials_available_3gpp(
+ struct wpa_supplicant *wpa_s, struct wpa_bss *bss)
+{
+ struct wpa_cred *cred, *selected = NULL;
+ int ret;
+
+#ifdef INTERWORKING_3GPP
+ if (bss->anqp == NULL || bss->anqp->anqp_3gpp == NULL)
+ return NULL;
+
+ for (cred = wpa_s->conf->cred; cred; cred = cred->next) {
+ char *sep;
+ const char *imsi;
+ int mnc_len;
+
+#ifdef PCSC_FUNCS
+ if (cred->pcsc && wpa_s->conf->pcsc_reader && wpa_s->scard &&
+ wpa_s->imsi[0]) {
+ imsi = wpa_s->imsi;
+ mnc_len = wpa_s->mnc_len;
+ goto compare;
+ }
+#endif /* PCSC_FUNCS */
+
+ if (cred->imsi == NULL || !cred->imsi[0] ||
+ cred->milenage == NULL || !cred->milenage[0])
+ continue;
+
+ sep = os_strchr(cred->imsi, '-');
+ if (sep == NULL ||
+ (sep - cred->imsi != 5 && sep - cred->imsi != 6))
+ continue;
+ mnc_len = sep - cred->imsi - 3;
+ imsi = cred->imsi;
+
+#ifdef PCSC_FUNCS
+ compare:
+#endif /* PCSC_FUNCS */
+ wpa_printf(MSG_DEBUG, "Interworking: Parsing 3GPP info from "
+ MACSTR, MAC2STR(bss->bssid));
+ ret = plmn_id_match(bss->anqp->anqp_3gpp, imsi, mnc_len);
+ wpa_printf(MSG_DEBUG, "PLMN match %sfound", ret ? "" : "not ");
+ if (ret) {
+ if (cred_excluded_ssid(cred, bss))
+ continue;
+ if (selected == NULL ||
+ selected->priority < cred->priority)
+ selected = cred;
+ }
+ }
+#endif /* INTERWORKING_3GPP */
+ return selected;
+}
+
+
+static struct wpa_cred * interworking_credentials_available_realm(
+ struct wpa_supplicant *wpa_s, struct wpa_bss *bss)
+{
+ struct wpa_cred *cred, *selected = NULL;
+ struct nai_realm *realm;
+ u16 count, i;
+
+ if (bss->anqp == NULL || bss->anqp->nai_realm == NULL)
+ return NULL;
+
+ if (wpa_s->conf->cred == NULL)
+ return NULL;
+
+ wpa_printf(MSG_DEBUG, "Interworking: Parsing NAI Realm list from "
+ MACSTR, MAC2STR(bss->bssid));
+ realm = nai_realm_parse(bss->anqp->nai_realm, &count);
+ if (realm == NULL) {
+ wpa_printf(MSG_DEBUG, "Interworking: Could not parse NAI "
+ "Realm list from " MACSTR, MAC2STR(bss->bssid));
+ return NULL;
+ }
+
+ for (cred = wpa_s->conf->cred; cred; cred = cred->next) {
+ if (cred->realm == NULL)
+ continue;
+
+ for (i = 0; i < count; i++) {
+ if (!nai_realm_match(&realm[i], cred->realm))
+ continue;
+ if (nai_realm_find_eap(cred, &realm[i])) {
+ if (cred_excluded_ssid(cred, bss))
+ continue;
+ if (selected == NULL ||
+ selected->priority < cred->priority)
+ selected = cred;
+ break;
+ }
+ }
+ }
+
+ nai_realm_free(realm, count);
+
+ return selected;
+}
+
+
+static struct wpa_cred * interworking_credentials_available(
+ struct wpa_supplicant *wpa_s, struct wpa_bss *bss)
+{
+ struct wpa_cred *cred, *cred2;
+
+ cred = interworking_credentials_available_realm(wpa_s, bss);
+ cred2 = interworking_credentials_available_3gpp(wpa_s, bss);
+ if (cred && cred2 && cred2->priority >= cred->priority)
+ cred = cred2;
+ if (!cred)
+ cred = cred2;
+
+ cred2 = interworking_credentials_available_roaming_consortium(wpa_s,
+ bss);
+ if (cred && cred2 && cred2->priority >= cred->priority)
+ cred = cred2;
+ if (!cred)
+ cred = cred2;
+
+ return cred;
+}
+
+
+static int domain_name_list_contains(struct wpabuf *domain_names,
+ const char *domain)
+{
+ const u8 *pos, *end;
+ size_t len;
+
+ len = os_strlen(domain);
+ pos = wpabuf_head(domain_names);
+ end = pos + wpabuf_len(domain_names);
+
+ while (pos + 1 < end) {
+ if (pos + 1 + pos[0] > end)
+ break;
+
+ wpa_hexdump_ascii(MSG_DEBUG, "Interworking: AP domain name",
+ pos + 1, pos[0]);
+ if (pos[0] == len &&
+ os_strncasecmp(domain, (const char *) (pos + 1), len) == 0)
+ return 1;
+
+ pos += 1 + pos[0];
+ }
+
+ return 0;
+}
+
+
+int interworking_home_sp_cred(struct wpa_supplicant *wpa_s,
+ struct wpa_cred *cred,
+ struct wpabuf *domain_names)
+{
+#ifdef INTERWORKING_3GPP
+ char nai[100], *realm;
+
+ char *imsi = NULL;
+ int mnc_len = 0;
+ if (cred->imsi)
+ imsi = cred->imsi;
+#ifdef CONFIG_PCSC
+ else if (cred->pcsc && wpa_s->conf->pcsc_reader &&
+ wpa_s->scard && wpa_s->imsi[0]) {
+ imsi = wpa_s->imsi;
+ mnc_len = wpa_s->mnc_len;
+ }
+#endif /* CONFIG_PCSC */
+ if (domain_names &&
+ imsi && build_root_nai(nai, sizeof(nai), imsi, mnc_len, 0) == 0) {
+ realm = os_strchr(nai, '@');
+ if (realm)
+ realm++;
+ wpa_printf(MSG_DEBUG, "Interworking: Search for match "
+ "with SIM/USIM domain %s", realm);
+ if (realm &&
+ domain_name_list_contains(domain_names, realm))
+ return 1;
+ }
+#endif /* INTERWORKING_3GPP */
+
+ if (domain_names == NULL || cred->domain == NULL)
+ return 0;
+
+ wpa_printf(MSG_DEBUG, "Interworking: Search for match with "
+ "home SP FQDN %s", cred->domain);
+ if (domain_name_list_contains(domain_names, cred->domain))
+ return 1;
+
+ return 0;
+}
+
+
+static int interworking_home_sp(struct wpa_supplicant *wpa_s,
+ struct wpabuf *domain_names)
+{
+ struct wpa_cred *cred;
+
+ if (domain_names == NULL || wpa_s->conf->cred == NULL)
+ return -1;
+
+ for (cred = wpa_s->conf->cred; cred; cred = cred->next) {
+ int res = interworking_home_sp_cred(wpa_s, cred, domain_names);
+ if (res)
+ return res;
+ }
+
+ return 0;
+}
+
+
+static int interworking_find_network_match(struct wpa_supplicant *wpa_s)
+{
+ struct wpa_bss *bss;
+ struct wpa_ssid *ssid;
+
+ dl_list_for_each(bss, &wpa_s->bss, struct wpa_bss, list) {
+ for (ssid = wpa_s->conf->ssid; ssid; ssid = ssid->next) {
+ if (wpas_network_disabled(wpa_s, ssid) ||
+ ssid->mode != WPAS_MODE_INFRA)
+ continue;
+ if (ssid->ssid_len != bss->ssid_len ||
+ os_memcmp(ssid->ssid, bss->ssid, ssid->ssid_len) !=
+ 0)
+ continue;
+ /*
+ * TODO: Consider more accurate matching of security
+ * configuration similarly to what is done in events.c
+ */
+ return 1;
+ }
+ }
+
+ return 0;
+}
+
+
+static void interworking_select_network(struct wpa_supplicant *wpa_s)
+{
+ struct wpa_bss *bss, *selected = NULL, *selected_home = NULL;
+ int selected_prio = -999999, selected_home_prio = -999999;
+ unsigned int count = 0;
+ const char *type;
+ int res;
+ struct wpa_cred *cred;
+
+ wpa_s->network_select = 0;
+
+ dl_list_for_each(bss, &wpa_s->bss, struct wpa_bss, list) {
+ cred = interworking_credentials_available(wpa_s, bss);
+ if (!cred)
+ continue;
+ if (!wpa_bss_get_ie(bss, WLAN_EID_RSN)) {
+ /*
+ * We currently support only HS 2.0 networks and those
+ * are required to use WPA2-Enterprise.
+ */
+ wpa_printf(MSG_DEBUG, "Interworking: Credential match "
+ "with " MACSTR " but network does not use "
+ "RSN", MAC2STR(bss->bssid));
+ continue;
+ }
+ count++;
+ res = interworking_home_sp(wpa_s, bss->anqp ?
+ bss->anqp->domain_name : NULL);
+ if (res > 0)
+ type = "home";
+ else if (res == 0)
+ type = "roaming";
+ else
+ type = "unknown";
+ wpa_msg(wpa_s, MSG_INFO, INTERWORKING_AP MACSTR " type=%s",
+ MAC2STR(bss->bssid), type);
+ if (wpa_s->auto_select ||
+ (wpa_s->conf->auto_interworking &&
+ wpa_s->auto_network_select)) {
+ if (selected == NULL ||
+ cred->priority > selected_prio) {
+ selected = bss;
+ selected_prio = cred->priority;
+ }
+ if (res > 0 &&
+ (selected_home == NULL ||
+ cred->priority > selected_home_prio)) {
+ selected_home = bss;
+ selected_home_prio = cred->priority;
+ }
+ }
+ }
+
+ if (selected_home && selected_home != selected &&
+ selected_home_prio >= selected_prio) {
+ /* Prefer network operated by the Home SP */
+ selected = selected_home;
+ }
+
+ if (count == 0) {
+ /*
+ * No matching network was found based on configured
+ * credentials. Check whether any of the enabled network blocks
+ * have matching APs.
+ */
+ if (interworking_find_network_match(wpa_s)) {
+ wpa_printf(MSG_DEBUG, "Interworking: Possible BSS "
+ "match for enabled network configurations");
+ if (wpa_s->auto_select)
+ interworking_reconnect(wpa_s);
+ return;
+ }
+
+ if (wpa_s->auto_network_select) {
+ wpa_printf(MSG_DEBUG, "Interworking: Continue "
+ "scanning after ANQP fetch");
+ wpa_supplicant_req_scan(wpa_s, wpa_s->scan_interval,
+ 0);
+ return;
+ }
+
+ wpa_msg(wpa_s, MSG_INFO, INTERWORKING_NO_MATCH "No network "
+ "with matching credentials found");
+ }
+
+ if (selected)
+ interworking_connect(wpa_s, selected);
+}
+
+
+static struct wpa_bss_anqp *
+interworking_match_anqp_info(struct wpa_supplicant *wpa_s, struct wpa_bss *bss)
+{
+ struct wpa_bss *other;
+
+ if (is_zero_ether_addr(bss->hessid))
+ return NULL; /* Cannot be in the same homegenous ESS */
+
+ dl_list_for_each(other, &wpa_s->bss, struct wpa_bss, list) {
+ if (other == bss)
+ continue;
+ if (other->anqp == NULL)
+ continue;
+ if (other->anqp->roaming_consortium == NULL &&
+ other->anqp->nai_realm == NULL &&
+ other->anqp->anqp_3gpp == NULL &&
+ other->anqp->domain_name == NULL)
+ continue;
+ if (!(other->flags & WPA_BSS_ANQP_FETCH_TRIED))
+ continue;
+ if (os_memcmp(bss->hessid, other->hessid, ETH_ALEN) != 0)
+ continue;
+ if (bss->ssid_len != other->ssid_len ||
+ os_memcmp(bss->ssid, other->ssid, bss->ssid_len) != 0)
+ continue;
+
+ wpa_printf(MSG_DEBUG, "Interworking: Share ANQP data with "
+ "already fetched BSSID " MACSTR " and " MACSTR,
+ MAC2STR(other->bssid), MAC2STR(bss->bssid));
+ other->anqp->users++;
+ return other->anqp;
+ }
+
+ return NULL;
+}
+
+
+static void interworking_next_anqp_fetch(struct wpa_supplicant *wpa_s)
+{
+ struct wpa_bss *bss;
+ int found = 0;
+ const u8 *ie;
+
+ if (eloop_terminated() || !wpa_s->fetch_anqp_in_progress)
+ return;
+
+ dl_list_for_each(bss, &wpa_s->bss, struct wpa_bss, list) {
+ if (!(bss->caps & IEEE80211_CAP_ESS))
+ continue;
+ ie = wpa_bss_get_ie(bss, WLAN_EID_EXT_CAPAB);
+ if (ie == NULL || ie[1] < 4 || !(ie[5] & 0x80))
+ continue; /* AP does not support Interworking */
+
+ if (!(bss->flags & WPA_BSS_ANQP_FETCH_TRIED)) {
+ if (bss->anqp == NULL) {
+ bss->anqp = interworking_match_anqp_info(wpa_s,
+ bss);
+ if (bss->anqp) {
+ /* Shared data already fetched */
+ continue;
+ }
+ bss->anqp = wpa_bss_anqp_alloc();
+ if (bss->anqp == NULL)
+ break;
+ }
+ found++;
+ bss->flags |= WPA_BSS_ANQP_FETCH_TRIED;
+ wpa_msg(wpa_s, MSG_INFO, "Starting ANQP fetch for "
+ MACSTR, MAC2STR(bss->bssid));
+ interworking_anqp_send_req(wpa_s, bss);
+ break;
+ }
+ }
+
+ if (found == 0) {
+ wpa_msg(wpa_s, MSG_INFO, "ANQP fetch completed");
+ wpa_s->fetch_anqp_in_progress = 0;
+ if (wpa_s->network_select)
+ interworking_select_network(wpa_s);
+ }
+}
+
+
+void interworking_start_fetch_anqp(struct wpa_supplicant *wpa_s)
+{
+ struct wpa_bss *bss;
+
+ dl_list_for_each(bss, &wpa_s->bss, struct wpa_bss, list)
+ bss->flags &= ~WPA_BSS_ANQP_FETCH_TRIED;
+
+ wpa_s->fetch_anqp_in_progress = 1;
+ interworking_next_anqp_fetch(wpa_s);
+}
+
+
+int interworking_fetch_anqp(struct wpa_supplicant *wpa_s)
+{
+ if (wpa_s->fetch_anqp_in_progress || wpa_s->network_select)
+ return 0;
+
+ wpa_s->network_select = 0;
+ wpa_s->fetch_all_anqp = 1;
+
+ interworking_start_fetch_anqp(wpa_s);
+
+ return 0;
+}
+
+
+void interworking_stop_fetch_anqp(struct wpa_supplicant *wpa_s)
+{
+ if (!wpa_s->fetch_anqp_in_progress)
+ return;
+
+ wpa_s->fetch_anqp_in_progress = 0;
+}
+
+
+int anqp_send_req(struct wpa_supplicant *wpa_s, const u8 *dst,
+ u16 info_ids[], size_t num_ids)
+{
+ struct wpabuf *buf;
+ int ret = 0;
+ int freq;
+ struct wpa_bss *bss;
+ int res;
+
+ freq = wpa_s->assoc_freq;
+ bss = wpa_bss_get_bssid(wpa_s, dst);
+ if (bss) {
+ wpa_bss_anqp_unshare_alloc(bss);
+ freq = bss->freq;
+ }
+ if (freq <= 0)
+ return -1;
+
+ wpa_printf(MSG_DEBUG, "ANQP: Query Request to " MACSTR " for %u id(s)",
+ MAC2STR(dst), (unsigned int) num_ids);
+
+ buf = anqp_build_req(info_ids, num_ids, NULL);
+ if (buf == NULL)
+ return -1;
+
+ res = gas_query_req(wpa_s->gas, dst, freq, buf, anqp_resp_cb, wpa_s);
+ if (res < 0) {
+ wpa_printf(MSG_DEBUG, "ANQP: Failed to send Query Request");
+ ret = -1;
+ } else
+ wpa_printf(MSG_DEBUG, "ANQP: Query started with dialog token "
+ "%u", res);
+
+ wpabuf_free(buf);
+ return ret;
+}
+
+
+static void interworking_parse_rx_anqp_resp(struct wpa_supplicant *wpa_s,
+ const u8 *sa, u16 info_id,
+ const u8 *data, size_t slen)
+{
+ const u8 *pos = data;
+ struct wpa_bss *bss = wpa_bss_get_bssid(wpa_s, sa);
+ struct wpa_bss_anqp *anqp = NULL;
+#ifdef CONFIG_HS20
+ u8 type;
+#endif /* CONFIG_HS20 */
+
+ if (bss)
+ anqp = bss->anqp;
+
+ switch (info_id) {
+ case ANQP_CAPABILITY_LIST:
+ wpa_msg(wpa_s, MSG_INFO, "RX-ANQP " MACSTR
+ " ANQP Capability list", MAC2STR(sa));
+ break;
+ case ANQP_VENUE_NAME:
+ wpa_msg(wpa_s, MSG_INFO, "RX-ANQP " MACSTR
+ " Venue Name", MAC2STR(sa));
+ wpa_hexdump_ascii(MSG_DEBUG, "ANQP: Venue Name", pos, slen);
+ if (anqp) {
+ wpabuf_free(anqp->venue_name);
+ anqp->venue_name = wpabuf_alloc_copy(pos, slen);
+ }
+ break;
+ case ANQP_NETWORK_AUTH_TYPE:
+ wpa_msg(wpa_s, MSG_INFO, "RX-ANQP " MACSTR
+ " Network Authentication Type information",
+ MAC2STR(sa));
+ wpa_hexdump_ascii(MSG_DEBUG, "ANQP: Network Authentication "
+ "Type", pos, slen);
+ if (anqp) {
+ wpabuf_free(anqp->network_auth_type);
+ anqp->network_auth_type = wpabuf_alloc_copy(pos, slen);
+ }
+ break;
+ case ANQP_ROAMING_CONSORTIUM:
+ wpa_msg(wpa_s, MSG_INFO, "RX-ANQP " MACSTR
+ " Roaming Consortium list", MAC2STR(sa));
+ wpa_hexdump_ascii(MSG_DEBUG, "ANQP: Roaming Consortium",
+ pos, slen);
+ if (anqp) {
+ wpabuf_free(anqp->roaming_consortium);
+ anqp->roaming_consortium = wpabuf_alloc_copy(pos, slen);
+ }
+ break;
+ case ANQP_IP_ADDR_TYPE_AVAILABILITY:
+ wpa_msg(wpa_s, MSG_INFO, "RX-ANQP " MACSTR
+ " IP Address Type Availability information",
+ MAC2STR(sa));
+ wpa_hexdump(MSG_MSGDUMP, "ANQP: IP Address Availability",
+ pos, slen);
+ if (anqp) {
+ wpabuf_free(anqp->ip_addr_type_availability);
+ anqp->ip_addr_type_availability =
+ wpabuf_alloc_copy(pos, slen);
+ }
+ break;
+ case ANQP_NAI_REALM:
+ wpa_msg(wpa_s, MSG_INFO, "RX-ANQP " MACSTR
+ " NAI Realm list", MAC2STR(sa));
+ wpa_hexdump_ascii(MSG_DEBUG, "ANQP: NAI Realm", pos, slen);
+ if (anqp) {
+ wpabuf_free(anqp->nai_realm);
+ anqp->nai_realm = wpabuf_alloc_copy(pos, slen);
+ }
+ break;
+ case ANQP_3GPP_CELLULAR_NETWORK:
+ wpa_msg(wpa_s, MSG_INFO, "RX-ANQP " MACSTR
+ " 3GPP Cellular Network information", MAC2STR(sa));
+ wpa_hexdump_ascii(MSG_DEBUG, "ANQP: 3GPP Cellular Network",
+ pos, slen);
+ if (anqp) {
+ wpabuf_free(anqp->anqp_3gpp);
+ anqp->anqp_3gpp = wpabuf_alloc_copy(pos, slen);
+ }
+ break;
+ case ANQP_DOMAIN_NAME:
+ wpa_msg(wpa_s, MSG_INFO, "RX-ANQP " MACSTR
+ " Domain Name list", MAC2STR(sa));
+ wpa_hexdump_ascii(MSG_MSGDUMP, "ANQP: Domain Name", pos, slen);
+ if (anqp) {
+ wpabuf_free(anqp->domain_name);
+ anqp->domain_name = wpabuf_alloc_copy(pos, slen);
+ }
+ break;
+ case ANQP_VENDOR_SPECIFIC:
+ if (slen < 3)
+ return;
+
+ switch (WPA_GET_BE24(pos)) {
+#ifdef CONFIG_HS20
+ case OUI_WFA:
+ pos += 3;
+ slen -= 3;
+
+ if (slen < 1)
+ return;
+ type = *pos++;
+ slen--;
+
+ switch (type) {
+ case HS20_ANQP_OUI_TYPE:
+ hs20_parse_rx_hs20_anqp_resp(wpa_s, sa, pos,
+ slen);
+ break;
+ default:
+ wpa_printf(MSG_DEBUG, "HS20: Unsupported ANQP "
+ "vendor type %u", type);
+ break;
+ }
+ break;
+#endif /* CONFIG_HS20 */
+ default:
+ wpa_printf(MSG_DEBUG, "Interworking: Unsupported "
+ "vendor-specific ANQP OUI %06x",
+ WPA_GET_BE24(pos));
+ return;
+ }
+ break;
+ default:
+ wpa_printf(MSG_DEBUG, "Interworking: Unsupported ANQP Info ID "
+ "%u", info_id);
+ break;
+ }
+}
+
+
+void anqp_resp_cb(void *ctx, const u8 *dst, u8 dialog_token,
+ enum gas_query_result result,
+ const struct wpabuf *adv_proto,
+ const struct wpabuf *resp, u16 status_code)
+{
+ struct wpa_supplicant *wpa_s = ctx;
+ const u8 *pos;
+ const u8 *end;
+ u16 info_id;
+ u16 slen;
+
+ if (result != GAS_QUERY_SUCCESS)
+ return;
+
+ pos = wpabuf_head(adv_proto);
+ if (wpabuf_len(adv_proto) < 4 || pos[0] != WLAN_EID_ADV_PROTO ||
+ pos[1] < 2 || pos[3] != ACCESS_NETWORK_QUERY_PROTOCOL) {
+ wpa_printf(MSG_DEBUG, "ANQP: Unexpected Advertisement "
+ "Protocol in response");
+ return;
+ }
+
+ pos = wpabuf_head(resp);
+ end = pos + wpabuf_len(resp);
+
+ while (pos < end) {
+ if (pos + 4 > end) {
+ wpa_printf(MSG_DEBUG, "ANQP: Invalid element");
+ break;
+ }
+ info_id = WPA_GET_LE16(pos);
+ pos += 2;
+ slen = WPA_GET_LE16(pos);
+ pos += 2;
+ if (pos + slen > end) {
+ wpa_printf(MSG_DEBUG, "ANQP: Invalid element length "
+ "for Info ID %u", info_id);
+ break;
+ }
+ interworking_parse_rx_anqp_resp(wpa_s, dst, info_id, pos,
+ slen);
+ pos += slen;
+ }
+}
+
+
+static void interworking_scan_res_handler(struct wpa_supplicant *wpa_s,
+ struct wpa_scan_results *scan_res)
+{
+ wpa_printf(MSG_DEBUG, "Interworking: Scan results available - start "
+ "ANQP fetch");
+ interworking_start_fetch_anqp(wpa_s);
+}
+
+
+int interworking_select(struct wpa_supplicant *wpa_s, int auto_select)
+{
+ interworking_stop_fetch_anqp(wpa_s);
+ wpa_s->network_select = 1;
+ wpa_s->auto_network_select = 0;
+ wpa_s->auto_select = !!auto_select;
+ wpa_s->fetch_all_anqp = 0;
+ wpa_printf(MSG_DEBUG, "Interworking: Start scan for network "
+ "selection");
+ wpa_s->scan_res_handler = interworking_scan_res_handler;
+ wpa_s->scan_req = MANUAL_SCAN_REQ;
+ wpa_supplicant_req_scan(wpa_s, 0, 0);
+
+ return 0;
+}
+
+
+static void gas_resp_cb(void *ctx, const u8 *addr, u8 dialog_token,
+ enum gas_query_result result,
+ const struct wpabuf *adv_proto,
+ const struct wpabuf *resp, u16 status_code)
+{
+ struct wpa_supplicant *wpa_s = ctx;
+
+ wpa_msg(wpa_s, MSG_INFO, GAS_RESPONSE_INFO "addr=" MACSTR
+ " dialog_token=%d status_code=%d resp_len=%d",
+ MAC2STR(addr), dialog_token, status_code,
+ resp ? (int) wpabuf_len(resp) : -1);
+ if (!resp)
+ return;
+
+ wpabuf_free(wpa_s->last_gas_resp);
+ wpa_s->last_gas_resp = wpabuf_dup(resp);
+ if (wpa_s->last_gas_resp == NULL)
+ return;
+ os_memcpy(wpa_s->last_gas_addr, addr, ETH_ALEN);
+ wpa_s->last_gas_dialog_token = dialog_token;
+}
+
+
+int gas_send_request(struct wpa_supplicant *wpa_s, const u8 *dst,
+ const struct wpabuf *adv_proto,
+ const struct wpabuf *query)
+{
+ struct wpabuf *buf;
+ int ret = 0;
+ int freq;
+ struct wpa_bss *bss;
+ int res;
+ size_t len;
+ u8 query_resp_len_limit = 0, pame_bi = 0;
+
+ freq = wpa_s->assoc_freq;
+ bss = wpa_bss_get_bssid(wpa_s, dst);
+ if (bss)
+ freq = bss->freq;
+ if (freq <= 0)
+ return -1;
+
+ wpa_printf(MSG_DEBUG, "GAS request to " MACSTR " (freq %d MHz)",
+ MAC2STR(dst), freq);
+ wpa_hexdump_buf(MSG_DEBUG, "Advertisement Protocol ID", adv_proto);
+ wpa_hexdump_buf(MSG_DEBUG, "GAS Query", query);
+
+ len = 3 + wpabuf_len(adv_proto) + 2;
+ if (query)
+ len += wpabuf_len(query);
+ buf = gas_build_initial_req(0, len);
+ if (buf == NULL)
+ return -1;
+
+ /* Advertisement Protocol IE */
+ wpabuf_put_u8(buf, WLAN_EID_ADV_PROTO);
+ wpabuf_put_u8(buf, 1 + wpabuf_len(adv_proto)); /* Length */
+ wpabuf_put_u8(buf, (query_resp_len_limit & 0x7f) |
+ (pame_bi ? 0x80 : 0));
+ wpabuf_put_buf(buf, adv_proto);
+
+ /* GAS Query */
+ if (query) {
+ wpabuf_put_le16(buf, wpabuf_len(query));
+ wpabuf_put_buf(buf, query);
+ } else
+ wpabuf_put_le16(buf, 0);
+
+ res = gas_query_req(wpa_s->gas, dst, freq, buf, gas_resp_cb, wpa_s);
+ if (res < 0) {
+ wpa_printf(MSG_DEBUG, "GAS: Failed to send Query Request");
+ ret = -1;
+ } else
+ wpa_printf(MSG_DEBUG, "GAS: Query started with dialog token "
+ "%u", res);
+
+ wpabuf_free(buf);
+ return ret;
+}
diff --git a/contrib/wpa/wpa_supplicant/interworking.h b/contrib/wpa/wpa_supplicant/interworking.h
new file mode 100644
index 0000000..4a4af82
--- /dev/null
+++ b/contrib/wpa/wpa_supplicant/interworking.h
@@ -0,0 +1,32 @@
+/*
+ * Interworking (IEEE 802.11u)
+ * Copyright (c) 2011-2012, Qualcomm Atheros
+ *
+ * This software may be distributed under the terms of the BSD license.
+ * See README for more details.
+ */
+
+#ifndef INTERWORKING_H
+#define INTERWORKING_H
+
+enum gas_query_result;
+
+int anqp_send_req(struct wpa_supplicant *wpa_s, const u8 *dst,
+ u16 info_ids[], size_t num_ids);
+void anqp_resp_cb(void *ctx, const u8 *dst, u8 dialog_token,
+ enum gas_query_result result,
+ const struct wpabuf *adv_proto,
+ const struct wpabuf *resp, u16 status_code);
+int gas_send_request(struct wpa_supplicant *wpa_s, const u8 *dst,
+ const struct wpabuf *adv_proto,
+ const struct wpabuf *query);
+int interworking_fetch_anqp(struct wpa_supplicant *wpa_s);
+void interworking_stop_fetch_anqp(struct wpa_supplicant *wpa_s);
+int interworking_select(struct wpa_supplicant *wpa_s, int auto_select);
+int interworking_connect(struct wpa_supplicant *wpa_s, struct wpa_bss *bss);
+void interworking_start_fetch_anqp(struct wpa_supplicant *wpa_s);
+int interworking_home_sp_cred(struct wpa_supplicant *wpa_s,
+ struct wpa_cred *cred,
+ struct wpabuf *domain_names);
+
+#endif /* INTERWORKING_H */
diff --git a/contrib/wpa/wpa_supplicant/main.c b/contrib/wpa/wpa_supplicant/main.c
index c0aa59c..19f7ce6 100644
--- a/contrib/wpa/wpa_supplicant/main.c
+++ b/contrib/wpa/wpa_supplicant/main.c
@@ -2,14 +2,8 @@
* WPA Supplicant / main() function for UNIX like OSes and MinGW
* Copyright (c) 2003-2007, Jouni Malinen <j@w1.fi>
*
- * This program is free software; you can redistribute it and/or modify
- * it under the terms of the GNU General Public License version 2 as
- * published by the Free Software Foundation.
- *
- * Alternatively, this software may be distributed under the terms of BSD
- * license.
- *
- * See README and COPYING for more details.
+ * This software may be distributed under the terms of the BSD license.
+ * See README for more details.
*/
#include "includes.h"
@@ -33,7 +27,8 @@ static void usage(void)
"[-g<global ctrl>] \\\n"
" -i<ifname> -c<config file> [-C<ctrl>] [-D<driver>] "
"[-p<driver_param>] \\\n"
- " [-b<br_ifname>] [-f<debug file>] \\\n"
+ " [-b<br_ifname>] [-f<debug file>] [-e<entropy file>] "
+ "\\\n"
" [-o<override driver>] [-O<override ctrl>] \\\n"
" [-N -i<ifname> -c<conf> [-C<ctrl>] "
"[-D<driver>] \\\n"
@@ -56,7 +51,8 @@ static void usage(void)
" -C = ctrl_interface parameter (only used if -c is not)\n"
" -i = interface name\n"
" -d = increase debugging verbosity (-dd even more)\n"
- " -D = driver name (can be multiple drivers: nl80211,wext)\n");
+ " -D = driver name (can be multiple drivers: nl80211,wext)\n"
+ " -e = entropy file\n");
#ifdef CONFIG_DEBUG_FILE
printf(" -f = log output to debug file instead of stdout\n");
#endif /* CONFIG_DEBUG_FILE */
@@ -65,9 +61,13 @@ static void usage(void)
#ifdef CONFIG_DEBUG_SYSLOG
printf(" -s = log output to syslog instead of stdout\n");
#endif /* CONFIG_DEBUG_SYSLOG */
+#ifdef CONFIG_DEBUG_LINUX_TRACING
+ printf(" -T = record to Linux tracing in addition to logging\n");
+ printf(" (records all messages regardless of debug verbosity)\n");
+#endif /* CONFIG_DEBUG_LINUX_TRACING */
printf(" -t = include timestamp in debug messages\n"
" -h = show this help text\n"
- " -L = show license (GPL and BSD)\n"
+ " -L = show license (BSD)\n"
" -o = override driver parameter for new interfaces\n"
" -O = override ctrl_interface parameter for new interfaces\n"
" -p = driver parameters\n"
@@ -101,20 +101,31 @@ static void license(void)
}
-static void wpa_supplicant_fd_workaround(void)
+static void wpa_supplicant_fd_workaround(int start)
{
#ifdef __linux__
- int s, i;
+ static int fd[3] = { -1, -1, -1 };
+ int i;
/* When started from pcmcia-cs scripts, wpa_supplicant might start with
* fd 0, 1, and 2 closed. This will cause some issues because many
* places in wpa_supplicant are still printing out to stdout. As a
* workaround, make sure that fd's 0, 1, and 2 are not used for other
* sockets. */
- for (i = 0; i < 3; i++) {
- s = open("/dev/null", O_RDWR);
- if (s > 2) {
- close(s);
- break;
+ if (start) {
+ for (i = 0; i < 3; i++) {
+ fd[i] = open("/dev/null", O_RDWR);
+ if (fd[i] > 2) {
+ close(fd[i]);
+ fd[i] = -1;
+ break;
+ }
+ }
+ } else {
+ for (i = 0; i < 3; i++) {
+ if (fd[i] >= 0) {
+ close(fd[i]);
+ fd[i] = -1;
+ }
}
}
#endif /* __linux__ */
@@ -140,10 +151,11 @@ int main(int argc, char *argv[])
return -1;
iface_count = 1;
- wpa_supplicant_fd_workaround();
+ wpa_supplicant_fd_workaround(1);
for (;;) {
- c = getopt(argc, argv, "b:Bc:C:D:df:g:hi:KLNo:O:p:P:qstuvW");
+ c = getopt(argc, argv,
+ "b:Bc:C:D:de:f:g:hi:KLNo:O:p:P:qsTtuvW");
if (c < 0)
break;
switch (c) {
@@ -172,6 +184,9 @@ int main(int argc, char *argv[])
params.wpa_debug_level--;
break;
#endif /* CONFIG_NO_STDOUT_DEBUG */
+ case 'e':
+ params.entropy_file = optarg;
+ break;
#ifdef CONFIG_DEBUG_FILE
case 'f':
params.wpa_debug_file_path = optarg;
@@ -215,6 +230,11 @@ int main(int argc, char *argv[])
params.wpa_debug_syslog++;
break;
#endif /* CONFIG_DEBUG_SYSLOG */
+#ifdef CONFIG_DEBUG_LINUX_TRACING
+ case 'T':
+ params.wpa_debug_tracing++;
+ break;
+#endif /* CONFIG_DEBUG_LINUX_TRACING */
case 't':
params.wpa_debug_timestamp++;
break;
@@ -232,8 +252,8 @@ int main(int argc, char *argv[])
break;
case 'N':
iface_count++;
- iface = os_realloc(ifaces, iface_count *
- sizeof(struct wpa_interface));
+ iface = os_realloc_array(ifaces, iface_count,
+ sizeof(struct wpa_interface));
if (iface == NULL)
goto out;
ifaces = iface;
@@ -253,6 +273,9 @@ int main(int argc, char *argv[])
wpa_printf(MSG_ERROR, "Failed to initialize wpa_supplicant");
exitcode = -1;
goto out;
+ } else {
+ wpa_printf(MSG_INFO, "Successfully initialized "
+ "wpa_supplicant");
}
for (i = 0; exitcode == 0 && i < iface_count; i++) {
@@ -276,6 +299,7 @@ int main(int argc, char *argv[])
wpa_supplicant_deinit(global);
out:
+ wpa_supplicant_fd_workaround(0);
os_free(ifaces);
os_free(params.pid_file);
diff --git a/contrib/wpa/wpa_supplicant/main_none.c b/contrib/wpa/wpa_supplicant/main_none.c
index 993338a..010c30a3 100644
--- a/contrib/wpa/wpa_supplicant/main_none.c
+++ b/contrib/wpa/wpa_supplicant/main_none.c
@@ -2,14 +2,8 @@
* WPA Supplicant / Example program entrypoint
* Copyright (c) 2003-2005, Jouni Malinen <j@w1.fi>
*
- * This program is free software; you can redistribute it and/or modify
- * it under the terms of the GNU General Public License version 2 as
- * published by the Free Software Foundation.
- *
- * Alternatively, this software may be distributed under the terms of BSD
- * license.
- *
- * See README and COPYING for more details.
+ * This software may be distributed under the terms of the BSD license.
+ * See README for more details.
*/
#include "includes.h"
diff --git a/contrib/wpa/wpa_supplicant/mlme.c b/contrib/wpa/wpa_supplicant/mlme.c
deleted file mode 100644
index eb60ac5..0000000
--- a/contrib/wpa/wpa_supplicant/mlme.c
+++ /dev/null
@@ -1,3198 +0,0 @@
-/*
- * WPA Supplicant - Client mode MLME
- * Copyright (c) 2003-2008, Jouni Malinen <j@w1.fi>
- * Copyright (c) 2004, Instant802 Networks, Inc.
- * Copyright (c) 2005-2006, Devicescape Software, Inc.
- *
- * This program is free software; you can redistribute it and/or modify
- * it under the terms of the GNU General Public License version 2 as
- * published by the Free Software Foundation.
- *
- * Alternatively, this software may be distributed under the terms of BSD
- * license.
- *
- * See README and COPYING for more details.
- */
-
-#include "includes.h"
-
-#include "common.h"
-#include "eloop.h"
-#include "config_ssid.h"
-#include "wpa_supplicant_i.h"
-#include "notify.h"
-#include "driver_i.h"
-#include "rsn_supp/wpa.h"
-#include "common/ieee802_11_defs.h"
-#include "common/ieee802_11_common.h"
-#include "mlme.h"
-
-
-/* Timeouts and intervals in milliseconds */
-#define IEEE80211_AUTH_TIMEOUT (200)
-#define IEEE80211_AUTH_MAX_TRIES 3
-#define IEEE80211_ASSOC_TIMEOUT (200)
-#define IEEE80211_ASSOC_MAX_TRIES 3
-#define IEEE80211_MONITORING_INTERVAL (2000)
-#define IEEE80211_PROBE_INTERVAL (60000)
-#define IEEE80211_RETRY_AUTH_INTERVAL (1000)
-#define IEEE80211_SCAN_INTERVAL (2000)
-#define IEEE80211_SCAN_INTERVAL_SLOW (15000)
-#define IEEE80211_IBSS_JOIN_TIMEOUT (20000)
-
-#define IEEE80211_PROBE_DELAY (33)
-#define IEEE80211_CHANNEL_TIME (33)
-#define IEEE80211_PASSIVE_CHANNEL_TIME (200)
-#define IEEE80211_SCAN_RESULT_EXPIRE (10000)
-#define IEEE80211_IBSS_MERGE_INTERVAL (30000)
-#define IEEE80211_IBSS_INACTIVITY_LIMIT (60000)
-
-#define IEEE80211_IBSS_MAX_STA_ENTRIES 128
-
-
-#define IEEE80211_FC(type, stype) host_to_le16((type << 2) | (stype << 4))
-
-
-struct ieee80211_sta_bss {
- struct ieee80211_sta_bss *next;
- struct ieee80211_sta_bss *hnext;
-
- u8 bssid[ETH_ALEN];
- u8 ssid[MAX_SSID_LEN];
- size_t ssid_len;
- u16 capability; /* host byte order */
- int hw_mode;
- int channel;
- int freq;
- int rssi;
- u8 *ie;
- size_t ie_len;
- u8 *wpa_ie;
- size_t wpa_ie_len;
- u8 *rsn_ie;
- size_t rsn_ie_len;
- u8 *wmm_ie;
- size_t wmm_ie_len;
- u8 *mdie;
- size_t mdie_len;
-#define IEEE80211_MAX_SUPP_RATES 32
- u8 supp_rates[IEEE80211_MAX_SUPP_RATES];
- size_t supp_rates_len;
- int beacon_int;
- u64 timestamp;
-
- int probe_resp;
- struct os_time last_update;
-};
-
-
-static void ieee80211_send_probe_req(struct wpa_supplicant *wpa_s,
- const u8 *dst,
- const u8 *ssid, size_t ssid_len);
-static struct ieee80211_sta_bss *
-ieee80211_bss_get(struct wpa_supplicant *wpa_s, const u8 *bssid);
-static int ieee80211_sta_find_ibss(struct wpa_supplicant *wpa_s);
-static int ieee80211_sta_wep_configured(struct wpa_supplicant *wpa_s);
-static void ieee80211_sta_timer(void *eloop_ctx, void *timeout_ctx);
-static void ieee80211_sta_scan_timer(void *eloop_ctx, void *timeout_ctx);
-static void ieee80211_build_tspec(struct wpabuf *buf);
-static int ieee80211_sta_set_probe_req_ie(struct wpa_supplicant *wpa_s,
- const u8 *ies, size_t ies_len);
-
-
-static int ieee80211_sta_set_channel(struct wpa_supplicant *wpa_s,
- enum hostapd_hw_mode phymode, int chan,
- int freq)
-{
- size_t i;
- struct hostapd_hw_modes *mode;
-
- for (i = 0; i < wpa_s->mlme.num_modes; i++) {
- mode = &wpa_s->mlme.modes[i];
- if (mode->mode == phymode) {
- wpa_s->mlme.curr_rates = mode->rates;
- wpa_s->mlme.num_curr_rates = mode->num_rates;
- break;
- }
- }
-
- return wpa_drv_set_channel(wpa_s, phymode, chan, freq);
-}
-
-
-static int ecw2cw(int ecw)
-{
- int cw = 1;
- while (ecw > 0) {
- cw <<= 1;
- ecw--;
- }
- return cw - 1;
-}
-
-
-static void ieee80211_sta_wmm_params(struct wpa_supplicant *wpa_s,
- const u8 *wmm_param, size_t wmm_param_len)
-{
- size_t left;
- int count;
- const u8 *pos;
- u8 wmm_acm;
-
- if (wmm_param_len < 8 || wmm_param[5] /* version */ != 1)
- return;
- count = wmm_param[6] & 0x0f;
- if (count == wpa_s->mlme.wmm_last_param_set)
- return;
- wpa_s->mlme.wmm_last_param_set = count;
-
- pos = wmm_param + 8;
- left = wmm_param_len - 8;
-
- wmm_acm = 0;
- for (; left >= 4; left -= 4, pos += 4) {
- int aci = (pos[0] >> 5) & 0x03;
- int acm = (pos[0] >> 4) & 0x01;
- int aifs, cw_max, cw_min, burst_time;
-
- switch (aci) {
- case 1: /* AC_BK */
- if (acm)
- wmm_acm |= BIT(1) | BIT(2); /* BK/- */
- break;
- case 2: /* AC_VI */
- if (acm)
- wmm_acm |= BIT(4) | BIT(5); /* CL/VI */
- break;
- case 3: /* AC_VO */
- if (acm)
- wmm_acm |= BIT(6) | BIT(7); /* VO/NC */
- break;
- case 0: /* AC_BE */
- default:
- if (acm)
- wmm_acm |= BIT(0) | BIT(3); /* BE/EE */
- break;
- }
-
- aifs = pos[0] & 0x0f;
- cw_max = ecw2cw((pos[1] & 0xf0) >> 4);
- cw_min = ecw2cw(pos[1] & 0x0f);
- /* TXOP is in units of 32 usec; burst_time in 0.1 ms */
- burst_time = (pos[2] | (pos[3] << 8)) * 32 / 100;
- wpa_printf(MSG_DEBUG, "MLME: WMM aci=%d acm=%d aifs=%d "
- "cWmin=%d cWmax=%d burst=%d",
- aci, acm, aifs, cw_min, cw_max, burst_time);
- /* TODO: driver configuration */
- }
-}
-
-
-static void ieee80211_set_associated(struct wpa_supplicant *wpa_s, int assoc)
-{
- if (wpa_s->mlme.associated == assoc && !assoc)
- return;
-
- wpa_s->mlme.associated = assoc;
-
- if (assoc) {
- union wpa_event_data data;
- os_memset(&data, 0, sizeof(data));
- wpa_s->mlme.prev_bssid_set = 1;
- os_memcpy(wpa_s->mlme.prev_bssid, wpa_s->bssid, ETH_ALEN);
- data.assoc_info.req_ies = wpa_s->mlme.assocreq_ies;
- data.assoc_info.req_ies_len = wpa_s->mlme.assocreq_ies_len;
- data.assoc_info.resp_ies = wpa_s->mlme.assocresp_ies;
- data.assoc_info.resp_ies_len = wpa_s->mlme.assocresp_ies_len;
- data.assoc_info.freq = wpa_s->mlme.freq;
- wpa_supplicant_event(wpa_s, EVENT_ASSOC, &data);
- } else {
- wpa_supplicant_event(wpa_s, EVENT_DISASSOC, NULL);
- }
- os_get_time(&wpa_s->mlme.last_probe);
-}
-
-
-static int ieee80211_sta_tx(struct wpa_supplicant *wpa_s, const u8 *buf,
- size_t len)
-{
- return wpa_drv_send_mlme(wpa_s, buf, len);
-}
-
-
-static void ieee80211_send_auth(struct wpa_supplicant *wpa_s,
- int transaction, const u8 *extra,
- size_t extra_len, int encrypt)
-{
- u8 *buf;
- size_t len;
- struct ieee80211_mgmt *mgmt;
-
- buf = os_malloc(sizeof(*mgmt) + 6 + extra_len);
- if (buf == NULL) {
- wpa_printf(MSG_DEBUG, "MLME: failed to allocate buffer for "
- "auth frame");
- return;
- }
-
- mgmt = (struct ieee80211_mgmt *) buf;
- len = 24 + 6;
- os_memset(mgmt, 0, 24 + 6);
- mgmt->frame_control = IEEE80211_FC(WLAN_FC_TYPE_MGMT,
- WLAN_FC_STYPE_AUTH);
- if (encrypt)
- mgmt->frame_control |= host_to_le16(WLAN_FC_ISWEP);
- os_memcpy(mgmt->da, wpa_s->bssid, ETH_ALEN);
- os_memcpy(mgmt->sa, wpa_s->own_addr, ETH_ALEN);
- os_memcpy(mgmt->bssid, wpa_s->bssid, ETH_ALEN);
- mgmt->u.auth.auth_alg = host_to_le16(wpa_s->mlme.auth_alg);
- mgmt->u.auth.auth_transaction = host_to_le16(transaction);
- wpa_s->mlme.auth_transaction = transaction + 1;
- mgmt->u.auth.status_code = host_to_le16(0);
- if (extra) {
- os_memcpy(buf + len, extra, extra_len);
- len += extra_len;
- }
-
- ieee80211_sta_tx(wpa_s, buf, len);
- os_free(buf);
-}
-
-
-static void ieee80211_reschedule_timer(struct wpa_supplicant *wpa_s, int ms)
-{
- eloop_cancel_timeout(ieee80211_sta_timer, wpa_s, NULL);
- eloop_register_timeout(ms / 1000, 1000 * (ms % 1000),
- ieee80211_sta_timer, wpa_s, NULL);
-}
-
-
-static void ieee80211_authenticate(struct wpa_supplicant *wpa_s)
-{
- u8 *extra;
- size_t extra_len;
-
- wpa_s->mlme.auth_tries++;
- if (wpa_s->mlme.auth_tries > IEEE80211_AUTH_MAX_TRIES) {
- wpa_printf(MSG_DEBUG, "MLME: authentication with AP " MACSTR
- " timed out", MAC2STR(wpa_s->bssid));
- return;
- }
-
- wpa_s->mlme.state = IEEE80211_AUTHENTICATE;
- wpa_printf(MSG_DEBUG, "MLME: authenticate with AP " MACSTR,
- MAC2STR(wpa_s->bssid));
-
- extra = NULL;
- extra_len = 0;
-
-#ifdef CONFIG_IEEE80211R
- if ((wpa_s->mlme.key_mgmt == KEY_MGMT_FT_802_1X ||
- wpa_s->mlme.key_mgmt == KEY_MGMT_FT_PSK) &&
- wpa_s->mlme.ft_ies) {
- struct ieee80211_sta_bss *bss;
- struct rsn_mdie *mdie = NULL;
- bss = ieee80211_bss_get(wpa_s, wpa_s->bssid);
- if (bss && bss->mdie_len >= 2 + sizeof(*mdie))
- mdie = (struct rsn_mdie *) (bss->mdie + 2);
- if (mdie &&
- os_memcmp(mdie->mobility_domain, wpa_s->mlme.current_md,
- MOBILITY_DOMAIN_ID_LEN) == 0) {
- wpa_printf(MSG_DEBUG, "MLME: Trying to use FT "
- "over-the-air");
- wpa_s->mlme.auth_alg = WLAN_AUTH_FT;
- extra = wpa_s->mlme.ft_ies;
- extra_len = wpa_s->mlme.ft_ies_len;
- }
- }
-#endif /* CONFIG_IEEE80211R */
-
- ieee80211_send_auth(wpa_s, 1, extra, extra_len, 0);
-
- ieee80211_reschedule_timer(wpa_s, IEEE80211_AUTH_TIMEOUT);
-}
-
-
-static void ieee80211_send_assoc(struct wpa_supplicant *wpa_s)
-{
- struct ieee80211_mgmt *mgmt;
- u8 *pos, *ies, *buf;
- int i, len;
- u16 capab;
- struct ieee80211_sta_bss *bss;
- int wmm = 0;
- size_t blen, buflen;
-
- if (wpa_s->mlme.curr_rates == NULL) {
- wpa_printf(MSG_DEBUG, "MLME: curr_rates not set for assoc");
- return;
- }
-
- buflen = sizeof(*mgmt) + 200 + wpa_s->mlme.extra_ie_len +
- wpa_s->mlme.ssid_len;
-#ifdef CONFIG_IEEE80211R
- if (wpa_s->mlme.ft_ies)
- buflen += wpa_s->mlme.ft_ies_len;
-#endif /* CONFIG_IEEE80211R */
- buf = os_malloc(buflen);
- if (buf == NULL) {
- wpa_printf(MSG_DEBUG, "MLME: failed to allocate buffer for "
- "assoc frame");
- return;
- }
- blen = 0;
-
- capab = wpa_s->mlme.capab;
- if (wpa_s->mlme.phymode == HOSTAPD_MODE_IEEE80211G) {
- capab |= WLAN_CAPABILITY_SHORT_SLOT_TIME |
- WLAN_CAPABILITY_SHORT_PREAMBLE;
- }
- bss = ieee80211_bss_get(wpa_s, wpa_s->bssid);
- if (bss) {
- if (bss->capability & WLAN_CAPABILITY_PRIVACY)
- capab |= WLAN_CAPABILITY_PRIVACY;
- if (bss->wmm_ie) {
- wmm = 1;
- }
- }
-
- mgmt = (struct ieee80211_mgmt *) buf;
- blen += 24;
- os_memset(mgmt, 0, 24);
- os_memcpy(mgmt->da, wpa_s->bssid, ETH_ALEN);
- os_memcpy(mgmt->sa, wpa_s->own_addr, ETH_ALEN);
- os_memcpy(mgmt->bssid, wpa_s->bssid, ETH_ALEN);
-
- if (wpa_s->mlme.prev_bssid_set) {
- blen += 10;
- mgmt->frame_control = IEEE80211_FC(WLAN_FC_TYPE_MGMT,
- WLAN_FC_STYPE_REASSOC_REQ);
- mgmt->u.reassoc_req.capab_info = host_to_le16(capab);
- mgmt->u.reassoc_req.listen_interval = host_to_le16(1);
- os_memcpy(mgmt->u.reassoc_req.current_ap,
- wpa_s->mlme.prev_bssid,
- ETH_ALEN);
- } else {
- blen += 4;
- mgmt->frame_control = IEEE80211_FC(WLAN_FC_TYPE_MGMT,
- WLAN_FC_STYPE_ASSOC_REQ);
- mgmt->u.assoc_req.capab_info = host_to_le16(capab);
- mgmt->u.assoc_req.listen_interval = host_to_le16(1);
- }
-
- /* SSID */
- ies = pos = buf + blen;
- blen += 2 + wpa_s->mlme.ssid_len;
- *pos++ = WLAN_EID_SSID;
- *pos++ = wpa_s->mlme.ssid_len;
- os_memcpy(pos, wpa_s->mlme.ssid, wpa_s->mlme.ssid_len);
-
- len = wpa_s->mlme.num_curr_rates;
- if (len > 8)
- len = 8;
- pos = buf + blen;
- blen += len + 2;
- *pos++ = WLAN_EID_SUPP_RATES;
- *pos++ = len;
- for (i = 0; i < len; i++)
- *pos++ = (u8) (wpa_s->mlme.curr_rates[i] / 5);
-
- if (wpa_s->mlme.num_curr_rates > len) {
- pos = buf + blen;
- blen += wpa_s->mlme.num_curr_rates - len + 2;
- *pos++ = WLAN_EID_EXT_SUPP_RATES;
- *pos++ = wpa_s->mlme.num_curr_rates - len;
- for (i = len; i < wpa_s->mlme.num_curr_rates; i++)
- *pos++ = (u8) (wpa_s->mlme.curr_rates[i] / 5);
- }
-
- if (wpa_s->mlme.extra_ie && wpa_s->mlme.auth_alg != WLAN_AUTH_FT) {
- pos = buf + blen;
- blen += wpa_s->mlme.extra_ie_len;
- os_memcpy(pos, wpa_s->mlme.extra_ie, wpa_s->mlme.extra_ie_len);
- }
-
-#ifdef CONFIG_IEEE80211R
- if ((wpa_s->mlme.key_mgmt == KEY_MGMT_FT_802_1X ||
- wpa_s->mlme.key_mgmt == KEY_MGMT_FT_PSK) &&
- wpa_s->mlme.auth_alg != WLAN_AUTH_FT &&
- bss && bss->mdie &&
- bss->mdie_len >= 2 + sizeof(struct rsn_mdie) &&
- bss->mdie[1] >= sizeof(struct rsn_mdie)) {
- pos = buf + blen;
- blen += 2 + sizeof(struct rsn_mdie);
- *pos++ = WLAN_EID_MOBILITY_DOMAIN;
- *pos++ = sizeof(struct rsn_mdie);
- os_memcpy(pos, bss->mdie + 2, MOBILITY_DOMAIN_ID_LEN);
- pos += MOBILITY_DOMAIN_ID_LEN;
- *pos++ = 0; /* FIX: copy from the target AP's MDIE */
- }
-
- if ((wpa_s->mlme.key_mgmt == KEY_MGMT_FT_802_1X ||
- wpa_s->mlme.key_mgmt == KEY_MGMT_FT_PSK) &&
- wpa_s->mlme.auth_alg == WLAN_AUTH_FT && wpa_s->mlme.ft_ies) {
- pos = buf + blen;
- os_memcpy(pos, wpa_s->mlme.ft_ies, wpa_s->mlme.ft_ies_len);
- pos += wpa_s->mlme.ft_ies_len;
- blen += wpa_s->mlme.ft_ies_len;
- }
-#endif /* CONFIG_IEEE80211R */
-
- if (wmm && wpa_s->mlme.wmm_enabled) {
- pos = buf + blen;
- blen += 9;
- *pos++ = WLAN_EID_VENDOR_SPECIFIC;
- *pos++ = 7; /* len */
- *pos++ = 0x00; /* Microsoft OUI 00:50:F2 */
- *pos++ = 0x50;
- *pos++ = 0xf2;
- *pos++ = 2; /* WMM */
- *pos++ = 0; /* WMM info */
- *pos++ = 1; /* WMM ver */
- *pos++ = 0;
- }
-
- os_free(wpa_s->mlme.assocreq_ies);
- wpa_s->mlme.assocreq_ies_len = (buf + blen) - ies;
- wpa_s->mlme.assocreq_ies = os_malloc(wpa_s->mlme.assocreq_ies_len);
- if (wpa_s->mlme.assocreq_ies) {
- os_memcpy(wpa_s->mlme.assocreq_ies, ies,
- wpa_s->mlme.assocreq_ies_len);
- }
-
- ieee80211_sta_tx(wpa_s, buf, blen);
- os_free(buf);
-}
-
-
-static void ieee80211_send_deauth(struct wpa_supplicant *wpa_s, u16 reason)
-{
- u8 *buf;
- size_t len;
- struct ieee80211_mgmt *mgmt;
-
- buf = os_zalloc(sizeof(*mgmt));
- if (buf == NULL) {
- wpa_printf(MSG_DEBUG, "MLME: failed to allocate buffer for "
- "deauth frame");
- return;
- }
-
- mgmt = (struct ieee80211_mgmt *) buf;
- len = 24;
- os_memcpy(mgmt->da, wpa_s->bssid, ETH_ALEN);
- os_memcpy(mgmt->sa, wpa_s->own_addr, ETH_ALEN);
- os_memcpy(mgmt->bssid, wpa_s->bssid, ETH_ALEN);
- mgmt->frame_control = IEEE80211_FC(WLAN_FC_TYPE_MGMT,
- WLAN_FC_STYPE_DEAUTH);
- len += 2;
- mgmt->u.deauth.reason_code = host_to_le16(reason);
-
- ieee80211_sta_tx(wpa_s, buf, len);
- os_free(buf);
-}
-
-
-static void ieee80211_send_disassoc(struct wpa_supplicant *wpa_s, u16 reason)
-{
- u8 *buf;
- size_t len;
- struct ieee80211_mgmt *mgmt;
-
- buf = os_zalloc(sizeof(*mgmt));
- if (buf == NULL) {
- wpa_printf(MSG_DEBUG, "MLME: failed to allocate buffer for "
- "disassoc frame");
- return;
- }
-
- mgmt = (struct ieee80211_mgmt *) buf;
- len = 24;
- os_memcpy(mgmt->da, wpa_s->bssid, ETH_ALEN);
- os_memcpy(mgmt->sa, wpa_s->own_addr, ETH_ALEN);
- os_memcpy(mgmt->bssid, wpa_s->bssid, ETH_ALEN);
- mgmt->frame_control = IEEE80211_FC(WLAN_FC_TYPE_MGMT,
- WLAN_FC_STYPE_DISASSOC);
- len += 2;
- mgmt->u.disassoc.reason_code = host_to_le16(reason);
-
- ieee80211_sta_tx(wpa_s, buf, len);
- os_free(buf);
-}
-
-
-static int ieee80211_privacy_mismatch(struct wpa_supplicant *wpa_s)
-{
- struct ieee80211_sta_bss *bss;
- int res = 0;
-
- if (wpa_s->mlme.mixed_cell ||
- wpa_s->mlme.key_mgmt != KEY_MGMT_NONE)
- return 0;
-
- bss = ieee80211_bss_get(wpa_s, wpa_s->bssid);
- if (bss == NULL)
- return 0;
-
- if (ieee80211_sta_wep_configured(wpa_s) !=
- !!(bss->capability & WLAN_CAPABILITY_PRIVACY))
- res = 1;
-
- return res;
-}
-
-
-static void ieee80211_associate(struct wpa_supplicant *wpa_s)
-{
- wpa_s->mlme.assoc_tries++;
- if (wpa_s->mlme.assoc_tries > IEEE80211_ASSOC_MAX_TRIES) {
- wpa_printf(MSG_DEBUG, "MLME: association with AP " MACSTR
- " timed out", MAC2STR(wpa_s->bssid));
- return;
- }
-
- wpa_s->mlme.state = IEEE80211_ASSOCIATE;
- wpa_printf(MSG_DEBUG, "MLME: associate with AP " MACSTR,
- MAC2STR(wpa_s->bssid));
- if (ieee80211_privacy_mismatch(wpa_s)) {
- wpa_printf(MSG_DEBUG, "MLME: mismatch in privacy "
- "configuration and mixed-cell disabled - abort "
- "association");
- return;
- }
-
- ieee80211_send_assoc(wpa_s);
-
- ieee80211_reschedule_timer(wpa_s, IEEE80211_ASSOC_TIMEOUT);
-}
-
-
-static void ieee80211_associated(struct wpa_supplicant *wpa_s)
-{
- int disassoc;
-
- /* TODO: start monitoring current AP signal quality and number of
- * missed beacons. Scan other channels every now and then and search
- * for better APs. */
- /* TODO: remove expired BSSes */
-
- wpa_s->mlme.state = IEEE80211_ASSOCIATED;
-
-#if 0 /* FIX */
- sta = sta_info_get(local, wpa_s->bssid);
- if (sta == NULL) {
- wpa_printf(MSG_DEBUG "MLME: No STA entry for own AP " MACSTR,
- MAC2STR(wpa_s->bssid));
- disassoc = 1;
- } else {
- disassoc = 0;
- if (time_after(jiffies,
- sta->last_rx + IEEE80211_MONITORING_INTERVAL)) {
- if (wpa_s->mlme.probereq_poll) {
- wpa_printf(MSG_DEBUG "MLME: No ProbeResp from "
- "current AP " MACSTR " - assume "
- "out of range",
- MAC2STR(wpa_s->bssid));
- disassoc = 1;
- } else {
- ieee80211_send_probe_req(
- wpa_s->bssid,
- wpa_s->mlme.scan_ssid,
- wpa_s->mlme.scan_ssid_len);
- wpa_s->mlme.probereq_poll = 1;
- }
- } else {
- wpa_s->mlme.probereq_poll = 0;
- if (time_after(jiffies, wpa_s->mlme.last_probe +
- IEEE80211_PROBE_INTERVAL)) {
- wpa_s->mlme.last_probe = jiffies;
- ieee80211_send_probe_req(wpa_s->bssid,
- wpa_s->mlme.ssid,
- wpa_s->mlme.ssid_len);
- }
- }
- sta_info_release(local, sta);
- }
-#else
- disassoc = 0;
-#endif
- if (disassoc) {
- wpa_supplicant_event(wpa_s, EVENT_DISASSOC, NULL);
- ieee80211_reschedule_timer(wpa_s,
- IEEE80211_MONITORING_INTERVAL +
- 30000);
- } else {
- ieee80211_reschedule_timer(wpa_s,
- IEEE80211_MONITORING_INTERVAL);
- }
-}
-
-
-static void ieee80211_send_probe_req(struct wpa_supplicant *wpa_s,
- const u8 *dst,
- const u8 *ssid, size_t ssid_len)
-{
- u8 *buf;
- size_t len;
- struct ieee80211_mgmt *mgmt;
- u8 *pos, *supp_rates;
- u8 *esupp_rates = NULL;
- int i;
-
- buf = os_malloc(sizeof(*mgmt) + 200 + wpa_s->mlme.extra_probe_ie_len);
- if (buf == NULL) {
- wpa_printf(MSG_DEBUG, "MLME: failed to allocate buffer for "
- "probe request");
- return;
- }
-
- mgmt = (struct ieee80211_mgmt *) buf;
- len = 24;
- os_memset(mgmt, 0, 24);
- mgmt->frame_control = IEEE80211_FC(WLAN_FC_TYPE_MGMT,
- WLAN_FC_STYPE_PROBE_REQ);
- os_memcpy(mgmt->sa, wpa_s->own_addr, ETH_ALEN);
- if (dst) {
- os_memcpy(mgmt->da, dst, ETH_ALEN);
- os_memcpy(mgmt->bssid, dst, ETH_ALEN);
- } else {
- os_memset(mgmt->da, 0xff, ETH_ALEN);
- os_memset(mgmt->bssid, 0xff, ETH_ALEN);
- }
- pos = buf + len;
- len += 2 + ssid_len;
- *pos++ = WLAN_EID_SSID;
- *pos++ = ssid_len;
- os_memcpy(pos, ssid, ssid_len);
-
- supp_rates = buf + len;
- len += 2;
- supp_rates[0] = WLAN_EID_SUPP_RATES;
- supp_rates[1] = 0;
- for (i = 0; i < wpa_s->mlme.num_curr_rates; i++) {
- if (esupp_rates) {
- pos = buf + len;
- len++;
- esupp_rates[1]++;
- } else if (supp_rates[1] == 8) {
- esupp_rates = pos;
- esupp_rates[0] = WLAN_EID_EXT_SUPP_RATES;
- esupp_rates[1] = 1;
- pos = &esupp_rates[2];
- len += 3;
- } else {
- pos = buf + len;
- len++;
- supp_rates[1]++;
- }
- *pos++ = wpa_s->mlme.curr_rates[i] / 5;
- }
-
- if (wpa_s->mlme.extra_probe_ie) {
- os_memcpy(pos, wpa_s->mlme.extra_probe_ie,
- wpa_s->mlme.extra_probe_ie_len);
- len += wpa_s->mlme.extra_probe_ie_len;
- }
-
- ieee80211_sta_tx(wpa_s, buf, len);
- os_free(buf);
-}
-
-
-static int ieee80211_sta_wep_configured(struct wpa_supplicant *wpa_s)
-{
-#if 0 /* FIX */
- if (sdata == NULL || sdata->default_key == NULL ||
- sdata->default_key->alg != ALG_WEP)
- return 0;
- return 1;
-#else
- return 0;
-#endif
-}
-
-
-static void ieee80211_auth_completed(struct wpa_supplicant *wpa_s)
-{
- wpa_printf(MSG_DEBUG, "MLME: authenticated");
- wpa_s->mlme.authenticated = 1;
- ieee80211_associate(wpa_s);
-}
-
-
-static void ieee80211_auth_challenge(struct wpa_supplicant *wpa_s,
- struct ieee80211_mgmt *mgmt,
- size_t len,
- struct ieee80211_rx_status *rx_status)
-{
- u8 *pos;
- struct ieee802_11_elems elems;
-
- wpa_printf(MSG_DEBUG, "MLME: replying to auth challenge");
- pos = mgmt->u.auth.variable;
- if (ieee802_11_parse_elems(pos, len - (pos - (u8 *) mgmt), &elems, 0)
- == ParseFailed) {
- wpa_printf(MSG_DEBUG, "MLME: failed to parse Auth(challenge)");
- return;
- }
- if (elems.challenge == NULL) {
- wpa_printf(MSG_DEBUG, "MLME: no challenge IE in shared key "
- "auth frame");
- return;
- }
- ieee80211_send_auth(wpa_s, 3, elems.challenge - 2,
- elems.challenge_len + 2, 1);
-}
-
-
-static void ieee80211_rx_mgmt_auth(struct wpa_supplicant *wpa_s,
- struct ieee80211_mgmt *mgmt,
- size_t len,
- struct ieee80211_rx_status *rx_status)
-{
- struct wpa_ssid *ssid = wpa_s->current_ssid;
- u16 auth_alg, auth_transaction, status_code;
- int adhoc;
-
- adhoc = ssid && ssid->mode == WPAS_MODE_IBSS;
-
- if (wpa_s->mlme.state != IEEE80211_AUTHENTICATE && !adhoc) {
- wpa_printf(MSG_DEBUG, "MLME: authentication frame received "
- "from " MACSTR ", but not in authenticate state - "
- "ignored", MAC2STR(mgmt->sa));
- return;
- }
-
- if (len < 24 + 6) {
- wpa_printf(MSG_DEBUG, "MLME: too short (%lu) authentication "
- "frame received from " MACSTR " - ignored",
- (unsigned long) len, MAC2STR(mgmt->sa));
- return;
- }
-
- if (!adhoc && os_memcmp(wpa_s->bssid, mgmt->sa, ETH_ALEN) != 0) {
- wpa_printf(MSG_DEBUG, "MLME: authentication frame received "
- "from unknown AP (SA=" MACSTR " BSSID=" MACSTR
- ") - ignored",
- MAC2STR(mgmt->sa), MAC2STR(mgmt->bssid));
- return;
- }
-
- if (adhoc && os_memcmp(wpa_s->bssid, mgmt->bssid, ETH_ALEN) != 0) {
- wpa_printf(MSG_DEBUG, "MLME: authentication frame received "
- "from unknown BSSID (SA=" MACSTR " BSSID=" MACSTR
- ") - ignored",
- MAC2STR(mgmt->sa), MAC2STR(mgmt->bssid));
- return;
- }
-
- auth_alg = le_to_host16(mgmt->u.auth.auth_alg);
- auth_transaction = le_to_host16(mgmt->u.auth.auth_transaction);
- status_code = le_to_host16(mgmt->u.auth.status_code);
-
- wpa_printf(MSG_DEBUG, "MLME: RX authentication from " MACSTR
- " (alg=%d transaction=%d status=%d)",
- MAC2STR(mgmt->sa), auth_alg, auth_transaction, status_code);
-
- if (adhoc) {
- /* IEEE 802.11 standard does not require authentication in IBSS
- * networks and most implementations do not seem to use it.
- * However, try to reply to authentication attempts if someone
- * has actually implemented this.
- * TODO: Could implement shared key authentication. */
- if (auth_alg != WLAN_AUTH_OPEN || auth_transaction != 1) {
- wpa_printf(MSG_DEBUG, "MLME: unexpected IBSS "
- "authentication frame (alg=%d "
- "transaction=%d)",
- auth_alg, auth_transaction);
- return;
- }
- ieee80211_send_auth(wpa_s, 2, NULL, 0, 0);
- }
-
- if (auth_alg != wpa_s->mlme.auth_alg ||
- auth_transaction != wpa_s->mlme.auth_transaction) {
- wpa_printf(MSG_DEBUG, "MLME: unexpected authentication frame "
- "(alg=%d transaction=%d)",
- auth_alg, auth_transaction);
- return;
- }
-
- if (status_code != WLAN_STATUS_SUCCESS) {
- wpa_printf(MSG_DEBUG, "MLME: AP denied authentication "
- "(auth_alg=%d code=%d)", wpa_s->mlme.auth_alg,
- status_code);
- if (status_code == WLAN_STATUS_NOT_SUPPORTED_AUTH_ALG) {
- const int num_algs = 3;
- u8 algs[num_algs];
- int i, pos;
- algs[0] = algs[1] = algs[2] = 0xff;
- if (wpa_s->mlme.auth_algs & WPA_AUTH_ALG_OPEN)
- algs[0] = WLAN_AUTH_OPEN;
- if (wpa_s->mlme.auth_algs & WPA_AUTH_ALG_SHARED)
- algs[1] = WLAN_AUTH_SHARED_KEY;
- if (wpa_s->mlme.auth_algs & WPA_AUTH_ALG_LEAP)
- algs[2] = WLAN_AUTH_LEAP;
- if (wpa_s->mlme.auth_alg == WLAN_AUTH_OPEN)
- pos = 0;
- else if (wpa_s->mlme.auth_alg == WLAN_AUTH_SHARED_KEY)
- pos = 1;
- else
- pos = 2;
- for (i = 0; i < num_algs; i++) {
- pos++;
- if (pos >= num_algs)
- pos = 0;
- if (algs[pos] == wpa_s->mlme.auth_alg ||
- algs[pos] == 0xff)
- continue;
- if (algs[pos] == WLAN_AUTH_SHARED_KEY &&
- !ieee80211_sta_wep_configured(wpa_s))
- continue;
- wpa_s->mlme.auth_alg = algs[pos];
- wpa_printf(MSG_DEBUG, "MLME: set auth_alg=%d "
- "for next try",
- wpa_s->mlme.auth_alg);
- break;
- }
- }
- return;
- }
-
- switch (wpa_s->mlme.auth_alg) {
- case WLAN_AUTH_OPEN:
- case WLAN_AUTH_LEAP:
- ieee80211_auth_completed(wpa_s);
- break;
- case WLAN_AUTH_SHARED_KEY:
- if (wpa_s->mlme.auth_transaction == 4)
- ieee80211_auth_completed(wpa_s);
- else
- ieee80211_auth_challenge(wpa_s, mgmt, len,
- rx_status);
- break;
-#ifdef CONFIG_IEEE80211R
- case WLAN_AUTH_FT:
- {
- union wpa_event_data data;
- struct wpabuf *ric = NULL;
- os_memset(&data, 0, sizeof(data));
- data.ft_ies.ies = mgmt->u.auth.variable;
- data.ft_ies.ies_len = len -
- (mgmt->u.auth.variable - (u8 *) mgmt);
- os_memcpy(data.ft_ies.target_ap, wpa_s->bssid, ETH_ALEN);
- if (os_strcmp(wpa_s->driver->name, "test") == 0 &&
- wpa_s->mlme.wmm_enabled) {
- ric = wpabuf_alloc(200);
- if (ric) {
- /* Build simple RIC-Request: RDIE | TSPEC */
-
- /* RIC Data (RDIE) */
- wpabuf_put_u8(ric, WLAN_EID_RIC_DATA);
- wpabuf_put_u8(ric, 4);
- wpabuf_put_u8(ric, 0); /* RDIE Identifier */
- wpabuf_put_u8(ric, 1); /* Resource Descriptor
- * Count */
- wpabuf_put_le16(ric, 0); /* Status Code */
-
- /* WMM TSPEC */
- ieee80211_build_tspec(ric);
-
- data.ft_ies.ric_ies = wpabuf_head(ric);
- data.ft_ies.ric_ies_len = wpabuf_len(ric);
- }
- }
-
- wpa_supplicant_event(wpa_s, EVENT_FT_RESPONSE, &data);
- wpabuf_free(ric);
- ieee80211_auth_completed(wpa_s);
- break;
- }
-#endif /* CONFIG_IEEE80211R */
- }
-}
-
-
-static void ieee80211_rx_mgmt_deauth(struct wpa_supplicant *wpa_s,
- struct ieee80211_mgmt *mgmt,
- size_t len,
- struct ieee80211_rx_status *rx_status)
-{
- u16 reason_code;
-
- if (len < 24 + 2) {
- wpa_printf(MSG_DEBUG, "MLME: too short (%lu) deauthentication "
- "frame received from " MACSTR " - ignored",
- (unsigned long) len, MAC2STR(mgmt->sa));
- return;
- }
-
- if (os_memcmp(wpa_s->bssid, mgmt->sa, ETH_ALEN) != 0) {
- wpa_printf(MSG_DEBUG, "MLME: deauthentication frame received "
- "from unknown AP (SA=" MACSTR " BSSID=" MACSTR
- ") - ignored",
- MAC2STR(mgmt->sa), MAC2STR(mgmt->bssid));
- return;
- }
-
- reason_code = le_to_host16(mgmt->u.deauth.reason_code);
-
- wpa_printf(MSG_DEBUG, "MLME: RX deauthentication from " MACSTR
- " (reason=%d)", MAC2STR(mgmt->sa), reason_code);
-
- if (wpa_s->mlme.authenticated)
- wpa_printf(MSG_DEBUG, "MLME: deauthenticated");
-
- if (wpa_s->mlme.state == IEEE80211_AUTHENTICATE ||
- wpa_s->mlme.state == IEEE80211_ASSOCIATE ||
- wpa_s->mlme.state == IEEE80211_ASSOCIATED) {
- wpa_s->mlme.state = IEEE80211_AUTHENTICATE;
- ieee80211_reschedule_timer(wpa_s,
- IEEE80211_RETRY_AUTH_INTERVAL);
- }
-
- ieee80211_set_associated(wpa_s, 0);
- wpa_s->mlme.authenticated = 0;
-}
-
-
-static void ieee80211_rx_mgmt_disassoc(struct wpa_supplicant *wpa_s,
- struct ieee80211_mgmt *mgmt,
- size_t len,
- struct ieee80211_rx_status *rx_status)
-{
- u16 reason_code;
-
- if (len < 24 + 2) {
- wpa_printf(MSG_DEBUG, "MLME: too short (%lu) disassociation "
- "frame received from " MACSTR " - ignored",
- (unsigned long) len, MAC2STR(mgmt->sa));
- return;
- }
-
- if (os_memcmp(wpa_s->bssid, mgmt->sa, ETH_ALEN) != 0) {
- wpa_printf(MSG_DEBUG, "MLME: disassociation frame received "
- "from unknown AP (SA=" MACSTR " BSSID=" MACSTR
- ") - ignored",
- MAC2STR(mgmt->sa), MAC2STR(mgmt->bssid));
- return;
- }
-
- reason_code = le_to_host16(mgmt->u.disassoc.reason_code);
-
- wpa_printf(MSG_DEBUG, "MLME: RX disassociation from " MACSTR
- " (reason=%d)", MAC2STR(mgmt->sa), reason_code);
-
- if (wpa_s->mlme.associated)
- wpa_printf(MSG_DEBUG, "MLME: disassociated");
-
- if (wpa_s->mlme.state == IEEE80211_ASSOCIATED) {
- wpa_s->mlme.state = IEEE80211_ASSOCIATE;
- ieee80211_reschedule_timer(wpa_s,
- IEEE80211_RETRY_AUTH_INTERVAL);
- }
-
- ieee80211_set_associated(wpa_s, 0);
-}
-
-
-static void ieee80211_build_tspec(struct wpabuf *buf)
-{
- struct wmm_tspec_element *tspec;
- int tid, up;
-
- tspec = wpabuf_put(buf, sizeof(*tspec));
- tspec->eid = WLAN_EID_VENDOR_SPECIFIC;
- tspec->length = sizeof(*tspec) - 2;
- tspec->oui[0] = 0x00;
- tspec->oui[1] = 0x50;
- tspec->oui[2] = 0xf2;
- tspec->oui_type = 2;
- tspec->oui_subtype = 2;
- tspec->version = 1;
-
- tid = 1;
- up = 6; /* Voice */
- tspec->ts_info[0] = (tid << 1) |
- (WMM_TSPEC_DIRECTION_BI_DIRECTIONAL << 5) |
- BIT(7);
- tspec->ts_info[1] = up << 3;
- tspec->nominal_msdu_size = host_to_le16(1530);
- tspec->mean_data_rate = host_to_le32(128000); /* bits per second */
- tspec->minimum_phy_rate = host_to_le32(6000000);
- tspec->surplus_bandwidth_allowance = host_to_le16(0x3000); /* 150% */
-}
-
-
-static void ieee80211_tx_addts(struct wpa_supplicant *wpa_s)
-{
- struct wpabuf *buf;
- struct ieee80211_mgmt *mgmt;
- size_t alen;
-
- wpa_printf(MSG_DEBUG, "MLME: Send ADDTS Request for Voice TSPEC");
- mgmt = NULL;
- alen = mgmt->u.action.u.wmm_action.variable - (u8 *) mgmt;
-
- buf = wpabuf_alloc(alen + sizeof(struct wmm_tspec_element));
- if (buf == NULL)
- return;
-
- mgmt = wpabuf_put(buf, alen);
- os_memcpy(mgmt->da, wpa_s->bssid, ETH_ALEN);
- os_memcpy(mgmt->sa, wpa_s->own_addr, ETH_ALEN);
- os_memcpy(mgmt->bssid, wpa_s->bssid, ETH_ALEN);
- mgmt->frame_control = IEEE80211_FC(WLAN_FC_TYPE_MGMT,
- WLAN_FC_STYPE_ACTION);
- mgmt->u.action.category = WLAN_ACTION_WMM;
- mgmt->u.action.u.wmm_action.action_code = WMM_ACTION_CODE_ADDTS_REQ;
- mgmt->u.action.u.wmm_action.dialog_token = 1;
- mgmt->u.action.u.wmm_action.status_code = 0;
-
- ieee80211_build_tspec(buf);
-
- ieee80211_sta_tx(wpa_s, wpabuf_head(buf), wpabuf_len(buf));
- wpabuf_free(buf);
-}
-
-
-static void ieee80211_rx_mgmt_assoc_resp(struct wpa_supplicant *wpa_s,
- struct ieee80211_mgmt *mgmt,
- size_t len,
- struct ieee80211_rx_status *rx_status,
- int reassoc)
-{
- u8 rates[32];
- size_t rates_len;
- u16 capab_info, status_code, aid;
- struct ieee802_11_elems elems;
- u8 *pos;
-
- /* AssocResp and ReassocResp have identical structure, so process both
- * of them in this function. */
-
- if (wpa_s->mlme.state != IEEE80211_ASSOCIATE) {
- wpa_printf(MSG_DEBUG, "MLME: association frame received from "
- MACSTR ", but not in associate state - ignored",
- MAC2STR(mgmt->sa));
- return;
- }
-
- if (len < 24 + 6) {
- wpa_printf(MSG_DEBUG, "MLME: too short (%lu) association "
- "frame received from " MACSTR " - ignored",
- (unsigned long) len, MAC2STR(mgmt->sa));
- return;
- }
-
- if (os_memcmp(wpa_s->bssid, mgmt->sa, ETH_ALEN) != 0) {
- wpa_printf(MSG_DEBUG, "MLME: association frame received from "
- "unknown AP (SA=" MACSTR " BSSID=" MACSTR ") - "
- "ignored", MAC2STR(mgmt->sa), MAC2STR(mgmt->bssid));
- return;
- }
-
- capab_info = le_to_host16(mgmt->u.assoc_resp.capab_info);
- status_code = le_to_host16(mgmt->u.assoc_resp.status_code);
- aid = le_to_host16(mgmt->u.assoc_resp.aid);
- if ((aid & (BIT(15) | BIT(14))) != (BIT(15) | BIT(14)))
- wpa_printf(MSG_DEBUG, "MLME: invalid aid value %d; bits 15:14 "
- "not set", aid);
- aid &= ~(BIT(15) | BIT(14));
-
- wpa_printf(MSG_DEBUG, "MLME: RX %sssocResp from " MACSTR
- " (capab=0x%x status=%d aid=%d)",
- reassoc ? "Rea" : "A", MAC2STR(mgmt->sa),
- capab_info, status_code, aid);
-
- pos = mgmt->u.assoc_resp.variable;
- if (ieee802_11_parse_elems(pos, len - (pos - (u8 *) mgmt), &elems, 0)
- == ParseFailed) {
- wpa_printf(MSG_DEBUG, "MLME: failed to parse AssocResp");
- return;
- }
-
- if (status_code != WLAN_STATUS_SUCCESS) {
- wpa_printf(MSG_DEBUG, "MLME: AP denied association (code=%d)",
- status_code);
-#ifdef CONFIG_IEEE80211W
- if (status_code == WLAN_STATUS_ASSOC_REJECTED_TEMPORARILY &&
- elems.timeout_int && elems.timeout_int_len == 5 &&
- elems.timeout_int[0] == WLAN_TIMEOUT_ASSOC_COMEBACK) {
- u32 tu, ms;
- tu = WPA_GET_LE32(elems.timeout_int + 1);
- ms = tu * 1024 / 1000;
- wpa_printf(MSG_DEBUG, "MLME: AP rejected association "
- "temporarily; comeback duration %u TU "
- "(%u ms)", tu, ms);
- if (ms > IEEE80211_ASSOC_TIMEOUT) {
- wpa_printf(MSG_DEBUG, "MLME: Update timer "
- "based on comeback duration");
- ieee80211_reschedule_timer(wpa_s, ms);
- }
- }
-#endif /* CONFIG_IEEE80211W */
- return;
- }
-
- if (elems.supp_rates == NULL) {
- wpa_printf(MSG_DEBUG, "MLME: no SuppRates element in "
- "AssocResp");
- return;
- }
-
- if (wpa_s->mlme.auth_alg == WLAN_AUTH_FT) {
- if (!reassoc) {
- wpa_printf(MSG_DEBUG, "MLME: AP tried to use "
- "association, not reassociation, response "
- "with FT");
- return;
- }
- if (wpa_ft_validate_reassoc_resp(
- wpa_s->wpa, pos, len - (pos - (u8 *) mgmt),
- mgmt->sa) < 0) {
- wpa_printf(MSG_DEBUG, "MLME: FT validation of Reassoc"
- "Resp failed");
- return;
- }
- } else if (wpa_sm_set_ft_params(wpa_s->wpa, pos,
- len - (pos - (u8 *) mgmt)) < 0)
- return;
-
- wpa_printf(MSG_DEBUG, "MLME: associated");
- wpa_s->mlme.aid = aid;
- wpa_s->mlme.ap_capab = capab_info;
-
- os_free(wpa_s->mlme.assocresp_ies);
- wpa_s->mlme.assocresp_ies_len = len - (pos - (u8 *) mgmt);
- wpa_s->mlme.assocresp_ies = os_malloc(wpa_s->mlme.assocresp_ies_len);
- if (wpa_s->mlme.assocresp_ies) {
- os_memcpy(wpa_s->mlme.assocresp_ies, pos,
- wpa_s->mlme.assocresp_ies_len);
- }
-
- ieee80211_set_associated(wpa_s, 1);
-
- rates_len = elems.supp_rates_len;
- if (rates_len > sizeof(rates))
- rates_len = sizeof(rates);
- os_memcpy(rates, elems.supp_rates, rates_len);
- if (elems.ext_supp_rates) {
- size_t _len = elems.ext_supp_rates_len;
- if (_len > sizeof(rates) - rates_len)
- _len = sizeof(rates) - rates_len;
- os_memcpy(rates + rates_len, elems.ext_supp_rates, _len);
- rates_len += _len;
- }
-
- if (wpa_drv_set_bssid(wpa_s, wpa_s->bssid) < 0) {
- wpa_printf(MSG_DEBUG, "MLME: failed to set BSSID for the "
- "netstack");
- }
- if (wpa_drv_set_ssid(wpa_s, wpa_s->mlme.ssid, wpa_s->mlme.ssid_len) <
- 0) {
- wpa_printf(MSG_DEBUG, "MLME: failed to set SSID for the "
- "netstack");
- }
-
- /* Remove STA entry before adding a new one just in case to avoid
- * problems with existing configuration (e.g., keys). */
- wpa_drv_mlme_remove_sta(wpa_s, wpa_s->bssid);
- if (wpa_drv_mlme_add_sta(wpa_s, wpa_s->bssid, rates, rates_len) < 0) {
- wpa_printf(MSG_DEBUG, "MLME: failed to add STA entry to the "
- "netstack");
- }
-
- if (elems.wmm && wpa_s->mlme.wmm_enabled)
- ieee80211_sta_wmm_params(wpa_s, elems.wmm, elems.wmm_len);
-
- ieee80211_associated(wpa_s);
-
- if (wpa_s->mlme.auth_alg != WLAN_AUTH_FT &&
- os_strcmp(wpa_s->driver->name, "test") == 0 &&
- elems.wmm && wpa_s->mlme.wmm_enabled) {
- /* Test WMM-AC - send ADDTS for WMM TSPEC */
- ieee80211_tx_addts(wpa_s);
- }
-}
-
-
-/* Caller must hold local->sta_bss_lock */
-static void __ieee80211_bss_hash_add(struct wpa_supplicant *wpa_s,
- struct ieee80211_sta_bss *bss)
-{
- bss->hnext = wpa_s->mlme.sta_bss_hash[STA_HASH(bss->bssid)];
- wpa_s->mlme.sta_bss_hash[STA_HASH(bss->bssid)] = bss;
-}
-
-
-/* Caller must hold local->sta_bss_lock */
-static void __ieee80211_bss_hash_del(struct wpa_supplicant *wpa_s,
- struct ieee80211_sta_bss *bss)
-{
- struct ieee80211_sta_bss *b, *prev = NULL;
- b = wpa_s->mlme.sta_bss_hash[STA_HASH(bss->bssid)];
- while (b) {
- if (b == bss) {
- if (prev == NULL) {
- wpa_s->mlme.sta_bss_hash[STA_HASH(bss->bssid)]
- = bss->hnext;
- } else {
- prev->hnext = bss->hnext;
- }
- break;
- }
- prev = b;
- b = b->hnext;
- }
-}
-
-
-static struct ieee80211_sta_bss *
-ieee80211_bss_add(struct wpa_supplicant *wpa_s, const u8 *bssid)
-{
- struct ieee80211_sta_bss *bss;
-
- bss = os_zalloc(sizeof(*bss));
- if (bss == NULL)
- return NULL;
- os_memcpy(bss->bssid, bssid, ETH_ALEN);
-
- /* TODO: order by RSSI? */
- bss->next = wpa_s->mlme.sta_bss_list;
- wpa_s->mlme.sta_bss_list = bss;
- __ieee80211_bss_hash_add(wpa_s, bss);
- return bss;
-}
-
-
-static struct ieee80211_sta_bss *
-ieee80211_bss_get(struct wpa_supplicant *wpa_s, const u8 *bssid)
-{
- struct ieee80211_sta_bss *bss;
-
- bss = wpa_s->mlme.sta_bss_hash[STA_HASH(bssid)];
- while (bss) {
- if (os_memcmp(bss->bssid, bssid, ETH_ALEN) == 0)
- break;
- bss = bss->hnext;
- }
- return bss;
-}
-
-
-static void ieee80211_bss_free(struct wpa_supplicant *wpa_s,
- struct ieee80211_sta_bss *bss)
-{
- __ieee80211_bss_hash_del(wpa_s, bss);
- os_free(bss->ie);
- os_free(bss->wpa_ie);
- os_free(bss->rsn_ie);
- os_free(bss->wmm_ie);
- os_free(bss->mdie);
- os_free(bss);
-}
-
-
-static void ieee80211_bss_list_deinit(struct wpa_supplicant *wpa_s)
-{
- struct ieee80211_sta_bss *bss, *prev;
-
- bss = wpa_s->mlme.sta_bss_list;
- wpa_s->mlme.sta_bss_list = NULL;
- while (bss) {
- prev = bss;
- bss = bss->next;
- ieee80211_bss_free(wpa_s, prev);
- }
-}
-
-
-static void ieee80211_bss_info(struct wpa_supplicant *wpa_s,
- struct ieee80211_mgmt *mgmt,
- size_t len,
- struct ieee80211_rx_status *rx_status,
- int beacon)
-{
- struct ieee802_11_elems elems;
- size_t baselen;
- int channel, invalid = 0, clen;
- struct ieee80211_sta_bss *bss;
- u64 timestamp;
- u8 *pos, *ie_pos;
- size_t ie_len;
-
- if (!beacon && os_memcmp(mgmt->da, wpa_s->own_addr, ETH_ALEN))
- return; /* ignore ProbeResp to foreign address */
-
-#if 0
- wpa_printf(MSG_MSGDUMP, "MLME: RX %s from " MACSTR " to " MACSTR,
- beacon ? "Beacon" : "Probe Response",
- MAC2STR(mgmt->sa), MAC2STR(mgmt->da));
-#endif
-
- baselen = (u8 *) mgmt->u.beacon.variable - (u8 *) mgmt;
- if (baselen > len)
- return;
-
- pos = mgmt->u.beacon.timestamp;
- timestamp = WPA_GET_LE64(pos);
-
-#if 0 /* FIX */
- if (local->conf.mode == IW_MODE_ADHOC && beacon &&
- os_memcmp(mgmt->bssid, local->bssid, ETH_ALEN) == 0) {
-#ifdef IEEE80211_IBSS_DEBUG
- static unsigned long last_tsf_debug = 0;
- u64 tsf;
- if (local->hw->get_tsf)
- tsf = local->hw->get_tsf(local->mdev);
- else
- tsf = -1LLU;
- if (time_after(jiffies, last_tsf_debug + 5 * HZ)) {
- wpa_printf(MSG_DEBUG, "RX beacon SA=" MACSTR " BSSID="
- MACSTR " TSF=0x%llx BCN=0x%llx diff=%lld "
- "@%ld",
- MAC2STR(mgmt->sa), MAC2STR(mgmt->bssid),
- tsf, timestamp, tsf - timestamp, jiffies);
- last_tsf_debug = jiffies;
- }
-#endif /* IEEE80211_IBSS_DEBUG */
- }
-#endif
-
- ie_pos = mgmt->u.beacon.variable;
- ie_len = len - baselen;
- if (ieee802_11_parse_elems(ie_pos, ie_len, &elems, 0) == ParseFailed)
- invalid = 1;
-
-#if 0 /* FIX */
- if (local->conf.mode == IW_MODE_ADHOC && elems.supp_rates &&
- os_memcmp(mgmt->bssid, local->bssid, ETH_ALEN) == 0 &&
- (sta = sta_info_get(local, mgmt->sa))) {
- struct ieee80211_rate *rates;
- size_t num_rates;
- u32 supp_rates, prev_rates;
- int i, j, oper_mode;
-
- rates = local->curr_rates;
- num_rates = local->num_curr_rates;
- oper_mode = wpa_s->mlme.sta_scanning ?
- local->scan_oper_phymode : local->conf.phymode;
- for (i = 0; i < local->hw->num_modes; i++) {
- struct ieee80211_hw_modes *mode = &local->hw->modes[i];
- if (oper_mode == mode->mode) {
- rates = mode->rates;
- num_rates = mode->num_rates;
- break;
- }
- }
-
- supp_rates = 0;
- for (i = 0; i < elems.supp_rates_len +
- elems.ext_supp_rates_len; i++) {
- u8 rate = 0;
- int own_rate;
- if (i < elems.supp_rates_len)
- rate = elems.supp_rates[i];
- else if (elems.ext_supp_rates)
- rate = elems.ext_supp_rates
- [i - elems.supp_rates_len];
- own_rate = 5 * (rate & 0x7f);
- if (oper_mode == MODE_ATHEROS_TURBO)
- own_rate *= 2;
- for (j = 0; j < num_rates; j++)
- if (rates[j].rate == own_rate)
- supp_rates |= BIT(j);
- }
-
- prev_rates = sta->supp_rates;
- sta->supp_rates &= supp_rates;
- if (sta->supp_rates == 0) {
- /* No matching rates - this should not really happen.
- * Make sure that at least one rate is marked
- * supported to avoid issues with TX rate ctrl. */
- sta->supp_rates = wpa_s->mlme.supp_rates_bits;
- }
- if (sta->supp_rates != prev_rates) {
- wpa_printf(MSG_DEBUG, "MLME: updated supp_rates set "
- "for " MACSTR " based on beacon info "
- "(0x%x & 0x%x -> 0x%x)",
- MAC2STR(sta->addr), prev_rates,
- supp_rates, sta->supp_rates);
- }
- sta_info_release(local, sta);
- }
-#endif
-
- if (elems.ssid == NULL)
- return;
-
- if (elems.ds_params && elems.ds_params_len == 1)
- channel = elems.ds_params[0];
- else
- channel = rx_status->channel;
-
- bss = ieee80211_bss_get(wpa_s, mgmt->bssid);
- if (bss == NULL) {
- bss = ieee80211_bss_add(wpa_s, mgmt->bssid);
- if (bss == NULL)
- return;
- } else {
-#if 0
- /* TODO: order by RSSI? */
- spin_lock_bh(&local->sta_bss_lock);
- list_move_tail(&bss->list, &local->sta_bss_list);
- spin_unlock_bh(&local->sta_bss_lock);
-#endif
- }
-
- if (bss->probe_resp && beacon) {
- /* Do not allow beacon to override data from Probe Response. */
- return;
- }
-
- bss->beacon_int = le_to_host16(mgmt->u.beacon.beacon_int);
- bss->capability = le_to_host16(mgmt->u.beacon.capab_info);
-
- if (bss->ie == NULL || bss->ie_len < ie_len) {
- os_free(bss->ie);
- bss->ie = os_malloc(ie_len);
- }
- if (bss->ie) {
- os_memcpy(bss->ie, ie_pos, ie_len);
- bss->ie_len = ie_len;
- }
-
- if (elems.ssid && elems.ssid_len <= MAX_SSID_LEN) {
- os_memcpy(bss->ssid, elems.ssid, elems.ssid_len);
- bss->ssid_len = elems.ssid_len;
- }
-
- bss->supp_rates_len = 0;
- if (elems.supp_rates) {
- clen = IEEE80211_MAX_SUPP_RATES - bss->supp_rates_len;
- if (clen > elems.supp_rates_len)
- clen = elems.supp_rates_len;
- os_memcpy(&bss->supp_rates[bss->supp_rates_len],
- elems.supp_rates, clen);
- bss->supp_rates_len += clen;
- }
- if (elems.ext_supp_rates) {
- clen = IEEE80211_MAX_SUPP_RATES - bss->supp_rates_len;
- if (clen > elems.ext_supp_rates_len)
- clen = elems.ext_supp_rates_len;
- os_memcpy(&bss->supp_rates[bss->supp_rates_len],
- elems.ext_supp_rates, clen);
- bss->supp_rates_len += clen;
- }
-
- if (elems.wpa_ie &&
- (bss->wpa_ie == NULL || bss->wpa_ie_len != elems.wpa_ie_len ||
- os_memcmp(bss->wpa_ie, elems.wpa_ie, elems.wpa_ie_len))) {
- os_free(bss->wpa_ie);
- bss->wpa_ie = os_malloc(elems.wpa_ie_len + 2);
- if (bss->wpa_ie) {
- os_memcpy(bss->wpa_ie, elems.wpa_ie - 2,
- elems.wpa_ie_len + 2);
- bss->wpa_ie_len = elems.wpa_ie_len + 2;
- } else
- bss->wpa_ie_len = 0;
- } else if (!elems.wpa_ie && bss->wpa_ie) {
- os_free(bss->wpa_ie);
- bss->wpa_ie = NULL;
- bss->wpa_ie_len = 0;
- }
-
- if (elems.rsn_ie &&
- (bss->rsn_ie == NULL || bss->rsn_ie_len != elems.rsn_ie_len ||
- os_memcmp(bss->rsn_ie, elems.rsn_ie, elems.rsn_ie_len))) {
- os_free(bss->rsn_ie);
- bss->rsn_ie = os_malloc(elems.rsn_ie_len + 2);
- if (bss->rsn_ie) {
- os_memcpy(bss->rsn_ie, elems.rsn_ie - 2,
- elems.rsn_ie_len + 2);
- bss->rsn_ie_len = elems.rsn_ie_len + 2;
- } else
- bss->rsn_ie_len = 0;
- } else if (!elems.rsn_ie && bss->rsn_ie) {
- os_free(bss->rsn_ie);
- bss->rsn_ie = NULL;
- bss->rsn_ie_len = 0;
- }
-
- if (elems.wmm &&
- (bss->wmm_ie == NULL || bss->wmm_ie_len != elems.wmm_len ||
- os_memcmp(bss->wmm_ie, elems.wmm, elems.wmm_len))) {
- os_free(bss->wmm_ie);
- bss->wmm_ie = os_malloc(elems.wmm_len + 2);
- if (bss->wmm_ie) {
- os_memcpy(bss->wmm_ie, elems.wmm - 2,
- elems.wmm_len + 2);
- bss->wmm_ie_len = elems.wmm_len + 2;
- } else
- bss->wmm_ie_len = 0;
- } else if (!elems.wmm && bss->wmm_ie) {
- os_free(bss->wmm_ie);
- bss->wmm_ie = NULL;
- bss->wmm_ie_len = 0;
- }
-
-#ifdef CONFIG_IEEE80211R
- if (elems.mdie &&
- (bss->mdie == NULL || bss->mdie_len != elems.mdie_len ||
- os_memcmp(bss->mdie, elems.mdie, elems.mdie_len))) {
- os_free(bss->mdie);
- bss->mdie = os_malloc(elems.mdie_len + 2);
- if (bss->mdie) {
- os_memcpy(bss->mdie, elems.mdie - 2,
- elems.mdie_len + 2);
- bss->mdie_len = elems.mdie_len + 2;
- } else
- bss->mdie_len = 0;
- } else if (!elems.mdie && bss->mdie) {
- os_free(bss->mdie);
- bss->mdie = NULL;
- bss->mdie_len = 0;
- }
-#endif /* CONFIG_IEEE80211R */
-
- bss->hw_mode = wpa_s->mlme.phymode;
- bss->channel = channel;
- bss->freq = wpa_s->mlme.freq;
- if (channel != wpa_s->mlme.channel &&
- (wpa_s->mlme.phymode == HOSTAPD_MODE_IEEE80211G ||
- wpa_s->mlme.phymode == HOSTAPD_MODE_IEEE80211B) &&
- channel >= 1 && channel <= 14) {
- static const int freq_list[] = {
- 2412, 2417, 2422, 2427, 2432, 2437, 2442,
- 2447, 2452, 2457, 2462, 2467, 2472, 2484
- };
- /* IEEE 802.11g/b mode can receive packets from neighboring
- * channels, so map the channel into frequency. */
- bss->freq = freq_list[channel - 1];
- }
- bss->timestamp = timestamp;
- os_get_time(&bss->last_update);
- bss->rssi = rx_status->ssi;
- if (!beacon)
- bss->probe_resp++;
-}
-
-
-static void ieee80211_rx_mgmt_probe_resp(struct wpa_supplicant *wpa_s,
- struct ieee80211_mgmt *mgmt,
- size_t len,
- struct ieee80211_rx_status *rx_status)
-{
- ieee80211_bss_info(wpa_s, mgmt, len, rx_status, 0);
-}
-
-
-static void ieee80211_rx_mgmt_beacon(struct wpa_supplicant *wpa_s,
- struct ieee80211_mgmt *mgmt,
- size_t len,
- struct ieee80211_rx_status *rx_status)
-{
- int use_protection;
- size_t baselen;
- struct ieee802_11_elems elems;
-
- ieee80211_bss_info(wpa_s, mgmt, len, rx_status, 1);
-
- if (!wpa_s->mlme.associated ||
- os_memcmp(wpa_s->bssid, mgmt->bssid, ETH_ALEN) != 0)
- return;
-
- /* Process beacon from the current BSS */
- baselen = (u8 *) mgmt->u.beacon.variable - (u8 *) mgmt;
- if (baselen > len)
- return;
-
- if (ieee802_11_parse_elems(mgmt->u.beacon.variable, len - baselen,
- &elems, 0) == ParseFailed)
- return;
-
- use_protection = 0;
- if (elems.erp_info && elems.erp_info_len >= 1) {
- use_protection =
- (elems.erp_info[0] & ERP_INFO_USE_PROTECTION) != 0;
- }
-
- if (use_protection != !!wpa_s->mlme.use_protection) {
- wpa_printf(MSG_DEBUG, "MLME: CTS protection %s (BSSID=" MACSTR
- ")",
- use_protection ? "enabled" : "disabled",
- MAC2STR(wpa_s->bssid));
- wpa_s->mlme.use_protection = use_protection ? 1 : 0;
- wpa_s->mlme.cts_protect_erp_frames = use_protection;
- }
-
- if (elems.wmm && wpa_s->mlme.wmm_enabled) {
- ieee80211_sta_wmm_params(wpa_s, elems.wmm,
- elems.wmm_len);
- }
-}
-
-
-static void ieee80211_rx_mgmt_probe_req(struct wpa_supplicant *wpa_s,
- struct ieee80211_mgmt *mgmt,
- size_t len,
- struct ieee80211_rx_status *rx_status)
-{
- int tx_last_beacon, adhoc;
-#if 0 /* FIX */
- struct ieee80211_mgmt *resp;
-#endif
- u8 *pos, *end;
- struct wpa_ssid *ssid = wpa_s->current_ssid;
-
- adhoc = ssid && ssid->mode == WPAS_MODE_IBSS;
-
- if (!adhoc || wpa_s->mlme.state != IEEE80211_IBSS_JOINED ||
- len < 24 + 2 || wpa_s->mlme.probe_resp == NULL)
- return;
-
-#if 0 /* FIX */
- if (local->hw->tx_last_beacon)
- tx_last_beacon = local->hw->tx_last_beacon(local->mdev);
- else
-#endif
- tx_last_beacon = 1;
-
-#ifdef IEEE80211_IBSS_DEBUG
- wpa_printf(MSG_DEBUG, "MLME: RX ProbeReq SA=" MACSTR " DA=" MACSTR
- " BSSID=" MACSTR " (tx_last_beacon=%d)",
- MAC2STR(mgmt->sa), MAC2STR(mgmt->da),
- MAC2STR(mgmt->bssid), tx_last_beacon);
-#endif /* IEEE80211_IBSS_DEBUG */
-
- if (!tx_last_beacon)
- return;
-
- if (os_memcmp(mgmt->bssid, wpa_s->bssid, ETH_ALEN) != 0 &&
- os_memcmp(mgmt->bssid, "\xff\xff\xff\xff\xff\xff", ETH_ALEN) != 0)
- return;
-
- end = ((u8 *) mgmt) + len;
- pos = mgmt->u.probe_req.variable;
- if (pos[0] != WLAN_EID_SSID ||
- pos + 2 + pos[1] > end) {
- wpa_printf(MSG_DEBUG, "MLME: Invalid SSID IE in ProbeReq from "
- MACSTR, MAC2STR(mgmt->sa));
- return;
- }
- if (pos[1] != 0 &&
- (pos[1] != wpa_s->mlme.ssid_len ||
- os_memcmp(pos + 2, wpa_s->mlme.ssid, wpa_s->mlme.ssid_len) != 0))
- {
- /* Ignore ProbeReq for foreign SSID */
- return;
- }
-
-#if 0 /* FIX */
- /* Reply with ProbeResp */
- skb = skb_copy(wpa_s->mlme.probe_resp, GFP_ATOMIC);
- if (skb == NULL)
- return;
-
- resp = (struct ieee80211_mgmt *) skb->data;
- os_memcpy(resp->da, mgmt->sa, ETH_ALEN);
-#ifdef IEEE80211_IBSS_DEBUG
- wpa_printf(MSG_DEBUG, "MLME: Sending ProbeResp to " MACSTR,
- MAC2STR(resp->da));
-#endif /* IEEE80211_IBSS_DEBUG */
- ieee80211_sta_tx(wpa_s, skb, 0, 1);
-#endif
-}
-
-
-#ifdef CONFIG_IEEE80211R
-static void ieee80211_rx_mgmt_ft_action(struct wpa_supplicant *wpa_s,
- struct ieee80211_mgmt *mgmt,
- size_t len,
- struct ieee80211_rx_status *rx_status)
-{
- union wpa_event_data data;
- u16 status;
- u8 *sta_addr, *target_ap_addr;
-
- if (len < 24 + 1 + sizeof(mgmt->u.action.u.ft_action_resp)) {
- wpa_printf(MSG_DEBUG, "MLME: Too short FT Action frame");
- return;
- }
-
- /*
- * Only FT Action Response is needed for now since reservation
- * protocol is not supported.
- */
- if (mgmt->u.action.u.ft_action_resp.action != 2) {
- wpa_printf(MSG_DEBUG, "MLME: Unexpected FT Action %d",
- mgmt->u.action.u.ft_action_resp.action);
- return;
- }
-
- status = le_to_host16(mgmt->u.action.u.ft_action_resp.status_code);
- sta_addr = mgmt->u.action.u.ft_action_resp.sta_addr;
- target_ap_addr = mgmt->u.action.u.ft_action_resp.target_ap_addr;
- wpa_printf(MSG_DEBUG, "MLME: Received FT Action Response: STA " MACSTR
- " TargetAP " MACSTR " Status Code %d",
- MAC2STR(sta_addr), MAC2STR(target_ap_addr), status);
- if (os_memcmp(sta_addr, wpa_s->own_addr, ETH_ALEN) != 0) {
- wpa_printf(MSG_DEBUG, "MLME: Foreign STA Address " MACSTR
- " in FT Action Response", MAC2STR(sta_addr));
- return;
- }
-
- if (status) {
- wpa_printf(MSG_DEBUG, "MLME: FT Action Response indicates "
- "failure (status code %d)", status);
- /* TODO: report error to FT code(?) */
- return;
- }
-
- os_memset(&data, 0, sizeof(data));
- data.ft_ies.ies = mgmt->u.action.u.ft_action_resp.variable;
- data.ft_ies.ies_len = len - (mgmt->u.action.u.ft_action_resp.variable -
- (u8 *) mgmt);
- data.ft_ies.ft_action = 1;
- os_memcpy(data.ft_ies.target_ap, target_ap_addr, ETH_ALEN);
- wpa_supplicant_event(wpa_s, EVENT_FT_RESPONSE, &data);
- /* TODO: should only re-associate, if EVENT_FT_RESPONSE was processed
- * successfully */
- wpa_s->mlme.prev_bssid_set = 1;
- wpa_s->mlme.auth_alg = WLAN_AUTH_FT;
- os_memcpy(wpa_s->mlme.prev_bssid, wpa_s->bssid, ETH_ALEN);
- os_memcpy(wpa_s->bssid, target_ap_addr, ETH_ALEN);
- ieee80211_associate(wpa_s);
-}
-#endif /* CONFIG_IEEE80211R */
-
-
-#ifdef CONFIG_IEEE80211W
-
-/* MLME-SAQuery.response */
-static int ieee80211_sta_send_sa_query_resp(struct wpa_supplicant *wpa_s,
- const u8 *addr, const u8 *trans_id)
-{
- struct ieee80211_mgmt *mgmt;
- int res;
- size_t len;
-
- mgmt = os_zalloc(sizeof(*mgmt));
- if (mgmt == NULL) {
- wpa_printf(MSG_DEBUG, "MLME: Failed to allocate buffer for "
- "SA Query action frame");
- return -1;
- }
-
- len = 24;
- os_memcpy(mgmt->da, addr, ETH_ALEN);
- os_memcpy(mgmt->sa, wpa_s->own_addr, ETH_ALEN);
- os_memcpy(mgmt->bssid, wpa_s->bssid, ETH_ALEN);
- mgmt->frame_control = IEEE80211_FC(WLAN_FC_TYPE_MGMT,
- WLAN_FC_STYPE_ACTION);
- mgmt->u.action.category = WLAN_ACTION_SA_QUERY;
- mgmt->u.action.u.sa_query_resp.action = WLAN_SA_QUERY_RESPONSE;
- os_memcpy(mgmt->u.action.u.sa_query_resp.trans_id, trans_id,
- WLAN_SA_QUERY_TR_ID_LEN);
- len += 1 + sizeof(mgmt->u.action.u.sa_query_resp);
-
- res = ieee80211_sta_tx(wpa_s, (u8 *) mgmt, len);
- os_free(mgmt);
-
- return res;
-}
-
-
-static void ieee80211_rx_mgmt_sa_query_action(
- struct wpa_supplicant *wpa_s, struct ieee80211_mgmt *mgmt, size_t len,
- struct ieee80211_rx_status *rx_status)
-{
- if (len < 24 + 1 + sizeof(mgmt->u.action.u.sa_query_req)) {
- wpa_printf(MSG_DEBUG, "MLME: Too short SA Query Action frame");
- return;
- }
-
- if (mgmt->u.action.u.sa_query_req.action != WLAN_SA_QUERY_REQUEST) {
- wpa_printf(MSG_DEBUG, "MLME: Unexpected SA Query Action %d",
- mgmt->u.action.u.sa_query_req.action);
- return;
- }
-
- if (os_memcmp(mgmt->sa, wpa_s->bssid, ETH_ALEN) != 0) {
- wpa_printf(MSG_DEBUG, "MLME: Ignore SA Query from unknown "
- "source " MACSTR, MAC2STR(mgmt->sa));
- return;
- }
-
- if (wpa_s->mlme.state == IEEE80211_ASSOCIATE) {
- wpa_printf(MSG_DEBUG, "MLME: Ignore SA query request during "
- "association process");
- return;
- }
-
- wpa_printf(MSG_DEBUG, "MLME: Replying to SA Query request");
- ieee80211_sta_send_sa_query_resp(wpa_s, mgmt->sa, mgmt->u.action.u.
- sa_query_req.trans_id);
-}
-
-#endif /* CONFIG_IEEE80211W */
-
-
-static void dump_tspec(struct wmm_tspec_element *tspec)
-{
- int up, psb, dir, tid;
- u16 val;
-
- up = (tspec->ts_info[1] >> 3) & 0x07;
- psb = (tspec->ts_info[1] >> 2) & 0x01;
- dir = (tspec->ts_info[0] >> 5) & 0x03;
- tid = (tspec->ts_info[0] >> 1) & 0x0f;
- wpa_printf(MSG_DEBUG, "WMM: TS Info: UP=%d PSB=%d Direction=%d TID=%d",
- up, psb, dir, tid);
- val = le_to_host16(tspec->nominal_msdu_size);
- wpa_printf(MSG_DEBUG, "WMM: Nominal MSDU Size: %d%s",
- val & 0x7fff, val & 0x8000 ? " (fixed)" : "");
- wpa_printf(MSG_DEBUG, "WMM: Mean Data Rate: %u bps",
- le_to_host32(tspec->mean_data_rate));
- wpa_printf(MSG_DEBUG, "WMM: Minimum PHY Rate: %u bps",
- le_to_host32(tspec->minimum_phy_rate));
- val = le_to_host16(tspec->surplus_bandwidth_allowance);
- wpa_printf(MSG_DEBUG, "WMM: Surplus Bandwidth Allowance: %u.%04u",
- val >> 13, 10000 * (val & 0x1fff) / 0x2000);
- val = le_to_host16(tspec->medium_time);
- wpa_printf(MSG_DEBUG, "WMM: Medium Time: %u (= %u usec/sec)",
- val, 32 * val);
-}
-
-
-static int is_wmm_tspec(const u8 *ie, size_t len)
-{
- const struct wmm_tspec_element *tspec;
-
- if (len < sizeof(*tspec))
- return 0;
-
- tspec = (const struct wmm_tspec_element *) ie;
- if (tspec->eid != WLAN_EID_VENDOR_SPECIFIC ||
- tspec->length < sizeof(*tspec) - 2 ||
- tspec->oui[0] != 0x00 || tspec->oui[1] != 0x50 ||
- tspec->oui[2] != 0xf2 || tspec->oui_type != 2 ||
- tspec->oui_subtype != 2 || tspec->version != 1)
- return 0;
-
- return 1;
-}
-
-
-static void ieee80211_rx_addts_resp(
- struct wpa_supplicant *wpa_s, struct ieee80211_mgmt *mgmt, size_t len,
- size_t var_len)
-{
- struct wmm_tspec_element *tspec;
-
- wpa_printf(MSG_DEBUG, "WMM: Received ADDTS Response");
- wpa_hexdump(MSG_MSGDUMP, "WMM: ADDTS Response IE(s)",
- mgmt->u.action.u.wmm_action.variable, var_len);
- if (!is_wmm_tspec(mgmt->u.action.u.wmm_action.variable, var_len))
- return;
- tspec = (struct wmm_tspec_element *)
- mgmt->u.action.u.wmm_action.variable;
- dump_tspec(tspec);
-}
-
-
-static void ieee80211_rx_delts(
- struct wpa_supplicant *wpa_s, struct ieee80211_mgmt *mgmt, size_t len,
- size_t var_len)
-{
- struct wmm_tspec_element *tspec;
-
- wpa_printf(MSG_DEBUG, "WMM: Received DELTS");
- wpa_hexdump(MSG_MSGDUMP, "WMM: DELTS IE(s)",
- mgmt->u.action.u.wmm_action.variable, var_len);
- if (!is_wmm_tspec(mgmt->u.action.u.wmm_action.variable, var_len))
- return;
- tspec = (struct wmm_tspec_element *)
- mgmt->u.action.u.wmm_action.variable;
- dump_tspec(tspec);
-}
-
-
-static void ieee80211_rx_mgmt_wmm_action(
- struct wpa_supplicant *wpa_s, struct ieee80211_mgmt *mgmt, size_t len,
- struct ieee80211_rx_status *rx_status)
-{
- size_t alen;
-
- alen = mgmt->u.action.u.wmm_action.variable - (u8 *) mgmt;
- if (len < alen) {
- wpa_printf(MSG_DEBUG, "WMM: Received Action frame too short");
- return;
- }
-
- wpa_printf(MSG_DEBUG, "WMM: Received Action frame: Action Code %d, "
- "Dialog Token %d, Status Code %d",
- mgmt->u.action.u.wmm_action.action_code,
- mgmt->u.action.u.wmm_action.dialog_token,
- mgmt->u.action.u.wmm_action.status_code);
-
- switch (mgmt->u.action.u.wmm_action.action_code) {
- case WMM_ACTION_CODE_ADDTS_RESP:
- ieee80211_rx_addts_resp(wpa_s, mgmt, len, len - alen);
- break;
- case WMM_ACTION_CODE_DELTS:
- ieee80211_rx_delts(wpa_s, mgmt, len, len - alen);
- break;
- default:
- wpa_printf(MSG_DEBUG, "WMM: Unsupported Action Code %d",
- mgmt->u.action.u.wmm_action.action_code);
- break;
- }
-}
-
-
-static void ieee80211_rx_mgmt_action(struct wpa_supplicant *wpa_s,
- struct ieee80211_mgmt *mgmt,
- size_t len,
- struct ieee80211_rx_status *rx_status)
-{
- wpa_printf(MSG_DEBUG, "MLME: received Action frame");
-
- if (len < 25)
- return;
-
- switch (mgmt->u.action.category) {
-#ifdef CONFIG_IEEE80211R
- case WLAN_ACTION_FT:
- ieee80211_rx_mgmt_ft_action(wpa_s, mgmt, len, rx_status);
- break;
-#endif /* CONFIG_IEEE80211R */
-#ifdef CONFIG_IEEE80211W
- case WLAN_ACTION_SA_QUERY:
- ieee80211_rx_mgmt_sa_query_action(wpa_s, mgmt, len, rx_status);
- break;
-#endif /* CONFIG_IEEE80211W */
- case WLAN_ACTION_WMM:
- ieee80211_rx_mgmt_wmm_action(wpa_s, mgmt, len, rx_status);
- break;
- case WLAN_ACTION_PUBLIC:
- if (wpa_s->mlme.public_action_cb) {
- wpa_s->mlme.public_action_cb(
- wpa_s->mlme.public_action_cb_ctx,
- (u8 *) mgmt, len, rx_status->freq);
- return;
- }
- break;
- default:
- wpa_printf(MSG_DEBUG, "MLME: unknown Action Category %d",
- mgmt->u.action.category);
- break;
- }
-}
-
-
-static void ieee80211_sta_rx_mgmt(struct wpa_supplicant *wpa_s,
- const u8 *buf, size_t len,
- struct ieee80211_rx_status *rx_status)
-{
- struct ieee80211_mgmt *mgmt;
- u16 fc;
-
- if (len < 24)
- return;
-
- mgmt = (struct ieee80211_mgmt *) buf;
- fc = le_to_host16(mgmt->frame_control);
-
- switch (WLAN_FC_GET_STYPE(fc)) {
- case WLAN_FC_STYPE_PROBE_REQ:
- ieee80211_rx_mgmt_probe_req(wpa_s, mgmt, len, rx_status);
- break;
- case WLAN_FC_STYPE_PROBE_RESP:
- ieee80211_rx_mgmt_probe_resp(wpa_s, mgmt, len, rx_status);
- break;
- case WLAN_FC_STYPE_BEACON:
- ieee80211_rx_mgmt_beacon(wpa_s, mgmt, len, rx_status);
- break;
- case WLAN_FC_STYPE_AUTH:
- ieee80211_rx_mgmt_auth(wpa_s, mgmt, len, rx_status);
- break;
- case WLAN_FC_STYPE_ASSOC_RESP:
- ieee80211_rx_mgmt_assoc_resp(wpa_s, mgmt, len, rx_status, 0);
- break;
- case WLAN_FC_STYPE_REASSOC_RESP:
- ieee80211_rx_mgmt_assoc_resp(wpa_s, mgmt, len, rx_status, 1);
- break;
- case WLAN_FC_STYPE_DEAUTH:
- ieee80211_rx_mgmt_deauth(wpa_s, mgmt, len, rx_status);
- break;
- case WLAN_FC_STYPE_DISASSOC:
- ieee80211_rx_mgmt_disassoc(wpa_s, mgmt, len, rx_status);
- break;
- case WLAN_FC_STYPE_ACTION:
- ieee80211_rx_mgmt_action(wpa_s, mgmt, len, rx_status);
- break;
- default:
- wpa_printf(MSG_DEBUG, "MLME: received unknown management "
- "frame - stype=%d", WLAN_FC_GET_STYPE(fc));
- break;
- }
-}
-
-
-static void ieee80211_sta_rx_scan(struct wpa_supplicant *wpa_s,
- const u8 *buf, size_t len,
- struct ieee80211_rx_status *rx_status)
-{
- struct ieee80211_mgmt *mgmt;
- u16 fc;
-
- if (len < 24)
- return;
-
- mgmt = (struct ieee80211_mgmt *) buf;
- fc = le_to_host16(mgmt->frame_control);
-
- if (WLAN_FC_GET_TYPE(fc) == WLAN_FC_TYPE_MGMT) {
- if (WLAN_FC_GET_STYPE(fc) == WLAN_FC_STYPE_PROBE_RESP) {
- ieee80211_rx_mgmt_probe_resp(wpa_s, mgmt,
- len, rx_status);
- } else if (WLAN_FC_GET_STYPE(fc) == WLAN_FC_STYPE_BEACON) {
- ieee80211_rx_mgmt_beacon(wpa_s, mgmt, len, rx_status);
- }
- }
-}
-
-
-static int ieee80211_sta_active_ibss(struct wpa_supplicant *wpa_s)
-{
- int active = 0;
-
-#if 0 /* FIX */
- list_for_each(ptr, &local->sta_list) {
- sta = list_entry(ptr, struct sta_info, list);
- if (sta->dev == dev &&
- time_after(sta->last_rx + IEEE80211_IBSS_MERGE_INTERVAL,
- jiffies)) {
- active++;
- break;
- }
- }
-#endif
-
- return active;
-}
-
-
-static void ieee80211_sta_expire(struct wpa_supplicant *wpa_s)
-{
-#if 0 /* FIX */
- list_for_each_safe(ptr, n, &local->sta_list) {
- sta = list_entry(ptr, struct sta_info, list);
- if (time_after(jiffies, sta->last_rx +
- IEEE80211_IBSS_INACTIVITY_LIMIT)) {
- wpa_printf(MSG_DEBUG, "MLME: expiring inactive STA "
- MACSTR, MAC2STR(sta->addr));
- sta_info_free(local, sta, 1);
- }
- }
-#endif
-}
-
-
-static void ieee80211_sta_merge_ibss(struct wpa_supplicant *wpa_s)
-{
- struct wpa_driver_scan_params params;
-
- ieee80211_reschedule_timer(wpa_s, IEEE80211_IBSS_MERGE_INTERVAL);
-
- ieee80211_sta_expire(wpa_s);
- if (ieee80211_sta_active_ibss(wpa_s))
- return;
-
- wpa_printf(MSG_DEBUG, "MLME: No active IBSS STAs - trying to scan for "
- "other IBSS networks with same SSID (merge)");
- os_memset(&params, 0, sizeof(params));
- params.ssids[0].ssid = wpa_s->mlme.ssid;
- params.ssids[0].ssid_len = wpa_s->mlme.ssid_len;
- params.num_ssids = wpa_s->mlme.ssid_len ? 1 : 0;
- ieee80211_sta_req_scan(wpa_s, &params);
-}
-
-
-static void ieee80211_sta_timer(void *eloop_ctx, void *timeout_ctx)
-{
- struct wpa_supplicant *wpa_s = eloop_ctx;
-
- switch (wpa_s->mlme.state) {
- case IEEE80211_DISABLED:
- break;
- case IEEE80211_AUTHENTICATE:
- ieee80211_authenticate(wpa_s);
- break;
- case IEEE80211_ASSOCIATE:
- ieee80211_associate(wpa_s);
- break;
- case IEEE80211_ASSOCIATED:
- ieee80211_associated(wpa_s);
- break;
- case IEEE80211_IBSS_SEARCH:
- ieee80211_sta_find_ibss(wpa_s);
- break;
- case IEEE80211_IBSS_JOINED:
- ieee80211_sta_merge_ibss(wpa_s);
- break;
- default:
- wpa_printf(MSG_DEBUG, "ieee80211_sta_timer: Unknown state %d",
- wpa_s->mlme.state);
- break;
- }
-
- if (ieee80211_privacy_mismatch(wpa_s)) {
- wpa_printf(MSG_DEBUG, "MLME: privacy configuration mismatch "
- "and mixed-cell disabled - disassociate");
-
- ieee80211_send_disassoc(wpa_s, WLAN_REASON_UNSPECIFIED);
- ieee80211_set_associated(wpa_s, 0);
- }
-}
-
-
-static void ieee80211_sta_new_auth(struct wpa_supplicant *wpa_s)
-{
- struct wpa_ssid *ssid = wpa_s->current_ssid;
- if (ssid && ssid->mode != WPAS_MODE_INFRA)
- return;
-
-#if 0 /* FIX */
- if (local->hw->reset_tsf) {
- /* Reset own TSF to allow time synchronization work. */
- local->hw->reset_tsf(local->mdev);
- }
-#endif
-
- wpa_s->mlme.wmm_last_param_set = -1; /* allow any WMM update */
-
-
- if (wpa_s->mlme.auth_algs & WPA_AUTH_ALG_OPEN)
- wpa_s->mlme.auth_alg = WLAN_AUTH_OPEN;
- else if (wpa_s->mlme.auth_algs & WPA_AUTH_ALG_SHARED)
- wpa_s->mlme.auth_alg = WLAN_AUTH_SHARED_KEY;
- else if (wpa_s->mlme.auth_algs & WPA_AUTH_ALG_LEAP)
- wpa_s->mlme.auth_alg = WLAN_AUTH_LEAP;
- else
- wpa_s->mlme.auth_alg = WLAN_AUTH_OPEN;
- wpa_printf(MSG_DEBUG, "MLME: Initial auth_alg=%d",
- wpa_s->mlme.auth_alg);
- wpa_s->mlme.auth_transaction = -1;
- wpa_s->mlme.auth_tries = wpa_s->mlme.assoc_tries = 0;
- ieee80211_authenticate(wpa_s);
-}
-
-
-static int ieee80211_ibss_allowed(struct wpa_supplicant *wpa_s)
-{
-#if 0 /* FIX */
- int m, c;
-
- for (m = 0; m < local->hw->num_modes; m++) {
- struct ieee80211_hw_modes *mode = &local->hw->modes[m];
- if (mode->mode != local->conf.phymode)
- continue;
- for (c = 0; c < mode->num_channels; c++) {
- struct ieee80211_channel *chan = &mode->channels[c];
- if (chan->flag & IEEE80211_CHAN_W_SCAN &&
- chan->chan == local->conf.channel) {
- if (chan->flag & IEEE80211_CHAN_W_IBSS)
- return 1;
- break;
- }
- }
- }
-#endif
-
- return 0;
-}
-
-
-static int ieee80211_sta_join_ibss(struct wpa_supplicant *wpa_s,
- struct ieee80211_sta_bss *bss)
-{
- int res = 0, rates, done = 0, bssid_changed;
- struct ieee80211_mgmt *mgmt;
-#if 0 /* FIX */
- struct ieee80211_tx_control control;
- struct ieee80211_rate *rate;
- struct rate_control_extra extra;
-#endif
- u8 *pos, *buf;
- size_t len;
-
- /* Remove possible STA entries from other IBSS networks. */
-#if 0 /* FIX */
- sta_info_flush(local, NULL);
-
- if (local->hw->reset_tsf) {
- /* Reset own TSF to allow time synchronization work. */
- local->hw->reset_tsf(local->mdev);
- }
-#endif
- bssid_changed = os_memcmp(wpa_s->bssid, bss->bssid, ETH_ALEN);
- os_memcpy(wpa_s->bssid, bss->bssid, ETH_ALEN);
- if (bssid_changed)
- wpas_notify_bssid_changed(wpa_s);
-
-#if 0 /* FIX */
- local->conf.beacon_int = bss->beacon_int >= 10 ? bss->beacon_int : 10;
-
- sdata->drop_unencrypted = bss->capability &
- host_to_le16(WLAN_CAPABILITY_PRIVACY) ? 1 : 0;
-#endif
-
-#if 0 /* FIX */
- os_memset(&rq, 0, sizeof(rq));
- rq.m = bss->freq * 100000;
- rq.e = 1;
- res = ieee80211_ioctl_siwfreq(wpa_s, NULL, &rq, NULL);
-#endif
-
- if (!ieee80211_ibss_allowed(wpa_s)) {
-#if 0 /* FIX */
- wpa_printf(MSG_DEBUG, "MLME: IBSS not allowed on channel %d "
- "(%d MHz)", local->conf.channel,
- local->conf.freq);
-#endif
- return -1;
- }
-
- /* Set beacon template based on scan results */
- buf = os_malloc(400);
- len = 0;
- do {
- if (buf == NULL)
- break;
-
- mgmt = (struct ieee80211_mgmt *) buf;
- len += 24 + sizeof(mgmt->u.beacon);
- os_memset(mgmt, 0, 24 + sizeof(mgmt->u.beacon));
- mgmt->frame_control = IEEE80211_FC(WLAN_FC_TYPE_MGMT,
- WLAN_FC_STYPE_BEACON);
- os_memset(mgmt->da, 0xff, ETH_ALEN);
- os_memcpy(mgmt->sa, wpa_s->own_addr, ETH_ALEN);
- os_memcpy(mgmt->bssid, wpa_s->bssid, ETH_ALEN);
-#if 0 /* FIX */
- mgmt->u.beacon.beacon_int =
- host_to_le16(local->conf.beacon_int);
-#endif
- mgmt->u.beacon.capab_info = host_to_le16(bss->capability);
-
- pos = buf + len;
- len += 2 + wpa_s->mlme.ssid_len;
- *pos++ = WLAN_EID_SSID;
- *pos++ = wpa_s->mlme.ssid_len;
- os_memcpy(pos, wpa_s->mlme.ssid, wpa_s->mlme.ssid_len);
-
- rates = bss->supp_rates_len;
- if (rates > 8)
- rates = 8;
- pos = buf + len;
- len += 2 + rates;
- *pos++ = WLAN_EID_SUPP_RATES;
- *pos++ = rates;
- os_memcpy(pos, bss->supp_rates, rates);
-
- pos = buf + len;
- len += 2 + 1;
- *pos++ = WLAN_EID_DS_PARAMS;
- *pos++ = 1;
- *pos++ = bss->channel;
-
- pos = buf + len;
- len += 2 + 2;
- *pos++ = WLAN_EID_IBSS_PARAMS;
- *pos++ = 2;
- /* FIX: set ATIM window based on scan results */
- *pos++ = 0;
- *pos++ = 0;
-
- if (bss->supp_rates_len > 8) {
- rates = bss->supp_rates_len - 8;
- pos = buf + len;
- len += 2 + rates;
- *pos++ = WLAN_EID_EXT_SUPP_RATES;
- *pos++ = rates;
- os_memcpy(pos, &bss->supp_rates[8], rates);
- }
-
-#if 0 /* FIX */
- os_memset(&control, 0, sizeof(control));
- control.pkt_type = PKT_PROBE_RESP;
- os_memset(&extra, 0, sizeof(extra));
- extra.endidx = local->num_curr_rates;
- rate = rate_control_get_rate(wpa_s, skb, &extra);
- if (rate == NULL) {
- wpa_printf(MSG_DEBUG, "MLME: Failed to determine TX "
- "rate for IBSS beacon");
- break;
- }
- control.tx_rate = (wpa_s->mlme.short_preamble &&
- (rate->flags & IEEE80211_RATE_PREAMBLE2)) ?
- rate->val2 : rate->val;
- control.antenna_sel = local->conf.antenna_sel;
- control.power_level = local->conf.power_level;
- control.no_ack = 1;
- control.retry_limit = 1;
- control.rts_cts_duration = 0;
-#endif
-
-#if 0 /* FIX */
- wpa_s->mlme.probe_resp = skb_copy(skb, GFP_ATOMIC);
- if (wpa_s->mlme.probe_resp) {
- mgmt = (struct ieee80211_mgmt *)
- wpa_s->mlme.probe_resp->data;
- mgmt->frame_control =
- IEEE80211_FC(WLAN_FC_TYPE_MGMT,
- WLAN_FC_STYPE_PROBE_RESP);
- } else {
- wpa_printf(MSG_DEBUG, "MLME: Could not allocate "
- "ProbeResp template for IBSS");
- }
-
- if (local->hw->beacon_update &&
- local->hw->beacon_update(wpa_s, skb, &control) == 0) {
- wpa_printf(MSG_DEBUG, "MLME: Configured IBSS beacon "
- "template based on scan results");
- skb = NULL;
- }
-
- rates = 0;
- for (i = 0; i < bss->supp_rates_len; i++) {
- int rate = (bss->supp_rates[i] & 0x7f) * 5;
- if (local->conf.phymode == MODE_ATHEROS_TURBO)
- rate *= 2;
- for (j = 0; j < local->num_curr_rates; j++)
- if (local->curr_rates[j] == rate)
- rates |= BIT(j);
- }
- wpa_s->mlme.supp_rates_bits = rates;
-#endif
- done = 1;
- } while (0);
-
- os_free(buf);
- if (!done) {
- wpa_printf(MSG_DEBUG, "MLME: Failed to configure IBSS beacon "
- "template");
- }
-
- wpa_s->mlme.state = IEEE80211_IBSS_JOINED;
- ieee80211_reschedule_timer(wpa_s, IEEE80211_IBSS_MERGE_INTERVAL);
-
- return res;
-}
-
-
-#if 0 /* FIX */
-static int ieee80211_sta_create_ibss(struct wpa_supplicant *wpa_s)
-{
- struct ieee80211_sta_bss *bss;
- u8 bssid[ETH_ALEN], *pos;
- int i;
-
-#if 0
- /* Easier testing, use fixed BSSID. */
- os_memset(bssid, 0xfe, ETH_ALEN);
-#else
- /* Generate random, not broadcast, locally administered BSSID. Mix in
- * own MAC address to make sure that devices that do not have proper
- * random number generator get different BSSID. */
- os_get_random(bssid, ETH_ALEN);
- for (i = 0; i < ETH_ALEN; i++)
- bssid[i] ^= wpa_s->own_addr[i];
- bssid[0] &= ~0x01;
- bssid[0] |= 0x02;
-#endif
-
- wpa_printf(MSG_DEBUG, "MLME: Creating new IBSS network, BSSID "
- MACSTR "", MAC2STR(bssid));
-
- bss = ieee80211_bss_add(wpa_s, bssid);
- if (bss == NULL)
- return -ENOMEM;
-
-#if 0 /* FIX */
- if (local->conf.beacon_int == 0)
- local->conf.beacon_int = 100;
- bss->beacon_int = local->conf.beacon_int;
- bss->hw_mode = local->conf.phymode;
- bss->channel = local->conf.channel;
- bss->freq = local->conf.freq;
-#endif
- os_get_time(&bss->last_update);
- bss->capability = host_to_le16(WLAN_CAPABILITY_IBSS);
-#if 0 /* FIX */
- if (sdata->default_key) {
- bss->capability |= host_to_le16(WLAN_CAPABILITY_PRIVACY);
- } else
- sdata->drop_unencrypted = 0;
- bss->supp_rates_len = local->num_curr_rates;
-#endif
- pos = bss->supp_rates;
-#if 0 /* FIX */
- for (i = 0; i < local->num_curr_rates; i++) {
- int rate = local->curr_rates[i];
- if (local->conf.phymode == MODE_ATHEROS_TURBO)
- rate /= 2;
- *pos++ = (u8) (rate / 5);
- }
-#endif
-
- return ieee80211_sta_join_ibss(wpa_s, bss);
-}
-#endif
-
-
-static int ieee80211_sta_find_ibss(struct wpa_supplicant *wpa_s)
-{
- struct ieee80211_sta_bss *bss;
- int found = 0;
- u8 bssid[ETH_ALEN];
- int active_ibss;
- struct os_time now;
-
- if (wpa_s->mlme.ssid_len == 0)
- return -EINVAL;
-
- active_ibss = ieee80211_sta_active_ibss(wpa_s);
-#ifdef IEEE80211_IBSS_DEBUG
- wpa_printf(MSG_DEBUG, "MLME: sta_find_ibss (active_ibss=%d)",
- active_ibss);
-#endif /* IEEE80211_IBSS_DEBUG */
- for (bss = wpa_s->mlme.sta_bss_list; bss; bss = bss->next) {
- if (wpa_s->mlme.ssid_len != bss->ssid_len ||
- os_memcmp(wpa_s->mlme.ssid, bss->ssid, bss->ssid_len) != 0
- || !(bss->capability & WLAN_CAPABILITY_IBSS))
- continue;
-#ifdef IEEE80211_IBSS_DEBUG
- wpa_printf(MSG_DEBUG, " bssid=" MACSTR " found",
- MAC2STR(bss->bssid));
-#endif /* IEEE80211_IBSS_DEBUG */
- os_memcpy(bssid, bss->bssid, ETH_ALEN);
- found = 1;
- if (active_ibss ||
- os_memcmp(bssid, wpa_s->bssid, ETH_ALEN) != 0)
- break;
- }
-
-#ifdef IEEE80211_IBSS_DEBUG
- wpa_printf(MSG_DEBUG, " sta_find_ibss: selected " MACSTR " current "
- MACSTR, MAC2STR(bssid), MAC2STR(wpa_s->bssid));
-#endif /* IEEE80211_IBSS_DEBUG */
- if (found && os_memcmp(wpa_s->bssid, bssid, ETH_ALEN) != 0 &&
- (bss = ieee80211_bss_get(wpa_s, bssid))) {
- wpa_printf(MSG_DEBUG, "MLME: Selected IBSS BSSID " MACSTR
- " based on configured SSID",
- MAC2STR(bssid));
- return ieee80211_sta_join_ibss(wpa_s, bss);
- }
-#ifdef IEEE80211_IBSS_DEBUG
- wpa_printf(MSG_DEBUG, " did not try to join ibss");
-#endif /* IEEE80211_IBSS_DEBUG */
-
- /* Selected IBSS not found in current scan results - try to scan */
- os_get_time(&now);
-#if 0 /* FIX */
- if (wpa_s->mlme.state == IEEE80211_IBSS_JOINED &&
- !ieee80211_sta_active_ibss(wpa_s)) {
- ieee80211_reschedule_timer(wpa_s,
- IEEE80211_IBSS_MERGE_INTERVAL);
- } else if (time_after(jiffies, wpa_s->mlme.last_scan_completed +
- IEEE80211_SCAN_INTERVAL)) {
- wpa_printf(MSG_DEBUG, "MLME: Trigger new scan to find an IBSS "
- "to join");
- return ieee80211_sta_req_scan(wpa_s->mlme.ssid,
- wpa_s->mlme.ssid_len);
- } else if (wpa_s->mlme.state != IEEE80211_IBSS_JOINED) {
- int interval = IEEE80211_SCAN_INTERVAL;
-
- if (time_after(jiffies, wpa_s->mlme.ibss_join_req +
- IEEE80211_IBSS_JOIN_TIMEOUT)) {
- if (wpa_s->mlme.create_ibss &&
- ieee80211_ibss_allowed(wpa_s))
- return ieee80211_sta_create_ibss(wpa_s);
- if (wpa_s->mlme.create_ibss) {
- wpa_printf(MSG_DEBUG, "MLME: IBSS not allowed "
- "on the configured channel %d "
- "(%d MHz)",
- local->conf.channel,
- local->conf.freq);
- }
-
- /* No IBSS found - decrease scan interval and continue
- * scanning. */
- interval = IEEE80211_SCAN_INTERVAL_SLOW;
- }
-
- wpa_s->mlme.state = IEEE80211_IBSS_SEARCH;
- ieee80211_reschedule_timer(wpa_s, interval);
- return 0;
- }
-#endif
-
- return 0;
-}
-
-
-int ieee80211_sta_get_ssid(struct wpa_supplicant *wpa_s, u8 *ssid,
- size_t *len)
-{
- os_memcpy(ssid, wpa_s->mlme.ssid, wpa_s->mlme.ssid_len);
- *len = wpa_s->mlme.ssid_len;
- return 0;
-}
-
-
-int ieee80211_sta_associate(struct wpa_supplicant *wpa_s,
- struct wpa_driver_associate_params *params)
-{
- struct ieee80211_sta_bss *bss;
- int bssid_changed;
-
- wpa_s->mlme.bssid_set = 0;
- wpa_s->mlme.freq = params->freq;
- if (params->bssid) {
- bssid_changed = os_memcmp(wpa_s->bssid, params->bssid,
- ETH_ALEN);
- os_memcpy(wpa_s->bssid, params->bssid, ETH_ALEN);
- if (bssid_changed)
- wpas_notify_bssid_changed(wpa_s);
-
- if (!is_zero_ether_addr(params->bssid))
- wpa_s->mlme.bssid_set = 1;
- bss = ieee80211_bss_get(wpa_s, wpa_s->bssid);
- if (bss) {
- wpa_s->mlme.phymode = bss->hw_mode;
- wpa_s->mlme.channel = bss->channel;
- wpa_s->mlme.freq = bss->freq;
- }
- }
-
-#if 0 /* FIX */
- /* TODO: This should always be done for IBSS, even if IEEE80211_QOS is
- * not defined. */
- if (local->hw->conf_tx) {
- struct ieee80211_tx_queue_params qparam;
- int i;
-
- os_memset(&qparam, 0, sizeof(qparam));
- /* TODO: are these ok defaults for all hw_modes? */
- qparam.aifs = 2;
- qparam.cw_min =
- local->conf.phymode == MODE_IEEE80211B ? 31 : 15;
- qparam.cw_max = 1023;
- qparam.burst_time = 0;
- for (i = IEEE80211_TX_QUEUE_DATA0; i < NUM_TX_DATA_QUEUES; i++)
- {
- local->hw->conf_tx(wpa_s, i + IEEE80211_TX_QUEUE_DATA0,
- &qparam);
- }
- /* IBSS uses different parameters for Beacon sending */
- qparam.cw_min++;
- qparam.cw_min *= 2;
- qparam.cw_min--;
- local->hw->conf_tx(wpa_s, IEEE80211_TX_QUEUE_BEACON, &qparam);
- }
-#endif
-
- if (wpa_s->mlme.ssid_len != params->ssid_len ||
- os_memcmp(wpa_s->mlme.ssid, params->ssid, params->ssid_len) != 0)
- wpa_s->mlme.prev_bssid_set = 0;
- os_memcpy(wpa_s->mlme.ssid, params->ssid, params->ssid_len);
- os_memset(wpa_s->mlme.ssid + params->ssid_len, 0,
- MAX_SSID_LEN - params->ssid_len);
- wpa_s->mlme.ssid_len = params->ssid_len;
- wpa_s->mlme.ssid_set = 1;
-
- os_free(wpa_s->mlme.extra_ie);
- if (params->wpa_ie == NULL || params->wpa_ie_len == 0) {
- wpa_s->mlme.extra_ie = NULL;
- wpa_s->mlme.extra_ie_len = 0;
- } else {
- wpa_s->mlme.extra_ie = os_malloc(params->wpa_ie_len);
- if (wpa_s->mlme.extra_ie == NULL) {
- wpa_s->mlme.extra_ie_len = 0;
- return -1;
- }
- os_memcpy(wpa_s->mlme.extra_ie, params->wpa_ie,
- params->wpa_ie_len);
- wpa_s->mlme.extra_ie_len = params->wpa_ie_len;
- }
-
- wpa_s->mlme.key_mgmt = params->key_mgmt_suite;
-
- ieee80211_sta_set_channel(wpa_s, wpa_s->mlme.phymode,
- wpa_s->mlme.channel, wpa_s->mlme.freq);
-
- if (params->mode == WPAS_MODE_IBSS && !wpa_s->mlme.bssid_set) {
- os_get_time(&wpa_s->mlme.ibss_join_req);
- wpa_s->mlme.state = IEEE80211_IBSS_SEARCH;
- return ieee80211_sta_find_ibss(wpa_s);
- }
-
- if (wpa_s->mlme.bssid_set)
- ieee80211_sta_new_auth(wpa_s);
-
- return 0;
-}
-
-
-static void ieee80211_sta_save_oper_chan(struct wpa_supplicant *wpa_s)
-{
- wpa_s->mlme.scan_oper_channel = wpa_s->mlme.channel;
- wpa_s->mlme.scan_oper_freq = wpa_s->mlme.freq;
- wpa_s->mlme.scan_oper_phymode = wpa_s->mlme.phymode;
-}
-
-
-static int ieee80211_sta_restore_oper_chan(struct wpa_supplicant *wpa_s)
-{
- wpa_s->mlme.channel = wpa_s->mlme.scan_oper_channel;
- wpa_s->mlme.freq = wpa_s->mlme.scan_oper_freq;
- wpa_s->mlme.phymode = wpa_s->mlme.scan_oper_phymode;
- if (wpa_s->mlme.freq == 0)
- return 0;
- return ieee80211_sta_set_channel(wpa_s, wpa_s->mlme.phymode,
- wpa_s->mlme.channel,
- wpa_s->mlme.freq);
-}
-
-
-static int ieee80211_active_scan(struct wpa_supplicant *wpa_s)
-{
- size_t m;
- int c;
-
- for (m = 0; m < wpa_s->mlme.num_modes; m++) {
- struct hostapd_hw_modes *mode = &wpa_s->mlme.modes[m];
- if ((int) mode->mode != (int) wpa_s->mlme.phymode)
- continue;
- for (c = 0; c < mode->num_channels; c++) {
- struct hostapd_channel_data *chan = &mode->channels[c];
- if (!(chan->flag & HOSTAPD_CHAN_DISABLED) &&
- chan->chan == wpa_s->mlme.channel) {
- if (!(chan->flag & HOSTAPD_CHAN_PASSIVE_SCAN))
- return 1;
- break;
- }
- }
- }
-
- return 0;
-}
-
-
-static void ieee80211_sta_scan_timer(void *eloop_ctx, void *timeout_ctx)
-{
- struct wpa_supplicant *wpa_s = eloop_ctx;
- struct hostapd_hw_modes *mode;
- struct hostapd_channel_data *chan;
- int skip = 0;
- int timeout = 0;
- struct wpa_ssid *ssid = wpa_s->current_ssid;
- int adhoc;
-
- if (!wpa_s->mlme.sta_scanning || wpa_s->mlme.modes == NULL)
- return;
-
- adhoc = ssid && ssid->mode == 1;
-
- switch (wpa_s->mlme.scan_state) {
- case SCAN_SET_CHANNEL:
- mode = &wpa_s->mlme.modes[wpa_s->mlme.scan_hw_mode_idx];
- if (wpa_s->mlme.scan_hw_mode_idx >=
- (int) wpa_s->mlme.num_modes ||
- (wpa_s->mlme.scan_hw_mode_idx + 1 ==
- (int) wpa_s->mlme.num_modes
- && wpa_s->mlme.scan_channel_idx >= mode->num_channels)) {
- if (ieee80211_sta_restore_oper_chan(wpa_s)) {
- wpa_printf(MSG_DEBUG, "MLME: failed to "
- "restore operational channel after "
- "scan");
- }
- wpa_printf(MSG_DEBUG, "MLME: scan completed");
- wpa_s->mlme.sta_scanning = 0;
- os_get_time(&wpa_s->mlme.last_scan_completed);
- wpa_supplicant_event(wpa_s, EVENT_SCAN_RESULTS, NULL);
- if (adhoc) {
- if (!wpa_s->mlme.bssid_set ||
- (wpa_s->mlme.state ==
- IEEE80211_IBSS_JOINED &&
- !ieee80211_sta_active_ibss(wpa_s)))
- ieee80211_sta_find_ibss(wpa_s);
- }
- return;
- }
- skip = !(wpa_s->mlme.hw_modes & (1 << mode->mode));
- chan = &mode->channels[wpa_s->mlme.scan_channel_idx];
- if ((chan->flag & HOSTAPD_CHAN_DISABLED) ||
- (adhoc && (chan->flag & HOSTAPD_CHAN_NO_IBSS)) ||
- (wpa_s->mlme.hw_modes & (1 << HOSTAPD_MODE_IEEE80211G) &&
- mode->mode == HOSTAPD_MODE_IEEE80211B &&
- wpa_s->mlme.scan_skip_11b))
- skip = 1;
- if (!skip && wpa_s->mlme.scan_freqs) {
- int i, found = 0;
- for (i = 0; wpa_s->mlme.scan_freqs[i]; i++) {
- if (wpa_s->mlme.scan_freqs[i] == chan->freq) {
- found = 1;
- break;
- }
- }
- if (!found)
- skip = 1;
- }
-
- if (!skip) {
- wpa_printf(MSG_MSGDUMP,
- "MLME: scan channel %d (%d MHz)",
- chan->chan, chan->freq);
-
- wpa_s->mlme.channel = chan->chan;
- wpa_s->mlme.freq = chan->freq;
- wpa_s->mlme.phymode = mode->mode;
- if (ieee80211_sta_set_channel(wpa_s, mode->mode,
- chan->chan, chan->freq))
- {
- wpa_printf(MSG_DEBUG, "MLME: failed to set "
- "channel %d (%d MHz) for scan",
- chan->chan, chan->freq);
- skip = 1;
- }
- }
-
- wpa_s->mlme.scan_channel_idx++;
- if (wpa_s->mlme.scan_channel_idx >=
- wpa_s->mlme.modes[wpa_s->mlme.scan_hw_mode_idx].
- num_channels) {
- wpa_s->mlme.scan_hw_mode_idx++;
- wpa_s->mlme.scan_channel_idx = 0;
- }
-
- if (skip) {
- timeout = 0;
- break;
- }
-
- timeout = IEEE80211_PROBE_DELAY;
- wpa_s->mlme.scan_state = SCAN_SEND_PROBE;
- break;
- case SCAN_SEND_PROBE:
- if (ieee80211_active_scan(wpa_s)) {
- ieee80211_send_probe_req(wpa_s, NULL,
- wpa_s->mlme.scan_ssid,
- wpa_s->mlme.scan_ssid_len);
- timeout = IEEE80211_CHANNEL_TIME;
- } else {
- timeout = IEEE80211_PASSIVE_CHANNEL_TIME;
- }
- wpa_s->mlme.scan_state = SCAN_SET_CHANNEL;
- break;
- }
-
- eloop_register_timeout(timeout / 1000, 1000 * (timeout % 1000),
- ieee80211_sta_scan_timer, wpa_s, NULL);
-}
-
-
-int ieee80211_sta_req_scan(struct wpa_supplicant *wpa_s,
- struct wpa_driver_scan_params *params)
-{
- const u8 *ssid = params->ssids[0].ssid;
- size_t ssid_len = params->ssids[0].ssid_len;
-
- if (ssid_len > MAX_SSID_LEN)
- return -1;
-
- /* MLME-SCAN.request (page 118) page 144 (11.1.3.1)
- * BSSType: INFRASTRUCTURE, INDEPENDENT, ANY_BSS
- * BSSID: MACAddress
- * SSID
- * ScanType: ACTIVE, PASSIVE
- * ProbeDelay: delay (in microseconds) to be used prior to transmitting
- * a Probe frame during active scanning
- * ChannelList
- * MinChannelTime (>= ProbeDelay), in TU
- * MaxChannelTime: (>= MinChannelTime), in TU
- */
-
- /* MLME-SCAN.confirm
- * BSSDescriptionSet
- * ResultCode: SUCCESS, INVALID_PARAMETERS
- */
-
- /* TODO: if assoc, move to power save mode for the duration of the
- * scan */
-
- if (wpa_s->mlme.sta_scanning)
- return -1;
-
- wpa_printf(MSG_DEBUG, "MLME: starting scan");
-
- ieee80211_sta_set_probe_req_ie(wpa_s, params->extra_ies,
- params->extra_ies_len);
-
- os_free(wpa_s->mlme.scan_freqs);
- if (params->freqs) {
- int i;
- for (i = 0; params->freqs[i]; i++)
- ;
- wpa_s->mlme.scan_freqs = os_malloc((i + 1) * sizeof(int));
- if (wpa_s->mlme.scan_freqs)
- os_memcpy(wpa_s->mlme.scan_freqs, params->freqs,
- (i + 1) * sizeof(int));
- } else
- wpa_s->mlme.scan_freqs = NULL;
-
- ieee80211_sta_save_oper_chan(wpa_s);
-
- wpa_s->mlme.sta_scanning = 1;
- /* TODO: stop TX queue? */
-
- if (ssid) {
- wpa_s->mlme.scan_ssid_len = ssid_len;
- os_memcpy(wpa_s->mlme.scan_ssid, ssid, ssid_len);
- } else
- wpa_s->mlme.scan_ssid_len = 0;
- wpa_s->mlme.scan_skip_11b = 1; /* FIX: clear this is 11g is not
- * supported */
- wpa_s->mlme.scan_state = SCAN_SET_CHANNEL;
- wpa_s->mlme.scan_hw_mode_idx = 0;
- wpa_s->mlme.scan_channel_idx = 0;
- eloop_register_timeout(0, 1, ieee80211_sta_scan_timer, wpa_s, NULL);
-
- return 0;
-}
-
-
-struct wpa_scan_results *
-ieee80211_sta_get_scan_results(struct wpa_supplicant *wpa_s)
-{
- size_t ap_num = 0;
- struct wpa_scan_results *res;
- struct wpa_scan_res *r;
- struct ieee80211_sta_bss *bss;
-
- res = os_zalloc(sizeof(*res));
- for (bss = wpa_s->mlme.sta_bss_list; bss; bss = bss->next)
- ap_num++;
- res->res = os_zalloc(ap_num * sizeof(struct wpa_scan_res *));
- if (res->res == NULL) {
- os_free(res);
- return NULL;
- }
-
- for (bss = wpa_s->mlme.sta_bss_list; bss; bss = bss->next) {
- r = os_zalloc(sizeof(*r) + bss->ie_len);
- if (r == NULL)
- break;
- os_memcpy(r->bssid, bss->bssid, ETH_ALEN);
- r->freq = bss->freq;
- r->beacon_int = bss->beacon_int;
- r->caps = bss->capability;
- r->level = bss->rssi;
- r->tsf = bss->timestamp;
- if (bss->ie) {
- r->ie_len = bss->ie_len;
- os_memcpy(r + 1, bss->ie, bss->ie_len);
- }
-
- res->res[res->num++] = r;
- }
-
- return res;
-}
-
-
-#if 0 /* FIX */
-struct sta_info * ieee80211_ibss_add_sta(struct wpa_supplicant *wpa_s,
- struct sk_buff *skb, u8 *bssid,
- u8 *addr)
-{
- struct ieee80211_local *local = dev->priv;
- struct list_head *ptr;
- struct sta_info *sta;
- struct wpa_supplicant *sta_dev = NULL;
-
- /* TODO: Could consider removing the least recently used entry and
- * allow new one to be added. */
- if (local->num_sta >= IEEE80211_IBSS_MAX_STA_ENTRIES) {
- if (net_ratelimit()) {
- wpa_printf(MSG_DEBUG, "MLME: No room for a new IBSS "
- "STA entry " MACSTR, MAC2STR(addr));
- }
- return NULL;
- }
-
- spin_lock_bh(&local->sub_if_lock);
- list_for_each(ptr, &local->sub_if_list) {
- sdata = list_entry(ptr, struct ieee80211_sub_if_data, list);
- if (sdata->type == IEEE80211_SUB_IF_TYPE_STA &&
- os_memcmp(bssid, sdata->u.sta.bssid, ETH_ALEN) == 0) {
- sta_dev = sdata->dev;
- break;
- }
- }
- spin_unlock_bh(&local->sub_if_lock);
-
- if (sta_dev == NULL)
- return NULL;
-
- wpa_printf(MSG_DEBUG, "MLME: Adding new IBSS station " MACSTR
- " (dev=%s)", MAC2STR(addr), sta_dev->name);
-
- sta = sta_info_add(wpa_s, addr);
- if (sta == NULL) {
- return NULL;
- }
-
- sta->dev = sta_dev;
- sta->supp_rates = wpa_s->mlme.supp_rates_bits;
-
- rate_control_rate_init(local, sta);
-
- return sta; /* caller will call sta_info_release() */
-}
-#endif
-
-
-int ieee80211_sta_deauthenticate(struct wpa_supplicant *wpa_s, u16 reason)
-{
- wpa_printf(MSG_DEBUG, "MLME: deauthenticate(reason=%d)", reason);
-
- ieee80211_send_deauth(wpa_s, reason);
- ieee80211_set_associated(wpa_s, 0);
- return 0;
-}
-
-
-int ieee80211_sta_disassociate(struct wpa_supplicant *wpa_s, u16 reason)
-{
- wpa_printf(MSG_DEBUG, "MLME: disassociate(reason=%d)", reason);
-
- if (!wpa_s->mlme.associated)
- return -1;
-
- ieee80211_send_disassoc(wpa_s, reason);
- ieee80211_set_associated(wpa_s, 0);
- return 0;
-}
-
-
-void ieee80211_sta_rx(struct wpa_supplicant *wpa_s, const u8 *buf, size_t len,
- struct ieee80211_rx_status *rx_status)
-{
- struct ieee80211_mgmt *mgmt;
- u16 fc;
- const u8 *pos;
-
- /* wpa_hexdump(MSG_MSGDUMP, "MLME: Received frame", buf, len); */
-
- if (wpa_s->mlme.sta_scanning) {
- ieee80211_sta_rx_scan(wpa_s, buf, len, rx_status);
- return;
- }
-
- if (len < 24)
- return;
-
- mgmt = (struct ieee80211_mgmt *) buf;
- fc = le_to_host16(mgmt->frame_control);
-
- if (WLAN_FC_GET_TYPE(fc) == WLAN_FC_TYPE_MGMT)
- ieee80211_sta_rx_mgmt(wpa_s, buf, len, rx_status);
- else if (WLAN_FC_GET_TYPE(fc) == WLAN_FC_TYPE_DATA) {
- if ((fc & (WLAN_FC_TODS | WLAN_FC_FROMDS)) !=
- WLAN_FC_FROMDS)
- return;
- /* mgmt->sa is actually BSSID for FromDS data frames */
- if (os_memcmp(mgmt->sa, wpa_s->bssid, ETH_ALEN) != 0)
- return;
- /* Skip IEEE 802.11 and LLC headers */
- pos = buf + 24 + 6;
- if (WPA_GET_BE16(pos) != ETH_P_EAPOL)
- return;
- pos += 2;
- /* mgmt->bssid is actually BSSID for SA data frames */
- wpa_supplicant_rx_eapol(wpa_s, mgmt->bssid,
- pos, buf + len - pos);
- }
-}
-
-
-void ieee80211_sta_free_hw_features(struct hostapd_hw_modes *hw_features,
- size_t num_hw_features)
-{
- size_t i;
-
- if (hw_features == NULL)
- return;
-
- for (i = 0; i < num_hw_features; i++) {
- os_free(hw_features[i].channels);
- os_free(hw_features[i].rates);
- }
-
- os_free(hw_features);
-}
-
-
-int ieee80211_sta_init(struct wpa_supplicant *wpa_s)
-{
- u16 num_modes, flags;
-
- wpa_s->mlme.modes = wpa_drv_get_hw_feature_data(wpa_s, &num_modes,
- &flags);
- if (wpa_s->mlme.modes == NULL) {
- wpa_printf(MSG_ERROR, "MLME: Failed to read supported "
- "channels and rates from the driver");
- return -1;
- }
-
- wpa_s->mlme.num_modes = num_modes;
-
- wpa_s->mlme.hw_modes = 1 << HOSTAPD_MODE_IEEE80211A;
- wpa_s->mlme.hw_modes |= 1 << HOSTAPD_MODE_IEEE80211B;
- wpa_s->mlme.hw_modes |= 1 << HOSTAPD_MODE_IEEE80211G;
-
- wpa_s->mlme.wmm_enabled = 1;
-
- return 0;
-}
-
-
-void ieee80211_sta_deinit(struct wpa_supplicant *wpa_s)
-{
- eloop_cancel_timeout(ieee80211_sta_timer, wpa_s, NULL);
- eloop_cancel_timeout(ieee80211_sta_scan_timer, wpa_s, NULL);
- os_free(wpa_s->mlme.extra_ie);
- wpa_s->mlme.extra_ie = NULL;
- os_free(wpa_s->mlme.extra_probe_ie);
- wpa_s->mlme.extra_probe_ie = NULL;
- os_free(wpa_s->mlme.assocreq_ies);
- wpa_s->mlme.assocreq_ies = NULL;
- os_free(wpa_s->mlme.assocresp_ies);
- wpa_s->mlme.assocresp_ies = NULL;
- ieee80211_bss_list_deinit(wpa_s);
- ieee80211_sta_free_hw_features(wpa_s->mlme.modes,
- wpa_s->mlme.num_modes);
-#ifdef CONFIG_IEEE80211R
- os_free(wpa_s->mlme.ft_ies);
- wpa_s->mlme.ft_ies = NULL;
- wpa_s->mlme.ft_ies_len = 0;
-#endif /* CONFIG_IEEE80211R */
-
- os_free(wpa_s->mlme.scan_freqs);
- wpa_s->mlme.scan_freqs = NULL;
-}
-
-
-#ifdef CONFIG_IEEE80211R
-
-int ieee80211_sta_update_ft_ies(struct wpa_supplicant *wpa_s, const u8 *md,
- const u8 *ies, size_t ies_len)
-{
- if (md == NULL) {
- wpa_printf(MSG_DEBUG, "MLME: Clear FT mobility domain");
- os_memset(wpa_s->mlme.current_md, 0, MOBILITY_DOMAIN_ID_LEN);
- } else {
- wpa_printf(MSG_DEBUG, "MLME: Update FT IEs for MD " MACSTR,
- MAC2STR(md));
- os_memcpy(wpa_s->mlme.current_md, md, MOBILITY_DOMAIN_ID_LEN);
- }
-
- wpa_hexdump(MSG_DEBUG, "MLME: FT IEs", ies, ies_len);
- os_free(wpa_s->mlme.ft_ies);
- wpa_s->mlme.ft_ies = os_malloc(ies_len);
- if (wpa_s->mlme.ft_ies == NULL)
- return -1;
- os_memcpy(wpa_s->mlme.ft_ies, ies, ies_len);
- wpa_s->mlme.ft_ies_len = ies_len;
-
- return 0;
-}
-
-
-int ieee80211_sta_send_ft_action(struct wpa_supplicant *wpa_s, u8 action,
- const u8 *target_ap,
- const u8 *ies, size_t ies_len)
-{
- u8 *buf;
- size_t len;
- struct ieee80211_mgmt *mgmt;
- int res;
-
- /*
- * Action frame payload:
- * Category[1] = 6 (Fast BSS Transition)
- * Action[1] = 1 (Fast BSS Transition Request)
- * STA Address
- * Target AP Address
- * FT IEs
- */
-
- buf = os_zalloc(sizeof(*mgmt) + ies_len);
- if (buf == NULL) {
- wpa_printf(MSG_DEBUG, "MLME: Failed to allocate buffer for "
- "FT action frame");
- return -1;
- }
-
- mgmt = (struct ieee80211_mgmt *) buf;
- len = 24;
- os_memcpy(mgmt->da, wpa_s->bssid, ETH_ALEN);
- os_memcpy(mgmt->sa, wpa_s->own_addr, ETH_ALEN);
- os_memcpy(mgmt->bssid, wpa_s->bssid, ETH_ALEN);
- mgmt->frame_control = IEEE80211_FC(WLAN_FC_TYPE_MGMT,
- WLAN_FC_STYPE_ACTION);
- mgmt->u.action.category = WLAN_ACTION_FT;
- mgmt->u.action.u.ft_action_req.action = action;
- os_memcpy(mgmt->u.action.u.ft_action_req.sta_addr, wpa_s->own_addr,
- ETH_ALEN);
- os_memcpy(mgmt->u.action.u.ft_action_req.target_ap_addr, target_ap,
- ETH_ALEN);
- os_memcpy(mgmt->u.action.u.ft_action_req.variable, ies, ies_len);
- len += 1 + sizeof(mgmt->u.action.u.ft_action_req) + ies_len;
-
- wpa_printf(MSG_DEBUG, "MLME: Send FT Action Frame: Action=%d "
- "Target AP=" MACSTR " body_len=%lu",
- action, MAC2STR(target_ap), (unsigned long) ies_len);
-
- res = ieee80211_sta_tx(wpa_s, buf, len);
- os_free(buf);
-
- return res;
-}
-
-#endif /* CONFIG_IEEE80211R */
-
-
-static int ieee80211_sta_set_probe_req_ie(struct wpa_supplicant *wpa_s,
- const u8 *ies, size_t ies_len)
-{
- os_free(wpa_s->mlme.extra_probe_ie);
- wpa_s->mlme.extra_probe_ie = NULL;
- wpa_s->mlme.extra_probe_ie_len = 0;
-
- if (ies == NULL)
- return 0;
-
- wpa_s->mlme.extra_probe_ie = os_malloc(ies_len);
- if (wpa_s->mlme.extra_probe_ie == NULL)
- return -1;
-
- os_memcpy(wpa_s->mlme.extra_probe_ie, ies, ies_len);
- wpa_s->mlme.extra_probe_ie_len = ies_len;
-
- return 0;
-}
diff --git a/contrib/wpa/wpa_supplicant/mlme.h b/contrib/wpa/wpa_supplicant/mlme.h
deleted file mode 100644
index 5db3665..0000000
--- a/contrib/wpa/wpa_supplicant/mlme.h
+++ /dev/null
@@ -1,129 +0,0 @@
-/*
- * WPA Supplicant - Client mode MLME
- * Copyright (c) 2003-2007, Jouni Malinen <j@w1.fi>
- * Copyright (c) 2004, Instant802 Networks, Inc.
- * Copyright (c) 2005-2006, Devicescape Software, Inc.
- *
- * This program is free software; you can redistribute it and/or modify
- * it under the terms of the GNU General Public License version 2 as
- * published by the Free Software Foundation.
- *
- * Alternatively, this software may be distributed under the terms of BSD
- * license.
- *
- * See README and COPYING for more details.
- */
-
-#ifndef MLME_H
-#define MLME_H
-
-struct wpa_supplicant;
-
-struct ieee80211_rx_status {
- int freq;
- int channel;
- int ssi;
-};
-
-#ifdef CONFIG_CLIENT_MLME
-
-int ieee80211_sta_init(struct wpa_supplicant *wpa_s);
-void ieee80211_sta_deinit(struct wpa_supplicant *wpa_s);
-int ieee80211_sta_req_scan(struct wpa_supplicant *wpa_s,
- struct wpa_driver_scan_params *params);
-int ieee80211_sta_deauthenticate(struct wpa_supplicant *wpa_s, u16 reason);
-int ieee80211_sta_disassociate(struct wpa_supplicant *wpa_s, u16 reason);
-int ieee80211_sta_associate(struct wpa_supplicant *wpa_s,
- struct wpa_driver_associate_params *params);
-int ieee80211_sta_get_ssid(struct wpa_supplicant *wpa_s, u8 *ssid,
- size_t *len);
-void ieee80211_sta_free_hw_features(struct hostapd_hw_modes *hw_features,
- size_t num_hw_features);
-void ieee80211_sta_rx(struct wpa_supplicant *wpa_s, const u8 *buf, size_t len,
- struct ieee80211_rx_status *rx_status);
-struct wpa_scan_results *
-ieee80211_sta_get_scan_results(struct wpa_supplicant *wpa_s);
-int ieee80211_sta_update_ft_ies(struct wpa_supplicant *wpa_s, const u8 *md,
- const u8 *ies, size_t ies_len);
-int ieee80211_sta_send_ft_action(struct wpa_supplicant *wpa_s, u8 action,
- const u8 *target_ap,
- const u8 *ies, size_t ies_len);
-
-#else /* CONFIG_CLIENT_MLME */
-
-static inline int ieee80211_sta_init(struct wpa_supplicant *wpa_s)
-{
- return 0;
-}
-
-static inline void ieee80211_sta_deinit(struct wpa_supplicant *wpa_s)
-{
-}
-
-static inline int ieee80211_sta_req_scan(struct wpa_supplicant *wpa_s,
- struct wpa_driver_scan_params *params)
-{
- return -1;
-}
-
-static inline int ieee80211_sta_deauthenticate(struct wpa_supplicant *wpa_s,
- u16 reason)
-{
- return -1;
-}
-
-static inline int ieee80211_sta_disassociate(struct wpa_supplicant *wpa_s,
- u16 reason)
-{
- return -1;
-}
-
-static inline int
-ieee80211_sta_associate(struct wpa_supplicant *wpa_s,
- struct wpa_driver_associate_params *params)
-{
- return -1;
-}
-
-static inline int ieee80211_sta_get_ssid(struct wpa_supplicant *wpa_s,
- u8 *ssid, size_t *len)
-{
- return -1;
-}
-
-static inline void
-ieee80211_sta_free_hw_features(struct hostapd_hw_modes *hw_features,
- size_t num_hw_features)
-{
-}
-
-static inline void
-ieee80211_sta_rx(struct wpa_supplicant *wpa_s, const u8 *buf, size_t len,
- struct ieee80211_rx_status *rx_status)
-{
-}
-
-static inline struct wpa_scan_results *
-ieee80211_sta_get_scan_results(struct wpa_supplicant *wpa_s)
-{
- return NULL;
-}
-
-static inline int
-ieee80211_sta_update_ft_ies(struct wpa_supplicant *wpa_s, const u8 *md,
- const u8 *ies, size_t ies_len)
-{
- return -1;
-}
-
-static inline int
-ieee80211_sta_send_ft_action(struct wpa_supplicant *wpa_s, u8 action,
- const u8 *target_ap,
- const u8 *ies, size_t ies_len)
-{
- return -1;
-}
-
-#endif /* CONFIG_CLIENT_MLME */
-
-#endif /* MLME_H */
diff --git a/contrib/wpa/wpa_supplicant/nfc_pw_token.c b/contrib/wpa/wpa_supplicant/nfc_pw_token.c
new file mode 100644
index 0000000..11afb5b
--- /dev/null
+++ b/contrib/wpa/wpa_supplicant/nfc_pw_token.c
@@ -0,0 +1,83 @@
+/*
+ * nfc_pw_token - Tool for building NFC password tokens for WPS
+ * Copyright (c) 2012, Jouni Malinen <j@w1.fi>
+ *
+ * This software may be distributed under the terms of the BSD license.
+ * See README for more details.
+ */
+
+#include "includes.h"
+
+#include "utils/common.h"
+#include "crypto/random.h"
+#include "wpa_supplicant_i.h"
+#include "config.h"
+#include "wps_supplicant.h"
+
+
+static void print_bin(const char *title, const struct wpabuf *buf)
+{
+ size_t i, len;
+ const u8 *pos;
+
+ if (buf == NULL)
+ return;
+
+ printf("%s=", title);
+
+ pos = wpabuf_head(buf);
+ len = wpabuf_len(buf);
+ for (i = 0; i < len; i++)
+ printf("%02X", *pos++);
+
+ printf("\n");
+}
+
+
+int main(int argc, char *argv[])
+{
+ struct wpa_supplicant wpa_s;
+ int ret = -1;
+ struct wpabuf *buf = NULL, *ndef = NULL;
+ char txt[1000];
+
+ if (os_program_init())
+ return -1;
+ random_init(NULL);
+
+ os_memset(&wpa_s, 0, sizeof(wpa_s));
+ wpa_s.conf = os_zalloc(sizeof(*wpa_s.conf));
+ if (wpa_s.conf == NULL)
+ goto fail;
+
+ buf = wpas_wps_nfc_token(&wpa_s, 0);
+ if (buf == NULL)
+ goto fail;
+
+ ndef = ndef_build_wifi(buf);
+ if (ndef == NULL)
+ goto fail;
+
+ wpa_snprintf_hex_uppercase(txt, sizeof(txt), wpabuf_head(buf),
+ wpabuf_len(buf));
+ printf("#WPS=%s\n", txt);
+
+ wpa_snprintf_hex_uppercase(txt, sizeof(txt), wpabuf_head(ndef),
+ wpabuf_len(ndef));
+ printf("#NDEF=%s\n", txt);
+
+ printf("wps_nfc_dev_pw_id=%d\n", wpa_s.conf->wps_nfc_dev_pw_id);
+ print_bin("wps_nfc_dh_pubkey", wpa_s.conf->wps_nfc_dh_pubkey);
+ print_bin("wps_nfc_dh_privkey", wpa_s.conf->wps_nfc_dh_privkey);
+ print_bin("wps_nfc_dev_pw", wpa_s.conf->wps_nfc_dev_pw);
+
+ ret = 0;
+fail:
+ wpabuf_free(ndef);
+ wpabuf_free(buf);
+ wpa_config_free(wpa_s.conf);
+ random_deinit();
+ os_program_deinit();
+
+ return ret;
+}
diff --git a/contrib/wpa/wpa_supplicant/notify.c b/contrib/wpa/wpa_supplicant/notify.c
index ac65b4f..9251f62 100644
--- a/contrib/wpa/wpa_supplicant/notify.c
+++ b/contrib/wpa/wpa_supplicant/notify.c
@@ -2,14 +2,8 @@
* wpa_supplicant - Event notifications
* Copyright (c) 2009-2010, Jouni Malinen <j@w1.fi>
*
- * This program is free software; you can redistribute it and/or modify
- * it under the terms of the GNU General Public License version 2 as
- * published by the Free Software Foundation.
- *
- * Alternatively, this software may be distributed under the terms of BSD
- * license.
- *
- * See README and COPYING for more details.
+ * This software may be distributed under the terms of the BSD license.
+ * See README for more details.
*/
#include "utils/includes.h"
@@ -22,8 +16,11 @@
#include "dbus/dbus_common.h"
#include "dbus/dbus_old.h"
#include "dbus/dbus_new.h"
+#include "rsn_supp/wpa.h"
#include "driver_i.h"
#include "scan.h"
+#include "p2p_supplicant.h"
+#include "sme.h"
#include "notify.h"
int wpas_notify_supplicant_initialized(struct wpa_global *global)
@@ -81,6 +78,28 @@ void wpas_notify_state_changed(struct wpa_supplicant *wpa_s,
/* notify the new DBus API */
wpas_dbus_signal_prop_changed(wpa_s, WPAS_DBUS_PROP_STATE);
+
+#ifdef CONFIG_P2P
+ if (new_state == WPA_COMPLETED)
+ wpas_p2p_notif_connected(wpa_s);
+ else if (old_state >= WPA_ASSOCIATED && new_state < WPA_ASSOCIATED)
+ wpas_p2p_notif_disconnected(wpa_s);
+#endif /* CONFIG_P2P */
+
+ sme_state_changed(wpa_s);
+
+#ifdef ANDROID
+ wpa_msg_ctrl(wpa_s, MSG_INFO, WPA_EVENT_STATE_CHANGE
+ "id=%d state=%d BSSID=" MACSTR,
+ wpa_s->current_ssid ? wpa_s->current_ssid->id : -1,
+ new_state, MAC2STR(wpa_s->pending_bssid));
+#endif /* ANDROID */
+}
+
+
+void wpas_notify_disconnect_reason(struct wpa_supplicant *wpa_s)
+{
+ wpas_dbus_signal_prop_changed(wpa_s, WPAS_DBUS_PROP_DISCONNECT_REASON);
}
@@ -102,6 +121,12 @@ void wpas_notify_bssid_changed(struct wpa_supplicant *wpa_s)
}
+void wpas_notify_auth_changed(struct wpa_supplicant *wpa_s)
+{
+ wpas_dbus_signal_prop_changed(wpa_s, WPAS_DBUS_PROP_CURRENT_AUTH_MODE);
+}
+
+
void wpas_notify_network_enabled_changed(struct wpa_supplicant *wpa_s,
struct wpa_ssid *ssid)
{
@@ -116,6 +141,15 @@ void wpas_notify_network_selected(struct wpa_supplicant *wpa_s,
}
+void wpas_notify_network_request(struct wpa_supplicant *wpa_s,
+ struct wpa_ssid *ssid,
+ enum wpa_ctrl_req_type rtype,
+ const char *default_txt)
+{
+ wpas_dbus_signal_network_request(wpa_s, ssid, rtype, default_txt);
+}
+
+
void wpas_notify_scanning(struct wpa_supplicant *wpa_s)
{
/* notify the old DBus API */
@@ -182,14 +216,45 @@ void wpas_notify_wps_event_success(struct wpa_supplicant *wpa_s)
void wpas_notify_network_added(struct wpa_supplicant *wpa_s,
struct wpa_ssid *ssid)
{
- wpas_dbus_register_network(wpa_s, ssid);
+ /*
+ * Networks objects created during any P2P activities should not be
+ * exposed out. They might/will confuse certain non-P2P aware
+ * applications since these network objects won't behave like
+ * regular ones.
+ */
+ if (wpa_s->global->p2p_group_formation != wpa_s)
+ wpas_dbus_register_network(wpa_s, ssid);
+}
+
+
+void wpas_notify_persistent_group_added(struct wpa_supplicant *wpa_s,
+ struct wpa_ssid *ssid)
+{
+#ifdef CONFIG_P2P
+ wpas_dbus_register_persistent_group(wpa_s, ssid);
+#endif /* CONFIG_P2P */
+}
+
+
+void wpas_notify_persistent_group_removed(struct wpa_supplicant *wpa_s,
+ struct wpa_ssid *ssid)
+{
+#ifdef CONFIG_P2P
+ wpas_dbus_unregister_persistent_group(wpa_s, ssid->id);
+#endif /* CONFIG_P2P */
}
void wpas_notify_network_removed(struct wpa_supplicant *wpa_s,
struct wpa_ssid *ssid)
{
- wpas_dbus_unregister_network(wpa_s, ssid->id);
+ if (wpa_s->wpa)
+ wpa_sm_pmksa_cache_flush(wpa_s->wpa, ssid);
+ if (wpa_s->global->p2p_group_formation != wpa_s)
+ wpas_dbus_unregister_network(wpa_s, ssid->id);
+#ifdef CONFIG_P2P
+ wpas_p2p_network_removed(wpa_s, ssid);
+#endif /* CONFIG_P2P */
}
@@ -258,6 +323,9 @@ void wpas_notify_bss_rsnie_changed(struct wpa_supplicant *wpa_s,
void wpas_notify_bss_wps_changed(struct wpa_supplicant *wpa_s,
unsigned int id)
{
+#ifdef CONFIG_WPS
+ wpas_dbus_bss_signal_prop_changed(wpa_s, WPAS_DBUS_BSS_PROP_WPS, id);
+#endif /* CONFIG_WPS */
}
@@ -337,3 +405,226 @@ void wpas_notify_resume(struct wpa_global *global)
wpa_supplicant_req_scan(wpa_s, 0, 100000);
}
}
+
+
+#ifdef CONFIG_P2P
+
+void wpas_notify_p2p_device_found(struct wpa_supplicant *wpa_s,
+ const u8 *dev_addr, int new_device)
+{
+ if (new_device) {
+ /* Create the new peer object */
+ wpas_dbus_register_peer(wpa_s, dev_addr);
+ }
+
+ /* Notify a new peer has been detected*/
+ wpas_dbus_signal_peer_device_found(wpa_s, dev_addr);
+}
+
+
+void wpas_notify_p2p_device_lost(struct wpa_supplicant *wpa_s,
+ const u8 *dev_addr)
+{
+ wpas_dbus_unregister_peer(wpa_s, dev_addr);
+
+ /* Create signal on interface object*/
+ wpas_dbus_signal_peer_device_lost(wpa_s, dev_addr);
+}
+
+
+void wpas_notify_p2p_group_removed(struct wpa_supplicant *wpa_s,
+ const struct wpa_ssid *ssid,
+ const char *role)
+{
+ wpas_dbus_unregister_p2p_group(wpa_s, ssid);
+
+ wpas_dbus_signal_p2p_group_removed(wpa_s, role);
+}
+
+
+void wpas_notify_p2p_go_neg_req(struct wpa_supplicant *wpa_s,
+ const u8 *src, u16 dev_passwd_id)
+{
+ wpas_dbus_signal_p2p_go_neg_req(wpa_s, src, dev_passwd_id);
+}
+
+
+void wpas_notify_p2p_go_neg_completed(struct wpa_supplicant *wpa_s,
+ struct p2p_go_neg_results *res)
+{
+ wpas_dbus_signal_p2p_go_neg_resp(wpa_s, res);
+}
+
+
+void wpas_notify_p2p_invitation_result(struct wpa_supplicant *wpa_s,
+ int status, const u8 *bssid)
+{
+ wpas_dbus_signal_p2p_invitation_result(wpa_s, status, bssid);
+}
+
+
+void wpas_notify_p2p_sd_request(struct wpa_supplicant *wpa_s,
+ int freq, const u8 *sa, u8 dialog_token,
+ u16 update_indic, const u8 *tlvs,
+ size_t tlvs_len)
+{
+ wpas_dbus_signal_p2p_sd_request(wpa_s, freq, sa, dialog_token,
+ update_indic, tlvs, tlvs_len);
+}
+
+
+void wpas_notify_p2p_sd_response(struct wpa_supplicant *wpa_s,
+ const u8 *sa, u16 update_indic,
+ const u8 *tlvs, size_t tlvs_len)
+{
+ wpas_dbus_signal_p2p_sd_response(wpa_s, sa, update_indic,
+ tlvs, tlvs_len);
+}
+
+
+/**
+ * wpas_notify_p2p_provision_discovery - Notification of provision discovery
+ * @dev_addr: Who sent the request or responded to our request.
+ * @request: Will be 1 if request, 0 for response.
+ * @status: Valid only in case of response (0 in case of success)
+ * @config_methods: WPS config methods
+ * @generated_pin: PIN to be displayed in case of WPS_CONFIG_DISPLAY method
+ *
+ * This can be used to notify:
+ * - Requests or responses
+ * - Various config methods
+ * - Failure condition in case of response
+ */
+void wpas_notify_p2p_provision_discovery(struct wpa_supplicant *wpa_s,
+ const u8 *dev_addr, int request,
+ enum p2p_prov_disc_status status,
+ u16 config_methods,
+ unsigned int generated_pin)
+{
+ wpas_dbus_signal_p2p_provision_discovery(wpa_s, dev_addr, request,
+ status, config_methods,
+ generated_pin);
+}
+
+
+void wpas_notify_p2p_group_started(struct wpa_supplicant *wpa_s,
+ struct wpa_ssid *ssid, int network_id,
+ int client)
+{
+ /* Notify a group has been started */
+ wpas_dbus_register_p2p_group(wpa_s, ssid);
+
+ wpas_dbus_signal_p2p_group_started(wpa_s, ssid, client, network_id);
+}
+
+
+void wpas_notify_p2p_wps_failed(struct wpa_supplicant *wpa_s,
+ struct wps_event_fail *fail)
+{
+ wpas_dbus_signal_p2p_wps_failed(wpa_s, fail);
+}
+
+#endif /* CONFIG_P2P */
+
+
+static void wpas_notify_ap_sta_authorized(struct wpa_supplicant *wpa_s,
+ const u8 *sta,
+ const u8 *p2p_dev_addr)
+{
+#ifdef CONFIG_P2P
+ wpas_p2p_notify_ap_sta_authorized(wpa_s, p2p_dev_addr);
+
+ /*
+ * Register a group member object corresponding to this peer and
+ * emit a PeerJoined signal. This will check if it really is a
+ * P2P group.
+ */
+ wpas_dbus_register_p2p_groupmember(wpa_s, sta);
+
+ /*
+ * Create 'peer-joined' signal on group object -- will also
+ * check P2P itself.
+ */
+ wpas_dbus_signal_p2p_peer_joined(wpa_s, sta);
+#endif /* CONFIG_P2P */
+}
+
+
+static void wpas_notify_ap_sta_deauthorized(struct wpa_supplicant *wpa_s,
+ const u8 *sta)
+{
+#ifdef CONFIG_P2P
+ /*
+ * Unregister a group member object corresponding to this peer
+ * if this is a P2P group.
+ */
+ wpas_dbus_unregister_p2p_groupmember(wpa_s, sta);
+
+ /*
+ * Create 'peer-disconnected' signal on group object if this
+ * is a P2P group.
+ */
+ wpas_dbus_signal_p2p_peer_disconnected(wpa_s, sta);
+#endif /* CONFIG_P2P */
+}
+
+
+void wpas_notify_sta_authorized(struct wpa_supplicant *wpa_s,
+ const u8 *mac_addr, int authorized,
+ const u8 *p2p_dev_addr)
+{
+ if (authorized)
+ wpas_notify_ap_sta_authorized(wpa_s, mac_addr, p2p_dev_addr);
+ else
+ wpas_notify_ap_sta_deauthorized(wpa_s, mac_addr);
+}
+
+
+void wpas_notify_certification(struct wpa_supplicant *wpa_s, int depth,
+ const char *subject, const char *cert_hash,
+ const struct wpabuf *cert)
+{
+ wpa_msg(wpa_s, MSG_INFO, WPA_EVENT_EAP_PEER_CERT
+ "depth=%d subject='%s'%s%s",
+ depth, subject,
+ cert_hash ? " hash=" : "",
+ cert_hash ? cert_hash : "");
+
+ if (cert) {
+ char *cert_hex;
+ size_t len = wpabuf_len(cert) * 2 + 1;
+ cert_hex = os_malloc(len);
+ if (cert_hex) {
+ wpa_snprintf_hex(cert_hex, len, wpabuf_head(cert),
+ wpabuf_len(cert));
+ wpa_msg_ctrl(wpa_s, MSG_INFO,
+ WPA_EVENT_EAP_PEER_CERT
+ "depth=%d subject='%s' cert=%s",
+ depth, subject, cert_hex);
+ os_free(cert_hex);
+ }
+ }
+
+ /* notify the old DBus API */
+ wpa_supplicant_dbus_notify_certification(wpa_s, depth, subject,
+ cert_hash, cert);
+ /* notify the new DBus API */
+ wpas_dbus_signal_certification(wpa_s, depth, subject, cert_hash, cert);
+}
+
+
+void wpas_notify_preq(struct wpa_supplicant *wpa_s,
+ const u8 *addr, const u8 *dst, const u8 *bssid,
+ const u8 *ie, size_t ie_len, u32 ssi_signal)
+{
+#ifdef CONFIG_AP
+ wpas_dbus_signal_preq(wpa_s, addr, dst, bssid, ie, ie_len, ssi_signal);
+#endif /* CONFIG_AP */
+}
+
+
+void wpas_notify_eap_status(struct wpa_supplicant *wpa_s, const char *status,
+ const char *parameter)
+{
+ wpas_dbus_signal_eap_status(wpa_s, status, parameter);
+}
diff --git a/contrib/wpa/wpa_supplicant/notify.h b/contrib/wpa/wpa_supplicant/notify.h
index 2e70bdb..58675ac 100644
--- a/contrib/wpa/wpa_supplicant/notify.h
+++ b/contrib/wpa/wpa_supplicant/notify.h
@@ -2,19 +2,15 @@
* wpa_supplicant - Event notifications
* Copyright (c) 2009-2010, Jouni Malinen <j@w1.fi>
*
- * This program is free software; you can redistribute it and/or modify
- * it under the terms of the GNU General Public License version 2 as
- * published by the Free Software Foundation.
- *
- * Alternatively, this software may be distributed under the terms of BSD
- * license.
- *
- * See README and COPYING for more details.
+ * This software may be distributed under the terms of the BSD license.
+ * See README for more details.
*/
#ifndef NOTIFY_H
#define NOTIFY_H
+#include "p2p/p2p.h"
+
struct wps_credential;
struct wps_event_m2d;
struct wps_event_fail;
@@ -26,13 +22,19 @@ void wpas_notify_iface_removed(struct wpa_supplicant *wpa_s);
void wpas_notify_state_changed(struct wpa_supplicant *wpa_s,
enum wpa_states new_state,
enum wpa_states old_state);
+void wpas_notify_disconnect_reason(struct wpa_supplicant *wpa_s);
void wpas_notify_network_changed(struct wpa_supplicant *wpa_s);
void wpas_notify_ap_scan_changed(struct wpa_supplicant *wpa_s);
void wpas_notify_bssid_changed(struct wpa_supplicant *wpa_s);
+void wpas_notify_auth_changed(struct wpa_supplicant *wpa_s);
void wpas_notify_network_enabled_changed(struct wpa_supplicant *wpa_s,
struct wpa_ssid *ssid);
void wpas_notify_network_selected(struct wpa_supplicant *wpa_s,
struct wpa_ssid *ssid);
+void wpas_notify_network_request(struct wpa_supplicant *wpa_s,
+ struct wpa_ssid *ssid,
+ enum wpa_ctrl_req_type rtype,
+ const char *default_txt);
void wpas_notify_scanning(struct wpa_supplicant *wpa_s);
void wpas_notify_scan_done(struct wpa_supplicant *wpa_s, int success);
void wpas_notify_scan_results(struct wpa_supplicant *wpa_s);
@@ -78,4 +80,52 @@ void wpas_notify_debug_show_keys_changed(struct wpa_global *global);
void wpas_notify_suspend(struct wpa_global *global);
void wpas_notify_resume(struct wpa_global *global);
+void wpas_notify_sta_authorized(struct wpa_supplicant *wpa_s,
+ const u8 *mac_addr, int authorized,
+ const u8 *p2p_dev_addr);
+void wpas_notify_p2p_device_found(struct wpa_supplicant *wpa_s,
+ const u8 *dev_addr, int new_device);
+void wpas_notify_p2p_device_lost(struct wpa_supplicant *wpa_s,
+ const u8 *dev_addr);
+void wpas_notify_p2p_group_removed(struct wpa_supplicant *wpa_s,
+ const struct wpa_ssid *ssid,
+ const char *role);
+void wpas_notify_p2p_go_neg_req(struct wpa_supplicant *wpa_s,
+ const u8 *src, u16 dev_passwd_id);
+void wpas_notify_p2p_go_neg_completed(struct wpa_supplicant *wpa_s,
+ struct p2p_go_neg_results *res);
+void wpas_notify_p2p_invitation_result(struct wpa_supplicant *wpa_s,
+ int status, const u8 *bssid);
+void wpas_notify_p2p_sd_request(struct wpa_supplicant *wpa_s,
+ int freq, const u8 *sa, u8 dialog_token,
+ u16 update_indic, const u8 *tlvs,
+ size_t tlvs_len);
+void wpas_notify_p2p_sd_response(struct wpa_supplicant *wpa_s,
+ const u8 *sa, u16 update_indic,
+ const u8 *tlvs, size_t tlvs_len);
+void wpas_notify_p2p_provision_discovery(struct wpa_supplicant *wpa_s,
+ const u8 *dev_addr, int request,
+ enum p2p_prov_disc_status status,
+ u16 config_methods,
+ unsigned int generated_pin);
+void wpas_notify_p2p_group_started(struct wpa_supplicant *wpa_s,
+ struct wpa_ssid *ssid, int network_id,
+ int client);
+void wpas_notify_persistent_group_added(struct wpa_supplicant *wpa_s,
+ struct wpa_ssid *ssid);
+void wpas_notify_persistent_group_removed(struct wpa_supplicant *wpa_s,
+ struct wpa_ssid *ssid);
+
+void wpas_notify_p2p_wps_failed(struct wpa_supplicant *wpa_s,
+ struct wps_event_fail *fail);
+
+void wpas_notify_certification(struct wpa_supplicant *wpa_s, int depth,
+ const char *subject, const char *cert_hash,
+ const struct wpabuf *cert);
+void wpas_notify_preq(struct wpa_supplicant *wpa_s,
+ const u8 *addr, const u8 *dst, const u8 *bssid,
+ const u8 *ie, size_t ie_len, u32 ssi_signal);
+void wpas_notify_eap_status(struct wpa_supplicant *wpa_s, const char *status,
+ const char *parameter);
+
#endif /* NOTIFY_H */
diff --git a/contrib/wpa/wpa_supplicant/offchannel.c b/contrib/wpa/wpa_supplicant/offchannel.c
new file mode 100644
index 0000000..856eca7
--- /dev/null
+++ b/contrib/wpa/wpa_supplicant/offchannel.c
@@ -0,0 +1,397 @@
+/*
+ * wpa_supplicant - Off-channel Action frame TX/RX
+ * Copyright (c) 2009-2010, Atheros Communications
+ * Copyright (c) 2011, Qualcomm Atheros
+ *
+ * This software may be distributed under the terms of the BSD license.
+ * See README for more details.
+ */
+
+#include "includes.h"
+
+#include "common.h"
+#include "utils/eloop.h"
+#include "wpa_supplicant_i.h"
+#include "driver_i.h"
+#include "offchannel.h"
+
+
+
+static struct wpa_supplicant *
+wpas_get_tx_interface(struct wpa_supplicant *wpa_s, const u8 *src)
+{
+ struct wpa_supplicant *iface;
+
+ if (os_memcmp(src, wpa_s->own_addr, ETH_ALEN) == 0)
+ return wpa_s;
+
+ /*
+ * Try to find a group interface that matches with the source address.
+ */
+ iface = wpa_s->global->ifaces;
+ while (iface) {
+ if (os_memcmp(wpa_s->pending_action_src,
+ iface->own_addr, ETH_ALEN) == 0)
+ break;
+ iface = iface->next;
+ }
+ if (iface) {
+ wpa_printf(MSG_DEBUG, "P2P: Use group interface %s "
+ "instead of interface %s for Action TX",
+ iface->ifname, wpa_s->ifname);
+ return iface;
+ }
+
+ return wpa_s;
+}
+
+
+static void wpas_send_action_cb(void *eloop_ctx, void *timeout_ctx)
+{
+ struct wpa_supplicant *wpa_s = eloop_ctx;
+ struct wpa_supplicant *iface;
+ int res;
+ int without_roc;
+
+ without_roc = wpa_s->pending_action_without_roc;
+ wpa_s->pending_action_without_roc = 0;
+ wpa_printf(MSG_DEBUG, "Off-channel: Send Action callback "
+ "(without_roc=%d pending_action_tx=%p)",
+ without_roc, wpa_s->pending_action_tx);
+
+ if (wpa_s->pending_action_tx == NULL)
+ return;
+
+ /*
+ * This call is likely going to be on the P2P device instance if the
+ * driver uses a separate interface for that purpose. However, some
+ * Action frames are actually sent within a P2P Group and when that is
+ * the case, we need to follow power saving (e.g., GO buffering the
+ * frame for a client in PS mode or a client following the advertised
+ * NoA from its GO). To make that easier for the driver, select the
+ * correct group interface here.
+ */
+ iface = wpas_get_tx_interface(wpa_s, wpa_s->pending_action_src);
+
+ if (wpa_s->off_channel_freq != wpa_s->pending_action_freq &&
+ wpa_s->pending_action_freq != 0 &&
+ wpa_s->pending_action_freq != iface->assoc_freq) {
+ wpa_printf(MSG_DEBUG, "Off-channel: Pending Action frame TX "
+ "waiting for another freq=%u (off_channel_freq=%u "
+ "assoc_freq=%u)",
+ wpa_s->pending_action_freq,
+ wpa_s->off_channel_freq,
+ iface->assoc_freq);
+ if (without_roc && wpa_s->off_channel_freq == 0) {
+ /*
+ * We may get here if wpas_send_action() found us to be
+ * on the correct channel, but remain-on-channel cancel
+ * event was received before getting here.
+ */
+ wpa_printf(MSG_DEBUG, "Off-channel: Schedule "
+ "remain-on-channel to send Action frame");
+ if (wpa_drv_remain_on_channel(
+ wpa_s, wpa_s->pending_action_freq, 200) <
+ 0) {
+ wpa_printf(MSG_DEBUG, "Off-channel: Failed to "
+ "request driver to remain on "
+ "channel (%u MHz) for Action Frame "
+ "TX", wpa_s->pending_action_freq);
+ } else {
+ wpa_s->off_channel_freq = 0;
+ wpa_s->roc_waiting_drv_freq =
+ wpa_s->pending_action_freq;
+ }
+ }
+ return;
+ }
+
+ wpa_printf(MSG_DEBUG, "Off-channel: Sending pending Action frame to "
+ MACSTR " using interface %s",
+ MAC2STR(wpa_s->pending_action_dst), iface->ifname);
+ res = wpa_drv_send_action(iface, wpa_s->pending_action_freq, 0,
+ wpa_s->pending_action_dst,
+ wpa_s->pending_action_src,
+ wpa_s->pending_action_bssid,
+ wpabuf_head(wpa_s->pending_action_tx),
+ wpabuf_len(wpa_s->pending_action_tx),
+ wpa_s->pending_action_no_cck);
+ if (res) {
+ wpa_printf(MSG_DEBUG, "Off-channel: Failed to send the "
+ "pending Action frame");
+ /*
+ * Use fake TX status event to allow state machines to
+ * continue.
+ */
+ offchannel_send_action_tx_status(
+ wpa_s, wpa_s->pending_action_dst,
+ wpabuf_head(wpa_s->pending_action_tx),
+ wpabuf_len(wpa_s->pending_action_tx),
+ OFFCHANNEL_SEND_ACTION_FAILED);
+ }
+}
+
+
+/**
+ * offchannel_send_action_tx_status - TX status callback
+ * @wpa_s: Pointer to wpa_supplicant data
+ * @dst: Destination MAC address of the transmitted Action frame
+ * @data: Transmitted frame payload
+ * @data_len: Length of @data in bytes
+ * @result: TX status
+ *
+ * This function is called whenever the driver indicates a TX status event for
+ * a frame sent by offchannel_send_action() using wpa_drv_send_action().
+ */
+void offchannel_send_action_tx_status(
+ struct wpa_supplicant *wpa_s, const u8 *dst, const u8 *data,
+ size_t data_len, enum offchannel_send_action_result result)
+{
+ if (wpa_s->pending_action_tx == NULL) {
+ wpa_printf(MSG_DEBUG, "Off-channel: Ignore Action TX status - "
+ "no pending operation");
+ return;
+ }
+
+ if (os_memcmp(dst, wpa_s->pending_action_dst, ETH_ALEN) != 0) {
+ wpa_printf(MSG_DEBUG, "Off-channel: Ignore Action TX status - "
+ "unknown destination address");
+ return;
+ }
+
+ wpabuf_free(wpa_s->pending_action_tx);
+ wpa_s->pending_action_tx = NULL;
+
+ wpa_printf(MSG_DEBUG, "Off-channel: TX status result=%d cb=%p",
+ result, wpa_s->pending_action_tx_status_cb);
+
+ if (wpa_s->pending_action_tx_status_cb) {
+ wpa_s->pending_action_tx_status_cb(
+ wpa_s, wpa_s->pending_action_freq,
+ wpa_s->pending_action_dst, wpa_s->pending_action_src,
+ wpa_s->pending_action_bssid,
+ data, data_len, result);
+ }
+}
+
+
+/**
+ * offchannel_send_action - Request off-channel Action frame TX
+ * @wpa_s: Pointer to wpa_supplicant data
+ * @freq: The frequency in MHz indicating the channel on which the frame is to
+ * transmitted or 0 for the current channel (only if associated)
+ * @dst: Action frame destination MAC address
+ * @src: Action frame source MAC address
+ * @bssid: Action frame BSSID
+ * @buf: Frame to transmit starting from the Category field
+ * @len: Length of @buf in bytes
+ * @wait_time: Wait time for response in milliseconds
+ * @tx_cb: Callback function for indicating TX status or %NULL for now callback
+ * @no_cck: Whether CCK rates are to be disallowed for TX rate selection
+ * Returns: 0 on success or -1 on failure
+ *
+ * This function is used to request an Action frame to be transmitted on the
+ * current operating channel or on another channel (off-channel). The actual
+ * frame transmission will be delayed until the driver is ready on the specified
+ * channel. The @wait_time parameter can be used to request the driver to remain
+ * awake on the channel to wait for a response.
+ */
+int offchannel_send_action(struct wpa_supplicant *wpa_s, unsigned int freq,
+ const u8 *dst, const u8 *src, const u8 *bssid,
+ const u8 *buf, size_t len, unsigned int wait_time,
+ void (*tx_cb)(struct wpa_supplicant *wpa_s,
+ unsigned int freq, const u8 *dst,
+ const u8 *src, const u8 *bssid,
+ const u8 *data, size_t data_len,
+ enum offchannel_send_action_result
+ result),
+ int no_cck)
+{
+ wpa_printf(MSG_DEBUG, "Off-channel: Send action frame: freq=%d dst="
+ MACSTR " src=" MACSTR " bssid=" MACSTR " len=%d",
+ freq, MAC2STR(dst), MAC2STR(src), MAC2STR(bssid),
+ (int) len);
+
+ wpa_s->pending_action_tx_status_cb = tx_cb;
+
+ if (wpa_s->pending_action_tx) {
+ wpa_printf(MSG_DEBUG, "Off-channel: Dropped pending Action "
+ "frame TX to " MACSTR,
+ MAC2STR(wpa_s->pending_action_dst));
+ wpabuf_free(wpa_s->pending_action_tx);
+ }
+ wpa_s->pending_action_tx = wpabuf_alloc(len);
+ if (wpa_s->pending_action_tx == NULL) {
+ wpa_printf(MSG_DEBUG, "Off-channel: Failed to allocate Action "
+ "frame TX buffer (len=%llu)",
+ (unsigned long long) len);
+ return -1;
+ }
+ wpabuf_put_data(wpa_s->pending_action_tx, buf, len);
+ os_memcpy(wpa_s->pending_action_src, src, ETH_ALEN);
+ os_memcpy(wpa_s->pending_action_dst, dst, ETH_ALEN);
+ os_memcpy(wpa_s->pending_action_bssid, bssid, ETH_ALEN);
+ wpa_s->pending_action_freq = freq;
+ wpa_s->pending_action_no_cck = no_cck;
+
+ if (freq != 0 && wpa_s->drv_flags & WPA_DRIVER_FLAGS_OFFCHANNEL_TX) {
+ struct wpa_supplicant *iface;
+
+ iface = wpas_get_tx_interface(wpa_s,
+ wpa_s->pending_action_src);
+ wpa_s->action_tx_wait_time = wait_time;
+
+ return wpa_drv_send_action(
+ iface, wpa_s->pending_action_freq,
+ wait_time, wpa_s->pending_action_dst,
+ wpa_s->pending_action_src, wpa_s->pending_action_bssid,
+ wpabuf_head(wpa_s->pending_action_tx),
+ wpabuf_len(wpa_s->pending_action_tx),
+ wpa_s->pending_action_no_cck);
+ }
+
+ if (freq) {
+ struct wpa_supplicant *tx_iface;
+ tx_iface = wpas_get_tx_interface(wpa_s, src);
+ if (tx_iface->assoc_freq == freq) {
+ wpa_printf(MSG_DEBUG, "Off-channel: Already on "
+ "requested channel (TX interface operating "
+ "channel)");
+ freq = 0;
+ }
+ }
+
+ if (wpa_s->off_channel_freq == freq || freq == 0) {
+ wpa_printf(MSG_DEBUG, "Off-channel: Already on requested "
+ "channel; send Action frame immediately");
+ /* TODO: Would there ever be need to extend the current
+ * duration on the channel? */
+ wpa_s->pending_action_without_roc = 1;
+ eloop_cancel_timeout(wpas_send_action_cb, wpa_s, NULL);
+ eloop_register_timeout(0, 0, wpas_send_action_cb, wpa_s, NULL);
+ return 0;
+ }
+ wpa_s->pending_action_without_roc = 0;
+
+ if (wpa_s->roc_waiting_drv_freq == freq) {
+ wpa_printf(MSG_DEBUG, "Off-channel: Already waiting for "
+ "driver to get to frequency %u MHz; continue "
+ "waiting to send the Action frame", freq);
+ return 0;
+ }
+
+ wpa_printf(MSG_DEBUG, "Off-channel: Schedule Action frame to be "
+ "transmitted once the driver gets to the requested "
+ "channel");
+ if (wait_time > wpa_s->max_remain_on_chan)
+ wait_time = wpa_s->max_remain_on_chan;
+ if (wpa_drv_remain_on_channel(wpa_s, freq, wait_time) < 0) {
+ wpa_printf(MSG_DEBUG, "Off-channel: Failed to request driver "
+ "to remain on channel (%u MHz) for Action "
+ "Frame TX", freq);
+ return -1;
+ }
+ wpa_s->off_channel_freq = 0;
+ wpa_s->roc_waiting_drv_freq = freq;
+
+ return 0;
+}
+
+
+/**
+ * offchannel_send_send_action_done - Notify completion of Action frame sequence
+ * @wpa_s: Pointer to wpa_supplicant data
+ *
+ * This function can be used to cancel a wait for additional response frames on
+ * the channel that was used with offchannel_send_action().
+ */
+void offchannel_send_action_done(struct wpa_supplicant *wpa_s)
+{
+ wpa_printf(MSG_DEBUG, "Off-channel: Action frame sequence done "
+ "notification");
+ wpabuf_free(wpa_s->pending_action_tx);
+ wpa_s->pending_action_tx = NULL;
+ if (wpa_s->drv_flags & WPA_DRIVER_FLAGS_OFFCHANNEL_TX &&
+ wpa_s->action_tx_wait_time)
+ wpa_drv_send_action_cancel_wait(wpa_s);
+
+ if (wpa_s->off_channel_freq || wpa_s->roc_waiting_drv_freq) {
+ wpa_drv_cancel_remain_on_channel(wpa_s);
+ wpa_s->off_channel_freq = 0;
+ wpa_s->roc_waiting_drv_freq = 0;
+ }
+}
+
+
+/**
+ * offchannel_remain_on_channel_cb - Remain-on-channel callback function
+ * @wpa_s: Pointer to wpa_supplicant data
+ * @freq: Frequency (in MHz) of the selected channel
+ * @duration: Duration of the remain-on-channel operation in milliseconds
+ *
+ * This function is called whenever the driver notifies beginning of a
+ * remain-on-channel operation.
+ */
+void offchannel_remain_on_channel_cb(struct wpa_supplicant *wpa_s,
+ unsigned int freq, unsigned int duration)
+{
+ wpa_s->roc_waiting_drv_freq = 0;
+ wpa_s->off_channel_freq = freq;
+ wpas_send_action_cb(wpa_s, NULL);
+}
+
+
+/**
+ * offchannel_cancel_remain_on_channel_cb - Remain-on-channel stopped callback
+ * @wpa_s: Pointer to wpa_supplicant data
+ * @freq: Frequency (in MHz) of the selected channel
+ *
+ * This function is called whenever the driver notifies termination of a
+ * remain-on-channel operation.
+ */
+void offchannel_cancel_remain_on_channel_cb(struct wpa_supplicant *wpa_s,
+ unsigned int freq)
+{
+ wpa_s->off_channel_freq = 0;
+}
+
+
+/**
+ * offchannel_pending_action_tx - Check whether there is a pending Action TX
+ * @wpa_s: Pointer to wpa_supplicant data
+ * Returns: Pointer to pending frame or %NULL if no pending operation
+ *
+ * This function can be used to check whether there is a pending Action frame TX
+ * operation. The returned pointer should be used only for checking whether it
+ * is %NULL (no pending frame) or to print the pointer value in debug
+ * information (i.e., the pointer should not be dereferenced).
+ */
+const void * offchannel_pending_action_tx(struct wpa_supplicant *wpa_s)
+{
+ return wpa_s->pending_action_tx;
+}
+
+
+/**
+ * offchannel_clear_pending_action_tx - Clear pending Action frame TX
+ * @wpa_s: Pointer to wpa_supplicant data
+ */
+void offchannel_clear_pending_action_tx(struct wpa_supplicant *wpa_s)
+{
+ wpabuf_free(wpa_s->pending_action_tx);
+ wpa_s->pending_action_tx = NULL;
+}
+
+
+/**
+ * offchannel_deinit - Deinit off-channel operations
+ * @wpa_s: Pointer to wpa_supplicant data
+ *
+ * This function is used to free up any allocated resources for off-channel
+ * operations.
+ */
+void offchannel_deinit(struct wpa_supplicant *wpa_s)
+{
+ offchannel_clear_pending_action_tx(wpa_s);
+ eloop_cancel_timeout(wpas_send_action_cb, wpa_s, NULL);
+}
diff --git a/contrib/wpa/wpa_supplicant/offchannel.h b/contrib/wpa/wpa_supplicant/offchannel.h
new file mode 100644
index 0000000..0ad7e18
--- /dev/null
+++ b/contrib/wpa/wpa_supplicant/offchannel.h
@@ -0,0 +1,35 @@
+/*
+ * wpa_supplicant - Off-channel Action frame TX/RX
+ * Copyright (c) 2009-2010, Atheros Communications
+ * Copyright (c) 2011, Qualcomm Atheros
+ *
+ * This software may be distributed under the terms of the BSD license.
+ * See README for more details.
+ */
+
+#ifndef OFFCHANNEL_H
+#define OFFCHANNEL_H
+
+int offchannel_send_action(struct wpa_supplicant *wpa_s, unsigned int freq,
+ const u8 *dst, const u8 *src, const u8 *bssid,
+ const u8 *buf, size_t len, unsigned int wait_time,
+ void (*tx_cb)(struct wpa_supplicant *wpa_s,
+ unsigned int freq, const u8 *dst,
+ const u8 *src, const u8 *bssid,
+ const u8 *data, size_t data_len,
+ enum offchannel_send_action_result
+ result),
+ int no_cck);
+void offchannel_send_action_done(struct wpa_supplicant *wpa_s);
+void offchannel_remain_on_channel_cb(struct wpa_supplicant *wpa_s,
+ unsigned int freq, unsigned int duration);
+void offchannel_cancel_remain_on_channel_cb(struct wpa_supplicant *wpa_s,
+ unsigned int freq);
+void offchannel_deinit(struct wpa_supplicant *wpa_s);
+void offchannel_send_action_tx_status(
+ struct wpa_supplicant *wpa_s, const u8 *dst, const u8 *data,
+ size_t data_len, enum offchannel_send_action_result result);
+const void * offchannel_pending_action_tx(struct wpa_supplicant *wpa_s);
+void offchannel_clear_pending_action_tx(struct wpa_supplicant *wpa_s);
+
+#endif /* OFFCHANNEL_H */
diff --git a/contrib/wpa/wpa_supplicant/p2p_supplicant.c b/contrib/wpa/wpa_supplicant/p2p_supplicant.c
new file mode 100644
index 0000000..0a09b00
--- /dev/null
+++ b/contrib/wpa/wpa_supplicant/p2p_supplicant.c
@@ -0,0 +1,5484 @@
+/*
+ * wpa_supplicant - P2P
+ * Copyright (c) 2009-2010, Atheros Communications
+ *
+ * This software may be distributed under the terms of the BSD license.
+ * See README for more details.
+ */
+
+#include "includes.h"
+
+#include "common.h"
+#include "eloop.h"
+#include "common/ieee802_11_common.h"
+#include "common/ieee802_11_defs.h"
+#include "common/wpa_ctrl.h"
+#include "wps/wps_i.h"
+#include "p2p/p2p.h"
+#include "ap/hostapd.h"
+#include "ap/ap_config.h"
+#include "ap/p2p_hostapd.h"
+#include "eapol_supp/eapol_supp_sm.h"
+#include "rsn_supp/wpa.h"
+#include "wpa_supplicant_i.h"
+#include "driver_i.h"
+#include "ap.h"
+#include "config_ssid.h"
+#include "config.h"
+#include "notify.h"
+#include "scan.h"
+#include "bss.h"
+#include "offchannel.h"
+#include "wps_supplicant.h"
+#include "p2p_supplicant.h"
+
+
+/*
+ * How many times to try to scan to find the GO before giving up on join
+ * request.
+ */
+#define P2P_MAX_JOIN_SCAN_ATTEMPTS 10
+
+#define P2P_AUTO_PD_SCAN_ATTEMPTS 5
+
+#ifndef P2P_MAX_CLIENT_IDLE
+/*
+ * How many seconds to try to reconnect to the GO when connection in P2P client
+ * role has been lost.
+ */
+#define P2P_MAX_CLIENT_IDLE 10
+#endif /* P2P_MAX_CLIENT_IDLE */
+
+#ifndef P2P_MAX_INITIAL_CONN_WAIT
+/*
+ * How many seconds to wait for initial 4-way handshake to get completed after
+ * WPS provisioning step.
+ */
+#define P2P_MAX_INITIAL_CONN_WAIT 10
+#endif /* P2P_MAX_INITIAL_CONN_WAIT */
+
+#ifndef P2P_CONCURRENT_SEARCH_DELAY
+#define P2P_CONCURRENT_SEARCH_DELAY 500
+#endif /* P2P_CONCURRENT_SEARCH_DELAY */
+
+enum p2p_group_removal_reason {
+ P2P_GROUP_REMOVAL_UNKNOWN,
+ P2P_GROUP_REMOVAL_SILENT,
+ P2P_GROUP_REMOVAL_FORMATION_FAILED,
+ P2P_GROUP_REMOVAL_REQUESTED,
+ P2P_GROUP_REMOVAL_IDLE_TIMEOUT,
+ P2P_GROUP_REMOVAL_UNAVAILABLE,
+ P2P_GROUP_REMOVAL_GO_ENDING_SESSION
+};
+
+
+static void wpas_p2p_long_listen_timeout(void *eloop_ctx, void *timeout_ctx);
+static struct wpa_supplicant *
+wpas_p2p_get_group_iface(struct wpa_supplicant *wpa_s, int addr_allocated,
+ int go);
+static int wpas_p2p_join_start(struct wpa_supplicant *wpa_s);
+static void wpas_p2p_join_scan_req(struct wpa_supplicant *wpa_s, int freq);
+static void wpas_p2p_join_scan(void *eloop_ctx, void *timeout_ctx);
+static int wpas_p2p_join(struct wpa_supplicant *wpa_s, const u8 *iface_addr,
+ const u8 *dev_addr, enum p2p_wps_method wps_method,
+ int auto_join);
+static int wpas_p2p_create_iface(struct wpa_supplicant *wpa_s);
+static void wpas_p2p_cross_connect_setup(struct wpa_supplicant *wpa_s);
+static void wpas_p2p_group_idle_timeout(void *eloop_ctx, void *timeout_ctx);
+static void wpas_p2p_set_group_idle_timeout(struct wpa_supplicant *wpa_s);
+static void wpas_p2p_fallback_to_go_neg(struct wpa_supplicant *wpa_s,
+ int group_added);
+static int wpas_p2p_stop_find_oper(struct wpa_supplicant *wpa_s);
+
+
+static void wpas_p2p_scan_res_handler(struct wpa_supplicant *wpa_s,
+ struct wpa_scan_results *scan_res)
+{
+ size_t i;
+
+ if (wpa_s->global->p2p_disabled || wpa_s->global->p2p == NULL)
+ return;
+
+ wpa_printf(MSG_DEBUG, "P2P: Scan results received (%d BSS)",
+ (int) scan_res->num);
+
+ for (i = 0; i < scan_res->num; i++) {
+ struct wpa_scan_res *bss = scan_res->res[i];
+ if (p2p_scan_res_handler(wpa_s->global->p2p, bss->bssid,
+ bss->freq, bss->age, bss->level,
+ (const u8 *) (bss + 1),
+ bss->ie_len) > 0)
+ break;
+ }
+
+ p2p_scan_res_handled(wpa_s->global->p2p);
+}
+
+
+static int wpas_p2p_scan(void *ctx, enum p2p_scan_type type, int freq,
+ unsigned int num_req_dev_types,
+ const u8 *req_dev_types, const u8 *dev_id, u16 pw_id)
+{
+ struct wpa_supplicant *wpa_s = ctx;
+ struct wpa_supplicant *ifs;
+ struct wpa_driver_scan_params params;
+ int ret;
+ struct wpabuf *wps_ie, *ies;
+ int social_channels[] = { 2412, 2437, 2462, 0, 0 };
+ size_t ielen;
+
+ if (wpa_s->global->p2p_disabled || wpa_s->global->p2p == NULL)
+ return -1;
+
+ for (ifs = wpa_s->global->ifaces; ifs; ifs = ifs->next) {
+ if (ifs->sta_scan_pending &&
+ wpas_p2p_in_progress(wpa_s) == 2) {
+ wpa_printf(MSG_DEBUG, "Delaying P2P scan to allow "
+ "pending station mode scan to be "
+ "completed on interface %s", ifs->ifname);
+ wpa_s->global->p2p_cb_on_scan_complete = 1;
+ wpa_supplicant_req_scan(ifs, 0, 0);
+ return 1;
+ }
+ }
+
+ os_memset(&params, 0, sizeof(params));
+
+ /* P2P Wildcard SSID */
+ params.num_ssids = 1;
+ params.ssids[0].ssid = (u8 *) P2P_WILDCARD_SSID;
+ params.ssids[0].ssid_len = P2P_WILDCARD_SSID_LEN;
+
+ wpa_s->wps->dev.p2p = 1;
+ wps_ie = wps_build_probe_req_ie(pw_id, &wpa_s->wps->dev,
+ wpa_s->wps->uuid, WPS_REQ_ENROLLEE,
+ num_req_dev_types, req_dev_types);
+ if (wps_ie == NULL)
+ return -1;
+
+ ielen = p2p_scan_ie_buf_len(wpa_s->global->p2p);
+ ies = wpabuf_alloc(wpabuf_len(wps_ie) + ielen);
+ if (ies == NULL) {
+ wpabuf_free(wps_ie);
+ return -1;
+ }
+ wpabuf_put_buf(ies, wps_ie);
+ wpabuf_free(wps_ie);
+
+ p2p_scan_ie(wpa_s->global->p2p, ies, dev_id);
+
+ params.p2p_probe = 1;
+ params.extra_ies = wpabuf_head(ies);
+ params.extra_ies_len = wpabuf_len(ies);
+
+ switch (type) {
+ case P2P_SCAN_SOCIAL:
+ params.freqs = social_channels;
+ break;
+ case P2P_SCAN_FULL:
+ break;
+ case P2P_SCAN_SOCIAL_PLUS_ONE:
+ social_channels[3] = freq;
+ params.freqs = social_channels;
+ break;
+ }
+
+ ret = wpa_drv_scan(wpa_s, &params);
+
+ wpabuf_free(ies);
+
+ if (ret) {
+ for (ifs = wpa_s->global->ifaces; ifs; ifs = ifs->next) {
+ if (ifs->scanning ||
+ ifs->scan_res_handler == wpas_p2p_scan_res_handler) {
+ wpa_s->global->p2p_cb_on_scan_complete = 1;
+ ret = 1;
+ break;
+ }
+ }
+ } else
+ wpa_s->scan_res_handler = wpas_p2p_scan_res_handler;
+
+ return ret;
+}
+
+
+static enum wpa_driver_if_type wpas_p2p_if_type(int p2p_group_interface)
+{
+ switch (p2p_group_interface) {
+ case P2P_GROUP_INTERFACE_PENDING:
+ return WPA_IF_P2P_GROUP;
+ case P2P_GROUP_INTERFACE_GO:
+ return WPA_IF_P2P_GO;
+ case P2P_GROUP_INTERFACE_CLIENT:
+ return WPA_IF_P2P_CLIENT;
+ }
+
+ return WPA_IF_P2P_GROUP;
+}
+
+
+static struct wpa_supplicant * wpas_get_p2p_group(struct wpa_supplicant *wpa_s,
+ const u8 *ssid,
+ size_t ssid_len, int *go)
+{
+ struct wpa_ssid *s;
+
+ for (wpa_s = wpa_s->global->ifaces; wpa_s; wpa_s = wpa_s->next) {
+ for (s = wpa_s->conf->ssid; s; s = s->next) {
+ if (s->disabled != 0 || !s->p2p_group ||
+ s->ssid_len != ssid_len ||
+ os_memcmp(ssid, s->ssid, ssid_len) != 0)
+ continue;
+ if (s->mode == WPAS_MODE_P2P_GO &&
+ s != wpa_s->current_ssid)
+ continue;
+ if (go)
+ *go = s->mode == WPAS_MODE_P2P_GO;
+ return wpa_s;
+ }
+ }
+
+ return NULL;
+}
+
+
+static int wpas_p2p_group_delete(struct wpa_supplicant *wpa_s,
+ enum p2p_group_removal_reason removal_reason)
+{
+ struct wpa_ssid *ssid;
+ char *gtype;
+ const char *reason;
+
+ ssid = wpa_s->current_ssid;
+ if (ssid == NULL) {
+ /*
+ * The current SSID was not known, but there may still be a
+ * pending P2P group interface waiting for provisioning or a
+ * P2P group that is trying to reconnect.
+ */
+ ssid = wpa_s->conf->ssid;
+ while (ssid) {
+ if (ssid->p2p_group && ssid->disabled != 2)
+ break;
+ ssid = ssid->next;
+ }
+ if (ssid == NULL &&
+ wpa_s->p2p_group_interface == NOT_P2P_GROUP_INTERFACE)
+ {
+ wpa_printf(MSG_ERROR, "P2P: P2P group interface "
+ "not found");
+ return -1;
+ }
+ }
+ if (wpa_s->p2p_group_interface == P2P_GROUP_INTERFACE_GO)
+ gtype = "GO";
+ else if (wpa_s->p2p_group_interface == P2P_GROUP_INTERFACE_CLIENT ||
+ (ssid && ssid->mode == WPAS_MODE_INFRA)) {
+ wpa_s->reassociate = 0;
+ wpa_s->disconnected = 1;
+ wpa_supplicant_deauthenticate(wpa_s,
+ WLAN_REASON_DEAUTH_LEAVING);
+ gtype = "client";
+ } else
+ gtype = "GO";
+ if (wpa_s->cross_connect_in_use) {
+ wpa_s->cross_connect_in_use = 0;
+ wpa_msg(wpa_s->parent, MSG_INFO,
+ P2P_EVENT_CROSS_CONNECT_DISABLE "%s %s",
+ wpa_s->ifname, wpa_s->cross_connect_uplink);
+ }
+ switch (removal_reason) {
+ case P2P_GROUP_REMOVAL_REQUESTED:
+ reason = " reason=REQUESTED";
+ break;
+ case P2P_GROUP_REMOVAL_FORMATION_FAILED:
+ reason = " reason=FORMATION_FAILED";
+ break;
+ case P2P_GROUP_REMOVAL_IDLE_TIMEOUT:
+ reason = " reason=IDLE";
+ break;
+ case P2P_GROUP_REMOVAL_UNAVAILABLE:
+ reason = " reason=UNAVAILABLE";
+ break;
+ case P2P_GROUP_REMOVAL_GO_ENDING_SESSION:
+ reason = " reason=GO_ENDING_SESSION";
+ break;
+ default:
+ reason = "";
+ break;
+ }
+ if (removal_reason != P2P_GROUP_REMOVAL_SILENT) {
+ wpa_msg(wpa_s->parent, MSG_INFO,
+ P2P_EVENT_GROUP_REMOVED "%s %s%s",
+ wpa_s->ifname, gtype, reason);
+ }
+
+ if (eloop_cancel_timeout(wpas_p2p_group_idle_timeout, wpa_s, NULL) > 0)
+ wpa_printf(MSG_DEBUG, "P2P: Cancelled P2P group idle timeout");
+
+ if (removal_reason != P2P_GROUP_REMOVAL_SILENT && ssid)
+ wpas_notify_p2p_group_removed(wpa_s, ssid, gtype);
+
+ if (wpa_s->p2p_group_interface != NOT_P2P_GROUP_INTERFACE) {
+ struct wpa_global *global;
+ char *ifname;
+ enum wpa_driver_if_type type;
+ wpa_printf(MSG_DEBUG, "P2P: Remove group interface %s",
+ wpa_s->ifname);
+ global = wpa_s->global;
+ ifname = os_strdup(wpa_s->ifname);
+ type = wpas_p2p_if_type(wpa_s->p2p_group_interface);
+ wpa_supplicant_remove_iface(wpa_s->global, wpa_s, 0);
+ wpa_s = global->ifaces;
+ if (wpa_s && ifname)
+ wpa_drv_if_remove(wpa_s, type, ifname);
+ os_free(ifname);
+ return 1;
+ }
+
+ wpa_printf(MSG_DEBUG, "P2P: Remove temporary group network");
+ if (ssid && (ssid->p2p_group ||
+ ssid->mode == WPAS_MODE_P2P_GROUP_FORMATION ||
+ (ssid->key_mgmt & WPA_KEY_MGMT_WPS))) {
+ int id = ssid->id;
+ if (ssid == wpa_s->current_ssid) {
+ wpa_sm_set_config(wpa_s->wpa, NULL);
+ eapol_sm_notify_config(wpa_s->eapol, NULL, NULL);
+ wpa_s->current_ssid = NULL;
+ }
+ /*
+ * Networks objects created during any P2P activities are not
+ * exposed out as they might/will confuse certain non-P2P aware
+ * applications since these network objects won't behave like
+ * regular ones.
+ *
+ * Likewise, we don't send out network removed signals for such
+ * network objects.
+ */
+ wpa_config_remove_network(wpa_s->conf, id);
+ wpa_supplicant_clear_status(wpa_s);
+ wpa_supplicant_cancel_sched_scan(wpa_s);
+ wpa_s->sta_scan_pending = 0;
+ } else {
+ wpa_printf(MSG_DEBUG, "P2P: Temporary group network not "
+ "found");
+ }
+ if (wpa_s->ap_iface)
+ wpa_supplicant_ap_deinit(wpa_s);
+ else
+ wpa_drv_deinit_p2p_cli(wpa_s);
+
+ return 0;
+}
+
+
+static int wpas_p2p_persistent_group(struct wpa_supplicant *wpa_s,
+ u8 *go_dev_addr,
+ const u8 *ssid, size_t ssid_len)
+{
+ struct wpa_bss *bss;
+ const u8 *bssid;
+ struct wpabuf *p2p;
+ u8 group_capab;
+ const u8 *addr;
+
+ if (wpa_s->go_params)
+ bssid = wpa_s->go_params->peer_interface_addr;
+ else
+ bssid = wpa_s->bssid;
+
+ bss = wpa_bss_get(wpa_s, bssid, ssid, ssid_len);
+ if (bss == NULL) {
+ u8 iface_addr[ETH_ALEN];
+ if (p2p_get_interface_addr(wpa_s->global->p2p, bssid,
+ iface_addr) == 0)
+ bss = wpa_bss_get(wpa_s, iface_addr, ssid, ssid_len);
+ }
+ if (bss == NULL) {
+ wpa_printf(MSG_DEBUG, "P2P: Could not figure out whether "
+ "group is persistent - BSS " MACSTR " not found",
+ MAC2STR(bssid));
+ return 0;
+ }
+
+ p2p = wpa_bss_get_vendor_ie_multi(bss, P2P_IE_VENDOR_TYPE);
+ if (p2p == NULL) {
+ wpa_printf(MSG_DEBUG, "P2P: Could not figure out whether "
+ "group is persistent - BSS " MACSTR
+ " did not include P2P IE", MAC2STR(bssid));
+ wpa_hexdump(MSG_DEBUG, "P2P: Probe Response IEs",
+ (u8 *) (bss + 1), bss->ie_len);
+ wpa_hexdump(MSG_DEBUG, "P2P: Beacon IEs",
+ ((u8 *) bss + 1) + bss->ie_len,
+ bss->beacon_ie_len);
+ return 0;
+ }
+
+ group_capab = p2p_get_group_capab(p2p);
+ addr = p2p_get_go_dev_addr(p2p);
+ wpa_printf(MSG_DEBUG, "P2P: Checking whether group is persistent: "
+ "group_capab=0x%x", group_capab);
+ if (addr) {
+ os_memcpy(go_dev_addr, addr, ETH_ALEN);
+ wpa_printf(MSG_DEBUG, "P2P: GO Device Address " MACSTR,
+ MAC2STR(addr));
+ } else
+ os_memset(go_dev_addr, 0, ETH_ALEN);
+ wpabuf_free(p2p);
+
+ wpa_printf(MSG_DEBUG, "P2P: BSS " MACSTR " group_capab=0x%x "
+ "go_dev_addr=" MACSTR,
+ MAC2STR(bssid), group_capab, MAC2STR(go_dev_addr));
+
+ return group_capab & P2P_GROUP_CAPAB_PERSISTENT_GROUP;
+}
+
+
+static int wpas_p2p_store_persistent_group(struct wpa_supplicant *wpa_s,
+ struct wpa_ssid *ssid,
+ const u8 *go_dev_addr)
+{
+ struct wpa_ssid *s;
+ int changed = 0;
+
+ wpa_printf(MSG_DEBUG, "P2P: Storing credentials for a persistent "
+ "group (GO Dev Addr " MACSTR ")", MAC2STR(go_dev_addr));
+ for (s = wpa_s->conf->ssid; s; s = s->next) {
+ if (s->disabled == 2 &&
+ os_memcmp(go_dev_addr, s->bssid, ETH_ALEN) == 0 &&
+ s->ssid_len == ssid->ssid_len &&
+ os_memcmp(ssid->ssid, s->ssid, ssid->ssid_len) == 0)
+ break;
+ }
+
+ if (s) {
+ wpa_printf(MSG_DEBUG, "P2P: Update existing persistent group "
+ "entry");
+ if (ssid->passphrase && !s->passphrase)
+ changed = 1;
+ else if (ssid->passphrase && s->passphrase &&
+ os_strcmp(ssid->passphrase, s->passphrase) != 0)
+ changed = 1;
+ } else {
+ wpa_printf(MSG_DEBUG, "P2P: Create a new persistent group "
+ "entry");
+ changed = 1;
+ s = wpa_config_add_network(wpa_s->conf);
+ if (s == NULL)
+ return -1;
+
+ /*
+ * Instead of network_added we emit persistent_group_added
+ * notification. Also to keep the defense checks in
+ * persistent_group obj registration method, we set the
+ * relevant flags in s to designate it as a persistent group.
+ */
+ s->p2p_group = 1;
+ s->p2p_persistent_group = 1;
+ wpas_notify_persistent_group_added(wpa_s, s);
+ wpa_config_set_network_defaults(s);
+ }
+
+ s->p2p_group = 1;
+ s->p2p_persistent_group = 1;
+ s->disabled = 2;
+ s->bssid_set = 1;
+ os_memcpy(s->bssid, go_dev_addr, ETH_ALEN);
+ s->mode = ssid->mode;
+ s->auth_alg = WPA_AUTH_ALG_OPEN;
+ s->key_mgmt = WPA_KEY_MGMT_PSK;
+ s->proto = WPA_PROTO_RSN;
+ s->pairwise_cipher = WPA_CIPHER_CCMP;
+ s->export_keys = 1;
+ if (ssid->passphrase) {
+ os_free(s->passphrase);
+ s->passphrase = os_strdup(ssid->passphrase);
+ }
+ if (ssid->psk_set) {
+ s->psk_set = 1;
+ os_memcpy(s->psk, ssid->psk, 32);
+ }
+ if (s->passphrase && !s->psk_set)
+ wpa_config_update_psk(s);
+ if (s->ssid == NULL || s->ssid_len < ssid->ssid_len) {
+ os_free(s->ssid);
+ s->ssid = os_malloc(ssid->ssid_len);
+ }
+ if (s->ssid) {
+ s->ssid_len = ssid->ssid_len;
+ os_memcpy(s->ssid, ssid->ssid, s->ssid_len);
+ }
+
+#ifndef CONFIG_NO_CONFIG_WRITE
+ if (changed && wpa_s->conf->update_config &&
+ wpa_config_write(wpa_s->confname, wpa_s->conf)) {
+ wpa_printf(MSG_DEBUG, "P2P: Failed to update configuration");
+ }
+#endif /* CONFIG_NO_CONFIG_WRITE */
+
+ return s->id;
+}
+
+
+static void wpas_p2p_add_persistent_group_client(struct wpa_supplicant *wpa_s,
+ const u8 *addr)
+{
+ struct wpa_ssid *ssid, *s;
+ u8 *n;
+ size_t i;
+ int found = 0;
+
+ ssid = wpa_s->current_ssid;
+ if (ssid == NULL || ssid->mode != WPAS_MODE_P2P_GO ||
+ !ssid->p2p_persistent_group)
+ return;
+
+ for (s = wpa_s->parent->conf->ssid; s; s = s->next) {
+ if (s->disabled != 2 || s->mode != WPAS_MODE_P2P_GO)
+ continue;
+
+ if (s->ssid_len == ssid->ssid_len &&
+ os_memcmp(s->ssid, ssid->ssid, s->ssid_len) == 0)
+ break;
+ }
+
+ if (s == NULL)
+ return;
+
+ for (i = 0; s->p2p_client_list && i < s->num_p2p_clients; i++) {
+ if (os_memcmp(s->p2p_client_list + i * ETH_ALEN, addr,
+ ETH_ALEN) != 0)
+ continue;
+
+ if (i == s->num_p2p_clients - 1)
+ return; /* already the most recent entry */
+
+ /* move the entry to mark it most recent */
+ os_memmove(s->p2p_client_list + i * ETH_ALEN,
+ s->p2p_client_list + (i + 1) * ETH_ALEN,
+ (s->num_p2p_clients - i - 1) * ETH_ALEN);
+ os_memcpy(s->p2p_client_list +
+ (s->num_p2p_clients - 1) * ETH_ALEN, addr, ETH_ALEN);
+ found = 1;
+ break;
+ }
+
+ if (!found && s->num_p2p_clients < P2P_MAX_STORED_CLIENTS) {
+ n = os_realloc_array(s->p2p_client_list,
+ s->num_p2p_clients + 1, ETH_ALEN);
+ if (n == NULL)
+ return;
+ os_memcpy(n + s->num_p2p_clients * ETH_ALEN, addr, ETH_ALEN);
+ s->p2p_client_list = n;
+ s->num_p2p_clients++;
+ } else if (!found) {
+ /* Not enough room for an additional entry - drop the oldest
+ * entry */
+ os_memmove(s->p2p_client_list,
+ s->p2p_client_list + ETH_ALEN,
+ (s->num_p2p_clients - 1) * ETH_ALEN);
+ os_memcpy(s->p2p_client_list +
+ (s->num_p2p_clients - 1) * ETH_ALEN,
+ addr, ETH_ALEN);
+ }
+
+#ifndef CONFIG_NO_CONFIG_WRITE
+ if (wpa_s->parent->conf->update_config &&
+ wpa_config_write(wpa_s->parent->confname, wpa_s->parent->conf))
+ wpa_printf(MSG_DEBUG, "P2P: Failed to update configuration");
+#endif /* CONFIG_NO_CONFIG_WRITE */
+}
+
+
+static void wpas_group_formation_completed(struct wpa_supplicant *wpa_s,
+ int success)
+{
+ struct wpa_ssid *ssid;
+ const char *ssid_txt;
+ int client;
+ int persistent;
+ u8 go_dev_addr[ETH_ALEN];
+ int network_id = -1;
+
+ /*
+ * This callback is likely called for the main interface. Update wpa_s
+ * to use the group interface if a new interface was created for the
+ * group.
+ */
+ if (wpa_s->global->p2p_group_formation)
+ wpa_s = wpa_s->global->p2p_group_formation;
+ wpa_s->global->p2p_group_formation = NULL;
+ wpa_s->p2p_in_provisioning = 0;
+
+ if (!success) {
+ wpa_msg(wpa_s->parent, MSG_INFO,
+ P2P_EVENT_GROUP_FORMATION_FAILURE);
+ wpas_p2p_group_delete(wpa_s,
+ P2P_GROUP_REMOVAL_FORMATION_FAILED);
+ return;
+ }
+
+ wpa_msg(wpa_s->parent, MSG_INFO, P2P_EVENT_GROUP_FORMATION_SUCCESS);
+
+ ssid = wpa_s->current_ssid;
+ if (ssid && ssid->mode == WPAS_MODE_P2P_GROUP_FORMATION) {
+ ssid->mode = WPAS_MODE_P2P_GO;
+ p2p_group_notif_formation_done(wpa_s->p2p_group);
+ wpa_supplicant_ap_mac_addr_filter(wpa_s, NULL);
+ }
+
+ persistent = 0;
+ if (ssid) {
+ ssid_txt = wpa_ssid_txt(ssid->ssid, ssid->ssid_len);
+ client = ssid->mode == WPAS_MODE_INFRA;
+ if (ssid->mode == WPAS_MODE_P2P_GO) {
+ persistent = ssid->p2p_persistent_group;
+ os_memcpy(go_dev_addr, wpa_s->global->p2p_dev_addr,
+ ETH_ALEN);
+ } else
+ persistent = wpas_p2p_persistent_group(wpa_s,
+ go_dev_addr,
+ ssid->ssid,
+ ssid->ssid_len);
+ } else {
+ ssid_txt = "";
+ client = wpa_s->p2p_group_interface ==
+ P2P_GROUP_INTERFACE_CLIENT;
+ os_memset(go_dev_addr, 0, ETH_ALEN);
+ }
+
+ wpa_s->show_group_started = 0;
+ if (client) {
+ /*
+ * Indicate event only after successfully completed 4-way
+ * handshake, i.e., when the interface is ready for data
+ * packets.
+ */
+ wpa_s->show_group_started = 1;
+ } else if (ssid && ssid->passphrase == NULL && ssid->psk_set) {
+ char psk[65];
+ wpa_snprintf_hex(psk, sizeof(psk), ssid->psk, 32);
+ wpa_msg(wpa_s->parent, MSG_INFO, P2P_EVENT_GROUP_STARTED
+ "%s GO ssid=\"%s\" freq=%d psk=%s go_dev_addr=" MACSTR
+ "%s",
+ wpa_s->ifname, ssid_txt, ssid->frequency, psk,
+ MAC2STR(go_dev_addr),
+ persistent ? " [PERSISTENT]" : "");
+ wpas_p2p_cross_connect_setup(wpa_s);
+ wpas_p2p_set_group_idle_timeout(wpa_s);
+ } else {
+ wpa_msg(wpa_s->parent, MSG_INFO, P2P_EVENT_GROUP_STARTED
+ "%s GO ssid=\"%s\" freq=%d passphrase=\"%s\" "
+ "go_dev_addr=" MACSTR "%s",
+ wpa_s->ifname, ssid_txt, ssid ? ssid->frequency : 0,
+ ssid && ssid->passphrase ? ssid->passphrase : "",
+ MAC2STR(go_dev_addr),
+ persistent ? " [PERSISTENT]" : "");
+ wpas_p2p_cross_connect_setup(wpa_s);
+ wpas_p2p_set_group_idle_timeout(wpa_s);
+ }
+
+ if (persistent)
+ network_id = wpas_p2p_store_persistent_group(wpa_s->parent,
+ ssid, go_dev_addr);
+ if (network_id < 0 && ssid)
+ network_id = ssid->id;
+ if (!client)
+ wpas_notify_p2p_group_started(wpa_s, ssid, network_id, 0);
+}
+
+
+static void wpas_p2p_send_action_tx_status(struct wpa_supplicant *wpa_s,
+ unsigned int freq,
+ const u8 *dst, const u8 *src,
+ const u8 *bssid,
+ const u8 *data, size_t data_len,
+ enum offchannel_send_action_result
+ result)
+{
+ enum p2p_send_action_result res = P2P_SEND_ACTION_SUCCESS;
+
+ if (wpa_s->global->p2p == NULL || wpa_s->global->p2p_disabled)
+ return;
+ if (wpa_s->drv_flags & WPA_DRIVER_FLAGS_P2P_MGMT)
+ return;
+
+ switch (result) {
+ case OFFCHANNEL_SEND_ACTION_SUCCESS:
+ res = P2P_SEND_ACTION_SUCCESS;
+ break;
+ case OFFCHANNEL_SEND_ACTION_NO_ACK:
+ res = P2P_SEND_ACTION_NO_ACK;
+ break;
+ case OFFCHANNEL_SEND_ACTION_FAILED:
+ res = P2P_SEND_ACTION_FAILED;
+ break;
+ }
+
+ p2p_send_action_cb(wpa_s->global->p2p, freq, dst, src, bssid, res);
+
+ if (result != OFFCHANNEL_SEND_ACTION_SUCCESS &&
+ wpa_s->pending_pd_before_join &&
+ (os_memcmp(dst, wpa_s->pending_join_dev_addr, ETH_ALEN) == 0 ||
+ os_memcmp(dst, wpa_s->pending_join_iface_addr, ETH_ALEN) == 0) &&
+ wpa_s->p2p_fallback_to_go_neg) {
+ wpa_s->pending_pd_before_join = 0;
+ wpa_dbg(wpa_s, MSG_DEBUG, "P2P: No ACK for PD Req "
+ "during p2p_connect-auto");
+ wpas_p2p_fallback_to_go_neg(wpa_s, 0);
+ return;
+ }
+}
+
+
+static int wpas_send_action(void *ctx, unsigned int freq, const u8 *dst,
+ const u8 *src, const u8 *bssid, const u8 *buf,
+ size_t len, unsigned int wait_time)
+{
+ struct wpa_supplicant *wpa_s = ctx;
+ return offchannel_send_action(wpa_s, freq, dst, src, bssid, buf, len,
+ wait_time,
+ wpas_p2p_send_action_tx_status, 1);
+}
+
+
+static void wpas_send_action_done(void *ctx)
+{
+ struct wpa_supplicant *wpa_s = ctx;
+ offchannel_send_action_done(wpa_s);
+}
+
+
+static int wpas_copy_go_neg_results(struct wpa_supplicant *wpa_s,
+ struct p2p_go_neg_results *params)
+{
+ if (wpa_s->go_params == NULL) {
+ wpa_s->go_params = os_malloc(sizeof(*params));
+ if (wpa_s->go_params == NULL)
+ return -1;
+ }
+ os_memcpy(wpa_s->go_params, params, sizeof(*params));
+ return 0;
+}
+
+
+static void wpas_start_wps_enrollee(struct wpa_supplicant *wpa_s,
+ struct p2p_go_neg_results *res)
+{
+ wpa_printf(MSG_DEBUG, "P2P: Start WPS Enrollee for peer " MACSTR,
+ MAC2STR(res->peer_interface_addr));
+ wpa_hexdump_ascii(MSG_DEBUG, "P2P: Start WPS Enrollee for SSID",
+ res->ssid, res->ssid_len);
+ wpa_supplicant_ap_deinit(wpa_s);
+ wpas_copy_go_neg_results(wpa_s, res);
+ if (res->wps_method == WPS_PBC)
+ wpas_wps_start_pbc(wpa_s, res->peer_interface_addr, 1);
+ else {
+ u16 dev_pw_id = DEV_PW_DEFAULT;
+ if (wpa_s->p2p_wps_method == WPS_PIN_KEYPAD)
+ dev_pw_id = DEV_PW_REGISTRAR_SPECIFIED;
+ wpas_wps_start_pin(wpa_s, res->peer_interface_addr,
+ wpa_s->p2p_pin, 1, dev_pw_id);
+ }
+}
+
+
+static void p2p_go_configured(void *ctx, void *data)
+{
+ struct wpa_supplicant *wpa_s = ctx;
+ struct p2p_go_neg_results *params = data;
+ struct wpa_ssid *ssid;
+ int network_id = -1;
+
+ ssid = wpa_s->current_ssid;
+ if (ssid && ssid->mode == WPAS_MODE_P2P_GO) {
+ wpa_printf(MSG_DEBUG, "P2P: Group setup without provisioning");
+ if (wpa_s->global->p2p_group_formation == wpa_s)
+ wpa_s->global->p2p_group_formation = NULL;
+ if (os_strlen(params->passphrase) > 0) {
+ wpa_msg(wpa_s->parent, MSG_INFO, P2P_EVENT_GROUP_STARTED
+ "%s GO ssid=\"%s\" freq=%d passphrase=\"%s\" "
+ "go_dev_addr=" MACSTR "%s", wpa_s->ifname,
+ wpa_ssid_txt(ssid->ssid, ssid->ssid_len),
+ ssid->frequency, params->passphrase,
+ MAC2STR(wpa_s->global->p2p_dev_addr),
+ params->persistent_group ? " [PERSISTENT]" :
+ "");
+ } else {
+ char psk[65];
+ wpa_snprintf_hex(psk, sizeof(psk), params->psk,
+ sizeof(params->psk));
+ wpa_msg(wpa_s->parent, MSG_INFO, P2P_EVENT_GROUP_STARTED
+ "%s GO ssid=\"%s\" freq=%d psk=%s "
+ "go_dev_addr=" MACSTR "%s", wpa_s->ifname,
+ wpa_ssid_txt(ssid->ssid, ssid->ssid_len),
+ ssid->frequency, psk,
+ MAC2STR(wpa_s->global->p2p_dev_addr),
+ params->persistent_group ? " [PERSISTENT]" :
+ "");
+ }
+
+ if (params->persistent_group)
+ network_id = wpas_p2p_store_persistent_group(
+ wpa_s->parent, ssid,
+ wpa_s->global->p2p_dev_addr);
+ if (network_id < 0)
+ network_id = ssid->id;
+ wpas_notify_p2p_group_started(wpa_s, ssid, network_id, 0);
+ wpas_p2p_cross_connect_setup(wpa_s);
+ wpas_p2p_set_group_idle_timeout(wpa_s);
+ return;
+ }
+
+ wpa_printf(MSG_DEBUG, "P2P: Setting up WPS for GO provisioning");
+ if (wpa_supplicant_ap_mac_addr_filter(wpa_s,
+ params->peer_interface_addr)) {
+ wpa_printf(MSG_DEBUG, "P2P: Failed to setup MAC address "
+ "filtering");
+ return;
+ }
+ if (params->wps_method == WPS_PBC)
+ wpa_supplicant_ap_wps_pbc(wpa_s, params->peer_interface_addr,
+ params->peer_device_addr);
+ else if (wpa_s->p2p_pin[0])
+ wpa_supplicant_ap_wps_pin(wpa_s, params->peer_interface_addr,
+ wpa_s->p2p_pin, NULL, 0, 0);
+ os_free(wpa_s->go_params);
+ wpa_s->go_params = NULL;
+}
+
+
+static void wpas_start_wps_go(struct wpa_supplicant *wpa_s,
+ struct p2p_go_neg_results *params,
+ int group_formation)
+{
+ struct wpa_ssid *ssid;
+
+ wpa_dbg(wpa_s, MSG_DEBUG, "P2P: Starting GO");
+ if (wpas_copy_go_neg_results(wpa_s, params) < 0) {
+ wpa_dbg(wpa_s, MSG_DEBUG, "P2P: Could not copy GO Negotiation "
+ "results");
+ return;
+ }
+
+ ssid = wpa_config_add_network(wpa_s->conf);
+ if (ssid == NULL) {
+ wpa_dbg(wpa_s, MSG_DEBUG, "P2P: Could not add network for GO");
+ return;
+ }
+
+ wpa_s->show_group_started = 0;
+
+ wpa_config_set_network_defaults(ssid);
+ ssid->temporary = 1;
+ ssid->p2p_group = 1;
+ ssid->p2p_persistent_group = params->persistent_group;
+ ssid->mode = group_formation ? WPAS_MODE_P2P_GROUP_FORMATION :
+ WPAS_MODE_P2P_GO;
+ ssid->frequency = params->freq;
+ ssid->ht40 = params->ht40;
+ ssid->ssid = os_zalloc(params->ssid_len + 1);
+ if (ssid->ssid) {
+ os_memcpy(ssid->ssid, params->ssid, params->ssid_len);
+ ssid->ssid_len = params->ssid_len;
+ }
+ ssid->auth_alg = WPA_AUTH_ALG_OPEN;
+ ssid->key_mgmt = WPA_KEY_MGMT_PSK;
+ ssid->proto = WPA_PROTO_RSN;
+ ssid->pairwise_cipher = WPA_CIPHER_CCMP;
+ if (os_strlen(params->passphrase) > 0) {
+ ssid->passphrase = os_strdup(params->passphrase);
+ if (ssid->passphrase == NULL) {
+ wpa_msg(wpa_s, MSG_ERROR, "P2P: Failed to copy "
+ "passphrase for GO");
+ wpa_config_remove_network(wpa_s->conf, ssid->id);
+ return;
+ }
+ } else
+ ssid->passphrase = NULL;
+ ssid->psk_set = params->psk_set;
+ if (ssid->psk_set)
+ os_memcpy(ssid->psk, params->psk, sizeof(ssid->psk));
+ else if (ssid->passphrase)
+ wpa_config_update_psk(ssid);
+ ssid->ap_max_inactivity = wpa_s->parent->conf->p2p_go_max_inactivity;
+
+ wpa_s->ap_configured_cb = p2p_go_configured;
+ wpa_s->ap_configured_cb_ctx = wpa_s;
+ wpa_s->ap_configured_cb_data = wpa_s->go_params;
+ wpa_s->connect_without_scan = ssid;
+ wpa_s->reassociate = 1;
+ wpa_s->disconnected = 0;
+ wpa_dbg(wpa_s, MSG_DEBUG, "P2P: Request scan (that will be skipped) to "
+ "start GO)");
+ wpa_supplicant_req_scan(wpa_s, 0, 0);
+}
+
+
+static void wpas_p2p_clone_config(struct wpa_supplicant *dst,
+ const struct wpa_supplicant *src)
+{
+ struct wpa_config *d;
+ const struct wpa_config *s;
+
+ d = dst->conf;
+ s = src->conf;
+
+#define C(n) if (s->n) d->n = os_strdup(s->n)
+ C(device_name);
+ C(manufacturer);
+ C(model_name);
+ C(model_number);
+ C(serial_number);
+ C(config_methods);
+#undef C
+
+ os_memcpy(d->device_type, s->device_type, WPS_DEV_TYPE_LEN);
+ os_memcpy(d->sec_device_type, s->sec_device_type,
+ sizeof(d->sec_device_type));
+ d->num_sec_device_types = s->num_sec_device_types;
+
+ d->p2p_group_idle = s->p2p_group_idle;
+ d->p2p_intra_bss = s->p2p_intra_bss;
+ d->persistent_reconnect = s->persistent_reconnect;
+ d->max_num_sta = s->max_num_sta;
+ d->pbc_in_m1 = s->pbc_in_m1;
+}
+
+
+static int wpas_p2p_add_group_interface(struct wpa_supplicant *wpa_s,
+ enum wpa_driver_if_type type)
+{
+ char ifname[120], force_ifname[120];
+
+ if (wpa_s->pending_interface_name[0]) {
+ wpa_printf(MSG_DEBUG, "P2P: Pending virtual interface exists "
+ "- skip creation of a new one");
+ if (is_zero_ether_addr(wpa_s->pending_interface_addr)) {
+ wpa_printf(MSG_DEBUG, "P2P: Pending virtual address "
+ "unknown?! ifname='%s'",
+ wpa_s->pending_interface_name);
+ return -1;
+ }
+ return 0;
+ }
+
+ os_snprintf(ifname, sizeof(ifname), "p2p-%s-%d", wpa_s->ifname,
+ wpa_s->p2p_group_idx);
+ if (os_strlen(ifname) >= IFNAMSIZ &&
+ os_strlen(wpa_s->ifname) < IFNAMSIZ) {
+ /* Try to avoid going over the IFNAMSIZ length limit */
+ os_snprintf(ifname, sizeof(ifname), "p2p-%d",
+ wpa_s->p2p_group_idx);
+ }
+ force_ifname[0] = '\0';
+
+ wpa_printf(MSG_DEBUG, "P2P: Create a new interface %s for the group",
+ ifname);
+ wpa_s->p2p_group_idx++;
+
+ wpa_s->pending_interface_type = type;
+ if (wpa_drv_if_add(wpa_s, type, ifname, NULL, NULL, force_ifname,
+ wpa_s->pending_interface_addr, NULL) < 0) {
+ wpa_printf(MSG_ERROR, "P2P: Failed to create new group "
+ "interface");
+ return -1;
+ }
+
+ if (force_ifname[0]) {
+ wpa_printf(MSG_DEBUG, "P2P: Driver forced interface name %s",
+ force_ifname);
+ os_strlcpy(wpa_s->pending_interface_name, force_ifname,
+ sizeof(wpa_s->pending_interface_name));
+ } else
+ os_strlcpy(wpa_s->pending_interface_name, ifname,
+ sizeof(wpa_s->pending_interface_name));
+ wpa_printf(MSG_DEBUG, "P2P: Created pending virtual interface %s addr "
+ MACSTR, wpa_s->pending_interface_name,
+ MAC2STR(wpa_s->pending_interface_addr));
+
+ return 0;
+}
+
+
+static void wpas_p2p_remove_pending_group_interface(
+ struct wpa_supplicant *wpa_s)
+{
+ if (!wpa_s->pending_interface_name[0] ||
+ is_zero_ether_addr(wpa_s->pending_interface_addr))
+ return; /* No pending virtual interface */
+
+ wpa_printf(MSG_DEBUG, "P2P: Removing pending group interface %s",
+ wpa_s->pending_interface_name);
+ wpa_drv_if_remove(wpa_s, wpa_s->pending_interface_type,
+ wpa_s->pending_interface_name);
+ os_memset(wpa_s->pending_interface_addr, 0, ETH_ALEN);
+ wpa_s->pending_interface_name[0] = '\0';
+}
+
+
+static struct wpa_supplicant *
+wpas_p2p_init_group_interface(struct wpa_supplicant *wpa_s, int go)
+{
+ struct wpa_interface iface;
+ struct wpa_supplicant *group_wpa_s;
+
+ if (!wpa_s->pending_interface_name[0]) {
+ wpa_printf(MSG_ERROR, "P2P: No pending group interface");
+ if (!wpas_p2p_create_iface(wpa_s))
+ return NULL;
+ /*
+ * Something has forced us to remove the pending interface; try
+ * to create a new one and hope for the best that we will get
+ * the same local address.
+ */
+ if (wpas_p2p_add_group_interface(wpa_s, go ? WPA_IF_P2P_GO :
+ WPA_IF_P2P_CLIENT) < 0)
+ return NULL;
+ }
+
+ os_memset(&iface, 0, sizeof(iface));
+ iface.ifname = wpa_s->pending_interface_name;
+ iface.driver = wpa_s->driver->name;
+ iface.ctrl_interface = wpa_s->conf->ctrl_interface;
+ iface.driver_param = wpa_s->conf->driver_param;
+ group_wpa_s = wpa_supplicant_add_iface(wpa_s->global, &iface);
+ if (group_wpa_s == NULL) {
+ wpa_printf(MSG_ERROR, "P2P: Failed to create new "
+ "wpa_supplicant interface");
+ return NULL;
+ }
+ wpa_s->pending_interface_name[0] = '\0';
+ group_wpa_s->parent = wpa_s;
+ group_wpa_s->p2p_group_interface = go ? P2P_GROUP_INTERFACE_GO :
+ P2P_GROUP_INTERFACE_CLIENT;
+ wpa_s->global->p2p_group_formation = group_wpa_s;
+
+ wpas_p2p_clone_config(group_wpa_s, wpa_s);
+
+ return group_wpa_s;
+}
+
+
+static void wpas_p2p_group_formation_timeout(void *eloop_ctx,
+ void *timeout_ctx)
+{
+ struct wpa_supplicant *wpa_s = eloop_ctx;
+ wpa_printf(MSG_DEBUG, "P2P: Group Formation timed out");
+ if (wpa_s->global->p2p)
+ p2p_group_formation_failed(wpa_s->global->p2p);
+ else if (wpa_s->drv_flags & WPA_DRIVER_FLAGS_P2P_MGMT)
+ wpa_drv_p2p_group_formation_failed(wpa_s);
+ wpas_group_formation_completed(wpa_s, 0);
+}
+
+
+void wpas_go_neg_completed(void *ctx, struct p2p_go_neg_results *res)
+{
+ struct wpa_supplicant *wpa_s = ctx;
+
+ if (wpa_s->off_channel_freq || wpa_s->roc_waiting_drv_freq) {
+ wpa_drv_cancel_remain_on_channel(wpa_s);
+ wpa_s->off_channel_freq = 0;
+ wpa_s->roc_waiting_drv_freq = 0;
+ }
+
+ if (res->status) {
+ wpa_msg(wpa_s, MSG_INFO, P2P_EVENT_GO_NEG_FAILURE "status=%d",
+ res->status);
+ wpas_notify_p2p_go_neg_completed(wpa_s, res);
+ wpas_p2p_remove_pending_group_interface(wpa_s);
+ return;
+ }
+
+ if (wpa_s->p2p_go_ht40)
+ res->ht40 = 1;
+
+ wpa_msg(wpa_s, MSG_INFO, P2P_EVENT_GO_NEG_SUCCESS);
+ wpas_notify_p2p_go_neg_completed(wpa_s, res);
+
+ if (res->role_go && wpa_s->p2p_persistent_id >= 0) {
+ struct wpa_ssid *ssid;
+ ssid = wpa_config_get_network(wpa_s->conf,
+ wpa_s->p2p_persistent_id);
+ if (ssid && ssid->disabled == 2 &&
+ ssid->mode == WPAS_MODE_P2P_GO && ssid->passphrase) {
+ size_t len = os_strlen(ssid->passphrase);
+ wpa_printf(MSG_DEBUG, "P2P: Override passphrase based "
+ "on requested persistent group");
+ os_memcpy(res->passphrase, ssid->passphrase, len);
+ res->passphrase[len] = '\0';
+ }
+ }
+
+ if (wpa_s->create_p2p_iface) {
+ struct wpa_supplicant *group_wpa_s =
+ wpas_p2p_init_group_interface(wpa_s, res->role_go);
+ if (group_wpa_s == NULL) {
+ wpas_p2p_remove_pending_group_interface(wpa_s);
+ return;
+ }
+ if (group_wpa_s != wpa_s) {
+ os_memcpy(group_wpa_s->p2p_pin, wpa_s->p2p_pin,
+ sizeof(group_wpa_s->p2p_pin));
+ group_wpa_s->p2p_wps_method = wpa_s->p2p_wps_method;
+ }
+ os_memset(wpa_s->pending_interface_addr, 0, ETH_ALEN);
+ wpa_s->pending_interface_name[0] = '\0';
+ group_wpa_s->p2p_in_provisioning = 1;
+
+ if (res->role_go)
+ wpas_start_wps_go(group_wpa_s, res, 1);
+ else
+ wpas_start_wps_enrollee(group_wpa_s, res);
+ } else {
+ wpa_s->p2p_in_provisioning = 1;
+ wpa_s->global->p2p_group_formation = wpa_s;
+
+ if (res->role_go)
+ wpas_start_wps_go(wpa_s, res, 1);
+ else
+ wpas_start_wps_enrollee(ctx, res);
+ }
+
+ wpa_s->p2p_long_listen = 0;
+ eloop_cancel_timeout(wpas_p2p_long_listen_timeout, wpa_s, NULL);
+
+ eloop_cancel_timeout(wpas_p2p_group_formation_timeout, wpa_s, NULL);
+ eloop_register_timeout(15 + res->peer_config_timeout / 100,
+ (res->peer_config_timeout % 100) * 10000,
+ wpas_p2p_group_formation_timeout, wpa_s, NULL);
+}
+
+
+void wpas_go_neg_req_rx(void *ctx, const u8 *src, u16 dev_passwd_id)
+{
+ struct wpa_supplicant *wpa_s = ctx;
+ wpa_msg(wpa_s, MSG_INFO, P2P_EVENT_GO_NEG_REQUEST MACSTR
+ " dev_passwd_id=%u", MAC2STR(src), dev_passwd_id);
+
+ wpas_notify_p2p_go_neg_req(wpa_s, src, dev_passwd_id);
+}
+
+
+void wpas_dev_found(void *ctx, const u8 *addr,
+ const struct p2p_peer_info *info,
+ int new_device)
+{
+#ifndef CONFIG_NO_STDOUT_DEBUG
+ struct wpa_supplicant *wpa_s = ctx;
+ char devtype[WPS_DEV_TYPE_BUFSIZE];
+
+ wpa_msg(wpa_s, MSG_INFO, P2P_EVENT_DEVICE_FOUND MACSTR
+ " p2p_dev_addr=" MACSTR
+ " pri_dev_type=%s name='%s' config_methods=0x%x "
+ "dev_capab=0x%x group_capab=0x%x",
+ MAC2STR(addr), MAC2STR(info->p2p_device_addr),
+ wps_dev_type_bin2str(info->pri_dev_type, devtype,
+ sizeof(devtype)),
+ info->device_name, info->config_methods,
+ info->dev_capab, info->group_capab);
+#endif /* CONFIG_NO_STDOUT_DEBUG */
+
+ wpas_notify_p2p_device_found(ctx, info->p2p_device_addr, new_device);
+}
+
+
+static void wpas_dev_lost(void *ctx, const u8 *dev_addr)
+{
+ struct wpa_supplicant *wpa_s = ctx;
+
+ wpa_msg(wpa_s, MSG_INFO, P2P_EVENT_DEVICE_LOST
+ "p2p_dev_addr=" MACSTR, MAC2STR(dev_addr));
+
+ wpas_notify_p2p_device_lost(wpa_s, dev_addr);
+}
+
+
+static int wpas_start_listen(void *ctx, unsigned int freq,
+ unsigned int duration,
+ const struct wpabuf *probe_resp_ie)
+{
+ struct wpa_supplicant *wpa_s = ctx;
+
+ wpa_drv_set_ap_wps_ie(wpa_s, NULL, probe_resp_ie, NULL);
+
+ if (wpa_drv_probe_req_report(wpa_s, 1) < 0) {
+ wpa_printf(MSG_DEBUG, "P2P: Failed to request the driver to "
+ "report received Probe Request frames");
+ return -1;
+ }
+
+ wpa_s->pending_listen_freq = freq;
+ wpa_s->pending_listen_duration = duration;
+
+ if (wpa_drv_remain_on_channel(wpa_s, freq, duration) < 0) {
+ wpa_printf(MSG_DEBUG, "P2P: Failed to request the driver "
+ "to remain on channel (%u MHz) for Listen "
+ "state", freq);
+ wpa_s->pending_listen_freq = 0;
+ return -1;
+ }
+ wpa_s->off_channel_freq = 0;
+ wpa_s->roc_waiting_drv_freq = freq;
+
+ return 0;
+}
+
+
+static void wpas_stop_listen(void *ctx)
+{
+ struct wpa_supplicant *wpa_s = ctx;
+ if (wpa_s->off_channel_freq || wpa_s->roc_waiting_drv_freq) {
+ wpa_drv_cancel_remain_on_channel(wpa_s);
+ wpa_s->off_channel_freq = 0;
+ wpa_s->roc_waiting_drv_freq = 0;
+ }
+ wpa_drv_set_ap_wps_ie(wpa_s, NULL, NULL, NULL);
+ wpa_drv_probe_req_report(wpa_s, 0);
+}
+
+
+static int wpas_send_probe_resp(void *ctx, const struct wpabuf *buf)
+{
+ struct wpa_supplicant *wpa_s = ctx;
+ return wpa_drv_send_mlme(wpa_s, wpabuf_head(buf), wpabuf_len(buf), 1);
+}
+
+
+/*
+ * DNS Header section is used only to calculate compression pointers, so the
+ * contents of this data does not matter, but the length needs to be reserved
+ * in the virtual packet.
+ */
+#define DNS_HEADER_LEN 12
+
+/*
+ * 27-octet in-memory packet from P2P specification containing two implied
+ * queries for _tcp.lcoal. PTR IN and _udp.local. PTR IN
+ */
+#define P2P_SD_IN_MEMORY_LEN 27
+
+static int p2p_sd_dns_uncompress_label(char **upos, char *uend, u8 *start,
+ u8 **spos, const u8 *end)
+{
+ while (*spos < end) {
+ u8 val = ((*spos)[0] & 0xc0) >> 6;
+ int len;
+
+ if (val == 1 || val == 2) {
+ /* These are reserved values in RFC 1035 */
+ wpa_printf(MSG_DEBUG, "P2P: Invalid domain name "
+ "sequence starting with 0x%x", val);
+ return -1;
+ }
+
+ if (val == 3) {
+ u16 offset;
+ u8 *spos_tmp;
+
+ /* Offset */
+ if (*spos + 2 > end) {
+ wpa_printf(MSG_DEBUG, "P2P: No room for full "
+ "DNS offset field");
+ return -1;
+ }
+
+ offset = (((*spos)[0] & 0x3f) << 8) | (*spos)[1];
+ if (offset >= *spos - start) {
+ wpa_printf(MSG_DEBUG, "P2P: Invalid DNS "
+ "pointer offset %u", offset);
+ return -1;
+ }
+
+ (*spos) += 2;
+ spos_tmp = start + offset;
+ return p2p_sd_dns_uncompress_label(upos, uend, start,
+ &spos_tmp,
+ *spos - 2);
+ }
+
+ /* Label */
+ len = (*spos)[0] & 0x3f;
+ if (len == 0)
+ return 0;
+
+ (*spos)++;
+ if (*spos + len > end) {
+ wpa_printf(MSG_DEBUG, "P2P: Invalid domain name "
+ "sequence - no room for label with length "
+ "%u", len);
+ return -1;
+ }
+
+ if (*upos + len + 2 > uend)
+ return -2;
+
+ os_memcpy(*upos, *spos, len);
+ *spos += len;
+ *upos += len;
+ (*upos)[0] = '.';
+ (*upos)++;
+ (*upos)[0] = '\0';
+ }
+
+ return 0;
+}
+
+
+/* Uncompress domain names per RFC 1035 using the P2P SD in-memory packet.
+ * Returns -1 on parsing error (invalid input sequence), -2 if output buffer is
+ * not large enough */
+static int p2p_sd_dns_uncompress(char *buf, size_t buf_len, const u8 *msg,
+ size_t msg_len, size_t offset)
+{
+ /* 27-octet in-memory packet from P2P specification */
+ const char *prefix = "\x04_tcp\x05local\x00\x00\x0C\x00\x01"
+ "\x04_udp\xC0\x11\x00\x0C\x00\x01";
+ u8 *tmp, *end, *spos;
+ char *upos, *uend;
+ int ret = 0;
+
+ if (buf_len < 2)
+ return -1;
+ if (offset > msg_len)
+ return -1;
+
+ tmp = os_malloc(DNS_HEADER_LEN + P2P_SD_IN_MEMORY_LEN + msg_len);
+ if (tmp == NULL)
+ return -1;
+ spos = tmp + DNS_HEADER_LEN + P2P_SD_IN_MEMORY_LEN;
+ end = spos + msg_len;
+ spos += offset;
+
+ os_memset(tmp, 0, DNS_HEADER_LEN);
+ os_memcpy(tmp + DNS_HEADER_LEN, prefix, P2P_SD_IN_MEMORY_LEN);
+ os_memcpy(tmp + DNS_HEADER_LEN + P2P_SD_IN_MEMORY_LEN, msg, msg_len);
+
+ upos = buf;
+ uend = buf + buf_len;
+
+ ret = p2p_sd_dns_uncompress_label(&upos, uend, tmp, &spos, end);
+ if (ret) {
+ os_free(tmp);
+ return ret;
+ }
+
+ if (upos == buf) {
+ upos[0] = '.';
+ upos[1] = '\0';
+ } else if (upos[-1] == '.')
+ upos[-1] = '\0';
+
+ os_free(tmp);
+ return 0;
+}
+
+
+static struct p2p_srv_bonjour *
+wpas_p2p_service_get_bonjour(struct wpa_supplicant *wpa_s,
+ const struct wpabuf *query)
+{
+ struct p2p_srv_bonjour *bsrv;
+ size_t len;
+
+ len = wpabuf_len(query);
+ dl_list_for_each(bsrv, &wpa_s->global->p2p_srv_bonjour,
+ struct p2p_srv_bonjour, list) {
+ if (len == wpabuf_len(bsrv->query) &&
+ os_memcmp(wpabuf_head(query), wpabuf_head(bsrv->query),
+ len) == 0)
+ return bsrv;
+ }
+ return NULL;
+}
+
+
+static struct p2p_srv_upnp *
+wpas_p2p_service_get_upnp(struct wpa_supplicant *wpa_s, u8 version,
+ const char *service)
+{
+ struct p2p_srv_upnp *usrv;
+
+ dl_list_for_each(usrv, &wpa_s->global->p2p_srv_upnp,
+ struct p2p_srv_upnp, list) {
+ if (version == usrv->version &&
+ os_strcmp(service, usrv->service) == 0)
+ return usrv;
+ }
+ return NULL;
+}
+
+
+static void wpas_sd_add_proto_not_avail(struct wpabuf *resp, u8 srv_proto,
+ u8 srv_trans_id)
+{
+ u8 *len_pos;
+
+ if (wpabuf_tailroom(resp) < 5)
+ return;
+
+ /* Length (to be filled) */
+ len_pos = wpabuf_put(resp, 2);
+ wpabuf_put_u8(resp, srv_proto);
+ wpabuf_put_u8(resp, srv_trans_id);
+ /* Status Code */
+ wpabuf_put_u8(resp, P2P_SD_PROTO_NOT_AVAILABLE);
+ /* Response Data: empty */
+ WPA_PUT_LE16(len_pos, (u8 *) wpabuf_put(resp, 0) - len_pos - 2);
+}
+
+
+static void wpas_sd_all_bonjour(struct wpa_supplicant *wpa_s,
+ struct wpabuf *resp, u8 srv_trans_id)
+{
+ struct p2p_srv_bonjour *bsrv;
+ u8 *len_pos;
+
+ wpa_printf(MSG_DEBUG, "P2P: SD Request for all Bonjour services");
+
+ if (dl_list_empty(&wpa_s->global->p2p_srv_bonjour)) {
+ wpa_printf(MSG_DEBUG, "P2P: Bonjour protocol not available");
+ return;
+ }
+
+ dl_list_for_each(bsrv, &wpa_s->global->p2p_srv_bonjour,
+ struct p2p_srv_bonjour, list) {
+ if (wpabuf_tailroom(resp) <
+ 5 + wpabuf_len(bsrv->query) + wpabuf_len(bsrv->resp))
+ return;
+ /* Length (to be filled) */
+ len_pos = wpabuf_put(resp, 2);
+ wpabuf_put_u8(resp, P2P_SERV_BONJOUR);
+ wpabuf_put_u8(resp, srv_trans_id);
+ /* Status Code */
+ wpabuf_put_u8(resp, P2P_SD_SUCCESS);
+ wpa_hexdump_ascii(MSG_DEBUG, "P2P: Matching Bonjour service",
+ wpabuf_head(bsrv->resp),
+ wpabuf_len(bsrv->resp));
+ /* Response Data */
+ wpabuf_put_buf(resp, bsrv->query); /* Key */
+ wpabuf_put_buf(resp, bsrv->resp); /* Value */
+ WPA_PUT_LE16(len_pos, (u8 *) wpabuf_put(resp, 0) - len_pos -
+ 2);
+ }
+}
+
+
+static int match_bonjour_query(struct p2p_srv_bonjour *bsrv, const u8 *query,
+ size_t query_len)
+{
+ char str_rx[256], str_srv[256];
+
+ if (query_len < 3 || wpabuf_len(bsrv->query) < 3)
+ return 0; /* Too short to include DNS Type and Version */
+ if (os_memcmp(query + query_len - 3,
+ wpabuf_head_u8(bsrv->query) + wpabuf_len(bsrv->query) - 3,
+ 3) != 0)
+ return 0; /* Mismatch in DNS Type or Version */
+ if (query_len == wpabuf_len(bsrv->query) &&
+ os_memcmp(query, wpabuf_head(bsrv->query), query_len - 3) == 0)
+ return 1; /* Binary match */
+
+ if (p2p_sd_dns_uncompress(str_rx, sizeof(str_rx), query, query_len - 3,
+ 0))
+ return 0; /* Failed to uncompress query */
+ if (p2p_sd_dns_uncompress(str_srv, sizeof(str_srv),
+ wpabuf_head(bsrv->query),
+ wpabuf_len(bsrv->query) - 3, 0))
+ return 0; /* Failed to uncompress service */
+
+ return os_strcmp(str_rx, str_srv) == 0;
+}
+
+
+static void wpas_sd_req_bonjour(struct wpa_supplicant *wpa_s,
+ struct wpabuf *resp, u8 srv_trans_id,
+ const u8 *query, size_t query_len)
+{
+ struct p2p_srv_bonjour *bsrv;
+ u8 *len_pos;
+ int matches = 0;
+
+ wpa_hexdump_ascii(MSG_DEBUG, "P2P: SD Request for Bonjour",
+ query, query_len);
+ if (dl_list_empty(&wpa_s->global->p2p_srv_bonjour)) {
+ wpa_printf(MSG_DEBUG, "P2P: Bonjour protocol not available");
+ wpas_sd_add_proto_not_avail(resp, P2P_SERV_BONJOUR,
+ srv_trans_id);
+ return;
+ }
+
+ if (query_len == 0) {
+ wpas_sd_all_bonjour(wpa_s, resp, srv_trans_id);
+ return;
+ }
+
+ dl_list_for_each(bsrv, &wpa_s->global->p2p_srv_bonjour,
+ struct p2p_srv_bonjour, list) {
+ if (!match_bonjour_query(bsrv, query, query_len))
+ continue;
+
+ if (wpabuf_tailroom(resp) <
+ 5 + query_len + wpabuf_len(bsrv->resp))
+ return;
+
+ matches++;
+
+ /* Length (to be filled) */
+ len_pos = wpabuf_put(resp, 2);
+ wpabuf_put_u8(resp, P2P_SERV_BONJOUR);
+ wpabuf_put_u8(resp, srv_trans_id);
+
+ /* Status Code */
+ wpabuf_put_u8(resp, P2P_SD_SUCCESS);
+ wpa_hexdump_ascii(MSG_DEBUG, "P2P: Matching Bonjour service",
+ wpabuf_head(bsrv->resp),
+ wpabuf_len(bsrv->resp));
+
+ /* Response Data */
+ wpabuf_put_data(resp, query, query_len); /* Key */
+ wpabuf_put_buf(resp, bsrv->resp); /* Value */
+
+ WPA_PUT_LE16(len_pos, (u8 *) wpabuf_put(resp, 0) - len_pos - 2);
+ }
+
+ if (matches == 0) {
+ wpa_printf(MSG_DEBUG, "P2P: Requested Bonjour service not "
+ "available");
+ if (wpabuf_tailroom(resp) < 5)
+ return;
+
+ /* Length (to be filled) */
+ len_pos = wpabuf_put(resp, 2);
+ wpabuf_put_u8(resp, P2P_SERV_BONJOUR);
+ wpabuf_put_u8(resp, srv_trans_id);
+
+ /* Status Code */
+ wpabuf_put_u8(resp, P2P_SD_REQUESTED_INFO_NOT_AVAILABLE);
+ /* Response Data: empty */
+ WPA_PUT_LE16(len_pos, (u8 *) wpabuf_put(resp, 0) - len_pos -
+ 2);
+ }
+}
+
+
+static void wpas_sd_all_upnp(struct wpa_supplicant *wpa_s,
+ struct wpabuf *resp, u8 srv_trans_id)
+{
+ struct p2p_srv_upnp *usrv;
+ u8 *len_pos;
+
+ wpa_printf(MSG_DEBUG, "P2P: SD Request for all UPnP services");
+
+ if (dl_list_empty(&wpa_s->global->p2p_srv_upnp)) {
+ wpa_printf(MSG_DEBUG, "P2P: UPnP protocol not available");
+ return;
+ }
+
+ dl_list_for_each(usrv, &wpa_s->global->p2p_srv_upnp,
+ struct p2p_srv_upnp, list) {
+ if (wpabuf_tailroom(resp) < 5 + 1 + os_strlen(usrv->service))
+ return;
+
+ /* Length (to be filled) */
+ len_pos = wpabuf_put(resp, 2);
+ wpabuf_put_u8(resp, P2P_SERV_UPNP);
+ wpabuf_put_u8(resp, srv_trans_id);
+
+ /* Status Code */
+ wpabuf_put_u8(resp, P2P_SD_SUCCESS);
+ /* Response Data */
+ wpabuf_put_u8(resp, usrv->version);
+ wpa_printf(MSG_DEBUG, "P2P: Matching UPnP Service: %s",
+ usrv->service);
+ wpabuf_put_str(resp, usrv->service);
+ WPA_PUT_LE16(len_pos, (u8 *) wpabuf_put(resp, 0) - len_pos -
+ 2);
+ }
+}
+
+
+static void wpas_sd_req_upnp(struct wpa_supplicant *wpa_s,
+ struct wpabuf *resp, u8 srv_trans_id,
+ const u8 *query, size_t query_len)
+{
+ struct p2p_srv_upnp *usrv;
+ u8 *len_pos;
+ u8 version;
+ char *str;
+ int count = 0;
+
+ wpa_hexdump_ascii(MSG_DEBUG, "P2P: SD Request for UPnP",
+ query, query_len);
+
+ if (dl_list_empty(&wpa_s->global->p2p_srv_upnp)) {
+ wpa_printf(MSG_DEBUG, "P2P: UPnP protocol not available");
+ wpas_sd_add_proto_not_avail(resp, P2P_SERV_UPNP,
+ srv_trans_id);
+ return;
+ }
+
+ if (query_len == 0) {
+ wpas_sd_all_upnp(wpa_s, resp, srv_trans_id);
+ return;
+ }
+
+ if (wpabuf_tailroom(resp) < 5)
+ return;
+
+ /* Length (to be filled) */
+ len_pos = wpabuf_put(resp, 2);
+ wpabuf_put_u8(resp, P2P_SERV_UPNP);
+ wpabuf_put_u8(resp, srv_trans_id);
+
+ version = query[0];
+ str = os_malloc(query_len);
+ if (str == NULL)
+ return;
+ os_memcpy(str, query + 1, query_len - 1);
+ str[query_len - 1] = '\0';
+
+ dl_list_for_each(usrv, &wpa_s->global->p2p_srv_upnp,
+ struct p2p_srv_upnp, list) {
+ if (version != usrv->version)
+ continue;
+
+ if (os_strcmp(str, "ssdp:all") != 0 &&
+ os_strstr(usrv->service, str) == NULL)
+ continue;
+
+ if (wpabuf_tailroom(resp) < 2)
+ break;
+ if (count == 0) {
+ /* Status Code */
+ wpabuf_put_u8(resp, P2P_SD_SUCCESS);
+ /* Response Data */
+ wpabuf_put_u8(resp, version);
+ } else
+ wpabuf_put_u8(resp, ',');
+
+ count++;
+
+ wpa_printf(MSG_DEBUG, "P2P: Matching UPnP Service: %s",
+ usrv->service);
+ if (wpabuf_tailroom(resp) < os_strlen(usrv->service))
+ break;
+ wpabuf_put_str(resp, usrv->service);
+ }
+ os_free(str);
+
+ if (count == 0) {
+ wpa_printf(MSG_DEBUG, "P2P: Requested UPnP service not "
+ "available");
+ /* Status Code */
+ wpabuf_put_u8(resp, P2P_SD_REQUESTED_INFO_NOT_AVAILABLE);
+ /* Response Data: empty */
+ }
+
+ WPA_PUT_LE16(len_pos, (u8 *) wpabuf_put(resp, 0) - len_pos - 2);
+}
+
+
+#ifdef CONFIG_WIFI_DISPLAY
+static void wpas_sd_req_wfd(struct wpa_supplicant *wpa_s,
+ struct wpabuf *resp, u8 srv_trans_id,
+ const u8 *query, size_t query_len)
+{
+ const u8 *pos;
+ u8 role;
+ u8 *len_pos;
+
+ wpa_hexdump(MSG_DEBUG, "P2P: SD Request for WFD", query, query_len);
+
+ if (!wpa_s->global->wifi_display) {
+ wpa_printf(MSG_DEBUG, "P2P: WFD protocol not available");
+ wpas_sd_add_proto_not_avail(resp, P2P_SERV_WIFI_DISPLAY,
+ srv_trans_id);
+ return;
+ }
+
+ if (query_len < 1) {
+ wpa_printf(MSG_DEBUG, "P2P: Missing WFD Requested Device "
+ "Role");
+ return;
+ }
+
+ if (wpabuf_tailroom(resp) < 5)
+ return;
+
+ pos = query;
+ role = *pos++;
+ wpa_printf(MSG_DEBUG, "P2P: WSD for device role 0x%x", role);
+
+ /* TODO: role specific handling */
+
+ /* Length (to be filled) */
+ len_pos = wpabuf_put(resp, 2);
+ wpabuf_put_u8(resp, P2P_SERV_WIFI_DISPLAY);
+ wpabuf_put_u8(resp, srv_trans_id);
+ wpabuf_put_u8(resp, P2P_SD_SUCCESS); /* Status Code */
+
+ while (pos < query + query_len) {
+ if (*pos < MAX_WFD_SUBELEMS &&
+ wpa_s->global->wfd_subelem[*pos] &&
+ wpabuf_tailroom(resp) >=
+ wpabuf_len(wpa_s->global->wfd_subelem[*pos])) {
+ wpa_printf(MSG_DEBUG, "P2P: Add WSD response "
+ "subelement %u", *pos);
+ wpabuf_put_buf(resp, wpa_s->global->wfd_subelem[*pos]);
+ }
+ pos++;
+ }
+
+ WPA_PUT_LE16(len_pos, (u8 *) wpabuf_put(resp, 0) - len_pos - 2);
+}
+#endif /* CONFIG_WIFI_DISPLAY */
+
+
+void wpas_sd_request(void *ctx, int freq, const u8 *sa, u8 dialog_token,
+ u16 update_indic, const u8 *tlvs, size_t tlvs_len)
+{
+ struct wpa_supplicant *wpa_s = ctx;
+ const u8 *pos = tlvs;
+ const u8 *end = tlvs + tlvs_len;
+ const u8 *tlv_end;
+ u16 slen;
+ struct wpabuf *resp;
+ u8 srv_proto, srv_trans_id;
+ size_t buf_len;
+ char *buf;
+
+ wpa_hexdump(MSG_MSGDUMP, "P2P: Service Discovery Request TLVs",
+ tlvs, tlvs_len);
+ buf_len = 2 * tlvs_len + 1;
+ buf = os_malloc(buf_len);
+ if (buf) {
+ wpa_snprintf_hex(buf, buf_len, tlvs, tlvs_len);
+ wpa_msg_ctrl(wpa_s, MSG_INFO, P2P_EVENT_SERV_DISC_REQ "%d "
+ MACSTR " %u %u %s",
+ freq, MAC2STR(sa), dialog_token, update_indic,
+ buf);
+ os_free(buf);
+ }
+
+ if (wpa_s->p2p_sd_over_ctrl_iface) {
+ wpas_notify_p2p_sd_request(wpa_s, freq, sa, dialog_token,
+ update_indic, tlvs, tlvs_len);
+ return; /* to be processed by an external program */
+ }
+
+ resp = wpabuf_alloc(10000);
+ if (resp == NULL)
+ return;
+
+ while (pos + 1 < end) {
+ wpa_printf(MSG_DEBUG, "P2P: Service Request TLV");
+ slen = WPA_GET_LE16(pos);
+ pos += 2;
+ if (pos + slen > end || slen < 2) {
+ wpa_printf(MSG_DEBUG, "P2P: Unexpected Query Data "
+ "length");
+ wpabuf_free(resp);
+ return;
+ }
+ tlv_end = pos + slen;
+
+ srv_proto = *pos++;
+ wpa_printf(MSG_DEBUG, "P2P: Service Protocol Type %u",
+ srv_proto);
+ srv_trans_id = *pos++;
+ wpa_printf(MSG_DEBUG, "P2P: Service Transaction ID %u",
+ srv_trans_id);
+
+ wpa_hexdump(MSG_MSGDUMP, "P2P: Query Data",
+ pos, tlv_end - pos);
+
+
+ if (wpa_s->force_long_sd) {
+ wpa_printf(MSG_DEBUG, "P2P: SD test - force long "
+ "response");
+ wpas_sd_all_bonjour(wpa_s, resp, srv_trans_id);
+ wpas_sd_all_upnp(wpa_s, resp, srv_trans_id);
+ goto done;
+ }
+
+ switch (srv_proto) {
+ case P2P_SERV_ALL_SERVICES:
+ wpa_printf(MSG_DEBUG, "P2P: Service Discovery Request "
+ "for all services");
+ if (dl_list_empty(&wpa_s->global->p2p_srv_upnp) &&
+ dl_list_empty(&wpa_s->global->p2p_srv_bonjour)) {
+ wpa_printf(MSG_DEBUG, "P2P: No service "
+ "discovery protocols available");
+ wpas_sd_add_proto_not_avail(
+ resp, P2P_SERV_ALL_SERVICES,
+ srv_trans_id);
+ break;
+ }
+ wpas_sd_all_bonjour(wpa_s, resp, srv_trans_id);
+ wpas_sd_all_upnp(wpa_s, resp, srv_trans_id);
+ break;
+ case P2P_SERV_BONJOUR:
+ wpas_sd_req_bonjour(wpa_s, resp, srv_trans_id,
+ pos, tlv_end - pos);
+ break;
+ case P2P_SERV_UPNP:
+ wpas_sd_req_upnp(wpa_s, resp, srv_trans_id,
+ pos, tlv_end - pos);
+ break;
+#ifdef CONFIG_WIFI_DISPLAY
+ case P2P_SERV_WIFI_DISPLAY:
+ wpas_sd_req_wfd(wpa_s, resp, srv_trans_id,
+ pos, tlv_end - pos);
+ break;
+#endif /* CONFIG_WIFI_DISPLAY */
+ default:
+ wpa_printf(MSG_DEBUG, "P2P: Unavailable service "
+ "protocol %u", srv_proto);
+ wpas_sd_add_proto_not_avail(resp, srv_proto,
+ srv_trans_id);
+ break;
+ }
+
+ pos = tlv_end;
+ }
+
+done:
+ wpas_notify_p2p_sd_request(wpa_s, freq, sa, dialog_token,
+ update_indic, tlvs, tlvs_len);
+
+ wpas_p2p_sd_response(wpa_s, freq, sa, dialog_token, resp);
+
+ wpabuf_free(resp);
+}
+
+
+void wpas_sd_response(void *ctx, const u8 *sa, u16 update_indic,
+ const u8 *tlvs, size_t tlvs_len)
+{
+ struct wpa_supplicant *wpa_s = ctx;
+ const u8 *pos = tlvs;
+ const u8 *end = tlvs + tlvs_len;
+ const u8 *tlv_end;
+ u16 slen;
+ size_t buf_len;
+ char *buf;
+
+ wpa_hexdump(MSG_MSGDUMP, "P2P: Service Discovery Response TLVs",
+ tlvs, tlvs_len);
+ if (tlvs_len > 1500) {
+ /* TODO: better way for handling this */
+ wpa_msg_ctrl(wpa_s, MSG_INFO,
+ P2P_EVENT_SERV_DISC_RESP MACSTR
+ " %u <long response: %u bytes>",
+ MAC2STR(sa), update_indic,
+ (unsigned int) tlvs_len);
+ } else {
+ buf_len = 2 * tlvs_len + 1;
+ buf = os_malloc(buf_len);
+ if (buf) {
+ wpa_snprintf_hex(buf, buf_len, tlvs, tlvs_len);
+ wpa_msg_ctrl(wpa_s, MSG_INFO,
+ P2P_EVENT_SERV_DISC_RESP MACSTR " %u %s",
+ MAC2STR(sa), update_indic, buf);
+ os_free(buf);
+ }
+ }
+
+ while (pos < end) {
+ u8 srv_proto, srv_trans_id, status;
+
+ wpa_printf(MSG_DEBUG, "P2P: Service Response TLV");
+ slen = WPA_GET_LE16(pos);
+ pos += 2;
+ if (pos + slen > end || slen < 3) {
+ wpa_printf(MSG_DEBUG, "P2P: Unexpected Response Data "
+ "length");
+ return;
+ }
+ tlv_end = pos + slen;
+
+ srv_proto = *pos++;
+ wpa_printf(MSG_DEBUG, "P2P: Service Protocol Type %u",
+ srv_proto);
+ srv_trans_id = *pos++;
+ wpa_printf(MSG_DEBUG, "P2P: Service Transaction ID %u",
+ srv_trans_id);
+ status = *pos++;
+ wpa_printf(MSG_DEBUG, "P2P: Status Code ID %u",
+ status);
+
+ wpa_hexdump(MSG_MSGDUMP, "P2P: Response Data",
+ pos, tlv_end - pos);
+
+ pos = tlv_end;
+ }
+
+ wpas_notify_p2p_sd_response(wpa_s, sa, update_indic, tlvs, tlvs_len);
+}
+
+
+u64 wpas_p2p_sd_request(struct wpa_supplicant *wpa_s, const u8 *dst,
+ const struct wpabuf *tlvs)
+{
+ if (wpa_s->drv_flags & WPA_DRIVER_FLAGS_P2P_MGMT)
+ return wpa_drv_p2p_sd_request(wpa_s, dst, tlvs);
+ if (wpa_s->global->p2p_disabled || wpa_s->global->p2p == NULL)
+ return 0;
+ return (uintptr_t) p2p_sd_request(wpa_s->global->p2p, dst, tlvs);
+}
+
+
+u64 wpas_p2p_sd_request_upnp(struct wpa_supplicant *wpa_s, const u8 *dst,
+ u8 version, const char *query)
+{
+ struct wpabuf *tlvs;
+ u64 ret;
+
+ tlvs = wpabuf_alloc(2 + 1 + 1 + 1 + os_strlen(query));
+ if (tlvs == NULL)
+ return 0;
+ wpabuf_put_le16(tlvs, 1 + 1 + 1 + os_strlen(query));
+ wpabuf_put_u8(tlvs, P2P_SERV_UPNP); /* Service Protocol Type */
+ wpabuf_put_u8(tlvs, 1); /* Service Transaction ID */
+ wpabuf_put_u8(tlvs, version);
+ wpabuf_put_str(tlvs, query);
+ ret = wpas_p2p_sd_request(wpa_s, dst, tlvs);
+ wpabuf_free(tlvs);
+ return ret;
+}
+
+
+#ifdef CONFIG_WIFI_DISPLAY
+
+static u64 wpas_p2p_sd_request_wfd(struct wpa_supplicant *wpa_s, const u8 *dst,
+ const struct wpabuf *tlvs)
+{
+ if (wpa_s->drv_flags & WPA_DRIVER_FLAGS_P2P_MGMT)
+ return 0;
+ if (wpa_s->global->p2p_disabled || wpa_s->global->p2p == NULL)
+ return 0;
+ return (uintptr_t) p2p_sd_request_wfd(wpa_s->global->p2p, dst, tlvs);
+}
+
+
+#define MAX_WFD_SD_SUBELEMS 20
+
+static void wfd_add_sd_req_role(struct wpabuf *tlvs, u8 id, u8 role,
+ const char *subelems)
+{
+ u8 *len;
+ const char *pos;
+ int val;
+ int count = 0;
+
+ len = wpabuf_put(tlvs, 2);
+ wpabuf_put_u8(tlvs, P2P_SERV_WIFI_DISPLAY); /* Service Protocol Type */
+ wpabuf_put_u8(tlvs, id); /* Service Transaction ID */
+
+ wpabuf_put_u8(tlvs, role);
+
+ pos = subelems;
+ while (*pos) {
+ val = atoi(pos);
+ if (val >= 0 && val < 256) {
+ wpabuf_put_u8(tlvs, val);
+ count++;
+ if (count == MAX_WFD_SD_SUBELEMS)
+ break;
+ }
+ pos = os_strchr(pos + 1, ',');
+ if (pos == NULL)
+ break;
+ pos++;
+ }
+
+ WPA_PUT_LE16(len, (u8 *) wpabuf_put(tlvs, 0) - len - 2);
+}
+
+
+u64 wpas_p2p_sd_request_wifi_display(struct wpa_supplicant *wpa_s,
+ const u8 *dst, const char *role)
+{
+ struct wpabuf *tlvs;
+ u64 ret;
+ const char *subelems;
+ u8 id = 1;
+
+ subelems = os_strchr(role, ' ');
+ if (subelems == NULL)
+ return 0;
+ subelems++;
+
+ tlvs = wpabuf_alloc(4 * (2 + 1 + 1 + 1 + MAX_WFD_SD_SUBELEMS));
+ if (tlvs == NULL)
+ return 0;
+
+ if (os_strstr(role, "[source]"))
+ wfd_add_sd_req_role(tlvs, id++, 0x00, subelems);
+ if (os_strstr(role, "[pri-sink]"))
+ wfd_add_sd_req_role(tlvs, id++, 0x01, subelems);
+ if (os_strstr(role, "[sec-sink]"))
+ wfd_add_sd_req_role(tlvs, id++, 0x02, subelems);
+ if (os_strstr(role, "[source+sink]"))
+ wfd_add_sd_req_role(tlvs, id++, 0x03, subelems);
+
+ ret = wpas_p2p_sd_request_wfd(wpa_s, dst, tlvs);
+ wpabuf_free(tlvs);
+ return ret;
+}
+
+#endif /* CONFIG_WIFI_DISPLAY */
+
+
+int wpas_p2p_sd_cancel_request(struct wpa_supplicant *wpa_s, u64 req)
+{
+ if (wpa_s->drv_flags & WPA_DRIVER_FLAGS_P2P_MGMT)
+ return wpa_drv_p2p_sd_cancel_request(wpa_s, req);
+ if (wpa_s->global->p2p_disabled || wpa_s->global->p2p == NULL)
+ return -1;
+ return p2p_sd_cancel_request(wpa_s->global->p2p,
+ (void *) (uintptr_t) req);
+}
+
+
+void wpas_p2p_sd_response(struct wpa_supplicant *wpa_s, int freq,
+ const u8 *dst, u8 dialog_token,
+ const struct wpabuf *resp_tlvs)
+{
+ if (wpa_s->drv_flags & WPA_DRIVER_FLAGS_P2P_MGMT) {
+ wpa_drv_p2p_sd_response(wpa_s, freq, dst, dialog_token,
+ resp_tlvs);
+ return;
+ }
+ if (wpa_s->global->p2p_disabled || wpa_s->global->p2p == NULL)
+ return;
+ p2p_sd_response(wpa_s->global->p2p, freq, dst, dialog_token,
+ resp_tlvs);
+}
+
+
+void wpas_p2p_sd_service_update(struct wpa_supplicant *wpa_s)
+{
+ if (wpa_s->drv_flags & WPA_DRIVER_FLAGS_P2P_MGMT) {
+ wpa_drv_p2p_service_update(wpa_s);
+ return;
+ }
+ if (wpa_s->global->p2p)
+ p2p_sd_service_update(wpa_s->global->p2p);
+}
+
+
+static void wpas_p2p_srv_bonjour_free(struct p2p_srv_bonjour *bsrv)
+{
+ dl_list_del(&bsrv->list);
+ wpabuf_free(bsrv->query);
+ wpabuf_free(bsrv->resp);
+ os_free(bsrv);
+}
+
+
+static void wpas_p2p_srv_upnp_free(struct p2p_srv_upnp *usrv)
+{
+ dl_list_del(&usrv->list);
+ os_free(usrv->service);
+ os_free(usrv);
+}
+
+
+void wpas_p2p_service_flush(struct wpa_supplicant *wpa_s)
+{
+ struct p2p_srv_bonjour *bsrv, *bn;
+ struct p2p_srv_upnp *usrv, *un;
+
+ dl_list_for_each_safe(bsrv, bn, &wpa_s->global->p2p_srv_bonjour,
+ struct p2p_srv_bonjour, list)
+ wpas_p2p_srv_bonjour_free(bsrv);
+
+ dl_list_for_each_safe(usrv, un, &wpa_s->global->p2p_srv_upnp,
+ struct p2p_srv_upnp, list)
+ wpas_p2p_srv_upnp_free(usrv);
+
+ wpas_p2p_sd_service_update(wpa_s);
+}
+
+
+int wpas_p2p_service_add_bonjour(struct wpa_supplicant *wpa_s,
+ struct wpabuf *query, struct wpabuf *resp)
+{
+ struct p2p_srv_bonjour *bsrv;
+
+ bsrv = os_zalloc(sizeof(*bsrv));
+ if (bsrv == NULL)
+ return -1;
+ bsrv->query = query;
+ bsrv->resp = resp;
+ dl_list_add(&wpa_s->global->p2p_srv_bonjour, &bsrv->list);
+
+ wpas_p2p_sd_service_update(wpa_s);
+ return 0;
+}
+
+
+int wpas_p2p_service_del_bonjour(struct wpa_supplicant *wpa_s,
+ const struct wpabuf *query)
+{
+ struct p2p_srv_bonjour *bsrv;
+
+ bsrv = wpas_p2p_service_get_bonjour(wpa_s, query);
+ if (bsrv == NULL)
+ return -1;
+ wpas_p2p_srv_bonjour_free(bsrv);
+ wpas_p2p_sd_service_update(wpa_s);
+ return 0;
+}
+
+
+int wpas_p2p_service_add_upnp(struct wpa_supplicant *wpa_s, u8 version,
+ const char *service)
+{
+ struct p2p_srv_upnp *usrv;
+
+ if (wpas_p2p_service_get_upnp(wpa_s, version, service))
+ return 0; /* Already listed */
+ usrv = os_zalloc(sizeof(*usrv));
+ if (usrv == NULL)
+ return -1;
+ usrv->version = version;
+ usrv->service = os_strdup(service);
+ if (usrv->service == NULL) {
+ os_free(usrv);
+ return -1;
+ }
+ dl_list_add(&wpa_s->global->p2p_srv_upnp, &usrv->list);
+
+ wpas_p2p_sd_service_update(wpa_s);
+ return 0;
+}
+
+
+int wpas_p2p_service_del_upnp(struct wpa_supplicant *wpa_s, u8 version,
+ const char *service)
+{
+ struct p2p_srv_upnp *usrv;
+
+ usrv = wpas_p2p_service_get_upnp(wpa_s, version, service);
+ if (usrv == NULL)
+ return -1;
+ wpas_p2p_srv_upnp_free(usrv);
+ wpas_p2p_sd_service_update(wpa_s);
+ return 0;
+}
+
+
+static void wpas_prov_disc_local_display(struct wpa_supplicant *wpa_s,
+ const u8 *peer, const char *params,
+ unsigned int generated_pin)
+{
+ wpa_msg(wpa_s, MSG_INFO, P2P_EVENT_PROV_DISC_SHOW_PIN MACSTR " %08d%s",
+ MAC2STR(peer), generated_pin, params);
+}
+
+
+static void wpas_prov_disc_local_keypad(struct wpa_supplicant *wpa_s,
+ const u8 *peer, const char *params)
+{
+ wpa_msg(wpa_s, MSG_INFO, P2P_EVENT_PROV_DISC_ENTER_PIN MACSTR "%s",
+ MAC2STR(peer), params);
+}
+
+
+void wpas_prov_disc_req(void *ctx, const u8 *peer, u16 config_methods,
+ const u8 *dev_addr, const u8 *pri_dev_type,
+ const char *dev_name, u16 supp_config_methods,
+ u8 dev_capab, u8 group_capab, const u8 *group_id,
+ size_t group_id_len)
+{
+ struct wpa_supplicant *wpa_s = ctx;
+ char devtype[WPS_DEV_TYPE_BUFSIZE];
+ char params[300];
+ u8 empty_dev_type[8];
+ unsigned int generated_pin = 0;
+ struct wpa_supplicant *group = NULL;
+
+ if (group_id) {
+ for (group = wpa_s->global->ifaces; group; group = group->next)
+ {
+ struct wpa_ssid *s = group->current_ssid;
+ if (s != NULL &&
+ s->mode == WPAS_MODE_P2P_GO &&
+ group_id_len - ETH_ALEN == s->ssid_len &&
+ os_memcmp(group_id + ETH_ALEN, s->ssid,
+ s->ssid_len) == 0)
+ break;
+ }
+ }
+
+ if (pri_dev_type == NULL) {
+ os_memset(empty_dev_type, 0, sizeof(empty_dev_type));
+ pri_dev_type = empty_dev_type;
+ }
+ os_snprintf(params, sizeof(params), " p2p_dev_addr=" MACSTR
+ " pri_dev_type=%s name='%s' config_methods=0x%x "
+ "dev_capab=0x%x group_capab=0x%x%s%s",
+ MAC2STR(dev_addr),
+ wps_dev_type_bin2str(pri_dev_type, devtype,
+ sizeof(devtype)),
+ dev_name, supp_config_methods, dev_capab, group_capab,
+ group ? " group=" : "",
+ group ? group->ifname : "");
+ params[sizeof(params) - 1] = '\0';
+
+ if (config_methods & WPS_CONFIG_DISPLAY) {
+ generated_pin = wps_generate_pin();
+ wpas_prov_disc_local_display(wpa_s, peer, params,
+ generated_pin);
+ } else if (config_methods & WPS_CONFIG_KEYPAD)
+ wpas_prov_disc_local_keypad(wpa_s, peer, params);
+ else if (config_methods & WPS_CONFIG_PUSHBUTTON)
+ wpa_msg(wpa_s, MSG_INFO, P2P_EVENT_PROV_DISC_PBC_REQ MACSTR
+ "%s", MAC2STR(peer), params);
+
+ wpas_notify_p2p_provision_discovery(wpa_s, peer, 1 /* request */,
+ P2P_PROV_DISC_SUCCESS,
+ config_methods, generated_pin);
+}
+
+
+void wpas_prov_disc_resp(void *ctx, const u8 *peer, u16 config_methods)
+{
+ struct wpa_supplicant *wpa_s = ctx;
+ unsigned int generated_pin = 0;
+ char params[20];
+
+ if (wpa_s->pending_pd_before_join &&
+ (os_memcmp(peer, wpa_s->pending_join_dev_addr, ETH_ALEN) == 0 ||
+ os_memcmp(peer, wpa_s->pending_join_iface_addr, ETH_ALEN) == 0)) {
+ wpa_s->pending_pd_before_join = 0;
+ wpa_printf(MSG_DEBUG, "P2P: Starting pending "
+ "join-existing-group operation");
+ wpas_p2p_join_start(wpa_s);
+ return;
+ }
+
+ if (wpa_s->pending_pd_use == AUTO_PD_JOIN ||
+ wpa_s->pending_pd_use == AUTO_PD_GO_NEG)
+ os_snprintf(params, sizeof(params), " peer_go=%d",
+ wpa_s->pending_pd_use == AUTO_PD_JOIN);
+ else
+ params[0] = '\0';
+
+ if (config_methods & WPS_CONFIG_DISPLAY)
+ wpas_prov_disc_local_keypad(wpa_s, peer, params);
+ else if (config_methods & WPS_CONFIG_KEYPAD) {
+ generated_pin = wps_generate_pin();
+ wpas_prov_disc_local_display(wpa_s, peer, params,
+ generated_pin);
+ } else if (config_methods & WPS_CONFIG_PUSHBUTTON)
+ wpa_msg(wpa_s, MSG_INFO, P2P_EVENT_PROV_DISC_PBC_RESP MACSTR
+ "%s", MAC2STR(peer), params);
+
+ wpas_notify_p2p_provision_discovery(wpa_s, peer, 0 /* response */,
+ P2P_PROV_DISC_SUCCESS,
+ config_methods, generated_pin);
+}
+
+
+static void wpas_prov_disc_fail(void *ctx, const u8 *peer,
+ enum p2p_prov_disc_status status)
+{
+ struct wpa_supplicant *wpa_s = ctx;
+
+ if (wpa_s->p2p_fallback_to_go_neg) {
+ wpa_dbg(wpa_s, MSG_DEBUG, "P2P: PD for p2p_connect-auto "
+ "failed - fall back to GO Negotiation");
+ wpas_p2p_fallback_to_go_neg(wpa_s, 0);
+ return;
+ }
+
+ if (status == P2P_PROV_DISC_TIMEOUT_JOIN) {
+ wpa_s->pending_pd_before_join = 0;
+ wpa_printf(MSG_DEBUG, "P2P: Starting pending "
+ "join-existing-group operation (no ACK for PD "
+ "Req attempts)");
+ wpas_p2p_join_start(wpa_s);
+ return;
+ }
+
+ wpa_msg(wpa_s, MSG_INFO, P2P_EVENT_PROV_DISC_FAILURE
+ " p2p_dev_addr=" MACSTR " status=%d",
+ MAC2STR(peer), status);
+
+ wpas_notify_p2p_provision_discovery(wpa_s, peer, 0 /* response */,
+ status, 0, 0);
+}
+
+
+static u8 wpas_invitation_process(void *ctx, const u8 *sa, const u8 *bssid,
+ const u8 *go_dev_addr, const u8 *ssid,
+ size_t ssid_len, int *go, u8 *group_bssid,
+ int *force_freq, int persistent_group)
+{
+ struct wpa_supplicant *wpa_s = ctx;
+ struct wpa_ssid *s;
+ u8 cur_bssid[ETH_ALEN];
+ int res;
+ struct wpa_supplicant *grp;
+
+ if (!persistent_group) {
+ wpa_printf(MSG_DEBUG, "P2P: Invitation from " MACSTR
+ " to join an active group", MAC2STR(sa));
+ if (!is_zero_ether_addr(wpa_s->p2p_auth_invite) &&
+ (os_memcmp(go_dev_addr, wpa_s->p2p_auth_invite, ETH_ALEN)
+ == 0 ||
+ os_memcmp(sa, wpa_s->p2p_auth_invite, ETH_ALEN) == 0)) {
+ wpa_printf(MSG_DEBUG, "P2P: Accept previously "
+ "authorized invitation");
+ goto accept_inv;
+ }
+ /*
+ * Do not accept the invitation automatically; notify user and
+ * request approval.
+ */
+ return P2P_SC_FAIL_INFO_CURRENTLY_UNAVAILABLE;
+ }
+
+ grp = wpas_get_p2p_group(wpa_s, ssid, ssid_len, go);
+ if (grp) {
+ wpa_printf(MSG_DEBUG, "P2P: Accept invitation to already "
+ "running persistent group");
+ if (*go)
+ os_memcpy(group_bssid, grp->own_addr, ETH_ALEN);
+ goto accept_inv;
+ }
+
+ if (!wpa_s->conf->persistent_reconnect)
+ return P2P_SC_FAIL_INFO_CURRENTLY_UNAVAILABLE;
+
+ for (s = wpa_s->conf->ssid; s; s = s->next) {
+ if (s->disabled == 2 &&
+ os_memcmp(s->bssid, go_dev_addr, ETH_ALEN) == 0 &&
+ s->ssid_len == ssid_len &&
+ os_memcmp(ssid, s->ssid, ssid_len) == 0)
+ break;
+ }
+
+ if (!s) {
+ wpa_printf(MSG_DEBUG, "P2P: Invitation from " MACSTR
+ " requested reinvocation of an unknown group",
+ MAC2STR(sa));
+ return P2P_SC_FAIL_UNKNOWN_GROUP;
+ }
+
+ if (s->mode == WPAS_MODE_P2P_GO && !wpas_p2p_create_iface(wpa_s)) {
+ *go = 1;
+ if (wpa_s->wpa_state >= WPA_AUTHENTICATING) {
+ wpa_printf(MSG_DEBUG, "P2P: The only available "
+ "interface is already in use - reject "
+ "invitation");
+ return P2P_SC_FAIL_UNABLE_TO_ACCOMMODATE;
+ }
+ os_memcpy(group_bssid, wpa_s->own_addr, ETH_ALEN);
+ } else if (s->mode == WPAS_MODE_P2P_GO) {
+ *go = 1;
+ if (wpas_p2p_add_group_interface(wpa_s, WPA_IF_P2P_GO) < 0)
+ {
+ wpa_printf(MSG_ERROR, "P2P: Failed to allocate a new "
+ "interface address for the group");
+ return P2P_SC_FAIL_UNABLE_TO_ACCOMMODATE;
+ }
+ os_memcpy(group_bssid, wpa_s->pending_interface_addr,
+ ETH_ALEN);
+ }
+
+accept_inv:
+ if (wpa_s->current_ssid && wpa_drv_get_bssid(wpa_s, cur_bssid) == 0 &&
+ wpa_s->assoc_freq) {
+ wpa_printf(MSG_DEBUG, "P2P: Trying to force channel to match "
+ "the channel we are already using");
+ *force_freq = wpa_s->assoc_freq;
+ }
+
+ res = wpa_drv_shared_freq(wpa_s);
+ if (res > 0) {
+ wpa_printf(MSG_DEBUG, "P2P: Trying to force channel to match "
+ "with the channel we are already using on a "
+ "shared interface");
+ *force_freq = res;
+ }
+
+ return P2P_SC_SUCCESS;
+}
+
+
+static void wpas_invitation_received(void *ctx, const u8 *sa, const u8 *bssid,
+ const u8 *ssid, size_t ssid_len,
+ const u8 *go_dev_addr, u8 status,
+ int op_freq)
+{
+ struct wpa_supplicant *wpa_s = ctx;
+ struct wpa_ssid *s;
+
+ for (s = wpa_s->conf->ssid; s; s = s->next) {
+ if (s->disabled == 2 &&
+ s->ssid_len == ssid_len &&
+ os_memcmp(ssid, s->ssid, ssid_len) == 0)
+ break;
+ }
+
+ if (status == P2P_SC_SUCCESS) {
+ wpa_printf(MSG_DEBUG, "P2P: Invitation from peer " MACSTR
+ " was accepted; op_freq=%d MHz",
+ MAC2STR(sa), op_freq);
+ if (s) {
+ int go = s->mode == WPAS_MODE_P2P_GO;
+ wpas_p2p_group_add_persistent(
+ wpa_s, s, go, go ? op_freq : 0, 0);
+ } else if (bssid) {
+ wpa_s->user_initiated_pd = 0;
+ wpas_p2p_join(wpa_s, bssid, go_dev_addr,
+ wpa_s->p2p_wps_method, 0);
+ }
+ return;
+ }
+
+ if (status != P2P_SC_FAIL_INFO_CURRENTLY_UNAVAILABLE) {
+ wpa_printf(MSG_DEBUG, "P2P: Invitation from peer " MACSTR
+ " was rejected (status %u)", MAC2STR(sa), status);
+ return;
+ }
+
+ if (!s) {
+ if (bssid) {
+ wpa_msg(wpa_s, MSG_INFO, P2P_EVENT_INVITATION_RECEIVED
+ "sa=" MACSTR " go_dev_addr=" MACSTR
+ " bssid=" MACSTR " unknown-network",
+ MAC2STR(sa), MAC2STR(go_dev_addr),
+ MAC2STR(bssid));
+ } else {
+ wpa_msg(wpa_s, MSG_INFO, P2P_EVENT_INVITATION_RECEIVED
+ "sa=" MACSTR " go_dev_addr=" MACSTR
+ " unknown-network",
+ MAC2STR(sa), MAC2STR(go_dev_addr));
+ }
+ return;
+ }
+
+ wpa_msg(wpa_s, MSG_INFO, P2P_EVENT_INVITATION_RECEIVED "sa=" MACSTR
+ " persistent=%d", MAC2STR(sa), s->id);
+}
+
+
+static void wpas_invitation_result(void *ctx, int status, const u8 *bssid)
+{
+ struct wpa_supplicant *wpa_s = ctx;
+ struct wpa_ssid *ssid;
+
+ if (bssid) {
+ wpa_msg(wpa_s, MSG_INFO, P2P_EVENT_INVITATION_RESULT
+ "status=%d " MACSTR,
+ status, MAC2STR(bssid));
+ } else {
+ wpa_msg(wpa_s, MSG_INFO, P2P_EVENT_INVITATION_RESULT
+ "status=%d ", status);
+ }
+ wpas_notify_p2p_invitation_result(wpa_s, status, bssid);
+
+ if (wpa_s->pending_invite_ssid_id == -1)
+ return; /* Invitation to active group */
+
+ if (status != P2P_SC_SUCCESS) {
+ wpas_p2p_remove_pending_group_interface(wpa_s);
+ return;
+ }
+
+ ssid = wpa_config_get_network(wpa_s->conf,
+ wpa_s->pending_invite_ssid_id);
+ if (ssid == NULL) {
+ wpa_printf(MSG_ERROR, "P2P: Could not find persistent group "
+ "data matching with invitation");
+ return;
+ }
+
+ /*
+ * The peer could have missed our ctrl::ack frame for Invitation
+ * Response and continue retransmitting the frame. To reduce the
+ * likelihood of the peer not getting successful TX status for the
+ * Invitation Response frame, wait a short time here before starting
+ * the persistent group so that we will remain on the current channel to
+ * acknowledge any possible retransmission from the peer.
+ */
+ wpa_dbg(wpa_s, MSG_DEBUG, "P2P: 50 ms wait on current channel before "
+ "starting persistent group");
+ os_sleep(0, 50000);
+
+ wpas_p2p_group_add_persistent(wpa_s, ssid,
+ ssid->mode == WPAS_MODE_P2P_GO,
+ wpa_s->p2p_persistent_go_freq,
+ wpa_s->p2p_go_ht40);
+}
+
+
+static int wpas_p2p_disallowed_freq(struct wpa_global *global,
+ unsigned int freq)
+{
+ unsigned int i;
+
+ if (global->p2p_disallow_freq == NULL)
+ return 0;
+
+ for (i = 0; i < global->num_p2p_disallow_freq; i++) {
+ if (freq >= global->p2p_disallow_freq[i].min &&
+ freq <= global->p2p_disallow_freq[i].max)
+ return 1;
+ }
+
+ return 0;
+}
+
+
+static void wpas_p2p_add_chan(struct p2p_reg_class *reg, u8 chan)
+{
+ reg->channel[reg->channels] = chan;
+ reg->channels++;
+}
+
+
+static int wpas_p2p_default_channels(struct wpa_supplicant *wpa_s,
+ struct p2p_channels *chan)
+{
+ int i, cla = 0;
+
+ wpa_printf(MSG_DEBUG, "P2P: Enable operating classes for 2.4 GHz "
+ "band");
+
+ /* Operating class 81 - 2.4 GHz band channels 1..13 */
+ chan->reg_class[cla].reg_class = 81;
+ chan->reg_class[cla].channels = 0;
+ for (i = 0; i < 11; i++) {
+ if (!wpas_p2p_disallowed_freq(wpa_s->global, 2412 + i * 5))
+ wpas_p2p_add_chan(&chan->reg_class[cla], i + 1);
+ }
+ if (chan->reg_class[cla].channels)
+ cla++;
+
+ wpa_printf(MSG_DEBUG, "P2P: Enable operating classes for lower 5 GHz "
+ "band");
+
+ /* Operating class 115 - 5 GHz, channels 36-48 */
+ chan->reg_class[cla].reg_class = 115;
+ chan->reg_class[cla].channels = 0;
+ if (!wpas_p2p_disallowed_freq(wpa_s->global, 5000 + 36 * 5))
+ wpas_p2p_add_chan(&chan->reg_class[cla], 36);
+ if (!wpas_p2p_disallowed_freq(wpa_s->global, 5000 + 40 * 5))
+ wpas_p2p_add_chan(&chan->reg_class[cla], 40);
+ if (!wpas_p2p_disallowed_freq(wpa_s->global, 5000 + 44 * 5))
+ wpas_p2p_add_chan(&chan->reg_class[cla], 44);
+ if (!wpas_p2p_disallowed_freq(wpa_s->global, 5000 + 48 * 5))
+ wpas_p2p_add_chan(&chan->reg_class[cla], 48);
+ if (chan->reg_class[cla].channels)
+ cla++;
+
+ wpa_printf(MSG_DEBUG, "P2P: Enable operating classes for higher 5 GHz "
+ "band");
+
+ /* Operating class 124 - 5 GHz, channels 149,153,157,161 */
+ chan->reg_class[cla].reg_class = 124;
+ chan->reg_class[cla].channels = 0;
+ if (!wpas_p2p_disallowed_freq(wpa_s->global, 5000 + 149 * 5))
+ wpas_p2p_add_chan(&chan->reg_class[cla], 149);
+ if (!wpas_p2p_disallowed_freq(wpa_s->global, 5000 + 153 * 5))
+ wpas_p2p_add_chan(&chan->reg_class[cla], 153);
+ if (!wpas_p2p_disallowed_freq(wpa_s->global, 5000 + 156 * 5))
+ wpas_p2p_add_chan(&chan->reg_class[cla], 157);
+ if (!wpas_p2p_disallowed_freq(wpa_s->global, 5000 + 161 * 5))
+ wpas_p2p_add_chan(&chan->reg_class[cla], 161);
+ if (chan->reg_class[cla].channels)
+ cla++;
+
+ chan->reg_classes = cla;
+ return 0;
+}
+
+
+static struct hostapd_hw_modes * get_mode(struct hostapd_hw_modes *modes,
+ u16 num_modes,
+ enum hostapd_hw_mode mode)
+{
+ u16 i;
+
+ for (i = 0; i < num_modes; i++) {
+ if (modes[i].mode == mode)
+ return &modes[i];
+ }
+
+ return NULL;
+}
+
+
+static int has_channel(struct wpa_global *global,
+ struct hostapd_hw_modes *mode, u8 chan, int *flags)
+{
+ int i;
+ unsigned int freq;
+
+ freq = (mode->mode == HOSTAPD_MODE_IEEE80211A ? 5000 : 2407) +
+ chan * 5;
+ if (wpas_p2p_disallowed_freq(global, freq))
+ return 0;
+
+ for (i = 0; i < mode->num_channels; i++) {
+ if (mode->channels[i].chan == chan) {
+ if (flags)
+ *flags = mode->channels[i].flag;
+ return !(mode->channels[i].flag &
+ (HOSTAPD_CHAN_DISABLED |
+ HOSTAPD_CHAN_PASSIVE_SCAN |
+ HOSTAPD_CHAN_NO_IBSS |
+ HOSTAPD_CHAN_RADAR));
+ }
+ }
+
+ return 0;
+}
+
+
+struct p2p_oper_class_map {
+ enum hostapd_hw_mode mode;
+ u8 op_class;
+ u8 min_chan;
+ u8 max_chan;
+ u8 inc;
+ enum { BW20, BW40PLUS, BW40MINUS } bw;
+};
+
+static struct p2p_oper_class_map op_class[] = {
+ { HOSTAPD_MODE_IEEE80211G, 81, 1, 13, 1, BW20 },
+#if 0 /* Do not enable HT40 on 2 GHz for now */
+ { HOSTAPD_MODE_IEEE80211G, 83, 1, 9, 1, BW40PLUS },
+ { HOSTAPD_MODE_IEEE80211G, 84, 5, 13, 1, BW40MINUS },
+#endif
+ { HOSTAPD_MODE_IEEE80211A, 115, 36, 48, 4, BW20 },
+ { HOSTAPD_MODE_IEEE80211A, 124, 149, 161, 4, BW20 },
+ { HOSTAPD_MODE_IEEE80211A, 116, 36, 44, 8, BW40PLUS },
+ { HOSTAPD_MODE_IEEE80211A, 117, 40, 48, 8, BW40MINUS },
+ { HOSTAPD_MODE_IEEE80211A, 126, 149, 157, 8, BW40PLUS },
+ { HOSTAPD_MODE_IEEE80211A, 127, 153, 161, 8, BW40MINUS },
+ { -1, 0, 0, 0, 0, BW20 }
+};
+
+
+static int wpas_p2p_verify_channel(struct wpa_supplicant *wpa_s,
+ struct hostapd_hw_modes *mode,
+ u8 channel, u8 bw)
+{
+ int flag;
+
+ if (!has_channel(wpa_s->global, mode, channel, &flag))
+ return -1;
+ if (bw == BW40MINUS &&
+ (!(flag & HOSTAPD_CHAN_HT40MINUS) ||
+ !has_channel(wpa_s->global, mode, channel - 4, NULL)))
+ return 0;
+ if (bw == BW40PLUS &&
+ (!(flag & HOSTAPD_CHAN_HT40PLUS) ||
+ !has_channel(wpa_s->global, mode, channel + 4, NULL)))
+ return 0;
+ return 1;
+}
+
+
+static int wpas_p2p_setup_channels(struct wpa_supplicant *wpa_s,
+ struct p2p_channels *chan)
+{
+ struct hostapd_hw_modes *mode;
+ int cla, op;
+
+ if (wpa_s->hw.modes == NULL) {
+ wpa_printf(MSG_DEBUG, "P2P: Driver did not support fetching "
+ "of all supported channels; assume dualband "
+ "support");
+ return wpas_p2p_default_channels(wpa_s, chan);
+ }
+
+ cla = 0;
+
+ for (op = 0; op_class[op].op_class; op++) {
+ struct p2p_oper_class_map *o = &op_class[op];
+ u8 ch;
+ struct p2p_reg_class *reg = NULL;
+
+ mode = get_mode(wpa_s->hw.modes, wpa_s->hw.num_modes, o->mode);
+ if (mode == NULL)
+ continue;
+ for (ch = o->min_chan; ch <= o->max_chan; ch += o->inc) {
+ if (wpas_p2p_verify_channel(wpa_s, mode, ch, o->bw) < 1)
+ continue;
+ if (reg == NULL) {
+ wpa_printf(MSG_DEBUG, "P2P: Add operating "
+ "class %u", o->op_class);
+ reg = &chan->reg_class[cla];
+ cla++;
+ reg->reg_class = o->op_class;
+ }
+ reg->channel[reg->channels] = ch;
+ reg->channels++;
+ }
+ if (reg) {
+ wpa_hexdump(MSG_DEBUG, "P2P: Channels",
+ reg->channel, reg->channels);
+ }
+ }
+
+ chan->reg_classes = cla;
+
+ return 0;
+}
+
+
+int wpas_p2p_get_ht40_mode(struct wpa_supplicant *wpa_s,
+ struct hostapd_hw_modes *mode, u8 channel)
+{
+ int op, ret;
+
+ for (op = 0; op_class[op].op_class; op++) {
+ struct p2p_oper_class_map *o = &op_class[op];
+ u8 ch;
+
+ for (ch = o->min_chan; ch <= o->max_chan; ch += o->inc) {
+ if (o->mode != HOSTAPD_MODE_IEEE80211A ||
+ o->bw == BW20 || ch != channel)
+ continue;
+ ret = wpas_p2p_verify_channel(wpa_s, mode, ch, o->bw);
+ if (ret < 0)
+ continue;
+ else if (ret > 0)
+ return (o->bw == BW40MINUS) ? -1 : 1;
+ else
+ return 0;
+ }
+ }
+ return 0;
+}
+
+
+static int wpas_get_noa(void *ctx, const u8 *interface_addr, u8 *buf,
+ size_t buf_len)
+{
+ struct wpa_supplicant *wpa_s = ctx;
+
+ for (wpa_s = wpa_s->global->ifaces; wpa_s; wpa_s = wpa_s->next) {
+ if (os_memcmp(wpa_s->own_addr, interface_addr, ETH_ALEN) == 0)
+ break;
+ }
+ if (wpa_s == NULL)
+ return -1;
+
+ return wpa_drv_get_noa(wpa_s, buf, buf_len);
+}
+
+
+static int wpas_go_connected(void *ctx, const u8 *dev_addr)
+{
+ struct wpa_supplicant *wpa_s = ctx;
+
+ for (wpa_s = wpa_s->global->ifaces; wpa_s; wpa_s = wpa_s->next) {
+ struct wpa_ssid *ssid = wpa_s->current_ssid;
+ if (ssid == NULL)
+ continue;
+ if (ssid->mode != WPAS_MODE_INFRA)
+ continue;
+ if (wpa_s->wpa_state != WPA_COMPLETED &&
+ wpa_s->wpa_state != WPA_GROUP_HANDSHAKE)
+ continue;
+ if (os_memcmp(wpa_s->go_dev_addr, dev_addr, ETH_ALEN) == 0)
+ return 1;
+ }
+
+ return 0;
+}
+
+
+/**
+ * wpas_p2p_init - Initialize P2P module for %wpa_supplicant
+ * @global: Pointer to global data from wpa_supplicant_init()
+ * @wpa_s: Pointer to wpa_supplicant data from wpa_supplicant_add_iface()
+ * Returns: 0 on success, -1 on failure
+ */
+int wpas_p2p_init(struct wpa_global *global, struct wpa_supplicant *wpa_s)
+{
+ struct p2p_config p2p;
+ unsigned int r;
+ int i;
+
+ if (!(wpa_s->drv_flags & WPA_DRIVER_FLAGS_P2P_CAPABLE))
+ return 0;
+
+ if (global->p2p)
+ return 0;
+
+ if (wpa_s->drv_flags & WPA_DRIVER_FLAGS_P2P_MGMT) {
+ struct p2p_params params;
+
+ wpa_printf(MSG_DEBUG, "P2P: Use driver-based P2P management");
+ os_memset(&params, 0, sizeof(params));
+ params.dev_name = wpa_s->conf->device_name;
+ os_memcpy(params.pri_dev_type, wpa_s->conf->device_type,
+ WPS_DEV_TYPE_LEN);
+ params.num_sec_dev_types = wpa_s->conf->num_sec_device_types;
+ os_memcpy(params.sec_dev_type,
+ wpa_s->conf->sec_device_type,
+ params.num_sec_dev_types * WPS_DEV_TYPE_LEN);
+
+ if (wpa_drv_p2p_set_params(wpa_s, &params) < 0)
+ return -1;
+
+ return 0;
+ }
+
+ os_memset(&p2p, 0, sizeof(p2p));
+ p2p.msg_ctx = wpa_s;
+ p2p.cb_ctx = wpa_s;
+ p2p.p2p_scan = wpas_p2p_scan;
+ p2p.send_action = wpas_send_action;
+ p2p.send_action_done = wpas_send_action_done;
+ p2p.go_neg_completed = wpas_go_neg_completed;
+ p2p.go_neg_req_rx = wpas_go_neg_req_rx;
+ p2p.dev_found = wpas_dev_found;
+ p2p.dev_lost = wpas_dev_lost;
+ p2p.start_listen = wpas_start_listen;
+ p2p.stop_listen = wpas_stop_listen;
+ p2p.send_probe_resp = wpas_send_probe_resp;
+ p2p.sd_request = wpas_sd_request;
+ p2p.sd_response = wpas_sd_response;
+ p2p.prov_disc_req = wpas_prov_disc_req;
+ p2p.prov_disc_resp = wpas_prov_disc_resp;
+ p2p.prov_disc_fail = wpas_prov_disc_fail;
+ p2p.invitation_process = wpas_invitation_process;
+ p2p.invitation_received = wpas_invitation_received;
+ p2p.invitation_result = wpas_invitation_result;
+ p2p.get_noa = wpas_get_noa;
+ p2p.go_connected = wpas_go_connected;
+
+ os_memcpy(wpa_s->global->p2p_dev_addr, wpa_s->own_addr, ETH_ALEN);
+ os_memcpy(p2p.dev_addr, wpa_s->global->p2p_dev_addr, ETH_ALEN);
+ p2p.dev_name = wpa_s->conf->device_name;
+ p2p.manufacturer = wpa_s->conf->manufacturer;
+ p2p.model_name = wpa_s->conf->model_name;
+ p2p.model_number = wpa_s->conf->model_number;
+ p2p.serial_number = wpa_s->conf->serial_number;
+ if (wpa_s->wps) {
+ os_memcpy(p2p.uuid, wpa_s->wps->uuid, 16);
+ p2p.config_methods = wpa_s->wps->config_methods;
+ }
+
+ if (wpa_s->conf->p2p_listen_reg_class &&
+ wpa_s->conf->p2p_listen_channel) {
+ p2p.reg_class = wpa_s->conf->p2p_listen_reg_class;
+ p2p.channel = wpa_s->conf->p2p_listen_channel;
+ } else {
+ p2p.reg_class = 81;
+ /*
+ * Pick one of the social channels randomly as the listen
+ * channel.
+ */
+ os_get_random((u8 *) &r, sizeof(r));
+ p2p.channel = 1 + (r % 3) * 5;
+ }
+ wpa_printf(MSG_DEBUG, "P2P: Own listen channel: %d", p2p.channel);
+
+ if (wpa_s->conf->p2p_oper_reg_class &&
+ wpa_s->conf->p2p_oper_channel) {
+ p2p.op_reg_class = wpa_s->conf->p2p_oper_reg_class;
+ p2p.op_channel = wpa_s->conf->p2p_oper_channel;
+ p2p.cfg_op_channel = 1;
+ wpa_printf(MSG_DEBUG, "P2P: Configured operating channel: "
+ "%d:%d", p2p.op_reg_class, p2p.op_channel);
+
+ } else {
+ p2p.op_reg_class = 81;
+ /*
+ * Use random operation channel from (1, 6, 11) if no other
+ * preference is indicated.
+ */
+ os_get_random((u8 *) &r, sizeof(r));
+ p2p.op_channel = 1 + (r % 3) * 5;
+ p2p.cfg_op_channel = 0;
+ wpa_printf(MSG_DEBUG, "P2P: Random operating channel: "
+ "%d:%d", p2p.op_reg_class, p2p.op_channel);
+ }
+ if (wpa_s->conf->country[0] && wpa_s->conf->country[1]) {
+ os_memcpy(p2p.country, wpa_s->conf->country, 2);
+ p2p.country[2] = 0x04;
+ } else
+ os_memcpy(p2p.country, "XX\x04", 3);
+
+ if (wpas_p2p_setup_channels(wpa_s, &p2p.channels)) {
+ wpa_printf(MSG_ERROR, "P2P: Failed to configure supported "
+ "channel list");
+ return -1;
+ }
+
+ os_memcpy(p2p.pri_dev_type, wpa_s->conf->device_type,
+ WPS_DEV_TYPE_LEN);
+
+ p2p.num_sec_dev_types = wpa_s->conf->num_sec_device_types;
+ os_memcpy(p2p.sec_dev_type, wpa_s->conf->sec_device_type,
+ p2p.num_sec_dev_types * WPS_DEV_TYPE_LEN);
+
+ p2p.concurrent_operations = !!(wpa_s->drv_flags &
+ WPA_DRIVER_FLAGS_P2P_CONCURRENT);
+
+ p2p.max_peers = 100;
+
+ if (wpa_s->conf->p2p_ssid_postfix) {
+ p2p.ssid_postfix_len =
+ os_strlen(wpa_s->conf->p2p_ssid_postfix);
+ if (p2p.ssid_postfix_len > sizeof(p2p.ssid_postfix))
+ p2p.ssid_postfix_len = sizeof(p2p.ssid_postfix);
+ os_memcpy(p2p.ssid_postfix, wpa_s->conf->p2p_ssid_postfix,
+ p2p.ssid_postfix_len);
+ }
+
+ p2p.p2p_intra_bss = wpa_s->conf->p2p_intra_bss;
+
+ p2p.max_listen = wpa_s->max_remain_on_chan;
+
+ global->p2p = p2p_init(&p2p);
+ if (global->p2p == NULL)
+ return -1;
+ global->p2p_init_wpa_s = wpa_s;
+
+ for (i = 0; i < MAX_WPS_VENDOR_EXT; i++) {
+ if (wpa_s->conf->wps_vendor_ext[i] == NULL)
+ continue;
+ p2p_add_wps_vendor_extension(
+ global->p2p, wpa_s->conf->wps_vendor_ext[i]);
+ }
+
+ return 0;
+}
+
+
+/**
+ * wpas_p2p_deinit - Deinitialize per-interface P2P data
+ * @wpa_s: Pointer to wpa_supplicant data from wpa_supplicant_add_iface()
+ *
+ * This function deinitialize per-interface P2P data.
+ */
+void wpas_p2p_deinit(struct wpa_supplicant *wpa_s)
+{
+ if (wpa_s->driver && wpa_s->drv_priv)
+ wpa_drv_probe_req_report(wpa_s, 0);
+
+ if (wpa_s->go_params) {
+ /* Clear any stored provisioning info */
+ p2p_clear_provisioning_info(
+ wpa_s->global->p2p,
+ wpa_s->go_params->peer_device_addr);
+ }
+
+ os_free(wpa_s->go_params);
+ wpa_s->go_params = NULL;
+ eloop_cancel_timeout(wpas_p2p_group_formation_timeout, wpa_s, NULL);
+ eloop_cancel_timeout(wpas_p2p_join_scan, wpa_s, NULL);
+ wpa_s->p2p_long_listen = 0;
+ eloop_cancel_timeout(wpas_p2p_long_listen_timeout, wpa_s, NULL);
+ eloop_cancel_timeout(wpas_p2p_group_idle_timeout, wpa_s, NULL);
+ wpas_p2p_remove_pending_group_interface(wpa_s);
+
+ /* TODO: remove group interface from the driver if this wpa_s instance
+ * is on top of a P2P group interface */
+}
+
+
+/**
+ * wpas_p2p_deinit_global - Deinitialize global P2P module
+ * @global: Pointer to global data from wpa_supplicant_init()
+ *
+ * This function deinitializes the global (per device) P2P module.
+ */
+void wpas_p2p_deinit_global(struct wpa_global *global)
+{
+ struct wpa_supplicant *wpa_s, *tmp;
+
+ wpa_s = global->ifaces;
+ if (wpa_s)
+ wpas_p2p_service_flush(wpa_s);
+
+ if (global->p2p == NULL)
+ return;
+
+ /* Remove remaining P2P group interfaces */
+ while (wpa_s && wpa_s->p2p_group_interface != NOT_P2P_GROUP_INTERFACE)
+ wpa_s = wpa_s->next;
+ while (wpa_s) {
+ tmp = global->ifaces;
+ while (tmp &&
+ (tmp == wpa_s ||
+ tmp->p2p_group_interface == NOT_P2P_GROUP_INTERFACE)) {
+ tmp = tmp->next;
+ }
+ if (tmp == NULL)
+ break;
+ /* Disconnect from the P2P group and deinit the interface */
+ wpas_p2p_disconnect(tmp);
+ }
+
+ /*
+ * Deinit GO data on any possibly remaining interface (if main
+ * interface is used as GO).
+ */
+ for (wpa_s = global->ifaces; wpa_s; wpa_s = wpa_s->next) {
+ if (wpa_s->ap_iface)
+ wpas_p2p_group_deinit(wpa_s);
+ }
+
+ p2p_deinit(global->p2p);
+ global->p2p = NULL;
+ global->p2p_init_wpa_s = NULL;
+}
+
+
+static int wpas_p2p_create_iface(struct wpa_supplicant *wpa_s)
+{
+ if (wpa_s->conf->p2p_no_group_iface)
+ return 0; /* separate interface disabled per configuration */
+ if (wpa_s->drv_flags &
+ (WPA_DRIVER_FLAGS_P2P_DEDICATED_INTERFACE |
+ WPA_DRIVER_FLAGS_P2P_MGMT_AND_NON_P2P))
+ return 1; /* P2P group requires a new interface in every case
+ */
+ if (!(wpa_s->drv_flags & WPA_DRIVER_FLAGS_P2P_CONCURRENT))
+ return 0; /* driver does not support concurrent operations */
+ if (wpa_s->global->ifaces->next)
+ return 1; /* more that one interface already in use */
+ if (wpa_s->wpa_state >= WPA_AUTHENTICATING)
+ return 1; /* this interface is already in use */
+ return 0;
+}
+
+
+static int wpas_p2p_start_go_neg(struct wpa_supplicant *wpa_s,
+ const u8 *peer_addr,
+ enum p2p_wps_method wps_method,
+ int go_intent, const u8 *own_interface_addr,
+ unsigned int force_freq, int persistent_group,
+ struct wpa_ssid *ssid, unsigned int pref_freq)
+{
+ if (persistent_group && wpa_s->conf->persistent_reconnect)
+ persistent_group = 2;
+
+ if (wpa_s->drv_flags & WPA_DRIVER_FLAGS_P2P_MGMT) {
+ return wpa_drv_p2p_connect(wpa_s, peer_addr, wps_method,
+ go_intent, own_interface_addr,
+ force_freq, persistent_group);
+ }
+
+ /*
+ * Increase GO config timeout if HT40 is used since it takes some time
+ * to scan channels for coex purposes before the BSS can be started.
+ */
+ p2p_set_config_timeout(wpa_s->global->p2p,
+ wpa_s->p2p_go_ht40 ? 255 : 100, 20);
+
+ return p2p_connect(wpa_s->global->p2p, peer_addr, wps_method,
+ go_intent, own_interface_addr, force_freq,
+ persistent_group, ssid ? ssid->ssid : NULL,
+ ssid ? ssid->ssid_len : 0,
+ wpa_s->p2p_pd_before_go_neg, pref_freq);
+}
+
+
+static int wpas_p2p_auth_go_neg(struct wpa_supplicant *wpa_s,
+ const u8 *peer_addr,
+ enum p2p_wps_method wps_method,
+ int go_intent, const u8 *own_interface_addr,
+ unsigned int force_freq, int persistent_group,
+ struct wpa_ssid *ssid, unsigned int pref_freq)
+{
+ if (persistent_group && wpa_s->conf->persistent_reconnect)
+ persistent_group = 2;
+
+ if (wpa_s->drv_flags & WPA_DRIVER_FLAGS_P2P_MGMT)
+ return -1;
+
+ return p2p_authorize(wpa_s->global->p2p, peer_addr, wps_method,
+ go_intent, own_interface_addr, force_freq,
+ persistent_group, ssid ? ssid->ssid : NULL,
+ ssid ? ssid->ssid_len : 0, pref_freq);
+}
+
+
+static void wpas_p2p_check_join_scan_limit(struct wpa_supplicant *wpa_s)
+{
+ wpa_s->p2p_join_scan_count++;
+ wpa_printf(MSG_DEBUG, "P2P: Join scan attempt %d",
+ wpa_s->p2p_join_scan_count);
+ if (wpa_s->p2p_join_scan_count > P2P_MAX_JOIN_SCAN_ATTEMPTS) {
+ wpa_printf(MSG_DEBUG, "P2P: Failed to find GO " MACSTR
+ " for join operationg - stop join attempt",
+ MAC2STR(wpa_s->pending_join_iface_addr));
+ eloop_cancel_timeout(wpas_p2p_join_scan, wpa_s, NULL);
+ if (wpa_s->p2p_auto_pd) {
+ wpa_s->p2p_auto_pd = 0;
+ wpa_msg(wpa_s, MSG_INFO, P2P_EVENT_PROV_DISC_FAILURE
+ " p2p_dev_addr=" MACSTR " status=N/A",
+ MAC2STR(wpa_s->pending_join_dev_addr));
+ return;
+ }
+ wpa_msg(wpa_s->parent, MSG_INFO,
+ P2P_EVENT_GROUP_FORMATION_FAILURE);
+ }
+}
+
+
+static int wpas_check_freq_conflict(struct wpa_supplicant *wpa_s, int freq)
+{
+ struct wpa_supplicant *iface;
+ int shared_freq;
+ u8 bssid[ETH_ALEN];
+
+ if (wpa_s->drv_flags & WPA_DRIVER_FLAGS_MULTI_CHANNEL_CONCURRENT)
+ return 0;
+
+ for (iface = wpa_s->global->ifaces; iface; iface = iface->next) {
+ if (!wpas_p2p_create_iface(wpa_s) && iface == wpa_s)
+ continue;
+ if (iface->current_ssid == NULL || iface->assoc_freq == 0)
+ continue;
+ if (iface->current_ssid->mode == WPAS_MODE_AP ||
+ iface->current_ssid->mode == WPAS_MODE_P2P_GO)
+ shared_freq = iface->current_ssid->frequency;
+ else if (wpa_drv_get_bssid(iface, bssid) == 0)
+ shared_freq = iface->assoc_freq;
+ else
+ shared_freq = 0;
+
+ if (shared_freq && freq != shared_freq) {
+ wpa_printf(MSG_DEBUG, "P2P: Frequency conflict - %s "
+ "connected on %d MHz - new connection on "
+ "%d MHz", iface->ifname, shared_freq, freq);
+ return 1;
+ }
+ }
+
+ shared_freq = wpa_drv_shared_freq(wpa_s);
+ if (shared_freq > 0 && shared_freq != freq) {
+ wpa_printf(MSG_DEBUG, "P2P: Frequency conflict - shared "
+ "virtual interface connected on %d MHz - new "
+ "connection on %d MHz", shared_freq, freq);
+ return 1;
+ }
+
+ return 0;
+}
+
+
+static int wpas_p2p_peer_go(struct wpa_supplicant *wpa_s,
+ const u8 *peer_dev_addr)
+{
+ struct wpa_bss *bss;
+ int updated;
+
+ bss = wpa_bss_get_p2p_dev_addr(wpa_s, peer_dev_addr);
+ if (bss == NULL)
+ return -1;
+ if (bss->last_update_idx < wpa_s->bss_update_idx) {
+ wpa_printf(MSG_DEBUG, "P2P: Peer BSS entry not updated in the "
+ "last scan");
+ return 0;
+ }
+
+ updated = os_time_before(&wpa_s->p2p_auto_started, &bss->last_update);
+ wpa_printf(MSG_DEBUG, "P2P: Current BSS entry for peer updated at "
+ "%ld.%06ld (%supdated in last scan)",
+ bss->last_update.sec, bss->last_update.usec,
+ updated ? "": "not ");
+
+ return updated;
+}
+
+
+static void wpas_p2p_scan_res_join(struct wpa_supplicant *wpa_s,
+ struct wpa_scan_results *scan_res)
+{
+ struct wpa_bss *bss;
+ int freq;
+ u8 iface_addr[ETH_ALEN];
+
+ eloop_cancel_timeout(wpas_p2p_join_scan, wpa_s, NULL);
+
+ if (wpa_s->global->p2p_disabled)
+ return;
+
+ wpa_printf(MSG_DEBUG, "P2P: Scan results received (%d BSS) for %sjoin",
+ scan_res ? (int) scan_res->num : -1,
+ wpa_s->p2p_auto_join ? "auto_" : "");
+
+ if (scan_res)
+ wpas_p2p_scan_res_handler(wpa_s, scan_res);
+
+ if (wpa_s->p2p_auto_pd) {
+ int join = wpas_p2p_peer_go(wpa_s,
+ wpa_s->pending_join_dev_addr);
+ if (join == 0 &&
+ wpa_s->auto_pd_scan_retry < P2P_AUTO_PD_SCAN_ATTEMPTS) {
+ wpa_s->auto_pd_scan_retry++;
+ bss = wpa_bss_get_bssid(wpa_s,
+ wpa_s->pending_join_dev_addr);
+ if (bss) {
+ freq = bss->freq;
+ wpa_printf(MSG_DEBUG, "P2P: Scan retry %d for "
+ "the peer " MACSTR " at %d MHz",
+ wpa_s->auto_pd_scan_retry,
+ MAC2STR(wpa_s->
+ pending_join_dev_addr),
+ freq);
+ wpas_p2p_join_scan_req(wpa_s, freq);
+ return;
+ }
+ }
+
+ if (join < 0)
+ join = 0;
+
+ wpa_s->p2p_auto_pd = 0;
+ wpa_s->pending_pd_use = join ? AUTO_PD_JOIN : AUTO_PD_GO_NEG;
+ wpa_printf(MSG_DEBUG, "P2P: Auto PD with " MACSTR " join=%d",
+ MAC2STR(wpa_s->pending_join_dev_addr), join);
+ if (p2p_prov_disc_req(wpa_s->global->p2p,
+ wpa_s->pending_join_dev_addr,
+ wpa_s->pending_pd_config_methods, join,
+ 0, wpa_s->user_initiated_pd) < 0) {
+ wpa_s->p2p_auto_pd = 0;
+ wpa_msg(wpa_s, MSG_INFO, P2P_EVENT_PROV_DISC_FAILURE
+ " p2p_dev_addr=" MACSTR " status=N/A",
+ MAC2STR(wpa_s->pending_join_dev_addr));
+ }
+ return;
+ }
+
+ if (wpa_s->p2p_auto_join) {
+ int join = wpas_p2p_peer_go(wpa_s,
+ wpa_s->pending_join_dev_addr);
+ if (join < 0) {
+ wpa_printf(MSG_DEBUG, "P2P: Peer was not found to be "
+ "running a GO -> use GO Negotiation");
+ wpas_p2p_connect(wpa_s, wpa_s->pending_join_dev_addr,
+ wpa_s->p2p_pin, wpa_s->p2p_wps_method,
+ wpa_s->p2p_persistent_group, 0, 0, 0,
+ wpa_s->p2p_go_intent,
+ wpa_s->p2p_connect_freq,
+ wpa_s->p2p_persistent_id,
+ wpa_s->p2p_pd_before_go_neg,
+ wpa_s->p2p_go_ht40);
+ return;
+ }
+
+ wpa_printf(MSG_DEBUG, "P2P: Peer was found running GO%s -> "
+ "try to join the group", join ? "" :
+ " in older scan");
+ if (!join)
+ wpa_s->p2p_fallback_to_go_neg = 1;
+ }
+
+ freq = p2p_get_oper_freq(wpa_s->global->p2p,
+ wpa_s->pending_join_iface_addr);
+ if (freq < 0 &&
+ p2p_get_interface_addr(wpa_s->global->p2p,
+ wpa_s->pending_join_dev_addr,
+ iface_addr) == 0 &&
+ os_memcmp(iface_addr, wpa_s->pending_join_dev_addr, ETH_ALEN) != 0)
+ {
+ wpa_printf(MSG_DEBUG, "P2P: Overwrite pending interface "
+ "address for join from " MACSTR " to " MACSTR
+ " based on newly discovered P2P peer entry",
+ MAC2STR(wpa_s->pending_join_iface_addr),
+ MAC2STR(iface_addr));
+ os_memcpy(wpa_s->pending_join_iface_addr, iface_addr,
+ ETH_ALEN);
+
+ freq = p2p_get_oper_freq(wpa_s->global->p2p,
+ wpa_s->pending_join_iface_addr);
+ }
+ if (freq >= 0) {
+ wpa_printf(MSG_DEBUG, "P2P: Target GO operating frequency "
+ "from P2P peer table: %d MHz", freq);
+ }
+ bss = wpa_bss_get_bssid(wpa_s, wpa_s->pending_join_iface_addr);
+ if (bss) {
+ freq = bss->freq;
+ wpa_printf(MSG_DEBUG, "P2P: Target GO operating frequency "
+ "from BSS table: %d MHz", freq);
+ }
+ if (freq > 0) {
+ u16 method;
+
+ if (wpas_check_freq_conflict(wpa_s, freq) > 0) {
+ wpa_msg(wpa_s->parent, MSG_INFO,
+ P2P_EVENT_GROUP_FORMATION_FAILURE
+ "reason=FREQ_CONFLICT");
+ return;
+ }
+
+ wpa_printf(MSG_DEBUG, "P2P: Send Provision Discovery Request "
+ "prior to joining an existing group (GO " MACSTR
+ " freq=%u MHz)",
+ MAC2STR(wpa_s->pending_join_dev_addr), freq);
+ wpa_s->pending_pd_before_join = 1;
+
+ switch (wpa_s->pending_join_wps_method) {
+ case WPS_PIN_DISPLAY:
+ method = WPS_CONFIG_KEYPAD;
+ break;
+ case WPS_PIN_KEYPAD:
+ method = WPS_CONFIG_DISPLAY;
+ break;
+ case WPS_PBC:
+ method = WPS_CONFIG_PUSHBUTTON;
+ break;
+ default:
+ method = 0;
+ break;
+ }
+
+ if ((p2p_get_provisioning_info(wpa_s->global->p2p,
+ wpa_s->pending_join_dev_addr) ==
+ method)) {
+ /*
+ * We have already performed provision discovery for
+ * joining the group. Proceed directly to join
+ * operation without duplicated provision discovery. */
+ wpa_printf(MSG_DEBUG, "P2P: Provision discovery "
+ "with " MACSTR " already done - proceed to "
+ "join",
+ MAC2STR(wpa_s->pending_join_dev_addr));
+ wpa_s->pending_pd_before_join = 0;
+ goto start;
+ }
+
+ if (p2p_prov_disc_req(wpa_s->global->p2p,
+ wpa_s->pending_join_dev_addr, method, 1,
+ freq, wpa_s->user_initiated_pd) < 0) {
+ wpa_printf(MSG_DEBUG, "P2P: Failed to send Provision "
+ "Discovery Request before joining an "
+ "existing group");
+ wpa_s->pending_pd_before_join = 0;
+ goto start;
+ }
+ return;
+ }
+
+ wpa_printf(MSG_DEBUG, "P2P: Failed to find BSS/GO - try again later");
+ eloop_cancel_timeout(wpas_p2p_join_scan, wpa_s, NULL);
+ eloop_register_timeout(1, 0, wpas_p2p_join_scan, wpa_s, NULL);
+ wpas_p2p_check_join_scan_limit(wpa_s);
+ return;
+
+start:
+ /* Start join operation immediately */
+ wpas_p2p_join_start(wpa_s);
+}
+
+
+static void wpas_p2p_join_scan_req(struct wpa_supplicant *wpa_s, int freq)
+{
+ int ret;
+ struct wpa_driver_scan_params params;
+ struct wpabuf *wps_ie, *ies;
+ size_t ielen;
+ int freqs[2] = { 0, 0 };
+
+ os_memset(&params, 0, sizeof(params));
+
+ /* P2P Wildcard SSID */
+ params.num_ssids = 1;
+ params.ssids[0].ssid = (u8 *) P2P_WILDCARD_SSID;
+ params.ssids[0].ssid_len = P2P_WILDCARD_SSID_LEN;
+
+ wpa_s->wps->dev.p2p = 1;
+ wps_ie = wps_build_probe_req_ie(DEV_PW_DEFAULT, &wpa_s->wps->dev,
+ wpa_s->wps->uuid, WPS_REQ_ENROLLEE, 0,
+ NULL);
+ if (wps_ie == NULL) {
+ wpas_p2p_scan_res_join(wpa_s, NULL);
+ return;
+ }
+
+ ielen = p2p_scan_ie_buf_len(wpa_s->global->p2p);
+ ies = wpabuf_alloc(wpabuf_len(wps_ie) + ielen);
+ if (ies == NULL) {
+ wpabuf_free(wps_ie);
+ wpas_p2p_scan_res_join(wpa_s, NULL);
+ return;
+ }
+ wpabuf_put_buf(ies, wps_ie);
+ wpabuf_free(wps_ie);
+
+ p2p_scan_ie(wpa_s->global->p2p, ies, NULL);
+
+ params.p2p_probe = 1;
+ params.extra_ies = wpabuf_head(ies);
+ params.extra_ies_len = wpabuf_len(ies);
+ if (freq > 0) {
+ freqs[0] = freq;
+ params.freqs = freqs;
+ }
+
+ /*
+ * Run a scan to update BSS table and start Provision Discovery once
+ * the new scan results become available.
+ */
+ ret = wpa_drv_scan(wpa_s, &params);
+ if (!ret)
+ wpa_s->scan_res_handler = wpas_p2p_scan_res_join;
+
+ wpabuf_free(ies);
+
+ if (ret) {
+ wpa_printf(MSG_DEBUG, "P2P: Failed to start scan for join - "
+ "try again later");
+ eloop_cancel_timeout(wpas_p2p_join_scan, wpa_s, NULL);
+ eloop_register_timeout(1, 0, wpas_p2p_join_scan, wpa_s, NULL);
+ wpas_p2p_check_join_scan_limit(wpa_s);
+ }
+}
+
+
+static void wpas_p2p_join_scan(void *eloop_ctx, void *timeout_ctx)
+{
+ struct wpa_supplicant *wpa_s = eloop_ctx;
+ wpas_p2p_join_scan_req(wpa_s, 0);
+}
+
+
+static int wpas_p2p_join(struct wpa_supplicant *wpa_s, const u8 *iface_addr,
+ const u8 *dev_addr, enum p2p_wps_method wps_method,
+ int auto_join)
+{
+ wpa_printf(MSG_DEBUG, "P2P: Request to join existing group (iface "
+ MACSTR " dev " MACSTR ")%s",
+ MAC2STR(iface_addr), MAC2STR(dev_addr),
+ auto_join ? " (auto_join)" : "");
+
+ wpa_s->p2p_auto_pd = 0;
+ wpa_s->p2p_auto_join = !!auto_join;
+ os_memcpy(wpa_s->pending_join_iface_addr, iface_addr, ETH_ALEN);
+ os_memcpy(wpa_s->pending_join_dev_addr, dev_addr, ETH_ALEN);
+ wpa_s->pending_join_wps_method = wps_method;
+
+ /* Make sure we are not running find during connection establishment */
+ wpas_p2p_stop_find(wpa_s);
+
+ wpa_s->p2p_join_scan_count = 0;
+ wpas_p2p_join_scan(wpa_s, NULL);
+ return 0;
+}
+
+
+static int wpas_p2p_join_start(struct wpa_supplicant *wpa_s)
+{
+ struct wpa_supplicant *group;
+ struct p2p_go_neg_results res;
+ struct wpa_bss *bss;
+
+ group = wpas_p2p_get_group_iface(wpa_s, 0, 0);
+ if (group == NULL)
+ return -1;
+ if (group != wpa_s) {
+ os_memcpy(group->p2p_pin, wpa_s->p2p_pin,
+ sizeof(group->p2p_pin));
+ group->p2p_wps_method = wpa_s->p2p_wps_method;
+ }
+
+ group->p2p_in_provisioning = 1;
+ wpa_s->global->p2p_group_formation = wpa_s;
+ group->p2p_fallback_to_go_neg = wpa_s->p2p_fallback_to_go_neg;
+
+ os_memset(&res, 0, sizeof(res));
+ os_memcpy(res.peer_interface_addr, wpa_s->pending_join_iface_addr,
+ ETH_ALEN);
+ res.wps_method = wpa_s->pending_join_wps_method;
+ bss = wpa_bss_get_bssid(wpa_s, wpa_s->pending_join_iface_addr);
+ if (bss) {
+ res.freq = bss->freq;
+ res.ssid_len = bss->ssid_len;
+ os_memcpy(res.ssid, bss->ssid, bss->ssid_len);
+ }
+
+ if (wpa_s->off_channel_freq || wpa_s->roc_waiting_drv_freq) {
+ wpa_printf(MSG_DEBUG, "P2P: Cancel remain-on-channel prior to "
+ "starting client");
+ wpa_drv_cancel_remain_on_channel(wpa_s);
+ wpa_s->off_channel_freq = 0;
+ wpa_s->roc_waiting_drv_freq = 0;
+ }
+ wpas_start_wps_enrollee(group, &res);
+
+ /*
+ * Allow a longer timeout for join-a-running-group than normal 15
+ * second group formation timeout since the GO may not have authorized
+ * our connection yet.
+ */
+ eloop_cancel_timeout(wpas_p2p_group_formation_timeout, wpa_s, NULL);
+ eloop_register_timeout(60, 0, wpas_p2p_group_formation_timeout,
+ wpa_s, NULL);
+
+ return 0;
+}
+
+
+/**
+ * wpas_p2p_connect - Request P2P Group Formation to be started
+ * @wpa_s: Pointer to wpa_supplicant data from wpa_supplicant_add_iface()
+ * @peer_addr: Address of the peer P2P Device
+ * @pin: PIN to use during provisioning or %NULL to indicate PBC mode
+ * @persistent_group: Whether to create a persistent group
+ * @auto_join: Whether to select join vs. GO Negotiation automatically
+ * @join: Whether to join an existing group (as a client) instead of starting
+ * Group Owner negotiation; @peer_addr is BSSID in that case
+ * @auth: Whether to only authorize the connection instead of doing that and
+ * initiating Group Owner negotiation
+ * @go_intent: GO Intent or -1 to use default
+ * @freq: Frequency for the group or 0 for auto-selection
+ * @persistent_id: Persistent group credentials to use for forcing GO
+ * parameters or -1 to generate new values (SSID/passphrase)
+ * @pd: Whether to send Provision Discovery prior to GO Negotiation as an
+ * interoperability workaround when initiating group formation
+ * @ht40: Start GO with 40 MHz channel width
+ * Returns: 0 or new PIN (if pin was %NULL) on success, -1 on unspecified
+ * failure, -2 on failure due to channel not currently available,
+ * -3 if forced channel is not supported
+ */
+int wpas_p2p_connect(struct wpa_supplicant *wpa_s, const u8 *peer_addr,
+ const char *pin, enum p2p_wps_method wps_method,
+ int persistent_group, int auto_join, int join, int auth,
+ int go_intent, int freq, int persistent_id, int pd,
+ int ht40)
+{
+ int force_freq = 0, pref_freq = 0, oper_freq = 0;
+ u8 bssid[ETH_ALEN];
+ int ret = 0;
+ enum wpa_driver_if_type iftype;
+ const u8 *if_addr;
+ struct wpa_ssid *ssid = NULL;
+
+ if (wpa_s->global->p2p_disabled || wpa_s->global->p2p == NULL)
+ return -1;
+
+ if (persistent_id >= 0) {
+ ssid = wpa_config_get_network(wpa_s->conf, persistent_id);
+ if (ssid == NULL || ssid->disabled != 2 ||
+ ssid->mode != WPAS_MODE_P2P_GO)
+ return -1;
+ }
+
+ if (go_intent < 0)
+ go_intent = wpa_s->conf->p2p_go_intent;
+
+ if (!auth)
+ wpa_s->p2p_long_listen = 0;
+
+ wpa_s->p2p_wps_method = wps_method;
+ wpa_s->p2p_persistent_group = !!persistent_group;
+ wpa_s->p2p_persistent_id = persistent_id;
+ wpa_s->p2p_go_intent = go_intent;
+ wpa_s->p2p_connect_freq = freq;
+ wpa_s->p2p_fallback_to_go_neg = 0;
+ wpa_s->p2p_pd_before_go_neg = !!pd;
+ wpa_s->p2p_go_ht40 = !!ht40;
+
+ if (pin)
+ os_strlcpy(wpa_s->p2p_pin, pin, sizeof(wpa_s->p2p_pin));
+ else if (wps_method == WPS_PIN_DISPLAY) {
+ ret = wps_generate_pin();
+ os_snprintf(wpa_s->p2p_pin, sizeof(wpa_s->p2p_pin), "%08d",
+ ret);
+ wpa_printf(MSG_DEBUG, "P2P: Randomly generated PIN: %s",
+ wpa_s->p2p_pin);
+ } else
+ wpa_s->p2p_pin[0] = '\0';
+
+ if (join || auto_join) {
+ u8 iface_addr[ETH_ALEN], dev_addr[ETH_ALEN];
+ if (auth) {
+ wpa_printf(MSG_DEBUG, "P2P: Authorize invitation to "
+ "connect a running group from " MACSTR,
+ MAC2STR(peer_addr));
+ os_memcpy(wpa_s->p2p_auth_invite, peer_addr, ETH_ALEN);
+ return ret;
+ }
+ os_memcpy(dev_addr, peer_addr, ETH_ALEN);
+ if (p2p_get_interface_addr(wpa_s->global->p2p, peer_addr,
+ iface_addr) < 0) {
+ os_memcpy(iface_addr, peer_addr, ETH_ALEN);
+ p2p_get_dev_addr(wpa_s->global->p2p, peer_addr,
+ dev_addr);
+ }
+ if (auto_join) {
+ os_get_time(&wpa_s->p2p_auto_started);
+ wpa_printf(MSG_DEBUG, "P2P: Auto join started at "
+ "%ld.%06ld",
+ wpa_s->p2p_auto_started.sec,
+ wpa_s->p2p_auto_started.usec);
+ }
+ wpa_s->user_initiated_pd = 1;
+ if (wpas_p2p_join(wpa_s, iface_addr, dev_addr, wps_method,
+ auto_join) < 0)
+ return -1;
+ return ret;
+ }
+
+ if (wpa_s->current_ssid && wpa_drv_get_bssid(wpa_s, bssid) == 0 &&
+ wpa_s->assoc_freq)
+ oper_freq = wpa_s->assoc_freq;
+ else {
+ oper_freq = wpa_drv_shared_freq(wpa_s);
+ if (oper_freq < 0)
+ oper_freq = 0;
+ }
+
+ if (freq > 0) {
+ if (!p2p_supported_freq(wpa_s->global->p2p, freq)) {
+ wpa_printf(MSG_DEBUG, "P2P: The forced channel "
+ "(%u MHz) is not supported for P2P uses",
+ freq);
+ return -3;
+ }
+
+ if (oper_freq > 0 && freq != oper_freq &&
+ !(wpa_s->drv_flags &
+ WPA_DRIVER_FLAGS_MULTI_CHANNEL_CONCURRENT)) {
+ wpa_printf(MSG_DEBUG, "P2P: Cannot start P2P group "
+ "on %u MHz while connected on another "
+ "channel (%u MHz)", freq, oper_freq);
+ return -2;
+ }
+ wpa_printf(MSG_DEBUG, "P2P: Trying to force us to use the "
+ "requested channel (%u MHz)", freq);
+ force_freq = freq;
+ } else if (oper_freq > 0 &&
+ !p2p_supported_freq(wpa_s->global->p2p, oper_freq)) {
+ if (!(wpa_s->drv_flags &
+ WPA_DRIVER_FLAGS_MULTI_CHANNEL_CONCURRENT)) {
+ wpa_printf(MSG_DEBUG, "P2P: Cannot start P2P group "
+ "while connected on non-P2P supported "
+ "channel (%u MHz)", oper_freq);
+ return -2;
+ }
+ wpa_printf(MSG_DEBUG, "P2P: Current operating channel "
+ "(%u MHz) not available for P2P - try to use "
+ "another channel", oper_freq);
+ force_freq = 0;
+ } else if (oper_freq > 0 &&
+ (wpa_s->drv_flags &
+ WPA_DRIVER_FLAGS_MULTI_CHANNEL_CONCURRENT)) {
+ wpa_printf(MSG_DEBUG, "P2P: Trying to prefer the channel we "
+ "are already using (%u MHz) on another interface",
+ oper_freq);
+ pref_freq = oper_freq;
+ } else if (oper_freq > 0) {
+ wpa_printf(MSG_DEBUG, "P2P: Trying to force us to use the "
+ "channel we are already using (%u MHz) on another "
+ "interface", oper_freq);
+ force_freq = oper_freq;
+ }
+
+ wpa_s->create_p2p_iface = wpas_p2p_create_iface(wpa_s);
+
+ if (wpa_s->create_p2p_iface) {
+ /* Prepare to add a new interface for the group */
+ iftype = WPA_IF_P2P_GROUP;
+ if (go_intent == 15)
+ iftype = WPA_IF_P2P_GO;
+ if (wpas_p2p_add_group_interface(wpa_s, iftype) < 0) {
+ wpa_printf(MSG_ERROR, "P2P: Failed to allocate a new "
+ "interface for the group");
+ return -1;
+ }
+
+ if_addr = wpa_s->pending_interface_addr;
+ } else
+ if_addr = wpa_s->own_addr;
+
+ if (auth) {
+ if (wpas_p2p_auth_go_neg(wpa_s, peer_addr, wps_method,
+ go_intent, if_addr,
+ force_freq, persistent_group, ssid,
+ pref_freq) < 0)
+ return -1;
+ return ret;
+ }
+
+ if (wpas_p2p_start_go_neg(wpa_s, peer_addr, wps_method,
+ go_intent, if_addr, force_freq,
+ persistent_group, ssid, pref_freq) < 0) {
+ if (wpa_s->create_p2p_iface)
+ wpas_p2p_remove_pending_group_interface(wpa_s);
+ return -1;
+ }
+ return ret;
+}
+
+
+/**
+ * wpas_p2p_remain_on_channel_cb - Indication of remain-on-channel start
+ * @wpa_s: Pointer to wpa_supplicant data from wpa_supplicant_add_iface()
+ * @freq: Frequency of the channel in MHz
+ * @duration: Duration of the stay on the channel in milliseconds
+ *
+ * This callback is called when the driver indicates that it has started the
+ * requested remain-on-channel duration.
+ */
+void wpas_p2p_remain_on_channel_cb(struct wpa_supplicant *wpa_s,
+ unsigned int freq, unsigned int duration)
+{
+ if (wpa_s->global->p2p_disabled || wpa_s->global->p2p == NULL)
+ return;
+ if (wpa_s->off_channel_freq == wpa_s->pending_listen_freq) {
+ p2p_listen_cb(wpa_s->global->p2p, wpa_s->pending_listen_freq,
+ wpa_s->pending_listen_duration);
+ wpa_s->pending_listen_freq = 0;
+ }
+}
+
+
+static int wpas_p2p_listen_start(struct wpa_supplicant *wpa_s,
+ unsigned int timeout)
+{
+ /* Limit maximum Listen state time based on driver limitation. */
+ if (timeout > wpa_s->max_remain_on_chan)
+ timeout = wpa_s->max_remain_on_chan;
+
+ if (wpa_s->drv_flags & WPA_DRIVER_FLAGS_P2P_MGMT)
+ return wpa_drv_p2p_listen(wpa_s, timeout);
+
+ return p2p_listen(wpa_s->global->p2p, timeout);
+}
+
+
+/**
+ * wpas_p2p_cancel_remain_on_channel_cb - Remain-on-channel timeout
+ * @wpa_s: Pointer to wpa_supplicant data from wpa_supplicant_add_iface()
+ * @freq: Frequency of the channel in MHz
+ *
+ * This callback is called when the driver indicates that a remain-on-channel
+ * operation has been completed, i.e., the duration on the requested channel
+ * has timed out.
+ */
+void wpas_p2p_cancel_remain_on_channel_cb(struct wpa_supplicant *wpa_s,
+ unsigned int freq)
+{
+ wpa_printf(MSG_DEBUG, "P2P: Cancel remain-on-channel callback "
+ "(p2p_long_listen=%d ms pending_action_tx=%p)",
+ wpa_s->p2p_long_listen, offchannel_pending_action_tx(wpa_s));
+ if (wpa_s->global->p2p_disabled || wpa_s->global->p2p == NULL)
+ return;
+ if (p2p_listen_end(wpa_s->global->p2p, freq) > 0)
+ return; /* P2P module started a new operation */
+ if (offchannel_pending_action_tx(wpa_s))
+ return;
+ if (wpa_s->p2p_long_listen > 0)
+ wpa_s->p2p_long_listen -= wpa_s->max_remain_on_chan;
+ if (wpa_s->p2p_long_listen > 0) {
+ wpa_printf(MSG_DEBUG, "P2P: Continuing long Listen state");
+ wpas_p2p_listen_start(wpa_s, wpa_s->p2p_long_listen);
+ }
+}
+
+
+/**
+ * wpas_p2p_group_remove - Remove a P2P group
+ * @wpa_s: Pointer to wpa_supplicant data from wpa_supplicant_add_iface()
+ * @ifname: Network interface name of the group interface or "*" to remove all
+ * groups
+ * Returns: 0 on success, -1 on failure
+ *
+ * This function is used to remove a P2P group. This can be used to disconnect
+ * from a group in which the local end is a P2P Client or to end a P2P Group in
+ * case the local end is the Group Owner. If a virtual network interface was
+ * created for this group, that interface will be removed. Otherwise, only the
+ * configured P2P group network will be removed from the interface.
+ */
+int wpas_p2p_group_remove(struct wpa_supplicant *wpa_s, const char *ifname)
+{
+ struct wpa_global *global = wpa_s->global;
+
+ if (os_strcmp(ifname, "*") == 0) {
+ struct wpa_supplicant *prev;
+ wpa_s = global->ifaces;
+ while (wpa_s) {
+ prev = wpa_s;
+ wpa_s = wpa_s->next;
+ wpas_p2p_disconnect(prev);
+ }
+ return 0;
+ }
+
+ for (wpa_s = global->ifaces; wpa_s; wpa_s = wpa_s->next) {
+ if (os_strcmp(wpa_s->ifname, ifname) == 0)
+ break;
+ }
+
+ return wpas_p2p_disconnect(wpa_s);
+}
+
+
+static int wpas_p2p_init_go_params(struct wpa_supplicant *wpa_s,
+ struct p2p_go_neg_results *params,
+ int freq, int ht40)
+{
+ u8 bssid[ETH_ALEN];
+ int res;
+
+ os_memset(params, 0, sizeof(*params));
+ params->role_go = 1;
+ params->ht40 = ht40;
+ if (freq) {
+ wpa_printf(MSG_DEBUG, "P2P: Set GO freq based on forced "
+ "frequency %d MHz", freq);
+ params->freq = freq;
+ } else if (wpa_s->conf->p2p_oper_reg_class == 81 &&
+ wpa_s->conf->p2p_oper_channel >= 1 &&
+ wpa_s->conf->p2p_oper_channel <= 11) {
+ params->freq = 2407 + 5 * wpa_s->conf->p2p_oper_channel;
+ wpa_printf(MSG_DEBUG, "P2P: Set GO freq based on configured "
+ "frequency %d MHz", params->freq);
+ } else if (wpa_s->conf->p2p_oper_reg_class == 115 ||
+ wpa_s->conf->p2p_oper_reg_class == 124) {
+ params->freq = 5000 + 5 * wpa_s->conf->p2p_oper_channel;
+ wpa_printf(MSG_DEBUG, "P2P: Set GO freq based on configured "
+ "frequency %d MHz", params->freq);
+ } else if (wpa_s->conf->p2p_oper_channel == 0 &&
+ wpa_s->best_overall_freq > 0 &&
+ p2p_supported_freq(wpa_s->global->p2p,
+ wpa_s->best_overall_freq)) {
+ params->freq = wpa_s->best_overall_freq;
+ wpa_printf(MSG_DEBUG, "P2P: Set GO freq based on best overall "
+ "channel %d MHz", params->freq);
+ } else if (wpa_s->conf->p2p_oper_channel == 0 &&
+ wpa_s->best_24_freq > 0 &&
+ p2p_supported_freq(wpa_s->global->p2p,
+ wpa_s->best_24_freq)) {
+ params->freq = wpa_s->best_24_freq;
+ wpa_printf(MSG_DEBUG, "P2P: Set GO freq based on best 2.4 GHz "
+ "channel %d MHz", params->freq);
+ } else if (wpa_s->conf->p2p_oper_channel == 0 &&
+ wpa_s->best_5_freq > 0 &&
+ p2p_supported_freq(wpa_s->global->p2p,
+ wpa_s->best_5_freq)) {
+ params->freq = wpa_s->best_5_freq;
+ wpa_printf(MSG_DEBUG, "P2P: Set GO freq based on best 5 GHz "
+ "channel %d MHz", params->freq);
+ } else {
+ int chan;
+ for (chan = 0; chan < 11; chan++) {
+ params->freq = 2412 + chan * 5;
+ if (!wpas_p2p_disallowed_freq(wpa_s->global,
+ params->freq))
+ break;
+ }
+ if (chan == 11) {
+ wpa_printf(MSG_DEBUG, "P2P: No 2.4 GHz channel "
+ "allowed");
+ return -1;
+ }
+ wpa_printf(MSG_DEBUG, "P2P: Set GO freq %d MHz (no preference "
+ "known)", params->freq);
+ }
+
+ if (wpa_s->current_ssid && wpa_drv_get_bssid(wpa_s, bssid) == 0 &&
+ wpa_s->assoc_freq && !freq) {
+ wpa_printf(MSG_DEBUG, "P2P: Force GO on the channel we are "
+ "already using");
+ params->freq = wpa_s->assoc_freq;
+ }
+
+ res = wpa_drv_shared_freq(wpa_s);
+ if (res > 0 && !freq) {
+ wpa_printf(MSG_DEBUG, "P2P: Force GO on the channel we are "
+ "already using on a shared interface");
+ params->freq = res;
+ } else if (res > 0 && freq != res &&
+ !(wpa_s->drv_flags &
+ WPA_DRIVER_FLAGS_MULTI_CHANNEL_CONCURRENT)) {
+ wpa_printf(MSG_DEBUG, "P2P: Cannot start P2P group on %u MHz "
+ "while connected on another channel (%u MHz)",
+ freq, res);
+ return -1;
+ }
+
+ return 0;
+}
+
+
+static struct wpa_supplicant *
+wpas_p2p_get_group_iface(struct wpa_supplicant *wpa_s, int addr_allocated,
+ int go)
+{
+ struct wpa_supplicant *group_wpa_s;
+
+ if (!wpas_p2p_create_iface(wpa_s)) {
+ wpa_dbg(wpa_s, MSG_DEBUG, "P2P: Use same interface for group "
+ "operations");
+ return wpa_s;
+ }
+
+ if (wpas_p2p_add_group_interface(wpa_s, go ? WPA_IF_P2P_GO :
+ WPA_IF_P2P_CLIENT) < 0) {
+ wpa_msg(wpa_s, MSG_ERROR, "P2P: Failed to add group interface");
+ return NULL;
+ }
+ group_wpa_s = wpas_p2p_init_group_interface(wpa_s, go);
+ if (group_wpa_s == NULL) {
+ wpa_msg(wpa_s, MSG_ERROR, "P2P: Failed to initialize group "
+ "interface");
+ wpas_p2p_remove_pending_group_interface(wpa_s);
+ return NULL;
+ }
+
+ wpa_dbg(wpa_s, MSG_DEBUG, "P2P: Use separate group interface %s",
+ group_wpa_s->ifname);
+ return group_wpa_s;
+}
+
+
+/**
+ * wpas_p2p_group_add - Add a new P2P group with local end as Group Owner
+ * @wpa_s: Pointer to wpa_supplicant data from wpa_supplicant_add_iface()
+ * @persistent_group: Whether to create a persistent group
+ * @freq: Frequency for the group or 0 to indicate no hardcoding
+ * Returns: 0 on success, -1 on failure
+ *
+ * This function creates a new P2P group with the local end as the Group Owner,
+ * i.e., without using Group Owner Negotiation.
+ */
+int wpas_p2p_group_add(struct wpa_supplicant *wpa_s, int persistent_group,
+ int freq, int ht40)
+{
+ struct p2p_go_neg_results params;
+ unsigned int r;
+
+ if (wpa_s->global->p2p_disabled || wpa_s->global->p2p == NULL)
+ return -1;
+
+ /* Make sure we are not running find during connection establishment */
+ wpa_printf(MSG_DEBUG, "P2P: Stop any on-going P2P FIND");
+ wpas_p2p_stop_find_oper(wpa_s);
+
+ if (freq == 2) {
+ wpa_printf(MSG_DEBUG, "P2P: Request to start GO on 2.4 GHz "
+ "band");
+ if (wpa_s->best_24_freq > 0 &&
+ p2p_supported_freq(wpa_s->global->p2p,
+ wpa_s->best_24_freq)) {
+ freq = wpa_s->best_24_freq;
+ wpa_printf(MSG_DEBUG, "P2P: Use best 2.4 GHz band "
+ "channel: %d MHz", freq);
+ } else {
+ os_get_random((u8 *) &r, sizeof(r));
+ freq = 2412 + (r % 3) * 25;
+ wpa_printf(MSG_DEBUG, "P2P: Use random 2.4 GHz band "
+ "channel: %d MHz", freq);
+ }
+ }
+
+ if (freq == 5) {
+ wpa_printf(MSG_DEBUG, "P2P: Request to start GO on 5 GHz "
+ "band");
+ if (wpa_s->best_5_freq > 0 &&
+ p2p_supported_freq(wpa_s->global->p2p,
+ wpa_s->best_5_freq)) {
+ freq = wpa_s->best_5_freq;
+ wpa_printf(MSG_DEBUG, "P2P: Use best 5 GHz band "
+ "channel: %d MHz", freq);
+ } else {
+ os_get_random((u8 *) &r, sizeof(r));
+ freq = 5180 + (r % 4) * 20;
+ if (!p2p_supported_freq(wpa_s->global->p2p, freq)) {
+ wpa_printf(MSG_DEBUG, "P2P: Could not select "
+ "5 GHz channel for P2P group");
+ return -1;
+ }
+ wpa_printf(MSG_DEBUG, "P2P: Use random 5 GHz band "
+ "channel: %d MHz", freq);
+ }
+ }
+
+ if (freq > 0 && !p2p_supported_freq(wpa_s->global->p2p, freq)) {
+ wpa_printf(MSG_DEBUG, "P2P: The forced channel for GO "
+ "(%u MHz) is not supported for P2P uses",
+ freq);
+ return -1;
+ }
+
+ if (wpas_p2p_init_go_params(wpa_s, &params, freq, ht40))
+ return -1;
+ if (params.freq &&
+ !p2p_supported_freq(wpa_s->global->p2p, params.freq)) {
+ wpa_printf(MSG_DEBUG, "P2P: The selected channel for GO "
+ "(%u MHz) is not supported for P2P uses",
+ params.freq);
+ return -1;
+ }
+ p2p_go_params(wpa_s->global->p2p, &params);
+ params.persistent_group = persistent_group;
+
+ wpa_s = wpas_p2p_get_group_iface(wpa_s, 0, 1);
+ if (wpa_s == NULL)
+ return -1;
+ wpas_start_wps_go(wpa_s, &params, 0);
+
+ return 0;
+}
+
+
+static int wpas_start_p2p_client(struct wpa_supplicant *wpa_s,
+ struct wpa_ssid *params, int addr_allocated)
+{
+ struct wpa_ssid *ssid;
+
+ wpa_s = wpas_p2p_get_group_iface(wpa_s, addr_allocated, 0);
+ if (wpa_s == NULL)
+ return -1;
+
+ wpa_supplicant_ap_deinit(wpa_s);
+
+ ssid = wpa_config_add_network(wpa_s->conf);
+ if (ssid == NULL)
+ return -1;
+ wpa_config_set_network_defaults(ssid);
+ ssid->temporary = 1;
+ ssid->proto = WPA_PROTO_RSN;
+ ssid->pairwise_cipher = WPA_CIPHER_CCMP;
+ ssid->group_cipher = WPA_CIPHER_CCMP;
+ ssid->key_mgmt = WPA_KEY_MGMT_PSK;
+ ssid->ssid = os_malloc(params->ssid_len);
+ if (ssid->ssid == NULL) {
+ wpa_config_remove_network(wpa_s->conf, ssid->id);
+ return -1;
+ }
+ os_memcpy(ssid->ssid, params->ssid, params->ssid_len);
+ ssid->ssid_len = params->ssid_len;
+ ssid->p2p_group = 1;
+ ssid->export_keys = 1;
+ if (params->psk_set) {
+ os_memcpy(ssid->psk, params->psk, 32);
+ ssid->psk_set = 1;
+ }
+ if (params->passphrase)
+ ssid->passphrase = os_strdup(params->passphrase);
+
+ wpa_supplicant_select_network(wpa_s, ssid);
+
+ wpa_s->show_group_started = 1;
+
+ return 0;
+}
+
+
+int wpas_p2p_group_add_persistent(struct wpa_supplicant *wpa_s,
+ struct wpa_ssid *ssid, int addr_allocated,
+ int freq, int ht40)
+{
+ struct p2p_go_neg_results params;
+ int go = 0;
+
+ if (ssid->disabled != 2 || ssid->ssid == NULL)
+ return -1;
+
+ if (wpas_get_p2p_group(wpa_s, ssid->ssid, ssid->ssid_len, &go) &&
+ go == (ssid->mode == WPAS_MODE_P2P_GO)) {
+ wpa_printf(MSG_DEBUG, "P2P: Requested persistent group is "
+ "already running");
+ return 0;
+ }
+
+ /* Make sure we are not running find during connection establishment */
+ wpas_p2p_stop_find_oper(wpa_s);
+
+ wpa_s->p2p_fallback_to_go_neg = 0;
+
+ if (ssid->mode == WPAS_MODE_INFRA)
+ return wpas_start_p2p_client(wpa_s, ssid, addr_allocated);
+
+ if (ssid->mode != WPAS_MODE_P2P_GO)
+ return -1;
+
+ if (wpas_p2p_init_go_params(wpa_s, &params, freq, ht40))
+ return -1;
+
+ params.role_go = 1;
+ params.psk_set = ssid->psk_set;
+ if (params.psk_set)
+ os_memcpy(params.psk, ssid->psk, sizeof(params.psk));
+ if (ssid->passphrase) {
+ if (os_strlen(ssid->passphrase) >= sizeof(params.passphrase)) {
+ wpa_printf(MSG_ERROR, "P2P: Invalid passphrase in "
+ "persistent group");
+ return -1;
+ }
+ os_strlcpy(params.passphrase, ssid->passphrase,
+ sizeof(params.passphrase));
+ }
+ os_memcpy(params.ssid, ssid->ssid, ssid->ssid_len);
+ params.ssid_len = ssid->ssid_len;
+ params.persistent_group = 1;
+
+ wpa_s = wpas_p2p_get_group_iface(wpa_s, addr_allocated, 1);
+ if (wpa_s == NULL)
+ return -1;
+
+ wpas_start_wps_go(wpa_s, &params, 0);
+
+ return 0;
+}
+
+
+static void wpas_p2p_ie_update(void *ctx, struct wpabuf *beacon_ies,
+ struct wpabuf *proberesp_ies)
+{
+ struct wpa_supplicant *wpa_s = ctx;
+ if (wpa_s->ap_iface) {
+ struct hostapd_data *hapd = wpa_s->ap_iface->bss[0];
+ if (!(hapd->conf->p2p & P2P_GROUP_OWNER)) {
+ wpabuf_free(beacon_ies);
+ wpabuf_free(proberesp_ies);
+ return;
+ }
+ if (beacon_ies) {
+ wpabuf_free(hapd->p2p_beacon_ie);
+ hapd->p2p_beacon_ie = beacon_ies;
+ }
+ wpabuf_free(hapd->p2p_probe_resp_ie);
+ hapd->p2p_probe_resp_ie = proberesp_ies;
+ } else {
+ wpabuf_free(beacon_ies);
+ wpabuf_free(proberesp_ies);
+ }
+ wpa_supplicant_ap_update_beacon(wpa_s);
+}
+
+
+static void wpas_p2p_idle_update(void *ctx, int idle)
+{
+ struct wpa_supplicant *wpa_s = ctx;
+ if (!wpa_s->ap_iface)
+ return;
+ wpa_printf(MSG_DEBUG, "P2P: GO - group %sidle", idle ? "" : "not ");
+ if (idle)
+ wpas_p2p_set_group_idle_timeout(wpa_s);
+ else
+ eloop_cancel_timeout(wpas_p2p_group_idle_timeout, wpa_s, NULL);
+}
+
+
+struct p2p_group * wpas_p2p_group_init(struct wpa_supplicant *wpa_s,
+ struct wpa_ssid *ssid)
+{
+ struct p2p_group *group;
+ struct p2p_group_config *cfg;
+
+ if (wpa_s->drv_flags & WPA_DRIVER_FLAGS_P2P_MGMT)
+ return NULL;
+ if (wpa_s->global->p2p_disabled || wpa_s->global->p2p == NULL)
+ return NULL;
+
+ cfg = os_zalloc(sizeof(*cfg));
+ if (cfg == NULL)
+ return NULL;
+
+ if (ssid->p2p_persistent_group && wpa_s->conf->persistent_reconnect)
+ cfg->persistent_group = 2;
+ else if (ssid->p2p_persistent_group)
+ cfg->persistent_group = 1;
+ os_memcpy(cfg->interface_addr, wpa_s->own_addr, ETH_ALEN);
+ if (wpa_s->max_stations &&
+ wpa_s->max_stations < wpa_s->conf->max_num_sta)
+ cfg->max_clients = wpa_s->max_stations;
+ else
+ cfg->max_clients = wpa_s->conf->max_num_sta;
+ os_memcpy(cfg->ssid, ssid->ssid, ssid->ssid_len);
+ cfg->ssid_len = ssid->ssid_len;
+ cfg->cb_ctx = wpa_s;
+ cfg->ie_update = wpas_p2p_ie_update;
+ cfg->idle_update = wpas_p2p_idle_update;
+
+ group = p2p_group_init(wpa_s->global->p2p, cfg);
+ if (group == NULL)
+ os_free(cfg);
+ if (ssid->mode != WPAS_MODE_P2P_GROUP_FORMATION)
+ p2p_group_notif_formation_done(group);
+ wpa_s->p2p_group = group;
+ return group;
+}
+
+
+void wpas_p2p_wps_success(struct wpa_supplicant *wpa_s, const u8 *peer_addr,
+ int registrar)
+{
+ struct wpa_ssid *ssid = wpa_s->current_ssid;
+
+ if (!wpa_s->p2p_in_provisioning) {
+ wpa_printf(MSG_DEBUG, "P2P: Ignore WPS success event - P2P "
+ "provisioning not in progress");
+ return;
+ }
+
+ if (ssid && ssid->mode == WPAS_MODE_INFRA) {
+ u8 go_dev_addr[ETH_ALEN];
+ os_memcpy(go_dev_addr, wpa_s->bssid, ETH_ALEN);
+ wpas_p2p_persistent_group(wpa_s, go_dev_addr, ssid->ssid,
+ ssid->ssid_len);
+ /* Clear any stored provisioning info */
+ p2p_clear_provisioning_info(wpa_s->global->p2p, go_dev_addr);
+ }
+
+ eloop_cancel_timeout(wpas_p2p_group_formation_timeout, wpa_s->parent,
+ NULL);
+ if (ssid && ssid->mode == WPAS_MODE_INFRA) {
+ /*
+ * Use a separate timeout for initial data connection to
+ * complete to allow the group to be removed automatically if
+ * something goes wrong in this step before the P2P group idle
+ * timeout mechanism is taken into use.
+ */
+ eloop_register_timeout(P2P_MAX_INITIAL_CONN_WAIT, 0,
+ wpas_p2p_group_formation_timeout,
+ wpa_s->parent, NULL);
+ }
+ if (wpa_s->global->p2p)
+ p2p_wps_success_cb(wpa_s->global->p2p, peer_addr);
+ else if (wpa_s->drv_flags & WPA_DRIVER_FLAGS_P2P_MGMT)
+ wpa_drv_wps_success_cb(wpa_s, peer_addr);
+ wpas_group_formation_completed(wpa_s, 1);
+}
+
+
+void wpas_p2p_wps_failed(struct wpa_supplicant *wpa_s,
+ struct wps_event_fail *fail)
+{
+ if (!wpa_s->p2p_in_provisioning) {
+ wpa_printf(MSG_DEBUG, "P2P: Ignore WPS fail event - P2P "
+ "provisioning not in progress");
+ return;
+ }
+
+ if (wpa_s->go_params) {
+ p2p_clear_provisioning_info(
+ wpa_s->global->p2p,
+ wpa_s->go_params->peer_device_addr);
+ }
+
+ wpas_notify_p2p_wps_failed(wpa_s, fail);
+}
+
+
+int wpas_p2p_prov_disc(struct wpa_supplicant *wpa_s, const u8 *peer_addr,
+ const char *config_method,
+ enum wpas_p2p_prov_disc_use use)
+{
+ u16 config_methods;
+
+ wpa_s->p2p_fallback_to_go_neg = 0;
+ wpa_s->pending_pd_use = NORMAL_PD;
+ if (os_strncmp(config_method, "display", 7) == 0)
+ config_methods = WPS_CONFIG_DISPLAY;
+ else if (os_strncmp(config_method, "keypad", 6) == 0)
+ config_methods = WPS_CONFIG_KEYPAD;
+ else if (os_strncmp(config_method, "pbc", 3) == 0 ||
+ os_strncmp(config_method, "pushbutton", 10) == 0)
+ config_methods = WPS_CONFIG_PUSHBUTTON;
+ else {
+ wpa_printf(MSG_DEBUG, "P2P: Unknown config method");
+ return -1;
+ }
+
+ if (use == WPAS_P2P_PD_AUTO) {
+ os_memcpy(wpa_s->pending_join_dev_addr, peer_addr, ETH_ALEN);
+ wpa_s->pending_pd_config_methods = config_methods;
+ wpa_s->p2p_auto_pd = 1;
+ wpa_s->p2p_auto_join = 0;
+ wpa_s->pending_pd_before_join = 0;
+ wpa_s->auto_pd_scan_retry = 0;
+ wpas_p2p_stop_find(wpa_s);
+ wpa_s->p2p_join_scan_count = 0;
+ os_get_time(&wpa_s->p2p_auto_started);
+ wpa_printf(MSG_DEBUG, "P2P: Auto PD started at %ld.%06ld",
+ wpa_s->p2p_auto_started.sec,
+ wpa_s->p2p_auto_started.usec);
+ wpas_p2p_join_scan(wpa_s, NULL);
+ return 0;
+ }
+
+ if (wpa_s->drv_flags & WPA_DRIVER_FLAGS_P2P_MGMT) {
+ return wpa_drv_p2p_prov_disc_req(wpa_s, peer_addr,
+ config_methods,
+ use == WPAS_P2P_PD_FOR_JOIN);
+ }
+
+ if (wpa_s->global->p2p == NULL || wpa_s->global->p2p_disabled)
+ return -1;
+
+ return p2p_prov_disc_req(wpa_s->global->p2p, peer_addr,
+ config_methods, use == WPAS_P2P_PD_FOR_JOIN,
+ 0, 1);
+}
+
+
+int wpas_p2p_scan_result_text(const u8 *ies, size_t ies_len, char *buf,
+ char *end)
+{
+ return p2p_scan_result_text(ies, ies_len, buf, end);
+}
+
+
+static void wpas_p2p_clear_pending_action_tx(struct wpa_supplicant *wpa_s)
+{
+ if (!offchannel_pending_action_tx(wpa_s))
+ return;
+
+ wpa_printf(MSG_DEBUG, "P2P: Drop pending Action TX due to new "
+ "operation request");
+ offchannel_clear_pending_action_tx(wpa_s);
+}
+
+
+int wpas_p2p_find(struct wpa_supplicant *wpa_s, unsigned int timeout,
+ enum p2p_discovery_type type,
+ unsigned int num_req_dev_types, const u8 *req_dev_types,
+ const u8 *dev_id, unsigned int search_delay)
+{
+ wpas_p2p_clear_pending_action_tx(wpa_s);
+ wpa_s->p2p_long_listen = 0;
+
+ if (wpa_s->drv_flags & WPA_DRIVER_FLAGS_P2P_MGMT)
+ return wpa_drv_p2p_find(wpa_s, timeout, type);
+
+ if (wpa_s->global->p2p_disabled || wpa_s->global->p2p == NULL ||
+ wpa_s->p2p_in_provisioning)
+ return -1;
+
+ wpa_supplicant_cancel_sched_scan(wpa_s);
+
+ return p2p_find(wpa_s->global->p2p, timeout, type,
+ num_req_dev_types, req_dev_types, dev_id,
+ search_delay);
+}
+
+
+static int wpas_p2p_stop_find_oper(struct wpa_supplicant *wpa_s)
+{
+ wpas_p2p_clear_pending_action_tx(wpa_s);
+ wpa_s->p2p_long_listen = 0;
+ eloop_cancel_timeout(wpas_p2p_long_listen_timeout, wpa_s, NULL);
+ eloop_cancel_timeout(wpas_p2p_join_scan, wpa_s, NULL);
+ wpa_s->global->p2p_cb_on_scan_complete = 0;
+
+ if (wpa_s->drv_flags & WPA_DRIVER_FLAGS_P2P_MGMT) {
+ wpa_drv_p2p_stop_find(wpa_s);
+ return 1;
+ }
+
+ if (wpa_s->global->p2p)
+ p2p_stop_find(wpa_s->global->p2p);
+
+ return 0;
+}
+
+
+void wpas_p2p_stop_find(struct wpa_supplicant *wpa_s)
+{
+ if (wpas_p2p_stop_find_oper(wpa_s) > 0)
+ return;
+ wpas_p2p_remove_pending_group_interface(wpa_s);
+}
+
+
+static void wpas_p2p_long_listen_timeout(void *eloop_ctx, void *timeout_ctx)
+{
+ struct wpa_supplicant *wpa_s = eloop_ctx;
+ wpa_s->p2p_long_listen = 0;
+}
+
+
+int wpas_p2p_listen(struct wpa_supplicant *wpa_s, unsigned int timeout)
+{
+ int res;
+
+ if (wpa_s->global->p2p_disabled || wpa_s->global->p2p == NULL)
+ return -1;
+
+ wpa_supplicant_cancel_sched_scan(wpa_s);
+ wpas_p2p_clear_pending_action_tx(wpa_s);
+
+ if (timeout == 0) {
+ /*
+ * This is a request for unlimited Listen state. However, at
+ * least for now, this is mapped to a Listen state for one
+ * hour.
+ */
+ timeout = 3600;
+ }
+ eloop_cancel_timeout(wpas_p2p_long_listen_timeout, wpa_s, NULL);
+ wpa_s->p2p_long_listen = 0;
+
+ /*
+ * Stop previous find/listen operation to avoid trying to request a new
+ * remain-on-channel operation while the driver is still running the
+ * previous one.
+ */
+ if (wpa_s->global->p2p)
+ p2p_stop_find(wpa_s->global->p2p);
+
+ res = wpas_p2p_listen_start(wpa_s, timeout * 1000);
+ if (res == 0 && timeout * 1000 > wpa_s->max_remain_on_chan) {
+ wpa_s->p2p_long_listen = timeout * 1000;
+ eloop_register_timeout(timeout, 0,
+ wpas_p2p_long_listen_timeout,
+ wpa_s, NULL);
+ }
+
+ return res;
+}
+
+
+int wpas_p2p_assoc_req_ie(struct wpa_supplicant *wpa_s, struct wpa_bss *bss,
+ u8 *buf, size_t len, int p2p_group)
+{
+ struct wpabuf *p2p_ie;
+ int ret;
+
+ if (wpa_s->global->p2p_disabled)
+ return -1;
+ if (wpa_s->global->p2p == NULL)
+ return -1;
+ if (bss == NULL)
+ return -1;
+
+ p2p_ie = wpa_bss_get_vendor_ie_multi(bss, P2P_IE_VENDOR_TYPE);
+ ret = p2p_assoc_req_ie(wpa_s->global->p2p, bss->bssid, buf, len,
+ p2p_group, p2p_ie);
+ wpabuf_free(p2p_ie);
+
+ return ret;
+}
+
+
+int wpas_p2p_probe_req_rx(struct wpa_supplicant *wpa_s, const u8 *addr,
+ const u8 *dst, const u8 *bssid,
+ const u8 *ie, size_t ie_len, int ssi_signal)
+{
+ if (wpa_s->global->p2p_disabled)
+ return 0;
+ if (wpa_s->global->p2p == NULL)
+ return 0;
+
+ switch (p2p_probe_req_rx(wpa_s->global->p2p, addr, dst, bssid,
+ ie, ie_len)) {
+ case P2P_PREQ_NOT_P2P:
+ wpas_notify_preq(wpa_s, addr, dst, bssid, ie, ie_len,
+ ssi_signal);
+ /* fall through */
+ case P2P_PREQ_MALFORMED:
+ case P2P_PREQ_NOT_LISTEN:
+ case P2P_PREQ_NOT_PROCESSED:
+ default: /* make gcc happy */
+ return 0;
+ case P2P_PREQ_PROCESSED:
+ return 1;
+ }
+}
+
+
+void wpas_p2p_rx_action(struct wpa_supplicant *wpa_s, const u8 *da,
+ const u8 *sa, const u8 *bssid,
+ u8 category, const u8 *data, size_t len, int freq)
+{
+ if (wpa_s->global->p2p_disabled)
+ return;
+ if (wpa_s->global->p2p == NULL)
+ return;
+
+ p2p_rx_action(wpa_s->global->p2p, da, sa, bssid, category, data, len,
+ freq);
+}
+
+
+void wpas_p2p_scan_ie(struct wpa_supplicant *wpa_s, struct wpabuf *ies)
+{
+ if (wpa_s->global->p2p_disabled)
+ return;
+ if (wpa_s->global->p2p == NULL)
+ return;
+
+ p2p_scan_ie(wpa_s->global->p2p, ies, NULL);
+}
+
+
+void wpas_p2p_group_deinit(struct wpa_supplicant *wpa_s)
+{
+ p2p_group_deinit(wpa_s->p2p_group);
+ wpa_s->p2p_group = NULL;
+
+ wpa_s->ap_configured_cb = NULL;
+ wpa_s->ap_configured_cb_ctx = NULL;
+ wpa_s->ap_configured_cb_data = NULL;
+ wpa_s->connect_without_scan = NULL;
+}
+
+
+int wpas_p2p_reject(struct wpa_supplicant *wpa_s, const u8 *addr)
+{
+ wpa_s->p2p_long_listen = 0;
+
+ if (wpa_s->drv_flags & WPA_DRIVER_FLAGS_P2P_MGMT)
+ return wpa_drv_p2p_reject(wpa_s, addr);
+
+ if (wpa_s->global->p2p_disabled || wpa_s->global->p2p == NULL)
+ return -1;
+
+ return p2p_reject(wpa_s->global->p2p, addr);
+}
+
+
+/* Invite to reinvoke a persistent group */
+int wpas_p2p_invite(struct wpa_supplicant *wpa_s, const u8 *peer_addr,
+ struct wpa_ssid *ssid, const u8 *go_dev_addr, int freq,
+ int ht40)
+{
+ enum p2p_invite_role role;
+ u8 *bssid = NULL;
+
+ wpa_s->p2p_persistent_go_freq = freq;
+ wpa_s->p2p_go_ht40 = !!ht40;
+ if (ssid->mode == WPAS_MODE_P2P_GO) {
+ role = P2P_INVITE_ROLE_GO;
+ if (peer_addr == NULL) {
+ wpa_printf(MSG_DEBUG, "P2P: Missing peer "
+ "address in invitation command");
+ return -1;
+ }
+ if (wpas_p2p_create_iface(wpa_s)) {
+ if (wpas_p2p_add_group_interface(wpa_s,
+ WPA_IF_P2P_GO) < 0) {
+ wpa_printf(MSG_ERROR, "P2P: Failed to "
+ "allocate a new interface for the "
+ "group");
+ return -1;
+ }
+ bssid = wpa_s->pending_interface_addr;
+ } else
+ bssid = wpa_s->own_addr;
+ } else {
+ role = P2P_INVITE_ROLE_CLIENT;
+ peer_addr = ssid->bssid;
+ }
+ wpa_s->pending_invite_ssid_id = ssid->id;
+
+ if (wpa_s->drv_flags & WPA_DRIVER_FLAGS_P2P_MGMT)
+ return wpa_drv_p2p_invite(wpa_s, peer_addr, role, bssid,
+ ssid->ssid, ssid->ssid_len,
+ go_dev_addr, 1);
+
+ if (wpa_s->global->p2p_disabled || wpa_s->global->p2p == NULL)
+ return -1;
+
+ return p2p_invite(wpa_s->global->p2p, peer_addr, role, bssid,
+ ssid->ssid, ssid->ssid_len, freq, go_dev_addr, 1);
+}
+
+
+/* Invite to join an active group */
+int wpas_p2p_invite_group(struct wpa_supplicant *wpa_s, const char *ifname,
+ const u8 *peer_addr, const u8 *go_dev_addr)
+{
+ struct wpa_global *global = wpa_s->global;
+ enum p2p_invite_role role;
+ u8 *bssid = NULL;
+ struct wpa_ssid *ssid;
+ int persistent;
+
+ wpa_s->p2p_persistent_go_freq = 0;
+ wpa_s->p2p_go_ht40 = 0;
+
+ for (wpa_s = global->ifaces; wpa_s; wpa_s = wpa_s->next) {
+ if (os_strcmp(wpa_s->ifname, ifname) == 0)
+ break;
+ }
+ if (wpa_s == NULL) {
+ wpa_printf(MSG_DEBUG, "P2P: Interface '%s' not found", ifname);
+ return -1;
+ }
+
+ ssid = wpa_s->current_ssid;
+ if (ssid == NULL) {
+ wpa_printf(MSG_DEBUG, "P2P: No current SSID to use for "
+ "invitation");
+ return -1;
+ }
+
+ persistent = ssid->p2p_persistent_group &&
+ wpas_p2p_get_persistent(wpa_s->parent, peer_addr,
+ ssid->ssid, ssid->ssid_len);
+
+ if (ssid->mode == WPAS_MODE_P2P_GO) {
+ role = P2P_INVITE_ROLE_ACTIVE_GO;
+ bssid = wpa_s->own_addr;
+ if (go_dev_addr == NULL)
+ go_dev_addr = wpa_s->global->p2p_dev_addr;
+ } else {
+ role = P2P_INVITE_ROLE_CLIENT;
+ if (wpa_s->wpa_state < WPA_ASSOCIATED) {
+ wpa_printf(MSG_DEBUG, "P2P: Not associated - cannot "
+ "invite to current group");
+ return -1;
+ }
+ bssid = wpa_s->bssid;
+ if (go_dev_addr == NULL &&
+ !is_zero_ether_addr(wpa_s->go_dev_addr))
+ go_dev_addr = wpa_s->go_dev_addr;
+ }
+ wpa_s->parent->pending_invite_ssid_id = -1;
+
+ if (wpa_s->drv_flags & WPA_DRIVER_FLAGS_P2P_MGMT)
+ return wpa_drv_p2p_invite(wpa_s, peer_addr, role, bssid,
+ ssid->ssid, ssid->ssid_len,
+ go_dev_addr, persistent);
+
+ if (wpa_s->global->p2p_disabled || wpa_s->global->p2p == NULL)
+ return -1;
+
+ return p2p_invite(wpa_s->global->p2p, peer_addr, role, bssid,
+ ssid->ssid, ssid->ssid_len, wpa_s->assoc_freq,
+ go_dev_addr, persistent);
+}
+
+
+void wpas_p2p_completed(struct wpa_supplicant *wpa_s)
+{
+ struct wpa_ssid *ssid = wpa_s->current_ssid;
+ const char *ssid_txt;
+ u8 go_dev_addr[ETH_ALEN];
+ int network_id = -1;
+ int persistent;
+ int freq;
+
+ if (ssid == NULL || ssid->mode != WPAS_MODE_P2P_GROUP_FORMATION) {
+ eloop_cancel_timeout(wpas_p2p_group_formation_timeout,
+ wpa_s->parent, NULL);
+ }
+
+ if (!wpa_s->show_group_started || !ssid)
+ goto done;
+
+ wpa_s->show_group_started = 0;
+
+ ssid_txt = wpa_ssid_txt(ssid->ssid, ssid->ssid_len);
+ os_memset(go_dev_addr, 0, ETH_ALEN);
+ if (ssid->bssid_set)
+ os_memcpy(go_dev_addr, ssid->bssid, ETH_ALEN);
+ persistent = wpas_p2p_persistent_group(wpa_s, go_dev_addr, ssid->ssid,
+ ssid->ssid_len);
+ os_memcpy(wpa_s->go_dev_addr, go_dev_addr, ETH_ALEN);
+
+ if (wpa_s->global->p2p_group_formation == wpa_s)
+ wpa_s->global->p2p_group_formation = NULL;
+
+ freq = wpa_s->current_bss ? wpa_s->current_bss->freq :
+ (int) wpa_s->assoc_freq;
+ if (ssid->passphrase == NULL && ssid->psk_set) {
+ char psk[65];
+ wpa_snprintf_hex(psk, sizeof(psk), ssid->psk, 32);
+ wpa_msg(wpa_s->parent, MSG_INFO, P2P_EVENT_GROUP_STARTED
+ "%s client ssid=\"%s\" freq=%d psk=%s go_dev_addr="
+ MACSTR "%s",
+ wpa_s->ifname, ssid_txt, freq, psk,
+ MAC2STR(go_dev_addr),
+ persistent ? " [PERSISTENT]" : "");
+ } else {
+ wpa_msg(wpa_s->parent, MSG_INFO, P2P_EVENT_GROUP_STARTED
+ "%s client ssid=\"%s\" freq=%d passphrase=\"%s\" "
+ "go_dev_addr=" MACSTR "%s",
+ wpa_s->ifname, ssid_txt, freq,
+ ssid->passphrase ? ssid->passphrase : "",
+ MAC2STR(go_dev_addr),
+ persistent ? " [PERSISTENT]" : "");
+ }
+
+ if (persistent)
+ network_id = wpas_p2p_store_persistent_group(wpa_s->parent,
+ ssid, go_dev_addr);
+ if (network_id < 0)
+ network_id = ssid->id;
+ wpas_notify_p2p_group_started(wpa_s, ssid, network_id, 1);
+
+done:
+ if (wpa_s->global->p2p_cb_on_scan_complete && !wpa_s->global->p2p_disabled &&
+ wpa_s->global->p2p != NULL) {
+ wpa_s->global->p2p_cb_on_scan_complete = 0;
+ if (p2p_other_scan_completed(wpa_s->global->p2p) == 1) {
+ wpa_dbg(wpa_s, MSG_DEBUG, "P2P: Pending P2P operation "
+ "continued after successful connection");
+ p2p_increase_search_delay(
+ wpa_s->global->p2p,
+ wpas_p2p_search_delay(wpa_s));
+ }
+ }
+}
+
+
+int wpas_p2p_presence_req(struct wpa_supplicant *wpa_s, u32 duration1,
+ u32 interval1, u32 duration2, u32 interval2)
+{
+ if (wpa_s->drv_flags & WPA_DRIVER_FLAGS_P2P_MGMT)
+ return -1;
+ if (wpa_s->global->p2p_disabled || wpa_s->global->p2p == NULL)
+ return -1;
+
+ if (wpa_s->wpa_state < WPA_ASSOCIATED ||
+ wpa_s->current_ssid == NULL ||
+ wpa_s->current_ssid->mode != WPAS_MODE_INFRA)
+ return -1;
+
+ return p2p_presence_req(wpa_s->global->p2p, wpa_s->bssid,
+ wpa_s->own_addr, wpa_s->assoc_freq,
+ duration1, interval1, duration2, interval2);
+}
+
+
+int wpas_p2p_ext_listen(struct wpa_supplicant *wpa_s, unsigned int period,
+ unsigned int interval)
+{
+ if (wpa_s->drv_flags & WPA_DRIVER_FLAGS_P2P_MGMT)
+ return -1;
+
+ if (wpa_s->global->p2p_disabled || wpa_s->global->p2p == NULL)
+ return -1;
+
+ return p2p_ext_listen(wpa_s->global->p2p, period, interval);
+}
+
+
+static int wpas_p2p_is_client(struct wpa_supplicant *wpa_s)
+{
+ if (wpa_s->current_ssid == NULL) {
+ /*
+ * current_ssid can be cleared when P2P client interface gets
+ * disconnected, so assume this interface was used as P2P
+ * client.
+ */
+ return 1;
+ }
+ return wpa_s->current_ssid->p2p_group &&
+ wpa_s->current_ssid->mode == WPAS_MODE_INFRA;
+}
+
+
+static void wpas_p2p_group_idle_timeout(void *eloop_ctx, void *timeout_ctx)
+{
+ struct wpa_supplicant *wpa_s = eloop_ctx;
+
+ if (wpa_s->conf->p2p_group_idle == 0 && !wpas_p2p_is_client(wpa_s)) {
+ wpa_printf(MSG_DEBUG, "P2P: Ignore group idle timeout - "
+ "disabled");
+ return;
+ }
+
+ wpa_printf(MSG_DEBUG, "P2P: Group idle timeout reached - terminate "
+ "group");
+ wpas_p2p_group_delete(wpa_s, P2P_GROUP_REMOVAL_IDLE_TIMEOUT);
+}
+
+
+static void wpas_p2p_set_group_idle_timeout(struct wpa_supplicant *wpa_s)
+{
+ int timeout;
+
+ if (eloop_cancel_timeout(wpas_p2p_group_idle_timeout, wpa_s, NULL) > 0)
+ wpa_printf(MSG_DEBUG, "P2P: Cancelled P2P group idle timeout");
+
+ if (wpa_s->current_ssid == NULL || !wpa_s->current_ssid->p2p_group)
+ return;
+
+ timeout = wpa_s->conf->p2p_group_idle;
+ if (wpa_s->current_ssid->mode == WPAS_MODE_INFRA &&
+ (timeout == 0 || timeout > P2P_MAX_CLIENT_IDLE))
+ timeout = P2P_MAX_CLIENT_IDLE;
+
+ if (timeout == 0)
+ return;
+
+ if (timeout < 0) {
+ if (wpa_s->current_ssid->mode == WPAS_MODE_INFRA)
+ timeout = 0; /* special client mode no-timeout */
+ else
+ return;
+ }
+
+ if (wpa_s->p2p_in_provisioning) {
+ /*
+ * Use the normal group formation timeout during the
+ * provisioning phase to avoid terminating this process too
+ * early due to group idle timeout.
+ */
+ wpa_printf(MSG_DEBUG, "P2P: Do not use P2P group idle timeout "
+ "during provisioning");
+ return;
+ }
+
+ if (wpa_s->show_group_started) {
+ /*
+ * Use the normal group formation timeout between the end of
+ * the provisioning phase and completion of 4-way handshake to
+ * avoid terminating this process too early due to group idle
+ * timeout.
+ */
+ wpa_printf(MSG_DEBUG, "P2P: Do not use P2P group idle timeout "
+ "while waiting for initial 4-way handshake to "
+ "complete");
+ return;
+ }
+
+ wpa_printf(MSG_DEBUG, "P2P: Set P2P group idle timeout to %u seconds",
+ timeout);
+ eloop_register_timeout(timeout, 0, wpas_p2p_group_idle_timeout,
+ wpa_s, NULL);
+}
+
+
+/* Returns 1 if the interface was removed */
+int wpas_p2p_deauth_notif(struct wpa_supplicant *wpa_s, const u8 *bssid,
+ u16 reason_code, const u8 *ie, size_t ie_len,
+ int locally_generated)
+{
+ if (wpa_s->global->p2p_disabled || wpa_s->global->p2p == NULL)
+ return 0;
+ if (wpa_s->drv_flags & WPA_DRIVER_FLAGS_P2P_MGMT)
+ return 0;
+
+ if (!locally_generated)
+ p2p_deauth_notif(wpa_s->global->p2p, bssid, reason_code, ie,
+ ie_len);
+
+ if (reason_code == WLAN_REASON_DEAUTH_LEAVING && !locally_generated &&
+ wpa_s->current_ssid &&
+ wpa_s->current_ssid->p2p_group &&
+ wpa_s->current_ssid->mode == WPAS_MODE_INFRA) {
+ wpa_printf(MSG_DEBUG, "P2P: GO indicated that the P2P Group "
+ "session is ending");
+ if (wpas_p2p_group_delete(wpa_s,
+ P2P_GROUP_REMOVAL_GO_ENDING_SESSION)
+ > 0)
+ return 1;
+ }
+
+ return 0;
+}
+
+
+void wpas_p2p_disassoc_notif(struct wpa_supplicant *wpa_s, const u8 *bssid,
+ u16 reason_code, const u8 *ie, size_t ie_len,
+ int locally_generated)
+{
+ if (wpa_s->global->p2p_disabled || wpa_s->global->p2p == NULL)
+ return;
+ if (wpa_s->drv_flags & WPA_DRIVER_FLAGS_P2P_MGMT)
+ return;
+
+ if (!locally_generated)
+ p2p_disassoc_notif(wpa_s->global->p2p, bssid, reason_code, ie,
+ ie_len);
+}
+
+
+void wpas_p2p_update_config(struct wpa_supplicant *wpa_s)
+{
+ struct p2p_data *p2p = wpa_s->global->p2p;
+
+ if (p2p == NULL)
+ return;
+
+ if (!(wpa_s->drv_flags & WPA_DRIVER_FLAGS_P2P_CAPABLE))
+ return;
+
+ if (wpa_s->conf->changed_parameters & CFG_CHANGED_DEVICE_NAME)
+ p2p_set_dev_name(p2p, wpa_s->conf->device_name);
+
+ if (wpa_s->conf->changed_parameters & CFG_CHANGED_DEVICE_TYPE)
+ p2p_set_pri_dev_type(p2p, wpa_s->conf->device_type);
+
+ if (wpa_s->wps &&
+ (wpa_s->conf->changed_parameters & CFG_CHANGED_CONFIG_METHODS))
+ p2p_set_config_methods(p2p, wpa_s->wps->config_methods);
+
+ if (wpa_s->wps && (wpa_s->conf->changed_parameters & CFG_CHANGED_UUID))
+ p2p_set_uuid(p2p, wpa_s->wps->uuid);
+
+ if (wpa_s->conf->changed_parameters & CFG_CHANGED_WPS_STRING) {
+ p2p_set_manufacturer(p2p, wpa_s->conf->manufacturer);
+ p2p_set_model_name(p2p, wpa_s->conf->model_name);
+ p2p_set_model_number(p2p, wpa_s->conf->model_number);
+ p2p_set_serial_number(p2p, wpa_s->conf->serial_number);
+ }
+
+ if (wpa_s->conf->changed_parameters & CFG_CHANGED_SEC_DEVICE_TYPE)
+ p2p_set_sec_dev_types(p2p,
+ (void *) wpa_s->conf->sec_device_type,
+ wpa_s->conf->num_sec_device_types);
+
+ if (wpa_s->conf->changed_parameters & CFG_CHANGED_VENDOR_EXTENSION) {
+ int i;
+ p2p_remove_wps_vendor_extensions(p2p);
+ for (i = 0; i < MAX_WPS_VENDOR_EXT; i++) {
+ if (wpa_s->conf->wps_vendor_ext[i] == NULL)
+ continue;
+ p2p_add_wps_vendor_extension(
+ p2p, wpa_s->conf->wps_vendor_ext[i]);
+ }
+ }
+
+ if ((wpa_s->conf->changed_parameters & CFG_CHANGED_COUNTRY) &&
+ wpa_s->conf->country[0] && wpa_s->conf->country[1]) {
+ char country[3];
+ country[0] = wpa_s->conf->country[0];
+ country[1] = wpa_s->conf->country[1];
+ country[2] = 0x04;
+ p2p_set_country(p2p, country);
+ }
+
+ if (wpa_s->conf->changed_parameters & CFG_CHANGED_P2P_SSID_POSTFIX) {
+ p2p_set_ssid_postfix(p2p, (u8 *) wpa_s->conf->p2p_ssid_postfix,
+ wpa_s->conf->p2p_ssid_postfix ?
+ os_strlen(wpa_s->conf->p2p_ssid_postfix) :
+ 0);
+ }
+
+ if (wpa_s->conf->changed_parameters & CFG_CHANGED_P2P_INTRA_BSS)
+ p2p_set_intra_bss_dist(p2p, wpa_s->conf->p2p_intra_bss);
+
+ if (wpa_s->conf->changed_parameters & CFG_CHANGED_P2P_LISTEN_CHANNEL) {
+ u8 reg_class, channel;
+ int ret;
+ unsigned int r;
+ if (wpa_s->conf->p2p_listen_reg_class &&
+ wpa_s->conf->p2p_listen_channel) {
+ reg_class = wpa_s->conf->p2p_listen_reg_class;
+ channel = wpa_s->conf->p2p_listen_channel;
+ } else {
+ reg_class = 81;
+ /*
+ * Pick one of the social channels randomly as the
+ * listen channel.
+ */
+ os_get_random((u8 *) &r, sizeof(r));
+ channel = 1 + (r % 3) * 5;
+ }
+ ret = p2p_set_listen_channel(p2p, reg_class, channel);
+ if (ret)
+ wpa_printf(MSG_ERROR, "P2P: Own listen channel update "
+ "failed: %d", ret);
+ }
+ if (wpa_s->conf->changed_parameters & CFG_CHANGED_P2P_OPER_CHANNEL) {
+ u8 op_reg_class, op_channel, cfg_op_channel;
+ int ret = 0;
+ unsigned int r;
+ if (wpa_s->conf->p2p_oper_reg_class &&
+ wpa_s->conf->p2p_oper_channel) {
+ op_reg_class = wpa_s->conf->p2p_oper_reg_class;
+ op_channel = wpa_s->conf->p2p_oper_channel;
+ cfg_op_channel = 1;
+ } else {
+ op_reg_class = 81;
+ /*
+ * Use random operation channel from (1, 6, 11)
+ *if no other preference is indicated.
+ */
+ os_get_random((u8 *) &r, sizeof(r));
+ op_channel = 1 + (r % 3) * 5;
+ cfg_op_channel = 0;
+ }
+ ret = p2p_set_oper_channel(p2p, op_reg_class, op_channel,
+ cfg_op_channel);
+ if (ret)
+ wpa_printf(MSG_ERROR, "P2P: Own oper channel update "
+ "failed: %d", ret);
+ }
+
+ if (wpa_s->conf->changed_parameters & CFG_CHANGED_P2P_PREF_CHAN) {
+ if (p2p_set_pref_chan(p2p, wpa_s->conf->num_p2p_pref_chan,
+ wpa_s->conf->p2p_pref_chan) < 0) {
+ wpa_printf(MSG_ERROR, "P2P: Preferred channel list "
+ "update failed");
+ }
+ }
+}
+
+
+int wpas_p2p_set_noa(struct wpa_supplicant *wpa_s, u8 count, int start,
+ int duration)
+{
+ if (!wpa_s->ap_iface)
+ return -1;
+ return hostapd_p2p_set_noa(wpa_s->ap_iface->bss[0], count, start,
+ duration);
+}
+
+
+int wpas_p2p_set_cross_connect(struct wpa_supplicant *wpa_s, int enabled)
+{
+ if (wpa_s->global->p2p_disabled || wpa_s->global->p2p == NULL)
+ return -1;
+ if (wpa_s->drv_flags & WPA_DRIVER_FLAGS_P2P_MGMT)
+ return -1;
+
+ wpa_s->global->cross_connection = enabled;
+ p2p_set_cross_connect(wpa_s->global->p2p, enabled);
+
+ if (!enabled) {
+ struct wpa_supplicant *iface;
+
+ for (iface = wpa_s->global->ifaces; iface; iface = iface->next)
+ {
+ if (iface->cross_connect_enabled == 0)
+ continue;
+
+ iface->cross_connect_enabled = 0;
+ iface->cross_connect_in_use = 0;
+ wpa_msg(iface->parent, MSG_INFO,
+ P2P_EVENT_CROSS_CONNECT_DISABLE "%s %s",
+ iface->ifname, iface->cross_connect_uplink);
+ }
+ }
+
+ return 0;
+}
+
+
+static void wpas_p2p_enable_cross_connect(struct wpa_supplicant *uplink)
+{
+ struct wpa_supplicant *iface;
+
+ if (!uplink->global->cross_connection)
+ return;
+
+ for (iface = uplink->global->ifaces; iface; iface = iface->next) {
+ if (!iface->cross_connect_enabled)
+ continue;
+ if (os_strcmp(uplink->ifname, iface->cross_connect_uplink) !=
+ 0)
+ continue;
+ if (iface->ap_iface == NULL)
+ continue;
+ if (iface->cross_connect_in_use)
+ continue;
+
+ iface->cross_connect_in_use = 1;
+ wpa_msg(iface->parent, MSG_INFO,
+ P2P_EVENT_CROSS_CONNECT_ENABLE "%s %s",
+ iface->ifname, iface->cross_connect_uplink);
+ }
+}
+
+
+static void wpas_p2p_disable_cross_connect(struct wpa_supplicant *uplink)
+{
+ struct wpa_supplicant *iface;
+
+ for (iface = uplink->global->ifaces; iface; iface = iface->next) {
+ if (!iface->cross_connect_enabled)
+ continue;
+ if (os_strcmp(uplink->ifname, iface->cross_connect_uplink) !=
+ 0)
+ continue;
+ if (!iface->cross_connect_in_use)
+ continue;
+
+ wpa_msg(iface->parent, MSG_INFO,
+ P2P_EVENT_CROSS_CONNECT_DISABLE "%s %s",
+ iface->ifname, iface->cross_connect_uplink);
+ iface->cross_connect_in_use = 0;
+ }
+}
+
+
+void wpas_p2p_notif_connected(struct wpa_supplicant *wpa_s)
+{
+ if (wpa_s->ap_iface || wpa_s->current_ssid == NULL ||
+ wpa_s->current_ssid->mode != WPAS_MODE_INFRA ||
+ wpa_s->cross_connect_disallowed)
+ wpas_p2p_disable_cross_connect(wpa_s);
+ else
+ wpas_p2p_enable_cross_connect(wpa_s);
+ if (!wpa_s->ap_iface &&
+ eloop_cancel_timeout(wpas_p2p_group_idle_timeout, wpa_s, NULL) > 0)
+ wpa_printf(MSG_DEBUG, "P2P: Cancelled P2P group idle timeout");
+}
+
+
+void wpas_p2p_notif_disconnected(struct wpa_supplicant *wpa_s)
+{
+ wpas_p2p_disable_cross_connect(wpa_s);
+ if (!wpa_s->ap_iface &&
+ !eloop_is_timeout_registered(wpas_p2p_group_idle_timeout,
+ wpa_s, NULL))
+ wpas_p2p_set_group_idle_timeout(wpa_s);
+}
+
+
+static void wpas_p2p_cross_connect_setup(struct wpa_supplicant *wpa_s)
+{
+ struct wpa_supplicant *iface;
+
+ if (!wpa_s->global->cross_connection)
+ return;
+
+ for (iface = wpa_s->global->ifaces; iface; iface = iface->next) {
+ if (iface == wpa_s)
+ continue;
+ if (iface->drv_flags &
+ WPA_DRIVER_FLAGS_P2P_DEDICATED_INTERFACE)
+ continue;
+ if (iface->drv_flags & WPA_DRIVER_FLAGS_P2P_CAPABLE)
+ continue;
+
+ wpa_s->cross_connect_enabled = 1;
+ os_strlcpy(wpa_s->cross_connect_uplink, iface->ifname,
+ sizeof(wpa_s->cross_connect_uplink));
+ wpa_printf(MSG_DEBUG, "P2P: Enable cross connection from "
+ "%s to %s whenever uplink is available",
+ wpa_s->ifname, wpa_s->cross_connect_uplink);
+
+ if (iface->ap_iface || iface->current_ssid == NULL ||
+ iface->current_ssid->mode != WPAS_MODE_INFRA ||
+ iface->cross_connect_disallowed ||
+ iface->wpa_state != WPA_COMPLETED)
+ break;
+
+ wpa_s->cross_connect_in_use = 1;
+ wpa_msg(wpa_s->parent, MSG_INFO,
+ P2P_EVENT_CROSS_CONNECT_ENABLE "%s %s",
+ wpa_s->ifname, wpa_s->cross_connect_uplink);
+ break;
+ }
+}
+
+
+int wpas_p2p_notif_pbc_overlap(struct wpa_supplicant *wpa_s)
+{
+ if (wpa_s->p2p_group_interface != P2P_GROUP_INTERFACE_CLIENT &&
+ !wpa_s->p2p_in_provisioning)
+ return 0; /* not P2P client operation */
+
+ wpa_printf(MSG_DEBUG, "P2P: Terminate connection due to WPS PBC "
+ "session overlap");
+ if (wpa_s != wpa_s->parent)
+ wpa_msg_ctrl(wpa_s->parent, MSG_INFO, WPS_EVENT_OVERLAP);
+
+ if (wpa_s->global->p2p)
+ p2p_group_formation_failed(wpa_s->global->p2p);
+
+ eloop_cancel_timeout(wpas_p2p_group_formation_timeout,
+ wpa_s->parent, NULL);
+
+ wpas_group_formation_completed(wpa_s, 0);
+ return 1;
+}
+
+
+void wpas_p2p_update_channel_list(struct wpa_supplicant *wpa_s)
+{
+ struct p2p_channels chan;
+
+ if (wpa_s->global == NULL || wpa_s->global->p2p == NULL)
+ return;
+
+ os_memset(&chan, 0, sizeof(chan));
+ if (wpas_p2p_setup_channels(wpa_s, &chan)) {
+ wpa_printf(MSG_ERROR, "P2P: Failed to update supported "
+ "channel list");
+ return;
+ }
+
+ p2p_update_channel_list(wpa_s->global->p2p, &chan);
+}
+
+
+static void wpas_p2p_scan_res_ignore(struct wpa_supplicant *wpa_s,
+ struct wpa_scan_results *scan_res)
+{
+ wpa_printf(MSG_DEBUG, "P2P: Ignore scan results");
+}
+
+
+int wpas_p2p_cancel(struct wpa_supplicant *wpa_s)
+{
+ struct wpa_global *global = wpa_s->global;
+ int found = 0;
+ const u8 *peer;
+
+ if (global->p2p == NULL)
+ return -1;
+
+ wpa_printf(MSG_DEBUG, "P2P: Request to cancel group formation");
+
+ if (wpa_s->pending_interface_name[0] &&
+ !is_zero_ether_addr(wpa_s->pending_interface_addr))
+ found = 1;
+
+ peer = p2p_get_go_neg_peer(global->p2p);
+ if (peer) {
+ wpa_printf(MSG_DEBUG, "P2P: Unauthorize pending GO Neg peer "
+ MACSTR, MAC2STR(peer));
+ p2p_unauthorize(global->p2p, peer);
+ found = 1;
+ }
+
+ if (wpa_s->scan_res_handler == wpas_p2p_scan_res_join) {
+ wpa_printf(MSG_DEBUG, "P2P: Stop pending scan for join");
+ wpa_s->scan_res_handler = wpas_p2p_scan_res_ignore;
+ found = 1;
+ }
+
+ if (wpa_s->pending_pd_before_join) {
+ wpa_printf(MSG_DEBUG, "P2P: Stop pending PD before join");
+ wpa_s->pending_pd_before_join = 0;
+ found = 1;
+ }
+
+ wpas_p2p_stop_find(wpa_s);
+
+ for (wpa_s = global->ifaces; wpa_s; wpa_s = wpa_s->next) {
+ if (wpa_s == global->p2p_group_formation &&
+ (wpa_s->p2p_in_provisioning ||
+ wpa_s->parent->pending_interface_type ==
+ WPA_IF_P2P_CLIENT)) {
+ wpa_printf(MSG_DEBUG, "P2P: Interface %s in group "
+ "formation found - cancelling",
+ wpa_s->ifname);
+ found = 1;
+ eloop_cancel_timeout(wpas_p2p_group_formation_timeout,
+ wpa_s->parent, NULL);
+ if (wpa_s->p2p_in_provisioning) {
+ wpas_group_formation_completed(wpa_s, 0);
+ break;
+ }
+ wpas_p2p_group_delete(wpa_s,
+ P2P_GROUP_REMOVAL_REQUESTED);
+ break;
+ }
+ }
+
+ if (!found) {
+ wpa_printf(MSG_DEBUG, "P2P: No ongoing group formation found");
+ return -1;
+ }
+
+ return 0;
+}
+
+
+void wpas_p2p_interface_unavailable(struct wpa_supplicant *wpa_s)
+{
+ if (wpa_s->current_ssid == NULL || !wpa_s->current_ssid->p2p_group)
+ return;
+
+ wpa_printf(MSG_DEBUG, "P2P: Remove group due to driver resource not "
+ "being available anymore");
+ wpas_p2p_group_delete(wpa_s, P2P_GROUP_REMOVAL_UNAVAILABLE);
+}
+
+
+void wpas_p2p_update_best_channels(struct wpa_supplicant *wpa_s,
+ int freq_24, int freq_5, int freq_overall)
+{
+ struct p2p_data *p2p = wpa_s->global->p2p;
+ if (p2p == NULL || (wpa_s->drv_flags & WPA_DRIVER_FLAGS_P2P_MGMT))
+ return;
+ p2p_set_best_channels(p2p, freq_24, freq_5, freq_overall);
+}
+
+
+int wpas_p2p_unauthorize(struct wpa_supplicant *wpa_s, const char *addr)
+{
+ u8 peer[ETH_ALEN];
+ struct p2p_data *p2p = wpa_s->global->p2p;
+
+ if (p2p == NULL || (wpa_s->drv_flags & WPA_DRIVER_FLAGS_P2P_MGMT))
+ return -1;
+
+ if (hwaddr_aton(addr, peer))
+ return -1;
+
+ return p2p_unauthorize(p2p, peer);
+}
+
+
+/**
+ * wpas_p2p_disconnect - Disconnect from a P2P Group
+ * @wpa_s: Pointer to wpa_supplicant data
+ * Returns: 0 on success, -1 on failure
+ *
+ * This can be used to disconnect from a group in which the local end is a P2P
+ * Client or to end a P2P Group in case the local end is the Group Owner. If a
+ * virtual network interface was created for this group, that interface will be
+ * removed. Otherwise, only the configured P2P group network will be removed
+ * from the interface.
+ */
+int wpas_p2p_disconnect(struct wpa_supplicant *wpa_s)
+{
+
+ if (wpa_s == NULL)
+ return -1;
+
+ return wpas_p2p_group_delete(wpa_s, P2P_GROUP_REMOVAL_REQUESTED) < 0 ?
+ -1 : 0;
+}
+
+
+int wpas_p2p_in_progress(struct wpa_supplicant *wpa_s)
+{
+ if (wpa_s->global->p2p_disabled || wpa_s->global->p2p == NULL)
+ return 0;
+
+ return p2p_in_progress(wpa_s->global->p2p);
+}
+
+
+void wpas_p2p_network_removed(struct wpa_supplicant *wpa_s,
+ struct wpa_ssid *ssid)
+{
+ if (wpa_s->p2p_in_provisioning && ssid->p2p_group &&
+ eloop_cancel_timeout(wpas_p2p_group_formation_timeout,
+ wpa_s->parent, NULL) > 0) {
+ /**
+ * Remove the network by scheduling the group formation
+ * timeout to happen immediately. The teardown code
+ * needs to be scheduled to run asynch later so that we
+ * don't delete data from under ourselves unexpectedly.
+ * Calling wpas_p2p_group_formation_timeout directly
+ * causes a series of crashes in WPS failure scenarios.
+ */
+ wpa_printf(MSG_DEBUG, "P2P: Canceled group formation due to "
+ "P2P group network getting removed");
+ eloop_register_timeout(0, 0, wpas_p2p_group_formation_timeout,
+ wpa_s->parent, NULL);
+ }
+}
+
+
+struct wpa_ssid * wpas_p2p_get_persistent(struct wpa_supplicant *wpa_s,
+ const u8 *addr, const u8 *ssid,
+ size_t ssid_len)
+{
+ struct wpa_ssid *s;
+ size_t i;
+
+ for (s = wpa_s->conf->ssid; s; s = s->next) {
+ if (s->disabled != 2)
+ continue;
+ if (ssid &&
+ (ssid_len != s->ssid_len ||
+ os_memcmp(ssid, s->ssid, ssid_len) != 0))
+ continue;
+ if (os_memcmp(s->bssid, addr, ETH_ALEN) == 0)
+ return s; /* peer is GO in the persistent group */
+ if (s->mode != WPAS_MODE_P2P_GO || s->p2p_client_list == NULL)
+ continue;
+ for (i = 0; i < s->num_p2p_clients; i++) {
+ if (os_memcmp(s->p2p_client_list + i * ETH_ALEN,
+ addr, ETH_ALEN) == 0)
+ return s; /* peer is P2P client in persistent
+ * group */
+ }
+ }
+
+ return NULL;
+}
+
+
+void wpas_p2p_notify_ap_sta_authorized(struct wpa_supplicant *wpa_s,
+ const u8 *addr)
+{
+ if (addr == NULL)
+ return;
+ wpas_p2p_add_persistent_group_client(wpa_s, addr);
+}
+
+
+static void wpas_p2p_fallback_to_go_neg(struct wpa_supplicant *wpa_s,
+ int group_added)
+{
+ struct wpa_supplicant *group = wpa_s;
+ if (wpa_s->global->p2p_group_formation)
+ group = wpa_s->global->p2p_group_formation;
+ wpa_s = wpa_s->parent;
+ offchannel_send_action_done(wpa_s);
+ if (group_added)
+ wpas_p2p_group_delete(group, P2P_GROUP_REMOVAL_SILENT);
+ wpa_dbg(wpa_s, MSG_DEBUG, "P2P: Fall back to GO Negotiation");
+ wpas_p2p_connect(wpa_s, wpa_s->pending_join_dev_addr, wpa_s->p2p_pin,
+ wpa_s->p2p_wps_method, wpa_s->p2p_persistent_group, 0,
+ 0, 0, wpa_s->p2p_go_intent, wpa_s->p2p_connect_freq,
+ wpa_s->p2p_persistent_id,
+ wpa_s->p2p_pd_before_go_neg,
+ wpa_s->p2p_go_ht40);
+}
+
+
+int wpas_p2p_scan_no_go_seen(struct wpa_supplicant *wpa_s)
+{
+ if (!wpa_s->p2p_fallback_to_go_neg ||
+ wpa_s->p2p_in_provisioning <= 5)
+ return 0;
+
+ if (wpas_p2p_peer_go(wpa_s, wpa_s->pending_join_dev_addr) > 0)
+ return 0; /* peer operating as a GO */
+
+ wpa_dbg(wpa_s, MSG_DEBUG, "P2P: GO not found for p2p_connect-auto - "
+ "fallback to GO Negotiation");
+ wpas_p2p_fallback_to_go_neg(wpa_s, 1);
+
+ return 1;
+}
+
+
+unsigned int wpas_p2p_search_delay(struct wpa_supplicant *wpa_s)
+{
+ const char *rn, *rn2;
+ struct wpa_supplicant *ifs;
+
+ if (wpa_s->wpa_state > WPA_SCANNING) {
+ wpa_dbg(wpa_s, MSG_DEBUG, "P2P: Use %u ms search delay due to "
+ "concurrent operation",
+ P2P_CONCURRENT_SEARCH_DELAY);
+ return P2P_CONCURRENT_SEARCH_DELAY;
+ }
+
+ if (!wpa_s->driver->get_radio_name)
+ return 0;
+ rn = wpa_s->driver->get_radio_name(wpa_s->drv_priv);
+ if (rn == NULL || rn[0] == '\0')
+ return 0;
+
+ for (ifs = wpa_s->global->ifaces; ifs; ifs = ifs->next) {
+ if (ifs == wpa_s || !ifs->driver->get_radio_name)
+ continue;
+
+ rn2 = ifs->driver->get_radio_name(ifs->drv_priv);
+ if (!rn2 || os_strcmp(rn, rn2) != 0)
+ continue;
+ if (ifs->wpa_state > WPA_SCANNING) {
+ wpa_dbg(wpa_s, MSG_DEBUG, "P2P: Use %u ms search "
+ "delay due to concurrent operation on "
+ "interface %s",
+ P2P_CONCURRENT_SEARCH_DELAY, ifs->ifname);
+ return P2P_CONCURRENT_SEARCH_DELAY;
+ }
+ }
+
+ return 0;
+}
diff --git a/contrib/wpa/wpa_supplicant/p2p_supplicant.h b/contrib/wpa/wpa_supplicant/p2p_supplicant.h
new file mode 100644
index 0000000..b6ecf14
--- /dev/null
+++ b/contrib/wpa/wpa_supplicant/p2p_supplicant.h
@@ -0,0 +1,151 @@
+/*
+ * wpa_supplicant - P2P
+ * Copyright (c) 2009-2010, Atheros Communications
+ *
+ * This software may be distributed under the terms of the BSD license.
+ * See README for more details.
+ */
+
+#ifndef P2P_SUPPLICANT_H
+#define P2P_SUPPLICANT_H
+
+enum p2p_wps_method;
+struct p2p_go_neg_results;
+enum p2p_send_action_result;
+struct p2p_peer_info;
+
+int wpas_p2p_init(struct wpa_global *global, struct wpa_supplicant *wpa_s);
+void wpas_p2p_deinit(struct wpa_supplicant *wpa_s);
+void wpas_p2p_deinit_global(struct wpa_global *global);
+int wpas_p2p_connect(struct wpa_supplicant *wpa_s, const u8 *peer_addr,
+ const char *pin, enum p2p_wps_method wps_method,
+ int persistent_group, int auto_join, int join,
+ int auth, int go_intent, int freq, int persistent_id,
+ int pd, int ht40);
+void wpas_p2p_remain_on_channel_cb(struct wpa_supplicant *wpa_s,
+ unsigned int freq, unsigned int duration);
+void wpas_p2p_cancel_remain_on_channel_cb(struct wpa_supplicant *wpa_s,
+ unsigned int freq);
+int wpas_p2p_group_remove(struct wpa_supplicant *wpa_s, const char *ifname);
+int wpas_p2p_group_add(struct wpa_supplicant *wpa_s, int persistent_group,
+ int freq, int ht40);
+int wpas_p2p_group_add_persistent(struct wpa_supplicant *wpa_s,
+ struct wpa_ssid *ssid, int addr_allocated,
+ int freq, int ht40);
+struct p2p_group * wpas_p2p_group_init(struct wpa_supplicant *wpa_s,
+ struct wpa_ssid *ssid);
+void wpas_p2p_wps_success(struct wpa_supplicant *wpa_s, const u8 *peer_addr,
+ int registrar);
+enum wpas_p2p_prov_disc_use {
+ WPAS_P2P_PD_FOR_GO_NEG,
+ WPAS_P2P_PD_FOR_JOIN,
+ WPAS_P2P_PD_AUTO
+};
+int wpas_p2p_prov_disc(struct wpa_supplicant *wpa_s, const u8 *peer_addr,
+ const char *config_method,
+ enum wpas_p2p_prov_disc_use use);
+void wpas_send_action_tx_status(struct wpa_supplicant *wpa_s, const u8 *dst,
+ const u8 *data, size_t data_len,
+ enum p2p_send_action_result result);
+int wpas_p2p_scan_result_text(const u8 *ies, size_t ies_len, char *buf,
+ char *end);
+enum p2p_discovery_type;
+int wpas_p2p_find(struct wpa_supplicant *wpa_s, unsigned int timeout,
+ enum p2p_discovery_type type,
+ unsigned int num_req_dev_types, const u8 *req_dev_types,
+ const u8 *dev_id, unsigned int search_delay);
+void wpas_p2p_stop_find(struct wpa_supplicant *wpa_s);
+int wpas_p2p_listen(struct wpa_supplicant *wpa_s, unsigned int timeout);
+int wpas_p2p_assoc_req_ie(struct wpa_supplicant *wpa_s, struct wpa_bss *bss,
+ u8 *buf, size_t len, int p2p_group);
+int wpas_p2p_probe_req_rx(struct wpa_supplicant *wpa_s, const u8 *addr,
+ const u8 *dst, const u8 *bssid,
+ const u8 *ie, size_t ie_len,
+ int ssi_signal);
+void wpas_p2p_rx_action(struct wpa_supplicant *wpa_s, const u8 *da,
+ const u8 *sa, const u8 *bssid,
+ u8 category, const u8 *data, size_t len, int freq);
+void wpas_p2p_scan_ie(struct wpa_supplicant *wpa_s, struct wpabuf *ies);
+void wpas_p2p_group_deinit(struct wpa_supplicant *wpa_s);
+void wpas_dev_found(void *ctx, const u8 *addr,
+ const struct p2p_peer_info *info,
+ int new_device);
+void wpas_go_neg_completed(void *ctx, struct p2p_go_neg_results *res);
+void wpas_go_neg_req_rx(void *ctx, const u8 *src, u16 dev_passwd_id);
+void wpas_prov_disc_req(void *ctx, const u8 *peer, u16 config_methods,
+ const u8 *dev_addr, const u8 *pri_dev_type,
+ const char *dev_name, u16 supp_config_methods,
+ u8 dev_capab, u8 group_capab, const u8 *group_id,
+ size_t group_id_len);
+void wpas_prov_disc_resp(void *ctx, const u8 *peer, u16 config_methods);
+void wpas_sd_request(void *ctx, int freq, const u8 *sa, u8 dialog_token,
+ u16 update_indic, const u8 *tlvs, size_t tlvs_len);
+void wpas_sd_response(void *ctx, const u8 *sa, u16 update_indic,
+ const u8 *tlvs, size_t tlvs_len);
+u64 wpas_p2p_sd_request(struct wpa_supplicant *wpa_s, const u8 *dst,
+ const struct wpabuf *tlvs);
+u64 wpas_p2p_sd_request_upnp(struct wpa_supplicant *wpa_s, const u8 *dst,
+ u8 version, const char *query);
+u64 wpas_p2p_sd_request_wifi_display(struct wpa_supplicant *wpa_s,
+ const u8 *dst, const char *role);
+int wpas_p2p_sd_cancel_request(struct wpa_supplicant *wpa_s, u64 req);
+void wpas_p2p_sd_response(struct wpa_supplicant *wpa_s, int freq,
+ const u8 *dst, u8 dialog_token,
+ const struct wpabuf *resp_tlvs);
+void wpas_p2p_sd_service_update(struct wpa_supplicant *wpa_s);
+void wpas_p2p_service_flush(struct wpa_supplicant *wpa_s);
+int wpas_p2p_service_add_bonjour(struct wpa_supplicant *wpa_s,
+ struct wpabuf *query, struct wpabuf *resp);
+int wpas_p2p_service_del_bonjour(struct wpa_supplicant *wpa_s,
+ const struct wpabuf *query);
+int wpas_p2p_service_add_upnp(struct wpa_supplicant *wpa_s, u8 version,
+ const char *service);
+int wpas_p2p_service_del_upnp(struct wpa_supplicant *wpa_s, u8 version,
+ const char *service);
+int wpas_p2p_reject(struct wpa_supplicant *wpa_s, const u8 *addr);
+int wpas_p2p_invite(struct wpa_supplicant *wpa_s, const u8 *peer_addr,
+ struct wpa_ssid *ssid, const u8 *go_dev_addr, int freq,
+ int ht40);
+int wpas_p2p_invite_group(struct wpa_supplicant *wpa_s, const char *ifname,
+ const u8 *peer_addr, const u8 *go_dev_addr);
+void wpas_p2p_completed(struct wpa_supplicant *wpa_s);
+int wpas_p2p_presence_req(struct wpa_supplicant *wpa_s, u32 duration1,
+ u32 interval1, u32 duration2, u32 interval2);
+int wpas_p2p_ext_listen(struct wpa_supplicant *wpa_s, unsigned int period,
+ unsigned int interval);
+int wpas_p2p_deauth_notif(struct wpa_supplicant *wpa_s, const u8 *bssid,
+ u16 reason_code, const u8 *ie, size_t ie_len,
+ int locally_generated);
+void wpas_p2p_disassoc_notif(struct wpa_supplicant *wpa_s, const u8 *bssid,
+ u16 reason_code, const u8 *ie, size_t ie_len,
+ int locally_generated);
+void wpas_p2p_update_config(struct wpa_supplicant *wpa_s);
+int wpas_p2p_set_noa(struct wpa_supplicant *wpa_s, u8 count, int start,
+ int duration);
+int wpas_p2p_set_cross_connect(struct wpa_supplicant *wpa_s, int enabled);
+void wpas_p2p_notif_connected(struct wpa_supplicant *wpa_s);
+void wpas_p2p_notif_disconnected(struct wpa_supplicant *wpa_s);
+int wpas_p2p_notif_pbc_overlap(struct wpa_supplicant *wpa_s);
+void wpas_p2p_update_channel_list(struct wpa_supplicant *wpa_s);
+int wpas_p2p_cancel(struct wpa_supplicant *wpa_s);
+void wpas_p2p_interface_unavailable(struct wpa_supplicant *wpa_s);
+void wpas_p2p_update_best_channels(struct wpa_supplicant *wpa_s,
+ int freq_24, int freq_5, int freq_overall);
+int wpas_p2p_unauthorize(struct wpa_supplicant *wpa_s, const char *addr);
+int wpas_p2p_disconnect(struct wpa_supplicant *wpa_s);
+void wpas_p2p_wps_failed(struct wpa_supplicant *wpa_s,
+ struct wps_event_fail *fail);
+int wpas_p2p_in_progress(struct wpa_supplicant *wpa_s);
+void wpas_p2p_network_removed(struct wpa_supplicant *wpa_s,
+ struct wpa_ssid *ssid);
+struct wpa_ssid * wpas_p2p_get_persistent(struct wpa_supplicant *wpa_s,
+ const u8 *addr, const u8 *ssid,
+ size_t ssid_len);
+void wpas_p2p_notify_ap_sta_authorized(struct wpa_supplicant *wpa_s,
+ const u8 *addr);
+int wpas_p2p_scan_no_go_seen(struct wpa_supplicant *wpa_s);
+int wpas_p2p_get_ht40_mode(struct wpa_supplicant *wpa_s,
+ struct hostapd_hw_modes *mode, u8 channel);
+unsigned int wpas_p2p_search_delay(struct wpa_supplicant *wpa_s);
+
+#endif /* P2P_SUPPLICANT_H */
diff --git a/contrib/wpa/wpa_supplicant/preauth_test.c b/contrib/wpa/wpa_supplicant/preauth_test.c
index d38a6bb..3503e65 100644
--- a/contrib/wpa/wpa_supplicant/preauth_test.c
+++ b/contrib/wpa/wpa_supplicant/preauth_test.c
@@ -2,14 +2,8 @@
* WPA Supplicant - test code for pre-authentication
* Copyright (c) 2003-2007, Jouni Malinen <j@w1.fi>
*
- * This program is free software; you can redistribute it and/or modify
- * it under the terms of the GNU General Public License version 2 as
- * published by the Free Software Foundation.
- *
- * Alternatively, this software may be distributed under the terms of BSD
- * license.
- *
- * See README and COPYING for more details.
+ * This software may be distributed under the terms of the BSD license.
+ * See README for more details.
*
* IEEE 802.1X Supplicant test code (to be used in place of wpa_supplicant.c.
* Not used in production version.
@@ -44,12 +38,6 @@ struct preauth_test_data {
};
-static void _wpa_supplicant_disassociate(void *wpa_s, int reason_code)
-{
- wpa_supplicant_disassociate(wpa_s, reason_code);
-}
-
-
static void _wpa_supplicant_deauthenticate(void *wpa_s, int reason_code)
{
wpa_supplicant_deauthenticate(wpa_s, reason_code);
@@ -244,7 +232,6 @@ static void wpa_init_conf(struct wpa_supplicant *wpa_s, const char *ifname)
ctx->set_state = _wpa_supplicant_set_state;
ctx->get_state = _wpa_supplicant_get_state;
ctx->deauthenticate = _wpa_supplicant_deauthenticate;
- ctx->disassociate = _wpa_supplicant_disassociate;
ctx->set_key = wpa_supplicant_set_key;
ctx->get_network_ctx = wpa_supplicant_get_network_ctx;
ctx->get_bssid = wpa_supplicant_get_bssid;
diff --git a/contrib/wpa/wpa_supplicant/scan.c b/contrib/wpa/wpa_supplicant/scan.c
index edc8c83..d2b671a 100644
--- a/contrib/wpa/wpa_supplicant/scan.c
+++ b/contrib/wpa/wpa_supplicant/scan.c
@@ -1,15 +1,9 @@
/*
* WPA Supplicant - Scanning
- * Copyright (c) 2003-2010, Jouni Malinen <j@w1.fi>
+ * Copyright (c) 2003-2012, Jouni Malinen <j@w1.fi>
*
- * This program is free software; you can redistribute it and/or modify
- * it under the terms of the GNU General Public License version 2 as
- * published by the Free Software Foundation.
- *
- * Alternatively, this software may be distributed under the terms of BSD
- * license.
- *
- * See README and COPYING for more details.
+ * This software may be distributed under the terms of the BSD license.
+ * See README for more details.
*/
#include "utils/includes.h"
@@ -20,8 +14,10 @@
#include "config.h"
#include "wpa_supplicant_i.h"
#include "driver_i.h"
-#include "mlme.h"
#include "wps_supplicant.h"
+#include "p2p_supplicant.h"
+#include "p2p/p2p.h"
+#include "hs20_supplicant.h"
#include "notify.h"
#include "bss.h"
#include "scan.h"
@@ -42,21 +38,21 @@ static void wpa_supplicant_gen_assoc_event(struct wpa_supplicant *wpa_s)
wpas_notify_network_changed(wpa_s);
}
wpa_supplicant_initiate_eapol(wpa_s);
- wpa_printf(MSG_DEBUG, "Already associated with a configured network - "
- "generating associated event");
+ wpa_dbg(wpa_s, MSG_DEBUG, "Already associated with a configured "
+ "network - generating associated event");
os_memset(&data, 0, sizeof(data));
wpa_supplicant_event(wpa_s, EVENT_ASSOC, &data);
}
#ifdef CONFIG_WPS
-static int wpas_wps_in_use(struct wpa_config *conf,
+static int wpas_wps_in_use(struct wpa_supplicant *wpa_s,
enum wps_request_type *req_type)
{
struct wpa_ssid *ssid;
int wps = 0;
- for (ssid = conf->ssid; ssid; ssid = ssid->next) {
+ for (ssid = wpa_s->conf->ssid; ssid; ssid = ssid->next) {
if (!(ssid->key_mgmt & WPA_KEY_MGMT_WPS))
continue;
@@ -69,20 +65,50 @@ static int wpas_wps_in_use(struct wpa_config *conf,
return 2;
}
+#ifdef CONFIG_P2P
+ if (!wpa_s->global->p2p_disabled && wpa_s->global->p2p &&
+ !wpa_s->conf->p2p_disabled) {
+ wpa_s->wps->dev.p2p = 1;
+ if (!wps) {
+ wps = 1;
+ *req_type = WPS_REQ_ENROLLEE_INFO;
+ }
+ }
+#endif /* CONFIG_P2P */
+
return wps;
}
#endif /* CONFIG_WPS */
-int wpa_supplicant_enabled_networks(struct wpa_config *conf)
+/**
+ * wpa_supplicant_enabled_networks - Check whether there are enabled networks
+ * @wpa_s: Pointer to wpa_supplicant data
+ * Returns: 0 if no networks are enabled, >0 if networks are enabled
+ *
+ * This function is used to figure out whether any networks (or Interworking
+ * with enabled credentials and auto_interworking) are present in the current
+ * configuration.
+ */
+int wpa_supplicant_enabled_networks(struct wpa_supplicant *wpa_s)
{
- struct wpa_ssid *ssid = conf->ssid;
+ struct wpa_ssid *ssid = wpa_s->conf->ssid;
+ int count = 0, disabled = 0;
while (ssid) {
- if (!ssid->disabled)
- return 1;
+ if (!wpas_network_disabled(wpa_s, ssid))
+ count++;
+ else
+ disabled++;
ssid = ssid->next;
}
- return 0;
+ if (wpa_s->conf->cred && wpa_s->conf->interworking &&
+ wpa_s->conf->auto_interworking)
+ count++;
+ if (count == 0 && disabled > 0) {
+ wpa_dbg(wpa_s, MSG_DEBUG, "No enabled networks (%d disabled "
+ "networks)", disabled);
+ }
+ return count;
}
@@ -90,15 +116,15 @@ static void wpa_supplicant_assoc_try(struct wpa_supplicant *wpa_s,
struct wpa_ssid *ssid)
{
while (ssid) {
- if (!ssid->disabled)
+ if (!wpas_network_disabled(wpa_s, ssid))
break;
ssid = ssid->next;
}
/* ap_scan=2 mode - try to associate with each SSID. */
if (ssid == NULL) {
- wpa_printf(MSG_DEBUG, "wpa_supplicant_scan: Reached "
- "end of scan list - go back to beginning");
+ wpa_dbg(wpa_s, MSG_DEBUG, "wpa_supplicant_assoc_try: Reached "
+ "end of scan list - go back to beginning");
wpa_s->prev_scan_ssid = WILDCARD_SSID_SCAN;
wpa_supplicant_req_scan(wpa_s, 0, 0);
return;
@@ -131,7 +157,7 @@ static void int_array_concat(int **res, const int *a)
reslen = int_array_len(*res);
alen = int_array_len(a);
- n = os_realloc(*res, (reslen + alen + 1) * sizeof(int));
+ n = os_realloc_array(*res, reslen + alen + 1, sizeof(int));
if (n == NULL) {
os_free(*res);
*res = NULL;
@@ -182,6 +208,12 @@ static void int_array_sort_unique(int *a)
}
+/**
+ * wpa_supplicant_trigger_scan - Request driver to start a scan
+ * @wpa_s: Pointer to wpa_supplicant data
+ * @params: Scan parameters
+ * Returns: 0 on success, -1 on failure
+ */
int wpa_supplicant_trigger_scan(struct wpa_supplicant *wpa_s,
struct wpa_driver_scan_params *params)
{
@@ -189,16 +221,71 @@ int wpa_supplicant_trigger_scan(struct wpa_supplicant *wpa_s,
wpa_supplicant_notify_scanning(wpa_s, 1);
- if (wpa_s->drv_flags & WPA_DRIVER_FLAGS_USER_SPACE_MLME)
- ret = ieee80211_sta_req_scan(wpa_s, params);
- else
- ret = wpa_drv_scan(wpa_s, params);
-
+ ret = wpa_drv_scan(wpa_s, params);
if (ret) {
wpa_supplicant_notify_scanning(wpa_s, 0);
wpas_notify_scan_done(wpa_s, 0);
- } else
+ } else {
wpa_s->scan_runs++;
+ wpa_s->normal_scans++;
+ }
+
+ return ret;
+}
+
+
+static void
+wpa_supplicant_delayed_sched_scan_timeout(void *eloop_ctx, void *timeout_ctx)
+{
+ struct wpa_supplicant *wpa_s = eloop_ctx;
+
+ wpa_dbg(wpa_s, MSG_DEBUG, "Starting delayed sched scan");
+
+ if (wpa_supplicant_req_sched_scan(wpa_s))
+ wpa_supplicant_req_scan(wpa_s, 0, 0);
+}
+
+
+static void
+wpa_supplicant_sched_scan_timeout(void *eloop_ctx, void *timeout_ctx)
+{
+ struct wpa_supplicant *wpa_s = eloop_ctx;
+
+ wpa_dbg(wpa_s, MSG_DEBUG, "Sched scan timeout - stopping it");
+
+ wpa_s->sched_scan_timed_out = 1;
+ wpa_supplicant_cancel_sched_scan(wpa_s);
+}
+
+
+static int
+wpa_supplicant_start_sched_scan(struct wpa_supplicant *wpa_s,
+ struct wpa_driver_scan_params *params,
+ int interval)
+{
+ int ret;
+
+ wpa_supplicant_notify_scanning(wpa_s, 1);
+ ret = wpa_drv_sched_scan(wpa_s, params, interval * 1000);
+ if (ret)
+ wpa_supplicant_notify_scanning(wpa_s, 0);
+ else
+ wpa_s->sched_scanning = 1;
+
+ return ret;
+}
+
+
+static int wpa_supplicant_stop_sched_scan(struct wpa_supplicant *wpa_s)
+{
+ int ret;
+
+ ret = wpa_drv_stop_sched_scan(wpa_s);
+ if (ret) {
+ wpa_dbg(wpa_s, MSG_DEBUG, "stopping sched_scan failed!");
+ /* TODO: what to do if stopping fails? */
+ return -1;
+ }
return ret;
}
@@ -237,36 +324,235 @@ wpa_supplicant_build_filter_ssids(struct wpa_config *conf, size_t *num_ssids)
}
-static void wpa_supplicant_scan(void *eloop_ctx, void *timeout_ctx)
+static void wpa_supplicant_optimize_freqs(
+ struct wpa_supplicant *wpa_s, struct wpa_driver_scan_params *params)
{
- struct wpa_supplicant *wpa_s = eloop_ctx;
- struct wpa_ssid *ssid;
- int scan_req = 0, ret;
- struct wpabuf *wps_ie = NULL;
+#ifdef CONFIG_P2P
+ if (params->freqs == NULL && wpa_s->p2p_in_provisioning &&
+ wpa_s->go_params) {
+ /* Optimize provisioning state scan based on GO information */
+ if (wpa_s->p2p_in_provisioning < 5 &&
+ wpa_s->go_params->freq > 0) {
+ wpa_dbg(wpa_s, MSG_DEBUG, "P2P: Scan only GO "
+ "preferred frequency %d MHz",
+ wpa_s->go_params->freq);
+ params->freqs = os_zalloc(2 * sizeof(int));
+ if (params->freqs)
+ params->freqs[0] = wpa_s->go_params->freq;
+ } else if (wpa_s->p2p_in_provisioning < 8 &&
+ wpa_s->go_params->freq_list[0]) {
+ wpa_dbg(wpa_s, MSG_DEBUG, "P2P: Scan only common "
+ "channels");
+ int_array_concat(&params->freqs,
+ wpa_s->go_params->freq_list);
+ if (params->freqs)
+ int_array_sort_unique(params->freqs);
+ }
+ wpa_s->p2p_in_provisioning++;
+ }
+#endif /* CONFIG_P2P */
+
+#ifdef CONFIG_WPS
+ if (params->freqs == NULL && wpa_s->after_wps && wpa_s->wps_freq) {
+ /*
+ * Optimize post-provisioning scan based on channel used
+ * during provisioning.
+ */
+ wpa_dbg(wpa_s, MSG_DEBUG, "WPS: Scan only frequency %u MHz "
+ "that was used during provisioning", wpa_s->wps_freq);
+ params->freqs = os_zalloc(2 * sizeof(int));
+ if (params->freqs)
+ params->freqs[0] = wpa_s->wps_freq;
+ wpa_s->after_wps--;
+ }
+
+ if (params->freqs == NULL && wpa_s->known_wps_freq && wpa_s->wps_freq)
+ {
+ /* Optimize provisioning scan based on already known channel */
+ wpa_dbg(wpa_s, MSG_DEBUG, "WPS: Scan only frequency %u MHz",
+ wpa_s->wps_freq);
+ params->freqs = os_zalloc(2 * sizeof(int));
+ if (params->freqs)
+ params->freqs[0] = wpa_s->wps_freq;
+ wpa_s->known_wps_freq = 0; /* only do this once */
+ }
+#endif /* CONFIG_WPS */
+}
+
+
+#ifdef CONFIG_INTERWORKING
+static void wpas_add_interworking_elements(struct wpa_supplicant *wpa_s,
+ struct wpabuf *buf)
+{
+ if (wpa_s->conf->interworking == 0)
+ return;
+
+ wpabuf_put_u8(buf, WLAN_EID_EXT_CAPAB);
+ wpabuf_put_u8(buf, 4);
+ wpabuf_put_u8(buf, 0x00);
+ wpabuf_put_u8(buf, 0x00);
+ wpabuf_put_u8(buf, 0x00);
+ wpabuf_put_u8(buf, 0x80); /* Bit 31 - Interworking */
+
+ wpabuf_put_u8(buf, WLAN_EID_INTERWORKING);
+ wpabuf_put_u8(buf, is_zero_ether_addr(wpa_s->conf->hessid) ? 1 :
+ 1 + ETH_ALEN);
+ wpabuf_put_u8(buf, wpa_s->conf->access_network_type);
+ /* No Venue Info */
+ if (!is_zero_ether_addr(wpa_s->conf->hessid))
+ wpabuf_put_data(buf, wpa_s->conf->hessid, ETH_ALEN);
+}
+#endif /* CONFIG_INTERWORKING */
+
+
+static struct wpabuf * wpa_supplicant_extra_ies(struct wpa_supplicant *wpa_s)
+{
+ struct wpabuf *extra_ie = NULL;
#ifdef CONFIG_WPS
int wps = 0;
enum wps_request_type req_type = WPS_REQ_ENROLLEE_INFO;
#endif /* CONFIG_WPS */
+
+#ifdef CONFIG_INTERWORKING
+ if (wpa_s->conf->interworking &&
+ wpabuf_resize(&extra_ie, 100) == 0)
+ wpas_add_interworking_elements(wpa_s, extra_ie);
+#endif /* CONFIG_INTERWORKING */
+
+#ifdef CONFIG_WPS
+ wps = wpas_wps_in_use(wpa_s, &req_type);
+
+ if (wps) {
+ struct wpabuf *wps_ie;
+ wps_ie = wps_build_probe_req_ie(wps == 2 ? DEV_PW_PUSHBUTTON :
+ DEV_PW_DEFAULT,
+ &wpa_s->wps->dev,
+ wpa_s->wps->uuid, req_type,
+ 0, NULL);
+ if (wps_ie) {
+ if (wpabuf_resize(&extra_ie, wpabuf_len(wps_ie)) == 0)
+ wpabuf_put_buf(extra_ie, wps_ie);
+ wpabuf_free(wps_ie);
+ }
+ }
+
+#ifdef CONFIG_P2P
+ if (wps) {
+ size_t ielen = p2p_scan_ie_buf_len(wpa_s->global->p2p);
+ if (wpabuf_resize(&extra_ie, ielen) == 0)
+ wpas_p2p_scan_ie(wpa_s, extra_ie);
+ }
+#endif /* CONFIG_P2P */
+
+#endif /* CONFIG_WPS */
+
+ return extra_ie;
+}
+
+
+#ifdef CONFIG_P2P
+
+/*
+ * Check whether there are any enabled networks or credentials that could be
+ * used for a non-P2P connection.
+ */
+static int non_p2p_network_enabled(struct wpa_supplicant *wpa_s)
+{
+ struct wpa_ssid *ssid;
+
+ for (ssid = wpa_s->conf->ssid; ssid; ssid = ssid->next) {
+ if (wpas_network_disabled(wpa_s, ssid))
+ continue;
+ if (!ssid->p2p_group)
+ return 1;
+ }
+
+ if (wpa_s->conf->cred && wpa_s->conf->interworking &&
+ wpa_s->conf->auto_interworking)
+ return 1;
+
+ return 0;
+}
+
+
+/*
+ * Find the operating frequency of any other virtual interface that is using
+ * the same radio concurrently.
+ */
+static int shared_vif_oper_freq(struct wpa_supplicant *wpa_s)
+{
+ const char *rn, *rn2;
+ struct wpa_supplicant *ifs;
+ u8 bssid[ETH_ALEN];
+
+ if (!wpa_s->driver->get_radio_name)
+ return -1;
+
+ rn = wpa_s->driver->get_radio_name(wpa_s->drv_priv);
+ if (rn == NULL || rn[0] == '\0')
+ return -1;
+
+ for (ifs = wpa_s->global->ifaces; ifs; ifs = ifs->next) {
+ if (ifs == wpa_s || !ifs->driver->get_radio_name)
+ continue;
+
+ rn2 = ifs->driver->get_radio_name(ifs->drv_priv);
+ if (!rn2 || os_strcmp(rn, rn2) != 0)
+ continue;
+
+ if (ifs->current_ssid == NULL || ifs->assoc_freq == 0)
+ continue;
+
+ if (ifs->current_ssid->mode == WPAS_MODE_AP ||
+ ifs->current_ssid->mode == WPAS_MODE_P2P_GO)
+ return ifs->current_ssid->frequency;
+ if (wpa_drv_get_bssid(ifs, bssid) == 0)
+ return ifs->assoc_freq;
+ }
+
+ return 0;
+}
+
+#endif /* CONFIG_P2P */
+
+
+static void wpa_supplicant_scan(void *eloop_ctx, void *timeout_ctx)
+{
+ struct wpa_supplicant *wpa_s = eloop_ctx;
+ struct wpa_ssid *ssid;
+ enum scan_req_type scan_req = NORMAL_SCAN_REQ;
+ int ret;
+ struct wpabuf *extra_ie = NULL;
struct wpa_driver_scan_params params;
+ struct wpa_driver_scan_params *scan_params;
size_t max_ssids;
enum wpa_states prev_state;
- if (wpa_s->disconnected && !wpa_s->scan_req) {
+ if (wpa_s->wpa_state == WPA_INTERFACE_DISABLED) {
+ wpa_dbg(wpa_s, MSG_DEBUG, "Skip scan - interface disabled");
+ return;
+ }
+
+ if (wpa_s->disconnected && wpa_s->scan_req == NORMAL_SCAN_REQ) {
+ wpa_dbg(wpa_s, MSG_DEBUG, "Disconnected - do not scan");
wpa_supplicant_set_state(wpa_s, WPA_DISCONNECTED);
return;
}
- if (!wpa_supplicant_enabled_networks(wpa_s->conf) &&
- !wpa_s->scan_req) {
- wpa_printf(MSG_DEBUG, "No enabled networks - do not scan");
+ if (!wpa_supplicant_enabled_networks(wpa_s) &&
+ wpa_s->scan_req == NORMAL_SCAN_REQ) {
+ wpa_dbg(wpa_s, MSG_DEBUG, "No enabled networks - do not scan");
wpa_supplicant_set_state(wpa_s, WPA_INACTIVE);
+#ifdef CONFIG_P2P
+ wpa_s->sta_scan_pending = 0;
+#endif /* CONFIG_P2P */
return;
}
if (wpa_s->conf->ap_scan != 0 &&
(wpa_s->drv_flags & WPA_DRIVER_FLAGS_WIRED)) {
- wpa_printf(MSG_DEBUG, "Using wired authentication - "
- "overriding ap_scan configuration");
+ wpa_dbg(wpa_s, MSG_DEBUG, "Using wired authentication - "
+ "overriding ap_scan configuration");
wpa_s->conf->ap_scan = 0;
wpas_notify_ap_scan_changed(wpa_s);
}
@@ -276,8 +562,24 @@ static void wpa_supplicant_scan(void *eloop_ctx, void *timeout_ctx)
return;
}
- if ((wpa_s->drv_flags & WPA_DRIVER_FLAGS_USER_SPACE_MLME) ||
- wpa_s->conf->ap_scan == 2)
+#ifdef CONFIG_P2P
+ if (wpas_p2p_in_progress(wpa_s)) {
+ if (wpa_s->sta_scan_pending &&
+ wpas_p2p_in_progress(wpa_s) == 2 &&
+ wpa_s->global->p2p_cb_on_scan_complete) {
+ wpa_dbg(wpa_s, MSG_DEBUG, "Process pending station "
+ "mode scan during P2P search");
+ } else {
+ wpa_dbg(wpa_s, MSG_DEBUG, "Delay station mode scan "
+ "while P2P operation is in progress");
+ wpa_s->sta_scan_pending = 1;
+ wpa_supplicant_req_scan(wpa_s, 5, 0);
+ return;
+ }
+ }
+#endif /* CONFIG_P2P */
+
+ if (wpa_s->conf->ap_scan == 2)
max_ssids = 1;
else {
max_ssids = wpa_s->max_scan_ssids;
@@ -285,12 +587,8 @@ static void wpa_supplicant_scan(void *eloop_ctx, void *timeout_ctx)
max_ssids = WPAS_MAX_SCAN_SSIDS;
}
-#ifdef CONFIG_WPS
- wps = wpas_wps_in_use(wpa_s->conf, &req_type);
-#endif /* CONFIG_WPS */
-
scan_req = wpa_s->scan_req;
- wpa_s->scan_req = 0;
+ wpa_s->scan_req = NORMAL_SCAN_REQ;
os_memset(&params, 0, sizeof(params));
@@ -299,6 +597,40 @@ static void wpa_supplicant_scan(void *eloop_ctx, void *timeout_ctx)
wpa_s->wpa_state == WPA_INACTIVE)
wpa_supplicant_set_state(wpa_s, WPA_SCANNING);
+ /*
+ * If autoscan has set its own scanning parameters
+ */
+ if (wpa_s->autoscan_params != NULL) {
+ scan_params = wpa_s->autoscan_params;
+ goto scan;
+ }
+
+ if (scan_req != MANUAL_SCAN_REQ && wpa_s->connect_without_scan) {
+ for (ssid = wpa_s->conf->ssid; ssid; ssid = ssid->next) {
+ if (ssid == wpa_s->connect_without_scan)
+ break;
+ }
+ wpa_s->connect_without_scan = NULL;
+ if (ssid) {
+ wpa_printf(MSG_DEBUG, "Start a pre-selected network "
+ "without scan step");
+ wpa_supplicant_associate(wpa_s, NULL, ssid);
+ return;
+ }
+ }
+
+#ifdef CONFIG_P2P
+ if ((wpa_s->p2p_in_provisioning || wpa_s->show_group_started) &&
+ wpa_s->go_params) {
+ wpa_printf(MSG_DEBUG, "P2P: Use specific SSID for scan during "
+ "P2P group formation");
+ params.ssids[0].ssid = wpa_s->go_params->ssid;
+ params.ssids[0].ssid_len = wpa_s->go_params->ssid_len;
+ params.num_ssids = 1;
+ goto ssid_list_set;
+ }
+#endif /* CONFIG_P2P */
+
/* Find the starting point from which to continue scanning */
ssid = wpa_s->conf->ssid;
if (wpa_s->prev_scan_ssid != WILDCARD_SSID_SCAN) {
@@ -311,9 +643,9 @@ static void wpa_supplicant_scan(void *eloop_ctx, void *timeout_ctx)
}
}
- if (scan_req != 2 && (wpa_s->conf->ap_scan == 2 ||
- wpa_s->connect_without_scan)) {
- wpa_s->connect_without_scan = 0;
+ if (scan_req != MANUAL_SCAN_REQ && wpa_s->conf->ap_scan == 2) {
+ wpa_s->connect_without_scan = NULL;
+ wpa_s->prev_scan_wildcard = 0;
wpa_supplicant_assoc_try(wpa_s, ssid);
return;
} else if (wpa_s->conf->ap_scan == 2) {
@@ -328,7 +660,8 @@ static void wpa_supplicant_scan(void *eloop_ctx, void *timeout_ctx)
if (ssid == NULL && max_ssids > 1)
ssid = wpa_s->conf->ssid;
while (ssid) {
- if (!ssid->disabled && ssid->scan_ssid) {
+ if (!wpas_network_disabled(wpa_s, ssid) &&
+ ssid->scan_ssid) {
wpa_hexdump_ascii(MSG_DEBUG, "Scan SSID",
ssid->ssid, ssid->ssid_len);
params.ssids[params.num_ssids].ssid =
@@ -348,7 +681,7 @@ static void wpa_supplicant_scan(void *eloop_ctx, void *timeout_ctx)
}
for (tssid = wpa_s->conf->ssid; tssid; tssid = tssid->next) {
- if (tssid->disabled)
+ if (wpas_network_disabled(wpa_s, tssid))
continue;
if ((params.freqs || !freqs_set) && tssid->scan_freq) {
int_array_concat(&params.freqs,
@@ -362,58 +695,123 @@ static void wpa_supplicant_scan(void *eloop_ctx, void *timeout_ctx)
int_array_sort_unique(params.freqs);
}
- if (ssid) {
- wpa_s->prev_scan_ssid = ssid;
- if (max_ssids > 1) {
- wpa_printf(MSG_DEBUG, "Include wildcard SSID in the "
- "scan request");
- params.num_ssids++;
+ if (ssid && max_ssids == 1) {
+ /*
+ * If the driver is limited to 1 SSID at a time interleave
+ * wildcard SSID scans with specific SSID scans to avoid
+ * waiting a long time for a wildcard scan.
+ */
+ if (!wpa_s->prev_scan_wildcard) {
+ params.ssids[0].ssid = NULL;
+ params.ssids[0].ssid_len = 0;
+ wpa_s->prev_scan_wildcard = 1;
+ wpa_dbg(wpa_s, MSG_DEBUG, "Starting AP scan for "
+ "wildcard SSID (Interleave with specific)");
+ } else {
+ wpa_s->prev_scan_ssid = ssid;
+ wpa_s->prev_scan_wildcard = 0;
+ wpa_dbg(wpa_s, MSG_DEBUG,
+ "Starting AP scan for specific SSID: %s",
+ wpa_ssid_txt(ssid->ssid, ssid->ssid_len));
}
- wpa_printf(MSG_DEBUG, "Starting AP scan for specific SSID(s)");
+ } else if (ssid) {
+ /* max_ssids > 1 */
+
+ wpa_s->prev_scan_ssid = ssid;
+ wpa_dbg(wpa_s, MSG_DEBUG, "Include wildcard SSID in "
+ "the scan request");
+ params.num_ssids++;
} else {
wpa_s->prev_scan_ssid = WILDCARD_SSID_SCAN;
params.num_ssids++;
- wpa_printf(MSG_DEBUG, "Starting AP scan for wildcard SSID");
+ wpa_dbg(wpa_s, MSG_DEBUG, "Starting AP scan for wildcard "
+ "SSID");
}
+#ifdef CONFIG_P2P
+ssid_list_set:
+#endif /* CONFIG_P2P */
+
+ wpa_supplicant_optimize_freqs(wpa_s, &params);
+ extra_ie = wpa_supplicant_extra_ies(wpa_s);
+
+#ifdef CONFIG_HS20
+ if (wpa_s->conf->hs20 && wpabuf_resize(&extra_ie, 6) == 0)
+ wpas_hs20_add_indication(extra_ie);
+#endif /* CONFIG_HS20 */
+
+ if (params.freqs == NULL && wpa_s->next_scan_freqs) {
+ wpa_dbg(wpa_s, MSG_DEBUG, "Optimize scan based on previously "
+ "generated frequency list");
+ params.freqs = wpa_s->next_scan_freqs;
+ } else
+ os_free(wpa_s->next_scan_freqs);
+ wpa_s->next_scan_freqs = NULL;
-#ifdef CONFIG_WPS
- if (params.freqs == NULL && wpa_s->after_wps && wpa_s->wps_freq) {
+ params.filter_ssids = wpa_supplicant_build_filter_ssids(
+ wpa_s->conf, &params.num_filter_ssids);
+ if (extra_ie) {
+ params.extra_ies = wpabuf_head(extra_ie);
+ params.extra_ies_len = wpabuf_len(extra_ie);
+ }
+
+#ifdef CONFIG_P2P
+ if (wpa_s->p2p_in_provisioning ||
+ (wpa_s->show_group_started && wpa_s->go_params)) {
/*
- * Optimize post-provisioning scan based on channel used
- * during provisioning.
+ * The interface may not yet be in P2P mode, so we have to
+ * explicitly request P2P probe to disable CCK rates.
*/
- wpa_printf(MSG_DEBUG, "WPS: Scan only frequency %u MHz that "
- "was used during provisioning", wpa_s->wps_freq);
- params.freqs = os_zalloc(2 * sizeof(int));
- if (params.freqs)
- params.freqs[0] = wpa_s->wps_freq;
- wpa_s->after_wps--;
+ params.p2p_probe = 1;
}
-
- if (wps) {
- wps_ie = wps_build_probe_req_ie(wps == 2, &wpa_s->wps->dev,
- wpa_s->wps->uuid, req_type);
- if (wps_ie) {
- params.extra_ies = wpabuf_head(wps_ie);
- params.extra_ies_len = wpabuf_len(wps_ie);
+#endif /* CONFIG_P2P */
+
+ scan_params = &params;
+
+scan:
+#ifdef CONFIG_P2P
+ /*
+ * If the driver does not support multi-channel concurrency and a
+ * virtual interface that shares the same radio with the wpa_s interface
+ * is operating there may not be need to scan other channels apart from
+ * the current operating channel on the other virtual interface. Filter
+ * out other channels in case we are trying to find a connection for a
+ * station interface when we are not configured to prefer station
+ * connection and a concurrent operation is already in process.
+ */
+ if (wpa_s->scan_for_connection && scan_req == NORMAL_SCAN_REQ &&
+ !scan_params->freqs && !params.freqs &&
+ wpas_is_p2p_prioritized(wpa_s) &&
+ !(wpa_s->drv_flags & WPA_DRIVER_FLAGS_MULTI_CHANNEL_CONCURRENT) &&
+ wpa_s->p2p_group_interface == NOT_P2P_GROUP_INTERFACE &&
+ non_p2p_network_enabled(wpa_s)) {
+ int freq = shared_vif_oper_freq(wpa_s);
+ if (freq > 0) {
+ wpa_dbg(wpa_s, MSG_DEBUG, "P2P: Scan only the current "
+ "operating channel (%d MHz) since driver does "
+ "not support multi-channel concurrency", freq);
+ params.freqs = os_zalloc(sizeof(int) * 2);
+ if (params.freqs)
+ params.freqs[0] = freq;
+ scan_params->freqs = params.freqs;
}
}
-#endif /* CONFIG_WPS */
-
- params.filter_ssids = wpa_supplicant_build_filter_ssids(
- wpa_s->conf, &params.num_filter_ssids);
+#endif /* CONFIG_P2P */
- ret = wpa_supplicant_trigger_scan(wpa_s, &params);
+ ret = wpa_supplicant_trigger_scan(wpa_s, scan_params);
- wpabuf_free(wps_ie);
+ wpabuf_free(extra_ie);
os_free(params.freqs);
os_free(params.filter_ssids);
if (ret) {
- wpa_printf(MSG_WARNING, "Failed to initiate AP scan.");
+ wpa_msg(wpa_s, MSG_WARNING, "Failed to initiate AP scan");
if (prev_state != wpa_s->wpa_state)
wpa_supplicant_set_state(wpa_s, prev_state);
+ /* Restore scan_req since we will try to scan again */
+ wpa_s->scan_req = scan_req;
wpa_supplicant_req_scan(wpa_s, 1, 0);
+ } else {
+ wpa_s->scan_for_connection = 0;
}
}
@@ -440,18 +838,19 @@ void wpa_supplicant_req_scan(struct wpa_supplicant *wpa_s, int sec, int usec)
struct wpa_ssid *ssid = wpa_s->conf->ssid;
while (ssid) {
- if (!ssid->disabled && ssid->scan_ssid)
+ if (!wpas_network_disabled(wpa_s, ssid) &&
+ ssid->scan_ssid)
break;
ssid = ssid->next;
}
if (ssid) {
- wpa_msg(wpa_s, MSG_DEBUG, "Not rescheduling scan to "
+ wpa_dbg(wpa_s, MSG_DEBUG, "Not rescheduling scan to "
"ensure that specific SSID scans occur");
return;
}
}
- wpa_msg(wpa_s, MSG_DEBUG, "Setting scan request: %d sec %d usec",
+ wpa_dbg(wpa_s, MSG_DEBUG, "Setting scan request: %d sec %d usec",
sec, usec);
eloop_cancel_timeout(wpa_supplicant_scan, wpa_s, NULL);
eloop_register_timeout(sec, usec, wpa_supplicant_scan, wpa_s, NULL);
@@ -459,6 +858,251 @@ void wpa_supplicant_req_scan(struct wpa_supplicant *wpa_s, int sec, int usec)
/**
+ * wpa_supplicant_delayed_sched_scan - Request a delayed scheduled scan
+ * @wpa_s: Pointer to wpa_supplicant data
+ * @sec: Number of seconds after which to scan
+ * @usec: Number of microseconds after which to scan
+ * Returns: 0 on success or -1 otherwise
+ *
+ * This function is used to schedule periodic scans for neighboring
+ * access points after the specified time.
+ */
+int wpa_supplicant_delayed_sched_scan(struct wpa_supplicant *wpa_s,
+ int sec, int usec)
+{
+ if (!wpa_s->sched_scan_supported)
+ return -1;
+
+ eloop_register_timeout(sec, usec,
+ wpa_supplicant_delayed_sched_scan_timeout,
+ wpa_s, NULL);
+
+ return 0;
+}
+
+
+/**
+ * wpa_supplicant_req_sched_scan - Start a periodic scheduled scan
+ * @wpa_s: Pointer to wpa_supplicant data
+ * Returns: 0 is sched_scan was started or -1 otherwise
+ *
+ * This function is used to schedule periodic scans for neighboring
+ * access points repeating the scan continuously.
+ */
+int wpa_supplicant_req_sched_scan(struct wpa_supplicant *wpa_s)
+{
+ struct wpa_driver_scan_params params;
+ struct wpa_driver_scan_params *scan_params;
+ enum wpa_states prev_state;
+ struct wpa_ssid *ssid = NULL;
+ struct wpabuf *extra_ie = NULL;
+ int ret;
+ unsigned int max_sched_scan_ssids;
+ int wildcard = 0;
+ int need_ssids;
+
+ if (!wpa_s->sched_scan_supported)
+ return -1;
+
+ if (wpa_s->max_sched_scan_ssids > WPAS_MAX_SCAN_SSIDS)
+ max_sched_scan_ssids = WPAS_MAX_SCAN_SSIDS;
+ else
+ max_sched_scan_ssids = wpa_s->max_sched_scan_ssids;
+ if (max_sched_scan_ssids < 1 || wpa_s->conf->disable_scan_offload)
+ return -1;
+
+ if (wpa_s->sched_scanning) {
+ wpa_dbg(wpa_s, MSG_DEBUG, "Already sched scanning");
+ return 0;
+ }
+
+ need_ssids = 0;
+ for (ssid = wpa_s->conf->ssid; ssid; ssid = ssid->next) {
+ if (!wpas_network_disabled(wpa_s, ssid) && !ssid->scan_ssid) {
+ /* Use wildcard SSID to find this network */
+ wildcard = 1;
+ } else if (!wpas_network_disabled(wpa_s, ssid) &&
+ ssid->ssid_len)
+ need_ssids++;
+
+#ifdef CONFIG_WPS
+ if (!wpas_network_disabled(wpa_s, ssid) &&
+ ssid->key_mgmt == WPA_KEY_MGMT_WPS) {
+ /*
+ * Normal scan is more reliable and faster for WPS
+ * operations and since these are for short periods of
+ * time, the benefit of trying to use sched_scan would
+ * be limited.
+ */
+ wpa_dbg(wpa_s, MSG_DEBUG, "Use normal scan instead of "
+ "sched_scan for WPS");
+ return -1;
+ }
+#endif /* CONFIG_WPS */
+ }
+ if (wildcard)
+ need_ssids++;
+
+ if (wpa_s->normal_scans < 3 &&
+ (need_ssids <= wpa_s->max_scan_ssids ||
+ wpa_s->max_scan_ssids >= (int) max_sched_scan_ssids)) {
+ /*
+ * When normal scan can speed up operations, use that for the
+ * first operations before starting the sched_scan to allow
+ * user space sleep more. We do this only if the normal scan
+ * has functionality that is suitable for this or if the
+ * sched_scan does not have better support for multiple SSIDs.
+ */
+ wpa_dbg(wpa_s, MSG_DEBUG, "Use normal scan instead of "
+ "sched_scan for initial scans (normal_scans=%d)",
+ wpa_s->normal_scans);
+ return -1;
+ }
+
+ os_memset(&params, 0, sizeof(params));
+
+ /* If we can't allocate space for the filters, we just don't filter */
+ params.filter_ssids = os_zalloc(wpa_s->max_match_sets *
+ sizeof(struct wpa_driver_scan_filter));
+
+ prev_state = wpa_s->wpa_state;
+ if (wpa_s->wpa_state == WPA_DISCONNECTED ||
+ wpa_s->wpa_state == WPA_INACTIVE)
+ wpa_supplicant_set_state(wpa_s, WPA_SCANNING);
+
+ if (wpa_s->autoscan_params != NULL) {
+ scan_params = wpa_s->autoscan_params;
+ goto scan;
+ }
+
+ /* Find the starting point from which to continue scanning */
+ ssid = wpa_s->conf->ssid;
+ if (wpa_s->prev_sched_ssid) {
+ while (ssid) {
+ if (ssid == wpa_s->prev_sched_ssid) {
+ ssid = ssid->next;
+ break;
+ }
+ ssid = ssid->next;
+ }
+ }
+
+ if (!ssid || !wpa_s->prev_sched_ssid) {
+ wpa_dbg(wpa_s, MSG_DEBUG, "Beginning of SSID list");
+
+ if (wpa_s->sched_scan_interval == 0)
+ wpa_s->sched_scan_interval = 10;
+ wpa_s->sched_scan_timeout = max_sched_scan_ssids * 2;
+ wpa_s->first_sched_scan = 1;
+ ssid = wpa_s->conf->ssid;
+ wpa_s->prev_sched_ssid = ssid;
+ }
+
+ if (wildcard) {
+ wpa_dbg(wpa_s, MSG_DEBUG, "Add wildcard SSID to sched_scan");
+ params.num_ssids++;
+ }
+
+ while (ssid) {
+ if (wpas_network_disabled(wpa_s, ssid))
+ goto next;
+
+ if (params.num_filter_ssids < wpa_s->max_match_sets &&
+ params.filter_ssids && ssid->ssid && ssid->ssid_len) {
+ wpa_dbg(wpa_s, MSG_DEBUG, "add to filter ssid: %s",
+ wpa_ssid_txt(ssid->ssid, ssid->ssid_len));
+ os_memcpy(params.filter_ssids[params.num_filter_ssids].ssid,
+ ssid->ssid, ssid->ssid_len);
+ params.filter_ssids[params.num_filter_ssids].ssid_len =
+ ssid->ssid_len;
+ params.num_filter_ssids++;
+ } else if (params.filter_ssids && ssid->ssid && ssid->ssid_len)
+ {
+ wpa_dbg(wpa_s, MSG_DEBUG, "Not enough room for SSID "
+ "filter for sched_scan - drop filter");
+ os_free(params.filter_ssids);
+ params.filter_ssids = NULL;
+ params.num_filter_ssids = 0;
+ }
+
+ if (ssid->scan_ssid && ssid->ssid && ssid->ssid_len) {
+ if (params.num_ssids == max_sched_scan_ssids)
+ break; /* only room for broadcast SSID */
+ wpa_dbg(wpa_s, MSG_DEBUG,
+ "add to active scan ssid: %s",
+ wpa_ssid_txt(ssid->ssid, ssid->ssid_len));
+ params.ssids[params.num_ssids].ssid =
+ ssid->ssid;
+ params.ssids[params.num_ssids].ssid_len =
+ ssid->ssid_len;
+ params.num_ssids++;
+ if (params.num_ssids >= max_sched_scan_ssids) {
+ wpa_s->prev_sched_ssid = ssid;
+ do {
+ ssid = ssid->next;
+ } while (ssid &&
+ (wpas_network_disabled(wpa_s, ssid) ||
+ !ssid->scan_ssid));
+ break;
+ }
+ }
+
+ next:
+ wpa_s->prev_sched_ssid = ssid;
+ ssid = ssid->next;
+ }
+
+ if (params.num_filter_ssids == 0) {
+ os_free(params.filter_ssids);
+ params.filter_ssids = NULL;
+ }
+
+ extra_ie = wpa_supplicant_extra_ies(wpa_s);
+ if (extra_ie) {
+ params.extra_ies = wpabuf_head(extra_ie);
+ params.extra_ies_len = wpabuf_len(extra_ie);
+ }
+
+ scan_params = &params;
+
+scan:
+ if (ssid || !wpa_s->first_sched_scan) {
+ wpa_dbg(wpa_s, MSG_DEBUG,
+ "Starting sched scan: interval %d timeout %d",
+ wpa_s->sched_scan_interval, wpa_s->sched_scan_timeout);
+ } else {
+ wpa_dbg(wpa_s, MSG_DEBUG,
+ "Starting sched scan: interval %d (no timeout)",
+ wpa_s->sched_scan_interval);
+ }
+
+ ret = wpa_supplicant_start_sched_scan(wpa_s, scan_params,
+ wpa_s->sched_scan_interval);
+ wpabuf_free(extra_ie);
+ os_free(params.filter_ssids);
+ if (ret) {
+ wpa_msg(wpa_s, MSG_WARNING, "Failed to initiate sched scan");
+ if (prev_state != wpa_s->wpa_state)
+ wpa_supplicant_set_state(wpa_s, prev_state);
+ return ret;
+ }
+
+ /* If we have more SSIDs to scan, add a timeout so we scan them too */
+ if (ssid || !wpa_s->first_sched_scan) {
+ wpa_s->sched_scan_timed_out = 0;
+ eloop_register_timeout(wpa_s->sched_scan_timeout, 0,
+ wpa_supplicant_sched_scan_timeout,
+ wpa_s, NULL);
+ wpa_s->first_sched_scan = 0;
+ wpa_s->sched_scan_timeout /= 2;
+ wpa_s->sched_scan_interval *= 2;
+ }
+
+ return 0;
+}
+
+
+/**
* wpa_supplicant_cancel_scan - Cancel a scheduled scan request
* @wpa_s: Pointer to wpa_supplicant data
*
@@ -467,11 +1111,38 @@ void wpa_supplicant_req_scan(struct wpa_supplicant *wpa_s, int sec, int usec)
*/
void wpa_supplicant_cancel_scan(struct wpa_supplicant *wpa_s)
{
- wpa_msg(wpa_s, MSG_DEBUG, "Cancelling scan request");
+ wpa_dbg(wpa_s, MSG_DEBUG, "Cancelling scan request");
eloop_cancel_timeout(wpa_supplicant_scan, wpa_s, NULL);
}
+/**
+ * wpa_supplicant_cancel_sched_scan - Stop running scheduled scans
+ * @wpa_s: Pointer to wpa_supplicant data
+ *
+ * This function is used to stop a periodic scheduled scan.
+ */
+void wpa_supplicant_cancel_sched_scan(struct wpa_supplicant *wpa_s)
+{
+ if (!wpa_s->sched_scanning)
+ return;
+
+ wpa_dbg(wpa_s, MSG_DEBUG, "Cancelling sched scan");
+ eloop_cancel_timeout(wpa_supplicant_sched_scan_timeout, wpa_s, NULL);
+ wpa_supplicant_stop_sched_scan(wpa_s);
+}
+
+
+/**
+ * wpa_supplicant_notify_scanning - Indicate possible scan state change
+ * @wpa_s: Pointer to wpa_supplicant data
+ * @scanning: Whether scanning is currently in progress
+ *
+ * This function is to generate scanning notifycations. It is called whenever
+ * there may have been a change in scanning (scan started, completed, stopped).
+ * wpas_notify_scanning() is called whenever the scanning state changed from the
+ * previously notified state.
+ */
void wpa_supplicant_notify_scanning(struct wpa_supplicant *wpa_s,
int scanning)
{
@@ -504,6 +1175,15 @@ static int wpa_scan_get_max_rate(const struct wpa_scan_res *res)
}
+/**
+ * wpa_scan_get_ie - Fetch a specified information element from a scan result
+ * @res: Scan result entry
+ * @ie: Information element identitifier (WLAN_EID_*)
+ * Returns: Pointer to the information element (id field) or %NULL if not found
+ *
+ * This function returns the first matching information element in the scan
+ * result.
+ */
const u8 * wpa_scan_get_ie(const struct wpa_scan_res *res, u8 ie)
{
const u8 *end, *pos;
@@ -523,6 +1203,15 @@ const u8 * wpa_scan_get_ie(const struct wpa_scan_res *res, u8 ie)
}
+/**
+ * wpa_scan_get_vendor_ie - Fetch vendor information element from a scan result
+ * @res: Scan result entry
+ * @vendor_type: Vendor type (four octets starting the IE payload)
+ * Returns: Pointer to the information element (id field) or %NULL if not found
+ *
+ * This function returns the first matching information element in the scan
+ * result.
+ */
const u8 * wpa_scan_get_vendor_ie(const struct wpa_scan_res *res,
u32 vendor_type)
{
@@ -544,6 +1233,16 @@ const u8 * wpa_scan_get_vendor_ie(const struct wpa_scan_res *res,
}
+/**
+ * wpa_scan_get_vendor_ie_multi - Fetch vendor IE data from a scan result
+ * @res: Scan result entry
+ * @vendor_type: Vendor type (four octets starting the IE payload)
+ * Returns: Pointer to the information element payload or %NULL if not found
+ *
+ * This function returns concatenated payload of possibly fragmented vendor
+ * specific information elements in the scan result. The caller is responsible
+ * for freeing the returned buffer.
+ */
struct wpabuf * wpa_scan_get_vendor_ie_multi(const struct wpa_scan_res *res,
u32 vendor_type)
{
@@ -575,15 +1274,28 @@ struct wpabuf * wpa_scan_get_vendor_ie_multi(const struct wpa_scan_res *res,
}
+/*
+ * Channels with a great SNR can operate at full rate. What is a great SNR?
+ * This doc https://supportforums.cisco.com/docs/DOC-12954 says, "the general
+ * rule of thumb is that any SNR above 20 is good." This one
+ * http://www.cisco.com/en/US/tech/tk722/tk809/technologies_q_and_a_item09186a00805e9a96.shtml#qa23
+ * recommends 25 as a minimum SNR for 54 Mbps data rate. 30 is chosen here as a
+ * conservative value.
+ */
+#define GREAT_SNR 30
+
/* Compare function for sorting scan results. Return >0 if @b is considered
* better. */
static int wpa_scan_result_compar(const void *a, const void *b)
{
+#define IS_5GHZ(n) (n > 4000)
+#define MIN(a,b) a < b ? a : b
struct wpa_scan_res **_wa = (void *) a;
struct wpa_scan_res **_wb = (void *) b;
struct wpa_scan_res *wa = *_wa;
struct wpa_scan_res *wb = *_wb;
int wpa_a, wpa_b, maxrate_a, maxrate_b;
+ int snr_a, snr_b;
/* WPA/WPA2 support preferred */
wpa_a = wpa_scan_get_vendor_ie(wa, WPA_IE_VENDOR_TYPE) != NULL ||
@@ -604,17 +1316,78 @@ static int wpa_scan_result_compar(const void *a, const void *b)
(wb->caps & IEEE80211_CAP_PRIVACY) == 0)
return -1;
- /* best/max rate preferred if signal level close enough XXX */
- if ((wa->level && wb->level && abs(wb->level - wa->level) < 5) ||
+ if ((wa->flags & wb->flags & WPA_SCAN_LEVEL_DBM) &&
+ !((wa->flags | wb->flags) & WPA_SCAN_NOISE_INVALID)) {
+ snr_a = MIN(wa->level - wa->noise, GREAT_SNR);
+ snr_b = MIN(wb->level - wb->noise, GREAT_SNR);
+ } else {
+ /* Not suitable information to calculate SNR, so use level */
+ snr_a = wa->level;
+ snr_b = wb->level;
+ }
+
+ /* best/max rate preferred if SNR close enough */
+ if ((snr_a && snr_b && abs(snr_b - snr_a) < 5) ||
(wa->qual && wb->qual && abs(wb->qual - wa->qual) < 10)) {
maxrate_a = wpa_scan_get_max_rate(wa);
maxrate_b = wpa_scan_get_max_rate(wb);
if (maxrate_a != maxrate_b)
return maxrate_b - maxrate_a;
+ if (IS_5GHZ(wa->freq) ^ IS_5GHZ(wb->freq))
+ return IS_5GHZ(wa->freq) ? -1 : 1;
}
/* use freq for channel preference */
+ /* all things being equal, use SNR; if SNRs are
+ * identical, use quality values since some drivers may only report
+ * that value and leave the signal level zero */
+ if (snr_b == snr_a)
+ return wb->qual - wa->qual;
+ return snr_b - snr_a;
+#undef MIN
+#undef IS_5GHZ
+}
+
+
+#ifdef CONFIG_WPS
+/* Compare function for sorting scan results when searching a WPS AP for
+ * provisioning. Return >0 if @b is considered better. */
+static int wpa_scan_result_wps_compar(const void *a, const void *b)
+{
+ struct wpa_scan_res **_wa = (void *) a;
+ struct wpa_scan_res **_wb = (void *) b;
+ struct wpa_scan_res *wa = *_wa;
+ struct wpa_scan_res *wb = *_wb;
+ int uses_wps_a, uses_wps_b;
+ struct wpabuf *wps_a, *wps_b;
+ int res;
+
+ /* Optimization - check WPS IE existence before allocated memory and
+ * doing full reassembly. */
+ uses_wps_a = wpa_scan_get_vendor_ie(wa, WPS_IE_VENDOR_TYPE) != NULL;
+ uses_wps_b = wpa_scan_get_vendor_ie(wb, WPS_IE_VENDOR_TYPE) != NULL;
+ if (uses_wps_a && !uses_wps_b)
+ return -1;
+ if (!uses_wps_a && uses_wps_b)
+ return 1;
+
+ if (uses_wps_a && uses_wps_b) {
+ wps_a = wpa_scan_get_vendor_ie_multi(wa, WPS_IE_VENDOR_TYPE);
+ wps_b = wpa_scan_get_vendor_ie_multi(wb, WPS_IE_VENDOR_TYPE);
+ res = wps_ap_priority_compar(wps_a, wps_b);
+ wpabuf_free(wps_a);
+ wpabuf_free(wps_b);
+ if (res)
+ return res;
+ }
+
+ /*
+ * Do not use current AP security policy as a sorting criteria during
+ * WPS provisioning step since the AP may get reconfigured at the
+ * completion of provisioning.
+ */
+
/* all things being equal, use signal level; if signal levels are
* identical, use quality values since some drivers may only report
* that value and leave the signal level zero */
@@ -622,6 +1395,99 @@ static int wpa_scan_result_compar(const void *a, const void *b)
return wb->qual - wa->qual;
return wb->level - wa->level;
}
+#endif /* CONFIG_WPS */
+
+
+static void dump_scan_res(struct wpa_scan_results *scan_res)
+{
+#ifndef CONFIG_NO_STDOUT_DEBUG
+ size_t i;
+
+ if (scan_res->res == NULL || scan_res->num == 0)
+ return;
+
+ wpa_printf(MSG_EXCESSIVE, "Sorted scan results");
+
+ for (i = 0; i < scan_res->num; i++) {
+ struct wpa_scan_res *r = scan_res->res[i];
+ u8 *pos;
+ if ((r->flags & (WPA_SCAN_LEVEL_DBM | WPA_SCAN_NOISE_INVALID))
+ == WPA_SCAN_LEVEL_DBM) {
+ int snr = r->level - r->noise;
+ wpa_printf(MSG_EXCESSIVE, MACSTR " freq=%d qual=%d "
+ "noise=%d level=%d snr=%d%s flags=0x%x",
+ MAC2STR(r->bssid), r->freq, r->qual,
+ r->noise, r->level, snr,
+ snr >= GREAT_SNR ? "*" : "", r->flags);
+ } else {
+ wpa_printf(MSG_EXCESSIVE, MACSTR " freq=%d qual=%d "
+ "noise=%d level=%d flags=0x%x",
+ MAC2STR(r->bssid), r->freq, r->qual,
+ r->noise, r->level, r->flags);
+ }
+ pos = (u8 *) (r + 1);
+ if (r->ie_len)
+ wpa_hexdump(MSG_EXCESSIVE, "IEs", pos, r->ie_len);
+ pos += r->ie_len;
+ if (r->beacon_ie_len)
+ wpa_hexdump(MSG_EXCESSIVE, "Beacon IEs",
+ pos, r->beacon_ie_len);
+ }
+#endif /* CONFIG_NO_STDOUT_DEBUG */
+}
+
+
+/**
+ * wpa_supplicant_filter_bssid_match - Is the specified BSSID allowed
+ * @wpa_s: Pointer to wpa_supplicant data
+ * @bssid: BSSID to check
+ * Returns: 0 if the BSSID is filtered or 1 if not
+ *
+ * This function is used to filter out specific BSSIDs from scan reslts mainly
+ * for testing purposes (SET bssid_filter ctrl_iface command).
+ */
+int wpa_supplicant_filter_bssid_match(struct wpa_supplicant *wpa_s,
+ const u8 *bssid)
+{
+ size_t i;
+
+ if (wpa_s->bssid_filter == NULL)
+ return 1;
+
+ for (i = 0; i < wpa_s->bssid_filter_count; i++) {
+ if (os_memcmp(wpa_s->bssid_filter + i * ETH_ALEN, bssid,
+ ETH_ALEN) == 0)
+ return 1;
+ }
+
+ return 0;
+}
+
+
+static void filter_scan_res(struct wpa_supplicant *wpa_s,
+ struct wpa_scan_results *res)
+{
+ size_t i, j;
+
+ if (wpa_s->bssid_filter == NULL)
+ return;
+
+ for (i = 0, j = 0; i < res->num; i++) {
+ if (wpa_supplicant_filter_bssid_match(wpa_s,
+ res->res[i]->bssid)) {
+ res->res[j++] = res->res[i];
+ } else {
+ os_free(res->res[i]);
+ res->res[i] = NULL;
+ }
+ }
+
+ if (res->num != j) {
+ wpa_printf(MSG_DEBUG, "Filtered out %d scan results",
+ (int) (res->num - j));
+ res->num = j;
+ }
+}
/**
@@ -641,18 +1507,26 @@ wpa_supplicant_get_scan_results(struct wpa_supplicant *wpa_s,
{
struct wpa_scan_results *scan_res;
size_t i;
+ int (*compar)(const void *, const void *) = wpa_scan_result_compar;
- if (wpa_s->drv_flags & WPA_DRIVER_FLAGS_USER_SPACE_MLME)
- scan_res = ieee80211_sta_get_scan_results(wpa_s);
- else
- scan_res = wpa_drv_get_scan_results2(wpa_s);
+ scan_res = wpa_drv_get_scan_results2(wpa_s);
if (scan_res == NULL) {
- wpa_printf(MSG_DEBUG, "Failed to get scan results");
+ wpa_dbg(wpa_s, MSG_DEBUG, "Failed to get scan results");
return NULL;
}
+ filter_scan_res(wpa_s, scan_res);
+
+#ifdef CONFIG_WPS
+ if (wpas_wps_in_progress(wpa_s)) {
+ wpa_dbg(wpa_s, MSG_DEBUG, "WPS: Order scan results with WPS "
+ "provisioning rules");
+ compar = wpa_scan_result_wps_compar;
+ }
+#endif /* CONFIG_WPS */
qsort(scan_res->res, scan_res->num, sizeof(struct wpa_scan_res *),
- wpa_scan_result_compar);
+ compar);
+ dump_scan_res(scan_res);
wpa_bss_update_start(wpa_s);
for (i = 0; i < scan_res->num; i++)
@@ -663,6 +1537,18 @@ wpa_supplicant_get_scan_results(struct wpa_supplicant *wpa_s,
}
+/**
+ * wpa_supplicant_update_scan_results - Update scan results from the driver
+ * @wpa_s: Pointer to wpa_supplicant data
+ * Returns: 0 on success, -1 on failure
+ *
+ * This function updates the BSS table within wpa_supplicant based on the
+ * currently available scan results from the driver without requesting a new
+ * scan. This is used in cases where the driver indicates an association
+ * (including roaming within ESS) and wpa_supplicant does not yet have the
+ * needed information to complete the connection (e.g., to perform validation
+ * steps in 4-way handshake).
+ */
int wpa_supplicant_update_scan_results(struct wpa_supplicant *wpa_s)
{
struct wpa_scan_results *scan_res;
@@ -673,17 +1559,3 @@ int wpa_supplicant_update_scan_results(struct wpa_supplicant *wpa_s)
return 0;
}
-
-
-void wpa_scan_results_free(struct wpa_scan_results *res)
-{
- size_t i;
-
- if (res == NULL)
- return;
-
- for (i = 0; i < res->num; i++)
- os_free(res->res[i]);
- os_free(res->res);
- os_free(res);
-}
diff --git a/contrib/wpa/wpa_supplicant/scan.h b/contrib/wpa/wpa_supplicant/scan.h
index 441fdbb..5096287 100644
--- a/contrib/wpa/wpa_supplicant/scan.h
+++ b/contrib/wpa/wpa_supplicant/scan.h
@@ -2,22 +2,20 @@
* WPA Supplicant - Scanning
* Copyright (c) 2003-2010, Jouni Malinen <j@w1.fi>
*
- * This program is free software; you can redistribute it and/or modify
- * it under the terms of the GNU General Public License version 2 as
- * published by the Free Software Foundation.
- *
- * Alternatively, this software may be distributed under the terms of BSD
- * license.
- *
- * See README and COPYING for more details.
+ * This software may be distributed under the terms of the BSD license.
+ * See README for more details.
*/
#ifndef SCAN_H
#define SCAN_H
-int wpa_supplicant_enabled_networks(struct wpa_config *conf);
+int wpa_supplicant_enabled_networks(struct wpa_supplicant *wpa_s);
void wpa_supplicant_req_scan(struct wpa_supplicant *wpa_s, int sec, int usec);
+int wpa_supplicant_delayed_sched_scan(struct wpa_supplicant *wpa_s,
+ int sec, int usec);
+int wpa_supplicant_req_sched_scan(struct wpa_supplicant *wpa_s);
void wpa_supplicant_cancel_scan(struct wpa_supplicant *wpa_s);
+void wpa_supplicant_cancel_sched_scan(struct wpa_supplicant *wpa_s);
void wpa_supplicant_notify_scanning(struct wpa_supplicant *wpa_s,
int scanning);
struct wpa_driver_scan_params;
@@ -32,6 +30,7 @@ const u8 * wpa_scan_get_vendor_ie(const struct wpa_scan_res *res,
u32 vendor_type);
struct wpabuf * wpa_scan_get_vendor_ie_multi(const struct wpa_scan_res *res,
u32 vendor_type);
-void wpa_scan_results_free(struct wpa_scan_results *res);
+int wpa_supplicant_filter_bssid_match(struct wpa_supplicant *wpa_s,
+ const u8 *bssid);
#endif /* SCAN_H */
diff --git a/contrib/wpa/wpa_supplicant/sme.c b/contrib/wpa/wpa_supplicant/sme.c
index 5604e97..77ad1d2 100644
--- a/contrib/wpa/wpa_supplicant/sme.c
+++ b/contrib/wpa/wpa_supplicant/sme.c
@@ -2,19 +2,14 @@
* wpa_supplicant - SME
* Copyright (c) 2009-2010, Jouni Malinen <j@w1.fi>
*
- * This program is free software; you can redistribute it and/or modify
- * it under the terms of the GNU General Public License version 2 as
- * published by the Free Software Foundation.
- *
- * Alternatively, this software may be distributed under the terms of BSD
- * license.
- *
- * See README and COPYING for more details.
+ * This software may be distributed under the terms of the BSD license.
+ * See README for more details.
*/
#include "includes.h"
#include "common.h"
+#include "utils/eloop.h"
#include "common/ieee802_11_defs.h"
#include "common/ieee802_11_common.h"
#include "eapol_supp/eapol_supp_sm.h"
@@ -26,14 +21,68 @@
#include "driver_i.h"
#include "wpas_glue.h"
#include "wps_supplicant.h"
+#include "p2p_supplicant.h"
#include "notify.h"
-#include "blacklist.h"
#include "bss.h"
#include "scan.h"
#include "sme.h"
+#include "hs20_supplicant.h"
-void sme_authenticate(struct wpa_supplicant *wpa_s,
- struct wpa_bss *bss, struct wpa_ssid *ssid)
+#define SME_AUTH_TIMEOUT 5
+#define SME_ASSOC_TIMEOUT 5
+
+static void sme_auth_timer(void *eloop_ctx, void *timeout_ctx);
+static void sme_assoc_timer(void *eloop_ctx, void *timeout_ctx);
+static void sme_obss_scan_timeout(void *eloop_ctx, void *timeout_ctx);
+#ifdef CONFIG_IEEE80211W
+static void sme_stop_sa_query(struct wpa_supplicant *wpa_s);
+#endif /* CONFIG_IEEE80211W */
+
+
+#ifdef CONFIG_SAE
+
+static struct wpabuf * sme_auth_build_sae_commit(struct wpa_supplicant *wpa_s)
+{
+ struct wpabuf *buf;
+
+ buf = wpabuf_alloc(4 + 2);
+ if (buf == NULL)
+ return NULL;
+
+ wpabuf_put_le16(buf, 1); /* Transaction seq# */
+ wpabuf_put_le16(buf, WLAN_STATUS_SUCCESS);
+ wpabuf_put_le16(buf, 19); /* Finite Cyclic Group */
+ /* TODO: Anti-Clogging Token (if requested) */
+ /* TODO: Scalar */
+ /* TODO: Element */
+
+ return buf;
+}
+
+
+static struct wpabuf * sme_auth_build_sae_confirm(struct wpa_supplicant *wpa_s)
+{
+ struct wpabuf *buf;
+
+ buf = wpabuf_alloc(4 + 2);
+ if (buf == NULL)
+ return NULL;
+
+ wpabuf_put_le16(buf, 2); /* Transaction seq# */
+ wpabuf_put_le16(buf, WLAN_STATUS_SUCCESS);
+ wpabuf_put_le16(buf, wpa_s->sme.sae_send_confirm);
+ wpa_s->sme.sae_send_confirm++;
+ /* TODO: Confirm */
+
+ return buf;
+}
+
+#endif /* CONFIG_SAE */
+
+
+static void sme_send_authentication(struct wpa_supplicant *wpa_s,
+ struct wpa_bss *bss, struct wpa_ssid *ssid,
+ int start)
{
struct wpa_driver_auth_params params;
struct wpa_ssid *old_ssid;
@@ -44,10 +93,13 @@ void sme_authenticate(struct wpa_supplicant *wpa_s,
const u8 *md = NULL;
#endif /* CONFIG_IEEE80211R */
int i, bssid_changed;
+ struct wpabuf *resp = NULL;
+ u8 ext_capab[10];
+ int ext_capab_len;
if (bss == NULL) {
- wpa_printf(MSG_ERROR, "SME: No scan result available for the "
- "network");
+ wpa_msg(wpa_s, MSG_ERROR, "SME: No scan result available for "
+ "the network");
return;
}
@@ -60,6 +112,7 @@ void sme_authenticate(struct wpa_supplicant *wpa_s,
params.bssid = bss->bssid;
params.ssid = bss->ssid;
params.ssid_len = bss->ssid_len;
+ params.p2p = ssid->p2p_group;
if (wpa_s->sme.ssid_len != params.ssid_len ||
os_memcmp(wpa_s->sme.ssid, params.ssid, params.ssid_len) != 0)
@@ -80,13 +133,28 @@ void sme_authenticate(struct wpa_supplicant *wpa_s,
}
}
#endif /* IEEE8021X_EAPOL */
- wpa_printf(MSG_DEBUG, "Automatic auth_alg selection: 0x%x",
- params.auth_alg);
+ wpa_dbg(wpa_s, MSG_DEBUG, "Automatic auth_alg selection: 0x%x",
+ params.auth_alg);
if (ssid->auth_alg) {
params.auth_alg = ssid->auth_alg;
- wpa_printf(MSG_DEBUG, "Overriding auth_alg selection: 0x%x",
- params.auth_alg);
+ wpa_dbg(wpa_s, MSG_DEBUG, "Overriding auth_alg selection: "
+ "0x%x", params.auth_alg);
}
+#ifdef CONFIG_SAE
+ if (wpa_key_mgmt_sae(ssid->key_mgmt)) {
+ const u8 *rsn;
+ struct wpa_ie_data ied;
+
+ rsn = wpa_bss_get_ie(bss, WLAN_EID_RSN);
+ if (rsn &&
+ wpa_parse_wpa_ie(rsn, 2 + rsn[1], &ied) == 0) {
+ if (wpa_key_mgmt_sae(ied.key_mgmt)) {
+ wpa_dbg(wpa_s, MSG_DEBUG, "Using SAE auth_alg");
+ params.auth_alg = WPA_AUTH_ALG_SAE;
+ }
+ }
+ }
+#endif /* CONFIG_SAE */
for (i = 0; i < NUM_WEP_KEYS; i++) {
if (ssid->wep_key_len[i])
@@ -103,13 +171,11 @@ void sme_authenticate(struct wpa_supplicant *wpa_s,
if ((wpa_bss_get_vendor_ie(bss, WPA_IE_VENDOR_TYPE) ||
wpa_bss_get_ie(bss, WLAN_EID_RSN)) &&
- (ssid->key_mgmt & (WPA_KEY_MGMT_IEEE8021X | WPA_KEY_MGMT_PSK |
- WPA_KEY_MGMT_FT_IEEE8021X |
- WPA_KEY_MGMT_FT_PSK |
- WPA_KEY_MGMT_IEEE8021X_SHA256 |
- WPA_KEY_MGMT_PSK_SHA256))) {
+ wpa_key_mgmt_wpa(ssid->key_mgmt)) {
int try_opportunistic;
- try_opportunistic = ssid->proactive_key_caching &&
+ try_opportunistic = (ssid->proactive_key_caching < 0 ?
+ wpa_s->conf->okc :
+ ssid->proactive_key_caching) &&
(ssid->proto & WPA_PROTO_RSN);
if (pmksa_cache_set_current(wpa_s->wpa, NULL, bss->bssid,
wpa_s->current_ssid,
@@ -119,22 +185,27 @@ void sme_authenticate(struct wpa_supplicant *wpa_s,
if (wpa_supplicant_set_suites(wpa_s, bss, ssid,
wpa_s->sme.assoc_req_ie,
&wpa_s->sme.assoc_req_ie_len)) {
- wpa_printf(MSG_WARNING, "SME: Failed to set WPA key "
- "management and encryption suites");
+ wpa_msg(wpa_s, MSG_WARNING, "SME: Failed to set WPA "
+ "key management and encryption suites");
return;
}
- } else if (ssid->key_mgmt &
- (WPA_KEY_MGMT_PSK | WPA_KEY_MGMT_IEEE8021X |
- WPA_KEY_MGMT_WPA_NONE | WPA_KEY_MGMT_FT_PSK |
- WPA_KEY_MGMT_FT_IEEE8021X | WPA_KEY_MGMT_PSK_SHA256 |
- WPA_KEY_MGMT_IEEE8021X_SHA256)) {
+ } else if ((ssid->key_mgmt & WPA_KEY_MGMT_IEEE8021X_NO_WPA) &&
+ wpa_key_mgmt_wpa_ieee8021x(ssid->key_mgmt)) {
+ /*
+ * Both WPA and non-WPA IEEE 802.1X enabled in configuration -
+ * use non-WPA since the scan results did not indicate that the
+ * AP is using WPA or WPA2.
+ */
+ wpa_supplicant_set_non_wpa_policy(wpa_s, ssid);
+ wpa_s->sme.assoc_req_ie_len = 0;
+ } else if (wpa_key_mgmt_wpa_any(ssid->key_mgmt)) {
wpa_s->sme.assoc_req_ie_len = sizeof(wpa_s->sme.assoc_req_ie);
if (wpa_supplicant_set_suites(wpa_s, NULL, ssid,
wpa_s->sme.assoc_req_ie,
&wpa_s->sme.assoc_req_ie_len)) {
- wpa_printf(MSG_WARNING, "SME: Failed to set WPA key "
- "management and encryption suites (no scan "
- "results)");
+ wpa_msg(wpa_s, MSG_WARNING, "SME: Failed to set WPA "
+ "key management and encryption suites (no "
+ "scan results)");
return;
}
#ifdef CONFIG_WPS
@@ -166,8 +237,7 @@ void sme_authenticate(struct wpa_supplicant *wpa_s,
wpa_ft_prepare_auth_request(wpa_s->wpa, ie);
}
- if (md && ssid->key_mgmt & (WPA_KEY_MGMT_FT_PSK |
- WPA_KEY_MGMT_FT_IEEE8021X)) {
+ if (md && wpa_key_mgmt_ft(ssid->key_mgmt)) {
if (wpa_s->sme.assoc_req_ie_len + 5 <
sizeof(wpa_s->sme.assoc_req_ie)) {
struct rsn_mdie *mdie;
@@ -185,8 +255,8 @@ void sme_authenticate(struct wpa_supplicant *wpa_s,
if (wpa_s->sme.ft_used &&
os_memcmp(md, wpa_s->sme.mobility_domain, 2) == 0 &&
wpa_sm_has_ptk(wpa_s->wpa)) {
- wpa_printf(MSG_DEBUG, "SME: Trying to use FT "
- "over-the-air");
+ wpa_dbg(wpa_s, MSG_DEBUG, "SME: Trying to use FT "
+ "over-the-air");
params.auth_alg = WPA_AUTH_ALG_FT;
params.ie = wpa_s->sme.ft_ies;
params.ie_len = wpa_s->sme.ft_ies_len;
@@ -195,23 +265,81 @@ void sme_authenticate(struct wpa_supplicant *wpa_s,
#endif /* CONFIG_IEEE80211R */
#ifdef CONFIG_IEEE80211W
- wpa_s->sme.mfp = ssid->ieee80211w;
- if (ssid->ieee80211w != NO_MGMT_FRAME_PROTECTION) {
+ wpa_s->sme.mfp = ssid->ieee80211w == MGMT_FRAME_PROTECTION_DEFAULT ?
+ wpa_s->conf->pmf : ssid->ieee80211w;
+ if (wpa_s->sme.mfp != NO_MGMT_FRAME_PROTECTION) {
const u8 *rsn = wpa_bss_get_ie(bss, WLAN_EID_RSN);
struct wpa_ie_data _ie;
if (rsn && wpa_parse_wpa_ie(rsn, 2 + rsn[1], &_ie) == 0 &&
_ie.capabilities &
(WPA_CAPABILITY_MFPC | WPA_CAPABILITY_MFPR)) {
- wpa_printf(MSG_DEBUG, "WPA: Selected AP supports MFP: "
- "require MFP");
+ wpa_dbg(wpa_s, MSG_DEBUG, "SME: Selected AP supports "
+ "MFP: require MFP");
wpa_s->sme.mfp = MGMT_FRAME_PROTECTION_REQUIRED;
}
}
#endif /* CONFIG_IEEE80211W */
+#ifdef CONFIG_P2P
+ if (wpa_s->global->p2p) {
+ u8 *pos;
+ size_t len;
+ int res;
+ pos = wpa_s->sme.assoc_req_ie + wpa_s->sme.assoc_req_ie_len;
+ len = sizeof(wpa_s->sme.assoc_req_ie) -
+ wpa_s->sme.assoc_req_ie_len;
+ res = wpas_p2p_assoc_req_ie(wpa_s, bss, pos, len,
+ ssid->p2p_group);
+ if (res >= 0)
+ wpa_s->sme.assoc_req_ie_len += res;
+ }
+#endif /* CONFIG_P2P */
+
+#ifdef CONFIG_HS20
+ if (wpa_s->conf->hs20) {
+ struct wpabuf *hs20;
+ hs20 = wpabuf_alloc(20);
+ if (hs20) {
+ wpas_hs20_add_indication(hs20);
+ os_memcpy(wpa_s->sme.assoc_req_ie +
+ wpa_s->sme.assoc_req_ie_len,
+ wpabuf_head(hs20), wpabuf_len(hs20));
+ wpa_s->sme.assoc_req_ie_len += wpabuf_len(hs20);
+ wpabuf_free(hs20);
+ }
+ }
+#endif /* CONFIG_HS20 */
+
+ ext_capab_len = wpas_build_ext_capab(wpa_s, ext_capab);
+ if (ext_capab_len > 0) {
+ u8 *pos = wpa_s->sme.assoc_req_ie;
+ if (wpa_s->sme.assoc_req_ie_len > 0 && pos[0] == WLAN_EID_RSN)
+ pos += 2 + pos[1];
+ os_memmove(pos + ext_capab_len, pos,
+ wpa_s->sme.assoc_req_ie_len -
+ (pos - wpa_s->sme.assoc_req_ie));
+ wpa_s->sme.assoc_req_ie_len += ext_capab_len;
+ os_memcpy(pos, ext_capab, ext_capab_len);
+ }
+
+#ifdef CONFIG_SAE
+ if (params.auth_alg == WPA_AUTH_ALG_SAE) {
+ if (start)
+ resp = sme_auth_build_sae_commit(wpa_s);
+ else
+ resp = sme_auth_build_sae_confirm(wpa_s);
+ if (resp == NULL)
+ return;
+ params.sae_data = wpabuf_head(resp);
+ params.sae_data_len = wpabuf_len(resp);
+ wpa_s->sme.sae_state = start ? SME_SAE_COMMIT : SME_SAE_CONFIRM;
+ }
+#endif /* CONFIG_SAE */
+
+ wpa_supplicant_cancel_sched_scan(wpa_s);
wpa_supplicant_cancel_scan(wpa_s);
- wpa_msg(wpa_s, MSG_INFO, "Trying to authenticate with " MACSTR
+ wpa_msg(wpa_s, MSG_INFO, "SME: Trying to authenticate with " MACSTR
" (SSID='%s' freq=%d MHz)", MAC2STR(params.bssid),
wpa_ssid_txt(params.ssid, params.ssid_len), params.freq);
@@ -226,19 +354,104 @@ void sme_authenticate(struct wpa_supplicant *wpa_s,
wpa_s->sme.auth_alg = params.auth_alg;
if (wpa_drv_authenticate(wpa_s, &params) < 0) {
- wpa_msg(wpa_s, MSG_INFO, "Authentication request to the "
+ wpa_msg(wpa_s, MSG_INFO, "SME: Authentication request to the "
"driver failed");
- wpa_supplicant_req_scan(wpa_s, 1, 0);
+ wpas_connection_failed(wpa_s, bss->bssid);
+ wpa_supplicant_mark_disassoc(wpa_s);
+ wpabuf_free(resp);
return;
}
- /* TODO: add timeout on authentication */
+ eloop_register_timeout(SME_AUTH_TIMEOUT, 0, sme_auth_timer, wpa_s,
+ NULL);
/*
* Association will be started based on the authentication event from
* the driver.
*/
+
+ wpabuf_free(resp);
+}
+
+
+void sme_authenticate(struct wpa_supplicant *wpa_s,
+ struct wpa_bss *bss, struct wpa_ssid *ssid)
+{
+ wpa_s->sme.sae_state = SME_SAE_INIT;
+ wpa_s->sme.sae_send_confirm = 0;
+ sme_send_authentication(wpa_s, bss, ssid, 1);
+}
+
+
+#ifdef CONFIG_SAE
+
+static int sme_sae_process_commit(struct wpa_supplicant *wpa_s, const u8 *data,
+ size_t len)
+{
+ /* Check Finite Cyclic Group */
+ if (len < 2)
+ return -1;
+ if (WPA_GET_LE16(data) != 19) {
+ wpa_printf(MSG_DEBUG, "SAE: Unsupported Finite Cyclic Group %u",
+ WPA_GET_LE16(data));
+ return -1;
+ }
+
+ /* TODO */
+
+ return 0;
+}
+
+
+static int sme_sae_process_confirm(struct wpa_supplicant *wpa_s, const u8 *data,
+ size_t len)
+{
+ u16 rc;
+
+ if (len < 2)
+ return -1;
+ rc = WPA_GET_LE16(data);
+ wpa_printf(MSG_DEBUG, "SAE: peer-send-confirm %u", rc);
+
+ /* TODO */
+ return 0;
+}
+
+
+static int sme_sae_auth(struct wpa_supplicant *wpa_s, u16 auth_transaction,
+ u16 status_code, const u8 *data, size_t len)
+{
+ wpa_dbg(wpa_s, MSG_DEBUG, "SME: SAE authentication transaction %u "
+ "status code %u", auth_transaction, status_code);
+ wpa_hexdump(MSG_DEBUG, "SME: SAE fields", data, len);
+
+ if (status_code != WLAN_STATUS_SUCCESS)
+ return -1;
+
+ if (auth_transaction == 1) {
+ wpa_dbg(wpa_s, MSG_DEBUG, "SME SAE commit");
+ if (wpa_s->current_bss == NULL ||
+ wpa_s->current_ssid == NULL)
+ return -1;
+ if (wpa_s->sme.sae_state != SME_SAE_COMMIT)
+ return -1;
+ if (sme_sae_process_commit(wpa_s, data, len) < 0)
+ return -1;
+ sme_send_authentication(wpa_s, wpa_s->current_bss,
+ wpa_s->current_ssid, 0);
+ return 0;
+ } else if (auth_transaction == 2) {
+ wpa_dbg(wpa_s, MSG_DEBUG, "SME SAE confirm");
+ if (wpa_s->sme.sae_state != SME_SAE_CONFIRM)
+ return -1;
+ if (sme_sae_process_confirm(wpa_s, data, len) < 0)
+ return -1;
+ return 1;
+ }
+
+ return -1;
}
+#endif /* CONFIG_SAE */
void sme_event_auth(struct wpa_supplicant *wpa_s, union wpa_event_data *data)
@@ -246,46 +459,67 @@ void sme_event_auth(struct wpa_supplicant *wpa_s, union wpa_event_data *data)
struct wpa_ssid *ssid = wpa_s->current_ssid;
if (ssid == NULL) {
- wpa_printf(MSG_DEBUG, "SME: Ignore authentication event when "
- "network is not selected");
+ wpa_dbg(wpa_s, MSG_DEBUG, "SME: Ignore authentication event "
+ "when network is not selected");
return;
}
if (wpa_s->wpa_state != WPA_AUTHENTICATING) {
- wpa_printf(MSG_DEBUG, "SME: Ignore authentication event when "
- "not in authenticating state");
+ wpa_dbg(wpa_s, MSG_DEBUG, "SME: Ignore authentication event "
+ "when not in authenticating state");
return;
}
if (os_memcmp(wpa_s->pending_bssid, data->auth.peer, ETH_ALEN) != 0) {
- wpa_printf(MSG_DEBUG, "SME: Ignore authentication with "
- "unexpected peer " MACSTR,
- MAC2STR(data->auth.peer));
+ wpa_dbg(wpa_s, MSG_DEBUG, "SME: Ignore authentication with "
+ "unexpected peer " MACSTR,
+ MAC2STR(data->auth.peer));
return;
}
- wpa_printf(MSG_DEBUG, "SME: Authentication response: peer=" MACSTR
- " auth_type=%d status_code=%d",
- MAC2STR(data->auth.peer), data->auth.auth_type,
- data->auth.status_code);
+ wpa_dbg(wpa_s, MSG_DEBUG, "SME: Authentication response: peer=" MACSTR
+ " auth_type=%d auth_transaction=%d status_code=%d",
+ MAC2STR(data->auth.peer), data->auth.auth_type,
+ data->auth.auth_transaction, data->auth.status_code);
wpa_hexdump(MSG_MSGDUMP, "SME: Authentication response IEs",
data->auth.ies, data->auth.ies_len);
+ eloop_cancel_timeout(sme_auth_timer, wpa_s, NULL);
+
+#ifdef CONFIG_SAE
+ if (data->auth.auth_type == WLAN_AUTH_SAE) {
+ int res;
+ res = sme_sae_auth(wpa_s, data->auth.auth_transaction,
+ data->auth.status_code, data->auth.ies,
+ data->auth.ies_len);
+ if (res < 0) {
+ wpas_connection_failed(wpa_s, wpa_s->pending_bssid);
+ wpa_supplicant_set_state(wpa_s, WPA_DISCONNECTED);
+
+ }
+ if (res != 1)
+ return;
+ }
+#endif /* CONFIG_SAE */
+
if (data->auth.status_code != WLAN_STATUS_SUCCESS) {
- wpa_printf(MSG_DEBUG, "SME: Authentication failed (status "
- "code %d)", data->auth.status_code);
+ wpa_dbg(wpa_s, MSG_DEBUG, "SME: Authentication failed (status "
+ "code %d)", data->auth.status_code);
if (data->auth.status_code !=
WLAN_STATUS_NOT_SUPPORTED_AUTH_ALG ||
wpa_s->sme.auth_alg == data->auth.auth_type ||
- wpa_s->current_ssid->auth_alg == WPA_AUTH_ALG_LEAP)
+ wpa_s->current_ssid->auth_alg == WPA_AUTH_ALG_LEAP) {
+ wpas_connection_failed(wpa_s, wpa_s->pending_bssid);
+ wpa_supplicant_set_state(wpa_s, WPA_DISCONNECTED);
return;
+ }
switch (data->auth.auth_type) {
case WLAN_AUTH_OPEN:
wpa_s->current_ssid->auth_alg = WPA_AUTH_ALG_SHARED;
- wpa_printf(MSG_DEBUG, "SME: Trying SHARED auth");
+ wpa_dbg(wpa_s, MSG_DEBUG, "SME: Trying SHARED auth");
wpa_supplicant_associate(wpa_s, wpa_s->current_bss,
wpa_s->current_ssid);
return;
@@ -293,7 +527,7 @@ void sme_event_auth(struct wpa_supplicant *wpa_s, union wpa_event_data *data)
case WLAN_AUTH_SHARED_KEY:
wpa_s->current_ssid->auth_alg = WPA_AUTH_ALG_LEAP;
- wpa_printf(MSG_DEBUG, "SME: Trying LEAP auth");
+ wpa_dbg(wpa_s, MSG_DEBUG, "SME: Trying LEAP auth");
wpa_supplicant_associate(wpa_s, wpa_s->current_bss,
wpa_s->current_ssid);
return;
@@ -324,15 +558,30 @@ void sme_associate(struct wpa_supplicant *wpa_s, enum wpas_mode mode,
{
struct wpa_driver_associate_params params;
struct ieee802_11_elems elems;
+#ifdef CONFIG_HT_OVERRIDES
+ struct ieee80211_ht_capabilities htcaps;
+ struct ieee80211_ht_capabilities htcaps_mask;
+#endif /* CONFIG_HT_OVERRIDES */
os_memset(&params, 0, sizeof(params));
params.bssid = bssid;
params.ssid = wpa_s->sme.ssid;
params.ssid_len = wpa_s->sme.ssid_len;
params.freq = wpa_s->sme.freq;
+ params.bg_scan_period = wpa_s->current_ssid ?
+ wpa_s->current_ssid->bg_scan_period : -1;
params.wpa_ie = wpa_s->sme.assoc_req_ie_len ?
wpa_s->sme.assoc_req_ie : NULL;
params.wpa_ie_len = wpa_s->sme.assoc_req_ie_len;
+ params.pairwise_suite = cipher_suite2driver(wpa_s->pairwise_cipher);
+ params.group_suite = cipher_suite2driver(wpa_s->group_cipher);
+#ifdef CONFIG_HT_OVERRIDES
+ os_memset(&htcaps, 0, sizeof(htcaps));
+ os_memset(&htcaps_mask, 0, sizeof(htcaps_mask));
+ params.htcaps = (u8 *) &htcaps;
+ params.htcaps_mask = (u8 *) &htcaps_mask;
+ wpa_supplicant_apply_ht_overrides(wpa_s, wpa_s->current_ssid, &params);
+#endif /* CONFIG_HT_OVERRIDES */
#ifdef CONFIG_IEEE80211R
if (auth_type == WLAN_AUTH_FT && wpa_s->sme.ft_ies) {
params.wpa_ie = wpa_s->sme.ft_ies;
@@ -354,26 +603,38 @@ void sme_associate(struct wpa_supplicant *wpa_s, enum wpas_mode mode,
if (params.wpa_ie == NULL ||
ieee802_11_parse_elems(params.wpa_ie, params.wpa_ie_len, &elems, 0)
< 0) {
- wpa_printf(MSG_DEBUG, "SME: Could not parse own IEs?!");
+ wpa_dbg(wpa_s, MSG_DEBUG, "SME: Could not parse own IEs?!");
os_memset(&elems, 0, sizeof(elems));
}
- if (elems.rsn_ie)
+ if (elems.rsn_ie) {
+ params.wpa_proto = WPA_PROTO_RSN;
wpa_sm_set_assoc_wpa_ie(wpa_s->wpa, elems.rsn_ie - 2,
elems.rsn_ie_len + 2);
- else if (elems.wpa_ie)
+ } else if (elems.wpa_ie) {
+ params.wpa_proto = WPA_PROTO_WPA;
wpa_sm_set_assoc_wpa_ie(wpa_s->wpa, elems.wpa_ie - 2,
elems.wpa_ie_len + 2);
- else
+ } else
wpa_sm_set_assoc_wpa_ie(wpa_s->wpa, NULL, 0);
+ if (wpa_s->current_ssid && wpa_s->current_ssid->p2p_group)
+ params.p2p = 1;
+
+ if (wpa_s->parent->set_sta_uapsd)
+ params.uapsd = wpa_s->parent->sta_uapsd;
+ else
+ params.uapsd = -1;
if (wpa_drv_associate(wpa_s, &params) < 0) {
- wpa_msg(wpa_s, MSG_INFO, "Association request to the driver "
- "failed");
- wpa_supplicant_req_scan(wpa_s, 5, 0);
+ wpa_msg(wpa_s, MSG_INFO, "SME: Association request to the "
+ "driver failed");
+ wpas_connection_failed(wpa_s, wpa_s->pending_bssid);
+ wpa_supplicant_set_state(wpa_s, WPA_DISCONNECTED);
+ os_memset(wpa_s->pending_bssid, 0, ETH_ALEN);
return;
}
- /* TODO: add timeout on association */
+ eloop_register_timeout(SME_ASSOC_TIMEOUT, 0, sme_assoc_timer, wpa_s,
+ NULL);
}
@@ -381,7 +642,7 @@ int sme_update_ft_ies(struct wpa_supplicant *wpa_s, const u8 *md,
const u8 *ies, size_t ies_len)
{
if (md == NULL || ies == NULL) {
- wpa_printf(MSG_DEBUG, "SME: Remove mobility domain");
+ wpa_dbg(wpa_s, MSG_DEBUG, "SME: Remove mobility domain");
os_free(wpa_s->sme.ft_ies);
wpa_s->sme.ft_ies = NULL;
wpa_s->sme.ft_ies_len = 0;
@@ -401,90 +662,580 @@ int sme_update_ft_ies(struct wpa_supplicant *wpa_s, const u8 *md,
}
-void sme_event_assoc_reject(struct wpa_supplicant *wpa_s,
- union wpa_event_data *data)
+static void sme_deauth(struct wpa_supplicant *wpa_s)
{
int bssid_changed;
- int timeout = 5000;
-
- wpa_printf(MSG_DEBUG, "SME: Association with " MACSTR " failed: "
- "status code %d", MAC2STR(wpa_s->pending_bssid),
- data->assoc_reject.status_code);
bssid_changed = !is_zero_ether_addr(wpa_s->bssid);
- /*
- * For now, unconditionally terminate the previous authentication. In
- * theory, this should not be needed, but mac80211 gets quite confused
- * if the authentication is left pending.. Some roaming cases might
- * benefit from using the previous authentication, so this could be
- * optimized in the future.
- */
if (wpa_drv_deauthenticate(wpa_s, wpa_s->pending_bssid,
WLAN_REASON_DEAUTH_LEAVING) < 0) {
- wpa_msg(wpa_s, MSG_INFO,
- "Deauth request to the driver failed");
+ wpa_msg(wpa_s, MSG_INFO, "SME: Deauth request to the driver "
+ "failed");
}
wpa_s->sme.prev_bssid_set = 0;
- if (wpa_blacklist_add(wpa_s, wpa_s->pending_bssid) == 0) {
- struct wpa_blacklist *b;
- b = wpa_blacklist_get(wpa_s, wpa_s->pending_bssid);
- if (b && b->count < 3) {
- /*
- * Speed up next attempt if there could be other APs
- * that could accept association.
- */
- timeout = 100;
- }
- }
+ wpas_connection_failed(wpa_s, wpa_s->pending_bssid);
wpa_supplicant_set_state(wpa_s, WPA_DISCONNECTED);
os_memset(wpa_s->bssid, 0, ETH_ALEN);
os_memset(wpa_s->pending_bssid, 0, ETH_ALEN);
if (bssid_changed)
wpas_notify_bssid_changed(wpa_s);
+}
+
+
+void sme_event_assoc_reject(struct wpa_supplicant *wpa_s,
+ union wpa_event_data *data)
+{
+ wpa_dbg(wpa_s, MSG_DEBUG, "SME: Association with " MACSTR " failed: "
+ "status code %d", MAC2STR(wpa_s->pending_bssid),
+ data->assoc_reject.status_code);
+
+ eloop_cancel_timeout(sme_assoc_timer, wpa_s, NULL);
/*
- * TODO: if more than one possible AP is available in scan results,
- * could try the other ones before requesting a new scan.
+ * For now, unconditionally terminate the previous authentication. In
+ * theory, this should not be needed, but mac80211 gets quite confused
+ * if the authentication is left pending.. Some roaming cases might
+ * benefit from using the previous authentication, so this could be
+ * optimized in the future.
*/
- wpa_supplicant_req_scan(wpa_s, timeout / 1000,
- 1000 * (timeout % 1000));
+ sme_deauth(wpa_s);
}
void sme_event_auth_timed_out(struct wpa_supplicant *wpa_s,
union wpa_event_data *data)
{
- wpa_printf(MSG_DEBUG, "SME: Authentication timed out");
- wpa_supplicant_req_scan(wpa_s, 5, 0);
+ wpa_dbg(wpa_s, MSG_DEBUG, "SME: Authentication timed out");
+ wpas_connection_failed(wpa_s, wpa_s->pending_bssid);
+ wpa_supplicant_mark_disassoc(wpa_s);
}
void sme_event_assoc_timed_out(struct wpa_supplicant *wpa_s,
union wpa_event_data *data)
{
- wpa_printf(MSG_DEBUG, "SME: Association timed out");
+ wpa_dbg(wpa_s, MSG_DEBUG, "SME: Association timed out");
+ wpas_connection_failed(wpa_s, wpa_s->pending_bssid);
wpa_supplicant_mark_disassoc(wpa_s);
- wpa_supplicant_req_scan(wpa_s, 5, 0);
}
void sme_event_disassoc(struct wpa_supplicant *wpa_s,
union wpa_event_data *data)
{
- wpa_printf(MSG_DEBUG, "SME: Disassociation event received");
- if (wpa_s->sme.prev_bssid_set &&
- !(wpa_s->drv_flags & WPA_DRIVER_FLAGS_USER_SPACE_MLME)) {
+ wpa_dbg(wpa_s, MSG_DEBUG, "SME: Disassociation event received");
+ if (wpa_s->sme.prev_bssid_set) {
/*
* cfg80211/mac80211 can get into somewhat confused state if
* the AP only disassociates us and leaves us in authenticated
* state. For now, force the state to be cleared to avoid
* confusing errors if we try to associate with the AP again.
*/
- wpa_printf(MSG_DEBUG, "SME: Deauthenticate to clear driver "
- "state");
+ wpa_dbg(wpa_s, MSG_DEBUG, "SME: Deauthenticate to clear "
+ "driver state");
wpa_drv_deauthenticate(wpa_s, wpa_s->sme.prev_bssid,
WLAN_REASON_DEAUTH_LEAVING);
}
}
+
+
+static void sme_auth_timer(void *eloop_ctx, void *timeout_ctx)
+{
+ struct wpa_supplicant *wpa_s = eloop_ctx;
+ if (wpa_s->wpa_state == WPA_AUTHENTICATING) {
+ wpa_msg(wpa_s, MSG_DEBUG, "SME: Authentication timeout");
+ sme_deauth(wpa_s);
+ }
+}
+
+
+static void sme_assoc_timer(void *eloop_ctx, void *timeout_ctx)
+{
+ struct wpa_supplicant *wpa_s = eloop_ctx;
+ if (wpa_s->wpa_state == WPA_ASSOCIATING) {
+ wpa_msg(wpa_s, MSG_DEBUG, "SME: Association timeout");
+ sme_deauth(wpa_s);
+ }
+}
+
+
+void sme_state_changed(struct wpa_supplicant *wpa_s)
+{
+ /* Make sure timers are cleaned up appropriately. */
+ if (wpa_s->wpa_state != WPA_ASSOCIATING)
+ eloop_cancel_timeout(sme_assoc_timer, wpa_s, NULL);
+ if (wpa_s->wpa_state != WPA_AUTHENTICATING)
+ eloop_cancel_timeout(sme_auth_timer, wpa_s, NULL);
+}
+
+
+void sme_disassoc_while_authenticating(struct wpa_supplicant *wpa_s,
+ const u8 *prev_pending_bssid)
+{
+ /*
+ * mac80211-workaround to force deauth on failed auth cmd,
+ * requires us to remain in authenticating state to allow the
+ * second authentication attempt to be continued properly.
+ */
+ wpa_dbg(wpa_s, MSG_DEBUG, "SME: Allow pending authentication "
+ "to proceed after disconnection event");
+ wpa_supplicant_set_state(wpa_s, WPA_AUTHENTICATING);
+ os_memcpy(wpa_s->pending_bssid, prev_pending_bssid, ETH_ALEN);
+
+ /*
+ * Re-arm authentication timer in case auth fails for whatever reason.
+ */
+ eloop_cancel_timeout(sme_auth_timer, wpa_s, NULL);
+ eloop_register_timeout(SME_AUTH_TIMEOUT, 0, sme_auth_timer, wpa_s,
+ NULL);
+}
+
+
+void sme_deinit(struct wpa_supplicant *wpa_s)
+{
+ os_free(wpa_s->sme.ft_ies);
+ wpa_s->sme.ft_ies = NULL;
+ wpa_s->sme.ft_ies_len = 0;
+#ifdef CONFIG_IEEE80211W
+ sme_stop_sa_query(wpa_s);
+#endif /* CONFIG_IEEE80211W */
+
+ eloop_cancel_timeout(sme_assoc_timer, wpa_s, NULL);
+ eloop_cancel_timeout(sme_auth_timer, wpa_s, NULL);
+ eloop_cancel_timeout(sme_obss_scan_timeout, wpa_s, NULL);
+}
+
+
+static void sme_send_2040_bss_coex(struct wpa_supplicant *wpa_s,
+ const u8 *chan_list, u8 num_channels,
+ u8 num_intol)
+{
+ struct ieee80211_2040_bss_coex_ie *bc_ie;
+ struct ieee80211_2040_intol_chan_report *ic_report;
+ struct wpabuf *buf;
+
+ wpa_printf(MSG_DEBUG, "SME: Send 20/40 BSS Coexistence to " MACSTR,
+ MAC2STR(wpa_s->bssid));
+
+ buf = wpabuf_alloc(2 + /* action.category + action_code */
+ sizeof(struct ieee80211_2040_bss_coex_ie) +
+ sizeof(struct ieee80211_2040_intol_chan_report) +
+ num_channels);
+ if (buf == NULL)
+ return;
+
+ wpabuf_put_u8(buf, WLAN_ACTION_PUBLIC);
+ wpabuf_put_u8(buf, WLAN_PA_20_40_BSS_COEX);
+
+ bc_ie = wpabuf_put(buf, sizeof(*bc_ie));
+ bc_ie->element_id = WLAN_EID_20_40_BSS_COEXISTENCE;
+ bc_ie->length = 1;
+ if (num_intol)
+ bc_ie->coex_param |= WLAN_20_40_BSS_COEX_20MHZ_WIDTH_REQ;
+
+ if (num_channels > 0) {
+ ic_report = wpabuf_put(buf, sizeof(*ic_report));
+ ic_report->element_id = WLAN_EID_20_40_BSS_INTOLERANT;
+ ic_report->length = num_channels + 1;
+ ic_report->op_class = 0;
+ os_memcpy(wpabuf_put(buf, num_channels), chan_list,
+ num_channels);
+ }
+
+ if (wpa_drv_send_action(wpa_s, wpa_s->assoc_freq, 0, wpa_s->bssid,
+ wpa_s->own_addr, wpa_s->bssid,
+ wpabuf_head(buf), wpabuf_len(buf), 0) < 0) {
+ wpa_msg(wpa_s, MSG_INFO,
+ "SME: Failed to send 20/40 BSS Coexistence frame");
+ }
+
+ wpabuf_free(buf);
+}
+
+
+/**
+ * enum wpas_band - Frequency band
+ * @WPAS_BAND_2GHZ: 2.4 GHz ISM band
+ * @WPAS_BAND_5GHZ: around 5 GHz band (4.9 - 5.7 GHz)
+ */
+enum wpas_band {
+ WPAS_BAND_2GHZ,
+ WPAS_BAND_5GHZ,
+ WPAS_BAND_INVALID
+};
+
+/**
+ * freq_to_channel - Convert frequency into channel info
+ * @channel: Buffer for returning channel number
+ * Returns: Band (2 or 5 GHz)
+ */
+static enum wpas_band freq_to_channel(int freq, u8 *channel)
+{
+ enum wpas_band band = (freq <= 2484) ? WPAS_BAND_2GHZ : WPAS_BAND_5GHZ;
+ u8 chan = 0;
+
+ if (freq >= 2412 && freq <= 2472)
+ chan = (freq - 2407) / 5;
+ else if (freq == 2484)
+ chan = 14;
+ else if (freq >= 5180 && freq <= 5805)
+ chan = (freq - 5000) / 5;
+
+ *channel = chan;
+ return band;
+}
+
+
+int sme_proc_obss_scan(struct wpa_supplicant *wpa_s)
+{
+ struct wpa_bss *bss;
+ const u8 *ie;
+ u16 ht_cap;
+ u8 chan_list[P2P_MAX_CHANNELS], channel;
+ u8 num_channels = 0, num_intol = 0, i;
+
+ if (!wpa_s->sme.sched_obss_scan)
+ return 0;
+
+ wpa_s->sme.sched_obss_scan = 0;
+ if (!wpa_s->current_bss || wpa_s->wpa_state != WPA_COMPLETED)
+ return 1;
+
+ /*
+ * Check whether AP uses regulatory triplet or channel triplet in
+ * country info. Right now the operating class of the BSS channel
+ * width trigger event is "unknown" (IEEE Std 802.11-2012 10.15.12),
+ * based on the assumption that operating class triplet is not used in
+ * beacon frame. If the First Channel Number/Operating Extension
+ * Identifier octet has a positive integer value of 201 or greater,
+ * then its operating class triplet.
+ *
+ * TODO: If Supported Operating Classes element is present in beacon
+ * frame, have to lookup operating class in Annex E and fill them in
+ * 2040 coex frame.
+ */
+ ie = wpa_bss_get_ie(wpa_s->current_bss, WLAN_EID_COUNTRY);
+ if (ie && (ie[1] >= 6) && (ie[5] >= 201))
+ return 1;
+
+ os_memset(chan_list, 0, sizeof(chan_list));
+
+ dl_list_for_each(bss, &wpa_s->bss, struct wpa_bss, list) {
+ /* Skip other band bss */
+ if (freq_to_channel(bss->freq, &channel) != WPAS_BAND_2GHZ)
+ continue;
+
+ ie = wpa_bss_get_ie(bss, WLAN_EID_HT_CAP);
+ ht_cap = (ie && (ie[1] == 26)) ? WPA_GET_LE16(ie + 2) : 0;
+
+ if (!ht_cap || (ht_cap & HT_CAP_INFO_40MHZ_INTOLERANT)) {
+ /* Check whether the channel is already considered */
+ for (i = 0; i < num_channels; i++) {
+ if (channel == chan_list[i])
+ break;
+ }
+ if (i != num_channels)
+ continue;
+
+ if (ht_cap & HT_CAP_INFO_40MHZ_INTOLERANT)
+ num_intol++;
+
+ chan_list[num_channels++] = channel;
+ }
+ }
+
+ sme_send_2040_bss_coex(wpa_s, chan_list, num_channels, num_intol);
+ return 1;
+}
+
+
+static struct hostapd_hw_modes * get_mode(struct hostapd_hw_modes *modes,
+ u16 num_modes,
+ enum hostapd_hw_mode mode)
+{
+ u16 i;
+
+ for (i = 0; i < num_modes; i++) {
+ if (modes[i].mode == mode)
+ return &modes[i];
+ }
+
+ return NULL;
+}
+
+
+static void wpa_setband_scan_freqs_list(struct wpa_supplicant *wpa_s,
+ enum hostapd_hw_mode band,
+ struct wpa_driver_scan_params *params)
+{
+ /* Include only supported channels for the specified band */
+ struct hostapd_hw_modes *mode;
+ int count, i;
+
+ mode = get_mode(wpa_s->hw.modes, wpa_s->hw.num_modes, band);
+ if (mode == NULL) {
+ /* No channels supported in this band - use empty list */
+ params->freqs = os_zalloc(sizeof(int));
+ return;
+ }
+
+ params->freqs = os_calloc(mode->num_channels + 1, sizeof(int));
+ if (params->freqs == NULL)
+ return;
+ for (count = 0, i = 0; i < mode->num_channels; i++) {
+ if (mode->channels[i].flag & HOSTAPD_CHAN_DISABLED)
+ continue;
+ params->freqs[count++] = mode->channels[i].freq;
+ }
+}
+
+
+static void sme_obss_scan_timeout(void *eloop_ctx, void *timeout_ctx)
+{
+ struct wpa_supplicant *wpa_s = eloop_ctx;
+ struct wpa_driver_scan_params params;
+
+ if (!wpa_s->current_bss) {
+ wpa_printf(MSG_DEBUG, "SME OBSS: Ignore scan request");
+ return;
+ }
+
+ os_memset(&params, 0, sizeof(params));
+ wpa_setband_scan_freqs_list(wpa_s, HOSTAPD_MODE_IEEE80211G, &params);
+ wpa_printf(MSG_DEBUG, "SME OBSS: Request an OBSS scan");
+
+ if (wpa_supplicant_trigger_scan(wpa_s, &params))
+ wpa_printf(MSG_DEBUG, "SME OBSS: Failed to trigger scan");
+ else
+ wpa_s->sme.sched_obss_scan = 1;
+ os_free(params.freqs);
+
+ eloop_register_timeout(wpa_s->sme.obss_scan_int, 0,
+ sme_obss_scan_timeout, wpa_s, NULL);
+}
+
+
+void sme_sched_obss_scan(struct wpa_supplicant *wpa_s, int enable)
+{
+ const u8 *ie;
+ struct wpa_bss *bss = wpa_s->current_bss;
+ struct wpa_ssid *ssid = wpa_s->current_ssid;
+ struct hostapd_hw_modes *hw_mode = NULL;
+ int i;
+
+ eloop_cancel_timeout(sme_obss_scan_timeout, wpa_s, NULL);
+ wpa_s->sme.sched_obss_scan = 0;
+ if (!enable)
+ return;
+
+ /*
+ * Schedule OBSS scan if driver is using station SME in wpa_supplicant
+ * or it expects OBSS scan to be performed by wpa_supplicant.
+ */
+ if (!((wpa_s->drv_flags & WPA_DRIVER_FLAGS_SME) ||
+ (wpa_s->drv_flags & WPA_DRIVER_FLAGS_OBSS_SCAN)) ||
+ ssid == NULL || ssid->mode != IEEE80211_MODE_INFRA)
+ return;
+
+ if (!wpa_s->hw.modes)
+ return;
+
+ /* only HT caps in 11g mode are relevant */
+ for (i = 0; i < wpa_s->hw.num_modes; i++) {
+ hw_mode = &wpa_s->hw.modes[i];
+ if (hw_mode->mode == HOSTAPD_MODE_IEEE80211G)
+ break;
+ }
+
+ /* Driver does not support HT40 for 11g or doesn't have 11g. */
+ if (i == wpa_s->hw.num_modes || !hw_mode ||
+ !(hw_mode->ht_capab & HT_CAP_INFO_SUPP_CHANNEL_WIDTH_SET))
+ return;
+
+ if (bss == NULL || bss->freq < 2400 || bss->freq > 2500)
+ return; /* Not associated on 2.4 GHz band */
+
+ /* Check whether AP supports HT40 */
+ ie = wpa_bss_get_ie(wpa_s->current_bss, WLAN_EID_HT_CAP);
+ if (!ie || ie[1] < 2 ||
+ !(WPA_GET_LE16(ie + 2) & HT_CAP_INFO_SUPP_CHANNEL_WIDTH_SET))
+ return; /* AP does not support HT40 */
+
+ ie = wpa_bss_get_ie(wpa_s->current_bss,
+ WLAN_EID_OVERLAPPING_BSS_SCAN_PARAMS);
+ if (!ie || ie[1] < 14)
+ return; /* AP does not request OBSS scans */
+
+ wpa_s->sme.obss_scan_int = WPA_GET_LE16(ie + 6);
+ if (wpa_s->sme.obss_scan_int < 10) {
+ wpa_printf(MSG_DEBUG, "SME: Invalid OBSS Scan Interval %u "
+ "replaced with the minimum 10 sec",
+ wpa_s->sme.obss_scan_int);
+ wpa_s->sme.obss_scan_int = 10;
+ }
+ wpa_printf(MSG_DEBUG, "SME: OBSS Scan Interval %u sec",
+ wpa_s->sme.obss_scan_int);
+ eloop_register_timeout(wpa_s->sme.obss_scan_int, 0,
+ sme_obss_scan_timeout, wpa_s, NULL);
+}
+
+
+#ifdef CONFIG_IEEE80211W
+
+static const unsigned int sa_query_max_timeout = 1000;
+static const unsigned int sa_query_retry_timeout = 201;
+
+static int sme_check_sa_query_timeout(struct wpa_supplicant *wpa_s)
+{
+ u32 tu;
+ struct os_time now, passed;
+ os_get_time(&now);
+ os_time_sub(&now, &wpa_s->sme.sa_query_start, &passed);
+ tu = (passed.sec * 1000000 + passed.usec) / 1024;
+ if (sa_query_max_timeout < tu) {
+ wpa_dbg(wpa_s, MSG_DEBUG, "SME: SA Query timed out");
+ sme_stop_sa_query(wpa_s);
+ wpa_supplicant_deauthenticate(
+ wpa_s, WLAN_REASON_PREV_AUTH_NOT_VALID);
+ return 1;
+ }
+
+ return 0;
+}
+
+
+static void sme_send_sa_query_req(struct wpa_supplicant *wpa_s,
+ const u8 *trans_id)
+{
+ u8 req[2 + WLAN_SA_QUERY_TR_ID_LEN];
+ wpa_dbg(wpa_s, MSG_DEBUG, "SME: Sending SA Query Request to "
+ MACSTR, MAC2STR(wpa_s->bssid));
+ wpa_hexdump(MSG_DEBUG, "SME: SA Query Transaction ID",
+ trans_id, WLAN_SA_QUERY_TR_ID_LEN);
+ req[0] = WLAN_ACTION_SA_QUERY;
+ req[1] = WLAN_SA_QUERY_REQUEST;
+ os_memcpy(req + 2, trans_id, WLAN_SA_QUERY_TR_ID_LEN);
+ if (wpa_drv_send_action(wpa_s, wpa_s->assoc_freq, 0, wpa_s->bssid,
+ wpa_s->own_addr, wpa_s->bssid,
+ req, sizeof(req), 0) < 0)
+ wpa_msg(wpa_s, MSG_INFO, "SME: Failed to send SA Query "
+ "Request");
+}
+
+
+static void sme_sa_query_timer(void *eloop_ctx, void *timeout_ctx)
+{
+ struct wpa_supplicant *wpa_s = eloop_ctx;
+ unsigned int timeout, sec, usec;
+ u8 *trans_id, *nbuf;
+
+ if (wpa_s->sme.sa_query_count > 0 &&
+ sme_check_sa_query_timeout(wpa_s))
+ return;
+
+ nbuf = os_realloc_array(wpa_s->sme.sa_query_trans_id,
+ wpa_s->sme.sa_query_count + 1,
+ WLAN_SA_QUERY_TR_ID_LEN);
+ if (nbuf == NULL)
+ return;
+ if (wpa_s->sme.sa_query_count == 0) {
+ /* Starting a new SA Query procedure */
+ os_get_time(&wpa_s->sme.sa_query_start);
+ }
+ trans_id = nbuf + wpa_s->sme.sa_query_count * WLAN_SA_QUERY_TR_ID_LEN;
+ wpa_s->sme.sa_query_trans_id = nbuf;
+ wpa_s->sme.sa_query_count++;
+
+ os_get_random(trans_id, WLAN_SA_QUERY_TR_ID_LEN);
+
+ timeout = sa_query_retry_timeout;
+ sec = ((timeout / 1000) * 1024) / 1000;
+ usec = (timeout % 1000) * 1024;
+ eloop_register_timeout(sec, usec, sme_sa_query_timer, wpa_s, NULL);
+
+ wpa_dbg(wpa_s, MSG_DEBUG, "SME: Association SA Query attempt %d",
+ wpa_s->sme.sa_query_count);
+
+ sme_send_sa_query_req(wpa_s, trans_id);
+}
+
+
+static void sme_start_sa_query(struct wpa_supplicant *wpa_s)
+{
+ sme_sa_query_timer(wpa_s, NULL);
+}
+
+
+static void sme_stop_sa_query(struct wpa_supplicant *wpa_s)
+{
+ eloop_cancel_timeout(sme_sa_query_timer, wpa_s, NULL);
+ os_free(wpa_s->sme.sa_query_trans_id);
+ wpa_s->sme.sa_query_trans_id = NULL;
+ wpa_s->sme.sa_query_count = 0;
+}
+
+
+void sme_event_unprot_disconnect(struct wpa_supplicant *wpa_s, const u8 *sa,
+ const u8 *da, u16 reason_code)
+{
+ struct wpa_ssid *ssid;
+
+ if (!(wpa_s->drv_flags & WPA_DRIVER_FLAGS_SME))
+ return;
+ if (wpa_s->wpa_state != WPA_COMPLETED)
+ return;
+ ssid = wpa_s->current_ssid;
+ if (ssid == NULL ||
+ (ssid->ieee80211w == MGMT_FRAME_PROTECTION_DEFAULT ?
+ wpa_s->conf->pmf : ssid->ieee80211w) == NO_MGMT_FRAME_PROTECTION)
+ return;
+ if (os_memcmp(sa, wpa_s->bssid, ETH_ALEN) != 0)
+ return;
+ if (reason_code != WLAN_REASON_CLASS2_FRAME_FROM_NONAUTH_STA &&
+ reason_code != WLAN_REASON_CLASS3_FRAME_FROM_NONASSOC_STA)
+ return;
+ if (wpa_s->sme.sa_query_count > 0)
+ return;
+
+ wpa_dbg(wpa_s, MSG_DEBUG, "SME: Unprotected disconnect dropped - "
+ "possible AP/STA state mismatch - trigger SA Query");
+ sme_start_sa_query(wpa_s);
+}
+
+
+void sme_sa_query_rx(struct wpa_supplicant *wpa_s, const u8 *sa,
+ const u8 *data, size_t len)
+{
+ int i;
+
+ if (wpa_s->sme.sa_query_trans_id == NULL ||
+ len < 1 + WLAN_SA_QUERY_TR_ID_LEN ||
+ data[0] != WLAN_SA_QUERY_RESPONSE)
+ return;
+ wpa_dbg(wpa_s, MSG_DEBUG, "SME: Received SA Query response from "
+ MACSTR " (trans_id %02x%02x)", MAC2STR(sa), data[1], data[2]);
+
+ if (os_memcmp(sa, wpa_s->bssid, ETH_ALEN) != 0)
+ return;
+
+ for (i = 0; i < wpa_s->sme.sa_query_count; i++) {
+ if (os_memcmp(wpa_s->sme.sa_query_trans_id +
+ i * WLAN_SA_QUERY_TR_ID_LEN,
+ data + 1, WLAN_SA_QUERY_TR_ID_LEN) == 0)
+ break;
+ }
+
+ if (i >= wpa_s->sme.sa_query_count) {
+ wpa_dbg(wpa_s, MSG_DEBUG, "SME: No matching SA Query "
+ "transaction identifier found");
+ return;
+ }
+
+ wpa_dbg(wpa_s, MSG_DEBUG, "SME: Reply to pending SA Query received "
+ "from " MACSTR, MAC2STR(sa));
+ sme_stop_sa_query(wpa_s);
+}
+
+#endif /* CONFIG_IEEE80211W */
diff --git a/contrib/wpa/wpa_supplicant/sme.h b/contrib/wpa/wpa_supplicant/sme.h
index 3ec8cc9..a7cc507 100644
--- a/contrib/wpa/wpa_supplicant/sme.h
+++ b/contrib/wpa/wpa_supplicant/sme.h
@@ -2,14 +2,8 @@
* wpa_supplicant - SME
* Copyright (c) 2009-2010, Jouni Malinen <j@w1.fi>
*
- * This program is free software; you can redistribute it and/or modify
- * it under the terms of the GNU General Public License version 2 as
- * published by the Free Software Foundation.
- *
- * Alternatively, this software may be distributed under the terms of BSD
- * license.
- *
- * See README and COPYING for more details.
+ * This software may be distributed under the terms of the BSD license.
+ * See README for more details.
*/
#ifndef SME_H
@@ -32,6 +26,17 @@ void sme_event_assoc_timed_out(struct wpa_supplicant *wpa_s,
union wpa_event_data *data);
void sme_event_disassoc(struct wpa_supplicant *wpa_s,
union wpa_event_data *data);
+void sme_event_unprot_disconnect(struct wpa_supplicant *wpa_s, const u8 *sa,
+ const u8 *da, u16 reason_code);
+void sme_sa_query_rx(struct wpa_supplicant *wpa_s, const u8 *sa,
+ const u8 *data, size_t len);
+void sme_state_changed(struct wpa_supplicant *wpa_s);
+void sme_disassoc_while_authenticating(struct wpa_supplicant *wpa_s,
+ const u8 *prev_pending_bssid);
+void sme_deinit(struct wpa_supplicant *wpa_s);
+
+int sme_proc_obss_scan(struct wpa_supplicant *wpa_s);
+void sme_sched_obss_scan(struct wpa_supplicant *wpa_s, int enable);
#else /* CONFIG_SME */
@@ -73,6 +78,36 @@ static inline void sme_event_disassoc(struct wpa_supplicant *wpa_s,
{
}
+static inline void sme_event_unprot_disconnect(struct wpa_supplicant *wpa_s,
+ const u8 *sa, const u8 *da,
+ u16 reason_code)
+{
+}
+
+static inline void sme_state_changed(struct wpa_supplicant *wpa_s)
+{
+}
+
+static inline void
+sme_disassoc_while_authenticating(struct wpa_supplicant *wpa_s,
+ const u8 *prev_pending_bssid)
+{
+}
+
+static inline void sme_deinit(struct wpa_supplicant *wpa_s)
+{
+}
+
+static inline int sme_proc_obss_scan(struct wpa_supplicant *wpa_s)
+{
+ return 0;
+}
+
+static inline void sme_sched_obss_scan(struct wpa_supplicant *wpa_s,
+ int enable)
+{
+}
+
#endif /* CONFIG_SME */
#endif /* SME_H */
diff --git a/contrib/wpa/wpa_supplicant/tests/test_eap_sim_common.c b/contrib/wpa/wpa_supplicant/tests/test_eap_sim_common.c
index deb19f6..f60b182 100644
--- a/contrib/wpa/wpa_supplicant/tests/test_eap_sim_common.c
+++ b/contrib/wpa/wpa_supplicant/tests/test_eap_sim_common.c
@@ -2,14 +2,8 @@
* Test program for EAP-SIM PRF
* Copyright (c) 2004-2006, Jouni Malinen <j@w1.fi>
*
- * This program is free software; you can redistribute it and/or modify
- * it under the terms of the GNU General Public License version 2 as
- * published by the Free Software Foundation.
- *
- * Alternatively, this software may be distributed under the terms of BSD
- * license.
- *
- * See README and COPYING for more details.
+ * This software may be distributed under the terms of the BSD license.
+ * See README for more details.
*/
#include "eap_common/eap_sim_common.c"
@@ -34,7 +28,7 @@ static int test_eap_sim_prf(void)
printf("Testing EAP-SIM PRF (FIPS 186-2 + change notice 1)\n");
eap_sim_prf(xkey, buf, sizeof(buf));
- if (memcmp(w, buf, sizeof(w) != 0)) {
+ if (memcmp(w, buf, sizeof(w)) != 0) {
printf("eap_sim_prf failed\n");
return 1;
}
diff --git a/contrib/wpa/wpa_supplicant/tests/test_wpa.c b/contrib/wpa/wpa_supplicant/tests/test_wpa.c
index 7947137..0d659ad 100644
--- a/contrib/wpa/wpa_supplicant/tests/test_wpa.c
+++ b/contrib/wpa/wpa_supplicant/tests/test_wpa.c
@@ -2,14 +2,8 @@
* Test program for combined WPA authenticator/supplicant
* Copyright (c) 2006-2007, Jouni Malinen <j@w1.fi>
*
- * This program is free software; you can redistribute it and/or modify
- * it under the terms of the GNU General Public License version 2 as
- * published by the Free Software Foundation.
- *
- * Alternatively, this software may be distributed under the terms of BSD
- * license.
- *
- * See README and COPYING for more details.
+ * This software may be distributed under the terms of the BSD license.
+ * See README for more details.
*/
#include "includes.h"
diff --git a/contrib/wpa/wpa_supplicant/utils/log2pcap.py b/contrib/wpa/wpa_supplicant/utils/log2pcap.py
new file mode 100755
index 0000000..65e2fa1
--- /dev/null
+++ b/contrib/wpa/wpa_supplicant/utils/log2pcap.py
@@ -0,0 +1,54 @@
+#!/usr/bin/env python
+#
+# Copyright (c) 2012, Intel Corporation
+#
+# Author: Johannes Berg <johannes@sipsolutions.net>
+#
+# This software may be distributed under the terms of the BSD license.
+# See README for more details.
+
+import sys, struct, re
+
+def write_pcap_header(pcap_file):
+ pcap_file.write(
+ struct.pack('<IHHIIII',
+ 0xa1b2c3d4, 2, 4, 0, 0, 65535,
+ 105 # raw 802.11 format
+ ))
+
+def pcap_addpacket(pcap_file, ts, data):
+ # ts in seconds, float
+ pcap_file.write(struct.pack('<IIII',
+ int(ts), int(1000000 * ts) % 1000000,
+ len(data), len(data)))
+ pcap_file.write(data)
+
+if __name__ == "__main__":
+ try:
+ input = sys.argv[1]
+ pcap = sys.argv[2]
+ except IndexError:
+ print "Usage: %s <log file> <pcap file>" % sys.argv[0]
+ sys.exit(2)
+
+ input_file = open(input, 'r')
+ pcap_file = open(pcap, 'w')
+ frame_re = re.compile(r'(([0-9]+.[0-9]{6}):\s*)?nl80211: MLME event frame - hexdump\(len=[0-9]*\):((\s*[0-9a-fA-F]{2})*)')
+
+ write_pcap_header(pcap_file)
+
+ for line in input_file:
+ m = frame_re.match(line)
+ if m is None:
+ continue
+ if m.group(2):
+ ts = float(m.group(2))
+ else:
+ ts = 0
+ hexdata = m.group(3)
+ hexdata = hexdata.split()
+ data = ''.join([chr(int(x, 16)) for x in hexdata])
+ pcap_addpacket(pcap_file, ts, data)
+
+ input_file.close()
+ pcap_file.close()
diff --git a/contrib/wpa/wpa_supplicant/wifi_display.c b/contrib/wpa/wpa_supplicant/wifi_display.c
new file mode 100644
index 0000000..92ca536
--- /dev/null
+++ b/contrib/wpa/wpa_supplicant/wifi_display.c
@@ -0,0 +1,251 @@
+/*
+ * wpa_supplicant - Wi-Fi Display
+ * Copyright (c) 2011, Atheros Communications, Inc.
+ * Copyright (c) 2011-2012, Qualcomm Atheros, Inc.
+ *
+ * This software may be distributed under the terms of the BSD license.
+ * See README for more details.
+ */
+
+#include "includes.h"
+
+#include "common.h"
+#include "p2p/p2p.h"
+#include "common/ieee802_11_defs.h"
+#include "wpa_supplicant_i.h"
+#include "wifi_display.h"
+
+
+int wifi_display_init(struct wpa_global *global)
+{
+ global->wifi_display = 1;
+ return 0;
+}
+
+
+void wifi_display_deinit(struct wpa_global *global)
+{
+ int i;
+ for (i = 0; i < MAX_WFD_SUBELEMS; i++) {
+ wpabuf_free(global->wfd_subelem[i]);
+ global->wfd_subelem[i] = NULL;
+ }
+}
+
+
+static int wifi_display_update_wfd_ie(struct wpa_global *global)
+{
+ struct wpabuf *ie, *buf;
+ size_t len, plen;
+
+ wpa_printf(MSG_DEBUG, "WFD: Update WFD IE");
+
+ if (!global->wifi_display) {
+ wpa_printf(MSG_DEBUG, "WFD: Wi-Fi Display disabled - do not "
+ "include WFD IE");
+ p2p_set_wfd_ie_beacon(global->p2p, NULL);
+ p2p_set_wfd_ie_probe_req(global->p2p, NULL);
+ p2p_set_wfd_ie_probe_resp(global->p2p, NULL);
+ p2p_set_wfd_ie_assoc_req(global->p2p, NULL);
+ p2p_set_wfd_ie_invitation(global->p2p, NULL);
+ p2p_set_wfd_ie_prov_disc_req(global->p2p, NULL);
+ p2p_set_wfd_ie_prov_disc_resp(global->p2p, NULL);
+ p2p_set_wfd_ie_go_neg(global->p2p, NULL);
+ p2p_set_wfd_dev_info(global->p2p, NULL);
+ p2p_set_wfd_assoc_bssid(global->p2p, NULL);
+ p2p_set_wfd_coupled_sink_info(global->p2p, NULL);
+ return 0;
+ }
+
+ p2p_set_wfd_dev_info(global->p2p,
+ global->wfd_subelem[WFD_SUBELEM_DEVICE_INFO]);
+ p2p_set_wfd_assoc_bssid(
+ global->p2p,
+ global->wfd_subelem[WFD_SUBELEM_ASSOCIATED_BSSID]);
+ p2p_set_wfd_coupled_sink_info(
+ global->p2p, global->wfd_subelem[WFD_SUBELEM_COUPLED_SINK]);
+
+ /*
+ * WFD IE is included in number of management frames. Two different
+ * sets of subelements are included depending on the frame:
+ *
+ * Beacon, (Re)Association Request, GO Negotiation Req/Resp/Conf,
+ * Provision Discovery Req:
+ * WFD Device Info
+ * [Associated BSSID]
+ * [Coupled Sink Info]
+ *
+ * Probe Request:
+ * WFD Device Info
+ * [Associated BSSID]
+ * [Coupled Sink Info]
+ * [WFD Extended Capability]
+ *
+ * Probe Response:
+ * WFD Device Info
+ * [Associated BSSID]
+ * [Coupled Sink Info]
+ * [WFD Extended Capability]
+ * [WFD Session Info]
+ *
+ * (Re)Association Response, P2P Invitation Req/Resp,
+ * Provision Discovery Resp:
+ * WFD Device Info
+ * [Associated BSSID]
+ * [Coupled Sink Info]
+ * [WFD Session Info]
+ */
+ len = 0;
+ if (global->wfd_subelem[WFD_SUBELEM_DEVICE_INFO])
+ len += wpabuf_len(global->wfd_subelem[
+ WFD_SUBELEM_DEVICE_INFO]);
+ if (global->wfd_subelem[WFD_SUBELEM_ASSOCIATED_BSSID])
+ len += wpabuf_len(global->wfd_subelem[
+ WFD_SUBELEM_ASSOCIATED_BSSID]);
+ if (global->wfd_subelem[WFD_SUBELEM_COUPLED_SINK])
+ len += wpabuf_len(global->wfd_subelem[
+ WFD_SUBELEM_COUPLED_SINK]);
+ if (global->wfd_subelem[WFD_SUBELEM_SESSION_INFO])
+ len += wpabuf_len(global->wfd_subelem[
+ WFD_SUBELEM_SESSION_INFO]);
+ if (global->wfd_subelem[WFD_SUBELEM_EXT_CAPAB])
+ len += wpabuf_len(global->wfd_subelem[WFD_SUBELEM_EXT_CAPAB]);
+ buf = wpabuf_alloc(len);
+ if (buf == NULL)
+ return -1;
+
+ if (global->wfd_subelem[WFD_SUBELEM_DEVICE_INFO])
+ wpabuf_put_buf(buf,
+ global->wfd_subelem[WFD_SUBELEM_DEVICE_INFO]);
+ if (global->wfd_subelem[WFD_SUBELEM_ASSOCIATED_BSSID])
+ wpabuf_put_buf(buf, global->wfd_subelem[
+ WFD_SUBELEM_ASSOCIATED_BSSID]);
+ if (global->wfd_subelem[WFD_SUBELEM_COUPLED_SINK])
+ wpabuf_put_buf(buf,
+ global->wfd_subelem[WFD_SUBELEM_COUPLED_SINK]);
+
+ ie = wifi_display_encaps(buf);
+ wpa_hexdump_buf(MSG_DEBUG, "WFD: WFD IE for Beacon", ie);
+ p2p_set_wfd_ie_beacon(global->p2p, ie);
+
+ ie = wifi_display_encaps(buf);
+ wpa_hexdump_buf(MSG_DEBUG, "WFD: WFD IE for (Re)Association Request",
+ ie);
+ p2p_set_wfd_ie_assoc_req(global->p2p, ie);
+
+ ie = wifi_display_encaps(buf);
+ wpa_hexdump_buf(MSG_DEBUG, "WFD: WFD IE for GO Negotiation", ie);
+ p2p_set_wfd_ie_go_neg(global->p2p, ie);
+
+ ie = wifi_display_encaps(buf);
+ wpa_hexdump_buf(MSG_DEBUG, "WFD: WFD IE for Provision Discovery "
+ "Request", ie);
+ p2p_set_wfd_ie_prov_disc_req(global->p2p, ie);
+
+ plen = buf->used;
+ if (global->wfd_subelem[WFD_SUBELEM_EXT_CAPAB])
+ wpabuf_put_buf(buf,
+ global->wfd_subelem[WFD_SUBELEM_EXT_CAPAB]);
+
+ ie = wifi_display_encaps(buf);
+ wpa_hexdump_buf(MSG_DEBUG, "WFD: WFD IE for Probe Request", ie);
+ p2p_set_wfd_ie_probe_req(global->p2p, ie);
+
+ if (global->wfd_subelem[WFD_SUBELEM_SESSION_INFO])
+ wpabuf_put_buf(buf,
+ global->wfd_subelem[WFD_SUBELEM_SESSION_INFO]);
+ ie = wifi_display_encaps(buf);
+ wpa_hexdump_buf(MSG_DEBUG, "WFD: WFD IE for Probe Response", ie);
+ p2p_set_wfd_ie_probe_resp(global->p2p, ie);
+
+ /* Remove WFD Extended Capability from buffer */
+ buf->used = plen;
+ if (global->wfd_subelem[WFD_SUBELEM_SESSION_INFO])
+ wpabuf_put_buf(buf,
+ global->wfd_subelem[WFD_SUBELEM_SESSION_INFO]);
+
+ ie = wifi_display_encaps(buf);
+ wpa_hexdump_buf(MSG_DEBUG, "WFD: WFD IE for P2P Invitation", ie);
+ p2p_set_wfd_ie_invitation(global->p2p, ie);
+
+ ie = wifi_display_encaps(buf);
+ wpa_hexdump_buf(MSG_DEBUG, "WFD: WFD IE for Provision Discovery "
+ "Response", ie);
+ p2p_set_wfd_ie_prov_disc_resp(global->p2p, ie);
+
+ wpabuf_free(buf);
+
+ return 0;
+}
+
+
+void wifi_display_enable(struct wpa_global *global, int enabled)
+{
+ wpa_printf(MSG_DEBUG, "WFD: Wi-Fi Display %s",
+ enabled ? "enabled" : "disabled");
+ global->wifi_display = enabled;
+ wifi_display_update_wfd_ie(global);
+}
+
+
+int wifi_display_subelem_set(struct wpa_global *global, char *cmd)
+{
+ char *pos;
+ int subelem;
+ size_t len;
+ struct wpabuf *e;
+
+ pos = os_strchr(cmd, ' ');
+ if (pos == NULL)
+ return -1;
+ *pos++ = '\0';
+ subelem = atoi(cmd);
+ if (subelem < 0 || subelem >= MAX_WFD_SUBELEMS)
+ return -1;
+
+ len = os_strlen(pos);
+ if (len & 1)
+ return -1;
+ len /= 2;
+
+ if (len == 0) {
+ /* Clear subelement */
+ e = NULL;
+ wpa_printf(MSG_DEBUG, "WFD: Clear subelement %d", subelem);
+ } else {
+ e = wpabuf_alloc(1 + len);
+ if (e == NULL)
+ return -1;
+ wpabuf_put_u8(e, subelem);
+ if (hexstr2bin(pos, wpabuf_put(e, len), len) < 0) {
+ wpabuf_free(e);
+ return -1;
+ }
+ wpa_printf(MSG_DEBUG, "WFD: Set subelement %d", subelem);
+ }
+
+ wpabuf_free(global->wfd_subelem[subelem]);
+ global->wfd_subelem[subelem] = e;
+ wifi_display_update_wfd_ie(global);
+
+ return 0;
+}
+
+
+int wifi_display_subelem_get(struct wpa_global *global, char *cmd,
+ char *buf, size_t buflen)
+{
+ int subelem;
+
+ subelem = atoi(cmd);
+ if (subelem < 0 || subelem >= MAX_WFD_SUBELEMS)
+ return -1;
+
+ if (global->wfd_subelem[subelem] == NULL)
+ return 0;
+
+ return wpa_snprintf_hex(buf, buflen,
+ wpabuf_head_u8(global->wfd_subelem[subelem]) +
+ 1,
+ wpabuf_len(global->wfd_subelem[subelem]) - 1);
+}
diff --git a/contrib/wpa/wpa_supplicant/wifi_display.h b/contrib/wpa/wpa_supplicant/wifi_display.h
new file mode 100644
index 0000000..b75d4f2
--- /dev/null
+++ b/contrib/wpa/wpa_supplicant/wifi_display.h
@@ -0,0 +1,20 @@
+/*
+ * wpa_supplicant - Wi-Fi Display
+ * Copyright (c) 2011, Atheros Communications, Inc.
+ * Copyright (c) 2011-2012, Qualcomm Atheros, Inc.
+ *
+ * This software may be distributed under the terms of the BSD license.
+ * See README for more details.
+ */
+
+#ifndef WIFI_DISPLAY_H
+#define WIFI_DISPLAY_H
+
+int wifi_display_init(struct wpa_global *global);
+void wifi_display_deinit(struct wpa_global *global);
+void wifi_display_enable(struct wpa_global *global, int enabled);
+int wifi_display_subelem_set(struct wpa_global *global, char *cmd);
+int wifi_display_subelem_get(struct wpa_global *global, char *cmd,
+ char *buf, size_t buflen);
+
+#endif /* WIFI_DISPLAY_H */
diff --git a/contrib/wpa/wpa_supplicant/wnm_sta.c b/contrib/wpa/wpa_supplicant/wnm_sta.c
new file mode 100644
index 0000000..4d9e453
--- /dev/null
+++ b/contrib/wpa/wpa_supplicant/wnm_sta.c
@@ -0,0 +1,423 @@
+/*
+ * wpa_supplicant - WNM
+ * Copyright (c) 2011-2012, Qualcomm Atheros, Inc.
+ *
+ * This software may be distributed under the terms of the BSD license.
+ * See README for more details.
+ */
+
+#include "utils/includes.h"
+
+#include "utils/common.h"
+#include "common/ieee802_11_defs.h"
+#include "rsn_supp/wpa.h"
+#include "wpa_supplicant_i.h"
+#include "driver_i.h"
+#include "scan.h"
+
+#define MAX_TFS_IE_LEN 1024
+
+
+/* get the TFS IE from driver */
+static int ieee80211_11_get_tfs_ie(struct wpa_supplicant *wpa_s, u8 *buf,
+ u16 *buf_len, enum wnm_oper oper)
+{
+ wpa_printf(MSG_DEBUG, "%s: TFS get operation %d", __func__, oper);
+
+ return wpa_drv_wnm_oper(wpa_s, oper, wpa_s->bssid, buf, buf_len);
+}
+
+
+/* set the TFS IE to driver */
+static int ieee80211_11_set_tfs_ie(struct wpa_supplicant *wpa_s,
+ const u8 *addr, u8 *buf, u16 *buf_len,
+ enum wnm_oper oper)
+{
+ wpa_printf(MSG_DEBUG, "%s: TFS set operation %d", __func__, oper);
+
+ return wpa_drv_wnm_oper(wpa_s, oper, addr, buf, buf_len);
+}
+
+
+/* MLME-SLEEPMODE.request */
+int ieee802_11_send_wnmsleep_req(struct wpa_supplicant *wpa_s,
+ u8 action, u16 intval, struct wpabuf *tfs_req)
+{
+ struct ieee80211_mgmt *mgmt;
+ int res;
+ size_t len;
+ struct wnm_sleep_element *wnmsleep_ie;
+ u8 *wnmtfs_ie;
+ u8 wnmsleep_ie_len;
+ u16 wnmtfs_ie_len; /* possibly multiple IE(s) */
+ enum wnm_oper tfs_oper = action == 0 ? WNM_SLEEP_TFS_REQ_IE_ADD :
+ WNM_SLEEP_TFS_REQ_IE_NONE;
+
+ wpa_printf(MSG_DEBUG, "WNM: Request to send WNM-Sleep Mode Request "
+ "action=%s to " MACSTR,
+ action == 0 ? "enter" : "exit",
+ MAC2STR(wpa_s->bssid));
+
+ /* WNM-Sleep Mode IE */
+ wnmsleep_ie_len = sizeof(struct wnm_sleep_element);
+ wnmsleep_ie = os_zalloc(sizeof(struct wnm_sleep_element));
+ if (wnmsleep_ie == NULL)
+ return -1;
+ wnmsleep_ie->eid = WLAN_EID_WNMSLEEP;
+ wnmsleep_ie->len = wnmsleep_ie_len - 2;
+ wnmsleep_ie->action_type = action;
+ wnmsleep_ie->status = WNM_STATUS_SLEEP_ACCEPT;
+ wnmsleep_ie->intval = host_to_le16(intval);
+ wpa_hexdump(MSG_DEBUG, "WNM: WNM-Sleep Mode element",
+ (u8 *) wnmsleep_ie, wnmsleep_ie_len);
+
+ /* TFS IE(s) */
+ if (tfs_req) {
+ wnmtfs_ie_len = wpabuf_len(tfs_req);
+ wnmtfs_ie = os_malloc(wnmtfs_ie_len);
+ if (wnmtfs_ie == NULL) {
+ os_free(wnmsleep_ie);
+ return -1;
+ }
+ os_memcpy(wnmtfs_ie, wpabuf_head(tfs_req), wnmtfs_ie_len);
+ } else {
+ wnmtfs_ie = os_zalloc(MAX_TFS_IE_LEN);
+ if (wnmtfs_ie == NULL) {
+ os_free(wnmsleep_ie);
+ return -1;
+ }
+ if (ieee80211_11_get_tfs_ie(wpa_s, wnmtfs_ie, &wnmtfs_ie_len,
+ tfs_oper)) {
+ wnmtfs_ie_len = 0;
+ os_free(wnmtfs_ie);
+ wnmtfs_ie = NULL;
+ }
+ }
+ wpa_hexdump(MSG_DEBUG, "WNM: TFS Request element",
+ (u8 *) wnmtfs_ie, wnmtfs_ie_len);
+
+ mgmt = os_zalloc(sizeof(*mgmt) + wnmsleep_ie_len + wnmtfs_ie_len);
+ if (mgmt == NULL) {
+ wpa_printf(MSG_DEBUG, "MLME: Failed to allocate buffer for "
+ "WNM-Sleep Request action frame");
+ os_free(wnmsleep_ie);
+ os_free(wnmtfs_ie);
+ return -1;
+ }
+
+ os_memcpy(mgmt->da, wpa_s->bssid, ETH_ALEN);
+ os_memcpy(mgmt->sa, wpa_s->own_addr, ETH_ALEN);
+ os_memcpy(mgmt->bssid, wpa_s->bssid, ETH_ALEN);
+ mgmt->frame_control = IEEE80211_FC(WLAN_FC_TYPE_MGMT,
+ WLAN_FC_STYPE_ACTION);
+ mgmt->u.action.category = WLAN_ACTION_WNM;
+ mgmt->u.action.u.wnm_sleep_req.action = WNM_SLEEP_MODE_REQ;
+ mgmt->u.action.u.wnm_sleep_req.dialogtoken = 1;
+ os_memcpy(mgmt->u.action.u.wnm_sleep_req.variable, wnmsleep_ie,
+ wnmsleep_ie_len);
+ /* copy TFS IE here */
+ if (wnmtfs_ie_len > 0) {
+ os_memcpy(mgmt->u.action.u.wnm_sleep_req.variable +
+ wnmsleep_ie_len, wnmtfs_ie, wnmtfs_ie_len);
+ }
+
+ len = 1 + sizeof(mgmt->u.action.u.wnm_sleep_req) + wnmsleep_ie_len +
+ wnmtfs_ie_len;
+
+ res = wpa_drv_send_action(wpa_s, wpa_s->assoc_freq, 0, wpa_s->bssid,
+ wpa_s->own_addr, wpa_s->bssid,
+ &mgmt->u.action.category, len, 0);
+ if (res < 0)
+ wpa_printf(MSG_DEBUG, "Failed to send WNM-Sleep Request "
+ "(action=%d, intval=%d)", action, intval);
+
+ os_free(wnmsleep_ie);
+ os_free(wnmtfs_ie);
+ os_free(mgmt);
+
+ return res;
+}
+
+
+static void wnm_sleep_mode_enter_success(struct wpa_supplicant *wpa_s,
+ u8 *tfsresp_ie_start,
+ u8 *tfsresp_ie_end)
+{
+ wpa_drv_wnm_oper(wpa_s, WNM_SLEEP_ENTER_CONFIRM,
+ wpa_s->bssid, NULL, NULL);
+ /* remove GTK/IGTK ?? */
+
+ /* set the TFS Resp IE(s) */
+ if (tfsresp_ie_start && tfsresp_ie_end &&
+ tfsresp_ie_end - tfsresp_ie_start >= 0) {
+ u16 tfsresp_ie_len;
+ tfsresp_ie_len = (tfsresp_ie_end + tfsresp_ie_end[1] + 2) -
+ tfsresp_ie_start;
+ wpa_printf(MSG_DEBUG, "TFS Resp IE(s) found");
+ /* pass the TFS Resp IE(s) to driver for processing */
+ if (ieee80211_11_set_tfs_ie(wpa_s, wpa_s->bssid,
+ tfsresp_ie_start,
+ &tfsresp_ie_len,
+ WNM_SLEEP_TFS_RESP_IE_SET))
+ wpa_printf(MSG_DEBUG, "WNM: Fail to set TFS Resp IE");
+ }
+}
+
+
+static void wnm_sleep_mode_exit_success(struct wpa_supplicant *wpa_s,
+ const u8 *frm, u16 key_len_total)
+{
+ u8 *ptr, *end;
+ u8 gtk_len;
+
+ wpa_drv_wnm_oper(wpa_s, WNM_SLEEP_EXIT_CONFIRM, wpa_s->bssid,
+ NULL, NULL);
+
+ /* Install GTK/IGTK */
+
+ /* point to key data field */
+ ptr = (u8 *) frm + 1 + 1 + 2;
+ end = ptr + key_len_total;
+ wpa_hexdump_key(MSG_DEBUG, "WNM: Key Data", ptr, key_len_total);
+
+ while (ptr + 1 < end) {
+ if (ptr + 2 + ptr[1] > end) {
+ wpa_printf(MSG_DEBUG, "WNM: Invalid Key Data element "
+ "length");
+ if (end > ptr) {
+ wpa_hexdump(MSG_DEBUG, "WNM: Remaining data",
+ ptr, end - ptr);
+ }
+ break;
+ }
+ if (*ptr == WNM_SLEEP_SUBELEM_GTK) {
+ if (ptr[1] < 11 + 5) {
+ wpa_printf(MSG_DEBUG, "WNM: Too short GTK "
+ "subelem");
+ break;
+ }
+ gtk_len = *(ptr + 4);
+ if (ptr[1] < 11 + gtk_len ||
+ gtk_len < 5 || gtk_len > 32) {
+ wpa_printf(MSG_DEBUG, "WNM: Invalid GTK "
+ "subelem");
+ break;
+ }
+ wpa_wnmsleep_install_key(
+ wpa_s->wpa,
+ WNM_SLEEP_SUBELEM_GTK,
+ ptr);
+ ptr += 13 + gtk_len;
+#ifdef CONFIG_IEEE80211W
+ } else if (*ptr == WNM_SLEEP_SUBELEM_IGTK) {
+ if (ptr[1] < 2 + 6 + WPA_IGTK_LEN) {
+ wpa_printf(MSG_DEBUG, "WNM: Too short IGTK "
+ "subelem");
+ break;
+ }
+ wpa_wnmsleep_install_key(wpa_s->wpa,
+ WNM_SLEEP_SUBELEM_IGTK, ptr);
+ ptr += 10 + WPA_IGTK_LEN;
+#endif /* CONFIG_IEEE80211W */
+ } else
+ break; /* skip the loop */
+ }
+}
+
+
+static void ieee802_11_rx_wnmsleep_resp(struct wpa_supplicant *wpa_s,
+ const u8 *frm, int len)
+{
+ /*
+ * Action [1] | Diaglog Token [1] | Key Data Len [2] | Key Data |
+ * WNM-Sleep Mode IE | TFS Response IE
+ */
+ u8 *pos = (u8 *) frm; /* point to action field */
+ u16 key_len_total = le_to_host16(*((u16 *)(frm+2)));
+ struct wnm_sleep_element *wnmsleep_ie = NULL;
+ /* multiple TFS Resp IE (assuming consecutive) */
+ u8 *tfsresp_ie_start = NULL;
+ u8 *tfsresp_ie_end = NULL;
+
+ wpa_printf(MSG_DEBUG, "action=%d token = %d key_len_total = %d",
+ frm[0], frm[1], key_len_total);
+ pos += 4 + key_len_total;
+ if (pos > frm + len) {
+ wpa_printf(MSG_INFO, "WNM: Too short frame for Key Data field");
+ return;
+ }
+ while (pos - frm < len) {
+ u8 ie_len = *(pos + 1);
+ if (pos + 2 + ie_len > frm + len) {
+ wpa_printf(MSG_INFO, "WNM: Invalid IE len %u", ie_len);
+ break;
+ }
+ wpa_hexdump(MSG_DEBUG, "WNM: Element", pos, 2 + ie_len);
+ if (*pos == WLAN_EID_WNMSLEEP)
+ wnmsleep_ie = (struct wnm_sleep_element *) pos;
+ else if (*pos == WLAN_EID_TFS_RESP) {
+ if (!tfsresp_ie_start)
+ tfsresp_ie_start = pos;
+ tfsresp_ie_end = pos;
+ } else
+ wpa_printf(MSG_DEBUG, "EID %d not recognized", *pos);
+ pos += ie_len + 2;
+ }
+
+ if (!wnmsleep_ie) {
+ wpa_printf(MSG_DEBUG, "No WNM-Sleep IE found");
+ return;
+ }
+
+ if (wnmsleep_ie->status == WNM_STATUS_SLEEP_ACCEPT ||
+ wnmsleep_ie->status == WNM_STATUS_SLEEP_EXIT_ACCEPT_GTK_UPDATE) {
+ wpa_printf(MSG_DEBUG, "Successfully recv WNM-Sleep Response "
+ "frame (action=%d, intval=%d)",
+ wnmsleep_ie->action_type, wnmsleep_ie->intval);
+ if (wnmsleep_ie->action_type == WNM_SLEEP_MODE_ENTER) {
+ wnm_sleep_mode_enter_success(wpa_s, tfsresp_ie_start,
+ tfsresp_ie_end);
+ } else if (wnmsleep_ie->action_type == WNM_SLEEP_MODE_EXIT) {
+ wnm_sleep_mode_exit_success(wpa_s, frm, key_len_total);
+ }
+ } else {
+ wpa_printf(MSG_DEBUG, "Reject recv WNM-Sleep Response frame "
+ "(action=%d, intval=%d)",
+ wnmsleep_ie->action_type, wnmsleep_ie->intval);
+ if (wnmsleep_ie->action_type == WNM_SLEEP_MODE_ENTER)
+ wpa_drv_wnm_oper(wpa_s, WNM_SLEEP_ENTER_FAIL,
+ wpa_s->bssid, NULL, NULL);
+ else if (wnmsleep_ie->action_type == WNM_SLEEP_MODE_EXIT)
+ wpa_drv_wnm_oper(wpa_s, WNM_SLEEP_EXIT_FAIL,
+ wpa_s->bssid, NULL, NULL);
+ }
+}
+
+
+static void wnm_send_bss_transition_mgmt_resp(struct wpa_supplicant *wpa_s,
+ u8 dialog_token, u8 status,
+ u8 delay, const u8 *target_bssid)
+{
+ u8 buf[1000], *pos;
+ struct ieee80211_mgmt *mgmt;
+ size_t len;
+
+ wpa_printf(MSG_DEBUG, "WNM: Send BSS Transition Management Response "
+ "to " MACSTR " dialog_token=%u status=%u delay=%d",
+ MAC2STR(wpa_s->bssid), dialog_token, status, delay);
+
+ mgmt = (struct ieee80211_mgmt *) buf;
+ os_memset(&buf, 0, sizeof(buf));
+ os_memcpy(mgmt->da, wpa_s->bssid, ETH_ALEN);
+ os_memcpy(mgmt->sa, wpa_s->own_addr, ETH_ALEN);
+ os_memcpy(mgmt->bssid, wpa_s->bssid, ETH_ALEN);
+ mgmt->frame_control = IEEE80211_FC(WLAN_FC_TYPE_MGMT,
+ WLAN_FC_STYPE_ACTION);
+ mgmt->u.action.category = WLAN_ACTION_WNM;
+ mgmt->u.action.u.bss_tm_resp.action = WNM_BSS_TRANS_MGMT_RESP;
+ mgmt->u.action.u.bss_tm_resp.dialog_token = dialog_token;
+ mgmt->u.action.u.bss_tm_resp.status_code = status;
+ mgmt->u.action.u.bss_tm_resp.bss_termination_delay = delay;
+ pos = mgmt->u.action.u.bss_tm_resp.variable;
+ if (target_bssid) {
+ os_memcpy(pos, target_bssid, ETH_ALEN);
+ pos += ETH_ALEN;
+ }
+
+ len = pos - (u8 *) &mgmt->u.action.category;
+
+ wpa_drv_send_action(wpa_s, wpa_s->assoc_freq, 0, wpa_s->bssid,
+ wpa_s->own_addr, wpa_s->bssid,
+ &mgmt->u.action.category, len, 0);
+}
+
+
+static void ieee802_11_rx_bss_trans_mgmt_req(struct wpa_supplicant *wpa_s,
+ const u8 *pos, const u8 *end,
+ int reply)
+{
+ u8 dialog_token;
+ u8 mode;
+ u16 disassoc_timer;
+
+ if (pos + 5 > end)
+ return;
+
+ dialog_token = pos[0];
+ mode = pos[1];
+ disassoc_timer = WPA_GET_LE16(pos + 2);
+
+ wpa_printf(MSG_DEBUG, "WNM: BSS Transition Management Request: "
+ "dialog_token=%u request_mode=0x%x "
+ "disassoc_timer=%u validity_interval=%u",
+ dialog_token, mode, disassoc_timer, pos[4]);
+ pos += 5;
+ if (mode & 0x08)
+ pos += 12; /* BSS Termination Duration */
+ if (mode & 0x10) {
+ char url[256];
+ if (pos + 1 > end || pos + 1 + pos[0] > end) {
+ wpa_printf(MSG_DEBUG, "WNM: Invalid BSS Transition "
+ "Management Request (URL)");
+ return;
+ }
+ os_memcpy(url, pos + 1, pos[0]);
+ url[pos[0]] = '\0';
+ wpa_msg(wpa_s, MSG_INFO, "WNM: ESS Disassociation Imminent - "
+ "session_info_url=%s", url);
+ }
+
+ if (mode & 0x04) {
+ wpa_msg(wpa_s, MSG_INFO, "WNM: Disassociation Imminent - "
+ "Disassociation Timer %u", disassoc_timer);
+ if (disassoc_timer && !wpa_s->scanning) {
+ /* TODO: mark current BSS less preferred for
+ * selection */
+ wpa_printf(MSG_DEBUG, "Trying to find another BSS");
+ wpa_supplicant_req_scan(wpa_s, 0, 0);
+ }
+ }
+
+ if (reply) {
+ /* TODO: add support for reporting Accept */
+ wnm_send_bss_transition_mgmt_resp(wpa_s, dialog_token,
+ 1 /* Reject - unspecified */,
+ 0, NULL);
+ }
+}
+
+
+void ieee802_11_rx_wnm_action(struct wpa_supplicant *wpa_s,
+ struct rx_action *action)
+{
+ const u8 *pos, *end;
+ u8 act;
+
+ if (action->data == NULL || action->len == 0)
+ return;
+
+ pos = action->data;
+ end = pos + action->len;
+ act = *pos++;
+
+ wpa_printf(MSG_DEBUG, "WNM: RX action %u from " MACSTR,
+ act, MAC2STR(action->sa));
+ if (wpa_s->wpa_state < WPA_ASSOCIATED ||
+ os_memcmp(action->sa, wpa_s->bssid, ETH_ALEN) != 0) {
+ wpa_printf(MSG_DEBUG, "WNM: Ignore unexpected WNM Action "
+ "frame");
+ return;
+ }
+
+ switch (act) {
+ case WNM_BSS_TRANS_MGMT_REQ:
+ ieee802_11_rx_bss_trans_mgmt_req(wpa_s, pos, end,
+ !(action->da[0] & 0x01));
+ break;
+ case WNM_SLEEP_MODE_RESP:
+ ieee802_11_rx_wnmsleep_resp(wpa_s, action->data, action->len);
+ break;
+ default:
+ break;
+ }
+}
diff --git a/contrib/wpa/wpa_supplicant/wnm_sta.h b/contrib/wpa/wpa_supplicant/wnm_sta.h
new file mode 100644
index 0000000..3f9d88b
--- /dev/null
+++ b/contrib/wpa/wpa_supplicant/wnm_sta.h
@@ -0,0 +1,21 @@
+/*
+ * IEEE 802.11v WNM related functions and structures
+ * Copyright (c) 2011-2012, Qualcomm Atheros, Inc.
+ *
+ * This software may be distributed under the terms of the BSD license.
+ * See README for more details.
+ */
+
+#ifndef WNM_STA_H
+#define WNM_STA_H
+
+struct rx_action;
+struct wpa_supplicant;
+
+int ieee802_11_send_wnmsleep_req(struct wpa_supplicant *wpa_s,
+ u8 action, u16 intval, struct wpabuf *tfs_req);
+
+void ieee802_11_rx_wnm_action(struct wpa_supplicant *wpa_s,
+ struct rx_action *action);
+
+#endif /* WNM_STA_H */
diff --git a/contrib/wpa/wpa_supplicant/wpa_cli.c b/contrib/wpa/wpa_supplicant/wpa_cli.c
index 162a0b8..0c6ef5e 100644
--- a/contrib/wpa/wpa_supplicant/wpa_cli.c
+++ b/contrib/wpa/wpa_supplicant/wpa_cli.c
@@ -1,15 +1,9 @@
/*
* WPA Supplicant - command line interface for wpa_supplicant daemon
- * Copyright (c) 2004-2010, Jouni Malinen <j@w1.fi>
+ * Copyright (c) 2004-2012, Jouni Malinen <j@w1.fi>
*
- * This program is free software; you can redistribute it and/or modify
- * it under the terms of the GNU General Public License version 2 as
- * published by the Free Software Foundation.
- *
- * Alternatively, this software may be distributed under the terms of BSD
- * license.
- *
- * See README and COPYING for more details.
+ * This software may be distributed under the terms of the BSD license.
+ * See README for more details.
*/
#include "includes.h"
@@ -19,47 +13,30 @@
#ifdef CONFIG_CTRL_IFACE_UNIX
#include <dirent.h>
#endif /* CONFIG_CTRL_IFACE_UNIX */
-#ifdef CONFIG_READLINE
-#include <readline/readline.h>
-#include <readline/history.h>
-#endif /* CONFIG_READLINE */
-#ifdef CONFIG_WPA_CLI_FORK
-#include <sys/wait.h>
-#endif /* CONFIG_WPA_CLI_FORK */
#include "common/wpa_ctrl.h"
-#include "common.h"
+#include "utils/common.h"
+#include "utils/eloop.h"
+#include "utils/edit.h"
+#include "utils/list.h"
#include "common/version.h"
+#include "common/ieee802_11_defs.h"
+#ifdef ANDROID
+#include <cutils/properties.h>
+#endif /* ANDROID */
static const char *wpa_cli_version =
"wpa_cli v" VERSION_STR "\n"
-"Copyright (c) 2004-2010, Jouni Malinen <j@w1.fi> and contributors";
+"Copyright (c) 2004-2012, Jouni Malinen <j@w1.fi> and contributors";
static const char *wpa_cli_license =
-"This program is free software. You can distribute it and/or modify it\n"
-"under the terms of the GNU General Public License version 2.\n"
-"\n"
-"Alternatively, this software may be distributed under the terms of the\n"
-"BSD license. See README and COPYING for more details.\n";
+"This software may be distributed under the terms of the BSD license.\n"
+"See README for more details.\n";
static const char *wpa_cli_full_license =
-"This program is free software; you can redistribute it and/or modify\n"
-"it under the terms of the GNU General Public License version 2 as\n"
-"published by the Free Software Foundation.\n"
-"\n"
-"This program is distributed in the hope that it will be useful,\n"
-"but WITHOUT ANY WARRANTY; without even the implied warranty of\n"
-"MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the\n"
-"GNU General Public License for more details.\n"
-"\n"
-"You should have received a copy of the GNU General Public License\n"
-"along with this program; if not, write to the Free Software\n"
-"Foundation, Inc., 51 Franklin St, Fifth Floor, Boston, MA 02110-1301 USA\n"
-"\n"
-"Alternatively, this software may be distributed under the terms of the\n"
-"BSD license.\n"
+"This software may be distributed under the terms of the BSD license.\n"
"\n"
"Redistribution and use in source and binary forms, with or without\n"
"modification, are permitted provided that the following conditions are\n"
@@ -91,22 +68,35 @@ static const char *wpa_cli_full_license =
static struct wpa_ctrl *ctrl_conn;
static struct wpa_ctrl *mon_conn;
-#ifdef CONFIG_WPA_CLI_FORK
-static pid_t mon_pid = 0;
-#endif /* CONFIG_WPA_CLI_FORK */
static int wpa_cli_quit = 0;
static int wpa_cli_attached = 0;
static int wpa_cli_connected = 0;
static int wpa_cli_last_id = 0;
-static const char *ctrl_iface_dir = "/var/run/wpa_supplicant";
+#ifndef CONFIG_CTRL_IFACE_DIR
+#define CONFIG_CTRL_IFACE_DIR "/var/run/wpa_supplicant"
+#endif /* CONFIG_CTRL_IFACE_DIR */
+static const char *ctrl_iface_dir = CONFIG_CTRL_IFACE_DIR;
static char *ctrl_ifname = NULL;
static const char *pid_file = NULL;
static const char *action_file = NULL;
static int ping_interval = 5;
static int interactive = 0;
+struct cli_txt_entry {
+ struct dl_list list;
+ char *txt;
+};
+
+static DEFINE_DL_LIST(bsses); /* struct cli_txt_entry */
+static DEFINE_DL_LIST(p2p_peers); /* struct cli_txt_entry */
+static DEFINE_DL_LIST(p2p_groups); /* struct cli_txt_entry */
+
-static void print_help();
+static void print_help(const char *cmd);
+static void wpa_cli_mon_receive(int sock, void *eloop_ctx, void *sock_ctx);
+static void wpa_cli_close_connection(void);
+static char * wpa_cli_get_default_ifname(void);
+static char ** wpa_list_cmd_list(void);
static void usage(void)
@@ -121,63 +111,195 @@ static void usage(void)
"events from\n"
" wpa_supplicant\n"
" -B = run a daemon in the background\n"
- " default path: /var/run/wpa_supplicant\n"
+ " default path: " CONFIG_CTRL_IFACE_DIR "\n"
" default interface: first interface found in socket path\n");
- print_help();
+ print_help(NULL);
}
-#ifdef CONFIG_WPA_CLI_FORK
-static int in_query = 0;
+static void cli_txt_list_free(struct cli_txt_entry *e)
+{
+ dl_list_del(&e->list);
+ os_free(e->txt);
+ os_free(e);
+}
+
-static void wpa_cli_monitor_sig(int sig)
+static void cli_txt_list_flush(struct dl_list *list)
{
- if (sig == SIGUSR1)
- in_query = 1;
- else if (sig == SIGUSR2)
- in_query = 0;
+ struct cli_txt_entry *e;
+ while ((e = dl_list_first(list, struct cli_txt_entry, list)))
+ cli_txt_list_free(e);
}
-static void wpa_cli_monitor(void)
+
+static struct cli_txt_entry * cli_txt_list_get(struct dl_list *txt_list,
+ const char *txt)
{
- char buf[256];
- size_t len = sizeof(buf) - 1;
- struct timeval tv;
- fd_set rfds;
+ struct cli_txt_entry *e;
+ dl_list_for_each(e, txt_list, struct cli_txt_entry, list) {
+ if (os_strcmp(e->txt, txt) == 0)
+ return e;
+ }
+ return NULL;
+}
- signal(SIGUSR1, wpa_cli_monitor_sig);
- signal(SIGUSR2, wpa_cli_monitor_sig);
- while (mon_conn) {
- int s = wpa_ctrl_get_fd(mon_conn);
- tv.tv_sec = 5;
- tv.tv_usec = 0;
- FD_ZERO(&rfds);
- FD_SET(s, &rfds);
- if (select(s + 1, &rfds, NULL, NULL, &tv) < 0) {
- if (errno == EINTR)
- continue;
- perror("select");
- break;
- }
- if (mon_conn == NULL)
+static void cli_txt_list_del(struct dl_list *txt_list, const char *txt)
+{
+ struct cli_txt_entry *e;
+ e = cli_txt_list_get(txt_list, txt);
+ if (e)
+ cli_txt_list_free(e);
+}
+
+
+static void cli_txt_list_del_addr(struct dl_list *txt_list, const char *txt)
+{
+ u8 addr[ETH_ALEN];
+ char buf[18];
+ if (hwaddr_aton(txt, addr) < 0)
+ return;
+ os_snprintf(buf, sizeof(buf), MACSTR, MAC2STR(addr));
+ cli_txt_list_del(txt_list, buf);
+}
+
+
+#ifdef CONFIG_P2P
+static void cli_txt_list_del_word(struct dl_list *txt_list, const char *txt)
+{
+ const char *end;
+ char *buf;
+ end = os_strchr(txt, ' ');
+ if (end == NULL)
+ end = txt + os_strlen(txt);
+ buf = os_malloc(end - txt + 1);
+ if (buf == NULL)
+ return;
+ os_memcpy(buf, txt, end - txt);
+ buf[end - txt] = '\0';
+ cli_txt_list_del(txt_list, buf);
+ os_free(buf);
+}
+#endif /* CONFIG_P2P */
+
+
+static int cli_txt_list_add(struct dl_list *txt_list, const char *txt)
+{
+ struct cli_txt_entry *e;
+ e = cli_txt_list_get(txt_list, txt);
+ if (e)
+ return 0;
+ e = os_zalloc(sizeof(*e));
+ if (e == NULL)
+ return -1;
+ e->txt = os_strdup(txt);
+ if (e->txt == NULL) {
+ os_free(e);
+ return -1;
+ }
+ dl_list_add(txt_list, &e->list);
+ return 0;
+}
+
+
+#ifdef CONFIG_P2P
+static int cli_txt_list_add_addr(struct dl_list *txt_list, const char *txt)
+{
+ u8 addr[ETH_ALEN];
+ char buf[18];
+ if (hwaddr_aton(txt, addr) < 0)
+ return -1;
+ os_snprintf(buf, sizeof(buf), MACSTR, MAC2STR(addr));
+ return cli_txt_list_add(txt_list, buf);
+}
+
+
+static int cli_txt_list_add_word(struct dl_list *txt_list, const char *txt)
+{
+ const char *end;
+ char *buf;
+ int ret;
+ end = os_strchr(txt, ' ');
+ if (end == NULL)
+ end = txt + os_strlen(txt);
+ buf = os_malloc(end - txt + 1);
+ if (buf == NULL)
+ return -1;
+ os_memcpy(buf, txt, end - txt);
+ buf[end - txt] = '\0';
+ ret = cli_txt_list_add(txt_list, buf);
+ os_free(buf);
+ return ret;
+}
+#endif /* CONFIG_P2P */
+
+
+static char ** cli_txt_list_array(struct dl_list *txt_list)
+{
+ unsigned int i, count = dl_list_len(txt_list);
+ char **res;
+ struct cli_txt_entry *e;
+
+ res = os_calloc(count + 1, sizeof(char *));
+ if (res == NULL)
+ return NULL;
+
+ i = 0;
+ dl_list_for_each(e, txt_list, struct cli_txt_entry, list) {
+ res[i] = os_strdup(e->txt);
+ if (res[i] == NULL)
break;
- if (FD_ISSET(s, &rfds)) {
- len = sizeof(buf) - 1;
- int res = wpa_ctrl_recv(mon_conn, buf, &len);
- if (res < 0) {
- perror("wpa_ctrl_recv");
- break;
- }
- buf[len] = '\0';
- if (in_query)
- printf("\r");
- printf("%s\n", buf);
- kill(getppid(), SIGUSR1);
+ i++;
+ }
+
+ return res;
+}
+
+
+static int get_cmd_arg_num(const char *str, int pos)
+{
+ int arg = 0, i;
+
+ for (i = 0; i <= pos; i++) {
+ if (str[i] != ' ') {
+ arg++;
+ while (i <= pos && str[i] != ' ')
+ i++;
}
}
+
+ if (arg > 0)
+ arg--;
+ return arg;
+}
+
+
+static int str_starts(const char *src, const char *match)
+{
+ return os_strncmp(src, match, os_strlen(match)) == 0;
+}
+
+
+static int wpa_cli_show_event(const char *event)
+{
+ const char *start;
+
+ start = os_strchr(event, '>');
+ if (start == NULL)
+ return 1;
+
+ start++;
+ /*
+ * Skip BSS added/removed events since they can be relatively frequent
+ * and are likely of not much use for an interactive user.
+ */
+ if (str_starts(start, WPA_EVENT_BSS_ADDED) ||
+ str_starts(start, WPA_EVENT_BSS_REMOVED))
+ return 0;
+
+ return 1;
}
-#endif /* CONFIG_WPA_CLI_FORK */
static int wpa_cli_open_connection(const char *ifname, int attach)
@@ -192,20 +314,31 @@ static int wpa_cli_open_connection(const char *ifname, int attach)
else
mon_conn = NULL;
#else /* CONFIG_CTRL_IFACE_UDP || CONFIG_CTRL_IFACE_NAMED_PIPE */
- char *cfile;
+ char *cfile = NULL;
int flen, res;
if (ifname == NULL)
return -1;
- flen = os_strlen(ctrl_iface_dir) + os_strlen(ifname) + 2;
- cfile = os_malloc(flen);
- if (cfile == NULL)
- return -1L;
- res = os_snprintf(cfile, flen, "%s/%s", ctrl_iface_dir, ifname);
- if (res < 0 || res >= flen) {
- os_free(cfile);
- return -1;
+#ifdef ANDROID
+ if (access(ctrl_iface_dir, F_OK) < 0) {
+ cfile = os_strdup(ifname);
+ if (cfile == NULL)
+ return -1;
+ }
+#endif /* ANDROID */
+
+ if (cfile == NULL) {
+ flen = os_strlen(ctrl_iface_dir) + os_strlen(ifname) + 2;
+ cfile = os_malloc(flen);
+ if (cfile == NULL)
+ return -1;
+ res = os_snprintf(cfile, flen, "%s/%s", ctrl_iface_dir,
+ ifname);
+ if (res < 0 || res >= flen) {
+ os_free(cfile);
+ return -1;
+ }
}
ctrl_conn = wpa_ctrl_open(cfile);
@@ -224,26 +357,16 @@ static int wpa_cli_open_connection(const char *ifname, int attach)
if (mon_conn) {
if (wpa_ctrl_attach(mon_conn) == 0) {
wpa_cli_attached = 1;
+ if (interactive)
+ eloop_register_read_sock(
+ wpa_ctrl_get_fd(mon_conn),
+ wpa_cli_mon_receive, NULL, NULL);
} else {
printf("Warning: Failed to attach to "
"wpa_supplicant.\n");
+ wpa_cli_close_connection();
return -1;
}
-
-#ifdef CONFIG_WPA_CLI_FORK
- {
- pid_t p = fork();
- if (p < 0) {
- perror("fork");
- return -1;
- }
- if (p == 0) {
- wpa_cli_monitor();
- exit(0);
- } else
- mon_pid = p;
- }
-#endif /* CONFIG_WPA_CLI_FORK */
}
return 0;
@@ -255,15 +378,6 @@ static void wpa_cli_close_connection(void)
if (ctrl_conn == NULL)
return;
-#ifdef CONFIG_WPA_CLI_FORK
- if (mon_pid) {
- int status;
- kill(mon_pid, SIGPIPE);
- wait(&status);
- mon_pid = 0;
- }
-#endif /* CONFIG_WPA_CLI_FORK */
-
if (wpa_cli_attached) {
wpa_ctrl_detach(interactive ? mon_conn : ctrl_conn);
wpa_cli_attached = 0;
@@ -271,6 +385,7 @@ static void wpa_cli_close_connection(void)
wpa_ctrl_close(ctrl_conn);
ctrl_conn = NULL;
if (mon_conn) {
+ eloop_unregister_read_sock(wpa_ctrl_get_fd(mon_conn));
wpa_ctrl_close(mon_conn);
mon_conn = NULL;
}
@@ -306,6 +421,8 @@ static int _wpa_ctrl_command(struct wpa_ctrl *ctrl, char *cmd, int print)
if (print) {
buf[len] = '\0';
printf("%s", buf);
+ if (interactive && len > 0 && buf[len - 1] != '\n')
+ printf("\n");
}
return 0;
}
@@ -317,10 +434,65 @@ static int wpa_ctrl_command(struct wpa_ctrl *ctrl, char *cmd)
}
+static int write_cmd(char *buf, size_t buflen, const char *cmd, int argc,
+ char *argv[])
+{
+ int i, res;
+ char *pos, *end;
+
+ pos = buf;
+ end = buf + buflen;
+
+ res = os_snprintf(pos, end - pos, "%s", cmd);
+ if (res < 0 || res >= end - pos)
+ goto fail;
+ pos += res;
+
+ for (i = 0; i < argc; i++) {
+ res = os_snprintf(pos, end - pos, " %s", argv[i]);
+ if (res < 0 || res >= end - pos)
+ goto fail;
+ pos += res;
+ }
+
+ buf[buflen - 1] = '\0';
+ return 0;
+
+fail:
+ printf("Too long command\n");
+ return -1;
+}
+
+
+static int wpa_cli_cmd(struct wpa_ctrl *ctrl, const char *cmd, int min_args,
+ int argc, char *argv[])
+{
+ char buf[256];
+ if (argc < min_args) {
+ printf("Invalid %s command - at least %d argument%s "
+ "required.\n", cmd, min_args,
+ min_args > 1 ? "s are" : " is");
+ return -1;
+ }
+ if (write_cmd(buf, sizeof(buf), cmd, argc, argv) < 0)
+ return -1;
+ return wpa_ctrl_command(ctrl, buf);
+}
+
+
+static int wpa_cli_cmd_ifname(struct wpa_ctrl *ctrl, int argc, char *argv[])
+{
+ return wpa_ctrl_command(ctrl, "IFNAME");
+}
+
+
static int wpa_cli_cmd_status(struct wpa_ctrl *ctrl, int argc, char *argv[])
{
- int verbose = argc > 0 && os_strcmp(argv[0], "verbose") == 0;
- return wpa_ctrl_command(ctrl, verbose ? "STATUS-VERBOSE" : "STATUS");
+ if (argc > 0 && os_strcmp(argv[0], "verbose") == 0)
+ return wpa_ctrl_command(ctrl, "STATUS-VERBOSE");
+ if (argc > 0 && os_strcmp(argv[0], "wps") == 0)
+ return wpa_ctrl_command(ctrl, "STATUS-WPS");
+ return wpa_ctrl_command(ctrl, "STATUS");
}
@@ -330,6 +502,18 @@ static int wpa_cli_cmd_ping(struct wpa_ctrl *ctrl, int argc, char *argv[])
}
+static int wpa_cli_cmd_relog(struct wpa_ctrl *ctrl, int argc, char *argv[])
+{
+ return wpa_ctrl_command(ctrl, "RELOG");
+}
+
+
+static int wpa_cli_cmd_note(struct wpa_ctrl *ctrl, int argc, char *argv[])
+{
+ return wpa_cli_cmd(ctrl, "NOTE", 1, argc, argv);
+}
+
+
static int wpa_cli_cmd_mib(struct wpa_ctrl *ctrl, int argc, char *argv[])
{
return wpa_ctrl_command(ctrl, "MIB");
@@ -344,11 +528,26 @@ static int wpa_cli_cmd_pmksa(struct wpa_ctrl *ctrl, int argc, char *argv[])
static int wpa_cli_cmd_help(struct wpa_ctrl *ctrl, int argc, char *argv[])
{
- print_help();
+ print_help(argc > 0 ? argv[0] : NULL);
return 0;
}
+static char ** wpa_cli_complete_help(const char *str, int pos)
+{
+ int arg = get_cmd_arg_num(str, pos);
+ char **res = NULL;
+
+ switch (arg) {
+ case 1:
+ res = wpa_list_cmd_list();
+ break;
+ }
+
+ return res;
+}
+
+
static int wpa_cli_cmd_license(struct wpa_ctrl *ctrl, int argc, char *argv[])
{
printf("%s\n\n%s\n", wpa_cli_version, wpa_cli_full_license);
@@ -359,6 +558,8 @@ static int wpa_cli_cmd_license(struct wpa_ctrl *ctrl, int argc, char *argv[])
static int wpa_cli_cmd_quit(struct wpa_ctrl *ctrl, int argc, char *argv[])
{
wpa_cli_quit = 1;
+ if (interactive)
+ eloop_terminate();
return 0;
}
@@ -393,13 +594,17 @@ static int wpa_cli_cmd_set(struct wpa_ctrl *ctrl, int argc, char *argv[])
return 0;
}
- if (argc != 2) {
+ if (argc != 1 && argc != 2) {
printf("Invalid SET command: needs two arguments (variable "
"name and value)\n");
return -1;
}
- res = os_snprintf(cmd, sizeof(cmd), "SET %s %s", argv[0], argv[1]);
+ if (argc == 1)
+ res = os_snprintf(cmd, sizeof(cmd), "SET %s ", argv[0]);
+ else
+ res = os_snprintf(cmd, sizeof(cmd), "SET %s %s",
+ argv[0], argv[1]);
if (res < 0 || (size_t) res >= sizeof(cmd) - 1) {
printf("Too long SET command.\n");
return -1;
@@ -408,6 +613,12 @@ static int wpa_cli_cmd_set(struct wpa_ctrl *ctrl, int argc, char *argv[])
}
+static int wpa_cli_cmd_get(struct wpa_ctrl *ctrl, int argc, char *argv[])
+{
+ return wpa_cli_cmd(ctrl, "GET", 1, argc, argv);
+}
+
+
static int wpa_cli_cmd_logoff(struct wpa_ctrl *ctrl, int argc, char *argv[])
{
return wpa_ctrl_command(ctrl, "LOGOFF");
@@ -430,37 +641,48 @@ static int wpa_cli_cmd_reassociate(struct wpa_ctrl *ctrl, int argc,
static int wpa_cli_cmd_preauthenticate(struct wpa_ctrl *ctrl, int argc,
char *argv[])
{
- char cmd[256];
- int res;
+ return wpa_cli_cmd(ctrl, "PREAUTH", 1, argc, argv);
+}
- if (argc != 1) {
- printf("Invalid PREAUTH command: needs one argument "
- "(BSSID)\n");
- return -1;
- }
- res = os_snprintf(cmd, sizeof(cmd), "PREAUTH %s", argv[0]);
- if (res < 0 || (size_t) res >= sizeof(cmd) - 1) {
- printf("Too long PREAUTH command.\n");
- return -1;
- }
- return wpa_ctrl_command(ctrl, cmd);
+static int wpa_cli_cmd_ap_scan(struct wpa_ctrl *ctrl, int argc, char *argv[])
+{
+ return wpa_cli_cmd(ctrl, "AP_SCAN", 1, argc, argv);
}
-static int wpa_cli_cmd_ap_scan(struct wpa_ctrl *ctrl, int argc, char *argv[])
+static int wpa_cli_cmd_scan_interval(struct wpa_ctrl *ctrl, int argc,
+ char *argv[])
+{
+ return wpa_cli_cmd(ctrl, "SCAN_INTERVAL", 1, argc, argv);
+}
+
+
+static int wpa_cli_cmd_bss_expire_age(struct wpa_ctrl *ctrl, int argc,
+ char *argv[])
+{
+ return wpa_cli_cmd(ctrl, "BSS_EXPIRE_AGE", 1, argc, argv);
+}
+
+
+static int wpa_cli_cmd_bss_expire_count(struct wpa_ctrl *ctrl, int argc,
+ char *argv[])
+{
+ return wpa_cli_cmd(ctrl, "BSS_EXPIRE_COUNT", 1, argc, argv);
+}
+
+
+static int wpa_cli_cmd_bss_flush(struct wpa_ctrl *ctrl, int argc, char *argv[])
{
char cmd[256];
int res;
- if (argc != 1) {
- printf("Invalid AP_SCAN command: needs one argument (ap_scan "
- "value)\n");
- return -1;
- }
- res = os_snprintf(cmd, sizeof(cmd), "AP_SCAN %s", argv[0]);
+ if (argc < 1)
+ res = os_snprintf(cmd, sizeof(cmd), "BSS_FLUSH 0");
+ else
+ res = os_snprintf(cmd, sizeof(cmd), "BSS_FLUSH %s", argv[0]);
if (res < 0 || (size_t) res >= sizeof(cmd) - 1) {
- printf("Too long AP_SCAN command.\n");
+ printf("Too long BSS_FLUSH command.\n");
return -1;
}
return wpa_ctrl_command(ctrl, cmd);
@@ -470,128 +692,157 @@ static int wpa_cli_cmd_ap_scan(struct wpa_ctrl *ctrl, int argc, char *argv[])
static int wpa_cli_cmd_stkstart(struct wpa_ctrl *ctrl, int argc,
char *argv[])
{
- char cmd[256];
- int res;
+ return wpa_cli_cmd(ctrl, "STKSTART", 1, argc, argv);
+}
- if (argc != 1) {
- printf("Invalid STKSTART command: needs one argument "
- "(Peer STA MAC address)\n");
- return -1;
- }
- res = os_snprintf(cmd, sizeof(cmd), "STKSTART %s", argv[0]);
- if (res < 0 || (size_t) res >= sizeof(cmd) - 1) {
- printf("Too long STKSTART command.\n");
+static int wpa_cli_cmd_ft_ds(struct wpa_ctrl *ctrl, int argc, char *argv[])
+{
+ return wpa_cli_cmd(ctrl, "FT_DS", 1, argc, argv);
+}
+
+
+static int wpa_cli_cmd_wps_pbc(struct wpa_ctrl *ctrl, int argc, char *argv[])
+{
+ return wpa_cli_cmd(ctrl, "WPS_PBC", 0, argc, argv);
+}
+
+
+static int wpa_cli_cmd_wps_pin(struct wpa_ctrl *ctrl, int argc, char *argv[])
+{
+ if (argc == 0) {
+ printf("Invalid WPS_PIN command: need one or two arguments:\n"
+ "- BSSID: use 'any' to select any\n"
+ "- PIN: optional, used only with devices that have no "
+ "display\n");
return -1;
}
- return wpa_ctrl_command(ctrl, cmd);
+
+ return wpa_cli_cmd(ctrl, "WPS_PIN", 1, argc, argv);
}
-static int wpa_cli_cmd_ft_ds(struct wpa_ctrl *ctrl, int argc, char *argv[])
+static int wpa_cli_cmd_wps_check_pin(struct wpa_ctrl *ctrl, int argc,
+ char *argv[])
{
- char cmd[256];
- int res;
+ return wpa_cli_cmd(ctrl, "WPS_CHECK_PIN", 1, argc, argv);
+}
+
+
+static int wpa_cli_cmd_wps_cancel(struct wpa_ctrl *ctrl, int argc,
+ char *argv[])
+{
+ return wpa_ctrl_command(ctrl, "WPS_CANCEL");
+}
+
+
+#ifdef CONFIG_WPS_NFC
+
+static int wpa_cli_cmd_wps_nfc(struct wpa_ctrl *ctrl, int argc, char *argv[])
+{
+ return wpa_cli_cmd(ctrl, "WPS_NFC", 0, argc, argv);
+}
+
+
+static int wpa_cli_cmd_wps_nfc_token(struct wpa_ctrl *ctrl, int argc,
+ char *argv[])
+{
+ return wpa_cli_cmd(ctrl, "WPS_NFC_TOKEN", 1, argc, argv);
+}
+
+
+static int wpa_cli_cmd_wps_nfc_tag_read(struct wpa_ctrl *ctrl, int argc,
+ char *argv[])
+{
+ int ret;
+ char *buf;
+ size_t buflen;
if (argc != 1) {
- printf("Invalid FT_DS command: needs one argument "
- "(Target AP MAC address)\n");
+ printf("Invalid 'wps_nfc_tag_read' command - one argument "
+ "is required.\n");
return -1;
}
- res = os_snprintf(cmd, sizeof(cmd), "FT_DS %s", argv[0]);
- if (res < 0 || (size_t) res >= sizeof(cmd) - 1) {
- printf("Too long FT_DS command.\n");
+ buflen = 18 + os_strlen(argv[0]);
+ buf = os_malloc(buflen);
+ if (buf == NULL)
return -1;
- }
- return wpa_ctrl_command(ctrl, cmd);
+ os_snprintf(buf, buflen, "WPS_NFC_TAG_READ %s", argv[0]);
+
+ ret = wpa_ctrl_command(ctrl, buf);
+ os_free(buf);
+
+ return ret;
}
-static int wpa_cli_cmd_wps_pbc(struct wpa_ctrl *ctrl, int argc, char *argv[])
+static int wpa_cli_cmd_nfc_get_handover_req(struct wpa_ctrl *ctrl, int argc,
+ char *argv[])
{
- char cmd[256];
- int res;
+ return wpa_cli_cmd(ctrl, "NFC_GET_HANDOVER_REQ", 2, argc, argv);
+}
- if (argc == 0) {
- /* Any BSSID */
- return wpa_ctrl_command(ctrl, "WPS_PBC");
- }
- /* Specific BSSID */
- res = os_snprintf(cmd, sizeof(cmd), "WPS_PBC %s", argv[0]);
- if (res < 0 || (size_t) res >= sizeof(cmd) - 1) {
- printf("Too long WPS_PBC command.\n");
- return -1;
- }
- return wpa_ctrl_command(ctrl, cmd);
+static int wpa_cli_cmd_nfc_get_handover_sel(struct wpa_ctrl *ctrl, int argc,
+ char *argv[])
+{
+ return wpa_cli_cmd(ctrl, "NFC_GET_HANDOVER_SEL", 2, argc, argv);
}
-static int wpa_cli_cmd_wps_pin(struct wpa_ctrl *ctrl, int argc, char *argv[])
+static int wpa_cli_cmd_nfc_rx_handover_req(struct wpa_ctrl *ctrl, int argc,
+ char *argv[])
{
- char cmd[256];
- int res;
+ int ret;
+ char *buf;
+ size_t buflen;
- if (argc == 0) {
- printf("Invalid WPS_PIN command: need one or two arguments:\n"
- "- BSSID: use 'any' to select any\n"
- "- PIN: optional, used only with devices that have no "
- "display\n");
+ if (argc != 1) {
+ printf("Invalid 'nfc_rx_handover_req' command - one argument "
+ "is required.\n");
return -1;
}
- if (argc == 1) {
- /* Use dynamically generated PIN (returned as reply) */
- res = os_snprintf(cmd, sizeof(cmd), "WPS_PIN %s", argv[0]);
- if (res < 0 || (size_t) res >= sizeof(cmd) - 1) {
- printf("Too long WPS_PIN command.\n");
- return -1;
- }
- return wpa_ctrl_command(ctrl, cmd);
- }
-
- /* Use hardcoded PIN from a label */
- res = os_snprintf(cmd, sizeof(cmd), "WPS_PIN %s %s", argv[0], argv[1]);
- if (res < 0 || (size_t) res >= sizeof(cmd) - 1) {
- printf("Too long WPS_PIN command.\n");
+ buflen = 21 + os_strlen(argv[0]);
+ buf = os_malloc(buflen);
+ if (buf == NULL)
return -1;
- }
- return wpa_ctrl_command(ctrl, cmd);
+ os_snprintf(buf, buflen, "NFC_RX_HANDOVER_REQ %s", argv[0]);
+
+ ret = wpa_ctrl_command(ctrl, buf);
+ os_free(buf);
+
+ return ret;
}
-#ifdef CONFIG_WPS_OOB
-static int wpa_cli_cmd_wps_oob(struct wpa_ctrl *ctrl, int argc, char *argv[])
+static int wpa_cli_cmd_nfc_rx_handover_sel(struct wpa_ctrl *ctrl, int argc,
+ char *argv[])
{
- char cmd[256];
- int res;
+ int ret;
+ char *buf;
+ size_t buflen;
- if (argc != 3 && argc != 4) {
- printf("Invalid WPS_OOB command: need three or four "
- "arguments:\n"
- "- DEV_TYPE: use 'ufd' or 'nfc'\n"
- "- PATH: path of OOB device like '/mnt'\n"
- "- METHOD: OOB method 'pin-e' or 'pin-r', "
- "'cred'\n"
- "- DEV_NAME: (only for NFC) device name like "
- "'pn531'\n");
+ if (argc != 1) {
+ printf("Invalid 'nfc_rx_handover_sel' command - one argument "
+ "is required.\n");
return -1;
}
- if (argc == 3)
- res = os_snprintf(cmd, sizeof(cmd), "WPS_OOB %s %s %s",
- argv[0], argv[1], argv[2]);
- else
- res = os_snprintf(cmd, sizeof(cmd), "WPS_OOB %s %s %s %s",
- argv[0], argv[1], argv[2], argv[3]);
- if (res < 0 || (size_t) res >= sizeof(cmd) - 1) {
- printf("Too long WPS_OOB command.\n");
+ buflen = 21 + os_strlen(argv[0]);
+ buf = os_malloc(buflen);
+ if (buf == NULL)
return -1;
- }
- return wpa_ctrl_command(ctrl, cmd);
+ os_snprintf(buf, buflen, "NFC_RX_HANDOVER_SEL %s", argv[0]);
+
+ ret = wpa_ctrl_command(ctrl, buf);
+ os_free(buf);
+
+ return ret;
}
-#endif /* CONFIG_WPS_OOB */
+
+#endif /* CONFIG_WPS_NFC */
static int wpa_cli_cmd_wps_reg(struct wpa_ctrl *ctrl, int argc, char *argv[])
@@ -602,7 +853,7 @@ static int wpa_cli_cmd_wps_reg(struct wpa_ctrl *ctrl, int argc, char *argv[])
if (argc == 2)
res = os_snprintf(cmd, sizeof(cmd), "WPS_REG %s %s",
argv[0], argv[1]);
- else if (argc == 6) {
+ else if (argc == 5 || argc == 6) {
char ssid_hex[2 * 32 + 1];
char key_hex[2 * 64 + 1];
int i;
@@ -615,10 +866,13 @@ static int wpa_cli_cmd_wps_reg(struct wpa_ctrl *ctrl, int argc, char *argv[])
}
key_hex[0] = '\0';
- for (i = 0; i < 64; i++) {
- if (argv[5][i] == '\0')
- break;
- os_snprintf(&key_hex[i * 2], 3, "%02x", argv[5][i]);
+ if (argc == 6) {
+ for (i = 0; i < 64; i++) {
+ if (argv[5][i] == '\0')
+ break;
+ os_snprintf(&key_hex[i * 2], 3, "%02x",
+ argv[5][i]);
+ }
}
res = os_snprintf(cmd, sizeof(cmd),
@@ -627,11 +881,11 @@ static int wpa_cli_cmd_wps_reg(struct wpa_ctrl *ctrl, int argc, char *argv[])
key_hex);
} else {
printf("Invalid WPS_REG command: need two arguments:\n"
- "- BSSID: use 'any' to select any\n"
+ "- BSSID of the target AP\n"
"- AP PIN\n");
printf("Alternatively, six arguments can be used to "
"reconfigure the AP:\n"
- "- BSSID: use 'any' to select any\n"
+ "- BSSID of the target AP\n"
"- AP PIN\n"
"- new SSID\n"
"- new auth (OPEN, WPAPSK, WPA2PSK)\n"
@@ -648,11 +902,17 @@ static int wpa_cli_cmd_wps_reg(struct wpa_ctrl *ctrl, int argc, char *argv[])
}
+static int wpa_cli_cmd_wps_ap_pin(struct wpa_ctrl *ctrl, int argc,
+ char *argv[])
+{
+ return wpa_cli_cmd(ctrl, "WPS_AP_PIN", 1, argc, argv);
+}
+
+
static int wpa_cli_cmd_wps_er_start(struct wpa_ctrl *ctrl, int argc,
char *argv[])
{
- return wpa_ctrl_command(ctrl, "WPS_ER_START");
-
+ return wpa_cli_cmd(ctrl, "WPS_ER_START", 0, argc, argv);
}
@@ -667,54 +927,29 @@ static int wpa_cli_cmd_wps_er_stop(struct wpa_ctrl *ctrl, int argc,
static int wpa_cli_cmd_wps_er_pin(struct wpa_ctrl *ctrl, int argc,
char *argv[])
{
- char cmd[256];
- int res;
-
- if (argc != 2) {
- printf("Invalid WPS_ER_PIN command: need two arguments:\n"
+ if (argc < 2) {
+ printf("Invalid WPS_ER_PIN command: need at least two "
+ "arguments:\n"
"- UUID: use 'any' to select any\n"
- "- PIN: Enrollee PIN\n");
+ "- PIN: Enrollee PIN\n"
+ "optional: - Enrollee MAC address\n");
return -1;
}
- res = os_snprintf(cmd, sizeof(cmd), "WPS_ER_PIN %s %s",
- argv[0], argv[1]);
- if (res < 0 || (size_t) res >= sizeof(cmd) - 1) {
- printf("Too long WPS_ER_PIN command.\n");
- return -1;
- }
- return wpa_ctrl_command(ctrl, cmd);
+ return wpa_cli_cmd(ctrl, "WPS_ER_PIN", 2, argc, argv);
}
static int wpa_cli_cmd_wps_er_pbc(struct wpa_ctrl *ctrl, int argc,
char *argv[])
{
- char cmd[256];
- int res;
-
- if (argc != 1) {
- printf("Invalid WPS_ER_PBC command: need one argument:\n"
- "- UUID: Specify the Enrollee\n");
- return -1;
- }
-
- res = os_snprintf(cmd, sizeof(cmd), "WPS_ER_PBC %s",
- argv[0]);
- if (res < 0 || (size_t) res >= sizeof(cmd) - 1) {
- printf("Too long WPS_ER_PBC command.\n");
- return -1;
- }
- return wpa_ctrl_command(ctrl, cmd);
+ return wpa_cli_cmd(ctrl, "WPS_ER_PBC", 1, argc, argv);
}
static int wpa_cli_cmd_wps_er_learn(struct wpa_ctrl *ctrl, int argc,
char *argv[])
{
- char cmd[256];
- int res;
-
if (argc != 2) {
printf("Invalid WPS_ER_LEARN command: need two arguments:\n"
"- UUID: specify which AP to use\n"
@@ -722,52 +957,102 @@ static int wpa_cli_cmd_wps_er_learn(struct wpa_ctrl *ctrl, int argc,
return -1;
}
- res = os_snprintf(cmd, sizeof(cmd), "WPS_ER_LEARN %s %s",
- argv[0], argv[1]);
- if (res < 0 || (size_t) res >= sizeof(cmd) - 1) {
- printf("Too long WPS_ER_LEARN command.\n");
+ return wpa_cli_cmd(ctrl, "WPS_ER_LEARN", 2, argc, argv);
+}
+
+
+static int wpa_cli_cmd_wps_er_set_config(struct wpa_ctrl *ctrl, int argc,
+ char *argv[])
+{
+ if (argc != 2) {
+ printf("Invalid WPS_ER_SET_CONFIG command: need two "
+ "arguments:\n"
+ "- UUID: specify which AP to use\n"
+ "- Network configuration id\n");
return -1;
}
- return wpa_ctrl_command(ctrl, cmd);
+
+ return wpa_cli_cmd(ctrl, "WPS_ER_SET_CONFIG", 2, argc, argv);
}
-static int wpa_cli_cmd_ibss_rsn(struct wpa_ctrl *ctrl, int argc, char *argv[])
+static int wpa_cli_cmd_wps_er_config(struct wpa_ctrl *ctrl, int argc,
+ char *argv[])
{
char cmd[256];
int res;
- if (argc != 1) {
- printf("Invalid IBSS_RSN command: needs one argument "
- "(Peer STA MAC address)\n");
+ if (argc == 5 || argc == 6) {
+ char ssid_hex[2 * 32 + 1];
+ char key_hex[2 * 64 + 1];
+ int i;
+
+ ssid_hex[0] = '\0';
+ for (i = 0; i < 32; i++) {
+ if (argv[2][i] == '\0')
+ break;
+ os_snprintf(&ssid_hex[i * 2], 3, "%02x", argv[2][i]);
+ }
+
+ key_hex[0] = '\0';
+ if (argc == 6) {
+ for (i = 0; i < 64; i++) {
+ if (argv[5][i] == '\0')
+ break;
+ os_snprintf(&key_hex[i * 2], 3, "%02x",
+ argv[5][i]);
+ }
+ }
+
+ res = os_snprintf(cmd, sizeof(cmd),
+ "WPS_ER_CONFIG %s %s %s %s %s %s",
+ argv[0], argv[1], ssid_hex, argv[3], argv[4],
+ key_hex);
+ } else {
+ printf("Invalid WPS_ER_CONFIG command: need six arguments:\n"
+ "- AP UUID\n"
+ "- AP PIN\n"
+ "- new SSID\n"
+ "- new auth (OPEN, WPAPSK, WPA2PSK)\n"
+ "- new encr (NONE, WEP, TKIP, CCMP)\n"
+ "- new key\n");
return -1;
}
- res = os_snprintf(cmd, sizeof(cmd), "IBSS_RSN %s", argv[0]);
if (res < 0 || (size_t) res >= sizeof(cmd) - 1) {
- printf("Too long IBSS_RSN command.\n");
+ printf("Too long WPS_ER_CONFIG command.\n");
return -1;
}
return wpa_ctrl_command(ctrl, cmd);
}
-static int wpa_cli_cmd_level(struct wpa_ctrl *ctrl, int argc, char *argv[])
+#ifdef CONFIG_WPS_NFC
+static int wpa_cli_cmd_wps_er_nfc_config_token(struct wpa_ctrl *ctrl, int argc,
+ char *argv[])
{
- char cmd[256];
- int res;
-
- if (argc != 1) {
- printf("Invalid LEVEL command: needs one argument (debug "
- "level)\n");
- return -1;
- }
- res = os_snprintf(cmd, sizeof(cmd), "LEVEL %s", argv[0]);
- if (res < 0 || (size_t) res >= sizeof(cmd) - 1) {
- printf("Too long LEVEL command.\n");
+ if (argc != 2) {
+ printf("Invalid WPS_ER_NFC_CONFIG_TOKEN command: need two "
+ "arguments:\n"
+ "- WPS/NDEF: token format\n"
+ "- UUID: specify which AP to use\n");
return -1;
}
- return wpa_ctrl_command(ctrl, cmd);
+
+ return wpa_cli_cmd(ctrl, "WPS_ER_NFC_CONFIG_TOKEN", 2, argc, argv);
+}
+#endif /* CONFIG_WPS_NFC */
+
+
+static int wpa_cli_cmd_ibss_rsn(struct wpa_ctrl *ctrl, int argc, char *argv[])
+{
+ return wpa_cli_cmd(ctrl, "IBSS_RSN", 1, argc, argv);
+}
+
+
+static int wpa_cli_cmd_level(struct wpa_ctrl *ctrl, int argc, char *argv[])
+{
+ return wpa_cli_cmd(ctrl, "LEVEL", 1, argc, argv);
}
@@ -972,33 +1257,25 @@ static int wpa_cli_cmd_passphrase(struct wpa_ctrl *ctrl, int argc,
static int wpa_cli_cmd_bssid(struct wpa_ctrl *ctrl, int argc, char *argv[])
{
- char cmd[256], *pos, *end;
- int i, ret;
-
if (argc < 2) {
printf("Invalid BSSID command: needs two arguments (network "
"id and BSSID)\n");
return -1;
}
- end = cmd + sizeof(cmd);
- pos = cmd;
- ret = os_snprintf(pos, end - pos, "BSSID");
- if (ret < 0 || ret >= end - pos) {
- printf("Too long BSSID command.\n");
- return -1;
- }
- pos += ret;
- for (i = 0; i < argc; i++) {
- ret = os_snprintf(pos, end - pos, " %s", argv[i]);
- if (ret < 0 || ret >= end - pos) {
- printf("Too long BSSID command.\n");
- return -1;
- }
- pos += ret;
- }
+ return wpa_cli_cmd(ctrl, "BSSID", 2, argc, argv);
+}
- return wpa_ctrl_command(ctrl, cmd);
+
+static int wpa_cli_cmd_blacklist(struct wpa_ctrl *ctrl, int argc, char *argv[])
+{
+ return wpa_cli_cmd(ctrl, "BLACKLIST", 0, argc, argv);
+}
+
+
+static int wpa_cli_cmd_log_level(struct wpa_ctrl *ctrl, int argc, char *argv[])
+{
+ return wpa_cli_cmd(ctrl, "LOG_LEVEL", 0, argc, argv);
}
@@ -1012,63 +1289,21 @@ static int wpa_cli_cmd_list_networks(struct wpa_ctrl *ctrl, int argc,
static int wpa_cli_cmd_select_network(struct wpa_ctrl *ctrl, int argc,
char *argv[])
{
- char cmd[32];
- int res;
-
- if (argc < 1) {
- printf("Invalid SELECT_NETWORK command: needs one argument "
- "(network id)\n");
- return -1;
- }
-
- res = os_snprintf(cmd, sizeof(cmd), "SELECT_NETWORK %s", argv[0]);
- if (res < 0 || (size_t) res >= sizeof(cmd))
- return -1;
- cmd[sizeof(cmd) - 1] = '\0';
-
- return wpa_ctrl_command(ctrl, cmd);
+ return wpa_cli_cmd(ctrl, "SELECT_NETWORK", 1, argc, argv);
}
static int wpa_cli_cmd_enable_network(struct wpa_ctrl *ctrl, int argc,
char *argv[])
{
- char cmd[32];
- int res;
-
- if (argc < 1) {
- printf("Invalid ENABLE_NETWORK command: needs one argument "
- "(network id)\n");
- return -1;
- }
-
- res = os_snprintf(cmd, sizeof(cmd), "ENABLE_NETWORK %s", argv[0]);
- if (res < 0 || (size_t) res >= sizeof(cmd))
- return -1;
- cmd[sizeof(cmd) - 1] = '\0';
-
- return wpa_ctrl_command(ctrl, cmd);
+ return wpa_cli_cmd(ctrl, "ENABLE_NETWORK", 1, argc, argv);
}
static int wpa_cli_cmd_disable_network(struct wpa_ctrl *ctrl, int argc,
char *argv[])
{
- char cmd[32];
- int res;
-
- if (argc < 1) {
- printf("Invalid DISABLE_NETWORK command: needs one argument "
- "(network id)\n");
- return -1;
- }
-
- res = os_snprintf(cmd, sizeof(cmd), "DISABLE_NETWORK %s", argv[0]);
- if (res < 0 || (size_t) res >= sizeof(cmd))
- return -1;
- cmd[sizeof(cmd) - 1] = '\0';
-
- return wpa_ctrl_command(ctrl, cmd);
+ return wpa_cli_cmd(ctrl, "DISABLE_NETWORK", 1, argc, argv);
}
@@ -1082,21 +1317,7 @@ static int wpa_cli_cmd_add_network(struct wpa_ctrl *ctrl, int argc,
static int wpa_cli_cmd_remove_network(struct wpa_ctrl *ctrl, int argc,
char *argv[])
{
- char cmd[32];
- int res;
-
- if (argc < 1) {
- printf("Invalid REMOVE_NETWORK command: needs one argument "
- "(network id)\n");
- return -1;
- }
-
- res = os_snprintf(cmd, sizeof(cmd), "REMOVE_NETWORK %s", argv[0]);
- if (res < 0 || (size_t) res >= sizeof(cmd))
- return -1;
- cmd[sizeof(cmd) - 1] = '\0';
-
- return wpa_ctrl_command(ctrl, cmd);
+ return wpa_cli_cmd(ctrl, "REMOVE_NETWORK", 1, argc, argv);
}
@@ -1124,36 +1345,24 @@ static void wpa_cli_show_network_variables(void)
static int wpa_cli_cmd_set_network(struct wpa_ctrl *ctrl, int argc,
char *argv[])
{
- char cmd[256];
- int res;
-
if (argc == 0) {
wpa_cli_show_network_variables();
return 0;
}
- if (argc != 3) {
+ if (argc < 3) {
printf("Invalid SET_NETWORK command: needs three arguments\n"
"(network id, variable name, and value)\n");
return -1;
}
- res = os_snprintf(cmd, sizeof(cmd), "SET_NETWORK %s %s %s",
- argv[0], argv[1], argv[2]);
- if (res < 0 || (size_t) res >= sizeof(cmd) - 1) {
- printf("Too long SET_NETWORK command.\n");
- return -1;
- }
- return wpa_ctrl_command(ctrl, cmd);
+ return wpa_cli_cmd(ctrl, "SET_NETWORK", 3, argc, argv);
}
static int wpa_cli_cmd_get_network(struct wpa_ctrl *ctrl, int argc,
char *argv[])
{
- char cmd[256];
- int res;
-
if (argc == 0) {
wpa_cli_show_network_variables();
return 0;
@@ -1165,13 +1374,39 @@ static int wpa_cli_cmd_get_network(struct wpa_ctrl *ctrl, int argc,
return -1;
}
- res = os_snprintf(cmd, sizeof(cmd), "GET_NETWORK %s %s",
- argv[0], argv[1]);
- if (res < 0 || (size_t) res >= sizeof(cmd) - 1) {
- printf("Too long GET_NETWORK command.\n");
+ return wpa_cli_cmd(ctrl, "GET_NETWORK", 2, argc, argv);
+}
+
+
+static int wpa_cli_cmd_list_creds(struct wpa_ctrl *ctrl, int argc,
+ char *argv[])
+{
+ return wpa_ctrl_command(ctrl, "LIST_CREDS");
+}
+
+
+static int wpa_cli_cmd_add_cred(struct wpa_ctrl *ctrl, int argc, char *argv[])
+{
+ return wpa_ctrl_command(ctrl, "ADD_CRED");
+}
+
+
+static int wpa_cli_cmd_remove_cred(struct wpa_ctrl *ctrl, int argc,
+ char *argv[])
+{
+ return wpa_cli_cmd(ctrl, "REMOVE_CRED", 1, argc, argv);
+}
+
+
+static int wpa_cli_cmd_set_cred(struct wpa_ctrl *ctrl, int argc, char *argv[])
+{
+ if (argc != 3) {
+ printf("Invalid SET_CRED command: needs three arguments\n"
+ "(cred id, variable name, and value)\n");
return -1;
}
- return wpa_ctrl_command(ctrl, cmd);
+
+ return wpa_cli_cmd(ctrl, "SET_CRED", 3, argc, argv);
}
@@ -1211,30 +1446,28 @@ static int wpa_cli_cmd_scan_results(struct wpa_ctrl *ctrl, int argc,
static int wpa_cli_cmd_bss(struct wpa_ctrl *ctrl, int argc, char *argv[])
{
- char cmd[64];
- int res;
+ return wpa_cli_cmd(ctrl, "BSS", 1, argc, argv);
+}
- if (argc != 1) {
- printf("Invalid BSS command: need one argument (index or "
- "BSSID)\n");
- return -1;
- }
- res = os_snprintf(cmd, sizeof(cmd), "BSS %s", argv[0]);
- if (res < 0 || (size_t) res >= sizeof(cmd))
- return -1;
- cmd[sizeof(cmd) - 1] = '\0';
+static char ** wpa_cli_complete_bss(const char *str, int pos)
+{
+ int arg = get_cmd_arg_num(str, pos);
+ char **res = NULL;
- return wpa_ctrl_command(ctrl, cmd);
+ switch (arg) {
+ case 1:
+ res = cli_txt_list_array(&bsses);
+ break;
+ }
+
+ return res;
}
static int wpa_cli_cmd_get_capability(struct wpa_ctrl *ctrl, int argc,
char *argv[])
{
- char cmd[64];
- int res;
-
if (argc < 1 || argc > 2) {
printf("Invalid GET_CAPABILITY command: need either one or "
"two arguments\n");
@@ -1247,13 +1480,7 @@ static int wpa_cli_cmd_get_capability(struct wpa_ctrl *ctrl, int argc,
return -1;
}
- res = os_snprintf(cmd, sizeof(cmd), "GET_CAPABILITY %s%s", argv[0],
- (argc == 2) ? " strict" : "");
- if (res < 0 || (size_t) res >= sizeof(cmd))
- return -1;
- cmd[sizeof(cmd) - 1] = '\0';
-
- return wpa_ctrl_command(ctrl, cmd);
+ return wpa_cli_cmd(ctrl, "GET_CAPABILITY", 1, argc, argv);
}
@@ -1333,20 +1560,7 @@ static int wpa_cli_cmd_interface_add(struct wpa_ctrl *ctrl, int argc,
static int wpa_cli_cmd_interface_remove(struct wpa_ctrl *ctrl, int argc,
char *argv[])
{
- char cmd[128];
- int res;
-
- if (argc != 1) {
- printf("Invalid INTERFACE_REMOVE command: needs one argument "
- "(interface name)\n");
- return -1;
- }
-
- res = os_snprintf(cmd, sizeof(cmd), "INTERFACE_REMOVE %s", argv[0]);
- if (res < 0 || (size_t) res >= sizeof(cmd))
- return -1;
- cmd[sizeof(cmd) - 1] = '\0';
- return wpa_ctrl_command(ctrl, cmd);
+ return wpa_cli_cmd(ctrl, "INTERFACE_REMOVE", 1, argc, argv);
}
@@ -1360,14 +1574,7 @@ static int wpa_cli_cmd_interface_list(struct wpa_ctrl *ctrl, int argc,
#ifdef CONFIG_AP
static int wpa_cli_cmd_sta(struct wpa_ctrl *ctrl, int argc, char *argv[])
{
- char buf[64];
- if (argc != 1) {
- printf("Invalid 'sta' command - exactly one argument, STA "
- "address, is required.\n");
- return -1;
- }
- os_snprintf(buf, sizeof(buf), "STA %s", argv[0]);
- return wpa_ctrl_command(ctrl, buf);
+ return wpa_cli_cmd(ctrl, "STA", 1, argc, argv);
}
@@ -1383,7 +1590,7 @@ static int wpa_ctrl_command_sta(struct wpa_ctrl *ctrl, char *cmd,
return -1;
}
len = sizeof(buf) - 1;
- ret = wpa_ctrl_request(ctrl, cmd, strlen(cmd), buf, &len,
+ ret = wpa_ctrl_request(ctrl, cmd, os_strlen(cmd), buf, &len,
wpa_cli_msg_cb);
if (ret == -2) {
printf("'%s' command timed out.\n", cmd);
@@ -1394,7 +1601,7 @@ static int wpa_ctrl_command_sta(struct wpa_ctrl *ctrl, char *cmd,
}
buf[len] = '\0';
- if (memcmp(buf, "FAIL", 4) == 0)
+ if (os_memcmp(buf, "FAIL", 4) == 0)
return -1;
printf("%s", buf);
@@ -1419,6 +1626,20 @@ static int wpa_cli_cmd_all_sta(struct wpa_ctrl *ctrl, int argc, char *argv[])
return -1;
}
+
+
+static int wpa_cli_cmd_deauthenticate(struct wpa_ctrl *ctrl, int argc,
+ char *argv[])
+{
+ return wpa_cli_cmd(ctrl, "DEAUTHENTICATE", 1, argc, argv);
+}
+
+
+static int wpa_cli_cmd_disassociate(struct wpa_ctrl *ctrl, int argc,
+ char *argv[])
+{
+ return wpa_cli_cmd(ctrl, "DISASSOCIATE", 1, argc, argv);
+}
#endif /* CONFIG_AP */
@@ -1442,23 +1663,607 @@ static int wpa_cli_cmd_drop_sa(struct wpa_ctrl *ctrl, int argc, char *argv[])
static int wpa_cli_cmd_roam(struct wpa_ctrl *ctrl, int argc, char *argv[])
{
- char cmd[128];
+ return wpa_cli_cmd(ctrl, "ROAM", 1, argc, argv);
+}
+
+
+#ifdef CONFIG_P2P
+
+static int wpa_cli_cmd_p2p_find(struct wpa_ctrl *ctrl, int argc, char *argv[])
+{
+ return wpa_cli_cmd(ctrl, "P2P_FIND", 0, argc, argv);
+}
+
+
+static char ** wpa_cli_complete_p2p_find(const char *str, int pos)
+{
+ char **res = NULL;
+ int arg = get_cmd_arg_num(str, pos);
+
+ res = os_calloc(6, sizeof(char *));
+ if (res == NULL)
+ return NULL;
+ res[0] = os_strdup("type=social");
+ if (res[0] == NULL) {
+ os_free(res);
+ return NULL;
+ }
+ res[1] = os_strdup("type=progressive");
+ if (res[1] == NULL)
+ return res;
+ res[2] = os_strdup("delay=");
+ if (res[2] == NULL)
+ return res;
+ res[3] = os_strdup("dev_id=");
+ if (res[3] == NULL)
+ return res;
+ if (arg == 1)
+ res[4] = os_strdup("[timeout]");
+
+ return res;
+}
+
+
+static int wpa_cli_cmd_p2p_stop_find(struct wpa_ctrl *ctrl, int argc,
+ char *argv[])
+{
+ return wpa_ctrl_command(ctrl, "P2P_STOP_FIND");
+}
+
+
+static int wpa_cli_cmd_p2p_connect(struct wpa_ctrl *ctrl, int argc,
+ char *argv[])
+{
+ return wpa_cli_cmd(ctrl, "P2P_CONNECT", 2, argc, argv);
+}
+
+
+static char ** wpa_cli_complete_p2p_connect(const char *str, int pos)
+{
+ int arg = get_cmd_arg_num(str, pos);
+ char **res = NULL;
+
+ switch (arg) {
+ case 1:
+ res = cli_txt_list_array(&p2p_peers);
+ break;
+ }
+
+ return res;
+}
+
+
+static int wpa_cli_cmd_p2p_listen(struct wpa_ctrl *ctrl, int argc,
+ char *argv[])
+{
+ return wpa_cli_cmd(ctrl, "P2P_LISTEN", 0, argc, argv);
+}
+
+
+static int wpa_cli_cmd_p2p_group_remove(struct wpa_ctrl *ctrl, int argc,
+ char *argv[])
+{
+ return wpa_cli_cmd(ctrl, "P2P_GROUP_REMOVE", 1, argc, argv);
+}
+
+
+static char ** wpa_cli_complete_p2p_group_remove(const char *str, int pos)
+{
+ int arg = get_cmd_arg_num(str, pos);
+ char **res = NULL;
+
+ switch (arg) {
+ case 1:
+ res = cli_txt_list_array(&p2p_groups);
+ break;
+ }
+
+ return res;
+}
+
+
+static int wpa_cli_cmd_p2p_group_add(struct wpa_ctrl *ctrl, int argc,
+ char *argv[])
+{
+ return wpa_cli_cmd(ctrl, "P2P_GROUP_ADD", 0, argc, argv);
+}
+
+
+static int wpa_cli_cmd_p2p_prov_disc(struct wpa_ctrl *ctrl, int argc,
+ char *argv[])
+{
+ if (argc != 2 && argc != 3) {
+ printf("Invalid P2P_PROV_DISC command: needs at least "
+ "two arguments, address and config method\n"
+ "(display, keypad, or pbc) and an optional join\n");
+ return -1;
+ }
+
+ return wpa_cli_cmd(ctrl, "P2P_PROV_DISC", 2, argc, argv);
+}
+
+
+static int wpa_cli_cmd_p2p_get_passphrase(struct wpa_ctrl *ctrl, int argc,
+ char *argv[])
+{
+ return wpa_ctrl_command(ctrl, "P2P_GET_PASSPHRASE");
+}
+
+
+static int wpa_cli_cmd_p2p_serv_disc_req(struct wpa_ctrl *ctrl, int argc,
+ char *argv[])
+{
+ char cmd[4096];
+
+ if (argc != 2 && argc != 4) {
+ printf("Invalid P2P_SERV_DISC_REQ command: needs two "
+ "arguments (address and TLVs) or four arguments "
+ "(address, \"upnp\", version, search target "
+ "(SSDP ST:)\n");
+ return -1;
+ }
+
+ if (write_cmd(cmd, sizeof(cmd), "P2P_SERV_DISC_REQ", argc, argv) < 0)
+ return -1;
+ return wpa_ctrl_command(ctrl, cmd);
+}
+
+
+static int wpa_cli_cmd_p2p_serv_disc_cancel_req(struct wpa_ctrl *ctrl,
+ int argc, char *argv[])
+{
+ return wpa_cli_cmd(ctrl, "P2P_SERV_DISC_CANCEL_REQ", 1, argc, argv);
+}
+
+
+static int wpa_cli_cmd_p2p_serv_disc_resp(struct wpa_ctrl *ctrl, int argc,
+ char *argv[])
+{
+ char cmd[4096];
+ int res;
+
+ if (argc != 4) {
+ printf("Invalid P2P_SERV_DISC_RESP command: needs four "
+ "arguments (freq, address, dialog token, and TLVs)\n");
+ return -1;
+ }
+
+ res = os_snprintf(cmd, sizeof(cmd), "P2P_SERV_DISC_RESP %s %s %s %s",
+ argv[0], argv[1], argv[2], argv[3]);
+ if (res < 0 || (size_t) res >= sizeof(cmd))
+ return -1;
+ cmd[sizeof(cmd) - 1] = '\0';
+ return wpa_ctrl_command(ctrl, cmd);
+}
+
+
+static int wpa_cli_cmd_p2p_service_update(struct wpa_ctrl *ctrl, int argc,
+ char *argv[])
+{
+ return wpa_ctrl_command(ctrl, "P2P_SERVICE_UPDATE");
+}
+
+
+static int wpa_cli_cmd_p2p_serv_disc_external(struct wpa_ctrl *ctrl,
+ int argc, char *argv[])
+{
+ return wpa_cli_cmd(ctrl, "P2P_SERV_DISC_EXTERNAL", 1, argc, argv);
+}
+
+
+static int wpa_cli_cmd_p2p_service_flush(struct wpa_ctrl *ctrl, int argc,
+ char *argv[])
+{
+ return wpa_ctrl_command(ctrl, "P2P_SERVICE_FLUSH");
+}
+
+
+static int wpa_cli_cmd_p2p_service_add(struct wpa_ctrl *ctrl, int argc,
+ char *argv[])
+{
+ char cmd[4096];
+ int res;
+
+ if (argc != 3 && argc != 4) {
+ printf("Invalid P2P_SERVICE_ADD command: needs three or four "
+ "arguments\n");
+ return -1;
+ }
+
+ if (argc == 4)
+ res = os_snprintf(cmd, sizeof(cmd),
+ "P2P_SERVICE_ADD %s %s %s %s",
+ argv[0], argv[1], argv[2], argv[3]);
+ else
+ res = os_snprintf(cmd, sizeof(cmd),
+ "P2P_SERVICE_ADD %s %s %s",
+ argv[0], argv[1], argv[2]);
+ if (res < 0 || (size_t) res >= sizeof(cmd))
+ return -1;
+ cmd[sizeof(cmd) - 1] = '\0';
+ return wpa_ctrl_command(ctrl, cmd);
+}
+
+
+static int wpa_cli_cmd_p2p_service_del(struct wpa_ctrl *ctrl, int argc,
+ char *argv[])
+{
+ char cmd[4096];
+ int res;
+
+ if (argc != 2 && argc != 3) {
+ printf("Invalid P2P_SERVICE_DEL command: needs two or three "
+ "arguments\n");
+ return -1;
+ }
+
+ if (argc == 3)
+ res = os_snprintf(cmd, sizeof(cmd),
+ "P2P_SERVICE_DEL %s %s %s",
+ argv[0], argv[1], argv[2]);
+ else
+ res = os_snprintf(cmd, sizeof(cmd),
+ "P2P_SERVICE_DEL %s %s",
+ argv[0], argv[1]);
+ if (res < 0 || (size_t) res >= sizeof(cmd))
+ return -1;
+ cmd[sizeof(cmd) - 1] = '\0';
+ return wpa_ctrl_command(ctrl, cmd);
+}
+
+
+static int wpa_cli_cmd_p2p_reject(struct wpa_ctrl *ctrl,
+ int argc, char *argv[])
+{
+ return wpa_cli_cmd(ctrl, "P2P_REJECT", 1, argc, argv);
+}
+
+
+static int wpa_cli_cmd_p2p_invite(struct wpa_ctrl *ctrl,
+ int argc, char *argv[])
+{
+ return wpa_cli_cmd(ctrl, "P2P_INVITE", 1, argc, argv);
+}
+
+
+static int wpa_cli_cmd_p2p_peer(struct wpa_ctrl *ctrl, int argc, char *argv[])
+{
+ return wpa_cli_cmd(ctrl, "P2P_PEER", 1, argc, argv);
+}
+
+
+static char ** wpa_cli_complete_p2p_peer(const char *str, int pos)
+{
+ int arg = get_cmd_arg_num(str, pos);
+ char **res = NULL;
+
+ switch (arg) {
+ case 1:
+ res = cli_txt_list_array(&p2p_peers);
+ break;
+ }
+
+ return res;
+}
+
+
+static int wpa_ctrl_command_p2p_peer(struct wpa_ctrl *ctrl, char *cmd,
+ char *addr, size_t addr_len,
+ int discovered)
+{
+ char buf[4096], *pos;
+ size_t len;
+ int ret;
+
+ if (ctrl_conn == NULL)
+ return -1;
+ len = sizeof(buf) - 1;
+ ret = wpa_ctrl_request(ctrl, cmd, os_strlen(cmd), buf, &len,
+ wpa_cli_msg_cb);
+ if (ret == -2) {
+ printf("'%s' command timed out.\n", cmd);
+ return -2;
+ } else if (ret < 0) {
+ printf("'%s' command failed.\n", cmd);
+ return -1;
+ }
+
+ buf[len] = '\0';
+ if (os_memcmp(buf, "FAIL", 4) == 0)
+ return -1;
+
+ pos = buf;
+ while (*pos != '\0' && *pos != '\n')
+ pos++;
+ *pos++ = '\0';
+ os_strlcpy(addr, buf, addr_len);
+ if (!discovered || os_strstr(pos, "[PROBE_REQ_ONLY]") == NULL)
+ printf("%s\n", addr);
+ return 0;
+}
+
+
+static int wpa_cli_cmd_p2p_peers(struct wpa_ctrl *ctrl, int argc, char *argv[])
+{
+ char addr[32], cmd[64];
+ int discovered;
+
+ discovered = argc > 0 && os_strcmp(argv[0], "discovered") == 0;
+
+ if (wpa_ctrl_command_p2p_peer(ctrl, "P2P_PEER FIRST",
+ addr, sizeof(addr), discovered))
+ return -1;
+ do {
+ os_snprintf(cmd, sizeof(cmd), "P2P_PEER NEXT-%s", addr);
+ } while (wpa_ctrl_command_p2p_peer(ctrl, cmd, addr, sizeof(addr),
+ discovered) == 0);
+
+ return 0;
+}
+
+
+static int wpa_cli_cmd_p2p_set(struct wpa_ctrl *ctrl, int argc, char *argv[])
+{
+ return wpa_cli_cmd(ctrl, "P2P_SET", 2, argc, argv);
+}
+
+
+static int wpa_cli_cmd_p2p_flush(struct wpa_ctrl *ctrl, int argc, char *argv[])
+{
+ return wpa_ctrl_command(ctrl, "P2P_FLUSH");
+}
+
+
+static int wpa_cli_cmd_p2p_cancel(struct wpa_ctrl *ctrl, int argc,
+ char *argv[])
+{
+ return wpa_ctrl_command(ctrl, "P2P_CANCEL");
+}
+
+
+static int wpa_cli_cmd_p2p_unauthorize(struct wpa_ctrl *ctrl, int argc,
+ char *argv[])
+{
+ return wpa_cli_cmd(ctrl, "P2P_UNAUTHORIZE", 1, argc, argv);
+}
+
+
+static int wpa_cli_cmd_p2p_presence_req(struct wpa_ctrl *ctrl, int argc,
+ char *argv[])
+{
+ if (argc != 0 && argc != 2 && argc != 4) {
+ printf("Invalid P2P_PRESENCE_REQ command: needs two arguments "
+ "(preferred duration, interval; in microsecods).\n"
+ "Optional second pair can be used to provide "
+ "acceptable values.\n");
+ return -1;
+ }
+
+ return wpa_cli_cmd(ctrl, "P2P_PRESENCE_REQ", 0, argc, argv);
+}
+
+
+static int wpa_cli_cmd_p2p_ext_listen(struct wpa_ctrl *ctrl, int argc,
+ char *argv[])
+{
+ if (argc != 0 && argc != 2) {
+ printf("Invalid P2P_EXT_LISTEN command: needs two arguments "
+ "(availability period, availability interval; in "
+ "millisecods).\n"
+ "Extended Listen Timing can be cancelled with this "
+ "command when used without parameters.\n");
+ return -1;
+ }
+
+ return wpa_cli_cmd(ctrl, "P2P_EXT_LISTEN", 0, argc, argv);
+}
+
+#endif /* CONFIG_P2P */
+
+#ifdef CONFIG_WIFI_DISPLAY
+
+static int wpa_cli_cmd_wfd_subelem_set(struct wpa_ctrl *ctrl, int argc,
+ char *argv[])
+{
+ char cmd[100];
+ int res;
+
+ if (argc != 1 && argc != 2) {
+ printf("Invalid WFD_SUBELEM_SET command: needs one or two "
+ "arguments (subelem, hexdump)\n");
+ return -1;
+ }
+
+ res = os_snprintf(cmd, sizeof(cmd), "WFD_SUBELEM_SET %s %s",
+ argv[0], argc > 1 ? argv[1] : "");
+ if (res < 0 || (size_t) res >= sizeof(cmd))
+ return -1;
+ cmd[sizeof(cmd) - 1] = '\0';
+ return wpa_ctrl_command(ctrl, cmd);
+}
+
+
+static int wpa_cli_cmd_wfd_subelem_get(struct wpa_ctrl *ctrl, int argc,
+ char *argv[])
+{
+ char cmd[100];
int res;
if (argc != 1) {
- printf("Invalid ROAM command: needs one argument "
- "(target AP's BSSID)\n");
+ printf("Invalid WFD_SUBELEM_GET command: needs one "
+ "argument (subelem)\n");
return -1;
}
- res = os_snprintf(cmd, sizeof(cmd), "ROAM %s", argv[0]);
- if (res < 0 || (size_t) res >= sizeof(cmd) - 1) {
- printf("Too long ROAM command.\n");
+ res = os_snprintf(cmd, sizeof(cmd), "WFD_SUBELEM_GET %s",
+ argv[0]);
+ if (res < 0 || (size_t) res >= sizeof(cmd))
+ return -1;
+ cmd[sizeof(cmd) - 1] = '\0';
+ return wpa_ctrl_command(ctrl, cmd);
+}
+#endif /* CONFIG_WIFI_DISPLAY */
+
+
+#ifdef CONFIG_INTERWORKING
+static int wpa_cli_cmd_fetch_anqp(struct wpa_ctrl *ctrl, int argc,
+ char *argv[])
+{
+ return wpa_ctrl_command(ctrl, "FETCH_ANQP");
+}
+
+
+static int wpa_cli_cmd_stop_fetch_anqp(struct wpa_ctrl *ctrl, int argc,
+ char *argv[])
+{
+ return wpa_ctrl_command(ctrl, "STOP_FETCH_ANQP");
+}
+
+
+static int wpa_cli_cmd_interworking_select(struct wpa_ctrl *ctrl, int argc,
+ char *argv[])
+{
+ return wpa_cli_cmd(ctrl, "INTERWORKING_SELECT", 0, argc, argv);
+}
+
+
+static int wpa_cli_cmd_interworking_connect(struct wpa_ctrl *ctrl, int argc,
+ char *argv[])
+{
+ return wpa_cli_cmd(ctrl, "INTERWORKING_CONNECT", 1, argc, argv);
+}
+
+
+static int wpa_cli_cmd_anqp_get(struct wpa_ctrl *ctrl, int argc, char *argv[])
+{
+ return wpa_cli_cmd(ctrl, "ANQP_GET", 2, argc, argv);
+}
+
+
+static int wpa_cli_cmd_gas_request(struct wpa_ctrl *ctrl, int argc,
+ char *argv[])
+{
+ return wpa_cli_cmd(ctrl, "GAS_REQUEST", 2, argc, argv);
+}
+
+
+static int wpa_cli_cmd_gas_response_get(struct wpa_ctrl *ctrl, int argc,
+ char *argv[])
+{
+ return wpa_cli_cmd(ctrl, "GAS_RESPONSE_GET", 2, argc, argv);
+}
+#endif /* CONFIG_INTERWORKING */
+
+
+#ifdef CONFIG_HS20
+
+static int wpa_cli_cmd_hs20_anqp_get(struct wpa_ctrl *ctrl, int argc,
+ char *argv[])
+{
+ return wpa_cli_cmd(ctrl, "HS20_ANQP_GET", 2, argc, argv);
+}
+
+
+static int wpa_cli_cmd_get_nai_home_realm_list(struct wpa_ctrl *ctrl, int argc,
+ char *argv[])
+{
+ char cmd[512];
+
+ if (argc == 0) {
+ printf("Command needs one or two arguments (dst mac addr and "
+ "optional home realm)\n");
return -1;
}
+
+ if (write_cmd(cmd, sizeof(cmd), "HS20_GET_NAI_HOME_REALM_LIST",
+ argc, argv) < 0)
+ return -1;
+
return wpa_ctrl_command(ctrl, cmd);
}
+#endif /* CONFIG_HS20 */
+
+
+static int wpa_cli_cmd_sta_autoconnect(struct wpa_ctrl *ctrl, int argc,
+ char *argv[])
+{
+ return wpa_cli_cmd(ctrl, "STA_AUTOCONNECT", 1, argc, argv);
+}
+
+
+static int wpa_cli_cmd_tdls_discover(struct wpa_ctrl *ctrl, int argc,
+ char *argv[])
+{
+ return wpa_cli_cmd(ctrl, "TDLS_DISCOVER", 1, argc, argv);
+}
+
+
+static int wpa_cli_cmd_tdls_setup(struct wpa_ctrl *ctrl, int argc,
+ char *argv[])
+{
+ return wpa_cli_cmd(ctrl, "TDLS_SETUP", 1, argc, argv);
+}
+
+
+static int wpa_cli_cmd_tdls_teardown(struct wpa_ctrl *ctrl, int argc,
+ char *argv[])
+{
+ return wpa_cli_cmd(ctrl, "TDLS_TEARDOWN", 1, argc, argv);
+}
+
+
+static int wpa_cli_cmd_signal_poll(struct wpa_ctrl *ctrl, int argc,
+ char *argv[])
+{
+ return wpa_ctrl_command(ctrl, "SIGNAL_POLL");
+}
+
+
+static int wpa_cli_cmd_pktcnt_poll(struct wpa_ctrl *ctrl, int argc,
+ char *argv[])
+{
+ return wpa_ctrl_command(ctrl, "PKTCNT_POLL");
+}
+
+
+static int wpa_cli_cmd_reauthenticate(struct wpa_ctrl *ctrl, int argc,
+ char *argv[])
+{
+ return wpa_ctrl_command(ctrl, "REAUTHENTICATE");
+}
+
+
+#ifdef CONFIG_AUTOSCAN
+
+static int wpa_cli_cmd_autoscan(struct wpa_ctrl *ctrl, int argc, char *argv[])
+{
+ if (argc == 0)
+ return wpa_ctrl_command(ctrl, "AUTOSCAN ");
+
+ return wpa_cli_cmd(ctrl, "AUTOSCAN", 0, argc, argv);
+}
+
+#endif /* CONFIG_AUTOSCAN */
+
+
+#ifdef CONFIG_WNM
+
+static int wpa_cli_cmd_wnm_sleep(struct wpa_ctrl *ctrl, int argc, char *argv[])
+{
+ return wpa_cli_cmd(ctrl, "WNM_SLEEP", 0, argc, argv);
+}
+
+#endif /* CONFIG_WNM */
+
+
+static int wpa_cli_cmd_raw(struct wpa_ctrl *ctrl, int argc, char *argv[])
+{
+ if (argc == 0)
+ return -1;
+ return wpa_cli_cmd(ctrl, argv[0], 0, argc - 1, &argv[1]);
+}
+
enum wpa_cli_cmd_flags {
cli_cmd_flag_none = 0x00,
@@ -1468,202 +2273,439 @@ enum wpa_cli_cmd_flags {
struct wpa_cli_cmd {
const char *cmd;
int (*handler)(struct wpa_ctrl *ctrl, int argc, char *argv[]);
+ char ** (*completion)(const char *str, int pos);
enum wpa_cli_cmd_flags flags;
const char *usage;
};
static struct wpa_cli_cmd wpa_cli_commands[] = {
- { "status", wpa_cli_cmd_status,
+ { "status", wpa_cli_cmd_status, NULL,
cli_cmd_flag_none,
"[verbose] = get current WPA/EAPOL/EAP status" },
- { "ping", wpa_cli_cmd_ping,
+ { "ifname", wpa_cli_cmd_ifname, NULL,
+ cli_cmd_flag_none,
+ "= get current interface name" },
+ { "ping", wpa_cli_cmd_ping, NULL,
cli_cmd_flag_none,
"= pings wpa_supplicant" },
- { "mib", wpa_cli_cmd_mib,
+ { "relog", wpa_cli_cmd_relog, NULL,
+ cli_cmd_flag_none,
+ "= re-open log-file (allow rolling logs)" },
+ { "note", wpa_cli_cmd_note, NULL,
+ cli_cmd_flag_none,
+ "<text> = add a note to wpa_supplicant debug log" },
+ { "mib", wpa_cli_cmd_mib, NULL,
cli_cmd_flag_none,
"= get MIB variables (dot1x, dot11)" },
- { "help", wpa_cli_cmd_help,
+ { "help", wpa_cli_cmd_help, wpa_cli_complete_help,
cli_cmd_flag_none,
- "= show this usage help" },
- { "interface", wpa_cli_cmd_interface,
+ "[command] = show usage help" },
+ { "interface", wpa_cli_cmd_interface, NULL,
cli_cmd_flag_none,
"[ifname] = show interfaces/select interface" },
- { "level", wpa_cli_cmd_level,
+ { "level", wpa_cli_cmd_level, NULL,
cli_cmd_flag_none,
"<debug level> = change debug level" },
- { "license", wpa_cli_cmd_license,
+ { "license", wpa_cli_cmd_license, NULL,
cli_cmd_flag_none,
"= show full wpa_cli license" },
- { "quit", wpa_cli_cmd_quit,
+ { "quit", wpa_cli_cmd_quit, NULL,
cli_cmd_flag_none,
"= exit wpa_cli" },
- { "set", wpa_cli_cmd_set,
+ { "set", wpa_cli_cmd_set, NULL,
cli_cmd_flag_none,
"= set variables (shows list of variables when run without "
"arguments)" },
- { "logon", wpa_cli_cmd_logon,
+ { "get", wpa_cli_cmd_get, NULL,
+ cli_cmd_flag_none,
+ "<name> = get information" },
+ { "logon", wpa_cli_cmd_logon, NULL,
cli_cmd_flag_none,
"= IEEE 802.1X EAPOL state machine logon" },
- { "logoff", wpa_cli_cmd_logoff,
+ { "logoff", wpa_cli_cmd_logoff, NULL,
cli_cmd_flag_none,
"= IEEE 802.1X EAPOL state machine logoff" },
- { "pmksa", wpa_cli_cmd_pmksa,
+ { "pmksa", wpa_cli_cmd_pmksa, NULL,
cli_cmd_flag_none,
"= show PMKSA cache" },
- { "reassociate", wpa_cli_cmd_reassociate,
+ { "reassociate", wpa_cli_cmd_reassociate, NULL,
cli_cmd_flag_none,
"= force reassociation" },
- { "preauthenticate", wpa_cli_cmd_preauthenticate,
+ { "preauthenticate", wpa_cli_cmd_preauthenticate, wpa_cli_complete_bss,
cli_cmd_flag_none,
"<BSSID> = force preauthentication" },
- { "identity", wpa_cli_cmd_identity,
+ { "identity", wpa_cli_cmd_identity, NULL,
cli_cmd_flag_none,
"<network id> <identity> = configure identity for an SSID" },
- { "password", wpa_cli_cmd_password,
+ { "password", wpa_cli_cmd_password, NULL,
cli_cmd_flag_sensitive,
"<network id> <password> = configure password for an SSID" },
- { "new_password", wpa_cli_cmd_new_password,
+ { "new_password", wpa_cli_cmd_new_password, NULL,
cli_cmd_flag_sensitive,
"<network id> <password> = change password for an SSID" },
- { "pin", wpa_cli_cmd_pin,
+ { "pin", wpa_cli_cmd_pin, NULL,
cli_cmd_flag_sensitive,
"<network id> <pin> = configure pin for an SSID" },
- { "otp", wpa_cli_cmd_otp,
+ { "otp", wpa_cli_cmd_otp, NULL,
cli_cmd_flag_sensitive,
"<network id> <password> = configure one-time-password for an SSID"
},
- { "passphrase", wpa_cli_cmd_passphrase,
+ { "passphrase", wpa_cli_cmd_passphrase, NULL,
cli_cmd_flag_sensitive,
"<network id> <passphrase> = configure private key passphrase\n"
" for an SSID" },
- { "bssid", wpa_cli_cmd_bssid,
+ { "bssid", wpa_cli_cmd_bssid, NULL,
cli_cmd_flag_none,
"<network id> <BSSID> = set preferred BSSID for an SSID" },
- { "list_networks", wpa_cli_cmd_list_networks,
+ { "blacklist", wpa_cli_cmd_blacklist, wpa_cli_complete_bss,
+ cli_cmd_flag_none,
+ "<BSSID> = add a BSSID to the blacklist\n"
+ "blacklist clear = clear the blacklist\n"
+ "blacklist = display the blacklist" },
+ { "log_level", wpa_cli_cmd_log_level, NULL,
+ cli_cmd_flag_none,
+ "<level> [<timestamp>] = update the log level/timestamp\n"
+ "log_level = display the current log level and log options" },
+ { "list_networks", wpa_cli_cmd_list_networks, NULL,
cli_cmd_flag_none,
"= list configured networks" },
- { "select_network", wpa_cli_cmd_select_network,
+ { "select_network", wpa_cli_cmd_select_network, NULL,
cli_cmd_flag_none,
"<network id> = select a network (disable others)" },
- { "enable_network", wpa_cli_cmd_enable_network,
+ { "enable_network", wpa_cli_cmd_enable_network, NULL,
cli_cmd_flag_none,
"<network id> = enable a network" },
- { "disable_network", wpa_cli_cmd_disable_network,
+ { "disable_network", wpa_cli_cmd_disable_network, NULL,
cli_cmd_flag_none,
"<network id> = disable a network" },
- { "add_network", wpa_cli_cmd_add_network,
+ { "add_network", wpa_cli_cmd_add_network, NULL,
cli_cmd_flag_none,
"= add a network" },
- { "remove_network", wpa_cli_cmd_remove_network,
+ { "remove_network", wpa_cli_cmd_remove_network, NULL,
cli_cmd_flag_none,
"<network id> = remove a network" },
- { "set_network", wpa_cli_cmd_set_network,
+ { "set_network", wpa_cli_cmd_set_network, NULL,
cli_cmd_flag_sensitive,
"<network id> <variable> <value> = set network variables (shows\n"
" list of variables when run without arguments)" },
- { "get_network", wpa_cli_cmd_get_network,
+ { "get_network", wpa_cli_cmd_get_network, NULL,
cli_cmd_flag_none,
"<network id> <variable> = get network variables" },
- { "save_config", wpa_cli_cmd_save_config,
+ { "list_creds", wpa_cli_cmd_list_creds, NULL,
+ cli_cmd_flag_none,
+ "= list configured credentials" },
+ { "add_cred", wpa_cli_cmd_add_cred, NULL,
+ cli_cmd_flag_none,
+ "= add a credential" },
+ { "remove_cred", wpa_cli_cmd_remove_cred, NULL,
+ cli_cmd_flag_none,
+ "<cred id> = remove a credential" },
+ { "set_cred", wpa_cli_cmd_set_cred, NULL,
+ cli_cmd_flag_sensitive,
+ "<cred id> <variable> <value> = set credential variables" },
+ { "save_config", wpa_cli_cmd_save_config, NULL,
cli_cmd_flag_none,
"= save the current configuration" },
- { "disconnect", wpa_cli_cmd_disconnect,
+ { "disconnect", wpa_cli_cmd_disconnect, NULL,
cli_cmd_flag_none,
"= disconnect and wait for reassociate/reconnect command before\n"
" connecting" },
- { "reconnect", wpa_cli_cmd_reconnect,
+ { "reconnect", wpa_cli_cmd_reconnect, NULL,
cli_cmd_flag_none,
"= like reassociate, but only takes effect if already disconnected"
},
- { "scan", wpa_cli_cmd_scan,
+ { "scan", wpa_cli_cmd_scan, NULL,
cli_cmd_flag_none,
"= request new BSS scan" },
- { "scan_results", wpa_cli_cmd_scan_results,
+ { "scan_results", wpa_cli_cmd_scan_results, NULL,
cli_cmd_flag_none,
"= get latest scan results" },
- { "bss", wpa_cli_cmd_bss,
+ { "bss", wpa_cli_cmd_bss, wpa_cli_complete_bss,
cli_cmd_flag_none,
"<<idx> | <bssid>> = get detailed scan result info" },
- { "get_capability", wpa_cli_cmd_get_capability,
+ { "get_capability", wpa_cli_cmd_get_capability, NULL,
cli_cmd_flag_none,
- "<eap/pairwise/group/key_mgmt/proto/auth_alg> = get capabilies" },
- { "reconfigure", wpa_cli_cmd_reconfigure,
+ "<eap/pairwise/group/key_mgmt/proto/auth_alg/channels> "
+ "= get capabilies" },
+ { "reconfigure", wpa_cli_cmd_reconfigure, NULL,
cli_cmd_flag_none,
"= force wpa_supplicant to re-read its configuration file" },
- { "terminate", wpa_cli_cmd_terminate,
+ { "terminate", wpa_cli_cmd_terminate, NULL,
cli_cmd_flag_none,
"= terminate wpa_supplicant" },
- { "interface_add", wpa_cli_cmd_interface_add,
+ { "interface_add", wpa_cli_cmd_interface_add, NULL,
cli_cmd_flag_none,
"<ifname> <confname> <driver> <ctrl_interface> <driver_param>\n"
" <bridge_name> = adds new interface, all parameters but <ifname>\n"
" are optional" },
- { "interface_remove", wpa_cli_cmd_interface_remove,
+ { "interface_remove", wpa_cli_cmd_interface_remove, NULL,
cli_cmd_flag_none,
"<ifname> = removes the interface" },
- { "interface_list", wpa_cli_cmd_interface_list,
+ { "interface_list", wpa_cli_cmd_interface_list, NULL,
cli_cmd_flag_none,
"= list available interfaces" },
- { "ap_scan", wpa_cli_cmd_ap_scan,
+ { "ap_scan", wpa_cli_cmd_ap_scan, NULL,
cli_cmd_flag_none,
"<value> = set ap_scan parameter" },
- { "stkstart", wpa_cli_cmd_stkstart,
+ { "scan_interval", wpa_cli_cmd_scan_interval, NULL,
+ cli_cmd_flag_none,
+ "<value> = set scan_interval parameter (in seconds)" },
+ { "bss_expire_age", wpa_cli_cmd_bss_expire_age, NULL,
+ cli_cmd_flag_none,
+ "<value> = set BSS expiration age parameter" },
+ { "bss_expire_count", wpa_cli_cmd_bss_expire_count, NULL,
+ cli_cmd_flag_none,
+ "<value> = set BSS expiration scan count parameter" },
+ { "bss_flush", wpa_cli_cmd_bss_flush, NULL,
+ cli_cmd_flag_none,
+ "<value> = set BSS flush age (0 by default)" },
+ { "stkstart", wpa_cli_cmd_stkstart, NULL,
cli_cmd_flag_none,
"<addr> = request STK negotiation with <addr>" },
- { "ft_ds", wpa_cli_cmd_ft_ds,
+ { "ft_ds", wpa_cli_cmd_ft_ds, wpa_cli_complete_bss,
cli_cmd_flag_none,
"<addr> = request over-the-DS FT with <addr>" },
- { "wps_pbc", wpa_cli_cmd_wps_pbc,
+ { "wps_pbc", wpa_cli_cmd_wps_pbc, wpa_cli_complete_bss,
cli_cmd_flag_none,
"[BSSID] = start Wi-Fi Protected Setup: Push Button Configuration" },
- { "wps_pin", wpa_cli_cmd_wps_pin,
+ { "wps_pin", wpa_cli_cmd_wps_pin, wpa_cli_complete_bss,
cli_cmd_flag_sensitive,
"<BSSID> [PIN] = start WPS PIN method (returns PIN, if not "
"hardcoded)" },
-#ifdef CONFIG_WPS_OOB
- { "wps_oob", wpa_cli_cmd_wps_oob,
+ { "wps_check_pin", wpa_cli_cmd_wps_check_pin, NULL,
+ cli_cmd_flag_sensitive,
+ "<PIN> = verify PIN checksum" },
+ { "wps_cancel", wpa_cli_cmd_wps_cancel, NULL, cli_cmd_flag_none,
+ "Cancels the pending WPS operation" },
+#ifdef CONFIG_WPS_NFC
+ { "wps_nfc", wpa_cli_cmd_wps_nfc, wpa_cli_complete_bss,
+ cli_cmd_flag_none,
+ "[BSSID] = start Wi-Fi Protected Setup: NFC" },
+ { "wps_nfc_token", wpa_cli_cmd_wps_nfc_token, NULL,
+ cli_cmd_flag_none,
+ "<WPS|NDEF> = create password token" },
+ { "wps_nfc_tag_read", wpa_cli_cmd_wps_nfc_tag_read, NULL,
cli_cmd_flag_sensitive,
- "<DEV_TYPE> <PATH> <METHOD> [DEV_NAME] = start WPS OOB" },
-#endif /* CONFIG_WPS_OOB */
- { "wps_reg", wpa_cli_cmd_wps_reg,
+ "<hexdump of payload> = report read NFC tag with WPS data" },
+ { "nfc_get_handover_req", wpa_cli_cmd_nfc_get_handover_req, NULL,
+ cli_cmd_flag_none,
+ "<NDEF> <WPS> = create NFC handover request" },
+ { "nfc_get_handover_sel", wpa_cli_cmd_nfc_get_handover_sel, NULL,
+ cli_cmd_flag_none,
+ "<NDEF> <WPS> = create NFC handover select" },
+ { "nfc_rx_handover_req", wpa_cli_cmd_nfc_rx_handover_req, NULL,
+ cli_cmd_flag_none,
+ "<hexdump of payload> = report received NFC handover request" },
+ { "nfc_rx_handover_sel", wpa_cli_cmd_nfc_rx_handover_sel, NULL,
+ cli_cmd_flag_none,
+ "<hexdump of payload> = report received NFC handover select" },
+#endif /* CONFIG_WPS_NFC */
+ { "wps_reg", wpa_cli_cmd_wps_reg, wpa_cli_complete_bss,
cli_cmd_flag_sensitive,
"<BSSID> <AP PIN> = start WPS Registrar to configure an AP" },
- { "wps_er_start", wpa_cli_cmd_wps_er_start,
+ { "wps_ap_pin", wpa_cli_cmd_wps_ap_pin, NULL,
+ cli_cmd_flag_sensitive,
+ "[params..] = enable/disable AP PIN" },
+ { "wps_er_start", wpa_cli_cmd_wps_er_start, NULL,
cli_cmd_flag_none,
- "= start Wi-Fi Protected Setup External Registrar" },
- { "wps_er_stop", wpa_cli_cmd_wps_er_stop,
+ "[IP address] = start Wi-Fi Protected Setup External Registrar" },
+ { "wps_er_stop", wpa_cli_cmd_wps_er_stop, NULL,
cli_cmd_flag_none,
"= stop Wi-Fi Protected Setup External Registrar" },
- { "wps_er_pin", wpa_cli_cmd_wps_er_pin,
+ { "wps_er_pin", wpa_cli_cmd_wps_er_pin, NULL,
cli_cmd_flag_sensitive,
"<UUID> <PIN> = add an Enrollee PIN to External Registrar" },
- { "wps_er_pbc", wpa_cli_cmd_wps_er_pbc,
+ { "wps_er_pbc", wpa_cli_cmd_wps_er_pbc, NULL,
cli_cmd_flag_none,
"<UUID> = accept an Enrollee PBC using External Registrar" },
- { "wps_er_learn", wpa_cli_cmd_wps_er_learn,
+ { "wps_er_learn", wpa_cli_cmd_wps_er_learn, NULL,
cli_cmd_flag_sensitive,
"<UUID> <PIN> = learn AP configuration" },
- { "ibss_rsn", wpa_cli_cmd_ibss_rsn,
+ { "wps_er_set_config", wpa_cli_cmd_wps_er_set_config, NULL,
+ cli_cmd_flag_none,
+ "<UUID> <network id> = set AP configuration for enrolling" },
+ { "wps_er_config", wpa_cli_cmd_wps_er_config, NULL,
+ cli_cmd_flag_sensitive,
+ "<UUID> <PIN> <SSID> <auth> <encr> <key> = configure AP" },
+#ifdef CONFIG_WPS_NFC
+ { "wps_er_nfc_config_token", wpa_cli_cmd_wps_er_nfc_config_token, NULL,
+ cli_cmd_flag_none,
+ "<WPS/NDEF> <UUID> = build NFC configuration token" },
+#endif /* CONFIG_WPS_NFC */
+ { "ibss_rsn", wpa_cli_cmd_ibss_rsn, NULL,
cli_cmd_flag_none,
"<addr> = request RSN authentication with <addr> in IBSS" },
#ifdef CONFIG_AP
- { "sta", wpa_cli_cmd_sta,
+ { "sta", wpa_cli_cmd_sta, NULL,
cli_cmd_flag_none,
"<addr> = get information about an associated station (AP)" },
- { "all_sta", wpa_cli_cmd_all_sta,
+ { "all_sta", wpa_cli_cmd_all_sta, NULL,
cli_cmd_flag_none,
"= get information about all associated stations (AP)" },
+ { "deauthenticate", wpa_cli_cmd_deauthenticate, NULL,
+ cli_cmd_flag_none,
+ "<addr> = deauthenticate a station" },
+ { "disassociate", wpa_cli_cmd_disassociate, NULL,
+ cli_cmd_flag_none,
+ "<addr> = disassociate a station" },
#endif /* CONFIG_AP */
- { "suspend", wpa_cli_cmd_suspend, cli_cmd_flag_none,
+ { "suspend", wpa_cli_cmd_suspend, NULL, cli_cmd_flag_none,
"= notification of suspend/hibernate" },
- { "resume", wpa_cli_cmd_resume, cli_cmd_flag_none,
+ { "resume", wpa_cli_cmd_resume, NULL, cli_cmd_flag_none,
"= notification of resume/thaw" },
- { "drop_sa", wpa_cli_cmd_drop_sa, cli_cmd_flag_none,
+ { "drop_sa", wpa_cli_cmd_drop_sa, NULL, cli_cmd_flag_none,
"= drop SA without deauth/disassoc (test command)" },
- { "roam", wpa_cli_cmd_roam,
+ { "roam", wpa_cli_cmd_roam, wpa_cli_complete_bss,
cli_cmd_flag_none,
"<addr> = roam to the specified BSS" },
- { NULL, NULL, cli_cmd_flag_none, NULL }
+#ifdef CONFIG_P2P
+ { "p2p_find", wpa_cli_cmd_p2p_find, wpa_cli_complete_p2p_find,
+ cli_cmd_flag_none,
+ "[timeout] [type=*] = find P2P Devices for up-to timeout seconds" },
+ { "p2p_stop_find", wpa_cli_cmd_p2p_stop_find, NULL, cli_cmd_flag_none,
+ "= stop P2P Devices search" },
+ { "p2p_connect", wpa_cli_cmd_p2p_connect, wpa_cli_complete_p2p_connect,
+ cli_cmd_flag_none,
+ "<addr> <\"pbc\"|PIN> [ht40] = connect to a P2P Device" },
+ { "p2p_listen", wpa_cli_cmd_p2p_listen, NULL, cli_cmd_flag_none,
+ "[timeout] = listen for P2P Devices for up-to timeout seconds" },
+ { "p2p_group_remove", wpa_cli_cmd_p2p_group_remove,
+ wpa_cli_complete_p2p_group_remove, cli_cmd_flag_none,
+ "<ifname> = remove P2P group interface (terminate group if GO)" },
+ { "p2p_group_add", wpa_cli_cmd_p2p_group_add, NULL, cli_cmd_flag_none,
+ "[ht40] = add a new P2P group (local end as GO)" },
+ { "p2p_prov_disc", wpa_cli_cmd_p2p_prov_disc,
+ wpa_cli_complete_p2p_peer, cli_cmd_flag_none,
+ "<addr> <method> = request provisioning discovery" },
+ { "p2p_get_passphrase", wpa_cli_cmd_p2p_get_passphrase, NULL,
+ cli_cmd_flag_none,
+ "= get the passphrase for a group (GO only)" },
+ { "p2p_serv_disc_req", wpa_cli_cmd_p2p_serv_disc_req,
+ wpa_cli_complete_p2p_peer, cli_cmd_flag_none,
+ "<addr> <TLVs> = schedule service discovery request" },
+ { "p2p_serv_disc_cancel_req", wpa_cli_cmd_p2p_serv_disc_cancel_req,
+ NULL, cli_cmd_flag_none,
+ "<id> = cancel pending service discovery request" },
+ { "p2p_serv_disc_resp", wpa_cli_cmd_p2p_serv_disc_resp, NULL,
+ cli_cmd_flag_none,
+ "<freq> <addr> <dialog token> <TLVs> = service discovery response" },
+ { "p2p_service_update", wpa_cli_cmd_p2p_service_update, NULL,
+ cli_cmd_flag_none,
+ "= indicate change in local services" },
+ { "p2p_serv_disc_external", wpa_cli_cmd_p2p_serv_disc_external, NULL,
+ cli_cmd_flag_none,
+ "<external> = set external processing of service discovery" },
+ { "p2p_service_flush", wpa_cli_cmd_p2p_service_flush, NULL,
+ cli_cmd_flag_none,
+ "= remove all stored service entries" },
+ { "p2p_service_add", wpa_cli_cmd_p2p_service_add, NULL,
+ cli_cmd_flag_none,
+ "<bonjour|upnp> <query|version> <response|service> = add a local "
+ "service" },
+ { "p2p_service_del", wpa_cli_cmd_p2p_service_del, NULL,
+ cli_cmd_flag_none,
+ "<bonjour|upnp> <query|version> [|service] = remove a local "
+ "service" },
+ { "p2p_reject", wpa_cli_cmd_p2p_reject, wpa_cli_complete_p2p_peer,
+ cli_cmd_flag_none,
+ "<addr> = reject connection attempts from a specific peer" },
+ { "p2p_invite", wpa_cli_cmd_p2p_invite, NULL,
+ cli_cmd_flag_none,
+ "<cmd> [peer=addr] = invite peer" },
+ { "p2p_peers", wpa_cli_cmd_p2p_peers, NULL, cli_cmd_flag_none,
+ "[discovered] = list known (optionally, only fully discovered) P2P "
+ "peers" },
+ { "p2p_peer", wpa_cli_cmd_p2p_peer, wpa_cli_complete_p2p_peer,
+ cli_cmd_flag_none,
+ "<address> = show information about known P2P peer" },
+ { "p2p_set", wpa_cli_cmd_p2p_set, NULL, cli_cmd_flag_none,
+ "<field> <value> = set a P2P parameter" },
+ { "p2p_flush", wpa_cli_cmd_p2p_flush, NULL, cli_cmd_flag_none,
+ "= flush P2P state" },
+ { "p2p_cancel", wpa_cli_cmd_p2p_cancel, NULL, cli_cmd_flag_none,
+ "= cancel P2P group formation" },
+ { "p2p_unauthorize", wpa_cli_cmd_p2p_unauthorize,
+ wpa_cli_complete_p2p_peer, cli_cmd_flag_none,
+ "<address> = unauthorize a peer" },
+ { "p2p_presence_req", wpa_cli_cmd_p2p_presence_req, NULL,
+ cli_cmd_flag_none,
+ "[<duration> <interval>] [<duration> <interval>] = request GO "
+ "presence" },
+ { "p2p_ext_listen", wpa_cli_cmd_p2p_ext_listen, NULL,
+ cli_cmd_flag_none,
+ "[<period> <interval>] = set extended listen timing" },
+#endif /* CONFIG_P2P */
+#ifdef CONFIG_WIFI_DISPLAY
+ { "wfd_subelem_set", wpa_cli_cmd_wfd_subelem_set, NULL,
+ cli_cmd_flag_none,
+ "<subelem> [contents] = set Wi-Fi Display subelement" },
+ { "wfd_subelem_get", wpa_cli_cmd_wfd_subelem_get, NULL,
+ cli_cmd_flag_none,
+ "<subelem> = get Wi-Fi Display subelement" },
+#endif /* CONFIG_WIFI_DISPLAY */
+#ifdef CONFIG_INTERWORKING
+ { "fetch_anqp", wpa_cli_cmd_fetch_anqp, NULL, cli_cmd_flag_none,
+ "= fetch ANQP information for all APs" },
+ { "stop_fetch_anqp", wpa_cli_cmd_stop_fetch_anqp, NULL,
+ cli_cmd_flag_none,
+ "= stop fetch_anqp operation" },
+ { "interworking_select", wpa_cli_cmd_interworking_select, NULL,
+ cli_cmd_flag_none,
+ "[auto] = perform Interworking network selection" },
+ { "interworking_connect", wpa_cli_cmd_interworking_connect,
+ wpa_cli_complete_bss, cli_cmd_flag_none,
+ "<BSSID> = connect using Interworking credentials" },
+ { "anqp_get", wpa_cli_cmd_anqp_get, wpa_cli_complete_bss,
+ cli_cmd_flag_none,
+ "<addr> <info id>[,<info id>]... = request ANQP information" },
+ { "gas_request", wpa_cli_cmd_gas_request, wpa_cli_complete_bss,
+ cli_cmd_flag_none,
+ "<addr> <AdvProtoID> [QueryReq] = GAS request" },
+ { "gas_response_get", wpa_cli_cmd_gas_response_get,
+ wpa_cli_complete_bss, cli_cmd_flag_none,
+ "<addr> <dialog token> [start,len] = Fetch last GAS response" },
+#endif /* CONFIG_INTERWORKING */
+#ifdef CONFIG_HS20
+ { "hs20_anqp_get", wpa_cli_cmd_hs20_anqp_get, wpa_cli_complete_bss,
+ cli_cmd_flag_none,
+ "<addr> <subtype>[,<subtype>]... = request HS 2.0 ANQP information"
+ },
+ { "nai_home_realm_list", wpa_cli_cmd_get_nai_home_realm_list,
+ wpa_cli_complete_bss, cli_cmd_flag_none,
+ "<addr> <home realm> = get HS20 nai home realm list" },
+#endif /* CONFIG_HS20 */
+ { "sta_autoconnect", wpa_cli_cmd_sta_autoconnect, NULL,
+ cli_cmd_flag_none,
+ "<0/1> = disable/enable automatic reconnection" },
+ { "tdls_discover", wpa_cli_cmd_tdls_discover, NULL,
+ cli_cmd_flag_none,
+ "<addr> = request TDLS discovery with <addr>" },
+ { "tdls_setup", wpa_cli_cmd_tdls_setup, NULL,
+ cli_cmd_flag_none,
+ "<addr> = request TDLS setup with <addr>" },
+ { "tdls_teardown", wpa_cli_cmd_tdls_teardown, NULL,
+ cli_cmd_flag_none,
+ "<addr> = tear down TDLS with <addr>" },
+ { "signal_poll", wpa_cli_cmd_signal_poll, NULL,
+ cli_cmd_flag_none,
+ "= get signal parameters" },
+ { "pktcnt_poll", wpa_cli_cmd_pktcnt_poll, NULL,
+ cli_cmd_flag_none,
+ "= get TX/RX packet counters" },
+ { "reauthenticate", wpa_cli_cmd_reauthenticate, NULL,
+ cli_cmd_flag_none,
+ "= trigger IEEE 802.1X/EAPOL reauthentication" },
+#ifdef CONFIG_AUTOSCAN
+ { "autoscan", wpa_cli_cmd_autoscan, NULL, cli_cmd_flag_none,
+ "[params] = Set or unset (if none) autoscan parameters" },
+#endif /* CONFIG_AUTOSCAN */
+#ifdef CONFIG_WNM
+ { "wnm_sleep", wpa_cli_cmd_wnm_sleep, NULL, cli_cmd_flag_none,
+ "<enter/exit> [interval=#] = enter/exit WNM-Sleep mode" },
+#endif /* CONFIG_WNM */
+ { "raw", wpa_cli_cmd_raw, NULL, cli_cmd_flag_sensitive,
+ "<params..> = Sent unprocessed command" },
+ { NULL, NULL, NULL, cli_cmd_flag_none, NULL }
};
@@ -1685,17 +2727,18 @@ static void print_cmd_help(struct wpa_cli_cmd *cmd, const char *pad)
}
-static void print_help(void)
+static void print_help(const char *cmd)
{
int n;
printf("commands:\n");
- for (n = 0; wpa_cli_commands[n].cmd; n++)
- print_cmd_help(&wpa_cli_commands[n], " ");
+ for (n = 0; wpa_cli_commands[n].cmd; n++) {
+ if (cmd == NULL || str_starts(wpa_cli_commands[n].cmd, cmd))
+ print_cmd_help(&wpa_cli_commands[n], " ");
+ }
}
-#ifdef CONFIG_READLINE
-static int cmd_has_sensitive_data(const char *cmd)
+static int wpa_cli_edit_filter_history_cb(void *ctx, const char *cmd)
{
const char *c, *delim;
int n;
@@ -1714,7 +2757,68 @@ static int cmd_has_sensitive_data(const char *cmd)
}
return 0;
}
-#endif /* CONFIG_READLINE */
+
+
+static char ** wpa_list_cmd_list(void)
+{
+ char **res;
+ int i, count;
+
+ count = sizeof(wpa_cli_commands) / sizeof(wpa_cli_commands[0]);
+ res = os_calloc(count, sizeof(char *));
+ if (res == NULL)
+ return NULL;
+
+ for (i = 0; wpa_cli_commands[i].cmd; i++) {
+ res[i] = os_strdup(wpa_cli_commands[i].cmd);
+ if (res[i] == NULL)
+ break;
+ }
+
+ return res;
+}
+
+
+static char ** wpa_cli_cmd_completion(const char *cmd, const char *str,
+ int pos)
+{
+ int i;
+
+ for (i = 0; wpa_cli_commands[i].cmd; i++) {
+ if (os_strcasecmp(wpa_cli_commands[i].cmd, cmd) == 0) {
+ if (wpa_cli_commands[i].completion)
+ return wpa_cli_commands[i].completion(str,
+ pos);
+ edit_clear_line();
+ printf("\r%s\n", wpa_cli_commands[i].usage);
+ edit_redraw();
+ break;
+ }
+ }
+
+ return NULL;
+}
+
+
+static char ** wpa_cli_edit_completion_cb(void *ctx, const char *str, int pos)
+{
+ char **res;
+ const char *end;
+ char *cmd;
+
+ end = os_strchr(str, ' ');
+ if (end == NULL || str + pos < end)
+ return wpa_list_cmd_list();
+
+ cmd = os_malloc(pos + 1);
+ if (cmd == NULL)
+ return NULL;
+ os_memcpy(cmd, str, pos);
+ cmd[end - str] = '\0';
+ res = wpa_cli_cmd_completion(cmd, str, pos);
+ os_free(cmd);
+ return res;
+}
static int wpa_request(struct wpa_ctrl *ctrl, int argc, char *argv[])
@@ -1852,6 +2956,24 @@ static void wpa_cli_action_process(const char *msg)
wpa_cli_connected = 0;
wpa_cli_exec(action_file, ctrl_ifname, "DISCONNECTED");
}
+ } else if (str_match(pos, P2P_EVENT_GROUP_STARTED)) {
+ wpa_cli_exec(action_file, ctrl_ifname, pos);
+ } else if (str_match(pos, P2P_EVENT_GROUP_REMOVED)) {
+ wpa_cli_exec(action_file, ctrl_ifname, pos);
+ } else if (str_match(pos, P2P_EVENT_CROSS_CONNECT_ENABLE)) {
+ wpa_cli_exec(action_file, ctrl_ifname, pos);
+ } else if (str_match(pos, P2P_EVENT_CROSS_CONNECT_DISABLE)) {
+ wpa_cli_exec(action_file, ctrl_ifname, pos);
+ } else if (str_match(pos, P2P_EVENT_GO_NEG_FAILURE)) {
+ wpa_cli_exec(action_file, ctrl_ifname, pos);
+ } else if (str_match(pos, WPS_EVENT_SUCCESS)) {
+ wpa_cli_exec(action_file, ctrl_ifname, pos);
+ } else if (str_match(pos, WPS_EVENT_FAIL)) {
+ wpa_cli_exec(action_file, ctrl_ifname, pos);
+ } else if (str_match(pos, AP_STA_CONNECTED)) {
+ wpa_cli_exec(action_file, ctrl_ifname, pos);
+ } else if (str_match(pos, AP_STA_DISCONNECTED)) {
+ wpa_cli_exec(action_file, ctrl_ifname, pos);
} else if (str_match(pos, WPA_EVENT_TERMINATING)) {
printf("wpa_supplicant is terminating - stop monitoring\n");
wpa_cli_quit = 1;
@@ -1870,14 +2992,114 @@ static void wpa_cli_action_cb(char *msg, size_t len)
static void wpa_cli_reconnect(void)
{
wpa_cli_close_connection();
- wpa_cli_open_connection(ctrl_ifname, 1);
+ if (wpa_cli_open_connection(ctrl_ifname, 1) < 0)
+ return;
+
+ if (interactive) {
+ edit_clear_line();
+ printf("\rConnection to wpa_supplicant re-established\n");
+ edit_redraw();
+ }
}
-static void wpa_cli_recv_pending(struct wpa_ctrl *ctrl, int in_read,
- int action_monitor)
+static void cli_event(const char *str)
+{
+ const char *start, *s;
+
+ start = os_strchr(str, '>');
+ if (start == NULL)
+ return;
+
+ start++;
+
+ if (str_starts(start, WPA_EVENT_BSS_ADDED)) {
+ s = os_strchr(start, ' ');
+ if (s == NULL)
+ return;
+ s = os_strchr(s + 1, ' ');
+ if (s == NULL)
+ return;
+ cli_txt_list_add(&bsses, s + 1);
+ return;
+ }
+
+ if (str_starts(start, WPA_EVENT_BSS_REMOVED)) {
+ s = os_strchr(start, ' ');
+ if (s == NULL)
+ return;
+ s = os_strchr(s + 1, ' ');
+ if (s == NULL)
+ return;
+ cli_txt_list_del_addr(&bsses, s + 1);
+ return;
+ }
+
+#ifdef CONFIG_P2P
+ if (str_starts(start, P2P_EVENT_DEVICE_FOUND)) {
+ s = os_strstr(start, " p2p_dev_addr=");
+ if (s == NULL)
+ return;
+ cli_txt_list_add_addr(&p2p_peers, s + 14);
+ return;
+ }
+
+ if (str_starts(start, P2P_EVENT_DEVICE_LOST)) {
+ s = os_strstr(start, " p2p_dev_addr=");
+ if (s == NULL)
+ return;
+ cli_txt_list_del_addr(&p2p_peers, s + 14);
+ return;
+ }
+
+ if (str_starts(start, P2P_EVENT_GROUP_STARTED)) {
+ s = os_strchr(start, ' ');
+ if (s == NULL)
+ return;
+ cli_txt_list_add_word(&p2p_groups, s + 1);
+ return;
+ }
+
+ if (str_starts(start, P2P_EVENT_GROUP_REMOVED)) {
+ s = os_strchr(start, ' ');
+ if (s == NULL)
+ return;
+ cli_txt_list_del_word(&p2p_groups, s + 1);
+ return;
+ }
+#endif /* CONFIG_P2P */
+}
+
+
+static int check_terminating(const char *msg)
+{
+ const char *pos = msg;
+
+ if (*pos == '<') {
+ /* skip priority */
+ pos = os_strchr(pos, '>');
+ if (pos)
+ pos++;
+ else
+ pos = msg;
+ }
+
+ if (str_match(pos, WPA_EVENT_TERMINATING) && ctrl_conn) {
+ edit_clear_line();
+ printf("\rConnection to wpa_supplicant lost - trying to "
+ "reconnect\n");
+ edit_redraw();
+ wpa_cli_attached = 0;
+ wpa_cli_close_connection();
+ return 1;
+ }
+
+ return 0;
+}
+
+
+static void wpa_cli_recv_pending(struct wpa_ctrl *ctrl, int action_monitor)
{
- int first = 1;
if (ctrl_conn == NULL) {
wpa_cli_reconnect();
return;
@@ -1890,14 +3112,15 @@ static void wpa_cli_recv_pending(struct wpa_ctrl *ctrl, int in_read,
if (action_monitor)
wpa_cli_action_process(buf);
else {
- if (in_read && first)
- printf("\r");
- first = 0;
- printf("%s\n", buf);
-#ifdef CONFIG_READLINE
- rl_on_new_line();
- rl_redisplay();
-#endif /* CONFIG_READLINE */
+ cli_event(buf);
+ if (wpa_cli_show_event(buf)) {
+ edit_clear_line();
+ printf("\r%s\n", buf);
+ edit_redraw();
+ }
+
+ if (interactive && check_terminating(buf) > 0)
+ return;
}
} else {
printf("Could not read pending message.\n");
@@ -1912,214 +3135,144 @@ static void wpa_cli_recv_pending(struct wpa_ctrl *ctrl, int in_read,
}
}
+#define max_args 10
-#ifdef CONFIG_READLINE
-static char * wpa_cli_cmd_gen(const char *text, int state)
+static int tokenize_cmd(char *cmd, char *argv[])
{
- static int i, len;
- const char *cmd;
-
- if (state == 0) {
- i = 0;
- len = os_strlen(text);
- }
+ char *pos;
+ int argc = 0;
- while ((cmd = wpa_cli_commands[i].cmd)) {
- i++;
- if (os_strncasecmp(cmd, text, len) == 0)
- return strdup(cmd);
+ pos = cmd;
+ for (;;) {
+ while (*pos == ' ')
+ pos++;
+ if (*pos == '\0')
+ break;
+ argv[argc] = pos;
+ argc++;
+ if (argc == max_args)
+ break;
+ if (*pos == '"') {
+ char *pos2 = os_strrchr(pos, '"');
+ if (pos2)
+ pos = pos2 + 1;
+ }
+ while (*pos != '\0' && *pos != ' ')
+ pos++;
+ if (*pos == ' ')
+ *pos++ = '\0';
}
- return NULL;
+ return argc;
}
-static char * wpa_cli_dummy_gen(const char *text, int state)
+static void wpa_cli_ping(void *eloop_ctx, void *timeout_ctx)
{
- int i;
-
- for (i = 0; wpa_cli_commands[i].cmd; i++) {
- const char *cmd = wpa_cli_commands[i].cmd;
- size_t len = os_strlen(cmd);
- if (os_strncasecmp(rl_line_buffer, cmd, len) == 0 &&
- rl_line_buffer[len] == ' ') {
- printf("\n%s\n", wpa_cli_commands[i].usage);
- rl_on_new_line();
- rl_redisplay();
- break;
- }
+ if (ctrl_conn && _wpa_ctrl_command(ctrl_conn, "PING", 0)) {
+ printf("Connection to wpa_supplicant lost - trying to "
+ "reconnect\n");
+ wpa_cli_close_connection();
}
-
- rl_attempted_completion_over = 1;
- return NULL;
+ if (!ctrl_conn)
+ wpa_cli_reconnect();
+ eloop_register_timeout(ping_interval, 0, wpa_cli_ping, NULL, NULL);
}
-static char * wpa_cli_status_gen(const char *text, int state)
+static void wpa_cli_mon_receive(int sock, void *eloop_ctx, void *sock_ctx)
{
- static int i, len;
- char *options[] = {
- "verbose", NULL
- };
- char *t;
-
- if (state == 0) {
- i = 0;
- len = os_strlen(text);
- }
+ wpa_cli_recv_pending(mon_conn, 0);
+}
- while ((t = options[i])) {
- i++;
- if (os_strncasecmp(t, text, len) == 0)
- return strdup(t);
- }
- rl_attempted_completion_over = 1;
- return NULL;
+static void wpa_cli_edit_cmd_cb(void *ctx, char *cmd)
+{
+ char *argv[max_args];
+ int argc;
+ argc = tokenize_cmd(cmd, argv);
+ if (argc)
+ wpa_request(ctrl_conn, argc, argv);
}
-static char ** wpa_cli_completion(const char *text, int start, int end)
+static void wpa_cli_edit_eof_cb(void *ctx)
{
- char * (*func)(const char *text, int state);
-
- if (start == 0)
- func = wpa_cli_cmd_gen;
- else if (os_strncasecmp(rl_line_buffer, "status ", 7) == 0)
- func = wpa_cli_status_gen;
- else
- func = wpa_cli_dummy_gen;
- return rl_completion_matches(text, func);
+ eloop_terminate();
}
-#endif /* CONFIG_READLINE */
-static void wpa_cli_interactive(void)
+static int warning_displayed = 0;
+static char *hfile = NULL;
+static int edit_started = 0;
+
+static void start_edit(void)
{
-#define max_args 10
- char cmdbuf[256], *cmd, *argv[max_args], *pos;
- int argc;
-#ifdef CONFIG_READLINE
- char *home, *hfile = NULL;
-#endif /* CONFIG_READLINE */
+ char *home;
+ char *ps = NULL;
- printf("\nInteractive mode\n\n");
+#ifdef CONFIG_CTRL_IFACE_UDP_REMOTE
+ ps = wpa_ctrl_get_remote_ifname(ctrl_conn);
+#endif /* CONFIG_CTRL_IFACE_UDP_REMOTE */
-#ifdef CONFIG_READLINE
- rl_attempted_completion_function = wpa_cli_completion;
home = getenv("HOME");
if (home) {
const char *fname = ".wpa_cli_history";
int hfile_len = os_strlen(home) + 1 + os_strlen(fname) + 1;
hfile = os_malloc(hfile_len);
- if (hfile) {
- int res;
- res = os_snprintf(hfile, hfile_len, "%s/%s", home,
- fname);
- if (res >= 0 && res < hfile_len) {
- hfile[hfile_len - 1] = '\0';
- read_history(hfile);
- stifle_history(100);
- }
- }
+ if (hfile)
+ os_snprintf(hfile, hfile_len, "%s/%s", home, fname);
}
-#endif /* CONFIG_READLINE */
- do {
- wpa_cli_recv_pending(mon_conn, 0, 0);
-#ifndef CONFIG_NATIVE_WINDOWS
- alarm(ping_interval);
-#endif /* CONFIG_NATIVE_WINDOWS */
-#ifdef CONFIG_WPA_CLI_FORK
- if (mon_pid)
- kill(mon_pid, SIGUSR1);
-#endif /* CONFIG_WPA_CLI_FORK */
-#ifdef CONFIG_READLINE
- cmd = readline("> ");
- if (cmd && *cmd) {
- HIST_ENTRY *h;
- while (next_history())
- ;
- h = previous_history();
- if (h == NULL || os_strcmp(cmd, h->line) != 0)
- add_history(cmd);
- next_history();
- }
-#else /* CONFIG_READLINE */
- printf("> ");
- cmd = fgets(cmdbuf, sizeof(cmdbuf), stdin);
-#endif /* CONFIG_READLINE */
-#ifndef CONFIG_NATIVE_WINDOWS
- alarm(0);
-#endif /* CONFIG_NATIVE_WINDOWS */
- if (cmd == NULL)
- break;
- wpa_cli_recv_pending(mon_conn, 0, 0);
- pos = cmd;
- while (*pos != '\0') {
- if (*pos == '\n') {
- *pos = '\0';
- break;
- }
- pos++;
- }
- argc = 0;
- pos = cmd;
- for (;;) {
- while (*pos == ' ')
- pos++;
- if (*pos == '\0')
- break;
- argv[argc] = pos;
- argc++;
- if (argc == max_args)
- break;
- if (*pos == '"') {
- char *pos2 = os_strrchr(pos, '"');
- if (pos2)
- pos = pos2 + 1;
- }
- while (*pos != '\0' && *pos != ' ')
- pos++;
- if (*pos == ' ')
- *pos++ = '\0';
- }
- if (argc)
- wpa_request(ctrl_conn, argc, argv);
-
- if (cmd != cmdbuf)
- free(cmd);
-#ifdef CONFIG_WPA_CLI_FORK
- if (mon_pid)
- kill(mon_pid, SIGUSR2);
-#endif /* CONFIG_WPA_CLI_FORK */
- } while (!wpa_cli_quit);
-
-#ifdef CONFIG_READLINE
- if (hfile) {
- /* Save command history, excluding lines that may contain
- * passwords. */
- HIST_ENTRY *h;
- history_set_pos(0);
- while ((h = current_history())) {
- char *p = h->line;
- while (*p == ' ' || *p == '\t')
- p++;
- if (cmd_has_sensitive_data(p)) {
- h = remove_history(where_history());
- if (h) {
- os_free(h->line);
- os_free(h->data);
- os_free(h);
- } else
- next_history();
- } else
- next_history();
+ if (edit_init(wpa_cli_edit_cmd_cb, wpa_cli_edit_eof_cb,
+ wpa_cli_edit_completion_cb, NULL, hfile, ps) < 0) {
+ eloop_terminate();
+ return;
+ }
+
+ edit_started = 1;
+ eloop_register_timeout(ping_interval, 0, wpa_cli_ping, NULL, NULL);
+}
+
+
+static void try_connection(void *eloop_ctx, void *timeout_ctx)
+{
+ if (ctrl_ifname == NULL)
+ ctrl_ifname = wpa_cli_get_default_ifname();
+
+ if (!wpa_cli_open_connection(ctrl_ifname, 1) == 0) {
+ if (!warning_displayed) {
+ printf("Could not connect to wpa_supplicant: "
+ "%s - re-trying\n", ctrl_ifname);
+ warning_displayed = 1;
}
- write_history(hfile);
- os_free(hfile);
+ eloop_register_timeout(1, 0, try_connection, NULL, NULL);
+ return;
}
-#endif /* CONFIG_READLINE */
+
+ if (warning_displayed)
+ printf("Connection established.\n");
+
+ start_edit();
+}
+
+
+static void wpa_cli_interactive(void)
+{
+ printf("\nInteractive mode\n\n");
+
+ eloop_register_timeout(0, 0, try_connection, NULL, NULL);
+ eloop_run();
+ eloop_cancel_timeout(try_connection, NULL, NULL);
+
+ cli_txt_list_flush(&p2p_peers);
+ cli_txt_list_flush(&p2p_groups);
+ cli_txt_list_flush(&bsses);
+ if (edit_started)
+ edit_deinit(hfile, wpa_cli_edit_filter_history_cb);
+ os_free(hfile);
+ eloop_cancel_timeout(wpa_cli_ping, NULL, NULL);
+ wpa_cli_close_connection();
}
@@ -2149,7 +3302,7 @@ static void wpa_cli_action(struct wpa_ctrl *ctrl)
}
if (FD_ISSET(fd, &rfds))
- wpa_cli_recv_pending(ctrl, 0, 1);
+ wpa_cli_recv_pending(ctrl, 1);
else {
/* verify that connection is still working */
len = sizeof(buf) - 1;
@@ -2175,39 +3328,11 @@ static void wpa_cli_cleanup(void)
os_program_deinit();
}
-static void wpa_cli_terminate(int sig)
-{
- wpa_cli_cleanup();
- exit(0);
-}
-
-#ifdef CONFIG_WPA_CLI_FORK
-static void wpa_cli_usr1(int sig)
+static void wpa_cli_terminate(int sig, void *ctx)
{
-#ifdef CONFIG_READLINE
- rl_on_new_line();
- rl_redisplay();
-#endif /* CONFIG_READLINE */
-}
-#endif /* CONFIG_WPA_CLI_FORK */
-
-
-#ifndef CONFIG_NATIVE_WINDOWS
-static void wpa_cli_alarm(int sig)
-{
- if (ctrl_conn && _wpa_ctrl_command(ctrl_conn, "PING", 0)) {
- printf("Connection to wpa_supplicant lost - trying to "
- "reconnect\n");
- wpa_cli_close_connection();
- }
- if (!ctrl_conn)
- wpa_cli_reconnect();
- if (mon_conn)
- wpa_cli_recv_pending(mon_conn, 1, 0);
- alarm(ping_interval);
+ eloop_terminate();
}
-#endif /* CONFIG_NATIVE_WINDOWS */
static char * wpa_cli_get_default_ifname(void)
@@ -2217,8 +3342,17 @@ static char * wpa_cli_get_default_ifname(void)
#ifdef CONFIG_CTRL_IFACE_UNIX
struct dirent *dent;
DIR *dir = opendir(ctrl_iface_dir);
- if (!dir)
+ if (!dir) {
+#ifdef ANDROID
+ char ifprop[PROPERTY_VALUE_MAX];
+ if (property_get("wifi.interface", ifprop, NULL) != 0) {
+ ifname = os_strdup(ifprop);
+ printf("Using interface '%s'\n", ifname);
+ return ifname;
+ }
+#endif /* ANDROID */
return NULL;
+ }
while ((dent = readdir(dir))) {
#ifdef _DIRENT_HAVE_D_TYPE
/*
@@ -2267,7 +3401,6 @@ static char * wpa_cli_get_default_ifname(void)
int main(int argc, char *argv[])
{
- int warning_displayed = 0;
int c;
int daemonize = 0;
int ret = 0;
@@ -2320,6 +3453,9 @@ int main(int argc, char *argv[])
if (interactive)
printf("%s\n\n%s\n\n", wpa_cli_version, wpa_cli_license);
+ if (eloop_init())
+ return -1;
+
if (global) {
#ifdef CONFIG_CTRL_IFACE_NAMED_PIPE
ctrl_conn = wpa_ctrl_open(NULL);
@@ -2327,47 +3463,26 @@ int main(int argc, char *argv[])
ctrl_conn = wpa_ctrl_open(global);
#endif /* CONFIG_CTRL_IFACE_NAMED_PIPE */
if (ctrl_conn == NULL) {
- perror("Failed to connect to wpa_supplicant - "
- "wpa_ctrl_open");
+ fprintf(stderr, "Failed to connect to wpa_supplicant "
+ "global interface: %s error: %s\n",
+ global, strerror(errno));
return -1;
}
}
-#ifndef _WIN32_WCE
- signal(SIGINT, wpa_cli_terminate);
- signal(SIGTERM, wpa_cli_terminate);
-#endif /* _WIN32_WCE */
-#ifndef CONFIG_NATIVE_WINDOWS
- signal(SIGALRM, wpa_cli_alarm);
-#endif /* CONFIG_NATIVE_WINDOWS */
-#ifdef CONFIG_WPA_CLI_FORK
- signal(SIGUSR1, wpa_cli_usr1);
-#endif /* CONFIG_WPA_CLI_FORK */
+ eloop_register_signal_terminate(wpa_cli_terminate, NULL);
if (ctrl_ifname == NULL)
ctrl_ifname = wpa_cli_get_default_ifname();
if (interactive) {
- for (; !global;) {
- if (wpa_cli_open_connection(ctrl_ifname, 1) == 0) {
- if (warning_displayed)
- printf("Connection established.\n");
- break;
- }
-
- if (!warning_displayed) {
- printf("Could not connect to wpa_supplicant - "
- "re-trying\n");
- warning_displayed = 1;
- }
- os_sleep(1, 0);
- continue;
- }
+ wpa_cli_interactive();
} else {
if (!global &&
wpa_cli_open_connection(ctrl_ifname, 0) < 0) {
- perror("Failed to connect to wpa_supplicant - "
- "wpa_ctrl_open");
+ fprintf(stderr, "Failed to connect to non-global "
+ "ctrl_ifname: %s error: %s\n",
+ ctrl_ifname, strerror(errno));
return -1;
}
@@ -2380,19 +3495,19 @@ int main(int argc, char *argv[])
return -1;
}
}
- }
- if (daemonize && os_daemonize(pid_file))
- return -1;
+ if (daemonize && os_daemonize(pid_file))
+ return -1;
- if (interactive)
- wpa_cli_interactive();
- else if (action_file)
- wpa_cli_action(ctrl_conn);
- else
- ret = wpa_request(ctrl_conn, argc - optind, &argv[optind]);
+ if (action_file)
+ wpa_cli_action(ctrl_conn);
+ else
+ ret = wpa_request(ctrl_conn, argc - optind,
+ &argv[optind]);
+ }
os_free(ctrl_ifname);
+ eloop_destroy();
wpa_cli_cleanup();
return ret;
diff --git a/contrib/wpa/wpa_supplicant/wpa_passphrase.c b/contrib/wpa/wpa_supplicant/wpa_passphrase.c
index 67465aa..9b568f0 100644
--- a/contrib/wpa/wpa_supplicant/wpa_passphrase.c
+++ b/contrib/wpa/wpa_supplicant/wpa_passphrase.c
@@ -2,14 +2,8 @@
* WPA Supplicant - ASCII passphrase to WPA PSK tool
* Copyright (c) 2003-2005, Jouni Malinen <j@w1.fi>
*
- * This program is free software; you can redistribute it and/or modify
- * it under the terms of the GNU General Public License version 2 as
- * published by the Free Software Foundation.
- *
- * Alternatively, this software may be distributed under the terms of BSD
- * license.
- *
- * See README and COPYING for more details.
+ * This software may be distributed under the terms of the BSD license.
+ * See README for more details.
*/
#include "includes.h"
@@ -58,7 +52,7 @@ int main(int argc, char *argv[])
return 1;
}
- pbkdf2_sha1(passphrase, ssid, os_strlen(ssid), 4096, psk, 32);
+ pbkdf2_sha1(passphrase, (u8 *) ssid, os_strlen(ssid), 4096, psk, 32);
printf("network={\n");
printf("\tssid=\"%s\"\n", ssid);
diff --git a/contrib/wpa/wpa_supplicant/wpa_priv.c b/contrib/wpa/wpa_supplicant/wpa_priv.c
index d2a991b..ad6a080 100644
--- a/contrib/wpa/wpa_supplicant/wpa_priv.c
+++ b/contrib/wpa/wpa_supplicant/wpa_priv.c
@@ -2,14 +2,8 @@
* WPA Supplicant / privileged helper program
* Copyright (c) 2007-2009, Jouni Malinen <j@w1.fi>
*
- * This program is free software; you can redistribute it and/or modify
- * it under the terms of the GNU General Public License version 2 as
- * published by the Free Software Foundation.
- *
- * Alternatively, this software may be distributed under the terms of BSD
- * license.
- *
- * See README and COPYING for more details.
+ * This software may be distributed under the terms of the BSD license.
+ * See README for more details.
*/
#include "includes.h"
@@ -649,7 +643,7 @@ wpa_priv_interface_init(const char *dir, const char *params)
}
if (bind(iface->fd, (struct sockaddr *) &addr,
sizeof(addr)) < 0) {
- perror("bind(PF_UNIX)");
+ perror("wpa-priv-iface-init: bind(PF_UNIX)");
goto fail;
}
wpa_printf(MSG_DEBUG, "Successfully replaced leftover "
@@ -825,7 +819,7 @@ static void wpa_priv_send_ft_response(struct wpa_priv_interface *iface,
}
-void wpa_supplicant_event(void *ctx, wpa_event_type event,
+void wpa_supplicant_event(void *ctx, enum wpa_event_type event,
union wpa_event_data *data)
{
struct wpa_priv_interface *iface = ctx;
@@ -915,7 +909,7 @@ void wpa_supplicant_rx_eapol(void *ctx, const u8 *src_addr,
}
-static void wpa_priv_terminate(int sig, void *eloop_ctx, void *signal_ctx)
+static void wpa_priv_terminate(int sig, void *signal_ctx)
{
wpa_printf(MSG_DEBUG, "wpa_priv termination requested");
eloop_terminate();
diff --git a/contrib/wpa/wpa_supplicant/wpa_supplicant.c b/contrib/wpa/wpa_supplicant/wpa_supplicant.c
index 37a539d..0fb4d0f 100644
--- a/contrib/wpa/wpa_supplicant/wpa_supplicant.c
+++ b/contrib/wpa/wpa_supplicant/wpa_supplicant.c
@@ -1,15 +1,9 @@
/*
* WPA Supplicant
- * Copyright (c) 2003-2010, Jouni Malinen <j@w1.fi>
+ * Copyright (c) 2003-2012, Jouni Malinen <j@w1.fi>
*
- * This program is free software; you can redistribute it and/or modify
- * it under the terms of the GNU General Public License version 2 as
- * published by the Free Software Foundation.
- *
- * Alternatively, this software may be distributed under the terms of BSD
- * license.
- *
- * See README and COPYING for more details.
+ * This software may be distributed under the terms of the BSD license.
+ * See README for more details.
*
* This file implements functions for registering and unregistering
* %wpa_supplicant interfaces. In addition, this file contains number of
@@ -19,12 +13,15 @@
#include "includes.h"
#include "common.h"
+#include "crypto/random.h"
+#include "crypto/sha1.h"
#include "eapol_supp/eapol_supp_sm.h"
#include "eap_peer/eap.h"
#include "eap_server/eap_methods.h"
#include "rsn_supp/wpa.h"
#include "eloop.h"
#include "config.h"
+#include "utils/ext_password.h"
#include "l2_packet/l2_packet.h"
#include "wpa_supplicant_i.h"
#include "driver_i.h"
@@ -34,29 +31,32 @@
#include "rsn_supp/preauth.h"
#include "rsn_supp/pmksa_cache.h"
#include "common/wpa_ctrl.h"
-#include "mlme.h"
#include "common/ieee802_11_defs.h"
+#include "p2p/p2p.h"
#include "blacklist.h"
#include "wpas_glue.h"
#include "wps_supplicant.h"
#include "ibss_rsn.h"
#include "sme.h"
+#include "gas_query.h"
#include "ap.h"
+#include "p2p_supplicant.h"
+#include "wifi_display.h"
#include "notify.h"
#include "bgscan.h"
+#include "autoscan.h"
#include "bss.h"
#include "scan.h"
+#include "offchannel.h"
+#include "hs20_supplicant.h"
const char *wpa_supplicant_version =
"wpa_supplicant v" VERSION_STR "\n"
-"Copyright (c) 2003-2010, Jouni Malinen <j@w1.fi> and contributors";
+"Copyright (c) 2003-2012, Jouni Malinen <j@w1.fi> and contributors";
const char *wpa_supplicant_license =
-"This program is free software. You can distribute it and/or modify it\n"
-"under the terms of the GNU General Public License version 2.\n"
-"\n"
-"Alternatively, this software may be distributed under the terms of the\n"
-"BSD license. See README and COPYING for more details.\n"
+"This software may be distributed under the terms of the BSD license.\n"
+"See README for more details.\n"
#ifdef EAP_TLS_OPENSSL
"\nThis product includes software developed by the OpenSSL Project\n"
"for use in the OpenSSL Toolkit (http://www.openssl.org/)\n"
@@ -66,22 +66,9 @@ const char *wpa_supplicant_license =
#ifndef CONFIG_NO_STDOUT_DEBUG
/* Long text divided into parts in order to fit in C89 strings size limits. */
const char *wpa_supplicant_full_license1 =
-"This program is free software; you can redistribute it and/or modify\n"
-"it under the terms of the GNU General Public License version 2 as\n"
-"published by the Free Software Foundation.\n"
-"\n"
-"This program is distributed in the hope that it will be useful,\n"
-"but WITHOUT ANY WARRANTY; without even the implied warranty of\n"
-"MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the\n"
-"GNU General Public License for more details.\n"
-"\n";
+"";
const char *wpa_supplicant_full_license2 =
-"You should have received a copy of the GNU General Public License\n"
-"along with this program; if not, write to the Free Software\n"
-"Foundation, Inc., 51 Franklin St, Fifth Floor, Boston, MA 02110-1301 USA\n"
-"\n"
-"Alternatively, this software may be distributed under the terms of the\n"
-"BSD license.\n"
+"This software may be distributed under the terms of the BSD license.\n"
"\n"
"Redistribution and use in source and binary forms, with or without\n"
"modification, are permitted provided that the following conditions are\n"
@@ -130,9 +117,8 @@ int wpa_set_wep_keys(struct wpa_supplicant *wpa_s, struct wpa_ssid *ssid)
continue;
set = 1;
- wpa_drv_set_key(wpa_s, WPA_ALG_WEP,
- (u8 *) "\xff\xff\xff\xff\xff\xff",
- i, i == ssid->wep_tx_keyidx, (u8 *) "", 0,
+ wpa_drv_set_key(wpa_s, WPA_ALG_WEP, NULL,
+ i, i == ssid->wep_tx_keyidx, NULL, 0,
ssid->wep_key[i], ssid->wep_key_len[i]);
}
@@ -152,13 +138,14 @@ static int wpa_supplicant_set_wpa_none_key(struct wpa_supplicant *wpa_s,
* sending unicast and multicast packets. */
if (ssid->mode != WPAS_MODE_IBSS) {
- wpa_printf(MSG_INFO, "WPA: Invalid mode %d (not IBSS/ad-hoc) "
- "for WPA-None", ssid->mode);
+ wpa_msg(wpa_s, MSG_INFO, "WPA: Invalid mode %d (not "
+ "IBSS/ad-hoc) for WPA-None", ssid->mode);
return -1;
}
if (!ssid->psk_set) {
- wpa_printf(MSG_INFO, "WPA: No PSK configured for WPA-None");
+ wpa_msg(wpa_s, MSG_INFO, "WPA: No PSK configured for "
+ "WPA-None");
return -1;
}
@@ -168,6 +155,11 @@ static int wpa_supplicant_set_wpa_none_key(struct wpa_supplicant *wpa_s,
keylen = 16;
alg = WPA_ALG_CCMP;
break;
+ case WPA_CIPHER_GCMP:
+ os_memcpy(key, ssid->psk, 16);
+ keylen = 16;
+ alg = WPA_ALG_GCMP;
+ break;
case WPA_CIPHER_TKIP:
/* WPA-None uses the same Michael MIC key for both TX and RX */
os_memcpy(key, ssid->psk, 16 + 8);
@@ -176,16 +168,15 @@ static int wpa_supplicant_set_wpa_none_key(struct wpa_supplicant *wpa_s,
alg = WPA_ALG_TKIP;
break;
default:
- wpa_printf(MSG_INFO, "WPA: Invalid group cipher %d for "
- "WPA-None", wpa_s->group_cipher);
+ wpa_msg(wpa_s, MSG_INFO, "WPA: Invalid group cipher %d for "
+ "WPA-None", wpa_s->group_cipher);
return -1;
}
/* TODO: should actually remember the previously used seq#, both for TX
* and RX from each STA.. */
- return wpa_drv_set_key(wpa_s, alg, (u8 *) "\xff\xff\xff\xff\xff\xff",
- 0, 1, seq, 6, key, keylen);
+ return wpa_drv_set_key(wpa_s, alg, NULL, 0, 1, seq, 6, key, keylen);
}
@@ -199,9 +190,25 @@ static void wpa_supplicant_timeout(void *eloop_ctx, void *timeout_ctx)
MAC2STR(bssid));
wpa_blacklist_add(wpa_s, bssid);
wpa_sm_notify_disassoc(wpa_s->wpa);
- wpa_supplicant_disassociate(wpa_s, WLAN_REASON_DEAUTH_LEAVING);
+ wpa_supplicant_deauthenticate(wpa_s, WLAN_REASON_DEAUTH_LEAVING);
wpa_s->reassociate = 1;
- wpa_supplicant_req_scan(wpa_s, 0, 0);
+
+ /*
+ * If we timed out, the AP or the local radio may be busy.
+ * So, wait a second until scanning again.
+ */
+ wpa_supplicant_req_scan(wpa_s, 1, 0);
+
+#ifdef CONFIG_P2P
+ if (wpa_s->global->p2p_cb_on_scan_complete && !wpa_s->global->p2p_disabled &&
+ wpa_s->global->p2p != NULL) {
+ wpa_s->global->p2p_cb_on_scan_complete = 0;
+ if (p2p_other_scan_completed(wpa_s->global->p2p) == 1) {
+ wpa_dbg(wpa_s, MSG_DEBUG, "P2P: Pending P2P operation "
+ "continued after timed out authentication");
+ }
+ }
+#endif /* CONFIG_P2P */
}
@@ -221,7 +228,7 @@ void wpa_supplicant_req_auth_timeout(struct wpa_supplicant *wpa_s,
(wpa_s->drv_flags & WPA_DRIVER_FLAGS_WIRED))
return;
- wpa_msg(wpa_s, MSG_DEBUG, "Setting authentication timeout: %d sec "
+ wpa_dbg(wpa_s, MSG_DEBUG, "Setting authentication timeout: %d sec "
"%d usec", sec, usec);
eloop_cancel_timeout(wpa_supplicant_timeout, wpa_s, NULL);
eloop_register_timeout(sec, usec, wpa_supplicant_timeout, wpa_s, NULL);
@@ -238,7 +245,7 @@ void wpa_supplicant_req_auth_timeout(struct wpa_supplicant *wpa_s,
*/
void wpa_supplicant_cancel_auth_timeout(struct wpa_supplicant *wpa_s)
{
- wpa_msg(wpa_s, MSG_DEBUG, "Cancelling authentication timeout");
+ wpa_dbg(wpa_s, MSG_DEBUG, "Cancelling authentication timeout");
eloop_cancel_timeout(wpa_supplicant_timeout, wpa_s, NULL);
wpa_blacklist_del(wpa_s, wpa_s->bssid);
}
@@ -361,9 +368,26 @@ void wpa_supplicant_set_non_wpa_policy(struct wpa_supplicant *wpa_s,
}
+void free_hw_features(struct wpa_supplicant *wpa_s)
+{
+ int i;
+ if (wpa_s->hw.modes == NULL)
+ return;
+
+ for (i = 0; i < wpa_s->hw.num_modes; i++) {
+ os_free(wpa_s->hw.modes[i].channels);
+ os_free(wpa_s->hw.modes[i].rates);
+ }
+
+ os_free(wpa_s->hw.modes);
+ wpa_s->hw.modes = NULL;
+}
+
+
static void wpa_supplicant_cleanup(struct wpa_supplicant *wpa_s)
{
bgscan_deinit(wpa_s);
+ autoscan_deinit(wpa_s);
scard_deinit(wpa_s->scard);
wpa_s->scard = NULL;
wpa_sm_set_scard_ctx(wpa_s->wpa, NULL);
@@ -375,16 +399,10 @@ static void wpa_supplicant_cleanup(struct wpa_supplicant *wpa_s)
wpa_s->l2_br = NULL;
}
- if (wpa_s->ctrl_iface) {
- wpa_supplicant_ctrl_iface_deinit(wpa_s->ctrl_iface);
- wpa_s->ctrl_iface = NULL;
- }
if (wpa_s->conf != NULL) {
struct wpa_ssid *ssid;
for (ssid = wpa_s->conf->ssid; ssid; ssid = ssid->next)
wpas_notify_network_removed(wpa_s, ssid);
- wpa_config_free(wpa_s->conf);
- wpa_s->conf = NULL;
}
os_free(wpa_s->confname);
@@ -396,6 +414,10 @@ static void wpa_supplicant_cleanup(struct wpa_supplicant *wpa_s)
rsn_preauth_deinit(wpa_s->wpa);
+#ifdef CONFIG_TDLS
+ wpa_tdls_deinit(wpa_s->wpa);
+#endif /* CONFIG_TDLS */
+
pmksa_candidate_free(wpa_s->wpa);
wpa_sm_deinit(wpa_s->wpa);
wpa_s->wpa = NULL;
@@ -405,8 +427,11 @@ static void wpa_supplicant_cleanup(struct wpa_supplicant *wpa_s)
wpa_supplicant_cancel_scan(wpa_s);
wpa_supplicant_cancel_auth_timeout(wpa_s);
-
- ieee80211_sta_deinit(wpa_s);
+ eloop_cancel_timeout(wpa_supplicant_stop_countermeasures, wpa_s, NULL);
+#ifdef CONFIG_DELAYED_MIC_ERROR_REPORT
+ eloop_cancel_timeout(wpa_supplicant_delayed_mic_error_report,
+ wpa_s, NULL);
+#endif /* CONFIG_DELAYED_MIC_ERROR_REPORT */
wpas_wps_deinit(wpa_s);
@@ -418,15 +443,47 @@ static void wpa_supplicant_cleanup(struct wpa_supplicant *wpa_s)
wpa_s->ibss_rsn = NULL;
#endif /* CONFIG_IBSS_RSN */
-#ifdef CONFIG_SME
- os_free(wpa_s->sme.ft_ies);
- wpa_s->sme.ft_ies = NULL;
- wpa_s->sme.ft_ies_len = 0;
-#endif /* CONFIG_SME */
+ sme_deinit(wpa_s);
#ifdef CONFIG_AP
wpa_supplicant_ap_deinit(wpa_s);
#endif /* CONFIG_AP */
+
+#ifdef CONFIG_P2P
+ wpas_p2p_deinit(wpa_s);
+#endif /* CONFIG_P2P */
+
+#ifdef CONFIG_OFFCHANNEL
+ offchannel_deinit(wpa_s);
+#endif /* CONFIG_OFFCHANNEL */
+
+ wpa_supplicant_cancel_sched_scan(wpa_s);
+
+ os_free(wpa_s->next_scan_freqs);
+ wpa_s->next_scan_freqs = NULL;
+
+ gas_query_deinit(wpa_s->gas);
+ wpa_s->gas = NULL;
+
+ free_hw_features(wpa_s);
+
+ os_free(wpa_s->bssid_filter);
+ wpa_s->bssid_filter = NULL;
+
+ os_free(wpa_s->disallow_aps_bssid);
+ wpa_s->disallow_aps_bssid = NULL;
+ os_free(wpa_s->disallow_aps_ssid);
+ wpa_s->disallow_aps_ssid = NULL;
+
+ wnm_bss_keep_alive_deinit(wpa_s);
+
+ ext_password_deinit(wpa_s->ext_pw);
+ wpa_s->ext_pw = NULL;
+
+ wpabuf_free(wpa_s->last_gas_resp);
+
+ os_free(wpa_s->last_scan_res);
+ wpa_s->last_scan_res = NULL;
}
@@ -440,8 +497,6 @@ static void wpa_supplicant_cleanup(struct wpa_supplicant *wpa_s)
*/
void wpa_clear_keys(struct wpa_supplicant *wpa_s, const u8 *addr)
{
- u8 *bcast = (u8 *) "\xff\xff\xff\xff\xff\xff";
-
if (wpa_s->keys_cleared) {
/* Some drivers (e.g., ndiswrapper & NDIS drivers) seem to have
* timing issues with keys being cleared just before new keys
@@ -450,19 +505,19 @@ void wpa_clear_keys(struct wpa_supplicant *wpa_s, const u8 *addr)
* client not receiving the first encrypted packets correctly.
* Skipping some of the extra key clearing steps seems to help
* in completing group key handshake more reliably. */
- wpa_printf(MSG_DEBUG, "No keys have been configured - "
- "skip key clearing");
+ wpa_dbg(wpa_s, MSG_DEBUG, "No keys have been configured - "
+ "skip key clearing");
return;
}
/* MLME-DELETEKEYS.request */
- wpa_drv_set_key(wpa_s, WPA_ALG_NONE, bcast, 0, 0, NULL, 0, NULL, 0);
- wpa_drv_set_key(wpa_s, WPA_ALG_NONE, bcast, 1, 0, NULL, 0, NULL, 0);
- wpa_drv_set_key(wpa_s, WPA_ALG_NONE, bcast, 2, 0, NULL, 0, NULL, 0);
- wpa_drv_set_key(wpa_s, WPA_ALG_NONE, bcast, 3, 0, NULL, 0, NULL, 0);
+ wpa_drv_set_key(wpa_s, WPA_ALG_NONE, NULL, 0, 0, NULL, 0, NULL, 0);
+ wpa_drv_set_key(wpa_s, WPA_ALG_NONE, NULL, 1, 0, NULL, 0, NULL, 0);
+ wpa_drv_set_key(wpa_s, WPA_ALG_NONE, NULL, 2, 0, NULL, 0, NULL, 0);
+ wpa_drv_set_key(wpa_s, WPA_ALG_NONE, NULL, 3, 0, NULL, 0, NULL, 0);
#ifdef CONFIG_IEEE80211W
- wpa_drv_set_key(wpa_s, WPA_ALG_NONE, bcast, 4, 0, NULL, 0, NULL, 0);
- wpa_drv_set_key(wpa_s, WPA_ALG_NONE, bcast, 5, 0, NULL, 0, NULL, 0);
+ wpa_drv_set_key(wpa_s, WPA_ALG_NONE, NULL, 4, 0, NULL, 0, NULL, 0);
+ wpa_drv_set_key(wpa_s, WPA_ALG_NONE, NULL, 5, 0, NULL, 0, NULL, 0);
#endif /* CONFIG_IEEE80211W */
if (addr) {
wpa_drv_set_key(wpa_s, WPA_ALG_NONE, addr, 0, 0, NULL, 0, NULL,
@@ -489,6 +544,8 @@ const char * wpa_supplicant_state_txt(enum wpa_states state)
return "DISCONNECTED";
case WPA_INACTIVE:
return "INACTIVE";
+ case WPA_INTERFACE_DISABLED:
+ return "INTERFACE_DISABLED";
case WPA_SCANNING:
return "SCANNING";
case WPA_AUTHENTICATING:
@@ -509,6 +566,74 @@ const char * wpa_supplicant_state_txt(enum wpa_states state)
}
+#ifdef CONFIG_BGSCAN
+
+static void wpa_supplicant_start_bgscan(struct wpa_supplicant *wpa_s)
+{
+ if (wpas_driver_bss_selection(wpa_s))
+ return;
+ if (wpa_s->current_ssid == wpa_s->bgscan_ssid)
+ return;
+
+ bgscan_deinit(wpa_s);
+ if (wpa_s->current_ssid && wpa_s->current_ssid->bgscan) {
+ if (bgscan_init(wpa_s, wpa_s->current_ssid)) {
+ wpa_dbg(wpa_s, MSG_DEBUG, "Failed to initialize "
+ "bgscan");
+ /*
+ * Live without bgscan; it is only used as a roaming
+ * optimization, so the initial connection is not
+ * affected.
+ */
+ } else {
+ struct wpa_scan_results *scan_res;
+ wpa_s->bgscan_ssid = wpa_s->current_ssid;
+ scan_res = wpa_supplicant_get_scan_results(wpa_s, NULL,
+ 0);
+ if (scan_res) {
+ bgscan_notify_scan(wpa_s, scan_res);
+ wpa_scan_results_free(scan_res);
+ }
+ }
+ } else
+ wpa_s->bgscan_ssid = NULL;
+}
+
+
+static void wpa_supplicant_stop_bgscan(struct wpa_supplicant *wpa_s)
+{
+ if (wpa_s->bgscan_ssid != NULL) {
+ bgscan_deinit(wpa_s);
+ wpa_s->bgscan_ssid = NULL;
+ }
+}
+
+#endif /* CONFIG_BGSCAN */
+
+
+static void wpa_supplicant_start_autoscan(struct wpa_supplicant *wpa_s)
+{
+ if (autoscan_init(wpa_s, 0))
+ wpa_dbg(wpa_s, MSG_DEBUG, "Failed to initialize autoscan");
+}
+
+
+static void wpa_supplicant_stop_autoscan(struct wpa_supplicant *wpa_s)
+{
+ autoscan_deinit(wpa_s);
+}
+
+
+void wpa_supplicant_reinit_autoscan(struct wpa_supplicant *wpa_s)
+{
+ if (wpa_s->wpa_state == WPA_DISCONNECTED ||
+ wpa_s->wpa_state == WPA_SCANNING) {
+ autoscan_deinit(wpa_s);
+ wpa_supplicant_start_autoscan(wpa_s);
+ }
+}
+
+
/**
* wpa_supplicant_set_state - Set current connection state
* @wpa_s: Pointer to wpa_supplicant data
@@ -522,9 +647,9 @@ void wpa_supplicant_set_state(struct wpa_supplicant *wpa_s,
{
enum wpa_states old_state = wpa_s->wpa_state;
- wpa_printf(MSG_DEBUG, "State: %s -> %s",
- wpa_supplicant_state_txt(wpa_s->wpa_state),
- wpa_supplicant_state_txt(state));
+ wpa_dbg(wpa_s, MSG_DEBUG, "State: %s -> %s",
+ wpa_supplicant_state_txt(wpa_s->wpa_state),
+ wpa_supplicant_state_txt(state));
if (state != WPA_SCANNING)
wpa_supplicant_notify_scanning(wpa_s, 0);
@@ -533,25 +658,55 @@ void wpa_supplicant_set_state(struct wpa_supplicant *wpa_s,
#if defined(CONFIG_CTRL_IFACE) || !defined(CONFIG_NO_STDOUT_DEBUG)
struct wpa_ssid *ssid = wpa_s->current_ssid;
wpa_msg(wpa_s, MSG_INFO, WPA_EVENT_CONNECTED "- Connection to "
- MACSTR " completed %s [id=%d id_str=%s]",
- MAC2STR(wpa_s->bssid), wpa_s->reassociated_connection ?
- "(reauth)" : "(auth)",
+ MACSTR " completed [id=%d id_str=%s]",
+ MAC2STR(wpa_s->bssid),
ssid ? ssid->id : -1,
ssid && ssid->id_str ? ssid->id_str : "");
#endif /* CONFIG_CTRL_IFACE || !CONFIG_NO_STDOUT_DEBUG */
+ wpas_clear_temp_disabled(wpa_s, ssid, 1);
+ wpa_s->extra_blacklist_count = 0;
wpa_s->new_connection = 0;
- wpa_s->reassociated_connection = 1;
wpa_drv_set_operstate(wpa_s, 1);
+#ifndef IEEE8021X_EAPOL
+ wpa_drv_set_supp_port(wpa_s, 1);
+#endif /* IEEE8021X_EAPOL */
wpa_s->after_wps = 0;
+#ifdef CONFIG_P2P
+ wpas_p2p_completed(wpa_s);
+#endif /* CONFIG_P2P */
+
+ sme_sched_obss_scan(wpa_s, 1);
} else if (state == WPA_DISCONNECTED || state == WPA_ASSOCIATING ||
state == WPA_ASSOCIATED) {
wpa_s->new_connection = 1;
wpa_drv_set_operstate(wpa_s, 0);
+#ifndef IEEE8021X_EAPOL
+ wpa_drv_set_supp_port(wpa_s, 0);
+#endif /* IEEE8021X_EAPOL */
+ sme_sched_obss_scan(wpa_s, 0);
}
wpa_s->wpa_state = state;
- if (wpa_s->wpa_state != old_state)
+#ifdef CONFIG_BGSCAN
+ if (state == WPA_COMPLETED)
+ wpa_supplicant_start_bgscan(wpa_s);
+ else
+ wpa_supplicant_stop_bgscan(wpa_s);
+#endif /* CONFIG_BGSCAN */
+
+ if (state == WPA_AUTHENTICATING)
+ wpa_supplicant_stop_autoscan(wpa_s);
+
+ if (state == WPA_DISCONNECTED || state == WPA_INACTIVE)
+ wpa_supplicant_start_autoscan(wpa_s);
+
+ if (wpa_s->wpa_state != old_state) {
wpas_notify_state_changed(wpa_s, wpa_s->wpa_state, old_state);
+
+ if (wpa_s->wpa_state == WPA_COMPLETED ||
+ old_state == WPA_COMPLETED)
+ wpas_notify_auth_changed(wpa_s);
+ }
}
@@ -575,16 +730,11 @@ void wpa_supplicant_terminate_proc(struct wpa_global *global)
static void wpa_supplicant_terminate(int sig, void *signal_ctx)
{
struct wpa_global *global = signal_ctx;
- struct wpa_supplicant *wpa_s;
- for (wpa_s = global->ifaces; wpa_s; wpa_s = wpa_s->next) {
- wpa_msg(wpa_s, MSG_INFO, WPA_EVENT_TERMINATING "- signal %d "
- "received", sig);
- }
wpa_supplicant_terminate_proc(global);
}
-static void wpa_supplicant_clear_status(struct wpa_supplicant *wpa_s)
+void wpa_supplicant_clear_status(struct wpa_supplicant *wpa_s)
{
enum wpa_states old_state = wpa_s->wpa_state;
@@ -592,7 +742,8 @@ static void wpa_supplicant_clear_status(struct wpa_supplicant *wpa_s)
wpa_s->group_cipher = 0;
wpa_s->mgmt_group_cipher = 0;
wpa_s->key_mgmt = 0;
- wpa_s->wpa_state = WPA_DISCONNECTED;
+ if (wpa_s->wpa_state != WPA_INTERFACE_DISABLED)
+ wpa_supplicant_set_state(wpa_s, WPA_DISCONNECTED);
if (wpa_s->wpa_state != old_state)
wpas_notify_state_changed(wpa_s, wpa_s->wpa_state, old_state);
@@ -613,7 +764,6 @@ static void wpa_supplicant_clear_status(struct wpa_supplicant *wpa_s)
int wpa_supplicant_reload_configuration(struct wpa_supplicant *wpa_s)
{
struct wpa_config *conf;
- struct wpa_ssid *old_ssid;
int reconf_ctrl;
int old_ap_scan;
@@ -625,6 +775,7 @@ int wpa_supplicant_reload_configuration(struct wpa_supplicant *wpa_s)
"file '%s' - exiting", wpa_s->confname);
return -1;
}
+ conf->changed_parameters = (unsigned int) -1;
reconf_ctrl = !!conf->ctrl_interface != !!wpa_s->conf->ctrl_interface
|| (conf->ctrl_interface && wpa_s->conf->ctrl_interface &&
@@ -637,10 +788,10 @@ int wpa_supplicant_reload_configuration(struct wpa_supplicant *wpa_s)
}
eapol_sm_invalidate_cached_session(wpa_s->eapol);
- old_ssid = wpa_s->current_ssid;
- wpa_s->current_ssid = NULL;
- if (old_ssid != wpa_s->current_ssid)
- wpas_notify_network_changed(wpa_s);
+ if (wpa_s->current_ssid) {
+ wpa_supplicant_deauthenticate(wpa_s,
+ WLAN_REASON_DEAUTH_LEAVING);
+ }
/*
* TODO: should notify EAPOL SM about changes in opensc_engine_path,
@@ -655,6 +806,7 @@ int wpa_supplicant_reload_configuration(struct wpa_supplicant *wpa_s)
}
eapol_sm_notify_config(wpa_s->eapol, NULL, NULL);
wpa_sm_set_config(wpa_s->wpa, NULL);
+ wpa_sm_pmksa_cache_flush(wpa_s->wpa, NULL);
wpa_sm_set_fast_reauth(wpa_s->wpa, wpa_s->conf->fast_reauth);
rsn_preauth_deinit(wpa_s->wpa);
@@ -667,10 +819,14 @@ int wpa_supplicant_reload_configuration(struct wpa_supplicant *wpa_s)
if (reconf_ctrl)
wpa_s->ctrl_iface = wpa_supplicant_ctrl_iface_init(wpa_s);
+ wpa_supplicant_update_config(wpa_s);
+
wpa_supplicant_clear_status(wpa_s);
- wpa_s->reassociate = 1;
- wpa_supplicant_req_scan(wpa_s, 0, 0);
- wpa_msg(wpa_s, MSG_DEBUG, "Reconfiguration completed");
+ if (wpa_supplicant_enabled_networks(wpa_s)) {
+ wpa_s->reassociate = 1;
+ wpa_supplicant_req_scan(wpa_s, 0, 0);
+ }
+ wpa_dbg(wpa_s, MSG_DEBUG, "Reconfiguration completed");
return 0;
}
@@ -679,8 +835,9 @@ static void wpa_supplicant_reconfig(int sig, void *signal_ctx)
{
struct wpa_global *global = signal_ctx;
struct wpa_supplicant *wpa_s;
- wpa_printf(MSG_DEBUG, "Signal %d received - reconfiguring", sig);
for (wpa_s = global->ifaces; wpa_s; wpa_s = wpa_s->next) {
+ wpa_dbg(wpa_s, MSG_DEBUG, "Signal %d received - reconfiguring",
+ sig);
if (wpa_supplicant_reload_configuration(wpa_s) < 0) {
wpa_supplicant_terminate_proc(global);
}
@@ -699,6 +856,8 @@ enum wpa_cipher cipher_suite2driver(int cipher)
return CIPHER_WEP104;
case WPA_CIPHER_CCMP:
return CIPHER_CCMP;
+ case WPA_CIPHER_GCMP:
+ return CIPHER_GCMP;
case WPA_CIPHER_TKIP:
default:
return CIPHER_TKIP;
@@ -747,8 +906,8 @@ static int wpa_supplicant_suites_from_ai(struct wpa_supplicant *wpa_s,
return -1;
}
- wpa_printf(MSG_DEBUG, "WPA: Using WPA IE from AssocReq to set cipher "
- "suites");
+ wpa_dbg(wpa_s, MSG_DEBUG, "WPA: Using WPA IE from AssocReq to set "
+ "cipher suites");
if (!(ie->group_cipher & ssid->group_cipher)) {
wpa_msg(wpa_s, MSG_INFO, "WPA: Driver used disabled group "
"cipher 0x%x (mask 0x%x) - reject",
@@ -770,7 +929,9 @@ static int wpa_supplicant_suites_from_ai(struct wpa_supplicant *wpa_s,
#ifdef CONFIG_IEEE80211W
if (!(ie->capabilities & WPA_CAPABILITY_MFPC) &&
- ssid->ieee80211w == MGMT_FRAME_PROTECTION_REQUIRED) {
+ (ssid->ieee80211w == MGMT_FRAME_PROTECTION_DEFAULT ?
+ wpa_s->conf->pmf : ssid->ieee80211w) ==
+ MGMT_FRAME_PROTECTION_REQUIRED) {
wpa_msg(wpa_s, MSG_INFO, "WPA: Driver associated with an AP "
"that does not support management frame protection - "
"reject");
@@ -815,14 +976,14 @@ int wpa_supplicant_set_suites(struct wpa_supplicant *wpa_s,
(ie.group_cipher & ssid->group_cipher) &&
(ie.pairwise_cipher & ssid->pairwise_cipher) &&
(ie.key_mgmt & ssid->key_mgmt)) {
- wpa_msg(wpa_s, MSG_DEBUG, "RSN: using IEEE 802.11i/D9.0");
+ wpa_dbg(wpa_s, MSG_DEBUG, "RSN: using IEEE 802.11i/D9.0");
proto = WPA_PROTO_RSN;
} else if (bss_wpa && (ssid->proto & WPA_PROTO_WPA) &&
wpa_parse_wpa_ie(bss_wpa, 2 +bss_wpa[1], &ie) == 0 &&
(ie.group_cipher & ssid->group_cipher) &&
(ie.pairwise_cipher & ssid->pairwise_cipher) &&
(ie.key_mgmt & ssid->key_mgmt)) {
- wpa_msg(wpa_s, MSG_DEBUG, "WPA: using IEEE 802.11i/D3.0");
+ wpa_dbg(wpa_s, MSG_DEBUG, "WPA: using IEEE 802.11i/D3.0");
proto = WPA_PROTO_WPA;
} else if (bss) {
wpa_msg(wpa_s, MSG_WARNING, "WPA: Failed to select WPA/RSN");
@@ -842,22 +1003,23 @@ int wpa_supplicant_set_suites(struct wpa_supplicant *wpa_s,
ssid->ieee80211w != NO_MGMT_FRAME_PROTECTION ?
WPA_CIPHER_AES_128_CMAC : 0;
#endif /* CONFIG_IEEE80211W */
- wpa_printf(MSG_DEBUG, "WPA: Set cipher suites based "
- "on configuration");
+ wpa_dbg(wpa_s, MSG_DEBUG, "WPA: Set cipher suites "
+ "based on configuration");
} else
proto = ie.proto;
}
- wpa_printf(MSG_DEBUG, "WPA: Selected cipher suites: group %d "
- "pairwise %d key_mgmt %d proto %d",
- ie.group_cipher, ie.pairwise_cipher, ie.key_mgmt, proto);
+ wpa_dbg(wpa_s, MSG_DEBUG, "WPA: Selected cipher suites: group %d "
+ "pairwise %d key_mgmt %d proto %d",
+ ie.group_cipher, ie.pairwise_cipher, ie.key_mgmt, proto);
#ifdef CONFIG_IEEE80211W
if (ssid->ieee80211w) {
- wpa_printf(MSG_DEBUG, "WPA: Selected mgmt group cipher %d",
- ie.mgmt_group_cipher);
+ wpa_dbg(wpa_s, MSG_DEBUG, "WPA: Selected mgmt group cipher %d",
+ ie.mgmt_group_cipher);
}
#endif /* CONFIG_IEEE80211W */
+ wpa_s->wpa_proto = proto;
wpa_sm_set_param(wpa_s->wpa, WPA_PARAM_PROTO, proto);
wpa_sm_set_param(wpa_s->wpa, WPA_PARAM_RSN_ENABLED,
!!(ssid->proto & WPA_PROTO_RSN));
@@ -873,69 +1035,88 @@ int wpa_supplicant_set_suites(struct wpa_supplicant *wpa_s,
sel = ie.group_cipher & ssid->group_cipher;
if (sel & WPA_CIPHER_CCMP) {
wpa_s->group_cipher = WPA_CIPHER_CCMP;
- wpa_msg(wpa_s, MSG_DEBUG, "WPA: using GTK CCMP");
+ wpa_dbg(wpa_s, MSG_DEBUG, "WPA: using GTK CCMP");
+ } else if (sel & WPA_CIPHER_GCMP) {
+ wpa_s->group_cipher = WPA_CIPHER_GCMP;
+ wpa_dbg(wpa_s, MSG_DEBUG, "WPA: using GTK GCMP");
} else if (sel & WPA_CIPHER_TKIP) {
wpa_s->group_cipher = WPA_CIPHER_TKIP;
- wpa_msg(wpa_s, MSG_DEBUG, "WPA: using GTK TKIP");
+ wpa_dbg(wpa_s, MSG_DEBUG, "WPA: using GTK TKIP");
} else if (sel & WPA_CIPHER_WEP104) {
wpa_s->group_cipher = WPA_CIPHER_WEP104;
- wpa_msg(wpa_s, MSG_DEBUG, "WPA: using GTK WEP104");
+ wpa_dbg(wpa_s, MSG_DEBUG, "WPA: using GTK WEP104");
} else if (sel & WPA_CIPHER_WEP40) {
wpa_s->group_cipher = WPA_CIPHER_WEP40;
- wpa_msg(wpa_s, MSG_DEBUG, "WPA: using GTK WEP40");
+ wpa_dbg(wpa_s, MSG_DEBUG, "WPA: using GTK WEP40");
} else {
- wpa_printf(MSG_WARNING, "WPA: Failed to select group cipher.");
+ wpa_msg(wpa_s, MSG_WARNING, "WPA: Failed to select group "
+ "cipher");
return -1;
}
sel = ie.pairwise_cipher & ssid->pairwise_cipher;
if (sel & WPA_CIPHER_CCMP) {
wpa_s->pairwise_cipher = WPA_CIPHER_CCMP;
- wpa_msg(wpa_s, MSG_DEBUG, "WPA: using PTK CCMP");
+ wpa_dbg(wpa_s, MSG_DEBUG, "WPA: using PTK CCMP");
+ } else if (sel & WPA_CIPHER_GCMP) {
+ wpa_s->pairwise_cipher = WPA_CIPHER_GCMP;
+ wpa_dbg(wpa_s, MSG_DEBUG, "WPA: using PTK GCMP");
} else if (sel & WPA_CIPHER_TKIP) {
wpa_s->pairwise_cipher = WPA_CIPHER_TKIP;
- wpa_msg(wpa_s, MSG_DEBUG, "WPA: using PTK TKIP");
+ wpa_dbg(wpa_s, MSG_DEBUG, "WPA: using PTK TKIP");
} else if (sel & WPA_CIPHER_NONE) {
wpa_s->pairwise_cipher = WPA_CIPHER_NONE;
- wpa_msg(wpa_s, MSG_DEBUG, "WPA: using PTK NONE");
+ wpa_dbg(wpa_s, MSG_DEBUG, "WPA: using PTK NONE");
} else {
- wpa_printf(MSG_WARNING, "WPA: Failed to select pairwise "
- "cipher.");
+ wpa_msg(wpa_s, MSG_WARNING, "WPA: Failed to select pairwise "
+ "cipher");
return -1;
}
sel = ie.key_mgmt & ssid->key_mgmt;
+#ifdef CONFIG_SAE
+ if (!(wpa_s->drv_flags & WPA_DRIVER_FLAGS_SAE))
+ sel &= ~(WPA_KEY_MGMT_SAE | WPA_KEY_MGMT_FT_SAE);
+#endif /* CONFIG_SAE */
if (0) {
#ifdef CONFIG_IEEE80211R
} else if (sel & WPA_KEY_MGMT_FT_IEEE8021X) {
wpa_s->key_mgmt = WPA_KEY_MGMT_FT_IEEE8021X;
- wpa_msg(wpa_s, MSG_DEBUG, "WPA: using KEY_MGMT FT/802.1X");
+ wpa_dbg(wpa_s, MSG_DEBUG, "WPA: using KEY_MGMT FT/802.1X");
} else if (sel & WPA_KEY_MGMT_FT_PSK) {
wpa_s->key_mgmt = WPA_KEY_MGMT_FT_PSK;
- wpa_msg(wpa_s, MSG_DEBUG, "WPA: using KEY_MGMT FT/PSK");
+ wpa_dbg(wpa_s, MSG_DEBUG, "WPA: using KEY_MGMT FT/PSK");
#endif /* CONFIG_IEEE80211R */
+#ifdef CONFIG_SAE
+ } else if (sel & WPA_KEY_MGMT_SAE) {
+ wpa_s->key_mgmt = WPA_KEY_MGMT_SAE;
+ wpa_dbg(wpa_s, MSG_DEBUG, "RSN: using KEY_MGMT SAE");
+ } else if (sel & WPA_KEY_MGMT_FT_SAE) {
+ wpa_s->key_mgmt = WPA_KEY_MGMT_FT_SAE;
+ wpa_dbg(wpa_s, MSG_DEBUG, "RSN: using KEY_MGMT FT/SAE");
+#endif /* CONFIG_SAE */
#ifdef CONFIG_IEEE80211W
} else if (sel & WPA_KEY_MGMT_IEEE8021X_SHA256) {
wpa_s->key_mgmt = WPA_KEY_MGMT_IEEE8021X_SHA256;
- wpa_msg(wpa_s, MSG_DEBUG,
+ wpa_dbg(wpa_s, MSG_DEBUG,
"WPA: using KEY_MGMT 802.1X with SHA256");
} else if (sel & WPA_KEY_MGMT_PSK_SHA256) {
wpa_s->key_mgmt = WPA_KEY_MGMT_PSK_SHA256;
- wpa_msg(wpa_s, MSG_DEBUG,
+ wpa_dbg(wpa_s, MSG_DEBUG,
"WPA: using KEY_MGMT PSK with SHA256");
#endif /* CONFIG_IEEE80211W */
} else if (sel & WPA_KEY_MGMT_IEEE8021X) {
wpa_s->key_mgmt = WPA_KEY_MGMT_IEEE8021X;
- wpa_msg(wpa_s, MSG_DEBUG, "WPA: using KEY_MGMT 802.1X");
+ wpa_dbg(wpa_s, MSG_DEBUG, "WPA: using KEY_MGMT 802.1X");
} else if (sel & WPA_KEY_MGMT_PSK) {
wpa_s->key_mgmt = WPA_KEY_MGMT_PSK;
- wpa_msg(wpa_s, MSG_DEBUG, "WPA: using KEY_MGMT WPA-PSK");
+ wpa_dbg(wpa_s, MSG_DEBUG, "WPA: using KEY_MGMT WPA-PSK");
} else if (sel & WPA_KEY_MGMT_WPA_NONE) {
wpa_s->key_mgmt = WPA_KEY_MGMT_WPA_NONE;
- wpa_msg(wpa_s, MSG_DEBUG, "WPA: using KEY_MGMT WPA-NONE");
+ wpa_dbg(wpa_s, MSG_DEBUG, "WPA: using KEY_MGMT WPA-NONE");
} else {
- wpa_printf(MSG_WARNING, "WPA: Failed to select authenticated "
- "key management type.");
+ wpa_msg(wpa_s, MSG_WARNING, "WPA: Failed to select "
+ "authenticated key management type");
return -1;
}
@@ -946,37 +1127,134 @@ int wpa_supplicant_set_suites(struct wpa_supplicant *wpa_s,
#ifdef CONFIG_IEEE80211W
sel = ie.mgmt_group_cipher;
- if (ssid->ieee80211w == NO_MGMT_FRAME_PROTECTION ||
+ if ((ssid->ieee80211w == MGMT_FRAME_PROTECTION_DEFAULT ?
+ wpa_s->conf->pmf : ssid->ieee80211w) == NO_MGMT_FRAME_PROTECTION ||
!(ie.capabilities & WPA_CAPABILITY_MFPC))
sel = 0;
if (sel & WPA_CIPHER_AES_128_CMAC) {
wpa_s->mgmt_group_cipher = WPA_CIPHER_AES_128_CMAC;
- wpa_msg(wpa_s, MSG_DEBUG, "WPA: using MGMT group cipher "
+ wpa_dbg(wpa_s, MSG_DEBUG, "WPA: using MGMT group cipher "
"AES-128-CMAC");
} else {
wpa_s->mgmt_group_cipher = 0;
- wpa_msg(wpa_s, MSG_DEBUG, "WPA: not using MGMT group cipher");
+ wpa_dbg(wpa_s, MSG_DEBUG, "WPA: not using MGMT group cipher");
}
wpa_sm_set_param(wpa_s->wpa, WPA_PARAM_MGMT_GROUP,
wpa_s->mgmt_group_cipher);
- wpa_sm_set_param(wpa_s->wpa, WPA_PARAM_MFP, ssid->ieee80211w);
+ wpa_sm_set_param(wpa_s->wpa, WPA_PARAM_MFP,
+ (ssid->ieee80211w == MGMT_FRAME_PROTECTION_DEFAULT ?
+ wpa_s->conf->pmf : ssid->ieee80211w));
#endif /* CONFIG_IEEE80211W */
if (wpa_sm_set_assoc_wpa_ie_default(wpa_s->wpa, wpa_ie, wpa_ie_len)) {
- wpa_printf(MSG_WARNING, "WPA: Failed to generate WPA IE.");
+ wpa_msg(wpa_s, MSG_WARNING, "WPA: Failed to generate WPA IE");
return -1;
}
- if (ssid->key_mgmt &
- (WPA_KEY_MGMT_PSK | WPA_KEY_MGMT_FT_PSK | WPA_KEY_MGMT_PSK_SHA256))
+ if (wpa_key_mgmt_wpa_psk(ssid->key_mgmt)) {
wpa_sm_set_pmk(wpa_s->wpa, ssid->psk, PMK_LEN);
- else
+#ifndef CONFIG_NO_PBKDF2
+ if (bss && ssid->bssid_set && ssid->ssid_len == 0 &&
+ ssid->passphrase) {
+ u8 psk[PMK_LEN];
+ pbkdf2_sha1(ssid->passphrase, bss->ssid, bss->ssid_len,
+ 4096, psk, PMK_LEN);
+ wpa_hexdump_key(MSG_MSGDUMP, "PSK (from passphrase)",
+ psk, PMK_LEN);
+ wpa_sm_set_pmk(wpa_s->wpa, psk, PMK_LEN);
+ }
+#endif /* CONFIG_NO_PBKDF2 */
+#ifdef CONFIG_EXT_PASSWORD
+ if (ssid->ext_psk) {
+ struct wpabuf *pw = ext_password_get(wpa_s->ext_pw,
+ ssid->ext_psk);
+ char pw_str[64 + 1];
+ u8 psk[PMK_LEN];
+
+ if (pw == NULL) {
+ wpa_msg(wpa_s, MSG_INFO, "EXT PW: No PSK "
+ "found from external storage");
+ return -1;
+ }
+
+ if (wpabuf_len(pw) < 8 || wpabuf_len(pw) > 64) {
+ wpa_msg(wpa_s, MSG_INFO, "EXT PW: Unexpected "
+ "PSK length %d in external storage",
+ (int) wpabuf_len(pw));
+ ext_password_free(pw);
+ return -1;
+ }
+
+ os_memcpy(pw_str, wpabuf_head(pw), wpabuf_len(pw));
+ pw_str[wpabuf_len(pw)] = '\0';
+
+#ifndef CONFIG_NO_PBKDF2
+ if (wpabuf_len(pw) >= 8 && wpabuf_len(pw) < 64 && bss)
+ {
+ pbkdf2_sha1(pw_str, bss->ssid, bss->ssid_len,
+ 4096, psk, PMK_LEN);
+ os_memset(pw_str, 0, sizeof(pw_str));
+ wpa_hexdump_key(MSG_MSGDUMP, "PSK (from "
+ "external passphrase)",
+ psk, PMK_LEN);
+ wpa_sm_set_pmk(wpa_s->wpa, psk, PMK_LEN);
+ } else
+#endif /* CONFIG_NO_PBKDF2 */
+ if (wpabuf_len(pw) == 2 * PMK_LEN) {
+ if (hexstr2bin(pw_str, psk, PMK_LEN) < 0) {
+ wpa_msg(wpa_s, MSG_INFO, "EXT PW: "
+ "Invalid PSK hex string");
+ os_memset(pw_str, 0, sizeof(pw_str));
+ ext_password_free(pw);
+ return -1;
+ }
+ wpa_sm_set_pmk(wpa_s->wpa, psk, PMK_LEN);
+ } else {
+ wpa_msg(wpa_s, MSG_INFO, "EXT PW: No suitable "
+ "PSK available");
+ os_memset(pw_str, 0, sizeof(pw_str));
+ ext_password_free(pw);
+ return -1;
+ }
+
+ os_memset(pw_str, 0, sizeof(pw_str));
+ ext_password_free(pw);
+ }
+#endif /* CONFIG_EXT_PASSWORD */
+ } else
wpa_sm_set_pmk_from_pmksa(wpa_s->wpa);
return 0;
}
+int wpas_build_ext_capab(struct wpa_supplicant *wpa_s, u8 *buf)
+{
+ u32 ext_capab = 0;
+ u8 *pos = buf;
+
+#ifdef CONFIG_INTERWORKING
+ if (wpa_s->conf->interworking)
+ ext_capab |= BIT(31); /* Interworking */
+#endif /* CONFIG_INTERWORKING */
+
+#ifdef CONFIG_WNM
+ ext_capab |= BIT(17); /* WNM-Sleep Mode */
+ ext_capab |= BIT(19); /* BSS Transition */
+#endif /* CONFIG_WNM */
+
+ if (!ext_capab)
+ return 0;
+
+ *pos++ = WLAN_EID_EXT_CAPAB;
+ *pos++ = 4;
+ WPA_PUT_LE32(pos, ext_capab);
+ pos += 4;
+
+ return pos - buf;
+}
+
+
/**
* wpa_supplicant_associate - Request association
* @wpa_s: Pointer to wpa_supplicant data
@@ -988,7 +1266,7 @@ int wpa_supplicant_set_suites(struct wpa_supplicant *wpa_s,
void wpa_supplicant_associate(struct wpa_supplicant *wpa_s,
struct wpa_bss *bss, struct wpa_ssid *ssid)
{
- u8 wpa_ie[80];
+ u8 wpa_ie[200];
size_t wpa_ie_len;
int use_crypt, ret, i, bssid_changed;
int algs = WPA_AUTH_ALG_OPEN;
@@ -998,31 +1276,53 @@ void wpa_supplicant_associate(struct wpa_supplicant *wpa_s,
struct wpa_driver_capa capa;
int assoc_failed = 0;
struct wpa_ssid *old_ssid;
+ u8 ext_capab[10];
+ int ext_capab_len;
+#ifdef CONFIG_HT_OVERRIDES
+ struct ieee80211_ht_capabilities htcaps;
+ struct ieee80211_ht_capabilities htcaps_mask;
+#endif /* CONFIG_HT_OVERRIDES */
+
+#ifdef CONFIG_IBSS_RSN
+ ibss_rsn_deinit(wpa_s->ibss_rsn);
+ wpa_s->ibss_rsn = NULL;
+#endif /* CONFIG_IBSS_RSN */
- if (ssid->mode == WPAS_MODE_AP) {
+ if (ssid->mode == WPAS_MODE_AP || ssid->mode == WPAS_MODE_P2P_GO ||
+ ssid->mode == WPAS_MODE_P2P_GROUP_FORMATION) {
#ifdef CONFIG_AP
if (!(wpa_s->drv_flags & WPA_DRIVER_FLAGS_AP)) {
- wpa_printf(MSG_INFO, "Driver does not support AP "
- "mode");
+ wpa_msg(wpa_s, MSG_INFO, "Driver does not support AP "
+ "mode");
+ return;
+ }
+ if (wpa_supplicant_create_ap(wpa_s, ssid) < 0) {
+ wpa_supplicant_set_state(wpa_s, WPA_DISCONNECTED);
return;
}
- wpa_supplicant_create_ap(wpa_s, ssid);
wpa_s->current_bss = bss;
#else /* CONFIG_AP */
- wpa_printf(MSG_ERROR, "AP mode support not included in the "
- "build");
+ wpa_msg(wpa_s, MSG_ERROR, "AP mode support not included in "
+ "the build");
#endif /* CONFIG_AP */
return;
}
+#ifdef CONFIG_TDLS
+ if (bss)
+ wpa_tdls_ap_ies(wpa_s->wpa, (const u8 *) (bss + 1),
+ bss->ie_len);
+#endif /* CONFIG_TDLS */
+
if ((wpa_s->drv_flags & WPA_DRIVER_FLAGS_SME) &&
ssid->mode == IEEE80211_MODE_INFRA) {
sme_authenticate(wpa_s, bss, ssid);
return;
}
+ os_memset(&params, 0, sizeof(params));
wpa_s->reassociate = 0;
- if (bss) {
+ if (bss && !wpas_driver_bss_selection(wpa_s)) {
#ifdef CONFIG_IEEE80211R
const u8 *ie, *md = NULL;
#endif /* CONFIG_IEEE80211R */
@@ -1050,7 +1350,7 @@ void wpa_supplicant_associate(struct wpa_supplicant *wpa_s,
(ssid->key_mgmt & WPA_KEY_MGMT_WPS)) {
/* Use ap_scan==1 style network selection to find the network
*/
- wpa_s->scan_req = 2;
+ wpa_s->scan_req = MANUAL_SCAN_REQ;
wpa_s->reassociate = 1;
wpa_supplicant_req_scan(wpa_s, 0, 0);
return;
@@ -1060,6 +1360,7 @@ void wpa_supplicant_associate(struct wpa_supplicant *wpa_s,
wpa_ssid_txt(ssid->ssid, ssid->ssid_len));
os_memset(wpa_s->pending_bssid, 0, ETH_ALEN);
}
+ wpa_supplicant_cancel_sched_scan(wpa_s);
wpa_supplicant_cancel_scan(wpa_s);
/* Starting new association, so clear the possibly used WPA IE from the
@@ -1076,22 +1377,20 @@ void wpa_supplicant_associate(struct wpa_supplicant *wpa_s,
}
}
#endif /* IEEE8021X_EAPOL */
- wpa_printf(MSG_DEBUG, "Automatic auth_alg selection: 0x%x", algs);
+ wpa_dbg(wpa_s, MSG_DEBUG, "Automatic auth_alg selection: 0x%x", algs);
if (ssid->auth_alg) {
algs = ssid->auth_alg;
- wpa_printf(MSG_DEBUG, "Overriding auth_alg selection: 0x%x",
- algs);
+ wpa_dbg(wpa_s, MSG_DEBUG, "Overriding auth_alg selection: "
+ "0x%x", algs);
}
if (bss && (wpa_bss_get_vendor_ie(bss, WPA_IE_VENDOR_TYPE) ||
wpa_bss_get_ie(bss, WLAN_EID_RSN)) &&
- (ssid->key_mgmt & (WPA_KEY_MGMT_IEEE8021X | WPA_KEY_MGMT_PSK |
- WPA_KEY_MGMT_FT_IEEE8021X |
- WPA_KEY_MGMT_FT_PSK |
- WPA_KEY_MGMT_IEEE8021X_SHA256 |
- WPA_KEY_MGMT_PSK_SHA256))) {
+ wpa_key_mgmt_wpa(ssid->key_mgmt)) {
int try_opportunistic;
- try_opportunistic = ssid->proactive_key_caching &&
+ try_opportunistic = (ssid->proactive_key_caching < 0 ?
+ wpa_s->conf->okc :
+ ssid->proactive_key_caching) &&
(ssid->proto & WPA_PROTO_RSN);
if (pmksa_cache_set_current(wpa_s->wpa, NULL, bss->bssid,
wpa_s->current_ssid,
@@ -1100,21 +1399,27 @@ void wpa_supplicant_associate(struct wpa_supplicant *wpa_s,
wpa_ie_len = sizeof(wpa_ie);
if (wpa_supplicant_set_suites(wpa_s, bss, ssid,
wpa_ie, &wpa_ie_len)) {
- wpa_printf(MSG_WARNING, "WPA: Failed to set WPA key "
- "management and encryption suites");
+ wpa_msg(wpa_s, MSG_WARNING, "WPA: Failed to set WPA "
+ "key management and encryption suites");
return;
}
- } else if (ssid->key_mgmt &
- (WPA_KEY_MGMT_PSK | WPA_KEY_MGMT_IEEE8021X |
- WPA_KEY_MGMT_WPA_NONE | WPA_KEY_MGMT_FT_PSK |
- WPA_KEY_MGMT_FT_IEEE8021X | WPA_KEY_MGMT_PSK_SHA256 |
- WPA_KEY_MGMT_IEEE8021X_SHA256)) {
+ } else if ((ssid->key_mgmt & WPA_KEY_MGMT_IEEE8021X_NO_WPA) && bss &&
+ wpa_key_mgmt_wpa_ieee8021x(ssid->key_mgmt)) {
+ /*
+ * Both WPA and non-WPA IEEE 802.1X enabled in configuration -
+ * use non-WPA since the scan results did not indicate that the
+ * AP is using WPA or WPA2.
+ */
+ wpa_supplicant_set_non_wpa_policy(wpa_s, ssid);
+ wpa_ie_len = 0;
+ wpa_s->wpa_proto = 0;
+ } else if (wpa_key_mgmt_wpa_any(ssid->key_mgmt)) {
wpa_ie_len = sizeof(wpa_ie);
if (wpa_supplicant_set_suites(wpa_s, NULL, ssid,
wpa_ie, &wpa_ie_len)) {
- wpa_printf(MSG_WARNING, "WPA: Failed to set WPA key "
- "management and encryption suites (no scan "
- "results)");
+ wpa_msg(wpa_s, MSG_WARNING, "WPA: Failed to set WPA "
+ "key management and encryption suites (no "
+ "scan results)");
return;
}
#ifdef CONFIG_WPS
@@ -1128,10 +1433,70 @@ void wpa_supplicant_associate(struct wpa_supplicant *wpa_s,
wpa_ie_len = 0;
wpabuf_free(wps_ie);
wpa_supplicant_set_non_wpa_policy(wpa_s, ssid);
+ if (!bss || (bss->caps & IEEE80211_CAP_PRIVACY))
+ params.wps = WPS_MODE_PRIVACY;
+ else
+ params.wps = WPS_MODE_OPEN;
+ wpa_s->wpa_proto = 0;
#endif /* CONFIG_WPS */
} else {
wpa_supplicant_set_non_wpa_policy(wpa_s, ssid);
wpa_ie_len = 0;
+ wpa_s->wpa_proto = 0;
+ }
+
+#ifdef CONFIG_P2P
+ if (wpa_s->global->p2p) {
+ u8 *pos;
+ size_t len;
+ int res;
+ pos = wpa_ie + wpa_ie_len;
+ len = sizeof(wpa_ie) - wpa_ie_len;
+ res = wpas_p2p_assoc_req_ie(wpa_s, bss, pos, len,
+ ssid->p2p_group);
+ if (res >= 0)
+ wpa_ie_len += res;
+ }
+
+ wpa_s->cross_connect_disallowed = 0;
+ if (bss) {
+ struct wpabuf *p2p;
+ p2p = wpa_bss_get_vendor_ie_multi(bss, P2P_IE_VENDOR_TYPE);
+ if (p2p) {
+ wpa_s->cross_connect_disallowed =
+ p2p_get_cross_connect_disallowed(p2p);
+ wpabuf_free(p2p);
+ wpa_dbg(wpa_s, MSG_DEBUG, "P2P: WLAN AP %s cross "
+ "connection",
+ wpa_s->cross_connect_disallowed ?
+ "disallows" : "allows");
+ }
+ }
+#endif /* CONFIG_P2P */
+
+#ifdef CONFIG_HS20
+ if (wpa_s->conf->hs20) {
+ struct wpabuf *hs20;
+ hs20 = wpabuf_alloc(20);
+ if (hs20) {
+ wpas_hs20_add_indication(hs20);
+ os_memcpy(wpa_ie + wpa_ie_len, wpabuf_head(hs20),
+ wpabuf_len(hs20));
+ wpa_ie_len += wpabuf_len(hs20);
+ wpabuf_free(hs20);
+ }
+ }
+#endif /* CONFIG_HS20 */
+
+ ext_capab_len = wpas_build_ext_capab(wpa_s, ext_capab);
+ if (ext_capab_len > 0) {
+ u8 *pos = wpa_ie;
+ if (wpa_ie_len > 0 && pos[0] == WLAN_EID_RSN)
+ pos += 2 + pos[1];
+ os_memmove(pos + ext_capab_len, pos,
+ wpa_ie_len - (pos - wpa_ie));
+ wpa_ie_len += ext_capab_len;
+ os_memcpy(pos, ext_capab, ext_capab_len);
}
wpa_clear_keys(wpa_s, bss ? bss->bssid : NULL);
@@ -1172,16 +1537,29 @@ void wpa_supplicant_associate(struct wpa_supplicant *wpa_s,
}
wpa_supplicant_set_state(wpa_s, WPA_ASSOCIATING);
- os_memset(&params, 0, sizeof(params));
if (bss) {
- params.bssid = bss->bssid;
params.ssid = bss->ssid;
params.ssid_len = bss->ssid_len;
- params.freq = bss->freq;
+ if (!wpas_driver_bss_selection(wpa_s) || ssid->bssid_set) {
+ wpa_printf(MSG_DEBUG, "Limit connection to BSSID "
+ MACSTR " freq=%u MHz based on scan results "
+ "(bssid_set=%d)",
+ MAC2STR(bss->bssid), bss->freq,
+ ssid->bssid_set);
+ params.bssid = bss->bssid;
+ params.freq = bss->freq;
+ }
} else {
params.ssid = ssid->ssid;
params.ssid_len = ssid->ssid_len;
}
+
+ if (ssid->mode == WPAS_MODE_IBSS && ssid->bssid_set &&
+ wpa_s->conf->ap_scan == 2) {
+ params.bssid = ssid->bssid;
+ params.fixed_bssid = 1;
+ }
+
if (ssid->mode == WPAS_MODE_IBSS && ssid->frequency > 0 &&
params.freq == 0)
params.freq = ssid->frequency; /* Initial channel for IBSS */
@@ -1190,8 +1568,10 @@ void wpa_supplicant_associate(struct wpa_supplicant *wpa_s,
params.pairwise_suite = cipher_pairwise;
params.group_suite = cipher_group;
params.key_mgmt_suite = key_mgmt2driver(wpa_s->key_mgmt);
+ params.wpa_proto = wpa_s->wpa_proto;
params.auth_alg = algs;
params.mode = ssid->mode;
+ params.bg_scan_period = ssid->bg_scan_period;
for (i = 0; i < NUM_WEP_KEYS; i++) {
if (ssid->wep_key_len[i])
params.wep_key[i] = ssid->wep_key[i];
@@ -1210,28 +1590,53 @@ void wpa_supplicant_associate(struct wpa_supplicant *wpa_s,
params.drop_unencrypted = use_crypt;
#ifdef CONFIG_IEEE80211W
- params.mgmt_frame_protection = ssid->ieee80211w;
- if (ssid->ieee80211w != NO_MGMT_FRAME_PROTECTION && bss) {
+ params.mgmt_frame_protection =
+ ssid->ieee80211w == MGMT_FRAME_PROTECTION_DEFAULT ?
+ wpa_s->conf->pmf : ssid->ieee80211w;
+ if (params.mgmt_frame_protection != NO_MGMT_FRAME_PROTECTION && bss) {
const u8 *rsn = wpa_bss_get_ie(bss, WLAN_EID_RSN);
struct wpa_ie_data ie;
if (rsn && wpa_parse_wpa_ie(rsn, 2 + rsn[1], &ie) == 0 &&
ie.capabilities &
(WPA_CAPABILITY_MFPC | WPA_CAPABILITY_MFPR)) {
- wpa_printf(MSG_DEBUG, "WPA: Selected AP supports MFP: "
- "require MFP");
+ wpa_dbg(wpa_s, MSG_DEBUG, "WPA: Selected AP supports "
+ "MFP: require MFP");
params.mgmt_frame_protection =
MGMT_FRAME_PROTECTION_REQUIRED;
}
}
#endif /* CONFIG_IEEE80211W */
- if (wpa_s->drv_flags & WPA_DRIVER_FLAGS_USER_SPACE_MLME)
- ret = ieee80211_sta_associate(wpa_s, &params);
+ params.p2p = ssid->p2p_group;
+
+ if (wpa_s->parent->set_sta_uapsd)
+ params.uapsd = wpa_s->parent->sta_uapsd;
else
- ret = wpa_drv_associate(wpa_s, &params);
+ params.uapsd = -1;
+
+#ifdef CONFIG_HT_OVERRIDES
+ os_memset(&htcaps, 0, sizeof(htcaps));
+ os_memset(&htcaps_mask, 0, sizeof(htcaps_mask));
+ params.htcaps = (u8 *) &htcaps;
+ params.htcaps_mask = (u8 *) &htcaps_mask;
+ wpa_supplicant_apply_ht_overrides(wpa_s, ssid, &params);
+#endif /* CONFIG_HT_OVERRIDES */
+
+ ret = wpa_drv_associate(wpa_s, &params);
if (ret < 0) {
wpa_msg(wpa_s, MSG_INFO, "Association request to the driver "
"failed");
+ if (wpa_s->drv_flags & WPA_DRIVER_FLAGS_SANE_ERROR_CODES) {
+ /*
+ * The driver is known to mean what is saying, so we
+ * can stop right here; the association will not
+ * succeed.
+ */
+ wpas_connection_failed(wpa_s, wpa_s->pending_bssid);
+ wpa_supplicant_set_state(wpa_s, WPA_DISCONNECTED);
+ os_memset(wpa_s->pending_bssid, 0, ETH_ALEN);
+ return;
+ }
/* try to continue anyway; new association will be tried again
* after timeout */
assoc_failed = 1;
@@ -1249,7 +1654,6 @@ void wpa_supplicant_associate(struct wpa_supplicant *wpa_s,
} else if (ssid->mode == WPAS_MODE_IBSS &&
wpa_s->key_mgmt != WPA_KEY_MGMT_NONE &&
wpa_s->key_mgmt != WPA_KEY_MGMT_WPA_NONE) {
- ibss_rsn_set_psk(wpa_s->ibss_rsn, ssid->psk);
/*
* RSN IBSS authentication is per-STA and we can disable the
* per-BSSID authentication.
@@ -1293,32 +1697,14 @@ void wpa_supplicant_associate(struct wpa_supplicant *wpa_s,
}
-/**
- * wpa_supplicant_disassociate - Disassociate the current connection
- * @wpa_s: Pointer to wpa_supplicant data
- * @reason_code: IEEE 802.11 reason code for the disassociate frame
- *
- * This function is used to request %wpa_supplicant to disassociate with the
- * current AP.
- */
-void wpa_supplicant_disassociate(struct wpa_supplicant *wpa_s,
- int reason_code)
+static void wpa_supplicant_clear_connection(struct wpa_supplicant *wpa_s,
+ const u8 *addr)
{
struct wpa_ssid *old_ssid;
- u8 *addr = NULL;
- if (!is_zero_ether_addr(wpa_s->bssid)) {
- if (wpa_s->drv_flags & WPA_DRIVER_FLAGS_USER_SPACE_MLME)
- ieee80211_sta_disassociate(wpa_s, reason_code);
- else
- wpa_drv_disassociate(wpa_s, wpa_s->bssid, reason_code);
- addr = wpa_s->bssid;
- }
wpa_clear_keys(wpa_s, addr);
- wpa_supplicant_mark_disassoc(wpa_s);
old_ssid = wpa_s->current_ssid;
- wpa_s->current_ssid = NULL;
- wpa_s->current_bss = NULL;
+ wpa_supplicant_mark_disassoc(wpa_s);
wpa_sm_set_config(wpa_s->wpa, NULL);
eapol_sm_notify_config(wpa_s->eapol, NULL, NULL);
if (old_ssid != wpa_s->current_ssid)
@@ -1338,27 +1724,43 @@ void wpa_supplicant_disassociate(struct wpa_supplicant *wpa_s,
void wpa_supplicant_deauthenticate(struct wpa_supplicant *wpa_s,
int reason_code)
{
- struct wpa_ssid *old_ssid;
u8 *addr = NULL;
+ union wpa_event_data event;
+ int zero_addr = 0;
- if (!is_zero_ether_addr(wpa_s->bssid)) {
- if (wpa_s->drv_flags & WPA_DRIVER_FLAGS_USER_SPACE_MLME)
- ieee80211_sta_deauthenticate(wpa_s, reason_code);
- else
- wpa_drv_deauthenticate(wpa_s, wpa_s->bssid,
- reason_code);
+ wpa_dbg(wpa_s, MSG_DEBUG, "Request to deauthenticate - bssid=" MACSTR
+ " pending_bssid=" MACSTR " reason=%d state=%s",
+ MAC2STR(wpa_s->bssid), MAC2STR(wpa_s->pending_bssid),
+ reason_code, wpa_supplicant_state_txt(wpa_s->wpa_state));
+
+ if (!is_zero_ether_addr(wpa_s->bssid))
+ addr = wpa_s->bssid;
+ else if (!is_zero_ether_addr(wpa_s->pending_bssid) &&
+ (wpa_s->wpa_state == WPA_AUTHENTICATING ||
+ wpa_s->wpa_state == WPA_ASSOCIATING))
+ addr = wpa_s->pending_bssid;
+ else if (wpa_s->wpa_state == WPA_ASSOCIATING) {
+ /*
+ * When using driver-based BSS selection, we may not know the
+ * BSSID with which we are currently trying to associate. We
+ * need to notify the driver of this disconnection even in such
+ * a case, so use the all zeros address here.
+ */
addr = wpa_s->bssid;
+ zero_addr = 1;
}
- wpa_clear_keys(wpa_s, addr);
- wpa_supplicant_mark_disassoc(wpa_s);
- old_ssid = wpa_s->current_ssid;
- wpa_s->current_ssid = NULL;
- wpa_s->current_bss = NULL;
- wpa_sm_set_config(wpa_s->wpa, NULL);
- eapol_sm_notify_config(wpa_s->eapol, NULL, NULL);
- if (old_ssid != wpa_s->current_ssid)
- wpas_notify_network_changed(wpa_s);
- eloop_cancel_timeout(wpa_supplicant_timeout, wpa_s, NULL);
+
+ if (addr) {
+ wpa_drv_deauthenticate(wpa_s, addr, reason_code);
+ os_memset(&event, 0, sizeof(event));
+ event.deauth_info.reason_code = (u16) reason_code;
+ event.deauth_info.locally_generated = 1;
+ wpa_supplicant_event(wpa_s, EVENT_DEAUTH, &event);
+ if (zero_addr)
+ addr = NULL;
+ }
+
+ wpa_supplicant_clear_connection(wpa_s, addr);
}
@@ -1376,8 +1778,11 @@ void wpa_supplicant_enable_network(struct wpa_supplicant *wpa_s,
int was_disabled;
if (ssid == NULL) {
- other_ssid = wpa_s->conf->ssid;
- while (other_ssid) {
+ for (other_ssid = wpa_s->conf->ssid; other_ssid;
+ other_ssid = other_ssid->next) {
+ if (other_ssid->disabled == 2)
+ continue; /* do not change persistent P2P group
+ * data */
if (other_ssid == wpa_s->current_ssid &&
other_ssid->disabled)
wpa_s->reassociate = 1;
@@ -1385,16 +1790,16 @@ void wpa_supplicant_enable_network(struct wpa_supplicant *wpa_s,
was_disabled = other_ssid->disabled;
other_ssid->disabled = 0;
+ if (was_disabled)
+ wpas_clear_temp_disabled(wpa_s, other_ssid, 0);
if (was_disabled != other_ssid->disabled)
wpas_notify_network_enabled_changed(
wpa_s, other_ssid);
-
- other_ssid = other_ssid->next;
}
if (wpa_s->reassociate)
wpa_supplicant_req_scan(wpa_s, 0, 0);
- } else if (ssid->disabled) {
+ } else if (ssid->disabled && ssid->disabled != 2) {
if (wpa_s->current_ssid == NULL) {
/*
* Try to reassociate since there is no current
@@ -1407,6 +1812,7 @@ void wpa_supplicant_enable_network(struct wpa_supplicant *wpa_s,
was_disabled = ssid->disabled;
ssid->disabled = 0;
+ wpas_clear_temp_disabled(wpa_s, ssid, 1);
if (was_disabled != ssid->disabled)
wpas_notify_network_enabled_changed(wpa_s, ssid);
@@ -1428,24 +1834,25 @@ void wpa_supplicant_disable_network(struct wpa_supplicant *wpa_s,
int was_disabled;
if (ssid == NULL) {
- other_ssid = wpa_s->conf->ssid;
- while (other_ssid) {
+ for (other_ssid = wpa_s->conf->ssid; other_ssid;
+ other_ssid = other_ssid->next) {
was_disabled = other_ssid->disabled;
+ if (was_disabled == 2)
+ continue; /* do not change persistent P2P group
+ * data */
other_ssid->disabled = 1;
if (was_disabled != other_ssid->disabled)
wpas_notify_network_enabled_changed(
wpa_s, other_ssid);
-
- other_ssid = other_ssid->next;
}
if (wpa_s->current_ssid)
- wpa_supplicant_disassociate(
+ wpa_supplicant_deauthenticate(
wpa_s, WLAN_REASON_DEAUTH_LEAVING);
- } else {
+ } else if (ssid->disabled != 2) {
if (ssid == wpa_s->current_ssid)
- wpa_supplicant_disassociate(
+ wpa_supplicant_deauthenticate(
wpa_s, WLAN_REASON_DEAUTH_LEAVING);
was_disabled = ssid->disabled;
@@ -1468,29 +1875,48 @@ void wpa_supplicant_select_network(struct wpa_supplicant *wpa_s,
{
struct wpa_ssid *other_ssid;
+ int disconnected = 0;
- if (ssid && ssid != wpa_s->current_ssid && wpa_s->current_ssid)
- wpa_supplicant_disassociate(
+ if (ssid && ssid != wpa_s->current_ssid && wpa_s->current_ssid) {
+ wpa_supplicant_deauthenticate(
wpa_s, WLAN_REASON_DEAUTH_LEAVING);
+ disconnected = 1;
+ }
+
+ if (ssid)
+ wpas_clear_temp_disabled(wpa_s, ssid, 1);
/*
* Mark all other networks disabled or mark all networks enabled if no
* network specified.
*/
- other_ssid = wpa_s->conf->ssid;
- while (other_ssid) {
+ for (other_ssid = wpa_s->conf->ssid; other_ssid;
+ other_ssid = other_ssid->next) {
int was_disabled = other_ssid->disabled;
+ if (was_disabled == 2)
+ continue; /* do not change persistent P2P group data */
other_ssid->disabled = ssid ? (ssid->id != other_ssid->id) : 0;
+ if (was_disabled && !other_ssid->disabled)
+ wpas_clear_temp_disabled(wpa_s, other_ssid, 0);
if (was_disabled != other_ssid->disabled)
wpas_notify_network_enabled_changed(wpa_s, other_ssid);
+ }
- other_ssid = other_ssid->next;
+ if (ssid && ssid == wpa_s->current_ssid && wpa_s->current_ssid) {
+ /* We are already associated with the selected network */
+ wpa_printf(MSG_DEBUG, "Already associated with the "
+ "selected network - do nothing");
+ return;
}
+
+ if (ssid)
+ wpa_s->current_ssid = ssid;
+ wpa_s->connect_without_scan = NULL;
wpa_s->disconnected = 0;
wpa_s->reassociate = 1;
- wpa_supplicant_req_scan(wpa_s, 0, 0);
+ wpa_supplicant_req_scan(wpa_s, 0, disconnected ? 100000 : 0);
if (ssid)
wpas_notify_network_selected(wpa_s, ssid);
@@ -1512,6 +1938,16 @@ int wpa_supplicant_set_ap_scan(struct wpa_supplicant *wpa_s, int ap_scan)
if (ap_scan < 0 || ap_scan > 2)
return -1;
+#ifdef ANDROID
+ if (ap_scan == 2 && ap_scan != wpa_s->conf->ap_scan &&
+ wpa_s->wpa_state >= WPA_ASSOCIATING &&
+ wpa_s->wpa_state < WPA_COMPLETED) {
+ wpa_printf(MSG_ERROR, "ap_scan = %d (%d) rejected while "
+ "associating", wpa_s->conf->ap_scan, ap_scan);
+ return 0;
+ }
+#endif /* ANDROID */
+
old_ap_scan = wpa_s->conf->ap_scan;
wpa_s->conf->ap_scan = ap_scan;
@@ -1523,6 +1959,75 @@ int wpa_supplicant_set_ap_scan(struct wpa_supplicant *wpa_s, int ap_scan)
/**
+ * wpa_supplicant_set_bss_expiration_age - Set BSS entry expiration age
+ * @wpa_s: wpa_supplicant structure for a network interface
+ * @expire_age: Expiration age in seconds
+ * Returns: 0 if succeed or -1 if expire_age has an invalid value
+ *
+ */
+int wpa_supplicant_set_bss_expiration_age(struct wpa_supplicant *wpa_s,
+ unsigned int bss_expire_age)
+{
+ if (bss_expire_age < 10) {
+ wpa_msg(wpa_s, MSG_ERROR, "Invalid bss expiration age %u",
+ bss_expire_age);
+ return -1;
+ }
+ wpa_msg(wpa_s, MSG_DEBUG, "Setting bss expiration age: %d sec",
+ bss_expire_age);
+ wpa_s->conf->bss_expiration_age = bss_expire_age;
+
+ return 0;
+}
+
+
+/**
+ * wpa_supplicant_set_bss_expiration_count - Set BSS entry expiration scan count
+ * @wpa_s: wpa_supplicant structure for a network interface
+ * @expire_count: number of scans after which an unseen BSS is reclaimed
+ * Returns: 0 if succeed or -1 if expire_count has an invalid value
+ *
+ */
+int wpa_supplicant_set_bss_expiration_count(struct wpa_supplicant *wpa_s,
+ unsigned int bss_expire_count)
+{
+ if (bss_expire_count < 1) {
+ wpa_msg(wpa_s, MSG_ERROR, "Invalid bss expiration count %u",
+ bss_expire_count);
+ return -1;
+ }
+ wpa_msg(wpa_s, MSG_DEBUG, "Setting bss expiration scan count: %u",
+ bss_expire_count);
+ wpa_s->conf->bss_expiration_scan_count = bss_expire_count;
+
+ return 0;
+}
+
+
+/**
+ * wpa_supplicant_set_scan_interval - Set scan interval
+ * @wpa_s: wpa_supplicant structure for a network interface
+ * @scan_interval: scan interval in seconds
+ * Returns: 0 if succeed or -1 if scan_interval has an invalid value
+ *
+ */
+int wpa_supplicant_set_scan_interval(struct wpa_supplicant *wpa_s,
+ int scan_interval)
+{
+ if (scan_interval < 0) {
+ wpa_msg(wpa_s, MSG_ERROR, "Invalid scan interval %d",
+ scan_interval);
+ return -1;
+ }
+ wpa_msg(wpa_s, MSG_DEBUG, "Setting scan interval: %d sec",
+ scan_interval);
+ wpa_s->scan_interval = scan_interval;
+
+ return 0;
+}
+
+
+/**
* wpa_supplicant_set_debug_params - Set global debug params
* @global: wpa_global structure
* @debug_level: debug level
@@ -1537,7 +2042,8 @@ int wpa_supplicant_set_debug_params(struct wpa_global *global, int debug_level,
int old_level, old_timestamp, old_show_keys;
/* check for allowed debuglevels */
- if (debug_level != MSG_MSGDUMP &&
+ if (debug_level != MSG_EXCESSIVE &&
+ debug_level != MSG_MSGDUMP &&
debug_level != MSG_DEBUG &&
debug_level != MSG_INFO &&
debug_level != MSG_WARNING &&
@@ -1577,26 +2083,17 @@ struct wpa_ssid * wpa_supplicant_get_ssid(struct wpa_supplicant *wpa_s)
u8 bssid[ETH_ALEN];
int wired;
- if (wpa_s->drv_flags & WPA_DRIVER_FLAGS_USER_SPACE_MLME) {
- if (ieee80211_sta_get_ssid(wpa_s, ssid, &ssid_len)) {
- wpa_printf(MSG_WARNING, "Could not read SSID from "
- "MLME.");
- return NULL;
- }
- } else {
- res = wpa_drv_get_ssid(wpa_s, ssid);
- if (res < 0) {
- wpa_printf(MSG_WARNING, "Could not read SSID from "
- "driver.");
- return NULL;
- }
- ssid_len = res;
+ res = wpa_drv_get_ssid(wpa_s, ssid);
+ if (res < 0) {
+ wpa_msg(wpa_s, MSG_WARNING, "Could not read SSID from "
+ "driver");
+ return NULL;
}
+ ssid_len = res;
- if (wpa_s->drv_flags & WPA_DRIVER_FLAGS_USER_SPACE_MLME)
- os_memcpy(bssid, wpa_s->bssid, ETH_ALEN);
- else if (wpa_drv_get_bssid(wpa_s, bssid) < 0) {
- wpa_printf(MSG_WARNING, "Could not read BSSID from driver.");
+ if (wpa_drv_get_bssid(wpa_s, bssid) < 0) {
+ wpa_msg(wpa_s, MSG_WARNING, "Could not read BSSID from "
+ "driver");
return NULL;
}
@@ -1605,20 +2102,26 @@ struct wpa_ssid * wpa_supplicant_get_ssid(struct wpa_supplicant *wpa_s)
entry = wpa_s->conf->ssid;
while (entry) {
- if (!entry->disabled &&
+ if (!wpas_network_disabled(wpa_s, entry) &&
((ssid_len == entry->ssid_len &&
os_memcmp(ssid, entry->ssid, ssid_len) == 0) || wired) &&
(!entry->bssid_set ||
os_memcmp(bssid, entry->bssid, ETH_ALEN) == 0))
return entry;
#ifdef CONFIG_WPS
- if (!entry->disabled &&
+ if (!wpas_network_disabled(wpa_s, entry) &&
(entry->key_mgmt & WPA_KEY_MGMT_WPS) &&
(entry->ssid == NULL || entry->ssid_len == 0) &&
(!entry->bssid_set ||
os_memcmp(bssid, entry->bssid, ETH_ALEN) == 0))
return entry;
#endif /* CONFIG_WPS */
+
+ if (!wpas_network_disabled(wpa_s, entry) && entry->bssid_set &&
+ entry->ssid_len == 0 &&
+ os_memcmp(bssid, entry->bssid, ETH_ALEN) == 0)
+ return entry;
+
entry = entry->next;
}
@@ -1626,45 +2129,68 @@ struct wpa_ssid * wpa_supplicant_get_ssid(struct wpa_supplicant *wpa_s)
}
+static int select_driver(struct wpa_supplicant *wpa_s, int i)
+{
+ struct wpa_global *global = wpa_s->global;
+
+ if (wpa_drivers[i]->global_init && global->drv_priv[i] == NULL) {
+ global->drv_priv[i] = wpa_drivers[i]->global_init();
+ if (global->drv_priv[i] == NULL) {
+ wpa_printf(MSG_ERROR, "Failed to initialize driver "
+ "'%s'", wpa_drivers[i]->name);
+ return -1;
+ }
+ }
+
+ wpa_s->driver = wpa_drivers[i];
+ wpa_s->global_drv_priv = global->drv_priv[i];
+
+ return 0;
+}
+
+
static int wpa_supplicant_set_driver(struct wpa_supplicant *wpa_s,
const char *name)
{
int i;
size_t len;
- const char *pos;
+ const char *pos, *driver = name;
if (wpa_s == NULL)
return -1;
if (wpa_drivers[0] == NULL) {
- wpa_printf(MSG_ERROR, "No driver interfaces build into "
- "wpa_supplicant.");
+ wpa_msg(wpa_s, MSG_ERROR, "No driver interfaces build into "
+ "wpa_supplicant");
return -1;
}
if (name == NULL) {
/* default to first driver in the list */
- wpa_s->driver = wpa_drivers[0];
- wpa_s->global_drv_priv = wpa_s->global->drv_priv[0];
- return 0;
+ return select_driver(wpa_s, 0);
}
- pos = os_strchr(name, ',');
- if (pos)
- len = pos - name;
- else
- len = os_strlen(name);
- for (i = 0; wpa_drivers[i]; i++) {
- if (os_strlen(wpa_drivers[i]->name) == len &&
- os_strncmp(name, wpa_drivers[i]->name, len) ==
- 0) {
- wpa_s->driver = wpa_drivers[i];
- wpa_s->global_drv_priv = wpa_s->global->drv_priv[i];
- return 0;
+ do {
+ pos = os_strchr(driver, ',');
+ if (pos)
+ len = pos - driver;
+ else
+ len = os_strlen(driver);
+
+ for (i = 0; wpa_drivers[i]; i++) {
+ if (os_strlen(wpa_drivers[i]->name) == len &&
+ os_strncmp(driver, wpa_drivers[i]->name, len) ==
+ 0) {
+ /* First driver that succeeds wins */
+ if (select_driver(wpa_s, i) == 0)
+ return 0;
+ }
}
- }
- wpa_printf(MSG_ERROR, "Unsupported driver '%s'.", name);
+ driver = pos + 1;
+ } while (pos);
+
+ wpa_msg(wpa_s, MSG_ERROR, "Unsupported driver '%s'", name);
return -1;
}
@@ -1688,20 +2214,31 @@ void wpa_supplicant_rx_eapol(void *ctx, const u8 *src_addr,
{
struct wpa_supplicant *wpa_s = ctx;
- wpa_printf(MSG_DEBUG, "RX EAPOL from " MACSTR, MAC2STR(src_addr));
+ wpa_dbg(wpa_s, MSG_DEBUG, "RX EAPOL from " MACSTR, MAC2STR(src_addr));
wpa_hexdump(MSG_MSGDUMP, "RX EAPOL", buf, len);
- if (wpa_s->wpa_state < WPA_ASSOCIATED) {
+ if (wpa_s->wpa_state < WPA_ASSOCIATED ||
+ (wpa_s->last_eapol_matches_bssid &&
+#ifdef CONFIG_AP
+ !wpa_s->ap_iface &&
+#endif /* CONFIG_AP */
+ os_memcmp(src_addr, wpa_s->bssid, ETH_ALEN) != 0)) {
/*
* There is possible race condition between receiving the
* association event and the EAPOL frame since they are coming
* through different paths from the driver. In order to avoid
* issues in trying to process the EAPOL frame before receiving
* association information, lets queue it for processing until
- * the association event is received.
+ * the association event is received. This may also be needed in
+ * driver-based roaming case, so also use src_addr != BSSID as a
+ * trigger if we have previously confirmed that the
+ * Authenticator uses BSSID as the src_addr (which is not the
+ * case with wired IEEE 802.1X).
*/
- wpa_printf(MSG_DEBUG, "Not associated - Delay processing of "
- "received EAPOL frame");
+ wpa_dbg(wpa_s, MSG_DEBUG, "Not associated - Delay processing "
+ "of received EAPOL frame (state=%s bssid=" MACSTR ")",
+ wpa_supplicant_state_txt(wpa_s->wpa_state),
+ MAC2STR(wpa_s->bssid));
wpabuf_free(wpa_s->pending_eapol_rx);
wpa_s->pending_eapol_rx = wpabuf_alloc_copy(buf, len);
if (wpa_s->pending_eapol_rx) {
@@ -1712,6 +2249,9 @@ void wpa_supplicant_rx_eapol(void *ctx, const u8 *src_addr,
return;
}
+ wpa_s->last_eapol_matches_bssid =
+ os_memcmp(src_addr, wpa_s->bssid, ETH_ALEN) == 0;
+
#ifdef CONFIG_AP
if (wpa_s->ap_iface) {
wpa_supplicant_ap_rx_eapol(wpa_s, src_addr, buf, len);
@@ -1720,8 +2260,8 @@ void wpa_supplicant_rx_eapol(void *ctx, const u8 *src_addr,
#endif /* CONFIG_AP */
if (wpa_s->key_mgmt == WPA_KEY_MGMT_NONE) {
- wpa_printf(MSG_DEBUG, "Ignored received EAPOL frame since "
- "no key management is configured");
+ wpa_dbg(wpa_s, MSG_DEBUG, "Ignored received EAPOL frame since "
+ "no key management is configured");
return;
}
@@ -1742,8 +2282,8 @@ void wpa_supplicant_rx_eapol(void *ctx, const u8 *src_addr,
wpa_s->eapol_received++;
if (wpa_s->countermeasures) {
- wpa_printf(MSG_INFO, "WPA: Countermeasures - dropped EAPOL "
- "packet");
+ wpa_msg(wpa_s, MSG_INFO, "WPA: Countermeasures - dropped "
+ "EAPOL packet");
return;
}
@@ -1780,52 +2320,93 @@ void wpa_supplicant_rx_eapol(void *ctx, const u8 *src_addr,
}
-/**
- * wpa_supplicant_driver_init - Initialize driver interface parameters
- * @wpa_s: Pointer to wpa_supplicant data
- * Returns: 0 on success, -1 on failure
- *
- * This function is called to initialize driver interface parameters.
- * wpa_drv_init() must have been called before this function to initialize the
- * driver interface.
- */
-int wpa_supplicant_driver_init(struct wpa_supplicant *wpa_s)
+int wpa_supplicant_update_mac_addr(struct wpa_supplicant *wpa_s)
{
- static int interface_count = 0;
-
if (wpa_s->driver->send_eapol) {
const u8 *addr = wpa_drv_get_mac_addr(wpa_s);
if (addr)
os_memcpy(wpa_s->own_addr, addr, ETH_ALEN);
- } else {
+ } else if (!(wpa_s->drv_flags &
+ WPA_DRIVER_FLAGS_P2P_DEDICATED_INTERFACE)) {
+ l2_packet_deinit(wpa_s->l2);
wpa_s->l2 = l2_packet_init(wpa_s->ifname,
wpa_drv_get_mac_addr(wpa_s),
ETH_P_EAPOL,
wpa_supplicant_rx_eapol, wpa_s, 0);
if (wpa_s->l2 == NULL)
return -1;
+ } else {
+ const u8 *addr = wpa_drv_get_mac_addr(wpa_s);
+ if (addr)
+ os_memcpy(wpa_s->own_addr, addr, ETH_ALEN);
}
if (wpa_s->l2 && l2_packet_get_own_addr(wpa_s->l2, wpa_s->own_addr)) {
- wpa_printf(MSG_ERROR, "Failed to get own L2 address");
+ wpa_msg(wpa_s, MSG_ERROR, "Failed to get own L2 address");
return -1;
}
- wpa_printf(MSG_DEBUG, "Own MAC address: " MACSTR,
- MAC2STR(wpa_s->own_addr));
+ wpa_dbg(wpa_s, MSG_DEBUG, "Own MAC address: " MACSTR,
+ MAC2STR(wpa_s->own_addr));
+ wpa_sm_set_own_addr(wpa_s->wpa, wpa_s->own_addr);
+
+ return 0;
+}
+
+
+static void wpa_supplicant_rx_eapol_bridge(void *ctx, const u8 *src_addr,
+ const u8 *buf, size_t len)
+{
+ struct wpa_supplicant *wpa_s = ctx;
+ const struct l2_ethhdr *eth;
+
+ if (len < sizeof(*eth))
+ return;
+ eth = (const struct l2_ethhdr *) buf;
+
+ if (os_memcmp(eth->h_dest, wpa_s->own_addr, ETH_ALEN) != 0 &&
+ !(eth->h_dest[0] & 0x01)) {
+ wpa_dbg(wpa_s, MSG_DEBUG, "RX EAPOL from " MACSTR " to " MACSTR
+ " (bridge - not for this interface - ignore)",
+ MAC2STR(src_addr), MAC2STR(eth->h_dest));
+ return;
+ }
+
+ wpa_dbg(wpa_s, MSG_DEBUG, "RX EAPOL from " MACSTR " to " MACSTR
+ " (bridge)", MAC2STR(src_addr), MAC2STR(eth->h_dest));
+ wpa_supplicant_rx_eapol(wpa_s, src_addr, buf + sizeof(*eth),
+ len - sizeof(*eth));
+}
+
+
+/**
+ * wpa_supplicant_driver_init - Initialize driver interface parameters
+ * @wpa_s: Pointer to wpa_supplicant data
+ * Returns: 0 on success, -1 on failure
+ *
+ * This function is called to initialize driver interface parameters.
+ * wpa_drv_init() must have been called before this function to initialize the
+ * driver interface.
+ */
+int wpa_supplicant_driver_init(struct wpa_supplicant *wpa_s)
+{
+ static int interface_count = 0;
+
+ if (wpa_supplicant_update_mac_addr(wpa_s) < 0)
+ return -1;
if (wpa_s->bridge_ifname[0]) {
- wpa_printf(MSG_DEBUG, "Receiving packets from bridge interface"
- " '%s'", wpa_s->bridge_ifname);
+ wpa_dbg(wpa_s, MSG_DEBUG, "Receiving packets from bridge "
+ "interface '%s'", wpa_s->bridge_ifname);
wpa_s->l2_br = l2_packet_init(wpa_s->bridge_ifname,
wpa_s->own_addr,
ETH_P_EAPOL,
- wpa_supplicant_rx_eapol, wpa_s,
- 0);
+ wpa_supplicant_rx_eapol_bridge,
+ wpa_s, 1);
if (wpa_s->l2_br == NULL) {
- wpa_printf(MSG_ERROR, "Failed to open l2_packet "
- "connection for the bridge interface '%s'",
- wpa_s->bridge_ifname);
+ wpa_msg(wpa_s, MSG_ERROR, "Failed to open l2_packet "
+ "connection for the bridge interface '%s'",
+ wpa_s->bridge_ifname);
return -1;
}
}
@@ -1836,12 +2417,17 @@ int wpa_supplicant_driver_init(struct wpa_supplicant *wpa_s)
* happen if wpa_supplicant is killed during countermeasures. */
wpa_drv_set_countermeasures(wpa_s, 0);
- wpa_printf(MSG_DEBUG, "RSN: flushing PMKID list in the driver");
+ wpa_dbg(wpa_s, MSG_DEBUG, "RSN: flushing PMKID list in the driver");
wpa_drv_flush_pmkid(wpa_s);
wpa_s->prev_scan_ssid = WILDCARD_SSID_SCAN;
- if (wpa_supplicant_enabled_networks(wpa_s->conf)) {
- wpa_supplicant_req_scan(wpa_s, interface_count, 100000);
+ wpa_s->prev_scan_wildcard = 0;
+
+ if (wpa_supplicant_enabled_networks(wpa_s)) {
+ if (wpa_supplicant_delayed_sched_scan(wpa_s, interface_count,
+ 100000))
+ wpa_supplicant_req_scan(wpa_s, interface_count,
+ 100000);
interface_count++;
} else
wpa_supplicant_set_state(wpa_s, WPA_INACTIVE);
@@ -1864,13 +2450,290 @@ static struct wpa_supplicant * wpa_supplicant_alloc(void)
wpa_s = os_zalloc(sizeof(*wpa_s));
if (wpa_s == NULL)
return NULL;
- wpa_s->scan_req = 1;
+ wpa_s->scan_req = INITIAL_SCAN_REQ;
+ wpa_s->scan_interval = 5;
wpa_s->new_connection = 1;
+ wpa_s->parent = wpa_s;
+ wpa_s->sched_scanning = 0;
return wpa_s;
}
+#ifdef CONFIG_HT_OVERRIDES
+
+static int wpa_set_htcap_mcs(struct wpa_supplicant *wpa_s,
+ struct ieee80211_ht_capabilities *htcaps,
+ struct ieee80211_ht_capabilities *htcaps_mask,
+ const char *ht_mcs)
+{
+ /* parse ht_mcs into hex array */
+ int i;
+ const char *tmp = ht_mcs;
+ char *end = NULL;
+
+ /* If ht_mcs is null, do not set anything */
+ if (!ht_mcs)
+ return 0;
+
+ /* This is what we are setting in the kernel */
+ os_memset(&htcaps->supported_mcs_set, 0, IEEE80211_HT_MCS_MASK_LEN);
+
+ wpa_msg(wpa_s, MSG_DEBUG, "set_htcap, ht_mcs -:%s:-", ht_mcs);
+
+ for (i = 0; i < IEEE80211_HT_MCS_MASK_LEN; i++) {
+ errno = 0;
+ long v = strtol(tmp, &end, 16);
+ if (errno == 0) {
+ wpa_msg(wpa_s, MSG_DEBUG,
+ "htcap value[%i]: %ld end: %p tmp: %p",
+ i, v, end, tmp);
+ if (end == tmp)
+ break;
+
+ htcaps->supported_mcs_set[i] = v;
+ tmp = end;
+ } else {
+ wpa_msg(wpa_s, MSG_ERROR,
+ "Failed to parse ht-mcs: %s, error: %s\n",
+ ht_mcs, strerror(errno));
+ return -1;
+ }
+ }
+
+ /*
+ * If we were able to parse any values, then set mask for the MCS set.
+ */
+ if (i) {
+ os_memset(&htcaps_mask->supported_mcs_set, 0xff,
+ IEEE80211_HT_MCS_MASK_LEN - 1);
+ /* skip the 3 reserved bits */
+ htcaps_mask->supported_mcs_set[IEEE80211_HT_MCS_MASK_LEN - 1] =
+ 0x1f;
+ }
+
+ return 0;
+}
+
+
+static int wpa_disable_max_amsdu(struct wpa_supplicant *wpa_s,
+ struct ieee80211_ht_capabilities *htcaps,
+ struct ieee80211_ht_capabilities *htcaps_mask,
+ int disabled)
+{
+ u16 msk;
+
+ wpa_msg(wpa_s, MSG_DEBUG, "set_disable_max_amsdu: %d", disabled);
+
+ if (disabled == -1)
+ return 0;
+
+ msk = host_to_le16(HT_CAP_INFO_MAX_AMSDU_SIZE);
+ htcaps_mask->ht_capabilities_info |= msk;
+ if (disabled)
+ htcaps->ht_capabilities_info &= msk;
+ else
+ htcaps->ht_capabilities_info |= msk;
+
+ return 0;
+}
+
+
+static int wpa_set_ampdu_factor(struct wpa_supplicant *wpa_s,
+ struct ieee80211_ht_capabilities *htcaps,
+ struct ieee80211_ht_capabilities *htcaps_mask,
+ int factor)
+{
+ wpa_msg(wpa_s, MSG_DEBUG, "set_ampdu_factor: %d", factor);
+
+ if (factor == -1)
+ return 0;
+
+ if (factor < 0 || factor > 3) {
+ wpa_msg(wpa_s, MSG_ERROR, "ampdu_factor: %d out of range. "
+ "Must be 0-3 or -1", factor);
+ return -EINVAL;
+ }
+
+ htcaps_mask->a_mpdu_params |= 0x3; /* 2 bits for factor */
+ htcaps->a_mpdu_params &= ~0x3;
+ htcaps->a_mpdu_params |= factor & 0x3;
+
+ return 0;
+}
+
+
+static int wpa_set_ampdu_density(struct wpa_supplicant *wpa_s,
+ struct ieee80211_ht_capabilities *htcaps,
+ struct ieee80211_ht_capabilities *htcaps_mask,
+ int density)
+{
+ wpa_msg(wpa_s, MSG_DEBUG, "set_ampdu_density: %d", density);
+
+ if (density == -1)
+ return 0;
+
+ if (density < 0 || density > 7) {
+ wpa_msg(wpa_s, MSG_ERROR,
+ "ampdu_density: %d out of range. Must be 0-7 or -1.",
+ density);
+ return -EINVAL;
+ }
+
+ htcaps_mask->a_mpdu_params |= 0x1C;
+ htcaps->a_mpdu_params &= ~(0x1C);
+ htcaps->a_mpdu_params |= (density << 2) & 0x1C;
+
+ return 0;
+}
+
+
+static int wpa_set_disable_ht40(struct wpa_supplicant *wpa_s,
+ struct ieee80211_ht_capabilities *htcaps,
+ struct ieee80211_ht_capabilities *htcaps_mask,
+ int disabled)
+{
+ /* Masking these out disables HT40 */
+ u16 msk = host_to_le16(HT_CAP_INFO_SUPP_CHANNEL_WIDTH_SET |
+ HT_CAP_INFO_SHORT_GI40MHZ);
+
+ wpa_msg(wpa_s, MSG_DEBUG, "set_disable_ht40: %d", disabled);
+
+ if (disabled)
+ htcaps->ht_capabilities_info &= ~msk;
+ else
+ htcaps->ht_capabilities_info |= msk;
+
+ htcaps_mask->ht_capabilities_info |= msk;
+
+ return 0;
+}
+
+
+static int wpa_set_disable_sgi(struct wpa_supplicant *wpa_s,
+ struct ieee80211_ht_capabilities *htcaps,
+ struct ieee80211_ht_capabilities *htcaps_mask,
+ int disabled)
+{
+ /* Masking these out disables SGI */
+ u16 msk = host_to_le16(HT_CAP_INFO_SHORT_GI20MHZ |
+ HT_CAP_INFO_SHORT_GI40MHZ);
+
+ wpa_msg(wpa_s, MSG_DEBUG, "set_disable_sgi: %d", disabled);
+
+ if (disabled)
+ htcaps->ht_capabilities_info &= ~msk;
+ else
+ htcaps->ht_capabilities_info |= msk;
+
+ htcaps_mask->ht_capabilities_info |= msk;
+
+ return 0;
+}
+
+
+void wpa_supplicant_apply_ht_overrides(
+ struct wpa_supplicant *wpa_s, struct wpa_ssid *ssid,
+ struct wpa_driver_associate_params *params)
+{
+ struct ieee80211_ht_capabilities *htcaps;
+ struct ieee80211_ht_capabilities *htcaps_mask;
+
+ if (!ssid)
+ return;
+
+ params->disable_ht = ssid->disable_ht;
+ if (!params->htcaps || !params->htcaps_mask)
+ return;
+
+ htcaps = (struct ieee80211_ht_capabilities *) params->htcaps;
+ htcaps_mask = (struct ieee80211_ht_capabilities *) params->htcaps_mask;
+ wpa_set_htcap_mcs(wpa_s, htcaps, htcaps_mask, ssid->ht_mcs);
+ wpa_disable_max_amsdu(wpa_s, htcaps, htcaps_mask,
+ ssid->disable_max_amsdu);
+ wpa_set_ampdu_factor(wpa_s, htcaps, htcaps_mask, ssid->ampdu_factor);
+ wpa_set_ampdu_density(wpa_s, htcaps, htcaps_mask, ssid->ampdu_density);
+ wpa_set_disable_ht40(wpa_s, htcaps, htcaps_mask, ssid->disable_ht40);
+ wpa_set_disable_sgi(wpa_s, htcaps, htcaps_mask, ssid->disable_sgi);
+}
+
+#endif /* CONFIG_HT_OVERRIDES */
+
+
+static int pcsc_reader_init(struct wpa_supplicant *wpa_s)
+{
+#ifdef PCSC_FUNCS
+ size_t len;
+
+ if (!wpa_s->conf->pcsc_reader)
+ return 0;
+
+ wpa_s->scard = scard_init(SCARD_TRY_BOTH, wpa_s->conf->pcsc_reader);
+ if (!wpa_s->scard)
+ return 1;
+
+ if (wpa_s->conf->pcsc_pin &&
+ scard_set_pin(wpa_s->scard, wpa_s->conf->pcsc_pin) < 0) {
+ scard_deinit(wpa_s->scard);
+ wpa_s->scard = NULL;
+ wpa_msg(wpa_s, MSG_ERROR, "PC/SC PIN validation failed");
+ return -1;
+ }
+
+ len = sizeof(wpa_s->imsi) - 1;
+ if (scard_get_imsi(wpa_s->scard, wpa_s->imsi, &len)) {
+ scard_deinit(wpa_s->scard);
+ wpa_s->scard = NULL;
+ wpa_msg(wpa_s, MSG_ERROR, "Could not read IMSI");
+ return -1;
+ }
+ wpa_s->imsi[len] = '\0';
+
+ wpa_s->mnc_len = scard_get_mnc_len(wpa_s->scard);
+
+ wpa_printf(MSG_DEBUG, "SCARD: IMSI %s (MNC length %d)",
+ wpa_s->imsi, wpa_s->mnc_len);
+
+ wpa_sm_set_scard_ctx(wpa_s->wpa, wpa_s->scard);
+ eapol_sm_register_scard_ctx(wpa_s->eapol, wpa_s->scard);
+#endif /* PCSC_FUNCS */
+
+ return 0;
+}
+
+
+int wpas_init_ext_pw(struct wpa_supplicant *wpa_s)
+{
+ char *val, *pos;
+
+ ext_password_deinit(wpa_s->ext_pw);
+ wpa_s->ext_pw = NULL;
+ eapol_sm_set_ext_pw_ctx(wpa_s->eapol, NULL);
+
+ if (!wpa_s->conf->ext_password_backend)
+ return 0;
+
+ val = os_strdup(wpa_s->conf->ext_password_backend);
+ if (val == NULL)
+ return -1;
+ pos = os_strchr(val, ':');
+ if (pos)
+ *pos++ = '\0';
+
+ wpa_printf(MSG_DEBUG, "EXT PW: Initialize backend '%s'", val);
+
+ wpa_s->ext_pw = ext_password_init(val, pos);
+ os_free(val);
+ if (wpa_s->ext_pw == NULL) {
+ wpa_printf(MSG_DEBUG, "EXT PW: Failed to initialize backend");
+ return -1;
+ }
+ eapol_sm_set_ext_pw_ctx(wpa_s->eapol, wpa_s->ext_pw);
+
+ return 0;
+}
+
+
static int wpa_supplicant_init_iface(struct wpa_supplicant *wpa_s,
struct wpa_interface *iface)
{
@@ -1969,24 +2832,25 @@ next_driver:
const char *pos;
pos = driver ? os_strchr(driver, ',') : NULL;
if (pos) {
- wpa_printf(MSG_DEBUG, "Failed to initialize driver "
- "interface - try next driver wrapper");
+ wpa_dbg(wpa_s, MSG_DEBUG, "Failed to initialize "
+ "driver interface - try next driver wrapper");
driver = pos + 1;
goto next_driver;
}
- wpa_printf(MSG_ERROR, "Failed to initialize driver interface");
+ wpa_msg(wpa_s, MSG_ERROR, "Failed to initialize driver "
+ "interface");
return -1;
}
if (wpa_drv_set_param(wpa_s, wpa_s->conf->driver_param) < 0) {
- wpa_printf(MSG_ERROR, "Driver interface rejected "
- "driver_param '%s'", wpa_s->conf->driver_param);
+ wpa_msg(wpa_s, MSG_ERROR, "Driver interface rejected "
+ "driver_param '%s'", wpa_s->conf->driver_param);
return -1;
}
ifname = wpa_drv_get_ifname(wpa_s);
if (ifname && os_strcmp(ifname, wpa_s->ifname) != 0) {
- wpa_printf(MSG_DEBUG, "Driver interface replaced interface "
- "name with '%s'", ifname);
+ wpa_dbg(wpa_s, MSG_DEBUG, "Driver interface replaced "
+ "interface name with '%s'", ifname);
os_strlcpy(wpa_s->ifname, ifname, sizeof(wpa_s->ifname));
}
@@ -2001,15 +2865,15 @@ next_driver:
if (wpa_s->conf->dot11RSNAConfigPMKLifetime &&
wpa_sm_set_param(wpa_s->wpa, RSNA_PMK_LIFETIME,
wpa_s->conf->dot11RSNAConfigPMKLifetime)) {
- wpa_printf(MSG_ERROR, "Invalid WPA parameter value for "
- "dot11RSNAConfigPMKLifetime");
+ wpa_msg(wpa_s, MSG_ERROR, "Invalid WPA parameter value for "
+ "dot11RSNAConfigPMKLifetime");
return -1;
}
if (wpa_s->conf->dot11RSNAConfigPMKReauthThreshold &&
wpa_sm_set_param(wpa_s->wpa, RSNA_PMK_REAUTH_THRESHOLD,
wpa_s->conf->dot11RSNAConfigPMKReauthThreshold)) {
- wpa_printf(MSG_ERROR, "Invalid WPA parameter value for "
+ wpa_msg(wpa_s, MSG_ERROR, "Invalid WPA parameter value for "
"dot11RSNAConfigPMKReauthThreshold");
return -1;
}
@@ -2017,19 +2881,26 @@ next_driver:
if (wpa_s->conf->dot11RSNAConfigSATimeout &&
wpa_sm_set_param(wpa_s->wpa, RSNA_SA_TIMEOUT,
wpa_s->conf->dot11RSNAConfigSATimeout)) {
- wpa_printf(MSG_ERROR, "Invalid WPA parameter value for "
- "dot11RSNAConfigSATimeout");
+ wpa_msg(wpa_s, MSG_ERROR, "Invalid WPA parameter value for "
+ "dot11RSNAConfigSATimeout");
return -1;
}
+ wpa_s->hw.modes = wpa_drv_get_hw_feature_data(wpa_s,
+ &wpa_s->hw.num_modes,
+ &wpa_s->hw.flags);
+
if (wpa_drv_get_capa(wpa_s, &capa) == 0) {
+ wpa_s->drv_capa_known = 1;
wpa_s->drv_flags = capa.flags;
- if (capa.flags & WPA_DRIVER_FLAGS_USER_SPACE_MLME) {
- if (ieee80211_sta_init(wpa_s))
- return -1;
- }
+ wpa_s->drv_enc = capa.enc;
+ wpa_s->probe_resp_offloads = capa.probe_resp_offloads;
wpa_s->max_scan_ssids = capa.max_scan_ssids;
+ wpa_s->max_sched_scan_ssids = capa.max_sched_scan_ssids;
+ wpa_s->sched_scan_supported = capa.sched_scan_supported;
+ wpa_s->max_match_sets = capa.max_match_sets;
wpa_s->max_remain_on_chan = capa.max_remain_on_chan;
+ wpa_s->max_stations = capa.max_stations;
}
if (wpa_s->max_remain_on_chan == 0)
wpa_s->max_remain_on_chan = 1000;
@@ -2037,14 +2908,17 @@ next_driver:
if (wpa_supplicant_driver_init(wpa_s) < 0)
return -1;
+#ifdef CONFIG_TDLS
+ if (wpa_tdls_init(wpa_s->wpa))
+ return -1;
+#endif /* CONFIG_TDLS */
+
if (wpa_s->conf->country[0] && wpa_s->conf->country[1] &&
wpa_drv_set_country(wpa_s, wpa_s->conf->country)) {
- wpa_printf(MSG_DEBUG, "Failed to set country");
+ wpa_dbg(wpa_s, MSG_DEBUG, "Failed to set country");
return -1;
}
- wpa_sm_set_own_addr(wpa_s->wpa, wpa_s->own_addr);
-
if (wpas_wps_init(wpa_s))
return -1;
@@ -2066,23 +2940,34 @@ next_driver:
return -1;
}
-#ifdef CONFIG_IBSS_RSN
- wpa_s->ibss_rsn = ibss_rsn_init(wpa_s);
- if (!wpa_s->ibss_rsn) {
- wpa_printf(MSG_DEBUG, "Failed to init IBSS RSN");
+ wpa_s->gas = gas_query_init(wpa_s);
+ if (wpa_s->gas == NULL) {
+ wpa_printf(MSG_ERROR, "Failed to initialize GAS query");
return -1;
}
-#endif /* CONFIG_IBSS_RSN */
+
+#ifdef CONFIG_P2P
+ if (wpas_p2p_init(wpa_s->global, wpa_s) < 0) {
+ wpa_msg(wpa_s, MSG_ERROR, "Failed to init P2P");
+ return -1;
+ }
+#endif /* CONFIG_P2P */
if (wpa_bss_init(wpa_s) < 0)
return -1;
+ if (pcsc_reader_init(wpa_s) < 0)
+ return -1;
+
+ if (wpas_init_ext_pw(wpa_s) < 0)
+ return -1;
+
return 0;
}
static void wpa_supplicant_deinit_iface(struct wpa_supplicant *wpa_s,
- int notify)
+ int notify, int terminate)
{
if (wpa_s->drv_priv) {
wpa_supplicant_deauthenticate(wpa_s,
@@ -2094,11 +2979,32 @@ static void wpa_supplicant_deinit_iface(struct wpa_supplicant *wpa_s,
wpa_supplicant_cleanup(wpa_s);
- if (notify)
- wpas_notify_iface_removed(wpa_s);
+#ifdef CONFIG_P2P
+ if (wpa_s == wpa_s->global->p2p_init_wpa_s && wpa_s->global->p2p) {
+ wpa_dbg(wpa_s, MSG_DEBUG, "P2P: Disable P2P since removing "
+ "the management interface is being removed");
+ wpas_p2p_deinit_global(wpa_s->global);
+ }
+#endif /* CONFIG_P2P */
if (wpa_s->drv_priv)
wpa_drv_deinit(wpa_s);
+
+ if (notify)
+ wpas_notify_iface_removed(wpa_s);
+
+ if (terminate)
+ wpa_msg(wpa_s, MSG_INFO, WPA_EVENT_TERMINATING);
+
+ if (wpa_s->ctrl_iface) {
+ wpa_supplicant_ctrl_iface_deinit(wpa_s->ctrl_iface);
+ wpa_s->ctrl_iface = NULL;
+ }
+
+ if (wpa_s->conf != NULL) {
+ wpa_config_free(wpa_s->conf);
+ wpa_s->conf = NULL;
+ }
}
@@ -2148,14 +3054,14 @@ struct wpa_supplicant * wpa_supplicant_add_iface(struct wpa_global *global,
if (wpa_supplicant_init_iface(wpa_s, &t_iface)) {
wpa_printf(MSG_DEBUG, "Failed to add interface %s",
iface->ifname);
- wpa_supplicant_deinit_iface(wpa_s, 0);
+ wpa_supplicant_deinit_iface(wpa_s, 0, 0);
os_free(wpa_s);
return NULL;
}
/* Notify the control interfaces about new iface */
if (wpas_notify_iface_added(wpa_s)) {
- wpa_supplicant_deinit_iface(wpa_s, 1);
+ wpa_supplicant_deinit_iface(wpa_s, 1, 0);
os_free(wpa_s);
return NULL;
}
@@ -2166,7 +3072,8 @@ struct wpa_supplicant * wpa_supplicant_add_iface(struct wpa_global *global,
wpa_s->next = global->ifaces;
global->ifaces = wpa_s;
- wpa_printf(MSG_DEBUG, "Added interface %s", wpa_s->ifname);
+ wpa_dbg(wpa_s, MSG_DEBUG, "Added interface %s", wpa_s->ifname);
+ wpa_supplicant_set_state(wpa_s, WPA_DISCONNECTED);
return wpa_s;
}
@@ -2184,7 +3091,8 @@ struct wpa_supplicant * wpa_supplicant_add_iface(struct wpa_global *global,
* %wpa_supplicant is terminated.
*/
int wpa_supplicant_remove_iface(struct wpa_global *global,
- struct wpa_supplicant *wpa_s)
+ struct wpa_supplicant *wpa_s,
+ int terminate)
{
struct wpa_supplicant *prev;
@@ -2200,9 +3108,11 @@ int wpa_supplicant_remove_iface(struct wpa_global *global,
prev->next = wpa_s->next;
}
- wpa_printf(MSG_DEBUG, "Removing interface %s", wpa_s->ifname);
+ wpa_dbg(wpa_s, MSG_DEBUG, "Removing interface %s", wpa_s->ifname);
- wpa_supplicant_deinit_iface(wpa_s, 1);
+ if (global->p2p_group_formation == wpa_s)
+ global->p2p_group_formation = NULL;
+ wpa_supplicant_deinit_iface(wpa_s, 1, terminate);
os_free(wpa_s);
return 0;
@@ -2210,6 +3120,28 @@ int wpa_supplicant_remove_iface(struct wpa_global *global,
/**
+ * wpa_supplicant_get_eap_mode - Get the current EAP mode
+ * @wpa_s: Pointer to the network interface
+ * Returns: Pointer to the eap mode or the string "UNKNOWN" if not found
+ */
+const char * wpa_supplicant_get_eap_mode(struct wpa_supplicant *wpa_s)
+{
+ const char *eapol_method;
+
+ if (wpa_key_mgmt_wpa_ieee8021x(wpa_s->key_mgmt) == 0 &&
+ wpa_s->key_mgmt != WPA_KEY_MGMT_IEEE8021X_NO_WPA) {
+ return "NO-EAP";
+ }
+
+ eapol_method = eapol_sm_get_method_name(wpa_s->eapol);
+ if (eapol_method == NULL)
+ return "UNKNOWN-EAP";
+
+ return eapol_method;
+}
+
+
+/**
* wpa_supplicant_get_iface - Get a new network interface
* @global: Pointer to global data from wpa_supplicant_init()
* @ifname: Interface name
@@ -2228,6 +3160,17 @@ struct wpa_supplicant * wpa_supplicant_get_iface(struct wpa_global *global,
}
+#ifndef CONFIG_NO_WPA_MSG
+static const char * wpa_supplicant_msg_ifname_cb(void *ctx)
+{
+ struct wpa_supplicant *wpa_s = ctx;
+ if (wpa_s == NULL)
+ return NULL;
+ return wpa_s->ifname;
+}
+#endif /* CONFIG_NO_WPA_MSG */
+
+
/**
* wpa_supplicant_init - Initialize %wpa_supplicant
* @params: Parameters for %wpa_supplicant
@@ -2245,9 +3188,28 @@ struct wpa_global * wpa_supplicant_init(struct wpa_params *params)
if (params == NULL)
return NULL;
+#ifdef CONFIG_DRIVER_NDIS
+ {
+ void driver_ndis_init_ops(void);
+ driver_ndis_init_ops();
+ }
+#endif /* CONFIG_DRIVER_NDIS */
+
+#ifndef CONFIG_NO_WPA_MSG
+ wpa_msg_register_ifname_cb(wpa_supplicant_msg_ifname_cb);
+#endif /* CONFIG_NO_WPA_MSG */
+
wpa_debug_open_file(params->wpa_debug_file_path);
if (params->wpa_debug_syslog)
wpa_debug_open_syslog();
+ if (params->wpa_debug_tracing) {
+ ret = wpa_debug_open_linux_tracing();
+ if (ret) {
+ wpa_printf(MSG_ERROR,
+ "Failed to enable trace logging");
+ return NULL;
+ }
+ }
ret = eap_register_methods();
if (ret) {
@@ -2261,6 +3223,8 @@ struct wpa_global * wpa_supplicant_init(struct wpa_params *params)
global = os_zalloc(sizeof(*global));
if (global == NULL)
return NULL;
+ dl_list_init(&global->p2p_srv_bonjour);
+ dl_list_init(&global->p2p_srv_upnp);
global->params.daemonize = params->daemonize;
global->params.wait_for_monitor = params->wait_for_monitor;
global->params.dbus_ctrl_interface = params->dbus_ctrl_interface;
@@ -2282,12 +3246,16 @@ struct wpa_global * wpa_supplicant_init(struct wpa_params *params)
wpa_debug_timestamp = global->params.wpa_debug_timestamp =
params->wpa_debug_timestamp;
+ wpa_printf(MSG_DEBUG, "wpa_supplicant v" VERSION_STR);
+
if (eloop_init()) {
wpa_printf(MSG_ERROR, "Failed to initialize event loop");
wpa_supplicant_deinit(global);
return NULL;
}
+ random_init(params->entropy_file);
+
global->ctrl_iface = wpa_supplicant_global_ctrl_iface_init(global);
if (global->ctrl_iface == NULL) {
wpa_supplicant_deinit(global);
@@ -2311,17 +3279,14 @@ struct wpa_global * wpa_supplicant_init(struct wpa_params *params)
wpa_supplicant_deinit(global);
return NULL;
}
- for (i = 0; wpa_drivers[i]; i++) {
- if (!wpa_drivers[i]->global_init)
- continue;
- global->drv_priv[i] = wpa_drivers[i]->global_init();
- if (global->drv_priv[i] == NULL) {
- wpa_printf(MSG_ERROR, "Failed to initialize driver "
- "'%s'", wpa_drivers[i]->name);
- wpa_supplicant_deinit(global);
- return NULL;
- }
+
+#ifdef CONFIG_WIFI_DISPLAY
+ if (wifi_display_init(global) < 0) {
+ wpa_printf(MSG_ERROR, "Failed to initialize Wi-Fi Display");
+ wpa_supplicant_deinit(global);
+ return NULL;
}
+#endif /* CONFIG_WIFI_DISPLAY */
return global;
}
@@ -2374,8 +3339,15 @@ void wpa_supplicant_deinit(struct wpa_global *global)
if (global == NULL)
return;
+#ifdef CONFIG_WIFI_DISPLAY
+ wifi_display_deinit(global);
+#endif /* CONFIG_WIFI_DISPLAY */
+#ifdef CONFIG_P2P
+ wpas_p2p_deinit_global(global);
+#endif /* CONFIG_P2P */
+
while (global->ifaces)
- wpa_supplicant_remove_iface(global, global->ifaces);
+ wpa_supplicant_remove_iface(global, global->ifaces, 1);
if (global->ctrl_iface)
wpa_supplicant_global_ctrl_iface_deinit(global->ctrl_iface);
@@ -2394,6 +3366,8 @@ void wpa_supplicant_deinit(struct wpa_global *global)
}
os_free(global->drv_priv);
+ random_deinit();
+
eloop_destroy();
if (global->params.pid_file) {
@@ -2404,7 +3378,419 @@ void wpa_supplicant_deinit(struct wpa_global *global)
os_free(global->params.override_driver);
os_free(global->params.override_ctrl_interface);
+ os_free(global->p2p_disallow_freq);
+
os_free(global);
wpa_debug_close_syslog();
wpa_debug_close_file();
+ wpa_debug_close_linux_tracing();
+}
+
+
+void wpa_supplicant_update_config(struct wpa_supplicant *wpa_s)
+{
+ if ((wpa_s->conf->changed_parameters & CFG_CHANGED_COUNTRY) &&
+ wpa_s->conf->country[0] && wpa_s->conf->country[1]) {
+ char country[3];
+ country[0] = wpa_s->conf->country[0];
+ country[1] = wpa_s->conf->country[1];
+ country[2] = '\0';
+ if (wpa_drv_set_country(wpa_s, country) < 0) {
+ wpa_printf(MSG_ERROR, "Failed to set country code "
+ "'%s'", country);
+ }
+ }
+
+ if (wpa_s->conf->changed_parameters & CFG_CHANGED_EXT_PW_BACKEND)
+ wpas_init_ext_pw(wpa_s);
+
+#ifdef CONFIG_WPS
+ wpas_wps_update_config(wpa_s);
+#endif /* CONFIG_WPS */
+
+#ifdef CONFIG_P2P
+ wpas_p2p_update_config(wpa_s);
+#endif /* CONFIG_P2P */
+
+ wpa_s->conf->changed_parameters = 0;
+}
+
+
+static void add_freq(int *freqs, int *num_freqs, int freq)
+{
+ int i;
+
+ for (i = 0; i < *num_freqs; i++) {
+ if (freqs[i] == freq)
+ return;
+ }
+
+ freqs[*num_freqs] = freq;
+ (*num_freqs)++;
+}
+
+
+static int * get_bss_freqs_in_ess(struct wpa_supplicant *wpa_s)
+{
+ struct wpa_bss *bss, *cbss;
+ const int max_freqs = 10;
+ int *freqs;
+ int num_freqs = 0;
+
+ freqs = os_zalloc(sizeof(int) * (max_freqs + 1));
+ if (freqs == NULL)
+ return NULL;
+
+ cbss = wpa_s->current_bss;
+
+ dl_list_for_each(bss, &wpa_s->bss, struct wpa_bss, list) {
+ if (bss == cbss)
+ continue;
+ if (bss->ssid_len == cbss->ssid_len &&
+ os_memcmp(bss->ssid, cbss->ssid, bss->ssid_len) == 0 &&
+ wpa_blacklist_get(wpa_s, bss->bssid) == NULL) {
+ add_freq(freqs, &num_freqs, bss->freq);
+ if (num_freqs == max_freqs)
+ break;
+ }
+ }
+
+ if (num_freqs == 0) {
+ os_free(freqs);
+ freqs = NULL;
+ }
+
+ return freqs;
+}
+
+
+void wpas_connection_failed(struct wpa_supplicant *wpa_s, const u8 *bssid)
+{
+ int timeout;
+ int count;
+ int *freqs = NULL;
+
+ /*
+ * Remove possible authentication timeout since the connection failed.
+ */
+ eloop_cancel_timeout(wpa_supplicant_timeout, wpa_s, NULL);
+
+ /*
+ * Add the failed BSSID into the blacklist and speed up next scan
+ * attempt if there could be other APs that could accept association.
+ * The current blacklist count indicates how many times we have tried
+ * connecting to this AP and multiple attempts mean that other APs are
+ * either not available or has already been tried, so that we can start
+ * increasing the delay here to avoid constant scanning.
+ */
+ count = wpa_blacklist_add(wpa_s, bssid);
+ if (count == 1 && wpa_s->current_bss) {
+ /*
+ * This BSS was not in the blacklist before. If there is
+ * another BSS available for the same ESS, we should try that
+ * next. Otherwise, we may as well try this one once more
+ * before allowing other, likely worse, ESSes to be considered.
+ */
+ freqs = get_bss_freqs_in_ess(wpa_s);
+ if (freqs) {
+ wpa_dbg(wpa_s, MSG_DEBUG, "Another BSS in this ESS "
+ "has been seen; try it next");
+ wpa_blacklist_add(wpa_s, bssid);
+ /*
+ * On the next scan, go through only the known channels
+ * used in this ESS based on previous scans to speed up
+ * common load balancing use case.
+ */
+ os_free(wpa_s->next_scan_freqs);
+ wpa_s->next_scan_freqs = freqs;
+ }
+ }
+
+ /*
+ * Add previous failure count in case the temporary blacklist was
+ * cleared due to no other BSSes being available.
+ */
+ count += wpa_s->extra_blacklist_count;
+
+ switch (count) {
+ case 1:
+ timeout = 100;
+ break;
+ case 2:
+ timeout = 500;
+ break;
+ case 3:
+ timeout = 1000;
+ break;
+ case 4:
+ timeout = 5000;
+ break;
+ default:
+ timeout = 10000;
+ break;
+ }
+
+ wpa_dbg(wpa_s, MSG_DEBUG, "Blacklist count %d --> request scan in %d "
+ "ms", count, timeout);
+
+ /*
+ * TODO: if more than one possible AP is available in scan results,
+ * could try the other ones before requesting a new scan.
+ */
+ wpa_supplicant_req_scan(wpa_s, timeout / 1000,
+ 1000 * (timeout % 1000));
+
+#ifdef CONFIG_P2P
+ if (wpa_s->global->p2p_cb_on_scan_complete && !wpa_s->global->p2p_disabled &&
+ wpa_s->global->p2p != NULL) {
+ wpa_s->global->p2p_cb_on_scan_complete = 0;
+ if (p2p_other_scan_completed(wpa_s->global->p2p) == 1) {
+ wpa_dbg(wpa_s, MSG_DEBUG, "P2P: Pending P2P operation "
+ "continued after failed association");
+ }
+ }
+#endif /* CONFIG_P2P */
+}
+
+
+int wpas_driver_bss_selection(struct wpa_supplicant *wpa_s)
+{
+ return wpa_s->conf->ap_scan == 2 ||
+ (wpa_s->drv_flags & WPA_DRIVER_FLAGS_BSS_SELECTION);
+}
+
+
+#if defined(CONFIG_CTRL_IFACE) || defined(CONFIG_CTRL_IFACE_DBUS_NEW)
+int wpa_supplicant_ctrl_iface_ctrl_rsp_handle(struct wpa_supplicant *wpa_s,
+ struct wpa_ssid *ssid,
+ const char *field,
+ const char *value)
+{
+#ifdef IEEE8021X_EAPOL
+ struct eap_peer_config *eap = &ssid->eap;
+
+ wpa_printf(MSG_DEBUG, "CTRL_IFACE: response handle field=%s", field);
+ wpa_hexdump_ascii_key(MSG_DEBUG, "CTRL_IFACE: response value",
+ (const u8 *) value, os_strlen(value));
+
+ switch (wpa_supplicant_ctrl_req_from_string(field)) {
+ case WPA_CTRL_REQ_EAP_IDENTITY:
+ os_free(eap->identity);
+ eap->identity = (u8 *) os_strdup(value);
+ eap->identity_len = os_strlen(value);
+ eap->pending_req_identity = 0;
+ if (ssid == wpa_s->current_ssid)
+ wpa_s->reassociate = 1;
+ break;
+ case WPA_CTRL_REQ_EAP_PASSWORD:
+ os_free(eap->password);
+ eap->password = (u8 *) os_strdup(value);
+ eap->password_len = os_strlen(value);
+ eap->pending_req_password = 0;
+ if (ssid == wpa_s->current_ssid)
+ wpa_s->reassociate = 1;
+ break;
+ case WPA_CTRL_REQ_EAP_NEW_PASSWORD:
+ os_free(eap->new_password);
+ eap->new_password = (u8 *) os_strdup(value);
+ eap->new_password_len = os_strlen(value);
+ eap->pending_req_new_password = 0;
+ if (ssid == wpa_s->current_ssid)
+ wpa_s->reassociate = 1;
+ break;
+ case WPA_CTRL_REQ_EAP_PIN:
+ os_free(eap->pin);
+ eap->pin = os_strdup(value);
+ eap->pending_req_pin = 0;
+ if (ssid == wpa_s->current_ssid)
+ wpa_s->reassociate = 1;
+ break;
+ case WPA_CTRL_REQ_EAP_OTP:
+ os_free(eap->otp);
+ eap->otp = (u8 *) os_strdup(value);
+ eap->otp_len = os_strlen(value);
+ os_free(eap->pending_req_otp);
+ eap->pending_req_otp = NULL;
+ eap->pending_req_otp_len = 0;
+ break;
+ case WPA_CTRL_REQ_EAP_PASSPHRASE:
+ os_free(eap->private_key_passwd);
+ eap->private_key_passwd = (u8 *) os_strdup(value);
+ eap->pending_req_passphrase = 0;
+ if (ssid == wpa_s->current_ssid)
+ wpa_s->reassociate = 1;
+ break;
+ default:
+ wpa_printf(MSG_DEBUG, "CTRL_IFACE: Unknown field '%s'", field);
+ return -1;
+ }
+
+ return 0;
+#else /* IEEE8021X_EAPOL */
+ wpa_printf(MSG_DEBUG, "CTRL_IFACE: IEEE 802.1X not included");
+ return -1;
+#endif /* IEEE8021X_EAPOL */
+}
+#endif /* CONFIG_CTRL_IFACE || CONFIG_CTRL_IFACE_DBUS_NEW */
+
+
+int wpas_network_disabled(struct wpa_supplicant *wpa_s, struct wpa_ssid *ssid)
+{
+ int i;
+ unsigned int drv_enc;
+
+ if (ssid == NULL)
+ return 1;
+
+ if (ssid->disabled)
+ return 1;
+
+ if (wpa_s && wpa_s->drv_capa_known)
+ drv_enc = wpa_s->drv_enc;
+ else
+ drv_enc = (unsigned int) -1;
+
+ for (i = 0; i < NUM_WEP_KEYS; i++) {
+ size_t len = ssid->wep_key_len[i];
+ if (len == 0)
+ continue;
+ if (len == 5 && (drv_enc & WPA_DRIVER_CAPA_ENC_WEP40))
+ continue;
+ if (len == 13 && (drv_enc & WPA_DRIVER_CAPA_ENC_WEP104))
+ continue;
+ if (len == 16 && (drv_enc & WPA_DRIVER_CAPA_ENC_WEP128))
+ continue;
+ return 1; /* invalid WEP key */
+ }
+
+ if (wpa_key_mgmt_wpa_psk(ssid->key_mgmt) && !ssid->psk_set &&
+ !ssid->ext_psk)
+ return 1;
+
+ return 0;
+}
+
+
+int wpas_is_p2p_prioritized(struct wpa_supplicant *wpa_s)
+{
+ if (wpa_s->global->conc_pref == WPA_CONC_PREF_P2P)
+ return 1;
+ if (wpa_s->global->conc_pref == WPA_CONC_PREF_STA)
+ return 0;
+ return -1;
+}
+
+
+void wpas_auth_failed(struct wpa_supplicant *wpa_s)
+{
+ struct wpa_ssid *ssid = wpa_s->current_ssid;
+ int dur;
+ struct os_time now;
+
+ if (ssid == NULL) {
+ wpa_printf(MSG_DEBUG, "Authentication failure but no known "
+ "SSID block");
+ return;
+ }
+
+ if (ssid->key_mgmt == WPA_KEY_MGMT_WPS)
+ return;
+
+ ssid->auth_failures++;
+ if (ssid->auth_failures > 50)
+ dur = 300;
+ else if (ssid->auth_failures > 20)
+ dur = 120;
+ else if (ssid->auth_failures > 10)
+ dur = 60;
+ else if (ssid->auth_failures > 5)
+ dur = 30;
+ else if (ssid->auth_failures > 1)
+ dur = 20;
+ else
+ dur = 10;
+
+ os_get_time(&now);
+ if (now.sec + dur <= ssid->disabled_until.sec)
+ return;
+
+ ssid->disabled_until.sec = now.sec + dur;
+
+ wpa_msg(wpa_s, MSG_INFO, WPA_EVENT_TEMP_DISABLED
+ "id=%d ssid=\"%s\" auth_failures=%u duration=%d",
+ ssid->id, wpa_ssid_txt(ssid->ssid, ssid->ssid_len),
+ ssid->auth_failures, dur);
+}
+
+
+void wpas_clear_temp_disabled(struct wpa_supplicant *wpa_s,
+ struct wpa_ssid *ssid, int clear_failures)
+{
+ if (ssid == NULL)
+ return;
+
+ if (ssid->disabled_until.sec) {
+ wpa_msg(wpa_s, MSG_INFO, WPA_EVENT_REENABLED
+ "id=%d ssid=\"%s\"",
+ ssid->id, wpa_ssid_txt(ssid->ssid, ssid->ssid_len));
+ }
+ ssid->disabled_until.sec = 0;
+ ssid->disabled_until.usec = 0;
+ if (clear_failures)
+ ssid->auth_failures = 0;
+}
+
+
+int disallowed_bssid(struct wpa_supplicant *wpa_s, const u8 *bssid)
+{
+ size_t i;
+
+ if (wpa_s->disallow_aps_bssid == NULL)
+ return 0;
+
+ for (i = 0; i < wpa_s->disallow_aps_bssid_count; i++) {
+ if (os_memcmp(wpa_s->disallow_aps_bssid + i * ETH_ALEN,
+ bssid, ETH_ALEN) == 0)
+ return 1;
+ }
+
+ return 0;
+}
+
+
+int disallowed_ssid(struct wpa_supplicant *wpa_s, const u8 *ssid,
+ size_t ssid_len)
+{
+ size_t i;
+
+ if (wpa_s->disallow_aps_ssid == NULL || ssid == NULL)
+ return 0;
+
+ for (i = 0; i < wpa_s->disallow_aps_ssid_count; i++) {
+ struct wpa_ssid_value *s = &wpa_s->disallow_aps_ssid[i];
+ if (ssid_len == s->ssid_len &&
+ os_memcmp(ssid, s->ssid, ssid_len) == 0)
+ return 1;
+ }
+
+ return 0;
+}
+
+
+/**
+ * wpas_request_connection - Request a new connection
+ * @wpa_s: Pointer to the network interface
+ *
+ * This function is used to request a new connection to be found. It will mark
+ * the interface to allow reassociation and request a new scan to find a
+ * suitable network to connect to.
+ */
+void wpas_request_connection(struct wpa_supplicant *wpa_s)
+{
+ wpa_s->normal_scans = 0;
+ wpa_supplicant_reinit_autoscan(wpa_s);
+ wpa_s->extra_blacklist_count = 0;
+ wpa_s->disconnected = 0;
+ wpa_s->reassociate = 1;
+ wpa_supplicant_req_scan(wpa_s, 0, 0);
}
diff --git a/contrib/wpa/wpa_supplicant/wpa_supplicant.conf b/contrib/wpa/wpa_supplicant/wpa_supplicant.conf
index 1b175ad..9ad3f8b 100644
--- a/contrib/wpa/wpa_supplicant/wpa_supplicant.conf
+++ b/contrib/wpa/wpa_supplicant/wpa_supplicant.conf
@@ -30,7 +30,7 @@
# Parameters for the control interface. If this is specified, wpa_supplicant
# will open a control interface that is available for external programs to
# manage wpa_supplicant. The meaning of this string depends on which control
-# interface mechanism is used. For all cases, the existance of this parameter
+# interface mechanism is used. For all cases, the existence of this parameter
# in configuration is used to determine whether the control interface is
# enabled.
#
@@ -190,8 +190,12 @@ fast_reauth=1
# Config Methods
# List of the supported configuration methods
# Available methods: usba ethernet label display ext_nfc_token int_nfc_token
-# nfc_interface push_button keypad
+# nfc_interface push_button keypad virtual_display physical_display
+# virtual_push_button physical_push_button
+# For WSC 1.0:
#config_methods=label display push_button keypad
+# For WSC 2.0:
+#config_methods=label virtual_display virtual_push_button keypad
# Credential processing
# 0 = process received credentials internally (default)
@@ -201,6 +205,22 @@ fast_reauth=1
# to external program(s)
#wps_cred_processing=0
+# Vendor attribute in WPS M1, e.g., Windows 7 Vertical Pairing
+# The vendor attribute contents to be added in M1 (hex string)
+#wps_vendor_ext_m1=000137100100020001
+
+# NFC password token for WPS
+# These parameters can be used to configure a fixed NFC password token for the
+# station. This can be generated, e.g., with nfc_pw_token. When these
+# parameters are used, the station is assumed to be deployed with a NFC tag
+# that includes the matching NFC password token (e.g., written based on the
+# NDEF record from nfc_pw_token).
+#
+#wps_nfc_dev_pw_id: Device Password ID (16..65535)
+#wps_nfc_dh_pubkey: Hexdump of DH Public Key
+#wps_nfc_dh_privkey: Hexdump of DH Private Key
+#wps_nfc_dev_pw: Hexdump of Device Password
+
# Maximum number of BSS entries to keep in memory
# Default: 200
# This can be used to limit memory use on the BSS entries (cached scan
@@ -208,12 +228,191 @@ fast_reauth=1
# of APs when using ap_scan=1 mode.
#bss_max_count=200
+# Automatic scan
+# This is an optional set of parameters for automatic scanning
+# within an interface in following format:
+#autoscan=<autoscan module name>:<module parameters>
+# autoscan is like bgscan but on disconnected or inactive state.
+# For instance, on exponential module parameters would be <base>:<limit>
+#autoscan=exponential:3:300
+# Which means a delay between scans on a base exponential of 3,
+# up to the limit of 300 seconds (3, 9, 27 ... 300)
+# For periodic module, parameters would be <fixed interval>
+#autoscan=periodic:30
+# So a delay of 30 seconds will be applied between each scan
# filter_ssids - SSID-based scan result filtering
# 0 = do not filter scan results (default)
# 1 = only include configured SSIDs in scan results/BSS table
#filter_ssids=0
+# Password (and passphrase, etc.) backend for external storage
+# format: <backend name>[:<optional backend parameters>]
+#ext_password_backend=test:pw1=password|pw2=testing
+
+# Timeout in seconds to detect STA inactivity (default: 300 seconds)
+#
+# This timeout value is used in P2P GO mode to clean up
+# inactive stations.
+#p2p_go_max_inactivity=300
+
+# Opportunistic Key Caching (also known as Proactive Key Caching) default
+# This parameter can be used to set the default behavior for the
+# proactive_key_caching parameter. By default, OKC is disabled unless enabled
+# with the global okc=1 parameter or with the per-network
+# proactive_key_caching=1 parameter. With okc=1, OKC is enabled by default, but
+# can be disabled with per-network proactive_key_caching=0 parameter.
+#okc=0
+
+# Protected Management Frames default
+# This parameter can be used to set the default behavior for the ieee80211w
+# parameter. By default, PMF is disabled unless enabled with the global pmf=1/2
+# parameter or with the per-network ieee80211w=1/2 parameter. With pmf=1/2, PMF
+# is enabled/required by default, but can be disabled with the per-network
+# ieee80211w parameter.
+#pmf=0
+
+# Interworking (IEEE 802.11u)
+
+# Enable Interworking
+# interworking=1
+
+# Homogenous ESS identifier
+# If this is set, scans will be used to request response only from BSSes
+# belonging to the specified Homogeneous ESS. This is used only if interworking
+# is enabled.
+# hessid=00:11:22:33:44:55
+
+# Automatic network selection behavior
+# 0 = do not automatically go through Interworking network selection
+# (i.e., require explicit interworking_select command for this; default)
+# 1 = perform Interworking network selection if one or more
+# credentials have been configured and scan did not find a
+# matching network block
+#auto_interworking=0
+
+# credential block
+#
+# Each credential used for automatic network selection is configured as a set
+# of parameters that are compared to the information advertised by the APs when
+# interworking_select and interworking_connect commands are used.
+#
+# credential fields:
+#
+# priority: Priority group
+# By default, all networks and credentials get the same priority group
+# (0). This field can be used to give higher priority for credentials
+# (and similarly in struct wpa_ssid for network blocks) to change the
+# Interworking automatic networking selection behavior. The matching
+# network (based on either an enabled network block or a credential)
+# with the highest priority value will be selected.
+#
+# pcsc: Use PC/SC and SIM/USIM card
+#
+# realm: Home Realm for Interworking
+#
+# username: Username for Interworking network selection
+#
+# password: Password for Interworking network selection
+#
+# ca_cert: CA certificate for Interworking network selection
+#
+# client_cert: File path to client certificate file (PEM/DER)
+# This field is used with Interworking networking selection for a case
+# where client certificate/private key is used for authentication
+# (EAP-TLS). Full path to the file should be used since working
+# directory may change when wpa_supplicant is run in the background.
+#
+# Alternatively, a named configuration blob can be used by setting
+# this to blob://blob_name.
+#
+# private_key: File path to client private key file (PEM/DER/PFX)
+# When PKCS#12/PFX file (.p12/.pfx) is used, client_cert should be
+# commented out. Both the private key and certificate will be read
+# from the PKCS#12 file in this case. Full path to the file should be
+# used since working directory may change when wpa_supplicant is run
+# in the background.
+#
+# Windows certificate store can be used by leaving client_cert out and
+# configuring private_key in one of the following formats:
+#
+# cert://substring_to_match
+#
+# hash://certificate_thumbprint_in_hex
+#
+# For example: private_key="hash://63093aa9c47f56ae88334c7b65a4"
+#
+# Note that when running wpa_supplicant as an application, the user
+# certificate store (My user account) is used, whereas computer store
+# (Computer account) is used when running wpasvc as a service.
+#
+# Alternatively, a named configuration blob can be used by setting
+# this to blob://blob_name.
+#
+# private_key_passwd: Password for private key file
+#
+# imsi: IMSI in <MCC> | <MNC> | '-' | <MSIN> format
+#
+# milenage: Milenage parameters for SIM/USIM simulator in <Ki>:<OPc>:<SQN>
+# format
+#
+# domain: Home service provider FQDN
+# This is used to compare against the Domain Name List to figure out
+# whether the AP is operated by the Home SP.
+#
+# roaming_consortium: Roaming Consortium OI
+# If roaming_consortium_len is non-zero, this field contains the
+# Roaming Consortium OI that can be used to determine which access
+# points support authentication with this credential. This is an
+# alternative to the use of the realm parameter. When using Roaming
+# Consortium to match the network, the EAP parameters need to be
+# pre-configured with the credential since the NAI Realm information
+# may not be available or fetched.
+#
+# eap: Pre-configured EAP method
+# This optional field can be used to specify which EAP method will be
+# used with this credential. If not set, the EAP method is selected
+# automatically based on ANQP information (e.g., NAI Realm).
+#
+# phase1: Pre-configure Phase 1 (outer authentication) parameters
+# This optional field is used with like the 'eap' parameter.
+#
+# phase2: Pre-configure Phase 2 (inner authentication) parameters
+# This optional field is used with like the 'eap' parameter.
+#
+# excluded_ssid: Excluded SSID
+# This optional field can be used to excluded specific SSID(s) from
+# matching with the network. Multiple entries can be used to specify more
+# than one SSID.
+#
+# for example:
+#
+#cred={
+# realm="example.com"
+# username="user@example.com"
+# password="password"
+# ca_cert="/etc/wpa_supplicant/ca.pem"
+# domain="example.com"
+#}
+#
+#cred={
+# imsi="310026-000000000"
+# milenage="90dca4eda45b53cf0f12d7c9c3bc6a89:cb9cccc4b9258e6dca4760379fb82"
+#}
+#
+#cred={
+# realm="example.com"
+# username="user"
+# password="password"
+# ca_cert="/etc/wpa_supplicant/ca.pem"
+# domain="example.com"
+# roaming_consortium=223344
+# eap=TTLS
+# phase2="auth=MSCHAPV2"
+#}
+
+# Hotspot 2.0
+# hs20=1
# network block
#
@@ -232,8 +431,10 @@ fast_reauth=1
# to external action script through wpa_cli as WPA_ID_STR environment
# variable to make it easier to do network specific configuration.
#
-# ssid: SSID (mandatory); either as an ASCII string with double quotation or
-# as hex string; network name
+# ssid: SSID (mandatory); network name in one of the optional formats:
+# - an ASCII string with double quotation
+# - a hex string (two characters per octet of SSID)
+# - a printf-escaped ASCII string P"<escaped string>"
#
# scan_ssid:
# 0 = do not scan this SSID with specific Probe Request frames (default)
@@ -284,6 +485,23 @@ fast_reauth=1
# set, scan results that do not match any of the specified frequencies are not
# considered when selecting a BSS.
#
+# bgscan: Background scanning
+# wpa_supplicant behavior for background scanning can be specified by
+# configuring a bgscan module. These modules are responsible for requesting
+# background scans for the purpose of roaming within an ESS (i.e., within a
+# single network block with all the APs using the same SSID). The bgscan
+# parameter uses following format: "<bgscan module name>:<module parameters>"
+# Following bgscan modules are available:
+# simple - Periodic background scans based on signal strength
+# bgscan="simple:<short bgscan interval in seconds>:<signal strength threshold>:
+# <long interval>"
+# bgscan="simple:30:-45:300"
+# learn - Learn channels used by the network and try to avoid bgscans on other
+# channels (experimental)
+# bgscan="learn:<short bgscan interval in seconds>:<signal strength threshold>:
+# <long interval>[:<database file name>]"
+# bgscan="learn:30:-45:300:/etc/wpa_supplicant/network1.bgscan"
+#
# proto: list of accepted protocols
# WPA = WPA/IEEE 802.11i/D3.0
# RSN = WPA2/IEEE 802.11i (also WPA2 can be used as an alias for RSN)
@@ -299,6 +517,16 @@ fast_reauth=1
# WPA-EAP-SHA256 = Like WPA-EAP but using stronger SHA256-based algorithms
# If not set, this defaults to: WPA-PSK WPA-EAP
#
+# ieee80211w: whether management frame protection is enabled
+# 0 = disabled (default unless changed with the global pmf parameter)
+# 1 = optional
+# 2 = required
+# The most common configuration options for this based on the PMF (protected
+# management frames) certification program are:
+# PMF enabled: ieee80211w=1 and key_mgmt=WPA-EAP WPA-EAP-SHA256
+# PMF required: ieee80211w=2 and key_mgmt=WPA-EAP-SHA256
+# (and similarly for WPA-PSK and WPA-WPSK-SHA256 if WPA2-Personal is used)
+#
# auth_alg: list of allowed IEEE 802.11 authentication algorithms
# OPEN = Open System authentication (required for WPA/WPA2)
# SHARED = Shared Key authentication (requires static WEP keys)
@@ -324,7 +552,8 @@ fast_reauth=1
# The key used in WPA-PSK mode can be entered either as 64 hex-digits, i.e.,
# 32 bytes or as an ASCII passphrase (in which case, the real PSK will be
# generated using the passphrase and SSID). ASCII passphrase must be between
-# 8 and 63 characters (inclusive).
+# 8 and 63 characters (inclusive). ext:<name of external PSK field> format can
+# be used to indicate that the PSK/passphrase is stored in external storage.
# This field is not needed, if WPA-EAP is used.
# Note: Separate tool, wpa_passphrase, can be used to generate 256-bit keys
# from ASCII passphrase. This process uses lot of CPU and wpa_supplicant
@@ -341,13 +570,13 @@ fast_reauth=1
#
# mixed_cell: This option can be used to configure whether so called mixed
# cells, i.e., networks that use both plaintext and encryption in the same
-# SSID, are allowed when selecting a BSS form scan results.
+# SSID, are allowed when selecting a BSS from scan results.
# 0 = disabled (default)
# 1 = enabled
#
# proactive_key_caching:
# Enable/disable opportunistic PMKSA caching for WPA2.
-# 0 = disabled (default)
+# 0 = disabled (default unless changed with the global okc parameter)
# 1 = enabled
#
# wep_key0..3: Static WEP key (ASCII in double quotation, e.g. "abcde" or
@@ -385,7 +614,8 @@ fast_reauth=1
# EAP-PSK/PAX/SAKE/GPSK.
# anonymous_identity: Anonymous identity string for EAP (to be used as the
# unencrypted identity with EAP types that support different tunnelled
-# identity, e.g., EAP-TTLS)
+# identity, e.g., EAP-TTLS). This field can also be used with
+# EAP-SIM/AKA/AKA' to store the pseudonym identity.
# password: Password string for EAP. This field can include either the
# plaintext password (using ASCII or hex string) or a NtPasswordHash
# (16-byte MD4 hash of password) in hash:<32 hex digits> format.
@@ -393,7 +623,8 @@ fast_reauth=1
# MSCHAP (EAP-MSCHAPv2, EAP-TTLS/MSCHAPv2, EAP-TTLS/MSCHAP, LEAP).
# EAP-PSK (128-bit PSK), EAP-PAX (128-bit PSK), and EAP-SAKE (256-bit
# PSK) is also configured using this field. For EAP-GPSK, this is a
-# variable length PSK.
+# variable length PSK. ext:<name of external password field> format can
+# be used to indicate that the password is stored in external storage.
# ca_cert: File path to CA certificate file (PEM/DER). This file can have one
# or more trusted CA certificates. If ca_cert and ca_path are not
# included, server certificate will not be verified. This is insecure and
@@ -496,6 +727,25 @@ fast_reauth=1
# phase2: Phase2 (inner authentication with TLS tunnel) parameters
# (string with field-value pairs, e.g., "auth=MSCHAPV2" for EAP-PEAP or
# "autheap=MSCHAPV2 autheap=MD5" for EAP-TTLS)
+#
+# TLS-based methods can use the following parameters to control TLS behavior
+# (these are normally in the phase1 parameter, but can be used also in the
+# phase2 parameter when EAP-TLS is used within the inner tunnel):
+# tls_allow_md5=1 - allow MD5-based certificate signatures (depending on the
+# TLS library, these may be disabled by default to enforce stronger
+# security)
+# tls_disable_time_checks=1 - ignore certificate validity time (this requests
+# the TLS library to accept certificates even if they are not currently
+# valid, i.e., have expired or have not yet become valid; this should be
+# used only for testing purposes)
+# tls_disable_session_ticket=1 - disable TLS Session Ticket extension
+# tls_disable_session_ticket=0 - allow TLS Session Ticket extension to be used
+# Note: If not set, this is automatically set to 1 for EAP-TLS/PEAP/TTLS
+# as a workaround for broken authentication server implementations unless
+# EAP workarounds are disabled with eap_workarounds=0.
+# For EAP-FAST, this must be set to 0 (or left unconfigured for the
+# default value to be used automatically).
+#
# Following certificate/private key fields are used in inner Phase2
# authentication when using EAP-TTLS or EAP-PEAP.
# ca_cert2: File path to CA certificate file. This file can have one or more
@@ -545,6 +795,54 @@ fast_reauth=1
# number of authentication servers. Strict EAP conformance mode can be
# configured by disabling workarounds with eap_workaround=0.
+# Station inactivity limit
+#
+# If a station does not send anything in ap_max_inactivity seconds, an
+# empty data frame is sent to it in order to verify whether it is
+# still in range. If this frame is not ACKed, the station will be
+# disassociated and then deauthenticated. This feature is used to
+# clear station table of old entries when the STAs move out of the
+# range.
+#
+# The station can associate again with the AP if it is still in range;
+# this inactivity poll is just used as a nicer way of verifying
+# inactivity; i.e., client will not report broken connection because
+# disassociation frame is not sent immediately without first polling
+# the STA with a data frame.
+# default: 300 (i.e., 5 minutes)
+#ap_max_inactivity=300
+
+# DTIM period in Beacon intervals for AP mode (default: 2)
+#dtim_period=2
+
+# disable_ht: Whether HT (802.11n) should be disabled.
+# 0 = HT enabled (if AP supports it)
+# 1 = HT disabled
+#
+# disable_ht40: Whether HT-40 (802.11n) should be disabled.
+# 0 = HT-40 enabled (if AP supports it)
+# 1 = HT-40 disabled
+#
+# disable_sgi: Whether SGI (short guard interval) should be disabled.
+# 0 = SGI enabled (if AP supports it)
+# 1 = SGI disabled
+#
+# ht_mcs: Configure allowed MCS rates.
+# Parsed as an array of bytes, in base-16 (ascii-hex)
+# ht_mcs="" // Use all available (default)
+# ht_mcs="0xff 00 00 00 00 00 00 00 00 00 " // Use MCS 0-7 only
+# ht_mcs="0xff ff 00 00 00 00 00 00 00 00 " // Use MCS 0-15 only
+#
+# disable_max_amsdu: Whether MAX_AMSDU should be disabled.
+# -1 = Do not make any changes.
+# 0 = Enable MAX-AMSDU if hardware supports it.
+# 1 = Disable AMSDU
+#
+# ampdu_density: Allow overriding AMPDU density configuration.
+# Treated as hint by the kernel.
+# -1 = Do not make any changes.
+# 0-3 = Set AMPDU density (aka factor) to specified value.
+
# Example blocks:
# Simple case: WPA-PSK, PSK as an ASCII passphrase, allow all valid ciphers
diff --git a/contrib/wpa/wpa_supplicant/wpa_supplicant.nsi b/contrib/wpa/wpa_supplicant/wpa_supplicant.nsi
deleted file mode 100644
index b9f0162..0000000
--- a/contrib/wpa/wpa_supplicant/wpa_supplicant.nsi
+++ /dev/null
@@ -1,112 +0,0 @@
-!define PRODUCT_NAME "wpa_supplicant"
-!define PRODUCT_VERSION "@WPAVER@"
-!define PRODUCT_PUBLISHER "Jouni Malinen"
-
-Name "${PRODUCT_NAME} ${PRODUCT_VERSION}"
-outfile "../wpa_supplicant-@WPAVER@.exe"
-
-installDir "$PROGRAMFILES\wpa_supplicant"
-
-Page Directory
-Page InstFiles
-
-section -Prerequisites
- SetOutPath $INSTDIR\Prerequisites
- MessageBox MB_YESNO "Install WinPcap?" /SD IDYES IDNO endWinPcap
- File "/opt/Qt-Win/files/WinPcap_4_1_2.exe"
- ExecWait "$INSTDIR\Prerequisites\WinPcap_4_1_2.exe"
- Goto endWinPcap
- endWinPcap:
-sectionEnd
-
-
-section
- setOutPath $INSTDIR
-
- File wpa_gui.exe
- File wpa_gui_de.qm
- File wpa_cli.exe
- File COPYING
- File README
- File README-Windows.txt
- File win_example.reg
- File win_if_list.exe
- File wpa_passphrase.exe
- File wpa_supplicant.conf
- File wpa_supplicant.exe
- File wpasvc.exe
-
- File /opt/Qt-Win/files/mingwm10.dll
- File /opt/Qt-Win/files/libgcc_s_dw2-1.dll
- File /opt/Qt-Win/files/QtCore4.dll
- File /opt/Qt-Win/files/QtGui4.dll
-
- WriteRegDWORD HKLM "Software\wpa_supplicant" "debug_level" 0
- WriteRegDWORD HKLM "Software\wpa_supplicant" "debug_show_keys" 0
- WriteRegDWORD HKLM "Software\wpa_supplicant" "debug_timestamp" 0
- WriteRegDWORD HKLM "Software\wpa_supplicant" "debug_use_file" 0
-
- WriteRegDWORD HKLM "Software\wpa_supplicant\configs\default" "ap_scan" 2
- WriteRegDWORD HKLM "Software\wpa_supplicant\configs\default" "update_config" 1
- WriteRegDWORD HKLM "Software\wpa_supplicant\configs\default\networks" "dummy" 1
- DeleteRegValue HKLM "Software\wpa_supplicant\configs\default\networks" "dummy"
-
- WriteRegDWORD HKLM "Software\wpa_supplicant\interfaces" "dummy" 1
- DeleteRegValue HKLM "Software\wpa_supplicant\interfaces" "dummy"
-
- writeUninstaller "$INSTDIR\uninstall.exe"
-
- WriteRegStr HKLM "Software\Microsoft\Windows\CurrentVersion\Uninstall\wpa_supplicant" \
- "DisplayName" "wpa_supplicant"
-WriteRegStr HKLM "Software\Microsoft\Windows\CurrentVersion\Uninstall\wpa_supplicant" \
- "UninstallString" "$INSTDIR\uninstall.exe"
-
- CreateDirectory "$SMPROGRAMS\wpa_supplicant"
- CreateShortCut "$SMPROGRAMS\wpa_supplicant\wpa_gui.lnk" "$INSTDIR\wpa_gui.exe"
- CreateShortCut "$SMPROGRAMS\wpa_supplicant\Uninstall.lnk" "$INSTDIR\uninstall.exe"
-
- ExecWait "$INSTDIR\wpasvc.exe reg"
-sectionEnd
-
-
-Function un.onInit
- MessageBox MB_YESNO "This will uninstall wpa_supplicant. Continue?" IDYES NoAbort
- Abort
- NoAbort:
-FunctionEnd
-
-section "uninstall"
- DeleteRegKey HKLM "Software\Microsoft\Windows\CurrentVersion\Uninstall\wpa_supplicant"
- delete "$INSTDIR\uninstall.exe"
-
- ExecWait "$INSTDIR\wpasvc.exe unreg"
-
- DeleteRegKey HKLM "Software\wpa_supplicant"
-
- delete "$INSTDIR\wpa_gui.exe"
- delete "$INSTDIR\wpa_gui_de.qm"
- delete "$INSTDIR\wpa_cli.exe"
- delete "$INSTDIR\COPYING"
- delete "$INSTDIR\README"
- delete "$INSTDIR\README-Windows.txt"
- delete "$INSTDIR\win_example.reg"
- delete "$INSTDIR\win_if_list.exe"
- delete "$INSTDIR\wpa_passphrase.exe"
- delete "$INSTDIR\wpa_supplicant.conf"
- delete "$INSTDIR\wpa_supplicant.exe"
- delete "$INSTDIR\wpasvc.exe"
-
- delete "$INSTDIR\mingwm10.dll"
- delete "$INSTDIR\libgcc_s_dw2-1.dll"
- delete "$INSTDIR\QtCore4.dll"
- delete "$INSTDIR\QtGui4.dll"
-
- delete "$INSTDIR\Prerequisites\WinPcap_4_1_2.exe"
- rmdir "$INSTDIR\Prerequisites"
-
- rmdir "$INSTDIR"
-
- delete "$SMPROGRAMS\wpa_supplicant\wpa_gui.lnk"
- delete "$SMPROGRAMS\wpa_supplicant\Uninstall.lnk"
- rmdir "$SMPROGRAMS\wpa_supplicant"
-sectionEnd
diff --git a/contrib/wpa/wpa_supplicant/wpa_supplicant_conf.mk b/contrib/wpa/wpa_supplicant/wpa_supplicant_conf.mk
new file mode 100644
index 0000000..74986ea
--- /dev/null
+++ b/contrib/wpa/wpa_supplicant/wpa_supplicant_conf.mk
@@ -0,0 +1,34 @@
+#
+# Copyright (C) 2010 The Android Open Source Project
+#
+# This software may be distributed under the terms of the BSD license.
+# See README for more details.
+#
+
+# Include this makefile to generate your hardware specific wpa_supplicant.conf
+# Requires: WIFI_DRIVER_SOCKET_IFACE
+
+LOCAL_PATH := $(call my-dir)
+
+########################
+include $(CLEAR_VARS)
+
+LOCAL_MODULE := wpa_supplicant.conf
+LOCAL_MODULE_CLASS := ETC
+LOCAL_MODULE_TAGS := optional
+LOCAL_MODULE_PATH := $(TARGET_OUT_ETC)/wifi
+
+include $(BUILD_SYSTEM)/base_rules.mk
+
+WPA_SUPPLICANT_CONF_TEMPLATE := $(LOCAL_PATH)/wpa_supplicant_template.conf
+WPA_SUPPLICANT_CONF_SCRIPT := $(LOCAL_PATH)/wpa_supplicant_conf.sh
+$(LOCAL_BUILT_MODULE): PRIVATE_WIFI_DRIVER_SOCKET_IFACE := $(WIFI_DRIVER_SOCKET_IFACE)
+$(LOCAL_BUILT_MODULE): PRIVATE_WPA_SUPPLICANT_CONF_TEMPLATE := $(WPA_SUPPLICANT_CONF_TEMPLATE)
+$(LOCAL_BUILT_MODULE): PRIVATE_WPA_SUPPLICANT_CONF_SCRIPT := $(WPA_SUPPLICANT_CONF_SCRIPT)
+$(LOCAL_BUILT_MODULE) : $(WPA_SUPPLICANT_CONF_TEMPLATE) $(WPA_SUPPLICANT_CONF_SCRIPT)
+ @echo Target wpa_supplicant.conf: $@
+ @mkdir -p $(dir $@)
+ $(hide) WIFI_DRIVER_SOCKET_IFACE="$(PRIVATE_WIFI_DRIVER_SOCKET_IFACE)" \
+ bash $(PRIVATE_WPA_SUPPLICANT_CONF_SCRIPT) $(PRIVATE_WPA_SUPPLICANT_CONF_TEMPLATE) > $@
+
+########################
diff --git a/contrib/wpa/wpa_supplicant/wpa_supplicant_conf.sh b/contrib/wpa/wpa_supplicant/wpa_supplicant_conf.sh
new file mode 100755
index 0000000..f36eef1
--- /dev/null
+++ b/contrib/wpa/wpa_supplicant/wpa_supplicant_conf.sh
@@ -0,0 +1,16 @@
+#!/bin/bash
+#
+# Copyright (C) 2010 The Android Open Source Project
+#
+# This software may be distributed under the terms of the BSD license.
+# See README for more details.
+#
+
+# Generate a wpa_supplicant.conf from the template.
+# $1: the template file name
+if [ -n "$WIFI_DRIVER_SOCKET_IFACE" ]
+then
+ sed -e 's/#.*$//' -e 's/[ \t]*$//' -e '/^$/d' < $1 | sed -e "s/wlan0/$WIFI_DRIVER_SOCKET_IFACE/"
+else
+ sed -e 's/#.*$//' -e 's/[ \t]*$//' -e '/^$/d' < $1
+fi
diff --git a/contrib/wpa/wpa_supplicant/wpa_supplicant_i.h b/contrib/wpa/wpa_supplicant/wpa_supplicant_i.h
index 6c36a1a..544977b 100644
--- a/contrib/wpa/wpa_supplicant/wpa_supplicant_i.h
+++ b/contrib/wpa/wpa_supplicant/wpa_supplicant_i.h
@@ -1,15 +1,9 @@
/*
* wpa_supplicant - Internal definitions
- * Copyright (c) 2003-2010, Jouni Malinen <j@w1.fi>
+ * Copyright (c) 2003-2012, Jouni Malinen <j@w1.fi>
*
- * This program is free software; you can redistribute it and/or modify
- * it under the terms of the GNU General Public License version 2 as
- * published by the Free Software Foundation.
- *
- * Alternatively, this software may be distributed under the terms of BSD
- * license.
- *
- * See README and COPYING for more details.
+ * This software may be distributed under the terms of the BSD license.
+ * See README for more details.
*/
#ifndef WPA_SUPPLICANT_I_H
@@ -17,6 +11,7 @@
#include "utils/list.h"
#include "common/defs.h"
+#include "config_ssid.h"
extern const char *wpa_supplicant_version;
extern const char *wpa_supplicant_license;
@@ -34,6 +29,8 @@ struct ibss_rsn;
struct scan_info;
struct wpa_bss;
struct wpa_scan_results;
+struct hostapd_hw_modes;
+struct wpa_driver_associate_params;
/*
* Forward declarations of private structures used within the ctrl_iface
@@ -164,6 +161,11 @@ struct wpa_params {
int wpa_debug_syslog;
/**
+ * wpa_debug_tracing - Enable log output through Linux tracing
+ */
+ int wpa_debug_tracing;
+
+ /**
* override_driver - Optional driver parameter override
*
* This parameter can be used to override the driver parameter in
@@ -180,8 +182,34 @@ struct wpa_params {
* created.
*/
char *override_ctrl_interface;
+
+ /**
+ * entropy_file - Optional entropy file
+ *
+ * This parameter can be used to configure wpa_supplicant to maintain
+ * its internal entropy store over restarts.
+ */
+ char *entropy_file;
};
+struct p2p_srv_bonjour {
+ struct dl_list list;
+ struct wpabuf *query;
+ struct wpabuf *resp;
+};
+
+struct p2p_srv_upnp {
+ struct dl_list list;
+ u8 version;
+ char *service;
+};
+
+struct wpa_freq_range {
+ unsigned int min;
+ unsigned int max;
+};
+
+
/**
* struct wpa_global - Internal, global data for all %wpa_supplicant interfaces
*
@@ -196,101 +224,56 @@ struct wpa_global {
void **drv_priv;
size_t drv_count;
struct os_time suspend_time;
+ struct p2p_data *p2p;
+ struct wpa_supplicant *p2p_init_wpa_s;
+ struct wpa_supplicant *p2p_group_formation;
+ u8 p2p_dev_addr[ETH_ALEN];
+ struct dl_list p2p_srv_bonjour; /* struct p2p_srv_bonjour */
+ struct dl_list p2p_srv_upnp; /* struct p2p_srv_upnp */
+ int p2p_disabled;
+ int cross_connection;
+ struct wpa_freq_range *p2p_disallow_freq;
+ unsigned int num_p2p_disallow_freq;
+ enum wpa_conc_pref {
+ WPA_CONC_PREF_NOT_SET,
+ WPA_CONC_PREF_STA,
+ WPA_CONC_PREF_P2P
+ } conc_pref;
+ unsigned int p2p_cb_on_scan_complete:1;
+
+#ifdef CONFIG_WIFI_DISPLAY
+ int wifi_display;
+#define MAX_WFD_SUBELEMS 10
+ struct wpabuf *wfd_subelem[MAX_WFD_SUBELEMS];
+#endif /* CONFIG_WIFI_DISPLAY */
};
-struct wpa_client_mlme {
-#ifdef CONFIG_CLIENT_MLME
- enum {
- IEEE80211_DISABLED, IEEE80211_AUTHENTICATE,
- IEEE80211_ASSOCIATE, IEEE80211_ASSOCIATED,
- IEEE80211_IBSS_SEARCH, IEEE80211_IBSS_JOINED
- } state;
- u8 prev_bssid[ETH_ALEN];
+/**
+ * offchannel_send_action_result - Result of offchannel send Action frame
+ */
+enum offchannel_send_action_result {
+ OFFCHANNEL_SEND_ACTION_SUCCESS /**< Frame was send and acknowledged */,
+ OFFCHANNEL_SEND_ACTION_NO_ACK /**< Frame was sent, but not acknowledged
+ */,
+ OFFCHANNEL_SEND_ACTION_FAILED /**< Frame was not sent due to a failure
+ */
+};
+
+struct wps_ap_info {
+ u8 bssid[ETH_ALEN];
+ enum wps_ap_info_type {
+ WPS_AP_NOT_SEL_REG,
+ WPS_AP_SEL_REG,
+ WPS_AP_SEL_REG_OUR
+ } type;
+ unsigned int tries;
+ struct os_time last_attempt;
+};
+
+struct wpa_ssid_value {
u8 ssid[32];
size_t ssid_len;
- u16 aid;
- u16 ap_capab, capab;
- u8 *extra_ie; /* to be added to the end of AssocReq */
- size_t extra_ie_len;
- u8 *extra_probe_ie; /* to be added to the end of ProbeReq */
- size_t extra_probe_ie_len;
- enum wpa_key_mgmt key_mgmt;
-
- /* The last AssocReq/Resp IEs */
- u8 *assocreq_ies, *assocresp_ies;
- size_t assocreq_ies_len, assocresp_ies_len;
-
- int auth_tries, assoc_tries;
-
- unsigned int ssid_set:1;
- unsigned int bssid_set:1;
- unsigned int prev_bssid_set:1;
- unsigned int authenticated:1;
- unsigned int associated:1;
- unsigned int probereq_poll:1;
- unsigned int use_protection:1;
- unsigned int create_ibss:1;
- unsigned int mixed_cell:1;
- unsigned int wmm_enabled:1;
-
- struct os_time last_probe;
-
- unsigned int auth_algs; /* bitfield of allowed auth algs
- * (WPA_AUTH_ALG_*) */
- int auth_alg; /* currently used IEEE 802.11 authentication algorithm */
- int auth_transaction;
-
- struct os_time ibss_join_req;
- u8 *probe_resp; /* ProbeResp template for IBSS */
- size_t probe_resp_len;
- u32 supp_rates_bits;
-
- int wmm_last_param_set;
-
- int sta_scanning;
- int scan_hw_mode_idx;
- int scan_channel_idx;
- enum { SCAN_SET_CHANNEL, SCAN_SEND_PROBE } scan_state;
- struct os_time last_scan_completed;
- int scan_oper_channel;
- int scan_oper_freq;
- int scan_oper_phymode;
- u8 scan_ssid[32];
- size_t scan_ssid_len;
- int scan_skip_11b;
- int *scan_freqs;
-
- struct ieee80211_sta_bss *sta_bss_list;
-#define STA_HASH_SIZE 256
-#define STA_HASH(sta) (sta[5])
- struct ieee80211_sta_bss *sta_bss_hash[STA_HASH_SIZE];
-
- int cts_protect_erp_frames;
-
- enum hostapd_hw_mode phymode; /* current mode */
- struct hostapd_hw_modes *modes;
- size_t num_modes;
- unsigned int hw_modes; /* bitfield of allowed hardware modes;
- * (1 << HOSTAPD_MODE_*) */
- int num_curr_rates;
- int *curr_rates;
- int freq; /* The current frequency in MHz */
- int channel; /* The current IEEE 802.11 channel number */
-
-#ifdef CONFIG_IEEE80211R
- u8 current_md[6];
- u8 *ft_ies;
- size_t ft_ies_len;
-#endif /* CONFIG_IEEE80211R */
-
- void (*public_action_cb)(void *ctx, const u8 *buf, size_t len,
- int freq);
- void *public_action_cb_ctx;
-
-#else /* CONFIG_CLIENT_MLME */
- int dummy; /* to keep MSVC happy */
-#endif /* CONFIG_CLIENT_MLME */
};
/**
@@ -303,6 +286,7 @@ struct wpa_client_mlme {
*/
struct wpa_supplicant {
struct wpa_global *global;
+ struct wpa_supplicant *parent;
struct wpa_supplicant *next;
struct l2_packet_data *l2;
struct l2_packet_data *l2_br;
@@ -313,6 +297,10 @@ struct wpa_supplicant {
#endif /* CONFIG_CTRL_IFACE_DBUS */
#ifdef CONFIG_CTRL_IFACE_DBUS_NEW
char *dbus_new_path;
+ char *dbus_groupobj_path;
+#ifdef CONFIG_AP
+ char *preq_notify_peer;
+#endif /* CONFIG_AP */
#endif /* CONFIG_CTRL_IFACE_DBUS_NEW */
char bridge_ifname[16];
@@ -322,7 +310,7 @@ struct wpa_supplicant {
os_time_t last_michael_mic_error;
u8 bssid[ETH_ALEN];
u8 pending_bssid[ETH_ALEN]; /* If wpa_state == WPA_ASSOCIATING, this
- * field contains the targer BSSID. */
+ * field contains the target BSSID. */
int reassociate; /* reassociation requested */
int disconnected; /* all connections disabled; i.e., do no reassociate
* before this has been cleared */
@@ -335,11 +323,23 @@ struct wpa_supplicant {
int pairwise_cipher;
int group_cipher;
int key_mgmt;
+ int wpa_proto;
int mgmt_group_cipher;
void *drv_priv; /* private data used by driver_ops */
void *global_drv_priv;
+ u8 *bssid_filter;
+ size_t bssid_filter_count;
+
+ u8 *disallow_aps_bssid;
+ size_t disallow_aps_bssid_count;
+ struct wpa_ssid_value *disallow_aps_ssid;
+ size_t disallow_aps_ssid_count;
+
+ /* previous scan was wildcard when interleaving between
+ * wildcard scans and specific SSID scan when max_ssids=1 */
+ int prev_scan_wildcard;
struct wpa_ssid *prev_scan_ssid; /* previously scanned SSID;
* NULL = not yet initialized (start
* with wildcard SSID)
@@ -348,6 +348,12 @@ struct wpa_supplicant {
*/
#define WILDCARD_SSID_SCAN ((struct wpa_ssid *) 1)
+ struct wpa_ssid *prev_sched_ssid; /* last SSID used in sched scan */
+ int sched_scan_timeout;
+ int sched_scan_interval;
+ int first_sched_scan;
+ int sched_scan_timed_out;
+
void (*scan_res_handler)(struct wpa_supplicant *wpa_s,
struct wpa_scan_results *scan_res);
struct dl_list bss; /* struct wpa_bss::list */
@@ -356,6 +362,16 @@ struct wpa_supplicant {
unsigned int bss_update_idx;
unsigned int bss_next_id;
+ /*
+ * Pointers to BSS entries in the order they were in the last scan
+ * results.
+ */
+ struct wpa_bss **last_scan_res;
+ unsigned int last_scan_res_used;
+ unsigned int last_scan_res_size;
+ int last_scan_full;
+ struct os_time last_scan;
+
struct wpa_driver_ops *driver;
int interface_removed; /* whether the network interface has been
* removed */
@@ -366,13 +382,17 @@ struct wpa_supplicant {
enum wpa_states wpa_state;
int scanning;
+ int sched_scanning;
int new_connection;
- int reassociated_connection;
int eapol_received; /* number of EAPOL packets received after the
* previous association event */
struct scard_data *scard;
+#ifdef PCSC_FUNCS
+ char imsi[20];
+ int mnc_len;
+#endif /* PCSC_FUNCS */
unsigned char last_eapol_src[ETH_ALEN];
@@ -380,14 +400,70 @@ struct wpa_supplicant {
struct wpa_blacklist *blacklist;
- int scan_req; /* manual scan request; this forces a scan even if there
- * are no enabled networks in the configuration */
+ /**
+ * extra_blacklist_count - Sum of blacklist counts after last connection
+ *
+ * This variable is used to maintain a count of temporary blacklisting
+ * failures (maximum number for any BSS) over blacklist clear
+ * operations. This is needed for figuring out whether there has been
+ * failures prior to the last blacklist clear operation which happens
+ * whenever no other not-blacklisted BSS candidates are available. This
+ * gets cleared whenever a connection has been established successfully.
+ */
+ int extra_blacklist_count;
+
+ /**
+ * scan_req - Type of the scan request
+ */
+ enum scan_req_type {
+ /**
+ * NORMAL_SCAN_REQ - Normal scan request
+ *
+ * This is used for scans initiated by wpa_supplicant to find an
+ * AP for a connection.
+ */
+ NORMAL_SCAN_REQ,
+
+ /**
+ * INITIAL_SCAN_REQ - Initial scan request
+ *
+ * This is used for the first scan on an interface to force at
+ * least one scan to be run even if the configuration does not
+ * include any enabled networks.
+ */
+ INITIAL_SCAN_REQ,
+
+ /**
+ * MANUAL_SCAN_REQ - Manual scan request
+ *
+ * This is used for scans where the user request a scan or
+ * a specific wpa_supplicant operation (e.g., WPS) requires scan
+ * to be run.
+ */
+ MANUAL_SCAN_REQ
+ } scan_req;
int scan_runs; /* number of scan runs since WPS was started */
+ int *next_scan_freqs;
+ int scan_interval; /* time in sec between scans to find suitable AP */
+ int normal_scans; /* normal scans run before sched_scan */
+ int scan_for_connection; /* whether the scan request was triggered for
+ * finding a connection */
- struct wpa_client_mlme mlme;
unsigned int drv_flags;
+ unsigned int drv_enc;
+
+ /*
+ * A bitmap of supported protocols for probe response offload. See
+ * struct wpa_driver_capa in driver.h
+ */
+ unsigned int probe_resp_offloads;
+
int max_scan_ssids;
+ int max_sched_scan_ssids;
+ int sched_scan_supported;
+ unsigned int max_match_sets;
unsigned int max_remain_on_chan;
+ unsigned int max_stations;
int pending_mic_error_report;
int pending_mic_error_pairwise;
@@ -401,15 +477,21 @@ struct wpa_supplicant {
struct wpabuf *pending_eapol_rx;
struct os_time pending_eapol_rx_time;
u8 pending_eapol_rx_src[ETH_ALEN];
+ unsigned int last_eapol_matches_bssid:1;
struct ibss_rsn *ibss_rsn;
+ int set_sta_uapsd;
+ int sta_uapsd;
+ int set_ap_uapsd;
+ int ap_uapsd;
+
#ifdef CONFIG_SME
struct {
u8 ssid[32];
size_t ssid_len;
int freq;
- u8 assoc_req_ie[80];
+ u8 assoc_req_ie[200];
size_t assoc_req_ie_len;
int mfp;
int ft_used;
@@ -419,6 +501,24 @@ struct wpa_supplicant {
u8 prev_bssid[ETH_ALEN];
int prev_bssid_set;
int auth_alg;
+ int proto;
+
+ int sa_query_count; /* number of pending SA Query requests;
+ * 0 = no SA Query in progress */
+ int sa_query_timed_out;
+ u8 *sa_query_trans_id; /* buffer of WLAN_SA_QUERY_TR_ID_LEN *
+ * sa_query_count octets of pending
+ * SA Query transaction identifiers */
+ struct os_time sa_query_start;
+ u8 sched_obss_scan;
+ u16 obss_scan_int;
+ u16 bss_max_idle_period;
+ enum {
+ SME_SAE_INIT,
+ SME_SAE_COMMIT,
+ SME_SAE_CONFIRM
+ } sae_state;
+ u16 sae_send_confirm;
} sme;
#endif /* CONFIG_SME */
@@ -429,23 +529,165 @@ struct wpa_supplicant {
void *ap_configured_cb_data;
#endif /* CONFIG_AP */
+ unsigned int off_channel_freq;
+ struct wpabuf *pending_action_tx;
+ u8 pending_action_src[ETH_ALEN];
+ u8 pending_action_dst[ETH_ALEN];
+ u8 pending_action_bssid[ETH_ALEN];
+ unsigned int pending_action_freq;
+ int pending_action_no_cck;
+ int pending_action_without_roc;
+ void (*pending_action_tx_status_cb)(struct wpa_supplicant *wpa_s,
+ unsigned int freq, const u8 *dst,
+ const u8 *src, const u8 *bssid,
+ const u8 *data, size_t data_len,
+ enum offchannel_send_action_result
+ result);
+ unsigned int roc_waiting_drv_freq;
+ int action_tx_wait_time;
+
+#ifdef CONFIG_P2P
+ struct p2p_go_neg_results *go_params;
+ int create_p2p_iface;
+ u8 pending_interface_addr[ETH_ALEN];
+ char pending_interface_name[100];
+ int pending_interface_type;
+ int p2p_group_idx;
+ unsigned int pending_listen_freq;
+ unsigned int pending_listen_duration;
+ enum {
+ NOT_P2P_GROUP_INTERFACE,
+ P2P_GROUP_INTERFACE_PENDING,
+ P2P_GROUP_INTERFACE_GO,
+ P2P_GROUP_INTERFACE_CLIENT
+ } p2p_group_interface;
+ struct p2p_group *p2p_group;
+ int p2p_long_listen; /* remaining time in long Listen state in ms */
+ char p2p_pin[10];
+ int p2p_wps_method;
+ u8 p2p_auth_invite[ETH_ALEN];
+ int p2p_sd_over_ctrl_iface;
+ int p2p_in_provisioning;
+ int pending_invite_ssid_id;
+ int show_group_started;
+ u8 go_dev_addr[ETH_ALEN];
+ int pending_pd_before_join;
+ u8 pending_join_iface_addr[ETH_ALEN];
+ u8 pending_join_dev_addr[ETH_ALEN];
+ int pending_join_wps_method;
+ int p2p_join_scan_count;
+ int auto_pd_scan_retry;
+ int force_long_sd;
+ u16 pending_pd_config_methods;
+ enum {
+ NORMAL_PD, AUTO_PD_GO_NEG, AUTO_PD_JOIN
+ } pending_pd_use;
+
+ /*
+ * Whether cross connection is disallowed by the AP to which this
+ * interface is associated (only valid if there is an association).
+ */
+ int cross_connect_disallowed;
+
+ /*
+ * Whether this P2P group is configured to use cross connection (only
+ * valid if this is P2P GO interface). The actual cross connect packet
+ * forwarding may not be configured depending on the uplink status.
+ */
+ int cross_connect_enabled;
+
+ /* Whether cross connection forwarding is in use at the moment. */
+ int cross_connect_in_use;
+
+ /*
+ * Uplink interface name for cross connection
+ */
+ char cross_connect_uplink[100];
+
+ unsigned int sta_scan_pending:1;
+ unsigned int p2p_auto_join:1;
+ unsigned int p2p_auto_pd:1;
+ unsigned int p2p_persistent_group:1;
+ unsigned int p2p_fallback_to_go_neg:1;
+ unsigned int p2p_pd_before_go_neg:1;
+ unsigned int p2p_go_ht40:1;
+ unsigned int user_initiated_pd:1;
+ int p2p_persistent_go_freq;
+ int p2p_persistent_id;
+ int p2p_go_intent;
+ int p2p_connect_freq;
+ struct os_time p2p_auto_started;
+#endif /* CONFIG_P2P */
+
struct wpa_ssid *bgscan_ssid;
const struct bgscan_ops *bgscan;
void *bgscan_priv;
- int connect_without_scan;
+ const struct autoscan_ops *autoscan;
+ struct wpa_driver_scan_params *autoscan_params;
+ void *autoscan_priv;
+
+ struct wpa_ssid *connect_without_scan;
+
+ struct wps_ap_info *wps_ap;
+ size_t num_wps_ap;
+ int wps_ap_iter;
int after_wps;
+ int known_wps_freq;
unsigned int wps_freq;
+ u16 wps_ap_channel;
+ int wps_fragment_size;
+ int auto_reconnect_disabled;
+
+ /* Channel preferences for AP/P2P GO use */
+ int best_24_freq;
+ int best_5_freq;
+ int best_overall_freq;
+
+ struct gas_query *gas;
+
+#ifdef CONFIG_INTERWORKING
+ unsigned int fetch_anqp_in_progress:1;
+ unsigned int network_select:1;
+ unsigned int auto_select:1;
+ unsigned int auto_network_select:1;
+ unsigned int fetch_all_anqp:1;
+#endif /* CONFIG_INTERWORKING */
+ unsigned int drv_capa_known;
+
+ struct {
+ struct hostapd_hw_modes *modes;
+ u16 num_modes;
+ u16 flags;
+ } hw;
+
+ int pno;
+
+ /* WLAN_REASON_* reason codes. Negative if locally generated. */
+ int disconnect_reason;
+
+ struct ext_password_data *ext_pw;
+
+ struct wpabuf *last_gas_resp;
+ u8 last_gas_addr[ETH_ALEN];
+ u8 last_gas_dialog_token;
+
+ unsigned int no_keep_alive:1;
};
/* wpa_supplicant.c */
+void wpa_supplicant_apply_ht_overrides(
+ struct wpa_supplicant *wpa_s, struct wpa_ssid *ssid,
+ struct wpa_driver_associate_params *params);
+
int wpa_set_wep_keys(struct wpa_supplicant *wpa_s, struct wpa_ssid *ssid);
int wpa_supplicant_reload_configuration(struct wpa_supplicant *wpa_s);
const char * wpa_supplicant_state_txt(enum wpa_states state);
+int wpa_supplicant_update_mac_addr(struct wpa_supplicant *wpa_s);
int wpa_supplicant_driver_init(struct wpa_supplicant *wpa_s);
int wpa_supplicant_set_suites(struct wpa_supplicant *wpa_s,
struct wpa_bss *bss, struct wpa_ssid *ssid,
@@ -459,14 +701,14 @@ void wpa_supplicant_initiate_eapol(struct wpa_supplicant *wpa_s);
void wpa_clear_keys(struct wpa_supplicant *wpa_s, const u8 *addr);
void wpa_supplicant_req_auth_timeout(struct wpa_supplicant *wpa_s,
int sec, int usec);
+void wpa_supplicant_reinit_autoscan(struct wpa_supplicant *wpa_s);
void wpa_supplicant_set_state(struct wpa_supplicant *wpa_s,
enum wpa_states state);
struct wpa_ssid * wpa_supplicant_get_ssid(struct wpa_supplicant *wpa_s);
+const char * wpa_supplicant_get_eap_mode(struct wpa_supplicant *wpa_s);
void wpa_supplicant_cancel_auth_timeout(struct wpa_supplicant *wpa_s);
void wpa_supplicant_deauthenticate(struct wpa_supplicant *wpa_s,
int reason_code);
-void wpa_supplicant_disassociate(struct wpa_supplicant *wpa_s,
- int reason_code);
void wpa_supplicant_enable_network(struct wpa_supplicant *wpa_s,
struct wpa_ssid *ssid);
@@ -476,16 +718,24 @@ void wpa_supplicant_select_network(struct wpa_supplicant *wpa_s,
struct wpa_ssid *ssid);
int wpa_supplicant_set_ap_scan(struct wpa_supplicant *wpa_s,
int ap_scan);
+int wpa_supplicant_set_bss_expiration_age(struct wpa_supplicant *wpa_s,
+ unsigned int expire_age);
+int wpa_supplicant_set_bss_expiration_count(struct wpa_supplicant *wpa_s,
+ unsigned int expire_count);
+int wpa_supplicant_set_scan_interval(struct wpa_supplicant *wpa_s,
+ int scan_interval);
int wpa_supplicant_set_debug_params(struct wpa_global *global,
int debug_level, int debug_timestamp,
int debug_show_keys);
+void free_hw_features(struct wpa_supplicant *wpa_s);
void wpa_show_license(void);
struct wpa_supplicant * wpa_supplicant_add_iface(struct wpa_global *global,
struct wpa_interface *iface);
int wpa_supplicant_remove_iface(struct wpa_global *global,
- struct wpa_supplicant *wpa_s);
+ struct wpa_supplicant *wpa_s,
+ int terminate);
struct wpa_supplicant * wpa_supplicant_get_iface(struct wpa_global *global,
const char *ifname);
struct wpa_global * wpa_supplicant_init(struct wpa_params *params);
@@ -499,14 +749,60 @@ void wpa_supplicant_rx_eapol(void *ctx, const u8 *src_addr,
const u8 *buf, size_t len);
enum wpa_key_mgmt key_mgmt2driver(int key_mgmt);
enum wpa_cipher cipher_suite2driver(int cipher);
+void wpa_supplicant_update_config(struct wpa_supplicant *wpa_s);
+void wpa_supplicant_clear_status(struct wpa_supplicant *wpa_s);
+void wpas_connection_failed(struct wpa_supplicant *wpa_s, const u8 *bssid);
+int wpas_driver_bss_selection(struct wpa_supplicant *wpa_s);
+int wpas_is_p2p_prioritized(struct wpa_supplicant *wpa_s);
+void wpas_auth_failed(struct wpa_supplicant *wpa_s);
+void wpas_clear_temp_disabled(struct wpa_supplicant *wpa_s,
+ struct wpa_ssid *ssid, int clear_failures);
+int disallowed_bssid(struct wpa_supplicant *wpa_s, const u8 *bssid);
+int disallowed_ssid(struct wpa_supplicant *wpa_s, const u8 *ssid,
+ size_t ssid_len);
+void wpas_request_connection(struct wpa_supplicant *wpa_s);
+int wpas_build_ext_capab(struct wpa_supplicant *wpa_s, u8 *buf);
+
+/**
+ * wpa_supplicant_ctrl_iface_ctrl_rsp_handle - Handle a control response
+ * @wpa_s: Pointer to wpa_supplicant data
+ * @ssid: Pointer to the network block the reply is for
+ * @field: field the response is a reply for
+ * @value: value (ie, password, etc) for @field
+ * Returns: 0 on success, non-zero on error
+ *
+ * Helper function to handle replies to control interface requests.
+ */
+int wpa_supplicant_ctrl_iface_ctrl_rsp_handle(struct wpa_supplicant *wpa_s,
+ struct wpa_ssid *ssid,
+ const char *field,
+ const char *value);
/* events.c */
void wpa_supplicant_mark_disassoc(struct wpa_supplicant *wpa_s);
-void wpa_supplicant_connect(struct wpa_supplicant *wpa_s,
- struct wpa_bss *selected,
- struct wpa_ssid *ssid);
+int wpa_supplicant_connect(struct wpa_supplicant *wpa_s,
+ struct wpa_bss *selected,
+ struct wpa_ssid *ssid);
+void wpa_supplicant_stop_countermeasures(void *eloop_ctx, void *sock_ctx);
+void wpa_supplicant_delayed_mic_error_report(void *eloop_ctx, void *sock_ctx);
+void wnm_bss_keep_alive_deinit(struct wpa_supplicant *wpa_s);
+int wpas_select_network_from_last_scan(struct wpa_supplicant *wpa_s);
/* eap_register.c */
int eap_register_methods(void);
+/**
+ * Utility method to tell if a given network is a persistent group
+ * @ssid: Network object
+ * Returns: 1 if network is a persistent group, 0 otherwise
+ */
+static inline int network_is_persistent_group(struct wpa_ssid *ssid)
+{
+ return ((ssid->disabled == 2) || ssid->p2p_persistent_group);
+}
+
+int wpas_network_disabled(struct wpa_supplicant *wpa_s, struct wpa_ssid *ssid);
+
+int wpas_init_ext_pw(struct wpa_supplicant *wpa_s);
+
#endif /* WPA_SUPPLICANT_I_H */
diff --git a/contrib/wpa/wpa_supplicant/wpa_supplicant_template.conf b/contrib/wpa/wpa_supplicant/wpa_supplicant_template.conf
new file mode 100644
index 0000000..a08eb33
--- /dev/null
+++ b/contrib/wpa/wpa_supplicant/wpa_supplicant_template.conf
@@ -0,0 +1,6 @@
+##### wpa_supplicant configuration file template #####
+update_config=1
+ctrl_interface=wlan0
+eapol_version=1
+ap_scan=1
+fast_reauth=1
diff --git a/contrib/wpa/wpa_supplicant/wpas_glue.c b/contrib/wpa/wpa_supplicant/wpas_glue.c
index 4af0cd0..6f69ddb 100644
--- a/contrib/wpa/wpa_supplicant/wpas_glue.c
+++ b/contrib/wpa/wpa_supplicant/wpas_glue.c
@@ -1,15 +1,9 @@
/*
* WPA Supplicant - Glue code to setup EAPOL and RSN modules
- * Copyright (c) 2003-2008, Jouni Malinen <j@w1.fi>
+ * Copyright (c) 2003-2012, Jouni Malinen <j@w1.fi>
*
- * This program is free software; you can redistribute it and/or modify
- * it under the terms of the GNU General Public License version 2 as
- * published by the Free Software Foundation.
- *
- * Alternatively, this software may be distributed under the terms of BSD
- * license.
- *
- * See README and COPYING for more details.
+ * This software may be distributed under the terms of the BSD license.
+ * See README for more details.
*/
#include "includes.h"
@@ -24,7 +18,6 @@
#include "wpa_supplicant_i.h"
#include "driver_i.h"
#include "rsn_supp/pmksa_cache.h"
-#include "mlme.h"
#include "sme.h"
#include "common/ieee802_11_defs.h"
#include "common/wpa_ctrl.h"
@@ -32,6 +25,7 @@
#include "wps_supplicant.h"
#include "bss.h"
#include "scan.h"
+#include "notify.h"
#ifndef CONFIG_NO_CONFIG_BLOBS
@@ -210,9 +204,8 @@ static int wpa_eapol_set_wep_key(void *ctx, int unicast, int keyidx,
wpa_s->group_cipher = cipher;
}
return wpa_drv_set_key(wpa_s, WPA_ALG_WEP,
- unicast ? wpa_s->bssid :
- (u8 *) "\xff\xff\xff\xff\xff\xff",
- keyidx, unicast, (u8 *) "", 0, key, keylen);
+ unicast ? wpa_s->bssid : NULL,
+ keyidx, unicast, NULL, 0, key, keylen);
}
@@ -255,14 +248,29 @@ static void wpa_supplicant_eapol_cb(struct eapol_sm *eapol, int success,
"handshake");
pmk_len = PMK_LEN;
- res = eapol_sm_get_key(eapol, pmk, PMK_LEN);
- if (res) {
- /*
- * EAP-LEAP is an exception from other EAP methods: it
- * uses only 16-byte PMK.
- */
- res = eapol_sm_get_key(eapol, pmk, 16);
- pmk_len = 16;
+ if (wpa_key_mgmt_ft(wpa_s->key_mgmt)) {
+#ifdef CONFIG_IEEE80211R
+ u8 buf[2 * PMK_LEN];
+ wpa_printf(MSG_DEBUG, "RSN: Use FT XXKey as PMK for "
+ "driver-based 4-way hs and FT");
+ res = eapol_sm_get_key(eapol, buf, 2 * PMK_LEN);
+ if (res == 0) {
+ os_memcpy(pmk, buf + PMK_LEN, PMK_LEN);
+ os_memset(buf, 0, sizeof(buf));
+ }
+#else /* CONFIG_IEEE80211R */
+ res = -1;
+#endif /* CONFIG_IEEE80211R */
+ } else {
+ res = eapol_sm_get_key(eapol, pmk, PMK_LEN);
+ if (res) {
+ /*
+ * EAP-LEAP is an exception from other EAP methods: it
+ * uses only 16-byte PMK.
+ */
+ res = eapol_sm_get_key(eapol, pmk, 16);
+ pmk_len = 16;
+ }
}
if (res) {
@@ -271,6 +279,9 @@ static void wpa_supplicant_eapol_cb(struct eapol_sm *eapol, int success,
return;
}
+ wpa_hexdump_key(MSG_DEBUG, "RSN: Configure PMK for driver-based 4-way "
+ "handshake", pmk, pmk_len);
+
if (wpa_drv_set_key(wpa_s, WPA_ALG_PMK, NULL, 0, 0, NULL, 0, pmk,
pmk_len)) {
wpa_printf(MSG_DEBUG, "Failed to set PMK to the driver");
@@ -395,14 +406,6 @@ static enum wpa_states _wpa_supplicant_get_state(void *wpa_s)
}
-static void _wpa_supplicant_disassociate(void *wpa_s, int reason_code)
-{
- wpa_supplicant_disassociate(wpa_s, reason_code);
- /* Schedule a scan to make sure we continue looking for networks */
- wpa_supplicant_req_scan(wpa_s, 5, 0);
-}
-
-
static void _wpa_supplicant_deauthenticate(void *wpa_s, int reason_code)
{
wpa_supplicant_deauthenticate(wpa_s, reason_code);
@@ -420,10 +423,6 @@ static void * wpa_supplicant_get_network_ctx(void *wpa_s)
static int wpa_supplicant_get_bssid(void *ctx, u8 *bssid)
{
struct wpa_supplicant *wpa_s = ctx;
- if (wpa_s->drv_flags & WPA_DRIVER_FLAGS_USER_SPACE_MLME) {
- os_memcpy(bssid, wpa_s->bssid, ETH_ALEN);
- return 0;
- }
return wpa_drv_get_bssid(wpa_s, bssid);
}
@@ -471,8 +470,6 @@ static int wpa_supplicant_update_ft_ies(void *ctx, const u8 *md,
const u8 *ies, size_t ies_len)
{
struct wpa_supplicant *wpa_s = ctx;
- if (wpa_s->drv_flags & WPA_DRIVER_FLAGS_USER_SPACE_MLME)
- return ieee80211_sta_update_ft_ies(wpa_s, md, ies, ies_len);
if (wpa_s->drv_flags & WPA_DRIVER_FLAGS_SME)
return sme_update_ft_ies(wpa_s, md, ies, ies_len);
return wpa_drv_update_ft_ies(wpa_s, md, ies, ies_len);
@@ -484,9 +481,6 @@ static int wpa_supplicant_send_ft_action(void *ctx, u8 action,
const u8 *ies, size_t ies_len)
{
struct wpa_supplicant *wpa_s = ctx;
- if (wpa_s->drv_flags & WPA_DRIVER_FLAGS_USER_SPACE_MLME)
- return ieee80211_sta_send_ft_action(wpa_s, action, target_ap,
- ies, ies_len);
return wpa_drv_send_ft_action(wpa_s, action, target_ap, ies, ies_len);
}
@@ -497,9 +491,6 @@ static int wpa_supplicant_mark_authenticated(void *ctx, const u8 *target_ap)
struct wpa_driver_auth_params params;
struct wpa_bss *bss;
- if (wpa_s->drv_flags & WPA_DRIVER_FLAGS_USER_SPACE_MLME)
- return -1;
-
bss = wpa_bss_get_bssid(wpa_s, target_ap);
if (bss == NULL)
return -1;
@@ -518,13 +509,142 @@ static int wpa_supplicant_mark_authenticated(void *ctx, const u8 *target_ap)
#endif /* CONFIG_NO_WPA */
+#ifdef CONFIG_TDLS
+
+static int wpa_supplicant_tdls_get_capa(void *ctx, int *tdls_supported,
+ int *tdls_ext_setup)
+{
+ struct wpa_supplicant *wpa_s = ctx;
+
+ *tdls_supported = 0;
+ *tdls_ext_setup = 0;
+
+ if (!wpa_s->drv_capa_known)
+ return -1;
+
+ if (wpa_s->drv_flags & WPA_DRIVER_FLAGS_TDLS_SUPPORT)
+ *tdls_supported = 1;
+
+ if (wpa_s->drv_flags & WPA_DRIVER_FLAGS_TDLS_EXTERNAL_SETUP)
+ *tdls_ext_setup = 1;
+
+ return 0;
+}
+
+
+static int wpa_supplicant_send_tdls_mgmt(void *ctx, const u8 *dst,
+ u8 action_code, u8 dialog_token,
+ u16 status_code, const u8 *buf,
+ size_t len)
+{
+ struct wpa_supplicant *wpa_s = ctx;
+ return wpa_drv_send_tdls_mgmt(wpa_s, dst, action_code, dialog_token,
+ status_code, buf, len);
+}
+
+
+static int wpa_supplicant_tdls_oper(void *ctx, int oper, const u8 *peer)
+{
+ struct wpa_supplicant *wpa_s = ctx;
+ return wpa_drv_tdls_oper(wpa_s, oper, peer);
+}
+
+
+static int wpa_supplicant_tdls_peer_addset(
+ void *ctx, const u8 *peer, int add, u16 capability,
+ const u8 *supp_rates, size_t supp_rates_len)
+{
+ struct wpa_supplicant *wpa_s = ctx;
+ struct hostapd_sta_add_params params;
+
+ params.addr = peer;
+ params.aid = 1;
+ params.capability = capability;
+ params.flags = WPA_STA_TDLS_PEER | WPA_STA_AUTHORIZED;
+ params.ht_capabilities = NULL;
+ params.listen_interval = 0;
+ params.supp_rates = supp_rates;
+ params.supp_rates_len = supp_rates_len;
+ params.set = !add;
+
+ return wpa_drv_sta_add(wpa_s, &params);
+}
+
+#endif /* CONFIG_TDLS */
+
+
+enum wpa_ctrl_req_type wpa_supplicant_ctrl_req_from_string(const char *field)
+{
+ if (os_strcmp(field, "IDENTITY") == 0)
+ return WPA_CTRL_REQ_EAP_IDENTITY;
+ else if (os_strcmp(field, "PASSWORD") == 0)
+ return WPA_CTRL_REQ_EAP_PASSWORD;
+ else if (os_strcmp(field, "NEW_PASSWORD") == 0)
+ return WPA_CTRL_REQ_EAP_NEW_PASSWORD;
+ else if (os_strcmp(field, "PIN") == 0)
+ return WPA_CTRL_REQ_EAP_PIN;
+ else if (os_strcmp(field, "OTP") == 0)
+ return WPA_CTRL_REQ_EAP_OTP;
+ else if (os_strcmp(field, "PASSPHRASE") == 0)
+ return WPA_CTRL_REQ_EAP_PASSPHRASE;
+ return WPA_CTRL_REQ_UNKNOWN;
+}
+
+
+const char * wpa_supplicant_ctrl_req_to_string(enum wpa_ctrl_req_type field,
+ const char *default_txt,
+ const char **txt)
+{
+ const char *ret = NULL;
+
+ *txt = default_txt;
+
+ switch (field) {
+ case WPA_CTRL_REQ_EAP_IDENTITY:
+ *txt = "Identity";
+ ret = "IDENTITY";
+ break;
+ case WPA_CTRL_REQ_EAP_PASSWORD:
+ *txt = "Password";
+ ret = "PASSWORD";
+ break;
+ case WPA_CTRL_REQ_EAP_NEW_PASSWORD:
+ *txt = "New Password";
+ ret = "NEW_PASSWORD";
+ break;
+ case WPA_CTRL_REQ_EAP_PIN:
+ *txt = "PIN";
+ ret = "PIN";
+ break;
+ case WPA_CTRL_REQ_EAP_OTP:
+ ret = "OTP";
+ break;
+ case WPA_CTRL_REQ_EAP_PASSPHRASE:
+ *txt = "Private key passphrase";
+ ret = "PASSPHRASE";
+ break;
+ default:
+ break;
+ }
+
+ /* txt needs to be something */
+ if (*txt == NULL) {
+ wpa_printf(MSG_WARNING, "No message for request %d", field);
+ ret = NULL;
+ }
+
+ return ret;
+}
+
#ifdef IEEE8021X_EAPOL
#if defined(CONFIG_CTRL_IFACE) || !defined(CONFIG_NO_STDOUT_DEBUG)
-static void wpa_supplicant_eap_param_needed(void *ctx, const char *field,
- const char *txt)
+static void wpa_supplicant_eap_param_needed(void *ctx,
+ enum wpa_ctrl_req_type field,
+ const char *default_txt)
{
struct wpa_supplicant *wpa_s = ctx;
struct wpa_ssid *ssid = wpa_s->current_ssid;
+ const char *field_name, *txt = NULL;
char *buf;
size_t buflen;
int len;
@@ -532,13 +652,23 @@ static void wpa_supplicant_eap_param_needed(void *ctx, const char *field,
if (ssid == NULL)
return;
+ wpas_notify_network_request(wpa_s, ssid, field, default_txt);
+
+ field_name = wpa_supplicant_ctrl_req_to_string(field, default_txt,
+ &txt);
+ if (field_name == NULL) {
+ wpa_printf(MSG_WARNING, "Unhandled EAP param %d needed",
+ field);
+ return;
+ }
+
buflen = 100 + os_strlen(txt) + ssid->ssid_len;
buf = os_malloc(buflen);
if (buf == NULL)
return;
len = os_snprintf(buf, buflen,
WPA_CTRL_REQ "%s-%d:%s needed for SSID ",
- field, ssid->id, txt);
+ field_name, ssid->id, txt);
if (len < 0 || (size_t) len >= buflen) {
os_free(buf);
return;
@@ -572,6 +702,63 @@ static void wpa_supplicant_port_cb(void *ctx, int authorized)
authorized ? "Authorized" : "Unauthorized");
wpa_drv_set_supp_port(wpa_s, authorized);
}
+
+
+static void wpa_supplicant_cert_cb(void *ctx, int depth, const char *subject,
+ const char *cert_hash,
+ const struct wpabuf *cert)
+{
+ struct wpa_supplicant *wpa_s = ctx;
+
+ wpas_notify_certification(wpa_s, depth, subject, cert_hash, cert);
+}
+
+
+static void wpa_supplicant_status_cb(void *ctx, const char *status,
+ const char *parameter)
+{
+ struct wpa_supplicant *wpa_s = ctx;
+
+ wpas_notify_eap_status(wpa_s, status, parameter);
+}
+
+
+static void wpa_supplicant_set_anon_id(void *ctx, const u8 *id, size_t len)
+{
+ struct wpa_supplicant *wpa_s = ctx;
+ char *str;
+ int res;
+
+ wpa_hexdump_ascii(MSG_DEBUG, "EAP method updated anonymous_identity",
+ id, len);
+
+ if (wpa_s->current_ssid == NULL)
+ return;
+
+ if (id == NULL) {
+ if (wpa_config_set(wpa_s->current_ssid, "anonymous_identity",
+ "NULL", 0) < 0)
+ return;
+ } else {
+ str = os_malloc(len * 2 + 1);
+ if (str == NULL)
+ return;
+ wpa_snprintf_hex(str, len * 2 + 1, id, len);
+ res = wpa_config_set(wpa_s->current_ssid, "anonymous_identity",
+ str, 0);
+ os_free(str);
+ if (res < 0)
+ return;
+ }
+
+ if (wpa_s->conf->update_config) {
+ res = wpa_config_write(wpa_s->confname, wpa_s->conf);
+ if (res) {
+ wpa_printf(MSG_DEBUG, "Failed to update config after "
+ "anonymous_id update");
+ }
+ }
+}
#endif /* IEEE8021X_EAPOL */
@@ -602,6 +789,9 @@ int wpa_supplicant_init_eapol(struct wpa_supplicant *wpa_s)
ctx->eap_param_needed = wpa_supplicant_eap_param_needed;
ctx->port_cb = wpa_supplicant_port_cb;
ctx->cb = wpa_supplicant_eapol_cb;
+ ctx->cert_cb = wpa_supplicant_cert_cb;
+ ctx->status_cb = wpa_supplicant_status_cb;
+ ctx->set_anon_id = wpa_supplicant_set_anon_id;
ctx->cb_ctx = wpa_s;
wpa_s->eapol = eapol_sm_init(ctx);
if (wpa_s->eapol == NULL) {
@@ -616,6 +806,18 @@ int wpa_supplicant_init_eapol(struct wpa_supplicant *wpa_s)
}
+#ifndef CONFIG_NO_WPA
+static void wpa_supplicant_set_rekey_offload(void *ctx, const u8 *kek,
+ const u8 *kck,
+ const u8 *replay_ctr)
+{
+ struct wpa_supplicant *wpa_s = ctx;
+
+ wpa_drv_set_rekey_info(wpa_s, kek, kck, replay_ctr);
+}
+#endif /* CONFIG_NO_WPA */
+
+
int wpa_supplicant_init_wpa(struct wpa_supplicant *wpa_s)
{
#ifndef CONFIG_NO_WPA
@@ -631,7 +833,6 @@ int wpa_supplicant_init_wpa(struct wpa_supplicant *wpa_s)
ctx->set_state = _wpa_supplicant_set_state;
ctx->get_state = _wpa_supplicant_get_state;
ctx->deauthenticate = _wpa_supplicant_deauthenticate;
- ctx->disassociate = _wpa_supplicant_disassociate;
ctx->set_key = wpa_supplicant_set_key;
ctx->get_network_ctx = wpa_supplicant_get_network_ctx;
ctx->get_bssid = wpa_supplicant_get_bssid;
@@ -651,6 +852,13 @@ int wpa_supplicant_init_wpa(struct wpa_supplicant *wpa_s)
ctx->send_ft_action = wpa_supplicant_send_ft_action;
ctx->mark_authenticated = wpa_supplicant_mark_authenticated;
#endif /* CONFIG_IEEE80211R */
+#ifdef CONFIG_TDLS
+ ctx->tdls_get_capa = wpa_supplicant_tdls_get_capa;
+ ctx->send_tdls_mgmt = wpa_supplicant_send_tdls_mgmt;
+ ctx->tdls_oper = wpa_supplicant_tdls_oper;
+ ctx->tdls_peer_addset = wpa_supplicant_tdls_peer_addset;
+#endif /* CONFIG_TDLS */
+ ctx->set_rekey_offload = wpa_supplicant_set_rekey_offload;
wpa_s->wpa = wpa_sm_init(ctx);
if (wpa_s->wpa == NULL) {
@@ -674,6 +882,8 @@ void wpa_supplicant_rsn_supp_set_config(struct wpa_supplicant *wpa_s,
conf.peerkey_enabled = ssid->peerkey;
conf.allowed_pairwise_cipher = ssid->pairwise_cipher;
#ifdef IEEE8021X_EAPOL
+ conf.proactive_key_caching = ssid->proactive_key_caching < 0 ?
+ wpa_s->conf->okc : ssid->proactive_key_caching;
conf.eap_workaround = ssid->eap_workaround;
conf.eap_conf_ctx = &ssid->eap;
#endif /* IEEE8021X_EAPOL */
diff --git a/contrib/wpa/wpa_supplicant/wpas_glue.h b/contrib/wpa/wpa_supplicant/wpas_glue.h
index b571e4d..9808c22 100644
--- a/contrib/wpa/wpa_supplicant/wpas_glue.h
+++ b/contrib/wpa/wpa_supplicant/wpas_glue.h
@@ -2,22 +2,24 @@
* WPA Supplicant - Glue code to setup EAPOL and RSN modules
* Copyright (c) 2003-2008, Jouni Malinen <j@w1.fi>
*
- * This program is free software; you can redistribute it and/or modify
- * it under the terms of the GNU General Public License version 2 as
- * published by the Free Software Foundation.
- *
- * Alternatively, this software may be distributed under the terms of BSD
- * license.
- *
- * See README and COPYING for more details.
+ * This software may be distributed under the terms of the BSD license.
+ * See README for more details.
*/
#ifndef WPAS_GLUE_H
#define WPAS_GLUE_H
+enum wpa_ctrl_req_type;
+
int wpa_supplicant_init_eapol(struct wpa_supplicant *wpa_s);
int wpa_supplicant_init_wpa(struct wpa_supplicant *wpa_s);
void wpa_supplicant_rsn_supp_set_config(struct wpa_supplicant *wpa_s,
struct wpa_ssid *ssid);
+const char * wpa_supplicant_ctrl_req_to_string(enum wpa_ctrl_req_type field,
+ const char *default_txt,
+ const char **txt);
+
+enum wpa_ctrl_req_type wpa_supplicant_ctrl_req_from_string(const char *field);
+
#endif /* WPAS_GLUE_H */
diff --git a/contrib/wpa/wpa_supplicant/wps_supplicant.c b/contrib/wpa/wpa_supplicant/wps_supplicant.c
index ba94d33..8ab5f64 100644
--- a/contrib/wpa/wpa_supplicant/wps_supplicant.c
+++ b/contrib/wpa/wpa_supplicant/wps_supplicant.c
@@ -1,15 +1,9 @@
/*
* wpa_supplicant / WPS integration
- * Copyright (c) 2008-2010, Jouni Malinen <j@w1.fi>
+ * Copyright (c) 2008-2012, Jouni Malinen <j@w1.fi>
*
- * This program is free software; you can redistribute it and/or modify
- * it under the terms of the GNU General Public License version 2 as
- * published by the Free Software Foundation.
- *
- * Alternatively, this software may be distributed under the terms of BSD
- * license.
- *
- * See README and COPYING for more details.
+ * This software may be distributed under the terms of the BSD license.
+ * See README for more details.
*/
#include "includes.h"
@@ -17,6 +11,7 @@
#include "common.h"
#include "eloop.h"
#include "uuid.h"
+#include "crypto/random.h"
#include "crypto/dh_group5.h"
#include "common/ieee802_11_defs.h"
#include "common/ieee802_11_common.h"
@@ -24,7 +19,9 @@
#include "common/wpa_ctrl.h"
#include "eap_common/eap_wsc_common.h"
#include "eap_peer/eap.h"
+#include "eapol_supp/eapol_supp_sm.h"
#include "rsn_supp/wpa.h"
+#include "wps/wps_attr_parse.h"
#include "config.h"
#include "wpa_supplicant_i.h"
#include "driver_i.h"
@@ -32,15 +29,29 @@
#include "blacklist.h"
#include "bss.h"
#include "scan.h"
+#include "ap.h"
+#include "p2p/p2p.h"
+#include "p2p_supplicant.h"
#include "wps_supplicant.h"
+#ifndef WPS_PIN_SCAN_IGNORE_SEL_REG
#define WPS_PIN_SCAN_IGNORE_SEL_REG 3
+#endif /* WPS_PIN_SCAN_IGNORE_SEL_REG */
static void wpas_wps_timeout(void *eloop_ctx, void *timeout_ctx);
static void wpas_clear_wps(struct wpa_supplicant *wpa_s);
+static void wpas_wps_clear_ap_info(struct wpa_supplicant *wpa_s)
+{
+ os_free(wpa_s->wps_ap);
+ wpa_s->wps_ap = NULL;
+ wpa_s->num_wps_ap = 0;
+ wpa_s->wps_ap_iter = 0;
+}
+
+
int wpas_wps_eapol_cb(struct wpa_supplicant *wpa_s)
{
if (!wpa_s->wps_success &&
@@ -64,16 +75,28 @@ int wpas_wps_eapol_cb(struct wpa_supplicant *wpa_s)
return 1;
}
+ wpas_wps_clear_ap_info(wpa_s);
eloop_cancel_timeout(wpas_wps_timeout, wpa_s, NULL);
+ if (wpa_s->key_mgmt == WPA_KEY_MGMT_WPS && !wpa_s->wps_success)
+ wpa_msg(wpa_s, MSG_INFO, WPS_EVENT_FAIL);
if (wpa_s->key_mgmt == WPA_KEY_MGMT_WPS && wpa_s->current_ssid &&
!(wpa_s->current_ssid->key_mgmt & WPA_KEY_MGMT_WPS)) {
+ int disabled = wpa_s->current_ssid->disabled;
+ unsigned int freq = wpa_s->assoc_freq;
wpa_printf(MSG_DEBUG, "WPS: Network configuration replaced - "
- "try to associate with the received credential");
+ "try to associate with the received credential "
+ "(freq=%u)", freq);
wpa_supplicant_deauthenticate(wpa_s,
WLAN_REASON_DEAUTH_LEAVING);
+ if (disabled) {
+ wpa_printf(MSG_DEBUG, "WPS: Current network is "
+ "disabled - wait for user to enable");
+ return 1;
+ }
wpa_s->after_wps = 5;
- wpa_s->wps_freq = wpa_s->assoc_freq;
+ wpa_s->wps_freq = freq;
+ wpa_s->normal_scans = 0;
wpa_s->reassociate = 1;
wpa_supplicant_req_scan(wpa_s, 0, 0);
return 1;
@@ -113,6 +136,8 @@ static void wpas_wps_security_workaround(struct wpa_supplicant *wpa_s,
if (wpa_drv_get_capa(wpa_s, &capa))
return; /* Unknown what driver supports */
+ if (ssid->ssid == NULL)
+ return;
bss = wpa_bss_get(wpa_s, cred->mac_addr, ssid->ssid, ssid->ssid_len);
if (bss == NULL) {
wpa_printf(MSG_DEBUG, "WPS: The AP was not found from BSS "
@@ -178,6 +203,9 @@ static int wpa_supplicant_wps_cred(void *ctx,
struct wpa_ssid *ssid = wpa_s->current_ssid;
u8 key_idx = 0;
u16 auth_type;
+#ifdef CONFIG_WPS_REG_DISABLE_OPEN
+ int registrar = 0;
+#endif /* CONFIG_WPS_REG_DISABLE_OPEN */
if ((wpa_s->conf->wps_cred_processing == 1 ||
wpa_s->conf->wps_cred_processing == 2) && cred->cred_attr) {
@@ -228,9 +256,25 @@ static int wpa_supplicant_wps_cred(void *ctx,
return 0;
}
+ if (auth_type == WPS_AUTH_WPAPSK || auth_type == WPS_AUTH_WPA2PSK) {
+ if (cred->key_len < 8 || cred->key_len > 2 * PMK_LEN) {
+ wpa_printf(MSG_ERROR, "WPS: Reject PSK credential with "
+ "invalid Network Key length %lu",
+ (unsigned long) cred->key_len);
+ return -1;
+ }
+ }
+
if (ssid && (ssid->key_mgmt & WPA_KEY_MGMT_WPS)) {
wpa_printf(MSG_DEBUG, "WPS: Replace WPS network block based "
"on the received credential");
+#ifdef CONFIG_WPS_REG_DISABLE_OPEN
+ if (ssid->eap.identity &&
+ ssid->eap.identity_len == WSC_ID_REGISTRAR_LEN &&
+ os_memcmp(ssid->eap.identity, WSC_ID_REGISTRAR,
+ WSC_ID_REGISTRAR_LEN) == 0)
+ registrar = 1;
+#endif /* CONFIG_WPS_REG_DISABLE_OPEN */
os_free(ssid->eap.identity);
ssid->eap.identity = NULL;
ssid->eap.identity_len = 0;
@@ -238,6 +282,13 @@ static int wpa_supplicant_wps_cred(void *ctx,
ssid->eap.phase1 = NULL;
os_free(ssid->eap.eap_methods);
ssid->eap.eap_methods = NULL;
+ if (!ssid->p2p_group) {
+ ssid->temporary = 0;
+ ssid->bssid_set = 0;
+ }
+ ssid->disabled_until.sec = 0;
+ ssid->disabled_until.usec = 0;
+ ssid->auth_failures = 0;
} else {
wpa_printf(MSG_DEBUG, "WPS: Create a new network based on the "
"received credential");
@@ -304,6 +355,16 @@ static int wpa_supplicant_wps_cred(void *ctx,
ssid->auth_alg = WPA_AUTH_ALG_OPEN;
ssid->key_mgmt = WPA_KEY_MGMT_NONE;
ssid->proto = 0;
+#ifdef CONFIG_WPS_REG_DISABLE_OPEN
+ if (registrar) {
+ wpa_msg(wpa_s, MSG_INFO, WPS_EVENT_OPEN_NETWORK
+ "id=%d - Credentials for an open "
+ "network disabled by default - use "
+ "'select_network %d' to enable",
+ ssid->id, ssid->id);
+ ssid->disabled = 1;
+ }
+#endif /* CONFIG_WPS_REG_DISABLE_OPEN */
break;
case WPS_AUTH_SHARED:
ssid->auth_alg = WPA_AUTH_ALG_SHARED;
@@ -315,16 +376,6 @@ static int wpa_supplicant_wps_cred(void *ctx,
ssid->key_mgmt = WPA_KEY_MGMT_PSK;
ssid->proto = WPA_PROTO_WPA;
break;
- case WPS_AUTH_WPA:
- ssid->auth_alg = WPA_AUTH_ALG_OPEN;
- ssid->key_mgmt = WPA_KEY_MGMT_IEEE8021X;
- ssid->proto = WPA_PROTO_WPA;
- break;
- case WPS_AUTH_WPA2:
- ssid->auth_alg = WPA_AUTH_ALG_OPEN;
- ssid->key_mgmt = WPA_KEY_MGMT_IEEE8021X;
- ssid->proto = WPA_PROTO_RSN;
- break;
case WPS_AUTH_WPA2PSK:
ssid->auth_alg = WPA_AUTH_ALG_OPEN;
ssid->key_mgmt = WPA_KEY_MGMT_PSK;
@@ -341,6 +392,7 @@ static int wpa_supplicant_wps_cred(void *ctx,
return -1;
}
ssid->psk_set = 1;
+ ssid->export_keys = 1;
} else if (cred->key_len >= 8 && cred->key_len < 2 * PMK_LEN) {
os_free(ssid->passphrase);
ssid->passphrase = os_malloc(cred->key_len + 1);
@@ -349,6 +401,7 @@ static int wpa_supplicant_wps_cred(void *ctx,
os_memcpy(ssid->passphrase, cred->key, cred->key_len);
ssid->passphrase[cred->key_len] = '\0';
wpa_config_update_psk(ssid);
+ ssid->export_keys = 1;
} else {
wpa_printf(MSG_ERROR, "WPS: Invalid Network Key "
"length %lu",
@@ -359,6 +412,9 @@ static int wpa_supplicant_wps_cred(void *ctx,
wpas_wps_security_workaround(wpa_s, ssid, cred);
+ if (cred->ap_channel)
+ wpa_s->wps_ap_channel = cred->ap_channel;
+
#ifndef CONFIG_NO_CONFIG_WRITE
if (wpa_s->conf->update_config &&
wpa_config_write(wpa_s->confname, wpa_s->conf)) {
@@ -367,10 +423,26 @@ static int wpa_supplicant_wps_cred(void *ctx,
}
#endif /* CONFIG_NO_CONFIG_WRITE */
+ /*
+ * Optimize the post-WPS scan based on the channel used during
+ * the provisioning in case EAP-Failure is not received.
+ */
+ wpa_s->after_wps = 5;
+ wpa_s->wps_freq = wpa_s->assoc_freq;
+
return 0;
}
+#ifdef CONFIG_P2P
+static void wpas_wps_pbc_overlap_cb(void *eloop_ctx, void *timeout_ctx)
+{
+ struct wpa_supplicant *wpa_s = eloop_ctx;
+ wpas_p2p_notif_pbc_overlap(wpa_s);
+}
+#endif /* CONFIG_P2P */
+
+
static void wpa_supplicant_wps_event_m2d(struct wpa_supplicant *wpa_s,
struct wps_event_m2d *m2d)
{
@@ -378,15 +450,97 @@ static void wpa_supplicant_wps_event_m2d(struct wpa_supplicant *wpa_s,
"dev_password_id=%d config_error=%d",
m2d->dev_password_id, m2d->config_error);
wpas_notify_wps_event_m2d(wpa_s, m2d);
+#ifdef CONFIG_P2P
+ if (wpa_s->parent && wpa_s->parent != wpa_s) {
+ wpa_msg(wpa_s->parent, MSG_INFO, WPS_EVENT_M2D
+ "dev_password_id=%d config_error=%d",
+ m2d->dev_password_id, m2d->config_error);
+ }
+ if (m2d->config_error == WPS_CFG_MULTIPLE_PBC_DETECTED) {
+ /*
+ * Notify P2P from eloop timeout to avoid issues with the
+ * interface getting removed while processing a message.
+ */
+ eloop_register_timeout(0, 0, wpas_wps_pbc_overlap_cb, wpa_s,
+ NULL);
+ }
+#endif /* CONFIG_P2P */
}
+static const char * wps_event_fail_reason[NUM_WPS_EI_VALUES] = {
+ "No Error", /* WPS_EI_NO_ERROR */
+ "TKIP Only Prohibited", /* WPS_EI_SECURITY_TKIP_ONLY_PROHIBITED */
+ "WEP Prohibited" /* WPS_EI_SECURITY_WEP_PROHIBITED */
+};
+
static void wpa_supplicant_wps_event_fail(struct wpa_supplicant *wpa_s,
struct wps_event_fail *fail)
{
- wpa_msg(wpa_s, MSG_INFO, WPS_EVENT_FAIL "msg=%d", fail->msg);
+ if (fail->error_indication > 0 &&
+ fail->error_indication < NUM_WPS_EI_VALUES) {
+ wpa_msg(wpa_s, MSG_INFO,
+ WPS_EVENT_FAIL "msg=%d config_error=%d reason=%d (%s)",
+ fail->msg, fail->config_error, fail->error_indication,
+ wps_event_fail_reason[fail->error_indication]);
+ if (wpa_s->parent && wpa_s->parent != wpa_s)
+ wpa_msg(wpa_s->parent, MSG_INFO, WPS_EVENT_FAIL
+ "msg=%d config_error=%d reason=%d (%s)",
+ fail->msg, fail->config_error,
+ fail->error_indication,
+ wps_event_fail_reason[fail->error_indication]);
+ } else {
+ wpa_msg(wpa_s, MSG_INFO,
+ WPS_EVENT_FAIL "msg=%d config_error=%d",
+ fail->msg, fail->config_error);
+ if (wpa_s->parent && wpa_s->parent != wpa_s)
+ wpa_msg(wpa_s->parent, MSG_INFO, WPS_EVENT_FAIL
+ "msg=%d config_error=%d",
+ fail->msg, fail->config_error);
+ }
wpas_clear_wps(wpa_s);
wpas_notify_wps_event_fail(wpa_s, fail);
+#ifdef CONFIG_P2P
+ wpas_p2p_wps_failed(wpa_s, fail);
+#endif /* CONFIG_P2P */
+}
+
+
+static void wpas_wps_reenable_networks_cb(void *eloop_ctx, void *timeout_ctx);
+
+static void wpas_wps_reenable_networks(struct wpa_supplicant *wpa_s)
+{
+ struct wpa_ssid *ssid;
+ int changed = 0;
+
+ eloop_cancel_timeout(wpas_wps_reenable_networks_cb, wpa_s, NULL);
+
+ for (ssid = wpa_s->conf->ssid; ssid; ssid = ssid->next) {
+ if (ssid->disabled_for_connect && ssid->disabled) {
+ ssid->disabled_for_connect = 0;
+ ssid->disabled = 0;
+ wpas_notify_network_enabled_changed(wpa_s, ssid);
+ changed++;
+ }
+ }
+
+ if (changed) {
+#ifndef CONFIG_NO_CONFIG_WRITE
+ if (wpa_s->conf->update_config &&
+ wpa_config_write(wpa_s->confname, wpa_s->conf)) {
+ wpa_printf(MSG_DEBUG, "WPS: Failed to update "
+ "configuration");
+ }
+#endif /* CONFIG_NO_CONFIG_WRITE */
+ }
+}
+
+
+static void wpas_wps_reenable_networks_cb(void *eloop_ctx, void *timeout_ctx)
+{
+ struct wpa_supplicant *wpa_s = eloop_ctx;
+ /* Enable the networks disabled during wpas_wps_reassoc */
+ wpas_wps_reenable_networks(wpa_s);
}
@@ -395,6 +549,18 @@ static void wpa_supplicant_wps_event_success(struct wpa_supplicant *wpa_s)
wpa_msg(wpa_s, MSG_INFO, WPS_EVENT_SUCCESS);
wpa_s->wps_success = 1;
wpas_notify_wps_event_success(wpa_s);
+
+ /*
+ * Enable the networks disabled during wpas_wps_reassoc after 10
+ * seconds. The 10 seconds timer is to allow the data connection to be
+ * formed before allowing other networks to be selected.
+ */
+ eloop_register_timeout(10, 0, wpas_wps_reenable_networks_cb, wpa_s,
+ NULL);
+
+#ifdef CONFIG_P2P
+ wpas_p2p_wps_success(wpa_s, wpa_s->bssid, 0);
+#endif /* CONFIG_P2P */
}
@@ -468,6 +634,59 @@ static void wpa_supplicant_wps_event_er_enrollee_remove(
}
+static void wpa_supplicant_wps_event_er_ap_settings(
+ struct wpa_supplicant *wpa_s,
+ struct wps_event_er_ap_settings *ap_settings)
+{
+ char uuid_str[100];
+ char key_str[65];
+ const struct wps_credential *cred = ap_settings->cred;
+
+ key_str[0] = '\0';
+ if (cred->auth_type & (WPS_AUTH_WPAPSK | WPS_AUTH_WPA2PSK)) {
+ if (cred->key_len >= 8 && cred->key_len <= 64) {
+ os_memcpy(key_str, cred->key, cred->key_len);
+ key_str[cred->key_len] = '\0';
+ }
+ }
+
+ uuid_bin2str(ap_settings->uuid, uuid_str, sizeof(uuid_str));
+ /* Use wpa_msg_ctrl to avoid showing the key in debug log */
+ wpa_msg_ctrl(wpa_s, MSG_INFO, WPS_EVENT_ER_AP_SETTINGS
+ "uuid=%s ssid=%s auth_type=0x%04x encr_type=0x%04x "
+ "key=%s",
+ uuid_str, wpa_ssid_txt(cred->ssid, cred->ssid_len),
+ cred->auth_type, cred->encr_type, key_str);
+}
+
+
+static void wpa_supplicant_wps_event_er_set_sel_reg(
+ struct wpa_supplicant *wpa_s,
+ struct wps_event_er_set_selected_registrar *ev)
+{
+ char uuid_str[100];
+
+ uuid_bin2str(ev->uuid, uuid_str, sizeof(uuid_str));
+ switch (ev->state) {
+ case WPS_ER_SET_SEL_REG_START:
+ wpa_msg(wpa_s, MSG_DEBUG, WPS_EVENT_ER_SET_SEL_REG
+ "uuid=%s state=START sel_reg=%d dev_passwd_id=%u "
+ "sel_reg_config_methods=0x%x",
+ uuid_str, ev->sel_reg, ev->dev_passwd_id,
+ ev->sel_reg_config_methods);
+ break;
+ case WPS_ER_SET_SEL_REG_DONE:
+ wpa_msg(wpa_s, MSG_DEBUG, WPS_EVENT_ER_SET_SEL_REG
+ "uuid=%s state=DONE", uuid_str);
+ break;
+ case WPS_ER_SET_SEL_REG_FAILED:
+ wpa_msg(wpa_s, MSG_INFO, WPS_EVENT_ER_SET_SEL_REG
+ "uuid=%s state=FAILED", uuid_str);
+ break;
+ }
+}
+
+
static void wpa_supplicant_wps_event(void *ctx, enum wps_event event,
union wps_event_data *data)
{
@@ -483,6 +702,10 @@ static void wpa_supplicant_wps_event(void *ctx, enum wps_event event,
wpa_supplicant_wps_event_success(wpa_s);
break;
case WPS_EV_PWD_AUTH_FAIL:
+#ifdef CONFIG_AP
+ if (wpa_s->ap_iface && data->pwd_auth_fail.enrollee)
+ wpa_supplicant_ap_pwd_auth_fail(wpa_s);
+#endif /* CONFIG_AP */
break;
case WPS_EV_PBC_OVERLAP:
break;
@@ -502,6 +725,16 @@ static void wpa_supplicant_wps_event(void *ctx, enum wps_event event,
wpa_supplicant_wps_event_er_enrollee_remove(wpa_s,
&data->enrollee);
break;
+ case WPS_EV_ER_AP_SETTINGS:
+ wpa_supplicant_wps_event_er_ap_settings(wpa_s,
+ &data->ap_settings);
+ break;
+ case WPS_EV_ER_SET_SELECTED_REGISTRAR:
+ wpa_supplicant_wps_event_er_set_sel_reg(wpa_s,
+ &data->set_sel_reg);
+ break;
+ case WPS_EV_AP_PIN_SUCCESS:
+ break;
}
}
@@ -519,7 +752,12 @@ enum wps_request_type wpas_wps_get_req_type(struct wpa_ssid *ssid)
static void wpas_clear_wps(struct wpa_supplicant *wpa_s)
{
int id;
- struct wpa_ssid *ssid, *remove_ssid = NULL;
+ struct wpa_ssid *ssid, *remove_ssid = NULL, *prev_current;
+
+ prev_current = wpa_s->current_ssid;
+
+ /* Enable the networks disabled during wpas_wps_reassoc */
+ wpas_wps_reenable_networks(wpa_s);
eloop_cancel_timeout(wpas_wps_timeout, wpa_s, NULL);
@@ -538,18 +776,25 @@ static void wpas_clear_wps(struct wpa_supplicant *wpa_s)
id = -1;
ssid = ssid->next;
if (id >= 0) {
+ if (prev_current == remove_ssid) {
+ wpa_sm_set_config(wpa_s->wpa, NULL);
+ eapol_sm_notify_config(wpa_s->eapol, NULL,
+ NULL);
+ }
wpas_notify_network_removed(wpa_s, remove_ssid);
wpa_config_remove_network(wpa_s->conf, id);
}
}
+
+ wpas_wps_clear_ap_info(wpa_s);
}
static void wpas_wps_timeout(void *eloop_ctx, void *timeout_ctx)
{
struct wpa_supplicant *wpa_s = eloop_ctx;
- wpa_printf(MSG_INFO, WPS_EVENT_TIMEOUT "Requested operation timed "
- "out");
+ wpa_msg(wpa_s, MSG_INFO, WPS_EVENT_TIMEOUT "Requested operation timed "
+ "out");
wpas_clear_wps(wpa_s);
}
@@ -564,6 +809,7 @@ static struct wpa_ssid * wpas_wps_add_network(struct wpa_supplicant *wpa_s,
return NULL;
wpas_notify_network_added(wpa_s, ssid);
wpa_config_set_network_defaults(ssid);
+ ssid->temporary = 1;
if (wpa_config_set(ssid, "key_mgmt", "WPS", 0) < 0 ||
wpa_config_set(ssid, "eap", "WSC", 0) < 0 ||
wpa_config_set(ssid, "identity", registrar ?
@@ -575,12 +821,20 @@ static struct wpa_ssid * wpas_wps_add_network(struct wpa_supplicant *wpa_s,
}
if (bssid) {
+#ifndef CONFIG_P2P
struct wpa_bss *bss;
int count = 0;
+#endif /* CONFIG_P2P */
os_memcpy(ssid->bssid, bssid, ETH_ALEN);
ssid->bssid_set = 1;
+ /*
+ * Note: With P2P, the SSID may change at the time the WPS
+ * provisioning is started, so better not filter the AP based
+ * on the current SSID in the scan results.
+ */
+#ifndef CONFIG_P2P
dl_list_for_each(bss, &wpa_s->bss, struct wpa_bss, list) {
if (os_memcmp(bssid, bss->bssid, ETH_ALEN) != 0)
continue;
@@ -604,6 +858,7 @@ static struct wpa_ssid * wpas_wps_add_network(struct wpa_supplicant *wpa_s,
ssid->ssid = NULL;
ssid->ssid_len = 0;
}
+#endif /* CONFIG_P2P */
}
return ssid;
@@ -611,45 +866,90 @@ static struct wpa_ssid * wpas_wps_add_network(struct wpa_supplicant *wpa_s,
static void wpas_wps_reassoc(struct wpa_supplicant *wpa_s,
- struct wpa_ssid *selected)
+ struct wpa_ssid *selected, const u8 *bssid)
{
struct wpa_ssid *ssid;
+ struct wpa_bss *bss;
+
+ wpa_s->known_wps_freq = 0;
+ if (bssid) {
+ bss = wpa_bss_get_bssid(wpa_s, bssid);
+ if (bss && bss->freq > 0) {
+ wpa_s->known_wps_freq = 1;
+ wpa_s->wps_freq = bss->freq;
+ }
+ }
+
+ if (wpa_s->current_ssid)
+ wpa_supplicant_deauthenticate(
+ wpa_s, WLAN_REASON_DEAUTH_LEAVING);
/* Mark all other networks disabled and trigger reassociation */
ssid = wpa_s->conf->ssid;
while (ssid) {
int was_disabled = ssid->disabled;
- ssid->disabled = ssid != selected;
- if (was_disabled != ssid->disabled)
- wpas_notify_network_enabled_changed(wpa_s, ssid);
+ ssid->disabled_for_connect = 0;
+ /*
+ * In case the network object corresponds to a persistent group
+ * then do not send out network disabled signal. In addition,
+ * do not change disabled status of persistent network objects
+ * from 2 to 1 should we connect to another network.
+ */
+ if (was_disabled != 2) {
+ ssid->disabled = ssid != selected;
+ if (was_disabled != ssid->disabled) {
+ if (ssid->disabled)
+ ssid->disabled_for_connect = 1;
+ wpas_notify_network_enabled_changed(wpa_s,
+ ssid);
+ }
+ }
ssid = ssid->next;
}
wpa_s->disconnected = 0;
wpa_s->reassociate = 1;
wpa_s->scan_runs = 0;
+ wpa_s->normal_scans = 0;
wpa_s->wps_success = 0;
wpa_s->blacklist_cleared = 0;
wpa_supplicant_req_scan(wpa_s, 0, 0);
}
-int wpas_wps_start_pbc(struct wpa_supplicant *wpa_s, const u8 *bssid)
+int wpas_wps_start_pbc(struct wpa_supplicant *wpa_s, const u8 *bssid,
+ int p2p_group)
{
struct wpa_ssid *ssid;
wpas_clear_wps(wpa_s);
ssid = wpas_wps_add_network(wpa_s, 0, bssid);
if (ssid == NULL)
return -1;
+ ssid->temporary = 1;
+ ssid->p2p_group = p2p_group;
+#ifdef CONFIG_P2P
+ if (p2p_group && wpa_s->go_params && wpa_s->go_params->ssid_len) {
+ ssid->ssid = os_zalloc(wpa_s->go_params->ssid_len + 1);
+ if (ssid->ssid) {
+ ssid->ssid_len = wpa_s->go_params->ssid_len;
+ os_memcpy(ssid->ssid, wpa_s->go_params->ssid,
+ ssid->ssid_len);
+ wpa_hexdump_ascii(MSG_DEBUG, "WPS: Use specific AP "
+ "SSID", ssid->ssid, ssid->ssid_len);
+ }
+ }
+#endif /* CONFIG_P2P */
wpa_config_set(ssid, "phase1", "\"pbc=1\"", 0);
+ if (wpa_s->wps_fragment_size)
+ ssid->eap.fragment_size = wpa_s->wps_fragment_size;
eloop_register_timeout(WPS_PBC_WALK_TIME, 0, wpas_wps_timeout,
wpa_s, NULL);
- wpas_wps_reassoc(wpa_s, ssid);
+ wpas_wps_reassoc(wpa_s, ssid, bssid);
return 0;
}
int wpas_wps_start_pin(struct wpa_supplicant *wpa_s, const u8 *bssid,
- const char *pin)
+ const char *pin, int p2p_group, u16 dev_pw_id)
{
struct wpa_ssid *ssid;
char val[128];
@@ -659,68 +959,67 @@ int wpas_wps_start_pin(struct wpa_supplicant *wpa_s, const u8 *bssid,
ssid = wpas_wps_add_network(wpa_s, 0, bssid);
if (ssid == NULL)
return -1;
+ ssid->temporary = 1;
+ ssid->p2p_group = p2p_group;
+#ifdef CONFIG_P2P
+ if (p2p_group && wpa_s->go_params && wpa_s->go_params->ssid_len) {
+ ssid->ssid = os_zalloc(wpa_s->go_params->ssid_len + 1);
+ if (ssid->ssid) {
+ ssid->ssid_len = wpa_s->go_params->ssid_len;
+ os_memcpy(ssid->ssid, wpa_s->go_params->ssid,
+ ssid->ssid_len);
+ wpa_hexdump_ascii(MSG_DEBUG, "WPS: Use specific AP "
+ "SSID", ssid->ssid, ssid->ssid_len);
+ }
+ }
+#endif /* CONFIG_P2P */
if (pin)
- os_snprintf(val, sizeof(val), "\"pin=%s\"", pin);
+ os_snprintf(val, sizeof(val), "\"pin=%s dev_pw_id=%u\"",
+ pin, dev_pw_id);
else {
rpin = wps_generate_pin();
- os_snprintf(val, sizeof(val), "\"pin=%08d\"", rpin);
+ os_snprintf(val, sizeof(val), "\"pin=%08d dev_pw_id=%u\"",
+ rpin, dev_pw_id);
}
wpa_config_set(ssid, "phase1", val, 0);
+ if (wpa_s->wps_fragment_size)
+ ssid->eap.fragment_size = wpa_s->wps_fragment_size;
eloop_register_timeout(WPS_PBC_WALK_TIME, 0, wpas_wps_timeout,
wpa_s, NULL);
- wpas_wps_reassoc(wpa_s, ssid);
+ wpa_s->wps_ap_iter = 1;
+ wpas_wps_reassoc(wpa_s, ssid, bssid);
return rpin;
}
-#ifdef CONFIG_WPS_OOB
-int wpas_wps_start_oob(struct wpa_supplicant *wpa_s, char *device_type,
- char *path, char *method, char *name)
+/* Cancel the wps pbc/pin requests */
+int wpas_wps_cancel(struct wpa_supplicant *wpa_s)
{
- struct wps_context *wps = wpa_s->wps;
- struct oob_device_data *oob_dev;
-
- oob_dev = wps_get_oob_device(device_type);
- if (oob_dev == NULL)
- return -1;
- oob_dev->device_path = path;
- oob_dev->device_name = name;
- wps->oob_conf.oob_method = wps_get_oob_method(method);
-
- if (wps->oob_conf.oob_method == OOB_METHOD_DEV_PWD_E) {
- /*
- * Use pre-configured DH keys in order to be able to write the
- * key hash into the OOB file.
- */
- wpabuf_free(wps->dh_pubkey);
- wpabuf_free(wps->dh_privkey);
- wps->dh_privkey = NULL;
- wps->dh_pubkey = NULL;
- dh5_free(wps->dh_ctx);
- wps->dh_ctx = dh5_init(&wps->dh_privkey, &wps->dh_pubkey);
- wps->dh_pubkey = wpabuf_zeropad(wps->dh_pubkey, 192);
- if (wps->dh_ctx == NULL || wps->dh_pubkey == NULL) {
- wpa_printf(MSG_ERROR, "WPS: Failed to initialize "
- "Diffie-Hellman handshake");
- return -1;
- }
+#ifdef CONFIG_AP
+ if (wpa_s->ap_iface) {
+ wpa_printf(MSG_DEBUG, "WPS: Cancelling in AP mode");
+ return wpa_supplicant_ap_wps_cancel(wpa_s);
}
+#endif /* CONFIG_AP */
- if (wps->oob_conf.oob_method == OOB_METHOD_CRED)
+ if (wpa_s->wpa_state == WPA_SCANNING ||
+ wpa_s->wpa_state == WPA_DISCONNECTED) {
+ wpa_printf(MSG_DEBUG, "WPS: Cancel operation - cancel scan");
+ wpa_supplicant_cancel_scan(wpa_s);
wpas_clear_wps(wpa_s);
-
- if (wps_process_oob(wps, oob_dev, 0) < 0)
- return -1;
-
- if ((wps->oob_conf.oob_method == OOB_METHOD_DEV_PWD_E ||
- wps->oob_conf.oob_method == OOB_METHOD_DEV_PWD_R) &&
- wpas_wps_start_pin(wpa_s, NULL,
- wpabuf_head(wps->oob_conf.dev_password)) < 0)
- return -1;
+ } else if (wpa_s->wpa_state >= WPA_ASSOCIATED) {
+ wpa_printf(MSG_DEBUG, "WPS: Cancel operation - "
+ "deauthenticate");
+ wpa_supplicant_deauthenticate(wpa_s,
+ WLAN_REASON_DEAUTH_LEAVING);
+ wpas_clear_wps(wpa_s);
+ } else {
+ wpas_wps_reenable_networks(wpa_s);
+ wpas_wps_clear_ap_info(wpa_s);
+ }
return 0;
}
-#endif /* CONFIG_WPS_OOB */
int wpas_wps_start_reg(struct wpa_supplicant *wpa_s, const u8 *bssid,
@@ -737,6 +1036,7 @@ int wpas_wps_start_reg(struct wpa_supplicant *wpa_s, const u8 *bssid,
ssid = wpas_wps_add_network(wpa_s, 1, bssid);
if (ssid == NULL)
return -1;
+ ssid->temporary = 1;
pos = val;
end = pos + sizeof(val);
res = os_snprintf(pos, end - pos, "\"pin=%s", pin);
@@ -756,9 +1056,11 @@ int wpas_wps_start_reg(struct wpa_supplicant *wpa_s, const u8 *bssid,
if (res < 0 || res >= end - pos)
return -1;
wpa_config_set(ssid, "phase1", val, 0);
+ if (wpa_s->wps_fragment_size)
+ ssid->eap.fragment_size = wpa_s->wps_fragment_size;
eloop_register_timeout(WPS_PBC_WALK_TIME, 0, wpas_wps_timeout,
wpa_s, NULL);
- wpas_wps_reassoc(wpa_s, ssid);
+ wpas_wps_reassoc(wpa_s, ssid, bssid);
return 0;
}
@@ -805,16 +1107,90 @@ static void wpas_wps_set_sel_reg_cb(void *ctx, int sel_reg, u16 dev_passwd_id,
if (wpa_s->wps_er == NULL)
return;
+ wpa_printf(MSG_DEBUG, "WPS ER: SetSelectedRegistrar - sel_reg=%d "
+ "dev_password_id=%u sel_reg_config_methods=0x%x",
+ sel_reg, dev_passwd_id, sel_reg_config_methods);
wps_er_set_sel_reg(wpa_s->wps_er, sel_reg, dev_passwd_id,
sel_reg_config_methods);
#endif /* CONFIG_WPS_ER */
}
+static u16 wps_fix_config_methods(u16 config_methods)
+{
+#ifdef CONFIG_WPS2
+ if ((config_methods &
+ (WPS_CONFIG_DISPLAY | WPS_CONFIG_VIRT_DISPLAY |
+ WPS_CONFIG_PHY_DISPLAY)) == WPS_CONFIG_DISPLAY) {
+ wpa_printf(MSG_INFO, "WPS: Converting display to "
+ "virtual_display for WPS 2.0 compliance");
+ config_methods |= WPS_CONFIG_VIRT_DISPLAY;
+ }
+ if ((config_methods &
+ (WPS_CONFIG_PUSHBUTTON | WPS_CONFIG_VIRT_PUSHBUTTON |
+ WPS_CONFIG_PHY_PUSHBUTTON)) == WPS_CONFIG_PUSHBUTTON) {
+ wpa_printf(MSG_INFO, "WPS: Converting push_button to "
+ "virtual_push_button for WPS 2.0 compliance");
+ config_methods |= WPS_CONFIG_VIRT_PUSHBUTTON;
+ }
+#endif /* CONFIG_WPS2 */
+
+ return config_methods;
+}
+
+
+static void wpas_wps_set_uuid(struct wpa_supplicant *wpa_s,
+ struct wps_context *wps)
+{
+ wpa_printf(MSG_DEBUG, "WPS: Set UUID for interface %s", wpa_s->ifname);
+ if (is_nil_uuid(wpa_s->conf->uuid)) {
+ struct wpa_supplicant *first;
+ first = wpa_s->global->ifaces;
+ while (first && first->next)
+ first = first->next;
+ if (first && first != wpa_s) {
+ if (wps != wpa_s->global->ifaces->wps)
+ os_memcpy(wps->uuid,
+ wpa_s->global->ifaces->wps->uuid,
+ WPS_UUID_LEN);
+ wpa_hexdump(MSG_DEBUG, "WPS: UUID from the first "
+ "interface", wps->uuid, WPS_UUID_LEN);
+ } else {
+ uuid_gen_mac_addr(wpa_s->own_addr, wps->uuid);
+ wpa_hexdump(MSG_DEBUG, "WPS: UUID based on MAC "
+ "address", wps->uuid, WPS_UUID_LEN);
+ }
+ } else {
+ os_memcpy(wps->uuid, wpa_s->conf->uuid, WPS_UUID_LEN);
+ wpa_hexdump(MSG_DEBUG, "WPS: UUID based on configuration",
+ wps->uuid, WPS_UUID_LEN);
+ }
+}
+
+
+static void wpas_wps_set_vendor_ext_m1(struct wpa_supplicant *wpa_s,
+ struct wps_context *wps)
+{
+ wpabuf_free(wps->dev.vendor_ext_m1);
+ wps->dev.vendor_ext_m1 = NULL;
+
+ if (wpa_s->conf->wps_vendor_ext_m1) {
+ wps->dev.vendor_ext_m1 =
+ wpabuf_dup(wpa_s->conf->wps_vendor_ext_m1);
+ if (!wps->dev.vendor_ext_m1) {
+ wpa_printf(MSG_ERROR, "WPS: Cannot "
+ "allocate memory for vendor_ext_m1");
+ }
+ }
+}
+
+
int wpas_wps_init(struct wpa_supplicant *wpa_s)
{
struct wps_context *wps;
struct wps_registrar_config rcfg;
+ struct hostapd_hw_modes *modes;
+ u16 m;
wps = os_zalloc(sizeof(*wps));
if (wps == NULL)
@@ -831,22 +1207,44 @@ int wpas_wps_init(struct wpa_supplicant *wpa_s)
wps->dev.serial_number = wpa_s->conf->serial_number;
wps->config_methods =
wps_config_methods_str2bin(wpa_s->conf->config_methods);
- if (wpa_s->conf->device_type &&
- wps_dev_type_str2bin(wpa_s->conf->device_type,
- wps->dev.pri_dev_type) < 0) {
- wpa_printf(MSG_ERROR, "WPS: Invalid device_type");
+ if ((wps->config_methods & (WPS_CONFIG_DISPLAY | WPS_CONFIG_LABEL)) ==
+ (WPS_CONFIG_DISPLAY | WPS_CONFIG_LABEL)) {
+ wpa_printf(MSG_ERROR, "WPS: Both Label and Display config "
+ "methods are not allowed at the same time");
os_free(wps);
return -1;
}
+ wps->config_methods = wps_fix_config_methods(wps->config_methods);
+ wps->dev.config_methods = wps->config_methods;
+ os_memcpy(wps->dev.pri_dev_type, wpa_s->conf->device_type,
+ WPS_DEV_TYPE_LEN);
+
+ wps->dev.num_sec_dev_types = wpa_s->conf->num_sec_device_types;
+ os_memcpy(wps->dev.sec_dev_type, wpa_s->conf->sec_device_type,
+ WPS_DEV_TYPE_LEN * wps->dev.num_sec_dev_types);
+
+ wpas_wps_set_vendor_ext_m1(wpa_s, wps);
+
wps->dev.os_version = WPA_GET_BE32(wpa_s->conf->os_version);
- wps->dev.rf_bands = WPS_RF_24GHZ | WPS_RF_50GHZ; /* TODO: config */
+ modes = wpa_s->hw.modes;
+ if (modes) {
+ for (m = 0; m < wpa_s->hw.num_modes; m++) {
+ if (modes[m].mode == HOSTAPD_MODE_IEEE80211B ||
+ modes[m].mode == HOSTAPD_MODE_IEEE80211G)
+ wps->dev.rf_bands |= WPS_RF_24GHZ;
+ else if (modes[m].mode == HOSTAPD_MODE_IEEE80211A)
+ wps->dev.rf_bands |= WPS_RF_50GHZ;
+ }
+ }
+ if (wps->dev.rf_bands == 0) {
+ /*
+ * Default to claiming support for both bands if the driver
+ * does not provide support for fetching supported bands.
+ */
+ wps->dev.rf_bands = WPS_RF_24GHZ | WPS_RF_50GHZ;
+ }
os_memcpy(wps->dev.mac_addr, wpa_s->own_addr, ETH_ALEN);
- if (is_nil_uuid(wpa_s->conf->uuid)) {
- uuid_gen_mac_addr(wpa_s->own_addr, wps->uuid);
- wpa_hexdump(MSG_DEBUG, "WPS: UUID based on MAC address",
- wps->uuid, WPS_UUID_LEN);
- } else
- os_memcpy(wps->uuid, wpa_s->conf->uuid, WPS_UUID_LEN);
+ wpas_wps_set_uuid(wpa_s, wps);
wps->auth_types = WPS_AUTH_WPA2PSK | WPS_AUTH_WPAPSK;
wps->encr_types = WPS_ENCR_AES | WPS_ENCR_TKIP;
@@ -873,6 +1271,8 @@ int wpas_wps_init(struct wpa_supplicant *wpa_s)
void wpas_wps_deinit(struct wpa_supplicant *wpa_s)
{
eloop_cancel_timeout(wpas_wps_timeout, wpa_s, NULL);
+ eloop_cancel_timeout(wpas_wps_reenable_networks_cb, wpa_s, NULL);
+ wpas_wps_clear_ap_info(wpa_s);
if (wpa_s->wps == NULL)
return;
@@ -885,8 +1285,7 @@ void wpas_wps_deinit(struct wpa_supplicant *wpa_s)
wps_registrar_deinit(wpa_s->wps->registrar);
wpabuf_free(wpa_s->wps->dh_pubkey);
wpabuf_free(wpa_s->wps->dh_privkey);
- wpabuf_free(wpa_s->wps->oob_conf.pubkey_hash);
- wpabuf_free(wpa_s->wps->oob_conf.dev_password);
+ wpabuf_free(wpa_s->wps->dev.vendor_ext_m1);
os_free(wpa_s->wps->network_key);
os_free(wpa_s->wps);
wpa_s->wps = NULL;
@@ -894,14 +1293,14 @@ void wpas_wps_deinit(struct wpa_supplicant *wpa_s)
int wpas_wps_ssid_bss_match(struct wpa_supplicant *wpa_s,
- struct wpa_ssid *ssid, struct wpa_scan_res *bss)
+ struct wpa_ssid *ssid, struct wpa_bss *bss)
{
struct wpabuf *wps_ie;
if (!(ssid->key_mgmt & WPA_KEY_MGMT_WPS))
return -1;
- wps_ie = wpa_scan_get_vendor_ie_multi(bss, WPS_IE_VENDOR_TYPE);
+ wps_ie = wpa_bss_get_vendor_ie_multi(bss, WPS_IE_VENDOR_TYPE);
if (eap_is_wps_pbc_enrollee(&ssid->eap)) {
if (!wps_ie) {
wpa_printf(MSG_DEBUG, " skip - non-WPS AP");
@@ -929,12 +1328,13 @@ int wpas_wps_ssid_bss_match(struct wpa_supplicant *wpa_s,
}
/*
- * Start with WPS APs that advertise active PIN Registrar and
- * allow any WPS AP after third scan since some APs do not set
- * Selected Registrar attribute properly when using external
- * Registrar.
+ * Start with WPS APs that advertise our address as an
+ * authorized MAC (v2.0) or active PIN Registrar (v1.0) and
+ * allow any WPS AP after couple of scans since some APs do not
+ * set Selected Registrar attribute properly when using
+ * external Registrar.
*/
- if (!wps_is_selected_pin_registrar(wps_ie)) {
+ if (!wps_is_addr_authorized(wps_ie, wpa_s->own_addr, 1)) {
if (wpa_s->scan_runs < WPS_PIN_SCAN_IGNORE_SEL_REG) {
wpa_printf(MSG_DEBUG, " skip - WPS AP "
"without active PIN Registrar");
@@ -944,7 +1344,7 @@ int wpas_wps_ssid_bss_match(struct wpa_supplicant *wpa_s,
wpa_printf(MSG_DEBUG, " selected based on WPS IE");
} else {
wpa_printf(MSG_DEBUG, " selected based on WPS IE "
- "(Active PIN)");
+ "(Authorized MAC or Active PIN)");
}
wpabuf_free(wps_ie);
return 1;
@@ -962,21 +1362,21 @@ int wpas_wps_ssid_bss_match(struct wpa_supplicant *wpa_s,
int wpas_wps_ssid_wildcard_ok(struct wpa_supplicant *wpa_s,
struct wpa_ssid *ssid,
- struct wpa_scan_res *bss)
+ struct wpa_bss *bss)
{
struct wpabuf *wps_ie = NULL;
int ret = 0;
if (eap_is_wps_pbc_enrollee(&ssid->eap)) {
- wps_ie = wpa_scan_get_vendor_ie_multi(bss, WPS_IE_VENDOR_TYPE);
+ wps_ie = wpa_bss_get_vendor_ie_multi(bss, WPS_IE_VENDOR_TYPE);
if (wps_ie && wps_is_selected_pbc_registrar(wps_ie)) {
/* allow wildcard SSID for WPS PBC */
ret = 1;
}
} else if (eap_is_wps_pin_enrollee(&ssid->eap)) {
- wps_ie = wpa_scan_get_vendor_ie_multi(bss, WPS_IE_VENDOR_TYPE);
+ wps_ie = wpa_bss_get_vendor_ie_multi(bss, WPS_IE_VENDOR_TYPE);
if (wps_ie &&
- (wps_is_selected_pin_registrar(wps_ie) ||
+ (wps_is_addr_authorized(wps_ie, wpa_s->own_addr, 1) ||
wpa_s->scan_runs >= WPS_PIN_SCAN_IGNORE_SEL_REG)) {
/* allow wildcard SSID for WPS PIN */
ret = 1;
@@ -989,6 +1389,28 @@ int wpas_wps_ssid_wildcard_ok(struct wpa_supplicant *wpa_s,
ret = 1;
}
+#ifdef CONFIG_WPS_STRICT
+ if (wps_ie) {
+ if (wps_validate_beacon_probe_resp(wps_ie, bss->beacon_ie_len >
+ 0, bss->bssid) < 0)
+ ret = 0;
+ if (bss->beacon_ie_len) {
+ struct wpabuf *bcn_wps;
+ bcn_wps = wpa_bss_get_vendor_ie_multi_beacon(
+ bss, WPS_IE_VENDOR_TYPE);
+ if (bcn_wps == NULL) {
+ wpa_printf(MSG_DEBUG, "WPS: Mandatory WPS IE "
+ "missing from AP Beacon");
+ ret = 0;
+ } else {
+ if (wps_validate_beacon(wps_ie) < 0)
+ ret = 0;
+ wpabuf_free(bcn_wps);
+ }
+ }
+ }
+#endif /* CONFIG_WPS_STRICT */
+
wpabuf_free(wps_ie);
return ret;
@@ -1006,12 +1428,21 @@ int wpas_wps_scan_pbc_overlap(struct wpa_supplicant *wpa_s,
if (!eap_is_wps_pbc_enrollee(&ssid->eap))
return 0;
+ wpa_printf(MSG_DEBUG, "WPS: Check whether PBC session overlap is "
+ "present in scan results; selected BSSID " MACSTR,
+ MAC2STR(selected->bssid));
+
/* Make sure that only one AP is in active PBC mode */
wps_ie = wpa_bss_get_vendor_ie_multi(selected, WPS_IE_VENDOR_TYPE);
- if (wps_ie)
+ if (wps_ie) {
sel_uuid = wps_get_uuid_e(wps_ie);
- else
+ wpa_hexdump(MSG_DEBUG, "WPS: UUID of the selected BSS",
+ sel_uuid, UUID_LEN);
+ } else {
+ wpa_printf(MSG_DEBUG, "WPS: Selected BSS does not include "
+ "WPS IE?!");
sel_uuid = NULL;
+ }
dl_list_for_each(bss, &wpa_s->bss, struct wpa_bss, list) {
struct wpabuf *ie;
@@ -1024,10 +1455,18 @@ int wpas_wps_scan_pbc_overlap(struct wpa_supplicant *wpa_s,
wpabuf_free(ie);
continue;
}
+ wpa_printf(MSG_DEBUG, "WPS: Another BSS in active PBC mode: "
+ MACSTR, MAC2STR(bss->bssid));
uuid = wps_get_uuid_e(ie);
+ wpa_hexdump(MSG_DEBUG, "WPS: UUID of the other BSS",
+ uuid, UUID_LEN);
if (sel_uuid == NULL || uuid == NULL ||
- os_memcmp(sel_uuid, uuid, 16) != 0) {
+ os_memcmp(sel_uuid, uuid, UUID_LEN) != 0) {
ret = 1; /* PBC overlap */
+ wpa_msg(wpa_s, MSG_INFO, "WPS: PBC overlap detected: "
+ MACSTR " and " MACSTR,
+ MAC2STR(selected->bssid),
+ MAC2STR(bss->bssid));
wpabuf_free(ie);
break;
}
@@ -1046,6 +1485,7 @@ int wpas_wps_scan_pbc_overlap(struct wpa_supplicant *wpa_s,
void wpas_wps_notify_scan_results(struct wpa_supplicant *wpa_s)
{
struct wpa_bss *bss;
+ unsigned int pbc = 0, auth = 0, pin = 0, wps = 0;
if (wpa_s->disconnected || wpa_s->wpa_state >= WPA_ASSOCIATED)
return;
@@ -1056,17 +1496,24 @@ void wpas_wps_notify_scan_results(struct wpa_supplicant *wpa_s)
if (!ie)
continue;
if (wps_is_selected_pbc_registrar(ie))
- wpa_msg_ctrl(wpa_s, MSG_INFO,
- WPS_EVENT_AP_AVAILABLE_PBC);
+ pbc++;
+ else if (wps_is_addr_authorized(ie, wpa_s->own_addr, 0))
+ auth++;
else if (wps_is_selected_pin_registrar(ie))
- wpa_msg_ctrl(wpa_s, MSG_INFO,
- WPS_EVENT_AP_AVAILABLE_PIN);
+ pin++;
else
- wpa_msg_ctrl(wpa_s, MSG_INFO,
- WPS_EVENT_AP_AVAILABLE);
+ wps++;
wpabuf_free(ie);
- break;
}
+
+ if (pbc)
+ wpa_msg_ctrl(wpa_s, MSG_INFO, WPS_EVENT_AP_AVAILABLE_PBC);
+ else if (auth)
+ wpa_msg_ctrl(wpa_s, MSG_INFO, WPS_EVENT_AP_AVAILABLE_AUTH);
+ else if (pin)
+ wpa_msg_ctrl(wpa_s, MSG_INFO, WPS_EVENT_AP_AVAILABLE_PIN);
+ else if (wps)
+ wpa_msg_ctrl(wpa_s, MSG_INFO, WPS_EVENT_AP_AVAILABLE);
}
@@ -1099,14 +1546,14 @@ int wpas_wps_scan_result_text(const u8 *ies, size_t ies_len, char *buf,
}
-int wpas_wps_er_start(struct wpa_supplicant *wpa_s)
+int wpas_wps_er_start(struct wpa_supplicant *wpa_s, const char *filter)
{
#ifdef CONFIG_WPS_ER
if (wpa_s->wps_er) {
wps_er_refresh(wpa_s->wps_er);
return 0;
}
- wpa_s->wps_er = wps_er_init(wpa_s->wps, wpa_s->ifname);
+ wpa_s->wps_er = wps_er_init(wpa_s->wps, wpa_s->ifname, filter);
if (wpa_s->wps_er == NULL)
return -1;
return 0;
@@ -1127,8 +1574,8 @@ int wpas_wps_er_stop(struct wpa_supplicant *wpa_s)
#ifdef CONFIG_WPS_ER
-int wpas_wps_er_add_pin(struct wpa_supplicant *wpa_s, const char *uuid,
- const char *pin)
+int wpas_wps_er_add_pin(struct wpa_supplicant *wpa_s, const u8 *addr,
+ const char *uuid, const char *pin)
{
u8 u[UUID_LEN];
int any = 0;
@@ -1137,7 +1584,8 @@ int wpas_wps_er_add_pin(struct wpa_supplicant *wpa_s, const char *uuid,
any = 1;
else if (uuid_str2bin(uuid, u))
return -1;
- return wps_registrar_add_pin(wpa_s->wps->registrar, any ? NULL : u,
+ return wps_registrar_add_pin(wpa_s->wps->registrar, addr,
+ any ? NULL : u,
(const u8 *) pin, os_strlen(pin), 300);
}
@@ -1164,10 +1612,135 @@ int wpas_wps_er_learn(struct wpa_supplicant *wpa_s, const char *uuid,
}
+int wpas_wps_er_set_config(struct wpa_supplicant *wpa_s, const char *uuid,
+ int id)
+{
+ u8 u[UUID_LEN];
+ struct wpa_ssid *ssid;
+ struct wps_credential cred;
+
+ if (uuid_str2bin(uuid, u))
+ return -1;
+ ssid = wpa_config_get_network(wpa_s->conf, id);
+ if (ssid == NULL || ssid->ssid == NULL)
+ return -1;
+
+ os_memset(&cred, 0, sizeof(cred));
+ if (ssid->ssid_len > 32)
+ return -1;
+ os_memcpy(cred.ssid, ssid->ssid, ssid->ssid_len);
+ cred.ssid_len = ssid->ssid_len;
+ if (ssid->key_mgmt & WPA_KEY_MGMT_PSK) {
+ cred.auth_type = (ssid->proto & WPA_PROTO_RSN) ?
+ WPS_AUTH_WPA2PSK : WPS_AUTH_WPAPSK;
+ if (ssid->pairwise_cipher & WPA_CIPHER_CCMP)
+ cred.encr_type = WPS_ENCR_AES;
+ else
+ cred.encr_type = WPS_ENCR_TKIP;
+ if (ssid->passphrase) {
+ cred.key_len = os_strlen(ssid->passphrase);
+ if (cred.key_len >= 64)
+ return -1;
+ os_memcpy(cred.key, ssid->passphrase, cred.key_len);
+ } else if (ssid->psk_set) {
+ cred.key_len = 32;
+ os_memcpy(cred.key, ssid->psk, 32);
+ } else
+ return -1;
+ } else {
+ cred.auth_type = WPS_AUTH_OPEN;
+ cred.encr_type = WPS_ENCR_NONE;
+ }
+ return wps_er_set_config(wpa_s->wps_er, u, &cred);
+}
+
+
+int wpas_wps_er_config(struct wpa_supplicant *wpa_s, const char *uuid,
+ const char *pin, struct wps_new_ap_settings *settings)
+{
+ u8 u[UUID_LEN];
+ struct wps_credential cred;
+ size_t len;
+
+ if (uuid_str2bin(uuid, u))
+ return -1;
+ if (settings->ssid_hex == NULL || settings->auth == NULL ||
+ settings->encr == NULL || settings->key_hex == NULL)
+ return -1;
+
+ os_memset(&cred, 0, sizeof(cred));
+ len = os_strlen(settings->ssid_hex);
+ if ((len & 1) || len > 2 * sizeof(cred.ssid) ||
+ hexstr2bin(settings->ssid_hex, cred.ssid, len / 2))
+ return -1;
+ cred.ssid_len = len / 2;
+
+ len = os_strlen(settings->key_hex);
+ if ((len & 1) || len > 2 * sizeof(cred.key) ||
+ hexstr2bin(settings->key_hex, cred.key, len / 2))
+ return -1;
+ cred.key_len = len / 2;
+
+ if (os_strcmp(settings->auth, "OPEN") == 0)
+ cred.auth_type = WPS_AUTH_OPEN;
+ else if (os_strcmp(settings->auth, "WPAPSK") == 0)
+ cred.auth_type = WPS_AUTH_WPAPSK;
+ else if (os_strcmp(settings->auth, "WPA2PSK") == 0)
+ cred.auth_type = WPS_AUTH_WPA2PSK;
+ else
+ return -1;
+
+ if (os_strcmp(settings->encr, "NONE") == 0)
+ cred.encr_type = WPS_ENCR_NONE;
+ else if (os_strcmp(settings->encr, "WEP") == 0)
+ cred.encr_type = WPS_ENCR_WEP;
+ else if (os_strcmp(settings->encr, "TKIP") == 0)
+ cred.encr_type = WPS_ENCR_TKIP;
+ else if (os_strcmp(settings->encr, "CCMP") == 0)
+ cred.encr_type = WPS_ENCR_AES;
+ else
+ return -1;
+
+ return wps_er_config(wpa_s->wps_er, u, (const u8 *) pin,
+ os_strlen(pin), &cred);
+}
+
+
+#ifdef CONFIG_WPS_NFC
+struct wpabuf * wpas_wps_er_nfc_config_token(struct wpa_supplicant *wpa_s,
+ int ndef, const char *uuid)
+{
+ struct wpabuf *ret;
+ u8 u[UUID_LEN];
+
+ if (!wpa_s->wps_er)
+ return NULL;
+
+ if (uuid_str2bin(uuid, u))
+ return NULL;
+
+ ret = wps_er_nfc_config_token(wpa_s->wps_er, u);
+ if (ndef && ret) {
+ struct wpabuf *tmp;
+ tmp = ndef_build_wifi(ret);
+ wpabuf_free(ret);
+ if (tmp == NULL)
+ return NULL;
+ ret = tmp;
+ }
+
+ return ret;
+}
+#endif /* CONFIG_WPS_NFC */
+
+
+static int callbacks_pending = 0;
+
static void wpas_wps_terminate_cb(void *ctx)
{
wpa_printf(MSG_DEBUG, "WPS ER: Terminated");
- eloop_terminate();
+ if (--callbacks_pending <= 0)
+ eloop_terminate();
}
#endif /* CONFIG_WPS_ER */
@@ -1176,6 +1749,7 @@ int wpas_wps_terminate_pending(struct wpa_supplicant *wpa_s)
{
#ifdef CONFIG_WPS_ER
if (wpa_s->wps_er) {
+ callbacks_pending++;
wps_er_deinit(wpa_s->wps_er, wpas_wps_terminate_cb, wpa_s);
wpa_s->wps_er = NULL;
return 1;
@@ -1183,3 +1757,386 @@ int wpas_wps_terminate_pending(struct wpa_supplicant *wpa_s)
#endif /* CONFIG_WPS_ER */
return 0;
}
+
+
+int wpas_wps_in_progress(struct wpa_supplicant *wpa_s)
+{
+ struct wpa_ssid *ssid;
+
+ for (ssid = wpa_s->conf->ssid; ssid; ssid = ssid->next) {
+ if (!ssid->disabled && ssid->key_mgmt == WPA_KEY_MGMT_WPS)
+ return 1;
+ }
+
+ return 0;
+}
+
+
+void wpas_wps_update_config(struct wpa_supplicant *wpa_s)
+{
+ struct wps_context *wps = wpa_s->wps;
+
+ if (wps == NULL)
+ return;
+
+ if (wpa_s->conf->changed_parameters & CFG_CHANGED_CONFIG_METHODS) {
+ wps->config_methods = wps_config_methods_str2bin(
+ wpa_s->conf->config_methods);
+ if ((wps->config_methods &
+ (WPS_CONFIG_DISPLAY | WPS_CONFIG_LABEL)) ==
+ (WPS_CONFIG_DISPLAY | WPS_CONFIG_LABEL)) {
+ wpa_printf(MSG_ERROR, "WPS: Both Label and Display "
+ "config methods are not allowed at the "
+ "same time");
+ wps->config_methods &= ~WPS_CONFIG_LABEL;
+ }
+ }
+ wps->config_methods = wps_fix_config_methods(wps->config_methods);
+ wps->dev.config_methods = wps->config_methods;
+
+ if (wpa_s->conf->changed_parameters & CFG_CHANGED_DEVICE_TYPE)
+ os_memcpy(wps->dev.pri_dev_type, wpa_s->conf->device_type,
+ WPS_DEV_TYPE_LEN);
+
+ if (wpa_s->conf->changed_parameters & CFG_CHANGED_SEC_DEVICE_TYPE) {
+ wps->dev.num_sec_dev_types = wpa_s->conf->num_sec_device_types;
+ os_memcpy(wps->dev.sec_dev_type, wpa_s->conf->sec_device_type,
+ wps->dev.num_sec_dev_types * WPS_DEV_TYPE_LEN);
+ }
+
+ if (wpa_s->conf->changed_parameters & CFG_CHANGED_VENDOR_EXTENSION)
+ wpas_wps_set_vendor_ext_m1(wpa_s, wps);
+
+ if (wpa_s->conf->changed_parameters & CFG_CHANGED_OS_VERSION)
+ wps->dev.os_version = WPA_GET_BE32(wpa_s->conf->os_version);
+
+ if (wpa_s->conf->changed_parameters & CFG_CHANGED_UUID)
+ wpas_wps_set_uuid(wpa_s, wps);
+
+ if (wpa_s->conf->changed_parameters &
+ (CFG_CHANGED_DEVICE_NAME | CFG_CHANGED_WPS_STRING)) {
+ /* Update pointers to make sure they refer current values */
+ wps->dev.device_name = wpa_s->conf->device_name;
+ wps->dev.manufacturer = wpa_s->conf->manufacturer;
+ wps->dev.model_name = wpa_s->conf->model_name;
+ wps->dev.model_number = wpa_s->conf->model_number;
+ wps->dev.serial_number = wpa_s->conf->serial_number;
+ }
+}
+
+
+#ifdef CONFIG_WPS_NFC
+
+struct wpabuf * wpas_wps_nfc_token(struct wpa_supplicant *wpa_s, int ndef)
+{
+ return wps_nfc_token_gen(ndef, &wpa_s->conf->wps_nfc_dev_pw_id,
+ &wpa_s->conf->wps_nfc_dh_pubkey,
+ &wpa_s->conf->wps_nfc_dh_privkey,
+ &wpa_s->conf->wps_nfc_dev_pw);
+}
+
+
+int wpas_wps_start_nfc(struct wpa_supplicant *wpa_s, const u8 *bssid)
+{
+ struct wps_context *wps = wpa_s->wps;
+ char pw[32 * 2 + 1];
+
+ if (wpa_s->conf->wps_nfc_dh_pubkey == NULL ||
+ wpa_s->conf->wps_nfc_dh_privkey == NULL ||
+ wpa_s->conf->wps_nfc_dev_pw == NULL)
+ return -1;
+
+ dh5_free(wps->dh_ctx);
+ wpabuf_free(wps->dh_pubkey);
+ wpabuf_free(wps->dh_privkey);
+ wps->dh_privkey = wpabuf_dup(wpa_s->conf->wps_nfc_dh_privkey);
+ wps->dh_pubkey = wpabuf_dup(wpa_s->conf->wps_nfc_dh_pubkey);
+ if (wps->dh_privkey == NULL || wps->dh_pubkey == NULL) {
+ wps->dh_ctx = NULL;
+ wpabuf_free(wps->dh_pubkey);
+ wps->dh_pubkey = NULL;
+ wpabuf_free(wps->dh_privkey);
+ wps->dh_privkey = NULL;
+ return -1;
+ }
+ wps->dh_ctx = dh5_init_fixed(wps->dh_privkey, wps->dh_pubkey);
+ if (wps->dh_ctx == NULL)
+ return -1;
+
+ wpa_snprintf_hex_uppercase(pw, sizeof(pw),
+ wpabuf_head(wpa_s->conf->wps_nfc_dev_pw),
+ wpabuf_len(wpa_s->conf->wps_nfc_dev_pw));
+ return wpas_wps_start_pin(wpa_s, bssid, pw, 0,
+ wpa_s->conf->wps_nfc_dev_pw_id);
+}
+
+
+static int wpas_wps_use_cred(struct wpa_supplicant *wpa_s,
+ struct wps_parse_attr *attr)
+{
+ wpa_s->wps_ap_channel = 0;
+
+ if (wps_oob_use_cred(wpa_s->wps, attr) < 0)
+ return -1;
+
+ if (wpa_s->wpa_state == WPA_INTERFACE_DISABLED)
+ return 0;
+
+ wpa_printf(MSG_DEBUG, "WPS: Request reconnection with new network "
+ "based on the received credential added");
+ wpa_s->normal_scans = 0;
+ wpa_supplicant_reinit_autoscan(wpa_s);
+ if (wpa_s->wps_ap_channel) {
+ u16 chan = wpa_s->wps_ap_channel;
+ int freq = 0;
+
+ if (chan >= 1 && chan <= 13)
+ freq = 2407 + 5 * chan;
+ else if (chan == 14)
+ freq = 2484;
+ else if (chan >= 30)
+ freq = 5000 + 5 * chan;
+
+ if (freq) {
+ wpa_printf(MSG_DEBUG, "WPS: Credential indicated "
+ "AP channel %u -> %u MHz", chan, freq);
+ wpa_s->after_wps = 5;
+ wpa_s->wps_freq = freq;
+ }
+ }
+ wpa_s->disconnected = 0;
+ wpa_s->reassociate = 1;
+ wpa_supplicant_req_scan(wpa_s, 0, 0);
+
+ return 0;
+}
+
+
+#ifdef CONFIG_WPS_ER
+static int wpas_wps_add_nfc_password_token(struct wpa_supplicant *wpa_s,
+ struct wps_parse_attr *attr)
+{
+ return wps_registrar_add_nfc_password_token(
+ wpa_s->wps->registrar, attr->oob_dev_password,
+ attr->oob_dev_password_len);
+}
+#endif /* CONFIG_WPS_ER */
+
+
+static int wpas_wps_nfc_tag_process(struct wpa_supplicant *wpa_s,
+ const struct wpabuf *wps)
+{
+ struct wps_parse_attr attr;
+
+ wpa_hexdump_buf(MSG_DEBUG, "WPS: Received NFC tag payload", wps);
+
+ if (wps_parse_msg(wps, &attr)) {
+ wpa_printf(MSG_DEBUG, "WPS: Ignore invalid data from NFC tag");
+ return -1;
+ }
+
+ if (attr.num_cred)
+ return wpas_wps_use_cred(wpa_s, &attr);
+
+#ifdef CONFIG_WPS_ER
+ if (attr.oob_dev_password)
+ return wpas_wps_add_nfc_password_token(wpa_s, &attr);
+#endif /* CONFIG_WPS_ER */
+
+ wpa_printf(MSG_DEBUG, "WPS: Ignore unrecognized NFC tag");
+ return -1;
+}
+
+
+int wpas_wps_nfc_tag_read(struct wpa_supplicant *wpa_s,
+ const struct wpabuf *data)
+{
+ const struct wpabuf *wps = data;
+ struct wpabuf *tmp = NULL;
+ int ret;
+
+ if (wpabuf_len(data) < 4)
+ return -1;
+
+ if (*wpabuf_head_u8(data) != 0x10) {
+ /* Assume this contains full NDEF record */
+ tmp = ndef_parse_wifi(data);
+ if (tmp == NULL) {
+ wpa_printf(MSG_DEBUG, "WPS: Could not parse NDEF");
+ return -1;
+ }
+ wps = tmp;
+ }
+
+ ret = wpas_wps_nfc_tag_process(wpa_s, wps);
+ wpabuf_free(tmp);
+ return ret;
+}
+
+
+struct wpabuf * wpas_wps_nfc_handover_req(struct wpa_supplicant *wpa_s)
+{
+ return ndef_build_wifi_hr();
+}
+
+
+struct wpabuf * wpas_wps_nfc_handover_sel(struct wpa_supplicant *wpa_s)
+{
+ return NULL;
+}
+
+
+int wpas_wps_nfc_rx_handover_req(struct wpa_supplicant *wpa_s,
+ const struct wpabuf *data)
+{
+ /* TODO */
+ return -1;
+}
+
+
+int wpas_wps_nfc_rx_handover_sel(struct wpa_supplicant *wpa_s,
+ const struct wpabuf *data)
+{
+ struct wpabuf *wps;
+ int ret;
+
+ wps = ndef_parse_wifi(data);
+ if (wps == NULL)
+ return -1;
+ wpa_printf(MSG_DEBUG, "WPS: Received application/vnd.wfa.wsc "
+ "payload from NFC connection handover");
+ wpa_hexdump_buf_key(MSG_DEBUG, "WPS: NFC payload", wps);
+ ret = wpas_wps_nfc_tag_process(wpa_s, wps);
+ wpabuf_free(wps);
+
+ return ret;
+}
+
+#endif /* CONFIG_WPS_NFC */
+
+
+extern int wpa_debug_level;
+
+static void wpas_wps_dump_ap_info(struct wpa_supplicant *wpa_s)
+{
+ size_t i;
+ struct os_time now;
+
+ if (wpa_debug_level > MSG_DEBUG)
+ return;
+
+ if (wpa_s->wps_ap == NULL)
+ return;
+
+ os_get_time(&now);
+
+ for (i = 0; i < wpa_s->num_wps_ap; i++) {
+ struct wps_ap_info *ap = &wpa_s->wps_ap[i];
+ struct wpa_blacklist *e = wpa_blacklist_get(wpa_s, ap->bssid);
+
+ wpa_printf(MSG_DEBUG, "WPS: AP[%d] " MACSTR " type=%d "
+ "tries=%d last_attempt=%d sec ago blacklist=%d",
+ (int) i, MAC2STR(ap->bssid), ap->type, ap->tries,
+ ap->last_attempt.sec > 0 ?
+ (int) now.sec - (int) ap->last_attempt.sec : -1,
+ e ? e->count : 0);
+ }
+}
+
+
+static struct wps_ap_info * wpas_wps_get_ap_info(struct wpa_supplicant *wpa_s,
+ const u8 *bssid)
+{
+ size_t i;
+
+ if (wpa_s->wps_ap == NULL)
+ return NULL;
+
+ for (i = 0; i < wpa_s->num_wps_ap; i++) {
+ struct wps_ap_info *ap = &wpa_s->wps_ap[i];
+ if (os_memcmp(ap->bssid, bssid, ETH_ALEN) == 0)
+ return ap;
+ }
+
+ return NULL;
+}
+
+
+static void wpas_wps_update_ap_info_bss(struct wpa_supplicant *wpa_s,
+ struct wpa_scan_res *res)
+{
+ struct wpabuf *wps;
+ enum wps_ap_info_type type;
+ struct wps_ap_info *ap;
+ int r;
+
+ if (wpa_scan_get_vendor_ie(res, WPS_IE_VENDOR_TYPE) == NULL)
+ return;
+
+ wps = wpa_scan_get_vendor_ie_multi(res, WPS_IE_VENDOR_TYPE);
+ if (wps == NULL)
+ return;
+
+ r = wps_is_addr_authorized(wps, wpa_s->own_addr, 1);
+ if (r == 2)
+ type = WPS_AP_SEL_REG_OUR;
+ else if (r == 1)
+ type = WPS_AP_SEL_REG;
+ else
+ type = WPS_AP_NOT_SEL_REG;
+
+ wpabuf_free(wps);
+
+ ap = wpas_wps_get_ap_info(wpa_s, res->bssid);
+ if (ap) {
+ if (ap->type != type) {
+ wpa_printf(MSG_DEBUG, "WPS: AP " MACSTR
+ " changed type %d -> %d",
+ MAC2STR(res->bssid), ap->type, type);
+ ap->type = type;
+ if (type != WPS_AP_NOT_SEL_REG)
+ wpa_blacklist_del(wpa_s, ap->bssid);
+ }
+ return;
+ }
+
+ ap = os_realloc_array(wpa_s->wps_ap, wpa_s->num_wps_ap + 1,
+ sizeof(struct wps_ap_info));
+ if (ap == NULL)
+ return;
+
+ wpa_s->wps_ap = ap;
+ ap = &wpa_s->wps_ap[wpa_s->num_wps_ap];
+ wpa_s->num_wps_ap++;
+
+ os_memset(ap, 0, sizeof(*ap));
+ os_memcpy(ap->bssid, res->bssid, ETH_ALEN);
+ ap->type = type;
+ wpa_printf(MSG_DEBUG, "WPS: AP " MACSTR " type %d added",
+ MAC2STR(ap->bssid), ap->type);
+}
+
+
+void wpas_wps_update_ap_info(struct wpa_supplicant *wpa_s,
+ struct wpa_scan_results *scan_res)
+{
+ size_t i;
+
+ for (i = 0; i < scan_res->num; i++)
+ wpas_wps_update_ap_info_bss(wpa_s, scan_res->res[i]);
+
+ wpas_wps_dump_ap_info(wpa_s);
+}
+
+
+void wpas_wps_notify_assoc(struct wpa_supplicant *wpa_s, const u8 *bssid)
+{
+ struct wps_ap_info *ap;
+ if (!wpa_s->wps_ap_iter)
+ return;
+ ap = wpas_wps_get_ap_info(wpa_s, bssid);
+ if (ap == NULL)
+ return;
+ ap->tries++;
+ os_get_time(&ap->last_attempt);
+}
diff --git a/contrib/wpa/wpa_supplicant/wps_supplicant.h b/contrib/wpa/wpa_supplicant/wps_supplicant.h
index ba2fb16..dd0dc60 100644
--- a/contrib/wpa/wpa_supplicant/wps_supplicant.h
+++ b/contrib/wpa/wpa_supplicant/wps_supplicant.h
@@ -1,21 +1,15 @@
/*
* wpa_supplicant / WPS integration
- * Copyright (c) 2008-2009, Jouni Malinen <j@w1.fi>
+ * Copyright (c) 2008-2012, Jouni Malinen <j@w1.fi>
*
- * This program is free software; you can redistribute it and/or modify
- * it under the terms of the GNU General Public License version 2 as
- * published by the Free Software Foundation.
- *
- * Alternatively, this software may be distributed under the terms of BSD
- * license.
- *
- * See README and COPYING for more details.
+ * This software may be distributed under the terms of the BSD license.
+ * See README for more details.
*/
#ifndef WPS_SUPPLICANT_H
#define WPS_SUPPLICANT_H
-struct wpa_scan_res;
+struct wpa_scan_results;
#ifdef CONFIG_WPS
@@ -35,31 +29,52 @@ int wpas_wps_init(struct wpa_supplicant *wpa_s);
void wpas_wps_deinit(struct wpa_supplicant *wpa_s);
int wpas_wps_eapol_cb(struct wpa_supplicant *wpa_s);
enum wps_request_type wpas_wps_get_req_type(struct wpa_ssid *ssid);
-int wpas_wps_start_pbc(struct wpa_supplicant *wpa_s, const u8 *bssid);
+int wpas_wps_start_pbc(struct wpa_supplicant *wpa_s, const u8 *bssid,
+ int p2p_group);
int wpas_wps_start_pin(struct wpa_supplicant *wpa_s, const u8 *bssid,
- const char *pin);
-int wpas_wps_start_oob(struct wpa_supplicant *wpa_s, char *device_type,
- char *path, char *method, char *name);
+ const char *pin, int p2p_group, u16 dev_pw_id);
+int wpas_wps_cancel(struct wpa_supplicant *wpa_s);
int wpas_wps_start_reg(struct wpa_supplicant *wpa_s, const u8 *bssid,
const char *pin, struct wps_new_ap_settings *settings);
int wpas_wps_ssid_bss_match(struct wpa_supplicant *wpa_s,
- struct wpa_ssid *ssid, struct wpa_scan_res *bss);
+ struct wpa_ssid *ssid, struct wpa_bss *bss);
int wpas_wps_ssid_wildcard_ok(struct wpa_supplicant *wpa_s,
- struct wpa_ssid *ssid, struct wpa_scan_res *bss);
+ struct wpa_ssid *ssid, struct wpa_bss *bss);
int wpas_wps_scan_pbc_overlap(struct wpa_supplicant *wpa_s,
struct wpa_bss *selected, struct wpa_ssid *ssid);
void wpas_wps_notify_scan_results(struct wpa_supplicant *wpa_s);
int wpas_wps_searching(struct wpa_supplicant *wpa_s);
int wpas_wps_scan_result_text(const u8 *ies, size_t ies_len, char *pos,
char *end);
-int wpas_wps_er_start(struct wpa_supplicant *wpa_s);
+int wpas_wps_er_start(struct wpa_supplicant *wpa_s, const char *filter);
int wpas_wps_er_stop(struct wpa_supplicant *wpa_s);
-int wpas_wps_er_add_pin(struct wpa_supplicant *wpa_s, const char *uuid,
- const char *pin);
+int wpas_wps_er_add_pin(struct wpa_supplicant *wpa_s, const u8 *addr,
+ const char *uuid, const char *pin);
int wpas_wps_er_pbc(struct wpa_supplicant *wpa_s, const char *uuid);
int wpas_wps_er_learn(struct wpa_supplicant *wpa_s, const char *uuid,
const char *pin);
+int wpas_wps_er_set_config(struct wpa_supplicant *wpa_s, const char *uuid,
+ int id);
+int wpas_wps_er_config(struct wpa_supplicant *wpa_s, const char *uuid,
+ const char *pin, struct wps_new_ap_settings *settings);
+struct wpabuf * wpas_wps_er_nfc_config_token(struct wpa_supplicant *wpa_s,
+ int ndef, const char *uuid);
int wpas_wps_terminate_pending(struct wpa_supplicant *wpa_s);
+int wpas_wps_in_progress(struct wpa_supplicant *wpa_s);
+void wpas_wps_update_config(struct wpa_supplicant *wpa_s);
+struct wpabuf * wpas_wps_nfc_token(struct wpa_supplicant *wpa_s, int ndef);
+int wpas_wps_start_nfc(struct wpa_supplicant *wpa_s, const u8 *bssid);
+int wpas_wps_nfc_tag_read(struct wpa_supplicant *wpa_s,
+ const struct wpabuf *data);
+struct wpabuf * wpas_wps_nfc_handover_req(struct wpa_supplicant *wpa_s);
+struct wpabuf * wpas_wps_nfc_handover_sel(struct wpa_supplicant *wpa_s);
+int wpas_wps_nfc_rx_handover_req(struct wpa_supplicant *wpa_s,
+ const struct wpabuf *data);
+int wpas_wps_nfc_rx_handover_sel(struct wpa_supplicant *wpa_s,
+ const struct wpabuf *data);
+void wpas_wps_update_ap_info(struct wpa_supplicant *wpa_s,
+ struct wpa_scan_results *scan_res);
+void wpas_wps_notify_assoc(struct wpa_supplicant *wpa_s, const u8 *bssid);
#else /* CONFIG_WPS */
@@ -84,14 +99,14 @@ static inline u8 wpas_wps_get_req_type(struct wpa_ssid *ssid)
static inline int wpas_wps_ssid_bss_match(struct wpa_supplicant *wpa_s,
struct wpa_ssid *ssid,
- struct wpa_scan_res *bss)
+ struct wpa_bss *bss)
{
return -1;
}
static inline int wpas_wps_ssid_wildcard_ok(struct wpa_supplicant *wpa_s,
struct wpa_ssid *ssid,
- struct wpa_scan_res *bss)
+ struct wpa_bss *bss)
{
return 0;
}
@@ -112,6 +127,16 @@ static inline int wpas_wps_searching(struct wpa_supplicant *wpa_s)
return 0;
}
+static inline void wpas_wps_update_ap_info(struct wpa_supplicant *wpa_s,
+ struct wpa_scan_results *scan_res)
+{
+}
+
+static inline void wpas_wps_notify_assoc(struct wpa_supplicant *wpa_s,
+ const u8 *bssid)
+{
+}
+
#endif /* CONFIG_WPS */
#endif /* WPS_SUPPLICANT_H */
diff --git a/crypto/heimdal/lib/gssapi/gssapi/gssapi_krb5.h b/crypto/heimdal/lib/gssapi/gssapi/gssapi_krb5.h
index 2f605f5..e7bb4c8 100644
--- a/crypto/heimdal/lib/gssapi/gssapi/gssapi_krb5.h
+++ b/crypto/heimdal/lib/gssapi/gssapi/gssapi_krb5.h
@@ -36,7 +36,7 @@
#ifndef GSSAPI_KRB5_H_
#define GSSAPI_KRB5_H_
-#include <gssapi.h>
+#include <gssapi/gssapi.h>
GSSAPI_CPP_START
diff --git a/crypto/openssh/sshd_config b/crypto/openssh/sshd_config
index cfd2bda..6e30637 100644
--- a/crypto/openssh/sshd_config
+++ b/crypto/openssh/sshd_config
@@ -105,7 +105,7 @@
#PrintLastLog yes
#TCPKeepAlive yes
#UseLogin no
-#UsePrivilegeSeparation sandbox
+#UsePrivilegeSeparation yes
#PermitUserEnvironment no
#Compression delayed
#ClientAliveInterval 0
diff --git a/crypto/openssh/sshd_config.5 b/crypto/openssh/sshd_config.5
index 3f34bed..5e3e289 100644
--- a/crypto/openssh/sshd_config.5
+++ b/crypto/openssh/sshd_config.5
@@ -1169,7 +1169,7 @@ the privilege of the authenticated user.
The goal of privilege separation is to prevent privilege
escalation by containing any corruption within the unprivileged processes.
The default is
-.Dq sandbox .
+.Dq yes .
If
.Cm UsePrivilegeSeparation
is set to
diff --git a/etc/defaults/rc.conf b/etc/defaults/rc.conf
index dda1855..47d5145 100644
--- a/etc/defaults/rc.conf
+++ b/etc/defaults/rc.conf
@@ -88,7 +88,7 @@ fsck_y_enable="NO" # Set to YES to do fsck -y if the initial preen fails.
fsck_y_flags="" # Additional flags for fsck -y
background_fsck="YES" # Attempt to run fsck in the background where possible.
background_fsck_delay="60" # Time to wait (seconds) before starting the fsck.
-netfs_types="nfs:NFS oldnfs:OLDNFS" # Net filesystems.
+netfs_types="nfs:NFS oldnfs:OLDNFS smbfs:SMB" # Net filesystems.
extra_netfs_types="NO" # List of network extra filesystem types for delayed
# mount at startup (or NO).
diff --git a/etc/mtree/BSD.include.dist b/etc/mtree/BSD.include.dist
index 02e831d..e295348 100644
--- a/etc/mtree/BSD.include.dist
+++ b/etc/mtree/BSD.include.dist
@@ -176,6 +176,8 @@
..
procfs
..
+ smbfs
+ ..
udf
..
unionfs
diff --git a/etc/mtree/BSD.usr.dist b/etc/mtree/BSD.usr.dist
index 02d2286..98e89b9 100644
--- a/etc/mtree/BSD.usr.dist
+++ b/etc/mtree/BSD.usr.dist
@@ -309,6 +309,10 @@
srcs
..
..
+ smbfs
+ print
+ ..
+ ..
sunrpc
dir
..
diff --git a/etc/network.subr b/etc/network.subr
index 63c3db3..c6b8585 100644
--- a/etc/network.subr
+++ b/etc/network.subr
@@ -164,6 +164,9 @@ ifconfig_up()
fi
fi
+ ifalias $1 link alias
+ ifalias $1 ether alias
+
if [ ${_cfg} -eq 0 ]; then
${IFCONFIG_CMD} $1 up
fi
@@ -318,6 +321,15 @@ dhcpif()
local _tmpargs _arg
_tmpargs=`_ifconfig_getargs $1`
+ case $1 in
+ lo[0-9]*|\
+ stf[0-9]*|\
+ faith[0-9]*|\
+ lp[0-9]*|\
+ sl[0-9]*)
+ return 1
+ ;;
+ esac
if noafif $1; then
return 1
fi
@@ -423,6 +435,9 @@ afexists()
return 1
fi
;;
+ link|ether)
+ return 0
+ ;;
*)
err 1 "afexists(): Unsupported address family: $_af"
;;
@@ -509,7 +524,7 @@ ipv6_autoconfif()
_if=$1
case $_if in
- lo0|\
+ lo[0-9]*|\
stf[0-9]*|\
faith[0-9]*|\
lp[0-9]*|\
@@ -691,7 +706,7 @@ ifalias()
afexists $2 || return $_ret
case "$2" in
- inet|inet6)
+ inet|inet6|link|ether)
ifalias_af_common $1 $2 $3 && _ret=0
;;
esac
@@ -898,6 +913,11 @@ ifalias_af_common_handler()
*) return ;;
esac
+ # link(ether) does not support address removal.
+ case $_af:$_action in
+ link:-alias|ether:-alias) return ;;
+ esac
+
_tmpargs=
for _c in $_args; do
case $_c in
@@ -956,6 +976,8 @@ ifalias_af_common()
inet\ *) _iaf=inet ;;
inet6\ *) _iaf=inet6 ;;
ipx\ *) _iaf=ipx ;;
+ link\ *) _iaf=link ;;
+ ether\ *) _iaf=ether ;;
esac
case ${_af}:${_action}:${_iaf}:"${ifconfig_args}" in
@@ -1003,7 +1025,7 @@ ifalias_af_common()
_tmpargs=
for _c in `get_if_var $_if ifconfig_IF_aliases` $_aliasn; do
case $_c in
- inet|inet6|ipx)
+ inet|inet6|ipx|link|ether)
case $_tmpargs in
${_af}\ *)
eval ifalias_af_common_handler $_if $_af $_action $_tmpargs && _ret=0
diff --git a/etc/newsyslog.conf b/etc/newsyslog.conf
index 76e0707..9572f84 100644
--- a/etc/newsyslog.conf
+++ b/etc/newsyslog.conf
@@ -32,6 +32,7 @@
/var/log/monthly.log 640 12 * $M1D0 JN
/var/log/pflog 600 3 100 * JB /var/run/pflogd.pid
/var/log/ppp.log root:network 640 3 100 * JC
+/var/log/devd.log 644 3 100 * JC
/var/log/security 600 10 100 * JC
/var/log/sendmail.st 640 10 * 168 BN
/var/log/utx.log 644 3 * @01T05 B
diff --git a/etc/rc.resume b/etc/rc.resume
index 4d17190..cce6161 100755
--- a/etc/rc.resume
+++ b/etc/rc.resume
@@ -52,10 +52,6 @@ fi
# suspend and reloading it on resume. Example:
# kldload usb
-# wpa_supplicant(8) doesn't seem to reassociate during resume. Uncomment
-# the following to signal it to reassociate.
-# /usr/sbin/wpa_cli reassociate
-
/usr/bin/logger -t $subsystem resumed at `/bin/date +'%Y%m%d %H:%M:%S'`
/bin/sync && /bin/sync && /bin/sync
diff --git a/etc/syslog.conf b/etc/syslog.conf
index 242d27a..702bd66 100644
--- a/etc/syslog.conf
+++ b/etc/syslog.conf
@@ -13,6 +13,7 @@ mail.info /var/log/maillog
lpr.info /var/log/lpd-errs
ftp.info /var/log/xferlog
cron.* /var/log/cron
+!-devd
*.=debug /var/log/debug.log
*.emerg *
# uncomment this to log all writes to /dev/console to /var/log/console.log
@@ -27,6 +28,9 @@ cron.* /var/log/cron
# news.crit /var/log/news/news.crit
# news.err /var/log/news/news.err
# news.notice /var/log/news/news.notice
+# Uncomment this if you wish to see messages produced by devd
+# !devd
+# *.>=info /var/log/devd.log
!ppp
*.* /var/log/ppp.log
!*
diff --git a/gnu/usr.bin/patch/pch.c b/gnu/usr.bin/patch/pch.c
index dba4582..37bbe56 100644
--- a/gnu/usr.bin/patch/pch.c
+++ b/gnu/usr.bin/patch/pch.c
@@ -83,12 +83,17 @@ re_patch(void)
void
open_patch_file(char *filename)
{
+ int nr, nw;
+
if (filename == Nullch || !*filename || strEQ(filename, "-")) {
pfp = fopen(TMPPATNAME, "w");
if (pfp == Nullfp)
pfatal2("can't create %s", TMPPATNAME);
- while (fgets(buf, buf_size, stdin) != Nullch)
- fputs(buf, pfp);
+ while ((nr = fread(buf, 1, buf_size, stdin)) > 0) {
+ nw = fwrite(buf, 1, nr, pfp);
+ if (nr != nw)
+ pfatal2("write error to %s", TMPPATNAME);
+ }
Fclose(pfp);
filename = TMPPATNAME;
}
@@ -1176,7 +1181,7 @@ pgets(bool do_indent)
indent++;
}
}
- Strncpy(buf, line, len - skipped);
+ memcpy(buf, line, len - skipped);
buf[len - skipped] = '\0';
}
return len;
diff --git a/include/Makefile b/include/Makefile
index 4ccb759..ae5ed7c 100644
--- a/include/Makefile
+++ b/include/Makefile
@@ -37,7 +37,7 @@ LHDRS= aio.h errno.h fcntl.h linker_set.h poll.h stdatomic.h stdint.h \
syslog.h ucontext.h
LDIRS= bsm cam geom net net80211 netatalk netgraph netinet netinet6 \
- netipsec ${_netipx} netnatm \
+ netipsec ${_netipx} netnatm netsmb \
nfs nfsclient nfsserver \
sys vm
@@ -48,7 +48,7 @@ LSUBDIRS= cam/ata cam/scsi \
dev/ofw dev/pbio dev/pci ${_dev_powermac_nvram} dev/ppbus dev/smbus \
dev/speaker dev/usb dev/utopia dev/vkbd dev/wi \
fs/devfs fs/fdescfs fs/msdosfs fs/nandfs fs/nfs fs/nullfs \
- fs/procfs fs/udf fs/unionfs \
+ fs/procfs fs/smbfs fs/udf fs/unionfs \
geom/cache geom/concat geom/eli geom/gate geom/journal geom/label \
geom/mirror geom/mountver geom/multipath geom/nop \
geom/raid geom/raid3 geom/shsec geom/stripe geom/virstor \
@@ -80,6 +80,7 @@ INCS+= iconv.h
LSUBSUBDIRS+= netgraph/bluetooth/include
.endif
+# XXX unconditionally needed by <netsmb/netbios.h>
#.if ${MK_IPX} != "no"
_netipx= netipx
#.endif
diff --git a/include/gssapi/gssapi.h b/include/gssapi/gssapi.h
index 032e784..bd2722c 100644
--- a/include/gssapi/gssapi.h
+++ b/include/gssapi/gssapi.h
@@ -44,6 +44,33 @@ typedef __ssize_t ssize_t;
#define _SSIZE_T_DECLARED
#endif
+/* Compatibility with Heimdal 1.5.1 */
+#ifndef GSSAPI_CPP_START
+#ifdef __cplusplus
+#define GSSAPI_CPP_START extern "C" {
+#define GSSAPI_CPP_END }
+#else
+#define GSSAPI_CPP_START
+#define GSSAPI_CPP_END
+#endif
+#endif
+
+/* Compatibility with Heimdal 1.5.1 */
+#ifndef BUILD_GSSAPI_LIB
+#define GSSAPI_LIB_FUNCTION
+#define GSSAPI_LIB_CALL
+#define GSSAPI_LIB_VARIABLE
+#endif
+
+/* Compatibility with Heimdal 1.5.1 */
+#ifndef GSSAPI_DEPRECATED_FUNCTION
+#if defined(__GNUC__) && ((__GNUC__ > 3) || ((__GNUC__ == 3) && (__GNUC_MINOR__ >= 1 )))
+#define GSSAPI_DEPRECATED_FUNCTION(X) __attribute__((deprecated))
+#else
+#define GSSAPI_DEPRECATED_FUNCTION(X)
+#endif
+#endif
+
#if 0
/*
* If the platform supports the xom.h header file, it should be
diff --git a/include/iconv.h b/include/iconv.h
index eecb2fd..7b628af 100644
--- a/include/iconv.h
+++ b/include/iconv.h
@@ -43,7 +43,7 @@
#define iconv_open libiconv_open
#define iconv_close libiconv_close
-#define iconv(cd, in, insize, out, outsize) libiconv(cd, __DECONST(char **, in), insize, out, outsize)
+#define iconv libiconv
#define iconv_t libiconv_t
struct __tag_iconv_t;
@@ -51,7 +51,7 @@ typedef struct __tag_iconv_t *iconv_t;
__BEGIN_DECLS
iconv_t libiconv_open(const char *, const char *);
-size_t libiconv(iconv_t, char ** __restrict,
+size_t libiconv(iconv_t, const char ** __restrict,
size_t * __restrict, char ** __restrict,
size_t * __restrict);
int libiconv_close(iconv_t);
@@ -60,7 +60,7 @@ int libiconv_close(iconv_t);
*/
int __iconv_get_list(char ***, size_t *, bool);
void __iconv_free_list(char **, size_t);
-size_t __iconv(iconv_t, char **, size_t *, char **,
+size_t __iconv(iconv_t, const char **, size_t *, char **,
size_t *, __uint32_t, size_t *);
#define __ICONV_F_HIDE_INVALID 0x0001
diff --git a/include/stdlib.h b/include/stdlib.h
index b73a54f..cc92273 100644
--- a/include/stdlib.h
+++ b/include/stdlib.h
@@ -69,7 +69,7 @@ typedef struct {
#define EXIT_FAILURE 1
#define EXIT_SUCCESS 0
-#define RAND_MAX 0x7fffffff
+#define RAND_MAX 0x7ffffffd
__BEGIN_DECLS
#ifdef _XLOCALE_H_
diff --git a/lib/Makefile b/lib/Makefile
index d28d55b..71fe4d2 100644
--- a/lib/Makefile
+++ b/lib/Makefile
@@ -102,6 +102,7 @@ SUBDIR= ${SUBDIR_ORDERED} \
${_librtld_db} \
${_libsdp} \
${_libsm} \
+ ${_libsmb} \
${_libsmdb} \
${_libsmutil} \
libstand \
@@ -205,6 +206,7 @@ _libypclnt= libypclnt
.endif
.if ${MACHINE_CPUARCH} == "i386" || ${MACHINE_CPUARCH} == "amd64"
+_libsmb= libsmb
_libvgl= libvgl
_libproc= libproc
_librtld_db= librtld_db
@@ -216,6 +218,7 @@ _libvmmapi= libvmmapi
.if ${MACHINE_CPUARCH} == "ia64"
_libefi= libefi
+_libsmb= libsmb
.endif
.if ${MACHINE_CPUARCH} == "mips"
@@ -226,6 +229,11 @@ _librtld_db= librtld_db
.if ${MACHINE_CPUARCH} == "powerpc"
_libproc= libproc
_librtld_db= librtld_db
+_libsmb= libsmb
+.endif
+
+.if ${MACHINE_CPUARCH} == "sparc64"
+_libsmb= libsmb
.endif
.if ${MK_OPENSSL} != "no"
diff --git a/lib/libc/gen/siginterrupt.c b/lib/libc/gen/siginterrupt.c
index dde474c..fde33ca 100644
--- a/lib/libc/gen/siginterrupt.c
+++ b/lib/libc/gen/siginterrupt.c
@@ -46,7 +46,7 @@ int
siginterrupt(sig, flag)
int sig, flag;
{
- extern sigset_t _sigintr;
+ extern sigset_t _sigintr __hidden;
struct sigaction sa;
int ret;
diff --git a/lib/libc/gen/signal.c b/lib/libc/gen/signal.c
index c93633e..ee96dcc 100644
--- a/lib/libc/gen/signal.c
+++ b/lib/libc/gen/signal.c
@@ -41,7 +41,7 @@ __FBSDID("$FreeBSD$");
#include "un-namespace.h"
#include "libc_private.h"
-sigset_t _sigintr; /* shared with siginterrupt */
+sigset_t _sigintr __hidden; /* shared with siginterrupt */
sig_t
signal(s, a)
diff --git a/lib/libc/iconv/Symbol.map b/lib/libc/iconv/Symbol.map
index 73e0e22..82be46f 100644
--- a/lib/libc/iconv/Symbol.map
+++ b/lib/libc/iconv/Symbol.map
@@ -17,6 +17,16 @@ FBSD_1.2 {
libiconvlist;
};
+FBSD_1.3 {
+ iconv;
+ iconv_open;
+ iconv_close;
+ iconv_open_into;
+ iconv_set_relocation_prefix;
+ iconvctl;
+ iconvlist;
+};
+
FBSDprivate_1.0 {
_citrus_bcs_convert_to_lower;
_citrus_bcs_convert_to_upper;
diff --git a/lib/libc/iconv/citrus_csmapper.c b/lib/libc/iconv/citrus_csmapper.c
index 5e0a01a..e2d10d5 100644
--- a/lib/libc/iconv/citrus_csmapper.c
+++ b/lib/libc/iconv/citrus_csmapper.c
@@ -58,6 +58,8 @@
static struct _citrus_mapper_area *maparea = NULL;
+static pthread_rwlock_t ma_lock = PTHREAD_RWLOCK_INITIALIZER;
+
#define CS_ALIAS _PATH_CSMAPPER "/charset.alias"
#define CS_PIVOT _PATH_CSMAPPER "/charset.pivot"
@@ -314,7 +316,7 @@ get_none(struct _citrus_mapper_area *__restrict ma,
{
int ret;
- WLOCK;
+ WLOCK(&ma_lock);
if (csm_none) {
*rcsm = csm_none;
ret = 0;
@@ -329,7 +331,7 @@ get_none(struct _citrus_mapper_area *__restrict ma,
*rcsm = csm_none;
ret = 0;
quit:
- UNLOCK;
+ UNLOCK(&ma_lock);
return (ret);
}
diff --git a/lib/libc/iconv/citrus_iconv.c b/lib/libc/iconv/citrus_iconv.c
index 04acccf..31cf7df 100644
--- a/lib/libc/iconv/citrus_iconv.c
+++ b/lib/libc/iconv/citrus_iconv.c
@@ -68,11 +68,13 @@ static int shared_max_reuse, shared_num_unused;
static _CITRUS_HASH_HEAD(, _citrus_iconv_shared, CI_HASH_SIZE) shared_pool;
static TAILQ_HEAD(, _citrus_iconv_shared) shared_unused;
+static pthread_rwlock_t ci_lock = PTHREAD_RWLOCK_INITIALIZER;
+
static __inline void
init_cache(void)
{
- WLOCK;
+ WLOCK(&ci_lock);
if (!isinit) {
_CITRUS_HASH_INIT(&shared_pool, CI_HASH_SIZE);
TAILQ_INIT(&shared_unused);
@@ -83,7 +85,7 @@ init_cache(void)
shared_max_reuse = CI_INITIAL_MAX_REUSE;
isinit = true;
}
- UNLOCK;
+ UNLOCK(&ci_lock);
}
static __inline void
@@ -195,7 +197,7 @@ get_shared(struct _citrus_iconv_shared * __restrict * __restrict rci,
snprintf(convname, sizeof(convname), "%s/%s", src, dst);
- WLOCK;
+ WLOCK(&ci_lock);
/* lookup alread existing entry */
hashval = hash_func(convname);
@@ -222,7 +224,7 @@ get_shared(struct _citrus_iconv_shared * __restrict * __restrict rci,
*rci = ci;
quit:
- UNLOCK;
+ UNLOCK(&ci_lock);
return (ret);
}
@@ -231,7 +233,7 @@ static void
release_shared(struct _citrus_iconv_shared * __restrict ci)
{
- WLOCK;
+ WLOCK(&ci_lock);
ci->ci_used_count--;
if (ci->ci_used_count == 0) {
/* put it into unused list */
@@ -247,7 +249,7 @@ release_shared(struct _citrus_iconv_shared * __restrict ci)
}
}
- UNLOCK;
+ UNLOCK(&ci_lock);
}
/*
diff --git a/lib/libc/iconv/citrus_iconv.h b/lib/libc/iconv/citrus_iconv.h
index 99604e9..ac14ac3 100644
--- a/lib/libc/iconv/citrus_iconv.h
+++ b/lib/libc/iconv/citrus_iconv.h
@@ -52,7 +52,7 @@ __END_DECLS
*/
static __inline int
_citrus_iconv_convert(struct _citrus_iconv * __restrict cv,
- char * __restrict * __restrict in, size_t * __restrict inbytes,
+ const char * __restrict * __restrict in, size_t * __restrict inbytes,
char * __restrict * __restrict out, size_t * __restrict outbytes,
uint32_t flags, size_t * __restrict nresults)
{
diff --git a/lib/libc/iconv/citrus_iconv_local.h b/lib/libc/iconv/citrus_iconv_local.h
index 081a708..52ac825 100644
--- a/lib/libc/iconv/citrus_iconv_local.h
+++ b/lib/libc/iconv/citrus_iconv_local.h
@@ -45,7 +45,7 @@ static void _citrus_##_m_##_iconv_uninit_shared \
(struct _citrus_iconv_shared *); \
static int _citrus_##_m_##_iconv_convert \
(struct _citrus_iconv * __restrict, \
- char * __restrict * __restrict, \
+ const char * __restrict * __restrict, \
size_t * __restrict, \
char * __restrict * __restrict, \
size_t * __restrict outbytes, \
@@ -74,7 +74,7 @@ typedef void (*_citrus_iconv_uninit_shared_t)
(struct _citrus_iconv_shared *);
typedef int (*_citrus_iconv_convert_t)
(struct _citrus_iconv * __restrict,
- char *__restrict* __restrict, size_t * __restrict,
+ const char *__restrict* __restrict, size_t * __restrict,
char * __restrict * __restrict, size_t * __restrict, uint32_t,
size_t * __restrict);
typedef int (*_citrus_iconv_init_context_t)(struct _citrus_iconv *);
diff --git a/lib/libc/iconv/citrus_lock.h b/lib/libc/iconv/citrus_lock.h
index ad9443a..3950644 100644
--- a/lib/libc/iconv/citrus_lock.h
+++ b/lib/libc/iconv/citrus_lock.h
@@ -27,9 +27,7 @@
#include <pthread.h>
-static pthread_rwlock_t lock;
-
-#define WLOCK if (__isthreaded) \
- pthread_rwlock_wrlock(&lock);
-#define UNLOCK if (__isthreaded) \
- pthread_rwlock_unlock(&lock);
+#define WLOCK(lock) if (__isthreaded) \
+ pthread_rwlock_wrlock(lock);
+#define UNLOCK(lock) if (__isthreaded) \
+ pthread_rwlock_unlock(lock);
diff --git a/lib/libc/iconv/citrus_mapper.c b/lib/libc/iconv/citrus_mapper.c
index b5ae96d..b1ee8fb 100644
--- a/lib/libc/iconv/citrus_mapper.c
+++ b/lib/libc/iconv/citrus_mapper.c
@@ -55,6 +55,8 @@
#define CM_HASH_SIZE 101
#define REFCOUNT_PERSISTENT -1
+static pthread_rwlock_t cm_lock = PTHREAD_RWLOCK_INITIALIZER;
+
struct _citrus_mapper_area {
_CITRUS_HASH_HEAD(, _citrus_mapper, CM_HASH_SIZE) ma_cache;
char *ma_dir;
@@ -75,7 +77,7 @@ _citrus_mapper_create_area(
char path[PATH_MAX];
int ret;
- WLOCK;
+ WLOCK(&cm_lock);
if (*rma != NULL) {
ret = 0;
@@ -96,7 +98,7 @@ _citrus_mapper_create_area(
ma->ma_dir = strdup(area);
if (ma->ma_dir == NULL) {
ret = errno;
- free(ma->ma_dir);
+ free(ma);
goto quit;
}
_CITRUS_HASH_INIT(&ma->ma_cache, CM_HASH_SIZE);
@@ -104,7 +106,7 @@ _citrus_mapper_create_area(
*rma = ma;
ret = 0;
quit:
- UNLOCK;
+ UNLOCK(&cm_lock);
return (ret);
}
@@ -316,7 +318,7 @@ _citrus_mapper_open(struct _citrus_mapper_area *__restrict ma,
variable = NULL;
- WLOCK;
+ WLOCK(&cm_lock);
/* search in the cache */
hashval = hash_func(mapname);
@@ -337,9 +339,9 @@ _citrus_mapper_open(struct _citrus_mapper_area *__restrict ma,
goto quit;
/* open mapper */
- UNLOCK;
+ UNLOCK(&cm_lock);
ret = mapper_open(ma, &cm, module, variable);
- WLOCK;
+ WLOCK(&cm_lock);
if (ret)
goto quit;
cm->cm_key = strdup(mapname);
@@ -356,7 +358,7 @@ _citrus_mapper_open(struct _citrus_mapper_area *__restrict ma,
*rcm = cm;
ret = 0;
quit:
- UNLOCK;
+ UNLOCK(&cm_lock);
return (ret);
}
@@ -370,7 +372,7 @@ _citrus_mapper_close(struct _citrus_mapper *cm)
{
if (cm) {
- WLOCK;
+ WLOCK(&cm_lock);
if (cm->cm_refcount == REFCOUNT_PERSISTENT)
goto quit;
if (cm->cm_refcount > 0) {
@@ -381,7 +383,7 @@ _citrus_mapper_close(struct _citrus_mapper *cm)
}
mapper_close(cm);
quit:
- UNLOCK;
+ UNLOCK(&cm_lock);
}
}
@@ -393,7 +395,7 @@ void
_citrus_mapper_set_persistent(struct _citrus_mapper * __restrict cm)
{
- WLOCK;
+ WLOCK(&cm_lock);
cm->cm_refcount = REFCOUNT_PERSISTENT;
- UNLOCK;
+ UNLOCK(&cm_lock);
}
diff --git a/lib/libc/iconv/citrus_none.c b/lib/libc/iconv/citrus_none.c
index 9ec4bd3..4f9b254 100644
--- a/lib/libc/iconv/citrus_none.c
+++ b/lib/libc/iconv/citrus_none.c
@@ -83,7 +83,7 @@ _citrus_NONE_stdenc_init_state(struct _citrus_stdenc * __restrict ce __unused,
static int
_citrus_NONE_stdenc_mbtocs(struct _citrus_stdenc * __restrict ce __unused,
- _csid_t *csid, _index_t *idx, char **s, size_t n,
+ _csid_t *csid, _index_t *idx, const char **s, size_t n,
void *ps __unused, size_t *nresult, struct iconv_hooks *hooks)
{
@@ -159,7 +159,7 @@ _citrus_NONE_stdenc_cstomb(struct _citrus_stdenc * __restrict ce __unused,
static int
_citrus_NONE_stdenc_mbtowc(struct _citrus_stdenc * __restrict ce __unused,
- _wc_t * __restrict pwc, char ** __restrict s, size_t n,
+ _wc_t * __restrict pwc, const char ** __restrict s, size_t n,
void * __restrict pspriv __unused, size_t * __restrict nresult,
struct iconv_hooks *hooks)
{
diff --git a/lib/libc/iconv/citrus_stdenc.h b/lib/libc/iconv/citrus_stdenc.h
index 50f4dff..28fa29d 100644
--- a/lib/libc/iconv/citrus_stdenc.h
+++ b/lib/libc/iconv/citrus_stdenc.h
@@ -69,7 +69,7 @@ _citrus_stdenc_init_state(struct _citrus_stdenc * __restrict ce,
static __inline int
_citrus_stdenc_mbtocs(struct _citrus_stdenc * __restrict ce,
_citrus_csid_t * __restrict csid, _citrus_index_t * __restrict idx,
- char ** __restrict s, size_t n, void * __restrict ps,
+ const char ** __restrict s, size_t n, void * __restrict ps,
size_t * __restrict nresult, struct iconv_hooks *hooks)
{
diff --git a/lib/libc/iconv/citrus_stdenc_local.h b/lib/libc/iconv/citrus_stdenc_local.h
index 141abff..7b627a0 100644
--- a/lib/libc/iconv/citrus_stdenc_local.h
+++ b/lib/libc/iconv/citrus_stdenc_local.h
@@ -55,7 +55,7 @@ static int _citrus_##_e_##_stdenc_mbtocs \
(struct _citrus_stdenc * __restrict, \
_citrus_csid_t * __restrict, \
_citrus_index_t * __restrict, \
- char ** __restrict, size_t, \
+ const char ** __restrict, size_t, \
void * __restrict, size_t * __restrict, \
struct iconv_hooks *); \
static int _citrus_##_e_##_stdenc_cstomb \
@@ -66,7 +66,7 @@ static int _citrus_##_e_##_stdenc_cstomb \
static int _citrus_##_e_##_stdenc_mbtowc \
(struct _citrus_stdenc * __restrict, \
_citrus_wc_t * __restrict, \
- char ** __restrict, size_t, \
+ const char ** __restrict, size_t, \
void * __restrict, size_t * __restrict, \
struct iconv_hooks *); \
static int _citrus_##_e_##_stdenc_wctomb \
@@ -106,7 +106,7 @@ typedef int (*_citrus_stdenc_init_state_t)
typedef int (*_citrus_stdenc_mbtocs_t)
(struct _citrus_stdenc * __restrict,
_citrus_csid_t * __restrict, _citrus_index_t * __restrict,
- char ** __restrict, size_t,
+ const char ** __restrict, size_t,
void * __restrict, size_t * __restrict,
struct iconv_hooks *);
typedef int (*_citrus_stdenc_cstomb_t)
@@ -116,7 +116,7 @@ typedef int (*_citrus_stdenc_cstomb_t)
typedef int (*_citrus_stdenc_mbtowc_t)
(struct _citrus_stdenc * __restrict,
_citrus_wc_t * __restrict,
- char ** __restrict, size_t,
+ const char ** __restrict, size_t,
void * __restrict, size_t * __restrict,
struct iconv_hooks *);
typedef int (*_citrus_stdenc_wctomb_t)
diff --git a/lib/libc/iconv/citrus_stdenc_template.h b/lib/libc/iconv/citrus_stdenc_template.h
index 9a05fa7..21bc5cc 100644
--- a/lib/libc/iconv/citrus_stdenc_template.h
+++ b/lib/libc/iconv/citrus_stdenc_template.h
@@ -112,7 +112,7 @@ _FUNCNAME(stdenc_init_state)(struct _citrus_stdenc * __restrict ce,
static int
_FUNCNAME(stdenc_mbtocs)(struct _citrus_stdenc * __restrict ce,
_citrus_csid_t * __restrict csid, _citrus_index_t * __restrict idx,
- char ** __restrict s, size_t n, void * __restrict ps,
+ const char ** __restrict s, size_t n, void * __restrict ps,
size_t * __restrict nresult, struct iconv_hooks *hooks)
{
wchar_t wc;
@@ -151,7 +151,7 @@ _FUNCNAME(stdenc_cstomb)(struct _citrus_stdenc * __restrict ce,
static int
_FUNCNAME(stdenc_mbtowc)(struct _citrus_stdenc * __restrict ce,
- _citrus_wc_t * __restrict wc, char ** __restrict s, size_t n,
+ _citrus_wc_t * __restrict wc, const char ** __restrict s, size_t n,
void * __restrict ps, size_t * __restrict nresult,
struct iconv_hooks *hooks)
{
diff --git a/lib/libc/iconv/iconv.c b/lib/libc/iconv/iconv.c
index 99cfc37..9b28ff6 100644
--- a/lib/libc/iconv/iconv.c
+++ b/lib/libc/iconv/iconv.c
@@ -47,16 +47,13 @@
#include "citrus_hash.h"
#include "citrus_iconv.h"
-#ifdef __weak_alias
-__weak_alias(libiconv, _iconv)
-__weak_alias(libiconv_open, _iconv_open)
-__weak_alias(libiconv_open_into, _iconv_open_into)
-__weak_alias(libiconv_close, _iconv_close)
-__weak_alias(libiconvlist, _iconvlist)
-__weak_alias(libiconvctl, _iconvctl)
-__weak_alias(libiconv_set_relocation_prefix, _iconv_set_relocation_prefix)
-__weak_alias(iconv_canonicalize, _iconv_canonicalize)
-#endif
+__weak_reference(libiconv, iconv);
+__weak_reference(libiconv_open, iconv_open);
+__weak_reference(libiconv_open_into, iconv_open_into);
+__weak_reference(libiconv_close, iconv_close);
+__weak_reference(libiconvlist, iconvlist);
+__weak_reference(libiconvctl, iconvctl);
+__weak_reference(libiconv_set_relocation_prefix, iconv_set_relocation_prefix);
#define ISBADF(_h_) (!(_h_) || (_h_) == (iconv_t)-1)
@@ -133,7 +130,7 @@ libiconv_close(iconv_t handle)
}
size_t
-libiconv(iconv_t handle, char **in, size_t *szin, char **out, size_t *szout)
+libiconv(iconv_t handle, const char **in, size_t *szin, char **out, size_t *szout)
{
size_t ret;
int err;
@@ -154,7 +151,7 @@ libiconv(iconv_t handle, char **in, size_t *szin, char **out, size_t *szout)
}
size_t
-__iconv(iconv_t handle, char **in, size_t *szin, char **out,
+__iconv(iconv_t handle, const char **in, size_t *szin, char **out,
size_t *szout, uint32_t flags, size_t *invalids)
{
size_t ret;
diff --git a/lib/libc/locale/cXXrtomb_iconv.h b/lib/libc/locale/cXXrtomb_iconv.h
index d6e7ce0..0ea553b 100644
--- a/lib/libc/locale/cXXrtomb_iconv.h
+++ b/lib/libc/locale/cXXrtomb_iconv.h
@@ -57,7 +57,8 @@ cXXrtomb_l(char * __restrict s, charXX_t c, mbstate_t * __restrict ps,
{
_ConversionState *cs;
struct _citrus_iconv *handle;
- char *src, *dst;
+ const char *src;
+ char *dst;
size_t srcleft, dstleft, invlen;
int err;
diff --git a/lib/libc/locale/mbrtocXX_iconv.h b/lib/libc/locale/mbrtocXX_iconv.h
index 9eb6f68..ac85a5a 100644
--- a/lib/libc/locale/mbrtocXX_iconv.h
+++ b/lib/libc/locale/mbrtocXX_iconv.h
@@ -99,7 +99,8 @@ mbrtocXX_l(charXX_t * __restrict pc, const char * __restrict s, size_t n,
/* Convert as few characters to the dst buffer as possible. */
for (i = 0; ; i++) {
- char *src, *dst;
+ const char *src;
+ char *dst;
size_t srcleft, dstleft, invlen;
int err;
diff --git a/lib/libc/stdlib/rand.c b/lib/libc/stdlib/rand.c
index f9c9b56..4f4aa8d 100644
--- a/lib/libc/stdlib/rand.c
+++ b/lib/libc/stdlib/rand.c
@@ -67,15 +67,15 @@ do_rand(unsigned long *ctx)
*/
long hi, lo, x;
- /* Can't be initialized with 0, so use another value. */
- if (*ctx == 0)
- *ctx = 123459876;
+ /* Must be in [1, 0x7ffffffe] range at this point. */
hi = *ctx / 127773;
lo = *ctx % 127773;
x = 16807 * lo - 2836 * hi;
if (x < 0)
x += 0x7fffffff;
- return ((*ctx = x) % ((u_long)RAND_MAX + 1));
+ *ctx = x;
+ /* Transform to [0, 0x7ffffffd] range. */
+ return (x - 1);
#endif /* !USE_WEAK_SEEDING */
}
@@ -83,15 +83,32 @@ do_rand(unsigned long *ctx)
int
rand_r(unsigned int *ctx)
{
- u_long val = (u_long) *ctx;
- int r = do_rand(&val);
+ u_long val;
+ int r;
- *ctx = (unsigned int) val;
+#ifdef USE_WEAK_SEEDING
+ val = *ctx;
+#else
+ /* Transform to [1, 0x7ffffffe] range. */
+ val = (*ctx % 0x7ffffffe) + 1;
+#endif
+ r = do_rand(&val);
+
+#ifdef USE_WEAK_SEEDING
+ *ctx = (unsigned int)val;
+#else
+ *ctx = (unsigned int)(val - 1);
+#endif
return (r);
}
-static u_long next = 1;
+static u_long next =
+#ifdef USE_WEAK_SEEDING
+ 1;
+#else
+ 2;
+#endif
int
rand()
@@ -104,6 +121,10 @@ srand(seed)
u_int seed;
{
next = seed;
+#ifndef USE_WEAK_SEEDING
+ /* Transform to [1, 0x7ffffffe] range. */
+ next = (next % 0x7ffffffe) + 1;
+#endif
}
@@ -125,6 +146,10 @@ sranddev()
mib[0] = CTL_KERN;
mib[1] = KERN_ARND;
sysctl(mib, 2, (void *)&next, &len, NULL, 0);
+#ifndef USE_WEAK_SEEDING
+ /* Transform to [1, 0x7ffffffe] range. */
+ next = (next % 0x7ffffffe) + 1;
+#endif
}
diff --git a/lib/libdwarf/dwarf_init.c b/lib/libdwarf/dwarf_init.c
index 6ebc7d9..ce85363 100644
--- a/lib/libdwarf/dwarf_init.c
+++ b/lib/libdwarf/dwarf_init.c
@@ -192,7 +192,7 @@ dwarf_read_sleb128(Elf_Data **dp, uint64_t *offsetp)
shift += 7;
} while ((b & 0x80) != 0);
- if (shift < 32 && (b & 0x40) != 0)
+ if (shift < 64 && (b & 0x40) != 0)
ret |= (-1 << shift);
return ret;
diff --git a/lib/libdwarf/dwarf_loc.c b/lib/libdwarf/dwarf_loc.c
index 6449706..12d4cab 100644
--- a/lib/libdwarf/dwarf_loc.c
+++ b/lib/libdwarf/dwarf_loc.c
@@ -46,7 +46,7 @@ dwarf_decode_sleb128(uint8_t **dp)
shift += 7;
} while ((b & 0x80) != 0);
- if (shift < 32 && (b & 0x40) != 0)
+ if (shift < 64 && (b & 0x40) != 0)
ret |= (-1 << shift);
*dp = src;
diff --git a/lib/libfetch/fetch.c b/lib/libfetch/fetch.c
index a081520..8d92bbc 100644
--- a/lib/libfetch/fetch.c
+++ b/lib/libfetch/fetch.c
@@ -376,7 +376,7 @@ fetchParseURL(const char *URL)
/* password */
if (*q == ':')
- q = fetch_pctdecode(u->pwd, ++q, URL_PWDLEN);
+ q = fetch_pctdecode(u->pwd, q + 1, URL_PWDLEN);
p++;
} else {
diff --git a/lib/libiconv_modules/BIG5/citrus_big5.c b/lib/libiconv_modules/BIG5/citrus_big5.c
index b6ece46..1d7d8f8 100644
--- a/lib/libiconv_modules/BIG5/citrus_big5.c
+++ b/lib/libiconv_modules/BIG5/citrus_big5.c
@@ -268,12 +268,12 @@ static int
/*ARGSUSED*/
_citrus_BIG5_mbrtowc_priv(_BIG5EncodingInfo * __restrict ei,
wchar_t * __restrict pwc,
- char ** __restrict s, size_t n,
+ const char ** __restrict s, size_t n,
_BIG5State * __restrict psenc,
size_t * __restrict nresult)
{
wchar_t wchar;
- char *s0;
+ const char *s0;
int c, chlenbak;
s0 = *s;
diff --git a/lib/libiconv_modules/DECHanyu/citrus_dechanyu.c b/lib/libiconv_modules/DECHanyu/citrus_dechanyu.c
index 0801ac9..9d1d394 100644
--- a/lib/libiconv_modules/DECHanyu/citrus_dechanyu.c
+++ b/lib/libiconv_modules/DECHanyu/citrus_dechanyu.c
@@ -163,10 +163,10 @@ is_94charset(int c)
static int
/*ARGSUSED*/
_citrus_DECHanyu_mbrtowc_priv(_DECHanyuEncodingInfo * __restrict ei,
- wchar_t * __restrict pwc, char ** __restrict s, size_t n,
+ wchar_t * __restrict pwc, const char ** __restrict s, size_t n,
_DECHanyuState * __restrict psenc, size_t * __restrict nresult)
{
- char *s0;
+ const char *s0;
wchar_t wc;
int ch;
diff --git a/lib/libiconv_modules/EUC/citrus_euc.c b/lib/libiconv_modules/EUC/citrus_euc.c
index 708473b..c153bef 100644
--- a/lib/libiconv_modules/EUC/citrus_euc.c
+++ b/lib/libiconv_modules/EUC/citrus_euc.c
@@ -188,12 +188,12 @@ _citrus_EUC_unpack_state(_EUCEncodingInfo *ei __unused, _EUCState *s,
}
static int
-_citrus_EUC_mbrtowc_priv(_EUCEncodingInfo *ei, wchar_t *pwc, char **s,
+_citrus_EUC_mbrtowc_priv(_EUCEncodingInfo *ei, wchar_t *pwc, const char **s,
size_t n, _EUCState *psenc, size_t *nresult)
{
wchar_t wchar;
int c, chlenbak, cs, len;
- char *s0, *s1 = NULL;
+ const char *s0, *s1 = NULL;
s0 = *s;
diff --git a/lib/libiconv_modules/EUCTW/citrus_euctw.c b/lib/libiconv_modules/EUCTW/citrus_euctw.c
index 79aa9fa..f1f8a49 100644
--- a/lib/libiconv_modules/EUCTW/citrus_euctw.c
+++ b/lib/libiconv_modules/EUCTW/citrus_euctw.c
@@ -174,10 +174,10 @@ _citrus_EUCTW_encoding_module_uninit(_EUCTWEncodingInfo *ei __unused)
static int
_citrus_EUCTW_mbrtowc_priv(_EUCTWEncodingInfo * __restrict ei,
- wchar_t * __restrict pwc, char ** __restrict s,
+ wchar_t * __restrict pwc, const char ** __restrict s,
size_t n, _EUCTWState * __restrict psenc, size_t * __restrict nresult)
{
- char *s0;
+ const char *s0;
wchar_t wchar;
int c, chlenbak, cs;
diff --git a/lib/libiconv_modules/GBK2K/citrus_gbk2k.c b/lib/libiconv_modules/GBK2K/citrus_gbk2k.c
index ff31725..8da5c7c 100644
--- a/lib/libiconv_modules/GBK2K/citrus_gbk2k.c
+++ b/lib/libiconv_modules/GBK2K/citrus_gbk2k.c
@@ -147,10 +147,10 @@ _mb_count(wchar_t v)
static int
_citrus_GBK2K_mbrtowc_priv(_GBK2KEncodingInfo * __restrict ei,
- wchar_t * __restrict pwc, char ** __restrict s, size_t n,
+ wchar_t * __restrict pwc, const char ** __restrict s, size_t n,
_GBK2KState * __restrict psenc, size_t * __restrict nresult)
{
- char *s0, *s1;
+ const char *s0, *s1;
wchar_t wc;
int chlenbak, len;
diff --git a/lib/libiconv_modules/HZ/citrus_hz.c b/lib/libiconv_modules/HZ/citrus_hz.c
index 3775ea6..9bc7a5b 100644
--- a/lib/libiconv_modules/HZ/citrus_hz.c
+++ b/lib/libiconv_modules/HZ/citrus_hz.c
@@ -173,13 +173,13 @@ _citrus_HZ_unpack_state(_HZEncodingInfo * __restrict ei __unused,
static int
_citrus_HZ_mbrtowc_priv(_HZEncodingInfo * __restrict ei,
- wchar_t * __restrict pwc, char ** __restrict s, size_t n,
+ wchar_t * __restrict pwc, const char ** __restrict s, size_t n,
_HZState * __restrict psenc, size_t * __restrict nresult)
{
escape_t *candidate, *init;
graphic_t *graphic;
const range_t *range;
- char *s0;
+ const char *s0;
wchar_t wc;
int bit, ch, head, len, tail;
diff --git a/lib/libiconv_modules/ISO2022/citrus_iso2022.c b/lib/libiconv_modules/ISO2022/citrus_iso2022.c
index 82ad7c3..2ae8df6 100644
--- a/lib/libiconv_modules/ISO2022/citrus_iso2022.c
+++ b/lib/libiconv_modules/ISO2022/citrus_iso2022.c
@@ -572,7 +572,7 @@ terminate:
static wchar_t
_ISO2022_sgetwchar(_ISO2022EncodingInfo * __restrict ei __unused,
- char * __restrict string, size_t n, char ** __restrict result,
+ const char * __restrict string, size_t n, const char ** __restrict result,
_ISO2022State * __restrict psenc)
{
const struct seqtable *sp;
@@ -840,10 +840,10 @@ asis:
static int
_citrus_ISO2022_mbrtowc_priv(_ISO2022EncodingInfo * __restrict ei,
- wchar_t * __restrict pwc, char ** __restrict s,
+ wchar_t * __restrict pwc, const char ** __restrict s,
size_t n, _ISO2022State * __restrict psenc, size_t * __restrict nresult)
{
- char *p, *result, *s0;
+ const char *p, *result, *s0;
wchar_t wchar;
int c, chlenbak;
diff --git a/lib/libiconv_modules/JOHAB/citrus_johab.c b/lib/libiconv_modules/JOHAB/citrus_johab.c
index 35c35f3..0365379 100644
--- a/lib/libiconv_modules/JOHAB/citrus_johab.c
+++ b/lib/libiconv_modules/JOHAB/citrus_johab.c
@@ -143,10 +143,10 @@ ishanja(int l, int t)
static int
/*ARGSUSED*/
_citrus_JOHAB_mbrtowc_priv(_JOHABEncodingInfo * __restrict ei,
- wchar_t * __restrict pwc, char ** __restrict s, size_t n,
+ wchar_t * __restrict pwc, const char ** __restrict s, size_t n,
_JOHABState * __restrict psenc, size_t * __restrict nresult)
{
- char *s0;
+ const char *s0;
int l, t;
if (*s == NULL) {
diff --git a/lib/libiconv_modules/MSKanji/citrus_mskanji.c b/lib/libiconv_modules/MSKanji/citrus_mskanji.c
index e655448..fc0f13f 100644
--- a/lib/libiconv_modules/MSKanji/citrus_mskanji.c
+++ b/lib/libiconv_modules/MSKanji/citrus_mskanji.c
@@ -151,10 +151,10 @@ _citrus_MSKanji_unpack_state(_MSKanjiEncodingInfo * __restrict ei __unused,
static int
/*ARGSUSED*/
_citrus_MSKanji_mbrtowc_priv(_MSKanjiEncodingInfo * __restrict ei,
- wchar_t * __restrict pwc, char ** __restrict s, size_t n,
+ wchar_t * __restrict pwc, const char ** __restrict s, size_t n,
_MSKanjiState * __restrict psenc, size_t * __restrict nresult)
{
- char *s0;
+ const char *s0;
wchar_t wchar;
int chlenbak, len;
diff --git a/lib/libiconv_modules/UES/citrus_ues.c b/lib/libiconv_modules/UES/citrus_ues.c
index 1c4bc83..872cc1f 100644
--- a/lib/libiconv_modules/UES/citrus_ues.c
+++ b/lib/libiconv_modules/UES/citrus_ues.c
@@ -183,10 +183,10 @@ is_basic(wchar_t wc)
static int
_citrus_UES_mbrtowc_priv(_UESEncodingInfo * __restrict ei,
- wchar_t * __restrict pwc, char ** __restrict s, size_t n,
+ wchar_t * __restrict pwc, const char ** __restrict s, size_t n,
_UESState * __restrict psenc, size_t * __restrict nresult)
{
- char *s0;
+ const char *s0;
int ch, head, num, tail;
wchar_t hi, wc;
diff --git a/lib/libiconv_modules/UTF1632/citrus_utf1632.c b/lib/libiconv_modules/UTF1632/citrus_utf1632.c
index 6682ab2..4ef4db1 100644
--- a/lib/libiconv_modules/UTF1632/citrus_utf1632.c
+++ b/lib/libiconv_modules/UTF1632/citrus_utf1632.c
@@ -97,9 +97,9 @@ _citrus_UTF1632_init_state(_UTF1632EncodingInfo *ei __unused,
static int
_citrus_UTF1632_mbrtowc_priv(_UTF1632EncodingInfo *ei, wchar_t *pwc,
- char **s, size_t n, _UTF1632State *psenc, size_t *nresult)
+ const char **s, size_t n, _UTF1632State *psenc, size_t *nresult)
{
- char *s0;
+ const char *s0;
size_t result;
wchar_t wc = L'\0';
int chlenbak, endian, needlen;
diff --git a/lib/libiconv_modules/UTF7/citrus_utf7.c b/lib/libiconv_modules/UTF7/citrus_utf7.c
index e99f980..925be6d 100644
--- a/lib/libiconv_modules/UTF7/citrus_utf7.c
+++ b/lib/libiconv_modules/UTF7/citrus_utf7.c
@@ -149,11 +149,11 @@ static const char spaces[] = " \t\r\n";
static int
_citrus_UTF7_mbtoutf16(_UTF7EncodingInfo * __restrict ei,
- uint16_t * __restrict u16, char ** __restrict s, size_t n,
+ uint16_t * __restrict u16, const char ** __restrict s, size_t n,
_UTF7State * __restrict psenc, size_t * __restrict nresult)
{
_UTF7State sv;
- char *s0;
+ const char *s0;
int done, i, len;
s0 = *s;
@@ -238,10 +238,10 @@ ilseq:
static int
_citrus_UTF7_mbrtowc_priv(_UTF7EncodingInfo * __restrict ei,
- wchar_t * __restrict pwc, char ** __restrict s, size_t n,
+ wchar_t * __restrict pwc, const char ** __restrict s, size_t n,
_UTF7State * __restrict psenc, size_t * __restrict nresult)
{
- char *s0;
+ const char *s0;
uint32_t u32;
uint16_t hi, lo;
size_t nr, siz;
diff --git a/lib/libiconv_modules/UTF8/citrus_utf8.c b/lib/libiconv_modules/UTF8/citrus_utf8.c
index 2bd8d81..cdbc53c 100644
--- a/lib/libiconv_modules/UTF8/citrus_utf8.c
+++ b/lib/libiconv_modules/UTF8/citrus_utf8.c
@@ -175,10 +175,10 @@ _citrus_UTF8_unpack_state(_UTF8EncodingInfo *ei __unused, _UTF8State *s,
}
static int
-_citrus_UTF8_mbrtowc_priv(_UTF8EncodingInfo *ei, wchar_t *pwc, char **s,
+_citrus_UTF8_mbrtowc_priv(_UTF8EncodingInfo *ei, wchar_t *pwc, const char **s,
size_t n, _UTF8State *psenc, size_t *nresult)
{
- char *s0;
+ const char *s0;
wchar_t wchar;
int i;
uint8_t c;
diff --git a/lib/libiconv_modules/VIQR/citrus_viqr.c b/lib/libiconv_modules/VIQR/citrus_viqr.c
index bd26749..20175a3 100644
--- a/lib/libiconv_modules/VIQR/citrus_viqr.c
+++ b/lib/libiconv_modules/VIQR/citrus_viqr.c
@@ -250,11 +250,11 @@ _citrus_VIQR_unpack_state(_VIQREncodingInfo * __restrict ei __unused,
static int
_citrus_VIQR_mbrtowc_priv(_VIQREncodingInfo * __restrict ei,
- wchar_t * __restrict pwc, char ** __restrict s, size_t n,
+ wchar_t * __restrict pwc, const char ** __restrict s, size_t n,
_VIQRState * __restrict psenc, size_t * __restrict nresult)
{
mnemonic_t *m, *m0;
- char *s0;
+ const char *s0;
wchar_t wc;
ssize_t i;
int ch, escape;
diff --git a/lib/libiconv_modules/ZW/citrus_zw.c b/lib/libiconv_modules/ZW/citrus_zw.c
index d57661f..b9dc10b 100644
--- a/lib/libiconv_modules/ZW/citrus_zw.c
+++ b/lib/libiconv_modules/ZW/citrus_zw.c
@@ -105,10 +105,10 @@ _citrus_ZW_unpack_state(_ZWEncodingInfo * __restrict ei __unused,
static int
_citrus_ZW_mbrtowc_priv(_ZWEncodingInfo * __restrict ei,
- wchar_t * __restrict pwc, char **__restrict s, size_t n,
+ wchar_t * __restrict pwc, const char **__restrict s, size_t n,
_ZWState * __restrict psenc, size_t * __restrict nresult)
{
- char *s0;
+ const char *s0;
wchar_t wc;
int ch, len;
diff --git a/lib/libiconv_modules/iconv_none/citrus_iconv_none.c b/lib/libiconv_modules/iconv_none/citrus_iconv_none.c
index d9230cb..76bb06f 100644
--- a/lib/libiconv_modules/iconv_none/citrus_iconv_none.c
+++ b/lib/libiconv_modules/iconv_none/citrus_iconv_none.c
@@ -97,7 +97,7 @@ _citrus_iconv_none_iconv_uninit_context(struct _citrus_iconv *cv __unused)
static int
/*ARGSUSED*/
_citrus_iconv_none_iconv_convert(struct _citrus_iconv * __restrict ci __unused,
- char * __restrict * __restrict in, size_t * __restrict inbytes,
+ const char * __restrict * __restrict in, size_t * __restrict inbytes,
char * __restrict * __restrict out, size_t * __restrict outbytes,
uint32_t flags __unused, size_t * __restrict invalids)
{
diff --git a/lib/libiconv_modules/iconv_std/citrus_iconv_std.c b/lib/libiconv_modules/iconv_std/citrus_iconv_std.c
index 8349c46..b30f099 100644
--- a/lib/libiconv_modules/iconv_std/citrus_iconv_std.c
+++ b/lib/libiconv_modules/iconv_std/citrus_iconv_std.c
@@ -104,7 +104,7 @@ init_encoding_state(struct _citrus_iconv_std_encoding *se)
static __inline int
mbtocsx(struct _citrus_iconv_std_encoding *se,
- _csid_t *csid, _index_t *idx, char **s, size_t n, size_t *nresult,
+ _csid_t *csid, _index_t *idx, const char **s, size_t n, size_t *nresult,
struct iconv_hooks *hooks)
{
@@ -461,7 +461,7 @@ _citrus_iconv_std_iconv_uninit_context(struct _citrus_iconv *cv)
static int
_citrus_iconv_std_iconv_convert(struct _citrus_iconv * __restrict cv,
- char * __restrict * __restrict in, size_t * __restrict inbytes,
+ const char * __restrict * __restrict in, size_t * __restrict inbytes,
char * __restrict * __restrict out, size_t * __restrict outbytes,
uint32_t flags, size_t * __restrict invalids)
{
@@ -469,7 +469,7 @@ _citrus_iconv_std_iconv_convert(struct _citrus_iconv * __restrict cv,
struct _citrus_iconv_std_context *sc = cv->cv_closure;
_csid_t csid;
_index_t idx;
- char *tmpin;
+ const char *tmpin;
size_t inval, szrin, szrout;
int ret, state = 0;
diff --git a/lib/libprocstat/Makefile b/lib/libprocstat/Makefile
index 1ba2398..af5a775 100644
--- a/lib/libprocstat/Makefile
+++ b/lib/libprocstat/Makefile
@@ -9,6 +9,7 @@ SRCS= cd9660.c \
core.c \
libprocstat.c \
msdosfs.c \
+ smbfs.c \
udf.c
VERSION_DEF= ${.CURDIR}/Versions.def
diff --git a/lib/libprocstat/common_kvm.h b/lib/libprocstat/common_kvm.h
index d5e08e1..06627bf 100644
--- a/lib/libprocstat/common_kvm.h
+++ b/lib/libprocstat/common_kvm.h
@@ -41,6 +41,7 @@ int devfs_filestat(kvm_t *kd, struct vnode *vp, struct vnstat *vn);
int isofs_filestat(kvm_t *kd, struct vnode *vp, struct vnstat *vn);
int msdosfs_filestat(kvm_t *kd, struct vnode *vp, struct vnstat *vn);
int nfs_filestat(kvm_t *kd, struct vnode *vp, struct vnstat *vn);
+int smbfs_filestat(kvm_t *kd, struct vnode *vp, struct vnstat *vn);
int udf_filestat(kvm_t *kd, struct vnode *vp, struct vnstat *vn);
int ufs_filestat(kvm_t *kd, struct vnode *vp, struct vnstat *vn);
int zfs_filestat(kvm_t *kd, struct vnode *vp, struct vnstat *vn);
diff --git a/lib/libprocstat/libprocstat.c b/lib/libprocstat/libprocstat.c
index 7626c19..dfb5ed3 100644
--- a/lib/libprocstat/libprocstat.c
+++ b/lib/libprocstat/libprocstat.c
@@ -1220,6 +1220,7 @@ procstat_get_vnode_info_kvm(kvm_t *kd, struct filestat *fst,
FSTYPE(isofs),
FSTYPE(msdosfs),
FSTYPE(nfs),
+ FSTYPE(smbfs),
FSTYPE(udf),
FSTYPE(ufs),
#ifdef LIBPROCSTAT_ZFS
diff --git a/lib/librt/sigev_thread.c b/lib/librt/sigev_thread.c
index 0246c0c..2677a8e 100644
--- a/lib/librt/sigev_thread.c
+++ b/lib/librt/sigev_thread.c
@@ -28,13 +28,13 @@
*/
#include <sys/types.h>
-#include <machine/atomic.h>
#include "namespace.h"
#include <err.h>
#include <errno.h>
#include <ucontext.h>
#include <sys/thr.h>
+#include <stdatomic.h>
#include <stdio.h>
#include <stdlib.h>
#include <string.h>
@@ -51,7 +51,7 @@ LIST_HEAD(sigev_list_head, sigev_node);
static struct sigev_list_head sigev_hash[HASH_QUEUES];
static struct sigev_list_head sigev_all;
static LIST_HEAD(,sigev_thread) sigev_threads;
-static unsigned int sigev_generation;
+static atomic_int sigev_generation;
static pthread_mutex_t *sigev_list_mtx;
static pthread_once_t sigev_once = PTHREAD_ONCE_INIT;
static pthread_once_t sigev_once_default = PTHREAD_ONCE_INIT;
@@ -196,7 +196,8 @@ __sigev_alloc(int type, const struct sigevent *evp, struct sigev_node *prev,
if (sn != NULL) {
sn->sn_value = evp->sigev_value;
sn->sn_func = evp->sigev_notify_function;
- sn->sn_gen = atomic_fetchadd_int(&sigev_generation, 1);
+ sn->sn_gen = atomic_fetch_add_explicit(&sigev_generation, 1,
+ memory_order_relaxed);
sn->sn_type = type;
_pthread_attr_init(&sn->sn_attr);
_pthread_attr_setdetachstate(&sn->sn_attr, PTHREAD_CREATE_DETACHED);
diff --git a/lib/libstand/nfs.c b/lib/libstand/nfs.c
index a7a7ccb..adb0a11 100644
--- a/lib/libstand/nfs.c
+++ b/lib/libstand/nfs.c
@@ -1465,8 +1465,9 @@ nfs_readdir(struct open_file *f, struct dirent *d)
d->d_name[d->d_namlen] = '\0';
pos = roundup(d->d_namlen, sizeof(uint32_t)) / sizeof(uint32_t);
- fp->off = cookie = ((uint64_t)ntohl(rent->nameplus[pos++]) << 32) |
- ntohl(rent->nameplus[pos++]);
+ fp->off = cookie = ((uint64_t)ntohl(rent->nameplus[pos]) << 32) |
+ ntohl(rent->nameplus[pos + 1]);
+ pos += 2;
buf = (u_char *)&rent->nameplus[pos];
return (0);
}
diff --git a/lib/libutil/login_times.c b/lib/libutil/login_times.c
index e2c7bf2..d13ed99 100644
--- a/lib/libutil/login_times.c
+++ b/lib/libutil/login_times.c
@@ -96,7 +96,7 @@ parse_lt(const char *str)
else
m.lt_start = 0;
if (*p == '-')
- p = parse_time(++p, &m.lt_end);
+ p = parse_time(p + 1, &m.lt_end);
else
m.lt_end = 1440;
diff --git a/rescue/rescue/Makefile b/rescue/rescue/Makefile
index caec928..80acf63 100644
--- a/rescue/rescue/Makefile
+++ b/rescue/rescue/Makefile
@@ -130,6 +130,8 @@ CRUNCH_LIBS+= -lgeom -lbsdxml -lkiconv -lmd -lsbuf -lufs -lz
.if ${MACHINE_CPUARCH} == "i386"
CRUNCH_PROGS_sbin+= bsdlabel sconfig fdisk
CRUNCH_ALIAS_bsdlabel= disklabel
+#CRUNCH_PROGS+= mount_smbfs
+#CRUNCH_LIBS+= -lsmb
.endif
.if ${MACHINE} == "pc98"
diff --git a/sbin/devd/devd.8 b/sbin/devd/devd.8
index 8e33236..1869de6 100644
--- a/sbin/devd/devd.8
+++ b/sbin/devd/devd.8
@@ -33,7 +33,7 @@
.Nd "device state change daemon"
.Sh SYNOPSIS
.Nm
-.Op Fl Ddn
+.Op Fl dn
.Op Fl f Ar file
.Op Fl l Ar num
.Sh DESCRIPTION
@@ -44,10 +44,8 @@ kernel events happen.
.Pp
The following options are accepted.
.Bl -tag -width ".Fl f Ar file"
-.It Fl D
-Enable debugging messages.
.It Fl d
-Run in the foreground instead of becoming a daemon.
+Run in the foreground instead of becoming a daemon and log additional information for debugging.
.It Fl f Ar file
Use configuration file
.Ar file
diff --git a/sbin/devd/devd.cc b/sbin/devd/devd.cc
index b227589..0f11a83 100644
--- a/sbin/devd/devd.cc
+++ b/sbin/devd/devd.cc
@@ -79,6 +79,7 @@ __FBSDID("$FreeBSD$");
#include <cstdio>
#include <csignal>
#include <cstring>
+#include <cstdarg>
#include <dirent.h>
#include <err.h>
@@ -87,6 +88,7 @@ __FBSDID("$FreeBSD$");
#include <paths.h>
#include <poll.h>
#include <regex.h>
+#include <syslog.h>
#include <unistd.h>
#include <algorithm>
@@ -114,13 +116,15 @@ static const char detach = '-';
static struct pidfh *pfh;
-int Dflag;
int dflag;
int nflag;
+static unsigned total_events = 0;
+static volatile sig_atomic_t got_siginfo = 0;
static volatile sig_atomic_t romeo_must_die = 0;
static const char *configfile = CF;
+static void devdlog(int priority, const char* message, ...);
static void event_loop(void);
static void usage(void);
@@ -167,7 +171,7 @@ bool
event_proc::run(config &c) const
{
vector<eps *>::const_iterator i;
-
+
for (i = _epsvec.begin(); i != _epsvec.end(); ++i)
if (!(*i)->do_action(c))
return (false);
@@ -175,7 +179,7 @@ event_proc::run(config &c) const
}
action::action(const char *cmd)
- : _cmd(cmd)
+ : _cmd(cmd)
{
// nothing
}
@@ -194,7 +198,7 @@ my_system(const char *command)
sigset_t newsigblock, oldsigblock;
if (!command) /* just checking... */
- return(1);
+ return (1);
/*
* Ignore SIGINT and SIGQUIT, block SIGCHLD. Remember to save
@@ -243,8 +247,7 @@ bool
action::do_action(config &c)
{
string s = c.expand_string(_cmd.c_str());
- if (Dflag)
- fprintf(stderr, "Executing '%s'\n", s.c_str());
+ devdlog(LOG_NOTICE, "Executing '%s'\n", s.c_str());
my_system(s.c_str());
return (true);
}
@@ -268,15 +271,22 @@ match::do_match(config &c)
const string &value = c.get_variable(_var);
bool retval;
- if (Dflag)
- fprintf(stderr, "Testing %s=%s against %s, invert=%d\n",
+ /*
+ * This function gets called WAY too often to justify calling syslog()
+ * each time, even at LOG_DEBUG. Because if syslogd isn't running, it
+ * can consume excessive amounts of systime inside of connect(). Only
+ * log when we're in -d mode.
+ */
+ if (dflag) {
+ devdlog(LOG_DEBUG, "Testing %s=%s against %s, invert=%d\n",
_var.c_str(), value.c_str(), _re.c_str(), _inv);
+ }
retval = (regexec(&_regex, value.c_str(), 0, NULL, 0) == 0);
if (_inv == 1)
retval = (retval == 0) ? 1 : 0;
- return retval;
+ return (retval);
}
#include <sys/sockio.h>
@@ -322,8 +332,7 @@ media::do_match(config &c)
value = c.get_variable("device-name");
if (value.empty())
value = c.get_variable("subsystem");
- if (Dflag)
- fprintf(stderr, "Testing media type of %s against 0x%x\n",
+ devdlog(LOG_DEBUG, "Testing media type of %s against 0x%x\n",
value.c_str(), _type);
retval = false;
@@ -335,20 +344,18 @@ media::do_match(config &c)
if (ioctl(s, SIOCGIFMEDIA, (caddr_t)&ifmr) >= 0 &&
ifmr.ifm_status & IFM_AVALID) {
- if (Dflag)
- fprintf(stderr, "%s has media type 0x%x\n",
+ devdlog(LOG_DEBUG, "%s has media type 0x%x\n",
value.c_str(), IFM_TYPE(ifmr.ifm_active));
retval = (IFM_TYPE(ifmr.ifm_active) == _type);
} else if (_type == -1) {
- if (Dflag)
- fprintf(stderr, "%s has unknown media type\n",
+ devdlog(LOG_DEBUG, "%s has unknown media type\n",
value.c_str());
retval = true;
}
close(s);
}
- return retval;
+ return (retval);
}
const string var_list::bogus = "_$_$_$_$_B_O_G_U_S_$_$_$_$_";
@@ -374,8 +381,14 @@ var_list::is_set(const string &var) const
void
var_list::set_variable(const string &var, const string &val)
{
- if (Dflag)
- fprintf(stderr, "setting %s=%s\n", var.c_str(), val.c_str());
+ /*
+ * This function gets called WAY too often to justify calling syslog()
+ * each time, even at LOG_DEBUG. Because if syslogd isn't running, it
+ * can consume excessive amounts of systime inside of connect(). Only
+ * log when we're in -d mode.
+ */
+ if (dflag)
+ devdlog(LOG_DEBUG, "setting %s=%s\n", var.c_str(), val.c_str());
_vars[var] = val;
}
@@ -393,8 +406,7 @@ config::reset(void)
void
config::parse_one_file(const char *fn)
{
- if (Dflag)
- fprintf(stderr, "Parsing %s\n", fn);
+ devdlog(LOG_DEBUG, "Parsing %s\n", fn);
yyin = fopen(fn, "r");
if (yyin == NULL)
err(1, "Cannot open config file %s", fn);
@@ -411,8 +423,7 @@ config::parse_files_in_dir(const char *dirname)
struct dirent *dp;
char path[PATH_MAX];
- if (Dflag)
- fprintf(stderr, "Parsing files in %s\n", dirname);
+ devdlog(LOG_DEBUG, "Parsing files in %s\n", dirname);
dirp = opendir(dirname);
if (dirp == NULL)
return;
@@ -460,7 +471,7 @@ void
config::open_pidfile()
{
pid_t otherpid;
-
+
if (_pidfile.empty())
return;
pfh = pidfile_open(_pidfile.c_str(), 0600, &otherpid);
@@ -474,21 +485,21 @@ config::open_pidfile()
void
config::write_pidfile()
{
-
+
pidfile_write(pfh);
}
void
config::close_pidfile()
{
-
+
pidfile_close(pfh);
}
void
config::remove_pidfile()
{
-
+
pidfile_remove(pfh);
}
@@ -536,11 +547,10 @@ void
config::push_var_table()
{
var_list *vl;
-
+
vl = new var_list();
_var_list_table.push_back(vl);
- if (Dflag)
- fprintf(stderr, "Pushing table\n");
+ devdlog(LOG_DEBUG, "Pushing table\n");
}
void
@@ -548,8 +558,7 @@ config::pop_var_table()
{
delete _var_list_table.back();
_var_list_table.pop_back();
- if (Dflag)
- fprintf(stderr, "Popping table\n");
+ devdlog(LOG_DEBUG, "Popping table\n");
}
void
@@ -573,7 +582,7 @@ config::get_variable(const string &var)
bool
config::is_id_char(char ch) const
{
- return (ch != '\0' && (isalpha(ch) || isdigit(ch) || ch == '_' ||
+ return (ch != '\0' && (isalpha(ch) || isdigit(ch) || ch == '_' ||
ch == '-'));
}
@@ -589,7 +598,7 @@ config::expand_one(const char *&src, string &dst)
dst += *src++;
return;
}
-
+
// $(foo) -> $(foo)
// Not sure if I want to support this or not, so for now we just pass
// it through.
@@ -606,7 +615,7 @@ config::expand_one(const char *&src, string &dst)
}
return;
}
-
+
// $[^A-Za-z] -> $\1
if (!isalpha(*src)) {
dst += '$';
@@ -657,7 +666,7 @@ bool
config::chop_var(char *&buffer, char *&lhs, char *&rhs) const
{
char *walker;
-
+
if (*buffer == '\0')
return (false);
walker = lhs = buffer;
@@ -731,8 +740,7 @@ config::find_and_execute(char type)
s = "detach";
break;
}
- if (Dflag)
- fprintf(stderr, "Processing %s event\n", s);
+ devdlog(LOG_DEBUG, "Processing %s event\n", s);
for (i = l->begin(); i != l->end(); ++i) {
if ((*i)->matches(*this)) {
(*i)->run(*this);
@@ -742,7 +750,7 @@ config::find_and_execute(char type)
}
-
+
static void
process_event(char *buffer)
{
@@ -750,8 +758,7 @@ process_event(char *buffer)
char *sp;
sp = buffer + 1;
- if (Dflag)
- fprintf(stderr, "Processing event '%s'\n", buffer);
+ devdlog(LOG_DEBUG, "Processing event '%s'\n", buffer);
type = *buffer++;
cfg.push_var_table();
// No match doesn't have a device, and the format is a little
@@ -794,7 +801,7 @@ process_event(char *buffer)
cfg.set_variable("bus", sp + 3);
break;
}
-
+
cfg.find_and_execute(type);
cfg.pop_var_table();
}
@@ -843,6 +850,8 @@ notify_clients(const char *data, int len)
--num_clients;
close(*i);
i = clients.erase(i);
+ devdlog(LOG_WARNING, "notify_clients: write() failed; "
+ "dropping unresponsive client\n");
} else
++i;
}
@@ -871,6 +880,8 @@ check_clients(void)
--num_clients;
close(*i);
i = clients.erase(i);
+ devdlog(LOG_NOTICE, "check_clients: "
+ "dropping disconnected client\n");
} else
++i;
}
@@ -923,8 +934,7 @@ event_loop(void)
rv = select(fd + 1, &fds, &fds, &fds, &tv);
// No events -> we've processed all pending events
if (rv == 0) {
- if (Dflag)
- fprintf(stderr, "Calling daemon\n");
+ devdlog(LOG_DEBUG, "Calling daemon\n");
cfg.remove_pidfile();
cfg.open_pidfile();
daemon(0, 0);
@@ -958,6 +968,11 @@ event_loop(void)
tv.tv_usec = 0;
}
rv = select(max_fd, &fds, NULL, NULL, &tv);
+ if (got_siginfo) {
+ devdlog(LOG_INFO, "Events received so far=%ld\n",
+ total_events);
+ got_siginfo = 0;
+ }
if (rv == -1) {
if (errno == EINTR)
continue;
@@ -967,6 +982,12 @@ event_loop(void)
if (FD_ISSET(fd, &fds)) {
rv = read(fd, buffer, sizeof(buffer) - 1);
if (rv > 0) {
+ total_events++;
+ if (rv == sizeof(buffer) - 1) {
+ devdlog(LOG_WARNING, "Warning: "
+ "available event data exceeded "
+ "buffer space\n");
+ }
notify_clients(buffer, rv);
buffer[rv] = '\0';
while (buffer[--rv] == '\n')
@@ -985,7 +1006,7 @@ event_loop(void)
}
close(fd);
}
-
+
/*
* functions that the parser uses.
*/
@@ -1070,7 +1091,7 @@ set_variable(const char *var, const char *val)
free(const_cast<char *>(val));
}
-
+
static void
gensighand(int)
@@ -1078,10 +1099,37 @@ gensighand(int)
romeo_must_die = 1;
}
+/*
+ * SIGINFO handler. Will print useful statistics to the syslog or stderr
+ * as appropriate
+ */
+static void
+siginfohand(int)
+{
+ got_siginfo = 1;
+}
+
+/*
+ * Local logging function. Prints to syslog if we're daemonized; syslog
+ * otherwise.
+ */
+static void
+devdlog(int priority, const char* fmt, ...)
+{
+ va_list argp;
+
+ va_start(argp, fmt);
+ if (dflag)
+ vfprintf(stderr, fmt, argp);
+ else
+ vsyslog(priority, fmt, argp);
+ va_end(argp);
+}
+
static void
usage()
{
- fprintf(stderr, "usage: %s [-Ddn] [-l connlimit] [-f file]\n",
+ fprintf(stderr, "usage: %s [-dn] [-l connlimit] [-f file]\n",
getprogname());
exit(1);
}
@@ -1111,11 +1159,8 @@ main(int argc, char **argv)
int ch;
check_devd_enabled();
- while ((ch = getopt(argc, argv, "Ddf:l:n")) != -1) {
+ while ((ch = getopt(argc, argv, "df:l:n")) != -1) {
switch (ch) {
- case 'D':
- Dflag++;
- break;
case 'd':
dflag++;
break;
@@ -1143,6 +1188,7 @@ main(int argc, char **argv)
signal(SIGHUP, gensighand);
signal(SIGINT, gensighand);
signal(SIGTERM, gensighand);
+ signal(SIGINFO, siginfohand);
event_loop();
return (0);
}
diff --git a/sbin/dhclient/bpf.c b/sbin/dhclient/bpf.c
index 9f8e45f..f435028 100644
--- a/sbin/dhclient/bpf.c
+++ b/sbin/dhclient/bpf.c
@@ -44,6 +44,8 @@
__FBSDID("$FreeBSD$");
#include "dhcpd.h"
+#include "privsep.h"
+#include <sys/capability.h>
#include <sys/ioctl.h>
#include <sys/uio.h>
@@ -61,15 +63,15 @@ __FBSDID("$FreeBSD$");
* mask.
*/
int
-if_register_bpf(struct interface_info *info)
+if_register_bpf(struct interface_info *info, int flags)
{
char filename[50];
int sock, b;
/* Open a BPF device */
- for (b = 0; 1; b++) {
+ for (b = 0;; b++) {
snprintf(filename, sizeof(filename), BPF_FORMAT, b);
- sock = open(filename, O_RDWR, 0);
+ sock = open(filename, flags);
if (sock < 0) {
if (errno == EBUSY)
continue;
@@ -87,16 +89,79 @@ if_register_bpf(struct interface_info *info)
return (sock);
}
+/*
+ * Packet write filter program:
+ * 'ip and udp and src port bootps and dst port (bootps or bootpc)'
+ */
+struct bpf_insn dhcp_bpf_wfilter[] = {
+ BPF_STMT(BPF_LD + BPF_B + BPF_IND, 14),
+ BPF_JUMP(BPF_JMP + BPF_JEQ + BPF_K, (IPVERSION << 4) + 5, 0, 12),
+
+ /* Make sure this is an IP packet... */
+ BPF_STMT(BPF_LD + BPF_H + BPF_ABS, 12),
+ BPF_JUMP(BPF_JMP + BPF_JEQ + BPF_K, ETHERTYPE_IP, 0, 10),
+
+ /* Make sure it's a UDP packet... */
+ BPF_STMT(BPF_LD + BPF_B + BPF_ABS, 23),
+ BPF_JUMP(BPF_JMP + BPF_JEQ + BPF_K, IPPROTO_UDP, 0, 8),
+
+ /* Make sure this isn't a fragment... */
+ BPF_STMT(BPF_LD + BPF_H + BPF_ABS, 20),
+ BPF_JUMP(BPF_JMP + BPF_JSET + BPF_K, 0x1fff, 6, 0), /* patched */
+
+ /* Get the IP header length... */
+ BPF_STMT(BPF_LDX + BPF_B + BPF_MSH, 14),
+
+ /* Make sure it's from the right port... */
+ BPF_STMT(BPF_LD + BPF_H + BPF_IND, 14),
+ BPF_JUMP(BPF_JMP + BPF_JEQ + BPF_K, 68, 0, 3),
+
+ /* Make sure it is to the right ports ... */
+ BPF_STMT(BPF_LD + BPF_H + BPF_IND, 16),
+ BPF_JUMP(BPF_JMP + BPF_JEQ + BPF_K, 67, 0, 1),
+
+ /* If we passed all the tests, ask for the whole packet. */
+ BPF_STMT(BPF_RET+BPF_K, (u_int)-1),
+
+ /* Otherwise, drop it. */
+ BPF_STMT(BPF_RET+BPF_K, 0),
+};
+
+int dhcp_bpf_wfilter_len = sizeof(dhcp_bpf_wfilter) / sizeof(struct bpf_insn);
+
void
if_register_send(struct interface_info *info)
{
+ struct bpf_version v;
+ struct bpf_program p;
int sock, on = 1;
- /*
- * If we're using the bpf API for sending and receiving, we
- * don't need to register this interface twice.
- */
- info->wfdesc = info->rfdesc;
+ /* Open a BPF device and hang it on this interface... */
+ info->wfdesc = if_register_bpf(info, O_WRONLY);
+
+ /* Make sure the BPF version is in range... */
+ if (ioctl(info->wfdesc, BIOCVERSION, &v) < 0)
+ error("Can't get BPF version: %m");
+
+ if (v.bv_major != BPF_MAJOR_VERSION ||
+ v.bv_minor < BPF_MINOR_VERSION)
+ error("Kernel BPF version out of range - recompile dhcpd!");
+
+ /* Set up the bpf write filter program structure. */
+ p.bf_len = dhcp_bpf_wfilter_len;
+ p.bf_insns = dhcp_bpf_wfilter;
+
+ if (dhcp_bpf_wfilter[7].k == 0x1fff)
+ dhcp_bpf_wfilter[7].k = htons(IP_MF|IP_OFFMASK);
+
+ if (ioctl(info->wfdesc, BIOCSETWF, &p) < 0)
+ error("Can't install write filter program: %m");
+
+ if (ioctl(info->wfdesc, BIOCLOCK, NULL) < 0)
+ error("Cannot lock bpf");
+
+ if (cap_rights_limit(info->wfdesc, CAP_WRITE) < 0 && errno != ENOSYS)
+ error("Can't limit bpf descriptor: %m");
/*
* Use raw socket for unicast send.
@@ -144,55 +209,16 @@ struct bpf_insn dhcp_bpf_filter[] = {
int dhcp_bpf_filter_len = sizeof(dhcp_bpf_filter) / sizeof(struct bpf_insn);
-/*
- * Packet write filter program:
- * 'ip and udp and src port bootps and dst port (bootps or bootpc)'
- */
-struct bpf_insn dhcp_bpf_wfilter[] = {
- BPF_STMT(BPF_LD + BPF_B + BPF_IND, 14),
- BPF_JUMP(BPF_JMP + BPF_JEQ + BPF_K, (IPVERSION << 4) + 5, 0, 12),
-
- /* Make sure this is an IP packet... */
- BPF_STMT(BPF_LD + BPF_H + BPF_ABS, 12),
- BPF_JUMP(BPF_JMP + BPF_JEQ + BPF_K, ETHERTYPE_IP, 0, 10),
-
- /* Make sure it's a UDP packet... */
- BPF_STMT(BPF_LD + BPF_B + BPF_ABS, 23),
- BPF_JUMP(BPF_JMP + BPF_JEQ + BPF_K, IPPROTO_UDP, 0, 8),
-
- /* Make sure this isn't a fragment... */
- BPF_STMT(BPF_LD + BPF_H + BPF_ABS, 20),
- BPF_JUMP(BPF_JMP + BPF_JSET + BPF_K, 0x1fff, 6, 0), /* patched */
-
- /* Get the IP header length... */
- BPF_STMT(BPF_LDX + BPF_B + BPF_MSH, 14),
-
- /* Make sure it's from the right port... */
- BPF_STMT(BPF_LD + BPF_H + BPF_IND, 14),
- BPF_JUMP(BPF_JMP + BPF_JEQ + BPF_K, 68, 0, 3),
-
- /* Make sure it is to the right ports ... */
- BPF_STMT(BPF_LD + BPF_H + BPF_IND, 16),
- BPF_JUMP(BPF_JMP + BPF_JEQ + BPF_K, 67, 0, 1),
-
- /* If we passed all the tests, ask for the whole packet. */
- BPF_STMT(BPF_RET+BPF_K, (u_int)-1),
-
- /* Otherwise, drop it. */
- BPF_STMT(BPF_RET+BPF_K, 0),
-};
-
-int dhcp_bpf_wfilter_len = sizeof(dhcp_bpf_wfilter) / sizeof(struct bpf_insn);
-
void
if_register_receive(struct interface_info *info)
{
+ static const unsigned long cmds[2] = { SIOCGIFFLAGS, SIOCGIFMEDIA };
struct bpf_version v;
struct bpf_program p;
int flag = 1, sz;
/* Open a BPF device and hang it on this interface... */
- info->rfdesc = if_register_bpf(info);
+ info->rfdesc = if_register_bpf(info, O_RDONLY);
/* Make sure the BPF version is in range... */
if (ioctl(info->rfdesc, BIOCVERSION, &v) < 0)
@@ -235,48 +261,95 @@ if_register_receive(struct interface_info *info)
if (ioctl(info->rfdesc, BIOCSETF, &p) < 0)
error("Can't install packet filter program: %m");
- /* Set up the bpf write filter program structure. */
- p.bf_len = dhcp_bpf_wfilter_len;
- p.bf_insns = dhcp_bpf_wfilter;
-
- if (dhcp_bpf_wfilter[7].k == 0x1fff)
- dhcp_bpf_wfilter[7].k = htons(IP_MF|IP_OFFMASK);
-
- if (ioctl(info->rfdesc, BIOCSETWF, &p) < 0)
- error("Can't install write filter program: %m");
-
if (ioctl(info->rfdesc, BIOCLOCK, NULL) < 0)
error("Cannot lock bpf");
+
+ if (cap_rights_limit(info->rfdesc,
+ CAP_IOCTL | CAP_POLL_EVENT | CAP_READ) < 0 && errno != ENOSYS) {
+ error("Can't limit bpf descriptor: %m");
+ }
+ if (cap_ioctls_limit(info->rfdesc, cmds, 2) < 0 && errno != ENOSYS)
+ error("Can't limit ioctls for bpf descriptor: %m");
}
-ssize_t
-send_packet(struct interface_info *interface, struct dhcp_packet *raw,
- size_t len, struct in_addr from, struct sockaddr_in *to,
- struct hardware *hto)
+void
+send_packet_unpriv(int privfd, struct dhcp_packet *raw, size_t len,
+ struct in_addr from, struct in_addr to)
+{
+ struct imsg_hdr hdr;
+ struct buf *buf;
+ int errs;
+
+ hdr.code = IMSG_SEND_PACKET;
+ hdr.len = sizeof(hdr) +
+ sizeof(size_t) + len +
+ sizeof(from) + sizeof(to);
+
+ if ((buf = buf_open(hdr.len)) == NULL)
+ error("buf_open: %m");
+
+ errs = 0;
+ errs += buf_add(buf, &hdr, sizeof(hdr));
+ errs += buf_add(buf, &len, sizeof(len));
+ errs += buf_add(buf, raw, len);
+ errs += buf_add(buf, &from, sizeof(from));
+ errs += buf_add(buf, &to, sizeof(to));
+ if (errs)
+ error("buf_add: %m");
+
+ if (buf_close(privfd, buf) == -1)
+ error("buf_close: %m");
+}
+
+void
+send_packet_priv(struct interface_info *interface, struct imsg_hdr *hdr, int fd)
{
unsigned char buf[256];
struct iovec iov[2];
struct msghdr msg;
+ struct dhcp_packet raw;
+ size_t len;
+ struct in_addr from, to;
int result, bufp = 0;
+ if (hdr->len < sizeof(*hdr) + sizeof(size_t))
+ error("corrupted message received");
+ buf_read(fd, &len, sizeof(len));
+ if (hdr->len != sizeof(*hdr) + sizeof(size_t) + len +
+ sizeof(from) + sizeof(to)) {
+ error("corrupted message received");
+ }
+ if (len > sizeof(raw))
+ error("corrupted message received");
+ buf_read(fd, &raw, len);
+ buf_read(fd, &from, sizeof(from));
+ buf_read(fd, &to, sizeof(to));
+
/* Assemble the headers... */
- if (to->sin_addr.s_addr == INADDR_BROADCAST)
- assemble_hw_header(interface, buf, &bufp, hto);
- assemble_udp_ip_header(buf, &bufp, from.s_addr,
- to->sin_addr.s_addr, to->sin_port, (unsigned char *)raw, len);
+ if (to.s_addr == INADDR_BROADCAST)
+ assemble_hw_header(interface, buf, &bufp);
+ assemble_udp_ip_header(buf, &bufp, from.s_addr, to.s_addr,
+ htons(REMOTE_PORT), (unsigned char *)&raw, len);
- iov[0].iov_base = (char *)buf;
+ iov[0].iov_base = buf;
iov[0].iov_len = bufp;
- iov[1].iov_base = (char *)raw;
+ iov[1].iov_base = &raw;
iov[1].iov_len = len;
/* Fire it off */
- if (to->sin_addr.s_addr == INADDR_BROADCAST)
+ if (to.s_addr == INADDR_BROADCAST)
result = writev(interface->wfdesc, iov, 2);
else {
+ struct sockaddr_in sato;
+
+ sato.sin_addr = to;
+ sato.sin_port = htons(REMOTE_PORT);
+ sato.sin_family = AF_INET;
+ sato.sin_len = sizeof(sato);
+
memset(&msg, 0, sizeof(msg));
- msg.msg_name = (struct sockaddr *)to;
- msg.msg_namelen = sizeof(*to);
+ msg.msg_name = (struct sockaddr *)&sato;
+ msg.msg_namelen = sizeof(sato);
msg.msg_iov = iov;
msg.msg_iovlen = 2;
result = sendmsg(interface->ufdesc, &msg, 0);
@@ -284,7 +357,6 @@ send_packet(struct interface_info *interface, struct dhcp_packet *raw,
if (result < 0)
warning("send_packet: %m");
- return (result);
}
ssize_t
diff --git a/sbin/dhclient/clparse.c b/sbin/dhclient/clparse.c
index 58de8cc..4f234c7 100644
--- a/sbin/dhclient/clparse.c
+++ b/sbin/dhclient/clparse.c
@@ -642,6 +642,10 @@ parse_client_lease_declaration(FILE *cfile, struct client_lease *lease,
case FILENAME:
lease->filename = parse_string(cfile);
return;
+ case NEXT_SERVER:
+ if (!parse_ip_addr(cfile, &lease->nextserver))
+ return;
+ break;
case SERVER_NAME:
lease->server_name = parse_string(cfile);
return;
diff --git a/sbin/dhclient/dhclient.c b/sbin/dhclient/dhclient.c
index f71c8c8..c8f05b5 100644
--- a/sbin/dhclient/dhclient.c
+++ b/sbin/dhclient/dhclient.c
@@ -59,6 +59,8 @@ __FBSDID("$FreeBSD$");
#include "dhcpd.h"
#include "privsep.h"
+#include <sys/capability.h>
+
#include <net80211/ieee80211_freebsd.h>
#ifndef _PATH_VAREMPTY
@@ -91,9 +93,10 @@ int log_perror = 1;
int privfd;
int nullfd = -1;
+char hostname[_POSIX_HOST_NAME_MAX + 1];
+
struct iaddr iaddr_broadcast = { 4, { 255, 255, 255, 255 } };
-struct in_addr inaddr_any;
-struct sockaddr_in sockaddr_broadcast;
+struct in_addr inaddr_any, inaddr_broadcast;
char *path_dhclient_pidfile;
struct pidfh *pidfile;
@@ -410,11 +413,7 @@ main(int argc, char *argv[])
tzset();
time(&cur_time);
- memset(&sockaddr_broadcast, 0, sizeof(sockaddr_broadcast));
- sockaddr_broadcast.sin_family = AF_INET;
- sockaddr_broadcast.sin_port = htons(REMOTE_PORT);
- sockaddr_broadcast.sin_addr.s_addr = INADDR_BROADCAST;
- sockaddr_broadcast.sin_len = sizeof(sockaddr_broadcast);
+ inaddr_broadcast.s_addr = INADDR_BROADCAST;
inaddr_any.s_addr = INADDR_ANY;
read_client_conf();
@@ -451,13 +450,37 @@ main(int argc, char *argv[])
error("no such user: nobody");
}
+ /*
+ * Obtain hostname before entering capability mode - it won't be
+ * possible then, as reading kern.hostname is not permitted.
+ */
+ if (gethostname(hostname, sizeof(hostname)) < 0)
+ hostname[0] = '\0';
+
+ priv_script_init("PREINIT", NULL);
+ if (ifi->client->alias)
+ priv_script_write_params("alias_", ifi->client->alias);
+ priv_script_go();
+
+ /* set up the interface */
+ discover_interfaces(ifi);
+
if (pipe(pipe_fd) == -1)
error("pipe");
fork_privchld(pipe_fd[0], pipe_fd[1]);
+ close(ifi->ufdesc);
+ ifi->ufdesc = -1;
+ close(ifi->wfdesc);
+ ifi->wfdesc = -1;
+
close(pipe_fd[0]);
privfd = pipe_fd[1];
+ if (cap_rights_limit(privfd, CAP_READ | CAP_WRITE) < 0 &&
+ errno != ENOSYS) {
+ error("can't limit private descriptor: %m");
+ }
if ((fd = open(path_dhclient_db, O_RDONLY|O_EXLOCK|O_CREAT, 0)) == -1)
error("can't open and lock %s: %m", path_dhclient_db);
@@ -465,16 +488,14 @@ main(int argc, char *argv[])
rewrite_client_leases();
close(fd);
- priv_script_init("PREINIT", NULL);
- if (ifi->client->alias)
- priv_script_write_params("alias_", ifi->client->alias);
- priv_script_go();
-
if ((routefd = socket(PF_ROUTE, SOCK_RAW, 0)) != -1)
add_protocol("AF_ROUTE", routefd, routehandler, ifi);
-
- /* set up the interface */
- discover_interfaces(ifi);
+ if (shutdown(routefd, SHUT_WR) < 0)
+ error("can't shutdown route socket: %m");
+ if (cap_rights_limit(routefd, CAP_POLL_EVENT | CAP_READ) < 0 &&
+ errno != ENOSYS) {
+ error("can't limit route socket: %m");
+ }
if (chroot(_PATH_VAREMPTY) == -1)
error("chroot");
@@ -490,6 +511,9 @@ main(int argc, char *argv[])
setproctitle("%s", ifi->name);
+ if (cap_enter() < 0 && errno != ENOSYS)
+ error("can't enter capability mode: %m");
+
if (immediate_daemon)
go_daemon();
@@ -1063,6 +1087,9 @@ packet_to_lease(struct packet *packet)
lease->address.len = sizeof(packet->raw->yiaddr);
memcpy(lease->address.iabuf, &packet->raw->yiaddr, lease->address.len);
+ lease->nextserver.len = sizeof(packet->raw->siaddr);
+ memcpy(lease->nextserver.iabuf, &packet->raw->siaddr, lease->nextserver.len);
+
/* If the server name was filled out, copy it.
Do not attempt to validate the server name as a host name.
RFC 2131 merely states that sname is NUL-terminated (which do
@@ -1223,13 +1250,12 @@ again:
ip->client->secs = ip->client->packet.secs;
note("DHCPDISCOVER on %s to %s port %d interval %d",
- ip->name, inet_ntoa(sockaddr_broadcast.sin_addr),
- ntohs(sockaddr_broadcast.sin_port),
+ ip->name, inet_ntoa(inaddr_broadcast), REMOTE_PORT,
(int)ip->client->interval);
/* Send out a packet. */
- (void)send_packet(ip, &ip->client->packet, ip->client->packet_length,
- inaddr_any, &sockaddr_broadcast, NULL);
+ send_packet_unpriv(privfd, &ip->client->packet,
+ ip->client->packet_length, inaddr_any, inaddr_broadcast);
add_timeout(cur_time + ip->client->interval, send_discover, ip);
}
@@ -1337,8 +1363,7 @@ void
send_request(void *ipp)
{
struct interface_info *ip = ipp;
- struct sockaddr_in destination;
- struct in_addr from;
+ struct in_addr from, to;
int interval;
/* Figure out how long it's been since we started transmitting. */
@@ -1426,18 +1451,13 @@ cancel:
/* If the lease T2 time has elapsed, or if we're not yet bound,
broadcast the DHCPREQUEST rather than unicasting. */
- memset(&destination, 0, sizeof(destination));
if (ip->client->state == S_REQUESTING ||
ip->client->state == S_REBOOTING ||
cur_time > ip->client->active->rebind)
- destination.sin_addr.s_addr = INADDR_BROADCAST;
+ to.s_addr = INADDR_BROADCAST;
else
- memcpy(&destination.sin_addr.s_addr,
- ip->client->destination.iabuf,
- sizeof(destination.sin_addr.s_addr));
- destination.sin_port = htons(REMOTE_PORT);
- destination.sin_family = AF_INET;
- destination.sin_len = sizeof(destination);
+ memcpy(&to.s_addr, ip->client->destination.iabuf,
+ sizeof(to.s_addr));
if (ip->client->state != S_REQUESTING)
memcpy(&from, ip->client->active->address.iabuf,
@@ -1455,12 +1475,12 @@ cancel:
ip->client->packet.secs = htons(65535);
}
- note("DHCPREQUEST on %s to %s port %d", ip->name,
- inet_ntoa(destination.sin_addr), ntohs(destination.sin_port));
+ note("DHCPREQUEST on %s to %s port %d", ip->name, inet_ntoa(to),
+ REMOTE_PORT);
/* Send out a packet. */
- (void) send_packet(ip, &ip->client->packet, ip->client->packet_length,
- from, &destination, NULL);
+ send_packet_unpriv(privfd, &ip->client->packet,
+ ip->client->packet_length, from, to);
add_timeout(cur_time + ip->client->interval, send_request, ip);
}
@@ -1471,12 +1491,11 @@ send_decline(void *ipp)
struct interface_info *ip = ipp;
note("DHCPDECLINE on %s to %s port %d", ip->name,
- inet_ntoa(sockaddr_broadcast.sin_addr),
- ntohs(sockaddr_broadcast.sin_port));
+ inet_ntoa(inaddr_broadcast), REMOTE_PORT);
/* Send out a packet. */
- (void) send_packet(ip, &ip->client->packet, ip->client->packet_length,
- inaddr_any, &sockaddr_broadcast, NULL);
+ send_packet_unpriv(privfd, &ip->client->packet,
+ ip->client->packet_length, inaddr_any, inaddr_broadcast);
}
void
@@ -1533,11 +1552,10 @@ make_discover(struct interface_info *ip, struct client_lease *lease)
ip->client->config->send_options[i].len;
options[i]->timeout = 0xFFFFFFFF;
}
-
+
/* send host name if not set via config file. */
- char hostname[_POSIX_HOST_NAME_MAX+1];
if (!options[DHO_HOST_NAME]) {
- if (gethostname(hostname, sizeof(hostname)) == 0) {
+ if (hostname[0] != '\0') {
size_t len;
char* posDot = strchr(hostname, '.');
if (posDot != NULL)
@@ -1558,7 +1576,7 @@ make_discover(struct interface_info *ip, struct client_lease *lease)
int hwlen = (ip->hw_address.hlen < sizeof(client_ident)-1) ?
ip->hw_address.hlen : sizeof(client_ident)-1;
client_ident[0] = ip->hw_address.htype;
- memcpy(&client_ident[1], ip->hw_address.haddr, hwlen);
+ memcpy(&client_ident[1], ip->hw_address.haddr, hwlen);
options[DHO_DHCP_CLIENT_IDENTIFIER] = &option_elements[DHO_DHCP_CLIENT_IDENTIFIER];
options[DHO_DHCP_CLIENT_IDENTIFIER]->value = client_ident;
options[DHO_DHCP_CLIENT_IDENTIFIER]->len = hwlen+1;
@@ -1657,11 +1675,10 @@ make_request(struct interface_info *ip, struct client_lease * lease)
ip->client->config->send_options[i].len;
options[i]->timeout = 0xFFFFFFFF;
}
-
+
/* send host name if not set via config file. */
- char hostname[_POSIX_HOST_NAME_MAX+1];
if (!options[DHO_HOST_NAME]) {
- if (gethostname(hostname, sizeof(hostname)) == 0) {
+ if (hostname[0] != '\0') {
size_t len;
char* posDot = strchr(hostname, '.');
if (posDot != NULL)
@@ -1682,7 +1699,7 @@ make_request(struct interface_info *ip, struct client_lease * lease)
int hwlen = (ip->hw_address.hlen < sizeof(client_ident)-1) ?
ip->hw_address.hlen : sizeof(client_ident)-1;
client_ident[0] = ip->hw_address.htype;
- memcpy(&client_ident[1], ip->hw_address.haddr, hwlen);
+ memcpy(&client_ident[1], ip->hw_address.haddr, hwlen);
options[DHO_DHCP_CLIENT_IDENTIFIER] = &option_elements[DHO_DHCP_CLIENT_IDENTIFIER];
options[DHO_DHCP_CLIENT_IDENTIFIER]->value = client_ident;
options[DHO_DHCP_CLIENT_IDENTIFIER]->len = hwlen+1;
@@ -1828,6 +1845,11 @@ rewrite_client_leases(void)
leaseFile = fopen(path_dhclient_db, "w");
if (!leaseFile)
error("can't create %s: %m", path_dhclient_db);
+ if (cap_rights_limit(fileno(leaseFile), CAP_FSTAT | CAP_FSYNC |
+ CAP_FTRUNCATE | CAP_SEEK | CAP_WRITE) < 0 &&
+ errno != ENOSYS) {
+ error("can't limit lease descriptor: %m");
+ }
} else {
fflush(leaseFile);
rewind(leaseFile);
@@ -1874,6 +1896,11 @@ write_client_lease(struct interface_info *ip, struct client_lease *lease,
fprintf(leaseFile, " bootp;\n");
fprintf(leaseFile, " interface \"%s\";\n", ip->name);
fprintf(leaseFile, " fixed-address %s;\n", piaddr(lease->address));
+ if (lease->nextserver.len == sizeof(inaddr_any) &&
+ 0 != memcmp(lease->nextserver.iabuf, &inaddr_any,
+ sizeof(inaddr_any)))
+ fprintf(leaseFile, " next-server %s;\n",
+ piaddr(lease->nextserver));
if (lease->filename)
fprintf(leaseFile, " filename \"%s\";\n", lease->filename);
if (lease->server_name)
@@ -2339,8 +2366,13 @@ go_daemon(void)
if (daemon(1, 0) == -1)
error("daemon");
- if (pidfile != NULL)
+ if (pidfile != NULL) {
pidfile_write(pidfile);
+ if (cap_rights_limit(pidfile_fileno(pidfile), CAP_NONE) < 0 &&
+ errno != ENOSYS) {
+ error("can't limit pidfile descriptor: %m");
+ }
+ }
/* we are chrooted, daemon(3) fails to open /dev/null */
if (nullfd != -1) {
@@ -2350,6 +2382,13 @@ go_daemon(void)
close(nullfd);
nullfd = -1;
}
+
+ if (cap_rights_limit(STDIN_FILENO, CAP_NONE) < 0 && errno != ENOSYS)
+ error("can't limit stdin: %m");
+ if (cap_rights_limit(STDOUT_FILENO, CAP_WRITE) < 0 && errno != ENOSYS)
+ error("can't limit stdout: %m");
+ if (cap_rights_limit(STDERR_FILENO, CAP_WRITE) < 0 && errno != ENOSYS)
+ error("can't limit stderr: %m");
}
int
@@ -2494,19 +2533,19 @@ check_classless_option(unsigned char *data, int len)
i += 4;
continue;
} else if (width < 9) {
- addr = (in_addr_t)(data[i] << 24);
+ addr = (in_addr_t)(data[i] << 24);
i += 1;
} else if (width < 17) {
- addr = (in_addr_t)(data[i] << 24) +
+ addr = (in_addr_t)(data[i] << 24) +
(in_addr_t)(data[i + 1] << 16);
i += 2;
} else if (width < 25) {
- addr = (in_addr_t)(data[i] << 24) +
+ addr = (in_addr_t)(data[i] << 24) +
(in_addr_t)(data[i + 1] << 16) +
(in_addr_t)(data[i + 2] << 8);
i += 3;
} else if (width < 33) {
- addr = (in_addr_t)(data[i] << 24) +
+ addr = (in_addr_t)(data[i] << 24) +
(in_addr_t)(data[i + 1] << 16) +
(in_addr_t)(data[i + 2] << 8) +
data[i + 3];
@@ -2530,7 +2569,7 @@ check_classless_option(unsigned char *data, int len)
addr &= mask;
data[i - 1] = (unsigned char)(
(addr >> (((32 - width)/8)*8)) & 0xFF);
- }
+ }
i += 4;
}
if (i > len) {
@@ -2694,6 +2733,8 @@ fork_privchld(int fd, int fd2)
dup2(nullfd, STDERR_FILENO);
close(nullfd);
close(fd2);
+ close(ifi->rfdesc);
+ ifi->rfdesc = -1;
for (;;) {
pfd[0].fd = fd;
@@ -2705,6 +2746,6 @@ fork_privchld(int fd, int fd2)
if (nfds == 0 || !(pfd[0].revents & POLLIN))
continue;
- dispatch_imsg(fd);
+ dispatch_imsg(ifi, fd);
}
}
diff --git a/sbin/dhclient/dhcpd.h b/sbin/dhclient/dhcpd.h
index 4762cbd..479753e 100644
--- a/sbin/dhclient/dhcpd.h
+++ b/sbin/dhclient/dhcpd.h
@@ -121,6 +121,7 @@ struct client_lease {
struct client_lease *next;
time_t expiry, renewal, rebind;
struct iaddr address;
+ struct iaddr nextserver;
char *server_name;
char *filename;
struct string_list *medium;
@@ -296,11 +297,13 @@ struct hash_table *new_hash_table(int);
struct hash_bucket *new_hash_bucket(void);
/* bpf.c */
-int if_register_bpf(struct interface_info *);
+int if_register_bpf(struct interface_info *, int);
void if_register_send(struct interface_info *);
void if_register_receive(struct interface_info *);
-ssize_t send_packet(struct interface_info *, struct dhcp_packet *, size_t,
- struct in_addr, struct sockaddr_in *, struct hardware *);
+void send_packet_unpriv(int, struct dhcp_packet *, size_t, struct in_addr,
+ struct in_addr);
+struct imsg_hdr;
+void send_packet_priv(struct interface_info *, struct imsg_hdr *, int);
ssize_t receive_packet(struct interface_info *, unsigned char *, size_t,
struct sockaddr_in *, struct hardware *);
@@ -404,20 +407,13 @@ void bootp(struct packet *);
void dhcp(struct packet *);
/* packet.c */
-void assemble_hw_header(struct interface_info *, unsigned char *,
- int *, struct hardware *);
+void assemble_hw_header(struct interface_info *, unsigned char *, int *);
void assemble_udp_ip_header(unsigned char *, int *, u_int32_t, u_int32_t,
unsigned int, unsigned char *, int);
ssize_t decode_hw_header(unsigned char *, int, struct hardware *);
ssize_t decode_udp_ip_header(unsigned char *, int, struct sockaddr_in *,
unsigned char *, int);
-/* ethernet.c */
-void assemble_ethernet_header(struct interface_info *, unsigned char *,
- int *, struct hardware *);
-ssize_t decode_ethernet_header(struct interface_info *, unsigned char *,
- int, struct hardware *);
-
/* clparse.c */
int read_client_conf(void);
void read_client_leases(void);
@@ -441,4 +437,4 @@ struct buf *buf_open(size_t);
int buf_add(struct buf *, void *, size_t);
int buf_close(int, struct buf *);
ssize_t buf_read(int, void *, size_t);
-void dispatch_imsg(int);
+void dispatch_imsg(struct interface_info *, int);
diff --git a/sbin/dhclient/packet.c b/sbin/dhclient/packet.c
index b7a4f21..f79ca2f 100644
--- a/sbin/dhclient/packet.c
+++ b/sbin/dhclient/packet.c
@@ -55,11 +55,6 @@ __FBSDID("$FreeBSD$");
u_int32_t checksum(unsigned char *, unsigned, u_int32_t);
u_int32_t wrapsum(u_int32_t);
-void assemble_ethernet_header(struct interface_info *, unsigned char *,
- int *, struct hardware *);
-ssize_t decode_ethernet_header(struct interface_info *, unsigned char *,
- int bufix, struct hardware *);
-
u_int32_t
checksum(unsigned char *buf, unsigned nbytes, u_int32_t sum)
{
@@ -95,14 +90,11 @@ wrapsum(u_int32_t sum)
void
assemble_hw_header(struct interface_info *interface, unsigned char *buf,
- int *bufix, struct hardware *to)
+ int *bufix)
{
struct ether_header eh;
- if (to != NULL && to->hlen == 6) /* XXX */
- memcpy(eh.ether_dhost, to->haddr, sizeof(eh.ether_dhost));
- else
- memset(eh.ether_dhost, 0xff, sizeof(eh.ether_dhost));
+ memset(eh.ether_dhost, 0xff, sizeof(eh.ether_dhost));
if (interface->hw_address.hlen == sizeof(eh.ether_shost))
memcpy(eh.ether_shost, interface->hw_address.haddr,
sizeof(eh.ether_shost));
diff --git a/sbin/dhclient/privsep.c b/sbin/dhclient/privsep.c
index b42572f..a0521a6 100644
--- a/sbin/dhclient/privsep.c
+++ b/sbin/dhclient/privsep.c
@@ -101,7 +101,7 @@ buf_read(int sock, void *buf, size_t nbytes)
}
void
-dispatch_imsg(int fd)
+dispatch_imsg(struct interface_info *ifi, int fd)
{
struct imsg_hdr hdr;
char *medium, *reason, *filename,
@@ -232,6 +232,9 @@ dispatch_imsg(int fd)
if (buf_close(fd, buf) == -1)
error("buf_close: %m");
break;
+ case IMSG_SEND_PACKET:
+ send_packet_priv(ifi, &hdr, fd);
+ break;
default:
error("received unknown message, code %d", hdr.code);
}
diff --git a/sbin/dhclient/privsep.h b/sbin/dhclient/privsep.h
index f30284e..d464da4 100644
--- a/sbin/dhclient/privsep.h
+++ b/sbin/dhclient/privsep.h
@@ -14,6 +14,8 @@
* WHATSOEVER RESULTING FROM LOSS OF MIND, USE, DATA OR PROFITS, WHETHER IN
* AN ACTION OF CONTRACT, NEGLIGENCE OR OTHER TORTIOUS ACTION, ARISING OUT
* OF OR IN CONNECTION WITH THE USE, ABUSE OR PERFORMANCE OF THIS SOFTWARE.
+ *
+ * $FreeBSD$
*/
#include <sys/types.h>
@@ -33,7 +35,8 @@ enum imsg_code {
IMSG_SCRIPT_INIT,
IMSG_SCRIPT_WRITE_PARAMS,
IMSG_SCRIPT_GO,
- IMSG_SCRIPT_GO_RET
+ IMSG_SCRIPT_GO_RET,
+ IMSG_SEND_PACKET
};
struct imsg_hdr {
diff --git a/sbin/geom/class/part/gpart.8 b/sbin/geom/class/part/gpart.8
index 7386900..3ce79a8 100644
--- a/sbin/geom/class/part/gpart.8
+++ b/sbin/geom/class/part/gpart.8
@@ -24,7 +24,7 @@
.\"
.\" $FreeBSD$
.\"
-.Dd January 25, 2013
+.Dd July 1, 2013
.Dt GPART 8
.Os
.Sh NAME
@@ -645,14 +645,12 @@ The scheme-specific attributes for GPT:
When set, the
.Nm gptboot
stage 1 boot loader will try to boot the system from this partition.
-Multiple partitions might be marked with the
+Multiple partitions can be marked with the
.Cm bootme
attribute.
-In such scenario the
-.Nm gptboot
-will try all
-.Cm bootme
-partitions one by one, until the next boot stage is successfully entered.
+See
+.Xr gptboot 8
+for more details.
.It Cm bootonce
Setting this attribute automatically sets the
.Cm bootme
@@ -660,49 +658,14 @@ attribute.
When set, the
.Nm gptboot
stage 1 boot loader will try to boot the system from this partition only once.
-Partitions with both
-.Cm bootonce
-and
-.Cm bootme
-attributes are tried before partitions with only the
-.Cm bootme
-attribute.
-Before
-.Cm bootonce
-partition is tried, the
-.Nm gptboot
-removes the
-.Cm bootme
-attribute and tries to execute the next boot stage.
-If it fails, the
-.Cm bootonce
-attribute that is now alone is replaced with the
-.Cm bootfailed
-attribute.
-If the execution of the next boot stage succeeds, but the system is not fully
-booted, the
-.Nm gptboot
-will look for
-.Cm bootonce
-attributes alone (without the
-.Cm bootme
-attribute) on the next system boot and will replace those with the
-.Cm bootfailed
-attribute.
-If the system is fully booted, the
-.Pa /etc/rc.d/gptboot
-start-up script will look for partition with the
-.Cm bootonce
-attribute alone, will remove the attribute and log that the system was
-successfully booted from this partition.
-There should be at most one
-.Cm bootonce
-partition when system is successfully booted.
-Multiple partitions might be marked with the
+Multiple partitions can be marked with the
.Cm bootonce
and
.Cm bootme
attribute pairs.
+See
+.Xr gptboot 8
+for more details.
.It Cm bootfailed
This attribute should not be manually managed.
It is managed by the
@@ -710,14 +673,9 @@ It is managed by the
stage 1 boot loader and the
.Pa /etc/rc.d/gptboot
start-up script.
-This attribute is used to mark partitions that had the
-.Cm bootonce
-attribute set, but we failed to boot from them.
-Once we successfully boot, the
-.Pa /etc/rc.d/gptboot
-script will log all the partitions we failed to boot from and will remove the
-.Cm bootfailed
-attributes.
+See
+.Xr gptboot 8
+for more details.
.El
.Pp
The scheme-specific attributes for MBR:
@@ -795,20 +753,17 @@ There are two variants of bootstrap code to write to this partition:
.Pa /boot/gptboot
and
.Pa /boot/gptzfsboot .
+.Pp
.Pa /boot/gptboot
-is used to boot from UFS.
-It searches through
+is used to boot from UFS partitions.
+.Cm gptboot
+searches through
.Cm freebsd-ufs
-partitions in the GPT and boots from the first one with the
+partitions in the GPT and selects one to boot based on the
.Cm bootonce
-attribute set.
-If that attribute is not found,
-.Pa /boot/gptboot
-boots from the first
-.Cm freebsd-ufs
-partition with the
+and
.Cm bootme
-attribute set.
+attributes.
If neither attribute is found,
.Pa /boot/gptboot
boots from the first
@@ -817,6 +772,10 @@ partition.
.Pa /boot/loader
.Pq the third bootstrap stage
is loaded from the first partition that matches these conditions.
+See
+.Xr gptboot 8
+for more information.
+.Pp
.Pa /boot/gptzfsboot
is used to boot from ZFS.
It searches through the GPT for
@@ -1105,7 +1064,8 @@ and
.Xr dd 1 ,
.Xr geom 4 ,
.Xr boot0cfg 8 ,
-.Xr geom 8
+.Xr geom 8 ,
+.Xr gptboot 8
.Sh HISTORY
The
.Nm
diff --git a/sbin/hastctl/hastctl.c b/sbin/hastctl/hastctl.c
index 6fe55fa..11b5b8d 100644
--- a/sbin/hastctl/hastctl.c
+++ b/sbin/hastctl/hastctl.c
@@ -293,6 +293,7 @@ control_set_role(struct nv *nv, const char *newrole)
static int
control_list(struct nv *nv)
{
+ pid_t pid;
unsigned int ii;
const char *str;
int error, ret;
@@ -331,6 +332,9 @@ control_list(struct nv *nv)
str = nv_get_string(nv, "status%u", ii);
if (str != NULL)
printf(" status: %s\n", str);
+ pid = nv_get_int32(nv, "workerpid%u", ii);
+ if (pid != 0)
+ printf(" workerpid: %d\n", pid);
printf(" dirty: %ju (%NB)\n",
(uintmax_t)nv_get_uint64(nv, "dirty%u", ii),
(intmax_t)nv_get_uint64(nv, "dirty%u", ii));
diff --git a/sbin/hastd/control.c b/sbin/hastd/control.c
index 3619fc6..922f507 100644
--- a/sbin/hastd/control.c
+++ b/sbin/hastd/control.c
@@ -271,6 +271,7 @@ control_status(struct hastd_config *cfg, struct nv *nvout,
nv_add_string(nvout, compression_name(res->hr_compression),
"compression%u", no);
nv_add_string(nvout, role2str(res->hr_role), "role%u", no);
+ nv_add_int32(nvout, res->hr_workerpid, "workerpid%u", no);
switch (res->hr_role) {
case HAST_ROLE_PRIMARY:
diff --git a/sbin/hastd/hastd.8 b/sbin/hastd/hastd.8
index b614f36..017e895 100644
--- a/sbin/hastd/hastd.8
+++ b/sbin/hastd/hastd.8
@@ -70,18 +70,18 @@ hastd: <resource name> (<role>)
.Pp
If (and only if)
.Nm
-operates in primary role for the given resource, corresponding
+operates in primary role for the given resource, a corresponding
.Pa /dev/hast/<name>
disk-like device (GEOM provider) is created.
File systems and applications can use this provider to send I/O
requests to.
Every write, delete and flush operation
.Dv ( BIO_WRITE , BIO_DELETE , BIO_FLUSH )
-is send to local component and replicated to the remote (secondary) node if it
-is available.
+is sent to the local component and replicated on the remote (secondary) node
+if it is available.
Read operations
.Dv ( BIO_READ )
-are handled locally unless I/O error occurs or local version of the data
+are handled locally unless an I/O error occurs or the local version of the data
is not up-to-date yet (synchronization is in progress).
.Pp
The
@@ -100,38 +100,38 @@ The connection between two
.Nm
daemons is always initiated from the one running as primary to the one
running as secondary.
-When primary
+When the primary
.Nm
-is unable to connect or connection fails, it will try to re-establish
-connection every few seconds.
-Once connection is established, primary
+is unable to connect or the connection fails, it will try to re-establish
+the connection every few seconds.
+Once the connection is established, the primary
.Nm
will synchronize every extent that was modified during connection outage
to the secondary
.Nm .
.Pp
-It is possible that in case of connection outage between the nodes
+It is possible that in the case of a connection outage between the nodes the
.Nm
primary role for the given resource will be configured on both nodes.
This in turn leads to incompatible data modifications.
-Such condition is called split-brain and cannot be automatically
+Such a condition is called a split-brain and cannot be automatically
resolved by the
.Nm
-daemon as this will lead most likely to data corruption or lost of
+daemon as this will lead most likely to data corruption or loss of
important changes.
Even though it cannot be fixed by
.Nm
-itself, it will be detected and further connection between independently
+itself, it will be detected and a further connection between independently
modified nodes will not be possible.
-Once this situation is manually resolved by an administrator, resource
+Once this situation is manually resolved by an administrator, the resource
on one of the nodes can be initialized (erasing local data), which makes
-connection to the remote node possible again.
-Connection of freshly initialized component will trigger full resource
+a connection to the remote node possible again.
+Connection of the freshly initialized component will trigger full resource
synchronization.
.Pp
-The
+A
.Nm
-daemon itself never picks his role up automatically.
+daemon never picks its role automatically.
The role has to be configured with the
.Xr hastctl 8
control utility by additional software like
@@ -139,7 +139,7 @@ control utility by additional software like
or
.Nm heartbeat
that can reliably manage role separation and switch secondary node to
-primary role in case of original primary failure.
+primary role in case of the primary's failure.
.Pp
The
.Nm
diff --git a/sbin/hastd/refcnt.h b/sbin/hastd/refcnt.h
index 5e3fb34..1246043 100644
--- a/sbin/hastd/refcnt.h
+++ b/sbin/hastd/refcnt.h
@@ -32,24 +32,24 @@
#ifndef __REFCNT_H__
#define __REFCNT_H__
-#include <stdatomic.h>
+#include <machine/atomic.h>
#include "pjdlog.h"
-typedef atomic_uint refcnt_t;
+typedef unsigned int refcnt_t;
static __inline void
refcnt_init(refcnt_t *count, unsigned int v)
{
- atomic_init(count, v);
+ *count = v;
}
static __inline void
refcnt_acquire(refcnt_t *count)
{
- atomic_fetch_add_explicit(count, 1, memory_order_acquire);
+ atomic_add_acq_int(count, 1);
}
static __inline unsigned int
@@ -58,7 +58,7 @@ refcnt_release(refcnt_t *count)
unsigned int old;
/* XXX: Should this have a rel membar? */
- old = atomic_fetch_sub(count, 1);
+ old = atomic_fetchadd_int(count, -1);
PJDLOG_ASSERT(old > 0);
return (old - 1);
}
diff --git a/sbin/ifconfig/af_nd6.c b/sbin/ifconfig/af_nd6.c
index 5c46452..b3db0a8 100644
--- a/sbin/ifconfig/af_nd6.c
+++ b/sbin/ifconfig/af_nd6.c
@@ -148,7 +148,7 @@ nd6_status(int s)
memset(&nd, 0, sizeof(nd));
strncpy(nd.ifname, ifr.ifr_name, sizeof(nd.ifname));
if ((s6 = socket(AF_INET6, SOCK_DGRAM, 0)) < 0) {
- if (errno != EAFNOSUPPORT)
+ if (errno != EAFNOSUPPORT && errno != EPROTONOSUPPORT)
warn("socket(AF_INET6, SOCK_DGRAM)");
return;
}
diff --git a/sbin/mdconfig/mdconfig.8 b/sbin/mdconfig/mdconfig.8
index b3cc110..9e30896 100644
--- a/sbin/mdconfig/mdconfig.8
+++ b/sbin/mdconfig/mdconfig.8
@@ -144,7 +144,7 @@ If both of
and
.Fl f
options are specified,
-display devices which match the two conditions.
+display devices which match the two conditions.
If the
.Fl v
option is specified, show all details.
diff --git a/sbin/mdconfig/mdconfig.c b/sbin/mdconfig/mdconfig.c
index 1a33905..6ba4cd6 100644
--- a/sbin/mdconfig/mdconfig.c
+++ b/sbin/mdconfig/mdconfig.c
@@ -450,7 +450,8 @@ md_list(const char *units, int opt, const char *fflag)
continue;
else
ffound = 1;
- }
+ } else if (fflag != NULL)
+ continue;
if (nflag && strncmp(pp->lg_name, MD_NAME, 2) == 0)
printf("%s", pp->lg_name + 2);
else
diff --git a/sbin/mount/mount.8 b/sbin/mount/mount.8
index 7cd8026..5b670ee 100644
--- a/sbin/mount/mount.8
+++ b/sbin/mount/mount.8
@@ -450,6 +450,7 @@ However, for the following file system types:
.Cm nfs ,
.Cm nullfs ,
.Cm oldnfs ,
+.Cm smbfs ,
.Cm udf ,
and
.Cm unionfs .
@@ -544,6 +545,7 @@ support for a particular file system might be provided either on a static
.Xr mount_msdosfs 8 ,
.Xr mount_nfs 8 ,
.Xr mount_nullfs 8 ,
+.Xr mount_smbfs 8 ,
.Xr mount_udf 8 ,
.Xr mount_unionfs 8 ,
.Xr umount 8 ,
diff --git a/sbin/mount/mount.c b/sbin/mount/mount.c
index a156089..6284822 100644
--- a/sbin/mount/mount.c
+++ b/sbin/mount/mount.c
@@ -143,7 +143,7 @@ use_mountprog(const char *vfstype)
unsigned int i;
const char *fs[] = {
"cd9660", "mfs", "msdosfs", "nfs",
- "nullfs", "oldnfs", "udf", "unionfs",
+ "nullfs", "oldnfs", "smbfs", "udf", "unionfs",
NULL
};
diff --git a/sbin/nvmecontrol/firmware.c b/sbin/nvmecontrol/firmware.c
index f82b886..4678301 100644
--- a/sbin/nvmecontrol/firmware.c
+++ b/sbin/nvmecontrol/firmware.c
@@ -82,7 +82,7 @@ read_image_file(char *path, void **buf, ssize_t *size)
exit(EX_IOERR);
}
if ((*buf = malloc(sb.st_size)) == NULL) {
- fprintf(stderr, "Unable to malloc %zd bytes.\n",
+ fprintf(stderr, "Unable to malloc %jd bytes.\n",
sb.st_size);
close(fd);
exit(EX_IOERR);
@@ -95,7 +95,7 @@ read_image_file(char *path, void **buf, ssize_t *size)
}
if (*size != sb.st_size) {
fprintf(stderr, "Error reading '%s', "
- "read %zd bytes, requested %zd bytes\n",
+ "read %zd bytes, requested %jd bytes\n",
path, *size, sb.st_size);
close(fd);
exit(EX_IOERR);
diff --git a/sbin/reboot/boot_i386.8 b/sbin/reboot/boot_i386.8
index 33ad8fe..e21e53f 100644
--- a/sbin/reboot/boot_i386.8
+++ b/sbin/reboot/boot_i386.8
@@ -36,7 +36,7 @@
.\"
.\" $FreeBSD$
.\"
-.Dd July 4, 2010
+.Dd July 1, 2013
.Dt BOOT 8 i386
.Os
.Sh NAME
@@ -351,6 +351,7 @@ requirement has not been adhered to.
.Xr bsdlabel 8 ,
.Xr btxld 8 ,
.Xr config 8 ,
+.Xr gptboot 8 ,
.Xr halt 8 ,
.Xr loader 8 ,
.Xr nextboot 8 ,
diff --git a/sbin/swapon/swapon.c b/sbin/swapon/swapon.c
index 55beb5b..5dcb18d 100644
--- a/sbin/swapon/swapon.c
+++ b/sbin/swapon/swapon.c
@@ -65,11 +65,11 @@ __FBSDID("$FreeBSD$");
#include <unistd.h>
static void usage(void);
-static const char *swap_on_off(char *, int, char *);
-static const char *swap_on_off_gbde(char *, int);
-static const char *swap_on_off_geli(char *, char *, int);
-static const char *swap_on_off_md(char *, char *, int);
-static const char *swap_on_off_sfile(char *, int);
+static const char *swap_on_off(const char *, int, char *);
+static const char *swap_on_off_gbde(const char *, int);
+static const char *swap_on_off_geli(const char *, char *, int);
+static const char *swap_on_off_md(const char *, char *, int);
+static const char *swap_on_off_sfile(const char *, int);
static void swaplist(int, int, int);
static int run_cmd(int *, const char *, ...) __printflike(2, 3);
@@ -217,215 +217,231 @@ main(int argc, char **argv)
}
static const char *
-swap_on_off(char *name, int doingall, char *mntops)
+swap_on_off(const char *name, int doingall, char *mntops)
{
char base[PATH_MAX];
/* Swap on vnode-backed md(4) device. */
if (mntops != NULL &&
- (fnmatch(_PATH_DEV MD_NAME "[0-9]*", name, 0) != FNM_NOMATCH ||
- fnmatch(MD_NAME "[0-9]*", name, 0) != FNM_NOMATCH ||
+ (fnmatch(_PATH_DEV MD_NAME "[0-9]*", name, 0) == 0 ||
+ fnmatch(MD_NAME "[0-9]*", name, 0) == 0 ||
strncmp(_PATH_DEV MD_NAME, name,
sizeof(_PATH_DEV) + sizeof(MD_NAME)) == 0 ||
strncmp(MD_NAME, name, sizeof(MD_NAME)) == 0))
return (swap_on_off_md(name, mntops, doingall));
- /* Swap on encrypted device by GEOM_BDE. */
basename_r(name, base);
- if (fnmatch("*.bde", base, 0) != FNM_NOMATCH)
+
+ /* Swap on encrypted device by GEOM_BDE. */
+ if (fnmatch("*.bde", base, 0) == 0)
return (swap_on_off_gbde(name, doingall));
/* Swap on encrypted device by GEOM_ELI. */
- if (fnmatch("*.eli", base, 0) != FNM_NOMATCH)
+ if (fnmatch("*.eli", base, 0) == 0)
return (swap_on_off_geli(name, mntops, doingall));
/* Swap on special file. */
return (swap_on_off_sfile(name, doingall));
}
+/* Strip off .bde or .eli suffix from swap device name */
+static char *
+swap_basename(const char *name)
+{
+ char *dname, *p;
+
+ dname = strdup(name);
+ p = strrchr(dname, '.');
+ /* assert(p != NULL); */
+ *p = '\0';
+
+ return (dname);
+}
+
static const char *
-swap_on_off_gbde(char *name, int doingall)
+swap_on_off_gbde(const char *name, int doingall)
{
const char *ret;
char pass[64 * 2 + 1], bpass[64];
- char *devname, *p;
- int i, fd, error;
+ char *dname;
+ int i, error;
- devname = strdup(name);
- p = strrchr(devname, '.');
- if (p == NULL) {
- warnx("%s: Malformed device name", name);
+ dname = swap_basename(name);
+ if (dname == NULL)
return (NULL);
- }
- *p = '\0';
- fd = -1;
- switch (which_prog) {
- case SWAPON:
+ if (which_prog == SWAPON) {
arc4random_buf(bpass, sizeof(bpass));
for (i = 0; i < (int)sizeof(bpass); i++)
sprintf(&pass[2 * i], "%02x", bpass[i]);
pass[sizeof(pass) - 1] = '\0';
- error = run_cmd(&fd, "%s init %s -P %s", _PATH_GBDE,
- devname, pass);
+ error = run_cmd(NULL, "%s init %s -P %s", _PATH_GBDE,
+ dname, pass);
if (error) {
/* bde device found. Ignore it. */
- close(fd);
+ free(dname);
if (!qflag)
warnx("%s: Device already in use", name);
return (NULL);
}
- close(fd);
- error = run_cmd(&fd, "%s attach %s -p %s", _PATH_GBDE,
- devname, pass);
+ error = run_cmd(NULL, "%s attach %s -p %s", _PATH_GBDE,
+ dname, pass);
+ free(dname);
if (error) {
- close(fd);
warnx("gbde (attach) error: %s", name);
return (NULL);
}
- break;
- case SWAPOFF:
- break;
- default:
- return (NULL);
- break;
}
- if (fd != -1)
- close(fd);
+
ret = swap_on_off_sfile(name, doingall);
- fd = -1;
- switch (which_prog) {
- case SWAPOFF:
- error = run_cmd(&fd, "%s detach %s", _PATH_GBDE, devname);
+ if (which_prog == SWAPOFF) {
+ error = run_cmd(NULL, "%s detach %s", _PATH_GBDE, dname);
+ free(dname);
if (error) {
/* bde device not found. Ignore it. */
if (!qflag)
- warnx("%s: Device not found", devname);
+ warnx("%s: Device not found", name);
return (NULL);
}
- break;
- default:
- return (NULL);
- break;
}
- if (fd != -1)
- close(fd);
return (ret);
}
-static const char *
-swap_on_off_geli(char *name, char *mntops, int doingall)
+/* Build geli(8) arguments from mntops */
+static char *
+swap_on_geli_args(const char *mntops)
{
- const char *ops, *aalgo, *ealgo, *keylen_str, *sectorsize_str;
- char *devname, *p;
- char args[4096];
- struct stat sb;
- int fd, error, keylen, sectorsize;
+ const char *aalgo, *ealgo, *keylen_str, *sectorsize_str;
+ const char *aflag, *eflag, *lflag, *sflag;
+ char *p;
+ char *args;
+ char *token, *string, *ops;
+ int argsize, pagesize;
+ size_t pagesize_len;
u_long ul;
- devname = strdup(name);
- p = strrchr(devname, '.');
- if (p == NULL) {
- warnx("%s: Malformed device name", name);
- return (NULL);
+ /* Use built-in defaults for geli(8) */
+ aalgo = ealgo = keylen_str = "";
+ aflag = eflag = lflag = "";
+
+ /* We will always specify sectorsize */
+ sflag = " -s ";
+ sectorsize_str = NULL;
+
+ if (mntops != NULL) {
+ string = ops = strdup(mntops);
+
+ while ((token = strsep(&string, ",")) != NULL) {
+ if ((p = strstr(token, "aalgo=")) == token) {
+ aalgo = p + sizeof("aalgo=") - 1;
+ aflag = " -a ";
+ } else if ((p = strstr(token, "ealgo=")) == token) {
+ ealgo = p + sizeof("ealgo=") - 1;
+ eflag = " -e ";
+ } else if ((p = strstr(token, "keylen=")) == token) {
+ keylen_str = p + sizeof("keylen=") - 1;
+ errno = 0;
+ ul = strtoul(keylen_str, &p, 10);
+ if (errno == 0) {
+ if (*p != '\0' || ul > INT_MAX)
+ errno = EINVAL;
+ }
+ if (errno) {
+ warn("Invalid keylen: %s", keylen_str);
+ free(ops);
+ return (NULL);
+ }
+ lflag = " -l ";
+ } else if ((p = strstr(token, "sectorsize=")) == token) {
+ sectorsize_str = p + sizeof("sectorsize=") - 1;
+ errno = 0;
+ ul = strtoul(sectorsize_str, &p, 10);
+ if (errno == 0) {
+ if (*p != '\0' || ul > INT_MAX)
+ errno = EINVAL;
+ }
+ if (errno) {
+ warn("Invalid sectorsize: %s", sectorsize_str);
+ free(ops);
+ return (NULL);
+ }
+ } else if (strcmp(token, "sw") != 0) {
+ warnx("Invalid option: %s", token);
+ free(ops);
+ return (NULL);
+ }
+ }
+ } else
+ ops = NULL;
+
+ /*
+ * If we do not have a sector size at this point, fill in
+ * pagesize as sector size.
+ */
+ if (sectorsize_str == NULL) {
+ /* Use pagesize as default sectorsize */
+ pagesize = getpagesize();
+ pagesize_len = snprintf(NULL, 0, "%d", pagesize) + 1;
+ p = alloca(pagesize_len);
+ snprintf(p, pagesize_len, "%d", pagesize);
+ sectorsize_str = p;
}
- *p = '\0';
- ops = strdup(mntops);
+ argsize = asprintf(&args, "%s%s%s%s%s%s%s%s -d",
+ aflag, aalgo, eflag, ealgo, lflag, keylen_str,
+ sflag, sectorsize_str);
- /* Default parameters for geli(8). */
- aalgo = "hmac/sha256";
- ealgo = "aes";
- keylen = 256;
- sectorsize = 4096;
+ free(ops);
+ return (args);
+}
- if ((p = strstr(ops, "aalgo=")) != NULL) {
- aalgo = p + sizeof("aalgo=") - 1;
- p = strchr(aalgo, ',');
- if (p != NULL)
- *p = '\0';
- }
- if ((p = strstr(ops, "ealgo=")) != NULL) {
- ealgo = p + sizeof("ealgo=") - 1;
- p = strchr(ealgo, ',');
- if (p != NULL)
- *p = '\0';
- }
- if ((p = strstr(ops, "keylen=")) != NULL) {
- keylen_str = p + sizeof("keylen=") - 1;
- p = strchr(keylen_str, ',');
- if (p != NULL)
- *p = '\0';
- errno = 0;
- ul = strtoul(keylen_str, &p, 10);
- if (errno == 0) {
- if (*p != '\0' || ul > INT_MAX)
- errno = EINVAL;
- }
- if (errno) {
- warn("Invalid keylen: %s", keylen_str);
+static const char *
+swap_on_off_geli(const char *name, char *mntops, int doingall)
+{
+ char *dname;
+ char *args;
+ struct stat sb;
+ int error;
+
+ error = stat(name, &sb);
+
+ if (which_prog == SWAPON) do {
+ /* Skip if the .eli device already exists */
+ if (error == 0)
+ break;
+
+ args = swap_on_geli_args(mntops);
+ if (args == NULL)
return (NULL);
- }
- keylen = (int)ul;
- }
- if ((p = strstr(ops, "sectorsize=")) != NULL) {
- sectorsize_str = p + sizeof("sectorsize=") - 1;
- p = strchr(sectorsize_str, ',');
- if (p != NULL)
- *p = '\0';
- errno = 0;
- ul = strtoul(sectorsize_str, &p, 10);
- if (errno == 0) {
- if (*p != '\0' || ul > INT_MAX)
- errno = EINVAL;
- }
- if (errno) {
- warn("Invalid sectorsize: %s", sectorsize_str);
+
+ dname = swap_basename(name);
+ if (dname == NULL) {
+ free(args);
return (NULL);
}
- sectorsize = (int)ul;
- }
- snprintf(args, sizeof(args), "-a %s -e %s -l %d -s %d -d",
- aalgo, ealgo, keylen, sectorsize);
- args[sizeof(args) - 1] = '\0';
- free((void *)ops);
- fd = -1;
- switch (which_prog) {
- case SWAPON:
- error = run_cmd(&fd, "%s onetime %s %s", _PATH_GELI, args,
- devname);
+ error = run_cmd(NULL, "%s onetime%s %s", _PATH_GELI, args,
+ dname);
+
+ free(dname);
+ free(args);
+
if (error) {
- /* eli device found. Ignore it. */
- close(fd);
- if (!qflag)
- warnx("%s: Device already in use "
- "or invalid parameters", name);
- return (NULL);
- }
- break;
- case SWAPOFF:
- if (stat(name, &sb) == -1 && errno == ENOENT) {
+ /* error occured during creation */
if (!qflag)
- warnx("%s: Device not found", name);
+ warnx("%s: Invalid parameters", name);
return (NULL);
}
- break;
- default:
- return (NULL);
- break;
- }
- if (fd != -1)
- close(fd);
+ } while (0);
return (swap_on_off_sfile(name, doingall));
}
static const char *
-swap_on_off_md(char *name, char *mntops, int doingall)
+swap_on_off_md(const char *name, char *mntops, int doingall)
{
FILE *sfd;
int fd, mdunit, error;
@@ -465,8 +481,7 @@ swap_on_off_md(char *name, char *mntops, int doingall)
return (NULL);
}
- switch (which_prog) {
- case SWAPON:
+ if (which_prog == SWAPON) {
if (mdunit == -1) {
error = run_cmd(&fd, "%s -l -n -f %s",
_PATH_MDCONFIG, vnodefile);
@@ -476,6 +491,7 @@ swap_on_off_md(char *name, char *mntops, int doingall)
if (!qflag)
warnx("%s: Device already in use",
vnodefile);
+ free(vnodefile);
return (NULL);
}
error = run_cmd(&fd, "%s -a -t vnode -n -f %s",
@@ -483,6 +499,7 @@ swap_on_off_md(char *name, char *mntops, int doingall)
if (error) {
warnx("mdconfig (attach) error: file=%s",
vnodefile);
+ free(vnodefile);
return (NULL);
}
sfd = fdopen(fd, "r");
@@ -522,6 +539,7 @@ swap_on_off_md(char *name, char *mntops, int doingall)
if (!qflag)
warnx("md%d on %s: Device already "
"in use", mdunit, vnodefile);
+ free(vnodefile);
return (NULL);
}
error = run_cmd(NULL, "%s -a -t vnode -u %d -f %s",
@@ -529,11 +547,11 @@ swap_on_off_md(char *name, char *mntops, int doingall)
if (error) {
warnx("mdconfig (attach) error: "
"md%d on file=%s", mdunit, vnodefile);
+ free(vnodefile);
return (NULL);
}
}
- break;
- case SWAPOFF:
+ } else /* SWAPOFF */ {
if (mdunit == -1) {
error = run_cmd(&fd, "%s -l -n -f %s",
_PATH_MDCONFIG, vnodefile);
@@ -543,6 +561,7 @@ swap_on_off_md(char *name, char *mntops, int doingall)
if (!qflag)
warnx("md on %s: Device not found",
vnodefile);
+ free(vnodefile);
return (NULL);
}
sfd = fdopen(fd, "r");
@@ -585,20 +604,17 @@ swap_on_off_md(char *name, char *mntops, int doingall)
if (!qflag)
warnx("md%d on %s: Device not found",
mdunit, vnodefile);
+ free(vnodefile);
return (NULL);
}
}
- break;
- default:
- return (NULL);
}
snprintf(mdpath, sizeof(mdpath), "%s%s%d", _PATH_DEV,
MD_NAME, mdunit);
mdpath[sizeof(mdpath) - 1] = '\0';
ret = swap_on_off_sfile(mdpath, doingall);
- switch (which_prog) {
- case SWAPOFF:
+ if (which_prog == SWAPOFF) {
if (ret != NULL) {
error = run_cmd(NULL, "%s -d -u %d",
_PATH_MDCONFIG, mdunit);
@@ -606,15 +622,13 @@ swap_on_off_md(char *name, char *mntops, int doingall)
warn("mdconfig (detach) detach failed: %s%s%d",
_PATH_DEV, MD_NAME, mdunit);
}
- break;
- default:
- break;
}
err:
if (sfd != NULL)
fclose(sfd);
if (fd != -1)
close(fd);
+ free(vnodefile);
return (ret);
}
@@ -693,21 +707,15 @@ run_cmd(int *ofd, const char *cmdline, ...)
}
static const char *
-swap_on_off_sfile(char *name, int doingall)
+swap_on_off_sfile(const char *name, int doingall)
{
int error;
- switch (which_prog) {
- case SWAPON:
+ if (which_prog == SWAPON)
error = swapon(name);
- break;
- case SWAPOFF:
+ else /* SWAPOFF */
error = swapoff(name);
- break;
- default:
- error = 0;
- break;
- }
+
if (error == -1) {
switch (errno) {
case EBUSY:
diff --git a/share/examples/Makefile b/share/examples/Makefile
index 90bef45..04f53d6 100644
--- a/share/examples/Makefile
+++ b/share/examples/Makefile
@@ -240,6 +240,7 @@ etc-examples:
.endif
.if ${SHARED} != "symlinks"
+SUBDIR= smbfs
.if ${MK_ATF} != "no"
SUBDIR+=atf
.endif
diff --git a/share/examples/etc/README.examples b/share/examples/etc/README.examples
index d703edb..f7bf4ce 100644
--- a/share/examples/etc/README.examples
+++ b/share/examples/etc/README.examples
@@ -40,6 +40,7 @@ netstart - network startup script run from /etc/rc
network.subr - routines for network configuration scripts
networks - see networks(5)
newsyslog.conf - configuration for system log file rotator newsyslog(8)
+nsmb.conf - smbfs lookups configuration file
opieaccess - OPIE database of trusted networks
pf.conf - pf(4) example configuration file
pf.os - SYN fingerprint database
diff --git a/share/examples/etc/make.conf b/share/examples/etc/make.conf
index 1a1c43c..19be713 100644
--- a/share/examples/etc/make.conf
+++ b/share/examples/etc/make.conf
@@ -38,8 +38,8 @@
# pentium3m, pentium3, pentium-m, pentium2,
# pentiumpro, pentium-mmx, pentium, i486
# (VIA CPUs) c7, c3-2, c3
-# AMD64 architecture: opteron-sse3, athlon64-sse3, k8-sse3, opteron,
-# athlon64, k8, core2, nocona
+# AMD64 architecture: amdfam10, opteron-sse3, athlon64-sse3, k8-sse3,
+# opteron, athlon64, k8, core2, nocona
# Intel ia64 architecture: itanium2, itanium
# SPARC-V9 architecture: v9 (generic 64-bit V9), ultrasparc (default
# if omitted), ultrasparc3
diff --git a/share/man/man4/Makefile b/share/man/man4/Makefile
index 523d4a9..e7f5f4c 100644
--- a/share/man/man4/Makefile
+++ b/share/man/man4/Makefile
@@ -81,6 +81,7 @@ MAN= aac.4 \
cardbus.4 \
carp.4 \
cas.4 \
+ cc_cdg.4 \
cc_chd.4 \
cc_cubic.4 \
cc_hd.4 \
diff --git a/share/man/man4/cc_cdg.4 b/share/man/man4/cc_cdg.4
new file mode 100644
index 0000000..c111bd4
--- /dev/null
+++ b/share/man/man4/cc_cdg.4
@@ -0,0 +1,155 @@
+.\"
+.\" Copyright (c) 2013 Swinburne University of Technology, Melbourne, Australia
+.\" All rights reserved.
+.\"
+.\" Redistribution and use in source and binary forms, with or without
+.\" modification, are permitted provided that the following conditions
+.\" are met:
+.\" 1. Redistributions of source code must retain the above copyright
+.\" notice, this list of conditions and the following disclaimer.
+.\" 2. Redistributions in binary form must reproduce the above copyright
+.\" notice, this list of conditions and the following disclaimer in the
+.\" documentation and/or other materials provided with the distribution.
+.\"
+.\" THIS SOFTWARE IS PROVIDED BY THE AUTHOR AND CONTRIBUTORS ``AS IS'' AND
+.\" ANY EXPRESS OR IMPLIED WARRANTIES, INCLUDING, BUT NOT LIMITED TO, THE
+.\" IMPLIED WARRANTIES OF MERCHANTABILITY AND FITNESS FOR A PARTICULAR PURPOSE
+.\" ARE DISCLAIMED. IN NO EVENT SHALL THE AUTHOR OR CONTRIBUTORS BE LIABLE FOR
+.\" ANY DIRECT, INDIRECT, INCIDENTAL, SPECIAL, EXEMPLARY, OR CONSEQUENTIAL
+.\" DAMAGES (INCLUDING, BUT NOT LIMITED TO, PROCUREMENT OF SUBSTITUTE GOODS
+.\" OR SERVICES; LOSS OF USE, DATA, OR PROFITS; OR BUSINESS INTERRUPTION)
+.\" HOWEVER CAUSED AND ON ANY THEORY OF LIABILITY, WHETHER IN CONTRACT, STRICT
+.\" LIABILITY, OR TORT (INCLUDING NEGLIGENCE OR OTHERWISE) ARISING IN ANY WAY
+.\" OUT OF THE USE OF THIS SOFTWARE, EVEN IF ADVISED OF THE POSSIBILITY OF
+.\" SUCH DAMAGE.
+.\"
+.\" $FreeBSD$
+.\"
+.Dd July 2, 2013
+.Dt CC_CDG 4
+.Os
+.Sh NAME
+.Nm cc_cdg
+.Nd CDG Congestion Control Algorithm
+.Sh DESCRIPTION
+CAIA-Delay Gradient (CDG) is a hybrid congestion control algorithm which reacts
+to both packet loss and inferred queuing delay.
+It attempts to operate as a delay-based algorithm where possible, but utilises
+heuristics to detect loss-based TCP cross traffic and will compete effectively
+as required.
+CDG is therefore incrementally deployable and suitable for use on shared
+networks.
+.Pp
+During delay-based operation, CDG uses a delay-gradient based probabilistic
+backoff mechanism, and will also try to infer non congestion related
+packet losses and avoid backing off when they occur.
+During loss-based operation, CDG essentially reverts to
+.Xr cc_newreno 4 Ns - Ns like
+behaviour.
+.Pp
+CDG switches to loss-based operation when it detects that a configurable number
+of consecutive delay-based backoffs have had no measurable effect.
+It periodically attempts to return to delay-based operation, but will keep
+switching back to loss-based operation as required.
+.Sh MIB Variables
+The algorithm exposes the following variables in the
+.Va net.inet.tcp.cc.cdg
+branch of the
+.Xr sysctl 3
+MIB:
+.Bl -tag -width ".Va exp_backoff_scale"
+.It Va version
+Current algorithm/implementation version number.
+.It Va beta_delay
+Delay-based window decrease factor as a percentage (on delay-based backoff, w =
+w * beta_delay / 100).
+Default is 70.
+.It Va beta_loss
+Loss-based window decrease factor as a percentage (on loss-based backoff, w =
+w * beta_loss / 100).
+Default is 50.
+.It Va exp_backoff_scale
+Scaling parameter for the probabilistic exponential backoff.
+Default is 2.
+.It Va smoothing_factor
+Number of samples used for moving average smoothing (0 means no smoothing).
+Default is 8.
+.It Va loss_compete_consec_cong
+Number of consecutive delay-gradient based congestion episodes which will
+trigger loss-based CC compatibility.
+Default is 5.
+.It Va loss_compete_hold_backoff
+Number of consecutive delay-gradient based congestion episodes to hold the
+window backoff for loss-based CC compatibility.
+Default is 5.
+.It Va alpha_inc
+If non-zero, this enables an experimental mode where CDG's window increase
+factor (alpha) is increased by 1 MSS every
+.Va alpha_inc
+RTTs during congestion avoidance mode.
+(Setting
+.Va alpha_inc
+to 1 results in the most aggressive growth of the window increase factor over
+time.
+Use higher
+.Va alpha_inc
+values for slower growth.)
+Default is 0.
+.El
+.Sh SEE ALSO
+.Xr cc_chd 4 ,
+.Xr cc_cubic 4 ,
+.Xr cc_hd 4 ,
+.Xr cc_htcp 4 ,
+.Xr cc_newreno 4 ,
+.Xr cc_vegas 4 ,
+.Xr h_ertt 4 ,
+.Xr mod_cc 4 ,
+.Xr tcp 4 ,
+.Xr khelp 9 ,
+.Xr mod_cc 9
+.Rs
+.%A "D. A. Hayes"
+.%A "G. Armitage"
+.%T "Revisiting TCP Congestion Control using Delay Gradients"
+.%J "Networking 2011 Proceedings, Part II"
+.%D "May 2011"
+.%P "328-341"
+.Re
+.Rs
+.%A "N. Khademi"
+.%A "G. Armitage"
+.%T "Minimising RTT across homogeneous 802.11 WLANs with CAIA Delay-Gradient TCP (v0.1)"
+.%R "CAIA Technical Report 121113A"
+.%D "November 2012"
+.%U "http://caia.swin.edu.au/reports/121113A/CAIA-TR-121113A.pdf"
+.Re
+.Sh ACKNOWLEDGEMENTS
+Development and testing of this software were made possible in part by grants
+from the FreeBSD Foundation and The Cisco University Research Program Fund, a
+corporate advised fund of Silicon Valley Community Foundation.
+.Sh HISTORY
+The
+.Nm
+congestion control module first appeared in
+.Fx 9.2 .
+.Pp
+The module was first released in 2011 by David Hayes whilst working on the
+NewTCP research project at Swinburne University of Technology's Centre for
+Advanced Internet Architectures, Melbourne, Australia.
+More details are available at:
+.Pp
+http://caia.swin.edu.au/urp/newtcp/
+.Sh AUTHORS
+.An -nosplit
+The
+.Nm
+congestion control module was written by
+.An David Hayes Aq david.hayes@ieee.org .
+This manual page was written by
+.An Lawrence Stewart Aq lstewart@FreeBSD.org
+and
+.An Grenville Armitage Aq garmitage@swin.edu.au .
+.Sh BUGS
+The underlying algorithm and parameter values are still a work in progress and
+may not be optimal for some network scenarios.
diff --git a/share/man/man4/oce.4 b/share/man/man4/oce.4
index 78043ff..ee3b33b 100644
--- a/share/man/man4/oce.4
+++ b/share/man/man4/oce.4
@@ -91,7 +91,7 @@ Firmware can be updated by following the steps below:
.It
Copy the below code to a Makefile:
.Bd -literal -offset indent
-\&.KMOD=elxflash
+KMOD=elxflash
FIRMWS=imagename.ufi:elxflash
\&.include <bsd.kmod.mk>
.Ed
diff --git a/share/man/man4/virtio.4 b/share/man/man4/virtio.4
index 6856f69..10ebf20 100644
--- a/share/man/man4/virtio.4
+++ b/share/man/man4/virtio.4
@@ -53,10 +53,10 @@ This emulation is often inefficient.
.Pp
VirtIO defines an interface for efficient I/O between the hypervisor and VM.
The
-.Xr virtio 4
+.Nm
module provides a shared memory transport called a virtqueue.
The
-.Xr virtio_pci 4
+.Xr virtio_pci
device driver represents an emulated PCI device that the hypervisor makes
available to the VM.
This device provides the probing, configuration, and
@@ -94,4 +94,4 @@ Support for VirtIO first appeared in
.An -nosplit
.Fx
support for VirtIO was first added by
-.An Bryan Venteicher Aq bryanv@daemoninthecloset.org .
+.An Bryan Venteicher Aq bryanv@FreeBSD.org .
diff --git a/share/man/man4/virtio_balloon.4 b/share/man/man4/virtio_balloon.4
index 96e8c73..450b5cd 100644
--- a/share/man/man4/virtio_balloon.4
+++ b/share/man/man4/virtio_balloon.4
@@ -59,6 +59,6 @@ The hypervisor can later signal the balloon to return the memory.
The
.Nm
driver was written by
-.An Bryan Venteicher Aq bryanv@daemoninthecloset.org .
+.An Bryan Venteicher Aq bryanv@FreeBSD.org .
It first appeared in
.Fx 9.0 .
diff --git a/share/man/man4/virtio_blk.4 b/share/man/man4/virtio_blk.4
index 7be4425..eb5fded 100644
--- a/share/man/man4/virtio_blk.4
+++ b/share/man/man4/virtio_blk.4
@@ -24,7 +24,7 @@
.\"
.\" $FreeBSD$
.\"
-.Dd January 22, 2012
+.Dd July 2, 2013
.Dt VIRTIO_BLK 4
.Os
.Sh NAME
@@ -53,11 +53,33 @@ Tunables can be set at the
.Xr loader 8
prompt before booting the kernel or stored in
.Xr loader.conf 5 .
-.Bl -tag -width "xxxxxx"
+.Bl -tag -width indent
.It Va hw.vtblk.no_ident
-This tunable disables retrieving the device identification string
-from the hypervisor.
+.It Va hw.vtblk. Ns Ar X Ns Va .no_ident
+.Pp
+These tunables disable retrieving the device identification string
+from the hypervisor either globally or per-device.
The default value is 0.
+.It Va hw.vtblk.writecache_mode
+.It Va hw.vtblk. Ns Ar X Ns Va .writecache_mode
+.Pp
+These tunables determine the write cache mode globally or per-device.
+The mode can changed only if the ConfigWCE feature is negotiated.
+Set to 0 for writethrough mode, 1 for writeback mode, and -1 to leave
+it as-is.
+The default value is to leave as-is.
+.El
+.Sh SYSCTL VARIABLES
+The following variables are available as
+.Xr sysctl 8
+variables.
+.Bl -tag -width indent
+.It Va dev.vtblk. Ns Ar X Ns Va .writecache_mode
+.Pp
+The write cache mode of the device can be either writethrough (0) or
+writeback (1).
+If the ConfigWCE feature is negotiated, the write cache mode can
+be toggled between writethrough and writeback.
.El
.Sh SEE ALSO
.Xr virtio 4
@@ -65,6 +87,6 @@ The default value is 0.
The
.Nm
driver was written by
-.An Bryan Venteicher Aq bryanv@daemoninthecloset.org .
+.An Bryan Venteicher Aq bryanv@FreeBSD.org .
It first appeared in
.Fx 9.0 .
diff --git a/share/man/man4/virtio_scsi.4 b/share/man/man4/virtio_scsi.4
index a2d30d4..b136de6 100644
--- a/share/man/man4/virtio_scsi.4
+++ b/share/man/man4/virtio_scsi.4
@@ -87,6 +87,6 @@ Enable tracing prints.
The
.Nm
driver was written by
-.An Bryan Venteicher Aq bryanv@daemoninthecloset.org .
+.An Bryan Venteicher Aq bryanv@FreeBSD.org .
It first appeared in
.Fx 10.0 .
diff --git a/share/man/man4/vtnet.4 b/share/man/man4/vtnet.4
index febb0ac..8d4d202 100644
--- a/share/man/man4/vtnet.4
+++ b/share/man/man4/vtnet.4
@@ -89,7 +89,7 @@ The default value is 0.
The
.Nm
driver was written by
-.An Bryan Venteicher Aq bryanv@daemoninthecloset.org .
+.An Bryan Venteicher Aq bryanv@FreeBSD.org .
It first appeared in
.Fx 9.0 .
.Sh CAVEATS
diff --git a/share/man/man5/fstab.5 b/share/man/man5/fstab.5
index 7daacf2..27b2ec7 100644
--- a/share/man/man5/fstab.5
+++ b/share/man/man5/fstab.5
@@ -231,7 +231,7 @@ is an
.Xr md 4
device file
.Pq Do md Dc or Do md[0-9]* Dc
-and
+and
.Dq file
is specified in
.Fa fs_mntopts ,
diff --git a/share/man/man5/src.conf.5 b/share/man/man5/src.conf.5
index a7759c3f..3547748 100644
--- a/share/man/man5/src.conf.5
+++ b/share/man/man5/src.conf.5
@@ -1,7 +1,7 @@
.\" DO NOT EDIT-- this file is automatically generated.
.\" from FreeBSD: head/tools/build/options/makeman 251685 2013-06-13 13:05:08Z emaste
.\" $FreeBSD$
-.Dd June 16, 2013
+.Dd July 3, 2013
.Dt SRC.CONF 5
.Os
.Sh NAME
@@ -1055,6 +1055,17 @@ Set to not build kernel modules that include sourceless microcode.
.It Va WITHOUT_SSP
.\" from FreeBSD: head/tools/build/options/WITHOUT_SSP 180012 2008-06-25 21:33:28Z ru
Set to not build world with propolice stack smashing protection.
+.It Va WITH_SVN
+.\" from FreeBSD: head/tools/build/options/WITH_SVN 252561 2013-07-03 12:36:47Z zeising
+Set to install
+.Xr svnlite 1
+as
+.Xr svn 1 .
+.It Va WITHOUT_SVNLITE
+.\" from FreeBSD: head/tools/build/options/WITHOUT_SVNLITE 252561 2013-07-03 12:36:47Z zeising
+Set to not build
+.Xr svnlite 1
+and related programs.
.It Va WITHOUT_SYMVER
.\" from FreeBSD: head/tools/build/options/WITHOUT_SYMVER 169649 2007-05-17 05:03:24Z deischen
Set to disable symbol versioning when building shared libraries.
diff --git a/share/man/man9/locking.9 b/share/man/man9/locking.9
index 046ce10..f8ac5f2 100644
--- a/share/man/man9/locking.9
+++ b/share/man/man9/locking.9
@@ -24,7 +24,7 @@
.\"
.\" $FreeBSD$
.\"
-.Dd May 22, 2013
+.Dd June 30, 2013
.Dt LOCKING 9
.Os
.Sh NAME
@@ -33,53 +33,51 @@
.Sh DESCRIPTION
The
.Em FreeBSD
-kernel is written to run across multiple CPUs and as such requires
-several different synchronization primitives to allow the developers
-to safely access and manipulate the many data types required.
+kernel is written to run across multiple CPUs and as such provides
+several different synchronization primitives to allow developers
+to safely access and manipulate many data types.
.Ss Mutexes
-Mutexes (also erroneously called "sleep mutexes") are the most commonly used
+Mutexes (also called "blocking mutexes") are the most commonly used
synchronization primitive in the kernel.
A thread acquires (locks) a mutex before accessing data shared with other
threads (including interrupt threads), and releases (unlocks) it afterwards.
If the mutex cannot be acquired, the thread requesting it will wait.
-Mutexes are by default adaptive, meaning that
+Mutexes are adaptive by default, meaning that
if the owner of a contended mutex is currently running on another CPU,
-then a thread attempting to acquire the mutex will briefly spin
-in the hope that the owner is only briefly holding it,
-and might release it shortly.
-If the owner does not do so, the waiting thread proceeds to yield the processor,
-allowing other threads to run.
-If the owner is not currently actually running then the spin step is skipped.
+then a thread attempting to acquire the mutex will spin rather than yielding
+the processor.
Mutexes fully support priority propagation.
.Pp
See
.Xr mutex 9
for details.
-.Ss Spin mutexes
-Spin mutexes are variation of basic mutexes; the main difference between
-the two is that spin mutexes never yield the processor - instead, they spin,
-waiting for the thread holding the lock,
-(which must be running on another CPU), to release it.
-Spin mutexes disable interrupts while the held so as to not get pre-empted.
-Since disabling interrupts is expensive, they are also generally slower.
-Spin mutexes should be used only when necessary, e.g. to protect data shared
+.Ss Spin Mutexes
+Spin mutexes are a variation of basic mutexes; the main difference between
+the two is that spin mutexes never block.
+Instead, they spin while waiting for the lock to be released.
+To avoid deadlock, a thread that holds a spin mutex must never yield its CPU.
+Unlike ordinary mutexes, spin mutexes disable interrupts when acquired.
+Since disabling interrupts can be expensive, they are generally slower to
+acquire and release.
+Spin mutexes should be used only when absolutely necessary,
+e.g. to protect data shared
with interrupt filter code (see
.Xr bus_setup_intr 9
-for details).
-.Ss Pool mutexes
-With most synchronization primitives, such as mutexes, programmer must
-provide a piece of allocated memory to hold the primitive.
+for details),
+or for scheduler internals.
+.Ss Mutex Pools
+With most synchronization primitives, such as mutexes, the programmer must
+provide memory to hold the primitive.
For example, a mutex may be embedded inside the structure it protects.
-Pool mutex is a variant of mutex without this requirement - to lock or unlock
-a pool mutex, one uses address of the structure being protected with it,
-not the mutex itself.
-Pool mutexes are seldom used.
+Mutex pools provide a preallocated set of mutexes to avoid this
+requirement.
+Note that mutexes from a pool may only be used as leaf locks.
.Pp
See
.Xr mtx_pool 9
for details.
-.Ss Reader/writer locks
-Reader/writer locks allow shared access to protected data by multiple threads,
+.Ss Reader/Writer Locks
+Reader/writer locks allow shared access to protected data by multiple threads
or exclusive access by a single thread.
The threads with shared access are known as
.Em readers
@@ -91,26 +89,16 @@ since it may modify protected data.
Reader/writer locks can be treated as mutexes (see above and
.Xr mutex 9 )
with shared/exclusive semantics.
-More specifically, regular mutexes can be
-considered to be equivalent to a write-lock on an
-.Em rw_lock.
-The
-.Em rw_lock
-locks have priority propagation like mutexes, but priority
-can be propagated only to an exclusive holder.
+Reader/writer locks support priority propagation like mutexes,
+but priority is propagated only to an exclusive holder.
This limitation comes from the fact that shared owners
are anonymous.
-Another important property is that shared holders of
-.Em rw_lock
-can recurse, but exclusive locks are not allowed to recurse.
-This ability should not be used lightly and
-.Em may go away.
.Pp
See
.Xr rwlock 9
for details.
-.Ss Read-mostly locks
-Mostly reader locks are similar to
+.Ss Read-Mostly Locks
+Read-mostly locks are similar to
.Em reader/writer
locks but optimized for very infrequent write locking.
.Em Read-mostly
@@ -122,21 +110,41 @@ data structure.
See
.Xr rmlock 9
for details.
+.Ss Sleepable Read-Mostly Locks
+Sleepable read-mostly locks are a variation on read-mostly locks.
+Threads holding an exclusive lock may sleep,
+but threads holding a shared lock may not.
+Priority is propagated to shared owners but not to exclusive owners.
.Ss Shared/exclusive locks
Shared/exclusive locks are similar to reader/writer locks; the main difference
-between them is that shared/exclusive locks may be held during unbounded sleep
-(and may thus perform an unbounded sleep).
-They are inherently less efficient than mutexes, reader/writer locks
-and read-mostly locks.
-They do not support priority propagation.
-They should be considered to be closely related to
-.Xr sleep 9 .
-They could in some cases be
-considered a conditional sleep.
+between them is that shared/exclusive locks may be held during unbounded sleep.
+Acquiring a contested shared/exclusive lock can perform an unbounded sleep.
+These locks do not support priority propagation.
.Pp
See
.Xr sx 9
for details.
+.Ss Lockmanager locks
+Lockmanager locks are sleepable shared/exclusive locks used mostly in
+.Xr VFS 9
+.Po
+as a
+.Xr vnode 9
+lock
+.Pc
+and in the buffer cache
+.Po
+.Xr BUF_LOCK 9
+.Pc .
+They have features other lock types do not have such as sleep
+timeouts, blocking upgrades,
+writer starvation avoidance, draining, and an interlock mutex,
+but this makes them complicated both to use and to implement;
+for this reason, they should be avoided.
+.Pp
+See
+.Xr lock 9
+for details.
.Ss Counting semaphores
Counting semaphores provide a mechanism for synchronizing access
to a pool of resources.
@@ -149,43 +157,21 @@ See
.Xr sema 9
for details.
.Ss Condition variables
-Condition variables are used in conjunction with mutexes to wait for
-conditions to occur.
-A thread must hold the mutex before calling the
-.Fn cv_wait* ,
+Condition variables are used in conjunction with locks to wait for
+a condition to become true.
+A thread must hold the associated lock before calling one of the
+.Fn cv_wait ,
functions.
-When a thread waits on a condition, the mutex
-is atomically released before the thread yields the processor,
-then reacquired before the function call returns.
+When a thread waits on a condition, the lock
+is atomically released before the thread yields the processor
+and reacquired before the function call returns.
+Condition variables may be used with blocking mutexes,
+reader/writer locks, read-mostly locks, and shared/exclusive locks.
.Pp
See
.Xr condvar 9
for details.
-.Ss Giant
-Giant is an instance of a mutex, with some special characteristics:
-.Bl -enum
-.It
-It is recursive.
-.It
-Drivers can request that Giant be locked around them
-by not marking themselves MPSAFE.
-Note that infrastructure to do this is slowly going away as non-MPSAFE
-drivers either became properly locked or disappear.
-.It
-Giant must be locked first before other locks.
-.It
-It is OK to hold Giant while performing unbounded sleep; in such case,
-Giant will be dropped before sleeping and picked up after wakeup.
-.It
-There are places in the kernel that drop Giant and pick it back up
-again.
-Sleep locks will do this before sleeping.
-Parts of the network or VM code may do this as well, depending on the
-setting of a sysctl.
-This means that you cannot count on Giant keeping other code from
-running if your code sleeps, even if you want it to.
-.El
-.Ss Sleep/wakeup
+.Ss Sleep/Wakeup
The functions
.Fn tsleep ,
.Fn msleep ,
@@ -194,7 +180,12 @@ The functions
.Fn wakeup ,
and
.Fn wakeup_one
-handle event-based thread blocking.
+also handle event-based thread blocking.
+Unlike condition variables,
+arbitrary addresses may be used as wait channels and a dedicated
+structure does not need to be allocated.
+However, care must be taken to ensure that wait channel addresses are
+unique to an event.
If a thread must wait for an external event, it is put to sleep by
.Fn tsleep ,
.Fn msleep ,
@@ -214,9 +205,10 @@ the thread is being put to sleep.
All threads sleeping on a single
.Fa chan
are woken up later by
-.Fn wakeup ,
-often called from inside an interrupt routine, to indicate that the
-resource the thread was blocking on is available now.
+.Fn wakeup
+.Pq often called from inside an interrupt routine
+to indicate that the
+event the thread was blocking on has occurred.
.Pp
Several of the sleep functions including
.Fn msleep ,
@@ -232,122 +224,170 @@ includes the
flag, then the lock will not be reacquired before returning.
The lock is used to ensure that a condition can be checked atomically,
and that the current thread can be suspended without missing a
-change to the condition, or an associated wakeup.
+change to the condition or an associated wakeup.
In addition, all of the sleep routines will fully drop the
.Va Giant
mutex
-(even if recursed)
+.Pq even if recursed
while the thread is suspended and will reacquire the
.Va Giant
-mutex before the function returns.
+mutex
+.Pq restoring any recursion
+before the function returns.
.Pp
-See
-.Xr sleep 9
-for details.
-.Ss Lockmanager locks
-Shared/exclusive locks, used mostly in
-.Xr VFS 9 ,
-in particular as a
-.Xr vnode 9
-lock.
-They have features other lock types do not have, such as sleep timeout,
-writer starvation avoidance, draining, and interlock mutex, but this makes them
-complicated to implement; for this reason, they are deprecated.
+The
+.Fn pause
+function is a special sleep function that waits for a specified
+amount of time to pass before the thread resumes execution.
+This sleep cannot be terminated early by either an explicit
+.Fn wakeup
+or a signal.
.Pp
See
-.Xr lock 9
+.Xr sleep 9
for details.
+.Ss Giant
+Giant is a special mutex used to protect data structures that do not
+yet have their own locks.
+Since it provides semantics akin to the old
+.Xr spl 9
+interface,
+Giant has special characteristics:
+.Bl -enum
+.It
+It is recursive.
+.It
+Drivers can request that Giant be locked around them
+by not marking themselves MPSAFE.
+Note that infrastructure to do this is slowly going away as non-MPSAFE
+drivers either became properly locked or disappear.
+.It
+Giant must be locked before other non-sleepable locks.
+.It
+Giant is dropped during unbounded sleeps and reacquired after wakeup.
+.It
+There are places in the kernel that drop Giant and pick it back up
+again.
+Sleep locks will do this before sleeping.
+Parts of the network or VM code may do this as well.
+This means that you cannot count on Giant keeping other code from
+running if your code sleeps, even if you want it to.
+.El
.Sh INTERACTIONS
-The primitives interact and have a number of rules regarding how
+The primitives can interact and have a number of rules regarding how
they can and can not be combined.
-Many of these rules are checked using the
-.Xr witness 4
-code.
-.Ss Bounded vs. unbounded sleep
-The following primitives perform bounded sleep:
- mutexes, pool mutexes, reader/writer locks and read-mostly locks.
+Many of these rules are checked by
+.Xr witness 4 .
+.Ss Bounded vs. Unbounded Sleep
+In a bounded sleep
+.Po also referred to as
+.Dq blocking
+.Pc
+the only resource needed to resume execution of a thread
+is CPU time for the owner of a lock that the thread is waiting to acquire.
+In an unbounded sleep
+.Po
+often referred to as simply
+.Dq sleeping
+.Pc
+a thread waits for an external event or for a condition
+to become true.
+In particular,
+a dependency chain of threads in bounded sleeps should always make forward
+progress,
+since there is always CPU time available.
+This requires that no thread in a bounded sleep is waiting for a lock held
+by a thread in an unbounded sleep.
+To avoid priority inversions,
+a thread in a bounded sleep lends its priority to the owner of the lock
+that it is waiting for.
.Pp
-The following primitives may perform an unbounded sleep:
-shared/exclusive locks, counting semaphores, condition variables, sleep/wakeup and lockmanager locks.
+The following primitives perform bounded sleeps:
+mutexes, reader/writer locks and read-mostly locks.
.Pp
+The following primitives perform unbounded sleeps:
+sleepable read-mostly locks, shared/exclusive locks, lockmanager locks,
+counting semaphores, condition variables, and sleep/wakeup.
+.Ss General Principles
+.Bl -bullet
+.It
It is an error to do any operation that could result in yielding the processor
while holding a spin mutex.
+.It
+It is an error to do any operation that could result in unbounded sleep
+while holding any primitive from the 'bounded sleep' group.
+For example, it is an error to try to acquire a shared/exclusive lock while
+holding a mutex, or to try to allocate memory with M_WAITOK while holding a
+reader/writer lock.
.Pp
-As a general rule, it is an error to do any operation that could result
-in unbounded sleep while holding any primitive from the 'bounded sleep' group.
-For example, it is an error to try to acquire shared/exclusive lock while
-holding mutex, or to try to allocate memory with M_WAITOK while holding
-read-write lock.
-.Pp
-As a special case, it is possible to call
+Note that the lock passed to one of the
.Fn sleep
or
-.Fn mtx_sleep
-while holding a single mutex.
-It will atomically drop that mutex and reacquire it as part of waking up.
-This is often a bad idea because it generally relies on the programmer having
-good knowledge of all of the call graph above the place where
-.Fn mtx_sleep
-is being called and assumptions the calling code has made.
-Because the lock gets dropped during sleep, one must re-test all
-the assumptions that were made before, all the way up the call graph to the
-place where the lock was acquired.
-.Pp
+.Fn cv_wait
+functions is dropped before the thread enters the unbounded sleep and does
+not violate this rule.
+.It
It is an error to do any operation that could result in yielding of
the processor when running inside an interrupt filter.
-.Pp
+.It
It is an error to do any operation that could result in unbounded sleep when
running inside an interrupt thread.
+.El
.Ss Interaction table
The following table shows what you can and can not do while holding
-one of the synchronization primitives discussed:
-.Bl -column ".Ic xxxxxxxxxxxxxxxx" ".Xr XXXXXXXXX" ".Xr XXXXXXX" ".Xr XXXXXXX" ".Xr XXXXXXX" ".Xr XXXXXX" -offset indent
-.It Em " You want:" Ta spin-mtx Ta mutex Ta rwlock Ta rmlock Ta sx Ta sleep
-.It Em "You have: " Ta ------ Ta ------ Ta ------ Ta ------ Ta ------ Ta ------
-.It spin mtx Ta \&ok-1 Ta \&no Ta \&no Ta \&no Ta \&no Ta \&no-3
-.It mutex Ta \&ok Ta \&ok-1 Ta \&ok Ta \&ok Ta \&no Ta \&no-3
-.It rwlock Ta \&ok Ta \&ok Ta \&ok-2 Ta \&ok Ta \&no Ta \&no-3
-.It rmlock Ta \&ok Ta \&ok Ta \&ok Ta \&ok-2 Ta \&no-5 Ta \&no-5
-.It sx Ta \&ok Ta \&ok Ta \&ok Ta \&ok Ta \&no-2 Ta \&ok-4
+one of the locking primitives discussed. Note that
+.Dq sleep
+includes
+.Fn sema_wait ,
+.Fn sema_timedwait ,
+any of the
+.Fn cv_wait
+functions,
+and any of the
+.Fn sleep
+functions.
+.Bl -column ".Ic xxxxxxxxxxxxxxxx" ".Xr XXXXXXXXX" ".Xr XXXXXXXXX" ".Xr XXXXXXX" ".Xr XXXXXXXXX" ".Xr XXXXXX" -offset 3n
+.It Em " You want:" Ta spin mtx Ta mutex/rw Ta rmlock Ta sleep rm Ta sx/lk Ta sleep
+.It Em "You have: " Ta -------- Ta -------- Ta ------ Ta -------- Ta ------ Ta ------
+.It spin mtx Ta \&ok Ta \&no Ta \&no Ta \&no Ta \&no Ta \&no-1
+.It mutex/rw Ta \&ok Ta \&ok Ta \&ok Ta \&no Ta \&no Ta \&no-1
+.It rmlock Ta \&ok Ta \&ok Ta \&ok Ta \&no Ta \&no Ta \&no-1
+.It sleep rm Ta \&ok Ta \&ok Ta \&ok Ta \&ok-2 Ta \&ok-2 Ta \&ok-2/3
+.It sx Ta \&ok Ta \&ok Ta \&ok Ta \&ok Ta \&ok Ta \&ok-3
+.It lockmgr Ta \&ok Ta \&ok Ta \&ok Ta \&ok Ta \&ok Ta \&ok
.El
.Pp
.Em *1
-Recursion is defined per lock.
-Lock order is important.
+There are calls that atomically release this primitive when going to sleep
+and reacquire it on wakeup
+.Po
+.Fn mtx_sleep ,
+.Fn rw_sleep ,
+.Fn msleep_spin ,
+etc.
+.Pc .
.Pp
.Em *2
-Readers can recurse though writers can not.
-Lock order is important.
+These cases are only allowed while holding a write lock on a sleepable
+read-mostly lock.
.Pp
.Em *3
-There are calls that atomically release this primitive when going to sleep
-and reacquire it on wakeup (e.g.
-.Fn mtx_sleep ,
-.Fn rw_sleep
-and
-.Fn msleep_spin ) .
-.Pp
-.Em *4
-Though one can sleep holding an sx lock, one can also use
-.Fn sx_sleep
-which will atomically release this primitive when going to sleep and
+Though one can sleep while holding this lock,
+one can also use a
+.Fn sleep
+function to atomically release this primitive when going to sleep and
reacquire it on wakeup.
.Pp
-.Em *5
-.Em Read-mostly
-locks can be initialized to support sleeping while holding a write lock.
-See
-.Xr rmlock 9
-for details.
+Note that non-blocking try operations on locks are always permitted.
.Ss Context mode table
The next table shows what can be used in different contexts.
At this time this is a rather easy to remember table.
-.Bl -column ".Ic Xxxxxxxxxxxxxxxxxxx" ".Xr XXXXXXXXX" ".Xr XXXXXXX" ".Xr XXXXXXX" ".Xr XXXXXXX" ".Xr XXXXXX" -offset indent
-.It Em "Context:" Ta spin mtx Ta mutex Ta sx Ta rwlock Ta rmlock Ta sleep
+.Bl -column ".Ic Xxxxxxxxxxxxxxxxxxx" ".Xr XXXXXXXXX" ".Xr XXXXXXXXX" ".Xr XXXXXXX" ".Xr XXXXXXXXX" ".Xr XXXXXX" -offset 3n
+.It Em "Context:" Ta spin mtx Ta mutex/rw Ta rmlock Ta sleep rm Ta sx/lk Ta sleep
.It interrupt filter: Ta \&ok Ta \&no Ta \&no Ta \&no Ta \&no Ta \&no
-.It interrupt thread: Ta \&ok Ta \&ok Ta \&no Ta \&ok Ta \&ok Ta \&no
-.It callout: Ta \&ok Ta \&ok Ta \&no Ta \&ok Ta \&no Ta \&no
-.It syscall: Ta \&ok Ta \&ok Ta \&ok Ta \&ok Ta \&ok Ta \&ok
+.It interrupt thread: Ta \&ok Ta \&ok Ta \&ok Ta \&no Ta \&no Ta \&no
+.It callout: Ta \&ok Ta \&ok Ta \&ok Ta \&no Ta \&no Ta \&no
+.It system call: Ta \&ok Ta \&ok Ta \&ok Ta \&ok Ta \&ok Ta \&ok
.El
.Sh SEE ALSO
.Xr witness 4 ,
diff --git a/share/misc/committers-src.dot b/share/misc/committers-src.dot
index d321381..6691efb 100644
--- a/share/misc/committers-src.dot
+++ b/share/misc/committers-src.dot
@@ -202,6 +202,7 @@ kevlo [label="Kevin Lo\nkevlo@FreeBSD.org\n2006/07/23"]
kib [label="Konstantin Belousov\nkib@FreeBSD.org\n2006/06/03"]
kmacy [label="Kip Macy\nkmacy@FreeBSD.org\n2005/06/01"]
le [label="Lukas Ertl\nle@FreeBSD.org\n2004/02/02"]
+loos [label="Luiz Otavio O Souza\nloos@FreeBSD.org\n2013/07/03"]
lstewart [label="Lawrence Stewart\nlstewart@FreeBSD.org\n2008/10/06"]
marcel [label="Marcel Moolenaar\nmarcel@FreeBSD.org\n1999/07/03"]
marius [label="Marius Strobl\nmarius@FreeBSD.org\n2004/04/17"]
@@ -305,6 +306,7 @@ day1 -> rgrimes
day1 -> alm
day1 -> dg
+adrian -> loos
adrian -> monthadar
adrian -> ray
adrian -> rmh
diff --git a/share/mk/bsd.libnames.mk b/share/mk/bsd.libnames.mk
index e11ac81..f78a656 100644
--- a/share/mk/bsd.libnames.mk
+++ b/share/mk/bsd.libnames.mk
@@ -149,6 +149,7 @@ LIBRT?= ${DESTDIR}${LIBDIR}/librt.a
LIBRTLD_DB?= ${DESTDIR}${LIBDIR}/librtld_db.a
LIBSBUF?= ${DESTDIR}${LIBDIR}/libsbuf.a
LIBSDP?= ${DESTDIR}${LIBDIR}/libsdp.a
+LIBSMB?= ${DESTDIR}${LIBDIR}/libsmb.a
LIBSSH?= ${DESTDIR}${LIBDIR}/libssh.a
LIBSSL?= ${DESTDIR}${LIBDIR}/libssl.a
LIBSTAND?= ${DESTDIR}${LIBDIR}/libstand.a
diff --git a/sys/amd64/amd64/pmap.c b/sys/amd64/amd64/pmap.c
index 8dcf232..e1d373c 100644
--- a/sys/amd64/amd64/pmap.c
+++ b/sys/amd64/amd64/pmap.c
@@ -4400,6 +4400,7 @@ pmap_remove_pages(pmap_t pmap)
int64_t bit;
uint64_t inuse, bitmask;
int allfree, field, freed, idx;
+ vm_paddr_t pa;
if (pmap != PCPU_GET(curpmap)) {
printf("warning: pmap_remove_pages called with non-current pmap\n");
@@ -4429,7 +4430,7 @@ pmap_remove_pages(pmap_t pmap)
pte = (pt_entry_t *)PHYS_TO_DMAP(tpte &
PG_FRAME);
pte = &pte[pmap_pte_index(pv->pv_va)];
- tpte = *pte & ~PG_PTE_PAT;
+ tpte = *pte;
}
if ((tpte & PG_V) == 0) {
panic("bad pte va %lx pte %lx",
@@ -4444,8 +4445,13 @@ pmap_remove_pages(pmap_t pmap)
continue;
}
- m = PHYS_TO_VM_PAGE(tpte & PG_FRAME);
- KASSERT(m->phys_addr == (tpte & PG_FRAME),
+ if (tpte & PG_PS)
+ pa = tpte & PG_PS_FRAME;
+ else
+ pa = tpte & PG_FRAME;
+
+ m = PHYS_TO_VM_PAGE(pa);
+ KASSERT(m->phys_addr == pa,
("vm_page_t %p phys_addr mismatch %016jx %016jx",
m, (uintmax_t)m->phys_addr,
(uintmax_t)tpte));
diff --git a/sys/amd64/include/counter.h b/sys/amd64/include/counter.h
index b37a4b8..b571e70 100644
--- a/sys/amd64/include/counter.h
+++ b/sys/amd64/include/counter.h
@@ -36,6 +36,44 @@ extern struct pcpu __pcpu[1];
#define counter_enter() do {} while (0)
#define counter_exit() do {} while (0)
+#ifdef IN_SUBR_COUNTER_C
+static inline uint64_t
+counter_u64_read_one(uint64_t *p, int cpu)
+{
+
+ return (*(uint64_t *)((char *)p + sizeof(struct pcpu) * cpu));
+}
+
+static inline uint64_t
+counter_u64_fetch_inline(uint64_t *p)
+{
+ uint64_t r;
+ int i;
+
+ r = 0;
+ for (i = 0; i < mp_ncpus; i++)
+ r += counter_u64_read_one((uint64_t *)p, i);
+
+ return (r);
+}
+
+static void
+counter_u64_zero_one_cpu(void *arg)
+{
+
+ *((uint64_t *)((char *)arg + sizeof(struct pcpu) *
+ PCPU_GET(cpuid))) = 0;
+}
+
+static inline void
+counter_u64_zero_inline(counter_u64_t c)
+{
+
+ smp_rendezvous(smp_no_rendevous_barrier, counter_u64_zero_one_cpu,
+ smp_no_rendevous_barrier, c);
+}
+#endif
+
#define counter_u64_add_protected(c, i) counter_u64_add(c, i)
static inline void
diff --git a/sys/amd64/vmm/intel/ept.c b/sys/amd64/vmm/intel/ept.c
index 4f91601..1ff1580 100644
--- a/sys/amd64/vmm/intel/ept.c
+++ b/sys/amd64/vmm/intel/ept.c
@@ -77,6 +77,11 @@ MALLOC_DECLARE(M_VMX);
static uint64_t page_sizes_mask;
+/*
+ * Set this to 1 to have the EPT tables respect the guest PAT settings
+ */
+static int ept_pat_passthru;
+
int
ept_init(void)
{
@@ -226,10 +231,13 @@ ept_create_mapping(uint64_t *ptp, vm_paddr_t gpa, vm_paddr_t hpa, size_t length,
ptp[ptpindex] |= EPT_PG_EX;
/*
- * XXX should we enforce this memory type by setting the
- * ignore PAT bit to 1.
+ * By default the PAT type is ignored - this appears to
+ * be how other hypervisors handle EPT. Allow this to be
+ * overridden.
*/
ptp[ptpindex] |= EPT_PG_MEMORY_TYPE(attr);
+ if (!ept_pat_passthru)
+ ptp[ptpindex] |= EPT_PG_IGNORE_PAT;
if (nlevels > 0)
ptp[ptpindex] |= EPT_PG_SUPERPAGE;
diff --git a/sys/amd64/vmm/vmm_instruction_emul.c b/sys/amd64/vmm/vmm_instruction_emul.c
index 8328144..4e63649 100644
--- a/sys/amd64/vmm/vmm_instruction_emul.c
+++ b/sys/amd64/vmm/vmm_instruction_emul.c
@@ -780,6 +780,19 @@ decode_immediate(struct vie *vie)
}
/*
+ * Verify that all the bytes in the instruction buffer were consumed.
+ */
+static int
+verify_inst_length(struct vie *vie)
+{
+
+ if (vie->num_processed == vie->num_valid)
+ return (0);
+ else
+ return (-1);
+}
+
+/*
* Verify that the 'guest linear address' provided as collateral of the nested
* page table fault matches with our instruction decoding.
*/
@@ -853,6 +866,9 @@ vmm_decode_instruction(struct vm *vm, int cpuid, uint64_t gla, struct vie *vie)
if (decode_immediate(vie))
return (-1);
+ if (verify_inst_length(vie))
+ return (-1);
+
if (verify_gla(vm, cpuid, gla, vie))
return (-1);
diff --git a/sys/amd64/vmm/x86.c b/sys/amd64/vmm/x86.c
index fa2eabc..262efbd 100644
--- a/sys/amd64/vmm/x86.c
+++ b/sys/amd64/vmm/x86.c
@@ -45,7 +45,9 @@ __FBSDID("$FreeBSD$");
#define CPUID_VM_HIGH 0x40000000
-static const char bhyve_id[12] = "BHyVE BHyVE ";
+static const char bhyve_id[12] = "bhyve bhyve ";
+
+static uint64_t bhyve_xcpuids;
int
x86_emulate_cpuid(struct vm *vm, int vcpu_id,
@@ -77,15 +79,13 @@ x86_emulate_cpuid(struct vm *vm, int vcpu_id,
* no multi-core or SMT.
*/
switch (func) {
+ /*
+ * Pass these through to the guest
+ */
case CPUID_0000_0000:
case CPUID_0000_0002:
case CPUID_0000_0003:
- case CPUID_0000_000A:
- cpuid_count(*eax, *ecx, regs);
- break;
-
case CPUID_8000_0000:
- case CPUID_8000_0001:
case CPUID_8000_0002:
case CPUID_8000_0003:
case CPUID_8000_0004:
@@ -94,6 +94,15 @@ x86_emulate_cpuid(struct vm *vm, int vcpu_id,
cpuid_count(*eax, *ecx, regs);
break;
+ case CPUID_8000_0001:
+ /*
+ * Hide rdtscp/ia32_tsc_aux until we know how
+ * to deal with them.
+ */
+ cpuid_count(*eax, *ecx, regs);
+ regs[3] &= ~AMDID_RDTSCP;
+ break;
+
case CPUID_8000_0007:
cpuid_count(*eax, *ecx, regs);
/*
@@ -150,6 +159,11 @@ x86_emulate_cpuid(struct vm *vm, int vcpu_id,
*/
regs[2] &= ~CPUID2_MON;
+ /*
+ * Hide the performance and debug features.
+ */
+ regs[2] &= ~CPUID2_PDCM;
+
/*
* Hide thermal monitoring
*/
@@ -161,6 +175,11 @@ x86_emulate_cpuid(struct vm *vm, int vcpu_id,
*/
regs[3] &= ~(CPUID_MCA | CPUID_MCE | CPUID_MTRR);
+ /*
+ * Hide the debug store capability.
+ */
+ regs[3] &= ~CPUID_DS;
+
/*
* Disable multi-core.
*/
@@ -180,6 +199,7 @@ x86_emulate_cpuid(struct vm *vm, int vcpu_id,
case CPUID_0000_0006:
case CPUID_0000_0007:
+ case CPUID_0000_000A:
/*
* Handle the access, but report 0 for
* all options
@@ -203,17 +223,25 @@ x86_emulate_cpuid(struct vm *vm, int vcpu_id,
case 0x40000000:
regs[0] = CPUID_VM_HIGH;
bcopy(bhyve_id, &regs[1], 4);
- bcopy(bhyve_id, &regs[2], 4);
- bcopy(bhyve_id, &regs[3], 4);
+ bcopy(bhyve_id + 4, &regs[2], 4);
+ bcopy(bhyve_id + 8, &regs[3], 4);
break;
+
default:
- /* XXX: Leaf 5? */
- return (0);
+ /*
+ * The leaf value has already been clamped so
+ * simply pass this through, keeping count of
+ * how many unhandled leaf values have been seen.
+ */
+ atomic_add_long(&bhyve_xcpuids, 1);
+ cpuid_count(*eax, *ecx, regs);
+ break;
}
*eax = regs[0];
*ebx = regs[1];
*ecx = regs[2];
*edx = regs[3];
+
return (1);
}
diff --git a/sys/arm/arm/bus_space_generic.c b/sys/arm/arm/bus_space_generic.c
index f8f38ab..29638845 100644
--- a/sys/arm/arm/bus_space_generic.c
+++ b/sys/arm/arm/bus_space_generic.c
@@ -73,7 +73,7 @@ generic_bs_map(void *t, bus_addr_t bpa, bus_size_t size, int flags,
offset = bpa & PAGE_MASK;
startpa = trunc_page(bpa);
- va = kmem_alloc(kernel_map, endpa - startpa);
+ va = kmem_alloc_nofault(kernel_map, endpa - startpa);
if (va == 0)
return (ENOMEM);
diff --git a/sys/arm/arm/busdma_machdep-v6.c b/sys/arm/arm/busdma_machdep-v6.c
index 5324f80..d5da5c7 100644
--- a/sys/arm/arm/busdma_machdep-v6.c
+++ b/sys/arm/arm/busdma_machdep-v6.c
@@ -192,14 +192,26 @@ static busdma_bufalloc_t standard_allocator; /* Cache of standard buffers */
static void
busdma_init(void *dummy)
{
+ int uma_flags;
+
+ uma_flags = 0;
/* Create a cache of buffers in standard (cacheable) memory. */
standard_allocator = busdma_bufalloc_create("buffer",
arm_dcache_align, /* minimum_alignment */
NULL, /* uma_alloc func */
NULL, /* uma_free func */
- 0); /* uma_zcreate_flags */
-
+ uma_flags); /* uma_zcreate_flags */
+
+#ifdef INVARIANTS
+ /*
+ * Force UMA zone to allocate service structures like
+ * slabs using own allocator. uma_debug code performs
+ * atomic ops on uma_slab_t fields and safety of this
+ * operation is not guaranteed for write-back caches
+ */
+ uma_flags = UMA_ZONE_OFFPAGE;
+#endif
/*
* Create a cache of buffers in uncacheable memory, to implement the
* BUS_DMA_COHERENT (and potentially BUS_DMA_NOCACHE) flag.
@@ -208,7 +220,7 @@ busdma_init(void *dummy)
arm_dcache_align, /* minimum_alignment */
busdma_bufalloc_alloc_uncacheable,
busdma_bufalloc_free_uncacheable,
- 0); /* uma_zcreate_flags */
+ uma_flags); /* uma_zcreate_flags */
}
/*
diff --git a/sys/arm/arm/cpufunc.c b/sys/arm/arm/cpufunc.c
index f235951..4d9889e 100644
--- a/sys/arm/arm/cpufunc.c
+++ b/sys/arm/arm/cpufunc.c
@@ -1481,7 +1481,8 @@ set_cpufuncs()
cputype == CPU_ID_CORTEXA8R3 ||
cputype == CPU_ID_CORTEXA9R1 ||
cputype == CPU_ID_CORTEXA9R2 ||
- cputype == CPU_ID_CORTEXA9R3) {
+ cputype == CPU_ID_CORTEXA9R3 ||
+ cputype == CPU_ID_CORTEXA15 ) {
cpufuncs = cortexa_cpufuncs;
cpu_reset_needs_v4_MMU_disable = 1; /* V4 or higher */
get_cachetype_cp15();
diff --git a/sys/arm/arm/db_trace.c b/sys/arm/arm/db_trace.c
index 7f16028..6b72cde 100644
--- a/sys/arm/arm/db_trace.c
+++ b/sys/arm/arm/db_trace.c
@@ -108,6 +108,7 @@ extern int extab_start, extab_end, exidx_start, exidx_end;
#define INSN_VSP_REG 0x90
#define INSN_POP_COUNT 0xa0
#define INSN_FINISH 0xb0
+#define INSN_POP_REGS 0xb1
#define INSN_VSP_LARGE_INC 0xb2
/* An item in the exception index table */
@@ -268,6 +269,24 @@ db_unwind_exec_insn(struct unwind_state *state)
/* Stop processing */
state->entries = 0;
+ } else if ((insn == INSN_POP_REGS)) {
+ unsigned int mask, reg;
+
+ mask = db_unwind_exec_read_byte(state);
+ if (mask == 0 || (mask & 0xf0) != 0)
+ return 1;
+
+ /* Update SP */
+ update_vsp = 1;
+
+ /* Load the registers */
+ for (reg = 0; mask && reg < 4; mask >>= 1, reg++) {
+ if (mask & 1) {
+ state->registers[reg] = *vsp++;
+ state->update_mask |= 1 << reg;
+ }
+ }
+
} else if ((insn & INSN_VSP_LARGE_INC_MASK) == INSN_VSP_LARGE_INC) {
unsigned int uleb128;
diff --git a/sys/arm/arm/exception.S b/sys/arm/arm/exception.S
index 498c43f..171211a 100644
--- a/sys/arm/arm/exception.S
+++ b/sys/arm/arm/exception.S
@@ -206,7 +206,7 @@ END(address_exception_entry)
*/
ASENTRY_NP(exception_exit)
- STOP_UNWINDING
+ UNWINDSVCFRAME
DO_AST
PULLFRAMEFROMSVCANDEXIT
END(exception_exit)
diff --git a/sys/arm/arm/generic_timer.c b/sys/arm/arm/generic_timer.c
new file mode 100644
index 0000000..48d92ae
--- /dev/null
+++ b/sys/arm/arm/generic_timer.c
@@ -0,0 +1,394 @@
+/*-
+ * Copyright (c) 2011 The FreeBSD Foundation
+ * Copyright (c) 2013 Ruslan Bukin <br@bsdpad.com>
+ * All rights reserved.
+ *
+ * Based on mpcore_timer.c developed by Ben Gray <ben.r.gray@gmail.com>
+ *
+ * Redistribution and use in source and binary forms, with or without
+ * modification, are permitted provided that the following conditions
+ * are met:
+ * 1. Redistributions of source code must retain the above copyright
+ * notice, this list of conditions and the following disclaimer.
+ * 2. Redistributions in binary form must reproduce the above copyright
+ * notice, this list of conditions and the following disclaimer in the
+ * documentation and/or other materials provided with the distribution.
+ * 3. The name of the company nor the name of the author may be used to
+ * endorse or promote products derived from this software without specific
+ * prior written permission.
+ *
+ * THIS SOFTWARE IS PROVIDED BY THE AUTHOR AND CONTRIBUTORS ``AS IS'' AND
+ * ANY EXPRESS OR IMPLIED WARRANTIES, INCLUDING, BUT NOT LIMITED TO, THE
+ * IMPLIED WARRANTIES OF MERCHANTABILITY AND FITNESS FOR A PARTICULAR PURPOSE
+ * ARE DISCLAIMED. IN NO EVENT SHALL THE AUTHOR OR CONTRIBUTORS BE LIABLE
+ * FOR ANY DIRECT, INDIRECT, INCIDENTAL, SPECIAL, EXEMPLARY, OR CONSEQUENTIAL
+ * DAMAGES (INCLUDING, BUT NOT LIMITED TO, PROCUREMENT OF SUBSTITUTE GOODS
+ * OR SERVICES; LOSS OF USE, DATA, OR PROFITS; OR BUSINESS INTERRUPTION)
+ * HOWEVER CAUSED AND ON ANY THEORY OF LIABILITY, WHETHER IN CONTRACT, STRICT
+ * LIABILITY, OR TORT (INCLUDING NEGLIGENCE OR OTHERWISE) ARISING IN ANY WAY
+ * OUT OF THE USE OF THIS SOFTWARE, EVEN IF ADVISED OF THE POSSIBILITY OF
+ * SUCH DAMAGE.
+ */
+
+/**
+ * Cortex-A15 (and probably A7) Generic Timer
+ */
+
+#include <sys/cdefs.h>
+__FBSDID("$FreeBSD$");
+
+#include <sys/param.h>
+#include <sys/systm.h>
+#include <sys/bus.h>
+#include <sys/kernel.h>
+#include <sys/module.h>
+#include <sys/malloc.h>
+#include <sys/rman.h>
+#include <sys/timeet.h>
+#include <sys/timetc.h>
+#include <sys/watchdog.h>
+#include <machine/bus.h>
+#include <machine/cpu.h>
+#include <machine/frame.h>
+#include <machine/intr.h>
+
+#include <dev/fdt/fdt_common.h>
+#include <dev/ofw/openfirm.h>
+#include <dev/ofw/ofw_bus.h>
+#include <dev/ofw/ofw_bus_subr.h>
+
+#include <machine/bus.h>
+#include <machine/fdt.h>
+
+#define GENERIC_TIMER_CTRL_ENABLE (1 << 0)
+#define GENERIC_TIMER_CTRL_INT_MASK (1 << 1)
+#define GENERIC_TIMER_CTRL_INT_STAT (1 << 2)
+#define GENERIC_TIMER_REG_CTRL 0
+#define GENERIC_TIMER_REG_TVAL 1
+
+#define GENERIC_TIMER_CNTKCTL_PL0PTEN (1 << 9) /* Physical timer registers
+ access from PL0 */
+#define GENERIC_TIMER_CNTKCTL_PL0VTEN (1 << 8) /* Virtual timer registers
+ access from PL0 */
+#define GENERIC_TIMER_CNTKCTL_EVNTI (1 << 4) /* Virtual counter
+ event bits */
+#define GENERIC_TIMER_CNTKCTL_EVNTDIR (1 << 3) /* Virtual counter
+ event transition */
+#define GENERIC_TIMER_CNTKCTL_EVNTEN (1 << 2) /* Enables events from
+ the virtual counter */
+#define GENERIC_TIMER_CNTKCTL_PL0VCTEN (1 << 1) /* CNTVCT and CNTFRQ
+ access from PL0 */
+#define GENERIC_TIMER_CNTKCTL_PL0PCTEN (1 << 0) /* CNTPCT and CNTFRQ
+ access from PL0 */
+
+#define GENERIC_TIMER_CNTPSIRQ 29
+
+struct arm_tmr_softc {
+ struct resource *irq_res;
+ uint32_t clkfreq;
+ struct eventtimer et;
+};
+
+static struct arm_tmr_softc *arm_tmr_sc = NULL;
+
+static timecounter_get_t arm_tmr_get_timecount;
+
+static struct timecounter arm_tmr_timecount = {
+ .tc_name = "ARM MPCore Timecounter",
+ .tc_get_timecount = arm_tmr_get_timecount,
+ .tc_poll_pps = NULL,
+ .tc_counter_mask = ~0u,
+ .tc_frequency = 0,
+ .tc_quality = 1000,
+};
+
+static inline int
+get_freq(void)
+{
+ uint32_t val;
+
+ __asm volatile("mrc p15, 0, %0, c14, c0, 0" : "=r" (val));
+
+ return (val);
+}
+
+static inline int
+set_freq(uint32_t val)
+{
+
+ __asm volatile("mcr p15, 0, %[val], c14, c0, 0" : :
+ [val] "r" (val));
+ isb();
+
+ return (val);
+}
+
+
+static inline long
+get_cntpct(void)
+{
+ uint64_t val;
+
+ __asm volatile("mrrc p15, 0, %Q0, %R0, c14" : "=r" (val));
+
+ return (val);
+}
+
+static inline int
+set_ctrl(uint32_t val)
+{
+
+ __asm volatile("mcr p15, 0, %[val], c14, c2, 1" : :
+ [val] "r" (val));
+ isb();
+
+ return (0);
+}
+
+static inline int
+set_tval(uint32_t val)
+{
+
+ __asm volatile("mcr p15, 0, %[val], c14, c2, 0" : :
+ [val] "r" (val));
+ isb();
+
+ return (0);
+}
+
+static inline int
+get_ctrl(void)
+{
+ uint32_t val;
+
+ __asm volatile("mrc p15, 0, %0, c14, c2, 1" : "=r" (val));
+
+ return (val);
+}
+
+static inline int
+get_tval(void)
+{
+ uint32_t val;
+
+ __asm volatile("mrc p15, 0, %0, c14, c2, 0" : "=r" (val));
+
+ return (val);
+}
+
+static inline void
+disable_user_access(void)
+{
+ uint32_t cntkctl;
+
+ __asm volatile("mrc p15, 0, %0, c14, c1, 0" : "=r" (cntkctl));
+ cntkctl &= ~(GENERIC_TIMER_CNTKCTL_PL0PTEN |
+ GENERIC_TIMER_CNTKCTL_PL0VTEN |
+ GENERIC_TIMER_CNTKCTL_EVNTEN |
+ GENERIC_TIMER_CNTKCTL_PL0VCTEN |
+ GENERIC_TIMER_CNTKCTL_PL0PCTEN);
+ __asm volatile("mcr p15, 0, %0, c14, c1, 0" : : "r" (cntkctl));
+ isb();
+}
+
+static unsigned
+arm_tmr_get_timecount(struct timecounter *tc)
+{
+
+ return (get_cntpct());
+}
+
+static int
+arm_tmr_start(struct eventtimer *et, sbintime_t first, sbintime_t period)
+{
+ struct arm_tmr_softc *sc;
+ int counts, ctrl;
+
+ sc = (struct arm_tmr_softc *)et->et_priv;
+
+ if (first != 0) {
+ counts = ((uint32_t)et->et_frequency * first) >> 32;
+ ctrl = get_ctrl();
+ ctrl &= ~GENERIC_TIMER_CTRL_INT_MASK;
+ ctrl |= GENERIC_TIMER_CTRL_ENABLE;
+ set_tval(counts);
+ set_ctrl(ctrl);
+ return (0);
+ }
+
+ return (EINVAL);
+
+}
+
+static int
+arm_tmr_stop(struct eventtimer *et)
+{
+ int ctrl;
+
+ ctrl = get_ctrl();
+ ctrl &= GENERIC_TIMER_CTRL_ENABLE;
+ set_ctrl(ctrl);
+
+ return (0);
+}
+
+static int
+arm_tmr_intr(void *arg)
+{
+ struct arm_tmr_softc *sc;
+ int ctrl;
+
+ sc = (struct arm_tmr_softc *)arg;
+ ctrl = get_ctrl();
+ if (ctrl & GENERIC_TIMER_CTRL_INT_STAT) {
+ ctrl |= GENERIC_TIMER_CTRL_INT_MASK;
+ set_ctrl(ctrl);
+ }
+
+ if (sc->et.et_active)
+ sc->et.et_event_cb(&sc->et, sc->et.et_arg);
+
+ return (FILTER_HANDLED);
+}
+
+static int
+arm_tmr_probe(device_t dev)
+{
+
+ if (!ofw_bus_is_compatible(dev, "arm,armv7-timer"))
+ return (ENXIO);
+
+ device_set_desc(dev, "ARMv7 Generic Timer");
+ return (BUS_PROBE_DEFAULT);
+}
+
+
+static int
+arm_tmr_attach(device_t dev)
+{
+ struct arm_tmr_softc *sc;
+ phandle_t node;
+ pcell_t clock;
+ void *ihl;
+ int rid;
+ int error;
+
+ sc = device_get_softc(dev);
+ if (arm_tmr_sc)
+ return (ENXIO);
+
+ /* Get the base clock frequency */
+ node = ofw_bus_get_node(dev);
+ error = OF_getprop(node, "clock-frequency", &clock, sizeof(clock));
+ if (error <= 0) {
+ device_printf(dev, "missing clock-frequency "
+ "attribute in FDT\n");
+ return (ENXIO);
+ }
+ sc->clkfreq = fdt32_to_cpu(clock);
+
+ rid = 0;
+ sc->irq_res = bus_alloc_resource(dev, SYS_RES_IRQ, &rid,
+ GENERIC_TIMER_CNTPSIRQ, GENERIC_TIMER_CNTPSIRQ,
+ 1, RF_SHAREABLE | RF_ACTIVE);
+
+ arm_tmr_sc = sc;
+
+ /* Setup and enable the timer */
+ if (bus_setup_intr(dev, sc->irq_res, INTR_TYPE_CLK, arm_tmr_intr,
+ NULL, sc, &ihl) != 0) {
+ bus_release_resource(dev, SYS_RES_IRQ, rid, sc->irq_res);
+ device_printf(dev, "Unable to setup the CLK irq handler.\n");
+ return (ENXIO);
+ }
+
+ set_freq(sc->clkfreq);
+ disable_user_access();
+
+ arm_tmr_timecount.tc_frequency = sc->clkfreq;
+ tc_init(&arm_tmr_timecount);
+
+ sc->et.et_name = "ARM MPCore Eventtimer";
+ sc->et.et_flags = ET_FLAGS_ONESHOT | ET_FLAGS_PERCPU;
+ sc->et.et_quality = 1000;
+
+ sc->et.et_frequency = sc->clkfreq;
+ sc->et.et_min_period = (0x00000002LLU << 32) / sc->et.et_frequency;
+ sc->et.et_max_period = (0xfffffffeLLU << 32) / sc->et.et_frequency;
+ sc->et.et_start = arm_tmr_start;
+ sc->et.et_stop = arm_tmr_stop;
+ sc->et.et_priv = sc;
+ et_register(&sc->et);
+
+ return (0);
+}
+
+static device_method_t arm_tmr_methods[] = {
+ DEVMETHOD(device_probe, arm_tmr_probe),
+ DEVMETHOD(device_attach, arm_tmr_attach),
+ { 0, 0 }
+};
+
+static driver_t arm_tmr_driver = {
+ "generic_timer",
+ arm_tmr_methods,
+ sizeof(struct arm_tmr_softc),
+};
+
+static devclass_t arm_tmr_devclass;
+
+DRIVER_MODULE(timer, simplebus, arm_tmr_driver, arm_tmr_devclass, 0, 0);
+
+void
+cpu_initclocks(void)
+{
+
+ if (PCPU_GET(cpuid) == 0)
+ cpu_initclocks_bsp();
+ else
+ cpu_initclocks_ap();
+}
+
+void
+DELAY(int usec)
+{
+ int32_t counts, counts_per_usec;
+ uint32_t first, last;
+
+ /*
+ * Check the timers are setup, if not just
+ * use a for loop for the meantime
+ */
+ if (arm_tmr_sc == NULL) {
+ for (; usec > 0; usec--)
+ for (counts = 200; counts > 0; counts--)
+ /*
+ * Prevent gcc from optimizing
+ * out the loop
+ */
+ cpufunc_nullop();
+ return;
+ }
+
+ /* Get the number of times to count */
+ counts_per_usec = ((arm_tmr_timecount.tc_frequency / 1000000) + 1);
+
+ /*
+ * Clamp the timeout at a maximum value (about 32 seconds with
+ * a 66MHz clock). *Nobody* should be delay()ing for anywhere
+ * near that length of time and if they are, they should be hung
+ * out to dry.
+ */
+ if (usec >= (0x80000000U / counts_per_usec))
+ counts = (0x80000000U / counts_per_usec) - 1;
+ else
+ counts = usec * counts_per_usec;
+
+ first = get_cntpct();
+
+ while (counts > 0) {
+ last = get_cntpct();
+ counts -= (int32_t)(last - first);
+ first = last;
+ }
+}
diff --git a/sys/arm/arm/gic.c b/sys/arm/arm/gic.c
index 2871d9d..c453a20 100644
--- a/sys/arm/arm/gic.c
+++ b/sys/arm/arm/gic.c
@@ -114,6 +114,7 @@ static void gic_post_filter(void *);
static int
arm_gic_probe(device_t dev)
{
+
if (!ofw_bus_is_compatible(dev, "arm,gic"))
return (ENXIO);
device_set_desc(dev, "ARM Generic Interrupt Controller");
@@ -123,14 +124,20 @@ arm_gic_probe(device_t dev)
void
gic_init_secondary(void)
{
- int nirqs;
-
+ int i, nirqs;
+
/* Get the number of interrupts */
nirqs = gic_d_read_4(GICD_TYPER);
nirqs = 32 * ((nirqs & 0x1f) + 1);
-
- for (int i = 0; i < nirqs; i += 4)
+
+ for (i = 0; i < nirqs; i += 4)
gic_d_write_4(GICD_IPRIORITYR(i >> 2), 0);
+
+ /* Set all the interrupts to be in Group 0 (secure) */
+ for (i = 0; i < nirqs; i += 32) {
+ gic_d_write_4(GICD_IGROUPR(i >> 5), 0);
+ }
+
/* Enable CPU interface */
gic_c_write_4(GICC_CTLR, 1);
@@ -139,7 +146,7 @@ gic_init_secondary(void)
/* Enable interrupt distribution */
gic_d_write_4(GICD_CTLR, 0x01);
-
+
/* Activate IRQ 29, ie private timer IRQ*/
gic_d_write_4(GICD_ISENABLER(29 >> 5), (1UL << (29 & 0x1F)));
}
@@ -147,7 +154,7 @@ gic_init_secondary(void)
static int
arm_gic_attach(device_t dev)
{
- struct arm_gic_softc *sc = device_get_softc(dev);
+ struct arm_gic_softc *sc;
int i;
uint32_t icciidr;
uint32_t nirqs;
@@ -155,6 +162,8 @@ arm_gic_attach(device_t dev)
if (arm_gic_sc)
return (ENXIO);
+ sc = device_get_softc(dev);
+
if (bus_alloc_resources(dev, arm_gic_spec, sc->gic_res)) {
device_printf(dev, "could not allocate resources\n");
return (ENXIO);
@@ -180,7 +189,7 @@ arm_gic_attach(device_t dev)
nirqs = 32 * ((nirqs & 0x1f) + 1);
icciidr = gic_c_read_4(GICC_IIDR);
- device_printf(dev,"pn 0x%x, arch 0x%x, rev 0x%x, implementer 0x%x nirqs %u\n",
+ device_printf(dev,"pn 0x%x, arch 0x%x, rev 0x%x, implementer 0x%x nirqs %u\n",
icciidr>>20, (icciidr>>16) & 0xF, (icciidr>>12) & 0xf,
(icciidr & 0xfff), nirqs);
@@ -195,10 +204,15 @@ arm_gic_attach(device_t dev)
}
for (i = 0; i < nirqs; i += 4) {
- gic_d_write_4(GICD_IPRIORITYR(i >> 2), 0);
+ gic_d_write_4(GICD_IPRIORITYR(i >> 2), 0);
gic_d_write_4(GICD_ITARGETSR(i >> 2), 0xffffffff);
}
+ /* Set all the interrupts to be in Group 0 (secure) */
+ for (i = 0; i < nirqs; i += 32) {
+ gic_d_write_4(GICD_IGROUPR(i >> 5), 0);
+ }
+
/* Enable CPU interface */
gic_c_write_4(GICC_CTLR, 1);
@@ -242,12 +256,12 @@ arm_get_next_irq(int last_irq)
active_irq = gic_c_read_4(GICC_IAR);
- /*
+ /*
* Immediatly EOIR the SGIs, because doing so requires the other
* bits (ie CPU number), not just the IRQ number, and we do not
* have this information later.
*/
-
+
if ((active_irq & 0x3ff) < 16)
gic_c_write_4(GICC_EOIR, active_irq);
active_irq &= 0x3FF;
@@ -257,7 +271,7 @@ arm_get_next_irq(int last_irq)
printf("Spurious interrupt detected [0x%08x]\n", active_irq);
return -1;
}
- gic_c_write_4(GICC_EOIR, active_irq);
+ gic_c_write_4(GICC_EOIR, active_irq);
return active_irq;
}
@@ -271,7 +285,7 @@ arm_mask_irq(uintptr_t nb)
void
arm_unmask_irq(uintptr_t nb)
{
-
+
gic_c_write_4(GICC_EOIR, nb);
gic_d_write_4(GICD_ISENABLER(nb >> 5), (1UL << (nb & 0x1F)));
}
@@ -286,19 +300,19 @@ pic_ipi_send(cpuset_t cpus, u_int ipi)
if (CPU_ISSET(i, &cpus))
val |= 1 << (16 + i);
gic_d_write_4(GICD_SGIR(0), val | ipi);
-
+
}
int
pic_ipi_get(int i)
{
-
+
if (i != -1) {
/*
* The intr code will automagically give the frame pointer
* if the interrupt argument is 0.
*/
- if ((unsigned int)i > 16)
+ if ((unsigned int)i > 16)
return (0);
return (i);
}
diff --git a/sys/arm/arm/identcpu.c b/sys/arm/arm/identcpu.c
index 414396a..33cfa1e 100644
--- a/sys/arm/arm/identcpu.c
+++ b/sys/arm/arm/identcpu.c
@@ -248,6 +248,8 @@ const struct cpuidtab cpuids[] = {
generic_steppings },
{ CPU_ID_CORTEXA9R3, CPU_CLASS_CORTEXA, "Cortex A9-r3",
generic_steppings },
+ { CPU_ID_CORTEXA15, CPU_CLASS_CORTEXA, "Cortex A15",
+ generic_steppings },
{ CPU_ID_SA110, CPU_CLASS_SA1, "SA-110",
sa110_steppings },
diff --git a/sys/arm/arm/pmap-v6.c b/sys/arm/arm/pmap-v6.c
index 9e0b8bd..c0f4c2c 100644
--- a/sys/arm/arm/pmap-v6.c
+++ b/sys/arm/arm/pmap-v6.c
@@ -2470,7 +2470,6 @@ pmap_remove_all(vm_page_t m)
else
cpu_tlb_flushD();
}
- vm_page_aflag_clear(m, PGA_WRITEABLE);
rw_wunlock(&pvh_global_lock);
}
@@ -2801,8 +2800,13 @@ validate:
}
if (prot & VM_PROT_WRITE) {
- /* Write enable */
- npte &= ~(L2_APX);
+ /*
+ * Enable write permission if the access type
+ * indicates write intention. Emulate modified
+ * bit otherwise.
+ */
+ if ((access & VM_PROT_WRITE) != 0)
+ npte &= ~(L2_APX);
if ((m->oflags & VPO_UNMANAGED) == 0) {
vm_page_aflag_set(m, PGA_WRITEABLE);
diff --git a/sys/arm/broadcom/bcm2835/bcm2835_mbox.c b/sys/arm/broadcom/bcm2835/bcm2835_mbox.c
index a262ad7..f18102b 100644
--- a/sys/arm/broadcom/bcm2835/bcm2835_mbox.c
+++ b/sys/arm/broadcom/bcm2835/bcm2835_mbox.c
@@ -163,11 +163,9 @@ bcm_mbox_attach(device_t dev)
}
/* Setup and enable the timer */
- if (bus_setup_intr(dev, sc->irq_res, INTR_TYPE_MISC,
- NULL, bcm_mbox_intr, sc,
- &sc->intr_hl) != 0) {
- bus_release_resource(dev, SYS_RES_IRQ, rid,
- sc->irq_res);
+ if (bus_setup_intr(dev, sc->irq_res, INTR_MPSAFE | INTR_TYPE_MISC,
+ NULL, bcm_mbox_intr, sc, &sc->intr_hl) != 0) {
+ bus_release_resource(dev, SYS_RES_IRQ, rid, sc->irq_res);
device_printf(dev, "Unable to setup the clock irq handler.\n");
return (ENXIO);
}
diff --git a/sys/arm/broadcom/bcm2835/bcm2835_sdhci.c b/sys/arm/broadcom/bcm2835/bcm2835_sdhci.c
index f4a2de6..90a5725 100644
--- a/sys/arm/broadcom/bcm2835/bcm2835_sdhci.c
+++ b/sys/arm/broadcom/bcm2835/bcm2835_sdhci.c
@@ -74,8 +74,6 @@ __FBSDID("$FreeBSD$");
#define BCM_SDHCI_BUFFER_SIZE 512
-#define DEBUG
-
#ifdef DEBUG
#define dprintf(fmt, args...) do { printf("%s(): ", __func__); \
printf(fmt,##args); } while (0)
diff --git a/sys/arm/conf/ARNDALE b/sys/arm/conf/ARNDALE
new file mode 100644
index 0000000..1864f33
--- /dev/null
+++ b/sys/arm/conf/ARNDALE
@@ -0,0 +1,135 @@
+# Kernel configuration for Arndale Board (Exynos5 Dual development platform).
+#
+# For more information on this file, please read the config(5) manual page,
+# and/or the handbook section on Kernel Configuration Files:
+#
+# http://www.FreeBSD.org/doc/en_US.ISO8859-1/books/handbook/kernelconfig-config.html
+#
+# The handbook is also available locally in /usr/share/doc/handbook
+# if you've installed the doc distribution, otherwise always see the
+# FreeBSD World Wide Web server (http://www.FreeBSD.org/) for the
+# latest information.
+#
+# An exhaustive list of options and more detailed explanations of the
+# device lines is also present in the ../../conf/NOTES and NOTES files.
+# If you are in doubt as to the purpose or necessity of a line, check first
+# in NOTES.
+#
+# $FreeBSD$
+
+ident ARNDALE
+
+include "../samsung/exynos/std.exynos5"
+
+makeoptions MODULES_OVERRIDE=""
+makeoptions WITHOUT_MODULES="ahc"
+
+makeoptions DEBUG=-g #Build kernel with gdb(1) debug symbols
+makeoptions WERROR="-Werror"
+
+options HZ=100 ##
+options SCHED_4BSD #4BSD scheduler
+options INET #InterNETworking
+options INET6 #IPv6 communications protocols
+options FFS #Berkeley Fast Filesystem
+options SOFTUPDATES
+options UFS_ACL #Support for access control lists
+options UFS_DIRHASH #Improve performance on big directories
+options MSDOSFS #MSDOS Filesystem
+options CD9660 #ISO 9660 Filesystem
+options PROCFS #Process filesystem (requires PSEUDOFS)
+options PSEUDOFS #Pseudo-filesystem framework
+options TMPFS
+options COMPAT_43 #Compatible with BSD 4.3 [KEEP THIS!]
+options SCSI_DELAY=5000 #Delay (in ms) before probing SCSI
+options KTRACE
+options SYSVSHM #SYSV-style shared memory
+options SYSVMSG #SYSV-style message queues
+options SYSVSEM #SYSV-style semaphores
+options _KPOSIX_PRIORITY_SCHEDULING #Posix P1003_1B real-time extensions
+options KBD_INSTALL_CDEV
+options PREEMPTION
+options FREEBSD_BOOT_LOADER
+
+# Debugging
+makeoptions DEBUG=-g #Build kernel with gdb(1) debug symbols
+options BREAK_TO_DEBUGGER
+#options VERBOSE_SYSINIT #Enable verbose sysinit messages
+options KDB
+options DDB #Enable the kernel debugger
+options INVARIANTS #Enable calls of extra sanity checking
+options INVARIANT_SUPPORT #Extra sanity checks of internal structures, required by INVARIANTS
+#options WITNESS #Enable checks to detect deadlocks and cycles
+#options WITNESS_SKIPSPIN #Don't run witness on spinlocks for speed
+options DIAGNOSTIC
+
+# NFS support
+options NFSCL #Network Filesystem Client
+options NFSLOCKD #Network Lock Manager
+options NFS_ROOT #NFS usable as /, requires NFSCLIENT
+
+# Uncomment this for NFS root
+#options NFS_ROOT #NFS usable as /, requires NFSCL
+#options BOOTP_NFSROOT
+#options BOOTP_COMPAT
+#options BOOTP
+#options BOOTP_NFSV3
+#options BOOTP_WIRED_TO=cpsw0
+
+device mmc # mmc/sd bus
+device mmcsd # mmc/sd flash cards
+device sdhci # generic sdhci
+
+options ROOTDEVNAME=\"ufs:/dev/da0\"
+
+#options SMP
+
+# Pseudo devices
+
+device loop
+device random
+device pty
+device md
+device gpio
+
+# USB support
+device usb
+options USB_DEBUG
+#options USB_REQ_DEBUG
+#options USB_VERBOSE
+#device musb
+device ehci
+#device ohci
+
+device umass
+device scbus # SCSI bus (required for SCSI)
+device da # Direct Access (disks)
+device pass
+
+# SATA
+#device ata
+#device atadisk
+#device mvs
+
+# Serial ports
+device uart
+
+# I2C (TWSI)
+#device iic
+#device iicbus
+
+# Ethernet
+device ether
+device mii
+device smsc
+device smscphy
+
+# USB ethernet support, requires miibus
+device miibus
+device axe # ASIX Electronics USB Ethernet
+device bpf # Berkeley packet filter
+
+#FDT
+options FDT
+options FDT_DTB_STATIC
+makeoptions FDT_DTS_FILE=exynos5250-arndale.dts
diff --git a/sys/arm/conf/RPI-B b/sys/arm/conf/RPI-B
index 4d840cb..38db5dd 100644
--- a/sys/arm/conf/RPI-B
+++ b/sys/arm/conf/RPI-B
@@ -38,6 +38,8 @@ options HZ=100
options SCHED_4BSD #4BSD scheduler
options INET #InterNETworking
+options INET6 #IPv6 communications protocols
+options SCTP #Stream Control Transmission Protocol
options FFS #Berkeley Fast Filesystem
options SOFTUPDATES #Enable FFS soft updates support
options UFS_ACL #Support for access control lists
@@ -117,4 +119,7 @@ options FDT
# Note: DTB is normally loaded and modified by RPi boot loader, then
# handed to kernel via U-Boot and ubldr.
#options FDT_DTB_STATIC
-makeoptions FDT_DTS_FILE=bcm2835-rpi-b.dts
+makeoptions FDT_DTS_FILE=rpi.dts
+
+device vfp # vfp/neon
+options ARM_VFP_SUPPORT # vfp/neon
diff --git a/sys/arm/include/armreg.h b/sys/arm/include/armreg.h
index 1f54f91..049d140 100644
--- a/sys/arm/include/armreg.h
+++ b/sys/arm/include/armreg.h
@@ -153,6 +153,7 @@
#define CPU_ID_CORTEXA9R1 0x411fc090
#define CPU_ID_CORTEXA9R2 0x412fc090
#define CPU_ID_CORTEXA9R3 0x413fc090
+#define CPU_ID_CORTEXA15 0x410fc0f0
#define CPU_ID_SA110 0x4401a100
#define CPU_ID_SA1100 0x4401a110
#define CPU_ID_TI925T 0x54029250
diff --git a/sys/arm/include/asmacros.h b/sys/arm/include/asmacros.h
index f83c14f..18dcef5 100644
--- a/sys/arm/include/asmacros.h
+++ b/sys/arm/include/asmacros.h
@@ -206,7 +206,16 @@
mov r0, r0; /* NOP for previous instruction */ \
add sp, sp, #(4*15); /* Adjust the stack pointer */ \
ldmia sp, {sp, lr, pc}^ /* Restore lr and exit */
-#endif
+#endif
+#if defined(__ARM_EABI__)
+#define UNWINDSVCFRAME \
+ .save {r13-r15}; /* Restore sp, lr, pc */ \
+ .pad #(2*4); /* Skip user sp and lr */ \
+ .save {r0-r12}; /* Restore r0-r12 */ \
+ .pad #(4) /* Skip spsr */
+#else
+#define UNWINDSVCFRAME
+#endif
#define DATA(name) \
.data ; \
diff --git a/sys/arm/include/counter.h b/sys/arm/include/counter.h
index 68f89e2..a6dfa8e 100644
--- a/sys/arm/include/counter.h
+++ b/sys/arm/include/counter.h
@@ -37,6 +37,46 @@
#define counter_enter() critical_enter()
#define counter_exit() critical_exit()
+#ifdef IN_SUBR_COUNTER_C
+/* XXXKIB non-atomic 64bit read */
+static inline uint64_t
+counter_u64_read_one(uint64_t *p, int cpu)
+{
+
+ return (*(uint64_t *)((char *)p + sizeof(struct pcpu) * cpu));
+}
+
+static inline uint64_t
+counter_u64_fetch_inline(uint64_t *p)
+{
+ uint64_t r;
+ int i;
+
+ r = 0;
+ for (i = 0; i < mp_ncpus; i++)
+ r += counter_u64_read_one((uint64_t *)p, i);
+
+ return (r);
+}
+
+/* XXXKIB non-atomic 64bit store, might interrupt increment */
+static void
+counter_u64_zero_one_cpu(void *arg)
+{
+
+ *((uint64_t *)((char *)arg + sizeof(struct pcpu) *
+ PCPU_GET(cpuid))) = 0;
+}
+
+static inline void
+counter_u64_zero_inline(counter_u64_t c)
+{
+
+ smp_rendezvous(smp_no_rendevous_barrier, counter_u64_zero_one_cpu,
+ smp_no_rendevous_barrier, c);
+}
+#endif
+
#define counter_u64_add_protected(c, inc) do { \
CRITICAL_ASSERT(curthread); \
*(uint64_t *)zpcpu_get(c) += (inc); \
diff --git a/sys/arm/include/intr.h b/sys/arm/include/intr.h
index 9392969..93a204c 100644
--- a/sys/arm/include/intr.h
+++ b/sys/arm/include/intr.h
@@ -51,7 +51,7 @@
defined(CPU_XSCALE_IXP435)
#define NIRQ 64
#elif defined(CPU_CORTEXA)
-#define NIRQ 128
+#define NIRQ 160
#elif defined(CPU_ARM1136) || defined(CPU_ARM1176)
#define NIRQ 128
#elif defined(SOC_MV_ARMADAXP)
diff --git a/sys/arm/samsung/exynos/arch_timer.c b/sys/arm/samsung/exynos/arch_timer.c
new file mode 100644
index 0000000..e5970b6
--- /dev/null
+++ b/sys/arm/samsung/exynos/arch_timer.c
@@ -0,0 +1,136 @@
+/*-
+ * Copyright (c) 2013 Ruslan Bukin <br@bsdpad.com>
+ * All rights reserved.
+ *
+ * Redistribution and use in source and binary forms, with or without
+ * modification, are permitted provided that the following conditions
+ * are met:
+ * 1. Redistributions of source code must retain the above copyright
+ * notice, this list of conditions and the following disclaimer.
+ * 2. Redistributions in binary form must reproduce the above copyright
+ * notice, this list of conditions and the following disclaimer in the
+ * documentation and/or other materials provided with the distribution.
+ *
+ * THIS SOFTWARE IS PROVIDED BY THE AUTHOR AND CONTRIBUTORS ``AS IS'' AND
+ * ANY EXPRESS OR IMPLIED WARRANTIES, INCLUDING, BUT NOT LIMITED TO, THE
+ * IMPLIED WARRANTIES OF MERCHANTABILITY AND FITNESS FOR A PARTICULAR PURPOSE
+ * ARE DISCLAIMED. IN NO EVENT SHALL THE AUTHOR OR CONTRIBUTORS BE LIABLE
+ * FOR ANY DIRECT, INDIRECT, INCIDENTAL, SPECIAL, EXEMPLARY, OR CONSEQUENTIAL
+ * DAMAGES (INCLUDING, BUT NOT LIMITED TO, PROCUREMENT OF SUBSTITUTE GOODS
+ * OR SERVICES; LOSS OF USE, DATA, OR PROFITS; OR BUSINESS INTERRUPTION)
+ * HOWEVER CAUSED AND ON ANY THEORY OF LIABILITY, WHETHER IN CONTRACT, STRICT
+ * LIABILITY, OR TORT (INCLUDING NEGLIGENCE OR OTHERWISE) ARISING IN ANY WAY
+ * OUT OF THE USE OF THIS SOFTWARE, EVEN IF ADVISED OF THE POSSIBILITY OF
+ * SUCH DAMAGE.
+ */
+
+/*
+ * This module just enables Exynos MCT, so ARMv7 Generic Timer will works
+ */
+
+#include <sys/cdefs.h>
+__FBSDID("$FreeBSD$");
+
+#include <sys/param.h>
+#include <sys/systm.h>
+#include <sys/bus.h>
+#include <sys/kernel.h>
+#include <sys/module.h>
+#include <sys/malloc.h>
+#include <sys/rman.h>
+#include <sys/timeet.h>
+#include <sys/timetc.h>
+#include <sys/watchdog.h>
+#include <machine/bus.h>
+#include <machine/cpu.h>
+#include <machine/frame.h>
+#include <machine/intr.h>
+
+#include <dev/fdt/fdt_common.h>
+#include <dev/ofw/openfirm.h>
+#include <dev/ofw/ofw_bus.h>
+#include <dev/ofw/ofw_bus_subr.h>
+
+#include <machine/bus.h>
+#include <machine/fdt.h>
+
+#define MCT_CTRL_START (1 << 8)
+#define MCT_CTRL (0x240)
+#define MCT_WRITE_STAT (0x24C)
+
+struct arm_tmr_softc {
+ struct resource *tmr_res[1];
+ bus_space_tag_t bst;
+ bus_space_handle_t bsh;
+};
+
+static struct resource_spec arm_tmr_spec[] = {
+ { SYS_RES_MEMORY, 0, RF_ACTIVE }, /* Timer registers */
+ { -1, 0 }
+};
+
+static int
+arm_tmr_probe(device_t dev)
+{
+ if (!ofw_bus_is_compatible(dev, "exynos,mct"))
+ return (ENXIO);
+
+ device_set_desc(dev, "Exynos MPCore Timer");
+ return (BUS_PROBE_DEFAULT);
+}
+
+static int
+arm_tmr_attach(device_t dev)
+{
+ struct arm_tmr_softc *sc;
+ int reg, i;
+ int mask;
+
+ sc = device_get_softc(dev);
+
+ if (bus_alloc_resources(dev, arm_tmr_spec, sc->tmr_res)) {
+ device_printf(dev, "could not allocate resources\n");
+ return (ENXIO);
+ }
+
+ /* Timer interface */
+ sc->bst = rman_get_bustag(sc->tmr_res[0]);
+ sc->bsh = rman_get_bushandle(sc->tmr_res[0]);
+
+ reg = bus_space_read_4(sc->bst, sc->bsh, MCT_CTRL);
+ reg |= MCT_CTRL_START;
+ bus_space_write_4(sc->bst, sc->bsh, MCT_CTRL, reg);
+
+ mask = (1 << 16);
+
+ /* Wait 10 times until written value is applied */
+ for (i = 0; i < 10; i++) {
+ reg = bus_space_read_4(sc->bst, sc->bsh, MCT_WRITE_STAT);
+ if (reg & mask) {
+ bus_space_write_4(sc->bst, sc->bsh,
+ MCT_WRITE_STAT, mask);
+ return (0);
+ }
+ cpufunc_nullop();
+ }
+
+ /* NOTREACHED */
+
+ panic("Can't enable timer\n");
+}
+
+static device_method_t arm_tmr_methods[] = {
+ DEVMETHOD(device_probe, arm_tmr_probe),
+ DEVMETHOD(device_attach, arm_tmr_attach),
+ { 0, 0 }
+};
+
+static driver_t arm_tmr_driver = {
+ "arch_timer",
+ arm_tmr_methods,
+ sizeof(struct arm_tmr_softc),
+};
+
+static devclass_t arm_tmr_devclass;
+
+DRIVER_MODULE(arch_timer, simplebus, arm_tmr_driver, arm_tmr_devclass, 0, 0);
diff --git a/sys/arm/samsung/exynos/bus_space.c b/sys/arm/samsung/exynos/bus_space.c
new file mode 100644
index 0000000..8d21f55
--- /dev/null
+++ b/sys/arm/samsung/exynos/bus_space.c
@@ -0,0 +1,153 @@
+/*-
+ * Copyright (c) 2012 Damjan Marion <damjan.marion@gmail.com>
+ * All rights reserved.
+ *
+ * Redistribution and use in source and binary forms, with or without
+ * modification, are permitted provided that the following conditions
+ * are met:
+ * 1. Redistributions of source code must retain the above copyright
+ * notice, this list of conditions and the following disclaimer.
+ * 2. Redistributions in binary form must reproduce the above copyright
+ * notice, this list of conditions and the following disclaimer in the
+ * documentation and/or other materials provided with the distribution.
+ *
+ * THIS SOFTWARE IS PROVIDED BY THE AUTHOR AND CONTRIBUTORS ``AS IS'' AND
+ * ANY EXPRESS OR IMPLIED WARRANTIES, INCLUDING, BUT NOT LIMITED TO, THE
+ * IMPLIED WARRANTIES OF MERCHANTABILITY AND FITNESS FOR A PARTICULAR PURPOSE
+ * ARE DISCLAIMED. IN NO EVENT SHALL THE AUTHOR OR CONTRIBUTORS BE LIABLE
+ * FOR ANY DIRECT, INDIRECT, INCIDENTAL, SPECIAL, EXEMPLARY, OR CONSEQUENTIAL
+ * DAMAGES (INCLUDING, BUT NOT LIMITED TO, PROCUREMENT OF SUBSTITUTE GOODS
+ * OR SERVICES; LOSS OF USE, DATA, OR PROFITS; OR BUSINESS INTERRUPTION)
+ * HOWEVER CAUSED AND ON ANY THEORY OF LIABILITY, WHETHER IN CONTRACT, STRICT
+ * LIABILITY, OR TORT (INCLUDING NEGLIGENCE OR OTHERWISE) ARISING IN ANY WAY
+ * OUT OF THE USE OF THIS SOFTWARE, EVEN IF ADVISED OF THE POSSIBILITY OF
+ * SUCH DAMAGE.
+ */
+
+#include <sys/cdefs.h>
+__FBSDID("$FreeBSD$");
+
+#include <sys/param.h>
+#include <sys/systm.h>
+#include <sys/bus.h>
+#include <sys/kernel.h>
+#include <sys/malloc.h>
+
+#include <machine/bus.h>
+
+/* Prototypes for all the bus_space structure functions */
+bs_protos(generic);
+bs_protos(generic_armv4);
+
+/*
+ * The bus space tag. This is constant for all instances, so
+ * we never have to explicitly "create" it.
+ */
+static struct bus_space _base_tag = {
+ /* cookie */
+ (void *) 0,
+
+ /* mapping/unmapping */
+ generic_bs_map,
+ generic_bs_unmap,
+ generic_bs_subregion,
+
+ /* allocation/deallocation */
+ generic_bs_alloc,
+ generic_bs_free,
+
+ /* barrier */
+ generic_bs_barrier,
+
+ /* read (single) */
+ generic_bs_r_1,
+ generic_armv4_bs_r_2,
+ generic_bs_r_4,
+ NULL,
+
+ /* read multiple */
+ generic_bs_rm_1,
+ generic_armv4_bs_rm_2,
+ generic_bs_rm_4,
+ NULL,
+
+ /* read region */
+ generic_bs_rr_1,
+ generic_armv4_bs_rr_2,
+ generic_bs_rr_4,
+ NULL,
+
+ /* write (single) */
+ generic_bs_w_1,
+ generic_armv4_bs_w_2,
+ generic_bs_w_4,
+ NULL,
+
+ /* write multiple */
+ generic_bs_wm_1,
+ generic_armv4_bs_wm_2,
+ generic_bs_wm_4,
+ NULL,
+
+ /* write region */
+ NULL,
+ NULL,
+ NULL,
+ NULL,
+
+ /* set multiple */
+ NULL,
+ NULL,
+ NULL,
+ NULL,
+
+ /* set region */
+ NULL,
+ NULL,
+ NULL,
+ NULL,
+
+ /* copy */
+ NULL,
+ NULL,
+ NULL,
+ NULL,
+
+ /* read stream (single) */
+ NULL,
+ NULL,
+ NULL,
+ NULL,
+
+ /* read multiple stream */
+ NULL,
+ generic_armv4_bs_rm_2, /* bus_space_read_multi_stream_2 */
+ NULL,
+ NULL,
+
+ /* read region stream */
+ NULL,
+ NULL,
+ NULL,
+ NULL,
+
+ /* write stream (single) */
+ NULL,
+ NULL,
+ NULL,
+ NULL,
+
+ /* write multiple stream */
+ NULL,
+ generic_armv4_bs_wm_2, /* bus_space_write_multi_stream_2 */
+ NULL,
+ NULL,
+
+ /* write region stream */
+ NULL,
+ NULL,
+ NULL,
+ NULL
+};
+
+bus_space_tag_t fdtbus_bs_tag = &_base_tag;
diff --git a/sys/arm/samsung/exynos/common.c b/sys/arm/samsung/exynos/common.c
new file mode 100644
index 0000000..658efc4
--- /dev/null
+++ b/sys/arm/samsung/exynos/common.c
@@ -0,0 +1,73 @@
+/*-
+ * Copyright (c) 2013 Ruslan Bukin <br@bsdpad.com>
+ * All rights reserved.
+ *
+ * Redistribution and use in source and binary forms, with or without
+ * modification, are permitted provided that the following conditions
+ * are met:
+ * 1. Redistributions of source code must retain the above copyright
+ * notice, this list of conditions and the following disclaimer.
+ * 2. Redistributions in binary form must reproduce the above copyright
+ * notice, this list of conditions and the following disclaimer in the
+ * documentation and/or other materials provided with the distribution.
+ *
+ * THIS SOFTWARE IS PROVIDED BY THE AUTHOR AND CONTRIBUTORS ``AS IS'' AND
+ * ANY EXPRESS OR IMPLIED WARRANTIES, INCLUDING, BUT NOT LIMITED TO, THE
+ * IMPLIED WARRANTIES OF MERCHANTABILITY AND FITNESS FOR A PARTICULAR PURPOSE
+ * ARE DISCLAIMED. IN NO EVENT SHALL THE AUTHOR OR CONTRIBUTORS BE LIABLE
+ * FOR ANY DIRECT, INDIRECT, INCIDENTAL, SPECIAL, EXEMPLARY, OR CONSEQUENTIAL
+ * DAMAGES (INCLUDING, BUT NOT LIMITED TO, PROCUREMENT OF SUBSTITUTE GOODS
+ * OR SERVICES; LOSS OF USE, DATA, OR PROFITS; OR BUSINESS INTERRUPTION)
+ * HOWEVER CAUSED AND ON ANY THEORY OF LIABILITY, WHETHER IN CONTRACT, STRICT
+ * LIABILITY, OR TORT (INCLUDING NEGLIGENCE OR OTHERWISE) ARISING IN ANY WAY
+ * OUT OF THE USE OF THIS SOFTWARE, EVEN IF ADVISED OF THE POSSIBILITY OF
+ * SUCH DAMAGE.
+ */
+
+#include <sys/cdefs.h>
+__FBSDID("$FreeBSD$");
+
+#include <sys/param.h>
+#include <sys/systm.h>
+#include <sys/bus.h>
+#include <sys/kernel.h>
+
+#include <dev/fdt/fdt_common.h>
+#include <dev/ofw/openfirm.h>
+
+#include <machine/bus.h>
+#include <machine/fdt.h>
+
+void
+cpu_reset(void)
+{
+ bus_space_handle_t bsh;
+
+ bus_space_map(fdtbus_bs_tag, 0x10040400, 0x1000, 0, &bsh);
+ bus_space_write_4(fdtbus_bs_tag, bsh, 0, 1);
+
+ while (1);
+}
+
+struct fdt_fixup_entry fdt_fixup_table[] = {
+ { NULL, NULL }
+};
+
+static int
+fdt_pic_decode_ic(phandle_t node, pcell_t *intr, int *interrupt, int *trig,
+ int *pol)
+{
+
+ if (!fdt_is_compatible(node, "arm,gic"))
+ return (ENXIO);
+
+ *interrupt = fdt32_to_cpu(intr[0]);
+ *trig = INTR_TRIGGER_CONFORM;
+ *pol = INTR_POLARITY_CONFORM;
+ return (0);
+}
+
+fdt_pic_decode_t fdt_pic_table[] = {
+ &fdt_pic_decode_ic,
+ NULL
+};
diff --git a/sys/arm/samsung/exynos/ehci_exynos5.c b/sys/arm/samsung/exynos/ehci_exynos5.c
new file mode 100644
index 0000000..ada0405
--- /dev/null
+++ b/sys/arm/samsung/exynos/ehci_exynos5.c
@@ -0,0 +1,367 @@
+/*-
+ * Copyright (c) 2013 Ruslan Bukin <br@bsdpad.com>
+ * All rights reserved.
+ *
+ * Redistribution and use in source and binary forms, with or without
+ * modification, are permitted provided that the following conditions
+ * are met:
+ * 1. Redistributions of source code must retain the above copyright
+ * notice, this list of conditions and the following disclaimer.
+ * 2. Redistributions in binary form must reproduce the above copyright
+ * notice, this list of conditions and the following disclaimer in the
+ * documentation and/or other materials provided with the distribution.
+ *
+ * THIS SOFTWARE IS PROVIDED BY THE AUTHOR AND CONTRIBUTORS ``AS IS'' AND
+ * ANY EXPRESS OR IMPLIED WARRANTIES, INCLUDING, BUT NOT LIMITED TO, THE
+ * IMPLIED WARRANTIES OF MERCHANTABILITY AND FITNESS FOR A PARTICULAR PURPOSE
+ * ARE DISCLAIMED. IN NO EVENT SHALL THE AUTHOR OR CONTRIBUTORS BE LIABLE
+ * FOR ANY DIRECT, INDIRECT, INCIDENTAL, SPECIAL, EXEMPLARY, OR CONSEQUENTIAL
+ * DAMAGES (INCLUDING, BUT NOT LIMITED TO, PROCUREMENT OF SUBSTITUTE GOODS
+ * OR SERVICES; LOSS OF USE, DATA, OR PROFITS; OR BUSINESS INTERRUPTION)
+ * HOWEVER CAUSED AND ON ANY THEORY OF LIABILITY, WHETHER IN CONTRACT, STRICT
+ * LIABILITY, OR TORT (INCLUDING NEGLIGENCE OR OTHERWISE) ARISING IN ANY WAY
+ * OUT OF THE USE OF THIS SOFTWARE, EVEN IF ADVISED OF THE POSSIBILITY OF
+ * SUCH DAMAGE.
+ */
+
+#include <sys/cdefs.h>
+__FBSDID("$FreeBSD$");
+
+#include "opt_bus.h"
+
+#include <sys/param.h>
+#include <sys/systm.h>
+#include <sys/kernel.h>
+#include <sys/module.h>
+#include <sys/bus.h>
+#include <sys/condvar.h>
+#include <sys/rman.h>
+
+#include <dev/ofw/ofw_bus.h>
+#include <dev/ofw/ofw_bus_subr.h>
+
+#include <dev/usb/usb.h>
+#include <dev/usb/usbdi.h>
+#include <dev/usb/usb_busdma.h>
+#include <dev/usb/usb_process.h>
+#include <dev/usb/usb_controller.h>
+#include <dev/usb/usb_bus.h>
+#include <dev/usb/controller/ehci.h>
+#include <dev/usb/controller/ehcireg.h>
+
+#include <dev/fdt/fdt_common.h>
+
+#include <machine/bus.h>
+#include <machine/resource.h>
+
+#include "opt_platform.h"
+
+/* GPIO control */
+#define GPIO_CON(x, v) ((v) << ((x) * 4))
+#define GPIO_MASK 0xf
+#define GPIO_OUTPUT 1
+#define GPIO_INPUT 0
+#define GPX3CON 0x0C60
+#define GPX3DAT 0x0C64
+#define PIN_USB 5
+
+/* PWR control */
+#define EXYNOS5_PWR_USBHOST_PHY 0x708
+#define PHY_POWER_ON 1
+#define PHY_POWER_OFF 0
+
+/* SYSREG */
+#define EXYNOS5_SYSREG_USB2_PHY 0x230
+#define USB2_MODE_HOST 0x1
+
+/* USB HOST */
+#define HOST_CTRL_CLK_24MHZ (5 << 16)
+#define HOST_CTRL_CLK_MASK (7 << 16)
+#define HOST_CTRL_SIDDQ (1 << 6)
+#define HOST_CTRL_SLEEP (1 << 5)
+#define HOST_CTRL_SUSPEND (1 << 4)
+#define HOST_CTRL_RESET_LINK (1 << 1)
+#define HOST_CTRL_RESET_PHY (1 << 0)
+#define HOST_CTRL_RESET_PHY_ALL (1 << 31)
+
+/* Forward declarations */
+static int exynos_ehci_attach(device_t dev);
+static int exynos_ehci_detach(device_t dev);
+static int exynos_ehci_probe(device_t dev);
+
+struct exynos_ehci_softc {
+ ehci_softc_t base;
+ struct resource *res[6];
+ bus_space_tag_t host_bst;
+ bus_space_tag_t pwr_bst;
+ bus_space_tag_t sysreg_bst;
+ bus_space_tag_t gpio_bst;
+ bus_space_handle_t host_bsh;
+ bus_space_handle_t pwr_bsh;
+ bus_space_handle_t sysreg_bsh;
+ bus_space_handle_t gpio_bsh;
+
+};
+
+static struct resource_spec exynos_ehci_spec[] = {
+ { SYS_RES_MEMORY, 0, RF_ACTIVE },
+ { SYS_RES_MEMORY, 1, RF_ACTIVE },
+ { SYS_RES_MEMORY, 2, RF_ACTIVE },
+ { SYS_RES_MEMORY, 3, RF_ACTIVE },
+ { SYS_RES_MEMORY, 4, RF_ACTIVE },
+ { SYS_RES_IRQ, 0, RF_ACTIVE },
+ { -1, 0 }
+};
+
+static device_method_t ehci_methods[] = {
+ /* Device interface */
+ DEVMETHOD(device_probe, exynos_ehci_probe),
+ DEVMETHOD(device_attach, exynos_ehci_attach),
+ DEVMETHOD(device_detach, exynos_ehci_detach),
+ DEVMETHOD(device_suspend, bus_generic_suspend),
+ DEVMETHOD(device_resume, bus_generic_resume),
+ DEVMETHOD(device_shutdown, bus_generic_shutdown),
+
+ /* Bus interface */
+ DEVMETHOD(bus_print_child, bus_generic_print_child),
+
+ { 0, 0 }
+};
+
+/* kobj_class definition */
+static driver_t ehci_driver = {
+ "ehci",
+ ehci_methods,
+ sizeof(ehci_softc_t)
+};
+
+static devclass_t ehci_devclass;
+
+DRIVER_MODULE(ehci, simplebus, ehci_driver, ehci_devclass, 0, 0);
+MODULE_DEPEND(ehci, usb, 1, 1, 1);
+
+/*
+ * Public methods
+ */
+static int
+exynos_ehci_probe(device_t dev)
+{
+
+ if (ofw_bus_is_compatible(dev, "exynos,usb-ehci") == 0)
+ return (ENXIO);
+
+ device_set_desc(dev, "Exynos integrated USB controller");
+ return (BUS_PROBE_DEFAULT);
+}
+
+static int
+gpio_ctrl(struct exynos_ehci_softc *esc, int dir, int power)
+{
+ int reg;
+
+ /* Power control */
+ reg = bus_space_read_4(esc->gpio_bst, esc->gpio_bsh, GPX3DAT);
+ reg &= ~(1 << PIN_USB);
+ reg |= (power << PIN_USB);
+ bus_space_write_4(esc->gpio_bst, esc->gpio_bsh, GPX3DAT, reg);
+
+ /* Input/Output control */
+ reg = bus_space_read_4(esc->gpio_bst, esc->gpio_bsh, GPX3CON);
+ reg &= ~GPIO_CON(PIN_USB, GPIO_MASK);
+ reg |= GPIO_CON(PIN_USB, dir);
+ bus_space_write_4(esc->gpio_bst, esc->gpio_bsh, GPX3CON, reg);
+
+ return (0);
+}
+
+static int
+phy_init(struct exynos_ehci_softc *esc)
+{
+ int reg;
+
+ gpio_ctrl(esc, GPIO_INPUT, 1);
+
+ /* set USB HOST mode */
+ bus_space_write_4(esc->sysreg_bst, esc->sysreg_bsh,
+ EXYNOS5_SYSREG_USB2_PHY, USB2_MODE_HOST);
+
+ /* Power ON phy */
+ bus_space_write_4(esc->pwr_bst, esc->pwr_bsh,
+ EXYNOS5_PWR_USBHOST_PHY, PHY_POWER_ON);
+
+ reg = bus_space_read_4(esc->host_bst, esc->host_bsh, 0x0);
+ reg &= ~(HOST_CTRL_CLK_MASK |
+ HOST_CTRL_RESET_PHY |
+ HOST_CTRL_RESET_PHY_ALL |
+ HOST_CTRL_SIDDQ |
+ HOST_CTRL_SUSPEND |
+ HOST_CTRL_SLEEP);
+
+ reg |= (HOST_CTRL_CLK_24MHZ |
+ HOST_CTRL_RESET_LINK);
+ bus_space_write_4(esc->host_bst, esc->host_bsh, 0x0, reg);
+
+ DELAY(10);
+
+ reg = bus_space_read_4(esc->host_bst, esc->host_bsh, 0x0);
+ reg &= ~(HOST_CTRL_RESET_LINK);
+ bus_space_write_4(esc->host_bst, esc->host_bsh, 0x0, reg);
+
+ gpio_ctrl(esc, GPIO_OUTPUT, 1);
+
+ return (0);
+}
+
+static int
+exynos_ehci_attach(device_t dev)
+{
+ struct exynos_ehci_softc *esc;
+ ehci_softc_t *sc;
+ bus_space_handle_t bsh;
+ int err;
+
+ esc = device_get_softc(dev);
+ sc = &esc->base;
+ sc->sc_bus.parent = dev;
+ sc->sc_bus.devices = sc->sc_devices;
+ sc->sc_bus.devices_max = EHCI_MAX_DEVICES;
+
+ if (bus_alloc_resources(dev, exynos_ehci_spec, esc->res)) {
+ device_printf(dev, "could not allocate resources\n");
+ return (ENXIO);
+ }
+
+ /* EHCI registers */
+ sc->sc_io_tag = rman_get_bustag(esc->res[0]);
+ bsh = rman_get_bushandle(esc->res[0]);
+ sc->sc_io_size = rman_get_size(esc->res[0]);
+
+ /* EHCI HOST ctrl registers */
+ esc->host_bst = rman_get_bustag(esc->res[1]);
+ esc->host_bsh = rman_get_bushandle(esc->res[1]);
+
+ /* PWR registers */
+ esc->pwr_bst = rman_get_bustag(esc->res[2]);
+ esc->pwr_bsh = rman_get_bushandle(esc->res[2]);
+
+ /* SYSREG */
+ esc->sysreg_bst = rman_get_bustag(esc->res[3]);
+ esc->sysreg_bsh = rman_get_bushandle(esc->res[3]);
+
+ /* GPIO */
+ esc->gpio_bst = rman_get_bustag(esc->res[4]);
+ esc->gpio_bsh = rman_get_bushandle(esc->res[4]);
+
+ /* get all DMA memory */
+ if (usb_bus_mem_alloc_all(&sc->sc_bus, USB_GET_DMA_TAG(dev),
+ &ehci_iterate_hw_softc))
+ return (ENXIO);
+
+ /*
+ * Set handle to USB related registers subregion used by
+ * generic EHCI driver.
+ */
+ err = bus_space_subregion(sc->sc_io_tag, bsh, 0x0,
+ sc->sc_io_size, &sc->sc_io_hdl);
+ if (err != 0)
+ return (ENXIO);
+
+ phy_init(esc);
+
+ /* Setup interrupt handler */
+ err = bus_setup_intr(dev, esc->res[5], INTR_TYPE_BIO | INTR_MPSAFE,
+ NULL, (driver_intr_t *)ehci_interrupt, sc,
+ &sc->sc_intr_hdl);
+ if (err) {
+ device_printf(dev, "Could not setup irq, "
+ "%d\n", err);
+ return (1);
+ }
+
+ /* Add USB device */
+ sc->sc_bus.bdev = device_add_child(dev, "usbus", -1);
+ if (!sc->sc_bus.bdev) {
+ device_printf(dev, "Could not add USB device\n");
+ err = bus_teardown_intr(dev, esc->res[5],
+ sc->sc_intr_hdl);
+ if (err)
+ device_printf(dev, "Could not tear down irq,"
+ " %d\n", err);
+ return (1);
+ }
+ device_set_ivars(sc->sc_bus.bdev, &sc->sc_bus);
+
+ strlcpy(sc->sc_vendor, "Samsung", sizeof(sc->sc_vendor));
+
+ err = ehci_init(sc);
+ if (!err) {
+ sc->sc_flags |= EHCI_SCFLG_DONEINIT;
+ err = device_probe_and_attach(sc->sc_bus.bdev);
+ } else {
+ device_printf(dev, "USB init failed err=%d\n", err);
+
+ device_delete_child(dev, sc->sc_bus.bdev);
+ sc->sc_bus.bdev = NULL;
+
+ err = bus_teardown_intr(dev, esc->res[5],
+ sc->sc_intr_hdl);
+ if (err)
+ device_printf(dev, "Could not tear down irq,"
+ " %d\n", err);
+ return (1);
+ }
+ return (0);
+}
+
+static int
+exynos_ehci_detach(device_t dev)
+{
+ struct exynos_ehci_softc *esc;
+ ehci_softc_t *sc;
+ int err;
+
+ esc = device_get_softc(dev);
+ sc = &esc->base;
+
+ if (sc->sc_flags & EHCI_SCFLG_DONEINIT)
+ return (0);
+
+ /*
+ * only call ehci_detach() after ehci_init()
+ */
+ if (sc->sc_flags & EHCI_SCFLG_DONEINIT) {
+ ehci_detach(sc);
+ sc->sc_flags &= ~EHCI_SCFLG_DONEINIT;
+ }
+
+ /*
+ * Disable interrupts that might have been switched on in
+ * ehci_init.
+ */
+ if (sc->sc_io_tag && sc->sc_io_hdl)
+ bus_space_write_4(sc->sc_io_tag, sc->sc_io_hdl,
+ EHCI_USBINTR, 0);
+
+ if (esc->res[5] && sc->sc_intr_hdl) {
+ err = bus_teardown_intr(dev, esc->res[5],
+ sc->sc_intr_hdl);
+ if (err) {
+ device_printf(dev, "Could not tear down irq,"
+ " %d\n", err);
+ return (err);
+ }
+ sc->sc_intr_hdl = NULL;
+ }
+
+ if (sc->sc_bus.bdev) {
+ device_delete_child(dev, sc->sc_bus.bdev);
+ sc->sc_bus.bdev = NULL;
+ }
+
+ /* During module unload there are lots of children leftover */
+ device_delete_children(dev);
+
+ bus_release_resources(dev, exynos_ehci_spec, esc->res);
+
+ return (0);
+}
diff --git a/sys/arm/samsung/exynos/exynos5_machdep.c b/sys/arm/samsung/exynos/exynos5_machdep.c
new file mode 100644
index 0000000..4927aed
--- /dev/null
+++ b/sys/arm/samsung/exynos/exynos5_machdep.c
@@ -0,0 +1,109 @@
+/*-
+ * Copyright (c) 2013 Ruslan Bukin <br@bsdpad.com>
+ * All rights reserved.
+ *
+ * Redistribution and use in source and binary forms, with or without
+ * modification, are permitted provided that the following conditions
+ * are met:
+ * 1. Redistributions of source code must retain the above copyright
+ * notice, this list of conditions and the following disclaimer.
+ * 2. Redistributions in binary form must reproduce the above copyright
+ * notice, this list of conditions and the following disclaimer in the
+ * documentation and/or other materials provided with the distribution.
+ *
+ * THIS SOFTWARE IS PROVIDED BY THE AUTHOR AND CONTRIBUTORS ``AS IS'' AND
+ * ANY EXPRESS OR IMPLIED WARRANTIES, INCLUDING, BUT NOT LIMITED TO, THE
+ * IMPLIED WARRANTIES OF MERCHANTABILITY AND FITNESS FOR A PARTICULAR PURPOSE
+ * ARE DISCLAIMED. IN NO EVENT SHALL THE AUTHOR OR CONTRIBUTORS BE LIABLE
+ * FOR ANY DIRECT, INDIRECT, INCIDENTAL, SPECIAL, EXEMPLARY, OR CONSEQUENTIAL
+ * DAMAGES (INCLUDING, BUT NOT LIMITED TO, PROCUREMENT OF SUBSTITUTE GOODS
+ * OR SERVICES; LOSS OF USE, DATA, OR PROFITS; OR BUSINESS INTERRUPTION)
+ * HOWEVER CAUSED AND ON ANY THEORY OF LIABILITY, WHETHER IN CONTRACT, STRICT
+ * LIABILITY, OR TORT (INCLUDING NEGLIGENCE OR OTHERWISE) ARISING IN ANY WAY
+ * OUT OF THE USE OF THIS SOFTWARE, EVEN IF ADVISED OF THE POSSIBILITY OF
+ * SUCH DAMAGE.
+ */
+
+#include "opt_ddb.h"
+#include "opt_platform.h"
+
+#include <sys/cdefs.h>
+__FBSDID("$FreeBSD$");
+
+#define _ARM32_BUS_DMA_PRIVATE
+#include <sys/param.h>
+#include <sys/systm.h>
+#include <sys/bus.h>
+
+#include <vm/vm.h>
+#include <vm/pmap.h>
+
+#include <machine/bus.h>
+#include <machine/frame.h> /* For trapframe_t, used in <machine/machdep.h> */
+#include <machine/machdep.h>
+#include <machine/pmap.h>
+
+#include <dev/fdt/fdt_common.h>
+
+#define DEVMAP_BOOTSTRAP_MAP_START 0xE0000000
+
+extern int unmapped_buf_allowed;
+
+vm_offset_t
+initarm_lastaddr(void)
+{
+
+ return (DEVMAP_BOOTSTRAP_MAP_START - ARM_NOCACHE_KVA_SIZE);
+}
+
+void
+initarm_gpio_init(void)
+{
+}
+
+void
+initarm_late_init(void)
+{
+
+ /* XXX: PR arm/180080 */
+ unmapped_buf_allowed = 0;
+}
+
+#define FDT_DEVMAP_MAX (1 + 2 + 1 + 1) /* FIXME */
+static struct pmap_devmap fdt_devmap[FDT_DEVMAP_MAX] = {
+ { 0, 0, 0, 0, 0, }
+};
+
+/*
+ * Construct pmap_devmap[] with DT-derived config data.
+ */
+int
+platform_devmap_init(void)
+{
+ int i;
+
+ i = 0;
+ fdt_devmap[i].pd_va = 0xe2C00000;
+ fdt_devmap[i].pd_pa = 0x12C00000;
+ fdt_devmap[i].pd_size = 0x100000;
+ fdt_devmap[i].pd_prot = VM_PROT_READ | VM_PROT_WRITE;
+ fdt_devmap[i].pd_cache = PTE_NOCACHE;
+ i++;
+
+ pmap_devmap_bootstrap_table = &fdt_devmap[0];
+ return (0);
+}
+
+struct arm32_dma_range *
+bus_dma_get_range(void)
+{
+
+ return (NULL);
+}
+
+int
+bus_dma_get_range_nb(void)
+{
+
+ return (0);
+}
diff --git a/sys/arm/samsung/exynos/exynos5_mp.c b/sys/arm/samsung/exynos/exynos5_mp.c
new file mode 100644
index 0000000..e99b61e
--- /dev/null
+++ b/sys/arm/samsung/exynos/exynos5_mp.c
@@ -0,0 +1,92 @@
+/*-
+ * Copyright (c) 2013 Ruslan Bukin <br@bsdpad.com>
+ * All rights reserved.
+ *
+ * Redistribution and use in source and binary forms, with or without
+ * modification, are permitted provided that the following conditions
+ * are met:
+ * 1. Redistributions of source code must retain the above copyright
+ * notice, this list of conditions and the following disclaimer.
+ * 2. Redistributions in binary form must reproduce the above copyright
+ * notice, this list of conditions and the following disclaimer in the
+ * documentation and/or other materials provided with the distribution.
+ *
+ * THIS SOFTWARE IS PROVIDED BY THE AUTHOR AND CONTRIBUTORS ``AS IS'' AND
+ * ANY EXPRESS OR IMPLIED WARRANTIES, INCLUDING, BUT NOT LIMITED TO, THE
+ * IMPLIED WARRANTIES OF MERCHANTABILITY AND FITNESS FOR A PARTICULAR PURPOSE
+ * ARE DISCLAIMED. IN NO EVENT SHALL THE AUTHOR OR CONTRIBUTORS BE LIABLE
+ * FOR ANY DIRECT, INDIRECT, INCIDENTAL, SPECIAL, EXEMPLARY, OR CONSEQUENTIAL
+ * DAMAGES (INCLUDING, BUT NOT LIMITED TO, PROCUREMENT OF SUBSTITUTE GOODS
+ * OR SERVICES; LOSS OF USE, DATA, OR PROFITS; OR BUSINESS INTERRUPTION)
+ * HOWEVER CAUSED AND ON ANY THEORY OF LIABILITY, WHETHER IN CONTRACT, STRICT
+ * LIABILITY, OR TORT (INCLUDING NEGLIGENCE OR OTHERWISE) ARISING IN ANY WAY
+ * OUT OF THE USE OF THIS SOFTWARE, EVEN IF ADVISED OF THE POSSIBILITY OF
+ * SUCH DAMAGE.
+ */
+
+#include <sys/cdefs.h>
+__FBSDID("$FreeBSD$");
+#include <sys/param.h>
+#include <sys/systm.h>
+#include <sys/bus.h>
+#include <sys/lock.h>
+#include <sys/mutex.h>
+#include <sys/smp.h>
+
+#include <machine/smp.h>
+#include <machine/fdt.h>
+#include <machine/intr.h>
+
+#define EXYNOS_SYSRAM 0x02020000
+
+void mpentry(void);
+void mptramp(void);
+
+void
+platform_mp_init_secondary(void)
+{
+
+ gic_init_secondary();
+}
+
+void
+platform_mp_setmaxid(void)
+{
+
+ mp_maxid = 1;
+}
+
+int
+platform_mp_probe(void)
+{
+
+ mp_ncpus = 2;
+ return (1);
+}
+
+void
+platform_mp_start_ap(void)
+{
+ bus_addr_t sysram;
+ int err;
+
+ err = bus_space_map(fdtbus_bs_tag, EXYNOS_SYSRAM, 0x100, 0, &sysram);
+ if (err != 0)
+ panic("Couldn't map sysram\n");
+
+ bus_space_write_4(fdtbus_bs_tag, sysram, 0x0,
+ pmap_kextract((vm_offset_t)mpentry));
+
+ cpu_idcache_wbinv_all();
+ cpu_l2cache_wbinv_all();
+
+ armv7_sev();
+ bus_space_unmap(fdtbus_bs_tag, sysram, 0x100);
+}
+
+void
+platform_ipi_send(cpuset_t cpus, u_int ipi)
+{
+
+ pic_ipi_send(cpus, ipi);
+}
diff --git a/sys/arm/samsung/exynos/files.exynos5 b/sys/arm/samsung/exynos/files.exynos5
new file mode 100644
index 0000000..e0b7407
--- /dev/null
+++ b/sys/arm/samsung/exynos/files.exynos5
@@ -0,0 +1,24 @@
+# $FreeBSD$
+
+kern/kern_clocksource.c standard
+
+arm/arm/bus_space_generic.c standard
+arm/arm/bus_space_asm_generic.S standard
+arm/arm/cpufunc_asm_armv5.S standard
+arm/arm/cpufunc_asm_arm10.S standard
+arm/arm/cpufunc_asm_arm11.S standard
+arm/arm/cpufunc_asm_armv7.S standard
+arm/arm/irq_dispatch.S standard
+
+arm/arm/gic.c standard
+arm/arm/generic_timer.c standard
+
+arm/samsung/exynos/arch_timer.c standard
+arm/samsung/exynos/exynos5_mp.c optional smp
+arm/samsung/exynos/bus_space.c standard
+arm/samsung/exynos/common.c standard
+arm/samsung/exynos/exynos5_machdep.c standard
+arm/samsung/exynos/uart.c optional uart
+arm/samsung/exynos/ehci_exynos5.c optional ehci
+
+#dev/sdhci/sdhci_fdt.c optional sdhci
diff --git a/sys/arm/samsung/exynos/std.exynos5 b/sys/arm/samsung/exynos/std.exynos5
new file mode 100644
index 0000000..58f692c
--- /dev/null
+++ b/sys/arm/samsung/exynos/std.exynos5
@@ -0,0 +1,23 @@
+# $FreeBSD$
+
+makeoption ARM_LITTLE_ENDIAN
+
+cpu CPU_CORTEXA
+machine arm armv6
+
+options PHYSADDR=0x40000000
+
+makeoptions KERNPHYSADDR=0x40f00000
+options KERNPHYSADDR=0x40f00000
+
+makeoptions KERNVIRTADDR=0xc0f00000
+options KERNVIRTADDR=0xc0f00000
+
+options STARTUP_PAGETABLE_ADDR=0x40100000
+
+options ARM_L2_PIPT
+
+options IPI_IRQ_START=0
+options IPI_IRQ_END=15
+
+files "../samsung/exynos/files.exynos5"
diff --git a/sys/arm/samsung/exynos/uart.c b/sys/arm/samsung/exynos/uart.c
new file mode 100644
index 0000000..eef99ff
--- /dev/null
+++ b/sys/arm/samsung/exynos/uart.c
@@ -0,0 +1,377 @@
+/*
+ * Copyright (c) 2003 Marcel Moolenaar
+ * Copyright (c) 2007-2009 Andrew Turner
+ * Copyright (c) 2013 Ruslan Bukin <br@bsdpad.com>
+ * All rights reserved.
+ *
+ * Redistribution and use in source and binary forms, with or without
+ * modification, are permitted provided that the following conditions
+ * are met:
+ *
+ * 1. Redistributions of source code must retain the above copyright
+ * notice, this list of conditions and the following disclaimer.
+ * 2. Redistributions in binary form must reproduce the above copyright
+ * notice, this list of conditions and the following disclaimer in the
+ * documentation and/or other materials provided with the distribution.
+ *
+ * THIS SOFTWARE IS PROVIDED BY THE AUTHOR ``AS IS'' AND ANY EXPRESS OR
+ * IMPLIED WARRANTIES, INCLUDING, BUT NOT LIMITED TO, THE IMPLIED WARRANTIES
+ * OF MERCHANTABILITY AND FITNESS FOR A PARTICULAR PURPOSE ARE DISCLAIMED.
+ * IN NO EVENT SHALL THE AUTHOR BE LIABLE FOR ANY DIRECT, INDIRECT,
+ * INCIDENTAL, SPECIAL, EXEMPLARY, OR CONSEQUENTIAL DAMAGES (INCLUDING, BUT
+ * NOT LIMITED TO, PROCUREMENT OF SUBSTITUTE GOODS OR SERVICES; LOSS OF USE,
+ * DATA, OR PROFITS; OR BUSINESS INTERRUPTION) HOWEVER CAUSED AND ON ANY
+ * THEORY OF LIABILITY, WHETHER IN CONTRACT, STRICT LIABILITY, OR TORT
+ * (INCLUDING NEGLIGENCE OR OTHERWISE) ARISING IN ANY WAY OUT OF THE USE OF
+ * THIS SOFTWARE, EVEN IF ADVISED OF THE POSSIBILITY OF SUCH DAMAGE.
+ */
+
+#include <sys/cdefs.h>
+__FBSDID("$FreeBSD$");
+
+#include <sys/param.h>
+#include <sys/systm.h>
+#include <sys/bus.h>
+#include <sys/conf.h>
+#include <sys/cons.h>
+#include <sys/tty.h>
+#include <sys/rman.h>
+#include <machine/bus.h>
+#include <machine/intr.h>
+
+#include <dev/uart/uart.h>
+#include <dev/uart/uart_cpu.h>
+#include <dev/uart/uart_bus.h>
+
+#include <arm/samsung/exynos/uart.h>
+
+#include "uart_if.h"
+
+#define DEF_CLK 100000000
+
+static int sscomspeed(long, long);
+static int s3c24x0_uart_param(struct uart_bas *, int, int, int, int);
+
+/*
+ * Low-level UART interface.
+ */
+static int s3c2410_probe(struct uart_bas *bas);
+static void s3c2410_init(struct uart_bas *bas, int, int, int, int);
+static void s3c2410_term(struct uart_bas *bas);
+static void s3c2410_putc(struct uart_bas *bas, int);
+static int s3c2410_rxready(struct uart_bas *bas);
+static int s3c2410_getc(struct uart_bas *bas, struct mtx *mtx);
+
+extern SLIST_HEAD(uart_devinfo_list, uart_devinfo) uart_sysdevs;
+
+static int
+sscomspeed(long speed, long frequency)
+{
+ int x;
+
+ if (speed <= 0 || frequency <= 0)
+ return (-1);
+ x = (frequency / 16) / speed;
+ return (x-1);
+}
+
+static int
+s3c24x0_uart_param(struct uart_bas *bas, int baudrate, int databits,
+ int stopbits, int parity)
+{
+ int brd, ulcon;
+
+ ulcon = 0;
+
+ switch(databits) {
+ case 5:
+ ulcon |= ULCON_LENGTH_5;
+ break;
+ case 6:
+ ulcon |= ULCON_LENGTH_6;
+ break;
+ case 7:
+ ulcon |= ULCON_LENGTH_7;
+ break;
+ case 8:
+ ulcon |= ULCON_LENGTH_8;
+ break;
+ default:
+ return (EINVAL);
+ }
+
+ switch (parity) {
+ case UART_PARITY_NONE:
+ ulcon |= ULCON_PARITY_NONE;
+ break;
+ case UART_PARITY_ODD:
+ ulcon |= ULCON_PARITY_ODD;
+ break;
+ case UART_PARITY_EVEN:
+ ulcon |= ULCON_PARITY_EVEN;
+ break;
+ case UART_PARITY_MARK:
+ case UART_PARITY_SPACE:
+ default:
+ return (EINVAL);
+ }
+
+ if (stopbits == 2)
+ ulcon |= ULCON_STOP;
+
+ uart_setreg(bas, SSCOM_ULCON, ulcon);
+
+ brd = sscomspeed(baudrate, bas->rclk);
+ uart_setreg(bas, SSCOM_UBRDIV, brd);
+
+ return (0);
+}
+
+struct uart_ops uart_s3c2410_ops = {
+ .probe = s3c2410_probe,
+ .init = s3c2410_init,
+ .term = s3c2410_term,
+ .putc = s3c2410_putc,
+ .rxready = s3c2410_rxready,
+ .getc = s3c2410_getc,
+};
+
+static int
+s3c2410_probe(struct uart_bas *bas)
+{
+
+ return (0);
+}
+
+static void
+s3c2410_init(struct uart_bas *bas, int baudrate, int databits, int stopbits,
+ int parity)
+{
+
+ if (bas->rclk == 0)
+ bas->rclk = DEF_CLK;
+
+ KASSERT(bas->rclk != 0, ("s3c2410_init: Invalid rclk"));
+
+ uart_setreg(bas, SSCOM_UCON, 0);
+ uart_setreg(bas, SSCOM_UFCON,
+ UFCON_TXTRIGGER_8 | UFCON_RXTRIGGER_8 |
+ UFCON_TXFIFO_RESET | UFCON_RXFIFO_RESET |
+ UFCON_FIFO_ENABLE);
+ s3c24x0_uart_param(bas, baudrate, databits, stopbits, parity);
+
+ /* Enable UART. */
+ uart_setreg(bas, SSCOM_UCON, UCON_TXMODE_INT | UCON_RXMODE_INT |
+ UCON_TOINT);
+ uart_setreg(bas, SSCOM_UMCON, UMCON_RTS);
+}
+
+static void
+s3c2410_term(struct uart_bas *bas)
+{
+ /* XXX */
+}
+
+static void
+s3c2410_putc(struct uart_bas *bas, int c)
+{
+
+ while ((bus_space_read_4(bas->bst, bas->bsh, SSCOM_UFSTAT) &
+ UFSTAT_TXFULL) == UFSTAT_TXFULL)
+ continue;
+
+ uart_setreg(bas, SSCOM_UTXH, c);
+}
+
+static int
+s3c2410_rxready(struct uart_bas *bas)
+{
+
+ return ((uart_getreg(bas, SSCOM_UTRSTAT) & UTRSTAT_RXREADY) ==
+ UTRSTAT_RXREADY);
+}
+
+static int
+s3c2410_getc(struct uart_bas *bas, struct mtx *mtx)
+{
+ int utrstat;
+
+ utrstat = bus_space_read_1(bas->bst, bas->bsh, SSCOM_UTRSTAT);
+ while (!(utrstat & UTRSTAT_RXREADY)) {
+ utrstat = bus_space_read_1(bas->bst, bas->bsh, SSCOM_UTRSTAT);
+ continue;
+ }
+
+ return (bus_space_read_1(bas->bst, bas->bsh, SSCOM_URXH));
+}
+
+static int s3c2410_bus_probe(struct uart_softc *sc);
+static int s3c2410_bus_attach(struct uart_softc *sc);
+static int s3c2410_bus_flush(struct uart_softc *, int);
+static int s3c2410_bus_getsig(struct uart_softc *);
+static int s3c2410_bus_ioctl(struct uart_softc *, int, intptr_t);
+static int s3c2410_bus_ipend(struct uart_softc *);
+static int s3c2410_bus_param(struct uart_softc *, int, int, int, int);
+static int s3c2410_bus_receive(struct uart_softc *);
+static int s3c2410_bus_setsig(struct uart_softc *, int);
+static int s3c2410_bus_transmit(struct uart_softc *);
+
+static kobj_method_t s3c2410_methods[] = {
+ KOBJMETHOD(uart_probe, s3c2410_bus_probe),
+ KOBJMETHOD(uart_attach, s3c2410_bus_attach),
+ KOBJMETHOD(uart_flush, s3c2410_bus_flush),
+ KOBJMETHOD(uart_getsig, s3c2410_bus_getsig),
+ KOBJMETHOD(uart_ioctl, s3c2410_bus_ioctl),
+ KOBJMETHOD(uart_ipend, s3c2410_bus_ipend),
+ KOBJMETHOD(uart_param, s3c2410_bus_param),
+ KOBJMETHOD(uart_receive, s3c2410_bus_receive),
+ KOBJMETHOD(uart_setsig, s3c2410_bus_setsig),
+ KOBJMETHOD(uart_transmit, s3c2410_bus_transmit),
+
+ {0, 0 }
+};
+
+int
+s3c2410_bus_probe(struct uart_softc *sc)
+{
+
+ sc->sc_txfifosz = 16;
+ sc->sc_rxfifosz = 16;
+
+ return (0);
+}
+
+static int
+s3c2410_bus_attach(struct uart_softc *sc)
+{
+
+ sc->sc_hwiflow = 0;
+ sc->sc_hwoflow = 0;
+
+ return (0);
+}
+
+static int
+s3c2410_bus_transmit(struct uart_softc *sc)
+{
+ int i;
+ int reg;
+
+ uart_lock(sc->sc_hwmtx);
+
+ for (i = 0; i < sc->sc_txdatasz; i++) {
+ s3c2410_putc(&sc->sc_bas, sc->sc_txbuf[i]);
+ uart_barrier(&sc->sc_bas);
+ }
+
+ sc->sc_txbusy = 1;
+
+ uart_unlock(sc->sc_hwmtx);
+
+ /* unmask TX interrupt */
+ reg = bus_space_read_4(sc->sc_bas.bst, sc->sc_bas.bsh, SSCOM_UINTM);
+ reg &= ~(1 << 2);
+ bus_space_write_4(sc->sc_bas.bst, sc->sc_bas.bsh, SSCOM_UINTM, reg);
+
+ return (0);
+}
+
+static int
+s3c2410_bus_setsig(struct uart_softc *sc, int sig)
+{
+
+ return (0);
+}
+
+static int
+s3c2410_bus_receive(struct uart_softc *sc)
+{
+
+ uart_rx_put(sc, uart_getreg(&sc->sc_bas, SSCOM_URXH));
+ return (0);
+}
+
+static int
+s3c2410_bus_param(struct uart_softc *sc, int baudrate, int databits,
+ int stopbits, int parity)
+{
+ int error;
+
+ if (sc->sc_bas.rclk == 0)
+ sc->sc_bas.rclk = DEF_CLK;
+
+ KASSERT(sc->sc_bas.rclk != 0, ("s3c2410_init: Invalid rclk"));
+
+ uart_lock(sc->sc_hwmtx);
+ error = s3c24x0_uart_param(&sc->sc_bas, baudrate, databits, stopbits,
+ parity);
+ uart_unlock(sc->sc_hwmtx);
+
+ return (error);
+}
+
+static int
+s3c2410_bus_ipend(struct uart_softc *sc)
+{
+ uint32_t ints;
+ uint32_t txempty, rxready;
+ int reg;
+ int ipend;
+
+ uart_lock(sc->sc_hwmtx);
+ ints = bus_space_read_4(sc->sc_bas.bst, sc->sc_bas.bsh, SSCOM_UINTP);
+ bus_space_write_4(sc->sc_bas.bst, sc->sc_bas.bsh, SSCOM_UINTP, ints);
+
+ txempty = (1 << 2);
+ rxready = (1 << 0);
+
+ ipend = 0;
+ if ((ints & txempty) > 0) {
+ if (sc->sc_txbusy != 0)
+ ipend |= SER_INT_TXIDLE;
+
+ /* mask TX interrupt */
+ reg = bus_space_read_4(sc->sc_bas.bst, sc->sc_bas.bsh,
+ SSCOM_UINTM);
+ reg |= (1 << 2);
+ bus_space_write_4(sc->sc_bas.bst, sc->sc_bas.bsh,
+ SSCOM_UINTM, reg);
+ }
+
+ if ((ints & rxready) > 0) {
+ ipend |= SER_INT_RXREADY;
+ }
+
+ uart_unlock(sc->sc_hwmtx);
+ return (ipend);
+}
+
+static int
+s3c2410_bus_flush(struct uart_softc *sc, int what)
+{
+
+ return (0);
+}
+
+static int
+s3c2410_bus_getsig(struct uart_softc *sc)
+{
+
+ return (0);
+}
+
+static int
+s3c2410_bus_ioctl(struct uart_softc *sc, int request, intptr_t data)
+{
+
+ return (EINVAL);
+}
+
+struct uart_class uart_s3c2410_class = {
+ "s3c2410 class",
+ s3c2410_methods,
+ 1,
+ .uc_ops = &uart_s3c2410_ops,
+ .uc_range = 8,
+ .uc_rclk = 0,
+};
diff --git a/sys/arm/samsung/exynos/uart.h b/sys/arm/samsung/exynos/uart.h
new file mode 100644
index 0000000..06afeff1
--- /dev/null
+++ b/sys/arm/samsung/exynos/uart.h
@@ -0,0 +1,126 @@
+/* $NetBSD: s3c2xx0reg.h,v 1.4 2004/02/12 03:47:29 bsh Exp $ */
+
+/*-
+ * Copyright (c) 2002, 2003 Fujitsu Component Limited
+ * Copyright (c) 2002, 2003 Genetec Corporation
+ * All rights reserved.
+ *
+ * Redistribution and use in source and binary forms, with or without
+ * modification, are permitted provided that the following conditions
+ * are met:
+ * 1. Redistributions of source code must retain the above copyright
+ * notice, this list of conditions and the following disclaimer.
+ * 2. Redistributions in binary form must reproduce the above copyright
+ * notice, this list of conditions and the following disclaimer in the
+ * documentation and/or other materials provided with the distribution.
+ * 3. Neither the name of The Fujitsu Component Limited nor the name of
+ * Genetec corporation may not be used to endorse or promote products
+ * derived from this software without specific prior written permission.
+ *
+ * THIS SOFTWARE IS PROVIDED BY FUJITSU COMPONENT LIMITED AND GENETEC
+ * CORPORATION ``AS IS'' AND ANY EXPRESS OR IMPLIED WARRANTIES,
+ * INCLUDING, BUT NOT LIMITED TO, THE IMPLIED WARRANTIES OF
+ * MERCHANTABILITY AND FITNESS FOR A PARTICULAR PURPOSE ARE
+ * DISCLAIMED. IN NO EVENT SHALL FUJITSU COMPONENT LIMITED OR GENETEC
+ * CORPORATION BE LIABLE FOR ANY DIRECT, INDIRECT, INCIDENTAL,
+ * SPECIAL, EXEMPLARY, OR CONSEQUENTIAL DAMAGES (INCLUDING, BUT NOT
+ * LIMITED TO, PROCUREMENT OF SUBSTITUTE GOODS OR SERVICES; LOSS OF
+ * USE, DATA, OR PROFITS; OR BUSINESS INTERRUPTION) HOWEVER CAUSED AND
+ * ON ANY THEORY OF LIABILITY, WHETHER IN CONTRACT, STRICT LIABILITY,
+ * OR TORT (INCLUDING NEGLIGENCE OR OTHERWISE) ARISING IN ANY WAY OUT
+ * OF THE USE OF THIS SOFTWARE, EVEN IF ADVISED OF THE POSSIBILITY OF
+ * SUCH DAMAGE.
+ *
+ * $FreeBSD$
+ */
+
+/* s3c2410-specific registers */
+#define UMCON_AFC (1 << 4) /* auto flow control */
+#define UMSTAT_DCTS (1 << 2) /* CTS change */
+#define ULCON_IR (1 << 6)
+#define ULCON_PARITY_SHIFT 3
+
+/*
+ * Exynos-specific
+ *
+ * UFSTAT_TXFULL register differs between Exynos and others.
+ * Others have UFSTAT_TXFULL (1 << 9)
+ */
+#define UFSTAT_TXFULL (1 << 24)
+
+#define SSCOM_UINTM 0x038
+#define SSCOM_UINTP 0x030
+
+/* common for s3c2800 and s3c24x0 */
+#define SSCOM_ULCON 0x00 /* UART line control */
+#define ULCON_PARITY_NONE (0 << ULCON_PARITY_SHIFT)
+#define ULCON_PARITY_ODD (4 << ULCON_PARITY_SHIFT)
+#define ULCON_PARITY_EVEN (5 << ULCON_PARITY_SHIFT)
+#define ULCON_PARITY_ONE (6 << ULCON_PARITY_SHIFT)
+#define ULCON_PARITY_ZERO (7 << ULCON_PARITY_SHIFT)
+#define ULCON_STOP (1 << 2)
+#define ULCON_LENGTH_5 0
+#define ULCON_LENGTH_6 1
+#define ULCON_LENGTH_7 2
+#define ULCON_LENGTH_8 3
+#define SSCOM_UCON 0x04 /* UART control */
+#define UCON_TXINT_TYPE (1 << 9) /* Tx interrupt. 0=pulse,1=level */
+#define UCON_TXINT_TYPE_LEVEL UCON_TXINT_TYPE
+#define UCON_TXINT_TYPE_PULSE 0
+#define UCON_RXINT_TYPE (1 << 8) /* Rx interrupt */
+#define UCON_RXINT_TYPE_LEVEL UCON_RXINT_TYPE
+#define UCON_RXINT_TYPE_PULSE 0
+#define UCON_TOINT (1 << 7) /* Rx timeout interrupt */
+#define UCON_ERRINT (1 << 6) /* receive error interrupt */
+#define UCON_LOOP (1 << 5) /* loopback */
+#define UCON_SBREAK (1 << 4) /* send break */
+#define UCON_TXMODE_DISABLE (0 << 2)
+#define UCON_TXMODE_INT (1 << 2)
+#define UCON_TXMODE_DMA (2 << 2)
+#define UCON_TXMODE_MASK (3 << 2)
+#define UCON_RXMODE_DISABLE (0 << 0)
+#define UCON_RXMODE_INT (1 << 0)
+#define UCON_RXMODE_DMA (2 << 0)
+#define UCON_RXMODE_MASK (3 << 0)
+#define SSCOM_UFCON 0x08 /* FIFO control */
+#define UFCON_TXTRIGGER_0 (0 << 6)
+#define UFCON_TXTRIGGER_4 (1 << 6)
+#define UFCON_TXTRIGGER_8 (2 << 6)
+#define UFCON_TXTRIGGER_16 (3 << 6)
+#define UFCON_RXTRIGGER_4 (0 << 4)
+#define UFCON_RXTRIGGER_8 (1 << 4)
+#define UFCON_RXTRIGGER_12 (2 << 4)
+#define UFCON_RXTRIGGER_16 (3 << 4)
+#define UFCON_TXFIFO_RESET (1 << 2)
+#define UFCON_RXFIFO_RESET (1 << 1)
+#define UFCON_FIFO_ENABLE (1 << 0)
+#define SSCOM_UMCON 0x0c /* MODEM control */
+#define UMCON_RTS (1 << 0) /* Request to send */
+#define SSCOM_UTRSTAT 0x10 /* Status register */
+#define UTRSTAT_TXSHIFTER_EMPTY ( 1<< 2)
+#define UTRSTAT_TXEMPTY (1 << 1) /* TX fifo or buffer empty */
+#define UTRSTAT_RXREADY (1 << 0) /* RX fifo or buffer is not empty */
+#define SSCOM_UERSTAT 0x14 /* Error status register */
+#define UERSTAT_BREAK (1 << 3) /* Break signal, not 2410 */
+#define UERSTAT_FRAME (1 << 2) /* Frame error */
+#define UERSTAT_PARITY (1 << 1) /* Parity error, not 2410 */
+#define UERSTAT_OVERRUN (1 << 0) /* Overrun */
+#define UERSTAT_ALL_ERRORS \
+ (UERSTAT_OVERRUN|UERSTAT_BREAK|UERSTAT_FRAME|UERSTAT_PARITY)
+#define SSCOM_UFSTAT 0x18 /* Fifo status register */
+#define UFSTAT_RXFULL (1 <<8) /* Rx fifo full */
+#define UFSTAT_TXCOUNT_SHIFT 4 /* TX FIFO count */
+#define UFSTAT_TXCOUNT (0x0f << UFSTAT_TXCOUNT_SHIFT)
+#define UFSTAT_RXCOUNT_SHIFT 0 /* RX FIFO count */
+#define UFSTAT_RXCOUNT (0x0f << UFSTAT_RXCOUNT_SHIFT)
+#define SSCOM_UMSTAT 0x1c /* Modem status register */
+#define UMSTAT_CTS (1 << 0) /* Clear to send */
+#if _BYTE_ORDER == _LITTLE_ENDIAN
+#define SSCOM_UTXH 0x20 /* Transmit data register */
+#define SSCOM_URXH 0x24 /* Receive data register */
+#else
+#define SSCOM_UTXH 0x23 /* Transmit data register */
+#define SSCOM_URXH 0x27 /* Receive data register */
+#endif
+#define SSCOM_UBRDIV 0x28 /* baud-reate divisor */
+#define SSCOM_SIZE 0x2c
diff --git a/sys/arm/ti/am335x/am335x_pwm.c b/sys/arm/ti/am335x/am335x_pwm.c
index c8135ca..d64e08f 100644
--- a/sys/arm/ti/am335x/am335x_pwm.c
+++ b/sys/arm/ti/am335x/am335x_pwm.c
@@ -36,6 +36,7 @@ __FBSDID("$FreeBSD$");
#include <sys/mutex.h>
#include <sys/resource.h>
#include <sys/rman.h>
+#include <sys/sysctl.h>
#include <machine/bus.h>
@@ -50,6 +51,9 @@ __FBSDID("$FreeBSD$");
#include "am335x_pwm.h"
#include "am335x_scm.h"
+/* In ticks */
+#define DEFAULT_PWM_PERIOD 1000
+
#define PWM_LOCK(_sc) mtx_lock(&(_sc)->sc_mtx)
#define PWM_UNLOCK(_sc) mtx_unlock(&(_sc)->sc_mtx)
#define PWM_LOCK_INIT(_sc) mtx_init(&(_sc)->sc_mtx, \
@@ -96,15 +100,24 @@ static struct resource_spec am335x_pwm_mem_spec[] = {
#define ECCTL2_TSCTRSTOP_FREERUN (1 << 4)
#define EPWM_TBCTL 0x00
+#define TBCTL_FREERUN (2 << 14)
#define TBCTL_PHDIR_UP (1 << 13)
#define TBCTL_PHDIR_DOWN (0 << 13)
#define TBCTL_CLKDIV(x) ((x) << 10)
+#define TBCTL_CLKDIV_MASK (3 << 10)
#define TBCTL_HSPCLKDIV(x) ((x) << 7)
+#define TBCTL_HSPCLKDIV_MASK (3 << 7)
#define TBCTL_SYNCOSEL_DISABLED (3 << 4)
#define TBCTL_PRDLD_SHADOW (0 << 3)
#define TBCTL_PRDLD_IMMEDIATE (0 << 3)
#define TBCTL_PHSEN_ENABLED (1 << 2)
#define TBCTL_PHSEN_DISABLED (0 << 2)
+#define TBCTL_CTRMODE_MASK (3)
+#define TBCTL_CTRMODE_UP (0 << 0)
+#define TBCTL_CTRMODE_DOWN (1 << 0)
+#define TBCTL_CTRMODE_UPDOWN (2 << 0)
+#define TBCTL_CTRMODE_FREEZE (3 << 0)
+
#define EPWM_TBSTS 0x02
#define EPWM_TBPHSHR 0x04
#define EPWM_TBPHS 0x06
@@ -130,10 +143,14 @@ static struct resource_spec am335x_pwm_mem_spec[] = {
/* CMPCTL_LOADAMODE_ZERO */
#define EPWM_AQCTLA 0x16
#define EPWM_AQCTLB 0x18
-#define AQCTL_CAU_NONE (0 << 0)
-#define AQCTL_CAU_CLEAR (1 << 0)
-#define AQCTL_CAU_SET (2 << 0)
-#define AQCTL_CAU_TOGGLE (3 << 0)
+#define AQCTL_CBU_NONE (0 << 8)
+#define AQCTL_CBU_CLEAR (1 << 8)
+#define AQCTL_CBU_SET (2 << 8)
+#define AQCTL_CBU_TOGGLE (3 << 8)
+#define AQCTL_CAU_NONE (0 << 4)
+#define AQCTL_CAU_CLEAR (1 << 4)
+#define AQCTL_CAU_SET (2 << 4)
+#define AQCTL_CAU_TOGGLE (3 << 4)
#define AQCTL_ZRO_NONE (0 << 0)
#define AQCTL_ZRO_CLEAR (1 << 0)
#define AQCTL_ZRO_SET (2 << 0)
@@ -141,6 +158,15 @@ static struct resource_spec am335x_pwm_mem_spec[] = {
#define EPWM_AQSFRC 0x1a
#define EPWM_AQCSFRC 0x1c
+/* Trip-Zone module */
+#define EPWM_TZCTL 0x28
+#define EPWM_TZFLG 0x2C
+/* High-Resolution PWM */
+#define EPWM_HRCTL 0x40
+#define HRCTL_DELMODE_BOTH 3
+#define HRCTL_DELMODE_FALL 2
+#define HRCTL_DELMODE_RISE 1
+
static device_probe_t am335x_pwm_probe;
static device_attach_t am335x_pwm_attach;
static device_detach_t am335x_pwm_detach;
@@ -150,6 +176,13 @@ struct am335x_pwm_softc {
struct mtx sc_mtx;
struct resource *sc_mem_res[4];
int sc_id;
+ /* sysctl for configuration */
+ struct sysctl_oid *sc_period_oid;
+ struct sysctl_oid *sc_chanA_oid;
+ struct sysctl_oid *sc_chanB_oid;
+ uint32_t sc_pwm_period;
+ uint32_t sc_pwm_dutyA;
+ uint32_t sc_pwm_dutyB;
};
static device_method_t am335x_pwm_methods[] = {
@@ -209,6 +242,71 @@ am335x_pwm_config_ecas(int unit, int period, int duty)
}
static int
+am335x_pwm_sysctl_duty(SYSCTL_HANDLER_ARGS)
+{
+ struct am335x_pwm_softc *sc = (struct am335x_pwm_softc*)arg1;
+ int error;
+ uint32_t duty;
+
+ if (oidp == sc->sc_chanA_oid)
+ duty = sc->sc_pwm_dutyA;
+ else
+ duty = sc->sc_pwm_dutyB;
+ error = sysctl_handle_int(oidp, &duty, 0, req);
+
+ if (error != 0 || req->newptr == NULL)
+ return (error);
+
+ if (duty > sc->sc_pwm_period) {
+ device_printf(sc->sc_dev, "Duty cycle can't be greater then period\n");
+ return (EINVAL);
+ }
+
+ PWM_LOCK(sc);
+ if (oidp == sc->sc_chanA_oid) {
+ sc->sc_pwm_dutyA = duty;
+ EPWM_WRITE2(sc, EPWM_CMPA, sc->sc_pwm_dutyA);
+ }
+ else {
+ sc->sc_pwm_dutyB = duty;
+ EPWM_WRITE2(sc, EPWM_CMPB, sc->sc_pwm_dutyB);
+ }
+ PWM_UNLOCK(sc);
+
+ return (error);
+}
+
+static int
+am335x_pwm_sysctl_period(SYSCTL_HANDLER_ARGS)
+{
+ struct am335x_pwm_softc *sc = (struct am335x_pwm_softc*)arg1;
+ int error;
+ uint32_t period;
+
+ period = sc->sc_pwm_period;
+ error = sysctl_handle_int(oidp, &period, 0, req);
+
+ if (error != 0 || req->newptr == NULL)
+ return (error);
+
+ if (period < 1)
+ return (EINVAL);
+
+ if ((period < sc->sc_pwm_dutyA) || (period < sc->sc_pwm_dutyB)) {
+ device_printf(sc->sc_dev, "Period can't be less then duty cycle\n");
+ return (EINVAL);
+ }
+
+
+ PWM_LOCK(sc);
+ sc->sc_pwm_period = period;
+ EPWM_WRITE2(sc, EPWM_TBPRD, period - 1);
+ PWM_UNLOCK(sc);
+
+ return (error);
+}
+
+static int
am335x_pwm_probe(device_t dev)
{
if (!ofw_bus_is_compatible(dev, "ti,am335x-pwm"))
@@ -227,6 +325,8 @@ am335x_pwm_attach(device_t dev)
uint32_t reg;
phandle_t node;
pcell_t did;
+ struct sysctl_ctx_list *ctx;
+ struct sysctl_oid *tree;
sc = device_get_softc(dev);
sc->sc_dev = dev;
@@ -252,6 +352,47 @@ am335x_pwm_attach(device_t dev)
reg |= (1 << sc->sc_id);
ti_scm_reg_write_4(SCM_PWMSS_CTRL, reg);
+ /* Init backlight interface */
+ ctx = device_get_sysctl_ctx(sc->sc_dev);
+ tree = device_get_sysctl_tree(sc->sc_dev);
+
+ sc->sc_period_oid = SYSCTL_ADD_PROC(ctx, SYSCTL_CHILDREN(tree), OID_AUTO,
+ "period", CTLTYPE_INT | CTLFLAG_RW, sc, 0,
+ am335x_pwm_sysctl_period, "I", "PWM period");
+
+ sc->sc_chanA_oid = SYSCTL_ADD_PROC(ctx, SYSCTL_CHILDREN(tree), OID_AUTO,
+ "dutyA", CTLTYPE_INT | CTLFLAG_RW, sc, 0,
+ am335x_pwm_sysctl_duty, "I", "Channel A duty cycles");
+
+ sc->sc_chanB_oid = SYSCTL_ADD_PROC(ctx, SYSCTL_CHILDREN(tree), OID_AUTO,
+ "dutyB", CTLTYPE_INT | CTLFLAG_RW, sc, 0,
+ am335x_pwm_sysctl_duty, "I", "Channel B duty cycles");
+
+
+ /* CONFIGURE EPWM1 */
+ reg = EPWM_READ2(sc, EPWM_TBCTL);
+ reg &= ~(TBCTL_CLKDIV_MASK | TBCTL_HSPCLKDIV_MASK);
+ EPWM_WRITE2(sc, EPWM_TBCTL, reg);
+
+ sc->sc_pwm_period = DEFAULT_PWM_PERIOD;
+ sc->sc_pwm_dutyA = 0;
+ sc->sc_pwm_dutyB = 0;
+
+ EPWM_WRITE2(sc, EPWM_TBPRD, sc->sc_pwm_period - 1);
+ EPWM_WRITE2(sc, EPWM_CMPA, sc->sc_pwm_dutyA);
+ EPWM_WRITE2(sc, EPWM_CMPB, sc->sc_pwm_dutyB);
+
+ EPWM_WRITE2(sc, EPWM_AQCTLA, (AQCTL_ZRO_SET | AQCTL_CAU_CLEAR));
+ EPWM_WRITE2(sc, EPWM_AQCTLB, (AQCTL_ZRO_SET | AQCTL_CBU_CLEAR));
+
+ /* START EPWM */
+ reg &= ~TBCTL_CTRMODE_MASK;
+ reg |= TBCTL_CTRMODE_UP | TBCTL_FREERUN;
+ EPWM_WRITE2(sc, EPWM_TBCTL, reg);
+
+ EPWM_WRITE2(sc, EPWM_TZCTL, 0xf);
+ reg = EPWM_READ2(sc, EPWM_TZFLG);
+
return (0);
fail:
PWM_LOCK_DESTROY(sc);
diff --git a/sys/arm/versatile/versatile_pci.c b/sys/arm/versatile/versatile_pci.c
index 1ef92ef..131209a 100644
--- a/sys/arm/versatile/versatile_pci.c
+++ b/sys/arm/versatile/versatile_pci.c
@@ -173,16 +173,17 @@ versatile_pci_attach(device_t dev)
/*
* Setup memory windows
*/
- versatile_pci_core_write_4(PCI_CORE_IMAP0, (PCI_IO_WINDOW >> 11));
- versatile_pci_core_write_4(PCI_CORE_IMAP1, (PCI_NPREFETCH_WINDOW >> 11));
- versatile_pci_core_write_4(PCI_CORE_IMAP2, (PCI_PREFETCH_WINDOW >> 11));
+ versatile_pci_core_write_4(PCI_CORE_IMAP0, (PCI_IO_WINDOW >> 28));
+ versatile_pci_core_write_4(PCI_CORE_IMAP1, (PCI_NPREFETCH_WINDOW >> 28));
+ versatile_pci_core_write_4(PCI_CORE_IMAP2, (PCI_PREFETCH_WINDOW >> 28));
/*
* XXX: this is SDRAM offset >> 28
+ * Unused as of QEMU 1.5
*/
- versatile_pci_core_write_4(PCI_CORE_SMAP0, 0);
- versatile_pci_core_write_4(PCI_CORE_SMAP1, 0);
- versatile_pci_core_write_4(PCI_CORE_SMAP2, 0);
+ versatile_pci_core_write_4(PCI_CORE_SMAP0, (PCI_IO_WINDOW >> 28));
+ versatile_pci_core_write_4(PCI_CORE_SMAP1, (PCI_NPREFETCH_WINDOW >> 28));
+ versatile_pci_core_write_4(PCI_CORE_SMAP2, (PCI_NPREFETCH_WINDOW >> 28));
versatile_pci_sys_write_4(SYS_PCICTL, 1);
@@ -307,7 +308,7 @@ versatile_pci_alloc_resource(device_t bus, device_t child, int type, int *rid,
struct resource *rv;
struct rman *rm;
- printf("Alloc resources %d, %08lx..%08lx, %ld\n", type, start, end, count);
+ dprintf("Alloc resources %d, %08lx..%08lx, %ld\n", type, start, end, count);
switch (type) {
case SYS_RES_IOPORT:
@@ -344,20 +345,23 @@ versatile_pci_activate_resource(device_t bus, device_t child, int type, int rid,
struct resource *r)
{
vm_offset_t vaddr;
- int res = (BUS_ACTIVATE_RESOURCE(device_get_parent(bus),
- child, type, rid, r));
-
- if (!res) {
- switch(type) {
- case SYS_RES_MEMORY:
- case SYS_RES_IOPORT:
- vaddr = (vm_offset_t)pmap_mapdev(rman_get_start(r),
- rman_get_size(r));
- rman_set_bushandle(r, vaddr);
- rman_set_bustag(r, versatile_bus_space_pcimem);
- break;
- }
+ int res;
+
+ switch(type) {
+ case SYS_RES_MEMORY:
+ case SYS_RES_IOPORT:
+ vaddr = (vm_offset_t)pmap_mapdev(rman_get_start(r),
+ rman_get_size(r));
+ rman_set_bushandle(r, vaddr);
+ rman_set_bustag(r, versatile_bus_space_pcimem);
+ res = rman_activate_resource(r);
+ break;
+ case SYS_RES_IRQ:
+ res = (BUS_ACTIVATE_RESOURCE(device_get_parent(bus),
+ child, type, rid, r));
+ break;
}
+
return (res);
}
diff --git a/sys/arm/versatile/versatile_sic.c b/sys/arm/versatile/versatile_sic.c
index d3eee6e..f401c30 100644
--- a/sys/arm/versatile/versatile_sic.c
+++ b/sys/arm/versatile/versatile_sic.c
@@ -110,7 +110,7 @@ versatile_sic_attach(device_t dev)
* Let PCI and Ethernet interrupts pass through
* IRQ25, IRQ27..IRQ31
*/
- pass_irqs = (7 << 27) | (1 << 25);
+ pass_irqs = (0x1f << 27) | (1 << 25);
sic_write_4(sc, SIC_PICENSET, pass_irqs);
return (0);
diff --git a/sys/boot/fdt/dts/am335x.dtsi b/sys/boot/fdt/dts/am335x.dtsi
index 482de9c..7a210b1 100644
--- a/sys/boot/fdt/dts/am335x.dtsi
+++ b/sys/boot/fdt/dts/am335x.dtsi
@@ -155,7 +155,7 @@
compatible = "ti,am335x-pwm";
#address-cells = <1>;
#size-cells = <1>;
- reg = < 0x48300000 0x100 /* PWMSS */
+ reg = < 0x48300000 0x100 /* PWMSS0 */
0x48300100 0x80 /* eCAP0 */
0x48300180 0x80 /* eQEP0 */
0x48300200 0x60 /* ePWM0 */
@@ -165,6 +165,34 @@
pwm-device-id = <0>;
};
+ pwm@48302000 {
+ compatible = "ti,am335x-pwm";
+ #address-cells = <1>;
+ #size-cells = <1>;
+ reg = < 0x48302000 0x100 /* PWMSS1 */
+ 0x48302100 0x80 /* eCAP1 */
+ 0x48302180 0x80 /* eQEP1 */
+ 0x48302200 0x60 /* ePWM1 */
+ >;
+ interrupts = <87 59>; /* ePWM1INT, ePWM1_TZINT */
+ interrupt-parent = <&AINTC>;
+ pwm-device-id = <1>;
+ };
+
+ pwm@48304000 {
+ compatible = "ti,am335x-pwm";
+ #address-cells = <1>;
+ #size-cells = <1>;
+ reg = < 0x48304000 0x100 /* PWMSS2 */
+ 0x48304100 0x80 /* eCAP2 */
+ 0x48304180 0x80 /* eQEP2 */
+ 0x48304200 0x60 /* ePWM2 */
+ >;
+ interrupts = <88 60>; /* ePWM2INT, ePWM2_TZINT */
+ interrupt-parent = <&AINTC>;
+ pwm-device-id = <2>;
+ };
+
lcd: lcd@4830e000 {
#address-cells = <1>;
#size-cells = <0>;
diff --git a/sys/boot/fdt/dts/bcm2835-rpi-b.dts b/sys/boot/fdt/dts/bcm2835.dtsi
index 80bbef0..8078ee4 100644
--- a/sys/boot/fdt/dts/bcm2835-rpi-b.dts
+++ b/sys/boot/fdt/dts/bcm2835.dtsi
@@ -1,19 +1,33 @@
/*
+ * Copyright (c) 2012 Oleksandr Tymoshenko <gonzo@bluezbox.com>
+ *
+ * Redistribution and use in source and binary forms, with or without
+ * modification, are permitted provided that the following conditions
+ * are met:
+ * 1. Redistributions of source code must retain the above copyright
+ * notice, this list of conditions and the following disclaimer.
+ * 2. Redistributions in binary form must reproduce the above copyright
+ * notice, this list of conditions and the following disclaimer in the
+ * documentation and/or other materials provided with the distribution.
+ *
+ * THIS SOFTWARE IS PROVIDED BY THE AUTHOR AND CONTRIBUTORS ``AS IS'' AND
+ * ANY EXPRESS OR IMPLIED WARRANTIES, INCLUDING, BUT NOT LIMITED TO, THE
+ * IMPLIED WARRANTIES OF MERCHANTABILITY AND FITNESS FOR A PARTICULAR PURPOSE
+ * ARE DISCLAIMED. IN NO EVENT SHALL THE AUTHOR OR CONTRIBUTORS BE LIABLE
+ * FOR ANY DIRECT, INDIRECT, INCIDENTAL, SPECIAL, EXEMPLARY, OR CONSEQUENTIAL
+ * DAMAGES (INCLUDING, BUT NOT LIMITED TO, PROCUREMENT OF SUBSTITUTE GOODS
+ * OR SERVICES; LOSS OF USE, DATA, OR PROFITS; OR BUSINESS INTERRUPTION)
+ * HOWEVER CAUSED AND ON ANY THEORY OF LIABILITY, WHETHER IN CONTRACT, STRICT
+ * LIABILITY, OR TORT (INCLUDING NEGLIGENCE OR OTHERWISE) ARISING IN ANY WAY
+ * OUT OF THE USE OF THIS SOFTWARE, EVEN IF ADVISED OF THE POSSIBILITY OF
+ * SUCH DAMAGE.
+ *
* $FreeBSD$
*/
-/dts-v1/;
-/memreserve/ 0x08000000 0x08000000; /* Set by VideoCore */
/ {
- model = "Raspberry Pi Model B (BCM2835)";
#address-cells = <1>;
#size-cells = <1>;
- compatible = "raspberrypi,model-b", "broadcom,bcm2835-vc", "broadcom,bcm2708-vc";
-
- system {
- revision = <0>; /* Set by VideoCore */
- serial = <0 0>; /* Set by VideoCore */
- };
cpus {
cpu@0 {
@@ -22,7 +36,7 @@
};
- axi {
+ SOC: axi {
compatible = "simple-bus";
#address-cells = <1>;
#size-cells = <1>;
@@ -30,7 +44,8 @@
ranges = <0 0x20000000 0x01000000>;
intc: interrupt-controller {
- compatible = "broadcom,bcm2835-armctrl-ic", "broadcom,bcm2708-armctrl-ic";
+ compatible = "broadcom,bcm2835-armctrl-ic",
+ "broadcom,bcm2708-armctrl-ic";
reg = <0xB200 0x200>;
interrupt-controller;
@@ -87,7 +102,8 @@
};
timer {
- compatible = "broadcom,bcm2835-system-timer", "broadcom,bcm2708-system-timer";
+ compatible = "broadcom,bcm2835-system-timer",
+ "broadcom,bcm2708-system-timer";
reg = <0x3000 0x1000>;
interrupts = <8 9 10 11>;
interrupt-parent = <&intc>;
@@ -104,15 +120,18 @@
};
watchdog0 {
- compatible = "broadcom,bcm2835-wdt", "broadcom,bcm2708-wdt";
+ compatible = "broadcom,bcm2835-wdt",
+ "broadcom,bcm2708-wdt";
reg = <0x10001c 0x0c>; /* 0x1c, 0x20, 0x24 */
};
gpio: gpio {
- compatible = "broadcom,bcm2835-gpio", "broadcom,bcm2708-gpio";
+ compatible = "broadcom,bcm2835-gpio",
+ "broadcom,bcm2708-gpio";
reg = <0x200000 0xb0>;
- /* Unusual arrangement of interrupts (determined by testing)
+ /* Unusual arrangement of interrupts
+ * (determined by testing)
* 17: Bank 0 (GPIOs 0-31)
* 19: Bank 1 (GPIOs 32-53)
* 18: Bank 2
@@ -130,301 +149,256 @@
pinctrl-names = "default";
pinctrl-0 = <&pins_reserved>;
- /* pins that can short 3.3V to GND in output mode: 46-47
- * pins used by VideoCore: 48-53
+ /* Pins that can short 3.3V to GND in output mode: 46-47
+ * Pins used by VideoCore: 48-53
*/
- broadcom,read-only = <46>, <47>, <48>, <49>, <50>, <51>, <52>, <53>;
+ broadcom,read-only = <46>, <47>, <48>, <49>, <50>,
+ <51>, <52>, <53>;
/* BSC0 */
pins_bsc0_a: bsc0_a {
broadcom,pins = <0>, <1>;
- broadcom,function = "ALT0";
};
pins_bsc0_b: bsc0_b {
broadcom,pins = <28>, <29>;
- broadcom,function = "ALT0";
};
pins_bsc0_c: bsc0_c {
broadcom,pins = <44>, <45>;
- broadcom,function = "ALT1";
};
/* BSC1 */
pins_bsc1_a: bsc1_a {
broadcom,pins = <2>, <3>;
- broadcom,function = "ALT0";
};
pins_bsc1_b: bsc1_b {
broadcom,pins = <44>, <45>;
- broadcom,function = "ALT2";
};
/* GPCLK0 */
pins_gpclk0_a: gpclk0_a {
broadcom,pins = <4>;
- broadcom,function = "ALT0";
};
pins_gpclk0_b: gpclk0_b {
broadcom,pins = <20>;
- broadcom,function = "ALT5";
};
pins_gpclk0_c: gpclk0_c {
broadcom,pins = <32>;
- broadcom,function = "ALT0";
};
pins_gpclk0_d: gpclk0_d {
broadcom,pins = <34>;
- broadcom,function = "ALT0";
};
/* GPCLK1 */
pins_gpclk1_a: gpclk1_a {
broadcom,pins = <5>;
- broadcom,function = "ALT0";
};
pins_gpclk1_b: gpclk1_b {
broadcom,pins = <21>;
- broadcom,function = "ALT5";
};
pins_gpclk1_c: gpclk1_c {
broadcom,pins = <42>;
- broadcom,function = "ALT0";
};
pins_gpclk1_d: gpclk1_d {
broadcom,pins = <44>;
- broadcom,function = "ALT0";
};
/* GPCLK2 */
pins_gpclk2_a: gpclk2_a {
broadcom,pins = <6>;
- broadcom,function = "ALT0";
};
pins_gpclk2_b: gpclk2_b {
broadcom,pins = <43>;
- broadcom,function = "ALT0";
};
/* SPI0 */
pins_spi0_a: spi0_a {
broadcom,pins = <7>, <8>, <9>, <10>, <11>;
- broadcom,function = "ALT0";
};
pins_spi0_b: spi0_b {
broadcom,pins = <35>, <36>, <37>, <38>, <39>;
- broadcom,function = "ALT0";
};
/* PWM */
pins_pwm0_a: pwm0_a {
broadcom,pins = <12>;
- broadcom,function = "ALT0";
};
pins_pwm0_b: pwm0_b {
broadcom,pins = <18>;
- broadcom,function = "ALT5";
};
pins_pwm0_c: pwm0_c {
broadcom,pins = <40>;
- broadcom,function = "ALT0";
};
pins_pwm1_a: pwm1_a {
broadcom,pins = <13>;
- broadcom,function = "ALT0";
};
pins_pwm1_b: pwm1_b {
broadcom,pins = <19>;
- broadcom,function = "ALT5";
};
pins_pwm1_c: pwm1_c {
broadcom,pins = <41>;
- broadcom,function = "ALT0";
};
pins_pwm1_d: pwm1_d {
broadcom,pins = <45>;
- broadcom,function = "ALT0";
};
/* UART0 */
pins_uart0_a: uart0_a {
broadcom,pins = <14>, <15>;
- broadcom,function = "ALT0";
};
pins_uart0_b: uart0_b {
broadcom,pins = <32>, <33>;
- broadcom,function = "ALT3";
};
pins_uart0_c: uart0_c {
broadcom,pins = <36>, <37>;
- broadcom,function = "ALT2";
};
pins_uart0_fc_a: uart0_fc_a {
broadcom,pins = <16>, <17>;
- broadcom,function = "ALT3";
};
pins_uart0_fc_b: uart0_fc_b {
broadcom,pins = <30>, <31>;
- broadcom,function = "ALT3";
};
pins_uart0_fc_c: uart0_fc_c {
broadcom,pins = <39>, <38>;
- broadcom,function = "ALT2";
};
/* PCM */
pins_pcm_a: pcm_a {
broadcom,pins = <18>, <19>, <20>, <21>;
- broadcom,function = "ALT0";
};
pins_pcm_b: pcm_b {
broadcom,pins = <28>, <29>, <30>, <31>;
- broadcom,function = "ALT2";
};
/* Secondary Address Bus */
pins_sm_addr_a: sm_addr_a {
broadcom,pins = <5>, <4>, <3>, <2>, <1>, <0>;
- broadcom,function = "ALT1";
};
pins_sm_addr_b: sm_addr_b {
- broadcom,pins = <33>, <32>, <31>, <30>, <29>, <28>;
- broadcom,function = "ALT1";
+ broadcom,pins = <33>, <32>, <31>, <30>, <29>,
+ <28>;
};
pins_sm_ctl_a: sm_ctl_a {
broadcom,pins = <6>, <7>;
- broadcom,function = "ALT1";
};
pins_sm_ctl_b: sm_ctl_b {
broadcom,pins = <34>, <35>;
- broadcom,function = "ALT1";
};
pins_sm_data_8bit_a: sm_data_8bit_a {
- broadcom,pins = <8>, <9>, <10>, <11>, <12>, <13>, <14>, <15>;
- broadcom,function = "ALT1";
+ broadcom,pins = <8>, <9>, <10>, <11>, <12>,
+ <13>, <14>, <15>;
};
pins_sm_data_8bit_b: sm_data_8bit_b {
- broadcom,pins = <36>, <37>, <38>, <39>, <40>, <41>, <42>, <43>;
- broadcom,function = "ALT1";
+ broadcom,pins = <36>, <37>, <38>, <39>, <40>,
+ <41>, <42>, <43>;
};
pins_sm_data_16bit: sm_data_16bit {
- broadcom,pins = <16>, <17>, <18>, <19>, <20>, <21>, <22>, <23>;
- broadcom,function = "ALT1";
+ broadcom,pins = <16>, <17>, <18>, <19>, <20>,
+ <21>, <22>, <23>;
};
pins_sm_data_18bit: sm_data_18bit {
broadcom,pins = <24>, <25>;
- broadcom,function = "ALT1";
};
/* BSCSL */
pins_bscsl: bscsl {
broadcom,pins = <18>, <19>;
- broadcom,function = "ALT3";
};
/* SPISL */
pins_spisl: spisl {
broadcom,pins = <18>, <19>, <20>, <21>;
- broadcom,function = "ALT3";
};
/* SPI1 */
pins_spi1: spi1 {
- broadcom,pins = <16>, <17>, <18>, <19>, <20>, <21>;
- broadcom,function = "ALT4";
+ broadcom,pins = <16>, <17>, <18>, <19>, <20>,
+ <21>;
};
/* UART1 */
pins_uart1_a: uart1_a {
broadcom,pins = <14>, <15>;
- broadcom,function = "ALT5";
};
pins_uart1_b: uart1_b {
broadcom,pins = <32>, <33>;
- broadcom,function = "ALT5";
};
pins_uart1_c: uart1_c {
broadcom,pins = <40>, <41>;
- broadcom,function = "ALT5";
};
pins_uart1_fc_a: uart1_fc_a {
broadcom,pins = <16>, <17>;
- broadcom,function = "ALT5";
};
pins_uart1_fc_b: uart1_fc_b {
broadcom,pins = <30>, <31>;
- broadcom,function = "ALT5";
};
pins_uart1_fc_c: uart1_fc_c {
broadcom,pins = <43>, <42>;
- broadcom,function = "ALT5";
};
/* SPI2 */
pins_spi2: spi2 {
- broadcom,pins = <40>, <41>, <42>, <43>, <44>, <45>;
- broadcom,function = "ALT4";
+ broadcom,pins = <40>, <41>, <42>, <43>, <44>,
+ <45>;
};
/* ARM JTAG */
pins_arm_jtag_trst: arm_jtag_trst {
broadcom,pins = <22>;
- broadcom,function = "ALT4";
};
pins_arm_jtag_a: arm_jtag_a {
broadcom,pins = <4>, <5>, <6>, <12>, <13>;
- broadcom,function = "ALT5";
};
pins_arm_jtag_b: arm_jtag_b {
broadcom,pins = <23>, <24>, <25>, <26>, <27>;
- broadcom,function = "ALT4";
};
/* Reserved */
pins_reserved: reserved {
- broadcom,pins = <48>, <49>, <50>, <51>, <52>, <53>;
- broadcom,function = "ALT3";
+ broadcom,pins = <48>, <49>, <50>, <51>, <52>,
+ <53>;
};
};
dma: dma {
- compatible = "broadcom,bcm2835-dma", "broadcom,bcm2708-dma";
+ compatible = "broadcom,bcm2835-dma",
+ "broadcom,bcm2708-dma";
reg = <0x7000 0x1000>, <0xE05000 0x1000>;
interrupts = <24 25 26 27 28 29 30 31 32 33 34 35 36>;
interrupt-parent = <&intc>;
@@ -433,7 +407,8 @@
};
vc_mbox: mbox {
- compatible = "broadcom,bcm2835-mbox", "broadcom,bcm2708-mbox";
+ compatible = "broadcom,bcm2835-mbox",
+ "broadcom,bcm2708-mbox";
reg = <0xB880 0x40>;
interrupts = <1>;
interrupt-parent = <&intc>;
@@ -450,7 +425,8 @@
};
sdhci {
- compatible = "broadcom,bcm2835-sdhci", "broadcom,bcm2708-sdhci";
+ compatible = "broadcom,bcm2835-sdhci",
+ "broadcom,bcm2708-sdhci";
reg = <0x300000 0x100>;
interrupts = <70>;
interrupt-parent = <&intc>;
@@ -459,7 +435,9 @@
};
uart0: uart0 {
- compatible = "broadcom,bcm2835-uart", "broadcom,bcm2708-uart", "arm,pl011", "arm,primecell";
+ compatible = "broadcom,bcm2835-uart",
+ "broadcom,bcm2708-uart", "arm,pl011",
+ "arm,primecell";
reg = <0x201000 0x1000>;
interrupts = <65>;
interrupt-parent = <&intc>;
@@ -476,104 +454,15 @@
};
usb {
- compatible = "broadcom,bcm2835-usb", "broadcom,bcm2708-usb", "synopsys,designware-hs-otg2";
+ compatible = "broadcom,bcm2835-usb",
+ "broadcom,bcm2708-usb",
+ "synopsys,designware-hs-otg2";
reg = <0x980000 0x20000>;
interrupts = <17>;
interrupt-parent = <&intc>;
#address-cells = <1>;
#size-cells = <0>;
- hub {
- compatible = "usb,hub", "usb,device";
- reg = <0x00000001>;
- #address-cells = <1>;
- #size-cells = <0>;
- ethernet {
- compatible = "net,ethernet", "usb,device";
- reg = <0x00000001>;
- mac-address = [00 00 00 00 00 00];
- };
- };
- };
-
- };
-
- memory {
- device_type = "memory";
- reg = <0 0x08000000>; /* 128MB */
- };
-
- display {
- compatible = "broadcom,bcm2835-fb", "broadcom,bcm2708-fb";
-
- broadcom,vc-mailbox = <&vc_mbox>;
- broadcom,vc-channel = <1>;
-
- broadcom,width = <0>; /* Set by VideoCore */
- broadcom,height = <0>; /* Set by VideoCore */
- broadcom,depth = <0>; /* Set by VideoCore */
- };
-
- leds {
- compatible = "gpio-leds";
-
- ok {
- label = "ok";
- gpios = <&gpio 16 1>;
-
- /* Don't change this - it configures
- * how the led driver determines if
- * the led is on or off when it loads.
- */
- default-state = "keep";
-
- /* This is the real default state. */
- linux,default-trigger = "default-on";
- };
- };
-
- power: regulator {
- compatible = "broadcom,bcm2835-power-mgr", "broadcom,bcm2708-power-mgr", "simple-bus";
- #address-cells = <1>;
- #size-cells = <0>;
-
- broadcom,vc-mailbox = <&vc_mbox>;
- broadcom,vc-channel = <0>;
-
- regulator-name = "VideoCore";
- regulator-min-microvolt = <5000000>;
- regulator-max-microvolt = <5000000>;
- regulator-always-on = <1>;
-
- sd_card_power: regulator@0 {
- compatible = "broadcom,bcm2835-power-dev", "broadcom,bcm2708-power-dev";
- reg = <0>;
-
- vin-supply = <&power>;
- regulator-name = "SD Card";
- regulator-min-microvolt = <3300000>;
- regulator-max-microvolt = <3300000>;
- };
-
- /* This is for the controller itself, not the root port */
- usb_hcd_power: regulator@3 {
- compatible = "broadcom,bcm2835-power-dev", "broadcom,bcm2708-power-dev";
- reg = <3>;
-
- vin-supply = <&power>;
- regulator-name = "USB HCD";
- regulator-min-microvolt = <5000000>;
- regulator-max-microvolt = <5000000>;
};
- };
-
- aliases {
- uart0 = &uart0;
- };
- chosen {
- bootargs = ""; /* Set by VideoCore */
- stdin = "uart0";
- stdout = "uart0";
};
-
};
diff --git a/sys/boot/fdt/dts/exynos5250-arndale.dts b/sys/boot/fdt/dts/exynos5250-arndale.dts
new file mode 100644
index 0000000..67cadcb
--- /dev/null
+++ b/sys/boot/fdt/dts/exynos5250-arndale.dts
@@ -0,0 +1,45 @@
+/*-
+ * Copyright (c) 2013 Ruslan Bukin <br@bsdpad.com>
+ * All rights reserved.
+ *
+ * Redistribution and use in source and binary forms, with or without
+ * modification, are permitted provided that the following conditions
+ * are met:
+ * 1. Redistributions of source code must retain the above copyright
+ * notice, this list of conditions and the following disclaimer.
+ * 2. Redistributions in binary form must reproduce the above copyright
+ * notice, this list of conditions and the following disclaimer in the
+ * documentation and/or other materials provided with the distribution.
+ *
+ * THIS SOFTWARE IS PROVIDED BY THE AUTHOR AND CONTRIBUTORS ``AS IS'' AND
+ * ANY EXPRESS OR IMPLIED WARRANTIES, INCLUDING, BUT NOT LIMITED TO, THE
+ * IMPLIED WARRANTIES OF MERCHANTABILITY AND FITNESS FOR A PARTICULAR PURPOSE
+ * ARE DISCLAIMED. IN NO EVENT SHALL THE AUTHOR OR CONTRIBUTORS BE LIABLE
+ * FOR ANY DIRECT, INDIRECT, INCIDENTAL, SPECIAL, EXEMPLARY, OR CONSEQUENTIAL
+ * DAMAGES (INCLUDING, BUT NOT LIMITED TO, PROCUREMENT OF SUBSTITUTE GOODS
+ * OR SERVICES; LOSS OF USE, DATA, OR PROFITS; OR BUSINESS INTERRUPTION)
+ * HOWEVER CAUSED AND ON ANY THEORY OF LIABILITY, WHETHER IN CONTRACT, STRICT
+ * LIABILITY, OR TORT (INCLUDING NEGLIGENCE OR OTHERWISE) ARISING IN ANY WAY
+ * OUT OF THE USE OF THIS SOFTWARE, EVEN IF ADVISED OF THE POSSIBILITY OF
+ * SUCH DAMAGE.
+ *
+ * $FreeBSD$
+ */
+
+/dts-v1/;
+
+/include/ "exynos5250.dtsi"
+
+/ {
+ model = "Arndale Board";
+
+ memory {
+ device_type = "memory";
+ reg = < 0x40000000 0x80000000 >; /* 2G */
+ };
+
+ chosen {
+ stdin = &serial2;
+ stdout = &serial2;
+ };
+};
diff --git a/sys/boot/fdt/dts/exynos5250.dtsi b/sys/boot/fdt/dts/exynos5250.dtsi
new file mode 100644
index 0000000..860fcd0
--- /dev/null
+++ b/sys/boot/fdt/dts/exynos5250.dtsi
@@ -0,0 +1,157 @@
+/*-
+ * Copyright (c) 2013 Ruslan Bukin <br@bsdpad.com>
+ * All rights reserved.
+ *
+ * Redistribution and use in source and binary forms, with or without
+ * modification, are permitted provided that the following conditions
+ * are met:
+ * 1. Redistributions of source code must retain the above copyright
+ * notice, this list of conditions and the following disclaimer.
+ * 2. Redistributions in binary form must reproduce the above copyright
+ * notice, this list of conditions and the following disclaimer in the
+ * documentation and/or other materials provided with the distribution.
+ *
+ * THIS SOFTWARE IS PROVIDED BY THE AUTHOR AND CONTRIBUTORS ``AS IS'' AND
+ * ANY EXPRESS OR IMPLIED WARRANTIES, INCLUDING, BUT NOT LIMITED TO, THE
+ * IMPLIED WARRANTIES OF MERCHANTABILITY AND FITNESS FOR A PARTICULAR PURPOSE
+ * ARE DISCLAIMED. IN NO EVENT SHALL THE AUTHOR OR CONTRIBUTORS BE LIABLE
+ * FOR ANY DIRECT, INDIRECT, INCIDENTAL, SPECIAL, EXEMPLARY, OR CONSEQUENTIAL
+ * DAMAGES (INCLUDING, BUT NOT LIMITED TO, PROCUREMENT OF SUBSTITUTE GOODS
+ * OR SERVICES; LOSS OF USE, DATA, OR PROFITS; OR BUSINESS INTERRUPTION)
+ * HOWEVER CAUSED AND ON ANY THEORY OF LIABILITY, WHETHER IN CONTRACT, STRICT
+ * LIABILITY, OR TORT (INCLUDING NEGLIGENCE OR OTHERWISE) ARISING IN ANY WAY
+ * OUT OF THE USE OF THIS SOFTWARE, EVEN IF ADVISED OF THE POSSIBILITY OF
+ * SUCH DAMAGE.
+ *
+ * $FreeBSD$
+ */
+
+/ {
+ compatible = "samsung,exynos5250";
+ #address-cells = <1>;
+ #size-cells = <1>;
+ interrupt-parent = <&GIC>;
+
+ Exynos5@0 {
+ #address-cells = <1>;
+ #size-cells = <1>;
+ compatible = "simple-bus";
+ ranges;
+ bus-frequency = <0>;
+
+ GIC: interrupt-controller@10481000 {
+ compatible = "arm,gic";
+ reg = < 0x10481000 0x1000 >, /* Distributor Registers */
+ < 0x10482000 0x2000 >; /* CPU Interface Registers */
+ interrupt-controller;
+ #address-cells = <0>;
+ #interrupt-cells = <1>;
+ };
+
+ mct {
+ compatible = "exynos,mct";
+ reg = < 0x101C0000 0x1000 >;
+ clock-frequency = <24000000>;
+ };
+
+ generic_timer {
+ compatible = "arm,armv7-timer";
+ clock-frequency = <24000000>;
+ };
+
+ pwm {
+ compatible = "samsung,s3c24x0-timer";
+ reg = <0x12DD0000 0x1000>;
+ interrupts = < 71 >;
+ interrupt-parent = <&GIC>;
+ clock-frequency = <24000000>;
+ };
+
+ usb@12110000 {
+ compatible = "exynos,usb-ehci", "usb-ehci";
+ reg = <0x12110000 0x1000>, /* EHCI */
+ <0x12130000 0x1000>, /* EHCI host ctrl */
+ <0x10040000 0x1000>, /* Power */
+ <0x10050000 0x1000>, /* Sysreg */
+ <0x11400000 0x1000>; /* GPIO left */
+ interrupts = < 103 >;
+ interrupt-parent = <&GIC>;
+ };
+
+ usb@12120000 {
+ compatible = "exynos,usb-ohci", "usb-ohci";
+ reg = <0x12120000 0x10000>;
+ interrupts = < 103 >;
+ interrupt-parent = <&GIC>;
+ };
+
+ sdhci@12200000 {
+ compatible = "sdhci_generic";
+ reg = <0x12200000 0x1000>;
+ interrupts = <107>;
+ interrupt-parent = <&GIC>;
+ clock-frequency = <24000000>; /* TODO: verify freq */
+ };
+
+ sdhci@12210000 {
+ compatible = "sdhci_generic";
+ reg = <0x12210000 0x1000>;
+ interrupts = <108>;
+ interrupt-parent = <&GIC>;
+ clock-frequency = <24000000>;
+ };
+
+ sdhci@12220000 {
+ compatible = "sdhci_generic";
+ reg = <0x12220000 0x1000>;
+ interrupts = <109>;
+ interrupt-parent = <&GIC>;
+ clock-frequency = <24000000>;
+ };
+
+ sdhci@12230000 {
+ compatible = "sdhci_generic";
+ reg = <0x12230000 0x1000>;
+ interrupts = <110>;
+ interrupt-parent = <&GIC>;
+ clock-frequency = <24000000>;
+ };
+
+ serial0: serial@12C00000 {
+ compatible = "exynos";
+ reg = <0x12C00000 0x100>;
+ interrupts = < 83 >;
+ interrupt-parent = <&GIC>;
+ clock-frequency = < 100000000 >;
+ current-speed = <115200>;
+ };
+
+ serial1: serial@12C10000 {
+ compatible = "exynos";
+ reg = <0x12C10000 0x100>;
+ interrupts = < 84 >;
+ interrupt-parent = <&GIC>;
+ clock-frequency = < 100000000 >;
+ current-speed = <115200>;
+ };
+
+ serial2: serial@12C20000 {
+ compatible = "exynos";
+ reg = <0x12C20000 0x100>;
+ interrupts = < 85 >;
+ interrupt-parent = <&GIC>;
+ clock-frequency = < 100000000 >;
+ current-speed = <115200>;
+ };
+
+ serial3: serial@12C30000 {
+ compatible = "exynos";
+ reg = <0x12C30000 0x100>;
+ interrupts = < 86 >;
+ interrupt-parent = <&GIC>;
+ clock-frequency = < 100000000 >;
+ current-speed = <115200>;
+ };
+
+ };
+};
diff --git a/sys/boot/fdt/dts/rpi.dts b/sys/boot/fdt/dts/rpi.dts
new file mode 100644
index 0000000..bdcee00
--- /dev/null
+++ b/sys/boot/fdt/dts/rpi.dts
@@ -0,0 +1,381 @@
+/*
+ * Copyright (c) 2012 Oleksandr Tymoshenko <gonzo@bluezbox.com>
+ *
+ * Redistribution and use in source and binary forms, with or without
+ * modification, are permitted provided that the following conditions
+ * are met:
+ * 1. Redistributions of source code must retain the above copyright
+ * notice, this list of conditions and the following disclaimer.
+ * 2. Redistributions in binary form must reproduce the above copyright
+ * notice, this list of conditions and the following disclaimer in the
+ * documentation and/or other materials provided with the distribution.
+ *
+ * THIS SOFTWARE IS PROVIDED BY THE AUTHOR AND CONTRIBUTORS ``AS IS'' AND
+ * ANY EXPRESS OR IMPLIED WARRANTIES, INCLUDING, BUT NOT LIMITED TO, THE
+ * IMPLIED WARRANTIES OF MERCHANTABILITY AND FITNESS FOR A PARTICULAR PURPOSE
+ * ARE DISCLAIMED. IN NO EVENT SHALL THE AUTHOR OR CONTRIBUTORS BE LIABLE
+ * FOR ANY DIRECT, INDIRECT, INCIDENTAL, SPECIAL, EXEMPLARY, OR CONSEQUENTIAL
+ * DAMAGES (INCLUDING, BUT NOT LIMITED TO, PROCUREMENT OF SUBSTITUTE GOODS
+ * OR SERVICES; LOSS OF USE, DATA, OR PROFITS; OR BUSINESS INTERRUPTION)
+ * HOWEVER CAUSED AND ON ANY THEORY OF LIABILITY, WHETHER IN CONTRACT, STRICT
+ * LIABILITY, OR TORT (INCLUDING NEGLIGENCE OR OTHERWISE) ARISING IN ANY WAY
+ * OUT OF THE USE OF THIS SOFTWARE, EVEN IF ADVISED OF THE POSSIBILITY OF
+ * SUCH DAMAGE.
+ *
+ * $FreeBSD$
+ */
+/dts-v1/;
+
+/include/ "bcm2835.dtsi"
+
+/memreserve/ 0x08000000 0x08000000; /* Set by VideoCore */
+
+/ {
+ model = "Raspberry Pi (BCM2835)";
+ compatible = "raspberrypi,model-a", "raspberrypi,model-b",
+ "broadcom,bcm2835-vc", "broadcom,bcm2708-vc";
+
+ memory {
+ device_type = "memory";
+ reg = <0 0x8000000>; /* 128MB, Set by VideoCore */
+
+ };
+
+ system {
+ revision = <0>; /* Set by VideoCore */
+ serial = <0 0>; /* Set by VideoCore */
+ };
+
+ axi {
+ gpio: gpio {
+ /* BSC0 */
+ pins_bsc0_a: bsc0_a {
+ broadcom,function = "ALT0";
+ };
+
+ pins_bsc0_b: bsc0_b {
+ broadcom,function = "ALT0";
+ };
+
+ pins_bsc0_c: bsc0_c {
+ broadcom,function = "ALT1";
+ };
+
+ /* BSC1 */
+ pins_bsc1_a: bsc1_a {
+ broadcom,function = "ALT0";
+ };
+
+ pins_bsc1_b: bsc1_b {
+ broadcom,function = "ALT2";
+ };
+
+ /* GPCLK0 */
+ pins_gpclk0_a: gpclk0_a {
+ broadcom,function = "ALT0";
+ };
+
+ pins_gpclk0_b: gpclk0_b {
+ broadcom,function = "ALT5";
+ };
+
+ pins_gpclk0_c: gpclk0_c {
+ broadcom,function = "ALT0";
+ };
+
+ pins_gpclk0_d: gpclk0_d {
+ broadcom,function = "ALT0";
+ };
+
+ /* GPCLK1 */
+ pins_gpclk1_a: gpclk1_a {
+ broadcom,function = "ALT0";
+ };
+
+ pins_gpclk1_b: gpclk1_b {
+ broadcom,function = "ALT5";
+ };
+
+ pins_gpclk1_c: gpclk1_c {
+ broadcom,function = "ALT0";
+ };
+
+ pins_gpclk1_d: gpclk1_d {
+ broadcom,function = "ALT0";
+ };
+
+ /* GPCLK2 */
+ pins_gpclk2_a: gpclk2_a {
+ broadcom,function = "ALT0";
+ };
+
+ pins_gpclk2_b: gpclk2_b {
+ broadcom,function = "ALT0";
+ };
+
+ /* SPI0 */
+ pins_spi0_a: spi0_a {
+ broadcom,function = "ALT0";
+ };
+
+ pins_spi0_b: spi0_b {
+ broadcom,function = "ALT0";
+ };
+
+ /* PWM */
+ pins_pwm0_a: pwm0_a {
+ broadcom,function = "ALT0";
+ };
+
+ pins_pwm0_b: pwm0_b {
+ broadcom,function = "ALT5";
+ };
+
+ pins_pwm0_c: pwm0_c {
+ broadcom,function = "ALT0";
+ };
+
+ pins_pwm1_a: pwm1_a {
+ broadcom,function = "ALT0";
+ };
+
+ pins_pwm1_b: pwm1_b {
+ broadcom,function = "ALT5";
+ };
+
+ pins_pwm1_c: pwm1_c {
+ broadcom,function = "ALT0";
+ };
+
+ pins_pwm1_d: pwm1_d {
+ broadcom,function = "ALT0";
+ };
+
+ /* UART0 */
+ pins_uart0_a: uart0_a {
+ broadcom,function = "ALT0";
+ };
+
+ pins_uart0_b: uart0_b {
+ broadcom,function = "ALT3";
+ };
+
+ pins_uart0_c: uart0_c {
+ broadcom,function = "ALT2";
+ };
+
+ pins_uart0_fc_a: uart0_fc_a {
+ broadcom,function = "ALT3";
+ };
+
+ pins_uart0_fc_b: uart0_fc_b {
+ broadcom,function = "ALT3";
+ };
+
+ pins_uart0_fc_c: uart0_fc_c {
+ broadcom,function = "ALT2";
+ };
+
+ /* PCM */
+ pins_pcm_a: pcm_a {
+ broadcom,function = "ALT0";
+ };
+
+ pins_pcm_b: pcm_b {
+ broadcom,function = "ALT2";
+ };
+
+ /* Secondary Address Bus */
+ pins_sm_addr_a: sm_addr_a {
+ broadcom,function = "ALT1";
+ };
+
+ pins_sm_addr_b: sm_addr_b {
+ broadcom,function = "ALT1";
+ };
+
+ pins_sm_ctl_a: sm_ctl_a {
+ broadcom,function = "ALT1";
+ };
+
+ pins_sm_ctl_b: sm_ctl_b {
+ broadcom,function = "ALT1";
+ };
+
+ pins_sm_data_8bit_a: sm_data_8bit_a {
+ broadcom,function = "ALT1";
+ };
+
+ pins_sm_data_8bit_b: sm_data_8bit_b {
+ broadcom,function = "ALT1";
+ };
+
+ pins_sm_data_16bit: sm_data_16bit {
+ broadcom,function = "ALT1";
+ };
+
+ pins_sm_data_18bit: sm_data_18bit {
+ broadcom,function = "ALT1";
+ };
+
+ /* BSCSL */
+ pins_bscsl: bscsl {
+ broadcom,function = "ALT3";
+ };
+
+ /* SPISL */
+ pins_spisl: spisl {
+ broadcom,function = "ALT3";
+ };
+
+ /* SPI1 */
+ pins_spi1: spi1 {
+ broadcom,function = "ALT4";
+ };
+
+ /* UART1 */
+ pins_uart1_a: uart1_a {
+ broadcom,function = "ALT5";
+ };
+
+ pins_uart1_b: uart1_b {
+ broadcom,function = "ALT5";
+ };
+
+ pins_uart1_c: uart1_c {
+ broadcom,function = "ALT5";
+ };
+
+ pins_uart1_fc_a: uart1_fc_a {
+ broadcom,function = "ALT5";
+ };
+
+ pins_uart1_fc_b: uart1_fc_b {
+ broadcom,function = "ALT5";
+ };
+
+ pins_uart1_fc_c: uart1_fc_c {
+ broadcom,function = "ALT5";
+ };
+
+ /* SPI2 */
+ pins_spi2: spi2 {
+ broadcom,function = "ALT4";
+ };
+
+ /* ARM JTAG */
+ pins_arm_jtag_trst: arm_jtag_trst {
+ broadcom,function = "ALT4";
+ };
+
+ pins_arm_jtag_a: arm_jtag_a {
+ broadcom,function = "ALT5";
+ };
+
+ pins_arm_jtag_b: arm_jtag_b {
+ broadcom,function = "ALT4";
+ };
+
+ /* Reserved */
+ pins_reserved: reserved {
+ broadcom,function = "ALT3";
+ };
+ };
+ usb {
+ hub {
+ compatible = "usb,hub", "usb,device";
+ reg = <0x00000001>;
+ #address-cells = <1>;
+ #size-cells = <0>;
+ ethernet {
+ compatible = "net,ethernet",
+ "usb,device";
+ reg = <0x00000001>;
+ mac-address = [00 00 00 00 00 00];
+ };
+ };
+
+ };
+
+
+ };
+
+ display {
+ compatible = "broadcom,bcm2835-fb", "broadcom,bcm2708-fb";
+
+ broadcom,vc-mailbox = <&vc_mbox>;
+ broadcom,vc-channel = <1>;
+
+ broadcom,width = <0>; /* Set by VideoCore */
+ broadcom,height = <0>; /* Set by VideoCore */
+ broadcom,depth = <0>; /* Set by VideoCore */
+ };
+
+ leds {
+ compatible = "gpio-leds";
+
+ ok {
+ label = "ok";
+ gpios = <&gpio 16 1>;
+
+ /* Don't change this - it configures
+ * how the led driver determines if
+ * the led is on or off when it loads.
+ */
+ default-state = "keep";
+
+ /* This is the real default state. */
+ linux,default-trigger = "default-on";
+ };
+ };
+
+ power: regulator {
+ compatible = "broadcom,bcm2835-power-mgr",
+ "broadcom,bcm2708-power-mgr",
+ "simple-bus";
+ #address-cells = <1>;
+ #size-cells = <0>;
+
+ broadcom,vc-mailbox = <&vc_mbox>;
+ broadcom,vc-channel = <0>;
+
+ regulator-name = "VideoCore";
+ regulator-min-microvolt = <5000000>;
+ regulator-max-microvolt = <5000000>;
+ regulator-always-on = <1>;
+
+ sd_card_power: regulator@0 {
+ compatible = "broadcom,bcm2835-power-dev",
+ "broadcom,bcm2708-power-dev";
+ reg = <0>;
+
+ vin-supply = <&power>;
+ regulator-name = "SD Card";
+ regulator-min-microvolt = <3300000>;
+ regulator-max-microvolt = <3300000>;
+ };
+
+ /* This is for the controller itself, not the root port */
+ usb_hcd_power: regulator@3 {
+ compatible = "broadcom,bcm2835-power-dev",
+ "broadcom,bcm2708-power-dev";
+ reg = <3>;
+
+ vin-supply = <&power>;
+ regulator-name = "USB HCD";
+ regulator-min-microvolt = <5000000>;
+ regulator-max-microvolt = <5000000>;
+ };
+ };
+
+ aliases {
+ uart0 = &uart0;
+ };
+
+ chosen {
+ bootargs = ""; /* Set by VideoCore */
+ stdin = "uart0";
+ stdout = "uart0";
+ };
+
+};
diff --git a/sys/boot/i386/gptboot/Makefile b/sys/boot/i386/gptboot/Makefile
index e1a640a..7fb0336 100644
--- a/sys/boot/i386/gptboot/Makefile
+++ b/sys/boot/i386/gptboot/Makefile
@@ -3,6 +3,7 @@
.PATH: ${.CURDIR}/../boot2 ${.CURDIR}/../common ${.CURDIR}/../../common
FILES= gptboot
+MAN= gptboot.8
NM?= nm
diff --git a/sys/boot/i386/gptboot/gptboot.8 b/sys/boot/i386/gptboot/gptboot.8
new file mode 100644
index 0000000..691bcb7
--- /dev/null
+++ b/sys/boot/i386/gptboot/gptboot.8
@@ -0,0 +1,238 @@
+.\" Copyright (c) 2013 Warren Block
+.\" All rights reserved.
+.\"
+.\" Redistribution and use in source and binary forms, with or without
+.\" modification, are permitted provided that the following conditions
+.\" are met:
+.\" 1. Redistributions of source code must retain the above copyright
+.\" notice, this list of conditions and the following disclaimer.
+.\" 2. Redistributions in binary form must reproduce the above copyright
+.\" notice, this list of conditions and the following disclaimer in the
+.\" documentation and/or other materials provided with the distribution.
+.\"
+.\" THIS SOFTWARE IS PROVIDED BY THE AUTHORS AND CONTRIBUTORS ``AS IS'' AND
+.\" ANY EXPRESS OR IMPLIED WARRANTIES, INCLUDING, BUT NOT LIMITED TO, THE
+.\" IMPLIED WARRANTIES OF MERCHANTABILITY AND FITNESS FOR A PARTICULAR PURPOSE
+.\" ARE DISCLAIMED. IN NO EVENT SHALL THE AUTHORS OR CONTRIBUTORS BE LIABLE
+.\" FOR ANY DIRECT, INDIRECT, INCIDENTAL, SPECIAL, EXEMPLARY, OR CONSEQUENTIAL
+.\" DAMAGES (INCLUDING, BUT NOT LIMITED TO, PROCUREMENT OF SUBSTITUTE GOODS
+.\" OR SERVICES; LOSS OF USE, DATA, OR PROFITS; OR BUSINESS INTERRUPTION)
+.\" HOWEVER CAUSED AND ON ANY THEORY OF LIABILITY, WHETHER IN CONTRACT, STRICT
+.\" LIABILITY, OR TORT (INCLUDING NEGLIGENCE OR OTHERWISE) ARISING IN ANY WAY
+.\" OUT OF THE USE OF THIS SOFTWARE, EVEN IF ADVISED OF THE POSSIBILITY OF
+.\" SUCH DAMAGE.
+.\"
+.\" $FreeBSD$
+.\"
+.Dd July 3, 2013
+.Dt GPTBOOT 8
+.Os
+.Sh NAME
+.Nm gptboot
+.Nd GPT bootcode for UFS on BIOS-based computers
+.Sh DESCRIPTION
+.Nm
+is used on BIOS-based computers to boot from a UFS partition on a
+GPT-partitioned disk.
+.Nm
+is installed in a
+.Cm freebsd-boot
+partition with
+.Xr gpart 8 .
+.Sh IMPLEMENTATION NOTES
+The GPT standard allows a variable number of partitions, but
+.Nm
+only boots from tables with 128 partitions or less.
+.Sh PARTITION ATTRIBUTES
+.Nm
+checks and manages several attributes of GPT UFS partitions.
+.Bl -tag -width ".Cm bootfailed"
+.It Cm bootme
+Attempt to boot from this partition.
+If more than one partition has the
+.Cm bootme
+attribute set,
+.Nm
+will attempt to boot each one until successful.
+.It Cm bootonce
+Attempt to boot from this partition only one time.
+Setting this attribute with
+.Xr gpart 8
+automatically also sets the
+.Cm bootme
+attribute.
+Multiple partitions may have the
+.Cm bootonce
+and
+.Cm bootme
+attributes set.
+.It Cm bootfailed
+The
+.Cm bootfailed
+attribute marks partitions that had the
+.Cm bootonce
+attribute set, but failed to boot.
+This attribute is managed by the system.
+See
+.Sx "BOOTING"
+and
+.Sx "POST-BOOT ACTIONS"
+below for details.
+.El
+.Sh USAGE
+For normal usage, the user does not have to set or manage any of the
+partition attributes.
+.Nm
+will boot from the first UFS partition found.
+.Pp
+The
+.Cm bootonce
+attribute can be used for testing an upgraded operating system on
+an already-working computer.
+The existing system partition is left untouched, and the new version
+of the operating system to be tested is installed on another partition.
+The
+.Cm bootonce
+attribute is set on that new test partition.
+The next boot is attempted from the test partition.
+Success or failure will be shown in the system log files.
+After a successful boot of the test partition, a user script can check
+the logs and change the
+.Cm bootme
+attributes so the test partition becomes the new system partition.
+Because the
+.Cm bootonce
+attribute is cleared after an attempted boot, a failed boot will not
+leave the system attempting to boot from a partition that will never
+succeed.
+Instead, the system will boot from the older, known-working operating
+system that has not been modified.
+If the
+.Cm bootme
+attribute is set on any partitions, booting will be attempted from them
+first.
+If no partitions with
+.Cm bootme
+attributes are found, booting will be attempted from the first UFS
+partition found.
+.Sh BOOTING
+.Nm
+first reads the partition table.
+All
+.Cm freebsd-ufs
+partitions with only the
+.Cm bootonce
+attribute set, indicating a failed boot, are set to
+.Cm bootfailed .
+.Nm
+then scans through all of the
+.Cm freebsd-ufs
+partitions.
+Boot behavior depends on the combination of
+.Cm bootme
+and
+.Cm bootonce
+attributes set on those partitions.
+.Bl -tag -width ".Cm bootonce + .Cm bootme"
+.It Cm bootonce + Cm bootme
+Highest priority: booting is attempted from each of the
+.Cm freebsd-ufs
+partitions with both of these attributes.
+On each partition, the
+.Cm bootme
+attribute is removed and the boot attempted.
+.It Cm bootme
+Middle priority: booting is attempted from each of the
+.Cm freebsd-ufs
+partitions with the
+.Cm bootme
+attribute.
+.El
+.Pp
+If neither
+.Cm bootonce
+nor
+.Cm bootme
+attributes are found on any partitions, booting is attempted from the
+first
+.Cm freebsd-ufs
+partition on the disk.
+.Sh POST-BOOT ACTIONS
+The startup script
+.Pa /etc/rc.d/gptboot
+checks the attributes of
+.Cm freebsd-ufs
+partitions on all GPT disks.
+Partitions with the
+.Cm bootfailed
+attribute generate a
+.Dq boot from X failed
+system log message.
+Partitions with only the
+.Cm bootonce
+attribute, indicating a partition that successfully booted, generate a
+.Dq boot from X succeeded
+system log message.
+The
+.Cm bootfailed
+attributes are cleared from all the partitions.
+The
+.Cm bootonce
+attribute is cleared from the partition that successfully booted.
+There is normally only one of these.
+.Sh FILES
+.Bl -tag -width /boot/gptboot -compact
+.It Pa /boot/gptboot
+bootcode binary
+.It Pa /boot.config
+parameters for the boot blocks
+.Pq optional
+.El
+.Sh EXAMPLES
+.Nm
+is typically installed in combination with a
+.Dq protective MBR
+.Po
+see
+.Xr gpart 8
+.Pc .
+Install
+.Nm
+on the
+.Pa ada0
+drive:
+.Bd -literal -offset indent
+gpart bootcode -b /boot/pmbr -p /boot/gptboot -i 1 ada0
+.Ed
+.Pp
+.Nm
+can also be installed without the PMBR:
+.Bd -literal -offset indent
+gpart bootcode -p /boot/gptboot -i 1 ada0
+.Ed
+.Pp
+Set the
+.Cm bootme
+attribute for partition 2:
+.Bd -literal -offset indent
+gpart set -a bootme -i 2 ada0
+.Ed
+.Pp
+Set the
+.Cm bootonce
+attribute for partition 2, automatically also setting the
+.Cm bootme
+attribute:
+.Bd -literal -offset indent
+gpart set -a bootonce -i 2 ada0
+.Ed
+.Sh SEE ALSO
+.Xr boot.config 5 ,
+.Xr rc.conf 5 ,
+.Xr boot 8 ,
+.Xr gpart 8
+.Sh HISTORY
+.Nm
+appeared in FreeBSD 7.1.
+.Sh AUTHORS
+Warren Block <wblock@FreeBSD.org>
diff --git a/sys/cam/cam_ccb.h b/sys/cam/cam_ccb.h
index 35296ce..04ed592 100644
--- a/sys/cam/cam_ccb.h
+++ b/sys/cam/cam_ccb.h
@@ -1296,6 +1296,19 @@ cam_fill_smpio(struct ccb_smpio *smpio, uint32_t retries,
smpio->smp_response_len = smp_response_len;
}
+static __inline void
+cam_set_ccbstatus(union ccb *ccb, cam_status status)
+{
+ ccb->ccb_h.status &= ~CAM_STATUS_MASK;
+ ccb->ccb_h.status |= status;
+}
+
+static __inline cam_status
+cam_ccb_status(union ccb *ccb)
+{
+ return ((cam_status)(ccb->ccb_h.status & CAM_STATUS_MASK));
+}
+
void cam_calc_geometry(struct ccb_calc_geometry *ccg, int extended);
__END_DECLS
diff --git a/sys/cam/ctl/ctl_backend_ramdisk.c b/sys/cam/ctl/ctl_backend_ramdisk.c
index 985fad4..63cc4e0 100644
--- a/sys/cam/ctl/ctl_backend_ramdisk.c
+++ b/sys/cam/ctl/ctl_backend_ramdisk.c
@@ -441,6 +441,9 @@ ctl_backend_ramdisk_rm(struct ctl_be_ramdisk_softc *softc,
snprintf(req->error_str, sizeof(req->error_str),
"%s: error %d returned from ctl_invalidate_lun() for "
"LUN %d", __func__, retval, params->lun_id);
+ mtx_lock(&softc->lock);
+ be_lun->flags &= ~CTL_BE_RAMDISK_LUN_WAITING;
+ mtx_unlock(&softc->lock);
goto bailout_error;
}
@@ -475,14 +478,6 @@ ctl_backend_ramdisk_rm(struct ctl_be_ramdisk_softc *softc,
return (retval);
bailout_error:
-
- /*
- * Don't leave the waiting flag set.
- */
- mtx_lock(&softc->lock);
- be_lun->flags &= ~CTL_BE_RAMDISK_LUN_WAITING;
- mtx_unlock(&softc->lock);
-
req->status = CTL_LUN_ERROR;
return (0);
diff --git a/sys/cam/scsi/scsi_da.c b/sys/cam/scsi/scsi_da.c
index e0e33d6..7988309 100644
--- a/sys/cam/scsi/scsi_da.c
+++ b/sys/cam/scsi/scsi_da.c
@@ -1746,7 +1746,7 @@ dadeletemaxsysctl(SYSCTL_HANDLER_ARGS)
return (error);
/* only accept values smaller than the calculated value */
- if (value > softc->disk->d_delmaxsize) {
+ if (value > dadeletemaxsize(softc, softc->delete_method)) {
return (EINVAL);
}
softc->disk->d_delmaxsize = value;
diff --git a/sys/cam/scsi/scsi_xpt.c b/sys/cam/scsi/scsi_xpt.c
index 74bfb0a..9269f36 100644
--- a/sys/cam/scsi/scsi_xpt.c
+++ b/sys/cam/scsi/scsi_xpt.c
@@ -974,7 +974,7 @@ proberequestdefaultnegotiation(struct cam_periph *periph)
cts.ccb_h.func_code = XPT_GET_TRAN_SETTINGS;
cts.type = CTS_TYPE_USER_SETTINGS;
xpt_action((union ccb *)&cts);
- if ((cts.ccb_h.status & CAM_STATUS_MASK) != CAM_REQ_CMP) {
+ if (cam_ccb_status((union ccb *)&cts) != CAM_REQ_CMP) {
return;
}
cts.ccb_h.func_code = XPT_SET_TRAN_SETTINGS;
@@ -996,7 +996,7 @@ proberequestbackoff(struct cam_periph *periph, struct cam_ed *device)
cts.ccb_h.func_code = XPT_GET_TRAN_SETTINGS;
cts.type = CTS_TYPE_CURRENT_SETTINGS;
xpt_action((union ccb *)&cts);
- if ((cts.ccb_h.status & CAM_STATUS_MASK) != CAM_REQ_CMP) {
+ if (cam_ccb_status((union ccb *)&cts) != CAM_REQ_CMP) {
if (bootverbose) {
xpt_print(periph->path,
"failed to get current device settings\n");
@@ -1075,7 +1075,7 @@ proberequestbackoff(struct cam_periph *periph, struct cam_ed *device)
cts.ccb_h.func_code = XPT_SET_TRAN_SETTINGS;
cts.type = CTS_TYPE_CURRENT_SETTINGS;
xpt_action((union ccb *)&cts);
- if ((cts.ccb_h.status & CAM_STATUS_MASK) == CAM_REQ_CMP) {
+ if (cam_ccb_status((union ccb *)&cts) != CAM_REQ_CMP) {
break;
}
CAM_DEBUG(periph->path, CAM_DEBUG_PROBE,
@@ -1105,7 +1105,7 @@ probedone(struct cam_periph *periph, union ccb *done_ccb)
switch (softc->action) {
case PROBE_TUR:
{
- if ((done_ccb->ccb_h.status & CAM_STATUS_MASK) != CAM_REQ_CMP) {
+ if (cam_ccb_status(done_ccb) != CAM_REQ_CMP) {
if (cam_periph_error(done_ccb, 0,
SF_NO_PRINT, NULL) == ERESTART) {
@@ -1128,7 +1128,7 @@ out:
case PROBE_INQUIRY:
case PROBE_FULL_INQUIRY:
{
- if ((done_ccb->ccb_h.status & CAM_STATUS_MASK) == CAM_REQ_CMP) {
+ if (cam_ccb_status(done_ccb) == CAM_REQ_CMP) {
struct scsi_inquiry_data *inq_buf;
u_int8_t periph_qual;
@@ -1246,7 +1246,7 @@ out:
nlun = scsi_4btoul(lp->length) / 8;
maxlun = (csio->dxfer_len / 8) - 1;
- if ((done_ccb->ccb_h.status & CAM_STATUS_MASK) != CAM_REQ_CMP) {
+ if (cam_ccb_status(done_ccb) != CAM_REQ_CMP) {
if (cam_periph_error(done_ccb, 0,
done_ccb->ccb_h.target_lun > 0 ?
SF_RETRY_UA|SF_QUIET_IR : SF_RETRY_UA,
@@ -1357,7 +1357,7 @@ out:
csio = &done_ccb->csio;
mode_hdr = (struct scsi_mode_header_6 *)csio->data_ptr;
- if ((csio->ccb_h.status & CAM_STATUS_MASK) == CAM_REQ_CMP) {
+ if (cam_ccb_status(done_ccb) == CAM_REQ_CMP) {
struct scsi_control_page *page;
u_int8_t *offset;
@@ -1492,7 +1492,7 @@ probe_device_check:
/*
* Don't process the command as it was never sent
*/
- } else if ((csio->ccb_h.status & CAM_STATUS_MASK) == CAM_REQ_CMP
+ } else if (cam_ccb_status(done_ccb) == CAM_REQ_CMP
&& (serial_buf->length > 0)) {
have_serialnum = 1;
@@ -1577,7 +1577,7 @@ probe_device_check:
}
case PROBE_TUR_FOR_NEGOTIATION:
case PROBE_DV_EXIT:
- if ((done_ccb->ccb_h.status & CAM_STATUS_MASK) != CAM_REQ_CMP) {
+ if (cam_ccb_status(done_ccb) != CAM_REQ_CMP) {
cam_periph_error(done_ccb, 0,
SF_NO_PRINT | SF_NO_RECOVERY | SF_NO_RETRY, NULL);
}
@@ -1628,7 +1628,7 @@ probe_device_check:
struct scsi_inquiry_data *nbuf;
struct ccb_scsiio *csio;
- if ((done_ccb->ccb_h.status & CAM_STATUS_MASK) != CAM_REQ_CMP) {
+ if (cam_ccb_status(done_ccb) != CAM_REQ_CMP) {
cam_periph_error(done_ccb, 0,
SF_NO_PRINT | SF_NO_RECOVERY | SF_NO_RETRY, NULL);
}
@@ -1984,7 +1984,7 @@ scsi_scan_bus(struct cam_periph *periph, union ccb *request_ccb)
oldpath = request_ccb->ccb_h.path;
- status = request_ccb->ccb_h.status & CAM_STATUS_MASK;
+ status = cam_ccb_status(request_ccb);
/* Reuse the same CCB to query if a device was really found */
scan_info = (scsi_scan_bus_info *)request_ccb->ccb_h.ppriv_ptr0;
xpt_setup_ccb(&request_ccb->ccb_h, request_ccb->ccb_h.path,
@@ -2667,7 +2667,7 @@ scsi_set_transfer_settings(struct ccb_trans_settings *cts, struct cam_ed *device
cur_cts.ccb_h.func_code = XPT_GET_TRAN_SETTINGS;
cur_cts.type = cts->type;
xpt_action((union ccb *)&cur_cts);
- if ((cur_cts.ccb_h.status & CAM_STATUS_MASK) != CAM_REQ_CMP) {
+ if (cam_ccb_status((union ccb *)&cur_cts) != CAM_REQ_CMP) {
return;
}
cur_scsi = &cur_cts.proto_specific.scsi;
@@ -2951,7 +2951,7 @@ scsi_announce_periph(struct cam_periph *periph)
cts.ccb_h.func_code = XPT_GET_TRAN_SETTINGS;
cts.type = CTS_TYPE_CURRENT_SETTINGS;
xpt_action((union ccb*)&cts);
- if ((cts.ccb_h.status & CAM_STATUS_MASK) != CAM_REQ_CMP)
+ if (cam_ccb_status((union ccb *)&cts) != CAM_REQ_CMP)
return;
/* Ask the SIM for its base transfer speed */
xpt_setup_ccb(&cpi.ccb_h, path, CAM_PRIORITY_NORMAL);
diff --git a/sys/cddl/compat/opensolaris/kern/opensolaris_cmn_err.c b/sys/cddl/compat/opensolaris/kern/opensolaris_cmn_err.c
index f1e4c18..7ae498b 100644
--- a/sys/cddl/compat/opensolaris/kern/opensolaris_cmn_err.c
+++ b/sys/cddl/compat/opensolaris/kern/opensolaris_cmn_err.c
@@ -75,7 +75,8 @@ cmn_err(int type, const char *fmt, ...)
}
int
-assfail(const char *a, const char *f, int l) {
+assfail(const char *a, const char *f, int l)
+{
panic("solaris assert: %s, file: %s, line: %d", a, f, l);
@@ -84,7 +85,8 @@ assfail(const char *a, const char *f, int l) {
void
assfail3(const char *a, uintmax_t lv, const char *op, uintmax_t rv,
- const char *f, int l) {
+ const char *f, int l)
+{
panic("solaris assert: %s (0x%jx %s 0x%jx), file: %s, line: %d",
a, lv, op, rv, f, l);
diff --git a/sys/cddl/contrib/opensolaris/uts/common/dtrace/fasttrap.c b/sys/cddl/contrib/opensolaris/uts/common/dtrace/fasttrap.c
index fee9efa..f51df4c 100644
--- a/sys/cddl/contrib/opensolaris/uts/common/dtrace/fasttrap.c
+++ b/sys/cddl/contrib/opensolaris/uts/common/dtrace/fasttrap.c
@@ -2435,6 +2435,7 @@ fasttrap_unload(void)
mtx_sleep(&fasttrap_cleanup_drain, &fasttrap_cleanup_mtx, 0, "ftcld",
0);
fasttrap_cleanup_proc = NULL;
+ mtx_destroy(&fasttrap_cleanup_mtx);
#ifdef DEBUG
mutex_enter(&fasttrap_count_mtx);
diff --git a/sys/cddl/contrib/opensolaris/uts/common/fs/zfs/dsl_dir.c b/sys/cddl/contrib/opensolaris/uts/common/fs/zfs/dsl_dir.c
index b0bac9c..7d0b104 100644
--- a/sys/cddl/contrib/opensolaris/uts/common/fs/zfs/dsl_dir.c
+++ b/sys/cddl/contrib/opensolaris/uts/common/fs/zfs/dsl_dir.c
@@ -1258,8 +1258,6 @@ dsl_dir_rename_sync(void *arg, dmu_tx_t *tx)
int error;
objset_t *mos = dp->dp_meta_objset;
- ASSERT(dmu_buf_refcount(dd->dd_dbuf) <= 2);
-
VERIFY0(dsl_dir_hold(dp, ddra->ddra_oldname, FTAG, &dd, NULL));
VERIFY0(dsl_dir_hold(dp, ddra->ddra_newname, FTAG, &newparent,
&mynewname));
diff --git a/sys/cddl/contrib/opensolaris/uts/common/fs/zfs/sys/zvol.h b/sys/cddl/contrib/opensolaris/uts/common/fs/zfs/sys/zvol.h
index c813797..52b8f30 100644
--- a/sys/cddl/contrib/opensolaris/uts/common/fs/zfs/sys/zvol.h
+++ b/sys/cddl/contrib/opensolaris/uts/common/fs/zfs/sys/zvol.h
@@ -71,7 +71,7 @@ extern void zvol_log_write_minor(void *minor_hdl, dmu_tx_t *tx, offset_t off,
ssize_t resid, boolean_t sync);
#endif /* sun */
-#ifdef __FreeBSD__
+#if defined(__FreeBSD__) || defined(__FreeBSD_kernel__)
extern int zvol_create_minors(const char *name);
extern void zvol_rename_minors(const char *oldname, const char *newname);
#endif
diff --git a/sys/cddl/contrib/opensolaris/uts/common/fs/zfs/zfs_acl.c b/sys/cddl/contrib/opensolaris/uts/common/fs/zfs/zfs_acl.c
index 3200e7e..6545dc2 100644
--- a/sys/cddl/contrib/opensolaris/uts/common/fs/zfs/zfs_acl.c
+++ b/sys/cddl/contrib/opensolaris/uts/common/fs/zfs/zfs_acl.c
@@ -1683,7 +1683,7 @@ zfs_acl_ids_create(znode_t *dzp, int flag, vattr_t *vap, cred_t *cr,
} else {
acl_ids->z_fgid = zfs_fuid_create_cred(zfsvfs,
ZFS_GROUP, cr, &acl_ids->z_fuidp);
-#ifdef __FreeBSD__
+#ifdef __FreeBSD_kernel__
gid = acl_ids->z_fgid = dzp->z_gid;
#else
gid = crgetgid(cr);
@@ -2374,7 +2374,7 @@ zfs_zaccess(znode_t *zp, int mode, int flags, boolean_t skipaclchk, cred_t *cr)
is_attr = ((zp->z_pflags & ZFS_XATTR) && (ZTOV(zp)->v_type == VDIR));
-#ifdef __FreeBSD__
+#ifdef __FreeBSD_kernel__
/*
* In FreeBSD, we don't care about permissions of individual ADS.
* Note that not checking them is not just an optimization - without
diff --git a/sys/cddl/contrib/opensolaris/uts/common/fs/zfs/zfs_dir.c b/sys/cddl/contrib/opensolaris/uts/common/fs/zfs/zfs_dir.c
index ac1aa42..c076775 100644
--- a/sys/cddl/contrib/opensolaris/uts/common/fs/zfs/zfs_dir.c
+++ b/sys/cddl/contrib/opensolaris/uts/common/fs/zfs/zfs_dir.c
@@ -938,7 +938,7 @@ zfs_make_xattrdir(znode_t *zp, vattr_t *vap, vnode_t **xvpp, cred_t *cr)
* In FreeBSD, access checking for creating an EA is being done
* in zfs_setextattr(),
*/
-#ifndef __FreeBSD__
+#ifndef __FreeBSD_kernel__
if (error = zfs_zaccess(zp, ACE_WRITE_NAMED_ATTRS, 0, B_FALSE, cr))
return (error);
#endif
diff --git a/sys/cddl/contrib/opensolaris/uts/common/fs/zfs/zfs_ioctl.c b/sys/cddl/contrib/opensolaris/uts/common/fs/zfs/zfs_ioctl.c
index ff9067e..a4ae7aa 100644
--- a/sys/cddl/contrib/opensolaris/uts/common/fs/zfs/zfs_ioctl.c
+++ b/sys/cddl/contrib/opensolaris/uts/common/fs/zfs/zfs_ioctl.c
@@ -5570,6 +5570,13 @@ zfs_ioctl_init(void)
zfs_ioctl_register_dataset_nolog(ZFS_IOC_TMP_SNAPSHOT,
zfs_ioc_tmp_snapshot, zfs_secpolicy_tmp_snapshot,
POOL_CHECK_SUSPENDED | POOL_CHECK_READONLY);
+
+#ifdef __FreeBSD__
+ zfs_ioctl_register_dataset_nolog(ZFS_IOC_JAIL, zfs_ioc_jail,
+ zfs_secpolicy_config, POOL_CHECK_NONE);
+ zfs_ioctl_register_dataset_nolog(ZFS_IOC_UNJAIL, zfs_ioc_unjail,
+ zfs_secpolicy_config, POOL_CHECK_NONE);
+#endif
}
int
diff --git a/sys/cddl/contrib/opensolaris/uts/common/fs/zfs/zfs_vfsops.c b/sys/cddl/contrib/opensolaris/uts/common/fs/zfs/zfs_vfsops.c
index fec6be4..665627d 100644
--- a/sys/cddl/contrib/opensolaris/uts/common/fs/zfs/zfs_vfsops.c
+++ b/sys/cddl/contrib/opensolaris/uts/common/fs/zfs/zfs_vfsops.c
@@ -173,7 +173,7 @@ zfs_sync(vfs_t *vfsp, int waitfor)
return (0);
}
-#ifndef __FreeBSD__
+#ifndef __FreeBSD_kernel__
static int
zfs_create_unique_device(dev_t *dev)
{
@@ -225,7 +225,7 @@ zfs_create_unique_device(dev_t *dev)
return (0);
}
-#endif /* !__FreeBSD__ */
+#endif /* !__FreeBSD_kernel__ */
static void
atime_changed_cb(void *arg, uint64_t newval)
diff --git a/sys/cddl/contrib/opensolaris/uts/common/fs/zfs/zfs_vnops.c b/sys/cddl/contrib/opensolaris/uts/common/fs/zfs/zfs_vnops.c
index c5b55ed..d949acb 100644
--- a/sys/cddl/contrib/opensolaris/uts/common/fs/zfs/zfs_vnops.c
+++ b/sys/cddl/contrib/opensolaris/uts/common/fs/zfs/zfs_vnops.c
@@ -345,10 +345,13 @@ page_busy(vnode_t *vp, int64_t start, int64_t off, int64_t nbytes)
vm_page_sleep(pp, "zfsmwb");
continue;
}
- } else {
+ } else if (pp == NULL) {
pp = vm_page_alloc(obj, OFF_TO_IDX(start),
VM_ALLOC_SYSTEM | VM_ALLOC_IFCACHED |
VM_ALLOC_NOBUSY);
+ } else {
+ ASSERT(pp != NULL && !pp->valid);
+ pp = NULL;
}
if (pp != NULL) {
diff --git a/sys/cddl/dev/dtmalloc/dtmalloc.c b/sys/cddl/dev/dtmalloc/dtmalloc.c
index ca822f9..81ff43f 100644
--- a/sys/cddl/dev/dtmalloc/dtmalloc.c
+++ b/sys/cddl/dev/dtmalloc/dtmalloc.c
@@ -28,6 +28,7 @@
#include <sys/param.h>
#include <sys/systm.h>
#include <sys/conf.h>
+#include <sys/ctype.h>
#include <sys/kernel.h>
#include <sys/malloc.h>
#include <sys/module.h>
@@ -111,8 +112,17 @@ dtmalloc_type_cb(struct malloc_type *mtp, void *arg __unused)
{
char name[DTRACE_FUNCNAMELEN];
struct malloc_type_internal *mtip = mtp->ks_handle;
+ int i;
+ /*
+ * malloc_type descriptions are allowed to contain whitespace, but
+ * DTrace probe identifiers are not, so replace the whitespace with
+ * underscores.
+ */
strlcpy(name, mtp->ks_shortdesc, sizeof(name));
+ for (i = 0; name[i] != 0; i++)
+ if (isspace(name[i]))
+ name[i] = '_';
if (dtrace_probe_lookup(dtmalloc_id, NULL, name, "malloc") != 0)
return;
diff --git a/sys/conf/NOTES b/sys/conf/NOTES
index 3961e0f..dce2168 100644
--- a/sys/conf/NOTES
+++ b/sys/conf/NOTES
@@ -602,6 +602,12 @@ options IPX #IPX/SPX communications protocols
options NETATALK #Appletalk communications protocols
options NETATALKDEBUG #Appletalk debugging
+#
+# SMB/CIFS requester
+# NETSMB enables support for SMB protocol, it requires LIBMCHAIN and LIBICONV
+# options.
+options NETSMB #SMB/CIFS requester
+
# mchain library. It can be either loaded as KLD or compiled into kernel
options LIBMCHAIN
@@ -1038,6 +1044,7 @@ options NULLFS #NULL filesystem
options PROCFS #Process filesystem (requires PSEUDOFS)
options PSEUDOFS #Pseudo-filesystem framework
options PSEUDOFS_TRACE #Debugging support for PSEUDOFS
+options SMBFS #SMB/CIFS filesystem
options TMPFS #Efficient memory filesystem
options UDF #Universal Disk Format
options UNIONFS #Union filesystem
diff --git a/sys/conf/files b/sys/conf/files
index ed0eba9..ed62aac 100644
--- a/sys/conf/files
+++ b/sys/conf/files
@@ -536,8 +536,8 @@ crypto/blowfish/bf_ecb.c optional ipsec
crypto/blowfish/bf_skey.c optional crypto | ipsec
crypto/camellia/camellia.c optional crypto | ipsec
crypto/camellia/camellia-api.c optional crypto | ipsec
-crypto/des/des_ecb.c optional crypto | ipsec
-crypto/des/des_setkey.c optional crypto | ipsec
+crypto/des/des_ecb.c optional crypto | ipsec | netsmb
+crypto/des/des_setkey.c optional crypto | ipsec | netsmb
crypto/rc4/rc4.c optional netgraph_mppc_encryption | kgssapi
crypto/rijndael/rijndael-alg-fst.c optional crypto | geom_bde | \
ipsec | random | wlan_ccmp
@@ -1185,10 +1185,34 @@ t4fw.fwo optional cxgbe \
no-implicit-rule \
clean "t4fw.fwo"
t4fw.fw optional cxgbe \
- dependency "$S/dev/cxgbe/firmware/t4fw-1.8.4.0.bin.uu" \
+ dependency "$S/dev/cxgbe/firmware/t4fw-1.8.11.0.bin.uu" \
compile-with "${NORMAL_FW}" \
no-obj no-implicit-rule \
clean "t4fw.fw"
+t5fw_cfg.c optional cxgbe \
+ compile-with "${AWK} -f $S/tools/fw_stub.awk t5fw_cfg.fw:t5fw_cfg t5fw.fw:t5fw -mt5fw_cfg -c${.TARGET}" \
+ no-implicit-rule before-depend local \
+ clean "t5fw_cfg.c"
+t5fw_cfg.fwo optional cxgbe \
+ dependency "t5fw_cfg.fw" \
+ compile-with "${NORMAL_FWO}" \
+ no-implicit-rule \
+ clean "t5fw_cfg.fwo"
+t5fw_cfg.fw optional cxgbe \
+ dependency "$S/dev/cxgbe/firmware/t5fw_cfg.txt" \
+ compile-with "${CP} ${.ALLSRC} ${.TARGET}" \
+ no-obj no-implicit-rule \
+ clean "t5fw_cfg.fw"
+t5fw.fwo optional cxgbe \
+ dependency "t5fw.fw" \
+ compile-with "${NORMAL_FWO}" \
+ no-implicit-rule \
+ clean "t5fw.fwo"
+t5fw.fw optional cxgbe \
+ dependency "$S/dev/cxgbe/firmware/t5fw-1.8.22.0.bin.uu" \
+ compile-with "${NORMAL_FW}" \
+ no-obj no-implicit-rule \
+ clean "t5fw.fw"
dev/cy/cy.c optional cy
dev/cy/cy_isa.c optional cy isa
dev/cy/cy_pci.c optional cy pci
@@ -1369,6 +1393,7 @@ dev/fdt/fdtbus.c optional fdt
dev/fdt/simplebus.c optional fdt
dev/fe/if_fe.c optional fe
dev/fe/if_fe_pccard.c optional fe pccard
+dev/filemon/filemon.c optional filemon
dev/firewire/firewire.c optional firewire
dev/firewire/fwcrom.c optional firewire
dev/firewire/fwdev.c optional firewire
@@ -2509,6 +2534,12 @@ fs/pseudofs/pseudofs.c optional pseudofs
fs/pseudofs/pseudofs_fileno.c optional pseudofs
fs/pseudofs/pseudofs_vncache.c optional pseudofs
fs/pseudofs/pseudofs_vnops.c optional pseudofs
+fs/smbfs/smbfs_io.c optional smbfs
+fs/smbfs/smbfs_node.c optional smbfs
+fs/smbfs/smbfs_smb.c optional smbfs
+fs/smbfs/smbfs_subr.c optional smbfs
+fs/smbfs/smbfs_vfsops.c optional smbfs
+fs/smbfs/smbfs_vnops.c optional smbfs
fs/udf/osta.c optional udf
fs/udf/udf_iconv.c optional udf_iconv
fs/udf/udf_vfsops.c optional udf
@@ -2746,6 +2777,7 @@ kern/kern_uuid.c standard
kern/kern_xxx.c standard
kern/link_elf.c standard
kern/linker_if.m standard
+kern/md4c.c optional netsmb
kern/md5c.c standard
kern/p1003_1b.c standard
kern/posix4_mib.c standard
@@ -2797,6 +2829,7 @@ kern/subr_trap.c standard
kern/subr_turnstile.c standard
kern/subr_uio.c standard
kern/subr_unit.c standard
+kern/subr_vmem.c standard
kern/subr_witness.c optional witness
kern/sys_capability.c standard
kern/sys_generic.c standard
@@ -3278,6 +3311,15 @@ netpfil/pf/pf_osfp.c optional pf inet
netpfil/pf/pf_ruleset.c optional pf inet
netpfil/pf/pf_table.c optional pf inet
netpfil/pf/in4_cksum.c optional pf inet
+netsmb/smb_conn.c optional netsmb
+netsmb/smb_crypt.c optional netsmb
+netsmb/smb_dev.c optional netsmb
+netsmb/smb_iod.c optional netsmb
+netsmb/smb_rq.c optional netsmb
+netsmb/smb_smb.c optional netsmb
+netsmb/smb_subr.c optional netsmb
+netsmb/smb_trantcp.c optional netsmb
+netsmb/smb_usr.c optional netsmb
nfs/bootp_subr.c optional bootp nfsclient | bootp nfscl
nfs/krpc_subr.c optional bootp nfsclient | bootp nfscl
nfs/nfs_common.c optional nfsclient | nfsserver
diff --git a/sys/conf/files.amd64 b/sys/conf/files.amd64
index 7a41a26..2cb1981 100644
--- a/sys/conf/files.amd64
+++ b/sys/conf/files.amd64
@@ -136,7 +136,7 @@ crypto/aesni/aeskeys_amd64.S optional aesni
crypto/aesni/aesni.c optional aesni
crypto/aesni/aesni_wrap.c optional aesni
crypto/blowfish/bf_enc.c optional crypto | ipsec
-crypto/des/des_enc.c optional crypto | ipsec
+crypto/des/des_enc.c optional crypto | ipsec | netsmb
crypto/via/padlock.c optional padlock
crypto/via/padlock_cipher.c optional padlock
crypto/via/padlock_hash.c optional padlock
diff --git a/sys/conf/files.arm b/sys/conf/files.arm
index 938386d..ac48704 100644
--- a/sys/conf/files.arm
+++ b/sys/conf/files.arm
@@ -61,7 +61,7 @@ board_id.h standard \
clean "board_id.h"
cddl/compat/opensolaris/kern/opensolaris_atomic.c optional zfs compile-with "${ZFS_C}"
crypto/blowfish/bf_enc.c optional crypto | ipsec
-crypto/des/des_enc.c optional crypto | ipsec
+crypto/des/des_enc.c optional crypto | ipsec | netsmb
dev/fb/fb.c optional sc
dev/hwpmc/hwpmc_arm.c optional hwpmc
dev/kbd/kbd.c optional sc
diff --git a/sys/conf/files.i386 b/sys/conf/files.i386
index 218472b..0f65d3b 100644
--- a/sys/conf/files.i386
+++ b/sys/conf/files.i386
@@ -122,7 +122,7 @@ crypto/aesni/aesencdec_i386.S optional aesni
crypto/aesni/aeskeys_i386.S optional aesni
crypto/aesni/aesni.c optional aesni
crypto/aesni/aesni_wrap.c optional aesni
-crypto/des/arch/i386/des_enc.S optional crypto | ipsec
+crypto/des/arch/i386/des_enc.S optional crypto | ipsec | netsmb
crypto/via/padlock.c optional padlock
crypto/via/padlock_cipher.c optional padlock
crypto/via/padlock_hash.c optional padlock
diff --git a/sys/conf/files.ia64 b/sys/conf/files.ia64
index 474ba1b..6719c98 100644
--- a/sys/conf/files.ia64
+++ b/sys/conf/files.ia64
@@ -41,7 +41,7 @@ contrib/ia64/libuwx/src/uwx_trace.c standard
contrib/ia64/libuwx/src/uwx_uinfo.c standard
contrib/ia64/libuwx/src/uwx_utable.c standard
crypto/blowfish/bf_enc.c optional crypto | ipsec
-crypto/des/des_enc.c optional crypto | ipsec
+crypto/des/des_enc.c optional crypto | ipsec | netsmb
dev/atkbdc/atkbd.c optional atkbd atkbdc
dev/atkbdc/atkbd_atkbdc.c optional atkbd atkbdc
dev/atkbdc/atkbdc.c optional atkbdc
diff --git a/sys/conf/files.mips b/sys/conf/files.mips
index 79676b9..ca8dee2 100644
--- a/sys/conf/files.mips
+++ b/sys/conf/files.mips
@@ -77,7 +77,7 @@ dev/uart/uart_cpu_fdt.c optional uart fdt
# crypto support -- use generic
crypto/blowfish/bf_enc.c optional crypto | ipsec
-crypto/des/des_enc.c optional crypto | ipsec
+crypto/des/des_enc.c optional crypto | ipsec | netsmb
# AP common nvram interface MIPS specific, but maybe should be more generic
dev/nvram2env/nvram2env.c optional nvram2env
diff --git a/sys/conf/files.pc98 b/sys/conf/files.pc98
index 252ecdd..a8e60b6 100644
--- a/sys/conf/files.pc98
+++ b/sys/conf/files.pc98
@@ -77,7 +77,7 @@ bf_enc.o optional crypto | ipsec \
dependency "$S/crypto/blowfish/arch/i386/bf_enc.S $S/crypto/blowfish/arch/i386/bf_enc_586.S $S/crypto/blowfish/arch/i386/bf_enc_686.S" \
compile-with "${CC} -c -I$S/crypto/blowfish/arch/i386 ${ASM_CFLAGS} ${WERROR} ${.IMPSRC}" \
no-implicit-rule
-crypto/des/arch/i386/des_enc.S optional crypto | ipsec
+crypto/des/arch/i386/des_enc.S optional crypto | ipsec | netsmb
dev/agp/agp_ali.c optional agp
dev/agp/agp_amd.c optional agp
dev/agp/agp_i810.c optional agp
diff --git a/sys/conf/files.powerpc b/sys/conf/files.powerpc
index 96331f7..5808d85 100644
--- a/sys/conf/files.powerpc
+++ b/sys/conf/files.powerpc
@@ -18,7 +18,7 @@ font.h optional sc \
cddl/compat/opensolaris/kern/opensolaris_atomic.c optional zfs powerpc compile-with "${ZFS_C}"
cddl/contrib/opensolaris/common/atomic/powerpc64/opensolaris_atomic.S optional zfs powerpc64 compile-with "${ZFS_S}"
crypto/blowfish/bf_enc.c optional crypto | ipsec
-crypto/des/des_enc.c optional crypto | ipsec
+crypto/des/des_enc.c optional crypto | ipsec | netsmb
dev/bm/if_bm.c optional bm powermac
dev/adb/adb_bus.c optional adb
dev/adb/adb_kbd.c optional adb
diff --git a/sys/conf/files.sparc64 b/sys/conf/files.sparc64
index 5019dab..a0fad9b 100644
--- a/sys/conf/files.sparc64
+++ b/sys/conf/files.sparc64
@@ -24,7 +24,7 @@ ukbdmap.h optional ukbd_dflt_keymap \
#
cddl/contrib/opensolaris/common/atomic/sparc64/opensolaris_atomic.S optional zfs compile-with "${ZFS_S}"
crypto/blowfish/bf_enc.c optional crypto | ipsec
-crypto/des/des_enc.c optional crypto | ipsec
+crypto/des/des_enc.c optional crypto | ipsec | netsmb
dev/atkbdc/atkbd.c optional atkbd atkbdc
dev/atkbdc/atkbd_atkbdc.c optional atkbd atkbdc
dev/atkbdc/atkbdc.c optional atkbdc
diff --git a/sys/conf/newvers.sh b/sys/conf/newvers.sh
index 1e5e24b..b0ca002 100644
--- a/sys/conf/newvers.sh
+++ b/sys/conf/newvers.sh
@@ -88,16 +88,26 @@ v=`cat version` u=${USER:-root} d=`pwd` h=${HOSTNAME:-`hostname`} t=`date`
i=`${MAKE:-make} -V KERN_IDENT`
compiler_v=$($(${MAKE:-make} -V CC) -v 2>&1 | grep 'version')
-for dir in /bin /usr/bin /usr/local/bin; do
+if [ -x /usr/bin/svnliteversion ] ; then
+ svnversion=/usr/bin/svnliteversion
+fi
+
+for dir in /usr/bin /usr/local/bin; do
+ if [ ! -z "${svnversion}" ] ; then
+ break
+ fi
if [ -x "${dir}/svnversion" ] && [ -z ${svnversion} ] ; then
svnversion=${dir}/svnversion
+ break
fi
+done
+for dir in /usr/bin /usr/local/bin; do
if [ -x "${dir}/p4" ] && [ -z ${p4_cmd} ] ; then
p4_cmd=${dir}/p4
fi
done
if [ -d "${SYSDIR}/../.git" ] ; then
- for dir in /bin /usr/bin /usr/local/bin; do
+ for dir in /usr/bin /usr/local/bin; do
if [ -x "${dir}/git" ] ; then
git_cmd="${dir}/git --git-dir=${SYSDIR}/../.git"
break
diff --git a/sys/conf/options b/sys/conf/options
index 03936fd..d9057cc 100644
--- a/sys/conf/options
+++ b/sys/conf/options
@@ -85,6 +85,7 @@ COMPRESS_USER_CORES opt_core.h
CY_PCI_FASTINTR
DEADLKRES opt_watchdog.h
DIRECTIO
+FILEMON opt_dontuse.h
FFCLOCK
FULL_PREEMPTION opt_sched.h
IPI_PREEMPTION opt_sched.h
@@ -222,9 +223,11 @@ NULLFS opt_dontuse.h
PROCFS opt_dontuse.h
PSEUDOFS opt_dontuse.h
REISERFS opt_dontuse.h
+SMBFS opt_dontuse.h
TMPFS opt_dontuse.h
UDF opt_dontuse.h
UNIONFS opt_dontuse.h
+ZFS opt_dontuse.h
# Pseudofs debugging
PSEUDOFS_TRACE opt_pseudofs.h
@@ -287,6 +290,9 @@ UFS_GJOURNAL opt_ufs.h
# they won't make any difference yet).
NFS_ROOT opt_nfsroot.h
+# SMB/CIFS requester
+NETSMB opt_netsmb.h
+
# Options used only in subr_param.c.
HZ opt_param.h
MAXFILES opt_param.h
@@ -836,8 +842,6 @@ HWPMC_MIPS_BACKTRACE opt_hwpmc_hooks.h
# XBOX options for FreeBSD/i386, but some files are MI
XBOX opt_xbox.h
-ZFS opt_dontuse.h
-
# Interrupt filtering
INTR_FILTER
diff --git a/sys/contrib/dev/ath/ath_hal/ar9300/ar9300_attach.c b/sys/contrib/dev/ath/ath_hal/ar9300/ar9300_attach.c
index 5142115..335414e 100644
--- a/sys/contrib/dev/ath/ath_hal/ar9300/ar9300_attach.c
+++ b/sys/contrib/dev/ath/ath_hal/ar9300/ar9300_attach.c
@@ -639,6 +639,11 @@ ar9300_attach(u_int16_t devid, HAL_SOFTC sc, HAL_BUS_TAG st,
/* FreeBSD: to make OTP work for now, provide this.. */
AH9300(ah)->ah_cal_mem = ath_hal_malloc(HOST_CALDATA_SIZE);
+ if (AH9300(ah)->ah_cal_mem == NULL) {
+ ath_hal_printf(ah, "%s: caldata malloc failed!\n", __func__);
+ ecode = HAL_EIO;
+ goto bad;
+ }
/*
* If eepromdata is not NULL, copy it it into ah_cal_mem.
diff --git a/sys/dev/acpica/acpi_pcib.c b/sys/dev/acpica/acpi_pcib.c
index 1a42d74..c4bded8 100644
--- a/sys/dev/acpica/acpi_pcib.c
+++ b/sys/dev/acpica/acpi_pcib.c
@@ -135,15 +135,6 @@ acpi_pcib_attach(device_t dev, ACPI_BUFFER *prt, int busno)
ACPI_FUNCTION_TRACE((char *)(uintptr_t)__func__);
/*
- * Don't attach if we're not really there.
- *
- * XXX: This isn't entirely correct since we may be a PCI bus
- * on a hot-plug docking station, etc.
- */
- if (!acpi_DeviceIsPresent(dev))
- return_VALUE(ENXIO);
-
- /*
* Get the PCI interrupt routing table for this bus. If we can't
* get it, this is not an error but may reduce functionality. There
* are several valid bridges in the field that do not have a _PRT, so
diff --git a/sys/dev/acpica/acpi_pcib_acpi.c b/sys/dev/acpica/acpi_pcib_acpi.c
index 36d9580..882834d 100644
--- a/sys/dev/acpica/acpi_pcib_acpi.c
+++ b/sys/dev/acpica/acpi_pcib_acpi.c
@@ -287,6 +287,12 @@ acpi_pcib_acpi_attach(device_t dev)
sc->ap_handle = acpi_get_handle(dev);
/*
+ * Don't attach if we're not really there.
+ */
+ if (!acpi_DeviceIsPresent(dev))
+ return (ENXIO);
+
+ /*
* Get our segment number by evaluating _SEG.
* It's OK for this to not exist.
*/
@@ -353,7 +359,7 @@ acpi_pcib_acpi_attach(device_t dev)
if (status != AE_NOT_FOUND) {
device_printf(dev, "could not evaluate _BBN - %s\n",
AcpiFormatException(status));
- return_VALUE (ENXIO);
+ return (ENXIO);
} else {
/* If it's not found, assume 0. */
sc->ap_bus = 0;
diff --git a/sys/dev/ath/if_ath_rx_edma.c b/sys/dev/ath/if_ath_rx_edma.c
index abfb57d..2be8627 100644
--- a/sys/dev/ath/if_ath_rx_edma.c
+++ b/sys/dev/ath/if_ath_rx_edma.c
@@ -424,9 +424,10 @@ ath_edma_recv_proc_queue(struct ath_softc *sc, HAL_RX_QUEUE qtype,
if (dosched && sc->sc_kickpcu) {
ATH_KTR(sc, ATH_KTR_ERROR, 0,
"ath_edma_recv_proc_queue(): kickpcu");
- device_printf(sc->sc_dev,
- "%s: handled npkts %d\n",
- __func__, npkts);
+ if (npkts > 0)
+ device_printf(sc->sc_dev,
+ "%s: handled npkts %d\n",
+ __func__, npkts);
/*
* XXX TODO: what should occur here? Just re-poke and
diff --git a/sys/dev/bce/if_bce.c b/sys/dev/bce/if_bce.c
index cdf9517..644348c 100644
--- a/sys/dev/bce/if_bce.c
+++ b/sys/dev/bce/if_bce.c
@@ -2077,10 +2077,12 @@ bce_miibus_statchg(device_t dev)
DBPRINT(sc, BCE_INFO_PHY,
"%s(): Enabling RX flow control.\n", __FUNCTION__);
BCE_SETBIT(sc, BCE_EMAC_RX_MODE, BCE_EMAC_RX_MODE_FLOW_EN);
+ sc->bce_flags |= BCE_USING_RX_FLOW_CONTROL;
} else {
DBPRINT(sc, BCE_INFO_PHY,
"%s(): Disabling RX flow control.\n", __FUNCTION__);
BCE_CLRBIT(sc, BCE_EMAC_RX_MODE, BCE_EMAC_RX_MODE_FLOW_EN);
+ sc->bce_flags &= ~BCE_USING_RX_FLOW_CONTROL;
}
if ((IFM_OPTIONS(media_active) & IFM_ETH_TXPAUSE) != 0) {
@@ -7828,18 +7830,42 @@ bce_ioctl(struct ifnet *ifp, u_long command, caddr_t data)
static void
bce_watchdog(struct bce_softc *sc)
{
+ uint32_t status;
+
DBENTER(BCE_EXTREME_SEND);
BCE_LOCK_ASSERT(sc);
+ status = 0;
/* If the watchdog timer hasn't expired then just exit. */
if (sc->watchdog_timer == 0 || --sc->watchdog_timer)
goto bce_watchdog_exit;
+ status = REG_RD(sc, BCE_EMAC_RX_STATUS);
/* If pause frames are active then don't reset the hardware. */
- /* ToDo: Should we reset the timer here? */
- if (REG_RD(sc, BCE_EMAC_TX_STATUS) & BCE_EMAC_TX_STATUS_XOFFED)
- goto bce_watchdog_exit;
+ if ((sc->bce_flags & BCE_USING_RX_FLOW_CONTROL) != 0) {
+ if ((status & BCE_EMAC_RX_STATUS_FFED) != 0) {
+ /*
+ * If link partner has us in XOFF state then wait for
+ * the condition to clear.
+ */
+ sc->watchdog_timer = BCE_TX_TIMEOUT;
+ goto bce_watchdog_exit;
+ } else if ((status & BCE_EMAC_RX_STATUS_FF_RECEIVED) != 0 &&
+ (status & BCE_EMAC_RX_STATUS_N_RECEIVED) != 0) {
+ /*
+ * If we're not currently XOFF'ed but have recently
+ * been XOFF'd/XON'd then assume that's delaying TX
+ * this time around.
+ */
+ sc->watchdog_timer = BCE_TX_TIMEOUT;
+ goto bce_watchdog_exit;
+ }
+ /*
+ * Any other condition is unexpected and the controller
+ * should be reset.
+ */
+ }
BCE_PRINTF("%s(%d): Watchdog timeout occurred, resetting!\n",
__FILE__, __LINE__);
@@ -7863,6 +7889,7 @@ bce_watchdog(struct bce_softc *sc)
sc->bce_ifp->if_oerrors++;
bce_watchdog_exit:
+ REG_WR(sc, BCE_EMAC_RX_STATUS, status);
DBEXIT(BCE_EXTREME_SEND);
}
diff --git a/sys/dev/bce/if_bcereg.h b/sys/dev/bce/if_bcereg.h
index 7138f3c..dab19c4 100644
--- a/sys/dev/bce/if_bcereg.h
+++ b/sys/dev/bce/if_bcereg.h
@@ -6465,6 +6465,7 @@ struct bce_softc
#define BCE_USING_MSIX_FLAG 0x00000100
#define BCE_PCIE_FLAG 0x00000200
#define BCE_USING_TX_FLOW_CONTROL 0x00000400
+#define BCE_USING_RX_FLOW_CONTROL 0x00000800
/* Controller capability flags. */
u32 bce_cap_flags;
diff --git a/sys/dev/bge/if_bge.c b/sys/dev/bge/if_bge.c
index ff1f030..306acc6 100644
--- a/sys/dev/bge/if_bge.c
+++ b/sys/dev/bge/if_bge.c
@@ -5271,7 +5271,7 @@ bge_start_locked(struct ifnet *ifp)
/*
* Set a timeout in case the chip goes out to lunch.
*/
- sc->bge_timer = 5;
+ sc->bge_timer = BGE_TX_TIMEOUT;
}
}
@@ -5776,12 +5776,40 @@ static void
bge_watchdog(struct bge_softc *sc)
{
struct ifnet *ifp;
+ uint32_t status;
BGE_LOCK_ASSERT(sc);
if (sc->bge_timer == 0 || --sc->bge_timer)
return;
+ /* If pause frames are active then don't reset the hardware. */
+ if ((CSR_READ_4(sc, BGE_RX_MODE) & BGE_RXMODE_FLOWCTL_ENABLE) != 0) {
+ status = CSR_READ_4(sc, BGE_RX_STS);
+ if ((status & BGE_RXSTAT_REMOTE_XOFFED) != 0) {
+ /*
+ * If link partner has us in XOFF state then wait for
+ * the condition to clear.
+ */
+ CSR_WRITE_4(sc, BGE_RX_STS, status);
+ sc->bge_timer = BGE_TX_TIMEOUT;
+ return;
+ } else if ((status & BGE_RXSTAT_RCVD_XOFF) != 0 &&
+ (status & BGE_RXSTAT_RCVD_XON) != 0) {
+ /*
+ * If link partner has us in XOFF state then wait for
+ * the condition to clear.
+ */
+ CSR_WRITE_4(sc, BGE_RX_STS, status);
+ sc->bge_timer = BGE_TX_TIMEOUT;
+ return;
+ }
+ /*
+ * Any other condition is unexpected and the controller
+ * should be reset.
+ */
+ }
+
ifp = sc->bge_ifp;
if_printf(ifp, "watchdog timeout -- resetting\n");
diff --git a/sys/dev/bge/if_bgereg.h b/sys/dev/bge/if_bgereg.h
index 760fc85..6f9822e 100644
--- a/sys/dev/bge/if_bgereg.h
+++ b/sys/dev/bge/if_bgereg.h
@@ -2918,6 +2918,7 @@ struct bge_dmamap_arg {
#define BGE_HWREV_TIGON_II 0x02
#define BGE_TIMEOUT 100000
#define BGE_TXCONS_UNSET 0xFFFF /* impossible value */
+#define BGE_TX_TIMEOUT 5
struct bge_bcom_hack {
int reg;
diff --git a/sys/dev/cpuctl/cpuctl.c b/sys/dev/cpuctl/cpuctl.c
index 742fa10..742ef0db 100644
--- a/sys/dev/cpuctl/cpuctl.c
+++ b/sys/dev/cpuctl/cpuctl.c
@@ -63,7 +63,7 @@ static d_ioctl_t cpuctl_ioctl;
# define DPRINTF(...)
#endif
-#define UCODE_SIZE_MAX (10 * 1024)
+#define UCODE_SIZE_MAX (16 * 1024)
static int cpuctl_do_msr(int cpu, cpuctl_msr_args_t *data, u_long cmd,
struct thread *td);
@@ -326,7 +326,7 @@ update_intel(int cpu, cpuctl_update_args_t *args, struct thread *td)
is_bound = cpu_sched_is_bound(td);
set_cpu(cpu, td);
critical_enter();
- rdmsr_safe(MSR_BIOS_SIGN, &rev0); /* Get current micorcode revision. */
+ rdmsr_safe(MSR_BIOS_SIGN, &rev0); /* Get current microcode revision. */
/*
* Perform update.
@@ -339,7 +339,7 @@ update_intel(int cpu, cpuctl_update_args_t *args, struct thread *td)
*/
do_cpuid(0, tmp);
critical_exit();
- rdmsr_safe(MSR_BIOS_SIGN, &rev1); /* Get new micorcode revision. */
+ rdmsr_safe(MSR_BIOS_SIGN, &rev1); /* Get new microcode revision. */
restore_cpu(oldcpu, is_bound, td);
if (rev1 > rev0)
ret = 0;
@@ -440,7 +440,7 @@ update_via(int cpu, cpuctl_update_args_t *args, struct thread *td)
is_bound = cpu_sched_is_bound(td);
set_cpu(cpu, td);
critical_enter();
- rdmsr_safe(MSR_BIOS_SIGN, &rev0); /* Get current micorcode revision. */
+ rdmsr_safe(MSR_BIOS_SIGN, &rev0); /* Get current microcode revision. */
/*
* Perform update.
diff --git a/sys/dev/cxgbe/adapter.h b/sys/dev/cxgbe/adapter.h
index 434fb53..c67570f 100644
--- a/sys/dev/cxgbe/adapter.h
+++ b/sys/dev/cxgbe/adapter.h
@@ -132,7 +132,6 @@ enum {
#else
FL_BUF_SIZES = 3, /* cluster, jumbo9k, jumbo16k */
#endif
- OFLD_BUF_SIZE = MJUM16BYTES, /* size of fl buffer for TOE rxq */
CTRL_EQ_QSIZE = 128,
@@ -219,6 +218,7 @@ struct port_info {
int qsize_rxq;
int qsize_txq;
+ int linkdnrc;
struct link_config link_cfg;
struct port_stats stats;
@@ -562,7 +562,6 @@ struct adapter {
struct taskqueue *tq[NCHAN]; /* taskqueues that flush data out */
struct port_info *port[MAX_NPORTS];
uint8_t chan_map[NCHAN];
- uint32_t filter_mode;
#ifdef TCP_OFFLOAD
void *tom_softc; /* (struct tom_data *) */
@@ -778,7 +777,7 @@ int t4_os_find_pci_capability(struct adapter *, int);
int t4_os_pci_save_state(struct adapter *);
int t4_os_pci_restore_state(struct adapter *);
void t4_os_portmod_changed(const struct adapter *, int);
-void t4_os_link_changed(struct adapter *, int, int);
+void t4_os_link_changed(struct adapter *, int, int, int);
void t4_iterate(void (*)(struct adapter *, void *), void *);
int t4_register_cpl_handler(struct adapter *, int, cpl_handler_t);
int t4_register_an_handler(struct adapter *, an_handler_t);
diff --git a/sys/dev/cxgbe/common/common.h b/sys/dev/cxgbe/common/common.h
index 1e58c38..22edff6 100644
--- a/sys/dev/cxgbe/common/common.h
+++ b/sys/dev/cxgbe/common/common.h
@@ -67,16 +67,6 @@ enum {
PAUSE_AUTONEG = 1 << 2
};
-#define FW_VERSION_MAJOR_T4 1
-#define FW_VERSION_MINOR_T4 8
-#define FW_VERSION_MICRO_T4 4
-#define FW_VERSION_BUILD_T4 0
-
-#define FW_VERSION_MAJOR_T5 0
-#define FW_VERSION_MINOR_T5 5
-#define FW_VERSION_MICRO_T5 18
-#define FW_VERSION_BUILD_T5 0
-
struct memwin {
uint32_t base;
uint32_t aperture;
@@ -229,6 +219,12 @@ struct tp_params {
unsigned int dack_re; /* DACK timer resolution */
unsigned int la_mask; /* what events are recorded by TP LA */
unsigned short tx_modq[NCHAN]; /* channel to modulation queue map */
+ uint32_t vlan_pri_map;
+ uint32_t ingress_config;
+ int8_t vlan_shift;
+ int8_t vnic_shift;
+ int8_t port_shift;
+ int8_t protocol_shift;
};
struct vpd_params {
@@ -431,6 +427,8 @@ int t4_get_tp_version(struct adapter *adapter, u32 *vers);
int t4_check_fw_version(struct adapter *adapter);
int t4_init_hw(struct adapter *adapter, u32 fw_params);
int t4_prep_adapter(struct adapter *adapter);
+int t4_init_tp_params(struct adapter *adap);
+int t4_filter_field_shift(const struct adapter *adap, int filter_sel);
int t4_port_init(struct port_info *p, int mbox, int pf, int vf);
int t4_reinit_adapter(struct adapter *adap);
void t4_fatal_err(struct adapter *adapter);
diff --git a/sys/dev/cxgbe/common/t4_hw.c b/sys/dev/cxgbe/common/t4_hw.c
index d1f8fd7..3005f63 100644
--- a/sys/dev/cxgbe/common/t4_hw.c
+++ b/sys/dev/cxgbe/common/t4_hw.c
@@ -975,14 +975,14 @@ int t4_check_fw_version(struct adapter *adapter)
switch (chip_id(adapter)) {
case CHELSIO_T4:
- exp_major = FW_VERSION_MAJOR_T4;
- exp_minor = FW_VERSION_MINOR_T4;
- exp_micro = FW_VERSION_MICRO_T4;
+ exp_major = T4FW_VERSION_MAJOR;
+ exp_minor = T4FW_VERSION_MINOR;
+ exp_micro = T4FW_VERSION_MICRO;
break;
case CHELSIO_T5:
- exp_major = FW_VERSION_MAJOR_T5;
- exp_minor = FW_VERSION_MINOR_T5;
- exp_micro = FW_VERSION_MICRO_T5;
+ exp_major = T5FW_VERSION_MAJOR;
+ exp_minor = T5FW_VERSION_MINOR;
+ exp_micro = T5FW_VERSION_MICRO;
break;
default:
CH_ERR(adapter, "Unsupported chip type, %x\n",
@@ -1128,7 +1128,19 @@ int t4_load_fw(struct adapter *adap, const u8 *fw_data, unsigned int size)
const u32 *p = (const u32 *)fw_data;
const struct fw_hdr *hdr = (const struct fw_hdr *)fw_data;
unsigned int sf_sec_size = adap->params.sf_size / adap->params.sf_nsec;
-
+ unsigned int fw_start_sec;
+ unsigned int fw_start;
+ unsigned int fw_size;
+
+ if (ntohl(hdr->magic) == FW_HDR_MAGIC_BOOTSTRAP) {
+ fw_start_sec = FLASH_FWBOOTSTRAP_START_SEC;
+ fw_start = FLASH_FWBOOTSTRAP_START;
+ fw_size = FLASH_FWBOOTSTRAP_MAX_SIZE;
+ } else {
+ fw_start_sec = FLASH_FW_START_SEC;
+ fw_start = FLASH_FW_START;
+ fw_size = FLASH_FW_MAX_SIZE;
+ }
if (!size) {
CH_ERR(adap, "FW image has no data\n");
return -EINVAL;
@@ -1141,9 +1153,8 @@ int t4_load_fw(struct adapter *adap, const u8 *fw_data, unsigned int size)
CH_ERR(adap, "FW image size differs from size in FW header\n");
return -EINVAL;
}
- if (size > FLASH_FW_MAX_SIZE) {
- CH_ERR(adap, "FW image too large, max is %u bytes\n",
- FLASH_FW_MAX_SIZE);
+ if (size > fw_size) {
+ CH_ERR(adap, "FW image too large, max is %u bytes\n", fw_size);
return -EFBIG;
}
if ((is_t4(adap) && hdr->chip != FW_HDR_CHIP_T4) ||
@@ -1164,8 +1175,7 @@ int t4_load_fw(struct adapter *adap, const u8 *fw_data, unsigned int size)
}
i = DIV_ROUND_UP(size, sf_sec_size); /* # of sectors spanned */
- ret = t4_flash_erase_sectors(adap, FLASH_FW_START_SEC,
- FLASH_FW_START_SEC + i - 1);
+ ret = t4_flash_erase_sectors(adap, fw_start_sec, fw_start_sec + i - 1);
if (ret)
goto out;
@@ -1176,11 +1186,11 @@ int t4_load_fw(struct adapter *adap, const u8 *fw_data, unsigned int size)
*/
memcpy(first_page, fw_data, SF_PAGE_SIZE);
((struct fw_hdr *)first_page)->fw_ver = htonl(0xffffffff);
- ret = t4_write_flash(adap, FLASH_FW_START, SF_PAGE_SIZE, first_page, 1);
+ ret = t4_write_flash(adap, fw_start, SF_PAGE_SIZE, first_page, 1);
if (ret)
goto out;
- addr = FLASH_FW_START;
+ addr = fw_start;
for (size -= SF_PAGE_SIZE; size; size -= SF_PAGE_SIZE) {
addr += SF_PAGE_SIZE;
fw_data += SF_PAGE_SIZE;
@@ -1190,7 +1200,7 @@ int t4_load_fw(struct adapter *adap, const u8 *fw_data, unsigned int size)
}
ret = t4_write_flash(adap,
- FLASH_FW_START + offsetof(struct fw_hdr, fw_ver),
+ fw_start + offsetof(struct fw_hdr, fw_ver),
sizeof(hdr->fw_ver), (const u8 *)&hdr->fw_ver, 1);
out:
if (ret)
@@ -4622,14 +4632,17 @@ int t4_fw_upgrade(struct adapter *adap, unsigned int mbox,
const u8 *fw_data, unsigned int size, int force)
{
const struct fw_hdr *fw_hdr = (const struct fw_hdr *)fw_data;
+ unsigned int bootstrap = ntohl(fw_hdr->magic) == FW_HDR_MAGIC_BOOTSTRAP;
int reset, ret;
- ret = t4_fw_halt(adap, mbox, force);
- if (ret < 0 && !force)
- return ret;
+ if (!bootstrap) {
+ ret = t4_fw_halt(adap, mbox, force);
+ if (ret < 0 && !force)
+ return ret;
+ }
ret = t4_load_fw(adap, fw_data, size);
- if (ret < 0)
+ if (ret < 0 || bootstrap)
return ret;
/*
@@ -5338,11 +5351,18 @@ int t4_handle_fw_rpl(struct adapter *adap, const __be64 *rpl)
if (link_ok != lc->link_ok || speed != lc->speed ||
fc != lc->fc) { /* something changed */
+ int reason;
+
+ if (!link_ok && lc->link_ok)
+ reason = G_FW_PORT_CMD_LINKDNRC(stat);
+ else
+ reason = -1;
+
lc->link_ok = link_ok;
lc->speed = speed;
lc->fc = fc;
lc->supported = ntohs(p->u.info.pcap);
- t4_os_link_changed(adap, i, link_ok);
+ t4_os_link_changed(adap, i, link_ok, reason);
}
if (mod != pi->mod_type) {
pi->mod_type = mod;
@@ -5508,6 +5528,91 @@ int __devinit t4_prep_adapter(struct adapter *adapter)
return 0;
}
+/**
+ * t4_init_tp_params - initialize adap->params.tp
+ * @adap: the adapter
+ *
+ * Initialize various fields of the adapter's TP Parameters structure.
+ */
+int __devinit t4_init_tp_params(struct adapter *adap)
+{
+ int chan;
+ u32 v;
+
+ v = t4_read_reg(adap, A_TP_TIMER_RESOLUTION);
+ adap->params.tp.tre = G_TIMERRESOLUTION(v);
+ adap->params.tp.dack_re = G_DELAYEDACKRESOLUTION(v);
+
+ /* MODQ_REQ_MAP defaults to setting queues 0-3 to chan 0-3 */
+ for (chan = 0; chan < NCHAN; chan++)
+ adap->params.tp.tx_modq[chan] = chan;
+
+ /*
+ * Cache the adapter's Compressed Filter Mode and global Incress
+ * Configuration.
+ */
+ t4_read_indirect(adap, A_TP_PIO_ADDR, A_TP_PIO_DATA,
+ &adap->params.tp.vlan_pri_map, 1,
+ A_TP_VLAN_PRI_MAP);
+ t4_read_indirect(adap, A_TP_PIO_ADDR, A_TP_PIO_DATA,
+ &adap->params.tp.ingress_config, 1,
+ A_TP_INGRESS_CONFIG);
+
+ /*
+ * Now that we have TP_VLAN_PRI_MAP cached, we can calculate the field
+ * shift positions of several elements of the Compressed Filter Tuple
+ * for this adapter which we need frequently ...
+ */
+ adap->params.tp.vlan_shift = t4_filter_field_shift(adap, F_VLAN);
+ adap->params.tp.vnic_shift = t4_filter_field_shift(adap, F_VNIC_ID);
+ adap->params.tp.port_shift = t4_filter_field_shift(adap, F_PORT);
+ adap->params.tp.protocol_shift = t4_filter_field_shift(adap, F_PROTOCOL);
+
+ /*
+ * If TP_INGRESS_CONFIG.VNID == 0, then TP_VLAN_PRI_MAP.VNIC_ID
+ * represents the presense of an Outer VLAN instead of a VNIC ID.
+ */
+ if ((adap->params.tp.ingress_config & F_VNIC) == 0)
+ adap->params.tp.vnic_shift = -1;
+
+ return 0;
+}
+
+/**
+ * t4_filter_field_shift - calculate filter field shift
+ * @adap: the adapter
+ * @filter_sel: the desired field (from TP_VLAN_PRI_MAP bits)
+ *
+ * Return the shift position of a filter field within the Compressed
+ * Filter Tuple. The filter field is specified via its selection bit
+ * within TP_VLAN_PRI_MAL (filter mode). E.g. F_VLAN.
+ */
+int t4_filter_field_shift(const struct adapter *adap, int filter_sel)
+{
+ unsigned int filter_mode = adap->params.tp.vlan_pri_map;
+ unsigned int sel;
+ int field_shift;
+
+ if ((filter_mode & filter_sel) == 0)
+ return -1;
+
+ for (sel = 1, field_shift = 0; sel < filter_sel; sel <<= 1) {
+ switch (filter_mode & sel) {
+ case F_FCOE: field_shift += W_FT_FCOE; break;
+ case F_PORT: field_shift += W_FT_PORT; break;
+ case F_VNIC_ID: field_shift += W_FT_VNIC_ID; break;
+ case F_VLAN: field_shift += W_FT_VLAN; break;
+ case F_TOS: field_shift += W_FT_TOS; break;
+ case F_PROTOCOL: field_shift += W_FT_PROTOCOL; break;
+ case F_ETHERTYPE: field_shift += W_FT_ETHERTYPE; break;
+ case F_MACMATCH: field_shift += W_FT_MACMATCH; break;
+ case F_MPSHITTYPE: field_shift += W_FT_MPSHITTYPE; break;
+ case F_FRAGMENTATION: field_shift += W_FT_FRAGMENTATION; break;
+ }
+ }
+ return field_shift;
+}
+
int __devinit t4_port_init(struct port_info *p, int mbox, int pf, int vf)
{
u8 addr[6];
diff --git a/sys/dev/cxgbe/common/t4_hw.h b/sys/dev/cxgbe/common/t4_hw.h
index 8b94169..3bc2096 100644
--- a/sys/dev/cxgbe/common/t4_hw.h
+++ b/sys/dev/cxgbe/common/t4_hw.h
@@ -230,7 +230,15 @@ enum {
FLASH_FW_NSECS = 16,
FLASH_FW_START = FLASH_START(FLASH_FW_START_SEC),
FLASH_FW_MAX_SIZE = FLASH_MAX_SIZE(FLASH_FW_NSECS),
-
+
+ /*
+ * Location of bootstrap firmware image in FLASH.
+ */
+ FLASH_FWBOOTSTRAP_START_SEC = 27,
+ FLASH_FWBOOTSTRAP_NSECS = 1,
+ FLASH_FWBOOTSTRAP_START = FLASH_START(FLASH_FWBOOTSTRAP_START_SEC),
+ FLASH_FWBOOTSTRAP_MAX_SIZE = FLASH_MAX_SIZE(FLASH_FWBOOTSTRAP_NSECS),
+
/*
* iSCSI persistent/crash information.
*/
@@ -248,19 +256,13 @@ enum {
FLASH_FCOE_CRASH_MAX_SIZE = FLASH_MAX_SIZE(FLASH_FCOE_CRASH_NSECS),
/*
- * Location of Firmware Configuration File in FLASH. Since the FPGA
- * "FLASH" is smaller we need to store the Configuration File in a
- * different location -- which will overlap the end of the firmware
- * image if firmware ever gets that large ...
+ * Location of Firmware Configuration File in FLASH.
*/
FLASH_CFG_START_SEC = 31,
FLASH_CFG_NSECS = 1,
FLASH_CFG_START = FLASH_START(FLASH_CFG_START_SEC),
FLASH_CFG_MAX_SIZE = FLASH_MAX_SIZE(FLASH_CFG_NSECS),
- FLASH_FPGA_CFG_START_SEC = 15,
- FLASH_FPGA_CFG_START = FLASH_START(FLASH_FPGA_CFG_START_SEC),
-
/*
* Sectors 32-63 are reserved for FLASH failover.
*/
diff --git a/sys/dev/cxgbe/common/t4_msg.h b/sys/dev/cxgbe/common/t4_msg.h
index 5988070..ef8fbe6 100644
--- a/sys/dev/cxgbe/common/t4_msg.h
+++ b/sys/dev/cxgbe/common/t4_msg.h
@@ -678,6 +678,15 @@ struct cpl_pass_accept_rpl {
__be64 opt0;
};
+struct cpl_t5_pass_accept_rpl {
+ WR_HDR;
+ union opcode_tid ot;
+ __be32 opt2;
+ __be64 opt0;
+ __be32 iss;
+ __be32 rsvd;
+};
+
struct cpl_act_open_req {
WR_HDR;
union opcode_tid ot;
@@ -702,7 +711,7 @@ struct cpl_t5_act_open_req {
__be32 local_ip;
__be32 peer_ip;
__be64 opt0;
- __be32 rsvd;
+ __be32 iss;
__be32 opt2;
__be64 params;
};
@@ -731,7 +740,7 @@ struct cpl_t5_act_open_req6 {
__be64 peer_ip_hi;
__be64 peer_ip_lo;
__be64 opt0;
- __be32 rsvd;
+ __be32 iss;
__be32 opt2;
__be64 params;
};
diff --git a/sys/dev/cxgbe/common/t4_regs_values.h b/sys/dev/cxgbe/common/t4_regs_values.h
index aacda81..40dabf1 100644
--- a/sys/dev/cxgbe/common/t4_regs_values.h
+++ b/sys/dev/cxgbe/common/t4_regs_values.h
@@ -189,4 +189,57 @@
#define X_MBOWNER_FW 1
#define X_MBOWNER_PL 2
+/*
+ * PCI-E definitions.
+ * ==================
+ */
+
+#define X_WINDOW_SHIFT 10
+#define X_PCIEOFST_SHIFT 10
+
+/*
+ * TP definitions.
+ * ===============
+ */
+
+/*
+ * TP_VLAN_PRI_MAP controls which subset of fields will be present in the
+ * Compressed Filter Tuple for LE filters. Each bit set in TP_VLAN_PRI_MAP
+ * selects for a particular field being present. These fields, when present
+ * in the Compressed Filter Tuple, have the following widths in bits.
+ */
+#define W_FT_FCOE 1
+#define W_FT_PORT 3
+#define W_FT_VNIC_ID 17
+#define W_FT_VLAN 17
+#define W_FT_TOS 8
+#define W_FT_PROTOCOL 8
+#define W_FT_ETHERTYPE 16
+#define W_FT_MACMATCH 9
+#define W_FT_MPSHITTYPE 3
+#define W_FT_FRAGMENTATION 1
+
+/*
+ * Some of the Compressed Filter Tuple fields have internal structure. These
+ * bit shifts/masks describe those structures. All shifts are relative to the
+ * base position of the fields within the Compressed Filter Tuple
+ */
+#define S_FT_VLAN_VLD 16
+#define V_FT_VLAN_VLD(x) ((x) << S_FT_VLAN_VLD)
+#define F_FT_VLAN_VLD V_FT_VLAN_VLD(1U)
+
+#define S_FT_VNID_ID_VF 0
+#define M_FT_VNID_ID_VF 0x7fU
+#define V_FT_VNID_ID_VF(x) ((x) << S_FT_VNID_ID_VF)
+#define G_FT_VNID_ID_VF(x) (((x) >> S_FT_VNID_ID_VF) & M_FT_VNID_ID_VF)
+
+#define S_FT_VNID_ID_PF 7
+#define M_FT_VNID_ID_PF 0x7U
+#define V_FT_VNID_ID_PF(x) ((x) << S_FT_VNID_ID_PF)
+#define G_FT_VNID_ID_PF(x) (((x) >> S_FT_VNID_ID_PF) & M_FT_VNID_ID_PF)
+
+#define S_FT_VNID_ID_VLD 16
+#define V_FT_VNID_ID_VLD(x) ((x) << S_FT_VNID_ID_VLD)
+#define F_FT_VNID_ID_VLD(x) V_FT_VNID_ID_VLD(1U)
+
#endif /* __T4_REGS_VALUES_H__ */
diff --git a/sys/dev/cxgbe/firmware/t4fw-1.8.11.0.bin.uu b/sys/dev/cxgbe/firmware/t4fw-1.8.11.0.bin.uu
new file mode 100644
index 0000000..2faec4e
--- /dev/null
+++ b/sys/dev/cxgbe/firmware/t4fw-1.8.11.0.bin.uu
@@ -0,0 +1,8417 @@
+/*-
+ * Copyright (c) 2013 Chelsio Communications, Inc.
+ * All rights reserved.
+ *
+ * Redistribution and use in source and binary forms, with or without
+ * modification, are permitted provided that the following conditions
+ * are met:
+ * 1. Redistributions of source code must retain the above copyright
+ * notice, this list of conditions and the following disclaimer.
+ * 2. Redistributions in binary form must reproduce the above copyright
+ * notice, this list of conditions and the following disclaimer in the
+ * documentation and/or other materials provided with the distribution.
+ *
+ * THIS SOFTWARE IS PROVIDED BY THE AUTHOR AND CONTRIBUTORS ``AS IS'' AND
+ * ANY EXPRESS OR IMPLIED WARRANTIES, INCLUDING, BUT NOT LIMITED TO, THE
+ * IMPLIED WARRANTIES OF MERCHANTABILITY AND FITNESS FOR A PARTICULAR PURPOSE
+ * ARE DISCLAIMED. IN NO EVENT SHALL THE AUTHOR OR CONTRIBUTORS BE LIABLE
+ * FOR ANY DIRECT, INDIRECT, INCIDENTAL, SPECIAL, EXEMPLARY, OR CONSEQUENTIAL
+ * DAMAGES (INCLUDING, BUT NOT LIMITED TO, PROCUREMENT OF SUBSTITUTE GOODS
+ * OR SERVICES; LOSS OF USE, DATA, OR PROFITS; OR BUSINESS INTERRUPTION)
+ * HOWEVER CAUSED AND ON ANY THEORY OF LIABILITY, WHETHER IN CONTRACT, STRICT
+ * LIABILITY, OR TORT (INCLUDING NEGLIGENCE OR OTHERWISE) ARISING IN ANY WAY
+ * OUT OF THE USE OF THIS SOFTWARE, EVEN IF ADVISED OF THE POSSIBILITY OF
+ * SUCH DAMAGE.
+ */
+begin-base64 644 t4fw
+AAADpgEICwAAAQkBAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAABAAAAAAAAAAAAAAAAAAAAAAAAAAAA
+AAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAA
+AAAAAAAAAAAAAAAAAAAAAAAAA4sDmgOhAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAA
+AAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAA
+AAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAA
+AAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAA
+AAAAAAAAAAAAAENoZWxzaW8gRlcgUlVOTUVNIERFQlVHPTAgKEJ1aWx0IEZyaSBNYXkgMjQgMTM6
+NDA6MTQgUERUIDIwMTMgb24gY2xlb3BhdHJhLmFzaWNkZXNpZ25lcnMuY29tOi9ob21lL2Zpcm13
+YXJlL2N2cy9mdy1yZWxlYXNlKSwgVmVyc2lvbiBUNHh4IDAxLjA4LjBiLjAwAAAAAAAAAPj2L99g
+AMAA4QAwuHj///8f/OFAgAAAAeEAe3AAABAAH//8EOEBlHAgAAAA4QGcBOEAeQAAAgBA4QB5gAAG
+AEAAAgAKAAYACuEAeQQACgAAgAABAuEAezzhAHtE4QB75OIAAAAAAQAA4QB7kCAAAAAAAIAA4QB7
+AAAAQAHhAHucAABAAERERELgAAAA4wAEY0REREDjAAgAIAACXAAAAAAf/5BQAAAAAB//kFQAAAAA
+H/+QWAAAAAAf/5BcH//AAAAAAAAAAAAAwAAS/88T/8+EIAQzAZMgEf/OEv/OkhAR/84S/86SEBH/
+zgH0MRH/zSIK/5IQAOQxAAUxAQIAEv/KAucxAhYAEf/JgRABAV/AIQIRAckREf/GEv/GkhAR/8YS
+/8aSEGAADxH/wRL/xZIQEf/BEv/EkhCBEBH/w8AgkhES/8KSEsAgkhMS/8GSEIIQAvJQZS/3Ef+/
+xy+SEBH/vpIQEv++E/++kyDAMpMhE/+9kyKCIhL/vBP/vJMgIyIhFP+7BDMByTgT/7qDMAODFAgz
+ERT/uKQzkyET/6yTImAACMIwkyET/6mTIhL/s5AgkCGQIpAjkCSQJZAmkCeQKJApkCqQK5AskC2Q
+LpAvICYQICYRgiIS/6bAMC03MC03NC03OC03PCM9AXIz7QACABL/o8AwLzcALzcQLzcgLzcwIz0B
+cjPtAAIAEv+ZwDAoNzAoNzQoNzgoNzwjPQFyM+0DAgAS/5YjCgAnNwAnNxAnNyAnNzAjPQFyM+0D
+AgAS/5AV/5EW/5HAMNcgBWYBYAASBDYFAAIA0w/TDwUzDG47FAdHFAcEQ3Yx5gQ2BQUzDG877QAC
+ABL/hRX/gyMKAAInAgcEQwQ+BQUzDAdHFG878AMCABL/f8kugyCEIYUivCJ0Ow6GULRVljC0M3Qz
+9GP/5gBlP+JlX98S/3PAMgMuBQMCABL/asAwKDdAKDdEKDdIKDdMIz0BcjPtAwIAAAIAEv9tLScA
+wBEBSTEASDEBAgDAABT/aQTSMRX/aJRQFP9oBNMxFf9olFAU/2cE1DEV/2eUUBT/ZwTVMRX/ZpRQ
+EP9mAwAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAA
+AAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAA
+AAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAA
+AAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAA
+AAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAA
+AAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAA
+AAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAA
+AAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAA
+AAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAA
+AAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAA
+AAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAA
+AAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAA
+AAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAf/AAA
+H/wAAOMACfgf/AAAH/wAAOMACfgf/AAAH/wAAOMACfgf/4AAH/+I8OMACfgf/4jwH/+I8OMAEugf
+/4jwH/+I8OMAEugf/4jwH/+KcOMAEugf/4pwH/+QUOMAFGgf/5BQH/+snOMAGkgf/6ycH/+snOMA
+NpQf/8AAH//9DeMANpQgAAAAIAABauMAc6QgAAF4IAABfOMAdRAgAAF8IAABheMAdRQgAAGYIAAB
+nOMAdSAgAAGcIAABpeMAdSQgAAG4IAABvOMAdTAgAAG8IAABxeMAdTQgAAHYIAAB2OMAdUAgAAHc
+IAAB4uMAdUAgAAH4IAAB+OMAdUggAAH8IAAB/OMAdUggAAIYIAACGOMAdUggAAIcIAACHOMAdUgg
+AAI4IAACOOMAdUggAAI8IAACPOMAdUggAAJYIAACWOMAdUggAAJcIAACYuMAdUggAAJ4IAACeOMA
+dVAgAAJ8IAACguMAdVAgAAKYIAGsnuMAdVggAoAAIAKTNOMCH2AgApM0IAKTNOMCMpQgApM4IAYK
+IOMCMpggBgogIAYO0OMFqYAgBoAAIAaNEOMFrjAgBo0QIAduWuMFu0AgB25gIAdvLOMGnJAgCMAA
+IAjAAOMGnVwgCMAAIAjAAOMGnVwgCMAAIAk2z+MGnVwAAAAAAAAAAAAAAAAgAA/2IAAP6CAAFAEg
+AA/oIAATbSAAD+ggABCdIAATBSAAEoogAA/oIAASOSAAEeogABF9IAAP1SAAEScgAA/oIAAP6CAA
+D+ggABC9AAAAAP///////w/8///w////APwgAJtzIACcsyAAnOMgAJypIACcaSAAnF8gAJwkIACc
+GiAAnBAgAJvAIACc4SAAm7YgAJucAAAAAAAAAAAAAAAAAAAACgAAAAoAAAAUAAAACgAAAAoAAAAK
+AAAACgAAAAoAAAAKAAAAAAAAAAAAAAAAAAAIAAAAEAAAAEAAAAEAAAAACAAAABAAAABAAAABAAAA
+BAAAABAAAABAAAABAAAA/xgwYGAAAAD/AAECAgAAACAG8tUgBvGCIAbzwyAG85ogBvNxIAbzSCAG
+8ycgBvL+ECBAAAAAAAAAAAAAAAAAAAQAAgABAACAAEAAIAAQAAggBw0rIAcNKyAHDLkgBwyAIAcL
+vyAHC6UgBwulIAcNKyAHDSsgBwulIAcLiyAHC4sgBw0rIAcNKyAHC1IgBw0rIAcNKyAHDSsgBw0r
+IAcNKyAHDSsgBw0rIAcNKyAHDSsgBw0rIAcNKyAHDSsgBw0rIAcNKyAHDSsgBw0rIAcLZyACivgA
+AAABIAKK/AAAAAIgAo2oAAAA/yACiTgAAAD/IAKJOAAAAAAgAo2oAAAAACACijAAAAABIAKKOAAA
+AAQgAopAAAAACCACikwAAAAQIAKKVAAAACAgAopcAAAAQCACimQAAACAIAKKeAAAAQAgAoqMAAAC
+ACACiqQAAAQAIAKKuAAACAAgAorIAAAQACACitQAACAAIAKK6AAAQAAgAoocAAAAECACiiQAAAAR
+IAKJnAAAAQAgAomoAAAAgCACibgAAABAIAKJyAAAACAgAonYAAAAECACiegAAAAIIAKJ9AAAAAQg
+AooAAAAAAiACigwAAAABAAAAAAAAAAAAAAABAAAAAQAAAAEAAAABAAAAAQAAAAEAAAABAAAAAQAA
+AAEAAAABAAAAAQAAAAEAAAABAAAAAQAAAAEAAAABAAAABwAAAAcAAAAGAAAABgAMNQAAEEaqABRY
+VQAYagAAACtoAAAjgwAAGGoAAA0GAAALKgAAAAAAAAAAAAAAAAAAaCsAAGgrAABsggAAb5wAAEpo
+AABKaAAATSkAAEpoAABO6gAATJgAAFI9AABPuAABhqAAAYagAAII1gACCNYAAgjVAAII1QACiwsA
+AosLAAII1QACtnIAArZyAAMNQAAEBgcAAAAAAAAAAAAAAAAgB1wIIAdb5iAHXAMgB1wDIAdb5iAH
+W+YgB1wIIAdcCCAHW+YgB1wIIAdb5iAHXAggB1wDIAdb5iAHW+YgB1vmIAdb5iAHW+YgB1wIIAdb
+5iAHW+YgB1vmIAdb5iAHW+YAAgIFBQgICwsODhERFBQXFxoaHR0gICMjJiYpKSwsLy8yMjU1ODg7
+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
+AAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAA
+AAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAA
+AAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAA
+AAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAA
+AAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAA
+AAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAA
+AAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAA
+AAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAA
+AAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAA
+AAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAA
+AAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAA
+AAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAA
+AAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAA
+AAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAA
+AAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAA
+AAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAA
+AAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAA
+AAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAA
+AAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAA
+AAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAA
+AAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAA
+AAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAA
+AAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAA
+AAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAA
+AAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAA
+AAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAA
+AAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAA
+AAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAA
+AAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAA
+AAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAA
+AAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAA
+AAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAA
+AAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAA
+AAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAA
+AAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAA
+AAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAA
+AAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAA
+AAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAA
+AAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAA
+AAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAA
+AAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAA
+AAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAA
+AAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAA
+AAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAA
+AAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAA
+AAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAA
+AAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAA
+AAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAA
+AAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAA
+AAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAA
+AAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAA
+AAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAA
+AAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAA
+AAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAA
+AAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAA
+AAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAA
+AAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAA
+AAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAA
+AAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAA
+AAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAA
+AAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAA
+AAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAA
+AAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAA
+AAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAA
+AAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAA
+AAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAA
+AAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAA
+AAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAA
+AAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAA
+AAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAA
+AAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAA
+AAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAA
+AAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAA
+AAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAA
+AAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAA
+AAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAA
+AAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAA
+AAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAA
+AAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAA
+AAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAA
+AAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAA
+AAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAA
+AAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAA
+AAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAA
+AAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAA
+AAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAA
+AAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAA
+AAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAA
+AAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAA
+AAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAA
+AAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAA
+AAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAA
+AAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAA
+AAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAA
+AAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAA
+AAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAA
+AAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAA
+AAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAA
+AAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAA
+AAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAA
+AAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAA
+AAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAA
+AAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAA
+AAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAA
+AAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAA
+AAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAA
+AAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAA
+AAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAA
+AAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAA
+AAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAA
+AAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAA
+AAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAA
+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
+9RQn8IEAAAjuAey7DAdxAQAALvYJ/+EGFaANFQD6qygVoAwFAFgAF+okAApYBIAA/ABCHaANJQBb
+/3jRDysyEi5SawuKROqqDwtgBIAA/0ABBTANFQD7QAgVr7uBAFgACfZiRhWv/iYAAAAAAAAAAGwQ
+BMBRAyQsAyIuAlI5pCLRDwAAbBAEKSEEhyD/IAAUtYMdAOl3CAxHgoAA+O8AC7AKFQD2/4AV5JMB
+AOZyACyBCoAA/UABBV/49QAIqAPmqgEMggqAAPsgBADROp0A6GYBCkAKgAAIZgLmdgAigRGAACUh
+AshKsFrqJQIpkASAANEPsVvrJQIpkASAANEPANIw0Q9sEAQnIQSIIA8CAPb/4BWvkzkA6SUFKy8C
+gADlhQgM7wKAAA1dDA0AhyohA/oAIh3mwwEADDtgCgpGDwIA/GJgR1AMBQAMnRENXQwNIIcMO2Jv
+Phf4wT4N7/31AMihc6EqCZIRojLRDwAAAADnIQQkyAUAAAkJTyklBXeRUbB2DGUR9QAAQv/+/gAA
+AAmUEaQ0/uAAFzWUHQDuiAgMz4KAAAmIDPkfgBWkRAEA7oIAKgEKgADpIQUtsAqAAA1vAw/uAQ5m
+ApaA+EAIFa/+hgAsJQX//pwNoAkFAAAAIAK28iACtuIgArbqIAK28iACtuIgArbiIAK24iACtuog
+ArbyIAK28iACtuIgArbiIAK3RCACt6QgArdEIAK3RCACt6QgArekIAK3pCACt0QgArdEIAK3RCAC
+t6QgArekIAK4USACuUsgArlVIAK4USACuUsgArlLIAK5SyACuVUgArhRIAK4USACuUsgArlLIALk
+WSAC5XQgAuRZIALkWSAC5XQgAuV0IALldCAC5FkgAuRZIALkWSAC5XQgAuV0IALmRSAC5sUgAuc+
+IALneCAC54sgAuhNIALoyCAC6NsgAulFIALpdSAC6ZoAAAAAIALw+SAC8kUgAu/MIALv7yAC8OEg
+Au+sIALvxCAC8mIgAvLAIAL2NSAC9PUgAvUvIALzUSAC88MgAvQxIAL0cCAC9KggAvWlIAL1nSAC
+9ZUgAvVZIAL1UQAAAAAAAAAAIAMgUiADIGggAyH6IAMijSADIwkgAyOFIAMkcCADJS0BEBgBAAEA
+AAAAAAAAAAAAIAO36SADt+kgA7X7IAO36SADtfsgA7fEIAO3nSADtfsgA7bRIAO1+yADtfsgA7bI
+IAO1+yADt+kgA7X7IAO1+yADt+kgA7YEAAAAAAAAAAADAQACAAAAAAAAAAAAAAAAIAQ78iAEPCAg
+BD2TIAQ78iAEPYsgBD11IAQ78iAEO/IgBDvyIAQ78iAEO/IgBDvyIAQ78iAEO/IgBDvyIAQ78iAE
+PWogBD1fIAQ78iAEO/IgBDvyIAQ78iAEO/IgBDvyIAQ78iAEO/IgBDvyIAQ78iAEO/IgBDvyIAQ7
+8iAEO/IgBD0wIAQ78iAEPRUgBD0VIAQ78iAEO/IgBDz1IAQ78iAEPRUgBDvyIAQ78iAEO/IgBDvy
+IAQ78iAEO/IgBDvyIAQ78iAEO/IgBDvyIAQ78iAEO/IgBDvyIAQ78iAEO/IgBDvyIAQ78iAEO/Ig
+BDvyIAQ78iAEO/IgBDvyIAQ78iAEPNUAAAAAAAAAAAAAAAAgBF1WIARdViAEXCkgBF1WIARXRiAE
+XUwgBF0oIARXRiAEV0YgBFdGIARXRiAEV0YgBFwMIARdViAEV0YgBFwMIARdVgAAAAAAAAAAAAAA
+ACAEaokgBGreIARtMyAEbOIgBGyaIARsbyAEbFwgBGxWIARrrAAAAAAAAAAAAAAAAAEQGAEAAgAA
+ARAYAQACAAAgBLh+IAS4UyAEtsMgBLgbIAS4GyAEuBsgBLbDIAS4GyAEtsMgBLf7IAS2wyAEtsMg
+BLf7IAS4GwAAAAAAAAAAIATcCyAE19QgBNvfIATbsyAE24cgBNf7IATX+yAE2BIgBNtuIATYRyAE
+2DMgBNf7IATYRyAE1/sgBNf7IATX+yAE3AsAAAAAAAAAAAAAAAAAAAAAIAUuoCAFJQggBSS4IAUi
+vAAAAAAAAAAAAAAAACAFJPIgBSTuIAUk7iAFJO4gBSTyIAUk7iAFJO4gBSTuIAUk7iAFJO4gBSTu
+IAUk7iAFJO4gBSTuIAUk7iAFJO4gBSTuIAUk7iAFJO4gBSTuIAUk7iAFJO4gBSTuIAUk7iAFJO4g
+BSTuIAUk7iAFJO4gBSTuIAUk7iAFJO4gBSTuIAUk7iAFJO4gBSTuIAUk7iAFJO4gBSTuIAUk7iAF
+JO4gBSTuIAUk7iAFJO4gBSTuIAUk7iAFJO4gBSTuIAUk7iAFJO4gBSTuIAUk7iAFJO4gBSTuIAUk
+7iAFJO4gBSTuIAUk7iAFJO4gBSTuIAUk7iAFJPIgBSTuIAUk7iAFJO4gBSTyIAUk7iAFJO4gBSTu
+IAUk8iAFJO4gBSTuIAUk7iAFJPIgBSTuIAUk7iAFJO4gBSTyIAUk7iAFJO4gBSTuIAUk8iAFJO4g
+BSTuIAUk7iAFJPIgBSTuIAUk7iAFJO4gBSTyIAUk7iAFJO4gBSTuIAUk8iAFJO4gBSTuIAUk7iAF
+JPIgBSTuIAUk7iAFJO4gBSTyIAUk7iAFJO4gBSTuIAUk8iAFJO4gBSTuIAUk7iAFJPIgBSTuIAUk
+7iAFJO4gBSTyIAUk7iAFJO4gBSTuIAUk8iAFJO4gBSTuIAUk7iAFJPIgBSTuIAUk7iAFJO4gBSTu
+IAUk7iAFJO4gBSTuIAUk7iAFJO4gBSTuIAUk7iAFJO4gBSTuIAUk7iAFJO4gBSTuIAUk7iAFJO4g
+BSTuIAUk8iAFJO4gBSTuIAUk7iAFJO4gBSTuIAUk7iAFJO4gBSTuIAUk7iAFJO4gBSTuIAUk8iAF
+JO4gBSTuIAUk7iAFJO4gBSTuIAUk7iAFJO4gBSTuIAUk7iAFJO4gBSTuIAUk7iAFJO4gBSTuIAUk
+7iAFJO4gBSTuIAUk7iAFJO4gBSTuIAUk7iAFJO4gBSTuIAUk8iAFJO4gBSTuIAUk7iAFJPIgBSTu
+IAUk7iAFJO4gBSTyAAAAAAAAAAAAAAAAIAUlQCAFJpUgBShDIAUoNSAFKCAgBSgSIAUn/SAFJ+8g
+BSfaIAUnzSAFJ7ogBSetIAUnmiAFJ40gBSd6IAUlNiAFJ2cgBSdaIAUnRyAFJzogBScmIAUnHCAF
+JwsgBScBIAUm8CAFJTYgBSU2IAUlNiAFJTYgBSU2IAUlNiAFJuMgBSVZIAUlNiAFJTYgBSU2IAUm
+xiAFJq0gBSajIAUmhCAFJTYgBSZxIAUmVyAFJkQgBSYqIAUmFyAFJf8gBSXsIAUl2SAFJXMAAAAA
+AAAAACAFLuQgBS7uIAUwWiAFMDIgBTArIAUwJCAFMB0gBTAWIAUwDyAFMAggBTABIAUv9yAFL+0g
+BS/PIAUvbiAFLz4gBS81IAUvICAFLxYgBS8PIAUvAiAFLvsgBS7WAAAAACAFjNQgBYzqIAWOgCAF
+jxUgBY+UIAWQDSAFkPIgBZGtIAW2kCAFs+wgBbKAIAWwVCAFrlAgBaNkIAWlsCAFrUggBaIIAAAA
+AAAAAAAAAAAAAw8IEoOOk5IAAAAAAAAAACAF1MUgBdN5IAXTfiAF1MUgBdN5IAXTeSAF03kgBdN+
+IAXUxSAF1MUgBdN5IAXTeVJWAAAAAAAAQb3NZQAAAABjb25maWd1cmF0aW9uIGZpbGUgcGFyc2Vy
+IGZvdW5kIGFkZGl0aW9uYWwgY29uZmlndXJhdGlvbiBhZnRlciBbZmluaV0KAAAwAAAAZmNvZQAA
+AAB2bmljX2lkAHZsYW4AAAAAdG9zAGV0aGVydHlwZQAAAG1hY21hdGNoAAAAAG1wc2hpdHR5cGUA
+AGZyYWdtZW50YXRpb24AAABiYXNpY3ZpcnR1YWwAAAAAc3lubWFwZW4AAAAAc3luNHR1cGVuaXB2
+NgAAAHN5bjJ0dXBlbmlwdjYAAABzeW40dHVwZW5pcHY0AAAAc3luMnR1cGVuaXB2NAAAAG9mZG1h
+cGVuAAAAAHRubG1hcGVuAAAAAHRubGFsbGxrcAAAAGhhc2h0b2VwbGl0egAAAAB0cF9waW8AAHRw
+X3RtX3BpbwAAAG5pY192bQAAbmljX3VtAABuaWNfdW1faXNnbABvZmxkAAAAAHJkZHAAAAAAcmRt
+YWMAAABpc2NzaV9pbml0aWF0b3JfcGR1AGlzY3NpX3RhcmdldF9wZHUAAAAAaXNjc2lfaW5pdGlh
+dG9yX2ZvZmxkAAAAaXNjc2lfdGFyZ2V0X2ZvZmxkAABmY29lX2luaXRpYXRvcgAAZmNvZV90YXJn
+ZXQAcG9mY29lX2luaXRpYXRvcgAAAABwb2Zjb2VfdGFyZ2V0AAAAcHBwAGRjYngAAAAAIAKS1AAE
+AAAAAAQAAAQAAAAAAAAgBfPwIAXbUCAF8+AgBdtgIAXbdCAF2oAgBdwcIAXaGAABAgMAAAAAIAOH
+ZCADhvggBGQQAAAAACADhvAgA4boIAOG4AAAAAAwMTIzNDU2Nzg5YWJjZGVmQUJDREVGAAAAAAAA
+AAAAAEFCQ0RFRkdISUpLTE1OT1BRUlNUVVZXWFlaYWJjZGVmZ2hpamtsbW5vcHFyc3R1dnd4eXow
+MTIzNDU2Nzg5Ky8AAAAAAAAAAAAAAAAAAAAAIAAf9AABAAAgAB/UAAUAASAAH7gAAwAFIAAfmAAH
+AABjglNjAAAAAP///////wAAAAQECAAHQwAAAAAAAAAAAP////////////////////8AIQAJAIEA
+QQAhAAkAIQAJAIEAQQCBAEEAgQBBAIEAQQAhAAkAgQBBACEACQAhABEAgQBBAIEAQQAhAAkAgQBB
+ACEACQAhAAkBAQBBAIEAIQCBACEAEBBhAQEAgQEBAIEDAgEAAAAAAAAAAAAAAAAAAAAAgAAAAQAA
+AAIAAAAEAAAACAAAABAAAAAAAAAAAAAgQIAAAAAAAAAAAAAAAAAAIAKJOAAAAAAgAo2oAAAAACAC
+iTwAAAABIAKNtAAAAAIgAolEAAAABCACiUwAAAAIIAKJVAAAABAgAo28AAAAICACiVgAAABAIAKJ
+ZAAAAIAgAolwAAABACACiXwAAAIAAAAABQAAAAMAAAABAAAAAiAFupggBbpUIAW5RCAFuAwgBbfM
+IAW3oCAFt/QAAAAAAAACAAAABAAAAAgATkEAAFYwAABWMQAAVjIAAFYzAABWNAAAVjUAAFY2AABW
+NwAAVjgAAFY5AABWQQAAVkIAAFZDAABWRAAAVkUAAFZGAABhbGwAKgAAAG5vbmUAAAAAMHgAAHBv
+cnQAAAAAcHJvdG9jb2wAAAAAZ2xvYmFsAABmdW5jdGlvbgAAAABmaW5pAAAAAHJlZwBmaWx0ZXJN
+b2RlAABmaWx0ZXJNYXNrAAByc3NfZ2xiX2NvbmZpZ19tb2RlAHJzc19nbGJfY29uZmlnX29wdGlv
+bnMAAHNnZV90aW1lcl92YWx1ZQB0cF9wbXJ4AHRwX3BtcnhfcGFnZXNpemUAAAAAdHBfbnJ4Y2gA
+AAAAdHBfcG10eAB0cF9wbXR4X3BhZ2VzaXplAAAAAHRwX250eGNoAAAAAG10dXMAAAAAbnZmAHd4
+X2NhcHMAcl9jYXBzAABuaXFmbGludAAAAABuZXEAbmV0aGN0cmwAAAAAbnZpAHJzc252aQAAbmV4
+YWN0ZgBjbWFzawAAAHBtYXNrAAAAbmV0aG9mbGQAAAAAbnJvdXRlAABuY2xpcAAAAG5maWx0ZXIA
+bnNlcnZlcgBuaGFzaAAAAHRwX2wydAAAdHBfZGRwAAB0cF9kZHBfaXNjc2kAAAAAdHBfc3RhZwB0
+cF9wYmwAAHRwX3JxAAAAaXNjc2lfbnRhc2sAaXNjc2lfbnNlc3MAaXNjc2lfbmNvbm5fcGVyX3Nl
+c3Npb24AaXNjc2lfbmluaXRpYXRvcl9pbnN0YW5jZQAAAGlzY3NpX21heF9zZ2UAAABwcG1fbWF4
+X3pvbmVzAAAAcHBtX3pvbmVfcmFuZ2UwAHBwbV96b25lX3JhbmdlMQBwcG1fem9uZV9yYW5nZTIA
+cHBtX3pvbmVfcmFuZ2UzAGZjb2VfbmZjZgAAAGZjb2VfbnZucAAAAGZjb2VfbnNzbgAAAGRjYgBi
+Z19tZW0AAGxwYmtfbWVtAAAAAGh3bQBsd20AZHdtAHZlcnNpb24AY2hlY2tzdW0AAAAAMDEyMzQ1
+Njc4OWFiY2RlZmdoaWprbG1ub3BxcnN0dXZ3eHl6AAAAAFllcwBTZXNzaW9uVHlwZQBPRk1hcmtl
+cgAAAABJRk1hcmtlcgAAAABEYXRhRGlnZXN0AABIZWFkZXJEaWdlc3QAAAAARXJyb3JSZWNvdmVy
+eUxldmVsAABJbW1lZGlhdGVEYXRhAAAARGF0YVBEVUluT3JkZXIAAERhdGFTZXF1ZW5jZUluT3Jk
+ZXIASW5pdGlhbFIyVAAATWF4Q29ubmVjdGlvbnMAAE1heFJlY3ZEYXRhU2VnbWVudExlbmd0aAAA
+AABJbml0aWF0b3JOYW1lAAAASW5pdGlhdG9yQWxpYXMAAERlZmF1bHRUaW1lMldhaXQAAAAARGVm
+YXVsdFRpbWUyUmV0YWluAABNYXhCdXJzdExlbmd0aAAARmlyc3RCdXJzdExlbmd0aAAAAABNYXhP
+dXRzdGFuZGluZ1IyVAAAAFRhcmdldE5hbWUAAFRhcmdldEFsaWFzAFRhcmdldEFkZHJlc3MAAABU
+YXJnZXRQb3J0YWxHcm91cFRhZwAAAABBdXRoTWV0aG9kAABTZW5kVGFyZ2V0cz1BbGwAQ0hBUF9B
+AABDSEFQX0kAAENIQVBfQwAAQ0hBUF9OAABDSEFQX1IAAERpc2NvdmVyeQAAAE5vcm1hbAAATm9u
+ZQAAAABDUkMzMkMAAENSQzMyQyxOb25lAE5vbmUsQ1JDMzJDAENIQVAAAAAAQ0hBUCxOb25lAAAA
+Tm9uZSxDSEFQAAAATm90VW5kZXJzdG9vZAAAAElycmVsZXZhbnQAAFJlamVjdAAATm8AADUAAABD
+SE5ldCAxLjAwAAAAAAAMAAAAAAEAAXwADAEAAAAAEAAAABQgBgVQAAADFQ5AAAAf/AAAH/wAAB//
+rKAf/6ygIAYO0CAGEkAgB28wIAdvMCAHwAAgCBBAIAhAACAIwAAAAAAA4QGOAAABkQyAAAAAIAkG
+gCAJBjAgCQawIAkGYAAACAD///P/H/+QoOECFgDhAhoA///I////F////x//AACAAAAAEAAAAhkc
+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
+qpox0Q9kTp2MZ4zOLMEinDHRD2ROjy7Sq54x0Q9kQeEoYGAvYGEIiBEI/wL+YCYV4AIFANEPZE5s
+KWBUC4tH+WAMK+IAnQDHr/pgJhWgAgUA0Q9kTk+LZ4u+i72bMdEPZE5CLsJVLcJUrt2w3Z0x0Q9k
+TjEvwlSfMdEPZE4nKcJTKMJSqYiwiJgx0Q9kThYqwlKaMdEPZE4Mi2eLviyyEIu/rLuwu5sx0Q9k
+TfiMZ4zOjM+cMdEPZE3rjWeN3o7cjduu3bDdnTHRD2RN2I5nju6O654x0Q9kTcuPZ4/+iP6P/aj/
+sP+fMdEPZE24iGeIjomKiImpiLCImDHRD2RNpYlniZ6JmZkx0Q9kTZiKZ4qui6iKp6uqsKqaMdEP
+ZE2Fi2eLvou3mzHRD2RNeIxnjM6NxozFrcywzJwx0Q9kTWWNZ43ejdWdMdEPZE1YjmeO7i/hKS7h
+KK/usO6eMdEPZE1Dj2eP/i/xKJ8x0Q9kTTWIZ4iOKYEnKIEmqYiwiJgx0Q9kTSCJZ4meKZEmmTHR
+D2RNEopniq4roSUqoSSrqrCqmjHRD2RM/Ytni74rsSSbMdEPZEzvjGeMzi3BIyzBIq3MsMycMdEP
+ZFDR2mBYGG4tYAwI3RH9RgAO8I4FAA7dAvxgJhXgAgUA0Q9kUK6OMf7EBh2o7h0A/sQmHaACBQDR
+D2RQl48x/swmHej/HQD+zAYd4AIFANEPZJEjL8LEjmAP7gwOWRQOmRH5rwAM9FsFAKuZ+ThoFaTu
+AQAA4QQAqxoLiAL5OGYVoAIFANEPAGRQSYkxDwIADwIAaJAB3pDuZHYrUASAAFsU18Ag0Q8AKMF+
+ZI0BKsF3KcF2qpmwmfhgJhXgAgUA0Q8rwX5kvP8twXb8YCYV4AIFANEPxy/RDwAAAC1gDC6wgADQ
+BP5fAA8wClUA+kBoHeAuAQDsXGwZcASAAFg16fJgJhWgAgUA0Q+PZ4/+iPOP8qj/sP/+YCYV4AIF
+ANEPiGeIjoiC+GAmFaACBQDRD4pvqaqwqvpgJhWgAgUA0Q+Lb/pgJhXgAgUA0Q8sYROpzLDM/GAm
+FaACBQDRDy1hE/xgJhXgAgUA0Q8AAAAAAAD/7nQNr/n1AChgDC6wgACBBACvGg/uAv9wBh2gAgUA
+0Q8AAAAAAAAAbBAEKCAKIyAIGltFwFDrIAspoASAAOo6CwwBFgAALKK5fLMXKSEC75tdZOgFAAAl
+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
+AS4EgYEBGwIBAQAOAoEBAS4CgYEACgIAAQAOAoEBAA4CAQEBGgGBgQEOAgEBAAAAAAAAAAAAAAAA
+AAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAcAwEAAA4DAQEATwQBAQBfBAEBADwEAQAAAAAA
+AABs/wEBAEwEAQEAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAACADatQgA2tQIANq
+8CADbFQAAAAAHwAAAAAAAAEAAAAAAAAAAgEAAAAAAAAEAgAAAAAAAAcSAAAAAAAACAMAAAAAAAAP
+EwAAAAAAABAEAAAAAAAAHxQAAAAAAAAgBQAAAAAAAD8VAAAAAAAAQAYAAAAAAACABwAAAAAAAMAW
+AAAAAAABAAgAAAAAAAIACQAAAAAAAwAXAAAAAAAEAAoAAAAAAAgACwAAAAAADAAYAAAAAAAQAAwA
+AAAAABgAGgAAAAAAIAANAAAAAABAAA4AAAAAAIAADwAAAAAAwAAaAAAAAADgABsAAAAAAQAAEAAA
+AAABgAAcAAAAAAHAAB0AAAAAAeAAHgAAAAACAAARAAAA8tUSmPLVEpjy1RKY8tUSmPLVEpjy1RKY
+8tUSmPLVEpjy1RKY8tUSmPLVEpgDhRsYA4UbmPLVEpjy1RKY8tUSmPLVEpjy1RKY8tUSmPLVEpjy
+1RKYAAAAAAAAAAAAAAAA//8ABQAGAAcACAAJAAoACwAMAA0ADgAPABD///////8ACgAUAIwAggBk
+AFoAoACWAHgAbgBQAEYAPAAyACgAHgAKBM4EkgnOCZICsgRWBOIEpgO2BBoHsglWBl4CdgniCaYH
+TgI6CLYJGggWBiICxgRqAJYDegFyB3YAvgPeBPYEugRCA6ICYgc6Bg4AggPKBC4AbgBaAyoBNgHq
+Az4HxglqAUoHEgWWCHoGwgH+BnICigNSBeYFvgjeAZoH2gn2CboJfgeeCUIIogkGAV4HYgJOByYD
+FgEiBYIIZgWqCMoJLgiOAQ4FbgVaBUYG1ggqBjYCEgHCBuoIPgDmBoYC2gR+Ap4EBgZKAiYIAgNm
+AKoDjgX6AEYB1gb+Bq4F0gGGB4oI8gMCCFIA+gUyAa4A0gPyB+4AMgaaAu4FHgAeBQoAAAnECYgE
+2AScB6gJTAnYCZwIrAkQArwEYAFoB2wE7ASwAlgHMAPABCQDIAEsB7wJYAWMCHAGaAKABbQI1Ans
+CbAJOAiYB1gCRAEYBXgIwAkkBWQFUAggBiwG4Ag0AtAEdAZAAhwAoAOEAcwG9AF8B4AISADwAMgD
+6AaQAuQFAATEBIgCqARMA6wEEAZUAmwHRAIwCAwGGACMA3AAtAPUBDgDmAYEAHgAZABQAeADNAFA
+BwgGuAH0A0gF3AGQB9AJdAeUCPwBVAccAwwIXAWgCIQBBAU8BswCCAG4ANwGfAKUA/wH+ANcBfAA
+PAakBcgI6AL4BSgBpAfkACgFFAAUAAAEyQSNCckJjQKtBFEE3QShA7EEFQetCVEGWQJxCd0JoQdJ
+AjUIsQkVCBEGHQLBBGUAkQN1AW0HcQC5A9kE8QS1BD0DnQJdBzUGCQB9A8UEKQBpAFUDJQExAeUD
+OQfBCWUBRQcNBZEIdQa9AfkGbQKFA00F4QW5CNkBlQfVCfEJtQl5B5kJPQidCQEBWQddAkkHIQMR
+AR0FfQhhBaUIxQkpCIkBCQVpBVUFQQbRCCUGMQINAb0G5Qg5AOEGgQLVBHkCmQQBBkUCIQf9A2EA
+pQOJBfUAQQHRBvkGqQXNAYEHhQjtAv0ITQD1BS0BqQDNA+0H6QAtBpUC6QUZABkFBQAACb8JgwTT
+BJcHowlHCdMJlwinCQsCtwRbAWMHZwTnBKsCUwcrA7sEHwMbAScHtwlbBYcIawZjAnsFrwjPCecJ
+qwkzCJMHUwI/ARMFcwi7CR8FXwVLCBsGJwbbCC8CywRvBjsCFwCbA38BxwbvAXcHewhDAOsAwwPj
+BosC3wT7BL8EgwKjBEcDpwQLBk8CZwc/AisIBwYTAIcDawCvA88EMwOTBf8AcwBfAEsB2wMvATsH
+AwazAe8DQwXXAYsHywlvB48I9wFPBxcDBwhXBZsIfwD/BTcGxwIDAbMA1wZ3Ao8D9wfzA1cF6wA3
+Bp8FwwjjAvMFIwGfB98AIwUPAAAACgCMAG4AlgB4ADwAUACgAIIAZAAyAEYAWgAoAB4AFAAAAIcA
+aQCRAHMANwBLAJsAfQBfAC0AQQBVACMAGQAAIAKN5CAHKHQgAo3oIAcvoCACjfQgBy7IIAKOACAH
+LnggAo4UIActzCACjiwgByxYIAKOPCAHLDAgAo5EIAcsCCACjlggByvYIAKOZCAHK7AgAo5sIAcr
+iCACjoAgBytUIAKOjCAHMFwAAAAAAAAAACACjpQgByg0IAKOmCAHJ7ggAo6gIAcngCACjqggBydI
+IAKOtCAHJxAgAo64IAcm2CACjsQgByagIAKOyCAHJ/AgAo7QIAcmaCACjtggByYwIAKO4CAHJEAg
+Ao7oIAcl+CACjvQgByXAIAKO/CAHJYggAo8EIAclUCACjwwgByUYIAKPFCAHJOAgAo28IAciSCAC
+jxwgByIQIAKPJCAHIdggAo8sIAchoCACjzwgByFoIAKPRCAHITAgAo9MIAcg+CACj1QgByDQIAKP
+YCAHIKggAo9sIAcgeCACj4QgByBQIAKPoCAHICggAo+wIAcgACACj8AgBx/YIAKP0CAHH7AgAo/g
+IAcfiCACj/AgBx9gIAKQACAHHzggApAMIAcfECACkBggBx7oAAAAAAAAAAAgApAkIAcd0CACkCgg
+Bx2gIAKQMCAHHXAgApA8IAcdQCACkEAgBx0QIAKQRCAHHOAgApBIIAccuCACkFAgBxyQIAKNyCAH
+MqAgAo3QIAcxzCACjbQgBzEUIAKN3CAHMGQAABAhIEIwY0CEUKVgxnDngQiRKaFKsWvBjNGt4c7x
+7xIxAhAycyJSUrVClHL3YtaTOYMYs3ujWtO9w5zz/+PeJGI0QwQgFAFk5nTHRKRUhaVqtUuFKJUJ
+5e71z8Ws1Y02UyZyFhEGMHbXZvZWlUa0t1unepcZhzj33+f+153HvEjEWOVohninCEAYYSgCOCPJ
+zNnt6Y75r4lImWmpCrkrWvVK1Hq3apYacQpQOjMqEtv9y9z7v+uem3mLWLs7qxpspnyHTORcxSwi
+PAMMYBxB7a79j83s3c2tKr0LjWidSX6XbrZe1U70PhMuMh5RDnD/n+++393P/L8brzqfWY94kYiB
+qbHKoevRDMEt8U7hbxCAAKEwwiDjUARAJXBGYGeDuZOYo/uz2sM90xzjf/NeArESkCLzMtJCNVIU
+YndyVrXqpcuVqIWJ9W7lT9UsxQ004iTDFKAEgXRmZEdUJEQFp9u3+oeZl7jnX/d+xx3XPCbTNvIG
+kRawZld2dkYVVjTZTMlt+Q7pL5nIiem5iqmrWERIZXgGaCcYwAjhOIIoo8t921zrP/sei/mb2Ku7
+u5pKdVpUajd6FgrxGtAqszqS/S7tD91szU29qq2LneiNyXwmbAdcZExFPKIsgxzgDMHvH/8+z13f
+fK+bv7qP2Z/4bhd+Nk5VXnQukz6yDtEe8AAAMAQAADAQAABZCAAAWaQAAZQwAAAQJAAAEDAAABA8
+AAAQ3AAAeywAAHs0AAHijAAB5owAAeqMAAHujAAB8owAAfaMAAH6jAAB/owAAI3MAAB+dAAAj9wA
+AZFYAACP/AAAdRwAAHUYAAB1EAAAd+AAAHfkAAB39AABnDwAARB0AAEQhAAAlAgAAJhcAACWDAAA
+lhQAAJYgAACWLAAA0CgAAJAIAAIQ3AACMNwAAlDcAAJw3AABkFQAAGAcAAGg2AABkJAAAHfgAAB3
+5AAAd/QAAHl4AAB5+AABlAQAAePAAAHnwAAB68AAAe/AAAHzwAAB98AAAfvAAAH/wAABlAwAADAA
+P///wAAAMAg////AAABZEKyAAAAAAFmsfiUAAAABlBAf////AAGUNAAAABEAAZQIH////wABlDgA
+AAARAAAQKH////8AABAsf////wAAEDR/////AAAQOH////8AABBAD0QAAAAAeygAH//iAAB7MD//
+7v8AAHs4AAAAAQAAe0A//+7/AAB8JAAf/+IAAeKIAAgAAAAB5ogACAAAAAHqiAAIAAAAAe6IAAgA
+AAAB8ogACAAAAAH2iAAIAAAAAfqIAAgAAAAB/ogACAAAAACNyP////8AAI3Q/////wAAfnB/v///
+AAB+eH+///8AAH4AAMAAAAABkVQB////AAGRnAH///8AAI/YAH///QAAj/j////9AAB1FAAAAAcA
+AHUMAAAACwAAd9wAAAADAAB38P////8AAZw4AAGAQAABnPgAAAADAACQBAAAAD4AARB4AP///wAB
+EHwA////AAEQiAAAAAAAAJQEAAD//wAAlBAAAP//AACYVAAAAf8AAJhYAAAB/wAAlggAAAAAAACW
+EAAf//8AAJYYAB///wAAlhwAD///AACWJAAP//8AAJYoAP///wAAljAA////AADQIAAAAAcAANAk
+AAAABwACENgAAAADAAIw2AAAAAMAAlDYAAAAAwACcNgAAAADAAGQUAAAAC8AAaDUAAABgwABkIwA
+OAAAAAGQmAA4AAAAAHl0AAAALwAAeXAAAAAFAAB59AAAAC8AAHnwAAAABeECDgAAAEAAAAD/8AAA
+MNThAF4AAAgACAAA5AAAAMQwAADEMR//mNzhAGIA4QByAAAAYkgABAAphhAAAAAAYkCAAAAAgBQA
+A4AWAAOAEgBDgBHSA4AQAAUAAHUEH/+aNB//koAf/5qgIAjt4B//maAAAEEeAADwAwAAQQ4AAEBA
+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
+o8zpzBECIAUAAOyZCAMMgQAAaGIiaGQp8p/7y6IAnQBj/54ZyQGpOSmQfWP/RKWa90AGHe//kgCl
+m/dgBB3v/2oApZz3gAYV7/9CAKWdJ9QA0Q+lniflANEPAAAAAAAAIAKNXAzAAAIgBe0cIAKNYAjA
+AAggBe0cIAKNZCDAAAwgBeykIAKNaAbAADQgBe54IAKNbAjAADggBe0cIAKNcALAADwgBe54IAKN
+dAjAAEAgBe0cIAKNeAiAAEQgBe2kIAKNfBiAAFggBe2kIAKNgBiAAGggBe2kIAKNhBiAAHggBe2k
+IAKNiBiAAIggBe2kIAKNjBiAANggBe2kIAKNkBiAAOQgBe2kIAKNlBiAAPAgBe2kIAKNmBiAAPwg
+Be2kIAKNnAiAATggBe2kcFsldV0gY2hhbmdpbmcgc3BlZWQgZnJvbSAlI3ggdG8gJSN4CgAAAAAA
+AAAAAAAAcFsldV0gYmNtODQ4MzQgbGluayBzdGF0dXMgY2hhbmdlIFVQCgAAAAAAAAAAAAAAcFsl
+dV0gYmNtODQ4MzQgbGluayBzdGF0dXMgY2hhbmdlIERPV04KAAAAAAAAAAAAaHdfY2w0NV91cGRf
+c3BkX2FkdiAlI3gKAAAAAAAAAABwWyV1XSBjaGFuZ2luZyBzcGVlZCBmcm9tICUjeCB0byAlI3gK
+AAAAAAAAAAAAAABwWyV1XSBjYWxpYnJhdGluZyBQSFkgU0VSREVTIGZvciBuZXcgc3BlZWQKAAAA
+AABwWyV1XSBhcTEyMDIgbGluayBzdGF0dXMgY2hhbmdlIFVQCgAAAAAAAAAAAAAAAABwWyV1XSBQ
+SFkgT1ZFUkhFQVRFRCAtIGZvcmNlZCBwb3dlciBkb3duICh0ZW1wPSVkKQoAAAAAAAAAAAAAAAAA
+cFsldV0gYXExMjAyIGxpbmsgc3RhdHVzIGNoYW5nZSBET1dOCgAAAAAAAAAAAAAAaW5vZGVfZmMt
+PmZsb3djX2ZsYWdzIFsweCV4XSwgaW5vZGVfZmMtPmZsb3djX2J1Zi0+Zmlmby5udW1fYnl0ZXMg
+WyV1XQoAAAAAAAAAAABhdXRoZW50aWNhdGVfdGFyZ2V0OiBLRVlfQ0hBUF9SRVNQIC0gWzB4JXgl
+eCV4JXgleCV4JXgleF0KAAAAAAAAYXV0aGVudGljYXRlX3RhcmdldDogS0VZX0NIQVBfUkVTUCAt
+IFsweCV4JXgleCV4JXgleCV4JXhdCgAAAAAAAGF1dGhlbnRpY2F0ZV90YXJnZXQ6IEluY29ycmVj
+dCBwYXNzd29yZAoAAAAAAAAAAHZhbGlkYXRlX2tleXZhbDogS2V5IENIQVBfQSwgdmFsIFslZF0K
+AAAAAAAAAAAAAHZhbGlkYXRlX2tleXZhbDogS2V5IENIQVBfSSwgdmFsIFslZF0KAAAAAAAAAAAA
+AENIQVBfQzogZGlnZXN0IGV4cGFuc2lvbiBlcnJvcgoAdmFsaWRhdGVfa2V5dmFsOiBLRVlfQ0hB
+UF9OQU1FLCBsZW4gWzB4JXhdCgAAAAAAQ0hBUF9OOiBUYXJnZXQgdXNlcmlkIG1pc21hdGNoCgBD
+SEFQX1I6IGRpZ2VzdCBleHBhbnNpb24gZXJyb3IKAFJlY2lldmVkIEF1dGhNZXRob2QgdmFsdWUK
+AAAAAAAAdmFsaWRhdGVfa2V5dmFsOiBmbG93Y19mb2lzY3NpX2Nvbm5fZmxhZ3MgWzB4JXhdCgAA
+AAAAAAAAAAAAAAAAAHZhbGlkYXRlX2tleXZhbDogZmxvd2NfZm9pc2NzaV9jb25uX2ZsYWdzIFsw
+eCV4XQoAAAAAAAAAAAAAAAAAAABOZWdvIEZCTCBbJXVdCgAAS0VZX01BWFJDVkRTTCBbJXVdCgAA
+AAAAAAAAAAAAAABEdXBsaWNhdGUgaXNjc2kga2V5CgAAAAAAAAAAAAAAAGZvaXNjc2lfcGFyc2Vf
+cmN2ZF9wYXJhbXM6IGVycm9yCgAAAAAAAAAAAAAAAAAAAGZvaXNjc2lfcGFyc2VfcmN2ZF9wYXJh
+bXM6IHJsZW4gJWQKAAAAAAAAAAAAAAAAAGFzeW5jX3BkdTogbG9nb3V0IHJlcXVlc3RlZCBibG9j
+a2luZyBzZXNzaW9uCgAAAGFzeW5jX3BkdTogc2Vzcy9jb25uIGRyb3AgcmVxdWVzdGVkIGJsb2Nr
+aW5nIHNlc3Npb24KAAAAAAAAAAAAAABsb2dpbl9yZXNwOiBmbG93Y19mb2lzY3NpX2Nvbm5fZmxh
+Z3MgWzB4JXhdCgAAAABsb2dpbl9yZXNwOiBmbG93Y19mb2lzY3NpX2Nvbm5fZmxhZ3MgWzB4JXhd
+LCByYyBbMHgleF0KAAAAAAAAAAAAaVNDU0kgU2VjLXBhcmFtcyByZWNlaXZlZGhhdmUgZXJyb3Jz
+ISEKAAAAAAAAAAAAVGFyZ2V0IG1vdmVkIHRlbXAuIGNvbm4gJXgsIHNlc3MgJXgKAAAAAAAAAAAA
+AAAATG9naW4gRmFpbGVkISEuIGNvbm5fZmMgWzB4JXhdLCBzZXNzX2ZjIFsweCV4XSwgc3RhdHVz
+X2NsYXNzIFsweCV4XQoAAAAAAAAAAAAAAABsb2dpbl9yZXNwOiBzZXNzX2ZjLT5mbG93Y19pZCBb
+JXVdLCBjb25uX2ZjLT5mbG93Y19pZCBbJXVdIGxvZ2dlZCBpbgoAAAAAAAAAAAAAAFByb3RvY29s
+IEVycm9yIGNiaXQgJWQgdGJpdCAlZCBjc2cgJWQgbnNnICVkCgAAAGNvbm4gdGlkIFsweCV4XSwg
+cnhfZGF0YS0+c2VxIFsweCV4XSwgcnhfZGF0YS0+bGVuIFsweCV4XSwgcnhfZGF0YS0+c3RhdHVz
+IFsweCV4XQoAAAAAAAAAAAAAAAAAAGNza19mYy0+Zmxvd2NfY3NvY2tfb2Zmc2V0IFsweCV4XSwg
+ZGxlbjE2IFsweCV4XQoAAAAAAAAAAAAAAAAAAABhY3RfZXN0OiBmbG93Y19mb2lzY3NpX2Nvbm5f
+ZmxhZ3MgWzB4JXhdCgAAAAAAAABhY3RfZXN0YWI6IHRjYl9mYy0+Zmxvd2NfYnVmIFsweCV4XSwg
+dGNiX2ZjLT5mbG93Y190eXBlIFsweCV4XSB0Y2JfZmMtPmZsb3djX3N0YXRlIFsweCV4XSwgbnBh
+Z2VzIFsweCV4XSwgZmxvd2NfdHBfc25kX21heCBbMHgleF0KAAAAAAAAAAAAAAAAAABhY3RfZXN0
+YWI6IGF0aWQgWzB4JXhdLCB0aWQgWzB4JXhdLCBvcCBbMHgleF0sIHJjdl9pc24gWzB4JXhdLCBz
+bmRfaXNuIFsweCV4XSwgY3NvY2stPmZsb3djX3N0YXRlIFsweCV4XSwgdGNwX29wdCBbMHgleF0s
+IHRjYl9mYy0+Zmxvd2NfaWQgWzB4JXhdIAoAAAAAAAAAAAAAAAAAY3NrX2ZjLT5mbG93Y19jc29j
+a19jb29raWUgWzB4JXhdIAoAAAAAAAAAAAAAAAAAbDNpbjRfZGV2X2NvbmZpZzogd3ItPnBhcmFt
+LnZsYW5pZCBbJXVdLCBsMmRldl9mYy0+Zmxvd2NfbmV0X2wyZGV2X3ZsYW5kZXYgWzB4JXhdCgAA
+AAAAAAAAAAAAAAAAbmV0X2wzaW40X2Rldl9jb25maWc6IGwyZGV2X2ZjLT5mbG93Y19pZCBbMHgl
+eF0sIGFkZHJlc3MgYWxyZWFkeSB1c2VkIGJ5IHBvcnQgJWQKAAAAAAAAAAAAAAAAAAAAbmV0X2wz
+aW40X2Rldl9jb25maWc6ICBhZGRyIFsweCV4XSwgbWFzayBbMHgleF0sIGd3IFsweCV4XSwgcmVm
+X2NudCBbMHgleF0gaW4gdXNlCgAAAAAAAAAAAAAAAAAAd3JoX2NobmV0X2lmY29uZjogbDJkZXZf
+ZmMtPmZsb3djX2lkIFsweCV4XSwgbDJkZXZfZmMtPmZsb3djX3R5cGUgWyUweF0sIGlmY29uZl93
+ci0+c3Vib3AgWzB4JXhdCgAAAAAAAAAAAAAAAAAAAHdyaF9jaG5ldF9pZmNvbmY6IGwyZGV2X2Zj
+LT5mbG93Y19pZCBbMHgleF0sIHVua25vd24gc3Vib3AgWzB4JXhdCgAAAAAAAAAAAAAAAAAAd3Jo
+X2NobmV0X2lmY29uZjogbDJkZXZfZmMtPmZsb3djX2lkIFsweCV4XSwgcmMgJWQKAAAAAAAAAAAA
+AAAAAG5ldGlmX2lwX2NvbmZsaWN0X3RpbWVyX2NiOiBsMmRldl9mYy0+Zmxvd2NfaWQgWzB4JXhd
+LCBpbmRldmN0eHQtPnN0YXRlIFslZF0sIGluZGV2Y3R4dC0+cmV0cnlfY250IFslZF0KAAAAAAAA
+AABuZXRpZl9pcF9jb25mbGljdF90aW1lcl9jYjogbDJkZXZfZmMtPmZsb3djX2lkIFsweCV4XSwg
+aW5kZXZjdHh0IFsweCV4XSwgaW4gZnJlZSBzdGF0ZQoAAAAAAAAAAABjbWRoX2NobmV0X2lmYWNl
+OiBmYyBbMHgleF0sIGZjLT5mbG93Y19pZCBbMHgleF0sIGZjLT5mbG93Y190eXBlIFsweCV4XSwg
+cCBbMHgleF0sIGxlbjE2IFsldV0sIGxvYyBbMHgleF0KAAAAAAAAY21kaF9jaG5ldF9pZmFjZTps
+MmRldl9mYyBbMHgleF0sIGwyZGV2X2ZjLT5mbG93Y19pZCBbMHgleF0sIGwyZGV2LT5mbG93Y190
+eXBlIFsldV0sIGwyZGV2X2ZjLT5mbG93Y19uZXRfbDJkZXZfZmxhZ3MgWyUweF0KAAAAAABjbWRo
+X2NobmV0X2lmYWNlOiBsMmRldl9mYy0+Zmxvd2NfbmV0X2wyZGV2X2ZsYWdzIGNoYW5nZWQgZnJv
+bSBbJTB4XSB0byBbJTB4XSwgcmMgWyVkXQoAAAAAAAAAAABjaG5ldF9sMmRldl91cF9tYl9jYjog
+cmMgWyVkXSwgcG9ydCBbJXVdLCBzdGF0ZSBbJXVdLCBjb29raWUgWzB4JXhdCgAAAAAAAAAAAAAA
+AGNobmV0X3F1ZXVlX3htaXQ6IGZjLT5mbG93Y19pZCBbMHgleF0sIGJ1Zl9sZW4gWzB4JXhdLCBi
+dWZmZXJlZCBbMHgleF0sIGZpZm8ubnVtX2J5dGVzIFslMHhdCgAAAGRoY3BfcHJvY2Vzc19jYjog
+bDJkZXZfZmMtPmZsb3djX2lkIFsweCV4XSwgZGhjdHh0LT5zdGF0ZSBbJTB4XSwgZGhjdHh0LT5y
+dHJ5X2NudCBbJXVdCgAAAAAAAAAAAGRoY3BfdGltZXJfY2I6IERIQ1BESVNDT1ZFUiBzZW50LCBi
+dXQgbm8gcmVwbHkgZnJvbSBhbnkgcG9zc2libGUgc2VydmVyIG9uIHRoZSBuZXR3b3JrLiBSZXRy
+eWluZyBhZ2FpbgoAAAAAAAAAAABkaGNwX3RpbWVyX2NiOiBsMmRldl9mYy0+Zmxvd2NfaWQgWzB4
+JXhdLCBzZW5kaW5nIERIQ1BESVNDT1ZFUiBmb3IgZGhjdHh0IFsweCV4XSBvbiBwaWQgWyVkXQoA
+AABkaGNwX3RpbWVyX2NiOiBsMmRldl9mYy0+Zmxvd2NfaWQgWzB4JXhdLCBESENQT0ZGRVIgcmVj
+ZWl2ZWQgZm9yIGRoY3R4dCBbJXhdIHBpZCBbJWRdCgAAAAAAAAAAAABkaGNwX3RpbWVyX2NiOiBs
+MmRldl9mYy0+Zmxvd2NfaWQgWzB4JXhdLCAgREhDUEFDSyByZWNlaXZlZCBmb3IgZGhjdHh0IFsl
+eF0sIHBpZCBbJWRdCgAAAAAAAAAAAABkaGNwX3RpbWVyX2NiOiBsMmRldl9mYy0+Zmxvd2NfaWQg
+WzB4JXhdLCBkaGN0eHQtPmlwYWRkciBbMHgleF0KAAAAAAAAAAAAAAAAAAAAAGRoY3BfdGltZXJf
+Y2I6IGwyZGV2X2ZjLT5mbG93Y19pZCBbMHgleF0sIG5vIHJlcGx5IGZyb20gZGhjcCBzZXJ2ZXIs
+IHRpbWluZyBvdXQKAAAAAAAAAAAAAAAAAAAAAGxvZ2luX3BhcmFtczogY29ubl9mYy0+Zmxvd2Nf
+Zm9pc2NzaV9jb25uX2ZsYWdzIFsweCV4XQoAAAAAAAAAAABsb2dpbl9wYXJhbXM6IGNvbm5fZmMt
+PmZsb3djX2ZvaXNjc2lfY29ubl9mbGFncyBbMHgleF0KAAAAAAAAAAAAYXV0aF9uZWdvX3NlY3Vy
+aXR5OiBzZW5kX2ZsYWcgWzB4JXhdLCBhdXRoX3BvbGljeSBbMHgleF0KAAAAAAAAAGF1dGhfbmVn
+b19zZWN1cml0eTogS0VZX0NIQVBfUkVTUCAtIGhhc2hbMHgleCV4JXgleCV4JXgleCV4XQoAAABh
+dXRoX25lZ29fc2VjdXJpdHk6IEtFWV9DSEFQX1JFU1AgLSBoYXNoWzB4JXgleCV4JXgleCV4JXgl
+eF0KAAAAYXV0aF9uZWdvX3NlY3VyaXR5OiBLRVlfQ0hBUF9SRVNQIC0gZXJyb3IgZW5jb2Rpbmcg
+dG8gaGV4CgAAAAAAAGF1dGhfbmVnb19zZWN1cml0eTogS0VZX0NIQVBfUkVTUCAtIGVsZW4gWzB4
+JXhdCgAAAAAAAAAAAAAAAAAAAABhdXRoX25lZ29fc2VjdXJpdHk6IEtFWV9DSEFQX0NIQUwgLSBl
+cnJvciBlbmNvZGluZyB0byBoZXgKAAAAAAAAYXV0aF9uZWdvX3NlY3VyaXR5OiBLRVlfQ0hBUF9D
+SEFMIC0gZWxlbiBbMHgleF0KAAAAAAAAAAAAAAAAAAAAAHNlbmRfbG9naW5fcmVxdWVzdDpjb25u
+X2ZjLT5mbG93Y19pZCBbMHgleF0sIHNlc3NfZmMtPmZsb3djX2lkIFsweCV4XSwgY3RybF94bWl0
+IFsweCV4XSwgc2Vzc19mYy0+Zmxvd2NfZm9pc2NzaV9zZXNzX3R5cGUgWzB4JXhdCgAAAAAAAAAA
+AAAAAAAAAHNlbmRfbG9naW5fcmVxdWVzdDogZmxhZyBbMHgleF0uCgAAAAAAAAAAAAAAAAAAAHNl
+bmRfbG9naW5fcmVxdWVzdDogdHhfbGVuIFsldV0KAAAAAAAAAAAAAAAAAAAAAHhtaXRfc2VuZHRh
+cmdldHM6IGNvbm4gdGlkIFsweCV4XSwgY3RybF94bWl0LT50dHQgWzB4JXhdLCBkYXRhX2xlbiBb
+JXVdLCBwYWRfbGVuIFsldV0KAAAAAAAAAAAAAHBpbmdfdGFyZ2V0OiBwaW5nIHRpbWVvdXQgYmxv
+Y2tpbmcgc2Vzc2lvbgoAAAAAAHNlbmRfbm9wX291dDogY29ubiBmbG93X2lkIFsweCV4XSwgcmVx
+IFsldV0sIHR0dCBbMHgleF0KAAAAAAAAAABjc29ja19mYWlsZWQ6IGNza19mYy0+Zmxvd2NfaWQg
+WzB4JXhdLCBjc2tfZmMtPmZsb3djX3N0YXRlIFsweCV4XSwgc2Vzc19mYy0+Zmxvd2NfaWQgWzB4
+JXhdLCBzZXNzX2ZjLT5mbG93Y19zdGF0ZSBbMHgleF0sIGV2dCBbMHgleF0KAAAAAAAAAAAAAABj
+b3NrY19mYWlsZWQ6IHNlc3Npb24gYmxvY2ssIGV2ZW50IFsweCV4XQoAAAAAAABjb3NrY19mYWls
+ZWQ6IHNlc3Npb24gYmxvY2ssIGV2ZW50IFsweCV4XQoAAAAAAABjaG5ldF9maW5kX2wydF9lbnRy
+eTogZGFkZHIgWyUwOHhdLCBbMHglMDh4XSwgbG9jYWwgbmV0d29yayBbJWRdCgAAAAAAAAAAAAAA
+AAAAAGwydGVudCBbJTB4XSwgbDJ0ZW50LT5pZHggWyVkXQoAcmMgWyVkXSwgY3NrX2ZjIFsweCV4
+XSwgY3NrX2ZjLT5mbG93Y19pZCBbMHgleF0KAAAAAAAAAAAAAAAAAAAAAGNzb2NrX2ZyZWU6IHNp
+emVvZihjc2tfZmMtPnUuY3NvY2spIFsldV0sIGJ5dGVzCgAAAAAAAAAAAAAAAAAAAABUQ1AgY29u
+biBlc3RhYmxpc2htZW50IGZhaWxlZCAlZAoAAAAAAAAAAAAAAAAAAABJbnZhbGlkIG9wY29kZSAw
+eCV4IGluIGN0cmwgcGF0aAoAAAAAAAAAAAAAAAAAAABERFAgZXJyb3IgWzB4JXhdLCBhYm9ydGlu
+ZyBjb25ubiBbMHgleF0KAAAAAAAAAABzZW5kX2Fib3J0X3JlcTogY3NrX2ZjLT5mbG93Y190eXBl
+IFsweCV4XSwgY3NrX2ZjLT5mbG93Y19pZCBbMHgleF0sIHRpZCBbMHgleF0sIHVscHR4Y2ggWyV1
+XSwgYnVmZmVyZWQgWyV1XQoAAAAAaXNjc2lfaGRyOiBiYWQgdGNiIHN0YXRlIHRvIHJ4IGRhdGEs
+IHRjYl9mYy0+Zmxvd2NfaWQgWzB4JXhdLCB0Y2JfZmMtPmZsb3djX3N0YXRlIFsweCV4XQoAAAAA
+AAAAZHJvcF9hbGxfdGFzazogcmVxdWVzdGVkIGxpZHggWyV1XQoAAAAAAAAAAAAAAAAAZ2V0X3Jl
+Zl90YXNrOiBpc3Rhc2stPmZsb3djX2lkIFsweCV4XSwgaXN0YXNrX2ZjLT5mbG93Y19zdGF0ZSBb
+JXVdLCBpc3Rhc2tfZmMtPmZsb3djX2ZvaXNjc2lfdGFza19saWR4IFsldV0sIGxpZHggWyV1XQoA
+AAAAAAAAAABwcm9jZXNzX3RtZl9yZXNwb25zZTogaXN0YXNrX2ZjLT5mbG93Y19idWYtPnNjaGVk
+X25vZGUubmV4dCBbMHgleF0sIGlzdGFza19mYyBbMHgleF0sIGlzdGFza19mYy0+Zmxvd2NfaWQg
+WyV1XQoAcHJvY2Vzc190bWZfcmVzcG9uc2U6IHVscHR4cGxlbjE2IFsldV0KAAAAAAAAAAAAcHJv
+Y2Vzc190bWZfcmVzcG9uc2U6IGRlbGF5X3Byb2Nlc3NpbmcsIG9wIFsleF0KAAAAAAAAAAAAAAAA
+AAAAAHByb2Nlc3NfdG1mX3Jlc3BvbnNlOjogd3Igb3Bjb2RlIFsweCV4XQoAAAAAAAAAAHByb2Nl
+c3NfdG1mX3Jlc3BvbnNlOjogdGFzayBmdW5jdGlvbiBbMHgleF0KAAAAAGNhbGxpbmcgZHJvcF90
+YXNrLCBpc3Rhc2tfZmMtPmZsb3djX2ZvaXNjc2lfdGFza19saWR4IDB4JXgKAAAAAAB3cmhfZm9p
+c2NzaV9ub2RlOiBub2RlX3dyLT5mbG93aWRfbGVuMTYgMiBbJXhdCgB3cmhfZm9pc2NzaV9jaGFw
+OiBpZF9sZW4gWyV4XSwgc2VjX2xlbiBbJXhdCgAAAAB3cmhfZm9pc2NzaV9jaGFwOiB0Z3RfaWRf
+bGVuIFsleF0sIHRndF9zZWNfbGVuIFsleF0KAAAAAAAAAAAAAAAAcGVlcl9jb246IGNza19mYyA9
+PiBmbG93aWQgWzB4JXhdLCBmbG93Y19idWYgWzB4JXhdCgAAAAAAAAAAAAAAAGZvaXNjc2lfY3Ry
+bDogc3Vib3AgWzB4JXhdLCBzZXNzX3R5cGVfdG9fZXJsIFsweCV4XSwgc2Vzc190eXBlIFsweCV4
+XQoAAAAAAAAAAAAAd2F0Y2hkb2cgY21kIGhhbmRsZXIgKHRpbWUgJXUgYWN0aW9uICV1KQoAAAAA
+AAAAV0FUQ0hET0c6IGRldmljZSBzaHV0ZG93bgoAAAAAAABXQVRDSERPRzogYnlwYXNzIHRpbWVv
+dXQKAAAAAAAAAFdBVENIRE9HOiBGTFIgLSBub3QgaW1wbGVtZW50ZWQgeWV0CgAAAAAAAAAAAAAA
+AGZpbHRlcjogcG9yZ3JhbW1pbmcgdGlkICV1IChsZSB0Y2FtIGluZGV4ICV1KS4uLgoAAAAAAAAA
+AAAAAAAAAABmaWx0ZXI6IHJlcXVlc3RpbmcgY29tcGxldGlvbi4uLgoAAAAAAAAAAAAAAAAAAABG
+Q09FIEZyZWU6IHN0aWxsIHlpZWxkZWQgd2hlbiBmcmVlaW5nLi4uZmxvd2NfaWQgJXggZmxvd2Nf
+ZmxhZ3MgJXggCgAAAAAAAAAAAAAAAHBvcnQgJWQgcHJpb3IgJWQgc2VsZWN0ICVkIHByb3RvY29s
+SUQgMHglMDR4CgAAAHBvcnQgJWQgc2V0IHBmY19lbiA9IDB4JXgKAAAAAAAAcG9ydCAlZCBzZXQg
+cGdpZF8wXzcgPSAweCUwOHggcGdfcGVyY2VudGFnZSAweCUwOHhfJTA4eCBudW1fdGNzX3N1cHBv
+cnRlZCAlZAoAAABwb3J0ICVkIHNldCBwZmNfZW4gPSAweCV4CgAAAAAAAEZDb0UgRERQIGZhaWxl
+ZCA6IG94X2lkIDB4JXggcnhfaWQgMHgleAoAAAAAAAAAAEZDb0UgRERQIGZhaWxlZCA6IERkcFJl
+cG9ydCAweCV4IERkcFZhbGlkIDB4JXgKAFBSTEkgUnNwIHRpbWVkb3V0IDogZmxvd2NfaWQgMHgl
+eCBveF9pZCAweCV4IHJ4X2lkIDB4JXggCgAAAAAAAABjYW5ub3QgYWxsb2NhdGUgb2ZmbG9hZGVk
+IGZpbHRlciBjb25uZWN0aW9uCgAAAABjYW5ub3QgYWxsb2NhdGUgb2ZmbG9hZGVkIGZpbHRlciBJ
+UHY2IGNvbm5lY3Rpb24KAAAAAAAAAAAAAAAAAAAAdW9bJXVdOiB3ciAlcCB3ci0+bGVuZ3RoICV1
+IG1zcyAldQoAAAAAAAAAAAAAAAAAdW9bJXVdOiBmIDB4JTA4eCBkc3RfY3BsIDB4JTA4eCBzcmNf
+Y3BsIDB4JTA4eCBpbW1kbGVuMTYgJXUKAAAAAHVvWyV1XTogbGFzdCBtc3MgJXUKAAAAAAAAAAAA
+AAAAdW9bJXVdOiAhbGFzdCB3ci0+c2NoZWRwa3RzaXplICV1CgAAAAAAAAAAAAAAAAAAaW52YWxp
+ZCBidWZmZXIgZ3JvdXBbJXVdIGNvbmZpZ3VyYXRpb246IG10dSAldSBsd20gJXUgaHdtICV1IGR3
+bSAldQoAAAAAAAAAAAAAAABmYyAldSB2ZiAldSBnb3QgaXZmPTB4JXgscmFuZ2U6ICUjeC0lI3gg
+KCV1LyV1IHVzZWQpCgAAAAAAAAAAAAAAVkkgJXUgY2Fubm90IGdldCBSU1Mgc2xpY2U6IE5vIG1v
+cmUgc2xpY2VzIGF2YWlsYWJsZSAodXNlZCAldS8ldSkKAAAAAAAAAAAAAAAAAABwZm4gJXUgdmZu
+ICV1IHdpdGggcG9ydCBtYXNrIDB4JXggY2Fubm90IGFjY2VzcyBwb3J0ICV1LCByZXQgJWQKAAAA
+AAAAAAAAAAAAAAAAAHBmbiAldSB2Zm4gJXUgY291bGQgbm90IGFsbG9jYXRlIHZpaWQsIHJldCAl
+ZAoAAHBmbiAldSB2Zm4gJXUgY291bGQgbWFwIHZpaWQgIDB4JXggdG8gZmxvd2MsIHJldCAlZAoA
+AAAAAAAAAAAAAABwZm4gJXUgdmZuICV1IGNvdWxkIG5vdCBhbGxvY2F0ZSB1d2lyZSBmdW5jICVk
+IG1hYyBhZGRyLCByZXQgJWQKAAAAAAAAAAAAAAAAAAAAAG1paV9mb3JjZV9zcGVlZFsldV06IHJj
+YXBzIDB4JXgKAAAAAAAAAAAAAAAAAAAAAG1paV9saW5rX3N0YXR1c1sldV06IGJtc3IgMHglMDh4
+IHN0YXR1cyAldSB4Z21fbHMgMHglMDh4CgAAAAAAAABwb3J0X2NtZF9oYW5kbGVyOiB1bmtub3du
+IHUuZGNiLnR5cGUgMHgleAoAAAAAAABwb3J0WyV1OjB4JTAyeDoweCUwMnhdOiBwaHkgcmVzZXQg
+bm90IGF2YWlsYWJsZQoAAAAAAAAAAAAAAAAAAAAAcG9ydFsldToweCUwMng6MHglMDJ4XTogdW5r
+bm93biBhY3Rpb24gMHgleAoAAAAAcG9ydFsldToweCUwMng6MHglMDJ4XTogdW5rbm93biByZWFk
+IGFjdGlvbiAweCV4CgAAAAAAAAAAAAAAAAAAAGNwbF9lcnJfbm90aWZ5OiB0aWQgJXUgY3BsIDB4
+JTA4eCUwOHgKAAAAAAAAAAAAAGNwbF9lcnJfbm90aWZ5OiB0aWQgJXUgY3BsIDB4JTA4eCUwOHgg
+MHglMDh4JTA4eAoAAAAAAAAAAAAAAAAAAABjcGxfZXJyX25vdGlmeTogdGlkICV1IGxlbiAldQoA
+AEZDT0UgRnJlZTogc3RpbGwgeWllbGRlZCB3aGVuIGZyZWVpbmcuLi5mbG93Y19pZCAleCBmbG93
+Y19mbGFncyAleCAKAAAAAAAAAAAAAAAARkNPRSBCUCBXUiBFUlI6IFdSIHdpdGggY29va2llICV4
+JXggZXJyb3JlZCBiYWNrIAoAAAAAAAAAAAAAAAAAAHNjc2lfYWJvcnQ6IEVudGVyaW5nIEFib3J0
+X3Rhc2ssIGJ1ZmZlcmVkIFsldV0KAHNjc2lfYWJvcnQ6OiB3ci0+aXFpZCBbMHgleF0sIGlzdGFz
+a19mYy0+Zmxvd2Nfc2dlX2lxaWQgWzB4JXhdCgBzY3NpX2Fib3J0OjogbHVuX2lkeCBbMHgleF0K
+AAAAAHNjc2lfYWJvcnQ6IHJlZiB0YXNrIG5vdCBvdXRzdGFuZGluZwoAAAAAAAAAAAAAAHNjc2lf
+YWJvcnQ6IHNyZXEtPm9wY29kZSBbMHgleF0gZmxhZ3MgWzB4JXhdCgAAAHNjc2lfYWJvcnQ6IHRh
+c2tfaWR4IFsldV0sIGl0dCBbMHgleF0KAAAAAAAAAAAAAGFib3J0L2Nsb3NlIFdSIHdpdGggY29v
+a2llIDB4JWx4IHdhcyBpc3N1ZWQgb24gc3NuIDB4JXggaW4gd3Jvbmcgc3RhdGUgMHgleAoAAAAA
+YWJvcnQgV1Igb24gc3NuIDB4JXggZGlkIG5vdCBmaW5kIFdSIHdpdGggY29va2llIDB4JXgleAoA
+AAAAAAAAAGNsb3NlIFdSIHdpdGggY29va2llIDB4JWx4IG9uIHNzbiAweCV4O2RpZCBub3QgZmlu
+ZCBXUiB3aXRoIGNvb2tpZSAweCVseAoAAAAAAAAAYWJvcnQgV1Igb24gc3NuIDB4JXggd2FzIGlz
+c3VlZCBvbiB4Y2hnIDB4JXggd2l0aCByeF9pZCAweCV4IGluIHdyb25nIHN0YXRlIDB4JXgKAAAA
+AAAAAAAAAAAAAAAAcG9ydCAlZCBhcHBsSUQgMHglMDR4IG91aSAweCUwNnggdXNlcl9wcmlvciAl
+ZAoAc2VuZCBkY2J4IGluZm86Zmxvd2NpZCAldQoAAAAAAABzZW5kIGRjYnggY29tcGxldGUKAAAA
+AAAAAAAAAAAAAERDQlggcG9ydCAldSBhbGxfc3luY2QgPSAldQoAAAAAY2huZXRfbDJ0X3VwZGF0
+ZTogbDJkZXZfZmMgWzB4JXhdLCBsMmRldl9mYy0+Zmxvd2NfaWQgWyV1XSBsMmRldl9mYy0+Zmxv
+d2NfZmxhZ3MgWzB4JXhdCgAAAAAAAAAAY2huZXRfbDJ0X3VwZGF0ZTogbDJkZXZfZmMtPmZsb3dj
+X2lkIFsldV0gYWxyZWFkeSBzY2hlZHVsZWQKAAAAAGNobmV0X2wydF91cGRhdGU6IGluIGRlbGF5
+ZWRfcHJvY2Vzc2luZywgbDJ0ZW50IFslMDh4XQoAAAAAAAAAAABjaG5ldF9sMnRfdXBkYXRlOiBs
+MnRfdXBkYXRlIHJlcXVlc3Qgc2VudCBsMnRlbnQgWyUwOHhdLCBsMnRlbnQtPmlkeCBbJWRdLCBs
+MnRlbnQtPnZsYW4gWyVkXQoAAABuZXRpZl9wcm9jZXNzX2RoY3A6IGwyZGV2X2ZjLT5mbG93Y19p
+ZCBbMHgleF0sIHByb2Nlc3NpbmcsIG9wdF9sZW4gJXUKAAAAAAAAAAAAAGNobmV0X2RoY3BfcmVj
+djogdmxhbmlkIFsldV0sIGwyZGV2X3BpZF9mYy0+Zmxvd2NfbmV0X2wyZGV2X3ZsYW5kZXYgWzB4
+JXhdLCBsMmRldl9mYyBbMHgleF0KAAAAAGNobmV0X2RoY3BfcmVjdjogbDJkZXZfZmMtPmZsb3dj
+X2lkIFsweCV4XSwgZGhjdHh0LT5zdGF0ZSBbJWRdLCBtYWxhY2lvdXMgZGhjcCByZWN2IGZvciBu
+byByZXF1ZXN0CgAAAAAAAAAAAAAAAABkaGN0eHQtPnN0YXRlIDogJWQKAAAAAAAAAAAAAAAAAGwy
+ZGV2X2ZjLT5mbG93Y19pZCBbMHgleF0sIEJhZCBESENQIGNvb2tpZSByZWNpZXZlZCwgYWJvcnRp
+bmcKAABOX1BPUlQgMHgleCV4JXggcmVqZWN0ZWQgUExPR0kgd2l0aCByZWFzb24gY29kZSAleAoA
+AAAAAAAAAAAAAAAAQUJUUyB3aGlsZSBhd2FpdGluZyBQUkxJIFJzcDogZmxvd2NfaWQgMHgleCBv
+eF9pZCAweCV4IHJ4X2lkIDB4JXggCgAAAAAAAAAAAAAAAABBQlRTIGZha2UgUnNwOiBsb2MgMHgl
+eCBveF9pZCAweCV4IHJ4X2lkIDB4JXgKAABjYW5ub3QgYWxsb2NhdGUgUE9GQ09FIGZpbHRlciBj
+b25uZWN0aW9uIGZvciB4X2lkICV4IAoAAAAAAAAAAAAAcmVsZWFzZV90aWQ6IHNpemVvZih0Y2Jf
+ZmMtPmZsb3djX2ZvaXNjc2lfY29ubikgWyV1XSwgYnl0ZXMKAAAAAGFjdF9vcGVuX3JwbDogYXRp
+ZCBbMHgleF0sIHRpZCBbMHgleF0sIG9wIFsweCV4XSwgc3RhdHVzIFsweCV4XQoAAAAAAAAAAAAA
+AAAAAAAAYWN0X29wZW5fcnBsOiBjc2tfZmMtPnRjYl9zdGF0ZSBbMHgleF0sIGNza19mYy0+Zmxh
+Z3MgWzB4JXhdLCB0Y2JfZmMtPmZsb3djX3N0YXRlIFsweCV4XQoAAAAAAAAAc2VuZF9hYm9ydF9y
+cGw6IGNza19mYy0+Zmxvd2NfdHlwZSBbMHgleF0sIGNza19mYy0+Zmxvd2NfaWQgWzB4JXhdLCB0
+aWQgWzB4JXhdLCB1bHB0eGNoIFsldV0sIGJ1ZmZlcmVkIFsldV0KAAAAAHdyaF9vZmxkX3RjcF9j
+bG9zZV9jb25fcmVwbHk6IHRjYl9mYy0+Zmxvd2NfaWQgWzB4JXhdLCB0Y2JfZmMtPmZsb3djX3R5
+cGUgWzB4JXhdLCBsZW4xNiBbJXVdLCBsb2MgWyV1XQoAAAAAAAAAAAB3cmhfb2ZsZF90Y3BfY2xv
+c2VfY29uX3JlcGx5OiBycGwtPm9wX1RpZCBbMHgleF0sIHJwbD5zdGF0dXMgWzB4JXhdLCBycGwt
+PnNuZF9ueHQgWzB4JXhdLCBycGwtPnJjdl9ueHQgWzB4JXhdCgAAdGNwX2Fib3J0X3JwbF9yc3M6
+IHRpZCBbMHgleF0sIHN0YXR1cyBbMHgleF0KAAAAdGNwX2Fib3J0X3JlcV9yc3M6IHRpZCBbMHgl
+eF0sIHN0YXR1cyBbMHgleF0KAAAAcGt0c2NoZWRfY2xfcmxbJXU6JXVdOiBtb2RlIHwgdW5pdCB8
+IHJhdGUgMHglMDZ4IG1pbiAldSBtYXggJXUgcGt0c2l6ZSAldQoAAAAAAABwYXJhbV9kbWFxWzB4
+JXg6MHgleF06IGRtYXEgMHgleCByZWFkICV1IHBmICV1IHJldCAlZAoAAAAAAAAAAAAATUMgaW5p
+dGlhbGl6YXRpb24gbm90IGNvbXBsZXRpbmcsIE1DIGN1cnJlbnQgaW5pdCBzdGF0ZSBpcyAweCUw
+MngKAAAAAAAAAAAAAAAAAABod19iY204NDgzNF9sb2Fkc2VxdWVuY2U6IFN0YXJ0ZWQKAAAAAAAA
+AAAAAAAAAABod19iY204NDgzNF9sb2Fkc2VxdWVuY2U6IFVwbG9hZCBpbWFnZSB0byBQSFkgb24t
+Y2hpcCBtZW1vcgoAAAAAaHdfYmNtODQ4MzRfbG9hZHNlcXVlbmNlOiBkb25lIGxvYWRpbmcgaW1h
+Z2UgKGkgPSAldSkKAAAAAAAAAAAAAEZMQVNIIG5vdCByZWFkeTogaSAldSBudnJSZWcgJSN4CgAA
+AAAAAAAAAAAAAAAAAEFRX0ZMQVNIX1JlYWR5IC0gVGltZW91dCAoMSkKAAAAQVFfRkxBU0hfUmVh
+ZHkgLSBUaW1lb3V0ICgyKQoAAAAJQVFfUmV0dXJuQ29udHJvbE9mRkxBU0gKAAAAAAAAAHBoeTog
+ZmFpbGVkIHRvIGFsbG9jYXRlZCBtZW1vcnkgZm9yIHBoeSBmdyBmaWxlLCByZXQgJWQKAAAAAAAA
+AABQSFkgRlcgdmVyc2lvbiBpbmZvcm1hdGlvbiBub3QgYXZhaWxhYmxlIGZvciB0aGlzIHRlY2hu
+b2xvZ3kKAAAAaHdfbGVfZmlsdGVyX2N0dXBsZTogdHVwbGUgJXUgbm90IHNwZWNpZmllZCBidXQg
+cmVxdWlyZWQgZm9yIG1hc2sgMHgleAoAAAAAAAAAAABsZSBjb25maWd1cmF0aW9uOiBuZW50cmll
+cyAldSByb3V0ZSAldSBjbGlwICV1IGZpbHRlciAldSBhY3RpdmUgJXUgc2VydmVyICV1IGhhc2gg
+JXUKAAAAAAAAAAAAAABsZSBjb25maWd1cmF0aW9uOiBuZW50cmllcyAldSByb3V0ZSAldSBjbGlw
+ICV1IGZpbHRlciAldSBzZXJ2ZXIgJXUgYWN0aXZlICV1IGhhc2ggJXUgbnNlcnZlcnNyYW0gJXUK
+AAAAAAAAAAAAAAAAY2ZfcGFyc2U6IGZpbGUgbWVtdHlwZSAweCV4IG1lbWFkZHIgMHgleCBtYXBw
+ZWQgQCAlcDoKAAAAAAAAAAAAAGNvbmZpZ3VyZWQgd2l0aCBjYXBzIG5ibXxsaW5rIDB4JTA4eCBz
+d2l0Y2h8bmljIDB4JTA4eCB0b2V8cmRtYSAweCUwOHggaXNjc2l8ZmNvZSAweCUwOHgKAAAAAAAA
+AG5ldCBWSSBhbGxvY2F0aW9uIGZhaWxlZCBmb3IgZmNfaWQgJXUgd2l0aCBlcnJvciAlZAoAAAAA
+AAAAAAAAAABuZXQgVkkgbWFjIGFkZHJlc3MgcHJvZ3JhbW1pbmcgZmFpbGVkIGZvciBmY19pZCAl
+dSB3aXRoIGVycm9yICVkCgAAAAAAAAAAAAAAAAAAAG5ldCBWSSByeG1vZGUgcHJvZ3JhbW1pbmcg
+ZmFpbGVkIGZvciBmY19pZCAldSB3aXRoIGVycm9yICVkCgAAAABuZXQgVkkgcnNzIGluZGlyZWN0
+aW9uIHRhYmxlIHByb2dyYW1taW5nIGZvciBmY19pZCAldSBmYWlsZWQgd2l0aCBlcnJvciAlZAoA
+AAAAAG5ldCBWSSByc3MgY29uZmlnIGNvbW1hbmQgZmFpbGVkIGZvciBmY19pZCAldSB3aXRoIGVy
+cm9yICVkCgAAAABuZXQgVkkgY29tbWFuZCBmYWlsZWQgZm9yIGZjX2lkICV1IHdpdGggZXJyb3Ig
+JWQKAAAAAAAAAAAAAAAAAAAAcHBtYXggWyV1XSwgYml0cyBbJXVdLCBGV19JU0NTSV9QQUdFUE9E
+X1RBR19JRFhfTUFYX1NJWkUgWyV1XQoAAGRlZmF1bHQgdGFnbWFzayBbMHglMHhdCgAAAAAAAAAA
+cHJvZ3JhbW1lZCB0YWdtYXNrIFsweCUweF0KAAAAAABwcG0tPmRkcF9pbmZvLmxsaW1pdCBbMHgl
+MHhdLCBwcG0tPmRkcF9pbmZvLnVsaW1pdCBbMHglMHhdLCBwcG0tPmRkcF9pbmZvLnNpemUgWzB4
+JTB4XQoAAAAAAAAAAABwcG0tPmRkcF9pbmZvLm1heF90eHN6IFsweCUweF0gcHBtLT5kZHBfaW5m
+by5tYXhfcnhzeiBbMHglMHhdIGlvc2l6ZSBbMHglMHhdCgAAAHBwbWF4IFsldV0sIGlkeF9iaXRz
+IFsldV0sIGlkeF9tYXNrIFsweCUweF0sIHJlc3ZkX3RhZ19tYXNrIFsweCUweF0sIHRhZ21hc2sg
+WzB4JTB4XQoAAAAAAAAAAAAAAHRhZyBpdHQgMHglMHgsIGJpdHMgJXUsIGFnZSAweCUweCwgYml0
+cyAldQoAAAAAAGN4Y25pYy0+aXNjc2lfcHBtIFsweCUweF0KAAAAAAAAc2NzaV9wbGRfc2l6ZSBb
+JXVdLCBBTElHTihzY3NpX3BsZF9zaXplLCAxNikgWyV1XQoAAAAAAAAAAAAAAAAAAG1heF9wcG9k
+X3pvbmVzIFsldV0KAAAAAAAAAAAAAAAAZm9pc2NzaV9pbml0OiBmb2lzY3NpX2luaXRfZG9uZSBb
+JXVdLCBkZXYucmVzLmZvaXNjc2lfbnRhc2tzIFsldV0sIGRldi5yZXMuZm9pc2NzaV9uc2VzcyBb
+JXVdLCBkZXYucmVzLm5jc29jayBbJXVdLCBkZXYucmVzLmZvaXNjc2lfbmluaXQgWyV1XSwgcmMg
+WyVkXQoAAAAAAAAAAGNoX2NsX3JhdGVbJXUvJXVdOiBjYXBwZWQgY2xhc3MgcmF0ZSBmcm9tIHJl
+cXVlc3RlZCAldSB0byBjb25maWd1cmVkIChlZmZlY3RpdmUpIGNoYW5uZWwgcmF0ZSAldQoAAAAA
+AAAAAAAAAAAAAABjaF9jbF9yYXRlWyV1LyV1XTogaW5jcmVhc2VkIGRlZmljaXRfaW5jciBmcm9t
+IHJlcXVlc3RlZCAldSB0byByZXF1aXJlZCBtaW4gb2YgJXU7IHJhdGUgJXUgKGVmZiAldSkgZGVm
+aWNpdF9tYXggJXUKAAAAAAAAAAAAAAAAAHBrdHNjaGVkIGNoYW5uZWwgJXUgc2V0cyBzcGVlZCAo
+ZnJvbSAldSkgdG8gJXUga2JwcwoAAAAAAAAAAAAAAABwb3J0X2NtZF9oYW5kbGVyOiB1bmtub3du
+IHUuZGNiLnR5cGUgMHgleAoAAAAAAABpMmMgZXJyb3IgY2F1c2VkIGJ5IG1vZHVsZSB1bnBsdWcK
+AAAAAAAAAAAAAAAAAABzZW5kdG8gcGVuZGluZzogd3JfcGVuZCAlcCBmb3IgcG9ydCAldSwgIHdh
+bnQgdG8gc2VuZCB0byBwb3J0ICV1CgAAAAAAAAAAAAAAAAAAAHNlbmR0bzpmbG93Y2lkICV1CgAA
+AAAAAAAAAAAAAAAAcG9ydF9saW5rX3N0YXRlX2hhbmRsZXJbJXVdIGxlYXZpbmcgU1RBVEVfVVAg
+KD0+cG93ZXJkb3duKQoAAAAAAHBvcnRfbGlua19zdGF0ZV9oYW5kbGVyWyV1XSBwb3dlcmluZyBk
+b3duCgAAAAAAAHBvcnRfbGlua19zdGF0ZV9oYW5kbGVyWyV1XSBsZWF2aW5nIHBvd2VyZG93biAo
+PT5kb3duKQoAAAAAAAAAAABwb3J0X2xpbmtfc3RhdGVfaGFuZGxlclsldV0gbGVhdmluZyBTVEFU
+RV9ET1dOICg9PnBvd2VydXApCgAAAAAAcG9ydF9saW5rX3N0YXRlX2hhbmRsZXJbJXVdIHBvd2Vy
+aW5nIHVwCgAAAAAAAAAAcG9ydF9saW5rX3N0YXRlX2hhbmRsZXIgbGVhdmluZyBwb3dlcnVwICg9
+PnVwKQoAcG9ydF9saW5rX3N0YXRlX2hhbmRsZXJbJXVdIG9oIG15IHdoYXQgaXMgZ29pbmcgb24g
+KHN0YXRlID0gJSN4CgAAAAAAAAAAAAAAAAAAAABwb3J0X2xpbmtfc3RhdGVfaGFuZGxlciBnb3Qg
+cmVzY2hlZHVsZWQKAAAAAAAAAABwb3J0X2xpbmtfc3RhdGVfaGFuZGxlcjogU29tZXRoaW5nIHdl
+bnQgdGVycmlibHkgd3JvbmcuIHJldCA9ICVkCgAAAAAAAAAAAAAAAAAAAHNldHRpbmcvdW5zZXR0
+aW5nIGhzcyByZXN5bmMgYml0CgAAAAAAAAAAAAAAAAAAAGxlIGluaXRpYWxpemF0aW9uOiBuZW50
+cmllcyAldSByb3V0ZSAldSBjbGlwICV1IGZpbHRlciAldSBhY3RpdmUgJXUgc2VydmVyICV1IGhh
+c2ggJXUKAAAAAAAAAAAAAGxlIGluaXRpYWxpemF0aW9uOiBuZW50cmllcyAldSByb3V0ZSAldSBj
+bGlwICV1IGZpbHRlciAldSBzZXJ2ZXIgJXUgYWN0aXZlICV1IGhhc2ggJXUgbnNlcnZlcnNyYW0g
+JXUKAAAAAAAAAAAAAAB0cCBjaGFubmVsIGNvbmZpZ3VyYXRpb246ICV1IHJ4IGFuZCAldSB0eCBj
+aGFubmVscwoAAAAAAAAAAAAAAAAAdHAgbWVtIGRpc3RyaWJ1dGlvblslJV0gcG10eCAldSBwbXJ4
+ICV1IGRkcCAldSBkZHBfaXNjc2kgJXUgc3RhZyAldSBwYmwgJXUgcnEgJXUgcnF1ZHAgJXUKAAAA
+AAAAVFBfUE1NX1JYX1BBR0VfU0laRSAldSBpcyBub3Qgc3VwcG9ydGVkCgAAAAAAAAAAVFBfUE1N
+X1RYX1BBR0VfU0laRSAldSBpcyBub3Qgc3VwcG9ydGVkCgAAAAAAAAAAY3hjbmljX2RldmljZV9p
+bml0OiBjeGNuaWMgWzB4JTB4XSwgY3hjbmljLT5maWx0ZXIgWyUweF0KAAAAAAAAAGN4Y25pY19k
+ZXZpY2VfaW5pdDogZGV2LnRwLm51bV90eF9wYWdlcyBbJXVdLCBkZXYubWNbMF0uc2l6ZSBbJXVd
+LCBkZXYudHAudHhfcGFnZV9zeiBbJXVdLCBjeGNuaWMgc2l6ZSBbJWRdLCByYyBbJWRdCgAAAAAA
+AAAAAAAAUG9ydFsldV06IFVua25vd24gU0dNSUkgc3ViLXR5cGUgJSN4CgAAAAAAAAAAAAAAUG9y
+dFsldV06IFVua25vd24gQlRfWEZJIHN1Yi10eXBlICUjeAoAAAAAAAAAAAAAUG9ydFsldV06IFVu
+a25vd24gQlRfWEFVSSBzdWItdHlwZSAlI3gKAAAAAAAAAAAAcG9ydF9pbml0WyV1XTogcG9ydCB0
+eXBlIDB4JXggaXMgbm90IHN1cHBvcnRlZAoARVEgcGZuICV1IHZmbiAldTogZGVzdHJveWluZyBl
+cWlkICV1IHdpdGggcGVuZGluZyBXUihzKSAobnVtX2J5dGVzICV1IGFuZCBmbGFncyAweCUwOHgK
+AAAAAAAAAAAAbDJkZXZfZmMtPmZsb3djX2lkIFsldV0sIGwyZGMtPnBmbiBbJXVdLCBsMmRjLT52
+Zm4gWyV1XSwgbDJkYy0+bHBvcnQgWyV1XSwgbDJkZXZfZmMtPmZsb3dpZCBbJXVdIGwyZGMtPnR4
+X2NoIFsldV0sIGRldi52cGQucG9ydHZlYyBbJXhdCgAAAAAAAAAAcG9ydHZlYyBbJXVdCgAAAEFo
+IGhhLi4uZG91YmxlIGZyZWUgb3hfaWQgMHgleCwgcnhfaWQgMHgleAoAAAAAAEhvc3QgUFJMSSBS
+ZXNwb25zZSB0aW1lZG91dDogb3hfaWQgMHgleCByeF9pZCAweCV4CgAAAAAAAAAAAAAAAABwZm4g
+JXUgdmZuICV1IHZpYSBjb21tYW5kCgAAAAAAAGNvbmZpZ3VyYXRpb24gZmlsZSBwYXJzZXI6IHNn
+ZSB0aW1lciB2YWx1ZVslaV0gaXMgdG9vIGxhcmdlLCBjaGFuZ2luZyBmcm9tICV1IHRvICV1dXNl
+Y3MKAAAAAAAAAGZpbHRlcm1hc2sgMHgleCBpcyBub3QgZXF1YWwvc3Vic2V0IHRvL29mIGZpbHRl
+cm1vZGUKAAAAAAAAAAAAAABod19sZV9jbGlwX2hhbmRsZXI6IHJlbW92ZWQgcG9zPSV1ICg9aWR4
+ICV1KQoAAABod19sZV9jbGlwX2hhbmRsZXI6IGFkZGluZyB0byBwb3M9JXUgKD1pZHggJXUpCgBt
+b2R1bGVbJXVdOiBwb3J0IG1vZHVsZSBpbnNlcnRlZCBhbmQgcmVhZHkKAAAAAABtb2R1bGVbJXVd
+OiBwb3J0IG1vZHVsZSByZW1vdmVkCgAAAAAAAAAAAAAAAAAAAABtb2R1bGVbJXVdOiB1bmtub3du
+IG1vZHVsZSBpZGVudGlmaWVyIDB4JTAyeAoAAABtb2R1bGVbJXVdOiBncGlvICV1IHRyYW5zIDEw
+RyAweCUwMnggMUcgMHglMDJ4IChsZW5ndGggJXUpIGNhYmxlIDB4JTAyeCAobGVuZ3RoICV1KSBt
+b2R1bGVfdHlwZSAweCUwMngKAAAAAAAAAAAAY3g0X2NyX3Bkb3duWyV1XTogZW4gJXUgdmlfZW5f
+Y250ICV1CgAAAAAAAAAAAAAAY3JfbW9kdWxlX3J4X2xvc1sldV06IHJ4X2xvcyBjaGFuZ2VkIHRv
+ICV1CgAAAAAAaW5pdF9wb3J0WyV1XTogIGxpbmsgc3RhdHVzIDB4JXggbW9kX3R5cGUgMHgleCB2
+aV9lbl9jbnQgJXUgcnhfbG9zICV1IGhzc19zaWdkZXQgJXUgcG9ydF9jZmcuc2lnZGV0ICV1CgAA
+AAAAAAAAAGluaXRfcG9ydFsldV06ICBsaW5rIHN0YXR1cyAweCV4CgAAAAAAAAAAAAAAAAAAAGlu
+aXRfcG9ydFsldV06ICBsaW5rIHN0YXR1cyAweCV4CgAAAAAAAAAAAAAAAAAAAGluaXRfcG9ydFsl
+dV06ICBsaW5rIHN0YXR1cyAweCV4CgAAAAAAAAAAAAAAAAAAAGluaXRfcG9ydFsldV06ICBsaW5r
+IHN0YXR1cyAweCV4CgAAAAAAAAAAAAAAAAAAAGluaXRfcG9ydFsldV06ICBsaW5rIHN0YXR1cyAw
+eCV4CgAAAAAAAAAAAAAAAAAAAGN4NF9jclsldV06IHJldCAlZCBzdGF0dXMgJXUgeGdtX2xzIDB4
+JTA4eAoAAAAAAHBmbiAldSB2Zm4gJXUKAABodyBwZiBiaXRtYXAgMHglMDJ4IHZmaWQgYml0bWFw
+IDB4JTA4eDoweCUwOHg6MHglMDh4OjB4JTA4eAoAAAAAYWZ0ZXIgdmZpZCBmaXh1cCwgdmZpZCBi
+aXRtYXAgMHglMDh4OjB4JTA4eDoweCUwOHg6MHglMDh4CgAAAAAAAHdvcmthcm91bmQxMzcyMzog
+ZGV0ZWN0ZWQgV1IgQCAweCUwOHggb2Ygc2l6ZSAldSBieXRlcywgZHJpYmJsaW5nIGl0IGluICV1
+IGJ5dGVzIGF0IGEgdGltZQoAAAAAAHRpbWVyIHF1ZXVlICV1IGxvc3QgYSB0aWNrISBuZXh0ICVw
+IGxhc3QgJXAgbnVtZSAldQoAAAAAAAAAAAAAAABmbHJfdGltZXJfc3RhcnQ6IGZsb3djX2lkICV1
+ICVwIGJ1ZiAlcAoAAAAAAAAAAABwY2llOiBucGYgJXUgKHBmYml0bWFwIDB4JTAyeCkgbnZmICV1
+IChwZiAwLi4zIDB4JTAyeCAweCUwMnggMHglMDJ4IDB4JTAyeCkKAAAAAGZhaWxlZCB0byBmaW5k
+IHRoZSAlYyVjIFZQRCBwYXJhbWV0ZXIKAAAAAAAAAAAAAGZhaWxlZCB0byBwYXJzZSB0aGUgJWMl
+YyBWUEQgcGFyYW1ldGVyCgAAAAAAAAAAAGZhaWxlZCB0byBzdWNjZXNzZnVsbHkgZmluZCBDaGVs
+c2lvIFZQRAoAAAAAAAAAAGxvZyBpbml0aWFsaXplZCBAIDB4JTA4eCBzaXplICV1ICgldSBlbnRy
+aWVzKQoAAHdyaF9mb2lzY3NpX3Njc2lfZXJyOiB3ciBbMHgleF0sIHF1ZXVlZCBvbiBmbG93YyBb
+MHgleF0sIGZpZm8gWzB4JXhdIGJ5dGVzX2F2YWlsYWJsZQoAAAAAAAAAAAAAAGh3IHJlZ2lzdGVy
+IG9wZXJhdGlvbiBub3QgY29tcGxldGluZywgcmVnIDB4JTA4eCBtYXNrIDB4JTA4eCB2YWx1ZSAw
+eCUwOHggKHJlZyAweCUwOHgpCgAAAAAAAAAAAE1ESU8gQ0w0NTogZmFpbGVkIHRvIHNldCB1cCBN
+TUQgYWRkcgoAAAAAAAAAAAAAAE1ESU86IGZhaWxlZCB0byB3cml0ZQoAAAAAAAAAAAAATURJTyBD
+TDQ1OiBmYWlsZWQgdG8gc2V0IHVwIE1NRCBhZGRyCgAAAAAAAAAAAAAATURJTzogZmFpbGVkIHRv
+IHJlYWQKAAAAAAAAAAAAAABzZW5kX2Nsb3NlX3JlcTogY3NrX2ZjLT5mbG93Y190eXBlIFsweCV4
+XSwgY3NrX2ZjLT5mbG93Y19pZCBbMHgleF0sIGNza19mYy0+dGNiX3N0YXRlIFsweCV4XQoAAABz
+ZW5kX2Nsb3NlX3JlcTogY3NrX2ZjLT5mbG93Y190eXBlIFsweCV4XSwgY3NrX2ZjLT5mbG93Y19p
+ZCBbMHgleF0sIHRpZCBbMHgleF0sIHVscHR4Y2ggWyV1XSxidWZmZXJlZCBbJXVdCgAAAAAAb2Zs
+ZF90Y3BfZG9fYWN0aXZlX2Nsb3NlOiBjc2tfZmMgWzB4JXhdLCBjc2tfZmMtPmZsb3djX2lkIFsw
+eCV4XSwgY3NrX2ZjLT50Y2Jfc3RhdGUgWzB4JXhdCgAAAAAAb2ZsZF90Y3BfZG9fYWN0aXZlX2Ns
+b3NlOiBjc2tfZmMgWzB4JXhdLCBjc2tfZmMtPmZsb3djX2lkIFsweCV4XSwgY3NrX2ZjLT50Y2Jf
+c3RhdGUgWzB4JXhdCgAAAAAAc2Vzc2lvbl9ibG9jazogc2Vzc19mYy0+Zmxvd2NfaWQgWzB4JXhd
+LCBzZXNzX2ZjLT5mbG93Y19zdGF0ZSBbMHgleF0sIGNvbm5fZmMtPmZsb3djX2lkIFsweCV4XSwg
+Y29ubl9mYy0+Zmxvd2Nfc3RhdGUgWzB4JXhdLCBjc2tfZmMtPmZsb3djX2lkIFsweCV4XSwgY3Nr
+X2ZjLT5mbG93Y19zdGF0ZSBbMHgleF0KAAAAAAAAAAAAAAAAAAAAb2ZsZF90Y3BfZGlzY29ubmVj
+dDogdGNiX2ZjLT5mbG93Y19pZCBbMHgleF0sIGNza19mYy0+Zmxvd2NfaWQgWzB4JXhdLCBjc2st
+PnRjYl9zdGF0ZSBbMHgleF0KAAAAZGVjb2RlX2Jhc2U2NF9zdHJpbmc6IGRsZW4gWyVkXQoAAAAA
+AAAAAAAAAAAAAAAAZGVjb2RlX2hleF9zdHJpbmc6IGRsZW4gWyVkXQoAAABsb2dnZWRfaW46IGZs
+b3djX2ZvaXNjc2lfY29ubl9mbGFncyBbMHgleF0KAAAAAABmb2lzY3NpX3ZhbGlkYXRlX2xvZ2lu
+X3N0YWdlOiAtIDEKAAAAAAAAAAAAAAAAAABTZXEgIyBtaXNtYXRjaCBzdGF0c24gPSAweCV4IGV4
+cHN0YXRzbiA9IDB4JXgKAAB0ZXh0X3Jlc3A6IGNfYml0IFsweCV4XSwgZl9iaXQgWzB4JXhdCgAA
+AAAAAAAAAABuZXRfbDJkZXZfZmluZF9ieV9hZGRyOiBsMmRldl9mYy0+Zmxvd2NfaWQgWzB4JXhd
+LCBsMmRjLT5scG9ydCBbJXVdLCBsMmRfZmMtPmZsb3djX2lkIFsweCV4XSwgbDJkYy0+aW40X2Rl
+di5pbl9hZGRyLmFkZHIgWzB4JXhdLCBhZGRyIFsweCV4XQoAAABuZXRfbDJkZXZfbXR1X2NvbmZp
+ZzogbDJkZXZfZmMtPmZsb3djX2lkIFsweCV4XSwgbXR1ICV1CgAAAAAAAAAAbmV0aWZfZG9fZGhj
+cDogd3ItPnBhcmFtLnZsYW5pZCBbJXVdLCBsMmRldl9mYy0+Zmxvd2NfbmV0X2wyZGV2X3ZsYW5k
+ZXYgWzB4JXhdCgBsMmRldl92aV9mc206IG1iIFsweCV4XSwgZGVmZXJyZWQsIHN0YXRlIFsweCV4
+XSwgcG9ydCBbMHgleF0KAAAAbDJkZXZfdmlfZnNtOiB2aWlkIFsweCV4XSBwb3J0IFsweCV4XSwg
+bWFjLWlkIFslMDJ4OiUwMng6JTAyeDolMDJ4OiUwMng6JTAyeF0uIAoAAAAAAAAAAAAAAAAAAAAA
+bDJkZXZfdmlfZnNtOiBzZ2VfZXFpZCBbMHgleF0sIHNnZV9pcWlkIFsweCV4XSwgc2dlX2VxY3Ig
+WzB4JXhdLCByc3Nfc3ogWzB4JXhdCgBsMmRldl92aV9mc206IGwyZGV2X2ZjLT5mbG93Y19uZXRf
+bDJkZXZfbXR1IFsldV0sIG1iX3NjcmF0Y2ggWzB4JXhdLCBwb3J0IFsweCV4XQoAAAAAAAAAAAAA
+AAAAAABsMmRldl92aV9mc206IHZpaWQgWyVkXSwgdmlfZmMtPmZsb3djX3ZpX2ZsYWdzIFsweCV4
+XQoAAAAAAAAAAAAAbDJkZXZfdmlfZnNtOiBwZm4gWzB4JXhdLCB2Zm4gWzB4JXhdLCBsMmRldl9m
+Yy0+Zmxvd2NfaWQgWzB4JXhdLCBscG9ydCBbMHgleF0sIHZpaWQgWzB4JXhdLCBmbGFncyBbMHgl
+eF0KAAAAAAAAAGwyZGV2X3ZpX2ZzbTogRXJyb3IgZnJlZWluZyBWSSwgcmMgWzB4JXhdCgAAAAAA
+AGwyZGV2X3ZpX2ZzbTogcGlkIFsweCV4XSwgdmlpZCBbMHgleF0sIG1iX2xvYyBbMHgleF0sIG1i
+X29yaWdbMHgleF0sIGwyZGV2X2ZsYWdzIFsweCV4XSwgcmMgWzB4JXhdCgAAAAAAAAAAAAAAAABl
+bmNvZGUgaGV4IHN0cmluZzogZGxlbiBbJWRdCgAAAHRjcF9zZW5kX2FvcGVuX3JlcTogY3NrX2Zj
+LT5mbG93Y19pZCBbMHgleF0sIGNza19mYy0+Zmxvd2Nfc3RhdGUgWzB4JXhdLCBidWZmZXJlZCBb
+JXVdLCByZXNfY250IFsweCV4XSwgc2NoZWRfbm9kZS5uZXh0IFsweCV4XQoAYW9wZW5fcmVxOiBo
+d19sZV9maWx0ZXJfY3R1cGxlIGZhaWxlZCA6ICVkCgAAAAAAb2ZsZF90Y3Bfc2VuZF9hb3Blbl9y
+ZXE6IGNwbF9yZXEtPkZpbHRlciBbMHglMHhdLCBjdHVwbGVzWzBdIFsweCV4XSwgY3R1cGxlc1sx
+XSBbMHgleF0KAAAAAAAAAAAAY3NvY2tfYWxsb2M6IHR4X2NoIFsweCV4XSwgbHBvcnQgWzB4JXhd
+LCBjb29raWUgWyUwOHhdCgAAAAAAAAAAAGNzb2NrX2FsbG9jOiBhdmFpbGFibGUgWyV1XSwgbmNz
+b2NrIFsldV0sIHBvczphdGlkIFsweCV4XSwgY3NrX2ZjIFsweCV4XSwgY3NrX2ZjLT5mbG93Y19p
+ZCBbMHgleF0sIHNwb3J0IFsldV0KAABmb2lzY3NpX3BlZXJfaW5pdDogY29ubl9hdHRyLT5oZGln
+ZXN0X3RvX2RkcF9wZ3N6IFsweCV4XQoAAAAAAAAAZm9pc2NzaV9wZWVyX2luaXQ6IGNvbm5fZmMt
+PmZsb3djX2ZvaXNjc2lfY29ubl9mbGFncyBbMHgleF0KAAAAAHRhc2tfZnJlZTogdHJ5aW5nIHRv
+IGZyZWUgcXVldWVkIHRhc2sgWzB4JXhdISEhCgAAAAAAAAAAAAAAAAAAAABXQVRDSERPRzogTm8g
+dGVtcGVyYXR1cmUgc2Vuc29yIGF2YWlsYWJsZS4KAAAAAABXQVRDSERPRzogQWN0aXZhdGluZwoA
+AAAAAAAAAAAAAFdBVENIRE9HIC0gRW5hYmxlIGFjdGlvbiAldSB0aW1lICV1CgAAAAAAAAAAAAAA
+AFdBVENIRE9HIC0gRGlzYWJsZSBhY3Rpb24gJXUKAAAAV0FUQ0hET0c6IERlLWFjdGl2YXRpbmcK
+AAAAAAAAAABzZXNzaW9uX3VuYmxvY2s6IHNlc3NfZmMtPmZsb3djX2lkIFsweCV4XSwgc2Vzc19m
+Yy0+Zmxvd2Nfc3RhdGUgWzB4JXhdLCBjb25uX2ZjLT5mbG93Y19pZCBbMHgleF0sIGNvbm5fZmMt
+PmZsb3djX3N0YXRlIFsweCV4XSwgY3NrX2ZjLT5mbG93Y19pZCBbMHgleF0sIGNza19mYy0+Zmxv
+d2Nfc3RhdGUgWzB4JXhdCgAAAAAAAAAAAAAAAABzZXNzaW9uX2xpbmtfbm90aWZ5OiBwb3J0IFsw
+eCV4XSBsaW5rIGRvd24gcGluZyByZWNvdmVyeSBkaXNhYmxlZCwgdHJ5aW5nIHRvIHJlY292ZXIg
+c2Vzc2lvbiBbMHgleF0gY29ubiBbMHgleF0KAAAAAAAAAAAAAAAAAAAAAHNlc3Npb25fbGlua19u
+b3RpZnk6IHBvcnQgWzB4JXhdIGxpbmsgdXAgc2Vzc2lvbiBbMHgleF0gY29ubiBbMHgleF0KAAAA
+AAAAAAAAAAAAZm9pc2NzaSBjb25uX2ZjIFsweCV4XSwgZmxvd2Nfc2NoZWRjbCBbMHgleF0sIGlu
+Z19jaCBbMHgleF0sIGVncl9jaCBbMHgleF0KAAAAAABsMmRldl9ub3RpZnkgd2l0aCB1bmtub3du
+IGZsYWcgWzB4JXhdCgAAAAAAAAAAAABGQ29FIEZDQiBsaW5rZG93bjogaW9fcmVxIDB4JXgleCBp
+cWlkIDB4JXggZmxvd2lkIDB4JXggb3AgMHgleAoAZmNvZSBub3RpZnkgOiBVcGRhdGUgbmV3IERD
+QlggdmFsdWVzIFZJIHN0YXRlIDB4JXggcHJpIDB4JXggc2NoZWRjbCAweCV4IGRjYnhfZG9uZSAw
+eCV4CgAAAAAAAAAAZmNvZSBub3RpZnkgOiBGQ0YgZmxvd2lkIDB4JXgsIHVscGNoIDB4JXggCgAA
+AAAAbmV0X2wyZGV2X25vdGlmeTogRXZlbnQgb24gbDJkZXZfZmMtPmZsb3djX2lkIFsweCV4XSwg
+cG9ydCBbJWRdLCBldmVudCBbMHgleF0sIHVscHR4Y2ggWyV1XSwgY2xhc3MgWzB4JXhdLCBwcmlv
+cml0eSBbMHgleF0KAAAAAABmY29lIG5vdGlmeSA6IERDQlggOiBwb3J0IDB4JXgsIHByaW9yaXR5
+IDB4JXggdWxwdHhjaCAweCV4IGNsYXNzIDB4JXgKAAAAAAAAAAAAAGNoX2NsX3JhdGVbJXUvJXVd
+OiBjYXBwZWQgZGVmaWNpdF9pbmNyIGZyb20gcmVxdWlyZWQgJXUgdG8gJXU7IHJhdGUgJXUgKGVm
+ZiAldSkgZGVmaWNpdF9tYXggJXUKAEZDb0UgRkNGIHRpbWVyOiBmbG93YyBzdGF0ZSAweCV4LCBw
+b3J0IDB4JXggLGZjZiAweCV4LCBmbG93Y19pZCAweCV4CgAAAAAAAAAAAAAAY29yZV9wcm9ncmFt
+X3RjYjogdGlkICUjeCB0X3N0YXRlICUjeCByY3ZfYWR2IDB4JTA4eCByY3Zfc2NhbGUgJSN4IHR4
+X21heCAlI3ggcmN2X254dCAlI3ggYXRpZCAlI3gKAAAAAAAAAAAAAAAAAAlvcHQwICUjeCV4IG9w
+dDIgJSN4IGlwdjYgJSN4IGZsYWdzX3RpbWVyIDB4JTA4eAoAAAAAAAAAAAAAAAAAAABvZmxkX2Nv
+bm5lY3Rpb25fd3I6IGNvbm5lY3Rpb24gd2l0aCA1LXR1cGxlIGxwIDB4JTA0eCBmcCAweCUwNHgg
+bGlwIDB4JTA4eCUwOHggcGlwIDB4JTA4eCUwOHggZmlsdGVyIDB4JTA4eCBleGlzdHMgQCBMRSBp
+bmRleCAldQoAAAAAAAAAAAAAAAAAAABvZmxkX2Nvbm5lY3Rpb25fd3I6IGNvbm5lY3Rpb24gd2l0
+aCA1LXR1cGxlIGxwIDB4JTA0eCBmcCAweCUwNHggbGlwIDB4JTA4eCBwaXAgMHglMDh4IGZpbHRl
+ciAweCUwOHggZXhpc3RzIEAgTEUgaW5kZXggJXUKAAAAAAAAAG9mbGRfY29ubmVjdGlvbl93cjog
+Y29ubmVjdGlvbiB3aXRoIDUtdHVwbGUgbHAgMHglMDR4IGZwIDB4JTA0eCBsaXAgMHglMDh4JTA4
+eCBwaXAgMHglMDh4JTA4eCBmaWx0ZXIgMHglMDh4CgAAAABvZmxkX2Nvbm5lY3Rpb25fd3I6IGNv
+bm5lY3Rpb24gd2l0aCA1LXR1cGxlIGxwIDB4JTA0eCBmcCAweCUwNHggbGlwIDB4JTA4eCBwaXAg
+MHglMDh4IGZpbHRlciAweCUwOHgKAAAAAAAAAAAAAAAASVFGTElOVCBwZm4gJXUgdmZuICV1OiBp
+cWVzaXplICV1IHRvbyBzbWFsbAoAAAAASVFGTElOVCBwZm4gJXUgdmZuICV1OiBpcWlkICV1IHRv
+byBsYXJnZSAobWF4ICV1KQoAAAAAAAAAAAAAAAAAAElRRkxJTlQgcGZuICV1IHZmbiAldTogaXFp
+ZCAldSBub3QgYWxsb2NhdGVkCgAAAElRRkxJTlQgcGZuICV1IHZmbiAldTogZmwwaWQgJXUgdG9v
+IGxhcmdlIChtYXggJXUpCgAAAAAAAAAAAAAAAABJUUZMSU5UIHBmbiAldSB2Zm4gJXU6IGZsMGlk
+ICV1IG5vdCBhbGxvY2F0ZWQKAABJUUZMSU5UIHBmbiAldSB2Zm4gJXU6IGZsMWlkICV1IHRvbyBs
+YXJnZSAobWF4ICV1KQoAAAAAAAAAAAAAAAAASVFGTElOVCBwZm4gJXUgdmZuICV1OiBmbDFpZCAl
+dSBub3QgYWxsb2NhdGVkCgAASVFGTElOVCBwZm4gJXUgdmZuICV1OiBmbDFpZCAldSBpcyB2YWxp
+ZCBidXQgbm90IGZsMGlkICV1CgAAAAAAAElRRkxJTlQgcGZuICV1IHZmbiAldTogZmwxaWQgJXUg
+aXMgdmFsaWQgYnV0IGhlYWRlciBzcGxpdCBmZWF0dXJlIGlzIG5vdCBlbmFibGVkCgAAAAAAAAAA
+AAAAAAAAAElRIHBmbiAldSB2Zm4gJXU6IGlxaWQgJXUgdG9vIGxhcmdlIChtYXggJXUpCgAAAElR
+IHBmbiAldSB2Zm4gJXU6IGlxaWQgJXUgbm90IGFsbG9jYXRlZAoAAAAAAAAAAElRIHBmbiAldSB2
+Zm4gJXU6IGZsMGlkICV1IGZsMWlkICV1IGJ1dCBub3Qgc3VwcG9ydGVkCgAAAAAAAAAAAABod191
+bHB0eF93b3JrYXJvdW5kX3ByMTY5NDlfZW5hYmxlZF9wZjogcGYgJXUgZW5hYmxlZCAldQoAAAAA
+AAAAaHdfdWxwdHhfd29ya2Fyb3VuZF9wcjE2OTQ5X2VuYWJsZWRfdmZpZDogdmZpZCAldSBlbmFi
+bGVkICV1CgAAAEVRIHBmbiAldSB2Zm4gJXU6IGNyZWF0aW5nIEVUSCBlcWlkICV1IHdpdGggcGVu
+ZGluZyBXUihzKSAobnVtX2J5dGVzICV1IGFuZCBmbGFncyAweCUwOHgKAAAAAAAAAEVRIHBmbiAl
+dSB2Zm4gJXU6IGNyZWF0aW5nIENUUkwgZXFpZCAldSB3aXRoIHBlbmRpbmcgV1IocykgKG51bV9i
+eXRlcyAldSBhbmQgZmxhZ3MgMHglMDh4CgAAAAAAAEVRIHBmbiAldSB2Zm4gJXU6IGVxaWQgJXUg
+dG9vIGxhcmdlIChtYXggJXUpCgAAAEVRIHBmbiAldSB2Zm4gJXU6IGVxaWQgJXUgbm90IGFsbG9j
+YXRlZAoAAAAAAAAAAGh3X2NpbV90cF93b3JrYXJvdW5kMTM3MjNfZW5hYmxlOiBwb3J0ICV1IHBy
+b3RvY29sIDB4JXggZW4gJXUgY3VycmVudCAweCV4IHdvcmthcm91bmRfcHIxMzcyMyAweCV4CgAA
+AAAAAAAAAAAAAABwb3J0X2JsaW5rX2xlZF9yZXN0b3JlCgAAAAAAAAAAAHBvcnRfYmxpbms6IGJs
+aW5rZHVyPTB4JXggYmxpbmtfcmVmY250CgAAAAAAAAAAAHBvcnRfYmxpbms6IAlibGlua19yZWZj
+bnQ9MHgleAoAcG9ydF9ibGluazogCWJsaW5rX3JlZmNudD0weCV4CgBtaWlfYWR2X2ZjWyV1XTog
+cmNhcHMgMHgleAoAAAAAAG1paV9hZHZfc3BlZWRbJXVdOiByY2FwcyAweCV4CgAAbWlpX2FucmVz
+dGFydFsldV06IGFjYXBzIDB4JXgKAABtaWlfaW5pdFsldV06IGFjYXBzIDB4JXgKAAAAAAAAAG1p
+aV9wZG93blsldV06IHBvd2VyZG93biBlbiAldQoAcG9ydFsldV06IGdhdmUgdXAgZml4aW5nIGVy
+cm9ycyEhIQoAAAAAAAAAAAAAAAAAcG9ydFsldToweCUwMng6MHglMDJ4XTogbDFjZmcsIHBjYXBz
+IDB4JXggYWNhcHMgMHgleCByY2FwcyAweCV4CgAAAAAAAAAAAAAAAAAAAABwb3J0WyV1OjB4JTAy
+eDoweCUwMnhdOiBsMWNmZywgaW52YWxpZCByZXF1ZXN0LCBwY2FwcyAweCV4IGFjYXBzIDB4JXgg
+cmNhcHMgMHgleAoAAAAAAAAAAAAAAAAAAABwb3J0WyV1OjB4JTAyeDoweCUwMnhdOiBsMWNmZywg
+bWRpIG4vYSBwY2FwcyAweCV4IGFjYXBzIDB4JXggcmNhcHMgMHgleAoAAAAAAAAAAHBvcnRbJXU6
+MHglMDJ4OjB4JTAyeF06IGwxY2ZnLCBjYW5ub3QgZm9yY2Ugbm8vbXVsdGlwbGUgc3BlZWQocyks
+IHBjYXBzIDB4JXggYWNhcHMgMHgleCByY2FwcyAweCV4CgAAAAAAAAAAAAAAAABnZXRfcmVmX3Rh
+c2s6IHJlcXVlc3RlZCBjb29raWU6IGhpZ2ggWzB4JTA4eF0sIGxvdyBbMHglMDh4XQoAAAAAZ2V0
+X3JlZl90YXNrOiB0YXNrIGNvb2tpZTogaGlnaCBbMHglMDh4XSwgbG93IFsweCUwOHhdCgAAAAAA
+AAAAAGdldF9yZWZfdGFzazogaXN0YXNrLT5mbG93Y19pZCBbMHgleF0sIGlzdGFza19mYy0+Zmxv
+d2Nfc3RhdGUgWyV1XQoAAAAAAAAAAAAAAAAAZ2V0X3JlZl90YXNrOiB0YXNrIG5vdCBmb3IgY29v
+a2llICVseAoAAAAAAAAAAAAAcHJvY2Vzc19kaGNwX29wdHM6IHJvb3QgcGF0aCBsZW4gWyVkXSBi
+eXRlcwoAAAAAbmV0aWZfcHJvY2Vzc19kaGNwX29wdHM6IGwyZGV2X2ZjLT5mbG93Y19pZCBbMHgl
+eF0sIE1TR19UWVBFIFslZF0sIGRoY3R4dC0+c3RhdGUgWyVkXQoAAAAAAAAAAAAAaWNtcF9yZWN2
+OiBsMmRldl9mYy0+Zmxvd2NfaWQgWzB4JXhdLCBwaWQgWzB4JXhdLCBpY21wIHR5cGUgWzB4JXhd
+CgAAAAAAAAAAAAAAAABBQlRTIEFDQyBhd2FpdGluZyBQUkxJIFJzcDogZmxvd2NfaWQgMHgleCBv
+eF9pZCAweCV4IHJ4X2lkIDB4JXggaXFpZCAweCV4CgAAAAAAAGNobmV0X2FycF9yZWN2OiBpcCBj
+b25mbGljdCBkZXRlY3RlZAoAAAAAAAAAAAAAAGNobmV0X2FycF9yZWN2OiBwaWQgWyV1XSwgdmxh
+biBbMHgleF0sIGFycCBvcCBbMHgleF0sIHNpcCBbMHgleF0sIHJpcCBbMHgleF0KAAAAY3NvY2tf
+cGVlcl9jbG9zZTogY3NrX2ZjLT5mbG93Y19pZCBbMHgleF0sIGNza19mYy0+dGNiX2ZjLT5mbG93
+Y19pZCBbMHgleF0sIGNza19mYy0+Zmxvd2Nfc3RhdGUgWzB4JXhdLCBjc2tfZmMtPmZsb3djX2Nz
+b2NrX3RjYl9mYy0+Zmxvd2Nfc3RhdGUgWzB4JXhdCgAAAAAAAAAAAGNzb2NrX3BlZXJfY2xvc2U6
+IGNza19mYy0+Zmxvd2NfaWQgWzB4JXhdLCBjc2tfZmMtPmZsb3djX3N0YXRlICBbMHgleF0KAAAA
+AAAAAAAAaHdfdWxwdHhfd29ya2Fyb3VuZF9wcjE2OTQ5X2VuYWJsZWRfcGZfaXE6IGlxICV1IGVu
+YWJsZWQgJXUgKHBmICV1KQoAAAAAAAAAAAAAAAB0Y2JfZmMtPmZsb3djX2lkIFsweCUwNnhdLCB0
+Y2JfZmMtPmZsb3djX3R5cGUgWzB4JXhdLCBjcGxvcCBbMHgleF0gCgAAAAAAAAAAAAAAAGNoX3Jh
+dGVbJXVdOiBjYXBwZWQgdGljayBmcm9tIHJlcXVpcmVkICV1IHRvIHN1cHBvcnRlZCAldTsgcmF0
+ZSAldSAoZWZmICV1KSBkZWZpY2l0X2luY3IgJXUgdGljayAldQoAAAAAAAAAAAAAAABwa3RzY2hl
+ZF9jaF9ybFsldV06IGNoYW5uZWwgcmwgbm90IGF2YWlsYWJsZSBpbiBjb25qdW5jdGlvbiB3aXRo
+IGZsb3cgc2hhcGluZwoAAHBrdHNjaGVkX2NoX3JsWyV1XTogcmF0ZSAldSBtYXggJXUKAAAAAAAA
+AAAAAAAAAHBrdHNjaGVkX2NsX3dyclsldToldV06IHdlaWdodCAldSB3ZWlnaHRlZF9tYXNrIDB4
+JXgKAAAAAAAAAAAAAABlcV9wYXJhbXNbMHgleDoweCV4XTogZG1hcSAweCV4IHJlYWQgJXUgcGYg
+JXUgZXFpZF9hcGkgJXUgcmV0ICVkCgAAAAAAAAAAAAAAAAAAAGh3X2JjbTg0MzRfY2hlY2tyYW06
+IFN0YXJ0CgAAAAAAUEhZWyV1XSBwcm9jZXNzb3Igbm90IHJ1bm5pbmcKAABQSFlbJXVdIEZXIGhh
+cyBiYWQgQ1JDCgAAAAAAAAAAAFBIWSBmaXJtd2FyZSBsb2FkIHN1Y2Nlc3NmdWwhCgAACUFRX1Rh
+a2VDb250cm9sT2ZGTEFTSDogMWUuYzAwMT0lI3ggMWUuYzQ1MD0lI3ggMWUuYzQ1MT0lI3ggMWUu
+MTAwPSUjeAoAAAAAAAAAAABBUV9BUElfV3JpdGVBbmRWZXJpZnlGbGFzaEltYWdlIC0gSW1hZ2Ug
+aW50ZWdyaXR5IGNoZWNrIGZhaWxlZCAoY2FsYyAlI3ggdmFsICUjeCkKAAAAAAAAAAAAAAAAAABB
+UV9BUElfV3JpdGVBbmRWZXJpZnlGbGFzaEltYWdlIC0gSW1hZ2UgaW50ZWdyaXR5IGNoZWNrIHBh
+c3NlZAoAQVFfQVBJX1dyaXRlQW5kVmVyaWZ5Rmxhc2hJbWFnZSAtIFRpbWVvdXQgd2FpdGluZyBm
+b3IgZmxhc2ggaW50ZXJmYWNlICgldSkKAAAAAABBUV9BUElfV3JpdGVBbmRWZXJpZnlGbGFzaElt
+YWdlIC0gVGltZW91dCB3YWl0aW5nIGZvciBmbGFzaCBpbnRlcmZhY2UgKCV1KQoAAAAAAEFRX0FQ
+SV9Xcml0ZUFuZFZlcmlmeUZsYXNoSW1hZ2UgLSBUaW1lb3V0IHdhaXRpbmcgZm9yIGZsYXNoIGlu
+dGVyZmFjZSAoJXUpCgAAAAAAQVFfQVBJX1dyaXRlQW5kVmVyaWZ5Rmxhc2hJbWFnZSAtIFRpbWVv
+dXQgd2FpdGluZyBmb3IgZmxhc2ggaW50ZXJmYWNlICgldSkgKHBwICUjeCBhcCAlI3gpCgAAAAAA
+QVFfQVBJX1dyaXRlQW5kVmVyaWZ5Rmxhc2hJbWFnZSAtIFRpbWVvdXQgd2FpdGluZyBmb3IgZmxh
+c2ggaW50ZXJmYWNlICgldSkKAAAAAABBUV9BUElfV3JpdGVBbmRWZXJpZnlGbGFzaEltYWdlIC0g
+VGltZW91dCB3YWl0aW5nIGZvciBmbGFzaCBpbnRlcmZhY2UgKCV1KQoAAAAAAEFRX0FQSV9Xcml0
+ZUFuZFZlcmlmeUZsYXNoSW1hZ2UgLSBFcnJvciBvbiBidXJuaW5nIEZMQVNIIChjcmMxNiBtaXNt
+YXRjaCkKAAAAAAAAbWFsbG9jX2RlY1sldV06IHN0YXJ0ICVwIGVuZCAlcCBuZXh0ICVwIHNpemUg
+MHgleCBhbGlnbWVudCAweCV4IHAgJXAKAAAAAAAAAAAAAABtYWxsb2NfaW5jWyV1XTogc3RhcnQg
+JXAgZW5kICVwIG5leHQgJXAgc2l6ZSAweCV4IGFsaWdtZW50IDB4JXggcCAlcAoAAAAAAAAAAAAA
+AGxlIGNvbmZpZ3VyYXRpb246IGhhc2ggbW9kZSByZXF1aXJlcyBhdCBsZWFzdCAxNiBlbnRyaWVz
+LCBuaGFzaCAldQoAAAAAAAAAAAAAAAAAbGUgY29uZmlndXJhdGlvbjogaGFzaCBtb2RlIHJlcXVp
+cmVzIGF0IGVudHJpZXMgdG8gYmUgYSBwb3dlciBvZiAyLCBuaGFzaCAldQoAAABsZSBjb25maWd1
+cmF0aW9uOiByZXF1ZXN0ZWQgJXUgdGNhbSBlbnRyaWVzIGJ1dCBvbmx5ICV1IGF2YWlsYWJsZQoA
+AAAAAAAAAAAAAAAAAGxlIGNvbmZpZ3VyYXRpb246IHRjYW0gcmVnaW9ucyBtdXN0IGhhdmUgbXVs
+dGlwbGUgb2YgMzIgZW50cmllcywgbnJvdXRlICV1IG5jbGlwICV1IG5maWx0ZXIgJXUgbnNlcnZl
+ciAldQoAAAAAAABvZmxkIHRwIHRpbWVyIHNldHRpbmdzIFRQX01TTCAweCUwOHggVFBfUlhUX01J
+TiAweCUwOHggVFBfUlhUX01BWCAweCUwOHggVFBfUEVSU19NSU4gMHglMDh4IFRQX1BFUlNfTUFY
+IDB4JTA4eAoAICAgICAgICAgICAgICAgICAgICAgICBUUF9LRUVQX0lETEUgMHglMDh4IFRQX0tF
+RVBfSU5UVkwgMHglMDh4IFRQX0lOSVRfU1JUVC5pbml0X3NydHRfbWF4cnR0IDB4JTA0eCBUUF9J
+TklUX1NSVFQuaW5pdF9zcnR0X2luaXRzcnR0IDB4JTA0eCBUUF9GSU5XQUlUMl9USU1FUiAweCUw
+OHgKAAAAAAAAAAAAAABjb25maWd1cmF0aW9uIGZpbGUgcGFyc2VyIGVuY291bnRlcmVkIGVycm9y
+IEAgbGluZSAldToKAAAAAAAAAAAAaHdfaTJjX3RyYW5zYWN0aW9uOiBuZGF0YSAldSBhZGRyX29w
+IDB4JXggZGF0YVswXSAweCV4IGRpZmYgJXUKAGh3X2kyY190cmFuc2FjdGlvbjogbmRhdGEgJXUg
+YWRkcl9vcCAweCV4IGRhdGFbMF0gMHgleCBkaWZmICV1IGRwb3MgJXUgY29udCAldSBmYWlsZWQg
+d2l0aCBlcnIgJWQKAAAAAAAAAAAAAAAAAABpMmMgdHJhbnNhY3Rpb24gZmFpbGVkIHRvIGNvbXBs
+ZXRlCgAAAAAAAAAAAAAAAABIT1NUIFBBR0VfU0laRSBbMHglMGx4XSB0b28gc21hbGwsIG1pbiBb
+MHglMGx4XSByZXF1aXJlZAoAAAAAAAAAcGFnZSBzaXplIFslbHVdIG1pc21hdGNoCgAAAAAAAABQ
+QUdFIHNpemUgJWx1IHVuc3VwcG9ydGVkLCBkZHAgZGlzYWJsZWQKAAAAAAAAAABIb3N0IHBhZ2Vf
+c2l6ZSAlbHUsIGRkcF9pZHggJXUKAEZDb0UgRERQIGluaXQ6IGZjb2UgbGxpbWl0IDB4JXgsIGZj
+b2UgdWxpbWl0IDB4JXggZ2JsIGxsaW1pdCAweCV4IGdibCB1bGltaXQgMHgleAoAAAAAAAAAAAAA
+AAAAAEZDb0UgRERQIGluaXQ6IGZjb2UgcHBvZCBvZmYgMHgleCwgZmNvZSBzdCBwcG9kIGFkZHIg
+MHgleCBmY29lIG51bSBwcG9kcyAweCV4CgAAZmNvZSB4Y2hnIG1nciBpbml0OiBOdW1iZXIgb2Yg
+ZXhjaGFuZ2VzIGZvciBGQ29FIGlzICV4CgAAAAAAAAAAAFFTRlAgbW9kdWxlIHVucGx1ZyAtIHJl
+aW5pdGlhbGl6aW5nIHJ4X2xvcyAgdG8gMHhmZgoAAAAAAAAAAAAAAABncGlvX3FzZnBfbW9kdWxl
+X3VwZGF0ZTogY2hhbmdlZCByeF9sb3MgZnJvbSAweCV4IHRvIDB4JXgKAAAAAAAAZ3Bpb19xc2Zw
+X21vZHVsZV91cGRhdGU6IGNoYW5nZWQgdHhfZGlzIGZyb20gMHgleCB0byAweCV4CgAAAAAAAENh
+bGN1bGF0aW9uIG91dCBvZiBib3VuZHMgZnVyaW5nIGluaXQ6ICUjeCAlI3ggJSN4CgAAAAAAAAAA
+AAAAAABxdWV1ZXNfcGVyX3BhZ2U6IHBmICV1IGhhcyBhIGJhcnNpemUgb2YgJXUtYnl0ZXMsIG9j
+cV9zaXplICV1CgAAaHdfc2dlX21hbWVtX2NhbGM6IGVuY291bnRlcmVkIGVycm9yICVkCgAAAAAA
+AAAAc2dlIHJlcXVpcmUgbmVxICV1IG5pcSAldSByb3VuZGluZyB0byAldSAldQoAAAAAaHdfc2dl
+X21hbWVtX2luaXQ6IGVuY291bnRlcmVkIGVycm9yICVkCgAAAAAAAAAAaHdfZWRjX2Jpc3RbJXVd
+OiBiaXN0X2NtZFsweCUwOHhdIGFkZHIgMHgleCBsZW4gMHgleAoAAAAAAAAAAAAAAGh3X2VkY19i
+aXN0WyV1XTogZG9uZSwgZW5jb3VudGVyZWQgJXUgZXJyb3JzIG9uIGZpcnN0IGFuZCAldSBlcnJv
+cnMgb24gc2Vjb25kIGF0dGVtcHQKAAAAAAAAAAAAAGZvaXNjc2lfYnllOmwyZGV2X2ZjLT5mbG93
+Y19pZCBbMHgleF0sIGwyZGV2X2ZjLT5mbG93Y19wY2llX3BmbiBbMHgleF0sIGwyZGV2X2ZjLT5m
+bG93Y19wY2llX3ZmbiBbMHgleF0sIHBvcnQgWzB4JXhdCgAAAAAAAAAAAAAAZm9pc2NzaV9ieWU6
+dmxhbmRldl9mYy0+Zmxvd2NfaWQgWzB4JXhdLCB2bGFuZGV2X2ZjLT5mbG93Y19wY2llX3BmbiBb
+MHgleF0sIHZsYW5kZXZfZmMtPmZsb3djX3BjaWVfdmZuIFsweCV4XSwgcG9ydCBbMHgleF0KAAAA
+AABod194Z21fbGVkX2xpbmtfdXAgbGVkMCBjb25maWc6ICV1CgAAAAAAAAAAAAAAAABod194Z21f
+bGVkX2xpbmtfdXAgbGVkMSBjb25maWc6ICV1CgAAAAAAAAAAAAAAAABoc3Nfc2lnZGV0WyV1XTog
+aHNzX3NpZ2RldCBjaGFuZ2VkIHRvIDB4JXgKAAAAAABwZm4gJXUgdmZuICV1IGhhcyBwbmR0eG5z
+ICV1IGFmdGVyIDEwMG1zCgAAAAAAAABiYWQgbWFpbGJveCBjbWQ6IHBmbiAweCV4IHZmbiAweCV4
+OyBvcGNvZGUgMHgleCA+IExBU1RDMkUgMHgleAoAbWFpbGJveCBjbWQgbm90IHlldCBzdXBwb3J0
+ZWQ6IHBmbiAweCV4IHZmbiAweCV4OyBvcGNvZGUgMHgleAoAAGJhZCBtYWlsYm94IGNtZDogcGZu
+IDB4JXggdmZuIDB4JXg7IG9wY29kZSAweCV4IGlzIHZhbGlkIHBvc3QgZGV2aWNlIGluaXQgb25s
+eQoAYmFkIG1haWxib3ggY21kOiBwZm4gMHgleCB2Zm4gMHgleDsgb3Bjb2RlIDB4JTAyeCByYW1h
+c2sgMHgleCBjbWQgcmFtYXNrIDB4JXgKAABiYWQgbWFpbGJveCBjbWQ6IHBmbiAweCV4IHZmbiAw
+eCV4OyBvcGNvZGUgMHglMDJ4IGxlbjE2IDB4JXggdmVyc3VzIGV4cGVjdGVkIGxlbjE2IDB4JXgK
+AAAAAAAAAABpbnN1ZmZpY2llbnQgY2FwcyB0byBwcm9jZXNzIG1haWxib3ggY21kOiBwZm4gMHgl
+eCB2Zm4gMHgleDsgcl9jYXBzIDB4JXggd3hfY2FwcyAweCV4IHJlcXVpcmVkIHJfY2FwcyAweCV4
+IHdfY2FwcyAweCV4CgAAAAAAAAAAAGluc3VmZmljaWVudCBjYXBzIHRvIHByb2Nlc3MgbWFpbGJv
+eCBjbWQ6IHBmbiAweCV4IHZmbiAweCV4OyByX2NhcHMgMHgleCB3eF9jYXBzIDB4JXggcmVxdWly
+ZWQgcl9jYXBzIDB4JXggd19jYXBzIDB4JXgKAAAAAAAAAAAAVlBEIHJlZ2lvbiBpcyB0b28gc21h
+bGwgKFNFUkNGR19TUl9QRk5WUERTSVpFIDB4JXgpCgAAAAAAAAAAAAAAAGNmOiBmYWlsZWQgdG8g
+YWxsb2NhdGVkIG1lbW9yeSBmb3IgY29uZmlndXJhdGlvbiBmaWxlLCByZXQgJWQKAAAAAAAAAAAA
+AAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAA
+AAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAA
+AAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAA
+AAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAA
+AAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAA
+AAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAA
+AAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAA
+AAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAA
+AAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAA
+AAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAA
+AAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAA
+AAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAA
+AAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAA
+AAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAA
+AAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAA
+AAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAA
+AAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAA
+AAAAAAAAAACCAAABIAAAAAAAAACCAAABAAAAAAAAAACAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAA
+AAAAAAAAAAAGgIAAAAAAAAAAAAAAAACAAAAAAAAAAAACgAAAAAAAAAAAAAIAAwAAAAABYAgAAAAA
+AwAAAAAAAAAAAAAAAwAAAAAAAAAAAAAAAgAAAAAAAAAAACAAAAAAAAAAAAAAAAEAA4AAAAAAAAAA
+AAAAAgAAAAAAAAAAACADgAAAAAAAAAAAABACgACAAAAAAAAAAAAAAAAAAAAAAAAAACAAAAAAAAAA
+AAAAAAAAAoAAAAAAAAAAAAAAAgAAAAAAAAAAAAAAAoAAAAAAAAAAAAEAAwAAAAAAAAAAAAACgwAA
+AAAAAAAAABACgAAAAAAAAAAAADAAAwAAAAAAAAgAADAAAwAAAAAAAAAAADAFgwAAAAAAAAgAADAF
+gwAAAAAAAAAAADAEgwAAAAAAAAgAADAEgwAAAAAAAAAAADADAwAAAAAAAAgAADADAwAAAAAAAAAA
+ADgDAwAAAAAAAAAAADgFgwAAAAAAAAAAADgEgwAAAAAAAAAAADgAAwAAAAAAAAAAADQGggAAAAAA
+AAAAADwDggAAAAAAAAAAADwAAwAAAAAAAAgAADwAAwAAAAAAAAAAADwEgwAAAAAAAAAAADwFAwAA
+AAAAAAAAAD0EAwAAAAAAAAAAADwDgwAAAAAAAAAAACwAAgAAAAAAAAAAACwFggAAAAAAAAAAACwF
+AgAAAAAAAAAAABAGgAAAAAAAAAAAABAGgsAAAAAAAAAAABAGgoAAAAAAAAAAAAAOggAAAAAAAAAA
+ABAHgoAAAAAgAAAAAAAHggAAAAAgAAAAABAHAoAAAAAAAAAAABAHAoAAAAAAAAAAABAHAoAAAAAA
+AAAAAAAHAgAAAAAgAAAAABAXgwAAAAAAAAgAABAXgwAAAAAAAAgAABAAAAAAAAAAAAAAABAGA4AA
+AAAAAAAAAAAOAwAAAAAAAAAAABAGA0AAAAAAAAAAABAGAwAAAAAAAAAAABAGAAAAAAAAAAAAAAAG
+A4AAAAAAAAAAAAAGAwAAAAAAAAAAAAAOAgAAAAAAAAAAAAAOAgAAAAAAAAAAABAGAgAAAAAAAAAA
+ABAGAgAAAAAAAAAAABAGAoAAAAAAAAAAABAGAoAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAA
+AAAAAAAAAAIAAAAAAAAAAAAQAwAAAAAAAAgAAAAAAAAAAAAAAAAAAP//////////////////////
+////////////////////////////////////////////////////////////////////////////
+////////////////////////////////////////////////////////////////////////////
+////////////////////////////////////////////////////////////////////////////
+////////////////////////////////////////////////////////////////////////////
+////////////////////////////////////////////////////////////////////////////
+////////////////////////////////////////////////////////////////////////////
+////////////////////////////////////////////////////////////////////////////
+////////////////////////////////////////////////////////////////////////////
+////////////////////////////////////////////////////////////////////////////
+////////////////////////////////////////////////////////////////////////////
+/wAAACAAAAAAwAAAAAAAACAAAAAA4AAAAAAAAAACAAAAAAAAAEAAAAAAAAAAAAAAAAAAASEAAAAA
+AAAAAAABASAAAAAAAAAAAAAAAgAAAAQABAAAAAAFAAAABAAAAAAAAAAAAKAAAAAAgAAAAACAAEAA
+AAAAAAIAAACAACAAAAAAAAIAAAEAQAAAAAAAAAAAAAEAQgAAAAAAAAAAAAAAIAAAAAAAAAAAAAIQ
+IAAAAAAAAAAAAAIMAgAAAAAAAAAAAACFAgAAAAQAAAAAAACAQgAAAAAAAAAAAAIAIgAAAAAAAAAA
+AACAQQAAAAAAAAAAAACAQYAAAAAAAAAAAAIAIQAAAAAAAAAAAAAQIIAAAAAAAAAAAAIlAIAAAAAA
+AAAAAAAFAAAAAAAAAAAAAAiIBIAAAAAAAAAAAAiIBIAAAAAAAAAAAAiiAIAAAAAAAAAAAAiiAIAA
+AAAAAAAAAAijAIAAAAAAAAAAAAijAIAAAAAAAAAAAAikgIAAAAAAAAAAAAikgIAAAAAAAAAAAASk
+gMAAAAAAAAAAAASiAMAAAAAAAAAAAASjAMAAAAAAAAAAAASIBMAAAAAAAAAAAAAJAYAAAAAAAAAA
+AAIMAIAAAAAAAAAAAACIBMAAAAAAAAAAAACIBIAAAAAAAAAAAAILAIAAAAAAAAAAAACKgIAAAAAA
+AAAAAAALgIAAAAAAAAAAAACMAIAAAAAAAAAAAAIgEIAAAAAAAAAAAAIKAIAAAAAAAAAAAAIKgIAA
+AAAAAAAAAAAJAoAAAAAAAAAAAAABAQAAAAAAAAAAAAABAUAAAAAAAAAAAAABAIAAAAAAAAAAAAAA
+AAAAAAAAAAAAAAAAAIAAAAAAAAAAAAAAgRAAAAAAAAAAAAAAgQgAAAAAAAAAAAAAgQQAAAAAAAAA
+AAAAgYAAAAAAAAAAAACAAMAAAAAAAAAAAACAAKAAAAAAAAAAAAAACAAAAAAAAAAAAACBgAAAAAAA
+AAAAAACBgIAAAAAAAAAAAACJgIAAAAAAAAAAAACJgMAAAAAAAAAAAAABggAAAAAAAAAAAAIBgAAA
+AAAAAAAAAAIBgIAAAAAAAAAAAABBgYAAAAAAAAAAAAIBgYAAAAAAAAAAAABJgYAAAAAAAAAAAAIJ
+gYAAAAAAAAAAAAIBgQAAAAAAAAAAAABBgQAAAAAAAAAAACAAAAAAAAAAAAAAABAAAAIAAAAAAAAA
+ABAAAAAAAAAAAAAAAACAAMAAAAAAAAAAAAAAAAAAAAAAAAAAAP//////////////////////////
+////////////////////////////////////////////////////////////////////////////
+////////////////////////////////////////////////////////////////////////////
+////////////////////////////////////////////////////////////////////////////
+////////////////////////////////////////////////////////////////////////////
+////////////////////////////////////////////////////////////////////////////
+////////////////////////////////////////////////////////////////////////////
+////////////////////////////////////////////////////////////////////////////
+////////////////////////////////////////////////////////////////////////////
+////////////////////////////////////////////////////////////////////////////
+/////////////////////////////////////////////////////////////////////////wAA
+AAAAAAAAAAAAAAAAAAAAAAAAAAAACACSAAAAAAAAAANmACAAQAAAAAAIAJIAAAAAAAAAA3IBJQRA
+AAAAAAAAAAAAAAAAAAADlgAgAEAAAAAACAAAAAIAiAOHAABWACAAQAAAAAAAAAAAAAAAAAAAA5YA
+IABAAAAAAAAAAAAAAAAAAAADlgAgAEAAAAAAAAAAAAAAAAAAAAOWACAAQAAAAAAAAAAAAAAAAAAA
+A5YAIABAAAAAAAgAAAACAIgDhQAAlgEgAEAAAAAAAAAAAAAAAAAAAAOWACAAQAAAAAAAAAAAAAAA
+AAAAA5YAIABAAAAAAAgAAAACAIgDhQAAlgEgAEAAAAAAAAAAAAAAAAAAAAOWACAAQAAAAAAAAAAA
+AAAAAAAAA5YAIABAAAAAAAgAAAACAIgDhQAAlgEgAEAAAAAAAAAAAAAAAAAAAAOWACAAQAAAAAAA
+AAAAAAAAAAIAA5YAJgRAAAAAAAhHIAEEAAACsgAC0gUgJEAAAAAAAAAAAAAAAAAAAAOWACAAQAAA
+AAAARyABBgyhgrBAAZIEICRAAAAAAABHIAEEDKBCs4ACEgUgJEAAAAAACAAAAAIAiAOFAACWASAA
+QAAAAAAIAAAAAgCIA4UAAJYBIABAAAAAAAgAAAACAIgDhQAAlgEgAEAAAAAAAAAAAAAAAAAAAAOW
+ACAAQAAAAAANxRK6ArCA0xRUi+JKRiRAAAAAAAXFEroCt4DTFFSLYkmmJEAAAAAABcUSogawgNMU
+VIviSkYkQAAAAAANxRKiBreA0xRUi2JJpiRAAAAAAAXFEqIGsIDTFFSL4kpGJEAAAAAADcUSoga3
+gNMUVItiSaYkQAAAAAAFxRKiBrCA0xRUi+JKRiRAAAAAAA3FEqIGt4DTFFSLYkmmJEAAAAAACcQQ
+oAIAgJAAAItiacYkQAAAAAABxBCgBrCAkwZUi2JpxiRAAAAAAAHEEKAGsICTBlSLYmnGJEAAAAAA
+AcUQuAawgJMWVItiSMYkQAAAAAAIoRCIAgCBWBIAC1IApiRAAAAAAAnAEJACsIADFlSKkgHGJEAA
+AAAACcAQuAawgAMQlIviAkYkQAAAAAAJwBC4ArSAAxCUi2IBpiRAAAAAAAnAELgCtIADEJSLYgGm
+JEAAAAAACcAQuAK0gAMQlItiAaYkQAAAAAAJwBC4ArSAAxCUi2IBpiRAAAAAAAGgEJAGtIADEJSL
+YgGmJEAAAAAAAcAQgAKwgAMUVIpSAMYkQAAAAAABwBCAArCAAxRUilIAxiRAAAAAAAHAEIACsIAD
+FFSKUgDGJEAAAAAACEcgAQQAAAKyAALSBSAkQAAAAAAAgQAAAgCFWAdAC1IApiRAAAAAAACBAAAC
+AIVYB0ALUgCmJEAAAAAAAAAAAAQAoEABgAHWACAAQAAAAAAAAAAABgChgABAAVYAIABAAAAAAAAA
+AAAEAKBAAYAB1gAgAEAAAAAACIEAAAIAhVmABAtSAKYkQAAAAAAJYUAAAAAAGAAAA0IBJqRAAAAA
+AAhAAAAAAAAAAAADUgEmJEAAAAAAAAAAAAQAoEABgAHWACAAQAAAAAAIAAAAAgCIA4cAA9YAJgRA
+AAAAAAgAAAACAIgDhwAD1gAmBEAAAAAAAAAAAAAAAAAAAAOWACAAQAAAAAAAAAAAAAAAAAAAA5YA
+IABAAAAAAAAAAAAEAKBAAYAB1gAgAEAAAAAACcAQuAK0gAMQlItiAaYkQAAAAAAAAAAAAAAAAAIA
+A5YAJgRAAAAAAAAAAAAAAAAAAAADlgAgAEAAAAAACAAAAAIAiAOFAACWASAAQAAAAAAAAAAABACg
+QAGAAdYAIABAAAAAAAAAAAAEAKBAAYAB1gAgAEAAAAAAAAAAAAQAoEABgAHWACAAQAAAAAAIoRCI
+AgCBWBAACRIBpiRAAAAAAAAAAAAEAKBAAYAB1gAgAEAAAAAAAAAAAAYAoYAAQAFWACAAQAAAAAAI
+AAAAAgCIA4VACNICRiRAAAAAAAnFEqIClIjSEICLIkimJEAAAAAACcAQgAKkiACHVItSAaYkQAAA
+AAAJwBCABrSIAwEUi1IBpiRAAAAAAA3AAAACsIDDFlSL4gNGJEAAAAAAAAAAAAAAAAAAAAOWACAA
+QAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAA
+AAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAA
+AAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAA
+AAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAA
+AAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAA
+AAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAA
+AAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAA
+AAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAA
+AAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAA
+AAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAA
+AAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAA
+AAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAA
+AAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAA
+AAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAA
+AAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAA
+AAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAA
+AAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAA
+AAAAAAAAAAAAAAAAAAAAAAgAMAAEAAAAAgAAAgAmJEAAAAAAAEcwAAYDIAK3AAgCAcAkQAAAAAAI
+AwAAAgagCrUACAIAxiRAAAAAAAAAAAAAAAAAAgAAAgAmJEAAAAAAACBQAAQAAAACAAACACYkQAAA
+AAAIYCAAhAAAAAAAAAYAIABAAAAAAAhgIACEAAAAAgEwAgAmJUAAAAAACGAAAAQAhAGABAQCAcYk
+wAAAAAABwAAAAkOAAwIMiAIBpiRAAAAAAAgAYAAEAAAAAgAAAgAmJEAAAAAACABgAAQAAAAAAAAE
+ASAAQAAAAAAAAAAAAAAAAAAAAAQBIABAAAAAAAQIFIAGCgAABwFMAiCmJkAAAAAACIAAAAYAhAGA
+BAgCAaYmQAAAAAAAQAAAAgCgAAJACAIBpiRAAAAAAAAAAAAAAAAAAgAAAgAmJEAAAAAABAAAAAKE
+AAMCiggCBKYkQAAAAAAAAAAAAAAAAAIAAAYBIEhAAAAAAAAgUAAEAAAAAgAAAgAmJEAAAAAACGAg
+AIQAAAACAAAGACYEQAAAAAAIYCAAhAAAAAIAAAIBJiVAAAAAAAhgAAAEAIQBgAQEAgHGJMAAAAAA
+CABgAAQAAAAAAAAEASAAQAAAAAAAAAAAAAAAAAAAAAIAJkxwAAAAAAAAAAAAAAAAAAAABgEgAAAA
+AAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAA
+AAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAA
+AAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAABwAAACLSQBAkWBAIExiRAAAAAAAHAAAAItJAE
+CRYEAgTGJEAAAAAABAh0gEwJAABQAFwCYeYkQAAAAAAMCHSARAAAAFIAQAJhJiRAAAAAAAgCEKAE
+AAAAAAEAAgAmJEAAAAAACAIQoAQAAAAAAQACACYkQAAAAAAECHQAQgEAAAcAiAJgxiRAAAAAAA3I
+FAACCQAABECcAmDmJEAAAAAACcgQgAa0kAQClIgCZcYkQAAAAAANyHQASLSQAwCUiAJgpiTAAAAA
+AA3IdABItJADAJSIAmCmJMAAAAAACEcAAAQAAAAAAAACASAkQAAAAAAIRwAABAAAAAIAAAIFICRA
+AAAAAABHIAEMByHCtwAIAgHAJEAAAAAAAEcgAQwHIcK3AAgCAcAkQAAAAAAARyABDAchwrcACAIB
+wCRAAAAAAAAAIAEIAIQABUCIAgHGJMAAAAAAAAAgAQgAhAAFQIgCAcYkwAAAAAAAACABCACEAAVA
+iAIBxiTAAAAAAAAAIAGGggABAsCIAgPGJMAAAAAAAAAgAYKCAAACwIgCA8YkwAAAAAAJwCABgqSA
+AQVAiAIBxiTAAAAAAAgAAAAMAIQABUCIAgHGJMAAAAAAAAAgAYaCAAECwIgCA8YkwAAAAAAAACAB
+goIAAADAiAICpiTAAAAAAAnAIAGCpIABBUCIAgHGJMAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAA
+AAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAA
+AAAAAAAAAAAAAAIAAAABAAEJAQAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAA
+AAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAA
+AAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAA
+AAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAA
+AAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAA
+AAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAA
+AAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAA
+AAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAA
+AAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAA
+AAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAA
+AAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAA
+AAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAA
+AAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAA
+AAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAFtnbG9iYWxdCnJz
+c19nbGJfY29uZmlnX21vZGU9YmFzaWN2aXJ0dWFsCnJzc19nbGJfY29uZmlnX29wdGlvbnM9dG5s
+bWFwZW4saGFzaHRvZXBsaXR6LHRubGFsbGxrcApyZWdbMHgxMDA4XT0weDQwODEwLzB4MjFjNzAK
+cmVnWzB4MTAwY109MHgyMjIyMjIyMgpyZWdbMHgxMGEwXT0weDAxMDQwODEwCnJlZ1sweDEwNDRd
+PTQwOTYKcmVnWzB4MTA0OF09NjU1MzYKcmVnWzB4MTA0Y109MTUzNgpyZWdbMHgxMDUwXT05MDI0
+CnJlZ1sweDEwNTRdPTkyMTYKcmVnWzB4MTA1OF09MjA0OApyZWdbMHgxMDVjXT0xMjgKcmVnWzB4
+MTA2MF09ODE5MgpyZWdbMHgxMDY0XT0xNjM4NApyZWdbMHgxMGE0XT0weGEwMDBhMDAwLzB4ZjAw
+MGYwMDAKcmVnWzB4MTBhOF09MHgyMDAwLzB4MjAwMApzZ2VfdGltZXJfdmFsdWU9NSwxMCwyMCw1
+MCwxMDAsMjAwCnJlZ1sweDdkYzBdPTB4NjJmODg0OQpmaWx0ZXJNb2RlPWZyYWdtZW50YXRpb24s
+bXBzaGl0dHlwZSxwcm90b2NvbCx2bGFuLHBvcnQsZmNvZQp0cF9wbXJ4PTMwCnRwX3BtcnhfcGFn
+ZXNpemU9NjRLCnRwX25yeGNoPTAKdHBfcG10eD01MAp0cF9wbXR4X3BhZ2VzaXplPTY0Swp0cF9u
+dHhjaD0wCltmdW5jdGlvbiIwIl0KbnZmPTE2Cnd4X2NhcHM9YWxsCnJfY2Fwcz1hbGwKbnZpPTEK
+bmlxZmxpbnQ9OApuZXRoY3RybD04Cm5lcT0xNgpuZXhhY3RmPTgKY21hc2s9YWxsCnBtYXNrPTB4
+MQpbZnVuY3Rpb24iMSJdCm52Zj0xNgp3eF9jYXBzPWFsbApyX2NhcHM9YWxsCm52aT0xCm5pcWZs
+aW50PTgKbmV0aGN0cmw9OApuZXE9MTYKbmV4YWN0Zj04CmNtYXNrPWFsbApwbWFzaz0weDIKW2Z1
+bmN0aW9uIjIiXQpudmY9MTYKd3hfY2Fwcz1hbGwKcl9jYXBzPWFsbApudmk9MQpuaXFmbGludD04
+Cm5ldGhjdHJsPTgKbmVxPTE2Cm5leGFjdGY9OApjbWFzaz1hbGwKcG1hc2s9MHg0CltmdW5jdGlv
+biIzIl0KbnZmPTE2Cnd4X2NhcHM9YWxsCnJfY2Fwcz1hbGwKbnZpPTEKbmlxZmxpbnQ9OApuZXRo
+Y3RybD04Cm5lcT0xNgpuZXhhY3RmPTgKY21hc2s9YWxsCnBtYXNrPTB4OApbZnVuY3Rpb24iNCJd
+Cnd4X2NhcHM9YWxsCnJfY2Fwcz1hbGwKbnZpPTI4Cm5pcWZsaW50PTE3MApuZXRoY3RybD0xMDAK
+bmVxPTI1NgpuZXhhY3RmPTQwCmNtYXNrPWFsbApwbWFzaz1hbGwKbmV0aG9mbGQ9MTAyNApucm91
+dGU9MzIKbmNsaXA9MzIKbmZpbHRlcj00OTYKbnNlcnZlcj00OTYKbmhhc2g9MTIyODgKcHJvdG9j
+b2w9bmljX3ZtLG9mbGQscmRkcCxyZG1hYyxpc2NzaV9pbml0aWF0b3JfcGR1LGlzY3NpX3Rhcmdl
+dF9wZHUKdHBfbDJ0PTMwNzIKdHBfZGRwPTIKdHBfZGRwX2lzY3NpPTIKdHBfc3RhZz0yCnRwX3Bi
+bD01CnRwX3JxPTcKW2Z1bmN0aW9uIjUiXQp3eF9jYXBzPWFsbApyX2NhcHM9YWxsCm52aT00Cm5p
+cWZsaW50PTM0Cm5ldGhjdHJsPTMyCm5lcT02NApuZXhhY3RmPTQKY21hc2s9YWxsCnBtYXNrPWFs
+bApuc2VydmVyPTE2Cm5oYXNoPTIwNDgKdHBfbDJ0PTEwMjQKcHJvdG9jb2w9aXNjc2lfaW5pdGlh
+dG9yX2ZvZmxkCnRwX2RkcF9pc2NzaT0yCmlzY3NpX250YXNrPTIwNDgKaXNjc2lfbnNlc3M9MjA0
+OAppc2NzaV9uY29ubl9wZXJfc2Vzc2lvbj0xCmlzY3NpX25pbml0aWF0b3JfaW5zdGFuY2U9NjQK
+W2Z1bmN0aW9uIjYiXQp3eF9jYXBzPWFsbApyX2NhcHM9YWxsCm52aT00Cm5pcWZsaW50PTM0Cm5l
+dGhjdHJsPTMyCm5lcT02NgpuZXhhY3RmPTMyCmNtYXNrPWFsbApwbWFzaz1hbGwKbmhhc2g9MjA0
+OApwcm90b2NvbD1mY29lX2luaXRpYXRvcgp0cF9kZHA9MgpmY29lX25mY2Y9MTYKZmNvZV9udm5w
+PTMyCmZjb2VfbnNzbj0xMDI0CltmdW5jdGlvbiIxMDIzIl0Kd3hfY2Fwcz1hbGwKcl9jYXBzPWFs
+bApudmk9NApjbWFzaz1hbGwKcG1hc2s9YWxsCm5leGFjdGY9OApuZmlsdGVyPTE2CltmdW5jdGlv
+biIwLyoiXQp3eF9jYXBzPTB4ODIKcl9jYXBzPTB4ODYKbnZpPTEKbmlxZmxpbnQ9NApuZXRoY3Ry
+bD0yCm5lcT00Cm5leGFjdGY9NApjbWFzaz1hbGwKcG1hc2s9MHgxCltmdW5jdGlvbiIxLyoiXQp3
+eF9jYXBzPTB4ODIKcl9jYXBzPTB4ODYKbnZpPTEKbmlxZmxpbnQ9NApuZXRoY3RybD0yCm5lcT00
+Cm5leGFjdGY9NApjbWFzaz1hbGwKcG1hc2s9MHgyCltmdW5jdGlvbiIyLyoiXQp3eF9jYXBzPTB4
+ODIKcl9jYXBzPTB4ODYKbnZpPTEKbmlxZmxpbnQ9NApuZXRoY3RybD0yCm5lcT00Cm5leGFjdGY9
+NApjbWFzaz1hbGwKcG1hc2s9MHg0CltmdW5jdGlvbiIzLyoiXQp3eF9jYXBzPTB4ODIKcl9jYXBz
+PTB4ODYKbnZpPTEKbmlxZmxpbnQ9NApuZXRoY3RybD0yCm5lcT00Cm5leGFjdGY9NApjbWFzaz1h
+bGwKcG1hc2s9MHg4Cltwb3J0IjAiXQpkY2I9cHBwLGRjYngKYmdfbWVtPTI1CmxwYmtfbWVtPTI1
+Cmh3bT0zMApsd209MTUKZHdtPTMwCltwb3J0IjEiXQpkY2I9cHBwLGRjYngKYmdfbWVtPTI1Cmxw
+YmtfbWVtPTI1Cmh3bT0zMApsd209MTUKZHdtPTMwCltwb3J0IjIiXQpkY2I9cHBwLGRjYngKYmdf
+bWVtPTI1CmxwYmtfbWVtPTI1Cmh3bT0zMApsd209MTUKZHdtPTMwCltwb3J0IjMiXQpkY2I9cHBw
+LGRjYngKYmdfbWVtPTI1CmxwYmtfbWVtPTI1Cmh3bT0zMApsd209MTUKZHdtPTMwCltmaW5pXQp2
+ZXJzaW9uPTB4MTQyNTAwMGQKY2hlY2tzdW09MHgyNWMyZjc4MgoAAAAAAAAAAAAAAAAAAAAAAAAA
+AAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAA
+AAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAA
+AAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAA
+AAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAA
+AAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAA
+AAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAA
+AAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAA
+AAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAA
+AAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAA
+AAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAA
+AAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAA
+AAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAA
+AAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAA
+AAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAA
+AAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAA
+AAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAA
+AAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAW2dsb2JhbF0KcnNzX2dsYl9j
+b25maWdfbW9kZT1iYXNpY3ZpcnR1YWwKcnNzX2dsYl9jb25maWdfb3B0aW9ucz10bmxtYXBlbixo
+YXNodG9lcGxpdHosdG5sYWxsbGtwCnJlZ1sweDEwMDhdPTB4NDA4MTAvMHgyMWM3MApyZWdbMHgx
+MDBjXT0weDIyMjIyMjIyCnJlZ1sweDEwYTBdPTB4MDEwNDA4MTAKcmVnWzB4MTA0NF09NDA5Ngpy
+ZWdbMHgxMDQ4XT02NTUzNgpyZWdbMHgxMDRjXT0xNTM2CnJlZ1sweDEwNTBdPTkwMjQKcmVnWzB4
+MTA1NF09OTIxNgpyZWdbMHgxMDU4XT0yMDQ4CnJlZ1sweDEwNWNdPTEyOApyZWdbMHgxMDYwXT04
+MTkyCnJlZ1sweDEwNjRdPTE2Mzg0CnJlZ1sweDEwYTRdPTB4YTAwMGEwMDAvMHhmMDAwZjAwMApy
+ZWdbMHgxMGE4XT0weDIwMDAvMHgyMDAwCnNnZV90aW1lcl92YWx1ZT01LDEwLDIwLDUwLDEwMCwy
+MDAKcmVnWzB4N2RjMF09MHg2MmY4ODQ5CmZpbHRlck1vZGU9ZnJhZ21lbnRhdGlvbixtcHNoaXR0
+eXBlLHByb3RvY29sLHZsYW4scG9ydCxmY29lCnRwX3Btcng9MzAKdHBfcG1yeF9wYWdlc2l6ZT02
+NEsKdHBfcG10eD01MAp0cF9wbXR4X3BhZ2VzaXplPTY0SwpbZnVuY3Rpb24iMCJdCnd4X2NhcHM9
+YWxsCnJfY2Fwcz1hbGwKbnZpPTI4Cm5pcWZsaW50PTE3MApuZXRoY3RybD05NgpuZXE9MjUyCm5l
+eGFjdGY9NDAKY21hc2s9YWxsCnBtYXNrPWFsbApucm91dGU9MzIKbmNsaXA9MzIKbmZpbHRlcj00
+OApuc2VydmVyPTMyCm5oYXNoPTAKcHJvdG9jb2w9bmljX3ZtLG9mbGQscmRkcCxyZG1hYyxpc2Nz
+aV9pbml0aWF0b3JfcGR1LGlzY3NpX3RhcmdldF9wZHUKdHBfbDJ0PTMwNzIKdHBfZGRwPTIKdHBf
+ZGRwX2lzY3NpPTIKdHBfc3RhZz0yCnRwX3BibD01CnRwX3JxPTcKW2Z1bmN0aW9uIjEiXQp3eF9j
+YXBzPWFsbApyX2NhcHM9YWxsCm52aT00Cm5pcWZsaW50PTM0Cm5ldGhjdHJsPTMyCm5lcT02Ngpu
+ZXhhY3RmPTMyCmNtYXNrPWFsbApwbWFzaz1hbGwKbmhhc2g9MApwcm90b2NvbD1mY29lX2luaXRp
+YXRvcgp0cF9kZHA9MgpmY29lX25mY2Y9MTYKZmNvZV9udm5wPTMyCmZjb2VfbnNzbj0xMDI0Cltm
+dW5jdGlvbiIxMDIzIl0Kd3hfY2Fwcz1hbGwKcl9jYXBzPWFsbApudmk9NApjbWFzaz1hbGwKcG1h
+c2s9YWxsCm5leGFjdGY9OApuZmlsdGVyPTE2CltmdW5jdGlvbiIwLyoiXQp3eF9jYXBzPTB4ODIK
+cl9jYXBzPTB4ODYKbnZpPTEKbmlxZmxpbnQ9NApuZXRoY3RybD0yCm5lcT00Cm5leGFjdGY9NApj
+bWFzaz1hbGwKcG1hc2s9MHgxCltmdW5jdGlvbiIxLyoiXQp3eF9jYXBzPTB4ODIKcl9jYXBzPTB4
+ODYKbnZpPTEKbmlxZmxpbnQ9NApuZXRoY3RybD0yCm5lcT00Cm5leGFjdGY9NApjbWFzaz1hbGwK
+cG1hc2s9MHgyCltwb3J0IjAiXQpkY2I9cHBwLGRjYngKYmdfbWVtPTI1CmxwYmtfbWVtPTI1Cmh3
+bT0zMApsd209MTUKZHdtPTMwCltwb3J0IjEiXQpkY2I9cHBwLGRjYngKYmdfbWVtPTI1CmxwYmtf
+bWVtPTI1Cmh3bT0zMApsd209MTUKZHdtPTMwCltwb3J0IjIiXQpkY2I9cHBwLGRjYngKYmdfbWVt
+PTI1CmxwYmtfbWVtPTI1Cmh3bT0zMApsd209MTUKZHdtPTMwCltwb3J0IjMiXQpkY2I9cHBwLGRj
+YngKYmdfbWVtPTI1CmxwYmtfbWVtPTI1Cmh3bT0zMApsd209MTUKZHdtPTMwCltmaW5pXQp2ZXJz
+aW9uPTB4MTQyNTAwMGMKY2hlY2tzdW09MHg0YzU2NjYzOQoAAAAAAAAAAAAAAAAAAAAAAAAAAAAA
+AAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAA
+AAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAA
+AAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAA
+AAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAA
+AAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAA
+AAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAA
+AAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAA=
+====
diff --git a/sys/dev/cxgbe/firmware/t4fw-1.8.4.0.bin.uu b/sys/dev/cxgbe/firmware/t4fw-1.8.4.0.bin.uu
deleted file mode 100644
index 129a69f..0000000
--- a/sys/dev/cxgbe/firmware/t4fw-1.8.4.0.bin.uu
+++ /dev/null
@@ -1,8237 +0,0 @@
-/*-
- * Copyright (c) 2013 Chelsio Communications, Inc.
- * All rights reserved.
- *
- * Redistribution and use in source and binary forms, with or without
- * modification, are permitted provided that the following conditions
- * are met:
- * 1. Redistributions of source code must retain the above copyright
- * notice, this list of conditions and the following disclaimer.
- * 2. Redistributions in binary form must reproduce the above copyright
- * notice, this list of conditions and the following disclaimer in the
- * documentation and/or other materials provided with the distribution.
- *
- * THIS SOFTWARE IS PROVIDED BY THE AUTHOR AND CONTRIBUTORS ``AS IS'' AND
- * ANY EXPRESS OR IMPLIED WARRANTIES, INCLUDING, BUT NOT LIMITED TO, THE
- * IMPLIED WARRANTIES OF MERCHANTABILITY AND FITNESS FOR A PARTICULAR PURPOSE
- * ARE DISCLAIMED. IN NO EVENT SHALL THE AUTHOR OR CONTRIBUTORS BE LIABLE
- * FOR ANY DIRECT, INDIRECT, INCIDENTAL, SPECIAL, EXEMPLARY, OR CONSEQUENTIAL
- * DAMAGES (INCLUDING, BUT NOT LIMITED TO, PROCUREMENT OF SUBSTITUTE GOODS
- * OR SERVICES; LOSS OF USE, DATA, OR PROFITS; OR BUSINESS INTERRUPTION)
- * HOWEVER CAUSED AND ON ANY THEORY OF LIABILITY, WHETHER IN CONTRACT, STRICT
- * LIABILITY, OR TORT (INCLUDING NEGLIGENCE OR OTHERWISE) ARISING IN ANY WAY
- * OUT OF THE USE OF THIS SOFTWARE, EVEN IF ADVISED OF THE POSSIBILITY OF
- * SUCH DAMAGE.
- */
-begin-base64 644 t4fw
-AAADkgEIBAAAAQkBAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAABAAAAAAAAAAAAAAAAAAAAAAAAAAAA
-AAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAA
-AAAAAAAAAAAAAAAAAAAAAAAAA3cDhgONAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAA
-AAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAA
-AAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAA
-AAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAA
-AAAAAAAAAAAAAENoZWxzaW8gRlcgUlVOTUVNIERFQlVHPTAgKEJ1aWx0IFRodSBGZWIgIDcgMTU6
-Mzg6MjYgUFNUIDIwMTMgb24gY2xlb3BhdHJhLmFzaWNkZXNpZ25lcnMuY29tOi9ob21lL2Zpcm13
-YXJlL2N2cy9mdy1yZWxlYXNlKSwgVmVyc2lvbiBUNHh4IDAxLjA4LjA0LjAwAAAAAAAAAJ/jNKZg
-AMQA4QAwuHj///8f/OFAgAAAAeEAe3AAABAAH//7gOEAYBDhAZRwIAAAAOEBnAThAHkAAAIAQOEA
-eYAABgBAAAIACgAGAArhAHkEAAoAAIAAAQDhAHs84QB7ROEAe+TiAAAAAAEAAOEAe5AgAAAAAACA
-AOEAewAAAEAB4QB7nAAAQABERERC4AAAAOMABHNERERA4wAIACAAAlwAAAAAH/+NIAAAAAAf/40k
-AAAAAB//jSgAAAAAH/+NLB//wAAAAAAAAAAAAMAAEv/OE//OhCAEMwGTIBH/zRL/zZIQEf/NEv/N
-khAR/80B9DER/8yQEBH/zCIK/5IQAOQxAAUxAQIAEv/JAucxAhYAEf/IgRABAV/AIQIRAckUEf/F
-Ev/FkhAR/8US/8WSEGAAEgAAABH/vxL/w5IQEf+/Ev/CkhCBEBH/wcAgkhES/8CSEsAgkhMS/7+S
-EIIQAvJQZS/3Ef+9xy+SEBH/vJIQEv+8E/+8kyDAMpMhE/+7kyKCIhL/uhP/upMgIyIhFP+5BDMB
-yTgT/7iDMAODFAgzERT/tqQzkyET/6qTImAACMIwkyET/6eTIhL/sZAgkCGQIpAjkCSQJZAmkCeQ
-KJApkCqQK5AskC2QLpAvICYQICYRgiIS/6TAMC03MC03NC03OC03PCM9AXIz7QACABL/oSMKAC83
-AC83EC83IC83MCM9AXIz7QACABL/lsAwKDcwKDc0KDc4KDc8Iz0BcjPtAwIAEv+UwDAnNwAnNxAn
-NyAnNzAjPQFyM+0DAgAS/44V/48W/4/AMNcgBWYBYAAWAAAAAAQ2BQACANMP0w8FMwxuOxQHRxQH
-BEN2MeYENgUFMwxvO+0AAgAS/4IV/4AjCgACJwIHBEMEPgUFMwwHRxRvO/ADAgAS/3zJLoMghCGF
-IrwidDsOhlC0VZYwtDN0M/Rj/+YAZT/iZV/fEv9wwDIDLgUDAgAS/2fAMCg3QCg3RCg3SCg3TCM9
-AXIz7QMCAAACABL/ai0nAMARAUkxAEgxAQIAwAAU/2YE0jEV/2WUUBT/ZQTTMRX/ZZRQFP9kBNQx
-Ff9klFAU/2QE1TEV/2OUUBD/YwMAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAA
-AAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAA
-AAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAA
-AAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAA
-AAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAA
-AAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAA
-AAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAA
-AAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAA
-AAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAA
-AAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAA
-AAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAA
-AAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAA
-AAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAf/AAA
-H/wAAOMACfgf/AAAH/wAAOMACfgf/AAAH/wAAOMACfgf/4AAH/+GAOMACfgf/4YAH/+GAOMAD/gf
-/4YAH/+GAOMAD/gf/4YAH/+HdOMAD/gf/4d0H/+NIOMAEWwf/40gH/+nhOMAFxgf/6eEH/+nhOMA
-MXwf/8AAH//8ZeMAMXwgAAAAIAABauMAbeQgAAF4IAABfOMAb1AgAAF8IAABheMAb1QgAAGYIAAB
-nOMAb2AgAAGcIAABpeMAb2QgAAG4IAABvOMAb3AgAAG8IAABxeMAb3QgAAHYIAAB2OMAb4AgAAHc
-IAAB4uMAb4AgAAH4IAAB+OMAb4ggAAH8IAAB/OMAb4ggAAIYIAACGOMAb4ggAAIcIAACHOMAb4gg
-AAI4IAACOOMAb4ggAAI8IAACPOMAb4ggAAJYIAACWOMAb4ggAAJcIAACYuMAb4ggAAJ4IAACeOMA
-b5AgAAJ8IAACguMAb5AgAAKYIAGaouMAb5ggAoAAIAKTYOMCB6QgApNgIAKTYOMCGwQgApNgIAYP
-KOMCGwQgBg8wIAYT8OMFltQgBoAAIAaNUOMFm5QgBo1QIAdgsuMFqOQgB2DAIAdhjOMGfFQgCMAA
-IAjAAOMGfSAgCMAAIAjAAOMGfSAgCMAAIAkvL+MGfSAAAAAAAAAAAAAAAAAgAAYvIAAGICAACjkg
-AAYgIAAJpSAABiAgAAbZIAAJPSAACMIgAAYgIAAIcSAACCQgAAe5IAAGDSAAB2QgAAYgIAAGICAA
-BiAgAAb6AAAAAP///////w/8///w////APwgAItrIACMqyAAjNsgAIyhIACMYSAAjFcgAIwcIACM
-EiAAjAggAIu4IACM2SAAi64gAIuUAAAAAAAAAAAAAAAAAAAACgAAAAoAAAAUAAAACgAAAAoAAAAK
-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
-AAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAA
-AAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAA
-AAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAA
-AAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAA
-AAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAA
-AAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAA
-AAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAA
-AAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAA
-AAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAA
-AAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAA
-AAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAA
-AAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAA
-AAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAA
-AAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAA
-AAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAA
-AAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAA
-AAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAA
-AAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAA
-AAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAA
-AAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAA
-AAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAA
-AAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAA
-AAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAA
-AAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAA
-AAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAA
-AAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAA
-AAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAA
-AAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAA
-AAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAA
-AAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAA
-AAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAA
-AAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAA
-AAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAA
-AAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAA
-AAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAA
-AAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAA
-AAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAA
-AAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAA
-AAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAA
-AAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAA
-AAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAA
-AAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAA
-AAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAA
-AAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAA
-AAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAA
-AAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAA
-AAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAA
-AAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAA
-AAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAA
-AAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAA
-AAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAA
-AAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAA
-AAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAA
-AAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAA
-AAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAA
-AAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAA
-AAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAA
-AAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAA
-AAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAA
-AAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAA
-AAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAA
-AAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAA
-AAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAA
-AAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAA
-AAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAA
-AAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAA
-AAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAA
-AAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAA
-AAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAA
-AAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAA
-AAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAA
-AAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAA
-AAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAA
-AAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAA
-AAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAA
-AAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAA
-AAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAA
-AAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAA
-AAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAA
-AAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAA
-AAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAA
-AAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAA
-AAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAA
-AAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAA
-AAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAA
-AAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAA
-AAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAA
-AAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAA
-AAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAA
-AAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAA
-AAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAA
-AAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAA
-AAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAA
-AAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAA
-AAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAA
-AAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAA
-AAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAA
-AAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAA
-AAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAA
-AAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAA
-AAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAA
-AAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAA
-AAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAA
-AAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAA
-AAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAA
-AAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAA
-AAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAA
-AAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAA
-AAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAA
-AAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAA
-AAAAAAAAAAAAAAAAAAAgCgAR8X8T8X/TDwPmMQECABbxfRfxfndrBpBgtGZ3Y/hUDoBVk5kPFABj
-//kAAABsEAQY8Xsd8XYrIAcc8XYqIQj6AgAHcbsBAO7cOQ3cAoAAC6oCnDDj8XAZsASAAAiqAhzx
-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
-IANNtCADTmEgA09bIANPZSADTmEgA09bIANPWyADT1sgA09lIANOYSADTmEgA09bIANPWwMPCBKD
-jpOSAAAAAAAAAAAgA4eRIAOIrCADh5EgA4eRIAOIrCADiKwgA4isIAOHkSADh5EgA4eRIAOIrCAD
-iKwgA4l9IAOJ/SADinYgA4qwIAOKwyADi4UgA4wAIAOMEyADjH0gA4ytIAOM0gAAAAAgA5RBIAOV
-jSADkxQgA5M3IAOUKSADkvQgA5MMIAOVqiADlgggA5l9IAOYPSADmHcgA5aZIAOXCyADl3kgA5e4
-IAOX8CADmO0gA5jlIAOY3SADmKEgA5iZARAYAQABAAAgA+OoIAPjqCAD4bsgA+OoIAPhuyAD44Qg
-A+NdIAPhuyAD4pEgA+G7IAPhuyAD4oggA+G7IAPjqCAD4bsgA+G7IAPjqCAD4cQAAAAAAAAAAAMB
-AAIAAAAAAAAAAAAAAAAgBGA9IARe8SAEXvYgBGA9IARe8SAEXvEgBF7xIARe9iAEYD0gBGA9IARe
-8SAEXvEgBGVqIARlmCAEZx8gBGVqIARnFyAEZv0gBGVqIARlaiAEZWogBGVqIARlaiAEZWogBGVq
-IARlaiAEZWogBGVqIARm6CAEZtMgBGVqIARlaiAEZWogBGVqIARlaiAEZWogBGVqIARlaiAEZWog
-BGVqIARlaiAEZWogBGVqIARlaiAEZqggBGVqIARmjSAEZo0gBGVqIARlaiAEZm0gBGVqIARmjSAE
-ZWogBGVqIARlaiAEZWogBGVqIARlaiAEZWogBGVqIARlaiAEZWogBGVqIARlaiAEZWogBGVqIARl
-aiAEZWogBGVqIARlaiAEZWogBGVqIARlaiAEZWogBGVqIARmTQAAAAAAAAAAAAAAACAEqrYgBKq2
-IASpiSAEqrYgBKSmIASqrCAEqoggBKSmIASkpiAEpKYgBKSmIASkpiAEqWwgBKq2IASkpiAEqWwg
-BKq2AAAAAAAAAAAAAAAAIAS5vCAEuhEgBLxjIAS8EiAEu8sgBLugIAS7jSAEu4cgBLrdAAAAAAAA
-AAAAAAAAARAYAQACAAABEBgBAAIAACAFCAYgBQfbIAUGQyAFB6MgBQejIAUHoyAFBkMgBQejIAUG
-QyAFB4QgBQZDIAUGQyAFB4QgBQejAAAAAAAAAAAgBSuwIAUnfCAFK4QgBStYIAUrLCAFJ6MgBSej
-IAUnuiAFKxMgBSfvIAUn2yAFJ6MgBSfvIAUnoyAFJ6MgBSejIAUrsAAAAAAAAAAAAAAAAAAAAAAg
-BX9kIAV12CAFdYggBXGIAAAAAAAAAAAAAAAAIAV1wiAFdb4gBXW+IAV1viAFdcIgBXW+IAV1viAF
-db4gBXW+IAV1viAFdb4gBXW+IAV1viAFdb4gBXW+IAV1viAFdb4gBXW+IAV1viAFdb4gBXW+IAV1
-viAFdb4gBXW+IAV1viAFdb4gBXW+IAV1viAFdb4gBXW+IAV1viAFdb4gBXW+IAV1viAFdb4gBXW+
-IAV1viAFdb4gBXW+IAV1viAFdb4gBXW+IAV1viAFdb4gBXW+IAV1viAFdb4gBXW+IAV1viAFdb4g
-BXW+IAV1viAFdb4gBXW+IAV1viAFdb4gBXW+IAV1viAFdb4gBXW+IAV1wiAFdb4gBXW+IAV1viAF
-dcIgBXW+IAV1viAFdb4gBXXCIAV1viAFdb4gBXW+IAV1wiAFdb4gBXW+IAV1viAFdcIgBXW+IAV1
-viAFdb4gBXXCIAV1viAFdb4gBXW+IAV1wiAFdb4gBXW+IAV1viAFdcIgBXW+IAV1viAFdb4gBXXC
-IAV1viAFdb4gBXW+IAV1wiAFdb4gBXW+IAV1viAFdcIgBXW+IAV1viAFdb4gBXXCIAV1viAFdb4g
-BXW+IAV1wiAFdb4gBXW+IAV1viAFdcIgBXW+IAV1viAFdb4gBXXCIAV1viAFdb4gBXW+IAV1wiAF
-db4gBXW+IAV1viAFdb4gBXW+IAV1viAFdb4gBXW+IAV1viAFdb4gBXW+IAV1viAFdb4gBXW+IAV1
-viAFdb4gBXW+IAV1viAFdb4gBXXCIAV1viAFdb4gBXW+IAV1viAFdb4gBXW+IAV1viAFdb4gBXW+
-IAV1viAFdb4gBXXCIAV1viAFdb4gBXW+IAV1viAFdb4gBXW+IAV1viAFdb4gBXW+IAV1viAFdb4g
-BXW+IAV1viAFdb4gBXW+IAV1viAFdb4gBXW+IAV1viAFdb4gBXW+IAV1viAFdb4gBXXCIAV1viAF
-db4gBXW+IAV1wiAFdb4gBXW+IAV1viAFdcIAAAAAAAAAAAAAAAAgBXYeIAV3HSAFeMsgBXi9IAV4
-qCAFeJogBXiFIAV4dyAFeGIgBXhVIAV4QiAFeDUgBXgiIAV4FSAFeAIgBXYLIAV37yAFd+IgBXfP
-IAV3wiAFd64gBXekIAV3kyAFd4kgBXd4IAV2CyAFdgsgBXYLIAV2CyAFdgsgBXYLIAV3ayAFdjcg
-BXYLIAV2CyAFdgsgBXdOIAV3NSAFdysgBXcMIAV2CyAFdvkgBXbfIAV2zCAFdrIgBXafIAV2hyAF
-dnQgBXZhIAV2UQAAAAAAAAAAIAV/uiAFf98gBYECIAWA2iAFgNMgBYDMIAWAxSAFgL4gBYC3IAWA
-sCAFgKkgBYCfIAWAlSAFgHcgBX/1IAV/lyAFf+wgBX/EAAAAAAAAAAAgBdCAIAXN5CAFzHggBcrE
-IAXHrCAFwcwgBcUIIAXFuFJWAAAAAAAAQb3NZQAAAABjb25maWd1cmF0aW9uIGZpbGUgcGFyc2Vy
-IGZvdW5kIGFkZGl0aW9uYWwgY29uZmlndXJhdGlvbiBhZnRlciBbZmluaV0KAABmY29lAAAAAHZu
-aWNfaWQAdmxhbgAAAAB0b3MAZXRoZXJ0eXBlAAAAbWFjbWF0Y2gAAAAAbXBzaGl0dHlwZQAAZnJh
-Z21lbnRhdGlvbgAAAGJhc2ljdmlydHVhbAAAAABzeW5tYXBlbgAAAABzeW40dHVwZW5pcHY2AAAA
-c3luMnR1cGVuaXB2NgAAAHN5bjR0dXBlbmlwdjQAAABzeW4ydHVwZW5pcHY0AAAAb2ZkbWFwZW4A
-AAAAdG5sbWFwZW4AAAAAdG5sYWxsbGtwAAAAaGFzaHRvZXBsaXR6AAAAAHRwX3BpbwAAdHBfdG1f
-cGlvAAAAMAAAAG5pY192bQAAbmljX3VtAABuaWNfdW1faXNnbABvZmxkAAAAAHJkZHAAAAAAcmRt
-YWMAAABpc2NzaV9pbml0aWF0b3JfcGR1AGlzY3NpX3RhcmdldF9wZHUAAAAAaXNjc2lfaW5pdGlh
-dG9yX2ZvZmxkAAAAaXNjc2lfdGFyZ2V0X2ZvZmxkAABmY29lX2luaXRpYXRvcgAAZmNvZV90YXJn
-ZXQAcG9mY29lX2luaXRpYXRvcgAAAABwb2Zjb2VfdGFyZ2V0AAAAcHBwAGRjYngAAAAAIAKQgAAE
-AAAAAAQAAAQAADB4AAAwWAAAMEIAACAF+SQgBd3wIAX5FCAF3gAgBd4UIAXdICAF3rwgBdy4AAEC
-AwAAAAAAAAAAAAAAACADsxwgA7KwIASzrAAAAAAgA7KoIAOyoCADspgAAAAAY4JTYwAAAAAAAAAA
-AAAAAAAAAAAAAAAEAAAABAAAAAhBQkNERUZHSElKS0xNTk9QUVJTVFVWV1hZWmFiY2RlZmdoaWpr
-bG1ub3BxcnN0dXZ3eHl6MDEyMzQ1Njc4OSsvPQAAAAAAAAAAAAAAAAAAAAsAAAAgApDACAAAACAC
-kMwIAAAAIAKQ2AoAAAAgApDkDAAAACACkPASAAAAIAKRAA0AAAAgApEUDgAAACACkSQTAAAAIAKR
-NAoAAAAgApFIDgAAACACkVQYAAAAIAKRZA0AAAAgApGADgAAACACkZAQAAAAIAKRoBIAAAAgApG0
-DgAAACACkcgQAAAAIAKR2BEAAAAgApHsCgAAACACkgALAAAAIAKSDA0AAAAgApIYFAAAACACkigK
-AAAAIAKSQA8AAAAgApJMBgAAACACklwGAAAAIAKSZAYAAAAgApJsBgAAACACknQGAAAAIAKSfAkA
-AAAgApKEBgAAACACkpAEAAAAIAKSmAYAAAAgApKgCwAAACACkqgLAAAAIAKStAQAAAAgApKYBAAA
-ACACksAJAAAAIAKSyAkAAAAgApLUAAAAAAAAAAANAAAAIAKS4AoAAAAgApLwBgAAACACkvwCAAAA
-IAKTBAMAAAAgApC8AQAAACACkwgAAAAAAAAAAP///////wAAAAdDAAAAAAD/////////////////
-////AAAAgAAAAQAAAAIAAAAEAAAACAAAABAAAAAAAAAAAAADAgEAAAAAAAAAAAAAAAAAAAAABQAA
-AAMAAAABAAAAAiAF1IwgBdREIAXTPCAF0fwgBdG8IAXRkCAF0eQAAAAAAAACAAAABAAAAAgAAAAA
-AAAEBAhOQQAAVjAAAFYxAABWMgAAVjMAAFY0AABWNQAAVjYAAFY3AABWOAAAVjkAAFZBAABWQgAA
-VkMAAFZEAABWRQAAVkYAAGFsbAAqAAAAbm9uZQAAAAAweAAAcG9ydAAAAABwcm90b2NvbAAAAABn
-bG9iYWwAAGZ1bmN0aW9uAAAAAGZpbmkAAAAAcmVnAGZpbHRlck1vZGUAAHJzc19nbGJfY29uZmln
-X21vZGUAcnNzX2dsYl9jb25maWdfb3B0aW9ucwAAc2dlX3RpbWVyX3ZhbHVlAHRwX3BtcngAdHBf
-cG1yeF9wYWdlc2l6ZQAAAAB0cF9wbXR4AHRwX3BtdHhfcGFnZXNpemUAAAAAbXR1cwAAAABudmYA
-d3hfY2FwcwByX2NhcHMAAG5pcWZsaW50AAAAAG5lcQBuZXRoY3RybAAAAABudmkAcnNzbnZpAABu
-ZXhhY3RmAGNtYXNrAAAAcG1hc2sAAABuZXRob2ZsZAAAAABucm91dGUAAG5jbGlwAAAAbmZpbHRl
-cgBuc2VydmVyAG5oYXNoAAAAdHBfbDJ0AAB0cF9kZHAAAHRwX2RkcF9pc2NzaQAAAAB0cF9zdGFn
-AHRwX3BibAAAdHBfcnEAAABpc2NzaV9udGFzawBpc2NzaV9uc2VzcwBpc2NzaV9uY29ubl9wZXJf
-c2Vzc2lvbgBpc2NzaV9uaW5pdGlhdG9yX2luc3RhbmNlAAAAaXNjc2lfbWF4X3NnZQAAAHBwbV9t
-YXhfem9uZXMAAABwcG1fem9uZV9yYW5nZTAAcHBtX3pvbmVfcmFuZ2UxAHBwbV96b25lX3Jhbmdl
-MgBwcG1fem9uZV9yYW5nZTMAZmNvZV9uZmNmAAAAZmNvZV9udm5wAAAAZmNvZV9uc3NuAAAAZGNi
-AGJnX21lbQAAbHBia19tZW0AAAAAaHdtAGx3bQBkd20AdmVyc2lvbgBjaGVja3N1bQAAAABDaGVs
-c2lvX0ZPaVNDU0kAMDEyMzQ1Njc4OWFiY2RlZmdoaWprbG1ub3BxcnN0dXZ3eHl6AAAAADBiAABZ
-ZXMAU2Vzc2lvblR5cGUAT0ZNYXJrZXIAAAAASUZNYXJrZXIAAAAARGF0YURpZ2VzdAAASGVhZGVy
-RGlnZXN0AAAAAEVycm9yUmVjb3ZlcnlMZXZlbAAASW1tZWRpYXRlRGF0YQAAAERhdGFQRFVJbk9y
-ZGVyAABEYXRhU2VxdWVuY2VJbk9yZGVyAEluaXRpYWxSMlQAAE1heENvbm5lY3Rpb25zAABNYXhS
-ZWN2RGF0YVNlZ21lbnRMZW5ndGgAAAAASW5pdGlhdG9yTmFtZQAAAEluaXRpYXRvckFsaWFzAABE
-ZWZhdWx0VGltZTJXYWl0AAAAAERlZmF1bHRUaW1lMlJldGFpbgAATWF4QnVyc3RMZW5ndGgAAEZp
-cnN0QnVyc3RMZW5ndGgAAAAATWF4T3V0c3RhbmRpbmdSMlQAAABUYXJnZXROYW1lAABUYXJnZXRB
-bGlhcwBUYXJnZXRBZGRyZXNzAAAAVGFyZ2V0UG9ydGFsR3JvdXBUYWcAAAAAQXV0aE1ldGhvZAAA
-U2VuZFRhcmdldHM9QWxsAENIQVBfQQAAQ0hBUF9JAABDSEFQX0MAAENIQVBfTgAAQ0hBUF9SAABE
-aXNjb3ZlcnkAAABOb3JtYWwAAE5vbmUAAAAAQ1JDMzJDAABDUkMzMkMsTm9uZQBOb25lLENSQzMy
-QwBDSEFQAAAAAENIQVAsTm9uZQAAAE5vbmUsQ0hBUAAAAE5vdFVuZGVyc3Rvb2QAAABJcnJlbGV2
-YW50AABSZWplY3QAAE5vAAA1AAAAAAAADAAAAAABAAF8AAwBAAAAABAAAAAUIAYKWAAAAxUOQAAA
-H/wAAB/8AAAf/6eIH/+niCAGE/AgBhiwIAdhkCAHYZAgB8AAIAgQQCAIQAAgCMAAIAjAACAJBvAg
-AqRsIAkHUCAGFEAIAAAAH/zgdCAIwFAoAAAAAQAAAOAAAAAf/44EAAgAAB//l3Af/5YoH/+WNP/9
-//8AmJaAAACAACACszwgCMDgH/+WRCAIwTAgAo0QAAAIBgAACAAABAAAAAD//yAJCJAD/9AAABAA
-AABAAAAf/5ZgAFAH/yAJCRAgAqyQAAAQAADAAAAAAA//ACAAAAAP//AgCMFQ//9AAB//lZAgCMGQ
-AAAf/h//ljjXaqR46Me3ViQgcNvBvc7u9XwPr0eHxiqoMEYT/UaVAWmAmNiLRPev//9bsYlc175r
-kBEi/Zhxk6Z5Q45JtAgh9h4lYsBAs0AmXlpR6bbHqtYvEF0CRBRT2KHmgefT+8gh4c3mwzcH1vTV
-DYdFWhTtqePpBfzvo/hnbwLZjSpMiv/6OUKHcfaBbZ1hIv3lOAykvupES97Pqfa7S2C+v7xwKJt+
-xuqhJ/rU7zCFBIgdBdnU0Dnm25nlH6J8+MSsVmX0KSJEQyr/l6uUI6f8k6A5ZVtZw48MzJL/7/R9
-hYRd0W+ofk/+LObgowFDFE4IEaH3U36CvTryNSrX0rvrhtORIAKLcCACiJAgAoxwIAKMACACkJAf
-/5iAIAkKICACi1AgCMHQIAjCED/////P////IAKLfCAJCkBnRSMB782riZi63P4QMlR2IAkKYCAJ
-CoAgCQqgIAkKwCAJCuAgApC4IAKKwCACjCwgAotsIAjCUCAJCwDAAAAAIAkLQAAAIAAgCMKA4QAA
-ACACixAgCMLQIAjC8CAIw4AgCMMgIAjDUB/83gAgCQfQIAkIMCAJC4AgBVgAGgAAACAJDDAAD0JA
-IAACmAACAAAgAovQIAkL4AD///8gAuVoIAjEoAQAAAiBAAAAAABIAAwAAAAgCMVAIAKLaAAAQAAg
-AueYIAKKRCAIxaAgCMVwIAjGACAIxdAgAwYEIAjGMCAJDGAgCMZgIAjGoCAIxwAgCMeAIAjG0CAI
-xzAgAAAAIAMC9CAIymAgBhP0CgAAACAC1ewgAqg8IAKlgAAAwAAgCQlQIAkJsCAIy/ACAAAAIAjM
-gCAIzUAf/5RwIAYSAAAP/AAAADAAIAjNcCAIzgAgCM6gVQAAAAAAD/4f/5YwAAAMIPAAAAAgCM7Q
-IAMvtB//lqAf/41wIAKLICAIzyAgCM9wH/+VhCAI0DAgCQ0AIAjP0CADGnAgCNCQIAjRACAI0VAg
-CQ2QIAjRkCAI0gD/9///H/+VmCAJDdAmAAAAIAjSYB//liQgCNLQIAYXMCAI01AgBhbwIAkOUBeQ
-AAAgBg9Af////yACgADAAAABIAjVMCAI1ZCAAAAAIAjTsCAGFkAAAFAAFLAAAIAAAAQgAy0YH/+P
-OCAI1KAVoAAAAACP/iAI1NAWoAAAIygVACCgAAA/8AAAI6AAAAP/AB8gBhb6IAjT8CAI1EAgCNXQ
-//8AABAAAAAgCNYgIAjWgA4AAAAgCNagKgAAACADLZQgCNbAIAjXECAI2VAf/5ZoIAjYoCAI2QAg
-CNdwIAjX4CACieQgCNhA4QGOACAJDpAAAZEMIAkO4CAJDxD///P/4QIWAOECGgD//8j///8X////
-H/8AAhkcAAIZfAACGZwf/5ZA///x/wACGqQAAhooAAIaqAACGjQAAhq0AAIaMAACGrD///f/AAD/
-6eECDgAAABOHIAKAICACgFAgAoCAAABADQAA//cAAA9CAAD/9QAA//AgCNoQIAjZsCAI2eAAAAwA
-AAAYAAACEDQAACBAAADEEAAAwAEAAMggIAjaQAAAxAD+AAAAAF/2pwCyN8dAAAAAHf///2IAAAAA
-AMQwIAkPYAAAxDEgCQ8wAADoACAI2pAgCNrQIAjaYAAAyAAgCQ+AAAAnECAJD7AAAMRFIASK0AAA
-yBAAABABAAAgfAAAMgDhAF4AAAJJ7wAAzAAgBhCMIAKAsCAI2wAgCNuQIAjbMCAI22D//7//IAjc
-AB//luAAHoSAAAGGoP/he4AABJPg//tsICAI3DAgCNygIAjc0CAI3QAgCN0wIAjdYCAI3cAgCN2Q
-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
-AO0WFinQBIAAW/pN+GBoHaAJRQDTD22aKSmAASqAAyuAAumAACzmAoAACKoRCroC7JkCDVQCgAAK
-mQLphgAkQBEAACoSNfpgaB3kbEUADwIA/UAARTAcBQBYyEX95OYFoApVAPwnZhWgOwUAWMwHG/Jk
-KhJADwIAI7D8KqwB67JAKeAEgABYyDoqEkHr8mgR4AUAAKyqKhZB9UAARTA91QD6KAYVoAwlAO2k
-ACVQBQAAWMgv9+S+BaudxQD84DmkYgCdACvKHPrgOUxiAJ0AIjqAAnsILrDk/XyQFeXuEQAObggu
-4IAutGT/fLAVoD8FAOsWPi7vAoAAD90B/XywFaPuIQAO3QINbQgt0IAttGUtsOb/kAAWMD7FAA7M
-Aft80BWh3TEADcwC/MAARjAINQD9kBAVoAlFAOy0ZiXYEQAAbZpi6HkIBdgRAACziPMgAES1qgEA
-qmoqoIAqtF8qkOQKKkWqaiqggCq0YCqQ5CyQ5QyqEf9ABAVzzCEADKoCqmoqoIAqtGEqkOUskOYO
-qhH/QAQFMcwxAAyqAqpqKqCAKrRiKpDmIhI+++QqBaXKAQCsbCzAgCy0Yygg8yqgBCokevpPZh2l
-mBEACWkI6ZCALEcCgADTD/hPBh3gOQUACYgBqG8v8ID+TyYd4BKFACoSQCs65Ot7CAlgBIAA6xY9
-JVANAABYx9otEkGzLq7epO8l9AAsco79wCAV4As1APwoJhXozLkA/WAKBiIAnQD8JsgVpGhFAPjg
-AEQyiQUA+OAARPOCBQCiciIWPiaQ3yIg7CKU3CWEACWEASWEAiWEAyWEBCWEBSWEBiWEByWECCWE
-CSWECiWECyWEDCWEDSWEDuWEDyD5gQAAlfCV8ZXylfOV9JX1lfaV95X4lfmV+pX7lfyV/ZX+JfYP
-JfYQJfYRJfYSJfYTJfYU9eKmFeAKVQDyK4YdoDsFAFjLcBvx0Brx0Bzxzu3xzBDxgQAAleCV4ZXi
-leOV5JXlleaV55XolemV6pXrleyV7ZXuJeYPJeYQJeYRJeYSJeYTJeYUJeYVLRYaLBYbKhYd+iOG
-FeAKVQD8JugVoDsFAFjLWCocYPorgBXgDBUAW/tlwKX8JwgVoDsFAFjLUShKgKh4L4DU8eZADeAM
-BQBtCAwpgNWxzOSQVGRABQAAY//s0tDRDwAT8bAmMCzaQOsyDCtgBIAAWMd7pkryepAV4DvVACuk
-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
-AgAAAAAAAAcSAAAAAAAACAMAAAAAAAAPEwAAAAAAABAEAAAAAAAAHxQAAAAAAAAgBQAAAAAAAD8V
-AAAAAAAAQAYAAAAAAACABwAAAAAAAMAWAAAAAAABAAgAAAAAAAIACQAAAAAAAwAXAAAAAAAEAAoA
-AAAAAAgACwAAAAAADAAYAAAAAAAQAAwAAAAAABgAGgAAAAAAIAANAAAAAABAAA4AAAAAAIAADwAA
-AAAAwAAaAAAAAADgABsAAAAAAQAAEAAAAAABgAAcAAAAAAHAAB0AAAAAAeAAHgAAAAACAAARAAAA
-8tUSmPLVEpjy1RKY8tUSmPLVEpjy1RKY8tUSmPLVEpjy1RKY8tUSmPLVEpgDhRsYA4UbmPLVEpjy
-1RKY8tUSmPLVEpjy1RKY8tUSmPLVEpjy1RKYAAAAAAAAAAAAAAAA//8ABQAGAAcACAAJAAoACwAM
-AA0ADgAPABD///////8ACgAUAIwAggBkAFoAoACWAHgAbgBQAEYAPAAyACgAHgAKBM4EkgnOCZIC
-sgRWBOIEpgO2BBoHsglWBl4CdgniCaYHTgI6CLYJGggWBiICxgRqAJYDegFyB3YAvgPeBPYEugRC
-A6ICYgc6Bg4AggPKBC4AbgBaAyoBNgHqAz4HxglqAUoHEgWWCHoGwgH+BnICigNSBeYFvgjeAZoH
-2gn2CboJfgeeCUIIogkGAV4HYgJOByYDFgEiBYIIZgWqCMoJLgiOAQ4FbgVaBUYG1ggqBjYCEgHC
-BuoIPgDmBoYC2gR+Ap4EBgZKAiYIAgNmAKoDjgX6AEYB1gb+Bq4F0gGGB4oI8gMCCFIA+gUyAa4A
-0gPyB+4AMgaaAu4FHgAeBQoAAAnECYgE2AScB6gJTAnYCZwIrAkQArwEYAFoB2wE7ASwAlgHMAPA
-BCQDIAEsB7wJYAWMCHAGaAKABbQI1AnsCbAJOAiYB1gCRAEYBXgIwAkkBWQFUAggBiwG4Ag0AtAE
-dAZAAhwAoAOEAcwG9AF8B4AISADwAMgD6AaQAuQFAATEBIgCqARMA6wEEAZUAmwHRAIwCAwGGACM
-A3AAtAPUBDgDmAYEAHgAZABQAeADNAFABwgGuAH0A0gF3AGQB9AJdAeUCPwBVAccAwwIXAWgCIQB
-BAU8BswCCAG4ANwGfAKUA/wH+ANcBfAAPAakBcgI6AL4BSgBpAfkACgFFAAUAAAEyQSNCckJjQKt
-BFEE3QShA7EEFQetCVEGWQJxCd0JoQdJAjUIsQkVCBEGHQLBBGUAkQN1AW0HcQC5A9kE8QS1BD0D
-nQJdBzUGCQB9A8UEKQBpAFUDJQExAeUDOQfBCWUBRQcNBZEIdQa9AfkGbQKFA00F4QW5CNkBlQfV
-CfEJtQl5B5kJPQidCQEBWQddAkkHIQMRAR0FfQhhBaUIxQkpCIkBCQVpBVUFQQbRCCUGMQINAb0G
-5Qg5AOEGgQLVBHkCmQQBBkUCIQf9A2EApQOJBfUAQQHRBvkGqQXNAYEHhQjtAv0ITQD1BS0BqQDN
-A+0H6QAtBpUC6QUZABkFBQAACb8JgwTTBJcHowlHCdMJlwinCQsCtwRbAWMHZwTnBKsCUwcrA7sE
-HwMbAScHtwlbBYcIawZjAnsFrwjPCecJqwkzCJMHUwI/ARMFcwi7CR8FXwVLCBsGJwbbCC8CywRv
-BjsCFwCbA38BxwbvAXcHewhDAOsAwwPjBosC3wT7BL8EgwKjBEcDpwQLBk8CZwc/AisIBwYTAIcD
-awCvA88EMwOTBf8AcwBfAEsB2wMvATsHAwazAe8DQwXXAYsHywlvB48I9wFPBxcDBwhXBZsIfwD/
-BTcGxwIDAbMA1wZ3Ao8D9wfzA1cF6wA3Bp8FwwjjAvMFIwGfB98AIwUPAAAACgCMAG4AlgB4ADwA
-UACgAIIAZAAyAEYAWgAoAB4AFAAAAIcAaQCRAHMANwBLAJsAfQBfAC0AQQBVACMAGQAAIAKOLCAH
-GqwgAo4wIAcgaCACjjwgByAYIAKOUCAHH2wgAo5oIAceLCACjnggBx4EIAKOgCAHHdwgAo6UIAcd
-tCACjpwgBx2MIAKOsCAHIRwgAo64IAcabCACjrwgBxnwIAKOxCAHGbggAo7MIAcZgCACjtggBxlI
-IAKO3CAHGRAgAo7oIAcY2CACjuwgBxooIAKO9CAHGKAgAo78IAcYaCACjwQgBxZ4IAKPDCAHGDAg
-Ao8YIAcX+CACjyAgBxfAIAKPKCAHF4ggAo8wIAcXUCACjzggBxcYIAKOBCAHFIAgAo9AIAcUSCAC
-j0ggBxQQIAKPUCAHE9ggAo9gIAcToCACj2ggBxNoIAKPcCAHEzAgAo94IAcTCCACj4QgBxLgIAKP
-kCAHErAgAo+oIAcSiCACj8QgBxJgIAKP1CAHEjggAo/kIAcSECACj/QgBxHoIAKQBCAHEcAgApAU
-IAcRmCACkCQgBxFwIAKQMCAHEUggApA8IAcRIAAAAAAAAAAAIAKQSCAHEAggApBMIAcP2CACkFQg
-Bw+oIAKQYCAHD3ggApBkIAcPSCACkGggBw8YIAKQbCAHDvAgApB0IAcOyCACjhAgByNgIAKOGCAH
-IowgAo38IAch1CACjiQgByEkAAAQISBCMGNAhFClYMZw54EIkSmhSrFrwYzRreHO8e8SMQIQMnMi
-UlK1QpRy92LWkzmDGLN7o1rTvcOc8//j3iRiNEMEIBQBZOZ0x0SkVIWlarVLhSiVCeXu9c/FrNWN
-NlMmchYRBjB212b2VpVGtLdbp3qXGYc499/n/tedx7xIxFjlaIZ4pwhAGGEoAjgjyczZ7emO+a+J
-SJlpqQq5K1r1StR6t2qWGnEKUDozKhLb/cvc+7/rnpt5i1i7O6sabKZ8h0zkXMUsIjwDDGAcQe2u
-/Y/N7N3NrSq9C41onUl+l262XtVO9D4TLjIeUQ5w/5/vvt/dz/y/G686n1mPeJGIgamxyqHr0QzB
-LfFO4W8QgAChMMIg41AEQCVwRmBng7mTmKP7s9rDPdMc43/zXgKxEpAi8zLSQjVSFGJ3cla16qXL
-laiFifVu5U/VLMUNNOIkwxSgBIF0ZmRHVCREBafbt/qHmZe451/3fscd1zwm0zbyBpEWsGZXdnZG
-FVY02UzJbfkO6S+ZyInpuYqpq1hESGV4BmgnGMAI4TiCKKPLfdtc6z/7Hov5m9iru7uaSnVaVGo3
-ehYK8RrQKrM6kv0u7Q/dbM1Nvaqti53ojcl8JmwHXGRMRTyiLIMc4AzB7x//Ps9d33yvm7+6j9mf
-+G4XfjZOVV50LpM+sg7RHvAAADAEAAAwEAAAWQgAAFmkAAGUMAAAECQAABAwAAAQPAAAENwAAHss
-AAB7NAAB4owAAeaMAAHqjAAB7owAAfKMAAH2jAAB+owAAf6MAACNzAAAfnQAAI/cAAGRWAAAj/wA
-AHUcAAB1GAAAdRAAAHfgAAB35AAAd/QAAZw8AAEQdAABEIQAAJQIAACYXAAAlgwAAJYUAACWIAAA
-liwAANAoAACQCAACENwAAjDcAAJQ3AACcNwAAZBUAABgHAABoNgAAZCQAAB34AAAd+QAAHf0AAB5
-eAAAefgAAZQEAAHjwAAB58AAAevAAAHvwAAB88AAAffAAAH7wAAB/8AAAZQMACEACQCBAEEAIQAJ
-ACEACQCBAEEAgQBBAIEAQQCBAEEAIQAJAIEAQQAhAAkAIQARAIEAQQCBAEEAIQAJAIEAQQAhAAkA
-IQAJAQEAQQCBACEAgQAhABAQYQAAAAAAAAAAAAAwAD///8AAADAIP///wAAAWRCsgAAAAABZrH4l
-AAAAAZQQH////wABlDQAAAARAAGUCB////8AAZQ4AAAAEQAAECh/////AAAQLH////8AABA0f///
-/wAAEDh/////AAAQQA9EAAAAAHsoAB//4gAAezA//+7/AAB7OAAAAAEAAHtAP//u/wAAfCQAH//i
-AAHiiAAIAAAAAeaIAAgAAAAB6ogACAAAAAHuiAAIAAAAAfKIAAgAAAAB9ogACAAAAAH6iAAIAAAA
-Af6IAAgAAAAAjcj/////AACN0P////8AAH5wf7///wAAfnh/v///AAB+AADAAAAAAZFUAf///wAB
-kZwB////AACP2AB///0AAI/4/////QAAdRQAAAAHAAB1DAAAAAsAAHfcAAAAAwAAd/D/////AAGc
-OAABgEAAAZz4AAAAAwAAkAQAAAA+AAEQeAD///8AARB8AP///wABEIgAAAAAAACUBAAA//8AAJQQ
-AAD//wAAmFQAAAH/AACYWAAAAf8AAJYIAAAAAAAAlhAAH///AACWGAAf//8AAJYcAA///wAAliQA
-D///AACWKAD///8AAJYwAP///wAA0CAAAAAHAADQJAAAAAcAAhDYAAAAAwACMNgAAAADAAJQ2AAA
-AAMAAnDYAAAAAwABkFAAAAAvAAGg1AAAAYMAAZCMADgAAAABkJgAOAAAAAB5dAAAAC8AAHlwAAAA
-BQAAefQAAAAvAAB58AAAAAXhAg4AAABAAAAA//AAADDU4QBeAAAIAAgAAOQAAADEMAAAxDEf/5W0
-H/+PLB//l3AgCPVAAABBHgAA8AMAAEEOAABAQAAAoAAAAKAgAABBiAAAf/8AAEGGAACAAAAAQYEA
-AKgZAACoGgAAwwAAAKgbAACoHAAAqBcAAP//AAAQBAAA5Z8AAB8RAADuCQAA//4AAOr/IAj1cAAA
-/wAgCPWwIAj18AAJiWgAAMRQIAj2ICAI9kAgCPZgAAAgAAAAxFEAAMABAADEUgAPAA8AAGGoIFAA
-ACAJIxAAAEANIAkjcCAJIzAgCSNQIE//gCAGhsQgCSPgIAkkQCAJI5AAAMAGAAgAAiAJJIAAAIDX
-AABAggAAxIIAAIQDIAkmcCAJJXAAAICmAACAPSAJJNAAAIBAIAkl0CAJJiAgCSUgH/+USB//lQwg
-CScQIAj2gOEBmgAf/5TYH/+OBAAACAD/wP//ABAAACAJJsAABggAIAj3ACAI92AgCShQIAkoACAJ
-J2AgCSew4QAuACACjTAAAJAAH/+PTAAsFACAAACA4QBaAIHw4IDhAC4IIAAAAOEAVgAMAAAA//OA
-AAAMOABGAAAAPz///4CAAADz/////+D//wABAAAgBoAAIAaFpCAGgQDhAZIAH/+OAOEAigDhAA4A
-4QB+AOEAegAAAgAAAH8AQAADAAAAgACAAMEAwP//I/8AAMgAECABIOEAjgA8AAAA//+//1Bo6Ef4
-////BAAAAJIAAADwAPAAn7/7+yAABAR/9///gAACAP//7////4D///f/fwAAfrAABgAAAAAP/wAA
-frQA////AEkkkgAAfhgIAQgBEAEQARABCAEAAH5AIAEgARUVFRWEIYQhEBAQEOEBjgAAAP5/BAQB
-gB//lpwf/5RwMzMzMyIiIiIRERER4QDOAOEAjgThAI4I4QCODCAGiMThAHYAIAKNUP//8ADg//4A
-gAABACAGh8QAAJAIAAAIwIABxBHEEcQRAP8A/wBAAEAAD0JAAAAcIP//AAAgCSjAIAkpMB//lKAz
-IhEAEREAADMzIiL/8P8AAAIAMyoqFRUf/5aUH/+VoAAA//0gCSngH/+VuCAI99AgByEkAFAAAACg
-AAAgoAAA4wACAOL//wDQAAAAz//+ACACiKAf/5WsH/+AoB//lcjhADkAH/+A0B//j1Af/4EgH/+V
-1OH//gAf/4EAAAP////8AAAABAAAH/+TcB//jvDhAZYA4QDeAOEA7gDhAf4A4QJeAOECPgDhAh4A
-4QHiAAACAQDhAQ4A4QCSAAgAAAD///D/H/+UmB//lcz/8AAAAAKABuEBngD/+///IAj6ECAI+nAf
-/4EoIAYSACACinAgCPrg4AAAAAAAfWwADwAAEAAAAAAAEAAgCPtAIAj7cB//j2Qf/4E4IAYUQB//
-lQD//8f/gACAAB//pKAf/5QYH/ziAIAAAAAAAHtgH/+YgB/83gDhAZgAH/+PNB//lnAf/5SkH/+W
-YCAJKrAgCPugIAj74CAI/AAgCPwgIAj8kCAI/PAgCP1QAAAf/yAI/YAgCP2gIAj94CAJKmAgCSog
-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
-4ogIAzAFAAB9awJj/6oikllkIJsVz+yMEPX/4h2gCgUA8AEQDaAGBQAAwMEk1Aws1A0m1CIv1HaY
-35fc86FoFe+CBQAi1CAi1CGC3qN3I5JZ4ogIBVP9AADr1S4jMAUAAONrS3ZgBQAAIpKfLZKmomIJ
-IxED3QjzoAYVoAPVAPOghh3gA4UA49QFLX0OAAAqUH4kTAHlr5FiqAUAAG0IDCpQfrFE5a+BYqgF
-AABj/+woklrLivWbigXgBgUA+ADiHaAH5QBtCCUskqArkqasbAnNEa27nLAutA0otAwntAQqklrl
-tS8jMAUAAHprAmP/08Ag0Q/Y4P/8TA2gBwUAAGwQBsCgW5AtEs5P0w+iqSiSgGeAC20IBSqSgGeg
-AmP/88ChW5AmoqkrkoBnsA1tCAUskoBnwARj//MAAPud5AXgChUA/Z3iBaANBQD/nz4F4A4VAFum
-jR/OVxPOLhjO6R7NuSkykioykSsykC420S420y421S421ywyjyg20Cg20i82zy821C0yji023i8y
-lCgyk63MLjKYLTKXLDbfrLsrNuCrqiwylSo24aqZKzKWKTbiqYgqMqAoNuOo/ykyoS825ygyoq3/
-Lzbor+4uNuSuzCw25i8ypq3MLjKnLTKoLDblrLsrNumrqiwyqSo26qqZKzKqKTbrqYgqMqwoNuyo
-/ykyrS827a/uKDKwLjburt0tNu8fzcGtzCw28C82zh7OJ6y7Kzbx/nrGFaANBQAtNt2rqio28qqZ
-KTbzqYgoNvRbpk4rMtcvMtYPAgDzb+AVr4gFAOgiAQ1uQoAAotp680gpMtQPAgDLn+o21y1YBIAA
-HM2RLjLV8iBmFaAKVQD6IAYV4IgFAPwgJhXgCwUA+CBGFaANFQBbqNjiNvUhAKGAAMAg0Q8AAAAA
-//8MDaACBQDHJNEPbBAEE881+5piBaAEBQD3noIFoAcVACSmgSSmgySmgCSmghvN1ygwdf2bxAXg
-3lUA/MsIFaAZBQDppookfKKAAA7MKB7N3Q3MLC+ywA7/AQ/PAi+2wC2y4A7dAQ3MAiy24Cuy4MCg
-W4+nFc3IEs3JGc54Ba8IKfaBGM8oKPaCJPaD8/AGFaAKFQBbj56lqiSmgRvN2iumgiSmgyKmgFv+
-Quah1W0QBIAAHc19LgoIL9KPDw9DLzR0LNKKG85S/4YADjAIBQD9sUYVoImFAG2aDQuJCySWQOSW
-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
-7/+SAKWb92AEHe//agClnPeABhXv/0IApZ0n1ADRD6WeJ+UA0Q8AAAAAAAAAAAAAAAAAACACjaQM
-wAACIAXtfCACjagIwAAIIAXtfCACjawgwAAMIAXtBCACjbAGwAA0IAXu2CACjbQIwAA4IAXtfCAC
-jbgCwAA8IAXu2CACjbwIwABAIAXtfCACjcAIgABEIAXuBCACjcQYgABYIAXuBCACjcgYgABkIAXu
-BCACjcwYgABwIAXuBCACjdAYgAB8IAXuBCACjdQYgAC4IAXuBCACjdgYgADEIAXuBCACjdwYgADQ
-IAXuBCACjeAYgADcIAXuBCACjeQIgAEYIAXuBGNzb2NrX2Nsb3NlZDogc2l6ZW9mKHRjYl9mYy0+
-dS5vZmxkLm8uZm9pc2NzaV9jb25uKSBbJXVdLCBieXRlcwoAAAAAAAAAAAAAAAAAAAAAY3NvY2tf
-ZmFpbGVkOiBjc2tfZmMtPmZsb3djX2lkIFsweCV4XSwgY3NrX2ZjLT5mbG93Y19zdGF0ZSBbMHgl
-eF0sIHNlc3NfZmMtPmZsb3djX2lkIFsweCV4XSwgc2Vzc19mYy0+Zmxvd2Nfc3RhdGUgWzB4JXhd
-LCBldnQgWzB4JXhdCgAAAAAAAAAAAAAAY2huZXRfZmluZF9sMnRfZW50cnk6IGRhZGRyIFslMDh4
-XSwgWzB4JTA4eF0sIGxvY2FsIG5ldHdvcmsgWyVkXQoAAAAAAAAAAAAAAAAAAABsMnRlbnQgWyUw
-eF0sIGwydGVudC0+aWR4IFslZF0KAHJjIFslZF0sIGNza19mYyBbMHgleF0sIGNza19mYy0+Zmxv
-d2NfaWQgWzB4JXhdCgAAAAAAAAAAAAAAAAAAAABjc29ja19mcmVlOiBzaXplb2YoY3NrX2ZjLT51
-LmNzb2NrKSBbJXVdLCBieXRlcwoAAAAAAAAAAAAAAAAAAAAAbG9naW5fcGFyYW1zOiBjb25uX2Zj
-LT5mbG93Y19mb2lzY3NpX2Nvbm5fZmxhZ3MgWzB4JXhdCgAAAAAAAAAAAGxvZ2luX3BhcmFtczog
-Y29ubl9mYy0+Zmxvd2NfZm9pc2NzaV9jb25uX2ZsYWdzIFsweCV4XQoAAAAAAAAAAABUQ1AgY29u
-biBlc3RhYmxpc2htZW50IGZhaWxlZCAlZAoAAAAAAAAAAAAAAAAAAABpbm9kZV9mYy0+Zmxvd2Nf
-ZmxhZ3MgWzB4JXhdLCBpbm9kZV9mYy0+Zmxvd2NfYnVmLT5maWZvLm51bV9ieXRlcyBbJXVdCgAA
-AAAAAAAAAHVzZXJuYW1lIG1pc21hdGNoCgAAAAAAAAAAAAAAAAAAUmVjdmQgYW5kIENvbXB1dGVk
-IGRpZ2VzdCBtaXNtYXRjaAoAAAAAAAAAAAAAAAAAYmFzZTY0IEVSUiwgJWQgbGVmdCwgd2l0aCAl
-ZCBwYWQuCgAAAAAAAAAAAAAAAAAAYmFzZTY0IEVSUiwgJWQgbGVmdCwgd2l0aCAlZCBwYWQuCgAA
-AAAAAAAAAAAAAAAAYmFzZTY0IEVSUiwgJWQgbGVmdCwgd2l0aCAlZCBwYWQuCgAAAAAAAAAAAAAA
-AAAAUmVjaWV2ZWQgQXV0aE1ldGhvZCB2YWx1ZQoAAAAAAAB2YWxpZGF0ZV9rZXl2YWw6IGZsb3dj
-X2ZvaXNjc2lfY29ubl9mbGFncyBbMHgleF0KAAAAAAAAAAAAAAAAAAAAdmFsaWRhdGVfa2V5dmFs
-OiBmbG93Y19mb2lzY3NpX2Nvbm5fZmxhZ3MgWzB4JXhdCgAAAAAAAAAAAAAAAAAAAE5lZ28gRkJM
-IFsldV0KAABLRVlfTUFYUkNWRFNMIFsldV0KAAAAAAAAAAAAAAAAAER1cGxpY2F0ZSBpc2NzaSBr
-ZXkKAAAAAAAAAAAAAAAAc2VuZF9sb2dpbl9yZXF1ZXN0OmNvbm5fZmMtPmZsb3djX2lkIFsweCV4
-XSwgc2Vzc19mYy0+Zmxvd2NfaWQgWzB4JXhdLCBwYXJhbSBbMHgleF0sIHBhcmFtLT5zZXNzX3R5
-cGUgWzB4JXhdLCBzZXNzX2ZjLT5mbG93Y19mb2lzY3NpX3Nlc3NfdHlwZSBbMHgleF0KAAAAAAAA
-AAAAAHNlbmRfbG9naW5fcmVxdWVzdDogdHhfbGVuIFsldV0KAAAAAAAAAAAAAAAAAAAAAGZvaXNj
-c2lfcGFyc2VfcmN2ZF9wYXJhbXM6IGVycm9yCgAAAAAAAAAAAAAAAAAAAGZvaXNjc2lfcGFyc2Vf
-cmN2ZF9wYXJhbXM6IHJsZW4gJWQKAAAAAAAAAAAAAAAAAFNlcSAjIG1pc21hdGNoIHN0YXRzbiA9
-IDB4JXggZXhwc3RhdHNuID0gMHgleAoAAHRleHRfcmVzcDogY19iaXQgWzB4JXhdLCBmX2JpdCBb
-MHgleF0KAAAAAAAAAAAAAGxvZ2luX3Jlc3A6IGZsb3djX2ZvaXNjc2lfY29ubl9mbGFncyBbMHgl
-eF0KAAAAAGxvZ2luX3Jlc3A6IGZsb3djX2ZvaXNjc2lfY29ubl9mbGFncyBbMHgleF0sIHJjIFsw
-eCV4XQoAAAAAAAAAAABpU0NTSSBTZWMtcGFyYW1zIHJlY2VpdmVkaGF2ZSBlcnJvcnMhIQoAAAAA
-AAAAAABUYXJnZXQgbW92ZWQgdGVtcC4gY29ubiAleCwgc2VzcyAleAoAAAAAAAAAAAAAAABMb2dp
-biBGYWlsZWQhIS4gY29ubiAlZCwgc2VzcyAlZAoAAAAAAAAAAAAAAAAAAABsb2dpbl9yZXNwOiBz
-ZXNzX2ZjLT5mbG93Y19pZCBbJXVdLCBjb25uX2ZjLT5mbG93Y19pZCBbJXVdIGxvZ2dlZCBpbgoA
-AAAAAAAAAAAAAFByb3RvY29sIEVycm9yIGNiaXQgJWQgdGJpdCAlZCBjc2cgJWQgbnNnICVkCgAA
-AGRyb3BfYWxsX3Rhc2s6IHJlcXVlc3RlZCBsaWR4IFsldV0KAAAAAAAAAAAAAAAAAGdldF9yZWZf
-dGFzazogaXN0YXNrLT5mbG93Y19pZCBbMHgleF0sIGlzdGFza19mYy0+Zmxvd2Nfc3RhdGUgWyV1
-XSwgaXN0YXNrX2ZjLT5mbG93Y19mb2lzY3NpX3Rhc2tfbGlkeCBbJXVdLCBsaWR4IFsldV0KAAAA
-AAAAAAAAcHJvY2Vzc190bWZfcmVzcG9uc2U6IGlzdGFza19mYy0+Zmxvd2NfYnVmLT5zY2hlZF9u
-b2RlLm5leHQgWzB4JXhdLCBpc3Rhc2tfZmMgWzB4JXhdLCBpc3Rhc2tfZmMtPmZsb3djX2lkIFsl
-dV0KAHByb2Nlc3NfdG1mX3Jlc3BvbnNlOiB1bHB0eHBsZW4xNiBbJXVdCgAAAAAAAAAAAHByb2Nl
-c3NfdG1mX3Jlc3BvbnNlOiBkZWxheV9wcm9jZXNzaW5nLCBvcCBbJXhdCgAAAAAAAAAAAAAAAAAA
-AABwcm9jZXNzX3RtZl9yZXNwb25zZTo6IHdyIG9wY29kZSBbMHgleF0KAAAAAAAAAABwcm9jZXNz
-X3RtZl9yZXNwb25zZTo6IHRhc2sgZnVuY3Rpb24gWzB4JXhdCgAAAABjYWxsaW5nIGRyb3BfdGFz
-aywgaXN0YXNrX2ZjLT5mbG93Y19mb2lzY3NpX3Rhc2tfbGlkeCAweCV4CgAAAAAAcHJjb2VzX2Rh
-dGFfaW5fcmVzcG9uc2U6IGNvbm4tPmZsb3djX2lkIFsweCV4XSwgaXN0YXNrX2ZjLT5mbG93Y19p
-ZCBbMHgleF0KAAAAAABERFAgZXJyb3IgZm9yIGNvbm4gWzB4JXhdLCBtYXNrIFsweCV4XQoAAAAA
-AAAAAABzZW5kX2Fib3J0X3JlcTogY3NrX2ZjLT5mbG93Y190eXBlIFsweCV4XSwgY3NrX2ZjLT5m
-bG93Y19pZCBbMHgleF0sIGNza19mYy0+Zmxvd2NfY3NvY2tfdGlkIFsweCV4XSwgdWxwdHhjaCBb
-JXVdLCBidWZmZXJlZCBbJXVdCgAAAAAAAAAAAAAAAAAAAABpc2NzaV9oZHI6IGJhZCB0Y2Igc3Rh
-dGUgdG8gcnggZGF0YSwgdGNiX2ZjLT5mbG93Y19pZCBbMHgleF0sIHRjYl9mYy0+Zmxvd2Nfc3Rh
-dGUgWzB4JXhdCgAAAAAAAAB3cmhfZm9pc2NzaV9ub2RlOiBub2RlX3dyLT5mbG93aWRfbGVuMTYg
-MiBbJXhdCgB3cmhfZm9pc2NzaV9jaGFwOiBpZF9sZW4gWyV4XSwgc2VjX2xlbiBbJXhdCgAAAAB3
-cmhfZm9pc2NzaV9jaGFwOiB0Z3RfaWRfbGVuIFsleF0sIHRndF9zZWNfbGVuIFsleF0KAAAAAAAA
-AAAAAAAAcGVlcl9jb246IGNza19mYyA9PiBmbG93aWQgWzB4JXhdLCBmbG93Y19idWYgWzB4JXhd
-CgAAAAAAAAAAAAAAAGZvaXNjc2lfY3RybDogc3Vib3AgWzB4JXhdLCBzZXNzX3R5cGVfdG9fZXJs
-IFsweCV4XSwgc2Vzc190eXBlIFsweCV4XQoAAAAAAAAAAAAASW52YWxpZCBvcGNvZGUgMHgleCBp
-biBjdHJsIHBhdGgKAAAAAAAAAAAAAAAAAAAAcnhfZGF0YS0+c2VxIFsweCV4XSwgcnhfZGF0YS0+
-bGVuIFsweCV4XSwgcnhfZGF0YS0+c3RhdHVzIFsweCV4XQoAAAAAAAAAAAAAAAAAAABjc2tfZmMt
-PmZsb3djX2Nzb2NrX29mZnNldCBbMHgleF0sIGRsZW4xNiBbMHgleF0KAAAAAAAAAAAAAAAAAAAA
-YWN0X2VzdDogZmxvd2NfZm9pc2NzaV9jb25uX2ZsYWdzIFsweCV4XQoAAAAAAAAAYWN0X2VzdGFi
-OiB0Y2JfZmMtPmZsb3djX2J1ZiBbMHgleF0sIHRjYl9mYy0+Zmxvd2NfdHlwZSBbMHgleF0gdGNi
-X2ZjLT5mbG93Y19zdGF0ZSBbMHgleF0sIG5wYWdlcyBbMHgleF0sIGZsb3djX3RwX3NuZF9tYXgg
-WzB4JXhdCgAAAAAAAAAAAAAAAAAAYWN0X2VzdGFiOiBhdGlkIFsweCV4XSwgdGlkIFsweCV4XSwg
-b3AgWzB4JXhdLCByY3ZfaXNuIFsweCV4XSwgc25kX2lzbiBbMHgleF0sIGNzb2NrLT5mbG93Y19z
-dGF0ZSBbMHgleF0sIHRjcF9vcHQgWzB4JXhdLCB0Y2JfZmMtPmZsb3djX2lkIFsweCV4XSAKAAAA
-AAAAAAAAAAAAAGNza19mYy0+Zmxvd2NfY3NvY2tfY29va2llIFsweCV4XSAKAAAAAAAAAAAAAAAA
-AG5ldGlmX2RvX2RoY3A6IHdyLT5wYXJhbS52bGFuaWQgWyV1XSwgbDJkZXZfZmMtPmZsb3djX25l
-dF9sMmRldl92bGFuZGV2IFsweCV4XQoAbmV0aWZfZG9fZGhjcDogcmwyZGV2X2ZjLT5mbG93Y19p
-ZCBbMHgleF0sIGRoY3BfY3R4dCBpbml0aWFsaXphdGlvbiBlcnJvcgoAAAAAAABsM2luNF9kZXZf
-Y29uZmlnOiB3ci0+cGFyYW0udmxhbmlkIFsldV0sIGwyZGV2X2ZjLT5mbG93Y19uZXRfbDJkZXZf
-dmxhbmRldiBbMHgleF0KAAAAAAAAAAAAAAAAAABuZXRfbDNpbjRfZGV2X2NvbmZpZzogbDJkZXZf
-ZmMtPmZsb3djX2lkIFsweCV4XSwgYWRkcmVzcyBhbHJlYWR5IHVzZWQgYnkgcG9ydCAlZAoAAAAA
-AAAAAAAAAAAAAABuZXRfbDNpbjRfZGV2X2NvbmZpZzogIGFkZHIgWzB4JXhdLCBtYXNrIFsweCV4
-XSwgZ3cgWzB4JXhdLCByZWZfY250IFsweCV4XSBpbiB1c2UKAAAAAAAAAAAAAAAAAAB3cmhfY2hu
-ZXRfaWZjb25mOiBsMmRldl9mYy0+Zmxvd2NfaWQgWzB4JXhdLCBsMmRldl9mYy0+Zmxvd2NfdHlw
-ZSBbJTB4XSwgaWZjb25mX3dyLT5zdWJvcCBbMHgleF0KAAAAAAAAAAAAAAAAAAAAd3JoX2NobmV0
-X2lmY29uZjogbDJkZXZfZmMtPmZsb3djX2lkIFsweCV4XSwgdW5rbm93biBzdWJvcCBbMHgleF0K
-AAAAAAAAAAAAAAAAAAB3cmhfY2huZXRfaWZjb25mOiBsMmRldl9mYy0+Zmxvd2NfaWQgWzB4JXhd
-LCByYyAlZAoAAAAAAAAAAAAAAAAAbmV0aWZfaXBfY29uZmxpY3RfdGltZXJfY2I6IGwyZGV2X2Zj
-LT5mbG93Y19pZCBbMHgleF0sIGluZGV2Y3R4dC0+c3RhdGUgWyVkXSwgaW5kZXZjdHh0LT5yZXRy
-eV9jbnQgWyVkXQoAAAAAAAAAAG5ldGlmX2lwX2NvbmZsaWN0X3RpbWVyX2NiOiBsMmRldl9mYy0+
-Zmxvd2NfaWQgWzB4JXhdLCBpbmRldmN0eHQgWzB4JXhdLCBpbiBmcmVlIHN0YXRlCgAAAAAAAAAA
-AGlmYWNlX2NtZF9oYW5kbGVyOiBmYyBbMHgleF0sIGZjLT5mbG93Y19pZCBbMHgleF0sIGZjLT5m
-bG93Y190eXBlIFsweCV4XSwgcCBbMHgleF0sIGxlbjE2IFsldV0sIGxvYyBbMHgleF0KAAAAAABp
-ZmFjZV9jbWRfaGFuZGxlcjpsMmRldl9mYyBbMHgleF0sIGwyZGV2X2ZjLT5mbG93Y19pZCBbMHgl
-eF0sIGwyZGV2LT5mbG93Y190eXBlIFsldV0sIGwyZGV2X2ZjLT5mbG93Y19uZXRfbDJkZXZfZmxh
-Z3MgWyUweF0KAAAAAGZvaXNjc2lfaWZhY2VfY21kX2hhbmRsZXI6IGwyZGV2X2ZjLT5mbG93Y19u
-ZXRfbDJkZXZfZmxhZ3MgY2hhbmdlZCBmcm9tIFslMHhdIHRvIFslMHhdCgAAAAAAAAAAAGNobmV0
-X2wyZGV2X3VwOiBtYiBbMHgleF0sIGRlZmVycmVkLCBzdGF0ZSBbMHgleF0sIHBvcnQgWzB4JXhd
-CgB2aWlkIFsweCV4XSBwb3J0IFsweCV4XSwgbWFjLWlkIFslMDJ4OiUwMng6JTAyeDolMDJ4OiUw
-Mng6JTAyeF0gcnNzX3NpemUgJXUKAAAAAHZpIHR5cGUgWzB4JXhdLCBwb3J0IFsweCV4XSwgZmxv
-d2Nfc2dlX2VxaWQgWzB4JXhdLCBmbG93Y19zZ2VfZXFpZCBbMHgleF0sIGZsb3djX3NnZV9lcWNy
-ICV1CgAAAG5ldGlmX3NldF9tYWM6IG1iX3NjcmF0Y2ggWzB4JXhdLCBwb3J0IFsweCV4XQoAAG5l
-dGlmX3NldF9yeG1vZGU6IGwyZGV2X2ZjLT5mbG93Y19uZXRfbDJkZXZfbXR1IFsldV0sIG1iX3Nj
-cmF0Y2ggWzB4JXhdLCBwb3J0IFsweCV4XQoAAAAAAAAAAAAAAGNobmV0X2wyZGV2X3VwOiBsMmRl
-dl9tYWMgWyUwMng6JTAyeDolMDJ4OiUwMng6JTAyeDolMDJ4XSwgZmxvd2NfbmV0X2wyZGV2X2Zs
-YWdzIFsweCV4XQoAAAAAAAAAAGNobmV0X2wyZGV2X3VwOiBtYl9sb2MgWzB4JXhdLCBtYl9vcmln
-IFsweCV4XSwgcmMgWyVkXQoAAAAAAAAAAABjaG5ldF9sMmRldl91cF9tYl9jYjogcmMgWyVkXSwg
-cG9ydCBbJXVdLCBzdGF0ZSBbJXVdLCBjb29raWUgWyV1XQoAAAAAAAAAAAAAAAAAAGNobmV0X3ht
-aXQ6IGwyZGV2X2ZjLT5mbG93Y19pZCBbMHgleF0sIHZsYW5pZCBbJXVdLCBMNENoa0Rpc2FibGVf
-dG9faVZsYW5UYWcgWzB4JXhdCgAAAAAAAAAAAAAAAGNobmV0X3htaXQ6IHR4X2RsZW4gWyV1XQoA
-AAAAAAAAY2huZXRfeG1pdDogYWxyZWFkeSBzY2hlZHVsZWQKAABjaG5ldF94bWl0OiBsMmRldl9m
-Yy0+Zmxvd2NpZCBbMHgleF0sIG5idWYgWzB4JXhdIGluIGRlbGF5ZWRfcHJvY2Vzc2luZwoAAAAA
-AAAAAGRoY3BfcHJvY2Vzc19jYjogbDJkZXZfZmMtPmZsb3djX2lkIFsweCV4XSwgZGhjdHh0LT5z
-dGF0ZSBbJTB4XSwgZGhjdHh0LT5ydHJ5X2NudCBbJXVdCgAAAAAAAAAAAGRoY3BfdGltZXJfY2I6
-IERIQ1BESVNDT1ZFUiBzZW50LCBidXQgbm8gcmVwbHkgZnJvbSBhbnkgcG9zc2libGUgc2VydmVy
-IG9uIHRoZSBuZXR3b3JrLiBSZXRyeWluZyBhZ2FpbgoAAAAAAAAAAABkaGNwX3RpbWVyX2NiOiBs
-MmRldl9mYy0+Zmxvd2NfaWQgWzB4JXhdLCBzZW5kaW5nIERIQ1BESVNDT1ZFUiBmb3IgZGhjdHh0
-IFsweCV4XSBvbiBwaWQgWyVkXQoAAABkaGNwX3RpbWVyX2NiOiBsMmRldl9mYy0+Zmxvd2NfaWQg
-WzB4JXhdLCBESENQT0ZGRVIgcmVjZWl2ZWQgZm9yIGRoY3R4dCBbJXhdIHBpZCBbJWRdCgAAAAAA
-AAAAAABkaGNwX3RpbWVyX2NiOiBsMmRldl9mYy0+Zmxvd2NfaWQgWzB4JXhdLCAgREhDUEFDSyBy
-ZWNlaXZlZCBmb3IgZGhjdHh0IFsleF0sIHBpZCBbJWRdCgAAAAAAAAAAAABkaGNwX3RpbWVyX2Ni
-OiBsMmRldl9mYy0+Zmxvd2NfaWQgWzB4JXhdLCBkaGN0eHQtPmlwYWRkciBbMHgleF0KAAAAAAAA
-AAAAAAAAAAAAAGRoY3BfdGltZXJfY2I6IGwyZGV2X2ZjLT5mbG93Y19pZCBbMHgleF0sIG5vIHJl
-cGx5IGZyb20gZGhjcCBzZXJ2ZXIsIHRpbWluZyBvdXQKAAAAAAAAAAAAAAAAAAAAAHBbJXVdIGNo
-YW5naW5nIHNwZWVkIGZyb20gJSN4IHRvICUjeAoAAAAAAAAAAAAAAHBbJXVdIGJjbTg0ODM0IGxp
-bmsgc3RhdHVzIGNoYW5nZSBVUAoAAAAAAAAAAAAAAHBbJXVdIGJjbTg0ODM0IGxpbmsgc3RhdHVz
-IGNoYW5nZSBET1dOCgAAAAAAAAAAAGh3X2NsNDVfdXBkX3NwZF9hZHYgJSN4CgAAAAAAAAAAcFsl
-dV0gYXExMjAyIGxpbmsgc3RhdHVzIGNoYW5nZSBVUAoAAAAAAAAAAAAAAAAAcFsldV0gUEhZIE9W
-RVJIRUFURUQgLSBmb3JjZWQgcG93ZXIgZG93biAodGVtcD0lZCkKAAAAAAAAAAAAAAAAAHBbJXVd
-IGFxMTIwMiBsaW5rIHN0YXR1cyBjaGFuZ2UgRE9XTgoAAAAAAAAAAAAAAG1vZHVsZVsldV06IHBv
-cnQgbW9kdWxlIGluc2VydGVkIGFuZCByZWFkeQoAAAAAAG1vZHVsZVsldV06IHBvcnQgbW9kdWxl
-IHJlbW92ZWQKAAAAAAAAAAAAAAAAAAAAAG1vZHVsZVsldV06IHVua25vd24gbW9kdWxlIGlkZW50
-aWZpZXIgMHglMDJ4CgAAAG1vZHVsZVsldV06IGdwaW8gJXUgdHJhbnMgMTBHIDB4JTAyeCAxRyAw
-eCUwMnggKGxlbmd0aCAldSkgY2FibGUgMHglMDJ4IChsZW5ndGggJXUpIG1vZHVsZV90eXBlIDB4
-JTAyeAoAAAAAAAAAAABjeDRfY3JfcGRvd25bJXVdOiBlbiAldSB2aV9lbl9jbnQgJXUKAAAAAAAA
-AAAAAABpbml0X3BvcnRbJXVdOiAgbGluayBzdGF0dXMgMHgleCBtb2RfdHlwZSAweCV4IHZpX2Vu
-X2NudCAldSByeF9sb3MgJXUgaHNzX3NpZ2RldCAldSBwb3J0X2NmZy5zaWdkZXQgJXUKAAAAAAAA
-AAAAaW5pdF9wb3J0WyV1XTogIGxpbmsgc3RhdHVzIDB4JXgKAAAAAAAAAAAAAAAAAAAAaW5pdF9w
-b3J0WyV1XTogIGxpbmsgc3RhdHVzIDB4JXgKAAAAAAAAAAAAAAAAAAAAaW5pdF9wb3J0WyV1XTog
-IGxpbmsgc3RhdHVzIDB4JXgKAAAAAAAAAAAAAAAAAAAAaW5pdF9wb3J0WyV1XTogIGxpbmsgc3Rh
-dHVzIDB4JXgKAAAAAAAAAAAAAAAAAAAAaW5pdF9wb3J0WyV1XTogIGxpbmsgc3RhdHVzIDB4JXgK
-AAAAAAAAAAAAAAAAAAAAbmV3IG1vZHVsZSBuZXcgc3BlZWQgKHRlY2hfdHlwZT0lI3gpCgAAAAAA
-AAAAAAAAY3JfbW9kdWxlX3N0YXR1c1sldV06IHR5cGUgY2hhbmdlZCBmcm9tIDB4JTAyeCB0byB0
-eXBlIDB4JTAyeCBzdHlwZSAweCUwMngKAAAAAABjeDRfY3JbJXVdOiByZXQgJWQgc3RhdHVzICV1
-IHhnbV9scyAweCUwOHgKAAAAAAB3YXRjaGRvZyBjbWQgaGFuZGxlciAodGltZSAldSBhY3Rpb24g
-JXUpCgAAAAAAAABXQVRDSERPRzogZGV2aWNlIHNodXRkb3duCgAAAAAAAFdBVENIRE9HOiBieXBh
-c3MgdGltZW91dAoAAAAAAAAAV0FUQ0hET0c6IEZMUiAtIG5vdCBpbXBsZW1lbnRlZCB5ZXQKAAAA
-AAAAAAAAAAAAZmlsdGVyOiBwb3JncmFtbWluZyB0aWQgJXUgKGxlIHRjYW0gaW5kZXggJXUpLi4u
-CgAAAAAAAAAAAAAAAAAAAGZpbHRlcjogcmVxdWVzdGluZyBjb21wbGV0aW9uLi4uCgAAAAAAAAAA
-AAAAAAAAAEZDT0UgRnJlZTogc3RpbGwgeWllbGRlZCB3aGVuIGZyZWVpbmcuLi5mbG93Y19pZCAl
-eCBmbG93Y19mbGFncyAleCAKAAAAAAAAAAAAAAAAcG9ydCAlZCBwcmlvciAlZCBzZWxlY3QgJWQg
-cHJvdG9jb2xJRCAweCUwNHgKAAAAcG9ydCAlZCBzZXQgcGZjX2VuID0gMHgleAoAAAAAAABwb3J0
-ICVkIHNldCBwZ2lkXzBfNyA9IDB4JTA4eCBwZ19wZXJjZW50YWdlIDB4JTA4eF8lMDh4IG51bV90
-Y3Nfc3VwcG9ydGVkICVkCgAAAHBvcnQgJWQgc2V0IHBmY19lbiA9IDB4JXgKAAAAAAAARkNvRSBE
-RFAgZmFpbGVkIDogb3hfaWQgMHgleCByeF9pZCAweCV4CgAAAAAAAAAARkNvRSBERFAgZmFpbGVk
-IDogRGRwUmVwb3J0IDB4JXggRGRwVmFsaWQgMHgleAoAUFJMSSBSc3AgdGltZWRvdXQgOiBmbG93
-Y19pZCAweCV4IG94X2lkIDB4JXggcnhfaWQgMHgleCAKAAAAAAAAAGNhbm5vdCBhbGxvY2F0ZSBv
-ZmZsb2FkZWQgZmlsdGVyIGNvbm5lY3Rpb24KAAAAAGNhbm5vdCBhbGxvY2F0ZSBvZmZsb2FkZWQg
-ZmlsdGVyIElQdjYgY29ubmVjdGlvbgoAAAAAAAAAAAAAAAAAAAB1b1sldV06IHdyICVwIHdyLT5s
-ZW5ndGggJXUgbXNzICV1CgAAAAAAAAAAAAAAAAB1b1sldV06IGYgMHglMDh4IGRzdF9jcGwgMHgl
-MDh4IHNyY19jcGwgMHglMDh4IGltbWRsZW4xNiAldQoAAAAAdW9bJXVdOiBsYXN0IG1zcyAldQoA
-AAAAAAAAAAAAAAB1b1sldV06ICFsYXN0IHdyLT5zY2hlZHBrdHNpemUgJXUKAAAAAAAAAAAAAAAA
-AABpbnZhbGlkIGJ1ZmZlciBncm91cFsldV0gY29uZmlndXJhdGlvbjogbXR1ICV1IGx3bSAldSBo
-d20gJXUgZHdtICV1CgAAAAAAAAAAAAAAAGZjICV1IHZmICV1IGdvdCBpdmY9MHgleCxyYW5nZTog
-JSN4LSUjeCAoJXUvJXUgdXNlZCkKAAAAAAAAAAAAAABWSSAldSBjYW5ub3QgZ2V0IFJTUyBzbGlj
-ZTogTm8gbW9yZSBzbGljZXMgYXZhaWxhYmxlICh1c2VkICV1LyV1KQoAAAAAAAAAAAAAAAAAAHBm
-biAldSB2Zm4gJXUgd2l0aCBwb3J0IG1hc2sgMHgleCBjYW5ub3QgYWNjZXNzIHBvcnQgJXUsIHJl
-dCAlZAoAAAAAAAAAAAAAAAAAAAAAcGZuICV1IHZmbiAldSBjb3VsZCBub3QgYWxsb2NhdGUgdmlp
-ZCwgcmV0ICVkCgAAcGZuICV1IHZmbiAldSBjb3VsZCBtYXAgdmlpZCAgMHgleCB0byBmbG93Yywg
-cmV0ICVkCgAAAAAAAAAAAAAAAHBmbiAldSB2Zm4gJXUgY291bGQgbm90IGFsbG9jYXRlIHV3aXJl
-IGZ1bmMgJWQgbWFjIGFkZHIsIHJldCAlZAoAAAAAAAAAAAAAAAAAAAAAbWlpX2Fkdl9mY1sldV06
-IHJjYXBzIDB4JXgKAAAAAABtaWlfYW5yZXN0YXJ0WyV1XTogYWNhcHMgMHgleAoAAG1paV9mb3Jj
-ZV9zcGVlZFsldV06IHJjYXBzIDB4JXgKAAAAAAAAAAAAAAAAAAAAAG1paV9saW5rX3N0YXR1c1sl
-dV06IGJtc3IgMHglMDh4IHN0YXR1cyAldSB4Z21fbHMgMHglMDh4CgAAAAAAAABwb3J0X2NtZF9o
-YW5kbGVyOiB1bmtub3duIHUuZGNiLnR5cGUgMHgleAoAAAAAAABwb3J0WyV1OjB4JTAyeDoweCUw
-MnhdOiBwaHkgcmVzZXQgbm90IGF2YWlsYWJsZQoAAAAAAAAAAAAAAAAAAAAAcG9ydFsldToweCUw
-Mng6MHglMDJ4XTogdW5rbm93biBhY3Rpb24gMHgleAoAAAAAcG9ydFsldToweCUwMng6MHglMDJ4
-XTogdW5rbm93biByZWFkIGFjdGlvbiAweCV4CgAAAAAAAAAAAAAAAAAAAGNoX2NsX3JhdGVbJXUv
-JXVdOiBjYXBwZWQgY2xhc3MgcmF0ZSBmcm9tIHJlcXVlc3RlZCAldSB0byBjb25maWd1cmVkIGNo
-YW5uZWwgcmF0ZSAldQoAAAAAAAAAAAAAAGNoX2NsX3JhdGVbJXUvJXVdOiBpbmNyZWFzZWQgZGVm
-aWNpdF9pbmNyIGZyb20gcmVxdWVzdGVkICV1IHRvIHJlcXVpcmVkIG1pbiBvZiAldTsgcmF0ZSAl
-dSAoZWZmICV1KSBkZWZpY2l0X21heCAldQoAAAAAAAAAAAAAAAAAcGt0c2NoZWQgY2hhbm5lbCAl
-dSBzZXRzIHNwZWVkIChmcm9tICV1KSB0byAldSBrYnBzCgAAAAAAAAAAAAAAAHBvcnRfY21kX2hh
-bmRsZXI6IHVua25vd24gdS5kY2IudHlwZSAweCV4CgAAAAAAAGh3X2kyY190cmFuc2FjdGlvbjog
-bmRhdGEgJXUgYWRkcl9vcCAweCV4IGRhdGFbMF0gMHgleCBkaWZmICV1CgBod19pMmNfdHJhbnNh
-Y3Rpb246IG5kYXRhICV1IGFkZHJfb3AgMHgleCBkYXRhWzBdIDB4JXggZGlmZiAldSBkcG9zICV1
-IGNvbnQgJXUgZmFpbGVkIHdpdGggZXJyICVkCgAAAAAAAAAAAAAAAAAAaTJjIHRyYW5zYWN0aW9u
-IGZhaWxlZCB0byBjb21wbGV0ZQoAAAAAAAAAAAAAAAAAaTJjIGVycm9yIGNhdXNlZCBieSBtb2R1
-bGUgdW5wbHVnCgAAAAAAAAAAAAAAAAAAUVNGUCBtb2R1bGUgdW5wbHVnIC0gcmVpbml0aWFsaXpp
-bmcgcnhfbG9zICB0byAweGZmCgAAAAAAAAAAAAAAAGdwaW9fcXNmcF9tb2R1bGVfdXBkYXRlOiBj
-aGFuZ2VkIHJ4X2xvcyBmcm9tIDB4JXggdG8gMHgleAoAAAAAAABncGlvX3FzZnBfbW9kdWxlX3Vw
-ZGF0ZTogY2hhbmdlZCB0eF9kaXMgZnJvbSAweCV4IHRvIDB4JXgKAAAAAAAAc2VuZHRvIHBlbmRp
-bmc6IHdyX3BlbmQgJXAgZm9yIHBvcnQgJXUsICB3YW50IHRvIHNlbmQgdG8gcG9ydCAldQoAAAAA
-AAAAAAAAAAAAAABzZW5kdG86Zmxvd2NpZCAldQoAAAAAAAAAAAAAAAAAAGNwbF9lcnJfbm90aWZ5
-OiB0aWQgJXUgY3BsIDB4JTA4eCUwOHgKAAAAAAAAAAAAAGNwbF9lcnJfbm90aWZ5OiB0aWQgJXUg
-Y3BsIDB4JTA4eCUwOHggMHglMDh4JTA4eAoAAAAAAAAAAAAAAAAAAABjcGxfZXJyX25vdGlmeTog
-dGlkICV1IGxlbiAldQoAAEZDT0UgRnJlZTogc3RpbGwgeWllbGRlZCB3aGVuIGZyZWVpbmcuLi5m
-bG93Y19pZCAleCBmbG93Y19mbGFncyAleCAKAAAAAAAAAAAAAAAARkNPRSBCUCBXUiBFUlI6IFdS
-IHdpdGggY29va2llICV4JXggZXJyb3JlZCBiYWNrIAoAAAAAAAAAAAAAAAAAAHNjc2lfYWJvcnQ6
-IEVudGVyaW5nIEFib3J0X3Rhc2sKAAAAAAAAAAAAAAAAAAAAAHNjc2lfYWJvcnQ6OiB3ci0+aXFp
-ZCBbMHgleF0sIGlzdGFza19mYy0+Zmxvd2Nfc2dlX2lxaWQgWzB4JXhdCgBzY3NpX2Fib3J0Ojog
-bHVuX2lkeCBbMHgleF0KAAAAAHNjc2lfYWJvcnQ6IHJlZiB0YXNrIG5vdCBvdXRzdGFuZGluZwoA
-AAAAAAAAAAAAAHNjc2lfYWJvcnQ6IHJlZiB0YXNrIGZvdW5kIGl0dCBbMHgleF0gY29va2llIFsw
-eCVwXQoAAAAAAAAAAAAAAABzY3NpX2Fib3J0OiBzcmVxLT5vcGNvZGUgWzB4JXhdIGZsYWdzIFsw
-eCV4XQoAAABzY3NpX2Fib3J0OiB0YXNrX2lkeCBbJXVdLCBpdHQgWzB4JXhdCgAAAAAAAAAAAABh
-Ym9ydC9jbG9zZSBXUiB3aXRoIGNvb2tpZSAweCVseCB3YXMgaXNzdWVkIG9uIHNzbiAweCV4IGlu
-IHdyb25nIHN0YXRlIDB4JXgKAAAAAGFib3J0IFdSIG9uIHNzbiAweCV4IGRpZCBub3QgZmluZCBX
-UiB3aXRoIGNvb2tpZSAweCV4JXgKAAAAAAAAAABjbG9zZSBXUiB3aXRoIGNvb2tpZSAweCVseCBv
-biBzc24gMHgleDtkaWQgbm90IGZpbmQgV1Igd2l0aCBjb29raWUgMHglbHgKAAAAAAAAAGFib3J0
-IFdSIG9uIHNzbiAweCV4IHdhcyBpc3N1ZWQgb24geGNoZyAweCV4IHdpdGggcnhfaWQgMHgleCBp
-biB3cm9uZyBzdGF0ZSAweCV4CgAAAAAAAAAAAAAAAAAAAHdyaF9mb2lzY3NpX3Njc2lfZXJyOiB3
-ciBbMHgleF0sIHF1ZXVlZCBvbiBmbG93YyBbMHgleF0sIGZpZm8gWzB4JXhdIGJ5dGVzX2F2YWls
-YWJsZQoAAAAAAAAAAAAAAG5ldF9sMmRldl9ub3RpZnk6IEV2ZW50IG9uIGwyZGV2X2ZjLT5mbG93
-Y19pZCBbMHgleF0sIHBvcnQgWyVkXSwgZXZlbnQgWzB4JXhdLCB1bHB0eGNoIFsldV0sIGNsYXNz
-IFsweCV4XSwgcHJpb3JpdHkgWzB4JXhdCgAAAAAAcG9ydCAlZCBhcHBsSUQgMHglMDR4IG91aSAw
-eCUwNnggdXNlcl9wcmlvciAlZAoAc2VuZCBkY2J4IGluZm86Zmxvd2NpZCAldQoAAAAAAABzZW5k
-IGRjYnggY29tcGxldGUKAAAAAAAAAAAAAAAAAERDQlggcG9ydCAldSBhbGxfc3luY2QgPSAldQoA
-AAAAY2huZXRfbDJ0X3VwZGF0ZTogbDJkZXZfZmMgWzB4JXhdLCBsMmRldl9mYy0+Zmxvd2NfaWQg
-WyV1XSBsMmRldl9mYy0+Zmxvd2NfZmxhZ3MgWzB4JXhdCgAAAAAAAAAAY2huZXRfbDJ0X3VwZGF0
-ZTogbDJkZXZfZmMtPmZsb3djX2lkIFsldV0gYWxyZWFkeSBzY2hlZHVsZWQKAAAAAGNobmV0X2wy
-dF91cGRhdGU6IGluIGRlbGF5ZWRfcHJvY2Vzc2luZywgbDJ0ZW50IFslMDh4XQoAAAAAAAAAAABj
-aG5ldF9sMnRfdXBkYXRlOiBsMnRfdXBkYXRlIHJlcXVlc3Qgc2VudCBsMnRlbnQgWyUwOHhdLCBs
-MnRlbnQtPmlkeCBbJWRdLCBsMnRlbnQtPnZsYW4gWyVkXQoAAABuZXRpZl9wcm9jZXNzX2RoY3A6
-IGwyZGV2X2ZjLT5mbG93Y19pZCBbMHgleF0sIHByb2Nlc3NpbmcsIG9wdF9sZW4gJXUKAAAAAAAA
-AAAAAGNobmV0X2RoY3BfcmVjdjogdmxhbmlkIFsldV0sIGwyZGV2X3BpZF9mYy0+Zmxvd2NfbmV0
-X2wyZGV2X3ZsYW5kZXYgWzB4JXhdLCBsMmRldl9mYyBbMHgleF0KAAAAAGNobmV0X2RoY3BfcmVj
-djogbDJkZXZfZmMtPmZsb3djX2lkIFsweCV4XSwgZGhjdHh0LT5zdGF0ZSBbJWRdLCBtYWxhY2lv
-dXMgZGhjcCByZWN2IGZvciBubyByZXF1ZXN0CgAAAAAAAAAAAAAAAABkaGN0eHQtPnN0YXRlIDog
-JWQAAAAAAAAAAAAAAAAAAGwyZGV2X2ZjLT5mbG93Y19pZCBbMHgleF0sIEJhZCBESENQIGNvb2tp
-ZSByZWNpZXZlZCwgYWJvcnRpbmcKAABOX1BPUlQgMHgleCV4JXggcmVqZWN0ZWQgUExPR0kgd2l0
-aCByZWFzb24gY29kZSAleAoAAAAAAAAAAAAAAAAAQUJUUyB3aGlsZSBhd2FpdGluZyBQUkxJIFJz
-cDogZmxvd2NfaWQgMHgleCBveF9pZCAweCV4IHJ4X2lkIDB4JXggCgAAAAAAAAAAAAAAAABBQlRT
-IGZha2UgUnNwOiBsb2MgMHgleCBveF9pZCAweCV4IHJ4X2lkIDB4JXgKAABjYW5ub3QgYWxsb2Nh
-dGUgUE9GQ09FIGZpbHRlciBjb25uZWN0aW9uIGZvciB4X2lkICV4IAoAAAAAAAAAAAAAYWN0X29w
-ZW5fcnBsOiBhdGlkIFsweCV4XSwgdGlkIFsweCV4XSwgb3AgWzB4JXhdLCBzdGF0dXMgWzB4JXhd
-CgAAAAAAAAAAAAAAAAAAAABhY3Rfb3Blbl9ycGw6IGNza19mYy0+dGNiX3N0YXRlIFsweCV4XSwg
-Y3NrX2ZjLT5mbGFncyBbMHgleF0sIHRjYl9mYy0+Zmxvd2Nfc3RhdGUgWzB4JXhdCgAAAAAAAABz
-ZW5kX2Fib3J0X3JwbDogY3NrX2ZjLT5mbG93Y190eXBlIFsweCV4XSwgY3NrX2ZjLT5mbG93Y19p
-ZCBbMHgleF0sIGNza19mYy0+Zmxvd2NfY3NvY2tfdGlkIFsweCV4XSwgdWxwdHhjaCBbJXVdLCBi
-dWZmZXJlZCBbJXVdCgAAAAAAAAAAAAAAAAAAAAB3cmhfb2ZsZF90Y3BfY2xvc2VfY29uX3JlcGx5
-OiB0Y2JfZmMtPmZsb3djX2lkIFsweCV4XSwgdGNiX2ZjLT5mbG93Y190eXBlIFsweCV4XSwgbGVu
-MTYgWyV1XSwgbG9jIFsldV0KAAAAAAAAAAAAd3JoX29mbGRfdGNwX2Nsb3NlX2Nvbl9yZXBseTog
-cnBsLT5vcF9UaWQgWzB4JXhdLCBycGw+c3RhdHVzIFsweCV4XSwgcnBsLT5zbmRfbnh0IFsweCV4
-XSwgcnBsLT5yY3Zfbnh0IFsweCV4XQoAAHRjcF9hYm9ydF9ycGxfcnNzOiB0aWQgWzB4JXhdLCBz
-dGF0dXMgWzB4JXhdCgAAAHRjcF9hYm9ydF9yZXFfcnNzOiB0aWQgWzB4JXhdLCBzdGF0dXMgWzB4
-JXhdCgAAAHBrdHNjaGVkX2NsX3JsWyV1OiV1XTogbW9kZSB8IHVuaXQgfCByYXRlIDB4JTA2eCBt
-aW4gJXUgbWF4ICV1IHBrdHNpemUgJXUKAAAAAAAAcGFyYW1fZG1hcVsweCV4OjB4JXhdOiBkbWFx
-IDB4JXggcmVhZCAldSBwZiAldSByZXQgJWQKAAAAAAAAAAAAAGh3X2JjbTg0ODM0X2xvYWRzZXF1
-ZW5jZTogU3RhcnRlZAoAAAAAAAAAAAAAAAAAAGh3X2JjbTg0ODM0X2xvYWRzZXF1ZW5jZTogVXBs
-b2FkIGltYWdlIHRvIFBIWSBvbi1jaGlwIG1lbW9yCgAAAABod19iY204NDgzNF9sb2Fkc2VxdWVu
-Y2U6IGRvbmUgbG9hZGluZyBpbWFnZSAoaSA9ICV1KQoAAAAAAAAAAAAARkxBU0ggbm90IHJlYWR5
-OiBpICV1IG52clJlZyAlI3gKAAAAAAAAAAAAAAAAAAAAQVFfRkxBU0hfUmVhZHkgLSBUaW1lb3V0
-ICgxKQoAAABBUV9GTEFTSF9SZWFkeSAtIFRpbWVvdXQgKDIpCgAAAAlBUV9SZXR1cm5Db250cm9s
-T2ZGTEFTSAoAAAAAAAAAcGh5OiBmYWlsZWQgdG8gYWxsb2NhdGVkIG1lbW9yeSBmb3IgcGh5IGZ3
-IGZpbGUsIHJldCAlZAoAAAAAAAAAAFBIWSBGVyB2ZXJzaW9uIGluZm9ybWF0aW9uIG5vdCBhdmFp
-bGFibGUgZm9yIHRoaXMgdGVjaG5vbG9neQoAAABsZSBjb25maWd1cmF0aW9uOiBuZW50cmllcyAl
-dSByb3V0ZSAldSBjbGlwICV1IGZpbHRlciAldSBhY3RpdmUgJXUgc2VydmVyICV1IGhhc2ggJXUK
-AAAAAAAAAAAAAABsZSBjb25maWd1cmF0aW9uOiBuZW50cmllcyAldSByb3V0ZSAldSBjbGlwICV1
-IGZpbHRlciAldSBzZXJ2ZXIgJXUgYWN0aXZlICV1IGhhc2ggJXUgbnNlcnZlcnNyYW0gJXUKAAAA
-AAAAAAAAAAAAY2ZfcGFyc2U6IGZpbGUgbWVtdHlwZSAweCV4IG1lbWFkZHIgMHgleCBtYXBwZWQg
-QCAlcDoKAAAAAAAAAAAAAGNvbmZpZ3VyZWQgd2l0aCBjYXBzIG5ibXxsaW5rIDB4JTA4eCBzd2l0
-Y2h8bmljIDB4JTA4eCB0b2V8cmRtYSAweCUwOHggaXNjc2l8ZmNvZSAweCUwOHgKAAAAAAAAAG5l
-dCBWSSBhbGxvY2F0aW9uIGZhaWxlZCBmb3IgZmNfaWQgJXUgd2l0aCBlcnJvciAlZAoAAAAAAAAA
-AAAAAABuZXQgVkkgbWFjIGFkZHJlc3MgcHJvZ3JhbW1pbmcgZmFpbGVkIGZvciBmY19pZCAldSB3
-aXRoIGVycm9yICVkCgAAAAAAAAAAAAAAAAAAAG5ldCBWSSByeG1vZGUgcHJvZ3JhbW1pbmcgZmFp
-bGVkIGZvciBmY19pZCAldSB3aXRoIGVycm9yICVkCgAAAABuZXQgVkkgcnNzIGluZGlyZWN0aW9u
-IHRhYmxlIHByb2dyYW1taW5nIGZvciBmY19pZCAldSBmYWlsZWQgd2l0aCBlcnJvciAlZAoAAAAA
-AG5ldCBWSSByc3MgY29uZmlnIGNvbW1hbmQgZmFpbGVkIGZvciBmY19pZCAldSB3aXRoIGVycm9y
-ICVkCgAAAABuZXQgVkkgY29tbWFuZCBmYWlsZWQgZm9yIGZjX2lkICV1IHdpdGggZXJyb3IgJWQK
-AAAAAAAAAAAAAAAAAAAAbGUgaW5pdGlhbGl6YXRpb246IG5lbnRyaWVzICV1IHJvdXRlICV1IGNs
-aXAgJXUgZmlsdGVyICV1IGFjdGl2ZSAldSBzZXJ2ZXIgJXUgaGFzaCAldQoAAAAAAAAAAAAAbGUg
-aW5pdGlhbGl6YXRpb246IG5lbnRyaWVzICV1IHJvdXRlICV1IGNsaXAgJXUgZmlsdGVyICV1IHNl
-cnZlciAldSBhY3RpdmUgJXUgaGFzaCAldSBuc2VydmVyc3JhbSAldQoAAAAAAAAAAAAAAHRwIG1l
-bSBkaXN0cmlidXRpb25bJSVdIHBtdHggJXUgcG1yeCAldSBkZHAgJXUgZGRwX2lzY3NpICV1IHN0
-YWcgJXUgcGJsICV1IHJxICV1IHJxdWRwICV1CgAAAAAAAFRQX1BNTV9SWF9QQUdFX1NJWkUgJXUg
-aXMgbm90IHN1cHBvcnRlZAoAAAAAAAAAAFRQX1BNTV9UWF9QQUdFX1NJWkUgJXUgaXMgbm90IHN1
-cHBvcnRlZAoAAAAAAAAAAHBwbWF4IFsldV0sIGJpdHMgWyV1XSwgRldfSVNDU0lfUEFHRVBPRF9U
-QUdfSURYX01BWF9TSVpFIFsldV0KAABkZWZhdWx0IHRhZ21hc2sgWzB4JTB4XQoAAAAAAAAAAHBy
-b2dyYW1tZWQgdGFnbWFzayBbMHglMHhdCgAAAAAAY3hjbmljLT5kZHBfaW5mby5sbGltaXQgWzB4
-JTB4XSwgY3hjbmljLT5kZHBfaW5mby51bGltaXQgWzB4JTB4XSwgY3hjbmljLT5kZHBfaW5mby5z
-aXplIFsweCUweF0KAAAAAAAAAAAAAAAAAAAAAGN4Y25pYy0+ZGRwX2luZm8ubWF4X3R4c3ogWzB4
-JTB4XSBjeGNuaWMtPmRkcF9pbmZvLm1heF9yeHN6IFsweCUweF0gaW9zaXplIFsweCUweF0KAAAA
-AAAAAAAAAAAAAHBwbWF4IFsldV0sIGlkeF9iaXRzIFsldV0sIGlkeF9tYXNrIFsweCUweF0sIHJl
-c3ZkX3RhZ19tYXNrIFsweCUweF0sIHRhZ21hc2sgWzB4JTB4XQoAAAAAAAAAAAAAAHRhZyBpdHQg
-MHglMHgsIGJpdHMgJXUsIGFnZSAweCUweCwgYml0cyAldQoAAAAAAGN4Y25pYy0+aXNjc2lfcHBt
-IFsweCUweF0KAAAAAAAAc2NzaV9wbGRfc2l6ZSBbJXVdLCBBTElHTihzY3NpX3BsZF9zaXplLCAx
-NikgWyV1XQoAAAAAAAAAAAAAAAAAAG1heF9wcG9kX3pvbmVzIFsldV0KAAAAAAAAAAAAAAAAbDJk
-ZXZfZmMtPmZsb3djX2lkIFsldV0sIGwyZGMtPnBmbiBbJXVdLCBsMmRjLT52Zm4gWyV1XSwgbDJk
-Yy0+bHBvcnQgWyV1XSwgbDJkZXZfZmMtPmZsb3dpZCBbJXVdIGwyZGMtPnR4X2NoIFsldV0sIGRl
-di52cGQucG9ydHZlYyBbJXhdCgAAAAAAAAAAcG9ydHZlYyBbJXVdCgAAAGN4Y25pY19kZXZpY2Vf
-aW5pdDogY3hjbmljX2RldmljZV9pbml0X2RvbmUgWyV1XSwgZGV2LnJlcy5mb2lzY3NpX250YXNr
-cyBbJXVdLCBkZXYucmVzLmZvaXNjc2lfbnNlc3MgWyV1XSwgZGV2LnJlcy5uY3NvY2sgWyV1XSwg
-ZGV2LnJlcy5mb2lzY3NpX25pbml0IFsldV0KAAAAAABhcnBfdGFibGVfc2l6ZSBbJXVdLCBhcnBf
-dGJsX2JtX3NpemUgWyV1XSwgbnN0YWNrX25sMnRfYm1fc2l6ZSBbJXVdLCBvZmxkX3Nwb3J0X2Jt
-X3NpemUgWyV1XQoAAABpc2NzaV9uc2Vzc19ibV9zaXplIFsldV0sIGNzb2NrX2JtX3NpemUgWyV1
-XSwgaXNjc2lfc2NzaV9udGFza3NfYm1fc2l6ZSBbJXVdCgAAAGN4Y25pY19kZXZpY2VfaW5pdDog
-Y3hjbmljIFsweCUweF0sIGN4Y25pYy0+ZmlsdGVyIFslMHhdLCBjeGNuaWMtPm9mbGRfdGNwX3Nw
-b3J0X2JtIFsleF0gc2l6ZW9mKHN0cnVjdCBmbG93YykgWyV1XSwgdG90YWxfaXNjc2lfYm1fc2l6
-ZSBbJXVdLCB0b3RhbF9tZW1fc2l6ZSBbJXVdCgAAAAAAAAAAAAAAAAAAY3hjbmljX2RldmljZV9p
-bml0OiBkZXYudHAubnVtX3R4X3BhZ2VzIFsldV0sIGRldi5tYy5zaXplIFsldV0sIGRldi50cC50
-eF9wYWdlX3N6IFsldV0gbnN0YWNrX25sMnQgWyV1XSwgZm9pc2NzaV9udGNiIFsldV0sIHJjIFsl
-dV0KAAAAAAAAAAAAAAAAUG9ydFsldV06IFVua25vd24gU0dNSUkgc3ViLXR5cGUgJSN4CgAAAAAA
-AAAAAAAAUG9ydFsldV06IFVua25vd24gQlRfWEZJIHN1Yi10eXBlICUjeAoAAAAAAAAAAAAAUG9y
-dFsldV06IFVua25vd24gQlRfWEFVSSBzdWItdHlwZSAlI3gKAAAAAAAAAAAAcG9ydF9pbml0WyV1
-XTogcG9ydCB0eXBlIDB4JXggaXMgbm90IHN1cHBvcnRlZAoARVEgcGZuICV1IHZmbiAldTogZGVz
-dHJveWluZyBlcWlkICV1IHdpdGggcGVuZGluZyBXUihzKSAobnVtX2J5dGVzICV1IGFuZCBmbGFn
-cyAweCUwOHgKAAAAAAAAAAAAbmV0X2wyZGV2X2ZyZWU6IGwyZGV2X2ZjLT5mbG93Y19pZCBbMHgl
-eF0sIHBvcyBbJXVdCgAAAAAAAAAAAAAAAEFoIGhhLi4uZG91YmxlIGZyZWUgb3hfaWQgMHgleCwg
-cnhfaWQgMHgleAoAAAAAAEhvc3QgUFJMSSBSZXNwb25zZSB0aW1lZG91dDogb3hfaWQgMHgleCBy
-eF9pZCAweCV4CgAAAAAAAAAAAAAAAABsMmRldl9mYy0+Zmxvd2NfaWQgWyV1XSwgbDJkZXZfZmMt
-PmZsb3djX25ldF9sMmRldl9wZm4gWyV1XSwgbDJkZXZfZmMtPmZsb3djX25ldF9sMmRldl92Zm4g
-WyV1XSwgbmV0X2wyZGMtPmxwb3J0IFsldV0KAAAAAAAAAAAAAHBmbiAldSB2Zm4gJXUgdmlhIGNv
-bW1hbmQKAAAAAAAAaHdfbGVfY2xpcF9oYW5kbGVyOiByZW1vdmVkIHBvcz0ldSAoPWlkeCAldSkK
-AAAAaHdfbGVfY2xpcF9oYW5kbGVyOiBhZGRpbmcgdG8gcG9zPSV1ICg9aWR4ICV1KQoAcGZuICV1
-IHZmbiAldQoAAGh3IHBmIGJpdG1hcCAweCUwMnggdmZpZCBiaXRtYXAgMHglMDh4OjB4JTA4eDow
-eCUwOHg6MHglMDh4CgAAAABhZnRlciB2ZmlkIGZpeHVwLCB2ZmlkIGJpdG1hcCAweCUwOHg6MHgl
-MDh4OjB4JTA4eDoweCUwOHgKAAAAAAAAd29ya2Fyb3VuZDEzNzIzOiBkZXRlY3RlZCBXUiBAIDB4
-JTA4eCBvZiBzaXplICV1IGJ5dGVzLCBkcmliYmxpbmcgaXQgaW4gJXUgYnl0ZXMgYXQgYSB0aW1l
-CgAAAAAAdGltZXIgcXVldWUgJXUgbG9zdCBhIHRpY2shIG5leHQgJXAgbGFzdCAlcCBudW1lICV1
-CgAAAAAAAAAAAAAAAGZscl90aW1lcl9zdGFydDogZmxvd2NfaWQgJXUgJXAgYnVmICVwCgAAAAAA
-AAAAAHNldHRpbmcvdW5zZXR0aW5nIGhzcyByZXN5bmMgYml0CgAAAAAAAAAAAAAAAAAAAE1DIGlu
-aXRpYWxpemF0aW9uIG5vdCBjb21wbGV0aW5nLCBNQyBjdXJyZW50IGluaXQgc3RhdGUgaXMgMHgl
-MDJ4CgAAAAAAAAAAAAAAAAAAcGNpZTogbnBmICV1IChwZmJpdG1hcCAweCUwMngpIG52ZiAldSAo
-cGYgMC4uMyAweCUwMnggMHglMDJ4IDB4JTAyeCAweCUwMngpCgAAAABmYWlsZWQgdG8gZmluZCB0
-aGUgJWMlYyBWUEQgcGFyYW1ldGVyCgAAAAAAAAAAAABmYWlsZWQgdG8gcGFyc2UgdGhlICVjJWMg
-VlBEIHBhcmFtZXRlcgoAAAAAAAAAAABmYWlsZWQgdG8gc3VjY2Vzc2Z1bGx5IGZpbmQgQ2hlbHNp
-byBWUEQKAAAAAAAAAABsb2cgaW5pdGlhbGl6ZWQgQCAweCUwOHggc2l6ZSAldSAoJXUgZW50cmll
-cykKAABzZW5kX2Nsb3NlX3JlcTogY3NrX2ZjLT5mbG93Y190eXBlIFsweCV4XSwgY3NrX2ZjLT5m
-bG93Y19pZCBbMHgleF0sIGNza19mYy0+dGNiX3N0YXRlIFsweCV4XQoAAABzZW5kX2Nsb3NlX3Jl
-cTogY3NrX2ZjLT5mbG93Y190eXBlIFsweCV4XSwgY3NrX2ZjLT5mbG93Y19pZCBbMHgleF0sIGNz
-a19mYy0+Zmxvd2NfY3NvY2tfdGlkIFsweCV4XSwgdWxwdHhjaCBbJXVdLGJ1ZmZlcmVkIFsldV0K
-AG9mbGRfdGNwX2RvX2FjdGl2ZV9jbG9zZTogY3NrX2ZjIFsweCV4XSwgY3NrX2ZjLT5mbG93Y19p
-ZCBbMHgleF0sIGNza19mYy0+dGNiX3N0YXRlIFsweCV4XQoAAAAAAG9mbGRfdGNwX2RvX2FjdGl2
-ZV9jbG9zZTogY3NrX2ZjIFsweCV4XSwgY3NrX2ZjLT5mbG93Y19pZCBbMHgleF0sIGNza19mYy0+
-dGNiX3N0YXRlIFsweCV4XQoAAAAAAHRjcF9zZW5kX2FvcGVuX3JlcTogY3NrX2ZjLT5mbG93Y19p
-ZCBbMHgleF0sIGNza19mYy0+Zmxvd2Nfc3RhdGUgWzB4JXhdLCBidWZmZXJlZCBbJXVdLCByZXNf
-Y250IFsweCV4XSwgc2NoZWRfbm9kZS5uZXh0IFsweCV4XQoAb2ZsZF90Y3Bfc2VuZF9hb3Blbl9y
-ZXE6IGNwbF9yZXEtPkZpbHRlciBbMHglMHhdCgAAAAAAAAAAAAAAAAAAAGNzb2NrX2FsbG9jOiB0
-eF9jaCBbMHgleF0sIGxwb3J0IFsweCV4XSwgc21hY19pZHggWyV1XSwgbXNzX2lkeCBbJXVdIGNv
-b2tpZSBbJTA4eF0KAAAAAAAAAAAAAAAAAGNzb2NrX2FsbG9jOiBhdmFpbGFibGUgWyV1XSwgbmNz
-b2NrIFsldV0sIHBvczphdGlkIFsweCV4XSwgY3NrX2ZjIFsweCV4XSwgY3NrX2ZjLT5mbG93Y19p
-ZCBbMHgleF0sIHNwb3J0IFsldV0KAABlbmNvZGVfbnVtYmVyOiBhbGVuIFsldV0KAAAAAAAAAGNo
-YXBfY2FsY19kaWdlc3RfbWQ1IC0xCgAAAAAAAAAAY2hhcF9jYWxjX2RpZ2VzdF9tZDUgLTIKAAAA
-AAAAAABjaGFwX2NhbGNfZGlnZXN0X21kNSAtMwoAAAAAAAAAAGNoYXBfY2FsY19kaWdlc3RfbWQ1
-IC00CgAAAAAAAAAAY2hhcF9jYWxjX2RpZ2VzdF9tZDUgLTUKAAAAAAAAAABjaGFwX2NhbGNfZGln
-ZXN0X21kNSAtNgoAAAAAAAAAAGZvaXNjc2lfcGVlcl9pbml0OiBjb25uX2F0dHItPmhkaWdlc3Rf
-dG9fZGRwX3Bnc3ogWzB4JXhdCgAAAAAAAABmb2lzY3NpX3BlZXJfaW5pdDogY29ubl9mYy0+Zmxv
-d2NfZm9pc2NzaV9jb25uX2ZsYWdzIFsweCV4XQoAAAAAb2ZsZF90Y3BfZGlzY29ubmVjdDogdGNi
-X2ZjLT5mbG93Y19pZCBbMHgleF0sIGNza19mYy0+Zmxvd2NfaWQgWzB4JXhdLCBjc2stPnRjYl9z
-dGF0ZSBbMHgleF0KAAAAeG1pdF9zZW5kdGFyZ2V0czogcGFyYW0tPnR0dCBbMHgleF0sIGRhdGFf
-bGVuIFsldV0sIHBhZF9sZW4gWyV1XQoAAAAAAAAAAAAAAAAAAABsb2dnZWRfaW46IGZsb3djX2Zv
-aXNjc2lfY29ubl9mbGFncyBbMHgleF0KAAAAAABmb2lzY3NpX3ZhbGlkYXRlX2xvZ2luX3N0YWdl
-OiAtIDEKAAAAAAAAAAAAAAAAAAB0YXNrX2ZyZWU6IHRyeWluZyB0byBmcmVlIHF1ZXVlZCB0YXNr
-IFsweCV4XSEhIQoAAAAAAAAAAAAAAAAAAAAASW52YWxpZCBvcGNvZGUgMHgleCBpbiBkYXRhIHBh
-dGgKAAAAAAAAAAAAAAAAAAAAbmV0X2wyZGV2X2ZpbmRfYnlfYWRkcjogbDJkZXZfZmMtPmZsb3dj
-X2lkIFsweCV4XSwgbDJkYy0+bHBvcnQgWyV1XSwgbDJkX2ZjLT5mbG93Y19pZCBbMHgleF0sIGwy
-ZGMtPmluNF9kZXYuaW5fYWRkci5hZGRyIFsweCV4XSwgYWRkciBbMHgleF0KAAAAbmV0X2wyZGV2
-X210dV9jb25maWc6IGwyZGV2X2ZjLT5mbG93Y19pZCBbMHgleF0sIG10dSAldQoAAAAAAAAAAGZv
-aXNjc2kgY29ubl9mYyBbMHgleF0sIGZsb3djX3NjaGVkY2wgWzB4JXhdLCBpbmdfY2ggWzB4JXhd
-LCBlZ3JfY2ggWzB4JXhdCgAAAAAAbDJkZXZfbm90aWZ5IHdpdGggdW5rbm93biBmbGFnIFsweCV4
-XQoAAAAAAAAAAAAAY2huZXRfdmlfZW5hYmxlOiB2aWlkIFslZF0sIHZpX2ZjLT5mbG93Y192aV9m
-bGFncyBbMHgleF0KAAAAAAAAAGh3IHJlZ2lzdGVyIG9wZXJhdGlvbiBub3QgY29tcGxldGluZywg
-cmVnIDB4JTA4eCBtYXNrIDB4JTA4eCB2YWx1ZSAweCUwOHgKAAAAAAAATURJTyBDTDQ1OiBmYWls
-ZWQgdG8gc2V0IHVwIE1NRCBhZGRyCgAAAAAAAAAAAAAATURJTzogZmFpbGVkIHRvIHJlYWQKAAAA
-AAAAAAAAAABNRElPIENMNDU6IGZhaWxlZCB0byBzZXQgdXAgTU1EIGFkZHIKAAAAAAAAAAAAAABN
-RElPOiBmYWlsZWQgdG8gd3JpdGUKAAAAAAAAAAAAAHBbJXVdIGNoYW5naW5nIHNwZWVkIGZyb20g
-JSN4IHRvICUjeAoAAAAAAAAAAAAAAHBbJXVdIGNhbGlicmF0aW5nIFBIWSBTRVJERVMgZm9yIG5l
-dyBzcGVlZAoAAAAAAGh3X3hnbV9sZWRfbGlua191cCBsZWQwIGNvbmZpZzogJXUKAAAAAAAAAAAA
-AAAAAGh3X3hnbV9sZWRfbGlua191cCBsZWQxIGNvbmZpZzogJXUKAAAAAAAAAAAAAAAAAGhzc19z
-aWdkZXRbJXVdOiBoc3Nfc2lnZGV0IGNoYW5nZWQgdG8gMHgleAoAAAAAAGNyX21vZHVsZV9yeF9s
-b3NbJXVdOiByeF9sb3MgY2hhbmdlZCB0byAldQoAAAAAAFdBVENIRE9HOiBObyB0ZW1wZXJhdHVy
-ZSBzZW5zb3IgYXZhaWxhYmxlLgoAAAAAAFdBVENIRE9HOiBBY3RpdmF0aW5nCgAAAAAAAAAAAAAA
-V0FUQ0hET0cgLSBFbmFibGUgYWN0aW9uICV1IHRpbWUgJXUKAAAAAAAAAAAAAAAAV0FUQ0hET0cg
-LSBEaXNhYmxlIGFjdGlvbiAldQoAAABXQVRDSERPRzogRGUtYWN0aXZhdGluZwoAAAAAAAAAAEZD
-b0UgRkNCIGxpbmtkb3duOiBpb19yZXEgMHgleCV4IGlxaWQgMHgleCBmbG93aWQgMHgleCBvcCAw
-eCV4CgBmY29lIG5vdGlmeSA6IFVwZGF0ZSBuZXcgRENCWCB2YWx1ZXMgVkkgc3RhdGUgMHgleCBw
-cmkgMHgleCBzY2hlZGNsIDB4JXggZGNieF9kb25lIDB4JXgKAAAAAAAAAABmY29lIG5vdGlmeSA6
-IEZDRiBmbG93aWQgMHgleCwgdWxwY2ggMHgleCAKAAAAAABmY29lIG5vdGlmeSA6IERDQlggOiBw
-b3J0IDB4JXgsIHByaW9yaXR5IDB4JXggdWxwdHhjaCAweCV4IGNsYXNzIDB4JXgKAAAAAAAAAAAA
-AGNoX2NsX3JhdGVbJXUvJXVdOiBjYXBwZWQgZGVmaWNpdF9pbmNyIGZyb20gcmVxdWlyZWQgJXUg
-dG8gJXU7IHJhdGUgJXUgKGVmZiAldSkgZGVmaWNpdF9tYXggJXUKAEZDb0UgRkNGIHRpbWVyOiBm
-bG93YyBzdGF0ZSAweCV4LCBwb3J0IDB4JXggLGZjZiAweCV4LCBmbG93Y19pZCAweCV4CgAAAAAA
-AAAAAAAAY29yZV9wcm9ncmFtX3RjYjogdGlkICUjeCB0X3N0YXRlICUjeCByY3ZfYWR2IDB4JTA4
-eCByY3Zfc2NhbGUgJSN4IHR4X21heCAlI3ggcmN2X254dCAlI3ggYXRpZCAlI3gKAAAAAAAAAAAA
-AAAAAAlvcHQwICUjeCV4IG9wdDIgJSN4IGlwdjYgJSN4IGZsYWdzX3RpbWVyIDB4JTA4eAoAAAAA
-AAAAAAAAAAAAAABvZmxkX2Nvbm5lY3Rpb25fd3I6IGNvbm5lY3Rpb24gd2l0aCA1LXR1cGxlIGxw
-IDB4JTA0eCBmcCAweCUwNHggbGlwIDB4JTA4eCUwOHggcGlwIDB4JTA4eCUwOHggZmlsdGVyIDB4
-JTA4eCBleGlzdHMgQCBMRSBpbmRleCAldQoAAAAAAAAAAAAAAAAAAABvZmxkX2Nvbm5lY3Rpb25f
-d3I6IGNvbm5lY3Rpb24gd2l0aCA1LXR1cGxlIGxwIDB4JTA0eCBmcCAweCUwNHggbGlwIDB4JTA4
-eCBwaXAgMHglMDh4IGZpbHRlciAweCUwOHggZXhpc3RzIEAgTEUgaW5kZXggJXUKAAAAAAAAAG9m
-bGRfY29ubmVjdGlvbl93cjogY29ubmVjdGlvbiB3aXRoIDUtdHVwbGUgbHAgMHglMDR4IGZwIDB4
-JTA0eCBsaXAgMHglMDh4JTA4eCBwaXAgMHglMDh4JTA4eCBmaWx0ZXIgMHglMDh4CgAAAABvZmxk
-X2Nvbm5lY3Rpb25fd3I6IGNvbm5lY3Rpb24gd2l0aCA1LXR1cGxlIGxwIDB4JTA0eCBmcCAweCUw
-NHggbGlwIDB4JTA4eCBwaXAgMHglMDh4IGZpbHRlciAweCUwOHgKAAAAAAAAAAAAAAAASVFGTElO
-VCBwZm4gJXUgdmZuICV1OiBpcWVzaXplICV1IHRvbyBzbWFsbAoAAAAASVFGTElOVCBwZm4gJXUg
-dmZuICV1OiBpcWlkICV1IHRvbyBsYXJnZSAobWF4ICV1KQoAAAAAAAAAAAAAAAAAAElRRkxJTlQg
-cGZuICV1IHZmbiAldTogaXFpZCAldSBub3QgYWxsb2NhdGVkCgAAAElRRkxJTlQgcGZuICV1IHZm
-biAldTogZmwwaWQgJXUgdG9vIGxhcmdlIChtYXggJXUpCgAAAAAAAAAAAAAAAABJUUZMSU5UIHBm
-biAldSB2Zm4gJXU6IGZsMGlkICV1IG5vdCBhbGxvY2F0ZWQKAABJUUZMSU5UIHBmbiAldSB2Zm4g
-JXU6IGZsMWlkICV1IHRvbyBsYXJnZSAobWF4ICV1KQoAAAAAAAAAAAAAAAAASVFGTElOVCBwZm4g
-JXUgdmZuICV1OiBmbDFpZCAldSBub3QgYWxsb2NhdGVkCgAASVFGTElOVCBwZm4gJXUgdmZuICV1
-OiBmbDFpZCAldSBpcyB2YWxpZCBidXQgbm90IGZsMGlkICV1CgAAAAAAAElRRkxJTlQgcGZuICV1
-IHZmbiAldTogZmwxaWQgJXUgaXMgdmFsaWQgYnV0IGhlYWRlciBzcGxpdCBmZWF0dXJlIGlzIG5v
-dCBlbmFibGVkCgAAAAAAAAAAAAAAAAAAAElRIHBmbiAldSB2Zm4gJXU6IGlxaWQgJXUgdG9vIGxh
-cmdlIChtYXggJXUpCgAAAElRIHBmbiAldSB2Zm4gJXU6IGlxaWQgJXUgbm90IGFsbG9jYXRlZAoA
-AAAAAAAAAElRIHBmbiAldSB2Zm4gJXU6IGZsMGlkICV1IGZsMWlkICV1IGJ1dCBub3Qgc3VwcG9y
-dGVkCgAAAAAAAAAAAABFUSBwZm4gJXUgdmZuICV1OiBjcmVhdGluZyBFVEggZXFpZCAldSB3aXRo
-IHBlbmRpbmcgV1IocykgKG51bV9ieXRlcyAldSBhbmQgZmxhZ3MgMHglMDh4CgAAAAAAAABFUSBw
-Zm4gJXUgdmZuICV1OiBjcmVhdGluZyBDVFJMIGVxaWQgJXUgd2l0aCBwZW5kaW5nIFdSKHMpIChu
-dW1fYnl0ZXMgJXUgYW5kIGZsYWdzIDB4JTA4eAoAAAAAAABFUSBwZm4gJXUgdmZuICV1OiBlcWlk
-ICV1IHRvbyBsYXJnZSAobWF4ICV1KQoAAABFUSBwZm4gJXUgdmZuICV1OiBlcWlkICV1IG5vdCBh
-bGxvY2F0ZWQKAAAAAAAAAABod19jaW1fdHBfd29ya2Fyb3VuZDEzNzIzX2VuYWJsZTogcG9ydCAl
-dSBwcm90b2NvbCAweCV4IGVuICV1IGN1cnJlbnQgMHgleCB3b3JrYXJvdW5kX3ByMTM3MjMgMHgl
-eAoAAAAAAAAAAAAAAAAAcG9ydF9ibGlua19sZWRfcmVzdG9yZQoAAAAAAAAAAABwb3J0X2JsaW5r
-OiBibGlua2R1cj0weCV4IGJsaW5rX3JlZmNudAoAAAAAAAAAAABwb3J0X2JsaW5rOiAJYmxpbmtf
-cmVmY250PTB4JXgKAHBvcnRfYmxpbms6IAlibGlua19yZWZjbnQ9MHgleAoAbWlpX2Fkdl9zcGVl
-ZFsldV06IHJjYXBzIDB4JXgKAABtaWlfaW5pdFsldV06IGFjYXBzIDB4JXgKAAAAAAAAAG1paV9w
-ZG93blsldV06IHBvd2VyZG93biBlbiAldQoAcG9ydFsldV06IGdhdmUgdXAgZml4aW5nIGVycm9y
-cyEhIQoAAAAAAAAAAAAAAAAAcG9ydFsldToweCUwMng6MHglMDJ4XTogbDFjZmcsIHBjYXBzIDB4
-JXggYWNhcHMgMHgleCByY2FwcyAweCV4CgAAAAAAAAAAAAAAAAAAAABwb3J0WyV1OjB4JTAyeDow
-eCUwMnhdOiBsMWNmZywgaW52YWxpZCByZXF1ZXN0LCBwY2FwcyAweCV4IGFjYXBzIDB4JXggcmNh
-cHMgMHgleAoAAAAAAAAAAAAAAAAAAABwb3J0WyV1OjB4JTAyeDoweCUwMnhdOiBsMWNmZywgbWRp
-IG4vYSBwY2FwcyAweCV4IGFjYXBzIDB4JXggcmNhcHMgMHgleAoAAAAAAAAAAHBvcnRbJXU6MHgl
-MDJ4OjB4JTAyeF06IGwxY2ZnLCBjYW5ub3QgZm9yY2UgbXVsdGlwbGUgc3BlZWRzLCBwY2FwcyAw
-eCV4IGFjYXBzIDB4JXggcmNhcHMgMHgleAoAAGNoX3JhdGVbJXVdOiBjYXBwZWQgdGljayBmcm9t
-IHJlcXVpcmVkICV1IHRvIHN1cHBvcnRlZCAldTsgcmF0ZSAldSAoZWZmICV1KSBkZWZpY2l0X2lu
-Y3IgJXUgdGljayAldQoAAAAAAAAAAAAAAABod19pMmNfdyBwb3J0ICV1IGRldiAleCByZWcgJXUg
-ZGF0YSAweCV4CgAAAAAAAABnZXRfcmVmX3Rhc2s6IHJlcXVlc3RlZCBjb29raWU6IGhpZ2ggWzB4
-JTA4eF0sIGxvdyBbMHglMDh4XQoAAAAAZ2V0X3JlZl90YXNrOiB0YXNrIGNvb2tpZTogaGlnaCBb
-MHglMDh4XSwgbG93IFsweCUwOHhdCgAAAAAAAAAAAGdldF9yZWZfdGFzazogaXN0YXNrLT5mbG93
-Y19pZCBbMHgleF0sIGlzdGFza19mYy0+Zmxvd2Nfc3RhdGUgWyV1XQoAAAAAAAAAAAAAAAAAZ2V0
-X3JlZl90YXNrOiB0YXNrIG5vdCBmb3IgY29va2llICVseAoAAAAAAAAAAAAAbmV0aWZfcHJvY2Vz
-c19kaGNwX29wdHM6IGwyZGV2X2ZjLT5mbG93Y19pZCBbMHgleF0sIE1TR19UWVBFIFslZF0sIGRo
-Y3R4dC0+c3RhdGUgWyVkXQoAAAAAAAAAAAAAaWNtcF9yZWN2OiBsMmRldl9mYy0+Zmxvd2NfaWQg
-WzB4JXhdLCBwaWQgWzB4JXhdLCBpY21wIHR5cGUgWzB4JXhdCgAAAAAAAAAAAAAAAABBQlRTIEFD
-QyBhd2FpdGluZyBQUkxJIFJzcDogZmxvd2NfaWQgMHgleCBveF9pZCAweCV4IHJ4X2lkIDB4JXgg
-aXFpZCAweCV4CgAAAAAAAGNobmV0X2FycF9yZWN2OiBwaWQgWyV1XSwgcGt0LT5haGRyLnNpcCBb
-MHgleF0sIHBrdC0+YWhkci5yaXAgWzB4JXhdLCBwa3QtPmFoZHIub3Bjb2QgWzB4JXhdCgAAAGNo
-bmV0X2FycF9yZWN2OiB2bGFuIGV4dHJhY3RlZCwgdmxhbmlkIFsldV0sIGwyZGV2X2ZjLT5mbG93
-Y19uZXRfbDJkZXZfdmxhbmRldiBbMHgleF0sIGwyZGV2X2ZjIFsweCV4XQoAAAAAAAAAAABjaG5l
-dF9hcnBfcmVjdjogaXAgY29uZmxpY3QgZGV0ZWN0ZWQKAAAAAAAAAAAAAABjaG5ldF9hcnBfcmVj
-djogdmxhbmlkIFslMHhdLCBwaWQgWyV1XQoAAAAAAAAAAABjaG5ldF9hcnBfcmVjdjogbDJkZXZf
-ZmMgWyV4XSwgcGt0LT5haGRyLnJpcCBbMHgleF0sIHBrdC0+YWhkci5zaXAgWzB4JXhdCgAAAAAA
-AGNzb2NrX3BlZXJfY2xvc2U6IGNza19mYy0+Zmxvd2NfaWQgWzB4JXhdLCBjc2tfZmMtPnRjYl9m
-Yy0+Zmxvd2NfaWQgWzB4JXhdLCBjc2tfZmMtPmZsb3djX3N0YXRlIFsweCV4XSwgY3NrX2ZjLT5m
-bG93Y19jc29ja190Y2JfZmMtPmZsb3djX3N0YXRlIFsweCV4XQoAAAAAAAAAAABjc29ja19wZWVy
-X2Nsb3NlOiBjc2tfZmMtPmZsb3djX2lkIFsweCV4XSwgY3NrX2ZjLT5mbG93Y19zdGF0ZSAgWzB4
-JXhdCgAAAAAAAAAAAHRjYl9mYy0+Zmxvd2NfaWQgWzB4JTA2eF0sIHRjYl9mYy0+Zmxvd2NfdHlw
-ZSBbMHgleF0sIGNwbG9wIFsweCV4XSAKAAAAAAAAAAAAAAAAcGt0c2NoZWRfY2hfcmxbJXVdOiBj
-aGFubmVsIHJsIG5vdCBhdmFpbGFibGUgaW4gY29uanVuY3Rpb24gd2l0aCBmbG93IHNoYXBpbmcK
-AABwa3RzY2hlZF9jaF9ybFsldV06IHJhdGUgJXUgbWF4ICV1CgAAAAAAAAAAAAAAAABwa3RzY2hl
-ZF9jbF93cnJbJXU6JXVdOiB3ZWlnaHQgJXUgd2VpZ2h0ZWRfbWFzayAweCV4CgAAAAAAAAAAAAAA
-ZXFfcGFyYW1zWzB4JXg6MHgleF06IGRtYXEgMHgleCByZWFkICV1IHBmICV1IGVxaWRfYXBpICV1
-IHJldCAlZAoAAAAAAAAAAAAAAAAAAABod19iY204NDM0X2NoZWNrcmFtOiBTdGFydAoAAAAAAFBI
-WVsldV0gcHJvY2Vzc29yIG5vdCBydW5uaW5nCgAAUEhZWyV1XSBGVyBoYXMgYmFkIENSQwoAAAAA
-AAAAAABQSFkgZmlybXdhcmUgbG9hZCBzdWNjZXNzZnVsIQoAAAlBUV9UYWtlQ29udHJvbE9mRkxB
-U0g6IDFlLmMwMDE9JSN4IDFlLmM0NTA9JSN4IDFlLmM0NTE9JSN4IDFlLjEwMD0lI3gKAAAAAAAA
-AAAAQVFfQVBJX1dyaXRlQW5kVmVyaWZ5Rmxhc2hJbWFnZSAtIEltYWdlIGludGVncml0eSBjaGVj
-ayBmYWlsZWQgKGNhbGMgJSN4IHZhbCAlI3gpCgAAAAAAAAAAAAAAAAAAQVFfQVBJX1dyaXRlQW5k
-VmVyaWZ5Rmxhc2hJbWFnZSAtIEltYWdlIGludGVncml0eSBjaGVjayBwYXNzZWQKAEFRX0FQSV9X
-cml0ZUFuZFZlcmlmeUZsYXNoSW1hZ2UgLSBUaW1lb3V0IHdhaXRpbmcgZm9yIGZsYXNoIGludGVy
-ZmFjZSAoJXUpCgAAAAAAQVFfQVBJX1dyaXRlQW5kVmVyaWZ5Rmxhc2hJbWFnZSAtIFRpbWVvdXQg
-d2FpdGluZyBmb3IgZmxhc2ggaW50ZXJmYWNlICgldSkKAAAAAABBUV9BUElfV3JpdGVBbmRWZXJp
-ZnlGbGFzaEltYWdlIC0gVGltZW91dCB3YWl0aW5nIGZvciBmbGFzaCBpbnRlcmZhY2UgKCV1KQoA
-AAAAAEFRX0FQSV9Xcml0ZUFuZFZlcmlmeUZsYXNoSW1hZ2UgLSBUaW1lb3V0IHdhaXRpbmcgZm9y
-IGZsYXNoIGludGVyZmFjZSAoJXUpIChwcCAlI3ggYXAgJSN4KQoAAAAAAEFRX0FQSV9Xcml0ZUFu
-ZFZlcmlmeUZsYXNoSW1hZ2UgLSBUaW1lb3V0IHdhaXRpbmcgZm9yIGZsYXNoIGludGVyZmFjZSAo
-JXUpCgAAAAAAQVFfQVBJX1dyaXRlQW5kVmVyaWZ5Rmxhc2hJbWFnZSAtIFRpbWVvdXQgd2FpdGlu
-ZyBmb3IgZmxhc2ggaW50ZXJmYWNlICgldSkKAAAAAABBUV9BUElfV3JpdGVBbmRWZXJpZnlGbGFz
-aEltYWdlIC0gRXJyb3Igb24gYnVybmluZyBGTEFTSCAoY3JjMTYgbWlzbWF0Y2gpCgAAAAAAAG1h
-bGxvY19kZWNbJXVdOiBzdGFydCAlcCBlbmQgJXAgbmV4dCAlcCBzaXplIDB4JXggYWxpZ21lbnQg
-MHgleCBwICVwCgAAAAAAAAAAAAAAbWFsbG9jX2luY1sldV06IHN0YXJ0ICVwIGVuZCAlcCBuZXh0
-ICVwIHNpemUgMHgleCBhbGlnbWVudCAweCV4IHAgJXAKAAAAAAAAAAAAAABsZSBjb25maWd1cmF0
-aW9uOiBoYXNoIG1vZGUgcmVxdWlyZXMgYXQgbGVhc3QgMTYgZW50cmllcywgbmhhc2ggJXUKAAAA
-AAAAAAAAAAAAAGxlIGNvbmZpZ3VyYXRpb246IGhhc2ggbW9kZSByZXF1aXJlcyBhdCBlbnRyaWVz
-IHRvIGJlIGEgcG93ZXIgb2YgMiwgbmhhc2ggJXUKAAAAbGUgY29uZmlndXJhdGlvbjogcmVxdWVz
-dGVkICV1IHRjYW0gZW50cmllcyBidXQgb25seSAldSBhdmFpbGFibGUKAAAAAAAAAAAAAAAAAABs
-ZSBjb25maWd1cmF0aW9uOiB0Y2FtIHJlZ2lvbnMgbXVzdCBoYXZlIG11bHRpcGxlIG9mIDMyIGVu
-dHJpZXMsIG5yb3V0ZSAldSBuY2xpcCAldSBuZmlsdGVyICV1IG5zZXJ2ZXIgJXUKAAAAAAAAb2Zs
-ZCB0cCB0aW1lciBzZXR0aW5ncyBUUF9NU0wgMHglMDh4IFRQX1JYVF9NSU4gMHglMDh4IFRQX1JY
-VF9NQVggMHglMDh4IFRQX1BFUlNfTUlOIDB4JTA4eCBUUF9QRVJTX01BWCAweCUwOHgKACAgICAg
-ICAgICAgICAgICAgICAgICAgVFBfS0VFUF9JRExFIDB4JTA4eCBUUF9LRUVQX0lOVFZMIDB4JTA4
-eCBUUF9JTklUX1NSVFQuaW5pdF9zcnR0X21heHJ0dCAweCUwNHggVFBfSU5JVF9TUlRULmluaXRf
-c3J0dF9pbml0c3J0dCAweCUwNHggVFBfRklOV0FJVDJfVElNRVIgMHglMDh4CgAAAAAAAAAAAAAA
-Y29uZmlndXJhdGlvbiBmaWxlIHBhcnNlciBlbmNvdW50ZXJlZCBlcnJvciBAIGxpbmUgJXU6CgAA
-AAAAAAAAAEhPU1QgUEFHRV9TSVpFIFsweCUwbHhdIHRvbyBzbWFsbCwgbWluIFsweCUwbHhdIHJl
-cXVpcmVkCgAAAAAAAABwYWdlIHNpemUgWyVsdV0gbWlzbWF0Y2gKAAAAAAAAAFBBR0Ugc2l6ZSAl
-bHUgdW5zdXBwb3J0ZWQsIGRkcCBkaXNhYmxlZAoAAAAAAAAAAEhvc3QgcGFnZV9zaXplICVsdSwg
-ZGRwX2lkeCAldQoARkNvRSBERFAgaW5pdDogZmNvZSBsbGltaXQgMHgleCwgZmNvZSB1bGltaXQg
-MHgleCBnYmwgbGxpbWl0IDB4JXggZ2JsIHVsaW1pdCAweCV4CgAAAAAAAAAAAAAAAAAARkNvRSBE
-RFAgaW5pdDogZmNvZSBwcG9kIG9mZiAweCV4LCBmY29lIHN0IHBwb2QgYWRkciAweCV4IGZjb2Ug
-bnVtIHBwb2RzIDB4JXgKAABmY29lIHhjaGcgbWdyIGluaXQ6IE51bWJlciBvZiBleGNoYW5nZXMg
-Zm9yIEZDb0UgaXMgJXgKAAAAAAAAAAAAQ2FsY3VsYXRpb24gb3V0IG9mIGJvdW5kcyBmdXJpbmcg
-aW5pdDogJSN4ICUjeCAlI3gKAAAAAAAAAAAAAAAAAHBmbiAldSB2Zm4gJXUgaGFzIHBuZHR4bnMg
-JXUgYWZ0ZXIgMTAwbXMKAAAAAAAAAGJhZCBtYWlsYm94IGNtZDogcGZuIDB4JXggdmZuIDB4JXg7
-IG9wY29kZSAweCV4ID4gTEFTVEMyRSAweCV4CgBtYWlsYm94IGNtZCBub3QgeWV0IHN1cHBvcnRl
-ZDogcGZuIDB4JXggdmZuIDB4JXg7IG9wY29kZSAweCV4CgAAYmFkIG1haWxib3ggY21kOiBwZm4g
-MHgleCB2Zm4gMHgleDsgb3Bjb2RlIDB4JXggaXMgdmFsaWQgcG9zdCBkZXZpY2UgaW5pdCBvbmx5
-CgBiYWQgbWFpbGJveCBjbWQ6IHBmbiAweCV4IHZmbiAweCV4OyBvcGNvZGUgMHglMDJ4IHJhbWFz
-ayAweCV4IGNtZCByYW1hc2sgMHgleAoAAGJhZCBtYWlsYm94IGNtZDogcGZuIDB4JXggdmZuIDB4
-JXg7IG9wY29kZSAweCUwMnggbGVuMTYgMHgleCB2ZXJzdXMgZXhwZWN0ZWQgbGVuMTYgMHgleAoA
-AAAAAAAAAGluc3VmZmljaWVudCBjYXBzIHRvIHByb2Nlc3MgbWFpbGJveCBjbWQ6IHBmbiAweCV4
-IHZmbiAweCV4OyByX2NhcHMgMHgleCB3eF9jYXBzIDB4JXggcmVxdWlyZWQgcl9jYXBzIDB4JXgg
-d19jYXBzIDB4JXgKAAAAAAAAAAAAaW5zdWZmaWNpZW50IGNhcHMgdG8gcHJvY2VzcyBtYWlsYm94
-IGNtZDogcGZuIDB4JXggdmZuIDB4JXg7IHJfY2FwcyAweCV4IHd4X2NhcHMgMHgleCByZXF1aXJl
-ZCByX2NhcHMgMHgleCB3X2NhcHMgMHgleAoAAAAAAAAAAABWUEQgcmVnaW9uIGlzIHRvbyBzbWFs
-bCAoU0VSQ0ZHX1NSX1BGTlZQRFNJWkUgMHgleCkKAAAAAAAAAAAAAAAAY2Y6IGZhaWxlZCB0byBh
-bGxvY2F0ZWQgbWVtb3J5IGZvciBjb25maWd1cmF0aW9uIGZpbGUsIHJldCAlZAoAAAAAAAAAAAAA
-AAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAA
-AAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAA
-AAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAA
-AAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAA
-AAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAA
-AAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAA
-AAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAA
-AAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAA
-AAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAA
-AAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAA
-AAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAA
-AAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAA
-AAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAA
-AAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAA
-AAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAA
-AAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAA
-AAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAIIAAAEgAAAAAAAAAIIAAAEAAAAAAAAAAIAAAAAA
-AAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAaAgAAAAAAAAAAAAAAAAIAAAAAAAAAAAAKAAAAA
-AAAAAAAAAgADAAAAAAFgCAAAAAADAAAAAAAAAAAAAAADAAAAAAAAAAAAAAACAAAAAAAAAAAAIAAA
-AAAAAAAAAAAAAQADgAAAAAAAAAAAAAACAAAAAAAAAAAAIAOAAAAAAAAAAAAAEAKAAIAAAAAAAAAA
-AAAAAAAAAAAAAAAAIAAAAAAAAAAAAAAAAAACgAAAAAAAAAAAAAACAAAAAAAAAAAAAAACgAAAAAAA
-AAAAAQADAAAAAAAAAAAAAAKDAAAAAAAAAAAAEAKAAAAAAAAAAAAAMAADAAAAAAAACAAAMAADAAAA
-AAAAAAAAMAWDAAAAAAAACAAAMAWDAAAAAAAAAAAAMASDAAAAAAAACAAAMASDAAAAAAAAAAAAMAMD
-AAAAAAAACAAAMAMDAAAAAAAAAAAAOAMDAAAAAAAAAAAAOAWDAAAAAAAAAAAAOASDAAAAAAAAAAAA
-OAADAAAAAAAAAAAANAaCAAAAAAAAAAAAPAOCAAAAAAAAAAAAPAADAAAAAAAACAAAPAADAAAAAAAA
-AAAAPASDAAAAAAAAAAAAPAUDAAAAAAAAAAAAPQQDAAAAAAAAAAAAPAODAAAAAAAAAAAALAACAAAA
-AAAAAAAALAWCAAAAAAAAAAAALAUCAAAAAAAAAAAAEAaAAAAAAAAAAAAAEAaCwAAAAAAAAAAAEAaC
-gAAAAAAAAAAAAA6CAAAAAAAAAAAAEAeCgAAAACAAAAAAAAeCAAAAACAAAAAAEAcCgAAAAAAAAAAA
-EAcCgAAAAAAAAAAAEAcCgAAAAAAAAAAAAAcCAAAAACAAAAAAEBeDAAAAAAAACAAAEBeDAAAAAAAA
-CAAAEAAAAAAAAAAAAAAAEAYDgAAAAAAAAAAAAA4DAAAAAAAAAAAAEAYDQAAAAAAAAAAAEAYDAAAA
-AAAAAAAAEAYAAAAAAAAAAAAAAAYDgAAAAAAAAAAAAAYDAAAAAAAAAAAAAA4CAAAAAAAAAAAAAA4C
-AAAAAAAAAAAAEAYCAAAAAAAAAAAAEAYCAAAAAAAAAAAAEAYCgAAAAAAAAAAAEAYCgAAAAAAAAAAA
-AAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAgAAAAAAAAAAABADAAAAAAAACAAAAAAAAAAAAAAA
-AAAA////////////////////////////////////////////////////////////////////////
-////////////////////////////////////////////////////////////////////////////
-////////////////////////////////////////////////////////////////////////////
-////////////////////////////////////////////////////////////////////////////
-////////////////////////////////////////////////////////////////////////////
-////////////////////////////////////////////////////////////////////////////
-////////////////////////////////////////////////////////////////////////////
-////////////////////////////////////////////////////////////////////////////
-////////////////////////////////////////////////////////////////////////////
-////////////////////////////////////////////////////////////////////////////
-////////////////////////////AAAAIAAAAADAAAAAAAAAIAAAAADgAAAAAAAAAAIAAAAAAAAA
-QAAAAAAAAAAAAAAAAAABIQAAAAAAAAAAAAEBIAAAAAAAAAAAAAACAAAABAAEAAAAAAUAAAAEAAAA
-AAAAAAAAoAAAAACAAAAAAIAAQAAAAAAAAgAAAIAAIAAAAAAAAgAAAQBAAAAAAAAAAAAAAQBCAAAA
-AAAAAAAAAAAgAAAAAAAAAAAAAhAgAAAAAAAAAAAAAgwCAAAAAAAAAAAAAIUCAAAABAAAAAAAAIBC
-AAAAAAAAAAAAAgAiAAAAAAAAAAAAAIBBAAAAAAAAAAAAAIBBgAAAAAAAAAAAAgAhAAAAAAAAAAAA
-ABAggAAAAAAAAAAAAiUAgAAAAAAAAAAAAAUAAAAAAAAAAAAACIgEgAAAAAAAAAAACIgEgAAAAAAA
-AAAACKIAgAAAAAAAAAAACKIAgAAAAAAAAAAACKMAgAAAAAAAAAAACKMAgAAAAAAAAAAACKSAgAAA
-AAAAAAAACKSAgAAAAAAAAAAABKSAwAAAAAAAAAAABKIAwAAAAAAAAAAABKMAwAAAAAAAAAAABIgE
-wAAAAAAAAAAAAAkBgAAAAAAAAAAAAgwAgAAAAAAAAAAAAIgEwAAAAAAAAAAAAIgEgAAAAAAAAAAA
-AgsAgAAAAAAAAAAAAIqAgAAAAAAAAAAAAAuAgAAAAAAAAAAAAIwAgAAAAAAAAAAAAiAQgAAAAAAA
-AAAAAgoAgAAAAAAAAAAAAgqAgAAAAAAAAAAAAAkCgAAAAAAAAAAAAAEBAAAAAAAAAAAAAAEBQAAA
-AAAAAAAAAAEAgAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAgAAAAAAAAAAAAACBEAAAAAAAAAAAAACB
-CAAAAAAAAAAAAACBBAAAAAAAAAAAAACBgAAAAAAAAAAAAIAAwAAAAAAAAAAAAIAAoAAAAAAAAAAA
-AAAIAAAAAAAAAAAAAIGAAAAAAAAAAAAAAIGAgAAAAAAAAAAAAImAgAAAAAAAAAAAAImAwAAAAAAA
-AAAAAAGCAAAAAAAAAAAAAgGAAAAAAAAAAAAAAgGAgAAAAAAAAAAAAEGBgAAAAAAAAAAAAgGBgAAA
-AAAAAAAAAEmBgAAAAAAAAAAAAgmBgAAAAAAAAAAAAgGBAAAAAAAAAAAAAEGBAAAAAAAAAAAAIAAA
-AAAAAAAAAAAAEAAAAgAAAAAAAAAAEAAAAAAAAAAAAAAAAIAAwAAAAAAAAAAAAAAAAAAAAAAAAAAA
-////////////////////////////////////////////////////////////////////////////
-////////////////////////////////////////////////////////////////////////////
-////////////////////////////////////////////////////////////////////////////
-////////////////////////////////////////////////////////////////////////////
-////////////////////////////////////////////////////////////////////////////
-////////////////////////////////////////////////////////////////////////////
-////////////////////////////////////////////////////////////////////////////
-////////////////////////////////////////////////////////////////////////////
-////////////////////////////////////////////////////////////////////////////
-////////////////////////////////////////////////////////////////////////////
-////////////////////////AAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAIAJIAAAAAAAAAA2YAIABA
-AAAAAAgAkgAAAAAAAAADcgElBEAAAAAAAAAAAAAAAAAAAAOWACAAQAAAAAAIAAAAAgCIA4cAAFYA
-IABAAAAAAAAAAAAAAAAAAAADlgAgAEAAAAAAAAAAAAAAAAAAAAOWACAAQAAAAAAAAAAAAAAAAAAA
-A5YAIABAAAAAAAAAAAAAAAAAAAADlgAgAEAAAAAACAAAAAIAiAOFAACWASAAQAAAAAAAAAAAAAAA
-AAAAA5YAIABAAAAAAAAAAAAAAAAAAAADlgAgAEAAAAAACAAAAAIAiAOFAACWASAAQAAAAAAAAAAA
-AAAAAAAAA5YAIABAAAAAAAAAAAAAAAAAAAADlgAgAEAAAAAACAAAAAIAiAOFAACWASAAQAAAAAAA
-AAAAAAAAAAAAA5YAIABAAAAAAAAAAAAAAAAAAgADlgAmBEAAAAAACEcgAQQAAAKyAALSBSAkQAAA
-AAAAAAAAAAAAAAAAA5YAIABAAAAAAABHIAEGDKGCsEABkgQgJEAAAAAAAEcgAQQMoEKzgAISBSAk
-QAAAAAAIAAAAAgCIA4UAAJYBIABAAAAAAAgAAAACAIgDhQAAlgEgAEAAAAAACAAAAAIAiAOFAACW
-ASAAQAAAAAAAAAAAAAAAAAAAA5YAIABAAAAAAA3FEroCsIDTFFSL4kpGJEAAAAAABcUSugK3gNMU
-VItiSaYkQAAAAAAFxRKiBrCA0xRUi+JKRiRAAAAAAA3FEqIGt4DTFFSLYkmmJEAAAAAABcUSogaw
-gNMUVIviSkYkQAAAAAANxRKiBreA0xRUi2JJpiRAAAAAAAXFEqIGsIDTFFSL4kpGJEAAAAAADcUS
-oga3gNMUVItiSaYkQAAAAAAJxBCgAgCAkAAAi2JpxiRAAAAAAAHEEKAGsICTBlSLYmnGJEAAAAAA
-AcQQoAawgJMGVItiacYkQAAAAAABxRC4BrCAkxZUi2JIxiRAAAAAAAihEIgCAIFYEgALUgCmJEAA
-AAAACcAQkAKwgAMWVIqSAcYkQAAAAAAJwBC4BrCAAxCUi+ICRiRAAAAAAAnAELgCtIADEJSLYgGm
-JEAAAAAACcAQuAK0gAMQlItiAaYkQAAAAAAJwBC4ArSAAxCUi2IBpiRAAAAAAAnAELgCtIADEJSL
-YgGmJEAAAAAAAaAQkAa0gAMQlItiAaYkQAAAAAABwBCAArCAAxRUilIAxiRAAAAAAAHAEIACsIAD
-FFSKUgDGJEAAAAAAAcAQgAKwgAMUVIpSAMYkQAAAAAAIRyABBAAAArIAAtIFICRAAAAAAACBAAAC
-AIVYB0ALUgCmJEAAAAAAAIEAAAIAhVgHQAtSAKYkQAAAAAAAAAAABACgQAGAAdYAIABAAAAAAAAA
-AAAGAKGAAEABVgAgAEAAAAAAAAAAAAQAoEABgAHWACAAQAAAAAAIgQAAAgCFWYAEC1IApiRAAAAA
-AAlhQAAAAAAYAAADQgEmpEAAAAAACEAAAAAAAAAAAANSASYkQAAAAAAAAAAABACgQAGAAdYAIABA
-AAAAAAgAAAACAIgDhwAD1gAmBEAAAAAACAAAAAIAiAOHAAPWACYEQAAAAAAAAAAAAAAAAAAAA5YA
-IABAAAAAAAAAAAAAAAAAAAADlgAgAEAAAAAAAAAAAAQAoEABgAHWACAAQAAAAAAJwBC4ArSAAxCU
-i2IBpiRAAAAAAAAAAAAAAAAAAgADlgAmBEAAAAAAAAAAAAAAAAAAAAOWACAAQAAAAAAIAAAAAgCI
-A4UAAJYBIABAAAAAAAAAAAAEAKBAAYAB1gAgAEAAAAAAAAAAAAQAoEABgAHWACAAQAAAAAAAAAAA
-BACgQAGAAdYAIABAAAAAAAihEIgCAIFYEAAJEgGmJEAAAAAAAAAAAAQAoEABgAHWACAAQAAAAAAA
-AAAABgChgABAAVYAIABAAAAAAAgAAAACAIgDhUAI0gJGJEAAAAAACcUSogKUiNIQgIsiSKYkQAAA
-AAAJwBCAAqSIAIdUi1IBpiRAAAAAAAnAEIAGtIgDARSLUgGmJEAAAAAADcAAAAKwgMMWVIviA0Yk
-QAAAAAAAAAAAAAAAAAAAA5YAIABAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAA
-AAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAA
-AAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAA
-AAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAA
-AAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAA
-AAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAA
-AAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAA
-AAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAA
-AAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAA
-AAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAA
-AAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAA
-AAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAA
-AAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAA
-AAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAA
-AAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAA
-AAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAA
-AAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAA
-AAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAACAAwAAQAAAACAAACACYkQAAAAAAA
-RzAABgMgArcACAIBwCRAAAAAAAgDAAACBqAKtQAIAgDGJEAAAAAAAAAAAAAAAAACAAACACYkQAAA
-AAAAIFAABAAAAAIAAAIAJiRAAAAAAAhgIACEAAAAAAAABgAgAEAAAAAACGAgAIQAAAACATACACYl
-QAAAAAAIYAAABACEAYAEBAIBxiTAAAAAAAHAAAACQ4ADAgyIAgGmJEAAAAAACABgAAQAAAACAAAC
-ACYkQAAAAAAIAGAABAAAAAAAAAQBIABAAAAAAAAAAAAAAAAAAAAABAEgAEAAAAAABAgUgAYKAAAH
-AUwCIKYmQAAAAAAIgAAABgCEAYAECAIBpiZAAAAAAABAAAACAKAAAkAIAgGmJEAAAAAAAAAAAAAA
-AAACAAACACYkQAAAAAAEAAAAAoQAAwKKCAIEpiRAAAAAAAAAAAAAAAAAAgAABgEgSEAAAAAAACBQ
-AAQAAAACAAACACYkQAAAAAAIYCAAhAAAAAIAAAYAJgRAAAAAAAhgIACEAAAAAgAAAgEmJUAAAAAA
-CGAAAAQAhAGABAQCAcYkwAAAAAAIAGAABAAAAAAAAAQBIABAAAAAAAAAAAAAAAAAAAAAAgAmTHAA
-AAAAAAAAAAAAAAAAAAAGASAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAA
-AAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAA
-AAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAHAAAAItJAE
-CRYEAgTGJEAAAAAAAcAAAAi0kAQJFgQCBMYkQAAAAAAECHSATAkAAFAAXAJh5iRAAAAAAAwIdIBE
-AAAAUgBAAmEmJEAAAAAACAIQoAQAAAAAAQACACYkQAAAAAAIAhCgBAAAAAABAAIAJiRAAAAAAAQI
-dABCAQAABwCIAmDGJEAAAAAADcgUAAIJAAAEQJwCYOYkQAAAAAAJyBCABrSQBAKUiAJlxiRAAAAA
-AA3IdABItJADAJSIAmCmJMAAAAAADch0AEi0kAMAlIgCYKYkwAAAAAAIRwAABAAAAAAAAAIBICRA
-AAAAAAhHAAAEAAAAAgAAAgUgJEAAAAAAAEcgAQwHIcK3AAgCAcAkQAAAAAAARyABDAchwrcACAIB
-wCRAAAAAAABHIAEMByHCtwAIAgHAJEAAAAAAAAAgAQgAhAAFQIgCAcYkwAAAAAAAACABCACEAAVA
-iAIBxiTAAAAAAAAAIAEIAIQABUCIAgHGJMAAAAAAAAAgAYaCAAECwIgCA8YkwAAAAAAAACABgoIA
-AALAiAIDxiTAAAAAAAnAIAGCpIABBUCIAgHGJMAAAAAACAAAAAwAhAAFQIgCAcYkwAAAAAAAACAB
-hoIAAQLAiAIDxiTAAAAAAAAAIAGCggAAAMCIAgKmJMAAAAAACcAgAYKkgAEFQIgCAcYkwAAAAAAA
-AAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAA
-AAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAgAAAAEAAQkBAAAAAAAAAAAAAAAAAAAAAAAA
-AAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAA
-AAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAA
-AAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAA
-AAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAA
-AAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAA
-AAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAA
-AAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAA
-AAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAA
-AAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAA
-AAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAA
-AAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAA
-AAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAA
-AAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAA
-AAAAAAAAAAAAW2dsb2JhbF0KcnNzX2dsYl9jb25maWdfbW9kZT1iYXNpY3ZpcnR1YWwKcnNzX2ds
-Yl9jb25maWdfb3B0aW9ucz10bmxtYXBlbixoYXNodG9lcGxpdHosdG5sYWxsbGtwCnJlZ1sweDEw
-MDhdPTB4NDA4MTAvMHgyMWM3MApyZWdbMHgxMDBjXT0weDIyMjIyMjIyCnJlZ1sweDEwYTBdPTB4
-MDEwNDA4MTAKcmVnWzB4MTA0NF09NDA5NgpyZWdbMHgxMDQ4XT02NTUzNgpyZWdbMHgxMDRjXT0x
-NTM2CnJlZ1sweDEwNTBdPTkwMjQKcmVnWzB4MTA1NF09OTIxNgpyZWdbMHgxMDU4XT0yMDQ4CnJl
-Z1sweDEwNWNdPTEyOApyZWdbMHgxMDYwXT04MTkyCnJlZ1sweDEwNjRdPTE2Mzg0CnJlZ1sweDEw
-YTRdPTB4YTAwMGEwMDAvMHhmMDAwZjAwMApyZWdbMHgxMGE4XT0weDIwMDAvMHgyMDAwCnNnZV90
-aW1lcl92YWx1ZT01LDEwLDIwLDUwLDEwMCwyMDAKcmVnWzB4N2RjMF09MHg2MmY4ODQ5CmZpbHRl
-ck1vZGU9ZnJhZ21lbnRhdGlvbixtcHNoaXR0eXBlLHByb3RvY29sLHZsYW4scG9ydCxmY29lCnRw
-X3Btcng9MzAKdHBfcG1yeF9wYWdlc2l6ZT02NEsKdHBfcG10eD01MAp0cF9wbXR4X3BhZ2VzaXpl
-PTY0SwpbZnVuY3Rpb24iMCJdCm52Zj0xNgp3eF9jYXBzPWFsbApyX2NhcHM9YWxsCm52aT0xCm5p
-cWZsaW50PTgKbmV0aGN0cmw9OApuZXE9MTYKbmV4YWN0Zj04CmNtYXNrPWFsbApwbWFzaz0weDEK
-W2Z1bmN0aW9uIjEiXQpudmY9MTYKd3hfY2Fwcz1hbGwKcl9jYXBzPWFsbApudmk9MQpuaXFmbGlu
-dD04Cm5ldGhjdHJsPTgKbmVxPTE2Cm5leGFjdGY9OApjbWFzaz1hbGwKcG1hc2s9MHgyCltmdW5j
-dGlvbiIyIl0KbnZmPTE2Cnd4X2NhcHM9YWxsCnJfY2Fwcz1hbGwKbnZpPTEKbmlxZmxpbnQ9OApu
-ZXRoY3RybD04Cm5lcT0xNgpuZXhhY3RmPTgKY21hc2s9YWxsCnBtYXNrPTB4NApbZnVuY3Rpb24i
-MyJdCm52Zj0xNgp3eF9jYXBzPWFsbApyX2NhcHM9YWxsCm52aT0xCm5pcWZsaW50PTgKbmV0aGN0
-cmw9OApuZXE9MTYKbmV4YWN0Zj04CmNtYXNrPWFsbApwbWFzaz0weDgKW2Z1bmN0aW9uIjQiXQp3
-eF9jYXBzPWFsbApyX2NhcHM9YWxsCm52aT0yOApuaXFmbGludD0xNzAKbmV0aGN0cmw9MTAwCm5l
-cT0yNTYKbmV4YWN0Zj00MApjbWFzaz1hbGwKcG1hc2s9YWxsCm5ldGhvZmxkPTEwMjQKbnJvdXRl
-PTMyCm5jbGlwPTMyCm5maWx0ZXI9NDk2Cm5zZXJ2ZXI9NDk2Cm5oYXNoPTEyMjg4CnByb3RvY29s
-PW5pY192bSxvZmxkLHJkZHAscmRtYWMsaXNjc2lfaW5pdGlhdG9yX3BkdSxpc2NzaV90YXJnZXRf
-cGR1CnRwX2wydD0zMDcyCnRwX2RkcD0yCnRwX2RkcF9pc2NzaT0yCnRwX3N0YWc9Mgp0cF9wYmw9
-NQp0cF9ycT03CltmdW5jdGlvbiI1Il0Kd3hfY2Fwcz1hbGwKcl9jYXBzPWFsbApudmk9NApuaXFm
-bGludD0zNApuZXRoY3RybD0zMgpuZXE9NjQKbmV4YWN0Zj00CmNtYXNrPWFsbApwbWFzaz1hbGwK
-bnNlcnZlcj0xNgpuaGFzaD0yMDQ4CnRwX2wydD0xMDI0CnByb3RvY29sPWlzY3NpX2luaXRpYXRv
-cl9mb2ZsZAp0cF9kZHBfaXNjc2k9Mgppc2NzaV9udGFzaz0yMDQ4CmlzY3NpX25zZXNzPTIwNDgK
-aXNjc2lfbmNvbm5fcGVyX3Nlc3Npb249MQppc2NzaV9uaW5pdGlhdG9yX2luc3RhbmNlPTY0Cltm
-dW5jdGlvbiI2Il0Kd3hfY2Fwcz1hbGwKcl9jYXBzPWFsbApudmk9NApuaXFmbGludD0zNApuZXRo
-Y3RybD0zMgpuZXE9NjYKbmV4YWN0Zj0zMgpjbWFzaz1hbGwKcG1hc2s9YWxsCm5oYXNoPTIwNDgK
-cHJvdG9jb2w9ZmNvZV9pbml0aWF0b3IKdHBfZGRwPTIKZmNvZV9uZmNmPTE2CmZjb2VfbnZucD0z
-MgpmY29lX25zc249MTAyNApbZnVuY3Rpb24iMTAyMyJdCnd4X2NhcHM9YWxsCnJfY2Fwcz1hbGwK
-bnZpPTQKY21hc2s9YWxsCnBtYXNrPWFsbApuZXhhY3RmPTgKbmZpbHRlcj0xNgpbZnVuY3Rpb24i
-MC8qIl0Kd3hfY2Fwcz0weDgyCnJfY2Fwcz0weDg2Cm52aT0xCm5pcWZsaW50PTQKbmV0aGN0cmw9
-MgpuZXE9NApuZXhhY3RmPTQKY21hc2s9YWxsCnBtYXNrPTB4MQpbZnVuY3Rpb24iMS8qIl0Kd3hf
-Y2Fwcz0weDgyCnJfY2Fwcz0weDg2Cm52aT0xCm5pcWZsaW50PTQKbmV0aGN0cmw9MgpuZXE9NApu
-ZXhhY3RmPTQKY21hc2s9YWxsCnBtYXNrPTB4MgpbZnVuY3Rpb24iMi8qIl0Kd3hfY2Fwcz0weDgy
-CnJfY2Fwcz0weDg2Cm52aT0xCm5pcWZsaW50PTQKbmV0aGN0cmw9MgpuZXE9NApuZXhhY3RmPTQK
-Y21hc2s9YWxsCnBtYXNrPTB4NApbZnVuY3Rpb24iMy8qIl0Kd3hfY2Fwcz0weDgyCnJfY2Fwcz0w
-eDg2Cm52aT0xCm5pcWZsaW50PTQKbmV0aGN0cmw9MgpuZXE9NApuZXhhY3RmPTQKY21hc2s9YWxs
-CnBtYXNrPTB4OApbcG9ydCIwIl0KZGNiPXBwcCxkY2J4CmJnX21lbT0yNQpscGJrX21lbT0yNQpo
-d209MzAKbHdtPTE1CmR3bT0zMApbcG9ydCIxIl0KZGNiPXBwcCxkY2J4CmJnX21lbT0yNQpscGJr
-X21lbT0yNQpod209MzAKbHdtPTE1CmR3bT0zMApbcG9ydCIyIl0KZGNiPXBwcCxkY2J4CmJnX21l
-bT0yNQpscGJrX21lbT0yNQpod209MzAKbHdtPTE1CmR3bT0zMApbcG9ydCIzIl0KZGNiPXBwcCxk
-Y2J4CmJnX21lbT0yNQpscGJrX21lbT0yNQpod209MzAKbHdtPTE1CmR3bT0zMApbZmluaV0KdmVy
-c2lvbj0weDE0MjUwMDBjCmNoZWNrc3VtPTB4NzY4ZWY3YTUKAAAAAAAAAAAAAAAAAAAAAAAAAAAA
-AAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAA
-AAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAA
-AAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAA
-AAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAA
-AAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAA
-AAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAA
-AAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAA
-AAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAA
-AAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAA
-AAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAA
-AAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAA
-AAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAA
-AAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAA
-AAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAA
-AAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAA
-AAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAA
-AAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAA
-AABbZ2xvYmFsXQpyc3NfZ2xiX2NvbmZpZ19tb2RlPWJhc2ljdmlydHVhbApyc3NfZ2xiX2NvbmZp
-Z19vcHRpb25zPXRubG1hcGVuLGhhc2h0b2VwbGl0eix0bmxhbGxsa3AKcmVnWzB4MTAwOF09MHg0
-MDgxMC8weDIxYzcwCnJlZ1sweDEwMGNdPTB4MjIyMjIyMjIKcmVnWzB4MTBhMF09MHgwMTA0MDgx
-MApyZWdbMHgxMDQ0XT00MDk2CnJlZ1sweDEwNDhdPTY1NTM2CnJlZ1sweDEwNGNdPTE1MzYKcmVn
-WzB4MTA1MF09OTAyNApyZWdbMHgxMDU0XT05MjE2CnJlZ1sweDEwNThdPTIwNDgKcmVnWzB4MTA1
-Y109MTI4CnJlZ1sweDEwNjBdPTgxOTIKcmVnWzB4MTA2NF09MTYzODQKcmVnWzB4MTBhNF09MHhh
-MDAwYTAwMC8weGYwMDBmMDAwCnJlZ1sweDEwYThdPTB4MjAwMC8weDIwMDAKc2dlX3RpbWVyX3Zh
-bHVlPTUsMTAsMjAsNTAsMTAwLDIwMApyZWdbMHg3ZGMwXT0weDYyZjg4NDkKZmlsdGVyTW9kZT1m
-cmFnbWVudGF0aW9uLG1wc2hpdHR5cGUscHJvdG9jb2wsdmxhbixwb3J0LGZjb2UKdHBfcG1yeD0z
-MAp0cF9wbXJ4X3BhZ2VzaXplPTY0Swp0cF9wbXR4PTUwCnRwX3BtdHhfcGFnZXNpemU9NjRLCltm
-dW5jdGlvbiIwIl0Kd3hfY2Fwcz1hbGwKcl9jYXBzPWFsbApudmk9MjgKbmlxZmxpbnQ9MTcwCm5l
-dGhjdHJsPTk2Cm5lcT0yNTIKbmV4YWN0Zj00MApjbWFzaz1hbGwKcG1hc2s9YWxsCm5yb3V0ZT0z
-MgpuY2xpcD0zMgpuZmlsdGVyPTQ4Cm5zZXJ2ZXI9MzIKbmhhc2g9MApwcm90b2NvbD1uaWNfdm0s
-b2ZsZCxyZGRwLHJkbWFjLGlzY3NpX2luaXRpYXRvcl9wZHUsaXNjc2lfdGFyZ2V0X3BkdQp0cF9s
-MnQ9MzA3Mgp0cF9kZHA9Mgp0cF9kZHBfaXNjc2k9Mgp0cF9zdGFnPTIKdHBfcGJsPTUKdHBfcnE9
-NwpbZnVuY3Rpb24iMSJdCnd4X2NhcHM9YWxsCnJfY2Fwcz1hbGwKbnZpPTQKbmlxZmxpbnQ9MzQK
-bmV0aGN0cmw9MzIKbmVxPTY2Cm5leGFjdGY9MzIKY21hc2s9YWxsCnBtYXNrPWFsbApuaGFzaD0w
-CnByb3RvY29sPWZjb2VfaW5pdGlhdG9yCnRwX2RkcD0yCmZjb2VfbmZjZj0xNgpmY29lX252bnA9
-MzIKZmNvZV9uc3NuPTEwMjQKW2Z1bmN0aW9uIjEwMjMiXQp3eF9jYXBzPWFsbApyX2NhcHM9YWxs
-Cm52aT00CmNtYXNrPWFsbApwbWFzaz1hbGwKbmV4YWN0Zj04Cm5maWx0ZXI9MTYKW2Z1bmN0aW9u
-IjAvKiJdCnd4X2NhcHM9MHg4MgpyX2NhcHM9MHg4Ngpudmk9MQpuaXFmbGludD00Cm5ldGhjdHJs
-PTIKbmVxPTQKbmV4YWN0Zj00CmNtYXNrPWFsbApwbWFzaz0weDEKW2Z1bmN0aW9uIjEvKiJdCnd4
-X2NhcHM9MHg4MgpyX2NhcHM9MHg4Ngpudmk9MQpuaXFmbGludD00Cm5ldGhjdHJsPTIKbmVxPTQK
-bmV4YWN0Zj00CmNtYXNrPWFsbApwbWFzaz0weDIKW3BvcnQiMCJdCmRjYj1wcHAsZGNieApiZ19t
-ZW09MjUKbHBia19tZW09MjUKaHdtPTMwCmx3bT0xNQpkd209MzAKW3BvcnQiMSJdCmRjYj1wcHAs
-ZGNieApiZ19tZW09MjUKbHBia19tZW09MjUKaHdtPTMwCmx3bT0xNQpkd209MzAKW3BvcnQiMiJd
-CmRjYj1wcHAsZGNieApiZ19tZW09MjUKbHBia19tZW09MjUKaHdtPTMwCmx3bT0xNQpkd209MzAK
-W3BvcnQiMyJdCmRjYj1wcHAsZGNieApiZ19tZW09MjUKbHBia19tZW09MjUKaHdtPTMwCmx3bT0x
-NQpkd209MzAKW2ZpbmldCnZlcnNpb249MHgxNDI1MDAwYwpjaGVja3N1bT0weDRjNTY2NjM5CgAA
-AAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAA
-AAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAA
-AAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAA
-AAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAA
-AAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAA
-AAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAA
-AAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAA==
-====
diff --git a/sys/dev/cxgbe/firmware/t4fw_cfg.txt b/sys/dev/cxgbe/firmware/t4fw_cfg.txt
index 2a9db62..be55453 100644
--- a/sys/dev/cxgbe/firmware/t4fw_cfg.txt
+++ b/sys/dev/cxgbe/firmware/t4fw_cfg.txt
@@ -15,10 +15,14 @@
sge_timer_value = 1, 5, 10, 50, 100, 200 # usecs
# TP_SHIFT_CNT
- reg[0x7dc0] = 0x64f8849
+ reg[0x7dc0] = 0x62f8849
filterMode = fragmentation, mpshittype, protocol, vlan, port, fcoe
+ # TP rx and tx channels (0 = auto).
+ tp_nrxch = 0
+ tp_ntxch = 0
+
# TP rx and tx payload memory (% of the total EDRAM + DDR3).
tp_pmrx = 38
tp_pmtx = 60
@@ -137,7 +141,7 @@
[fini]
version = 0x1
- checksum = 0xfdebb6ef
+ checksum = 0x6cc2514b
#
# $FreeBSD$
#
diff --git a/sys/dev/cxgbe/firmware/t4fw_cfg_uwire.txt b/sys/dev/cxgbe/firmware/t4fw_cfg_uwire.txt
index ca8de49..30a9f9f 100644
--- a/sys/dev/cxgbe/firmware/t4fw_cfg_uwire.txt
+++ b/sys/dev/cxgbe/firmware/t4fw_cfg_uwire.txt
@@ -109,7 +109,7 @@
reg[0x10a8] = 0x2000/0x2000 # SGE_DOORBELL_CONTROL
sge_timer_value = 5, 10, 20, 50, 100, 200 # SGE_TIMER_VALUE* in usecs
- reg[0x7dc0] = 0x64f8849 # TP_SHIFT_CNT
+ reg[0x7dc0] = 0x62f8849 # TP_SHIFT_CNT
# Selection of tuples for LE filter lookup, fields (and widths which
# must sum to <= 36): { IP Fragment (1), MPS Match Type (3),
@@ -124,6 +124,9 @@
# TP RX payload page size
tp_pmrx_pagesize = 64K
+ # TP number of RX channels
+ tp_nrxch = 0 # 0 (auto) = 1
+
# Percentage of dynamic memory (in either the EDRAM or external MEM)
# to use for TP TX payload
tp_pmtx = 50
@@ -131,6 +134,9 @@
# TP TX payload page size
tp_pmtx_pagesize = 64K
+ # TP number of TX channels
+ tp_ntxch = 0 # 0 (auto) = equal number of ports
+
# Some "definitions" to make the rest of this a bit more readable. We support
# 4 ports, 3 functions (NIC, FCoE and iSCSI), scaling up to 8 "CPU Queue Sets"
# per function per port ...
@@ -514,8 +520,8 @@
dwm = 30
[fini]
- version = 0x1425000b
- checksum = 0x7690f7a5
+ version = 0x1425000d
+ checksum = 0x25c2f782
# Total resources used by above allocations:
# Virtual Interfaces: 104
diff --git a/sys/dev/cxgbe/firmware/t4fw_interface.h b/sys/dev/cxgbe/firmware/t4fw_interface.h
index a4d7f6d..d9e4997 100644
--- a/sys/dev/cxgbe/firmware/t4fw_interface.h
+++ b/sys/dev/cxgbe/firmware/t4fw_interface.h
@@ -3430,6 +3430,7 @@ enum fw_memtype_cf {
FW_MEMTYPE_CF_EXTMEM = 0x2,
FW_MEMTYPE_CF_FLASH = 0x4,
FW_MEMTYPE_CF_INTERNAL = 0x5,
+ FW_MEMTYPE_CF_EXTMEM1 = 0x6,
};
struct fw_caps_config_cmd {
@@ -3518,6 +3519,7 @@ enum fw_params_param_dev {
*/
FW_PARAMS_PARAM_DEV_INTFVER_FCOEPDU = 0x15,
FW_PARAMS_PARAM_DEV_MCINIT = 0x16,
+ FW_PARAMS_PARAM_DEV_ULPTX_MEMWRITE_DSGL = 0x17,
};
/*
@@ -3576,7 +3578,8 @@ enum fw_params_param_dmaq {
FW_PARAMS_PARAM_DMAQ_EQ_CMPLIQID_MNGT = 0x10,
FW_PARAMS_PARAM_DMAQ_EQ_CMPLIQID_CTRL = 0x11,
FW_PARAMS_PARAM_DMAQ_EQ_SCHEDCLASS_ETH = 0x12,
- FW_PARAMS_PARAM_DMAQ_EQ_DCBPRIO_ETH = 0x13
+ FW_PARAMS_PARAM_DMAQ_EQ_DCBPRIO_ETH = 0x13,
+ FW_PARAMS_PARAM_DMAQ_CONM_CTXT = 0x20,
};
/*
@@ -3603,6 +3606,7 @@ enum fw_params_phyfw_actions {
enum fw_params_param_dev_diag {
FW_PARAM_DEV_DIAG_TMP = 0x00,
+ FW_PARAM_DEV_DIAG_VDD = 0x01,
};
#define S_FW_PARAMS_MNEM 24
@@ -6767,6 +6771,8 @@ struct fw_sched_cmd {
__u8 type;
__u8 minmaxen;
__u8 r3[5];
+ __u8 nclasses[4];
+ __be32 r4;
} config;
struct fw_sched_params {
__u8 sc;
@@ -7581,7 +7587,7 @@ struct fw_hdr {
__u8 intfver_fcoe;
__u32 reserved2;
__u32 reserved3;
- __u32 reserved4;
+ __u32 magic; /* runtime or bootstrap fw */
__be32 flags;
__be32 reserved6[23];
};
@@ -7620,14 +7626,40 @@ enum fw_hdr_chip {
(((x) >> S_FW_HDR_FW_VER_BUILD) & M_FW_HDR_FW_VER_BUILD)
enum {
- FW_HDR_INTFVER_NIC = 0x00,
- FW_HDR_INTFVER_VNIC = 0x00,
- FW_HDR_INTFVER_OFLD = 0x00,
- FW_HDR_INTFVER_RI = 0x00,
- FW_HDR_INTFVER_ISCSIPDU = 0x00,
- FW_HDR_INTFVER_ISCSI = 0x00,
- FW_HDR_INTFVER_FCOEPDU = 0x00,
- FW_HDR_INTFVER_FCOE = 0x00,
+ T4FW_VERSION_MAJOR = 0x01,
+ T4FW_VERSION_MINOR = 0x08,
+ T4FW_VERSION_MICRO = 0x0b,
+ T4FW_VERSION_BUILD = 0x00,
+
+ T5FW_VERSION_MAJOR = 0x01,
+ T5FW_VERSION_MINOR = 0x08,
+ T5FW_VERSION_MICRO = 0x16,
+ T5FW_VERSION_BUILD = 0x00,
+};
+
+enum {
+ T4FW_HDR_INTFVER_NIC = 0x00,
+ T4FW_HDR_INTFVER_VNIC = 0x00,
+ T4FW_HDR_INTFVER_OFLD = 0x00,
+ T4FW_HDR_INTFVER_RI = 0x00,
+ T4FW_HDR_INTFVER_ISCSIPDU = 0x00,
+ T4FW_HDR_INTFVER_ISCSI = 0x00,
+ T4FW_HDR_INTFVER_FCOEPDU = 0x00,
+ T4FW_HDR_INTFVER_FCOE = 0x00,
+
+ T5FW_HDR_INTFVER_NIC = 0x00,
+ T5FW_HDR_INTFVER_VNIC = 0x00,
+ T5FW_HDR_INTFVER_OFLD = 0x00,
+ T5FW_HDR_INTFVER_RI = 0x00,
+ T5FW_HDR_INTFVER_ISCSIPDU= 0x00,
+ T5FW_HDR_INTFVER_ISCSI = 0x00,
+ T5FW_HDR_INTFVER_FCOEPDU= 0x00,
+ T5FW_HDR_INTFVER_FCOE = 0x00,
+};
+
+enum {
+ FW_HDR_MAGIC_RUNTIME = 0x00000000,
+ FW_HDR_MAGIC_BOOTSTRAP = 0x626f6f74,
};
enum fw_hdr_flags {
diff --git a/sys/dev/cxgbe/firmware/t5fw-1.8.22.0.bin.uu b/sys/dev/cxgbe/firmware/t5fw-1.8.22.0.bin.uu
new file mode 100644
index 0000000..bfc993f
--- /dev/null
+++ b/sys/dev/cxgbe/firmware/t5fw-1.8.22.0.bin.uu
@@ -0,0 +1,8112 @@
+/*-
+ * Copyright (c) 2013 Chelsio Communications, Inc.
+ * All rights reserved.
+ *
+ * Redistribution and use in source and binary forms, with or without
+ * modification, are permitted provided that the following conditions
+ * are met:
+ * 1. Redistributions of source code must retain the above copyright
+ * notice, this list of conditions and the following disclaimer.
+ * 2. Redistributions in binary form must reproduce the above copyright
+ * notice, this list of conditions and the following disclaimer in the
+ * documentation and/or other materials provided with the distribution.
+ *
+ * THIS SOFTWARE IS PROVIDED BY THE AUTHOR AND CONTRIBUTORS ``AS IS'' AND
+ * ANY EXPRESS OR IMPLIED WARRANTIES, INCLUDING, BUT NOT LIMITED TO, THE
+ * IMPLIED WARRANTIES OF MERCHANTABILITY AND FITNESS FOR A PARTICULAR PURPOSE
+ * ARE DISCLAIMED. IN NO EVENT SHALL THE AUTHOR OR CONTRIBUTORS BE LIABLE
+ * FOR ANY DIRECT, INDIRECT, INCIDENTAL, SPECIAL, EXEMPLARY, OR CONSEQUENTIAL
+ * DAMAGES (INCLUDING, BUT NOT LIMITED TO, PROCUREMENT OF SUBSTITUTE GOODS
+ * OR SERVICES; LOSS OF USE, DATA, OR PROFITS; OR BUSINESS INTERRUPTION)
+ * HOWEVER CAUSED AND ON ANY THEORY OF LIABILITY, WHETHER IN CONTRACT, STRICT
+ * LIABILITY, OR TORT (INCLUDING NEGLIGENCE OR OTHERWISE) ARISING IN ANY WAY
+ * OUT OF THE USE OF THIS SOFTWARE, EVEN IF ADVISED OF THE POSSIBILITY OF
+ * SUCH DAMAGE.
+ */
+begin-base64 644 t5fw
+AAEDhAEIFgAAAQQFAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAABAAAAAAAAAAAAAAAAAAAAAAAAAAAA
+AAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAA
+AAAAAAAAAAAAAAAAAAAAAAAAA2kDeAN/AAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAA
+AAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAA
+AAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAA
+AAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAA
+AAAAAAAAAAAAAENoZWxzaW8gRlcgUlVOTUVNIERFQlVHPTAgKEJ1aWx0IE1vbiBKdWwgIDEgMTI6
+MDk6MzEgUERUIDIwMTMgb24gY2xlb3BhdHJhLmFzaWNkZXNpZ25lcnMuY29tOi9ob21lL2Zpcm13
+YXJlL2N2cy9mdy1yZWxlYXNlKSwgVmVyc2lvbiBUNXh4IDAxLjA4LjE2LjAwAAAAAAAAAErb7x1g
+AMAAH/zhSOEAMLh4////H/zhQIAAAAHhAHtwAAAQAB//9sggAAAA4QGcBOEFAAAAAgBA4QUIAAAG
+AEAAAgAMAAYADOEFAAQACgAAgAABAuEAezzhAHtE4QB75OIAAAAAAQAA4QB7kCAAAAAAAIAA4QB7
+AAAAQAHhAHucAABAAERERELgAAAA4wAEc0REREDjAAgAIAACXAAAAAAf/49gAAAAAB//j2QAAAAA
+H/+PaAAAAAAf/49sH//AAAAAAAAAAAAAwAAR/88iCoeSEIIQEv/OE//OhCAEMwGTIBH/zBL/zZIQ
+Ef/MEv/NkhAR/8wB9DEA5DEABTEBAgAS/8oC5zECFgAR/8iBEAEBX8AhAhEByRMR/8YS/8aSEBH/
+xhL/xpIQYAARAAAR/8AS/8SSEBH/wBL/w5IQgRAR/8LAIJIREv/BkhLAIJITEv/AkhCCEALyUGUv
+9xH/vscvkhAR/72SEBL/vRP/vZMgwDKTIRP/vJMigiIS/7sT/7uTICMiIRT/ugQzAck4E/+5gzAD
+gxQIMxEU/7ekM5MhE/+rkyJgAAjCMJMhE/+okyIS/7KQIJAhkCKQI5AkkCWQJpAnkCiQKZAqkCuQ
+LJAtkC6QLyAmECAmEYIiEv+lwDAtNzAtNzQtNzgtNzwjPQFyM+0AAgAS/6IjCgAvNwAvNxAvNyAv
+NzAjPQFyM+0AAgAS/5fAMCg3MCg3NCg3OCg3PCM9AXIz7QMCABL/lcAwJzcAJzcQJzcgJzcwIz0B
+cjPtAwIAEv+PFf+QFv+QwDDXIAVmAWAAHgAAAAAAAAAAAAAAAAQ2BQACANMP0w8FMwxuOxQHRxQH
+BEN2MeYENgUFMwxvO+0AAgAS/4EV/38jCgACJwIHBEMEPgUFMwwHRxRvO/ADAgAS/3vJLoMghCGF
+IrwidDsOhlC0VZYwtDN0M/Rj/+YAZT/iZV/fEv9vwDIDLgUDAgAS/2bAMCg3QCg3RCg3SCg3TCM9
+AXIz7QMCAAACABL/aS0nAMARAUkxAEgxAQIAwAAU/2UE0jEV/2SUUBT/ZATTMRX/ZJRQFP9jBNQx
+Ff9jlFAU/2ME1TEV/2KUUBD/YgMAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAA
+AAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAA
+AAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAA
+AAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAA
+AAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAA
+AAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAA
+AAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAA
+AAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAA
+AAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAA
+AAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAA
+AAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAA
+AAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAA
+AAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAf/AAA
+H/wAAOMACfgf/AAAH/wAAOMACfgf/AAAH/wAAOMACfgf/4AAH/+H8OMACfgf/4fwH/+H8OMAEegf
+/4fwH/+H8OMAEegf/4fwH/+JoOMAEegf/4mgH/+PYOMAE5gf/49gH/+rjOMAGVgf/6uMH/+rjOMA
+NYQf/8AAH//3xeMANYQgAAAAIAABauMAbUwgAAF4IAABfOMAbrggAAF8IAABheMAbrwgAAGYIAAB
+nOMAbsggAAGcIAABpeMAbswgAAG4IAABvOMAbtggAAG8IAABxeMAbtwgAAHYIAAB2OMAbuggAAHc
+IAAB4uMAbuggAAH4IAAB+OMAbvAgAAH8IAAB/OMAbvAgAAIYIAACGOMAbvAgAAIcIAACHOMAbvAg
+AAI4IAACOOMAbvAgAAI8IAACPOMAbvAgAAJYIAACWOMAbvAgAAJcIAACYuMAbvAgAAJ4IAACeOMA
+bvggAAJ8IAACguMAbvggAAKYIAGDZuMAbwAgAoAAIAKXlOMB79AgApeUIAKXlOMCB2QgApeYIAXg
+GOMCB2ggBeAgIAXkAOMFT/AgBoAAIAaZwOMFU9AgBpnAIAd+UOMFbZAgB35QIAd/0OMGUiAgCMAA
+IAjAAOMGU6AgCMAAIAjAAOMGU6AgCMAAIAk9z+MGU6AAAAAAAAAAAAAAAAAgAA/uIAAP4CAAE/kg
+AA/gIAATZSAAD+AgABCVIAAS/SAAEoUgAA/gIAASMCAAEeIgABF1IAAPzSAAER8gAA/gIAAP4CAA
+D+AgABC1AAAAAP///////w/8///w////APwgAId9IACIpyAAiNggAIifIACIZSAAiF4gAIgiIACI
+GiAAiBIgAIfDIACI1iAAh7sgAIekAAAAAAAAAAAAAAAAAAAACgAAAAoAAAAUAAAACgAAAAoAAAAK
+AAAACgAAAAoAAAAKAAAAAAAAAAAAAAAAAAAIAAAAEAAAAEAAAAEAAAAACAAAABAAAABAAAABAAAA
+BAAAABAAAABAAAABAAAA/xgwYGAAAAD/AAECAgAAABAgQAAAAAAAAAAAAAAAAAAEAAIAAQAAgABA
+ACAAEAAIIAKO5AAAAAEgAo7oAAAAAiACkbQAAAD/IAKNBAAAAP8gAo0EAAAAACACkbQAAAAAIAKO
+HAAAAAEgAo4kAAAABCACjiwAAAAIIAKOOAAAABAgAo5AAAAAICACjkgAAABAIAKOUAAAAIAgAo5k
+AAABACACjngAAAIAIAKOkAAABAAgAo6kAAAIACACjrQAABAAIAKOwAAAIAAgAo7UAABAAAgEAgAA
+AAAAAAAAAAAAAAAgAo4IAAAAECACjhAAAAARIAKNiAAAAQAgAo2UAAAAgCACjaQAAABAIAKNtAAA
+ACAgAo3EAAAAECACjdQAAAAIIAKN4AAAAAQgAo3sAAAAAiACjfgAAAABAIDA/wAAAAAgAV5pIAFe
+/yABXikgAV7SIAFesSABXowgAV4/AAAAACAHZ1ggB2cSIAdnTiAHZ04gB2cSIAdnEiAHZ1ggB2dY
+IAdnEiAHZ1ggB2cSIAdnWCAHZ04gB2cSIAdnEiAHZxIgB2cSIAdnEiAHZ1ggB2cSIAdnEiAHZxIg
+B2cSIAdnEiAHZ1ggB2dYIAdnWCAHZ1ggB2dYIAdnWCAHZ1ggB2dYIAdnEiAHZxIgB2cSIAdnEiAH
+ZxIgB2cSIAdnEiAHZxIgB2cSIAdnEiAHZxIgB2cSIAdnEiAHZxIgB2cSIAdnEgACAgUFCAgLCw4O
+EREUFBcXGhodHSAgIyMmJikpLCwvLzIyNTU4ODs7AAAAAAAAAAEDEREICBAJAwEAAAAAAAAgA+38
+IAFGcCAANdAgASw4IAFCWCABPRAgARGoIAMgtB//5Wgf/+IkIACJZB//1tAgAFmwIABM2AAAAAAA
+AAAAIAEtmCAAd9gAAAAAAAAAAB//0ewf/8WIH//DZB//wXAgAElEIABB5CAAQEggAIBwH//bSCAF
+sRAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAIAFMcCABNhggAJCsIACP
+0B//6ggf/804H//J0CAAdZAgBET4IAD1eCAA2JAgAMP4IAC+KCAArmggAKTcIACSpCAD8BwgAz4w
+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
+AE01oAAASLkf/5UcH/+ZHOEALgAf/5kg4QMGAP//v//hAA4AH/+aJAAAC2gf/5toH/+bZAAADaAA
+AP+AH/+bXB//m8gD/wAA/7///x/8v/88AAAAAAf//4MAAAAgCQuwIAF2KB//msQAAAAAAAAAAAAA
+AAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAA
+AAAAAACBgAAAAAAAAB//9NAf//TQH//0kB//9JAf//SQH//0kB//7mAf//K0H//w8B//8PAf//Dw
+IAWy+AAAAAAAAAAAAAAAAAAAAAAgBbVoIAW1aAAAAAAAAAAAAAAAAAAAAAAgAWA8IAWy+B//87Qf
+//O0H//ztB//87Qf//O0H//ztAAAAAAf/+5oAAAAAAAAAAAAAAAAAAAAAAIBAAAAAAAAAAAAAAAA
+AAAEAAAAAAAAAIGAAAAAAAAQBAAAAAAAAAKBAAAAAAAACAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAA
+AAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAA
+AAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAA
+AAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAA
+AAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAA
+AAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAA
+AAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAA
+AAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAA
+AAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAA
+AAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAA
+AAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAA
+AAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAA
+AAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAA
+AAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAA
+AAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAA
+AAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAA
+AAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAA
+AAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAA
+AAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAA
+AAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAA
+AAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAA
+AAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAA
+AAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAA
+AAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAA
+AAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAA
+AAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAA
+AAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAA
+AAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAA
+AAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAA
+AAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAA
+AAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAA
+AAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAA
+AAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAA
+AAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAA
+AAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAA
+AAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAA
+AAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAA
+AAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAA
+AAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAA
+AAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAA
+AAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAA
+AAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAA
+AAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAA
+AAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAA
+AAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAA
+AAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAA
+AAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAA
+AAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAA
+AAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAA
+AAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAA
+AAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAA
+AAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAA
+AAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAA
+AAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAA
+AAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAA
+AAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAA
+AAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAA
+AAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAA
+AAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAA
+AAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAA
+AAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAA
+AAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAA
+AAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAA
+AAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAA
+AAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAA
+AAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAA
+AAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAA
+AAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAA
+AAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAA
+AAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAA
+AAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAA
+AAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAA
+AAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAA
+AAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAA
+AAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAA
+AAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAA
+AAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAA
+AAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAA
+AAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAA
+AAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAA
+AAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAA
+AAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAA
+AAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAA
+AAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAA
+AAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAA
+AAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAA
+AAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAA
+AAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAA
+AAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAA
+AAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAA
+AAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAA
+AAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAA
+AAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAA
+AAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAA
+AAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAA
+AAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAA
+AAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAA
+AAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAA
+AAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAA
+AAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAA
+AAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAA
+AAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAA
+AAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAA
+AAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAA
+AAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAA
+AAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAA
+AAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAA
+AAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAA
+AAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAA
+AAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAA
+AAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAA
+AAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAA
+AAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAA
+AAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAA
+AAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAA
+AAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAA
+AAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAA
+AAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAA
+AAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAA
+AAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAA
+AAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAA
+AAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAA
+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
+cJmh0Q8cgb6cMIsg+LsRAAEQYDD0NgIqCQBm8Jsx0Q9sEARkIGJkMF9kUFyIIvmBqhA+AlDw+koU
+AE8AtiAECEcMiBGpiCmCOnqTQCyCORmByfjMAAA3ADcgAAmLbakCAEhhHoGinsCNIJpQG4Gj88YD
+LYAEP2D7xgIsCQBusP3GASAgAhMw0Q/AINEP2iD7TBgiAABgsFv94cAg0Q8AACAC0HUgAtCLIALS
+GCAC0qcgAtMnIALToiAC1JggAtVUARAYAQABAAAAAAAAAAAAACADZuIgA2biIANk7iADZuIgA2Tu
+IANmvSADZpYgA2TuIANlySADZO4gA2TuIANlwCADZO4gA2biIANk7iADZO4gA2biIANk+wAAAAAA
+AAAAAwEAAgAAAAAAAAAAAAAAACAD5fIgA+YgIAPnpCAD5fIgA+ecIAPnhSAD5fIgA+XyIAPl8iAD
+5fIgA+XyIAPl8iAD5fIgA+XyIAPl8iAD5fIgA+d6IAPnbyAD5fIgA+XyIAPl8iAD5fIgA+XyIAPl
+8iAD5fIgA+XyIAPl8iAD5fIgA+XyIAPl8iAD5fIgA+XyIAPnRCAD5fIgA+c8IAPnPCAD5fIgA+Xy
+IAPnHyAD5fIgA+c8IAPl8iAD5fIgA+XyIAPl8iAD5fIgA+XyIAPl8iAD5fIgA+XyIAPl8iAD5fIg
+A+XyIAPl8iAD5fIgA+XyIAPl8iAD5fIgA+XyIAPl8iAD5fIgA+XyIAPl8iAD5fIgA+cCAAAAAAAA
+AAAAAAAAIAQD2CAEA9ggBAMkIAQD2CAD/uggBAPQIAQDriAD/uggA/7oIAP+6CAD/uggA/7oIAQD
+ByAEA9ggA/7oIAQDByAEA9gAAAAAAAAAAAAAAAAgBBCRIAQQ5yAEEzwgBBLqIAQSniAEEnIgBBJi
+IAQSXCAEEbgAAAAAAAAAAAAAAAABEBgBAAIAAAEQGAEAAgAAIARhziAEYaMgBGALIARhayAEYWsg
+BGFrIARgCyAEYWsgBGALIARhTCAEYAsgBGALIARhTCAEYWsAAAAAAAAAACAEhbggBIFxIASFjCAE
+hWAgBIU0IASBkyAEgZMgBIGqIASFGyAEgd8gBIHLIASBkyAEgd8gBIGTIASBkyAEgZMgBIW4AAAA
+AAAAAAAAAAAAAAAAACAEzaAgBMgAIATHqCAExcgAAAAAAAAAAAAAAAAgBMfoIATH4iAEx+IgBMfi
+IATH6CAEx+IgBMfiIATH4iAEx+IgBMfiIATH4iAEx+IgBMfiIATH4iAEx+IgBMfiIATH4iAEx+Ig
+BMfiIATH4iAEx+IgBMfiIATH4iAEx+IgBMfiIATH4iAEx+IgBMfiIATH4iAEx+IgBMfiIATH4iAE
+x+IgBMfiIATH4iAEx+IgBMfiIATH4iAEx+IgBMfiIATH4iAEx+IgBMfiIATH4iAEx+IgBMfiIATH
+4iAEx+IgBMfiIATH4iAEx+IgBMfiIATH4iAEx+IgBMfiIATH4iAEx+IgBMfiIATH4iAEx+IgBMfo
+IATH4iAEx+IgBMfiIATH6CAEx+IgBMfiIATH4iAEx+ggBMfiIATH4iAEx+IgBMfoIATH4iAEx+Ig
+BMfiIATH6CAEx+IgBMfiIATH4iAEx+ggBMfiIATH4iAEx+IgBMfoIATH4iAEx+IgBMfiIATH6CAE
+x+IgBMfiIATH4iAEx+ggBMfiIATH4iAEx+IgBMfoIATH4iAEx+IgBMfiIATH6CAEx+IgBMfiIATH
+4iAEx+ggBMfiIATH4iAEx+IgBMfoIATH4iAEx+IgBMfiIATH6CAEx+IgBMfiIATH4iAEx+ggBMfi
+IATH4iAEx+IgBMfoIATH4iAEx+IgBMfiIATH4iAEx+IgBMfiIATH4iAEx+IgBMfiIATH4iAEx+Ig
+BMfiIATH4iAEx+IgBMfiIATH4iAEx+IgBMfiIATH4iAEx+ggBMfiIATH4iAEx+IgBMfiIATH4iAE
+x+IgBMfiIATH4iAEx+IgBMfiIATH4iAEx+ggBMfiIATH4iAEx+IgBMfiIATH4iAEx+IgBMfiIATH
+4iAEx+IgBMfiIATH4iAEx+IgBMfiIATH4iAEx+IgBMfiIATH4iAEx+IgBMfiIATH4iAEx+IgBMfi
+IATH4iAEx+ggBMfiIATH4iAEx+IgBMfoIATH4iAEx+IgBMfiIATH6AAAAAAAAAAAAAAAACAEyDgg
+BMmNIATLOyAEyy0gBMsYIATLCiAEyvUgBMrnIATK0iAEysUgBMqyIATKpSAEypIgBMqFIATKciAE
+yDAgBMpfIATKUiAEyj8gBMoyIATKHiAEyhQgBMoDIATJ+SAEyeggBMgwIATIMCAEyDAgBMgwIATI
+MCAEyDAgBMnbIATIUSAEyDAgBMgwIATIMCAEyb4gBMmlIATJmyAEyXwgBMgwIATJaSAEyU8gBMk8
+IATJIiAEyQ8gBMj3IATI5CAEyNEgBMhrAAAAAAAAAAAgBM3gIATN6iAEzzogBM8PIATPCCAEzwEg
+BM76IATO8yAEzuwgBM7lIATO3iAEztQgBM7KIATOrCAEzkIgBM3VIATOOSAEziMgBM4ZIATOEiAE
+zgUgBM3+IATN0iAEzfcgBQJqIAUCYCAFAmYgBQJqIAUCYCAFAmAgBQJgIAUCZiAFAmogBQJqIAUC
+aiAFAmAgBQJqIAUCYCAFAmQgBQJqAAAAAAAAAAAAAAAAAAAAACAFKsIgBSrYIAUsZiAFLPYgBS11
+IAUt6SAFLs4gBS+LIAVPeCAFTNwgBUtwIAVJVCAFR2AgBUCwIAVBYCAFRjAgBUAoAAAAAAMPCBKD
+jpOSIAV+PSAFfpwgBX6PIAV+giAFfnEgBX5kIAV+VyAFfkogBX7JIAV/NCAFfycgBX8aIAV/DSAF
+fvwgBX7rIAV+2iAFf2EgBX/IIAV/tyAFf6YgBX+VIAV/iCAFf3sgBX9uIAV/+SAFgFwgBYBPIAWA
+QiAFgDUgBYAkIAWAEyAFgAYgBYCJIAWA7CAFgN8gBYDSIAWAxSAFgLggBYCnIAWAliAFj1AgBY/K
+IAWPvCAFj64gBY+aIAWPhiAFj3IgBY9eIAWP2CAFkG4gBZBaIAWQRiAFkDIgBZAeIAWQBSAFj+wg
+BZCCIAWRLCAFkRMgBZD6IAWQ4SAFkMggBZC0IAWQmyAFkUUgBZHvIAWR1iAFkb0gBZGkIAWRiyAF
+kXIgBZFeIAWSCCAFkrIgBZKZIAWSgCAFkmcgBZJOIAWSNSAFkhwgBZLLIAWTeiAFk2EgBZNIIAWT
+LyAFkxYgBZL9IAWS5CAFk4ggBZQnIAWUGSAFlAUgBZPsIAWT0yAFk7ogBZOhIAWUQCAFlOogBZTR
+IAWUuCAFlKQgBZSLIAWUciAFlFkgBZUDIAWVrSAFlZQgBZV7IAWVYiAFlU4gBZU1IAWVHCAFlcYg
+BZZwIAWWVyAFlj4gBZYlIAWWDCAFlfggBZXfIAWWiSAFlzMgBZcaIAWXASAFluggBZbPIAWWtiAF
+lqIgBZdMIAWX5iAFl9IgBZe+IAWXpSAFl4wgBZdzIAWXWiAFl/ogBZieIAWYhSAFmGwgBZheIAWY
+RSAFmCwgBZgTIAWGDyAFjwsgBZkvIAWZFiAFmP0gBZjpIAWY0CAFmLcgBaFIIAWh9yAFod4gBaHF
+IAWhrCAFoZMgBaF6IAWhYSAFogsgBaK6IAWioSAFooggBaJvIAWiViAFoj0gBaIkIAWi0yAFo4Ig
+BaNpIAWjUCAFozcgBaMeIAWjBSAFouwgBaObIAWkRSAFpCwgBaQTIAWj+iAFo+EgBaPIIAWjryAF
+pF4gBaUNIAWk9CAFpNsgBaTCIAWkqSAFpJAgBaR3IAWlJiAFpcogBaW8IAWloyAFpYogBaVxIAWl
+WCAFpT8gBaXjIAWmkiAFpnkgBaZgIAWmRyAFpi4gBaYVIAWl/CAFpqsgBadKIAWnMSAFpyMgBacP
+IAWm9iAFpt0gBabEIAWnYyAFqAcgBafuIAWn1SAFp7wgBaejIAWniiAFp3wgBYo3IAWKUCAFqJ0g
+BaiEIAWoayAFqFIgBag5IAWoICAFmUggBZn3IAWZ3iAFmcUgBZmsIAWZkyAFmXogBZlhIAWKryAF
+itAgBZqIIAWabyAFmlYgBZo9IAWaJCAFmgsgBZqcIAWbQSAFmyggBZsUIAWbACAFmucgBZrOIAWa
+tSAFm1ogBZv6IAWb5iAFm80gBZu0IAWbmyAFm4IgBZtuIAWcDiAFnLggBZyfIAWchiAFnG0gBZxU
+IAWcQCAFnCcgBZzRIAWdeyAFnWIgBZ1JIAWdMCAFnRwgBZ0DIAWc6iAFnZQgBZ4zIAWeGiAFngYg
+BZ34IAWd3yAFncYgBZ2tIAWeTCAFnvYgBZ7iIAWeySAFnrAgBZ6XIAWefiAFnmUgBZ8PIAWfviAF
+n6UgBZ+MIAWfcyAFn1ogBZ9BIAWfKCAFn9IgBaB8IAWgYyAFoEogBaAxIAWgGCAFn/8gBZ/mIAWg
+lSAFoS8gBaEWIAWg/SAFoOQgBaDQIAWgvCAFoK5SVgAAAAAAAEHNzWUAAAAAQS6EgAAAAAAQAAAA
+Y29uZmlndXJhdGlvbiBmaWxlIHBhcnNlciBmb3VuZCBhZGRpdGlvbmFsIGNvbmZpZ3VyYXRpb24g
+YWZ0ZXIgW2ZpbmldCgAAMAAAAGZjb2UAAAAAdm5pY19pZAB2bGFuAAAAAHRvcwBldGhlcnR5cGUA
+AABtYWNtYXRjaAAAAABtcHNoaXR0eXBlAABmcmFnbWVudGF0aW9uAAAAc3J2cnNyYW0AAAAAZmNv
+ZW1hc2sAAAAAY29tcGFjdABiYXNpY3ZpcnR1YWwAAAAAc3lubWFwZW4AAAAAc3luNHR1cGVuaXB2
+NgAAAHN5bjJ0dXBlbmlwdjYAAABzeW40dHVwZW5pcHY0AAAAc3luMnR1cGVuaXB2NAAAAG9mZG1h
+cGVuAAAAAHRubG1hcGVuAAAAAHRubGFsbGxrcAAAAGhhc2h0b2VwbGl0egAAAAB0cF9waW8AAHRw
+X3RtX3BpbwAAAG5pY192bQAAbmljX3VtAABuaWNfdW1faXNnbABvZmxkAAAAAHJkZHAAAAAAcmRt
+YWMAAABpc2NzaV9pbml0aWF0b3JfcGR1AGlzY3NpX3RhcmdldF9wZHUAAAAAaXNjc2lfaW5pdGlh
+dG9yX2ZvZmxkAAAAaXNjc2lfdGFyZ2V0X2ZvZmxkAABmY29lX2luaXRpYXRvcgAAZmNvZV90YXJn
+ZXQAcG9mY29lX2luaXRpYXRvcgAAAABwb2Zjb2VfdGFyZ2V0AAAAcHBwAGRjYngAAAAAIAKXNAAE
+AAAAAAQAAAQAACAFx+wgBXooIAXH2CAFejggBXpMIAV5kCAFetAgBXlsAAECAwAAAAAAAAAAAAAA
+ACADNbQgAzVAIAQKEAAAAAAgAzU4IAM1MCADNSgAAAAAMDEyMzQ1Njc4OWFiY2RlZkFCQ0RFRgAA
+AAAAAAAAAABBQkNERUZHSElKS0xNTk9QUVJTVFVWV1hZWmFiY2RlZmdoaWprbG1ub3BxcnN0dXZ3
+eHl6MDEyMzQ1Njc4OSsvAAAAAAAAAAAAAAAAAAAAACAAH+AAAQAAIAAfwAAFAAEgAB+kAAMABSAA
+H4QABwAAY4JTYwAAAAD///////8AAAAEBAgAB0MAAAAAAAAAAAAAIQAJAIEAQQAhAAkAIQAJAIEA
+QQCBAEEAgQBBAIEAQQAhAAkAgQBBACEACQAhABEAgQBBAIEAQQAhAAkAgQBBACEACQAhAAkBAQBB
+AIEAIQCBACEAEBBhAQEAgQEBAIEDAgEAIECAAAAAAAAAAAAAIAKNBAAAAAAgApG0AAAAACACjQgA
+AAABIAKRwAAAAAIgAo0QAAAABCACjRgAAAAIIAKNIAAAABAgApHIAAAAICACjSQAAABAIAKNMAAA
+AIAgAo08AAABACACjUgAAAIAIAKNWAAAIAAgAo1kAABAACACjXAAAIAAAAAAAAAAAAAAAAIEAAAA
+RAAAAAQAAABAIAVTsCAFU2wgBVJEIAVQ+CAFULQgBVCIIAVQ4AAAAAAAAAIAAAAEAAAACABOQQAA
+VjAAAFYxAABWMgAAVjMAAFY0AABWNQAAVjYAAFY3AABWOAAAVjkAAFZBAABWQgAAVkMAAFZEAABW
+RQAAVkYAAFZHAABWSAAAVkkAAFZKAABWSwAAVkwAAFZNAABWTgAAVk8AAFZQAABWUQAAVlIAAFZT
+AABWVAAAVlUAAGFsbAAqAAAAbm9uZQAAAAAweAAAcG9ydAAAAABwcm90b2NvbAAAAABnbG9iYWwA
+AGZ1bmN0aW9uAAAAAGZpbmkAAAAAcmVnAGZpbHRlck1vZGUAAGZpbHRlck1hc2sAAHJzc19nbGJf
+Y29uZmlnX21vZGUAcnNzX2dsYl9jb25maWdfb3B0aW9ucwAAc2dlX3RpbWVyX3ZhbHVlAHRwX3Bt
+cngAdHBfcG1yeF9wYWdlc2l6ZQAAAAB0cF9ucnhjaAAAAAB0cF9wbXR4AHRwX3BtdHhfcGFnZXNp
+emUAAAAAdHBfbnR4Y2gAAAAAbXR1cwAAAABwY2llX21hX3JzcF90aW1lcnZhbHVlAABwbF90aW1l
+b3V0X3ZhbHVlAAAAAGJhcjJ0aHJvdHRsZWNvdW50AAAAY3BsdHhkYXRhX21lbWFsaWduAABudmYA
+d3hfY2FwcwByX2NhcHMAAG5pcWZsaW50AAAAAG5lcQBuZXRoY3RybAAAAABudmkAcnNzbnZpAABu
+ZXhhY3RmAGNtYXNrAAAAcG1hc2sAAABuZXRob2ZsZAAAAABucm91dGUAAG5jbGlwAAAAbmZpbHRl
+cgBuc2VydmVyAG5oYXNoAAAAdHBfbDJ0AAB0cF9kZHAAAHRwX2RkcF9pc2NzaQAAAAB0cF9zdGFn
+AHRwX3BibAAAdHBfcnEAAABpc2NzaV9udGFzawBpc2NzaV9uc2VzcwBpc2NzaV9uY29ubl9wZXJf
+c2Vzc2lvbgBpc2NzaV9uaW5pdGlhdG9yX2luc3RhbmNlAAAAaXNjc2lfbWF4X3NnZQAAAHBwbV9t
+YXhfem9uZXMAAABwcG1fem9uZV9yYW5nZTAAcHBtX3pvbmVfcmFuZ2UxAHBwbV96b25lX3Jhbmdl
+MgBwcG1fem9uZV9yYW5nZTMAZmNvZV9uZmNmAAAAZmNvZV9udm5wAAAAZmNvZV9uc3NuAAAAZGNi
+AGJnX21lbQAAbHBia19tZW0AAAAAaHdtAGx3bQBkd20AdmVyc2lvbgBjaGVja3N1bQAAAAAwMTIz
+NDU2Nzg5YWJjZGVmZ2hpamtsbW5vcHFyc3R1dnd4eXoAAAAAWWVzAFNlc3Npb25UeXBlAE9GTWFy
+a2VyAAAAAElGTWFya2VyAAAAAERhdGFEaWdlc3QAAEhlYWRlckRpZ2VzdAAAAABFcnJvclJlY292
+ZXJ5TGV2ZWwAAEltbWVkaWF0ZURhdGEAAABEYXRhUERVSW5PcmRlcgAARGF0YVNlcXVlbmNlSW5P
+cmRlcgBJbml0aWFsUjJUAABNYXhDb25uZWN0aW9ucwAATWF4UmVjdkRhdGFTZWdtZW50TGVuZ3Ro
+AAAAAEluaXRpYXRvck5hbWUAAABJbml0aWF0b3JBbGlhcwAARGVmYXVsdFRpbWUyV2FpdAAAAABE
+ZWZhdWx0VGltZTJSZXRhaW4AAE1heEJ1cnN0TGVuZ3RoAABGaXJzdEJ1cnN0TGVuZ3RoAAAAAE1h
+eE91dHN0YW5kaW5nUjJUAAAAVGFyZ2V0TmFtZQAAVGFyZ2V0QWxpYXMAVGFyZ2V0QWRkcmVzcwAA
+AFRhcmdldFBvcnRhbEdyb3VwVGFnAAAAAEF1dGhNZXRob2QAAFNlbmRUYXJnZXRzPUFsbABDSEFQ
+X0EAAENIQVBfSQAAQ0hBUF9DAABDSEFQX04AAENIQVBfUgAARGlzY292ZXJ5AAAATm9ybWFsAABO
+b25lAAAAAENSQzMyQwAAQ1JDMzJDLE5vbmUATm9uZSxDUkMzMkMAQ0hBUAAAAABDSEFQLE5vbmUA
+AABOb25lLENIQVAAAABOb3RVbmRlcnN0b29kAAAASXJyZWxldmFudAAAUmVqZWN0AABObwAANQAA
+AENITmV0IDEuMDAAAAAAAAwAAAAAAQABfAAMAQAAAAAQAAAAFCAF23AAAAL9DkAAAB/8AAAf/AAA
+H/+rkB//q5AgBeQAIAXncCAHf9AgB3/QIAfAACAIEEAgCEAAIAjAAAAAAAAgCQyAIAkM4AgAAAAf
+/OLkIAKm0B//mbAf/5vwAQAAACgAAAAgCMAA4QAAAAAgAAAf/4SwH/+FoB//gyAgCMBQIAjAkCAI
+wNAgAo8gIAKMoCACj3AgAo7wIAkPMCACjtAgCQ9gIAkPgAAAgAAAD0JAIAAgAAACAADgAAAAIAjD
+ECAIwuAACAAAIAjDcCAIw0AgAt58IAkOEB//j7Af/5mkAP///yAIw7AgCQ+wIAjD4CAIxCAgCMSA
+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
+AAKLCgACtnEAAAAeAAAALQABBGoAAGyBAABMlwACiwoAAgjVAAKLCgACtnEAAAAeAAAALQABhqAA
+AG+bAABWzgACiwoAAgjVAAKLCgADDUAAAAAeAAAAKAABWzgAAG+bAABUdQACiwoAAgjVAAKLCgAD
+DUAAAAAeAAAAKAABOIAAAG+bAABSPAACiwoAAgjVAAKLCgADDUAAAAAeAAAAKAABHBgAAG+cAABQ
+IQACiwoAAgjWAAKLCgADDUAAAAAeAAAAKAABbHQAAHLjAABXYwADDUAAAosKAAKLCgADDUAAAAAb
+AAAAIwABS1IAAHLjAABVWAADDUAAAosKAAKLCgADDUAAAAAbAAAAIwABL7YAAHLjAABTZQADDUAA
+AosKAAKLCgADDUAAAAAbAAAAIwABGFkAAHLjAABRiAADDUAAAosKAAKLCgADDUAAAAAbAAAAIwAB
+eroAAHZfAABaPgADDUAAAosKAAKLCgADDUAAAAAZAAAAIwABWysAAHZfAABYVgADDUAAAosKAAKL
+CgADDUAAAAAZAAAAIwABQV4AAHZfAABWgQADDUAAAosKAAKLCgADDUAAAAAZAAAAIwABKZMAAHZg
+AABUwQADDUEAAosLAAKLCwADDUEAAAAZAAAAIwABESoAAG+bAABP3QACCNUAAgjVAAKLCgADDUAA
+AAAeAAAAKAABFwQAAGyBAABOIAACiwoAAosKAAKLCgADDUAAAAAbAAAAIwAAAAAAACtoAAAjgwAA
+GGoAAA0GAAALKgAAAAAAAAAAAAAAAAABABAAEQAeAA0AEgAHAB8ADgALABMAFQAbAAgAFwAgAA8A
+HQAMAAYACgAUABoAFgAcAAUACQAZAAQAGAADAAIAAQB7AHUA+wD1AEUAbwB9AHcAXwBpAMUA7wCj
+AD8A/QD3ALsAOQDfAOkAzwCdAEcAcQAPAFkAJQC/ABMAYwB/AHkAbQBdAD0AuQCbAA0AYQBrAAsA
+CQBRAB8AMQBTAMcA8QAhALUAjwDZAK0AMwClAEEAVQCXAJMA4wApAMkA/wD5APMAwwDtAN0A5wAj
+AL0AOwC3AE8AHQCNANcAkQDhAOsA2wAbAIsAiQCHAK8A0QCfADUALQCxANMAFwCnAEkAcwBDAGcA
+oQA3AM0AVwARAFsAmQAHAC8AswCrAJUAJwDBAOUATQDVABkAhQArABUAZQDLAAUAqQBLAIMAAwCB
+AAAA+gD0AHwAdgDEAO4A/AD2AN4A6ABGAHAAJAC+AH4AeAA8ALgAYABqAFAAHgDGAPAAjgDYAKQA
+QACSAOIA/gD4AOwA3AC8ADoAHACMAOAA6gCKAIgA0ACeALAA0gBIAHIAoAA2ABAAWgAuALIAJgDA
+ANQAGAAUAGQAqABKAIAAegB0AEQAbgBeAGgAogA+ALoAOADOAJwADgBYABIAYgBsAFwAmgAMAAoA
+CAAwAFIAIAC0AKwAMgBUAJYAKADIAPIAwgDmACIAtgBOANYAkADaABoAhgCuADQALAAWAKYAQgBm
+AMwAVgCYAAYAqgCUAOQATACEACoAygAEAIIAAgABAAMAAgAEAAAAAAAAAAAAAQACABwAGwAWABUA
+HgAdABgAFwAMAAsAEAAPACAAHwAaABkAFAATAAoACQAOAA0AEgARAAgABwAGAAUABAADAAEAAAAC
+AAQAAAAAAAAAACACkfAgByPYIAKR9CAHKwwgApIAIAcqICACkgwgBynQIAKSICAHKSQgApI4IAcn
+sCACkkggByeIIAKSUCAHJ2AgApJkIAcnMCACknAgBycIIAKSeCAHJuAgApKMIAcmrCACkpggByvY
+IAKSoCAHI1AgApK4IAcjDCACkswgByJ4IAKS4CAHIlAAAAAAAAAAACACkvQgByIEIAKS+CAHIYgg
+ApMAIAchUCACkwggByEYIAKTFCAHIOAgApMYIAcgqCACkyQgByBwIAKTKCAHIcAgApMwIAcgOCAC
+kzggByAAIAKTQCAHHhQgApNIIAcfyCACk1QgBx+QIAKTXCAHH1ggApNkIAcfICACk2wgBx7oIAKT
+dCAHHrAgApHIIAccICACk3wgBxvoIAKThCAHG7AgApOMIAcbeCACk5wgBxtAIAKTpCAHGwggApOs
+IAca0CACk7QgBxqoIAKTwCAHGoAgApPMIAcaUCACk+QgBxooIAKUACAHGgAgApQQIAcZ2CAClCAg
+BxmwIAKUMCAHGYggApRAIAcZYCAClFAgBxk4IAKUYCAHGRAgApRsIAcY6CAClHggBxjAAAAAAAAA
+AAAgApSEIAcXlCAClIggBxdgIAKUkCAHFywgApScIAcW+CAClKAgBxbEIAKUpCAHFpAgApSoIAcW
+aCAClLAgBxZAIAKR1CAHLgAgApHcIActNCACkcAgByyEIAKR6CAHK+AAADAEAAAwEAAAWdwAAFqA
+AABahAAAWogAAFqMAABakAABlDAAABAkAAAQMAAAEDwAABDcAAARDAAAeywAAHs0AAHijAAB5owA
+AeqMAAHujAAB8owAAfaMAAH6jAAB/owAAI3MAACOgAAAfnQAAI/cAAGRWAABknAAAI/8AAQTHAAE
+ExgABBMQAASTHAAEkxgABJMQAAB34AAAd+QAAHf0AAB4BAABnDwAARB0AAEQhAAAlAgAAJhcAACW
+DAAAlhQAAJYgAACWLAAAlsQAANAoAACQCAADCNwAA0jcAAOI3AADyNwAAwjkAANI5AADiOQAA8jk
+AAGQVAAAYBwAAaDYAAGQkAAAd+AAAHfkAAB39AAAeAQAAHjYAAB44AAAePQABQB4AAUIeAAFEwgA
+AZQEAAHjwAAB58AAAevAAAHvwAAB88AAAffAAAH7wAAB/8AAAZQMAAAAAAAAAAAAAAAAAAAwAL//
+/0AAADAIv///QAABlBCf////AAGUNAAAABAAAZQIn//j/wABlDgAAAAQAAAQKOH///8AABAs4f//
+/wAAEDQB////AAAQOAH///8AABBAAAQAAAAAERAA////AAARFAD///8AAHsoAH3/4gAAezA//+7/
+AAB7OAAAAAEAAHtAP//u/wAAfCQAff/iAAHiiAAIAAAAAeaIAAgAAAAB6ogACAAAAAHuiAAIAAAA
+AfKIAAgAAAAB9ogACAAAAAH6iAAIAAAAAf6IAAgAAAAAjcj////wAACN0P////AAAI58AAAA/wAA
+joQAAAD/AAB+cH+//38AAH54f7//fwAAfgAAwAAAAAGRVAH///8AAZGcAf///wAAj9gDf///AACP
++P////8ABBMUAAAABwAEEwwAAAALAASTFAAAAAcABJMMAAAACwAAd9wAAAADAAB39P////8AAHgE
+AAAAAwABnDgAAcBAAAGc+AABwEAAAJAEAAAAPgABEHgA////AAEQfAD///8AARCIAAAAAAAAlAQA
+AP//AACUEAAA//8AAJhUAAAB/wAAmFgAAAH/AACWCAAAAAAAAJYcAP///wAAliQA////AACWKAD/
+//8AAJYwAP///wAA0CAAAAAHAADQJAAAAAcAAwjYAAAAAwADSNgAAAADAAOI2AAAAAMAA8jYAAAA
+AwADCOQAAAADAANI5AAAAAMAA4jkAAAAAwADyOQAAAADAAGQUAAAAO8AAaDUAAABgwABkIwAOAAA
+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
+AABVVQAEcgQABAD4AAQA3AAEANgABHJIAAQBOAAEARgABHJMAAQA9AAEclAABHQIAARyCAAEQEgA
+BErcIAkEUCAJBJAgCQTQAARBLAAEQTwABEDgIAkFEAAEQQAgCQVAAARwSAAEcGAABHIgAAR2DAAE
+eAQAAYaggBAAACAJOMAgApDwgBIAAwAEAOiAEAADAAQAwAAATiAABADMAAQAxAAEAMgABAE0IAaL
+1CAGjxRBLoSAAAIAIQAP/ykABABEIAkFcAAEAkQABAJMAAQCSAYGAAAABAGAAAAeeAABMLAABADQ
+AAQA1AAEAPAABAD8AAII1QABBGoABAEMAAQBEAAAosIABAEUAAAw1AAEARwABAEkAAQBKAAEASwA
+BAFAAAQBRAAEAlAABAEgAAQCYAAEAmQABAJUAAEAIQAEQfwABEv8AACAIAAACAgABHAwAARgwAAE
+YMQAAIgAAARB1AAES9QABEHYAARL2AAEQdwABEvcAARwOAAEcAAgCQWgAARwBAAAwAAgCQXgAARg
+yAAAgCQABEAcAARKHAAAFywgCQYgIAkGUEHNzWUABGDkAARg0CAJBoAABEAAAARABAAEQAwABEAI
+AARKBAAEcDQABHAIAARQAAAEVAAABGDMAAB/AAAEQdAABEvQAARwUAAEUGgABFBAAARUaCAGilgA
+BFBQAARUUAAEUiQABFIoAARSLAAEQBAABEgQAADDAAAEQBQABEgUIAaK2AAEQeAABEvgIAaLWAAE
+QegABEvoAARB5AAES+QABEHsAARL7AAEQBgABEoYAADu8AAEUGAABFRgAAARAAAAETAAABAwIAaD
+kCAGgIwgBoYwIAaE5CAGiNAgBoeEAAP//wAEEwQABBMkAP///wCBAiDhAeJA/8AMAB//kEAgCQlA
+H/+VaAAA4+8ABFCwAARUsAAEYOAAAMHzH/+aSB//gmD//H//AAB+6IAABwCAAAUAgAAGAIAABACA
+AAEAgAACACAHflAgCQmQH/+QjCAHf9AgAoygIAkJwEAAAAAf/5m0VVVVX1VVVVUAD///IAXguB//
+kJcAVgAA4QT+AOEFBgAAJgAA4QHeAOEAWgDf//4AAACAYAAAQAkAAAnEIAk74CAJPDAgCTxgIAk9
+ICAJPPAgCTuwH/+XiN6tvu8gBpB0IAaSxAAJiWggBpJ0IAaQNCACkSDiAAYA4gAGCOIAAgDiAAII
+IAk9UCAJCfDi//4AH/+ZaCAIQAAgCQogH/+Z1B//lLAgB9AAAAAAAGwQBvoKBSAIEFgw/P1TEgAA
+aLBbzHkb/VHTD9MPKbDkKLAYCYgCyojz/U4QzBAgMAQkKPT6+yIAICTw+iwAAgAAWPBbigx0oQJl
+r+7SoNEPAAArvfgsskorskz+CgMgDBBAMPwJQgAAECAw+w1CAfICUnD6hDkGAIyHYP5EAgABEFAw
+aZcCaNeeE/02Css4+3VTD/8QMDAvMrPDgAhIAgaIAwj/Af82syABEFAwW8ajKTKzwaAKmQL5NrMg
+ARBQMFvGnioyshv9KAuqAio2sikKgCk2ri8yrxj9JQj/AS82r/39IxA0EHAwDl4o+P0iHAAgd3CJ
+2YvaiteP1va7EQggBD5g+6oRCAkAXnAKmQIJ/wII/wIvNrAe/RguNrErMrKO1YjYH/0WjdTziBEP
+gAQ7oPz9Eh4JAEOw/7sBDAkAd3ANuwIMuwL7NrIgARBQMFvGeigyshn9DPr9ChAQEFgw/Qp4IBAQ
+YDD5iAEAFBBwMPg2siIAAHhwWq72yajAovz9AxAIEFgwW8wbY/6ZAADz/u9gABBQMMGkW8ZowrAq
+MrMLTAIGzAMMqgEKSgILqgL6NrMgFBBQMFvGYGP+aABsEAQZ/PP5ksAgCAA0oMAg0Q8b/PArsH8c
+/PHz/O8b8AQ+4Ay7AgnqUfs2SSPCAj6g9/zrEAEQKDD2CgIgEhBwMC52sCZ2sy0KyC12sSwa9Cx2
+sttQ/QpkIAEQYDD6/OEQChBwMPV2kSAAEHgwWq7J9qJfYgAAIrDA7/8KByAGEEAw+goFIAQQWDD5
+/NcQABBgMB381i12oMTRLXaAwNgtdqMrdrXE2i12tih2ty92uCx2uSh2uip2uy52vMHVLXa9KHa+
+K3a/KnbAwNotdsEqdsIvKgAvdsMrdsTBgCh2xS8KZC92xisqmyt2xyV2yCp2ySp2yigKAyh2yy8K
+Ei92zC52zRv8uyuySioKTv46ACpUAVww/nbOK9AEPuALqgwqdrQslhQmlhUoChgolhka/LIb/LL/
+/K8QARBwMP92kCAAEHgwWq6T9qGHYgAAIrAa/Kob/Kr8CgAgChBoMP78qBAAEHgw/naQIAEQcDBa
+ron2oV5iAAAisBr8nxv8oPwKACAKEGgw//yfEAEQcDAPAgAPAgD/dpAgABB4MFquffahL2IAACKw
+GvyUG/yU/AoAIAoQaDD4/JQQARBwMPh2kCAAEHgwWq5z9qEGYgAAIrAa/Ikb/Ir8CgAgChBoMPn8
+ixABEHAw+XaQIAAQeDBarmn2oN1iAAAisPv8gBAAEGAw+vyDEAoQaDD6dpAgARBwMPr8eRAAEHgw
+Wq5e9qCyYgAAIrAb/Hv7NkAgARBQMFvF0hb8eR78eBz8ePw2QCAAECAw1eAtMkD/AgACAFXrUMCq
+W8XKsUT2Sehx/gJxcP4KAC/7EBgw+goFIAgQWDD8/GwSAABo8FvLb/YwVGIAACDw+vxoEAcQWDD9
+CmQgARBgMP8KACABEHAw/naBIAoQcDBarjz2oCliAAAisPr8XRAHEFgw/QpkIAMQYDD+CgogAhB4
+MP92gSAAEHgwWq4x1KDJR8Cl/PxUEAgQWDD9LAACAABxMFvLVNJA0Q/SQNEPAAAAAAAA8/9sYAAQ
+GDBsEAQT/Er0CgAgABBQMPYKzCCAECgw9TgIAAEQODAogND5MlUgGAA2IPRMASIAIDTw+ULpaAAg
+KPDSoNEPZJ/o8EEED+UANKAAexpkv9raQFvFlWav4mP/zwAAAGwQEhv8NRL8Mx/8NI24LLAXKrI7
+j/D/FgAiAABwcPuw4yAAECAwKSJx+Q9bCWABTDD00MRoACB+cPCTEQl0AWgwA4MCIyZyoc8v8AAv
+JnQvInbANPP/AggAIE4w/yZ2IKwANqDwmBEPdAFUMAj/Ai8mgi8idK69LdAAKPqP+P8BDcAEP2AP
+3QItJnQoInYsChAMiAIoJnYkJnMqCggqJnob/BD5Cg0gIAJQcPkmeyBgEGAwW8OhGvwH+QoYICAC
+QHBtmg/5ggAgCAJSsPmmPyAIAkIwLSK7H/wDHvwDD90BDt0CLSa7HPwBLCaDG/wB+yaEIAAQEDDR
+DyQmcigidsfLDIgB+CZ2L1wAtqAkJoJj/4QAAGwQDhz7+BT73h779/b79RAAEDgw+EDRJAAQUDDz
++/URABBoMPn78hABEHgw9WGVKgABRDD7+zkCAAAQ8PvSOQgFAF6w+eXiL+ABFDD/5eMgEgD+MIju
+ZIPOLmGPK2GRKmGTrr2q3aXd/ghECgIObJDyCh8j5wC2IAsJRPWT3GiAAVAwZYPU/wIACAHoqJAo
+YZ4O/gwN+QwpZZf+ZY4t4AF0MAvfDP9lkCNXADYgCvsMK2WSCbkM+WWWKeABTDAFmwwrZZQO2hEo
+YZD6xoQp4AQ6ICjGhyJhlA6fEf/GhSPgBDigIsaGLmGU/8b9L+AEO6D+xvwgABAQMPRhmCABV6yg
+y077TP8qAdDFIP8CAAgB2KLQ2kBbtG8d+7Mo0oob+7XwqREIAEBaMP9hjCgJAEowKNaKL9b+LNKB
+HvuCDswCLNaBwKhbqzkrYsQuYsL1pRECAAASsPW0DA/wEFAwCkQB/wIACgEN9RAoYsFkghAa+4Qp
+oNErCoD0ZsQoCQBecPmk0SIAAFkwHPucL2LD9RYBIAUQUDD7FgAgEBBoMP0WAiAAEFgw9BYDIAIQ
+aDBbyoZkQfv0ZugiyAA1IPUKACAAECAwKmLowLjTD/SqCAAAEGAwW8PjsVXzWeh0ACAgsMAgZiHG
+W4kD9qHAYgAAErAc+33TD9MPK8KBHfuA0w8PAgANuwL7xoEgQAJQcFuI5fahmGIAABKw+/t6EEAC
+UHBap3/2oYViAAASsBj7di9iTidmTfz7bBAAEFAw+P8BD/8QGDD/Zk4gBRBwMNMPbeoY2zDAmXmj
+Adtw/c0EIAgCUrD71gAgCAJjMBr7Z1uIxhz7Z/oKACAREHAwbeoU2zDC8X+jAgd7AivGFPqsBCAI
+AmMwGvtfW4i8HPtc+goAIAUQQDDTD22KE9swwJl5owHbcCvGKPqsBCAIAmMwGvtVW4ixHPtS+goA
+IAwQWDBtuhPbMMLRfaMB23Arxi36rAQgCAJjMBr7TFuIpxz7SPoKACAFEHAwbeoT2zDA+X+jAdtw
+K8Y5+qwEIAgCYzAa+0NbiJ0c+z76CgAgDBBAMG2KE9swwpF5owHbcCvGPvqsBCAIAmMwGvs6W4iT
+82GXIAA2rKAtYY0uYY8rYZEsYZ4qYZMlYZX0YZggLwA3IJMRlRKUExz7MPoWACIAAHrw+goEIAYQ
+WDBbyhTRDwAAAAAA8/4EYAAQIDAc+yiUE5MSlRH6FgAiAAB68PhhmSAEEFAw+BYEIAYQWDBbygfR
+DwAAK2K6L2K59LwPL/AQQDAIRAGkWnrzWSlit2SQU/pmuiIAAFqwHPsWLmK49RYBIAUQUDD7FgAg
+EBBoMP0WAiAAEFgw9BYDIAAQaDBbyfNj/bML6AwKiAwoZZIFjwwJ/wz/ZZYp4AFEMAWYDChllGP8
+o/P/sGAAECAwKWIWZZwq92WYICAQcDD+ZY8gIBBYMPtlkSWgEFAw+mWTIgAQKDAlZZXz/Dln4BBo
+MAAAlRD8+vQSAABrsP+sAAIAAHLw+goCIAYQWDBbydTz/H1v6hAQMPP9Xm/0EBAw/vwAAAIQUDD8
++ukQBhBYMFvJy/P8Wm/0EBAwAAAAAAD9TAAAAhBQMPz64hAGEFgwW8nDY/yEAAD9TAAAAhBQMPz6
+3RAGEFgwW8m9Y/xsAABsEAjz+toQABBQMPb61xIAACKwKSpAKTaqJzKFIjKGGPrUwFP4eAEP/BBI
+MPkiAQQFAEFwBCIC8jaGIFoAFfDAqPoWBCAIECgw9WR+IAgQIDD0FgAgCBAYMPMWASAIEEAwmBJg
+ACMAAPoWBCACECgw9WR+IAMQIDD0FgAgABAYMPMWASABEEAwmBL0VQoMVAE4MPQKICxXATww9TUI
+DEAEP2D4+rQUACAuMPNgfiQAICzw9UQMACgQODD0ZH8mAwA/MPT6rRbwBD3g+fqsFgkAP3D3MwIA
+AxAQMPVC0CIJAETwDwIAbSod8mB+ICACITAJVQH3IgIECQAs8PVGzCIJAESwJULQEvqc+PqeECAQ
+MDD5VwEGAwAzMP9mEAYJADzw90bQIAMQGDD3+pUSAAAgcG06GyMi4PVCACAgAhCwtEQHMwEGVQIF
+MwIIMwIjJtz2+ooQIAIocPj6jBAgECAw9/qGEB8QcDAPAgDTD20IRfQSFAQDAHEwK2LsJmwQ+E8R
+AgMAEzD5UgAqwAQ5IPraAgLwBDig+LsBDgkAF/D/qgIICQBecPqZAgAIAilw+WboIA4IObBj/67A
+INEPAAAAbBAE9vpyFAAgJPAGJgrzZech/gIpcCVl6NEPAGwQBBX6bPYiACAgEBgwbToGh1B2ewW4
+VcIg0Q+XICJQBNEPAGwQCMBg8xYCIgAAWXD3+mESAABRMPIWBSIAACCw+xYEIAICELD6FgMgQAIp
+8Cpyf1uprfssAAIAABqw+nJ/IgAAYPBbxdX0oBBgEAI58PV523AQAjGwwCDRD7E8rEwqwADF3f8K
+ACYAhu6Q9fwAAgAAcHDzCgAgABAQMPQKIiAvEDgwbQgTyaPKJHehQmgiX7HK3KAqoAB9oWNj/+XI
+MSc0AGRfsPRUACAAEBAw0Q8AdKnZ8iwBIAICUzD65gAgCAJzsPP/zWIAAGKwAAAAAAD/xAAgAgIQ
+sPrMASIAABsw+uYAIAgCc7Dz/6hiAABisHSpnC/EAPP/lmIAACswySZoIUJoIjLIMSc0AGRfR/RU
+ACAAEBAw0Q+NEvLWACAHADTgJzQAy18Y+hwS+c+oIvRUACIAIBGw0Q+LFPoSASAAEGAwW6hKixP6
+EgAgABBgMFuoRokS8pYAIAcANOAnNADIUSRUAGau7Rj6DBL5v6giomLRDxj6CRL5vPgSAiIAIECw
+/4YAIgAgEbDRD2wQBIswJrAAJwoA/wIAAgAASvD/AgAIAEEFoGRgegu5AvgaACAAEFAw/AoJICMQ
+aDBtCGFobBV8YRJ9YSH4jP8gAgJSsPYkACACAhCwsXereSaQAGhpWWSAbM9mYABRJJAA0w8PAgD4
+SUJgABAoMKt79rAAIgAAOvBtCBT0YDFgAgIpcCZwAbF3+GkiYgAASfBj/+TTD2P/l8CA+CQAIAIC
+UnD6NgAgABAQMNEPq3urWcmCwLD7JAAgAgJicPw2ACIAABKw0Q/GKtEPAGwQCFv+6fanHmIAABKw
+FfnOGfnP+vnMEAEQWDD8+cwQABAQMBj5zCimpSympCKmpx/5yi+mph75yS6mqR35yS2mqCymqxj5
+xyimqi1Sgh/5xh75xg/dAQ7dAi1Wghz5xCxWhitWhyhSqggYS/8CAAYDzE4QFPl3F/m/8/m/H/8Q
+MDAd+b4c+b6c0CgywBn5vQmIASg2wC4y0C/qwP/uAQEVEHgwD+4CLjbQHfm3LTbRKDLYHPm2GfmY
+DIgBCYgCKDbYLjLbH/myD+4CLjbbHfmxLXY1KTLdLPrfDJkBKTbdLjLSGPmtH/mtCO4BD+4CLjbS
+LDLSKUDQx94NzAH8NtIiOAE6YC4y0xj5ph/5pgjuAQ/uAi420yY29CY29S4ywR/5otMP0w//7gEA
+gBB4MA/uAi42wSwywh35nQ3MAiw2wilyLRv5mxr5m/z5nBgAQF5w+vmZGAkAVnD5di0gBRBYMFuo
+xxr5lPz5lRAGEFgwW6jDGvmR/PmREAcQWDBbqMAa+Y38+Y4QCBBYMFuovBr5ivz5ihAJEFgwW6i5
+GvmG/PmHEAoQWDBbqLUa+YP8+YMQCxBYMFuoshr5gRz5gxj5gfh2DiAgEFgwDwIAW6is+vl8EUEQ
+WDD9CgAg/xBgMFung/r5dxFBEFgw/QoAIP8QYDBbp38a+XL8+XQQ5BBIMPl2CiAjEFgwW6idGvlt
+/PlvECQQWDBbqJoa+Wn8+WsQJRBYMFuolhv5apt8m3sU+Rf1CgAgABBQMPIKzCCAEDAw+3YNLAAg
+MTAswNAPAgD9QlUk1gA3IPVcASQAIBEw+VLmbAAgMTD2pLxiAAASsBX5WQ8CAA8CAC1S1B/5Vx75
+Vw/dAf7dAgAuEFAw/VbUIAEQWDBbhs/6Ci4gBxBYMFuGyPoKLSABEFgwW4bK+gotICkQWDBbhsP6
+CisgARBYMFuGxPoKKyApEFgwW4a9+goyIAEQWDBbhr/6CjIgKRBYMFuGuPoKLyABEFgwW4a5+gov
+ICwQWDBbhrL6CiYgARBYMFuGtPoKJiApEFgwW4at+go6IEcQWDBbhqr7CgEgqRBQMFuGq/sKLSCp
+EFAwW4al+go2IAMQWDBbhqb6CjYgLhBYMFuGn/oKNyABEFgwW4ag+go3IDwQWDBbhpr6CiUgAhBY
+MFuGm/oKJSADEFgwW4aU+go7IAIQWDBbhpX6CjsgBhBYMFuGj/sKASCzEFAwW4aQFPkSKEGBwSj/
+AgAIAhvAkPsKViCzEFAwW4aF+gpHIAEQWDBbhoYpQYH/AgAIAhNIkPoKRyA6EFgwW4Z9+gpGIAEQ
+WDBbhn4qQYEPAgAPAgD/AgAIAgfQkPoKRiA5EFgwW4Zz+gpAIEwQWDBbhnD6CjMgTRBYMFuGbvoK
+OSBOEFgwW4Zr+gqyIGIQWDBbhmj6CkkgTxBYMFuGZfoKTSABEFgwW4Zn+wphIE0QUDBbhmAtMv4e
++HMO3QItNv4rUpAsCi8MuwIrVpBb/Jz2osRiAAASsBz438Cg/MCAIAQQeDBt+gwAoAQMDRt/1wGx
+qbGqG/jcFfjbF/jZEvjWLkF7/QoEIfoCenAP2Tj+LkACAABScP/40RoFAHNwKLKALkF7x8sMiAH4
+toAgAT1/kP8CAAABvgZg/wIAAgG+gmD/AgAEAcICYBr4lBv4pxz4x/gKACAQEEgw0w9tmg0LiQos
+lp/6lq8gAgJCMFv8L/aiI2IAABKwGvi9+woRIBEQYDBblF0oUigW+LoX+E/4FgQgHwA2IMCgW5Tn
++goAKAAgNrAqloGLFCuWgiqWgyeWgCVSWw8CAMlXwKFblN79CgAsACAysC3GgSXGgi3GgyfGgBj4
+qfv6/yBUEFAw+YJ/IAgCQjAPAgAPAgBtqgz5gn8qACAycCumgLSIKkF+ppzTD/vGgCKQADagwNAa
++Ij8Cv8gJxBYMFumkRr4hRv4lxz4l1unsh74cR/4lS/mcBr4fxz4lR34ky3meP34kxJAEFgwW6aG
+xJAqCgMKmSz5GBQP/BBgMPr4GhAGAkIwDIgB9/iLHSAEOiD6ojYpgAQ6IP6ZEQgJAGIw+fhPGAkA
+SjD3pygAMhBYMAuqKCiWpVuHpRv4CyuyNiwKZPy6KAIAADKwW4egHPgGLMI2LTro/cooAgAAKrBb
+h5uwaPitEQ8ABDog/t0CAf4CYXANzQItNuQAwAT3ChkAFBBYMAuqLCo27Ssy7CsWBSkKACk25QCA
+BAcFGSUWBgUuFC425gpYESg25wVZCik26Cg26Rf4YQdXKCc26iYKSwZWKCY26xz4XQy7AQtbAis2
+7Pz4WxJYEFAw+lUoAAgQWDD1Nu4gABBoMPkWACIAAHow+BYBIAQQUDBbxuz1FgEiAABp8P8SBSIA
+AHGw/PhOEAQQUDD4EgYgCBBYMPgWAC/wAXwwW8bhKUF+zJ4qQX/MqStBgMy0LEGBZMFT0Q9k2yra
+UFvBNmarMmP7H8Dg/lYcIrQAOmD/AgAEAJUCYGmhUilQgBr4Obqs/qwFKAAgMrDygM0uACAzsP3g
+zSwAIDMw/8DNIAAQWDD7hM0oACBMsPvkzSgAIE9w+8TNKAAgT/ApVIBj/UYAAAAAAP9WHSFiAD6g
+/wIAA/6bGqAvUIooUIArUIX5UI8gABBgMCxUhfxUjygAIFow+FSALgAgT/AvVIpj/QcAAAAAAAD7
+CgIgsxBQMFuFfGP7vgAA+gpHIAIQWDBbhXhj+88AAPoKRiACEFgwW4V0Y/vmAAAuorwtUqof+AoO
+Tksu7P7/7hEMAEB/cA7dAi1WqmP4Sy9Bf2X9bShBgGWNZylBgWWdYfP9YGAMEGgwG/f+K1YcY/yO
+HPf+Hff8LVYcLFYdY/x/J1YdL1Ye8lYfIAAQcDAuVhxj/GsAACdWHS9WHiJWH2P+zSsy4B338hz3
+8tMPDbsBDLsCKzbgKTLAGvemCpkCKTbALzLYGPeIGvfH/PfqHgkAR/D/NtggIBBYMFum8h/3oyry
+GMCUCaoCKvYYKPIZCYgCKPYZwOEu9iDRDwAAbBAEEvfeIiF/wDXzKBNwABAgMBP32yQ06CQ06SQ0
+6iQ069EPbBAEE/fVFPfWIjF/BCIBIjV/0Q9sECj3CgEiIQA0oP8CAAABngSg/wIAAgGkgKD/AgAE
+AfIAoPb66iQBpgSg8AHpYAAQGDAlEkYmHIT2+uokACA1cP18AAACEFAw/PfBEAEQWDBbxk3AovsK
+ASAAEEgw+VR9IAoQQDD4VHwiAABgcFvGRhv3uMCh+rSIIADLLaAf97Uv8h3+CgAiAABY8P8qFAAA
+EGAw9KOYaGIBeDAKShRtiQuJsLHu+7wELAAgYnBkoRKJtIiwhLH9sgIgIAJzsPyyAygAIGIw+LIF
+JAAgQTD0sgYsACAncP2yBywAIGsw/LIIKAAgZnD5sgkoACBKMPiyCiQAIEEw9LILLAAgJ3D9sgws
+ACBrMPyyDSgAIGZw+YgIAf4CSrD4vEAkACBBMPSyDywAICdw/bIOLAAgazAPAgAPAgBtmYf5ggAg
+IAJzsPyCASwAIGdw/YICKgAgaTD5ggMkACBScPyCBCQAICMw/YIFJAAgI3D5ggYkACAicPyCByQA
+ICMw/YIIJAAgI3D5ggkkACAicPyCCiQAICMw/YILJAAgI3D5ggwkACAicPyCDSQAICMw/YIOJAAg
+I3D0gg8oACAmcPnMCACAAkIwrNysTPv3ZB4gAXww+B0BID0AN+Dz6QoABBBQMP+qDAAgAkIw+ZIA
+KAAgQ/DTD/kWRCAAEEgwDwIA0w/TD22pB/mEACACAkIwKhJErKwsth4d91HAwSzUiMg2/wIABADd
+AKDSYNEPAAAe90wANRGuU/5cAAAEEFAw/PdJEAEQWDD9LAACAAB48FvFz8Cl+woBIgAAYPBbxcwt
+MAAjFkD1CgAhCAJgcPTRLmD/EHgw/wIABgCbf1DA0P0WRSAAEFgwKxZH8AA+b+oQMDAAAC4SR9MP
+ZO2dKxJBiOEsEkL9EkMiAABQcAuAAPahDGIAADKwLBJA0w8swADVcPTAf2D/EGgwfcF3+x0BIgAA
+UHBb/NUsEAD6FkYgAgI5cA8CAP0KWy/OADcgfcmjKByEqKUuUHsvCl0PAgB/6ZItHQH73AQiAABQ
+cPzcCCAYAmtwW/xi+hZHITwANqAvEkVl8SQZ9xCIoXmJiSsSQPz3ChABEFAwKhZFA7sMK8YdY/9y
+ZmB3HfcFwMEs1Ihj/TIV9wYY9wYAMxGlNfP+1mIAIETwAAAV9wMY9wMAMxGlNfP+wWIAIETwGPZz
+KIDRGvb++fb/EJAAfjAjkcQHMxHz/qJiACBU8AAAAAAAAPXMAAAAEDgw8/ydb+oQMDD1zAAAABA4
+MPP8jW/qEDAwJRJGKByE8/x/ZAAgRXAAACORwwczEfP+XmIAIFTwAAD79pkSAABQ8FvHvvoKBiAA
+EFgw/AoAIAAQaDD+CgAgABB4MFu/R9Jg0Q8A3XD79t0QBhBQMP/2ixUABDzg/goBIgAAYXBbvz4Y
+9tgogoBogA4T9tRj/gAA8/2QYAAQYDAT9tHz/dlv/hAwMMCi/PbPEAEQWDBbxU7z++1v6hAwMGwQ
+BBb2yyliWShiWCRiWvn2yRgAIEow+AoAJAAgQTD0QNhh/gIRMCeSeCVipgl3EadVKFQhKFQgmFyY
+WyhWDyhWDihWEShWEihUVShUVChUfihUfyhVLChVMihVMyhVNChVNShVNihUbihUbyhUcChUcShU
+cvhUcy//EDgw91R2IAEQIDBtKWcikngjYqYoVRPyQggAAgIhMPhVOiOQBDig+FUSJAAgFPAoVHEo
+VHIoVHMoVCEoVCCYXJhbmF+YXihWEShWEihUVShUVChUfihUfyhVLCdUdihVMihVMyhVNChVNShV
+NihUbihUbyhUcChVEyhVEihVOtEP0Q8AAABsEBIY9o0jFhEkFheFiIuFh4eJhoyEjYOOgv+CASAg
+AjBwn2GeYp1jnGSZZpdnm2UlZgj4ggAiAABQ8PhmACJtADSgKBIXKxIXFvZ89wqAL8AQYDD0slRv
+BBAoMC1ihi5ihPqIEQAJEEgw+SYPIH4CQjDzJgUoAEBiMPP12xgAIEKw+CYGL/AQWDD5YoMkACAv
+cAtVAf8CAAoBA/VQ+jDRIf8ANmD1ZoYqCQA6sPo00SIAAGlw9PXqEAUQUDD/YoUgABBYMPUWAyD8
+EEAw/RYAIBAQYDD8FgIgAhBoMPgWASIAAGEwW8TSGfXr+lwAAcYANWD6JhAhuQA2oIov9woAIAAQ
+GDD0FhMgABBoMPShGGAgAiBwKRYSLRYWKxIXikALqijwcAQAZBBYMAuqLPsiECB+AlKwCmoUKhYU
++goZCgAgHvArFhgqFhVbpVItYoYoYoMuYoT61QwP+BBYMAtVAf+sAAoAZHVQ+vWfEMAANiApoNH1
+ZoYggBBYMP8WECgJAF5w+aTRIgAAaXAsEhMoEhAvYoX9FgAgBRBQMPUWAyAIEGgw/RYCIAAQWDD4
+FgEgAhBoMFvEnvpcAACAADVgKRIY+pYAIOEANqAlEhH7EhUgABBgMFu9/C8SFC0SFiwSGPBxBAAB
+EHAwAO4alcGdxJ/D+v8RDAAgb/D+xgYkACB9cP0WFiH+Amtw/cYFIYACWXCbwoovtET3fAEgOAIY
+8PUWESv/e1XQwCDRDy8WEPP/WGAAECgwAAAAAC1ifC9iey4SEPXcBy/4EEAwCFUBpe75YnkqACF3
+0Mua/mZ8IgAAa7AsEhIoEhAuYnr9FgAgBRBQMPUWAyAIEGgw/RYCIAAQWDD4FgEgABBoMFvEaPP/
+LGIAAFFw8//IYAAQKDDz/hFgABAoMMck0Q/BJtEPwCzRDwAtYnwvYnv13A8v8BBAMPsK/CQAQEVw
++1sIAgAAYnD5YnkqACTf0GSQQftmfCIAAGrw/mJ6IAUQUDD1FgMgABBYMP0WACD8EEAw+BYBIBAQ
+aDD9FgIgABBoMFvESBn1YPP922IAAFFwAAAAAPP/wmAAECgwbBAQW4TmIy0BKiZgW4Tg/TIgIA0Q
+WDD19cEQDBBgMPo2ISAEEEAw/a0MAAEQIDD69boQAgJrcP02IiEkEDAwF/VqGfW3KKRFKDSjKDSd
+JTYbJDShLDYcLDSkK6REKTYd+zSlIBAQSDApNKf3ctogDhBYMPs0piACEGAw/DSiIAAQYDD8NKAn
+8AE8MPc2HiYAIDCw9zYfIgAAWbDTD22KEi+wgADxBABOGvXhCHACAlrwsczAxAwOR/40nSQBHgOg
+/PWZEAUQUDD99ZUQMBBYMFvEDS4yIC0yIQ7dDLHdDW0U+dwAAmMAN2AND19k8vDCoAmIV2SC8wnL
+U2Sy+gnsUWTC/7CuCeo71aAqCgX89YYQMBBYMP5cAAAREHgwW8P6wYEIWDb4NJwh/gIyMABhBABF
+GvCBBAH+AilwJTYj9TYkIKACSHDwRhoADAJCMPCBBAEgEFAw8E8aAf4CMbD2NiUh/gJ78P82JioA
+IFCwK6CDLKCCLaCBLqCAnpCdkZySm5MU9TD89WkQBRBQMP1C2SAwEFgwW8PcLzIlCv8RL0bZ/PVj
+EAUQUDD9QtkgMBBYMFvD1Rz1YC0yICsSFS8SFykSFvgSFCAFEFAw/jIhLoAEP+DwmRELgAQ+4PuI
+AggJAH5w/zIiKAkASjD4RtogMBBYMFvDxfz1UBIAAHnw/TIeIAUQUDD+Mh8gMBBYMFvDvhT1QRz1
+Si8yJf4wnCAFEFAw+TImIDAQWDD5FgApoAQ5oPgWASIAAGlwW8OzHPVAHfVB/kBEIAUQUDD4QEUg
+MBBYMPgWACAPEHgw0w9bw6r8PGwiAABocPkyGyAEEEAw0w8PAgD5FgAgCBBIMG2aJ/yJCAoAIGow
++ZIAIAgCWjD5pgAoACBm8PmSACoAIGrw+aYAIBACQjDApfz1JxAwEFgwW8OVwKX89SUQMBBYMP0a
+8CHwEHAwW8OQ/PUhEAUQUDD7CjAgBRBoMFvDi/syICFsEFAw+ioIDaAEOWBb/nrAINEPAAAAAPz1
+FxAFEFAw/fUHEDAQWDBbw38sMKQtMhsAwQQAThoAwAQNCxlksO0sMhywy/CwBAH+Akuw+QkZD/8Q
+UDBtCAoJGRT0kBFgAgJSsGP/7gAA8/2+YAAQKDAAwQQATRrwsAQB/gJbcPsLGQ//EEgwbQgKCxsU
+9LAJYAICSnBj/+4AAAqZDClkfCwyHPxkgCACAmpwLWR9KjIcspv7ZH4gAgJSsCpkgS8yHLOY+GR/
+IAQCe/AvZIIuMhyz7i5kg/oKACAEEHAwbeoSKGCAAIEEAE8a9fEIcAICMbCxqsCkCg5HLjSdY/zh
+AAAA2RHz/QpgEBBQMAiZEfP9BWHwAlKwAAAMmRHz/P5h+AJSsA6ZEfP8+WH8AlKwwKL89NIQABBY
+MFvDO/z00BAFEFAw/fS+EDAQWDBbwzdj/KEAAABsEAoX9MoqcsAscrgrffcosNH0CoAtkAQ7IP+H
+DnoAIGKwpKpbmbRgAAkAAC0qgK2qW5mxGfSsLZIS/nJjIC4AN2DAICxyYStyYC9yYi5yY5ISnBH7
+FgAgBRBQMPz0tRAwEFgwW8Mb0Q8AAAD69CQQNAA3oP9yYioAFnaQy/X7cmAqABn+kMu6/HJhKgAc
+XpD4CkAgBwA3IHyLOPP/sW/qEBAwACxyYStyYC9yYvP/n2/qEBAwLHJhK3Jg8/+Rb+oQEDAALHJh
+8/+Fb+oQEDAAAAAAAPv0JRFMEEgw8xYEKAAgTLD5FgUhRBBQMPkmUyoAIFCwKhYHKiZRKiZS+SZU
+IkAQUDBbkG+aGPsqgCJAEFAwW5BsJRIII3JhJnKoDwIABTMoLnKm820MD4AQYDD6FgYsAEBncPXc
+AAoA+PdQKHKlZIHmGvPRKaDR/XaoKAkAJnD5pNEiAAAzcBzz6i9yp/YWACAFEFAw8xYBIIAQaDD9
+FgIgABBYMPUWAyACEGgwW8LU/VwAAmkANWD8cmEhvAA3YGTAivbcICAAECAw89wIIGACK3Ab8/CV
+3JXd/RYKL/8QQDD/CgAvwBBgMP/VFCJAEHAw/tUVLABAYbD41gAggAJjMCzWCfzWCCBwAlNwW4Nk
++/PhEkAQUDBbkDWMGisiUY4XmsGKGClyYZOxLsYD+8YCIAICITD8rQgEACAusPMmUSYAIDKw+UOJ
+cgAgHrCNFityYCoKAv27KACAEGAwWCQ+3aD7cmAg+QA2oGSwjPasICBgAiqw9AoAIBACGrCV3JXd
+/RYJIAAQQDD5+v8vwBBwMPnWACJAEHgw/9UVLgBAcbD41RQggAJzsP7WCSBwAlNw/tYIIoAQWDBb
+gzn6KkAigBBYMFuQCo4ZLCJTjxWa4YoWK3Jgk8Ev5gP85gIgAgIhMP6tCAQAIC6w8yZTJgAgMrD7
+Q4dyACAesNqwW6MZ26D8ChAgAhBQMFgkEytyYPomViBMADagwMBbu98qcmJboxDBwPusAAACEFAw
+WCQKL3Ji+iZYIFkAtqAscmEd8/QrcmAucmMt0hLz/S5v9BAQMAAAAAAAAPP+K2AAECgwLHJhHfPr
+L3JiLnJjLdIS8/0Jb/QQEDAAK3JgHfPlL3JiLnJjLdIS8/zxb/QQEDAA+/wAAAAQYDBbu78qcmFb
+ovHBwPusAAACEFAwWCPrLHJh+iZXL8MANqD7zAAAABBgMFu7tSpyYFui5ywKEPusAAACEFAwWCPg
+K3Jg+iZVL4EANqDAwFu7rCpyY1ui3sHA+6wAAAIQUDBYI9cucmP6JlkgfgC2oCxyYR3zwStyYC9y
+Yi3SEvP8Y2/0EBAwLXKeL3KdDwIA+PqAIP4CI3AIRAEDTAh880ApcpsPAgDLl/x2niIAAGswHPNH
+LnKc8xYBIAUQUDD9FgAgABBYMPQWAyCAEGgw/RYCIAAQaDBbwiTz/URiAABpMPP/y2AAECAw++wA
+AAAQYDBbu4P7EgQiAABQsFv92R7zni3iEsDxCv04LeYS8/vIYgAAErBsEAgW85UsYoAtYngrbfYp
+sNHzFgUggBAgMPVt/y2QBD9g/5cofAAgazCkyluYjS9ieC5SwAn/Ea/u9OoIAgAAMrBbmIUKZwxg
+ACMAAAAnKoCnyluYhClieChSwAmZEamI94oIAgAAMrBbmHsKZwwmUqj+UqYjwAQ94PNkDA/wEFgw
+C0QB+FKlKgEZ9RBkgisa8t8poNErCoD0VqgoCQBecPmk0SIAADEwL1KnHPL2nBb2FgAgBRBQMPMW
+ASAAEFgw9BYDIBAQQDD4FgIgAhBoMFvB4GRB/PQmAyHaADUgGvN1A0kU93MKD/AQQDDwCgcD0AQ8
+4G2ZAgBEYSZSqC5Spg8CAANkDAhEAf8CAAoA23UQKFKlZIGrGvK9KaDRKwqA9FaoKAkAXnD5pNEi
+AAAxMIwWL1Kn9hYAIAUQUDDzFgEgEBBoMP0WAiAAEFgw9BYDIAIQaDBbwb9kQdf0JgQhVQA1INpA
+/DwAAAAQWDBbulwPehGaFFuiUCZSqC5SpvpkDA/wEEAwCEQB86wACgCfdRApUqVkkTMb8p0qsNEs
+CoD0VqgqCQBisPq00SIAADEwjBYvUqf2FgAgBRBQMPMWASAQEGgw/RYCIAAQWDD0FgMgAhBoMFvB
+n2RBt/QmASDVADUg2kD7EgQgABBgMFu6/9pwW6IxJlKoLlKm+mQMD/AQQDAIRAHzrAAKAJD1EClS
+pWSRFhvyfSqw0SwKgPRWqCoJAGKw+rTRIgAAMTCMFi9Sp/YWACAFEFAw8xYBIBAQaDD9FgIgABBY
+MPQWAyACEGgwW8GAZEGS9CYCIFgANSDaQPt8AAAAEGAwW7rgHfK//vKwEAAQWDD4CgAgEBBIMG2a
+Hg2JAinm+flSxiACAkIwKuL5+5kIAAQCWvAKCk0qlQqKFS8tAS5Sdy70NFqfo8Ag0Q/HJNEPAAAA
+8/3pYAAQIDDz/mlgABAgMPP+4WAAECAwKFKeL1Kd9IwPL/AQSDAJRAGkO/8CAAoATN/QKlKbZKCO
++1aeIgAAQvAc8m4uUpzzFgEgBRBQMPQWAyAQEGgw/RYCIAAQWDD4FgAgABBoMFvBTGP9rQAAAPP+
+/mAAECAwKFKeL1Kd9IwPL/AQSDAJRAGkO3vzQSpSm8ur+1aeIgAAQvAc8lguUpzzFgEgBRBQMPQW
+AyAQEGgw/RYCIAAQWDD4FgAgABBoMFvBNWP92ADz/3VgABAgMPP/x2AAECAwKFKeL1Kd9IwPL/AQ
+SDAJRAGkPn7zOSpSm8uj/laeIgAAQ7Ac8kAuUpzzFgEgBRBQMPQWAyAQEGgw/RYCIAAQWDD4FgAg
+ABBoMFvBHWP9+ADz/89gABAgMChSni9SnfSMDy/wEEgwCUQBpDt78zkqUpvLo/tWniIAAELwHPIq
+LlKc8xYBIAUQUDD0FgMgEBBoMP0WAiAAEFgw+BYAIAAQaDBbwQdj/h0A8//PYAAQIDBsEAQY8pzA
+kCmG+/goCgeQBD1g9koRCVAEPOD6mQIGCQA9sPiNBCYJAE3wl4DRDwBsEAoU8pGISYlIikeLRoxF
+jUSFQYZAjkOPQp8SnhOWEPUWASAgAjBwnWCcYZtimmOZZJhlhUqVZvRCCyAEECgw9GYHICQANKBp
+IRTybAAACBAoMG1ZCYgg84EIcAgCELDGKtEPwCDRD/P/52IAABBwbBAQEvIH0w8iIoMqCqTyCEMA
+CBBYMPgWACAfEGAwW72X+woEIB8QYDD6FggoZAEQMPgWASC4EFAwW72Q+woAIB8QYDD6FgkoaAEU
+MPkWAiDMEFAwW72J+hYKIBwQWDDyykMAHxBgMPoWAyDcEFAwW72D+hYLIB8QYDDyC1MA8BBQMPsW
+BCAYEFgwW718+hYMIBQQWDDyTFMBBBBQMPwWBSAfEGAwW711+woQIB8QYDD6Fg0seAEUMP0WBiEY
+EFAwW71u+woMIB8QYDD6Fg4ufAEQMP4WByEsEFAwW71o+hYPIAAQIDDzHCAggAIocPIcAAAAEEAw
++BYTIAAQeDD/FhQgARA4MIkwiCC7mQCRBPB2GgAUAkIwAIAEBgYby2sa8YIqopYGqixbqlQb8X8r
+spcqFhAGuixbqlEuEhQsEhMvEhAAQQQArRrw/xoMCQBrMPwWEy4JAHuwLhYU9EwEIAgCGPD1OZ5w
+CAIQsBnxrSsSFCuWhPYKACAQEFgw+BITIAAQUDD6FhUgHxBgMPiWvSFAEFAwW705+hYIIBQQWDD6
+GkwgHxBgMFu9NPoWCSAYEFgw+hpYIB8QYDBbvTD6FgogHBBYMPoaZCAfEGAwW70r+hYLIAAQWDD6
+GnQgHxBgMFu9JvoWDCAEEFgw+hqAIB8QYDBbvSH6Fg0gCBBYMPoajCAfEGAwW70d+hYOIAwQWDD6
+GpggHxBgMFu9GPoWDyAAECAw8hwAAEACGHCMMIsgLMwLAMEE8HoaABQCWvAAsAQKChvKry0aAAra
+LCoWEVuqCioWEioSEVuqCCgSEi8SFQBBBACIGvCuGg4JAEfw/xYVJgkAcbD0TAQgCAIY8PU5qXAI
+AhCwGfFmKhIVKpaF9pa+IAAQEDDRDwBsEAQU8Wz3CgAgBRAwMAcCR/sKACIAAFCwW41EKQoICXkC
+CQlHKUZSKEJTJQoA9UZWI1AEPKADMxQlPQEa8cH7CgIgABBgMP84EAAFEGgw9ogCAAEQcDD4Rlgg
+ABB4MFqi9PagcmACAhjwdTnOwLArRlixd/lCWCAAMC6gaXaOEvGtH/Gw/vGwEAAQaDD8CgAgEBAY
+MAPbAgsLRytGUgyJFACZEQ6ZAilGU/3cASjgAWgw8AIHCcAEOiD5QlMoACB6MPiCOSCAEEgwbZoC
+AEhh/M0IKYICO2DSoNEPbBAEGvGbDwIADwIAKqJ/+MrgI+gQWDALqiyoqG6IBSsal3q7XCoKZBTx
+k9MP+kV+IAEQWDBbjS/z8YwQABAQMPpFfyAAECgw2iBbjSHaIFuNGfU2AiAAECAw+iwAAgAAWTD8
+CgMgARBoMFuM/7FEaUvnsSLzPBAloAI4oMAg0Q8AACzqcKysbsgFLQrPetsK8/+UYDIQUDAAAAAu
++jiurm7oBMX3evsH8/98YBkQUDDz/3RgChBQMGwQBBjxb/bxZxAAEGgw+QoAIAEQYDDwABZgAxBY
+MAAA+UkIAAICa3D41FRgAgJCMCKAtfQKAC/pADSgB9UR9lUIC5AEO2BtCDMsZvupT/NdBCMABDvg
+kjArZvu0VfeAtS8ABDkg9v8KAAICITD//QQuCQBysP72ACv/07kQY//F0Q8AAGwQBBLwnNMP0w8p
+Iprz8NcfwBAoMA8CAPSQK2B+AlJwBaoBKiaaKjahKCKW9AoAIBcANiD6CgAiAABZMFuLhCsilrFE
+e0PsKSKcDwIADwIA9JArYH4CanAF3QEtJpwtNqIsIpf0CgAgFwA3IPoKASIAAFkwW4t2LiKXsUR+
+Q+wpIp4PAgAPAgD0kC1gfgIicAVEASQmniQ2oy8imfQKACAZADfg+goCIgAAWTBbi2goIpmxRNMP
+eEPqKSKY9AoAIBkANmD6CgMiAABZMFuLYCoimLFE0w96Q+opIqIqOgD0kFZgfgJicAXMASwmoisi
+oioyzys2ziqtAxvwni4yvBjxELCvD38U9P8RDgBAQ7AP7gIuNrwtMqx/1wgoMssICEt7gTgsMszA
+l3yQCC0yzA1dS3vRBsAg0Q9j/7wuMswS8QEvrf4PbxT7/xEOAEATsA/uAv42zCAAEBAw0Q8oMssc
+8N0prMD5aRQIAEBiMAmIAig2y2P/rABsEBIa8KMpCgD6oIAgBBBAMG2KDACQBAoMG3/HAbGbsZn6
+8O0R+gJK8PXwfxAEEEAwCYs4GPDn+fDnH/8QcDD7HxQCABBoMP9WgCAAECgw0w9t2hklhoElhoAq
+loMuloIqloH+loAgEAJCMCmcEBvw2xzw2x3w3B7w3PgKACBAEEgwbZokC4kKKZ0ElZANiQopnQSV
+kA6JCimdBJWQ/IkKAAICQjApnQSVkFuMAvjwzxAIEFAwbaoFJYYwKI0EEvDH9PBPEAAQGDAlJsIl
+JsMlJsQlJsX1JsYiAABQ8FuLzioiwA8CAASqAvomwCACAhjw8i1AJagCOOD48EsQCBBQMG2qB/WG
+hCAIAkIwF/C5EvC5JXavKyKawsD8uwIAABBAMPsmmiMIEFgwbboWJXbGJXbHJXbIJXbJJXbK+HbF
+IAICQjD4HAAAEBBQMA8CANMPbaoH9YYAIAgCQjD0CoggABAYMPo8AAIAAFhwW4uRsTN0Oe8T8CX0
+CgAgAhAwMBvwjvzwjRIAAFDwW4ucIz1A8k0KAAICITD21o0lxgI5IBrwPCqggPTwjRAAEBgwADAE
+Cgsb/wIAAgB2ftCxM/RNQCXYAjjgG/CN+vCLHYAEOqBbi4ob78olJov98IkQgBAYMPuw0S/4EHgw
+9ApkL/4QcDD68IQQBBBAMPsLQAABEGAw+WoAKgUAXzD8HFAqBQBacPkKACDAAlhwbYpZo9IoIMwi
+IM0IqBwEiCzyRQwIAEByMAWFKAKIKARVLASILP6IAQAKAmtw/lUBCAEAQbD1ZTcCAEB6MPK2AChA
+AUAw+7wEJAAgRXD1xgAiACAosPzMBCgAIBZw/hxQKgAwypDAwG0IHA7NCovQscwMDEH4vP4iGAA6
+4PjWACH8Akpweas6Y//c2jD8HEAgABBYMFu+Ii8RIC9Givrv9hCEAnBwLeEALuEBAN0R+qCALAkA
+d3AtRolj/uUAAAAAAAD6my1wABBgMNMPbQgdDs0Ki9AszAEMDEH4sgxgBAJC8PjWACAEAkpwepsF
+Y//ZAAAAKRxQiJD4doQgCAJ6cP/yACAIAkHw/4aEIBACcnD+4gAgEAJ58P72hCAYAmpw/dIAIBgC
+cfAt5oQpHGCOkP52iCAIAmpw/dIAIAgCcfD95oggEAJicPzCACAQAmnw/NaIIBgCWnD7sgAgGAJh
+8CvGiBrvuy8SGywSGC0SGi4SGQw8FA09FA4+FP8/FA+gBDug/v8QDUAEP2D/3QIMCQBzMP3MAgAp
+EFgwW57dwCDaIFuKeQIqAluKcLEiaSTvwCD7Wu4iAABQsFuK0LEiaSTvwCDRD2wQCBTwCRfvYhPv
+XShBJBzvb/5BFSAfEBAw+0EXIX4ANiAlQRkqQRv2QRMsACB28KXdqt3+D0QKAbJtkPXzNGiAAVgw
+9YMsaIABLDBlkyT+aAwIAZDQkChFFAuPDA1iDCJFHQX/DP9FGCngAUAwC4kMKUUWAv8M/0UcL+AB
+fDAK+Qz5RRop4AQ6ICJBFvg2hCPgBDigIjaHIkEaDv8R/zaFI+AEOKAiNoYsQRr/Nv0t4AQ7IPw2
+/CAAEBAwZiKsIkEeyyj4LP8qAXpEoP8CAAgBghIQ2iBbp+YtMorwrhEMAEA/cPxBEiwJAHdwLTaK
+LDb+KTKBG+76C5kCKTaBKkES9u/KEAAQEDD178kQMQA2oC8yvdMP0w/8KBEOAEA38Aj/Ai82vSU2
+vFuelvSgCGACAhCwxy7RDypBEnoj0gqrCilB6vs2jSG9ADZgLDKBHe+5DcwBLDaBIkEeI0EdKkEb
+JUEZL0EkK0EXJkET/kEVIasAN+CVEJMRkhP8768SAAB68PoWAiIAAGmw+goEIAYQWDBbvfTAINEP
+AAAqMoQpQRIoQRMKmQwJKRQpRRUJiAwoRRQlMoQmMocvQRQGVQwFJRQlRRcF/wwvRRYtMocuMoUO
+3QwNLRQtRRkqMoUrQRcuQRUsMoYlQRn2QRMugAF0MPyqDAwAIHbw9d0IDeIBUDD82AgL4gFQMPpF
+GyoBAMWQZfHUCwhEZYHOBQlEZZHI/wIACADi0JAsQSQObgz+RRQoACBXcAlpDPlFHS3gAXQwC98M
+/0UWIQMANyAF+wwrRRgJuQz5RRwp4AFMMAqbDCtFGg7YESJBFvg2hCPgBDigIjaHL0EaDp4R/jaF
+L+AEP+AvNoYsQRr+Nv0t4AQ7IPw2/CAAEBAwZiDPKTKBe5ZaKDKKCAhV8IEEAAEQEDAAIhoCAk/y
+RR4uRAA0oP8CAAoAtUSgsCn/AgAIALwSUNogW6dnLjKK8K8RDgBAO7D9QRIuCQB7sC42ii02/isy
+gRzuewy7Ais2gWP+AMCAKEUeY/34KUHrZZ5AKkHsZa46K0HtZb40Y/49AAAc70iSE5MS9RYAIgAA
+abD6FgEiAAB68PhBHyAEEFAw+BYEIAYQWDBbvYjAINEPAAAL6AwFiAwoRRgKjAwJzAz8RRwp4AFE
+MAqfDC9FGmP+99EPAAAAAAAA/1wAAgAAa7D6FgAiAABy8PoKAiAGEFgwW7118/0pb+oQEDAAAAAA
+AP5sAAACEFAw/MywIAYQWDBbvW3z/Qlv9BAQMAAAAAAA+goCIAYQWDD87oQSAABosFu9ZWP9KwAA
++goCIAYQWDD87n8SAABosFu9X2P9E99Q+goCIgAAa7D8FgAiAABy8PzudBAGEFgwW71X8/6Ob+oQ
+EDDeYPzubxwAIFdw+woGIAIQUDBbvVDz/nFv9BAQMPoKAiAGEFgw/O5oEgAAaLBbvUlj/LsAAPoK
+AiAGEFgw/O5jEgAAaLBbvUNj/KMAAGwQGBbuxBLuZR/u9ilhEShiNy3wBCrwDPzyAiBQAlhwnLD6
+tAQgQAJwcC3kBP/yACQAEGgw/+YAKAEASjD4FiQqA4/HUP0WICABEFAwF+5m0w/TDyp28Vu5+hTu
+4ylCR/8CAAAEkipgGu7hGe7h+woAIAAQaDAooIEuoIAskIMvkIAikIIjkIH/oIIqAQB+8P6ggywB
+AHdw+N03CgEAHvDyuzcMAQB/cPy7NwwBAHdwGu5psbv5CgAgARAYMPqggCAEEEAwbYoMAJAECgwb
+f8cBsZuxmRzux/JAviH6Akrw9UC/IAQQQDD5izgABBBQMPsiNwACAkNw8kS+JAEARXD1RL8gBhBY
+MPUNRw7gARAwW7z9HO65JRYfIhYlL0DALkC9LUC8KEDBKBYAK0DCKxYBKkDDKhYCKUDEKRYD+EDF
+IAYQWDD4FgQgBBBQMFu87vscMCAAEFAwW4p7Fe3ZG+6nKhIML1Kh9PAPagAgWrAsUqD/AgAABNUr
+IC1SnP8CAAAEi6tgLkJH/wIAAAQPK6AvYejy7lYQBCWr4CxCFitCFS1igilihP2ZDAoDAGbw8pkI
+ABgQYDALmSwMmCwrnDAMuy4LmQypiAqIESiNDPoWHioAIEKw//qAIP4CcrAP7gEuFiIudsRbicws
+EiQoQkcrEiIKzDcJzBH/AgAKACBm8P8CAAAEAaogLhIg0w/TDw7uDw3uES4WIxruPtMPDwIAL6Ec
+9PAgb8AQMDAc7k0swWAd7bwrvD/2uwENwAQ7IPvWjCoAIGbw/u3pEH4CavAG3QEtFiGd4Vu2ixzu
+Ih/t+S0SISsSJPnuKxwAIG6wLdw/Bt0B+JHoKgAgX3D99uUgfgJa8P/6wCoAQDbw+rwAB5wANiAt
+QMEoQMAqQMUuQMMpQMImQMQrFhL+QkcoACB2cPpmCAgAIGow+YgIAgAAUvD7KQwGACBBsPYWHSSq
+ADegDjgUL0C9/xYcIAIQUDD4FhogABBYMFuKFysSGlqdbykSGioWGwmqKFuJeywSGyoSHcC4DLsM
++6ooAGQQWDBanWYrEhv6FhUqACBW8PsWFiAIEFAwC6oMKxIcKhYX+6ooAGQQWDBanV0c7eovEiMr
+EhouQhUqFhgLqigODQbyRiEoAEBXcPlGIiAiADfgr+iwiAjYAQiYDPhGIigAIBIwLkIVKHbGKUIi
+Dg0GL1Ka0w9k96UoUpxkh7YvUp5k98cvUqHI9ChSoGSH0yhSoy/6wPgWFCAKADYgKFKiZIfTKBIS
+DwIADwIADIg3rojyqggB/gJCMPpGHygAQENw+EYhKgBAS3AIqgz4UNEqACASsCpGIvoSFiAgEEgw
++YgCAIAQSDD5EhgoCQBKMChU0fqZCAAIEEAwCYgMKUIWKhIXCLgoCQkG+RIVKABASjALqigLmSj4
+RiAugBBAMPKqCAgAIEZwZGDoK0DAZLZoBp0s/b0oAH4CUrAPqgH6RiMsAEB/cP1GJCoAIGqwK0DB
+ZLZUBp0s/b0oAH4CUrAPqgH6RiUsAEB/cP1GJioAIGqwK0DCZLZABp0s/b0oAH4CUrAPqgH6Rics
+AEB/cP1GKCoAIGqwK0DDZLYsBp0s/b0oAH4CUrAPqgH6RiksAEB/cP1GKioAIGqwK0DEZLYYBp0s
+/b0oAH4CUrAPqgH6RissAEB/cP1GLCoAIGqwK0DFZLYEBp0s/b0oAH4CUrAPqgH6Ri0sAEB/cP1G
+LioAIGqwessKLlDRwvAP7gIuVNFbuMj7QhUgABBQMFv6//anYWIAABKw+0IWIAEQUDBb+vsKogL9
+QhYgA7AuoP7SFWAAEFAwbQgKsaoAoQQAOBp9iwJj/+4iEiUmHCD4QiAoACA0sCmQACpGGQ2ILAmJ
+LgmIDPhGGCBQAhhwoy8v8AAuQhgC/xD9ds0sCQB/sCxCGRrtmPvtmRHqAmMw/XbOLQAEOyBbiNIu
+QhYqQhUrQhgtEh8vQiICuyz87ZAYACAzcCiAAAr/LA7MLPxGGyH8AlrwK0YaHO1ACPguCP8ML0YX
+H+2Ho9kpkAAoQhcBmRD6dssoCQBKMC5CGCtCF667+bwwIBgQcDAOmS4Juwz4dswgYAJa8ClCGCpC
+Fw6ZLA6qLA2qLB3s7QKZLACqEfvWGygJAFZwKXbHKhIeKnbFKPJXCrkRDogs+ogRCgAgSrD61hgq
+ACBCsC/yWA7/LAr/EfrWGSoAIHqwDrss+tYaK6AEPuD9Qh8qACBasCtCIS12yCt2yhntYC1CIytC
+JChCI/vs6hgAIFow/bbbIf4CQjAuQiUottwvQiYtQiWv3f621yH+AmtwL0InLbbYKEIoLkInqO7/
+lvUh/gJzsChCJy6W9i1CKC9CJ63/+LbfIf4Ce/AtQikvtuAuQiooQimuiP2W9yH+AkIwLkIpKJb4
+L0IqLUIpr93+tuMh/gJrcChCKy225ClCLC9CK6n/+LbhIf4Ce/AtQi0vtuIuQi4pQi2umf226SH+
+Akpw+bbqKgAH0xAsUNHC0A3MAixU0Vu4OcAg0Q8a7O//AgAKAMDGkCoWIPP422ACEFAwAAAuQnr7
+FhIhfAA3oA44FC9Avf8WHCACEFAw+BYZIAAQWDBbiOsrEhlanEMpEhkqFhPTDwmqKFuITiwSEyoS
+HcC4DLsM+6ooAGQQWDBanDorEhP6Fg0qACBW8PsWDiAIEFAwC6oMKxIcKhYP+6ooAGQQWDBanDAc
+7L0qFhAiRiEpEhkuQhUvEiMJqSj+DQYACBBYMPkWESgAQE9w+UYiICIAN+Cv6rCqCtoBCpoM+kYi
+KgAgErAuQhUqdsYpQiIODQYvUppk84MoUpxkg5QqUp5ko6UvUqHI9ChSoGSDwipSo9MP9KAKb8AQ
+eDAoUqJkg8MoEhIMiDeuiLCICNgB+EYhKgBAS3AIqgz4UNEqACASsCpGIvoSDiAgEEgw+YgCAIAQ
+SDD5EhAoCQBKMChU0SgSGfpCFigAIFZwCbkMCYkoCgoG+hIRKABAVnApRiD5Eg0qACASsCpGH4of
+CJko+oooDoAQQDComfP7UWoAIBKwAP8CAAoBDkSQIhYg8/dWYAMQUDAtQLxk0qouQL3+FhwipAA3
+oCJCFShCFvYSHSIAAFOw8oIIDoAQcDDymQwCACBC8PgIBgH+AhCw/pkIDgAgarD5qigCAEASMP2e
+KAYAIHGwIkYfBu4s/kIVKABAcjAGqiz4RiAiACBAsP4OBgIAIBOw+uoBAf4CELD6RiIuAEATsC5G
+IfP6smoAIHKwAAAoQnr/AgAD+2yqICpCFSlCFgoqNvpGFSgDAEywKUYWY/a9K0J6/wIAA/vvquAs
+UppkwpQtUp5k0p4pUqP/AgAB++QqYC5Sov8CAAP73qugKlai8/eyagAgSrAvYen/AgAD+9kr4Chh
+6v8CAAP706ogKWHr/wIAA/vOKmBj988AACxCev8CAAP7/SsgLWHoZNImLhIgK3bGDu4PC+sLwPAv
+FiNj9+soken1iGFvwBB4MC2R6mXYVi6R62XoUMePKHbGwNAtds0tds4tdsstdswZ6+Mtlhstdsco
+dsUolhgolhkolhoodsgvUpr4dsoh+gA34C5SnmTh/ylSo2ScCS9SomX8AypWovP7/WoAIEqwAChS
+nSpWnPP242oAIFIwx9/9RiMgABBYMCtGJGP5pMeP+EYlIAAQcDAuRiZj+bjH3/1GJyAAEFgwK0Yo
+Y/nMx4/4RikgABBwMC5GKmP54Mff/UYrIAAQWDArRixj+fTH//9GLSAAEHAwLkYuY/oIAAAd663/
+AgAKAEZHUC0WIPP1M2AEEFAwAAAqVqDz9lNqACB6sChSmy9CIQiZDPlGIi4AIE/wL1aaY/hCKFKd
+L0IhCJkM+UYiLgAgT/AvVpxj+DEoUp8vQiEImQz5RiIuACBP8C9WnmP4IChCIQ+ZDPlGIigAIEow
+KFagY/gXKBIUCJkMKEIh+UYiKAAgSjAoVqJj+BQd7B5402YtFiDz9KVgBRBQMMff/UYfIAAQWDAr
+RiAtRiErRiJj+HEoUpsvQiEImQz5RiIuACBP8C9WmmP8ZC9SnSpCIQ+ZDPlGIioAIEqwKlacY/xT
+KlKfKEIhCpkM+UYiKAAgSjAoVp5j/EIAG+uiKxYg8/Q7YAAQUDAAAChCIQ+ZDPlGIigAIEowKFag
+Y/woKEIhCpkM+UYiKAAgSjAoVqJj/CcpYellndIsYeplzcxj/dUALVKbKlaa8/1hagAgU3AAAC5S
+nypWnvP9V2oAIFOwAAAqUpsrVprz/gBqACBasAAAK1KfKlae8/32agAgUvAAAP1CFSACEFAw/Ovg
+EAgQWDBbuhnRD8Ci/OvdEAgQWDBbuhbRDwAAAABsEAwU69kT6z4e69gc65US69iF5o3niOWJ5Prg
+AiAgAlhw+rQCIEACeHCZ8JjxnfOV8v7hAC//ECgw/rUAIAMQcDAf68aVwJXBlcIlxgMlxgT4MqQg
+IAJQcPXGBSABEGgw9cYGIAAQSDD1xgcuAEB+MP/ZOAguAUAw+SRVKAAgUjD4gAAgQAJYcPgkVCAH
+EFAw/EJKIAgQKDBtWg8vsQcrvP7/JSsqAARj0LCqx68oMqQb67D1qREIAEBaMAmIAig2pBfrAidy
+hSYyggd3Uvd8Aix/EEAw+XcRBgBAQbAHZgImNoIY66Uf6wkmISstIFX8MoIgARAoMCUkkCpCTCtC
+SitGSysmFysmGPomFiCAEEgw9UHlIEAQODDw0QQMAEB7MPBmGgYFAGZwJ4SA9iYZKABVq5D/65Ef
+wBBwMPqqCQBgAmhw+7kJC6AEOqD6OhQJoAQ+YPk5FAB+AlKw/PACIH4CSnD81AIoAEB2cP/xACoA
+QHKw/9UAIIACUrD6JhsggAJKcCkmHSgypB/qn/kcMCguAUAw//KZKAAgSjAogAAI/yjDjwj/Cw7/
+AfoiFiCAAnvwLyYfLTLJddcXHOtwKzLP/wIABgBc5tDasMTQDa0KLSYjW/jCwCDRDy5KAA6+N/4m
+FyoBAHKwW6NtKiYWKUEYKiYX+kYiIgAAKrD6RiQgigA2YPrq6x3wBD5gDd0J+0EXIAYCY3AN3DsM
+LBIMuwgLWwgLqjYqJhYqJhdbo1wuIhcc61T9IhYgBhBYMPXoDAIAAHqw+BYAIAQQUDBbuYAqIhZb
+o1L6IhciAABKsCkmFlujT/oiFiIAAFqwKyYXBb0MLUYlBawMLEYjY/60Kqw/DqoBKjbPY/9AKkI1
+BaoJKiYWKiYXY/+SAGwQBCZaQPoKgCIAAEEw/frAIEACWLDzJgAgYAJgsPwmDCAAEEgw/CYNIh8B
+JDD5JRQqAEBu8PWlOgCAAlrw+yYJIHACULD7JggoBQAZsPglFSIAAFlwW3n/A2Q5+1wAAgAAUTBb
+hs+aIdEPAGwQBhPq0SgyWxXq0PIKACA1ADYgKFJ7JDKmDwIAqCgJiBGoRCpCBysqAPqiDiAAEGAw
+W7KrKTJbsSIPAgD6Rg4r/+nMkBLrDvQKACAAEDAw+yDlIgAAUTBbhqb0TAEmAQBRsPlE6WACAhCw
+LDJZKzJYKjJarLv7qggAABAgMPwWAiDWADagsW7+FgAgABBoMJ0TYABTLyBUKyRV+/sIAgAAUvCb
+Ev0SACoAFVqQgiAKuAxtiR0oUnspMqb6iAgAAgJSsAmIEaiYJ4QMJoQNkogthDcqMlkpMlgoMlqq
+mfRMASgAIEoweEtuIlJ4KjKmokIJIhGqIiYgDYsS9yAML5YANaCLEylSe6uZCZkR+CIAKAAgTrCZ
+ESeUDPaUDSIAAFmw+JYIIgAAUfBbhnKNEYwTKSBUKtQ3/CRVIAICYzD8FgMjIAE6YIoSqp76JFUh
+/gJbsJsSY/9M0Q8AAGwQEhTqe9MPKEGtJwqA+AhBAAAQKDAIdTn6+v8iAABZcFuGbvsKACIAADKw
+9RYSL/8QUDBbhmkqFhb6KkAgABBYMFuGZioWFfr6/yEAEFgwW4ZiKhYU+vr/IQAQWDBbhl8qFhP6
++v8hABBYMFuGWyoWEfsKAC//EFAwW4ZYmhz6+v8kABBYMFuGVSoWEPsaACJAEFAwW4ZRmh/7WoAi
+QBBQMFuGTpoe+uqgE4AQWDBbhksqFg37CgAv/xBQMFuGR5ob+woAL/8QUDBbhkSaGvsKAC//EFAw
+W4ZBKhYJ+vr/JwAQWDBbhj2aGPr6/ycAEFgwW4Y6mhf6+v8nABBYMFuGNyoWBvr6/yCAEFgwW4Yz
+mhX6+v8hABBYMFuGMJoU+woAL/8QUDBbhi2aE/r6/yOAEFgwW4Yp1aD7CgAv/xBQMFuGJi1CLChC
+N4cXI0IzixOPGS5CLSIWGClCOCJCMg/uKAuZKAczKIsUhxiPGguIKAciKA/dKIce/0IxIgAgGLAi
+FhqCHP5CKywAIHdwI0IiAv8oghsHMyiHHQLuKPJCISgAIEowKBYZiB8HIignQiPzQh8iACAYsAh3
+KCgSECsSESlCHggzKCgSFPdCHCIAIDzwC5koKxITCHcoKEIdLEIa/xIWLgAge7ALiCiLFflCNSgA
+IEowD8woL0I0C5koixYL/ygrQhv5EhUuACBP8Am7KClCGae7+WooAgAAOrCsrKvMqMyjzKLMrsz9
+EhosACBrMC5CO/1COSwAIGswBe4oB90o/hIZLAAgd3CVEv/MCAf/EEAw8hIYLAAgczD5gw18ACBr
+MPAAB2IAABqwAMAwo8XaUFuF0dtQ9xYBIIAQYDBYGfD5rAAAgBBgMPesAABKADTgLUIZJxIS9QoA
+IDcAN2D6FhciAAAasP18AAIAAFDw+/r/L/8QYDBb/t5aln0uQhmxVf5T4XIAIB2w+RIXIIAQYDAG
+Vyinl2QmScAw9hYAIAAQKDAmQoClZiNmAC5CafhCGSoAB/TQDogI/wIACgBwxNAuQmr5QhoqAAd0
+0K6Z/wIACgCKTNAuQmv6QhsqAAd00K6q/wIACgCX1NAuQmz7QhwqAAf00A67CP8CAAoApNzQLkJt
+/UIdKgAHdNCu3f8CAAoAsmzQLkJu/0IeKgAHdNCu//8CAAoAv/zQLkJv+EIfKgAH9NAOiAj/AgAK
+AMzE0C5Cc/lCIyoAB3TQrpn/AgAKAN5M0C5CcvpCIioAB3TQrqr/AgAKAO/U0C5CcftCISoAB3TQ
+rrv/AgAKAP1c0C5CcP1CICoBDnTQrt3+ChQqAQlo0C5kBGAANwAAAPp8AAIAAFjw/RISIAAQeDD/
+ZAQv/xBgMFv+jogQx7/7pgAgABBIMPlmByYAID4wWpYoLAqAsTP8VQgP/2UQ0GAFAgAAAAAAAPp8
+AAIAAFjw/QoAIAIQYDD8ZAQv/xBgMFv+fC0SFvpmByCAEGAw8//DZgAgP3AAAPp8AAIAAFjw/goD
+IkAQYDD+ZAQgABBoMFv+cC8SFfpmByCAEGAw8/+TZgAgP/AAAPp8AAIAAFjw/Pr/IAQQQDD4ZAQh
+ABBoMFv+ZCkSFPpmByCAEGAw8/9jZgAgPnAAAPs8AA//EGAw+goFIQAQaDD6ZAQiAABR8Fv+WCsS
+E/pmByCAEGAw8/8zZgAgPvAAAPp8AAIAAFjw/AoBIQAQaDD8ZAQv/xBgMFv+TC0SEfpmByCAEGAw
+8/8DZgAgP3AAAPp8AAIAAFjw/Pr/IBIQcDD+ZAQkABBoMFv+QJpniK4vEhD2hgAggBBgMPP+z2YA
+ID/wAAAAAAAA/CpAIgAAWPD6ChghABBoMPpkBC+WEEgw+WQFIgAAUfBb/jCLH/pmByCAEGAw8/6U
+ZgAgPvAAAAD6fAACAABY8PwKFiWAEGgw/GQEIkAQYDBb/iSNHvpmByCAEGAw8/5kZgAgP3AAAAD8
+6VsSAABR8Ps8AAAVEHAw/mQEI4AQaDBb/hiPHfpmByCAEGAw8/40ZgAgP/AuQnf4QjEqAAf00A6I
+CP8CAAoAkMTQLkJ0+UIrKgAHdNCumf8CAAoAnkzQLkJ1+kIsKgAHdNCuqv8CAAoAq9TQLkJ2+0It
+KgAH9NAOuwj/AgAKALjc0C5CeP1CMioAB3TQrt3/AgAKAMZs0C5Cef9CMyoAB3TQrv//AgAKANP8
+0C5CevhCNCoAB/TQDogI/wIACgDgxNAuQnv5QjUqAAd00K6Z/wIACgDuTNAuQnz6QjcqAAd00K6q
+/wIACgD71NAuQn37QjgqAAf00A67CP8CAAoBCNzQLkJ+/UI7KgAHdNCu3f8CAAoBFmzQLkJ//0I5
+K/6fdNCu//8CAAv+mnjQ+nwAAgAAWPD8+v8gGhBAMPhkBCAAEGgwW/3NiRH6ZgcggBBgMPP9B2YA
+ID5wAAAAAAAA+zwAD/8QYDD9CgAgCBBQMPpkBCIAAFHwW/3Aixz6ZgcggBBgMPP81GYAID7wAAAA
++nwAAgAAWPD9CgAgCRBgMPxkBC//EGAwW/20jRv6ZgcggBBgMPP8pGYAID9wAAAA+nwAAgAAWPD8
++v8gChBwMP5kBCAAEGgwW/2ojxr6ZgcggBBgMPP8dGYAID/wAAAA+nwAAgAAWPD8+v8gCxBAMPhk
+BCAAEGgwW/2ciRn6ZgcggBBgMPP8RGYAID5wAAAA+zwAD/8QYDD6CgwnABBoMPpkBCIAAFHwW/2Q
+ixj6ZgcggBBgMPP8FGYAID7wAAAA+nwAAgAAWPD8Cg0nABBoMPxkBC//EGAwW/2EjRf6ZgcggBBg
+MPP75GYAID9wAAAA+nwAAgAAWPD8+v8gDhBwMP5kBCcAEGgwW/14jxb6ZgcggBBgMPP7tGYAID/w
+AAAA+nwAAgAAWPD8+v8gDxBAMPhkBCCAEGgwW/1siRX6ZgcggBBgMPP7hGYAID5wAAAA+zwAD/8Q
+YDD6ChAhABBoMPpkBCIAAFHwW/1gixT6ZgcggBBgMPP7VGYAID7wAAAA+nwAAgAAWPD9CgAgERBg
+MPxkBC//EGAwW/1UjRP6ZgcggBBgMPP7JGYAID9wAAAA+nwAAgAAWPD8+v8gGRBwMP5kBCOAEGgw
+W/1IjxL6ZgcggBBgMPP69GYAID/wKEIy9yqQInAQGDD1CgAgeQA2ICZCeCJCgKZWCWYRpiKGJ4Zu
+wMD7IRIgwAJRsFuwC4srmirzaggAABBgMFuwB4sumi33aggAABBgMFuwAyomEPsiESSgEFAw+moI
+AAAQYDBbr/4qJhP7IFQmsBBQMPpqCAAAEGAwW6/4K0IysVX6JhQr/8fdUCxCM/UKACB5ADcgJkJ5
+IkKAplYJZhGmIoYnhm7AwPshEiDAAlGwW6/riyuaKvNqCAAAEGAwW6/niy6aLfdqCAAAEGAwW6/j
+KiYQ+yIRJKAQUDD6aggAABBgMFuv3iomE/sgVCawEFAw+moIAAAQYDBbr9grQjOxVfomFCv/x91Q
+LEI09QoAIHkANyAmQnoiQoCmVglmEaYihieGbsDA+yESIMACUbBbr8uLK5oq82oIAAAQYDBbr8eL
+Lpot92oIAAAQYDBbr8MqJhD7IhEkoBBQMPpqCAAAEGAwW6++KiYT+yBUJrAQUDD6aggAABBgMFuv
+uCtCNLFV+iYUK//H3VDAINEPAAAAbBAGGOgcFugcAiUK+FUKAIQANKD/AgAAAE4EoPpStSIoAjig
+KVK0epkOW4OcIlKz0Q8AACJSs9EPLFKxBA4GA60MDN0MDt0B+Oe1HAAgazAsVrMrYkcvYhQZ5/P6
+CgIgARBwMPv/CAAAEGgw/P8MAgAAWzD5zAgOACBH8FuwER/nGy7w0SIKgALuAi700SJSs9EPAChS
+taOIqEj0CQYB/gJCMPJSsygAQEowKFa00Q8AAAAA+iwAAAAQWDBbg634+sAgfgIisAhEAfpSsSIA
+AFkwW7htwKFbg22UEBzn6PsKCCABEGgw/woAIgAAOrD6CgUiAABx8Fu2D8Cg0qAT52ijcyo2gSQ2
+gio2gwLkFgECANpw+TKDIAIQWDBaOuUkMojISfp8AAACEFgwWjrh3kAC5BbApfzn0hAIEFgw/zKI
+IAEQaDBbtfsqYugpUrKqmSlm6ChStPhm6iAAEBAwIlaxIlayIlazIla0Ila10Q9sEAgc53QX5v3T
+DyjCE/ZylSBFADYgwCDdYP5iUCAFEFAw/Oe8EDAQWDBbteYf57oc57st8msu8pj/8mchsBBIMPkW
+ACAFEFAw8hYBIDAQWDBbtdzRDwAqcncocnT+cnUuUBAwMPamCA/wEFgwC2YB/wIACgBgdZD75sEQ
+uAA2ICmw0SoKgPZ2dygJAFZw+bTRIgAAUbAvcnb2FgMhsBBgMPwWASACEGgw+hYAIBAQWDD7FgIg
+BRBQMPzmzhAAEFgwW7XA/WwAAIEANaD23AAAbAA3YNrQ/BqwIAAQWDBbrlwa5wL2dpUhQBBYMFuS
+9dsg8hYEIgAAYPD6ZlAiAABpMP5cAAIAAFGwW/OZ/QoAIAEQSDD6mjkCAAASsPesAACMADSgCp04
+Zd7tY/7/AAAAAAAAAPP/XmAAEDAw8/7tb/QQEDAAAAAAACpybS9ybPasDy/wEEAw+xqwJgBAQbCr
+a3vzPylyasuZ+3ZtIgAAUvAc5q0ucmuWE/oWACGwEFgw+xYBIAAQaDD7CgAgEBBQMPoWAiAFEFAw
+W7WK8/8sYgAAabDz/8lgABAwMNww/UwAAgAAcXD7EgQiAABRsFvyTB7nBMDx/eITIAAQYDDyrAAA
+ARBQMPesOAIAAEiw8v04CAUAF/D95hMuQwA3IMCACag4ZY4hY/4zAAAAbBAEEub1KCIWyIPAINEP
+APrmuBFBEFgwW5KrGOdCHedC+IF/IUEQWDD50j4gABB4MP4KECIAAGKw+uatGABAcjD+GgAoBQBH
+8PnWPiwJAHMwW5XXW3YMwJH5JhYgABAQMNEPAGwQBPPnMRH8EFAwFOalCgw/KULfCVkUKTYcKELh
+CGgUKDYdJELjBFQUJDYeEuaGIiLeAgJA8jSQIAAQEDDRDwBsEAQT5yL05yIQABAwMA8CAPIKBCIA
+ACjwbSob8lJ/IAQCGPDyNeMgCAIhMCJSfwIyFCI15yZGmMAg0Q9sEAQX5xUc5t0U5xQV5xX95xUQ
+ABAQMPsKECIAADEw81wIIgAASXD4TAACAABQ8NMPbbojLYaDKYZ/+YaAIFACSnAqhoH6hoIgUAJS
+sPuNAiBQAkIwLLUMG+cEHOcE+W0EIAAQQDD4lh8gJAJQsFt2BBrmcCqggAAgBAoKG3+vQRzmvx3m
++PsqoCACAhCw+2YIBgAgXfD7RAgCACBc8Po8AAQAIF1w+VwAAgAAQTD/AgAAEBBYMP8CAAX/upig
+wCDRDxvmihrm6iuyfSqigfzmXxoAIFyw/ebnG5AEPuD7CsgqACBasFo84WP/k2wQBBTm4ipCavLm
+NBAAEEgw+EoAKnwBUDBtigoMmxD7JvsgAgJKcGSgUWmhTiNCaypCVluexluetPwKICAIEFgw9zc8
+cgAAKrAd5tEa5tL/5hAeBwEYMNMP/to5AA4AZPAPqgJ6NwUY5e4IqgJ7NxMZ5mjwAA1qCQBKsADA
+INEPABrmxfw3DHAEEEgwHeYL0w8NqgL+5sIQMgB08P6qAgAiAHjwL0B90w/TD3//AgyqAn83Agmq
+AguoAigm/CYKCwVmDARjECMm/RnmtQk5Aikm/iMm/ygtBCiCABvms/rmER8/EEgw/OavGABASjD5
+LQQoCQBaMPiWACA4EFgwW5U5GuYI/GwRCUAEOaD4bhEMgAQ9oPxpEA8ABD2g+f8CDAkAbPD47gIM
+CQB/cPxsAgwJAHdw/cwCADkQWDBblSkqQlIY5Z/8VhAAMBAYMPUKACAhADagCGYC3GD65fISAABY
+8FuVIClCUrFV+VPqcAICGPDAMPo8AAAAEFgw/AoAIAAQaDBbdZWxM2k+5yMKANowW3WIsTNpO/Uq
+IsAb5oQLqgL6JsAgABAQMNEPbBAEwCDRDwBsEAYW5n7TDw8CAChiXBXmfPLmfBAAECAw++Z7EpEA
+NiD7FgEgARBIMPkWAiAAEFAwmhAX5gYncnwjYqWnRwl3EQczCCcyBydyDitiWvwKACIAAFHwW63i
++2JaIAAQYDD6NhsgQAJR8Fut3fo2HCAAEFgwK3YyK3YzKWKhwaD6NAQoACBNMJkwKVCA/AofIAEQ
+cDD5CEYAABA4MPkNRAwABqIgLDQhJzQkYAAKLjQk/TQhIAAQODAkNCIvUFAnNRwnNR0nNR4nNR8n
+NConNCsnNGT3Nh0l7hBIMCk1GSc0NCc0LCc0Nfc0QSABEFgw9zUzIAUQQDD4NCkgARBQMPo0LS6A
+AXww/zQjIgAAUTBbf737CgAiAABisPw0aSIAAFEwW3+4LjAj+jRoIBAQSDApNGr5NGsiAImDoPvn
+B2AJEHgwa+Vt/+FqcA4QQDD44UhwHxBIMBrlKv8CAA4AtkuQKqDRDwIADwIAf6cLKyKALPrPDLsB
+KyaA+kwAAAAQWDBbfzUtYlzyLUAgAgIhMPVcASv/UO0QYAEYGuYD+jYdIBAQeDD/NR8gwBBwMC41
+HmAAFgAa5f36Nh0gCBBIMPk1HyDAEEAwKDUeiKbaMAuAAPowIyIAAEqwKTUcW38VKzAj/TBpIgAA
+YrD+MGgiAABRMFubA/oWACAAYa6gKDIdKIIJAzoCC4AA+hYAIABXrqD6PAAAARBYMFt+3voWACAA
+Ti6g2jBbfcgpMRx7n2F8n27/AgAAAFp6UPpMAAACEFgwW38FY/87AACJEsidW38c+hYAIAAzrqDA
+oJoSLlCIihH46B1gAhBYMPoKAiAYEFgw/OXmEgAAaTBbs/MqMh1j/0YqNh0rNR9j/z0AAAAAAPpM
+AAAQEFgwW37vY/7kAAD6TAAACBBYMFt+62P+1MDAnBAd5NMt0NF73ySJENKQ0Q8AAAAAAAAA+goC
+IBgQWDD85dASAABpMFuz24IQ0Q8AW31ziRDSkNEPxirRDwAAAGwQFBTlyBjlyCtC5ypC5idCGSlC
+Fy5CHS9CGyyCtC1CHy0WEywWFy8WFS4WFJkVJxYWKULo+4FqKgAgWrCbE/qBbigAIFZwmhErgWz7
+FgIgABAQMPiBcCAAEDgw+BYAI70ANmD5FgQvwBAoMP/lsBAAEHAw/hYSL/8QEDD/FhggABBYMB7l
+Nh/lqCjieCbyyqh4CYgRqGaDZxrkuYM+KBIXKWE+mTOYMi1CzaiYKBYX+RIFKgAFbpCdNGAABwCJ
+FSxhNpw0KZw/BZkBKTYFLUCQ/RYcIGgAN2ArQhgqYG4qFg4rFg9btaD9EhwgABBgMFu03QKqAf0K
+ASAAEGAw/Nw5CgUAU3D1vgEKCQBisPsKACAkADagLBIYjR/+Eg4gAhBQMP8SHCAAEFgwW7OL8AAG
+b+oQWDCeNmay3Yk1KhIWiDYqrD8FqgH6NgcoACBKMP9AkSB+AkIwBYgBmBX/FhsgaQA34CtCGipg
+b5ocmx1btX79EhsgABBgMFu0uwKsAf4KASAAEGgw/e05DAUAY7D1vwEMCQBrMPsKACAnADcgLBIY
+jR3+EgwgAhBQMP8SGyAAEFgwW7No8AAJb+oQWDAAAACfOGayUIo3KRIVjDgpnD8FmQH5NgksACBT
+MPhAkiB+AmMwBcwBLBYW+BYaIGYANiArQhwqYHCaGpsbW7Va/RIaIAAQYDBbtJcCqgH9CgEgABBg
+MPzcOQoFAFNw9b0BCgkAYrD7CgAgJAA2oCwSGI0b/hIKIAIQUDD/EhogABBYMFuzRfAABm/qEFgw
+nTpmscWJOS4SFIg6DwIALuw/Be4B/jYLKAAgSjD/QJMgfgJCMAWIASgWFf8WGSBoADfgK0IeKmBx
+KhYIKxYJW7U2/RIZIAAQYDBbtHMCrwH5CgEgABBAMPiYOQ4FAH5w9bkBDgkAR/D7CgAgJAA34CwS
+GI0Z/hIIIAIQUDD/EhkgABBYMFuzIfAABm/qEFgwmTxmsTWOOyoSE408/xICIH4CUrD5Mg4qAEAq
+sPo2DSwAIHdw/ECTIH4Ca3D6Fh0sAEAvcC0WFP0SACCBADcgLECUK0IgKmBymhabFywWEFu1Dv0S
+ECAAEGAwW7RL9bgBDABAFrD/CgEgABBwMP7+OQwFAG/w+hIdLAkAd3D7CgAgKwA3YCwSGI0X/hIG
+IAIQUDD/EhAgABBYMFuy94o9jz4vFhHwAAlv6hBYMCgWEZg+jRCOEY8SjBNgAAaOEYwTKRYRKWE6
+LDUiKGEyLzUkKTYQKDUj+BISLAAgYjCYP5wT+WEzKAAgQnApNSUoFhIuNSYoYTQoNSctNSj5YTUu
+ACB+cPgSES4AIHIw/xYCIAICOfD5NSksACBucPkSBCgAIEKw/hYBIH4CQjD9FgAoAEAqMPgWEy/+
+N8nQ0rDRD9EPAGwQBBrkTdMPJqJYFeTB9AoAIDIANaAY5L/35L8QABBIMG0IHCJwgABABAICG38n
+CwVGCyhmQClmQSaiWLFEdksCY//cKKJZ9AoAIF4ANiAb5DttCFAvsnkuoqavTwn/Ea/uLOETL+Bi
+hu8j4FYi4FeO7PszEQAQAmkw9mYRAlAEOKD2IgIOCQAf8PXdCw4JABfwL9ZAAO4RDswCLNZBKaJZ
+sUR5SwJj/6jRDwBsEAYY5CMugjL0CgAgIgC3oGAAnQAAAB7kli7iQrFE/wIAAgAgH3D/AgAKAEPx
+EBPkGBLkjyMyeCIikKNDCTMRoyIlIQcjIRMqIAz7IA0kbAEsMFt/+RrkhykhByghEgqZAfOZAgAA
+EGgw+SUHL6kANiAFVRFtCDUsIA0vIAwY5BzAkCmG+/PeCAACAmtw+yESL5AEP+D47goOCQB9cP7t
+BCwJAHsw/OYAK/+221Bj/8MAwNQN7Tb3CgAgxAA3YB7kEy4WAhnj8o8SGORpKZJ4KIKQ//C1KAAg
+TfAJmRH5iAgAABAgMPgWASCDADfgihGxQvqgDCIAAFiwW7FokhAmoQfzoRMiAAAqsCtQDfqgDCZs
+ATAwW3/IGuRXKVEHKFESCpkB85kCAAAQEDD5VQcgIQA2INtg8yoIAgAAcTD8CgEiAABp8FvxTStR
+ErEieyPijRKMEC3QtaMj/cOQcgAAIzAd5EMt0kLA5A7dNo4S93wBIAICc7D+FgIr/6Vt0C96//3j
+TBoAE5/QG+PZ89kMA4AQYDD7OAoAABBQMG2ZDCq2+/mNBCAIAkIwnJAZ5DApkc3Ag3mAZRjjthTk
+LSmCeIiKI0KQ9UIYKAAgSjD0QhkpkAQ6IKgzIjEHpUT6MAwkAwBpMPswDSJsARAwW3+SHOPA9UgM
+D1AEOKDTD/xZCgAAEFgwbYoVKDAMK8b7+p0EIAgCSnAJiBEI6AKYoNEPbBAGG+OzJAoC9Lb7IAgQ
+SDD0CgAiAABC8G2aCfmNBCAIAkIwlJDAkRjjqvm2+yQAEEgwDwIADwIAbZoJ+Y0EIAgCQjCUkARK
+AlvyChPjQBXi9fahpmIAABKwW/HP9qGbYgAAErArUjYsGvQMuygrNpAa4/kqNo8kNotbf0dmIXtb
+9+b2oXViAAASsPrjQhAnEFgwW482HuPxCi9A/+R+LAMBVDD95H8kAQFQMPTkfSgAAVAw+OR8IAQQ
+IDBb9Kn2oTliAAASsPrjMxFBEFgwW48nF+PjCotA0w8rdLgS4zUpMtP8IX0gAKQuYP8CAAIAl/cQ
+LSF+9uN7EA8At2AuIX/M5C8hgGTx2hTjD/sKAiAAEGAw/goBIgAQaDD649IQAhBAMPhGEyAAEHgw
+WpSm9qDJYgAAErDAkSlGEygywih2Ri8y2J9gLjLZLmYBLTLbLWYCLDLwLGYDKzLs+2YEIABOLKAp
+Yv8a4xAImREropALC0cLmQIpppBb82P2oHxiAAASsFvyaPagcWIAABKwW/II9qBmYgAAErAb43Eq
+chHTDwuqCBvi0Cq2q1vx4BPjS/gKgCAAEBAw/Pr0IAMQIDAtMjpu0zYrMjnLsPoKACABEGAw/QoA
+IAAQSDD5FgEgABBwMPkWAiABEEAw+BYAIAAQeDBalh4kNjnLLtEPsIj6jAEgARBoMPraOQACAlow
++8I4D60AtqBj/94AAAAa4t37GkAjJxBgMFuSCmP+wy5Q0QTuAi5U0WP+rwAY4ugb4ub8+v8gVBBQ
+MPmCfyAIAkIwDwIA0w/TD22qDPmCfyoAIFpwLKaAtIgqUNH7nQgCAABy8NMP/NaAIKQA/rAY43rT
+Dw8CACmCf/qCgCBNEFgwbboS+YKBKgAgdnD6toAgEAJCMCqCgC9Q0K6Y+oaAIjgBO+Aa4qcsonYr
+CgILzAIspnYpon4LmQIppn5bfrce4pMt4oLA8Q/dAi3mgtEPAAAAAADz/lRgABAQMGwQBB3i39MP
+0w8s0lnTDw8CAGTAQfjM/yIAAFsw+MoBDgAPxxBtCA/5rP8iAABasPmqAQ4ABM7QY//pAAAPuxEe
+41AvzB8PXxQr5Hz/5H0gABBQMCrkfvjSpiCAEFAwCAA/W5sKCgE/0Q8AAGwQDBbjRY8gFeLE8+K8
+EgAASPCZFS1SeShSgCxSei5SeCtSf/LwDCIAADiwkhcv8A2fGBLik/m7EQ+QBDug/o4IDZAEOyD+
+FgktkAQ/YP5iNiwAIG4w/RYKLAAgYjD8FgsoACBaMPgWDCBYADegKgoAW360AqkIKJKAZ4ALbQgF
+KpKAZ6ACY//zG+KcL2I2HOKa+goCIAAQaDD//fAgARBwMFurACNWZC5iNhjjHCNWZint8PlWZSgA
+IEOwKFZnKFZoK2JpyrP6CgEiACAfsFt+naKpLJKAZ8ALbQgFK5KAZ7ACY//zLmI2ZOO/W367LVIh
+LlIiL1IgIlIjI1IfJFIeKFIdKVIcK1IaLFIZKlIbLFZq/FIrKgAgZvArVmv7UiwqACBasCpWbPpS
+LSgAIFZwKVZt+VIxKAAgSjAoVm74UjIkACBBMCRWb/RSMyIAICTwI1Zz81I0IgAgGLAiVnDyUjUu
+ACAX8C9Wcv9SNy4AIHuwLlZx/lI4LAAgd3AtVnT9UjssACBrMPxWdSoAIGbw+1Z2KgAgWrD6Vncg
+ABBgMPxWaSgAIFZw+VZ4KAAgSjD4VnkkACBBMPRWeiIAICTw81Z7IgAgGLDyVnwuACAX8P9WfS4A
+IHuw/lZ+LAAgd3AtVn9bqqvToFuqqtKgW6qpGeJX+/qAIAAQaDD/4bQQgBBAMPcWBCoABhJQ9xYE
+IAIQaDD5pxEKAEiSUClSaC5SZtMPB5IMCyIB/wIACgEndJAqUmVkokMr8NHyVmgqCQBG8Pv00SIA
+AEiwHOG+L1Jn+RYAIAUQUDD3FgEggBBoMP0WAiAAEFgw8hYDIAIQaDBbsKhlIIwf4nco8rUk+oD/
+8rQg/gISMAQiAaJ+/wIACgDt99AZ4m8pkrJkkc0Z4m3wADNgABBoMBriag3ZCgqZCiiStf+StCD+
+AhIwCyIBon7/AgAKANd30CuSsmSxo/8CAAIA4YNg/pa1IgAAQ7Ac4aYukrP3FgEggBAgMCQWAvIW
+AyAFEFAw+BYAIAAQWDBbsITyVoAh5AA0oCdSfyRSeilSeChSeS1SMvmZEQWQBDkg+YgRB5AEPeD3
+JwgNkAQ/YP1KFAgAIECw+BYGJAAgILD4EgkoACBMsNMPbakFAAiGAElhihkNaxRbffQtUjOJFvgS
+Ci2QBD9gDUoUbakFAAiGAElhihoNaxRbfestUjT4EgstkAQ/YA1KFG2pBQAIhgBEYSoSCw1rFFt9
+4y1SOfgSDC2QBD9gDUoUbakFAAiGAEdhKhIMDWsUW33bwKH7CgAgABBgMFv6BS9RrcDjf+APwpDw
+ABJgAhBAMAAAAAAAAPkKACAGEEAwLWLIyNaomgraKFt94dowW/dLW/bzLmI2yOwOPBT74kUQAhBQ
+MFv58yhiaciEK2I2ZLBmG+Fs/OFpEAAQUDBb+e36CgAgABBYMFt9xxviOxziO/0KACIAAHqw/goB
+IAEQUDBbqhWKF4sYW68zjRSMFZrQ88YAIAAQEDDRDwAAAAD54foQABBoMPP+aGAAEBAw8/3LYAAQ
+EDAAABviJvg8FAACEFAwW/nTY/+HGOENL4DRKgqACv8CL4TRL5K0Y/4rL2JpwKL84ZESAABY8P7/
+CAAAEGgw//3wIAEQcDBbqfYoYmkjVmQjVmYpjfD5VmUoACBA8CiN8ChWZyhWaGP7/8ck0Q8AAAAA
+bBAG2iD7HAAAPRBgMFuPYxjiCokQIoJ/CpI78oZ/IgAAErDRDwAAAGwQBtog+xwAAD0QYDBbj1kY
+4gGJECKCfwqSO/KGfyIAABKw0Q8AAABsEAbaIPscAAA9EGAwW49P+eH4EAAMrqAESAryEgAoACBK
+MPKE6CIAABKw0Q/SoNEPAAAAbBAG2iD7HAAAPRBgMFuPQvnh6xAADK6gBEgK8hIAKAAgSjDyhOci
+AAASsNEP0qDRDwAAAGwQBtog+xwAAD0QYDBbjzX54d4QAAyuoARICvISACgAIEow8oTmIgAAErDR
+D9Kg0Q8AAABsEAbaIPscAAA9EGAwW48o+eHREAAMrqAESAryEgAoACBKMPKE5SIAABKw0Q/SoNEP
+AAAAbBAG2iD7HAAAPRBgMFuPG/nhxRAADK6gBEgK8hIAKAAgSjDyhOgiAAASsNEP0qDRDwAAAGwQ
+Ch/hvIv0iPby8gciAABQsIn1jPON8o7xnhGdEpwTmRWSF5gWKxYE//IAIgAAGTD/FgAgPRBYMFuQ
+EPasAABGADagKKAAwJD74KYQrwA2IAmUAvMWCCBAAjhw0xCFMA8CAA8CAAVaAluQFvtcAAIAABKw
++mwAAgAAYLBbrD3Ip7gzdznXxirRD6YsK8AAwtz9sQpyAABTMGW/5GAAAbHK+TIBL+EANqAuoADW
+oPXvqmQJACJwG+CLihgvCv//QVJ6ACBasPng8BCAEGAw/KwIAAEQWDD0xMwgGgB9MC2RfwvdAi2V
+f35HIy6Rf8D0D+4C/pV/IAAQEDDRDykKgKm4+EgIAAAQEDAihMzRD8Ag0Q8AACwKgPysCAAAEFgw
++8TMIAAQEDDRDwAAAGwQBtog+xwAAD0QYDBbjsMY4W+JECKCfwqSO/KGfyIAABKw0Q8AAABsEAba
+IPscAAA9EGAwW465GOFmiRAign8Kkjvyhn8iAAASsNEPAAAAbBAG2iD7HAAAPRBgMFuOrxjhXYkQ
+IoJ/CpI78oZ/IgAAErDRDwAAAGwQBtog+xwAAD0QYDBbjqUY4VSJECKCfwqSO/KGfyIAABKw0Q8A
+AABsEAbaIPscAAA9EGAwW46bGOFLiRAign8Kkjvyhn8iAAASsNEPAAAAbBAG2iD7HAAAPRBgMFuO
+kRjhAokQIoJ/CpI78oZ/IgAAErDRDwAAAGwQBtog+xwAAD0QYDBbjocY4TiJECKCfwqSO/KGfyIA
+ABKw0Q8AAABsEAbaIPscAAA9EGAwW459GOEviRAign8Kkjvyhn8iAAASsNEPAAAAbBAG2iD7HAAA
+PRBgMFuOcxjhJokQIoJ/CpI78oZ/IgAAErDRDwAAAGwQBtog+xwAAD0QYDBbjmkY4R2JECKCfwqS
+O/KGfyIAABKw0Q8AAABsEAbaIPscAAA9EGAwW45f+OETEAALLqCJECKCgAkiKPKGfiIAABKw0Q/S
+oNEPAABsEAbaIPscAAA9EGAwW45TGOEIiRAign8Kkjvyhn8iAAASsNEPAAAAbBAG2iD7HAAAPRBg
+MFuOSRjf8IkQIoJ/CpI78oZ/IgAAErDRDwAAAGwQBtog+xwAAD0QYDBbjj/2oB9iAAASsPo8AAIA
+AFkw/QpyIgAAYXD/EgAgARBwMFgYmdEPAAAAbBAG2iD7HAAAPRBgMFuOMfagH2IAABKw+jwAAgAA
+WTD9CnEiAABhcP8SACABEHAwWBiL0Q8AAABsEAbaIPscAAA9EGAwW44j9qAfYgAAErD6PAACAABZ
+MP0KcCIAAGFw/xIAIAEQcDBYGH3RDwAAAGwQBtog+xwAAD0QYDBbjhX2oB9iAAASsPo8AAIAAFkw
+/QpvIgAAYXD/EgAgARBwMFgYb9EPAAAAbBAG2iD7HAAAPRBgMFuOB/agH2IAABKw+jwAAgAAWTD9
+Cm4iAABhcP8SACABEHAwWBhh0Q8AAABsEAbaIPscAAA9EGAwW4359qAfYgAAErD6PAACAABZMP0K
+dCIAAGFw/xIAIAIQcDBYGFPRDwAAAGwQFhvgpPwKgCIAAFBwW6cj+iwAAD0QWDBbjvXyrAAByQA2
+oCigACMWIiUWIfQWICCHADYg9QosIAAQSDD5FiMg/gIgcPAAW2ACAiEwuGb/AgAGAMslkCdiAAd6
+AluO9vt8AAIAABqw+iwAAgAAYPBbqx1lr9aiNy1wAPXRMXIAAHHwZd/G+WIBIV8AN6AqEiMo4AD6
+mgICAAATsPoWIyAdADYg8/+vYgAAMHAAAAAA8//SYAICcfDAsCsWIyoSIiwSIfsSICBeEGgw/xIj
+IAIQcDBYGB0sEiP539oQAhBwMA8CAP/HDXABEGgwL5GrDv8CL5WrKBIj/YcNcAgQEDAqkasCqgIq
+lasrEiP8tw1wEBBgMC+Rqwz/Ai+VqygSI9MP0w97hwgqkawNqgIqlawrEiP6tw1wIBAYMC+RrQ3/
+Ai+VrSgSI3mHCCqRrQ6qAiqVrSgSICsSI4+aKgpgCroBCo85/5YKIBoAYvArka4NuwIrla4tEiN3
+1wgvka4O/wIvla4oEiN2hwgqka4MqgIqla4rEiN1twgtka4D3QItla4uEiN05wovka/AhQj/Ai+V
+ryoSI3OnCiuRr8DWDbsCK5WvLhIjcucIL5GvAv8CL5WvKBIjcYcTKpGvDKoC+pWvIAAQEDDRD8Yq
+0Q/AINEPAAAAbBAI2iD7HAAAPRBgMFuNbvaghWIAABKwHN9gwND8wIAgIAJYcPoKASAEEEAwbYoP
+fKAJsd36tgAgCAJa8A+qEfoKASBVADdgJhwQ/hIAIAAQeDD7CgAgBBBAMNMPbYojeuAYesAK8AAS
+bgkAfrAAAAANuS4GmQqJkA+fAvu8ASvwBDqg2jD7TAACAABhcP4KASBhEGgwWBev0Q8AbBAG2iD7
+HAAAPRBgMFuNR/agH2IAABKw+jwAAgAAWTD9CmwiAABhcP8SACACEHAwWBeh0Q8AAABsEAbaIPsc
+AAA9EGAwW4059qAfYgAAErD6PAACAABZMP0KaiIAAGFw/xIAIAIQcDBYF5PRDwAAAGwQBtog+xwA
+AD0QYDBbjSv2oB9iAAASsPo8AAIAAFkw/QpoIgAAYXD/EgAgAhBwMFgXhdEPAAAAbBAG2iD7HAAA
+PRBgMFuNHfagH2IAABKw+jwAAgAAWTD9CmYiAABhcP8SACACEHAwWBd30Q8AAABsEAbaIPscAAA9
+EGAwW40P9qAfYgAAErD6PAACAABZMP0KZCIAAGFw/xIAIAIQcDBYF2nRDwAAAGwQBtog+xwAAD0Q
+YDBbjQH2oB9iAAASsPo8AAIAAFkw/Qp8IgAAYXD/EgAgAhBwMFgXW9EPAAAAbBAG2iD7HAAAPRBg
+MFuM8/agH2IAABKw+jwAAgAAWTD9CmAiAABhcP8SACABEHAwWBdN0Q8AAABsEAbaIPscAAA9EGAw
+W4zl9qAfYgAAErD6PAACAABZMPxcAABYEGgw/xIAIAIQcDBYFz/RDwAAAGwQBtog+xwAAD0QYDBb
+jNf2oB9iAAASsPo8AAIAAFkw/FwAAFQQaDD/EgAgARBwMFgXMdEPAAAAbBAG2iD7HAAAPRBgMFuM
+yfagH2IAABKw+jwAAgAAWTD8XAAARBBoMP8SACAEEHAwWBcj0Q8AAABsEAbaIPscAAA9EGAwW4y7
+9qAfYgAAErD6PAACAABZMPxcAAA4EGgw/xIAIAQQcDBYFxXRDwAAAGwQBtog+xwAAD0QYDBbjK32
+oB9iAAASsPo8AAIAAFkw/FwAACQQaDD/EgAgAhBwMFgXB9EPAAAAbBAG2iD7HAAAPRBgMFuMn/ag
+H2IAABKw+jwAAgAAWTD8XAAAIRBoMP8SACABEHAwWBb50Q8AAABsEAbaIPscAAA9EGAwW4yR9qAf
+YgAAErD6PAACAABZMPxcAAAgEGgw/xIAIAEQcDBYFuvRDwAAAGwQBtog+xwAAD0QYDBbjIP2oB9i
+AAASsPo8AAIAAFkw/Qp+IgAAYXD/EgAgARBwMFgW3Rne/yiQfcChCogCKJR90Q8AbBAGaTEFbkQG
+ZEADxirRD9og+xwAAD0QYDBbjG/43yUQABIuoPhICACAEFgw+RIAKAAgWjAigM0JIjbyhM0iAAAS
+sNEP0qDRDwAAAGwQBtog+xwAAD0QYDBbjF8Y3xeJECKAfwqSO/KEfyIAABKw0Q8AAABsEAYCKgL7
+HAAAPRBgMFuMVWageR3d44sQL9DRLdI2+N53FAAQSDD/D0AAARBwMPvdKA4FAH+wD5g5+NMlcAAQ
+WDD4CgogDBBgMA+MOW0ID7HMAMEEAOka+dMHcAICWvBj/+kd3g0v0qzAh/Ld4RgDAELwAIEEAOwa
+8v8BDQAEOyAPzAL81qwiAAASsNEP0qDRDwAAbBAG2iD7HAAAPRBgMFuMMPndvxAAFS6gixApkjYY
+3fgLmSgigrwb3i/5SRQCAEBYsAkiAvKGvCIAABKw0Q/SoNEPAABsEAYY3tvTDw8CACmAAikUAiiB
+ACgVABPd6SMykAODQW8zRNog/Ao9IAgCWHBbjBf83doQACYuoCvC8Y0RoT7+4AAj6BB4MA/dKA7d
+LPsLRw2ABD9gDbsC+8bxIgAAErDRDwAAAAAAAPoKAiABEFgw/N7BEgAAaPBbrKXHK9EPANKg0Q8A
+AAAAbBAMHN67x98tFgQrwgEowgMpwgIpFgIoFgMrFgH8wgAiAABQsPwWACBbEFgwW40C8qwAAWYA
+NqAqoAAuCmB662QvCnp6817TEPcKOiAgAihwhDDaQFuNCvtMAAIAADKw+iwAAgAAYbBbqTHKo7gz
+dTne8goAIAAQGDD5CgEgABBQMPKaOAAAEEAwCpg4zYdgAQeibCvAAPrMAAYBAr7QZb/KYAH8AMAw
+/AoAIF0QaDD0CgAiAABwsPscICIAAFCw/woLIgAAKvDTD236HSqgAH2hH/SgN2ACAmMw+rQAKgAg
+EzD+rAAAAgJa8PwKCyAWAnCw+hwgIAICE7D1yAgAYAJYcPSEACAAEGAwW4u++iwAAD0QWDBbjMjy
+rAAAfQA2oCugAP0KLyAiADbg3KBtCA19sQ0rwAH0sA9gAgJjMGP/65QUYABYAAAAiRRkkFDAwPoK
+CyBAAkhwbaoVKiAA9KARYAICELAqlAD8zAEgAgJKcMDLpcv8CgAgQAJQcPS0ACBoAlhwW4ufCv5Q
+/QoBIAAQYDAO3DhlwLXGKtEPAAD6HCAgABBgMP4sAAALEHgw0w9t+hgr4AB9sRr0sKFgAgJjMCuk
+APLOCAACAlKw/AoLIBYCcLD6HCAgAgITsPXICABoAlhw9IQAIAAQYDBbi4X69lAAAGWuoMDA+goL
+IEACSHDTD22qFSogAPSgEWACAhCwKpQA/MwBIAICSnDAy6XL/AoAIEACUHD0tAAgIAJYcFuLdPsK
+ASAAEGAwBrw49M9WaB8BVDDA0Am9OGTfSck0aDtWweF+MTXAINEPAADz/41v6hBQMBjdY4IcqCIo
+IoCJFP8SDS//EFAwCpkDCYgBCP8C/yaAIAAQEDDRDwAa3UaLHIwUjR1bi1HAINEPscqDMfP90mIA
+ABKwGt1BixyMFI0dW4tKwCDRDwAAAAD6CgEgABBIMAapOGWfgWP+yAAAAGwQBtog+xwAAD0QYDBb
+i0j2oBhiAAASsCgSABveAQgJR/i0fiQSAL5gaJMB0Q/GKtEPAABsEAbaIPscAAA9EGAwW4s7GN34
+iRAign8Kkjvyhn8iAAASsNEPAAAAbBAG2iD7HAAAPRBgMFuLMRjd74kQIoCACpI78oSAIgAAErDR
+DwAAAGwQBtog+xwAAD0QYDBbiyf2oBRiAAASsIkQG93gCQhH+bR/IgwAviDRD8Yq0Q8AAGwQBtog
++xwAAD0QYDBbixsY3dqJECKCfwqSO/KGfyIAABKw0Q8AAABsEAbaIPscAAA9EGAwW4sRGN3NiRAi
+gH0KkjvyhH0iAAASsNEPAAAAbBAI+iwAAD0QWDBbjBXzrAABPgA2oPfc0hAAECAw9t1JECwQKDD7
+CgAgABBgMPwWBSIAAFDw8ABCYAsQSDAAABndQQQfFAf/Ci3yrvo8AA//EHAw/pgDAAICITD4EgUs
+AEBHcPsKACwJAG7w/fauJgBwESD5Cgsg2AC2IN4QbZodLaAA9dEfcgAAevD00LhgAgJa8C3kAPO6
+CAACAnOw/woLIBYCUPArHBDx/ggAABBgMPOsASAAEGgw/eQAIgAAUHBbitrSoPcgWGIAAFDw2RD/
+CgAgCxBAMG2KFC2gAMnW/ZQAIAICe/D6rAEgAgJKcP8KCyAWAlDwKxwQ8f0IAAAQYDDzrAEgABBI
+MPnUACIAAFBwW4rF9qA1YgAAErDA4Z4VGt0BjhQqon8OqygsOugMuyx7Yxr53MIT/4L9EPP/AGsA
+BD7gAPP/e2/qEBAw0Q8f3Xb83XYQARBYMAr/LP1MAAAEEFAwW6tRG9z2Y//FAAAAbBAOG91v+hwA
+AEgQYDBbo+L6LAAAPRBYMFuLtPKsAAB8ADagKKAA0w9kgG/3CgAgkAIwcPAADWIAABhwAAAAALgz
+djFWhDDaQFuLuvtMAAIAACqw+iwAAgAAYXBbp+Flr92iXCrAAMLc/aE3cgAAWzBlr8v5MgEgJAA2
+4C6wANKw9e+vZgkAPnAvGgx/cAwS3U33Jn8gABAQMNEPxirRDwAAAADz/8xgAgJbMGwQBPosAAA9
+EFgwW4uN8qwAABsANqAT3UHaMFuLm9yg+zwAAgAAULBbp8PIosYq0Q8Y3Tsa3Twign8Z3NUKIgEJ
+IgLyhn8gABAQMNEPAAAAbBAWG901/Ap4IgAAUHBbo6P6LAAAPRBYMFuLdfSsAACAADagKKAADwIA
+DwIAZICp+QoAIPACMHD5FiAgLBA4MPAADWIAABBwAAAAALgidiFOgyDaMFuLePs8AAIAACqw+kwA
+AgAAYXBbp59lr92kXCrAAPehWXIAAFswZa/N+SIBIB4ANuAsEiAqsAD8nAICAAAi8PwWIC+oALag
+YAA/AMYqZiAyGt0NLqF+fewJLaV/0Q8AAAAAAP6hfyABEFgw/N0IEAQQUDBbqt3GKtEPAPP/qmAC
+Alsw0Q/A8C8WIC0SIPP/vGAAEBAwAABsEBYb3Pr8CngiAABQcFujaPosAAA9EFgwW4s69KwAAHwA
+NqAooADTD2SAf/kKACDwAjBw+RYgICwQODDwAA1iAAAQcAAAAAC4InYhToMg2jBbiz77PAACAAAq
+sPpMAAIAAGFwW6dlZa/dpF0q0AD3oTFyAABbcGWvzfkiASAeADbgLBIgKrAA/JwCAgAAIvD8FiAv
+qAC2oGAAFQDGKtEPAAAAAPP/0mACAltwwNAtFiAe3M8a2/L8EiAgABAQMPzlfiFAEFgwW4sd0Q9s
+EATAINEPAGwQBi0gAPs8AAIAAGEw+lwAAD0QeDD/0SlwABAwMMU7c9Ef3iBtCBX00EhgAgIxsC3g
+Af/RDHACAnOwc9EEY//jAAAX3LibEvwWASAAEBgw+hYAICACIfAlcn/bIPxsAAIAAFFwW6cwyKy4
+d/R56HAQAhjwxirRD9pQW4r/dqnpGtteGNypqjp4oeiLEiitFCiCMYwR/RIAIgAAULALgADSoNEP
+bBAG3ED6IAAiAABpcPIWAyAAEDAw/jwAAD0QeDD/oS5yAAAYsMUrcqEk2zAPAgDTD20IFfSgSGAC
+AjGwKrAB/6EMcAICWvByoQRj/94AABfcjp4S/BYBIAAQEDD9FgAgYAIp8CRyf9sw/GwAAgAAUTBb
+pwTIrLh39XnocBACELDGKtEP2kBbitN2qeka2zIY3H+qKnih6IsSKK0UKIIljBH9EgAiAABQ8AuA
+ANKg0Q9sEAbcQPogACIAAGlw/wo9IgAAcPDyFgMiAAAYsP+hLHAAEBAwxUt0oSLbMA8CANMPbQgV
+9KCNYAICELAqsAH/oQpwAgJa8HShAmP/3hXcZZ0SnBGeEBbcYvAAD2AAEDgwALhm9WFdcBACOfAk
+Yn/bMPwsAAIAAFEwW6bWZa/i2kBbiqhyqdoa2wcY3FeqenihMosQabEUjBEtOv99yQwe3FMv4oAu
+4n+v7p4RixAorRMoghmMEf0SAiIAAFDwC4AA0qDRD8Yq0Q8AAABsEAbcQPogACIAAGlw/wo9IgAA
+cPDyFgMiAAAYsP+hKXAAEBAwxUt0oR8DOwJtCBX0oERgAgIQsCqwAf+hC3ACAlrwdKEDY//jABXc
+N54SFtw1nBH9FgAgABA4MCRif9sw/CwAAgAAUTBbpqfIrLhm9WnocBACOfDGKtEP2kBbinZyqeka
+2tUY3Cmqenih6IsSKK0SKII1jBH9EgAiAABQ8AuAANKg0Q9sEAQa2tsZ3CAsokr6okwgzBBoMA0t
+KPTaxhA0EFgw8so4CAAgbnD5kOUodwFQMAuIHPPcHxQAIEEw9EIAIMoANmD/AgAAAGEGYP8CAAIA
+XQJg/wIAAgBZBmD/AgAEAFUCYBPcCgrZQciZaJEKaZINYAAHAAADMxQDQxQDUxQDCklbku8KDF/7
+3AISAABQsFqMnBvbTgPaFNMPC6oCW5LoCgxf+9v8EgAAULBajJUDqkJbkuMKDF/72/gSAABQsFqM
+kPvbAhvwBDkgW5Oo3KD72/MSAABQsFqMihvb8fosAAAKEGAwWoyGG9vv+iwAAAoQYDBajIMb2+z8
+2+wSAABQsFqMf8Ag0Q9j/1kAbBAIF9qYHtvoL3JKJ3JMLuIA8vc4ACACaHD3FgYmPQE8MP7WACwA
+IG3wLdAAG9vfHNvg+N0RAgAAULBaiqgU290T2931294QABAwMPosAAIAAFkwWoqDCuhB+ilBAhoA
+uiBvkgVvogJusgHAYfosAAIAAFjwWop7JE0C8z0CIZoIKTD6TkIMQAFQMPrPQgxIAVQw/903DAEA
+czANzDf728gcACBhsPzMAyIAAFCwWoxR+9vEEgAAULD8CgcgARBoMFqKhvvadRIAAFCw/AoHIAEQ
+aDD4HBQgChBwMPgWACABEHgwWopkyaHAovzbtxAIEFgwW6luxyvRDwAA+9uyEgAAULD8CgcgAhBo
+MFqKc/vaYhIAAFCw/AoHIAMQaDD5HBQgChBwMPkWACABEHgwWopSyK/AovzbpRAIEFgwW6lbxyvR
+D4sW+tuiEMwQYDD8LCgAABB4MP4KgC3ABD3g/KoIDOABbDD+qQgKOwFcMP2UziAZADbg+LEtYAEQ
+YDBosi5oszYskM1gAAsAL5TO/JDNIgAAa/Cuq/uwzCIAAFCwW5LU0qDRDyyUzCyUzWP/5S+UzCyU
+zWP/3AAAL5TML5TN8//RYgAAY/AAAGwQCPvbgRIAAFCw/AoBIAEQaDBaij/7230SAABQsPwKASAA
+EGgw/gpkICACQHD4FgAgFBB4MFqKHcmjwKL823QQCBBYMFupJscr0Q8AAAAA+9tuEgAAULD8CgEg
+ABBoMFqKK/vbahIAAFCw/AoBIAEQaDD+CmQgIAJIcPkWACAUEHgwWooKyabAovzbYhAIEFgwW6kT
+xyvRDwAAAAAAAAD8218SAABQsP3aZxAgAlhw+xYAIMgQcDD721gQChB4MFqJ+sivwKL821YQCBBY
+MFupBMcr0Q/AINEPAABsEFrTIPvbOxIAAFCw/QoAIgAQYDBaigce20wf2zwc20wd20oa203720sQ
+BRBAMCgWkisWjSoWnS0WmywWnC8Wmf4WmiBAAnhw/xaeIIACcHD+Fp8gwAJgcPwWoCD+Akhw/R0B
+IGICSnApFqEa2z0b2zsrFo8qFo79FqIgABBIMPkWmCCgAmtwLRaQYAEnAHiiAdLgwKX82zMQCBBY
+MP1MAAIAAHCwW6jX3UD6CgUgCBBYMPzbLRAMAhCw/iwAAgAAeLBbqNAkEo36PAACAABZMFqJuvIF
+RgAIEFgw+gJHBYAEPWD82yISCQAosPoKBSIAAGiwW6jD3CD6PAACAABZMFqLkiISmCUSjiYSj/cS
+kCIAAFDw+xKdIAAQYDBai4sqEqIpEqEsEp4uEp8vEqAtEpwrEpsoEp0kTQImbQIlXQL1Fo4gAgIQ
+sCIWmPYWjyAgAjnwJxaQJBaNKI0CK70C/d0CICACe/D/FqAgCAJzsC4Wn/0WnCAIAmMwLBaeKxab
++BadICACSnD5FqEgIAJSsCoWoikSmSoSmigSkimdAiqtAvoWmiH+AkIwKBaS+RaZIm4ANiD7Epki
+AABQ8FqJfyQSjiUSjyYSkCcSoiwSnisdAS0SoC4SoS4WqP0WpyD+Alrw+sYAIEICYvD8FqUgYgJa
+8PsWpiAAEBAw+jwAAgAAWXBaiW0qFqT7TAACAABQ8FqJai0SpCgSpy4SqA2fRv+GACzBAWww/eYA
+KAEAa/D9+TYOyQFQMP52ACrBAVAw+us3DAMAU7D8EqYoAwBmcPsSpSgBAFowmmCYwJmwHNrEmROY
+FC0WAP4WASAIEFgw+hYCIgAAcLD9EpggBRBQMFuoYPRMCCAQAilw+RKoIAgCMbD4EqcgCAI58P8S
+piACAhCw/hKlIAgCSnD5FqggCAJCMPgWpyAIAnvw/xamIAgCc7D+FqUl/5oYoPsSmiIAAFDwWok5
+0qD7EpsiAABQ8FqJNvsSnCbIARAw9hZwJMABFDD1FnEkyAFQMPQWciLAAVAw8hZzIgAAUPBaiSv9
+bAACAABxcP9MAAAEEDgw8hYAIAgQWDD82pMSyAFQMPIWASAFEFAwW6gzKhKYIxaM0w/6rPwgAhBw
+MArnOPYK/yAAYaXgIhaL9B0BIP8QMDDzHQEg/gIhMPM8fyBCAiEw9HQKAEICKPD0FqkgABA4MPQ8
+MSCCAhjwj0CNUI4w8hKLLAAgf3ANHRT+2AwABRBQMPgiCAIAAHiw/NpzEsABEDDyFgAgCBBYMFuo
+EigSqfYmNAYAgDyw8zwEIAgCKXD4WbVwCAIhMCMSjPkSnyQAIDHw9BQUD/oQEDD0TPogGhBAMPSW
+ACqAASAw/qzgK/5IlqDyCgUn/kcKoPP8hmIAABKwAADz/8JgABA4MAAAAADaMCoWjAERAlgBG2ak
+FSoSjBvaUhzaU1qKxSoSjBvaO/wKASABEGgwWoj6KhKMG9o4+B0BIAEQYDD4jH8gABBoMP4KZCCi
+AkIw+BYAIBQQeDBaiNdlo9AqEowb2iz8CgEgABBoMFqI6yoSjBvaKfkdASABEGAw+Zx/IAEQaDD+
+CmQgogJKcPkWACAUEHgwWojIZaPSG9oiHNoiHdkrKB0B+hKMIP4CQjD+CsggogJCMPgWACAKEHgw
+Woi9ZaN5GtoLGdoLH9oo+NodEAAQaDD9FoogABBgMCwWhCgWlS8Wo/kWhSBAAlhw+xaUIP4CcHD6
+FoYggAJYcPsWkyDAAlBw+haXIGICc7AuFokpHQEe2gwpFoj+FpYgoAJKcCkWhyMShyQSiCUSiSYS
+lycSliISlYxgLVIAKhKM/90RDXAEOyD9zAICAABYsFqKeIxAjTAqEoz/3RENcAQ7IP3MAgIAAFnw
+Wopy/hKjIAgCGPD0TAQgCAIpcPZsBCAQAhCw/imvcBACOfAlEoYjEoQkEoX6EowiAABZcFqIgdag
++hKMIgAAWTBaiH382fASAABCsP4SlCpIAVQw+xZ9KCoBNDD5FoEmJgE0MPcWgiIuATAw8haAJkAB
+VDD3Fn8iAAB5sP7iACIiATAw8haDJkwBUDD2FnwiRAFQMPIWfigqAXQw+RZ5Ki4BcDD6FngoIgF0
+MPkWeyomAXAw+hZ6IgAAaPD4FgAgBRBQMFunb/sSfSH4AkDw/woCIAQQUDD4+jgAABBwMP8dASAA
+Jiag/R0CIP4Ce/D5/GEg4gJ78G2qKIuQ0w8PAgD0sRZgCAJKcP8CAAAArgbg/wIAAgC9BuCx7v/8
+BCAIAmtwJxJ/KxJ9IhJ+JhJ8HNm2+hKML4AEOuD8LREPQAQ9oP19Ag4JAHuw/t0CAgAAWTBaiFct
+EpON0PzZrBAgAmtw/RaRLCUBbDD6EowvwAQ/YPTYEQ+ABDtg+O4CDAkAf3D+3QICAABZcFqISSYS
+hycSiCISifzZnhAFEFAw/RKRIAgQWDBbpzYpEpQqEpMrEpcoEpYvEpUuEqMlXQIkTQIkFoX1FoYg
+IAIxsPYWhyAgAjnw9xaIICACELDyFokgAgIY8CMWhC7tAi/9AviNAiAgAlrw+xaXIAgCUrD6FpMg
+CAJKcCkWlCgWli8Wlf4WoyX+1RzgIhKK0Q+G0GhjIv8CAAP/cpmgi/DNufoKAiAIEFgw/Nl4EgAA
+aPBbpxHHK9EPi/DIuLC8nPBj/soAAAD6CgIgCBBYMPzZcBIAAGjwW6cHxyvRDwCI0P8CAAP/UR4g
+i/Blv876CgIgCBBYMPzZZxIAAGjwW6b+xyvRD4rQZa6Ei/D/sw9gAgJi8JzwY/51AAAAAAAA+goC
+IAgQWDD82VwSAABo8Fum8ccr0Q8A0qDRD8Ci/Nk6EAgQWDBbpuxgAAzAovzZOhAIEFgwW6bo/NlQ
+EAIQUDD7Cggv+xBoMP0WiiAFEGgwW6bhIhKK0Q8AAMCi/NkrEAgQWDBbptxj/80AbBAs+9k4EgAA
+ULBah8b72TUSAAAasPzZPxIAAFCwWomlG9k9HNgrHdgq8xY+IgAAULBah9omCgD5HFAiAABQcPXZ
+NxAAEFgwFNk2E9k2KxY9KhY8KRY72iD7TAACAABhsFqJlNog+zwAAgAAYbBaiZEkTQLzPQIhvggp
+MAIqAlv9g9Og9hZOIAN2LqAjEjskEjwW2SQX2SInFksmFkwX2SIW2SL7EksiAABQsFqHnZpA+xJM
+IgAAULBah5r6NgAiAABZ8PwKACIAAFCwWol52iD7bAAAABBgMFqJdiZtAikSTCgSSyd9AimdAviN
+AiAgAhjw+BZLICACITD5FkwhSggqMCYSTvvZChIAAFCwWoeFKxI9KRI7KhI8HdkGtJn6rAQgAgJa
+8P1mCAX/jprgIRZGHtj8H9j6GtkA+hZJIAAQSDApFkcvFkP+FkIggAJocP0WPyCgAmBw/BZFIHgQ
+YDAjEkYrEkWEMYYwLrIA9odGBMgBJDD3FjAkwAEgMPQWKS7IAXQwLxYs9RYxLsABcDAuFjQlMgL+
+sgImwAEwMCYWKPMyAybIASgw9QVGDsgBdDD1FiouwAFwMP4WNiTIARgw/xYuIsABHDDzFisg/gJQ
+cPOyASCCAlKw9hYyIAAQeDD7sgMgBBAwMPQWMyzIARww/RYtIsABHDDzFjUsyAFcMP0WLyrAAVww
++xY3IAAQIDBtag2OoG/oAbH/fssBsUS0qsn8yUoqHH/6rEEgBBBAMG2KDY6gb+gGKwqAq+uboLSq
+9goAIAAQKDDzGgAgABAgMP9s/yAAEDgw+hx/IAAQcDD6rEEgBBBoMG3aHchhf+EQK6IADwIACzM0
++1UIBACAWTD+7AEgCAJSsPNNDAAIEFgwfbp2sWb/AgAEAmCVoCUKAPQKACABEHAw/hY5IQAQGDAv
+EjkPAgBl/5UpEkYoEj//AgAL/01KEBvYkxzXgPosAAAAEGgwWocw+9iVEgAAULBahw8b2H/8Ej4i
+AABQsFqI8BvYexzXJvosAAAAEGgwWocmwCDRDwAAAAAnFjn6CgEiAABJsPapOQAEEEAwCYgMCFUs
+9GAtZMABLDAuHf7+7H8h/gJ5sPwSSSCCAnOw/m4KAAUQUDD+4n8iAABpcFumBiwKeMDw+hx/IAAQ
+IDD6rCEgBBBAMG2KDY6gb+gBsf9+ywGxRLSqyfzJSiocf/qsISAEEFgwbboNjqBv6AYrCoCr65ug
+tKr2CgAgABAgMPMaAC2ABD1g/RZBIAAQKDD/bP8gABA4MPocfyAAEHAw+qwhIAQQQDBtih3IYX/h
+ECuiAA8CAAszNPtVCAQAgFkw/uwBIAgCUrDzTQwACBBYMH26HLFm/wIABAGnlaDAkfUKACAAECAw
+8AEDYQAQGDAA+AoBIgAAebD2jzkABBBwMA/uDA5eLPcWTS7AAXAw/hZEIC0ANaAuHf78Ekkg/gJz
+sP0SRCBCAnOw/m4KAAUQUDD+4n8h/gJ5sFulwywKeC8SR2v0VMDg/xx/IAAQIDD//DEgBBBAMG2K
+DYrwb6gBse56ywGxRLT/yezJSi8cf//8MSAEEEAwbYoNivBvqAYoCoCoqJjwtP/AYPUKACAAECAw
+8ACAYQAQGDAtEkEsEkTaIPsSQywJAGswWoh5LhJH/Ap4JAEoI6ApEk0vEkYoEkUqEkMtEkIrEkcq
+rQIt3QL9FkIgAgJa8CsWR/oWQyAgAkIw+BZFICACe/AvFkZlnp1j/ZuxZv8CAAQA/BWgwFD0CgAg
+ARBwMP4WQCEAEBgwLxJAZP+D92z/IAAQUDD/HH8gABBwMP/8MSAEEEAwbYoeyGF34RGI8NMPDwIA
+CDM0+FUIBACAQTD+7AEgCAJ78PNJDAAIEFgwebKYKhZA+QoBIgAAQbD2mDkABBAYMAgzDANTLPRg
+KmLAARwwLh3+/ux/If4CebD8EkkgYgJzsP5uCgAFEFAw/uJ/IgAAaPBbpWr/CgAgeBBQMPwcfyAA
+ECAw/MxRIAQQQDBtihaOwA8CAG/oAi/8Af6rBnAIAmMwsUTTD/MWOCAlADfgyU0sHH/8zFEgBBBo
+MG3aEI7ALQqA/+gGbAAgb7CdwLTM/h3+IAAQKDD+7H8gABAgMPMaACCiAnOw/hZKIAAQMDDwAERh
+/gJ5sAAAAPgKBCoFADMwCogMCFgsCAhG+BY6IBoANaAuEkr8EkkgBRBQMP7ifyIAAGowW6U8LxI6
+LxZI9H6vYf4CebD+CgAgABA4MPwcfyIAAFGw/MxRIAQQQDBtih/IYX/hEo3ADwIADwIADTM0/VUI
+BACAaTD+7AEgCAJjMPNODAAIEFgw/wIAAAEQYDD/AgAJ/7p20C8SSvcKASAAECgw8xoAIAAQIDD2
+bAEgCAJ78P8WSiQSAL2gY/9+AAAc1479EkcgAhBQMFulF8cr0Q8c14r9EkcgAhBQMFulEscr0Q8t
+EjgsEkgPAgD43RECAABQsPsSQiwJAGswWofc8/2UYHgQYDAc1379EkcgAhBQMFulBccr0Q8c13r9
+EkcgAhBQMFulAMcr0Q8AAAAA/Nd2EAIQUDD9Ej0gCBBYMFuk+dIw0Q8AbBAKE9Xl0w8oMkonMkwb
+120W1dEChzj3d1MANBBAMAh4HBzWHf3XaBYAIEGw9mIAIgAAULBahfYb12Uc1hf6LAAAMhBoMFqF
+8hvXYRzWE/3XCRIAAFCwWoXu+9deEgAAULD8CgIgAhBoMFqF6RvXKhzXK/osAAAAEGgw+RwQIAoQ
+cDD5FgAgARB4MFqFyMmiwKL811EQCBBYMFuk0ccr0Q8AAAAb1y38100SAABQsFqHnhvXS/osAAAA
+EGAwWoea+TKHI5QANKD5q1IKXQFIMAuqDLWqG9dDHNdE9K0RAAYQcDD+3QICAABQsFqFyRPXLBTX
+KhXXPtog/AoAIgAAWTBah4naIPs8AAAAEGAwWoeGJE0C8z0CIb4IKTAb1zX81lUSAABQsFqHgG53
+EvvXMRIAAFGwW46X8AARYgAAGrD71y4SAABRsFuOktOg+9crEgAAULBahZDVoPvXKRIAAFCwWoWN
+1KD71ycSAABQsFqFihvXJfzXJRAZEHAwBe4M/j43DAAgVTD93AYgHAJzsP/dEQ+ABDug/t0CAgAA
+ULBahZv71xcSAABQsFqFe8CyW45406D71xYSAABQsFqFdsCyW450/tWrHAAgVPD71xEQBAJrcPzX
+EB3QBD9g/t0CAgAAULBahYr71wcSAABQsFqFadSg+9cJEgAAULBahWbToPvXBxIAAFCwWoVj+j4I
+ABACaTD+1cscAQB3cBvXAfzXAh2gBD9g/t0CAgAAULBahXcb1v781v4SAABQsFqHOhvW/PzW+xIA
+AFCwWoc2+9brEgAAULBahVArCgJbjk1bjjNbjiLVoPvW9BIAAFCwWoVJwLJbjkdbji1bjhzUoPvW
+7xIAAFCwWoVDwLJbjkFbjidbjhbToPvW6hIAAFCwWoU9wLJbjjtbjiFbjhD71uUdwAQ44PhOEQ1A
+BD1g/t0CDAkAYrD9zAICAABQsFqHFvvW3RIAAFCwWoUvKwoCW44tW44TW44B1aD71tgSAABQsFqF
+KcCyW44nW44NW4371KD7CgIgGRBQMFuOIluOCFuN9tOg+woCICgQUDBbjh1bjgNbjfL4TRENQAQ5
+YP3MAg3ABDzg+9bGHAkAazD6LAAMCQBisFqG+PvWrRIAAFCwWoUR06D71rISAABQsFqFDqo6+qz+
+IAIQWDBbjgpbjfBbjd7ToPvWtxIAAFCwWoUGwLJbjgRbjepbjdgb1rP0PRENgAQ6oP3MAgIAAFCw
+Wobi+9auEgAAULD8CmAgYBBoMFqFFxvWqhzVL/3VLhIAAFCwWoUTFNZaE9amFdZaFtam2iD81YgS
+AABZcFqG0tog+0wAAAAQYDBahs/aIPzVBhIAAFjwWobMIz0CJV0C9E0CIZ4IMXDaIFv9GmaiffvW
+PhIAAFCw/AoBIAEQaDBahPz71joSAABQsPwKASAAEGgw/hwQIBQQeDD+FgAgZBBwMFqE2sqnwKL8
+1ogQCBBYMFuj48cr0Q/5S1IKVwFIMAuqDPP8bmAKAlKwAAAAAAD71iYSAABQsPwKASAAEGgwWoTj
++9YiEgAAULD+CmQgARBoMPwcECAUEHgw/BYAIAEQYDBahMLJpsCi/NZxEAgQWDBbo8vHK9EPAAAA
+AAAAAPvWFhIAAFCw/NYVECACaHD9FgAgyBBwMP3VGxAKEHgwWoSyyaHAovzWYxAIEFgwW6O8xyvR
+DwAAF9ZhH9Sn/NZeEAgQcDD+FgkgBxBoMP0WCiAAECAw/BYLIAAQGDD/LwoAGBAwMP8WByAFEHgw
+/xYIIBUQKDCLF9MPK7KKAGAE+wsZD/8QYDD81k4aEQBm8AsLQvsWBSoAIFzw/LsKAgAAULBahI3c
+oPsSCyIAAFCwWoZt/NZEEAUQUDD+EgkgCBBYMP8SBSIAAGkwW6OVixcrsooAUAT7CxkP/xBgMPzW
+OhoRAGbwCwtC+xYGKgAgXPD8uwoCAABQsFqEd9yg+3wAAgAAULBahlj81jAQBRBQMP4SCiAIEFgw
+/xIGIgAAaTBbo38nfQL8EgggAgIhMPgSCyH0Ailw/hIKIfQCMbD9EgkggBB4MPiNAiIAIHzw+BYL
+IfwCc7D+Fgoh/AJrcP0WCSH+AmMw/BYILwsAtyAb1cr6LAAACBBgMFqGO/vWFBIAAFCwWoRUyKz7
+1hISAABQsFqEUWWqRPvWEBIAAFCwWoRN+9YOEgAAULBahEv71c0SAABQsFqESPvWCRIAAFCwWoRF
+2iBb+lXSoNEP0qDRDwAAAGwQChfUQClySihyTBTULgKYOPh4UwA0EEgwCYgcqESEQPvV+xIAAFEw
+W401/NX6EAUQMDD6ZjcAChBAMAhmNghjEPvUMhwJAGDw/BYEIgAAULBahhAb1C0c1C76LAAAABBo
+MPgcECAKEHAw+BYAIAEQeDBahCn81egSQgC2oPvVuxIAAFCwWoQf/dQiEfYCKrAJXBEMPAL71B0c
+CQBrMPwWBCIAAFCwWoX7G9QYHNQY+iwAAAAQaDD4HBAgChBwMPgWACABEHgwWoQTZaINHNQS+9QO
+HAkAYPD8FgQiAABQsFqF7BvUChzUCvosAAAAEGgw+BwQIAoQcDD4FgAgARB4MFqEBWWh9GQhmCty
+SypyTBzUZx3VwvkcICg+AVgwCYgKhdOO0Y/Sn5KekfWWAyoAQGaw/dIAIAAQUDD9lgAgABAoMPiC
+ACABEEgw+5o4AAIQSDAKlTn91bIUCQBFcAxcEQw8AvvT6RwJAGsw/BYEIgAAULBahccb0+Uc0+X6
+LAAAABBoMPgcECAKEHAw+BYAIAEQeDBag+BloYL71aISAABQsFqD19Wg+9V0EgAAULBag9T/AgAK
+AGORYCVc/PxVEQQAZ5agwIh6iwf/AgAKAG/GoMCgB6kR+BoAJAkATXD91ZEUCQBFcAxcEQw8AvvT
+xxwJAGsw/BYEIgAAULBahaUb08Ic08L6LAAAABBoMPgcECAKEHAw+BYAIAEQeDBag71loRn7ahoi
+AABRMFuMtPvVYBIAEGAw+iwADAEAUzBahZQb07Ic07f8FgQiAABQsFqFkPvTrRIAAFCw/NOsEAoQ
+cDD9HBAgARB4MP0WACAAEGgwWoOoZaDowCDRDyVc9PxVEQAEEEAw+FUCBf+cxqDAiP8CAAv/mlYQ
+8/82YfgCUrArckkqckpj/mMAAMCyW4yTY/8eAAAAAAD2FgAgAhBQMPsKCCAAEGgw/goAIAAQeDBb
+op3HK9EPAJYQ/NVOH9AEOWD6CgIgCBBYMP0KAyACEHgwW6KVxyvRD5YQ/NVGEAIQUDD7CgggAxBo
+MP4KACADEHgwW6KNxyvRDwCWEPzVPRIAAHFw+goCIAgQWDD9CgMgARB4MFuihMcr0Q+WEPzVNRIA
+AHFw+goCIAgQWDD9CgMgABB4MFuifMcr0Q8AAAD81S0QAhBQMPsKCCAFEGgw/goAIAAQSDD5FgAg
+ABB4MFuicccr0Q8AbBAKF9NdKnJKJXJMFtNLAqU49XhTADQQSDAJiByoZiNiACt60Po8AASFASgw
+W4xR+9UcEgAAYrD2FgYiAABQsFqFMRzVGPvVGBIAAFCwDDwsWoUtG9UW/ArIIgAAULBahSob1RP8
+KgAiAABQsFqFJvvVDRIAAFDwW4w+G9UOCqwK/8wRAgAAULBahR8Y1QsERAvTDwhECvtCfyIAAFDw
+W4w1HNUGBQtC/LsKAgAAMrD7sn8iAABQ8FuMLteg+0KFIgAAUPBbjCsrQoOaGfcWBSIAAFDwW4wn
++kKHIgAAOrBbpFr8rAACAABq8PrU9RAAEFgwW6L0W6NN+6wAAgAAUPBbjBwtCgQH1zcHewJbjBn1
+uFEEIAC+oPAAFWAAEFAwAAAAAMDh+goCIfYCerAP6jj+qhACAdGGIBvU4g8CAAuqAhvUX/zU4BAI
+EGgw+iwADAkAbrBagyb71N0SAABQsPwKASABEGgwWoMh+9MAEgAAULD8CgEgARBoMP4cECAUEHgw
+/hYAIDIQcDBagwDJo8Ci/NTPEAgQWDBbognHK9EPAAAAABvUy/osAAAIEGAwWoTVG9TJ+iwAAAEQ
+YDBahNIb08L8OgAiAABQsFqEzvvURhIAAFCw/AoCIAIQaDBagwSMFizCAR3UviwWBwxsDPvUux2A
+BDsg/cwCAgAAULBahMEb1Lj6LAAAABBgMFqEvh7UtfzUthxUASwwDt0cG9S0/cwMA+gQaDANzCz8
+FggiAABQsFqEtIUXG9Su+iwAAAQQYDBahLCNFfosAAAkEGAw+9RzHAEAazBahKvaIPvUbxIAAGGw
+WoSoG9Qa+iwAAAIQYDBahKQb1GL6LAAAABBgMFqEodog+9SEEgAAYbBahJ3aIPvUUhIAAGFwWoSa
++0KAIgAAUPBbi7L71JMQDxBgMPosAAwBAFMwWoST+0KBIgAAUPBbi6v71FoQFBBgMPosAAwBAFMw
+WoSL3GD71E4SAABQsFqEiNxw+9SEEgAAULBahIX71IISAABQ8FuLnfvUPhAEEDAw+iwADAEAUbBa
+hH371HwSAABQ8FuLlfvUNBAGEGAw+iwADAEAUzBahHb71HMSAABQ8FuLjgpsN/vUKRIAAFCwWoRv
+G9Rv/CoAIgAAULBahGz7QoQiAABQ8FuLhPvUahADEGAw+iwADAEAUzBahGT71GYSAABQ8FuLffvU
+ZBAKEGAw+iwADAEAUzBahF371GASAABQ8FuLdYQY+9QiEEAQYDD6LAAMAQBTMFqEVfrS5BIAAFkw
+W4ttG9RX/KwAAgAAULBahE/71C0SAABQ8FuLZ/vUURAFECAw+iwADAEAUTBahEf71CYSAABQ8FuL
+XwpMN/vUShIAAFCwWoRBiBkkCgMPAgD4RDcCAABQsPvURBIAAGEwWoQ6+9Q5EgAAUPBbi1L70+wQ
+DBBgMPosAAwBAFMwWoQzsUz71DsSAABQsFqELxvUOfosAAAAEGAwWoQs+9Q2EgAAULD8Ch8gABBo
+MFqCYRvUM/osAAABEGAwWoQkGdI/+ZKHIGcANKD5qlIKXQFMMAq7DLW7BbsIC6oI+qz/IAIQWDBb
+izTcoPvUJRIAAFCwWoQWG9Qk+iwAAAAQYDBahBPaIPvUIRH+Amlw/R0UAB8QYDBagkfAINEPAB7U
+HPP8Y2oJAHKwAAAAAAD5SlIKVwFMMAq7DPP/m2AKAlrwAGwQBCs66As7KBrSkAsbFFuLGvKqKAPo
+EFgwW4sXW4sL0Q8AAGwQEPrSFRIJADSgJqJMKKJLKBYVBnVTZFIF/wIAAAEFBWBuUg/AnXWTCvAA
+CWABEDgwAAAAwHIT0/0U0/3aIPzT/RIAAFjwWoPoIz0CdDnsGdIe/dP5EgwBPWDdkBvT9/zT9hIA
+AFCwWoIZE9HrDHoRqjonotApotEootIqotMb0/D8iBEJYAQ+YPN3EQvgBDqg+XcCCAkAUjD80qYW
+CQBF8PosAAIAAGnwWoIJLhIV/9JVEAEQUDDTD/5uUQYMAL1gwKAb0+D80psQNBBAMAhVHCYWEfjT
+3BQAIB1wJRYThVL47REOBQBWMP4WECwJAH9w/RYUL/AEOWD+CkAsCQB3cP7dAgIAAFCwWoHxKRIV
+89PPGGMBTDD0WBEFgAQ+YPbTzBQJAEVw/VwAAgAAULD80x0SAABY8FqB5SM9AnY55hPTxBXTxdxw
++iwAAgAAWPBag6UjPQJ1OewnEhTz078QQBBAMPXTvhYJAEXw3HD6LAACAABY8FqDnCM9AnU57MCh
+W5sYG9O3HNIf+iwAAAAQaDBagc/zCgAgMhAoMMGkW5sQsTN1OfUb07Ac00j90rYSAABQsP4KZCAg
+Akhw+RYAIBQQeDBaganIr8Ci/NOnEAgQWDBboLLHK9EPG9OlHNOl/dIIEGQQcDD6HBAgFBB4MPoW
+ACIAAFCwWoGcy6HAovzTnRAIEFgwW6ClxyvRDyaiSiuiSSsWFWP99wAAAADz/hNgABA4MPP+C2AA
+EDgwAAAb05L805ISAABQsFqDahPTkRXTkdog/NOOEgAAWPBag2UjPQJ1OewnEhMPAgCHcPrTihIA
+AFnwW/9cG9N+HNGx+iwAAAAQaDBagZUb03/8020SAABQsFqDVxPTfvzSKRIAAFCw/dNnEgAAWPBa
+gYwjPQJ1Oeb6CiAiAABZ8Fv/SxPTXtog/AoAIgAAWPBag0ojPQJ0Oez6CiAiAABZ8Fv/QhnRYSUS
+FQ8CACmShwV1Q/koUQgwAUww9FHHaAUAFjAqEhBkpgn/AgAAAyuGoP8CAAIDUAKgKxIVCwpC+hYO
+LEsBXDAtFg/93QkMPgFYMP0SECgAIE9w+qoJCnIBXDD7Fg0qACBisPkWDCIDTIdg/NNTEAgQWDD9
+XP8h/gJysP88AAAFEFAwW6BRJBYW/RINIgAAeTD800sQBRBQMP4SDCAIEFgw8xYLIf4Ca3BboEgU
+00X6EhYgABBwMP4WEiOQBD3gW6Jl/KwAAgAAavD7EhIiAABRMFug/luhWMGPCjks+hILKAEAyjAo
+FBRbolv9vAACAABisPsSEiIAAFEwW6D0wU9boU0KOiwrHBb7FgoqAQDRMPoUFSAoAhhwHNGFJDAA
+G9Mp/EwCAgAAULBagvn7fAAH0BBQMFv+9BzTGdog0w/70yEcCQBhMFqC8vvTIBIAAFCw/AowIDIQ
+cDD9HBAgFBB4MP0WACAwEGgwWoEKyaPAovzTFxAIEFgwW6AUxyvRDwAAAAAb0xD6LAAAABBgMFqC
+4PvTDhIAAFCwWoD5/hwUKmABUDD6FgQgEghw8JoYYAAdFNLxjRT8CvAiAABQsPzdEQIAAFkwWoEM
+JE0CdknkjhqxM/8CAA//oPDQYAAQLxIVDwhC+BYOLksBfDAvFg8pEhEJOUFklSv/AgAAApgGYP8C
+AAICmQJg/wIAAgKahmD1FgcgABAYMBrS7hbS7hXS7vTS7xAAEFgw8wxDAA8QaDArFhf6FgYmAcZv
+ECsWF/oWBiIB1gcgJE0CJV0CJm0C+q0CIA8QaDDzQxQAAgJa8PMMQwWaAj7g+gogIgAAWfBb/qUT
+0tgU0tr6LAACAABY8PwKgCCAEGgwWoDbIz0CdDnm+gogIgAAWfBb/poT0s78CoAiAABQsP0KACIA
+AFjwWoDSIz0CdDnm+gogIgAAWfBb/pAd0K/TDy3ShxvSxv2uUgxUAWww/NH9HAUAF7D43RECAABQ
+sFqAxBvSvxzSBv3ROxIAAFCwWoDAE9K8FNK82iD80VYSAABY8FqCgSM9AnQ57Pt8AAAgEFAwW/56
+LRITLdILG9K0HNK0+N0RAgAAULBagLElEhMPAgCFXBPSr/TSsBWABD1g/VwAAgAAULD80qoSAABY
+8FqApyM9AnQ55vvSqBIAAFCw/AoQIBAQaDBagKEU0qUT0qUZ0qaMF4gf+woCIAEQaDD83DkAABBQ
+MPbSnxoFAGLwCogKCYgKJYJ+iRiaGfiCfyXABD1g9JsRC4AEOmD8lxEKCQBasPiIEQYJAFXw+FUC
+BgkATfDccPosAAIAAFkwWoJN2iD7PAACAABhcFqCSiRNAvM9AiG+CDEwE9KJFNKJ3FD6LAACAABY
+8FqCQyM9AnQ57BvShfzRPRIAAFCwWoI+G9KC/NE5EgAAULBagjob0oD80TYSAABQsFqCNxPSfRTS
+fdog/NJ9EgAAWPBagjIjPQJ0Oewb0nj80lISAABQsFqCLRPSdhTSd9og/NJzEgAAWPBagigjPQJ0
+OeyDGRvScfzSSBIAAFCwWoIjKBIOGdJtA4gKCYgKJYJ+KIJ/E9Jr/FURCYAEOiD00mkUCQBFcNog
++zwAAgAAYXBaghYjPQJ0OewpEhUY0mIJiVIMmRGpiCSCfiiCfxPSX/xEEQmABDog9tJdFAkAQTDa
+IPs8AAIAAGEwWoIIIz0CdjnsE9JYFtJY3FD6LAACAABY8FqCASM9AnY57BPSUxXSVNxA+iwAAgAA
+WPBagfsjPQJ1OewqChRbmXcqChRbmXYqCgJbmXQoEhUlEhET0kkPAgD1BVMIWwFAMPOJEQlwBDog
+/FURCAkASjD00kMUCQBFcP1cAAIAAFCw/NJAEgAAWPBagB8jPQJ0OeYqEhXTD9MPCupD89I6GUAE
+PqD4pREJwAQ6oPqIAgQJAE1w9NI1FAkARXDaIPxcAAIAAFjwWoHWIz0CdDnswCDRDwCLFvzQpRIA
+AFCwWoHQ2iD80ioSAABZsFqBzdog/NIoEgAAWXBagcpgACcAixb80TcSAABQsFqBxdog/NB3EgAA
+WbBagcLaIPzSHhIAAFlwWoG/2iD7TAAAABBgMFqBvIoWKxIXY/wbJBIV+NIXGkABIDCaHvqqCQo+
+ASQw+9ISGgAgWrAFUwkKMwoLMwrzMn8qSwEkMPsWDyRyASAwlB0Luwn0RAkKACBecJscC0QKCEQK
+JELtY/ntJBIV+NIFGkABIDCaHvqqCQo+ASQw+9IAGgAgWrAFUwkKMwoLMwrzMn8qSwEkMPsWDyRy
+ASAwlB0Luwn0RAkKACBecJscC0QKCEQKJEJ/Y/mcKxIV++xRCkABWDD6Fg4sSwFcMP0WDypyAVww
+mx0N3Qn6qgkMACBPcJ0c8/lvagAgYrCZHJsdGNHmFNHkBVMJCjMKBDMKIzJ/C7QJCUQKCEQKJEJ/
+Y/lFE9HflRdj+uuVF/P65mP/EBgwlRfz+txgPxAYMACVF/P60WAPEBgwAABsEATIJ2ghBcYq0Q8A
+ABbPiRvR0ShiSiViTPkKASAAEBgw8pM4AgAAULDzhTkABxBgMFqBZCliSihiTPTPbhgFABpw+HhT
+ADQQSDAJiByoRIRB+kz8IAIQWDBbiHUpYkooYkz3z2QYBQAacPh4UwA0EEgwCYgcG9G5/tG6FgAg
+RfD3cgEh/gJJMPU/QQgAAUww9a0RCPAEPmD1CEIGAAE8MPqIEQdgBD3g+d0CBgkARfD//xEMCQA/
+cPzRqRwJAH9w/t0CAgAAULBaf3jAINEPAAAAAGwQBhbQOfvPVhIAAGiw9CCmYIAQYDD0BQYAAE8E
+oB3QOyrSgSjSfi7SfwOiDAJSAX4jfPmw0SB5ADYg8taBKAkAZnD5tNEiAABQsBzPYy/SgJMR9BYC
+IAAQWDD6FgAgAhBoMPIWAyAFEFAwW55PzyYqYrX0oggAABBoMP9itCH+AhCw+GKyIgBAEXDzKwgC
+AAAqsPvzcXIAAFGwZIBp2mDwAD9gABBoMNEPAAAAAAAA8/+XYAAQEDAN2goGqgolorX0AgYIACAh
+cP+itCH+AkIwCCIBoyn+orIqABdP0MrmaNIsojUlprUcz0kuorOVEJMR9BYCIAAQWDDyFgMgBRBQ
+MFueKdEPAPP/3GAAEBAwL7DRDP8CL7TRL6K0Y//DAGwQBBTPufPRWxAAEBAw2iBbmoAKCEFpgSH8
+rx5yAABI8AAEiwBJYQBJYQBJYQBJYfosAAAAEFgwW5pxsSLzPQQpmgI4oMAg0Q8AAABsEATyAkcA
+DRAYMPMhHXAOECAwdCEV+AqBLiQAOKD5CocgFARAsHkhAtEPABvO8iqw0cDIDKoCKrTR0Q9sEAQW
+zx4GJgslYuAY0TkENxH3RwIEAEBFcAdVAiVm4NEPAABsEAQWzxUGJgslYtAY0TAENxH3RwIEAEBF
+cAdVAiVm0NEPAABsEApbmlQX0Bzzz7MQABBIMPIKASIAAHKw9tEkEAsQKDD6NDQgCBBAMG2KEgCQ
+BA4KG/+nB3ACAlpwK3Z/sZn0CgAiAGj/kMAg/wIAAgDS+5D/AgACAVb3kP8CAAIBx/OQ/wIAAgI6
+75D/AgACAnLrkP8CAAICqueQeOdjKhok/M9zEBIQWDBbmo/6Zd4gFRBYMPx6/yE0EFAwW5qKCqsC
+/CwAAAcQUDBb/8wqYd4KC0P+Cg0mAsOG4P8CAAYCv/bQW/+uK2HeCwtD/jA0IA4EKvBpugosYEHB
+0A3MAixkQRzQ9C1yfyR2gCswNZsQKjA2mhEpMDf5FgIiAAB5MPgwOCAAEFgw+BYDIAQQUDBbnarR
+DwAqCpz8z00QDhBYMFuaaRzPSvsKHiIAAGqw/WXXITgQUDBbmmMiND38ev8gERBYMPo0NSAAEHgw
+/zQ5LuABUDD+FgQgrBBQMFuaWvwKACIAABKw+goAIgAAWLBb/5v6GkQgAgIQsPx6/yAEEFgwW5pQ
+KDA11aD0CgAgJwA2ICowOdtQ+koIAgAAYLBb/4YrMDX0TAEgAgJhcPtD4XIAIGCwKmHX9BIEKmAB
+VDD1CgsmAkqG4MDd/wIABgJF7tBb/2orYdcLC0P/AgAGAkUu0P8CAAoCQQLgLjA0Y/5YAAAqCrD8
+zxUQChBYMFuaMRzPE/sKAiIAAGqw/WXYIUgQUDBbmizAvfo0Nif/EGAw9DQ6LuABVDD/RAgAAhBw
+MP40PSDAEFAwW5oi+hYFIgAAWrD8LAAAARBQMFv/ZCgSBfsKCCFQEFAw+IwBJ/8QYDD0FgQiACBA
+sFuaFikwNtWg9AoAICcANmAqMDrbUPpKCAIAAGCwW/9MKzA29EwBIAICYXD7Q+FyACBgsCph2A8C
+APQSBCpgAVQw9QoLJgHlBuAtCg3/AgAGAd/u0PoKRwANEHAwfqEULwoOf6EOaK4LKAqBeKEFKQqH
+eakKKmBBwMgMqgIqZEH/AgAGAa8u0P8CAAoBqwLgLjA0Y/1SAAAAACoKxPzO0RAGEFgwW5ntHM7P
++woGIgAAarD9ZdkhVBBQMFuZ6MC5+jQ3J/8QYDD0NDsu4AFUMP9ECAADEHAw/jQ9INQQUDBbmd76
+FgYiAABasPwsAAACEFAwW/8giBb7CgwhXBBQMPiMASf/EGAw9BYEIgAgQLBbmdIpMDfVoPQKACAn
+ADZgKjA721D6SggCAABgsFv/CCswN/RMASACAmFw+0PhcgAgYLAqYdn0EgQqYAFUMPUKCyYBZwbg
+wN3/AgAGAWJu0Fv+7Cth2QsLQ/8CAAYBYa7Q/wIACgFdguAuMDT/AgAB/jzzkCoK2PzOlxACEFgw
+W5mzHM6U+woKIgAAarD9ZdohYBBQMFuZrcC1+jQ4J/8QYDD0NDwgBBB4MP80PS7gAVAw+groJAAg
+cTBbmaT6FgciAABasPwsAAADEFAwW/7liBf7ChAhaBBQMPiMASf/EGAw9BYEIgAgQLBbmZgpMDjV
+oPQKACAnADZgKjA821D6SggCAABgsFv+ziswOPRMASACAmFw+0PhcgAgYLAqYdr0EgQqYAFUMPUK
+CyYBAgbgwN3/AgAGAP1u0Fv+sith2tMP0w8LC0P/AgAGAPqu0P8CAAoA9oLgLjA0/wIAAf3J75Aq
+Cuj8zlsQHhBYMFuZd/pl2yABEFgw/Hr/IPwQUDBbmXL6FggiAABasPwsAAAEEFAwW/60jBgqYdv8
+zAEqYAFUMPwiCAYA1IbgLQoN/wIABgDPbtBb/pMrYdsLC0P/AgAGAM6u0P8CAAoAyoLgLjA0/wIA
+Af2R65AqCvz8zj0QGhBYMFuZWfpl3CAdEFgw/Hr/IQwQUDBbmVT6FgkiAABasPwsAAAFEFAwW/6W
+jBkqYdz8zAEqYAFUMPwiCAYAqIbgLQoN/wIABgCjbtBb/nUrYdwLC0P/AgAGAKKu0P8CAAoAnoLg
+LjA0/wIAAf1Z55AqGhD8zh8QFhBYMFuZO/pl3SAZEFgw/Hr/ISAQUDBbmTb6FgoiAABasPwsAAAG
+EFAwW/54jBoqYd38zAEqYAFUMPwiCAYAfIbgwN3/AgAGAHfu0Fv+Vyth3QsLQ/8CAAYAdy7Q/wIA
+CgBzAuAuMDRj+jwsYEHA0g3MAixkQWP6di5gQcHwD+4CLmRBY/yfKGBBwJIJiAIoZEFj+2oAACpg
+QcGwC6oCKmRBY/tzAAAsYEHA0g3MAixkQWP8NgAALmBBwPIP7gIuZEFj/TEAAChgQcGQCYgCKGRB
+Y/06AAArYEHAwgy7AitkQWP9+wAALWBBweAO3QItZEFj/ggAAC9gQcCCCP8CL2RBY/5XAAApYEHB
+oAqZAilkQWP+YAAAK2BBwMIMuwIrZEFj/q8AAC1gQcHgDt0CLWRBY/64AAAvYEHAggj/Ai9kQWP/
+BgAAKWBBwaAKmQIpZEFj/w8AAGwQBhbPYvfNEBAAEBgw9c8EEfQQIDDaMFuWkv9w0SAAIK6g8s7+
+EIIA//BkMHYpckoockwDmDj6YpkgABSyIGSgCwM6AluWbmagFGAAAcCg+wrMIAICGPD5MrpmACBZ
+sNKg0Q/AINEP2jD8z0kSAABYsFp+2SItAnUp7BLPRtow/AoAIgAAWLBaftMcz0MiLQJ8Kekbz0L8
+z0ISAABQ8Fp+zmP/iB/M4C/ywJ8QHszgwN795kAgARBQMFuWRhjM3CIKD/KGQCAAEBAwwaRblkGx
+InQp9YkQCelR/wIAA/+oHmAbzNMazOMqtkBj/z8AAGwQBBnM1RzPKxbMwPiQ0SABEFAw8woAIBQQ
+KDD0Chgv/xA4MPKcAAIA7v4QI8YfI8Ye88SAIDAQcDArIXQfzx4LXEb/zwoKACjzEI/wCvAAAABv
+OxopYoQAMQQAqBoAiBEHjAMMmQEJiAIoZoRgACQsYsDB5wPuDADhBACpGgedAw3MAQyZAilmwGAA
+BwDaMFuYD8ChKyF0B7wD9MsBDgAJJxBouFJouzf/AgAEAKojEPIsAiACAhjw/wIAADAQcDD/AgAP
+/7qo0PcKhyAAEBgw8syhEA4QWDDwAFVgFBAgMC9ixwAxBACrGge5Aw+fAQ+/Ai9mx2AACAAxBACr
+Gge5AyhixgmIAQi4AihmxmP/nwB7qQz6PAAAARBYMFuXn8C+8zwBIf4CITD0QLdgBAIQsCohdHeh
+FwpaRmiiLGiqWf8CAAYARa6QaaHFYACAAC1ilh7O2v7MwRwAQHdwDt0CLWaWY/+6AAAAANowW5fK
++woOL64AtqAtIXQZzs74kIAsQAFsMPDRBAABEGAwAMwaDIgCKJSAY/+HANowW5e++woOL34AtqAq
+IXQbzGsKCkKrqyuw5AsLRFuXjB3OvsDPLNSA8/9aYA4QWDAAAAAA+jwAAAEQWDBbl3Dz/0NgDhBY
+MAAAW5jPW5jFH86yKvYeK/YfLmKH/maHIAAQEDDRD8Yq0Q/AINEPAAAAAAAAAGwQBhbM3w8CACZh
+whjM3AdmEfPMlhYAIEGw+s6lEAEQWDD8CgEgChBoMPgqACAAEHAw+DY6IAAQeDBafiwZzEIokNLy
+zpwSAAAhsPfOmxABEFgw+rg7AAAQKDD4lNIgAR8uoJYQi0KKQSs2O4lAKjY8KTY9Gs6P+woBIAEQ
+YDD/VhEAChBoMPdoAgAAEHAw+DY6IAAQeDBafhVmof/6zoQQARBYMP0KCiAAEHAw8mwCAAAQeDD8
+NjogARBgMFp+C/ah2WACAilw+V6ZYBgCITAtEgAXznoSznr93QYgABAoMP0WACAA3C6gDdQCi0KK
+QSs2OylCACo2PCk2PRrObfsKASABEGAw/1YRAAoQaDDyaAIAABBwMPg2OiAAEHgwWn3zZqF3+s5i
+EAEQWDD9CgogABBwMPdsAgAAEHgw/DY6IAEQYDBafen2oVFgAgIpcPlemGAYAiEwLRIAFsv+/d0G
+IAAQKDD9FgAgAJmuoA3UAo1EjEMtNjuLQiw2PIpBKzY9iUAqNj4pNj8azkn7CgEgARBgMP9YEQAK
+EGgw9ogCAAAQcDD4NjogABB4MFp9z/ag5WACAilw9EwUL2oCOWCEEPbOQBAAECgwLU0KjdQsTQqM
+wy02OytNCouyLDY8Kk0KiqErNj0pTQqJkCo2Pik2PxrOL/sKASABEGAw/1gRAAoQaDD2iAIAABBw
+MPg2OiAAEHgwWn219qCFYAICKXD0TBQtTAI5YIQQ9s4nEAAQKDAtTQwt0iQsTQwswiMtNjsrTQwr
+siIsNjwqTQwqoiErNj0pTQwpkiAqNj4pNj8azhT7CgEgARBgMP9YEQAKEGgw9ogCAAAQcDD4Njog
+ABB4MFp9mvagDGACAilw9EwULUICOWDSoNEP0qDRD9Kg0Q/SoNEPAAAAbBAG2iBbaA6UEBzMifsK
+CCIAAGiw/zwAAgAAMrD6CgUiAABxsFuasMBwFcwKpWUjVoEkVoInVoMH5BYBAgDaYPhSgyACEFgw
+Wh+HI1KI0w8PAgDIOfpsAAACEFgwWh+C/SwAAgAAcPAH5Bb8zHIQBRBQMP9SiCAIEFgwW5qbwCDR
+DwAAbBAEE83n/8vqEAEQcDD9Ch4gBBBIMPgKICIAAFDwbYoKK6AFeyACf7c1vKoay3z88IAgABBY
+MA8CAP6k3CAAEBAwbZoYKKDk+NMQcAICUrAAsQQA6RoJzAIs9ICxu9EPjqAczdD94AAgCBBYMP7g
+ASACEFAwW5p72TD/CiAg/hBQMG36DSiQBQqIAfiUBSAYAkpwxyvRDwBsEAYoIAD5CoIiAABQsPIt
+BCAUBEowxy7RDwAAJ6wW/wIACgDzkdAUzbuaEBPNuZMRE8258AB2YEAQKDAAAAAAALwzdDFVKDAF
+BYgB+TAELeICPiB2menacPsyACACEGAwW5cE/AoAL+oQWDAKyzhmv86NESwxA4gy2mD7fAMsACBr
+MAuAAPahkmABEHgwLjAF0w8P7gIuNAUmcALzzZ8QBgJBsKh3/wIACgCwEdADOwL6fAAAAhBgMFuW
+7vPNkxAKADagJnACY/97iRC0ev8CAAoAoVJQCXoMtKr6C0MAABBIMG25DosQLLAAsbv7FgAoACBP
+MApLFGSxCIgQLYAELIAAKoABL4AC/oADKAAgTzD8gAUoACBOsPqABi4AIE/w+YAHLgAge7D/gAgs
+ACB3cP6ACSwAIGsw/YAKKgAgYrD8gAsoACBWcPqADC4AIE/w+YANLgAge7D+gA8sACB3cP2ADiwA
+IGsw/KoIACACQjD6mQgB/gJS8G2pgvmAACwAIEtw/IABKgAgY7AtgAL5gAMuACBScPyABC4AIHMw
+/YAFLgAgc3D5gAYuACBycPyABy4AIHMw/YAILgAgc3D5gAkuACBycPyACi4AIHMw/YALLgAgc3D5
+gAwuACBycPyADS4AIHMw/YAOLgAgc3D+gA8oACB2cPnJCAAgAkIwCdkIqekayuAppN3EoFv/VtKg
+0Q8A8//sYAAQSDCOMBzNP/3gACACEFAw/uABIAgQWDBbmeZj/mEAbBAEHMtlK8J/LMKA+csNH5AE
+OuD+zAgAGhBoMC3EBJvAKJKDGs0xCogCKJaDW5NYwCDRD2wQBBjNLRnNLRrNLRvNLvIKACAGEGAw
+DAk/Ago/Agc/Agg/Ags/CwQ/CgU/CQY/+IJ/IIAQUDAIAD9bg4wezSIKAT/y5H4gCBB4MP/kfCAB
+EGgwLeR9W5N+W5Nb0Q8AAGwQBhrLP9MPIqJYwEj8y2EQABB4MP76/yFrADSg9vwAAAAQSDD4CgAg
+ABAoMG0IWSeini2ipqdnCXIRot0l1RMr0RIu1HaZ35jcJtQiL9QNJtQM9NQFIAwQEDDy1AQgARAY
+MCPUIJfQI9Qhh9uD3iKiWPZsASQAIF1w94gICAAgHnD81S4qAAORkGP/nyuiWWSwrBXM9v0SAC//
+ECAw+woAIAAQMDDwAExggBA4MMDRme8m5CIt5A0k5Az45gwvgBAYMCPkISPkIPPiCy//EBAwIuR2
+gu7zolkoACAaMPu8/yACAmtw9mwBKAAgFnD85S4qACcZkCKiny6ipqJiCSMR8woNLgAgG7Aj5ATy
+5gAgCBAQMPLkBS+YALbgsVWnWyuwzfW/h2ACAiEwbQgOsVWnWyuwzfW/dWACAiEwY//qLKJay8n0
+yscQABAwMPgKByAOEDgwbQgkIqKgLqKmomIJIxGj7pLgL+QNKOQMJ+QELaJasWb05S8qAAPpkGP/
+1MAg0Q/Z8PP/A2AAEEAwAABsEAb7y18QARBQMPzLXhAAEGgw/8yzEAEQcDBbkzkayssTyukby1cZ
+ysosMl0tMlwuMlspNp0pNp8pNqEpNqMvMlorNpwrNp4qNpsqNqAoMlkoNqoqMl8rMl74MmAuACBH
+8CkyYy82q/8yYi4AIHuwLjas/jJhLAAgd3AtNq39MmssACBrMCw2rvwybCoAIGbwKzav+zJtKgAg
+WrAqNrP6MnEoACBWcCk2sPkycigAIEowKDay+DJzLgAgR/AvNrH/MnQuACB7sC42tP4ydSwAIHdw
+LTa1/TJ3LAAgazAsNrb8MngqACBm8Cs2t/syeyoAIFqw+ja4KAAgVnApNrkaypD6NpooACBKMCg2
+uvnKkh4AIEfwLza7+TaiLgAge7D+NrwgABBAMPg2qSwAIHdw/Ta9LAAgazD8Nr4qACBm8Cs2v1uS
+6ysyoy8yovj6gCD+AhLw+a0RAgBAQLCi2vkyoCoAI1fQy576NqMiAABasBzKGS4yofIWAyAFEFAw
++xYAIIAQQDD9FgEgABBYMPgWAiABEGgwW5j28jbAIBMANKDAINEPAAAA8//EYAAQEDDHJNEPbBAG
+FMnc+snIEAAQKDD2yokQARAYMCWmgSWmgyWmgCWmgihA0SkKEPmmiiCAAP4wG8xBLWJ/GMw+/8pC
+EGoQcDAO3SgeykIP3SwvgoDAzP3MNw4AQHfwD88CL4aALbKADt0BDcwCLLaAK7KAG8rZ/MwyEAAQ
+UDBb/hfAofzKRxAAEFgwW/4TW/rfGcnr0w8okoX/QNAgIBBYMPv6/SgJAFow+JaFIAQQSDD4zCQS
+PgC74G2aFymCkCqNBAuZASmGkCmikCiNCAuZASmmkB3MHMfPLNYgLNYhLNYiLNYjLNYkW5VUH8nh
+wIgq8o/6CUMKYAFQMPpE0CgJAEZwKUTQLvKKG8qk+QqILgkAQ7D+9oogABBAMG2aDQuJCyWWQPWW
+QSACAkIwG8ndEswGJbaQI7aRIyaAW/p/LCKAHcwD0w8NzAIsJoBb/TlmoUVb/LP+Yn8gAJ+uoPnJ
+6BD/EDgw8hqQIAMQeDAC7iwP7iwqQob+lrwiQAFQMPQhomJKAVQw/wIAAADxhKD/AgAEAO2EoP8C
+AAAA6YTg/wIABADlhOBuJAUKOEYoFgBuNAQK2UeZEhnJXfmSjyIA5hig/wIAAgCyAOBlMdT5a1MA
+ABBQMPoWASvwBD7gKkKJALAECgoZCgpBW5P7yjL/AgACAKmA4P8CAAYA4Bjg/wIABAB1QKCMEosQ
+/wIADgBvYtDAoGaggFv8RmagetpA/QoEIIAQYDBt2gysqyuwlPe5X3ACAlKwKQoALEDRf8dbHcvE
+HsmuLebALebAwCDaIFuSobEiaST1L0DR/wIAAABef9AZykYokoEquv4KiAEoloFbkk3AoGagHRzJ
+UCXGcivCdsfbDbsB+8Z2IAAQEDDRDwt5FGP/odKg0Q8fy64uYn8P7iwu7P4OHhT77hEACBB4MPpA
+5C4JAHJw/8mQHgkAe7D+9sAkGAA6oP8CAAP/vp6gGcmKKJLAxqL6CgUoAEBSMAqIAiiWwGP/X40R
+ghL8CgEg4BBQMP8iEQ3ABD9g+ywADOABbDBbkzn5rAAAQwA2oGee+5kTYAC7W5IoZ69QY/9SZD7q
+YADP+EKIKnYBTDAPuxEAsAQICBkICEGYEWP+mQAAihFbkq9j/sQAAAAAAPoK4CIAAFiw/AoDIDAQ
+aDBbkyLz/6piAABKsN0g/jwAAAIQUDD8y3gQCBBYMFuYEPP+jm/aEFAw/SwAAAIQUDD8y3MQCBBY
+MFuYCWP/4QAA/TwAAAIQUDD8y24QCBBYMFuYA2P/yQAA/TwAAAIQUDD8y2kQCBBYMFuX/WP/sQAA
+AAAAAAD9PAAAAhBQMPzLYhAIEFgw+Q4GAgAAEnBbl/Tz/h5iAABQsP08AAACEFAw/MtbEAgQWDBb
+l+1j/gEAAGwQChPI2Sgw0RLLVfUyRCBqAH4wJSZ+HMkeGctSKiJ+KSaA/MKOI+gQWDALqiwqNjYL
+zCgsJn8LzCz8NjUgABAQMNEPACoayPwK/yAPEFgwW5Sa+woXIgAAIrD6GsggAxBgMFuUlsG5/Aof
+IgAAMrD0FgghyBBQMFuUkMG+/AofIgAAIrD2FgchyBBQMFuUi8C4/AoDIgAAMrD0FgYhzBBQMFuU
+hvsKFSIAACKw+hrUIAEQYDBblIHBtvwKASIAADqw9BYFIdQQUDBblHwdyyccyyb0okdhgAIjcARu
+CS7hftMPDl4s9TJEIbQQUDD+HxQAHhBYMP8mfiD/EGAwW5Rv+woGIgAAOrD6GrggAxBgMFuUasC4
+/AofIgAAMrD3FgQhuBBQMFuUZcC9/AofIgAAOrD2FgMhuBBQMFuUYMG3/AoDIgAAMrD3FgIhuBBQ
+MFuUWvoWCSAEEFgw+hrEIAEQYDBblFb7CgUiAAA6sPoaxCABEGAwW5RRZKGBBGsJK7F+DwIAC1ss
+FchlKyZ/LFKzLfrADcwB/FazIAEQUDBbkdUuUrPB8A/uAv5WsyABEFAwW5HQHshaGshcLVKyDt0C
+LVayLAqALFauKVKvG8hVC5kBKVavHchVjqmIqo+njKb2iBEOIAQ7oPv/EQ4JAEOwD+4CDswCDcwC
+LFawG8hMK1axKFKyi6WNqBzIS4qk890RC4AEPuD5yEcaCQBu8PyIAQoJAFqwCogCCYgC+FayIAEQ
+UDBbka8oUrIZyEH6yD8QEBBYMP0KeCAQEGAw+YgBABQQcDD4VrIiAAB4cFp6K2Sg2cCi/Mg3EAgQ
+WDBbl1Abyr8pUq8sUrD6UrIgfgCicP8CAAAAaz5QLSJ//yJ+KBYBVDD4MkQj6BBwMA7dLA7/LP82
+NioFAE4w/TY1LeAEOuD8JoAgABAQMNEPKTJE/iJ+KIABYDD0iAkMFgFUMPiBfioFAG5w+SJ/I+gQ
+aDAN7iwIuCwNmSz+NjYp4AQ6ICgmgPk2NSAAEBAw0Q/AkQeXOWRxA/8CAAAAewXg/wIAA/81AeDz
+/m5gABBYMMCxB7c5ZHFG/wIAAABnBeD/AgAD/tSB4PP9rGAAEHAwwaRbkWwsUrPC39MPDcwC/Faz
+IBQQUDBbkWdj/xUAH8qDJTJE+odEDuABUDD/7gkGFgFQMPzvUQoFADVw/xYBJioBYDD64X4kgAFk
+MFuZDR7KeY0RDt0J/dF+IAAQYDBbmDodynYNfQn90X4gABBgMFuYRB3Kbg1tCf3RfiAAEGAwW5hA
+BF0J/dF+IAAQYDBbmDwuIn//In4p4AQ64PgmgCPoEBAwAu4sAv8sLzY2/jY1IAAQEDDRDwAA8/zq
+YgAAcXDz/YRiAABZcAAAG8pXihQLqgn6oX4iAABZcFuY6R7KVY0T0w8O3Qn90X4gABBgMFuYFh7K
+UY0SDt0J/dF+IAAQYDBbmB8eykmNGdMPDt0J/dF+IAAQYDBbmBoEbQn90X4gABBgMFuYFmP9HAAA
+ihgMqgn6oX4iAABZcFuY0R7KPY0X0w8O3Qn90X4gABBgMFuX/h7KOY0WDt0J/dF+IAAQYDBbmAce
+yjGNFdMPDt0J/dF+IAAQYDBbmAIEbQn90X4gABBgMFuX/vP8GmIAAHLwbBCGwKD7CgggDxBgMFuT
+dxjKJwGiCouBjIAsFgArFgH4ggIiAABqsPgWAiIAW5agIiIA+EoAIABUrKAbyh1yikwssoAYyhwr
+soHyzBoAIAJQcCymAPK7GgB/EEgwbZoa+YKAIBACQjD7pgEgEAJSsCuCfwSZjpmgBLuO+6YBICAC
+UHBb/CT3oE5iAAASsBvKCiyygBjKCiuygfLMGgAgAlBwLKYA8rsaAH8QSDBtmhr5goAgEAJCMPum
+ASAQAlKwK4J/BJmOmaAEu477pgEgIAJQcFv8ENKgx858IRbRDwDRDwDAovzJ9hAIEFgwW5Z8xyvR
+D8Ci/MnzEAgQWDBblnjRDwBsEAQZyfAWyIMqkoEqZqb5koIgBBBQMCpmgipmgSpmgCpmkCpmifpm
+jCAIEBgw82Z0IAAQQDAoZp8pZqf4x1YQARBIMClmjhLHbBvHbSOA0SIigf7HaxQAEGAw+TQCDAAB
+HDD9yzkAGgC8sCtlftEPAAAAAPQCQAQAEHgw9ITRLgUAE/AuZX7RDwAAAGwQBBzJ0BjJzR7Hqv3J
+zRAEEFAw+oSCIAAQWDArhUAthh7+hh8ggBB4MFuWScAg0Q8AAABsEAQcycUaycUYycIqxn/6rUAg
+iBBYMPqGfyAAEGAwW4+l0Q9sEAYax777rPQgrAA8oGgibokQ9JB5YAICEnD+Ij9gARAgMGAADgAA
+AAAAaWQDpaiXgHJLKPo8AAIAAFkwW5Uy+GESYAICITD5Yt9oACAusCeVAGP/26WrJ7QAY//T0Q8r
+ongpooCjuwm7Efhha2gAIF5waGJqaWTkpZyXwNEPAPhANGACAhEwckOpY//PAAAsoniEESmyg6PM
++cwRAAICITD4YR1oACBmcGhiHWhkInJDgWP/pwAAGcfeqTkpkOVj/0+lmiekAGP/5KWbJ7UAY//c
+pZyXwGP/1aWdJ9QA0Q+lniflANEPIAKRLAzAAAIgBbo0IAKRMAjAAAggBbo0IAKRNDzAAAwgBbmE
+IAKROAbAADQgBbuQIAKRPAjAADggBbo0IAKRQALAADwgBbuQIAKRRAjAAEAgBbo0IAKRSAhAAEgg
+Bbq8IAKRTAhAAEwgBbq8IAKRUAhAAFAgBbq8IAKRVAhAAFQgBbq8IAKRWCBAAFggBbq8IAKRXCBA
+AGggBbq8IAKRYCBAAHggBbq8IAKRZCBAAIggBbq8IAKRaCBAAJggBbq8IAKRbCBAAKggBbq8IAKR
+cCBAALggBbq8IAKRdCBAAMggBbq8IAKReAxAANggBbkEIAKRfAxAAOQgBbkEIAKRgAxAAPAgBbkE
+IAKRhAxAAPwgBbkEIAKRiAxAAQggBbkEIAKRjAxAARQgBbkEIAKRkAxAASAgBbkEIAKRlAxAASwg
+BbkEIAKRmAhAATggBbq8IAKRnAhAATwgBbq8IAKRoAhAAUAgBbq8IAKRpBBAAUQgBbq8IAKRqBBA
+AUwgBbq8aW5vZGVfZmMtPmZsb3djX2ZsYWdzIFsweCV4XSwgaW5vZGVfZmMtPmZsb3djX2J1Zi0+
+Zmlmby5udW1fYnl0ZXMgWyV1XQoAAAAAAAAAAABhdXRoZW50aWNhdGVfdGFyZ2V0OiBLRVlfQ0hB
+UF9SRVNQIC0gWzB4JXgleCV4JXgleCV4JXgleF0KAAAAAAAAYXV0aGVudGljYXRlX3RhcmdldDog
+S0VZX0NIQVBfUkVTUCAtIFsweCV4JXgleCV4JXgleCV4JXhdCgAAAAAAAGF1dGhlbnRpY2F0ZV90
+YXJnZXQ6IEluY29ycmVjdCBwYXNzd29yZAoAAAAAAAAAAHZhbGlkYXRlX2tleXZhbDogS2V5IENI
+QVBfQSwgdmFsIFslZF0KAAAAAAAAAAAAAHZhbGlkYXRlX2tleXZhbDogS2V5IENIQVBfSSwgdmFs
+IFslZF0KAAAAAAAAAAAAAENIQVBfQzogZGlnZXN0IGV4cGFuc2lvbiBlcnJvcgoAdmFsaWRhdGVf
+a2V5dmFsOiBLRVlfQ0hBUF9OQU1FLCBsZW4gWzB4JXhdCgAAAAAAQ0hBUF9OOiBUYXJnZXQgdXNl
+cmlkIG1pc21hdGNoCgBDSEFQX1I6IGRpZ2VzdCBleHBhbnNpb24gZXJyb3IKAFJlY2lldmVkIEF1
+dGhNZXRob2QgdmFsdWUKAAAAAAAAdmFsaWRhdGVfa2V5dmFsOiBmbG93Y19mb2lzY3NpX2Nvbm5f
+ZmxhZ3MgWzB4JXhdCgAAAAAAAAAAAAAAAAAAAHZhbGlkYXRlX2tleXZhbDogZmxvd2NfZm9pc2Nz
+aV9jb25uX2ZsYWdzIFsweCV4XQoAAAAAAAAAAAAAAAAAAABOZWdvIEZCTCBbJXVdCgAAS0VZX01B
+WFJDVkRTTCBbJXVdCgAAAAAAAAAAAAAAAABEdXBsaWNhdGUgaXNjc2kga2V5CgAAAAAAAAAAAAAA
+AGZvaXNjc2lfcGFyc2VfcmN2ZF9wYXJhbXM6IGVycm9yCgAAAAAAAAAAAAAAAAAAAGZvaXNjc2lf
+cGFyc2VfcmN2ZF9wYXJhbXM6IHJsZW4gJWQKAAAAAAAAAAAAAAAAAGFzeW5jX3BkdTogbG9nb3V0
+IHJlcXVlc3RlZCBibG9ja2luZyBzZXNzaW9uCgAAAGFzeW5jX3BkdTogc2Vzcy9jb25uIGRyb3Ag
+cmVxdWVzdGVkIGJsb2NraW5nIHNlc3Npb24KAAAAAAAAAAAAAABsb2dpbl9yZXNwOiBmbG93Y19m
+b2lzY3NpX2Nvbm5fZmxhZ3MgWzB4JXhdCgAAAABsb2dpbl9yZXNwOiBmbG93Y19mb2lzY3NpX2Nv
+bm5fZmxhZ3MgWzB4JXhdLCByYyBbMHgleF0KAAAAAAAAAAAAaVNDU0kgU2VjLXBhcmFtcyByZWNl
+aXZlZGhhdmUgZXJyb3JzISEKAAAAAAAAAAAAVGFyZ2V0IG1vdmVkIHRlbXAuIGNvbm4gJXgsIHNl
+c3MgJXgKAAAAAAAAAAAAAAAATG9naW4gRmFpbGVkISEuIGNvbm5fZmMgWzB4JXhdLCBzZXNzX2Zj
+IFsweCV4XSwgc3RhdHVzX2NsYXNzIFsweCV4XQoAAAAAAAAAAAAAAABsb2dpbl9yZXNwOiBzZXNz
+X2ZjLT5mbG93Y19pZCBbJXVdLCBjb25uX2ZjLT5mbG93Y19pZCBbJXVdIGxvZ2dlZCBpbgoAAAAA
+AAAAAAAAAFByb3RvY29sIEVycm9yIGNiaXQgJWQgdGJpdCAlZCBjc2cgJWQgbnNnICVkCgAAAGNv
+bm4gdGlkIFsweCV4XSwgcnhfZGF0YS0+c2VxIFsweCV4XSwgcnhfZGF0YS0+bGVuIFsweCV4XSwg
+cnhfZGF0YS0+c3RhdHVzIFsweCV4XQoAAAAAAAAAAAAAAAAAAGNza19mYy0+Zmxvd2NfY3NvY2tf
+b2Zmc2V0IFsweCV4XSwgZGxlbjE2IFsweCV4XQoAAAAAAAAAAAAAAAAAAABhY3RfZXN0OiBmbG93
+Y19mb2lzY3NpX2Nvbm5fZmxhZ3MgWzB4JXhdCgAAAAAAAABhY3RfZXN0YWI6IHRjYl9mYy0+Zmxv
+d2NfYnVmIFsweCV4XSwgdGNiX2ZjLT5mbG93Y190eXBlIFsweCV4XSB0Y2JfZmMtPmZsb3djX3N0
+YXRlIFsweCV4XSwgbnBhZ2VzIFsweCV4XSwgZmxvd2NfdHBfc25kX21heCBbMHgleF0KAAAAAAAA
+AAAAAAAAAABhY3RfZXN0YWI6IGF0aWQgWzB4JXhdLCB0aWQgWzB4JXhdLCBvcCBbMHgleF0sIHJj
+dl9pc24gWzB4JXhdLCBzbmRfaXNuIFsweCV4XSwgY3NvY2stPmZsb3djX3N0YXRlIFsweCV4XSwg
+dGNwX29wdCBbMHgleF0sIHRjYl9mYy0+Zmxvd2NfaWQgWzB4JXhdIAoAAAAAAAAAAAAAAAAAY3Nr
+X2ZjLT5mbG93Y19jc29ja19jb29raWUgWzB4JXhdIAoAAAAAAAAAAAAAAAAAbDNpbjRfZGV2X2Nv
+bmZpZzogd3ItPnBhcmFtLnZsYW5pZCBbJXVdLCBsMmRldl9mYy0+Zmxvd2NfbmV0X2wyZGV2X3Zs
+YW5kZXYgWzB4JXhdCgAAAAAAAAAAAAAAAAAAbmV0X2wzaW40X2Rldl9jb25maWc6IGwyZGV2X2Zj
+LT5mbG93Y19pZCBbMHgleF0sIGFkZHJlc3MgYWxyZWFkeSB1c2VkIGJ5IHBvcnQgJWQKAAAAAAAA
+AAAAAAAAAAAAbmV0X2wzaW40X2Rldl9jb25maWc6ICBhZGRyIFsweCV4XSwgbWFzayBbMHgleF0s
+IGd3IFsweCV4XSwgcmVmX2NudCBbMHgleF0gaW4gdXNlCgAAAAAAAAAAAAAAAAAAd3JoX2NobmV0
+X2lmY29uZjogbDJkZXZfZmMtPmZsb3djX2lkIFsweCV4XSwgbDJkZXZfZmMtPmZsb3djX3R5cGUg
+WyUweF0sIGlmY29uZl93ci0+c3Vib3AgWzB4JXhdCgAAAAAAAAAAAAAAAAAAAHdyaF9jaG5ldF9p
+ZmNvbmY6IGwyZGV2X2ZjLT5mbG93Y19pZCBbMHgleF0sIHVua25vd24gc3Vib3AgWzB4JXhdCgAA
+AAAAAAAAAAAAAAAAd3JoX2NobmV0X2lmY29uZjogbDJkZXZfZmMtPmZsb3djX2lkIFsweCV4XSwg
+cmMgJWQKAAAAAAAAAAAAAAAAAG5ldGlmX2lwX2NvbmZsaWN0X3RpbWVyX2NiOiBsMmRldl9mYy0+
+Zmxvd2NfaWQgWzB4JXhdLCBpbmRldmN0eHQtPnN0YXRlIFslZF0sIGluZGV2Y3R4dC0+cmV0cnlf
+Y250IFslZF0KAAAAAAAAAABuZXRpZl9pcF9jb25mbGljdF90aW1lcl9jYjogbDJkZXZfZmMtPmZs
+b3djX2lkIFsweCV4XSwgaW5kZXZjdHh0IFsweCV4XSwgaW4gZnJlZSBzdGF0ZQoAAAAAAAAAAABj
+bWRoX2NobmV0X2lmYWNlOiBmYyBbMHgleF0sIGZjLT5mbG93Y19pZCBbMHgleF0sIGZjLT5mbG93
+Y190eXBlIFsweCV4XSwgcCBbMHgleF0sIGxlbjE2IFsldV0sIGxvYyBbMHgleF0KAAAAAAAAY21k
+aF9jaG5ldF9pZmFjZTpsMmRldl9mYyBbMHgleF0sIGwyZGV2X2ZjLT5mbG93Y19pZCBbMHgleF0s
+IGwyZGV2LT5mbG93Y190eXBlIFsldV0sIGwyZGV2X2ZjLT5mbG93Y19uZXRfbDJkZXZfZmxhZ3Mg
+WyUweF0KAAAAAABjbWRoX2NobmV0X2lmYWNlOiByMlswXSAldSByMlsxXSAldQoAAAAAAAAAAAAA
+AABjbWRoX2NobmV0X2lmYWNlOiBsMmRldl9mYy0+Zmxvd2NfbmV0X2wyZGV2X2ZsYWdzIGNoYW5n
+ZWQgZnJvbSBbJTB4XSB0byBbJTB4XSwgcmMgWyVkXQoAAAAAAAAAAABjaG5ldF9sMmRldl91cF9t
+Yl9jYjogcmMgWyVkXSwgcG9ydCBbJXVdLCBzdGF0ZSBbJXVdLCBjb29raWUgWzB4JXhdCgAAAAAA
+AAAAAAAAAGNobmV0X3F1ZXVlX3htaXQ6IGZjLT5mbG93Y19pZCBbMHgleF0sIGJ1Zl9sZW4gWzB4
+JXhdLCBidWZmZXJlZCBbMHgleF0sIGZpZm8ubnVtX2J5dGVzIFslMHhdCgAAAGRoY3BfcHJvY2Vz
+c19jYjogbDJkZXZfZmMtPmZsb3djX2lkIFsweCV4XSwgZGhjdHh0LT5zdGF0ZSBbJTB4XSwgZGhj
+dHh0LT5ydHJ5X2NudCBbJXVdCgAAAAAAAAAAAGRoY3BfdGltZXJfY2I6IERIQ1BESVNDT1ZFUiBz
+ZW50LCBidXQgbm8gcmVwbHkgZnJvbSBhbnkgcG9zc2libGUgc2VydmVyIG9uIHRoZSBuZXR3b3Jr
+LiBSZXRyeWluZyBhZ2FpbgoAAAAAAAAAAABkaGNwX3RpbWVyX2NiOiBsMmRldl9mYy0+Zmxvd2Nf
+aWQgWzB4JXhdLCBzZW5kaW5nIERIQ1BESVNDT1ZFUiBmb3IgZGhjdHh0IFsweCV4XSBvbiBwaWQg
+WyVkXQoAAABkaGNwX3RpbWVyX2NiOiBsMmRldl9mYy0+Zmxvd2NfaWQgWzB4JXhdLCBESENQT0ZG
+RVIgcmVjZWl2ZWQgZm9yIGRoY3R4dCBbJXhdIHBpZCBbJWRdCgAAAAAAAAAAAABkaGNwX3RpbWVy
+X2NiOiBsMmRldl9mYy0+Zmxvd2NfaWQgWzB4JXhdLCAgREhDUEFDSyByZWNlaXZlZCBmb3IgZGhj
+dHh0IFsleF0sIHBpZCBbJWRdCgAAAAAAAAAAAABkaGNwX3RpbWVyX2NiOiBsMmRldl9mYy0+Zmxv
+d2NfaWQgWzB4JXhdLCBkaGN0eHQtPmlwYWRkciBbMHgleF0KAAAAAAAAAAAAAAAAAAAAAGRoY3Bf
+dGltZXJfY2I6IGwyZGV2X2ZjLT5mbG93Y19pZCBbMHgleF0sIG5vIHJlcGx5IGZyb20gZGhjcCBz
+ZXJ2ZXIsIHRpbWluZyBvdXQKAAAAAAAAAAAAAAAAAAAAAGxvZ2luX3BhcmFtczogY29ubl9mYy0+
+Zmxvd2NfZm9pc2NzaV9jb25uX2ZsYWdzIFsweCV4XQoAAAAAAAAAAABsb2dpbl9wYXJhbXM6IGNv
+bm5fZmMtPmZsb3djX2ZvaXNjc2lfY29ubl9mbGFncyBbMHgleF0KAAAAAAAAAAAAYXV0aF9uZWdv
+X3NlY3VyaXR5OiBzZW5kX2ZsYWcgWzB4JXhdLCBhdXRoX3BvbGljeSBbMHgleF0KAAAAAAAAAGF1
+dGhfbmVnb19zZWN1cml0eTogS0VZX0NIQVBfUkVTUCAtIGhhc2hbMHgleCV4JXgleCV4JXgleCV4
+XQoAAABhdXRoX25lZ29fc2VjdXJpdHk6IEtFWV9DSEFQX1JFU1AgLSBoYXNoWzB4JXgleCV4JXgl
+eCV4JXgleF0KAAAAYXV0aF9uZWdvX3NlY3VyaXR5OiBLRVlfQ0hBUF9SRVNQIC0gZXJyb3IgZW5j
+b2RpbmcgdG8gaGV4CgAAAAAAAGF1dGhfbmVnb19zZWN1cml0eTogS0VZX0NIQVBfUkVTUCAtIGVs
+ZW4gWzB4JXhdCgAAAAAAAAAAAAAAAAAAAABhdXRoX25lZ29fc2VjdXJpdHk6IEtFWV9DSEFQX0NI
+QUwgLSBlcnJvciBlbmNvZGluZyB0byBoZXgKAAAAAAAAYXV0aF9uZWdvX3NlY3VyaXR5OiBLRVlf
+Q0hBUF9DSEFMIC0gZWxlbiBbMHgleF0KAAAAAAAAAAAAAAAAAAAAAHNlbmRfbG9naW5fcmVxdWVz
+dDpjb25uX2ZjLT5mbG93Y19pZCBbMHgleF0sIHNlc3NfZmMtPmZsb3djX2lkIFsweCV4XSwgY3Ry
+bF94bWl0IFsweCV4XSwgc2Vzc19mYy0+Zmxvd2NfZm9pc2NzaV9zZXNzX3R5cGUgWzB4JXhdCgAA
+AAAAAAAAAAAAAAAAAHNlbmRfbG9naW5fcmVxdWVzdDogZmxhZyBbMHgleF0uCgAAAAAAAAAAAAAA
+AAAAAHNlbmRfbG9naW5fcmVxdWVzdDogdHhfbGVuIFsldV0KAAAAAAAAAAAAAAAAAAAAAHhtaXRf
+c2VuZHRhcmdldHM6IGNvbm4gdGlkIFsweCV4XSwgY3RybF94bWl0LT50dHQgWzB4JXhdLCBkYXRh
+X2xlbiBbJXVdLCBwYWRfbGVuIFsldV0KAAAAAAAAAAAAAHBpbmdfdGFyZ2V0OiBwaW5nIHRpbWVv
+dXQgYmxvY2tpbmcgc2Vzc2lvbgoAAAAAAHNlbmRfbm9wX291dDogY29ubiBmbG93X2lkIFsweCV4
+XSwgcmVxIFsldV0sIHR0dCBbMHgleF0KAAAAAAAAAABjc29ja19mYWlsZWQ6IGNza19mYy0+Zmxv
+d2NfaWQgWzB4JXhdLCBjc2tfZmMtPmZsb3djX3N0YXRlIFsweCV4XSwgc2Vzc19mYy0+Zmxvd2Nf
+aWQgWzB4JXhdLCBzZXNzX2ZjLT5mbG93Y19zdGF0ZSBbMHgleF0sIGV2dCBbMHgleF0KAAAAAAAA
+AAAAAABjb3NrY19mYWlsZWQ6IHNlc3Npb24gYmxvY2ssIGV2ZW50IFsweCV4XQoAAAAAAABjb3Nr
+Y19mYWlsZWQ6IHNlc3Npb24gYmxvY2ssIGV2ZW50IFsweCV4XQoAAAAAAABjaG5ldF9maW5kX2wy
+dF9lbnRyeTogZGFkZHIgWyUwOHhdLCBbMHglMDh4XSwgbG9jYWwgbmV0d29yayBbJWRdCgAAAAAA
+AAAAAAAAAAAAAGwydGVudCBbJTB4XSwgbDJ0ZW50LT5pZHggWyVkXQoAcmMgWyVkXSwgY3NrX2Zj
+IFsweCV4XSwgY3NrX2ZjLT5mbG93Y19pZCBbMHgleF0KAAAAAAAAAAAAAAAAAAAAAGNzb2NrX2Zy
+ZWU6IHNpemVvZihjc2tfZmMtPnUuY3NvY2spIFsldV0sIGJ5dGVzCgAAAAAAAAAAAAAAAAAAAABU
+Q1AgY29ubiBlc3RhYmxpc2htZW50IGZhaWxlZCAlZAoAAAAAAAAAAAAAAAAAAABJbnZhbGlkIG9w
+Y29kZSAweCV4IGluIGN0cmwgcGF0aAoAAAAAAAAAAAAAAAAAAABERFAgZXJyb3IgWzB4JXhdLCBh
+Ym9ydGluZyBjb25ubiBbMHgleF0KAAAAAAAAAABpc2NzaV9oZHI6IGJhZCB0Y2Igc3RhdGUgdG8g
+cnggZGF0YSwgdGNiX2ZjLT5mbG93Y19pZCBbMHgleF0sIHRjYl9mYy0+Zmxvd2Nfc3RhdGUgWzB4
+JXhdCgAAAAAAAABpc2NzaV9oZHJfcng6IEludmFsaWQgdGFzayBzdGF0ZSAweCV4IGZvciB0YXNr
+IDB4JXgKAAAAAAAAAAAAAAAAaXNjc2lfZGF0YTogcmN2ZCBwYXlsb2FkCgAAAAAAAABkcm9wX2Fs
+bF90YXNrOiByZXF1ZXN0ZWQgbGlkeCBbJXVdCgAAAAAAAAAAAAAAAABnZXRfcmVmX3Rhc2s6IGlz
+dGFzay0+Zmxvd2NfaWQgWzB4JXhdLCBpc3Rhc2tfZmMtPmZsb3djX3N0YXRlIFsldV0sIGFicnRf
+bGlkeCBbMHgleF0sIGxpZHggWzB4JXhdCgAAAAAAAAAAAAAAAAAAcHJvY2Vzc190bWZfcmVzcG9u
+c2U6IGlzdGFza19mYy0+Zmxvd2NfYnVmLT5zY2hlZF9ub2RlLm5leHQgWzB4JXhdLCBpc3Rhc2tf
+ZmMgWzB4JXhdLCBpc3Rhc2tfZmMtPmZsb3djX2lkIFsldV0KAHByb2Nlc3NfdG1mX3Jlc3BvbnNl
+OiB1bHB0eHBsZW4xNiBbJXVdCgAAAAAAAAAAAHByb2Nlc3NfdG1mX3Jlc3BvbnNlOiBkZWxheV9w
+cm9jZXNzaW5nLCBvcCBbJXhdCgAAAAAAAAAAAAAAAAAAAABwcm9jZXNzX3RtZl9yZXNwb25zZTo6
+IHdyIG9wY29kZSBbMHgleF0KAAAAAAAAAABwcm9jZXNzX3RtZl9yZXNwb25zZTo6IHRhc2sgZnVu
+Y3Rpb24gWzB4JXhdCgAAAABjYWxsaW5nIGRyb3BfdGFzaywgbGlkeCBbMHgleF0KAHdyaF9mb2lz
+Y3NpX25vZGU6IG5vZGVfd3ItPmZsb3dpZF9sZW4xNiAyIFsleF0KAHdyaF9mb2lzY3NpX2NoYXA6
+IGlkX2xlbiBbJXhdLCBzZWNfbGVuIFsleF0KAAAAAHdyaF9mb2lzY3NpX2NoYXA6IHRndF9pZF9s
+ZW4gWyV4XSwgdGd0X3NlY19sZW4gWyV4XQoAAAAAAAAAAAAAAABwZWVyX2NvbjogY3NrX2ZjID0+
+IGZsb3dpZCBbMHgleF0sIGZsb3djX2J1ZiBbMHgleF0KAAAAAAAAAAAAAAAAZm9pc2NzaV9jdHJs
+OiBzdWJvcCBbMHgleF0sIHNlc3NfdHlwZV90b19lcmwgWzB4JXhdLCBzZXNzX3R5cGUgWzB4JXhd
+CgAAAAAAAAAAAAB3YXRjaGRvZyBjbWQgaGFuZGxlciAodGltZSAldSBhY3Rpb24gJXUpCgAAAAAA
+AABXQVRDSERPRzogZGV2aWNlIHNodXRkb3duCgAAAAAAAFdBVENIRE9HOiBieXBhc3MgdGltZW91
+dAoAAAAAAAAAV0FUQ0hET0c6IEZMUiAtIG5vdCBpbXBsZW1lbnRlZCB5ZXQKAAAAAAAAAAAAAAAA
+V0FUQ0hET0c6IHRlbXBlcmF0dXJlIG9mICVkQyBleGNlZWRzIHRocmVzaG9sZCBvZiAlZEMKAAAA
+AAAAAAAAAGZpbHRlcjogcG9yZ3JhbW1pbmcgdGlkICV1IChsZSB0Y2FtIGluZGV4ICV1KS4uLgoA
+AAAAAAAAAAAAAAAAAABmaWx0ZXI6IHJlcXVlc3RpbmcgY29tcGxldGlvbi4uLgoAAAAAAAAAAAAA
+AAAAAABGQ09FIEZyZWU6IHN0aWxsIHlpZWxkZWQgd2hlbiBmcmVlaW5nLi4uZmxvd2NfaWQgJXgg
+Zmxvd2NfZmxhZ3MgJXggCgAAAAAAAAAAAAAAAHBvcnQgJWQgcHJpb3IgJWQgc2VsZWN0ICVkIHBy
+b3RvY29sSUQgMHglMDR4CgAAAHBvcnQgJWQgc2V0IHBmY19lbiA9IDB4JXgKAAAAAAAAcG9ydCAl
+ZCBzZXQgcGdpZF8wXzcgPSAweCUwOHggcGdfcGVyY2VudGFnZSAweCUwOHhfJTA4eCBudW1fdGNz
+X3N1cHBvcnRlZCAlZAoAAABwb3J0ICVkIHNldCBwZmNfZW4gPSAweCV4CgAAAAAAAEZDb0UgRERQ
+IGZhaWxlZCA6IG94X2lkIDB4JXggcnhfaWQgMHgleAoAAAAAAAAAAEZDb0UgRERQIGZhaWxlZCA6
+IERkcFJlcG9ydCAweCV4IERkcFZhbGlkIDB4JXgKAFBSTEkgUnNwIHRpbWVkb3V0IDogZmxvd2Nf
+aWQgMHgleCBveF9pZCAweCV4IHJ4X2lkIDB4JXggCgAAAAAAAABjYW5ub3QgYWxsb2NhdGUgb2Zm
+bG9hZGVkIGZpbHRlciBjb25uZWN0aW9uCgAAAABjYW5ub3QgYWxsb2NhdGUgb2ZmbG9hZGVkIGZp
+bHRlciBJUHY2IGNvbm5lY3Rpb24KAAAAAAAAAAAAAAAAAAAAdW9bJXVdOiB3ciAlcCB3ci0+bGVu
+Z3RoICV1IG1zcyAldQoAAAAAAAAAAAAAAAAAdW9bJXVdOiBmIDB4JTA4eCBkc3RfY3BsIDB4JTA4
+eCBzcmNfY3BsIDB4JTA4eCBpbW1kbGVuMTYgJXUKAAAAAHVvWyV1XTogbGFzdCBtc3MgJXUKAAAA
+AAAAAAAAAAAAdW9bJXVdOiAhbGFzdCB3ci0+c2NoZWRwa3RzaXplICV1CgAAAAAAAAAAAAAAAAAA
+aW52YWxpZCBidWZmZXIgZ3JvdXBbJXVdIGNvbmZpZ3VyYXRpb246IG10dSAldSBsd20gJXUgaHdt
+ICV1IGR3bSAldQoAAAAAAAAAAAAAAABmYyAldSB2ZiAldSBnb3QgaXZmPTB4JXgscmFuZ2U6ICUj
+eC0lI3ggKCV1LyV1IHVzZWQpCgAAAAAAAAAAAAAAVkkgJXUgY2Fubm90IGdldCBSU1Mgc2xpY2U6
+IE5vIG1vcmUgc2xpY2VzIGF2YWlsYWJsZSAodXNlZCAldS8ldSkKAAAAAAAAAAAAAAAAAABwZm4g
+JXUgdmZuICV1IHdpdGggcG9ydCBtYXNrIDB4JXggY2Fubm90IGFjY2VzcyBwb3J0ICV1LCByZXQg
+JWQKAAAAAAAAAAAAAAAAAAAAAHBmbiAldSB2Zm4gJXUgY291bGQgbm90IGFsbG9jYXRlIHZpaWQs
+IHJldCAlZAoAAHBmbiAldSB2Zm4gJXUgY291bGQgbWFwIHZpaWQgIDB4JXggdG8gZmxvd2MsIHJl
+dCAlZAoAAAAAAAAAAAAAAABwZm4gJXUgdmZuICV1IGNvdWxkIG5vdCBhbGxvY2F0ZSB1d2lyZSBm
+dW5jICVkIG1hYyBhZGRyLCByZXQgJWQKAAAAAAAAAAAAAAAAAAAAAG1paV9mb3JjZV9zcGVlZFsl
+dV06IHJjYXBzIDB4JXgKAAAAAAAAAAAAAAAAAAAAAG1paV9saW5rX3N0YXR1c1sldV06IGJtc3Ig
+MHglMDh4IHN0YXR1cyAldSBtYWNfbHMgMHglMDh4CgAAAAAAAABwb3J0X2NtZF9oYW5kbGVyOiB1
+bmtub3duIHUuZGNiLnR5cGUgMHgleAoAAAAAAABwb3J0WyV1OjB4JTAyeDoweCUwMnhdOiBwaHkg
+cmVzZXQgbm90IGF2YWlsYWJsZQoAAAAAAAAAAAAAAAAAAAAAcG9ydFsldToweCUwMng6MHglMDJ4
+XTogdW5rbm93biBhY3Rpb24gMHgleAoAAAAAcG9ydFsldToweCUwMng6MHglMDJ4XTogdW5rbm93
+biByZWFkIGFjdGlvbiAweCV4CgAAAAAAAAAAAAAAAAAAAGNwbF9lcnJfbm90aWZ5OiB0aWQgJXUg
+Y3BsIDB4JTA4eCUwOHgKAAAAAAAAAAAAAGNwbF9lcnJfbm90aWZ5OiB0aWQgJXUgY3BsIDB4JTA4
+eCUwOHggMHglMDh4JTA4eAoAAAAAAAAAAAAAAAAAAABjcGxfZXJyX25vdGlmeTogdGlkICV1IGxl
+biAldQoAAEZDT0UgRnJlZTogc3RpbGwgeWllbGRlZCB3aGVuIGZyZWVpbmcuLi5mbG93Y19pZCAl
+eCBmbG93Y19mbGFncyAleCAKAAAAAAAAAAAAAAAARkNPRSBCUCBXUiBFUlI6IFdSIHdpdGggY29v
+a2llICV4JXggZXJyb3JlZCBiYWNrIAoAAAAAAAAAAAAAAAAAAHNjc2lfYWJvcnQ6IEVudGVyaW5n
+IEFib3J0X3Rhc2ssIGJ1ZmZlcmVkIFsldV0KAHNjc2lfYWJvcnQ6OiB3ci0+aXFpZCBbMHgleF0s
+IGlzdGFza19mYy0+Zmxvd2Nfc2dlX2lxaWQgWzB4JXhdCgBzY3NpX2Fib3J0OjogbHVuX2lkeCBb
+MHgleF0KAAAAAHNjc2lfYWJvcnQ6IHJlZiB0YXNrIG5vdCBvdXRzdGFuZGluZwoAAAAAAAAAAAAA
+AHNjc2lfYWJvcnQ6IHNyZXEtPm9wY29kZSBbMHgleF0gZmxhZ3MgWzB4JXhdCgAAAHNjc2lfYWJv
+cnQ6IHRhc2tfaWR4IFsldV0sIGl0dCBbMHgleF0KAAAAAAAAAAAAAGFib3J0L2Nsb3NlIFdSIHdp
+dGggY29va2llIDB4JWx4IHdhcyBpc3N1ZWQgb24gc3NuIDB4JXggaW4gd3Jvbmcgc3RhdGUgMHgl
+eAoAAAAAYWJvcnQgV1Igb24gc3NuIDB4JXggZGlkIG5vdCBmaW5kIFdSIHdpdGggY29va2llIDB4
+JXgleAoAAAAAAAAAAGNsb3NlIFdSIHdpdGggY29va2llIDB4JWx4IG9uIHNzbiAweCV4O2RpZCBu
+b3QgZmluZCBXUiB3aXRoIGNvb2tpZSAweCVseAoAAAAAAAAAYWJvcnQgV1Igb24gc3NuIDB4JXgg
+d2FzIGlzc3VlZCBvbiB4Y2hnIDB4JXggd2l0aCByeF9pZCAweCV4IGluIHdyb25nIHN0YXRlIDB4
+JXgKAAAAAAAAAAAAAAAAAAAAcG9ydCAlZCBhcHBsSUQgMHglMDR4IG91aSAweCUwNnggdXNlcl9w
+cmlvciAlZAoAc2VuZCBkY2J4IGluZm86Zmxvd2NpZCAldQoAAAAAAABzZW5kIGRjYnggY29tcGxl
+dGUKAAAAAAAAAAAAAAAAAERDQlggcG9ydCAldSBhbGxfc3luY2QgPSAldQoAAAAAY2huZXRfbDJ0
+X3VwZGF0ZTogbDJ0X3VwZGF0ZSByZXF1ZXN0IHNlbnQgbDJ0ZW50IFslMDh4XSwgbDJ0ZW50LT5p
+ZHggWyVkXSwgbDJ0ZW50LT52bGFuIFslZF0KAAAAbmV0aWZfcHJvY2Vzc19kaGNwOiBsMmRldl9m
+Yy0+Zmxvd2NfaWQgWzB4JXhdLCBwcm9jZXNzaW5nLCBvcHRfbGVuICV1CgAAAAAAAAAAAABjaG5l
+dF9kaGNwX3JlY3Y6IHZsYW5pZCBbJXVdLCBsMmRldl9waWRfZmMtPmZsb3djX25ldF9sMmRldl92
+bGFuZGV2IFsweCV4XSwgbDJkZXZfZmMgWzB4JXhdCgAAAABjaG5ldF9kaGNwX3JlY3Y6IGwyZGV2
+X2ZjLT5mbG93Y19pZCBbMHgleF0sIGRoY3R4dC0+c3RhdGUgWyVkXSwgbWFsYWNpb3VzIGRoY3Ag
+cmVjdiBmb3Igbm8gcmVxdWVzdAoAAAAAAAAAAAAAAAAAZGhjdHh0LT5zdGF0ZSA6ICVkCgAAAAAA
+AAAAAAAAAABsMmRldl9mYy0+Zmxvd2NfaWQgWzB4JXhdLCBCYWQgREhDUCBjb29raWUgcmVjaWV2
+ZWQsIGFib3J0aW5nCgAATl9QT1JUIDB4JXgleCV4IHJlamVjdGVkIFBMT0dJIHdpdGggcmVhc29u
+IGNvZGUgJXgKAAAAAAAAAAAAAAAAAEFCVFMgd2hpbGUgYXdhaXRpbmcgUFJMSSBSc3A6IGZsb3dj
+X2lkIDB4JXggb3hfaWQgMHgleCByeF9pZCAweCV4IAoAAAAAAAAAAAAAAAAAQUJUUyBmYWtlIFJz
+cDogbG9jIDB4JXggb3hfaWQgMHgleCByeF9pZCAweCV4CgAAcmVsZWFzZV90aWQ6IHNpemVvZih0
+Y2JfZmMtPmZsb3djX2ZvaXNjc2lfY29ubikgWyV1XSwgYnl0ZXMKAAAAAGFjdF9vcGVuX3JwbDog
+YXRpZCBbMHgleF0sIHRpZCBbMHgleF0sIG9wIFsweCV4XSwgc3RhdHVzIFsweCV4XQoAAAAAAAAA
+AAAAAAAAAAAAYWN0X29wZW5fcnBsOiBjc2tfZmMtPnRjYl9zdGF0ZSBbMHgleF0sIGNza19mYy0+
+ZmxhZ3MgWzB4JXhdLCB0Y2JfZmMtPmZsb3djX3N0YXRlIFsweCV4XQoAAAAAAAAAc2VuZF9hYm9y
+dF9ycGw6IGNza19mYy0+Zmxvd2NfdHlwZSBbMHgleF0sIGNza19mYy0+Zmxvd2NfaWQgWzB4JXhd
+LCB0aWQgWzB4JXhdLCB1bHB0eGNoIFsldV0sIGJ1ZmZlcmVkIFsldV0KAAAAAHdyaF9vZmxkX3Rj
+cF9jbG9zZV9jb25fcmVwbHk6IHRjYl9mYy0+Zmxvd2NfaWQgWzB4JXhdLCB0Y2JfZmMtPmZsb3dj
+X3R5cGUgWzB4JXhdLCBsZW4xNiBbJXVdLCBsb2MgWyV1XQoAAAAAAAAAAAB3cmhfb2ZsZF90Y3Bf
+Y2xvc2VfY29uX3JlcGx5OiBycGwtPm9wX1RpZCBbMHgleF0sIHJwbD5zdGF0dXMgWzB4JXhdLCBy
+cGwtPnNuZF9ueHQgWzB4JXhdLCBycGwtPnJjdl9ueHQgWzB4JXhdCgAAdGNwX2Fib3J0X3JwbF9y
+c3M6IHRpZCBbMHgleF0sIHN0YXR1cyBbMHgleF0KAAAAdGNwX2Fib3J0X3JlcV9yc3M6IHRpZCBb
+MHgleF0sIHN0YXR1cyBbMHgleF0KAAAAcGt0c2NoZWRfY2xfcmxbJXU6JXVdOiBtb2RlIHwgdW5p
+dCB8IHJhdGUgMHglMDZ4IG1pbiAldSBtYXggJXUgcGt0c2l6ZSAldQoAAAAAAABwYXJhbV9kbWFx
+WzB4JXg6MHgleF06IGRtYXEgMHgleCByZWFkICV1IHBmICV1IHJldCAlZAoAAAAAAAAAAAAATUNb
+JXVdIGluaXRfc3RhdGVfbWFjaGluZSAweCUwMngKAAAAAAAAAAAAAAAAAAAATUMgaW5pdGlhbGl6
+YXRpb24gbm90IGNvbXBsZXRpbmcsIE1DIGN1cnJlbnQgaW5pdCBzdGF0ZSBpcyAweCUwMngKAAAA
+AAAAAAAAAAAAAABNQ1sldV0gX2h3X21jX2luaXRfbWMKAAAAAAAAAAAAAF9od19tY19pbml0X21j
+X2ZwZ2FbJXVdOiBlcnJvciAlZAoAAAAAAAAAAAAAAAAAAGh3X2xlX2ZpbHRlcl9jdHVwbGU6IHR1
+cGxlICV1IG5vdCBzcGVjaWZpZWQgYnV0IHJlcXVpcmVkIGZvciBtYXNrIDB4JXgKAAAAAAAAAAAA
+bGUgY29uZmlndXJhdGlvbjogaGFzaCByZWdpb24gdG9vIGxhcmdlIHRvIGVuYWJsZSBzZXJ2ZXIg
+c3JhbQoAAGxlIGNvbmZpZ3VyYXRpb246IGNhbm5vdCBlbmFibGUgc2VydmVyIHNyYW0gd2hlbiBo
+YXNoIHJlZ2lvbiBpcyBkaXNhYmxlZAoAAAAAAAAAbGUgY29uZmlndXJhdGlvbjogbmVudHJpZXMg
+JXUgcm91dGUgJXUgY2xpcCAldSBmaWx0ZXIgJXUgYWN0aXZlICV1IHNlcnZlciAldSBoYXNoICV1
+CgAAAAAAAAAAAAAAbGUgY29uZmlndXJhdGlvbjogbmVudHJpZXMgJXUgcm91dGUgJXUgY2xpcCAl
+dSBmaWx0ZXIgJXUgc2VydmVyICV1IGFjdGl2ZSAldSBoYXNoICV1IG5zZXJ2ZXJzcmFtICV1CgAA
+AAAAAAAAAAAAAGNmX3BhcnNlOiBmaWxlIG1lbXR5cGUgMHgleCBtZW1hZGRyIDB4JXggbWFwcGVk
+IEAgJXA6CgAAAAAAAAAAAABjb25maWd1cmVkIHdpdGggY2FwcyBuYm18bGluayAweCUwOHggc3dp
+dGNofG5pYyAweCUwOHggdG9lfHJkbWEgMHglMDh4IGlzY3NpfGZjb2UgMHglMDh4CgAAAAAAAABu
+ZXQgVkkgYWxsb2NhdGlvbiBmYWlsZWQgZm9yIGZjX2lkICV1IHdpdGggZXJyb3IgJWQKAAAAAAAA
+AAAAAAAAbmV0IFZJIG1hYyBhZGRyZXNzIHByb2dyYW1taW5nIGZhaWxlZCBmb3IgZmNfaWQgJXUg
+d2l0aCBlcnJvciAlZAoAAAAAAAAAAAAAAAAAAABuZXQgVkkgcnhtb2RlIHByb2dyYW1taW5nIGZh
+aWxlZCBmb3IgZmNfaWQgJXUgd2l0aCBlcnJvciAlZAoAAAAAbmV0IFZJIHJzcyBpbmRpcmVjdGlv
+biB0YWJsZSBwcm9ncmFtbWluZyBmb3IgZmNfaWQgJXUgZmFpbGVkIHdpdGggZXJyb3IgJWQKAAAA
+AABuZXQgVkkgcnNzIGNvbmZpZyBjb21tYW5kIGZhaWxlZCBmb3IgZmNfaWQgJXUgd2l0aCBlcnJv
+ciAlZAoAAAAAbmV0IFZJIGNvbW1hbmQgZmFpbGVkIGZvciBmY19pZCAldSB3aXRoIGVycm9yICVk
+CgAAAAAAAAAAAAAAAAAAAHBwbWF4IFsldV0sIGJpdHMgWyV1XSwgRldfSVNDU0lfUEFHRVBPRF9U
+QUdfSURYX01BWF9TSVpFIFsldV0KAABkZWZhdWx0IHRhZ21hc2sgWzB4JTB4XQoAAAAAAAAAAHBy
+b2dyYW1tZWQgdGFnbWFzayBbMHglMHhdCgAAAAAAcHBtLT5kZHBfaW5mby5sbGltaXQgWzB4JTB4
+XSwgcHBtLT5kZHBfaW5mby51bGltaXQgWzB4JTB4XSwgcHBtLT5kZHBfaW5mby5zaXplIFsweCUw
+eF0KAAAAAAAAAAAAcHBtLT5kZHBfaW5mby5tYXhfdHhzeiBbMHglMHhdIHBwbS0+ZGRwX2luZm8u
+bWF4X3J4c3ogWzB4JTB4XSBpb3NpemUgWzB4JTB4XQoAAABwcG1heCBbJXVdLCBpZHhfYml0cyBb
+JXVdLCBpZHhfbWFzayBbMHglMHhdLCByZXN2ZF90YWdfbWFzayBbMHglMHhdLCB0YWdtYXNrIFsw
+eCUweF0KAAAAAAAAAAAAAAB0YWcgaXR0IDB4JTB4LCBiaXRzICV1LCBhZ2UgMHglMHgsIGJpdHMg
+JXUKAAAAAABjeGNuaWMtPmlzY3NpX3BwbSBbMHglMHhdCgAAAAAAAHNjc2lfcGxkX3NpemUgWyV1
+XSwgQUxJR04oc2NzaV9wbGRfc2l6ZSwgMTYpIFsldV0KAAAAAAAAAAAAAAAAAABtYXhfcHBvZF96
+b25lcyBbJXVdCgAAAAAAAAAAAAAAAGZvaXNjc2lfaW5pdDogZm9pc2NzaV9pbml0X2RvbmUgWyV1
+XSwgZGV2LnJlcy5mb2lzY3NpX250YXNrcyBbJXVdLCBkZXYucmVzLmZvaXNjc2lfbnNlc3MgWyV1
+XSwgZGV2LnJlcy5uY3NvY2sgWyV1XSwgZGV2LnJlcy5mb2lzY3NpX25pbml0IFsldV0sIHJjIFsl
+ZF0KAAAAAAAAAABjaF9jbF9yYXRlWyV1LyV1XTogY2FwcGVkIGNsYXNzIHJhdGUgZnJvbSByZXF1
+ZXN0ZWQgJXUgdG8gY29uZmlndXJlZCAoZWZmZWN0aXZlKSBjaGFubmVsIHJhdGUgJXUKAAAAAAAA
+AAAAAAAAAAAAY2hfY2xfcmF0ZVsldS8ldV06IGluY3JlYXNlZCBkZWZpY2l0X2luY3IgZnJvbSBy
+ZXF1ZXN0ZWQgJXUgdG8gcmVxdWlyZWQgbWluIG9mICV1OyByYXRlICV1IChlZmYgJXUpIGRlZmlj
+aXRfbWF4ICV1CgAAAAAAAAAAAAAAAABwa3RzY2hlZCBjaGFubmVsICV1IHNldHMgc3BlZWQgKGZy
+b20gJXUpIHRvICV1IGticHMKAAAAAAAAAAAAAAAAcG9ydF9jbWRfaGFuZGxlcjogdW5rbm93biB1
+LmRjYi50eXBlIDB4JXgKAAAAAAAAaTJjIGVycm9yIGNhdXNlZCBieSBtb2R1bGUgdW5wbHVnCgAA
+AAAAAAAAAAAAAAAAc2VuZHRvIHBlbmRpbmc6IHdyX3BlbmQgJXAgZm9yIHBvcnQgJXUsICB3YW50
+IHRvIHNlbmQgdG8gcG9ydCAldQoAAAAAAAAAAAAAAAAAAABzZW5kdG86Zmxvd2NpZCAldQoAAAAA
+AAAAAAAAAAAAAHBvcnRfbGlua19zdGF0ZV9oYW5kbGVyWyV1XSBsZWF2aW5nIFNUQVRFX1VQICg9
+PnBvd2VyZG93bikKAAAAAABwb3J0X2xpbmtfc3RhdGVfaGFuZGxlclsldV0gcG93ZXJpbmcgZG93
+bgoAAAAAAABwb3J0X2xpbmtfc3RhdGVfaGFuZGxlclsldV0gbGVhdmluZyBwb3dlcmRvd24gKD0+
+ZG93bikKAAAAAAAAAAAAcG9ydF9saW5rX3N0YXRlX2hhbmRsZXJbJXVdIGxlYXZpbmcgU1RBVEVf
+RE9XTiAoPT5wb3dlcnVwKQoAAAAAAHBvcnRfbGlua19zdGF0ZV9oYW5kbGVyWyV1XSBwb3dlcmlu
+ZyB1cAoAAAAAAAAAAHBvcnRfbGlua19zdGF0ZV9oYW5kbGVyIGxlYXZpbmcgcG93ZXJ1cCAoPT51
+cCkKAHBvcnRfbGlua19zdGF0ZV9oYW5kbGVyWyV1XSBvaCBteSB3aGF0IGlzIGdvaW5nIG9uIChz
+dGF0ZSA9ICUjeAoAAAAAAAAAAAAAAAAAAAAAcG9ydF9saW5rX3N0YXRlX2hhbmRsZXIgZ290IHJl
+c2NoZWR1bGVkCgAAAAAAAAAAcG9ydF9saW5rX3N0YXRlX2hhbmRsZXI6IFNvbWV0aGluZyB3ZW50
+IHRlcnJpYmx5IHdyb25nLiByZXQgPSAlZAoAAAAAAAAAAAAAAAAAAABod19zZ2VfbWFtZW1faW5p
+dDogZW5jb3VudGVyZWQgZXJyb3IgJWQKAAAAAAAAAABsZSBpbml0aWFsaXphdGlvbjogbmVudHJp
+ZXMgJXUgcm91dGUgJXUgY2xpcCAldSBmaWx0ZXIgJXUgYWN0aXZlICV1IHNlcnZlciAldSBoYXNo
+ICV1CgAAAAAAAAAAAABsZSBpbml0aWFsaXphdGlvbjogbmVudHJpZXMgJXUgcm91dGUgJXUgY2xp
+cCAldSBmaWx0ZXIgJXUgc2VydmVyICV1IGFjdGl2ZSAldSBoYXNoICV1IG5zZXJ2ZXJzcmFtICV1
+CgAAAAAAAAAAAAAAdHAgY2hhbm5lbCBjb25maWd1cmF0aW9uOiAldSByeCBhbmQgJXUgdHggY2hh
+bm5lbHMKAAAAAAAAAAAAAAAAAHRwIG1lbSBkaXN0cmlidXRpb25bJSVdIHBtdHggJXUgcG1yeCAl
+dSBkZHAgJXUgZGRwX2lzY3NpICV1IHN0YWcgJXUgcGJsICV1IHJxICV1IHJxdWRwICV1CgAAAAAA
+AFRQX1BNTV9SWF9QQUdFX1NJWkUgJXUgaXMgbm90IHN1cHBvcnRlZAoAAAAAAAAAAFRQX1BNTV9U
+WF9QQUdFX1NJWkUgJXUgaXMgbm90IHN1cHBvcnRlZAoAAAAAAAAAAGN4Y25pY19kZXZpY2VfaW5p
+dDogY3hjbmljIFsweCUweF0sIGN4Y25pYy0+ZmlsdGVyIFslMHhdCgAAAAAAAABjeGNuaWNfZGV2
+aWNlX2luaXQ6IGRldi50cC5udW1fdHhfcGFnZXMgWyV1XSwgZGV2Lm1jWzBdLnNpemUgWyV1XSwg
+ZGV2LnRwLnR4X3BhZ2Vfc3ogWyV1XSwgY3hjbmljIHNpemUgWyVkXSwgcmMgWyVkXQoAAAAAAAAA
+AAAAAFBvcnRbJXVdOiBVbmtub3duIFNHTUlJIHN1Yi10eXBlICUjeAoAAAAAAAAAAAAAAHBvcnRf
+aW5pdFsldV06IHBvcnQgdHlwZSAweCV4IGlzIG5vdCBzdXBwb3J0ZWQKAEVRIHBmbiAldSB2Zm4g
+JXU6IGRlc3Ryb3lpbmcgZXFpZCAldSB3aXRoIHBlbmRpbmcgV1IocykgKG51bV9ieXRlcyAldSBh
+bmQgZmxhZ3MgMHglMDh4CgAAAAAAAAAAAGwyZGV2X2ZjLT5mbG93Y19pZCBbJXVdLCBsMmRjLT5w
+Zm4gWyV1XSwgbDJkYy0+dmZuIFsldV0sIGwyZGMtPmxwb3J0IFsldV0sIGwyZGV2X2ZjLT5mbG93
+aWQgWyV1XSBsMmRjLT50eF9jaCBbJXVdLCBkZXYudnBkLnBvcnR2ZWMgWyV4XQoAAAAAAAAAAHBv
+cnR2ZWMgWyV1XQoAAABBaCBoYS4uLmRvdWJsZSBmcmVlIG94X2lkIDB4JXgsIHJ4X2lkIDB4JXgK
+AAAAAABIb3N0IFBSTEkgUmVzcG9uc2UgdGltZWRvdXQ6IG94X2lkIDB4JXggcnhfaWQgMHgleAoA
+AAAAAAAAAAAAAAAAcGZuICV1IHZmbiAldSB2aWEgY29tbWFuZAoAAAAAAABod19pMmNfdHJhbnNh
+Y3Rpb246IG5kYXRhICV1IGFkZHJfb3AgMHgleCBkYXRhWzBdIDB4JXggZGlmZiAldQoAaHdfaTJj
+X3RyYW5zYWN0aW9uOiBuZGF0YSAldSBhZGRyX29wIDB4JXggZGF0YVswXSAweCV4IGRpZmYgJXUg
+ZHBvcyAldSBjb250ICV1IGZhaWxlZCB3aXRoIGVyciAlZAoAAAAAAAAAAAAAAAAAAGkyYyB0cmFu
+c2FjdGlvbiBmYWlsZWQgdG8gY29tcGxldGUKAAAAAAAAAAAAAAAAAFBMX1BDSUVfTElOSy5zcGVl
+ZCBvZiAldSBpcyBub3Qgc3VwcG9ydGVkCmZpbGUsIHJldCBGV19FSU8KAAAAAABjb25maWd1cmF0
+aW9uIGZpbGUgcGFyc2VyOiBzZ2UgdGltZXIgdmFsdWVbJWldIGlzIHRvbyBsYXJnZSwgY2hhbmdp
+bmcgZnJvbSAldSB0byAldXVzZWNzCgAAAAAAAABmaWx0ZXJtYXNrIDB4JXggaXMgbm90IGVxdWFs
+L3N1YnNldCB0by9vZiBmaWx0ZXJtb2RlCgAAAAAAAAAAAAAAaHdfbGVfY2xpcF9oYW5kbGVyOiBy
+ZW1vdmVkIHBvcz0ldSAoPWlkeCAldSkKAAAAaHdfbGVfY2xpcF9oYW5kbGVyOiBhZGRpbmcgdG8g
+cG9zPSV1ICg9aWR4ICV1KQoAbW9kdWxlWyV1XTogcG9ydCBtb2R1bGUgaW5zZXJ0ZWQgYW5kIHJl
+YWR5CgAAAAAAbW9kdWxlWyV1XTogcG9ydCBtb2R1bGUgcmVtb3ZlZAoAAAAAAAAAAAAAAAAAAAAA
+bW9kdWxlWyV1XTogdW5rbm93biBtb2R1bGUgaWRlbnRpZmllciAweCUwMngKAAAAbW9kdWxlWyV1
+XTogZ3BpbyAldSB0cmFucyAxMEcgMHglMDJ4IDFHIDB4JTAyeCAobGVuZ3RoICV1KSBjYWJsZSAw
+eCUwMnggKGxlbmd0aCAldSkgbW9kdWxlX3R5cGUgMHglMDJ4CgAAAAAAAAAAAGN4NF9jcl9wZG93
+blsldV06IGVuICV1IHZpX2VuX2NudCAldQoAAAAAAAAAAAAAAGhzc19zaWdkZXRbJXVdOiBoc3Nf
+c2lnZGV0IGNoYW5nZWQgdG8gMHgleAoAAAAAAGNyX21vZHVsZV9yeF9sb3NbJXVdOiByeF9sb3Mg
+Y2hhbmdlZCB0byAldQoAAAAAAGN4NF9jclsldV06IHJldCAlZCBzdGF0dXMgJXUgeGdtX2xzIDB4
+JTA4eAoAAAAAAE1DOiBleHBlY3RlZCBzdGF0ZSB0byBzd2l0Y2ggdG8gQ0ZHLgAAAAAAAAAAAAAA
+AE1DOiBleHBlY3RlZCBzdGF0ZSB0byBzd2l0Y2ggdG8gQWNjZXNzLgAAAAAAAAAAAE1DOiBleHBl
+Y3RlZCBzdGF0ZSB0byBzd2l0Y2ggdG8gQ0ZHLgAAAAAAAAAAAAAAAE1DOiBleHBlY3RlZCBzdGF0
+ZSB0byBzd2l0Y2ggdG8gQWNjZXNzLgAAAAAAAAAAAElJLjEuYnggZHAxOFsldV0gcVsldV0gJSN4
+ICUjeCAlI3ggJSN4IG1pbiAlI3ggbWF4ICUjeAoAAAAAAAAAAABJSS4xLmMtZC4gJSN4ICUjeCAl
+I3ggJSN4IGFsbCAgJSN4CgAAAAAAAAAAAAAAAABJSS4yLmIgKCUjeCAtICUjeCArICUjeCkgJSAx
+MjggPSAlI3gKAAAAAAAAAAAAAABJSS4zIGluZXdfMWUgYWZ0ZXIgbGltaXQgY29tcHV0ZSBpdGVt
+cF8xZSAleCwgaW5ld18xZSAleAoAAAAAAAAASUkuMy4gaXRlbXBfMWUgJSN4IGluZXdfMWUgJSN4
+IGluZXdfMWUgJWQKAAAAAAAASUkuNC4gc2V0XzFlICUjeAoAAAAAAAAAAAAAAAAAAABNQzogY2Fs
+aWJyYXRpb24gZmFpbGVkIGZvciBlcnJhdGEyOSBkcDE4ICV1CgAAAABJVi4xLiBkcDE4WyV1XSBw
+aGFzZV9zZWwgYmVmb3JlICUjeCBhZnRlciAlI3gsIGdhdGVfZGVsYXkgJSN4CgAATUMgZXJyYXRh
+MjkgaXNzdWU6IGRwMTggJXUgcXVhZCAldSBjYW5ub3QgYmUgZGVjcmVhc2VkCgAAAAAAAAAAAE1D
+IGVycmF0YTI5IGlzc3VlOiBkcDE4ICV1IHF1YWQgJXUgY2Fubm90IGJlIGRlY3JlYXNlZAoAAAAA
+AAAAAABNQyBlcnJhdGEyOSBpc3N1ZTogZHAxOCAldSBxdWFkICV1IGNhbm5vdCBiZSBkZWNyZWFz
+ZWQKAAAAAAAAAAAATUMgZXJyYXRhMjkgaXNzdWU6IGRwMTggJXUgcXVhZCAldSBjYW5ub3QgYmUg
+ZGVjcmVhc2VkCgAAAAAAAAAAAHRlbXAyXzFlKzB4MTAgPSAlI3gKAAAAAAAAAAAAAAAATUM6IGNh
+bGlicmF0aW9uIGZhaWxlZCBmb3IgZXJyYXRhMjEgaXRlcmF0aW9uICV1CgAAAAAAAAAAAAAAAAAA
+AE1DIGVycmF0YSAyMTogZHAxOFsldV0gcHIwIG4wMiBmYWlsZWQgdG8gZ2V0IGF2ZXJhZ2UKAAAA
+AAAAAAAAAABNQyBlcnJhdGEgMjE6IGRwMThbJXVdIHByMCBuMTMgZmFpbGVkIHRvIGdldCBhdmVy
+YWdlCgAAAAAAAAAAAAAATUMgZXJyYXRhIDIxOiBkcDE4WyV1XSBwcjEgbjAyIGZhaWxlZCB0byBn
+ZXQgYXZlcmFnZQoAAAAAAAAAAAAAAE1DIGVycmF0YSAyMTogZHAxOFsldV0gcHIxIG4xMyBmYWls
+ZWQgdG8gZ2V0IGF2ZXJhZ2UKAAAAAAAAAAAAAABNQyBpbml0aWFsaXphdGlvbiBmYWlsZWQ6IENh
+bGlicmF0aW9uIGNvbXBsZXRlIGJpdCBub3QgdG9nZ2xpbmcKAAAAAAAAAAAAAAAAAAAAAE1DIGlu
+aXRpYWxpemF0aW9uIGZhaWxlZDogREZJIGluaXQgbm90IGdvaW5nIHRvIDAKAAAAAAAAAAAAAAAA
+AABNQyBpbml0aWFsaXphdGlvbiBmYWlsZWQ6IERGSSBpbml0IG5vdCBjb21wbGV0aW5nCgAAAAAA
+AAAAAAAAAAAATUMgaW5pdGlhbGl6YXRpb24gZmFpbGVkOiBDYWxpYnJhdGlvbiBkaWRuJ3QgY29t
+cGxldGUuCgAAAAAAAAAAAERQMTggJXUsIGJ5dGVfbGFuZSAldSwgYml0X3NlbGVjdCAldQoAAAAA
+AAAAAAAAAERQMTggJXUsIGJ5dGVfbGFuZSAldSwgYml0X3NlbGVjdCAldQoAAAAAAAAAAAAAAE1D
+IGZhaWxlZCB0byBnZXQgVVBDVEwgcG93ZXIgdXAgZG9uZQoAAAAAAAAAAAAAAE1DIGluaXRpYWxp
+emF0aW9uIGZhaWxlZDogRGlkbid0IGdldCBhbGwgRFAxOHMgbG9ja2VkCgAAAAAAAAAAAABNQyBp
+bml0aWFsaXphdGlvbiBmYWlsZWQ6IERpZG4ndCBnZXQgYm90aCBBRFJzIGxvY2tlZAoAAAAAAAAA
+AAAAQ3VycmVudCBTbGV3IHR4X3JvdyAlZDogdHhfY29sICVkLCB2YWwgJWQKAAAAAAAAQ3VycmVu
+dCBTbGV3IGFkZHJfcm93ICVkOiBhZGRyX2NvbCAlZCwgdmFsICVkCgAATUMgaW5pdGlhbGl6YXRp
+b24gZmFpbGVkOiBTTEVXX0RPTkVfU1RBVFVTIG5ldmVyIHRvZ2dsZWQAAAAAAAAAAGZscl9wZnZm
+X2ZzbVsldToldV06IHVua25vd24gc3RhdGUgJXUKAAAAAAAAAAAAAGh3IHBmIGJpdG1hcCAweCUw
+MnggdmZpZCBiaXRtYXAgMHglMDh4OjB4JTA4eDoweCUwOHg6MHglMDh4CgAAAABhZnRlciB2Zmlk
+IGZpeHVwLCB2ZmlkIGJpdG1hcCAweCUwOHg6MHglMDh4OjB4JTA4eDoweCUwOHgKAAAAAAAATUNb
+JXVdOiBmYWlsZWQgdG8gc3dpdGNoIGNvbnRyb2xsZXIgdG8gQ0ZHIHN0YXRlCgAAAAAAAAAAAAAA
+AAAAAE1DWyV1XTogZmFpbGVkIHRvIHN3aXRjaCBjb250cm9sbGVyIHRvIElOSVRfTUVNIHN0YXRl
+CgAAAAAAAAAAAABNQ1sldV06IGZhaWxlZCB0byBzd2l0Y2ggY29udHJvbGxlciB0byBDRkcgc3Rh
+dGUKAAAAAAAAAAAAAAAAAAAATUNbJXVdOiBwZXJpb2RpYyBjYWxpYnJhdGlvbiBmYWlsZWQgd2l0
+aCBlcnJvciAldQoAAAAAAAAAAAAAAAAAAHRpbWVyIHF1ZXVlICV1IGxvc3QgYSB0aWNrISBuZXh0
+ICVwIGxhc3QgJXAgbnVtZSAldQoAAAAAAAAAAAAAAABmbHJfdGltZXJfc3RhcnQ6IGZsb3djX2lk
+ICV1ICVwIGJ1ZiAlcAoAAAAAAAAAAABNQUM6IFBMTHMgZGlkbid0IGxvY2sKAAAAAAAAAAAAAHBj
+aWU6IHJlYWQgZnJvbSBzZXJjZmcgcGNpZV9pcF91cl9tYXhmdW5jIDB4JXggcGZiaXRtYXAgMHgl
+eAoAAABwY2llOiBucGYgJXUgKHBmYml0bWFwIDB4JTAyeCkgbnZmICV1IChwZiAwLi4zIDB4JTAy
+eCAweCUwMnggMHglMDJ4IDB4JTAyeCkKAAAAAGZhaWxlZCB0byBmaW5kIHRoZSAlYyVjIFZQRCBw
+YXJhbWV0ZXIKAAAAAAAAAAAAAGZhaWxlZCB0byBwYXJzZSB0aGUgJWMlYyBWUEQgcGFyYW1ldGVy
+CgAAAAAAAAAAAGZhaWxlZCB0byBzdWNjZXNzZnVsbHkgZmluZCBDaGVsc2lvIFZQRAoAAAAAAAAA
+AGxvZyBpbml0aWFsaXplZCBAIDB4JTA4eCBzaXplICV1ICgldSBlbnRyaWVzKQoAAGJvb3RzdHJh
+cCBmaXJtd2FyZSB0b29rICV1IG1zZWNzIHRvIHJ1bgoAAAAAAAAAAGZsb3djICV1IChTR0UgZXFp
+ZCAldSkgKEVUSENUUkwgcXVldWUpIGV4cGVyaWVuY2VkIGEgUENJIERNQSBSRUFEIHdvcmsgcmVx
+dWVzdCBlcnJvciAoaW5ib3VuZCBxdWV1ZSAldSkKAAAAAAAAAABmbG93YyAldSAoU0dFIGVxaWQg
+JXUpIGV4cGVyaWVuY2VkIGFuIHVuZXhwZWN0ZWQgUENJIERNQSBSRUFEIHdvcmsgcmVxdWVzdCBl
+cnJvciAoaW5ib3VuZCBxdWV1ZSAldSkKAAAAAAAAAAAAAAAAZmxvd2MgJXUgZXhwZXJpZW5jZWQg
+YW4gdW5leHBlY3RlZCBQQ0kgRE1BIFJFQUQgZXJyb3IgKGluYm91bmQgcXVldWUgJXUpCgAAAAAA
+AAB3cmhfZm9pc2NzaV9zY3NpX2Vycjogd3IgWzB4JXhdLCBxdWV1ZWQgb24gZmxvd2MgWzB4JXhd
+LCBmaWZvIFsweCV4XSBieXRlc19hdmFpbGFibGUKAAAAAAAAAAAAAABzZW5kX2Fib3J0X3JlcTog
+Y3NrX2ZjLT5mbG93Y190eXBlIFsweCV4XSwgY3NrX2ZjLT5mbG93Y19pZCBbMHgleF0sIHRpZCBb
+MHgleF0sIHVscHR4Y2ggWyV1XSwgYnVmZmVyZWQgWyV1XQoAAAAAc2VuZF9jbG9zZV9yZXE6IGNz
+a19mYy0+Zmxvd2NfdHlwZSBbMHgleF0sIGNza19mYy0+Zmxvd2NfaWQgWzB4JXhdLCBjc2tfZmMt
+PnRjYl9zdGF0ZSBbMHgleF0KAAAAc2VuZF9jbG9zZV9yZXE6IGNza19mYy0+Zmxvd2NfdHlwZSBb
+MHgleF0sIGNza19mYy0+Zmxvd2NfaWQgWzB4JXhdLCB0aWQgWzB4JXhdLCB1bHB0eGNoIFsldV0s
+YnVmZmVyZWQgWyV1XQoAAAAAAG9mbGRfdGNwX2RvX2FjdGl2ZV9jbG9zZTogY3NrX2ZjIFsweCV4
+XSwgY3NrX2ZjLT5mbG93Y19pZCBbMHgleF0sIGNza19mYy0+dGNiX3N0YXRlIFsweCV4XQoAAAAA
+AG9mbGRfdGNwX2RvX2FjdGl2ZV9jbG9zZTogY3NrX2ZjIFsweCV4XSwgY3NrX2ZjLT5mbG93Y19p
+ZCBbMHgleF0sIGNza19mYy0+dGNiX3N0YXRlIFsweCV4XQoAAAAAAHNlc3Npb25fYmxvY2s6IHNl
+c3NfZmMtPmZsb3djX2lkIFsweCV4XSwgc2Vzc19mYy0+Zmxvd2Nfc3RhdGUgWzB4JXhdLCBjb25u
+X2ZjLT5mbG93Y19pZCBbMHgleF0sIGNvbm5fZmMtPmZsb3djX3N0YXRlIFsweCV4XSwgY3NrX2Zj
+LT5mbG93Y19pZCBbMHgleF0sIGNza19mYy0+Zmxvd2Nfc3RhdGUgWzB4JXhdCgAAAAAAAAAAAAAA
+AAAAAG9mbGRfdGNwX2Rpc2Nvbm5lY3Q6IHRjYl9mYy0+Zmxvd2NfaWQgWzB4JXhdLCBjc2tfZmMt
+PmZsb3djX2lkIFsweCV4XSwgY3NrLT50Y2Jfc3RhdGUgWzB4JXhdCgAAAGRlY29kZV9iYXNlNjRf
+c3RyaW5nOiBkbGVuIFslZF0KAAAAAAAAAAAAAAAAAAAAAGRlY29kZV9oZXhfc3RyaW5nOiBkbGVu
+IFslZF0KAAAAbG9nZ2VkX2luOiBmbG93Y19mb2lzY3NpX2Nvbm5fZmxhZ3MgWzB4JXhdCgAAAAAA
+Zm9pc2NzaV92YWxpZGF0ZV9sb2dpbl9zdGFnZTogLSAxCgAAAAAAAAAAAAAAAAAAU2VxICMgbWlz
+bWF0Y2ggc3RhdHNuID0gMHgleCBleHBzdGF0c24gPSAweCV4CgAAdGV4dF9yZXNwOiBjX2JpdCBb
+MHgleF0sIGZfYml0IFsweCV4XQoAAAAAAAAAAAAAY3BsX3R4X3BrdDogaW50ZiBbMHgleF0KAAAA
+AAAAAABuZXRfbDJkZXZfZmluZF9ieV9hZGRyOiBsMmRldl9mYy0+Zmxvd2NfaWQgWzB4JXhdLCBs
+MmRjLT5scG9ydCBbJXVdLCBsMmRfZmMtPmZsb3djX2lkIFsweCV4XSwgbDJkYy0+aW40X2Rldi5p
+bl9hZGRyLmFkZHIgWzB4JXhdLCBhZGRyIFsweCV4XQoAAABuZXRfbDJkZXZfbXR1X2NvbmZpZzog
+bDJkZXZfZmMtPmZsb3djX2lkIFsweCV4XSwgbXR1ICV1CgAAAAAAAAAAbmV0aWZfZG9fZGhjcDog
+d3ItPnBhcmFtLnZsYW5pZCBbJXVdLCBsMmRldl9mYy0+Zmxvd2NfbmV0X2wyZGV2X3ZsYW5kZXYg
+WzB4JXhdCgBsMmRldl92aV9mc206IG1iIFsweCV4XSwgZGVmZXJyZWQsIHN0YXRlIFsweCV4XSwg
+cG9ydCBbMHgleF0KAAAAbDJkZXZfdmlfZnNtOiB2aWlkIFsweCV4XSBwb3J0IFsweCV4XSwgbWFj
+LWlkIFslMDJ4OiUwMng6JTAyeDolMDJ4OiUwMng6JTAyeF0uIAoAAAAAAAAAAAAAAAAAAAAAbDJk
+ZXZfdmlfZnNtOiBzZ2VfZXFpZCBbMHgleF0sIHNnZV9pcWlkIFsweCV4XSwgc2dlX2VxY3IgWzB4
+JXhdLCByc3Nfc3ogWzB4JXhdCgBsMmRldl92aV9mc206IGwyZGV2X2ZjLT5mbG93Y19uZXRfbDJk
+ZXZfbXR1IFsldV0sIG1iX3NjcmF0Y2ggWzB4JXhdLCBwb3J0IFsweCV4XQoAAAAAAAAAAAAAAAAA
+AABsMmRldl92aV9mc206IHZpaWQgWyVkXSwgdmlfZmMtPmZsb3djX3ZpX2ZsYWdzIFsweCV4XQoA
+AAAAAAAAAAAAbDJkZXZfdmlfZnNtOiBwZm4gWzB4JXhdLCB2Zm4gWzB4JXhdLCBsMmRldl9mYy0+
+Zmxvd2NfaWQgWzB4JXhdLCBscG9ydCBbMHgleF0sIHZpaWQgWzB4JXhdLCBmbGFncyBbMHgleF0K
+AAAAAAAAAGwyZGV2X3ZpX2ZzbTogRXJyb3IgZnJlZWluZyBWSSwgcmMgWzB4JXhdCgAAAAAAAGwy
+ZGV2X3ZpX2ZzbTogcGlkIFsweCV4XSwgdmlpZCBbMHgleF0sIG1iX2xvYyBbMHgleF0sIG1iX29y
+aWdbMHgleF0sIGwyZGV2X2ZsYWdzIFsweCV4XSwgcmMgWzB4JXhdCgAAAAAAAAAAAAAAAABjcGxf
+dHhfcGt0OiBpbnRmIFsweCV4XQoAAAAAAAAAAGVuY29kZSBoZXggc3RyaW5nOiBkbGVuIFslZF0K
+AAAAdGNwX3NlbmRfYW9wZW5fcmVxOiBjc2tfZmMtPmZsb3djX2lkIFsweCV4XSwgY3NrX2ZjLT5m
+bG93Y19zdGF0ZSBbMHgleF0sIGJ1ZmZlcmVkIFsldV0sIHJlc19jbnQgWzB4JXhdLCBpcV9pZHgg
+WzB4JXhdCgAAAAAAAAAAAABhb3Blbl9yZXE6IGh3X2xlX2ZpbHRlcl9jdHVwbGUgZmFpbGVkCgAA
+AAAAAAAAAABvZmxkX3RjcF9zZW5kX2FvcGVuX3JlcTogY3BsX3JlcS0+RmlsdGVyIFsweCUweF0s
+IGN0dXBsZXNbMF0gWzB4JXhdLCBjdHVwbGVzWzFdIFsweCV4XQoAAAAAAAAAAABjc29ja19hbGxv
+YzogdHhfY2ggWzB4JXhdLCBscG9ydCBbMHgleF0sIGNvb2tpZSBbJTA4eF0KAAAAAAAAAAAAY3Nv
+Y2tfYWxsb2M6IGF2YWlsYWJsZSBbJXVdLCBuY3NvY2sgWyV1XSwgcG9zOmF0aWQgWzB4JXhdLCBj
+c2tfZmMgWzB4JXhdLCBjc2tfZmMtPmZsb3djX2lkIFsweCV4XSwgc3BvcnQgWyV1XQoAAGZvaXNj
+c2lfcGVlcl9pbml0OiBjb25uX2F0dHItPmhkaWdlc3RfdG9fZGRwX3Bnc3ogWzB4JXhdCgAAAAAA
+AABmb2lzY3NpX3BlZXJfaW5pdDogY29ubl9mYy0+Zmxvd2NfZm9pc2NzaV9jb25uX2ZsYWdzIFsw
+eCV4XQoAAAAAdGFza19mcmVlOiB0cnlpbmcgdG8gZnJlZSBxdWV1ZWQgdGFzayBbMHgleF0hISEK
+AAAAAAAAAAAAAAAAAAAAAFdBVENIRE9HOiBObyB0ZW1wZXJhdHVyZSBzZW5zb3IgYXZhaWxhYmxl
+LgoAAAAAAFdBVENIRE9HOiBBY3RpdmF0aW5nCgAAAAAAAAAAAAAAV0FUQ0hET0cgLSBFbmFibGUg
+YWN0aW9uICV1IHRpbWUgJXUKAAAAAAAAAAAAAAAAV0FUQ0hET0cgLSBEaXNhYmxlIGFjdGlvbiAl
+dQoAAABXQVRDSERPRzogRGUtYWN0aXZhdGluZwoAAAAAAAAAAHNlc3Npb25fdW5ibG9jazogc2Vz
+c19mYy0+Zmxvd2NfaWQgWzB4JXhdLCBzZXNzX2ZjLT5mbG93Y19zdGF0ZSBbMHgleF0sIGNvbm5f
+ZmMtPmZsb3djX2lkIFsweCV4XSwgY29ubl9mYy0+Zmxvd2Nfc3RhdGUgWzB4JXhdLCBjc2tfZmMt
+PmZsb3djX2lkIFsweCV4XSwgY3NrX2ZjLT5mbG93Y19zdGF0ZSBbMHgleF0KAAAAAAAAAAAAAAAA
+AHNlc3Npb25fbGlua19ub3RpZnk6IHBvcnQgWzB4JXhdIGxpbmsgZG93biBwaW5nIHJlY292ZXJ5
+IGRpc2FibGVkLCB0cnlpbmcgdG8gcmVjb3ZlciBzZXNzaW9uIFsweCV4XSBjb25uIFsweCV4XQoA
+AAAAAAAAAAAAAAAAAAAAc2Vzc2lvbl9saW5rX25vdGlmeTogcG9ydCBbMHgleF0gbGluayB1cCBz
+ZXNzaW9uIFsweCV4XSBjb25uIFsweCV4XQoAAAAAAAAAAAAAAABmb2lzY3NpIGNvbm5fZmMgWzB4
+JXhdLCBmbG93Y19zY2hlZGNsIFsweCV4XSwgaW5nX2NoIFsweCV4XSwgZWdyX2NoIFsweCV4XQoA
+AAAAAGwyZGV2X25vdGlmeSB3aXRoIHVua25vd24gZmxhZyBbMHgleF0KAAAAAAAAAAAAAEZDb0Ug
+RkNCIGxpbmtkb3duOiBpb19yZXEgMHgleCV4IGlxaWQgMHgleCBmbG93aWQgMHgleCBvcCAweCV4
+CgBmY29lIG5vdGlmeSA6IFVwZGF0ZSBuZXcgRENCWCB2YWx1ZXMgVkkgc3RhdGUgMHgleCBwcmkg
+MHgleCBzY2hlZGNsIDB4JXggZGNieF9kb25lIDB4JXgKAAAAAAAAAABmY29lIG5vdGlmeSA6IEZD
+RiBmbG93aWQgMHgleCwgdWxwY2ggMHgleCAKAAAAAABuZXRfbDJkZXZfbm90aWZ5OiBFdmVudCBv
+biBsMmRldl9mYy0+Zmxvd2NfaWQgWzB4JXhdLCBwb3J0IFslZF0sIGV2ZW50IFsweCV4XSwgdWxw
+dHhjaCBbJXVdLCBjbGFzcyBbMHgleF0sIHByaW9yaXR5IFsweCV4XQoAAAAAAGZjb2Ugbm90aWZ5
+IDogRENCWCA6IHBvcnQgMHgleCwgcHJpb3JpdHkgMHgleCB1bHB0eGNoIDB4JXggY2xhc3MgMHgl
+eAoAAAAAAAAAAAAAY2hfY2xfcmF0ZVsldS8ldV06IGNhcHBlZCBkZWZpY2l0X2luY3IgZnJvbSBy
+ZXF1aXJlZCAldSB0byAldTsgcmF0ZSAldSAoZWZmICV1KSBkZWZpY2l0X21heCAldQoARkNvRSBG
+Q0YgdGltZXI6IGZsb3djIHN0YXRlIDB4JXgsIHBvcnQgMHgleCAsZmNmIDB4JXgsIGZsb3djX2lk
+IDB4JXgKAAAAAAAAAAAAAABjb3JlX3Byb2dyYW1fdGNiOiB0aWQgJSN4IHRfc3RhdGUgJSN4IHJj
+dl9hZHYgMHglMDh4IHJjdl9zY2FsZSAlI3ggdHhfbWF4ICUjeCByY3Zfbnh0ICUjeCBhdGlkICUj
+eAoAAAAAAAAAAAAAAAAACW9wdDAgJSN4JXggb3B0MiAlI3ggaXB2NiAlI3ggZmxhZ3NfdGltZXIg
+MHglMDh4CgAAAAAAAAAAAAAAAAAAAG9mbGRfY29ubmVjdGlvbl93cjogY29ubmVjdGlvbiB3aXRo
+IDUtdHVwbGUgbHAgMHglMDR4IGZwIDB4JTA0eCBsaXAgMHglMDh4JTA4eCBwaXAgMHglMDh4JTA4
+eCBmaWx0ZXIgMHglMDh4IGV4aXN0cyBAIExFIGluZGV4ICV1CgAAAAAAAAAAAAAAAAAAAG9mbGRf
+Y29ubmVjdGlvbl93cjogY29ubmVjdGlvbiB3aXRoIDUtdHVwbGUgbHAgMHglMDR4IGZwIDB4JTA0
+eCBsaXAgMHglMDh4IHBpcCAweCUwOHggZmlsdGVyIDB4JTA4eCBleGlzdHMgQCBMRSBpbmRleCAl
+dQoAAAAAAAAAb2ZsZF9jb25uZWN0aW9uX3dyOiBjb25uZWN0aW9uIHdpdGggNS10dXBsZSBscCAw
+eCUwNHggZnAgMHglMDR4IGxpcCAweCUwOHglMDh4IHBpcCAweCUwOHglMDh4IGZpbHRlciAweCUw
+OHgKAAAAAG9mbGRfY29ubmVjdGlvbl93cjogY29ubmVjdGlvbiB3aXRoIDUtdHVwbGUgbHAgMHgl
+MDR4IGZwIDB4JTA0eCBsaXAgMHglMDh4IHBpcCAweCUwOHggZmlsdGVyIDB4JTA4eAoAAAAAAAAA
+AAAAAABJUUZMSU5UIHBmbiAldSB2Zm4gJXU6IGlxZXNpemUgJXUgdG9vIHNtYWxsCgAAAABJUUZM
+SU5UIHBmbiAldSB2Zm4gJXU6IGlxaWQgJXUgdG9vIGxhcmdlIChtYXggJXUpCgAAAAAAAAAAAAAA
+AAAASVFGTElOVCBwZm4gJXUgdmZuICV1OiBpcWlkICV1IG5vdCBhbGxvY2F0ZWQKAAAASVFGTElO
+VCBwZm4gJXUgdmZuICV1OiBmbDBpZCAldSB0b28gbGFyZ2UgKG1heCAldSkKAAAAAAAAAAAAAAAA
+AElRRkxJTlQgcGZuICV1IHZmbiAldTogZmwwaWQgJXUgbm90IGFsbG9jYXRlZAoAAElRRkxJTlQg
+cGZuICV1IHZmbiAldTogZmwxaWQgJXUgdG9vIGxhcmdlIChtYXggJXUpCgAAAAAAAAAAAAAAAABJ
+UUZMSU5UIHBmbiAldSB2Zm4gJXU6IGZsMWlkICV1IG5vdCBhbGxvY2F0ZWQKAABJUUZMSU5UIHBm
+biAldSB2Zm4gJXU6IGZsMWlkICV1IGlzIHZhbGlkIGJ1dCBub3QgZmwwaWQgJXUKAAAAAAAASVFG
+TElOVCBwZm4gJXUgdmZuICV1OiBmbDFpZCAldSBpcyB2YWxpZCBidXQgaGVhZGVyIHNwbGl0IGZl
+YXR1cmUgaXMgbm90IGVuYWJsZWQKAAAAAAAAAAAAAAAAAAAASVEgcGZuICV1IHZmbiAldTogaXFp
+ZCAldSB0b28gbGFyZ2UgKG1heCAldSkKAAAASVEgcGZuICV1IHZmbiAldTogaXFpZCAldSBub3Qg
+YWxsb2NhdGVkCgAAAAAAAAAASVEgcGZuICV1IHZmbiAldTogZmwwaWQgJXUgZmwxaWQgJXUgYnV0
+IG5vdCBzdXBwb3J0ZWQKAAAAAAAAAAAAAGh3X3VscHR4X3dvcmthcm91bmRfcHIxNjk0OV9lbmFi
+bGVkX3BmOiBwZiAldSBlbmFibGVkICV1CgAAAAAAAABod191bHB0eF93b3JrYXJvdW5kX3ByMTY5
+NDlfZW5hYmxlZF92ZmlkOiB2ZmlkICV1IGVuYWJsZWQgJXUKAAAARVEgcGZuICV1IHZmbiAldTog
+Y3JlYXRpbmcgRVRIIGVxaWQgJXUgd2l0aCBwZW5kaW5nIFdSKHMpIChudW1fYnl0ZXMgJXUgYW5k
+IGZsYWdzIDB4JTA4eAoAAAAAAAAARVEgcGZuICV1IHZmbiAldTogY3JlYXRpbmcgQ1RSTCBlcWlk
+ICV1IHdpdGggcGVuZGluZyBXUihzKSAobnVtX2J5dGVzICV1IGFuZCBmbGFncyAweCUwOHgKAAAA
+AAAARVEgcGZuICV1IHZmbiAldTogZXFpZCAldSB0b28gbGFyZ2UgKG1heCAldSkKAAAARVEgcGZu
+ICV1IHZmbiAldTogZXFpZCAldSBub3QgYWxsb2NhdGVkCgAAAAAAAAAAcG9ydF9ibGlua19sZWRf
+cmVzdG9yZQoAAAAAAAAAAABwb3J0X2JsaW5rOiBibGlua2R1cj0weCV4IGJsaW5rX3JlZmNudAoA
+AAAAAAAAAABwb3J0X2JsaW5rOiAJYmxpbmtfcmVmY250PTB4JXgKAHBvcnRfYmxpbms6IAlibGlu
+a19yZWZjbnQ9MHgleAoAaHcgcmVnaXN0ZXIgb3BlcmF0aW9uIG5vdCBjb21wbGV0aW5nLCByZWcg
+MHglMDh4IG1hc2sgMHglMDh4IHZhbHVlIDB4JTA4eCAocmVnIDB4JTA4eCkKAAAAAAAAAAAATURJ
+TyBDTDQ1OiBmYWlsZWQgdG8gc2V0IHVwIE1NRCBhZGRyCgAAAAAAAAAAAAAATURJTzogZmFpbGVk
+IHRvIHJlYWQKAAAAAAAAAAAAAABNRElPIENMNDU6IGZhaWxlZCB0byBzZXQgdXAgTU1EIGFkZHIK
+AAAAAAAAAAAAAABNRElPOiBmYWlsZWQgdG8gd3JpdGUKAAAAAAAAAAAAAG1paV9hZHZfZmNbJXVd
+OiByY2FwcyAweCV4CgAAAAAAbWlpX2Fkdl9zcGVlZFsldV06IHJjYXBzIDB4JXgKAABtaWlfYW5y
+ZXN0YXJ0WyV1XTogYWNhcHMgMHgleAoAAG1paV9pbml0WyV1XTogYWNhcHMgMHgleAoAAAAAAAAA
+bWlpX3Bkb3duWyV1XTogcG93ZXJkb3duIGVuICV1CgBwb3J0WyV1OjB4JTAyeDoweCUwMnhdOiBs
+MWNmZywgcGNhcHMgMHgleCBhY2FwcyAweCV4IHJjYXBzIDB4JXgKAAAAAAAAAAAAAAAAAAAAAHBv
+cnRbJXU6MHglMDJ4OjB4JTAyeF06IGwxY2ZnLCBpbnZhbGlkIHJlcXVlc3QsIHBjYXBzIDB4JXgg
+YWNhcHMgMHgleCByY2FwcyAweCV4CgAAAAAAAAAAAAAAAAAAAHBvcnRbJXU6MHglMDJ4OjB4JTAy
+eF06IGwxY2ZnLCBtZGkgbi9hIHBjYXBzIDB4JXggYWNhcHMgMHgleCByY2FwcyAweCV4CgAAAAAA
+AAAAcG9ydFsldToweCUwMng6MHglMDJ4XTogbDFjZmcsIGNhbm5vdCBmb3JjZSBuby9tdWx0aXBs
+ZSBzcGVlZChzKSwgcGNhcHMgMHgleCBhY2FwcyAweCV4IHJjYXBzIDB4JXgKAAAAAAAAAAAAAAAA
+AGdldF9yZWZfdGFzazogcmVxdWVzdGVkIGNvb2tpZTogaGlnaCBbMHglMDh4XSwgbG93IFsweCUw
+OHhdCgAAAABnZXRfcmVmX3Rhc2s6IHRhc2sgY29va2llOiBoaWdoIFsweCUwOHhdLCBsb3cgWzB4
+JTA4eF0KAAAAAAAAAAAAZ2V0X3JlZl90YXNrOiBpc3Rhc2stPmZsb3djX2lkIFsweCV4XSwgaXN0
+YXNrX2ZjLT5mbG93Y19zdGF0ZSBbJXVdCgAAAAAAAAAAAAAAAABnZXRfcmVmX3Rhc2s6IHRhc2sg
+bm90IGZvciBjb29raWUgJWx4CgAAAAAAAAAAAABjaG5ldF9sMnRfdXBkYXRlOiBsMmRldl9mYyBb
+MHgleF0sIGwyZGV2X2ZjLT5mbG93Y19pZCBbJXVdIGwyZGV2X2ZjLT5mbG93Y19mbGFncyBbMHgl
+eF0sIGludGYgWzB4JXhdCgAAAAAAAAAAAAAAY2huZXRfbDJ0X3VwZGF0ZTogbDJkZXZfZmMtPmZs
+b3djX2lkIFsldV0gYWxyZWFkeSBzY2hlZHVsZWQKAAAAAGNobmV0X2wydF91cGRhdGU6IGluIGRl
+bGF5ZWRfcHJvY2Vzc2luZywgbDJ0ZW50IFslMDh4XQoAAAAAAAAAAABwcm9jZXNzX2RoY3Bfb3B0
+czogcm9vdCBwYXRoIGxlbiBbJWRdIGJ5dGVzCgAAAABuZXRpZl9wcm9jZXNzX2RoY3Bfb3B0czog
+bDJkZXZfZmMtPmZsb3djX2lkIFsweCV4XSwgTVNHX1RZUEUgWyVkXSwgZGhjdHh0LT5zdGF0ZSBb
+JWRdCgAAAAAAAAAAAABpY21wX3JlY3Y6IGwyZGV2X2ZjLT5mbG93Y19pZCBbMHgleF0sIHBpZCBb
+MHgleF0sIGljbXAgdHlwZSBbMHgleF0KAAAAAAAAAAAAAAAAAGZjX3NlbmRfYWxsb2NfY3BsOiBm
+YWlsZWQgdG8gc2V0dXAgZmlsdGVyIGN0dXBsZQoAAAAAAAAAAAAAAAAAAABmY29lX2NvbXB1dGVf
+Y3R1cGxlIHZsYW4gJXggdmlpZCAleCBwb3J0ICV4IG1wc19pZHggJXgKAAAAAAAAAAAAZmNvZV9j
+b21wdXRlX2N0dXBsZSAweCV4OiV4CgAAAABBQlRTIEFDQyBhd2FpdGluZyBQUkxJIFJzcDogZmxv
+d2NfaWQgMHgleCBveF9pZCAweCV4IHJ4X2lkIDB4JXggaXFpZCAweCV4CgAAAAAAAGFycF9yZWN2
+OiBpcGlkIFsweCV4XSwgaW5fYWRkci5hZGRyIFsweCV4XSwgc2lwIFsweCV4XSwgcmlwIFsweCV4
+XSwgYXJwX29wIFsweCV4XQoAAAAAAAAAAAAAAAAAAGNobmV0X2FycF9yZWN2OiBpcCBjb25mbGlj
+dCBkZXRlY3RlZAoAAAAAAAAAAAAAAGNobmV0X2FycF9yZWN2OiBwaWQgWyV1XSwgdmxhbiBbMHgl
+eF0sIGFycCBvcCBbMHgleF0sIHNpcCBbMHgleF0sIHJpcCBbMHgleF0KAAAAY3NvY2tfcGVlcl9j
+bG9zZTogY3NrX2ZjLT5mbG93Y19pZCBbMHgleF0sIGNza19mYy0+dGNiX2ZjLT5mbG93Y19pZCBb
+MHgleF0sIGNza19mYy0+Zmxvd2Nfc3RhdGUgWzB4JXhdLCBjc2tfZmMtPmZsb3djX2Nzb2NrX3Rj
+Yl9mYy0+Zmxvd2Nfc3RhdGUgWzB4JXhdCgAAAAAAAAAAAGNzb2NrX3BlZXJfY2xvc2U6IGNza19m
+Yy0+Zmxvd2NfaWQgWzB4JXhdLCBjc2tfZmMtPmZsb3djX3N0YXRlICBbMHgleF0KAAAAAAAAAAAA
+dGNiX2ZjLT5mbG93Y19pZCBbMHglMDZ4XSwgdGNiX2ZjLT5mbG93Y190eXBlIFsweCV4XSwgY3Bs
+b3AgWzB4JXhdIAoAAAAAAAAAAAAAAABjaF9yYXRlWyV1XTogY2FwcGVkIHRpY2sgZnJvbSByZXF1
+aXJlZCAldSB0byBzdXBwb3J0ZWQgJXU7IHJhdGUgJXUgKGVmZiAldSkgZGVmaWNpdF9pbmNyICV1
+IHRpY2sgJXUKAAAAAAAAAAAAAAAAcGt0c2NoZWRfY2hfcmxbJXVdOiBjaGFubmVsIHJsIG5vdCBh
+dmFpbGFibGUgaW4gY29uanVuY3Rpb24gd2l0aCBmbG93IHNoYXBpbmcKAABwa3RzY2hlZF9jaF9y
+bFsldV06IHJhdGUgJXUgbWF4ICV1CgAAAAAAAAAAAAAAAABwa3RzY2hlZF9jbF93cnJbJXU6JXVd
+OiB3ZWlnaHQgJXUgd2VpZ2h0ZWRfbWFzayAweCV4CgAAAAAAAAAAAAAAZXFfcGFyYW1zWzB4JXg6
+MHgleF06IGRtYXEgMHgleCByZWFkICV1IHBmICV1IGVxaWRfYXBpICV1IHJldCAlZAoAAAAAAAAA
+AAAAAAAAAABNQyBDTEsgc2V0dGluZyBmYWlsZWQ6IFBMTF9NX0xPQ0sgbmV2ZXIgdG9nZ2xlZAoA
+AAAAAAAAAAAAAAAAAAAAd2FpdF9mb3JfY2FsaWJfZG9uZTogcmV0ICVkIGluICV1IGF0dGVtcHRz
+CgAAAAAAbWFsbG9jX2RlY1sldV06IHN0YXJ0ICVwIGVuZCAlcCBuZXh0ICVwIHNpemUgMHgleCBh
+bGlnbWVudCAweCV4IHAgJXAKAAAAAAAAAAAAAABtYWxsb2NfaW5jWyV1XTogc3RhcnQgJXAgZW5k
+ICVwIG5leHQgJXAgc2l6ZSAweCV4IGFsaWdtZW50IDB4JXggcCAlcAoAAAAAAAAAAAAAAGxlIGNv
+bmZpZ3VyYXRpb246IGhhc2ggbW9kZSByZXF1aXJlcyBhdCBsZWFzdCAxNiBlbnRyaWVzLCBuaGFz
+aCAldQoAAAAAAAAAAAAAAAAAbGUgY29uZmlndXJhdGlvbjogaGFzaCBtb2RlIHJlcXVpcmVzIGF0
+IGVudHJpZXMgdG8gYmUgYSBwb3dlciBvZiAyLCBuaGFzaCAldQoAAABsZSBjb25maWd1cmF0aW9u
+OiByZXF1ZXN0ZWQgJXUgdGNhbSBlbnRyaWVzIGJ1dCBvbmx5ICV1IGF2YWlsYWJsZQoAAAAAAAAA
+AAAAAAAAAGxlIGNvbmZpZ3VyYXRpb246IHRjYW0gcmVnaW9ucyBtdXN0IGhhdmUgbXVsdGlwbGUg
+b2YgMzIgZW50cmllcywgbnJvdXRlICV1IG5jbGlwICV1IG5maWx0ZXIgJXUgbnNlcnZlciAldQoA
+AAAAAABvZmxkIHRwIHRpbWVyIHNldHRpbmdzIFRQX01TTCAweCUwOHggVFBfUlhUX01JTiAweCUw
+OHggVFBfUlhUX01BWCAweCUwOHggVFBfUEVSU19NSU4gMHglMDh4IFRQX1BFUlNfTUFYIDB4JTA4
+eAoAICAgICAgICAgICAgICAgICAgICAgICBUUF9LRUVQX0lETEUgMHglMDh4IFRQX0tFRVBfSU5U
+VkwgMHglMDh4IFRQX0lOSVRfU1JUVC5pbml0X3NydHRfbWF4cnR0IDB4JTA0eCBUUF9JTklUX1NS
+VFQuaW5pdF9zcnR0X2luaXRzcnR0IDB4JTA0eCBUUF9GSU5XQUlUMl9USU1FUiAweCUwOHgKAAAA
+AAAAAAAAAABjb25maWd1cmF0aW9uIGZpbGUgcGFyc2VyIGVuY291bnRlcmVkIGVycm9yIEAgbGlu
+ZSAldToKAAAAAAAAAAAASE9TVCBQQUdFX1NJWkUgWzB4JTBseF0gdG9vIHNtYWxsLCBtaW4gWzB4
+JTBseF0gcmVxdWlyZWQKAAAAAAAAAHBhZ2Ugc2l6ZSBbJWx1XSBtaXNtYXRjaAoAAAAAAAAAUEFH
+RSBzaXplICVsdSB1bnN1cHBvcnRlZCwgZGRwIGRpc2FibGVkCgAAAAAAAAAASG9zdCBwYWdlX3Np
+emUgJWx1LCBkZHBfaWR4ICV1CgBGQ29FIEREUCBpbml0OiBmY29lIGxsaW1pdCAweCV4LCBmY29l
+IHVsaW1pdCAweCV4IGdibCBsbGltaXQgMHgleCBnYmwgdWxpbWl0IDB4JXggcGNic3ogJXgKAAAA
+AABGQ29FIEREUCBpbml0OiBmY29lIHBwb2Qgb2ZmIDB4JXgsIGZjb2Ugc3QgcHBvZCBhZGRyIDB4
+JXggZmNvZSBudW0gcHBvZHMgMHgleAoAAGZjb2UgeGNoZyBtZ3IgaW5pdDogTnVtYmVyIG9mIGV4
+Y2hhbmdlcyBmb3IgRkNvRSBpcyAleAoAAAAAAAAAAABRU0ZQIG1vZHVsZSB1bnBsdWcgLSByZWlu
+aXRpYWxpemluZyByeF9sb3MgIHRvIDB4ZmYKAAAAAAAAAAAAAAAAZ3Bpb19xc2ZwX21vZHVsZV91
+cGRhdGU6IGNoYW5nZWQgcnhfbG9zIGZyb20gMHgleCB0byAweCV4CgAAAAAAAGdwaW9fcXNmcF9t
+b2R1bGVfdXBkYXRlOiBjaGFuZ2VkIHR4X2RpcyBmcm9tIDB4JXggdG8gMHgleAoAAAAAAABNQUMg
+ZmFpbGVkIHRvIHJlc3luYyB0eAoAAAAAAAAAAENhbGN1bGF0aW9uIG91dCBvZiBib3VuZHMgZnVy
+aW5nIGluaXQ6ICUjeCAlI3ggJSN4CgAAAAAAAAAAAAAAAABod19zZ2VfbWFtZW1fY2FsYzogZW5j
+b3VudGVyZWQgZXJyb3IgJWQKAAAAAAAAAABzZ2UgcmVxdWlyZSBuZXEgJXUgbmlxICV1IHJvdW5k
+aW5nIHRvICV1ICV1CgAAAABod19lZGNfYmlzdFsldV06IGJpc3RfY21kWzB4JTA4eF0gYWRkciAw
+eCV4IGxlbiAweCV4CgAAAAAAAAAAAAAAaHdfZWRjX2Jpc3RbJXVdOiBkb25lLCBlbmNvdW50ZXJl
+ZCAldSBlcnJvcnMgb24gZmlyc3QgYW5kICV1IGVycm9ycyBvbiBzZWNvbmQgYXR0ZW1wdAoAAAAA
+AAAAAAAAVGVtcGVyYXR1cmUvVm9sdGFnZSBTZW5zb3I6IENvcmUgY2xvY2sgJWQgPiA1MDA7IHVz
+aW5nIDUwMCB0byBzdGF5IGluIGNvbXBsaWFuY2Ugd2l0aCBoYXJkd2FyZS4KAAAAAAAAAAAAAAAA
+AAAAAGZvaXNjc2lfYnllOmwyZGV2X2ZjLT5mbG93Y19pZCBbMHgleF0sIGwyZGV2X2ZjLT5mbG93
+Y19wY2llX3BmbiBbMHgleF0sIGwyZGV2X2ZjLT5mbG93Y19wY2llX3ZmbiBbMHgleF0sIHBvcnQg
+WzB4JXhdCgAAAAAAAAAAAAAAZm9pc2NzaV9ieWU6dmxhbmRldl9mYy0+Zmxvd2NfaWQgWzB4JXhd
+LCB2bGFuZGV2X2ZjLT5mbG93Y19wY2llX3BmbiBbMHgleF0sIHZsYW5kZXZfZmMtPmZsb3djX3Bj
+aWVfdmZuIFsweCV4XSwgcG9ydCBbMHgleF0KAAAAAABpbml0X3BvcnRbJXVdOiAgbGluayBzdGF0
+dXMgMHgleCBtb2RfdHlwZSAweCV4IHZpX2VuX2NudCAldSByeF9sb3MgJXUgaHNzX3NpZ2RldCAl
+dSBwb3J0X2NmZy5zaWdkZXQgJXUKAAAAAAAAAAAAaW5pdF9wb3J0WyV1XTogIGxpbmsgc3RhdHVz
+IDB4JXgKAAAAAAAAAAAAAAAAAAAAcnhjZmdfdnBkIHBvcnQgJXUgbGFuZSAldSBwdHlwZSAldSA9
+PiBpZHg9ICV1ID0+IHJ4Y2ZnID0gJSN4CgAAAFJhbmdlIGNhbGM6IEF2ZXJhZ2VkICUjeCBidXQg
+aWdub3JlZCB2YWx1ZSAlI3ggKGl0ZXJhdGlvbiAldSkKAABNQyBjYWxpYnJhdGlvbiBmYWlsZWQ6
+IERGSSBpbml0IG5vdCBnb2luZyB0byAwCgBNQyBjYWxpYnJhdGlvbiBmYWlsZWQ6IERGSSBpbml0
+IG5vdCBjb21wbGV0aW5nCgBNQyBjYWxpYnJhdGlvbiBmYWlsZWQ6IENhbGlicmF0aW9uIGRpZG4n
+dCBjb21wbGV0ZS4KAAAAAAAAAAAAAAAATUMgY29tbWFuZCBmYWlsZWQgdG8gY29tcGxldGUob3Bj
+b2RlICUjeCBjYWRkciAlI3ggYmFkZHIgJSN4IGRlbGF5ICVkKQoAAAAAAAAAAABwZm5fYml0bWFw
+IDB4JXgKAAAAAAAAAAAAAAAAAAAAAGJhZCBtYWlsYm94IGNtZDogcGZuIDB4JXggdmZuIDB4JXg7
+IG9wY29kZSAweCV4ID4gTEFTVEMyRSAweCV4CgBtYWlsYm94IGNtZCBub3QgeWV0IHN1cHBvcnRl
+ZDogcGZuIDB4JXggdmZuIDB4JXg7IG9wY29kZSAweCV4CgAAYmFkIG1haWxib3ggY21kOiBwZm4g
+MHgleCB2Zm4gMHgleDsgb3Bjb2RlIDB4JXggaXMgdmFsaWQgcG9zdCBkZXZpY2UgaW5pdCBvbmx5
+CgBiYWQgbWFpbGJveCBjbWQ6IHBmbiAweCV4IHZmbiAweCV4OyBvcGNvZGUgMHglMDJ4IHJhbWFz
+ayAweCV4IGNtZCByYW1hc2sgMHgleAoAAGJhZCBtYWlsYm94IGNtZDogcGZuIDB4JXggdmZuIDB4
+JXg7IG9wY29kZSAweCUwMnggbGVuMTYgMHgleCB2ZXJzdXMgZXhwZWN0ZWQgbGVuMTYgMHgleAoA
+AAAAAAAAAGluc3VmZmljaWVudCBjYXBzIHRvIHByb2Nlc3MgbWFpbGJveCBjbWQ6IHBmbiAweCV4
+IHZmbiAweCV4OyByX2NhcHMgMHgleCB3eF9jYXBzIDB4JXggcmVxdWlyZWQgcl9jYXBzIDB4JXgg
+d19jYXBzIDB4JXgKAAAAAAAAAAAAaW5zdWZmaWNpZW50IGNhcHMgdG8gcHJvY2VzcyBtYWlsYm94
+IGNtZDogcGZuIDB4JXggdmZuIDB4JXg7IHJfY2FwcyAweCV4IHd4X2NhcHMgMHgleCByZXF1aXJl
+ZCByX2NhcHMgMHgleCB3X2NhcHMgMHgleAoAAAAAAAAAAABod19wb3dlcl9wcmVwOiBWREQ9Tk9O
+RSBidXQgVkNTPSVkCgAAAAAAAAAAAAAAAABod19wb3dlcl9wcmVwOiB1bnN1cHBvcnRlZCBleHRl
+cm5hbCBhZGp1c3RhYmxlIHBvd2VyIHJlZ3VsYXRvcnMgVkREPSVkLCBWQ1M9JWQKAGh3X3Bvd2Vy
+X3ByZXA6IHVuc3VwcG9ydGVkIFZERD0lZAoAAAAAAAAAAAAAAAAAAGh3X3Bvd2VyX3ByZXA6IHVu
+c3VwcG9ydGVkIFZDUz0lZAoAAAAAAAAAAAAAAAAAAGh3X3Bvd2VyX3ByZXA6IGkyYyB3cml0ZSBl
+cnJvciwgVkREPSVkLHJldD0lZAoAAGh3X3Bvd2VyX3ByZXA6IHVuc3VwcG9ydGVkIFZERD0lZAoA
+AAAAAAAAAAAAAAAAAGh3X3Bvd2VyX3ByZXA6IGkyYyB3cml0ZSBlcnJvciwgVkNTPSVkLHJldD0l
+ZAoAAGh3X3Bvd2VyX3ByZXA6IHVuc3VwcG9ydGVkIFZDUz0lZAoAAAAAAAAAAAAAAAAAAFZQRCBy
+ZWdpb24gaXMgdG9vIHNtYWxsIChTRVJDRkdfU1JfUEZOVlBEU0laRSAweCV4KQoAAAAAAAAAAAAA
+AABjZjogZmFpbGVkIHRvIGFsbG9jYXRlZCBtZW1vcnkgZm9yIGNvbmZpZ3VyYXRpb24gZmlsZSwg
+cmV0ICVkCgAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAA
+AAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAA
+AAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAA
+AAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAA
+AAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAA
+AAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAA
+AAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAA
+AAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAA
+AAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAA
+AAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAA
+AAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAA
+AAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAggAAASAAAAAAAAAAggAA
+AQAAAAAAAAAAgAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAABoCAAAAAAAAAAAAAAAAA
+gAAAAAAAAAAAAQAAAAAQAAAAAAAAAAMAAAAAAAAAAAAAAAMAAAAAAAAAAAAAEAAAAAAAAAAIAAAA
+AAIAAAAAAAAAAAAgAAAAAAAAAAAAAAABAAOAAAAAAAAAAAAAAAIAAAAAAAAAAAAgA4AAAAAAAAAA
+AAAQAoAAgAAAAAAAAACAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAQAAAAAAAAAAgAAAAAAAA
+AAAAAACAAAKAAAAAAAAAAAAAAAKAAAAAAAAAAACAAAIAAAAAAAAAAAAAAAIAAAAAAAAAAAAAAAKA
+AAAAAAAAAAABAAMAAAAAAAAAAAAAAoMAAAAAAAAAAAAQAoAAAAAAAAAAAAAwAAMAAAAAAAAIAAAw
+AAMAAAAAAAAAAAAwBYMAAAAAAAAIAAAwBYMAAAAAAAAAAAAwBIMAAAAAAAAIAAAwBIMAAAAAAAAA
+AAAwAwMAAAAAAAAIAAAwAwMAAAAAAAAAAAA4AwMAAAAAAAAAAAA4BYMAAAAAAAAAAAA4BIMAAAAA
+AAAAAAA4AAMAAAAAAAAAAAA0BoIAAAAAAAAAAAA8A4IAAAAAAAAAAAA8AAMAAAAAAAAIAAA8AAMA
+AAAAAAAAAAA8BIMAAAAAAAAAAAA8BQMAAAAAAAAAAAA9BAMAAAAAAAAAAAA8A4MAAAAAAAAAAAAs
+AAIAAAAAAAAAAAAsBYIAAAAAAAAAAAAsBQIAAAAAAAAAAAAQBoAAAAAAAAAAAAAQBoKAAAAAAAAA
+AAAADoIAAAAAAAAAAAAQB4KAAAAAIAAAAAAAB4IAAAAAIAAAAAAQBwKAAAAAAAAAAAAQBwKAAAAA
+AAAAAAAQBwKAAAAAAAAAAAAABwIAAAAAIAAAAAAQF4MAAAAAAAAIAAAQF4MAAAAAAAAIAAAQAAAA
+AAAAAAAAAAAQBgOAAAAAAAAAAAAADgMAAAAAAAAAAAAQBgNAAAAAAAAAAAAQBgMAAAAAAAAAAAAQ
+BgAAAAAAAAAAAAAABgOAAAAAAAAAAAAABgMAAAAAAAAAAAAADgIAAAAAAAAAAAAADgIAAAAAAAAA
+AAAQBgIAAAAAAAAAAAAQBgIAAAAAAAAAAAAQBgKAAAAAAAAAAAAQBgKAAAAAAAAAAAAAAAIAAAAA
+AAAAAAAAAAAAAAAAAAAAAAAAAAACAAAAAAAAAAAAEAMAAAAAAAAIAAAAAAAAAAAAAAAAAAD/////
+////////////////////////////////////////////////////////////////////////////
+////////////////////////////////////////////////////////////////////////////
+////////////////////////////////////////////////////////////////////////////
+////////////////////////////////////////////////////////////////////////////
+////////////////////////////////////////////////////////////////////////////
+////////////////////////////////////////////////////////////////////////////
+////////////////////////////////////////////////////////////////////////////
+////////////////////////////////////////////////////////////////////////////
+////////////////////////////////////////////////////////////////////////////
+//////////////////////////////////////////////8AAAAgAAAAAMAAAAAAAAAgAAAAAOAA
+AAAAAAAAAgAAAAAAAABAAAAAAAAAAAAAAAAAAAEhAAAAAAAAAAAAAQEgAAAAAAAAAAAAAAIAAAAE
+AAQAAAAABoAAAAQAAAAAAAAAgABAAAAAAAACAAAAgAAgAAAAAAACAAAIgAAAAAAAQAAAAAABAEAA
+AAAAAAAAAAABAEIAAAAAAAAAAAAAACAAAAAAAAAAAAACECAAAAAAAAAAAAACDAIAAAAAAAAAAAAA
+hQIAAAAEAAAAAAAAgEIAAAAAAAAAAACAgEIAQAAAAAAAAACAgEIAAAAAAAAAAAACACIAAAAAAAAA
+AAAAgEEAAAAAAAAAAACAgEEAAAAAAAAAAAAAgEGAAAAAAAAAAACAgEGAAAAAAAAAAAACACEAAAAA
+AAAAAAAAECCAAAAAAAAAAAACJQCAAAAAAAAAAAAABQAAAAAAAAAAAAAIiASAAAAAAAAAAAAIiASA
+AAAAAAAAAAAIogCAAAAAAAAAAAAIogCAAAAAAAAAAAAIowCAAAAAAAAAAAAIowCAAAAAAAAAAAAI
+pICAAAAAAAAAAAAIpICAAAAAAAAAAAAEpIDAAAAAAAAAAAAEogDAAAAAAAAAAAAEowDAAAAAAAAA
+AAAEiATAAAAAAAAAAAAACQGAAAAAAAAAAAACDACAAAAAAAAAAAAAiATAAAAAAAAAAAAAiASAAAAA
+AAAAAAACCwCAAAAAAAAAAAAAioCAAAAAAAAAAAAAC4CAAAAAAAAAAAAAjACAAAAAAAAAAAACIBCA
+AAAAAAAAAAACCgCAAAAAAAAAAAACCoCAAAAAAAAAAAAACQKAAAAAAAAAAAAAAQEAAAAAAAAAAAAA
+AQCAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAACAAAAAAAAAAAAAAIEQAAAAAAAAAAAAAIEIAAAAAAAA
+AAAAAIEEAAAAAAAAAAAAAIGAAAAAAAAAAAAAgADAAAAAAAAAAAAAgACgAAAAAAAAAAAAAAgAAAAA
+AAAAAAAAgYAAAAAAAAAAAAAAgYCAAAAAAAAAAAAAiYCAAAAAAAAAAAAAiYDAAAAAAAAAAAAAAYIA
+AAAAAAAAAAACAYAAAAAAAAAAAAACAYCAAAAAAAAAAAAAQYGAAAAAAAAAAAACAYGAAAAAAAAAAAAA
+SYGAAAAAAAAAAAACCYGAAAAAAAAAAAACAYEAAAAAAAAAAAAAQYEAAAAAAAAAAAAgAAAAAAAAAAAA
+AAAQAAACAAAAAAAAAAAQAAAAAAAAAAAAAAAAgADAAAAAAAAAAAAAAAAAAAAAAAAAAAD/////////
+////////////////////////////////////////////////////////////////////////////
+////////////////////////////////////////////////////////////////////////////
+////////////////////////////////////////////////////////////////////////////
+////////////////////////////////////////////////////////////////////////////
+////////////////////////////////////////////////////////////////////////////
+////////////////////////////////////////////////////////////////////////////
+////////////////////////////////////////////////////////////////////////////
+////////////////////////////////////////////////////////////////////////////
+////////////////////////////////////////////////////////////////////////////
+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
+AAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAA
+AAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAA
+AAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAA
+AAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAA
+AAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAA
+AAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAA
+AAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAA
+AAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAA
+AAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAA
+AAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAA
+AAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAA
+AAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAA
+AAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAA
+AAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAIADAABAAA
+AAIAAAIAJiRAAAAAAABHMAAGAyACtwAIAgHAJEAAAAAACAMAAAIGoAq3AAgCAcYkRgAAAAAAAAAA
+AAAAAAIAAAIAJiRAAAAAAAAgUAAEAAAAAAAAAgEmJEYAAAAACGAgAIQAAAAAAAAGACAAQAAAAAAI
+YCAAhAAAAAIBMAIAJiVAAAAAAAhgAAAEAIQBggQEAgHGJMIAAAAAAcAAAAJDgAMCDIgCAaYkQAAA
+AAAIAGAABAAAAAIAAAIAJiRAAAAAAAgAYAAEAAAAAAAABAEgAEAAAAAAAAAAAAAAAAAAAAAEASAA
+QAAAAAAECBSABgoAAAcBTAIgpiZAAAAAAAiAAAAGAIQBgAQIAgGmJMAAAAAAAEAAAAIAoAAAQAgC
+AKYkRgAAAAAAAAAAAAAAAAIAAAIAJiRAAAAAAAQAAAAChAADAooIAgTAJEAAAAAAAAAAAAAAAAAC
+AAAGASBIQAAAAAAAIFAABAAAAAIAAAIAJiRAAAAAAAhgIACEAAAAAgAABgAmBEAAAAAACGAgAIQA
+AAACAAACASYlQAAAAAAIYAAABACEAYAEBAIBxiTAAAAAAAgAYAAEAAAAAAAABAEgAEAAAAAAAACA
+ACAAAAAAAAACACZMcAAAAAAAAIAAIAAAAAAAAAYBIAAAAAAAAAAAgAAgAAAAAAAAAgAmTHAAAAAA
+AACAACAAAAAAAAAGASAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAA
+AAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAA
+AAAAAAAACcAAAAi0jAQJFgQCBMYkQAAAAAAJwAAADDSABAkWBAIExiRAAAAAAAQIdIBMCQAAUABc
+AmHmJEAAAAAADAh0gEQAAABSAEACYSYkQAAAAAAIAhCgBAAAAAABAAIAJiRAAAAAAAgCEKAEAAAA
+AAEAAgAmJEAAAAAABAh0AEIBAAAHAIgCYMYkQAAAAAANyBQAAgkAAARAnAJg5iRAAAAAAAnIEIAC
+tIAEApSIAmXGJEAAAAAADch0AEy0gAMAlIgCYKYkwAAAAAAFyHQASDSQAwCUiAJgpiTAAAAAAAhH
+AAAEAAAAAAAAAgEgJEAAAAAACEcAAAQAAAAAAAACBEAkQAAAAAAARyABDAchwrcACAIAwCREAAAA
+AABHIAEMByHCtwAIAgDAJEQAAAAAAEcgAQwHIcK3AAgCAMAkRAAAAAAAACABCACEAAVAiAIBxiTA
+AAAAAAAAIAEIAIQABUCIAgHGJMAAAAAAAAAgAQgAhAAFQIgCAcYkwAAAAAAAACABhoIAAQLAiAID
+xiTAAAAAAAAAIAGCggAAAsCIAgPGJMAAAAAACcAgAYKkgAEFQIgCAcYkwAAAAAAIAAAADACEAAVA
+iAIBxiTAAAAAAAAAIAGGggABAsCIAgPGJMAAAAAAAAAgAYKCAAAAwIgCAqYkwAAAAAAJwCABgqSA
+AQVAiAIBxiTAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAA
+AAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAABBAUAAAAA
+AAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAA
+AAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAA
+AAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAA
+AAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAA
+AAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAA
+AAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAA
+AAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAA
+AAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAA
+AAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAA
+AAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAA
+AAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAA
+AAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAA
+AAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAA
+AAAAAAAAAAAAAAAAAAAAAAAAAAAAAABbZ2xvYmFsXQpyc3NfZ2xiX2NvbmZpZ19tb2RlPWJhc2lj
+dmlydHVhbApyc3NfZ2xiX2NvbmZpZ19vcHRpb25zPXRubG1hcGVuLGhhc2h0b2VwbGl0eix0bmxh
+bGxsa3AKcGxfdGltZW91dF92YWx1ZT0yMDAKcmVnWzB4MTAwOF09MHg0MDgxMC8weDIxYzcwCnJl
+Z1sweDEwMGNdPTB4MjIyMjIyMjIKcmVnWzB4MTBhMF09MHgwMTA0MDgxMApyZWdbMHgxMDQ0XT00
+MDk2CnJlZ1sweDEwNDhdPTY1NTM2CnJlZ1sweDEwNGNdPTE1MzYKcmVnWzB4MTA1MF09OTAyNApy
+ZWdbMHgxMDU0XT05MjE2CnJlZ1sweDEwNThdPTIwNDgKcmVnWzB4MTA1Y109MTI4CnJlZ1sweDEw
+NjBdPTgxOTIKcmVnWzB4MTA2NF09MTYzODQKcmVnWzB4MTBhNF09MHhhMDAwYTAwMC8weGYwMDBm
+MDAwCnJlZ1sweDEwYThdPTB4NDAyMDAwLzB4NDAyMDAwCmJhcjJ0aHJvdHRsZWNvdW50PTUwMApz
+Z2VfdGltZXJfdmFsdWU9NSwxMCwyMCw1MCwxMDAsMjAwCnJlZ1sweDExMjRdPTB4MDAwMDA0MDAv
+MHgwMDAwMDQwMApyZWdbMHgxMTMwXT0weDAwZDVmZmViCnJlZ1sweDExM2NdPTB4MDAwMmZmYzAK
+cmVnWzB4N2RjMF09MHgwNjJmODg0OQpmaWx0ZXJNb2RlPXNydnJzcmFtLGZyYWdtZW50YXRpb24s
+bXBzaGl0dHlwZSxwcm90b2NvbCx2bGFuLHBvcnQsZmNvZQpmaWx0ZXJNYXNrPXByb3RvY29sLGZj
+b2UKdHBfcG1yeD0zMAp0cF9wbXJ4X3BhZ2VzaXplPTY0Swp0cF9ucnhjaD0wCnRwX3BtdHg9NTAK
+dHBfcG10eF9wYWdlc2l6ZT02NEsKdHBfbnR4Y2g9MApyZWdbMHg3ZDA4XT0weDAwMDAwODAwLzB4
+MDAwMDA4MDAKcmVnWzB4MTljMDRdPTB4MDA0MDAwMDAvMHgwMDQwMDAwMApbZnVuY3Rpb24iMCJd
+Cm52Zj0xNgp3eF9jYXBzPWFsbApyX2NhcHM9YWxsCm52aT0xCm5pcWZsaW50PTgKbmV0aGN0cmw9
+OApuZXE9MTYKbmV4YWN0Zj04CmNtYXNrPWFsbApwbWFzaz0weDEKW2Z1bmN0aW9uIjEiXQpudmY9
+MTYKd3hfY2Fwcz1hbGwKcl9jYXBzPWFsbApudmk9MQpuaXFmbGludD04Cm5ldGhjdHJsPTgKbmVx
+PTE2Cm5leGFjdGY9OApjbWFzaz1hbGwKcG1hc2s9MHgyCltmdW5jdGlvbiIyIl0KbnZmPTE2Cnd4
+X2NhcHM9YWxsCnJfY2Fwcz1hbGwKbnZpPTEKbmlxZmxpbnQ9OApuZXRoY3RybD04Cm5lcT0xNgpu
+ZXhhY3RmPTgKY21hc2s9YWxsCnBtYXNrPTB4NApbZnVuY3Rpb24iMyJdCm52Zj0xNgp3eF9jYXBz
+PWFsbApyX2NhcHM9YWxsCm52aT0xCm5pcWZsaW50PTgKbmV0aGN0cmw9OApuZXE9MTYKbmV4YWN0
+Zj04CmNtYXNrPWFsbApwbWFzaz0weDgKW2Z1bmN0aW9uIjQiXQp3eF9jYXBzPWFsbApyX2NhcHM9
+YWxsCm52aT0yOApuaXFmbGludD0xNzAKbmV0aGN0cmw9MTAwCm5lcT0yNTYKbmV4YWN0Zj00MApj
+bWFzaz1hbGwKcG1hc2s9YWxsCm5ldGhvZmxkPTEwMjQKbnJvdXRlPTMyCm5jbGlwPTMyCm5maWx0
+ZXI9NDk2Cm5zZXJ2ZXI9NDk2Cm5oYXNoPTEyMjg4CnByb3RvY29sPW5pY192bSxvZmxkLHJkZHAs
+cmRtYWMsaXNjc2lfaW5pdGlhdG9yX3BkdSxpc2NzaV90YXJnZXRfcGR1CnRwX2wydD0zMDcyCnRw
+X2RkcD0yCnRwX2RkcF9pc2NzaT0yCnRwX3N0YWc9Mgp0cF9wYmw9NQp0cF9ycT03CltmdW5jdGlv
+biI1Il0Kd3hfY2Fwcz1hbGwKcl9jYXBzPWFsbApudmk9NApuaXFmbGludD0zNApuZXRoY3RybD0z
+MgpuZXE9NjQKbmV4YWN0Zj00CmNtYXNrPWFsbApwbWFzaz1hbGwKbnNlcnZlcj0xNgpuaGFzaD0y
+MDQ4CnRwX2wydD0xMDI0CnByb3RvY29sPWlzY3NpX2luaXRpYXRvcl9mb2ZsZAp0cF9kZHBfaXNj
+c2k9Mgppc2NzaV9udGFzaz0yMDQ4CmlzY3NpX25zZXNzPTIwNDgKaXNjc2lfbmNvbm5fcGVyX3Nl
+c3Npb249MQppc2NzaV9uaW5pdGlhdG9yX2luc3RhbmNlPTY0CltmdW5jdGlvbiI2Il0Kd3hfY2Fw
+cz1hbGwKcl9jYXBzPWFsbApudmk9NApuaXFmbGludD0zNApuZXRoY3RybD0zMgpuZXE9NjYKbmV4
+YWN0Zj0zMgpjbWFzaz1hbGwKcG1hc2s9YWxsCm5oYXNoPTIwNDgKcHJvdG9jb2w9ZmNvZV9pbml0
+aWF0b3IKdHBfZGRwPTIKZmNvZV9uZmNmPTE2CmZjb2VfbnZucD0zMgpmY29lX25zc249MTAyNApb
+ZnVuY3Rpb24iMTAyMyJdCnd4X2NhcHM9YWxsCnJfY2Fwcz1hbGwKbnZpPTQKY21hc2s9YWxsCnBt
+YXNrPWFsbApuZXhhY3RmPTgKbmZpbHRlcj0xNgpbZnVuY3Rpb24iMC8qIl0Kd3hfY2Fwcz0weDgy
+CnJfY2Fwcz0weDg2Cm52aT0xCm5pcWZsaW50PTQKbmV0aGN0cmw9MgpuZXE9NApuZXhhY3RmPTQK
+Y21hc2s9YWxsCnBtYXNrPTB4MQpbZnVuY3Rpb24iMS8qIl0Kd3hfY2Fwcz0weDgyCnJfY2Fwcz0w
+eDg2Cm52aT0xCm5pcWZsaW50PTQKbmV0aGN0cmw9MgpuZXE9NApuZXhhY3RmPTQKY21hc2s9YWxs
+CnBtYXNrPTB4MgpbZnVuY3Rpb24iMi8qIl0Kd3hfY2Fwcz0weDgyCnJfY2Fwcz0weDg2Cm52aT0x
+Cm5pcWZsaW50PTQKbmV0aGN0cmw9MgpuZXE9NApuZXhhY3RmPTQKY21hc2s9YWxsCnBtYXNrPTB4
+NApbZnVuY3Rpb24iMy8qIl0Kd3hfY2Fwcz0weDgyCnJfY2Fwcz0weDg2Cm52aT0xCm5pcWZsaW50
+PTQKbmV0aGN0cmw9MgpuZXE9NApuZXhhY3RmPTQKY21hc2s9YWxsCnBtYXNrPTB4OApbcG9ydCIw
+Il0KZGNiPXBwcCxkY2J4CmJnX21lbT0yNQpscGJrX21lbT0yNQpod209MzAKbHdtPTE1CmR3bT0z
+MApbcG9ydCIxIl0KZGNiPXBwcCxkY2J4CmJnX21lbT0yNQpscGJrX21lbT0yNQpod209MzAKbHdt
+PTE1CmR3bT0zMApbcG9ydCIyIl0KZGNiPXBwcCxkY2J4CmJnX21lbT0yNQpscGJrX21lbT0yNQpo
+d209MzAKbHdtPTE1CmR3bT0zMApbcG9ydCIzIl0KZGNiPXBwcCxkY2J4CmJnX21lbT0yNQpscGJr
+X21lbT0yNQpod209MzAKbHdtPTE1CmR3bT0zMApbZmluaV0KdmVyc2lvbj0weDE0MjUwMDBmCmNo
+ZWNrc3VtPTB4MjNhMmQ4NTAKAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAA
+AAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAA
+AAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAA
+AAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAA
+AAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAA
+AAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAA
+AAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAA
+AAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAA
+AAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAA
+AAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAA
+AAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAA
+AAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAA
+AAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAA
+AAAAAAAAAAAAAAAAAAAAAFtnbG9iYWxdCnJzc19nbGJfY29uZmlnX21vZGU9YmFzaWN2aXJ0dWFs
+CnJzc19nbGJfY29uZmlnX29wdGlvbnM9dG5sbWFwZW4saGFzaHRvZXBsaXR6LHRubGFsbGxrcApw
+Y2llX21hX3JzcF90aW1lcnZhbHVlPTUwMApyZWdbMHg1OWM0XT0weDMvMHgzCnBsX3RpbWVvdXRf
+dmFsdWU9MjAwCnJlZ1sweDEwMDhdPTB4NDA4MTAvMHgyMWM3MApyZWdbMHgxMDBjXT0weDIyMjIy
+MjIyCnJlZ1sweDEwYTBdPTB4MDEwNDA4MTAKcmVnWzB4MTA0NF09NDA5NgpyZWdbMHgxMDQ4XT02
+NTUzNgpyZWdbMHgxMDRjXT0xNTM2CnJlZ1sweDEwNTBdPTkwMjQKcmVnWzB4MTA1NF09OTIxNgpy
+ZWdbMHgxMDU4XT0yMDQ4CnJlZ1sweDEwNWNdPTEyOApyZWdbMHgxMDYwXT04MTkyCnJlZ1sweDEw
+NjRdPTE2Mzg0CnJlZ1sweDEwYTRdPTB4YTAwMGEwMDAvMHhmMDAwZjAwMApyZWdbMHgxMGE4XT0w
+eDQwMjAwMC8weDQwMjAwMApiYXIydGhyb3R0bGVjb3VudD01MDAKc2dlX3RpbWVyX3ZhbHVlPTUs
+MTAsMjAsNTAsMTAwLDIwMApyZWdbMHgxMTI0XT0weDAwMDAwNDAwLzB4MDAwMDA0MDAKcmVnWzB4
+MTEzMF09MHgwMGQ1ZmZlYgpyZWdbMHgxMTNjXT0weDAwMDJmZmMwCnJlZ1sweDdkYzBdPTB4MDYy
+Zjg4NDkKZmlsdGVyTW9kZT1mcmFnbWVudGF0aW9uLG1wc2hpdHR5cGUscHJvdG9jb2wsdmxhbixw
+b3J0LGZjb2Usc3J2cnNyYW0KdHBfcG1yeD0zMAp0cF9wbXJ4X3BhZ2VzaXplPTY0Swp0cF9ucnhj
+aD0wCnRwX3BtdHg9NTAKdHBfcG10eF9wYWdlc2l6ZT02NEsKdHBfbnR4Y2g9MApyZWdbMHgxOWMw
+NF09MHgwMDQwMDAwMC8weDAwNDAwMDAwCltmdW5jdGlvbiIwIl0Kd3hfY2Fwcz1hbGwKcl9jYXBz
+PWFsbApudmk9MjgKbmlxZmxpbnQ9MTcwCm5ldGhjdHJsPTk2Cm5lcT0yNTIKbmV4YWN0Zj00MApj
+bWFzaz1hbGwKcG1hc2s9YWxsCm5yb3V0ZT0zMgpuY2xpcD0zMgpuZmlsdGVyPTQ4Cm5zZXJ2ZXI9
+MzIKbmhhc2g9MjA0OApwcm90b2NvbD1uaWNfdm0sb2ZsZCxyZGRwLHJkbWFjLGlzY3NpX2luaXRp
+YXRvcl9wZHUsaXNjc2lfdGFyZ2V0X3BkdQp0cF9sMnQ9MzA3Mgp0cF9kZHA9Mgp0cF9kZHBfaXNj
+c2k9Mgp0cF9zdGFnPTIKdHBfcGJsPTUKdHBfcnE9NwpbZnVuY3Rpb24iMSJdCnd4X2NhcHM9YWxs
+CnJfY2Fwcz1hbGwKbnZpPTQKbmlxZmxpbnQ9MzQKbmV0aGN0cmw9MzIKbmVxPTY2Cm5leGFjdGY9
+MzIKY21hc2s9YWxsCnBtYXNrPWFsbApuaGFzaD0yMDQ4CnByb3RvY29sPWZjb2VfaW5pdGlhdG9y
+CnRwX2RkcD0yCmZjb2VfbmZjZj0xNgpmY29lX252bnA9MzIKZmNvZV9uc3NuPTEwMjQKW2Z1bmN0
+aW9uIjEwMjMiXQp3eF9jYXBzPWFsbApyX2NhcHM9YWxsCm52aT00CmNtYXNrPWFsbApwbWFzaz1h
+bGwKbmV4YWN0Zj04Cm5maWx0ZXI9MTYKW2Z1bmN0aW9uIjAvKiJdCnd4X2NhcHM9MHg4MgpyX2Nh
+cHM9MHg4Ngpudmk9MQpuaXFmbGludD00Cm5ldGhjdHJsPTIKbmVxPTQKbmV4YWN0Zj00CmNtYXNr
+PWFsbApwbWFzaz0weDEKW2Z1bmN0aW9uIjEvKiJdCnd4X2NhcHM9MHg4MgpyX2NhcHM9MHg4Ngpu
+dmk9MQpuaXFmbGludD00Cm5ldGhjdHJsPTIKbmVxPTQKbmV4YWN0Zj00CmNtYXNrPWFsbApwbWFz
+az0weDIKW3BvcnQiMCJdCmRjYj1wcHAsZGNieApiZ19tZW09MjUKbHBia19tZW09MjUKaHdtPTMw
+Cmx3bT0xNQpkd209MzAKW3BvcnQiMSJdCmRjYj1wcHAsZGNieApiZ19tZW09MjUKbHBia19tZW09
+MjUKaHdtPTMwCmx3bT0xNQpkd209MzAKW3BvcnQiMiJdCmRjYj1wcHAsZGNieApiZ19tZW09MjUK
+bHBia19tZW09MjUKaHdtPTMwCmx3bT0xNQpkd209MzAKW3BvcnQiMyJdCmRjYj1wcHAsZGNieApi
+Z19tZW09MjUKbHBia19tZW09MjUKaHdtPTMwCmx3bT0xNQpkd209MzAKW2ZpbmldCnZlcnNpb249
+MHgxNDI1MDAwZApjaGVja3N1bT0weGU1NmNiOTk5CgAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAA
+AAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAA
+AAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAA
+AAAAAAAAAAAAAAAA
+====
diff --git a/sys/dev/cxgbe/firmware/t5fw_cfg.txt b/sys/dev/cxgbe/firmware/t5fw_cfg.txt
new file mode 100644
index 0000000..95eebfa
--- /dev/null
+++ b/sys/dev/cxgbe/firmware/t5fw_cfg.txt
@@ -0,0 +1,151 @@
+# Firmware configuration file.
+#
+# Global limits (some are hardware limits, others are due to the firmware).
+# nvi = 128 virtual interfaces
+# niqflint = 1023 ingress queues with freelists and/or interrupts
+# nethctrl = 64K Ethernet or ctrl egress queues
+# neq = 64K egress queues of all kinds, including freelists
+# nexactf = 336 MPS TCAM entries, can oversubscribe.
+#
+
+[global]
+ rss_glb_config_mode = basicvirtual
+ rss_glb_config_options = tnlmapen, hashtoeplitz, tnlalllkp
+
+ sge_timer_value = 1, 5, 10, 50, 100, 200 # usecs
+
+ # TP_SHIFT_CNT
+ reg[0x7dc0] = 0x62f8849
+
+ # TP_GLOBAL_CONFIG
+ reg[0x7d08] = 0x00000800/0x00000800 # set IssFromCplEnable
+
+ filterMode = fragmentation, mpshittype, protocol, vlan, port, fcoe
+ filterMask = protocol, fcoe
+
+ # TP rx and tx channels (0 = auto).
+ tp_nrxch = 0
+ tp_ntxch = 0
+
+ # TP rx and tx payload memory (% of the total EDRAM + DDR3).
+ tp_pmrx = 38
+ tp_pmtx = 60
+ tp_pmrx_pagesize = 64K
+ tp_pmtx_pagesize = 64K
+
+# PFs 0-3. These get 8 MSI/8 MSI-X vectors each. VFs are supported by
+# these 4 PFs only. Not used here at all.
+[function "0"]
+ nvf = 16
+ nvi = 1
+[function "0/*"]
+ nvi = 1
+
+[function "1"]
+ nvf = 16
+ nvi = 1
+[function "1/*"]
+ nvi = 1
+
+[function "2"]
+ nvf = 16
+ nvi = 1
+[function "2/*"]
+ nvi = 1
+
+[function "3"]
+ nvf = 16
+ nvi = 1
+[function "3/*"]
+ nvi = 1
+
+# PF4 is the resource-rich PF that the bus/nexus driver attaches to.
+# It gets 32 MSI/128 MSI-X vectors.
+[function "4"]
+ wx_caps = all
+ r_caps = all
+ nvi = 32
+ niqflint = 256
+ nethctrl = 128
+ neq = 256
+ nexactf = 328
+ cmask = all
+ pmask = all
+
+ # driver will mask off features it won't use
+ protocol = ofld
+
+ tp_l2t = 4096
+ tp_ddp = 2
+
+ # TCAM has 8K cells; each region must start at a multiple of 128 cell.
+ # Each entry in these categories takes 4 cells each. nhash will use the
+ # TCAM iff there is room left (that is, the rest don't add up to 2048).
+ nroute = 32
+ nclip = 32
+ nfilter = 1008
+ nserver = 512
+ nhash = 16384
+
+# PF5 is the SCSI Controller PF. It gets 32 MSI/40 MSI-X vectors.
+# Not used right now.
+[function "5"]
+ nvi = 1
+
+# PF6 is the FCoE Controller PF. It gets 32 MSI/40 MSI-X vectors.
+# Not used right now.
+[function "6"]
+ nvi = 1
+
+# The following function, 1023, is not an actual PCIE function but is used to
+# configure and reserve firmware internal resources that come from the global
+# resource pool.
+[function "1023"]
+ wx_caps = all
+ r_caps = all
+ nvi = 4
+ cmask = all
+ pmask = all
+ nexactf = 8
+ nfilter = 16
+
+# MPS has 192K buffer space for ingress packets from the wire as well as
+# loopback path of the L2 switch.
+[port "0"]
+ dcb = none
+ bg_mem = 25
+ lpbk_mem = 25
+ hwm = 30
+ lwm = 15
+ dwm = 30
+
+[port "1"]
+ dcb = none
+ bg_mem = 25
+ lpbk_mem = 25
+ hwm = 30
+ lwm = 15
+ dwm = 30
+
+[port "2"]
+ dcb = none
+ bg_mem = 25
+ lpbk_mem = 25
+ hwm = 30
+ lwm = 15
+ dwm = 30
+
+[port "3"]
+ dcb = none
+ bg_mem = 25
+ lpbk_mem = 25
+ hwm = 30
+ lwm = 15
+ dwm = 30
+
+[fini]
+ version = 0x1
+ checksum = 0xb2417251
+#
+# $FreeBSD$
+#
diff --git a/sys/dev/cxgbe/firmware/t5fw_cfg_fpga.txt b/sys/dev/cxgbe/firmware/t5fw_cfg_fpga.txt
new file mode 100644
index 0000000..eb4ed4b
--- /dev/null
+++ b/sys/dev/cxgbe/firmware/t5fw_cfg_fpga.txt
@@ -0,0 +1,477 @@
+# Chelsio T5 Factory Default configuration file.
+#
+# Copyright (C) 2010-2013 Chelsio Communications. All rights reserved.
+#
+# DO NOT MODIFY THIS FILE UNDER ANY CIRCUMSTANCES. MODIFICATION OF
+# THIS FILE WILL RESULT IN A NON-FUNCTIONAL T4 ADAPTER AND MAY RESULT
+# IN PHYSICAL DAMAGE TO T4 ADAPTERS.
+
+# This file provides the default, power-on configuration for 4-port T4-based
+# adapters shipped from the factory. These defaults are designed to address
+# the needs of the vast majority of T4 customers. The basic idea is to have
+# a default configuration which allows a customer to plug a T4 adapter in and
+# have it work regardless of OS, driver or application except in the most
+# unusual and/or demanding customer applications.
+#
+# Many of the T4 resources which are described by this configuration are
+# finite. This requires balancing the configuration/operation needs of
+# device drivers across OSes and a large number of customer application.
+#
+# Some of the more important resources to allocate and their constaints are:
+# 1. Virtual Interfaces: 128.
+# 2. Ingress Queues with Free Lists: 1024. PCI-E SR-IOV Virtual Functions
+# must use a power of 2 Ingress Queues.
+# 3. Egress Queues: 128K. PCI-E SR-IOV Virtual Functions must use a
+# power of 2 Egress Queues.
+# 4. MSI-X Vectors: 1088. A complication here is that the PCI-E SR-IOV
+# Virtual Functions based off of a Physical Function all get the
+# same umber of MSI-X Vectors as the base Physical Function.
+# Additionally, regardless of whether Virtual Functions are enabled or
+# not, their MSI-X "needs" are counted by the PCI-E implementation.
+# And finally, all Physical Funcations capable of supporting Virtual
+# Functions (PF0-3) must have the same number of configured TotalVFs in
+# their SR-IOV Capabilities.
+# 5. Multi-Port Support (MPS) TCAM: 336 entries to support MAC destination
+# address matching on Ingress Packets.
+#
+# Some of the important OS/Driver resource needs are:
+# 6. Some OS Drivers will manage all resources through a single Physical
+# Function (currently PF0 but it could be any Physical Function). Thus,
+# this "Unified PF" will need to have enough resources allocated to it
+# to allow for this. And because of the MSI-X resource allocation
+# constraints mentioned above, this probably means we'll either have to
+# severely limit the TotalVFs if we continue to use PF0 as the Unified PF
+# or we'll need to move the Unified PF into the PF4-7 range since those
+# Physical Functions don't have any Virtual Functions associated with
+# them.
+# 7. Some OS Drivers will manage different ports and functions (NIC,
+# storage, etc.) on different Physical Functions. For example, NIC
+# functions for ports 0-3 on PF0-3, FCoE on PF4, iSCSI on PF5, etc.
+#
+# Some of the customer application needs which need to be accommodated:
+# 8. Some customers will want to support large CPU count systems with
+# good scaling. Thus, we'll need to accommodate a number of
+# Ingress Queues and MSI-X Vectors to allow up to some number of CPUs
+# to be involved per port and per application function. For example,
+# in the case where all ports and application functions will be
+# managed via a single Unified PF and we want to accommodate scaling up
+# to 8 CPUs, we would want:
+#
+# 4 ports *
+# 3 application functions (NIC, FCoE, iSCSI) per port *
+# 8 Ingress Queue/MSI-X Vectors per application function
+#
+# for a total of 96 Ingress Queues and MSI-X Vectors on the Unified PF.
+# (Plus a few for Firmware Event Queues, etc.)
+#
+# 9. Some customers will want to use T4's PCI-E SR-IOV Capability to allow
+# Virtual Machines to directly access T4 functionality via SR-IOV
+# Virtual Functions and "PCI Device Passthrough" -- this is especially
+# true for the NIC application functionality. (Note that there is
+# currently no ability to use the TOE, FCoE, iSCSI, etc. via Virtual
+# Functions so this is in fact solely limited to NIC.)
+#
+
+
+# Global configuration settings.
+#
+[global]
+ rss_glb_config_mode = basicvirtual
+ rss_glb_config_options = tnlmapen,hashtoeplitz,tnlalllkp
+
+ # PCIE_MA_RSP register
+ pcie_ma_rsp_timervalue = 500 # the timer value in units of us
+ reg[0x59c4] = 0x3/0x3 # enable the timers
+
+ # PL_TIMEOUT register
+ pl_timeout_value = 200 # the timeout value in units of us
+
+ # The following Scatter Gather Engine (SGE) settings assume a 4KB Host
+ # Page Size and a 64B L1 Cache Line Size. It programs the
+ # EgrStatusPageSize and IngPadBoundary to 64B and the PktShift to 2.
+ # If a Master PF Driver finds itself on a machine with different
+ # parameters, then the Master PF Driver is responsible for initializing
+ # these parameters to appropriate values.
+ #
+ # Notes:
+ # 1. The Free List Buffer Sizes below are raw and the firmware will
+ # round them up to the Ingress Padding Boundary.
+ # 2. The SGE Timer Values below are expressed below in microseconds.
+ # The firmware will convert these values to Core Clock Ticks when
+ # it processes the configuration parameters.
+ #
+ reg[0x1008] = 0x40810/0x21c70 # SGE_CONTROL
+ reg[0x100c] = 0x22222222 # SGE_HOST_PAGE_SIZE
+ reg[0x10a0] = 0x01040810 # SGE_INGRESS_RX_THRESHOLD
+ reg[0x1044] = 4096 # SGE_FL_BUFFER_SIZE0
+ reg[0x1048] = 65536 # SGE_FL_BUFFER_SIZE1
+ reg[0x104c] = 1536 # SGE_FL_BUFFER_SIZE2
+ reg[0x1050] = 9024 # SGE_FL_BUFFER_SIZE3
+ reg[0x1054] = 9216 # SGE_FL_BUFFER_SIZE4
+ reg[0x1058] = 2048 # SGE_FL_BUFFER_SIZE5
+ reg[0x105c] = 128 # SGE_FL_BUFFER_SIZE6
+ reg[0x1060] = 8192 # SGE_FL_BUFFER_SIZE7
+ reg[0x1064] = 16384 # SGE_FL_BUFFER_SIZE8
+ reg[0x10a4] = 0xa000a000/0xf000f000 # SGE_DBFIFO_STATUS
+ reg[0x10a8] = 0x402000/0x402000 # SGE_DOORBELL_CONTROL
+
+ # SGE_THROTTLE_CONTROL
+ bar2throttlecount = 500 # bar2throttlecount in us
+
+ sge_timer_value = 5, 10, 20, 50, 100, 200 # SGE_TIMER_VALUE* in usecs
+
+
+ reg[0x1124] = 0x00000400/0x00000400 # SGE_CONTROL2, enable VFIFO; if
+ # SGE_VFIFO_SIZE is not set, then
+ # firmware will set it up in function
+ # of number of egress queues used
+
+ reg[0x1130] = 0x00d5ffeb # SGE_DBP_FETCH_THRESHOLD, fetch
+ # threshold set to queue depth
+ # minus 128-entries for FL and HP
+ # queues, and 0xfff for LP which
+ # prompts the firmware to set it up
+ # in function of egress queues
+ # used
+
+ reg[0x113c] = 0x0002ffc0 # SGE_VFIFO_SIZE, set to 0x2ffc0 which
+ # prompts the firmware to set it up in
+ # function of number of egress queues
+ # used
+
+ reg[0x7dc0] = 0x062f8849 # TP_SHIFT_CNT
+
+ # Selection of tuples for LE filter lookup, fields (and widths which
+ # must sum to <= 36): { IP Fragment (1), MPS Match Type (3),
+ # IP Protocol (8), [Inner] VLAN (17), Port (3), FCoE (1) }
+ #
+ filterMode = fragmentation, mpshittype, protocol, vlan, port, fcoe, srvrsram
+
+ # Percentage of dynamic memory (in either the EDRAM or external MEM)
+ # to use for TP RX payload
+ tp_pmrx = 30
+
+ # TP RX payload page size
+ tp_pmrx_pagesize = 64K
+
+ # TP number of RX channels
+ tp_nrxch = 0 # 0 (auto) = 1
+
+ # Percentage of dynamic memory (in either the EDRAM or external MEM)
+ # to use for TP TX payload
+ tp_pmtx = 50
+
+ # TP TX payload page size
+ tp_pmtx_pagesize = 64K
+
+ # TP number of TX channels
+ tp_ntxch = 0 # 0 (auto) = equal number of ports
+
+ reg[0x19c04] = 0x00400000/0x00400000 # LE Server SRAM Enable
+
+# Some "definitions" to make the rest of this a bit more readable. We support
+# 4 ports, 3 functions (NIC, FCoE and iSCSI), scaling up to 8 "CPU Queue Sets"
+# per function per port ...
+#
+# NMSIX = 1088 # available MSI-X Vectors
+# NVI = 128 # available Virtual Interfaces
+# NMPSTCAM = 336 # MPS TCAM entries
+#
+# NPORTS = 4 # ports
+# NCPUS = 8 # CPUs we want to support scalably
+# NFUNCS = 3 # functions per port (NIC, FCoE, iSCSI)
+
+# Breakdown of Virtual Interface/Queue/Interrupt resources for the "Unified
+# PF" which many OS Drivers will use to manage most or all functions.
+#
+# Each Ingress Queue can use one MSI-X interrupt but some Ingress Queues can
+# use Forwarded Interrupt Ingress Queues. For these latter, an Ingress Queue
+# would be created and the Queue ID of a Forwarded Interrupt Ingress Queue
+# will be specified as the "Ingress Queue Asynchronous Destination Index."
+# Thus, the number of MSI-X Vectors assigned to the Unified PF will be less
+# than or equal to the number of Ingress Queues ...
+#
+# NVI_NIC = 4 # NIC access to NPORTS
+# NFLIQ_NIC = 32 # NIC Ingress Queues with Free Lists
+# NETHCTRL_NIC = 32 # NIC Ethernet Control/TX Queues
+# NEQ_NIC = 64 # NIC Egress Queues (FL, ETHCTRL/TX)
+# NMPSTCAM_NIC = 16 # NIC MPS TCAM Entries (NPORTS*4)
+# NMSIX_NIC = 32 # NIC MSI-X Interrupt Vectors (FLIQ)
+#
+# NVI_OFLD = 0 # Offload uses NIC function to access ports
+# NFLIQ_OFLD = 16 # Offload Ingress Queues with Free Lists
+# NETHCTRL_OFLD = 0 # Offload Ethernet Control/TX Queues
+# NEQ_OFLD = 16 # Offload Egress Queues (FL)
+# NMPSTCAM_OFLD = 0 # Offload MPS TCAM Entries (uses NIC's)
+# NMSIX_OFLD = 16 # Offload MSI-X Interrupt Vectors (FLIQ)
+#
+# NVI_RDMA = 0 # RDMA uses NIC function to access ports
+# NFLIQ_RDMA = 4 # RDMA Ingress Queues with Free Lists
+# NETHCTRL_RDMA = 0 # RDMA Ethernet Control/TX Queues
+# NEQ_RDMA = 4 # RDMA Egress Queues (FL)
+# NMPSTCAM_RDMA = 0 # RDMA MPS TCAM Entries (uses NIC's)
+# NMSIX_RDMA = 4 # RDMA MSI-X Interrupt Vectors (FLIQ)
+#
+# NEQ_WD = 128 # Wire Direct TX Queues and FLs
+# NETHCTRL_WD = 64 # Wire Direct TX Queues
+# NFLIQ_WD = 64 ` # Wire Direct Ingress Queues with Free Lists
+#
+# NVI_ISCSI = 4 # ISCSI access to NPORTS
+# NFLIQ_ISCSI = 4 # ISCSI Ingress Queues with Free Lists
+# NETHCTRL_ISCSI = 0 # ISCSI Ethernet Control/TX Queues
+# NEQ_ISCSI = 4 # ISCSI Egress Queues (FL)
+# NMPSTCAM_ISCSI = 4 # ISCSI MPS TCAM Entries (NPORTS)
+# NMSIX_ISCSI = 4 # ISCSI MSI-X Interrupt Vectors (FLIQ)
+#
+# NVI_FCOE = 4 # FCOE access to NPORTS
+# NFLIQ_FCOE = 34 # FCOE Ingress Queues with Free Lists
+# NETHCTRL_FCOE = 32 # FCOE Ethernet Control/TX Queues
+# NEQ_FCOE = 66 # FCOE Egress Queues (FL)
+# NMPSTCAM_FCOE = 32 # FCOE MPS TCAM Entries (NPORTS)
+# NMSIX_FCOE = 34 # FCOE MSI-X Interrupt Vectors (FLIQ)
+
+# Two extra Ingress Queues per function for Firmware Events and Forwarded
+# Interrupts, and two extra interrupts per function for Firmware Events (or a
+# Forwarded Interrupt Queue) and General Interrupts per function.
+#
+# NFLIQ_EXTRA = 6 # "extra" Ingress Queues 2*NFUNCS (Firmware and
+# # Forwarded Interrupts
+# NMSIX_EXTRA = 6 # extra interrupts 2*NFUNCS (Firmware and
+# # General Interrupts
+
+# Microsoft HyperV resources. The HyperV Virtual Ingress Queues will have
+# their interrupts forwarded to another set of Forwarded Interrupt Queues.
+#
+# NVI_HYPERV = 16 # VMs we want to support
+# NVIIQ_HYPERV = 2 # Virtual Ingress Queues with Free Lists per VM
+# NFLIQ_HYPERV = 40 # VIQs + NCPUS Forwarded Interrupt Queues
+# NEQ_HYPERV = 32 # VIQs Free Lists
+# NMPSTCAM_HYPERV = 16 # MPS TCAM Entries (NVI_HYPERV)
+# NMSIX_HYPERV = 8 # NCPUS Forwarded Interrupt Queues
+
+# Adding all of the above Unified PF resource needs together: (NIC + OFLD +
+# RDMA + ISCSI + FCOE + EXTRA + HYPERV)
+#
+# NVI_UNIFIED = 28
+# NFLIQ_UNIFIED = 106
+# NETHCTRL_UNIFIED = 32
+# NEQ_UNIFIED = 124
+# NMPSTCAM_UNIFIED = 40
+#
+# The sum of all the MSI-X resources above is 74 MSI-X Vectors but we'll round
+# that up to 128 to make sure the Unified PF doesn't run out of resources.
+#
+# NMSIX_UNIFIED = 128
+#
+# The Storage PFs could need up to NPORTS*NCPUS + NMSIX_EXTRA MSI-X Vectors
+# which is 34 but they're probably safe with 32.
+#
+# NMSIX_STORAGE = 32
+
+# Note: The UnifiedPF is PF4 which doesn't have any Virtual Functions
+# associated with it. Thus, the MSI-X Vector allocations we give to the
+# UnifiedPF aren't inherited by any Virtual Functions. As a result we can
+# provision many more Virtual Functions than we can if the UnifiedPF were
+# one of PF0-3.
+#
+
+# All of the below PCI-E parameters are actually stored in various *_init.txt
+# files. We include them below essentially as comments.
+#
+# For PF0-3 we assign 8 vectors each for NIC Ingress Queues of the associated
+# ports 0-3.
+#
+# For PF4, the Unified PF, we give it an MSI-X Table Size as outlined above.
+#
+# For PF5-6 we assign enough MSI-X Vectors to support FCoE and iSCSI
+# storage applications across all four possible ports.
+#
+# Additionally, since the UnifiedPF isn't one of the per-port Physical
+# Functions, we give the UnifiedPF and the PF0-3 Physical Functions
+# different PCI Device IDs which will allow Unified and Per-Port Drivers
+# to directly select the type of Physical Function to which they wish to be
+# attached.
+#
+# Note that the actual values used for the PCI-E Intelectual Property will be
+# 1 less than those below since that's the way it "counts" things. For
+# readability, we use the number we actually mean ...
+#
+# PF0_INT = 8 # NCPUS
+# PF1_INT = 8 # NCPUS
+# PF2_INT = 8 # NCPUS
+# PF3_INT = 8 # NCPUS
+# PF0_3_INT = 32 # PF0_INT + PF1_INT + PF2_INT + PF3_INT
+#
+# PF4_INT = 128 # NMSIX_UNIFIED
+# PF5_INT = 32 # NMSIX_STORAGE
+# PF6_INT = 32 # NMSIX_STORAGE
+# PF7_INT = 0 # Nothing Assigned
+# PF4_7_INT = 192 # PF4_INT + PF5_INT + PF6_INT + PF7_INT
+#
+# PF0_7_INT = 224 # PF0_3_INT + PF4_7_INT
+#
+# With the above we can get 17 VFs/PF0-3 (limited by 336 MPS TCAM entries)
+# but we'll lower that to 16 to make our total 64 and a nice power of 2 ...
+#
+# NVF = 16
+
+# For those OSes which manage different ports on different PFs, we need
+# only enough resources to support a single port's NIC application functions
+# on PF0-3. The below assumes that we're only doing NIC with NCPUS "Queue
+# Sets" for ports 0-3. The FCoE and iSCSI functions for such OSes will be
+# managed on the "storage PFs" (see below).
+#
+
+# Some OS Drivers manage all application functions for all ports via PF4.
+# Thus we need to provide a large number of resources here. For Egress
+# Queues we need to account for both TX Queues as well as Free List Queues
+# (because the host is responsible for producing Free List Buffers for the
+# hardware to consume).
+#
+[function "0"]
+ wx_caps = all # write/execute permissions for all commands
+ r_caps = all # read permissions for all commands
+ nvi = 28 # NVI_UNIFIED
+ niqflint = 170 # NFLIQ_UNIFIED + NLFIQ_WD
+ nethctrl = 96 # NETHCTRL_UNIFIED + NETHCTRL_WD
+ neq = 252 # NEQ_UNIFIED + NEQ_WD
+ nexactf = 40 # NMPSTCAM_UNIFIED
+ cmask = all # access to all channels
+ pmask = all # access to all four ports ...
+ nroute = 32 # number of routing region entries
+ nclip = 32 # number of clip region entries
+ nfilter = 48 # number of filter region entries
+ nserver = 32 # number of server region entries
+ nhash = 2048 # number of hash region entries
+ protocol = nic_vm, ofld, rddp, rdmac, iscsi_initiator_pdu, iscsi_target_pdu
+ tp_l2t = 3072
+ tp_ddp = 2
+ tp_ddp_iscsi = 2
+ tp_stag = 2
+ tp_pbl = 5
+ tp_rq = 7
+
+# We have FCoE and iSCSI storage functions on PF5 and PF6 each of which may
+# need to have Virtual Interfaces on each of the four ports with up to NCPUS
+# "Queue Sets" each.
+#
+[function "1"]
+ wx_caps = all # write/execute permissions for all commands
+ r_caps = all # read permissions for all commands
+ nvi = 4 # NPORTS
+ niqflint = 34 # NPORTS*NCPUS + NMSIX_EXTRA
+ nethctrl = 32 # NPORTS*NCPUS
+ neq = 66 # NPORTS*NCPUS * 2 (FL, ETHCTRL/TX) + 2 (EXTRA)
+ nexactf = 32 # NPORTS + adding 28 exact entries for FCoE
+ # which is OK since < MIN(SUM PF0..3, PF4)
+ # and we never load PF0..3 and PF4 concurrently
+ cmask = all # access to all channels
+ pmask = all # access to all four ports ...
+ nhash = 2048
+ protocol = fcoe_initiator
+ tp_ddp = 2
+ fcoe_nfcf = 16
+ fcoe_nvnp = 32
+ fcoe_nssn = 1024
+
+# The following function, 1023, is not an actual PCIE function but is used to
+# configure and reserve firmware internal resources that come from the global
+# resource pool.
+#
+[function "1023"]
+ wx_caps = all # write/execute permissions for all commands
+ r_caps = all # read permissions for all commands
+ nvi = 4 # NVI_UNIFIED
+ cmask = all # access to all channels
+ pmask = all # access to all four ports ...
+ nexactf = 8 # NPORTS + DCBX +
+ nfilter = 16 # number of filter region entries
+
+# For Virtual functions, we only allow NIC functionality and we only allow
+# access to one port (1 << PF). Note that because of limitations in the
+# Scatter Gather Engine (SGE) hardware which checks writes to VF KDOORBELL
+# and GTS registers, the number of Ingress and Egress Queues must be a power
+# of 2.
+#
+[function "0/*"] # NVF
+ wx_caps = 0x82 # DMAQ | VF
+ r_caps = 0x86 # DMAQ | VF | PORT
+ nvi = 1 # 1 port
+ niqflint = 4 # 2 "Queue Sets" + NXIQ
+ nethctrl = 2 # 2 "Queue Sets"
+ neq = 4 # 2 "Queue Sets" * 2
+ nexactf = 4
+ cmask = all # access to all channels
+ pmask = 0x1 # access to only one port ...
+
+[function "1/*"] # NVF
+ wx_caps = 0x82 # DMAQ | VF
+ r_caps = 0x86 # DMAQ | VF | PORT
+ nvi = 1 # 1 port
+ niqflint = 4 # 2 "Queue Sets" + NXIQ
+ nethctrl = 2 # 2 "Queue Sets"
+ neq = 4 # 2 "Queue Sets" * 2
+ nexactf = 4
+ cmask = all # access to all channels
+ pmask = 0x2 # access to only one port ...
+
+# MPS features a 196608 bytes ingress buffer that is used for ingress buffering
+# for packets from the wire as well as the loopback path of the L2 switch. The
+# folling params control how the buffer memory is distributed and the L2 flow
+# control settings:
+#
+# bg_mem: %-age of mem to use for port/buffer group
+# lpbk_mem: %-age of port/bg mem to use for loopback
+# hwm: high watermark; bytes available when starting to send pause
+# frames (in units of 0.1 MTU)
+# lwm: low watermark; bytes remaining when sending 'unpause' frame
+# (in inuits of 0.1 MTU)
+# dwm: minimum delta between high and low watermark (in units of 100
+# Bytes)
+#
+[port "0"]
+ dcb = ppp, dcbx # configure for DCB PPP and enable DCBX offload
+ bg_mem = 25
+ lpbk_mem = 25
+ hwm = 30
+ lwm = 15
+ dwm = 30
+
+[port "1"]
+ dcb = ppp, dcbx
+ bg_mem = 25
+ lpbk_mem = 25
+ hwm = 30
+ lwm = 15
+ dwm = 30
+
+[port "2"]
+ dcb = ppp, dcbx
+ bg_mem = 25
+ lpbk_mem = 25
+ hwm = 30
+ lwm = 15
+ dwm = 30
+
+[port "3"]
+ dcb = ppp, dcbx
+ bg_mem = 25
+ lpbk_mem = 25
+ hwm = 30
+ lwm = 15
+ dwm = 30
+
+[fini]
+ version = 0x1425000d
+ checksum = 0xe56cb999
+
+# Total resources used by above allocations:
+# Virtual Interfaces: 104
+# Ingress Queues/w Free Lists and Interrupts: 526
+# Egress Queues: 702
+# MPS TCAM Entries: 336
+# MSI-X Vectors: 736
+# Virtual Functions: 64
+#
+# $FreeBSD$
+#
diff --git a/sys/dev/cxgbe/firmware/t5fw_cfg_uwire.txt b/sys/dev/cxgbe/firmware/t5fw_cfg_uwire.txt
new file mode 100644
index 0000000..fd68b72
--- /dev/null
+++ b/sys/dev/cxgbe/firmware/t5fw_cfg_uwire.txt
@@ -0,0 +1,568 @@
+# Chelsio T5 Factory Default configuration file.
+#
+# Copyright (C) 2010-2013 Chelsio Communications. All rights reserved.
+#
+# DO NOT MODIFY THIS FILE UNDER ANY CIRCUMSTANCES. MODIFICATION OF
+# THIS FILE WILL RESULT IN A NON-FUNCTIONAL T4 ADAPTER AND MAY RESULT
+# IN PHYSICAL DAMAGE TO T4 ADAPTERS.
+
+# This file provides the default, power-on configuration for 4-port T4-based
+# adapters shipped from the factory. These defaults are designed to address
+# the needs of the vast majority of T4 customers. The basic idea is to have
+# a default configuration which allows a customer to plug a T4 adapter in and
+# have it work regardless of OS, driver or application except in the most
+# unusual and/or demanding customer applications.
+#
+# Many of the T4 resources which are described by this configuration are
+# finite. This requires balancing the configuration/operation needs of
+# device drivers across OSes and a large number of customer application.
+#
+# Some of the more important resources to allocate and their constaints are:
+# 1. Virtual Interfaces: 128.
+# 2. Ingress Queues with Free Lists: 1024. PCI-E SR-IOV Virtual Functions
+# must use a power of 2 Ingress Queues.
+# 3. Egress Queues: 128K. PCI-E SR-IOV Virtual Functions must use a
+# power of 2 Egress Queues.
+# 4. MSI-X Vectors: 1088. A complication here is that the PCI-E SR-IOV
+# Virtual Functions based off of a Physical Function all get the
+# same umber of MSI-X Vectors as the base Physical Function.
+# Additionally, regardless of whether Virtual Functions are enabled or
+# not, their MSI-X "needs" are counted by the PCI-E implementation.
+# And finally, all Physical Funcations capable of supporting Virtual
+# Functions (PF0-3) must have the same number of configured TotalVFs in
+# their SR-IOV Capabilities.
+# 5. Multi-Port Support (MPS) TCAM: 336 entries to support MAC destination
+# address matching on Ingress Packets.
+#
+# Some of the important OS/Driver resource needs are:
+# 6. Some OS Drivers will manage all resources through a single Physical
+# Function (currently PF0 but it could be any Physical Function). Thus,
+# this "Unified PF" will need to have enough resources allocated to it
+# to allow for this. And because of the MSI-X resource allocation
+# constraints mentioned above, this probably means we'll either have to
+# severely limit the TotalVFs if we continue to use PF0 as the Unified PF
+# or we'll need to move the Unified PF into the PF4-7 range since those
+# Physical Functions don't have any Virtual Functions associated with
+# them.
+# 7. Some OS Drivers will manage different ports and functions (NIC,
+# storage, etc.) on different Physical Functions. For example, NIC
+# functions for ports 0-3 on PF0-3, FCoE on PF4, iSCSI on PF5, etc.
+#
+# Some of the customer application needs which need to be accommodated:
+# 8. Some customers will want to support large CPU count systems with
+# good scaling. Thus, we'll need to accommodate a number of
+# Ingress Queues and MSI-X Vectors to allow up to some number of CPUs
+# to be involved per port and per application function. For example,
+# in the case where all ports and application functions will be
+# managed via a single Unified PF and we want to accommodate scaling up
+# to 8 CPUs, we would want:
+#
+# 4 ports *
+# 3 application functions (NIC, FCoE, iSCSI) per port *
+# 8 Ingress Queue/MSI-X Vectors per application function
+#
+# for a total of 96 Ingress Queues and MSI-X Vectors on the Unified PF.
+# (Plus a few for Firmware Event Queues, etc.)
+#
+# 9. Some customers will want to use T4's PCI-E SR-IOV Capability to allow
+# Virtual Machines to directly access T4 functionality via SR-IOV
+# Virtual Functions and "PCI Device Passthrough" -- this is especially
+# true for the NIC application functionality. (Note that there is
+# currently no ability to use the TOE, FCoE, iSCSI, etc. via Virtual
+# Functions so this is in fact solely limited to NIC.)
+#
+
+
+# Global configuration settings.
+#
+[global]
+ rss_glb_config_mode = basicvirtual
+ rss_glb_config_options = tnlmapen,hashtoeplitz,tnlalllkp
+
+ # PL_TIMEOUT register
+ pl_timeout_value = 200 # the timeout value in units of us
+
+ # The following Scatter Gather Engine (SGE) settings assume a 4KB Host
+ # Page Size and a 64B L1 Cache Line Size. It programs the
+ # EgrStatusPageSize and IngPadBoundary to 64B and the PktShift to 2.
+ # If a Master PF Driver finds itself on a machine with different
+ # parameters, then the Master PF Driver is responsible for initializing
+ # these parameters to appropriate values.
+ #
+ # Notes:
+ # 1. The Free List Buffer Sizes below are raw and the firmware will
+ # round them up to the Ingress Padding Boundary.
+ # 2. The SGE Timer Values below are expressed below in microseconds.
+ # The firmware will convert these values to Core Clock Ticks when
+ # it processes the configuration parameters.
+ #
+ reg[0x1008] = 0x40810/0x21c70 # SGE_CONTROL
+ reg[0x100c] = 0x22222222 # SGE_HOST_PAGE_SIZE
+ reg[0x10a0] = 0x01040810 # SGE_INGRESS_RX_THRESHOLD
+ reg[0x1044] = 4096 # SGE_FL_BUFFER_SIZE0
+ reg[0x1048] = 65536 # SGE_FL_BUFFER_SIZE1
+ reg[0x104c] = 1536 # SGE_FL_BUFFER_SIZE2
+ reg[0x1050] = 9024 # SGE_FL_BUFFER_SIZE3
+ reg[0x1054] = 9216 # SGE_FL_BUFFER_SIZE4
+ reg[0x1058] = 2048 # SGE_FL_BUFFER_SIZE5
+ reg[0x105c] = 128 # SGE_FL_BUFFER_SIZE6
+ reg[0x1060] = 8192 # SGE_FL_BUFFER_SIZE7
+ reg[0x1064] = 16384 # SGE_FL_BUFFER_SIZE8
+ reg[0x10a4] = 0xa000a000/0xf000f000 # SGE_DBFIFO_STATUS
+ reg[0x10a8] = 0x402000/0x402000 # SGE_DOORBELL_CONTROL
+
+ # SGE_THROTTLE_CONTROL
+ bar2throttlecount = 500 # bar2throttlecount in us
+
+ sge_timer_value = 5, 10, 20, 50, 100, 200 # SGE_TIMER_VALUE* in usecs
+
+
+ reg[0x1124] = 0x00000400/0x00000400 # SGE_CONTROL2, enable VFIFO; if
+ # SGE_VFIFO_SIZE is not set, then
+ # firmware will set it up in function
+ # of number of egress queues used
+
+ reg[0x1130] = 0x00d5ffeb # SGE_DBP_FETCH_THRESHOLD, fetch
+ # threshold set to queue depth
+ # minus 128-entries for FL and HP
+ # queues, and 0xfff for LP which
+ # prompts the firmware to set it up
+ # in function of egress queues
+ # used
+
+ reg[0x113c] = 0x0002ffc0 # SGE_VFIFO_SIZE, set to 0x2ffc0 which
+ # prompts the firmware to set it up in
+ # function of number of egress queues
+ # used
+
+ reg[0x7dc0] = 0x062f8849 # TP_SHIFT_CNT
+
+ # Selection of tuples for LE filter lookup, fields (and widths which
+ # must sum to <= 36): { IP Fragment (1), MPS Match Type (3),
+ # IP Protocol (8), [Inner] VLAN (17), Port (3), FCoE (1) }
+ #
+ filterMode = srvrsram, fragmentation, mpshittype, protocol, vlan, port, fcoe
+ filterMask = protocol, fcoe
+
+ # Percentage of dynamic memory (in either the EDRAM or external MEM)
+ # to use for TP RX payload
+ tp_pmrx = 30
+
+ # TP RX payload page size
+ tp_pmrx_pagesize = 64K
+
+ # TP number of RX channels
+ tp_nrxch = 0 # 0 (auto) = 1
+
+ # Percentage of dynamic memory (in either the EDRAM or external MEM)
+ # to use for TP TX payload
+ tp_pmtx = 50
+
+ # TP TX payload page size
+ tp_pmtx_pagesize = 64K
+
+ # TP number of TX channels
+ tp_ntxch = 0 # 0 (auto) = equal number of ports
+
+ # TP_GLOBAL_CONFIG
+ reg[0x7d08] = 0x00000800/0x00000800 # set IssFromCplEnable
+
+ # LE_DB_CONFIG
+ reg[0x19c04] = 0x00400000/0x00400000 # LE Server SRAM Enable
+
+# Some "definitions" to make the rest of this a bit more readable. We support
+# 4 ports, 3 functions (NIC, FCoE and iSCSI), scaling up to 8 "CPU Queue Sets"
+# per function per port ...
+#
+# NMSIX = 1088 # available MSI-X Vectors
+# NVI = 128 # available Virtual Interfaces
+# NMPSTCAM = 336 # MPS TCAM entries
+#
+# NPORTS = 4 # ports
+# NCPUS = 8 # CPUs we want to support scalably
+# NFUNCS = 3 # functions per port (NIC, FCoE, iSCSI)
+
+# Breakdown of Virtual Interface/Queue/Interrupt resources for the "Unified
+# PF" which many OS Drivers will use to manage most or all functions.
+#
+# Each Ingress Queue can use one MSI-X interrupt but some Ingress Queues can
+# use Forwarded Interrupt Ingress Queues. For these latter, an Ingress Queue
+# would be created and the Queue ID of a Forwarded Interrupt Ingress Queue
+# will be specified as the "Ingress Queue Asynchronous Destination Index."
+# Thus, the number of MSI-X Vectors assigned to the Unified PF will be less
+# than or equal to the number of Ingress Queues ...
+#
+# NVI_NIC = 4 # NIC access to NPORTS
+# NFLIQ_NIC = 32 # NIC Ingress Queues with Free Lists
+# NETHCTRL_NIC = 32 # NIC Ethernet Control/TX Queues
+# NEQ_NIC = 64 # NIC Egress Queues (FL, ETHCTRL/TX)
+# NMPSTCAM_NIC = 16 # NIC MPS TCAM Entries (NPORTS*4)
+# NMSIX_NIC = 32 # NIC MSI-X Interrupt Vectors (FLIQ)
+#
+# NVI_OFLD = 0 # Offload uses NIC function to access ports
+# NFLIQ_OFLD = 16 # Offload Ingress Queues with Free Lists
+# NETHCTRL_OFLD = 0 # Offload Ethernet Control/TX Queues
+# NEQ_OFLD = 16 # Offload Egress Queues (FL)
+# NMPSTCAM_OFLD = 0 # Offload MPS TCAM Entries (uses NIC's)
+# NMSIX_OFLD = 16 # Offload MSI-X Interrupt Vectors (FLIQ)
+#
+# NVI_RDMA = 0 # RDMA uses NIC function to access ports
+# NFLIQ_RDMA = 4 # RDMA Ingress Queues with Free Lists
+# NETHCTRL_RDMA = 0 # RDMA Ethernet Control/TX Queues
+# NEQ_RDMA = 4 # RDMA Egress Queues (FL)
+# NMPSTCAM_RDMA = 0 # RDMA MPS TCAM Entries (uses NIC's)
+# NMSIX_RDMA = 4 # RDMA MSI-X Interrupt Vectors (FLIQ)
+#
+# NEQ_WD = 128 # Wire Direct TX Queues and FLs
+# NETHCTRL_WD = 64 # Wire Direct TX Queues
+# NFLIQ_WD = 64 ` # Wire Direct Ingress Queues with Free Lists
+#
+# NVI_ISCSI = 4 # ISCSI access to NPORTS
+# NFLIQ_ISCSI = 4 # ISCSI Ingress Queues with Free Lists
+# NETHCTRL_ISCSI = 0 # ISCSI Ethernet Control/TX Queues
+# NEQ_ISCSI = 4 # ISCSI Egress Queues (FL)
+# NMPSTCAM_ISCSI = 4 # ISCSI MPS TCAM Entries (NPORTS)
+# NMSIX_ISCSI = 4 # ISCSI MSI-X Interrupt Vectors (FLIQ)
+#
+# NVI_FCOE = 4 # FCOE access to NPORTS
+# NFLIQ_FCOE = 34 # FCOE Ingress Queues with Free Lists
+# NETHCTRL_FCOE = 32 # FCOE Ethernet Control/TX Queues
+# NEQ_FCOE = 66 # FCOE Egress Queues (FL)
+# NMPSTCAM_FCOE = 32 # FCOE MPS TCAM Entries (NPORTS)
+# NMSIX_FCOE = 34 # FCOE MSI-X Interrupt Vectors (FLIQ)
+
+# Two extra Ingress Queues per function for Firmware Events and Forwarded
+# Interrupts, and two extra interrupts per function for Firmware Events (or a
+# Forwarded Interrupt Queue) and General Interrupts per function.
+#
+# NFLIQ_EXTRA = 6 # "extra" Ingress Queues 2*NFUNCS (Firmware and
+# # Forwarded Interrupts
+# NMSIX_EXTRA = 6 # extra interrupts 2*NFUNCS (Firmware and
+# # General Interrupts
+
+# Microsoft HyperV resources. The HyperV Virtual Ingress Queues will have
+# their interrupts forwarded to another set of Forwarded Interrupt Queues.
+#
+# NVI_HYPERV = 16 # VMs we want to support
+# NVIIQ_HYPERV = 2 # Virtual Ingress Queues with Free Lists per VM
+# NFLIQ_HYPERV = 40 # VIQs + NCPUS Forwarded Interrupt Queues
+# NEQ_HYPERV = 32 # VIQs Free Lists
+# NMPSTCAM_HYPERV = 16 # MPS TCAM Entries (NVI_HYPERV)
+# NMSIX_HYPERV = 8 # NCPUS Forwarded Interrupt Queues
+
+# Adding all of the above Unified PF resource needs together: (NIC + OFLD +
+# RDMA + ISCSI + FCOE + EXTRA + HYPERV)
+#
+# NVI_UNIFIED = 28
+# NFLIQ_UNIFIED = 106
+# NETHCTRL_UNIFIED = 32
+# NEQ_UNIFIED = 124
+# NMPSTCAM_UNIFIED = 40
+#
+# The sum of all the MSI-X resources above is 74 MSI-X Vectors but we'll round
+# that up to 128 to make sure the Unified PF doesn't run out of resources.
+#
+# NMSIX_UNIFIED = 128
+#
+# The Storage PFs could need up to NPORTS*NCPUS + NMSIX_EXTRA MSI-X Vectors
+# which is 34 but they're probably safe with 32.
+#
+# NMSIX_STORAGE = 32
+
+# Note: The UnifiedPF is PF4 which doesn't have any Virtual Functions
+# associated with it. Thus, the MSI-X Vector allocations we give to the
+# UnifiedPF aren't inherited by any Virtual Functions. As a result we can
+# provision many more Virtual Functions than we can if the UnifiedPF were
+# one of PF0-3.
+#
+
+# All of the below PCI-E parameters are actually stored in various *_init.txt
+# files. We include them below essentially as comments.
+#
+# For PF0-3 we assign 8 vectors each for NIC Ingress Queues of the associated
+# ports 0-3.
+#
+# For PF4, the Unified PF, we give it an MSI-X Table Size as outlined above.
+#
+# For PF5-6 we assign enough MSI-X Vectors to support FCoE and iSCSI
+# storage applications across all four possible ports.
+#
+# Additionally, since the UnifiedPF isn't one of the per-port Physical
+# Functions, we give the UnifiedPF and the PF0-3 Physical Functions
+# different PCI Device IDs which will allow Unified and Per-Port Drivers
+# to directly select the type of Physical Function to which they wish to be
+# attached.
+#
+# Note that the actual values used for the PCI-E Intelectual Property will be
+# 1 less than those below since that's the way it "counts" things. For
+# readability, we use the number we actually mean ...
+#
+# PF0_INT = 8 # NCPUS
+# PF1_INT = 8 # NCPUS
+# PF2_INT = 8 # NCPUS
+# PF3_INT = 8 # NCPUS
+# PF0_3_INT = 32 # PF0_INT + PF1_INT + PF2_INT + PF3_INT
+#
+# PF4_INT = 128 # NMSIX_UNIFIED
+# PF5_INT = 32 # NMSIX_STORAGE
+# PF6_INT = 32 # NMSIX_STORAGE
+# PF7_INT = 0 # Nothing Assigned
+# PF4_7_INT = 192 # PF4_INT + PF5_INT + PF6_INT + PF7_INT
+#
+# PF0_7_INT = 224 # PF0_3_INT + PF4_7_INT
+#
+# With the above we can get 17 VFs/PF0-3 (limited by 336 MPS TCAM entries)
+# but we'll lower that to 16 to make our total 64 and a nice power of 2 ...
+#
+# NVF = 16
+
+# For those OSes which manage different ports on different PFs, we need
+# only enough resources to support a single port's NIC application functions
+# on PF0-3. The below assumes that we're only doing NIC with NCPUS "Queue
+# Sets" for ports 0-3. The FCoE and iSCSI functions for such OSes will be
+# managed on the "storage PFs" (see below).
+#
+[function "0"]
+ nvf = 16 # NVF on this function
+ wx_caps = all # write/execute permissions for all commands
+ r_caps = all # read permissions for all commands
+ nvi = 1 # 1 port
+ niqflint = 8 # NCPUS "Queue Sets"
+ nethctrl = 8 # NCPUS "Queue Sets"
+ neq = 16 # niqflint + nethctrl Egress Queues
+ nexactf = 8 # number of exact MPSTCAM MAC filters
+ cmask = all # access to all channels
+ pmask = 0x1 # access to only one port
+
+[function "1"]
+ nvf = 16 # NVF on this function
+ wx_caps = all # write/execute permissions for all commands
+ r_caps = all # read permissions for all commands
+ nvi = 1 # 1 port
+ niqflint = 8 # NCPUS "Queue Sets"
+ nethctrl = 8 # NCPUS "Queue Sets"
+ neq = 16 # niqflint + nethctrl Egress Queues
+ nexactf = 8 # number of exact MPSTCAM MAC filters
+ cmask = all # access to all channels
+ pmask = 0x2 # access to only one port
+
+[function "2"]
+ nvf = 16 # NVF on this function
+ wx_caps = all # write/execute permissions for all commands
+ r_caps = all # read permissions for all commands
+ nvi = 1 # 1 port
+ niqflint = 8 # NCPUS "Queue Sets"
+ nethctrl = 8 # NCPUS "Queue Sets"
+ neq = 16 # niqflint + nethctrl Egress Queues
+ nexactf = 8 # number of exact MPSTCAM MAC filters
+ cmask = all # access to all channels
+ pmask = 0x4 # access to only one port
+
+[function "3"]
+ nvf = 16 # NVF on this function
+ wx_caps = all # write/execute permissions for all commands
+ r_caps = all # read permissions for all commands
+ nvi = 1 # 1 port
+ niqflint = 8 # NCPUS "Queue Sets"
+ nethctrl = 8 # NCPUS "Queue Sets"
+ neq = 16 # niqflint + nethctrl Egress Queues
+ nexactf = 8 # number of exact MPSTCAM MAC filters
+ cmask = all # access to all channels
+ pmask = 0x8 # access to only one port
+
+# Some OS Drivers manage all application functions for all ports via PF4.
+# Thus we need to provide a large number of resources here. For Egress
+# Queues we need to account for both TX Queues as well as Free List Queues
+# (because the host is responsible for producing Free List Buffers for the
+# hardware to consume).
+#
+[function "4"]
+ wx_caps = all # write/execute permissions for all commands
+ r_caps = all # read permissions for all commands
+ nvi = 28 # NVI_UNIFIED
+ niqflint = 170 # NFLIQ_UNIFIED + NLFIQ_WD
+ nethctrl = 100 # NETHCTRL_UNIFIED + NETHCTRL_WD
+ neq = 256 # NEQ_UNIFIED + NEQ_WD
+ nexactf = 40 # NMPSTCAM_UNIFIED
+ cmask = all # access to all channels
+ pmask = all # access to all four ports ...
+ nethofld = 1024 # number of user mode ethernet flow contexts
+ nroute = 32 # number of routing region entries
+ nclip = 32 # number of clip region entries
+ nfilter = 496 # number of filter region entries
+ nserver = 496 # number of server region entries
+ nhash = 12288 # number of hash region entries
+ protocol = nic_vm, ofld, rddp, rdmac, iscsi_initiator_pdu, iscsi_target_pdu
+ tp_l2t = 3072
+ tp_ddp = 2
+ tp_ddp_iscsi = 2
+ tp_stag = 2
+ tp_pbl = 5
+ tp_rq = 7
+
+# We have FCoE and iSCSI storage functions on PF5 and PF6 each of which may
+# need to have Virtual Interfaces on each of the four ports with up to NCPUS
+# "Queue Sets" each.
+#
+[function "5"]
+ wx_caps = all # write/execute permissions for all commands
+ r_caps = all # read permissions for all commands
+ nvi = 4 # NPORTS
+ niqflint = 34 # NPORTS*NCPUS + NMSIX_EXTRA
+ nethctrl = 32 # NPORTS*NCPUS
+ neq = 64 # NPORTS*NCPUS * 2 (FL, ETHCTRL/TX)
+ nexactf = 4 # NPORTS
+ cmask = all # access to all channels
+ pmask = all # access to all four ports ...
+ nserver = 16
+ nhash = 2048
+ tp_l2t = 1024
+ protocol = iscsi_initiator_fofld
+ tp_ddp_iscsi = 2
+ iscsi_ntask = 2048
+ iscsi_nsess = 2048
+ iscsi_nconn_per_session = 1
+ iscsi_ninitiator_instance = 64
+
+[function "6"]
+ wx_caps = all # write/execute permissions for all commands
+ r_caps = all # read permissions for all commands
+ nvi = 4 # NPORTS
+ niqflint = 34 # NPORTS*NCPUS + NMSIX_EXTRA
+ nethctrl = 32 # NPORTS*NCPUS
+ neq = 66 # NPORTS*NCPUS * 2 (FL, ETHCTRL/TX) + 2 (EXTRA)
+ nexactf = 32 # NPORTS + adding 28 exact entries for FCoE
+ # which is OK since < MIN(SUM PF0..3, PF4)
+ # and we never load PF0..3 and PF4 concurrently
+ cmask = all # access to all channels
+ pmask = all # access to all four ports ...
+ nhash = 2048
+ protocol = fcoe_initiator
+ tp_ddp = 2
+ fcoe_nfcf = 16
+ fcoe_nvnp = 32
+ fcoe_nssn = 1024
+
+# The following function, 1023, is not an actual PCIE function but is used to
+# configure and reserve firmware internal resources that come from the global
+# resource pool.
+#
+[function "1023"]
+ wx_caps = all # write/execute permissions for all commands
+ r_caps = all # read permissions for all commands
+ nvi = 4 # NVI_UNIFIED
+ cmask = all # access to all channels
+ pmask = all # access to all four ports ...
+ nexactf = 8 # NPORTS + DCBX +
+ nfilter = 16 # number of filter region entries
+
+# For Virtual functions, we only allow NIC functionality and we only allow
+# access to one port (1 << PF). Note that because of limitations in the
+# Scatter Gather Engine (SGE) hardware which checks writes to VF KDOORBELL
+# and GTS registers, the number of Ingress and Egress Queues must be a power
+# of 2.
+#
+[function "0/*"] # NVF
+ wx_caps = 0x82 # DMAQ | VF
+ r_caps = 0x86 # DMAQ | VF | PORT
+ nvi = 1 # 1 port
+ niqflint = 4 # 2 "Queue Sets" + NXIQ
+ nethctrl = 2 # 2 "Queue Sets"
+ neq = 4 # 2 "Queue Sets" * 2
+ nexactf = 4
+ cmask = all # access to all channels
+ pmask = 0x1 # access to only one port ...
+
+[function "1/*"] # NVF
+ wx_caps = 0x82 # DMAQ | VF
+ r_caps = 0x86 # DMAQ | VF | PORT
+ nvi = 1 # 1 port
+ niqflint = 4 # 2 "Queue Sets" + NXIQ
+ nethctrl = 2 # 2 "Queue Sets"
+ neq = 4 # 2 "Queue Sets" * 2
+ nexactf = 4
+ cmask = all # access to all channels
+ pmask = 0x2 # access to only one port ...
+
+[function "2/*"] # NVF
+ wx_caps = 0x82 # DMAQ | VF
+ r_caps = 0x86 # DMAQ | VF | PORT
+ nvi = 1 # 1 port
+ niqflint = 4 # 2 "Queue Sets" + NXIQ
+ nethctrl = 2 # 2 "Queue Sets"
+ neq = 4 # 2 "Queue Sets" * 2
+ nexactf = 4
+ cmask = all # access to all channels
+ pmask = 0x4 # access to only one port ...
+
+[function "3/*"] # NVF
+ wx_caps = 0x82 # DMAQ | VF
+ r_caps = 0x86 # DMAQ | VF | PORT
+ nvi = 1 # 1 port
+ niqflint = 4 # 2 "Queue Sets" + NXIQ
+ nethctrl = 2 # 2 "Queue Sets"
+ neq = 4 # 2 "Queue Sets" * 2
+ nexactf = 4
+ cmask = all # access to all channels
+ pmask = 0x8 # access to only one port ...
+
+# MPS features a 196608 bytes ingress buffer that is used for ingress buffering
+# for packets from the wire as well as the loopback path of the L2 switch. The
+# folling params control how the buffer memory is distributed and the L2 flow
+# control settings:
+#
+# bg_mem: %-age of mem to use for port/buffer group
+# lpbk_mem: %-age of port/bg mem to use for loopback
+# hwm: high watermark; bytes available when starting to send pause
+# frames (in units of 0.1 MTU)
+# lwm: low watermark; bytes remaining when sending 'unpause' frame
+# (in inuits of 0.1 MTU)
+# dwm: minimum delta between high and low watermark (in units of 100
+# Bytes)
+#
+[port "0"]
+ dcb = ppp, dcbx # configure for DCB PPP and enable DCBX offload
+ bg_mem = 25
+ lpbk_mem = 25
+ hwm = 30
+ lwm = 15
+ dwm = 30
+
+[port "1"]
+ dcb = ppp, dcbx
+ bg_mem = 25
+ lpbk_mem = 25
+ hwm = 30
+ lwm = 15
+ dwm = 30
+
+[port "2"]
+ dcb = ppp, dcbx
+ bg_mem = 25
+ lpbk_mem = 25
+ hwm = 30
+ lwm = 15
+ dwm = 30
+
+[port "3"]
+ dcb = ppp, dcbx
+ bg_mem = 25
+ lpbk_mem = 25
+ hwm = 30
+ lwm = 15
+ dwm = 30
+
+[fini]
+ version = 0x1425000f
+ checksum = 0x23a2d850
+
+# Total resources used by above allocations:
+# Virtual Interfaces: 104
+# Ingress Queues/w Free Lists and Interrupts: 526
+# Egress Queues: 702
+# MPS TCAM Entries: 336
+# MSI-X Vectors: 736
+# Virtual Functions: 64
+#
+# $FreeBSD$
+#
diff --git a/sys/dev/cxgbe/offload.h b/sys/dev/cxgbe/offload.h
index 6090775..7f4b38e 100644
--- a/sys/dev/cxgbe/offload.h
+++ b/sys/dev/cxgbe/offload.h
@@ -140,6 +140,7 @@ struct tom_tunables {
int ddp;
int indsz;
int ddp_thres;
+ int rx_coalesce;
};
int t4_register_uld(struct uld_info *);
diff --git a/sys/dev/cxgbe/t4_main.c b/sys/dev/cxgbe/t4_main.c
index e3836ce..762540e 100644
--- a/sys/dev/cxgbe/t4_main.c
+++ b/sys/dev/cxgbe/t4_main.c
@@ -373,6 +373,7 @@ static int t4_sysctls(struct adapter *);
static int cxgbe_sysctls(struct port_info *);
static int sysctl_int_array(SYSCTL_HANDLER_ARGS);
static int sysctl_bitfield(SYSCTL_HANDLER_ARGS);
+static int sysctl_btphy(SYSCTL_HANDLER_ARGS);
static int sysctl_holdoff_tmr_idx(SYSCTL_HANDLER_ARGS);
static int sysctl_holdoff_pktc_idx(SYSCTL_HANDLER_ARGS);
static int sysctl_qsize_rxq(SYSCTL_HANDLER_ARGS);
@@ -633,9 +634,6 @@ t4_attach(device_t dev)
if (rc != 0)
goto done; /* error message displayed already */
- for (i = 0; i < NCHAN; i++)
- sc->params.tp.tx_modq[i] = i;
-
rc = t4_create_dma_tag(sc);
if (rc != 0)
goto done; /* error message displayed already */
@@ -682,6 +680,7 @@ t4_attach(device_t dev)
}
pi->xact_addr_filt = -1;
+ pi->linkdnrc = -1;
pi->qsize_rxq = t4_qsize_rxq;
pi->qsize_txq = t4_qsize_txq;
@@ -1075,9 +1074,10 @@ cxgbe_ioctl(struct ifnet *ifp, unsigned long cmd, caddr_t data)
if (rc)
return (rc);
ifp->if_mtu = mtu;
- if (ifp->if_drv_flags & IFF_DRV_RUNNING) {
+ if (pi->flags & PORT_INIT_DONE) {
t4_update_fl_bufsize(ifp);
- rc = update_mac_settings(pi, XGMAC_MTU);
+ if (ifp->if_drv_flags & IFF_DRV_RUNNING)
+ rc = update_mac_settings(pi, XGMAC_MTU);
}
end_synchronized_op(sc, 0);
break;
@@ -1826,11 +1826,11 @@ allocate:
}
#define FW_VERSION(chip) ( \
- V_FW_HDR_FW_VER_MAJOR(FW_VERSION_MAJOR_##chip) | \
- V_FW_HDR_FW_VER_MINOR(FW_VERSION_MINOR_##chip) | \
- V_FW_HDR_FW_VER_MICRO(FW_VERSION_MICRO_##chip) | \
- V_FW_HDR_FW_VER_BUILD(FW_VERSION_BUILD_##chip))
-#define FW_INTFVER(chip, intf) (FW_HDR_INTFVER_##intf)
+ V_FW_HDR_FW_VER_MAJOR(chip##FW_VERSION_MAJOR) | \
+ V_FW_HDR_FW_VER_MINOR(chip##FW_VERSION_MINOR) | \
+ V_FW_HDR_FW_VER_MICRO(chip##FW_VERSION_MICRO) | \
+ V_FW_HDR_FW_VER_BUILD(chip##FW_VERSION_BUILD))
+#define FW_INTFVER(chip, intf) (chip##FW_HDR_INTFVER_##intf)
struct fw_info {
uint8_t chip;
@@ -2089,6 +2089,7 @@ prep_firmware(struct adapter *sc)
G_FW_HDR_FW_VER_MINOR(sc->params.fw_vers),
G_FW_HDR_FW_VER_MICRO(sc->params.fw_vers),
G_FW_HDR_FW_VER_BUILD(sc->params.fw_vers));
+ t4_get_tp_version(sc, &sc->params.tp_vers);
/* Reset device */
if (need_fw_reset &&
@@ -2932,7 +2933,8 @@ cxgbe_uninit_synchronized(struct port_info *pi)
pi->link_cfg.link_ok = 0;
pi->link_cfg.speed = 0;
- t4_os_link_changed(sc, pi->port_id, 0);
+ pi->linkdnrc = -1;
+ t4_os_link_changed(sc, pi->port_id, 0, -1);
return (0);
}
@@ -3634,7 +3636,7 @@ t4_get_regs(struct adapter *sc, struct t4_regdump *regs, uint8_t *buf)
0x1fee0, 0x1fee0,
0x1ff00, 0x1ff84,
0x1ffc0, 0x1ffc8,
- 0x30000, 0x30040,
+ 0x30000, 0x30030,
0x30100, 0x30144,
0x30190, 0x301d0,
0x30200, 0x30318,
@@ -3643,29 +3645,29 @@ t4_get_regs(struct adapter *sc, struct t4_regdump *regs, uint8_t *buf)
0x30800, 0x30834,
0x308c0, 0x30908,
0x30910, 0x309ac,
- 0x30a00, 0x30a04,
- 0x30a0c, 0x30a2c,
+ 0x30a00, 0x30a2c,
0x30a44, 0x30a50,
0x30a74, 0x30c24,
+ 0x30d00, 0x30d00,
0x30d08, 0x30d14,
0x30d1c, 0x30d20,
0x30d3c, 0x30d50,
0x31200, 0x3120c,
0x31220, 0x31220,
0x31240, 0x31240,
- 0x31600, 0x31600,
- 0x31608, 0x3160c,
+ 0x31600, 0x3160c,
0x31a00, 0x31a1c,
- 0x31e04, 0x31e20,
+ 0x31e00, 0x31e20,
0x31e38, 0x31e3c,
0x31e80, 0x31e80,
0x31e88, 0x31ea8,
0x31eb0, 0x31eb4,
0x31ec8, 0x31ed4,
0x31fb8, 0x32004,
- 0x32208, 0x3223c,
- 0x32248, 0x3227c,
- 0x32288, 0x322bc,
+ 0x32200, 0x32200,
+ 0x32208, 0x32240,
+ 0x32248, 0x32280,
+ 0x32288, 0x322c0,
0x322c8, 0x322fc,
0x32600, 0x32630,
0x32a00, 0x32abc,
@@ -3697,7 +3699,7 @@ t4_get_regs(struct adapter *sc, struct t4_regdump *regs, uint8_t *buf)
0x33c28, 0x33c28,
0x33c3c, 0x33c50,
0x33cf0, 0x33cfc,
- 0x34000, 0x34040,
+ 0x34000, 0x34030,
0x34100, 0x34144,
0x34190, 0x341d0,
0x34200, 0x34318,
@@ -3706,29 +3708,29 @@ t4_get_regs(struct adapter *sc, struct t4_regdump *regs, uint8_t *buf)
0x34800, 0x34834,
0x348c0, 0x34908,
0x34910, 0x349ac,
- 0x34a00, 0x34a04,
- 0x34a0c, 0x34a2c,
+ 0x34a00, 0x34a2c,
0x34a44, 0x34a50,
0x34a74, 0x34c24,
+ 0x34d00, 0x34d00,
0x34d08, 0x34d14,
0x34d1c, 0x34d20,
0x34d3c, 0x34d50,
0x35200, 0x3520c,
0x35220, 0x35220,
0x35240, 0x35240,
- 0x35600, 0x35600,
- 0x35608, 0x3560c,
+ 0x35600, 0x3560c,
0x35a00, 0x35a1c,
- 0x35e04, 0x35e20,
+ 0x35e00, 0x35e20,
0x35e38, 0x35e3c,
0x35e80, 0x35e80,
0x35e88, 0x35ea8,
0x35eb0, 0x35eb4,
0x35ec8, 0x35ed4,
0x35fb8, 0x36004,
- 0x36208, 0x3623c,
- 0x36248, 0x3627c,
- 0x36288, 0x362bc,
+ 0x36200, 0x36200,
+ 0x36208, 0x36240,
+ 0x36248, 0x36280,
+ 0x36288, 0x362c0,
0x362c8, 0x362fc,
0x36600, 0x36630,
0x36a00, 0x36abc,
@@ -3760,7 +3762,7 @@ t4_get_regs(struct adapter *sc, struct t4_regdump *regs, uint8_t *buf)
0x37c28, 0x37c28,
0x37c3c, 0x37c50,
0x37cf0, 0x37cfc,
- 0x38000, 0x38040,
+ 0x38000, 0x38030,
0x38100, 0x38144,
0x38190, 0x381d0,
0x38200, 0x38318,
@@ -3769,29 +3771,29 @@ t4_get_regs(struct adapter *sc, struct t4_regdump *regs, uint8_t *buf)
0x38800, 0x38834,
0x388c0, 0x38908,
0x38910, 0x389ac,
- 0x38a00, 0x38a04,
- 0x38a0c, 0x38a2c,
+ 0x38a00, 0x38a2c,
0x38a44, 0x38a50,
0x38a74, 0x38c24,
+ 0x38d00, 0x38d00,
0x38d08, 0x38d14,
0x38d1c, 0x38d20,
0x38d3c, 0x38d50,
0x39200, 0x3920c,
0x39220, 0x39220,
0x39240, 0x39240,
- 0x39600, 0x39600,
- 0x39608, 0x3960c,
+ 0x39600, 0x3960c,
0x39a00, 0x39a1c,
- 0x39e04, 0x39e20,
+ 0x39e00, 0x39e20,
0x39e38, 0x39e3c,
0x39e80, 0x39e80,
0x39e88, 0x39ea8,
0x39eb0, 0x39eb4,
0x39ec8, 0x39ed4,
0x39fb8, 0x3a004,
- 0x3a208, 0x3a23c,
- 0x3a248, 0x3a27c,
- 0x3a288, 0x3a2bc,
+ 0x3a200, 0x3a200,
+ 0x3a208, 0x3a240,
+ 0x3a248, 0x3a280,
+ 0x3a288, 0x3a2c0,
0x3a2c8, 0x3a2fc,
0x3a600, 0x3a630,
0x3aa00, 0x3aabc,
@@ -3823,7 +3825,7 @@ t4_get_regs(struct adapter *sc, struct t4_regdump *regs, uint8_t *buf)
0x3bc28, 0x3bc28,
0x3bc3c, 0x3bc50,
0x3bcf0, 0x3bcfc,
- 0x3c000, 0x3c040,
+ 0x3c000, 0x3c030,
0x3c100, 0x3c144,
0x3c190, 0x3c1d0,
0x3c200, 0x3c318,
@@ -3832,29 +3834,29 @@ t4_get_regs(struct adapter *sc, struct t4_regdump *regs, uint8_t *buf)
0x3c800, 0x3c834,
0x3c8c0, 0x3c908,
0x3c910, 0x3c9ac,
- 0x3ca00, 0x3ca04,
- 0x3ca0c, 0x3ca2c,
+ 0x3ca00, 0x3ca2c,
0x3ca44, 0x3ca50,
0x3ca74, 0x3cc24,
+ 0x3cd00, 0x3cd00,
0x3cd08, 0x3cd14,
0x3cd1c, 0x3cd20,
0x3cd3c, 0x3cd50,
0x3d200, 0x3d20c,
0x3d220, 0x3d220,
0x3d240, 0x3d240,
- 0x3d600, 0x3d600,
- 0x3d608, 0x3d60c,
+ 0x3d600, 0x3d60c,
0x3da00, 0x3da1c,
- 0x3de04, 0x3de20,
+ 0x3de00, 0x3de20,
0x3de38, 0x3de3c,
0x3de80, 0x3de80,
0x3de88, 0x3dea8,
0x3deb0, 0x3deb4,
0x3dec8, 0x3ded4,
0x3dfb8, 0x3e004,
- 0x3e208, 0x3e23c,
- 0x3e248, 0x3e27c,
- 0x3e288, 0x3e2bc,
+ 0x3e200, 0x3e200,
+ 0x3e208, 0x3e240,
+ 0x3e248, 0x3e280,
+ 0x3e288, 0x3e2c0,
0x3e2c8, 0x3e2fc,
0x3e600, 0x3e630,
0x3ea00, 0x3eabc,
@@ -3893,7 +3895,7 @@ t4_get_regs(struct adapter *sc, struct t4_regdump *regs, uint8_t *buf)
0x40200, 0x40298,
0x402ac, 0x4033c,
0x403f8, 0x403fc,
- 0x41300, 0x413c4,
+ 0x41304, 0x413c4,
0x41400, 0x4141c,
0x41480, 0x414d0,
0x44000, 0x44078,
@@ -3921,7 +3923,7 @@ t4_get_regs(struct adapter *sc, struct t4_regdump *regs, uint8_t *buf)
0x48200, 0x48298,
0x482ac, 0x4833c,
0x483f8, 0x483fc,
- 0x49300, 0x493c4,
+ 0x49304, 0x493c4,
0x49400, 0x4941c,
0x49480, 0x494d0,
0x4c000, 0x4c078,
@@ -4189,6 +4191,9 @@ t4_sysctls(struct adapter *sc)
sizeof(sc->sge.counter_val), sysctl_int_array, "A",
"interrupt holdoff packet counter values");
+ SYSCTL_ADD_INT(ctx, children, OID_AUTO, "nfilters", CTLFLAG_RD,
+ NULL, sc->tids.nftids, "number of filters");
+
#ifdef SBUF_DRAIN
/*
* dev.t4nex.X.misc. Marked CTLFLAG_SKIP to avoid information overload.
@@ -4380,6 +4385,10 @@ t4_sysctls(struct adapter *sc)
G_RXCOALESCESIZE(t4_read_reg(sc, A_TP_PARA_REG2));
SYSCTL_ADD_INT(ctx, children, OID_AUTO, "ddp_thres", CTLFLAG_RW,
&sc->tt.ddp_thres, 0, "DDP threshold");
+
+ sc->tt.rx_coalesce = 1;
+ SYSCTL_ADD_INT(ctx, children, OID_AUTO, "rx_coalesce",
+ CTLFLAG_RW, &sc->tt.rx_coalesce, 0, "receive coalescing");
}
#endif
@@ -4402,6 +4411,16 @@ cxgbe_sysctls(struct port_info *pi)
oid = device_get_sysctl_tree(pi->dev);
children = SYSCTL_CHILDREN(oid);
+ SYSCTL_ADD_INT(ctx, children, OID_AUTO, "linkdnrc", CTLFLAG_RD,
+ &pi->linkdnrc, 0, "reason why link is down");
+ if (pi->port_type == FW_PORT_TYPE_BT_XAUI) {
+ SYSCTL_ADD_PROC(ctx, children, OID_AUTO, "temperature",
+ CTLTYPE_INT | CTLFLAG_RD, pi, 0, sysctl_btphy, "I",
+ "PHY temperature (in Celsius)");
+ SYSCTL_ADD_PROC(ctx, children, OID_AUTO, "fw_version",
+ CTLTYPE_INT | CTLFLAG_RD, pi, 1, sysctl_btphy, "I",
+ "PHY firmware version");
+ }
SYSCTL_ADD_INT(ctx, children, OID_AUTO, "nrxq", CTLFLAG_RD,
&pi->nrxq, 0, "# of rx queues");
SYSCTL_ADD_INT(ctx, children, OID_AUTO, "ntxq", CTLFLAG_RD,
@@ -4639,12 +4658,40 @@ sysctl_bitfield(SYSCTL_HANDLER_ARGS)
}
static int
+sysctl_btphy(SYSCTL_HANDLER_ARGS)
+{
+ struct port_info *pi = arg1;
+ int op = arg2;
+ struct adapter *sc = pi->adapter;
+ u_int v;
+ int rc;
+
+ rc = begin_synchronized_op(sc, pi, SLEEP_OK | INTR_OK, "t4btt");
+ if (rc)
+ return (rc);
+ /* XXX: magic numbers */
+ rc = -t4_mdio_rd(sc, sc->mbox, pi->mdio_addr, 0x1e, op ? 0x20 : 0xc820,
+ &v);
+ end_synchronized_op(sc, 0);
+ if (rc)
+ return (rc);
+ if (op == 0)
+ v /= 256;
+
+ rc = sysctl_handle_int(oidp, &v, 0, req);
+ return (rc);
+}
+
+static int
sysctl_holdoff_tmr_idx(SYSCTL_HANDLER_ARGS)
{
struct port_info *pi = arg1;
struct adapter *sc = pi->adapter;
int idx, rc, i;
struct sge_rxq *rxq;
+#ifdef TCP_OFFLOAD
+ struct sge_ofld_rxq *ofld_rxq;
+#endif
uint8_t v;
idx = pi->tmr_idx;
@@ -4669,6 +4716,15 @@ sysctl_holdoff_tmr_idx(SYSCTL_HANDLER_ARGS)
rxq->iq.intr_params = v;
#endif
}
+#ifdef TCP_OFFLOAD
+ for_each_ofld_rxq(pi, i, ofld_rxq) {
+#ifdef atomic_store_rel_8
+ atomic_store_rel_8(&ofld_rxq->iq.intr_params, v);
+#else
+ ofld_rxq->iq.intr_params = v;
+#endif
+ }
+#endif
pi->tmr_idx = idx;
end_synchronized_op(sc, LOCK_HELD);
@@ -6519,13 +6575,14 @@ get_filter_mode(struct adapter *sc, uint32_t *mode)
t4_read_indirect(sc, A_TP_PIO_ADDR, A_TP_PIO_DATA, &fconf, 1,
A_TP_VLAN_PRI_MAP);
- if (sc->filter_mode != fconf) {
+ if (sc->params.tp.vlan_pri_map != fconf) {
log(LOG_WARNING, "%s: cached filter mode out of sync %x %x.\n",
- device_get_nameunit(sc->dev), sc->filter_mode, fconf);
- sc->filter_mode = fconf;
+ device_get_nameunit(sc->dev), sc->params.tp.vlan_pri_map,
+ fconf);
+ sc->params.tp.vlan_pri_map = fconf;
}
- *mode = fconf_to_mode(sc->filter_mode);
+ *mode = fconf_to_mode(sc->params.tp.vlan_pri_map);
end_synchronized_op(sc, LOCK_HELD);
return (0);
@@ -6658,7 +6715,8 @@ set_filter(struct adapter *sc, struct t4_filter *t)
}
/* Validate against the global filter mode */
- if ((sc->filter_mode | fspec_to_fconf(&t->fs)) != sc->filter_mode) {
+ if ((sc->params.tp.vlan_pri_map | fspec_to_fconf(&t->fs)) !=
+ sc->params.tp.vlan_pri_map) {
rc = E2BIG;
goto done;
}
@@ -7171,16 +7229,20 @@ t4_os_portmod_changed(const struct adapter *sc, int idx)
}
void
-t4_os_link_changed(struct adapter *sc, int idx, int link_stat)
+t4_os_link_changed(struct adapter *sc, int idx, int link_stat, int reason)
{
struct port_info *pi = sc->port[idx];
struct ifnet *ifp = pi->ifp;
if (link_stat) {
+ pi->linkdnrc = -1;
ifp->if_baudrate = IF_Mbps(pi->link_cfg.speed);
if_link_state_change(ifp, LINK_STATE_UP);
- } else
+ } else {
+ if (reason >= 0)
+ pi->linkdnrc = reason;
if_link_state_change(ifp, LINK_STATE_DOWN);
+ }
}
void
diff --git a/sys/dev/cxgbe/t4_sge.c b/sys/dev/cxgbe/t4_sge.c
index 84fb44e..586137a 100644
--- a/sys/dev/cxgbe/t4_sge.c
+++ b/sys/dev/cxgbe/t4_sge.c
@@ -474,16 +474,11 @@ t4_read_chip_settings(struct adapter *sc)
s->s_qpp = r & M_QUEUESPERPAGEPF0;
}
- r = t4_read_reg(sc, A_TP_TIMER_RESOLUTION);
- sc->params.tp.tre = G_TIMERRESOLUTION(r);
- sc->params.tp.dack_re = G_DELAYEDACKRESOLUTION(r);
+ t4_init_tp_params(sc);
t4_read_mtu_tbl(sc, sc->params.mtus, NULL);
t4_load_mtus(sc, sc->params.mtus, sc->params.a_wnd, sc->params.b_wnd);
- t4_read_indirect(sc, A_TP_PIO_ADDR, A_TP_PIO_DATA, &sc->filter_mode, 1,
- A_TP_VLAN_PRI_MAP);
-
return (rc);
}
@@ -664,6 +659,18 @@ mtu_to_bufsize(int mtu)
return (bufsize);
}
+#ifdef TCP_OFFLOAD
+static inline int
+mtu_to_bufsize_toe(struct adapter *sc, int mtu)
+{
+
+ if (sc->tt.rx_coalesce)
+ return (G_RXCOALESCESIZE(t4_read_reg(sc, A_TP_PARA_REG2)));
+
+ return (mtu);
+}
+#endif
+
int
t4_setup_port_queues(struct port_info *pi)
{
@@ -678,9 +685,10 @@ t4_setup_port_queues(struct port_info *pi)
#endif
char name[16];
struct adapter *sc = pi->adapter;
+ struct ifnet *ifp = pi->ifp;
struct sysctl_oid *oid = device_get_sysctl_tree(pi->dev);
struct sysctl_oid_list *children = SYSCTL_CHILDREN(oid);
- int bufsize = mtu_to_bufsize(pi->ifp->if_mtu);
+ int bufsize;
oid = SYSCTL_ADD_NODE(&pi->ctx, children, OID_AUTO, "rxq", CTLFLAG_RD,
NULL, "rx queues");
@@ -701,6 +709,7 @@ t4_setup_port_queues(struct port_info *pi)
* a) initialize iq and fl
* b) allocate queue iff it will take direct interrupts.
*/
+ bufsize = mtu_to_bufsize(ifp->if_mtu);
for_each_rxq(pi, i, rxq) {
init_iq(&rxq->iq, sc, pi->tmr_idx, pi->pktc_idx, pi->qsize_rxq,
@@ -724,6 +733,7 @@ t4_setup_port_queues(struct port_info *pi)
}
#ifdef TCP_OFFLOAD
+ bufsize = mtu_to_bufsize_toe(sc, ifp->if_mtu);
for_each_ofld_rxq(pi, i, ofld_rxq) {
init_iq(&ofld_rxq->iq, sc, pi->tmr_idx, pi->pktc_idx,
@@ -731,7 +741,7 @@ t4_setup_port_queues(struct port_info *pi)
snprintf(name, sizeof(name), "%s ofld_rxq%d-fl",
device_get_nameunit(pi->dev), i);
- init_fl(&ofld_rxq->fl, pi->qsize_rxq / 8, OFLD_BUF_SIZE, name);
+ init_fl(&ofld_rxq->fl, pi->qsize_rxq / 8, bufsize, name);
if (sc->flags & INTR_DIRECT ||
(sc->intr_count > 1 && pi->nofldrxq > pi->nrxq)) {
@@ -1342,7 +1352,7 @@ t4_wrq_tx_locked(struct adapter *sc, struct sge_wrq *wrq, struct wrqe *wr)
eq->pidx -= eq->cap;
eq->pending += ndesc;
- if (eq->pending > 16)
+ if (eq->pending >= 8)
ring_eq_db(sc, eq);
wrq->tx_wrs++;
@@ -1513,8 +1523,8 @@ t4_eth_tx(struct ifnet *ifp, struct sge_txq *txq, struct mbuf *m)
if (sgl.nsegs == 0)
m_freem(m);
doorbell:
- if (eq->pending >= 64)
- ring_eq_db(sc, eq);
+ if (eq->pending >= 8)
+ ring_eq_db(sc, eq);
can_reclaim = reclaimable(eq);
if (can_reclaim >= 32)
@@ -1562,9 +1572,13 @@ t4_update_fl_bufsize(struct ifnet *ifp)
{
struct port_info *pi = ifp->if_softc;
struct sge_rxq *rxq;
+#ifdef TCP_OFFLOAD
+ struct sge_ofld_rxq *ofld_rxq;
+#endif
struct sge_fl *fl;
- int i, bufsize = mtu_to_bufsize(ifp->if_mtu);
+ int i, bufsize;
+ bufsize = mtu_to_bufsize(ifp->if_mtu);
for_each_rxq(pi, i, rxq) {
fl = &rxq->fl;
@@ -1572,6 +1586,16 @@ t4_update_fl_bufsize(struct ifnet *ifp)
set_fl_tag_idx(fl, bufsize);
FL_UNLOCK(fl);
}
+#ifdef TCP_OFFLOAD
+ bufsize = mtu_to_bufsize_toe(pi->adapter, ifp->if_mtu);
+ for_each_ofld_rxq(pi, i, ofld_rxq) {
+ fl = &ofld_rxq->fl;
+
+ FL_LOCK(fl);
+ set_fl_tag_idx(fl, bufsize);
+ FL_UNLOCK(fl);
+ }
+#endif
}
int
diff --git a/sys/dev/cxgbe/tom/t4_connect.c b/sys/dev/cxgbe/tom/t4_connect.c
index ac48d31..08267cd 100644
--- a/sys/dev/cxgbe/tom/t4_connect.c
+++ b/sys/dev/cxgbe/tom/t4_connect.c
@@ -57,6 +57,7 @@ __FBSDID("$FreeBSD$");
#include "common/common.h"
#include "common/t4_msg.h"
#include "common/t4_regs.h"
+#include "common/t4_regs_values.h"
#include "tom/t4_tom_l2t.h"
#include "tom/t4_tom.h"
@@ -246,9 +247,12 @@ calc_opt2a(struct socket *so, struct toepcb *toep)
/* RX_COALESCE is always a valid value (M_RX_COALESCE). */
if (is_t4(sc))
opt2 |= F_RX_COALESCE_VALID;
- else
+ else {
opt2 |= F_T5_OPT_2_VALID;
- opt2 |= V_RX_COALESCE(M_RX_COALESCE);
+ opt2 |= F_CONG_CNTRL_VALID; /* OPT_2_ISS really, for T5 */
+ }
+ if (sc->tt.rx_coalesce)
+ opt2 |= V_RX_COALESCE(M_RX_COALESCE);
#ifdef USE_DDP_RX_FLOW_CONTROL
if (toep->ulp_mode == ULP_MODE_TCPDDP)
@@ -384,10 +388,18 @@ t4_connect(struct toedev *tod, struct socket *so, struct rtentry *rt,
if (toep->ce == NULL)
DONT_OFFLOAD_ACTIVE_OPEN(ENOENT);
- INIT_TP_WR(cpl, 0);
+ if (is_t4(sc)) {
+ INIT_TP_WR(cpl, 0);
+ cpl->params = select_ntuple(pi, toep->l2te);
+ } else {
+ struct cpl_t5_act_open_req6 *c5 = (void *)cpl;
+
+ INIT_TP_WR(c5, 0);
+ c5->iss = htobe32(tp->iss);
+ c5->params = select_ntuple(pi, toep->l2te);
+ }
OPCODE_TID(cpl) = htobe32(MK_OPCODE_TID(CPL_ACT_OPEN_REQ6,
qid_atid));
-
cpl->local_port = inp->inp_lport;
cpl->local_ip_hi = *(uint64_t *)&inp->in6p_laddr.s6_addr[0];
cpl->local_ip_lo = *(uint64_t *)&inp->in6p_laddr.s6_addr[8];
@@ -397,20 +409,19 @@ t4_connect(struct toedev *tod, struct socket *so, struct rtentry *rt,
cpl->opt0 = calc_opt0(so, pi, toep->l2te, mtu_idx, rscale,
toep->rx_credits, toep->ulp_mode);
cpl->opt2 = calc_opt2a(so, toep);
+ } else {
+ struct cpl_act_open_req *cpl = wrtod(wr);
+
if (is_t4(sc)) {
- cpl->params = select_ntuple(pi, toep->l2te,
- sc->filter_mode);
+ INIT_TP_WR(cpl, 0);
+ cpl->params = select_ntuple(pi, toep->l2te);
} else {
- struct cpl_t5_act_open_req6 *c5 = (void *)cpl;
+ struct cpl_t5_act_open_req *c5 = (void *)cpl;
- c5->rsvd = 0;
- c5->params = select_ntuple(pi, toep->l2te,
- sc->filter_mode);
+ INIT_TP_WR(c5, 0);
+ c5->iss = htobe32(tp->iss);
+ c5->params = select_ntuple(pi, toep->l2te);
}
- } else {
- struct cpl_act_open_req *cpl = wrtod(wr);
-
- INIT_TP_WR(cpl, 0);
OPCODE_TID(cpl) = htobe32(MK_OPCODE_TID(CPL_ACT_OPEN_REQ,
qid_atid));
inp_4tuple_get(inp, &cpl->local_ip, &cpl->local_port,
@@ -418,16 +429,6 @@ t4_connect(struct toedev *tod, struct socket *so, struct rtentry *rt,
cpl->opt0 = calc_opt0(so, pi, toep->l2te, mtu_idx, rscale,
toep->rx_credits, toep->ulp_mode);
cpl->opt2 = calc_opt2a(so, toep);
- if (is_t4(sc)) {
- cpl->params = select_ntuple(pi, toep->l2te,
- sc->filter_mode);
- } else {
- struct cpl_t5_act_open_req6 *c5 = (void *)cpl;
-
- c5->rsvd = 0;
- c5->params = select_ntuple(pi, toep->l2te,
- sc->filter_mode);
- }
}
CTR5(KTR_CXGBE, "%s: atid %u (%s), toep %p, inp %p", __func__,
diff --git a/sys/dev/cxgbe/tom/t4_listen.c b/sys/dev/cxgbe/tom/t4_listen.c
index 7db4126..9e1dc80 100644
--- a/sys/dev/cxgbe/tom/t4_listen.c
+++ b/sys/dev/cxgbe/tom/t4_listen.c
@@ -674,6 +674,12 @@ t4_syncache_respond(struct toedev *tod, void *arg, struct mbuf *m)
synqe->iss = be32toh(th->th_seq);
synqe->ts = to.to_tsval;
+ if (is_t5(sc)) {
+ struct cpl_t5_pass_accept_rpl *rpl5 = wrtod(wr);
+
+ rpl5->iss = th->th_seq;
+ }
+
e = &sc->l2t->l2tab[synqe->l2e_idx];
t4_l2t_send(sc, wr, e);
@@ -1011,9 +1017,12 @@ calc_opt2p(struct adapter *sc, struct port_info *pi, int rxqid,
/* RX_COALESCE is always a valid value (0 or M_RX_COALESCE). */
if (is_t4(sc))
opt2 |= F_RX_COALESCE_VALID;
- else
+ else {
opt2 |= F_T5_OPT_2_VALID;
- opt2 |= V_RX_COALESCE(M_RX_COALESCE);
+ opt2 |= F_CONG_CNTRL_VALID; /* OPT_2_ISS really, for T5 */
+ }
+ if (sc->tt.rx_coalesce)
+ opt2 |= V_RX_COALESCE(M_RX_COALESCE);
#ifdef USE_DDP_RX_FLOW_CONTROL
if (ulp_mode == ULP_MODE_TCPDDP)
@@ -1287,7 +1296,8 @@ do_pass_accept_req(struct sge_iq *iq, const struct rss_header *rss,
if (synqe == NULL)
REJECT_PASS_ACCEPT();
- wr = alloc_wrqe(sizeof(*rpl), &sc->sge.ctrlq[pi->port_id]);
+ wr = alloc_wrqe(is_t4(sc) ? sizeof(struct cpl_pass_accept_rpl) :
+ sizeof(struct cpl_t5_pass_accept_rpl), &sc->sge.ctrlq[pi->port_id]);
if (wr == NULL)
REJECT_PASS_ACCEPT();
rpl = wrtod(wr);
@@ -1328,7 +1338,13 @@ do_pass_accept_req(struct sge_iq *iq, const struct rss_header *rss,
save_qids_in_mbuf(m, pi);
get_qids_from_mbuf(m, NULL, &rxqid);
- INIT_TP_WR_MIT_CPL(rpl, CPL_PASS_ACCEPT_RPL, tid);
+ if (is_t4(sc))
+ INIT_TP_WR_MIT_CPL(rpl, CPL_PASS_ACCEPT_RPL, tid);
+ else {
+ struct cpl_t5_pass_accept_rpl *rpl5 = (void *)rpl;
+
+ INIT_TP_WR_MIT_CPL(rpl5, CPL_PASS_ACCEPT_RPL, tid);
+ }
if (sc->tt.ddp && (so->so_options & SO_NO_DDP) == 0) {
ulp_mode = ULP_MODE_TCPDDP;
synqe->flags |= TPF_SYNQE_TCPDDP;
diff --git a/sys/dev/cxgbe/tom/t4_tom.c b/sys/dev/cxgbe/tom/t4_tom.c
index a254253..671174d 100644
--- a/sys/dev/cxgbe/tom/t4_tom.c
+++ b/sys/dev/cxgbe/tom/t4_tom.c
@@ -58,6 +58,8 @@ __FBSDID("$FreeBSD$");
#include "common/common.h"
#include "common/t4_msg.h"
#include "common/t4_regs.h"
+#include "common/t4_regs_values.h"
+#include "common/t4_tcb.h"
#include "tom/t4_tom_l2t.h"
#include "tom/t4_tom.h"
@@ -330,6 +332,30 @@ t4_pcb_detach(struct toedev *tod __unused, struct tcpcb *tp)
}
/*
+ * setsockopt handler.
+ */
+static void
+t4_ctloutput(struct toedev *tod, struct tcpcb *tp, int dir, int name)
+{
+ struct adapter *sc = tod->tod_softc;
+ struct toepcb *toep = tp->t_toe;
+
+ if (dir == SOPT_GET)
+ return;
+
+ CTR4(KTR_CXGBE, "%s: tp %p, dir %u, name %u", __func__, tp, dir, name);
+
+ switch (name) {
+ case TCP_NODELAY:
+ t4_set_tcb_field(sc, toep, 1, W_TCB_T_FLAGS, V_TF_NAGLE(1),
+ V_TF_NAGLE(tp->t_flags & TF_NODELAY ? 0 : 1));
+ break;
+ default:
+ break;
+ }
+}
+
+/*
* The TOE driver will not receive any more CPLs for the tid associated with the
* toepcb; release the hold on the inpcb.
*/
@@ -513,38 +539,38 @@ calc_opt0(struct socket *so, struct port_info *pi, struct l2t_entry *e,
return htobe64(opt0);
}
-#define FILTER_SEL_WIDTH_P_FC (3 + 1)
-#define FILTER_SEL_WIDTH_VIN_P_FC (6 + 7 + FILTER_SEL_WIDTH_P_FC)
-#define FILTER_SEL_WIDTH_TAG_P_FC (3 + FILTER_SEL_WIDTH_VIN_P_FC)
-#define FILTER_SEL_WIDTH_VLD_TAG_P_FC (1 + FILTER_SEL_WIDTH_TAG_P_FC)
-#define VLAN_NONE 0xfff
-#define FILTER_SEL_VLAN_NONE 0xffff
-
uint64_t
-select_ntuple(struct port_info *pi, struct l2t_entry *e, uint32_t filter_mode)
+select_ntuple(struct port_info *pi, struct l2t_entry *e)
{
+ struct adapter *sc = pi->adapter;
+ struct tp_params *tp = &sc->params.tp;
uint16_t viid = pi->viid;
- uint32_t ntuple = 0;
-
- if (filter_mode == HW_TPL_FR_MT_PR_IV_P_FC) {
- if (e->vlan == VLAN_NONE)
- ntuple |= FILTER_SEL_VLAN_NONE << FILTER_SEL_WIDTH_P_FC;
- else {
- ntuple |= e->vlan << FILTER_SEL_WIDTH_P_FC;
- ntuple |= 1 << FILTER_SEL_WIDTH_VLD_TAG_P_FC;
- }
- ntuple |= e->lport << S_PORT;
- ntuple |= IPPROTO_TCP << FILTER_SEL_WIDTH_VLD_TAG_P_FC;
- } else if (filter_mode == HW_TPL_FR_MT_PR_OV_P_FC) {
- ntuple |= G_FW_VIID_VIN(viid) << FILTER_SEL_WIDTH_P_FC;
- ntuple |= G_FW_VIID_PFN(viid) << FILTER_SEL_WIDTH_VIN_P_FC;
- ntuple |= G_FW_VIID_VIVLD(viid) << FILTER_SEL_WIDTH_TAG_P_FC;
- ntuple |= e->lport << S_PORT;
- ntuple |= IPPROTO_TCP << FILTER_SEL_WIDTH_VLD_TAG_P_FC;
- }
-
- if (is_t4(pi->adapter))
- return (htobe32(ntuple));
+ uint64_t ntuple = 0;
+
+ /*
+ * Initialize each of the fields which we care about which are present
+ * in the Compressed Filter Tuple.
+ */
+ if (tp->vlan_shift >= 0 && e->vlan != CPL_L2T_VLAN_NONE)
+ ntuple |= (uint64_t)(F_FT_VLAN_VLD | e->vlan) << tp->vlan_shift;
+
+ if (tp->port_shift >= 0)
+ ntuple |= (uint64_t)e->lport << tp->port_shift;
+
+ if (tp->protocol_shift >= 0)
+ ntuple |= (uint64_t)IPPROTO_TCP << tp->protocol_shift;
+
+ if (tp->vnic_shift >= 0) {
+ uint32_t vf = G_FW_VIID_VIN(viid);
+ uint32_t pf = G_FW_VIID_PFN(viid);
+ uint32_t vld = G_FW_VIID_VIVLD(viid);
+
+ ntuple |= (uint64_t)(V_FT_VNID_ID_VF(vf) | V_FT_VNID_ID_PF(pf) |
+ V_FT_VNID_ID_VLD(vld)) << tp->vnic_shift;
+ }
+
+ if (is_t4(sc))
+ return (htobe32((uint32_t)ntuple));
else
return (htobe64(V_FILTER_TUPLE(ntuple)));
}
@@ -945,6 +971,7 @@ t4_tom_activate(struct adapter *sc)
tod->tod_syncache_removed = t4_syncache_removed;
tod->tod_syncache_respond = t4_syncache_respond;
tod->tod_offload_socket = t4_offload_socket;
+ tod->tod_ctloutput = t4_ctloutput;
for_each_port(sc, i)
TOEDEV(sc->port[i]->ifp) = &td->tod;
diff --git a/sys/dev/cxgbe/tom/t4_tom.h b/sys/dev/cxgbe/tom/t4_tom.h
index 4411a06..b86a76c 100644
--- a/sys/dev/cxgbe/tom/t4_tom.h
+++ b/sys/dev/cxgbe/tom/t4_tom.h
@@ -234,7 +234,7 @@ u_long select_rcv_wnd(struct socket *);
int select_rcv_wscale(void);
uint64_t calc_opt0(struct socket *, struct port_info *, struct l2t_entry *,
int, int, int, int);
-uint64_t select_ntuple(struct port_info *, struct l2t_entry *, uint32_t);
+uint64_t select_ntuple(struct port_info *, struct l2t_entry *);
void set_tcpddp_ulp_mode(struct toepcb *);
int negative_advice(int);
struct clip_entry *hold_lip(struct tom_data *, struct in6_addr *);
diff --git a/sys/dev/iwn/if_iwn.c b/sys/dev/iwn/if_iwn.c
index b6b523a..6e0cc1b 100644
--- a/sys/dev/iwn/if_iwn.c
+++ b/sys/dev/iwn/if_iwn.c
@@ -94,7 +94,7 @@ static const struct iwn_ident iwn_ident_table[] = {
{ 0x8086, 0x0885, "Intel Centrino Wireless-N + WiMAX 6150" },
{ 0x8086, 0x0886, "Intel Centrino Wireless-N + WiMAX 6150" },
{ 0x8086, 0x0896, "Intel Centrino Wireless-N 130" },
- { 0x8086, 0x0887, "Intel Centrino Wireless-N 130" },
+ { 0x8086, 0x0897, "Intel Centrino Wireless-N 130" },
{ 0x8086, 0x08ae, "Intel Centrino Wireless-N 100" },
{ 0x8086, 0x08af, "Intel Centrino Wireless-N 100" },
{ 0x8086, 0x4229, "Intel Wireless WiFi Link 4965" },
@@ -335,6 +335,8 @@ enum {
IWN_DEBUG_NODE = 0x00000400, /* node management */
IWN_DEBUG_LED = 0x00000800, /* led management */
IWN_DEBUG_CMD = 0x00001000, /* cmd submission */
+ IWN_DEBUG_TXRATE = 0x00002000, /* TX rate debugging */
+ IWN_DEBUG_PWRSAVE = 0x00004000, /* Power save operations */
IWN_DEBUG_FATAL = 0x80000000, /* fatal errors */
IWN_DEBUG_ANY = 0xffffffff
};
@@ -585,6 +587,7 @@ iwn_attach(device_t dev)
| IEEE80211_C_IBSS /* ibss/adhoc mode */
#endif
| IEEE80211_C_WME /* WME */
+ | IEEE80211_C_PMGT /* Station-side power mgmt */
;
/* Read MAC address, channels, etc from EEPROM. */
@@ -2097,56 +2100,106 @@ rate2plcp(int rate)
return 0;
}
-static void
-iwn_newassoc(struct ieee80211_node *ni, int isnew)
+/*
+ * Calculate the required PLCP value from the given rate,
+ * to the given node.
+ *
+ * This will take the node configuration (eg 11n, rate table
+ * setup, etc) into consideration.
+ */
+static uint32_t
+iwn_rate_to_plcp(struct iwn_softc *sc, struct ieee80211_node *ni,
+ uint8_t rate)
{
#define RV(v) ((v) & IEEE80211_RATE_VAL)
struct ieee80211com *ic = ni->ni_ic;
- struct iwn_softc *sc = ic->ic_ifp->if_softc;
- struct iwn_node *wn = (void *)ni;
uint8_t txant1, txant2;
- int i, plcp, rate, ridx;
+ uint32_t plcp = 0;
+ int ridx;
/* Use the first valid TX antenna. */
txant1 = IWN_LSB(sc->txchainmask);
txant2 = IWN_LSB(sc->txchainmask & ~txant1);
+ /*
+ * If it's an MCS rate, let's set the plcp correctly
+ * and set the relevant flags based on the node config.
+ */
if (IEEE80211_IS_CHAN_HT(ni->ni_chan)) {
- ridx = ni->ni_rates.rs_nrates - 1;
- for (i = ni->ni_htrates.rs_nrates - 1; i >= 0; i--) {
- plcp = RV(ni->ni_htrates.rs_rates[i]) | IWN_RFLAG_MCS;
- if (IEEE80211_IS_CHAN_HT40(ni->ni_chan)) {
- plcp |= IWN_RFLAG_HT40;
- if (ni->ni_htcap & IEEE80211_HTCAP_SHORTGI40)
- plcp |= IWN_RFLAG_SGI;
- } else if (ni->ni_htcap & IEEE80211_HTCAP_SHORTGI20)
+ /*
+ * Set the initial PLCP value to be between 0->31 for
+ * MCS 0 -> MCS 31, then set the "I'm an MCS rate!"
+ * flag.
+ */
+ plcp = RV(rate) | IWN_RFLAG_MCS;
+
+ /*
+ * XXX the following should only occur if both
+ * the local configuration _and_ the remote node
+ * advertise these capabilities. Thus this code
+ * may need fixing!
+ */
+
+ /*
+ * Set the channel width and guard interval.
+ */
+ if (IEEE80211_IS_CHAN_HT40(ni->ni_chan)) {
+ plcp |= IWN_RFLAG_HT40;
+ if (ni->ni_htcap & IEEE80211_HTCAP_SHORTGI40)
plcp |= IWN_RFLAG_SGI;
- if (RV(ni->ni_htrates.rs_rates[i]) > 7)
- plcp |= IWN_RFLAG_ANT(txant1 | txant2);
- else
- plcp |= IWN_RFLAG_ANT(txant1);
- if (ridx >= 0) {
- rate = RV(ni->ni_rates.rs_rates[ridx]);
- wn->ridx[rate] = plcp;
- }
- wn->ridx[IEEE80211_RATE_MCS | i] = plcp;
- ridx--;
+ } else if (ni->ni_htcap & IEEE80211_HTCAP_SHORTGI20) {
+ plcp |= IWN_RFLAG_SGI;
}
- } else {
- for (i = 0; i < ni->ni_rates.rs_nrates; i++) {
- rate = RV(ni->ni_rates.rs_rates[i]);
- plcp = rate2plcp(rate);
- ridx = ic->ic_rt->rateCodeToIndex[rate];
- if (ridx < IWN_RIDX_OFDM6 &&
- IEEE80211_IS_CHAN_2GHZ(ni->ni_chan))
- plcp |= IWN_RFLAG_CCK;
+
+ /*
+ * If it's a two stream rate, enable TX on both
+ * antennas.
+ *
+ * XXX three stream rates?
+ */
+ if (rate > 0x87)
+ plcp |= IWN_RFLAG_ANT(txant1 | txant2);
+ else
plcp |= IWN_RFLAG_ANT(txant1);
- wn->ridx[rate] = htole32(plcp);
- }
+ } else {
+ /*
+ * Set the initial PLCP - fine for both
+ * OFDM and CCK rates.
+ */
+ plcp = rate2plcp(rate);
+
+ /* Set CCK flag if it's CCK */
+
+ /* XXX It would be nice to have a method
+ * to map the ridx -> phy table entry
+ * so we could just query that, rather than
+ * this hack to check against IWN_RIDX_OFDM6.
+ */
+ ridx = ieee80211_legacy_rate_lookup(ic->ic_rt,
+ rate & IEEE80211_RATE_VAL);
+ if (ridx < IWN_RIDX_OFDM6 &&
+ IEEE80211_IS_CHAN_2GHZ(ni->ni_chan))
+ plcp |= IWN_RFLAG_CCK;
+
+ /* Set antenna configuration */
+ plcp |= IWN_RFLAG_ANT(txant1);
}
+
+ DPRINTF(sc, IWN_DEBUG_TXRATE, "%s: rate=0x%02x, plcp=0x%08x\n",
+ __func__,
+ rate,
+ plcp);
+
+ return (htole32(plcp));
#undef RV
}
+static void
+iwn_newassoc(struct ieee80211_node *ni, int isnew)
+{
+ /* Doesn't do anything at the moment */
+}
+
static int
iwn_media_change(struct ifnet *ifp)
{
@@ -3400,7 +3453,8 @@ iwn_tx_data(struct iwn_softc *sc, struct mbuf *m, struct ieee80211_node *ni)
(void) ieee80211_ratectl_rate(ni, NULL, 0);
rate = ni->ni_txrate;
}
- ridx = ic->ic_rt->rateCodeToIndex[rate];
+ ridx = ieee80211_legacy_rate_lookup(ic->ic_rt,
+ rate & IEEE80211_RATE_VAL);
/* Encrypt the frame if need be. */
if (wh->i_fc[1] & IEEE80211_FC1_WEP) {
@@ -3506,7 +3560,7 @@ iwn_tx_data(struct iwn_softc *sc, struct mbuf *m, struct ieee80211_node *ni)
tx->rts_ntries = 60;
tx->data_ntries = 15;
tx->lifetime = htole32(IWN_LIFETIME_INFINITE);
- tx->rate = wn->ridx[rate];
+ tx->rate = iwn_rate_to_plcp(sc, ni, rate);
if (tx->id == sc->broadcast_id) {
/* Group or management frame. */
tx->linkq = 0;
@@ -3637,7 +3691,8 @@ iwn_tx_data_raw(struct iwn_softc *sc, struct mbuf *m,
/* Choose a TX rate index. */
rate = params->ibp_rate0;
- ridx = ic->ic_rt->rateCodeToIndex[rate];
+ ridx = ieee80211_legacy_rate_lookup(ic->ic_rt,
+ rate & IEEE80211_RATE_VAL);
if (ridx == (uint8_t)-1) {
/* XXX fall back to mcast/mgmt rate? */
m_freem(m);
@@ -3713,14 +3768,18 @@ iwn_tx_data_raw(struct iwn_softc *sc, struct mbuf *m,
tx->rts_ntries = params->ibp_try1;
tx->data_ntries = params->ibp_try0;
tx->lifetime = htole32(IWN_LIFETIME_INFINITE);
+
+ /* XXX should just use iwn_rate_to_plcp() */
tx->rate = htole32(rate2plcp(rate));
if (ridx < IWN_RIDX_OFDM6 &&
IEEE80211_IS_CHAN_2GHZ(ni->ni_chan))
tx->rate |= htole32(IWN_RFLAG_CCK);
+
/* Group or management frame. */
tx->linkq = 0;
txant = IWN_LSB(sc->txchainmask);
tx->rate |= htole32(IWN_RFLAG_ANT(txant));
+
/* Set physical address of "scratch area". */
tx->loaddr = htole32(IWN_LOADDR(data->scratch_paddr));
tx->hiaddr = IWN_HIADDR(data->scratch_paddr);
@@ -4076,14 +4135,20 @@ iwn_set_link_quality(struct iwn_softc *sc, struct ieee80211_node *ni)
else
txrate = rs->rs_nrates - 1;
for (i = 0; i < IWN_MAX_TX_RETRIES; i++) {
+ uint32_t plcp;
+
if (IEEE80211_IS_CHAN_HT(ni->ni_chan))
rate = IEEE80211_RATE_MCS | txrate;
else
rate = RV(rs->rs_rates[txrate]);
- linkq.retry[i] = wn->ridx[rate];
- if ((le32toh(wn->ridx[rate]) & IWN_RFLAG_MCS) &&
- RV(le32toh(wn->ridx[rate])) > 7)
+ /* Do rate -> PLCP config mapping */
+ plcp = iwn_rate_to_plcp(sc, ni, rate);
+ linkq.retry[i] = plcp;
+
+ /* Special case for dual-stream rates? */
+ if ((le32toh(plcp) & IWN_RFLAG_MCS) &&
+ RV(le32toh(plcp)) > 7)
linkq.mimo = i + 1;
/* Next retry at immediate lower bit-rate. */
@@ -4585,6 +4650,8 @@ iwn_collect_noise(struct iwn_softc *sc,
{
struct iwn_ops *ops = &sc->ops;
struct iwn_calib_state *calib = &sc->calib;
+ struct ifnet *ifp = sc->sc_ifp;
+ struct ieee80211com *ic = ifp->if_l2com;
uint32_t val;
int i;
@@ -4623,12 +4690,9 @@ iwn_collect_noise(struct iwn_softc *sc,
(void)iwn_cmd(sc, IWN_CMD_RXON, &sc->rxon, sc->rxonsz, 1);
#endif
-#if 0
- /* XXX: not yet */
/* Enable power-saving mode if requested by user. */
- if (sc->sc_ic.ic_flags & IEEE80211_F_PMGTON)
+ if (ic->ic_flags & IEEE80211_F_PMGTON)
(void)iwn_set_pslevel(sc, 0, 3, 1);
-#endif
}
static int
@@ -4940,6 +5004,13 @@ iwn_set_pslevel(struct iwn_softc *sc, int dtim, int level, int async)
uint32_t reg;
int i;
+ DPRINTF(sc, IWN_DEBUG_PWRSAVE,
+ "%s: dtim=%d, level=%d, async=%d\n",
+ __func__,
+ dtim,
+ level,
+ async);
+
/* Select which PS parameters to use. */
if (dtim <= 2)
pmgt = &iwn_pmgt[0][level];
diff --git a/sys/dev/iwn/if_iwnvar.h b/sys/dev/iwn/if_iwnvar.h
index 7326260..0bcc19b 100644
--- a/sys/dev/iwn/if_iwnvar.h
+++ b/sys/dev/iwn/if_iwnvar.h
@@ -102,7 +102,6 @@ struct iwn_node {
struct ieee80211_node ni; /* must be the first */
uint16_t disable_tid;
uint8_t id;
- uint32_t ridx[256];
struct {
uint64_t bitmap;
int startidx;
diff --git a/sys/dev/mfi/mfi.c b/sys/dev/mfi/mfi.c
index cf2d505..b20ddc5 100644
--- a/sys/dev/mfi/mfi.c
+++ b/sys/dev/mfi/mfi.c
@@ -3773,12 +3773,15 @@ mfi_timeout(void *data)
MFI_PRINT_CMD(cm);
MFI_VALIDATE_CMD(sc, cm);
/*
- * Fail the command instead of leaving it on
- * the queue where it could remain stuck forever
+ * While commands can get stuck forever we do
+ * not fail them as there is no way to tell if
+ * the controller has actually processed them
+ * or not.
+ *
+ * In addition its very likely that force
+ * failing a command here would cause a panic
+ * e.g. in UFS.
*/
- mfi_remove_busy(cm);
- cm->cm_error = ETIMEDOUT;
- mfi_complete(sc, cm);
timedout++;
}
}
diff --git a/sys/dev/pci/pci.c b/sys/dev/pci/pci.c
index bea162b..091d02c 100644
--- a/sys/dev/pci/pci.c
+++ b/sys/dev/pci/pci.c
@@ -152,6 +152,7 @@ static device_method_t pci_methods[] = {
DEVMETHOD(bus_release_resource, bus_generic_rl_release_resource),
DEVMETHOD(bus_activate_resource, pci_activate_resource),
DEVMETHOD(bus_deactivate_resource, pci_deactivate_resource),
+ DEVMETHOD(bus_child_detached, pci_child_detached),
DEVMETHOD(bus_child_pnpinfo_str, pci_child_pnpinfo_str_method),
DEVMETHOD(bus_child_location_str, pci_child_location_str_method),
DEVMETHOD(bus_remap_intr, pci_remap_intr_method),
@@ -3489,7 +3490,7 @@ pci_driver_added(device_t dev, driver_t *driver)
pci_printf(&dinfo->cfg, "reprobing on driver added\n");
pci_cfg_restore(child, dinfo);
if (device_probe_and_attach(child) != 0)
- pci_cfg_save(child, dinfo, 1);
+ pci_child_detached(dev, child);
}
free(devlist, M_TEMP);
}
@@ -3804,6 +3805,34 @@ pci_probe_nomatch(device_t dev, device_t child)
pci_cfg_save(child, device_get_ivars(child), 1);
}
+void
+pci_child_detached(device_t dev, device_t child)
+{
+ struct pci_devinfo *dinfo;
+ struct resource_list *rl;
+
+ dinfo = device_get_ivars(child);
+ rl = &dinfo->resources;
+
+ /*
+ * Have to deallocate IRQs before releasing any MSI messages and
+ * have to release MSI messages before deallocating any memory
+ * BARs.
+ */
+ if (resource_list_release_active(rl, dev, child, SYS_RES_IRQ) != 0)
+ pci_printf(&dinfo->cfg, "Device leaked IRQ resources\n");
+ if (dinfo->cfg.msi.msi_alloc != 0 || dinfo->cfg.msix.msix_alloc != 0) {
+ pci_printf(&dinfo->cfg, "Device leaked MSI vectors\n");
+ (void)pci_release_msi(child);
+ }
+ if (resource_list_release_active(rl, dev, child, SYS_RES_MEMORY) != 0)
+ pci_printf(&dinfo->cfg, "Device leaked memory resources\n");
+ if (resource_list_release_active(rl, dev, child, SYS_RES_IOPORT) != 0)
+ pci_printf(&dinfo->cfg, "Device leaked I/O resources\n");
+
+ pci_cfg_save(child, dinfo, 1);
+}
+
/*
* Parse the PCI device database, if loaded, and return a pointer to a
* description of the device.
diff --git a/sys/dev/pci/pci_private.h b/sys/dev/pci/pci_private.h
index b4c0c9e..9eb0df0 100644
--- a/sys/dev/pci/pci_private.h
+++ b/sys/dev/pci/pci_private.h
@@ -106,6 +106,7 @@ struct pci_devinfo *pci_read_device(device_t pcib, int d, int b, int s, int f,
size_t size);
void pci_print_verbose(struct pci_devinfo *dinfo);
int pci_freecfg(struct pci_devinfo *dinfo);
+void pci_child_detached(device_t dev, device_t child);
int pci_child_location_str_method(device_t cbdev, device_t child,
char *buf, size_t buflen);
int pci_child_pnpinfo_str_method(device_t cbdev, device_t child,
diff --git a/sys/dev/qlxgbe/ql_os.c b/sys/dev/qlxgbe/ql_os.c
index 757d612..c6fcae3 100644
--- a/sys/dev/qlxgbe/ql_os.c
+++ b/sys/dev/qlxgbe/ql_os.c
@@ -1646,11 +1646,13 @@ qla_error_recovery(void *context, int pending)
if ((ha->pci_func & 0x1) == 0) {
- if (!ha->msg_from_peer)
+ if (!ha->msg_from_peer) {
qla_send_msg_to_peer(ha, QL_PEER_MSG_RESET);
- while ((ha->msg_from_peer != QL_PEER_MSG_ACK) && msecs_100--)
- qla_mdelay(__func__, 100);
+ while ((ha->msg_from_peer != QL_PEER_MSG_ACK) &&
+ msecs_100--)
+ qla_mdelay(__func__, 100);
+ }
ha->msg_from_peer = 0;
diff --git a/sys/dev/ral/rt2560.c b/sys/dev/ral/rt2560.c
index c67f1f7..7c1b9de 100644
--- a/sys/dev/ral/rt2560.c
+++ b/sys/dev/ral/rt2560.c
@@ -2370,7 +2370,7 @@ rt2560_set_basicrates(struct rt2560_softc *sc,
if (!(rate & IEEE80211_RATE_BASIC))
continue;
- mask |= 1 << ic->ic_rt->rateCodeToIndex[RV(rate)];
+ mask |= 1 << ieee80211_legacy_rate_lookup(ic->ic_rt, RV(rate));
}
RAL_WRITE(sc, RT2560_ARSP_PLCP_1, mask);
diff --git a/sys/dev/ral/rt2661.c b/sys/dev/ral/rt2661.c
index 8ae2515..70c30f1 100644
--- a/sys/dev/ral/rt2661.c
+++ b/sys/dev/ral/rt2661.c
@@ -1923,7 +1923,7 @@ rt2661_set_basicrates(struct rt2661_softc *sc,
if (!(rate & IEEE80211_RATE_BASIC))
continue;
- mask |= 1 << ic->ic_rt->rateCodeToIndex[RV(rate)];
+ mask |= 1 << ieee80211_legacy_rate_lookup(ic->ic_rt, RV(rate));
}
RAL_WRITE(sc, RT2661_TXRX_CSR5, mask);
diff --git a/sys/dev/ral/rt2860.c b/sys/dev/ral/rt2860.c
index 4207d75..3bdd01d 100644
--- a/sys/dev/ral/rt2860.c
+++ b/sys/dev/ral/rt2860.c
@@ -1528,7 +1528,7 @@ rt2860_tx(struct rt2860_softc *sc, struct mbuf *m, struct ieee80211_node *ni)
tid = 0;
}
ring = &sc->txq[qid];
- ridx = ic->ic_rt->rateCodeToIndex[rate];
+ ridx = ieee80211_legacy_rate_lookup(ic->ic_rt, rate);
/* get MCS code from rate index */
mcs = rt2860_rates[ridx].mcs;
@@ -1779,7 +1779,8 @@ rt2860_tx_raw(struct rt2860_softc *sc, struct mbuf *m,
/* Choose a TX rate index. */
rate = params->ibp_rate0;
- ridx = ic->ic_rt->rateCodeToIndex[rate];
+ ridx = ieee80211_legacy_rate_lookup(ic->ic_rt,
+ rate & IEEE80211_RATE_VAL);
if (ridx == (uint8_t)-1) {
/* XXX fall back to mcast/mgmt rate? */
m_freem(m);
@@ -2311,7 +2312,7 @@ rt2860_set_basicrates(struct rt2860_softc *sc,
if (!(rate & IEEE80211_RATE_BASIC))
continue;
- mask |= 1 << ic->ic_rt->rateCodeToIndex[RV(rate)];
+ mask |= 1 << ieee80211_legacy_rate_lookup(ic->ic_rt, RV(rate));
}
RAL_WRITE(sc, RT2860_LEGACY_BASIC_RATE, mask);
diff --git a/sys/dev/re/if_re.c b/sys/dev/re/if_re.c
index 44b4d28..1e906a7 100644
--- a/sys/dev/re/if_re.c
+++ b/sys/dev/re/if_re.c
@@ -1321,7 +1321,7 @@ re_attach(device_t dev)
SYS_RES_IRQ, &rid, RF_ACTIVE);
if (sc->rl_irq[i] == NULL) {
device_printf(dev,
- "couldn't llocate IRQ resources for "
+ "couldn't allocate IRQ resources for "
"message %d\n", rid);
error = ENXIO;
goto fail;
@@ -1632,7 +1632,7 @@ re_attach(device_t dev)
/*
* Don't enable TSO by default. It is known to generate
* corrupted TCP segments(bad TCP options) under certain
- * circumtances.
+ * circumstances.
*/
ifp->if_hwassist &= ~CSUM_TSO;
ifp->if_capenable &= ~(IFCAP_TSO4 | IFCAP_VLAN_HWTSO);
@@ -2784,7 +2784,7 @@ re_encap(struct rl_softc *sc, struct mbuf **m_head)
/*
* Unconditionally enable IP checksum if TCP or UDP
* checksum is required. Otherwise, TCP/UDP checksum
- * does't make effects.
+ * doesn't make effects.
*/
if (((*m_head)->m_pkthdr.csum_flags & RE_CSUM_FEATURES) != 0) {
if ((sc->rl_flags & RL_FLAG_DESCV2) == 0) {
@@ -3247,7 +3247,7 @@ re_init_locked(struct rl_softc *sc)
if ((sc->rl_flags & RL_FLAG_JUMBOV2) != 0) {
/*
* For controllers that use new jumbo frame scheme,
- * set maximum size of jumbo frame depedning on
+ * set maximum size of jumbo frame depending on
* controller revisions.
*/
if (ifp->if_mtu > RL_MTU)
@@ -3948,7 +3948,7 @@ re_sysctl_stats(SYSCTL_HANDLER_ARGS)
RL_UNLOCK(sc);
if (i == 0) {
device_printf(sc->rl_dev,
- "DUMP statistics request timedout\n");
+ "DUMP statistics request timed out\n");
return (ETIMEDOUT);
}
done:
diff --git a/sys/dev/uart/uart.h b/sys/dev/uart/uart.h
index 772793f..fb0f699 100644
--- a/sys/dev/uart/uart.h
+++ b/sys/dev/uart/uart.h
@@ -67,6 +67,7 @@ struct uart_class;
extern struct uart_class uart_imx_class __attribute__((weak));
extern struct uart_class uart_ns8250_class __attribute__((weak));
extern struct uart_class uart_quicc_class __attribute__((weak));
+extern struct uart_class uart_s3c2410_class __attribute__((weak));
extern struct uart_class uart_sab82532_class __attribute__((weak));
extern struct uart_class uart_sbbc_class __attribute__((weak));
extern struct uart_class uart_z8530_class __attribute__((weak));
diff --git a/sys/dev/uart/uart_bus_fdt.c b/sys/dev/uart/uart_bus_fdt.c
index d7b6017..4f6ac9b 100644
--- a/sys/dev/uart/uart_bus_fdt.c
+++ b/sys/dev/uart/uart_bus_fdt.c
@@ -109,6 +109,8 @@ uart_fdt_probe(device_t dev)
sc->sc_class = &uart_imx_class;
else if (ofw_bus_is_compatible(dev, "arm,pl011"))
sc->sc_class = &uart_pl011_class;
+ else if (ofw_bus_is_compatible(dev, "exynos"))
+ sc->sc_class = &uart_s3c2410_class;
else if (ofw_bus_is_compatible(dev, "cadence,uart"))
sc->sc_class = &uart_cdnc_class;
else
diff --git a/sys/dev/uart/uart_cpu_fdt.c b/sys/dev/uart/uart_cpu_fdt.c
index 52d5f00..0052388 100644
--- a/sys/dev/uart/uart_cpu_fdt.c
+++ b/sys/dev/uart/uart_cpu_fdt.c
@@ -149,6 +149,8 @@ uart_cpu_getdev(int devtype, struct uart_devinfo *di)
class = &uart_ns8250_class;
if (fdt_is_compatible(node, "arm,pl011"))
class = &uart_pl011_class;
+ if (fdt_is_compatible(node, "exynos"))
+ class = &uart_s3c2410_class;
if (fdt_is_compatible(node, "cadence,uart"))
class = &uart_cdnc_class;
diff --git a/sys/dev/uart/uart_subr.c b/sys/dev/uart/uart_subr.c
index acbf3b4..0277b21 100644
--- a/sys/dev/uart/uart_subr.c
+++ b/sys/dev/uart/uart_subr.c
@@ -54,6 +54,7 @@ static struct uart_class *uart_classes[] = {
&uart_z8530_class,
#if defined(__arm__)
&uart_lpc_class,
+ &uart_s3c2410_class,
#endif
};
static size_t uart_nclasses = sizeof(uart_classes) / sizeof(uart_classes[0]);
diff --git a/sys/dev/usb/quirk/usb_quirk.c b/sys/dev/usb/quirk/usb_quirk.c
index 98e7c42..e46f269 100644
--- a/sys/dev/usb/quirk/usb_quirk.c
+++ b/sys/dev/usb/quirk/usb_quirk.c
@@ -94,6 +94,7 @@ static struct usb_quirk_entry usb_quirks[USB_DEV_QUIRKS_MAX] = {
USB_QUIRK(SILICONPORTALS, YAPPHONE, 0x100, 0x100, UQ_AU_INP_ASYNC),
USB_QUIRK(LOGITECH, UN53B, 0x0000, 0xffff, UQ_NO_STRINGS),
USB_QUIRK(ELSA, MODEM1, 0x0000, 0xffff, UQ_CFG_INDEX_1),
+ USB_QUIRK(PLANEX2, MZKUE150N, 0x0000, 0xffff, UQ_CFG_INDEX_1),
/* Quirks for printer devices */
USB_QUIRK(HP, 895C, 0x0000, 0xffff, UQ_BROKEN_BIDIR),
USB_QUIRK(HP, 880C, 0x0000, 0xffff, UQ_BROKEN_BIDIR),
diff --git a/sys/dev/usb/usbdevs b/sys/dev/usb/usbdevs
index 21a8901..8dee4f0 100644
--- a/sys/dev/usb/usbdevs
+++ b/sys/dev/usb/usbdevs
@@ -849,6 +849,7 @@ product ACCTON RT3070_2 0xa702 RT3070
product ACCTON RT2870_1 0xb522 RT2870
product ACCTON RT3070_3 0xc522 RT3070
product ACCTON RT3070_5 0xd522 RT3070
+product ACCTON RTL8192SU 0xc512 RTL8192SU
product ACCTON ZD1211B 0xe501 ZD1211B
/* Aceeca products */
@@ -1134,7 +1135,9 @@ product ASUS RT2870_4 0x1760 RT2870
product ASUS RT2870_5 0x1761 RT2870
product ASUS USBN13 0x1784 USB-N13
product ASUS RT3070_1 0x1790 RT3070
+product ASUS USBN10 0x1786 USB-N10
product ASUS RTL8192CU 0x17ab RTL8192CU
+product ASUS RTL8192SU 0x1791 RTL8192SU
product ASUS A730W 0x4202 ASUS MyPal A730W
product ASUS P535 0x420f ASUS P535 PDA
product ASUS GMSC 0x422f ASUS Generic Mass Storage
@@ -1185,7 +1188,11 @@ product AZUREWAVE RT3070_3 0x3305 RT3070
product AZUREWAVE RTL8188CU 0x3357 RTL8188CU
product AZUREWAVE RTL8188CE_1 0x3358 RTL8188CE
product AZUREWAVE RTL8188CE_2 0x3359 RTL8188CE
-
+product AZUREWAVE RTL8192SU_1 0x3306 RTL8192SU
+product AZUREWAVE RTL8192SU_2 0x3309 RTL8192SU
+product AZUREWAVE RTL8192SU_3 0x3310 RTL8192SU
+product AZUREWAVE RTL8192SU_4 0x3311 RTL8192SU
+product AZUREWAVE RTL8192SU_5 0x3325 RTL8192SU
/* Baltech products */
product BALTECH CARDREADER 0x9999 Card reader
@@ -1242,6 +1249,9 @@ product BELKIN F5D7050E 0x705e F5D7050E Wireless Adapter
product BELKIN RT2870_1 0x8053 RT2870
product BELKIN RT2870_2 0x805c RT2870
product BELKIN F5D8053V3 0x815c F5D8053 v3
+product BELKIN RTL8192SU_1 0x815f RTL8192SU
+product BELKIN RTL8192SU_2 0x845a RTL8192SU
+product BELKIN RTL8192SU_3 0x945a RTL8192SU
product BELKIN F5D8055 0x825a F5D8055
product BELKIN F5D8055V2 0x825b F5D8055 v2
product BELKIN F5D9050V3 0x905b F5D9050 ver 3 Wireless Adapter
@@ -1368,6 +1378,9 @@ product CONCEPTRONIC AR5523_1 0x7801 AR5523
product CONCEPTRONIC AR5523_1_NF 0x7802 AR5523 (no firmware)
product CONCEPTRONIC AR5523_2 0x7811 AR5523
product CONCEPTRONIC AR5523_2_NF 0x7812 AR5523 (no firmware)
+product CONCEPTRONIC2 RTL8192SU_1 0x3300 RTL8192SU
+product CONCEPTRONIC2 RTL8192SU_2 0x3301 RTL8192SU
+product CONCEPTRONIC2 RTL8192SU_3 0x3302 RTL8192SU
product CONCEPTRONIC2 C54RU 0x3c02 C54RU WLAN
product CONCEPTRONIC2 C54RU2 0x3c22 C54RU
product CONCEPTRONIC2 RT3070_1 0x3c08 RT3070
@@ -1404,6 +1417,7 @@ product COREGA RT2870_2 0x003c RT2870
product COREGA RT2870_3 0x003f RT2870
product COREGA RT3070 0x0041 RT3070
product COREGA CGWLUSB300GNM 0x0042 CG-WLUSB300GNM
+product COREGA RTL8192SU 0x0047 RTL8192SU
product COREGA RTL8192CU 0x0056 RTL8192CU
product COREGA WLUSB_11_STICK 0x7613 WLAN USB Stick 11
@@ -1546,6 +1560,9 @@ product DLINK RTL8192CU_1 0x3307 RTL8192CU
product DLINK RTL8192CU_2 0x3309 RTL8192CU
product DLINK RTL8192CU_3 0x330a RTL8192CU
product DLINK DWA131B 0x330d DWA-131 rev B
+product DLINK2 RTL8192SU_1 0x3300 RTL8192SU
+product DLINK2 RTL8192SU_2 0x3302 RTL8192SU
+product DLINK2 DWA131A1 0x3303 DWA-131 A1
product DLINK2 DWA120 0x3a0c DWA-120
product DLINK2 DWA120_NF 0x3a0d DWA-120 (no firmware)
product DLINK2 DWLG122C1 0x3c03 DWL-G122 c1
@@ -1582,6 +1599,9 @@ product DYNASTREAM ANTDEVBOARD2 0x1006 ANT dev board
/* Edimax products */
product EDIMAX EW7318USG 0x7318 USB Wireless dongle
+product EDIMAX RTL8192SU_1 0x7611 RTL8192SU
+product EDIMAX RTL8192SU_2 0x7612 RTL8192SU
+product EDIMAX RTL8192SU_3 0x7622 RTL8192SU
product EDIMAX RT2870_1 0x7711 RT2870
product EDIMAX EW7717 0x7717 EW-7717
product EDIMAX EW7718 0x7718 EW-7718
@@ -2075,6 +2095,8 @@ product GUILLEMOT HWGUSB254 0xe000 HWGUSB2-54 WLAN
product GUILLEMOT HWGUSB254LB 0xe010 HWGUSB2-54-LB
product GUILLEMOT HWGUSB254V2AP 0xe020 HWGUSB2-54V2-AP
product GUILLEMOT HWNU300 0xe030 HWNU-300
+product GUILLEMOT HWNUM300 0xe031 HWNUm-300
+product GUILLEMOT HWGUN54 0xe032 HWGUn-54
product GUILLEMOT HWNUP150 0xe033 HWNUP-150
/* Hagiwara products */
@@ -2101,6 +2123,8 @@ product HAWKING HWUN2 0x0009 HWUN2
product HAWKING RT3070 0x000b RT3070
product HAWKING RTL8192CU 0x0019 RTL8192CU
product HAWKING UF100 0x400c 10/100 USB Ethernet
+product HAWKING RTL8192SU_1 0x0015 RTL8192SU
+product HAWKING RTL8192SU_2 0x0016 RTL8192SU
/* HID Global GmbH products */
product HIDGLOBAL CM2020 0x0596 Omnikey Cardman 2020
@@ -3327,6 +3351,7 @@ product PLANEX2 RTL8192CU 0xab2b RTL8192CU
product PLANEX2 GWUS54HP 0xab01 GW-US54HP
product PLANEX2 GWUS300MINIS 0xab24 GW-US300MiniS
product PLANEX2 RT3070 0xab25 RT3070
+product PLANEX2 MZKUE150N 0xab2f MZK-UE150N
product PLANEX2 GWUS54MINI2 0xab50 GW-US54Mini2
product PLANEX2 GWUS54SG 0xc002 GW-US54SG
product PLANEX2 GWUS54GZL 0xc007 GW-US54GZL
@@ -3338,6 +3363,7 @@ product PLANEX2 GWUSVALUEEZ 0xed17 GW-USValue-EZ
product PLANEX3 GWUS54GZ 0xab10 GW-US54GZ
product PLANEX3 GU1000T 0xab11 GU-1000T
product PLANEX3 GWUS54MINI 0xab13 GW-US54Mini
+product PLANEX2 GWUSNANO 0xab28 GW-USNano
/* Plextor Corp. */
product PLEXTOR 40_12_40U 0x0011 PlexWriter 40/12/40U
@@ -3555,6 +3581,10 @@ product REALTEK USB20CRW 0x0158 USB20CRW Card Reader
product REALTEK RTL8188CTV 0x018a RTL8188CTV
product REALTEK USBKR100 0x8150 USBKR100 USB Ethernet
product REALTEK RTL8188CE_0 0x8170 RTL8188CE
+product REALTEK RTL8171 0x8171 RTL8171
+product REALTEK RTL8172 0x8172 RTL8172
+product REALTEK RTL8173 0x8173 RTL8173
+product REALTEK RTL8174 0x8174 RTL8174
product REALTEK RTL8188CE_1 0x817e RTL8188CE
product REALTEK RTL8188CU_0 0x8176 RTL8188CU
product REALTEK RTL8188CU_1 0x817a RTL8188CU
@@ -3569,7 +3599,10 @@ product REALTEK RTL8191CU 0x8177 RTL8191CU
product REALTEK RTL8192CU 0x8178 RTL8192CU
product REALTEK RTL8192CE 0x817c RTL8192CE
product REALTEK RTL8188RU_1 0x817d RTL8188RU
+product REALTEK RTL8712 0x8712 RTL8712
+product REALTEK RTL8713 0x8712 RTL8713
product REALTEK RTL8188RU_2 0x317f RTL8188RU
+product REALTEK RTL8192SU 0xc512 RTL8192SU
/* RedOctane products */
product REDOCTANE DUMMY 0x0000 Dummy product
@@ -3720,6 +3753,8 @@ product SENAO RT3072_2 0x9707 RT3072
product SENAO RT3072_3 0x9708 RT3072
product SENAO RT3072_4 0x9709 RT3072
product SENAO RT3072_5 0x9801 RT3072
+product SENAO RTL8192SU_1 0x9603 RTL8192SU
+product SENAO RTL8192SU_2 0x9605 RTL8192SU
/* ShanTou products */
product SHANTOU ST268 0x0268 ST268
@@ -3940,9 +3975,11 @@ product SITECOMEU WL608 0x003f WL-608
product SITECOMEU RT3071 0x0040 RT3071
product SITECOMEU RT3072_1 0x0041 RT3072
product SITECOMEU RT3072_2 0x0042 RT3072
+product SITECOMEU WL353 0x0045 WL-353
product SITECOMEU RT3072_3 0x0047 RT3072
product SITECOMEU RT3072_4 0x0048 RT3072
product SITECOMEU RT3072_5 0x004a RT3072
+product SITECOMEU WL349V1 0x004b WL-349 v1
product SITECOMEU RT3072_6 0x004d RT3072
product SITECOMEU RTL8188CU_1 0x0052 RTL8188CU
product SITECOMEU RTL8188CU_2 0x005c RTL8188CU
@@ -4110,6 +4147,7 @@ product SURECOM RT2573 0x31f3 RT2573
/* Sweex products */
product SWEEX ZD1211 0x1809 ZD1211
product SWEEX2 LW153 0x0153 LW153
+product SWEEX2 LW154 0x0154 LW154
product SWEEX2 LW303 0x0302 LW303
product SWEEX2 LW313 0x0313 LW313
diff --git a/sys/dev/usb/wlan/if_rum.c b/sys/dev/usb/wlan/if_rum.c
index fa4e9f1..1d17ceb 100644
--- a/sys/dev/usb/wlan/if_rum.c
+++ b/sys/dev/usb/wlan/if_rum.c
@@ -502,6 +502,12 @@ rum_attach(device_t self)
| IEEE80211_C_WPA /* 802.11i */
;
+ ic->ic_cryptocaps =
+ IEEE80211_CRYPTO_WEP |
+ IEEE80211_CRYPTO_AES_CCM |
+ IEEE80211_CRYPTO_TKIPMIC |
+ IEEE80211_CRYPTO_TKIP;
+
bands = 0;
setbit(&bands, IEEE80211_MODE_11B);
setbit(&bands, IEEE80211_MODE_11G);
diff --git a/sys/dev/usb/wlan/if_uath.c b/sys/dev/usb/wlan/if_uath.c
index f238d0d..76bd02b 100644
--- a/sys/dev/usb/wlan/if_uath.c
+++ b/sys/dev/usb/wlan/if_uath.c
@@ -457,6 +457,12 @@ uath_attach(device_t dev)
IEEE80211_C_BGSCAN | /* capable of bg scanning */
IEEE80211_C_TXFRAG; /* handle tx frags */
+ ic->ic_cryptocaps =
+ IEEE80211_CRYPTO_WEP |
+ IEEE80211_CRYPTO_AES_CCM |
+ IEEE80211_CRYPTO_TKIPMIC |
+ IEEE80211_CRYPTO_TKIP;
+
/* put a regulatory domain to reveal informations. */
uath_regdomain = sc->sc_devcap.regDomain;
diff --git a/sys/dev/usb/wlan/if_upgt.c b/sys/dev/usb/wlan/if_upgt.c
index eff7922..fb4d619 100644
--- a/sys/dev/usb/wlan/if_upgt.c
+++ b/sys/dev/usb/wlan/if_upgt.c
@@ -353,6 +353,12 @@ upgt_attach(device_t dev)
| IEEE80211_C_WPA /* 802.11i */
;
+ ic->ic_cryptocaps =
+ IEEE80211_CRYPTO_WEP |
+ IEEE80211_CRYPTO_AES_CCM |
+ IEEE80211_CRYPTO_TKIPMIC |
+ IEEE80211_CRYPTO_TKIP;
+
bands = 0;
setbit(&bands, IEEE80211_MODE_11B);
setbit(&bands, IEEE80211_MODE_11G);
diff --git a/sys/dev/usb/wlan/if_ural.c b/sys/dev/usb/wlan/if_ural.c
index f3fa1ca..0815452 100644
--- a/sys/dev/usb/wlan/if_ural.c
+++ b/sys/dev/usb/wlan/if_ural.c
@@ -487,6 +487,12 @@ ural_attach(device_t self)
| IEEE80211_C_WPA /* 802.11i */
;
+ ic->ic_cryptocaps =
+ IEEE80211_CRYPTO_WEP |
+ IEEE80211_CRYPTO_AES_CCM |
+ IEEE80211_CRYPTO_TKIPMIC |
+ IEEE80211_CRYPTO_TKIP;
+
bands = 0;
setbit(&bands, IEEE80211_MODE_11B);
setbit(&bands, IEEE80211_MODE_11G);
diff --git a/sys/dev/usb/wlan/if_urtw.c b/sys/dev/usb/wlan/if_urtw.c
index 184317b..10fd7db 100644
--- a/sys/dev/usb/wlan/if_urtw.c
+++ b/sys/dev/usb/wlan/if_urtw.c
@@ -893,6 +893,12 @@ urtw_attach(device_t dev)
IEEE80211_C_BGSCAN | /* capable of bg scanning */
IEEE80211_C_WPA; /* 802.11i */
+ ic->ic_cryptocaps =
+ IEEE80211_CRYPTO_WEP |
+ IEEE80211_CRYPTO_AES_CCM |
+ IEEE80211_CRYPTO_TKIPMIC |
+ IEEE80211_CRYPTO_TKIP;
+
bands = 0;
setbit(&bands, IEEE80211_MODE_11B);
setbit(&bands, IEEE80211_MODE_11G);
diff --git a/sys/dev/usb/wlan/if_urtwn.c b/sys/dev/usb/wlan/if_urtwn.c
index 180dbe2..48ac4cb 100644
--- a/sys/dev/usb/wlan/if_urtwn.c
+++ b/sys/dev/usb/wlan/if_urtwn.c
@@ -80,6 +80,7 @@ SYSCTL_INT(_hw_usb_urtwn, OID_AUTO, debug, CTLFLAG_RW, &urtwn_debug, 0,
"Debug level");
#endif
+#define URTWN_RSSI(r) (r) - 110
#define IEEE80211_HAS_ADDR4(wh) \
(((wh)->i_fc[1] & IEEE80211_FC1_DIR_MASK) == IEEE80211_FC1_DIR_DSTODS)
@@ -418,6 +419,12 @@ urtwn_attach(device_t self)
| IEEE80211_C_WPA /* 802.11i */
;
+ ic->ic_cryptocaps =
+ IEEE80211_CRYPTO_WEP |
+ IEEE80211_CRYPTO_AES_CCM |
+ IEEE80211_CRYPTO_TKIPMIC |
+ IEEE80211_CRYPTO_TKIP;
+
bands = 0;
setbit(&bands, IEEE80211_MODE_11B);
setbit(&bands, IEEE80211_MODE_11G);
@@ -610,6 +617,11 @@ urtwn_rx_frame(struct urtwn_softc *sc, uint8_t *buf, int pktlen, int *rssi_p)
rssi = urtwn_get_rssi(sc, rate, &stat[1]);
/* Update our average RSSI. */
urtwn_update_avgrssi(sc, rate, rssi);
+ /*
+ * Convert the RSSI to a range that will be accepted
+ * by net80211.
+ */
+ rssi = URTWN_RSSI(rssi);
}
m = m_getcl(M_DONTWAIT, MT_DATA, M_PKTHDR);
@@ -1253,6 +1265,7 @@ urtwn_ra_init(struct urtwn_softc *sc)
cmd.mask = htole32(mode << 28 | basicrates);
error = urtwn_fw_cmd(sc, R92C_CMD_MACID_CONFIG, &cmd, sizeof(cmd));
if (error != 0) {
+ ieee80211_free_node(ni);
device_printf(sc->sc_dev,
"could not add broadcast station\n");
return (error);
@@ -1267,6 +1280,7 @@ urtwn_ra_init(struct urtwn_softc *sc)
cmd.mask = htole32(mode << 28 | rates);
error = urtwn_fw_cmd(sc, R92C_CMD_MACID_CONFIG, &cmd, sizeof(cmd));
if (error != 0) {
+ ieee80211_free_node(ni);
device_printf(sc->sc_dev, "could not add BSS station\n");
return (error);
}
@@ -1276,7 +1290,9 @@ urtwn_ra_init(struct urtwn_softc *sc)
maxrate);
/* Indicate highest supported rate. */
- ni->ni_txrate = rs->rs_nrates - 1;
+ ni->ni_txrate = rs->rs_rates[rs->rs_nrates - 1];
+ ieee80211_free_node(ni);
+
return (0);
}
diff --git a/sys/dev/usb/wlan/if_zyd.c b/sys/dev/usb/wlan/if_zyd.c
index 3d3f269..d245d99 100644
--- a/sys/dev/usb/wlan/if_zyd.c
+++ b/sys/dev/usb/wlan/if_zyd.c
@@ -400,6 +400,12 @@ zyd_attach(device_t dev)
| IEEE80211_C_WPA /* 802.11i */
;
+ ic->ic_cryptocaps =
+ IEEE80211_CRYPTO_WEP |
+ IEEE80211_CRYPTO_AES_CCM |
+ IEEE80211_CRYPTO_TKIPMIC |
+ IEEE80211_CRYPTO_TKIP;
+
bands = 0;
setbit(&bands, IEEE80211_MODE_11B);
setbit(&bands, IEEE80211_MODE_11G);
diff --git a/sys/dev/virtio/balloon/virtio_balloon.c b/sys/dev/virtio/balloon/virtio_balloon.c
index 9b45459..9a87cf2 100644
--- a/sys/dev/virtio/balloon/virtio_balloon.c
+++ b/sys/dev/virtio/balloon/virtio_balloon.c
@@ -1,5 +1,5 @@
/*-
- * Copyright (c) 2011, Bryan Venteicher <bryanv@daemoninthecloset.org>
+ * Copyright (c) 2011, Bryan Venteicher <bryanv@FreeBSD.org>
* All rights reserved.
*
* Redistribution and use in source and binary forms, with or without
@@ -70,7 +70,7 @@ struct vtballoon_softc {
uint32_t vtballoon_current_npages;
TAILQ_HEAD(,vm_page) vtballoon_pages;
- struct proc *vtballoon_kproc;
+ struct thread *vtballoon_td;
uint32_t *vtballoon_page_frames;
int vtballoon_timeout;
};
@@ -90,7 +90,7 @@ static int vtballoon_config_change(device_t);
static void vtballoon_negotiate_features(struct vtballoon_softc *);
static int vtballoon_alloc_virtqueues(struct vtballoon_softc *);
-static int vtballoon_vq_intr(void *);
+static void vtballoon_vq_intr(void *);
static void vtballoon_inflate(struct vtballoon_softc *, int);
static void vtballoon_deflate(struct vtballoon_softc *, int);
@@ -127,9 +127,9 @@ CTASSERT(VTBALLOON_PAGES_PER_REQUEST * sizeof(uint32_t) <= PAGE_SIZE);
#define VTBALLOON_MTX(_sc) &(_sc)->vtballoon_mtx
#define VTBALLOON_LOCK_INIT(_sc, _name) mtx_init(VTBALLOON_MTX((_sc)), _name, \
- "VirtIO Balloon Lock", MTX_SPIN)
-#define VTBALLOON_LOCK(_sc) mtx_lock_spin(VTBALLOON_MTX((_sc)))
-#define VTBALLOON_UNLOCK(_sc) mtx_unlock_spin(VTBALLOON_MTX((_sc)))
+ "VirtIO Balloon Lock", MTX_DEF)
+#define VTBALLOON_LOCK(_sc) mtx_lock(VTBALLOON_MTX((_sc)))
+#define VTBALLOON_UNLOCK(_sc) mtx_unlock(VTBALLOON_MTX((_sc)))
#define VTBALLOON_LOCK_DESTROY(_sc) mtx_destroy(VTBALLOON_MTX((_sc)))
static device_method_t vtballoon_methods[] = {
@@ -206,10 +206,10 @@ vtballoon_attach(device_t dev)
goto fail;
}
- error = kproc_create(vtballoon_thread, sc, &sc->vtballoon_kproc,
+ error = kthread_add(vtballoon_thread, sc, NULL, &sc->vtballoon_td,
0, 0, "virtio_balloon");
if (error) {
- device_printf(dev, "cannot create balloon kproc\n");
+ device_printf(dev, "cannot create balloon kthread\n");
goto fail;
}
@@ -230,15 +230,14 @@ vtballoon_detach(device_t dev)
sc = device_get_softc(dev);
- if (sc->vtballoon_kproc != NULL) {
+ if (sc->vtballoon_td != NULL) {
VTBALLOON_LOCK(sc);
sc->vtballoon_flags |= VTBALLOON_FLAG_DETACH;
wakeup_one(sc);
- msleep_spin(sc->vtballoon_kproc, VTBALLOON_MTX(sc),
- "vtbdth", 0);
+ msleep(sc->vtballoon_td, VTBALLOON_MTX(sc), 0, "vtbdth", 0);
VTBALLOON_UNLOCK(sc);
- sc->vtballoon_kproc = NULL;
+ sc->vtballoon_td = NULL;
}
if (device_is_attached(dev)) {
@@ -300,7 +299,7 @@ vtballoon_alloc_virtqueues(struct vtballoon_softc *sc)
return (virtio_alloc_virtqueues(dev, 0, nvqs, vq_info));
}
-static int
+static void
vtballoon_vq_intr(void *xsc)
{
struct vtballoon_softc *sc;
@@ -310,8 +309,6 @@ vtballoon_vq_intr(void *xsc)
VTBALLOON_LOCK(sc);
wakeup_one(sc);
VTBALLOON_UNLOCK(sc);
-
- return (1);
}
static void
@@ -322,28 +319,26 @@ vtballoon_inflate(struct vtballoon_softc *sc, int npages)
int i;
vq = sc->vtballoon_inflate_vq;
- m = NULL;
if (npages > VTBALLOON_PAGES_PER_REQUEST)
npages = VTBALLOON_PAGES_PER_REQUEST;
- KASSERT(npages > 0, ("balloon doesn't need inflating?"));
for (i = 0; i < npages; i++) {
- if ((m = vtballoon_alloc_page(sc)) == NULL)
+ if ((m = vtballoon_alloc_page(sc)) == NULL) {
+ sc->vtballoon_timeout = VTBALLOON_LOWMEM_TIMEOUT;
break;
+ }
sc->vtballoon_page_frames[i] =
VM_PAGE_TO_PHYS(m) >> VIRTIO_BALLOON_PFN_SHIFT;
- KASSERT(m->queue == PQ_NONE, ("allocated page on queue"));
+ KASSERT(m->queue == PQ_NONE,
+ ("%s: allocated page %p on queue", __func__, m));
TAILQ_INSERT_TAIL(&sc->vtballoon_pages, m, pageq);
}
if (i > 0)
vtballoon_send_page_frames(sc, vq, i);
-
- if (m == NULL)
- sc->vtballoon_timeout = VTBALLOON_LOWMEM_TIMEOUT;
}
static void
@@ -359,11 +354,10 @@ vtballoon_deflate(struct vtballoon_softc *sc, int npages)
if (npages > VTBALLOON_PAGES_PER_REQUEST)
npages = VTBALLOON_PAGES_PER_REQUEST;
- KASSERT(npages > 0, ("balloon doesn't need deflating?"));
for (i = 0; i < npages; i++) {
m = TAILQ_FIRST(&sc->vtballoon_pages);
- KASSERT(m != NULL, ("no more pages to deflate"));
+ KASSERT(m != NULL, ("%s: no more pages to deflate", __func__));
sc->vtballoon_page_frames[i] =
VM_PAGE_TO_PHYS(m) >> VIRTIO_BALLOON_PFN_SHIFT;
@@ -385,7 +379,9 @@ vtballoon_deflate(struct vtballoon_softc *sc, int npages)
KASSERT((TAILQ_EMPTY(&sc->vtballoon_pages) &&
sc->vtballoon_current_npages == 0) ||
(!TAILQ_EMPTY(&sc->vtballoon_pages) &&
- sc->vtballoon_current_npages != 0), ("balloon empty?"));
+ sc->vtballoon_current_npages != 0),
+ ("%s: bogus page count %d", __func__,
+ sc->vtballoon_current_npages));
}
static void
@@ -413,7 +409,7 @@ vtballoon_send_page_frames(struct vtballoon_softc *sc, struct virtqueue *vq,
*/
VTBALLOON_LOCK(sc);
while ((c = virtqueue_dequeue(vq, NULL)) == NULL)
- msleep_spin(sc, VTBALLOON_MTX(sc), "vtbspf", 0);
+ msleep(sc, VTBALLOON_MTX(sc), 0, "vtbspf", 0);
VTBALLOON_UNLOCK(sc);
KASSERT(c == vq, ("unexpected balloon operation response"));
@@ -512,7 +508,7 @@ vtballoon_sleep(struct vtballoon_softc *sc)
if (current < desired && timeout == 0)
break;
- msleep_spin(sc, VTBALLOON_MTX(sc), "vtbslp", timeout);
+ msleep(sc, VTBALLOON_MTX(sc), 0, "vtbslp", timeout);
}
VTBALLOON_UNLOCK(sc);
@@ -544,7 +540,7 @@ vtballoon_thread(void *xsc)
}
}
- kproc_exit(0);
+ kthread_exit();
}
static void
diff --git a/sys/dev/virtio/block/virtio_blk.c b/sys/dev/virtio/block/virtio_blk.c
index 21d7703..5219a38 100644
--- a/sys/dev/virtio/block/virtio_blk.c
+++ b/sys/dev/virtio/block/virtio_blk.c
@@ -1,5 +1,5 @@
/*-
- * Copyright (c) 2011, Bryan Venteicher <bryanv@daemoninthecloset.org>
+ * Copyright (c) 2011, Bryan Venteicher <bryanv@FreeBSD.org>
* All rights reserved.
*
* Redistribution and use in source and binary forms, with or without
@@ -36,10 +36,10 @@ __FBSDID("$FreeBSD$");
#include <sys/malloc.h>
#include <sys/module.h>
#include <sys/sglist.h>
+#include <sys/sysctl.h>
#include <sys/lock.h>
#include <sys/mutex.h>
#include <sys/queue.h>
-#include <sys/taskqueue.h>
#include <geom/geom_disk.h>
@@ -62,6 +62,12 @@ struct vtblk_request {
TAILQ_ENTRY(vtblk_request) vbr_link;
};
+enum vtblk_cache_mode {
+ VTBLK_CACHE_WRITETHROUGH,
+ VTBLK_CACHE_WRITEBACK,
+ VTBLK_CACHE_MAX
+};
+
struct vtblk_softc {
device_t vtblk_dev;
struct mtx vtblk_mtx;
@@ -73,6 +79,7 @@ struct vtblk_softc {
#define VTBLK_FLAG_SUSPEND 0x0008
#define VTBLK_FLAG_DUMPING 0x0010
#define VTBLK_FLAG_BARRIER 0x0020
+#define VTBLK_FLAG_WC_CONFIG 0x0040
struct virtqueue *vtblk_vq;
struct sglist *vtblk_sglist;
@@ -85,11 +92,9 @@ struct vtblk_softc {
vtblk_req_ready;
struct vtblk_request *vtblk_req_ordered;
- struct taskqueue *vtblk_tq;
- struct task vtblk_intr_task;
-
int vtblk_max_nsegs;
int vtblk_request_count;
+ enum vtblk_cache_mode vtblk_write_cache;
struct vtblk_request vtblk_dump_request;
};
@@ -102,8 +107,9 @@ static struct virtio_feature_desc vtblk_feature_desc[] = {
{ VIRTIO_BLK_F_RO, "ReadOnly" },
{ VIRTIO_BLK_F_BLK_SIZE, "BlockSize" },
{ VIRTIO_BLK_F_SCSI, "SCSICmds" },
- { VIRTIO_BLK_F_FLUSH, "FlushCmd" },
+ { VIRTIO_BLK_F_WCE, "WriteCache" },
{ VIRTIO_BLK_F_TOPOLOGY, "Topology" },
+ { VIRTIO_BLK_F_CONFIG_WCE, "ConfigWCE" },
{ 0, NULL }
};
@@ -116,6 +122,7 @@ static int vtblk_detach(device_t);
static int vtblk_suspend(device_t);
static int vtblk_resume(device_t);
static int vtblk_shutdown(device_t);
+static int vtblk_config_change(device_t);
static int vtblk_open(struct disk *);
static int vtblk_close(struct disk *);
@@ -128,6 +135,11 @@ static void vtblk_negotiate_features(struct vtblk_softc *);
static int vtblk_maximum_segments(struct vtblk_softc *,
struct virtio_blk_config *);
static int vtblk_alloc_virtqueue(struct vtblk_softc *);
+static void vtblk_resize_disk(struct vtblk_softc *, uint64_t);
+static void vtblk_set_write_cache(struct vtblk_softc *, int);
+static int vtblk_write_cache_enabled(struct vtblk_softc *sc,
+ struct virtio_blk_config *);
+static int vtblk_write_cache_sysctl(SYSCTL_HANDLER_ARGS);
static void vtblk_alloc_disk(struct vtblk_softc *,
struct virtio_blk_config *);
static void vtblk_create_disk(struct vtblk_softc *);
@@ -138,11 +150,12 @@ static struct vtblk_request * vtblk_bio_request(struct vtblk_softc *);
static int vtblk_execute_request(struct vtblk_softc *,
struct vtblk_request *);
-static int vtblk_vq_intr(void *);
-static void vtblk_intr_task(void *, int);
+static void vtblk_vq_intr(void *);
static void vtblk_stop(struct vtblk_softc *);
+static void vtblk_read_config(struct vtblk_softc *,
+ struct virtio_blk_config *);
static void vtblk_get_ident(struct vtblk_softc *);
static void vtblk_prepare_dump(struct vtblk_softc *);
static int vtblk_write_dump(struct vtblk_softc *, void *, off_t, size_t);
@@ -167,9 +180,14 @@ static void vtblk_enqueue_ready(struct vtblk_softc *,
static int vtblk_request_error(struct vtblk_request *);
static void vtblk_finish_bio(struct bio *, int);
+static void vtblk_setup_sysctl(struct vtblk_softc *);
+static int vtblk_tunable_int(struct vtblk_softc *, const char *, int);
+
/* Tunables. */
static int vtblk_no_ident = 0;
TUNABLE_INT("hw.vtblk.no_ident", &vtblk_no_ident);
+static int vtblk_writecache_mode = -1;
+TUNABLE_INT("hw.vtblk.writecache_mode", &vtblk_writecache_mode);
/* Features desired/implemented by this driver. */
#define VTBLK_FEATURES \
@@ -179,13 +197,14 @@ TUNABLE_INT("hw.vtblk.no_ident", &vtblk_no_ident);
VIRTIO_BLK_F_GEOMETRY | \
VIRTIO_BLK_F_RO | \
VIRTIO_BLK_F_BLK_SIZE | \
- VIRTIO_BLK_F_FLUSH | \
+ VIRTIO_BLK_F_WCE | \
+ VIRTIO_BLK_F_CONFIG_WCE | \
VIRTIO_RING_F_INDIRECT_DESC)
#define VTBLK_MTX(_sc) &(_sc)->vtblk_mtx
#define VTBLK_LOCK_INIT(_sc, _name) \
mtx_init(VTBLK_MTX((_sc)), (_name), \
- "VTBLK Lock", MTX_DEF)
+ "VirtIO Block Lock", MTX_DEF)
#define VTBLK_LOCK(_sc) mtx_lock(VTBLK_MTX((_sc)))
#define VTBLK_UNLOCK(_sc) mtx_unlock(VTBLK_MTX((_sc)))
#define VTBLK_LOCK_DESTROY(_sc) mtx_destroy(VTBLK_MTX((_sc)))
@@ -211,6 +230,9 @@ static device_method_t vtblk_methods[] = {
DEVMETHOD(device_resume, vtblk_resume),
DEVMETHOD(device_shutdown, vtblk_shutdown),
+ /* VirtIO methods. */
+ DEVMETHOD(virtio_config_change, vtblk_config_change),
+
DEVMETHOD_END
};
@@ -284,10 +306,13 @@ vtblk_attach(device_t dev)
sc->vtblk_flags |= VTBLK_FLAG_READONLY;
if (virtio_with_feature(dev, VIRTIO_BLK_F_BARRIER))
sc->vtblk_flags |= VTBLK_FLAG_BARRIER;
+ if (virtio_with_feature(dev, VIRTIO_BLK_F_CONFIG_WCE))
+ sc->vtblk_flags |= VTBLK_FLAG_WC_CONFIG;
+
+ vtblk_setup_sysctl(sc);
/* Get local copy of config. */
- virtio_read_device_config(dev, 0, &blkcfg,
- sizeof(struct virtio_blk_config));
+ vtblk_read_config(sc, &blkcfg);
/*
* With the current sglist(9) implementation, it is not easy
@@ -333,24 +358,12 @@ vtblk_attach(device_t dev)
vtblk_alloc_disk(sc, &blkcfg);
- TASK_INIT(&sc->vtblk_intr_task, 0, vtblk_intr_task, sc);
- sc->vtblk_tq = taskqueue_create_fast("vtblk_taskq", M_NOWAIT,
- taskqueue_thread_enqueue, &sc->vtblk_tq);
- if (sc->vtblk_tq == NULL) {
- error = ENOMEM;
- device_printf(dev, "cannot allocate taskqueue\n");
- goto fail;
- }
-
error = virtio_setup_intr(dev, INTR_TYPE_BIO | INTR_ENTROPY);
if (error) {
device_printf(dev, "cannot setup virtqueue interrupt\n");
goto fail;
}
- taskqueue_start_threads(&sc->vtblk_tq, 1, PI_DISK, "%s taskq",
- device_get_nameunit(dev));
-
vtblk_create_disk(sc);
virtqueue_enable_intr(sc->vtblk_vq);
@@ -375,12 +388,6 @@ vtblk_detach(device_t dev)
vtblk_stop(sc);
VTBLK_UNLOCK(sc);
- if (sc->vtblk_tq != NULL) {
- taskqueue_drain(sc->vtblk_tq, &sc->vtblk_intr_task);
- taskqueue_free(sc->vtblk_tq);
- sc->vtblk_tq = NULL;
- }
-
vtblk_drain(sc);
if (sc->vtblk_disk != NULL) {
@@ -441,6 +448,26 @@ vtblk_shutdown(device_t dev)
}
static int
+vtblk_config_change(device_t dev)
+{
+ struct vtblk_softc *sc;
+ struct virtio_blk_config blkcfg;
+ uint64_t capacity;
+
+ sc = device_get_softc(dev);
+
+ vtblk_read_config(sc, &blkcfg);
+
+ /* Capacity is always in 512-byte units. */
+ capacity = blkcfg.capacity * 512;
+
+ if (sc->vtblk_disk->d_mediasize != capacity)
+ vtblk_resize_disk(sc, capacity);
+
+ return (0);
+}
+
+static int
vtblk_open(struct disk *dp)
{
struct vtblk_softc *sc;
@@ -541,8 +568,8 @@ vtblk_strategy(struct bio *bp)
max_nsegs = sc->vtblk_max_nsegs - VTBLK_MIN_SEGMENTS;
KASSERT(nsegs <= max_nsegs,
- ("bio %p spanned too many segments: %d, max: %d",
- bp, nsegs, max_nsegs));
+ ("%s: bio %p spanned too many segments: %d, max: %d",
+ __func__, bp, nsegs, max_nsegs));
}
#endif
@@ -606,6 +633,84 @@ vtblk_alloc_virtqueue(struct vtblk_softc *sc)
}
static void
+vtblk_resize_disk(struct vtblk_softc *sc, uint64_t new_capacity)
+{
+ device_t dev;
+ struct disk *dp;
+ int error;
+
+ dev = sc->vtblk_dev;
+ dp = sc->vtblk_disk;
+
+ dp->d_mediasize = new_capacity;
+ if (bootverbose) {
+ device_printf(dev, "resized to %juMB (%ju %u byte sectors)\n",
+ (uintmax_t) dp->d_mediasize >> 20,
+ (uintmax_t) dp->d_mediasize / dp->d_sectorsize,
+ dp->d_sectorsize);
+ }
+
+ error = disk_resize(dp, M_NOWAIT);
+ if (error) {
+ device_printf(dev,
+ "disk_resize(9) failed, error: %d\n", error);
+ }
+}
+
+static void
+vtblk_set_write_cache(struct vtblk_softc *sc, int wc)
+{
+
+ /* Set either writeback (1) or writethrough (0) mode. */
+ virtio_write_dev_config_1(sc->vtblk_dev,
+ offsetof(struct virtio_blk_config, writeback), wc);
+}
+
+static int
+vtblk_write_cache_enabled(struct vtblk_softc *sc,
+ struct virtio_blk_config *blkcfg)
+{
+ int wc;
+
+ if (sc->vtblk_flags & VTBLK_FLAG_WC_CONFIG) {
+ wc = vtblk_tunable_int(sc, "writecache_mode",
+ vtblk_writecache_mode);
+ if (wc >= 0 && wc < VTBLK_CACHE_MAX)
+ vtblk_set_write_cache(sc, wc);
+ else
+ wc = blkcfg->writeback;
+ } else
+ wc = virtio_with_feature(sc->vtblk_dev, VIRTIO_BLK_F_WCE);
+
+ return (wc);
+}
+
+static int
+vtblk_write_cache_sysctl(SYSCTL_HANDLER_ARGS)
+{
+ struct vtblk_softc *sc;
+ int wc, error;
+
+ sc = oidp->oid_arg1;
+ wc = sc->vtblk_write_cache;
+
+ error = sysctl_handle_int(oidp, &wc, 0, req);
+ if (error || req->newptr == NULL)
+ return (error);
+ if ((sc->vtblk_flags & VTBLK_FLAG_WC_CONFIG) == 0)
+ return (EPERM);
+ if (wc < 0 || wc >= VTBLK_CACHE_MAX)
+ return (EINVAL);
+
+ VTBLK_LOCK(sc);
+ sc->vtblk_write_cache = wc;
+ vtblk_set_write_cache(sc, sc->vtblk_write_cache);
+ VTBLK_UNLOCK(sc);
+
+ return (0);
+}
+
+static void
vtblk_alloc_disk(struct vtblk_softc *sc, struct virtio_blk_config *blkcfg)
{
device_t dev;
@@ -621,6 +726,11 @@ vtblk_alloc_disk(struct vtblk_softc *sc, struct virtio_blk_config *blkcfg)
dp->d_name = VTBLK_DISK_NAME;
dp->d_unit = device_get_unit(dev);
dp->d_drv1 = sc;
+ dp->d_flags = DISKFLAG_CANFLUSHCACHE;
+ dp->d_hba_vendor = virtio_get_vendor(dev);
+ dp->d_hba_device = virtio_get_device(dev);
+ dp->d_hba_subvendor = virtio_get_subvendor(dev);
+ dp->d_hba_subdevice = virtio_get_subdevice(dev);
if ((sc->vtblk_flags & VTBLK_FLAG_READONLY) == 0)
dp->d_dump = vtblk_dump;
@@ -656,8 +766,18 @@ vtblk_alloc_disk(struct vtblk_softc *sc, struct virtio_blk_config *blkcfg)
dp->d_fwheads = blkcfg->geometry.heads;
}
- if (virtio_with_feature(dev, VIRTIO_BLK_F_FLUSH))
- dp->d_flags |= DISKFLAG_CANFLUSHCACHE;
+ if (virtio_with_feature(dev, VIRTIO_BLK_F_TOPOLOGY)) {
+ dp->d_stripesize = dp->d_sectorsize *
+ (1 << blkcfg->topology.physical_block_exp);
+ dp->d_stripeoffset = (dp->d_stripesize -
+ blkcfg->topology.alignment_offset * dp->d_sectorsize) %
+ dp->d_stripesize;
+ }
+
+ if (vtblk_write_cache_enabled(sc, blkcfg) != 0)
+ sc->vtblk_write_cache = VTBLK_CACHE_WRITEBACK;
+ else
+ sc->vtblk_write_cache = VTBLK_CACHE_WRITETHROUGH;
}
static void
@@ -765,8 +885,7 @@ vtblk_bio_request(struct vtblk_softc *sc)
req->vbr_hdr.sector = bp->bio_offset / 512;
break;
default:
- panic("%s: bio with unhandled cmd: %d", __FUNCTION__,
- bp->bio_cmd);
+ panic("%s: bio with unhandled cmd: %d", __func__, bp->bio_cmd);
}
return (req);
@@ -809,14 +928,13 @@ vtblk_execute_request(struct vtblk_softc *sc, struct vtblk_request *req)
}
sglist_reset(sg);
-
sglist_append(sg, &req->vbr_hdr, sizeof(struct virtio_blk_outhdr));
if (bp->bio_cmd == BIO_READ || bp->bio_cmd == BIO_WRITE) {
error = sglist_append(sg, bp->bio_data, bp->bio_bcount);
if (error || sg->sg_nseg == sg->sg_maxseg)
panic("%s: data buffer too big bio:%p error:%d",
- __FUNCTION__, bp, error);
+ __func__, bp, error);
/* BIO_READ means the host writes into our buffer. */
if (bp->bio_cmd == BIO_READ)
@@ -834,28 +952,16 @@ vtblk_execute_request(struct vtblk_softc *sc, struct vtblk_request *req)
return (error);
}
-static int
-vtblk_vq_intr(void *xsc)
-{
- struct vtblk_softc *sc;
-
- sc = xsc;
-
- virtqueue_disable_intr(sc->vtblk_vq);
- taskqueue_enqueue_fast(sc->vtblk_tq, &sc->vtblk_intr_task);
-
- return (1);
-}
-
static void
-vtblk_intr_task(void *arg, int pending)
+vtblk_vq_intr(void *xsc)
{
struct vtblk_softc *sc;
struct virtqueue *vq;
- sc = arg;
+ sc = xsc;
vq = sc->vtblk_vq;
+again:
VTBLK_LOCK(sc);
if (sc->vtblk_flags & VTBLK_FLAG_DETACH) {
VTBLK_UNLOCK(sc);
@@ -872,9 +978,7 @@ vtblk_intr_task(void *arg, int pending)
if (virtqueue_enable_intr(vq) != 0) {
virtqueue_disable_intr(vq);
VTBLK_UNLOCK(sc);
- taskqueue_enqueue_fast(sc->vtblk_tq,
- &sc->vtblk_intr_task);
- return;
+ goto again;
}
VTBLK_UNLOCK(sc);
@@ -888,6 +992,37 @@ vtblk_stop(struct vtblk_softc *sc)
virtio_stop(sc->vtblk_dev);
}
+#define VTBLK_GET_CONFIG(_dev, _feature, _field, _cfg) \
+ if (virtio_with_feature(_dev, _feature)) { \
+ virtio_read_device_config(_dev, \
+ offsetof(struct virtio_blk_config, _field), \
+ &(_cfg)->_field, sizeof((_cfg)->_field)); \
+ }
+
+static void
+vtblk_read_config(struct vtblk_softc *sc, struct virtio_blk_config *blkcfg)
+{
+ device_t dev;
+
+ dev = sc->vtblk_dev;
+
+ bzero(blkcfg, sizeof(struct virtio_blk_config));
+
+ /* The capacity is always available. */
+ virtio_read_device_config(dev, offsetof(struct virtio_blk_config,
+ capacity), &blkcfg->capacity, sizeof(blkcfg->capacity));
+
+ /* Read the configuration if the feature was negotiated. */
+ VTBLK_GET_CONFIG(dev, VIRTIO_BLK_F_SIZE_MAX, size_max, blkcfg);
+ VTBLK_GET_CONFIG(dev, VIRTIO_BLK_F_SEG_MAX, seg_max, blkcfg);
+ VTBLK_GET_CONFIG(dev, VIRTIO_BLK_F_GEOMETRY, geometry, blkcfg);
+ VTBLK_GET_CONFIG(dev, VIRTIO_BLK_F_BLK_SIZE, blk_size, blkcfg);
+ VTBLK_GET_CONFIG(dev, VIRTIO_BLK_F_TOPOLOGY, topology, blkcfg);
+ VTBLK_GET_CONFIG(dev, VIRTIO_BLK_F_CONFIG_WCE, writeback, blkcfg);
+}
+
+#undef VTBLK_GET_CONFIG
+
static void
vtblk_get_ident(struct vtblk_softc *sc)
{
@@ -899,7 +1034,7 @@ vtblk_get_ident(struct vtblk_softc *sc)
dp = sc->vtblk_disk;
len = MIN(VIRTIO_BLK_ID_BYTES, DISK_IDENT_SIZE);
- if (vtblk_no_ident != 0)
+ if (vtblk_tunable_int(sc, "no_ident", vtblk_no_ident) != 0)
return;
req = vtblk_dequeue_request(sc);
@@ -949,8 +1084,10 @@ vtblk_prepare_dump(struct vtblk_softc *sc)
*/
vtblk_drain_vq(sc, 1);
- if (virtio_reinit(dev, sc->vtblk_features) != 0)
- panic("cannot reinit VirtIO block device during dump");
+ if (virtio_reinit(dev, sc->vtblk_features) != 0) {
+ panic("%s: cannot reinit VirtIO block device during dump",
+ device_get_nameunit(dev));
+ }
virtqueue_disable_intr(vq);
virtio_reinit_complete(dev);
@@ -1003,7 +1140,6 @@ static int
vtblk_poll_request(struct vtblk_softc *sc, struct vtblk_request *req)
{
struct virtqueue *vq;
- struct vtblk_request *r;
int error;
vq = sc->vtblk_vq;
@@ -1016,14 +1152,12 @@ vtblk_poll_request(struct vtblk_softc *sc, struct vtblk_request *req)
return (error);
virtqueue_notify(vq);
-
- r = virtqueue_poll(vq, NULL);
- KASSERT(r == req, ("unexpected request response: %p/%p", r, req));
+ virtqueue_poll(vq, NULL);
error = vtblk_request_error(req);
if (error && bootverbose) {
device_printf(sc->vtblk_dev,
- "%s: IO error: %d\n", __FUNCTION__, error);
+ "%s: IO error: %d\n", __func__, error);
}
return (error);
@@ -1154,7 +1288,7 @@ vtblk_free_requests(struct vtblk_softc *sc)
struct vtblk_request *req;
KASSERT(TAILQ_EMPTY(&sc->vtblk_req_ready),
- ("ready requests left on queue"));
+ ("%s: ready requests left on queue", __func__));
while ((req = vtblk_dequeue_request(sc)) != NULL) {
sc->vtblk_request_count--;
@@ -1162,7 +1296,7 @@ vtblk_free_requests(struct vtblk_softc *sc)
}
KASSERT(sc->vtblk_request_count == 0,
- ("leaked requests: %d", sc->vtblk_request_count));
+ ("%s: leaked %d requests", __func__, sc->vtblk_request_count));
}
static struct vtblk_request *
@@ -1236,3 +1370,33 @@ vtblk_finish_bio(struct bio *bp, int error)
biodone(bp);
}
+
+static void
+vtblk_setup_sysctl(struct vtblk_softc *sc)
+{
+ device_t dev;
+ struct sysctl_ctx_list *ctx;
+ struct sysctl_oid *tree;
+ struct sysctl_oid_list *child;
+
+ dev = sc->vtblk_dev;
+ ctx = device_get_sysctl_ctx(dev);
+ tree = device_get_sysctl_tree(dev);
+ child = SYSCTL_CHILDREN(tree);
+
+ SYSCTL_ADD_PROC(ctx, child, OID_AUTO, "writecache_mode",
+ CTLTYPE_INT | CTLFLAG_RW, sc, 0, vtblk_write_cache_sysctl,
+ "I", "Write cache mode (writethrough (0) or writeback (1))");
+}
+
+static int
+vtblk_tunable_int(struct vtblk_softc *sc, const char *knob, int def)
+{
+ char path[64];
+
+ snprintf(path, sizeof(path),
+ "hw.vtblk.%d.%s", device_get_unit(sc->vtblk_dev), knob);
+ TUNABLE_INT_FETCH(path, &def);
+
+ return (def);
+}
diff --git a/sys/dev/virtio/block/virtio_blk.h b/sys/dev/virtio/block/virtio_blk.h
index fdac9e5..cdb8d3f 100644
--- a/sys/dev/virtio/block/virtio_blk.h
+++ b/sys/dev/virtio/block/virtio_blk.h
@@ -39,8 +39,9 @@
#define VIRTIO_BLK_F_RO 0x0020 /* Disk is read-only */
#define VIRTIO_BLK_F_BLK_SIZE 0x0040 /* Block size of disk is available*/
#define VIRTIO_BLK_F_SCSI 0x0080 /* Supports scsi command passthru */
-#define VIRTIO_BLK_F_FLUSH 0x0200 /* Cache flush command support */
+#define VIRTIO_BLK_F_WCE 0x0200 /* Writeback mode enabled after reset */
#define VIRTIO_BLK_F_TOPOLOGY 0x0400 /* Topology information is available */
+#define VIRTIO_BLK_F_CONFIG_WCE 0x0800 /* Writeback mode available in config */
#define VIRTIO_BLK_ID_BYTES 20 /* ID string length */
@@ -51,15 +52,27 @@ struct virtio_blk_config {
uint32_t size_max;
/* The maximum number of segments (if VIRTIO_BLK_F_SEG_MAX) */
uint32_t seg_max;
- /* geometry the device (if VIRTIO_BLK_F_GEOMETRY) */
+ /* Geometry of the device (if VIRTIO_BLK_F_GEOMETRY) */
struct virtio_blk_geometry {
uint16_t cylinders;
uint8_t heads;
uint8_t sectors;
} geometry;
- /* block size of device (if VIRTIO_BLK_F_BLK_SIZE) */
+ /* Block size of device (if VIRTIO_BLK_F_BLK_SIZE) */
uint32_t blk_size;
+
+ /* Topology of the device (if VIRTIO_BLK_F_TOPOLOGY) */
+ struct virtio_blk_topology {
+ uint8_t physical_block_exp;
+ uint8_t alignment_offset;
+ uint16_t min_io_size;
+ uint16_t opt_io_size;
+ } topology;
+
+ /* Writeback mode (if VIRTIO_BLK_F_CONFIG_WCE) */
+ uint8_t writeback;
+
} __packed;
/*
diff --git a/sys/dev/virtio/network/if_vtnet.c b/sys/dev/virtio/network/if_vtnet.c
index 8e2de46..89604d1 100644
--- a/sys/dev/virtio/network/if_vtnet.c
+++ b/sys/dev/virtio/network/if_vtnet.c
@@ -1,5 +1,5 @@
/*-
- * Copyright (c) 2011, Bryan Venteicher <bryanv@daemoninthecloset.org>
+ * Copyright (c) 2011, Bryan Venteicher <bryanv@FreeBSD.org>
* All rights reserved.
*
* Redistribution and use in source and binary forms, with or without
@@ -42,7 +42,6 @@ __FBSDID("$FreeBSD$");
#include <sys/module.h>
#include <sys/socket.h>
#include <sys/sysctl.h>
-#include <sys/taskqueue.h>
#include <sys/random.h>
#include <sys/sglist.h>
#include <sys/lock.h>
@@ -97,7 +96,6 @@ static void vtnet_set_hwaddr(struct vtnet_softc *);
static int vtnet_is_link_up(struct vtnet_softc *);
static void vtnet_update_link_status(struct vtnet_softc *);
static void vtnet_watchdog(struct vtnet_softc *);
-static void vtnet_config_change_task(void *, int);
static int vtnet_change_mtu(struct vtnet_softc *, int);
static int vtnet_ioctl(struct ifnet *, u_long, caddr_t);
@@ -123,8 +121,7 @@ static int vtnet_rx_csum(struct vtnet_softc *, struct mbuf *,
struct virtio_net_hdr *);
static int vtnet_rxeof_merged(struct vtnet_softc *, struct mbuf *, int);
static int vtnet_rxeof(struct vtnet_softc *, int, int *);
-static void vtnet_rx_intr_task(void *, int);
-static int vtnet_rx_vq_intr(void *);
+static void vtnet_rx_vq_intr(void *);
static void vtnet_txeof(struct vtnet_softc *);
static struct mbuf * vtnet_tx_offload(struct vtnet_softc *, struct mbuf *,
@@ -135,8 +132,7 @@ static int vtnet_encap(struct vtnet_softc *, struct mbuf **);
static void vtnet_start_locked(struct ifnet *);
static void vtnet_start(struct ifnet *);
static void vtnet_tick(void *);
-static void vtnet_tx_intr_task(void *, int);
-static int vtnet_tx_vq_intr(void *);
+static void vtnet_tx_vq_intr(void *);
static void vtnet_stop(struct vtnet_softc *);
static int vtnet_reinit(struct vtnet_softc *);
@@ -427,19 +423,6 @@ vtnet_attach(device_t dev)
ifp->if_capabilities |= IFCAP_POLLING;
#endif
- TASK_INIT(&sc->vtnet_rx_intr_task, 0, vtnet_rx_intr_task, sc);
- TASK_INIT(&sc->vtnet_tx_intr_task, 0, vtnet_tx_intr_task, sc);
- TASK_INIT(&sc->vtnet_cfgchg_task, 0, vtnet_config_change_task, sc);
-
- sc->vtnet_tq = taskqueue_create_fast("vtnet_taskq", M_NOWAIT,
- taskqueue_thread_enqueue, &sc->vtnet_tq);
- if (sc->vtnet_tq == NULL) {
- error = ENOMEM;
- device_printf(dev, "cannot allocate taskqueue\n");
- ether_ifdetach(ifp);
- goto fail;
- }
-
error = virtio_setup_intr(dev, INTR_TYPE_NET);
if (error) {
device_printf(dev, "cannot setup virtqueue interrupts\n");
@@ -447,9 +430,6 @@ vtnet_attach(device_t dev)
goto fail;
}
- taskqueue_start_threads(&sc->vtnet_tq, 1, PI_NET, "%s taskq",
- device_get_nameunit(dev));
-
/*
* Device defaults to promiscuous mode for backwards
* compatibility. Turn it off if possible.
@@ -495,18 +475,10 @@ vtnet_detach(device_t dev)
VTNET_UNLOCK(sc);
callout_drain(&sc->vtnet_tick_ch);
- taskqueue_drain(taskqueue_fast, &sc->vtnet_cfgchg_task);
ether_ifdetach(ifp);
}
- if (sc->vtnet_tq != NULL) {
- taskqueue_drain(sc->vtnet_tq, &sc->vtnet_rx_intr_task);
- taskqueue_drain(sc->vtnet_tq, &sc->vtnet_tx_intr_task);
- taskqueue_free(sc->vtnet_tq);
- sc->vtnet_tq = NULL;
- }
-
if (sc->vtnet_vlan_attach != NULL) {
EVENTHANDLER_DEREGISTER(vlan_config, sc->vtnet_vlan_attach);
sc->vtnet_vlan_attach = NULL;
@@ -590,9 +562,11 @@ vtnet_config_change(device_t dev)
sc = device_get_softc(dev);
- taskqueue_enqueue_fast(taskqueue_fast, &sc->vtnet_cfgchg_task);
+ VTNET_LOCK(sc);
+ vtnet_update_link_status(sc);
+ VTNET_UNLOCK(sc);
- return (1);
+ return (0);
}
static void
@@ -788,18 +762,6 @@ vtnet_watchdog(struct vtnet_softc *sc)
vtnet_init_locked(sc);
}
-static void
-vtnet_config_change_task(void *arg, int pending)
-{
- struct vtnet_softc *sc;
-
- sc = arg;
-
- VTNET_LOCK(sc);
- vtnet_update_link_status(sc);
- VTNET_UNLOCK(sc);
-}
-
static int
vtnet_ioctl(struct ifnet *ifp, u_long cmd, caddr_t data)
{
@@ -1705,15 +1667,16 @@ vtnet_rxeof(struct vtnet_softc *sc, int count, int *rx_npktsp)
}
static void
-vtnet_rx_intr_task(void *arg, int pending)
+vtnet_rx_vq_intr(void *xsc)
{
struct vtnet_softc *sc;
struct ifnet *ifp;
int more;
- sc = arg;
+ sc = xsc;
ifp = sc->vtnet_ifp;
+again:
VTNET_LOCK(sc);
#ifdef DEVICE_POLLING
@@ -1730,31 +1693,15 @@ vtnet_rx_intr_task(void *arg, int pending)
}
more = vtnet_rxeof(sc, sc->vtnet_rx_process_limit, NULL);
- if (!more && vtnet_enable_rx_intr(sc) != 0) {
- vtnet_disable_rx_intr(sc);
- more = 1;
- }
-
- VTNET_UNLOCK(sc);
-
- if (more) {
+ if (more || vtnet_enable_rx_intr(sc) != 0) {
+ if (!more)
+ vtnet_disable_rx_intr(sc);
sc->vtnet_stats.rx_task_rescheduled++;
- taskqueue_enqueue_fast(sc->vtnet_tq,
- &sc->vtnet_rx_intr_task);
+ VTNET_UNLOCK(sc);
+ goto again;
}
-}
-
-static int
-vtnet_rx_vq_intr(void *xsc)
-{
- struct vtnet_softc *sc;
-
- sc = xsc;
-
- vtnet_disable_rx_intr(sc);
- taskqueue_enqueue_fast(sc->vtnet_tq, &sc->vtnet_rx_intr_task);
- return (1);
+ VTNET_UNLOCK(sc);
}
static void
@@ -1800,7 +1747,6 @@ vtnet_tx_offload(struct vtnet_softc *sc, struct mbuf *m,
uint8_t ip_proto, gso_type;
ifp = sc->vtnet_ifp;
- M_ASSERTPKTHDR(m);
ip_offset = sizeof(struct ether_header);
if (m->m_len < ip_offset) {
@@ -1918,7 +1864,7 @@ vtnet_enqueue_txbuf(struct vtnet_softc *sc, struct mbuf **m_head,
sglist_init(&sg, VTNET_MAX_TX_SEGS, segs);
error = sglist_append(&sg, &txhdr->vth_uhdr, sc->vtnet_hdr_size);
KASSERT(error == 0 && sg.sg_nseg == 1,
- ("cannot add header to sglist"));
+ ("%s: cannot add header to sglist error %d", __func__, error));
again:
error = sglist_append_mbuf(&sg, m);
@@ -1955,6 +1901,7 @@ vtnet_encap(struct vtnet_softc *sc, struct mbuf **m_head)
int error;
m = *m_head;
+ M_ASSERTPKTHDR(m);
txhdr = uma_zalloc(vtnet_tx_header_zone, M_NOWAIT | M_ZERO);
if (txhdr == NULL) {
@@ -2077,14 +2024,15 @@ vtnet_tick(void *xsc)
}
static void
-vtnet_tx_intr_task(void *arg, int pending)
+vtnet_tx_vq_intr(void *xsc)
{
struct vtnet_softc *sc;
struct ifnet *ifp;
- sc = arg;
+ sc = xsc;
ifp = sc->vtnet_ifp;
+again:
VTNET_LOCK(sc);
#ifdef DEVICE_POLLING
@@ -2109,26 +2057,12 @@ vtnet_tx_intr_task(void *arg, int pending)
vtnet_disable_tx_intr(sc);
sc->vtnet_stats.tx_task_rescheduled++;
VTNET_UNLOCK(sc);
- taskqueue_enqueue_fast(sc->vtnet_tq, &sc->vtnet_tx_intr_task);
- return;
+ goto again;
}
VTNET_UNLOCK(sc);
}
-static int
-vtnet_tx_vq_intr(void *xsc)
-{
- struct vtnet_softc *sc;
-
- sc = xsc;
-
- vtnet_disable_tx_intr(sc);
- taskqueue_enqueue_fast(sc->vtnet_tq, &sc->vtnet_tx_intr_task);
-
- return (1);
-}
-
static void
vtnet_stop(struct vtnet_softc *sc)
{
diff --git a/sys/dev/virtio/network/if_vtnetvar.h b/sys/dev/virtio/network/if_vtnetvar.h
index 184870e..d870436 100644
--- a/sys/dev/virtio/network/if_vtnetvar.h
+++ b/sys/dev/virtio/network/if_vtnetvar.h
@@ -1,5 +1,5 @@
/*-
- * Copyright (c) 2011, Bryan Venteicher <bryanv@daemoninthecloset.org>
+ * Copyright (c) 2011, Bryan Venteicher <bryanv@FreeBSD.org>
* All rights reserved.
*
* Redistribution and use in source and binary forms, with or without
@@ -79,11 +79,6 @@ struct vtnet_softc {
int vtnet_watchdog_timer;
uint64_t vtnet_features;
- struct taskqueue *vtnet_tq;
- struct task vtnet_rx_intr_task;
- struct task vtnet_tx_intr_task;
- struct task vtnet_cfgchg_task;
-
struct vtnet_statistics vtnet_stats;
struct callout vtnet_tick_ch;
diff --git a/sys/dev/virtio/pci/virtio_pci.c b/sys/dev/virtio/pci/virtio_pci.c
index 917ca84..b3df3d9 100644
--- a/sys/dev/virtio/pci/virtio_pci.c
+++ b/sys/dev/virtio/pci/virtio_pci.c
@@ -1,5 +1,5 @@
/*-
- * Copyright (c) 2011, Bryan Venteicher <bryanv@daemoninthecloset.org>
+ * Copyright (c) 2011, Bryan Venteicher <bryanv@FreeBSD.org>
* All rights reserved.
*
* Redistribution and use in source and binary forms, with or without
@@ -51,6 +51,17 @@ __FBSDID("$FreeBSD$");
#include "virtio_bus_if.h"
#include "virtio_if.h"
+struct vtpci_interrupt {
+ struct resource *vti_irq;
+ int vti_rid;
+ void *vti_handler;
+};
+
+struct vtpci_virtqueue {
+ struct virtqueue *vtv_vq;
+ int vtv_no_intr;
+};
+
struct vtpci_softc {
device_t vtpci_dev;
struct resource *vtpci_res;
@@ -69,40 +80,22 @@ struct vtpci_softc {
device_t vtpci_child_dev;
struct virtio_feature_desc *vtpci_child_feat_desc;
- /*
- * Ideally, each virtqueue that the driver provides a callback for
- * will receive its own MSIX vector. If there are not sufficient
- * vectors available, we will then attempt to have all the VQs
- * share one vector. Note that when using MSIX, the configuration
- * changed notifications must be on their own vector.
- *
- * If MSIX is not available, we will attempt to have the whole
- * device share one MSI vector, and then, finally, one legacy
- * interrupt.
- */
int vtpci_nvqs;
- struct vtpci_virtqueue {
- struct virtqueue *vq;
- /* Device did not provide a callback for this virtqueue. */
- int no_intr;
- /* Index into vtpci_intr_res[] below. Unused, then -1. */
- int ires_idx;
- } vtpci_vqx[VIRTIO_MAX_VIRTQUEUES];
+ struct vtpci_virtqueue *vtpci_vqs;
/*
- * When using MSIX interrupts, the first element of vtpci_intr_res[]
- * is always the configuration changed notifications. The remaining
- * element(s) are used for the virtqueues.
+ * Ideally, each virtqueue that the driver provides a callback for will
+ * receive its own MSIX vector. If there are not sufficient vectors
+ * available, then attempt to have all the VQs share one vector. For
+ * MSIX, the configuration changed notifications must be on their own
+ * vector.
*
- * With MSI and legacy interrupts, only the first element of
- * vtpci_intr_res[] is used.
+ * If MSIX is not available, we will attempt to have the whole device
+ * share one MSI vector, and then, finally, one legacy interrupt.
*/
- int vtpci_nintr_res;
- struct vtpci_intr_resource {
- struct resource *irq;
- int rid;
- void *intrhand;
- } vtpci_intr_res[1 + VIRTIO_MAX_VIRTQUEUES];
+ struct vtpci_interrupt vtpci_device_interrupt;
+ struct vtpci_interrupt *vtpci_msix_vq_interrupts;
+ int vtpci_nmsix_resources;
};
static int vtpci_probe(device_t);
@@ -134,36 +127,45 @@ static void vtpci_describe_features(struct vtpci_softc *, const char *,
uint64_t);
static void vtpci_probe_and_attach_child(struct vtpci_softc *);
-static int vtpci_alloc_msix(struct vtpci_softc *, int);
-static int vtpci_alloc_msi(struct vtpci_softc *);
-static int vtpci_alloc_intr_msix_pervq(struct vtpci_softc *);
-static int vtpci_alloc_intr_msix_shared(struct vtpci_softc *);
-static int vtpci_alloc_intr_msi(struct vtpci_softc *);
-static int vtpci_alloc_intr_legacy(struct vtpci_softc *);
+static int vtpci_alloc_msix(struct vtpci_softc *, int);
+static int vtpci_alloc_msi(struct vtpci_softc *);
+static int vtpci_alloc_intr_msix_pervq(struct vtpci_softc *);
+static int vtpci_alloc_intr_msix_shared(struct vtpci_softc *);
+static int vtpci_alloc_intr_msi(struct vtpci_softc *);
+static int vtpci_alloc_intr_legacy(struct vtpci_softc *);
+static int vtpci_alloc_interrupt(struct vtpci_softc *, int, int,
+ struct vtpci_interrupt *);
static int vtpci_alloc_intr_resources(struct vtpci_softc *);
-static int vtpci_setup_legacy_interrupt(struct vtpci_softc *,
+static int vtpci_setup_legacy_interrupt(struct vtpci_softc *,
+ enum intr_type);
+static int vtpci_setup_pervq_msix_interrupts(struct vtpci_softc *,
enum intr_type);
-static int vtpci_setup_msix_interrupts(struct vtpci_softc *,
+static int vtpci_setup_msix_interrupts(struct vtpci_softc *,
enum intr_type);
-static int vtpci_setup_interrupts(struct vtpci_softc *, enum intr_type);
+static int vtpci_setup_interrupts(struct vtpci_softc *, enum intr_type);
-static int vtpci_register_msix_vector(struct vtpci_softc *, int, int);
-static int vtpci_set_host_msix_vectors(struct vtpci_softc *);
-static int vtpci_reinit_virtqueue(struct vtpci_softc *, int);
+static int vtpci_register_msix_vector(struct vtpci_softc *, int,
+ struct vtpci_interrupt *);
+static int vtpci_set_host_msix_vectors(struct vtpci_softc *);
+static int vtpci_reinit_virtqueue(struct vtpci_softc *, int);
+static void vtpci_free_interrupt(struct vtpci_softc *,
+ struct vtpci_interrupt *);
static void vtpci_free_interrupts(struct vtpci_softc *);
static void vtpci_free_virtqueues(struct vtpci_softc *);
-static void vtpci_cleanup_setup_intr_attempt(struct vtpci_softc *);
static void vtpci_release_child_resources(struct vtpci_softc *);
+static void vtpci_cleanup_setup_intr_attempt(struct vtpci_softc *);
static void vtpci_reset(struct vtpci_softc *);
static void vtpci_select_virtqueue(struct vtpci_softc *, int);
-static int vtpci_legacy_intr(void *);
-static int vtpci_vq_shared_intr(void *);
-static int vtpci_vq_intr(void *);
-static int vtpci_config_intr(void *);
+static void vtpci_legacy_intr(void *);
+static int vtpci_vq_shared_intr_filter(void *);
+static void vtpci_vq_shared_intr(void *);
+static int vtpci_vq_intr_filter(void *);
+static void vtpci_vq_intr(void *);
+static void vtpci_config_intr(void *);
#define vtpci_setup_msi_interrupt vtpci_setup_legacy_interrupt
@@ -478,18 +480,19 @@ vtpci_alloc_virtqueues(device_t dev, int flags, int nvqs,
uint16_t size;
sc = device_get_softc(dev);
- error = 0;
if (sc->vtpci_nvqs != 0)
return (EALREADY);
- if (nvqs <= 0 || nvqs > VIRTIO_MAX_VIRTQUEUES)
+ if (nvqs <= 0)
return (EINVAL);
- if (flags & VIRTIO_ALLOC_VQS_DISABLE_MSIX)
- sc->vtpci_flags |= VTPCI_FLAG_NO_MSIX;
+ sc->vtpci_vqs = malloc(nvqs * sizeof(struct vtpci_virtqueue),
+ M_DEVBUF, M_NOWAIT | M_ZERO);
+ if (sc->vtpci_vqs == NULL)
+ return (ENOMEM);
for (idx = 0; idx < nvqs; idx++) {
- vqx = &sc->vtpci_vqx[idx];
+ vqx = &sc->vtpci_vqs[idx];
info = &vq_info[idx];
vtpci_select_virtqueue(sc, idx);
@@ -506,12 +509,15 @@ vtpci_alloc_virtqueues(device_t dev, int flags, int nvqs,
vtpci_write_config_4(sc, VIRTIO_PCI_QUEUE_PFN,
virtqueue_paddr(vq) >> VIRTIO_PCI_QUEUE_ADDR_SHIFT);
- vqx->vq = *info->vqai_vq = vq;
- vqx->no_intr = info->vqai_intr == NULL;
+ vqx->vtv_vq = *info->vqai_vq = vq;
+ vqx->vtv_no_intr = info->vqai_intr == NULL;
sc->vtpci_nvqs++;
}
+ if (error)
+ vtpci_free_virtqueues(sc);
+
return (error);
}
@@ -772,7 +778,7 @@ vtpci_alloc_msix(struct vtpci_softc *sc, int nvectors)
cnt = required;
if (pci_alloc_msix(dev, &cnt) == 0 && cnt >= required) {
- sc->vtpci_nintr_res = required;
+ sc->vtpci_nmsix_resources = required;
return (0);
}
@@ -795,10 +801,8 @@ vtpci_alloc_msi(struct vtpci_softc *sc)
return (1);
cnt = required;
- if (pci_alloc_msi(dev, &cnt) == 0 && cnt >= required) {
- sc->vtpci_nintr_res = required;
+ if (pci_alloc_msi(dev, &cnt) == 0 && cnt >= required)
return (0);
- }
pci_release_msi(dev);
@@ -815,7 +819,7 @@ vtpci_alloc_intr_msix_pervq(struct vtpci_softc *sc)
return (ENOTSUP);
for (nvectors = 0, i = 0; i < sc->vtpci_nvqs; i++) {
- if (sc->vtpci_vqx[i].no_intr == 0)
+ if (sc->vtpci_vqs[i].vtv_no_intr == 0)
nvectors++;
}
@@ -869,54 +873,62 @@ vtpci_alloc_intr_legacy(struct vtpci_softc *sc)
{
sc->vtpci_flags |= VTPCI_FLAG_LEGACY;
- sc->vtpci_nintr_res = 1;
return (0);
}
static int
-vtpci_alloc_intr_resources(struct vtpci_softc *sc)
+vtpci_alloc_interrupt(struct vtpci_softc *sc, int rid, int flags,
+ struct vtpci_interrupt *intr)
{
- device_t dev;
struct resource *irq;
- struct vtpci_virtqueue *vqx;
- int i, rid, flags, res_idx;
- dev = sc->vtpci_dev;
+ irq = bus_alloc_resource_any(sc->vtpci_dev, SYS_RES_IRQ, &rid, flags);
+ if (irq == NULL)
+ return (ENXIO);
- if (sc->vtpci_flags & VTPCI_FLAG_LEGACY) {
- rid = 0;
- flags = RF_ACTIVE | RF_SHAREABLE;
- } else {
- rid = 1;
- flags = RF_ACTIVE;
- }
+ intr->vti_irq = irq;
+ intr->vti_rid = rid;
- for (i = 0; i < sc->vtpci_nintr_res; i++) {
- irq = bus_alloc_resource_any(dev, SYS_RES_IRQ, &rid, flags);
- if (irq == NULL)
- return (ENXIO);
+ return (0);
+}
- sc->vtpci_intr_res[i].irq = irq;
- sc->vtpci_intr_res[i].rid = rid++;
- }
+static int
+vtpci_alloc_intr_resources(struct vtpci_softc *sc)
+{
+ struct vtpci_interrupt *intr;
+ int i, rid, flags, nvq_intrs, error;
+
+ rid = 0;
+ flags = RF_ACTIVE;
+
+ if (sc->vtpci_flags & VTPCI_FLAG_LEGACY)
+ flags |= RF_SHAREABLE;
+ else
+ rid = 1;
/*
- * Map the virtqueue into the correct index in vq_intr_res[]. The
- * first index is reserved for configuration changed notifications.
+ * For legacy and MSI interrupts, this single resource handles all
+ * interrupts. For MSIX, this resource is used for the configuration
+ * changed interrupt.
*/
- for (i = 0, res_idx = 1; i < sc->vtpci_nvqs; i++) {
- vqx = &sc->vtpci_vqx[i];
-
- if (sc->vtpci_flags & VTPCI_FLAG_MSIX) {
- if (vqx->no_intr != 0)
- vqx->ires_idx = -1;
- else if (sc->vtpci_flags & VTPCI_FLAG_SHARED_MSIX)
- vqx->ires_idx = res_idx;
- else
- vqx->ires_idx = res_idx++;
- } else
- vqx->ires_idx = -1;
+ intr = &sc->vtpci_device_interrupt;
+ error = vtpci_alloc_interrupt(sc, rid, flags, intr);
+ if (error || sc->vtpci_flags & (VTPCI_FLAG_LEGACY | VTPCI_FLAG_MSI))
+ return (error);
+
+ /* Subtract one for the configuration changed interrupt. */
+ nvq_intrs = sc->vtpci_nmsix_resources - 1;
+
+ intr = sc->vtpci_msix_vq_interrupts = malloc(nvq_intrs *
+ sizeof(struct vtpci_interrupt), M_DEVBUF, M_NOWAIT | M_ZERO);
+ if (sc->vtpci_msix_vq_interrupts == NULL)
+ return (ENOMEM);
+
+ for (i = 0, rid++; i < nvq_intrs; i++, rid++, intr++) {
+ error = vtpci_alloc_interrupt(sc, rid, flags, intr);
+ if (error)
+ return (error);
}
return (0);
@@ -925,67 +937,67 @@ vtpci_alloc_intr_resources(struct vtpci_softc *sc)
static int
vtpci_setup_legacy_interrupt(struct vtpci_softc *sc, enum intr_type type)
{
- device_t dev;
- struct vtpci_intr_resource *ires;
+ struct vtpci_interrupt *intr;
int error;
- dev = sc->vtpci_dev;
-
- ires = &sc->vtpci_intr_res[0];
- error = bus_setup_intr(dev, ires->irq, type, vtpci_legacy_intr, NULL,
- sc, &ires->intrhand);
+ intr = &sc->vtpci_device_interrupt;
+ error = bus_setup_intr(sc->vtpci_dev, intr->vti_irq, type, NULL,
+ vtpci_legacy_intr, sc, &intr->vti_handler);
return (error);
}
static int
-vtpci_setup_msix_interrupts(struct vtpci_softc *sc, enum intr_type type)
+vtpci_setup_pervq_msix_interrupts(struct vtpci_softc *sc, enum intr_type type)
{
- device_t dev;
- struct vtpci_intr_resource *ires;
struct vtpci_virtqueue *vqx;
+ struct vtpci_interrupt *intr;
int i, error;
- dev = sc->vtpci_dev;
+ intr = sc->vtpci_msix_vq_interrupts;
- /*
- * The first resource is used for configuration changed interrupts.
- */
- ires = &sc->vtpci_intr_res[0];
- error = bus_setup_intr(dev, ires->irq, type, vtpci_config_intr,
- NULL, sc, &ires->intrhand);
- if (error)
- return (error);
+ for (i = 0; i < sc->vtpci_nvqs; i++) {
+ vqx = &sc->vtpci_vqs[i];
- if (sc->vtpci_flags & VTPCI_FLAG_SHARED_MSIX) {
- ires = &sc->vtpci_intr_res[1];
+ if (vqx->vtv_no_intr)
+ continue;
- error = bus_setup_intr(dev, ires->irq, type,
- vtpci_vq_shared_intr, NULL, sc, &ires->intrhand);
+ error = bus_setup_intr(sc->vtpci_dev, intr->vti_irq, type,
+ vtpci_vq_intr_filter, vtpci_vq_intr, vqx->vtv_vq,
+ &intr->vti_handler);
if (error)
return (error);
- } else {
- /*
- * Each remaining resource is assigned to a specific virtqueue.
- */
- for (i = 0; i < sc->vtpci_nvqs; i++) {
- vqx = &sc->vtpci_vqx[i];
- if (vqx->ires_idx < 1)
- continue;
-
- ires = &sc->vtpci_intr_res[vqx->ires_idx];
- error = bus_setup_intr(dev, ires->irq, type,
- vtpci_vq_intr, NULL, vqx->vq, &ires->intrhand);
- if (error)
- return (error);
- }
+
+ intr++;
}
- error = vtpci_set_host_msix_vectors(sc);
+ return (0);
+}
+
+static int
+vtpci_setup_msix_interrupts(struct vtpci_softc *sc, enum intr_type type)
+{
+ device_t dev;
+ struct vtpci_interrupt *intr;
+ int error;
+
+ dev = sc->vtpci_dev;
+ intr = &sc->vtpci_device_interrupt;
+
+ error = bus_setup_intr(dev, intr->vti_irq, type, NULL,
+ vtpci_config_intr, sc, &intr->vti_handler);
if (error)
return (error);
- return (0);
+ if (sc->vtpci_flags & VTPCI_FLAG_SHARED_MSIX) {
+ intr = sc->vtpci_msix_vq_interrupts;
+ error = bus_setup_intr(dev, intr->vti_irq, type,
+ vtpci_vq_shared_intr_filter, vtpci_vq_shared_intr, sc,
+ &intr->vti_handler);
+ } else
+ error = vtpci_setup_pervq_msix_interrupts(sc, type);
+
+ return (error ? error : vtpci_set_host_msix_vectors(sc));
}
static int
@@ -995,7 +1007,7 @@ vtpci_setup_interrupts(struct vtpci_softc *sc, enum intr_type type)
type |= INTR_MPSAFE;
KASSERT(sc->vtpci_flags & VTPCI_FLAG_ITYPE_MASK,
- ("no interrupt type selected: %#x", sc->vtpci_flags));
+ ("%s: no interrupt type selected %#x", __func__, sc->vtpci_flags));
error = vtpci_alloc_intr_resources(sc);
if (error)
@@ -1012,34 +1024,24 @@ vtpci_setup_interrupts(struct vtpci_softc *sc, enum intr_type type)
}
static int
-vtpci_register_msix_vector(struct vtpci_softc *sc, int offset, int res_idx)
+vtpci_register_msix_vector(struct vtpci_softc *sc, int offset,
+ struct vtpci_interrupt *intr)
{
device_t dev;
- uint16_t vector, rdvector;
+ uint16_t vector;
dev = sc->vtpci_dev;
- if (res_idx != -1) {
+ if (intr != NULL) {
/* Map from guest rid to host vector. */
- vector = sc->vtpci_intr_res[res_idx].rid - 1;
+ vector = intr->vti_rid - 1;
} else
vector = VIRTIO_MSI_NO_VECTOR;
- /*
- * Assert the first resource is always used for the configuration
- * changed interrupts.
- */
- if (res_idx == 0) {
- KASSERT(vector == 0 && offset == VIRTIO_MSI_CONFIG_VECTOR,
- ("bad first res use vector:%d offset:%d", vector, offset));
- } else
- KASSERT(offset == VIRTIO_MSI_QUEUE_VECTOR, ("bad offset"));
-
vtpci_write_config_2(sc, offset, vector);
/* Read vector to determine if the host had sufficient resources. */
- rdvector = vtpci_read_config_2(sc, offset);
- if (rdvector != vector) {
+ if (vtpci_read_config_2(sc, offset) != vector) {
device_printf(dev,
"insufficient host resources for MSIX interrupts\n");
return (ENODEV);
@@ -1051,24 +1053,40 @@ vtpci_register_msix_vector(struct vtpci_softc *sc, int offset, int res_idx)
static int
vtpci_set_host_msix_vectors(struct vtpci_softc *sc)
{
- struct vtpci_virtqueue *vqx;
- int idx, error;
+ struct vtpci_interrupt *intr, *tintr;
+ int idx, offset, error;
+
+ intr = &sc->vtpci_device_interrupt;
+ offset = VIRTIO_MSI_CONFIG_VECTOR;
- error = vtpci_register_msix_vector(sc, VIRTIO_MSI_CONFIG_VECTOR, 0);
+ error = vtpci_register_msix_vector(sc, offset, intr);
if (error)
return (error);
- for (idx = 0; idx < sc->vtpci_nvqs; idx++) {
- vqx = &sc->vtpci_vqx[idx];
+ intr = sc->vtpci_msix_vq_interrupts;
+ offset = VIRTIO_MSI_QUEUE_VECTOR;
+ for (idx = 0; idx < sc->vtpci_nvqs; idx++) {
vtpci_select_virtqueue(sc, idx);
- error = vtpci_register_msix_vector(sc, VIRTIO_MSI_QUEUE_VECTOR,
- vqx->ires_idx);
+
+ if (sc->vtpci_vqs[idx].vtv_no_intr)
+ tintr = NULL;
+ else
+ tintr = intr;
+
+ error = vtpci_register_msix_vector(sc, offset, tintr);
if (error)
- return (error);
+ break;
+
+ /*
+ * For shared MSIX, all the virtqueues share the first
+ * interrupt.
+ */
+ if ((sc->vtpci_flags & VTPCI_FLAG_SHARED_MSIX) == 0)
+ intr++;
}
- return (0);
+ return (error);
}
static int
@@ -1079,10 +1097,10 @@ vtpci_reinit_virtqueue(struct vtpci_softc *sc, int idx)
int error;
uint16_t size;
- vqx = &sc->vtpci_vqx[idx];
- vq = vqx->vq;
+ vqx = &sc->vtpci_vqs[idx];
+ vq = vqx->vtv_vq;
- KASSERT(vq != NULL, ("vq %d not allocated", idx));
+ KASSERT(vq != NULL, ("%s: vq %d not allocated", __func__, idx));
vtpci_select_virtqueue(sc, idx);
size = vtpci_read_config_2(sc, VIRTIO_PCI_QUEUE_NUM);
@@ -1098,35 +1116,50 @@ vtpci_reinit_virtqueue(struct vtpci_softc *sc, int idx)
}
static void
-vtpci_free_interrupts(struct vtpci_softc *sc)
+vtpci_free_interrupt(struct vtpci_softc *sc, struct vtpci_interrupt *intr)
{
device_t dev;
- struct vtpci_intr_resource *ires;
- int i;
dev = sc->vtpci_dev;
- for (i = 0; i < sc->vtpci_nintr_res; i++) {
- ires = &sc->vtpci_intr_res[i];
+ if (intr->vti_handler != NULL) {
+ bus_teardown_intr(dev, intr->vti_irq, intr->vti_handler);
+ intr->vti_handler = NULL;
+ }
- if (ires->intrhand != NULL) {
- bus_teardown_intr(dev, ires->irq, ires->intrhand);
- ires->intrhand = NULL;
- }
+ if (intr->vti_irq != NULL) {
+ bus_release_resource(dev, SYS_RES_IRQ, intr->vti_rid,
+ intr->vti_irq);
+ intr->vti_irq = NULL;
+ intr->vti_rid = -1;
+ }
+}
- if (ires->irq != NULL) {
- bus_release_resource(dev, SYS_RES_IRQ, ires->rid,
- ires->irq);
- ires->irq = NULL;
- }
+static void
+vtpci_free_interrupts(struct vtpci_softc *sc)
+{
+ struct vtpci_interrupt *intr;
+ int i, nvq_intrs;
- ires->rid = -1;
+ vtpci_free_interrupt(sc, &sc->vtpci_device_interrupt);
+
+ if (sc->vtpci_nmsix_resources != 0) {
+ nvq_intrs = sc->vtpci_nmsix_resources - 1;
+ sc->vtpci_nmsix_resources = 0;
+
+ intr = sc->vtpci_msix_vq_interrupts;
+ if (intr != NULL) {
+ for (i = 0; i < nvq_intrs; i++, intr++)
+ vtpci_free_interrupt(sc, intr);
+
+ free(sc->vtpci_msix_vq_interrupts, M_DEVBUF);
+ sc->vtpci_msix_vq_interrupts = NULL;
+ }
}
if (sc->vtpci_flags & (VTPCI_FLAG_MSI | VTPCI_FLAG_MSIX))
- pci_release_msi(dev);
+ pci_release_msi(sc->vtpci_dev);
- sc->vtpci_nintr_res = 0;
sc->vtpci_flags &= ~VTPCI_FLAG_ITYPE_MASK;
}
@@ -1134,19 +1167,32 @@ static void
vtpci_free_virtqueues(struct vtpci_softc *sc)
{
struct vtpci_virtqueue *vqx;
- int i;
+ int idx;
- for (i = 0; i < sc->vtpci_nvqs; i++) {
- vqx = &sc->vtpci_vqx[i];
+ for (idx = 0; idx < sc->vtpci_nvqs; idx++) {
+ vqx = &sc->vtpci_vqs[idx];
+
+ vtpci_select_virtqueue(sc, idx);
+ vtpci_write_config_4(sc, VIRTIO_PCI_QUEUE_PFN, 0);
- virtqueue_free(vqx->vq);
- vqx->vq = NULL;
+ virtqueue_free(vqx->vtv_vq);
+ vqx->vtv_vq = NULL;
}
+ free(sc->vtpci_vqs, M_DEVBUF);
+ sc->vtpci_vqs = NULL;
sc->vtpci_nvqs = 0;
}
static void
+vtpci_release_child_resources(struct vtpci_softc *sc)
+{
+
+ vtpci_free_interrupts(sc);
+ vtpci_free_virtqueues(sc);
+}
+
+static void
vtpci_cleanup_setup_intr_attempt(struct vtpci_softc *sc)
{
int idx;
@@ -1166,14 +1212,6 @@ vtpci_cleanup_setup_intr_attempt(struct vtpci_softc *sc)
}
static void
-vtpci_release_child_resources(struct vtpci_softc *sc)
-{
-
- vtpci_free_interrupts(sc);
- vtpci_free_virtqueues(sc);
-}
-
-static void
vtpci_reset(struct vtpci_softc *sc)
{
@@ -1191,7 +1229,7 @@ vtpci_select_virtqueue(struct vtpci_softc *sc, int idx)
vtpci_write_config_2(sc, VIRTIO_PCI_QUEUE_SEL, idx);
}
-static int
+static void
vtpci_legacy_intr(void *xsc)
{
struct vtpci_softc *sc;
@@ -1200,7 +1238,7 @@ vtpci_legacy_intr(void *xsc)
uint8_t isr;
sc = xsc;
- vqx = &sc->vtpci_vqx[0];
+ vqx = &sc->vtpci_vqs[0];
/* Reading the ISR also clears it. */
isr = vtpci_read_config_1(sc, VIRTIO_PCI_ISR);
@@ -1208,15 +1246,16 @@ vtpci_legacy_intr(void *xsc)
if (isr & VIRTIO_PCI_ISR_CONFIG)
vtpci_config_intr(sc);
- if (isr & VIRTIO_PCI_ISR_INTR)
- for (i = 0; i < sc->vtpci_nvqs; i++, vqx++)
- virtqueue_intr(vqx->vq);
-
- return (isr ? FILTER_HANDLED : FILTER_STRAY);
+ if (isr & VIRTIO_PCI_ISR_INTR) {
+ for (i = 0; i < sc->vtpci_nvqs; i++, vqx++) {
+ if (vqx->vtv_no_intr == 0)
+ virtqueue_intr(vqx->vtv_vq);
+ }
+ }
}
static int
-vtpci_vq_shared_intr(void *xsc)
+vtpci_vq_shared_intr_filter(void *xsc)
{
struct vtpci_softc *sc;
struct vtpci_virtqueue *vqx;
@@ -1224,39 +1263,62 @@ vtpci_vq_shared_intr(void *xsc)
rc = 0;
sc = xsc;
- vqx = &sc->vtpci_vqx[0];
+ vqx = &sc->vtpci_vqs[0];
- for (i = 0; i < sc->vtpci_nvqs; i++, vqx++)
- rc |= virtqueue_intr(vqx->vq);
+ for (i = 0; i < sc->vtpci_nvqs; i++, vqx++) {
+ if (vqx->vtv_no_intr == 0)
+ rc |= virtqueue_intr_filter(vqx->vtv_vq);
+ }
- return (rc ? FILTER_HANDLED : FILTER_STRAY);
+ return (rc ? FILTER_SCHEDULE_THREAD : FILTER_STRAY);
+}
+
+static void
+vtpci_vq_shared_intr(void *xsc)
+{
+ struct vtpci_softc *sc;
+ struct vtpci_virtqueue *vqx;
+ int i;
+
+ sc = xsc;
+ vqx = &sc->vtpci_vqs[0];
+
+ for (i = 0; i < sc->vtpci_nvqs; i++, vqx++) {
+ if (vqx->vtv_no_intr == 0)
+ virtqueue_intr(vqx->vtv_vq);
+ }
}
static int
-vtpci_vq_intr(void *xvq)
+vtpci_vq_intr_filter(void *xvq)
{
struct virtqueue *vq;
int rc;
vq = xvq;
- rc = virtqueue_intr(vq);
+ rc = virtqueue_intr_filter(vq);
- return (rc ? FILTER_HANDLED : FILTER_STRAY);
+ return (rc ? FILTER_SCHEDULE_THREAD : FILTER_STRAY);
}
-static int
+static void
+vtpci_vq_intr(void *xvq)
+{
+ struct virtqueue *vq;
+
+ vq = xvq;
+ virtqueue_intr(vq);
+}
+
+static void
vtpci_config_intr(void *xsc)
{
struct vtpci_softc *sc;
device_t child;
- int rc;
- rc = 0;
sc = xsc;
child = sc->vtpci_child_dev;
if (child != NULL)
- rc = VIRTIO_CONFIG_CHANGE(child);
-
- return (rc ? FILTER_HANDLED : FILTER_STRAY);
+ VIRTIO_CONFIG_CHANGE(child);
}
diff --git a/sys/dev/virtio/scsi/virtio_scsi.c b/sys/dev/virtio/scsi/virtio_scsi.c
index 52f1581..e5f922b 100644
--- a/sys/dev/virtio/scsi/virtio_scsi.c
+++ b/sys/dev/virtio/scsi/virtio_scsi.c
@@ -1,5 +1,5 @@
/*-
- * Copyright (c) 2012, Bryan Venteicher <bryanv@daemoninthecloset.org>
+ * Copyright (c) 2012, Bryan Venteicher <bryanv@FreeBSD.org>
* All rights reserved.
*
* Redistribution and use in source and binary forms, with or without
@@ -40,7 +40,6 @@ __FBSDID("$FreeBSD$");
#include <sys/lock.h>
#include <sys/mutex.h>
#include <sys/callout.h>
-#include <sys/taskqueue.h>
#include <sys/queue.h>
#include <sys/sbuf.h>
@@ -172,13 +171,10 @@ static struct vtscsi_request * vtscsi_dequeue_request(struct vtscsi_softc *);
static void vtscsi_complete_request(struct vtscsi_request *);
static void vtscsi_complete_vq(struct vtscsi_softc *, struct virtqueue *);
-static void vtscsi_control_vq_task(void *, int);
-static void vtscsi_event_vq_task(void *, int);
-static void vtscsi_request_vq_task(void *, int);
-static int vtscsi_control_vq_intr(void *);
-static int vtscsi_event_vq_intr(void *);
-static int vtscsi_request_vq_intr(void *);
+static void vtscsi_control_vq_intr(void *);
+static void vtscsi_event_vq_intr(void *);
+static void vtscsi_request_vq_intr(void *);
static void vtscsi_disable_vqs_intr(struct vtscsi_softc *);
static void vtscsi_enable_vqs_intr(struct vtscsi_softc *);
@@ -333,30 +329,12 @@ vtscsi_attach(device_t dev)
goto fail;
}
- TASK_INIT(&sc->vtscsi_control_intr_task, 0,
- vtscsi_control_vq_task, sc);
- TASK_INIT(&sc->vtscsi_event_intr_task, 0,
- vtscsi_event_vq_task, sc);
- TASK_INIT(&sc->vtscsi_request_intr_task, 0,
- vtscsi_request_vq_task, sc);
-
- sc->vtscsi_tq = taskqueue_create_fast("vtscsi_taskq", M_NOWAIT,
- taskqueue_thread_enqueue, &sc->vtscsi_tq);
- if (sc->vtscsi_tq == NULL) {
- error = ENOMEM;
- device_printf(dev, "cannot allocate taskqueue\n");
- goto fail;
- }
-
error = virtio_setup_intr(dev, INTR_TYPE_CAM);
if (error) {
device_printf(dev, "cannot setup virtqueue interrupts\n");
goto fail;
}
- taskqueue_start_threads(&sc->vtscsi_tq, 1, PI_DISK, "%s taskq",
- device_get_nameunit(dev));
-
vtscsi_enable_vqs_intr(sc);
/*
@@ -389,14 +367,6 @@ vtscsi_detach(device_t dev)
vtscsi_stop(sc);
VTSCSI_UNLOCK(sc);
- if (sc->vtscsi_tq != NULL) {
- taskqueue_drain(sc->vtscsi_tq, &sc->vtscsi_control_intr_task);
- taskqueue_drain(sc->vtscsi_tq, &sc->vtscsi_event_intr_task);
- taskqueue_drain(sc->vtscsi_tq, &sc->vtscsi_request_intr_task);
- taskqueue_free(sc->vtscsi_tq);
- sc->vtscsi_tq = NULL;
- }
-
vtscsi_complete_vqs(sc);
vtscsi_drain_vqs(sc);
@@ -572,19 +542,14 @@ vtscsi_register_cam(struct vtscsi_softc *sc)
goto fail;
}
- VTSCSI_UNLOCK(sc);
-
- /*
- * The async register apparently needs to be done without
- * the lock held, otherwise it can recurse on the lock.
- */
if (vtscsi_register_async(sc) != CAM_REQ_CMP) {
error = EIO;
device_printf(dev, "cannot register async callback\n");
- VTSCSI_LOCK(sc);
goto fail;
}
+ VTSCSI_UNLOCK(sc);
+
return (0);
fail:
@@ -652,8 +617,6 @@ vtscsi_register_async(struct vtscsi_softc *sc)
{
struct ccb_setasync csa;
- VTSCSI_LOCK_NOTOWNED(sc);
-
xpt_setup_ccb(&csa.ccb_h, sc->vtscsi_path, 5);
csa.ccb_h.func_code = XPT_SASYNC_CB;
csa.event_enable = AC_LOST_DEVICE | AC_FOUND_DEVICE;
@@ -1268,7 +1231,7 @@ vtscsi_scsi_cmd_cam_status(struct virtio_scsi_cmd_resp *cmd_resp)
status = CAM_REQ_ABORTED;
break;
case VIRTIO_SCSI_S_BAD_TARGET:
- status = CAM_TID_INVALID;
+ status = CAM_SEL_TIMEOUT;
break;
case VIRTIO_SCSI_S_RESET:
status = CAM_SCSI_BUS_RESET;
@@ -2152,14 +2115,15 @@ vtscsi_complete_vq(struct vtscsi_softc *sc, struct virtqueue *vq)
}
static void
-vtscsi_control_vq_task(void *arg, int pending)
+vtscsi_control_vq_intr(void *xsc)
{
struct vtscsi_softc *sc;
struct virtqueue *vq;
- sc = arg;
+ sc = xsc;
vq = sc->vtscsi_control_vq;
+again:
VTSCSI_LOCK(sc);
vtscsi_complete_vq(sc, sc->vtscsi_control_vq);
@@ -2167,24 +2131,23 @@ vtscsi_control_vq_task(void *arg, int pending)
if (virtqueue_enable_intr(vq) != 0) {
virtqueue_disable_intr(vq);
VTSCSI_UNLOCK(sc);
- taskqueue_enqueue_fast(sc->vtscsi_tq,
- &sc->vtscsi_control_intr_task);
- return;
+ goto again;
}
VTSCSI_UNLOCK(sc);
}
static void
-vtscsi_event_vq_task(void *arg, int pending)
+vtscsi_event_vq_intr(void *xsc)
{
struct vtscsi_softc *sc;
struct virtqueue *vq;
struct virtio_scsi_event *event;
- sc = arg;
+ sc = xsc;
vq = sc->vtscsi_event_vq;
+again:
VTSCSI_LOCK(sc);
while ((event = virtqueue_dequeue(vq, NULL)) != NULL)
@@ -2193,23 +2156,22 @@ vtscsi_event_vq_task(void *arg, int pending)
if (virtqueue_enable_intr(vq) != 0) {
virtqueue_disable_intr(vq);
VTSCSI_UNLOCK(sc);
- taskqueue_enqueue_fast(sc->vtscsi_tq,
- &sc->vtscsi_control_intr_task);
- return;
+ goto again;
}
VTSCSI_UNLOCK(sc);
}
static void
-vtscsi_request_vq_task(void *arg, int pending)
+vtscsi_request_vq_intr(void *xsc)
{
struct vtscsi_softc *sc;
struct virtqueue *vq;
- sc = arg;
+ sc = xsc;
vq = sc->vtscsi_request_vq;
+again:
VTSCSI_LOCK(sc);
vtscsi_complete_vq(sc, sc->vtscsi_request_vq);
@@ -2217,56 +2179,12 @@ vtscsi_request_vq_task(void *arg, int pending)
if (virtqueue_enable_intr(vq) != 0) {
virtqueue_disable_intr(vq);
VTSCSI_UNLOCK(sc);
- taskqueue_enqueue_fast(sc->vtscsi_tq,
- &sc->vtscsi_request_intr_task);
- return;
+ goto again;
}
VTSCSI_UNLOCK(sc);
}
-static int
-vtscsi_control_vq_intr(void *xsc)
-{
- struct vtscsi_softc *sc;
-
- sc = xsc;
-
- virtqueue_disable_intr(sc->vtscsi_control_vq);
- taskqueue_enqueue_fast(sc->vtscsi_tq,
- &sc->vtscsi_control_intr_task);
-
- return (1);
-}
-
-static int
-vtscsi_event_vq_intr(void *xsc)
-{
- struct vtscsi_softc *sc;
-
- sc = xsc;
-
- virtqueue_disable_intr(sc->vtscsi_event_vq);
- taskqueue_enqueue_fast(sc->vtscsi_tq,
- &sc->vtscsi_event_intr_task);
-
- return (1);
-}
-
-static int
-vtscsi_request_vq_intr(void *xsc)
-{
- struct vtscsi_softc *sc;
-
- sc = xsc;
-
- virtqueue_disable_intr(sc->vtscsi_request_vq);
- taskqueue_enqueue_fast(sc->vtscsi_tq,
- &sc->vtscsi_request_intr_task);
-
- return (1);
-}
-
static void
vtscsi_disable_vqs_intr(struct vtscsi_softc *sc)
{
diff --git a/sys/dev/virtio/scsi/virtio_scsivar.h b/sys/dev/virtio/scsi/virtio_scsivar.h
index 7afe32f..f30f667 100644
--- a/sys/dev/virtio/scsi/virtio_scsivar.h
+++ b/sys/dev/virtio/scsi/virtio_scsivar.h
@@ -1,5 +1,5 @@
/*-
- * Copyright (c) 2012, Bryan Venteicher <bryanv@daemoninthecloset.org>
+ * Copyright (c) 2012, Bryan Venteicher <bryanv@FreeBSD.org>
* All rights reserved.
*
* Redistribution and use in source and binary forms, with or without
@@ -62,11 +62,6 @@ struct vtscsi_softc {
struct virtqueue *vtscsi_event_vq;
struct virtqueue *vtscsi_request_vq;
- struct taskqueue *vtscsi_tq;
- struct task vtscsi_control_intr_task;
- struct task vtscsi_event_intr_task;
- struct task vtscsi_request_intr_task;
-
struct cam_sim *vtscsi_sim;
struct cam_path *vtscsi_path;
diff --git a/sys/dev/virtio/virtio.c b/sys/dev/virtio/virtio.c
index 6c6b0bc..ea2b91a 100644
--- a/sys/dev/virtio/virtio.c
+++ b/sys/dev/virtio/virtio.c
@@ -1,5 +1,5 @@
/*-
- * Copyright (c) 2011, Bryan Venteicher <bryanv@daemoninthecloset.org>
+ * Copyright (c) 2011, Bryan Venteicher <bryanv@FreeBSD.org>
* All rights reserved.
*
* Redistribution and use in source and binary forms, with or without
@@ -36,7 +36,6 @@ __FBSDID("$FreeBSD$");
#include <machine/bus.h>
#include <machine/resource.h>
-#include <machine/_inttypes.h>
#include <sys/bus.h>
#include <sys/rman.h>
@@ -49,8 +48,8 @@ static int virtio_modevent(module_t, int, void *);
static const char *virtio_feature_name(uint64_t, struct virtio_feature_desc *);
static struct virtio_ident {
- uint16_t devid;
- char *name;
+ uint16_t devid;
+ const char *name;
} virtio_ident_table[] = {
{ VIRTIO_ID_NETWORK, "Network" },
{ VIRTIO_ID_BLOCK, "Block" },
@@ -87,9 +86,29 @@ virtio_device_name(uint16_t devid)
return (NULL);
}
+static const char *
+virtio_feature_name(uint64_t val, struct virtio_feature_desc *desc)
+{
+ int i, j;
+ struct virtio_feature_desc *descs[2] = { desc,
+ virtio_common_feature_desc };
+
+ for (i = 0; i < 2; i++) {
+ if (descs[i] == NULL)
+ continue;
+
+ for (j = 0; descs[i][j].vfd_val != 0; j++) {
+ if (val == descs[i][j].vfd_val)
+ return (descs[i][j].vfd_str);
+ }
+ }
+
+ return (NULL);
+}
+
void
virtio_describe(device_t dev, const char *msg,
- uint64_t features, struct virtio_feature_desc *feature_desc)
+ uint64_t features, struct virtio_feature_desc *desc)
{
struct sbuf sb;
uint64_t val;
@@ -98,13 +117,12 @@ virtio_describe(device_t dev, const char *msg,
int n;
if ((buf = malloc(512, M_TEMP, M_NOWAIT)) == NULL) {
- device_printf(dev, "%s features: 0x%"PRIx64"\n", msg,
- features);
+ device_printf(dev, "%s features: %#jx\n", msg, (uintmax_t) features);
return;
}
sbuf_new(&sb, buf, 512, SBUF_FIXEDLEN);
- sbuf_printf(&sb, "%s features: 0x%"PRIx64, msg, features);
+ sbuf_printf(&sb, "%s features: %#jx", msg, (uintmax_t) features);
for (n = 0, val = 1ULL << 63; val != 0; val >>= 1) {
/*
@@ -119,15 +137,9 @@ virtio_describe(device_t dev, const char *msg,
else
sbuf_cat(&sb, ",");
- name = NULL;
- if (feature_desc != NULL)
- name = virtio_feature_name(val, feature_desc);
+ name = virtio_feature_name(val, desc);
if (name == NULL)
- name = virtio_feature_name(val,
- virtio_common_feature_desc);
-
- if (name == NULL)
- sbuf_printf(&sb, "0x%"PRIx64, val);
+ sbuf_printf(&sb, "%#jx", (uintmax_t) val);
else
sbuf_cat(&sb, name);
}
@@ -147,18 +159,6 @@ virtio_describe(device_t dev, const char *msg,
free(buf, M_TEMP);
}
-static const char *
-virtio_feature_name(uint64_t val, struct virtio_feature_desc *feature_desc)
-{
- int i;
-
- for (i = 0; feature_desc[i].vfd_val != 0; i++)
- if (val == feature_desc[i].vfd_val)
- return (feature_desc[i].vfd_str);
-
- return (NULL);
-}
-
/*
* VirtIO bus method wrappers.
*/
@@ -251,13 +251,12 @@ virtio_modevent(module_t mod, int type, void *unused)
{
int error;
- error = 0;
-
switch (type) {
case MOD_LOAD:
case MOD_QUIESCE:
case MOD_UNLOAD:
case MOD_SHUTDOWN:
+ error = 0;
break;
default:
error = EOPNOTSUPP;
diff --git a/sys/dev/virtio/virtio.h b/sys/dev/virtio/virtio.h
index 4d069dd..b1334f8 100644
--- a/sys/dev/virtio/virtio.h
+++ b/sys/dev/virtio/virtio.h
@@ -71,11 +71,6 @@ struct vq_alloc_info;
#define VIRTIO_TRANSPORT_F_END 32
/*
- * Maximum number of virtqueues per device.
- */
-#define VIRTIO_MAX_VIRTQUEUES 8
-
-/*
* Each virtqueue indirect descriptor list must be physically contiguous.
* To allow us to malloc(9) each list individually, limit the number
* supported to what will fit in one page. With 4KB pages, this is a limit
@@ -99,7 +94,7 @@ struct vq_alloc_info;
struct virtio_feature_desc {
uint64_t vfd_val;
- char *vfd_str;
+ const char *vfd_str;
};
const char *virtio_device_name(uint16_t devid);
diff --git a/sys/dev/virtio/virtio_bus_if.m b/sys/dev/virtio/virtio_bus_if.m
index ec2029d..74341ff 100644
--- a/sys/dev/virtio/virtio_bus_if.m
+++ b/sys/dev/virtio/virtio_bus_if.m
@@ -1,5 +1,5 @@
#-
-# Copyright (c) 2011, Bryan Venteicher <bryanv@daemoninthecloset.org>
+# Copyright (c) 2011, Bryan Venteicher <bryanv@FreeBSD.org>
# All rights reserved.
#
# Redistribution and use in source and binary forms, with or without
@@ -50,9 +50,6 @@ METHOD int alloc_virtqueues {
int nvqs;
struct vq_alloc_info *info;
};
-HEADER {
-#define VIRTIO_ALLOC_VQS_DISABLE_MSIX 0x1
-};
METHOD int setup_intr {
device_t dev;
diff --git a/sys/dev/virtio/virtio_if.m b/sys/dev/virtio/virtio_if.m
index 701678c..9a99d37 100644
--- a/sys/dev/virtio/virtio_if.m
+++ b/sys/dev/virtio/virtio_if.m
@@ -1,5 +1,5 @@
#-
-# Copyright (c) 2011, Bryan Venteicher <bryanv@daemoninthecloset.org>
+# Copyright (c) 2011, Bryan Venteicher <bryanv@FreeBSD.org>
# All rights reserved.
#
# Redistribution and use in source and binary forms, with or without
@@ -33,8 +33,7 @@ CODE {
static int
virtio_default_config_change(device_t dev)
{
- /* Return that we've handled the change. */
- return (1);
+ return (0);
}
};
diff --git a/sys/dev/virtio/virtqueue.c b/sys/dev/virtio/virtqueue.c
index 1553afa..a82426e 100644
--- a/sys/dev/virtio/virtqueue.c
+++ b/sys/dev/virtio/virtqueue.c
@@ -1,5 +1,5 @@
/*-
- * Copyright (c) 2011, Bryan Venteicher <bryanv@daemoninthecloset.org>
+ * Copyright (c) 2011, Bryan Venteicher <bryanv@FreeBSD.org>
* All rights reserved.
*
* Redistribution and use in source and binary forms, with or without
@@ -414,18 +414,24 @@ virtqueue_nused(struct virtqueue *vq)
}
int
-virtqueue_intr(struct virtqueue *vq)
+virtqueue_intr_filter(struct virtqueue *vq)
{
- if (vq->vq_intrhand == NULL ||
- vq->vq_used_cons_idx == vq->vq_ring.used->idx)
+ if (vq->vq_used_cons_idx == vq->vq_ring.used->idx)
return (0);
- vq->vq_intrhand(vq->vq_intrhand_arg);
+ virtqueue_disable_intr(vq);
return (1);
}
+void
+virtqueue_intr(struct virtqueue *vq)
+{
+
+ vq->vq_intrhand(vq->vq_intrhand_arg);
+}
+
int
virtqueue_enable_intr(struct virtqueue *vq)
{
diff --git a/sys/dev/virtio/virtqueue.h b/sys/dev/virtio/virtqueue.h
index 0296b8c..128a10a 100644
--- a/sys/dev/virtio/virtqueue.h
+++ b/sys/dev/virtio/virtqueue.h
@@ -1,5 +1,5 @@
/*-
- * Copyright (c) 2011, Bryan Venteicher <bryanv@daemoninthecloset.org>
+ * Copyright (c) 2011, Bryan Venteicher <bryanv@FreeBSD.org>
* All rights reserved.
*
* Redistribution and use in source and binary forms, with or without
@@ -39,7 +39,7 @@ struct sglist;
#define VIRTIO_RING_F_EVENT_IDX (1 << 29)
/* Device callback for a virtqueue interrupt. */
-typedef int virtqueue_intr_t(void *);
+typedef void virtqueue_intr_t(void *);
#define VIRTQUEUE_MAX_NAME_SZ 32
@@ -70,7 +70,8 @@ void *virtqueue_drain(struct virtqueue *vq, int *last);
void virtqueue_free(struct virtqueue *vq);
int virtqueue_reinit(struct virtqueue *vq, uint16_t size);
-int virtqueue_intr(struct virtqueue *vq);
+int virtqueue_intr_filter(struct virtqueue *vq);
+void virtqueue_intr(struct virtqueue *vq);
int virtqueue_enable_intr(struct virtqueue *vq);
int virtqueue_postpone_intr(struct virtqueue *vq);
void virtqueue_disable_intr(struct virtqueue *vq);
diff --git a/sys/fs/ext2fs/ext2_lookup.c b/sys/fs/ext2fs/ext2_lookup.c
index 75b11a8..623260d 100644
--- a/sys/fs/ext2fs/ext2_lookup.c
+++ b/sys/fs/ext2fs/ext2_lookup.c
@@ -289,9 +289,9 @@ ext2_lookup_ino(struct vnode *vdp, struct vnode **vpp, struct componentname *cnp
int entryoffsetinblock; /* offset of ep in bp's buffer */
enum {NONE, COMPACT, FOUND} slotstatus;
doff_t slotoffset; /* offset of area with free space */
- int slotsize; /* size of area at slotoffset */
doff_t i_diroff; /* cached i_diroff value */
doff_t i_offset; /* cached i_offset value */
+ int slotsize; /* size of area at slotoffset */
int slotfreespace; /* amount of space free in slot */
int slotneeded; /* size of the entry we're seeking */
int numdirpasses; /* strategy for directory search */
@@ -476,7 +476,6 @@ searchloop:
endsearch = i_diroff;
goto searchloop;
}
- dp->i_offset = i_offset;
if (bp != NULL)
brelse(bp);
/*
@@ -512,7 +511,6 @@ searchloop:
enduseful = slotoffset + slotsize;
}
dp->i_endoff = roundup2(enduseful, DIRBLKSIZ);
- dp->i_flag |= IN_CHANGE | IN_UPDATE;
/*
* We return with the directory locked, so that
* the parameters we set up above will still be
@@ -560,12 +558,13 @@ found:
*/
if ((flags & ISLASTCN) && nameiop == LOOKUP)
dp->i_diroff = i_offset &~ (DIRBLKSIZ - 1);
- dp->i_offset = i_offset;
/*
* If deleting, and at end of pathname, return
* parameters which can be used to remove file.
*/
if (nameiop == DELETE && (flags & ISLASTCN)) {
+ if (flags & LOCKPARENT)
+ ASSERT_VOP_ELOCKED(vdp, __FUNCTION__);
/*
* Write access to directory required to delete files.
*/
@@ -576,7 +575,13 @@ found:
* and distance past previous entry (if there
* is a previous entry in this block) in dp->i_count.
* Save directory inode pointer in ndp->ni_dvp for dirremove().
+ *
+ * Technically we shouldn't be setting these in the
+ * WANTPARENT case (first lookup in rename()), but any
+ * lookups that will result in directory changes will
+ * overwrite these.
*/
+ dp->i_offset = i_offset;
if ((dp->i_offset & (DIRBLKSIZ - 1)) == 0)
dp->i_count = 0;
else
@@ -621,6 +626,7 @@ found:
* Careful about locking second inode.
* This can only occur if the target is ".".
*/
+ dp->i_offset = i_offset;
if (dp->i_number == ino)
return (EISDIR);
if (dd_ino != NULL)
@@ -656,10 +662,7 @@ found:
*/
pdp = vdp;
if (flags & ISDOTDOT) {
- ltype = VOP_ISLOCKED(pdp);
- VOP_UNLOCK(pdp, 0); /* race to get the inode */
- error = VFS_VGET(vdp->v_mount, ino, cnp->cn_lkflags, &tdp);
- vn_lock(pdp, ltype | LK_RETRY);
+ error = vn_vget_ino(pdp, ino, cnp->cn_lkflags, &tdp);
if (pdp->v_iflag & VI_DOOMED) {
if (error == 0)
vput(tdp);
diff --git a/sys/fs/ext2fs/ext2_vfsops.c b/sys/fs/ext2fs/ext2_vfsops.c
index 54d1e89..9e6ce1a 100644
--- a/sys/fs/ext2fs/ext2_vfsops.c
+++ b/sys/fs/ext2fs/ext2_vfsops.c
@@ -979,7 +979,7 @@ ext2_vget(struct mount *mp, ino_t ino, int flags, struct vnode **vpp)
* already have one. This should only happen on old filesystems.
*/
if (ip->i_gen == 0) {
- ip->i_gen = random() / 2 + 1;
+ ip->i_gen = random() + 1;
if ((vp->v_mount->mnt_flag & MNT_RDONLY) == 0)
ip->i_flag |= IN_MODIFIED;
}
diff --git a/sys/fs/nfsclient/nfs_clport.c b/sys/fs/nfsclient/nfs_clport.c
index b2ed50a..d7b082b 100644
--- a/sys/fs/nfsclient/nfs_clport.c
+++ b/sys/fs/nfsclient/nfs_clport.c
@@ -433,6 +433,7 @@ nfscl_loadattrcache(struct vnode **vpp, struct nfsvattr *nap, void *nvaper,
vap->va_size = np->n_size;
np->n_attrstamp = 0;
KDTRACE_NFS_ATTRCACHE_FLUSH_DONE(vp);
+ vnode_pager_setsize(vp, np->n_size);
} else if (np->n_flag & NMODIFIED) {
/*
* We've modified the file: Use the larger
@@ -444,12 +445,22 @@ nfscl_loadattrcache(struct vnode **vpp, struct nfsvattr *nap, void *nvaper,
np->n_size = vap->va_size;
np->n_flag |= NSIZECHANGED;
}
+ vnode_pager_setsize(vp, np->n_size);
+ } else if (vap->va_size < np->n_size) {
+ /*
+ * When shrinking the size, the call to
+ * vnode_pager_setsize() cannot be done
+ * with the mutex held, so delay it until
+ * after the mtx_unlock call.
+ */
+ nsize = np->n_size = vap->va_size;
+ np->n_flag |= NSIZECHANGED;
+ setnsize = 1;
} else {
np->n_size = vap->va_size;
np->n_flag |= NSIZECHANGED;
+ vnode_pager_setsize(vp, np->n_size);
}
- setnsize = 1;
- nsize = vap->va_size;
} else {
np->n_size = vap->va_size;
}
diff --git a/sys/fs/nullfs/null_vnops.c b/sys/fs/nullfs/null_vnops.c
index 6ff15ee..70402e3 100644
--- a/sys/fs/nullfs/null_vnops.c
+++ b/sys/fs/nullfs/null_vnops.c
@@ -554,6 +554,7 @@ null_rename(struct vop_rename_args *ap)
struct vnode *fvp = ap->a_fvp;
struct vnode *fdvp = ap->a_fdvp;
struct vnode *tvp = ap->a_tvp;
+ struct null_node *tnn;
/* Check for cross-device rename. */
if ((fvp->v_mount != tdvp->v_mount) ||
@@ -568,7 +569,11 @@ null_rename(struct vop_rename_args *ap)
vrele(fvp);
return (EXDEV);
}
-
+
+ if (tvp != NULL) {
+ tnn = VTONULL(tvp);
+ tnn->null_flags |= NULLV_DROP;
+ }
return (null_bypass((struct vop_generic_args *)ap));
}
diff --git a/sys/fs/smbfs/smbfs_node.c b/sys/fs/smbfs/smbfs_node.c
index 481fa1d..1b55ff3 100644
--- a/sys/fs/smbfs/smbfs_node.c
+++ b/sys/fs/smbfs/smbfs_node.c
@@ -89,7 +89,7 @@ smbfs_vnode_cmp(struct vnode *vp, void *_sc)
struct smbnode *np;
struct smbcmp *sc;
- np = (struct smbnode *) vp;
+ np = (struct smbnode *) vp->v_data;
sc = (struct smbcmp *) _sc;
if (np->n_parent != sc->n_parent || np->n_nmlen != sc->n_nmlen ||
bcmp(sc->n_name, np->n_name, sc->n_nmlen) != 0)
diff --git a/sys/fs/smbfs/smbfs_smb.c b/sys/fs/smbfs/smbfs_smb.c
index ec4a49d..0e10e5e 100644
--- a/sys/fs/smbfs/smbfs_smb.c
+++ b/sys/fs/smbfs/smbfs_smb.c
@@ -94,12 +94,10 @@ smbfs_smb_lockandx(struct smbnode *np, int op, u_int32_t pid, off_t start, off_t
if (op == SMB_LOCK_SHARED)
ltype |= SMB_LOCKING_ANDX_SHARED_LOCK;
- rqp = malloc(sizeof(struct smb_rq), M_SMBFSDATA, M_WAITOK);
- error = smb_rq_init(rqp, SSTOCP(ssp), SMB_COM_LOCKING_ANDX, scred);
- if (error) {
- free(rqp, M_SMBFSDATA);
- return error;
- }
+
+ error = smb_rq_alloc(SSTOCP(ssp), SMB_COM_LOCKING_ANDX, scred, &rqp);
+ if (error)
+ return (error);
smb_rq_getrequest(rqp, &mbp);
smb_rq_wstart(rqp);
mb_put_uint8(mbp, 0xff); /* secondary command */
@@ -119,7 +117,6 @@ smbfs_smb_lockandx(struct smbnode *np, int op, u_int32_t pid, off_t start, off_t
smb_rq_bend(rqp);
error = smb_rq_simple(rqp);
smb_rq_done(rqp);
- free(rqp, M_SMBFSDATA);
return error;
}
@@ -188,19 +185,16 @@ smbfs_smb_statfs(struct smb_share *ssp, struct statfs *sbp,
u_int16_t units, bpu, bsize, funits;
int error;
- rqp = malloc(sizeof(struct smb_rq), M_SMBFSDATA, M_WAITOK);
- error = smb_rq_init(rqp, SSTOCP(ssp), SMB_COM_QUERY_INFORMATION_DISK, scred);
- if (error) {
- free(rqp, M_SMBFSDATA);
- return error;
- }
+ error = smb_rq_alloc(SSTOCP(ssp), SMB_COM_QUERY_INFORMATION_DISK,
+ scred, &rqp);
+ if (error)
+ return (error);
smb_rq_wstart(rqp);
smb_rq_wend(rqp);
smb_rq_bstart(rqp);
smb_rq_bend(rqp);
error = smb_rq_simple(rqp);
if (error) {
- free(rqp, M_SMBFSDATA);
smb_rq_done(rqp);
return error;
}
@@ -216,7 +210,6 @@ smbfs_smb_statfs(struct smb_share *ssp, struct statfs *sbp,
sbp->f_files = 0xffff; /* total file nodes in filesystem */
sbp->f_ffree = 0xffff; /* free file nodes in fs */
smb_rq_done(rqp);
- free(rqp, M_SMBFSDATA);
return 0;
}
@@ -260,12 +253,9 @@ smb_smb_flush(struct smbnode *np, struct smb_cred *scred)
if ((np->n_flag & NOPEN) == 0 || !SMBTOV(np) ||
SMBTOV(np)->v_type != VREG)
return 0; /* not a regular open file */
- rqp = malloc(sizeof(struct smb_rq), M_SMBFSDATA, M_WAITOK);
- error = smb_rq_init(rqp, SSTOCP(ssp), SMB_COM_FLUSH, scred);
- if (error) {
- free(rqp, M_SMBFSDATA);
+ error = smb_rq_alloc(SSTOCP(ssp), SMB_COM_FLUSH, scred, &rqp);
+ if (error)
return (error);
- }
smb_rq_getrequest(rqp, &mbp);
smb_rq_wstart(rqp);
mb_put_mem(mbp, (caddr_t)&np->n_fid, 2, MB_MSYSTEM);
@@ -274,7 +264,6 @@ smb_smb_flush(struct smbnode *np, struct smb_cred *scred)
smb_rq_bend(rqp);
error = smb_rq_simple(rqp);
smb_rq_done(rqp);
- free(rqp, M_SMBFSDATA);
if (!error)
np->n_flag &= ~NFLUSHWIRE;
return (error);
@@ -301,12 +290,9 @@ smbfs_smb_setfsize(struct smbnode *np, int newsize, struct smb_cred *scred)
return (0);
}
- rqp = malloc(sizeof(struct smb_rq), M_SMBFSDATA, M_WAITOK);
- error = smb_rq_init(rqp, SSTOCP(ssp), SMB_COM_WRITE, scred);
- if (error) {
- free(rqp, M_SMBFSDATA);
- return error;
- }
+ error = smb_rq_alloc(SSTOCP(ssp), SMB_COM_WRITE, scred, &rqp);
+ if (error)
+ return (error);
smb_rq_getrequest(rqp, &mbp);
smb_rq_wstart(rqp);
mb_put_mem(mbp, (caddr_t)&np->n_fid, 2, MB_MSYSTEM);
@@ -320,7 +306,6 @@ smbfs_smb_setfsize(struct smbnode *np, int newsize, struct smb_cred *scred)
smb_rq_bend(rqp);
error = smb_rq_simple(rqp);
smb_rq_done(rqp);
- free(rqp, M_SMBFSDATA);
return error;
}
@@ -337,12 +322,10 @@ smbfs_smb_query_info(struct smbnode *np, const char *name, int len,
u_int16_t wattr;
u_int32_t lint;
- rqp = malloc(sizeof(struct smb_rq), M_SMBFSDATA, M_WAITOK);
- error = smb_rq_init(rqp, SSTOCP(ssp), SMB_COM_QUERY_INFORMATION, scred);
- if (error) {
- free(rqp, M_SMBFSDATA);
- return error;
- }
+ error = smb_rq_alloc(SSTOCP(ssp), SMB_COM_QUERY_INFORMATION, scred,
+ &rqp);
+ if (error)
+ return (error);
smb_rq_getrequest(rqp, &mbp);
smb_rq_wstart(rqp);
smb_rq_wend(rqp);
@@ -377,7 +360,6 @@ smbfs_smb_query_info(struct smbnode *np, const char *name, int len,
fap->fa_size = lint;
} while(0);
smb_rq_done(rqp);
- free(rqp, M_SMBFSDATA);
return error;
}
@@ -394,12 +376,10 @@ smbfs_smb_setpattr(struct smbnode *np, u_int16_t attr, struct timespec *mtime,
u_long time;
int error, svtz;
- rqp = malloc(sizeof(struct smb_rq), M_SMBFSDATA, M_WAITOK);
- error = smb_rq_init(rqp, SSTOCP(ssp), SMB_COM_SET_INFORMATION, scred);
- if (error) {
- free(rqp, M_SMBFSDATA);
- return error;
- }
+ error = smb_rq_alloc(SSTOCP(ssp), SMB_COM_SET_INFORMATION, scred,
+ &rqp);
+ if (error)
+ return (error);
svtz = SSTOVC(ssp)->vc_sopt.sv_tz;
smb_rq_getrequest(rqp, &mbp);
smb_rq_wstart(rqp);
@@ -431,7 +411,6 @@ smbfs_smb_setpattr(struct smbnode *np, u_int16_t attr, struct timespec *mtime,
}
} while(0);
smb_rq_done(rqp);
- free(rqp, M_SMBFSDATA);
return error;
}
@@ -554,12 +533,10 @@ smbfs_smb_setftime(struct smbnode *np, struct timespec *mtime,
u_int16_t date, time;
int error, tzoff;
- rqp = malloc(sizeof(struct smb_rq), M_SMBFSDATA, M_WAITOK);
- error = smb_rq_init(rqp, SSTOCP(ssp), SMB_COM_SET_INFORMATION2, scred);
- if (error) {
- free(rqp, M_SMBFSDATA);
- return error;
- }
+ error = smb_rq_alloc(SSTOCP(ssp), SMB_COM_SET_INFORMATION2, scred,
+ &rqp);
+ if (error)
+ return (error);
tzoff = SSTOVC(ssp)->vc_sopt.sv_tz;
smb_rq_getrequest(rqp, &mbp);
smb_rq_wstart(rqp);
@@ -584,7 +561,6 @@ smbfs_smb_setftime(struct smbnode *np, struct timespec *mtime,
error = smb_rq_simple(rqp);
SMBSDEBUG("%d\n", error);
smb_rq_done(rqp);
- free(rqp, M_SMBFSDATA);
return error;
}
@@ -648,12 +624,9 @@ smbfs_smb_open(struct smbnode *np, int accmode, struct smb_cred *scred)
u_int16_t fid, wattr, grantedmode;
int error;
- rqp = malloc(sizeof(struct smb_rq), M_SMBFSDATA, M_WAITOK);
- error = smb_rq_init(rqp, SSTOCP(ssp), SMB_COM_OPEN, scred);
- if (error) {
- free(rqp, M_SMBFSDATA);
- return error;
- }
+ error = smb_rq_alloc(SSTOCP(ssp), SMB_COM_OPEN, scred, &rqp);
+ if (error)
+ return (error);
smb_rq_getrequest(rqp, &mbp);
smb_rq_wstart(rqp);
mb_put_uint16le(mbp, accmode);
@@ -684,7 +657,6 @@ smbfs_smb_open(struct smbnode *np, int accmode, struct smb_cred *scred)
*/
} while(0);
smb_rq_done(rqp);
- free(rqp, M_SMBFSDATA);
if (error)
return error;
np->n_fid = fid;
@@ -702,12 +674,9 @@ smbfs_smb_close(struct smb_share *ssp, u_int16_t fid, struct timespec *mtime,
u_long time;
int error;
- rqp = malloc(sizeof(struct smb_rq), M_SMBFSDATA, M_WAITOK);
- error = smb_rq_init(rqp, SSTOCP(ssp), SMB_COM_CLOSE, scred);
- if (error) {
- free(rqp, M_SMBFSDATA);
- return error;
- }
+ error = smb_rq_alloc(SSTOCP(ssp), SMB_COM_CLOSE, scred, &rqp);
+ if (error)
+ return (error);
smb_rq_getrequest(rqp, &mbp);
smb_rq_wstart(rqp);
mb_put_mem(mbp, (caddr_t)&fid, sizeof(fid), MB_MSYSTEM);
@@ -721,7 +690,6 @@ smbfs_smb_close(struct smb_share *ssp, u_int16_t fid, struct timespec *mtime,
smb_rq_bend(rqp);
error = smb_rq_simple(rqp);
smb_rq_done(rqp);
- free(rqp, M_SMBFSDATA);
return error;
}
@@ -739,12 +707,9 @@ smbfs_smb_create(struct smbnode *dnp, const char *name, int nmlen,
u_long tm;
int error;
- rqp = malloc(sizeof(struct smb_rq), M_SMBFSDATA, M_WAITOK);
- error = smb_rq_init(rqp, SSTOCP(ssp), SMB_COM_CREATE, scred);
- if (error) {
- free(rqp, M_SMBFSDATA);
- return error;
- }
+ error = smb_rq_alloc(SSTOCP(ssp), SMB_COM_CREATE, scred, &rqp);
+ if (error)
+ return (error);
smb_rq_getrequest(rqp, &mbp);
smb_rq_wstart(rqp);
mb_put_uint16le(mbp, SMB_FA_ARCHIVE); /* attributes */
@@ -771,7 +736,6 @@ smbfs_smb_create(struct smbnode *dnp, const char *name, int nmlen,
if (error)
return error;
smbfs_smb_close(ssp, fid, &ctime, scred);
- free(rqp, M_SMBFSDATA);
return error;
}
@@ -783,12 +747,9 @@ smbfs_smb_delete(struct smbnode *np, struct smb_cred *scred)
struct mbchain *mbp;
int error;
- rqp = malloc(sizeof(struct smb_rq), M_SMBFSDATA, M_WAITOK);
- error = smb_rq_init(rqp, SSTOCP(ssp), SMB_COM_DELETE, scred);
- if (error) {
- free(rqp, M_SMBFSDATA);
- return error;
- }
+ error = smb_rq_alloc(SSTOCP(ssp), SMB_COM_DELETE, scred, &rqp);
+ if (error)
+ return (error);
smb_rq_getrequest(rqp, &mbp);
smb_rq_wstart(rqp);
mb_put_uint16le(mbp, SMB_FA_SYSTEM | SMB_FA_HIDDEN);
@@ -801,7 +762,6 @@ smbfs_smb_delete(struct smbnode *np, struct smb_cred *scred)
error = smb_rq_simple(rqp);
}
smb_rq_done(rqp);
- free(rqp, M_SMBFSDATA);
return error;
}
@@ -814,12 +774,9 @@ smbfs_smb_rename(struct smbnode *src, struct smbnode *tdnp,
struct mbchain *mbp;
int error;
- rqp = malloc(sizeof(struct smb_rq), M_SMBFSDATA, M_WAITOK);
- error = smb_rq_init(rqp, SSTOCP(ssp), SMB_COM_RENAME, scred);
- if (error) {
- free(rqp, M_SMBFSDATA);
- return error;
- }
+ error = smb_rq_alloc(SSTOCP(ssp), SMB_COM_RENAME, scred, &rqp);
+ if (error)
+ return (error);
smb_rq_getrequest(rqp, &mbp);
smb_rq_wstart(rqp);
mb_put_uint16le(mbp, SMB_FA_SYSTEM | SMB_FA_HIDDEN);
@@ -838,7 +795,6 @@ smbfs_smb_rename(struct smbnode *src, struct smbnode *tdnp,
error = smb_rq_simple(rqp);
} while(0);
smb_rq_done(rqp);
- free(rqp, M_SMBFSDATA);
return error;
}
@@ -851,12 +807,9 @@ smbfs_smb_move(struct smbnode *src, struct smbnode *tdnp,
struct mbchain *mbp;
int error;
- rqp = malloc(sizeof(struct smb_rq), M_SMBFSDATA, M_WAITOK);
- error = smb_rq_init(rqp, SSTOCP(ssp), SMB_COM_MOVE, scred);
- if (error) {
- free(rqp, M_SMBFSDATA);
- return error;
- }
+ error = smb_rq_alloc(SSTOCP(ssp), SMB_COM_MOVE, scred, &rqp);
+ if (error)
+ return (error);
smb_rq_getrequest(rqp, &mbp);
smb_rq_wstart(rqp);
mb_put_uint16le(mbp, SMB_TID_UNKNOWN);
@@ -877,7 +830,6 @@ smbfs_smb_move(struct smbnode *src, struct smbnode *tdnp,
error = smb_rq_simple(rqp);
} while(0);
smb_rq_done(rqp);
- free(rqp, M_SMBFSDATA);
return error;
}
@@ -890,12 +842,10 @@ smbfs_smb_mkdir(struct smbnode *dnp, const char *name, int len,
struct mbchain *mbp;
int error;
- rqp = malloc(sizeof(struct smb_rq), M_SMBFSDATA, M_WAITOK);
- error = smb_rq_init(rqp, SSTOCP(ssp), SMB_COM_CREATE_DIRECTORY, scred);
- if (error) {
- free(rqp, M_SMBFSDATA);
- return error;
- }
+ error = smb_rq_alloc(SSTOCP(ssp), SMB_COM_CREATE_DIRECTORY, scred,
+ &rqp);
+ if (error)
+ return (error);
smb_rq_getrequest(rqp, &mbp);
smb_rq_wstart(rqp);
smb_rq_wend(rqp);
@@ -907,7 +857,6 @@ smbfs_smb_mkdir(struct smbnode *dnp, const char *name, int len,
error = smb_rq_simple(rqp);
}
smb_rq_done(rqp);
- free(rqp, M_SMBFSDATA);
return error;
}
@@ -919,12 +868,10 @@ smbfs_smb_rmdir(struct smbnode *np, struct smb_cred *scred)
struct mbchain *mbp;
int error;
- rqp = malloc(sizeof(struct smb_rq), M_SMBFSDATA, M_WAITOK);
- error = smb_rq_init(rqp, SSTOCP(ssp), SMB_COM_DELETE_DIRECTORY, scred);
- if (error) {
- free(rqp, M_SMBFSDATA);
- return error;
- }
+ error = smb_rq_alloc(SSTOCP(ssp), SMB_COM_DELETE_DIRECTORY, scred,
+ &rqp);
+ if (error)
+ return (error);
smb_rq_getrequest(rqp, &mbp);
smb_rq_wstart(rqp);
smb_rq_wend(rqp);
@@ -936,7 +883,6 @@ smbfs_smb_rmdir(struct smbnode *np, struct smb_cred *scred)
error = smb_rq_simple(rqp);
}
smb_rq_done(rqp);
- free(rqp, M_SMBFSDATA);
return error;
}
@@ -958,7 +904,7 @@ smbfs_smb_search(struct smbfs_fctx *ctx)
}
error = smb_rq_alloc(SSTOCP(ctx->f_ssp), SMB_COM_SEARCH, ctx->f_scred, &rqp);
if (error)
- return error;
+ return (error);
ctx->f_rq = rqp;
smb_rq_getrequest(rqp, &mbp);
smb_rq_wstart(rqp);
@@ -1204,12 +1150,10 @@ smbfs_smb_findclose2(struct smbfs_fctx *ctx)
struct mbchain *mbp;
int error;
- rqp = malloc(sizeof(struct smb_rq), M_SMBFSDATA, M_WAITOK);
- error = smb_rq_init(rqp, SSTOCP(ctx->f_ssp), SMB_COM_FIND_CLOSE2, ctx->f_scred);
- if (error) {
- free(rqp, M_SMBFSDATA);
- return error;
- }
+ error = smb_rq_alloc(SSTOCP(ctx->f_ssp), SMB_COM_FIND_CLOSE2,
+ ctx->f_scred, &rqp);
+ if (error)
+ return (error);
smb_rq_getrequest(rqp, &mbp);
smb_rq_wstart(rqp);
mb_put_mem(mbp, (caddr_t)&ctx->f_Sid, 2, MB_MSYSTEM);
@@ -1218,7 +1162,6 @@ smbfs_smb_findclose2(struct smbfs_fctx *ctx)
smb_rq_bend(rqp);
error = smb_rq_simple(rqp);
smb_rq_done(rqp);
- free(rqp, M_SMBFSDATA);
return error;
}
diff --git a/sys/fs/smbfs/smbfs_vfsops.c b/sys/fs/smbfs/smbfs_vfsops.c
index 66512e9..2d33df3 100644
--- a/sys/fs/smbfs/smbfs_vfsops.c
+++ b/sys/fs/smbfs/smbfs_vfsops.c
@@ -324,11 +324,6 @@ smbfs_root(struct mount *mp, int flags, struct vnode **vpp)
td = curthread;
cred = td->td_ucred;
- if (smp == NULL) {
- SMBERROR("smp == NULL (bug in umount)\n");
- vfs_mount_error(mp, "smp == NULL (bug in umount)");
- return EINVAL;
- }
if (smp->sm_root) {
*vpp = SMBTOV(smp->sm_root);
return vget(*vpp, LK_EXCLUSIVE | LK_RETRY, td);
diff --git a/sys/fs/smbfs/smbfs_vnops.c b/sys/fs/smbfs/smbfs_vnops.c
index 6ceed9c..ef1dc65 100644
--- a/sys/fs/smbfs/smbfs_vnops.c
+++ b/sys/fs/smbfs/smbfs_vnops.c
@@ -1282,11 +1282,14 @@ smbfs_lookup(ap)
error = vfs_busy(mp, 0);
vn_lock(dvp, LK_EXCLUSIVE | LK_RETRY);
vfs_rel(mp);
- if (error)
- return (ENOENT);
+ if (error) {
+ error = ENOENT;
+ goto out;
+ }
if ((dvp->v_iflag & VI_DOOMED) != 0) {
vfs_unbusy(mp);
- return (ENOENT);
+ error = ENOENT;
+ goto out;
}
}
VOP_UNLOCK(dvp, 0);
diff --git a/sys/geom/geom_disk.c b/sys/geom/geom_disk.c
index a9f7048..60cff73 100644
--- a/sys/geom/geom_disk.c
+++ b/sys/geom/geom_disk.c
@@ -148,14 +148,12 @@ g_disk_access(struct g_provider *pp, int r, int w, int e)
dp->d_name, dp->d_unit);
dp->d_maxsize = DFLTPHYS;
}
- if (dp->d_flags & DISKFLAG_CANDELETE) {
- if (bootverbose && dp->d_delmaxsize == 0) {
- printf("WARNING: Disk drive %s%d has no d_delmaxsize\n",
- dp->d_name, dp->d_unit);
- dp->d_delmaxsize = dp->d_maxsize;
+ if (dp->d_delmaxsize == 0) {
+ if (bootverbose && dp->d_flags & DISKFLAG_CANDELETE) {
+ printf("WARNING: Disk drive %s%d has no "
+ "d_delmaxsize\n", dp->d_name, dp->d_unit);
}
- } else {
- dp->d_delmaxsize = 0;
+ dp->d_delmaxsize = dp->d_maxsize;
}
pp->stripeoffset = dp->d_stripeoffset;
pp->stripesize = dp->d_stripesize;
@@ -629,7 +627,7 @@ void
disk_create(struct disk *dp, int version)
{
- if (version != DISK_VERSION_02) {
+ if (version != DISK_VERSION) {
printf("WARNING: Attempt to add disk %s%d %s",
dp->d_name, dp->d_unit,
" using incompatible ABI version of disk(9)\n");
diff --git a/sys/geom/geom_disk.h b/sys/geom/geom_disk.h
index 05c5de3..852047b 100644
--- a/sys/geom/geom_disk.h
+++ b/sys/geom/geom_disk.h
@@ -120,7 +120,8 @@ int disk_resize(struct disk *dp, int flag);
#define DISK_VERSION_00 0x58561059
#define DISK_VERSION_01 0x5856105a
#define DISK_VERSION_02 0x5856105b
-#define DISK_VERSION DISK_VERSION_02
+#define DISK_VERSION_03 0x5856105c
+#define DISK_VERSION DISK_VERSION_03
#endif /* _KERNEL */
#endif /* _GEOM_GEOM_DISK_H_ */
diff --git a/sys/geom/geom_io.c b/sys/geom/geom_io.c
index 25a90de..0e79920 100644
--- a/sys/geom/geom_io.c
+++ b/sys/geom/geom_io.c
@@ -49,6 +49,7 @@ __FBSDID("$FreeBSD$");
#include <sys/proc.h>
#include <sys/stack.h>
#include <sys/sysctl.h>
+#include <sys/vmem.h>
#include <sys/errno.h>
#include <geom/geom.h>
@@ -626,7 +627,6 @@ g_io_transient_map_bio(struct bio *bp)
vm_offset_t addr;
long size;
u_int retried;
- int rv;
KASSERT(unmapped_buf_allowed, ("unmapped disabled"));
@@ -636,10 +636,7 @@ g_io_transient_map_bio(struct bio *bp)
retried = 0;
atomic_add_long(&transient_maps, 1);
retry:
- vm_map_lock(bio_transient_map);
- if (vm_map_findspace(bio_transient_map, vm_map_min(bio_transient_map),
- size, &addr)) {
- vm_map_unlock(bio_transient_map);
+ if (vmem_alloc(transient_arena, size, M_BESTFIT | M_NOWAIT, &addr)) {
if (transient_map_retries != 0 &&
retried >= transient_map_retries) {
g_io_deliver(bp, EDEADLK/* XXXKIB */);
@@ -651,7 +648,7 @@ retry:
/*
* Naive attempt to quisce the I/O to get more
* in-flight requests completed and defragment
- * the bio_transient_map.
+ * the transient_arena.
*/
CTR3(KTR_GEOM, "g_down retrymap bp %p provider %s r %d",
bp, bp->bio_to->name, retried);
@@ -661,12 +658,6 @@ retry:
goto retry;
}
}
- rv = vm_map_insert(bio_transient_map, NULL, 0, addr, addr + size,
- VM_PROT_RW, VM_PROT_RW, MAP_NOFAULT);
- KASSERT(rv == KERN_SUCCESS,
- ("vm_map_insert(bio_transient_map) rv %d %jx %lx",
- rv, (uintmax_t)addr, size));
- vm_map_unlock(bio_transient_map);
atomic_add_int(&inflight_transient_maps, 1);
pmap_qenter((vm_offset_t)addr, bp->bio_ma, OFF_TO_IDX(size));
bp->bio_data = (caddr_t)addr + bp->bio_ma_offset;
diff --git a/sys/i386/include/counter.h b/sys/i386/include/counter.h
index 3e93b36..01b09bb 100644
--- a/sys/i386/include/counter.h
+++ b/sys/i386/include/counter.h
@@ -67,6 +67,93 @@ counter_64_inc_8b(uint64_t *p, int64_t inc)
: "memory", "cc", "eax", "edx", "ebx", "ecx");
}
+#ifdef IN_SUBR_COUNTER_C
+static inline uint64_t
+counter_u64_read_one_8b(uint64_t *p)
+{
+ uint32_t res_lo, res_high;
+
+ __asm __volatile(
+ "movl %%eax,%%ebx\n\t"
+ "movl %%edx,%%ecx\n\t"
+ "cmpxchg8b (%2)"
+ : "=a" (res_lo), "=d"(res_high)
+ : "SD" (p)
+ : "cc", "ebx", "ecx");
+ return (res_lo + ((uint64_t)res_high << 32));
+}
+
+static inline uint64_t
+counter_u64_fetch_inline(uint64_t *p)
+{
+ uint64_t res;
+ int i;
+
+ res = 0;
+ if ((cpu_feature & CPUID_CX8) == 0) {
+ /*
+ * The machines without cmpxchg8b are not SMP.
+ * Disabling the preemption provides atomicity of the
+ * counter reading, since update is done in the
+ * critical section as well.
+ */
+ critical_enter();
+ for (i = 0; i < mp_ncpus; i++) {
+ res += *(uint64_t *)((char *)p +
+ sizeof(struct pcpu) * i);
+ }
+ critical_exit();
+ } else {
+ for (i = 0; i < mp_ncpus; i++)
+ res += counter_u64_read_one_8b((uint64_t *)((char *)p +
+ sizeof(struct pcpu) * i));
+ }
+ return (res);
+}
+
+static inline void
+counter_u64_zero_one_8b(uint64_t *p)
+{
+
+ __asm __volatile(
+ "movl (%0),%%eax\n\t"
+ "movl 4(%0),%%edx\n"
+ "xorl %%ebx,%%ebx\n\t"
+ "xorl %%ecx,%%ecx\n\t"
+"1:\n\t"
+ "cmpxchg8b (%0)\n\t"
+ "jnz 1b"
+ :
+ : "SD" (p)
+ : "memory", "cc", "eax", "edx", "ebx", "ecx");
+}
+
+static void
+counter_u64_zero_one_cpu(void *arg)
+{
+ uint64_t *p;
+
+ p = (uint64_t *)((char *)arg + sizeof(struct pcpu) * PCPU_GET(cpuid));
+ counter_u64_zero_one_8b(p);
+}
+
+static inline void
+counter_u64_zero_inline(counter_u64_t c)
+{
+ int i;
+
+ if ((cpu_feature & CPUID_CX8) == 0) {
+ critical_enter();
+ for (i = 0; i < mp_ncpus; i++)
+ *(uint64_t *)((char *)c + sizeof(struct pcpu) * i) = 0;
+ critical_exit();
+ } else {
+ smp_rendezvous(smp_no_rendevous_barrier,
+ counter_u64_zero_one_cpu, smp_no_rendevous_barrier, c);
+ }
+}
+#endif
+
#define counter_u64_add_protected(c, inc) do { \
if ((cpu_feature & CPUID_CX8) == 0) { \
CRITICAL_ASSERT(curthread); \
diff --git a/sys/ia64/include/counter.h b/sys/ia64/include/counter.h
index 68f89e2..a3fe871 100644
--- a/sys/ia64/include/counter.h
+++ b/sys/ia64/include/counter.h
@@ -37,6 +37,45 @@
#define counter_enter() critical_enter()
#define counter_exit() critical_exit()
+#ifdef IN_SUBR_COUNTER_C
+static inline uint64_t
+counter_u64_read_one(uint64_t *p, int cpu)
+{
+
+ return (*(uint64_t *)((char *)p + sizeof(struct pcpu) * cpu));
+}
+
+static inline uint64_t
+counter_u64_fetch_inline(uint64_t *p)
+{
+ uint64_t r;
+ int i;
+
+ r = 0;
+ for (i = 0; i < mp_ncpus; i++)
+ r += counter_u64_read_one((uint64_t *)p, i);
+
+ return (r);
+}
+
+/* XXXKIB might interrupt increment */
+static void
+counter_u64_zero_one_cpu(void *arg)
+{
+
+ *((uint64_t *)((char *)arg + sizeof(struct pcpu) *
+ PCPU_GET(cpuid))) = 0;
+}
+
+static inline void
+counter_u64_zero_inline(counter_u64_t c)
+{
+
+ smp_rendezvous(smp_no_rendevous_barrier, counter_u64_zero_one_cpu,
+ smp_no_rendevous_barrier, c);
+}
+#endif
+
#define counter_u64_add_protected(c, inc) do { \
CRITICAL_ASSERT(curthread); \
*(uint64_t *)zpcpu_get(c) += (inc); \
diff --git a/sys/kern/Make.tags.inc b/sys/kern/Make.tags.inc
index 81205a6..cb8a3ff 100644
--- a/sys/kern/Make.tags.inc
+++ b/sys/kern/Make.tags.inc
@@ -30,6 +30,7 @@ COMM= ${SYS}/dev/advansys/*.[ch] \
${SYS}/fs/msdosfs/*.[ch] \
${SYS}/fs/nullfs/*.[ch] \
${SYS}/fs/procfs/*.[ch] \
+ ${SYS}/fs/smbfs/*.[ch] \
${SYS}/fs/udf/*.[ch] \
${SYS}/fs/unionfs/*.[ch] \
${SYS}/geom/*.[ch] \
diff --git a/sys/kern/kern_acct.c b/sys/kern/kern_acct.c
index 3362112..ef3fd2e 100644
--- a/sys/kern/kern_acct.c
+++ b/sys/kern/kern_acct.c
@@ -133,6 +133,7 @@ static int acct_configured;
static int acct_suspended;
static struct vnode *acct_vp;
static struct ucred *acct_cred;
+static struct plimit *acct_limit;
static int acct_flags;
static struct sx acct_sx;
@@ -196,7 +197,7 @@ int
sys_acct(struct thread *td, struct acct_args *uap)
{
struct nameidata nd;
- int error, flags, replacing;
+ int error, flags, i, replacing;
error = priv_check(td, PRIV_ACCT);
if (error)
@@ -267,6 +268,15 @@ sys_acct(struct thread *td, struct acct_args *uap)
}
/*
+ * Create our own plimit object without limits. It will be assigned
+ * to exiting processes.
+ */
+ acct_limit = lim_alloc();
+ for (i = 0; i < RLIM_NLIMITS; i++)
+ acct_limit->pl_rlimit[i].rlim_cur =
+ acct_limit->pl_rlimit[i].rlim_max = RLIM_INFINITY;
+
+ /*
* Save the new accounting file vnode, and schedule the new
* free space watcher.
*/
@@ -284,12 +294,7 @@ sys_acct(struct thread *td, struct acct_args *uap)
error = kproc_create(acct_thread, NULL, NULL, 0, 0,
"accounting");
if (error) {
- (void) vn_close(acct_vp, acct_flags, acct_cred, td);
- crfree(acct_cred);
- acct_configured = 0;
- acct_vp = NULL;
- acct_cred = NULL;
- acct_flags = 0;
+ (void) acct_disable(td, 0);
sx_xunlock(&acct_sx);
log(LOG_NOTICE, "Unable to start accounting thread\n");
return (error);
@@ -314,6 +319,7 @@ acct_disable(struct thread *td, int logging)
sx_assert(&acct_sx, SX_XLOCKED);
error = vn_close(acct_vp, acct_flags, acct_cred, td);
crfree(acct_cred);
+ lim_free(acct_limit);
acct_configured = 0;
acct_vp = NULL;
acct_cred = NULL;
@@ -334,7 +340,7 @@ acct_process(struct thread *td)
{
struct acctv2 acct;
struct timeval ut, st, tmp;
- struct plimit *newlim, *oldlim;
+ struct plimit *oldlim;
struct proc *p;
struct rusage ru;
int t, ret;
@@ -410,7 +416,6 @@ acct_process(struct thread *td)
/* (8) The boolean flags that tell how the process terminated, etc. */
acct.ac_flagx = p->p_acflag;
- PROC_UNLOCK(p);
/* Setup ancillary structure fields. */
acct.ac_flagx |= ANVER;
@@ -419,14 +424,10 @@ acct_process(struct thread *td)
acct.ac_len = acct.ac_len2 = sizeof(acct);
/*
- * Eliminate any file size rlimit.
+ * Eliminate rlimits (file size limit in particular).
*/
- newlim = lim_alloc();
- PROC_LOCK(p);
oldlim = p->p_limit;
- lim_copy(newlim, oldlim);
- newlim->pl_rlimit[RLIMIT_FSIZE].rlim_cur = RLIM_INFINITY;
- p->p_limit = newlim;
+ p->p_limit = lim_hold(acct_limit);
PROC_UNLOCK(p);
lim_free(oldlim);
diff --git a/sys/kern/kern_clock.c b/sys/kern/kern_clock.c
index 2841f8c..4cfd219 100644
--- a/sys/kern/kern_clock.c
+++ b/sys/kern/kern_clock.c
@@ -216,13 +216,8 @@ deadlkres(void)
}
FOREACH_THREAD_IN_PROC(p, td) {
- /*
- * Once a thread is found in "interesting"
- * state a possible ticks wrap-up needs to be
- * checked.
- */
thread_lock(td);
- if (TD_ON_LOCK(td) && ticks < td->td_blktick) {
+ if (TD_ON_LOCK(td)) {
/*
* The thread should be blocked on a
@@ -247,8 +242,7 @@ deadlkres(void)
__func__, td, tticks);
}
} else if (TD_IS_SLEEPING(td) &&
- TD_ON_SLEEPQ(td) &&
- ticks < td->td_blktick) {
+ TD_ON_SLEEPQ(td)) {
/*
* Check if the thread is sleeping on a
diff --git a/sys/kern/kern_descrip.c b/sys/kern/kern_descrip.c
index c0ecf15..fb264ba 100644
--- a/sys/kern/kern_descrip.c
+++ b/sys/kern/kern_descrip.c
@@ -3176,10 +3176,16 @@ static SYSCTL_NODE(_kern_proc, KERN_PROC_OFILEDESC, ofiledesc, CTLFLAG_RD,
CTASSERT(sizeof(struct kinfo_file) == KINFO_FILE_SIZE);
#endif
+struct export_fd_buf {
+ struct filedesc *fdp;
+ struct sbuf *sb;
+ ssize_t remainder;
+ struct kinfo_file kif;
+};
+
static int
export_fd_to_sb(void *data, int type, int fd, int fflags, int refcnt,
- int64_t offset, cap_rights_t fd_cap_rights, struct kinfo_file *kif,
- struct sbuf *sb, ssize_t *remainder)
+ int64_t offset, cap_rights_t fd_cap_rights, struct export_fd_buf *efbuf)
{
struct {
int fflag;
@@ -3202,16 +3208,23 @@ export_fd_to_sb(void *data, int type, int fd, int fflags, int refcnt,
{ O_TRUNC, KF_FLAG_TRUNC }
};
#define NFFLAGS (sizeof(fflags_table) / sizeof(*fflags_table))
+ struct kinfo_file *kif;
struct vnode *vp;
- int error;
+ int error, locked;
unsigned int i;
- if (*remainder == 0)
+ if (efbuf->remainder == 0)
return (0);
+ kif = &efbuf->kif;
bzero(kif, sizeof(*kif));
+ locked = efbuf->fdp != NULL;
switch (type) {
case KF_TYPE_FIFO:
case KF_TYPE_VNODE:
+ if (locked) {
+ FILEDESC_SUNLOCK(efbuf->fdp);
+ locked = 0;
+ }
vp = (struct vnode *)data;
error = fill_vnode_info(vp, kif);
vrele(vp);
@@ -3255,15 +3268,21 @@ export_fd_to_sb(void *data, int type, int fd, int fflags, int refcnt,
kif->kf_structsize = offsetof(struct kinfo_file, kf_path) +
strlen(kif->kf_path) + 1;
kif->kf_structsize = roundup(kif->kf_structsize, sizeof(uint64_t));
- if (*remainder != -1) {
- if (*remainder < kif->kf_structsize) {
+ if (efbuf->remainder != -1) {
+ if (efbuf->remainder < kif->kf_structsize) {
/* Terminate export. */
- *remainder = 0;
+ efbuf->remainder = 0;
+ if (efbuf->fdp != NULL && !locked)
+ FILEDESC_SLOCK(efbuf->fdp);
return (0);
}
- *remainder -= kif->kf_structsize;
+ efbuf->remainder -= kif->kf_structsize;
}
- error = sbuf_bcat(sb, kif, kif->kf_structsize);
+ if (locked)
+ FILEDESC_SUNLOCK(efbuf->fdp);
+ error = sbuf_bcat(efbuf->sb, kif, kif->kf_structsize);
+ if (efbuf->fdp != NULL)
+ FILEDESC_SLOCK(efbuf->fdp);
return (error);
}
@@ -3277,18 +3296,16 @@ kern_proc_filedesc_out(struct proc *p, struct sbuf *sb, ssize_t maxlen)
{
struct file *fp;
struct filedesc *fdp;
- struct kinfo_file *kif;
+ struct export_fd_buf *efbuf;
struct vnode *cttyvp, *textvp, *tracevp;
int64_t offset;
void *data;
- ssize_t remainder;
int error, i;
int type, refcnt, fflags;
cap_rights_t fd_cap_rights;
PROC_LOCK_ASSERT(p, MA_OWNED);
- remainder = maxlen;
/* ktrace vnode */
tracevp = p->p_tracevp;
if (tracevp != NULL)
@@ -3306,46 +3323,44 @@ kern_proc_filedesc_out(struct proc *p, struct sbuf *sb, ssize_t maxlen)
}
fdp = fdhold(p);
PROC_UNLOCK(p);
- kif = malloc(sizeof(*kif), M_TEMP, M_WAITOK);
+ efbuf = malloc(sizeof(*efbuf), M_TEMP, M_WAITOK);
+ efbuf->fdp = NULL;
+ efbuf->sb = sb;
+ efbuf->remainder = maxlen;
if (tracevp != NULL)
export_fd_to_sb(tracevp, KF_TYPE_VNODE, KF_FD_TYPE_TRACE,
- FREAD | FWRITE, -1, -1, 0, kif, sb, &remainder);
+ FREAD | FWRITE, -1, -1, 0, efbuf);
if (textvp != NULL)
export_fd_to_sb(textvp, KF_TYPE_VNODE, KF_FD_TYPE_TEXT,
- FREAD, -1, -1, 0, kif, sb, &remainder);
+ FREAD, -1, -1, 0, efbuf);
if (cttyvp != NULL)
export_fd_to_sb(cttyvp, KF_TYPE_VNODE, KF_FD_TYPE_CTTY,
- FREAD | FWRITE, -1, -1, 0, kif, sb, &remainder);
+ FREAD | FWRITE, -1, -1, 0, efbuf);
error = 0;
if (fdp == NULL)
goto fail;
+ efbuf->fdp = fdp;
FILEDESC_SLOCK(fdp);
/* working directory */
if (fdp->fd_cdir != NULL) {
vref(fdp->fd_cdir);
data = fdp->fd_cdir;
- FILEDESC_SUNLOCK(fdp);
export_fd_to_sb(data, KF_TYPE_VNODE, KF_FD_TYPE_CWD,
- FREAD, -1, -1, 0, kif, sb, &remainder);
- FILEDESC_SLOCK(fdp);
+ FREAD, -1, -1, 0, efbuf);
}
/* root directory */
if (fdp->fd_rdir != NULL) {
vref(fdp->fd_rdir);
data = fdp->fd_rdir;
- FILEDESC_SUNLOCK(fdp);
export_fd_to_sb(data, KF_TYPE_VNODE, KF_FD_TYPE_ROOT,
- FREAD, -1, -1, 0, kif, sb, &remainder);
- FILEDESC_SLOCK(fdp);
+ FREAD, -1, -1, 0, efbuf);
}
/* jail directory */
if (fdp->fd_jdir != NULL) {
vref(fdp->fd_jdir);
data = fdp->fd_jdir;
- FILEDESC_SUNLOCK(fdp);
export_fd_to_sb(data, KF_TYPE_VNODE, KF_FD_TYPE_JAIL,
- FREAD, -1, -1, 0, kif, sb, &remainder);
- FILEDESC_SLOCK(fdp);
+ FREAD, -1, -1, 0, efbuf);
}
for (i = 0; i < fdp->fd_nfiles; i++) {
if ((fp = fdp->fd_ofiles[i].fde_file) == NULL)
@@ -3427,20 +3442,15 @@ kern_proc_filedesc_out(struct proc *p, struct sbuf *sb, ssize_t maxlen)
* re-validate and re-evaluate its properties when
* the loop continues.
*/
- if (type == KF_TYPE_VNODE || type == KF_TYPE_FIFO)
- FILEDESC_SUNLOCK(fdp);
error = export_fd_to_sb(data, type, i, fflags, refcnt,
- offset, fd_cap_rights, kif, sb, &remainder);
- if (type == KF_TYPE_VNODE || type == KF_TYPE_FIFO)
- FILEDESC_SLOCK(fdp);
+ offset, fd_cap_rights, efbuf);
if (error)
break;
}
FILEDESC_SUNLOCK(fdp);
+ fddrop(fdp);
fail:
- if (fdp != NULL)
- fddrop(fdp);
- free(kif, M_TEMP);
+ free(efbuf, M_TEMP);
return (error);
}
diff --git a/sys/kern/kern_intr.c b/sys/kern/kern_intr.c
index 8d63c9b..63d8469 100644
--- a/sys/kern/kern_intr.c
+++ b/sys/kern/kern_intr.c
@@ -841,7 +841,7 @@ ok:
* again and remove this handler if it has already passed
* it on the list.
*/
- ie->ie_thread->it_need = 1;
+ atomic_store_rel_int(&ie->ie_thread->it_need, 1);
} else
TAILQ_REMOVE(&ie->ie_handlers, handler, ih_next);
thread_unlock(ie->ie_thread->it_thread);
@@ -912,7 +912,7 @@ intr_event_schedule_thread(struct intr_event *ie)
* running. Then, lock the thread and see if we actually need to
* put it on the runqueue.
*/
- it->it_need = 1;
+ atomic_store_rel_int(&it->it_need, 1);
thread_lock(td);
if (TD_AWAITING_INTR(td)) {
CTR3(KTR_INTR, "%s: schedule pid %d (%s)", __func__, p->p_pid,
@@ -990,7 +990,7 @@ ok:
* again and remove this handler if it has already passed
* it on the list.
*/
- it->it_need = 1;
+ atomic_store_rel_int(&it->it_need, 1);
} else
TAILQ_REMOVE(&ie->ie_handlers, handler, ih_next);
thread_unlock(it->it_thread);
@@ -1066,7 +1066,7 @@ intr_event_schedule_thread(struct intr_event *ie, struct intr_thread *it)
* running. Then, lock the thread and see if we actually need to
* put it on the runqueue.
*/
- it->it_need = 1;
+ atomic_store_rel_int(&it->it_need, 1);
thread_lock(td);
if (TD_AWAITING_INTR(td)) {
CTR3(KTR_INTR, "%s: schedule pid %d (%s)", __func__, p->p_pid,
@@ -1247,7 +1247,7 @@ intr_event_execute_handlers(struct proc *p, struct intr_event *ie)
* interrupt threads always invoke all of their handlers.
*/
if (ie->ie_flags & IE_SOFT) {
- if (!ih->ih_need)
+ if (atomic_load_acq_int(&ih->ih_need) == 0)
continue;
else
atomic_store_rel_int(&ih->ih_need, 0);
@@ -1349,7 +1349,7 @@ ithread_loop(void *arg)
* we are running, it will set it_need to note that we
* should make another pass.
*/
- while (ithd->it_need) {
+ while (atomic_load_acq_int(&ithd->it_need) != 0) {
/*
* This might need a full read and write barrier
* to make sure that this write posts before any
@@ -1368,7 +1368,8 @@ ithread_loop(void *arg)
* set again, so we have to check it again.
*/
thread_lock(td);
- if (!ithd->it_need && !(ithd->it_flags & (IT_DEAD | IT_WAIT))) {
+ if ((atomic_load_acq_int(&ithd->it_need) == 0) &&
+ !(ithd->it_flags & (IT_DEAD | IT_WAIT))) {
TD_SET_IWAIT(td);
ie->ie_count = 0;
mi_switch(SW_VOL | SWT_IWAIT, NULL);
@@ -1529,7 +1530,7 @@ ithread_loop(void *arg)
* we are running, it will set it_need to note that we
* should make another pass.
*/
- while (ithd->it_need) {
+ while (atomic_load_acq_int(&ithd->it_need) != 0) {
/*
* This might need a full read and write barrier
* to make sure that this write posts before any
@@ -1551,7 +1552,8 @@ ithread_loop(void *arg)
* set again, so we have to check it again.
*/
thread_lock(td);
- if (!ithd->it_need && !(ithd->it_flags & (IT_DEAD | IT_WAIT))) {
+ if ((atomic_load_acq_int(&ithd->it_need) == 0) &&
+ !(ithd->it_flags & (IT_DEAD | IT_WAIT))) {
TD_SET_IWAIT(td);
ie->ie_count = 0;
mi_switch(SW_VOL | SWT_IWAIT, NULL);
diff --git a/sys/kern/kern_synch.c b/sys/kern/kern_synch.c
index 33d2db5..84b1c61 100644
--- a/sys/kern/kern_synch.c
+++ b/sys/kern/kern_synch.c
@@ -132,7 +132,7 @@ sleepinit(void)
/*
* General sleep call. Suspends the current thread until a wakeup is
* performed on the specified identifier. The thread will then be made
- * runnable with the specified priority. Sleeps at most timo/hz seconds
+ * runnable with the specified priority. Sleeps at most sbt units of time
* (0 means no timeout). If pri includes the PCATCH flag, let signals
* interrupt the sleep, otherwise ignore them while sleeping. Returns 0 if
* awakened, EWOULDBLOCK if the timeout expires. If PCATCH is set and a
diff --git a/sys/kern/subr_bus.c b/sys/kern/subr_bus.c
index 1d45f28..f196c8b 100644
--- a/sys/kern/subr_bus.c
+++ b/sys/kern/subr_bus.c
@@ -3316,6 +3316,48 @@ resource_list_release(struct resource_list *rl, device_t bus, device_t child,
}
/**
+ * @brief Release all active resources of a given type
+ *
+ * Release all active resources of a specified type. This is intended
+ * to be used to cleanup resources leaked by a driver after detach or
+ * a failed attach.
+ *
+ * @param rl the resource list which was allocated from
+ * @param bus the parent device of @p child
+ * @param child the device whose active resources are being released
+ * @param type the type of resources to release
+ *
+ * @retval 0 success
+ * @retval EBUSY at least one resource was active
+ */
+int
+resource_list_release_active(struct resource_list *rl, device_t bus,
+ device_t child, int type)
+{
+ struct resource_list_entry *rle;
+ int error, retval;
+
+ retval = 0;
+ STAILQ_FOREACH(rle, rl, link) {
+ if (rle->type != type)
+ continue;
+ if (rle->res == NULL)
+ continue;
+ if ((rle->flags & (RLE_RESERVED | RLE_ALLOCATED)) ==
+ RLE_RESERVED)
+ continue;
+ retval = EBUSY;
+ error = resource_list_release(rl, bus, child, type,
+ rman_get_rid(rle->res), rle->res);
+ if (error != 0)
+ device_printf(bus,
+ "Failed to release active resource: %d\n", error);
+ }
+ return (retval);
+}
+
+
+/**
* @brief Fully release a reserved resource
*
* Fully releases a resource reserved via resource_list_reserve().
diff --git a/sys/kern/subr_counter.c b/sys/kern/subr_counter.c
index a98ed40..2656ed7 100644
--- a/sys/kern/subr_counter.c
+++ b/sys/kern/subr_counter.c
@@ -29,34 +29,32 @@ __FBSDID("$FreeBSD$");
#include <sys/param.h>
#include <sys/systm.h>
-#include <sys/counter.h>
#include <sys/kernel.h>
+#include <sys/lock.h>
+#include <sys/mutex.h>
+#include <sys/proc.h>
+#include <sys/sched.h>
#include <sys/smp.h>
#include <sys/sysctl.h>
#include <vm/uma.h>
+
+#define IN_SUBR_COUNTER_C
+#include <sys/counter.h>
static uma_zone_t uint64_pcpu_zone;
void
counter_u64_zero(counter_u64_t c)
{
- int i;
- for (i = 0; i < mp_ncpus; i++)
- *(uint64_t *)((char *)c + sizeof(struct pcpu) * i) = 0;
+ counter_u64_zero_inline(c);
}
uint64_t
counter_u64_fetch(counter_u64_t c)
{
- uint64_t r;
- int i;
- r = 0;
- for (i = 0; i < mp_ncpus; i++)
- r += *(uint64_t *)((char *)c + sizeof(struct pcpu) * i);
-
- return (r);
+ return (counter_u64_fetch_inline(c));
}
counter_u64_t
diff --git a/sys/kern/subr_vmem.c b/sys/kern/subr_vmem.c
new file mode 100644
index 0000000..b48dbca
--- /dev/null
+++ b/sys/kern/subr_vmem.c
@@ -0,0 +1,1372 @@
+/*-
+ * Copyright (c)2006,2007,2008,2009 YAMAMOTO Takashi,
+ * Copyright (c) 2013 EMC Corp.
+ * All rights reserved.
+ *
+ * Redistribution and use in source and binary forms, with or without
+ * modification, are permitted provided that the following conditions
+ * are met:
+ * 1. Redistributions of source code must retain the above copyright
+ * notice, this list of conditions and the following disclaimer.
+ * 2. Redistributions in binary form must reproduce the above copyright
+ * notice, this list of conditions and the following disclaimer in the
+ * documentation and/or other materials provided with the distribution.
+ *
+ * THIS SOFTWARE IS PROVIDED BY THE AUTHOR AND CONTRIBUTORS ``AS IS'' AND
+ * ANY EXPRESS OR IMPLIED WARRANTIES, INCLUDING, BUT NOT LIMITED TO, THE
+ * IMPLIED WARRANTIES OF MERCHANTABILITY AND FITNESS FOR A PARTICULAR PURPOSE
+ * ARE DISCLAIMED. IN NO EVENT SHALL THE AUTHOR OR CONTRIBUTORS BE LIABLE
+ * FOR ANY DIRECT, INDIRECT, INCIDENTAL, SPECIAL, EXEMPLARY, OR CONSEQUENTIAL
+ * DAMAGES (INCLUDING, BUT NOT LIMITED TO, PROCUREMENT OF SUBSTITUTE GOODS
+ * OR SERVICES; LOSS OF USE, DATA, OR PROFITS; OR BUSINESS INTERRUPTION)
+ * HOWEVER CAUSED AND ON ANY THEORY OF LIABILITY, WHETHER IN CONTRACT, STRICT
+ * LIABILITY, OR TORT (INCLUDING NEGLIGENCE OR OTHERWISE) ARISING IN ANY WAY
+ * OUT OF THE USE OF THIS SOFTWARE, EVEN IF ADVISED OF THE POSSIBILITY OF
+ * SUCH DAMAGE.
+ */
+
+/*
+ * From:
+ * $NetBSD: vmem_impl.h,v 1.2 2013/01/29 21:26:24 para Exp $
+ * $NetBSD: subr_vmem.c,v 1.83 2013/03/06 11:20:10 yamt Exp $
+ */
+
+/*
+ * reference:
+ * - Magazines and Vmem: Extending the Slab Allocator
+ * to Many CPUs and Arbitrary Resources
+ * http://www.usenix.org/event/usenix01/bonwick.html
+ */
+
+#include <sys/cdefs.h>
+__FBSDID("$FreeBSD$");
+
+#include "opt_ddb.h"
+
+#include <sys/param.h>
+#include <sys/systm.h>
+#include <sys/kernel.h>
+#include <sys/queue.h>
+#include <sys/callout.h>
+#include <sys/hash.h>
+#include <sys/lock.h>
+#include <sys/malloc.h>
+#include <sys/mutex.h>
+#include <sys/smp.h>
+#include <sys/condvar.h>
+#include <sys/taskqueue.h>
+#include <sys/vmem.h>
+
+#include <vm/uma.h>
+#include <vm/vm.h>
+#include <vm/pmap.h>
+#include <vm/vm_map.h>
+#include <vm/vm_kern.h>
+#include <vm/vm_extern.h>
+#include <vm/vm_param.h>
+#include <vm/vm_pageout.h>
+
+#define VMEM_MAXORDER (sizeof(vmem_size_t) * NBBY)
+
+#define VMEM_HASHSIZE_MIN 16
+#define VMEM_HASHSIZE_MAX 131072
+
+#define VMEM_QCACHE_IDX_MAX 16
+
+#define VMEM_FITMASK (M_BESTFIT | M_FIRSTFIT)
+
+#define VMEM_FLAGS \
+ (M_NOWAIT | M_WAITOK | M_USE_RESERVE | M_NOVM | M_BESTFIT | M_FIRSTFIT)
+
+#define BT_FLAGS (M_NOWAIT | M_WAITOK | M_USE_RESERVE | M_NOVM)
+
+#define QC_NAME_MAX 16
+
+/*
+ * Data structures private to vmem.
+ */
+MALLOC_DEFINE(M_VMEM, "vmem", "vmem internal structures");
+
+typedef struct vmem_btag bt_t;
+
+TAILQ_HEAD(vmem_seglist, vmem_btag);
+LIST_HEAD(vmem_freelist, vmem_btag);
+LIST_HEAD(vmem_hashlist, vmem_btag);
+
+struct qcache {
+ uma_zone_t qc_cache;
+ vmem_t *qc_vmem;
+ vmem_size_t qc_size;
+ char qc_name[QC_NAME_MAX];
+};
+typedef struct qcache qcache_t;
+#define QC_POOL_TO_QCACHE(pool) ((qcache_t *)(pool->pr_qcache))
+
+#define VMEM_NAME_MAX 16
+
+/* vmem arena */
+struct vmem {
+ struct mtx_padalign vm_lock;
+ struct cv vm_cv;
+ char vm_name[VMEM_NAME_MAX+1];
+ LIST_ENTRY(vmem) vm_alllist;
+ struct vmem_hashlist vm_hash0[VMEM_HASHSIZE_MIN];
+ struct vmem_freelist vm_freelist[VMEM_MAXORDER];
+ struct vmem_seglist vm_seglist;
+ struct vmem_hashlist *vm_hashlist;
+ vmem_size_t vm_hashsize;
+
+ /* Constant after init */
+ vmem_size_t vm_qcache_max;
+ vmem_size_t vm_quantum_mask;
+ vmem_size_t vm_import_quantum;
+ int vm_quantum_shift;
+
+ /* Written on alloc/free */
+ LIST_HEAD(, vmem_btag) vm_freetags;
+ int vm_nfreetags;
+ int vm_nbusytag;
+ vmem_size_t vm_inuse;
+ vmem_size_t vm_size;
+
+ /* Used on import. */
+ vmem_import_t *vm_importfn;
+ vmem_release_t *vm_releasefn;
+ void *vm_arg;
+
+ /* Space exhaustion callback. */
+ vmem_reclaim_t *vm_reclaimfn;
+
+ /* quantum cache */
+ qcache_t vm_qcache[VMEM_QCACHE_IDX_MAX];
+};
+
+/* boundary tag */
+struct vmem_btag {
+ TAILQ_ENTRY(vmem_btag) bt_seglist;
+ union {
+ LIST_ENTRY(vmem_btag) u_freelist; /* BT_TYPE_FREE */
+ LIST_ENTRY(vmem_btag) u_hashlist; /* BT_TYPE_BUSY */
+ } bt_u;
+#define bt_hashlist bt_u.u_hashlist
+#define bt_freelist bt_u.u_freelist
+ vmem_addr_t bt_start;
+ vmem_size_t bt_size;
+ int bt_type;
+};
+
+#define BT_TYPE_SPAN 1 /* Allocated from importfn */
+#define BT_TYPE_SPAN_STATIC 2 /* vmem_add() or create. */
+#define BT_TYPE_FREE 3 /* Available space. */
+#define BT_TYPE_BUSY 4 /* Used space. */
+#define BT_ISSPAN_P(bt) ((bt)->bt_type <= BT_TYPE_SPAN_STATIC)
+
+#define BT_END(bt) ((bt)->bt_start + (bt)->bt_size - 1)
+
+#if defined(DIAGNOSTIC)
+static void vmem_check(vmem_t *);
+#endif
+
+static struct callout vmem_periodic_ch;
+static int vmem_periodic_interval;
+static struct task vmem_periodic_wk;
+
+static struct mtx_padalign vmem_list_lock;
+static LIST_HEAD(, vmem) vmem_list = LIST_HEAD_INITIALIZER(vmem_list);
+
+/* ---- misc */
+#define VMEM_CONDVAR_INIT(vm, wchan) cv_init(&vm->vm_cv, wchan)
+#define VMEM_CONDVAR_DESTROY(vm) cv_destroy(&vm->vm_cv)
+#define VMEM_CONDVAR_WAIT(vm) cv_wait(&vm->vm_cv, &vm->vm_lock)
+#define VMEM_CONDVAR_BROADCAST(vm) cv_broadcast(&vm->vm_cv)
+
+
+#define VMEM_LOCK(vm) mtx_lock(&vm->vm_lock)
+#define VMEM_TRYLOCK(vm) mtx_trylock(&vm->vm_lock)
+#define VMEM_UNLOCK(vm) mtx_unlock(&vm->vm_lock)
+#define VMEM_LOCK_INIT(vm, name) mtx_init(&vm->vm_lock, (name), NULL, MTX_DEF)
+#define VMEM_LOCK_DESTROY(vm) mtx_destroy(&vm->vm_lock)
+#define VMEM_ASSERT_LOCKED(vm) mtx_assert(&vm->vm_lock, MA_OWNED);
+
+#define VMEM_ALIGNUP(addr, align) (-(-(addr) & -(align)))
+
+#define VMEM_CROSS_P(addr1, addr2, boundary) \
+ ((((addr1) ^ (addr2)) & -(boundary)) != 0)
+
+#define ORDER2SIZE(order) ((vmem_size_t)1 << (order))
+#define SIZE2ORDER(size) ((int)flsl(size) - 1)
+
+/*
+ * Maximum number of boundary tags that may be required to satisfy an
+ * allocation. Two may be required to import. Another two may be
+ * required to clip edges.
+ */
+#define BT_MAXALLOC 4
+
+/*
+ * Max free limits the number of locally cached boundary tags. We
+ * just want to avoid hitting the zone allocator for every call.
+ */
+#define BT_MAXFREE (BT_MAXALLOC * 8)
+
+/* Allocator for boundary tags. */
+static uma_zone_t vmem_bt_zone;
+
+/* boot time arena storage. */
+static struct vmem buffer_arena_storage;
+static struct vmem transient_arena_storage;
+vmem_t *buffer_arena = &buffer_arena_storage;
+vmem_t *transient_arena = &transient_arena_storage;
+
+/*
+ * Fill the vmem's boundary tag cache. We guarantee that boundary tag
+ * allocation will not fail once bt_fill() passes. To do so we cache
+ * at least the maximum possible tag allocations in the arena.
+ */
+static int
+bt_fill(vmem_t *vm, int flags)
+{
+ bt_t *bt;
+
+ VMEM_ASSERT_LOCKED(vm);
+
+ /*
+ * Loop until we meet the reserve. To minimize the lock shuffle
+ * and prevent simultaneous fills we first try a NOWAIT regardless
+ * of the caller's flags. Specify M_NOVM so we don't recurse while
+ * holding a vmem lock.
+ */
+ while (vm->vm_nfreetags < BT_MAXALLOC) {
+ bt = uma_zalloc(vmem_bt_zone,
+ (flags & M_USE_RESERVE) | M_NOWAIT | M_NOVM);
+ if (bt == NULL) {
+ VMEM_UNLOCK(vm);
+ bt = uma_zalloc(vmem_bt_zone, flags);
+ VMEM_LOCK(vm);
+ if (bt == NULL && (flags & M_NOWAIT) != 0)
+ break;
+ }
+ LIST_INSERT_HEAD(&vm->vm_freetags, bt, bt_freelist);
+ vm->vm_nfreetags++;
+ }
+
+ if (vm->vm_nfreetags < BT_MAXALLOC)
+ return ENOMEM;
+
+ return 0;
+}
+
+/*
+ * Pop a tag off of the freetag stack.
+ */
+static bt_t *
+bt_alloc(vmem_t *vm)
+{
+ bt_t *bt;
+
+ VMEM_ASSERT_LOCKED(vm);
+ bt = LIST_FIRST(&vm->vm_freetags);
+ MPASS(bt != NULL);
+ LIST_REMOVE(bt, bt_freelist);
+ vm->vm_nfreetags--;
+
+ return bt;
+}
+
+/*
+ * Trim the per-vmem free list. Returns with the lock released to
+ * avoid allocator recursions.
+ */
+static void
+bt_freetrim(vmem_t *vm, int freelimit)
+{
+ LIST_HEAD(, vmem_btag) freetags;
+ bt_t *bt;
+
+ LIST_INIT(&freetags);
+ VMEM_ASSERT_LOCKED(vm);
+ while (vm->vm_nfreetags > freelimit) {
+ bt = LIST_FIRST(&vm->vm_freetags);
+ LIST_REMOVE(bt, bt_freelist);
+ vm->vm_nfreetags--;
+ LIST_INSERT_HEAD(&freetags, bt, bt_freelist);
+ }
+ VMEM_UNLOCK(vm);
+ while ((bt = LIST_FIRST(&freetags)) != NULL) {
+ LIST_REMOVE(bt, bt_freelist);
+ uma_zfree(vmem_bt_zone, bt);
+ }
+}
+
+static inline void
+bt_free(vmem_t *vm, bt_t *bt)
+{
+
+ VMEM_ASSERT_LOCKED(vm);
+ MPASS(LIST_FIRST(&vm->vm_freetags) != bt);
+ LIST_INSERT_HEAD(&vm->vm_freetags, bt, bt_freelist);
+ vm->vm_nfreetags++;
+}
+
+/*
+ * freelist[0] ... [1, 1]
+ * freelist[1] ... [2, 3]
+ * freelist[2] ... [4, 7]
+ * freelist[3] ... [8, 15]
+ * :
+ * freelist[n] ... [(1 << n), (1 << (n + 1)) - 1]
+ * :
+ */
+
+static struct vmem_freelist *
+bt_freehead_tofree(vmem_t *vm, vmem_size_t size)
+{
+ const vmem_size_t qsize = size >> vm->vm_quantum_shift;
+ const int idx = SIZE2ORDER(qsize);
+
+ MPASS(size != 0 && qsize != 0);
+ MPASS((size & vm->vm_quantum_mask) == 0);
+ MPASS(idx >= 0);
+ MPASS(idx < VMEM_MAXORDER);
+
+ return &vm->vm_freelist[idx];
+}
+
+/*
+ * bt_freehead_toalloc: return the freelist for the given size and allocation
+ * strategy.
+ *
+ * For M_FIRSTFIT, return the list in which any blocks are large enough
+ * for the requested size. otherwise, return the list which can have blocks
+ * large enough for the requested size.
+ */
+static struct vmem_freelist *
+bt_freehead_toalloc(vmem_t *vm, vmem_size_t size, int strat)
+{
+ const vmem_size_t qsize = size >> vm->vm_quantum_shift;
+ int idx = SIZE2ORDER(qsize);
+
+ MPASS(size != 0 && qsize != 0);
+ MPASS((size & vm->vm_quantum_mask) == 0);
+
+ if (strat == M_FIRSTFIT && ORDER2SIZE(idx) != qsize) {
+ idx++;
+ /* check too large request? */
+ }
+ MPASS(idx >= 0);
+ MPASS(idx < VMEM_MAXORDER);
+
+ return &vm->vm_freelist[idx];
+}
+
+/* ---- boundary tag hash */
+
+static struct vmem_hashlist *
+bt_hashhead(vmem_t *vm, vmem_addr_t addr)
+{
+ struct vmem_hashlist *list;
+ unsigned int hash;
+
+ hash = hash32_buf(&addr, sizeof(addr), 0);
+ list = &vm->vm_hashlist[hash % vm->vm_hashsize];
+
+ return list;
+}
+
+static bt_t *
+bt_lookupbusy(vmem_t *vm, vmem_addr_t addr)
+{
+ struct vmem_hashlist *list;
+ bt_t *bt;
+
+ VMEM_ASSERT_LOCKED(vm);
+ list = bt_hashhead(vm, addr);
+ LIST_FOREACH(bt, list, bt_hashlist) {
+ if (bt->bt_start == addr) {
+ break;
+ }
+ }
+
+ return bt;
+}
+
+static void
+bt_rembusy(vmem_t *vm, bt_t *bt)
+{
+
+ VMEM_ASSERT_LOCKED(vm);
+ MPASS(vm->vm_nbusytag > 0);
+ vm->vm_inuse -= bt->bt_size;
+ vm->vm_nbusytag--;
+ LIST_REMOVE(bt, bt_hashlist);
+}
+
+static void
+bt_insbusy(vmem_t *vm, bt_t *bt)
+{
+ struct vmem_hashlist *list;
+
+ VMEM_ASSERT_LOCKED(vm);
+ MPASS(bt->bt_type == BT_TYPE_BUSY);
+
+ list = bt_hashhead(vm, bt->bt_start);
+ LIST_INSERT_HEAD(list, bt, bt_hashlist);
+ vm->vm_nbusytag++;
+ vm->vm_inuse += bt->bt_size;
+}
+
+/* ---- boundary tag list */
+
+static void
+bt_remseg(vmem_t *vm, bt_t *bt)
+{
+
+ TAILQ_REMOVE(&vm->vm_seglist, bt, bt_seglist);
+ bt_free(vm, bt);
+}
+
+static void
+bt_insseg(vmem_t *vm, bt_t *bt, bt_t *prev)
+{
+
+ TAILQ_INSERT_AFTER(&vm->vm_seglist, prev, bt, bt_seglist);
+}
+
+static void
+bt_insseg_tail(vmem_t *vm, bt_t *bt)
+{
+
+ TAILQ_INSERT_TAIL(&vm->vm_seglist, bt, bt_seglist);
+}
+
+static void
+bt_remfree(vmem_t *vm, bt_t *bt)
+{
+
+ MPASS(bt->bt_type == BT_TYPE_FREE);
+
+ LIST_REMOVE(bt, bt_freelist);
+}
+
+static void
+bt_insfree(vmem_t *vm, bt_t *bt)
+{
+ struct vmem_freelist *list;
+
+ list = bt_freehead_tofree(vm, bt->bt_size);
+ LIST_INSERT_HEAD(list, bt, bt_freelist);
+}
+
+/* ---- vmem internal functions */
+
+/*
+ * Import from the arena into the quantum cache in UMA.
+ */
+static int
+qc_import(void *arg, void **store, int cnt, int flags)
+{
+ qcache_t *qc;
+ vmem_addr_t addr;
+ int i;
+
+ qc = arg;
+ flags |= M_BESTFIT;
+ for (i = 0; i < cnt; i++) {
+ if (vmem_xalloc(qc->qc_vmem, qc->qc_size, 0, 0, 0,
+ VMEM_ADDR_MIN, VMEM_ADDR_MAX, flags, &addr) != 0)
+ break;
+ store[i] = (void *)addr;
+ /* Only guarantee one allocation. */
+ flags &= ~M_WAITOK;
+ flags |= M_NOWAIT;
+ }
+ return i;
+}
+
+/*
+ * Release memory from the UMA cache to the arena.
+ */
+static void
+qc_release(void *arg, void **store, int cnt)
+{
+ qcache_t *qc;
+ int i;
+
+ qc = arg;
+ for (i = 0; i < cnt; i++)
+ vmem_xfree(qc->qc_vmem, (vmem_addr_t)store[i], qc->qc_size);
+}
+
+static void
+qc_init(vmem_t *vm, vmem_size_t qcache_max)
+{
+ qcache_t *qc;
+ vmem_size_t size;
+ int qcache_idx_max;
+ int i;
+
+ MPASS((qcache_max & vm->vm_quantum_mask) == 0);
+ qcache_idx_max = MIN(qcache_max >> vm->vm_quantum_shift,
+ VMEM_QCACHE_IDX_MAX);
+ vm->vm_qcache_max = qcache_idx_max << vm->vm_quantum_shift;
+ for (i = 0; i < qcache_idx_max; i++) {
+ qc = &vm->vm_qcache[i];
+ size = (i + 1) << vm->vm_quantum_shift;
+ snprintf(qc->qc_name, sizeof(qc->qc_name), "%s-%zu",
+ vm->vm_name, size);
+ qc->qc_vmem = vm;
+ qc->qc_size = size;
+ qc->qc_cache = uma_zcache_create(qc->qc_name, size,
+ NULL, NULL, NULL, NULL, qc_import, qc_release, qc,
+ UMA_ZONE_VM);
+ MPASS(qc->qc_cache);
+ }
+}
+
+static void
+qc_destroy(vmem_t *vm)
+{
+ int qcache_idx_max;
+ int i;
+
+ qcache_idx_max = vm->vm_qcache_max >> vm->vm_quantum_shift;
+ for (i = 0; i < qcache_idx_max; i++)
+ uma_zdestroy(vm->vm_qcache[i].qc_cache);
+}
+
+static void
+qc_drain(vmem_t *vm)
+{
+ int qcache_idx_max;
+ int i;
+
+ qcache_idx_max = vm->vm_qcache_max >> vm->vm_quantum_shift;
+ for (i = 0; i < qcache_idx_max; i++)
+ zone_drain(vm->vm_qcache[i].qc_cache);
+}
+
+void
+vmem_startup(void)
+{
+
+ mtx_init(&vmem_list_lock, "vmem list lock", NULL, MTX_DEF);
+ vmem_bt_zone = uma_zcreate("vmem btag",
+ sizeof(struct vmem_btag), NULL, NULL, NULL, NULL,
+ UMA_ALIGN_PTR, UMA_ZONE_VM);
+}
+
+/* ---- rehash */
+
+static int
+vmem_rehash(vmem_t *vm, vmem_size_t newhashsize)
+{
+ bt_t *bt;
+ int i;
+ struct vmem_hashlist *newhashlist;
+ struct vmem_hashlist *oldhashlist;
+ vmem_size_t oldhashsize;
+
+ MPASS(newhashsize > 0);
+
+ newhashlist = malloc(sizeof(struct vmem_hashlist) * newhashsize,
+ M_VMEM, M_NOWAIT);
+ if (newhashlist == NULL)
+ return ENOMEM;
+ for (i = 0; i < newhashsize; i++) {
+ LIST_INIT(&newhashlist[i]);
+ }
+
+ VMEM_LOCK(vm);
+ oldhashlist = vm->vm_hashlist;
+ oldhashsize = vm->vm_hashsize;
+ vm->vm_hashlist = newhashlist;
+ vm->vm_hashsize = newhashsize;
+ if (oldhashlist == NULL) {
+ VMEM_UNLOCK(vm);
+ return 0;
+ }
+ for (i = 0; i < oldhashsize; i++) {
+ while ((bt = LIST_FIRST(&oldhashlist[i])) != NULL) {
+ bt_rembusy(vm, bt);
+ bt_insbusy(vm, bt);
+ }
+ }
+ VMEM_UNLOCK(vm);
+
+ if (oldhashlist != vm->vm_hash0) {
+ free(oldhashlist, M_VMEM);
+ }
+
+ return 0;
+}
+
+static void
+vmem_periodic_kick(void *dummy)
+{
+
+ taskqueue_enqueue(taskqueue_thread, &vmem_periodic_wk);
+}
+
+static void
+vmem_periodic(void *unused, int pending)
+{
+ vmem_t *vm;
+ vmem_size_t desired;
+ vmem_size_t current;
+
+ mtx_lock(&vmem_list_lock);
+ LIST_FOREACH(vm, &vmem_list, vm_alllist) {
+#ifdef DIAGNOSTIC
+ /* Convenient time to verify vmem state. */
+ VMEM_LOCK(vm);
+ vmem_check(vm);
+ VMEM_UNLOCK(vm);
+#endif
+ desired = 1 << flsl(vm->vm_nbusytag);
+ desired = MIN(MAX(desired, VMEM_HASHSIZE_MIN),
+ VMEM_HASHSIZE_MAX);
+ current = vm->vm_hashsize;
+
+ /* Grow in powers of two. Shrink less aggressively. */
+ if (desired >= current * 2 || desired * 4 <= current)
+ vmem_rehash(vm, desired);
+ }
+ mtx_unlock(&vmem_list_lock);
+
+ callout_reset(&vmem_periodic_ch, vmem_periodic_interval,
+ vmem_periodic_kick, NULL);
+}
+
+static void
+vmem_start_callout(void *unused)
+{
+
+ TASK_INIT(&vmem_periodic_wk, 0, vmem_periodic, NULL);
+ vmem_periodic_interval = hz * 10;
+ callout_init(&vmem_periodic_ch, CALLOUT_MPSAFE);
+ callout_reset(&vmem_periodic_ch, vmem_periodic_interval,
+ vmem_periodic_kick, NULL);
+}
+SYSINIT(vfs, SI_SUB_CONFIGURE, SI_ORDER_ANY, vmem_start_callout, NULL);
+
+static void
+vmem_add1(vmem_t *vm, vmem_addr_t addr, vmem_size_t size, int flags, int type)
+{
+ bt_t *btspan;
+ bt_t *btfree;
+
+ MPASS(type == BT_TYPE_SPAN || type == BT_TYPE_SPAN_STATIC);
+
+ btspan = bt_alloc(vm);
+ btspan->bt_type = type;
+ btspan->bt_start = addr;
+ btspan->bt_size = size;
+
+ btfree = bt_alloc(vm);
+ btfree->bt_type = BT_TYPE_FREE;
+ btfree->bt_start = addr;
+ btfree->bt_size = size;
+
+ bt_insseg_tail(vm, btspan);
+ bt_insseg(vm, btfree, btspan);
+ bt_insfree(vm, btfree);
+ vm->vm_size += size;
+}
+
+static void
+vmem_destroy1(vmem_t *vm)
+{
+ bt_t *bt;
+
+ /*
+ * Drain per-cpu quantum caches.
+ */
+ qc_destroy(vm);
+
+ /*
+ * The vmem should now only contain empty segments.
+ */
+ VMEM_LOCK(vm);
+ MPASS(vm->vm_nbusytag == 0);
+
+ while ((bt = TAILQ_FIRST(&vm->vm_seglist)) != NULL)
+ bt_remseg(vm, bt);
+
+ if (vm->vm_hashlist != NULL && vm->vm_hashlist != vm->vm_hash0)
+ free(vm->vm_hashlist, M_VMEM);
+
+ bt_freetrim(vm, 0);
+
+ VMEM_CONDVAR_DESTROY(vm);
+ VMEM_LOCK_DESTROY(vm);
+ free(vm, M_VMEM);
+}
+
+static int
+vmem_import(vmem_t *vm, vmem_size_t size, int flags)
+{
+ vmem_addr_t addr;
+ int error;
+
+ if (vm->vm_importfn == NULL)
+ return EINVAL;
+
+ size = roundup(size, vm->vm_import_quantum);
+
+ /*
+ * Hide MAXALLOC tags so we're guaranteed to be able to add this
+ * span and the tag we want to allocate from it.
+ */
+ MPASS(vm->vm_nfreetags >= BT_MAXALLOC);
+ vm->vm_nfreetags -= BT_MAXALLOC;
+ VMEM_UNLOCK(vm);
+ error = (vm->vm_importfn)(vm->vm_arg, size, flags, &addr);
+ VMEM_LOCK(vm);
+ vm->vm_nfreetags += BT_MAXALLOC;
+ if (error)
+ return ENOMEM;
+
+ vmem_add1(vm, addr, size, flags, BT_TYPE_SPAN);
+
+ return 0;
+}
+
+/*
+ * vmem_fit: check if a bt can satisfy the given restrictions.
+ *
+ * it's a caller's responsibility to ensure the region is big enough
+ * before calling us.
+ */
+static int
+vmem_fit(const bt_t *bt, vmem_size_t size, vmem_size_t align,
+ vmem_size_t phase, vmem_size_t nocross, vmem_addr_t minaddr,
+ vmem_addr_t maxaddr, vmem_addr_t *addrp)
+{
+ vmem_addr_t start;
+ vmem_addr_t end;
+
+ MPASS(size > 0);
+ MPASS(bt->bt_size >= size); /* caller's responsibility */
+
+ /*
+ * XXX assumption: vmem_addr_t and vmem_size_t are
+ * unsigned integer of the same size.
+ */
+
+ start = bt->bt_start;
+ if (start < minaddr) {
+ start = minaddr;
+ }
+ end = BT_END(bt);
+ if (end > maxaddr)
+ end = maxaddr;
+ if (start > end)
+ return (ENOMEM);
+
+ start = VMEM_ALIGNUP(start - phase, align) + phase;
+ if (start < bt->bt_start)
+ start += align;
+ if (VMEM_CROSS_P(start, start + size - 1, nocross)) {
+ MPASS(align < nocross);
+ start = VMEM_ALIGNUP(start - phase, nocross) + phase;
+ }
+ if (start <= end && end - start >= size - 1) {
+ MPASS((start & (align - 1)) == phase);
+ MPASS(!VMEM_CROSS_P(start, start + size - 1, nocross));
+ MPASS(minaddr <= start);
+ MPASS(maxaddr == 0 || start + size - 1 <= maxaddr);
+ MPASS(bt->bt_start <= start);
+ MPASS(BT_END(bt) - start >= size - 1);
+ *addrp = start;
+
+ return (0);
+ }
+ return (ENOMEM);
+}
+
+/*
+ * vmem_clip: Trim the boundary tag edges to the requested start and size.
+ */
+static void
+vmem_clip(vmem_t *vm, bt_t *bt, vmem_addr_t start, vmem_size_t size)
+{
+ bt_t *btnew;
+ bt_t *btprev;
+
+ VMEM_ASSERT_LOCKED(vm);
+ MPASS(bt->bt_type == BT_TYPE_FREE);
+ MPASS(bt->bt_size >= size);
+ bt_remfree(vm, bt);
+ if (bt->bt_start != start) {
+ btprev = bt_alloc(vm);
+ btprev->bt_type = BT_TYPE_FREE;
+ btprev->bt_start = bt->bt_start;
+ btprev->bt_size = start - bt->bt_start;
+ bt->bt_start = start;
+ bt->bt_size -= btprev->bt_size;
+ bt_insfree(vm, btprev);
+ bt_insseg(vm, btprev,
+ TAILQ_PREV(bt, vmem_seglist, bt_seglist));
+ }
+ MPASS(bt->bt_start == start);
+ if (bt->bt_size != size && bt->bt_size - size > vm->vm_quantum_mask) {
+ /* split */
+ btnew = bt_alloc(vm);
+ btnew->bt_type = BT_TYPE_BUSY;
+ btnew->bt_start = bt->bt_start;
+ btnew->bt_size = size;
+ bt->bt_start = bt->bt_start + size;
+ bt->bt_size -= size;
+ bt_insfree(vm, bt);
+ bt_insseg(vm, btnew,
+ TAILQ_PREV(bt, vmem_seglist, bt_seglist));
+ bt_insbusy(vm, btnew);
+ bt = btnew;
+ } else {
+ bt->bt_type = BT_TYPE_BUSY;
+ bt_insbusy(vm, bt);
+ }
+ MPASS(bt->bt_size >= size);
+ bt->bt_type = BT_TYPE_BUSY;
+}
+
+/* ---- vmem API */
+
+void
+vmem_set_import(vmem_t *vm, vmem_import_t *importfn,
+ vmem_release_t *releasefn, void *arg, vmem_size_t import_quantum)
+{
+
+ VMEM_LOCK(vm);
+ vm->vm_importfn = importfn;
+ vm->vm_releasefn = releasefn;
+ vm->vm_arg = arg;
+ vm->vm_import_quantum = import_quantum;
+ VMEM_UNLOCK(vm);
+}
+
+void
+vmem_set_reclaim(vmem_t *vm, vmem_reclaim_t *reclaimfn)
+{
+
+ VMEM_LOCK(vm);
+ vm->vm_reclaimfn = reclaimfn;
+ VMEM_UNLOCK(vm);
+}
+
+/*
+ * vmem_init: Initializes vmem arena.
+ */
+vmem_t *
+vmem_init(vmem_t *vm, const char *name, vmem_addr_t base, vmem_size_t size,
+ vmem_size_t quantum, vmem_size_t qcache_max, int flags)
+{
+ int i;
+
+ MPASS(quantum > 0);
+
+ bzero(vm, sizeof(*vm));
+
+ VMEM_CONDVAR_INIT(vm, name);
+ VMEM_LOCK_INIT(vm, name);
+ vm->vm_nfreetags = 0;
+ LIST_INIT(&vm->vm_freetags);
+ strlcpy(vm->vm_name, name, sizeof(vm->vm_name));
+ vm->vm_quantum_mask = quantum - 1;
+ vm->vm_quantum_shift = SIZE2ORDER(quantum);
+ MPASS(ORDER2SIZE(vm->vm_quantum_shift) == quantum);
+ vm->vm_nbusytag = 0;
+ vm->vm_size = 0;
+ vm->vm_inuse = 0;
+ qc_init(vm, qcache_max);
+
+ TAILQ_INIT(&vm->vm_seglist);
+ for (i = 0; i < VMEM_MAXORDER; i++) {
+ LIST_INIT(&vm->vm_freelist[i]);
+ }
+ memset(&vm->vm_hash0, 0, sizeof(vm->vm_hash0));
+ vm->vm_hashsize = VMEM_HASHSIZE_MIN;
+ vm->vm_hashlist = vm->vm_hash0;
+
+ if (size != 0) {
+ if (vmem_add(vm, base, size, flags) != 0) {
+ vmem_destroy1(vm);
+ return NULL;
+ }
+ }
+
+ mtx_lock(&vmem_list_lock);
+ LIST_INSERT_HEAD(&vmem_list, vm, vm_alllist);
+ mtx_unlock(&vmem_list_lock);
+
+ return vm;
+}
+
+/*
+ * vmem_create: create an arena.
+ */
+vmem_t *
+vmem_create(const char *name, vmem_addr_t base, vmem_size_t size,
+ vmem_size_t quantum, vmem_size_t qcache_max, int flags)
+{
+
+ vmem_t *vm;
+
+ vm = malloc(sizeof(*vm), M_VMEM, flags & (M_WAITOK|M_NOWAIT));
+ if (vm == NULL)
+ return (NULL);
+ if (vmem_init(vm, name, base, size, quantum, qcache_max,
+ flags) == NULL) {
+ free(vm, M_VMEM);
+ return (NULL);
+ }
+ return (vm);
+}
+
+void
+vmem_destroy(vmem_t *vm)
+{
+
+ mtx_lock(&vmem_list_lock);
+ LIST_REMOVE(vm, vm_alllist);
+ mtx_unlock(&vmem_list_lock);
+
+ vmem_destroy1(vm);
+}
+
+vmem_size_t
+vmem_roundup_size(vmem_t *vm, vmem_size_t size)
+{
+
+ return (size + vm->vm_quantum_mask) & ~vm->vm_quantum_mask;
+}
+
+/*
+ * vmem_alloc: allocate resource from the arena.
+ */
+int
+vmem_alloc(vmem_t *vm, vmem_size_t size, int flags, vmem_addr_t *addrp)
+{
+ const int strat __unused = flags & VMEM_FITMASK;
+ qcache_t *qc;
+
+ flags &= VMEM_FLAGS;
+ MPASS(size > 0);
+ MPASS(strat == M_BESTFIT || strat == M_FIRSTFIT);
+ if ((flags & M_NOWAIT) == 0)
+ WITNESS_WARN(WARN_GIANTOK | WARN_SLEEPOK, NULL, "vmem_alloc");
+
+ if (size <= vm->vm_qcache_max) {
+ qc = &vm->vm_qcache[(size - 1) >> vm->vm_quantum_shift];
+ *addrp = (vmem_addr_t)uma_zalloc(qc->qc_cache, flags);
+ if (*addrp == 0)
+ return (ENOMEM);
+ return (0);
+ }
+
+ return vmem_xalloc(vm, size, 0, 0, 0, VMEM_ADDR_MIN, VMEM_ADDR_MAX,
+ flags, addrp);
+}
+
+int
+vmem_xalloc(vmem_t *vm, const vmem_size_t size0, vmem_size_t align,
+ const vmem_size_t phase, const vmem_size_t nocross,
+ const vmem_addr_t minaddr, const vmem_addr_t maxaddr, int flags,
+ vmem_addr_t *addrp)
+{
+ const vmem_size_t size = vmem_roundup_size(vm, size0);
+ struct vmem_freelist *list;
+ struct vmem_freelist *first;
+ struct vmem_freelist *end;
+ vmem_size_t avail;
+ bt_t *bt;
+ int error;
+ int strat;
+
+ flags &= VMEM_FLAGS;
+ strat = flags & VMEM_FITMASK;
+ MPASS(size0 > 0);
+ MPASS(size > 0);
+ MPASS(strat == M_BESTFIT || strat == M_FIRSTFIT);
+ MPASS((flags & (M_NOWAIT|M_WAITOK)) != (M_NOWAIT|M_WAITOK));
+ if ((flags & M_NOWAIT) == 0)
+ WITNESS_WARN(WARN_GIANTOK | WARN_SLEEPOK, NULL, "vmem_xalloc");
+ MPASS((align & vm->vm_quantum_mask) == 0);
+ MPASS((align & (align - 1)) == 0);
+ MPASS((phase & vm->vm_quantum_mask) == 0);
+ MPASS((nocross & vm->vm_quantum_mask) == 0);
+ MPASS((nocross & (nocross - 1)) == 0);
+ MPASS((align == 0 && phase == 0) || phase < align);
+ MPASS(nocross == 0 || nocross >= size);
+ MPASS(minaddr <= maxaddr);
+ MPASS(!VMEM_CROSS_P(phase, phase + size - 1, nocross));
+
+ if (align == 0)
+ align = vm->vm_quantum_mask + 1;
+
+ *addrp = 0;
+ end = &vm->vm_freelist[VMEM_MAXORDER];
+ /*
+ * choose a free block from which we allocate.
+ */
+ first = bt_freehead_toalloc(vm, size, strat);
+ VMEM_LOCK(vm);
+ for (;;) {
+ /*
+ * Make sure we have enough tags to complete the
+ * operation.
+ */
+ if (vm->vm_nfreetags < BT_MAXALLOC &&
+ bt_fill(vm, flags) != 0) {
+ error = ENOMEM;
+ break;
+ }
+ /*
+ * Scan freelists looking for a tag that satisfies the
+ * allocation. If we're doing BESTFIT we may encounter
+ * sizes below the request. If we're doing FIRSTFIT we
+ * inspect only the first element from each list.
+ */
+ for (list = first; list < end; list++) {
+ LIST_FOREACH(bt, list, bt_freelist) {
+ if (bt->bt_size >= size) {
+ error = vmem_fit(bt, size, align, phase,
+ nocross, minaddr, maxaddr, addrp);
+ if (error == 0) {
+ vmem_clip(vm, bt, *addrp, size);
+ goto out;
+ }
+ }
+ /* FIRST skips to the next list. */
+ if (strat == M_FIRSTFIT)
+ break;
+ }
+ }
+ /*
+ * Retry if the fast algorithm failed.
+ */
+ if (strat == M_FIRSTFIT) {
+ strat = M_BESTFIT;
+ first = bt_freehead_toalloc(vm, size, strat);
+ continue;
+ }
+ /*
+ * XXX it is possible to fail to meet restrictions with the
+ * imported region. It is up to the user to specify the
+ * import quantum such that it can satisfy any allocation.
+ */
+ if (vmem_import(vm, size, flags) == 0)
+ continue;
+
+ /*
+ * Try to free some space from the quantum cache or reclaim
+ * functions if available.
+ */
+ if (vm->vm_qcache_max != 0 || vm->vm_reclaimfn != NULL) {
+ avail = vm->vm_size - vm->vm_inuse;
+ VMEM_UNLOCK(vm);
+ if (vm->vm_qcache_max != 0)
+ qc_drain(vm);
+ if (vm->vm_reclaimfn != NULL)
+ vm->vm_reclaimfn(vm, flags);
+ VMEM_LOCK(vm);
+ /* If we were successful retry even NOWAIT. */
+ if (vm->vm_size - vm->vm_inuse > avail)
+ continue;
+ }
+ if ((flags & M_NOWAIT) != 0) {
+ error = ENOMEM;
+ break;
+ }
+ VMEM_CONDVAR_WAIT(vm);
+ }
+out:
+ VMEM_UNLOCK(vm);
+ if (error != 0 && (flags & M_NOWAIT) == 0)
+ panic("failed to allocate waiting allocation\n");
+
+ return (error);
+}
+
+/*
+ * vmem_free: free the resource to the arena.
+ */
+void
+vmem_free(vmem_t *vm, vmem_addr_t addr, vmem_size_t size)
+{
+ qcache_t *qc;
+ MPASS(size > 0);
+
+ if (size <= vm->vm_qcache_max) {
+ qc = &vm->vm_qcache[(size - 1) >> vm->vm_quantum_shift];
+ uma_zfree(qc->qc_cache, (void *)addr);
+ } else
+ vmem_xfree(vm, addr, size);
+}
+
+void
+vmem_xfree(vmem_t *vm, vmem_addr_t addr, vmem_size_t size)
+{
+ bt_t *bt;
+ bt_t *t;
+
+ MPASS(size > 0);
+
+ VMEM_LOCK(vm);
+ bt = bt_lookupbusy(vm, addr);
+ MPASS(bt != NULL);
+ MPASS(bt->bt_start == addr);
+ MPASS(bt->bt_size == vmem_roundup_size(vm, size) ||
+ bt->bt_size - vmem_roundup_size(vm, size) <= vm->vm_quantum_mask);
+ MPASS(bt->bt_type == BT_TYPE_BUSY);
+ bt_rembusy(vm, bt);
+ bt->bt_type = BT_TYPE_FREE;
+
+ /* coalesce */
+ t = TAILQ_NEXT(bt, bt_seglist);
+ if (t != NULL && t->bt_type == BT_TYPE_FREE) {
+ MPASS(BT_END(bt) < t->bt_start); /* YYY */
+ bt->bt_size += t->bt_size;
+ bt_remfree(vm, t);
+ bt_remseg(vm, t);
+ }
+ t = TAILQ_PREV(bt, vmem_seglist, bt_seglist);
+ if (t != NULL && t->bt_type == BT_TYPE_FREE) {
+ MPASS(BT_END(t) < bt->bt_start); /* YYY */
+ bt->bt_size += t->bt_size;
+ bt->bt_start = t->bt_start;
+ bt_remfree(vm, t);
+ bt_remseg(vm, t);
+ }
+
+ t = TAILQ_PREV(bt, vmem_seglist, bt_seglist);
+ MPASS(t != NULL);
+ MPASS(BT_ISSPAN_P(t) || t->bt_type == BT_TYPE_BUSY);
+ if (vm->vm_releasefn != NULL && t->bt_type == BT_TYPE_SPAN &&
+ t->bt_size == bt->bt_size) {
+ vmem_addr_t spanaddr;
+ vmem_size_t spansize;
+
+ MPASS(t->bt_start == bt->bt_start);
+ spanaddr = bt->bt_start;
+ spansize = bt->bt_size;
+ bt_remseg(vm, bt);
+ bt_remseg(vm, t);
+ vm->vm_size -= spansize;
+ VMEM_CONDVAR_BROADCAST(vm);
+ bt_freetrim(vm, BT_MAXFREE);
+ (*vm->vm_releasefn)(vm->vm_arg, spanaddr, spansize);
+ } else {
+ bt_insfree(vm, bt);
+ VMEM_CONDVAR_BROADCAST(vm);
+ bt_freetrim(vm, BT_MAXFREE);
+ }
+}
+
+/*
+ * vmem_add:
+ *
+ */
+int
+vmem_add(vmem_t *vm, vmem_addr_t addr, vmem_size_t size, int flags)
+{
+ int error;
+
+ error = 0;
+ flags &= VMEM_FLAGS;
+ VMEM_LOCK(vm);
+ if (vm->vm_nfreetags >= BT_MAXALLOC || bt_fill(vm, flags) == 0)
+ vmem_add1(vm, addr, size, flags, BT_TYPE_SPAN_STATIC);
+ else
+ error = ENOMEM;
+ VMEM_UNLOCK(vm);
+
+ return (error);
+}
+
+/*
+ * vmem_size: information about arenas size
+ */
+vmem_size_t
+vmem_size(vmem_t *vm, int typemask)
+{
+
+ switch (typemask) {
+ case VMEM_ALLOC:
+ return vm->vm_inuse;
+ case VMEM_FREE:
+ return vm->vm_size - vm->vm_inuse;
+ case VMEM_FREE|VMEM_ALLOC:
+ return vm->vm_size;
+ default:
+ panic("vmem_size");
+ }
+}
+
+/* ---- debug */
+
+#if defined(DDB) || defined(DIAGNOSTIC)
+
+static void bt_dump(const bt_t *, int (*)(const char *, ...)
+ __printflike(1, 2));
+
+static const char *
+bt_type_string(int type)
+{
+
+ switch (type) {
+ case BT_TYPE_BUSY:
+ return "busy";
+ case BT_TYPE_FREE:
+ return "free";
+ case BT_TYPE_SPAN:
+ return "span";
+ case BT_TYPE_SPAN_STATIC:
+ return "static span";
+ default:
+ break;
+ }
+ return "BOGUS";
+}
+
+static void
+bt_dump(const bt_t *bt, int (*pr)(const char *, ...))
+{
+
+ (*pr)("\t%p: %jx %jx, %d(%s)\n",
+ bt, (intmax_t)bt->bt_start, (intmax_t)bt->bt_size,
+ bt->bt_type, bt_type_string(bt->bt_type));
+}
+
+static void
+vmem_dump(const vmem_t *vm , int (*pr)(const char *, ...) __printflike(1, 2))
+{
+ const bt_t *bt;
+ int i;
+
+ (*pr)("vmem %p '%s'\n", vm, vm->vm_name);
+ TAILQ_FOREACH(bt, &vm->vm_seglist, bt_seglist) {
+ bt_dump(bt, pr);
+ }
+
+ for (i = 0; i < VMEM_MAXORDER; i++) {
+ const struct vmem_freelist *fl = &vm->vm_freelist[i];
+
+ if (LIST_EMPTY(fl)) {
+ continue;
+ }
+
+ (*pr)("freelist[%d]\n", i);
+ LIST_FOREACH(bt, fl, bt_freelist) {
+ bt_dump(bt, pr);
+ }
+ }
+}
+
+#endif /* defined(DDB) || defined(DIAGNOSTIC) */
+
+#if defined(DDB)
+static bt_t *
+vmem_whatis_lookup(vmem_t *vm, vmem_addr_t addr)
+{
+ bt_t *bt;
+
+ TAILQ_FOREACH(bt, &vm->vm_seglist, bt_seglist) {
+ if (BT_ISSPAN_P(bt)) {
+ continue;
+ }
+ if (bt->bt_start <= addr && addr <= BT_END(bt)) {
+ return bt;
+ }
+ }
+
+ return NULL;
+}
+
+void
+vmem_whatis(vmem_addr_t addr, int (*pr)(const char *, ...))
+{
+ vmem_t *vm;
+
+ LIST_FOREACH(vm, &vmem_list, vm_alllist) {
+ bt_t *bt;
+
+ bt = vmem_whatis_lookup(vm, addr);
+ if (bt == NULL) {
+ continue;
+ }
+ (*pr)("%p is %p+%zu in VMEM '%s' (%s)\n",
+ (void *)addr, (void *)bt->bt_start,
+ (vmem_size_t)(addr - bt->bt_start), vm->vm_name,
+ (bt->bt_type == BT_TYPE_BUSY) ? "allocated" : "free");
+ }
+}
+
+void
+vmem_printall(const char *modif, int (*pr)(const char *, ...))
+{
+ const vmem_t *vm;
+
+ LIST_FOREACH(vm, &vmem_list, vm_alllist) {
+ vmem_dump(vm, pr);
+ }
+}
+
+void
+vmem_print(vmem_addr_t addr, const char *modif, int (*pr)(const char *, ...))
+{
+ const vmem_t *vm = (const void *)addr;
+
+ vmem_dump(vm, pr);
+}
+#endif /* defined(DDB) */
+
+#define vmem_printf printf
+
+#if defined(DIAGNOSTIC)
+
+static bool
+vmem_check_sanity(vmem_t *vm)
+{
+ const bt_t *bt, *bt2;
+
+ MPASS(vm != NULL);
+
+ TAILQ_FOREACH(bt, &vm->vm_seglist, bt_seglist) {
+ if (bt->bt_start > BT_END(bt)) {
+ printf("corrupted tag\n");
+ bt_dump(bt, vmem_printf);
+ return false;
+ }
+ }
+ TAILQ_FOREACH(bt, &vm->vm_seglist, bt_seglist) {
+ TAILQ_FOREACH(bt2, &vm->vm_seglist, bt_seglist) {
+ if (bt == bt2) {
+ continue;
+ }
+ if (BT_ISSPAN_P(bt) != BT_ISSPAN_P(bt2)) {
+ continue;
+ }
+ if (bt->bt_start <= BT_END(bt2) &&
+ bt2->bt_start <= BT_END(bt)) {
+ printf("overwrapped tags\n");
+ bt_dump(bt, vmem_printf);
+ bt_dump(bt2, vmem_printf);
+ return false;
+ }
+ }
+ }
+
+ return true;
+}
+
+static void
+vmem_check(vmem_t *vm)
+{
+
+ if (!vmem_check_sanity(vm)) {
+ panic("insanity vmem %p", vm);
+ }
+}
+
+#endif /* defined(DIAGNOSTIC) */
diff --git a/sys/kern/sys_generic.c b/sys/kern/sys_generic.c
index 45b9327..44d1a89 100644
--- a/sys/kern/sys_generic.c
+++ b/sys/kern/sys_generic.c
@@ -1498,6 +1498,62 @@ sys_openbsd_poll(td, uap)
}
/*
+ * XXX This was created specifically to support netncp and netsmb. This
+ * allows the caller to specify a socket to wait for events on. It returns
+ * 0 if any events matched and an error otherwise. There is no way to
+ * determine which events fired.
+ */
+int
+selsocket(struct socket *so, int events, struct timeval *tvp, struct thread *td)
+{
+ struct timeval rtv;
+ sbintime_t asbt, precision, rsbt;
+ int error;
+
+ precision = 0; /* stupid gcc! */
+ if (tvp != NULL) {
+ rtv = *tvp;
+ if (rtv.tv_sec < 0 || rtv.tv_usec < 0 ||
+ rtv.tv_usec >= 1000000)
+ return (EINVAL);
+ if (!timevalisset(&rtv))
+ asbt = 0;
+ else if (rtv.tv_sec <= INT32_MAX) {
+ rsbt = tvtosbt(rtv);
+ precision = rsbt;
+ precision >>= tc_precexp;
+ if (TIMESEL(&asbt, rsbt))
+ asbt += tc_tick_sbt;
+ if (asbt <= INT64_MAX - rsbt)
+ asbt += rsbt;
+ else
+ asbt = -1;
+ } else
+ asbt = -1;
+ } else
+ asbt = -1;
+ seltdinit(td);
+ /*
+ * Iterate until the timeout expires or the socket becomes ready.
+ */
+ for (;;) {
+ selfdalloc(td, NULL);
+ error = sopoll(so, events, NULL, td);
+ /* error here is actually the ready events. */
+ if (error)
+ return (0);
+ error = seltdwait(td, asbt, precision);
+ if (error)
+ break;
+ }
+ seltdclear(td);
+ /* XXX Duplicates ncp/smb behavior. */
+ if (error == ERESTART)
+ error = 0;
+ return (error);
+}
+
+/*
* Preallocate two selfds associated with 'cookie'. Some fo_poll routines
* have two select sets, one for read and another for write.
*/
diff --git a/sys/kern/uipc_usrreq.c b/sys/kern/uipc_usrreq.c
index 5d8e814..7a4db04 100644
--- a/sys/kern/uipc_usrreq.c
+++ b/sys/kern/uipc_usrreq.c
@@ -1764,8 +1764,8 @@ unp_externalize(struct mbuf *control, struct mbuf **controlp, int flags)
}
for (i = 0; i < newfds; i++, fdp++) {
fde = &fdesc->fd_ofiles[*fdp];
- fde->fde_file = fdep[0]->fde_file;
- filecaps_move(&fdep[0]->fde_caps,
+ fde->fde_file = fdep[i]->fde_file;
+ filecaps_move(&fdep[i]->fde_caps,
&fde->fde_caps);
if ((flags & MSG_CMSG_CLOEXEC) != 0)
fde->fde_flags |= UF_EXCLOSE;
diff --git a/sys/kern/vfs_bio.c b/sys/kern/vfs_bio.c
index 767ed30..f6d5a85 100644
--- a/sys/kern/vfs_bio.c
+++ b/sys/kern/vfs_bio.c
@@ -64,6 +64,7 @@ __FBSDID("$FreeBSD$");
#include <sys/resourcevar.h>
#include <sys/rwlock.h>
#include <sys/sysctl.h>
+#include <sys/vmem.h>
#include <sys/vmmeter.h>
#include <sys/vnode.h>
#include <geom/geom.h>
@@ -920,13 +921,13 @@ bfreekva(struct buf *bp)
atomic_subtract_long(&bufspace, bp->b_kvasize);
if ((bp->b_flags & B_UNMAPPED) == 0) {
BUF_CHECK_MAPPED(bp);
- vm_map_remove(buffer_map, (vm_offset_t)bp->b_kvabase,
- (vm_offset_t)bp->b_kvabase + bp->b_kvasize);
+ vmem_free(buffer_arena, (vm_offset_t)bp->b_kvabase,
+ bp->b_kvasize);
} else {
BUF_CHECK_UNMAPPED(bp);
if ((bp->b_flags & B_KVAALLOC) != 0) {
- vm_map_remove(buffer_map, (vm_offset_t)bp->b_kvaalloc,
- (vm_offset_t)bp->b_kvaalloc + bp->b_kvasize);
+ vmem_free(buffer_arena, (vm_offset_t)bp->b_kvaalloc,
+ bp->b_kvasize);
}
atomic_subtract_long(&unmapped_bufspace, bp->b_kvasize);
bp->b_flags &= ~(B_UNMAPPED | B_KVAALLOC);
@@ -2019,15 +2020,11 @@ static int
allocbufkva(struct buf *bp, int maxsize, int gbflags)
{
vm_offset_t addr;
- int rv;
bfreekva(bp);
addr = 0;
- vm_map_lock(buffer_map);
- if (vm_map_findspace(buffer_map, vm_map_min(buffer_map), maxsize,
- &addr)) {
- vm_map_unlock(buffer_map);
+ if (vmem_alloc(buffer_arena, maxsize, M_BESTFIT | M_NOWAIT, &addr)) {
/*
* Buffer map is too fragmented. Request the caller
* to defragment the map.
@@ -2035,10 +2032,6 @@ allocbufkva(struct buf *bp, int maxsize, int gbflags)
atomic_add_int(&bufdefragcnt, 1);
return (1);
}
- rv = vm_map_insert(buffer_map, NULL, 0, addr, addr + maxsize,
- VM_PROT_RW, VM_PROT_RW, MAP_NOFAULT);
- KASSERT(rv == KERN_SUCCESS, ("vm_map_insert(buffer_map) rv %d", rv));
- vm_map_unlock(buffer_map);
setbufkva(bp, addr, maxsize, gbflags);
atomic_add_long(&bufspace, bp->b_kvasize);
return (0);
@@ -2389,7 +2382,7 @@ restart:
* We block if:
* We have insufficient buffer headers
* We have insufficient buffer space
- * buffer_map is too fragmented ( space reservation fails )
+ * buffer_arena is too fragmented ( space reservation fails )
* If we have to flush dirty buffers ( but we try to avoid this )
*/
static struct buf *
@@ -3593,7 +3586,7 @@ biodone(struct bio *bp)
done(bp);
if (transient) {
pmap_qremove(start, OFF_TO_IDX(end - start));
- vm_map_remove(bio_transient_map, start, end);
+ vmem_free(transient_arena, start, end - start);
atomic_add_int(&inflight_transient_maps, -1);
}
}
diff --git a/sys/mips/conf/AP93.hints b/sys/mips/conf/AP93.hints
index 22be50f..da4c320 100644
--- a/sys/mips/conf/AP93.hints
+++ b/sys/mips/conf/AP93.hints
@@ -36,6 +36,9 @@ hint.arswitch.0.is_gmii=0 # No, not GMII
hint.pcib.0.bus.0.0.0.ath_fixup_addr=0x1fff1000
hint.pcib.0.bus.0.0.0.ath_fixup_size=4096
+# ath0 - eeprom comes from here
+hint.ath.0.eeprom_firmware="pcib.0.bus.0.0.0.eeprom_firmware"
+
# Signal leds
hint.gpioled.0.at="gpiobus0"
hint.gpioled.0.name="sig1"
diff --git a/sys/mips/conf/ENH200 b/sys/mips/conf/ENH200
new file mode 100644
index 0000000..886397f
--- /dev/null
+++ b/sys/mips/conf/ENH200
@@ -0,0 +1,44 @@
+#
+# Specific board setup for the Engenius ENH-200 802.11bgn mesh node.
+#
+# The Engenius ENH-200 has the following hardware:
+#
+# + AR7240 CPU SoC
+# + AR9285 Wifi
+# + Integrated switch
+# + 8MB flash
+# + 32MB RAM
+# + uboot environment
+
+# $FreeBSD$
+
+include "AR724X_BASE"
+ident "ENH200"
+hints "ENH200.hints"
+
+options AR71XX_REALMEM=32*1024*1024
+
+options AR71XX_ENV_UBOOT
+
+# For DOS - enable if required
+options MSDOSFS
+
+# uncompress - to boot read-only lzma natively from flash
+device geom_uncompress
+options GEOM_UNCOMPRESS
+options ROOTDEVNAME=\"ufs:/dev/map/rootfs.uncompress\"
+
+# Used for the static uboot partition map
+device geom_map
+
+# Options needed for the EEPROM based calibration/PCI configuration data.
+options AR71XX_ATH_EEPROM # Fetch EEPROM/PCI config from flash
+options ATH_EEPROM_FIRMWARE # Use EEPROM from flash
+device firmware # Used by the above
+
+# Options required for miiproxy and mdiobus
+options ARGE_MDIO # Export an MDIO bus separate from arge
+device miiproxy # MDIO bus <-> MII PHY rendezvous
+
+device etherswitch
+device arswitch
diff --git a/sys/mips/conf/ENH200.hints b/sys/mips/conf/ENH200.hints
new file mode 100644
index 0000000..347c04e
--- /dev/null
+++ b/sys/mips/conf/ENH200.hints
@@ -0,0 +1,124 @@
+# $FreeBSD$
+
+# arge0 MDIO bus
+hint.argemdio.0.at="nexus0"
+hint.argemdio.0.maddr=0x19000000
+hint.argemdio.0.msize=0x1000
+hint.argemdio.0.order=0
+
+# arge1 MDIO bus doesn't exist on the AR7240
+
+# arge0: MII; dedicated PHY 4 on switch, connected via internal switch
+# MDIO bus.
+
+# hint.arge.0.eeprommac=0x83fe9ff0
+hint.arge.0.phymask=0x10 # PHY 4
+# hint.arge.0.miimode=2 # MII
+hint.arge.0.mdio=mdioproxy1 # Hanging off the arswitch MDIO bus
+
+# arge1: connected to the LAN switch MAC, at 1000BaseTX / GMII.
+hint.arge.1.phymask=0x0
+# hint.arge.1.miimode=1 # GMII
+hint.arge.1.media=1000 # Force to 1000BaseTX/full
+hint.arge.1.fduplex=1
+
+#
+# AR7240 switch config
+#
+hint.arswitch.0.at="mdio0"
+hint.arswitch.0.is_7240=1 # We need to be explicitly told this
+hint.arswitch.0.numphys=4 # 4 active switch PHYs (PHY 0 -> 3)
+hint.arswitch.0.phy4cpu=1 # Yes, PHY 4 == dedicated PHY
+hint.arswitch.0.is_rgmii=0 # No, not RGMII
+hint.arswitch.0.is_gmii=0 # No, not GMII
+
+# ath0 hint - pcie slot 0
+hint.pcib.0.bus.0.0.0.ath_fixup_addr=0x1fff1000
+hint.pcib.0.bus.0.0.0.ath_fixup_size=4096
+
+# ath0 - eeprom comes from here
+hint.ath.0.eeprom_firmware="pcib.0.bus.0.0.0.eeprom_firmware"
+
+# Signal leds
+hint.gpioled.0.at="gpiobus0"
+hint.gpioled.0.name="sig1"
+hint.gpioled.0.pins=0x0001 # pin 0
+hint.gpioled.1.at="gpiobus0"
+hint.gpioled.1.name="sig2"
+hint.gpioled.1.pins=0x0002 # pin 1
+hint.gpioled.2.at="gpiobus0"
+hint.gpioled.2.name="sig3"
+hint.gpioled.2.pins=0x0800 # pin 11
+hint.gpioled.3.at="gpiobus0"
+hint.gpioled.3.name="sig4"
+hint.gpioled.3.pins=0x0080 # pin 7
+
+# nvram mapping - XXX ?
+#hint.nvram.0.base=0x1f030000
+#hint.nvram.0.maxsize=0x2000
+#hint.nvram.0.flags=3 # 1 = No check, 2 = Format Generic
+#hint.nvram.1.base=0x1f032000
+#hint.nvram.1.maxsize=0x4000
+#hint.nvram.1.flags=3 # 1 = No check, 2 = Format Generic
+
+# GEOM_MAP
+#
+# The default bootargs:
+#
+# bootargs=console=ttyS0,115200 root=31:04 rootfstype=squashfs init=/etc/preinit mtdparts=ar7240-nor0:256k(u-boot),64k(u-boot-env),320k(custom),1024k(kernel),4928k(rootfs),1536k(failsafe),64k(ART) board=ENH200
+#
+# However there's not a lot of space in this image layout.
+#
+# Thus, an alternate layout will be used, complete with reconfiguring
+# uboot to use the new base address.
+#
+# 256k - uboot (0x000000 -> 0x040000)
+# 64k - uboot-env (0x040000 -> 0x050000)
+# 1728k - kernel (0x050000 -> 0x200000)
+# 6016k - rootfs (0x200000 -> 0x7e0000)
+# 64k - config (0x7e0000 -> 0x7f0000)
+# 64k - ART (0x7f0000 -> 0x800000)
+#
+# For this, the 'bootcmd' environment variable needs to be
+# changed to point to the new location:
+#
+# ar7240> setenv bootcmd 'bootm 0x9f050000'
+
+# uboot (256k)
+hint.map.0.at="flash/spi0"
+hint.map.0.start=0x00000000
+hint.map.0.end=0x00040000
+hint.map.0.name="uboot"
+hint.map.0.readonly=1
+
+# uboot-env (64k)
+hint.map.1.at="flash/spi0"
+hint.map.1.start=0x00040000
+hint.map.1.end=0x00050000
+hint.map.1.name="uboot-env"
+hint.map.1.readonly=1
+
+# kernel (1728k)
+hint.map.2.at="flash/spi0"
+hint.map.2.start=0x00050000
+hint.map.2.end=0x00200000
+hint.map.2.name="kernel"
+
+# rootfs (6016k)
+hint.map.3.at="flash/spi0"
+hint.map.3.start=0x00200000
+hint.map.3.end=0x007e0000
+hint.map.3.name="rootfs"
+
+# config (64k)
+hint.map.4.at="flash/spi0"
+hint.map.4.start=0x007e0000
+hint.map.4.end=0x007f0000
+hint.map.4.name="cfg"
+
+# ART (64k)
+hint.map.5.at="flash/spi0"
+hint.map.5.start=0x007f0000
+hint.map.5.end=0x00800000
+hint.map.5.name="ART"
+hint.map.5.readonly=1
diff --git a/sys/mips/include/counter.h b/sys/mips/include/counter.h
index 68f89e2..193c98e 100644
--- a/sys/mips/include/counter.h
+++ b/sys/mips/include/counter.h
@@ -37,6 +37,46 @@
#define counter_enter() critical_enter()
#define counter_exit() critical_exit()
+#ifdef IN_SUBR_COUNTER_C
+/* XXXKIB non-atomic 64bit read on 32bit */
+static inline uint64_t
+counter_u64_read_one(uint64_t *p, int cpu)
+{
+
+ return (*(uint64_t *)((char *)p + sizeof(struct pcpu) * cpu));
+}
+
+static inline uint64_t
+counter_u64_fetch_inline(uint64_t *p)
+{
+ uint64_t r;
+ int i;
+
+ r = 0;
+ for (i = 0; i < mp_ncpus; i++)
+ r += counter_u64_read_one((uint64_t *)p, i);
+
+ return (r);
+}
+
+/* XXXKIB non-atomic 64bit store on 32bit, might interrupt increment */
+static void
+counter_u64_zero_one_cpu(void *arg)
+{
+
+ *((uint64_t *)((char *)arg + sizeof(struct pcpu) *
+ PCPU_GET(cpuid))) = 0;
+}
+
+static inline void
+counter_u64_zero_inline(counter_u64_t c)
+{
+
+ smp_rendezvous(smp_no_rendevous_barrier, counter_u64_zero_one_cpu,
+ smp_no_rendevous_barrier, c);
+}
+#endif
+
#define counter_u64_add_protected(c, inc) do { \
CRITICAL_ASSERT(curthread); \
*(uint64_t *)zpcpu_get(c) += (inc); \
diff --git a/sys/modules/Makefile b/sys/modules/Makefile
index ac620d3..8f58ed0 100644
--- a/sys/modules/Makefile
+++ b/sys/modules/Makefile
@@ -81,7 +81,7 @@ SUBDIR= \
${_ctau} \
ctl \
${_cxgb} \
- cxgbe \
+ ${_cxgbe} \
${_cyclic} \
dc \
dcons \
@@ -302,6 +302,7 @@ SUBDIR= \
siis \
sis \
sk \
+ ${_smbfs} \
${_sn} \
${_snc} \
snp \
@@ -386,6 +387,10 @@ _cxgb= cxgb
.endif
.endif
+.if ${MK_SOURCELESS_UCODE} != "no"
+_cxgbe= cxgbe
+.endif
+
.if ${MK_CRYPT} != "no" || defined(ALL_MODULES)
.if exists(${.CURDIR}/../opencrypto)
_crypto= crypto
@@ -521,6 +526,7 @@ _rdma= rdma
_safe= safe
_sbni= sbni
_scsi_low= scsi_low
+_smbfs= smbfs
_sound= sound
_speaker= speaker
_splash= splash
@@ -724,6 +730,7 @@ _s3= s3
_safe= safe
_scsi_low= scsi_low
_sfxge= sfxge
+_smbfs= smbfs
_sound= sound
_speaker= speaker
_splash= splash
@@ -781,6 +788,7 @@ _ips= ips
_mly= mly
_pccard= pccard
_scsi_low= scsi_low
+_smbfs= smbfs
_sound= sound
_splash= splash
_sppp= sppp
@@ -803,6 +811,7 @@ _drm= drm
_exca= exca
_nvram= powermac_nvram
_pccard= pccard
+_smbfs= smbfs
_sound= sound
_cyclic= cyclic
_dtrace= dtrace
@@ -829,6 +838,7 @@ _igb= igb
.if ${MK_CDDL} != "no" || defined(ALL_MODULES)
_opensolaris= opensolaris
.endif
+_smbfs= smbfs
_sound= sound
.if ${MK_ZFS} != "no" || defined(ALL_MODULES)
_zfs= zfs
diff --git a/sys/modules/cc/Makefile b/sys/modules/cc/Makefile
index e3e8336..7b851f5 100644
--- a/sys/modules/cc/Makefile
+++ b/sys/modules/cc/Makefile
@@ -1,6 +1,7 @@
# $FreeBSD$
-SUBDIR= cc_chd \
+SUBDIR= cc_cdg \
+ cc_chd \
cc_cubic \
cc_hd \
cc_htcp \
diff --git a/sys/modules/cc/cc_cdg/Makefile b/sys/modules/cc/cc_cdg/Makefile
new file mode 100644
index 0000000..bc6f62d
--- /dev/null
+++ b/sys/modules/cc/cc_cdg/Makefile
@@ -0,0 +1,9 @@
+# $FreeBSD$
+
+.include <bsd.own.mk>
+
+.PATH: ${.CURDIR}/../../../netinet/cc
+KMOD= cc_cdg
+SRCS= cc_cdg.c
+
+.include <bsd.kmod.mk>
diff --git a/sys/modules/cxgbe/Makefile b/sys/modules/cxgbe/Makefile
index 2f848b6..fb75485 100644
--- a/sys/modules/cxgbe/Makefile
+++ b/sys/modules/cxgbe/Makefile
@@ -4,7 +4,7 @@
SUBDIR = if_cxgbe
SUBDIR+= t4_firmware
-#SUBDIR+= t5_firmware
+SUBDIR+= t5_firmware
SUBDIR+= ${_tom}
.if ${MACHINE_CPUARCH} == "amd64" || ${MACHINE_CPUARCH} == "i386"
diff --git a/sys/modules/cxgbe/t4_firmware/Makefile b/sys/modules/cxgbe/t4_firmware/Makefile
index fea29a5..d5b86d1 100644
--- a/sys/modules/cxgbe/t4_firmware/Makefile
+++ b/sys/modules/cxgbe/t4_firmware/Makefile
@@ -17,7 +17,7 @@ FIRMWS += ${F}:${F:C/.txt//}:1.0.0.0
.endif
.endfor
-T4FW_VER = 1.8.4.0
+T4FW_VER = 1.8.11.0
FIRMWS += t4fw.fw:t4fw:${T4FW_VER}
CLEANFILES += t4fw.fw
diff --git a/sys/modules/cxgbe/t5_firmware/Makefile b/sys/modules/cxgbe/t5_firmware/Makefile
new file mode 100644
index 0000000..6637b22
--- /dev/null
+++ b/sys/modules/cxgbe/t5_firmware/Makefile
@@ -0,0 +1,27 @@
+#
+# $FreeBSD$
+#
+
+T5FW = ${.CURDIR}/../../../dev/cxgbe/firmware
+.PATH: ${T5FW}
+
+KMOD = t5fw_cfg
+FIRMWS = ${KMOD}.txt:${KMOD}:1.0.0.0
+
+# You can have additional configuration files in the ${T5FW} directory.
+# t5fw_cfg_<name>.txt
+CFG_FILES != cd ${T5FW} && echo ${KMOD}_*.txt
+.for F in ${CFG_FILES}
+.if exists(${F})
+FIRMWS += ${F}:${F:C/.txt//}:1.0.0.0
+.endif
+.endfor
+
+T5FW_VER = 1.8.22.0
+FIRMWS += t5fw.fw:t5fw:${T5FW_VER}
+CLEANFILES += t5fw.fw
+
+t5fw.fw: t5fw-${T5FW_VER}.bin.uu
+ uudecode -o ${.TARGET} ${.ALLSRC}
+
+.include <bsd.kmod.mk>
diff --git a/sys/net/if_bridge.c b/sys/net/if_bridge.c
index bc9bda09..b33696f 100644
--- a/sys/net/if_bridge.c
+++ b/sys/net/if_bridge.c
@@ -118,6 +118,7 @@ __FBSDID("$FreeBSD$");
#ifdef INET6
#include <netinet/ip6.h>
#include <netinet6/ip6_var.h>
+#include <netinet6/in6_ifattach.h>
#endif
#if defined(INET) || defined(INET6)
#include <netinet/ip_carp.h>
@@ -1041,14 +1042,6 @@ bridge_ioctl_add(struct bridge_softc *sc, void *arg)
if (ifs->if_bridge != NULL)
return (EBUSY);
- bif = malloc(sizeof(*bif), M_DEVBUF, M_NOWAIT|M_ZERO);
- if (bif == NULL)
- return (ENOMEM);
-
- bif->bif_ifp = ifs;
- bif->bif_flags = IFBIF_LEARNING | IFBIF_DISCOVER;
- bif->bif_savedcaps = ifs->if_capenable;
-
switch (ifs->if_type) {
case IFT_ETHER:
case IFT_L2VLAN:
@@ -1056,20 +1049,95 @@ bridge_ioctl_add(struct bridge_softc *sc, void *arg)
/* permitted interface types */
break;
default:
- error = EINVAL;
- goto out;
+ return (EINVAL);
}
+#ifdef INET6
+ /*
+ * Two valid inet6 addresses with link-local scope must not be
+ * on the parent interface and the member interfaces at the
+ * same time. This restriction is needed to prevent violation
+ * of link-local scope zone. Attempts to add a member
+ * interface which has inet6 addresses when the parent has
+ * inet6 triggers removal of all inet6 addresses on the member
+ * interface.
+ */
+
+ /* Check if the parent interface has a link-local scope addr. */
+ if (in6ifa_llaonifp(sc->sc_ifp) != NULL) {
+ /*
+ * If any, remove all inet6 addresses from the member
+ * interfaces.
+ */
+ BRIDGE_XLOCK(sc);
+ LIST_FOREACH(bif, &sc->sc_iflist, bif_next) {
+ if (in6ifa_llaonifp(bif->bif_ifp)) {
+ BRIDGE_UNLOCK(sc);
+ in6_ifdetach(bif->bif_ifp);
+ BRIDGE_LOCK(sc);
+ if_printf(sc->sc_ifp,
+ "IPv6 addresses on %s have been removed "
+ "before adding it as a member to prevent "
+ "IPv6 address scope violation.\n",
+ bif->bif_ifp->if_xname);
+ }
+ }
+ BRIDGE_XDROP(sc);
+ if (in6ifa_llaonifp(ifs)) {
+ BRIDGE_UNLOCK(sc);
+ in6_ifdetach(ifs);
+ BRIDGE_LOCK(sc);
+ if_printf(sc->sc_ifp,
+ "IPv6 addresses on %s have been removed "
+ "before adding it as a member to prevent "
+ "IPv6 address scope violation.\n",
+ ifs->if_xname);
+ }
+ } else {
+ struct in6_ifaddr *ia6_m, *ia6_s;
+ /*
+ * If not, check whether one of the existing member
+ * interfaces have inet6 address. If any, remove
+ * inet6 addresses on the interface to be added.
+ */
+ ia6_m = NULL;
+ BRIDGE_XLOCK(sc);
+ LIST_FOREACH(bif, &sc->sc_iflist, bif_next) {
+ ia6_m = in6ifa_llaonifp(bif->bif_ifp);
+ if (ia6_m != NULL)
+ break;
+ }
+ BRIDGE_XDROP(sc);
+ ia6_s = in6ifa_llaonifp(ifs);
+
+ if (ia6_m != NULL && ia6_s != NULL) {
+ BRIDGE_UNLOCK(sc);
+ in6_ifdetach(ifs);
+ BRIDGE_LOCK(sc);
+ if_printf(sc->sc_ifp, "IPv6 addresses on %s have "
+ "been removed before adding it as a member "
+ "to prevent IPv6 address scope violation.\n",
+ ifs->if_xname);
+ }
+ }
+#endif
/* Allow the first Ethernet member to define the MTU */
if (LIST_EMPTY(&sc->sc_iflist))
sc->sc_ifp->if_mtu = ifs->if_mtu;
else if (sc->sc_ifp->if_mtu != ifs->if_mtu) {
if_printf(sc->sc_ifp, "invalid MTU: %lu(%s) != %lu\n",
ifs->if_mtu, ifs->if_xname, sc->sc_ifp->if_mtu);
- error = EINVAL;
- goto out;
+ return (EINVAL);
}
+ bif = malloc(sizeof(*bif), M_DEVBUF, M_NOWAIT|M_ZERO);
+ if (bif == NULL)
+ return (ENOMEM);
+
+ bif->bif_ifp = ifs;
+ bif->bif_flags = IFBIF_LEARNING | IFBIF_DISCOVER;
+ bif->bif_savedcaps = ifs->if_capenable;
+
/*
* Assign the interface's MAC address to the bridge if it's the first
* member and the MAC address of the bridge has not been changed from
@@ -1104,12 +1172,10 @@ bridge_ioctl_add(struct bridge_softc *sc, void *arg)
BRIDGE_LOCK(sc);
break;
}
- if (error)
- bridge_delete_member(sc, bif, 0);
-out:
+
if (error) {
- if (bif != NULL)
- free(bif, M_DEVBUF);
+ bridge_delete_member(sc, bif, 0);
+ free(bif, M_DEVBUF);
}
return (error);
}
@@ -3408,7 +3474,7 @@ bridge_fragment(struct ifnet *ifp, struct mbuf *m, struct ether_header *eh,
continue;
}
bcopy(eh, mtod(m0, caddr_t), ETHER_HDR_LEN);
- } else
+ } else
m_freem(m);
}
diff --git a/sys/net/if_lagg.c b/sys/net/if_lagg.c
index 11273b2..9867c97 100644
--- a/sys/net/if_lagg.c
+++ b/sys/net/if_lagg.c
@@ -63,6 +63,8 @@ __FBSDID("$FreeBSD$");
#ifdef INET6
#include <netinet/ip6.h>
+#include <netinet6/in6_var.h>
+#include <netinet6/in6_ifattach.h>
#endif
#include <net/if_vlan_var.h>
@@ -543,6 +545,34 @@ lagg_port_create(struct lagg_softc *sc, struct ifnet *ifp)
if (ifp->if_type != IFT_ETHER)
return (EPROTONOSUPPORT);
+#ifdef INET6
+ /*
+ * The member interface should not have inet6 address because
+ * two interfaces with a valid link-local scope zone must not be
+ * merged in any form. This restriction is needed to
+ * prevent violation of link-local scope zone. Attempts to
+ * add a member interface which has inet6 addresses triggers
+ * removal of all inet6 addresses on the member interface.
+ */
+ SLIST_FOREACH(lp, &sc->sc_ports, lp_entries) {
+ if (in6ifa_llaonifp(lp->lp_ifp)) {
+ in6_ifdetach(lp->lp_ifp);
+ if_printf(sc->sc_ifp,
+ "IPv6 addresses on %s have been removed "
+ "before adding it as a member to prevent "
+ "IPv6 address scope violation.\n",
+ lp->lp_ifp->if_xname);
+ }
+ }
+ if (in6ifa_llaonifp(ifp)) {
+ in6_ifdetach(ifp);
+ if_printf(sc->sc_ifp,
+ "IPv6 addresses on %s have been removed "
+ "before adding it as a member to prevent "
+ "IPv6 address scope violation.\n",
+ ifp->if_xname);
+ }
+#endif
/* Allow the first Ethernet member to define the MTU */
if (SLIST_EMPTY(&sc->sc_ports))
sc->sc_ifp->if_mtu = ifp->if_mtu;
diff --git a/sys/net80211/ieee80211_amrr.c b/sys/net80211/ieee80211_amrr.c
index 73dd901..003b4bc 100644
--- a/sys/net80211/ieee80211_amrr.c
+++ b/sys/net80211/ieee80211_amrr.c
@@ -46,6 +46,7 @@ __FBSDID("$FreeBSD$");
#endif
#include <net80211/ieee80211_var.h>
+#include <net80211/ieee80211_ht.h>
#include <net80211/ieee80211_amrr.h>
#include <net80211/ieee80211_ratectl.h>
@@ -128,13 +129,25 @@ amrr_deinit(struct ieee80211vap *vap)
free(vap->iv_rs, M_80211_RATECTL);
}
+static int
+amrr_node_is_11n(struct ieee80211_node *ni)
+{
+
+ if (ni->ni_chan == NULL)
+ return (0);
+ if (ni->ni_chan == IEEE80211_CHAN_ANYC)
+ return (0);
+ return (IEEE80211_IS_CHAN_HT(ni->ni_chan));
+}
+
static void
amrr_node_init(struct ieee80211_node *ni)
{
- const struct ieee80211_rateset *rs = &ni->ni_rates;
+ const struct ieee80211_rateset *rs = NULL;
struct ieee80211vap *vap = ni->ni_vap;
struct ieee80211_amrr *amrr = vap->iv_rs;
struct ieee80211_amrr_node *amn;
+ uint8_t rate;
if (ni->ni_rctls == NULL) {
ni->ni_rctls = amn = malloc(sizeof(struct ieee80211_amrr_node),
@@ -152,16 +165,50 @@ amrr_node_init(struct ieee80211_node *ni)
amn->amn_txcnt = amn->amn_retrycnt = 0;
amn->amn_success_threshold = amrr->amrr_min_success_threshold;
- /* pick initial rate */
- for (amn->amn_rix = rs->rs_nrates - 1;
- amn->amn_rix > 0 && (rs->rs_rates[amn->amn_rix] & IEEE80211_RATE_VAL) > 72;
- amn->amn_rix--)
- ;
- ni->ni_txrate = rs->rs_rates[amn->amn_rix] & IEEE80211_RATE_VAL;
+ /* 11n or not? Pick the right rateset */
+ if (amrr_node_is_11n(ni)) {
+ /* XXX ew */
+ IEEE80211_NOTE(ni->ni_vap, IEEE80211_MSG_RATECTL, ni,
+ "%s: 11n node", __func__);
+ rs = (struct ieee80211_rateset *) &ni->ni_htrates;
+ } else {
+ IEEE80211_NOTE(ni->ni_vap, IEEE80211_MSG_RATECTL, ni,
+ "%s: non-11n node", __func__);
+ rs = &ni->ni_rates;
+ }
+
+ /* Initial rate - lowest */
+ rate = rs->rs_rates[0];
+
+ /* XXX clear the basic rate flag if it's not 11n */
+ if (! amrr_node_is_11n(ni))
+ rate &= IEEE80211_RATE_VAL;
+
+ /* pick initial rate from the rateset - HT or otherwise */
+ for (amn->amn_rix = rs->rs_nrates - 1; amn->amn_rix > 0;
+ amn->amn_rix--) {
+ /* legacy - anything < 36mbit, stop searching */
+ /* 11n - stop at MCS4 / MCS12 / MCS28 */
+ if (amrr_node_is_11n(ni) &&
+ (rs->rs_rates[amn->amn_rix] & 0x7) < 4)
+ break;
+ else if ((rs->rs_rates[amn->amn_rix] & IEEE80211_RATE_VAL) <= 72)
+ break;
+ rate = rs->rs_rates[amn->amn_rix] & IEEE80211_RATE_VAL;
+ }
+
+ /* if the rate is an 11n rate, ensure the MCS bit is set */
+ if (amrr_node_is_11n(ni))
+ rate |= IEEE80211_RATE_MCS;
+
+ /* Assign initial rate from the rateset */
+ ni->ni_txrate = rate;
amn->amn_ticks = ticks;
IEEE80211_NOTE(ni->ni_vap, IEEE80211_MSG_RATECTL, ni,
- "AMRR initial rate %d", ni->ni_txrate);
+ "AMRR: nrates=%d, initial rate %d",
+ rs->rs_nrates,
+ rate);
}
static void
@@ -175,19 +222,42 @@ amrr_update(struct ieee80211_amrr *amrr, struct ieee80211_amrr_node *amn,
struct ieee80211_node *ni)
{
int rix = amn->amn_rix;
+ const struct ieee80211_rateset *rs = NULL;
KASSERT(is_enough(amn), ("txcnt %d", amn->amn_txcnt));
+ /* 11n or not? Pick the right rateset */
+ if (amrr_node_is_11n(ni)) {
+ /* XXX ew */
+ rs = (struct ieee80211_rateset *) &ni->ni_htrates;
+ } else {
+ rs = &ni->ni_rates;
+ }
+
+ IEEE80211_NOTE(ni->ni_vap, IEEE80211_MSG_RATECTL, ni,
+ "AMRR: current rate %d, txcnt=%d, retrycnt=%d",
+ rs->rs_rates[rix] & IEEE80211_RATE_VAL,
+ amn->amn_txcnt,
+ amn->amn_retrycnt);
+
+ /*
+ * XXX This is totally bogus for 11n, as although high MCS
+ * rates for each stream may be failing, the next stream
+ * should be checked.
+ *
+ * Eg, if MCS5 is ok but MCS6/7 isn't, and we can go up to
+ * MCS23, we should skip 6/7 and try 8 onwards.
+ */
if (is_success(amn)) {
amn->amn_success++;
if (amn->amn_success >= amn->amn_success_threshold &&
- rix + 1 < ni->ni_rates.rs_nrates) {
+ rix + 1 < rs->rs_nrates) {
amn->amn_recovery = 1;
amn->amn_success = 0;
rix++;
IEEE80211_NOTE(ni->ni_vap, IEEE80211_MSG_RATECTL, ni,
"AMRR increasing rate %d (txcnt=%d retrycnt=%d)",
- ni->ni_rates.rs_rates[rix] & IEEE80211_RATE_VAL,
+ rs->rs_rates[rix] & IEEE80211_RATE_VAL,
amn->amn_txcnt, amn->amn_retrycnt);
} else {
amn->amn_recovery = 0;
@@ -208,7 +278,7 @@ amrr_update(struct ieee80211_amrr *amrr, struct ieee80211_amrr_node *amn,
rix--;
IEEE80211_NOTE(ni->ni_vap, IEEE80211_MSG_RATECTL, ni,
"AMRR decreasing rate %d (txcnt=%d retrycnt=%d)",
- ni->ni_rates.rs_rates[rix] & IEEE80211_RATE_VAL,
+ rs->rs_rates[rix] & IEEE80211_RATE_VAL,
amn->amn_txcnt, amn->amn_retrycnt);
}
amn->amn_recovery = 0;
@@ -231,14 +301,27 @@ amrr_rate(struct ieee80211_node *ni, void *arg __unused, uint32_t iarg __unused)
{
struct ieee80211_amrr_node *amn = ni->ni_rctls;
struct ieee80211_amrr *amrr = amn->amn_amrr;
+ const struct ieee80211_rateset *rs = NULL;
int rix;
+ /* 11n or not? Pick the right rateset */
+ if (amrr_node_is_11n(ni)) {
+ /* XXX ew */
+ rs = (struct ieee80211_rateset *) &ni->ni_htrates;
+ } else {
+ rs = &ni->ni_rates;
+ }
+
if (is_enough(amn) && (ticks - amn->amn_ticks) > amrr->amrr_interval) {
rix = amrr_update(amrr, amn, ni);
if (rix != amn->amn_rix) {
/* update public rate */
- ni->ni_txrate =
- ni->ni_rates.rs_rates[rix] & IEEE80211_RATE_VAL;
+ ni->ni_txrate = rs->rs_rates[rix];
+ /* XXX strip basic rate flag from txrate, if non-11n */
+ if (amrr_node_is_11n(ni))
+ ni->ni_txrate |= IEEE80211_RATE_MCS;
+ else
+ ni->ni_txrate &= IEEE80211_RATE_VAL;
amn->amn_rix = rix;
}
amn->amn_ticks = ticks;
diff --git a/sys/net80211/ieee80211_ioctl.c b/sys/net80211/ieee80211_ioctl.c
index 9f58754..6b668cb 100644
--- a/sys/net80211/ieee80211_ioctl.c
+++ b/sys/net80211/ieee80211_ioctl.c
@@ -1589,7 +1589,9 @@ ieee80211_ioctl_setmlme(struct ieee80211vap *vap, struct ieee80211req *ireq)
mlme.im_op == IEEE80211_MLME_ASSOC)
return setmlme_assoc_sta(vap, mlme.im_macaddr,
vap->iv_des_ssid[0].len, vap->iv_des_ssid[0].ssid);
- else if (mlme.im_op == IEEE80211_MLME_ASSOC)
+ else if ((vap->iv_opmode == IEEE80211_M_IBSS ||
+ vap->iv_opmode == IEEE80211_M_AHDEMO) &&
+ mlme.im_op == IEEE80211_MLME_ASSOC)
return setmlme_assoc_adhoc(vap, mlme.im_macaddr,
mlme.im_ssid_len, mlme.im_ssid);
else
diff --git a/sys/net80211/ieee80211_phy.c b/sys/net80211/ieee80211_phy.c
index d2ad60d..d1924d8 100644
--- a/sys/net80211/ieee80211_phy.c
+++ b/sys/net80211/ieee80211_phy.c
@@ -60,8 +60,11 @@ struct ieee80211_ds_plcp_hdr {
#define TURBO IEEE80211_T_TURBO
#define HALF IEEE80211_T_OFDM_HALF
#define QUART IEEE80211_T_OFDM_QUARTER
+#define HT IEEE80211_T_HT
+/* XXX the 11n and the basic rate flag are unfortunately overlapping. Grr. */
+#define N(r) (IEEE80211_RATE_MCS | r)
#define PBCC (IEEE80211_T_OFDM_QUARTER+1) /* XXX */
-#define B(r) (0x80 | r)
+#define B(r) (IEEE80211_RATE_BASIC | r)
#define Mb(x) (x*1000)
static struct ieee80211_rate_table ieee80211_11b_table = {
@@ -176,6 +179,98 @@ static struct ieee80211_rate_table ieee80211_turboa_table = {
},
};
+static struct ieee80211_rate_table ieee80211_11ng_table = {
+ .rateCount = 36,
+ .info = {
+/* short ctrl */
+/* Preamble dot11Rate Rate */
+ [0] = { .phy = CCK, 1000, 0x00, B(2), 0 },
+ [1] = { .phy = CCK, 2000, 0x04, B(4), 1 },
+ [2] = { .phy = CCK, 5500, 0x04, B(11), 2 },
+ [3] = { .phy = CCK, 11000, 0x04, B(22), 3 },
+ [4] = { .phy = OFDM, 6000, 0x00, 12, 4 },
+ [5] = { .phy = OFDM, 9000, 0x00, 18, 4 },
+ [6] = { .phy = OFDM, 12000, 0x00, 24, 6 },
+ [7] = { .phy = OFDM, 18000, 0x00, 36, 6 },
+ [8] = { .phy = OFDM, 24000, 0x00, 48, 8 },
+ [9] = { .phy = OFDM, 36000, 0x00, 72, 8 },
+ [10] = { .phy = OFDM, 48000, 0x00, 96, 8 },
+ [11] = { .phy = OFDM, 54000, 0x00, 108, 8 },
+
+ [12] = { .phy = HT, 6500, 0x00, N(0), 4 },
+ [13] = { .phy = HT, 13000, 0x00, N(1), 6 },
+ [14] = { .phy = HT, 19500, 0x00, N(2), 6 },
+ [15] = { .phy = HT, 26000, 0x00, N(3), 8 },
+ [16] = { .phy = HT, 39000, 0x00, N(4), 8 },
+ [17] = { .phy = HT, 52000, 0x00, N(5), 8 },
+ [18] = { .phy = HT, 58500, 0x00, N(6), 8 },
+ [19] = { .phy = HT, 65000, 0x00, N(7), 8 },
+
+ [20] = { .phy = HT, 13000, 0x00, N(8), 4 },
+ [21] = { .phy = HT, 26000, 0x00, N(9), 6 },
+ [22] = { .phy = HT, 39000, 0x00, N(10), 6 },
+ [23] = { .phy = HT, 52000, 0x00, N(11), 8 },
+ [24] = { .phy = HT, 78000, 0x00, N(12), 8 },
+ [25] = { .phy = HT, 104000, 0x00, N(13), 8 },
+ [26] = { .phy = HT, 117000, 0x00, N(14), 8 },
+ [27] = { .phy = HT, 130000, 0x00, N(15), 8 },
+
+ [28] = { .phy = HT, 19500, 0x00, N(16), 4 },
+ [29] = { .phy = HT, 39000, 0x00, N(17), 6 },
+ [30] = { .phy = HT, 58500, 0x00, N(18), 6 },
+ [31] = { .phy = HT, 78000, 0x00, N(19), 8 },
+ [32] = { .phy = HT, 117000, 0x00, N(20), 8 },
+ [33] = { .phy = HT, 156000, 0x00, N(21), 8 },
+ [34] = { .phy = HT, 175500, 0x00, N(22), 8 },
+ [35] = { .phy = HT, 195000, 0x00, N(23), 8 },
+
+ },
+};
+
+static struct ieee80211_rate_table ieee80211_11na_table = {
+ .rateCount = 32,
+ .info = {
+/* short ctrl */
+/* Preamble dot11Rate Rate */
+ [0] = { .phy = OFDM, 6000, 0x00, B(12), 0 },
+ [1] = { .phy = OFDM, 9000, 0x00, 18, 0 },
+ [2] = { .phy = OFDM, 12000, 0x00, B(24), 2 },
+ [3] = { .phy = OFDM, 18000, 0x00, 36, 2 },
+ [4] = { .phy = OFDM, 24000, 0x00, B(48), 4 },
+ [5] = { .phy = OFDM, 36000, 0x00, 72, 4 },
+ [6] = { .phy = OFDM, 48000, 0x00, 96, 4 },
+ [7] = { .phy = OFDM, 54000, 0x00, 108, 4 },
+
+ [8] = { .phy = HT, 6500, 0x00, N(0), 0 },
+ [9] = { .phy = HT, 13000, 0x00, N(1), 2 },
+ [10] = { .phy = HT, 19500, 0x00, N(2), 2 },
+ [11] = { .phy = HT, 26000, 0x00, N(3), 4 },
+ [12] = { .phy = HT, 39000, 0x00, N(4), 4 },
+ [13] = { .phy = HT, 52000, 0x00, N(5), 4 },
+ [14] = { .phy = HT, 58500, 0x00, N(6), 4 },
+ [15] = { .phy = HT, 65000, 0x00, N(7), 4 },
+
+ [16] = { .phy = HT, 13000, 0x00, N(8), 0 },
+ [17] = { .phy = HT, 26000, 0x00, N(9), 2 },
+ [18] = { .phy = HT, 39000, 0x00, N(10), 2 },
+ [19] = { .phy = HT, 52000, 0x00, N(11), 4 },
+ [20] = { .phy = HT, 78000, 0x00, N(12), 4 },
+ [21] = { .phy = HT, 104000, 0x00, N(13), 4 },
+ [22] = { .phy = HT, 117000, 0x00, N(14), 4 },
+ [23] = { .phy = HT, 130000, 0x00, N(15), 4 },
+
+ [24] = { .phy = HT, 19500, 0x00, N(16), 0 },
+ [25] = { .phy = HT, 39000, 0x00, N(17), 2 },
+ [26] = { .phy = HT, 58500, 0x00, N(18), 2 },
+ [27] = { .phy = HT, 78000, 0x00, N(19), 4 },
+ [28] = { .phy = HT, 117000, 0x00, N(20), 4 },
+ [29] = { .phy = HT, 156000, 0x00, N(21), 4 },
+ [30] = { .phy = HT, 175500, 0x00, N(22), 4 },
+ [31] = { .phy = HT, 195000, 0x00, N(23), 4 },
+
+ },
+};
+
#undef Mb
#undef B
#undef OFDM
@@ -184,6 +279,8 @@ static struct ieee80211_rate_table ieee80211_turboa_table = {
#undef CCK
#undef TURBO
#undef XR
+#undef HT
+#undef N
/*
* Setup a rate table's reverse lookup table and fill in
@@ -210,15 +307,23 @@ ieee80211_setup_ratetable(struct ieee80211_rate_table *rt)
uint8_t cix = rt->info[i].ctlRateIndex;
uint8_t ctl_rate = rt->info[cix].dot11Rate;
- rt->rateCodeToIndex[code] = i;
- if (code & IEEE80211_RATE_BASIC) {
- /*
- * Map w/o basic rate bit too.
- */
- code &= IEEE80211_RATE_VAL;
- rt->rateCodeToIndex[code] = i;
+ /*
+ * Map without the basic rate bit.
+ *
+ * It's up to the caller to ensure that the basic
+ * rate bit is stripped here.
+ *
+ * For HT, use the MCS rate bit.
+ */
+ code &= IEEE80211_RATE_VAL;
+ if (rt->info[i].phy == IEEE80211_T_HT) {
+ code |= IEEE80211_RATE_MCS;
}
+ /* XXX assume the control rate is non-MCS? */
+ ctl_rate &= IEEE80211_RATE_VAL;
+ rt->rateCodeToIndex[code] = i;
+
/*
* XXX for 11g the control rate to use for 5.5 and 11 Mb/s
* depends on whether they are marked as basic rates;
@@ -247,11 +352,10 @@ ieee80211_phy_init(void)
static struct ieee80211_rate_table * const ratetables[] = {
&ieee80211_half_table,
&ieee80211_quarter_table,
- &ieee80211_11a_table,
- &ieee80211_11g_table,
+ &ieee80211_11na_table,
+ &ieee80211_11ng_table,
&ieee80211_turbog_table,
&ieee80211_turboa_table,
- &ieee80211_turboa_table,
&ieee80211_11a_table,
&ieee80211_11g_table,
&ieee80211_11b_table
@@ -276,9 +380,9 @@ ieee80211_get_ratetable(struct ieee80211_channel *c)
else if (IEEE80211_IS_CHAN_QUARTER(c))
rt = &ieee80211_quarter_table;
else if (IEEE80211_IS_CHAN_HTA(c))
- rt = &ieee80211_11a_table; /* XXX */
+ rt = &ieee80211_11na_table;
else if (IEEE80211_IS_CHAN_HTG(c))
- rt = &ieee80211_11g_table; /* XXX */
+ rt = &ieee80211_11ng_table;
else if (IEEE80211_IS_CHAN_108G(c))
rt = &ieee80211_turbog_table;
else if (IEEE80211_IS_CHAN_ST(c))
@@ -463,3 +567,66 @@ ieee80211_compute_duration(const struct ieee80211_rate_table *rt,
}
return txTime;
}
+
+static const uint16_t ht20_bps[32] = {
+ 26, 52, 78, 104, 156, 208, 234, 260,
+ 52, 104, 156, 208, 312, 416, 468, 520,
+ 78, 156, 234, 312, 468, 624, 702, 780,
+ 104, 208, 312, 416, 624, 832, 936, 1040
+};
+static const uint16_t ht40_bps[32] = {
+ 54, 108, 162, 216, 324, 432, 486, 540,
+ 108, 216, 324, 432, 648, 864, 972, 1080,
+ 162, 324, 486, 648, 972, 1296, 1458, 1620,
+ 216, 432, 648, 864, 1296, 1728, 1944, 2160
+};
+
+
+#define OFDM_PLCP_BITS 22
+#define HT_L_STF 8
+#define HT_L_LTF 8
+#define HT_L_SIG 4
+#define HT_SIG 8
+#define HT_STF 4
+#define HT_LTF(n) ((n) * 4)
+
+#define HT_RC_2_MCS(_rc) ((_rc) & 0xf)
+#define HT_RC_2_STREAMS(_rc) ((((_rc) & 0x78) >> 3) + 1)
+#define IS_HT_RATE(_rc) ( (_rc) & IEEE80211_RATE_MCS)
+
+/*
+ * Calculate the transmit duration of an 11n frame.
+ */
+uint32_t
+ieee80211_compute_duration_ht(uint32_t frameLen, uint16_t rate,
+ int streams, int isht40, int isShortGI)
+{
+ uint32_t bitsPerSymbol, numBits, numSymbols, txTime;
+
+ KASSERT(rate & IEEE80211_RATE_MCS, ("not mcs %d", rate));
+ KASSERT((rate &~ IEEE80211_RATE_MCS) < 31, ("bad mcs 0x%x", rate));
+
+ if (isht40)
+ bitsPerSymbol = ht40_bps[rate & 0x1f];
+ else
+ bitsPerSymbol = ht20_bps[rate & 0x1f];
+ numBits = OFDM_PLCP_BITS + (frameLen << 3);
+ numSymbols = howmany(numBits, bitsPerSymbol);
+ if (isShortGI)
+ txTime = ((numSymbols * 18) + 4) / 5; /* 3.6us */
+ else
+ txTime = numSymbols * 4; /* 4us */
+ return txTime + HT_L_STF + HT_L_LTF +
+ HT_L_SIG + HT_SIG + HT_STF + HT_LTF(streams);
+}
+
+#undef IS_HT_RATE
+#undef HT_RC_2_STREAMS
+#undef HT_RC_2_MCS
+#undef HT_LTF
+#undef HT_STF
+#undef HT_SIG
+#undef HT_L_SIG
+#undef HT_L_LTF
+#undef HT_L_STF
+#undef OFDM_PLCP_BITS
diff --git a/sys/net80211/ieee80211_phy.h b/sys/net80211/ieee80211_phy.h
index 56b404f7..bf39cdd 100644
--- a/sys/net80211/ieee80211_phy.h
+++ b/sys/net80211/ieee80211_phy.h
@@ -60,6 +60,8 @@
struct ieee80211_channel;
+#define IEEE80211_RATE_TABLE_SIZE 128
+
struct ieee80211_rate_table {
int rateCount; /* NB: for proper padding */
uint8_t rateCodeToIndex[256]; /* back mapping */
@@ -74,7 +76,7 @@ struct ieee80211_rate_table {
* rate; used for dur. calcs */
uint16_t lpAckDuration; /* long preamble ACK dur. */
uint16_t spAckDuration; /* short preamble ACK dur. */
- } info[32];
+ } info[IEEE80211_RATE_TABLE_SIZE];
};
const struct ieee80211_rate_table *ieee80211_get_ratetable(
@@ -83,7 +85,14 @@ const struct ieee80211_rate_table *ieee80211_get_ratetable(
static __inline__ uint8_t
ieee80211_ack_rate(const struct ieee80211_rate_table *rt, uint8_t rate)
{
- uint8_t cix = rt->info[rt->rateCodeToIndex[rate]].ctlRateIndex;
+ /*
+ * XXX Assert this is for a legacy rate; not for an MCS rate.
+ * If the caller wishes to use it for a basic rate, they should
+ * clear the high bit first.
+ */
+ KASSERT(! (rate & 0x80), ("rate %d is basic/mcs?", rate));
+
+ uint8_t cix = rt->info[rt->rateCodeToIndex[rate & IEEE80211_RATE_VAL]].ctlRateIndex;
KASSERT(cix != (uint8_t)-1, ("rate %d has no info", rate));
return rt->info[cix].dot11Rate;
}
@@ -91,7 +100,14 @@ ieee80211_ack_rate(const struct ieee80211_rate_table *rt, uint8_t rate)
static __inline__ uint8_t
ieee80211_ctl_rate(const struct ieee80211_rate_table *rt, uint8_t rate)
{
- uint8_t cix = rt->info[rt->rateCodeToIndex[rate]].ctlRateIndex;
+ /*
+ * XXX Assert this is for a legacy rate; not for an MCS rate.
+ * If the caller wishes to use it for a basic rate, they should
+ * clear the high bit first.
+ */
+ KASSERT(! (rate & 0x80), ("rate %d is basic/mcs?", rate));
+
+ uint8_t cix = rt->info[rt->rateCodeToIndex[rate & IEEE80211_RATE_VAL]].ctlRateIndex;
KASSERT(cix != (uint8_t)-1, ("rate %d has no info", rate));
return rt->info[cix].dot11Rate;
}
@@ -99,7 +115,14 @@ ieee80211_ctl_rate(const struct ieee80211_rate_table *rt, uint8_t rate)
static __inline__ enum ieee80211_phytype
ieee80211_rate2phytype(const struct ieee80211_rate_table *rt, uint8_t rate)
{
- uint8_t rix = rt->rateCodeToIndex[rate];
+ /*
+ * XXX Assert this is for a legacy rate; not for an MCS rate.
+ * If the caller wishes to use it for a basic rate, they should
+ * clear the high bit first.
+ */
+ KASSERT(! (rate & 0x80), ("rate %d is basic/mcs?", rate));
+
+ uint8_t rix = rt->rateCodeToIndex[rate & IEEE80211_RATE_VAL];
KASSERT(rix != (uint8_t)-1, ("rate %d has no info", rate));
return rt->info[rix].phy;
}
@@ -107,6 +130,13 @@ ieee80211_rate2phytype(const struct ieee80211_rate_table *rt, uint8_t rate)
static __inline__ int
ieee80211_isratevalid(const struct ieee80211_rate_table *rt, uint8_t rate)
{
+ /*
+ * XXX Assert this is for a legacy rate; not for an MCS rate.
+ * If the caller wishes to use it for a basic rate, they should
+ * clear the high bit first.
+ */
+ KASSERT(! (rate & 0x80), ("rate %d is basic/mcs?", rate));
+
return rt->rateCodeToIndex[rate] != (uint8_t)-1;
}
@@ -134,6 +164,14 @@ ieee80211_ack_duration(const struct ieee80211_rate_table *rt,
}
}
+static __inline__ uint8_t
+ieee80211_legacy_rate_lookup(const struct ieee80211_rate_table *rt,
+ uint8_t rate)
+{
+
+ return (rt->rateCodeToIndex[rate & IEEE80211_RATE_VAL]);
+}
+
/*
* Compute the time to transmit a frame of length frameLen bytes
* using the specified 802.11 rate code, phy, and short preamble
@@ -151,5 +189,10 @@ uint8_t ieee80211_plcp2rate(uint8_t, enum ieee80211_phytype);
* Convert 802.11 rate code to PLCP signal.
*/
uint8_t ieee80211_rate2plcp(int, enum ieee80211_phytype);
+
+uint32_t ieee80211_compute_duration_ht(uint32_t frameLen,
+ uint16_t rate, int streams, int isht40,
+ int isShortGI);
+
#endif /* _KERNEL */
#endif /* !_NET80211_IEEE80211_PHY_H_ */
diff --git a/sys/netinet/cc/cc_cdg.c b/sys/netinet/cc/cc_cdg.c
new file mode 100644
index 0000000..69335db
--- /dev/null
+++ b/sys/netinet/cc/cc_cdg.c
@@ -0,0 +1,695 @@
+/*-
+ * Copyright (c) 2009-2013
+ * Swinburne University of Technology, Melbourne, Australia
+ * All rights reserved.
+ *
+ * This software was developed at the Centre for Advanced Internet
+ * Architectures, Swinburne University of Technology, by David Hayes, made
+ * possible in part by a gift from The Cisco University Research Program Fund,
+ * a corporate advised fund of Silicon Valley Community Foundation. Development
+ * and testing were further assisted by a grant from the FreeBSD Foundation.
+ *
+ * Redistribution and use in source and binary forms, with or without
+ * modification, are permitted provided that the following conditions
+ * are met:
+ * 1. Redistributions of source code must retain the above copyright
+ * notice, this list of conditions and the following disclaimer.
+ * 2. Redistributions in binary form must reproduce the above copyright
+ * notice, this list of conditions and the following disclaimer in the
+ * documentation and/or other materials provided with the distribution.
+ *
+ * THIS SOFTWARE IS PROVIDED BY THE AUTHOR AND CONTRIBUTORS ``AS IS'' AND
+ * ANY EXPRESS OR IMPLIED WARRANTIES, INCLUDING, BUT NOT LIMITED TO, THE
+ * IMPLIED WARRANTIES OF MERCHANTABILITY AND FITNESS FOR A PARTICULAR PURPOSE
+ * ARE DISCLAIMED. IN NO EVENT SHALL THE AUTHOR OR CONTRIBUTORS BE LIABLE
+ * FOR ANY DIRECT, INDIRECT, INCIDENTAL, SPECIAL, EXEMPLARY, OR CONSEQUENTIAL
+ * DAMAGES (INCLUDING, BUT NOT LIMITED TO, PROCUREMENT OF SUBSTITUTE GOODS
+ * OR SERVICES; LOSS OF USE, DATA, OR PROFITS; OR BUSINESS INTERRUPTION)
+ * HOWEVER CAUSED AND ON ANY THEORY OF LIABILITY, WHETHER IN CONTRACT, STRICT
+ * LIABILITY, OR TORT (INCLUDING NEGLIGENCE OR OTHERWISE) ARISING IN ANY WAY
+ * OUT OF THE USE OF THIS SOFTWARE, EVEN IF ADVISED OF THE POSSIBILITY OF
+ * SUCH DAMAGE.
+ */
+
+/*
+ * CAIA Delay-Gradient (CDG) congestion control algorithm
+ *
+ * An implemention of the delay-gradient congestion control algorithm proposed
+ * in the following paper:
+ *
+ * D. A. Hayes and G. Armitage, "Revisiting TCP Congestion Control using Delay
+ * Gradients", in IFIP Networking, Valencia, Spain, 9-13 May 2011.
+ *
+ * Developed as part of the NewTCP research project at Swinburne University of
+ * Technology's Centre for Advanced Internet Architectures, Melbourne,
+ * Australia. More details are available at:
+ * http://caia.swin.edu.au/urp/newtcp/
+ */
+
+#include <sys/cdefs.h>
+__FBSDID("$FreeBSD$");
+
+#include <sys/param.h>
+#include <sys/hhook.h>
+#include <sys/kernel.h>
+#include <sys/khelp.h>
+#include <sys/limits.h>
+#include <sys/lock.h>
+#include <sys/malloc.h>
+#include <sys/module.h>
+#include <sys/queue.h>
+#include <sys/socket.h>
+#include <sys/socketvar.h>
+#include <sys/sysctl.h>
+#include <sys/systm.h>
+
+#include <net/if.h>
+#include <net/vnet.h>
+
+#include <netinet/cc.h>
+#include <netinet/tcp_seq.h>
+#include <netinet/tcp_timer.h>
+#include <netinet/tcp_var.h>
+
+#include <netinet/cc/cc_module.h>
+
+#include <netinet/khelp/h_ertt.h>
+
+#include <vm/uma.h>
+
+#define CDG_VERSION "0.1"
+
+#define CAST_PTR_INT(X) (*((int*)(X)))
+
+#ifndef VIMAGE
+#define vnet_sysctl_handle_uint(oidp, arg1, arg2, req) \
+ sysctl_handle_int(oidp, arg1, arg2, req)
+#endif
+
+/* Private delay-gradient induced congestion control signal. */
+#define CC_CDG_DELAY 0x01000000
+
+/* NewReno window deflation factor on loss (as a percentage). */
+#define RENO_BETA 50
+
+/* Queue states. */
+#define CDG_Q_EMPTY 1
+#define CDG_Q_RISING 2
+#define CDG_Q_FALLING 3
+#define CDG_Q_FULL 4
+#define CDG_Q_UNKNOWN 9999
+
+/* Number of bit shifts used in probexp lookup table. */
+#define EXP_PREC 15
+
+/* Largest gradient represented in probexp lookup table. */
+#define MAXGRAD 5
+
+/*
+ * Delay Precision Enhance - number of bit shifts used for qtrend related
+ * integer arithmetic precision.
+ */
+#define D_P_E 7
+
+struct qdiff_sample {
+ long qdiff;
+ STAILQ_ENTRY(qdiff_sample) qdiff_lnk;
+};
+
+struct cdg {
+ long max_qtrend;
+ long min_qtrend;
+ STAILQ_HEAD(minrtts_head, qdiff_sample) qdiffmin_q;
+ STAILQ_HEAD(maxrtts_head, qdiff_sample) qdiffmax_q;
+ long window_incr;
+ /* rttcount for window increase when in congestion avoidance */
+ long rtt_count;
+ /* maximum measured rtt within an rtt period */
+ int maxrtt_in_rtt;
+ /* maximum measured rtt within prev rtt period */
+ int maxrtt_in_prevrtt;
+ /* minimum measured rtt within an rtt period */
+ int minrtt_in_rtt;
+ /* minimum measured rtt within prev rtt period */
+ int minrtt_in_prevrtt;
+ /* consecutive congestion episode counter */
+ uint32_t consec_cong_cnt;
+ /* when tracking a new reno type loss window */
+ uint32_t shadow_w;
+ /* maximum number of samples in the moving average queue */
+ int sample_q_size;
+ /* number of samples in the moving average queue */
+ int num_samples;
+ /* estimate of the queue state of the path */
+ int queue_state;
+};
+
+/*
+ * Lookup table for:
+ * (1 - exp(-x)) << EXP_PREC, where x = [0,MAXGRAD] in 2^-7 increments
+ *
+ * Note: probexp[0] is set to 10 (not 0) as a safety for very low increase
+ * gradients.
+ */
+static const int probexp[641] = {
+ 10,255,508,759,1008,1255,1501,1744,1985,2225,2463,2698,2932,3165,3395,3624,
+ 3850,4075,4299,4520,4740,4958,5175,5389,5602,5814,6024,6232,6438,6643,6846,
+ 7048,7248,7447,7644,7839,8033,8226,8417,8606,8794,8981,9166,9350,9532,9713,
+ 9892,10070,10247,10422,10596,10769,10940,11110,11278,11445,11611,11776,11939,
+ 12101,12262,12422,12580,12737,12893,13048,13201,13354,13505,13655,13803,13951,
+ 14097,14243,14387,14530,14672,14813,14952,15091,15229,15365,15500,15635,15768,
+ 15900,16032,16162,16291,16419,16547,16673,16798,16922,17046,17168,17289,17410,
+ 17529,17648,17766,17882,17998,18113,18227,18340,18453,18564,18675,18784,18893,
+ 19001,19108,19215,19320,19425,19529,19632,19734,19835,19936,20036,20135,20233,
+ 20331,20427,20523,20619,20713,20807,20900,20993,21084,21175,21265,21355,21444,
+ 21532,21619,21706,21792,21878,21962,22046,22130,22213,22295,22376,22457,22537,
+ 22617,22696,22774,22852,22929,23006,23082,23157,23232,23306,23380,23453,23525,
+ 23597,23669,23739,23810,23879,23949,24017,24085,24153,24220,24286,24352,24418,
+ 24483,24547,24611,24675,24738,24800,24862,24924,24985,25045,25106,25165,25224,
+ 25283,25341,25399,25456,25513,25570,25626,25681,25737,25791,25846,25899,25953,
+ 26006,26059,26111,26163,26214,26265,26316,26366,26416,26465,26514,26563,26611,
+ 26659,26707,26754,26801,26847,26893,26939,26984,27029,27074,27118,27162,27206,
+ 27249,27292,27335,27377,27419,27460,27502,27543,27583,27624,27664,27703,27743,
+ 27782,27821,27859,27897,27935,27973,28010,28047,28084,28121,28157,28193,28228,
+ 28263,28299,28333,28368,28402,28436,28470,28503,28536,28569,28602,28634,28667,
+ 28699,28730,28762,28793,28824,28854,28885,28915,28945,28975,29004,29034,29063,
+ 29092,29120,29149,29177,29205,29232,29260,29287,29314,29341,29368,29394,29421,
+ 29447,29472,29498,29524,29549,29574,29599,29623,29648,29672,29696,29720,29744,
+ 29767,29791,29814,29837,29860,29882,29905,29927,29949,29971,29993,30014,30036,
+ 30057,30078,30099,30120,30141,30161,30181,30201,30221,30241,30261,30280,30300,
+ 30319,30338,30357,30376,30394,30413,30431,30449,30467,30485,30503,30521,30538,
+ 30555,30573,30590,30607,30624,30640,30657,30673,30690,30706,30722,30738,30753,
+ 30769,30785,30800,30815,30831,30846,30861,30876,30890,30905,30919,30934,30948,
+ 30962,30976,30990,31004,31018,31031,31045,31058,31072,31085,31098,31111,31124,
+ 31137,31149,31162,31174,31187,31199,31211,31223,31235,31247,31259,31271,31283,
+ 31294,31306,31317,31328,31339,31351,31362,31373,31383,31394,31405,31416,31426,
+ 31436,31447,31457,31467,31477,31487,31497,31507,31517,31527,31537,31546,31556,
+ 31565,31574,31584,31593,31602,31611,31620,31629,31638,31647,31655,31664,31673,
+ 31681,31690,31698,31706,31715,31723,31731,31739,31747,31755,31763,31771,31778,
+ 31786,31794,31801,31809,31816,31824,31831,31838,31846,31853,31860,31867,31874,
+ 31881,31888,31895,31902,31908,31915,31922,31928,31935,31941,31948,31954,31960,
+ 31967,31973,31979,31985,31991,31997,32003,32009,32015,32021,32027,32033,32038,
+ 32044,32050,32055,32061,32066,32072,32077,32083,32088,32093,32098,32104,32109,
+ 32114,32119,32124,32129,32134,32139,32144,32149,32154,32158,32163,32168,32173,
+ 32177,32182,32186,32191,32195,32200,32204,32209,32213,32217,32222,32226,32230,
+ 32234,32238,32242,32247,32251,32255,32259,32263,32267,32270,32274,32278,32282,
+ 32286,32290,32293,32297,32301,32304,32308,32311,32315,32318,32322,32325,32329,
+ 32332,32336,32339,32342,32346,32349,32352,32356,32359,32362,32365,32368,32371,
+ 32374,32377,32381,32384,32387,32389,32392,32395,32398,32401,32404,32407,32410,
+ 32412,32415,32418,32421,32423,32426,32429,32431,32434,32437,32439,32442,32444,
+ 32447,32449,32452,32454,32457,32459,32461,32464,32466,32469,32471,32473,32476,
+ 32478,32480,32482,32485,32487,32489,32491,32493,32495,32497,32500,32502,32504,
+ 32506,32508,32510,32512,32514,32516,32518,32520,32522,32524,32526,32527,32529,
+ 32531,32533,32535,32537,32538,32540,32542,32544,32545,32547};
+
+static uma_zone_t qdiffsample_zone;
+
+static MALLOC_DEFINE(M_CDG, "cdg data",
+ "Per connection data required for the CDG congestion control algorithm");
+
+static int ertt_id;
+
+static VNET_DEFINE(uint32_t, cdg_alpha_inc);
+static VNET_DEFINE(uint32_t, cdg_beta_delay);
+static VNET_DEFINE(uint32_t, cdg_beta_loss);
+static VNET_DEFINE(uint32_t, cdg_smoothing_factor);
+static VNET_DEFINE(uint32_t, cdg_exp_backoff_scale);
+static VNET_DEFINE(uint32_t, cdg_consec_cong);
+static VNET_DEFINE(uint32_t, cdg_hold_backoff);
+#define V_cdg_alpha_inc VNET(cdg_alpha_inc)
+#define V_cdg_beta_delay VNET(cdg_beta_delay)
+#define V_cdg_beta_loss VNET(cdg_beta_loss)
+#define V_cdg_smoothing_factor VNET(cdg_smoothing_factor)
+#define V_cdg_exp_backoff_scale VNET(cdg_exp_backoff_scale)
+#define V_cdg_consec_cong VNET(cdg_consec_cong)
+#define V_cdg_hold_backoff VNET(cdg_hold_backoff)
+
+/* Function prototypes. */
+static int cdg_mod_init(void);
+static void cdg_conn_init(struct cc_var *ccv);
+static int cdg_cb_init(struct cc_var *ccv);
+static void cdg_cb_destroy(struct cc_var *ccv);
+static void cdg_cong_signal(struct cc_var *ccv, uint32_t signal_type);
+static void cdg_ack_received(struct cc_var *ccv, uint16_t ack_type);
+
+struct cc_algo cdg_cc_algo = {
+ .name = "cdg",
+ .mod_init = cdg_mod_init,
+ .ack_received = cdg_ack_received,
+ .cb_destroy = cdg_cb_destroy,
+ .cb_init = cdg_cb_init,
+ .conn_init = cdg_conn_init,
+ .cong_signal = cdg_cong_signal
+};
+
+/* Vnet created and being initialised. */
+static void
+cdg_init_vnet(const void *unused __unused)
+{
+
+ V_cdg_alpha_inc = 0;
+ V_cdg_beta_delay = 70;
+ V_cdg_beta_loss = 50;
+ V_cdg_smoothing_factor = 8;
+ V_cdg_exp_backoff_scale = 3;
+ V_cdg_consec_cong = 5;
+ V_cdg_hold_backoff = 5;
+}
+
+static int
+cdg_mod_init(void)
+{
+ VNET_ITERATOR_DECL(v);
+
+ ertt_id = khelp_get_id("ertt");
+ if (ertt_id <= 0)
+ return (EINVAL);
+
+ qdiffsample_zone = uma_zcreate("cdg_qdiffsample",
+ sizeof(struct qdiff_sample), NULL, NULL, NULL, NULL, 0, 0);
+
+ VNET_LIST_RLOCK();
+ VNET_FOREACH(v) {
+ CURVNET_SET(v);
+ cdg_init_vnet(NULL);
+ CURVNET_RESTORE();
+ }
+ VNET_LIST_RUNLOCK();
+
+ cdg_cc_algo.post_recovery = newreno_cc_algo.post_recovery;
+ cdg_cc_algo.after_idle = newreno_cc_algo.after_idle;
+
+ return (0);
+}
+
+static int
+cdg_cb_init(struct cc_var *ccv)
+{
+ struct cdg *cdg_data;
+
+ cdg_data = malloc(sizeof(struct cdg), M_CDG, M_NOWAIT);
+ if (cdg_data == NULL)
+ return (ENOMEM);
+
+ cdg_data->shadow_w = 0;
+ cdg_data->max_qtrend = 0;
+ cdg_data->min_qtrend = 0;
+ cdg_data->queue_state = CDG_Q_UNKNOWN;
+ cdg_data->maxrtt_in_rtt = 0;
+ cdg_data->maxrtt_in_prevrtt = 0;
+ cdg_data->minrtt_in_rtt = INT_MAX;
+ cdg_data->minrtt_in_prevrtt = 0;
+ cdg_data->window_incr = 0;
+ cdg_data->rtt_count = 0;
+ cdg_data->consec_cong_cnt = 0;
+ cdg_data->sample_q_size = V_cdg_smoothing_factor;
+ cdg_data->num_samples = 0;
+ STAILQ_INIT(&cdg_data->qdiffmin_q);
+ STAILQ_INIT(&cdg_data->qdiffmax_q);
+
+ ccv->cc_data = cdg_data;
+
+ return (0);
+}
+
+static void
+cdg_conn_init(struct cc_var *ccv)
+{
+ struct cdg *cdg_data = ccv->cc_data;
+
+ /*
+ * Initialise the shadow_cwnd in case we are competing with loss based
+ * flows from the start
+ */
+ cdg_data->shadow_w = CCV(ccv, snd_cwnd);
+}
+
+static void
+cdg_cb_destroy(struct cc_var *ccv)
+{
+ struct cdg *cdg_data;
+ struct qdiff_sample *qds, *qds_n;
+
+ cdg_data = ccv->cc_data;
+
+ qds = STAILQ_FIRST(&cdg_data->qdiffmin_q);
+ while (qds != NULL) {
+ qds_n = STAILQ_NEXT(qds, qdiff_lnk);
+ uma_zfree(qdiffsample_zone,qds);
+ qds = qds_n;
+ }
+
+ qds = STAILQ_FIRST(&cdg_data->qdiffmax_q);
+ while (qds != NULL) {
+ qds_n = STAILQ_NEXT(qds, qdiff_lnk);
+ uma_zfree(qdiffsample_zone,qds);
+ qds = qds_n;
+ }
+
+ free(ccv->cc_data, M_CDG);
+}
+
+static int
+cdg_beta_handler(SYSCTL_HANDLER_ARGS)
+{
+
+ if (req->newptr != NULL &&
+ (CAST_PTR_INT(req->newptr) == 0 || CAST_PTR_INT(req->newptr) > 100))
+ return (EINVAL);
+
+ return (vnet_sysctl_handle_uint(oidp, arg1, arg2, req));
+}
+
+static int
+cdg_exp_backoff_scale_handler(SYSCTL_HANDLER_ARGS)
+{
+
+ if (req->newptr != NULL && CAST_PTR_INT(req->newptr) < 1)
+ return (EINVAL);
+
+ return (vnet_sysctl_handle_uint(oidp, arg1, arg2, req));
+}
+
+static inline unsigned long
+cdg_window_decrease(struct cc_var *ccv, unsigned long owin, unsigned int beta)
+{
+
+ return ((ulmin(CCV(ccv, snd_wnd), owin) * beta) / 100);
+}
+
+/*
+ * Window increase function
+ * This window increase function is independent of the initial window size
+ * to ensure small window flows are not discriminated against (i.e. fairness).
+ * It increases at 1pkt/rtt like Reno for alpha_inc rtts, and then 2pkts/rtt for
+ * the next alpha_inc rtts, etc.
+ */
+static void
+cdg_window_increase(struct cc_var *ccv, int new_measurement)
+{
+ struct cdg *cdg_data;
+ int incr, s_w_incr;
+
+ cdg_data = ccv->cc_data;
+ incr = s_w_incr = 0;
+
+ if (CCV(ccv, snd_cwnd) <= CCV(ccv, snd_ssthresh)) {
+ /* Slow start. */
+ incr = CCV(ccv, t_maxseg);
+ s_w_incr = incr;
+ cdg_data->window_incr = cdg_data->rtt_count = 0;
+ } else {
+ /* Congestion avoidance. */
+ if (new_measurement) {
+ s_w_incr = CCV(ccv, t_maxseg);
+ if (V_cdg_alpha_inc == 0) {
+ incr = CCV(ccv, t_maxseg);
+ } else {
+ if (++cdg_data->rtt_count >= V_cdg_alpha_inc) {
+ cdg_data->window_incr++;
+ cdg_data->rtt_count = 0;
+ }
+ incr = CCV(ccv, t_maxseg) *
+ cdg_data->window_incr;
+ }
+ }
+ }
+
+ if (cdg_data->shadow_w > 0)
+ cdg_data->shadow_w = ulmin(cdg_data->shadow_w + s_w_incr,
+ TCP_MAXWIN << CCV(ccv, snd_scale));
+
+ CCV(ccv, snd_cwnd) = ulmin(CCV(ccv, snd_cwnd) + incr,
+ TCP_MAXWIN << CCV(ccv, snd_scale));
+}
+
+static void
+cdg_cong_signal(struct cc_var *ccv, uint32_t signal_type)
+{
+ struct cdg *cdg_data = ccv->cc_data;
+
+ switch(signal_type) {
+ case CC_CDG_DELAY:
+ CCV(ccv, snd_ssthresh) = cdg_window_decrease(ccv,
+ CCV(ccv, snd_cwnd), V_cdg_beta_delay);
+ CCV(ccv, snd_cwnd) = CCV(ccv, snd_ssthresh);
+ CCV(ccv, snd_recover) = CCV(ccv, snd_max);
+ cdg_data->window_incr = cdg_data->rtt_count = 0;
+ ENTER_CONGRECOVERY(CCV(ccv, t_flags));
+ break;
+ case CC_NDUPACK:
+ /*
+ * If already responding to congestion OR we have guessed no
+ * queue in the path is full.
+ */
+ if (IN_CONGRECOVERY(CCV(ccv, t_flags)) ||
+ cdg_data->queue_state < CDG_Q_FULL) {
+ CCV(ccv, snd_ssthresh) = CCV(ccv, snd_cwnd);
+ CCV(ccv, snd_recover) = CCV(ccv, snd_max);
+ } else {
+ /*
+ * Loss is likely to be congestion related. We have
+ * inferred a queue full state, so have shadow window
+ * react to loss as NewReno would.
+ */
+ if (cdg_data->shadow_w > 0)
+ cdg_data->shadow_w = cdg_window_decrease(ccv,
+ cdg_data->shadow_w, RENO_BETA);
+
+ CCV(ccv, snd_ssthresh) = ulmax(cdg_data->shadow_w,
+ cdg_window_decrease(ccv, CCV(ccv, snd_cwnd),
+ V_cdg_beta_loss));
+
+ cdg_data->window_incr = cdg_data->rtt_count = 0;
+ }
+ ENTER_RECOVERY(CCV(ccv, t_flags));
+ break;
+ default:
+ newreno_cc_algo.cong_signal(ccv, signal_type);
+ break;
+ }
+}
+
+/*
+ * Using a negative exponential probabilistic backoff so that sources with
+ * varying RTTs which share the same link will, on average, have the same
+ * probability of backoff over time.
+ *
+ * Prob_backoff = 1 - exp(-qtrend / V_cdg_exp_backoff_scale), where
+ * V_cdg_exp_backoff_scale is the average qtrend for the exponential backoff.
+ */
+static inline int
+prob_backoff(long qtrend)
+{
+ int backoff, idx, p;
+
+ backoff = (qtrend > ((MAXGRAD * V_cdg_exp_backoff_scale) << D_P_E));
+
+ if (!backoff) {
+ if (V_cdg_exp_backoff_scale > 1)
+ idx = (qtrend + V_cdg_exp_backoff_scale / 2) /
+ V_cdg_exp_backoff_scale;
+ else
+ idx = qtrend;
+
+ /* Backoff probability proportional to rate of queue growth. */
+ p = (INT_MAX / (1 << EXP_PREC)) * probexp[idx];
+ backoff = (random() < p);
+ }
+
+ return (backoff);
+}
+
+static inline void
+calc_moving_average(struct cdg *cdg_data, long qdiff_max, long qdiff_min)
+{
+ struct qdiff_sample *qds;
+
+ ++cdg_data->num_samples;
+ if (cdg_data->num_samples > cdg_data->sample_q_size) {
+ /* Minimum RTT. */
+ qds = STAILQ_FIRST(&cdg_data->qdiffmin_q);
+ cdg_data->min_qtrend = cdg_data->min_qtrend +
+ (qdiff_min - qds->qdiff) / cdg_data->sample_q_size;
+ STAILQ_REMOVE_HEAD(&cdg_data->qdiffmin_q, qdiff_lnk);
+ qds->qdiff = qdiff_min;
+ STAILQ_INSERT_TAIL(&cdg_data->qdiffmin_q, qds, qdiff_lnk);
+
+ /* Maximum RTT. */
+ qds = STAILQ_FIRST(&cdg_data->qdiffmax_q);
+ cdg_data->max_qtrend = cdg_data->max_qtrend +
+ (qdiff_max - qds->qdiff) / cdg_data->sample_q_size;
+ STAILQ_REMOVE_HEAD(&cdg_data->qdiffmax_q, qdiff_lnk);
+ qds->qdiff = qdiff_max;
+ STAILQ_INSERT_TAIL(&cdg_data->qdiffmax_q, qds, qdiff_lnk);
+ --cdg_data->num_samples;
+ } else {
+ qds = uma_zalloc(qdiffsample_zone, M_NOWAIT);
+ if (qds != NULL) {
+ cdg_data->min_qtrend = cdg_data->min_qtrend +
+ qdiff_min / cdg_data->sample_q_size;
+ qds->qdiff = qdiff_min;
+ STAILQ_INSERT_TAIL(&cdg_data->qdiffmin_q, qds,
+ qdiff_lnk);
+ }
+
+ qds = uma_zalloc(qdiffsample_zone, M_NOWAIT);
+ if (qds) {
+ cdg_data->max_qtrend = cdg_data->max_qtrend +
+ qdiff_max / cdg_data->sample_q_size;
+ qds->qdiff = qdiff_max;
+ STAILQ_INSERT_TAIL(&cdg_data->qdiffmax_q, qds,
+ qdiff_lnk);
+ }
+ }
+}
+
+static void
+cdg_ack_received(struct cc_var *ccv, uint16_t ack_type)
+{
+ struct cdg *cdg_data;
+ struct ertt *e_t;
+ long qdiff_max, qdiff_min;
+ int congestion, new_measurement, slowstart;
+
+ cdg_data = ccv->cc_data;
+ e_t = (struct ertt *)khelp_get_osd(CCV(ccv, osd), ertt_id);
+ new_measurement = e_t->flags & ERTT_NEW_MEASUREMENT;
+ congestion = 0;
+ cdg_data->maxrtt_in_rtt = imax(e_t->rtt, cdg_data->maxrtt_in_rtt);
+ cdg_data->minrtt_in_rtt = imin(e_t->rtt, cdg_data->minrtt_in_rtt);
+
+ if (new_measurement) {
+ slowstart = (CCV(ccv, snd_cwnd) <= CCV(ccv, snd_ssthresh));
+ /*
+ * Update smoothed gradient measurements. Since we are only
+ * using one measurement per RTT, use max or min rtt_in_rtt.
+ * This is also less noisy than a sample RTT measurement. Max
+ * RTT measurements can have trouble due to OS issues.
+ */
+ if (cdg_data->maxrtt_in_prevrtt) {
+ qdiff_max = ((long)(cdg_data->maxrtt_in_rtt -
+ cdg_data->maxrtt_in_prevrtt) << D_P_E );
+ qdiff_min = ((long)(cdg_data->minrtt_in_rtt -
+ cdg_data->minrtt_in_prevrtt) << D_P_E );
+
+ calc_moving_average(cdg_data, qdiff_max, qdiff_min);
+
+ /* Probabilistic backoff with respect to gradient. */
+ if (slowstart && qdiff_min > 0)
+ congestion = prob_backoff(qdiff_min);
+ else if (cdg_data->min_qtrend > 0)
+ congestion = prob_backoff(cdg_data->min_qtrend);
+ else if (slowstart && qdiff_max > 0)
+ congestion = prob_backoff(qdiff_max);
+ else if (cdg_data->max_qtrend > 0)
+ congestion = prob_backoff(cdg_data->max_qtrend);
+
+ /* Update estimate of queue state. */
+ if (cdg_data->min_qtrend > 0 &&
+ cdg_data->max_qtrend <= 0) {
+ cdg_data->queue_state = CDG_Q_FULL;
+ } else if (cdg_data->min_qtrend >= 0 &&
+ cdg_data->max_qtrend < 0) {
+ cdg_data->queue_state = CDG_Q_EMPTY;
+ cdg_data->shadow_w = 0;
+ } else if (cdg_data->min_qtrend > 0 &&
+ cdg_data->max_qtrend > 0) {
+ cdg_data->queue_state = CDG_Q_RISING;
+ } else if (cdg_data->min_qtrend < 0 &&
+ cdg_data->max_qtrend < 0) {
+ cdg_data->queue_state = CDG_Q_FALLING;
+ }
+
+ if (cdg_data->min_qtrend < 0 ||
+ cdg_data->max_qtrend < 0)
+ cdg_data->consec_cong_cnt = 0;
+ }
+
+ cdg_data->minrtt_in_prevrtt = cdg_data->minrtt_in_rtt;
+ cdg_data->minrtt_in_rtt = INT_MAX;
+ cdg_data->maxrtt_in_prevrtt = cdg_data->maxrtt_in_rtt;
+ cdg_data->maxrtt_in_rtt = 0;
+ e_t->flags &= ~ERTT_NEW_MEASUREMENT;
+ }
+
+ if (congestion) {
+ cdg_data->consec_cong_cnt++;
+ if (!IN_RECOVERY(CCV(ccv, t_flags))) {
+ if (cdg_data->consec_cong_cnt <= V_cdg_consec_cong)
+ cdg_cong_signal(ccv, CC_CDG_DELAY);
+ else
+ /*
+ * We have been backing off but the queue is not
+ * falling. Assume we are competing with
+ * loss-based flows and don't back off for the
+ * next V_cdg_hold_backoff RTT periods.
+ */
+ if (cdg_data->consec_cong_cnt >=
+ V_cdg_consec_cong + V_cdg_hold_backoff)
+ cdg_data->consec_cong_cnt = 0;
+
+ /* Won't see effect until 2nd RTT. */
+ cdg_data->maxrtt_in_prevrtt = 0;
+ /*
+ * Resync shadow window in case we are competing with a
+ * loss based flow
+ */
+ cdg_data->shadow_w = ulmax(CCV(ccv, snd_cwnd),
+ cdg_data->shadow_w);
+ }
+ } else if (ack_type == CC_ACK)
+ cdg_window_increase(ccv, new_measurement);
+}
+
+/* When a vnet is created and being initialised, init the per-stack CDG vars. */
+VNET_SYSINIT(cdg_init_vnet, SI_SUB_PROTO_BEGIN, SI_ORDER_FIRST,
+ cdg_init_vnet, NULL);
+
+SYSCTL_DECL(_net_inet_tcp_cc_cdg);
+SYSCTL_NODE(_net_inet_tcp_cc, OID_AUTO, cdg, CTLFLAG_RW, NULL,
+ "CAIA delay-gradient congestion control related settings");
+
+SYSCTL_STRING(_net_inet_tcp_cc_cdg, OID_AUTO, version,
+ CTLFLAG_RD, CDG_VERSION, sizeof(CDG_VERSION) - 1,
+ "Current algorithm/implementation version number");
+
+SYSCTL_VNET_UINT(_net_inet_tcp_cc_cdg, OID_AUTO, alpha_inc,
+ CTLFLAG_RW, &VNET_NAME(cdg_alpha_inc), 0,
+ "Increment the window increase factor alpha by 1 MSS segment every "
+ "alpha_inc RTTs during congestion avoidance mode.");
+
+SYSCTL_VNET_PROC(_net_inet_tcp_cc_cdg, OID_AUTO, beta_delay,
+ CTLTYPE_UINT|CTLFLAG_RW, &VNET_NAME(cdg_beta_delay), 70,
+ &cdg_beta_handler, "IU",
+ "Delay-based window decrease factor as a percentage "
+ "(on delay-based backoff, w = w * beta_delay / 100)");
+
+SYSCTL_VNET_PROC(_net_inet_tcp_cc_cdg, OID_AUTO, beta_loss,
+ CTLTYPE_UINT|CTLFLAG_RW, &VNET_NAME(cdg_beta_loss), 50,
+ &cdg_beta_handler, "IU",
+ "Loss-based window decrease factor as a percentage "
+ "(on loss-based backoff, w = w * beta_loss / 100)");
+
+SYSCTL_VNET_PROC(_net_inet_tcp_cc_cdg, OID_AUTO, exp_backoff_scale,
+ CTLTYPE_UINT|CTLFLAG_RW, &VNET_NAME(cdg_exp_backoff_scale), 2,
+ &cdg_exp_backoff_scale_handler, "IU",
+ "Scaling parameter for the probabilistic exponential backoff");
+
+SYSCTL_VNET_UINT(_net_inet_tcp_cc_cdg, OID_AUTO, smoothing_factor,
+ CTLFLAG_RW, &VNET_NAME(cdg_smoothing_factor), 8,
+ "Number of samples used for moving average smoothing (0 = no smoothing)");
+
+SYSCTL_VNET_UINT(_net_inet_tcp_cc_cdg, OID_AUTO, loss_compete_consec_cong,
+ CTLFLAG_RW, &VNET_NAME(cdg_consec_cong), 5,
+ "Number of consecutive delay-gradient based congestion episodes which will "
+ "trigger loss based CC compatibility");
+
+SYSCTL_VNET_UINT(_net_inet_tcp_cc_cdg, OID_AUTO, loss_compete_hold_backoff,
+ CTLFLAG_RW, &VNET_NAME(cdg_hold_backoff), 5,
+ "Number of consecutive delay-gradient based congestion episodes to hold "
+ "the window backoff for loss based CC compatibility");
+
+DECLARE_CC_MODULE(cdg, &cdg_cc_algo);
+
+MODULE_DEPEND(cdg, ertt, 1, 1, 1);
diff --git a/sys/netinet/if_ether.c b/sys/netinet/if_ether.c
index 37249e5..ad31557 100644
--- a/sys/netinet/if_ether.c
+++ b/sys/netinet/if_ether.c
@@ -174,7 +174,7 @@ arptimer(void *arg)
ifp = lle->lle_tbl->llt_ifp;
CURVNET_SET(ifp->if_vnet);
- if (lle->la_flags != LLE_DELETED) {
+ if ((lle->la_flags & LLE_DELETED) == 0) {
int evt;
if (lle->la_flags & LLE_VALID)
diff --git a/sys/netinet/in_mcast.c b/sys/netinet/in_mcast.c
index b0d53b0..31f2fe1 100644
--- a/sys/netinet/in_mcast.c
+++ b/sys/netinet/in_mcast.c
@@ -1236,7 +1236,9 @@ in_leavegroup_locked(struct in_multi *inm, /*const*/ struct in_mfilter *imf)
KASSERT(error == 0, ("%s: failed to merge inm state", __func__));
CTR1(KTR_IGMPV3, "%s: doing igmp downcall", __func__);
+ CURVNET_SET(inm->inm_ifp->if_vnet);
error = igmp_change_state(inm);
+ CURVNET_RESTORE();
if (error)
CTR1(KTR_IGMPV3, "%s: failed igmp downcall", __func__);
diff --git a/sys/netinet/in_pcb.c b/sys/netinet/in_pcb.c
index f5e2ef2..3506b74 100644
--- a/sys/netinet/in_pcb.c
+++ b/sys/netinet/in_pcb.c
@@ -467,6 +467,23 @@ in_pcb_lport(struct inpcb *inp, struct in_addr *laddrp, u_short *lportp,
return (0);
}
+
+/*
+ * Return cached socket options.
+ */
+short
+inp_so_options(const struct inpcb *inp)
+{
+ short so_options;
+
+ so_options = 0;
+
+ if ((inp->inp_flags2 & INP_REUSEPORT) != 0)
+ so_options |= SO_REUSEPORT;
+ if ((inp->inp_flags2 & INP_REUSEADDR) != 0)
+ so_options |= SO_REUSEADDR;
+ return (so_options);
+}
#endif /* INET || INET6 */
#ifdef INET
@@ -595,8 +612,7 @@ in_pcbbind_setup(struct inpcb *inp, struct sockaddr *nam, in_addr_t *laddrp,
if (tw == NULL ||
(reuseport & tw->tw_so_options) == 0)
return (EADDRINUSE);
- } else if (t && (reuseport == 0 ||
- (t->inp_flags2 & INP_REUSEPORT) == 0)) {
+ } else if (t && (reuseport & inp_so_options(t)) == 0) {
#ifdef INET6
if (ntohl(sin->sin_addr.s_addr) !=
INADDR_ANY ||
diff --git a/sys/netinet/in_pcb.h b/sys/netinet/in_pcb.h
index 2df90b0..1760bca 100644
--- a/sys/netinet/in_pcb.h
+++ b/sys/netinet/in_pcb.h
@@ -442,6 +442,7 @@ struct tcpcb *
inp_inpcbtotcpcb(struct inpcb *inp);
void inp_4tuple_get(struct inpcb *inp, uint32_t *laddr, uint16_t *lp,
uint32_t *faddr, uint16_t *fp);
+short inp_so_options(const struct inpcb *inp);
#endif /* _KERNEL */
@@ -543,6 +544,7 @@ void inp_4tuple_get(struct inpcb *inp, uint32_t *laddr, uint16_t *lp,
#define INP_PCBGROUPWILD 0x00000004 /* in pcbgroup wildcard list */
#define INP_REUSEPORT 0x00000008 /* SO_REUSEPORT option is set */
#define INP_FREED 0x00000010 /* inp itself is not valid */
+#define INP_REUSEADDR 0x00000020 /* SO_REUSEADDR option is set */
/*
* Flags passed to in_pcblookup*() functions.
diff --git a/sys/netinet/ip_output.c b/sys/netinet/ip_output.c
index 21d47f7..d835bd2 100644
--- a/sys/netinet/ip_output.c
+++ b/sys/netinet/ip_output.c
@@ -900,13 +900,10 @@ ip_ctloutput(struct socket *so, struct sockopt *sopt)
switch (sopt->sopt_name) {
case SO_REUSEADDR:
INP_WLOCK(inp);
- if (IN_MULTICAST(ntohl(inp->inp_laddr.s_addr))) {
- if ((so->so_options &
- (SO_REUSEADDR | SO_REUSEPORT)) != 0)
- inp->inp_flags2 |= INP_REUSEPORT;
- else
- inp->inp_flags2 &= ~INP_REUSEPORT;
- }
+ if ((so->so_options & SO_REUSEADDR) != 0)
+ inp->inp_flags2 |= INP_REUSEADDR;
+ else
+ inp->inp_flags2 &= ~INP_REUSEADDR;
INP_WUNLOCK(inp);
error = 0;
break;
diff --git a/sys/netinet/sctp_indata.c b/sys/netinet/sctp_indata.c
index b4a0d08..7bad54c 100644
--- a/sys/netinet/sctp_indata.c
+++ b/sys/netinet/sctp_indata.c
@@ -1729,7 +1729,6 @@ sctp_process_a_data_chunk(struct sctp_tcb *stcb, struct sctp_association *asoc,
sctp_alloc_a_readq(stcb, control);
sctp_build_readq_entry_mac(control, stcb, asoc->context, net, tsn,
protocol_id,
- stcb->asoc.context,
strmno, strmseq,
chunk_flags,
dmbuf);
@@ -1857,7 +1856,6 @@ failed_pdapi_express_del:
sctp_alloc_a_readq(stcb, control);
sctp_build_readq_entry_mac(control, stcb, asoc->context, net, tsn,
protocol_id,
- stcb->asoc.context,
strmno, strmseq,
chunk_flags,
dmbuf);
diff --git a/sys/netinet/sctp_indata.h b/sys/netinet/sctp_indata.h
index 5eaa1f4..79a86e2 100644
--- a/sys/netinet/sctp_indata.h
+++ b/sys/netinet/sctp_indata.h
@@ -47,14 +47,14 @@ sctp_build_readq_entry(struct sctp_tcb *stcb,
struct mbuf *dm);
-#define sctp_build_readq_entry_mac(_ctl, in_it, a, net, tsn, ppid, context, stream_no, stream_seq, flags, dm) do { \
+#define sctp_build_readq_entry_mac(_ctl, in_it, context, net, tsn, ppid, stream_no, stream_seq, flags, dm) do { \
if (_ctl) { \
atomic_add_int(&((net)->ref_count), 1); \
(_ctl)->sinfo_stream = stream_no; \
(_ctl)->sinfo_ssn = stream_seq; \
(_ctl)->sinfo_flags = (flags << 8); \
(_ctl)->sinfo_ppid = ppid; \
- (_ctl)->sinfo_context = a; \
+ (_ctl)->sinfo_context = context; \
(_ctl)->sinfo_timetolive = 0; \
(_ctl)->sinfo_tsn = tsn; \
(_ctl)->sinfo_cumtsn = tsn; \
diff --git a/sys/netinet/sctp_input.c b/sys/netinet/sctp_input.c
index 0528d3d..c19464f 100644
--- a/sys/netinet/sctp_input.c
+++ b/sys/netinet/sctp_input.c
@@ -1028,12 +1028,13 @@ sctp_handle_shutdown_ack(struct sctp_shutdown_ack_chunk *cp SCTP_UNUSED,
SCTP_SOCKET_UNLOCK(so, 1);
#endif
}
- /* are the queues empty? */
+#ifdef INVARIANTS
if (!TAILQ_EMPTY(&asoc->send_queue) ||
!TAILQ_EMPTY(&asoc->sent_queue) ||
!stcb->asoc.ss_functions.sctp_ss_is_empty(stcb, asoc)) {
- sctp_report_all_outbound(stcb, 0, 0, SCTP_SO_NOT_LOCKED);
+ panic("Queues are not empty when handling SHUTDOWN-ACK");
}
+#endif
/* stop the timer */
sctp_timer_stop(SCTP_TIMER_TYPE_SHUTDOWN, stcb->sctp_ep, stcb, net, SCTP_FROM_SCTP_INPUT + SCTP_LOC_9);
/* send SHUTDOWN-COMPLETE */
@@ -1875,9 +1876,14 @@ sctp_process_cookie_existing(struct mbuf *m, int iphlen, int offset,
cookie->tie_tag_peer_vtag != 0) {
struct sctpasochead *head;
+#if defined(__APPLE__) || defined(SCTP_SO_LOCK_TESTING)
+ struct socket *so;
+
+#endif
+
if (asoc->peer_supports_nat) {
/*
- * This is a gross gross hack. just call the
+ * This is a gross gross hack. Just call the
* cookie_new code since we are allowing a duplicate
* association. I hope this works...
*/
@@ -1939,6 +1945,10 @@ sctp_process_cookie_existing(struct mbuf *m, int iphlen, int offset,
asoc->mapping_array_size);
}
SCTP_TCB_UNLOCK(stcb);
+#if defined(__APPLE__) || defined(SCTP_SO_LOCK_TESTING)
+ so = SCTP_INP_SO(stcb->sctp_ep);
+ SCTP_SOCKET_LOCK(so, 1);
+#endif
SCTP_INP_INFO_WLOCK();
SCTP_INP_WLOCK(stcb->sctp_ep);
SCTP_TCB_LOCK(stcb);
@@ -1946,7 +1956,7 @@ sctp_process_cookie_existing(struct mbuf *m, int iphlen, int offset,
/* send up all the data */
SCTP_TCB_SEND_LOCK(stcb);
- sctp_report_all_outbound(stcb, 0, 1, SCTP_SO_NOT_LOCKED);
+ sctp_report_all_outbound(stcb, 0, 1, SCTP_SO_LOCKED);
for (i = 0; i < stcb->asoc.streamoutcnt; i++) {
stcb->asoc.strmout[i].chunks_on_queues = 0;
stcb->asoc.strmout[i].stream_no = i;
@@ -1968,11 +1978,15 @@ sctp_process_cookie_existing(struct mbuf *m, int iphlen, int offset,
*/
LIST_INSERT_HEAD(head, stcb, sctp_asocs);
- /* process the INIT info (peer's info) */
SCTP_TCB_SEND_UNLOCK(stcb);
SCTP_INP_WUNLOCK(stcb->sctp_ep);
SCTP_INP_INFO_WUNLOCK();
-
+#if defined(__APPLE__) || defined(SCTP_SO_LOCK_TESTING)
+ SCTP_SOCKET_UNLOCK(so, 1);
+#endif
+ asoc->total_flight = 0;
+ asoc->total_flight_count = 0;
+ /* process the INIT info (peer's info) */
retval = sctp_process_init(init_cp, stcb);
if (retval < 0) {
if (how_indx < sizeof(asoc->cookie_how))
@@ -3196,13 +3210,14 @@ sctp_handle_shutdown_complete(struct sctp_shutdown_complete_chunk *cp SCTP_UNUSE
/* notify upper layer protocol */
if (stcb->sctp_socket) {
sctp_ulp_notify(SCTP_NOTIFY_ASSOC_DOWN, stcb, 0, NULL, SCTP_SO_NOT_LOCKED);
- /* are the queues empty? they should be */
- if (!TAILQ_EMPTY(&asoc->send_queue) ||
- !TAILQ_EMPTY(&asoc->sent_queue) ||
- !stcb->asoc.ss_functions.sctp_ss_is_empty(stcb, asoc)) {
- sctp_report_all_outbound(stcb, 0, 0, SCTP_SO_NOT_LOCKED);
- }
}
+#ifdef INVARIANTS
+ if (!TAILQ_EMPTY(&asoc->send_queue) ||
+ !TAILQ_EMPTY(&asoc->sent_queue) ||
+ !stcb->asoc.ss_functions.sctp_ss_is_empty(stcb, asoc)) {
+ panic("Queues are not empty when handling SHUTDOWN-COMPLETE");
+ }
+#endif
/* stop the timer */
sctp_timer_stop(SCTP_TIMER_TYPE_SHUTDOWNACK, stcb->sctp_ep, stcb, net, SCTP_FROM_SCTP_INPUT + SCTP_LOC_22);
SCTP_STAT_INCR_COUNTER32(sctps_shutdown);
@@ -3491,18 +3506,13 @@ sctp_reset_in_stream(struct sctp_tcb *stcb, uint32_t number_entries, uint16_t *
}
static void
-sctp_reset_out_streams(struct sctp_tcb *stcb, int number_entries, uint16_t * list)
+sctp_reset_out_streams(struct sctp_tcb *stcb, uint32_t number_entries, uint16_t * list)
{
- int i;
+ uint32_t i;
+ uint16_t temp;
- if (number_entries == 0) {
- for (i = 0; i < stcb->asoc.streamoutcnt; i++) {
- stcb->asoc.strmout[i].next_sequence_send = 0;
- }
- } else if (number_entries) {
+ if (number_entries > 0) {
for (i = 0; i < number_entries; i++) {
- uint16_t temp;
-
temp = ntohs(list[i]);
if (temp >= stcb->asoc.streamoutcnt) {
/* no such stream */
@@ -3510,6 +3520,10 @@ sctp_reset_out_streams(struct sctp_tcb *stcb, int number_entries, uint16_t * lis
}
stcb->asoc.strmout[temp].next_sequence_send = 0;
}
+ } else {
+ for (i = 0; i < stcb->asoc.streamoutcnt; i++) {
+ stcb->asoc.strmout[i].next_sequence_send = 0;
+ }
}
sctp_ulp_notify(SCTP_NOTIFY_STR_RESET_SEND, stcb, number_entries, (void *)list, SCTP_SO_NOT_LOCKED);
}
@@ -3596,7 +3610,7 @@ sctp_handle_stream_reset_response(struct sctp_tcb *stcb,
struct sctp_association *asoc = &stcb->asoc;
struct sctp_tmit_chunk *chk;
struct sctp_stream_reset_out_request *srparam;
- int number_entries;
+ uint32_t number_entries;
if (asoc->stream_reset_outstanding == 0) {
/* duplicate */
@@ -4554,8 +4568,10 @@ __attribute__((noinline))
if ((ch->chunk_type == SCTP_ABORT_ASSOCIATION) ||
(ch->chunk_type == SCTP_SHUTDOWN_COMPLETE) ||
(ch->chunk_type == SCTP_PACKET_DROPPED)) {
- if ((vtag_in == asoc->my_vtag) ||
- ((ch->chunk_flags & SCTP_HAD_NO_TCB) &&
+ /* Take the T-bit always into account. */
+ if ((((ch->chunk_flags & SCTP_HAD_NO_TCB) == 0) &&
+ (vtag_in == asoc->my_vtag)) ||
+ (((ch->chunk_flags & SCTP_HAD_NO_TCB) == SCTP_HAD_NO_TCB) &&
(vtag_in == asoc->peer_vtag))) {
/* this is valid */
} else {
diff --git a/sys/netinet/sctp_pcb.c b/sys/netinet/sctp_pcb.c
index e98ca0c..00f685f 100644
--- a/sys/netinet/sctp_pcb.c
+++ b/sys/netinet/sctp_pcb.c
@@ -4451,23 +4451,21 @@ sctp_delete_from_timewait(uint32_t tag, uint16_t lport, uint16_t rport)
int i;
chain = &SCTP_BASE_INFO(vtag_timewait)[(tag % SCTP_STACK_VTAG_HASH_SIZE)];
- if (!LIST_EMPTY(chain)) {
- LIST_FOREACH(twait_block, chain, sctp_nxt_tagblock) {
- for (i = 0; i < SCTP_NUMBER_IN_VTAG_BLOCK; i++) {
- if ((twait_block->vtag_block[i].v_tag == tag) &&
- (twait_block->vtag_block[i].lport == lport) &&
- (twait_block->vtag_block[i].rport == rport)) {
- twait_block->vtag_block[i].tv_sec_at_expire = 0;
- twait_block->vtag_block[i].v_tag = 0;
- twait_block->vtag_block[i].lport = 0;
- twait_block->vtag_block[i].rport = 0;
- found = 1;
- break;
- }
- }
- if (found)
+ LIST_FOREACH(twait_block, chain, sctp_nxt_tagblock) {
+ for (i = 0; i < SCTP_NUMBER_IN_VTAG_BLOCK; i++) {
+ if ((twait_block->vtag_block[i].v_tag == tag) &&
+ (twait_block->vtag_block[i].lport == lport) &&
+ (twait_block->vtag_block[i].rport == rport)) {
+ twait_block->vtag_block[i].tv_sec_at_expire = 0;
+ twait_block->vtag_block[i].v_tag = 0;
+ twait_block->vtag_block[i].lport = 0;
+ twait_block->vtag_block[i].rport = 0;
+ found = 1;
break;
+ }
}
+ if (found)
+ break;
}
}
@@ -4481,19 +4479,17 @@ sctp_is_in_timewait(uint32_t tag, uint16_t lport, uint16_t rport)
SCTP_INP_INFO_WLOCK();
chain = &SCTP_BASE_INFO(vtag_timewait)[(tag % SCTP_STACK_VTAG_HASH_SIZE)];
- if (!LIST_EMPTY(chain)) {
- LIST_FOREACH(twait_block, chain, sctp_nxt_tagblock) {
- for (i = 0; i < SCTP_NUMBER_IN_VTAG_BLOCK; i++) {
- if ((twait_block->vtag_block[i].v_tag == tag) &&
- (twait_block->vtag_block[i].lport == lport) &&
- (twait_block->vtag_block[i].rport == rport)) {
- found = 1;
- break;
- }
- }
- if (found)
+ LIST_FOREACH(twait_block, chain, sctp_nxt_tagblock) {
+ for (i = 0; i < SCTP_NUMBER_IN_VTAG_BLOCK; i++) {
+ if ((twait_block->vtag_block[i].v_tag == tag) &&
+ (twait_block->vtag_block[i].lport == lport) &&
+ (twait_block->vtag_block[i].rport == rport)) {
+ found = 1;
break;
+ }
}
+ if (found)
+ break;
}
SCTP_INP_INFO_WUNLOCK();
return (found);
@@ -4515,42 +4511,40 @@ sctp_add_vtag_to_timewait(uint32_t tag, uint32_t time, uint16_t lport, uint16_t
(void)SCTP_GETTIME_TIMEVAL(&now);
chain = &SCTP_BASE_INFO(vtag_timewait)[(tag % SCTP_STACK_VTAG_HASH_SIZE)];
set = 0;
- if (!LIST_EMPTY(chain)) {
+ LIST_FOREACH(twait_block, chain, sctp_nxt_tagblock) {
/* Block(s) present, lets find space, and expire on the fly */
- LIST_FOREACH(twait_block, chain, sctp_nxt_tagblock) {
- for (i = 0; i < SCTP_NUMBER_IN_VTAG_BLOCK; i++) {
- if ((twait_block->vtag_block[i].v_tag == 0) &&
- !set) {
- twait_block->vtag_block[i].tv_sec_at_expire =
- now.tv_sec + time;
+ for (i = 0; i < SCTP_NUMBER_IN_VTAG_BLOCK; i++) {
+ if ((twait_block->vtag_block[i].v_tag == 0) &&
+ !set) {
+ twait_block->vtag_block[i].tv_sec_at_expire =
+ now.tv_sec + time;
+ twait_block->vtag_block[i].v_tag = tag;
+ twait_block->vtag_block[i].lport = lport;
+ twait_block->vtag_block[i].rport = rport;
+ set = 1;
+ } else if ((twait_block->vtag_block[i].v_tag) &&
+ ((long)twait_block->vtag_block[i].tv_sec_at_expire < now.tv_sec)) {
+ /* Audit expires this guy */
+ twait_block->vtag_block[i].tv_sec_at_expire = 0;
+ twait_block->vtag_block[i].v_tag = 0;
+ twait_block->vtag_block[i].lport = 0;
+ twait_block->vtag_block[i].rport = 0;
+ if (set == 0) {
+ /* Reuse it for my new tag */
+ twait_block->vtag_block[i].tv_sec_at_expire = now.tv_sec + time;
twait_block->vtag_block[i].v_tag = tag;
twait_block->vtag_block[i].lport = lport;
twait_block->vtag_block[i].rport = rport;
set = 1;
- } else if ((twait_block->vtag_block[i].v_tag) &&
- ((long)twait_block->vtag_block[i].tv_sec_at_expire < now.tv_sec)) {
- /* Audit expires this guy */
- twait_block->vtag_block[i].tv_sec_at_expire = 0;
- twait_block->vtag_block[i].v_tag = 0;
- twait_block->vtag_block[i].lport = 0;
- twait_block->vtag_block[i].rport = 0;
- if (set == 0) {
- /* Reuse it for my new tag */
- twait_block->vtag_block[i].tv_sec_at_expire = now.tv_sec + time;
- twait_block->vtag_block[i].v_tag = tag;
- twait_block->vtag_block[i].lport = lport;
- twait_block->vtag_block[i].rport = rport;
- set = 1;
- }
}
}
- if (set) {
- /*
- * We only do up to the block where we can
- * place our tag for audits
- */
- break;
- }
+ }
+ if (set) {
+ /*
+ * We only do up to the block where we can place our
+ * tag for audits
+ */
+ break;
}
}
/* Need to add a new block to chain */
@@ -6700,30 +6694,28 @@ skip_vtag_check:
chain = &SCTP_BASE_INFO(vtag_timewait)[(tag % SCTP_STACK_VTAG_HASH_SIZE)];
/* Now what about timed wait ? */
- if (!LIST_EMPTY(chain)) {
+ LIST_FOREACH(twait_block, chain, sctp_nxt_tagblock) {
/*
* Block(s) are present, lets see if we have this tag in the
* list
*/
- LIST_FOREACH(twait_block, chain, sctp_nxt_tagblock) {
- for (i = 0; i < SCTP_NUMBER_IN_VTAG_BLOCK; i++) {
- if (twait_block->vtag_block[i].v_tag == 0) {
- /* not used */
- continue;
- } else if ((long)twait_block->vtag_block[i].tv_sec_at_expire <
- now->tv_sec) {
- /* Audit expires this guy */
- twait_block->vtag_block[i].tv_sec_at_expire = 0;
- twait_block->vtag_block[i].v_tag = 0;
- twait_block->vtag_block[i].lport = 0;
- twait_block->vtag_block[i].rport = 0;
- } else if ((twait_block->vtag_block[i].v_tag == tag) &&
- (twait_block->vtag_block[i].lport == lport) &&
- (twait_block->vtag_block[i].rport == rport)) {
- /* Bad tag, sorry :< */
- SCTP_INP_INFO_RUNLOCK();
- return (0);
- }
+ for (i = 0; i < SCTP_NUMBER_IN_VTAG_BLOCK; i++) {
+ if (twait_block->vtag_block[i].v_tag == 0) {
+ /* not used */
+ continue;
+ } else if ((long)twait_block->vtag_block[i].tv_sec_at_expire <
+ now->tv_sec) {
+ /* Audit expires this guy */
+ twait_block->vtag_block[i].tv_sec_at_expire = 0;
+ twait_block->vtag_block[i].v_tag = 0;
+ twait_block->vtag_block[i].lport = 0;
+ twait_block->vtag_block[i].rport = 0;
+ } else if ((twait_block->vtag_block[i].v_tag == tag) &&
+ (twait_block->vtag_block[i].lport == lport) &&
+ (twait_block->vtag_block[i].rport == rport)) {
+ /* Bad tag, sorry :< */
+ SCTP_INP_INFO_RUNLOCK();
+ return (0);
}
}
}
diff --git a/sys/netinet6/in6.c b/sys/netinet6/in6.c
index 80359c1..9e005c5 100644
--- a/sys/netinet6/in6.c
+++ b/sys/netinet6/in6.c
@@ -1987,6 +1987,32 @@ in6ifa_ifpwithaddr(struct ifnet *ifp, struct in6_addr *addr)
}
/*
+ * Find a link-local scoped address on ifp and return it if any.
+ */
+struct in6_ifaddr *
+in6ifa_llaonifp(struct ifnet *ifp)
+{
+ struct sockaddr_in6 *sin6;
+ struct ifaddr *ifa;
+
+ if (ND_IFINFO(ifp)->flags & ND6_IFF_IFDISABLED)
+ return (NULL);
+ if_addr_rlock(ifp);
+ TAILQ_FOREACH(ifa, &ifp->if_addrhead, ifa_link) {
+ if (ifa->ifa_addr->sa_family != AF_INET6)
+ continue;
+ sin6 = (struct sockaddr_in6 *)ifa->ifa_addr;
+ if (IN6_IS_SCOPE_LINKLOCAL(&sin6->sin6_addr) ||
+ IN6_IS_ADDR_MC_INTFACELOCAL(&sin6->sin6_addr) ||
+ IN6_IS_ADDR_MC_NODELOCAL(&sin6->sin6_addr))
+ break;
+ }
+ if_addr_runlock(ifp);
+
+ return ((struct in6_ifaddr *)ifa);
+}
+
+/*
* Convert IP6 address to printable (loggable) representation. Caller
* has to make sure that ip6buf is at least INET6_ADDRSTRLEN long.
*/
diff --git a/sys/netinet6/in6_ifattach.c b/sys/netinet6/in6_ifattach.c
index 85890ab..674356a 100644
--- a/sys/netinet6/in6_ifattach.c
+++ b/sys/netinet6/in6_ifattach.c
@@ -266,6 +266,7 @@ found:
/* get EUI64 */
switch (ifp->if_type) {
+ case IFT_BRIDGE:
case IFT_ETHER:
case IFT_L2VLAN:
case IFT_FDDI:
@@ -727,6 +728,8 @@ in6_ifattach(struct ifnet *ifp, struct ifnet *altifp)
switch (ifp->if_type) {
case IFT_PFLOG:
case IFT_PFSYNC:
+ ND_IFINFO(ifp)->flags &= ~ND6_IFF_AUTO_LINKLOCAL;
+ ND_IFINFO(ifp)->flags |= ND6_IFF_IFDISABLED;
return;
}
@@ -734,7 +737,6 @@ in6_ifattach(struct ifnet *ifp, struct ifnet *altifp)
* quirks based on interface type
*/
switch (ifp->if_type) {
-#ifdef IFT_STF
case IFT_STF:
/*
* 6to4 interface is a very special kind of beast.
@@ -742,8 +744,8 @@ in6_ifattach(struct ifnet *ifp, struct ifnet *altifp)
* linklocals for 6to4 interface, but there's no use and
* it is rather harmful to have one.
*/
- goto statinit;
-#endif
+ ND_IFINFO(ifp)->flags &= ~ND6_IFF_AUTO_LINKLOCAL;
+ break;
default:
break;
}
@@ -777,8 +779,7 @@ in6_ifattach(struct ifnet *ifp, struct ifnet *altifp)
/*
* assign a link-local address, if there's none.
*/
- if (ifp->if_type != IFT_BRIDGE &&
- !(ND_IFINFO(ifp)->flags & ND6_IFF_IFDISABLED) &&
+ if (!(ND_IFINFO(ifp)->flags & ND6_IFF_IFDISABLED) &&
ND_IFINFO(ifp)->flags & ND6_IFF_AUTO_LINKLOCAL) {
int error;
@@ -795,10 +796,6 @@ in6_ifattach(struct ifnet *ifp, struct ifnet *altifp)
ifa_free(&ia->ia_ifa);
}
-#ifdef IFT_STF /* XXX */
-statinit:
-#endif
-
/* update dynamically. */
if (V_in6_maxmtu < ifp->if_mtu)
V_in6_maxmtu = ifp->if_mtu;
diff --git a/sys/netinet6/in6_pcb.c b/sys/netinet6/in6_pcb.c
index d5db387..a0a6874 100644
--- a/sys/netinet6/in6_pcb.c
+++ b/sys/netinet6/in6_pcb.c
@@ -243,8 +243,7 @@ in6_pcbbind(register struct inpcb *inp, struct sockaddr *nam,
if (tw == NULL ||
(reuseport & tw->tw_so_options) == 0)
return (EADDRINUSE);
- } else if (t && (reuseport == 0 ||
- (t->inp_flags2 & INP_REUSEPORT) == 0)) {
+ } else if (t && (reuseport & inp_so_options(t)) == 0) {
return (EADDRINUSE);
}
#ifdef INET
@@ -265,8 +264,8 @@ in6_pcbbind(register struct inpcb *inp, struct sockaddr *nam,
INP_IPV6PROTO) ==
(t->inp_vflag & INP_IPV6PROTO))))
return (EADDRINUSE);
- } else if (t && (reuseport == 0 ||
- (t->inp_flags2 & INP_REUSEPORT) == 0) &&
+ } else if (t &&
+ (reuseport & inp_so_options(t)) == 0 &&
(ntohl(t->inp_laddr.s_addr) != INADDR_ANY ||
(t->inp_vflag & INP_IPV6PROTO) != 0))
return (EADDRINUSE);
diff --git a/sys/netinet6/in6_var.h b/sys/netinet6/in6_var.h
index 3b4e89e..b3f7218 100644
--- a/sys/netinet6/in6_var.h
+++ b/sys/netinet6/in6_var.h
@@ -800,6 +800,7 @@ void in6_setmaxmtu(void);
int in6_if2idlen(struct ifnet *);
struct in6_ifaddr *in6ifa_ifpforlinklocal(struct ifnet *, int);
struct in6_ifaddr *in6ifa_ifpwithaddr(struct ifnet *, struct in6_addr *);
+struct in6_ifaddr *in6ifa_llaonifp(struct ifnet *);
char *ip6_sprintf(char *, const struct in6_addr *);
int in6_addr2zoneid(struct ifnet *, struct in6_addr *, u_int32_t *);
int in6_matchlen(struct in6_addr *, struct in6_addr *);
diff --git a/sys/netinet6/ip6_output.c b/sys/netinet6/ip6_output.c
index 2736566..5df3572 100644
--- a/sys/netinet6/ip6_output.c
+++ b/sys/netinet6/ip6_output.c
@@ -1477,13 +1477,10 @@ ip6_ctloutput(struct socket *so, struct sockopt *sopt)
switch (sopt->sopt_name) {
case SO_REUSEADDR:
INP_WLOCK(in6p);
- if (IN_MULTICAST(ntohl(in6p->inp_laddr.s_addr))) {
- if ((so->so_options &
- (SO_REUSEADDR | SO_REUSEPORT)) != 0)
- in6p->inp_flags2 |= INP_REUSEPORT;
- else
- in6p->inp_flags2 &= ~INP_REUSEPORT;
- }
+ if ((so->so_options & SO_REUSEADDR) != 0)
+ in6p->inp_flags2 |= INP_REUSEADDR;
+ else
+ in6p->inp_flags2 &= ~INP_REUSEADDR;
INP_WUNLOCK(in6p);
error = 0;
break;
diff --git a/sys/netinet6/nd6.c b/sys/netinet6/nd6.c
index 0e30825..0a67a8a 100644
--- a/sys/netinet6/nd6.c
+++ b/sys/netinet6/nd6.c
@@ -176,13 +176,25 @@ nd6_ifattach(struct ifnet *ifp)
nd->flags = ND6_IFF_PERFORMNUD;
- /* A loopback interface always has ND6_IFF_AUTO_LINKLOCAL. */
- if (V_ip6_auto_linklocal || (ifp->if_flags & IFF_LOOPBACK))
+ /* A loopback interface always has ND6_IFF_AUTO_LINKLOCAL.
+ * XXXHRS: Clear ND6_IFF_AUTO_LINKLOCAL on an IFT_BRIDGE interface by
+ * default regardless of the V_ip6_auto_linklocal configuration to
+ * give a reasonable default behavior.
+ */
+ if ((V_ip6_auto_linklocal && ifp->if_type != IFT_BRIDGE) ||
+ (ifp->if_flags & IFF_LOOPBACK))
nd->flags |= ND6_IFF_AUTO_LINKLOCAL;
-
- /* A loopback interface does not need to accept RTADV. */
- if (V_ip6_accept_rtadv && !(ifp->if_flags & IFF_LOOPBACK))
- nd->flags |= ND6_IFF_ACCEPT_RTADV;
+ /*
+ * A loopback interface does not need to accept RTADV.
+ * XXXHRS: Clear ND6_IFF_ACCEPT_RTADV on an IFT_BRIDGE interface by
+ * default regardless of the V_ip6_accept_rtadv configuration to
+ * prevent the interface from accepting RA messages arrived
+ * on one of the member interfaces with ND6_IFF_ACCEPT_RTADV.
+ */
+ if (V_ip6_accept_rtadv &&
+ !(ifp->if_flags & IFF_LOOPBACK) &&
+ (ifp->if_type != IFT_BRIDGE))
+ nd->flags |= ND6_IFF_ACCEPT_RTADV;
if (V_ip6_no_radr && !(ifp->if_flags & IFF_LOOPBACK))
nd->flags |= ND6_IFF_NO_RADR;
diff --git a/sys/netsmb/smb_conn.c b/sys/netsmb/smb_conn.c
index 190d127..d58bc72 100644
--- a/sys/netsmb/smb_conn.c
+++ b/sys/netsmb/smb_conn.c
@@ -530,7 +530,6 @@ smb_vc_free(struct smb_connobj *cp)
/*
* Called when use count of VC dropped to zero.
- * VC should be locked on enter with LK_DRAIN.
*/
static void
smb_vc_gone(struct smb_connobj *cp, struct smb_cred *scred)
diff --git a/sys/nfsclient/nfs_subs.c b/sys/nfsclient/nfs_subs.c
index c41bc61..5dfab1c 100644
--- a/sys/nfsclient/nfs_subs.c
+++ b/sys/nfsclient/nfs_subs.c
@@ -581,6 +581,7 @@ nfs_loadattrcache(struct vnode **vpp, struct mbuf **mdp, caddr_t *dposp,
vap->va_size = np->n_size;
np->n_attrstamp = 0;
KDTRACE_NFS_ATTRCACHE_FLUSH_DONE(vp);
+ vnode_pager_setsize(vp, np->n_size);
} else if (np->n_flag & NMODIFIED) {
/*
* We've modified the file: Use the larger
@@ -592,12 +593,22 @@ nfs_loadattrcache(struct vnode **vpp, struct mbuf **mdp, caddr_t *dposp,
np->n_size = vap->va_size;
np->n_flag |= NSIZECHANGED;
}
+ vnode_pager_setsize(vp, np->n_size);
+ } else if (vap->va_size < np->n_size) {
+ /*
+ * When shrinking the size, the call to
+ * vnode_pager_setsize() cannot be done
+ * with the mutex held, so delay it until
+ * after the mtx_unlock call.
+ */
+ nsize = np->n_size = vap->va_size;
+ np->n_flag |= NSIZECHANGED;
+ setnsize = 1;
} else {
np->n_size = vap->va_size;
np->n_flag |= NSIZECHANGED;
+ vnode_pager_setsize(vp, np->n_size);
}
- setnsize = 1;
- nsize = vap->va_size;
} else {
np->n_size = vap->va_size;
}
diff --git a/sys/nfsclient/nfs_vnops.c b/sys/nfsclient/nfs_vnops.c
index d834b89..69ff21d 100644
--- a/sys/nfsclient/nfs_vnops.c
+++ b/sys/nfsclient/nfs_vnops.c
@@ -1379,8 +1379,8 @@ nfs_writerpc(struct vnode *vp, struct uio *uiop, struct ucred *cred,
while (tsiz > 0) {
nfsstats.rpccnt[NFSPROC_WRITE]++;
len = (tsiz > wsize) ? wsize : tsiz;
- mreq = m_get2(NFSX_FH(v3) + 5 * NFSX_UNSIGNED + nfsm_rndup(len),
- M_WAITOK, MT_DATA, 0);
+ mreq = m_get2(NFSX_FH(v3) + 5 * NFSX_UNSIGNED, M_WAITOK,
+ MT_DATA, 0);
mb = mreq;
bpos = mtod(mb, caddr_t);
nfsm_fhtom(vp, v3);
diff --git a/sys/powerpc/include/counter.h b/sys/powerpc/include/counter.h
index 81b6bbc..e876f62 100644
--- a/sys/powerpc/include/counter.h
+++ b/sys/powerpc/include/counter.h
@@ -39,6 +39,44 @@
#define counter_enter() do {} while (0)
#define counter_exit() do {} while (0)
+#ifdef IN_SUBR_COUNTER_C
+static inline uint64_t
+counter_u64_read_one(uint64_t *p, int cpu)
+{
+
+ return (*(uint64_t *)((char *)p + sizeof(struct pcpu) * cpu));
+}
+
+static inline uint64_t
+counter_u64_fetch_inline(uint64_t *p)
+{
+ uint64_t r;
+ int i;
+
+ r = 0;
+ for (i = 0; i < mp_ncpus; i++)
+ r += counter_u64_read_one((uint64_t *)p, i);
+
+ return (r);
+}
+
+static void
+counter_u64_zero_one_cpu(void *arg)
+{
+
+ *((uint64_t *)((char *)arg + sizeof(struct pcpu) *
+ PCPU_GET(cpuid))) = 0;
+}
+
+static inline void
+counter_u64_zero_inline(counter_u64_t c)
+{
+
+ smp_rendezvous(smp_no_rendevous_barrier, counter_u64_zero_one_cpu,
+ smp_no_rendevous_barrier, c);
+}
+#endif
+
#define counter_u64_add_protected(c, i) counter_u64_add(c, i)
extern struct pcpu __pcpu[MAXCPU];
@@ -65,6 +103,46 @@ counter_u64_add(counter_u64_t c, int64_t inc)
#define counter_enter() critical_enter()
#define counter_exit() critical_exit()
+#ifdef IN_SUBR_COUNTER_C
+/* XXXKIB non-atomic 64bit read */
+static inline uint64_t
+counter_u64_read_one(uint64_t *p, int cpu)
+{
+
+ return (*(uint64_t *)((char *)p + sizeof(struct pcpu) * cpu));
+}
+
+static inline uint64_t
+counter_u64_fetch_inline(uint64_t *p)
+{
+ uint64_t r;
+ int i;
+
+ r = 0;
+ for (i = 0; i < mp_ncpus; i++)
+ r += counter_u64_read_one((uint64_t *)p, i);
+
+ return (r);
+}
+
+/* XXXKIB non-atomic 64bit store, might interrupt increment */
+static void
+counter_u64_zero_one_cpu(void *arg)
+{
+
+ *((uint64_t *)((char *)arg + sizeof(struct pcpu) *
+ PCPU_GET(cpuid))) = 0;
+}
+
+static inline void
+counter_u64_zero_inline(counter_u64_t c)
+{
+
+ smp_rendezvous(smp_no_rendevous_barrier, counter_u64_zero_one_cpu,
+ smp_no_rendevous_barrier, c);
+}
+#endif
+
#define counter_u64_add_protected(c, inc) do { \
CRITICAL_ASSERT(curthread); \
*(uint64_t *)zpcpu_get(c) += (inc); \
diff --git a/sys/powerpc/wii/wii_ipcreg.h b/sys/powerpc/wii/wii_ipcreg.h
index 197a4e0..86b3e1f 100644
--- a/sys/powerpc/wii/wii_ipcreg.h
+++ b/sys/powerpc/wii/wii_ipcreg.h
@@ -34,4 +34,69 @@
#define WIIIPC_IOH_ADDR 0x133e0000
#define WIIIPC_IOH_LEN 0xc20000
+#define WIIIPC_TXBUF 0x00
+#define WIIIPC_CSR 0x04
+#define WIIIPC_CSR_TXSTART 0x01
+#define WIIIPC_CSR_TBEI 0x02
+#define WIIIPC_CSR_RBFI 0x04
+#define WIIIPC_CSR_RXREADY 0x08
+#define WIIIPC_CSR_RBFIMASK 0x10
+#define WIIIPC_CSR_TBEIMASK 0x20
+#define WIIIPC_RXBUF 0x08
+#define WIIIPC_ISR 0x30
+#define WIIIPC_ISR_MAGIC 0x40000000
+
+enum wiiipc_cmd {
+ WIIIPC_CMD_OPEN = 1,
+ WIIIPC_CMD_CLOSE = 2,
+ WIIIPC_CMD_READ = 3,
+ WIIIPC_CMD_WRITE = 4,
+ WIIIPC_CMD_SEEK = 5,
+ WIIIPC_CMD_IOCTL = 6,
+ WIIIPC_CMD_IOCTLV = 7,
+ WIIIPC_CMD_ASYNCRESP = 8
+};
+
+struct wiiipc_ipc_msg {
+ uint32_t ipc_cmd;
+ int32_t ipc_result;
+ int32_t ipc_fd; /* WIIIPC_CMD_ASYNCRESP - the original cmd */
+ union {
+ struct {
+ intptr_t pathname;
+ uint32_t mode;
+ } _ipc_open;
+ struct {
+ intptr_t data;
+ uint32_t len;
+ } _ipc_read, _ipc_write;
+ struct {
+ int32_t offset;
+ int32_t whence;
+ } _ipc_seek;
+ struct {
+ uint32_t request;
+ intptr_t ibuf;
+ uint32_t ilen;
+ intptr_t obuf;
+ uint32_t olen;
+ } _ipc_ioctl;
+ struct {
+ uint32_t request;
+ uint32_t argin;
+ uint32_t argout;
+ intptr_t iovec;
+ } _ipc_ioctlv;
+ uint32_t _ipc_argv[5];
+ } args;
+} __attribute__((packed));
+
+CTASSERT(sizeof(struct wiiipc_ipc_msg) == 32);
+
+#define ipc_open args._ipc_open
+#define ipc_read args._ipc_read
+#define ipc_write args._ipc_write
+#define ipc_ioctl args._ipc_ioctl
+#define ipc_ioctlv args._ipc_ioctlv
+
#endif /* _POWERPC_WII_WII_IPCREG_H */
diff --git a/sys/sparc64/include/counter.h b/sys/sparc64/include/counter.h
index 68f89e2..a3fe871 100644
--- a/sys/sparc64/include/counter.h
+++ b/sys/sparc64/include/counter.h
@@ -37,6 +37,45 @@
#define counter_enter() critical_enter()
#define counter_exit() critical_exit()
+#ifdef IN_SUBR_COUNTER_C
+static inline uint64_t
+counter_u64_read_one(uint64_t *p, int cpu)
+{
+
+ return (*(uint64_t *)((char *)p + sizeof(struct pcpu) * cpu));
+}
+
+static inline uint64_t
+counter_u64_fetch_inline(uint64_t *p)
+{
+ uint64_t r;
+ int i;
+
+ r = 0;
+ for (i = 0; i < mp_ncpus; i++)
+ r += counter_u64_read_one((uint64_t *)p, i);
+
+ return (r);
+}
+
+/* XXXKIB might interrupt increment */
+static void
+counter_u64_zero_one_cpu(void *arg)
+{
+
+ *((uint64_t *)((char *)arg + sizeof(struct pcpu) *
+ PCPU_GET(cpuid))) = 0;
+}
+
+static inline void
+counter_u64_zero_inline(counter_u64_t c)
+{
+
+ smp_rendezvous(smp_no_rendevous_barrier, counter_u64_zero_one_cpu,
+ smp_no_rendevous_barrier, c);
+}
+#endif
+
#define counter_u64_add_protected(c, inc) do { \
CRITICAL_ASSERT(curthread); \
*(uint64_t *)zpcpu_get(c) += (inc); \
diff --git a/sys/sys/bus.h b/sys/sys/bus.h
index 152b067..06e0f6c 100644
--- a/sys/sys/bus.h
+++ b/sys/sys/bus.h
@@ -273,6 +273,9 @@ struct resource *
int resource_list_release(struct resource_list *rl,
device_t bus, device_t child,
int type, int rid, struct resource *res);
+int resource_list_release_active(struct resource_list *rl,
+ device_t bus, device_t child,
+ int type);
struct resource *
resource_list_reserve(struct resource_list *rl,
device_t bus, device_t child,
diff --git a/sys/sys/malloc.h b/sys/sys/malloc.h
index b0e1805..8f51606 100644
--- a/sys/sys/malloc.h
+++ b/sys/sys/malloc.h
@@ -51,6 +51,8 @@
#define M_NOVM 0x0200 /* don't ask VM for pages */
#define M_USE_RESERVE 0x0400 /* can alloc out of reserve memory */
#define M_NODUMP 0x0800 /* don't dump pages in this allocation */
+#define M_FIRSTFIT 0x1000 /* Only for vmem, fast fit. */
+#define M_BESTFIT 0x2000 /* Only for vmem, low fragmentation. */
#define M_MAGIC 877983977 /* time when first defined :-) */
diff --git a/sys/sys/socketvar.h b/sys/sys/socketvar.h
index 77f31df..89dec18 100644
--- a/sys/sys/socketvar.h
+++ b/sys/sys/socketvar.h
@@ -371,6 +371,8 @@ void soupcall_clear(struct socket *so, int which);
void soupcall_set(struct socket *so, int which,
int (*func)(struct socket *, void *, int), void *arg);
void sowakeup(struct socket *so, struct sockbuf *sb);
+int selsocket(struct socket *so, int events, struct timeval *tv,
+ struct thread *td);
/*
* Accept filter functions (duh).
diff --git a/sys/sys/stdatomic.h b/sys/sys/stdatomic.h
index 8d7a20b..054ded6 100644
--- a/sys/sys/stdatomic.h
+++ b/sys/sys/stdatomic.h
@@ -122,33 +122,44 @@
* atomic operations.
*/
-enum memory_order {
+typedef enum {
memory_order_relaxed = __ATOMIC_RELAXED,
memory_order_consume = __ATOMIC_CONSUME,
memory_order_acquire = __ATOMIC_ACQUIRE,
memory_order_release = __ATOMIC_RELEASE,
memory_order_acq_rel = __ATOMIC_ACQ_REL,
memory_order_seq_cst = __ATOMIC_SEQ_CST
-};
+} memory_order;
/*
* 7.17.4 Fences.
*/
+static __inline void
+atomic_thread_fence(memory_order __order __unused)
+{
+
#ifdef __CLANG_ATOMICS
-#define atomic_thread_fence(order) __c11_atomic_thread_fence(order)
-#define atomic_signal_fence(order) __c11_atomic_signal_fence(order)
+ __c11_atomic_thread_fence(__order);
#elif defined(__GNUC_ATOMICS)
-#define atomic_thread_fence(order) __atomic_thread_fence(order)
-#define atomic_signal_fence(order) __atomic_signal_fence(order)
+ __atomic_thread_fence(__order);
#else
-#define atomic_thread_fence(order) ((void)(order), __sync_synchronize())
-#define atomic_signal_fence(order) __extension__ ({ \
- (void)(order); \
- __asm volatile ("" ::: "memory"); \
- (void)0; \
-})
+ __sync_synchronize();
+#endif
+}
+
+static __inline void
+atomic_signal_fence(memory_order __order __unused)
+{
+
+#ifdef __CLANG_ATOMICS
+ __c11_atomic_signal_fence(__order);
+#elif defined(__GNUC_ATOMICS)
+ __atomic_signal_fence(__order);
+#else
+ __asm volatile ("" ::: "memory");
#endif
+}
/*
* 7.17.5 Lock-free property.
@@ -270,6 +281,8 @@ typedef _Atomic(__uintmax_t) atomic_uintmax_t;
#define atomic_store_explicit(object, desired, order) \
__atomic_store_n(&(object)->__val, desired, order)
#else
+#define __atomic_apply_stride(object, operand) \
+ (((__typeof__((object)->__val))0) + (operand))
#define atomic_compare_exchange_strong_explicit(object, expected, \
desired, success, failure) __extension__ ({ \
__typeof__(expected) __ep = (expected); \
@@ -302,13 +315,15 @@ __extension__ ({ \
})
#endif
#define atomic_fetch_add_explicit(object, operand, order) \
- ((void)(order), __sync_fetch_and_add(&(object)->__val, operand))
+ ((void)(order), __sync_fetch_and_add(&(object)->__val, \
+ __atomic_apply_stride(object, operand)))
#define atomic_fetch_and_explicit(object, operand, order) \
((void)(order), __sync_fetch_and_and(&(object)->__val, operand))
#define atomic_fetch_or_explicit(object, operand, order) \
((void)(order), __sync_fetch_and_or(&(object)->__val, operand))
#define atomic_fetch_sub_explicit(object, operand, order) \
- ((void)(order), __sync_fetch_and_sub(&(object)->__val, operand))
+ ((void)(order), __sync_fetch_and_sub(&(object)->__val, \
+ __atomic_apply_stride(object, operand)))
#define atomic_fetch_xor_explicit(object, operand, order) \
((void)(order), __sync_fetch_and_xor(&(object)->__val, operand))
#define atomic_load_explicit(object, order) \
@@ -319,8 +334,12 @@ __extension__ ({ \
/*
* Convenience functions.
+ *
+ * Don't provide these in kernel space. In kernel space, we should be
+ * disciplined enough to always provide explicit barriers.
*/
+#ifndef _KERNEL
#define atomic_compare_exchange_strong(object, expected, desired) \
atomic_compare_exchange_strong_explicit(object, expected, \
desired, memory_order_seq_cst, memory_order_seq_cst)
@@ -343,23 +362,54 @@ __extension__ ({ \
atomic_load_explicit(object, memory_order_seq_cst)
#define atomic_store(object, desired) \
atomic_store_explicit(object, desired, memory_order_seq_cst)
+#endif /* !_KERNEL */
/*
* 7.17.8 Atomic flag type and operations.
+ *
+ * XXX: Assume atomic_bool can be used as an atomic_flag. Is there some
+ * kind of compiler built-in type we could use?
*/
-typedef atomic_bool atomic_flag;
+typedef struct {
+ atomic_bool __flag;
+} atomic_flag;
+
+#define ATOMIC_FLAG_INIT { ATOMIC_VAR_INIT(0) }
+
+static __inline _Bool
+atomic_flag_test_and_set_explicit(volatile atomic_flag *__object,
+ memory_order __order)
+{
+ _Bool __expected;
+
+ __expected = 0;
+ return (atomic_compare_exchange_strong_explicit(&__object->__flag,
+ &__expected, 1, __order, __order));
+}
+
+static __inline void
+atomic_flag_clear_explicit(volatile atomic_flag *__object, memory_order __order)
+{
+
+ atomic_store_explicit(&__object->__flag, 0, __order);
+}
+
+#ifndef _KERNEL
+static __inline _Bool
+atomic_flag_test_and_set(volatile atomic_flag *__object)
+{
-#define ATOMIC_FLAG_INIT ATOMIC_VAR_INIT(0)
+ return (atomic_flag_test_and_set_explicit(__object,
+ memory_order_seq_cst));
+}
-#define atomic_flag_clear_explicit(object, order) \
- atomic_store_explicit(object, 0, order)
-#define atomic_flag_test_and_set_explicit(object, order) \
- atomic_compare_exchange_strong_explicit(object, 0, 1, order, order)
+static __inline void
+atomic_flag_clear(volatile atomic_flag *__object)
+{
-#define atomic_flag_clear(object) \
- atomic_flag_clear_explicit(object, memory_order_seq_cst)
-#define atomic_flag_test_and_set(object) \
- atomic_flag_test_and_set_explicit(object, memory_order_seq_cst)
+ atomic_flag_clear_explicit(__object, memory_order_seq_cst);
+}
+#endif /* !_KERNEL */
#endif /* !_STDATOMIC_H_ */
diff --git a/sys/sys/vmem.h b/sys/sys/vmem.h
new file mode 100644
index 0000000..47f55fb
--- /dev/null
+++ b/sys/sys/vmem.h
@@ -0,0 +1,135 @@
+/*-
+ * Copyright (c)2006 YAMAMOTO Takashi,
+ * All rights reserved.
+ *
+ * Redistribution and use in source and binary forms, with or without
+ * modification, are permitted provided that the following conditions
+ * are met:
+ * 1. Redistributions of source code must retain the above copyright
+ * notice, this list of conditions and the following disclaimer.
+ * 2. Redistributions in binary form must reproduce the above copyright
+ * notice, this list of conditions and the following disclaimer in the
+ * documentation and/or other materials provided with the distribution.
+ *
+ * THIS SOFTWARE IS PROVIDED BY THE AUTHOR AND CONTRIBUTORS ``AS IS'' AND
+ * ANY EXPRESS OR IMPLIED WARRANTIES, INCLUDING, BUT NOT LIMITED TO, THE
+ * IMPLIED WARRANTIES OF MERCHANTABILITY AND FITNESS FOR A PARTICULAR PURPOSE
+ * ARE DISCLAIMED. IN NO EVENT SHALL THE AUTHOR OR CONTRIBUTORS BE LIABLE
+ * FOR ANY DIRECT, INDIRECT, INCIDENTAL, SPECIAL, EXEMPLARY, OR CONSEQUENTIAL
+ * DAMAGES (INCLUDING, BUT NOT LIMITED TO, PROCUREMENT OF SUBSTITUTE GOODS
+ * OR SERVICES; LOSS OF USE, DATA, OR PROFITS; OR BUSINESS INTERRUPTION)
+ * HOWEVER CAUSED AND ON ANY THEORY OF LIABILITY, WHETHER IN CONTRACT, STRICT
+ * LIABILITY, OR TORT (INCLUDING NEGLIGENCE OR OTHERWISE) ARISING IN ANY WAY
+ * OUT OF THE USE OF THIS SOFTWARE, EVEN IF ADVISED OF THE POSSIBILITY OF
+ * SUCH DAMAGE.
+ */
+/* From $NetBSD: vmem.h,v 1.20 2013/01/29 21:26:24 para Exp $ */
+
+/* $FreeBSD$ */
+
+#ifndef _SYS_VMEM_H_
+#define _SYS_VMEM_H_
+
+#include <sys/types.h>
+
+#ifdef _KERNEL
+
+typedef struct vmem vmem_t;
+
+typedef uintptr_t vmem_addr_t;
+typedef size_t vmem_size_t;
+
+#define VMEM_ADDR_MIN 0
+#define VMEM_ADDR_MAX (~(vmem_addr_t)0)
+
+typedef int (vmem_import_t)(void *, vmem_size_t, int, vmem_addr_t *);
+typedef void (vmem_release_t)(void *, vmem_addr_t, vmem_size_t);
+typedef void (vmem_reclaim_t)(vmem_t *, int);
+
+/*
+ * Create a vmem:
+ * name - Name of the region
+ * base - Initial span start (optional)
+ * size - Initial span size
+ * quantum - Natural unit of allocation (ie PAGE_SIZE, 1, etc)
+ * qcache_max - Maximum size to quantum cache. This creates a UMA
+ * cache for each multiple of quantum up to qcache_max.
+ * flags - M_* flags
+ */
+vmem_t *vmem_create(const char *name, vmem_addr_t base,
+ vmem_size_t size, vmem_size_t quantum, vmem_size_t qcache_max, int flags);
+vmem_t *vmem_init(vmem_t *vm, const char *name, vmem_addr_t base,
+ vmem_size_t size, vmem_size_t quantum, vmem_size_t qcache_max, int flags);
+void vmem_destroy(vmem_t *);
+
+/*
+ * Set callbacks for bringing in dynamic regions:
+ * importfn - Backing store import routine.
+ * releasefn - Backing store release routine.
+ * arg - Backing store argument
+ * import_quantum - Size to import from backing store
+ */
+
+void vmem_set_import(vmem_t *vm, vmem_import_t *importfn,
+ vmem_release_t *releasefn, void *arg, vmem_size_t import_quantum);
+
+/*
+ * Set a callback for reclaiming memory when space is exhausted:
+ */
+void vmem_set_reclaim(vmem_t *vm, vmem_reclaim_t *reclaimfn);
+
+/*
+ * Allocate and free linear regions from a vmem. Must specify
+ * BESTFIT or FIRSTFIT. Free is non-blocking. These routines
+ * respect the quantum caches.
+ */
+int vmem_alloc(vmem_t *vm, vmem_size_t size, int flags, vmem_addr_t *addrp);
+void vmem_free(vmem_t *vm, vmem_addr_t addr, vmem_size_t size);
+
+/*
+ * Constrained allocate and free routines. These bypass the quantum cache.
+ * size - Size in units of 1, not quantum.
+ * align - Required alignment of the start of region
+ * phase - Offset from alignment
+ * nocross - Illegal boundary
+ * minaddr - Minimum allowed address for last byte
+ * maxaddr - Maximum allowed address for first byte
+ * flags - M_* flags
+ * addrp - result
+ */
+int vmem_xalloc(vmem_t *vm, vmem_size_t size, vmem_size_t align,
+ vmem_size_t phase, vmem_size_t nocross, vmem_addr_t minaddr,
+ vmem_addr_t maxaddr, int flags, vmem_addr_t *addrp);
+void vmem_xfree(vmem_t *vm, vmem_addr_t addr, vmem_size_t size);
+
+/*
+ * Add a static region to a vmem after create. This won't be freed
+ * until the vmem is destroyed.
+ */
+int vmem_add(vmem_t *vm, vmem_addr_t addr, vmem_size_t size, int flags);
+
+/*
+ * Given roundup size to the vmem's native quantum size.
+ */
+vmem_size_t vmem_roundup_size(vmem_t *vm, vmem_size_t size);
+
+/*
+ * Report vmem utilization according to the requested type.
+ */
+vmem_size_t vmem_size(vmem_t *vm, int typemask);
+
+void vmem_whatis(vmem_addr_t addr, int (*fn)(const char *, ...)
+ __printflike(1, 2));
+void vmem_print(vmem_addr_t addr, const char *, int (*fn)(const char *, ...)
+ __printflike(1, 2));
+void vmem_printall(const char *, int (*fn)(const char *, ...)
+ __printflike(1, 2));
+void vmem_startup(void);
+
+/* vmem_size typemask */
+#define VMEM_ALLOC 0x01
+#define VMEM_FREE 0x02
+
+#endif /* _KERNEL */
+
+#endif /* !_SYS_VMEM_H_ */
diff --git a/sys/ufs/ffs/ffs_alloc.c b/sys/ufs/ffs/ffs_alloc.c
index d7db636..287ccbb 100644
--- a/sys/ufs/ffs/ffs_alloc.c
+++ b/sys/ufs/ffs/ffs_alloc.c
@@ -1710,7 +1710,7 @@ ffs_alloccgblk(ip, bp, bpref, size)
cgp = (struct cg *)bp->b_data;
blksfree = cg_blksfree(cgp);
if (bpref == 0) {
- bpref = cgp->cg_rotor;
+ bpref = cgbase(fs, cgp->cg_cgx) + cgp->cg_rotor + fs->fs_frag;
} else if ((cgbpref = dtog(fs, bpref)) != cgp->cg_cgx) {
/* map bpref to correct zone in this cg */
if (bpref < cgdata(fs, cgbpref))
diff --git a/sys/ufs/ffs/ffs_balloc.c b/sys/ufs/ffs/ffs_balloc.c
index 1e809bb..1b830ae 100644
--- a/sys/ufs/ffs/ffs_balloc.c
+++ b/sys/ufs/ffs/ffs_balloc.c
@@ -299,6 +299,10 @@ retry:
continue;
}
UFS_LOCK(ump);
+ /*
+ * If parent indirect has just been allocated, try to cluster
+ * immediately following it.
+ */
if (pref == 0)
pref = ffs_blkpref_ufs1(ip, lbn, i - num - 1,
(ufs1_daddr_t *)0);
@@ -368,7 +372,14 @@ retry:
*/
if (nb == 0) {
UFS_LOCK(ump);
- if (pref == 0)
+ /*
+ * If allocating metadata at the front of the cylinder
+ * group and parent indirect block has just been allocated,
+ * then cluster next to it if it is the first indirect in
+ * the file. Otherwise it has been allocated in the metadata
+ * area, so we want to find our own place out in the data area.
+ */
+ if (pref == 0 || (lbn > NDADDR && fs->fs_metaspace != 0))
pref = ffs_blkpref_ufs1(ip, lbn, indirs[i].in_off,
&bap[0]);
error = ffs_alloc(ip, lbn, pref, (int)fs->fs_bsize,
@@ -850,6 +861,10 @@ retry:
continue;
}
UFS_LOCK(ump);
+ /*
+ * If parent indirect has just been allocated, try to cluster
+ * immediately following it.
+ */
if (pref == 0)
pref = ffs_blkpref_ufs2(ip, lbn, i - num - 1,
(ufs2_daddr_t *)0);
@@ -920,7 +935,14 @@ retry:
*/
if (nb == 0) {
UFS_LOCK(ump);
- if (pref == 0)
+ /*
+ * If allocating metadata at the front of the cylinder
+ * group and parent indirect block has just been allocated,
+ * then cluster next to it if it is the first indirect in
+ * the file. Otherwise it has been allocated in the metadata
+ * area, so we want to find our own place out in the data area.
+ */
+ if (pref == 0 || (lbn > NDADDR && fs->fs_metaspace != 0))
pref = ffs_blkpref_ufs2(ip, lbn, indirs[i].in_off,
&bap[0]);
error = ffs_alloc(ip, lbn, pref, (int)fs->fs_bsize,
diff --git a/sys/ufs/ufs/dinode.h b/sys/ufs/ufs/dinode.h
index 8aa1fe0..6d06bde 100644
--- a/sys/ufs/ufs/dinode.h
+++ b/sys/ufs/ufs/dinode.h
@@ -138,7 +138,7 @@ struct ufs2_dinode {
int32_t di_atimensec; /* 68: Last access time. */
int32_t di_ctimensec; /* 72: Last inode change time. */
int32_t di_birthnsec; /* 76: Inode creation time. */
- int32_t di_gen; /* 80: Generation number. */
+ u_int32_t di_gen; /* 80: Generation number. */
u_int32_t di_kernflags; /* 84: Kernel flags. */
u_int32_t di_flags; /* 88: Status flags (chflags). */
int32_t di_extsize; /* 92: External attributes block. */
@@ -180,7 +180,7 @@ struct ufs1_dinode {
ufs1_daddr_t di_ib[NIADDR]; /* 88: Indirect disk blocks. */
u_int32_t di_flags; /* 100: Status flags (chflags). */
int32_t di_blocks; /* 104: Blocks actually held. */
- int32_t di_gen; /* 108: Generation number. */
+ u_int32_t di_gen; /* 108: Generation number. */
u_int32_t di_uid; /* 112: File owner. */
u_int32_t di_gid; /* 116: File group. */
u_int64_t di_modrev; /* 120: i_modrev for NFSv4 */
diff --git a/sys/ufs/ufs/inode.h b/sys/ufs/ufs/inode.h
index 6416632..97ec63f 100644
--- a/sys/ufs/ufs/inode.h
+++ b/sys/ufs/ufs/inode.h
@@ -102,7 +102,7 @@ struct inode {
int16_t i_nlink; /* File link count. */
u_int64_t i_size; /* File byte count. */
u_int32_t i_flags; /* Status flags (chflags). */
- int64_t i_gen; /* Generation number. */
+ u_int64_t i_gen; /* Generation number. */
u_int32_t i_uid; /* File owner. */
u_int32_t i_gid; /* File group. */
/*
diff --git a/sys/ufs/ufs/ufs_extattr.c b/sys/ufs/ufs/ufs_extattr.c
index d52f650..209d93e 100644
--- a/sys/ufs/ufs/ufs_extattr.c
+++ b/sys/ufs/ufs/ufs_extattr.c
@@ -399,20 +399,8 @@ ufs_extattr_iterate_directory(struct ufsmount *ump, struct vnode *dvp,
return (error);
}
- /*
- * XXXRW: While in UFS, we always get DIRBLKSIZ returns from
- * the directory code on success, on other file systems this
- * may not be the case. For portability, we should check the
- * read length on return from ufs_readdir().
- */
- edp = (struct dirent *)&dirbuf[DIRBLKSIZ];
+ edp = (struct dirent *)&dirbuf[DIRBLKSIZ - auio.uio_resid];
for (dp = (struct dirent *)dirbuf; dp < edp; ) {
-#if (BYTE_ORDER == LITTLE_ENDIAN)
- dp->d_type = dp->d_namlen;
- dp->d_namlen = 0;
-#else
- dp->d_type = 0;
-#endif
if (dp->d_reclen == 0)
break;
error = ufs_extattr_lookup(dvp, UE_GETDIR_LOCKPARENT,
@@ -934,8 +922,8 @@ ufs_extattr_get(struct vnode *vp, int attrnamespace, const char *name,
* is to coerce this to undefined, and let it get cleaned
* up by the next write or extattrctl clean.
*/
- printf("ufs_extattr_get (%s): inode number inconsistency (%d, %jd)\n",
- mp->mnt_stat.f_mntonname, ueh.ueh_i_gen, (intmax_t)ip->i_gen);
+ printf("ufs_extattr_get (%s): inode number inconsistency (%d, %ju)\n",
+ mp->mnt_stat.f_mntonname, ueh.ueh_i_gen, (uintmax_t)ip->i_gen);
error = ENOATTR;
goto vopunlock_exit;
}
diff --git a/sys/ufs/ufs/ufs_vnops.c b/sys/ufs/ufs/ufs_vnops.c
index 574c59c..14df1f3 100644
--- a/sys/ufs/ufs/ufs_vnops.c
+++ b/sys/ufs/ufs/ufs_vnops.c
@@ -2161,12 +2161,6 @@ ufs_symlink(ap)
/*
* Vnode op for reading directories.
- *
- * The routine below assumes that the on-disk format of a directory
- * is the same as that defined by <sys/dirent.h>. If the on-disk
- * format changes, then it will be necessary to do a conversion
- * from the on-disk format that read returns to the format defined
- * by <sys/dirent.h>.
*/
int
ufs_readdir(ap)
@@ -2179,103 +2173,123 @@ ufs_readdir(ap)
u_long **a_cookies;
} */ *ap;
{
+ struct vnode *vp = ap->a_vp;
struct uio *uio = ap->a_uio;
+ struct buf *bp;
struct inode *ip;
+ struct direct *dp, *edp;
+ u_long *cookies;
+ struct dirent dstdp;
+ off_t offset, startoffset;
+ size_t readcnt, skipcnt;
+ ssize_t startresid;
+ int ncookies;
int error;
- size_t count, lost;
- off_t off;
- if (ap->a_ncookies != NULL)
- /*
- * Ensure that the block is aligned. The caller can use
- * the cookies to determine where in the block to start.
- */
- uio->uio_offset &= ~(DIRBLKSIZ - 1);
- ip = VTOI(ap->a_vp);
+ if (uio->uio_offset < 0)
+ return (EINVAL);
+ ip = VTOI(vp);
if (ip->i_effnlink == 0)
return (0);
- off = uio->uio_offset;
- count = uio->uio_resid;
- /* Make sure we don't return partial entries. */
- if (count <= ((uio->uio_offset + count) & (DIRBLKSIZ -1)))
- return (EINVAL);
- count -= (uio->uio_offset + count) & (DIRBLKSIZ -1);
- lost = uio->uio_resid - count;
- uio->uio_resid = count;
- uio->uio_iov->iov_len = count;
-# if (BYTE_ORDER == LITTLE_ENDIAN)
- if (ap->a_vp->v_mount->mnt_maxsymlinklen > 0) {
- error = VOP_READ(ap->a_vp, uio, 0, ap->a_cred);
- } else {
- struct dirent *dp, *edp;
- struct uio auio;
- struct iovec aiov;
- caddr_t dirbuf;
- int readcnt;
- u_char tmp;
-
- auio = *uio;
- auio.uio_iov = &aiov;
- auio.uio_iovcnt = 1;
- auio.uio_segflg = UIO_SYSSPACE;
- aiov.iov_len = count;
- dirbuf = malloc(count, M_TEMP, M_WAITOK);
- aiov.iov_base = dirbuf;
- error = VOP_READ(ap->a_vp, &auio, 0, ap->a_cred);
- if (error == 0) {
- readcnt = count - auio.uio_resid;
- edp = (struct dirent *)&dirbuf[readcnt];
- for (dp = (struct dirent *)dirbuf; dp < edp; ) {
- tmp = dp->d_namlen;
- dp->d_namlen = dp->d_type;
- dp->d_type = tmp;
- if (dp->d_reclen > 0) {
- dp = (struct dirent *)
- ((char *)dp + dp->d_reclen);
- } else {
- error = EIO;
- break;
- }
- }
- if (dp >= edp)
- error = uiomove(dirbuf, readcnt, uio);
+ if (ap->a_ncookies != NULL) {
+ ncookies = uio->uio_resid;
+ if (uio->uio_offset >= ip->i_size)
+ ncookies = 0;
+ else if (ip->i_size - uio->uio_offset < ncookies)
+ ncookies = ip->i_size - uio->uio_offset;
+ ncookies = ncookies / (offsetof(struct direct, d_name) + 4) + 1;
+ cookies = malloc(ncookies * sizeof(*cookies), M_TEMP, M_WAITOK);
+ *ap->a_ncookies = ncookies;
+ *ap->a_cookies = cookies;
+ } else {
+ ncookies = 0;
+ cookies = NULL;
+ }
+ offset = startoffset = uio->uio_offset;
+ startresid = uio->uio_resid;
+ error = 0;
+ while (error == 0 && uio->uio_resid > 0 &&
+ uio->uio_offset < ip->i_size) {
+ error = ffs_blkatoff(vp, uio->uio_offset, NULL, &bp);
+ if (error)
+ break;
+ if (bp->b_offset + bp->b_bcount > ip->i_size)
+ readcnt = ip->i_size - bp->b_offset;
+ else
+ readcnt = bp->b_bcount;
+ skipcnt = (size_t)(uio->uio_offset - bp->b_offset) &
+ ~(size_t)(DIRBLKSIZ - 1);
+ offset = bp->b_offset + skipcnt;
+ dp = (struct direct *)&bp->b_data[skipcnt];
+ edp = (struct direct *)&bp->b_data[readcnt];
+ while (error == 0 && uio->uio_resid > 0 && dp < edp) {
+ if (dp->d_reclen <= offsetof(struct direct, d_name) ||
+ (caddr_t)dp + dp->d_reclen > (caddr_t)edp) {
+ error = EIO;
+ break;
+ }
+#if BYTE_ORDER == LITTLE_ENDIAN
+ /* Old filesystem format. */
+ if (vp->v_mount->mnt_maxsymlinklen <= 0) {
+ dstdp.d_namlen = dp->d_type;
+ dstdp.d_type = dp->d_namlen;
+ } else
+#endif
+ {
+ dstdp.d_namlen = dp->d_namlen;
+ dstdp.d_type = dp->d_type;
+ }
+ if (offsetof(struct direct, d_name) + dstdp.d_namlen >
+ dp->d_reclen) {
+ error = EIO;
+ break;
+ }
+ if (offset < startoffset || dp->d_ino == 0)
+ goto nextentry;
+ dstdp.d_fileno = dp->d_ino;
+ dstdp.d_reclen = GENERIC_DIRSIZ(&dstdp);
+ bcopy(dp->d_name, dstdp.d_name, dstdp.d_namlen);
+ dstdp.d_name[dstdp.d_namlen] = '\0';
+ if (dstdp.d_reclen > uio->uio_resid) {
+ if (uio->uio_resid == startresid)
+ error = EINVAL;
+ else
+ error = EJUSTRETURN;
+ break;
}
- free(dirbuf, M_TEMP);
+ /* Advance dp. */
+ error = uiomove((caddr_t)&dstdp, dstdp.d_reclen, uio);
+ if (error)
+ break;
+ if (cookies != NULL) {
+ KASSERT(ncookies > 0,
+ ("ufs_readdir: cookies buffer too small"));
+ *cookies = offset + dp->d_reclen;
+ cookies++;
+ ncookies--;
+ }
+nextentry:
+ offset += dp->d_reclen;
+ dp = (struct direct *)((caddr_t)dp + dp->d_reclen);
}
-# else
- error = VOP_READ(ap->a_vp, uio, 0, ap->a_cred);
-# endif
- if (!error && ap->a_ncookies != NULL) {
- struct dirent* dpStart;
- struct dirent* dpEnd;
- struct dirent* dp;
- int ncookies;
- u_long *cookies;
- u_long *cookiep;
-
- if (uio->uio_segflg != UIO_SYSSPACE || uio->uio_iovcnt != 1)
- panic("ufs_readdir: unexpected uio from NFS server");
- dpStart = (struct dirent *)
- ((char *)uio->uio_iov->iov_base - (uio->uio_offset - off));
- dpEnd = (struct dirent *) uio->uio_iov->iov_base;
- for (dp = dpStart, ncookies = 0;
- dp < dpEnd;
- dp = (struct dirent *)((caddr_t) dp + dp->d_reclen))
- ncookies++;
- cookies = malloc(ncookies * sizeof(u_long), M_TEMP,
- M_WAITOK);
- for (dp = dpStart, cookiep = cookies;
- dp < dpEnd;
- dp = (struct dirent *)((caddr_t) dp + dp->d_reclen)) {
- off += dp->d_reclen;
- *cookiep++ = (u_long) off;
+ bqrelse(bp);
+ uio->uio_offset = offset;
+ }
+ /* We need to correct uio_offset. */
+ uio->uio_offset = offset;
+ if (error == EJUSTRETURN)
+ error = 0;
+ if (ap->a_ncookies != NULL) {
+ if (error == 0) {
+ ap->a_ncookies -= ncookies;
+ } else {
+ free(*ap->a_cookies, M_TEMP);
+ *ap->a_ncookies = 0;
+ *ap->a_cookies = NULL;
}
- *ap->a_ncookies = ncookies;
- *ap->a_cookies = cookies;
}
- uio->uio_resid += lost;
- if (ap->a_eofflag)
- *ap->a_eofflag = VTOI(ap->a_vp)->i_size <= uio->uio_offset;
+ if (error == 0 && ap->a_eofflag)
+ *ap->a_eofflag = ip->i_size <= uio->uio_offset;
return (error);
}
diff --git a/sys/vm/uma_core.c b/sys/vm/uma_core.c
index 27abc3e..3a9492a 100644
--- a/sys/vm/uma_core.c
+++ b/sys/vm/uma_core.c
@@ -493,7 +493,7 @@ keg_timeout(uma_keg_t keg)
KEG_UNLOCK(keg);
hash_free(&oldhash);
- KEG_LOCK(keg);
+ return;
}
}
KEG_UNLOCK(keg);
diff --git a/sys/vm/vm.h b/sys/vm/vm.h
index 106c510..bfb4a60 100644
--- a/sys/vm/vm.h
+++ b/sys/vm/vm.h
@@ -134,10 +134,6 @@ struct kva_md_info {
vm_offset_t buffer_eva;
vm_offset_t clean_sva;
vm_offset_t clean_eva;
- vm_offset_t pager_sva;
- vm_offset_t pager_eva;
- vm_offset_t bio_transient_sva;
- vm_offset_t bio_transient_eva;
};
extern struct kva_md_info kmi;
diff --git a/sys/vm/vm_init.c b/sys/vm/vm_init.c
index 62e9891..2c4bcb6 100644
--- a/sys/vm/vm_init.c
+++ b/sys/vm/vm_init.c
@@ -76,6 +76,7 @@ __FBSDID("$FreeBSD$");
#include <sys/pipe.h>
#include <sys/bio.h>
#include <sys/buf.h>
+#include <sys/vmem.h>
#include <vm/vm.h>
#include <vm/vm_param.h>
@@ -120,6 +121,7 @@ vm_mem_init(dummy)
/*
* Initialize other VM packages
*/
+ vmem_startup();
vm_object_init();
vm_map_startup();
kmem_init(virtual_avail, virtual_end);
@@ -183,29 +185,31 @@ again:
if ((vm_size_t)((char *)v - firstaddr) != size)
panic("startup: table size inconsistency");
+ size = (long)nbuf * BKVASIZE + (long)nswbuf * MAXPHYS +
+ (long)bio_transient_maxcnt * MAXPHYS;
clean_map = kmem_suballoc(kernel_map, &kmi->clean_sva, &kmi->clean_eva,
- (long)nbuf * BKVASIZE + (long)nswbuf * MAXPHYS +
- (long)bio_transient_maxcnt * MAXPHYS, TRUE);
- buffer_map = kmem_suballoc(clean_map, &kmi->buffer_sva,
- &kmi->buffer_eva, (long)nbuf * BKVASIZE, FALSE);
- buffer_map->system_map = 1;
+ size, TRUE);
+
+ size = (long)nbuf * BKVASIZE;
+ kmi->buffer_sva = kmem_alloc_nofault(clean_map, size);
+ kmi->buffer_eva = kmi->buffer_sva + size;
+ vmem_init(buffer_arena, "buffer arena", kmi->buffer_sva, size,
+ PAGE_SIZE, 0, 0);
+
+ size = (long)nswbuf * MAXPHYS;
+ swapbkva = kmem_alloc_nofault(clean_map, size);
+ if (!swapbkva)
+ panic("Not enough clean_map VM space for pager buffers");
+
if (bio_transient_maxcnt != 0) {
- bio_transient_map = kmem_suballoc(clean_map,
- &kmi->bio_transient_sva, &kmi->bio_transient_eva,
- (long)bio_transient_maxcnt * MAXPHYS, FALSE);
- bio_transient_map->system_map = 1;
+ size = (long)bio_transient_maxcnt * MAXPHYS;
+ vmem_init(transient_arena, "transient arena",
+ kmem_alloc_nofault(clean_map, size),
+ size, PAGE_SIZE, 0, 0);
}
- pager_map = kmem_suballoc(clean_map, &kmi->pager_sva, &kmi->pager_eva,
- (long)nswbuf * MAXPHYS, FALSE);
- pager_map->system_map = 1;
exec_map = kmem_suballoc(kernel_map, &minaddr, &maxaddr,
exec_map_entries * round_page(PATH_MAX + ARG_MAX), FALSE);
pipe_map = kmem_suballoc(kernel_map, &minaddr, &maxaddr, maxpipekva,
FALSE);
-
- /*
- * XXX: Mbuf system machine-specific initializations should
- * go here, if anywhere.
- */
}
diff --git a/sys/vm/vm_kern.c b/sys/vm/vm_kern.c
index 7c7ccc1..42cd699 100644
--- a/sys/vm/vm_kern.c
+++ b/sys/vm/vm_kern.c
@@ -89,8 +89,6 @@ vm_map_t kernel_map;
vm_map_t kmem_map;
vm_map_t exec_map;
vm_map_t pipe_map;
-vm_map_t buffer_map;
-vm_map_t bio_transient_map;
const void *zero_region;
CTASSERT((ZERO_REGION_SIZE & PAGE_MASK) == 0);
diff --git a/sys/vm/vm_kern.h b/sys/vm/vm_kern.h
index 5730cef..1479e5f 100644
--- a/sys/vm/vm_kern.h
+++ b/sys/vm/vm_kern.h
@@ -64,11 +64,13 @@
#define _VM_VM_KERN_H_ 1
/* Kernel memory management definitions. */
-extern vm_map_t buffer_map;
extern vm_map_t kernel_map;
extern vm_map_t kmem_map;
extern vm_map_t exec_map;
extern vm_map_t pipe_map;
+extern struct vmem *buffer_arena;
+extern struct vmem *transient_arena;
+extern vm_offset_t swapbkva;
extern u_long vm_kmem_size;
#endif /* _VM_VM_KERN_H_ */
diff --git a/sys/vm/vm_object.c b/sys/vm/vm_object.c
index 97d8557..1c20ca6 100644
--- a/sys/vm/vm_object.c
+++ b/sys/vm/vm_object.c
@@ -2231,12 +2231,6 @@ vm_object_in_map(vm_object_t object)
/* sx_sunlock(&allproc_lock); */
if (_vm_object_in_map(kernel_map, object, 0))
return 1;
- if (_vm_object_in_map(kmem_map, object, 0))
- return 1;
- if (_vm_object_in_map(pager_map, object, 0))
- return 1;
- if (_vm_object_in_map(buffer_map, object, 0))
- return 1;
return 0;
}
diff --git a/sys/vm/vm_page.c b/sys/vm/vm_page.c
index 65ca2e3..a47209f 100644
--- a/sys/vm/vm_page.c
+++ b/sys/vm/vm_page.c
@@ -645,6 +645,7 @@ vm_page_initfake(vm_page_t m, vm_paddr_t paddr, vm_memattr_t memattr)
/* Fictitious pages don't use "order" or "pool". */
m->oflags = VPO_BUSY | VPO_UNMANAGED;
m->wire_count = 1;
+ pmap_page_init(m);
memattr:
pmap_page_set_memattr(m, memattr);
}
diff --git a/sys/vm/vm_pager.c b/sys/vm/vm_pager.c
index 989c318..c7d038b 100644
--- a/sys/vm/vm_pager.c
+++ b/sys/vm/vm_pager.c
@@ -78,6 +78,7 @@ __FBSDID("$FreeBSD$");
#include <vm/vm.h>
#include <vm/vm_param.h>
+#include <vm/vm_kern.h>
#include <vm/vm_object.h>
#include <vm/vm_page.h>
#include <vm/vm_pager.h>
@@ -174,11 +175,10 @@ static const int npagers = sizeof(pagertab) / sizeof(pagertab[0]);
* cleaning requests (NPENDINGIO == 64) * the maximum swap cluster size
* (MAXPHYS == 64k) if you want to get the most efficiency.
*/
-vm_map_t pager_map;
-static int bswneeded;
-static vm_offset_t swapbkva; /* swap buffers kva */
-struct mtx pbuf_mtx;
+struct mtx_padalign pbuf_mtx;
static TAILQ_HEAD(swqueue, buf) bswlist;
+static int bswneeded;
+vm_offset_t swapbkva; /* swap buffers kva */
void
vm_pager_init()
@@ -215,10 +215,6 @@ vm_pager_bufferinit()
cluster_pbuf_freecnt = nswbuf / 2;
vnode_pbuf_freecnt = nswbuf / 2 + 1;
-
- swapbkva = kmem_alloc_nofault(pager_map, nswbuf * MAXPHYS);
- if (!swapbkva)
- panic("Not enough pager_map VM space for physical buffers");
}
/*
diff --git a/sys/vm/vm_pager.h b/sys/vm/vm_pager.h
index b5d923c..aa1b7f0 100644
--- a/sys/vm/vm_pager.h
+++ b/sys/vm/vm_pager.h
@@ -95,9 +95,8 @@ extern struct pagerops mgtdevicepagerops;
#ifdef _KERNEL
-extern vm_map_t pager_map;
extern struct pagerops *pagertab[];
-extern struct mtx pbuf_mtx;
+extern struct mtx_padalign pbuf_mtx;
vm_object_t vm_pager_allocate(objtype_t, void *, vm_ooffset_t, vm_prot_t,
vm_ooffset_t, struct ucred *);
diff --git a/sys/vm/vm_phys.c b/sys/vm/vm_phys.c
index b871d79..66f3b0c 100644
--- a/sys/vm/vm_phys.c
+++ b/sys/vm/vm_phys.c
@@ -559,7 +559,6 @@ vm_phys_fictitious_reg_range(vm_paddr_t start, vm_paddr_t end,
}
for (i = 0; i < page_count; i++) {
vm_page_initfake(&fp[i], start + PAGE_SIZE * i, memattr);
- pmap_page_init(&fp[i]);
fp[i].oflags &= ~(VPO_BUSY | VPO_UNMANAGED);
}
mtx_lock(&vm_phys_fictitious_reg_mtx);
diff --git a/tools/build/options/WITHOUT_SVNLITE b/tools/build/options/WITHOUT_SVNLITE
new file mode 100644
index 0000000..61b66dc
--- /dev/null
+++ b/tools/build/options/WITHOUT_SVNLITE
@@ -0,0 +1,4 @@
+.\" $FreeBSD$
+Set to not build
+.Xr svnlite 1
+and related programs.
diff --git a/tools/build/options/WITH_SVN b/tools/build/options/WITH_SVN
new file mode 100644
index 0000000..829a3a7
--- /dev/null
+++ b/tools/build/options/WITH_SVN
@@ -0,0 +1,5 @@
+.\" $FreeBSD$
+Set to install
+.Xr svnlite 1
+as
+.Xr svn 1 .
diff --git a/tools/tools/cxgbetool/cxgbetool.c b/tools/tools/cxgbetool/cxgbetool.c
index 6b90b12..a97cf1f 100644
--- a/tools/tools/cxgbetool/cxgbetool.c
+++ b/tools/tools/cxgbetool/cxgbetool.c
@@ -965,6 +965,7 @@ set_filter(uint32_t idx, int argc, const char *argv[])
};
bzero(&t, sizeof (t));
t.idx = idx;
+ t.fs.hitcnts = 1;
for (start_arg = 0; start_arg + 2 <= argc; start_arg += 2) {
const char **args = &argv[start_arg];
diff --git a/usr.bin/Makefile b/usr.bin/Makefile
index 535b2ab..0e7edf2 100644
--- a/usr.bin/Makefile
+++ b/usr.bin/Makefile
@@ -366,7 +366,7 @@ SUBDIR+= users
SUBDIR+= who
.endif
-.if ${MACHINE_ARCH} == "amd64" || ${MACHINE_ARCH} == "ia64" || ${MACHINE_ARCH} == "sparc64" || ${MACHINE_ARCH} == "i386"
+.if ${MACHINE_ARCH} == "amd64" || ${MACHINE_ARCH} == "ia64" || ${MACHINE_ARCH} == "sparc64" || ${MACHINE_ARCH} == "i386" || ${MACHINE_ARCH} == "armv6"
.if ${MK_SVN} == "yes" || ${MK_SVNLITE} == "yes"
SUBDIR+= svn
.endif
diff --git a/usr.bin/Makefile.amd64 b/usr.bin/Makefile.amd64
index b720e3d..fee2fb0 100644
--- a/usr.bin/Makefile.amd64
+++ b/usr.bin/Makefile.amd64
@@ -1,2 +1,3 @@
# $FreeBSD$
+SUBDIR+= smbutil
diff --git a/usr.bin/Makefile.i386 b/usr.bin/Makefile.i386
index b720e3d..fee2fb0 100644
--- a/usr.bin/Makefile.i386
+++ b/usr.bin/Makefile.i386
@@ -1,2 +1,3 @@
# $FreeBSD$
+SUBDIR+= smbutil
diff --git a/usr.bin/Makefile.ia64 b/usr.bin/Makefile.ia64
index b720e3d..fee2fb0 100644
--- a/usr.bin/Makefile.ia64
+++ b/usr.bin/Makefile.ia64
@@ -1,2 +1,3 @@
# $FreeBSD$
+SUBDIR+= smbutil
diff --git a/usr.bin/Makefile.powerpc b/usr.bin/Makefile.powerpc
index b720e3d..fee2fb0 100644
--- a/usr.bin/Makefile.powerpc
+++ b/usr.bin/Makefile.powerpc
@@ -1,2 +1,3 @@
# $FreeBSD$
+SUBDIR+= smbutil
diff --git a/usr.bin/Makefile.sparc64 b/usr.bin/Makefile.sparc64
index b720e3d..fee2fb0 100644
--- a/usr.bin/Makefile.sparc64
+++ b/usr.bin/Makefile.sparc64
@@ -1,2 +1,3 @@
# $FreeBSD$
+SUBDIR+= smbutil
diff --git a/usr.bin/calendar/calendars/calendar.freebsd b/usr.bin/calendar/calendars/calendar.freebsd
index b1a05c1..333eebc 100644
--- a/usr.bin/calendar/calendars/calendar.freebsd
+++ b/usr.bin/calendar/calendars/calendar.freebsd
@@ -62,6 +62,7 @@
02/14 Manolis Kiagias <manolis@FreeBSD.org> born in Chania, Greece, 1970
02/14 Erwin Lansing <erwin@FreeBSD.org> born in 's-Hertogenbosch, the Netherlands, 1975
02/14 Martin Blapp <mbr@FreeBSD.org> born in Olten, Switzerland, 1976
+02/15 Hiren Panchasara <hiren@FreeBSD.org> born in Ahmedabad, Gujarat, India, 1984
02/19 Murray Stokely <murray@FreeBSD.org> born in Jacksonville, Florida, United States, 1979
02/20 Anders Nordby <anders@FreeBSD.org> born in Oslo, Norway, 1976
02/21 Alexey Zelkin <phantom@FreeBSD.org> born in Simferopol, Ukraine, 1978
diff --git a/usr.bin/iconv/iconv.c b/usr.bin/iconv/iconv.c
index f7bd0a36..77dd11a 100644
--- a/usr.bin/iconv/iconv.c
+++ b/usr.bin/iconv/iconv.c
@@ -74,7 +74,7 @@ do_conv(FILE *fp, const char *from, const char *to, bool silent,
{
iconv_t cd;
char inbuf[INBUFSIZE], outbuf[OUTBUFSIZE], *out;
- char *in;
+ const char *in;
size_t inbytes, outbytes, ret;
if ((cd = iconv_open(to, from)) == (iconv_t)-1)
diff --git a/usr.bin/kdump/kdump.c b/usr.bin/kdump/kdump.c
index 550c4cb..77a0cae 100644
--- a/usr.bin/kdump/kdump.c
+++ b/usr.bin/kdump/kdump.c
@@ -1482,6 +1482,8 @@ ktrsockaddr(struct sockaddr *sa)
TODO: Support additional address families
#include <netnatm/natm.h>
struct sockaddr_natm *natm;
+ #include <netsmb/netbios.h>
+ struct sockaddr_nb *nb;
*/
char addr[64];
diff --git a/usr.bin/killall/killall.1 b/usr.bin/killall/killall.1
index 7beaadb..be813f7 100644
--- a/usr.bin/killall/killall.1
+++ b/usr.bin/killall/killall.1
@@ -24,7 +24,7 @@
.\"
.\" $FreeBSD$
.\"
-.Dd June 27, 2012
+.Dd June 30, 2013
.Dt KILLALL 1
.Os
.Sh NAME
@@ -110,6 +110,8 @@ the specified
Limit potentially matching processes to those matching
the specified
.Ar procname .
+.It Fl q
+Suppress error message if no processes are matched.
.It Fl z
Do not skip zombies.
This should not have any effect except to print a few error messages
diff --git a/usr.bin/killall/killall.c b/usr.bin/killall/killall.c
index 83e829a..dd22a83 100644
--- a/usr.bin/killall/killall.c
+++ b/usr.bin/killall/killall.c
@@ -53,7 +53,7 @@ static void __dead2
usage(void)
{
- fprintf(stderr, "usage: killall [-delmsvz] [-help] [-I] [-j jail]\n");
+ fprintf(stderr, "usage: killall [-delmsqvz] [-help] [-I] [-j jail]\n");
fprintf(stderr,
" [-u user] [-t tty] [-c cmd] [-SIGNAL] [cmd]...\n");
fprintf(stderr, "At least one option or argument to specify processes must be given.\n");
@@ -101,6 +101,7 @@ main(int ac, char **av)
char *user = NULL;
char *tty = NULL;
char *cmd = NULL;
+ int qflag = 0;
int vflag = 0;
int sflag = 0;
int dflag = 0;
@@ -191,6 +192,9 @@ main(int ac, char **av)
errx(1, "must specify procname");
cmd = *av;
break;
+ case 'q':
+ qflag++;
+ break;
case 'v':
vflag++;
break;
@@ -417,8 +421,9 @@ main(int ac, char **av)
}
}
if (killed == 0) {
- fprintf(stderr, "No matching processes %swere found\n",
- getuid() != 0 ? "belonging to you " : "");
+ if (!qflag)
+ fprintf(stderr, "No matching processes %swere found\n",
+ getuid() != 0 ? "belonging to you " : "");
errors = 1;
}
exit(errors);
diff --git a/usr.bin/mail/popen.c b/usr.bin/mail/popen.c
index c4d1746..60a0936 100644
--- a/usr.bin/mail/popen.c
+++ b/usr.bin/mail/popen.c
@@ -316,7 +316,7 @@ sigchild(int signo __unused)
int status;
struct child *cp;
- while ((pid = waitpid((pid_t)-1, &status, WNOHANG)) > 0) {
+ while ((pid = waitpid(-1, &status, WNOHANG)) > 0) {
cp = findchild(pid);
if (cp->free)
delchild(cp);
diff --git a/usr.bin/make/job.c b/usr.bin/make/job.c
index bc1d6a0..6fee0ec 100644
--- a/usr.bin/make/job.c
+++ b/usr.bin/make/job.c
@@ -2128,7 +2128,7 @@ Job_CatchChildren(Boolean block)
}
for (;;) {
- pid = waitpid((pid_t)-1, &status,
+ pid = waitpid(-1, &status,
(block ? 0 : WNOHANG) | WUNTRACED);
if (pid <= 0)
break;
@@ -2628,7 +2628,7 @@ Job_AbortAll(void)
/*
* Catch as many children as want to report in at first, then give up
*/
- while (waitpid((pid_t)-1, &foo, WNOHANG) > 0)
+ while (waitpid(-1, &foo, WNOHANG) > 0)
;
}
diff --git a/usr.bin/mkcsmapper/lex.l b/usr.bin/mkcsmapper/lex.l
index 4ba7fc4..33b7f8e 100644
--- a/usr.bin/mkcsmapper/lex.l
+++ b/usr.bin/mkcsmapper/lex.l
@@ -41,6 +41,8 @@
#include "ldef.h"
#include "yacc.h"
+#define YY_DECL int yylex(void)
+
int linenumber = 1;
%}
%option noinput
diff --git a/usr.bin/mkesdb/lex.l b/usr.bin/mkesdb/lex.l
index 19c5379..c9d9cc1 100644
--- a/usr.bin/mkesdb/lex.l
+++ b/usr.bin/mkesdb/lex.l
@@ -43,6 +43,8 @@
#include "ldef.h"
#include "yacc.h"
+#define YY_DECL int yylex(void)
+
int linenumber = 1;
%}
%option noinput
diff --git a/usr.bin/patch/pch.c b/usr.bin/patch/pch.c
index 411dcac..c6224e7 100644
--- a/usr.bin/patch/pch.c
+++ b/usr.bin/patch/pch.c
@@ -101,13 +101,17 @@ void
open_patch_file(const char *filename)
{
struct stat filestat;
+ int nr, nw;
if (filename == NULL || *filename == '\0' || strEQ(filename, "-")) {
pfp = fopen(TMPPATNAME, "w");
if (pfp == NULL)
pfatal("can't create %s", TMPPATNAME);
- while (fgets(buf, buf_size, stdin) != NULL)
- fputs(buf, pfp);
+ while ((nr = fread(buf, 1, buf_size, stdin)) > 0) {
+ nw = fwrite(buf, 1, nr, pfp);
+ if (nr != nw)
+ pfatal("write error to %s", TMPPATNAME);
+ }
if (ferror(pfp) || fclose(pfp))
pfatal("can't write %s", TMPPATNAME);
filename = TMPPATNAME;
@@ -1200,7 +1204,7 @@ pgets(bool do_indent)
indent++;
}
}
- strncpy(buf, line, len - skipped);
+ memcpy(buf, line, len - skipped);
buf[len - skipped] = '\0';
}
return len;
diff --git a/usr.bin/rwho/rwho.c b/usr.bin/rwho/rwho.c
index 5f1334f..bcb5adb 100644
--- a/usr.bin/rwho/rwho.c
+++ b/usr.bin/rwho/rwho.c
@@ -1,6 +1,7 @@
-/*
- * Copyright (c) 1983, 1993
- * The Regents of the University of California. All rights reserved.
+/*-
+ * Copyright (c) 1983, 1993 The Regents of the University of California.
+ * Copyright (c) 2013 Mariusz Zaborski <oshogbo@FreeBSD.org>
+ * All rights reserved.
*
* Redistribution and use in source and binary forms, with or without
* modification, are permitted provided that the following conditions
@@ -42,6 +43,7 @@ static char sccsid[] = "@(#)rwho.c 8.1 (Berkeley) 6/6/93";
#include <sys/cdefs.h>
__FBSDID("$FreeBSD$");
+#include <sys/capability.h>
#include <sys/param.h>
#include <sys/file.h>
@@ -49,6 +51,7 @@ __FBSDID("$FreeBSD$");
#include <dirent.h>
#include <err.h>
+#include <errno.h>
#include <langinfo.h>
#include <locale.h>
#include <stdio.h>
@@ -58,22 +61,21 @@ __FBSDID("$FreeBSD$");
#include <timeconv.h>
#include <unistd.h>
-static DIR *dirp;
+#define NUSERS 1000
+#define WHDRSIZE (ssize_t)(sizeof(wd) - sizeof(wd.wd_we))
+/*
+ * this macro should be shared with ruptime.
+ */
+#define down(w,now) ((now) - (w)->wd_recvtime > 11 * 60)
+static DIR *dirp;
static struct whod wd;
-#define NUSERS 1000
+static int nusers;
static struct myutmp {
char myhost[sizeof(wd.wd_hostname)];
int myidle;
struct outmp myutmp;
} myutmp[NUSERS];
-static int nusers;
-
-#define WHDRSIZE (ssize_t)(sizeof (wd) - sizeof (wd.wd_we))
-/*
- * this macro should be shared with ruptime.
- */
-#define down(w,now) ((now) - (w)->wd_recvtime > 11 * 60)
static time_t now;
static int aflg;
@@ -88,17 +90,20 @@ main(int argc, char *argv[])
struct dirent *dp;
int width;
ssize_t cc;
- register struct whod *w = &wd;
- register struct whoent *we;
- register struct myutmp *mp;
+ struct whod *w;
+ struct whoent *we;
+ struct myutmp *mp;
int f, n, i;
int d_first;
+ int dfd;
+ time_t ct;
+ w = &wd;
(void) setlocale(LC_TIME, "");
d_first = (*nl_langinfo(D_MD_ORDER) == 'd');
- while ((ch = getopt(argc, argv, "a")) != -1)
- switch((char)ch) {
+ while ((ch = getopt(argc, argv, "a")) != -1) {
+ switch ((char)ch) {
case 'a':
aflg = 1;
break;
@@ -106,52 +111,73 @@ main(int argc, char *argv[])
default:
usage();
}
+ }
argc -= optind;
argv += optind;
if (argc != 0)
usage();
- if (chdir(_PATH_RWHODIR) || (dirp = opendir(".")) == NULL)
- err(1, "%s", _PATH_RWHODIR);
+ if (chdir(_PATH_RWHODIR) < 0)
+ err(1, "chdir(%s)", _PATH_RWHODIR);
+ if ((dirp = opendir(".")) == NULL)
+ err(1, "opendir(%s)", _PATH_RWHODIR);
+ dfd = dirfd(dirp);
mp = myutmp;
- (void)time(&now);
- while ((dp = readdir(dirp))) {
- if (dp->d_ino == 0 || strncmp(dp->d_name, "whod.", 5))
+ if (cap_rights_limit(dfd, CAP_READ | CAP_LOOKUP) < 0 && errno != ENOSYS)
+ err(1, "cap_rights_limit failed: %s", _PATH_RWHODIR);
+ /*
+ * Cache files required for time(3) and localtime(3) before entering
+ * capability mode.
+ */
+ (void) time(&ct);
+ (void) localtime(&ct);
+ if (cap_enter() < 0 && errno != ENOSYS)
+ err(1, "cap_enter");
+ (void) time(&now);
+ while ((dp = readdir(dirp)) != NULL) {
+ if (dp->d_ino == 0 || strncmp(dp->d_name, "whod.", 5) != 0)
continue;
- f = open(dp->d_name, O_RDONLY);
+ f = openat(dfd, dp->d_name, O_RDONLY);
if (f < 0)
continue;
- cc = read(f, (char *)&wd, sizeof (struct whod));
+ if (cap_rights_limit(f, CAP_READ) < 0 && errno != ENOSYS)
+ err(1, "cap_rights_limit failed: %s", dp->d_name);
+ cc = read(f, (char *)&wd, sizeof(struct whod));
if (cc < WHDRSIZE) {
(void) close(f);
continue;
}
- if (down(w,now)) {
+ if (down(w, now) != 0) {
(void) close(f);
continue;
}
cc -= WHDRSIZE;
we = w->wd_we;
- for (n = cc / sizeof (struct whoent); n > 0; n--) {
- if (aflg == 0 && we->we_idle >= 60*60) {
+ for (n = cc / sizeof(struct whoent); n > 0; n--) {
+ if (aflg == 0 && we->we_idle >= 60 * 60) {
we++;
continue;
}
if (nusers >= NUSERS)
errx(1, "too many users");
- mp->myutmp = we->we_utmp; mp->myidle = we->we_idle;
+ mp->myutmp = we->we_utmp;
+ mp->myidle = we->we_idle;
(void) strcpy(mp->myhost, w->wd_hostname);
- nusers++; we++; mp++;
+ nusers++;
+ we++;
+ mp++;
}
(void) close(f);
}
- qsort((char *)myutmp, nusers, sizeof (struct myutmp), utmpcmp);
+ qsort((char *)myutmp, nusers, sizeof(struct myutmp), utmpcmp);
mp = myutmp;
width = 0;
for (i = 0; i < nusers; i++) {
/* append one for the blank and use 8 for the out_line */
- int j = strlen(mp->myhost) + 1 + sizeof(mp->myutmp.out_line);
+ int j;
+
+ j = strlen(mp->myhost) + 1 + sizeof(mp->myutmp.out_line);
if (j > width)
width = j;
mp++;
@@ -159,31 +185,29 @@ main(int argc, char *argv[])
mp = myutmp;
for (i = 0; i < nusers; i++) {
char buf[BUFSIZ], cbuf[80];
- time_t t = _int_to_time(mp->myutmp.out_time);
+ time_t t;
- strftime(cbuf, sizeof(cbuf),
- d_first ? "%e %b %R" : "%b %e %R",
- localtime(&t));
- (void)sprintf(buf, "%s:%-.*s", mp->myhost,
- (int)sizeof(mp->myutmp.out_line), mp->myutmp.out_line);
+ t = _int_to_time(mp->myutmp.out_time);
+ strftime(cbuf, sizeof(cbuf), d_first ? "%e %b %R" : "%b %e %R",
+ localtime(&t));
+ (void) sprintf(buf, "%s:%-.*s", mp->myhost,
+ (int)sizeof(mp->myutmp.out_line), mp->myutmp.out_line);
printf("%-*.*s %-*s %s",
- (int)sizeof(mp->myutmp.out_name),
- (int)sizeof(mp->myutmp.out_name),
- mp->myutmp.out_name,
- width,
- buf,
- cbuf);
+ (int)sizeof(mp->myutmp.out_name),
+ (int)sizeof(mp->myutmp.out_name),
+ mp->myutmp.out_name, width, buf, cbuf);
mp->myidle /= 60;
- if (mp->myidle) {
- if (aflg) {
- if (mp->myidle >= 100*60)
- mp->myidle = 100*60 - 1;
+ if (mp->myidle != 0) {
+ if (aflg != 0) {
+ if (mp->myidle >= 100 * 60)
+ mp->myidle = 100 * 60 - 1;
if (mp->myidle >= 60)
printf(" %2d", mp->myidle / 60);
else
printf(" ");
- } else
+ } else {
printf(" ");
+ }
printf(":%02d", mp->myidle % 60);
}
printf("\n");
@@ -196,6 +220,7 @@ main(int argc, char *argv[])
static void
usage(void)
{
+
fprintf(stderr, "usage: rwho [-a]\n");
exit(1);
}
@@ -208,12 +233,12 @@ utmpcmp(const void *u1, const void *u2)
int rc;
rc = strncmp(MYUTMP(u1)->myutmp.out_name, MYUTMP(u2)->myutmp.out_name,
- sizeof(MYUTMP(u2)->myutmp.out_name));
- if (rc)
+ sizeof(MYUTMP(u2)->myutmp.out_name));
+ if (rc != 0)
return (rc);
rc = strcmp(MYUTMP(u1)->myhost, MYUTMP(u2)->myhost);
- if (rc)
+ if (rc != 0)
return (rc);
- return (strncmp(MYUTMP(u1)->myutmp.out_line, MYUTMP(u2)->myutmp.out_line,
- sizeof(MYUTMP(u2)->myutmp.out_line)));
+ return (strncmp(MYUTMP(u1)->myutmp.out_line,
+ MYUTMP(u2)->myutmp.out_line, sizeof(MYUTMP(u2)->myutmp.out_line)));
}
diff --git a/usr.bin/svn/lib/libapr_util/Makefile b/usr.bin/svn/lib/libapr_util/Makefile
index 37b787b..4d82443 100644
--- a/usr.bin/svn/lib/libapr_util/Makefile
+++ b/usr.bin/svn/lib/libapr_util/Makefile
@@ -28,5 +28,11 @@ CFLAGS+= -DHAVE_CONFIG_H \
-I${APR}/include/arch/unix \
-I${APR}/include
+.include <bsd.own.mk>
+
+.if ${MK_ICONV} == "yes"
+CFLAGS+= -DHAVE_ICONV_H=1 -DAPU_HAVE_ICONV=1
+.endif
+
.include <bsd.lib.mk>
diff --git a/usr.bin/svn/lib/libapr_util/apr_ldap.h b/usr.bin/svn/lib/libapr_util/apr_ldap.h
index c0bb0b2..27a96de 100644
--- a/usr.bin/svn/lib/libapr_util/apr_ldap.h
+++ b/usr.bin/svn/lib/libapr_util/apr_ldap.h
@@ -41,6 +41,7 @@
#define APR_HAS_MOZILLA_LDAPSDK 0
#define APR_HAS_OPENLDAP_LDAPSDK 0
#define APR_HAS_MICROSOFT_LDAPSDK 0
+#define APR_HAS_TIVOLI_LDAPSDK 0
#define APR_HAS_ZOS_LDAPSDK 0
#define APR_HAS_OTHER_LDAPSDK 0
@@ -58,7 +59,11 @@
* apr_ldap_url_parse*() functions have been rewritten specifically for
* APR, so the APR_HAS_LDAP_URL_PARSE macro is forced to zero.
*/
+#if APR_HAS_TIVOLI_LDAPSDK
+#define APR_HAS_LDAP_SSL 0
+#else
#define APR_HAS_LDAP_SSL 1
+#endif
#define APR_HAS_LDAP_URL_PARSE 0
#if APR_HAS_OPENLDAP_LDAPSDK && !defined(LDAP_DEPRECATED)
@@ -98,15 +103,19 @@
/*
* For ldap function calls that input a size limit on the number of returned elements
* Some SDKs do not have the define for LDAP_DEFAULT_LIMIT (-1) or LDAP_NO_LIMIT (0)
+ * LDAP_DEFAULT_LIMIT is preferred as it allows inheritance from whatever the SDK
+ * or process is configured for.
*/
-#if APR_HAS_ZOS_LDAPSDK
-#define APR_LDAP_SIZELIMIT LDAP_NO_LIMIT
-#else
#ifdef LDAP_DEFAULT_LIMIT
#define APR_LDAP_SIZELIMIT LDAP_DEFAULT_LIMIT
#else
-#define APR_LDAP_SIZELIMIT -1 /* equivalent to LDAP_DEFAULT_LIMIT */
+#ifdef LDAP_NO_LIMIT
+#define APR_LDAP_SIZELIMIT LDAP_NO_LIMIT
+#endif
#endif
+
+#ifndef APR_LDAP_SIZELIMIT
+#define APR_LDAP_SIZELIMIT 0 /* equivalent to LDAP_NO_LIMIT, and what goes on the wire */
#endif
/*
@@ -147,10 +156,43 @@ typedef struct apr_ldap_err_t {
}
#endif
+/* The MS SDK returns LDAP_UNAVAILABLE when the backend has closed the connection
+ * between LDAP calls. Protect with APR_HAS_MICROSOFT_LDAPSDK in case someone
+ * manually chooses another SDK on Windows
+ */
+#if APR_HAS_MICROSOFT_LDAPSDK
+#define APR_LDAP_IS_SERVER_DOWN(s) ((s) == LDAP_SERVER_DOWN \
+ || (s) == LDAP_UNAVAILABLE)
+#else
+#define APR_LDAP_IS_SERVER_DOWN(s) ((s) == LDAP_SERVER_DOWN)
+#endif
+
+/* These symbols are not actually exported in a DSO build, but mapped into
+ * a private exported function array for apr_ldap_stub to bind dynamically.
+ * Rename them appropriately to protect the global namespace.
+ */
+#ifdef APU_DSO_LDAP_BUILD
+
+#define apr_ldap_info apr__ldap_info
+#define apr_ldap_init apr__ldap_init
+#define apr_ldap_ssl_init apr__ldap_ssl_init
+#define apr_ldap_ssl_deinit apr__ldap_ssl_deinit
+#define apr_ldap_get_option apr__ldap_get_option
+#define apr_ldap_set_option apr__ldap_set_option
+#define apr_ldap_rebind_init apr__ldap_rebind_init
+#define apr_ldap_rebind_add apr__ldap_rebind_add
+#define apr_ldap_rebind_remove apr__ldap_rebind_remove
+
+#define APU_DECLARE_LDAP(type) type
+#else
+#define APU_DECLARE_LDAP(type) APU_DECLARE(type)
+#endif
+
#include "apr_ldap_url.h"
#include "apr_ldap_init.h"
#include "apr_ldap_option.h"
+#include "apr_ldap_rebind.h"
-/** @} */
#endif /* APR_HAS_LDAP */
+/** @} */
#endif /* APU_LDAP_H */
diff --git a/usr.bin/svn/lib/libapr_util/apu.h b/usr.bin/svn/lib/libapr_util/apu.h
index fc173e2..ea090bf 100644
--- a/usr.bin/svn/lib/libapr_util/apu.h
+++ b/usr.bin/svn/lib/libapr_util/apu.h
@@ -48,25 +48,41 @@
* use the most appropriate calling convention. Public APR functions with
* variable arguments must use APU_DECLARE_NONSTD().
*
- * @deffunc APU_DECLARE(rettype) apr_func(args);
+ * @fn APU_DECLARE(rettype) apr_func(args);
*/
#define APU_DECLARE(type) type
/**
* The public APR-UTIL functions using variable arguments are declared with
* APU_DECLARE_NONSTD(), as they must use the C language calling convention.
*
- * @deffunc APU_DECLARE_NONSTD(rettype) apr_func(args, ...);
+ * @fn APU_DECLARE_NONSTD(rettype) apr_func(args, ...);
*/
#define APU_DECLARE_NONSTD(type) type
/**
* The public APR-UTIL variables are declared with APU_DECLARE_DATA.
* This assures the appropriate indirection is invoked at compile time.
*
- * @deffunc APU_DECLARE_DATA type apr_variable;
- * @tip APU_DECLARE_DATA extern type apr_variable; syntax is required for
+ * @fn APU_DECLARE_DATA type apr_variable;
+ * @note APU_DECLARE_DATA extern type apr_variable; syntax is required for
* declarations within headers to properly import the variable.
*/
#define APU_DECLARE_DATA
+
+#if !defined(WIN32) || defined(APU_MODULE_DECLARE_STATIC)
+/**
+ * Declare a dso module's exported module structure as APU_MODULE_DECLARE_DATA.
+ *
+ * Unless APU_MODULE_DECLARE_STATIC is defined at compile time, symbols
+ * declared with APU_MODULE_DECLARE_DATA are always exported.
+ * @code
+ * module APU_MODULE_DECLARE_DATA mod_tag
+ * @endcode
+ */
+#define APU_MODULE_DECLARE_DATA
+#else
+#define APU_MODULE_DECLARE_DATA __declspec(dllexport)
+#endif
+
/*
* we always have SDBM (it's in our codebase)
*/
@@ -77,15 +93,26 @@
#if APU_HAVE_DB
#define APU_HAVE_DB_VERSION 0
-#endif /* APU_HAVE_DB */
+#endif
#define APU_HAVE_PGSQL 0
#define APU_HAVE_MYSQL 0
#define APU_HAVE_SQLITE3 0
#define APU_HAVE_SQLITE2 0
+#define APU_HAVE_ORACLE 0
+#define APU_HAVE_FREETDS 0
+#define APU_HAVE_ODBC 0
+
+#define APU_HAVE_CRYPTO 0
+#define APU_HAVE_OPENSSL 0
+#define APU_HAVE_NSS 0
+#ifndef APU_HAVE_APR_ICONV
#define APU_HAVE_APR_ICONV 0
+#endif
+#ifndef APU_HAVE_ICONV
#define APU_HAVE_ICONV 0
+#endif
#define APR_HAS_XLATE (APU_HAVE_APR_ICONV || APU_HAVE_ICONV)
#endif /* APU_H */
diff --git a/usr.bin/svn/lib/libapr_util/apu_config.h b/usr.bin/svn/lib/libapr_util/apu_config.h
index cb4e932..ac247d0 100644
--- a/usr.bin/svn/lib/libapr_util/apu_config.h
+++ b/usr.bin/svn/lib/libapr_util/apu_config.h
@@ -6,9 +6,18 @@
/* Define if the system crypt() function is threadsafe */
/* #undef APU_CRYPT_THREADSAFE */
+/* Define to 1 if modular components are built as DSOs */
+/* #undef APU_DSO_BUILD */
+
+/* Define to be absolute path to DSO directory */
+/* #undef APU_DSO_LIBDIR */
+
/* Define if the inbuf parm to iconv() is const char ** */
/* #undef APU_ICONV_INBUF_CONST */
+/* Define that OpenSSL uses const buffers */
+#define CRYPTO_OPENSSL_CONST_BUFFERS 1
+
/* Define if crypt_r has uses CRYPTD */
/* #undef CRYPT_R_CRYPTD */
@@ -21,9 +30,16 @@
/* Define to 1 if you have the `crypt_r' function. */
/* #undef HAVE_CRYPT_R */
+/* Define to 1 if you have the declaration of `EVP_PKEY_CTX_new', and to 0 if
+ you don't. */
+#define HAVE_DECL_EVP_PKEY_CTX_NEW 1
+
/* Define if expat.h is available */
#define HAVE_EXPAT_H 1
+/* Define to 1 if you have the <freetds/sybdb.h> header file. */
+/* #undef HAVE_FREETDS_SYBDB_H */
+
/* Define to 1 if you have the <iconv.h> header file. */
/* #undef HAVE_ICONV_H */
@@ -42,9 +58,6 @@
/* Define to 1 if you have the <ldap_ssl.h> header file. */
/* #undef HAVE_LDAP_SSL_H */
-/* Define to 1 if you have the `lber' library (-llber). */
-/* #undef HAVE_LIBLBER */
-
/* Define to 1 if you have the <libpq-fe.h> header file. */
/* #undef HAVE_LIBPQ_FE_H */
@@ -57,18 +70,57 @@
/* Define to 1 if you have the <mysql/mysql.h> header file. */
/* #undef HAVE_MYSQL_MYSQL_H */
+/* Define to 1 if you have the <mysql/my_global.h> header file. */
+/* #undef HAVE_MYSQL_MY_GLOBAL_H */
+
+/* Define to 1 if you have the <mysql/my_sys.h> header file. */
+/* #undef HAVE_MYSQL_MY_SYS_H */
+
+/* Define to 1 if you have the <my_global.h> header file. */
+/* #undef HAVE_MY_GLOBAL_H */
+
+/* Define to 1 if you have the <my_sys.h> header file. */
+/* #undef HAVE_MY_SYS_H */
+
/* Define to 1 if you have the `nl_langinfo' function. */
#define HAVE_NL_LANGINFO 1
+/* Define to 1 if you have the <nss.h> header file. */
+/* #undef HAVE_NSS_H */
+
+/* Define to 1 if you have the <nss/nss.h> header file. */
+/* #undef HAVE_NSS_NSS_H */
+
+/* Define to 1 if you have the <nss/pk11pub.h> header file. */
+/* #undef HAVE_NSS_PK11PUB_H */
+
+/* Define to 1 if you have the <oci.h> header file. */
+/* #undef HAVE_OCI_H */
+
+/* Define to 1 if you have the <odbc/sql.h> header file. */
+/* #undef HAVE_ODBC_SQL_H */
+
+/* Define to 1 if you have the <openssl/x509.h> header file. */
+#define HAVE_OPENSSL_X509_H 1
+
+/* Define to 1 if you have the <pk11pub.h> header file. */
+/* #undef HAVE_PK11PUB_H */
+
/* Define to 1 if you have the <postgresql/libpq-fe.h> header file. */
/* #undef HAVE_POSTGRESQL_LIBPQ_FE_H */
+/* Define to 1 if you have the <prerror.h> header file. */
+/* #undef HAVE_PRERROR_H */
+
/* Define to 1 if you have the <sqlite3.h> header file. */
/* #undef HAVE_SQLITE3_H */
/* Define to 1 if you have the <sqlite.h> header file. */
/* #undef HAVE_SQLITE_H */
+/* Define to 1 if you have the <sql.h> header file. */
+/* #undef HAVE_SQL_H */
+
/* Define to 1 if you have the <stdint.h> header file. */
#define HAVE_STDINT_H 1
@@ -81,6 +133,9 @@
/* Define to 1 if you have the <string.h> header file. */
#define HAVE_STRING_H 1
+/* Define to 1 if you have the <sybdb.h> header file. */
+/* #undef HAVE_SYBDB_H */
+
/* Define to 1 if you have the <sys/stat.h> header file. */
#define HAVE_SYS_STAT_H 1
@@ -99,6 +154,9 @@
/* Define if xml/xmlparse.h is available */
/* #undef HAVE_XML_XMLPARSE_H */
+/* Define if ldap_set_rebind_proc takes three arguments */
+/* #undef LDAP_SET_REBIND_PROC_THREE */
+
/* Define to the address where bug reports for this package should be sent. */
#define PACKAGE_BUGREPORT ""
@@ -111,6 +169,9 @@
/* Define to the one symbol short name of this package. */
#define PACKAGE_TARNAME ""
+/* Define to the home page for this package. */
+#define PACKAGE_URL ""
+
/* Define to the version of this package. */
#define PACKAGE_VERSION ""
diff --git a/usr.bin/truss/syscalls.c b/usr.bin/truss/syscalls.c
index 0afd083..1a9c4de 100644
--- a/usr.bin/truss/syscalls.c
+++ b/usr.bin/truss/syscalls.c
@@ -343,7 +343,7 @@ static struct xlat open_flags[] = {
X(O_RDONLY) X(O_WRONLY) X(O_RDWR) X(O_ACCMODE) X(O_NONBLOCK)
X(O_APPEND) X(O_SHLOCK) X(O_EXLOCK) X(O_ASYNC) X(O_FSYNC)
X(O_NOFOLLOW) X(O_CREAT) X(O_TRUNC) X(O_EXCL) X(O_NOCTTY)
- X(O_DIRECT) XEND
+ X(O_DIRECT) X(O_DIRECTORY) X(O_EXEC) X(O_TTY_INIT) X(O_CLOEXEC) XEND
};
static struct xlat shutdown_arg[] = {
diff --git a/usr.sbin/Makefile.amd64 b/usr.sbin/Makefile.amd64
index 5ee2165..b821625 100644
--- a/usr.sbin/Makefile.amd64
+++ b/usr.sbin/Makefile.amd64
@@ -20,6 +20,7 @@ SUBDIR+= btxld
SUBDIR+= cpucontrol
SUBDIR+= kgmon
SUBDIR+= lptcontrol
+SUBDIR+= mount_smbfs
SUBDIR+= mptable
.if ${MK_NDIS} != "no"
SUBDIR+= ndiscvt
diff --git a/usr.sbin/Makefile.i386 b/usr.sbin/Makefile.i386
index 901c2d9..7d66f78 100644
--- a/usr.sbin/Makefile.i386
+++ b/usr.sbin/Makefile.i386
@@ -12,6 +12,7 @@ SUBDIR+= cpucontrol
SUBDIR+= kgmon
SUBDIR+= kgzip
SUBDIR+= lptcontrol
+SUBDIR+= mount_smbfs
SUBDIR+= mptable
.if ${MK_NDIS} != "no"
SUBDIR+= ndiscvt
diff --git a/usr.sbin/Makefile.ia64 b/usr.sbin/Makefile.ia64
index d047b33..0a897b4 100644
--- a/usr.sbin/Makefile.ia64
+++ b/usr.sbin/Makefile.ia64
@@ -4,5 +4,6 @@
SUBDIR+= acpi
.endif
SUBDIR+= kgmon
+SUBDIR+= mount_smbfs
SUBDIR:= ${SUBDIR:Nuathload}
SUBDIR+= zzz
diff --git a/usr.sbin/Makefile.powerpc b/usr.sbin/Makefile.powerpc
index 131eb57..8833a27 100644
--- a/usr.sbin/Makefile.powerpc
+++ b/usr.sbin/Makefile.powerpc
@@ -1,4 +1,5 @@
# $FreeBSD$
+SUBDIR+= mount_smbfs
SUBDIR+= nvram
SUBDIR+= ofwdump
diff --git a/usr.sbin/Makefile.sparc64 b/usr.sbin/Makefile.sparc64
index 81f7a9b..632b3a8 100644
--- a/usr.sbin/Makefile.sparc64
+++ b/usr.sbin/Makefile.sparc64
@@ -1,4 +1,5 @@
# $FreeBSD$
SUBDIR+= eeprom
+SUBDIR+= mount_smbfs
SUBDIR+= ofwdump
diff --git a/usr.sbin/bhyve/atpic.c b/usr.sbin/bhyve/atpic.c
index a9fb084..5b4dd79 100644
--- a/usr.sbin/bhyve/atpic.c
+++ b/usr.sbin/bhyve/atpic.c
@@ -37,13 +37,6 @@ __FBSDID("$FreeBSD$");
#include "inout.h"
-/*
- * FreeBSD only writes to the 8259 interrupt controllers to put them in a
- * shutdown state.
- *
- * So, we just ignore the writes.
- */
-
#define IO_ICU1 0x20
#define IO_ICU2 0xA0
#define ICU_IMR_OFFSET 1
@@ -55,8 +48,14 @@ atpic_handler(struct vmctx *ctx, int vcpu, int in, int port, int bytes,
if (bytes != 1)
return (-1);
- if (in)
- return (-1);
+ if (in) {
+ if (port & ICU_IMR_OFFSET) {
+ /* all interrupts masked */
+ *eax = 0xff;
+ } else {
+ *eax = 0x00;
+ }
+ }
/* Pretend all writes to the 8259 are alright */
return (0);
diff --git a/usr.sbin/bhyve/bhyverun.c b/usr.sbin/bhyve/bhyverun.c
index 66883be..ae22de2 100644
--- a/usr.sbin/bhyve/bhyverun.c
+++ b/usr.sbin/bhyve/bhyverun.c
@@ -734,7 +734,13 @@ main(int argc, char *argv[])
init_mem();
init_inout();
- init_pci(ctx);
+
+ /*
+ * Exit if a device emulation finds an error in it's initilization
+ */
+ if (init_pci(ctx) != 0)
+ exit(1);
+
if (ioapic)
ioapic_init(0);
diff --git a/usr.sbin/bhyve/pci_emul.c b/usr.sbin/bhyve/pci_emul.c
index 37f1778..00adf62 100644
--- a/usr.sbin/bhyve/pci_emul.c
+++ b/usr.sbin/bhyve/pci_emul.c
@@ -47,13 +47,14 @@ __FBSDID("$FreeBSD$");
#include "bhyverun.h"
#include "inout.h"
#include "mem.h"
-#include "mptbl.h"
#include "pci_emul.h"
#include "ioapic.h"
#define CONF1_ADDR_PORT 0x0cf8
#define CONF1_DATA_PORT 0x0cfc
+#define CONF1_ENABLE 0x80000000ul
+
#define CFGWRITE(pi,off,val,b) \
do { \
if ((b) == 1) { \
@@ -139,20 +140,15 @@ pci_parse_slot(char *opt, int legacy)
error = -1;
str = cpy = strdup(opt);
- config = NULL;
-
- if (strchr(str, ':') != NULL) {
- slot = strsep(&str, ":");
- func = strsep(&str, ",");
- } else {
- slot = strsep(&str, ",");
- func = NULL;
- }
-
+ slot = strsep(&str, ",");
+ func = NULL;
+ if (strchr(slot, ':') != NULL) {
+ func = cpy;
+ (void) strsep(&func, ":");
+ }
+
emul = strsep(&str, ",");
- if (str != NULL) {
- config = strsep(&str, ",");
- }
+ config = str;
if (emul == NULL) {
pci_parse_slot_usage(opt);
@@ -666,11 +662,13 @@ pci_emul_finddev(char *name)
return (NULL);
}
-static void
+static int
pci_emul_init(struct vmctx *ctx, struct pci_devemu *pde, int slot, int func,
char *params)
{
struct pci_devinst *pdi;
+ int err;
+
pdi = malloc(sizeof(struct pci_devinst));
bzero(pdi, sizeof(*pdi));
@@ -688,12 +686,15 @@ pci_emul_init(struct vmctx *ctx, struct pci_devemu *pde, int slot, int func,
pci_set_cfgdata8(pdi, PCIR_COMMAND,
PCIM_CMD_PORTEN | PCIM_CMD_MEMEN | PCIM_CMD_BUSMASTEREN);
- if ((*pde->pe_init)(ctx, pdi, params) != 0) {
+ err = (*pde->pe_init)(ctx, pdi, params);
+ if (err != 0) {
free(pdi);
} else {
pci_emul_devices++;
pci_slotinfo[slot][func].si_devi = pdi;
- }
+ }
+
+ return (err);
}
void
@@ -993,7 +994,7 @@ pci_emul_fallback_handler(struct vmctx *ctx, int vcpu, int dir, uint64_t addr,
return (0);
}
-void
+int
init_pci(struct vmctx *ctx)
{
struct mem_range memp;
@@ -1013,8 +1014,10 @@ init_pci(struct vmctx *ctx)
if (si->si_name != NULL) {
pde = pci_emul_finddev(si->si_name);
assert(pde != NULL);
- pci_emul_init(ctx, pde, slot, func,
- si->si_param);
+ error = pci_emul_init(ctx, pde, slot, func,
+ si->si_param);
+ if (error)
+ return (error);
}
}
}
@@ -1051,6 +1054,8 @@ init_pci(struct vmctx *ctx)
error = register_mem_fallback(&memp);
assert(error == 0);
+
+ return (0);
}
int
@@ -1224,20 +1229,29 @@ pci_emul_cfgaddr(struct vmctx *ctx, int vcpu, int in, int port, int bytes,
{
uint32_t x;
- assert(!in);
-
- if (bytes != 4)
- return (-1);
+ if (bytes != 4) {
+ if (in)
+ *eax = (bytes == 2) ? 0xffff : 0xff;
+ return (0);
+ }
- x = *eax;
- cfgoff = x & PCI_REGMAX;
- cfgfunc = (x >> 8) & PCI_FUNCMAX;
- cfgslot = (x >> 11) & PCI_SLOTMAX;
- cfgbus = (x >> 16) & PCI_BUSMAX;
+ if (in) {
+ x = (cfgbus << 16) |
+ (cfgslot << 11) |
+ (cfgfunc << 8) |
+ cfgoff;
+ *eax = x | CONF1_ENABLE;
+ } else {
+ x = *eax;
+ cfgoff = x & PCI_REGMAX;
+ cfgfunc = (x >> 8) & PCI_FUNCMAX;
+ cfgslot = (x >> 11) & PCI_SLOTMAX;
+ cfgbus = (x >> 16) & PCI_BUSMAX;
+ }
return (0);
}
-INOUT_PORT(pci_cfgaddr, CONF1_ADDR_PORT, IOPORT_F_OUT, pci_emul_cfgaddr);
+INOUT_PORT(pci_cfgaddr, CONF1_ADDR_PORT, IOPORT_F_INOUT, pci_emul_cfgaddr);
static uint32_t
bits_changed(uint32_t old, uint32_t new, uint32_t mask)
diff --git a/usr.sbin/bhyve/pci_emul.h b/usr.sbin/bhyve/pci_emul.h
index 654b2f6..4e04a1e 100644
--- a/usr.sbin/bhyve/pci_emul.h
+++ b/usr.sbin/bhyve/pci_emul.h
@@ -183,7 +183,7 @@ struct pciecap {
uint16_t slot_status2;
} __packed;
-void init_pci(struct vmctx *ctx);
+int init_pci(struct vmctx *ctx);
void msicap_cfgwrite(struct pci_devinst *pi, int capoff, int offset,
int bytes, uint32_t val);
void msixcap_cfgwrite(struct pci_devinst *pi, int capoff, int offset,
diff --git a/usr.sbin/bhyve/pci_virtio_net.c b/usr.sbin/bhyve/pci_virtio_net.c
index 4c03463..19f9ffe 100644
--- a/usr.sbin/bhyve/pci_virtio_net.c
+++ b/usr.sbin/bhyve/pci_virtio_net.c
@@ -34,6 +34,7 @@ __FBSDID("$FreeBSD$");
#include <sys/select.h>
#include <sys/uio.h>
#include <sys/ioctl.h>
+#include <net/ethernet.h>
#include <errno.h>
#include <fcntl.h>
@@ -638,6 +639,30 @@ pci_vtnet_ring_init(struct pci_vtnet_softc *sc, uint64_t pfn)
}
static int
+pci_vtnet_parsemac(char *mac_str, uint8_t *mac_addr)
+{
+ struct ether_addr *ea;
+ char *tmpstr;
+ char zero_addr[ETHER_ADDR_LEN] = { 0, 0, 0, 0, 0, 0 };
+
+ tmpstr = strsep(&mac_str,"=");
+
+ if ((mac_str != NULL) && (!strcmp(tmpstr,"mac"))) {
+ ea = ether_aton(mac_str);
+
+ if (ea == NULL || ETHER_IS_MULTICAST(ea->octet) ||
+ memcmp(ea->octet, zero_addr, ETHER_ADDR_LEN) == 0) {
+ fprintf(stderr, "Invalid MAC %s\n", mac_str);
+ return (EINVAL);
+ } else
+ memcpy(mac_addr, ea->octet, ETHER_ADDR_LEN);
+ }
+
+ return (0);
+}
+
+
+static int
pci_vtnet_init(struct vmctx *ctx, struct pci_devinst *pi, char *opts)
{
MD5_CTX mdctx;
@@ -646,6 +671,9 @@ pci_vtnet_init(struct vmctx *ctx, struct pci_devinst *pi, char *opts)
char tname[MAXCOMLEN + 1];
struct pci_vtnet_softc *sc;
const char *env_msi;
+ char *devname;
+ char *vtopts;
+ int mac_provided;
sc = malloc(sizeof(struct pci_vtnet_softc));
memset(sc, 0, sizeof(struct pci_vtnet_softc));
@@ -664,14 +692,31 @@ pci_vtnet_init(struct vmctx *ctx, struct pci_devinst *pi, char *opts)
}
/*
- * Attempt to open the tap device
+ * Attempt to open the tap device and read the MAC address
+ * if specified
*/
+ mac_provided = 0;
sc->vsc_tapfd = -1;
if (opts != NULL) {
char tbuf[80];
+ int err;
+
+ devname = vtopts = strdup(opts);
+ (void) strsep(&vtopts, ",");
+
+ if (vtopts != NULL) {
+ err = pci_vtnet_parsemac(vtopts, sc->vsc_macaddr);
+ if (err != 0) {
+ free(devname);
+ return (err);
+ }
+ mac_provided = 1;
+ }
strcpy(tbuf, "/dev/");
- strlcat(tbuf, opts, sizeof(tbuf));
+ strlcat(tbuf, devname, sizeof(tbuf));
+
+ free(devname);
sc->vsc_tapfd = open(tbuf, O_RDWR);
if (sc->vsc_tapfd == -1) {
@@ -701,29 +746,25 @@ pci_vtnet_init(struct vmctx *ctx, struct pci_devinst *pi, char *opts)
}
/*
- * The MAC address is the standard NetApp OUI of 00-a0-98,
- * followed by an MD5 of the vm name. The slot/func number is
- * prepended to this for slots other than 1:0, so that
- * a bootloader can netboot from the equivalent of slot 1.
+ * The default MAC address is the standard NetApp OUI of 00-a0-98,
+ * followed by an MD5 of the PCI slot/func number and dev name
*/
- if (pi->pi_slot == 1 && pi->pi_func == 0) {
- strncpy(nstr, vmname, sizeof(nstr));
- } else {
+ if (!mac_provided) {
snprintf(nstr, sizeof(nstr), "%d-%d-%s", pi->pi_slot,
- pi->pi_func, vmname);
+ pi->pi_func, vmname);
+
+ MD5Init(&mdctx);
+ MD5Update(&mdctx, nstr, strlen(nstr));
+ MD5Final(digest, &mdctx);
+
+ sc->vsc_macaddr[0] = 0x00;
+ sc->vsc_macaddr[1] = 0xa0;
+ sc->vsc_macaddr[2] = 0x98;
+ sc->vsc_macaddr[3] = digest[0];
+ sc->vsc_macaddr[4] = digest[1];
+ sc->vsc_macaddr[5] = digest[2];
}
- MD5Init(&mdctx);
- MD5Update(&mdctx, nstr, strlen(nstr));
- MD5Final(digest, &mdctx);
-
- sc->vsc_macaddr[0] = 0x00;
- sc->vsc_macaddr[1] = 0xa0;
- sc->vsc_macaddr[2] = 0x98;
- sc->vsc_macaddr[3] = digest[0];
- sc->vsc_macaddr[4] = digest[1];
- sc->vsc_macaddr[5] = digest[2];
-
/* initialize config space */
pci_set_cfgdata16(pi, PCIR_DEVICE, VIRTIO_DEV_NET);
pci_set_cfgdata16(pi, PCIR_VENDOR, VIRTIO_VENDOR);
diff --git a/usr.sbin/bsdconfig/include/messages.subr b/usr.sbin/bsdconfig/include/messages.subr
index 8f486fc..39434ae 100644
--- a/usr.sbin/bsdconfig/include/messages.subr
+++ b/usr.sbin/bsdconfig/include/messages.subr
@@ -34,6 +34,8 @@ hline_arrows_tab_punc_enter="Use arrows, TAB, punctuation, ENTER"
hline_choose_help_for_more_information_on_media_types="Choose Help for more information on the various media types"
msg_accept_continue="Accept/Continue"
msg_accessibility_desc="Ports to help disabled users."
+msg_adding_package_as_a_dependency_from_media="Adding %s (as a dependency) from %s"
+msg_adding_package_from_media="Adding %s from %s"
msg_afterstep_desc="Ports to support the AfterStep window manager."
msg_all="All"
msg_all_desc="All available packages in all categories."
@@ -174,6 +176,7 @@ msg_invalid_ipv4_address="Invalid IPv4 address"
msg_invalid_name_server_ip_address_specified="Invalid name server IP address specified"
msg_invalid_netmask_value="Invalid netmask value"
msg_invalid_nfs_path_specification="Invalid NFS path specification. Must be of the form:\nhost:/full/pathname/to/FreeBSD/distdir"
+msg_io_error_while_reading_in_the_package="I/O error while reading in the %s package."
msg_io_or_format_error_on_index_file="I/O or format error on %s file.\nPlease verify media (or path to media) and try again."
msg_ipv4_address="IPv4 Address"
msg_ipv4_gateway="IPv4 Gateway"
@@ -196,6 +199,7 @@ msg_length_of_specified_url_is_too_long="Length of specified URL is %u character
msg_linux_desc="Linux programs that can run under binary compatibility."
msg_lisp_desc="Software related to the Lisp language."
msg_lithuania="Lithuania"
+msg_loading_of_dependent_package_failed="Loading of dependent package %s failed"
msg_located_index_now_reading_package_data_from_it="Located INDEX, now reading package data from it..."
msg_logging_in_to_user_at_host="Logging in to %s@%s.."
msg_looking_up_host="Looking up host %s"
@@ -238,6 +242,7 @@ msg_no_dos_primary_partitions_found="No DOS primary partitions found! This inst
msg_no_floppy_devices_found="No floppy devices found! Please check that your system's configuration\nis correct. For more information, consult the hardware guide in the Doc\nmenu."
msg_no_gateway_has_been_set="No gateway has been set. You will be unable to access hosts\nnot on your local network"
msg_no_network_devices="No network devices available!"
+msg_no_package_name_passed_in_package_variable="No package name passed in package variable"
msg_no_packages_were_selected_for_extraction="No packages were selected for extraction."
msg_no_such_file_or_directory="%s: %s: No such file or directory"
msg_no_usb_devices_found="No USB devices found (try Options/Re-scan Devices)"
@@ -250,7 +255,9 @@ msg_ok="OK"
msg_options="Options"
msg_options_editor="Options Editor"
msg_other="other"
+msg_package_read_successfully_waiting_for_pkg_add="Package %s read successfully - waiting for pkg_add(1)"
msg_package_temp="Package Temp"
+msg_package_was_added_successfully="Package %s was added successfully"
msg_packages="packages"
msg_page_of_npages="(Page %s of %s)"
msg_palm_desc="Software support for the Palm(tm) series."
@@ -258,6 +265,7 @@ msg_parallel_desc="Applications dealing with parallelism in computing."
msg_pear_desc="Software related to the Pear PHP framework."
msg_perl5_desc="Utilities/modules for the PERL5 language."
msg_permission_denied="%s: %s: Permission denied"
+msg_pkg_add_apparently_did_not_like_the_package="pkg_add(1) apparently did not like the %s package."
msg_plan9_desc="Software from the Plan9 operating system."
msg_please_check_the_url_and_try_again="No such directory: %s\nplease check the URL and try again.\n"
msg_please_enter_password="Please enter your password for sudo(8):"
@@ -301,6 +309,7 @@ msg_quick_start_how_to_use_this_menu_system="Quick start - How to use this menu
msg_reinstall="Reinstall"
msg_reinstall_desc="Mark this package for reinstall"
msg_release_name="Release Name"
+msg_required_package_not_found="Warning: %s is a required package but was not found."
msg_rerun_bsdconfig_initial_device_probe="Re-run bsdconfig initial device probe"
msg_rescan_devices="Re-scan Devices"
msg_reset="RESET!"
@@ -334,6 +343,7 @@ msg_slovenia="Slovenia"
msg_snapshots_server_japan="Snapshots Server Japan"
msg_snapshots_server_sweden="Snapshots Server Sweden"
msg_sorry_invalid_url="Sorry, %s is an invalid URL!"
+msg_sorry_package_was_not_found_in_the_index="Sorry, package %s was not found in the INDEX."
msg_sorry_try_again="Sorry, try again."
msg_south_africa="South Africa"
msg_spain="Spain"
@@ -358,8 +368,10 @@ msg_uk="UK"
msg_ukraine="Ukraine"
msg_ukrainian_desc="Ported software for the Ukrainian market."
msg_unable_to_configure_device="Unable to configure the %s interface!\nThis installation method cannot be used."
+msg_unable_to_fetch_package_from_selected_media="Unable to fetch package %s from selected media.\nNo package add will be done."
msg_unable_to_get_file_from_selected_media="Unable to get %s file from selected media.\n\nThis may be because the packages collection is not available\non the distribution media you've chosen, most likely an FTP site\nwithout the packages collection mirrored. Please verify that\nyour media, or your path to the media, is correct and try again."
msg_unable_to_get_proper_ftp_path="Unable to get proper FTP path. FTP media not initialized."
+msg_unable_to_initialize_media_type_for_package_extract="Unable to initialize media type for package extract."
msg_unable_to_make_directory_mountpoint="Unable to make %s directory mountpoint for %s!"
msg_unable_to_open="Unable to open %s"
msg_uninstall="Uninstall"
diff --git a/usr.sbin/bsdconfig/networking/share/Makefile b/usr.sbin/bsdconfig/networking/share/Makefile
index 6bd594c..cf1119d 100644
--- a/usr.sbin/bsdconfig/networking/share/Makefile
+++ b/usr.sbin/bsdconfig/networking/share/Makefile
@@ -4,7 +4,7 @@ NO_OBJ=
FILESDIR= ${SHAREDIR}/bsdconfig/networking
FILES= common.subr device.subr hostname.subr ipaddr.subr media.subr \
- netmask.subr resolv.subr routing.subr
+ netmask.subr resolv.subr routing.subr services.subr
beforeinstall:
mkdir -p ${DESTDIR}${FILESDIR}
diff --git a/usr.sbin/bsdconfig/networking/share/services.subr b/usr.sbin/bsdconfig/networking/share/services.subr
new file mode 100644
index 0000000..ae7bce3
--- /dev/null
+++ b/usr.sbin/bsdconfig/networking/share/services.subr
@@ -0,0 +1,54 @@
+if [ ! "$_NETWORKING_SERVICES_SUBR" ]; then _NETWORKING_SERVICES_SUBR=1
+#
+# Copyright (c) 2013 Devin Teske
+# All Rights Reserved.
+#
+# Redistribution and use in source and binary forms, with or without
+# modification, are permitted provided that the following conditions
+# are met:
+# 1. Redistributions of source code must retain the above copyright
+# notice, this list of conditions and the following disclaimer.
+# 2. Redistributions in binary form must reproduce the above copyright
+# notice, this list of conditions and the following disclaimer in the
+# documentation and/or other materials provided with the distribution.
+#
+# THIS SOFTWARE IS PROVIDED BY THE AUTHOR AND CONTRIBUTORS ``AS IS'' AND
+# ANY EXPRESS OR IMPLIED WARRANTIES, INCLUDING BUT NOT LIMITED TO, THE
+# IMPLIED WARRANTIES OF MERCHANTABILITY AND FITNESS FOR A PARTICULAR PURPOSE
+# ARE DISCLAIMED. IN NO EVENT SHALL THE AUTHOR OR CONTRIBUTORS BE LIABLE
+# FOR ANY DIRECT, INDIRECT, INCIDENTAL, SPECIAL, EXEMPLARY, OR CONSEQUENTIAL
+# DAMAGES (INLUDING, BUT NOT LIMITED TO, PROCUREMENT OF SUBSTITUTE GOODS
+# OR SERVICES; LOSS OF USE, DATA, OR PROFITS; OR BUSINESS INTERRUPTION)
+# HOWEVER CAUSED AND ON ANY THEORY OF LIABILITY, WHETHER IN CONTRACT, STRICT
+# LIABILITY, OR TORT (INCLUDING NEGLIGENCE OR OTHERWISE) ARISING IN ANY WAY
+# OUT OF THE USE OF THIS SOFTWARE, EVEN IF ADVISED OF THE POSSIBILITY OF
+# SUCH DAMAGE.
+#
+# $FreeBSD$
+#
+############################################################ INCLUDES
+
+BSDCFG_SHARE="/usr/share/bsdconfig"
+. $BSDCFG_SHARE/common.subr || exit 1
+f_dprintf "%s: loading includes..." networking/services.subr
+f_include $BSDCFG_SHARE/packages/packages.subr
+f_include $BSDCFG_SHARE/sysrc.subr
+
+############################################################ FUNCTIONS
+
+# f_config_pcnfsd
+#
+# Load pcnfsd package and adjust mountd_flags in rc.conf(5).
+#
+f_config_pcnfsd()
+{
+ f_package_add "pcnfsd" || return $?
+ f_sysrc_set mountd_flags "-n"
+ return $SUCCESS
+}
+
+############################################################ MAIN
+
+f_dprintf "%s: Successfully loaded." networking/services.subr
+
+fi # ! $_NETWORKING_SERVICES_SUBR
diff --git a/usr.sbin/bsdconfig/share/media/cdrom.subr b/usr.sbin/bsdconfig/share/media/cdrom.subr
index 6f7afcd..4350367 100644
--- a/usr.sbin/bsdconfig/share/media/cdrom.subr
+++ b/usr.sbin/bsdconfig/share/media/cdrom.subr
@@ -146,8 +146,8 @@ f_media_init_cdrom()
# f_media_get_cdrom $device $file [$probe_only]
#
-# Returns data from $file on a mounted CDROM device. Similar to cat(1).
-# $probe_only is currently unused by this media type.
+# Returns data from $file on a mounted CDROM device. Similar to cat(1). If
+# $probe_only is present and non-NULL, returns success if $file exists.
#
f_media_get_cdrom()
{
@@ -156,7 +156,7 @@ f_media_get_cdrom()
f_dprintf "f_media_get_cdrom: dev=[%s] file=[%s] probe_only=%s" \
"$dev" "$file" "$probe_only"
- f_media_generic_get "$MOUNTPOINT" "$file"
+ f_media_generic_get "$MOUNTPOINT" "$file" "$probe_only"
}
# f_media_shutdown_cdrom $device
diff --git a/usr.sbin/bsdconfig/share/media/common.subr b/usr.sbin/bsdconfig/share/media/common.subr
index 581e501..4550d70 100644
--- a/usr.sbin/bsdconfig/share/media/common.subr
+++ b/usr.sbin/bsdconfig/share/media/common.subr
@@ -83,16 +83,18 @@ f_media_verify()
f_struct device_media || f_media_get_type
}
-# f_media_generic_get $base $file
+# f_media_generic_get $base $file [$probe_only]
#
-# A generic open which follows a well-known "path" of places to look.
+# A generic open which follows a well-known "path" of places to look. If
+# $probe_only is present and non-NULL, returns success if $file exists.
#
f_media_generic_get()
{
- local base="$1" file="$2"
+ local base="$1" file="$2" probe_only="$3"
local fname=f_media_generic_get
- f_dprintf "%s: base=[%s] files=[%s]" $fname "$base" "$file"
+ f_dprintf "%s: base=[%s] files=[%s] probe_only=%s" \
+ $fname "$base" "$file" "$probe_only"
local rel path
f_getvar $VAR_RELNAME rel
@@ -104,10 +106,19 @@ f_media_generic_get()
; do
if [ -f "$path" -a -r "$path" ]; then
f_dprintf "%s: file exists path=[%s]" $fname "$path"
+ [ "$probe_only" ] && return $SUCCESS
cat "$path"
return
fi
done
+
+ path="$base/releases/$rel/$file" # Final path to try
+ if [ -f "$path" -a -r "$path" ]; then
+ f_dprintf "%s: file exists path=[%s]" $fname "$path"
+ [ "$probe_only" ] && return $SUCCESS
+ elif [ "$probe_only" ]; then
+ return $FAILURE
+ fi
cat "$base/releases/$rel/$file" # Final path to try
}
diff --git a/usr.sbin/bsdconfig/share/media/directory.subr b/usr.sbin/bsdconfig/share/media/directory.subr
index cd0fd41..82c1b8c 100644
--- a/usr.sbin/bsdconfig/share/media/directory.subr
+++ b/usr.sbin/bsdconfig/share/media/directory.subr
@@ -69,6 +69,7 @@ f_media_set_directory()
[ "$path" ] || return $FAILURE
f_struct_new DEVICE device_directory
+ device_directory set name "$path"
device_directory set get f_media_get_directory
device_directory set init f_media_init_directory
device_directory set shutdown f_media_shutdown_directory
@@ -117,7 +118,8 @@ f_media_init_directory()
# f_media_get_directory $device $file [$probe_only]
#
# Returns data from $file in the existing/current filesystem. Similar to
-# cat(1). $probe_only is currently unused by this media type.
+# cat(1). If $probe_only is present and non-NULL, returns success if $file
+# exists.
#
f_media_get_directory()
{
@@ -127,7 +129,7 @@ f_media_get_directory()
"$dev" "$file" "$probe_only"
device_$dev get private path
- f_media_generic_get "$path" "$file"
+ f_media_generic_get "$path" "$file" "$probe_only"
}
# f_media_shutdown_directory $device
diff --git a/usr.sbin/bsdconfig/share/media/dos.subr b/usr.sbin/bsdconfig/share/media/dos.subr
index ab15097..9320c2c 100644
--- a/usr.sbin/bsdconfig/share/media/dos.subr
+++ b/usr.sbin/bsdconfig/share/media/dos.subr
@@ -125,7 +125,7 @@ f_media_init_dos()
# f_media_get_dos $device $file [$probe_only]
#
# Returns data from $file on a mounted DOS partition device. Similar to cat(1).
-# $probe_only is currently unused by this media type.
+# If $probe_only is present and non-NULL, returns success if $file exists.
#
f_media_get_dos()
{
@@ -134,7 +134,7 @@ f_media_get_dos()
f_dprintf "f_media_get_dos: dev=[%s] file=[%s] probe_only=%s" \
"$dev" "$file" "$probe_only"
- f_media_generic_get "$MOUNTPOINT" "$file"
+ f_media_generic_get "$MOUNTPOINT" "$file" "$probe_only"
}
# f_media_shutdown_dos $device
diff --git a/usr.sbin/bsdconfig/share/media/floppy.subr b/usr.sbin/bsdconfig/share/media/floppy.subr
index bb78518..beb574e 100644
--- a/usr.sbin/bsdconfig/share/media/floppy.subr
+++ b/usr.sbin/bsdconfig/share/media/floppy.subr
@@ -178,6 +178,8 @@ f_media_get_floppy()
f_media_init_floppy "$dev" || return $FAILURE
nretries=$(( $nretries - 1 ))
done
+ elif [ "$probe_only" ]; then
+ return $SUCCESS
fi
cat "$fp"
}
diff --git a/usr.sbin/bsdconfig/share/media/ftp.subr b/usr.sbin/bsdconfig/share/media/ftp.subr
index c2c7e6a..fb96b91 100644
--- a/usr.sbin/bsdconfig/share/media/ftp.subr
+++ b/usr.sbin/bsdconfig/share/media/ftp.subr
@@ -792,8 +792,8 @@ f_media_init_ftp()
#
# Returns data from $file on an FTP server using ftp(1). Please note that
# $device is unused but must be present (even if null). Information is instead
-# gathered from the environment. $probe_only is currently unused by this media
-# type.
+# gathered from the environment. If $probe_only is present and non-NULL,
+# returns success if $file exists.
#
# Variables from variable.subr used to configure the connection are as follows
# (all of which are configured by f_media_set_ftp above):
@@ -900,6 +900,17 @@ f_media_get_ftp()
f_dprintf "sending ftp request for: %s" "ftp://$host$port/$dir/$file"
+ if [ "$probe_only" ]; then
+ local url="ftp://$userpass$host$port/$dir/$file"
+ [ "$use_anon" ] && url="ftp://$host$port/$dir/$file"
+ if ! size=$( fetch -s "$url" 2>&1 ) || ! f_isinteger "$size"
+ then
+ f_dprintf "request failed! size response=[%s]" "$size"
+ return $FAILURE
+ fi
+ return $SUCCESS
+ fi
+
eval FTPMODE=\"\$mode\" ${use_anon:+FTPANONPASS=\"\$pass\"} \
ftp -V ${use_anon:+-a} -o - \
\"ftp://\$userpass\$host\$port/\$dir/\$file\" 2> /dev/null
diff --git a/usr.sbin/bsdconfig/share/media/nfs.subr b/usr.sbin/bsdconfig/share/media/nfs.subr
index b1dad4f..86bdde2 100644
--- a/usr.sbin/bsdconfig/share/media/nfs.subr
+++ b/usr.sbin/bsdconfig/share/media/nfs.subr
@@ -210,8 +210,8 @@ f_media_init_nfs()
# f_media_get_nfs $device $file [$probe_only]
#
-# Returns data from $file on a mounted NFS device. Similar to cat(1).
-# $probe_only is currently unused by this media type.
+# Returns data from $file on a mounted NFS device. Similar to cat(1). If
+# $probe_only is present and non-NULL, returns success if $file exists.
#
f_media_get_nfs()
{
@@ -220,7 +220,7 @@ f_media_get_nfs()
f_dprintf "f_media_get_nfs: dev=[%s] file=[%s] probe_only=%s" \
"$dev" "$file" "$probe_only"
- f_media_generic_get "$MOUNTPOINT" "$file"
+ f_media_generic_get "$MOUNTPOINT" "$file" "$probe_only"
}
# f_media_shutdown_nfs $device
diff --git a/usr.sbin/bsdconfig/share/media/ufs.subr b/usr.sbin/bsdconfig/share/media/ufs.subr
index 5a4113e..14e2081 100644
--- a/usr.sbin/bsdconfig/share/media/ufs.subr
+++ b/usr.sbin/bsdconfig/share/media/ufs.subr
@@ -155,7 +155,7 @@ f_media_init_ufs()
# f_media_get_ufs $device $file [$probe_only]
#
# Returns data from $file on a mounted UFS partition device. Similar to cat(1).
-# $probe_only is currently unused by this media type.
+# If $probe_only is present and non-NULL, returns success if $file exists.
#
f_media_get_ufs()
{
@@ -164,7 +164,7 @@ f_media_get_ufs()
f_dprintf "f_media_get_ufs: dev=[%s] file=[%s] probe_only=%s" \
"$dev" "$file" "$probe_only"
- f_media_generic_get "$MOUNTPOINT" "$file"
+ f_media_generic_get "$MOUNTPOINT" "$file" "$probe_only"
}
# f_media_shutdown_ufs $device
diff --git a/usr.sbin/bsdconfig/share/media/usb.subr b/usr.sbin/bsdconfig/share/media/usb.subr
index 4117e51..c2ece47 100644
--- a/usr.sbin/bsdconfig/share/media/usb.subr
+++ b/usr.sbin/bsdconfig/share/media/usb.subr
@@ -135,7 +135,7 @@ f_media_init_usb()
# f_media_get_usb $device $file [$probe_only]
#
# Returns data from $file on a mounted USB disk device. Similar to cat(1).
-# $probe_only is currently unused by this media type.
+# If $probe_only is present and non-NULL, returns success if $file exists.
#
f_media_get_usb()
{
@@ -144,7 +144,7 @@ f_media_get_usb()
f_dprintf "f_media_get_usb: dev=[%s] file=[%s] probe_only=%s" \
"$dev" "$file" "$probe_only"
- f_media_generic_get "$MOUNTPOINT" "$file"
+ f_media_generic_get "$MOUNTPOINT" "$file" "$probe_only"
}
# f_media_shutdown_usb $device
diff --git a/usr.sbin/bsdconfig/share/packages/index.subr b/usr.sbin/bsdconfig/share/packages/index.subr
index 7525ae6..bc26f50 100755
--- a/usr.sbin/bsdconfig/share/packages/index.subr
+++ b/usr.sbin/bsdconfig/share/packages/index.subr
@@ -207,7 +207,8 @@ f_index_read()
# f_index_extract_pages $var_to_get $var_basename $pagesize [$category]
#
-# Extracts the package INDEX into a series of sequential variables
+# Extracts the package INDEX ($PACKAGE_INDEX by default if/when $var_to_get is
+# NULL; but should not be missing) into a series of sequential variables
# corresponding to "pages" containing up to $pagesize packages. The package
# INDEX data must be contained in the variable $var_to_get. The extracted pages
# are stored in variables ${var_basename}_# -- where "#" is a the page number.
@@ -217,7 +218,7 @@ f_index_read()
#
f_index_extract_pages()
{
- local var_to_get="$1" var_basename="$2" pagesize="$3"
+ local var_to_get="${1:-PACKAGE_INDEX}" var_basename="$2" pagesize="$3"
local category="$4" # Optional
eval "$(
@@ -241,6 +242,44 @@ f_index_extract_pages()
)"
}
+# f_index_search $var_to_get $name [$var_to_set]
+#
+# Search the package INDEX ($PACKAGE_INDEX by default if/when $var_to_get is
+# NULL; but should not be missing) for $name, returning the first match.
+# Matches are strict (not regular expressions) and must match the beginning
+# portion of the package name to be considered a match. If $var_to_set is
+# missing or NULL, output is sent to standard output. If a match is found,
+# returns success; otherwise failure.
+#
+f_index_search()
+{
+ local __var_to_get="${1:-PACKAGE_INDEX}" __pkg_basename="$2"
+ local __var_to_set="$3"
+
+ f_dprintf "f_index_search: Searching package data (in %s) for %s" \
+ "$__var_to_get" "$__pkg_basename"
+
+ local __pkg=
+ __pkg=$( debug= f_getvar "$__var_to_get" |
+ awk -F'|' -v basename="$__pkg_basename" '
+ BEGIN { n = length(basename) }
+ substr($1, 0, n) == basename { print $1; exit }
+ ' )
+ if [ ! "$__pkg" ]; then
+ f_dprintf "f_index_search: No packages matching %s found" \
+ "$__pkg_basename"
+ return $FAILURE
+ fi
+
+ f_dprintf "f_index_search: Found package %s" "$__pkg"
+ if [ "$__var_to_set" ]; then
+ setvar "$__var_to_set" "$__pkg"
+ else
+ echo "$__pkg"
+ fi
+ return $SUCCESS
+}
+
############################################################ MAIN
f_dprintf "%s: Successfully loaded." packages/index.subr
diff --git a/usr.sbin/bsdconfig/share/packages/packages.subr b/usr.sbin/bsdconfig/share/packages/packages.subr
index 388fd24..1a9bdf9 100755
--- a/usr.sbin/bsdconfig/share/packages/packages.subr
+++ b/usr.sbin/bsdconfig/share/packages/packages.subr
@@ -32,6 +32,8 @@ BSDCFG_SHARE="/usr/share/bsdconfig"
. $BSDCFG_SHARE/common.subr || exit 1
f_dprintf "%s: loading includes..." "$0"
f_include $BSDCFG_SHARE/dialog.subr
+f_include $BSDCFG_SHARE/device.subr
+f_include $BSDCFG_SHARE/media/common.subr
f_include $BSDCFG_SHARE/packages/categories.subr
f_include $BSDCFG_SHARE/packages/index.subr
f_include $BSDCFG_SHARE/strings.subr
@@ -48,8 +50,17 @@ f_include_lang $BSDCFG_LIBE/include/messages.subr
############################################################ GLOBALS
-PACKAGE_CATEGORIES=
-SELECTED_PACKAGES=
+#
+# Package extensions to try
+#
+PACKAGE_EXTENSIONS=".tbz .tbz2 .tgz"
+
+#
+# Variables used to track runtime states
+#
+PACKAGES_DETECTED= # Boolean (NULL/non-NULL); detected installed packages?
+PACKAGE_CATEGORIES= # List of package categories parsed from INDEX
+SELECTED_PACKAGES= # Packages selected by user in [X]dialog(1) interface
#
# Options
@@ -157,6 +168,15 @@ f_package_deselect()
f_package_detect_installed()
{
local installed package varpkg
+ #
+ # XXX KLUDGE ALERT! This makes evil assumptions about how XXX
+ # packages register themselves and should *really* be done with
+ # `pkg_info -e <name>' except that this is too slow for an
+ # item check routine.. :-(
+ #
+ # NOTE: When transitioning to pkgng, make a single fork to `pkg' to
+ # produce a list of all installed packages and parse _that_
+ #
installed=$( find -s /var/db/pkg -mindepth 1 -maxdepth 1 -type d |
sed -e 's:/var/db/pkg/::' )
for package in $installed; do
@@ -608,25 +628,16 @@ f_package_review()
#
# Process each of the selected packages:
- # + First, process dependencies.
- # + Second, process packages marked for Install.
- # + Third, process packages marked for Re-install.
+ # + First, process packages marked for Install.
+ # + Second, process packages marked for Re-install.
# + Finally, process packages marked for Uninstall.
#
for package in $SELECTED_PACKAGES; do
mark=
f_str2varname "$package" varpkg
f_getvar _mark_$varpkg mark
- [ "$mark" = "D" ] || continue
- # XXX Install dependency
- f_package_deselect "$package"
- done
- for package in $SELECTED_PACKAGES; do
- mark=
- f_str2varname "$package" varpkg
- f_getvar _mark_$varpkg mark
[ "$mark" = "I" ] || continue
- # XXX Install package
+ f_package_add "$package" || continue
f_package_deselect "$package"
done
for package in $SELECTED_PACKAGES; do
@@ -646,9 +657,6 @@ f_package_review()
f_package_deselect "$package"
done
- # XXX
- f_show_msg "Coming soon..."
-
return $SUCCESS
}
@@ -812,6 +820,236 @@ f_package_config()
done
}
+# f_package_add $package_name [$depended]
+#
+# Like f_package_extract(), but assumes current media device and chases deps.
+# Note that $package_name should not contain the archive suffix (e.g., `.tbz').
+# If $depended is present and non-NULL, the package is treated as a dependency
+# (in this function, dependencies are not handled any differently, but the
+# f_package_extract() function is passed this value and it displays a different
+# message when installing a dependency versus non-dependency).
+#
+f_package_add()
+{
+ local name="$1" depended="$2" status=$SUCCESS retval
+
+ local alert=f_show_msg no_confirm=
+ f_getvar $VAR_NO_CONFIRM no_confirm
+ [ "$no_confirm" ] && alert=f_show_info
+
+ if ! { [ "$name" ] || { f_getvar $VAR_PACKAGE name && [ "$name" ]; }; }
+ then
+ f_dprintf "packageAdd: %s" \
+ "$msg_no_package_name_passed_in_package_variable"
+ return $FAILURE
+ fi
+
+ { # Verify and initialize device media if-defined
+ f_media_verify &&
+ f_device_init media &&
+ f_index_initialize packages/INDEX
+ } || return $FAILURE
+
+ # Now we have (indirectly via f_index_read()):
+ # CATEGORY_MENU_LIST _categories_{varpkg} _rundeps_{varpkg}
+ # PACKAGE_CATEGORIES _npkgs
+
+ local varpkg
+ f_str2varname "$name" varpkg
+
+ # Just as-in the user-interface (opposed to scripted-use), only allow
+ # packages with at least one category to be recognized.
+ #
+ local pkgcat=
+ if ! f_getvar _categories_$varpkg pkgcat || [ ! "$pkgcat" ]; then
+ # $pkg may be a partial name, search the index (this is slow)
+ f_index_search PACKAGE_INDEX $name name
+ if [ ! "$name" ]; then
+ f_show_msg \
+ "$msg_sorry_package_was_not_found_in_the_index" \
+ "$name"
+ return $FAILURE
+ fi
+ f_str2varname "$name" varpkg
+ fi
+
+ # If invoked through the scripted interface, we likely have not yet
+ # detected the installed packages -- something we should do only once.
+ #
+ if [ ! "$PACKAGES_DETECTED" ]; then
+ f_package_detect_installed
+ export PACKAGES_DETECTED=1 # exported for awk(1) ENVIRON[]
+ fi
+ # Now we have: _mark_{varpkg}=X for all installed packages
+
+ #
+ # Since we're maintaining data structures for installed packages,
+ # short-circuit the package dependency checks if the package is already
+ # installed. This prevents wasted cycles, minor delays between package
+ # extractions, and worst-case an infinite loop with a certain faulty
+ # INDEX file.
+ #
+ local mark=
+ f_getvar _mark_$varpkg mark && [ "$mark" = "X" ] && return $SUCCESS
+
+ local dep vardep rundeps=
+ f_getvar _rundeps_$varpkg rundeps
+ for dep in $rundeps; do
+ f_str2varname "$dep" vardep
+
+ # Skip dependency if already installed
+ mark=
+ f_getvar _mark_$vardep mark && [ "$mark" = "X" ] && continue
+
+ # Just as-in the user-interface (opposed to scripted-use), only
+ # allow packages with at least one category to be recognized.
+ #
+ local depcat=
+ if ! f_getvar _categories_$vardep depcat || [ ! "$depcat" ]
+ then
+ $alert "$msg_required_package_not_found" "$dep"
+ [ "$no_confirm" ] && sleep 2
+ fi
+
+ f_package_add "$dep"
+ retval=$?
+ if [ $retval -ne $SUCCESS ]; then
+ status=$(( $status | $retval ))
+
+ # XXX package could be on a future disc volume
+ # XXX (not supporting multiple disc volumes yet)
+
+ $alert "$msg_loading_of_dependent_package_failed" \
+ "$dep"
+ [ "$no_confirm" ] && sleep 2
+ fi
+ done
+ [ $status -eq $SUCCESS ] || return $status
+
+ #
+ # Done with the deps? Try to load the real m'coy.
+ #
+
+ f_package_extract media "$name" "$depended"
+ retval=$?
+ if [ $retval -ne $SUCCESS ]; then
+ status=$(( $status | $retval ))
+ else
+ setvar _mark_$varpkg X
+ fi
+
+ return $status
+}
+
+# f_package_extract $device $name [$depended]
+#
+# Extract a package based on a namespec and media device. If $depended is
+# present and non-NULL, the notification displayed while installing the package
+# has "as a dependency" appended.
+#
+f_package_extract()
+{
+ local device="$1" name="$2" depended="$3"
+
+ # Check to make sure it's not already there
+ local varpkg mark=
+ f_str2varname "$name" varpkg
+ f_getvar _mark_$varpkg mark
+ [ "$mark" = "X" ] && return $SUCCESS
+
+ if ! f_device_init $device; then
+ f_show_msg \
+ "$msg_unable_to_initialize_media_type_for_package_extract"
+ return $FAILURE
+ fi
+
+ # If necessary, initialize the ldconfig hints
+ [ -f "/var/run/ld-elf.so.hints" ] ||
+ f_quietly ldconfig /usr/lib /usr/lib/compat /usr/local/lib
+
+ # Make a couple paranoid locations for temp
+ # files to live if user specified none
+ local tmpdir
+ f_getvar $VAR_PKG_TMPDIR:-/var/tmp tmpdir
+ f_quietly mkdir -p -m 1777 "$tmpdir"
+
+ local path
+ case "$name" in
+ */*) path="$name" ;;
+ *)
+ case "$name" in
+ *-*|*_*) path="packages/All/$name" ;;
+ *) path="packages/Latest/$name"
+ esac
+ esac
+
+ local fname=f_package_extract
+
+ # We have a path, call the device strategy routine to get the file
+ local pkg_ext probe_only=1 found=
+ for pkg_ext in "" $PACKAGE_EXTENSIONS; do
+ if f_device_get $device "$path$pkg_ext" $probe_only; then
+ path="$path$pkg_ext"
+ f_dprintf "%s: found path=[%s] dev=[%s]" \
+ $fname "$path" "$device"
+ found=1
+ break
+ fi
+ done
+
+ local alert=f_show_msg no_confirm=
+ f_getvar $VAR_NO_CONFIRM no_confirm
+ [ "$no_confirm" ] && alert=f_show_info
+
+ if [ ! "$found" ]; then
+ f_dprintf "%s: No such %s file on %s device" \
+ $fname "$path" "$device"
+ $alert "$msg_unable_to_fetch_package_from_selected_media" \
+ "$name"
+ [ "$no_confirm" ] && sleep 2
+ return $FAILURE
+ fi
+
+ local devname=
+ f_struct device_$device get name devname
+ if [ "$depended" ]; then
+ f_show_info "$msg_adding_package_as_a_dependency_from_media" \
+ "$name" "$devname"
+ else
+ f_show_info "$msg_adding_package_from_media" "$name" "$devname"
+ fi
+
+ # Get package data and pipe into pkg_add(1) while providing feedback
+ {
+ if ! f_device_get $device "$path"; then
+ $alert "$msg_io_error_while_reading_in_the_package" \
+ "$name" \
+ >&$DIALOG_TERMINAL_PASSTHRU_FD 2> /dev/null
+ [ "$no_confirm" ] && sleep 2
+ else
+ f_show_info \
+ "$msg_package_read_successfully_waiting_for_pkg_add" \
+ "$name" >&$DIALOG_TERMINAL_PASSTHRU_FD 2> /dev/null
+ fi
+ } | {
+ if f_debugging; then
+ /usr/sbin/pkg_add -v -
+ else
+ f_quietly /usr/sbin/pkg_add -
+ fi
+ }
+ if [ $? -ne $SUCCESS ]; then
+ $alert "$msg_pkg_add_apparently_did_not_like_the_package" \
+ "$name"
+ [ "$no_confirm" ] && sleep 2
+ else
+ f_show_info "$msg_package_was_added_successfully" "$name"
+ sleep 1
+ fi
+
+ return $SUCCESS
+}
+
############################################################ MAIN
f_dprintf "%s: Successfully loaded." packages/packages.subr
diff --git a/usr.sbin/bsdconfig/share/script.subr b/usr.sbin/bsdconfig/share/script.subr
index 8b8c8e2..5574863 100644
--- a/usr.sbin/bsdconfig/share/script.subr
+++ b/usr.sbin/bsdconfig/share/script.subr
@@ -34,6 +34,8 @@ f_dprintf "%s: loading includes..." script.subr
f_include $BSDCFG_SHARE/device.subr
f_include $BSDCFG_SHARE/media/any.subr
f_include $BSDCFG_SHARE/media/tcpip.subr
+f_include $BSDCFG_SHARE/mustberoot.subr
+f_include $BSDCFG_SHARE/networking/services.subr
f_include $BSDCFG_SHARE/packages/packages.subr
f_include $BSDCFG_SHARE/variable.subr
@@ -187,8 +189,12 @@ f_resword_new mediaSetHTTP f_media_set_http
# media/httpproxy.subr
f_resword_new mediaSetHTTPProxy f_media_set_http_proxy
+# networking/services.subr
+f_resword_new configPCNFSD f_config_pcnfsd
+
# packages/packages.subr
f_resword_new configPackages f_package_config
+f_resword_new packageAdd f_package_add
# variable.subr
f_resword_new installVarDefaults f_variable_set_defaults
diff --git a/usr.sbin/bsdconfig/share/variable.subr b/usr.sbin/bsdconfig/share/variable.subr
index e841029..27010f9 100644
--- a/usr.sbin/bsdconfig/share/variable.subr
+++ b/usr.sbin/bsdconfig/share/variable.subr
@@ -106,6 +106,8 @@ f_variable_get_value()
#
f_variable_set_defaults()
{
+ f_dprintf "f_variable_set_defaults: Initializing defaults..."
+
#
# Initialize various user-edittable values to their defaults
#
@@ -120,6 +122,27 @@ f_variable_set_defaults()
setvar $VAR_PKG_TMPDIR "/var/tmp"
setvar $VAR_RELNAME "$UNAME_R"
+ #
+ # Debugging
+ #
+ if f_debugging; then
+ local var
+ for var in \
+ $VAR_EDITOR \
+ $VAR_FTP_STATE \
+ $VAR_FTP_USER \
+ $VAR_HOSTNAME \
+ $VAR_MEDIA_TIMEOUT \
+ $VAR_NFS_SECURE \
+ $VAR_NFS_TCP \
+ $VAR_NFS_V3 \
+ $VAR_PKG_TMPDIR \
+ $VAR_RELNAME \
+ ; do
+ f_quietly f_getvar $var
+ done
+ fi
+
f_dprintf "f_variable_set_defaults: Defaults initialized."
}
diff --git a/usr.sbin/bsnmpd/modules/snmp_hast/BEGEMOT-HAST-MIB.txt b/usr.sbin/bsnmpd/modules/snmp_hast/BEGEMOT-HAST-MIB.txt
index df64c497..6e20bdd 100644
--- a/usr.sbin/bsnmpd/modules/snmp_hast/BEGEMOT-HAST-MIB.txt
+++ b/usr.sbin/bsnmpd/modules/snmp_hast/BEGEMOT-HAST-MIB.txt
@@ -57,6 +57,9 @@ begemotHast MODULE-IDENTITY
REVISION "201304130000Z"
DESCRIPTION
"Initial revision."
+ REVISION "201307010000Z"
+ DESCRIPTION
+ "Added hastResourceWorkerPid."
::= { begemot 220 }
begemotHastObjects OBJECT IDENTIFIER ::= { begemotHast 1 }
@@ -116,7 +119,8 @@ HastResourceEntry ::= SEQUENCE {
hastResourceReadErrors Counter64,
hastResourceWriteErrors Counter64,
hastResourceDeleteErrors Counter64,
- hastResourceFlushErrors Counter64
+ hastResourceFlushErrors Counter64,
+ hastResourceWorkerPid INTEGER
}
hastResourceIndex OBJECT-TYPE
@@ -295,4 +299,12 @@ hastResourceFlushErrors OBJECT-TYPE
"Count of resource local flush operations that failed."
::= { hastResourceEntry 21 }
+hastResourceWorkerPid OBJECT-TYPE
+ SYNTAX INTEGER
+ MAX-ACCESS read-only
+ STATUS current
+ DESCRIPTION
+ "Worker process ID."
+ ::= { hastResourceEntry 22 }
+
END
diff --git a/usr.sbin/bsnmpd/modules/snmp_hast/hast_snmp.c b/usr.sbin/bsnmpd/modules/snmp_hast/hast_snmp.c
index c5abd64..ef8832a 100644
--- a/usr.sbin/bsnmpd/modules/snmp_hast/hast_snmp.c
+++ b/usr.sbin/bsnmpd/modules/snmp_hast/hast_snmp.c
@@ -78,6 +78,7 @@ struct hast_snmp_resource {
uint64_t write_errors;
uint64_t delete_errors;
uint64_t flush_errors;
+ pid_t workerpid;
};
static TAILQ_HEAD(, hast_snmp_resource) resources =
@@ -343,6 +344,7 @@ update_resources(void)
nv_get_uint64(nvout, "stat_delete_error%u", i);
res->flush_errors =
nv_get_uint64(nvout, "stat_flush_error%u", i);
+ res->workerpid = nv_get_int32(nvout, "workerpid%u", i);
TAILQ_INSERT_TAIL(&resources, res, link);
}
nv_free(nvout);
@@ -498,6 +500,9 @@ op_hastResourceTable(struct snmp_context *context __unused,
case LEAF_hastResourceFlushErrors:
value->v.counter64 = res->flush_errors;
break;
+ case LEAF_hastResourceWorkerPid:
+ value->v.integer = res->workerpid;
+ break;
default:
ret = SNMP_ERR_RES_UNAVAIL;
break;
diff --git a/usr.sbin/bsnmpd/modules/snmp_hast/hast_tree.def b/usr.sbin/bsnmpd/modules/snmp_hast/hast_tree.def
index 289c608..3769bd2 100644
--- a/usr.sbin/bsnmpd/modules/snmp_hast/hast_tree.def
+++ b/usr.sbin/bsnmpd/modules/snmp_hast/hast_tree.def
@@ -59,6 +59,7 @@
(19 hastResourceWriteErrors COUNTER64 GET)
(20 hastResourceDeleteErrors COUNTER64 GET)
(21 hastResourceFlushErrors COUNTER64 GET)
+ (22 hastResourceWorkerPid INTEGER GET)
)
)
)
diff --git a/usr.sbin/newsyslog/newsyslog.c b/usr.sbin/newsyslog/newsyslog.c
index 7a7e26f..69a63c4 100644
--- a/usr.sbin/newsyslog/newsyslog.c
+++ b/usr.sbin/newsyslog/newsyslog.c
@@ -1083,7 +1083,7 @@ parse_file(FILE *cf, struct cflist *work_p, struct cflist *glob_p,
* at any time, etc).
*/
if (strcasecmp(DEBUG_MARKER, q) == 0) {
- q = parse = missing_field(sob(++parse), errline);
+ q = parse = missing_field(sob(parse + 1), errline);
parse = son(parse);
if (!*parse)
warnx("debug line specifies no option:\n%s",
@@ -1096,7 +1096,7 @@ parse_file(FILE *cf, struct cflist *work_p, struct cflist *glob_p,
} else if (strcasecmp(INCLUDE_MARKER, q) == 0) {
if (verbose)
printf("Found: %s", errline);
- q = parse = missing_field(sob(++parse), errline);
+ q = parse = missing_field(sob(parse + 1), errline);
parse = son(parse);
if (!*parse) {
warnx("include line missing argument:\n%s",
@@ -1138,7 +1138,7 @@ parse_file(FILE *cf, struct cflist *work_p, struct cflist *glob_p,
defconf_p = working;
}
- q = parse = missing_field(sob(++parse), errline);
+ q = parse = missing_field(sob(parse + 1), errline);
parse = son(parse);
if (!*parse)
errx(1, "malformed line (missing fields):\n%s",
@@ -1172,7 +1172,7 @@ parse_file(FILE *cf, struct cflist *work_p, struct cflist *glob_p,
} else
working->gid = (gid_t)-1;
- q = parse = missing_field(sob(++parse), errline);
+ q = parse = missing_field(sob(parse + 1), errline);
parse = son(parse);
if (!*parse)
errx(1, "malformed line (missing fields):\n%s",
@@ -1187,7 +1187,7 @@ parse_file(FILE *cf, struct cflist *work_p, struct cflist *glob_p,
errx(1, "error in config file; bad permissions:\n%s",
errline);
- q = parse = missing_field(sob(++parse), errline);
+ q = parse = missing_field(sob(parse + 1), errline);
parse = son(parse);
if (!*parse)
errx(1, "malformed line (missing fields):\n%s",
@@ -1197,7 +1197,7 @@ parse_file(FILE *cf, struct cflist *work_p, struct cflist *glob_p,
errx(1, "error in config file; bad value for count of logs to save:\n%s",
errline);
- q = parse = missing_field(sob(++parse), errline);
+ q = parse = missing_field(sob(parse + 1), errline);
parse = son(parse);
if (!*parse)
errx(1, "malformed line (missing fields):\n%s",
@@ -1215,7 +1215,7 @@ parse_file(FILE *cf, struct cflist *work_p, struct cflist *glob_p,
working->flags = 0;
working->compress = COMPRESS_NONE;
- q = parse = missing_field(sob(++parse), errline);
+ q = parse = missing_field(sob(parse + 1), errline);
parse = son(parse);
eol = !*parse;
*parse = '\0';
@@ -1257,7 +1257,7 @@ no_trimat:
if (eol)
q = NULL;
else {
- q = parse = sob(++parse); /* Optional field */
+ q = parse = sob(parse + 1); /* Optional field */
parse = son(parse);
if (!*parse)
eol = 1;
@@ -1327,7 +1327,7 @@ no_trimat:
if (eol)
q = NULL;
else {
- q = parse = sob(++parse); /* Optional field */
+ q = parse = sob(parse + 1); /* Optional field */
parse = son(parse);
if (!*parse)
eol = 1;
@@ -1348,7 +1348,7 @@ no_trimat:
if (eol)
q = NULL;
else {
- q = parse = sob(++parse); /* Optional field */
+ q = parse = sob(parse + 1); /* Optional field */
*(parse = son(parse)) = '\0';
}
diff --git a/usr.sbin/nfsd/nfsv4.4 b/usr.sbin/nfsd/nfsv4.4
index e0e6023..8d9bc80 100644
--- a/usr.sbin/nfsd/nfsv4.4
+++ b/usr.sbin/nfsd/nfsv4.4
@@ -24,7 +24,7 @@
.\"
.\" $FreeBSD$
.\"
-.Dd May 1, 2013
+.Dd July 1, 2013
.Dt NFSV4 4
.Os
.Sh NAME
@@ -233,6 +233,20 @@ plus set ``tcp'' and
The
.Xr nfsuserd 8
must be running, as above.
+Also, since an
+.Nm
+mount uses the host uuid to identify the client uniquely to the server,
+you cannot safely do an
+.Nm
+mount when
+.sp
+.Bd -literal -offset indent -compact
+hostid_enable="NO"
+.Ed
+.sp
+is set in
+.Xr rc.conf 5 .
+.sp
If the
.Nm
server that is being mounted on supports delegations, you can start the
diff --git a/usr.sbin/pkg_install/add/extract.c b/usr.sbin/pkg_install/add/extract.c
index 9769464..9913126 100644
--- a/usr.sbin/pkg_install/add/extract.c
+++ b/usr.sbin/pkg_install/add/extract.c
@@ -110,7 +110,8 @@ extract_plist(const char *home, Package *pkg)
PackingList p = pkg->head;
char *last_file, *prefix = NULL;
char *where_args, *perm_args, *last_chdir;
- int maxargs, where_count = 0, perm_count = 0, add_count;
+ long maxargs;
+ int where_count = 0, perm_count = 0, add_count;
Boolean preserve;
maxargs = sysconf(_SC_ARG_MAX) / 2; /* Just use half the argument space */
diff --git a/usr.sbin/pkg_install/create/pl.c b/usr.sbin/pkg_install/create/pl.c
index 716fd05..1316eda 100644
--- a/usr.sbin/pkg_install/create/pl.c
+++ b/usr.sbin/pkg_install/create/pl.c
@@ -140,7 +140,8 @@ copy_plist(const char *home, Package *plist)
const char *there = NULL, *mythere;
char *where_args, *prefix = NULL;
const char *last_chdir, *root = "/";
- int maxargs, where_count = 0, add_count;
+ long maxargs;
+ int where_count = 0, add_count;
struct stat stb;
dev_t curdir;
diff --git a/usr.sbin/pkg_install/lib/exec.c b/usr.sbin/pkg_install/lib/exec.c
index 0804501..c0b4ac3 100644
--- a/usr.sbin/pkg_install/lib/exec.c
+++ b/usr.sbin/pkg_install/lib/exec.c
@@ -34,7 +34,8 @@ vsystem(const char *fmt, ...)
{
va_list args;
char *cmd;
- int ret, maxargs;
+ long maxargs;
+ int ret;
maxargs = sysconf(_SC_ARG_MAX);
maxargs -= 32; /* some slop for the sh -c */
@@ -64,7 +65,7 @@ vpipe(const char *fmt, ...)
{
FILE *fp;
char *cmd, *rp;
- int maxargs;
+ long maxargs;
va_list args;
rp = malloc(MAXPATHLEN);
diff --git a/usr.sbin/powerd/powerd.8 b/usr.sbin/powerd/powerd.8
index 0991bce..853282c 100644
--- a/usr.sbin/powerd/powerd.8
+++ b/usr.sbin/powerd/powerd.8
@@ -24,7 +24,7 @@
.\"
.\" $FreeBSD$
.\"
-.Dd December 21, 2009
+.Dd July 4, 2013
.Dt POWERD 8
.Os
.Sh NAME
@@ -47,27 +47,43 @@ The
.Nm
utility monitors the system state and sets various power control options
accordingly.
-It offers four modes (maximum, minimum, adaptive and hiadaptive) that can be
-individually selected while on AC power or batteries.
-The modes maximum, minimum, adaptive and hiadaptive may be abbreviated
-max, min, adp, hadp.
-.Pp
-Maximum mode chooses the highest performance values.
-Minimum mode selects the lowest performance values to get the most power
-savings.
-Adaptive mode attempts to strike a balance by degrading performance when
-the system appears idle and increasing it when the system is busy.
+It offers power-saving modes that can be
+individually selected for operation on AC power or batteries.
+.Bl -tag -width ".Ar hiadaptive"
+.It Ar maximum
+Choose the highest performance values.
+May be abbreviated as
+.Ar max .
+.It Ar minimum
+Choose the lowest performance values to get the most power savings.
+May be abbreviated as
+.Ar min .
+.It Ar adaptive
+Attempt to strike a balance by degrading performance when the system
+appears idle and increasing it when the system is busy.
It offers a good balance between a small performance loss for greatly
increased power savings.
-Hiadaptive mode is like adaptive mode, but tuned for systems where
-performance and interactivity are more important than power consumption.
-It increases frequency faster, reduces the frequency less aggressively and
+May be abbreviated as
+.Ar adp .
+.It Ar hiadaptive
+Like
+.Ar adaptive
+mode, but tuned for systems where performance and interactivity are
+more important than power consumption.
+It increases frequency faster, reduces frequency less aggressively, and
will maintain full frequency for longer.
-The default mode is adaptive for battery power and hiadaptive for the rest.
+May be abbreviated as
+.Ar hadp .
+.El
+.Pp
+The default mode is
+.Ar adaptive
+for battery power and
+.Ar hiadaptive
+for the rest.
.Pp
-The
.Nm
-utility recognizes the following runtime options:
+recognizes these runtime options:
.Bl -tag -width ".Fl r Ar percent"
.It Fl a Ar mode
Selects the
diff --git a/usr.sbin/powerd/powerd.c b/usr.sbin/powerd/powerd.c
index 254c237..2c6eac2 100644
--- a/usr.sbin/powerd/powerd.c
+++ b/usr.sbin/powerd/powerd.c
@@ -154,7 +154,7 @@ read_usage_times(int *load)
error = sysctl(cp_times_mib, 2, cp_times, &cp_times_len, NULL, 0);
if (error)
return (error);
-
+
if (load) {
*load = 0;
for (cpu = 0; cpu < ncpus; cpu++) {
@@ -165,7 +165,7 @@ read_usage_times(int *load)
}
if (total == 0)
continue;
- *load += 100 - (cp_times[cpu * CPUSTATES + CP_IDLE] -
+ *load += 100 - (cp_times[cpu * CPUSTATES + CP_IDLE] -
cp_times_old[cpu * CPUSTATES + CP_IDLE]) * 100 / total;
}
}
@@ -236,7 +236,7 @@ get_freq(void)
{
size_t len;
int curfreq;
-
+
len = sizeof(curfreq);
if (sysctl(freq_mib, 4, &curfreq, &len, NULL, 0) != 0) {
if (vflag)
@@ -262,7 +262,7 @@ static int
get_freq_id(int freq, int *freqs, int numfreqs)
{
int i = 1;
-
+
while (i < numfreqs) {
if (freqs[i] < freq)
break;
@@ -717,7 +717,7 @@ main(int argc, char * argv[])
idle = 0;
if (set_freq(freq) != 0) {
warn("error setting CPU freq %d",
- freq);
+ freq);
continue;
}
}
@@ -730,7 +730,7 @@ main(int argc, char * argv[])
warn("read_usage_times() failed");
continue;
}
-
+
if (mode == MODE_ADAPTIVE) {
if (load > cpu_running_mark) {
if (load > 95 || load > cpu_running_mark * 2)
@@ -741,7 +741,7 @@ main(int argc, char * argv[])
freq = freqs[0];
} else if (load < cpu_idle_mark &&
curfreq * load < freqs[get_freq_id(
- freq * 7 / 8, freqs, numfreqs)] *
+ freq * 7 / 8, freqs, numfreqs)] *
cpu_running_mark) {
freq = freq * 7 / 8;
if (freq < freqs[numfreqs - 1])
@@ -757,7 +757,7 @@ main(int argc, char * argv[])
freq = freqs[0] * 2;
} else if (load < cpu_idle_mark / 2 &&
curfreq * load < freqs[get_freq_id(
- freq * 31 / 32, freqs, numfreqs)] *
+ freq * 31 / 32, freqs, numfreqs)] *
cpu_running_mark / 2) {
freq = freq * 31 / 32;
if (freq < freqs[numfreqs - 1])
diff --git a/usr.sbin/pw/pw_user.c b/usr.sbin/pw/pw_user.c
index 5f4d7a9..def238c 100644
--- a/usr.sbin/pw/pw_user.c
+++ b/usr.sbin/pw/pw_user.c
@@ -200,7 +200,7 @@ pw_user(struct userconf * cnf, int mode, struct cargs * args)
strlcpy(dbuf, cnf->home, sizeof(dbuf));
p = dbuf;
if (stat(dbuf, &st) == -1) {
- while ((p = strchr(++p, '/')) != NULL) {
+ while ((p = strchr(p + 1, '/')) != NULL) {
*p = '\0';
if (stat(dbuf, &st) == -1) {
if (mkdir(dbuf, _DEF_DIRMODE) == -1)
@@ -513,8 +513,6 @@ pw_user(struct userconf * cnf, int mode, struct cargs * args)
time_t now = time(NULL);
time_t expire = parse_date(now, arg->val);
- if (now == expire)
- errx(EX_DATAERR, "invalid password change date `%s'", arg->val);
if (pwd->pw_change != expire) {
pwd->pw_change = expire;
edited = 1;
@@ -533,8 +531,6 @@ pw_user(struct userconf * cnf, int mode, struct cargs * args)
time_t now = time(NULL);
time_t expire = parse_date(now, arg->val);
- if (now == expire)
- errx(EX_DATAERR, "invalid account expiry date `%s'", arg->val);
if (pwd->pw_expire != expire) {
pwd->pw_expire = expire;
edited = 1;
@@ -577,7 +573,7 @@ pw_user(struct userconf * cnf, int mode, struct cargs * args)
lc = login_getpwclass(pwd);
if (lc == NULL ||
- login_setcryptfmt(lc, "md5", NULL) == NULL)
+ login_setcryptfmt(lc, "sha512", NULL) == NULL)
warn("setting crypt(3) format");
login_close(lc);
pwd->pw_passwd = pw_password(cnf, args, pwd->pw_name);
diff --git a/usr.sbin/rwhod/rwhod.c b/usr.sbin/rwhod/rwhod.c
index 16bf948..26b0500 100644
--- a/usr.sbin/rwhod/rwhod.c
+++ b/usr.sbin/rwhod/rwhod.c
@@ -1,6 +1,7 @@
-/*
- * Copyright (c) 1983, 1993
- * The Regents of the University of California. All rights reserved.
+/*-
+ * Copyright (c) 1983, 1993 The Regents of the University of California.
+ * Copyright (c) 2013 Mariusz Zaborski <oshogbo@FreeBSD.org>
+ * All rights reserved.
*
* Redistribution and use in source and binary forms, with or without
* modification, are permitted provided that the following conditions
@@ -42,12 +43,15 @@ static char sccsid[] = "@(#)rwhod.c 8.1 (Berkeley) 6/6/93";
#include <sys/cdefs.h>
__FBSDID("$FreeBSD$");
+#include <sys/capability.h>
#include <sys/param.h>
#include <sys/socket.h>
#include <sys/stat.h>
#include <sys/signal.h>
#include <sys/ioctl.h>
#include <sys/sysctl.h>
+#include <sys/procdesc.h>
+#include <sys/wait.h>
#include <net/if.h>
#include <net/if_dl.h>
@@ -60,47 +64,17 @@ __FBSDID("$FreeBSD$");
#include <err.h>
#include <errno.h>
#include <fcntl.h>
+#include <grp.h>
#include <netdb.h>
#include <paths.h>
+#include <pwd.h>
#include <stdio.h>
#include <stdlib.h>
#include <string.h>
#include <syslog.h>
#include <timeconv.h>
-#include <unistd.h>
#include <utmpx.h>
-#include <pwd.h>
-#include <grp.h>
-
-/*
- * This version of Berkeley's rwhod has been modified to use IP multicast
- * datagrams, under control of a new command-line option:
- *
- * rwhod -m causes rwhod to use IP multicast (instead of
- * broadcast or unicast) on all interfaces that have
- * the IFF_MULTICAST flag set in their "ifnet" structs
- * (excluding the loopback interface). The multicast
- * reports are sent with a time-to-live of 1, to prevent
- * forwarding beyond the directly-connected subnet(s).
- *
- * rwhod -m <ttl> causes rwhod to send IP multicast datagrams with a
- * time-to-live of <ttl>, via a SINGLE interface rather
- * than all interfaces. <ttl> must be between 0 and
- * MAX_MULTICAST_SCOPE, defined below. Note that "-m 1"
- * is different than "-m", in that "-m 1" specifies
- * transmission on one interface only.
- *
- * When "-m" is used without a <ttl> argument, the program accepts multicast
- * rwhod reports from all multicast-capable interfaces. If a <ttl> argument
- * is given, it accepts multicast reports from only one interface, the one
- * on which reports are sent (which may be controlled via the host's routing
- * table). Regardless of the "-m" option, the program accepts broadcast or
- * unicast reports from all interfaces. Thus, this program will hear the
- * reports of old, non-multicasting rwhods, but, if multicasting is used,
- * those old rwhods won't hear the reports generated by this program.
- *
- * -- Steve Deering, Stanford University, February 1989
- */
+#include <unistd.h>
#define UNPRIV_USER "daemon"
#define UNPRIV_GROUP "daemon"
@@ -112,21 +86,21 @@ __FBSDID("$FreeBSD$");
#define MAX_MULTICAST_SCOPE 32 /* "site-wide", by convention */
#define INADDR_WHOD_GROUP (u_long)0xe0000103 /* 224.0.1.3 */
- /* (belongs in protocols/rwhod.h) */
+ /* (belongs in protocols/rwhod.h) */
-int insecure_mode;
-int quiet_mode;
-int iff_flag = IFF_POINTOPOINT;
-int multicast_mode = NO_MULTICAST;
-int multicast_scope;
-struct sockaddr_in multicast_addr =
- { sizeof multicast_addr, AF_INET, 0, { 0 }, { 0 } };
+int insecure_mode;
+int quiet_mode;
+int iff_flag = IFF_POINTOPOINT;
+int multicast_mode = NO_MULTICAST;
+int multicast_scope;
+struct sockaddr_in multicast_addr =
+ { sizeof(multicast_addr), AF_INET, 0, { 0 }, { 0 } };
/*
- * Alarm interval. Don't forget to change the down time check in ruptime
+ * Sleep interval. Don't forget to change the down time check in ruptime
* if this is changed.
*/
-#define AL_INTERVAL (3 * 60)
+#define SL_INTERVAL (3 * 60)
char myname[MAXHOSTNAMELEN];
@@ -137,72 +111,109 @@ char myname[MAXHOSTNAMELEN];
*/
struct neighbor {
struct neighbor *n_next;
- char *n_name; /* interface name */
+ char *n_name; /* interface name */
struct sockaddr *n_addr; /* who to send to */
- int n_addrlen; /* size of address */
- int n_flags; /* should forward?, interface flags */
+ int n_addrlen; /* size of address */
+ int n_flags; /* should forward?, interface flags */
};
struct neighbor *neighbors;
struct whod mywd;
-struct servent *sp;
+struct servent *sp;
int s;
+int fdp;
+pid_t pid_child_receiver;
#define WHDRSIZE (int)(sizeof(mywd) - sizeof(mywd.wd_we))
-void run_as(uid_t *, gid_t *);
-int configure(int);
-void getboottime(int);
-void onalrm(int);
-void quit(const char *);
-void rt_xaddrs(caddr_t, caddr_t, struct rt_addrinfo *);
-int verify(char *, int);
+int configure(int so);
+void getboottime(int signo __unused);
+void receiver_process(void);
+void rt_xaddrs(caddr_t cp, caddr_t cplim, struct rt_addrinfo *rtinfo);
+void run_as(uid_t *uid, gid_t *gid);
+void quit(const char *msg);
+void sender_process(void);
+int verify(char *name, int maxlen);
static void usage(void);
+
#ifdef DEBUG
-char *interval(int, char *);
-void Sendto(int, const void *, size_t, int, const struct sockaddr *, int);
+char *interval(int time, char *updown);
+void Sendto(int s, const void *buf, size_t cc, int flags,
+ const struct sockaddr *to, int tolen);
#define sendto Sendto
#endif
+/*
+ * This version of Berkeley's rwhod has been modified to use IP multicast
+ * datagrams, under control of a new command-line option:
+ *
+ * rwhod -m causes rwhod to use IP multicast (instead of
+ * broadcast or unicast) on all interfaces that have
+ * the IFF_MULTICAST flag set in their "ifnet" structs
+ * (excluding the loopback interface). The multicast
+ * reports are sent with a time-to-live of 1, to prevent
+ * forwarding beyond the directly-connected subnet(s).
+ *
+ * rwhod -m <ttl> causes rwhod to send IP multicast datagrams with a
+ * time-to-live of <ttl>, via a SINGLE interface rather
+ * than all interfaces. <ttl> must be between 0 and
+ * MAX_MULTICAST_SCOPE, defined below. Note that "-m 1"
+ * is different than "-m", in that "-m 1" specifies
+ * transmission on one interface only.
+ *
+ * When "-m" is used without a <ttl> argument, the program accepts multicast
+ * rwhod reports from all multicast-capable interfaces. If a <ttl> argument
+ * is given, it accepts multicast reports from only one interface, the one
+ * on which reports are sent (which may be controlled via the host's routing
+ * table). Regardless of the "-m" option, the program accepts broadcast or
+ * unicast reports from all interfaces. Thus, this program will hear the
+ * reports of old, non-multicasting rwhods, but, if multicasting is used,
+ * those old rwhods won't hear the reports generated by this program.
+ *
+ * -- Steve Deering, Stanford University, February 1989
+ */
int
main(int argc, char *argv[])
{
- struct sockaddr_in from;
- struct stat st;
- char path[64];
- int on = 1;
+ int on;
char *cp;
struct sockaddr_in soin;
uid_t unpriv_uid;
gid_t unpriv_gid;
+ on = 1;
if (getuid())
errx(1, "not super user");
run_as(&unpriv_uid, &unpriv_gid);
- argv++; argc--;
+ argv++;
+ argc--;
while (argc > 0 && *argv[0] == '-') {
if (strcmp(*argv, "-m") == 0) {
if (argc > 1 && isdigit(*(argv + 1)[0])) {
- argv++, argc--;
+ argv++;
+ argc--;
multicast_mode = SCOPED_MULTICAST;
multicast_scope = atoi(*argv);
- if (multicast_scope > MAX_MULTICAST_SCOPE)
+ if (multicast_scope > MAX_MULTICAST_SCOPE) {
errx(1, "ttl must not exceed %u",
- MAX_MULTICAST_SCOPE);
+ MAX_MULTICAST_SCOPE);
+ }
+ } else {
+ multicast_mode = PER_INTERFACE_MULTICAST;
}
- else multicast_mode = PER_INTERFACE_MULTICAST;
- }
- else if (strcmp(*argv, "-i") == 0)
+ } else if (strcmp(*argv, "-i") == 0) {
insecure_mode = 1;
- else if (strcmp(*argv, "-l") == 0)
+ } else if (strcmp(*argv, "-l") == 0) {
quiet_mode = 1;
- else if (strcmp(*argv, "-p") == 0)
+ } else if (strcmp(*argv, "-p") == 0) {
iff_flag = 0;
- else
+ } else {
usage();
- argv++, argc--;
+ }
+ argv++;
+ argc--;
}
if (argc > 0)
usage();
@@ -210,7 +221,7 @@ main(int argc, char *argv[])
daemon(1, 0);
#endif
(void) signal(SIGHUP, getboottime);
- openlog("rwhod", LOG_PID, LOG_DAEMON);
+ openlog("rwhod", LOG_PID | LOG_NDELAY, LOG_DAEMON);
sp = getservbyname("who", "udp");
if (sp == NULL) {
syslog(LOG_ERR, "who/udp: unknown service");
@@ -229,8 +240,7 @@ main(int argc, char *argv[])
}
if ((cp = strchr(myname, '.')) != NULL)
*cp = '\0';
- strncpy(mywd.wd_hostname, myname, sizeof(mywd.wd_hostname) - 1);
- mywd.wd_hostname[sizeof(mywd.wd_hostname) - 1] = '\0';
+ strlcpy(mywd.wd_hostname, myname, sizeof(mywd.wd_hostname));
getboottime(0);
if ((s = socket(AF_INET, SOCK_DGRAM, 0)) < 0) {
syslog(LOG_ERR, "socket: %m");
@@ -252,7 +262,7 @@ main(int argc, char *argv[])
syslog(LOG_ERR, "setgid: %m");
exit(1);
}
- if (setgroups(1, &unpriv_gid) != 0) { /* XXX BOGUS groups[0] = egid */
+ if (setgroups(1, &unpriv_gid) != 0) { /* XXX BOGUS groups[0] = egid */
syslog(LOG_ERR, "setgroups: %m");
exit(1);
}
@@ -263,17 +273,100 @@ main(int argc, char *argv[])
if (!configure(s))
exit(1);
if (!quiet_mode) {
- signal(SIGALRM, onalrm);
- onalrm(0);
+ pid_child_receiver = pdfork(&fdp, 0);
+ if (pid_child_receiver == 0) {
+ receiver_process();
+ } else if (pid_child_receiver > 0) {
+ sender_process();
+ } else if (pid_child_receiver == -1) {
+ syslog(LOG_ERR, "pdfork: %m");
+ exit(1);
+ }
+ } else {
+ receiver_process();
}
- for (;;) {
- struct whod wd;
- socklen_t len = sizeof(from);
- int cc, whod;
- time_t t;
+}
+
+static void
+usage(void)
+{
+
+ fprintf(stderr, "usage: rwhod [-i] [-p] [-l] [-m [ttl]]\n");
+ exit(1);
+}
+
+void
+run_as(uid_t *uid, gid_t *gid)
+{
+ struct passwd *pw;
+ struct group *gr;
+
+ pw = getpwnam(UNPRIV_USER);
+ if (pw == NULL) {
+ syslog(LOG_ERR, "getpwnam(%s): %m", UNPRIV_USER);
+ exit(1);
+ }
+ *uid = pw->pw_uid;
+
+ gr = getgrnam(UNPRIV_GROUP);
+ if (gr == NULL) {
+ syslog(LOG_ERR, "getgrnam(%s): %m", UNPRIV_GROUP);
+ exit(1);
+ }
+ *gid = gr->gr_gid;
+}
+
+/*
+ * Check out host name for unprintables
+ * and other funnies before allowing a file
+ * to be created. Sorry, but blanks aren't allowed.
+ */
+int
+verify(char *name, int maxlen)
+{
+ int size;
+
+ size = 0;
+ while (*name != '\0' && size < maxlen - 1) {
+ if (!isascii(*name) || !isalnum(*name) || ispunct(*name))
+ return (0);
+ name++;
+ size++;
+ }
+ *name = '\0';
+ return (size > 0);
+}
- cc = recvfrom(s, (char *)&wd, sizeof(struct whod), 0,
- (struct sockaddr *)&from, &len);
+void
+receiver_process(void)
+{
+ struct sockaddr_in from;
+ struct stat st;
+ char path[64];
+ int dirfd;
+ struct whod wd;
+ socklen_t len;
+ int cc, whod;
+ time_t t;
+
+ len = sizeof(from);
+ dirfd = open(".", O_RDONLY | O_DIRECTORY);
+ if (dirfd < 0) {
+ syslog(LOG_WARNING, "%s: %m", _PATH_RWHODIR);
+ exit(1);
+ }
+ if (cap_rights_limit(dirfd, CAP_CREATE | CAP_WRITE | CAP_FTRUNCATE |
+ CAP_SEEK | CAP_LOOKUP | CAP_FSTAT) < 0 && errno != ENOSYS) {
+ syslog(LOG_WARNING, "cap_rights_limit: %m");
+ exit(1);
+ }
+ if (cap_enter() < 0 && errno != ENOSYS) {
+ syslog(LOG_ERR, "cap_enter: %m");
+ exit(1);
+ }
+ for (;;) {
+ cc = recvfrom(s, &wd, sizeof(wd), 0, (struct sockaddr *)&from,
+ &len);
if (cc <= 0) {
if (cc < 0 && errno != EINTR)
syslog(LOG_WARNING, "recv: %m");
@@ -293,26 +386,32 @@ main(int argc, char *argv[])
continue;
if (wd.wd_type != WHODTYPE_STATUS)
continue;
- if (!verify(wd.wd_hostname, sizeof wd.wd_hostname)) {
+ if (!verify(wd.wd_hostname, sizeof(wd.wd_hostname))) {
syslog(LOG_WARNING, "malformed host name from %s",
inet_ntoa(from.sin_addr));
continue;
}
- (void) snprintf(path, sizeof path, "whod.%s", wd.wd_hostname);
+ (void) snprintf(path, sizeof(path), "whod.%s", wd.wd_hostname);
/*
* Rather than truncating and growing the file each time,
* use ftruncate if size is less than previous size.
*/
- whod = open(path, O_WRONLY | O_CREAT, 0644);
+ whod = openat(dirfd, path, O_WRONLY | O_CREAT, 0644);
if (whod < 0) {
syslog(LOG_WARNING, "%s: %m", path);
continue;
}
+ if (cap_rights_limit(whod, CAP_WRITE | CAP_FTRUNCATE |
+ CAP_FSTAT) < 0 && errno != ENOSYS) {
+ syslog(LOG_WARNING, "cap_rights_limit: %m");
+ exit(1);
+ }
#if ENDIAN != BIG_ENDIAN
{
- int i, n = (cc - WHDRSIZE)/sizeof(struct whoent);
struct whoent *we;
+ int i, n;
+ n = (cc - WHDRSIZE) / sizeof(struct whoent);
/* undo header byte swapping before writing to file */
wd.wd_sendtime = ntohl(wd.wd_sendtime);
for (i = 0; i < 3; i++)
@@ -334,141 +433,104 @@ main(int argc, char *argv[])
ftruncate(whod, cc);
(void) close(whod);
}
-}
-
-static void
-usage()
-{
- fprintf(stderr, "usage: rwhod [-i] [-p] [-l] [-m [ttl]]\n");
- exit(1);
+ (void) close(dirfd);
}
void
-run_as(uid, gid)
- uid_t *uid;
- gid_t *gid;
+sender_process(void)
{
- struct passwd *pw;
- struct group *gr;
-
- pw = getpwnam(UNPRIV_USER);
- if (!pw) {
- syslog(LOG_ERR, "getpwnam(%s): %m", UNPRIV_USER);
- exit(1);
- }
- *uid = pw->pw_uid;
-
- gr = getgrnam(UNPRIV_GROUP);
- if (!gr) {
- syslog(LOG_ERR, "getgrnam(%s): %m", UNPRIV_GROUP);
- exit(1);
- }
- *gid = gr->gr_gid;
-}
-
-/*
- * Check out host name for unprintables
- * and other funnies before allowing a file
- * to be created. Sorry, but blanks aren't allowed.
- */
-int
-verify(name, maxlen)
- register char *name;
- register int maxlen;
-{
- register int size = 0;
-
- while (*name && size < maxlen - 1) {
- if (!isascii(*name) || !(isalnum(*name) || ispunct(*name)))
- return (0);
- name++, size++;
- }
- *name = '\0';
- return (size > 0);
-}
-
-void
-onalrm(int signo __unused)
-{
- struct neighbor *np;
- struct whoent *we = mywd.wd_we, *wend;
- struct stat stb;
- struct utmpx *ut;
- static int alarmcount = 0;
+ int sendcount;
double avenrun[3];
time_t now;
- int i, cc;
-
- now = time(NULL);
- if (alarmcount % 10 == 0)
- getboottime(0);
- alarmcount++;
- wend = &mywd.wd_we[1024 / sizeof(struct whoent)];
- setutxent();
- while ((ut = getutxent()) != NULL && we < wend) {
- if (ut->ut_type != USER_PROCESS)
- continue;
- strncpy(we->we_utmp.out_line, ut->ut_line,
- sizeof(we->we_utmp.out_line));
- strncpy(we->we_utmp.out_name, ut->ut_user,
- sizeof(we->we_utmp.out_name));
- we->we_utmp.out_time =
- htonl(_time_to_time32(ut->ut_tv.tv_sec));
- we++;
- }
- endutxent();
+ int i, cc, status;
+ struct utmpx *ut;
+ struct stat stb;
+ struct neighbor *np;
+ struct whoent *we, *wend;
- if (chdir(_PATH_DEV)) {
- syslog(LOG_ERR, "chdir(%s): %m", _PATH_DEV);
- exit(1);
- }
- wend = we;
- for (we = mywd.wd_we; we < wend; we++) {
- if (stat(we->we_utmp.out_line, &stb) >= 0)
- we->we_idle = htonl(now - stb.st_atime);
- we++;
- }
- (void)getloadavg(avenrun, sizeof(avenrun)/sizeof(avenrun[0]));
- for (i = 0; i < 3; i++)
- mywd.wd_loadav[i] = htonl((u_long)(avenrun[i] * 100));
- cc = (char *)wend - (char *)&mywd;
- mywd.wd_sendtime = htonl(_time_to_time32(time(NULL)));
- mywd.wd_vers = WHODVERSION;
- mywd.wd_type = WHODTYPE_STATUS;
- if (multicast_mode == SCOPED_MULTICAST) {
- (void) sendto(s, (char *)&mywd, cc, 0,
- (struct sockaddr *)&multicast_addr,
- sizeof(multicast_addr));
- }
- else for (np = neighbors; np != NULL; np = np->n_next) {
- if (multicast_mode == PER_INTERFACE_MULTICAST &&
- np->n_flags & IFF_MULTICAST) {
- /*
- * Select the outgoing interface for the multicast.
- */
- if (setsockopt(s, IPPROTO_IP, IP_MULTICAST_IF,
- &(((struct sockaddr_in *)np->n_addr)->sin_addr),
- sizeof(struct in_addr)) < 0) {
- syslog(LOG_ERR,
- "setsockopt IP_MULTICAST_IF: %m");
- exit(1);
- }
+ sendcount = 0;
+ for (;;) {
+ we = mywd.wd_we;
+ now = time(NULL);
+ if (sendcount % 10 == 0)
+ getboottime(0);
+ sendcount++;
+ wend = &mywd.wd_we[1024 / sizeof(struct whoent)];
+ setutxent();
+ while ((ut = getutxent()) != NULL && we < wend) {
+ if (ut->ut_type != USER_PROCESS)
+ continue;
+ strncpy(we->we_utmp.out_line, ut->ut_line,
+ sizeof(we->we_utmp.out_line));
+ strncpy(we->we_utmp.out_name, ut->ut_user,
+ sizeof(we->we_utmp.out_name));
+ we->we_utmp.out_time =
+ htonl(_time_to_time32(ut->ut_tv.tv_sec));
+ we++;
+ }
+ endutxent();
+
+ if (chdir(_PATH_DEV) < 0) {
+ syslog(LOG_ERR, "chdir(%s): %m", _PATH_DEV);
+ exit(1);
+ }
+ wend = we;
+ for (we = mywd.wd_we; we < wend; we++) {
+ if (stat(we->we_utmp.out_line, &stb) >= 0)
+ we->we_idle = htonl(now - stb.st_atime);
+ we++;
+ }
+ (void) getloadavg(avenrun,
+ sizeof(avenrun) / sizeof(avenrun[0]));
+ for (i = 0; i < 3; i++)
+ mywd.wd_loadav[i] = htonl((u_long)(avenrun[i] * 100));
+ cc = (char *)wend - (char *)&mywd;
+ mywd.wd_sendtime = htonl(_time_to_time32(time(NULL)));
+ mywd.wd_vers = WHODVERSION;
+ mywd.wd_type = WHODTYPE_STATUS;
+ if (multicast_mode == SCOPED_MULTICAST) {
(void) sendto(s, (char *)&mywd, cc, 0,
- (struct sockaddr *)&multicast_addr,
- sizeof(multicast_addr));
- } else (void) sendto(s, (char *)&mywd, cc, 0,
- np->n_addr, np->n_addrlen);
- }
- if (chdir(_PATH_RWHODIR)) {
- syslog(LOG_ERR, "chdir(%s): %m", _PATH_RWHODIR);
- exit(1);
+ (struct sockaddr *)&multicast_addr,
+ sizeof(multicast_addr));
+ } else {
+ for (np = neighbors; np != NULL; np = np->n_next) {
+ if (multicast_mode == PER_INTERFACE_MULTICAST &&
+ (np->n_flags & IFF_MULTICAST) != 0) {
+ /*
+ * Select the outgoing interface for the
+ * multicast.
+ */
+ if (setsockopt(s, IPPROTO_IP,
+ IP_MULTICAST_IF,
+ &(((struct sockaddr_in *)np->n_addr)->sin_addr),
+ sizeof(struct in_addr)) < 0) {
+ syslog(LOG_ERR,
+ "setsockopt IP_MULTICAST_IF: %m");
+ exit(1);
+ }
+ (void) sendto(s, (char *)&mywd, cc, 0,
+ (struct sockaddr *)&multicast_addr,
+ sizeof(multicast_addr));
+ } else {
+ (void) sendto(s, (char *)&mywd, cc, 0,
+ np->n_addr, np->n_addrlen);
+ }
+ }
+ }
+ if (chdir(_PATH_RWHODIR) < 0) {
+ syslog(LOG_ERR, "chdir(%s): %m", _PATH_RWHODIR);
+ exit(1);
+ }
+ if (waitpid(pid_child_receiver, &status, WNOHANG) ==
+ pid_child_receiver) {
+ break;
+ }
+ sleep(SL_INTERVAL);
}
- (void) alarm(AL_INTERVAL);
}
void
-getboottime(signo)
- int signo __unused;
+getboottime(int signo __unused)
{
int mib[2];
size_t size;
@@ -485,26 +547,25 @@ getboottime(signo)
}
void
-quit(msg)
- const char *msg;
+quit(const char *msg)
{
+
syslog(LOG_ERR, "%s", msg);
exit(1);
}
void
-rt_xaddrs(cp, cplim, rtinfo)
- register caddr_t cp, cplim;
- register struct rt_addrinfo *rtinfo;
+rt_xaddrs(caddr_t cp, caddr_t cplim, struct rt_addrinfo *rtinfo)
{
- register struct sockaddr *sa;
- register int i;
+ struct sockaddr *sa;
+ int i;
memset(rtinfo->rti_info, 0, sizeof(rtinfo->rti_info));
- for (i = 0; (i < RTAX_MAX) && (cp < cplim); i++) {
+ for (i = 0; i < RTAX_MAX && cp < cplim; i++) {
if ((rtinfo->rti_addrs & (1 << i)) == 0)
continue;
- rtinfo->rti_info[i] = sa = (struct sockaddr *)cp;
+ sa = (struct sockaddr *)cp;
+ rtinfo->rti_info[i] = sa;
cp += SA_SIZE(sa);
}
}
@@ -514,18 +575,18 @@ rt_xaddrs(cp, cplim, rtinfo)
* networks which deserve status information.
*/
int
-configure(so)
- int so;
+configure(int so)
{
- register struct neighbor *np;
- register struct if_msghdr *ifm;
- register struct ifa_msghdr *ifam;
+ struct neighbor *np;
+ struct if_msghdr *ifm;
+ struct ifa_msghdr *ifam;
struct sockaddr_dl *sdl;
size_t needed;
- int mib[6], flags = 0, len;
+ int mib[6], flags, lflags, len;
char *buf, *lim, *next;
struct rt_addrinfo info;
+ flags = 0;
if (multicast_mode != NO_MULTICAST) {
multicast_addr.sin_addr.s_addr = htonl(INADDR_WHOD_GROUP);
multicast_addr.sin_port = sp->s_port;
@@ -538,19 +599,19 @@ configure(so)
mreq.imr_multiaddr.s_addr = htonl(INADDR_WHOD_GROUP);
mreq.imr_interface.s_addr = htonl(INADDR_ANY);
if (setsockopt(so, IPPROTO_IP, IP_ADD_MEMBERSHIP,
- &mreq, sizeof(mreq)) < 0) {
+ &mreq, sizeof(mreq)) < 0) {
syslog(LOG_ERR,
- "setsockopt IP_ADD_MEMBERSHIP: %m");
- return(0);
+ "setsockopt IP_ADD_MEMBERSHIP: %m");
+ return (0);
}
ttl = multicast_scope;
- if (setsockopt(so, IPPROTO_IP, IP_MULTICAST_TTL,
- &ttl, sizeof(ttl)) < 0) {
+ if (setsockopt(so, IPPROTO_IP, IP_MULTICAST_TTL, &ttl,
+ sizeof(ttl)) < 0) {
syslog(LOG_ERR,
- "setsockopt IP_MULTICAST_TTL: %m");
- return(0);
+ "setsockopt IP_MULTICAST_TTL: %m");
+ return (0);
}
- return(1);
+ return (1);
}
mib[0] = CTL_NET;
@@ -575,34 +636,38 @@ configure(so)
flags = ifm->ifm_flags;
continue;
}
- if ((flags & IFF_UP) == 0 ||
- (flags & (((multicast_mode == PER_INTERFACE_MULTICAST) ?
- IFF_MULTICAST : 0) |
- IFF_BROADCAST|iff_flag)) == 0)
+ if ((flags & IFF_UP) == 0)
+ continue;
+ lflags = IFF_BROADCAST | iff_flag;
+ if (multicast_mode == PER_INTERFACE_MULTICAST)
+ lflags |= IFF_MULTICAST;
+ if ((flags & lflags) == 0)
continue;
if (ifm->ifm_type != RTM_NEWADDR)
quit("out of sync parsing NET_RT_IFLIST");
ifam = (struct ifa_msghdr *)ifm;
info.rti_addrs = ifam->ifam_addrs;
rt_xaddrs((char *)(ifam + 1), ifam->ifam_msglen + (char *)ifam,
- &info);
+ &info);
/* gag, wish we could get rid of Internet dependencies */
-#define dstaddr info.rti_info[RTAX_BRD]
-#define ifaddr info.rti_info[RTAX_IFA]
-#define IPADDR_SA(x) ((struct sockaddr_in *)(x))->sin_addr.s_addr
-#define PORT_SA(x) ((struct sockaddr_in *)(x))->sin_port
+#define dstaddr info.rti_info[RTAX_BRD]
+#define ifaddr info.rti_info[RTAX_IFA]
+#define IPADDR_SA(x) ((struct sockaddr_in *)(x))->sin_addr.s_addr
+#define PORT_SA(x) ((struct sockaddr_in *)(x))->sin_port
if (dstaddr == 0 || dstaddr->sa_family != AF_INET)
continue;
PORT_SA(dstaddr) = sp->s_port;
- for (np = neighbors; np != NULL; np = np->n_next)
+ for (np = neighbors; np != NULL; np = np->n_next) {
if (memcmp(sdl->sdl_data, np->n_name,
- sdl->sdl_nlen) == 0 &&
- IPADDR_SA(np->n_addr) == IPADDR_SA(dstaddr))
+ sdl->sdl_nlen) == 0 &&
+ IPADDR_SA(np->n_addr) == IPADDR_SA(dstaddr)) {
break;
+ }
+ }
if (np != NULL)
continue;
len = sizeof(*np) + dstaddr->sa_len + sdl->sdl_nlen + 1;
- np = (struct neighbor *)malloc(len);
+ np = malloc(len);
if (np == NULL)
quit("malloc of neighbor structure");
memset(np, 0, len);
@@ -613,24 +678,24 @@ configure(so)
memcpy((char *)np->n_addr, (char *)dstaddr, np->n_addrlen);
memcpy(np->n_name, sdl->sdl_data, sdl->sdl_nlen);
if (multicast_mode == PER_INTERFACE_MULTICAST &&
- (flags & IFF_MULTICAST) &&
- !(flags & IFF_LOOPBACK)) {
+ (flags & IFF_MULTICAST) != 0 &&
+ (flags & IFF_LOOPBACK) == 0) {
struct ip_mreq mreq;
memcpy((char *)np->n_addr, (char *)ifaddr,
- np->n_addrlen);
+ np->n_addrlen);
mreq.imr_multiaddr.s_addr = htonl(INADDR_WHOD_GROUP);
mreq.imr_interface.s_addr =
- ((struct sockaddr_in *)np->n_addr)->sin_addr.s_addr;
+ ((struct sockaddr_in *)np->n_addr)->sin_addr.s_addr;
if (setsockopt(s, IPPROTO_IP, IP_ADD_MEMBERSHIP,
- &mreq, sizeof(mreq)) < 0) {
+ &mreq, sizeof(mreq)) < 0) {
syslog(LOG_ERR,
"setsockopt IP_ADD_MEMBERSHIP: %m");
#if 0
/* Fall back to broadcast on this if. */
np->n_flags &= ~IFF_MULTICAST;
#else
- free((char *)np);
+ free(np);
continue;
#endif
}
@@ -644,36 +709,32 @@ configure(so)
#ifdef DEBUG
void
-Sendto(s, buf, cc, flags, to, tolen)
- int s;
- const void *buf;
- size_t cc;
- int flags;
- const struct sockaddr *to;
- int tolen;
+Sendto(int s, const void *buf, size_t cc, int flags, const struct sockaddr *to,
+ int tolen)
{
- register struct whod *w = (struct whod *)buf;
- register struct whoent *we;
- struct sockaddr_in *sin = (struct sockaddr_in *)to;
+ struct whod *w;
+ struct whoent *we;
+ struct sockaddr_in *sin;
+ w = (struct whod *)buf;
+ sin = (struct sockaddr_in *)to;
printf("sendto %x.%d\n", ntohl(sin->sin_addr.s_addr),
- ntohs(sin->sin_port));
+ ntohs(sin->sin_port));
printf("hostname %s %s\n", w->wd_hostname,
- interval(ntohl(w->wd_sendtime) - ntohl(w->wd_boottime), " up"));
+ interval(ntohl(w->wd_sendtime) - ntohl(w->wd_boottime), " up"));
printf("load %4.2f, %4.2f, %4.2f\n",
ntohl(w->wd_loadav[0]) / 100.0, ntohl(w->wd_loadav[1]) / 100.0,
ntohl(w->wd_loadav[2]) / 100.0);
cc -= WHDRSIZE;
for (we = w->wd_we, cc /= sizeof(struct whoent); cc > 0; cc--, we++) {
time_t t = _time32_to_time(ntohl(we->we_utmp.out_time));
- printf("%-8.8s %s:%s %.12s",
- we->we_utmp.out_name,
- w->wd_hostname, we->we_utmp.out_line,
- ctime(&t)+4);
+
+ printf("%-8.8s %s:%s %.12s", we->we_utmp.out_name,
+ w->wd_hostname, we->we_utmp.out_line, ctime(&t) + 4);
we->we_idle = ntohl(we->we_idle) / 60;
- if (we->we_idle) {
- if (we->we_idle >= 100*60)
- we->we_idle = 100*60 - 1;
+ if (we->we_idle != 0) {
+ if (we->we_idle >= 100 * 60)
+ we->we_idle = 100 * 60 - 1;
if (we->we_idle >= 60)
printf(" %2d", we->we_idle / 60);
else
@@ -685,26 +746,27 @@ Sendto(s, buf, cc, flags, to, tolen)
}
char *
-interval(time, updown)
- int time;
- char *updown;
+interval(int time, char *updown)
{
static char resbuf[32];
int days, hours, minutes;
- if (time < 0 || time > 3*30*24*60*60) {
+ if (time < 0 || time > 3 * 30 * 24 * 60 * 60) {
(void) sprintf(resbuf, " %s ??:??", updown);
return (resbuf);
}
minutes = (time + 59) / 60; /* round to minutes */
- hours = minutes / 60; minutes %= 60;
- days = hours / 24; hours %= 24;
- if (days)
+ hours = minutes / 60;
+ minutes %= 60;
+ days = hours / 24;
+ hours %= 24;
+ if (days > 0) {
(void) sprintf(resbuf, "%s %2d+%02d:%02d",
updown, days, hours, minutes);
- else
+ } else {
(void) sprintf(resbuf, "%s %2d:%02d",
updown, hours, minutes);
+ }
return (resbuf);
}
#endif
diff --git a/usr.sbin/wpa/Makefile.crypto b/usr.sbin/wpa/Makefile.crypto
index e1ac445..94367bb 100644
--- a/usr.sbin/wpa/Makefile.crypto
+++ b/usr.sbin/wpa/Makefile.crypto
@@ -1,20 +1,24 @@
# $FreeBSD$
.if ${MK_OPENSSL} != "no" && !defined(RELEASE_CRUNCH)
-SRCS+= crypto_openssl.c
+SRCS+= crypto_openssl.c random.c sha1-prf.c sha256-prf.c
DPADD+= ${LIBSSL} ${LIBCRYPTO}
LDADD+= -lssl -lcrypto
+CFLAGS+= -DCONFIG_SHA256
.else
CFLAGS+=-DCONFIG_CRYPTO_INTERNAL
-SRCS+= crypto_internal.c
+SRCS+= crypto_internal.c random.c
CONFIG_INTERNAL_AES=y
CONFIG_INTERNAL_DES=y
CONFIG_INTERNAL_MD4=y
CONFIG_INTERNAL_MD5=y
CONFIG_INTERNAL_RC4=y
CONFIG_INTERNAL_SHA1=y
+NEED_SHA256=y
CONFIG_INTERNAL_SHA256=y
CONFIG_INTERNAL_TLS=y
+CONFIG_INTERNAL_DH5=y
+CONFIG_INTERNAL_DH=y
NEED_AES_ENC=true
.endif
@@ -105,17 +109,25 @@ SRCS+= rc4.c
.endif
.if defined(CONFIG_INTERNAL_SHA1)
-SRCS+= sha1-internal.c
+SRCS+= sha1-internal.c sha1-pbkdf2.c sha1.c sha1-prf.c
.endif
.if defined(NEED_SHA256)
CFLAGS+=-DCONFIG_SHA256
SRCS+= sha256.c
.if defined(CONFIG_INTERNAL_SHA256)
-SRCS+= sha256-internal.c
+SRCS+= sha256-internal.c sha256-prf.c
.endif
.endif
.if defined(NEED_TLS_PRF)
SRCS+= sha1-tlsprf.c
.endif
+
+.if defined(CONFIG_INTERNAL_DH5)
+SRCS+= dh_group5.c
+.endif
+
+.if defined(CONFIG_INTERNAL_DH)
+SRCS+= dh_groups.c
+.endif
diff --git a/usr.sbin/wpa/Makefile.inc b/usr.sbin/wpa/Makefile.inc
index bbd55fc..0b13b97 100644
--- a/usr.sbin/wpa/Makefile.inc
+++ b/usr.sbin/wpa/Makefile.inc
@@ -19,7 +19,8 @@ HOSTAPD_DISTDIR?= ${WPA_DISTDIR}/hostapd
${WPA_DISTDIR}/src/radius \
${WPA_DISTDIR}/src/rsn_supp \
${WPA_DISTDIR}/src/tls \
- ${WPA_DISTDIR}/src/utils
+ ${WPA_DISTDIR}/src/utils \
+ ${WPA_DISTDIR}/src/wps
CFLAGS+=-I${.CURDIR}
CFLAGS+=-I${HOSTAPD_DISTDIR}
@@ -29,6 +30,7 @@ CFLAGS+=-I${WPA_DISTDIR}/src/crypto
CFLAGS+=-I${WPA_DISTDIR}/src/drivers
CFLAGS+=-I${WPA_DISTDIR}/src/l2_packet
CFLAGS+=-I${WPA_DISTDIR}/src/utils
+CFLAGS+=-I${WPA_DISTDIR}/src/wps
CFLAGS+= -DCONFIG_CTRL_IFACE
CFLAGS+= -DCONFIG_CTRL_IFACE_UNIX
diff --git a/usr.sbin/wpa/hostapd/Makefile b/usr.sbin/wpa/hostapd/Makefile
index 273c127..530cf02 100644
--- a/usr.sbin/wpa/hostapd/Makefile
+++ b/usr.sbin/wpa/hostapd/Makefile
@@ -6,55 +6,22 @@
${WPA_DISTDIR}/src/drivers
PROG= hostapd
-SRCS= accounting.c \
- aes-wrap.c \
- ap_config.c \
- ap_drv_ops.c \
- ap_mlme.c \
- authsrv.c \
- base64.c \
- chap.c \
- common.c \
- config_file.c \
- ctrl_iface.c \
- ctrl_iface_ap.c \
- drivers.c \
- drv_callbacks.c \
- eap_common.c \
- eap_peap_common.c \
- eap_register.c \
- eapol_auth_dump.c \
- eapol_auth_sm.c \
- eap_server.c \
- eap_server_methods.c \
- eloop.c \
- hostapd.c \
- ieee802_11_auth.c \
- ieee802_11_common.c \
- ieee802_1x.c \
- ip_addr.c \
- main.c \
- md5.c \
- ms_funcs.c \
- os_unix.c \
- peerkey_auth.c \
- pmksa_cache_auth.c \
- preauth_auth.c \
- radius.c \
- radius_client.c \
- sha1-pbkdf2.c \
- sha1.c \
- sta_info.c \
- tkip_countermeasures.c \
- utils.c \
- vlan_init.c \
- wpa_auth.c \
- wpa_auth_glue.c \
- wpa_auth_ie.c \
- wpa_common.c \
- wpa_debug.c \
- wpabuf.c
-SRCS+= l2_packet_freebsd.c driver_freebsd.c
+SRCS= accounting.c aes-wrap.c ap_config.c ap_drv_ops.c ap_mlme.c authsrv.c \
+ base64.c beacon.c chap.c common.c config_file.c ctrl_iface.c \
+ ctrl_iface_ap.c driver_common.c l2_packet_freebsd.c driver_bsd.c \
+ drivers.c drv_callbacks.c eap_common.c eap_peap_common.c \
+ eap_register.c eap_server.c eap_server_methods.c eap_user_db.c \
+ eapol_auth_dump.c eapol_auth_sm.c eloop.c gas.c gas_serv.c hostapd.c \
+ hs20.c http_client.c http_server.c httpread.c ieee802_11_auth.c \
+ ieee802_11_common.c ieee802_11_shared.c ieee802_1x.c ip_addr.c \
+ main.c md5.c ms_funcs.c os_unix.c peerkey_auth.c pmksa_cache_auth.c \
+ preauth_auth.c radius.c radius_client.c radius_das.c sta_info.c \
+ tkip_countermeasures.c upnp_xml.c utils.c uuid.c vlan_init.c \
+ wpa_auth.c wpa_auth_glue.c wpa_auth_ie.c wpa_common.c wpa_debug.c \
+ wpabuf.c wps.c wps_attr_build.c wps_attr_parse.c wps_attr_process.c \
+ wps_common.c wps_dev_attr.c wps_enrollee.c wps_hostapd.c \
+ wps_registrar.c wps_upnp.c wps_upnp_ap.c wps_upnp_event.c \
+ wps_upnp_ssdp.c wps_upnp_web.c
MAN= hostapd.8 hostapd.conf.5
@@ -68,7 +35,12 @@ CFLAGS+=-DCONFIG_DRIVER_BSD \
-DHOSTAPD \
-DCONFIG_DRIVER_RADIUS_ACL \
-DCONFIG_RSN_PREAUTH \
- -DCONFIG_PEERKEY
+ -DCONFIG_PEERKEY \
+ -DCONFIG_WPS \
+ -DCONFIG_WPS2 \
+ -DCONFIG_WPS_UPNP \
+ -DCONFIG_INTERWORKING \
+ -DCONFIG_HS20
.if ${MK_INET6} != "no"
CFLAGS+= -DCONFIG_IPV6
.endif
@@ -92,6 +64,7 @@ CFLAGS+=-DDPKCS12_FUNCS \
-DEAP_SERVER_TLS \
-DEAP_SERVER_TTLS \
-DEAP_TLS_FUNCS \
+ -DEAP_SERVER_WSC \
-DCONFIG_NO_DUMP_STATE
SRCS+= dump_state.c \
eap_server_gtc.c \
@@ -101,9 +74,14 @@ SRCS+= dump_state.c \
eap_server_peap.c \
eap_server_tls.c \
eap_server_tls_common.c \
- eap_server_ttls.c
+ eap_server_ttls.c \
+ eap_server_wsc.c \
+ eap_wsc_common.c
TLS_FUNCS=y
-NEED_SHA256=y
+
+.if !empty(CFLAGS:M*-DCONFIG_WPS)
+NEED_SIM_COMMON=y
+.endif
.if !empty(CFLAGS:M*-DEAP_SERVER_AKA)
SRCS+= eap_server_aka.c
diff --git a/usr.sbin/wpa/hostapd/driver_freebsd.c b/usr.sbin/wpa/hostapd/driver_freebsd.c
deleted file mode 100644
index 9b9d7d4..0000000
--- a/usr.sbin/wpa/hostapd/driver_freebsd.c
+++ /dev/null
@@ -1,787 +0,0 @@
-/*
- * Host AP - driver interaction with BSD net80211 layer
- * Copyright (c) 2004, Sam Leffler <sam@errno.com>
- * Copyright (c) 2004, 2Wire, Inc
- *
- * This program is free software; you can redistribute it and/or modify
- * it under the terms of the GNU General Public License version 2 as
- * published by the Free Software Foundation.
- *
- * Alternatively, this software may be distributed under the terms of BSD
- * license.
- *
- * See README and COPYING for more details.
- *
- * $FreeBSD$
- */
-
-#include "includes.h"
-#include <sys/ioctl.h>
-
-#include "common.h"
-#include "driver.h"
-#include "eloop.h"
-#include "common/ieee802_11_defs.h"
-#include "common/wpa_common.h"
-
-#include <sys/socket.h>
-#include <net/if.h>
-#include <net/route.h>
-#include <netinet/in.h>
-
-#include <net80211/ieee80211_ioctl.h>
-#include <net80211/ieee80211_freebsd.h>
-
-#include "l2_packet/l2_packet.h"
-
-struct bsd_driver_data {
- struct hostapd_data *hapd; /* back pointer */
-
- int sock; /* open socket for 802.11 ioctls */
- struct l2_packet_data *sock_xmit;/* raw packet xmit socket */
- int route; /* routing socket for events */
- char ifname[IFNAMSIZ+1]; /* interface name */
- unsigned int ifindex; /* interface index */
- void *ctx;
- struct wpa_driver_capa capa; /* driver capability */
- int is_ap; /* Access point mode */
- int prev_roaming; /* roaming state to restore on deinit */
- int prev_privacy; /* privacy state to restore on deinit */
- int prev_wpa; /* wpa state to restore on deinit */
-};
-
-static int
-bsd_set80211(void *priv, int op, int val, const void *arg, int arg_len)
-{
- struct bsd_driver_data *drv = priv;
- struct ieee80211req ireq;
-
- os_memset(&ireq, 0, sizeof(ireq));
- os_strlcpy(ireq.i_name, drv->ifname, sizeof(ireq.i_name));
- ireq.i_type = op;
- ireq.i_val = val;
- ireq.i_data = (void *) arg;
- ireq.i_len = arg_len;
-
- if (ioctl(drv->sock, SIOCS80211, &ireq) < 0) {
- wpa_printf(MSG_ERROR, "ioctl[SIOCS80211, op=%u, val=%u, "
- "arg_len=%u]: %s", op, val, arg_len,
- strerror(errno));
- return -1;
- }
- return 0;
-}
-
-static int
-bsd_get80211(void *priv, struct ieee80211req *ireq, int op, void *arg,
- int arg_len)
-{
- struct bsd_driver_data *drv = priv;
-
- os_memset(ireq, 0, sizeof(*ireq));
- os_strlcpy(ireq->i_name, drv->ifname, sizeof(ireq->i_name));
- ireq->i_type = op;
- ireq->i_len = arg_len;
- ireq->i_data = arg;
-
- if (ioctl(drv->sock, SIOCG80211, ireq) < 0) {
- wpa_printf(MSG_ERROR, "ioctl[SIOCS80211, op=%u, "
- "arg_len=%u]: %s", op, arg_len, strerror(errno));
- return -1;
- }
- return 0;
-}
-
-static int
-get80211var(struct bsd_driver_data *drv, int op, void *arg, int arg_len)
-{
- struct ieee80211req ireq;
-
- if (bsd_get80211(drv, &ireq, op, arg, arg_len) < 0)
- return -1;
- return ireq.i_len;
-}
-
-static int
-set80211var(struct bsd_driver_data *drv, int op, const void *arg, int arg_len)
-{
- return bsd_set80211(drv, op, 0, arg, arg_len);
-}
-
-static int
-set80211param(struct bsd_driver_data *drv, int op, int arg)
-{
- return bsd_set80211(drv, op, arg, NULL, 0);
-}
-
-static int
-bsd_get_ssid(void *priv, u8 *ssid, int len)
-{
- struct bsd_driver_data *drv = priv;
-
- return get80211var(drv, IEEE80211_IOC_SSID, ssid, IEEE80211_NWID_LEN);
-}
-
-static int
-bsd_set_ssid(void *priv, const u8 *ssid, int ssid_len)
-{
- struct bsd_driver_data *drv = priv;
-
- return set80211var(drv, IEEE80211_IOC_SSID, ssid, ssid_len);
-}
-
-static int
-bsd_del_key(void *priv, const u8 *addr, int key_idx)
-{
- struct ieee80211req_del_key wk;
-
- os_memset(&wk, 0, sizeof(wk));
- if (addr == NULL) {
- wpa_printf(MSG_DEBUG, "%s: key_idx=%d", __func__, key_idx);
- wk.idk_keyix = key_idx;
- } else {
- wpa_printf(MSG_DEBUG, "%s: addr=" MACSTR, __func__,
- MAC2STR(addr));
- os_memcpy(wk.idk_macaddr, addr, IEEE80211_ADDR_LEN);
- wk.idk_keyix = (u_int8_t) IEEE80211_KEYIX_NONE; /* XXX */
- }
-
- return set80211var(priv, IEEE80211_IOC_DELKEY, &wk, sizeof(wk));
-}
-
-static int
-bsd_send_mlme_param(void *priv, const u8 op, const u16 reason, const u8 *addr)
-{
- struct ieee80211req_mlme mlme;
-
- os_memset(&mlme, 0, sizeof(mlme));
- mlme.im_op = op;
- mlme.im_reason = reason;
- os_memcpy(mlme.im_macaddr, addr, IEEE80211_ADDR_LEN);
- return set80211var(priv, IEEE80211_IOC_MLME, &mlme, sizeof(mlme));
-}
-
-static int
-bsd_ctrl_iface(void *priv, int enable)
-{
- struct bsd_driver_data *drv = priv;
- struct ifreq ifr;
-
- if (drv->sock < 0)
- return -1;
-
- os_memset(&ifr, 0, sizeof(ifr));
- os_strlcpy(ifr.ifr_name, drv->ifname, sizeof(ifr.ifr_name));
-
- if (ioctl(drv->sock, SIOCGIFFLAGS, &ifr) < 0) {
- perror("ioctl[SIOCGIFFLAGS]");
- return -1;
- }
-
- if (enable) {
- if ((ifr.ifr_flags & IFF_UP) == IFF_UP)
- return 0;
- ifr.ifr_flags |= IFF_UP;
- } else {
- if ((ifr.ifr_flags & IFF_UP) == 0)
- return 0;
- ifr.ifr_flags &= ~IFF_UP;
- }
-
- if (ioctl(drv->sock, SIOCSIFFLAGS, &ifr) < 0) {
- perror("ioctl[SIOCSIFFLAGS]");
- return -1;
- }
-
- return 0;
-}
-
-static int
-bsd_commit(void *priv)
-{
- return bsd_ctrl_iface(priv, 1);
-}
-
-static int
-bsd_set_key(const char *ifname, void *priv, enum wpa_alg alg,
- const unsigned char *addr, int key_idx, int set_tx, const u8 *seq,
- size_t seq_len, const u8 *key, size_t key_len)
-{
- struct ieee80211req_key wk;
-
- wpa_printf(MSG_DEBUG, "%s: alg=%d addr=%p key_idx=%d set_tx=%d "
- "seq_len=%zu key_len=%zu", __func__, alg, addr, key_idx,
- set_tx, seq_len, key_len);
-
- if (alg == WPA_ALG_NONE) {
- return bsd_del_key(priv, addr, key_idx);
- }
-
- os_memset(&wk, 0, sizeof(wk));
- switch (alg) {
- case WPA_ALG_WEP:
- wk.ik_type = IEEE80211_CIPHER_WEP;
- break;
- case WPA_ALG_TKIP:
- wk.ik_type = IEEE80211_CIPHER_TKIP;
- break;
- case WPA_ALG_CCMP:
- wk.ik_type = IEEE80211_CIPHER_AES_CCM;
- break;
- default:
- wpa_printf(MSG_ERROR, "%s: unknown alg=%d", __func__, alg);
- return -1;
- }
-
- wk.ik_flags = IEEE80211_KEY_RECV;
- if (set_tx)
- wk.ik_flags |= IEEE80211_KEY_XMIT;
-
- if (addr == NULL) {
- os_memset(wk.ik_macaddr, 0xff, IEEE80211_ADDR_LEN);
- wk.ik_keyix = key_idx;
- } else {
- os_memcpy(wk.ik_macaddr, addr, IEEE80211_ADDR_LEN);
- /*
- * Deduce whether group/global or unicast key by checking
- * the address (yech). Note also that we can only mark global
- * keys default; doing this for a unicast key is an error.
- */
- if (os_memcmp(addr, "\xff\xff\xff\xff\xff\xff",
- IEEE80211_ADDR_LEN) == 0) {
- wk.ik_flags |= IEEE80211_KEY_GROUP;
- wk.ik_keyix = key_idx;
- } else {
- wk.ik_keyix = key_idx == 0 ? IEEE80211_KEYIX_NONE :
- key_idx;
- }
- }
- if (wk.ik_keyix != IEEE80211_KEYIX_NONE && set_tx)
- wk.ik_flags |= IEEE80211_KEY_DEFAULT;
- wk.ik_keylen = key_len;
- os_memcpy(&wk.ik_keyrsc, seq, seq_len);
- os_memcpy(wk.ik_keydata, key, key_len);
-
- return set80211var(priv, IEEE80211_IOC_WPAKEY, &wk, sizeof(wk));
-}
-
-static int
-bsd_configure_wpa(void *priv, struct wpa_bss_params *params)
-{
- wpa_printf(MSG_DEBUG, "%s: enable WPA= 0x%x", __func__, params->wpa);
- if (set80211param(priv, IEEE80211_IOC_WPA, params->wpa)) {
- printf("Unable to set WPA to %u\n", params->wpa);
- return -1;
- }
- return 0;
-}
-
-static int
-bsd_set_ieee8021x(void *priv, struct wpa_bss_params *params)
-{
- wpa_printf(MSG_DEBUG, "%s: enabled=%d", __func__, params->enabled);
-
- if (!params->enabled) {
- /* XXX restore state */
- return set80211param(priv, IEEE80211_IOC_AUTHMODE,
- IEEE80211_AUTH_AUTO);
- }
- if (!params->wpa && !params->ieee802_1x) {
- wpa_printf(MSG_ERROR, "%s: No 802.1X or WPA enabled",
- __func__);
- return -1;
- }
- if (params->wpa && bsd_configure_wpa(priv, params) != 0) {
- wpa_printf(MSG_ERROR, "%s: Failed to configure WPA state",
- __func__);
- return -1;
- }
- if (set80211param(priv, IEEE80211_IOC_AUTHMODE,
- (params->wpa ? IEEE80211_AUTH_WPA : IEEE80211_AUTH_8021X))) {
- wpa_printf(MSG_ERROR, "%s: Failed to enable WPA/802.1X",
- __func__);
- return -1;
- }
- return 0;
-}
-
-static int
-bsd_set_sta_authorized(void *priv, const u8 *addr,
- int total_flags, int flags_or, int flags_and)
-{
- int authorized = -1;
-
- /* For now, only support setting Authorized flag */
- if (flags_or & WPA_STA_AUTHORIZED)
- authorized = 1;
- if (!(flags_and & WPA_STA_AUTHORIZED))
- authorized = 0;
-
- if (authorized < 0)
- return 0;
-
- return bsd_send_mlme_param(priv, authorized ?
- IEEE80211_MLME_AUTHORIZE :
- IEEE80211_MLME_UNAUTHORIZE, 0, addr);
-}
-
-static void
-bsd_new_sta(void *priv, void *ctx, u8 addr[IEEE80211_ADDR_LEN])
-{
- struct ieee80211req_wpaie ie;
- int ielen = 0;
- u8 *iebuf = NULL;
-
- /*
- * Fetch and validate any negotiated WPA/RSN parameters.
- */
- memset(&ie, 0, sizeof(ie));
- memcpy(ie.wpa_macaddr, addr, IEEE80211_ADDR_LEN);
- if (get80211var(priv, IEEE80211_IOC_WPAIE, &ie, sizeof(ie)) < 0) {
- printf("Failed to get WPA/RSN information element.\n");
- goto no_ie;
- }
- iebuf = ie.wpa_ie;
- ielen = ie.wpa_ie[1];
- if (ielen == 0)
- iebuf = NULL;
- else
- ielen += 2;
-
-no_ie:
- drv_event_assoc(ctx, addr, iebuf, ielen);
-}
-
-static int
-bsd_send_eapol(void *priv, const u8 *addr, const u8 *data, size_t data_len,
- int encrypt, const u8 *own_addr)
-{
- struct bsd_driver_data *drv = priv;
-
- wpa_hexdump(MSG_MSGDUMP, "TX EAPOL", data, data_len);
-
- return l2_packet_send(drv->sock_xmit, addr, ETH_P_EAPOL, data,
- data_len);
-}
-
-static int
-bsd_set_opt_ie(void *priv, const u8 *ie, size_t ie_len)
-{
- wpa_printf(MSG_DEBUG, "%s: set WPA+RSN ie (len %lu)", __func__,
- (unsigned long)ie_len);
- return bsd_set80211(priv, IEEE80211_IOC_APPIE, IEEE80211_APPIE_WPA,
- ie, ie_len);
-}
-
-/*
- * Avoid conflicts with hostapd definitions by undefining couple of defines
- * from net80211 header files.
- */
-#undef RSN_VERSION
-#undef WPA_VERSION
-#undef WPA_OUI_TYPE
-
-static int bsd_sta_deauth(void *priv, const u8 *own_addr, const u8 *addr,
- int reason_code);
-
-static const char *
-ether_sprintf(const u8 *addr)
-{
- static char buf[sizeof(MACSTR)];
-
- if (addr != NULL)
- snprintf(buf, sizeof(buf), MACSTR, MAC2STR(addr));
- else
- snprintf(buf, sizeof(buf), MACSTR, 0,0,0,0,0,0);
- return buf;
-}
-
-static int
-bsd_set_privacy(void *priv, int enabled)
-{
- wpa_printf(MSG_DEBUG, "%s: enabled=%d", __func__, enabled);
-
- return set80211param(priv, IEEE80211_IOC_PRIVACY, enabled);
-}
-
-static int
-bsd_get_seqnum(const char *ifname, void *priv, const u8 *addr, int idx,
- u8 *seq)
-{
- struct ieee80211req_key wk;
-
- wpa_printf(MSG_DEBUG, "%s: addr=%s idx=%d",
- __func__, ether_sprintf(addr), idx);
-
- memset(&wk, 0, sizeof(wk));
- if (addr == NULL)
- memset(wk.ik_macaddr, 0xff, IEEE80211_ADDR_LEN);
- else
- memcpy(wk.ik_macaddr, addr, IEEE80211_ADDR_LEN);
- wk.ik_keyix = idx;
-
- if (get80211var(priv, IEEE80211_IOC_WPAKEY, &wk, sizeof(wk)) < 0) {
- printf("Failed to get encryption.\n");
- return -1;
- }
-
-#ifdef WORDS_BIGENDIAN
- {
- /*
- * wk.ik_keytsc is in host byte order (big endian), need to
- * swap it to match with the byte order used in WPA.
- */
- int i;
- u8 tmp[WPA_KEY_RSC_LEN];
- memcpy(tmp, &wk.ik_keytsc, sizeof(wk.ik_keytsc));
- for (i = 0; i < WPA_KEY_RSC_LEN; i++) {
- seq[i] = tmp[WPA_KEY_RSC_LEN - i - 1];
- }
- }
-#else /* WORDS_BIGENDIAN */
- memcpy(seq, &wk.ik_keytsc, sizeof(wk.ik_keytsc));
-#endif /* WORDS_BIGENDIAN */
- return 0;
-}
-
-
-static int
-bsd_flush(void *priv)
-{
- u8 allsta[IEEE80211_ADDR_LEN];
-
- memset(allsta, 0xff, IEEE80211_ADDR_LEN);
- return bsd_sta_deauth(priv, NULL, allsta, IEEE80211_REASON_AUTH_LEAVE);
-}
-
-
-static int
-bsd_read_sta_driver_data(void *priv, struct hostap_sta_driver_data *data,
- const u8 *addr)
-{
- struct ieee80211req_sta_stats stats;
-
- memcpy(stats.is_u.macaddr, addr, IEEE80211_ADDR_LEN);
- if (get80211var(priv, IEEE80211_IOC_STA_STATS, &stats, sizeof(stats))
- > 0) {
- /* XXX? do packets counts include non-data frames? */
- data->rx_packets = stats.is_stats.ns_rx_data;
- data->rx_bytes = stats.is_stats.ns_rx_bytes;
- data->tx_packets = stats.is_stats.ns_tx_data;
- data->tx_bytes = stats.is_stats.ns_tx_bytes;
- }
- return 0;
-}
-
-static int
-bsd_sta_clear_stats(void *priv, const u8 *addr)
-{
- struct ieee80211req_sta_stats stats;
-
- wpa_printf(MSG_DEBUG, "%s: addr=%s", __func__, ether_sprintf(addr));
-
- /* zero station statistics */
- memset(&stats, 0, sizeof(stats));
- memcpy(stats.is_u.macaddr, addr, IEEE80211_ADDR_LEN);
- return set80211var(priv, IEEE80211_IOC_STA_STATS, &stats,
- sizeof(stats));
-}
-
-static int
-bsd_sta_deauth(void *priv, const u8 *own_addr, const u8 *addr, int reason_code)
-{
- return bsd_send_mlme_param(priv, IEEE80211_MLME_DEAUTH, reason_code,
- addr);
-}
-
-static int
-bsd_sta_disassoc(void *priv, const u8 *own_addr, const u8 *addr,
- int reason_code)
-{
- return bsd_send_mlme_param(priv, IEEE80211_MLME_DISASSOC, reason_code,
- addr);
-}
-
-static void
-bsd_wireless_event_receive(int sock, void *ctx, void *sock_ctx)
-{
- struct bsd_driver_data *drv = ctx;
- char buf[2048];
- struct if_announcemsghdr *ifan;
- struct rt_msghdr *rtm;
- struct ieee80211_michael_event *mic;
- struct ieee80211_join_event *join;
- struct ieee80211_leave_event *leave;
-#ifdef CONFIG_DRIVER_RADIUS_ACL
- struct ieee80211_auth_event *auth;
-#endif
- int n;
- union wpa_event_data data;
-
- n = read(sock, buf, sizeof(buf));
- if (n < 0) {
- if (errno != EINTR && errno != EAGAIN)
- perror("read(PF_ROUTE)");
- return;
- }
-
- rtm = (struct rt_msghdr *) buf;
- if (rtm->rtm_version != RTM_VERSION) {
- wpa_printf(MSG_DEBUG, "Routing message version %d not "
- "understood\n", rtm->rtm_version);
- return;
- }
- ifan = (struct if_announcemsghdr *) rtm;
- if (ifan->ifan_index != drv->ifindex) {
- wpa_printf(MSG_DEBUG, "Discard routing message to if#%d "
- "(not for us %d)\n",
- ifan->ifan_index, drv->ifindex);
- return;
- }
- switch (rtm->rtm_type) {
- case RTM_IEEE80211:
- switch (ifan->ifan_what) {
- case RTM_IEEE80211_ASSOC:
- case RTM_IEEE80211_REASSOC:
- case RTM_IEEE80211_DISASSOC:
- case RTM_IEEE80211_SCAN:
- break;
- case RTM_IEEE80211_LEAVE:
- leave = (struct ieee80211_leave_event *) &ifan[1];
- drv_event_disassoc(drv->hapd, leave->iev_addr);
- break;
- case RTM_IEEE80211_JOIN:
-#ifdef RTM_IEEE80211_REJOIN
- case RTM_IEEE80211_REJOIN:
-#endif
- join = (struct ieee80211_join_event *) &ifan[1];
- bsd_new_sta(drv, drv->hapd, join->iev_addr);
- break;
- case RTM_IEEE80211_REPLAY:
- /* ignore */
- break;
- case RTM_IEEE80211_MICHAEL:
- mic = (struct ieee80211_michael_event *) &ifan[1];
- wpa_printf(MSG_DEBUG,
- "Michael MIC failure wireless event: "
- "keyix=%u src_addr=" MACSTR, mic->iev_keyix,
- MAC2STR(mic->iev_src));
- os_memset(&data, 0, sizeof(data));
- data.michael_mic_failure.unicast = 1;
- data.michael_mic_failure.src = mic->iev_src;
- wpa_supplicant_event(drv->hapd,
- EVENT_MICHAEL_MIC_FAILURE, &data);
- break;
-#ifdef CONFIG_DRIVER_RADIUS_ACL_NOT_YET
- case RTM_IEEE80211_AUTH:
- auth = (struct ieee80211_auth_event *) &ifan[1];
- wpa_printf(MSG_DEBUG, "802.11 AUTH, STA = " MACSTR,
- MAC2STR(auth->iev_addr));
- n = hostapd_allowed_address(drv->hapd, auth->iev_addr,
- NULL, 0, NULL, NULL, NULL);
- switch (n) {
- case HOSTAPD_ACL_ACCEPT:
- case HOSTAPD_ACL_REJECT:
- hostapd_set_radius_acl_auth(drv->hapd,
- auth->iev_addr, n, 0);
- wpa_printf(MSG_DEBUG,
- "802.11 AUTH, STA = " MACSTR " hostapd says: %s",
- MAC2STR(auth->iev_addr),
- (n == HOSTAPD_ACL_ACCEPT ?
- "ACCEPT" : "REJECT" ));
- break;
- case HOSTAPD_ACL_PENDING:
- wpa_printf(MSG_DEBUG,
- "802.11 AUTH, STA = " MACSTR " pending",
- MAC2STR(auth->iev_addr));
- break;
- }
- break;
-#endif /* CONFIG_DRIVER_RADIUS_ACL */
- }
- break;
- }
-}
-
-static void
-handle_read(void *ctx, const u8 *src_addr, const u8 *buf, size_t len)
-{
- struct bsd_driver_data *drv = ctx;
- drv_event_eapol_rx(drv->hapd, src_addr, buf, len);
-}
-
-static int
-bsd_set_countermeasures(void *priv, int enabled)
-{
- wpa_printf(MSG_DEBUG, "%s: enabled=%d", __FUNCTION__, enabled);
- return set80211param(priv, IEEE80211_IOC_COUNTERMEASURES, enabled);
-}
-
-#ifdef CONFIG_DRIVER_RADIUS_ACL_NOT_YET
-static int
-bsd_set_radius_acl_auth(void *priv, const u8 *mac, int accepted,
- u32 session_timeout)
-{
- struct bsd_driver_data *drv = priv;
- struct hostapd_data *hapd = drv->hapd;
- struct ieee80211req_mlme mlme;
-
- switch (accepted) {
- case HOSTAPD_ACL_ACCEPT_TIMEOUT:
- wpa_printf(MSG_DEBUG, "[%s] STA " MACSTR
- " has been accepted by RADIUS ACL with timeout "
- "of %d.\n", hapd->conf->iface, MAC2STR(mac),
- session_timeout);
- mlme.im_reason = IEEE80211_STATUS_SUCCESS;
- break;
- case HOSTAPD_ACL_ACCEPT:
- wpa_printf(MSG_DEBUG, "[%s] STA " MACSTR
- " has been accepted by RADIUS ACL.\n",
- hapd->conf->iface, MAC2STR(mac));
- mlme.im_reason = IEEE80211_STATUS_SUCCESS;
- break;
- case HOSTAPD_ACL_REJECT:
- wpa_printf(MSG_DEBUG, "[%s] STA " MACSTR
- " has been rejected by RADIUS ACL.\n",
- hapd->conf->iface, MAC2STR(mac));
- mlme.im_reason = IEEE80211_STATUS_UNSPECIFIED;
- break;
- default:
- wpa_printf(MSG_ERROR, "[%s] STA " MACSTR
- " has unknown status (%d) by RADIUS ACL. "
- "Nothing to do...\n", hapd->conf->iface,
- MAC2STR(mac), accepted);
- return 0;
- }
- memset(&mlme, 0, sizeof(mlme));
- mlme.im_op = IEEE80211_MLME_AUTH;
- memcpy(mlme.im_macaddr, mac, IEEE80211_ADDR_LEN);
- return set80211var(drv, IEEE80211_IOC_MLME, &mlme, sizeof(mlme));
-}
-
-static int
-bsd_set_radius_acl_expire(void *priv, const u8 *mac)
-{
- struct bsd_driver_data *drv = priv;
- struct hostapd_data *hapd = drv->hapd;
-
- /*
- * The expiry of the MAC address from RADIUS ACL cache doesn't mean
- * that we should kick off the client. Our current approach doesn't
- * require adding/removing entries from an allow/deny list; so this
- * function is likely unnecessary
- */
- wpa_printf(MSG_DEBUG, "[%s] STA " MACSTR " radius acl cache "
- "expired; nothing to do...", hapd->conf->iface,
- MAC2STR(mac));
- return 0;
-}
-#endif /* CONFIG_DRIVER_RADIUS_ACL */
-
-static void *
-bsd_init(struct hostapd_data *hapd, struct wpa_init_params *params)
-{
- struct bsd_driver_data *drv;
-
- drv = os_zalloc(sizeof(struct bsd_driver_data));
- if (drv == NULL) {
- printf("Could not allocate memory for bsd driver data\n");
- goto bad;
- }
-
- drv->hapd = hapd;
- drv->sock = socket(PF_INET, SOCK_DGRAM, 0);
- if (drv->sock < 0) {
- perror("socket[PF_INET,SOCK_DGRAM]");
- goto bad;
- }
- os_strlcpy(drv->ifname, params->ifname, sizeof(drv->ifname));
- /*
- * NB: We require the interface name be mappable to an index.
- * This implies we do not support having wpa_supplicant
- * wait for an interface to appear. This seems ok; that
- * doesn't belong here; it's really the job of devd.
- * XXXSCW: devd is FreeBSD-specific.
- */
- drv->ifindex = if_nametoindex(drv->ifname);
- if (drv->ifindex == 0) {
- printf("%s: interface %s does not exist", __func__,
- drv->ifname);
- goto bad;
- }
-
- drv->sock_xmit = l2_packet_init(drv->ifname, NULL, ETH_P_EAPOL,
- handle_read, drv, 0);
- if (drv->sock_xmit == NULL)
- goto bad;
- if (l2_packet_get_own_addr(drv->sock_xmit, params->own_addr))
- goto bad;
-
- /* mark down during setup */
- if (bsd_ctrl_iface(drv, 0) < 0)
- goto bad;
-
- drv->route = socket(PF_ROUTE, SOCK_RAW, 0);
- if (drv->route < 0) {
- perror("socket(PF_ROUTE,SOCK_RAW)");
- goto bad;
- }
- eloop_register_read_sock(drv->route, bsd_wireless_event_receive, drv,
- NULL);
-
- return drv;
-bad:
- if (drv == NULL)
- return NULL;
- if (drv->sock_xmit != NULL)
- l2_packet_deinit(drv->sock_xmit);
- if (drv->sock >= 0)
- close(drv->sock);
- os_free(drv);
- return NULL;
-}
-
-
-static void
-bsd_deinit(void *priv)
-{
- struct bsd_driver_data *drv = priv;
-
- if (drv->route >= 0) {
- eloop_unregister_read_sock(drv->route);
- close(drv->route);
- }
- bsd_ctrl_iface(drv, 0);
- if (drv->sock >= 0)
- close(drv->sock);
- if (drv->sock_xmit != NULL)
- l2_packet_deinit(drv->sock_xmit);
- os_free(drv);
-}
-
-const struct wpa_driver_ops wpa_driver_bsd_ops = {
- .name = "bsd",
- .desc = "BSD 802.11 support",
- .hapd_init = bsd_init,
- .hapd_deinit = bsd_deinit,
- .set_privacy = bsd_set_privacy,
- .get_seqnum = bsd_get_seqnum,
- .flush = bsd_flush,
- .read_sta_data = bsd_read_sta_driver_data,
- .sta_clear_stats = bsd_sta_clear_stats,
- .sta_disassoc = bsd_sta_disassoc,
- .sta_deauth = bsd_sta_deauth,
- .set_key = bsd_set_key,
- .set_ieee8021x = bsd_set_ieee8021x,
- .hapd_set_ssid = bsd_set_ssid,
- .hapd_get_ssid = bsd_get_ssid,
- .hapd_send_eapol = bsd_send_eapol,
- .sta_set_flags = bsd_set_sta_authorized,
- .set_generic_elem = bsd_set_opt_ie,
- .set_countermeasures = bsd_set_countermeasures,
- .commit = bsd_commit,
-#ifdef CONFIG_DRIVER_RADIUS_ACL_NOT_YET
- .set_radius_acl_auth = bsd_set_radius_acl_auth,
- .set_radius_acl_expire = bsd_set_radius_acl_expire,
-#endif
-};
diff --git a/usr.sbin/wpa/hostapd_cli/Makefile b/usr.sbin/wpa/hostapd_cli/Makefile
index 667134c..8677fbf 100644
--- a/usr.sbin/wpa/hostapd_cli/Makefile
+++ b/usr.sbin/wpa/hostapd_cli/Makefile
@@ -5,7 +5,7 @@
.PATH.c:${HOSTAPD_DISTDIR}
PROG= hostapd_cli
-SRCS= hostapd_cli.c wpa_ctrl.c os_unix.c
+SRCS= common.c edit.c eloop.c hostapd_cli.c os_unix.c wpa_ctrl.c wpa_debug.c
CFLAGS+= -DCONFIG_CTRL_IFACE
CFLAGS+= -DCONFIG_CTRL_IFACE_UNIX
diff --git a/usr.sbin/wpa/wpa_cli/Makefile b/usr.sbin/wpa/wpa_cli/Makefile
index 1721437..da25325 100644
--- a/usr.sbin/wpa/wpa_cli/Makefile
+++ b/usr.sbin/wpa/wpa_cli/Makefile
@@ -5,7 +5,7 @@
.PATH.c:${WPA_SUPPLICANT_DISTDIR}
PROG= wpa_cli
-SRCS= wpa_cli.c wpa_ctrl.c os_unix.c
+SRCS= common.c edit.c eloop.c os_unix.c wpa_cli.c wpa_ctrl.c wpa_debug.c
MAN= wpa_cli.8
diff --git a/usr.sbin/wpa/wpa_passphrase/Makefile b/usr.sbin/wpa/wpa_passphrase/Makefile
index 385e23c..16321c4 100644
--- a/usr.sbin/wpa/wpa_passphrase/Makefile
+++ b/usr.sbin/wpa/wpa_passphrase/Makefile
@@ -5,8 +5,8 @@
.PATH.c:${WPA_SUPPLICANT_DISTDIR}
PROG= wpa_passphrase
-SRCS= wpa_passphrase.c sha1.c sha1-internal.c sha1-pbkdf2.c \
- md5.c md5-internal.c
+SRCS= common.c md5-internal.c md5.c os_unix.c sha1-internal.c sha1-pbkdf2.c sha1.c \
+ wpa_passphrase.c
CFLAGS+= -DINTERNAL_SHA1
CFLAGS+= -DINTERNAL_MD5
diff --git a/usr.sbin/wpa/wpa_priv/Makefile b/usr.sbin/wpa/wpa_priv/Makefile
new file mode 100644
index 0000000..4dbc631
--- /dev/null
+++ b/usr.sbin/wpa/wpa_priv/Makefile
@@ -0,0 +1,17 @@
+# $FreeBSD$
+
+.include "${.CURDIR}/../Makefile.inc"
+
+.PATH.c:${WPA_SUPPLICANT_DISTDIR} \
+ ${WPA_DISTDIR}/src/drivers
+
+PROG= wpa_priv
+SRCS= drivers.c os_unix.c eloop.c common.c wpa_debug.c wpabuf.c wpa_priv.c \
+ driver_common.c l2_packet_freebsd.c
+
+DPADD+= ${LIBPCAP}
+LDADD+= -lpcap
+
+.include "${.CURDIR}/../Makefile.crypto"
+
+.include <bsd.prog.mk>
diff --git a/usr.sbin/wpa/wpa_supplicant/Makefile b/usr.sbin/wpa/wpa_supplicant/Makefile
index 6c352ab..3424413 100644
--- a/usr.sbin/wpa/wpa_supplicant/Makefile
+++ b/usr.sbin/wpa/wpa_supplicant/Makefile
@@ -6,41 +6,19 @@
${WPA_DISTDIR}/src/drivers
PROG= wpa_supplicant
-SRCS= aes-unwrap.c \
- base64.c \
- blacklist.c \
- bss.c \
- common.c \
- config.c \
- config_file.c \
- ctrl_iface.c \
- ctrl_iface_unix.c \
- driver_ndis.c \
- driver_wired.c \
- drivers.c \
- eap_register.c \
- eloop.c \
- events.c \
- main.c \
- md5.c \
- notify.c \
- os_unix.c \
- peerkey.c \
- pmksa_cache.c \
- preauth.c \
- scan.c \
- sha1-pbkdf2.c \
- sha1.c \
- wpa.c \
- wpa_common.c \
- wpa_debug.c \
- wpa_ie.c \
- wpa_supplicant.c \
- wpabuf.c \
- wpas_glue.c
-SRCS+= driver_freebsd.c \
- l2_packet_freebsd.c \
- Packet32.c
+SRCS= aes-unwrap.c base64.c blacklist.c bss.c common.c config.c \
+ config_file.c ctrl_iface.c ctrl_iface_unix.c driver_bsd.c \
+ driver_common.c driver_ndis.c driver_wired.c drivers.c \
+ eap_register.c eloop.c events.c gas.c gas_query.c hs20.c \
+ hs20_supplicant.c http_client.c http_server.c httpread.c \
+ ieee802_11_common.c interworking.c l2_packet_freebsd.c main.c \
+ md5.c notify.c offchannel.c os_unix.c peerkey.c pmksa_cache.c \
+ preauth.c scan.c upnp_xml.c uuid.c wpa.c wpa_common.c wpa_debug.c \
+ wpa_ft.c wpa_ie.c wpa_supplicant.c wpabuf.c wpas_glue.c wps.c \
+ wps_attr_build.c wps_attr_parse.c wps_attr_process.c \
+ wps_common.c wps_dev_attr.c wps_enrollee.c wps_registrar.c \
+ wps_supplicant.c wps_upnp.c wps_upnp_ap.c wps_upnp_event.c \
+ wps_upnp_ssdp.c wps_upnp_web.c Packet32.c
MAN= wpa_supplicant.8 wpa_supplicant.conf.5
@@ -58,6 +36,15 @@ CFLAGS+=-DCONFIG_BACKEND_FILE \
-DCONFIG_PEERKEY \
-DCONFIG_SMARTCARD \
-DCONFIG_TERMINATE_ONLASTIF \
+ -DCONFIG_WPS \
+ -DCONFIG_WPS2 \
+ -DCONFIG_WPS_UPNP \
+ -DCONFIG_TLS=openssl \
+ -DCONFIG_IEEE80211R \
+ -DCONFIG_INTERWORKING \
+ -DCONFIG_PRIVSEP \
+ -DCONFIG_HS20 \
+ -DCONFIG_GAS \
-DPKCS12_FUNCS
#CFLAGS+= -g
DPADD+= ${LIBPCAP}
@@ -79,6 +66,9 @@ CFLAGS+=-DEAP_GTC \
-DEAP_PSK \
-DEAP_TLS \
-DEAP_TTLS \
+ -DEAP_GTC \
+ -DEAP_OTP \
+ -DEAP_LEAP \
-DIEEE8021X_EAPOL
SRCS+= chap.c \
eap.c \
@@ -103,7 +93,10 @@ TLS_FUNCS=y
NEED_AES_EAX=y
NEED_AES_ENCBLOCK=y
NEED_AES_OMAC1=y
-NEED_SHA256=y
+.endif
+
+.if !empty(CFLAGS:M-DCONFIG_WPS)
+NEED_AES_CBC=y
.endif
.if !empty(CFLAGS:M*-DEAP_AKA)
diff --git a/usr.sbin/wpa/wpa_supplicant/driver_freebsd.c b/usr.sbin/wpa/wpa_supplicant/driver_freebsd.c
deleted file mode 100644
index 24a0b9c..0000000
--- a/usr.sbin/wpa/wpa_supplicant/driver_freebsd.c
+++ /dev/null
@@ -1,934 +0,0 @@
-/*
- * WPA Supplicant - driver interaction with BSD net80211 layer
- * Copyright (c) 2004, Sam Leffler <sam@errno.com>
- *
- * This program is free software; you can redistribute it and/or modify
- * it under the terms of the GNU General Public License version 2 as
- * published by the Free Software Foundation.
- *
- * Alternatively, this software may be distributed under the terms of BSD
- * license.
- *
- * See README and COPYING for more details.
- *
- * $FreeBSD$
- */
-
-#include <stdlib.h>
-#include <stdio.h>
-#include <unistd.h>
-#include <string.h>
-#include <sys/ioctl.h>
-#include <errno.h>
-
-#include "common.h"
-#include "driver.h"
-#include "eloop.h"
-#include "l2_packet.h"
-#include "ieee802_11_defs.h"
-
-#include <sys/socket.h>
-#include <net/if.h>
-#include <net/if_media.h>
-#include <net/ethernet.h>
-
-#include <net80211/ieee80211_ioctl.h>
-
-struct wpa_driver_bsd_data {
- int sock; /* open socket for 802.11 ioctls */
- int route; /* routing socket for events */
- char ifname[IFNAMSIZ+1]; /* interface name */
- unsigned int ifindex; /* interface index */
- void *ctx;
- int prev_roaming; /* roaming state to restore on deinit */
- int prev_privacy; /* privacy state to restore on deinit */
- int prev_wpa; /* wpa state to restore on deinit */
- int prev_scanvalid; /* scan valid to restore on deinit */
- uint8_t lastssid[IEEE80211_NWID_LEN];
- int lastssid_len;
- uint32_t drivercaps; /* general driver capabilities */
- uint32_t cryptocaps; /* hardware crypto support */
- enum ieee80211_opmode opmode; /* operation mode */
-};
-
-static enum ieee80211_opmode
-get80211opmode(struct wpa_driver_bsd_data *drv)
-{
- struct ifmediareq ifmr;
-
- (void) memset(&ifmr, 0, sizeof(ifmr));
- (void) strncpy(ifmr.ifm_name, drv->ifname, sizeof(ifmr.ifm_name));
-
- if (ioctl(drv->sock, SIOCGIFMEDIA, (caddr_t)&ifmr) >= 0) {
- if (ifmr.ifm_current & IFM_IEEE80211_ADHOC) {
- if (ifmr.ifm_current & IFM_FLAG0)
- return IEEE80211_M_AHDEMO;
- else
- return IEEE80211_M_IBSS;
- }
- if (ifmr.ifm_current & IFM_IEEE80211_HOSTAP)
- return IEEE80211_M_HOSTAP;
- if (ifmr.ifm_current & IFM_IEEE80211_MONITOR)
- return IEEE80211_M_MONITOR;
- if (ifmr.ifm_current & IFM_IEEE80211_MBSS)
- return IEEE80211_M_MBSS;
- }
- return IEEE80211_M_STA;
-}
-
-static int
-set80211var(struct wpa_driver_bsd_data *drv, int op, const void *arg, int arg_len)
-{
- struct ieee80211req ireq;
-
- memset(&ireq, 0, sizeof(ireq));
- strncpy(ireq.i_name, drv->ifname, IFNAMSIZ);
- ireq.i_type = op;
- ireq.i_len = arg_len;
- ireq.i_data = (void *) arg;
-
- if (ioctl(drv->sock, SIOCS80211, &ireq) < 0) {
- fprintf(stderr, "ioctl[SIOCS80211, op %u, len %u]: %s\n",
- op, arg_len, strerror(errno));
- return -1;
- }
- return 0;
-}
-
-static int
-get80211var(struct wpa_driver_bsd_data *drv, int op, void *arg, int arg_len)
-{
- struct ieee80211req ireq;
-
- memset(&ireq, 0, sizeof(ireq));
- strncpy(ireq.i_name, drv->ifname, IFNAMSIZ);
- ireq.i_type = op;
- ireq.i_len = arg_len;
- ireq.i_data = arg;
-
- if (ioctl(drv->sock, SIOCG80211, &ireq) < 0) {
- fprintf(stderr, "ioctl[SIOCG80211, op %u, len %u]: %s\n",
- op, arg_len, strerror(errno));
- return -1;
- }
- return ireq.i_len;
-}
-
-static int
-set80211param(struct wpa_driver_bsd_data *drv, int op, int arg)
-{
- struct ieee80211req ireq;
-
- memset(&ireq, 0, sizeof(ireq));
- strncpy(ireq.i_name, drv->ifname, IFNAMSIZ);
- ireq.i_type = op;
- ireq.i_val = arg;
-
- if (ioctl(drv->sock, SIOCS80211, &ireq) < 0) {
- fprintf(stderr, "ioctl[SIOCS80211, op %u, arg 0x%x]: %s\n",
- op, arg, strerror(errno));
- return -1;
- }
- return 0;
-}
-
-static int
-get80211param(struct wpa_driver_bsd_data *drv, int op)
-{
- struct ieee80211req ireq;
-
- memset(&ireq, 0, sizeof(ireq));
- strncpy(ireq.i_name, drv->ifname, IFNAMSIZ);
- ireq.i_type = op;
-
- if (ioctl(drv->sock, SIOCG80211, &ireq) < 0) {
- fprintf(stderr, "ioctl[SIOCG80211, op %u]: %s\n",
- op, strerror(errno));
- return -1;
- }
- return ireq.i_val;
-}
-
-static int
-getifflags(struct wpa_driver_bsd_data *drv, int *flags)
-{
- struct ifreq ifr;
-
- memset(&ifr, 0, sizeof(ifr));
- strncpy(ifr.ifr_name, drv->ifname, sizeof (ifr.ifr_name));
- if (ioctl(drv->sock, SIOCGIFFLAGS, (caddr_t)&ifr) < 0) {
- perror("SIOCGIFFLAGS");
- return errno;
- }
- *flags = (ifr.ifr_flags & 0xffff) | (ifr.ifr_flagshigh << 16);
- return 0;
-}
-
-static int
-setifflags(struct wpa_driver_bsd_data *drv, int flags)
-{
- struct ifreq ifr;
-
- memset(&ifr, 0, sizeof(ifr));
- strncpy(ifr.ifr_name, drv->ifname, sizeof (ifr.ifr_name));
- ifr.ifr_flags = flags & 0xffff;
- ifr.ifr_flagshigh = flags >> 16;
- if (ioctl(drv->sock, SIOCSIFFLAGS, (caddr_t)&ifr) < 0) {
- perror("SIOCSIFFLAGS");
- return errno;
- }
- return 0;
-}
-
-static int
-wpa_driver_bsd_get_bssid(void *priv, u8 *bssid)
-{
- struct wpa_driver_bsd_data *drv = priv;
-
- return get80211var(drv, IEEE80211_IOC_BSSID,
- bssid, IEEE80211_ADDR_LEN) < 0 ? -1 : 0;
-}
-
-#if 0
-static int
-wpa_driver_bsd_set_bssid(void *priv, const char *bssid)
-{
- struct wpa_driver_bsd_data *drv = priv;
-
- return set80211var(drv, IEEE80211_IOC_BSSID,
- bssid, IEEE80211_ADDR_LEN);
-}
-#endif
-
-static int
-wpa_driver_bsd_get_ssid(void *priv, u8 *ssid)
-{
- struct wpa_driver_bsd_data *drv = priv;
-
- return get80211var(drv, IEEE80211_IOC_SSID,
- ssid, IEEE80211_NWID_LEN);
-}
-
-static int
-wpa_driver_bsd_set_ssid(void *priv, const char *ssid,
- size_t ssid_len)
-{
- struct wpa_driver_bsd_data *drv = priv;
-
- return set80211var(drv, IEEE80211_IOC_SSID, ssid, ssid_len);
-}
-
-static int
-wpa_driver_bsd_set_wpa_ie(struct wpa_driver_bsd_data *drv,
- const u8 *wpa_ie, size_t wpa_ie_len)
-{
- struct ieee80211req ireq;
-
- memset(&ireq, 0, sizeof(ireq));
- strncpy(ireq.i_name, drv->ifname, IFNAMSIZ);
- ireq.i_type = IEEE80211_IOC_APPIE;
- ireq.i_val = IEEE80211_APPIE_WPA;
- ireq.i_len = wpa_ie_len;
- ireq.i_data = (void *) wpa_ie;
- if (ioctl(drv->sock, SIOCS80211, &ireq) < 0) {
- fprintf(stderr,
- "ioctl[IEEE80211_IOC_APPIE:IEEE80211_APPIE_WPA]: %s\n",
- strerror(errno));
- return -1;
- }
- return 0;
-}
-
-static int
-wpa_driver_bsd_set_wpa_internal(void *priv, int wpa, int privacy)
-{
- struct wpa_driver_bsd_data *drv = priv;
- int ret = 0;
-
- wpa_printf(MSG_DEBUG, "%s: wpa=%d privacy=%d",
- __FUNCTION__, wpa, privacy);
-
- if (!wpa && wpa_driver_bsd_set_wpa_ie(drv, NULL, 0) < 0)
- ret = -1;
- if (set80211param(drv, IEEE80211_IOC_PRIVACY, privacy) < 0)
- ret = -1;
- if (set80211param(drv, IEEE80211_IOC_WPA, wpa) < 0)
- ret = -1;
-
- return ret;
-}
-
-static int
-wpa_driver_bsd_del_key(struct wpa_driver_bsd_data *drv, int key_idx,
- const unsigned char *addr)
-{
- struct ieee80211req_del_key wk;
-
- memset(&wk, 0, sizeof(wk));
- if (addr != NULL &&
- bcmp(addr, "\xff\xff\xff\xff\xff\xff", IEEE80211_ADDR_LEN) != 0) {
- struct ether_addr ea;
-
- memcpy(&ea, addr, IEEE80211_ADDR_LEN);
- wpa_printf(MSG_DEBUG, "%s: addr=%s keyidx=%d",
- __func__, ether_ntoa(&ea), key_idx);
- memcpy(wk.idk_macaddr, addr, IEEE80211_ADDR_LEN);
- wk.idk_keyix = (uint8_t) IEEE80211_KEYIX_NONE;
- } else {
- wpa_printf(MSG_DEBUG, "%s: keyidx=%d", __func__, key_idx);
- wk.idk_keyix = key_idx;
- }
- return set80211var(drv, IEEE80211_IOC_DELKEY, &wk, sizeof(wk));
-}
-
-static int
-wpa_driver_bsd_set_key(const char *ifname, void *priv, enum wpa_alg alg,
- const unsigned char *addr, int key_idx, int set_tx,
- const u8 *seq, size_t seq_len,
- const u8 *key, size_t key_len)
-{
- struct wpa_driver_bsd_data *drv = priv;
- struct ieee80211req_key wk;
- struct ether_addr ea;
- char *alg_name;
- u_int8_t cipher;
-
- if (alg == WPA_ALG_NONE)
- return wpa_driver_bsd_del_key(drv, key_idx, addr);
-
- switch (alg) {
- case WPA_ALG_WEP:
- alg_name = "WEP";
- cipher = IEEE80211_CIPHER_WEP;
- break;
- case WPA_ALG_TKIP:
- alg_name = "TKIP";
- cipher = IEEE80211_CIPHER_TKIP;
- break;
- case WPA_ALG_CCMP:
- alg_name = "CCMP";
- cipher = IEEE80211_CIPHER_AES_CCM;
- break;
- default:
- wpa_printf(MSG_DEBUG, "%s: unknown/unsupported algorithm %d",
- __func__, alg);
- return -1;
- }
-
- memcpy(&ea, addr, IEEE80211_ADDR_LEN);
- wpa_printf(MSG_DEBUG,
- "%s: alg=%s addr=%s key_idx=%d set_tx=%d seq_len=%zu key_len=%zu",
- __func__, alg_name, ether_ntoa(&ea), key_idx, set_tx,
- seq_len, key_len);
-
- if (seq_len > sizeof(u_int64_t)) {
- wpa_printf(MSG_DEBUG, "%s: seq_len %zu too big",
- __func__, seq_len);
- return -2;
- }
- if (key_len > sizeof(wk.ik_keydata)) {
- wpa_printf(MSG_DEBUG, "%s: key length %zu too big",
- __func__, key_len);
- return -3;
- }
-
- memset(&wk, 0, sizeof(wk));
- wk.ik_type = cipher;
- wk.ik_flags = IEEE80211_KEY_RECV;
- if (set_tx)
- wk.ik_flags |= IEEE80211_KEY_XMIT;
- memcpy(wk.ik_macaddr, addr, IEEE80211_ADDR_LEN);
- /*
- * Deduce whether group/global or unicast key by checking
- * the address (yech). Note also that we can only mark global
- * keys default; doing this for a unicast key is an error.
- */
- if (bcmp(addr, "\xff\xff\xff\xff\xff\xff", IEEE80211_ADDR_LEN) == 0) {
- wk.ik_flags |= IEEE80211_KEY_GROUP;
- wk.ik_keyix = key_idx;
- } else {
- wk.ik_keyix = (key_idx == 0 ? IEEE80211_KEYIX_NONE : key_idx);
- }
- if (wk.ik_keyix != IEEE80211_KEYIX_NONE && set_tx)
- wk.ik_flags |= IEEE80211_KEY_DEFAULT;
- /*
- * Ignore replay failures in IBSS and AHDEMO mode.
- */
- if (drv->opmode == IEEE80211_M_IBSS ||
- drv->opmode == IEEE80211_M_AHDEMO)
- wk.ik_flags |= IEEE80211_KEY_NOREPLAY;
- wk.ik_keylen = key_len;
- memcpy(&wk.ik_keyrsc, seq, seq_len);
- wk.ik_keyrsc = le64toh(wk.ik_keyrsc);
- memcpy(wk.ik_keydata, key, key_len);
-
- return set80211var(drv, IEEE80211_IOC_WPAKEY, &wk, sizeof(wk));
-}
-
-static int
-wpa_driver_bsd_set_countermeasures(void *priv, int enabled)
-{
- struct wpa_driver_bsd_data *drv = priv;
-
- wpa_printf(MSG_DEBUG, "%s: enabled=%d", __func__, enabled);
- return set80211param(drv, IEEE80211_IOC_COUNTERMEASURES, enabled);
-}
-
-
-static int
-wpa_driver_bsd_set_drop_unencrypted(void *priv, int enabled)
-{
- struct wpa_driver_bsd_data *drv = priv;
-
- wpa_printf(MSG_DEBUG, "%s: enabled=%d", __func__, enabled);
- return set80211param(drv, IEEE80211_IOC_DROPUNENCRYPTED, enabled);
-}
-
-static int
-wpa_driver_bsd_set_auth_alg(void *priv, int auth_alg)
-{
- struct wpa_driver_bsd_data *drv = priv;
- int authmode;
-
- if ((auth_alg & WPA_AUTH_ALG_OPEN) &&
- (auth_alg & WPA_AUTH_ALG_SHARED))
- authmode = IEEE80211_AUTH_AUTO;
- else if (auth_alg & WPA_AUTH_ALG_SHARED)
- authmode = IEEE80211_AUTH_SHARED;
- else
- authmode = IEEE80211_AUTH_OPEN;
-
- wpa_printf(MSG_DEBUG, "%s alg 0x%x authmode %u",
- __func__, auth_alg, authmode);
-
- return set80211param(drv, IEEE80211_IOC_AUTHMODE, authmode);
-}
-
-static int
-wpa_driver_bsd_deauthenticate(void *priv, const u8 *addr, int reason_code)
-{
- struct wpa_driver_bsd_data *drv = priv;
- struct ieee80211req_mlme mlme;
-
- drv->lastssid_len = 0;
-
- wpa_printf(MSG_DEBUG, "%s", __func__);
- memset(&mlme, 0, sizeof(mlme));
- mlme.im_op = IEEE80211_MLME_DEAUTH;
- mlme.im_reason = reason_code;
- memcpy(mlme.im_macaddr, addr, IEEE80211_ADDR_LEN);
- return set80211var(drv, IEEE80211_IOC_MLME, &mlme, sizeof(mlme));
-}
-
-static int
-wpa_driver_bsd_disassociate(void *priv, const u8 *addr, int reason_code)
-{
- struct wpa_driver_bsd_data *drv = priv;
- struct ieee80211req_mlme mlme;
-
- drv->lastssid_len = 0;
-
- wpa_printf(MSG_DEBUG, "%s", __func__);
- memset(&mlme, 0, sizeof(mlme));
- mlme.im_op = IEEE80211_MLME_DISASSOC;
- mlme.im_reason = reason_code;
- memcpy(mlme.im_macaddr, addr, IEEE80211_ADDR_LEN);
- return set80211var(drv, IEEE80211_IOC_MLME, &mlme, sizeof(mlme));
-}
-
-static int
-wpa_driver_bsd_associate(void *priv, struct wpa_driver_associate_params *params)
-{
- struct wpa_driver_bsd_data *drv = priv;
- struct ieee80211req_mlme mlme;
- int flags, privacy;
-
- wpa_printf(MSG_DEBUG,
- "%s: ssid '%.*s' wpa ie len %u pairwise %u group %u key mgmt %u"
- , __func__
- , params->ssid_len, params->ssid
- , params->wpa_ie_len
- , params->pairwise_suite
- , params->group_suite
- , params->key_mgmt_suite
- );
-
- /* NB: interface must be marked UP to associate */
- if (getifflags(drv, &flags) != 0) {
- wpa_printf(MSG_DEBUG, "%s did not mark interface UP", __func__);
- return -1;
- }
- if ((flags & IFF_UP) == 0 && setifflags(drv, flags | IFF_UP) != 0) {
- wpa_printf(MSG_DEBUG, "%s unable to mark interface UP",
- __func__);
- return -1;
- }
-
- if (wpa_driver_bsd_set_drop_unencrypted(drv, params->drop_unencrypted)
- < 0)
- return -1;
- if (wpa_driver_bsd_set_auth_alg(drv, params->auth_alg) < 0)
- return -1;
- /* XXX error handling is wrong but unclear what to do... */
- if (wpa_driver_bsd_set_wpa_ie(drv, params->wpa_ie, params->wpa_ie_len) < 0)
- return -1;
-
- privacy = !(params->pairwise_suite == CIPHER_NONE &&
- params->group_suite == CIPHER_NONE &&
- params->key_mgmt_suite == KEY_MGMT_NONE &&
- params->wpa_ie_len == 0);
- wpa_printf(MSG_DEBUG, "%s: set PRIVACY %u", __func__, privacy);
-
- if (set80211param(drv, IEEE80211_IOC_PRIVACY, privacy) < 0)
- return -1;
-
- if (params->wpa_ie_len &&
- set80211param(drv, IEEE80211_IOC_WPA,
- params->wpa_ie[0] == WLAN_EID_RSN ? 2 : 1) < 0)
- return -1;
-
- memset(&mlme, 0, sizeof(mlme));
- mlme.im_op = IEEE80211_MLME_ASSOC;
- if (params->ssid != NULL)
- memcpy(mlme.im_ssid, params->ssid, params->ssid_len);
- mlme.im_ssid_len = params->ssid_len;
- if (params->bssid != NULL)
- memcpy(mlme.im_macaddr, params->bssid, IEEE80211_ADDR_LEN);
- if (set80211var(drv, IEEE80211_IOC_MLME, &mlme, sizeof(mlme)) < 0)
- return -1;
- memcpy(drv->lastssid, params->ssid, params->ssid_len);
- drv->lastssid_len = params->ssid_len;
- return 0;
-}
-
-static int
-wpa_driver_bsd_scan(void *priv, struct wpa_driver_scan_params *params)
-{
- struct wpa_driver_bsd_data *drv = priv;
- struct ieee80211_scan_req sr;
- int i;
- int flags;
-
- /* XXX not true but easiest to perpetuate the myth */
- /* NB: interface must be marked UP to do a scan */
- if (getifflags(drv, &flags) != 0) {
- wpa_printf(MSG_DEBUG, "%s did not mark interface UP", __func__);
- return -1;
- }
- if ((flags & IFF_UP) == 0 && setifflags(drv, flags | IFF_UP) != 0) {
- wpa_printf(MSG_DEBUG, "%s unable to mark interface UP",
- __func__);
- return -1;
- }
-
- memset(&sr, 0, sizeof(sr));
- sr.sr_flags = IEEE80211_IOC_SCAN_ACTIVE
- | IEEE80211_IOC_SCAN_ONCE
- | IEEE80211_IOC_SCAN_NOJOIN
- ;
- sr.sr_duration = IEEE80211_IOC_SCAN_FOREVER;
- if (params->num_ssids > 0) {
- sr.sr_nssid = params->num_ssids;
-#if 0
- /* Boundary check is done by upper layer */
- if (sr.sr_nssid > IEEE80211_IOC_SCAN_MAX_SSID)
- sr.sr_nssid = IEEE80211_IOC_SCAN_MAX_SSID;
-#endif
- /* NB: check scan cache first */
- sr.sr_flags |= IEEE80211_IOC_SCAN_CHECK;
-}
- for (i = 0; i < sr.sr_nssid; i++) {
- sr.sr_ssid[i].len = params->ssids[i].ssid_len;
- os_memcpy(sr.sr_ssid[i].ssid, params->ssids[i].ssid,
- sr.sr_ssid[i].len);
- }
- /* NB: net80211 delivers a scan complete event so no need to poll */
- return set80211var(drv, IEEE80211_IOC_SCAN_REQ, &sr, sizeof(sr));
-}
-
-#include <net/route.h>
-#include <net80211/ieee80211_freebsd.h>
-
-static void
-wpa_driver_bsd_event_receive(int sock, void *ctx, void *sock_ctx)
-{
- struct wpa_driver_bsd_data *drv = sock_ctx;
- char buf[2048];
- struct if_announcemsghdr *ifan;
- struct if_msghdr *ifm;
- struct rt_msghdr *rtm;
- union wpa_event_data event;
- struct ieee80211_michael_event *mic;
- int n;
-
- n = read(sock, buf, sizeof(buf));
- if (n < 0) {
- if (errno != EINTR && errno != EAGAIN)
- perror("read(PF_ROUTE)");
- return;
- }
-
- rtm = (struct rt_msghdr *) buf;
- if (rtm->rtm_version != RTM_VERSION) {
- wpa_printf(MSG_DEBUG, "Routing message version %d not "
- "understood\n", rtm->rtm_version);
- return;
- }
- memset(&event, 0, sizeof(event));
- switch (rtm->rtm_type) {
- case RTM_IFANNOUNCE:
- ifan = (struct if_announcemsghdr *) rtm;
- if (ifan->ifan_index != drv->ifindex)
- break;
- strlcpy(event.interface_status.ifname, drv->ifname,
- sizeof(event.interface_status.ifname));
- switch (ifan->ifan_what) {
- case IFAN_DEPARTURE:
- event.interface_status.ievent = EVENT_INTERFACE_REMOVED;
- default:
- return;
- }
- wpa_printf(MSG_DEBUG, "RTM_IFANNOUNCE: Interface '%s' %s",
- event.interface_status.ifname,
- ifan->ifan_what == IFAN_DEPARTURE ?
- "removed" : "added");
- wpa_supplicant_event(ctx, EVENT_INTERFACE_STATUS, &event);
- break;
- case RTM_IEEE80211:
- ifan = (struct if_announcemsghdr *) rtm;
- if (ifan->ifan_index != drv->ifindex)
- break;
- switch (ifan->ifan_what) {
- case RTM_IEEE80211_ASSOC:
- case RTM_IEEE80211_REASSOC:
- wpa_supplicant_event(ctx, EVENT_ASSOC, NULL);
- break;
- case RTM_IEEE80211_DISASSOC:
- wpa_supplicant_event(ctx, EVENT_DISASSOC, NULL);
- break;
- case RTM_IEEE80211_SCAN:
- wpa_supplicant_event(ctx, EVENT_SCAN_RESULTS, NULL);
- break;
- case RTM_IEEE80211_REPLAY:
- /* ignore */
- break;
- case RTM_IEEE80211_MICHAEL:
- mic = (struct ieee80211_michael_event *) &ifan[1];
- wpa_printf(MSG_DEBUG,
- "Michael MIC failure wireless event: "
- "keyix=%u src_addr=" MACSTR, mic->iev_keyix,
- MAC2STR(mic->iev_src));
-
- memset(&event, 0, sizeof(event));
- event.michael_mic_failure.unicast =
- !IEEE80211_IS_MULTICAST(mic->iev_dst);
- wpa_supplicant_event(ctx, EVENT_MICHAEL_MIC_FAILURE,
- &event);
- break;
- }
- break;
- case RTM_IFINFO:
- ifm = (struct if_msghdr *) rtm;
- if (ifm->ifm_index != drv->ifindex)
- break;
- if ((rtm->rtm_flags & RTF_UP) == 0) {
- strlcpy(event.interface_status.ifname, drv->ifname,
- sizeof(event.interface_status.ifname));
- event.interface_status.ievent = EVENT_INTERFACE_REMOVED;
- wpa_printf(MSG_DEBUG, "RTM_IFINFO: Interface '%s' DOWN",
- event.interface_status.ifname);
- wpa_supplicant_event(ctx, EVENT_INTERFACE_STATUS, &event);
- }
- break;
- }
-}
-
-static int
-getmaxrate(const uint8_t rates[15], uint8_t nrates)
-{
- int i, maxrate = -1;
-
- for (i = 0; i < nrates; i++) {
- int rate = rates[i] & IEEE80211_RATE_VAL;
- if (rate > maxrate)
- rate = maxrate;
- }
- return maxrate;
-}
-
-/* unalligned little endian access */
-#define LE_READ_4(p) \
- ((u_int32_t) \
- ((((const u_int8_t *)(p))[0] ) | \
- (((const u_int8_t *)(p))[1] << 8) | \
- (((const u_int8_t *)(p))[2] << 16) | \
- (((const u_int8_t *)(p))[3] << 24)))
-
-static int __inline
-iswpaoui(const u_int8_t *frm)
-{
- return frm[1] > 3 && LE_READ_4(frm+2) == ((WPA_OUI_TYPE<<24)|WPA_OUI);
-}
-
-
-static void
-wpa_driver_bsd_add_scan_entry(struct wpa_scan_results *res,
- struct ieee80211req_scan_result *sr)
-{
- struct wpa_scan_res *result, **tmp;
- size_t extra_len;
- u8 *pos;
-
- extra_len = 2 + sr->isr_ssid_len;
- extra_len += 2 + sr->isr_nrates;
- extra_len += 3; /* ERP IE */
- extra_len += sr->isr_ie_len;
-
- result = os_zalloc(sizeof(*result) + extra_len);
- if (result == NULL)
- return;
- os_memcpy(result->bssid, sr->isr_bssid, ETH_ALEN);
- result->freq = sr->isr_freq;
- result->beacon_int = sr->isr_intval;
- result->caps = sr->isr_capinfo;
- result->qual = sr->isr_rssi;
- result->noise = sr->isr_noise;
- /*
- * the rssi value reported by the kernel is in 0.5dB steps relative to
- * the reported noise floor. see ieee80211_node.h for details.
- */
- result->level = sr->isr_rssi / 2 + sr->isr_noise;
-
- pos = (u8 *)(result + 1);
-
- *pos++ = WLAN_EID_SSID;
- *pos++ = sr->isr_ssid_len;
- os_memcpy(pos, sr + 1, sr->isr_ssid_len);
- pos += sr->isr_ssid_len;
-
- /*
- * Deal all rates as supported rate.
- * Because net80211 doesn't report extended supported rate or not.
- */
- *pos++ = WLAN_EID_SUPP_RATES;
- *pos++ = sr->isr_nrates;
- os_memcpy(pos, sr->isr_rates, sr->isr_nrates);
- pos += sr->isr_nrates;
-
- *pos++ = WLAN_EID_ERP_INFO;
- *pos++ = 1;
- *pos++ = sr->isr_erp;
-
- os_memcpy(pos, (u8 *)(sr + 1) + sr->isr_ssid_len, sr->isr_ie_len);
- pos += sr->isr_ie_len;
-
- result->ie_len = pos - (u8 *)(result + 1);
-
- tmp = os_realloc(res->res,
- (res->num + 1) * sizeof(struct wpa_scan_res *));
- if (tmp == NULL) {
- os_free(result);
- return;
- }
- tmp[res->num++] = result;
- res->res = tmp;
-}
-
-static struct wpa_scan_results *
-wpa_driver_bsd_get_scan_results2(void *priv)
-{
- struct ieee80211req_scan_result *sr;
- struct wpa_scan_results *res;
- int len, rest;
- uint8_t buf[24*1024], *pos;
-
- len = get80211var(priv, IEEE80211_IOC_SCAN_RESULTS, buf, 24*1024);
- if (len < 0)
- return NULL;
-
- res = os_zalloc(sizeof(*res));
- if (res == NULL)
- return NULL;
-
- pos = buf;
- rest = len;
- while (rest >= sizeof(struct ieee80211req_scan_result)) {
- sr = (struct ieee80211req_scan_result *)pos;
- wpa_driver_bsd_add_scan_entry(res, sr);
- pos += sr->isr_len;
- rest -= sr->isr_len;
- }
-
- wpa_printf(MSG_DEBUG, "Received %d bytes of scan results (%lu BSSes)",
- len, (unsigned long)res->num);
-
- return (res);
-}
-
-
-#define GETPARAM(drv, param, v) \
- (((v) = get80211param(drv, param)) != -1)
-#define IEEE80211_C_BGSCAN 0x20000000
-
-/*
- * Set the scan cache valid threshold to 1.5 x bg scan interval
- * to force all scan requests to consult the cache unless they
- * explicitly bypass it.
- */
-static int
-setscanvalid(struct wpa_driver_bsd_data *drv)
-{
- int bgscan, scanvalid;
-
- if (!GETPARAM(drv, IEEE80211_IOC_SCANVALID, drv->prev_scanvalid) ||
- !GETPARAM(drv, IEEE80211_IOC_BGSCAN_INTERVAL, bgscan))
- return -1;
- scanvalid = 3*bgscan/2;
- return (drv->prev_scanvalid < scanvalid) ?
- set80211param(drv, IEEE80211_IOC_SCANVALID, scanvalid) : 0;
-}
-
-static void *
-wpa_driver_bsd_init(void *ctx, const char *ifname)
-{
- struct wpa_driver_bsd_data *drv;
- struct ieee80211_devcaps_req devcaps;
- int flags;
-
- drv = malloc(sizeof(*drv));
- if (drv == NULL)
- return NULL;
- memset(drv, 0, sizeof(*drv));
- /*
- * NB: We require the interface name be mappable to an index.
- * This implies we do not support having wpa_supplicant
- * wait for an interface to appear. This seems ok; that
- * doesn't belong here; it's really the job of devd.
- */
- drv->ifindex = if_nametoindex(ifname);
- if (drv->ifindex == 0) {
- wpa_printf(MSG_DEBUG, "%s: interface %s does not exist",
- __func__, ifname);
- goto fail1;
- }
- drv->sock = socket(PF_INET, SOCK_DGRAM, 0);
- if (drv->sock < 0)
- goto fail1;
- drv->ctx = ctx;
- strncpy(drv->ifname, ifname, sizeof(drv->ifname));
-
- /*
- * Mark the interface as down to ensure wpa_supplicant has exclusive
- * access to the net80211 state machine, do this before opening the
- * route socket to avoid a false event that the interface disappeared.
- */
- if (getifflags(drv, &flags) == 0)
- (void) setifflags(drv, flags &~ IFF_UP);
-
- drv->route = socket(PF_ROUTE, SOCK_RAW, 0);
- if (drv->route < 0)
- goto fail;
- eloop_register_read_sock(drv->route,
- wpa_driver_bsd_event_receive, ctx, drv);
-
- if (get80211var(drv, IEEE80211_IOC_DEVCAPS, &devcaps, sizeof(devcaps)) < 0) {
- wpa_printf(MSG_DEBUG,
- "%s: failed to get device capabilities: %s",
- __func__, strerror(errno));
- goto fail;
- }
- drv->drivercaps = devcaps.dc_drivercaps;
- drv->cryptocaps = devcaps.dc_cryptocaps;
-
- if (!GETPARAM(drv, IEEE80211_IOC_ROAMING, drv->prev_roaming)) {
- wpa_printf(MSG_DEBUG, "%s: failed to get roaming state: %s",
- __func__, strerror(errno));
- goto fail;
- }
- if (!GETPARAM(drv, IEEE80211_IOC_PRIVACY, drv->prev_privacy)) {
- wpa_printf(MSG_DEBUG, "%s: failed to get privacy state: %s",
- __func__, strerror(errno));
- goto fail;
- }
- if (!GETPARAM(drv, IEEE80211_IOC_WPA, drv->prev_wpa)) {
- wpa_printf(MSG_DEBUG, "%s: failed to get wpa state: %s",
- __func__, strerror(errno));
- goto fail;
- }
- if (set80211param(drv, IEEE80211_IOC_ROAMING, IEEE80211_ROAMING_MANUAL) < 0) {
- wpa_printf(MSG_DEBUG, "%s: failed to set wpa_supplicant-based "
- "roaming: %s", __func__, strerror(errno));
- goto fail;
- }
- if (drv->drivercaps & IEEE80211_C_BGSCAN) {
- /*
- * Driver does background scanning; force the scan valid
- * setting to 1.5 x bg scan interval so the scan cache is
- * always consulted before we force a foreground scan.
- */
- if (setscanvalid(drv) < 0) {
- wpa_printf(MSG_DEBUG,
- "%s: warning, failed to set scanvalid, scanning "
- "may be suboptimal: %s", __func__, strerror(errno));
- }
- }
- if (set80211param(drv, IEEE80211_IOC_WPA, 1+2) < 0) {
- wpa_printf(MSG_DEBUG, "%s: failed to enable WPA support %s",
- __func__, strerror(errno));
- goto fail;
- }
- drv->opmode = get80211opmode(drv);
-
- return drv;
-fail:
- close(drv->sock);
-fail1:
- free(drv);
- return NULL;
-}
-#undef GETPARAM
-
-static void
-wpa_driver_bsd_deinit(void *priv)
-{
- struct wpa_driver_bsd_data *drv = priv;
- int flags;
-
- /* NB: mark interface down */
- if (getifflags(drv, &flags) == 0)
- (void) setifflags(drv, flags &~ IFF_UP);
-
- wpa_driver_bsd_set_wpa_internal(drv, drv->prev_wpa, drv->prev_privacy);
- if (set80211param(drv, IEEE80211_IOC_ROAMING, drv->prev_roaming) < 0) {
- /* NB: don't whinge if device ejected or equivalent */
- if (errno != ENXIO)
- wpa_printf(MSG_DEBUG, "%s: failed to restore roaming "
- "state", __func__);
- }
- if (drv->drivercaps & IEEE80211_C_BGSCAN) {
- /* XXX check return value */
- (void) set80211param(drv, IEEE80211_IOC_SCANVALID,
- drv->prev_scanvalid);
- }
-
- (void) close(drv->route); /* ioctl socket */
- (void) close(drv->sock); /* event socket */
- free(drv);
-}
-
-
-struct wpa_driver_ops wpa_driver_bsd_ops = {
- .name = "bsd",
- .desc = "BSD 802.11 support (Atheros, etc.)",
- .init = wpa_driver_bsd_init,
- .deinit = wpa_driver_bsd_deinit,
- .get_bssid = wpa_driver_bsd_get_bssid,
- .get_ssid = wpa_driver_bsd_get_ssid,
- .set_key = wpa_driver_bsd_set_key,
- .set_countermeasures = wpa_driver_bsd_set_countermeasures,
- .scan2 = wpa_driver_bsd_scan,
- .get_scan_results2 = wpa_driver_bsd_get_scan_results2,
- .deauthenticate = wpa_driver_bsd_deauthenticate,
- .disassociate = wpa_driver_bsd_disassociate,
- .associate = wpa_driver_bsd_associate,
-};
OpenPOWER on IntegriCloud