source: fedkit/gateway_lib.pm @ c0a8738

axis_examplecompt_changesinfo-opsversion-3.01version-3.02
Last change on this file since c0a8738 was 8d4e4fb, checked in by Ted Faber <faber@…>, 14 years ago

Add support for parameters via files in a standard emulab location

Also moved combo_active.pl to combo.pl and added support for passive gateways.

  • Property mode set to 100644
File size: 8.2 KB
Line 
1#!/usr/bin/perl
2
3package gateway_lib;
4
5# Package stuff to keep the caller's namespace clean, but allow additions if
6# they need it.
7require Exporter;
8@ISA=qw(Exporter);
9@EXPORT_OK=qw(set_sshd_params import_key wait_for_DNS deter_tunnelip
10    configure_outgoing_iface add_route bind_tap_to_iface iface_to_addr
11    dest_to_iface addr_to_iface read_config emulab_config_filename);
12
13
14use strict;
15
16use IO::File;
17use IO::Pipe;
18
19use File::Temp;
20use File::Copy;
21
22# Standard locations of these commands (FreeBSD)
23my $IFCONFIG = "/sbin/ifconfig";
24my $ROUTE = "/sbin/route";
25my $TMCC = "/usr/local/etc/emulab/tmcc";
26my $FINDIF = "/usr/local/etc/emulab/findif";
27my $TMCC = "/usr/local/etc/emulab/tmcc";
28
29# Takes an ssh config file and a reference to a hash of keys whose values must
30# be set a specific way.  Replaces existing entries with the set values.
31sub set_sshd_params {
32    my($keys, $file) = @_;
33    my $f;
34    my $t;
35
36    $file = "/etc/ssh/sshd_config" unless $file;
37    $f = new IO::File("$file") || die "Can't read $f: $!\n";
38    $t = new File::Temp() || die "Can't open tempfile: $!\n";
39
40    while (<$f>) {
41        foreach my $k (keys %{$keys}) {
42            s/^\s*#?\s*$k.*/$k $keys->{$k}/;
43        }
44        print $t $_;
45    }
46    $f->close();
47    $t->close();
48    copy("$file", "$file.hold");
49    copy($t->filename, $file);
50    # When $t goes out of scope, the tmpfile is deleted
51}
52
53# Append the given keyfile to the given authorised key file.
54sub import_key {
55    my($keyfile, $authkeys) = @_;
56
57    my $kf = new IO::File($keyfile) || die "Can't open $keyfile:$!\n";
58    my $ak = new IO::File(">>$authkeys") || die "Can't open $authkeys: $!\n";
59
60    while (<$kf>) {
61        print $ak $_;
62    }
63    $kf->close();
64    $ak->close();
65}
66
67# Keep trying to look up the given hostname until successful.  If timeout is
68# given, die after waiting that long.  If sleep is given, wait that many
69# seconds between attempts (defaults to 5). 
70sub wait_for_DNS {
71    my($name, $timeout, $sleep) = @_;
72    my $start = time();
73    $sleep = 5 unless $sleep;
74    my @rv;
75
76    while (!@rv) {
77        @rv = gethostbyname($name);
78        die "Timeout waiting for DNS to get $name\n" 
79            if ($timeout && time() - $start > $timeout);
80        sleep($sleep) unless @rv;
81    }
82}
83
84# Get the external access parameters (interface, address, netmask, mac address,
85# and next hop router) from tmcd in Emulabs that support the DETER tunnelip
86# extension.
87sub deter_tunnelip {
88    # To parse tmcc
89    my $tmcc = new IO::Pipe || die "Can't create tmcc pipe: $!\n";
90    my $interface;              # Interface with external address
91    my $ip;                     # IP address of external interface
92    my $mac;                    # MAC address
93    my $netmask;                # Netmask
94    my $router;                 # Router for the internet
95
96
97    # Parse out the info about tunnelips.  Format is usually one line of
98    # ATTR=VALUE.  Multiple lines are possible.
99    $tmcc->reader("$TMCC tunnelip");
100    while (<$tmcc>) {
101        chomp;
102        /TUNNELIP=([\d\.]*)/ && do { $ip = $1; };
103        /TUNNELMASK=([\d\.]*)/ && do { $netmask = $1; };
104        /TUNNELMAC=([[:xdigit:]]*)/ && do { $mac = $1; };
105        /TUNNELROUTER=([\d\.]*)/ && do { $router = $1; };
106    }
107    $tmcc->close();
108
109    die "No MAC information for tunnel.\n" unless $mac;
110
111    # Use the emulab findif command to get the right interface to configure
112    $interface = `$FINDIF $mac`;
113    chomp $interface;
114    die "Can't get interface for mac address $mac: $?" if $? || !$interface;
115
116    return ($interface, $ip, $netmask, $mac, $router);
117}
118
119
120# Configure the given interface with the given IP address and netmask.
121sub configure_outgoing_iface {
122    my ($interface, $ip, $netmask) = @_;
123
124    my @ifconfig = ($IFCONFIG, $interface, $ip);
125    push(@ifconfig, 'netmask', $netmask) if $netmask;
126
127    system(@ifconfig);
128    die join(" ", @ifconfig) . " failed: $!\n" if $?;
129}
130
131# Add a route to the destination through the router.  If wait is given, do not
132# attempt to add the route until DNS has the hostname in it.  If timeout is
133# given, only wait that many seconds for DNS to acquire it.
134sub add_route {
135    my($routedest, $router, $wait, $timeout) = @_;
136    my @cmd;
137    # Linux and FreeBSD use slightly different route syntax, so get the OS
138    my $os = `uname`;
139    chomp $os;
140
141    $timeout = 0 unless $timeout;
142    die "add_host_route needs a router and a destination\n"
143        unless $router && $routedest;
144
145    wait_for_DNS($routedest, $timeout) if $wait;
146
147    if ( $os =~ /^Linux/ ) { 
148        @cmd = ($ROUTE, 'add', $routedest, 'gw', $router);
149    }
150    elsif ( $os =~ /^FreeBSD/ ) {
151        @cmd = ($ROUTE, 'add', $routedest, $router);
152    }
153    else {
154        die "Unknown OS: $os\n";
155    }
156    system(@cmd);
157    warn join(" ", @cmd) . " failed: $?\n" if $?;
158    return $?;
159}
160
161# Connect the tap($tapno) interface to $iface at the link level.  Remove any IP
162# addresses assigned to interface to avoid confusing the routing system.  We're
163# very tolerant of errors as the bridge and other interfaces may already exist
164# when this is called.
165sub bind_tap_to_iface {
166    my($tapno, $iface) = @_;
167    my $bridge = "bridge$tapno";
168    my $tap = "tap$tapno";
169    my @addr = iface_to_addr($iface);
170
171    # Wait for the tap
172    system("$IFCONFIG $tap > /dev/null 2>/dev/null");
173    while ($?) {
174        system("$IFCONFIG $tap > /dev/null 2>/dev/null");
175    }
176
177    system("ifconfig $bridge create");
178    warn "Cannot create bridge: $?\n" if $?;
179    foreach my $a (@addr) {
180        system("ifconfig $iface delete $a");
181        warn "Cannot delete address $a: $?\n" if $?;
182    }
183    system("ifconfig $bridge addm $iface up");
184    warn "Cannot add intefrace $iface to bridge: $?\n" if $?;
185    system("ifconfig $bridge addm $tap");
186    warn "Cannot add intefrace $tap to bridge: $?\n" if $?;
187    return $?;
188}
189
190# Return the IP addresses accociated with this interface (as a list)
191sub iface_to_addr {
192    my($iface) = @_;
193    my $ipipe = new IO::Pipe() || die "Can't create pipe for ifconfig: $!\n";
194    my @addr;
195
196    $ipipe->reader("$IFCONFIG $iface");
197    while(<$ipipe>) {
198        /inet\s+([0-9\.]+)/ && push(@addr, $1);
199    }
200    $ipipe->close();
201    return @addr;
202}
203
204# Return the interface that packets to this host (IP or DNS) would be sent on.
205sub dest_to_iface {
206    my($dest) =@_;
207    my $rpipe = new IO::Pipe() || die "Can't create pipe for route: $!\n";
208
209    $rpipe->reader("$ROUTE get $dest");
210    while (<$rpipe>) {
211        /interface:\s*([[:alnum:]]+)/ && do {
212            my $iface = $1;
213            $rpipe->close();
214            return $iface;
215        };
216    }
217    $rpipe->close();
218
219    die "No route to $dest\n";
220}
221
222# Return the interface bound to this IP address.  If there are more than one,
223# the first one returned by ifconfig is the one returned.
224sub addr_to_iface {
225    my($addr) = @_;
226    my $ipipe = new IO::Pipe() || die "Can't create pipe for ifconfig: $!\n";
227    my $iface;
228
229    $ipipe->reader("$IFCONFIG");
230    while(<$ipipe>) {
231        /^([[:alnum:]]+):/ && do {
232            $iface = $1;
233            next;
234        };
235        /inet\s+([0-9\.]+)/ && do {
236            if ($1 eq $addr) {
237                $ipipe->close();
238                return $iface;
239            }
240        };
241    }
242    $ipipe->close();
243    die "Cannot match $addr to an interface\n";
244}
245
246# untested
247sub read_config {
248    my($file, $map) = @_;
249    my %param;
250    my %keywords;
251    my $f;
252
253    foreach my $k (keys %{$map}) {
254        if ( $k =~ /(\S+)=[si]/ ) {
255            $keywords{$1} = $map->{$k};
256            $param{$1}++;
257        }
258        else {
259            $keywords{$k} = $map->{$k};
260        }
261    }
262
263    $f = new IO::File($file) || die "Can't open $file: $!\n";
264    while (<$f>) {
265        /^\s*([^:]+):\s*(.*)/ && do {
266            my $keyw = $1;
267            my $val = $2;
268
269            $keyw =~ tr [A-Z] [a-z];
270
271            if ($keywords{$keyw} ) {
272                if ( $param{$keyw} ) { 
273                    if ( ref($keywords{$keyw}) eq 'SCALAR') {
274                        ${$keywords{$keyw}} = $val;
275                    }
276                    elsif( ref($keywords{$keyw}) eq 'ARRAY') {
277                        push(@{$keywords{$keyw}}, $val);
278                    }
279                    else {
280                        die "Unknown variable type for $keyw\n";
281                    }
282                }
283                else  { ${$keywords{$keyw}}++; }
284            }
285            next;
286        };
287    }
288    $f->close();
289}
290
291
292sub emulab_config_filename {
293    # Find the configuration file in the usual place, which depends on what
294    # experiment and project we're in.
295    my $pid;
296    my $eid;
297    my $filename;
298    my $tmcd = new IO::Pipe() || die "Can't create pipe: $!\n";
299
300    $tmcd->reader("$TMCC status");
301
302    while (<$tmcd>) {
303        chomp;
304        /ALLOCATED=([^\/]+)\/(\S+)/ && do {
305            $pid = $1;
306            $eid = $2;
307        };
308    }
309    $tmcd->close();
310    my $hn = `hostname`;
311    chomp $hn;
312    $hn =~ s/\..*//;
313    $filename = "/proj/$pid/exp/$eid/tmp/$hn.gw.conf"
314        if $pid and $eid;
315
316    return $filename;
317}
318
3191;
Note: See TracBrowser for help on using the repository browser.