liblocsetup.pm 68.5 KB
Newer Older
1
#!/usr/bin/perl -wT
Leigh B. Stoller's avatar
Leigh B. Stoller committed
2
#
3
# Copyright (c) 2000-2015 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
	 os_getarpinfo os_createarpentry os_removearpentry
44
	 os_getstaticarp os_setstaticarp os_ismounted os_unmount os_mount
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
$MOUNT		= "/bin/mount";
92
$TMPASSWD	= "$ETCDIR/passwd";
93
94
95
$TMGROUP	= "$ETCDIR/group";
$TMSHADOW       = "$ETCDIR/shadow";
$TMGSHADOW      = "$ETCDIR/gshadow";
96
97
$SFSSD		= "/usr/local/sbin/sfssd";
$SFSCD		= "/usr/local/sbin/sfscd";
Mike Hibler's avatar
Mike Hibler committed
98
$RPMCMD		= "/bin/rpm";
99
$HOSTSFILE	= "/etc/hosts";
100
$WGET		= "/usr/bin/wget";
Leigh B Stoller's avatar
Leigh B Stoller committed
101
$CHMOD		= "/bin/chmod";
102
$ARP		= "/sbin/arp";
103
104
105
106

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

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

143
144
my $debug = 0;

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

156
157
158
    #
    # 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
159
    # files and their shadow counterparts, remove any emulab u/gids from the
160
161
162
163
164
    # 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 (!).
    #
165
166
167
168
169
170
171
172
    # 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.
    #
173
174
175
    my %PDB;
    my %GDB;

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

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

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
223
    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;
	}
    }

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

227
228
    # remove emulab groups first (save a bit of work):
    while (my ($group,$gid) = each(%GDB)) {
229
230
	print "DEBUG: $group/$gid\n"
	    if ($debug);
231
232
233
234
235
	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};
236
237
		print "DEBUG: deleted group $group from $file\n"
		    if ($debug);
238
239
240
241
242
243
244
245
246
247
248
249
	    }
	}
    }

    # 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};
250
251
		print "DEBUG: deleted user  $user from $file\n"
		    if ($debug);
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
284
	    }
	}

	# 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;
	    }
	}
285
    }
286
287
288
289
290
291
292
293
294
295

    # 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
296
	    next
297
298
299
300
301
302
		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
303
		$lineList{$master}->[$lineHash{$master}->{$ent}] =
304
		    $lineList{$real}->[$lineHash{$real}->{$ent}];
305
306
		print "DEBUG: adding $ent to $master\n"
		    if ($debug);
307
308
	    }
	    # or replace modified entities
Ryan Jackson's avatar
Ryan Jackson committed
309
	    elsif ($lineList{$real}->[$lineHash{$real}->{$ent}]
310
		   ne $lineList{$master}->[$lineHash{$master}->{$ent}]) {
Ryan Jackson's avatar
Ryan Jackson committed
311
		$lineList{$master}->[$lineHash{$master}->{$ent}] =
312
		    $lineList{$real}->[$lineHash{$real}->{$ent}];
313
314
		print "DEBUG: updating $ent in $master\n"
		    if ($debug);
315
316
317
318
319
320
321
322
323
324
325
326
327
328
329
	    }
	}

	# 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};
	}
330
    }
331

Ryan Jackson's avatar
Ryan Jackson committed
332
    # now write the masters to .new files so we can diff, do the diff for
333
334
335
336
337
338
339
340
341
342
343
344
345
346
347
348
349
350
351
352
353
354
    # 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) {
355
356
357
358
359
360
361
362
363
364
365
		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");
366
367
368
369
370
371
	    }
	}
	else {
	    print STDERR "Running 'diff -q -u $file ${file}.new'\n";
	    $retval = system("diff -q -u $file ${file}.new");
	    if ($retval) {
372
373
374
375
376
377
378
379
380
381
382
		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");
383
384
385
386
387
388
389
	    }
	}
    }

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

390
391
392
393
394
395
396
397
398
399
400
401
402
403
404
405
    #
    # 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;
    }

406
407
408
409
410
411
412
    return 0;
}

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

420
421
422
423
424
425
426
427
428
429
430
    #
    # Inside a container, we get a regular interface, but tmcd sets the
    # type=gre so we know to set the MTU properly. This number seems to
    # work for both openvz and xen containers. 
    #
    if ($iface_type eq "gre" && GENVNODE()) {
	$uplines   = "$IFCONFIGBIN $iface $inet netmask $mask mtu 1450 up";
	$downlines = "$IFCONFIGBIN $iface down";
	return ($uplines, $downlines);
    }

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

Kirk Webb's avatar
   
Kirk Webb committed
437
438
439
440
441
        # Get a handle on the "VAP" interface we will create when
        # setting up this interface.
        my ($ifnum) = $iface =~ /wifi(\d+)/;
        my $athiface = "ath" . $ifnum;

442
443
444
445
446
	#
	# 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
447
        my $privcmd = "/usr/local/sbin/iwpriv $athiface mode ";
Kirk Webb's avatar
   
Kirk Webb committed
448
449
450
451
452
453
454
455
456
457
458
459
460
461
462

        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
463

464
465
466
467
	#
	# 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
468
469
470
	my $iwcmd = "/usr/local/sbin/iwconfig $athiface ";
        my $wlccmd = "/usr/local/bin/wlanconfig $athiface create ".
            "wlandev $iface ";
471
472
473
474
475
476
477
478
479
480
481
482
483
484
485
486
487
488
489
490
491
492
493
494
495
496
497
498
499
500
501

	#
	# 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
502
	# Allow this too.
503
504
505
506
507
508
509
510
511
512
	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'};
513
514
515
516
	}

	#
	# We demand to be told if we are the master or a peon.
517
518
519
520
521
522
	# 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
523
	#
524
525
	# This needs to be last for some reason.
	#
526
527
528
	if (exists($settings->{'accesspoint'})) {
	    my $accesspoint = $settings->{"accesspoint"};
	    my $accesspointwdots;
Ryan Jackson's avatar
Ryan Jackson committed
529

530
531
532
533
534
535
536
537
538
539
540
541
542
543
	    # 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
544

545
546
547
548
549
550
551
552
	    if (libsetup::findiface($accesspoint) eq $iface) {
		$wlccmd .= " wlanmode ap";
		$iwcmd .= " mode Master";
	    }
	    else {
		$wlccmd .= " wlanmode sta";
		$iwcmd .= " mode Managed ap $accesspointwdots";
	    }
553
	}
554
555
556
557
558
559
560
561
562
	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
563
564
	    elsif ($settings->{'mode'} =~ /ap/i
		   || $settings->{'mode'} =~ /access[\s\-]*point/i
565
566
567
568
		   || $settings->{'mode'} =~ /master/i) {
		$wlccmd .= " wlanmode ap";
		$iwcmd .= " mode Master";
	    }
Ryan Jackson's avatar
Ryan Jackson committed
569
	    elsif ($settings->{'mode'} =~ /sta/i
570
571
572
573
574
575
576
577
		   || $settings->{'mode'} =~ /managed/i) {
		$wlccmd .= " wlanmode sta";
		$iwcmd .= " mode Managed ap any";
	    }
	    else {
		warn("*** WARNING: Invalid mode provided for $iface!\n");
		return undef;
	    }
578
579
	}
	else {
580
	    warn("*** WARNING: No mode implied for $iface!\n");
581
582
	    return undef;
	}
583

Kirk Webb's avatar
   
Kirk Webb committed
584
        $uplines   = $wlccmd . "\n";
585
	$uplines  .= $privcmd . "\n";
Kirk Webb's avatar
   
Kirk Webb committed
586
	$uplines  .= $iwcmd . "\n";
Kirk Webb's avatar
   
Kirk Webb committed
587
588
	$uplines  .= sprintf($IFCONFIG, $athiface, $inet, $mask) . "\n";
	$downlines  = "$IFCONFIGBIN $athiface down\n";
589
	$downlines .= "$WLANCONFIG $athiface destroy\n";
Kirk Webb's avatar
   
Kirk Webb committed
590
	$downlines .= "$IFCONFIGBIN $iface down\n";
591
592
	return ($uplines, $downlines);
    }
593

Kirk Webb's avatar
   
Kirk Webb committed
594
595
596
597
598
    #
    # GNU Radio network interface on the flex900 daugherboard
    #
    if ($iface_type eq "flex900" && defined($settings)) {

Ryan Jackson's avatar
Ryan Jackson committed
599
        my $tuncmd =
Kirk Webb's avatar
   
Kirk Webb committed
600
601
602
603
604
605
606
607
608
609
610
            "/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
611
        if (!exists($settings->{"protocol"}) ||
Kirk Webb's avatar
   
Kirk Webb committed
612
613
614
615
616
617
618
619
620
621
622
623
624
625
626
627
628
629
630
631
632
633
            $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";

634
635
636
637
638
639
640
	if (exists($settings->{'carrierthresh'})) {
	    $tuncmd .= " -c " . $settings->{'carrierthresh'};
	}
	if (exists($settings->{'rxgain'})) {
	    $tuncmd .= " --rx-gain=" . $settings->{'rxgain'};
	}

Kirk Webb's avatar
   
Kirk Webb committed
641
642
643
644
645
646
647
648
        $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);
    }

649
    #
650
    # Only do this stuff if we have a physical interface, otherwise it doesn't
Ryan Jackson's avatar
Ryan Jackson committed
651
    # mean anything.  We need this for virtnodes whose networks must be
652
    # config'd from inside the container, vm, whatever.
653
    #
654
655
656
657
    if ($iface_type ne 'veth') {
        #
        # Need to check units on the speed. Just in case.
        #
658
659
660
661
	if (!defined($speed)) {
	    warn("*** No speed defined, default to 100Mbps\n");
	    $speed = 100;
	}
662
663
664
665
666
667
668
669
670
671
672
	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
673
674
675
676
	    if ($speed == 10000) {
		$media = $IFC_10000MBS;
	    }
	    elsif ($speed == 1000) {
677
678
679
680
681
682
683
684
		$media = $IFC_1000MBS;
	    }
	    elsif ($speed == 100) {
		$media = $IFC_100MBS;
	    }
	    elsif ($speed == 10) {
		$media = $IFC_10MBS;
	    }
685
686
687
688
	    elsif ($speed == 0) {
		warn("*** Speed was 0 in ifconfig, defaulting to auto negotiation (and Gb media)\n");
		$media = $IFC_1000MBS;
	    }
689
690
691
692
693
	    else {
		warn("*** Bad Speed $speed in ifconfig, default to 100Mbps\n");
		$speed = 100;
		$media = $IFC_100MBS;
	    }
694
	}
695
696
697
698
	if (!defined($duplex)) {
	    warn("*** No duplex defined, default to full\n");
	    $duplex = "full";
	}
699
700
	if ($duplex eq "full") {
	    $media = "$media-$IFC_FDUPLEX";
701
	}
702
703
	elsif ($duplex eq "half") {
	    $media = "$media-$IFC_HDUPLEX";
704
705
	}
	else {
706
707
708
	    warn("*** Bad duplex $duplex in ifconfig, default to full\n");
	    $duplex = "full";
	    $media = "$media-$IFC_FDUPLEX";
709
710
	}

711
        #
Mike Hibler's avatar
Mike Hibler committed
712
713
714
715
716
        # 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!
717
718
719
720
721
722
723
724
725
        #
	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
726
	    $uplines =
727
		"if $ethtool $iface >/dev/null 2>&1; then\n    ";
728
729
730
731
732
733
734
735
736
737
738
739
	    #
	    # 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    ";
740
741
742
743
	    }
	    else {
		$uplines .=
		    "  $ethtool -s $iface autoneg off speed $speed duplex $duplex\n    " .
Mike Hibler's avatar
Mike Hibler committed
744
		    "  sleep 2 # needed due to likely bug in e100 driver on pc850s\n    ";
745
746
	    }
	    $uplines .= 
747
748
749
750
751
752
		"else\n    " .
		"  /sbin/mii-tool --force=$media $iface\n    " .
		"fi\n    ";
	} else {
	    $uplines = "/sbin/mii-tool --force=$media $iface\n    ";
	}
753
754
    }

755
756
757
758
    if ($inet eq "") {
	$uplines .= "$IFCONFIGBIN $iface up";
    }
    else {
759
760
	$uplines  .= sprintf($IFCONFIG, $iface, $inet, $mask);
	$downlines = "$IFCONFIGBIN $iface down";
761
762
763
764
765
766

	#
	# XXX make sure we don't do reverse path filtering on the interface;
	# our current route generator which will produce asymmetric routes.
	#
	$uplines .= "\n    /sbin/sysctl net.ipv4.conf.$iface.rp_filter=0";
767
    }
Ryan Jackson's avatar
Ryan Jackson committed
768

769
    return ($uplines, $downlines);
770
771
}

772
#
773
# Specialized function for configing virtual ethernet devices:
774
#
775
#	'veth'	one end of an etun device embedded in a vserver
776
777
778
779
#	'vlan'	802.1q tagged vlan devices
#	'alias'	IP aliases on physical interfaces
#
sub os_ifconfig_veth($$$$$;$$$$%)
780
{
781
782
783
784
    my ($iface, $inet, $mask, $id, $vmac,
	$rtabid, $encap, $vtag, $itype, $cookie) = @_;
    my ($uplines, $downlines);

785
786
    if ($itype !~ /^(alias|vlan|veth)$/) {
	warn("Unknown virtual interface type $itype\n");
787
788
789
	return "";
    }

790
791
792
793
794
795
796
797
798
799
800
801
802
803
804
805
806
807
808
809
810
811
812
813
814
815
816
817
818
819
820
821
822
823
824
825
826
827
828
    #
    # 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
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
859
860
861
862
863
864
865
866
867
868
869
870
871
872
873
874
875
876
	    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);
    }

877
878
879
880
881
882
883
884
885
886
887
888
889
890
891
892
893
894
    #
    # 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
895
    #	vconfig add eth0 601
896
897
898
899
900
901
902
903
904
905
906
907
908
909
910
911
912
913
914
915
916
917
    #   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);
918
919
920
	# configure the MAC address.
	$uplines   .= "\n    $IFCONFIGBIN $vdev hw ether $vmac"
	    if ($vmac);
921
922
923
924
925
926
	#
	# XXX make sure we don't do reverse path filtering on the interface;
	# our current route generator which will produce asymmetric routes.
	#
	$uplines .= "\n    /sbin/sysctl net.ipv4.conf.$vdev.rp_filter=0";

927
928
929
930
931
	$downlines .= "$IFCONFIGBIN $vdev down\n    ";
	$downlines .= "$VLANCONFIG rem $vdev";
    }

    return ($uplines, $downlines);
932
933
}

934
935
936
937
938
939
940
941
942
943
944
945
946
947
948
949
950
951
952
953
954
955
#
# 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>
956
    # veth:  veth<ID>
957
958
959
960
961
962
    #
    my $itype = $ifconfig->{"ITYPE"};
    if ($itype eq "alias") {
	return $piface;
    } elsif ($itype eq "vlan") {
	return $itype . $ifconfig->{"VTAG"};
963
964
    } elsif ($itype eq "veth") {
	return $itype . $ifconfig->{"ID"};
965
966
967
968
969
970
    }

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

971
972
973
974
#
# Generate and return an string that is approriate for putting
# into /etc/hosts.
#
975
sub os_etchosts_line($$$)
976
{
977
    my ($name, $ip, $aliases) = @_;
Ryan Jackson's avatar
Ryan Jackson committed
978

979
    return sprintf("%s\t%s %s", $ip, $name, $aliases);
980
981
982
983
}

#
# Add a new group
Ryan Jackson's avatar
Ryan Jackson committed
984
#
985
sub os_groupadd($$)
986
{
987
988
989
990
991
992
993
994
995
996
997
998
999
1000
    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 {
For faster browsing, not all history is shown. View entire blame