liblocsetup.pm 65.4 KB
Newer Older
1
#!/usr/bin/perl -wT
Leigh B. Stoller's avatar
Leigh B. Stoller committed
2
#
3
# Copyright (c) 2000-2013 University of Utah and the Flux Group.
4
5
6
7
8
9
10
11
12
13
14
15
16
17
18
19
20
21
22
# 
# {{{EMULAB-LICENSE
# 
# This file is part of the Emulab network testbed software.
# 
# This file is free software: you can redistribute it and/or modify it
# under the terms of the GNU Affero General Public License as published by
# the Free Software Foundation, either version 3 of the License, or (at
# your option) any later version.
# 
# This file is distributed in the hope that it will be useful, but WITHOUT
# ANY WARRANTY; without even the implied warranty of MERCHANTABILITY or
# FITNESS FOR A PARTICULAR PURPOSE.  See the GNU Affero General Public
# License for more details.
# 
# You should have received a copy of the GNU Affero General Public License
# along with this file.  If not, see <http://www.gnu.org/licenses/>.
# 
# }}}
Leigh B. Stoller's avatar
Leigh B. Stoller committed
23
24
#

25
26
27
28
29
30
31
#
# Linux specific routines and constants for the client bootime setup stuff.
#
package liblocsetup;
use Exporter;
@ISA = "Exporter";
@EXPORT =
32
    qw ( $CP $EGREP $NFSMOUNT $UMOUNT $TMPASSWD $SFSSD $SFSCD $RPMCMD
Leigh B Stoller's avatar
Leigh B Stoller committed
33
	 $HOSTSFILE $LOOPBACKMOUNT $TMGROUP $TMSHADOW $TMGSHADOW $CHMOD
34
	 os_account_cleanup os_ifconfig_line os_etchosts_line
35
	 os_setup os_groupadd os_useradd os_userdel os_usermod os_mkdir
36
	 os_ifconfig_veth os_viface_name os_modpasswd
37
38
	 os_routing_enable_forward os_routing_enable_gated
	 os_routing_add_manual os_routing_del_manual os_homedirdel
39
	 os_groupdel os_getnfsmounts os_islocaldir os_mountextrafs
40
	 os_fwconfig_line os_fwrouteconfig_line os_config_gre
41
	 os_get_disks os_get_disk_size os_get_partition_info os_nfsmount
42
	 os_get_ctrlnet_ip
43
44
	 os_getarpinfo os_createarpentry os_removearpentry
	 os_getstaticarp os_setstaticarp
45
46
       );

47
48
sub VERSION()	{ return 1.0; }

49
50
# Must come after package declaration!
use English;
51
use Fcntl;
52

53
54
55
56
57
58
59
60
61
62
63
64
65
66
67
# Load up the paths. Its conditionalized to be compatabile with older images.
# Note this file has probably already been loaded by the caller.
BEGIN
{
    if (-e "/etc/emulab/paths.pm") {
	require "/etc/emulab/paths.pm";
	import emulabpaths;
    }
    else {
	my $ETCDIR  = "/etc/rc.d/testbed";
	my $BINDIR  = "/etc/rc.d/testbed";
	my $VARDIR  = "/etc/rc.d/testbed";
	my $BOOTDIR = "/etc/rc.d/testbed";
    }
}
68

69
70
# Convenience.
sub REMOTE()	{ return libsetup::REMOTE(); }
71
sub REMOTEDED()	{ return libsetup::REMOTEDED(); }
72
sub PLAB()	{ return libsetup::PLAB(); }
73
74
75
sub LINUXJAILED()  { return libsetup::LINUXJAILED(); }
sub GENVNODE()     { return libsetup::GENVNODE(); }
sub GENVNODETYPE() { return libsetup::GENVNODETYPE(); }
Mike Hibler's avatar
Mike Hibler committed
76
sub INXENVM()   { return libsetup::INXENVM(); }
77
sub INVZVM()    { return libsetup::INVZVM(); }
78

79
80
#
# Various programs and things specific to Linux and that we want to export.
Ryan Jackson's avatar
Ryan Jackson committed
81
#
82
$CP		= "/bin/cp";
Mike Hibler's avatar
Mike Hibler committed
83
$DF		= "/bin/df";
84
$EGREP		= "/bin/egrep -q";
85
86
87
# Note that we try multiple versions in os_nfsmount below; this is for legacy
# code, or code where the mount is best done in the caller itself... or code
# I didn't want to convert!
Mike Hibler's avatar
Mike Hibler committed
88
$NFSMOUNT	= "/bin/mount -o nolock,udp";
89
$LOOPBACKMOUNT	= "/bin/mount -n -o bind ";
90
$UMOUNT		= "/bin/umount";
91
$TMPASSWD	= "$ETCDIR/passwd";
92
93
94
$TMGROUP	= "$ETCDIR/group";
$TMSHADOW       = "$ETCDIR/shadow";
$TMGSHADOW      = "$ETCDIR/gshadow";
95
96
$SFSSD		= "/usr/local/sbin/sfssd";
$SFSCD		= "/usr/local/sbin/sfscd";
Mike Hibler's avatar
Mike Hibler committed
97
$RPMCMD		= "/bin/rpm";
98
$HOSTSFILE	= "/etc/hosts";
99
$WGET		= "/usr/bin/wget";
Leigh B Stoller's avatar
Leigh B Stoller committed
100
$CHMOD		= "/bin/chmod";
101
$ARP		= "/sbin/arp";
102
103
104
105

#
# These are not exported
#
106
107
108
my $TMGROUP	= "$ETCDIR/group";
my $TMSHADOW    = "$ETCDIR/shadow";
my $TMGSHADOW   = "$ETCDIR/gshadow";
109
110
111
112
my $USERADD     = "/usr/sbin/useradd";
my $USERDEL     = "/usr/sbin/userdel";
my $USERMOD     = "/usr/sbin/usermod";
my $GROUPADD	= "/usr/sbin/groupadd";
113
my $GROUPDEL	= "/usr/sbin/groupdel";
114
115
my $IFCONFIGBIN = "/sbin/ifconfig";
my $IFCONFIG    = "$IFCONFIGBIN %s inet %s netmask %s";
116
my $VLANCONFIG  = "/sbin/vconfig";
Mike Hibler's avatar
Mike Hibler committed
117
118
# XXX 10000 is probably not right, but we don't use mii-tool here
my $IFC_10000MBS = "10000baseTx";
119
my $IFC_1000MBS  = "1000baseTx";
120
121
122
123
my $IFC_100MBS  = "100baseTx";
my $IFC_10MBS   = "10baseT";
my $IFC_FDUPLEX = "FD";
my $IFC_HDUPLEX = "HD";
124
my @LOCKFILES   = ("/etc/group.lock", "/etc/gshadow.lock");
125
my $MKDIR	= "/bin/mkdir";
126
127
my $GATED	= "/usr/sbin/gated";
my $ROUTE	= "/sbin/route";
128
129
my $SHELLS	= "/etc/shells";
my $DEFSHELL	= "/bin/tcsh";
130
131
132
133
134
135
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';
my $BRCTL       = "/usr/sbin/brctl";
136
my $ISCSI	= "/sbin/iscsiadm";
137

138
139
140
141
my $PASSDB   = "$VARDIR/db/passdb";
my $GROUPDB  = "$VARDIR/db/groupdb";
my $SYSETCDIR = "/etc";

142
143
my $debug = 0;

144
145
#
# OS dependent part of cleanup node state.
Ryan Jackson's avatar
Ryan Jackson committed
146
#
147
sub os_account_cleanup($)
148
{
149
150
151
152
153
154
    # XXX this stuff should be lifted up into rc.accounts, sigh
    my ($updatemasterpasswdfiles) = @_;
    if (!defined($updatemasterpasswdfiles)) {
	$updatemasterpasswdfiles = 0;
    }

155
156
157
    #
    # Don't just splat the master passwd/group files into place from $ETCDIR.
    # Instead, grab the current Emulab uids/gids, grab the current group/passwd
Ryan Jackson's avatar
Ryan Jackson committed
158
    # files and their shadow counterparts, remove any emulab u/gids from the
159
160
161
162
163
    # loaded instance of the current files, then push any new/changed uid/gids
    # into the master files in $ETCDIR.  Also, we remove accounts from the
    # master files if they no longer appear in the current files.  Finally, we
    # strip deleted uids from any groups they might appear in (!).
    #
164
165
166
167
168
169
170
171
    # And now we only do the merge if told to do so.  This is the default 
    # coming from prepare, now.  If not merging, we just overwrite the real 
    # files with the master files -- we do not update the master files.
    #
    # We *do* output the diff to say what *would* have changed, so that
    # an operator can know that they need to manually add a user to the 
    # master files.
    #
172
173
174
    my %PDB;
    my %GDB;

175
    dbmopen(%PDB, $PASSDB, 0660) or
176
	die "Cannot open $PASSDB: $!";
177
    dbmopen(%GDB, $GROUPDB, 0660) or
178
	die "Cannot open $GROUPDB: $!";
179

180
181
182
183
184
185
    if ($debug) {
	use Data::Dumper;
	print Dumper(%PDB) . "\n\n";
	print Dumper(%GDB) . "\n\n";
    }

186
187
188
189
190
191
192
193
194
195
196
197
198
199
200
201
202
203
204
205
206
207
208
209
210
211
212
213
214
215
216
217
218
219
220
221
222
    my %lineHash = ();
    my %lineList = ();

    foreach my $file ("$SYSETCDIR/passwd","$SYSETCDIR/group",
		      "$SYSETCDIR/shadow","$SYSETCDIR/gshadow",
		      "$ETCDIR/passwd","$ETCDIR/group",
		      "$ETCDIR/shadow","$ETCDIR/gshadow") {
	open(FD,$file)
	    or die "open($file): $!";
	my $i = 0;
	my $lineCounter = 1;
	while (my $line = <FD>) {
	    chomp($line);

	    # store the line in the list
	    if (!defined($lineList{$file})) {
		$lineList{$file} = [];
	    }
	    $lineList{$file}->[$i] = $line;

	    # fill the hash for fast lookups, and place the array idx of the
	    # element in the lineList as the hash value so that we can undef
	    # it if deleting it, while still preserving the line orderings in
	    # the original files.  whoo!
	    if ($line ne '' && $line =~ /^([^:]+):.*$/) {
		if (!defined($lineHash{$file})) {
		    $lineHash{$file} = {};
		}
		$lineHash{$file}->{$1} = $i++;
	    }
	    else {
		print STDERR "malformed line $lineCounter in $file, ignoring!\n";
	    }
	    ++$lineCounter;
	}
    }

223
224
225
    print Dumper(%lineHash) . "\n\n\n"
	if ($debug);

226
227
    # remove emulab groups first (save a bit of work):
    while (my ($group,$gid) = each(%GDB)) {
228
229
	print "DEBUG: $group/$gid\n"
	    if ($debug);
230
231
232
233
234
	foreach my $file ("$SYSETCDIR/group","$SYSETCDIR/gshadow") {
	    if (defined($lineHash{$file}->{$group})) {
		# undef its line
		$lineList{$file}->[$lineHash{$file}->{$group}] = undef;
		delete $lineHash{$file}->{$group};
235
236
		print "DEBUG: deleted group $group from $file\n"
		    if ($debug);
237
238
239
240
241
242
243
244
245
246
247
248
	    }
	}
    }

    # now remove emulab users from users files, AND from the group list
    # in any groups :-)
    while (my ($user,$uid) = each(%PDB)) {
	foreach my $file ("$SYSETCDIR/passwd","$SYSETCDIR/shadow") {
	    if (defined($lineHash{$file}->{$user})) {
		# undef its line
		$lineList{$file}->[$lineHash{$file}->{$user}] = undef;
		delete $lineHash{$file}->{$user};
249
250
		print "DEBUG: deleted user  $user from $file\n"
		    if ($debug);
251
252
253
254
255
256
257
258
259
260
261
262
263
264
265
266
267
268
269
270
271
272
273
274
275
276
277
278
279
280
281
282
283
	    }
	}

	# this is indeed a lot of extra text processing, but whatever
	foreach my $file ("$SYSETCDIR/group","$SYSETCDIR/gshadow") {
	    foreach my $group (keys(%{$lineHash{$file}})) {
		my $groupLine = $lineList{$file}->[$lineHash{$file}->{$group}];
		# grab the fields
		# split using -1 to make sure our empty trailing fields are
		# added!
		my @elms = split(/\s*:\s*/,$groupLine,-1);
		# grab the user list
		my @ulist = split(/\s*,\s*/,$elms[scalar(@elms)-1]);
		# build a new list
		my @newulist = ();
		my $j = 0;
		my $k = 0;
		for ($j = 0; $j < scalar(@ulist); ++$j) {
		    # only add to the new user list if it's not the user we're
		    # removing.
		    my $suser = $ulist[$j];
		    if ($suser ne $user) {
			$newulist[$k++] = $suser;
		    }
		}
		# rebuild the user list
		$elms[scalar(@elms)-1] = join(',',@newulist);
		# rebuild the line from the fields
		$groupLine = join(':',@elms);
		# stick the line back into the "file"
		$lineList{$file}->[$lineHash{$file}->{$group}] = $groupLine;
	    }
	}
284
    }
285
286
287
288
289
290
291
292
293
294

    # now, merge current files into masters.
    foreach my $pairRef (["$SYSETCDIR/passwd","$ETCDIR/passwd"],
			 ["$SYSETCDIR/group","$ETCDIR/group"],
			 ["$SYSETCDIR/shadow","$ETCDIR/shadow"],
			 ["$SYSETCDIR/gshadow","$ETCDIR/gshadow"]) {
	my ($real,$master) = @$pairRef;

	foreach my $ent (keys(%{$lineHash{$real}})) {
	    # skip root and toor!
Ryan Jackson's avatar
Ryan Jackson committed
295
	    next
296
297
298
299
300
301
		if ($ent eq 'root' || $ent eq 'toor');

	    # push new entities into master
	    if (!defined($lineHash{$master}->{$ent})) {
		# append new "line"
		$lineHash{$master}->{$ent} = scalar(@{$lineList{$master}});
Ryan Jackson's avatar
Ryan Jackson committed
302
		$lineList{$master}->[$lineHash{$master}->{$ent}] =
303
		    $lineList{$real}->[$lineHash{$real}->{$ent}];
304
305
		print "DEBUG: adding $ent to $master\n"
		    if ($debug);
306
307
	    }
	    # or replace modified entities
Ryan Jackson's avatar
Ryan Jackson committed
308
	    elsif ($lineList{$real}->[$lineHash{$real}->{$ent}]
309
		   ne $lineList{$master}->[$lineHash{$master}->{$ent}]) {
Ryan Jackson's avatar
Ryan Jackson committed
310
		$lineList{$master}->[$lineHash{$master}->{$ent}] =
311
		    $lineList{$real}->[$lineHash{$real}->{$ent}];
312
313
		print "DEBUG: updating $ent in $master\n"
		    if ($debug);
314
315
316
317
318
319
320
321
322
323
324
325
326
327
328
	    }
	}

	# now remove stale lines from the master
	my @todelete = ();
	foreach my $ent (keys(%{$lineHash{$master}})) {
	    if (!defined($lineHash{$real}->{$ent})) {
		# undef its line
		$lineList{$master}->[$lineHash{$master}->{$ent}] = undef;
		push @todelete, $ent;
	    }
	}
	foreach my $delent (@todelete) {
	    delete $lineHash{$master}->{$delent};
	}
329
    }
330

Ryan Jackson's avatar
Ryan Jackson committed
331
    # now write the masters to .new files so we can diff, do the diff for
332
333
334
335
336
337
338
339
340
341
342
343
344
345
346
347
348
349
350
351
352
353
    # files that are world-readable, then mv into place over the masters.
    my %modes = ( "$ETCDIR/passwd" => 0644,"$ETCDIR/group" => 0644,
		  "$ETCDIR/shadow" => 0600,"$ETCDIR/gshadow" => 0600 );
    foreach my $file (keys(%modes)) {
	sysopen(FD,"${file}.new",O_CREAT | O_WRONLY | O_TRUNC,$modes{$file})
	    # safe to die here cause we haven't moved any .new files into
	    # place
	    or die "sysopen(${file}.new): $!";
	for (my $i = 0; $i < scalar(@{$lineList{$file}}); ++$i) {
	    # remember, some lines may be undef cause we deleted them
	    if (defined($lineList{$file}->[$i])) {
		print FD $lineList{$file}->[$i] . "\n";
	    }
	}
	close(FD);
    }
    foreach my $file (keys(%modes)) {
	my $retval;
	if ($modes{$file} == 0644) {
	    print STDERR "Running 'diff -u $file ${file}.new'\n";
	    $retval = system("diff -u $file ${file}.new");
	    if ($retval) {
354
355
356
357
358
359
360
361
362
363
364
		if ($updatemasterpasswdfiles) {
		    print STDERR "Files ${file}.new and $file differ; updating $file.\n";
		    system("mv ${file}.new $file");
		}
		else {
		    print STDERR "Files ${file}.new and $file differ, but I was told not to update the master files!.\n";
		    system("rm -f ${file}.new");
		}
	    }
	    else {
		system("rm -f ${file}.new");
365
366
367
368
369
370
	    }
	}
	else {
	    print STDERR "Running 'diff -q -u $file ${file}.new'\n";
	    $retval = system("diff -q -u $file ${file}.new");
	    if ($retval) {
371
372
373
374
375
376
377
378
379
380
381
		if ($updatemasterpasswdfiles) {
		    print STDERR "Files ${file}.new and $file differ, but we can't show the changes!  Updating $file anyway!\n";
		    system("mv ${file}.new $file");
		}
		else {
		    print STDERR "Files ${file}.new and $file differ, but we can't show the changes, and I was told not to update the master files!\n";
		    system("rm -f ${file}.new");
		}
	    }
	    else {
		system("rm -f ${file}.new");
382
383
384
385
386
387
388
	    }
	}
    }

    dbmclose(%PDB);
    dbmclose(%GDB);

389
390
391
392
393
394
395
396
397
398
399
400
401
402
403
404
    #
    # Splat the new files into place, doh
    #
    unlink @LOCKFILES;

    printf STDOUT "Resetting passwd and group files\n";
    if (system("$CP -f $TMGROUP $TMPASSWD /etc") != 0) {
        print STDERR "Could not copy default group file into place: $!\n";
        return -1;
    }

    if (system("$CP -f $TMSHADOW $TMGSHADOW /etc") != 0) {
        print STDERR "Could not copy default passwd file into place: $!\n";
        return -1;
    }

405
406
407
408
409
410
411
    return 0;
}

#
# Generate and return an ifconfig line that is approriate for putting
# into a shell script (invoked at bootup).
#
412
sub os_ifconfig_line($$$$$$$$;$$$)
413
{
414
    my ($iface, $inet, $mask, $speed, $duplex, $aliases, $iface_type, $lan,
415
	$settings, $rtabid, $cookie) = @_;
416
417
418
419
420
    my ($miirest, $miisleep, $miisetspd, $media);
    my ($uplines, $downlines);

    #
    # Special handling for new style interfaces (which have settings).
Ryan Jackson's avatar
Ryan Jackson committed
421
    # This should all move into per-type modules at some point.
422
    #
Kirk Webb's avatar
   
Kirk Webb committed
423
    if ($iface_type eq "ath" && defined($settings)) {
424

Kirk Webb's avatar
   
Kirk Webb committed
425
426
427
428
429
        # Get a handle on the "VAP" interface we will create when
        # setting up this interface.
        my ($ifnum) = $iface =~ /wifi(\d+)/;
        my $athiface = "ath" . $ifnum;

430
431
432
433
434
	#
	# Setting the protocol is special and appears to be card specific.
	# How stupid is that!
	#
	my $protocol = $settings->{"protocol"};
Kirk Webb's avatar
   
Kirk Webb committed
435
        my $privcmd = "/usr/local/sbin/iwpriv $athiface mode ";
Kirk Webb's avatar
   
Kirk Webb committed
436
437
438
439
440
441
442
443
444
445
446
447
448
449
450

        SWITCH1: for ($protocol) {
          /^80211a$/ && do {
              $privcmd .= "1";
              last SWITCH1;
          };
          /^80211b$/ && do {
              $privcmd .= "2";
              last SWITCH1;
          };
          /^80211g$/ && do {
              $privcmd .= "3";
              last SWITCH1;
          };
        }
Ryan Jackson's avatar
Ryan Jackson committed
451

452
453
454
455
	#
	# At the moment, we expect just the various flavors of 80211, and
	# we treat them all the same, configuring with iwconfig and iwpriv.
	#
Kirk Webb's avatar
   
Kirk Webb committed
456
457
458
	my $iwcmd = "/usr/local/sbin/iwconfig $athiface ";
        my $wlccmd = "/usr/local/bin/wlanconfig $athiface create ".
            "wlandev $iface ";
459
460
461
462
463
464
465
466
467
468
469
470
471
472
473
474
475
476
477
478
479
480
481
482
483
484
485
486
487
488
489

	#
	# We demand to be given an ssid.
	#
	if (!exists($settings->{"ssid"})) {
	    warn("*** WARNING: No SSID provided for $iface!\n");
	    return undef;
	}
	$iwcmd .= "essid ". $settings->{"ssid"};

	# If we do not get a channel, pick one.
	if (exists($settings->{"channel"})) {
	    $iwcmd .= " channel " . $settings->{"channel"};
	}
	else {
	    $iwcmd .= " channel 3";
	}

	# txpower and rate default to auto if not specified.
	if (exists($settings->{"rate"})) {
	    $iwcmd .= " rate " . $settings->{"rate"};
	}
	else {
	    $iwcmd .= " rate auto";
	}
	if (exists($settings->{"txpower"})) {
	    $iwcmd .= " txpower " . $settings->{"txpower"};
	}
	else {
	    $iwcmd .= " txpower auto";
	}
Ryan Jackson's avatar
Ryan Jackson committed
490
	# Allow this too.
491
492
493
494
495
496
497
498
499
500
	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'};
501
502
503
504
	}

	#
	# We demand to be told if we are the master or a peon.
505
506
507
508
509
510
	# 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.
Ryan Jackson's avatar
Ryan Jackson committed
511
	#
512
513
	# This needs to be last for some reason.
	#
514
515
516
	if (exists($settings->{'accesspoint'})) {
	    my $accesspoint = $settings->{"accesspoint"};
	    my $accesspointwdots;
Ryan Jackson's avatar
Ryan Jackson committed
517

518
519
520
521
522
523
524
525
526
527
528
529
530
531
	    # 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;
	    }
Ryan Jackson's avatar
Ryan Jackson committed
532

533
534
535
536
537
538
539
540
	    if (libsetup::findiface($accesspoint) eq $iface) {
		$wlccmd .= " wlanmode ap";
		$iwcmd .= " mode Master";
	    }
	    else {
		$wlccmd .= " wlanmode sta";
		$iwcmd .= " mode Managed ap $accesspointwdots";
	    }
541
	}
542
543
544
545
546
547
548
549
550
	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";
	    }
Ryan Jackson's avatar
Ryan Jackson committed
551
552
	    elsif ($settings->{'mode'} =~ /ap/i
		   || $settings->{'mode'} =~ /access[\s\-]*point/i
553
554
555
556
		   || $settings->{'mode'} =~ /master/i) {
		$wlccmd .= " wlanmode ap";
		$iwcmd .= " mode Master";
	    }
Ryan Jackson's avatar
Ryan Jackson committed
557
	    elsif ($settings->{'mode'} =~ /sta/i
558
559
560
561
562
563
564
565
		   || $settings->{'mode'} =~ /managed/i) {
		$wlccmd .= " wlanmode sta";
		$iwcmd .= " mode Managed ap any";
	    }
	    else {
		warn("*** WARNING: Invalid mode provided for $iface!\n");
		return undef;
	    }
566
567
	}
	else {
568
	    warn("*** WARNING: No mode implied for $iface!\n");
569
570
	    return undef;
	}
571

Kirk Webb's avatar
   
Kirk Webb committed
572
        $uplines   = $wlccmd . "\n";
573
	$uplines  .= $privcmd . "\n";
Kirk Webb's avatar
   
Kirk Webb committed
574
	$uplines  .= $iwcmd . "\n";
Kirk Webb's avatar
   
Kirk Webb committed
575
576
	$uplines  .= sprintf($IFCONFIG, $athiface, $inet, $mask) . "\n";
	$downlines  = "$IFCONFIGBIN $athiface down\n";
577
	$downlines .= "$WLANCONFIG $athiface destroy\n";
Kirk Webb's avatar
   
Kirk Webb committed
578
	$downlines .= "$IFCONFIGBIN $iface down\n";
579
580
	return ($uplines, $downlines);
    }
581

Kirk Webb's avatar
   
Kirk Webb committed
582
583
584
585
586
    #
    # GNU Radio network interface on the flex900 daugherboard
    #
    if ($iface_type eq "flex900" && defined($settings)) {

Ryan Jackson's avatar
Ryan Jackson committed
587
        my $tuncmd =
Kirk Webb's avatar
   
Kirk Webb committed
588
589
590
591
592
593
594
595
596
597
598
            "/bin/env PYTHONPATH=/usr/local/lib/python2.4/site-packages ".
            "$BINDIR/tunnel.py";

        if (!exists($settings->{"mac"})) {
            warn("*** WARNING: No mac address provided for gnuradio ".
                 "interface!\n");
            return undef;
        }

        my $mac = $settings->{"mac"};

Ryan Jackson's avatar
Ryan Jackson committed
599
        if (!exists($settings->{"protocol"}) ||
Kirk Webb's avatar
   
Kirk Webb committed
600
601
602
603
604
605
606
607
608
609
610
611
612
613
614
615
616
617
618
619
620
621
            $settings->{"protocol"} ne "flex900") {
            warn("*** WARNING: Unknown gnuradio protocol specified!\n");
            return undef;
        }

        if (!exists($settings->{"frequency"})) {
            warn("*** WARNING: No frequency specified for gnuradio ".
                 "interface!\n");
            return undef;
        }

        my $frequency = $settings->{"frequency"};
        $tuncmd .= " -f $frequency";

        if (!exists($settings->{"rate"})) {
            warn("*** WARNING: No rate specified for gnuradio interface!\n");
            return undef;
        }

        my $rate = $settings->{"rate"};
        $tuncmd .= " -r $rate";

622
623
624
625
626
627
628
	if (exists($settings->{'carrierthresh'})) {
	    $tuncmd .= " -c " . $settings->{'carrierthresh'};
	}
	if (exists($settings->{'rxgain'})) {
	    $tuncmd .= " --rx-gain=" . $settings->{'rxgain'};
	}

Kirk Webb's avatar
   
Kirk Webb committed
629
630
631
632
633
634
635
636
        $uplines = $tuncmd . " > /dev/null 2>&1 &\n";
        $uplines .= "sleep 5\n";
        $uplines .= "$IFCONFIGBIN $iface hw ether $mac\n";
        $uplines .= sprintf($IFCONFIG, $iface, $inet, $mask) . "\n";
        $downlines = "$IFCONFIGBIN $iface down";
        return ($uplines, $downlines);
    }

637
    #
638
    # Only do this stuff if we have a physical interface, otherwise it doesn't
Ryan Jackson's avatar
Ryan Jackson committed
639
    # mean anything.  We need this for virtnodes whose networks must be
640
    # config'd from inside the container, vm, whatever.
641
    #
642
643
644
645
    if ($iface_type ne 'veth') {
        #
        # Need to check units on the speed. Just in case.
        #
646
647
648
649
	if (!defined($speed)) {
	    warn("*** No speed defined, default to 100Mbps\n");
	    $speed = 100;
	}
650
651
652
653
654
655
656
657
658
659
660
	if ($speed =~ /(\d*)([A-Za-z]*)/) {
	    if ($2 eq "Mbps") {
		$speed = $1;
	    }
	    elsif ($2 eq "Kbps") {
		$speed = $1 / 1000;
	    }
	    else {
		warn("*** Bad speed units $2 in ifconfig, default to 100Mbps\n");
		$speed = 100;
	    }
Mike Hibler's avatar
Mike Hibler committed
661
662
663
664
	    if ($speed == 10000) {
		$media = $IFC_10000MBS;
	    }
	    elsif ($speed == 1000) {
665
666
667
668
669
670
671
672
		$media = $IFC_1000MBS;
	    }
	    elsif ($speed == 100) {
		$media = $IFC_100MBS;
	    }
	    elsif ($speed == 10) {
		$media = $IFC_10MBS;
	    }
673
674
675
676
	    elsif ($speed == 0) {
		warn("*** Speed was 0 in ifconfig, defaulting to auto negotiation (and Gb media)\n");
		$media = $IFC_1000MBS;
	    }
677
678
679
680
681
	    else {
		warn("*** Bad Speed $speed in ifconfig, default to 100Mbps\n");
		$speed = 100;
		$media = $IFC_100MBS;
	    }
682
	}
683
684
685
686
	if (!defined($duplex)) {
	    warn("*** No duplex defined, default to full\n");
	    $duplex = "full";
	}
687
688
	if ($duplex eq "full") {
	    $media = "$media-$IFC_FDUPLEX";
689
	}
690
691
	elsif ($duplex eq "half") {
	    $media = "$media-$IFC_HDUPLEX";
692
693
	}
	else {
694
695
696
	    warn("*** Bad duplex $duplex in ifconfig, default to full\n");
	    $duplex = "full";
	    $media = "$media-$IFC_FDUPLEX";
697
698
	}

699
        #
Mike Hibler's avatar
Mike Hibler committed
700
701
702
703
704
        # Linux is apparently changing from mii-tool to ethtool but some
	# drivers don't support the new interface (3c59x), some don't support
	# the old interface (e1000), and some (eepro100) support the new
	# interface just enough that they can report success but not actually
	# do anything. Sweet!
705
706
707
708
709
710
711
712
713
        #
	my $ethtool;
	if (-e "/sbin/ethtool") {
	    $ethtool = "/sbin/ethtool";
	} elsif (-e "/usr/sbin/ethtool") {
	    $ethtool = "/usr/sbin/ethtool";
	}
	if (defined($ethtool)) {
	    # this seems to work for returning an error on eepro100
Ryan Jackson's avatar
Ryan Jackson committed
714
	    $uplines =
715
		"if $ethtool $iface >/dev/null 2>&1; then\n    ";
716
717
718
719
720
721
722
723
724
725
726
727
	    #
	    # Special cases:
	    # - '0' means autoneg
	    # - '1000', aka gigabit, we *must* turn on autoneg--
	    #   it's part of the GbE protocol.
	    # - '10000', aka 10GE, don't try to set anything
	    #
	    if ($speed eq '0' || $speed eq '1000') {
		$uplines .= "  $ethtool -s $iface autoneg on\n    ";
	    }
	    elsif ($speed eq '10000') {
		$uplines .= "  true\n    ";
728
729
730
731
	    }
	    else {
		$uplines .=
		    "  $ethtool -s $iface autoneg off speed $speed duplex $duplex\n    " .
Mike Hibler's avatar
Mike Hibler committed
732
		    "  sleep 2 # needed due to likely bug in e100 driver on pc850s\n    ";
733
734
	    }
	    $uplines .= 
735
736
737
738
739
740
		"else\n    " .
		"  /sbin/mii-tool --force=$media $iface\n    " .
		"fi\n    ";
	} else {
	    $uplines = "/sbin/mii-tool --force=$media $iface\n    ";
	}
741
742
    }

743
744
745
746
    if ($inet eq "") {
	$uplines .= "$IFCONFIGBIN $iface up";
    }
    else {
747
748
749
	$uplines  .= sprintf($IFCONFIG, $iface, $inet, $mask);
	$downlines = "$IFCONFIGBIN $iface down";
    }
Ryan Jackson's avatar
Ryan Jackson committed
750

751
    return ($uplines, $downlines);
752
753
}

754
#
755
# Specialized function for configing virtual ethernet devices:
756
#
757
#	'veth'	one end of an etun device embedded in a vserver
758
759
760
761
#	'vlan'	802.1q tagged vlan devices
#	'alias'	IP aliases on physical interfaces
#
sub os_ifconfig_veth($$$$$;$$$$%)
762
{
763
764
765
766
    my ($iface, $inet, $mask, $id, $vmac,
	$rtabid, $encap, $vtag, $itype, $cookie) = @_;
    my ($uplines, $downlines);

767
768
    if ($itype !~ /^(alias|vlan|veth)$/) {
	warn("Unknown virtual interface type $itype\n");
769
770
771
	return "";
    }

772
773
774
775
776
777
778
779
780
781
782
783
784
785
786
787
788
789
790
791
792
793
794
795
796
797
798
799
800
801
802
803
804
805
806
807
808
809
810
    #
    # Veth.
    #
    # Veths for Linux vservers mean vtun devices.  One end is outside
    # the vserver and is bridged with other veths and peths as appropriate
    # to form the topology.  The other end goes in the vserver and is
    # configured with an IP address.  This final step is not done here
    # as the vserver must be running first.
    #
    # In the current configuration, there is configuration that takes
    # place both inside and outside the vserver.
    #
    # Inside:
    # The inside case (LINUXJAILED() == 1) just configures the IP info on
    # the interface.
    #
    # Outside:
    # The outside actions are much more involved as described above.
    # The VTAG identifies a bridge device "ebrN" to be used.
    # The RTABID identifies the namespace, but we don't care here.
    #
    # To create a etun pair you do:
    #    echo etun0,etun1 > /sys/module/etun/parameters/newif
    # To destroy do:
    #    echo etun0 > /sys/module/etun/parameters/delif
    #
    if ($itype eq "veth") {
	#
	# We are inside a Linux jail.
	# We configure the interface pretty much like normal.
	#
	if (LINUXJAILED()) {
	    if ($inet eq "") {
		$uplines .= "$IFCONFIGBIN $iface up";
	    }
	    else {
		$uplines  .= sprintf($IFCONFIG, $iface, $inet, $mask);
		$downlines = "$IFCONFIGBIN $iface down";
	    }
Ryan Jackson's avatar
Ryan Jackson committed
811

812
813
814
815
816
817
818
819
820
821
822
823
824
825
826
827
828
829
830
831
832
833
834
835
836
837
838
839
840
841
842
843
844
845
846
847
848
849
850
851
852
853
854
855
856
857
858
	    return ($uplines, $downlines);
	}

	#
	# Outside jail.
	# Create tunnels and bridge and plumb them all together.
	#
	my $brdev = "ebr$vtag";
	my $iniface = "veth$id";
	my $outiface = "peth$id";
	my $devdir = "/sys/module/etun/parameters";

	# UP
	$uplines = "";

	# modprobe (should be done already for cnet setup, but who cares)
	$uplines .= "modprobe etun\n";

	# make sure bridge device exists and is up
	$uplines .= "    $IFCONFIGBIN $brdev >/dev/null 2>&1 || {";
	$uplines .= "        $BRCTL addbr $brdev\n";
	$uplines .= "        $IFCONFIGBIN $brdev up\n";
	$uplines .= "    }\n";

	# create the tunnel device
	$uplines .= "    echo $outiface,$iniface > $devdir/newif || exit 1\n";

	# bring up outside IF, insert into bridge device
	$uplines .= "    $IFCONFIGBIN $outiface up || exit 2\n";
	$uplines .= "    $BRCTL addif $brdev $outiface || exit 3\n";

	# configure the MAC address for the inside device
	$uplines .= "    $IFCONFIGBIN $iniface hw ether $vmac || exit 4\n";

	# DOWN
	$downlines = "";

	# remove IF from bridge device, down it (remove bridge if empty?)
	$downlines .= "$BRCTL delif $brdev $outiface || exit 13\n";
	$downlines .= "    $IFCONFIGBIN $outiface down || exit 12\n";

	# destroy tunnel devices (this will fail if inside IF in vserver still)
	$downlines .= "    echo $iniface > $devdir/delif || exit 11\n";

	return ($uplines, $downlines);
    }

859
860
861
862
863
864
865
866
867
868
869
870
871
872
873
874
875
876
    #
    # IP aliases
    #
    if ($itype eq "alias") {
	my $aif = "$iface:$id";

	$uplines   = sprintf($IFCONFIG, $aif, $inet, $mask);
	$downlines = "$IFCONFIGBIN $aif down";

	return ($uplines, $downlines);
    }

    #
    # VLANs
    #   insmod 8021q (once only)
    #   vconfig set_name_type VLAN_PLUS_VID_NO_PAD (once only)
    #
    #	ifconfig eth0 up (should be done before we are ever called)
Ryan Jackson's avatar
Ryan Jackson committed
877
    #	vconfig add eth0 601
878
879
880
881
882
883
884
885
886
887
888
889
890
891
892
893
894
895
896
897
898
899
    #   ifconfig vlan601 inet ...
    #
    #   ifconfig vlan601 down
    #	vconfig rem vlan601
    #
    if ($itype eq "vlan") {
	if (!defined($vtag)) {
	    warn("No vtag in veth config\n");
	    return "";
	}

	# one time stuff
	if (!exists($cookie->{"vlan"})) {
	    $uplines  = "/sbin/insmod 8021q >/dev/null 2>&1\n    ";
	    $uplines .= "$VLANCONFIG set_name_type VLAN_PLUS_VID_NO_PAD\n    ";
	    $cookie->{"vlan"} = 1;
	}

	my $vdev = "vlan$vtag";

	$uplines   .= "$VLANCONFIG add $iface $vtag\n    ";
	$uplines   .= sprintf($IFCONFIG, $vdev, $inet, $mask);
900
901
902
	# configure the MAC address.
	$uplines   .= "\n    $IFCONFIGBIN $vdev hw ether $vmac"
	    if ($vmac);
903
904
905
906
907
	$downlines .= "$IFCONFIGBIN $vdev down\n    ";
	$downlines .= "$VLANCONFIG rem $vdev";
    }

    return ($uplines, $downlines);
908
909
}

910
911
912
913
914
915
916
917
918
919
920
921
922
923
924
925
926
927
928
929
930
931
#
# Compute the name of a virtual interface device based on the
# information in ifconfig hash (as returned by getifconfig).
#
sub os_viface_name($)
{
    my ($ifconfig) = @_;
    my $piface = $ifconfig->{"IFACE"};

    #
    # Physical interfaces use their own name
    #
    if (!$ifconfig->{"ISVIRT"}) {
	return $piface;
    }

    #
    # Otherwise we have a virtual interface: alias, veth, vlan.
    #
    # alias: There is an alias device, but not sure what it is good for
    #        so for now we just return the phys device.
    # vlan:  vlan<VTAG>
932
    # veth:  veth<ID>
933
934
935
936
937
938
    #
    my $itype = $ifconfig->{"ITYPE"};
    if ($itype eq "alias") {
	return $piface;
    } elsif ($itype eq "vlan") {
	return $itype . $ifconfig->{"VTAG"};
939
940
    } elsif ($itype eq "veth") {
	return $itype . $ifconfig->{"ID"};
941
942
943
944
945
946
    }

    warn("Linux does not support virtual interface type '$itype'\n");
    return undef;
}

947
948
949
950
#
# Generate and return an string that is approriate for putting
# into /etc/hosts.
#
951
sub os_etchosts_line($$$)
952
{
953
    my ($name, $ip, $aliases) = @_;
Ryan Jackson's avatar
Ryan Jackson committed
954

955
    return sprintf("%s\t%s %s", $ip, $name, $aliases);
956
957
958
959
}

#
# Add a new group
Ryan Jackson's avatar
Ryan Jackson committed
960
#
961
sub os_groupadd($$)
962
{
963
964
965
966
967
968
969
970
971
972
973
974
975
976
977
    if (INVZVM()) {
	my $tries  = 10;
	my $result = 1;
	while ($tries-- > 0) {
	    $result = os_groupadd_real(@_);
	    last
		if (!$result || !$tries);

	    warn("$GROUPADD returned $result ... trying again in a bit\n");
	    sleep(10);
	}
	return $result;
    }
    else {
	return os_groupadd_real(@_);
978
979
980
981
    }
}

sub os_groupadd_real($$)
982
{
983
    my ($group, $gid) = @_;
984
985
986
987

    return system("$GROUPADD -g $gid $group");
}

988
989
#
# Delete an old group
Ryan Jackson's avatar
Ryan Jackson committed
990
#
991
992
993
994
995
996
997
sub os_groupdel($)
{
    my($group) = @_;

    return system("$GROUPDEL $group");
}

998
999
#
# Remove a user account.
Ryan Jackson's avatar
Ryan Jackson committed
1000
#
For faster browsing, not all history is shown. View entire blame