Commit 9ca9e1d9 authored by David Johnson's avatar David Johnson
Browse files

* tmcd/common/ifdynconfig: New client side command, intended to be called

    from link-agent to provide dynamic link modification.  Calls
    liblocsetup::os_ifdynconfig_cmds to get a list of commands to change
    the iface config; if this function does not exist, it tries to do its
    best via ifconfig.

* tmcd/linux/liblocsetup.pm: Added support for dynamic configuration of
    ath and flex900 ifacetypes.  Also added more static support for both,
    including adhoc/monitor modes, rts threshold, etc, for ath; carrier
    threshold and rx gain for flex900 (sometimes these params really need
    to be set so our gnu radios can actually talk...).

* event/link-agent/link-agent.c: Do link up/down/modify by calling
    ifdynconfig with the event params.  Backwards compat with old
    functionality.
parent b8c6c2a1
......@@ -5,7 +5,9 @@
*/
/*
* Link Agent; very linux/wireless specific at the moment!
* Link Agent: supports up/down, parameterized modifications to interfaces.
* Commands are passed to a perl script and interpreted and executed in
* OS/device specific ways; this makes life easier for us all.
*/
#include <stdio.h>
......@@ -26,6 +28,7 @@
static char *progname;
static int debug = 0;
static int verbose = 0;
static char *ifdynconfig = "/usr/local/etc/emulab/ifdynconfig";
static void callback(event_handle_t handle,
event_notification_t notification, void *data);
......@@ -43,21 +46,6 @@ typedef struct ifmap {
} ifmap_t;
ifmap_t *mapping = (ifmap_t *) NULL;
/*
* List of allowed settings we can change. We do not worry about verifying
* the arguments; people are free to screw themselves.
*
* XXX Very iwconfig specific!
*/
char *settings[] = {
"sensitivity",
"txpower",
"channel",
"rate",
"bitrate",
"accesspoint"
};
void
usage()
{
......@@ -318,8 +306,6 @@ callback(event_handle_t handle, event_notification_t notification, void *data)
char eventtype[TBDB_FLEN_EVEVENTTYPE];
char args[2 * BUFSIZ];
char cmd[2 * BUFSIZ];
char updown[BUFSIZ];
int doupdown = 0; /* 1 = before, 2 = after */
ifmap_t *mp;
event_notification_get_objname(handle, notification,
......@@ -355,20 +341,23 @@ callback(event_handle_t handle, event_notification_t notification, void *data)
*/
if (strcmp(eventtype, TBDB_EVENTTYPE_UP) == 0) {
/* sprintf(cmd, "iwconfig %s txpower auto", mp->iface); */
sprintf(cmd, "ifconfig %s up", mp->iface);
sprintf(cmd, "%s %s up", ifdynconfig, mp->iface);
runcommand(cmd);
}
else if (strcmp(eventtype, TBDB_EVENTTYPE_DOWN) == 0) {
/* sprintf(cmd, "iwconfig %s txpower off", mp->iface); */
sprintf(cmd, "ifconfig %s down", mp->iface);
sprintf(cmd, "%s %s down", ifdynconfig, mp->iface);
runcommand(cmd);
}
else if (strcmp(eventtype, TBDB_EVENTTYPE_MODIFY) == 0) {
char *ap = args;
char *cp = cmd, *ecp = &cmd[sizeof(cmd)-1];
int cmdlen;
char cargs[2 * BUFSIZ];
char *cap = cargs, *ecap = &cargs[sizeof(cargs)-1];
int found_enable = 0;
cp += OUTPUT(cp, sizeof(cmd), "iwconfig %s ", mp->iface);
cp += OUTPUT(cp,sizeof(cmd),"%s %s ",ifdynconfig,mp->iface);
cmdlen = strlen(cmd);
/*
......@@ -429,82 +418,32 @@ callback(event_handle_t handle, event_notification_t notification, void *data)
* Alias for UP/DOWN events above. Note that
* we want to run this first/last.
*/
found_enable = 1;
if (! strcasecmp("yes", value)) {
doupdown = 2;
sprintf(updown,
"ifconfig %s up", mp->iface);
cp += OUTPUT(cp,ecp - cp, " up");
}
else if (! strcasecmp("no", value)) {
doupdown = 1;
sprintf(updown,
"ifconfig %s down", mp->iface);
cp += OUTPUT(cp,ecp - cp, " down");
}
else {
error("Ignoring setting: %s=%s\n",
setting, value);
found_enable = 0;
continue;
}
}
else if (! strcasecmp("sensitivity", setting)) {
cp += OUTPUT(cp, ecp - cp, " sens %s", value);
}
else if (! strcasecmp("txpower", setting)) {
if (*value == '\0')
value = "auto";
cp += OUTPUT(cp, ecp - cp, " txpower %s",
value);
}
else if (! strcasecmp("rate", setting) ||
! strcasecmp("bitrate", setting)) {
if (*value == '\0')
value = "auto";
cp += OUTPUT(cp, ecp - cp, " rate %s", value);
}
else if (! strcasecmp("channel", setting)) {
if (*value == '\0')
value = "3";
cp += OUTPUT(cp, ecp - cp, " channel %s",
value);
}
else if (! strcasecmp("accesspoint", setting)) {
char *mac = value;
if (! index(mac, ':')) {
mac = convertmac(mac);
if (mac == NULL)
continue;
}
if (strcasecmp(mac, mp->mac)) {
cp += OUTPUT(cp, ecp - cp,
" mode Managed ap %s",
mac);
}
else {
cp += OUTPUT(cp, ecp - cp,
" mode Master");
}
}
else {
error("Ignoring setting: %s=%s\n",
setting, value);
continue;
cap += OUTPUT(cap,ecap - cap," %s=%s",
setting,value);
}
info("%s\n", cmd);
}
if (doupdown == 1) {
runcommand(updown);
if (!found_enable) {
cp += OUTPUT(cp,ecp - cp," modify");
}
cp += OUTPUT(cp,ecp - cp," %s",cargs);
if (strlen(cmd) > cmdlen)
runcommand(cmd);
if (doupdown == 2) {
runcommand(updown);
}
}
else if (debug) {
info("Ignoring event: %s %s %s\n", objname, eventtype, args);
......
......@@ -77,6 +77,7 @@ common-script-install: dir-install
$(INSTALL) -m 755 $(SRCDIR)/runcvsup.sh $(BINDIR)/runcvsup.sh
$(INSTALL) -m 755 $(SRCDIR)/update $(BINDIR)/update
$(INSTALL) -m 755 $(SRCDIR)/ifsetup $(BINDIR)/ifsetup
$(INSTALL) -m 755 $(SRCDIR)/ifdynconfig $(BINDIR)/ifdynconfig
$(INSTALL) -m 755 $(SRCDIR)/vnodesetup $(BINDIR)/vnodesetup
$(INSTALL) -m 755 $(SRCDIR)/bootsubnodes $(BINDIR)/bootsubnodes
$(INSTALL) -m 755 $(SRCDIR)/bootvnodes $(BINDIR)/bootvnodes
......
#!/usr/bin/perl -w
#
# EMULAB-COPYRIGHT
# Copyright (c) 2007 University of Utah and the Flux Group.
# All rights reserved.
#
use English;
use Getopt::Std;
#
# Dynamically change the configuration of an interface. Since this is both
# OS- and interface type-dependent, it basically just calls into liblocsetup
# to get the appropriate commands to execute to perform the reconfig.
#
sub usage(;$) {
my $prog = shift;
if (!defined($prog)) {
$prog = '';
}
print "Usage: $prog iface up|down|modify [key1=val1 key2=val2 ... ]\n";
exit(1);
}
my $optlist = "d";
my $iface;
my $action;
# Drag in path stuff so we can find emulab stuff.
BEGIN { require "/etc/emulab/paths.pm"; import emulabpaths; }
# Only root.
if ($EUID != 0) {
die("*** $0:\n".
" Must be root to run this script!\n");
}
# Script specific goo.
my $IFCONFIG = "$BINDIR/rc/rc.ifconfig";
my $ROUTECONFIG = "$BINDIR/rc/rc.route";
my $TUNCONFIG = "$BINDIR/rc/rc.tunnels";
my $DELAYCONFIG = "$BINDIR/rc/rc.delays";
my $KEYCONFIG = "$BINDIR/rc/rc.keys";
my $TRACECONFIG = "$BINDIR/rc/rc.trace";
#
# Load the OS independent support library. It will load the OS dependent
# library and initialize itself.
#
use libsetup;
use libtmcc;
# Protos.
sub doboot();
sub doshutdown();
sub doreconfig();
sub docleanup();
my %options;
my $progname = $ARGV[0];
my $debug = 0;
my %modopts = ();
# Parse command line.
if (!getopts($optlist,\%options)) {
usage($progname);
}
if (defined($options{'d'})) {
$debug = 1;
}
# Allow default above.
if (@ARGV) {
$iface = shift @ARGV;
}
else {
usage();
}
if (@ARGV) {
$action = shift @ARGV;
$action = lc($action);
if ($action ne 'up' && $action ne 'down' && $action ne 'modify') {
print STDERR "ERROR: unknown action '$action'!\n";
exit(3);
}
}
else {
usage();
}
if ($action eq 'modify') {
if (!scalar(@ARGV)) {
print STDERR "ERROR: action modify, but no params!\n";
exit(4);
}
}
if (scalar(@ARGV)) {
# dump key=val params into a hash
foreach my $arg (@ARGV) {
my @ts = split(/=/,$arg);
if (scalar(@ts) != 2) {
# don't want the user to be fooled into thinking all configs
# happened, instead of only some...
print STDERR "ERROR: improper key=val pair '$arg'!\n";
exit(25);
}
else {
# NOTE: the event system changes these to uppercase; we want
# lowercase...
$modopts{lc($ts[0])} = $ts[1];
}
}
}
# grab the interface config data
my @ifacecfg;
libsetup::getifconfig(\@ifacecfg);
# find settings for our interface
my $iface_hashref;
foreach my $ifs (@ifacecfg) {
if ($ifs->{'IFACE'} eq $iface) {
$iface_hashref = $ifs;
last;
}
}
if (!defined($iface_hashref)) {
print STDERR "ERROR: could not find Emulab config data for " .
"interface '$iface'!\n";
exit(10);
}
# workaround until support is added to all osdep libs...
if (defined(&liblocsetup::os_ifdynconfig_cmds)) {
my @cmds = ();
my $retval = liblocsetup::os_ifdynconfig_cmds(\@cmds,$iface,$action,
\%modopts,$iface_hashref);
# execute...
foreach my $cmd (@cmds) {
print "cmd = '$cmd'\n";
system($cmd);
}
# we assume that these commands return 0 -- there's not a lot else we can
# do.
exit(0);
}
elsif ($action eq 'modify') {
print STDERR "ERROR: OS does not support parameterized " .
"link modification!\n";
exit(6);
}
else {
# best effort at up/down: ifconfig -- and assume it's in PATH
exit(system("ifconfig $iface $action"));
}
# should not get here
exit(0);
......@@ -79,6 +79,11 @@ my $GATED = "/usr/sbin/gated";
my $ROUTE = "/sbin/route";
my $SHELLS = "/etc/shells";
my $DEFSHELL = "/bin/tcsh";
my $IWCONFIG = '/usr/local/sbin/iwconfig';
my $WLANCONFIG = '/usr/local/bin/wlanconfig';
my $RMMOD = '/sbin/rmmod';
my $MODPROBE = '/sbin/modprobe';
my $IWPRIV = '/usr/local/sbin/iwpriv';
#
# OS dependent part of cleanup node state.
......@@ -183,51 +188,93 @@ sub os_ifconfig_line($$$$$$$;$$$)
$iwcmd .= " txpower auto";
}
# Allow this too.
if (exists($settings->{"sensitivity"})) {
$iwcmd .= " sens " . $settings->{"sensitivity"};
if (exists($settings->{"sens"})) {
$iwcmd .= " sens " . $settings->{"sens"};
}
# allow rts threshold and frag size
if (exists($settings->{'rts'})) {
$iwcmd .= ' rts ' . $settings->{'rts'};
}
if (exists($settings->{'frag'})) {
$iwcmd .= ' frag ' . $settings->{'frag'};
}
#
# We demand to be told if we are the master or a peon.
# We might also be in another mode. Thus, if accesspoint is specified,
# we assume we are in either ap/sta (Master/Managed) mode. If not,
# we look for a 'mode' argument and assume adhoc if we don't get one.
# The reason to assume adhoc is because we need accesspoint set to
# know how to configure the device for ap/sta modes, and setting a
# device to monitor mode by default sucks.
#
# This needs to be last for some reason.
#
if (!exists($settings->{"accesspoint"})) {
warn("*** WARNING: No accesspoint provided for $iface!\n");
return undef;
}
my $accesspoint = $settings->{"accesspoint"};
my $accesspointwdots;
# Allow either dotted or undotted notation!
if ($accesspoint =~ /^(\w{2})(\w{2})(\w{2})(\w{2})(\w{2})(\w{2})$/) {
$accesspointwdots = "$1:$2:$3:$4:$5:$6";
if (exists($settings->{'accesspoint'})) {
my $accesspoint = $settings->{"accesspoint"};
my $accesspointwdots;
# Allow either dotted or undotted notation!
if ($accesspoint =~ /^(\w{2})(\w{2})(\w{2})(\w{2})(\w{2})(\w{2})$/) {
$accesspointwdots = "$1:$2:$3:$4:$5:$6";
}
elsif ($accesspoint =~
/^(\w{2}):(\w{2}):(\w{2}):(\w{2}):(\w{2}):(\w{2})$/) {
$accesspointwdots = $accesspoint;
$accesspoint = "${1}${2}${3}${4}${5}${6}";
}
else {
warn("*** WARNING: Improper format for MAC ($accesspoint) ".
"provided for $iface!\n");
return undef;
}
if (libsetup::findiface($accesspoint) eq $iface) {
$wlccmd .= " wlanmode ap";
$iwcmd .= " mode Master";
}
else {
$wlccmd .= " wlanmode sta";
$iwcmd .= " mode Managed ap $accesspointwdots";
}
}
elsif ($accesspoint =~
/^(\w{2}):(\w{2}):(\w{2}):(\w{2}):(\w{2}):(\w{2})$/) {
$accesspointwdots = $accesspoint;
$accesspoint = "${1}${2}${3}${4}${5}${6}";
elsif (exists($settings->{'mode'})) {
if ($settings->{'mode'} =~ /ad[\s\-]*hoc/i) {
$wlccmd .= " wlanmode adhoc";
$iwcmd .= " mode Ad-Hoc";
}
elsif ($settings->{'mode'} =~ /monitor/i) {
$wlccmd .= " wlanmode monitor";
$iwcmd .= " mode Monitor";
}
elsif ($settings->{'mode'} =~ /ap/i
|| $settings->{'mode'} =~ /access[\s\-]*point/i
|| $settings->{'mode'} =~ /master/i) {
$wlccmd .= " wlanmode ap";
$iwcmd .= " mode Master";
}
elsif ($settings->{'mode'} =~ /sta/i
|| $settings->{'mode'} =~ /managed/i) {
$wlccmd .= " wlanmode sta";
$iwcmd .= " mode Managed ap any";
}
else {
warn("*** WARNING: Invalid mode provided for $iface!\n");
return undef;
}
}
else {
warn("*** WARNING: Improper format for MAC ($accesspoint) ".
"provided for $iface!\n");
warn("*** WARNING: No mode implied for $iface!\n");
return undef;
}
if (libsetup::findiface($accesspoint) eq $iface) {
$wlccmd .= " wlanmode ap";
$iwcmd .= " mode Master";
}
else {
$wlccmd .= " wlanmode sta";
$iwcmd .= " mode Managed ap $accesspointwdots";
}
$uplines = $wlccmd . "\n";
$uplines .= $privcmd . "\n";
$uplines .= $iwcmd . "\n";
$uplines .= sprintf($IFCONFIG, $athiface, $inet, $mask) . "\n";
$downlines = "$IFCONFIGBIN $athiface down\n";
$downlines .= "/usr/local/bin/wlanconfig $athiface destroy\n";
$downlines .= "$WLANCONFIG $athiface destroy\n";
$downlines .= "$IFCONFIGBIN $iface down\n";
return ($uplines, $downlines);
}
......@@ -272,6 +319,13 @@ sub os_ifconfig_line($$$$$$$;$$$)
my $rate = $settings->{"rate"};
$tuncmd .= " -r $rate";
if (exists($settings->{'carrierthresh'})) {
$tuncmd .= " -c " . $settings->{'carrierthresh'};
}
if (exists($settings->{'rxgain'})) {
$tuncmd .= " --rx-gain=" . $settings->{'rxgain'};
}
$uplines = $tuncmd . " > /dev/null 2>&1 &\n";
$uplines .= "sleep 5\n";
$uplines .= "$IFCONFIGBIN $iface hw ether $mac\n";
......@@ -742,4 +796,556 @@ sub os_fwrouteconfig_line($$$)
return ($upline, $downline);
}
# proto for a function used in os_ifdynconfig_cmds
sub getCurrentIwconfig($;$);
#
# Returns a list of commands needed to change the current device state to
# something matching the given configuration options.
#
sub os_ifdynconfig_cmds($$$$$)
{
my ($ret_aref,$iface,$action,$optref,$ifcfg) = @_;
my %opts = %$optref;
my %flags = ();
# this is the hash returned from getifconfig, but only for this interface
my %emifc = %$ifcfg;
my @cmds = ();
# only handle the atheros case for now, since it's the only one
# that can be significantly parameterized
if (exists($emifc{'TYPE'}) && $emifc{'TYPE'} eq 'ath') {
my ($ifnum) = $iface =~ /wifi(\d+)/;
my $ath = "ath${ifnum}";
my $wifi = $iface;
# check flags
my ($reset_wlan,$reset_kmod,$remember) = (0,0,0);
if (exists($opts{'resetkmod'}) && $opts{'resetkmod'} == 1) {
$reset_kmod = 1;
# note that this forces a wlan reset too!
$reset_wlan = 1;
delete $opts{'resetkmod'};
}
if (exists($flags{'resetwlan'}) && $opts{'resetwlan'} == 1) {
$reset_wlan = 1;
delete $opts{'resetwlan'};
}
# we only want to try to keep similar config options
# if the user tells us to...
if (exists($flags{'usecurrent'}) && $opts{'usecurrent'} == 1) {
$remember = 1;
delete $opts{'usecurrent'};
}
# handle the up/down case right away.
if (($action eq 'up' || $action eq 'down')
&& scalar(keys(%opts)) == 0) {
push @cmds,"$IFCONFIGBIN $ath $action";
@$ret_aref = @cmds;
return 0;
}
# first grab as much current state as we can, so we don't destroy
# previous state if we have to destroy the VAP (i.e., athX) interface
#
# NOTE that we don't bother grabbing current ifconfig state --
# we assume that the current state is just what Emulab configured!
my $iwc_ref = getCurrentIwconfig($ath);
my %iwc = %$iwc_ref;
# hash containing new config:
my %niwc;
# first, whack the emulab and user-supplied configs
# so that the iwconfig params match what we need to give iwconfig
# i.e., emulab specifies ssid and we need essid.
if (exists($emifc{'ssid'})) {
$emifc{'essid'} = $emifc{'ssid'};
delete $emifc{'ssid'};
}
if (exists($opts{'ssid'})) {
$opts{'essid'} = $opts{'ssid'};
delete $opts{'ssid'};
}
if (exists($opts{'ap'})) {
$opts{'accesspoint'} = $opts{'ap'};
delete $opts{'ap'};
}
# we want this to be determined by the keyword 'freq' to iwconfig,
# not channel
if (exists($opts{'channel'}) && !exists($opts{'freq'})) {
$opts{'freq'} = $opts{'channel'};
}
for my $ok (keys(%opts)) {
print STDERR "opts kv $ok=".$opts{$ok}."\n";
}
for my $tk (keys(%iwc)) {
print STDERR "iwc kv $tk=".$iwc{$tk}."\n";
}
# here's how we set things up: we set niwc to emulab wireless data
# (i.e., INTERFACE_SETTINGs), then add in any current state, then
# add in any of the reconfig options.
my $key;
if ($remember) {
for $key (keys(%{$emifc{'SETTINGS'}})) {
$niwc{$key} = $emifc{'SETTINGS'}->{$key};
}
for $key (keys(%iwc)) {
$niwc{$key} = $iwc{$key};
}
}
for $key (keys(%opts)) {
$niwc{$key} = $opts{$key};
}
for my $nk (keys(%niwc)) {
print STDERR "niwc kv $nk=".$niwc{$nk}."\n";
}
# see what has changed and what we're going to have to do
my ($mode_ch,$proto_ch) = (0,0);
# first, change mode to a string matching those returned by iwconfig:
if (exists($niwc{'mode'})) {
if ($niwc{'mode'} =~ /ad[\s\-]{0,1}hoc/i) {
$niwc{'mode'} = 'Ad-Hoc';
}
elsif ($niwc{'mode'} =~ /monitor/i) {
$niwc{'mode'} = "Monitor";
}
elsif ($niwc{'mode'} =~ /ap/i
|| $niwc{'mode'} =~ /master/i) {
$niwc{'mode'} = "Master";
}
elsif ($niwc{'mode'} =~ /sta/i
|| $niwc{'mode'} =~ /managed/i) {
$niwc{'mode'} = 'Managed';
}