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