#!/usr/bin/perl -w ## ----- ----- ----- ----- ----- ----- ----- ----- ----- ----- ## LANforge server script for associating virtual stations ## to an arbitrary SSID. You have options for creating a series ## of Layer-3 connections per station created. Support for various ## security modes for stations: wep, wpa, wpa2. ## ## Install: ## copy this script to /home/lanforge/scripts ## ## Preparation: ## This script expects a free radio (like wiphy0) to create ## wifi stations on. It also expects an upstream wired port to ## make tcp connections to. These ports should be able to ## communicate with each other. ## ## Usage Overview: ## Use -h to show options. ## There are two activities that this script presently performs: ## ## Step1: create 1 wifi station, pass traffic to the upstream port ## back and forth, and then disassociate. This activity could ## be split into several steps if testing traffic up- or ## down-stream only is desired. ## ## Step2: create many wifi stations, wait until we see IPs appear ## on them and then disassociate all of them. This activity could ## also be modified to test for wifi association instead of address ## aquisition. The present example uses static address assignment. ## ## add: create and delete WiFi Virtual Radios. Also has option to ## create station on specified virtual radio. ## ## (C) 2013, Candela Technologies Inc. support@candelatech.com ## ----- ----- ----- ----- ----- ----- ----- ----- ----- ----- package main; use strict; use warnings; use diagnostics; use Carp; #$SIG{ __DIE__ } = sub { Carp::confess( @_ ) }; #$SIG{ __WARN__ } = sub { Carp::confess( @_ ) }; use POSIX qw(ceil floor); use Scalar::Util; #::looks_like_number; use Getopt::Long; use Socket; # Un-buffer output $| = 1; use Cwd qw(getcwd); my $cwd = getcwd(); # this is pedantic necessity for the following use statements if ( $cwd =~ q(.*LANforge-Server\scripts$)) { use lib '/home/lanforge/scripts' } else { use lib '/home/lanforge/scripts'; } use List::Util qw(first); use LANforge::Endpoint; use LANforge::Port; use LANforge::Utils; use Net::Telnet (); our $num_stations = 1; our $netmask = "255.255.0.0"; our $default_ip_addr = "DHCP"; # or IP my $log_cli = "unset"; # use ENV{'LOG_CLI'} # the upstream port should have an IP in same subnet range # and we're assuming the port is on the same resource (1). our $upstream_port = "eth1"; # Step 1 upstream port our $sta_wiphy = "wiphy0"; # physical parent (radio) of virtual stations our $phy_channel = -1; # channel number our $phy_antenna = 0; # number of antennas, all our %wiphy_bssids = (); our $admin_down_on_add = 0; our $ssid; our $first_sta = "sta100"; our $passphrase = ''; our $change_mac = 0; our $min_tx = "10000000"; our $max_tx = "SAME"; our $security = "open"; our $xsec = ""; # extra 802.1* options: use-11u,use-11u-internet,use-dot1x our %sec_options = ( "open" => 0x0, "wpa" => 0x10, "wep" => 0x200, "wpa2" => 0x400, "no-ht40" => 0x800, # Disable ht-40 "use-scan-ssid" => 0x1000, # Enable SCAN-SSID flag in wpa_supplicant. "use-pasv-scan" => 0x2000, # Use passive scanning (don't send probe requests). "no-sgi" => 0x4000, # Disable SGI (Short Guard Interval). "use-radio-migration" => 0x8000, # OK-To-Migrate (Allow migration between LANforge radios) "use-more-debug" => 0x10000, # Verbose-Debug: more info in wpa-supplicant and hostapd logs. "use-11u" => 0x20000, # Enable 802.11u (Interworking) feature. "use-11u-auto" => 0x40000, # Enable 802.11u (I...) Auto-internetworking. Always enabled currently. "use-11u-internet" => 0x80000, # AP Provides access to internet (802.11u I...) "use-11u-x-steps" => 0x100000, # AP requires additional step for access (802.11u I...) "use-11u-emrg-advert" => 0x200000, # AP claims emergency services reachable (802.11u I...) "use-11u-emrg-unauth" => 0x400000, # AP provides Unauthenticated emergency services (802.11u I...) "use-hs20" => 0x800000, # Enable Hotspot 2.0 (HS20) feature. Req WPA-2. "no-dgaf" => 0x1000000, # AP: Disable DGAF (used by HotSpot 2.0). "use-dot1x" => 0x2000000, # Use 802.1x (RADIUS for AP). "use-11r-pmska" => 0x4000000, # Enable PMSKA caching for WPA2 (Rel to 802.11r). "no-ht80" => 0x8000000, # Disable HT80 (for AC chipset NICs only) "use-ibss" => 0x20000000, # Station should be in IBSS mode. "use-osen" => 0x40000000, # Enable OSEN protocol (OSU Server-only Auth) ); our $cx_type = "tcp"; our %cx_types = ( "tcp" => "lf_tcp", "udp" => "lf_udp", "tcp6" => "lf_tcp6", "udp6" => "lf_udp6", ); our %antenna_table = ( 0 => 0, "0" => 0, -1 => 0, '0' => 0, '-1' => 0, 'ALL' => 0, '1' => 1, '1x1' => 1, 'A' => 1, '2' => 4, '2x2' => 4, 'AB' => 4, '3' => 7, '3x3' => 7, 'ABC' => 7, '4' => 8, '4x4' => 8, 'ABCD' => 8, ); our $duration = 30; # seconds to transmit in step 1 our $db_preload = ""; # use for loading before station creation our $db_save = ""; # use for saving a scenario that we just ran our $db_postload = ""; # use for cleanup after running/saving a scenario our $poll_time = 5; # seconds our $traffic_type = "separate"; # separate: download then upload, concurrent: at same time our $default_mac_pat = "xx:xx:xx:*:*:xx"; our $mac_pattern = $::default_mac_pat; ## ----- ----- ----- ----- ----- ----- ----- ----- ----- ----- # ## Usage # ## ----- ----- ----- ----- ----- ----- ----- ----- ----- ----- # our %wifi_modes = ( "a" => "1", "b" => "2", "g" => "3", "abg" => "4", "abgn" => "5", "bgn" => "6", "bg" => "7", "abgnAC" => "8", "anAC" => "9", "an" => "10", ); our $wifi_mode =""; our $bssid = ""; my $mode_list = join("|", sort keys %wifi_modes); my $usage = qq($0 [--mgr {host-name | IP}] [--mgr_port {ip port}] # use if on non-default management port [--resource {resource}] # use if multiple lanforge systems; defaults to 1 [--quiet { yes | no }] # debug output; -q [--log_cli] # enables CLI command printing to STDOUT # same effect when setting env var LOG_CLI=STDOUT ## AP selection [--radio {name}] # e.g. wiphy2 [--channel {channel}] # e.g. 52, 161, 153 # please check the LANforge GUI to verify resulting selection # center channels might be selected differently than you intended [--antenna {1,2,3,4}] # select number of antennas [--ssid {ssid}] # e.g. jedtest [--bssid {aa:bb:cc:00:11:22, or DEFAULT} # AP BSSID to connect to [--security {open|wep|wpa|wpa2}] # station authentication type, Default is open [--xsec {comma,separated,list} ] # dot1x, 11u, other features, read script [--passphrase {...}] # implies wpa2 if --security not set [--wifi_mode {$mode_list}] ## station configuration [--num_stations {$num_stations}] # Defaults to 1 [--first_sta {$first_sta}] [--first_ip {DHCP |ip address}] [--netmask {$netmask}] [--change_mac {0|1}] # If this is set to 0, then we will not change MAC if the station already exists. # This is now the default behaviour. [--mac-pattern {$default_mac_pat}] # xx : uses parent radio octet # [0-9a-f] : use this value for octet # * : generates random octet # Use quotes around this argument! EG: # --mac_pattern '00:xx:*:*:xx:xx' ## connection configuration [--cxtype {tcp/tcp6/udp/udp6}] # use a tcp/udp connection, default tcp [--upstream {name|$upstream_port}] # could be AP or could be port on LANforge # connected to WAN side of AP [--bps-min {$min_tx}] # minimum tx bps [--bps-max {SAME|bps-value}] # maximum tx bps, use SAME or omit for SAME [--duration {$duration}] # connection duration, seconds, default 60 [--poll-time {$poll_time}] # nap time between connection displays [--action {step1,step2,add,del,del_all_phy}] # step1: creates stations and L3 connections # step2: does bringup test # add: creates station on specified radio, or radio if no stations are requested # del: Delete the specified port. # del_all_phy: Delete all interfaces with the specified parent device. [--traffic_type {separate|concurrent}] # for step1: separate does download then upload # concurrent does upload and download at same time [--admin_down_on_add] # when creating stations, create them admin-down [--db_preload {scenario name}] # load this database before creating stations # option intended as a cleanup step [--db_save {name}] # save the state of this test scenario after running the # connections, before --db_postload [--db_postload {scenario name}] # load this database after running connections, # option intended as a cleanup step ## virtual radio configuration [--vrad_chan {channel}] [--port_del {name}] # deletes port given Examples: ## connecting to an open AP, at 2Mbps, for 20 minutes $0 --action step1 --radio wiphy0 --ssid ap-test-01 \\ --bps-min 2000000 --duration 1200 --upstream eth1 $0 --action step2 --radio wiphy2 --ssid jedtest \\ --first_sta sta100 --first_ip DHCP --num_stations 3 \\ --security wpa2 --passphrase jedtest1 --mac_pattern 'xx:xx:xx:*:*:*' ## using a second lanforge system to connect to wpa2 AP: $0 --mgr 192.168.100.1 --resource 2 --radio wiphy2 \\ --ssid jedtest --passphrase 'asdf1234' \\ --num_stations 10 --first_sta sta400 \\ --first_ip DHCP --upstream eth1 --action step1 ## (Windows) using a beginning database and saving the resulting database: C:\\Users\\bob> cd "c:\\Program Files (x86)\\LANforge-Server\\scripts" C:\\Program Files (x86)\\LANforge-Server\\scripts>perl lf_associate_ap.pl --mgr jedtest \\ --resource 2 --radio wiphy2 --first_ip DHCP \\ --duration 10 --bps-min 10k --bps-max 20M --cxtype tcp \\ --ssid jedtest --passphrase jedtest1 --security wpa2 \\ --first_sta 300 --db_preload Radio2 --db_save run_results --num_stations 3 ## connecting to wpa AP: $0 --mgr 192.168.100.1 --radio wiphy0 \\ --ssid jedtest --passphrase 'asdf1234' --security wep \\ --num_stations 10 --first_sta sta400 \\ --first_ip DHCP --upstream eth1 --action step1 ## creating and deleting a virtual radio: $0 --mgr 192.168.100.1 --resource 2 \\ --radio vphy1 --vrad_chan 36 --action add $0 --mgr 192.168.100.1 --resource 2 \\ --port_del vrad1 --action del ## Adding a station to a new or existing virtual radio: $0 --mgr 192.168.100.1 --resource 2 \\ --radio vphy1 --first_sta sta0 --first_ip DHCP --ssid my_ssid --action add ## Add lots of stations to a radio $0 --mgr ben-ota-1 --resource 2 --action add --radio wiphy0 --ssid Lede-ventana \\ --first_sta sta100 --first_ip DHCP --num_stations 63 ## Delete all virtual devices on wiphy0 $0 --mgr ben-ota-1 --resource 2 --action del_all_phy --port_del wiphy0 ); my $shelf_num = 1; # Default values for cmd-line args. our $report_timer = 1000; # milliseconds our $test_mgr = "default_tm"; # name of test manager our $resource = 1; # might be referred to as card_id our $resource2 = 1; # might be referred to as card_id our $begin_ip = $default_ip_addr; # sta_names is a set of names and static IP addresses to assign them. # As many stations as are in the set will be created. If you want to use # DHCP, replace the ip with "DHCP". # example # %sta_names = ( # "sta1" => "192.168.0.1", # "sta2" => "NEXT" # "sta2" => "DHCP" #); our %sta_names = (); our %cx_names = (); our $quiet = "yes"; # debugging our $action = "step1"; # default action our $lfmgr_host = "localhost"; # LANforge manager IP # Virtual radio defaults. our $vrad_chan = -1; # default channel (AUTO) # ----- ----- ----- ----- ----- ----- ----- ----- ----- ----- # Nothing to configure below here, most likely. # ----- ----- ----- ----- ----- ----- ----- ----- ----- ----- my $lfmgr_port = 4001; # LANforge manager port our $quiesce_sec = 3; # pretty standard =pod this fmt_cmd subroutine is now disabled, please use utils->fmt_cmd =cut #sub fmt_cmd { # my $rv; # if ($::utils->can('fmt_cmd')) { # #print "fmt_cmd passing down to Utils::fmt_cmd()\n"; # $rv = $::utils->fmt_cmd(@_); # return $rv; # } # # for my $hunk (@_) { # die("fmt_cmd called with empty space or null argument, bye.") unless(defined $hunk && $hunk ne ''); # die("rv[${rv}]\n --> fmt_cmd passed an array, bye.") if(ref($hunk) eq 'ARRAY'); # die("rv[${rv}]\n --> fmt_cmd passed a hash, bye.") if(ref($hunk) eq 'HASH'); # $hunk = "0" if($hunk eq "0" || $hunk eq "+0"); # # if( $hunk eq "" ) { # #print "hunk[".$hunk."] --> "; # $hunk = 'NA'; # #print "hunk[".$hunk."]\n"; # #print "fmt_cmd: warning: hunk was blank, now NA. Prev hunks: $rv\n" # } # $rv .= ( $hunk =~m/ +/) ? "'$hunk' " : "$hunk "; # } # chomp $rv; # print "cmd formatted to: $rv\n" unless($::utils->isQuiet()); # return $rv; #} # deprecated, please use utils->doCmd() #sub doCmd { # my $cmd = shift; # die("doCmd: Blank command, bye.") unless ($cmd); # die("doCmd: Telnet uninitialized, check that '\$t' is set. Bye." ) unless ($main::t); # # if ($::utils->can('doCmd')) { # #print "doCmd passing down to Utils::doCmd($cmd)\n"; # $::utils->doCmd($cmd); # } # else { # $main::t->print($cmd); # my @rslt = $::t->waitfor('/ \>\>RSLT:(.*)/'); # print "**************\n @rslt ................\n\n" unless($::utils->isQuiet()); # } #} sub db_exists { my $db_name = shift; die ("::db_exists: called with blank database name. Did you mean EMPTY?") if ($db_name eq ""); print "Looking for database $db_name ..."; my @db_names = split("\n", $::utils->doAsyncCmd("show_dbs")); my @match = grep { /^$db_name\/$/ } @db_names; return 1 if (@match > 0); print "Warning! Scenario $db_name not found among: ".join(", ", @db_names)."\n"; return 0; } sub load_db { my $db_name = shift; die ("::load_db: called with blank database name. Did you mean EMPTY?") if ($db_name eq ""); print "Loading database $db_name ..."; $::utils->doCmd(fmt_cmd("load", $db_name, "overwrite")); for (my $i = 20 ; $i>0; $i--) { sleep(1); my $up = 0; my $has_tx_bytes = 0; my $sta_cnt = 0; my $prev_cnt = 0; my $status = $::utils->doAsyncCmd(fmt_cmd("nc_show_ports", 1, $::resource, "ALL")); my @status = split("\n", $status); foreach (@status){ if (/^Shelf: 1, Card: \d+\s+Port: \d+\s+Type: STA\s+/) { $sta_cnt++; print "sta_cnt $sta_cnt up $up has_tx %has_tx_bytes\n"; } if ($sta_cnt > $prev_cnt) { if ( /IP: \d+\.\d+\.\d+\.\d+ / && !/IP: 0\.0\.0\.0 /) { $up++; } if ( /Txb: \d+ / && !/Txb: 0 / ) { $has_tx_bytes ++; } $prev_cnt = $sta_cnt if ( /^\s*$/ ); } } # ~foreach } print "done\n"; } sub save_db { my $db_name = shift; die ("::save_db: called with blank database name. Please debug.") if ($db_name eq ""); print "Saving database $db_name ..."; if (db_exists($db_name)==1) { print "Warning: will over-write database $db_name! "; } $::utils->doCmd($::utils->fmt_cmd("save", $db_name)); print "done\n"; } sub get_radio_bssid { my $radio_name = shift; die ("::get_radio_bssid: blank radio name. Please debug.") if ($radio_name eq ""); return $::wiphy_bssids{ $radio_name } if (exists($::wiphy_bssids{ $radio_name })); #print "* looking up $radio_name for bssid..."; my @status_lines = split("\n", $::utils->doAsyncCmd($::utils->fmt_cmd("show_port", 1, $::resource, $radio_name))); my @mac_lines = grep { / MAC: [^ ]+/ } @status_lines; die ("::get_radio_bssid: failed to find radio bssid, no MAC lines") if (@mac_lines < 1); my ($parent_bssid) = $mac_lines[0] =~ / MAC: ([^ ]+)/; die ("::get_radio_bssid: failed to find radio bssid, MAC was empty") if ($parent_bssid eq ""); $::wiphy_bssids{ $radio_name } = $parent_bssid; #print $parent_bssid."\n"; return $parent_bssid; } sub new_mac_from_pattern { my $parent_mac = shift; my $pattern = shift; die ("::new_mac_pattern: blank parent_mac. Please debug.") if ($parent_mac eq ""); die ("::new_mac_pattern: blank pattern. Please debug.") if ($pattern eq ""); my @parent_hunks = split(":", $parent_mac); my @pattern_hunks = split(":", $pattern); die ("::new_mac_pattern: parent_mac needs to be colon-separated. Please debug.") if (@parent_hunks != 6); die ("::new_mac_pattern: pattern needs to be colon-separated. Please debug.") if (@pattern_hunks != 6); my @new_hunks = (); for (my $i=0; $i < 6; $i++) { if ($pattern_hunks[$i] =~ /xx/i) { $new_hunks[ $i ] = $parent_hunks[ $i ]; } elsif ($pattern_hunks[$i] =~ /[*]+/) { my $r=int(rand(255)); if ($i == 0) { $r |= 0x002; # sets the 'locally administered bit' $r &= 0x0FE; # use if this upstream routers squash local admin bit macs # $r &= 0x0DF; } $new_hunks[ $i ] = sprintf("%02X", $r); } else { $new_hunks[ $i ] = $pattern_hunks[ $i ]; } } return lc(join(":", @new_hunks)); } # ~new_mac_pattern sub new_random_mac { my $rv = "00:"; for (my $i=0; $i<5; $i++) { $rv.=sprintf("%02X",int(rand(255))).(($i<4)?':':''); } return $rv; } sub fmt_vsta_cmd { my ($resource, $sta_wiphy, $sta_name, $flags, $ssid, $passphrase, $mac, $flags_mask, $wifi_m, $bssid ) = @_; die("fmt_vsta_cmd wants sta_wiphy name, bye.") unless($sta_wiphy); my $key = "[BLANK]"; my $ap = "DEFAULT"; if ((defined $bssid) && ($bssid ne "")) { $ap = $bssid; } my $cfg_file = "NA"; my $mode = 8; # default to a/b/g/n/AC my $rate = "NA"; my $amsdu = "NA"; my $ampdu_factor = "NA"; my $ampdu_density = "NA"; my $sta_br_id = "NA"; $key = $passphrase if ($passphrase ne ""); if ($wifi_m ne "") { if (exists $::wifi_modes{$wifi_m}) { $mode = $::wifi_modes{$wifi_m}; } else { print "Wifi Mode [$wifi_m] not recognised. Please use:\n"; print join(", ", sort keys %::wifi_modes); exit 1; } } $flags = "+0" if ($flags == 0); # perl goes funny on zeros $flags_mask = "+0" if ($flags_mask == 0); $flags = "NA" if ($flags eq ""); return $::utils->fmt_cmd("add_sta", 1, $resource, $sta_wiphy, $sta_name, "$flags", "$ssid", "NA", "$key", $ap, $cfg_file, $mac, $mode, $rate, $amsdu, $ampdu_factor, $ampdu_density, $sta_br_id, "$flags_mask" ); } sub fmt_vrad_cmd { my ($resource, $sta_wiphy, $vrad_chan ) = @_; die("fmt_vrad_cmd requires sta_wiphy.") unless($sta_wiphy); my $mode = "NA"; my $country = "NA"; my $frequency = "NA"; my $frag_thresh = "NA"; my $rate = "NA"; my $rts = "NA"; my $txpower = "NA"; my $mac = "NA"; my $antenna = "NA"; my $flags = "0x1"; my $flags_mask = "NA"; return $::utils->fmt_cmd("set_wifi_radio", 1, $resource, $sta_wiphy, $mode, $vrad_chan, $country, $frequency, $frag_thresh, $rate, $rts, $txpower, $mac, "$antenna", "$flags", "$flags_mask" ); } sub createEpPair { my $sta_name = shift; die("createEpPair: please pass station name, bye") unless(defined $sta_name && $sta_name ne ''); die("createEpPair: please define upstream_port, bye") unless(defined $::upstream_port && $::upstream_port ne ''); my $port_a = $sta_name; my $port_b = $::upstream_port; my $cx_name = $::cx_names{$sta_name}->{"cx"}; my $ep1 = $::cx_names{$sta_name}->{"ep1"}; my $ep2 = $::cx_names{$sta_name}->{"ep2"}; my %min_pkt_szs = ( 'tcp' => [ 1460, 1460 ], 'tcp6' => [ 1460, 1460 ], 'udp' => [ 1472, 1472 ], 'udp6' => [ 1472, 1472 ] ); my %max_pkt_szs = ( 'tcp' => [ 1460, 1460 ], 'tcp6' => [ 1460, 1460 ], 'udp' => [ 1472, 1472 ], 'udp6' => [ 1472, 1472 ] ); $::cx_type = "tcp" if ($::cx_type eq ""); print "\n cxtype [$::cx_type]\n" unless($::utils->isQuiet()); if ( ! exists $::cx_types{$::cx_type} ) { die( "Please choose connection type: ".join(", ", keys(%::cx_types))); } my $cxtype = $::cx_types{$::cx_type}; my $rate_min = "+0"; # we will set these later my $rate_max = "+0"; # using set_endp_tx_bounds die("createEpPair: wants cx_name, bye.") unless(defined $cx_name && $cx_name ne ''); die("createEpPair: wants ep1 name, bye.") unless(defined $ep1 && $ep1 ne ''); die("createEpPair: wants ep2 name, bye.") unless(defined $ep2 && $ep2 ne ''); my $cmd = $::utils->fmt_cmd("add_endp", $ep1, 1, $::resource, $port_a, $cxtype, -1, "NA", "$rate_min", "$rate_max", "NA", $min_pkt_szs{$::cx_type}[0], @{$max_pkt_szs{$::cx_type}}[0], "increasing", "NO", "NA", "NA", "NA"); print "EP1: $cmd\n" unless($::utils->isQuiet()); $::utils->doCmd($cmd); $cmd = $::utils->fmt_cmd("add_endp", $ep2, 1, $::resource2, $port_b, $cxtype, -1, "NA", "$rate_min", "$rate_max", "NA", $min_pkt_szs{$::cx_type}[1], @{$max_pkt_szs{$::cx_type}}[1], "increasing", "NO", "NA", "NA", "NA"); print "EP2: $cmd\n" unless($::utils->isQuiet()); $::utils->doCmd($cmd); # Now, add the cross-connect $::utils->doCmd($::utils->fmt_cmd("add_cx", $cx_name, $::test_mgr, $ep1, $ep2)); $::utils->doCmd($::utils->fmt_cmd("set_cx_report_timer", $::test_mgr, $cx_name, $::report_timer)); } sub fmt_port_cmd { my($resource, $port_id, $ip_addr, $mac_addr) = @_; my $use_dhcp = ($ip_addr eq "DHCP") ? 1 : 0; my $ip = ($use_dhcp) ? "0.0.0.0" : $ip_addr ; $mac_addr = die("fmt_port_cmd requires mac_addr") if(!$mac_addr); # || $mac_addr eq "NA"); #print "fmt_port_cmd: RES $resource PORT $port_id IP_A $ip_addr MAC $mac_addr -> $ip\n" unless($::quiet eq "yes"); my $cmd_flags = 'NA'; #0; my $cur_flags = 0; $cur_flags |= 0x80000000 if($use_dhcp); #print "fmt_port_cmd: DHCP($use_dhcp) $cur_flags\n" unless($::quiet eq "yes"); my $ist_flags = 0; $ist_flags |= 0x2; # check current flags $ist_flags |= 0x4 if($ip ne "NA"); $ist_flags |= 0x8 if($::netmask ne "NA"); $ist_flags |= 0x20 if($mac_addr ne "NA"); $ist_flags |= 0x4000; # Always interested in DHCP, we either set it to DHCP or IP $ist_flags |= 0x800000; # port up my $gateway = "0.0.0.0"; my $dns_servers = "NA"; my $dhcp_client_id = "NONE"; my $flags2 = "NA"; # Ben suggests using $sta_name before using $port_id $cur_flags = "+0" if(!$cur_flags); $cmd_flags = "+0" if(!$cmd_flags); $ist_flags = "+0" if(!$ist_flags); my $cmd = $::utils->fmt_cmd("set_port", 1, $::resource, $port_id, $ip, $::netmask, $gateway, "$cmd_flags", "$cur_flags", "$mac_addr", "NA", "NA", "NA", "$ist_flags", $::report_timer, "$flags2", "NA","NA","NA","NA","NA","NA","NA","NA","NA","NA","NA","NA","NA", $dns_servers, "NA", $dhcp_client_id); print("fmt_port_cmd: ".$cmd) unless($::utils->isQuiet()); return $cmd; } sub fmt_port_down { my($resource, $port_id, $ip_addr, $ip_mask) = @_; die("fmt_port_down wants resource id, bye.") unless($resource); die("fmt_port_down wants port_id id, bye.") unless($port_id); die("fmt_port_down wants ip_addr id, bye.") unless($ip_addr); die("fmt_port_down wants ip_mask id, bye.") unless($ip_mask); my $use_dhcp = ($ip_addr eq "DHCP") ? 1 : 0; my $ip = ($use_dhcp) ? "0.0.0.0" : $ip_addr ; my $cmd_flags = "NA"; my $cur_flags = 0; $cur_flags |= 0x1; # port down my $ist_flags = 0; $ist_flags |= 0x2; # check current flags $ist_flags |= 0x800000; # port down my $dhcp_id = "NONE"; my $netmask = "$ip_mask"; my $gateway = "0.0.0.0"; my $dns_servers = "NA"; my $dhcp_client_id = "NONE"; my $flags2 = "NA"; $cmd_flags = "+0" if(!$cmd_flags); # zeros are falsy in perl $cur_flags = "+0" if(!$cur_flags); $ist_flags = "+0" if(!$ist_flags); my $cmd = $::utils->fmt_cmd("set_port", 1, $resource, $port_id, $ip_addr, $netmask, $gateway, "$cmd_flags", "$cur_flags", "NA", "NA", "NA", "NA", "$ist_flags", $::report_timer, "$flags2", "NA","NA","NA","NA","NA","NA","NA","NA","NA","NA","NA","NA","NA", $dns_servers, "NA", $dhcp_client_id); return $cmd; } # ----- ----- ----- ----- ----- ----- ----- ----- ----- ----- ----- ----- ----- ----- ----# # WiFi FLAGS # # and please see the CLI users guide (flags can get updated) # # http://www.candelatech.com/lfcli_ug.php # # # # 0x10 Enable WPA # # 0x20 Use Custom wpa_supplicant config file. # # 0x100 Use wpa_supplicant configured for WEP encryption. # # 0x200 Use wpa_supplicant configured for WPA2 encryption. # # 0x400 Disable HT-40 even if hardware and AP support it. # # 0x800 Enable SCAN-SSID flag in wpa_supplicant. # # 0x1000 Enable PCSC (used by WPA-SIM) # # 0x2000 Disable SGI (Short Guard Interval). # # 0x4000 OK-To-Migrate (Allow migration between LANforge radios) # # 0x8000 Verbose-Debug: Increase debug info in wpa-supplicant and hostapd logs. # # 0x10000 Enable 802.11u (Interworking) feature. # # 0x20000 Enable 802.11u (Interworking) Auto-internetworking feature. # # 0x40000 AP Provides access to internet (802.11u Interworking) # # 0x80000 AP requires additional step for access (802.11u Interworking) # # 0x100000 AP claims emergency services reachable (802.11u Interworking) # # 0x200000 AP provides Unauthenticated emergency services (802.11u Interworking) # # 0x400000 Enable Hotspot 2.0 (HS20) feature. Requires WPA-2. # # 0x800000 AP: Disable DGAF (used by HotSpot 2.0). # # 0x1000000 Use 802.1x (RADIUS for AP). # # 0x2000000 Enable oportunistic PMSKA caching for WPA2 (Related to 802.11r). # # # # ----- ----- ----- ----- ----- ----- ----- ----- ----- ----- ----- ----- ----- ----- ----# sub new_wifi_station { my $sta_name = shift; die("new_wifi_station wants station name, bye") unless(defined $sta_name && $sta_name ne ''); my $ip_addr = shift; die("new_wifi_station wants ip_address, bye") unless(defined $ip_addr && $ip_addr ne ''); my $rh_results = shift; die("new_wifi_station wants hash ref to place results, bye.") unless(defined $rh_results); my $wifi_m = shift; my $sleep_amt = shift; my $mac_addr = ""; print "## new-wifi-station, sta-name: $sta_name change-mac: $change_mac" unless($::utils->isQuiet()); if (! $::change_mac) { my $status = $::utils->doAsyncCmd($::utils->fmt_cmd("show_port", 1, $::resource, $sta_name)); if ($status =~ /MAC:\s+(\S+)\s+/) { $mac_addr = $1; } } if ($mac_addr eq "") { # Couldn't find it, or we want to change the mac print "## calculating new mac-addr.." unless($::utils->isQuiet()); my $parent_mac = get_radio_bssid($::sta_wiphy); die("new_wifi_station: unable to find bssid of parent radio") if ($parent_mac eq ""); $mac_addr = new_mac_from_pattern($parent_mac, $::mac_pattern); } print "## $sta_name $mac_addr; " unless($::utils->isQuiet()); my $flags = +0; # set this to string later my $flagsmask = +0; # set this to string later # To set zero value set the bit in flags to zero. # Set the flagsmask value to 1 if you want the value to be set to 1 or 0. # NOTE: This script is used to change things, not just create them, so it is not # always wrong to not set passphrase since it could be set already. if ($::passphrase eq "") { $::passphrase = "NA"; #if($::security ne "open") { # die("Passphrase not set when --security [$::security] chosen. Please set passphrase."); #} } if ( ! exists($::sec_options{$::security})) { die( "Unknown security option [{$::security}]"); } $flags |= $::sec_options{$::security}; # This doesn't work since 'open' maps to 0x0 and we need to also disable # flags that might be set previously. # $flagsmask |= $::sec_options{$::security}; # We are always configuring security to one thing or another, so we need to # mask all of the bits properly. $flagsmask |= (0x10 | 0x200 | 0x400); if (defined $::xsec && "$::xsec" ne "") { for my $sec_op (split(',', $::xsec)) { next if (!defined $::sec_options{$sec_op}); $flags |= $::sec_options{$sec_op}; $flagsmask |= $::sec_options{$sec_op}; } } $flags = "+0" if ( $flags == 0); $flagsmask = "+0" if ( $flagsmask == 0); # perform the station create first, then assign IP as necessary my $sta1_cmd = fmt_vsta_cmd($::resource, $::sta_wiphy, $sta_name, "$flags", "$::ssid", "$::passphrase", $mac_addr, "$flagsmask", $wifi_m, $::bssid); $::utils->doCmd($sta1_cmd); $sta1_cmd = fmt_port_cmd($resource, $sta_name, $ip_addr, $mac_addr); $::utils->doCmd($sta1_cmd); if ($::admin_down_on_add) { my $cur_flags = 0x1; # port down my $ist_flags = 0x800000; # port down $sta1_cmd = $::utils->fmt_cmd("set_port", 1, $resource, $sta_name, "NA", "NA", "NA", "NA", "$cur_flags", "NA", "NA", "NA", "NA", "$ist_flags"); $::utils->doCmd($sta1_cmd); } if ($sleep_amt > 0) { sleep $sleep_amt; } my $data = [ $mac_addr, $sta_name, $sta1_cmd ]; $rh_results->{$sta_name} = $data; } sub new_wifi_radio { my $cmd = fmt_vrad_cmd($::resource, $::sta_wiphy, $::vrad_chan ); $::utils->doCmd($cmd); } sub delete_port { if (defined $::port_del) { print "deleting port $::port_del\n" unless($::utils->isQuiet()); $::utils->doCmd($::utils->fmt_cmd("rm_vlan", 1, $::resource, $::port_del)); } } sub get_sta_state { my($rs_status) = @_; die("is_assoc_state: wants ref to status string") unless($rs_status); my @lines = split(/\n/, $$rs_status); my $careful = 0; my $name = "unknown"; my $ip = "0.0.0.0"; my $assoc = "unknown"; my $first; my $freq; my @hunks; my $mac; my $gw; my $mask; my $channel; my $mode; for my $line (@lines) { $first = "_"; $line =~ m/^\s+(\S+?:)\s+/; #print "}}}} $line\n"; if ($1 && $1 eq "MAC:" ) { @hunks = split(/: /, $line); $mac = (split(/ /, $hunks[1]))[0]; $name = (split(/ /, $hunks[2]))[0]; next; } if ($1 && $1 eq "IP:") { @hunks = split(/: /, $line); $ip = (split(/ /, $hunks[1]))[0]; $mask = (split(/ /, $hunks[2]))[0]; $gw = (split(/ /, $hunks[3]))[0]; next; } if ($1 && $1 eq "Probed:") { @hunks = split(/: /, $line); $careful = 1; $mode = (split(/ /, $hunks[2]))[0]; next; } if( $careful && $1 eq "Channel:" ) { @hunks = split(/: /, $line); $channel = (split(/ /, $hunks[1]))[0]; $freq = (split(/ /, $hunks[3]))[0]; $assoc = (split(/ /, $hunks[4]))[0]; #print " assoc:".$assoc; last; } } my %rv = ( "assoc" => $assoc, "freq" => $freq, "ip" => $ip, "mask" => $mask, "gw" => $gw, "mac" => $mac, "mode" => $mode, "name" => $name ); return %rv; } sub awaitStationRemoval { my $old_sta_count = (keys %::sta_names); print "Waiting for $old_sta_count stations to be removed..."; while( $old_sta_count > 0 ) { $old_sta_count = (keys %::sta_names); for my $sta_name (sort(keys %::sta_names)) { print " $sta_name,"; my $status = $::utils->doAsyncCmd($::utils->fmt_cmd("show_port", 1, $::resource, $sta_name)); $old_sta_count-- if( $status =~ m/Could not find/); } if ($old_sta_count > 0) { #print "$old_sta_count..."; sleep 1; } } print " Old stations removed\n"; } #~expand to multiple cross-connects sub removeOldCrossConnects { print "Removing old cross-connects, and endpoints ...\n"; for my $sta_name (sort(keys(%::sta_names))) { my $cx_name = $::cx_names{$sta_name}->{"cx"}; my $ep1 = $::cx_names{$sta_name}->{"ep1"}; my $ep2 = $::cx_names{$sta_name}->{"ep2"}; $::utils->doCmd("rm_cx $::test_mgr $cx_name"); $::utils->doCmd("rm_endp $ep1"); $::utils->doCmd("rm_endp $ep2"); print " $cx_name ($ep1 - $ep2)..."; } print " done.\n"; } sub removeOldStations { print "Deleting ports:"; foreach my $sta_name (reverse sort(keys %::sta_names)) { my $status = $::utils->doAsyncCmd($::utils->fmt_cmd("show_port", 1, $::resource, $sta_name)); if ($status =~ /Type:/) { # It exists, remove it # print "...$sta_name "; $::utils->doCmd($::utils->fmt_cmd("rm_vlan", 1, $::resource, $sta_name)); } } print " done.\n"; } sub awaitNewStations { print "Waiting for stations to associate..."; my $new_sta_count = keys(%::sta_names); my $found_stations = 0; while( $new_sta_count > $found_stations ) { $found_stations = 0; my @are_assoc = (); my @not_assoc = (); for my $sta_name (sort(keys(%::sta_names))) { my $status = $::utils->doAsyncCmd($::utils->fmt_cmd("show_port", 1, $::resource, $sta_name)); my %sta_status = get_sta_state(\$status); #print " $sta_name ".$sta_status{"assoc"}; if( $sta_status{"assoc"} ne "Not-Associated") { push(@are_assoc, $sta_name); } else { push(@not_assoc, $sta_name); } } #~foreach sta $found_stations = @are_assoc; print " $found_stations/$new_sta_count seen to associate\n"; if ( $found_stations != $new_sta_count ){ print " Associated:".join(", ", @are_assoc)."\n"; print " Pending :".join(", ", @not_assoc)."\n"; } } # ~while } sub endpointReport { my $ep = shift; my ($ep_name, $tx_rate, $rx_rate,$rx_bps); die("endpointReport: should be passed name of endpoint, bye.") unless ( $ep ne '' ); my $blob = $::utils->doAsyncCmd($::utils->fmt_cmd("nc_show_endpoints", "$ep"), "\n"); #print "BLOB: $blob\n\n\n"; ( $ep_name ) = ($blob =~ m/^Endpoint \[(.*?)\] /mg); ( $tx_rate ) = ($blob =~ m/(Tx Bytes: .*$)/mg); ( $rx_rate ) = ($blob =~ m/(Rx Bytes: .*$)/mg); print "$ep_name:\t$tx_rate\n\t\t$rx_rate\n"; } sub showEndpoints { for my $sta_name (sort(keys(%::sta_names))) { my $ep1 = $::cx_names{$sta_name}->{ep1}; my $ep2 = $::cx_names{$sta_name}->{ep2}; endpointReport($ep1); endpointReport($ep2); } } sub createEndpointPairs { print "\nCreating connections: "; for my $sta_name (sort(keys(%::sta_names))) { my $cx = $::cx_names{$sta_name}->{"cx"}; print " $cx ($sta_name - $::upstream_port), "; createEpPair($sta_name); } print "done.\n"; } sub evalUnits { my $val = shift; if ($val =~ /^\d+$/) { return +0+$val; } my $pow = 1; if ($val =~ /(\d+)(\w+)/) { my $pref = $1; my $suff = $2; if ($suff =~ /[Kk][Bbps]*$/) { $pow = 1000; } elsif ($suff =~ /[Mm][Bbps]*$/) { $pow = 1000000; } elsif ($suff =~ /[Gg][Bbps]*$/) { $pow = 1000000000; } if ($pref == 0 || $pow == 0) { print "Warning: speed coeficients [$pref,$pow] appear suspicious\n"; } my $speed =0 + ($pref * $pow); #print ">>>> setting speed to $speed <<<<\n"; return $speed; } print "Warning: speed[$val] appears suspicious\n"; return $val; } sub adjustForSimultaneous { my $no_rate = "+0"; if (lc($::min_tx) eq "same" ) { die "--min_tx may not be 'same', please provide a number or formatted unit in K, M or G"; } my $rate_min = evalUnits($::min_tx); #print "rate_min now: $rate_min\n"; $::max_tx = $::min_tx if (lc($::max_tx) eq "same" ) ; my $rate_max = evalUnits($::max_tx); #print "rate_max now: $rate_max\n"; print "Adjusting cx min/max tx for concurrent test: "; for my $sta_name (sort(keys(%::sta_names))) { my $cx = $::cx_names{$sta_name}->{"cx"}; my $ep1 = $::cx_names{$sta_name}->{"ep1"}; my $ep2 = $::cx_names{$sta_name}->{"ep2"}; #print "UPLOAD: ".$::utils->fmt_cmd("set_endp_tx_bounds", $ep1, "$rate_min", "$rate_max")."\n"; #print "UPLOAD: ".$::utils->fmt_cmd("set_endp_tx_bounds", $ep2, "$no_rate", "$no_rate")."\n"; $::utils->doCmd($::utils->fmt_cmd("set_endp_tx_bounds", $ep1, "$rate_min", "$rate_max")); $::utils->doCmd($::utils->fmt_cmd("set_endp_tx_bounds", $ep2, "$rate_min", "$rate_max")); $::utils->doCmd($::utils->fmt_cmd("set_endp_quiesce", $ep1, "$::quiesce_sec")); $::utils->doCmd($::utils->fmt_cmd("set_endp_quiesce", $ep2, "$::quiesce_sec")); } for my $sta_name (sort(keys(%::sta_names))) { my $cx = $::cx_names{$sta_name}->{"cx"}; $::utils->doCmd($::utils->fmt_cmd("set_cx_state", $::test_mgr, $cx, "RUNNING")); print " $cx..."; } print "done.\n"; } # ~adjustForDuplex # adjust the transmit rate up for endpoint 1, and down for endpoint 2 sub adjustForUpload { my $no_rate = "+0"; if (lc($::min_tx) eq "same" ) { die "--min_tx may not be 'same', please provide a number or formatted unit in K, M or G"; } my $rate_min = evalUnits($::min_tx); $::max_tx = $::min_tx if (lc($::max_tx) eq "same" ) ; my $rate_max = evalUnits($::max_tx); print "Adjusting cx min/max tx for upload test: "; for my $sta_name (sort(keys(%::sta_names))) { my $cx = $::cx_names{$sta_name}->{"cx"}; my $ep1 = $::cx_names{$sta_name}->{"ep1"}; my $ep2 = $::cx_names{$sta_name}->{"ep2"}; #print "UPLOAD: ".fmt_cmd("set_endp_tx_bounds", $ep1, "$rate_min", "$rate_max")."\n"; #print "UPLOAD: ".fmt_cmd("set_endp_tx_bounds", $ep2, "$no_rate", "$no_rate")."\n"; $::utils->doCmd($::utils->fmt_cmd("set_endp_tx_bounds", $ep1, "$rate_min", "$rate_max")); $::utils->doCmd($::utils->fmt_cmd("set_endp_tx_bounds", $ep2, "$no_rate", "$no_rate")); $::utils->doCmd($::utils->fmt_cmd("set_endp_quiesce", $ep1, "$::quiesce_sec")); $::utils->doCmd($::utils->fmt_cmd("set_endp_quiesce", $ep2, "$::quiesce_sec")); } for my $sta_name (sort(keys(%::sta_names))) { my $cx = $::cx_names{$sta_name}->{"cx"}; $::utils->doCmd($::utils->fmt_cmd("set_cx_state", $::test_mgr, $cx, "RUNNING")); print " $cx..."; } print "done.\n"; } # ~adjustForUpload sub printShowEndpointStats { my $lines = shift; for my $line (split(/\n/, $lines)) { if ($line =~ m/RealRxRate:/) { my ($bps_rx) = ($line =~ m/RealRxRate: (\d+)bps /); if ($bps_rx >=1000000) { $bps_rx = ceil($bps_rx / 1000000)."M"; } elsif ($bps_rx >= 1000) { $bps_rx = ceil($bps_rx / 1000)."K"; } print " ${bps_rx}bps"; } if ($line =~ m/Tx Bytes:/) { my ($tx) = ($line =~ m/Total:\s+(\d+)/); if($tx >=(1024*1024)) { $tx = ceil($tx / (1024*1024))."M"; } elsif ($tx >= 1024) { $tx = ceil($tx / 1024)."K"; } print " / ${tx}B\t"; } } } sub awaitTransfers { my $begin_time = time; my $end_time = $begin_time + $::duration; my $lines; #my $print_nap = 3; my $passes = 0; for my $sta_name (sort(keys(%::sta_names))) { my $ep1 = $::cx_names{$sta_name}->{"ep1"}; my $ep2 = $::cx_names{$sta_name}->{"ep2"}; print " $ep1 Rx-bps/Tx-B \t$ep2 Rx-bps/Tx-B |" } print "\n"; while( time < $end_time ) { sleep $::poll_time; #if($passes == 0) { for my $sta_name (sort(keys(%::sta_names))) { my $cx = $::cx_names{$sta_name}->{"cx"}; my $ep1 = $::cx_names{$sta_name}->{"ep1"}; my $ep2 = $::cx_names{$sta_name}->{"ep2"}; $lines = $::utils->doAsyncCmd($::utils->fmt_cmd("nc_show_endpoints", "$ep1"), "\n"); printShowEndpointStats($lines); $lines = $::utils->doAsyncCmd($::utils->fmt_cmd("nc_show_endpoints", "$ep2"), "\n"); printShowEndpointStats($lines); print " |"; } #$passes = $print_nap + 1; print "\n"; #} #$passes--; } } # ~awaitUploads sub adjustForDownload { my $no_rate = "+0"; if (lc($::min_tx) eq "same" ) { die "--min_tx may not be 'same', please provide a number or formatted unit in K, M or G"; } my $rate_min = evalUnits($::min_tx); $::max_tx = $::min_tx if (lc($::max_tx) eq "same" ) ; my $rate_max = evalUnits($::max_tx); print "Adjusting tx_rate for download..."; for my $sta_name (sort(keys(%::sta_names))) { my $ep1 = $::cx_names{$sta_name}->{"ep1"}; my $ep2 = $::cx_names{$sta_name}->{"ep2"}; #print "Download: ".fmt_cmd("set_endp_tx_bounds", $ep1, "$no_rate", "$no_rate")."\n"; #print "Download: ".fmt_cmd("set_endp_tx_bounds", $ep2, "$rate_min", "$rate_max")."\n"; $::utils->doCmd($::utils->fmt_cmd("set_endp_tx_bounds", $ep1, "$no_rate", "$no_rate")); $::utils->doCmd($::utils->fmt_cmd("set_endp_tx_bounds", $ep2, "$rate_min", "$rate_max")); $::utils->doCmd($::utils->fmt_cmd("set_endp_quiesce", $ep1, "$::quiesce_sec")); $::utils->doCmd($::utils->fmt_cmd("set_endp_quiesce", $ep2, "$::quiesce_sec")); } for my $sta_name (sort(keys(%::sta_names))) { my $cx = $::cx_names{$sta_name}->{"cx"}; $::utils->doCmd($::utils->fmt_cmd("set_cx_state", $::test_mgr, $cx, "RUNNING")); print " $cx..." } print "done\n"; } # ~adjustForUpload sub quiesceConnections { for my $sta_name (sort(keys(%::sta_names))) { my $cx = $::cx_names{$sta_name}->{"cx"}; $::utils->doCmd($::utils->fmt_cmd("set_cx_state", $::test_mgr, $cx, "QUIESCE")); } } sub resetCounters { for my $sta_name (sort(keys(%::sta_names))) { my $cx = $::cx_names{$sta_name}->{"cx"}; $::utils->doCmd("clear_cx_counters $cx"); } $::utils->doCmd("clear_endp_counters all"); } ## ----- ----- ----- ----- ----- ----- ----- ----- ----- ----- ## ## Create a virtual station and associate it with and SSID, ## then pass traffic to and from it. ## ## ----- ----- ----- ----- ----- ----- ----- ----- ----- ----- sub doStep_1 { my $sta_name = (sort(keys %::sta_names))[0]; removeOldCrossConnects(); sleep(2); removeOldStations(); sleep(1); awaitStationRemoval(); sleep 1; my $cmd; my %results1 = (); # make sure that ::num_station and ::sta_names is consistent if ($::num_stations != (keys %::sta_names)) { die "Unexpected difference between number of station names and num_stations, did num_stations not get set?"; } # create stations print " Creating new stations: "; for $sta_name (sort(keys %::sta_names)) { # sta, ip, rh, $ip_addr print " $sta_name "; new_wifi_station( $sta_name, $::sta_names{$sta_name}, \%results1, $::wifi_mode, 0); } print " Created $::num_stations stations\n"; my $new_sta_count = keys %results1; my $found_stations = 0; awaitNewStations(); sleep 1; # we create a pair of connection endpoints and # a cross-connect between them for every station createEndpointPairs(); sleep 5; if ($::traffic_type eq "separate") { adjustForUpload(); print " started uploads.\n"; awaitTransfers(); quiesceConnections(); sleep 1+$::quiesce_sec; # the STOPPED signal might report short on packets because # there might be queued packets in the backlog. If you need # more precise readings, use the QUIESCE command which waits # a specified number of seconds for all connections to close showEndpoints(); resetCounters(); # adjust the transmit rate down for endpoint 1, and up for endpoint 2 adjustForDownload(); print "\nStarted download...\n"; awaitTransfers(); } elsif ($::traffic_type eq "concurrent") { adjustForSimultaneous(); print "\nStarted concurrent traffic...\n"; awaitTransfers(); } else { print "Unkown traffic_type $::traffic_type, exiting.\n"; exit(1); } quiesceConnections(); sleep 1+$::quiesce_sec; showEndpoints(); } # ~step1 ## ----- ----- ----- ----- ----- ----- ----- ----- ----- ----- ## ## Create a series of stations and associate them to ## the SSID. Then disassociate them. ## ## ----- ----- ----- ----- ----- ----- ----- ----- ----- ----- sub doStep_2 { my %results2 = (); # delete previous stations print "Removing old stations..."; for my $sta_name (sort(keys %::sta_names)) { # if we have a port eid for this station, let's delete the port so we can start fresh my $del_cmd = $::utils->fmt_cmd("rm_vlan", 1, $::resource, $sta_name); print "$sta_name " unless($::utils->isQuiet()); $::utils->doCmd($del_cmd); } # poll until they are gone my $old_sta_count = (keys(%::sta_names)); while( $old_sta_count > 0 ) { $old_sta_count = (keys(%::sta_names)); sleep 1; for my $sta_name (sort(keys %::sta_names)) { my $status = $::utils->doAsyncCmd($::utils->fmt_cmd("show_port", 1, $::resource, $sta_name)); #print ">>status>>$status\n"; $old_sta_count-- if( $status =~ /Could not find/); # ?? } } print "Old stations removed.\n"; print "Creating new stations..."; # create new stations for my $sta_name (sort(keys %::sta_names)) { die("misconfiguration! ") if( ref($sta_name) eq "HASH"); my $ip = $::sta_names{$sta_name}; print "$sta_name " unless($::utils->isQuiet()); new_wifi_station( $sta_name, $ip, \%results2, $::wifi_mode, 0); # Uncomment to diagnose connection results. The IPs assigned # are unlikely to appear instantly, but the mac and entity id # used internally by LANforge will be set. #my $ra_data = $results{$sta_name}}; #my $mac = $results{$sta_name}[0]; #my $eid = $results{$sta_name}[1]; #print "created $sta_name, mac $mac, EID: $eid\n"; #print "CMD: ".$results{$sta_name}[2]."\n\n"; } sleep 1; my $num_stations = (keys %::sta_names); print "Created $num_stations stations.\nPolling for association: "; # we can view IP assignment as well as station association my $num_assoc = 0; my $num_ip = 0; my $begin_time = time; my %assoc_state = (); for my $sta_name (sort(keys %::sta_names)) { $assoc_state{$sta_name} = {}; } my $port; #while($num_ip < $num_stations) { # if we just cared about IPs while($num_assoc < $num_stations) { sleep 1; $num_assoc = 0; $num_ip = 0; for my $sta_name (sort(keys %::sta_names)) { my $status = $::utils->doAsyncCmd($::utils->fmt_cmd("show_port", 1, $::resource, $sta_name)); my %state = get_sta_state(\$status); #print $state{"name"}.": ".$state{"assoc"}." "; $num_assoc++ if($state{"assoc"} ne "Not-Associated"); #print $state{"ip"}."/".$state{"mask"}." gw:".$state{"gw"}."\n"; $num_ip++ if($state{"ip"} ne "0.0.0.0" ); } print "$num_assoc stations associated, $num_ip stations with IPs\n"; } my $end_time = time; my $delta = $end_time - $begin_time; print "Association took about $delta seconds\n"; print "Bringing those stations down now: "; for my $sta_name (keys %::sta_names) { my $cmd = fmt_port_down($::resource, $sta_name, "0.0.0.0", "0.0.0.0"); #$::netmask $::utils->doCmd($cmd); print "$sta_name " unless ($::utils->isQuiet()); } print "...stations down. Done.\n" } ## ----- ----- ----- ----- ----- ----- ----- ----- ----- ----- ## ## Create a station ## ## ----- ----- ----- ----- ----- ----- ----- ----- ----- ----- sub doAdd { # create virtual station if ($::num_stations > 0 && defined $::sta_wiphy) { my %results2 = (); for my $sta_name (sort(keys %::sta_names)) { die("misconfiguration! ") if( ref($sta_name) eq "HASH"); my $ip = $::sta_names{$sta_name}; new_wifi_station( $sta_name, $ip, \%results2, $::wifi_mode, 0); } } elsif (defined $::sta_wiphy) { print "Creating virtual radio: $::sta_wiphy.\n"; new_wifi_radio(); } else { print "Please define a radio with --radio\n"; exit(1); } }# doAdd sub doDelWiphyVdevs { if (defined $::port_del) { # List ports on the resource in question, delete anything that has port_del for # a parent. my $q; for ($q = 0; $q < 5; $q++) { my @ports = $::utils->getPortListing(1, $::resource); my $found = 0; my $i; for ($i = 0; $i<@ports; $i++) { my $dev = $ports[$i]->dev(); my $parent = $ports[$i]->parent(); if ($parent eq $::port_del) { print "deleting port $dev\n" unless($::utils->isQuiet()); $::utils->doCmd($::utils->fmt_cmd("rm_vlan", 1, $::resource, $dev)); $found++; } } if ($found == 0) { last; } sleep(10); } } } sub doDel { if (defined $::port_del) { #delete any port listed delete_port(); } }# doDel sub ip2ipn { return unpack 'N', inet_aton(shift); } sub ipn2ip { return inet_ntoa( pack 'N', shift ); } sub initStationAddr { die("Zero stations cannot be very useful, bye.") if ($::num_stations < 1); if ($::num_stations > 200 ) { println("Over 200 stations is unlikely to work on one machine, expect over-subscription behavior."); sleep 2; } my $ip; my $ip_obj; if($::first_ip eq "DHCP"){ $ip = "DHCP"; } else { $ip = $::first_ip; } # often people create own stations at sta0 or sta1 and # those are really hard to sort in the Ports Tab. We shall # start with sta100 by default. Separate the numeric suffix # use that as offset my $offset = 100; if ($::first_sta =~ /^.*?(\d+)\s*$/) { $offset = $1; } for( my $i=0; $i < $::num_stations ; $i++ ) { my $suffix = 0 + $i + $offset; my $name; if ($i == 0) { $name = $::first_sta; } else { $name = sprintf("sta%03d", $suffix); } my $ep_name1 = sprintf("ep-A%03d", $suffix); my $ep_name2 = sprintf("ep-B%03d", $suffix); my $cx_name = sprintf("cx-%03d", $suffix); if ($ip ne 'DHCP' && $i > 0) { my $val = ip2ipn($ip); #print "ip[$ip] to int[$val]\n"; $ip = ipn2ip( 1 + $val); #print "ip[$ip] from int[".(1+$val)."]\n"; } $::sta_names{$name} = $ip; $::cx_names{$name} = { ep1 => $ep_name1, ep2 => $ep_name2, cx => $cx_name }; } } # ~initStationAddr ## ----- ----- ----- ----- ----- ----- ----- ----- ----- ----- ## ## Set phy channel, antennas ## ## ----- ----- ----- ----- ----- ----- ----- ----- ----- ----- sub set_channel { my $res = shift; my $phy = shift; my $chan = shift; die("set_channel: unset resource") unless ((defined $res) && ("" ne $res)); die("set_channel: unset radio") unless ((defined $phy) && ("" ne $phy)); die("set_channel: unset channel") unless ((defined $chan) && ("" ne $chan)); my $mode = 'NA'; my $cmd = $::utils->fmt_cmd("set_wifi_radio", 1, $res, $phy, $mode, $chan); $::utils->doAsyncCmd($cmd); sleep 1; } sub set_antenna { my $res = shift; my $phy = shift; my $ant = shift; die("set_channel: unset resource") unless ((defined $res) && ("" ne $res)); die("set_channel: unset radio") unless ((defined $phy) && ("" ne $phy)); die("Antenna mode [$ant] does not exist.") if (! exists $::antenna_table{$ant}); my $mode = 'NA'; my $chan = ((defined $::phy_channel) && ($::phy_channel > 0)) ? $::phy_channel : 'NA'; my $country = 'NA'; my $freq = 'NA'; #'0xFFFF' will override channel my $frag = 'NA'; my $rate = 'NA'; my $rts = 'NA'; my $txpower = 'NA'; my $mac = 'NA'; my $antenna = $::antenna_table{$ant}; #print "ANTENNA: $ant -> $antenna\n"; my $cmd = $::utils->fmt_cmd("set_wifi_radio", 1, $::resource, $phy, $mode, $chan, $country, $freq, $frag, $rate, $rts, $txpower, $mac, $antenna); $::utils->doAsyncCmd($cmd); sleep 1; } ## ----- ----- ----- ----- ----- ----- ----- ----- ----- ----- ## ## M A I N ## ## ----- ----- ----- ----- ----- ----- ----- ----- ----- ----- my $help; if (@ARGV < 2) { print $usage; exit 0; } GetOptions ( 'mgr|m=s' => \$::lfmgr_host, 'mgr_port|p=i' => \$lfmgr_port, 'resource|r=i' => \$::resource, 'resource2|r2=i' => \$::resource2, 'quiet|q=s' => \$::quiet, 'radio|o=s' => \$::sta_wiphy, 'channel|chan=i' => \$::phy_channel, 'antenna|ant=s' => \$::phy_antenna, 'ssid|s=s' => \$::ssid, 'security=s' => \$::security, 'xsec=s' => \$::xsec, 'passphrase|h=s' => \$::passphrase, 'first_ip|b=s' => \$::first_ip, 'first_sta|c=s' => \$::first_sta, 'num_stations|n=i' => \$::num_stations, 'netmask|k=s' => \$::netmask, 'change_mac=i' => \$::change_mac, 'mac-pattern|mac_pattern=s' => \$::mac_pattern, 'cxtype|x=s' => \$::cx_type, 'bps_min|bps-min|y=s' => \$::min_tx, 'bps_max|bps-max|z=s' => \$::max_tx, 'duration|e=i' => \$::duration, 'upstream|t=s' => \$::upstream_port, 'action|a=s' => \$action, 'db_preload=s' => \$::db_preload, 'db_save=s' => \$::db_save, 'db_postload=s' => \$::db_postload, 'poll_time|poll-time=i' => \$::poll_time, 'wifi_mode|mode=s' => \$::wifi_mode, 'bssid=s' => \$::bssid, 'traffic_type=s' => \$::traffic_type, 'vrad_chan=i' => \$::vrad_chan, 'port_del=s' => \$::port_del, 'admin_down_on_add' => \$::admin_down_on_add, 'log_cli=s{0,1}' => \$log_cli, # use ENV{LOG_CLI} elsewhere 'help|?' => \$help, ) || (print($usage) && exit(1)); if ($help) { print($usage) && exit(0); } $SIG{ __DIE__ } = sub { Carp::confess( @_ ) }; if ($::quiet eq "0") { $::quiet = "no"; } elsif ($::quiet eq "1") { $::quiet = "yes"; } # Open connection to the LANforge server. if (defined $log_cli) { if ($log_cli ne "unset") { # here is how we reset the variable if it was used as a flag if ($log_cli eq "") { $ENV{'LOG_CLI'} = 1; } else { $ENV{'LOG_CLI'} = $log_cli; } } } our $t = new Net::Telnet(Prompt => '/default\@btbits\>\>/', Timeout => 20); $t->open(Host => $lfmgr_host, Port => $lfmgr_port, Timeout => 10); $t->waitfor("/btbits\>\>/"); # Configure our utils. our $utils = new LANforge::Utils(); $utils->telnet($t); # Set our telnet object. if ($utils->isQuiet()) { if (defined $ENV{'LOG_CLI'} && $ENV{'LOG_CLI'} ne "") { $utils->cli_send_silent(0); } else { $utils->cli_send_silent(1); # Do not show input to telnet } $utils->cli_rcv_silent(1); # Repress output from telnet } else { $utils->cli_send_silent(0); # Show input to telnet $utils->cli_rcv_silent(0); # Show output from telnet } $utils->log_cli("# $0 ".`date "+%Y-%m-%d %H:%M:%S"`); if ($db_postload ne "" && db_exists($::db_postload)==0) { print("Scenario [$::db_postload] does not exist. Please create it first."); exit(1); } if ($::db_preload ne "") { if(db_exists($::db_preload)==1) { print "Loading scenario $::db_preload..."; load_db($::db_preload); print "done\n"; } else { print("Scenario [$::db_postload] does not exist. Please create it first."); exit(1); } } if (!($action =~ /del/)) { # Below steps are unrelated to deleting objects if(!defined $::first_ip || $::first_ip eq '') { print("Please specify the first IP for stations. You may choose DHCP or an IP that will be incremented.\n"); exit(1); } if(! $::ssid ) { print("Please configure SSID for stations to associate with.\n"); exit(1); } if(! $::sta_wiphy ) { print("Please specify the base radio port for the wifi stations. ".$usage ); exit(1); } } if(! $action ) { print("Please specify which test action we want: step1: connect one station and pass upload and download traffic step2: connect 10 wifi stations and disconnect. add: create virtual radio.\n del: Delete virtual radio.\n"); exit(1); } if (!($action =~ /del/)) { # Below steps are unrelated to deleting objects if(0 == keys(%::sta_names)) { initStationAddr(); } if(! %sta_names ) { print("Please configure station list to test with.\n"); exit(1); } } if ($action =~ /step|add/) { if (defined $::phy_channel && $::phy_channel > 0) { set_channel($::resource, $::sta_wiphy, $::phy_channel); } if (defined $::phy_antenna) { set_antenna($::resource, $::sta_wiphy, $::phy_antenna); } } # take first station and associate it or fail if ($action eq "step1" ) { if ($traffic_type !~ /^(concurrent|separate)$/ ) { print("Please specify concurrent or separate as traffic_type.\n"); exit(1); } doStep_1(%sta_names, $::ssid, $sta_wiphy); if ($db_save ne "") { save_db($::db_save); } if ($::db_postload ne "") { load_db($::db_postload); } } elsif( $action eq "step2" ) { doStep_2(%sta_names, $::ssid, $sta_wiphy); if ($::db_postload ne "") { load_db($::db_preload); } } elsif ($action eq "add" ) { doAdd(); if ($::db_postload ne "") { load_db($::db_preload); } } elsif ($action eq "del" ) { doDel(); if ($::db_postload ne "") { load_db($::db_preload); } } elsif ($action eq "del_all_phy" ) { doDelWiphyVdevs(); if ($::db_postload ne "") { load_db($::db_preload); } } elsif ($action eq "show_port") { print $utils->doAsyncCmd($::utils->fmt_cmd("nc_show_port", 1, $resource, (sort(keys %sta_names))[0])) . "\n"; } exit(0);