Files
wlan-lanforge-scripts/lf_associate_ap.pl
sushant 3f047763bb Added uss-bss-transition and disable-twt
added example on how to use flags

Signed-off-by: sushant <sushant.bawiskar@candelatech.com>
2021-05-17 17:13:28 +05:30

1812 lines
65 KiB
Perl
Executable File

#!/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) 2020, 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;
no warnings 'portable'; # Support for 64-bit ints required
use Socket;
#use Data::Dumper;
our $binsleep = 0;
if ( -x "/bin/sleep" || -x "/usr/bin/sleep") {
$::binsleep = 1;
}
sub altsleep {
my ($time) = @_;
if ($::binsleep) {
`sleep $time`;
}
elsif ( $time < 1) {
sleep(1);
}
elsif (int($time) != $time) {
$time += 1.0;
$time = int($time);
sleep($time);
}
else {
sleep($time);
}
}
# Un-buffer output
$| = 1;
use Cwd qw(getcwd);
my $cwd = getcwd();
# use lib prepends to @INC, so put lower priority first
# This is before run-time, so cannot condition this with normal 'if' logic.
use lib '/home/lanforge/scripts';
use lib "./";
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 = ""; # channel number
our $phy_antenna = ""; # number of antennas, 0 means 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)
"disable_roam" => 0x80000000, # Disable automatic station roaming based on scan results.
"ht160_enable" => 0x100000000, # Enable HT160 mode.
"disable_fast_reauth" => 0x200000000, # Disable fast_reauth option for virtual stations.
"mesh_mode" => 0x400000000, # Station should be in MESH mode.
"power_save_enable" => 0x800000000, # Station should enable power-save. May not work in all drivers/configurations.
"create_admin_down" => 0x1000000000, # Station should be created admin-down.
"wds-mode" => 0x2000000000, # WDS station (sort of like a lame mesh), not supported on ath10k
"no-supp-op-class-ie" => 0x4000000000, # Do not include supported-oper-class-IE in assoc requests. May work around AP bugs.
"txo-enable" => 0x8000000000, # Enable/disable tx-offloads, typically managed by set_wifi_txo command
"wpa3" => 0x10000000000, # Enable WPA-3 (SAE Personal) mode.
"use-bss-transition" => 0x80000000000, # Enable BSS transition.
"disable-twt" => 0x100000000000, # Disable TWT mode
);
our %ieee80211w_options = (
"disabled" => 0,
"optional" => 1,
"required" => 2,
"0" => 0,
"1" => 1,
"2" => 2
);
our $ieee80211w = "NA";
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;
our $gateway = "NA";
our %wifi_modes = (
"a" => "1",
"b" => "2",
"g" => "3",
"abg" => "4",
"abgn" => "5",
"bgn" => "6",
"bg" => "7",
"abgnAC" => "8",
"anAC" => "9",
"an" => "10",
"bgnAC" => "11",
"abgnAX" => "12",
"bgnAX" => "13",
"anAX" => "14"
);
our $wifi_mode ="";
our $bssid = "";
my $mode_list = join("|", sort keys %wifi_modes);
## ----- ----- ----- ----- ----- ----- ----- ----- ----- ----- #
## Usage #
## ----- ----- ----- ----- ----- ----- ----- ----- ----- ----- #
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|wpa3}] # station authentication type, Default is open
[--xsec {comma,separated,list} ] # dot1x, 11u, other features, read script {to set flags same as in add_sta}
[--passphrase {...}] # Set security too if you want to enable security
[--wifi_mode {$mode_list}]
[--ieee80211w {disabled,optional,required}] # protected management frames (wpa2-ent/wpa3) also { NA, 0, 1, 2 }
## station configuration
[--num_stations {$num_stations}] # Defaults to 1
[--first_sta {$first_sta}]
[--first_ip {DHCP | DHCP6 | DHCP,DHCP6 |<ip address>}]
# use DHCP,DHCP6 to enable both DHCP and DHCP6
[--netmask {$netmask}]
[--gateway {$gateway}]
[--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 <num_stations> 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:*:*:*'
Note: mac_pattern is NOT a regex, it is octet based tokens:
* = rand(256)
xx = parent mac octet
You can specify a numeric mac address (d0:01:00:00:af:ff) and it can get incremented
## 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
## Create a station and set Flags
$0 --mgr localhost --radio wiphy1 --ssid sushant-AP --action add --num_stations 1 \\
--xsec use-bss-transition --first_ip DHCP
);
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 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($::utils->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 { /\s+MAC:\s+[^ ]+/ } @status_lines;
die ("::get_radio_bssid: failed to find radio bssid, no MAC lines")
if (@mac_lines < 1);
my ($parent_bssid) = $mac_lines[0] =~ /\s+MAC:\s+([^ ]+)/;
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 "");
if (($pattern !~ /x+/i) && ($pattern !~ /[*]+/)) {
return $pattern; # this lacks pattern tokens
}
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 ];
}
}
#print "####### new_mac_from_pattern: [$parent_mac][$pattern] -> ".lc(join(":", @new_hunks))."\n";
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)?':':'');
}
#print "new_random_mac $rv\n";
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 "");
$::ieee80211w = "NA"
if (!(defined $::ieee80211w) || ($::ieee80211w eq ""));
if ($::ieee80211w ne "NA") {
if ( exists $::ieee80211w_options{ $::ieee80211w }) {
$::ieee80211w = $::ieee80211w_options{ $::ieee80211w };
}
elsif ((int($::ieee80211w) < 0) || (int($::ieee80211w) > 2)) {
print("\n* ieee80211w value outside of values {0, 1, 2} or {disabled, optional, required} -- being set to NA\n");
$::ieee80211w = "NA";
}
# print("\n* ieee80211w value set to $::ieee80211w \n");
}
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", $::ieee80211w );
}
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 =~ /\bDHCP\b/) ? 1 : 0;
my $use_dhcp6 = ($ip_addr =~ /\bDHCP6\b/) ? 1 : 0;
my $ip = ($use_dhcp||$use_dhcp6) ? "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);
$cur_flags |= 0x20000000000 if ($use_dhcp6);
#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 |= 0x10 if (($::gateway ne "NA") || ($::gateway ne "") || ($::gateway ne "0.0.0.0"));
$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
$ist_flags |= 0x1000000; # Always interested in DHCP, we either set it to DHCP or IP
my $gw = "0.0.0.0";
if (($::gateway ne "") || ($::gateway ne "") || ($::gateway ne "0.0.0.0")) {
$gw = $::gateway;
}
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,
$gw, "$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 =~ /\bDHCP\b/) ? 1 : 0;
my $use_dhcp6 = ($ip_addr =~ /\bDHCP6\b/) ? 1 : 0;
my $ip = ($use_dhcp||$use_dhcp6) ? "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 $gw = (($::gateway eq "NA") || ($::gateway eq "") || ($::gateway eq "0.0.0.0")) ? "0.0.0.0" : $::gateway;
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, $gw, "$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 $num_in_series = shift; # use this to add to non-patterned mac-address
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;
}
}
#print "new_wifi_station->mac_addr: $mac_addr\n";
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 "OLD MAC $::mac_pattern NEW MAC $mac_addr\n";
if (($mac_addr eq $::mac_pattern) && ($num_in_series > 0)) {
$mac_addr = $::utils->mac_add($::mac_pattern, $num_in_series);
}
#print "OLD MAC $::mac_pattern NEWER MAC $mac_addr\n";
#print "new_wifi_station->new_mac_from_pattern: $mac_addr\n";
}
#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 | 0x10000000000);
$flagsmask |= 0x1000000000 if ($::admin_down_on_add);
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);
#$::utils->sleep_ms(20);
$sta1_cmd = fmt_port_cmd($resource, $sta_name, $ip_addr, $mac_addr);
$::utils->doCmd($sta1_cmd);
#$::utils->sleep_ms(20);
#$::utils->doAsyncCmd($::utils->fmt_cmd("nc_show_port", 1, $::resource, $sta_name));
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);
#$::utils->sleep_ms(20);
}
#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));
$::utils->sleep_ms(20);
}
}
sub get_sta_state {
my($rs_status) = @_;
die("is_assoc_state: wants ref to status string") unless($rs_status);
my @lines = split(/\r?\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;
my $probed_seen = 0;
for my $line (@lines) {
$first = "_";
my($key) = $line =~ m/^\s*([^:]+:)\s+/;
#print "{{{$key}}} $line\n";
next if ($line =~ /^\s*$/);
next if ($line =~ /RSLT:/);
last if ($line =~ /default@/);
$probed_seen++ if ($line =~ /Probed/);
if ($key && $key eq "MAC:" ) {
@hunks = split(/: /, $line);
$mac = (split(/ /, $hunks[1]))[0];
$name = (split(/ /, $hunks[2]))[0];
next;
}
if ($key && $key eq "IP:") {
@hunks = split(/: /, $line);
$ip = (split(/ /, $hunks[1]))[0];
$mask = (split(/ /, $hunks[2]))[0];
$gw = (split(/ /, $hunks[3]))[0];
next;
}
if ($probed_seen && ($line =~ /Mode:/)) {
@hunks = split(/: /, $line);
$careful = 1;
$mode = (split(/ /, $hunks[2]))[0];
next;
}
if( $probed_seen && $careful && ($key eq "Channel:")) {
@hunks = split(/: /, $line);
#print Dumper(\@hunks);
$channel = (split(/ /, $hunks[1]))[0];
$freq = (split(/ /, $hunks[3]))[0];
if ((@hunks > 3) && (defined $hunks[4])) {
$assoc = (split(/ /, $hunks[4]))[0];
}
}
}
my %rv = (
"assoc" => $assoc,
"freq" => $freq,
"ip" => $ip,
"mask" => $mask,
"gw" => $gw,
"mac" => $mac,
"mode" => $mode,
"name" => $name );
#print Dumper(\%rv);
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));
}
}
sleep(1);
# force a refresh on them so phantom doesn't show
foreach my $sta_name (reverse sort(keys %::sta_names)) {
my $status = $::utils->doAsyncCmd($::utils->fmt_cmd("nc_show_port", 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));
#print "STATUS: $status\n\n";
my %sta_status = get_sta_state(\$status);
#print " $sta_name ".$sta_status{"assoc"};
if( $sta_status{"assoc"} !~ /NA|Not-Associated|unknown/) {
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";
}
sleep 1;
} # ~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: ";
my $i = 0;
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, $i);
altsleep(0.12);
altsleep(0.6) if (($i % 5) == 0);
$i++;
}
sleep(1);
#print "**************************************************\n";
foreach my $sta_name (sort(keys %::sta_names)) {
my $status = $::utils->doAsyncCmd($::utils->fmt_cmd("nc_show_port", 1, $::resource, $sta_name));
}
sleep(1);
#print "**************************************************\n";
print " Created $::num_stations stations\n";
sleep(1);
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
my $i = 0;
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, $i);
$i++;
# 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"} !~ /NA|Not-Associated|unknown/);
#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
#print "## doAdd: sta_wiphy[$::sta_wiphy]\n";
if ($::num_stations > 0 && defined $::sta_wiphy) {
my %results2 = ();
print "creating stations:";
my $i = 0;
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";
new_wifi_station( $sta_name, $ip, \%results2, $::wifi_mode, $i);
if (($i % 10) == 9) {
$::utils->sleep_ms(120);
}
else {
$::utils->sleep_ms(30);
}
$i++;
}
print " done\n";
}
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 =~ /^DHCP/){
$ip = $::first_ip;
}
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 = $::phy_channel;
if ($chan eq "") {
$chan = "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,
'lf_mgr_port|lf_port|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|password|pass|h=s' => \$::passphrase,
'first_ip|b=s' => \$::first_ip,
'first_sta|c=s' => \$::first_sta,
'num_stations|num_sta|n=i' => \$::num_stations,
'netmask|k=s' => \$::netmask,
'gateway|g=s' => \$::gateway,
'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,
'ieee80211w=s' => \$::ieee80211w,
'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;
}
}
}
# Configure our utils.
our $utils = new LANforge::Utils();
print "Connecting to $lfmgr_host, $lfmgr_port\n";
$::utils->connect($lfmgr_host, $lfmgr_port);
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, DHCP6, or DHCP,DHCP6 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 ($::phy_channel ne "") {
set_channel($::resource, $::sta_wiphy, $::phy_channel);
}
if ($::phy_antenna ne "") {
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);