4 use Debconf::Client::ConfModule ':all';
7 use Digest::SHA1 'sha1_hex';
11 my $debconf_owner = 'openflow-switch';
13 my $default = '/etc/default/openflow-switch';
14 my $etc = '/etc/openflow-switch';
15 my $privkey_file = "$etc/of0-privkey.pem";
16 my $req_file = "$etc/of0-req.pem";
17 my $cert_file = "$etc/of0-cert.pem";
18 my $cacert_file = "$etc/cacert.pem";
20 my $ua = LWP::UserAgent->new;
26 title('OpenFlow Switch Setup');
28 my (%netdevs) = find_netdevs();
29 db_subst('netdevs', 'choices',
30 join(', ', map($netdevs{$_}, sort(keys(%netdevs)))));
31 db_set('netdevs', join(', ', grep(!/IP/, values(%netdevs))));
34 my (%config) = load_config($default);
38 db_set('netdevs', join(', ', map($netdevs{$_},
39 grep(exists $netdevs{$_}, split))))
42 db_set('band', $_ eq 'no' ? 'in-band' : 'out-of-band')
44 SWITCH_IP => sub { db_set('switch-ip', $_) },
45 CONTROLLER => sub { db_set('controller-vconn', $_) },
46 PRIVKEY => sub { $privkey_file = $_ },
47 CERT => sub { $cert_file = $_ },
48 CACERT => sub { $cacert_file = $_ },
51 for my $key (keys(%map)) {
52 local $_ = $config{$key};
53 &{$map{$key}}() if defined && !/^\s*$/;
57 my $cacert_preverified = -e $cacert_file;
59 if (! -e $privkey_file) {
60 my $old_umask = umask(077);
61 run_cmd("ofp-pki req $etc/of0 >&2 2>/dev/null");
62 chmod(0644, $req_file) or die "$req_file: chmod: $!\n";
66 my ($req, $req_fingerprint);
67 if (! -e $cert_file) {
68 open(REQ, '<', $req_file) or die "$req_file: open: $!\n";
69 $req = join('', <REQ>);
71 $req_fingerprint = sha1_hex($req);
76 # User backed up from first dialog box.
80 # Prompt for ports to include in switch.
85 # Validate the chosen ports.
86 my (@netdevs) = split(', ', db_get('netdevs'));
88 # No ports chosen. Disable switch.
89 db_input('no-netdevs');
90 return 'prev' if db_go();
92 } elsif (my (@conf_netdevs) = grep(/IP/, @netdevs)) {
93 # Point out that some ports have configured IP addresses.
94 db_subst('configured-netdevs', 'configured-netdevs',
95 join(', ', @conf_netdevs));
96 db_input('configured-netdevs');
104 # In-band or out-of-band controller?
109 return 'skip' if db_get('band') eq 'out-of-band';
111 db_input('switch-ip');
112 return 'prev' if db_go();
114 my $ip = db_get('switch-ip');
115 return 'next' if $ip =~ /^dhcp|\d+\.\d+.\d+.\d+$/i;
117 db_input('switch-ip-error');
123 my $old_vconn = db_get('controller-vconn');
124 db_input('controller-vconn');
125 return 'prev' if db_go();
127 my $vconn = db_get('controller-vconn');
128 if ($vconn =~ /^(tcp|ssl):([^:]+)(:.*)?/) {
129 if ($old_vconn ne $vconn
130 || db_get('pki-host') eq '') {
131 db_set('pki-host', $2);
136 db_input('controller-vconn-error');
141 return 'skip' if !ssl_enabled();
142 return 'skip' if -e $cacert_file && -e $cert_file;
144 db_input('pki-host');
145 return 'prev' if db_go();
149 return 'skip' if !ssl_enabled();
150 return 'skip' if -e $cacert_file;
152 my $pki_host = db_get('pki-host');
153 my $url = "http://$pki_host/openflow/pki/controllerca/cacert.pem";
154 my $response = $ua->get($url, ':content_file' => $cacert_file);
155 if ($response->is_success) {
159 db_subst('fetch-cacert-failed', 'url', $url);
160 db_subst('fetch-cacert-failed', 'error', $response->status_line);
161 db_subst('fetch-cacert-failed', 'pki-host', $pki_host);
162 db_input('fetch-cacert-failed');
167 return 'skip' if !ssl_enabled();
168 return 'skip' if -e $cert_file;
171 db_set('send-cert-req', 'yes');
172 db_input('send-cert-req');
173 return 'prev' if db_go();
174 return 'next' if db_get('send-cert-req') eq 'no';
176 my $pki_host = db_get('pki-host');
177 my $url = "http://$pki_host/cgi-bin/ofp-pki-cgi";
178 my $response = $ua->post($url, {'type' => 'switch',
180 return 'next' if $response->is_success;
182 db_subst('send-cert-req-failed', 'url', $url);
183 db_subst('send-cert-req-failed', 'error',
184 $response->status_line);
185 db_subst('send-cert-req-failed', 'pki-host',
187 db_input('send-cert-req-failed');
192 return 'skip' if !ssl_enabled();
193 return 'skip' if $cacert_preverified;
195 my ($cacert_fingerprint) = x509_fingerprint($cacert_file);
196 db_subst('verify-controller-ca', 'fingerprint', $cacert_fingerprint);
197 db_input('verify-controller-ca');
198 return 'prev' if db_go();
199 return 'next' if db_get('verify-controller-ca') eq 'yes';
200 unlink($cacert_file);
204 return 'skip' if !ssl_enabled();
205 return 'skip' if -e $cert_file;
208 db_set('fetch-switch-cert', 'yes');
209 db_input('fetch-switch-cert');
210 return 'prev' if db_go();
211 exit(1) if db_get('fetch-switch-cert') eq 'no';
213 my $pki_host = db_get('pki-host');
214 my $url = "http://$pki_host/openflow/pki/switchca/certs/$req_fingerprint-cert.pem";
215 my $response = $ua->get($url, ':content_file' => $cert_file);
216 if ($response->is_success) {
220 db_subst('fetch-switch-cert-failed', 'url', $url);
221 db_subst('fetch-switch-cert-failed', 'error',
222 $response->status_line);
223 db_subst('fetch-switch-cert-failed', 'pki-host',
225 db_input('fetch-switch-cert-failed');
230 db_input('complete');
242 my $ret = &{$states[$state]}();
243 $ret = db_go() ? 'prev' : 'next' if !defined $ret;
244 if ($ret eq 'next') {
246 } elsif ($ret eq 'prev') {
248 } elsif ($ret eq 'skip') {
250 } elsif ($ret eq 'done') {
253 die "unknown ret $ret";
255 $state += $direction;
259 $config{NETDEVS} = join(' ', map(/^(\S+)/, split(', ', db_get('netdevs'))));
260 if (db_get('band') eq 'in-band') {
261 $config{IN_BAND} = 'yes';
262 $config{SWITCH_IP} = db_get('switch-ip');
264 $config{IN_BAND} = 'no';
266 $config{CONTROLLER} = db_get('controller-vconn');
267 $config{PRIVKEY} = $privkey_file;
268 $config{CERT} = $cert_file;
269 $config{CACERT} = $cacert_file;
270 save_config($default, %config);
272 dup2(2, 1); # Get stdout back.
273 system("/etc/init.d/openflow-switch restart");
276 return db_get('controller-vconn') =~ /^ssl:/;
280 my ($question, $key, $value) = @_;
281 $question = "$debconf_owner/$question";
282 my ($ret, $seen) = subst($question, $key, $value);
283 if ($ret && $ret != 30) {
284 die "Error substituting $value for $key in debconf question "
285 . "$question: $seen";
290 my ($question, $value) = @_;
291 $question = "$debconf_owner/$question";
292 my ($ret, $seen) = set($question, $value);
293 if ($ret && $ret != 30) {
294 die "Error setting debconf question $question to $value: $seen";
300 $question = "$debconf_owner/$question";
301 my ($ret, $seen) = get($question);
303 die "Error getting debconf question $question answer: $seen";
309 my ($question, $flag, $value) = @_;
310 $question = "$debconf_owner/$question";
311 my ($ret, $seen) = fset($question, $flag, $value);
312 if ($ret && $ret != 30) {
313 die "Error setting debconf question $question flag $flag to $value: "
319 my ($question, $flag) = @_;
320 $question = "$debconf_owner/$question";
321 my ($ret, $seen) = fget($question, $flag);
323 die "Error getting debconf question $question flag $flag: $seen";
330 db_fset($question, "seen", "false");
332 $question = "$debconf_owner/$question";
333 my ($ret, $seen) = input('high', $question);
334 if ($ret && $ret != 30) {
335 die "Error requesting debconf question $question: $seen";
341 my ($ret, $seen) = go();
342 if (!defined($ret)) {
343 exit(1); # Cancel button was pushed.
345 if ($ret && $ret != 30) {
346 die "Error asking debconf questions: $seen";
353 return if system($cmd) == 0;
356 die "$cmd: failed to execute: $!\n";
358 die sprintf("$cmd: child died with signal %d, %s coredump\n",
359 ($? & 127), ($? & 128) ? 'with' : 'without');
361 die sprintf("$cmd: child exited with value %d\n", $? >> 8);
365 sub x509_fingerprint {
367 my $cmd = "openssl x509 -noout -in $file -fingerprint";
368 open(OPENSSL, '-|', $cmd) or die "$cmd: failed to execute: $!\n";
369 my $line = <OPENSSL>;
371 my ($fingerprint) = $line =~ /SHA1 Fingerprint=(.*)/;
372 return $line if !defined $fingerprint;
373 $fingerprint =~ s/://g;
378 my ($netdev, %netdevs);
379 open(IFCONFIG, "/sbin/ifconfig -a|") or die "ifconfig failed: $!";
381 if (my ($nd) = /^([^\s]+)/) {
383 $netdevs{$netdev} = "$netdev";
384 if (my ($hwaddr) = /HWaddr (\S+)/) {
385 $netdevs{$netdev} .= " (MAC: $hwaddr)";
387 } elsif (my ($ip4) = /^\s*inet addr:(\S+)/) {
388 $netdevs{$netdev} .= " (IP: $ip4)";
389 } elsif (my ($ip6) = /^\s*inet6 addr:(\S+)/) {
390 $netdevs{$netdev} .= " (IPv6: $ip6)";
393 foreach my $nd (keys(%netdevs)) {
394 delete $netdevs{$nd} if $nd eq 'lo' || $nd =~ /^wmaster/;
402 my ($cmd) = "set -a && . $file && env";
403 if (!open(VARS, '-|', $cmd)) {
404 print STDERR "$cmd: failed to execute: $!\n";
409 my ($var, $value) = /^([^=]+)=(.*)$/ or next;
410 $config{$var} = $value;
418 if (m&^[-a-zA-Z0-9:./%^_+,]*$&) {
427 my ($var, $value) = @_;
428 return $var . '=' . shell_escape($value);
432 my ($file, %config) = @_;
434 if (open(FILE, '<', $file)) {
440 # Replace all existing variable assignments.
441 for (my ($i) = 0; $i <= $#lines; $i++) {
442 local $_ = $lines[$i];
443 my ($var, $value) = /^\s*([^=#]+)=(.*)$/ or next;
444 if (exists($config{$var})) {
445 $lines[$i] = shell_assign($var, $config{$var});
446 delete $config{$var};
448 $lines[$i] = "#$lines[$i]";
452 # Find a place to put any remaining variable assignments.
454 for my $var (keys(%config)) {
455 my $assign = shell_assign($var, $config{$var});
457 # Replace the last commented-out variable assignment to $var, if any.
458 for (my ($i) = $#lines; $i >= 0; $i--) {
459 local $_ = $lines[$i];
460 if (/^\s*#\s*$var=/) {
461 $lines[$i] = $assign;
466 # Find a place to add the var: after the final commented line
467 # just after a line that contains "$var:".
468 for (my ($i) = 0; $i <= $#lines; $i++) {
469 if ($lines[$i] =~ /^\s*#\s*$var:/) {
470 for (my ($j) = $i + 1; $j <= $#lines; $j++) {
471 if ($lines[$j] !~ /^\s*#/) {
472 splice(@lines, $j, 0, $assign);
480 push(@lines, $assign);
483 open(NEWFILE, '>', "$file.tmp") or die "$file.tmp: create: $!\n";
484 print NEWFILE join('', map("$_\n", @lines));
486 rename("$file.tmp", $file) or die "$file.tmp: rename to $file: $!\n";