summaryrefslogtreecommitdiffstats
path: root/lib/network/connection/wireless.pm
diff options
context:
space:
mode:
Diffstat (limited to 'lib/network/connection/wireless.pm')
-rw-r--r--lib/network/connection/wireless.pm1132
1 files changed, 1132 insertions, 0 deletions
diff --git a/lib/network/connection/wireless.pm b/lib/network/connection/wireless.pm
new file mode 100644
index 0000000..e036f6c
--- /dev/null
+++ b/lib/network/connection/wireless.pm
@@ -0,0 +1,1132 @@
+package network::connection::wireless;
+
+use base qw(network::connection::ethernet);
+
+use strict;
+use common;
+use log;
+use network::network;
+
+#- class attributes:
+#- network: ID of the selected network
+
+sub get_type_name() { N("Wireless") }
+sub get_type_description() { N("Wireless (Wi-Fi)") }
+sub _get_type_icon() { 'wireless' }
+sub get_devices {
+ my ($_class, %options) = @_;
+ require detect_devices;
+ my @devices = detect_devices::probe_category('network/wireless');
+ my @wireless = grep { detect_devices::is_wireless_interface($_) } detect_devices::get_lan_interfaces();
+ my @all_devices = (@devices, network::connection::ethernet::get_unlisted_devices(\@wireless, \@devices));
+ foreach (@all_devices) {
+ my $interface = $_->{interface} || network::connection::ethernet::device_to_interface($_) or next;
+ my $driver = network::connection::ethernet::interface_to_driver($interface) or next;
+ $_->{driver} = $driver if $driver;
+ }
+ @all_devices,
+ if_(!$options{automatic_only}, {
+ driver => 'ndiswrapper',
+ description => N("Use a Windows driver (with ndiswrapper)"),
+ });
+}
+
+sub handles_ifcfg {
+ my ($_class, $ifcfg) = @_;
+ require detect_devices;
+ detect_devices::is_wireless_interface($ifcfg->{DEVICE}) || exists $ifcfg->{WIRELESS_MODE};
+}
+
+sub get_metric { 35 }
+
+#- http://www.linux-wireless.org/Install-HOWTO/WL/WEP-Key-HOWTO.txt
+my $wpa_supplicant_max_wep_key_len = 32;
+
+our %wireless_enc_modes = (
+ none => N_("None"),
+ open => N_("Open WEP"),
+ restricted => N_("Restricted WEP"),
+ 'wpa-psk' => N_("WPA/WPA2 Pre-Shared Key"),
+ 'wpa-eap' => N_("WPA/WPA2 Enterprise"),
+);
+#define the eap related variables we handle
+#0 means we preserve value if found
+#1 means we save without quotes
+#2 save with quotes
+my %eap_vars = (
+ ssid => 2,
+ scan_ssid => 1,
+ identity => 2,
+ password => 2,
+ key_mgmt => 1,
+ eap => 1,
+ pairwise => 1,
+ group => 1,
+ proto => 1,
+ ca_cert => 2,
+ client_cert => 2,
+ phase2 => 2,
+ anonymous_identity => 2,
+ subject_match => 2,
+ disabled => 0,
+ id_str => 0,
+ bssid => 0,
+ priority => 0,
+ auth_alg => 0,
+ eapol_flags => 0,
+ proactive_key_caching => 0,
+ peerkey => 0,
+ ca_path => 0,
+ private_key => 0,
+ private_key_passwd => 0,
+ dh_file => 0,
+ altsubject_match => 0,
+ phase1 => 0,
+ fragment_size => 0,
+ eap_workaround => 0,
+);
+
+my @thirdparty_settings = (
+ {
+ name => 'zd1201',
+ description => 'ZyDAS ZD1201',
+ url => 'http://linux-lc100020.sourceforge.net/',
+ firmware => {
+ test_file => 'zd1201*.fw',
+ },
+ },
+
+ (map {
+ {
+ name => "ipw${_}",
+ description => "Intel(R) PRO/Wireless ${_}",
+ url => "http://ipw${_}.sourceforge.net/",
+ firmware => {
+ url => "http://ipw${_}.sourceforge.net/firmware.php",
+ test_file => "ipw$_-*.fw",
+ },
+ };
+ } (2100, 2200)),
+
+ {
+ name => "ipw3945",
+ description => "Intel(R) PRO/Wireless 3945",
+ url => "http://ipw3945.sourceforge.net/",
+ firmware => {
+ package => "ipw3945-ucode",
+ test_file => "ipw3945.ucode",
+ },
+ tools => {
+ package => "ipw3945d",
+ test_file => '/usr/sbin/ipw3945d',
+ },
+ },
+
+ (map {
+ my ($version, $ucode_api, $ucode_version) = @$_;
+ $ucode_version ||= $version;
+ {
+ name => "iwl${version}",
+ description => "Intel(R) PRO/Wireless ${version}",
+ url => "http://intellinuxwireless.org/",
+ firmware => {
+ package => "iwlwifi-${version}-ucode",
+ test_file => "iwlwifi-${ucode_version}${ucode_api}.ucode",
+ },
+ sleep => 1,
+ };
+ } ([ 3945, '-1' ], [ 4965, '-1' ], [ 'agn', '-1', 5000 ])),
+
+ {
+ name => 'p54pci',
+ description => 'PCI adaptors based on the Intersil Prism54 chip series',
+ url => 'http://wireless.kernel.org/en/users/Drivers/p54',
+ firmware => {
+ url => 'http://wireless.kernel.org/en/users/Drivers/p54#firmware',
+ test_file => "isl3886pci",
+ },
+ },
+
+ {
+ name => 'p54usb',
+ description => 'USB adaptors based on the Intersil Prism54 chip series',
+ url => 'http://wireless.kernel.org/en/users/Drivers/p54',
+ firmware => {
+ url => 'http://wireless.kernel.org/en/users/Drivers/p54#firmware',
+ test_file => "isl388*usb",
+ },
+ },
+
+ {
+ name => 'atmel',
+ matching => [ qw(at76_usb atmel_cs atmel_pci) ],
+ description => 'Atmel at76c50x cards',
+ url => 'http://thekelleys.org.uk/atmel/',
+ firmware => {
+ test_file => 'atmel_at76c50*',
+ },
+ links => 'http://at76c503a.berlios.de/',
+ },
+
+ {
+ name => 'madwifi',
+ matching => 'ath_pci',
+ description => 'Multiband Atheros Driver for WiFi',
+ url => 'http://madwifi.org/',
+ kernel_module => 1,
+ tools => {
+ optional => 1,
+ test_file => '/usr/bin/athstats',
+ },
+ },
+
+ {
+ name => 'prism2',
+ matching => qr/^prism2_/,
+ description => 'Prism2 based cards',
+ tools => {
+ package => 'prism2-utils',
+ test_file => '/sbin/wlanctl-ng',
+ },
+ },
+
+ {
+ name => 'zd1211',
+ matching => 'zd1211rw',
+ description => 'ZD1211 chip',
+ firmware => {
+ url => 'http://sourceforge.net/projects/zd1211/',
+ test_file => 'zd1211/zd1211_*',
+ },
+ },
+
+ {
+ name => 'bcm43xx',
+ description => 'Broadcom bcm43xx wireless chips',
+ url => 'http://bcm43xx.berlios.de/',
+ firmware => {
+ test_file => 'bcm43xx_microcode*.fw',
+ no_package => 1,
+ extract => {
+ name => 'bcm43xx-fwcutter',
+ test_file => '/usr/bin/bcm43xx-fwcutter',
+ windows_source => 'bcmwl5.sys',
+ default_source => 'bcmwl5.sys',
+ run => sub {
+ my ($file) = @_;
+ run_program::rooted($::prefix, '/usr/bin/bcm43xx-fwcutter',
+ '-w', $network::thirdparty::firmware_directory, $file);
+ },
+ },
+ },
+ },
+
+ (map {
+ +{
+ name => $_,
+ description => "Broadcom $_ wireless chips",
+ url => 'http://wireless.kernel.org/en/users/Drivers/b43',
+ firmware => {
+ test_file => $_ . "/ucode*.fw",
+ no_package => 1,
+ extract => {
+ name => 'b43-fwcutter',
+ test_file => '/usr/bin/b43-fwcutter',
+ windows_source => 'bcmwl5.sys',
+ default_source => 'bcmwl5.sys',
+ run => sub {
+ my ($file) = @_;
+ run_program::rooted($::prefix, '/usr/bin/b43-fwcutter',
+ '-w', $network::thirdparty::firmware_directory, $file);
+ },
+ },
+ },
+ };
+ } qw(b43 b43legacy)),
+
+ {
+ name => 'broadcom-wl',
+ matching => 'wl',
+ description => 'Broadcom Hybrid',
+ url => 'http://www.broadcom.com/support/802.11/linux_sta.php',
+ kernel_module => 1,
+ },
+
+ {
+ name => 'acx100',
+ matching => [ qw(acx_pci acx_usb) ],
+ description => 'ACX100/ACX111/TNETW1450',
+ firmware => {
+ url => 'http://acx100.sourceforge.net/wiki/Firmware',
+ test_file => 'tiacx1*',
+ no_distro_package => 1,
+ },
+ },
+
+ {
+ name => 'ndiswrapper',
+ description => 'Wireless device using ndiswrapper (windows drivers)',
+ tools => {
+ test_file => '/usr/sbin/ndiswrapper',
+ },
+ firmware => {
+ user_install => sub {
+ my ($settings, $in) = @_;
+ require network::ndiswrapper;
+ $settings->{device} = network::ndiswrapper::select_device($in) or return;
+ network::ndiswrapper::setup_device($in, $settings->{device});
+ $settings->{device}{driver} = $settings->{name};
+ },
+ url => 'http://ndiswrapper.sourceforge.net/mediawiki/index.php/List',
+ component_name => N_("Windows driver"),
+ no_package => 1,
+ },
+ no_module_reload => 1,
+ },
+
+ {
+ name => 'rt61',
+ matching => 'rt61pci',
+ description => 'Ralink RT61 802.11abg WLAN',
+ firmware => {
+ url => 'http://rt2x00.serialmonkey.com/',
+ test_file => 'rt2661.bin',
+ },
+ },
+
+ {
+ name => 'rt73',
+ matching => 'rt73usb',
+ description => 'Ralink RT73 802.11abg WLAN',
+ firmware => {
+ url => 'http://rt2x00.serialmonkey.com/',
+ test_file => 'rt73.bin',
+ },
+ },
+
+ (map {
+ +{
+ name => "rt${_}",
+ matching => qr/^rt${_}(sta|)$/,
+ description => 'Ralink RT${_} WiFi',
+ kernel_module => 1,
+ firmware => {
+ url => 'http://www.ralinktech.com/',
+ test_file => "rt${_}.bin",
+ },
+ };
+ } (2860, 2870, 3090)),
+
+ {
+ name => 'rtl8187se',
+ matching => 'r8180',
+ description => 'Realtek RTL8180 / RTL8185 WiFi',
+ kernel_module => 1,
+ },
+);
+
+sub get_packages { 'wireless-tools' }
+
+sub get_thirdparty_settings() {
+ \@thirdparty_settings;
+}
+
+sub setup_thirdparty {
+ my ($self, $in) = @_;
+ require network::rfswitch;
+ network::rfswitch::configure();
+ if ($self->get_driver eq 'ndiswrapper') {
+ require network::ndiswrapper;
+ my @devices = map { network::ndiswrapper::present_devices($_) } network::ndiswrapper::installed_drivers();
+ return {} if member($self->{device}, @devices) && network::ndiswrapper::find_interface($self->{device});
+ }
+ my $thirdparty = $self->SUPER::setup_thirdparty($in);
+ my $driver = $self->get_driver;
+ if ($self->{thirdparty} && $driver eq 'ipw3945' && !$self->rf_killed && !$self->SUPER::check_device) {
+ log::explanations("Reloading module $driver");
+ eval { modules::unload($driver) };
+ eval { modules::load($driver) };
+ }
+ $thirdparty;
+}
+
+sub rf_killed {
+ my ($self) = @_;
+ if ($self->{device}{sysfs_device}) {
+ my $rf_kill_path = $self->{device}{sysfs_device} . "/rf_kill";
+ if (-e $rf_kill_path) {
+ my $rf_kill = chomp_(cat_($rf_kill_path));
+ #- for ipw drivers, 0 means no RF kill switch
+ return $rf_kill != 0;
+ }
+ }
+ undef;
+}
+
+sub check_device {
+ my ($self) = @_;
+ if ($self->rf_killed) {
+ $self->{device}{error} = N("Your wireless card is disabled, please enable the wireless switch (RF kill switch) first.");
+ return 0;
+ }
+ return $self->SUPER::check_device;
+}
+
+sub load_interface_settings {
+ my ($self) = @_;
+ $self->network::connection::load_interface_settings;
+ $self->{hide_passwords} = 1;
+ # override ifcfg with network-specific settings if available
+ my $network = $self->get_selected_network;
+ $self->{ifcfg}= $network ?
+ get_network_ifcfg($network->{ap}) || get_network_ifcfg($network->{essid}) :
+ $self->{ifcfg};
+
+ $self->SUPER::map_ifcfg2config_settings;
+}
+
+sub get_networks {
+ my ($self, $o_net) = @_;
+ require network::monitor;
+ ($self->{networks}, $self->{control}{roaming}) = network::monitor::list_wireless($o_net && $o_net->{monitor}, $self->get_interface);
+ $self->probed_networks;
+ $self->{networks};
+}
+
+sub refresh_roaming_ids {
+ my ($self) = @_;
+ #- needed when switching from non-roaming to roaming
+ #- or after restarting wpa_supplicant
+ #- to get fresh wpa_supplicant network IDs
+ get_networks($self) if $self->{control}{roaming};
+}
+
+sub selected_network_is_configured {
+ my ($self) = @_;
+ $self->refresh_roaming_ids;
+ $self->SUPER::selected_network_is_configured;
+}
+
+sub guess_network {
+ my ($_self) = @_;
+ #- FIXME: try to find the AP matching $self->{ifcfg}{WIRELESS_ESSID};
+}
+
+sub get_network_ifcfg {
+ my ($ssid) = @_;
+ require network::network;
+ my $file = $::prefix . $network::network::wireless_d . '/' . $ssid;
+ -f $file && { getVarsFromSh($file) };
+}
+
+sub guess_network_access_settings {
+ my ($self) = @_;
+
+ my $network = $self->get_selected_network;
+ my $ifcfg = $self->{ifcfg};
+ $ifcfg ||= {};
+
+ $self->{access}{network}{bssid} = $network && $network->{hidden} && $network->{ap};
+ $self->{access}{network}{essid} = $network && $network->{essid} || $ifcfg->{WIRELESS_ESSID} || !$network && "any";
+ ($self->{access}{network}{key}, my $restricted, $self->{access}{network}{force_ascii_key}) =
+ get_wep_key_from_iwconfig($ifcfg->{WIRELESS_ENC_KEY});
+
+ $self->{access}{network}{encryption} =
+ $network && $network->{flags} =~ /eap/i ?
+ 'wpa-eap' :
+ $network && $network->{flags} =~ /wpa/i ?
+ 'wpa-psk' :
+ $network && $network->{flags} =~ /wep/i || $self->{access}{network}{key} ?
+ $ifcfg->{WIRELESS_ENC_MODE} || ($restricted ? 'restricted' : 'open') :
+ 'none';
+
+ undef $self->{ifcfg}{WIRELESS_IWPRIV} if is_old_rt2x00($self->get_driver) && $self->{ifcfg}{WIRELESS_IWPRIV} =~ /WPAPSK/;
+
+ my $system_file = '/etc/sysconfig/drakx-net';
+ my %global_settings = getVarsFromSh($system_file);
+ $self->{control}{roaming} =
+ (exists $self->{ifcfg}{WIRELESS_WPA_DRIVER} || text2bool($global_settings{ROAMING}))
+ && !is_old_rt2x00($self->get_driver);
+
+ $self->{access}{network}{mode} =
+ $network && $network->{mode} ||
+ $ifcfg->{WIRELESS_MODE} ||
+ 'Managed';
+
+ wpa_supplicant_load_eap_settings($self->{access}{network}) if $self->need_wpa_supplicant;
+}
+
+sub get_network_access_settings_label { N("Wireless settings") }
+
+sub get_network_access_settings {
+ my ($self) = @_;
+ [
+ { label => N("Operating Mode"), val => \$self->{access}{network}{mode},
+ list => [ N_("Ad-hoc"), N_("Managed"), N_("Master"), N_("Repeater"), N_("Secondary"), N_("Auto") ],
+ format => \&translate,
+ },
+ { label => N("Network name (ESSID)"), val => \$self->{access}{network}{essid},
+ disabled => sub { my $network = $self->get_selected_network; $network && $network->{essid} } },
+ { label => N("Encryption mode"), val => \$self->{access}{network}{encryption}, list => [ keys %wireless_enc_modes ],
+ sort => 1, format => sub { translate($wireless_enc_modes{$_[0]}) } },
+ { label => N("Encryption key"), val => \$self->{access}{network}{key},
+ hidden => sub { $self->{hide_passwords} },
+ disabled => sub { member($self->{access}{network}{encryption}, qw(none wpa-eap)) } },
+ { text => N("Hide password"),
+ type => "bool", val => \$self->{hide_passwords} },
+ { text => N("Force using this key as ASCII string (e.g. for Livebox)"),
+ type => "bool", val => \$self->{access}{network}{force_ascii_key},
+ disabled => sub {
+ #- only for WEP keys looking like hexadecimal
+ !member($self->{access}{network}{encryption}, qw(open restricted)) ||
+ !get_hex_key($self->{access}{network}{key});
+ } },
+ { label => N("EAP Login/Username"), val => \$self->{access}{network}{eap_identity},
+ disabled => sub { $self->{access}{network}{encryption} ne 'wpa-eap' },
+ help => N("The login or username. Format is plain text. If you
+need to specify domain then try the untested syntax
+ DOMAIN\\username") },
+ { label => N("EAP Password"), val => \$self->{access}{network}{eap_password},
+ hidden => sub { $self->{hide_passwords} },
+ disabled => sub { $self->{access}{network}{encryption} ne 'wpa-eap' },
+ help => N(" Password: A string.
+Note that this is not the same thing as a psk.
+____________________________________________________
+RELATED ADDITIONAL INFORMATION:
+In the Advanced Page, you can select which EAP mode
+is used for authentication. For the eap mode setting
+ Auto Detect: implies all possible modes are tried.
+
+If Auto Detect fails, try the PEAP TTLS combo bofore others
+Note:
+ The settings MD5, MSCHAPV2, OTP and GTC imply
+automatically PEAP and TTLS modes.
+ TLS mode is completely certificate based and may ignore
+the username and password values specified here.") },
+ { label => N("EAP client certificate"), val => \$self->{access}{network}{eap_client_cert},
+ disabled => sub { $self->{access}{network}{encryption} ne 'wpa-eap' },
+ help => N("The complete path and filename of client certificate. This is
+only used for EAP certificate based authentication. It could be
+considered as the alternative to username/password combo.
+ Note: other related settings are shown on the Advanced page.") },
+ { label => N("Network ID"), val => \$self->{ifcfg}{WIRELESS_NWID}, advanced => 1 },
+ { label => N("Operating frequency"), val => \$self->{ifcfg}{WIRELESS_FREQ}, advanced => 1 },
+ { label => N("Sensitivity threshold"), val => \$self->{ifcfg}{WIRELESS_SENS}, advanced => 1 },
+ { label => N("Bitrate (in b/s)"), val => \$self->{ifcfg}{WIRELESS_RATE}, advanced => 1 },
+ { label => N("RTS/CTS"), val => \$self->{ifcfg}{WIRELESS_RTS}, advanced => 1,
+ help => N("RTS/CTS adds a handshake before each packet transmission to make sure that the
+channel is clear. This adds overhead, but increase performance in case of hidden
+nodes or large number of active nodes. This parameter sets the size of the
+smallest packet for which the node sends RTS, a value equal to the maximum
+packet size disable the scheme. You may also set this parameter to auto, fixed
+or off.")
+ },
+ { label => N("Fragmentation"), val => \$self->{ifcfg}{WIRELESS_FRAG}, advanced => 1 },
+ { label => N("iwconfig command extra arguments"), val => \$self->{ifcfg}{WIRELESS_IWCONFIG}, advanced => 1,
+ help => N("Here, one can configure some extra wireless parameters such as:
+ap, channel, commit, enc, power, retry, sens, txpower (nick is already set as the hostname).
+
+See iwconfig(8) man page for further information."),
+ },
+ { label =>
+ #-PO: split the "xyz command extra argument" translated string into two lines if it's bigger than the english one
+ N("iwspy command extra arguments"), val => \$self->{ifcfg}{WIRELESS_IWSPY}, advanced => 1,
+ help => N("iwspy is used to set a list of addresses in a wireless network
+interface and to read back quality of link information for each of those.
+
+This information is the same as the one available in /proc/net/wireless :
+quality of the link, signal strength and noise level.
+
+See iwpspy(8) man page for further information."),
+ },
+ { label => N("iwpriv command extra arguments"), val => \$self->{ifcfg}{WIRELESS_IWPRIV}, advanced => 1,
+ disabled => sub { $self->need_rt2x00_iwpriv },
+ help => N("iwpriv enable to set up optionals (private) parameters of a wireless network
+interface.
+
+iwpriv deals with parameters and setting specific to each driver (as opposed to
+iwconfig which deals with generic ones).
+
+In theory, the documentation of each device driver should indicate how to use
+those interface specific commands and their effect.
+
+See iwpriv(8) man page for further information."),
+ },
+ { label => N("EAP Protocol"), val => \$self->{access}{network}{forceeap},
+ list => [ N_("Auto Detect"), N_("WPA2"), N_("WPA") ],
+ sort => 1, format => \&translate, advanced => 1,
+ help => N("Auto Detect is recommended as it first tries WPA version 2 with
+a fallback to WPA version 1") },
+ { label => N("EAP Mode"), val => \$self->{access}{network}{eap_eap},
+ list => [ N_("Auto Detect"), N_("PEAP"), N_("TTLS"), N_("TLS"), N_("MSCHAPV2"), N_("MD5"), N_("OTP"), N_("GTC"), N_("LEAP") , N_("PEAP TTLS"), N_("TTLS TLS") ],
+ sort => 1, format => \&translate, advanced => 1, },
+ { label => N("EAP key_mgmt"), val => \$self->{access}{network}{eap_key_mgmt}, advanced => 1,
+ disabled => sub { $self->{access}{network}{encryption} ne 'wpa-eap' },
+ help => N("list of accepted authenticated key management protocols.
+possible values are WPA-EAP, IEEE8021X, NONE") },
+ { label => N("EAP outer identity"), val => \$self->{access}{network}{eap_anonymous_identity}, advanced => 1,
+ disabled => sub { $self->{access}{network}{encryption} ne 'wpa-eap' },
+ help => N("Anonymous identity string for EAP: to be used as the
+unencrypted identity with EAP types that support different
+tunnelled identity, e.g., TTLS") },
+ { label => N("EAP phase2"), val => \$self->{access}{network}{eap_phase2}, advanced => 1,
+ disabled => sub { $self->{access}{network}{encryption} ne 'wpa-eap' } ,
+ help => N("Inner authentication with TLS tunnel parameters.
+input is string with field-value pairs, Examples:
+auth=MSCHAPV2 for PEAP or
+autheap=MSCHAPV2 autheap=MD5 for TTLS") },
+ { label => N("EAP CA certificate"), val => \$self->{access}{network}{eap_ca_cert}, advanced => 1,
+ disabled => sub { $self->{access}{network}{encryption} ne 'wpa-eap' },
+ help => N("Full file path to CA certificate file (PEM/DER). This file
+can have one or more trusted CA certificates. If ca_cert are not
+included, server certificate will not be verified. If possible,
+a trusted CA certificate should always be configured
+when using TLS or TTLS or PEAP.") },
+ { label => N("EAP certificate subject match"), val => \$self->{access}{network}{eap_subject_match}, advanced => 1,
+ disabled => sub { $self->{access}{network}{encryption} ne 'wpa-eap' },
+ help => N(" Substring to be matched against the subject of
+the authentication server certificate. If this string is set,
+the server certificate is only accepted if it contains this
+string in the subject. The subject string is in following format:
+/C=US/ST=CA/L=San Francisco/CN=Test AS/emailAddress=as\@example.com") },
+ { label => N("Extra directives"), val => \$self->{access}{network}{extra}, advanced => 1,
+ help => N("Here one can pass extra settings to wpa_supplicant
+The expected format is a string field=value pair. Multiple values
+maybe specified, separating each value with the # character.
+Note: directives are passed unchecked and may cause the wpa
+negotiation to fail silently. Supported directives are preserved
+across editing.
+Supported directives are :
+ disabled, id_str, bssid, priority, auth_alg, eapol_flags,
+ proactive_key_caching, peerkey, ca_path, private_key,
+ private_key_passwd, dh_file, altsubject_match, phase1,
+ fragment_size and eap_workaround, pairwise, group
+ Others such as key_mgmt, eap maybe used to force
+ special settings different from the U.I settings.") },
+ ];
+}
+
+sub check_network_access_settings {
+ my ($self) = @_;
+
+ if (!member($self->{access}{network}{encryption}, qw(none wpa-eap)) && !$self->{access}{network}{key}) {
+ $self->{network_access}{error}{message} = N("An encryption key is required.");
+ $self->{network_access}{error}{field} = \$self->{access}{network}{key};
+ return 0;
+ }
+
+ if ($self->{access}{network}{encryption} eq 'wpa-psk' &&
+ !convert_psk_key_for_wpa_supplicant($self->{access}{network}{key})) {
+ $self->{network_access}{error}{message} = N("The pre-shared key should have between 8 and 63 ASCII characters, or 64 hexadecimal characters.");
+ $self->{network_access}{error}{field} = \$self->{access}{network}{key};
+ return 0;
+ }
+ if (member($self->{access}{network}{encryption}, qw(open restricted)) &&
+ !convert_wep_key_for_wpa_supplicant($self->{access}{network}{key}, $self->{access}{network}{force_ascii_key})) {
+ $self->{network_access}{error}{message} = N("The WEP key should have at most %d ASCII characters or %d hexadecimal characters.",
+ $wpa_supplicant_max_wep_key_len, $wpa_supplicant_max_wep_key_len * 2);
+ $self->{network_access}{error}{field} = \$self->{access}{network}{key};
+ return 0;
+ }
+
+ if ($self->{ifcfg}{WIRELESS_FREQ} && $self->{ifcfg}{WIRELESS_FREQ} !~ /[0-9.]*[kGM]/) {
+ $self->{network_access}{error}{message} = N("Freq should have the suffix k, M or G (for example, \"2.46G\" for 2.46 GHz frequency), or add enough '0' (zeroes).");
+ $self->{network_access}{error}{field} = \$self->{ifcfg}{WIRELESS_FREQ};
+ return 0;
+ }
+
+ if ($self->{ifcfg}{WIRELESS_RATE} && $self->{ifcfg}{WIRELESS_RATE} !~ /[0-9.]*[kGM]/) {
+ $self->{network_access}{error}{message} = N("Rate should have the suffix k, M or G (for example, \"11M\" for 11M), or add enough '0' (zeroes).");
+ $self->{network_access}{error}{field} = \$self->{ifcfg}{WIRELESS_RATE};
+ return 0;
+ }
+
+ return 1;
+}
+
+sub get_control_settings {
+ my ($self) = @_;
+ [
+ @{$self->SUPER::get_control_settings},
+ { text => N("Allow access point roaming"), val => \$self->{control}{roaming}, type => "bool",
+ disabled => sub { is_wpa_supplicant_blacklisted($self->get_driver) } },
+ ];
+}
+
+sub need_wpa_supplicant {
+ my ($self) = @_;
+ ($self->{control}{roaming} || $self->{access}{network}{encryption} =~ /^wpa-/) && !is_old_rt2x00($self->get_driver);
+}
+
+sub install_packages {
+ my ($self, $in) = @_;
+ if ($self->need_wpa_supplicant) {
+ $in->do_pkgs->ensure_is_installed('wpa_supplicant', '/usr/sbin/wpa_supplicant') or return;
+ $in->do_pkgs->ensure_is_installed('mandi', '/usr/sbin/mandi');
+ }
+ $self->SUPER::install_packages($in);
+}
+
+
+sub build_ifcfg_settings {
+ my ($self) = @_;
+
+ # if we are not using WEP, the key is always ASCII (#52128)
+ $self->{access}{network}{force_ascii_key} = 1 unless member($self->{access}{network}{encryption}, qw(open restricted));
+
+ my $settings = {
+ WIRELESS_MODE => $self->{access}{network}{mode},
+ if_($self->need_wpa_supplicant,
+ WIRELESS_WPA_DRIVER => wpa_supplicant_get_driver($self->get_driver),
+ WIRELESS_WPA_REASSOCIATE => bool2yesno($self->need_wpa_supplicant_reassociate),
+ MII_NOT_SUPPORTED => 'no',
+ ),
+ WIRELESS_ESSID => $self->{access}{network}{essid},
+ if_($self->{access}{network}{encryption} ne 'none',
+ WIRELESS_ENC_KEY => convert_wep_key_for_iwconfig($self->{access}{network}{key}, $self->{access}{network}{force_ascii_key})),
+ if_(member($self->{access}{network}{encryption}, qw(open restricted)),
+ WIRELESS_ENC_MODE => $self->{access}{network}{encryption}),
+ if_($self->need_rt2x00_iwpriv,
+ #- use iwpriv for WPA with rt2400/rt2500 drivers, they don't plan to support wpa_supplicant
+ WIRELESS_IWPRIV => qq(set AuthMode=WPAPSK
+set EncrypType=TKIP
+set SSID=$self->{access}{network}{essid}
+set WPAPSK="$self->{access}{network}{key}"
+set TxRate=0)),
+ (map { $_ => $self->{ifcfg}{$_} }
+ qw(WIRELESS_NWID WIRELESS_FREQ WIRELESS_SENS WIRELESS_RATE WIRELESS_RTS WIRELESS_FRAG WIRELESS_IWCONFIG WIRELESS_IWSPY), if_(!$self->need_rt2x00_iwpriv, 'WIRELESS_IWPRIV')),
+ };
+ $self->SUPER::build_ifcfg_settings($settings);
+}
+
+sub add_network_to_wpa_supplicant {
+ my ($self) = @_;
+ if ($self->{access}{network}{encryption} eq 'wpa-eap') {
+ wpa_supplicant_add_eap_network($self->{access}{network});
+ } else {
+ wpa_supplicant_add_network($self->{access}{network});
+ }
+ #- this should be handled by the monitoring daemon instead
+ run_program::run('/usr/sbin/wpa_cli', 'reconfigure');
+}
+
+sub write_settings {
+ my ($self, $o_net, $o_modules_conf) = @_;
+
+ my $network = $self->get_selected_network;
+ network::network::write_wireless_conf($_, $self->build_ifcfg_settings) foreach
+ grep { $_ } ($network ? $network->{ap} : ()), $self->{access}{network}{essid};
+
+ $self->add_network_to_wpa_supplicant if $self->need_wpa_supplicant;
+
+ wlan_ng_configure($self->{access}{network}{essid}, $self->{access}{network}{key}, $self->get_interface, $self->get_driver) if $self->{thirdparty}{name} eq 'prism2';
+
+ $self->SUPER::write_settings($o_net, $o_modules_conf);
+}
+
+sub apply_network_selection {
+ my ($self) = @_;
+ require network::network;
+ my $file = network::network::get_ifcfg_file($self->get_interface);
+ network::network::write_interface_settings($self->build_ifcfg_settings, $file);
+
+ $self->add_network_to_wpa_supplicant if $self->need_wpa_supplicant;
+}
+
+sub network_is_configured {
+ my ($self, $network) = @_;
+ if ($self->{control}{roaming}) {
+ return defined $network->{id};
+ } else {
+ my $wireless_ifcfg = get_network_ifcfg($network->{ap}) || defined $network->{essid} && get_network_ifcfg($network->{essid});
+ return $wireless_ifcfg;
+ }
+}
+
+sub connect {
+ my ($self, $_in, $net) = @_;
+
+ $self->SUPER::connect;
+
+ if ($self->{control}{roaming}) {
+ my $network_id;
+ foreach (0 .. 1) {
+ $self->refresh_roaming_ids if $_;
+ my $network = $self->get_selected_network;
+ $network_id = $network->{id} if $network && defined $network->{id};
+ }
+ if (defined $network_id) {
+ if ($net->{monitor}) {
+ log::explanations("selecting wpa_supplicant network $network_id through network monitor");
+ eval { $net->{monitor}->select_network($network_id) };
+ return !$@;
+ } else {
+ run_program::run('/usr/sbin/wpa_cli', 'select_network', $network_id);
+ }
+ }
+ }
+}
+
+sub get_status_message {
+ my ($self, $status) = @_;
+ my $interface = $self->get_interface;
+ my ($current_essid, $current_ap) = get_access_point($interface);
+ my $network = $current_essid || $current_ap && "[$current_ap]";
+ {
+ link_up => N("Associated to wireless network \"%s\" on interface %s", $network, $interface),
+ link_down => N("Lost association to wireless network on interface %s", $interface),
+ }->{$status} || $self->SUPER::get_status_message($status);
+}
+
+
+
+my $wpa_supplicant_conf = "/etc/wpa_supplicant.conf";
+
+sub get_access_point {
+ my ($intf) = @_;
+ (chomp_(`/sbin/iwgetid -r $intf 2>/dev/null`), lc(chomp_(`/sbin/iwgetid -r -a $intf 2>/dev/null`)));
+}
+
+sub is_old_rt2x00 {
+ my ($module) = @_;
+ member($module, qw(rt2400 rt2500 rt2570 rt61 rt73));
+}
+
+sub is_wpa_supplicant_blacklisted {
+ my ($module) = @_;
+ is_old_rt2x00($module);
+}
+
+sub need_wpa_supplicant_reassociate {
+ my ($self) = @_;
+ $self->get_driver eq 'rt61pci';
+}
+
+sub need_rt2x00_iwpriv {
+ my ($self) = @_;
+ is_old_rt2x00($self->get_driver) && $self->{access}{network}{encryption} eq 'wpa-psk';
+}
+
+sub get_hex_key {
+ my ($key) = @_;
+ #- odd number or non-hexa characters, consider the key as ASCII and prepend "s:"
+ if ($key =~ /^([[:xdigit:]]{4}[\:-]?)+[[:xdigit:]]{2,}$/) {
+ $key =~ s/[\:-]//g;
+ return lc($key);
+ }
+}
+
+sub convert_wep_key_for_iwconfig {
+ my ($real_key, $force_ascii) = @_;
+ !$force_ascii && get_hex_key($real_key) || "s:$real_key";
+}
+
+sub convert_wep_key_for_wpa_supplicant {
+ my ($key, $force_ascii) = @_;
+ if (my $hex_key = !$force_ascii && get_hex_key($key)) {
+ return length($hex_key) <= $wpa_supplicant_max_wep_key_len * 2 && $hex_key;
+ } else {
+ return length($key) <= $wpa_supplicant_max_wep_key_len && qq("$key");
+ }
+}
+
+sub get_wep_key_from_iwconfig {
+ my ($key) = @_;
+ my ($mode, $real_key) = $key =~ /^(?:(open|restricted)\s+)?(.*)$/;
+ my $is_ascii = $real_key =~ s/^s://;
+ my $force_ascii = to_bool($is_ascii && get_hex_key($real_key));
+ ($real_key, $mode eq 'restricted', $force_ascii);
+}
+
+sub convert_psk_key_for_wpa_supplicant {
+ my ($key) = @_;
+ my $l = length($key);
+ $l == 64 ?
+ get_hex_key($key) :
+ $l >= 8 && $l <= 63 ?
+ qq("$key") :
+ undef;
+}
+
+#- FIXME: to be improved (quotes, comments)
+sub wlan_ng_update_vars {
+ my ($file, $vars) = @_;
+ substInFile {
+ while (my ($key, $value) = each(%$vars)) {
+ s/^#?\Q$key\E=(?:"[^#]*"|[^#\s]*)(\s*#.*)?/$key=$value$1/ and delete $vars->{$key};
+ }
+ $_ .= join('', map { "$_=$vars->{$_}\n" } keys %$vars) if eof;
+ } $file;
+}
+
+sub wlan_ng_configure {
+ my ($essid, $key, $device, $module) = @_;
+ my $wlan_conf_file = "$::prefix/etc/wlan/wlan.conf";
+ my @wlan_devices = split(/ /, (cat_($wlan_conf_file) =~ /^WLAN_DEVICES="(.*)"/m)[0]);
+ push @wlan_devices, $device unless member($device, @wlan_devices);
+ #- enable device and make it use the choosen ESSID
+ wlan_ng_update_vars($wlan_conf_file,
+ {
+ WLAN_DEVICES => qq("@wlan_devices"),
+ "SSID_$device" => qq("$essid"),
+ "ENABLE_$device" => "y"
+ });
+
+ my $wlan_ssid_file = "$::prefix/etc/wlan/wlancfg-$essid";
+ #- copy default settings for this ESSID if config file does not exist
+ -f $wlan_ssid_file or cp_f("$::prefix/etc/wlan/wlancfg-DEFAULT", $wlan_ssid_file);
+
+ #- enable/disable encryption
+ wlan_ng_update_vars($wlan_ssid_file,
+ {
+ (map { $_ => $key ? "true" : "false" } qw(lnxreq_hostWEPEncrypt lnxreq_hostWEPDecrypt dot11PrivacyInvoked dot11ExcludeUnencrypted)),
+ AuthType => $key ? qq("sharedkey") : qq("opensystem"),
+ if_($key,
+ dot11WEPDefaultKeyID => 0,
+ dot11WEPDefaultKey0 => qq("$key")
+ )
+ });
+ #- hide settings for non-root users
+ chmod 0600, $wlan_conf_file;
+ chmod 0600, $wlan_ssid_file;
+
+ #- apply settings on wlan interface
+ require services;
+ services::restart($module eq 'prism2_cs' ? 'pcmcia' : 'wlan');
+}
+
+sub wpa_supplicant_get_driver {
+ my ($module) = @_;
+ $module =~ /^hostap_/ ? "hostap" :
+ $module eq "prism54" ? "prism54" :
+ $module =~ /^ath_/ ? "madwifi" :
+ $module =~ /^at76c50|atmel_/ ? "atmel" :
+ "wext";
+}
+
+sub wpa_supplicant_add_network {
+ my ($ui_input) = @_;
+ my $conf = wpa_supplicant_read_conf();
+
+ # use shorter variables
+ my $essid = $ui_input->{essid};
+ my $bssid = $ui_input->{bssid};
+ my $enc_mode = $ui_input->{encryption};
+ my $key = $ui_input->{key};
+ my $force_ascii = $ui_input->{force_ascii_key};
+ my $mode = $ui_input->{mode};
+
+ my $network = {
+ ssid => qq("$essid"),
+ scan_ssid => to_bool($bssid), #- hidden or non-broadcasted SSIDs
+ if_($bssid, bssid => $bssid),
+ if_($enc_mode ne 'none', priority => 1),
+ };
+
+ if ($enc_mode eq 'wpa-psk') {
+ $network->{psk} = convert_psk_key_for_wpa_supplicant($key);
+ } else {
+ $network->{key_mgmt} = 'NONE';
+ $network->{mode} = to_bool($mode eq 'Ad-hoc');
+ if (member($enc_mode, qw(open restricted))) {
+ put_in_hash($network, {
+ wep_key0 => convert_wep_key_for_wpa_supplicant($key, $force_ascii),
+ wep_tx_keyidx => 0,
+ auth_alg => $enc_mode eq 'restricted' ? 'SHARED' : 'OPEN',
+ });
+ }
+ }
+
+ #- handle extra variables as final overides
+ handle_extra_params($network, $ui_input->{extra});
+
+ @$conf = difference2($conf, [ wpa_supplicant_find_similar($conf, $network) ]);
+ push @$conf, $network;
+ wpa_supplicant_write_conf($conf);
+}
+
+sub wpa_supplicant_find_similar {
+ my ($conf, $network) = @_;
+ grep {
+ my $current = $_;
+ any { exists $network->{$_} && $network->{$_} eq $current->{$_} } qw(ssid bssid);
+ } @$conf;
+}
+
+sub wpa_supplicant_read_conf() {
+ my @conf;
+ my $network;
+ foreach (cat_($::prefix . $wpa_supplicant_conf)) {
+ if ($network) {
+ #- in a "network = {}" block
+ # value is either the string with "quotes" - or a full-length string
+ if (/^\s*(\w+)=\s*(?|([^"].*)|("[^"]*")).*$/) {
+ $network->{$1} = $2;
+ } elsif (/^\}/) {
+ #- end of network block
+ push @conf, $network;
+ undef $network;
+ }
+ } elsif (/^\s*network={/) {
+ #- beginning of a new network block
+ $network = {};
+ }
+ }
+ \@conf;
+}
+
+sub wpa_supplicant_write_conf {
+ my ($conf) = @_;
+ my $buf;
+ my @conf = @$conf;
+ my $network;
+ foreach (cat_($::prefix . $wpa_supplicant_conf)) {
+ if ($network) {
+ #- in a "network = {}" block
+ if (/^\s*(\w+)=(.*)$/) {
+ push @{$network->{entries}}, { key => $1, value => $2 };
+ member($1, qw(ssid bssid)) and $network->{$1} = $2;
+ } elsif (/^\}/) {
+ #- end of network block, write it
+ $buf .= "network={$network->{comment}\n";
+
+ my $new_network = first(wpa_supplicant_find_similar(\@conf, $network));
+ foreach (@{$network->{entries}}) {
+ my $key = $_->{key};
+ if ($new_network) {
+ #- do not write entry if not provided in the new network
+ exists $new_network->{$key} or next;
+ #- update value from the new network
+ $_->{value} = delete $new_network->{$key};
+ }
+ $buf .= " ";
+ $buf .= "$key=$_->{value}" if $key;
+ $buf .= "$_->{comment}\n";
+ }
+ if ($new_network) {
+ #- write new keys
+ while (my ($key, $value) = each(%$new_network)) {
+ $buf .= " $key=$value\n";
+ }
+ }
+ $buf .= "}\n";
+ $new_network and @conf = grep { $_ != $new_network } @conf;
+ undef $network;
+ } else {
+ #- unrecognized, keep it anyway
+ push @{$network->{entries}}, { comment => $_ };
+ }
+ } else {
+ if (/^\s*network={/) {
+ #- beginning of a new network block
+ $network = {};
+ } else {
+ #- keep other options, comments
+ $buf .= $_;
+ }
+ }
+ }
+
+ #- write remaining networks
+ foreach (@conf) {
+ $buf .= "\nnetwork={\n";
+ while (my ($key, $value) = each(%$_)) {
+ $buf .= " $key=$value\n";
+ }
+ $buf .= "}\n";
+ }
+
+ output($::prefix . $wpa_supplicant_conf, $buf);
+ #- hide keys for non-root users
+ chmod 0600, $::prefix . $wpa_supplicant_conf;
+}
+
+sub wpa_supplicant_load_eap_settings {
+ my ($network) = @_;
+ my $quoted_essid = qq("$network->{essid}");
+ my $conf = wpa_supplicant_read_conf();
+ foreach my $old_net (@$conf) {
+ if ($old_net->{ssid} eq $network->{essid} || $old_net->{ssid} eq $quoted_essid) {
+ $network->{extra} = '';
+ foreach my $eap_var (keys %eap_vars) {
+ next if $eap_var eq 'ssid';
+ my $ui_var = join('_', "eap", $eap_var);
+ if (defined $old_net->{$eap_var}) {
+ if ($eap_vars{$eap_var} == 0) {
+ if ($network->{extra} eq "") {
+ $network->{extra} = "$eap_var=$old_net->{$eap_var}";
+ } else {
+ $network->{extra} = join('#', $network->{extra}, "$eap_var=$old_net->{$eap_var}");
+ }
+ } else {
+ $network->{$ui_var} = $old_net->{$eap_var};
+ #- remove quotes on selected variables
+ $network->{$ui_var} = $1 if $eap_vars{$eap_var} == 2 && $network->{$ui_var} =~ /^"(.*)"$/;
+ if ($eap_var eq "proto") {
+ $network->{forceeap} = 'WPA2' if $old_net->{$eap_var} eq "RSN";
+ $network->{forceeap} = 'WPA' if $old_net->{$eap_var} eq "WPA";
+ }
+ }
+ }
+ }
+ last;
+ }
+ }
+}
+
+sub handle_extra_params {
+ my ($network, $extra) = @_;
+ #- handle extra variables as final overides
+ if (defined $extra && $extra ne "") {
+ #- FIXME: should split it on what the # sign?
+ foreach my $extra_var (split('#', $extra)) {
+ my ($key, $val) = split('=', $extra_var, 2);
+ $network->{$key} = $val;
+ }
+ }
+}
+sub wpa_supplicant_add_eap_network {
+ my ($ui_input) = @_;
+
+ #- expect all variables for us to be prefixed with eap_
+ my $conf = wpa_supplicant_read_conf();
+ my $default_eap_cfg = {
+ pairwise => 'CCMP TKIP',
+ group => 'CCMP TKIP',
+ proto => 'RSN WPA',
+ key_mgmt => 'WPA-EAP IEEE8021X NONE',
+ scan_ssid => 1,
+ };
+ if ($ui_input->{forceeap} eq 'WPA') {
+ #- WPA only
+ $default_eap_cfg->{pairwise} = 'TKIP';
+ $default_eap_cfg->{group} = 'TKIP';
+ $default_eap_cfg->{proto} = 'WPA';
+ } elsif ($ui_input->{forceeap} eq 'WPA2') {
+ #- WPA2 only
+ $default_eap_cfg->{pairwise} = 'CCMP TKIP';
+ $default_eap_cfg->{group} = 'CCMP TKIP';
+ $default_eap_cfg->{proto} = 'RSN';
+ }
+ my $network = { ssid => qq("$ui_input->{essid}") };
+ #- set the values
+ foreach my $eap_var (keys %eap_vars) {
+ my $key = join('_', "eap", $eap_var);
+ if (!defined $ui_input->{$key} || $ui_input->{$key} =~ /auto detect/i) {
+ $network->{$eap_var} = $default_eap_cfg->{$eap_var} if $default_eap_cfg->{$eap_var};
+ } else {
+ #- do not define if blank, the save routine will delete entry from file
+ next if !$ui_input->{$key};
+ $network->{$eap_var} = $eap_vars{$eap_var} == 2 ? qq("$ui_input->{$key}") : $ui_input->{$key};
+ }
+ }
+ #- handle extra variables as final overides
+ handle_extra_params($network, $ui_input->{extra});
+
+ $network->{mode} = to_bool($ui_input->{mode} eq 'Ad-hoc');
+
+ @$conf = difference2($conf, [ wpa_supplicant_find_similar($conf, $network) ]);
+ push @$conf, $network;
+ wpa_supplicant_write_conf($conf);
+}
+1;