mirror of
https://github.com/Telecominfraproject/wlan-lanforge-scripts.git
synced 2025-10-28 17:32:35 +00:00
added example on how to use flags Signed-off-by: sushant <sushant.bawiskar@candelatech.com>
1812 lines
65 KiB
Perl
Executable File
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);
|