Merge branch 'locking'
authorBen Pfaff <blp@nicira.com>
Thu, 24 Jul 2008 23:07:32 +0000 (16:07 -0700)
committerBen Pfaff <blp@nicira.com>
Thu, 24 Jul 2008 23:07:32 +0000 (16:07 -0700)
86 files changed:
.gitignore
Make.vars
configure.ac
controller/Makefile.am
controller/controller.c
datapath/datapath.c
datapath/hwtable_dummy/hwtable_dummy.c
datapath/linux-2.4/Modules.mk
datapath/linux-2.4/compat-2.4/include/linux/rculist.h [new file with mode: 0644]
datapath/linux-2.6/Modules.mk
datapath/linux-2.6/compat-2.6/include/linux/rculist.h [new file with mode: 0644]
datapath/table-linear.c
debian/ofp-switch-setup
debian/openflow-common.install
debian/openflow-common.manpages
debian/openflow-controller.README.Debian
debian/openflow-controller.install
debian/openflow-controller.manpages
debian/openflow-pki.install
debian/openflow-switch.config [deleted file]
debian/openflow-switch.default
debian/openflow-switch.init
debian/openflow-switch.install
debian/openflow-switch.manpages
debian/openflow-switch.postinst [deleted file]
debian/openflow-switch.templates
debian/po/templates.pot
debian/rules
include/daemon.h
include/dhcp-client.h
include/dhcp.h
include/openflow.h
include/vlog.h
lib/Makefile.am
lib/buffer.c
lib/command-line.c
lib/csum.c
lib/daemon.c
lib/dhcp-client.c
lib/dhcp.c
lib/dpif.c
lib/dynamic-string.c
lib/fatal-signal.c
lib/fault.c
lib/flow.c
lib/hash.c
lib/learning-switch.c
lib/list.c
lib/mac-learning.c
lib/netdev.c
lib/netlink.c
lib/ofp-print.c
lib/poll-loop.c
lib/queue.c
lib/random.c
lib/rconn.c
lib/socket-util.c
lib/util.c
lib/vconn-netlink.c
lib/vconn-ssl.c
lib/vconn-tcp.c
lib/vconn.c
lib/vlog-socket.c
lib/vlog.c
secchan/Makefile.am
secchan/secchan.8.in
secchan/secchan.c
switch/Makefile.am
switch/chain.c
switch/crc32.c
switch/datapath.c
switch/switch-flow.c
switch/switch.c
switch/table-hash.c
switch/table-linear.c
tests/Makefile.am
tests/test-dhcp-client.c
tests/test-list.c
tests/test-type-props.c
utilities/Makefile.am
utilities/dpctl.c
utilities/ofp-discover.8.in [new file with mode: 0644]
utilities/ofp-discover.c [new file with mode: 0644]
utilities/ofp-kill.8.in [new file with mode: 0644]
utilities/ofp-kill.c [new file with mode: 0644]
utilities/vlogconf.c

index 0332974497d2f4ef66a31c5d420b11ecfe17d795..ff011f2a793eda9c91b01443c9141bd76134a11a 100644 (file)
@@ -32,8 +32,6 @@
 /configure-stamp
 /depcomp
 /install-sh
-/libtool
-/ltmain.sh
 /missing
 /stamp-h1
 Module.symvers
index 98a9e4476011a4e0e406ccc17d34f9e680a678e6..583d3eb0f9183c6d03dceb75420ba6f1230fa88a 100644 (file)
--- a/Make.vars
+++ b/Make.vars
@@ -1,7 +1,6 @@
 # -*- makefile -*-
 
 AM_CPPFLAGS = $(SSL_CFLAGS)
-LIBS = $(SSL_LIBS)
 
 COMMON_FLAGS = -DVERSION=\"$(VERSION)\"
 if NDEBUG
index 5fea45f8fd9e6c08738a011d12565fafe8f51c78..40fe94f5082ffae9d206781375e1e96b925362d6 100644 (file)
@@ -1,10 +1,12 @@
 AC_PREREQ(2.59)
 AC_INIT(openflow, v0.8.1, info@openflowswitch.org)
+AC_CONFIG_HEADERS([config.h])
 AM_INIT_AUTOMAKE
 
 AC_PROG_CC
 AC_PROG_CPP
 AC_PROG_LD
+AC_PROG_RANLIB
 
 AC_ARG_VAR([PERL], [path to Perl interpreter])
 AC_PATH_PROG([PERL], perl, no)
@@ -14,8 +16,6 @@ fi
 
 AC_USE_SYSTEM_EXTENSIONS
 
-AC_PROG_LIBTOOL
-
 AC_ARG_ENABLE(
   [ndebug],
   [AC_HELP_STRING([--enable-ndebug], 
@@ -109,8 +109,9 @@ if test "$HAVE_OPENSSL" = yes; then
 fi
 
 AC_CHECK_LIB([socket], [connect])
-AC_CHECK_LIB([resolv], [gethostbyname])
-AC_CHECK_LIB([dl], [dladdr])
+AC_SEARCH_LIBS([gethostbyname], [resolv], [RESOLVER_LIBS=-lresolv])
+AC_CHECK_LIB([dl], [dladdr], [FAULT_LIBS=-ldl])
+AC_SUBST([FAULT_LIBS])
 
 CFLAGS="$CFLAGS -Wall -Wno-sign-compare"
 
index 1a5e01a8efa7836caa5c15986e8f20477cb67b83..81863ff38c26cee1d7a97b91746e946dbad73bd0 100644 (file)
@@ -5,7 +5,7 @@ man_MANS = controller.8
 DISTCLEANFILES = controller.8
 
 controller_SOURCES = controller.c
-controller_LDADD = ../lib/libopenflow.la -ldl
+controller_LDADD = ../lib/libopenflow.a $(FAULT_LIBS) $(SSL_LIBS)
 
 EXTRA_DIST = controller.8.in
 controller.8: controller.8.in Makefile
index c43926c40f0be33ada72c492f118cdff80152833..61a550a41c821a2316ef5d8bb7af152d8b765086 100644 (file)
@@ -31,6 +31,8 @@
  * derivatives without specific, written prior permission.
  */
 
+#include <config.h>
+
 #include <errno.h>
 #include <getopt.h>
 #include <limits.h>
@@ -251,7 +253,7 @@ parse_options(int argc, char *argv[])
             break;
 
         case 'P':
-            set_pidfile(optarg ? optarg : "controller.pid");
+            set_pidfile(optarg);
             break;
 
         case 'H':
index d55ac2ea2a5d69beaf93d85d9b96e12f8364aeb5..a303f5008282fb6885b7aecb6fac4053bf48caf7 100644 (file)
@@ -28,6 +28,7 @@
 #include <linux/netfilter_bridge.h>
 #include <linux/inetdevice.h>
 #include <linux/list.h>
+#include <linux/rculist.h>
 
 #include "openflow-netlink.h"
 #include "datapath.h"
index f6c45cdc5a4423d4f80048c8176d62a81d4c3a70..a80ce6b7250ea5cf4d48ebc51592174272ce33db 100644 (file)
@@ -35,6 +35,7 @@
 #include <linux/rcupdate.h>
 #include <linux/slab.h>
 #include <linux/list.h>
+#include <linux/rculist.h>
 #include <linux/delay.h>
 #include <linux/if_arp.h>
 
index 7681d76ee8c6784871991700ca966b2a0f5030c8..278f16a32985641a208a928716cc9d79bf51a231 100644 (file)
@@ -41,6 +41,7 @@ compat24_headers = \
        linux-2.4/compat-2.4/include/linux/netdevice.h \
        linux-2.4/compat-2.4/include/linux/netlink.h \
        linux-2.4/compat-2.4/include/linux/random.h \
+       linux-2.4/compat-2.4/include/linux/rculist.h \
        linux-2.4/compat-2.4/include/linux/rcupdate.h \
        linux-2.4/compat-2.4/include/linux/sched.h \
        linux-2.4/compat-2.4/include/linux/skbuff.h \
diff --git a/datapath/linux-2.4/compat-2.4/include/linux/rculist.h b/datapath/linux-2.4/compat-2.4/include/linux/rculist.h
new file mode 100644 (file)
index 0000000..c7a69f9
--- /dev/null
@@ -0,0 +1,2 @@
+/* In Linux 2.6.26, part of list.h was broken out into rculist.h. */
+#include <linux/list.h>
index 3a1d653d401cca72380fac8dd09d5346822e9a33..b950355b9868f63f78ecfe8a5da04add26839161 100644 (file)
@@ -10,6 +10,7 @@ openflow_headers += \
        linux-2.6/compat-2.6/include/linux/mutex.h \
        linux-2.6/compat-2.6/include/linux/netlink.h \
        linux-2.6/compat-2.6/include/linux/random.h \
+       linux-2.6/compat-2.6/include/linux/rculist.h \
        linux-2.6/compat-2.6/include/linux/skbuff.h \
        linux-2.6/compat-2.6/include/linux/tcp.h \
        linux-2.6/compat-2.6/include/linux/timer.h \
diff --git a/datapath/linux-2.6/compat-2.6/include/linux/rculist.h b/datapath/linux-2.6/compat-2.6/include/linux/rculist.h
new file mode 100644 (file)
index 0000000..4164c0e
--- /dev/null
@@ -0,0 +1,12 @@
+#ifndef __LINUX_RCULIST_WRAPPER_H
+#define __LINUX_RCULIST_WRAPPER_H
+
+#include <linux/version.h>
+#if LINUX_VERSION_CODE >= KERNEL_VERSION(2,6,26)
+#include_next <linux/rculist.h>
+#else
+/* Prior to 2.6.26, the contents of rculist.h were part of list.h. */
+#include <linux/list.h>
+#endif
+
+#endif
index a22776a303f748a8daad2aaccce249669c9f3a50..b4d4a77551ed41c2b7f06475e63ec147c2dcc7d4 100644 (file)
@@ -10,7 +10,7 @@
 
 #include <linux/rcupdate.h>
 #include <linux/slab.h>
-#include <linux/list.h>
+#include <linux/rculist.h>
 
 struct sw_table_linear {
        struct sw_table swt;
index 0c03c3323015b927f528ad0a94a44ef985363e0c..275ddfca86554401261a2ca34e02941d9e0b0562 100755 (executable)
@@ -12,15 +12,20 @@ my $debconf_owner = 'openflow-switch';
 
 my $default = '/etc/default/openflow-switch';
 my $etc = '/etc/openflow-switch';
+my $rundir = '/var/run';
 my $privkey_file = "$etc/of0-privkey.pem";
 my $req_file = "$etc/of0-req.pem";
 my $cert_file = "$etc/of0-cert.pem";
 my $cacert_file = "$etc/cacert.pem";
+my $ofp_discover_pidfile = "$rundir/ofp-discover.pid";
 
 my $ua = LWP::UserAgent->new;
 $ua->timeout(10);
 $ua->env_proxy;
 
+system("/etc/init.d/openflow-switch stop 1>&2");
+kill_ofp_discover();
+
 version('2.0');
 capb('backup');
 title('OpenFlow Switch Setup');
@@ -38,8 +43,9 @@ if (-e $default) {
            db_set('netdevs', join(', ', map($netdevs{$_},
                                             grep(exists $netdevs{$_}, split))))
        },
-       IN_BAND => sub {
-           db_set('band', $_ eq 'no' ? 'in-band' : 'out-of-band')
+       MODE => sub {
+           db_set('mode',
+                  $_ eq 'in-band' || $_ eq 'out-of-band' ? $_ : 'discovery')
        },
        SWITCH_IP => sub { db_set('switch-ip', $_) },
        CONTROLLER => sub { db_set('controller-vconn', $_) },
@@ -55,21 +61,9 @@ if (-e $default) {
 }
 
 my $cacert_preverified = -e $cacert_file;
-
-if (! -e $privkey_file) {
-    my $old_umask = umask(077);
-    run_cmd("ofp-pki req $etc/of0 >&2 2>/dev/null");
-    chmod(0644, $req_file) or die "$req_file: chmod: $!\n";
-    umask($old_umask);
-}
-
 my ($req, $req_fingerprint);
-if (! -e $cert_file) {
-    open(REQ, '<', $req_file) or die "$req_file: open: $!\n";
-    $req = join('', <REQ>);
-    close(REQ);
-    $req_fingerprint = sha1_hex($req);
-}
+
+my %options;
 
 my (@states) =
   (sub {
@@ -101,12 +95,69 @@ my (@states) =
        }
    },
    sub {
-       # In-band or out-of-band controller?
-       db_input('band');
+       # Discovery or in-band or out-of-band controller?
+       db_input('mode');
        return;
    },
    sub {
-       return 'skip' if db_get('band') eq 'out-of-band';
+       return 'skip' if db_get('mode') ne 'discovery';
+       for (;;) {
+           # Notify user that we are going to do discovery.
+           db_input('discover');
+           return 'prev' if db_go();
+           print STDERR "Please wait up to 30 seconds for discovery...\n";
+
+           # Make sure that there's no running discovery process.
+           kill_ofp_discover();
+
+           # Do discovery.
+           %options = ();
+           open(DISCOVER, '-|', 'ofp-discover --timeout=30 --pidfile '
+                . join(' ', netdev_names()));
+           while (<DISCOVER>) {
+               chomp;
+               if (my ($name, $value) = /^([^=]+)=(.*)$/) {
+                   if ($value =~ /^"(.*)"$/) {
+                       $value = $1;
+                       $value =~ s/\\([0-7][0-7][0-7])/chr($1)/ge;
+                   } else {
+                       $value =~ s/^(0x[[:xdigit:]]+)$/hex($1)/e;
+                   }
+                   $options{$name} = $value;
+               }
+               last if /^$/;
+           }
+
+           # Check results.
+           my $vconn = $options{'ofp-controller-vconn'};
+           my $pki_uri = $options{'ofp-pki-uri'};
+           return 'next'
+             if (defined($vconn)
+                 && is_valid_vconn($vconn)
+                 && (!is_ssl_vconn($vconn) || defined($pki_uri)));
+
+           # Try again?
+           kill_ofp_discover();
+           db_input('discovery-failure');
+           db_go();
+       }
+   },
+   sub {
+       return 'skip' if db_get('mode') ne 'discovery';
+
+       my $vconn = $options{'ofp-controller-vconn'};
+       my $pki_uri = $options{'ofp-pki-uri'};
+       db_subst('discovery-success', 'controller-vconn', $vconn);
+       db_subst('discovery-success',
+                'pki-uri', is_ssl_vconn($vconn) ? $pki_uri : "no PKI in use");
+       db_input('discovery-success');
+       return 'prev' if db_go();
+       db_set('controller-vconn', $vconn);
+       db_set('pki-uri', $pki_uri);
+       return 'next';
+   },
+   sub {
+       return 'skip' if db_get('mode') ne 'in-band';
        for (;;) {
            db_input('switch-ip');
            return 'prev' if db_go();
@@ -119,16 +170,16 @@ my (@states) =
        }
    },
    sub {
+       return 'skip' if db_get('mode') eq 'discovery';
        for (;;) {
            my $old_vconn = db_get('controller-vconn');
            db_input('controller-vconn');
            return 'prev' if db_go();
 
            my $vconn = db_get('controller-vconn');
-           if ($vconn =~ /^(tcp|ssl):([^:]+)(:.*)?/) {
-               if ($old_vconn ne $vconn
-                   || db_get('pki-host') eq '') {
-                   db_set('pki-host', $2);
+           if (is_valid_vconn($vconn)) {
+               if ($old_vconn ne $vconn || db_get('pki-uri') eq '') {
+                   db_set('pki-uri', pki_host_to_uri($2));
                }
                return 'next';
            }
@@ -137,11 +188,29 @@ my (@states) =
            db_go();
        }
    },
+   sub {
+       return 'skip' if !ssl_enabled();
+
+       if (! -e $privkey_file) {
+           my $old_umask = umask(077);
+           run_cmd("ofp-pki req $etc/of0 >&2 2>/dev/null");
+           chmod(0644, $req_file) or die "$req_file: chmod: $!\n";
+           umask($old_umask);
+       }
+
+       if (! -e $cert_file) {
+           open(REQ, '<', $req_file) or die "$req_file: open: $!\n";
+           $req = join('', <REQ>);
+           close(REQ);
+           $req_fingerprint = sha1_hex($req);
+       }
+       return 'skip';
+   },
    sub {
        return 'skip' if !ssl_enabled();
        return 'skip' if -e $cacert_file && -e $cert_file;
 
-       db_input('pki-host');
+       db_input('pki-uri');
        return 'prev' if db_go();
        return;
    },
@@ -149,8 +218,16 @@ my (@states) =
        return 'skip' if !ssl_enabled();
        return 'skip' if -e $cacert_file;
 
-       my $pki_host = db_get('pki-host');
-       my $url = "http://$pki_host/openflow/pki/controllerca/cacert.pem";
+       my $pki_uri = db_get('pki-uri');
+       if ($pki_uri !~ /:/) {
+           $pki_uri = pki_host_to_uri($pki_uri);
+       } else {
+           # Trim trailing slashes.
+           $pki_uri =~ s%/+$%%;
+       }
+       db_set('pki-uri', $pki_uri);
+
+       my $url = "$pki_uri/controllerca/cacert.pem";
        my $response = $ua->get($url, ':content_file' => $cacert_file);
        if ($response->is_success) {
            return 'next';
@@ -158,7 +235,7 @@ my (@states) =
 
        db_subst('fetch-cacert-failed', 'url', $url);
        db_subst('fetch-cacert-failed', 'error', $response->status_line);
-       db_subst('fetch-cacert-failed', 'pki-host', $pki_host);
+       db_subst('fetch-cacert-failed', 'pki-uri', $pki_uri);
        db_input('fetch-cacert-failed');
        db_go();
        return 'prev';
@@ -173,8 +250,9 @@ my (@states) =
            return 'prev' if db_go();
            return 'next' if db_get('send-cert-req') eq 'no';
 
-           my $pki_host = db_get('pki-host');
-           my $url = "http://$pki_host/cgi-bin/ofp-pki-cgi";
+           my $pki_uri = db_get('pki-uri');
+           my ($pki_base_uri) = $pki_uri =~ m%^([^/]+://[^/]+)/%;
+           my $url = "$pki_base_uri/cgi-bin/ofp-pki-cgi";
            my $response = $ua->post($url, {'type' => 'switch',
                                            'req' => $req});
            return 'next' if $response->is_success;
@@ -182,8 +260,7 @@ my (@states) =
            db_subst('send-cert-req-failed', 'url', $url);
            db_subst('send-cert-req-failed', 'error',
                     $response->status_line);
-           db_subst('send-cert-req-failed', 'pki-host',
-                    $pki_host);
+           db_subst('send-cert-req-failed', 'pki-uri', $pki_uri);
            db_input('send-cert-req-failed');
            db_go();
        }
@@ -210,8 +287,8 @@ my (@states) =
            return 'prev' if db_go();
            exit(1) if db_get('fetch-switch-cert') eq 'no';
 
-           my $pki_host = db_get('pki-host');
-           my $url = "http://$pki_host/openflow/pki/switchca/certs/$req_fingerprint-cert.pem";
+           my $pki_uri = db_get('pki-uri');
+           my $url = "$pki_uri/switchca/certs/$req_fingerprint-cert.pem";
            my $response = $ua->get($url, ':content_file' => $cert_file);
            if ($response->is_success) {
                return 'next';
@@ -220,8 +297,7 @@ my (@states) =
            db_subst('fetch-switch-cert-failed', 'url', $url);
            db_subst('fetch-switch-cert-failed', 'error',
                     $response->status_line);
-           db_subst('fetch-switch-cert-failed', 'pki-host',
-                    $pki_host);
+           db_subst('fetch-switch-cert-failed', 'pki-uri', $pki_uri);
            db_input('fetch-switch-cert-failed');
            db_go();
        }
@@ -256,24 +332,25 @@ for (;;) {
 }
 
 my %config;
-$config{NETDEVS} = join(' ', map(/^(\S+)/, split(', ', db_get('netdevs'))));
-if (db_get('band') eq 'in-band') {
-    $config{IN_BAND} = 'yes';
+$config{NETDEVS} = join(' ', netdev_names());
+$config{MODE} = db_get('mode');
+if (db_get('mode') eq 'in-band') {
     $config{SWITCH_IP} = db_get('switch-ip');
-} else {
-    $config{IN_BAND} = 'no';
 }
-$config{CONTROLLER} = db_get('controller-vconn');
+if (db_get('mode') ne 'discovery') {
+    $config{CONTROLLER} = db_get('controller-vconn');
+}
 $config{PRIVKEY} = $privkey_file;
 $config{CERT} = $cert_file;
 $config{CACERT} = $cacert_file;
 save_config($default, %config);
 
 dup2(2, 1);                     # Get stdout back.
-system("/etc/init.d/openflow-switch restart");
+kill_ofp_discover();
+system("/etc/init.d/openflow-switch start");
 
 sub ssl_enabled {
-    return db_get('controller-vconn') =~ /^ssl:/;
+    return is_ssl_vconn(db_get('controller-vconn'));
 }
 
 sub db_subst {
@@ -288,7 +365,7 @@ sub db_subst {
 
 sub db_set {
     my ($question, $value) = @_;
-    $question = "$debconf_owner/$question";
+   $question = "$debconf_owner/$question";
     my ($ret, $seen) = set($question, $value);
     if ($ret && $ret != 30) {
         die "Error setting debconf question $question to $value: $seen";
@@ -415,7 +492,9 @@ sub load_config {
 
 sub shell_escape {
     local $_ = $_[0];
-    if (m&^[-a-zA-Z0-9:./%^_+,]*$&) {
+    if ($_ eq '') {
+        return '""';
+    } elsif (m&^[-a-zA-Z0-9:./%^_+,]*$&) {
         return $_;
     } else {
         s/'/'\\''/;
@@ -485,3 +564,28 @@ sub save_config {
     close(NEWFILE);
     rename("$file.tmp", $file) or die "$file.tmp: rename to $file: $!\n";
 }
+
+sub pki_host_to_uri {
+    my ($pki_host) = @_;
+    return "http://$pki_host/openflow/pki";
+}
+
+sub kill_ofp_discover {
+    # Delegate this to a subprocess because there is no portable way
+    # to invoke fcntl(F_GETLK) from Perl.
+    system("ofp-kill --force $ofp_discover_pidfile");
+}
+
+sub netdev_names {
+    return map(/^(\S+)/, split(', ', db_get('netdevs')));
+}
+
+sub is_valid_vconn {
+    my ($vconn) = @_;
+    return scalar($vconn =~ /^(tcp|ssl):([^:]+)(:.*)?/);
+}
+
+sub is_ssl_vconn {
+    my ($vconn) = @_;
+    return scalar($vconn =~ /^ssl:/);
+}
index 4175c301140ee07da55b3d304697b3d65f87214b..624bc34430de159fc867885859f838cfdefeb345 100644 (file)
@@ -1,2 +1,2 @@
-utilities/ofp-pki usr/sbin
-utilities/vlogconf usr/sbin
+_debian/utilities/ofp-pki usr/sbin
+_debian/utilities/vlogconf usr/sbin
index 282ec32956c58f78bd6224bdb07b600ede6da507..ee20d1ea954cc90a854e379c0dfe11f228becba0 100644 (file)
@@ -1,2 +1,2 @@
 utilities/vlogconf.8
-utilities/ofp-pki.8
+_debian/utilities/ofp-pki.8
index c54e56999edac140e9949ddc525c9157ddda5723..19d5cb9b864e84972d568e82dedbe270eb1cc66b 100644 (file)
@@ -1,5 +1,10 @@
 README.Debian for openflow-controller
 -------------------------------------
 
-* To reconfigure the controller, edit /etc/default/openflow-controller
+* To (re)configure the controller, edit /etc/default/openflow-controller
   and run "/etc/init.d/openflow-controller restart".
+
+* To enable OpenFlow switches to automatically discover the location
+  of the controller, you must install and configure a DHCP server.
+  The secchan(8) manpage (found in the openflow-switch package) gives
+  a working example configuration file for the ISC DHCP server.
index 75ab43568499fb5becd50b07c8d20740db136830..3932ab6a67eda12d48f2720795023195e46ee3a6 100644 (file)
@@ -1 +1 @@
-controller/controller usr/sbin
+_debian/controller/controller usr/sbin
index c30c912b225408b7b448457a916fd1383d49386b..3fbaaeaf4e1f177cdf1fa95ba4cf762e3a7db6a5 100644 (file)
@@ -1 +1 @@
-controller/controller.8
+_debian/controller/controller.8
index 58cfb4e902f567ee2bafb82ef390ee313bf18142..cd530ca4a56608f0887dd39b09fbb85937ed1a23 100644 (file)
@@ -1 +1 @@
-utilities/ofp-pki-cgi usr/lib/cgi-bin
+_debian/utilities/ofp-pki-cgi usr/lib/cgi-bin
diff --git a/debian/openflow-switch.config b/debian/openflow-switch.config
deleted file mode 100755 (executable)
index 7546e02..0000000
+++ /dev/null
@@ -1,4 +0,0 @@
-#!/bin/sh
-set -e
-. /usr/share/debconf/confmodule
-# Nothing more to do -- the user is responsible for running ofp-switch-setup.
index f2ce5a6ae8a40b12d49c252f15c232d5ba1ec6eb..e4fb07bc65e76b8b7cefab09528b14ed907f278b 100644 (file)
 # become part of the switch and cannot be used for other purposes.
 #NETDEVS=""
 
-# IN_BAND: The OpenFlow switch must be able to contact the OpenFlow
-# controller over the network.  It can do so in one of two ways:
+# MODE: The OpenFlow switch has three modes that determine how it
+# reaches the controller:
+#
+# * in-band with discovery: A single network is used for OpenFlow
+#   traffic and other data traffic; that is, the switch contacts the
+#   controller over one of the network devices selected as OpenFlow
+#   switch ports.  The switch automatically determines the location of
+#   the controller using a DHCP request with an OpenFlow-specific
+#   vendor option.  This is the most common case.
 # 
-# * in-band: A single network is used for OpenFlow traffic and other
-#   data traffic; that is, the switch contacts the controller over one
-#   of the network devices selected as OpenFlow switch ports in the
-#   previous question.  This is the most common case.
+# * in-band: As above, but the location of the controller is manually
+#   configured.
 # 
 # * out-of-band: OpenFlow traffic uses a network separate from the
 #   data traffic that it controls.  If this is the case, the control
 #   one of those selected as an OpenFlow switch port in the previous
 #   question.
 #
-# Set IN_BAND to yes for in-band control, or to no for out-of-band
-# control.
-IN_BAND=yes
+# Set MODE to 'discovery', 'in-band', or 'out-of-band' for these
+# respective cases.
+MODE=discovery
 
-# SWITCH_IP: For in-band communication with the controller, the
-# OpenFlow switch must be able to determine its own IP address.  Its
-# IP address may be configured statically or dynamically:
+# SWITCH_IP: In 'in-band' mode, the switch's IP address may be
+# configured statically or dynamically:
 # 
 # * For static configuration, specify the switch's IP address as a
 #   string.
@@ -51,7 +55,7 @@ IN_BAND=yes
 #   if the network topology allows the switch to contact the DHCP
 #   server before it connects to the OpenFlow controller.
 #
-# If IN_BAND is set to "no" above, this setting has no effect.
+# This setting has no effect unless MODE is set to 'in-band'.
 SWITCH_IP=dhcp
 
 # CONTROLLER: Location of controller.
@@ -59,6 +63,7 @@ SWITCH_IP=dhcp
 #  tcp:HOST[:PORT]         via TCP to PORT (default: 975) on HOST
 #  ssl:HOST[:PORT]         via SSL to PORT (default: 976) on HOST
 # The default below assumes that the controller is running locally.
+# This setting has no effect when MODE is set to 'discovery'.
 #CONTROLLER="tcp:127.0.0.1"
 
 # PRIVKEY: Name of file containing switch's private key.
index 1f03df104f4b3f5ba715f1279ce4c509e41d54ed..0c8a8688e26f9e2ba1bb373de30a00c73f92fdef 100755 (executable)
@@ -50,8 +50,14 @@ running_pid()
     [ ! -d /proc/$pid ] &&  return 1
     cmd=`cat /proc/$pid/cmdline | tr "\000" "\n"|head -n 1 |cut -d : -f 1`
     # Is this the expected child?
-    [ "$cmd" != "$name" ] &&  return 1
-    return 0
+    case $cmd in
+        $name|*/$name)
+            return 0
+            ;;
+        *)
+            return 1
+            ;;
+    esac
 }
 
 running()
@@ -108,6 +114,26 @@ check_op() {
     fi
 }
 
+configure_ssl() {
+    if test ! -e "$PRIVKEY" || test ! -e "$CERT" || test ! -e "$CACERT"; then
+        if test ! -e "$PRIVKEY"; then
+            echo "$PRIVKEY: private key missing" >&2
+        fi
+        if test ! -e "$CERT"; then
+            echo "$CERT: certificate for private key missing" >&2
+        fi
+        if test ! -e "$CACERT"; then
+            echo "$CACERT: CA certificate missing" >&2
+        fi
+        echo "Run ofp-switch-setup or edit /etc/default/openflow-switch to configure" >&2
+        if test "$MODE" = discovery; then
+            echo "You may also delete or rename $PRIVKEY to disable SSL requirement" >&2
+        fi
+        exit 1
+    fi
+    SSL_OPTS="--private-key=$PRIVKEY --certificate=$CERT --ca-cert=$CACERT"
+}
+
 case "$1" in
     start)
         if test -z "$NETDEVS"; then
@@ -115,40 +141,36 @@ case "$1" in
             echo "Run ofp-switch-setup or edit /etc/default/openflow-switch to configure" >&2
             exit 0
         fi
-        if test -z "$CONTROLLER"; then
-            echo "$default: No controller configured, switch disabled" >&2
-            echo "Run ofp-switch-setup or edit /etc/default/openflow-switch to configure" >&2
-            exit 0
-        fi
-        if test "$IN_BAND" != yes && test "$IN_BAND" != no; then
-            echo "$default: IN_BAND must set to 'yes' or 'no'" >&2
+        if test "$MODE" = discovery; then
+            unset CONTROLLER
+        elif test "$MODE" = in-band || test "$MODE" = out-of-band; then
+            if test -z "$CONTROLLER"; then
+                echo "$default: No controller configured and not configured for discovery, switch disabled" >&2
+                echo "Run ofp-switch-setup or edit /etc/default/openflow-switch to configure" >&2
+                exit 0
+            fi
+        else
+            echo "$default: MODE must set to 'discovery', 'in-band', or 'out-of-band'" >&2
             echo "Run ofp-switch-setup or edit /etc/default/openflow-switch to configure" >&2
             exit 1
         fi
+        : ${PRIVKEY:=/etc/openflow-switch/of0-privkey.pem}
+        : ${CERT:=/etc/openflow-switch/of0-cert.pem}
+        : ${CACERT:=/etc/openflow-switch/cacert.pem}
         case $CONTROLLER in
+            '')
+                # Discovery mode.
+                if test -e "$PRIVKEY"; then
+                    configure_ssl
+                fi
+                ;;
             tcp:*)
                 ;;
             ssl:*)
-                : ${PRIVKEY:=/etc/openflow-switch/of0-privkey.pem}
-                : ${CERT:=/etc/openflow-switch/of0-cert.pem}
-                : ${CACERT:=/etc/openflow-switch/cacert.pem}
-                if test ! -e "$PRIVKEY" || test ! -e "$CERT" ||
-                    test ! -e "$CACERT"; then
-                    if test ! -e "$PRIVKEY"; then
-                        echo "$PRIVKEY: private key missing" >&2
-                    fi
-                    if test ! -e "$CERT"; then
-                        echo "$CERT: certificate for private key missing" >&2
-                    fi
-                    if test ! -e "$CACERT"; then
-                        echo "$CACERT: CA certificate missing" >&2
-                    fi
-                    echo "Run ofp-switch-setup or edit /etc/default/openflow-switch to configure" >&2
-                    exit 1
-                fi
+                configure_ssl
                 ;;
             *)
-                echo "$default: CONTROLLER must be in the form 'ssl:HOST[:PORT]' or 'tcp:HOST[:PORT]'" >&2
+                echo "$default: CONTROLLER must be in the form 'ssl:HOST[:PORT]' or 'tcp:HOST[:PORT]' when not in discovery mode" >&2
                 echo "Run ofp-switch-setup or edit /etc/default/openflow-switch to configure" >&2
                 exit 1
         esac
@@ -174,7 +196,7 @@ case "$1" in
             must_succeed "Adding $netdev to datapath" dpctl addif nl:0 $netdev
         done
 
-        if test "$IN_BAND" = yes; then
+        if test "$MODE" = in-band; then
             if test "$SWITCH_IP" = dhcp; then
                 must_succeed "Temporarily disabling of0" ifconfig of0 down
             else
@@ -187,14 +209,14 @@ case "$1" in
        echo -n "Starting $DESC: "
        start-stop-daemon --start --quiet --pidfile $PIDFILE \
            --exec $DAEMON -- nl:0 $CONTROLLER --detach --pidfile=$PIDFILE \
-            $DAEMON_OPTS $SSL_OPTS
+            --verbose=ANY:console:emer $DAEMON_OPTS $SSL_OPTS
         if running; then
             echo "$NAME."
         else
             echo " ERROR."
         fi
 
-        if test "$IN_BAND" = yes && test "$SWITCH_IP" = dhcp; then
+        if test "$MODE" = in-band && test "$SWITCH_IP" = dhcp; then
             echo -n "Starting dhclient on of0: "
            start-stop-daemon --start --quiet --pidfile $DHCLIENT_PIDFILE \
                --exec /sbin/dhclient -- -q -pf $DHCLIENT_PIDFILE of0
index 96db7278b26dc45f0612f65d94b8c8fad228b816..2c85136180e5871ed1dc4271c29f79942331f2dc 100644 (file)
@@ -1,4 +1,6 @@
-switch/switch usr/sbin
-secchan/secchan usr/sbin
-utilities/dpctl usr/sbin
+_debian/switch/switch usr/sbin
+_debian/secchan/secchan usr/sbin
+_debian/utilities/dpctl usr/sbin
+_debian/utilities/ofp-discover usr/sbin
+_debian/utilities/ofp-kill usr/sbin
 debian/ofp-switch-setup usr/sbin
index 905d0606dc4034963a0d6e2f3ac26268bb310f9a..b012d8dbfbb28427b0adaab7c8e2ffef81628892 100644 (file)
@@ -1,4 +1,6 @@
 debian/ofp-switch-setup.8
-secchan/secchan.8
-switch/switch.8
+_debian/secchan/secchan.8
+_debian/switch/switch.8
+_debian/utilities/ofp-discover.8
+_debian/utilities/ofp-kill.8
 utilities/dpctl.8
diff --git a/debian/openflow-switch.postinst b/debian/openflow-switch.postinst
deleted file mode 100755 (executable)
index c8dc9a4..0000000
+++ /dev/null
@@ -1,14 +0,0 @@
-#!/bin/sh
-# postinst script for openflow-switch
-#
-# see: dh_installdeb(1)
-
-set -e
-
-. /usr/share/debconf/confmodule
-
-#DEBHELPER#
-
-exit 0
-
-
index c6d4b5e4cb0030b27bf960c308acd49626570caa..7876109798ec02b9f3081500ed3105f0e8947dda 100644 (file)
@@ -35,24 +35,59 @@ _Description: Some Network Devices Have IP or IPv6 Addresses
  If this is an unintentional mistake, move back and fix the selection,
  or de-configure the IP or IPv6 from these network devices.
 
-Template: openflow-switch/band
+Template: openflow-switch/mode
 Type: select
-_Choices: in-band, out-of-band
-Default: in-band
+_Choices: discovery, in-band, out-of-band
+Default: discovery
 _Description: Switch-to-controller access method:
  The OpenFlow switch must be able to contact the OpenFlow controller over
- the network.  It can do so in one of two ways:
+ the network.  It can do so in one of three ways:
  .
- in-band: A single network is used for OpenFlow traffic and other data
- traffic; that is, the switch contacts the controller over one of the
- network devices selected as OpenFlow switch netdevs in the previous
- question.  This is the most common case.
+ discovery: A single network is used for OpenFlow traffic and other
+ data traffic; that is, the switch contacts the controller over one of
+ the network devices selected as OpenFlow switch network devices in
+ the previous question.  The switch automatically determines the
+ location of the controller using a DHCP request with an
+ OpenFlow-specific vendor option.  This is the most common case.
+ .
+ in-band: As above, but the location of the controller is manually
+ configured.
  .
  out-of-band: OpenFlow traffic uses a network separate from the data traffic
  that it controls.  If this is the case, the control network must already
  be configured on a network device other than one of those selected as
  an OpenFlow switch netdev in the previous question.
 
+Template: openflow-switch/discover
+Type: note
+_Description: Preparing to discover controller.
+ The setup program will now attempt to discover the OpenFlow controller.
+ Controller discovery may take up to 30 seconds.  Please be patient.
+ .
+ See secchan(8) for instructions on how to configure a DHCP server for
+ controller discovery.
+
+Template: openflow-switch/discovery-failure
+Type: error
+_Description: Controller discovery failed.
+ The controller's location could not be determined automatically.
+ .
+ Ensure that the OpenFlow DHCP server is properly configured.  See
+ secchan(8) for instructions on how to configure a DHCP server for
+ controller discovery.
+
+Template: openflow-switch/discovery-success
+Type: boolean
+Default: true
+_Description: Use discovered settings?
+ Controller discovery obtained the following settings:
+ .
+ Controller location: ${controller-vconn}
+ .
+ PKI URL: ${pki-uri}
+ .
+ Please verify that these settings are correct.
+
 Template: openflow-switch/switch-ip
 Type: string
 Default: dhcp
@@ -89,12 +124,16 @@ _Description: The controller location is invalid.
  connect to the controller over SSL (recommended for security) or
  "tcp:HOST[:PORT]" to connect over cleartext TCP.
 
-Template: openflow-switch/pki-host
+Template: openflow-switch/pki-uri
 Type: string
-_Description: OpenFlow PKI server host name:
- Specify the host name or IP address of the server that hosts the OpenFlow
- public key infrastructure (PKI).  This is usually the same host as the
- OpenFlow controller.
+_Description: OpenFlow PKI server host name or URL:
+ Specify a URL to the OpenFlow public key infrastructure (PKI).  If a
+ host name or IP address is specified in place of a URL, then
+ http://<host>/openflow/pki/ will be used,
+ where <host> is the specified host name or IP address.
+ .
+ The OpenFlow PKI is usually on the same machine as the OpenFlow
+ controller.
  .
  The setup process will connect to the OpenFlow PKI server over
  HTTP, using the system's configured default HTTP proxy (if any).
@@ -105,7 +144,7 @@ _Description: The switch CA certificate could not be retrieved.
  Retrieval of ${url} failed, with the following status: "${error}".
  .
  Ensure that the OpenFlow PKI server is correctly configured and
- available on ${pki-host}.  If the system is configured to use an HTTP
+ available at ${pki-uri}.  If the system is configured to use an HTTP
  proxy, also make sure that the HTTP proxy is available and that the
  PKI server can be reached through it.
 
@@ -147,7 +186,7 @@ _Description: The certificate request could not be sent.
  Posting to ${url} failed, with the following status: "${error}".
  .
  Ensure that the OpenFlow PKI server is correctly configured and
- available on ${pki-host}.
+ available at ${pki-uri}.
 
 Template: openflow-switch/fetch-switch-cert
 Type: select
@@ -179,7 +218,7 @@ _Description: Signed switch certificate could not be retrieved.
  .
  This probably indicates that the switch's certificate request has not
  yet been signed.  If this is the problem, it may be fixed by signing
- the certificate request at ${pki-host}, then trying to fetch the
+ the certificate request at ${pki-uri}, then trying to fetch the
  signed switch certificate again.
 
 Template: openflow-switch/complete
index 927eb09c721b179e84f3c4b58713ffe2ddc511eb..96f7f02c8167bab5c980da742ef154cffdeac503 100644 (file)
@@ -8,7 +8,7 @@ msgid ""
 msgstr ""
 "Project-Id-Version: PACKAGE VERSION\n"
 "Report-Msgid-Bugs-To: openflow-dev@lists.stanford.edu\n"
-"POT-Creation-Date: 2008-06-12 14:45-0700\n"
+"POT-Creation-Date: 2008-07-23 14:55-0700\n"
 "PO-Revision-Date: YEAR-MO-DA HO:MI+ZONE\n"
 "Last-Translator: FULL NAME <EMAIL@ADDRESS>\n"
 "Language-Team: LANGUAGE <LL@li.org>\n"
@@ -104,7 +104,7 @@ msgstr ""
 #. Type: select
 #. Choices
 #: ../openflow-switch.templates:4001
-msgid "in-band, out-of-band"
+msgid "discovery, in-band, out-of-band"
 msgstr ""
 
 #. Type: select
@@ -118,17 +118,26 @@ msgstr ""
 #: ../openflow-switch.templates:4002
 msgid ""
 "The OpenFlow switch must be able to contact the OpenFlow controller over the "
-"network.  It can do so in one of two ways:"
+"network.  It can do so in one of three ways:"
 msgstr ""
 
 #. Type: select
 #. Description
 #: ../openflow-switch.templates:4002
 msgid ""
-"in-band: A single network is used for OpenFlow traffic and other data "
+"discovery: A single network is used for OpenFlow traffic and other data "
 "traffic; that is, the switch contacts the controller over one of the network "
-"devices selected as OpenFlow switch netdevs in the previous question.  This "
-"is the most common case."
+"devices selected as OpenFlow switch network devices in the previous "
+"question.  The switch automatically determines the location of the "
+"controller using a DHCP request with an OpenFlow-specific vendor option.  "
+"This is the most common case."
+msgstr ""
+
+#. Type: select
+#. Description
+#: ../openflow-switch.templates:4002
+msgid ""
+"in-band: As above, but the location of the controller is manually configured."
 msgstr ""
 
 #. Type: select
@@ -141,15 +150,87 @@ msgid ""
 "OpenFlow switch netdev in the previous question."
 msgstr ""
 
-#. Type: string
+#. Type: note
 #. Description
 #: ../openflow-switch.templates:5001
+msgid "Preparing to discover controller."
+msgstr ""
+
+#. Type: note
+#. Description
+#: ../openflow-switch.templates:5001
+msgid ""
+"The setup program will now attempt to discover the OpenFlow controller. "
+"Controller discovery may take up to 30 seconds.  Please be patient."
+msgstr ""
+
+#. Type: note
+#. Description
+#: ../openflow-switch.templates:5001
+msgid ""
+"See secchan(8) for instructions on how to configure a DHCP server for "
+"controller discovery."
+msgstr ""
+
+#. Type: error
+#. Description
+#: ../openflow-switch.templates:6001
+msgid "Controller discovery failed."
+msgstr ""
+
+#. Type: error
+#. Description
+#: ../openflow-switch.templates:6001
+msgid "The controller's location could not be determined automatically."
+msgstr ""
+
+#. Type: error
+#. Description
+#: ../openflow-switch.templates:6001
+msgid ""
+"Ensure that the OpenFlow DHCP server is properly configured.  See secchan(8) "
+"for instructions on how to configure a DHCP server for controller discovery."
+msgstr ""
+
+#. Type: boolean
+#. Description
+#: ../openflow-switch.templates:7001
+msgid "Use discovered settings?"
+msgstr ""
+
+#. Type: boolean
+#. Description
+#: ../openflow-switch.templates:7001
+msgid "Controller discovery obtained the following settings:"
+msgstr ""
+
+#. Type: boolean
+#. Description
+#: ../openflow-switch.templates:7001
+msgid "Controller location: ${controller-vconn}"
+msgstr ""
+
+#. Type: boolean
+#. Description
+#: ../openflow-switch.templates:7001
+msgid "PKI URL: ${pki-uri}"
+msgstr ""
+
+#. Type: boolean
+#. Description
+#: ../openflow-switch.templates:7001
+msgid "Please verify that these settings are correct."
+msgstr ""
+
+#. Type: string
+#. Description
+#: ../openflow-switch.templates:8001
 msgid "Switch IP address:"
 msgstr ""
 
 #. Type: string
 #. Description
-#: ../openflow-switch.templates:5001
+#: ../openflow-switch.templates:8001
 msgid ""
 "For in-band communication with the controller, the OpenFlow switch must be "
 "able to determine its own IP address.  Its IP address may be configured "
@@ -158,13 +239,13 @@ msgstr ""
 
 #. Type: string
 #. Description
-#: ../openflow-switch.templates:5001
+#: ../openflow-switch.templates:8001
 msgid "For static configuration, specify the switch's IP address as a string."
 msgstr ""
 
 #. Type: string
 #. Description
-#: ../openflow-switch.templates:5001
+#: ../openflow-switch.templates:8001
 msgid ""
 "For dynamic configuration with DHCP (the most common case), specify \"dhcp"
 "\". Configuration with DHCP will only work reliably if the network topology "
@@ -174,13 +255,13 @@ msgstr ""
 
 #. Type: error
 #. Description
-#: ../openflow-switch.templates:6001
+#: ../openflow-switch.templates:9001
 msgid "The switch IP address is invalid."
 msgstr ""
 
 #. Type: error
 #. Description
-#: ../openflow-switch.templates:6001
+#: ../openflow-switch.templates:9001
 msgid ""
 "The switch IP address must specified as \"dhcp\" or a valid IP address in "
 "dotted-octet form (e.g. \"1.2.3.4\")."
@@ -188,13 +269,13 @@ msgstr ""
 
 #. Type: string
 #. Description
-#: ../openflow-switch.templates:7001
+#: ../openflow-switch.templates:10001
 msgid "Controller location:"
 msgstr ""
 
 #. Type: string
 #. Description
-#: ../openflow-switch.templates:7001
+#: ../openflow-switch.templates:10001
 msgid ""
 "Specify how the OpenFlow switch should connect to the OpenFlow controller. "
 "The value should be in form \"ssl:HOST[:PORT]\" to connect to the controller "
@@ -204,13 +285,13 @@ msgstr ""
 
 #. Type: error
 #. Description
-#: ../openflow-switch.templates:8001
+#: ../openflow-switch.templates:11001
 msgid "The controller location is invalid."
 msgstr ""
 
 #. Type: error
 #. Description
-#: ../openflow-switch.templates:8001
+#: ../openflow-switch.templates:11001
 msgid ""
 "The controller location must be specifed as \"ssl:HOST[:PORT]\" to connect "
 "to the controller over SSL (recommended for security) or \"tcp:HOST[:PORT]\" "
@@ -219,22 +300,30 @@ msgstr ""
 
 #. Type: string
 #. Description
-#: ../openflow-switch.templates:9001
-msgid "OpenFlow PKI server host name:"
+#: ../openflow-switch.templates:12001
+msgid "OpenFlow PKI server host name or URL:"
 msgstr ""
 
 #. Type: string
 #. Description
-#: ../openflow-switch.templates:9001
+#: ../openflow-switch.templates:12001
 msgid ""
-"Specify the host name or IP address of the server that hosts the OpenFlow "
-"public key infrastructure (PKI).  This is usually the same host as the "
-"OpenFlow controller."
+"Specify a URL to the OpenFlow public key infrastructure (PKI).  If a host "
+"name or IP address is specified in place of a URL, then http://<host>/"
+"openflow/pki/ will be used, where <host> is the specified host name or IP "
+"address."
 msgstr ""
 
 #. Type: string
 #. Description
-#: ../openflow-switch.templates:9001
+#: ../openflow-switch.templates:12001
+msgid ""
+"The OpenFlow PKI is usually on the same machine as the OpenFlow controller."
+msgstr ""
+
+#. Type: string
+#. Description
+#: ../openflow-switch.templates:12001
 msgid ""
 "The setup process will connect to the OpenFlow PKI server over HTTP, using "
 "the system's configured default HTTP proxy (if any)."
@@ -242,22 +331,22 @@ msgstr ""
 
 #. Type: error
 #. Description
-#: ../openflow-switch.templates:10001
+#: ../openflow-switch.templates:13001
 msgid "The switch CA certificate could not be retrieved."
 msgstr ""
 
 #. Type: error
 #. Description
-#: ../openflow-switch.templates:10001
+#: ../openflow-switch.templates:13001
 msgid "Retrieval of ${url} failed, with the following status: \"${error}\"."
 msgstr ""
 
 #. Type: error
 #. Description
-#: ../openflow-switch.templates:10001
+#: ../openflow-switch.templates:13001
 msgid ""
-"Ensure that the OpenFlow PKI server is correctly configured and available on "
-"${pki-host}.  If the system is configured to use an HTTP proxy, also make "
+"Ensure that the OpenFlow PKI server is correctly configured and available at "
+"${pki-uri}.  If the system is configured to use an HTTP proxy, also make "
 "sure that the HTTP proxy is available and that the PKI server can be reached "
 "through it."
 msgstr ""
@@ -268,20 +357,20 @@ msgstr ""
 #. Choices
 #. Type: select
 #. Choices
-#: ../openflow-switch.templates:11001 ../openflow-switch.templates:12001
-#: ../openflow-switch.templates:14001
+#: ../openflow-switch.templates:14001 ../openflow-switch.templates:15001
+#: ../openflow-switch.templates:17001
 msgid "yes, no"
 msgstr ""
 
 #. Type: select
 #. Description
-#: ../openflow-switch.templates:11002
+#: ../openflow-switch.templates:14002
 msgid "Is ${fingerprint} the controller CA's fingerprint?"
 msgstr ""
 
 #. Type: select
 #. Description
-#: ../openflow-switch.templates:11002
+#: ../openflow-switch.templates:14002
 msgid ""
 "If a man-in-the-middle attack is possible in your network environment, check "
 "that the controller CA's fingerprint is really ${fingerprint}.  Answer \"yes"
@@ -290,7 +379,7 @@ msgstr ""
 
 #. Type: select
 #. Description
-#: ../openflow-switch.templates:11002
+#: ../openflow-switch.templates:14002
 msgid ""
 "If a man-in-the-middle attack is not a concern, there is no need to verify "
 "the fingerprint.  Simply answer \"yes\"."
@@ -298,13 +387,13 @@ msgstr ""
 
 #. Type: select
 #. Description
-#: ../openflow-switch.templates:12002
+#: ../openflow-switch.templates:15002
 msgid "Send certificate request to switch CA?"
 msgstr ""
 
 #. Type: select
 #. Description
-#: ../openflow-switch.templates:12002
+#: ../openflow-switch.templates:15002
 msgid ""
 "Before it can connect to the controller over SSL, the OpenFlow switch's key "
 "must be signed by the switch certificate authority (CA) located on the "
@@ -314,7 +403,7 @@ msgstr ""
 
 #. Type: select
 #. Description
-#: ../openflow-switch.templates:12002
+#: ../openflow-switch.templates:15002
 msgid ""
 "Answer \"yes\" to send a signing request to the switch CA now.  This is "
 "ordinarily the correct choice.  There is no harm in sending a given signing "
@@ -323,7 +412,7 @@ msgstr ""
 
 #. Type: select
 #. Description
-#: ../openflow-switch.templates:12002
+#: ../openflow-switch.templates:15002
 msgid ""
 "Answer \"no\" to skip sending a signing request to the switch CA. Unless the "
 "request has already been sent to the switch CA, manual sending of the "
@@ -332,33 +421,33 @@ msgstr ""
 
 #. Type: error
 #. Description
-#: ../openflow-switch.templates:13001
+#: ../openflow-switch.templates:16001
 msgid "The certificate request could not be sent."
 msgstr ""
 
 #. Type: error
 #. Description
-#: ../openflow-switch.templates:13001
+#: ../openflow-switch.templates:16001
 msgid "Posting to ${url} failed, with the following status: \"${error}\"."
 msgstr ""
 
 #. Type: error
 #. Description
-#: ../openflow-switch.templates:13001
+#: ../openflow-switch.templates:16001
 msgid ""
-"Ensure that the OpenFlow PKI server is correctly configured and available on "
-"${pki-host}."
+"Ensure that the OpenFlow PKI server is correctly configured and available at "
+"${pki-uri}."
 msgstr ""
 
 #. Type: select
 #. Description
-#: ../openflow-switch.templates:14002
+#: ../openflow-switch.templates:17002
 msgid "Fetch signed switch certificate from PKI server?"
 msgstr ""
 
 #. Type: select
 #. Description
-#: ../openflow-switch.templates:14002
+#: ../openflow-switch.templates:17002
 msgid ""
 "Before it can connect to the controller over SSL, the OpenFlow switch's key "
 "must be signed by the switch certificate authority (CA) located on the "
@@ -368,7 +457,7 @@ msgstr ""
 
 #. Type: select
 #. Description
-#: ../openflow-switch.templates:14002
+#: ../openflow-switch.templates:17002
 msgid ""
 "At this point, a signing request has been sent to the switch CA (or sending "
 "a request has been manually skipped), but the signed certificate has not yet "
@@ -378,7 +467,7 @@ msgstr ""
 
 #. Type: select
 #. Description
-#: ../openflow-switch.templates:14002
+#: ../openflow-switch.templates:17002
 msgid ""
 "Answer \"yes\" to attempt to retrieve the signed switch certificate from the "
 "switch CA.  If the switch certificate request has been signed at the PKI "
@@ -387,7 +476,7 @@ msgstr ""
 
 #. Type: select
 #. Description
-#: ../openflow-switch.templates:14002
+#: ../openflow-switch.templates:17002
 msgid ""
 "Answer \"no\" to postpone switch configuration.  The configuration process "
 "must be restarted later, when the switch certificate request has been signed."
@@ -395,13 +484,13 @@ msgstr ""
 
 #. Type: error
 #. Description
-#: ../openflow-switch.templates:15001
+#: ../openflow-switch.templates:18001
 msgid "Signed switch certificate could not be retrieved."
 msgstr ""
 
 #. Type: error
 #. Description
-#: ../openflow-switch.templates:15001
+#: ../openflow-switch.templates:18001
 msgid ""
 "The signed switch certificate could not be retrieved from the switch CA: "
 "retrieval of ${url} failed, with the following status: \"${error}\"."
@@ -409,23 +498,23 @@ msgstr ""
 
 #. Type: error
 #. Description
-#: ../openflow-switch.templates:15001
+#: ../openflow-switch.templates:18001
 msgid ""
 "This probably indicates that the switch's certificate request has not yet "
 "been signed.  If this is the problem, it may be fixed by signing the "
-"certificate request at ${pki-host}, then trying to fetch the signed switch "
+"certificate request at ${pki-uri}, then trying to fetch the signed switch "
 "certificate again."
 msgstr ""
 
 #. Type: note
 #. Description
-#: ../openflow-switch.templates:16001
+#: ../openflow-switch.templates:19001
 msgid "OpenFlow Switch Setup Finished"
 msgstr ""
 
 #. Type: note
 #. Description
-#: ../openflow-switch.templates:16001
+#: ../openflow-switch.templates:19001
 msgid ""
 "Setup of this OpenFlow switch is finished.  Complete the setup procedure to "
 "enable the switch."
index ab44b77f667ea0d4bd17d0351666e40d77d7fdd4..ecd706f81898b58e084a91f540fc6512cf9c839d 100755 (executable)
@@ -29,8 +29,10 @@ configure: configure-stamp
 configure-stamp:
        dh_testdir
        test -e configure || ./boot.sh
-       test -e Makefile || \
-               ./configure --prefix=/usr --localstatedir=/var --enable-ssl
+       test -d _debian || mkdir _debian
+       cd _debian && ( \
+               test -e Makefile || \
+               ../configure --prefix=/usr --localstatedir=/var --enable-ssl)
        touch configure-stamp
 
 #Architecture 
@@ -38,18 +40,19 @@ build: build-arch build-indep
 
 build-arch: build-arch-stamp
 build-arch-stamp: configure-stamp 
-       $(MAKE)
+       $(MAKE) -C _debian
        touch $@
 
 build-indep: build-indep-stamp
 build-indep-stamp: configure-stamp 
-       $(MAKE) dist distdir=openflow
+       $(MAKE) -C _debian dist distdir=openflow
        touch $@
 
 clean:
        dh_testdir
        dh_testroot
        rm -f build-arch-stamp build-indep-stamp configure-stamp
+       rm -rf _debian
        [ ! -f Makefile ] || $(MAKE) distclean
        dh_clean 
        debconf-updatepo
@@ -67,7 +70,7 @@ endif
 
 kdist_clean:
        dh_clean
-       make KERNELDIR=$(KSRC) KVERREL=$(KVERS) clean
+       rm -rf openflow
 
 kdist_config: prep-deb-files
 
@@ -97,7 +100,7 @@ install-indep: build-indep
        dh_testroot
        dh_clean -k -i 
        dh_installdirs -i
-       cp openflow.tar.gz $(MODDIR)
+       cp _debian/openflow.tar.gz $(MODDIR)
        cd debian; cp changelog control compat *.modules.in rules $(MODDIR)/debian
        cd debian/openflow-datapath-source/usr/src && tar -c modules | bzip2 -9 > openflow-datapath.tar.bz2 && rm -rf modules
        install -m644 debian/openflow-pki.apache2 debian/openflow-pki/etc/apache2/sites-available/openflow-pki
@@ -108,7 +111,7 @@ install-arch: build-arch
        dh_testroot
        dh_clean -k -s 
        dh_installdirs -s
-       $(MAKE) DESTDIR=$(CURDIR)/debian/openflow install
+       $(MAKE) -C _debian DESTDIR=$(CURDIR)/debian/openflow install
        cp debian/openflow-switch.overrides debian/openflow-switch/usr/share/lintian/overrides/openflow-switch
        dh_install -s
 
index 208cee9c7f1e8ade733fd88fb0d63ebc080adc23..d7273e02666a583c487905cff42db6f18f0185bf 100644 (file)
@@ -34,6 +34,7 @@
 #ifndef DAEMON_H
 #define DAEMON_H 1
 
+char *make_pidfile_name(const char *name);
 void set_pidfile(const char *name);
 void set_detach(void);
 void daemonize(void);
index 960e81ec735e7ace15ef4226bb3a0d18d9743119..9019ff116255de4b44ab7e219f9cc535ab810aa2 100644 (file)
@@ -39,6 +39,7 @@
 
 struct dhclient;
 struct dhcp_msg;
+struct netdev;
 int dhclient_create(const char *netdev,
                     void (*modify_request)(struct dhcp_msg *, void *aux),
                     bool (*validate_offer)(const struct dhcp_msg *, void *aux),
@@ -53,8 +54,12 @@ bool dhclient_changed(struct dhclient *);
 
 uint32_t dhclient_get_ip(const struct dhclient *);
 uint32_t dhclient_get_netmask(const struct dhclient *);
+uint32_t dhclient_get_router(const struct dhclient *);
 const struct dhcp_msg *dhclient_get_config(const struct dhclient *);
 
+int dhclient_configure_netdev(struct dhclient *);
+int dhclient_update_resolv_conf(struct dhclient *);
+
 void dhclient_run(struct dhclient *);
 void dhclient_wait(struct dhclient *);
 
index 82f3d3e0111ce908eed92367f27d6cb41036250a..2d00f0261a017d3262dcf2d220360c04878cc3fd 100644 (file)
@@ -193,7 +193,8 @@ const char *dhcp_type_name(enum dhcp_msg_type);
     DHCP_OPT(T2,                       59, SECS,    1, 1)               \
     DHCP_OPT(VENDOR_CLASS,             60, STRING,  1, SIZE_MAX)        \
     DHCP_OPT(CLIENT_ID,                61, UINT8,   2, SIZE_MAX)        \
-    DHCP_VNDOPT(OFP_CONTROLLER_VCONN,   1, STRING,  1, SIZE_MAX)
+    DHCP_VNDOPT(OFP_CONTROLLER_VCONN,   1, STRING,  1, SIZE_MAX)        \
+    DHCP_VNDOPT(OFP_PKI_URI,            2, STRING,  1, SIZE_MAX)
 
 /* Shorthand for defining vendor options (used above). */
 #define DHCP_VNDOPT(NAME, CODE, ARG, MIN, MAX) \
@@ -267,7 +268,8 @@ bool dhcp_msg_get_uint16(const struct dhcp_msg *, int code,
                          size_t offset, uint16_t *);
 const char *dhcp_option_to_string(const struct dhcp_option *, int code,
                                   struct ds *);
-const char *dhcp_msg_to_string(const struct dhcp_msg *, struct ds *);
+const char *dhcp_msg_to_string(const struct dhcp_msg *, bool multiline,
+                               struct ds *);
 int dhcp_parse(struct dhcp_msg *, const struct buffer *);
 void dhcp_assemble(const struct dhcp_msg *, struct buffer *);
 
index 3525e166bf832377ba3c86a2210ad737a4c78bdc..2c4746c69256205da4f5cea2bfa3daf1d7cca73d 100644 (file)
@@ -42,7 +42,9 @@
 #include <stdint.h>
 #endif
 
-#ifndef __cplusplus
+#ifdef SWIG
+#define OFP_ASSERT(EXPR)        /* SWIG can't handle OFP_ASSERT. */
+#elif !defined(__cplusplus)
 /* Build-time assertion for use in a declaration context. */
 #define OFP_ASSERT(EXPR)                                                \
         extern int (*build_assert(void))[ sizeof(struct {               \
 #define OFP_ASSERT BOOST_STATIC_ASSERT
 #endif /* __cplusplus */
 
+#ifndef SWIG
+#define OFP_PACKED __attribute__((packed))
+#else
+#define OFP_PACKED              /* SWIG doesn't understand __attribute. */
+#endif
+
 /* Maximum length of a OpenFlow packet. */
 #define OFP_MAXLEN (sizeof(struct ofp_switch_features) \
         + (sizeof(struct ofp_phy_port) * OFPP_MAX) + 200)
@@ -280,7 +288,7 @@ struct ofp_action {
         struct ofp_action_output output; /* OFPAT_OUTPUT: output struct. */
         uint16_t vlan_id;                /* OFPAT_SET_DL_VLAN: VLAN id. */
         uint8_t  dl_addr[OFP_ETH_ALEN];  /* OFPAT_SET_DL_SRC/DST */
-        uint32_t nw_addr __attribute__((packed)); /* OFPAT_SET_NW_SRC/DST */
+        uint32_t nw_addr OFP_PACKED;     /* OFPAT_SET_NW_SRC/DST */
         uint16_t tp;                     /* OFPAT_SET_TP_SRC/DST */
     } arg;
 };
index fe5b4322a5efc56482e4bf2d6fb1d85e0de8c828..2ecaa419a6656b0799f8c2b8dc64e1924cf434a5 100644 (file)
@@ -76,6 +76,7 @@ enum vlog_facility vlog_get_facility_val(const char *name);
         VLOG_MODULE(mac_learning)               \
         VLOG_MODULE(netdev)                     \
         VLOG_MODULE(netlink)                    \
+        VLOG_MODULE(ofp_discover)               \
         VLOG_MODULE(poll_loop)                  \
         VLOG_MODULE(secchan)                    \
         VLOG_MODULE(rconn)                      \
index 404d925ede558ee20baf7b6b3a39184550a82491..bedd77fafb8393582b4fbaa682c754d0e332b707 100644 (file)
@@ -1,8 +1,8 @@
 include ../Make.vars
 
-noinst_LTLIBRARIES = libopenflow.la
+noinst_LIBRARIES = libopenflow.a
 
-libopenflow_la_SOURCES = \
+libopenflow_a_SOURCES = \
        buffer.c \
        command-line.c \
        csum.c \
@@ -31,16 +31,16 @@ libopenflow_la_SOURCES = \
        vlog.c
 
 if HAVE_NETLINK
-libopenflow_la_SOURCES += \
+libopenflow_a_SOURCES += \
        dpif.c \
        netlink.c \
        vconn-netlink.c
 endif
 
 if HAVE_OPENSSL
-libopenflow_la_SOURCES += \
+libopenflow_a_SOURCES += \
        vconn-ssl.c 
-nodist_libopenflow_la_SOURCES = dhparams.c
+nodist_libopenflow_a_SOURCES = dhparams.c
 dhparams.c: dh1024.pem dh2048.pem dh4096.pem
        (echo '#include "dhparams.h"' &&                        \
         openssl dhparam -C -in $(srcdir)/dh1024.pem -noout &&  \
index 1abe240647f8721ed93367c11de0bc14a6447333..2014248dcb8c744950e3428354901a2994d82c32 100644 (file)
@@ -31,6 +31,7 @@
  * derivatives without specific, written prior permission.
  */
 
+#include <config.h>
 #include "buffer.h"
 #include <assert.h>
 #include <stdlib.h>
index 5ad98d76d08ba9e5109ea0335ac55762857c0498..7ab08a4d3d96bf3198ac698fb09132197a403627 100644 (file)
@@ -31,6 +31,7 @@
  * derivatives without specific, written prior permission.
  */
 
+#include <config.h>
 #include "command-line.h"
 #include <getopt.h>
 #include <limits.h>
index 6e9b06b028fec7a6839475a281c5444d9b4fdb98..81b3dc13b493faabec2537ed5e9d4062654219be 100644 (file)
@@ -31,6 +31,7 @@
  * derivatives without specific, written prior permission.
  */
 
+#include <config.h>
 #include "csum.h"
 
 /* Returns the IP checksum of the 'n' bytes in 'data'. */
index e3c2b13e007b7e96e34c7847c6a10c95597a91cb..b5977172203d0a351c829f938119681a4c555003 100644 (file)
  * derivatives without specific, written prior permission.
  */
 
+#include <config.h>
 #include "daemon.h"
 #include <errno.h>
+#include <fcntl.h>
 #include <stdlib.h>
 #include <string.h>
 #include <unistd.h>
@@ -48,15 +50,27 @@ static bool detach;
 /* Name of pidfile (null if none). */
 static char *pidfile;
 
+/* Returns the file name that would be used for a pidfile if 'name' were
+ * provided to set_pidfile().  The caller must free the returned string. */
+char *
+make_pidfile_name(const char *name) 
+{
+    return (!name ? xasprintf("%s/%s.pid", RUNDIR, program_name)
+            : *name == '/' ? xstrdup(name)
+            : xasprintf("%s/%s", RUNDIR, name));
+}
+
 /* Sets up a following call to daemonize() to create a pidfile named 'name'.
  * If 'name' begins with '/', then it is treated as an absolute path.
  * Otherwise, it is taken relative to RUNDIR, which is $(prefix)/var/run by
- * default. */
+ * default.
+ *
+ * If 'name' is null, then program_name followed by ".pid" is used. */
 void
 set_pidfile(const char *name)
 {
     free(pidfile);
-    pidfile = *name == '/' ? xstrdup(name) : xasprintf("%s/%s", RUNDIR, name);
+    pidfile = make_pidfile_name(name);
 }
 
 /* Sets up a following call to daemonize() to detach from the foreground
@@ -67,25 +81,56 @@ set_detach(void)
     detach = true;
 }
 
-/* If a pidfile has been configured, creates it and stores 'pid' in it.  It is
- * the caller's responsibility to make sure that the pidfile will eventually
- * be deleted. */
+/* If a pidfile has been configured, creates it and stores the running process'
+ * pid init.  Ensures that the pidfile will be deleted when the process
+ * exits. */
 static void
-make_pidfile(pid_t pid)
+make_pidfile(void)
 {
     if (pidfile) {
-        FILE *file;
+        /* Create pidfile via temporary file, so that observers never see an
+         * empty pidfile or an unlocked pidfile. */
+        long int pid = getpid();
+        char *tmpfile;
+        int fd;
 
-        file = fopen(pidfile, "w");
-        if (file) {
-            fprintf(file, "%ld\n", (long int) pid);
-            fclose(file);
+        tmpfile = xasprintf("%s.tmp%ld", pidfile, pid);
+        fatal_signal_add_file_to_unlink(tmpfile);
+        fd = open(tmpfile, O_CREAT | O_WRONLY | O_TRUNC, 0666);
+        if (fd >= 0) {
+            struct flock lck;
+            lck.l_type = F_WRLCK;
+            lck.l_whence = SEEK_SET;
+            lck.l_start = 0;
+            lck.l_len = 0;
+            if (fcntl(fd, F_SETLK, &lck) >= 0) {
+                char *text = xasprintf("%ld\n", pid);
+                if (write(fd, text, strlen(text)) == strlen(text)) {
+                    fatal_signal_add_file_to_unlink(pidfile);
+                    if (rename(tmpfile, pidfile) < 0) {
+                        VLOG_ERR("failed to rename \"%s\" to \"%s\": %s",
+                                 tmpfile, pidfile, strerror(errno));
+                        fatal_signal_remove_file_to_unlink(pidfile);
+                        close(fd);
+                    } else {
+                        /* Keep 'fd' open to retain the lock. */
+                    }
+                } else {
+                    VLOG_ERR("%s: write failed: %s", tmpfile, strerror(errno));
+                    close(fd);
+                }
+            } else {
+                VLOG_ERR("%s: fcntl failed: %s", tmpfile, strerror(errno));
+                close(fd);
+            }
         } else {
-            VLOG_ERR("failed to create \"%s\": %s", pidfile, strerror(errno));
+            VLOG_ERR("%s: create failed: %s", tmpfile, strerror(errno));
         }
-        free(pidfile);
-        pidfile = NULL;
+        fatal_signal_remove_file_to_unlink(tmpfile);
+        free(tmpfile);
     }
+    free(pidfile);
+    pidfile = NULL;
 }
 
 /* If configured with set_pidfile() or set_detach(), creates the pid file and
@@ -94,28 +139,37 @@ void
 daemonize(void)
 {
     if (detach) {
-        pid_t pid;
+        char c = 0;
+        int fds[2];
+        if (pipe(fds) < 0) {
+            fatal(errno, "pipe failed");
+        }
 
-        /* Fork and exit from the parent. */
-        pid = fork();
-        if (pid < 0) {
-            fatal(errno, "could not fork");
-        } else if (pid) {
+        switch (fork()) {
+        default:
+            /* Parent process: wait for child to create pidfile, then exit. */
+            close(fds[1]);
             fatal_signal_fork();
-            make_pidfile(pid);
+            read(fds[0], &c, 1);
             exit(0);
-        }
 
-        if (pidfile) {
-            fatal_signal_add_file_to_unlink(pidfile);
+        case 0:
+            /* Child process. */
+            close(fds[0]);
+            make_pidfile();
+            write(fds[1], &c, 1);
+            close(fds[1]);
+            setsid();
+            chdir("/");
+            break;
+
+        case -1:
+            /* Error. */
+            fatal(errno, "could not fork");
+            break;
         }
-        setsid();
-        chdir("/");
     } else {
-        if (pidfile) {
-            fatal_signal_add_file_to_unlink(pidfile);
-        }
-        make_pidfile(getpid());
+        make_pidfile();
     }
 }
 
index 98f9b445219d400c6be93be8518958d81d58a958..1eee5e577c40d3f4d83181a27e9de1048dac1082 100644 (file)
@@ -31,6 +31,7 @@
  * derivatives without specific, written prior permission.
  */
 
+#include <config.h>
 #include "dhcp-client.h"
 #include <arpa/inet.h>
 #include <assert.h>
@@ -39,7 +40,9 @@
 #include <limits.h>
 #include <stdlib.h>
 #include <string.h>
+#include <sys/types.h>
 #include <time.h>
+#include <unistd.h>
 #include "buffer.h"
 #include "csum.h"
 #include "dhcp.h"
@@ -163,7 +166,6 @@ dhclient_create(const char *netdev_name,
                 bool (*validate_offer)(const struct dhcp_msg *, void *aux),
                 void *aux, struct dhclient **cli_)
 {
-    struct in_addr any = { INADDR_ANY };
     struct dhclient *cli;
     struct netdev *netdev;
     int error;
@@ -178,9 +180,9 @@ dhclient_create(const char *netdev_name,
         return error;
     }
 
-    error = netdev_set_in4(netdev, any, any);
+    error = netdev_turn_flags_on(netdev, NETDEV_UP, false);
     if (error) {
-        VLOG_ERR("could not remove IPv4 address from %s network device: %s",
+        VLOG_ERR("could not bring %s device up: %s",
                  netdev_name, strerror(error));
         netdev_close(netdev);
         return error;
@@ -213,7 +215,6 @@ dhclient_init(struct dhclient *cli, uint32_t requested_ip)
 {
     state_transition(cli, requested_ip ? S_INIT_REBOOT : S_INIT);
     cli->ipaddr = requested_ip;
-    cli->changed = true;
     cli->min_timeout = 0;
     cli->init_delay = 0;
 }
@@ -319,6 +320,14 @@ dhclient_get_netmask(const struct dhclient *cli)
     return dhclient_is_bound(cli) ? cli->netmask : 0;
 }
 
+/* If 'cli' is bound to an IP address and 'cli' has a default gateway, returns
+ * that default gateway; otherwise, returns 0. */
+uint32_t
+dhclient_get_router(const struct dhclient *cli)
+{
+    return dhclient_is_bound(cli) ? cli->router : 0;
+}
+
 /* If 'cli' is bound to an IP address, returns the DHCP message that was
  * received to obtain that IP address (so that the caller can obtain additional
  * options from it).  Otherwise, returns a null pointer. */
@@ -327,6 +336,128 @@ dhclient_get_config(const struct dhclient *cli)
 {
     return dhclient_is_bound(cli) ? cli->binding : NULL;
 }
+
+/* Configures the network device backing 'cli' to the network address and other
+ * parameters obtained via DHCP.  If no address is bound on 'cli', removes any
+ * configured address from 'cli'.
+ *
+ * To use a dhclient as a regular DHCP client that binds and unbinds from IP
+ * addresses in the usual fashion, call this function after dhclient_run() if
+ * anything has changed, like so:
+ *
+ * dhclient_run(cli);
+ * if (dhclient_changed(cli)) {
+ *     dhclient_configure_netdev(cli);
+ * }
+ *
+ */
+int
+dhclient_configure_netdev(struct dhclient *cli)
+{
+    struct in_addr addr = { dhclient_get_ip(cli) };
+    struct in_addr mask = { dhclient_get_netmask(cli) };
+    struct in_addr router = { dhclient_get_router(cli) };
+    int error;
+
+    error = netdev_set_in4(cli->netdev, addr, mask);
+    if (error) {
+        VLOG_ERR("could not set %s address "IP_FMT"/"IP_FMT": %s",
+                 netdev_get_name(cli->netdev),
+                 IP_ARGS(&addr.s_addr), IP_ARGS(&mask.s_addr),
+                 strerror(error));
+    }
+
+    if (!error && router.s_addr) {
+        error = netdev_add_router(cli->netdev, router);
+        if (error) {
+            VLOG_ERR("failed to add default route to "IP_FMT" on %s: %s",
+                     IP_ARGS(&router), netdev_get_name(cli->netdev),
+                     strerror(error));
+        }
+    }
+
+    return error;
+}
+
+/* If 'cli' is bound and the binding includes DNS domain parameters, updates
+ * /etc/resolv.conf will be updated to match the received parameters.  Returns
+ * 0 if successful, otherwise a positive errno value. */
+int
+dhclient_update_resolv_conf(struct dhclient *cli)
+{
+    uint32_t dns_server;
+    char *domain_name;
+    bool has_domain_name;
+    char new_name[128];
+    FILE *old, *new;
+    int i;
+
+    if (!dhclient_is_bound(cli)) {
+        return 0;
+    }
+    if (!dhcp_msg_get_ip(cli->binding, DHCP_CODE_DNS_SERVER, 0, &dns_server)) {
+        VLOG_DBG("binding does not include any DNS servers");
+        return 0;
+    }
+
+    sprintf(new_name, "/etc/resolv.conf.tmp%ld", (long int) getpid());
+    new = fopen(new_name, "w");
+    if (!new) {
+        VLOG_WARN("%s: create: %s", new_name, strerror(errno));
+        return errno;
+    }
+
+    domain_name = dhcp_msg_get_string(cli->binding, DHCP_CODE_DOMAIN_NAME);
+    has_domain_name = domain_name != NULL;
+    if (domain_name) {
+        if (strspn(domain_name, "-_.0123456789abcdefghijklmnopqrstuvwxyz"
+                   "ABCDEFGHIJKLMNOPQRSTUVWXYZ") == strlen(domain_name)) {
+            fprintf(new, "domain %s\n", domain_name);
+        } else {
+            VLOG_WARN("ignoring invalid domain name %s", domain_name);
+            has_domain_name = false;
+        }
+    } else {
+        VLOG_DBG("binding does not include domain name");
+    }
+    free(domain_name);
+
+    for (i = 0; dhcp_msg_get_ip(cli->binding, DHCP_CODE_DNS_SERVER,
+                                i, &dns_server); i++) {
+        fprintf(new, "nameserver "IP_FMT"\n", IP_ARGS(&dns_server));
+    }
+
+    old = fopen("/etc/resolv.conf", "r");
+    if (old) {
+        char line[128];
+
+        while (fgets(line, sizeof line, old)) {
+            char *kw = xmemdup0(line, strcspn(line, " \t\r\n"));
+            if (strcmp(kw, "nameserver")
+                && (!has_domain_name
+                    || (strcmp(kw, "domain") && strcmp(kw, "search")))) {
+                fputs(line, new);
+            }
+            free(kw);
+        }
+        fclose(old);
+    } else {
+        VLOG_DBG("/etc/resolv.conf: open: %s", strerror(errno));
+    }
+
+    if (fclose(new) < 0) {
+        VLOG_WARN("%s: close: %s", new_name, strerror(errno));
+        return errno;
+    }
+
+    if (rename(new_name, "/etc/resolv.conf") < 0) {
+        VLOG_WARN("failed to rename %s to /etc/resolv.conf: %s",
+                  new_name, strerror(errno));
+        return errno;
+    }
+
+    return 0;
+}
 \f
 /* DHCP protocol. */
 
@@ -390,11 +521,12 @@ dhcp_receive(struct dhclient *cli, unsigned int msgs, struct dhcp_msg *msg)
         if (msg->type < 0 || msg->type > 31 || !((1u << msg->type) & msgs)) {
             VLOG_DBG("received unexpected %s in %s state: %s",
                      dhcp_type_name(msg->type), state_name(cli->state),
-                     dhcp_msg_to_string(msg, &cli->s));
+                     dhcp_msg_to_string(msg, false, &cli->s));
         } else if (msg->xid != cli->xid) {
             VLOG_DBG("ignoring %s with xid != %08"PRIx32" in %s state: %s",
                      dhcp_type_name(msg->type), msg->xid,
-                     state_name(cli->state), dhcp_msg_to_string(msg, &cli->s));
+                     state_name(cli->state),
+                     dhcp_msg_to_string(msg, false, &cli->s));
         } else {
             return true;
         }
@@ -408,18 +540,18 @@ validate_offered_options(struct dhclient *cli, const struct dhcp_msg *msg)
 {
     uint32_t lease, netmask;
     if (!dhcp_msg_get_secs(msg, DHCP_CODE_LEASE_TIME, 0, &lease)) {
-        VLOG_WARN("%s lacks lease time: %s",
-                  dhcp_type_name(msg->type), dhcp_msg_to_string(msg, &cli->s));
+        VLOG_WARN("%s lacks lease time: %s", dhcp_type_name(msg->type),
+                  dhcp_msg_to_string(msg, false, &cli->s));
     } else if (!dhcp_msg_get_ip(msg, DHCP_CODE_SUBNET_MASK, 0, &netmask)) {
-        VLOG_WARN("%s lacks netmask: %s",
-                  dhcp_type_name(msg->type), dhcp_msg_to_string(msg, &cli->s));
+        VLOG_WARN("%s lacks netmask: %s", dhcp_type_name(msg->type),
+                  dhcp_msg_to_string(msg, false, &cli->s));
     } else if (lease < MIN_ACCEPTABLE_LEASE) {
         VLOG_WARN("Ignoring %s with %"PRIu32"-second lease time: %s",
                   dhcp_type_name(msg->type), lease,
-                  dhcp_msg_to_string(msg, &cli->s));
+                  dhcp_msg_to_string(msg, false, &cli->s));
     } else if (cli->validate_offer && !cli->validate_offer(msg, cli->aux)) {
         VLOG_DBG("client validation hook refused offer: %s",
-                 dhcp_msg_to_string(msg, &cli->s));
+                 dhcp_msg_to_string(msg, false, &cli->s));
     } else {
         return true;
     }
@@ -443,11 +575,12 @@ dhclient_run_SELECTING(struct dhclient *cli)
         if (!dhcp_msg_get_ip(&msg, DHCP_CODE_SERVER_IDENTIFIER,
                              0, &cli->server_ip)) {
             VLOG_WARN("DHCPOFFER lacks server identifier: %s",
-                      dhcp_msg_to_string(&msg, &cli->s));
+                      dhcp_msg_to_string(&msg, false, &cli->s));
             continue;
         }
 
-        VLOG_DBG("accepting DHCPOFFER: %s", dhcp_msg_to_string(&msg, &cli->s));
+        VLOG_DBG("accepting DHCPOFFER: %s",
+                 dhcp_msg_to_string(&msg, false, &cli->s));
         cli->ipaddr = msg.yiaddr;
         state_transition(cli, S_REQUESTING);
         break;
@@ -501,7 +634,7 @@ receive_ack(struct dhclient *cli)
             cli->router = INADDR_ANY;
         }
         state_transition(cli, S_BOUND);
-        VLOG_DBG("Bound: %s", dhcp_msg_to_string(&msg, &cli->s));
+        VLOG_DBG("Bound: %s", dhcp_msg_to_string(&msg, false, &cli->s));
         return true;
     }
 }
@@ -591,51 +724,31 @@ state_transition(struct dhclient *cli, enum dhclient_state state)
 {
     bool was_bound = dhclient_is_bound(cli);
     bool am_bound;
-    VLOG_DBG("entering %s", state_name(state));
-    cli->state = state;
+    if (cli->state != state) {
+        VLOG_DBG("entering %s", state_name(state)); 
+        cli->state = state;
+    }
     cli->state_entered = time(0);
     cli->retransmit = cli->delay = 0;
     am_bound = dhclient_is_bound(cli);
     if (was_bound != am_bound) {
-        struct in_addr addr, mask;
-        int error;
-
         cli->changed = true;
         if (am_bound) {
-            VLOG_WARN("%s: binding to "IP_FMT"/"IP_FMT,
+            assert(cli->binding != NULL);
+            VLOG_WARN("%s: obtained address "IP_FMT", netmask "IP_FMT,
                       netdev_get_name(cli->netdev),
                       IP_ARGS(&cli->ipaddr), IP_ARGS(&cli->netmask));
-            addr.s_addr = cli->ipaddr;
-            mask.s_addr = cli->netmask;
-        } else {
-            VLOG_WARN("%s: unbinding IPv4 network address",
-                      netdev_get_name(cli->netdev));
-            addr.s_addr = mask.s_addr = INADDR_ANY;
-        }
-        error = netdev_set_in4(cli->netdev, addr, mask);
-        if (error) {
-            VLOG_ERR("could not set %s address "IP_FMT"/"IP_FMT": %s",
-                     netdev_get_name(cli->netdev),
-                     IP_ARGS(&addr.s_addr), IP_ARGS(&mask.s_addr),
-                     strerror(error));
-        }
-        if (am_bound && !error && cli->router) {
-            struct in_addr router = { cli->router };
-            error = netdev_add_router(cli->netdev, router);
-            VLOG_WARN("%s: configuring router "IP_FMT,
-                      netdev_get_name(cli->netdev), IP_ARGS(cli->router));
-            if (error) {
-                VLOG_ERR("failed to add default route to "IP_FMT" on %s: %s",
-                         IP_ARGS(&router), netdev_get_name(cli->netdev),
-                         strerror(error));
+            if (cli->router) {
+                VLOG_WARN("%s: obtained default gateway "IP_FMT,
+                          netdev_get_name(cli->netdev), IP_ARGS(&cli->router));
             }
-        }
-        if (am_bound) {
-            assert(cli->binding != NULL);
         } else {
             dhcp_msg_uninit(cli->binding);
             free(cli->binding);
             cli->binding = NULL;
+
+            VLOG_WARN("%s: network address unbound",
+                      netdev_get_name(cli->netdev));
         }
     }
     if (cli->state & (S_SELECTING | S_REQUESTING | S_REBOOTING)) {
@@ -671,7 +784,6 @@ dhclient_msg_init(struct dhclient *cli, enum dhcp_msg_type type,
     msg->secs = cli->secs;
     msg->type = type;
     memcpy(msg->chaddr, netdev_get_etheraddr(cli->netdev), ETH_ADDR_LEN);
-    
 }
 
 static unsigned int
@@ -702,7 +814,7 @@ do_receive_msg(struct dhclient *cli, struct dhcp_msg *msg)
         buffer_clear(&b);
         error = netdev_recv(cli->netdev, &b);
         if (error) {
-            break;
+            goto drained;
         }
 
         flow_extract(&b, 0, &flow);
@@ -731,12 +843,13 @@ do_receive_msg(struct dhclient *cli, struct dhcp_msg *msg)
         buffer_pull(&b, b.l7 - b.data);
         error = dhcp_parse(msg, &b);
         if (!error) {
-            VLOG_DBG("received %s", dhcp_msg_to_string(msg, &cli->s));
+            VLOG_DBG("received %s", dhcp_msg_to_string(msg, false, &cli->s));
             buffer_uninit(&b);
             return true;
         }
     }
     netdev_drain(cli->netdev);
+drained:
     buffer_uninit(&b);
     return false;
 }
@@ -805,7 +918,7 @@ do_send_msg(struct dhclient *cli, const struct dhcp_msg *msg)
      * frame to have to be discarded or fragmented if it travels over a regular
      * Ethernet at some point.  1500 bytes should be enough for anyone. */
     if (b.size <= ETH_TOTAL_MAX) {
-        VLOG_DBG("sending %s", dhcp_msg_to_string(msg, &cli->s));
+        VLOG_DBG("sending %s", dhcp_msg_to_string(msg, false, &cli->s));
         error = netdev_send(cli->netdev, &b);
         if (error) {
             VLOG_ERR("send failed on %s: %s",
index 851c5638f1c06a372e5d27af8bc5bce1afbbba3d..5021d5e0b8903714c0663b8427893935485985ce 100644 (file)
@@ -31,6 +31,7 @@
  * derivatives without specific, written prior permission.
  */
 
+#include <config.h>
 #include "dhcp.h"
 #include <arpa/inet.h>
 #include <assert.h>
@@ -373,6 +374,31 @@ dhcp_msg_get_uint16(const struct dhcp_msg *msg, int code,
     }
 }
 
+/* Appends a string representing 'duration' seconds to 'ds'. */
+static void
+put_duration(struct ds *ds, unsigned int duration)
+{
+    if (duration) {
+        if (duration >= 86400) {
+            ds_put_format(ds, "%ud", duration / 86400);
+            duration %= 86400;
+        }
+        if (duration >= 3600) {
+            ds_put_format(ds, "%uh", duration / 3600);
+            duration %= 3600;
+        }
+        if (duration >= 60) {
+            ds_put_format(ds, "%umin", duration / 60);
+            duration %= 60;
+        }
+        if (duration > 0) {
+            ds_put_format(ds, "%us", duration);
+        }
+    } else {
+        ds_put_cstr(ds, "0s");
+    }
+}
+
 /* Appends a string representation of 'opt', which has the given 'code', to
  * 'ds'. */
 const char *
@@ -382,7 +408,6 @@ dhcp_option_to_string(const struct dhcp_option *opt, int code, struct ds *ds)
     const struct arg_type *type = &types[class->type];
     size_t offset;
 
-    ds_put_char(ds, ' ');
     if (class->name) {
         const char *cp;
         for (cp = class->name; *cp; cp++) {
@@ -404,7 +429,6 @@ dhcp_option_to_string(const struct dhcp_option *opt, int code, struct ds *ds)
         const uint16_t *uint16 = p;
         const char *cp = p;
         unsigned char c;
-        unsigned int secs;
 
         if (offset && class->type != DHCP_ARG_STRING) {
             ds_put_cstr(ds, class->type == DHCP_ARG_UINT8 ? ":" : ", ");
@@ -425,22 +449,7 @@ dhcp_option_to_string(const struct dhcp_option *opt, int code, struct ds *ds)
             ds_put_format(ds, "%"PRIu32, ntohl(*uint32));
             break;
         case DHCP_ARG_SECS:
-            secs = ntohl(*uint32);
-            if (secs >= 86400) {
-                ds_put_format(ds, "%ud", secs / 86400);
-                secs %= 86400;
-            }
-            if (secs >= 3600) {
-                ds_put_format(ds, "%uh", secs / 3600);
-                secs %= 3600;
-            }
-            if (secs >= 60) {
-                ds_put_format(ds, "%umin", secs / 60);
-                secs %= 60;
-            }
-            if (secs > 0 || *uint32 == 0) {
-                ds_put_format(ds, "%us", secs);
-            }
+            put_duration(ds, ntohl(*uint32));
             break;
         case DHCP_ARG_STRING:
             c = *cp;
@@ -479,20 +488,26 @@ dhcp_option_to_string(const struct dhcp_option *opt, int code, struct ds *ds)
     return ds_cstr(ds);
 }
 
-/* Replaces 'ds' by a string representation of 'msg'. */
+/* Replaces 'ds' by a string representation of 'msg'.  If 'multiline' is
+ * false, 'ds' receives a single-line representation of 'msg', otherwise a
+ * multiline representation. */
 const char *
-dhcp_msg_to_string(const struct dhcp_msg *msg, struct ds *ds)
+dhcp_msg_to_string(const struct dhcp_msg *msg, bool multiline, struct ds *ds)
 {
+    char separator = multiline ? '\n' : ' ';
     int code;
 
     ds_clear(ds);
-    ds_put_format(ds, "%s %s xid=%08"PRIx32" secs=%"PRIu16,
-                  (msg->op == DHCP_BOOTREQUEST ? "BOOTREQUEST"
-                   : msg->op == DHCP_BOOTREPLY ? "BOOTREPLY"
-                   : "<<bad DHCP op>>"),
-                  dhcp_type_name(msg->type), msg->xid, msg->secs);
+    ds_put_format(ds, "op=%s",
+                  (msg->op == DHCP_BOOTREQUEST ? "request"
+                   : msg->op == DHCP_BOOTREPLY ? "reply"
+                   : "error"));
+    ds_put_format(ds, "%ctype=%s", separator, dhcp_type_name(msg->type));
+    ds_put_format(ds, "%cxid=0x%08"PRIx32, separator, msg->xid);
+    ds_put_format(ds, "%csecs=", separator);
+    put_duration(ds, msg->secs);
     if (msg->flags) {
-        ds_put_cstr(ds, " flags=");
+        ds_put_format(ds, "%cflags=", separator);
         if (msg->flags & DHCP_FLAGS_BROADCAST) {
             ds_put_cstr(ds, "[BROADCAST]");
         }
@@ -501,25 +516,30 @@ dhcp_msg_to_string(const struct dhcp_msg *msg, struct ds *ds)
         }
     }
     if (msg->ciaddr) {
-        ds_put_format(ds, " ciaddr="IP_FMT, IP_ARGS(&msg->ciaddr));
+        ds_put_format(ds, "%cciaddr="IP_FMT, separator, IP_ARGS(&msg->ciaddr));
     }
     if (msg->yiaddr) {
-        ds_put_format(ds, " yiaddr="IP_FMT, IP_ARGS(&msg->yiaddr));
+        ds_put_format(ds, "%cyiaddr="IP_FMT, separator, IP_ARGS(&msg->yiaddr));
     }
     if (msg->siaddr) {
-        ds_put_format(ds, " siaddr="IP_FMT, IP_ARGS(&msg->siaddr));
+        ds_put_format(ds, "%csiaddr="IP_FMT, separator, IP_ARGS(&msg->siaddr));
     }
     if (msg->giaddr) {
-        ds_put_format(ds, " giaddr="IP_FMT, IP_ARGS(&msg->giaddr));
+        ds_put_format(ds, "%cgiaddr="IP_FMT, separator, IP_ARGS(&msg->giaddr));
     }
-    ds_put_format(ds, " chaddr="ETH_ADDR_FMT, ETH_ADDR_ARGS(msg->chaddr));
+    ds_put_format(ds, "%cchaddr="ETH_ADDR_FMT,
+                  separator, ETH_ADDR_ARGS(msg->chaddr));
 
     for (code = 0; code < DHCP_N_OPTIONS; code++) {
         const struct dhcp_option *opt = &msg->options[code];
         if (opt->data) {
+            ds_put_char(ds, separator);
             dhcp_option_to_string(opt, code, ds);
         }
     }
+    if (multiline) {
+        ds_put_char(ds, separator);
+    }
     return ds_cstr(ds);
 }
 
@@ -692,7 +712,7 @@ error:
         VLOG_DBG("invalid DHCP message dump:\n%s", ds_cstr(&ds));
 
         ds_clear(&ds);
-        dhcp_msg_to_string(msg, &ds);
+        dhcp_msg_to_string(msg, false, &ds);
         VLOG_DBG("partially dissected DHCP message: %s", ds_cstr(&ds));
 
         ds_destroy(&ds);
index 5d9c33c6a17f7a4b37d2c6d34a76d82310cd8fb2..202a1828380de6aef8517a6d1e605464fc2dca90 100644 (file)
@@ -31,6 +31,7 @@
  * derivatives without specific, written prior permission.
  */
 
+#include <config.h>
 #include "dpif.h"
 
 #include <ctype.h>
index f08e8cb526a9b00e1176dfa3a4f3802104a2e71c..d12739b7551a751a7a6f42c60496adf5804d16f0 100644 (file)
@@ -31,6 +31,7 @@
  * derivatives without specific, written prior permission.
  */
 
+#include <config.h>
 #include "dynamic-string.h"
 #include <assert.h>
 #include <stdlib.h>
index 43b27a39a0d0e3b1311eb54402b4fb2e43f8744d..fe1f52b2df22cb827d44642d7ac79979997bf634 100644 (file)
@@ -30,6 +30,7 @@
  * advertising or publicity pertaining to the Software or any
  * derivatives without specific, written prior permission.
  */
+#include <config.h>
 #include "fatal-signal.h"
 #include <assert.h>
 #include <errno.h>
@@ -42,7 +43,7 @@
 #include "util.h"
 
 /* Signals to catch. */
-static const int fatal_signals[] = { SIGTERM, SIGINT, SIGHUP };
+static const int fatal_signals[] = { SIGTERM, SIGINT, SIGHUP, SIGALRM };
 
 /* Signals to catch as a sigset_t. */
 static sigset_t fatal_signal_set;
index 0967f7dbbe33bd9a345b7adfde3334c2f0f6ed84..762357097d7dc6d5edd042ac02fbda2189d4aa27 100644 (file)
@@ -31,6 +31,7 @@
  * derivatives without specific, written prior permission.
  */
 
+#include <config.h>
 #include "fault.h"
 #include <dlfcn.h>
 #include <inttypes.h>
index 449a286f78dea7144415d56a944c8902c671bd01..7340e7d48a54d643795f9d48b44a77748bc90058 100644 (file)
@@ -30,6 +30,7 @@
  * advertising or publicity pertaining to the Software or any
  * derivatives without specific, written prior permission.
  */
+#include <config.h>
 #include <sys/types.h>
 #include "flow.h"
 #include <inttypes.h>
index b65a49df275758a62d812de394dd9608d76d4a87..8e87216b8a9dee863a38510dfdce36f25c2722c6 100644 (file)
@@ -30,6 +30,7 @@
  * advertising or publicity pertaining to the Software or any
  * derivatives without specific, written prior permission.
  */
+#include <config.h>
 #include "hash.h"
 
 uint32_t
index 3b432d3b288351e7e260a32bd01ba8201598fd38..b477e7051cbb49bbd527cf81842d51a8a48c4a3e 100644 (file)
@@ -31,6 +31,7 @@
  * derivatives without specific, written prior permission.
  */
 
+#include <config.h>
 #include "learning-switch.h"
 
 #include <errno.h>
index c35ef10bba49347b4dfe056911457ea2123da837..8eaf9fe94d3d5dac8204eab8ca7c66c3eac9702a 100644 (file)
@@ -30,6 +30,7 @@
  * advertising or publicity pertaining to the Software or any
  * derivatives without specific, written prior permission.
  */
+#include <config.h>
 #include "list.h"
 #include <assert.h>
 
index 5311ccf64b006de113f3bb3f8e6aec5318966397..29a66f1b9ca7de33bf4be3458fe8ff54f4b159b5 100644 (file)
@@ -31,6 +31,7 @@
  * derivatives without specific, written prior permission.
  */
 
+#include <config.h>
 #include "mac-learning.h"
 
 #include <assert.h>
index 34c7747ddd803ca59a2b08bb5456fd04f01099fd..0bcef6712ffdac5499c705ad5abb401c4ce9cc48 100644 (file)
@@ -31,6 +31,7 @@
  * derivatives without specific, written prior permission.
  */
 
+#include <config.h>
 #include "netdev.h"
 
 #include <assert.h>
index 2d63ceba68cb7ceda907a04ef1ef7d85715a98b6..d9dd435c0211959f4f80812a6e4b71a882d1ae9a 100644 (file)
@@ -31,6 +31,7 @@
  * derivatives without specific, written prior permission.
  */
 
+#include <config.h>
 #include "netlink.h"
 #include <assert.h>
 #include <errno.h>
index 6ba3694eb3319fc1c44b9eb38b4328df5ac83206..2af4553f491885c0d7bb17f916fb67d7d16a0b23 100644 (file)
@@ -31,6 +31,7 @@
  * derivatives without specific, written prior permission.
  */
 
+#include <config.h>
 #include "ofp-print.h"
 #include "xtoxll.h"
 
index 556521ab91882c2b69266919c56d9ede1b699ceb..71f1e6f24de2662d4fc86f36092e9ab5041bae3c 100644 (file)
@@ -31,6 +31,7 @@
  * derivatives without specific, written prior permission.
  */
 
+#include <config.h>
 #include "poll-loop.h"
 #include <assert.h>
 #include <errno.h>
index 586ae52a00e95b09f7db6f3c6fb0ec48f1a05c64..635e14426192df7bc3fc52cf2d6685fbd3aa6125 100644 (file)
@@ -31,6 +31,7 @@
  * derivatives without specific, written prior permission.
  */
 
+#include <config.h>
 #include "queue.h"
 #include <assert.h>
 #include "buffer.h"
index d5d8b957fb39876e4ce9115726440cd3c07a12cd..c7a3edf7468edd9fd17a759cd59ee50edfba9ff5 100644 (file)
@@ -31,6 +31,7 @@
  * derivatives without specific, written prior permission.
  */
 
+#include <config.h>
 #include "random.h"
 
 #include <errno.h>
index a145e4e3181f1454b7e11d7c3828edc1695f0f8e..5bd352b0fab332148566e4a50d121864432b7490 100644 (file)
@@ -31,6 +31,7 @@
  * derivatives without specific, written prior permission.
  */
 
+#include <config.h>
 #include "rconn.h"
 #include <assert.h>
 #include <errno.h>
index 20e0f2073bacb704c43cf63fbd832e3d0c2f1808..91cdc1d6829c1b663e52ead3b71b9d86f1038475 100644 (file)
@@ -31,6 +31,7 @@
  * derivatives without specific, written prior permission.
  */
 
+#include <config.h>
 #include "socket-util.h"
 #include <arpa/inet.h>
 #include <errno.h>
index 230a2470425db0917d823b6a2ac468a594f1163e..59c7806e61e3dc6cf46fa3974904a6c20dd2dfe1 100644 (file)
@@ -31,6 +31,7 @@
  * derivatives without specific, written prior permission.
  */
 
+#include <config.h>
 #include "util.h"
 #include <stdarg.h>
 #include <stdio.h>
index 84d7d3548196ae1fc87f62e58da939ae64bc9c80..403f3fc18cdd94b3c525c5cdcd8dad54a7343a2f 100644 (file)
@@ -31,6 +31,7 @@
  * derivatives without specific, written prior permission.
  */
 
+#include <config.h>
 #include "vconn.h"
 #include <arpa/inet.h>
 #include <assert.h>
index a7060d74bb9a91e4f1bd7c80829437a9b4132a1a..d109cfdb0d394b4118c38c2eff458a55dc79499f 100644 (file)
@@ -31,6 +31,7 @@
  * derivatives without specific, written prior permission.
  */
 
+#include <config.h>
 #include "vconn-ssl.h"
 #include "dhparams.h"
 #include <assert.h>
index 80c8f56c5b6c558846d3c8686042a592edf763cf..7fbec081ea9f80734742f9c22cb2bf8b81cff454 100644 (file)
@@ -31,6 +31,7 @@
  * derivatives without specific, written prior permission.
  */
 
+#include <config.h>
 #include "vconn.h"
 #include <assert.h>
 #include <errno.h>
index 103c9df58b20713c7008bd00eb2d92ca73c67acd..99a2eb016062390c14eaf78789121a9febb7ff5a 100644 (file)
@@ -31,6 +31,7 @@
  * derivatives without specific, written prior permission.
  */
 
+#include <config.h>
 #include "vconn.h"
 #include <assert.h>
 #include <errno.h>
@@ -265,10 +266,24 @@ vconn_recv(struct vconn *vconn, struct buffer **msgp)
     int retval = vconn_connect(vconn);
     if (!retval) {
         retval = (vconn->class->recv)(vconn, msgp);
-        if (VLOG_IS_DBG_ENABLED() && !retval) {
-            char *s = ofp_to_string((*msgp)->data, (*msgp)->size, 1);
-            VLOG_DBG("received: %s", s);
-            free(s);
+        if (!retval) {
+            struct ofp_header *oh;
+
+            if (VLOG_IS_DBG_ENABLED()) {
+                char *s = ofp_to_string((*msgp)->data, (*msgp)->size, 1);
+                VLOG_DBG("received: %s", s);
+                free(s);
+            }
+
+            oh = buffer_at_assert(*msgp, 0, sizeof *oh);
+            if (oh->version != OFP_VERSION) {
+                VLOG_ERR("received OpenFlow version %02"PRIx8" "
+                         "!= expected %02x",
+                         oh->version, OFP_VERSION);
+                buffer_delete(*msgp);
+                *msgp = NULL;
+                return EPROTO;
+            }
         }
     }
     if (retval) {
index 05b91864ccbc182c07e8632baf9c98f0b40d0ca9..7caaf12e180e9014a9555045b29a88388533c4a2 100644 (file)
@@ -31,6 +31,7 @@
  * derivatives without specific, written prior permission.
  */
 
+#include <config.h>
 #include "vlog-socket.h"
 #include <errno.h>
 #include <sys/un.h>
index 6b3d871f5197f402425863b3156780e298383a1a..1cc24f85542a5bc9b39e03acb154919b0d3d799c 100644 (file)
@@ -31,6 +31,7 @@
  * derivatives without specific, written prior permission.
  */
 
+#include <config.h>
 #include "vlog.h"
 #include <assert.h>
 #include <errno.h>
@@ -344,13 +345,17 @@ vlog(enum vlog_module module, enum vlog_level level, const char *message, ...)
 
         if (log_syslog) {
             static const int syslog_levels[VLL_N_LEVELS] = {
-                [VLL_EMER] = LOG_EMERG,
+                [VLL_EMER] = LOG_ALERT,
                 [VLL_ERR] = LOG_ERR,
                 [VLL_WARN] = LOG_WARNING,
                 [VLL_DBG] = LOG_DEBUG,
             };
+            char *line, *save_ptr;
 
-            syslog(syslog_levels[level], "%s", s + time_len);
+            for (line = strtok_r(s + time_len, "\n", &save_ptr); line != NULL;
+                 line = strtok_r(NULL, "\n", &save_ptr)) {
+                syslog(syslog_levels[level], "%s", line);
+            }
         }
         errno = save_errno;
     }
index c575b479edb65699f3cf3035597f0b41655688b3..bedb286b22b110262a8a0a93f076197c9e8a51d2 100644 (file)
@@ -9,7 +9,7 @@ man_MANS =
 endif
 
 secchan_SOURCES = secchan.c
-secchan_LDADD = ../lib/libopenflow.la -ldl
+secchan_LDADD = ../lib/libopenflow.a $(FAULT_LIBS) $(SSL_LIBS)
 
 EXTRA_DIST = secchan.8.in
 DISTCLEANFILES = secchan.8
index 512ee920de4d8a2a37dd720aa8f0167872de00e3..03c516cf7e726204ec594e18837ffac006134452 100644 (file)
@@ -76,6 +76,12 @@ vendor-specific option with code 1 whose contents are a string
 specifying the location of the controller in the same format used on
 the \fBsecchan\fR command line (e.g. \fBssl:192.168.0.1\fR).
 
+The DHCP reply may also, optionally, include a vendor-specific option
+with code 2 whose contents are a string specifying the URI to the base
+of the OpenFlow PKI (e.g. \fBhttp://192.168.0.1/openflow/pki\fR).
+This URI is used only for bootstrapping the OpenFlow PKI at initial
+switch setup; \fBsecchan\fR does not use it at all.
+
 The following ISC DHCP server configuration file assigns the IP
 address range 192.168.0.20 through 192.168.0.30 to OpenFlow switches
 that follow the switch protocol and addresses 192.168.0.1 through
@@ -89,6 +95,8 @@ option space openflow;
 .br
 option openflow.controller-vconn code 1 = text;
 .br
+option openflow.pki-uri code 2 = text;
+.br
 class "OpenFlow" {
 .br
   match if option vendor-class-identifier = "OpenFlow";
@@ -96,6 +104,8 @@ class "OpenFlow" {
   vendor-option-space openflow;
 .br
   option openflow.controller-vconn "tcp:192.168.0.10";
+.br
+  option openflow.pki-uri "http://192.168.0.10/openflow/pki";
 .br
   option vendor-class-identifier "OpenFlow";
 .br
@@ -160,8 +170,8 @@ has started up.  Thus, start \fBsecchan\fR without configuring
 When \fBsecchan\fR performs controller discovery (see \fBCONTACTING
 THE CONTROLLER\fR, above, for more information about controller
 discovery), it validates the controller location obtained via DHCP
-with a regular expression.  Only controllers whose names match the
-regular expression will be accepted.
+with a POSIX extended regular expression.  Only controllers whose
+names match the regular expression will be accepted.
 
 The default regular expression is \fBssl:.*\fR (meaning that only SSL
 controller connections will be accepted) when any of the SSL
@@ -174,6 +184,24 @@ controller location string, as if it begins with \fB^\fR.
 
 When controller discovery is not performed, this option has no effect.
 
+.TP
+\fB--no-resolv-conf\fR
+When \fBsecchan\fR performs controller discovery (see \fBCONTACTING
+THE CONTROLLER\fR, above, for more information about controller
+discovery), by default it overwrites the system's
+\fB/etc/resolv.conf\fR with domain information and DNS servers
+obtained via DHCP.  If the location of the controller is specified
+using a hostname, rather than an IP address, and the network's DNS
+servers ever change, this behavior is essential.  But because it also
+interferes with any administrator or process that manages
+\fB/etc/resolv.conf\fR, when this option is specified, \fBsecchan\fR
+will not modify \fB/etc/resolv.conf\fR.
+
+\fBsecchan\fR will only modify \fBresolv.conf\fR if the DHCP response
+that it receives specifies one or more DNS servers.
+
+When controller discovery is not performed, this option has no effect.
+
 .TP
 \fB-f\fR, \fB--fail=\fR[\fBopen\fR|\fBclosed\fR]
 The controller is, ordinarily, responsible for setting up all flows on
@@ -322,6 +350,7 @@ Prints version information to the console.
 .SH "SEE ALSO"
 
 .BR dpctl (8),
+.BR ofp-discover (8),
 .BR controller (8),
 .BR ofp-pki (8),
 .BR vlogconf (8),
index 7fb50c97b13b021755f6bfa2257ded0cbd1e9d7c..fb0eb41cd661bba968bd80f2e83ec19140075164 100644 (file)
@@ -31,6 +31,7 @@
  * derivatives without specific, written prior permission.
  */
 
+#include <config.h>
 #include <errno.h>
 #include <getopt.h>
 #include <inttypes.h>
@@ -66,8 +67,6 @@
 #include "vlog.h"
 #define THIS_MODULE VLM_secchan
 
-#include "ofp-print.h"
-
 static const char *listen_vconn_name;
 
 struct half {
@@ -134,6 +133,10 @@ static struct dhclient *dhcp;
 static const char *accept_controller_re;
 static regex_t accept_controller_regex;
 
+/* --no-resolv-conf: Update resolv.conf upon successful controller
+ * discovery? */
+static bool update_resolv_conf = true;
+
 static void parse_options(int argc, char *argv[]);
 static void usage(void) NO_RETURN;
 
@@ -298,8 +301,13 @@ main(int argc, char *argv[])
             }
             dhclient_run(dhcp);
             if (dhclient_changed(dhcp)) {
-                free(controller_name);
+                dhclient_configure_netdev(dhcp);
+                if (update_resolv_conf) {
+                    dhclient_update_resolv_conf(dhcp);
+                }
+
                 if (dhclient_is_bound(dhcp)) {
+                    free(controller_name);
                     controller_name = dhcp_msg_get_string(
                         dhclient_get_config(dhcp),
                         DHCP_CODE_OFP_CONTROLLER_VCONN);
@@ -309,6 +317,7 @@ main(int argc, char *argv[])
                 } else if (controller_name) {
                     VLOG_WARN("%s: discover controller no longer available",
                               controller_name);
+                    free(controller_name);
                     controller_name = NULL;
                     rconn_disconnect(remote_rconn);
                 }
@@ -666,6 +675,10 @@ validate_dhcp_offer(const struct dhcp_msg *msg, void *aux)
         return false;
     }
     accept = !regexec(&accept_controller_regex, vconn_name, 0, NULL, 0);
+    if (!accept) {
+        VLOG_WARN("rejecting controller vconn that fails to match %s",
+                  accept_controller_re);
+    }
     free(vconn_name);
     return accept;
 }
@@ -675,12 +688,14 @@ parse_options(int argc, char *argv[])
 {
     enum {
         OPT_ACCEPT_VCONN = UCHAR_MAX + 1,
+        OPT_NO_RESOLV_CONF,
         OPT_INACTIVITY_PROBE,
         OPT_MAX_IDLE,
         OPT_MAX_BACKOFF
     };
     static struct option long_options[] = {
         {"accept-vconn", required_argument, 0, OPT_ACCEPT_VCONN},
+        {"no-resolv-conf", no_argument, 0, OPT_NO_RESOLV_CONF},
         {"fail",        required_argument, 0, 'f'},
         {"inactivity-probe", required_argument, 0, OPT_INACTIVITY_PROBE},
         {"max-idle",    required_argument, 0, OPT_MAX_IDLE},
@@ -711,6 +726,10 @@ parse_options(int argc, char *argv[])
                                     : xasprintf("^%s", optarg));
             break;
 
+        case OPT_NO_RESOLV_CONF:
+            update_resolv_conf = false;
+            break;
+
         case 'f':
             if (!strcmp(optarg, "open")) {
                 fail_mode = FAIL_OPEN;
@@ -755,7 +774,7 @@ parse_options(int argc, char *argv[])
             break;
 
         case 'P':
-            set_pidfile(optarg ? optarg : "secchan.pid");
+            set_pidfile(optarg);
             break;
 
         case 'l':
@@ -798,8 +817,10 @@ usage(void)
            "omitted, then secchan performs controller autodiscovery.\n",
            program_name, program_name);
     vconn_usage(true, true);
-    printf("\nNetworking options:\n"
+    printf("\nController discovery options:\n"
            "  --accept-vconn=REGEX    accept matching discovered controllers\n"
+           "  --no-resolv-conf        do not update /etc/resolv.conf\n"
+           "\nNetworking options:\n"
            "  -f, --fail=open|closed  when controller connection fails:\n"
            "                            closed: drop all packets\n"
            "                            open (default): act as learning switch\n"
index bd1b06ee1ed9e367983ddd79824a5c71fac93467..164c21f3ab8cccc831716335cdf9bdc2f7ec2356 100644 (file)
@@ -17,7 +17,7 @@ switch_SOURCES = \
        table-hash.c \
        table-linear.c
 
-switch_LDADD = ../lib/libopenflow.la -ldl
+switch_LDADD = ../lib/libopenflow.a $(FAULT_LIBS) $(SSL_LIBS)
 
 EXTRA_DIST = switch.8.in
 DISTCLEANFILES = switch.8
index a85a650121579a528d8d764dbea807bc11a9a4c8..1e45a44b65e4914a9949e44f93ad38ba2c946a9d 100644 (file)
@@ -31,6 +31,7 @@
  * derivatives without specific, written prior permission.
  */
 
+#include <config.h>
 #include "chain.h"
 #include <assert.h>
 #include <errno.h>
index 97c09611e12046e7725d3ded93c713b57eb74f7d..f6c2c0b3774d452f27cd8b4d295cae2350ec1d2d 100644 (file)
@@ -31,6 +31,7 @@
  * derivatives without specific, written prior permission.
  */
 
+#include <config.h>
 #include "crc32.h"
 
 void
index fb688af60e2bfb3c13c987e2b69e38e3246f7b55..505ebf326b1245239771481a1f00c8028503fe60 100644 (file)
@@ -1522,8 +1522,8 @@ fwd_control_input(struct datapath *dp, const struct sender *sender,
     struct ofp_header *oh;
 
     oh = (struct ofp_header *) msg;
-    if (oh->version != OFP_VERSION || oh->type >= ARRAY_SIZE(packets)
-        || ntohs(oh->length) > length)
+    assert(oh->version == OFP_VERSION);
+    if (oh->type >= ARRAY_SIZE(packets) || ntohs(oh->length) > length)
         return -EINVAL;
 
     pkt = &packets[oh->type];
index 2af71d668aaad4f5c325d8021243ca6539d8c28c..732cea540d239b0682deab2a90f1211c317f1f0e 100644 (file)
@@ -31,6 +31,7 @@
  * derivatives without specific, written prior permission.
  */
 
+#include <config.h>
 #include "switch-flow.h"
 #include <arpa/inet.h>
 #include <assert.h>
index 6cc6ae4e79e9803e3b87336e10c9a96ff93374f6..d745bc83566c953304215d5b234601f45410efe6 100644 (file)
@@ -31,6 +31,7 @@
  * derivatives without specific, written prior permission.
  */
 
+#include <config.h>
 #include <errno.h>
 #include <getopt.h>
 #include <limits.h>
@@ -199,7 +200,7 @@ parse_options(int argc, char *argv[])
             break;
 
         case 'P':
-            set_pidfile(optarg ? optarg : "switch.pid");
+            set_pidfile(optarg);
             break;
 
         case 'v':
index aaec5039daad8a56585e1ec7fca3d8a66437fe34..461a4636f12a42f64eb83adf9d3545453435100e 100644 (file)
@@ -31,6 +31,7 @@
  * derivatives without specific, written prior permission.
  */
 
+#include <config.h>
 #include "table.h"
 #include <assert.h>
 #include <stdlib.h>
index 6371bc25c795d929514d67a1b5f7bbfdb677275e..ff9d60b69cd9fcf1ca9fefbf5f5b33e54bb5f17f 100644 (file)
@@ -31,6 +31,7 @@
  * derivatives without specific, written prior permission.
  */
 
+#include <config.h>
 #include "table.h"
 #include <stdlib.h>
 #include "flow.h"
index 8c548c048c85e14346193db901f801ab2534a38c..2548ea0af4047046a013fb3ba7b15bf55bbe43d5 100644 (file)
@@ -5,13 +5,13 @@ TESTS = test-list
 check_PROGRAMS = test-list
 
 test_list_SOURCES = test-list.c
-test_list_LDADD = ../lib/libopenflow.la
+test_list_LDADD = ../lib/libopenflow.a
 
 TESTS += test-type-props
 check_PROGRAMS += test-type-props
 test_type_props_SOURCES = test-type-props.c
 
-check_PROGRAMS += test-dhcp-client
+noinst_PROGRAMS = test-dhcp-client
 test_dhcp_client_SOURCES = test-dhcp-client.c
-test_dhcp_client_LDADD = ../lib/libopenflow.la -ldl
+test_dhcp_client_LDADD = ../lib/libopenflow.a $(FAULT_LIBS)
 
index 528d1b19430aca4f8c7769c283ae3e97ddcf1fe3..285d99fb8eff417a34c33e03d5ba2a648dbb7d19 100644 (file)
@@ -31,6 +31,7 @@
  * derivatives without specific, written prior permission.
  */
 
+#include <config.h>
 #include "dhcp-client.h"
 #include <arpa/inet.h>
 #include <getopt.h>
@@ -52,6 +53,9 @@ static struct in_addr request_ip;
  * vendor class string is included. */
 static const char *vendor_class;
 
+/* --no-resolv-conf: Update /etc/resolv.conf to match DHCP reply? */
+static bool update_resolv_conf = true;
+
 static void parse_options(int argc, char *argv[]);
 static void usage(void);
 static void release(void *cli_);
@@ -85,8 +89,14 @@ main(int argc, char *argv[])
     for (;;) {
         fatal_signal_block();
         dhclient_run(cli);
-        fatal_signal_unblock();
+        if (dhclient_changed(cli)) {
+            dhclient_configure_netdev(cli);
+            if (update_resolv_conf) {
+                dhclient_update_resolv_conf(cli);
+            }
+        }
         dhclient_wait(cli);
+        fatal_signal_unblock();
         poll_block();
     }
 }
@@ -96,6 +106,9 @@ release(void *cli_)
 {
     struct dhclient *cli = cli_;
     dhclient_release(cli);
+    if (dhclient_changed(cli)) {
+        dhclient_configure_netdev(cli);
+    }
 }
 
 static void
@@ -111,11 +124,13 @@ parse_options(int argc, char *argv[])
 {
     enum {
         OPT_REQUEST_IP = UCHAR_MAX + 1,
-        OPT_VENDOR_CLASS
+        OPT_VENDOR_CLASS,
+        OPT_NO_RESOLV_CONF
     };
     static struct option long_options[] = {
         {"request-ip",  required_argument, 0, OPT_REQUEST_IP },
         {"vendor-class", required_argument, 0, OPT_VENDOR_CLASS },
+        {"no-resolv-conf", no_argument, 0, OPT_NO_RESOLV_CONF},
         {"verbose",     optional_argument, 0, 'v'},
         {"help",        no_argument, 0, 'h'},
         {"version",     no_argument, 0, 'V'},
@@ -142,6 +157,10 @@ parse_options(int argc, char *argv[])
             vendor_class = optarg;
             break;
 
+        case OPT_NO_RESOLV_CONF:
+            update_resolv_conf = false;
+            break;
+
         case 'h':
             usage();
 
@@ -174,6 +193,7 @@ usage(void)
            "                          do not request a specific IP)\n"
            "  --vendor-class=STRING   use STRING as vendor class (default:\n"
            "                          none); use OpenFlow to imitate secchan\n"
+           "  --no-resolv-conf        do not update /etc/resolv.conf\n"
            "\nOther options:\n"
            "  -v, --verbose=MODULE[:FACILITY[:LEVEL]]  set logging levels\n"
            "  -v, --verbose           set maximum verbosity level\n"
index b9599dbfee71f87d89acdc9b547ab48b4d32d530..62857be9489b4859df71c5cc53c7755c5efac4a5 100644 (file)
@@ -1,6 +1,7 @@
 /* A non-exhaustive test for some of the functions and macros declared in
  * list.h. */
 
+#include <config.h>
 #include "list.h"
 #include <string.h>
 
index 5dff4d41a7bef3f597ab418701c1d120e676b4d4..67dabae8672b64da40b078293aa9b088d2d95f94 100644 (file)
@@ -1,3 +1,4 @@
+#include <config.h>
 #include "type-props.h"
 #include <stdio.h>
 #include <stdlib.h>
index 566e2addb6c9c9ec489bb288c77f17a2c11977da..0d682fee4d3e8136a4a406ae7a97f889e65bfefc 100644 (file)
@@ -1,20 +1,27 @@
 include ../Make.vars
 
-bin_PROGRAMS = vlogconf dpctl
+bin_PROGRAMS = vlogconf dpctl ofp-discover ofp-kill
 bin_SCRIPTS = ofp-pki
 noinst_SCRIPTS = ofp-pki-cgi
 
-EXTRA_DIST = ofp-pki.in ofp-pki-cgi.in ofp-pki.8.in
-DISTCLEANFILES = ofp-pki ofp-pki-cgi ofp-pki.8
+EXTRA_DIST = ofp-pki.in ofp-pki-cgi.in ofp-pki.8.in ofp-discover.8.in  \
+ofp-kill.8.in
+DISTCLEANFILES = ofp-pki ofp-pki-cgi ofp-pki.8 ofp-discover.8 ofp-kill.8
 
 dist_man_MANS = vlogconf.8 dpctl.8
-man_MANS = ofp-pki.8
+man_MANS = ofp-pki.8 ofp-discover.8 ofp-kill.8
 
 dpctl_SOURCES = dpctl.c
-dpctl_LDADD = ../lib/libopenflow.la
+dpctl_LDADD = ../lib/libopenflow.a $(FAULT_LIBS) $(SSL_LIBS)
 
 vlogconf_SOURCES = vlogconf.c
-vlogconf_LDADD = ../lib/libopenflow.la
+vlogconf_LDADD = ../lib/libopenflow.a
+
+ofp_discover_SOURCES = ofp-discover.c
+ofp_discover_LDADD = ../lib/libopenflow.a
+
+ofp_kill_SOURCES = ofp-kill.c
+ofp_kill_LDADD = ../lib/libopenflow.a
 
 pkidir = $(pkgdatadir)/pki
 
@@ -26,3 +33,7 @@ ofp-pki-cgi: ofp-pki-cgi.in Makefile
        chmod +x ofp-pki-cgi
 ofp-pki.8: ofp-pki.8.in Makefile
        ($(do_subst) && $(ro_man)) < $(srcdir)/ofp-pki.8.in > ofp-pki.8
+ofp-discover.8: ofp-discover.8.in Makefile
+       ($(do_subst) && $(ro_man)) < $(srcdir)/ofp-discover.8.in > ofp-discover.8
+ofp-kill.8: ofp-kill.8.in Makefile
+       ($(do_subst) && $(ro_man)) < $(srcdir)/ofp-kill.8.in > ofp-kill.8
index f5b2adece075686ba8ed3abc0718f8467a63be0f..acadb88d5ff947a5167dddbb3d14172ce3e8995c 100644 (file)
@@ -31,6 +31,7 @@
  * derivatives without specific, written prior permission.
  */
 
+#include <config.h>
 #include <errno.h>
 #include <getopt.h>
 #include <inttypes.h>
@@ -126,10 +127,9 @@ parse_options(int argc, char *argv[])
 
     for (;;) {
         unsigned long int timeout;
-        int indexptr;
         int c;
 
-        c = getopt_long(argc, argv, short_options, long_options, &indexptr);
+        c = getopt_long(argc, argv, short_options, long_options, NULL);
         if (c == -1) {
             break;
         }
diff --git a/utilities/ofp-discover.8.in b/utilities/ofp-discover.8.in
new file mode 100644 (file)
index 0000000..19923a3
--- /dev/null
@@ -0,0 +1,131 @@
+.TH ofp\-discover 8 "May 2008" "OpenFlow" "OpenFlow Manual"
+
+.SH NAME
+ofp\-discover \- controller discovery utility
+
+.SH SYNOPSIS
+.B ofp\-discover
+[\fIoptions\fR] \fInetdev\fR [\fInetdev\fR...]
+
+.SH DESCRIPTION
+The \fBofp\-discover\fR program attempts to discover the location of
+an OpenFlow controller on one of the network devices listed on the
+command line.  It repeatedly broadcasts a DHCP request with vendor
+class identifier \fBOpenFlow\fR on each network device until it
+receives an acceptable DHCP response.  It will accept any valid DHCP
+reply that has the same vendor class identifier and includes a
+vendor-specific option with code 1 whose contents are a string
+specifying the location of the controller in the same format used on
+the \fBsecchan\fR command line (e.g. \fBssl:192.168.0.1\fR).
+
+When \fBofp\-discover\fR receives an acceptable response, it prints
+the details of the response on \fBstdout\fR.  Then, by default, it
+configures the network device on which the response was received with
+the received IP address, netmask, and default gateway, and detaches
+itself to the background.
+
+.SH OPTIONS
+.TP
+\fB--accept-vconn=\fIregex\fR
+By default, \fBofp\-discover\fR accepts any controller location
+advertised over DHCP.  With this option, only controllers whose names
+match POSIX extended regular expression \fIregex\fR will be accepted.
+Specifying \fBssl:.*\fR for \fIregex\fR, for example, would cause only
+SSL controller connections to be accepted.
+
+The \fIregex\fR is implicitly anchored at the beginning of the
+controller location string, as if it begins with \fB^\fR.
+
+.TP
+\fB--exit-without-bind\fR
+By default, \fBofp\-discover\fR binds the network device that receives
+the first acceptable response to the IP address received over DHCP.
+With this option, the configuration of the network device is not
+changed at all, except to bring it up if it is initially down, and
+\fBofp\-discover\fR will exit immediately after it receives an
+acceptable DHCP response.
+
+This option is mutually exclusive with \fB--exit-after-bind\fR and
+\fB--no-detach\fR.
+
+.TP
+\fB--exit-after-bind\fR
+By default, after it receives an acceptable DHCP response,
+\fBofp\-discover\fR detaches itself from the foreground session and
+runs in the background maintaining the DHCP lease as necessary.  With
+this option, \fBofp\-discover\fR will exit immediately after it
+receives an acceptable DHCP response and configures the network device
+with the received IP address.  The address obtained via DHCP could
+therefore be used past the expiration of its lease.
+
+This option is mutually exclusive with \fB--exit-without-bind\fR and
+\fB--no-detach\fR.
+
+.TP
+\fB--no-detach\fR
+By default, \fBofp\-discover\fR runs in the foreground until it obtains
+an acceptable DHCP response, then it detaches itself from the
+foreground session and run as a background process.  This option
+prevents \fBofp\-discover\fR from detaching, causing it to run in the
+foreground even after it obtains a DHCP response.
+
+This option is mutually exclusive with \fB--exit-without-bind\fR and
+\fB--exit-after-bind\fR.
+
+.TP
+\fB-P\fR[\fIpidfile\fR], \fB--pidfile\fR[\fB=\fIpidfile\fR]
+Causes a file (by default, \fBofp\-discover.pid\fR) to be created indicating
+the PID of the running process.  If \fIpidfile\fR is not specified, or
+if it does not begin with \fB/\fR, then it is created in
+\fB@rundir@\fR.
+
+The \fIpidfile\fR is created when \fBofp\-discover\fR detaches, so
+this this option has no effect when one of \fB--exit-without-bind\fR,
+\fB--exit-after-bind\fR, or \fB--no-detach\fR is also given.
+
+.TP
+.BR \-h ", " \-\^\-help
+Prints a brief help message to the console.
+
+.TP
+\fB-v\fR \fImodule\fR[\fB:\fIfacility\fR[\fB:\fIlevel\fR]], \fB--verbose=\fImodule\fR[\fB:\fIfacility\fR[\fB:\fIlevel\fR]]
+Sets the logging level for \fImodule\fR in \fIfacility\fR to
+\fIlevel\fR.  The \fImodule\fR may be any valid module name (as
+displayed by the \fB--list\fR action on \fBvlogconf\fR(8)), or the
+special name \fBANY\fR to set the logging levels for all modules.  The
+\fIfacility\fR may be \fBsyslog\fR or \fBconsole\fR to set the levels
+for logging to the system log or to the console, respectively, or
+\fBANY\fR to set the logging levels for both facilities.  If it is
+omitted, \fIfacility\fR defaults to \fBANY\fR.  The \fIlevel\fR must
+be one of \fBemer\fR, \fBerr\fR, \fBwarn\fR, or \fBdbg\fR, designating
+the minimum severity of a message for it to be logged.  If it is
+omitted, \fIlevel\fR defaults to \fBdbg\fR.
+
+.TP
+\fB-v\fR, \fB--verbose\fR
+Sets the maximum logging verbosity level, equivalent to
+\fB--verbose=ANY:ANY:dbg\fR.
+
+.TP
+.BR \-V ", " \-\^\-version
+Prints version information to the console.
+
+.SH BUGS
+
+If the network devices specified on the command line have been added
+to an OpenFlow switch with \fBdpctl addif\fR, then controller
+discovery will fail because \fBofp\-discover\fR will not be able to
+see DHCP responses, even though tools such as \fBtcpdump\fR(8) and
+\fBwireshark\fR(1) can see them on the wire.  This is because of the
+structure of the Linux kernel networking stack, which hands packets
+first to programs that listen for all arriving packets, then to
+OpenFlow, then to programs that listen for a specific kind of packet.
+OpenFlow consumes all the packets handed to it, so tools like
+\fBtcpdump\fR that look at all packets will see packets arriving on
+OpenFlow interfaces, but \fRofp\-discover\fR, which listens only for
+arriving IP packets, will not.
+
+.SH "SEE ALSO"
+
+.BR secchan (8),
+.BR ofp-pki (8)
diff --git a/utilities/ofp-discover.c b/utilities/ofp-discover.c
new file mode 100644 (file)
index 0000000..7dfc56a
--- /dev/null
@@ -0,0 +1,411 @@
+/* Copyright (c) 2008 The Board of Trustees of The Leland Stanford
+ * Junior University
+ *
+ * We are making the OpenFlow specification and associated documentation
+ * (Software) available for public use and benefit with the expectation
+ * that others will use, modify and enhance the Software and contribute
+ * those enhancements back to the community. However, since we would
+ * like to make the Software available for broadest use, with as few
+ * restrictions as possible permission is hereby granted, free of
+ * charge, to any person obtaining a copy of this Software to deal in
+ * the Software under the copyrights without restriction, including
+ * without limitation the rights to use, copy, modify, merge, publish,
+ * distribute, sublicense, and/or sell copies of the Software, and to
+ * permit persons to whom the Software is furnished to do so, subject to
+ * the following conditions:
+ *
+ * The above copyright notice and this permission notice shall be
+ * included in all copies or substantial portions of the Software.
+ *
+ * THE SOFTWARE IS PROVIDED "AS IS", WITHOUT WARRANTY OF ANY KIND,
+ * EXPRESS OR IMPLIED, INCLUDING BUT NOT LIMITED TO THE WARRANTIES OF
+ * MERCHANTABILITY, FITNESS FOR A PARTICULAR PURPOSE AND
+ * NONINFRINGEMENT.  IN NO EVENT SHALL THE AUTHORS OR COPYRIGHT HOLDERS
+ * BE LIABLE FOR ANY CLAIM, DAMAGES OR OTHER LIABILITY, WHETHER IN AN
+ * ACTION OF CONTRACT, TORT OR OTHERWISE, ARISING FROM, OUT OF OR IN
+ * CONNECTION WITH THE SOFTWARE OR THE USE OR OTHER DEALINGS IN THE
+ * SOFTWARE.
+ *
+ * The name and trademarks of copyright holder(s) may NOT be used in
+ * advertising or publicity pertaining to the Software or any
+ * derivatives without specific, written prior permission.
+ */
+
+#include <config.h>
+#include <getopt.h>
+#include <limits.h>
+#include <regex.h>
+#include <signal.h>
+#include <stdlib.h>
+#include <unistd.h>
+#include "command-line.h"
+#include "daemon.h"
+#include "dhcp-client.h"
+#include "dhcp.h"
+#include "dynamic-string.h"
+#include "fatal-signal.h"
+#include "netdev.h"
+#include "poll-loop.h"
+#include "util.h"
+#include "vlog-socket.h"
+
+#include "vlog.h"
+#define THIS_MODULE VLM_ofp_discover
+
+struct iface {
+    const char *name;
+    struct dhclient *dhcp;
+};
+
+/* The interfaces that we serve. */
+static struct iface *ifaces;
+static int n_ifaces;
+
+/* --accept-vconn: Regular expression specifying the class of controller vconns
+ * that we will accept during autodiscovery. */
+static const char *accept_controller_re = ".*";
+static regex_t accept_controller_regex;
+
+/* --exit-without-bind: Exit after discovering the controller, without binding
+ * the network device to an IP address? */
+static bool exit_without_bind;
+
+/* --exit-after-bind: Exit after discovering the controller, after binding the
+ * network device to an IP address? */
+static bool exit_after_bind;
+
+static bool iface_init(struct iface *, const char *netdev_name);
+static void release_ifaces(void *aux UNUSED);
+
+static void parse_options(int argc, char *argv[]);
+static void usage(void) NO_RETURN;
+
+static void modify_dhcp_request(struct dhcp_msg *, void *aux);
+static bool validate_dhcp_offer(const struct dhcp_msg *, void *aux);
+
+int
+main(int argc, char *argv[])
+{
+    int retval;
+    int i;
+
+    set_program_name(argv[0]);
+    vlog_init();
+    parse_options(argc, argv);
+
+    argc -= optind;
+    argv += optind;
+    if (argc < 1) {
+        fatal(0, "need at least one non-option argument; "
+              "use --help for usage");
+    }
+
+    ifaces = xmalloc(argc * sizeof *ifaces);
+    n_ifaces = 0;
+    for (i = 0; i < argc; i++) {
+        if (iface_init(&ifaces[n_ifaces], argv[i])) {
+            n_ifaces++;
+        }
+    }
+    if (!n_ifaces) {
+        fatal(0, "failed to initialize any DHCP clients");
+    }
+
+    for (i = 0; i < n_ifaces; i++) {
+        struct iface *iface = &ifaces[i];
+        dhclient_init(iface->dhcp, 0);
+    }
+    fatal_signal_add_hook(release_ifaces, NULL);
+
+    retval = regcomp(&accept_controller_regex, accept_controller_re,
+                     REG_NOSUB | REG_EXTENDED);
+    if (retval) {
+        size_t length = regerror(retval, &accept_controller_regex, NULL, 0);
+        char *buffer = xmalloc(length);
+        regerror(retval, &accept_controller_regex, buffer, length);
+        fatal(0, "%s: %s", accept_controller_re, buffer);
+    }
+
+    retval = vlog_server_listen(NULL, NULL);
+    if (retval) {
+        fatal(retval, "Could not listen for vlog connections");
+    }
+
+    signal(SIGPIPE, SIG_IGN);
+    for (;;) {
+        fatal_signal_block();
+        for (i = 0; i < n_ifaces; i++) {
+            struct iface *iface = &ifaces[i];
+            dhclient_run(iface->dhcp);
+            if (dhclient_changed(iface->dhcp)) {
+                bool is_bound = dhclient_is_bound(iface->dhcp);
+                int j;
+
+                /* Configure network device. */
+                if (!exit_without_bind) {
+                    dhclient_configure_netdev(iface->dhcp);
+                    dhclient_update_resolv_conf(iface->dhcp);
+                }
+
+                if (is_bound) {
+                    static bool detached = false;
+                    struct ds ds;
+
+                    /* Disable timeout, since discovery was successful. */
+                    alarm(0);
+
+                    /* Print discovered parameters. */
+                    ds_init(&ds);
+                    dhcp_msg_to_string(dhclient_get_config(iface->dhcp),
+                                       true, &ds);
+                    fputs(ds_cstr(&ds), stdout);
+                    putchar('\n');
+                    fflush(stdout);
+                    ds_destroy(&ds);
+
+                    /* Exit if the user requested it. */
+                    if (exit_without_bind) {
+                        VLOG_DBG("exiting because of successful binding on %s "
+                                 "and --exit-without-bind specified",
+                                 iface->name);
+                        exit(0);
+                    }
+                    if (exit_after_bind) {
+                        VLOG_DBG("exiting because of successful binding on %s "
+                                 "and --exit-after-bind specified",
+                                 iface->name);
+                        exit(0);
+                    }
+
+                    /* Detach into background, if we haven't already. */
+                    if (!detached) {
+                        detached = true;
+                        daemonize();
+                    }
+                }
+
+                /* We only want an address on a single one of our interfaces.
+                 * So: if we have an address on this interface, stop looking
+                 * for one on the others; if we don't have an address on this
+                 * interface, start looking everywhere. */
+                for (j = 0; j < n_ifaces; j++) {
+                    struct iface *if2 = &ifaces[j];
+                    if (iface != if2) {
+                        if (is_bound) {
+                            dhclient_release(if2->dhcp);
+                        } else {
+                            dhclient_init(if2->dhcp, 0);
+                        }
+                    }
+                }
+            }
+        }
+        for (i = 0; i < n_ifaces; i++) {
+            struct iface *iface = &ifaces[i];
+            dhclient_wait(iface->dhcp);
+        }
+        fatal_signal_unblock();
+        poll_block();
+    }
+
+    return 0;
+}
+
+static bool
+iface_init(struct iface *iface, const char *netdev_name)
+{
+    int retval;
+
+    iface->name = netdev_name;
+    iface->dhcp = NULL;
+
+    if (exit_after_bind) {
+        /* Bring this interface up permanently, so that the bound address
+         * persists past program termination. */
+        struct netdev *netdev;
+
+        retval = netdev_open(iface->name, NETDEV_ETH_TYPE_NONE, &netdev);
+        if (retval) {
+            error(retval, "Could not open %s device", iface->name);
+            return false;
+        }
+        retval = netdev_turn_flags_on(netdev, NETDEV_UP, true);
+        if (retval) {
+            error(retval, "Could not bring %s device up", iface->name);
+            return false;
+        }
+        netdev_close(netdev);
+    }
+
+    retval = dhclient_create(iface->name, modify_dhcp_request,
+                             validate_dhcp_offer, NULL, &iface->dhcp);
+    if (retval) {
+        error(retval, "%s: failed to initialize DHCP client", iface->name);
+        return false;
+    }
+
+    return true;
+}
+
+static void
+release_ifaces(void *aux UNUSED)
+{
+    int i;
+
+    for (i = 0; i < n_ifaces; i++) {
+        struct dhclient *dhcp = ifaces[i].dhcp;
+        dhclient_release(dhcp);
+        if (dhclient_changed(dhcp)) {
+            dhclient_configure_netdev(dhcp);
+        }
+    }
+}
+
+static void
+modify_dhcp_request(struct dhcp_msg *msg, void *aux)
+{
+    dhcp_msg_put_string(msg, DHCP_CODE_VENDOR_CLASS, "OpenFlow");
+}
+
+static bool
+validate_dhcp_offer(const struct dhcp_msg *msg, void *aux)
+{
+    char *vconn_name;
+    bool accept;
+
+    vconn_name = dhcp_msg_get_string(msg, DHCP_CODE_OFP_CONTROLLER_VCONN);
+    if (!vconn_name) {
+        VLOG_WARN("rejecting DHCP offer missing controller vconn");
+        return false;
+    }
+    accept = !regexec(&accept_controller_regex, vconn_name, 0, NULL, 0);
+    free(vconn_name);
+    return accept;
+}
+
+static void
+parse_options(int argc, char *argv[])
+{
+    enum {
+        OPT_ACCEPT_VCONN = UCHAR_MAX + 1,
+        OPT_EXIT_WITHOUT_BIND,
+        OPT_EXIT_AFTER_BIND,
+        OPT_NO_DETACH,
+    };
+    static struct option long_options[] = {
+        {"accept-vconn", required_argument, 0, OPT_ACCEPT_VCONN},
+        {"exit-without-bind", no_argument, 0, OPT_EXIT_WITHOUT_BIND},
+        {"exit-after-bind", no_argument, 0, OPT_EXIT_AFTER_BIND},
+        {"no-detach",   no_argument, 0, OPT_NO_DETACH},
+        {"timeout",     required_argument, 0, 't'},
+        {"pidfile",     optional_argument, 0, 'P'},
+        {"verbose",     optional_argument, 0, 'v'},
+        {"help",        no_argument, 0, 'h'},
+        {"version",     no_argument, 0, 'V'},
+        {0, 0, 0, 0},
+    };
+    char *short_options = long_options_to_short_options(long_options);
+    bool detach_after_bind = true;
+
+    for (;;) {
+        unsigned long int timeout;
+        int c;
+
+        c = getopt_long(argc, argv, short_options, long_options, NULL);
+        if (c == -1) {
+            break;
+        }
+
+        switch (c) {
+        case OPT_ACCEPT_VCONN:
+            accept_controller_re = (optarg[0] == '^'
+                                    ? optarg
+                                    : xasprintf("^%s", optarg));
+            break;
+
+        case OPT_EXIT_WITHOUT_BIND:
+            exit_without_bind = true;
+            break;
+
+        case OPT_EXIT_AFTER_BIND:
+            exit_after_bind = true;
+            break;
+
+        case OPT_NO_DETACH:
+            detach_after_bind = false;
+            break;
+
+        case 'P':
+            set_pidfile(optarg);
+            break;
+
+        case 't':
+            timeout = strtoul(optarg, NULL, 10);
+            if (timeout <= 0) {
+                fatal(0, "value %s on -t or --timeout is not at least 1",
+                      optarg);
+            } else if (timeout < UINT_MAX) {
+                /* Add 1 because historical implementations allow an alarm to
+                 * occur up to a second early. */
+                alarm(timeout + 1);
+            } else {
+                alarm(UINT_MAX);
+            }
+            signal(SIGALRM, SIG_DFL);
+            break;
+
+        case 'h':
+            usage();
+
+        case 'V':
+            printf("%s "VERSION" compiled "__DATE__" "__TIME__"\n", argv[0]);
+            exit(EXIT_SUCCESS);
+
+        case 'v':
+            vlog_set_verbosity(optarg);
+            break;
+
+        case '?':
+            exit(EXIT_FAILURE);
+
+        default:
+            abort();
+        }
+    }
+    free(short_options);
+
+    if ((exit_without_bind + exit_after_bind + !detach_after_bind) > 1) {
+        fatal(0, "--exit-without-bind, --exit-after-bind, and --no-detach "
+              "are mutually exclusive");
+    }
+    if (detach_after_bind) {
+        set_detach();
+    }
+}
+
+static void
+usage(void)
+{
+    printf("%s: a tool for discovering OpenFlow controllers.\n"
+           "usage: %s [OPTIONS] NETDEV [NETDEV...]\n"
+           "where each NETDEV is a network device on which to perform\n"
+           "controller discovery.\n"
+           "\nOrdinarily, ofp-discover runs in the foreground until it\n"
+           "obtains an IP address and discovers an OpenFlow controller via\n"
+           "DHCP, then it prints information about the controller to stdout\n"
+           "and detaches to the background to maintain the IP address lease.\n"
+           "\nNetworking options:\n"
+           "  --accept-vconn=REGEX    accept matching discovered controllers\n"
+           "  --exit-without-bind     exit after discovery, without binding\n"
+           "  --exit-after-bind       exit after discovery, after binding\n"
+           "  --no-detach             do not detach after discovery\n"
+           "\nOther options:\n"
+           "  -t, --timeout=SECS      give up discovery after SECS seconds\n"
+           "  -P, --pidfile[=FILE]    create pidfile (default: %s/%s.pid)\n"
+           "  -v, --verbose=MODULE[:FACILITY[:LEVEL]]  set logging levels\n"
+           "  -v, --verbose           set maximum verbosity level\n"
+           "  -h, --help              display this help message\n"
+           "  -V, --version           display version information\n",
+           program_name, program_name, RUNDIR, program_name);
+    exit(EXIT_SUCCESS);
+}
diff --git a/utilities/ofp-kill.8.in b/utilities/ofp-kill.8.in
new file mode 100644 (file)
index 0000000..122ff54
--- /dev/null
@@ -0,0 +1,65 @@
+.TH ofp\-kill 8 "May 2008" "OpenFlow" "OpenFlow Manual"
+
+.SH NAME
+ofp\-kill \- kills processes given their pidfiles
+
+.SH SYNOPSIS
+.B ofp\-kill
+[\fIoptions\fR] \fIpidfile\fR [\fIpidfile\fR...]
+
+.SH DESCRIPTION
+The \fBofp\-kill\fR program reads each \fIpidfile\fR specified on the
+command line and sends a signal to the program associated with it, if
+any.  It reads one line of text from \fIpidfile\fR, which must contain
+the PID of the process to kill as a text string.  It then uses
+\fBfcntl\fR(2) to verify that a process with the PID from the file
+owns a lock on \fIpidfile\fR before it sends the signal.
+
+A \fIpidfile\fR whose name begins with \fB/\fR is used literally.
+Otherwise, \fB@rundir@/\fR is prefixed.
+
+This program exists for use by \fBofp\-switch\-setup\fR, which cannot
+easily implement its functionality since Perl has no portable
+interface to \fBfcntl\fR-based file locking.
+
+.SH OPTIONS
+.TP
+\fB-s \fInumber\fR|\fIname\fR, \fB\-\^\-signal=\fInumber\fR|\fIname\fR
+Sets the signal to be sent to each process.  Signals may be given by
+number (e.g. \fB1\fR) or by name (e.g. \fBHUP\fR or \fBSIGHUP\fR).
+By default, \fBSIGTERM\fR is sent.
+
+.TP
+\fB-f\fR, \fB\-\^\-force\fR
+Causes \fBofp\-kill\fR to ignore all errors without printing a message
+to \fBstderr\fR, and to exit with return code 0.
+
+.TP
+.BR \-h ", " \-\^\-help
+Prints a brief help message to the console.
+
+.TP
+.BR \-V ", " \-\^\-version
+Prints version information to the console.
+
+.SH "EXIT CODE"
+
+Without \fB-f\fR or \fB\-\^\-force\fR, \fBofp\-kill\fR exits with
+status 0 if at least one \fIpidfile\fR was given and the process
+represented by every \fIpidfile\fR was signaled successfully,
+otherwise with status 1.
+
+With \fB-f\fR or \fB\-\^\-force\fR, \fBofp\-kill\fR always exits with
+status 0.
+
+.SH BUGS
+
+There is a race between verifying the lock on \fIpidfile\fR and
+actually killing the process.
+
+\fBofp\-kill\fR does not wait for the signaled processes to die before
+exiting.
+
+.SH "SEE ALSO"
+
+.BR ofp\-switch\-setup (8)
diff --git a/utilities/ofp-kill.c b/utilities/ofp-kill.c
new file mode 100644 (file)
index 0000000..9647f46
--- /dev/null
@@ -0,0 +1,258 @@
+/* Copyright (c) 2008 The Board of Trustees of The Leland Stanford
+ * Junior University
+ *
+ * We are making the OpenFlow specification and associated documentation
+ * (Software) available for public use and benefit with the expectation
+ * that others will use, modify and enhance the Software and contribute
+ * those enhancements back to the community. However, since we would
+ * like to make the Software available for broadest use, with as few
+ * restrictions as possible permission is hereby granted, free of
+ * charge, to any person obtaining a copy of this Software to deal in
+ * the Software under the copyrights without restriction, including
+ * without limitation the rights to use, copy, modify, merge, publish,
+ * distribute, sublicense, and/or sell copies of the Software, and to
+ * permit persons to whom the Software is furnished to do so, subject to
+ * the following conditions:
+ *
+ * The above copyright notice and this permission notice shall be
+ * included in all copies or substantial portions of the Software.
+ *
+ * THE SOFTWARE IS PROVIDED "AS IS", WITHOUT WARRANTY OF ANY KIND,
+ * EXPRESS OR IMPLIED, INCLUDING BUT NOT LIMITED TO THE WARRANTIES OF
+ * MERCHANTABILITY, FITNESS FOR A PARTICULAR PURPOSE AND
+ * NONINFRINGEMENT.  IN NO EVENT SHALL THE AUTHORS OR COPYRIGHT HOLDERS
+ * BE LIABLE FOR ANY CLAIM, DAMAGES OR OTHER LIABILITY, WHETHER IN AN
+ * ACTION OF CONTRACT, TORT OR OTHERWISE, ARISING FROM, OUT OF OR IN
+ * CONNECTION WITH THE SOFTWARE OR THE USE OR OTHER DEALINGS IN THE
+ * SOFTWARE.
+ *
+ * The name and trademarks of copyright holder(s) may NOT be used in
+ * advertising or publicity pertaining to the Software or any
+ * derivatives without specific, written prior permission.
+ */
+
+#include <config.h>
+#include <errno.h>
+#include <fcntl.h>
+#include <getopt.h>
+#include <signal.h>
+#include <stdarg.h>
+#include <stdlib.h>
+#include <string.h>
+#include "command-line.h"
+#include "daemon.h"
+#include "util.h"
+
+/* -s, --signal: signal to send. */
+static int sig_nr = SIGTERM;
+
+/* -f, --force: ignore errors. */
+static bool force;
+
+static void cond_error(int err_no, const char *, ...) PRINTF_FORMAT(2, 3);
+static bool kill_pidfile(const char *pidfile, FILE *);
+
+static void parse_options(int argc, char *argv[]);
+static void usage(void);
+
+int
+main(int argc, char *argv[])
+{
+    bool ok = true;
+    int i;
+
+    set_program_name(argv[0]);
+    parse_options(argc, argv);
+
+    argc -= optind;
+    argv += optind;
+    if (argc < 1) {
+        if (!force) {
+            fatal(0, "need at least one non-option argument; "
+                  "use --help for usage");
+        }
+    }
+
+    for (i = 0; i < argc; i++) {
+        char *pidfile;
+        FILE *file;
+
+        pidfile = make_pidfile_name(argv[i]);
+        file = fopen(pidfile, "r");
+        if (!file) {
+            ok = false;
+            cond_error(errno, "%s: open", pidfile);
+        } else {
+            ok = kill_pidfile(argv[i], file) && ok;
+            fclose(file);
+        }
+        free(pidfile);
+    }
+
+    return ok || force ? EXIT_SUCCESS : EXIT_FAILURE;
+}
+
+static bool
+kill_pidfile(const char *pidfile, FILE *file)
+{
+    char line[128];
+    struct flock lck;
+
+    lck.l_type = F_WRLCK;
+    lck.l_whence = SEEK_SET;
+    lck.l_start = 0;
+    lck.l_len = 0;
+    if (fcntl(fileno(file), F_GETLK, &lck)) {
+        cond_error(errno, "%s: fcntl", pidfile); 
+        return false;
+    }
+
+    if (!fgets(line, sizeof line, file)) {
+        cond_error(errno, "%s: read", pidfile);
+        return false;
+    }
+
+    if (lck.l_pid != strtoul(line, NULL, 10)) {
+        cond_error(errno, "l_pid (%ld) != %s pid (%s)",
+                   (long int) lck.l_pid, pidfile, line);
+        return false;
+    }
+
+    if (kill(lck.l_pid, sig_nr) < 0) {
+        cond_error(errno, "%s: kill(%ld)", pidfile, (long int) lck.l_pid);
+        return false;
+    }
+
+    return true;
+}
+
+static void
+parse_options(int argc, char *argv[])
+{
+    static struct option long_options[] = {
+        {"signal",      required_argument, 0, 's'},
+        {"force",       no_argument, 0, 'f'},
+        {"help",        no_argument, 0, 'h'},
+        {"version",     no_argument, 0, 'V'},
+        {0, 0, 0, 0},
+    };
+    char *short_options = long_options_to_short_options(long_options);
+
+    for (;;) {
+        int c;
+
+        c = getopt_long(argc, argv, short_options, long_options, NULL);
+        if (c == -1) {
+            break;
+        }
+
+        switch (c) {
+        case 's':
+            if (atoi(optarg) || !strcmp(optarg, "0")) {
+                sig_nr = atoi(optarg);
+            } else {
+                struct signal_name {
+                    const char *name;
+                    int number;
+                };
+
+                static const struct signal_name signals[] = {
+#define SIGNAL(NAME) { #NAME, NAME }
+                    SIGNAL(SIGABRT),
+                    SIGNAL(SIGALRM),
+                    SIGNAL(SIGBUS),
+                    SIGNAL(SIGCHLD),
+                    SIGNAL(SIGCONT),
+                    SIGNAL(SIGFPE),
+                    SIGNAL(SIGHUP),
+                    SIGNAL(SIGILL),
+                    SIGNAL(SIGINT),
+                    SIGNAL(SIGKILL),
+                    SIGNAL(SIGPIPE),
+                    SIGNAL(SIGQUIT),
+                    SIGNAL(SIGSEGV),
+                    SIGNAL(SIGSTOP),
+                    SIGNAL(SIGTERM),
+                    SIGNAL(SIGTSTP),
+                    SIGNAL(SIGTTIN),
+                    SIGNAL(SIGTTOU),
+                    SIGNAL(SIGUSR1),
+                    SIGNAL(SIGUSR2),
+#ifdef SIGPOLL
+                    SIGNAL(SIGPOLL),
+#endif
+                    SIGNAL(SIGPROF),
+                    SIGNAL(SIGSYS),
+                    SIGNAL(SIGTRAP),
+                    SIGNAL(SIGURG),
+                    SIGNAL(SIGVTALRM),
+                    SIGNAL(SIGXCPU),
+                    SIGNAL(SIGXFSZ),
+#undef SIGNAL
+                };
+                int i;
+
+                for (i = 0; i < ARRAY_SIZE(signals); i++) {
+                    const struct signal_name *s = &signals[i];
+                    if (!strcmp(optarg, s->name)
+                        || !strcmp(optarg, s->name + 3)) {
+                        sig_nr = s->number;
+                        goto got_name;
+                    }
+                }
+                fatal(0, "unknown signal \"%s\"", optarg);
+            got_name: ;
+            }
+            break;
+
+        case 'f':
+            force = true;
+            break;
+
+        case 'h':
+            usage();
+
+        case 'V':
+            printf("%s "VERSION" compiled "__DATE__" "__TIME__"\n", argv[0]);
+            exit(EXIT_SUCCESS);
+
+        case '?':
+            exit(EXIT_FAILURE);
+
+        default:
+            abort();
+        }
+    }
+    free(short_options);
+}
+
+static void
+usage(void)
+{
+    printf("%s: kills a program using a pidfile\n"
+           "usage: %s [OPTIONS] PIDFILE [PIDFILE...]\n"
+           "where each PIDFILE is a pidfile created by an OpenFlow daemon.\n"
+           "\nOptions:\n"
+           "  -s, --signal=NUMBER|NAME  signal to send (default: TERM)\n"
+           "  -f, --force             ignore errors\n"
+           "  -h, --help              display this help message\n"
+           "  -V, --version           display version information\n",
+           program_name, program_name);
+    exit(EXIT_SUCCESS);
+}
+
+static void
+cond_error(int err_no, const char *format, ...)
+{
+    if (!force) {
+        va_list args;
+
+        fprintf(stderr, "%s: ", program_name);
+        va_start(args, format);
+        vfprintf(stderr, format, args);
+        va_end(args);
+        if (err_no != 0)
+            fprintf(stderr, " (%s)", strerror(err_no));
+        putc('\n', stderr);
+    }
+}
index f42740d89c959be4f42f471e85000f3e4140fd3d..838597d436c7786c70247ce438b59d6ff40e788a 100644 (file)
@@ -30,6 +30,7 @@
  * advertising or publicity pertaining to the Software or any
  * derivatives without specific, written prior permission.
  */
+#include <config.h>
 #include "vlog.h"
 
 #include <dirent.h>