/configure-stamp
/depcomp
/install-sh
-/libtool
-/ltmain.sh
/missing
/stamp-h1
Module.symvers
# -*- makefile -*-
AM_CPPFLAGS = $(SSL_CFLAGS)
-LIBS = $(SSL_LIBS)
COMMON_FLAGS = -DVERSION=\"$(VERSION)\"
if NDEBUG
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)
AC_USE_SYSTEM_EXTENSIONS
-AC_PROG_LIBTOOL
-
AC_ARG_ENABLE(
[ndebug],
[AC_HELP_STRING([--enable-ndebug],
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"
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
* derivatives without specific, written prior permission.
*/
+#include <config.h>
+
#include <errno.h>
#include <getopt.h>
#include <limits.h>
break;
case 'P':
- set_pidfile(optarg ? optarg : "controller.pid");
+ set_pidfile(optarg);
break;
case 'H':
#include <linux/netfilter_bridge.h>
#include <linux/inetdevice.h>
#include <linux/list.h>
+#include <linux/rculist.h>
#include "openflow-netlink.h"
#include "datapath.h"
#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>
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 \
--- /dev/null
+/* In Linux 2.6.26, part of list.h was broken out into rculist.h. */
+#include <linux/list.h>
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 \
--- /dev/null
+#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
#include <linux/rcupdate.h>
#include <linux/slab.h>
-#include <linux/list.h>
+#include <linux/rculist.h>
struct sw_table_linear {
struct sw_table swt;
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');
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', $_) },
}
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 {
}
},
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();
}
},
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';
}
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;
},
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';
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';
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;
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();
}
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';
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();
}
}
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 {
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";
sub shell_escape {
local $_ = $_[0];
- if (m&^[-a-zA-Z0-9:./%^_+,]*$&) {
+ if ($_ eq '') {
+ return '""';
+ } elsif (m&^[-a-zA-Z0-9:./%^_+,]*$&) {
return $_;
} else {
s/'/'\\''/;
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:/);
+}
-utilities/ofp-pki usr/sbin
-utilities/vlogconf usr/sbin
+_debian/utilities/ofp-pki usr/sbin
+_debian/utilities/vlogconf usr/sbin
utilities/vlogconf.8
-utilities/ofp-pki.8
+_debian/utilities/ofp-pki.8
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.
-controller/controller usr/sbin
+_debian/controller/controller usr/sbin
-controller/controller.8
+_debian/controller/controller.8
-utilities/ofp-pki-cgi usr/lib/cgi-bin
+_debian/utilities/ofp-pki-cgi usr/lib/cgi-bin
+++ /dev/null
-#!/bin/sh
-set -e
-. /usr/share/debconf/confmodule
-# Nothing more to do -- the user is responsible for running ofp-switch-setup.
# 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.
# 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.
# 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.
[ ! -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()
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
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
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
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
-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
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
+++ /dev/null
-#!/bin/sh
-# postinst script for openflow-switch
-#
-# see: dh_installdeb(1)
-
-set -e
-
-. /usr/share/debconf/confmodule
-
-#DEBHELPER#
-
-exit 0
-
-
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
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).
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.
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
.
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
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"
#. Type: select
#. Choices
#: ../openflow-switch.templates:4001
-msgid "in-band, out-of-band"
+msgid "discovery, in-band, out-of-band"
msgstr ""
#. Type: select
#: ../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
"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 "
#. 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 "
#. 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\")."
#. 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 "
#. 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]\" "
#. 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)."
#. 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 ""
#. 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"
#. 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\"."
#. 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 "
#. 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 "
#. 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 "
#. 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 "
#. 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 "
#. 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 "
#. 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."
#. 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}\"."
#. 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."
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
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
kdist_clean:
dh_clean
- make KERNELDIR=$(KSRC) KVERREL=$(KVERS) clean
+ rm -rf openflow
kdist_config: prep-deb-files
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
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
#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);
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),
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 *);
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) \
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 *);
#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)
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;
};
VLOG_MODULE(mac_learning) \
VLOG_MODULE(netdev) \
VLOG_MODULE(netlink) \
+ VLOG_MODULE(ofp_discover) \
VLOG_MODULE(poll_loop) \
VLOG_MODULE(secchan) \
VLOG_MODULE(rconn) \
include ../Make.vars
-noinst_LTLIBRARIES = libopenflow.la
+noinst_LIBRARIES = libopenflow.a
-libopenflow_la_SOURCES = \
+libopenflow_a_SOURCES = \
buffer.c \
command-line.c \
csum.c \
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 && \
* derivatives without specific, written prior permission.
*/
+#include <config.h>
#include "buffer.h"
#include <assert.h>
#include <stdlib.h>
* derivatives without specific, written prior permission.
*/
+#include <config.h>
#include "command-line.h"
#include <getopt.h>
#include <limits.h>
* derivatives without specific, written prior permission.
*/
+#include <config.h>
#include "csum.h"
/* Returns the IP checksum of the 'n' bytes in 'data'. */
* 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>
/* 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
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
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();
}
}
* derivatives without specific, written prior permission.
*/
+#include <config.h>
#include "dhcp-client.h"
#include <arpa/inet.h>
#include <assert.h>
#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"
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;
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;
{
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;
}
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. */
{
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. */
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;
}
{
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;
}
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;
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;
}
}
{
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)) {
msg->secs = cli->secs;
msg->type = type;
memcpy(msg->chaddr, netdev_get_etheraddr(cli->netdev), ETH_ADDR_LEN);
-
}
static unsigned int
buffer_clear(&b);
error = netdev_recv(cli->netdev, &b);
if (error) {
- break;
+ goto drained;
}
flow_extract(&b, 0, &flow);
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;
}
* 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",
* derivatives without specific, written prior permission.
*/
+#include <config.h>
#include "dhcp.h"
#include <arpa/inet.h>
#include <assert.h>
}
}
+/* 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 *
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++) {
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 ? ":" : ", ");
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;
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]");
}
}
}
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);
}
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);
* derivatives without specific, written prior permission.
*/
+#include <config.h>
#include "dpif.h"
#include <ctype.h>
* derivatives without specific, written prior permission.
*/
+#include <config.h>
#include "dynamic-string.h"
#include <assert.h>
#include <stdlib.h>
* 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>
#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;
* derivatives without specific, written prior permission.
*/
+#include <config.h>
#include "fault.h"
#include <dlfcn.h>
#include <inttypes.h>
* 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>
* advertising or publicity pertaining to the Software or any
* derivatives without specific, written prior permission.
*/
+#include <config.h>
#include "hash.h"
uint32_t
* derivatives without specific, written prior permission.
*/
+#include <config.h>
#include "learning-switch.h"
#include <errno.h>
* advertising or publicity pertaining to the Software or any
* derivatives without specific, written prior permission.
*/
+#include <config.h>
#include "list.h"
#include <assert.h>
* derivatives without specific, written prior permission.
*/
+#include <config.h>
#include "mac-learning.h"
#include <assert.h>
* derivatives without specific, written prior permission.
*/
+#include <config.h>
#include "netdev.h"
#include <assert.h>
* derivatives without specific, written prior permission.
*/
+#include <config.h>
#include "netlink.h"
#include <assert.h>
#include <errno.h>
* derivatives without specific, written prior permission.
*/
+#include <config.h>
#include "ofp-print.h"
#include "xtoxll.h"
* derivatives without specific, written prior permission.
*/
+#include <config.h>
#include "poll-loop.h"
#include <assert.h>
#include <errno.h>
* derivatives without specific, written prior permission.
*/
+#include <config.h>
#include "queue.h"
#include <assert.h>
#include "buffer.h"
* derivatives without specific, written prior permission.
*/
+#include <config.h>
#include "random.h"
#include <errno.h>
* derivatives without specific, written prior permission.
*/
+#include <config.h>
#include "rconn.h"
#include <assert.h>
#include <errno.h>
* derivatives without specific, written prior permission.
*/
+#include <config.h>
#include "socket-util.h"
#include <arpa/inet.h>
#include <errno.h>
* derivatives without specific, written prior permission.
*/
+#include <config.h>
#include "util.h"
#include <stdarg.h>
#include <stdio.h>
* derivatives without specific, written prior permission.
*/
+#include <config.h>
#include "vconn.h"
#include <arpa/inet.h>
#include <assert.h>
* derivatives without specific, written prior permission.
*/
+#include <config.h>
#include "vconn-ssl.h"
#include "dhparams.h"
#include <assert.h>
* derivatives without specific, written prior permission.
*/
+#include <config.h>
#include "vconn.h"
#include <assert.h>
#include <errno.h>
* derivatives without specific, written prior permission.
*/
+#include <config.h>
#include "vconn.h"
#include <assert.h>
#include <errno.h>
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) {
* derivatives without specific, written prior permission.
*/
+#include <config.h>
#include "vlog-socket.h"
#include <errno.h>
#include <sys/un.h>
* derivatives without specific, written prior permission.
*/
+#include <config.h>
#include "vlog.h"
#include <assert.h>
#include <errno.h>
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;
}
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
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
.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";
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
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
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
.SH "SEE ALSO"
.BR dpctl (8),
+.BR ofp-discover (8),
.BR controller (8),
.BR ofp-pki (8),
.BR vlogconf (8),
* derivatives without specific, written prior permission.
*/
+#include <config.h>
#include <errno.h>
#include <getopt.h>
#include <inttypes.h>
#include "vlog.h"
#define THIS_MODULE VLM_secchan
-#include "ofp-print.h"
-
static const char *listen_vconn_name;
struct half {
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;
}
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);
} else if (controller_name) {
VLOG_WARN("%s: discover controller no longer available",
controller_name);
+ free(controller_name);
controller_name = NULL;
rconn_disconnect(remote_rconn);
}
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;
}
{
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},
: xasprintf("^%s", optarg));
break;
+ case OPT_NO_RESOLV_CONF:
+ update_resolv_conf = false;
+ break;
+
case 'f':
if (!strcmp(optarg, "open")) {
fail_mode = FAIL_OPEN;
break;
case 'P':
- set_pidfile(optarg ? optarg : "secchan.pid");
+ set_pidfile(optarg);
break;
case 'l':
"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"
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
* derivatives without specific, written prior permission.
*/
+#include <config.h>
#include "chain.h"
#include <assert.h>
#include <errno.h>
* derivatives without specific, written prior permission.
*/
+#include <config.h>
#include "crc32.h"
void
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];
* derivatives without specific, written prior permission.
*/
+#include <config.h>
#include "switch-flow.h"
#include <arpa/inet.h>
#include <assert.h>
* derivatives without specific, written prior permission.
*/
+#include <config.h>
#include <errno.h>
#include <getopt.h>
#include <limits.h>
break;
case 'P':
- set_pidfile(optarg ? optarg : "switch.pid");
+ set_pidfile(optarg);
break;
case 'v':
* derivatives without specific, written prior permission.
*/
+#include <config.h>
#include "table.h"
#include <assert.h>
#include <stdlib.h>
* derivatives without specific, written prior permission.
*/
+#include <config.h>
#include "table.h"
#include <stdlib.h>
#include "flow.h"
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)
* derivatives without specific, written prior permission.
*/
+#include <config.h>
#include "dhcp-client.h"
#include <arpa/inet.h>
#include <getopt.h>
* 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_);
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();
}
}
{
struct dhclient *cli = cli_;
dhclient_release(cli);
+ if (dhclient_changed(cli)) {
+ dhclient_configure_netdev(cli);
+ }
}
static void
{
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'},
vendor_class = optarg;
break;
+ case OPT_NO_RESOLV_CONF:
+ update_resolv_conf = false;
+ break;
+
case 'h':
usage();
" 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"
/* A non-exhaustive test for some of the functions and macros declared in
* list.h. */
+#include <config.h>
#include "list.h"
#include <string.h>
+#include <config.h>
#include "type-props.h"
#include <stdio.h>
#include <stdlib.h>
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
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
* derivatives without specific, written prior permission.
*/
+#include <config.h>
#include <errno.h>
#include <getopt.h>
#include <inttypes.h>
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;
}
--- /dev/null
+.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)
--- /dev/null
+/* 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);
+}
--- /dev/null
+.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)
--- /dev/null
+/* 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);
+ }
+}
* advertising or publicity pertaining to the Software or any
* derivatives without specific, written prior permission.
*/
+#include <config.h>
#include "vlog.h"
#include <dirent.h>