Merge commit '7b3017805866b2bb36a092a3ea25ff0ec9b6fd9e' into experimental
Conflicts: datapath/hwtable-dummy/hwtable-dummy.c datapath/hwtable_dummy/Modules.mk datapath/hwtable_dummy/hwtable_dummy.c datapath/hwtable_nf2/hwtable_nf2.c utilities/dpctl.c Resolved above conflicts
This commit is contained in:
commit
1feb499bf3
126 files changed
+7964
-1829
No files matched your search
@@ -32,8 +32,6 @@
|
||||
/configure-stamp
|
||||
/depcomp
|
||||
/install-sh
|
||||
/libtool
|
||||
/ltmain.sh
|
||||
/missing
|
||||
/stamp-h1
|
||||
Module.symvers
|
||||
|
||||
@@ -402,48 +402,72 @@ following instructions to set up remote switches:
|
||||
network. There are two ways to do this:
|
||||
|
||||
- Use a "control network" that is completely separate from the
|
||||
"data network" to be controlled. To do so, configure a
|
||||
network device (one that has not been added to the datapath
|
||||
with "dpctl addif") to access the control network in the usual
|
||||
way.
|
||||
"data network" to be controlled ("out-of-band control"). To
|
||||
do so, configure a network device (one that has not been added
|
||||
to the datapath with "dpctl addif") to access the control
|
||||
network in the usual way.
|
||||
|
||||
- Use the same network for control and for data. For this
|
||||
purpose, each datapath nl:K has a corresponding virtual
|
||||
network device named ofK. Start by bringing up of0 before you
|
||||
start the secure channel:
|
||||
- Use the same network for control and for data ("in-band
|
||||
control"). For this purpose, each datapath nl:K has a
|
||||
corresponding virtual network device named ofK.
|
||||
|
||||
# ifconfig of0 up
|
||||
When in-band control is used, the location of the controller
|
||||
may be configured manually or discovered automatically:
|
||||
|
||||
Before the secure channel starts up, the of0 device cannot
|
||||
send or receive any packets, so the next step depends on
|
||||
whether connectivity is required to configure the device's IP
|
||||
address:
|
||||
* Manual configuration: Start by bringing up of0 before
|
||||
you start the secure channel:
|
||||
|
||||
. If the switch has a static IP address, you may configure
|
||||
its IP address now, e.g.:
|
||||
# ifconfig of0 up
|
||||
|
||||
# ifconfig of0 192.168.1.1
|
||||
Before the secure channel starts up, the of0 device
|
||||
cannot send or receive any packets, so the next step
|
||||
depends on whether connectivity is required to configure
|
||||
the device's IP address:
|
||||
|
||||
. If the switch does not have a static IP address, e.g. its
|
||||
IP address is obtained dynamically via DHCP, then proceed
|
||||
to step 4. The DHCP client will not be able to contact
|
||||
the DHCP server until the secure channel has started up.
|
||||
. If the switch has a static IP address, you may
|
||||
configure its IP address now, e.g.:
|
||||
|
||||
# ifconfig of0 192.168.1.1
|
||||
|
||||
. If the switch does not have a static IP address,
|
||||
e.g. its IP address is obtained dynamically via
|
||||
DHCP, then proceed to step 4. The DHCP client will
|
||||
not be able to contact the DHCP server until the
|
||||
secure channel has started up.
|
||||
|
||||
* Controller discovery: No special setup is required at
|
||||
the switch, but you must specially configure a DHCP
|
||||
server to give out the switch's IP address and to tell
|
||||
it the location of the controller. See secchan(8) for
|
||||
details.
|
||||
|
||||
4. Run secchan on the datapath host to start the secure channel
|
||||
connecting the datapath to a remote controller. (See secchan(8)
|
||||
for usage details). The channel should be configured to connect to
|
||||
the controller's IP address on the port configured in step 2.
|
||||
for usage details). The details depend on how you configured the
|
||||
network in step 3:
|
||||
|
||||
If the controller is running on host 192.168.1.2 port 975 (the
|
||||
default port) and the datapath ID is 0, the secchan invocation
|
||||
would look like:
|
||||
- If you are using in-band control and controller discovery,
|
||||
invoke secchan something like this:
|
||||
|
||||
# secchan -v nl:0 tcp:192.168.1.2
|
||||
# secchan -v nl:0
|
||||
|
||||
If you are using separate control and data networks, or if the
|
||||
networks are combined and the switch has a static IP address, the
|
||||
secure channel should quickly connect to the controller. Setup is
|
||||
now complete. Otherwise, proceed to step 5.
|
||||
The secure channel should connect to the controller after it
|
||||
obtains its own IP address and the controller's location via
|
||||
DHCP. This can take a few seconds. Switch setup is now
|
||||
complete.
|
||||
|
||||
- Otherwise, the secure channel should be configured to connect
|
||||
to the controller's IP address on the port configured in step
|
||||
2. If the controller is running on host 192.168.1.2 port 975
|
||||
(the default port) and the datapath ID is 0, the secchan
|
||||
invocation would look like:
|
||||
|
||||
# secchan -v nl:0 tcp:192.168.1.2
|
||||
|
||||
If you are using out-of-band control, or if you are using
|
||||
in-band control and the switch has a static IP address, the
|
||||
secure channel should quickly connect to the controller.
|
||||
Setup is now complete. Otherwise, proceed to step 5.
|
||||
|
||||
5. If you are using the same network for control and data, and the
|
||||
switch obtains its IP address dynamically, then you may now obtain
|
||||
|
||||
@@ -1,7 +1,6 @@
|
||||
# -*- makefile -*-
|
||||
|
||||
AM_CPPFLAGS = $(SSL_CFLAGS)
|
||||
LIBS = $(SSL_LIBS)
|
||||
|
||||
COMMON_FLAGS = -DVERSION=\"$(VERSION)\"
|
||||
if NDEBUG
|
||||
|
||||
+5
-4
@@ -1,10 +1,12 @@
|
||||
AC_PREREQ(2.59)
|
||||
AC_INIT(openflow, v0.8.1, info@openflowswitch.org)
|
||||
AC_CONFIG_HEADERS([config.h])
|
||||
AM_INIT_AUTOMAKE
|
||||
|
||||
AC_PROG_CC
|
||||
AC_PROG_CPP
|
||||
AC_PROG_LD
|
||||
AC_PROG_RANLIB
|
||||
|
||||
AC_ARG_VAR([PERL], [path to Perl interpreter])
|
||||
AC_PATH_PROG([PERL], perl, no)
|
||||
@@ -14,8 +16,6 @@ fi
|
||||
|
||||
AC_USE_SYSTEM_EXTENSIONS
|
||||
|
||||
AC_PROG_LIBTOOL
|
||||
|
||||
AC_ARG_ENABLE(
|
||||
[ndebug],
|
||||
[AC_HELP_STRING([--enable-ndebug],
|
||||
@@ -132,8 +132,9 @@ if test "$HAVE_OPENSSL" = yes; then
|
||||
fi
|
||||
|
||||
AC_CHECK_LIB([socket], [connect])
|
||||
AC_CHECK_LIB([resolv], [gethostbyname])
|
||||
AC_CHECK_LIB([dl], [dladdr])
|
||||
AC_SEARCH_LIBS([gethostbyname], [resolv], [RESOLVER_LIBS=-lresolv])
|
||||
AC_CHECK_LIB([dl], [dladdr], [FAULT_LIBS=-ldl])
|
||||
AC_SUBST([FAULT_LIBS])
|
||||
|
||||
CFLAGS="$CFLAGS -Wall -Wno-sign-compare"
|
||||
|
||||
|
||||
@@ -5,7 +5,7 @@ man_MANS = controller.8
|
||||
DISTCLEANFILES = controller.8
|
||||
|
||||
controller_SOURCES = controller.c
|
||||
controller_LDADD = ../lib/libopenflow.la -ldl
|
||||
controller_LDADD = ../lib/libopenflow.a $(FAULT_LIBS) $(SSL_LIBS)
|
||||
|
||||
EXTRA_DIST = controller.8.in
|
||||
controller.8: controller.8.in Makefile
|
||||
|
||||
@@ -28,6 +28,11 @@ this form is used.
|
||||
Listens for TCP connections from remote OpenFlow switches on
|
||||
\fIport\fR (default: 975).
|
||||
|
||||
.TP
|
||||
\fBpunix:\fIfile\fR
|
||||
Listens for connections from OpenFlow switches on the Unix domain
|
||||
server socket named \fIfile\fR.
|
||||
|
||||
.TP
|
||||
\fBnl:\fIdp_idx\fR
|
||||
The local Netlink datapath numbered \fIdp_idx\fR, as configured with
|
||||
@@ -46,6 +51,10 @@ The specified SSL \fIport\fR (default: 976) on the given remote
|
||||
The specified TCP \fIport\fR (default: 975) on the given remote
|
||||
\fIhost\fR.
|
||||
|
||||
.TP
|
||||
\fBunix:\fIfile\fR
|
||||
The Unix domain server socket named \fIfile\fR.
|
||||
|
||||
.SH OPTIONS
|
||||
.TP
|
||||
\fB-p\fR, \fB--private-key=\fIprivkey.pem\fR
|
||||
@@ -73,6 +82,21 @@ in the network passes through the controller.
|
||||
This option is most useful for debugging. It reduces switching
|
||||
performance, so it should not be used in production.
|
||||
|
||||
.TP
|
||||
\fB--max-idle=\fIsecs\fR|\fBpermanent\fR
|
||||
Sets \fIsecs\fR as the number of seconds that a flow set up by the
|
||||
controller will remain in the switch's flow table without any matching
|
||||
packets being seen. If \fBpermanent\fR is specified, which is not
|
||||
recommended, flows will never expire. The default is 60 seconds.
|
||||
|
||||
This option affects only flows set up by the OpenFlow controller. In
|
||||
some configurations, the OpenFlow secure channel can set up some flows
|
||||
on its own. To set the idle time for those flows, pass
|
||||
\fB--max-idle\fR to \fBsecchan\fR(8).
|
||||
|
||||
This option has no effect when \fB-n\fR (or \fB--noflow\fR) is in use
|
||||
(because the controller does not set up flows in that case).
|
||||
|
||||
.TP
|
||||
.BR \-H ", " \-\^\-hub
|
||||
By default, the controller acts as an L2 MAC-learning switch. This
|
||||
@@ -103,17 +127,18 @@ run as a background process.
|
||||
Prints a brief help message to the console.
|
||||
|
||||
.TP
|
||||
\fB-v\fR \fImodule\fB:\fIfacility\fB:\fIlevel\fR, \fB--verbose=\fImodule\fB:\fIfacility\fB:\fIlevel\fR
|
||||
\fB-v\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. 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.
|
||||
\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
|
||||
|
||||
+26
-3
@@ -31,8 +31,11 @@
|
||||
* derivatives without specific, written prior permission.
|
||||
*/
|
||||
|
||||
#include <config.h>
|
||||
|
||||
#include <errno.h>
|
||||
#include <getopt.h>
|
||||
#include <limits.h>
|
||||
#include <stdlib.h>
|
||||
#include <string.h>
|
||||
|
||||
@@ -42,6 +45,7 @@
|
||||
#include "daemon.h"
|
||||
#include "fault.h"
|
||||
#include "learning-switch.h"
|
||||
#include "openflow.h"
|
||||
#include "poll-loop.h"
|
||||
#include "rconn.h"
|
||||
#include "util.h"
|
||||
@@ -66,6 +70,9 @@ static bool learn_macs = true;
|
||||
/* Set up flows? (If not, every packet is processed at the controller.) */
|
||||
static bool setup_flows = true;
|
||||
|
||||
/* --max-idle: Maximum idle time, in seconds, before flows expire. */
|
||||
static int max_idle = 60;
|
||||
|
||||
static int do_switching(struct switch_ *);
|
||||
static void new_switch(struct switch_ *, struct vconn *, const char *name);
|
||||
static void parse_options(int argc, char *argv[]);
|
||||
@@ -189,7 +196,8 @@ static void
|
||||
new_switch(struct switch_ *sw, struct vconn *vconn, const char *name)
|
||||
{
|
||||
sw->rconn = rconn_new_from_vconn(name, 128, vconn);
|
||||
sw->lswitch = lswitch_create(sw->rconn, learn_macs, setup_flows);
|
||||
sw->lswitch = lswitch_create(sw->rconn, learn_macs,
|
||||
setup_flows ? max_idle : -1);
|
||||
}
|
||||
|
||||
static int
|
||||
@@ -215,11 +223,13 @@ do_switching(struct switch_ *sw)
|
||||
static void
|
||||
parse_options(int argc, char *argv[])
|
||||
{
|
||||
enum { OPT_MAX_IDLE = UCHAR_MAX + 1 };
|
||||
static struct option long_options[] = {
|
||||
{"detach", no_argument, 0, 'D'},
|
||||
{"pidfile", optional_argument, 0, 'P'},
|
||||
{"hub", no_argument, 0, 'H'},
|
||||
{"noflow", no_argument, 0, 'n'},
|
||||
{"max-idle", required_argument, 0, OPT_MAX_IDLE},
|
||||
{"verbose", optional_argument, 0, 'v'},
|
||||
{"help", no_argument, 0, 'h'},
|
||||
{"version", no_argument, 0, 'V'},
|
||||
@@ -243,7 +253,7 @@ parse_options(int argc, char *argv[])
|
||||
break;
|
||||
|
||||
case 'P':
|
||||
set_pidfile(optarg ? optarg : "controller.pid");
|
||||
set_pidfile(optarg);
|
||||
break;
|
||||
|
||||
case 'H':
|
||||
@@ -254,6 +264,18 @@ parse_options(int argc, char *argv[])
|
||||
setup_flows = false;
|
||||
break;
|
||||
|
||||
case OPT_MAX_IDLE:
|
||||
if (!strcmp(optarg, "permanent")) {
|
||||
max_idle = OFP_FLOW_PERMANENT;
|
||||
} else {
|
||||
max_idle = atoi(optarg);
|
||||
if (max_idle < 1 || max_idle > 65535) {
|
||||
fatal(0, "--max-idle argument must be between 1 and "
|
||||
"65535 or the word 'permanent'");
|
||||
}
|
||||
}
|
||||
break;
|
||||
|
||||
case 'h':
|
||||
usage();
|
||||
|
||||
@@ -290,7 +312,8 @@ usage(void)
|
||||
" -P, --pidfile[=FILE] create pidfile (default: %s/controller.pid)\n"
|
||||
" -H, --hub act as hub instead of learning switch\n"
|
||||
" -n, --noflow pass traffic, but don't add flows\n"
|
||||
" -v, --verbose=MODULE:FACILITY:LEVEL configure logging levels\n"
|
||||
" --max-idle=SECS max idle time for new flows\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",
|
||||
|
||||
@@ -7,7 +7,7 @@ SUBDIRS += linux-2.4
|
||||
endif
|
||||
|
||||
EXTRA_DIST = $(dist_headers) $(dist_sources)
|
||||
EXTRA_DIST += hwtable-dummy/Modules.mk hwtable-dummy/hwtable-dummy.c
|
||||
EXTRA_DIST += hwtable_dummy/Modules.mk hwtable_dummy/hwtable_dummy.c
|
||||
|
||||
# Suppress warnings about GNU extensions in Modules.mk files.
|
||||
AUTOMAKE_OPTIONS = -Wno-portability
|
||||
|
||||
@@ -5,7 +5,6 @@ openflow_sources = \
|
||||
chain.c \
|
||||
crc32.c \
|
||||
datapath.c \
|
||||
datapath_t.c \
|
||||
dp_dev.c \
|
||||
flow.c \
|
||||
forward.c \
|
||||
@@ -18,7 +17,6 @@ openflow_headers = \
|
||||
compat.h \
|
||||
crc32.h \
|
||||
datapath.h \
|
||||
datapath_t.h \
|
||||
dp_dev.h \
|
||||
flow.h \
|
||||
forward.h \
|
||||
|
||||
+8
-10
@@ -62,8 +62,7 @@ error:
|
||||
/* Searches 'chain' for a flow matching 'key', which must not have any wildcard
|
||||
* fields. Returns the flow if successful, otherwise a null pointer.
|
||||
*
|
||||
* Caller must hold rcu_read_lock, and not release it until it is done with the
|
||||
* returned flow. */
|
||||
* Caller must hold rcu_read_lock or dp_mutex. */
|
||||
struct sw_flow *chain_lookup(struct sw_chain *chain,
|
||||
const struct sw_flow_key *key)
|
||||
{
|
||||
@@ -85,12 +84,12 @@ struct sw_flow *chain_lookup(struct sw_chain *chain,
|
||||
* If successful, 'flow' becomes owned by the chain, otherwise it is retained
|
||||
* by the caller.
|
||||
*
|
||||
* Caller must hold rcu_read_lock. If insertion is successful, it must not
|
||||
* release rcu_read_lock until it is done with the inserted flow. */
|
||||
* Caller must hold dp_mutex. */
|
||||
int chain_insert(struct sw_chain *chain, struct sw_flow *flow)
|
||||
{
|
||||
int i;
|
||||
|
||||
might_sleep();
|
||||
for (i = 0; i < chain->n_tables; i++) {
|
||||
struct sw_table *t = chain->tables[i];
|
||||
if (t->insert(t, flow))
|
||||
@@ -107,18 +106,17 @@ int chain_insert(struct sw_chain *chain, struct sw_flow *flow)
|
||||
* iterating through the entire contents of each table for keys that contain
|
||||
* wildcards. Relatively cheap for fully specified keys.
|
||||
*
|
||||
* The caller need not hold any locks. */
|
||||
* Caller must hold dp_mutex. */
|
||||
int chain_delete(struct sw_chain *chain, const struct sw_flow_key *key,
|
||||
uint16_t priority, int strict)
|
||||
{
|
||||
int count = 0;
|
||||
int i;
|
||||
|
||||
might_sleep();
|
||||
for (i = 0; i < chain->n_tables; i++) {
|
||||
struct sw_table *t = chain->tables[i];
|
||||
rcu_read_lock();
|
||||
count += t->delete(t, key, priority, strict);
|
||||
rcu_read_unlock();
|
||||
}
|
||||
|
||||
return count;
|
||||
@@ -130,17 +128,17 @@ int chain_delete(struct sw_chain *chain, const struct sw_flow_key *key,
|
||||
* Expensive as currently implemented, since it iterates through the entire
|
||||
* contents of each table.
|
||||
*
|
||||
* The caller need not hold any locks. */
|
||||
* Caller must not hold dp_mutex, because individual tables take and release it
|
||||
* as necessary. */
|
||||
int chain_timeout(struct sw_chain *chain)
|
||||
{
|
||||
int count = 0;
|
||||
int i;
|
||||
|
||||
might_sleep();
|
||||
for (i = 0; i < chain->n_tables; i++) {
|
||||
struct sw_table *t = chain->tables[i];
|
||||
rcu_read_lock();
|
||||
count += t->timeout(chain->dp, t);
|
||||
rcu_read_unlock();
|
||||
}
|
||||
return count;
|
||||
}
|
||||
|
||||
+54
-88
@@ -28,6 +28,7 @@
|
||||
#include <linux/netfilter_bridge.h>
|
||||
#include <linux/inetdevice.h>
|
||||
#include <linux/list.h>
|
||||
#include <linux/rculist.h>
|
||||
|
||||
#include "openflow-netlink.h"
|
||||
#include "datapath.h"
|
||||
@@ -36,7 +37,6 @@
|
||||
#include "dp_dev.h"
|
||||
#include "forward.h"
|
||||
#include "flow.h"
|
||||
#include "datapath_t.h"
|
||||
|
||||
#include "compat.h"
|
||||
|
||||
@@ -64,15 +64,17 @@ static struct genl_multicast_group mc_group;
|
||||
/* It's hard to imagine wanting more than one datapath, but... */
|
||||
#define DP_MAX 32
|
||||
|
||||
/* datapaths. Protected on the read side by rcu_read_lock, on the write side
|
||||
* by dp_mutex.
|
||||
/* Datapaths. Protected on the read side by rcu_read_lock, on the write side
|
||||
* by dp_mutex. dp_mutex is almost completely redundant with genl_mutex
|
||||
* maintained by the Generic Netlink code, but the timeout path needs mutual
|
||||
* exclusion too.
|
||||
*
|
||||
* It is safe to access the datapath and net_bridge_port structures with just
|
||||
* the dp_mutex, but to access the chain you need to take the rcu_read_lock
|
||||
* also (because dp_mutex doesn't prevent flows from being destroyed).
|
||||
* dp_mutex.
|
||||
*/
|
||||
static struct datapath *dps[DP_MAX];
|
||||
static DEFINE_MUTEX(dp_mutex);
|
||||
DEFINE_MUTEX(dp_mutex);
|
||||
EXPORT_SYMBOL(dp_mutex);
|
||||
|
||||
static int dp_maint_func(void *data);
|
||||
static int send_port_status(struct net_bridge_port *p, uint8_t status);
|
||||
@@ -201,13 +203,9 @@ alloc_openflow_skb(struct datapath *dp, size_t openflow_len, uint8_t type,
|
||||
static int
|
||||
send_openflow_skb(struct sk_buff *skb, const struct sender *sender)
|
||||
{
|
||||
int err = (sender
|
||||
? genlmsg_unicast(skb, sender->pid)
|
||||
: genlmsg_multicast(skb, 0, mc_group.id, GFP_ATOMIC));
|
||||
if (err && net_ratelimit())
|
||||
printk(KERN_WARNING "send_openflow_skb: send failed: %d\n",
|
||||
err);
|
||||
return err;
|
||||
return (sender
|
||||
? genlmsg_unicast(skb, sender->pid)
|
||||
: genlmsg_multicast(skb, 0, mc_group.id, GFP_ATOMIC));
|
||||
}
|
||||
|
||||
/* Generates a unique datapath id. It incorporates the datapath index
|
||||
@@ -246,9 +244,7 @@ uint64_t gen_datapath_id(uint16_t dp_idx)
|
||||
}
|
||||
|
||||
/* Creates a new datapath numbered 'dp_idx'. Returns 0 for success or a
|
||||
* negative error code.
|
||||
*
|
||||
* Not called with any locks. */
|
||||
* negative error code. */
|
||||
static int new_dp(int dp_idx)
|
||||
{
|
||||
struct datapath *dp;
|
||||
@@ -260,9 +256,8 @@ static int new_dp(int dp_idx)
|
||||
if (!try_module_get(THIS_MODULE))
|
||||
return -ENODEV;
|
||||
|
||||
mutex_lock(&dp_mutex);
|
||||
dp = rcu_dereference(dps[dp_idx]);
|
||||
if (dp != NULL) {
|
||||
/* Exit early if a datapath with that number already exists. */
|
||||
if (dps[dp_idx]) {
|
||||
err = -EEXIST;
|
||||
goto err_unlock;
|
||||
}
|
||||
@@ -297,8 +292,7 @@ static int new_dp(int dp_idx)
|
||||
if (IS_ERR(dp->dp_task))
|
||||
goto err_destroy_chain;
|
||||
|
||||
rcu_assign_pointer(dps[dp_idx], dp);
|
||||
mutex_unlock(&dp_mutex);
|
||||
dps[dp_idx] = dp;
|
||||
|
||||
return 0;
|
||||
|
||||
@@ -311,12 +305,11 @@ err_destroy_dp_dev:
|
||||
err_free_dp:
|
||||
kfree(dp);
|
||||
err_unlock:
|
||||
mutex_unlock(&dp_mutex);
|
||||
module_put(THIS_MODULE);
|
||||
return err;
|
||||
}
|
||||
|
||||
/* Find and return a free port number under 'dp'. Called under dp_mutex. */
|
||||
/* Find and return a free port number under 'dp'. */
|
||||
static int find_portno(struct datapath *dp)
|
||||
{
|
||||
int i;
|
||||
@@ -354,7 +347,6 @@ static struct net_bridge_port *new_nbp(struct datapath *dp,
|
||||
return p;
|
||||
}
|
||||
|
||||
/* Called with dp_mutex. */
|
||||
int add_switch_port(struct datapath *dp, struct net_device *dev)
|
||||
{
|
||||
struct net_bridge_port *p;
|
||||
@@ -378,8 +370,7 @@ int add_switch_port(struct datapath *dp, struct net_device *dev)
|
||||
return 0;
|
||||
}
|
||||
|
||||
/* Delete 'p' from switch.
|
||||
* Called with dp_mutex. */
|
||||
/* Delete 'p' from switch. */
|
||||
static int del_switch_port(struct net_bridge_port *p)
|
||||
{
|
||||
/* First drop references to device. */
|
||||
@@ -403,7 +394,6 @@ static int del_switch_port(struct net_bridge_port *p)
|
||||
return 0;
|
||||
}
|
||||
|
||||
/* Called with dp_mutex. */
|
||||
static void del_dp(struct datapath *dp)
|
||||
{
|
||||
struct net_bridge_port *p, *n;
|
||||
@@ -447,10 +437,7 @@ static void
|
||||
do_port_input(struct net_bridge_port *p, struct sk_buff *skb)
|
||||
{
|
||||
/* Push the Ethernet header back on. */
|
||||
if (skb->protocol == htons(ETH_P_8021Q))
|
||||
skb_push(skb, VLAN_ETH_HLEN);
|
||||
else
|
||||
skb_push(skb, ETH_HLEN);
|
||||
skb_push(skb, ETH_HLEN);
|
||||
fwd_port_input(p->dp->chain, skb, p->port_no);
|
||||
}
|
||||
|
||||
@@ -474,8 +461,6 @@ static int dp_frame_hook(struct net_bridge_port *p, struct sk_buff **pskb)
|
||||
}
|
||||
#else
|
||||
/* NB: This has only been tested on 2.4.35 */
|
||||
|
||||
/* Called without any locks (?) */
|
||||
static void dp_frame_hook(struct sk_buff *skb)
|
||||
{
|
||||
struct net_bridge_port *p = skb->dev->br_port;
|
||||
@@ -820,6 +805,22 @@ dp_send_error_msg(struct datapath *dp, const struct sender *sender,
|
||||
return send_openflow_skb(skb, sender);
|
||||
}
|
||||
|
||||
int
|
||||
dp_send_echo_reply(struct datapath *dp, const struct sender *sender,
|
||||
const struct ofp_header *rq)
|
||||
{
|
||||
struct sk_buff *skb;
|
||||
struct ofp_header *reply;
|
||||
|
||||
reply = alloc_openflow_skb(dp, ntohs(rq->length), OFPT_ECHO_REPLY,
|
||||
sender, &skb);
|
||||
if (!reply)
|
||||
return -ENOMEM;
|
||||
|
||||
memcpy(reply + 1, rq + 1, ntohs(rq->length) - sizeof *rq);
|
||||
return send_openflow_skb(skb, sender);
|
||||
}
|
||||
|
||||
/* Generic Netlink interface.
|
||||
*
|
||||
* See netlink(7) for an introduction to netlink. See
|
||||
@@ -873,7 +874,6 @@ static int dp_genl_del(struct sk_buff *skb, struct genl_info *info)
|
||||
if (!info->attrs[DP_GENL_A_DP_IDX])
|
||||
return -EINVAL;
|
||||
|
||||
mutex_lock(&dp_mutex);
|
||||
dp = dp_get(nla_get_u32((info->attrs[DP_GENL_A_DP_IDX])));
|
||||
if (!dp)
|
||||
err = -ENOENT;
|
||||
@@ -881,7 +881,6 @@ static int dp_genl_del(struct sk_buff *skb, struct genl_info *info)
|
||||
del_dp(dp);
|
||||
err = 0;
|
||||
}
|
||||
mutex_unlock(&dp_mutex);
|
||||
return err;
|
||||
}
|
||||
|
||||
@@ -965,7 +964,6 @@ static int dp_genl_add_del_port(struct sk_buff *skb, struct genl_info *info)
|
||||
return -EINVAL;
|
||||
|
||||
/* Get datapath. */
|
||||
mutex_lock(&dp_mutex);
|
||||
dp = dp_get(nla_get_u32(info->attrs[DP_GENL_A_DP_IDX]));
|
||||
if (!dp) {
|
||||
err = -ENOENT;
|
||||
@@ -994,7 +992,6 @@ static int dp_genl_add_del_port(struct sk_buff *skb, struct genl_info *info)
|
||||
out_put:
|
||||
dev_put(port);
|
||||
out:
|
||||
mutex_unlock(&dp_mutex);
|
||||
return err;
|
||||
}
|
||||
|
||||
@@ -1025,26 +1022,22 @@ static int dp_genl_openflow(struct sk_buff *skb, struct genl_info *info)
|
||||
if (!info->attrs[DP_GENL_A_DP_IDX] || !va)
|
||||
return -EINVAL;
|
||||
|
||||
rcu_read_lock();
|
||||
dp = dp_get(nla_get_u32(info->attrs[DP_GENL_A_DP_IDX]));
|
||||
if (!dp) {
|
||||
err = -ENOENT;
|
||||
goto out;
|
||||
}
|
||||
if (!dp)
|
||||
return -ENOENT;
|
||||
|
||||
if (nla_len(va) < sizeof(struct ofp_header)) {
|
||||
err = -EINVAL;
|
||||
goto out;
|
||||
}
|
||||
if (nla_len(va) < sizeof(struct ofp_header))
|
||||
return -EINVAL;
|
||||
oh = nla_data(va);
|
||||
|
||||
sender.xid = oh->xid;
|
||||
sender.pid = info->snd_pid;
|
||||
sender.seq = info->snd_seq;
|
||||
err = fwd_control_input(dp->chain, &sender, nla_data(va), nla_len(va));
|
||||
|
||||
out:
|
||||
rcu_read_unlock();
|
||||
mutex_lock(&dp_mutex);
|
||||
err = fwd_control_input(dp->chain, &sender,
|
||||
nla_data(va), nla_len(va));
|
||||
mutex_unlock(&dp_mutex);
|
||||
return err;
|
||||
}
|
||||
|
||||
@@ -1360,7 +1353,6 @@ dp_genl_openflow_dumpit(struct sk_buff *skb, struct netlink_callback *cb)
|
||||
* struct genl_ops. This kluge supports earlier versions also. */
|
||||
cb->done = dp_genl_openflow_done;
|
||||
|
||||
rcu_read_lock();
|
||||
if (!cb->args[0]) {
|
||||
struct nlattr *attrs[DP_GENL_A_MAX + 1];
|
||||
struct ofp_stats_request *rq;
|
||||
@@ -1373,21 +1365,17 @@ dp_genl_openflow_dumpit(struct sk_buff *skb, struct netlink_callback *cb)
|
||||
if (err < 0)
|
||||
return err;
|
||||
|
||||
err = -EINVAL;
|
||||
|
||||
if (!attrs[DP_GENL_A_DP_IDX])
|
||||
goto out;
|
||||
return -EINVAL;
|
||||
dp_idx = nla_get_u16(attrs[DP_GENL_A_DP_IDX]);
|
||||
dp = dp_get(dp_idx);
|
||||
if (!dp) {
|
||||
err = -ENOENT;
|
||||
goto out;
|
||||
}
|
||||
if (!dp)
|
||||
return -ENOENT;
|
||||
|
||||
va = attrs[DP_GENL_A_OPENFLOW];
|
||||
len = nla_len(va);
|
||||
if (!va || len < sizeof *rq)
|
||||
goto out;
|
||||
return -EINVAL;
|
||||
|
||||
rq = nla_data(va);
|
||||
type = ntohs(rq->type);
|
||||
@@ -1396,12 +1384,12 @@ dp_genl_openflow_dumpit(struct sk_buff *skb, struct netlink_callback *cb)
|
||||
|| ntohs(rq->header.length) != len
|
||||
|| type >= ARRAY_SIZE(stats)
|
||||
|| !stats[type].dump)
|
||||
goto out;
|
||||
return -EINVAL;
|
||||
|
||||
s = &stats[type];
|
||||
body_len = len - offsetof(struct ofp_stats_request, body);
|
||||
if (body_len < s->min_body || body_len > s->max_body)
|
||||
goto out;
|
||||
return -EINVAL;
|
||||
|
||||
cb->args[0] = 1;
|
||||
cb->args[1] = dp_idx;
|
||||
@@ -1411,7 +1399,7 @@ dp_genl_openflow_dumpit(struct sk_buff *skb, struct netlink_callback *cb)
|
||||
void *state;
|
||||
err = s->init(dp, rq->body, body_len, &state);
|
||||
if (err)
|
||||
goto out;
|
||||
return err;
|
||||
cb->args[4] = (long) state;
|
||||
}
|
||||
} else if (cb->args[0] == 1) {
|
||||
@@ -1419,13 +1407,10 @@ dp_genl_openflow_dumpit(struct sk_buff *skb, struct netlink_callback *cb)
|
||||
s = &stats[cb->args[2]];
|
||||
|
||||
dp = dp_get(dp_idx);
|
||||
if (!dp) {
|
||||
err = -ENOENT;
|
||||
goto out;
|
||||
}
|
||||
if (!dp)
|
||||
return -ENOENT;
|
||||
} else {
|
||||
err = 0;
|
||||
goto out;
|
||||
return 0;
|
||||
}
|
||||
|
||||
sender.xid = cb->args[3];
|
||||
@@ -1434,10 +1419,8 @@ dp_genl_openflow_dumpit(struct sk_buff *skb, struct netlink_callback *cb)
|
||||
|
||||
osr = put_openflow_headers(dp, skb, OFPT_STATS_REPLY, &sender,
|
||||
&max_openflow_len);
|
||||
if (IS_ERR(osr)) {
|
||||
err = PTR_ERR(osr);
|
||||
goto out;
|
||||
}
|
||||
if (IS_ERR(osr))
|
||||
return PTR_ERR(osr);
|
||||
osr->type = htons(s - stats);
|
||||
osr->flags = 0;
|
||||
resize_openflow_skb(skb, &osr->header, max_openflow_len);
|
||||
@@ -1456,8 +1439,6 @@ dp_genl_openflow_dumpit(struct sk_buff *skb, struct netlink_callback *cb)
|
||||
err = skb->len;
|
||||
}
|
||||
|
||||
out:
|
||||
rcu_read_unlock();
|
||||
return err;
|
||||
}
|
||||
|
||||
@@ -1480,20 +1461,6 @@ static struct genl_ops dp_genl_ops_openflow = {
|
||||
.dumpit = dp_genl_openflow_dumpit,
|
||||
};
|
||||
|
||||
static struct nla_policy dp_genl_benchmark_policy[DP_GENL_A_MAX + 1] = {
|
||||
[DP_GENL_A_DP_IDX] = { .type = NLA_U32 },
|
||||
[DP_GENL_A_NPACKETS] = { .type = NLA_U32 },
|
||||
[DP_GENL_A_PSIZE] = { .type = NLA_U32 },
|
||||
};
|
||||
|
||||
static struct genl_ops dp_genl_ops_benchmark_nl = {
|
||||
.cmd = DP_GENL_C_BENCHMARK_NL,
|
||||
.flags = GENL_ADMIN_PERM, /* Requires CAP_NET_ADMIN privilege. */
|
||||
.policy = dp_genl_benchmark_policy,
|
||||
.doit = dp_genl_benchmark_nl,
|
||||
.dumpit = NULL,
|
||||
};
|
||||
|
||||
static struct genl_ops *dp_genl_all_ops[] = {
|
||||
/* Keep this operation first. Generic Netlink dispatching
|
||||
* looks up operations with linear search, so we want it at the
|
||||
@@ -1505,7 +1472,6 @@ static struct genl_ops *dp_genl_all_ops[] = {
|
||||
&dp_genl_ops_query_dp,
|
||||
&dp_genl_ops_add_port,
|
||||
&dp_genl_ops_del_port,
|
||||
&dp_genl_ops_benchmark_nl,
|
||||
};
|
||||
|
||||
static int dp_init_netlink(void)
|
||||
|
||||
@@ -3,6 +3,7 @@
|
||||
#ifndef DATAPATH_H
|
||||
#define DATAPATH_H 1
|
||||
|
||||
#include <linux/mutex.h>
|
||||
#include <linux/netlink.h>
|
||||
#include <linux/netdevice.h>
|
||||
#include <linux/skbuff.h>
|
||||
@@ -67,6 +68,8 @@ struct sender {
|
||||
uint32_t seq; /* Netlink sequence ID of request. */
|
||||
};
|
||||
|
||||
extern struct mutex dp_mutex;
|
||||
|
||||
int dp_output_port(struct datapath *, struct sk_buff *, int out_port);
|
||||
int dp_output_control(struct datapath *, struct sk_buff *, uint32_t,
|
||||
size_t, int);
|
||||
@@ -77,6 +80,8 @@ int dp_send_flow_expired(struct datapath *, struct sw_flow *);
|
||||
int dp_send_error_msg(struct datapath *, const struct sender *,
|
||||
uint16_t, uint16_t, const uint8_t *, size_t);
|
||||
int dp_update_port_flags(struct datapath *dp, const struct ofp_phy_port *opp);
|
||||
int dp_send_echo_reply(struct datapath *, const struct sender *,
|
||||
const struct ofp_header *);
|
||||
|
||||
/* Should hold at least RCU read lock when calling */
|
||||
struct datapath *dp_get(int dp_idx);
|
||||
|
||||
@@ -1,118 +0,0 @@
|
||||
#include "datapath_t.h"
|
||||
#include <linux/skbuff.h>
|
||||
#include <linux/in.h>
|
||||
#include <linux/ip.h>
|
||||
#include <linux/if_ether.h>
|
||||
#include <linux/udp.h>
|
||||
#include <linux/rcupdate.h>
|
||||
|
||||
#include "datapath.h"
|
||||
|
||||
static struct sk_buff *
|
||||
gen_sk_buff(struct datapath *dp, uint32_t packet_size)
|
||||
{
|
||||
int in_port;
|
||||
struct sk_buff *skb;
|
||||
struct ethhdr *eh;
|
||||
struct iphdr *ih;
|
||||
struct udphdr *uh;
|
||||
|
||||
for (in_port = 0; in_port < OFPP_MAX; in_port++) {
|
||||
if (dp->ports[in_port] != NULL)
|
||||
break;
|
||||
}
|
||||
|
||||
if (in_port == OFPP_MAX) {
|
||||
printk("benchmark: no in_port to send packets as\n");
|
||||
return NULL;
|
||||
}
|
||||
|
||||
skb = alloc_skb(packet_size, GFP_ATOMIC);
|
||||
if (!skb) {
|
||||
printk("benchmark: cannot allocate skb for benchmark\n");
|
||||
return NULL;
|
||||
}
|
||||
|
||||
skb_put(skb, packet_size);
|
||||
skb_set_mac_header(skb, 0);
|
||||
eh = eth_hdr(skb);
|
||||
memcpy(eh->h_dest, "\x12\x34\x56\x78\x9a\xbc", ETH_ALEN);
|
||||
memcpy(eh->h_source, "\xab\xcd\xef\x12\x34\x56", ETH_ALEN);
|
||||
eh->h_proto = htons(ETH_P_IP);
|
||||
skb_set_network_header(skb, sizeof(*eh));
|
||||
ih = ip_hdr(skb);
|
||||
ih->ihl = 5;
|
||||
ih->version = IPVERSION;
|
||||
ih->tos = 0;
|
||||
ih->tot_len = htons(packet_size - sizeof(*eh));
|
||||
ih->id = htons(12345);
|
||||
ih->frag_off = 0;
|
||||
ih->ttl = IPDEFTTL;
|
||||
ih->protocol = IPPROTO_UDP;
|
||||
ih->check = 0; /* want this to be right?! */
|
||||
ih->saddr = 0x12345678;
|
||||
ih->daddr = 0x1234abcd;
|
||||
skb_set_transport_header(skb, sizeof(*eh) + sizeof(*ih));
|
||||
uh = udp_hdr(skb);
|
||||
uh->source = htons(1234);
|
||||
uh->dest = htons(5678);
|
||||
uh->len = htons(packet_size - sizeof(*eh) - sizeof(*ih));
|
||||
uh->check = 0;
|
||||
if (dp_set_origin(dp, in_port, skb)) {
|
||||
printk("benchmark: could not set origin\n");
|
||||
kfree_skb(skb);
|
||||
return NULL;
|
||||
}
|
||||
|
||||
return skb;
|
||||
}
|
||||
|
||||
int
|
||||
dp_genl_benchmark_nl(struct sk_buff *skb, struct genl_info *info)
|
||||
{
|
||||
struct datapath *dp;
|
||||
uint32_t num_packets = 0;
|
||||
int i, err = 0;
|
||||
struct sk_buff *skb2;
|
||||
|
||||
if (!info->attrs[DP_GENL_A_DP_IDX] || !info->attrs[DP_GENL_A_NPACKETS]
|
||||
|| !info->attrs[DP_GENL_A_PSIZE])
|
||||
return -EINVAL;
|
||||
|
||||
num_packets = nla_get_u32((info->attrs[DP_GENL_A_NPACKETS]));
|
||||
|
||||
rcu_read_lock();
|
||||
dp = dp_get(nla_get_u32((info->attrs[DP_GENL_A_DP_IDX])));
|
||||
if (!dp)
|
||||
err = -ENOENT;
|
||||
else {
|
||||
if (num_packets == 0)
|
||||
goto benchmark_unlock;
|
||||
|
||||
skb2 = gen_sk_buff(dp, nla_get_u32((info->attrs[DP_GENL_A_PSIZE])));
|
||||
if (skb2 == NULL) {
|
||||
err = -ENOMEM;
|
||||
goto benchmark_unlock;
|
||||
}
|
||||
|
||||
for (i = 0; i < num_packets; i++) {
|
||||
struct sk_buff *copy = skb_get(skb2);
|
||||
if (copy == NULL) {
|
||||
printk("benchmark: skb_get failed\n");
|
||||
err = -ENOMEM;
|
||||
break;
|
||||
}
|
||||
if ((err = dp_output_control(dp, copy, -1,
|
||||
0, OFPR_ACTION)))
|
||||
{
|
||||
printk("benchmark: output control ret %d on iter %d\n", err, i);
|
||||
break;
|
||||
}
|
||||
}
|
||||
kfree_skb(skb2);
|
||||
}
|
||||
|
||||
benchmark_unlock:
|
||||
rcu_read_unlock();
|
||||
return err;
|
||||
}
|
||||
@@ -1,12 +0,0 @@
|
||||
#ifndef DATAPATH_T_H
|
||||
#define DATAPATH_T_H 1
|
||||
|
||||
#include <linux/socket.h>
|
||||
#include <linux/capability.h>
|
||||
#include <linux/skbuff.h>
|
||||
#include <net/genetlink.h>
|
||||
#include "openflow-netlink.h"
|
||||
|
||||
int dp_genl_benchmark_nl(struct sk_buff *, struct genl_info *);
|
||||
|
||||
#endif
|
||||
+10
-13
@@ -27,6 +27,7 @@ int dp_dev_recv(struct net_device *netdev, struct sk_buff *skb)
|
||||
{
|
||||
int len = skb->len;
|
||||
struct dp_dev *dp_dev = dp_dev_priv(netdev);
|
||||
skb->dev = netdev;
|
||||
skb->pkt_type = PACKET_HOST;
|
||||
skb->protocol = eth_type_trans(skb, netdev);
|
||||
if (in_interrupt())
|
||||
@@ -54,19 +55,16 @@ static int dp_dev_mac_addr(struct net_device *dev, void *p)
|
||||
static int dp_dev_xmit(struct sk_buff *skb, struct net_device *netdev)
|
||||
{
|
||||
struct dp_dev *dp_dev = dp_dev_priv(netdev);
|
||||
struct datapath *dp;
|
||||
struct datapath *dp = dp_dev->dp;
|
||||
|
||||
dp_dev->stats.tx_packets++;
|
||||
dp_dev->stats.tx_bytes += skb->len;
|
||||
|
||||
skb_reset_mac_header(skb);
|
||||
rcu_read_lock();
|
||||
dp = dp_dev->dp;
|
||||
if (likely(dp != NULL)) {
|
||||
dp_dev->stats.tx_packets++;
|
||||
dp_dev->stats.tx_bytes += skb->len;
|
||||
skb_reset_mac_header(skb);
|
||||
fwd_port_input(dp->chain, skb, OFPP_LOCAL);
|
||||
} else {
|
||||
dp_dev->stats.tx_dropped++;
|
||||
kfree_skb(skb);
|
||||
}
|
||||
fwd_port_input(dp->chain, skb, OFPP_LOCAL);
|
||||
rcu_read_unlock();
|
||||
|
||||
return 0;
|
||||
}
|
||||
|
||||
@@ -126,8 +124,7 @@ int dp_dev_setup(struct datapath *dp)
|
||||
|
||||
void dp_dev_destroy(struct datapath *dp)
|
||||
{
|
||||
struct dp_dev *dp_dev = dp_dev_priv(dp->netdev);
|
||||
dp_dev->dp = NULL;
|
||||
netif_tx_disable(dp->netdev);
|
||||
synchronize_net();
|
||||
unregister_netdev(dp->netdev);
|
||||
free_netdev(dp->netdev);
|
||||
|
||||
@@ -125,16 +125,6 @@ void flow_fill_match(struct ofp_match* to, const struct sw_flow_key* from)
|
||||
memset(to->pad, '\0', sizeof(to->pad));
|
||||
}
|
||||
|
||||
/* Returns true if 'flow' can be deleted and set up for a deferred free, false
|
||||
* if deletion has already been scheduled (by another thread).
|
||||
*
|
||||
* Caller must hold rcu_read_lock. */
|
||||
int flow_del(struct sw_flow *flow)
|
||||
{
|
||||
return !atomic_cmpxchg(&flow->deleted, 0, 1);
|
||||
}
|
||||
EXPORT_SYMBOL(flow_del);
|
||||
|
||||
/* Allocates and returns a new flow with 'n_actions' action, using allocation
|
||||
* flags 'flags'. Returns the new flow or a null pointer on failure. */
|
||||
struct sw_flow *flow_alloc(int n_actions, gfp_t flags)
|
||||
|
||||
+2
-11
@@ -2,7 +2,6 @@
|
||||
#define FLOW_H 1
|
||||
|
||||
#include <linux/kernel.h>
|
||||
#include <asm/atomic.h>
|
||||
#include <linux/spinlock.h>
|
||||
#include <linux/list.h>
|
||||
#include <linux/types.h>
|
||||
@@ -49,15 +48,9 @@ static inline void check_key_align(void)
|
||||
/* Locking:
|
||||
*
|
||||
* - Readers must take rcu_read_lock and hold it the entire time that the flow
|
||||
* must continue to exist. Readers need not take delete_lock. They *may*
|
||||
* examine 'deleted' *if* it is important not to read stale data.
|
||||
* must continue to exist.
|
||||
*
|
||||
* - Deleters must take rcu_read_lock and call flow_del to verify that another
|
||||
* thread has not already deleted the flow. If not, do a deferred free of
|
||||
* the flow with call_rcu, then rcu_assign_pointer or [h]list_del_rcu the
|
||||
* flow.
|
||||
*
|
||||
* - In-place update not yet contemplated.
|
||||
* - Writers must hold dp_mutex.
|
||||
*/
|
||||
struct sw_flow {
|
||||
struct sw_flow_key key;
|
||||
@@ -81,7 +74,6 @@ struct sw_flow {
|
||||
uint64_t packet_count; /* Number of packets associated with this entry */
|
||||
uint64_t byte_count; /* Number of bytes associated with this entry */
|
||||
|
||||
atomic_t deleted; /* 0 if not deleted, 1 if deleted. */
|
||||
struct rcu_head rcu;
|
||||
};
|
||||
|
||||
@@ -92,7 +84,6 @@ struct sw_flow *flow_alloc(int n_actions, gfp_t flags);
|
||||
void flow_free(struct sw_flow *);
|
||||
void flow_deferred_free(struct sw_flow *);
|
||||
void flow_extract(struct sk_buff *, uint16_t in_port, struct sw_flow_key *);
|
||||
int flow_del(struct sw_flow *);
|
||||
void flow_extract_match(struct sw_flow_key* to, const struct ofp_match* from);
|
||||
void flow_fill_match(struct ofp_match* to, const struct sw_flow_key* from);
|
||||
|
||||
|
||||
+41
-7
@@ -87,10 +87,16 @@ void execute_actions(struct datapath *dp, struct sk_buff *skb,
|
||||
max_len = ntohs(a->arg.output.max_len);
|
||||
} else {
|
||||
if (!make_writable(&skb)) {
|
||||
printk("make_writable failed\n");
|
||||
if (net_ratelimit())
|
||||
printk("make_writable failed\n");
|
||||
break;
|
||||
}
|
||||
skb = execute_setter(skb, eth_proto, key, a);
|
||||
if (!skb) {
|
||||
if (net_ratelimit())
|
||||
printk("execute_setter lost skb\n");
|
||||
return;
|
||||
}
|
||||
}
|
||||
}
|
||||
if (prev_port != -1)
|
||||
@@ -199,17 +205,23 @@ static struct sk_buff *vlan_pull_tag(struct sk_buff *skb)
|
||||
static struct sk_buff *modify_vlan(struct sk_buff *skb,
|
||||
const struct sw_flow_key *key, const struct ofp_action *a)
|
||||
{
|
||||
uint16_t new_id = a->arg.vlan_id;
|
||||
uint16_t new_id = ntohs(a->arg.vlan_id);
|
||||
|
||||
if (new_id != OFP_VLAN_NONE) {
|
||||
if (key->dl_vlan != htons(OFP_VLAN_NONE)) {
|
||||
/* Modify vlan id, but maintain other TCI values */
|
||||
struct vlan_ethhdr *vh = vlan_eth_hdr(skb);
|
||||
vh->h_vlan_TCI = (vh->h_vlan_TCI
|
||||
& ~(htons(VLAN_VID_MASK))) | htons(new_id);
|
||||
& ~(htons(VLAN_VID_MASK))) | a->arg.vlan_id;
|
||||
} else {
|
||||
/* Add vlan header */
|
||||
skb = vlan_put_tag(skb, new_id);
|
||||
|
||||
/* xxx The vlan_put_tag function, doesn't seem to work
|
||||
* xxx reliably when it attempts to use the hardware-accelerated
|
||||
* xxx version. We'll directly use the software version
|
||||
* xxx until the problem can be diagnosed.
|
||||
*/
|
||||
skb = __vlan_put_tag(skb, new_id);
|
||||
}
|
||||
} else {
|
||||
/* Remove an existing vlan header if it exists */
|
||||
@@ -342,6 +354,20 @@ recv_port_mod(struct sw_chain *chain, const struct sender *sender,
|
||||
return 0;
|
||||
}
|
||||
|
||||
static int
|
||||
recv_echo_request(struct sw_chain *chain, const struct sender *sender,
|
||||
const void *msg)
|
||||
{
|
||||
return dp_send_echo_reply(chain->dp, sender, msg);
|
||||
}
|
||||
|
||||
static int
|
||||
recv_echo_reply(struct sw_chain *chain, const struct sender *sender,
|
||||
const void *msg)
|
||||
{
|
||||
return 0;
|
||||
}
|
||||
|
||||
static int
|
||||
add_flow(struct sw_chain *chain, const struct ofp_flow_mod *ofm)
|
||||
{
|
||||
@@ -359,7 +385,8 @@ add_flow(struct sw_chain *chain, const struct ofp_flow_mod *ofm)
|
||||
const struct ofp_action *a = &ofm->actions[i];
|
||||
|
||||
if (a->type == htons(OFPAT_OUTPUT)
|
||||
&& a->arg.output.port == htons(OFPP_TABLE)) {
|
||||
&& (a->arg.output.port == htons(OFPP_TABLE)
|
||||
|| a->arg.output.port == htons(OFPP_NONE))) {
|
||||
/* xxx Send fancy new error message? */
|
||||
goto error;
|
||||
}
|
||||
@@ -379,7 +406,6 @@ add_flow(struct sw_chain *chain, const struct ofp_flow_mod *ofm)
|
||||
flow->init_time = jiffies;
|
||||
flow->byte_count = 0;
|
||||
flow->packet_count = 0;
|
||||
atomic_set(&flow->deleted, 0);
|
||||
spin_lock_init(&flow->lock);
|
||||
memcpy(flow->actions, ofm->actions, n_acts * sizeof *flow->actions);
|
||||
|
||||
@@ -471,6 +497,14 @@ fwd_control_input(struct sw_chain *chain, const struct sender *sender,
|
||||
sizeof (struct ofp_port_mod),
|
||||
recv_port_mod,
|
||||
},
|
||||
[OFPT_ECHO_REQUEST] = {
|
||||
sizeof (struct ofp_header),
|
||||
recv_echo_request,
|
||||
},
|
||||
[OFPT_ECHO_REPLY] = {
|
||||
sizeof (struct ofp_header),
|
||||
recv_echo_reply,
|
||||
},
|
||||
};
|
||||
|
||||
const struct openflow_packet *pkt;
|
||||
@@ -603,7 +637,7 @@ make_writable(struct sk_buff **pskb)
|
||||
if (skb_shared(*pskb) || skb_cloned(*pskb))
|
||||
goto copy_skb;
|
||||
|
||||
return pskb_may_pull(*pskb, 64); /* FIXME? */
|
||||
return pskb_may_pull(*pskb, 40); /* FIXME? */
|
||||
|
||||
copy_skb:
|
||||
nskb = skb_copy(*pskb, GFP_ATOMIC);
|
||||
|
||||
@@ -3,5 +3,5 @@ all_modules += hwtable_dummy
|
||||
|
||||
# Specify the source files that comprise the module.
|
||||
hwtable_dummy_sources = \
|
||||
hwtable_dummy/hwtable-dummy.c
|
||||
hwtable_dummy/hwtable_dummy.c
|
||||
|
||||
@@ -7,7 +7,6 @@
|
||||
/flow.c
|
||||
/forward.c
|
||||
/forward_t.c
|
||||
/datapath_t.c
|
||||
/table-hash.c
|
||||
/table-linear.c
|
||||
/table-mac.c
|
||||
|
||||
@@ -41,6 +41,7 @@ compat24_headers = \
|
||||
linux-2.4/compat-2.4/include/linux/netdevice.h \
|
||||
linux-2.4/compat-2.4/include/linux/netlink.h \
|
||||
linux-2.4/compat-2.4/include/linux/random.h \
|
||||
linux-2.4/compat-2.4/include/linux/rculist.h \
|
||||
linux-2.4/compat-2.4/include/linux/rcupdate.h \
|
||||
linux-2.4/compat-2.4/include/linux/sched.h \
|
||||
linux-2.4/compat-2.4/include/linux/skbuff.h \
|
||||
|
||||
@@ -2,6 +2,7 @@
|
||||
#define __LINUX_KERNEL_WRAPPER_H 1
|
||||
|
||||
#include_next <linux/kernel.h>
|
||||
#include <linux/config.h>
|
||||
|
||||
/**
|
||||
* container_of - cast a member of a structure out to the containing structure
|
||||
@@ -40,4 +41,26 @@ int vprintk(const char *msg, ...)
|
||||
/* Force a compilation error if condition is true */
|
||||
#define BUILD_BUG_ON(condition) ((void)sizeof(char[1 - 2*!!(condition)]))
|
||||
|
||||
/**
|
||||
* might_sleep - annotation for functions that can sleep
|
||||
*
|
||||
* this macro will print a stack trace if it is executed in an atomic
|
||||
* context (spinlock, irq-handler, ...).
|
||||
*
|
||||
* This is a useful debugging help to be able to catch problems early and not
|
||||
* be bitten later when the calling function happens to sleep when it is not
|
||||
* supposed to.
|
||||
*/
|
||||
#define might_resched() do { } while (0)
|
||||
|
||||
#ifdef CONFIG_DEBUG_SPINLOCK
|
||||
void __might_sleep(char *file, int line);
|
||||
# define might_sleep() \
|
||||
do { __might_sleep(__FILE__, __LINE__); might_resched(); } while (0)
|
||||
#else
|
||||
# define might_sleep() do { might_resched(); } while (0)
|
||||
#endif
|
||||
|
||||
#define might_sleep_if(cond) do { if (cond) might_sleep(); } while (0)
|
||||
|
||||
#endif
|
||||
@@ -0,0 +1,2 @@
|
||||
/* In Linux 2.6.26, part of list.h was broken out into rculist.h. */
|
||||
#include <linux/list.h>
|
||||
@@ -2,9 +2,11 @@
|
||||
* Distributed under the terms of the GNU GPL version 2.
|
||||
*/
|
||||
|
||||
#include <linux/config.h>
|
||||
#include <linux/kernel.h>
|
||||
#include <linux/module.h>
|
||||
#include <linux/slab.h>
|
||||
#include <asm/hardirq.h>
|
||||
|
||||
int vprintk(const char *msg, ...)
|
||||
{
|
||||
@@ -25,3 +27,20 @@ int vprintk(const char *msg, ...)
|
||||
}
|
||||
|
||||
EXPORT_SYMBOL(vprintk);
|
||||
|
||||
#ifdef CONFIG_DEBUG_SPINLOCK
|
||||
void __might_sleep(char *file, int line)
|
||||
{
|
||||
static unsigned long prev_jiffy; /* ratelimiting */
|
||||
|
||||
if ((in_interrupt()) && !oops_in_progress) {
|
||||
if (time_before(jiffies, prev_jiffy + HZ) && prev_jiffy)
|
||||
return;
|
||||
prev_jiffy = jiffies;
|
||||
printk(KERN_ERR "BUG: sleeping function called from invalid"
|
||||
" context at %s:%d\n", file, line);
|
||||
dump_stack();
|
||||
}
|
||||
}
|
||||
EXPORT_SYMBOL(__might_sleep);
|
||||
#endif
|
||||
@@ -5,7 +5,6 @@
|
||||
/crc32.c
|
||||
/crc_t.c
|
||||
/datapath.c
|
||||
/datapath_t.c
|
||||
/dp_dev.c
|
||||
/flow.c
|
||||
/forward.c
|
||||
|
||||
@@ -10,6 +10,7 @@ openflow_headers += \
|
||||
linux-2.6/compat-2.6/include/linux/mutex.h \
|
||||
linux-2.6/compat-2.6/include/linux/netlink.h \
|
||||
linux-2.6/compat-2.6/include/linux/random.h \
|
||||
linux-2.6/compat-2.6/include/linux/rculist.h \
|
||||
linux-2.6/compat-2.6/include/linux/skbuff.h \
|
||||
linux-2.6/compat-2.6/include/linux/tcp.h \
|
||||
linux-2.6/compat-2.6/include/linux/timer.h \
|
||||
|
||||
@@ -0,0 +1,12 @@
|
||||
#ifndef __LINUX_RCULIST_WRAPPER_H
|
||||
#define __LINUX_RCULIST_WRAPPER_H
|
||||
|
||||
#include <linux/version.h>
|
||||
#if LINUX_VERSION_CODE >= KERNEL_VERSION(2,6,26)
|
||||
#include_next <linux/rculist.h>
|
||||
#else
|
||||
/* Prior to 2.6.26, the contents of rculist.h were part of list.h. */
|
||||
#include <linux/list.h>
|
||||
#endif
|
||||
|
||||
#endif
|
||||
+12
-21
@@ -21,9 +21,8 @@ static void kmem_free(void *, size_t);
|
||||
|
||||
struct sw_table_hash {
|
||||
struct sw_table swt;
|
||||
spinlock_t lock;
|
||||
struct crc32 crc32;
|
||||
atomic_t n_flows;
|
||||
unsigned int n_flows;
|
||||
unsigned int bucket_mask; /* Number of buckets minus 1. */
|
||||
struct sw_flow **buckets;
|
||||
};
|
||||
@@ -47,22 +46,19 @@ static int table_hash_insert(struct sw_table *swt, struct sw_flow *flow)
|
||||
{
|
||||
struct sw_table_hash *th = (struct sw_table_hash *) swt;
|
||||
struct sw_flow **bucket;
|
||||
unsigned long int flags;
|
||||
int retval;
|
||||
|
||||
if (flow->key.wildcards != 0)
|
||||
return 0;
|
||||
|
||||
spin_lock_irqsave(&th->lock, flags);
|
||||
bucket = find_bucket(swt, &flow->key);
|
||||
if (*bucket == NULL) {
|
||||
atomic_inc(&th->n_flows);
|
||||
th->n_flows++;
|
||||
rcu_assign_pointer(*bucket, flow);
|
||||
retval = 1;
|
||||
} else {
|
||||
struct sw_flow *old_flow = *bucket;
|
||||
if (!memcmp(&old_flow->key, &flow->key, sizeof flow->key)
|
||||
&& flow_del(old_flow)) {
|
||||
if (!memcmp(&old_flow->key, &flow->key, sizeof flow->key)) {
|
||||
rcu_assign_pointer(*bucket, flow);
|
||||
flow_deferred_free(old_flow);
|
||||
retval = 1;
|
||||
@@ -70,19 +66,15 @@ static int table_hash_insert(struct sw_table *swt, struct sw_flow *flow)
|
||||
retval = 0;
|
||||
}
|
||||
}
|
||||
spin_unlock_irqrestore(&th->lock, flags);
|
||||
return retval;
|
||||
}
|
||||
|
||||
/* Caller must update n_flows. */
|
||||
static int do_delete(struct sw_flow **bucket, struct sw_flow *flow)
|
||||
{
|
||||
if (flow_del(flow)) {
|
||||
rcu_assign_pointer(*bucket, NULL);
|
||||
flow_deferred_free(flow);
|
||||
return 1;
|
||||
}
|
||||
return 0;
|
||||
rcu_assign_pointer(*bucket, NULL);
|
||||
flow_deferred_free(flow);
|
||||
return 1;
|
||||
}
|
||||
|
||||
/* Returns number of deleted flows. We can ignore the priority
|
||||
@@ -110,8 +102,7 @@ static int table_hash_delete(struct sw_table *swt,
|
||||
count += do_delete(bucket, flow);
|
||||
}
|
||||
}
|
||||
if (count)
|
||||
atomic_sub(count, &th->n_flows);
|
||||
th->n_flows -= count;
|
||||
return count;
|
||||
}
|
||||
|
||||
@@ -121,6 +112,7 @@ static int table_hash_timeout(struct datapath *dp, struct sw_table *swt)
|
||||
unsigned int i;
|
||||
int count = 0;
|
||||
|
||||
mutex_lock(&dp_mutex);
|
||||
for (i = 0; i <= th->bucket_mask; i++) {
|
||||
struct sw_flow **bucket = &th->buckets[i];
|
||||
struct sw_flow *flow = *bucket;
|
||||
@@ -130,9 +122,9 @@ static int table_hash_timeout(struct datapath *dp, struct sw_table *swt)
|
||||
dp_send_flow_expired(dp, flow);
|
||||
}
|
||||
}
|
||||
th->n_flows -= count;
|
||||
mutex_unlock(&dp_mutex);
|
||||
|
||||
if (count)
|
||||
atomic_sub(count, &th->n_flows);
|
||||
return count;
|
||||
}
|
||||
|
||||
@@ -191,7 +183,7 @@ static void table_hash_stats(struct sw_table *swt,
|
||||
{
|
||||
struct sw_table_hash *th = (struct sw_table_hash *) swt;
|
||||
stats->name = "hash";
|
||||
stats->n_flows = atomic_read(&th->n_flows);
|
||||
stats->n_flows = th->n_flows;
|
||||
stats->max_flows = th->bucket_mask + 1;
|
||||
}
|
||||
|
||||
@@ -223,9 +215,8 @@ struct sw_table *table_hash_create(unsigned int polynomial,
|
||||
swt->iterate = table_hash_iterate;
|
||||
swt->stats = table_hash_stats;
|
||||
|
||||
spin_lock_init(&th->lock);
|
||||
crc32_init(&th->crc32, polynomial);
|
||||
atomic_set(&th->n_flows, 0);
|
||||
th->n_flows = 0;
|
||||
|
||||
return swt;
|
||||
}
|
||||
|
||||
+19
-30
@@ -10,14 +10,13 @@
|
||||
|
||||
#include <linux/rcupdate.h>
|
||||
#include <linux/slab.h>
|
||||
#include <linux/list.h>
|
||||
#include <linux/rculist.h>
|
||||
|
||||
struct sw_table_linear {
|
||||
struct sw_table swt;
|
||||
|
||||
spinlock_t lock;
|
||||
unsigned int max_flows;
|
||||
atomic_t n_flows;
|
||||
unsigned int n_flows;
|
||||
struct list_head flows;
|
||||
struct list_head iter_flows;
|
||||
unsigned long int next_serial;
|
||||
@@ -38,7 +37,6 @@ static struct sw_flow *table_linear_lookup(struct sw_table *swt,
|
||||
static int table_linear_insert(struct sw_table *swt, struct sw_flow *flow)
|
||||
{
|
||||
struct sw_table_linear *tl = (struct sw_table_linear *) swt;
|
||||
unsigned long int flags;
|
||||
struct sw_flow *f;
|
||||
|
||||
|
||||
@@ -46,16 +44,13 @@ static int table_linear_insert(struct sw_table *swt, struct sw_flow *flow)
|
||||
* always be placed behind those with equal priority. Just replace
|
||||
* any flows that match exactly.
|
||||
*/
|
||||
spin_lock_irqsave(&tl->lock, flags);
|
||||
list_for_each_entry_rcu (f, &tl->flows, node) {
|
||||
list_for_each_entry (f, &tl->flows, node) {
|
||||
if (f->priority == flow->priority
|
||||
&& f->key.wildcards == flow->key.wildcards
|
||||
&& flow_matches(&f->key, &flow->key)
|
||||
&& flow_del(f)) {
|
||||
&& flow_matches(&f->key, &flow->key)) {
|
||||
flow->serial = f->serial;
|
||||
list_replace_rcu(&f->node, &flow->node);
|
||||
list_replace_rcu(&f->iter_node, &flow->iter_node);
|
||||
spin_unlock_irqrestore(&tl->lock, flags);
|
||||
flow_deferred_free(f);
|
||||
return 1;
|
||||
}
|
||||
@@ -65,29 +60,24 @@ static int table_linear_insert(struct sw_table *swt, struct sw_flow *flow)
|
||||
}
|
||||
|
||||
/* Make sure there's room in the table. */
|
||||
if (atomic_read(&tl->n_flows) >= tl->max_flows) {
|
||||
spin_unlock_irqrestore(&tl->lock, flags);
|
||||
if (tl->n_flows >= tl->max_flows) {
|
||||
return 0;
|
||||
}
|
||||
atomic_inc(&tl->n_flows);
|
||||
tl->n_flows++;
|
||||
|
||||
/* Insert the entry immediately in front of where we're pointing. */
|
||||
flow->serial = tl->next_serial++;
|
||||
list_add_tail_rcu(&flow->node, &f->node);
|
||||
list_add_rcu(&flow->iter_node, &tl->iter_flows);
|
||||
spin_unlock_irqrestore(&tl->lock, flags);
|
||||
return 1;
|
||||
}
|
||||
|
||||
static int do_delete(struct sw_table *swt, struct sw_flow *flow)
|
||||
{
|
||||
if (flow_del(flow)) {
|
||||
list_del_rcu(&flow->node);
|
||||
list_del_rcu(&flow->iter_node);
|
||||
flow_deferred_free(flow);
|
||||
return 1;
|
||||
}
|
||||
return 0;
|
||||
list_del_rcu(&flow->node);
|
||||
list_del_rcu(&flow->iter_node);
|
||||
flow_deferred_free(flow);
|
||||
return 1;
|
||||
}
|
||||
|
||||
static int table_linear_delete(struct sw_table *swt,
|
||||
@@ -97,13 +87,12 @@ static int table_linear_delete(struct sw_table *swt,
|
||||
struct sw_flow *flow;
|
||||
unsigned int count = 0;
|
||||
|
||||
list_for_each_entry_rcu (flow, &tl->flows, node) {
|
||||
list_for_each_entry (flow, &tl->flows, node) {
|
||||
if (flow_del_matches(&flow->key, key, strict)
|
||||
&& (!strict || (flow->priority == priority)))
|
||||
count += do_delete(swt, flow);
|
||||
}
|
||||
if (count)
|
||||
atomic_sub(count, &tl->n_flows);
|
||||
tl->n_flows -= count;
|
||||
return count;
|
||||
}
|
||||
|
||||
@@ -113,15 +102,16 @@ static int table_linear_timeout(struct datapath *dp, struct sw_table *swt)
|
||||
struct sw_flow *flow;
|
||||
int count = 0;
|
||||
|
||||
list_for_each_entry_rcu (flow, &tl->flows, node) {
|
||||
mutex_lock(&dp_mutex);
|
||||
list_for_each_entry (flow, &tl->flows, node) {
|
||||
if (flow_timeout(flow)) {
|
||||
count += do_delete(swt, flow);
|
||||
if (dp->flags & OFPC_SEND_FLOW_EXP)
|
||||
dp_send_flow_expired(dp, flow);
|
||||
}
|
||||
}
|
||||
if (count)
|
||||
atomic_sub(count, &tl->n_flows);
|
||||
tl->n_flows -= count;
|
||||
mutex_unlock(&dp_mutex);
|
||||
return count;
|
||||
}
|
||||
|
||||
@@ -149,7 +139,7 @@ static int table_linear_iterate(struct sw_table *swt,
|
||||
unsigned long start;
|
||||
|
||||
start = position->private[0];
|
||||
list_for_each_entry_rcu (flow, &tl->iter_flows, iter_node) {
|
||||
list_for_each_entry (flow, &tl->iter_flows, iter_node) {
|
||||
if (flow->serial >= start && flow_matches(key, &flow->key)) {
|
||||
int error = callback(flow, private);
|
||||
if (error) {
|
||||
@@ -166,7 +156,7 @@ static void table_linear_stats(struct sw_table *swt,
|
||||
{
|
||||
struct sw_table_linear *tl = (struct sw_table_linear *) swt;
|
||||
stats->name = "linear";
|
||||
stats->n_flows = atomic_read(&tl->n_flows);
|
||||
stats->n_flows = tl->n_flows;
|
||||
stats->max_flows = tl->max_flows;
|
||||
}
|
||||
|
||||
@@ -190,10 +180,9 @@ struct sw_table *table_linear_create(unsigned int max_flows)
|
||||
swt->stats = table_linear_stats;
|
||||
|
||||
tl->max_flows = max_flows;
|
||||
atomic_set(&tl->n_flows, 0);
|
||||
tl->n_flows = 0;
|
||||
INIT_LIST_HEAD(&tl->flows);
|
||||
INIT_LIST_HEAD(&tl->iter_flows);
|
||||
spin_lock_init(&tl->lock);
|
||||
tl->next_serial = 0;
|
||||
|
||||
return swt;
|
||||
|
||||
Vendored
+151
-45
@@ -12,15 +12,20 @@ my $debconf_owner = 'openflow-switch';
|
||||
|
||||
my $default = '/etc/default/openflow-switch';
|
||||
my $etc = '/etc/openflow-switch';
|
||||
my $rundir = '/var/run';
|
||||
my $privkey_file = "$etc/of0-privkey.pem";
|
||||
my $req_file = "$etc/of0-req.pem";
|
||||
my $cert_file = "$etc/of0-cert.pem";
|
||||
my $cacert_file = "$etc/cacert.pem";
|
||||
my $ofp_discover_pidfile = "$rundir/ofp-discover.pid";
|
||||
|
||||
my $ua = LWP::UserAgent->new;
|
||||
$ua->timeout(10);
|
||||
$ua->env_proxy;
|
||||
|
||||
system("/etc/init.d/openflow-switch stop 1>&2");
|
||||
kill_ofp_discover();
|
||||
|
||||
version('2.0');
|
||||
capb('backup');
|
||||
title('OpenFlow Switch Setup');
|
||||
@@ -38,8 +43,9 @@ if (-e $default) {
|
||||
db_set('netdevs', join(', ', map($netdevs{$_},
|
||||
grep(exists $netdevs{$_}, split))))
|
||||
},
|
||||
IN_BAND => sub {
|
||||
db_set('band', $_ eq 'no' ? 'in-band' : 'out-of-band')
|
||||
MODE => sub {
|
||||
db_set('mode',
|
||||
$_ eq 'in-band' || $_ eq 'out-of-band' ? $_ : 'discovery')
|
||||
},
|
||||
SWITCH_IP => sub { db_set('switch-ip', $_) },
|
||||
CONTROLLER => sub { db_set('controller-vconn', $_) },
|
||||
@@ -55,21 +61,9 @@ if (-e $default) {
|
||||
}
|
||||
|
||||
my $cacert_preverified = -e $cacert_file;
|
||||
|
||||
if (! -e $privkey_file) {
|
||||
my $old_umask = umask(077);
|
||||
run_cmd("ofp-pki req $etc/of0 >&2 2>/dev/null");
|
||||
chmod(0644, $req_file) or die "$req_file: chmod: $!\n";
|
||||
umask($old_umask);
|
||||
}
|
||||
|
||||
my ($req, $req_fingerprint);
|
||||
if (! -e $cert_file) {
|
||||
open(REQ, '<', $req_file) or die "$req_file: open: $!\n";
|
||||
$req = join('', <REQ>);
|
||||
close(REQ);
|
||||
$req_fingerprint = sha1_hex($req);
|
||||
}
|
||||
|
||||
my %options;
|
||||
|
||||
my (@states) =
|
||||
(sub {
|
||||
@@ -101,12 +95,71 @@ my (@states) =
|
||||
}
|
||||
},
|
||||
sub {
|
||||
# In-band or out-of-band controller?
|
||||
db_input('band');
|
||||
# Discovery or in-band or out-of-band controller?
|
||||
db_input('mode');
|
||||
return;
|
||||
},
|
||||
sub {
|
||||
return 'skip' if db_get('band') eq 'out-of-band';
|
||||
return 'skip' if db_get('mode') ne 'discovery';
|
||||
for (;;) {
|
||||
# Notify user that we are going to do discovery.
|
||||
db_input('discover');
|
||||
return 'prev' if db_go();
|
||||
print STDERR "Please wait up to 30 seconds for discovery...\n";
|
||||
|
||||
# Make sure that there's no running discovery process.
|
||||
kill_ofp_discover();
|
||||
|
||||
# Do discovery.
|
||||
%options = ();
|
||||
open(DISCOVER, '-|', 'ofp-discover --timeout=30 --pidfile '
|
||||
. join(' ', netdev_names()));
|
||||
while (<DISCOVER>) {
|
||||
chomp;
|
||||
if (my ($name, $value) = /^([^=]+)=(.*)$/) {
|
||||
if ($value =~ /^"(.*)"$/) {
|
||||
$value = $1;
|
||||
$value =~ s/\\([0-7][0-7][0-7])/chr($1)/ge;
|
||||
} else {
|
||||
$value =~ s/^(0x[[:xdigit:]]+)$/hex($1)/e;
|
||||
$value = '' if $value eq 'empty';
|
||||
next if $value eq 'null'; # Shouldn't happen.
|
||||
}
|
||||
$options{$name} = $value;
|
||||
}
|
||||
last if /^$/;
|
||||
}
|
||||
|
||||
# Check results.
|
||||
my $vconn = $options{'ofp-controller-vconn'};
|
||||
my $pki_uri = $options{'ofp-pki-uri'};
|
||||
return 'next'
|
||||
if (defined($vconn)
|
||||
&& is_valid_vconn($vconn)
|
||||
&& (!is_ssl_vconn($vconn) || defined($pki_uri)));
|
||||
|
||||
# Try again?
|
||||
kill_ofp_discover();
|
||||
db_input('discovery-failure');
|
||||
db_go();
|
||||
}
|
||||
},
|
||||
sub {
|
||||
return 'skip' if db_get('mode') ne 'discovery';
|
||||
|
||||
my $vconn = $options{'ofp-controller-vconn'};
|
||||
my $pki_uri = $options{'ofp-pki-uri'};
|
||||
db_subst('discovery-success', 'controller-vconn', $vconn);
|
||||
db_subst('discovery-success',
|
||||
'pki-uri', is_ssl_vconn($vconn) ? $pki_uri : "no PKI in use");
|
||||
db_input('discovery-success');
|
||||
return 'prev' if db_go();
|
||||
db_set('controller-vconn', $vconn);
|
||||
db_set('pki-uri', $pki_uri);
|
||||
return 'next';
|
||||
},
|
||||
sub {
|
||||
return 'skip' if db_get('mode') ne 'in-band';
|
||||
for (;;) {
|
||||
db_input('switch-ip');
|
||||
return 'prev' if db_go();
|
||||
@@ -119,16 +172,16 @@ my (@states) =
|
||||
}
|
||||
},
|
||||
sub {
|
||||
return 'skip' if db_get('mode') eq 'discovery';
|
||||
for (;;) {
|
||||
my $old_vconn = db_get('controller-vconn');
|
||||
db_input('controller-vconn');
|
||||
return 'prev' if db_go();
|
||||
|
||||
my $vconn = db_get('controller-vconn');
|
||||
if ($vconn =~ /^(tcp|ssl):([^:]+)(:.*)?/) {
|
||||
if ($old_vconn ne $vconn
|
||||
|| db_get('pki-host') eq '') {
|
||||
db_set('pki-host', $2);
|
||||
if (is_valid_vconn($vconn)) {
|
||||
if ($old_vconn ne $vconn || db_get('pki-uri') eq '') {
|
||||
db_set('pki-uri', pki_host_to_uri($2));
|
||||
}
|
||||
return 'next';
|
||||
}
|
||||
@@ -137,11 +190,29 @@ my (@states) =
|
||||
db_go();
|
||||
}
|
||||
},
|
||||
sub {
|
||||
return 'skip' if !ssl_enabled();
|
||||
|
||||
if (! -e $privkey_file) {
|
||||
my $old_umask = umask(077);
|
||||
run_cmd("ofp-pki req $etc/of0 >&2 2>/dev/null");
|
||||
chmod(0644, $req_file) or die "$req_file: chmod: $!\n";
|
||||
umask($old_umask);
|
||||
}
|
||||
|
||||
if (! -e $cert_file) {
|
||||
open(REQ, '<', $req_file) or die "$req_file: open: $!\n";
|
||||
$req = join('', <REQ>);
|
||||
close(REQ);
|
||||
$req_fingerprint = sha1_hex($req);
|
||||
}
|
||||
return 'skip';
|
||||
},
|
||||
sub {
|
||||
return 'skip' if !ssl_enabled();
|
||||
return 'skip' if -e $cacert_file && -e $cert_file;
|
||||
|
||||
db_input('pki-host');
|
||||
db_input('pki-uri');
|
||||
return 'prev' if db_go();
|
||||
return;
|
||||
},
|
||||
@@ -149,8 +220,16 @@ my (@states) =
|
||||
return 'skip' if !ssl_enabled();
|
||||
return 'skip' if -e $cacert_file;
|
||||
|
||||
my $pki_host = db_get('pki-host');
|
||||
my $url = "http://$pki_host/openflow/pki/controllerca/cacert.pem";
|
||||
my $pki_uri = db_get('pki-uri');
|
||||
if ($pki_uri !~ /:/) {
|
||||
$pki_uri = pki_host_to_uri($pki_uri);
|
||||
} else {
|
||||
# Trim trailing slashes.
|
||||
$pki_uri =~ s%/+$%%;
|
||||
}
|
||||
db_set('pki-uri', $pki_uri);
|
||||
|
||||
my $url = "$pki_uri/controllerca/cacert.pem";
|
||||
my $response = $ua->get($url, ':content_file' => $cacert_file);
|
||||
if ($response->is_success) {
|
||||
return 'next';
|
||||
@@ -158,7 +237,7 @@ my (@states) =
|
||||
|
||||
db_subst('fetch-cacert-failed', 'url', $url);
|
||||
db_subst('fetch-cacert-failed', 'error', $response->status_line);
|
||||
db_subst('fetch-cacert-failed', 'pki-host', $pki_host);
|
||||
db_subst('fetch-cacert-failed', 'pki-uri', $pki_uri);
|
||||
db_input('fetch-cacert-failed');
|
||||
db_go();
|
||||
return 'prev';
|
||||
@@ -173,8 +252,9 @@ my (@states) =
|
||||
return 'prev' if db_go();
|
||||
return 'next' if db_get('send-cert-req') eq 'no';
|
||||
|
||||
my $pki_host = db_get('pki-host');
|
||||
my $url = "http://$pki_host/cgi-bin/ofp-pki-cgi";
|
||||
my $pki_uri = db_get('pki-uri');
|
||||
my ($pki_base_uri) = $pki_uri =~ m%^([^/]+://[^/]+)/%;
|
||||
my $url = "$pki_base_uri/cgi-bin/ofp-pki-cgi";
|
||||
my $response = $ua->post($url, {'type' => 'switch',
|
||||
'req' => $req});
|
||||
return 'next' if $response->is_success;
|
||||
@@ -182,8 +262,7 @@ my (@states) =
|
||||
db_subst('send-cert-req-failed', 'url', $url);
|
||||
db_subst('send-cert-req-failed', 'error',
|
||||
$response->status_line);
|
||||
db_subst('send-cert-req-failed', 'pki-host',
|
||||
$pki_host);
|
||||
db_subst('send-cert-req-failed', 'pki-uri', $pki_uri);
|
||||
db_input('send-cert-req-failed');
|
||||
db_go();
|
||||
}
|
||||
@@ -210,8 +289,8 @@ my (@states) =
|
||||
return 'prev' if db_go();
|
||||
exit(1) if db_get('fetch-switch-cert') eq 'no';
|
||||
|
||||
my $pki_host = db_get('pki-host');
|
||||
my $url = "http://$pki_host/openflow/pki/switchca/certs/$req_fingerprint-cert.pem";
|
||||
my $pki_uri = db_get('pki-uri');
|
||||
my $url = "$pki_uri/switchca/certs/$req_fingerprint-cert.pem";
|
||||
my $response = $ua->get($url, ':content_file' => $cert_file);
|
||||
if ($response->is_success) {
|
||||
return 'next';
|
||||
@@ -220,8 +299,7 @@ my (@states) =
|
||||
db_subst('fetch-switch-cert-failed', 'url', $url);
|
||||
db_subst('fetch-switch-cert-failed', 'error',
|
||||
$response->status_line);
|
||||
db_subst('fetch-switch-cert-failed', 'pki-host',
|
||||
$pki_host);
|
||||
db_subst('fetch-switch-cert-failed', 'pki-uri', $pki_uri);
|
||||
db_input('fetch-switch-cert-failed');
|
||||
db_go();
|
||||
}
|
||||
@@ -256,24 +334,25 @@ for (;;) {
|
||||
}
|
||||
|
||||
my %config;
|
||||
$config{NETDEVS} = join(' ', map(/^(\S+)/, split(', ', db_get('netdevs'))));
|
||||
if (db_get('band') eq 'in-band') {
|
||||
$config{IN_BAND} = 'yes';
|
||||
$config{NETDEVS} = join(' ', netdev_names());
|
||||
$config{MODE} = db_get('mode');
|
||||
if (db_get('mode') eq 'in-band') {
|
||||
$config{SWITCH_IP} = db_get('switch-ip');
|
||||
} else {
|
||||
$config{IN_BAND} = 'no';
|
||||
}
|
||||
$config{CONTROLLER} = db_get('controller-vconn');
|
||||
if (db_get('mode') ne 'discovery') {
|
||||
$config{CONTROLLER} = db_get('controller-vconn');
|
||||
}
|
||||
$config{PRIVKEY} = $privkey_file;
|
||||
$config{CERT} = $cert_file;
|
||||
$config{CACERT} = $cacert_file;
|
||||
save_config($default, %config);
|
||||
|
||||
dup2(2, 1); # Get stdout back.
|
||||
system("/etc/init.d/openflow-switch restart");
|
||||
kill_ofp_discover();
|
||||
system("/etc/init.d/openflow-switch start");
|
||||
|
||||
sub ssl_enabled {
|
||||
return db_get('controller-vconn') =~ /^ssl:/;
|
||||
return is_ssl_vconn(db_get('controller-vconn'));
|
||||
}
|
||||
|
||||
sub db_subst {
|
||||
@@ -288,7 +367,7 @@ sub db_subst {
|
||||
|
||||
sub db_set {
|
||||
my ($question, $value) = @_;
|
||||
$question = "$debconf_owner/$question";
|
||||
$question = "$debconf_owner/$question";
|
||||
my ($ret, $seen) = set($question, $value);
|
||||
if ($ret && $ret != 30) {
|
||||
die "Error setting debconf question $question to $value: $seen";
|
||||
@@ -415,7 +494,9 @@ sub load_config {
|
||||
|
||||
sub shell_escape {
|
||||
local $_ = $_[0];
|
||||
if (m&^[-a-zA-Z0-9:./%^_+,]*$&) {
|
||||
if ($_ eq '') {
|
||||
return '""';
|
||||
} elsif (m&^[-a-zA-Z0-9:./%^_+,]*$&) {
|
||||
return $_;
|
||||
} else {
|
||||
s/'/'\\''/;
|
||||
@@ -485,3 +566,28 @@ sub save_config {
|
||||
close(NEWFILE);
|
||||
rename("$file.tmp", $file) or die "$file.tmp: rename to $file: $!\n";
|
||||
}
|
||||
|
||||
sub pki_host_to_uri {
|
||||
my ($pki_host) = @_;
|
||||
return "http://$pki_host/openflow/pki";
|
||||
}
|
||||
|
||||
sub kill_ofp_discover {
|
||||
# Delegate this to a subprocess because there is no portable way
|
||||
# to invoke fcntl(F_GETLK) from Perl.
|
||||
system("ofp-kill --force $ofp_discover_pidfile");
|
||||
}
|
||||
|
||||
sub netdev_names {
|
||||
return map(/^(\S+)/, split(', ', db_get('netdevs')));
|
||||
}
|
||||
|
||||
sub is_valid_vconn {
|
||||
my ($vconn) = @_;
|
||||
return scalar($vconn =~ /^(tcp|ssl):([^:]+)(:.*)?/);
|
||||
}
|
||||
|
||||
sub is_ssl_vconn {
|
||||
my ($vconn) = @_;
|
||||
return scalar($vconn =~ /^ssl:/);
|
||||
}
|
||||
Vendored
+2
-2
@@ -1,2 +1,2 @@
|
||||
utilities/ofp-pki usr/sbin
|
||||
utilities/vlogconf usr/sbin
|
||||
_debian/utilities/ofp-pki usr/sbin
|
||||
_debian/utilities/vlogconf usr/sbin
|
||||
Vendored
+1
-1
@@ -1,2 +1,2 @@
|
||||
utilities/vlogconf.8
|
||||
utilities/ofp-pki.8
|
||||
_debian/utilities/ofp-pki.8
|
||||
+6
-1
@@ -1,5 +1,10 @@
|
||||
README.Debian for openflow-controller
|
||||
-------------------------------------
|
||||
|
||||
* To reconfigure the controller, edit /etc/default/openflow-controller
|
||||
* To (re)configure the controller, edit /etc/default/openflow-controller
|
||||
and run "/etc/init.d/openflow-controller restart".
|
||||
|
||||
* To enable OpenFlow switches to automatically discover the location
|
||||
of the controller, you must install and configure a DHCP server.
|
||||
The secchan(8) manpage (found in the openflow-switch package) gives
|
||||
a working example configuration file for the ISC DHCP server.
|
||||
Vendored
+1
-1
@@ -1 +1 @@
|
||||
controller/controller usr/sbin
|
||||
_debian/controller/controller usr/sbin
|
||||
Vendored
+1
-1
@@ -1 +1 @@
|
||||
controller/controller.8
|
||||
_debian/controller/controller.8
|
||||
Vendored
+1
-1
@@ -1 +1 @@
|
||||
utilities/ofp-pki-cgi usr/lib/cgi-bin
|
||||
_debian/utilities/ofp-pki-cgi usr/lib/cgi-bin
|
||||
Vendored
-4
@@ -1,4 +0,0 @@
|
||||
#!/bin/sh
|
||||
set -e
|
||||
. /usr/share/debconf/confmodule
|
||||
# Nothing more to do -- the user is responsible for running ofp-switch-setup.
|
||||
Vendored
+18
-13
@@ -21,13 +21,18 @@
|
||||
# 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
|
||||
@@ -35,13 +40,12 @@
|
||||
# one of those selected as an OpenFlow switch port in the previous
|
||||
# question.
|
||||
#
|
||||
# Set IN_BAND to yes for in-band control, or to no for out-of-band
|
||||
# control.
|
||||
IN_BAND=yes
|
||||
# Set MODE to 'discovery', 'in-band', or 'out-of-band' for these
|
||||
# respective cases.
|
||||
MODE=discovery
|
||||
|
||||
# SWITCH_IP: For in-band communication with the controller, the
|
||||
# OpenFlow switch must be able to determine its own IP address. Its
|
||||
# IP address may be configured statically or dynamically:
|
||||
# SWITCH_IP: In 'in-band' mode, the switch's IP address may be
|
||||
# configured statically or dynamically:
|
||||
#
|
||||
# * For static configuration, specify the switch's IP address as a
|
||||
# string.
|
||||
@@ -51,7 +55,7 @@ IN_BAND=yes
|
||||
# if the network topology allows the switch to contact the DHCP
|
||||
# server before it connects to the OpenFlow controller.
|
||||
#
|
||||
# If IN_BAND is set to "no" above, this setting has no effect.
|
||||
# This setting has no effect unless MODE is set to 'in-band'.
|
||||
SWITCH_IP=dhcp
|
||||
|
||||
# CONTROLLER: Location of controller.
|
||||
@@ -59,6 +63,7 @@ SWITCH_IP=dhcp
|
||||
# tcp:HOST[:PORT] via TCP to PORT (default: 975) on HOST
|
||||
# ssl:HOST[:PORT] via SSL to PORT (default: 976) on HOST
|
||||
# The default below assumes that the controller is running locally.
|
||||
# This setting has no effect when MODE is set to 'discovery'.
|
||||
#CONTROLLER="tcp:127.0.0.1"
|
||||
|
||||
# PRIVKEY: Name of file containing switch's private key.
|
||||
|
||||
Vendored
+53
-29
@@ -27,6 +27,7 @@ test -x $DAEMON || exit 0
|
||||
|
||||
LOGDIR=/var/log/openflow
|
||||
PIDFILE=/var/run/$NAME.pid
|
||||
MGMTSOCK=/var/run/$NAME.socket
|
||||
DHCLIENT_PIDFILE=/var/run/dhclient.of0.pid
|
||||
DODTIME=1 # Time to wait for the server to die, in seconds
|
||||
# If this value is set too low you might not
|
||||
@@ -50,8 +51,14 @@ running_pid()
|
||||
[ ! -d /proc/$pid ] && return 1
|
||||
cmd=`cat /proc/$pid/cmdline | tr "\000" "\n"|head -n 1 |cut -d : -f 1`
|
||||
# Is this the expected child?
|
||||
[ "$cmd" != "$name" ] && return 1
|
||||
return 0
|
||||
case $cmd in
|
||||
$name|*/$name)
|
||||
return 0
|
||||
;;
|
||||
*)
|
||||
return 1
|
||||
;;
|
||||
esac
|
||||
}
|
||||
|
||||
running()
|
||||
@@ -108,6 +115,26 @@ check_op() {
|
||||
fi
|
||||
}
|
||||
|
||||
configure_ssl() {
|
||||
if test ! -e "$PRIVKEY" || test ! -e "$CERT" || test ! -e "$CACERT"; then
|
||||
if test ! -e "$PRIVKEY"; then
|
||||
echo "$PRIVKEY: private key missing" >&2
|
||||
fi
|
||||
if test ! -e "$CERT"; then
|
||||
echo "$CERT: certificate for private key missing" >&2
|
||||
fi
|
||||
if test ! -e "$CACERT"; then
|
||||
echo "$CACERT: CA certificate missing" >&2
|
||||
fi
|
||||
echo "Run ofp-switch-setup or edit /etc/default/openflow-switch to configure" >&2
|
||||
if test "$MODE" = discovery; then
|
||||
echo "You may also delete or rename $PRIVKEY to disable SSL requirement" >&2
|
||||
fi
|
||||
exit 1
|
||||
fi
|
||||
SSL_OPTS="--private-key=$PRIVKEY --certificate=$CERT --ca-cert=$CACERT"
|
||||
}
|
||||
|
||||
case "$1" in
|
||||
start)
|
||||
if test -z "$NETDEVS"; then
|
||||
@@ -115,40 +142,36 @@ case "$1" in
|
||||
echo "Run ofp-switch-setup or edit /etc/default/openflow-switch to configure" >&2
|
||||
exit 0
|
||||
fi
|
||||
if test -z "$CONTROLLER"; then
|
||||
echo "$default: No controller configured, switch disabled" >&2
|
||||
echo "Run ofp-switch-setup or edit /etc/default/openflow-switch to configure" >&2
|
||||
exit 0
|
||||
fi
|
||||
if test "$IN_BAND" != yes && test "$IN_BAND" != no; then
|
||||
echo "$default: IN_BAND must set to 'yes' or 'no'" >&2
|
||||
if test "$MODE" = discovery; then
|
||||
unset CONTROLLER
|
||||
elif test "$MODE" = in-band || test "$MODE" = out-of-band; then
|
||||
if test -z "$CONTROLLER"; then
|
||||
echo "$default: No controller configured and not configured for discovery, switch disabled" >&2
|
||||
echo "Run ofp-switch-setup or edit /etc/default/openflow-switch to configure" >&2
|
||||
exit 0
|
||||
fi
|
||||
else
|
||||
echo "$default: MODE must set to 'discovery', 'in-band', or 'out-of-band'" >&2
|
||||
echo "Run ofp-switch-setup or edit /etc/default/openflow-switch to configure" >&2
|
||||
exit 1
|
||||
fi
|
||||
: ${PRIVKEY:=/etc/openflow-switch/of0-privkey.pem}
|
||||
: ${CERT:=/etc/openflow-switch/of0-cert.pem}
|
||||
: ${CACERT:=/etc/openflow-switch/cacert.pem}
|
||||
case $CONTROLLER in
|
||||
'')
|
||||
# Discovery mode.
|
||||
if test -e "$PRIVKEY"; then
|
||||
configure_ssl
|
||||
fi
|
||||
;;
|
||||
tcp:*)
|
||||
;;
|
||||
ssl:*)
|
||||
: ${PRIVKEY:=/etc/openflow-switch/of0-privkey.pem}
|
||||
: ${CERT:=/etc/openflow-switch/of0-cert.pem}
|
||||
: ${CACERT:=/etc/openflow-switch/cacert.pem}
|
||||
if test ! -e "$PRIVKEY" || test ! -e "$CERT" ||
|
||||
test ! -e "$CACERT"; then
|
||||
if test ! -e "$PRIVKEY"; then
|
||||
echo "$PRIVKEY: private key missing" >&2
|
||||
fi
|
||||
if test ! -e "$CERT"; then
|
||||
echo "$CERT: certificate for private key missing" >&2
|
||||
fi
|
||||
if test ! -e "$CACERT"; then
|
||||
echo "$CACERT: CA certificate missing" >&2
|
||||
fi
|
||||
echo "Run ofp-switch-setup or edit /etc/default/openflow-switch to configure" >&2
|
||||
exit 1
|
||||
fi
|
||||
configure_ssl
|
||||
;;
|
||||
*)
|
||||
echo "$default: CONTROLLER must be in the form 'ssl:HOST[:PORT]' or 'tcp:HOST[:PORT]'" >&2
|
||||
echo "$default: CONTROLLER must be in the form 'ssl:HOST[:PORT]' or 'tcp:HOST[:PORT]' when not in discovery mode" >&2
|
||||
echo "Run ofp-switch-setup or edit /etc/default/openflow-switch to configure" >&2
|
||||
exit 1
|
||||
esac
|
||||
@@ -174,7 +197,7 @@ case "$1" in
|
||||
must_succeed "Adding $netdev to datapath" dpctl addif nl:0 $netdev
|
||||
done
|
||||
|
||||
if test "$IN_BAND" = yes; then
|
||||
if test "$MODE" = in-band; then
|
||||
if test "$SWITCH_IP" = dhcp; then
|
||||
must_succeed "Temporarily disabling of0" ifconfig of0 down
|
||||
else
|
||||
@@ -187,6 +210,7 @@ case "$1" in
|
||||
echo -n "Starting $DESC: "
|
||||
start-stop-daemon --start --quiet --pidfile $PIDFILE \
|
||||
--exec $DAEMON -- nl:0 $CONTROLLER --detach --pidfile=$PIDFILE \
|
||||
--verbose=ANY:console:emer --listen=punix:$MGMTSOCK \
|
||||
$DAEMON_OPTS $SSL_OPTS
|
||||
if running; then
|
||||
echo "$NAME."
|
||||
@@ -194,7 +218,7 @@ case "$1" in
|
||||
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
|
||||
|
||||
Vendored
+5
-3
@@ -1,4 +1,6 @@
|
||||
switch/switch usr/sbin
|
||||
secchan/secchan usr/sbin
|
||||
utilities/dpctl usr/sbin
|
||||
_debian/switch/switch usr/sbin
|
||||
_debian/secchan/secchan usr/sbin
|
||||
_debian/utilities/dpctl usr/sbin
|
||||
_debian/utilities/ofp-discover usr/sbin
|
||||
_debian/utilities/ofp-kill usr/sbin
|
||||
debian/ofp-switch-setup usr/sbin
|
||||
Vendored
+4
-2
@@ -1,4 +1,6 @@
|
||||
debian/ofp-switch-setup.8
|
||||
secchan/secchan.8
|
||||
switch/switch.8
|
||||
_debian/secchan/secchan.8
|
||||
_debian/switch/switch.8
|
||||
_debian/utilities/ofp-discover.8
|
||||
_debian/utilities/ofp-kill.8
|
||||
utilities/dpctl.8
|
||||
Vendored
-14
@@ -1,14 +0,0 @@
|
||||
#!/bin/sh
|
||||
# postinst script for openflow-switch
|
||||
#
|
||||
# see: dh_installdeb(1)
|
||||
|
||||
set -e
|
||||
|
||||
. /usr/share/debconf/confmodule
|
||||
|
||||
#DEBHELPER#
|
||||
|
||||
exit 0
|
||||
|
||||
|
||||
Vendored
+55
-16
@@ -35,24 +35,59 @@ _Description: Some Network Devices Have IP or IPv6 Addresses
|
||||
If this is an unintentional mistake, move back and fix the selection,
|
||||
or de-configure the IP or IPv6 from these network devices.
|
||||
|
||||
Template: openflow-switch/band
|
||||
Template: openflow-switch/mode
|
||||
Type: select
|
||||
_Choices: in-band, out-of-band
|
||||
Default: in-band
|
||||
_Choices: discovery, in-band, out-of-band
|
||||
Default: discovery
|
||||
_Description: Switch-to-controller access method:
|
||||
The OpenFlow switch must be able to contact the OpenFlow controller over
|
||||
the network. It can do so in one of two ways:
|
||||
the network. It can do so in one of three ways:
|
||||
.
|
||||
in-band: A single network is used for OpenFlow traffic and other data
|
||||
traffic; that is, the switch contacts the controller over one of the
|
||||
network devices selected as OpenFlow switch netdevs in the previous
|
||||
question. This is the most common case.
|
||||
discovery: A single network is used for OpenFlow traffic and other
|
||||
data traffic; that is, the switch contacts the controller over one of
|
||||
the network devices selected as OpenFlow switch network devices in
|
||||
the previous question. The switch automatically determines the
|
||||
location of the controller using a DHCP request with an
|
||||
OpenFlow-specific vendor option. This is the most common case.
|
||||
.
|
||||
in-band: As above, but the location of the controller is manually
|
||||
configured.
|
||||
.
|
||||
out-of-band: OpenFlow traffic uses a network separate from the data traffic
|
||||
that it controls. If this is the case, the control network must already
|
||||
be configured on a network device other than one of those selected as
|
||||
an OpenFlow switch netdev in the previous question.
|
||||
|
||||
Template: openflow-switch/discover
|
||||
Type: note
|
||||
_Description: Preparing to discover controller.
|
||||
The setup program will now attempt to discover the OpenFlow controller.
|
||||
Controller discovery may take up to 30 seconds. Please be patient.
|
||||
.
|
||||
See secchan(8) for instructions on how to configure a DHCP server for
|
||||
controller discovery.
|
||||
|
||||
Template: openflow-switch/discovery-failure
|
||||
Type: error
|
||||
_Description: Controller discovery failed.
|
||||
The controller's location could not be determined automatically.
|
||||
.
|
||||
Ensure that the OpenFlow DHCP server is properly configured. See
|
||||
secchan(8) for instructions on how to configure a DHCP server for
|
||||
controller discovery.
|
||||
|
||||
Template: openflow-switch/discovery-success
|
||||
Type: boolean
|
||||
Default: true
|
||||
_Description: Use discovered settings?
|
||||
Controller discovery obtained the following settings:
|
||||
.
|
||||
Controller location: ${controller-vconn}
|
||||
.
|
||||
PKI URL: ${pki-uri}
|
||||
.
|
||||
Please verify that these settings are correct.
|
||||
|
||||
Template: openflow-switch/switch-ip
|
||||
Type: string
|
||||
Default: dhcp
|
||||
@@ -89,12 +124,16 @@ _Description: The controller location is invalid.
|
||||
connect to the controller over SSL (recommended for security) or
|
||||
"tcp:HOST[:PORT]" to connect over cleartext TCP.
|
||||
|
||||
Template: openflow-switch/pki-host
|
||||
Template: openflow-switch/pki-uri
|
||||
Type: string
|
||||
_Description: OpenFlow PKI server host name:
|
||||
Specify the host name or IP address of the server that hosts the OpenFlow
|
||||
public key infrastructure (PKI). This is usually the same host as the
|
||||
OpenFlow controller.
|
||||
_Description: OpenFlow PKI server host name or URL:
|
||||
Specify a URL to the OpenFlow public key infrastructure (PKI). If a
|
||||
host name or IP address is specified in place of a URL, then
|
||||
http://<host>/openflow/pki/ will be used,
|
||||
where <host> is the specified host name or IP address.
|
||||
.
|
||||
The OpenFlow PKI is usually on the same machine as the OpenFlow
|
||||
controller.
|
||||
.
|
||||
The setup process will connect to the OpenFlow PKI server over
|
||||
HTTP, using the system's configured default HTTP proxy (if any).
|
||||
@@ -105,7 +144,7 @@ _Description: The switch CA certificate could not be retrieved.
|
||||
Retrieval of ${url} failed, with the following status: "${error}".
|
||||
.
|
||||
Ensure that the OpenFlow PKI server is correctly configured and
|
||||
available on ${pki-host}. If the system is configured to use an HTTP
|
||||
available at ${pki-uri}. If the system is configured to use an HTTP
|
||||
proxy, also make sure that the HTTP proxy is available and that the
|
||||
PKI server can be reached through it.
|
||||
|
||||
@@ -147,7 +186,7 @@ _Description: The certificate request could not be sent.
|
||||
Posting to ${url} failed, with the following status: "${error}".
|
||||
.
|
||||
Ensure that the OpenFlow PKI server is correctly configured and
|
||||
available on ${pki-host}.
|
||||
available at ${pki-uri}.
|
||||
|
||||
Template: openflow-switch/fetch-switch-cert
|
||||
Type: select
|
||||
@@ -179,7 +218,7 @@ _Description: Signed switch certificate could not be retrieved.
|
||||
.
|
||||
This probably indicates that the switch's certificate request has not
|
||||
yet been signed. If this is the problem, it may be fixed by signing
|
||||
the certificate request at ${pki-host}, then trying to fetch the
|
||||
the certificate request at ${pki-uri}, then trying to fetch the
|
||||
signed switch certificate again.
|
||||
|
||||
Template: openflow-switch/complete
|
||||
|
||||
Vendored
+142
-53
@@ -8,7 +8,7 @@ msgid ""
|
||||
msgstr ""
|
||||
"Project-Id-Version: PACKAGE VERSION\n"
|
||||
"Report-Msgid-Bugs-To: openflow-dev@lists.stanford.edu\n"
|
||||
"POT-Creation-Date: 2008-06-12 14:45-0700\n"
|
||||
"POT-Creation-Date: 2008-07-23 14:55-0700\n"
|
||||
"PO-Revision-Date: YEAR-MO-DA HO:MI+ZONE\n"
|
||||
"Last-Translator: FULL NAME <EMAIL@ADDRESS>\n"
|
||||
"Language-Team: LANGUAGE <LL@li.org>\n"
|
||||
@@ -104,7 +104,7 @@ msgstr ""
|
||||
#. Type: select
|
||||
#. Choices
|
||||
#: ../openflow-switch.templates:4001
|
||||
msgid "in-band, out-of-band"
|
||||
msgid "discovery, in-band, out-of-band"
|
||||
msgstr ""
|
||||
|
||||
#. Type: select
|
||||
@@ -118,17 +118,26 @@ msgstr ""
|
||||
#: ../openflow-switch.templates:4002
|
||||
msgid ""
|
||||
"The OpenFlow switch must be able to contact the OpenFlow controller over the "
|
||||
"network. It can do so in one of two ways:"
|
||||
"network. It can do so in one of three ways:"
|
||||
msgstr ""
|
||||
|
||||
#. Type: select
|
||||
#. Description
|
||||
#: ../openflow-switch.templates:4002
|
||||
msgid ""
|
||||
"in-band: A single network is used for OpenFlow traffic and other data "
|
||||
"discovery: A single network is used for OpenFlow traffic and other data "
|
||||
"traffic; that is, the switch contacts the controller over one of the network "
|
||||
"devices selected as OpenFlow switch netdevs in the previous question. This "
|
||||
"is the most common case."
|
||||
"devices selected as OpenFlow switch network devices in the previous "
|
||||
"question. The switch automatically determines the location of the "
|
||||
"controller using a DHCP request with an OpenFlow-specific vendor option. "
|
||||
"This is the most common case."
|
||||
msgstr ""
|
||||
|
||||
#. Type: select
|
||||
#. Description
|
||||
#: ../openflow-switch.templates:4002
|
||||
msgid ""
|
||||
"in-band: As above, but the location of the controller is manually configured."
|
||||
msgstr ""
|
||||
|
||||
#. Type: select
|
||||
@@ -141,15 +150,87 @@ msgid ""
|
||||
"OpenFlow switch netdev in the previous question."
|
||||
msgstr ""
|
||||
|
||||
#. Type: string
|
||||
#. Type: note
|
||||
#. Description
|
||||
#: ../openflow-switch.templates:5001
|
||||
msgid "Preparing to discover controller."
|
||||
msgstr ""
|
||||
|
||||
#. Type: note
|
||||
#. Description
|
||||
#: ../openflow-switch.templates:5001
|
||||
msgid ""
|
||||
"The setup program will now attempt to discover the OpenFlow controller. "
|
||||
"Controller discovery may take up to 30 seconds. Please be patient."
|
||||
msgstr ""
|
||||
|
||||
#. Type: note
|
||||
#. Description
|
||||
#: ../openflow-switch.templates:5001
|
||||
msgid ""
|
||||
"See secchan(8) for instructions on how to configure a DHCP server for "
|
||||
"controller discovery."
|
||||
msgstr ""
|
||||
|
||||
#. Type: error
|
||||
#. Description
|
||||
#: ../openflow-switch.templates:6001
|
||||
msgid "Controller discovery failed."
|
||||
msgstr ""
|
||||
|
||||
#. Type: error
|
||||
#. Description
|
||||
#: ../openflow-switch.templates:6001
|
||||
msgid "The controller's location could not be determined automatically."
|
||||
msgstr ""
|
||||
|
||||
#. Type: error
|
||||
#. Description
|
||||
#: ../openflow-switch.templates:6001
|
||||
msgid ""
|
||||
"Ensure that the OpenFlow DHCP server is properly configured. See secchan(8) "
|
||||
"for instructions on how to configure a DHCP server for controller discovery."
|
||||
msgstr ""
|
||||
|
||||
#. Type: boolean
|
||||
#. Description
|
||||
#: ../openflow-switch.templates:7001
|
||||
msgid "Use discovered settings?"
|
||||
msgstr ""
|
||||
|
||||
#. Type: boolean
|
||||
#. Description
|
||||
#: ../openflow-switch.templates:7001
|
||||
msgid "Controller discovery obtained the following settings:"
|
||||
msgstr ""
|
||||
|
||||
#. Type: boolean
|
||||
#. Description
|
||||
#: ../openflow-switch.templates:7001
|
||||
msgid "Controller location: ${controller-vconn}"
|
||||
msgstr ""
|
||||
|
||||
#. Type: boolean
|
||||
#. Description
|
||||
#: ../openflow-switch.templates:7001
|
||||
msgid "PKI URL: ${pki-uri}"
|
||||
msgstr ""
|
||||
|
||||
#. Type: boolean
|
||||
#. Description
|
||||
#: ../openflow-switch.templates:7001
|
||||
msgid "Please verify that these settings are correct."
|
||||
msgstr ""
|
||||
|
||||
#. Type: string
|
||||
#. Description
|
||||
#: ../openflow-switch.templates:8001
|
||||
msgid "Switch IP address:"
|
||||
msgstr ""
|
||||
|
||||
#. Type: string
|
||||
#. Description
|
||||
#: ../openflow-switch.templates:5001
|
||||
#: ../openflow-switch.templates:8001
|
||||
msgid ""
|
||||
"For in-band communication with the controller, the OpenFlow switch must be "
|
||||
"able to determine its own IP address. Its IP address may be configured "
|
||||
@@ -158,13 +239,13 @@ msgstr ""
|
||||
|
||||
#. Type: string
|
||||
#. Description
|
||||
#: ../openflow-switch.templates:5001
|
||||
#: ../openflow-switch.templates:8001
|
||||
msgid "For static configuration, specify the switch's IP address as a string."
|
||||
msgstr ""
|
||||
|
||||
#. Type: string
|
||||
#. Description
|
||||
#: ../openflow-switch.templates:5001
|
||||
#: ../openflow-switch.templates:8001
|
||||
msgid ""
|
||||
"For dynamic configuration with DHCP (the most common case), specify \"dhcp"
|
||||
"\". Configuration with DHCP will only work reliably if the network topology "
|
||||
@@ -174,13 +255,13 @@ msgstr ""
|
||||
|
||||
#. Type: error
|
||||
#. Description
|
||||
#: ../openflow-switch.templates:6001
|
||||
#: ../openflow-switch.templates:9001
|
||||
msgid "The switch IP address is invalid."
|
||||
msgstr ""
|
||||
|
||||
#. Type: error
|
||||
#. Description
|
||||
#: ../openflow-switch.templates:6001
|
||||
#: ../openflow-switch.templates:9001
|
||||
msgid ""
|
||||
"The switch IP address must specified as \"dhcp\" or a valid IP address in "
|
||||
"dotted-octet form (e.g. \"1.2.3.4\")."
|
||||
@@ -188,13 +269,13 @@ msgstr ""
|
||||
|
||||
#. Type: string
|
||||
#. Description
|
||||
#: ../openflow-switch.templates:7001
|
||||
#: ../openflow-switch.templates:10001
|
||||
msgid "Controller location:"
|
||||
msgstr ""
|
||||
|
||||
#. Type: string
|
||||
#. Description
|
||||
#: ../openflow-switch.templates:7001
|
||||
#: ../openflow-switch.templates:10001
|
||||
msgid ""
|
||||
"Specify how the OpenFlow switch should connect to the OpenFlow controller. "
|
||||
"The value should be in form \"ssl:HOST[:PORT]\" to connect to the controller "
|
||||
@@ -204,13 +285,13 @@ msgstr ""
|
||||
|
||||
#. Type: error
|
||||
#. Description
|
||||
#: ../openflow-switch.templates:8001
|
||||
#: ../openflow-switch.templates:11001
|
||||
msgid "The controller location is invalid."
|
||||
msgstr ""
|
||||
|
||||
#. Type: error
|
||||
#. Description
|
||||
#: ../openflow-switch.templates:8001
|
||||
#: ../openflow-switch.templates:11001
|
||||
msgid ""
|
||||
"The controller location must be specifed as \"ssl:HOST[:PORT]\" to connect "
|
||||
"to the controller over SSL (recommended for security) or \"tcp:HOST[:PORT]\" "
|
||||
@@ -219,22 +300,30 @@ msgstr ""
|
||||
|
||||
#. Type: string
|
||||
#. Description
|
||||
#: ../openflow-switch.templates:9001
|
||||
msgid "OpenFlow PKI server host name:"
|
||||
#: ../openflow-switch.templates:12001
|
||||
msgid "OpenFlow PKI server host name or URL:"
|
||||
msgstr ""
|
||||
|
||||
#. Type: string
|
||||
#. Description
|
||||
#: ../openflow-switch.templates:9001
|
||||
#: ../openflow-switch.templates:12001
|
||||
msgid ""
|
||||
"Specify the host name or IP address of the server that hosts the OpenFlow "
|
||||
"public key infrastructure (PKI). This is usually the same host as the "
|
||||
"OpenFlow controller."
|
||||
"Specify a URL to the OpenFlow public key infrastructure (PKI). If a host "
|
||||
"name or IP address is specified in place of a URL, then http://<host>/"
|
||||
"openflow/pki/ will be used, where <host> is the specified host name or IP "
|
||||
"address."
|
||||
msgstr ""
|
||||
|
||||
#. Type: string
|
||||
#. Description
|
||||
#: ../openflow-switch.templates:9001
|
||||
#: ../openflow-switch.templates:12001
|
||||
msgid ""
|
||||
"The OpenFlow PKI is usually on the same machine as the OpenFlow controller."
|
||||
msgstr ""
|
||||
|
||||
#. Type: string
|
||||
#. Description
|
||||
#: ../openflow-switch.templates:12001
|
||||
msgid ""
|
||||
"The setup process will connect to the OpenFlow PKI server over HTTP, using "
|
||||
"the system's configured default HTTP proxy (if any)."
|
||||
@@ -242,22 +331,22 @@ msgstr ""
|
||||
|
||||
#. Type: error
|
||||
#. Description
|
||||
#: ../openflow-switch.templates:10001
|
||||
#: ../openflow-switch.templates:13001
|
||||
msgid "The switch CA certificate could not be retrieved."
|
||||
msgstr ""
|
||||
|
||||
#. Type: error
|
||||
#. Description
|
||||
#: ../openflow-switch.templates:10001
|
||||
#: ../openflow-switch.templates:13001
|
||||
msgid "Retrieval of ${url} failed, with the following status: \"${error}\"."
|
||||
msgstr ""
|
||||
|
||||
#. Type: error
|
||||
#. Description
|
||||
#: ../openflow-switch.templates:10001
|
||||
#: ../openflow-switch.templates:13001
|
||||
msgid ""
|
||||
"Ensure that the OpenFlow PKI server is correctly configured and available on "
|
||||
"${pki-host}. If the system is configured to use an HTTP proxy, also make "
|
||||
"Ensure that the OpenFlow PKI server is correctly configured and available at "
|
||||
"${pki-uri}. If the system is configured to use an HTTP proxy, also make "
|
||||
"sure that the HTTP proxy is available and that the PKI server can be reached "
|
||||
"through it."
|
||||
msgstr ""
|
||||
@@ -268,20 +357,20 @@ msgstr ""
|
||||
#. Choices
|
||||
#. Type: select
|
||||
#. Choices
|
||||
#: ../openflow-switch.templates:11001 ../openflow-switch.templates:12001
|
||||
#: ../openflow-switch.templates:14001
|
||||
#: ../openflow-switch.templates:14001 ../openflow-switch.templates:15001
|
||||
#: ../openflow-switch.templates:17001
|
||||
msgid "yes, no"
|
||||
msgstr ""
|
||||
|
||||
#. Type: select
|
||||
#. Description
|
||||
#: ../openflow-switch.templates:11002
|
||||
#: ../openflow-switch.templates:14002
|
||||
msgid "Is ${fingerprint} the controller CA's fingerprint?"
|
||||
msgstr ""
|
||||
|
||||
#. Type: select
|
||||
#. Description
|
||||
#: ../openflow-switch.templates:11002
|
||||
#: ../openflow-switch.templates:14002
|
||||
msgid ""
|
||||
"If a man-in-the-middle attack is possible in your network environment, check "
|
||||
"that the controller CA's fingerprint is really ${fingerprint}. Answer \"yes"
|
||||
@@ -290,7 +379,7 @@ msgstr ""
|
||||
|
||||
#. Type: select
|
||||
#. Description
|
||||
#: ../openflow-switch.templates:11002
|
||||
#: ../openflow-switch.templates:14002
|
||||
msgid ""
|
||||
"If a man-in-the-middle attack is not a concern, there is no need to verify "
|
||||
"the fingerprint. Simply answer \"yes\"."
|
||||
@@ -298,13 +387,13 @@ msgstr ""
|
||||
|
||||
#. Type: select
|
||||
#. Description
|
||||
#: ../openflow-switch.templates:12002
|
||||
#: ../openflow-switch.templates:15002
|
||||
msgid "Send certificate request to switch CA?"
|
||||
msgstr ""
|
||||
|
||||
#. Type: select
|
||||
#. Description
|
||||
#: ../openflow-switch.templates:12002
|
||||
#: ../openflow-switch.templates:15002
|
||||
msgid ""
|
||||
"Before it can connect to the controller over SSL, the OpenFlow switch's key "
|
||||
"must be signed by the switch certificate authority (CA) located on the "
|
||||
@@ -314,7 +403,7 @@ msgstr ""
|
||||
|
||||
#. Type: select
|
||||
#. Description
|
||||
#: ../openflow-switch.templates:12002
|
||||
#: ../openflow-switch.templates:15002
|
||||
msgid ""
|
||||
"Answer \"yes\" to send a signing request to the switch CA now. This is "
|
||||
"ordinarily the correct choice. There is no harm in sending a given signing "
|
||||
@@ -323,7 +412,7 @@ msgstr ""
|
||||
|
||||
#. Type: select
|
||||
#. Description
|
||||
#: ../openflow-switch.templates:12002
|
||||
#: ../openflow-switch.templates:15002
|
||||
msgid ""
|
||||
"Answer \"no\" to skip sending a signing request to the switch CA. Unless the "
|
||||
"request has already been sent to the switch CA, manual sending of the "
|
||||
@@ -332,33 +421,33 @@ msgstr ""
|
||||
|
||||
#. Type: error
|
||||
#. Description
|
||||
#: ../openflow-switch.templates:13001
|
||||
#: ../openflow-switch.templates:16001
|
||||
msgid "The certificate request could not be sent."
|
||||
msgstr ""
|
||||
|
||||
#. Type: error
|
||||
#. Description
|
||||
#: ../openflow-switch.templates:13001
|
||||
#: ../openflow-switch.templates:16001
|
||||
msgid "Posting to ${url} failed, with the following status: \"${error}\"."
|
||||
msgstr ""
|
||||
|
||||
#. Type: error
|
||||
#. Description
|
||||
#: ../openflow-switch.templates:13001
|
||||
#: ../openflow-switch.templates:16001
|
||||
msgid ""
|
||||
"Ensure that the OpenFlow PKI server is correctly configured and available on "
|
||||
"${pki-host}."
|
||||
"Ensure that the OpenFlow PKI server is correctly configured and available at "
|
||||
"${pki-uri}."
|
||||
msgstr ""
|
||||
|
||||
#. Type: select
|
||||
#. Description
|
||||
#: ../openflow-switch.templates:14002
|
||||
#: ../openflow-switch.templates:17002
|
||||
msgid "Fetch signed switch certificate from PKI server?"
|
||||
msgstr ""
|
||||
|
||||
#. Type: select
|
||||
#. Description
|
||||
#: ../openflow-switch.templates:14002
|
||||
#: ../openflow-switch.templates:17002
|
||||
msgid ""
|
||||
"Before it can connect to the controller over SSL, the OpenFlow switch's key "
|
||||
"must be signed by the switch certificate authority (CA) located on the "
|
||||
@@ -368,7 +457,7 @@ msgstr ""
|
||||
|
||||
#. Type: select
|
||||
#. Description
|
||||
#: ../openflow-switch.templates:14002
|
||||
#: ../openflow-switch.templates:17002
|
||||
msgid ""
|
||||
"At this point, a signing request has been sent to the switch CA (or sending "
|
||||
"a request has been manually skipped), but the signed certificate has not yet "
|
||||
@@ -378,7 +467,7 @@ msgstr ""
|
||||
|
||||
#. Type: select
|
||||
#. Description
|
||||
#: ../openflow-switch.templates:14002
|
||||
#: ../openflow-switch.templates:17002
|
||||
msgid ""
|
||||
"Answer \"yes\" to attempt to retrieve the signed switch certificate from the "
|
||||
"switch CA. If the switch certificate request has been signed at the PKI "
|
||||
@@ -387,7 +476,7 @@ msgstr ""
|
||||
|
||||
#. Type: select
|
||||
#. Description
|
||||
#: ../openflow-switch.templates:14002
|
||||
#: ../openflow-switch.templates:17002
|
||||
msgid ""
|
||||
"Answer \"no\" to postpone switch configuration. The configuration process "
|
||||
"must be restarted later, when the switch certificate request has been signed."
|
||||
@@ -395,13 +484,13 @@ msgstr ""
|
||||
|
||||
#. Type: error
|
||||
#. Description
|
||||
#: ../openflow-switch.templates:15001
|
||||
#: ../openflow-switch.templates:18001
|
||||
msgid "Signed switch certificate could not be retrieved."
|
||||
msgstr ""
|
||||
|
||||
#. Type: error
|
||||
#. Description
|
||||
#: ../openflow-switch.templates:15001
|
||||
#: ../openflow-switch.templates:18001
|
||||
msgid ""
|
||||
"The signed switch certificate could not be retrieved from the switch CA: "
|
||||
"retrieval of ${url} failed, with the following status: \"${error}\"."
|
||||
@@ -409,23 +498,23 @@ msgstr ""
|
||||
|
||||
#. Type: error
|
||||
#. Description
|
||||
#: ../openflow-switch.templates:15001
|
||||
#: ../openflow-switch.templates:18001
|
||||
msgid ""
|
||||
"This probably indicates that the switch's certificate request has not yet "
|
||||
"been signed. If this is the problem, it may be fixed by signing the "
|
||||
"certificate request at ${pki-host}, then trying to fetch the signed switch "
|
||||
"certificate request at ${pki-uri}, then trying to fetch the signed switch "
|
||||
"certificate again."
|
||||
msgstr ""
|
||||
|
||||
#. Type: note
|
||||
#. Description
|
||||
#: ../openflow-switch.templates:16001
|
||||
#: ../openflow-switch.templates:19001
|
||||
msgid "OpenFlow Switch Setup Finished"
|
||||
msgstr ""
|
||||
|
||||
#. Type: note
|
||||
#. Description
|
||||
#: ../openflow-switch.templates:16001
|
||||
#: ../openflow-switch.templates:19001
|
||||
msgid ""
|
||||
"Setup of this OpenFlow switch is finished. Complete the setup procedure to "
|
||||
"enable the switch."
|
||||
|
||||
Vendored
+10
-7
@@ -29,8 +29,10 @@ configure: configure-stamp
|
||||
configure-stamp:
|
||||
dh_testdir
|
||||
test -e configure || ./boot.sh
|
||||
test -e Makefile || \
|
||||
./configure --prefix=/usr --localstatedir=/var --enable-ssl
|
||||
test -d _debian || mkdir _debian
|
||||
cd _debian && ( \
|
||||
test -e Makefile || \
|
||||
../configure --prefix=/usr --localstatedir=/var --enable-ssl)
|
||||
touch configure-stamp
|
||||
|
||||
#Architecture
|
||||
@@ -38,18 +40,19 @@ build: build-arch build-indep
|
||||
|
||||
build-arch: build-arch-stamp
|
||||
build-arch-stamp: configure-stamp
|
||||
$(MAKE)
|
||||
$(MAKE) -C _debian
|
||||
touch $@
|
||||
|
||||
build-indep: build-indep-stamp
|
||||
build-indep-stamp: configure-stamp
|
||||
$(MAKE) dist distdir=openflow
|
||||
$(MAKE) -C _debian dist distdir=openflow
|
||||
touch $@
|
||||
|
||||
clean:
|
||||
dh_testdir
|
||||
dh_testroot
|
||||
rm -f build-arch-stamp build-indep-stamp configure-stamp
|
||||
rm -rf _debian
|
||||
[ ! -f Makefile ] || $(MAKE) distclean
|
||||
dh_clean
|
||||
debconf-updatepo
|
||||
@@ -67,7 +70,7 @@ endif
|
||||
|
||||
kdist_clean:
|
||||
dh_clean
|
||||
make KERNELDIR=$(KSRC) KVERREL=$(KVERS) clean
|
||||
rm -rf openflow
|
||||
|
||||
kdist_config: prep-deb-files
|
||||
|
||||
@@ -97,7 +100,7 @@ install-indep: build-indep
|
||||
dh_testroot
|
||||
dh_clean -k -i
|
||||
dh_installdirs -i
|
||||
cp openflow.tar.gz $(MODDIR)
|
||||
cp _debian/openflow.tar.gz $(MODDIR)
|
||||
cd debian; cp changelog control compat *.modules.in rules $(MODDIR)/debian
|
||||
cd debian/openflow-datapath-source/usr/src && tar -c modules | bzip2 -9 > openflow-datapath.tar.bz2 && rm -rf modules
|
||||
install -m644 debian/openflow-pki.apache2 debian/openflow-pki/etc/apache2/sites-available/openflow-pki
|
||||
@@ -108,7 +111,7 @@ install-arch: build-arch
|
||||
dh_testroot
|
||||
dh_clean -k -s
|
||||
dh_installdirs -s
|
||||
$(MAKE) DESTDIR=$(CURDIR)/debian/openflow install
|
||||
$(MAKE) -C _debian DESTDIR=$(CURDIR)/debian/openflow install
|
||||
cp debian/openflow-switch.overrides debian/openflow-switch/usr/share/lintian/overrides/openflow-switch
|
||||
dh_install -s
|
||||
|
||||
|
||||
@@ -2,7 +2,10 @@ noinst_HEADERS = \
|
||||
buffer.h \
|
||||
command-line.h \
|
||||
compiler.h \
|
||||
csum.h \
|
||||
daemon.h \
|
||||
dhcp-client.h \
|
||||
dhcp.h \
|
||||
dynamic-string.h \
|
||||
dpif.h \
|
||||
fatal-signal.h \
|
||||
@@ -21,11 +24,15 @@ noinst_HEADERS = \
|
||||
packets.h \
|
||||
poll-loop.h \
|
||||
queue.h \
|
||||
random.h \
|
||||
rconn.h \
|
||||
socket-util.h \
|
||||
type-props.h \
|
||||
timeval.h \
|
||||
util.h \
|
||||
vconn.h \
|
||||
vconn-ssl.h \
|
||||
vconn-stream.h \
|
||||
vlog-socket.h \
|
||||
vlog.h \
|
||||
xtoxll.h \
|
||||
|
||||
+7
-2
@@ -48,6 +48,7 @@ struct buffer {
|
||||
void *l2; /* Link-level header. */
|
||||
void *l3; /* Network-level header. */
|
||||
void *l4; /* Transport-level header. */
|
||||
void *l7; /* Application data. */
|
||||
|
||||
struct buffer *next; /* Next in a list of buffers. */
|
||||
};
|
||||
@@ -69,13 +70,17 @@ void *buffer_end(const struct buffer *);
|
||||
|
||||
void *buffer_put_uninit(struct buffer *, size_t);
|
||||
void *buffer_put(struct buffer *, const void *, size_t);
|
||||
void buffer_reserve(struct buffer *, size_t);
|
||||
void *buffer_push_uninit(struct buffer *b, size_t);
|
||||
void *buffer_push(struct buffer *b, const void *, size_t);
|
||||
|
||||
size_t buffer_headroom(struct buffer *);
|
||||
size_t buffer_tailroom(struct buffer *);
|
||||
void buffer_reserve_tailroom(struct buffer *, size_t);
|
||||
void buffer_prealloc_headroom(struct buffer *, size_t);
|
||||
void buffer_prealloc_tailroom(struct buffer *, size_t);
|
||||
|
||||
void buffer_clear(struct buffer *);
|
||||
void buffer_pull(struct buffer *, size_t);
|
||||
void *buffer_pull(struct buffer *, size_t);
|
||||
void *buffer_try_pull(struct buffer *, size_t);
|
||||
|
||||
#endif /* buffer.h */
|
||||
@@ -0,0 +1,48 @@
|
||||
/* 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.
|
||||
*/
|
||||
|
||||
#ifndef CSUM_H
|
||||
#define CSUM_H 1
|
||||
|
||||
#include <stddef.h>
|
||||
#include <stdint.h>
|
||||
|
||||
uint16_t csum(const void *, size_t);
|
||||
uint32_t csum_add16(uint32_t partial, uint16_t);
|
||||
uint32_t csum_add32(uint32_t partial, uint32_t);
|
||||
uint32_t csum_continue(uint32_t partial, const void *, size_t);
|
||||
uint16_t csum_finish(uint32_t partial);
|
||||
uint16_t recalc_csum16(uint16_t old_csum, uint16_t old_u16, uint16_t new_u16);
|
||||
uint16_t recalc_csum32(uint16_t old_csum, uint32_t old_u32, uint32_t new_u32);
|
||||
|
||||
#endif /* csum.h */
|
||||
@@ -34,6 +34,7 @@
|
||||
#ifndef DAEMON_H
|
||||
#define DAEMON_H 1
|
||||
|
||||
char *make_pidfile_name(const char *name);
|
||||
void set_pidfile(const char *name);
|
||||
void set_detach(void);
|
||||
void daemonize(void);
|
||||
|
||||
@@ -0,0 +1,66 @@
|
||||
/* 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.
|
||||
*/
|
||||
|
||||
#ifndef DHCP_CLIENT_H
|
||||
#define DHCP_CLIENT_H 1
|
||||
|
||||
#include <stdbool.h>
|
||||
#include <stdint.h>
|
||||
|
||||
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),
|
||||
void *aux, struct dhclient **);
|
||||
void dhclient_destroy(struct dhclient *);
|
||||
|
||||
void dhclient_init(struct dhclient *, uint32_t requested_ip);
|
||||
void dhclient_release(struct dhclient *);
|
||||
void dhclient_force_renew(struct dhclient *, int deadline);
|
||||
bool dhclient_is_bound(const struct dhclient *);
|
||||
bool dhclient_changed(struct dhclient *);
|
||||
|
||||
uint32_t dhclient_get_ip(const struct dhclient *);
|
||||
uint32_t dhclient_get_netmask(const struct dhclient *);
|
||||
uint32_t dhclient_get_router(const struct dhclient *);
|
||||
const struct dhcp_msg *dhclient_get_config(const struct dhclient *);
|
||||
|
||||
int dhclient_configure_netdev(struct dhclient *);
|
||||
int dhclient_update_resolv_conf(struct dhclient *);
|
||||
|
||||
void dhclient_run(struct dhclient *);
|
||||
void dhclient_wait(struct dhclient *);
|
||||
|
||||
#endif /* dhcp-client.h */
|
||||
+279
@@ -0,0 +1,279 @@
|
||||
/* 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.
|
||||
*/
|
||||
|
||||
#ifndef DHCP_H
|
||||
#define DHCP_H 1
|
||||
|
||||
#include <stdint.h>
|
||||
#include "packets.h"
|
||||
#include "util.h"
|
||||
|
||||
struct ds;
|
||||
struct buffer;
|
||||
|
||||
/* Values for 'op' field. */
|
||||
#define DHCP_BOOTREQUEST 1 /* Message sent by DHCP client. */
|
||||
#define DHCP_BOOTREPLY 2 /* Message sent by DHCP server. */
|
||||
|
||||
/* Bits in 'flags' field. */
|
||||
#define DHCP_FLAGS_BROADCAST 0x8000 /* Server must broadcast all replies. */
|
||||
#define DHCP_FLAGS_MBZ 0x7fff /* Must be zero. */
|
||||
|
||||
/* First four bytes of 'options' field. */
|
||||
#define DHCP_OPTS_COOKIE 0x63825363
|
||||
|
||||
#define DHCP_HEADER_LEN 236
|
||||
struct dhcp_header {
|
||||
uint8_t op; /* DHCP_BOOTREQUEST or DHCP_BOOTREPLY. */
|
||||
uint8_t htype; /* ARP_HRD_ETHERNET (typically). */
|
||||
uint8_t hlen; /* ETH_ADDR_LEN (typically). */
|
||||
uint8_t hops; /* Hop count; set to 0 by client. */
|
||||
uint32_t xid; /* Transaction ID. */
|
||||
uint16_t secs; /* Since client started address acquisition. */
|
||||
uint16_t flags; /* DHCP_FLAGS_*. */
|
||||
uint32_t ciaddr; /* Client IP, if it has a lease for one. */
|
||||
uint32_t yiaddr; /* Client ("your") IP address. */
|
||||
uint32_t siaddr; /* Next server IP address. */
|
||||
uint32_t giaddr; /* Relay agent IP address. */
|
||||
uint8_t chaddr[16]; /* Client hardware address. */
|
||||
char sname[64]; /* Optional server host name. */
|
||||
char file[128]; /* Boot file name. */
|
||||
/* Followed by variable-length options field. */
|
||||
};
|
||||
BUILD_ASSERT_DECL(DHCP_HEADER_LEN == sizeof(struct dhcp_header));
|
||||
|
||||
#define DHCP_ARGS \
|
||||
DHCP_ARG(FIXED, 0) /* Fixed-length option (PAD and END only). */ \
|
||||
DHCP_ARG(IP, 4) /* IP addresses. */ \
|
||||
DHCP_ARG(SECS, 4) /* 32-bit duration in seconds. */ \
|
||||
DHCP_ARG(STRING, 1) /* NVT string, optionally null-terminated. */ \
|
||||
DHCP_ARG(UINT8, 1) /* 8-bit unsigned integer. */ \
|
||||
DHCP_ARG(UINT16, 2) /* 16-bit unsigned integer. */ \
|
||||
DHCP_ARG(UINT32, 4) /* 32-bit unsigned integer. */ \
|
||||
DHCP_ARG(BOOLEAN, 1) /* Boolean octet (0 or 1). */
|
||||
|
||||
/* DHCP option argument types. */
|
||||
enum dhcp_arg_type {
|
||||
#define DHCP_ARG(NAME, SIZE) DHCP_ARG_##NAME,
|
||||
DHCP_ARGS
|
||||
#undef DHCP_ARG
|
||||
};
|
||||
|
||||
#define DHCP_MSGS \
|
||||
DHCP_MSG(DHCPDISCOVER, 1) /* Client->server: What IPs are available? */ \
|
||||
DHCP_MSG(DHCPOFFER, 2) /* Server->client: This IP is available. */ \
|
||||
DHCP_MSG(DHCPREQUEST, 3) /* Client->server: I want that IP. */ \
|
||||
DHCP_MSG(DHCPDECLINE, 4) /* Client->server: That IP is in use!. */ \
|
||||
DHCP_MSG(DHCPACK, 5) /* Server->client: You can have that IP. */ \
|
||||
DHCP_MSG(DHCPNAK, 6) /* Server->client: You can't have that IP. */ \
|
||||
DHCP_MSG(DHCPRELEASE, 7) /* Client->server: I'm done with this IP. */ \
|
||||
DHCP_MSG(DCHPINFORM, 8) /* Client->server: I'm using this IP. */
|
||||
|
||||
/* DHCP message type (this is the argument for the DHCP_MSG_TYPE option). */
|
||||
enum dhcp_msg_type {
|
||||
#define DHCP_MSG(NAME, VALUE) NAME = VALUE,
|
||||
DHCP_MSGS
|
||||
#undef DHCP_MSG
|
||||
};
|
||||
const char *dhcp_type_name(enum dhcp_msg_type);
|
||||
|
||||
/* DHCP allows for 256 standardized options and 256 vendor-specific options.
|
||||
* We put them in a single array, with the standard options at the
|
||||
* beginning. */
|
||||
#define DHCP_N_OPTIONS 512
|
||||
#define DHCP_VENDOR_OFS 256
|
||||
|
||||
/* DHCP options. */
|
||||
#define DHCP_OPTS \
|
||||
/* arg min max */ \
|
||||
/* name code type args args */ \
|
||||
DHCP_OPT(PAD, 0, FIXED, 0, 0) \
|
||||
DHCP_OPT(END, 255, FIXED, 0, 0) \
|
||||
DHCP_OPT(SUBNET_MASK, 1, IP, 1, 1) \
|
||||
DHCP_OPT(TIME_OFFSET, 2, SECS, 1, 1) \
|
||||
DHCP_OPT(ROUTER, 3, IP, 1, SIZE_MAX) \
|
||||
/* Time Server Option is obsolete. */ \
|
||||
/* Name Server Option is obsolete. */ \
|
||||
DHCP_OPT(DNS_SERVER, 6, IP, 1, SIZE_MAX) \
|
||||
/* Log Server Option is obsolete. */ \
|
||||
/* Cookie Server Option is obsolete. */ \
|
||||
DHCP_OPT(LPR_SERVER, 9, IP, 1, SIZE_MAX) \
|
||||
/* Impress Server Option is obsolete. */ \
|
||||
/* Resource Location Server Option is obsolete. */ \
|
||||
DHCP_OPT(HOST_NAME, 12, STRING, 1, SIZE_MAX) \
|
||||
DHCP_OPT(BOOT_FILE_SIZE, 13, UINT16, 1, 1) \
|
||||
/* Merit Dump File option is obsolete. */ \
|
||||
DHCP_OPT(DOMAIN_NAME, 15, STRING, 1, SIZE_MAX) \
|
||||
/* Swap Server option is obsolete. */ \
|
||||
DHCP_OPT(ROOT_PATH, 17, STRING, 1, SIZE_MAX) \
|
||||
DHCP_OPT(EXTENSIONS_PATH, 18, STRING, 1, SIZE_MAX) \
|
||||
DHCP_OPT(IP_FORWARDING, 19, BOOLEAN, 1, 1) \
|
||||
DHCP_OPT(SOURCE_ROUTING, 20, BOOLEAN, 1, 1) \
|
||||
DHCP_OPT(POLICY_FILTER, 21, IP, 2, SIZE_MAX) \
|
||||
DHCP_OPT(MAX_DGRAM_REASSEMBLY, 22, UINT16, 1, 1) \
|
||||
DHCP_OPT(IP_TTL, 23, UINT8, 1, 1) \
|
||||
DHCP_OPT(PATH_MTU_TIMEOUT, 24, SECS, 1, 1) \
|
||||
DHCP_OPT(PATH_MTU_PLATEAU, 25, UINT16, 2, SIZE_MAX) \
|
||||
DHCP_OPT(MTU, 26, UINT16, 1, 1) \
|
||||
DHCP_OPT(ALL_SUBNETS_ARE_LOCAL, 27, BOOLEAN, 1, 1) \
|
||||
DHCP_OPT(BROADCAST_ADDRESS, 28, IP, 1, 1) \
|
||||
DHCP_OPT(PERFORM_MASK_DISCOVERY, 29, BOOLEAN, 1, 1) \
|
||||
DHCP_OPT(MASK_SUPPLIER, 30, BOOLEAN, 1, 1) \
|
||||
DHCP_OPT(PERFORM_ROUTER_DISCOVERY, 31, BOOLEAN, 1, 1) \
|
||||
DHCP_OPT(ROUTER_SOLICITATION, 32, IP, 1, 1) \
|
||||
DHCP_OPT(STATIC_ROUTE, 33, IP, 2, SIZE_MAX) \
|
||||
/* Trailer Encapsulation Option is obsolete. */ \
|
||||
DHCP_OPT(ARP_CACHE_TIMEOUT, 35, SECS, 1, 1) \
|
||||
DHCP_OPT(ETHERNET_ENCAPSULATION, 36, BOOLEAN, 1, 1) \
|
||||
DHCP_OPT(TCP_TTL, 37, UINT8, 1, 1) \
|
||||
DHCP_OPT(TCP_KEEPALIVE_INTERVAL, 38, SECS, 1, 1) \
|
||||
DHCP_OPT(TCP_KEEPALIVE_GARBAGE, 39, BOOLEAN, 1, 1) \
|
||||
DHCP_OPT(NIS_DOMAIN, 40, STRING, 1, SIZE_MAX) \
|
||||
DHCP_OPT(NIS_SERVERS, 41, IP, 1, SIZE_MAX) \
|
||||
DHCP_OPT(NTP_SERVERS, 42, IP, 1, SIZE_MAX) \
|
||||
DHCP_OPT(VENDOR_SPECIFIC, 43, UINT8, 1, SIZE_MAX) \
|
||||
DHCP_OPT(NETBIOS_NS, 44, IP, 1, SIZE_MAX) \
|
||||
DHCP_OPT(NETBIOS_DDS, 45, IP, 1, SIZE_MAX) \
|
||||
DHCP_OPT(NETBIOS_NODE_TYPE, 46, UINT8, 1, 1) \
|
||||
DHCP_OPT(NETBIOS_SCOPE, 47, STRING, 1, SIZE_MAX) \
|
||||
DHCP_OPT(X_FONT_SERVER, 48, IP, 1, SIZE_MAX) \
|
||||
DHCP_OPT(XDM, 49, IP, 1, SIZE_MAX) \
|
||||
DHCP_OPT(NISPLUS_DOMAIN, 64, STRING, 1, SIZE_MAX) \
|
||||
DHCP_OPT(NISPLUS_SERVERS, 65, IP, 1, SIZE_MAX) \
|
||||
DHCP_OPT(MOBILE_IP_HOME_AGENT, 68, IP, 0, SIZE_MAX) \
|
||||
DHCP_OPT(SMTP_SERVER, 69, IP, 1, SIZE_MAX) \
|
||||
DHCP_OPT(POP3_SERVER, 70, IP, 1, SIZE_MAX) \
|
||||
DHCP_OPT(NNTP_SERVER, 71, IP, 1, SIZE_MAX) \
|
||||
DHCP_OPT(WWW_SERVER, 72, IP, 1, SIZE_MAX) \
|
||||
DHCP_OPT(FINGER_SERVER, 73, IP, 1, SIZE_MAX) \
|
||||
DHCP_OPT(IRC_SERVER, 74, IP, 1, SIZE_MAX) \
|
||||
/* StreetTalk Server Option is obsolete. */ \
|
||||
/* StreetTalk Directory Assistance Server Option is obsolete. */ \
|
||||
DHCP_OPT(REQUESTED_IP, 50, IP, 1, 1) \
|
||||
DHCP_OPT(LEASE_TIME, 51, SECS, 1, 1) \
|
||||
DHCP_OPT(OPTION_OVERLOAD, 52, UINT8, 1, 1) \
|
||||
DHCP_OPT(TFTP_SERVER, 66, STRING, 1, SIZE_MAX) \
|
||||
DHCP_OPT(BOOTFILE_NAME, 67, STRING, 1, SIZE_MAX) \
|
||||
DHCP_OPT(DHCP_MSG_TYPE, 53, UINT8, 1, 1) \
|
||||
DHCP_OPT(SERVER_IDENTIFIER, 54, IP, 1, 1) \
|
||||
DHCP_OPT(PARAMETER_REQUEST_LIST, 55, UINT8, 1, SIZE_MAX) \
|
||||
DHCP_OPT(MESSAGE, 56, STRING, 1, SIZE_MAX) \
|
||||
DHCP_OPT(MAX_DHCP_MSG_SIZE, 57, UINT16, 1, 1) \
|
||||
DHCP_OPT(T1, 58, SECS, 1, 1) \
|
||||
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_PKI_URI, 2, STRING, 1, SIZE_MAX)
|
||||
|
||||
/* Shorthand for defining vendor options (used above). */
|
||||
#define DHCP_VNDOPT(NAME, CODE, ARG, MIN, MAX) \
|
||||
DHCP_OPT(NAME, (CODE) + DHCP_VENDOR_OFS, ARG, MIN, MAX)
|
||||
|
||||
/* DHCP option codes. */
|
||||
enum {
|
||||
#define DHCP_OPT(NAME, VALUE, ARGTYPE, MIN_ARGS, MAX_ARGS) \
|
||||
DHCP_CODE_##NAME = VALUE,
|
||||
DHCP_OPTS
|
||||
#undef DHCP_OPT
|
||||
};
|
||||
|
||||
/* The contents of a DHCP option.
|
||||
*
|
||||
* DHCP options can (rarely) be present but lack content. To represent such an
|
||||
* option, 'n' is 0 and 'data' is non-null (but does not point to anything
|
||||
* useful). */
|
||||
struct dhcp_option {
|
||||
size_t n; /* Number of bytes of data. */
|
||||
void *data; /* Data. */
|
||||
};
|
||||
|
||||
const char *dhcp_option_to_string(const struct dhcp_option *, int code,
|
||||
struct ds *);
|
||||
bool dhcp_option_equals(const struct dhcp_option *,
|
||||
const struct dhcp_option *);
|
||||
|
||||
/* Abstracted DHCP protocol message, to make them easier to manipulate than
|
||||
* through raw protocol buffers. */
|
||||
struct dhcp_msg {
|
||||
/* For use by calling code. */
|
||||
uint8_t op; /* DHCP_BOOTREQUEST or DHCP_BOOTREPLY. */
|
||||
uint32_t xid; /* Transaction ID. */
|
||||
uint16_t secs; /* Since client started address acquisition. */
|
||||
uint16_t flags; /* DHCP_FLAGS_*. */
|
||||
uint32_t ciaddr; /* Client IP, if it has a lease for one. */
|
||||
uint32_t yiaddr; /* Client ("your") IP address. */
|
||||
uint32_t siaddr; /* Next server IP address. */
|
||||
uint32_t giaddr; /* Relay agent IP address. */
|
||||
uint8_t chaddr[ETH_ADDR_LEN]; /* Client hardware address. */
|
||||
enum dhcp_msg_type type; /* DHCP_CODE_DHCP_MSG_TYPE option argument. */
|
||||
struct dhcp_option options[DHCP_N_OPTIONS]; /* Indexed by option code. */
|
||||
|
||||
/* For direct use only by dhcp_msg_*() functions. */
|
||||
uint8_t *data;
|
||||
size_t data_used, data_allocated;
|
||||
};
|
||||
|
||||
void dhcp_msg_init(struct dhcp_msg *);
|
||||
void dhcp_msg_uninit(struct dhcp_msg *);
|
||||
void dhcp_msg_copy(struct dhcp_msg *, const struct dhcp_msg *);
|
||||
void dhcp_msg_put(struct dhcp_msg *, int code, const void *, size_t);
|
||||
void dhcp_msg_put_bool(struct dhcp_msg *, int code, bool);
|
||||
void dhcp_msg_put_secs(struct dhcp_msg *, int code, uint32_t);
|
||||
void dhcp_msg_put_ip(struct dhcp_msg *, int code, uint32_t);
|
||||
void dhcp_msg_put_string(struct dhcp_msg *, int code, const char *);
|
||||
void dhcp_msg_put_uint8(struct dhcp_msg *, int code, uint8_t);
|
||||
void dhcp_msg_put_uint8_array(struct dhcp_msg *, int code,
|
||||
const uint8_t[], size_t n);
|
||||
void dhcp_msg_put_uint16(struct dhcp_msg *, int code, uint16_t);
|
||||
void dhcp_msg_put_uint16_array(struct dhcp_msg *, int code,
|
||||
const uint16_t[], size_t n);
|
||||
const void *dhcp_msg_get(const struct dhcp_msg *, int code, size_t offset,
|
||||
size_t size);
|
||||
bool dhcp_msg_get_bool(const struct dhcp_msg *, int code,
|
||||
size_t offset, bool *);
|
||||
bool dhcp_msg_get_secs(const struct dhcp_msg *, int code,
|
||||
size_t offset, uint32_t *);
|
||||
bool dhcp_msg_get_ip(const struct dhcp_msg *, int code,
|
||||
size_t offset, uint32_t *);
|
||||
char *dhcp_msg_get_string(const struct dhcp_msg *, int code);
|
||||
bool dhcp_msg_get_uint8(const struct dhcp_msg *, int code,
|
||||
size_t offset, uint8_t *);
|
||||
bool dhcp_msg_get_uint16(const struct dhcp_msg *, int code,
|
||||
size_t offset, uint16_t *);
|
||||
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 *);
|
||||
|
||||
#endif /* dhcp.h */
|
||||
@@ -60,6 +60,5 @@ int dpif_add_dp(struct dpif *);
|
||||
int dpif_del_dp(struct dpif *);
|
||||
int dpif_add_port(struct dpif *, const char *netdev);
|
||||
int dpif_del_port(struct dpif *, const char *netdev);
|
||||
int dpif_benchmark_nl(struct dpif *, uint32_t, uint32_t);
|
||||
|
||||
#endif /* dpif.h */
|
||||
@@ -49,6 +49,7 @@ struct ds {
|
||||
#define DS_EMPTY_INITIALIZER { NULL, 0, 0 }
|
||||
|
||||
void ds_init(struct ds *);
|
||||
void ds_clear(struct ds *);
|
||||
void ds_reserve(struct ds *, size_t min_length);
|
||||
void ds_put_char(struct ds *, char);
|
||||
void ds_put_cstr(struct ds *, const char *);
|
||||
|
||||
@@ -39,8 +39,7 @@
|
||||
struct buffer;
|
||||
struct rconn;
|
||||
|
||||
struct lswitch *lswitch_create(struct rconn *,
|
||||
bool learn_macs, bool setup_flows);
|
||||
struct lswitch *lswitch_create(struct rconn *, bool learn_macs, int max_idle);
|
||||
void lswitch_destroy(struct lswitch *);
|
||||
void lswitch_process_packet(struct lswitch *, struct rconn *,
|
||||
const struct buffer *);
|
||||
|
||||
+15
-3
@@ -52,20 +52,32 @@ enum netdev_flags {
|
||||
NETDEV_PROMISC = 0x0002 /* Promiscuous mode? */
|
||||
};
|
||||
|
||||
enum netdev_pseudo_ethertype {
|
||||
NETDEV_ETH_TYPE_NONE = -128, /* Receive no frames. */
|
||||
NETDEV_ETH_TYPE_ANY, /* Receive all frames. */
|
||||
NETDEV_ETH_TYPE_802_2 /* Receive all IEEE 802.2 frames. */
|
||||
};
|
||||
|
||||
struct netdev;
|
||||
int netdev_open(const char *name, struct netdev **);
|
||||
int netdev_open(const char *name, int ethertype, struct netdev **);
|
||||
void netdev_close(struct netdev *);
|
||||
int netdev_recv(struct netdev *, struct buffer *);
|
||||
void netdev_recv_wait(struct netdev *);
|
||||
int netdev_send(struct netdev *, struct buffer *);
|
||||
void netdev_drain(struct netdev *);
|
||||
int netdev_send(struct netdev *, const struct buffer *);
|
||||
const uint8_t *netdev_get_etheraddr(const struct netdev *);
|
||||
const char *netdev_get_name(const struct netdev *);
|
||||
int netdev_get_mtu(const struct netdev *);
|
||||
int netdev_get_speed(const struct netdev *);
|
||||
uint32_t netdev_get_features(const struct netdev *);
|
||||
bool netdev_get_in4(const struct netdev *, struct in_addr *);
|
||||
int netdev_set_in4(struct netdev *, struct in_addr addr, struct in_addr mask);
|
||||
int netdev_add_router(struct netdev *, struct in_addr router);
|
||||
bool netdev_get_in6(const struct netdev *, struct in6_addr *);
|
||||
int netdev_get_flags(const struct netdev *, enum netdev_flags *);
|
||||
int netdev_set_flags(struct netdev *, enum netdev_flags);
|
||||
int netdev_set_flags(struct netdev *, enum netdev_flags, bool permanent);
|
||||
int netdev_turn_flags_on(struct netdev *, enum netdev_flags, bool permanent);
|
||||
int netdev_turn_flags_off(struct netdev *, enum netdev_flags, bool permanent);
|
||||
int netdev_arp_lookup(const struct netdev *, uint32_t ip, uint8_t mac[6]);
|
||||
|
||||
#endif /* netdev.h */
|
||||
@@ -61,8 +61,6 @@ enum dp_genl_command {
|
||||
DP_GENL_C_DEL_PORT, /* Remove port from datapath. */
|
||||
DP_GENL_C_OPENFLOW, /* Encapsulated OpenFlow protocol. */
|
||||
|
||||
DP_GENL_C_BENCHMARK_NL, /* Benchmark netlink connection */
|
||||
|
||||
__DP_GENL_C_MAX,
|
||||
DP_GENL_C_MAX = __DP_GENL_C_MAX - 1
|
||||
};
|
||||
|
||||
+52
-6
@@ -42,6 +42,24 @@
|
||||
#include <stdint.h>
|
||||
#endif
|
||||
|
||||
#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 { \
|
||||
unsigned int build_assert_failed : (EXPR) ? 1 : -1; })]
|
||||
#else /* __cplusplus */
|
||||
#include <boost/static_assert.hpp>
|
||||
#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)
|
||||
@@ -50,7 +68,7 @@
|
||||
/* The most significant bit being set in the version field indicates an
|
||||
* experimental OpenFlow version.
|
||||
*/
|
||||
#define OFP_VERSION 0x83
|
||||
#define OFP_VERSION 0x85
|
||||
|
||||
#define OFP_MAX_TABLE_NAME_LEN 32
|
||||
#define OFP_MAX_PORT_NAME_LEN 16
|
||||
@@ -94,7 +112,9 @@ enum ofp_type {
|
||||
OFPT_PORT_STATUS, /* 11 Async message */
|
||||
OFPT_ERROR_MSG, /* 12 Async message */
|
||||
OFPT_STATS_REQUEST, /* 13 Controller/switch message */
|
||||
OFPT_STATS_REPLY /* 14 Controller/switch message */
|
||||
OFPT_STATS_REPLY, /* 14 Controller/switch message */
|
||||
OFPT_ECHO_REQUEST, /* 15 Symmetric message */
|
||||
OFPT_ECHO_REPLY /* 16 Symmetric message */
|
||||
};
|
||||
|
||||
/* Header on all OpenFlow packets. */
|
||||
@@ -106,6 +126,7 @@ struct ofp_header {
|
||||
Replies use the same id as was in the request
|
||||
to facilitate pairing. */
|
||||
};
|
||||
OFP_ASSERT(sizeof(struct ofp_header) == 8);
|
||||
|
||||
#define OFP_DEFAULT_MISS_SEND_LEN 128
|
||||
|
||||
@@ -121,6 +142,7 @@ struct ofp_switch_config {
|
||||
uint16_t miss_send_len; /* Max bytes of new flow that datapath should
|
||||
send to the controller. */
|
||||
};
|
||||
OFP_ASSERT(sizeof(struct ofp_switch_config) == 12);
|
||||
|
||||
/* Capabilities supported by the datapath. */
|
||||
enum ofp_capabilities {
|
||||
@@ -158,6 +180,7 @@ struct ofp_phy_port {
|
||||
uint32_t speed; /* Current speed in Mbps */
|
||||
uint32_t features; /* Bitmap of supported "ofp_port_features"s. */
|
||||
};
|
||||
OFP_ASSERT(sizeof(struct ofp_phy_port) == 36);
|
||||
|
||||
/* Switch features. */
|
||||
struct ofp_switch_features {
|
||||
@@ -177,12 +200,14 @@ struct ofp_switch_features {
|
||||
/* Features. */
|
||||
uint32_t capabilities; /* Bitmap of support "ofp_capabilities". */
|
||||
uint32_t actions; /* Bitmap of supported "ofp_action_type"s. */
|
||||
uint8_t pad[4]; /* Align to 64-bits. */
|
||||
|
||||
/* Port info.*/
|
||||
struct ofp_phy_port ports[0]; /* Port definitions. The number of ports
|
||||
is inferred from the length field in
|
||||
the header. */
|
||||
};
|
||||
OFP_ASSERT(sizeof(struct ofp_switch_features) == 48);
|
||||
|
||||
/* What changed about the phsyical port */
|
||||
enum ofp_port_reason {
|
||||
@@ -198,12 +223,14 @@ struct ofp_port_status {
|
||||
uint8_t pad[3]; /* Align to 32-bits */
|
||||
struct ofp_phy_port desc;
|
||||
};
|
||||
OFP_ASSERT(sizeof(struct ofp_port_status) == 48);
|
||||
|
||||
/* Modify behavior of the physical port */
|
||||
struct ofp_port_mod {
|
||||
struct ofp_header header;
|
||||
struct ofp_phy_port desc;
|
||||
};
|
||||
OFP_ASSERT(sizeof(struct ofp_port_mod) == 44);
|
||||
|
||||
/* Why is this packet being sent to the controller? */
|
||||
enum ofp_reason {
|
||||
@@ -226,6 +253,7 @@ struct ofp_packet_in {
|
||||
offsetof(struct ofp_packet_in, data) ==
|
||||
sizeof(struct ofp_packet_in) - 2. */
|
||||
};
|
||||
OFP_ASSERT(sizeof(struct ofp_packet_in) == 20);
|
||||
|
||||
enum ofp_action_type {
|
||||
OFPAT_OUTPUT, /* Output to switch port. */
|
||||
@@ -245,6 +273,7 @@ struct ofp_action_output {
|
||||
uint16_t max_len;
|
||||
uint16_t port;
|
||||
};
|
||||
OFP_ASSERT(sizeof(struct ofp_action_output) == 4);
|
||||
|
||||
/* The VLAN id is 12-bits, so we'll use the entire 16 bits to indicate
|
||||
* special conditions. All ones is used to indicate that no VLAN id was
|
||||
@@ -259,10 +288,11 @@ struct ofp_action {
|
||||
struct ofp_action_output output; /* OFPAT_OUTPUT: output struct. */
|
||||
uint16_t vlan_id; /* OFPAT_SET_DL_VLAN: VLAN id. */
|
||||
uint8_t dl_addr[OFP_ETH_ALEN]; /* OFPAT_SET_DL_SRC/DST */
|
||||
uint32_t nw_addr; /* 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;
|
||||
};
|
||||
OFP_ASSERT(sizeof(struct ofp_action) == 8);
|
||||
|
||||
/* Send packet (controller -> datapath). */
|
||||
struct ofp_packet_out {
|
||||
@@ -275,6 +305,7 @@ struct ofp_packet_out {
|
||||
uint8_t data[0]; /* buffer_id == -1 */
|
||||
} u;
|
||||
};
|
||||
OFP_ASSERT(sizeof(struct ofp_packet_out) == 16);
|
||||
|
||||
enum ofp_flow_mod_command {
|
||||
OFPFC_ADD, /* New flow. */
|
||||
@@ -323,6 +354,7 @@ struct ofp_match {
|
||||
uint16_t tp_src; /* TCP/UDP source port. */
|
||||
uint16_t tp_dst; /* TCP/UDP destination port. */
|
||||
};
|
||||
OFP_ASSERT(sizeof(struct ofp_match) == 36);
|
||||
|
||||
/* Value used in "max_idle" to indicate that the entry is permanent */
|
||||
#define OFP_FLOW_PERMANENT 0
|
||||
@@ -345,6 +377,7 @@ struct ofp_flow_mod {
|
||||
struct ofp_action actions[0]; /* The number of actions is inferred from
|
||||
the length field in the header. */
|
||||
};
|
||||
OFP_ASSERT(sizeof(struct ofp_flow_mod) == 60);
|
||||
|
||||
/* Flow expiration (datapath -> controller). */
|
||||
struct ofp_flow_expired {
|
||||
@@ -355,9 +388,11 @@ struct ofp_flow_expired {
|
||||
uint8_t pad[2]; /* Align to 32-bits. */
|
||||
|
||||
uint32_t duration; /* Time flow was alive in seconds. */
|
||||
uint8_t pad2[4]; /* Align to 64-bits. */
|
||||
uint64_t packet_count;
|
||||
uint64_t byte_count;
|
||||
};
|
||||
OFP_ASSERT(sizeof(struct ofp_flow_expired) == 72);
|
||||
|
||||
/* Error message (datapath -> controller). */
|
||||
struct ofp_error_msg {
|
||||
@@ -368,6 +403,7 @@ struct ofp_error_msg {
|
||||
uint8_t data[0]; /* Variable-length data. Interpreted based
|
||||
on the type and code. */
|
||||
};
|
||||
OFP_ASSERT(sizeof(struct ofp_error_msg) == 12);
|
||||
|
||||
enum ofp_stats_types {
|
||||
/* Individual flow statistics.
|
||||
@@ -397,6 +433,7 @@ struct ofp_stats_request {
|
||||
uint16_t flags; /* OFPSF_REQ_* flags (none yet defined). */
|
||||
uint8_t body[0]; /* Body of the request. */
|
||||
};
|
||||
OFP_ASSERT(sizeof(struct ofp_stats_request) == 12);
|
||||
|
||||
enum ofp_stats_reply_flags {
|
||||
OFPSF_REPLY_MORE = 1 << 0, /* More replies to follow */
|
||||
@@ -408,6 +445,7 @@ struct ofp_stats_reply {
|
||||
uint16_t flags; /* OFPSF_REPLY_* flags. */
|
||||
uint8_t body[0]; /* Body of the reply. */
|
||||
};
|
||||
OFP_ASSERT(sizeof(struct ofp_stats_reply) == 12);
|
||||
|
||||
/* Body for ofp_stats_request of type OFPST_FLOW. */
|
||||
struct ofp_flow_stats_request {
|
||||
@@ -416,6 +454,7 @@ struct ofp_flow_stats_request {
|
||||
or 0xff for all tables. */
|
||||
uint8_t pad[3]; /* Align to 32 bits. */
|
||||
};
|
||||
OFP_ASSERT(sizeof(struct ofp_flow_stats_request) == 40);
|
||||
|
||||
/* Body of reply to OFPST_FLOW request. */
|
||||
struct ofp_flow_stats {
|
||||
@@ -424,13 +463,14 @@ struct ofp_flow_stats {
|
||||
uint8_t pad;
|
||||
struct ofp_match match; /* Description of fields. */
|
||||
uint32_t duration; /* Time flow has been alive in seconds. */
|
||||
uint64_t packet_count; /* Number of packets in flow. */
|
||||
uint64_t byte_count; /* Number of bytes in flow. */
|
||||
uint16_t priority; /* Priority of the entry. Only meaningful
|
||||
when this is not an exact-match entry. */
|
||||
uint16_t max_idle; /* Number of seconds idle before expiration. */
|
||||
uint64_t packet_count; /* Number of packets in flow. */
|
||||
uint64_t byte_count; /* Number of bytes in flow. */
|
||||
struct ofp_action actions[0]; /* Actions. */
|
||||
};
|
||||
OFP_ASSERT(sizeof(struct ofp_flow_stats) == 64);
|
||||
|
||||
/* Body for ofp_stats_request of type OFPST_AGGREGATE. */
|
||||
struct ofp_aggregate_stats_request {
|
||||
@@ -439,13 +479,16 @@ struct ofp_aggregate_stats_request {
|
||||
or 0xff for all tables. */
|
||||
uint8_t pad[3]; /* Align to 32 bits. */
|
||||
};
|
||||
OFP_ASSERT(sizeof(struct ofp_aggregate_stats_request) == 40);
|
||||
|
||||
/* Body of reply to OFPST_AGGREGATE request. */
|
||||
struct ofp_aggregate_stats_reply {
|
||||
uint64_t packet_count; /* Number of packets in flows. */
|
||||
uint64_t byte_count; /* Number of bytes in flows. */
|
||||
uint32_t flow_count; /* Number of flows. */
|
||||
uint8_t pad[4]; /* Align to 64 bits. */
|
||||
};
|
||||
OFP_ASSERT(sizeof(struct ofp_aggregate_stats_reply) == 24);
|
||||
|
||||
/* Body of reply to OFPST_TABLE request. */
|
||||
struct ofp_table_stats {
|
||||
@@ -454,16 +497,19 @@ struct ofp_table_stats {
|
||||
char name[OFP_MAX_TABLE_NAME_LEN];
|
||||
uint32_t max_entries; /* Max number of entries supported */
|
||||
uint32_t active_count; /* Number of active entries */
|
||||
uint8_t pad2[4]; /* Align to 64 bits. */
|
||||
uint64_t matched_count; /* Number of packets that hit table */
|
||||
};
|
||||
OFP_ASSERT(sizeof(struct ofp_table_stats) == 56);
|
||||
|
||||
/* Statistics about a particular port */
|
||||
struct ofp_port_stats {
|
||||
uint16_t port_no;
|
||||
uint8_t pad[2]; /* Align to 32-bits */
|
||||
uint8_t pad[6]; /* Align to 64-bits */
|
||||
uint64_t rx_count; /* Number of received packets */
|
||||
uint64_t tx_count; /* Number of transmitted packets */
|
||||
uint64_t drop_count; /* Number of packets dropped by interface */
|
||||
};
|
||||
OFP_ASSERT(sizeof(struct ofp_port_stats) == 32);
|
||||
|
||||
#endif /* openflow.h */
|
||||
+44
-1
@@ -35,10 +35,15 @@
|
||||
|
||||
#include <stdint.h>
|
||||
#include <string.h>
|
||||
#include "compiler.h"
|
||||
#include "random.h"
|
||||
#include "util.h"
|
||||
|
||||
#define ETH_ADDR_LEN 6
|
||||
|
||||
static const uint8_t eth_addr_broadcast[ETH_ADDR_LEN] UNUSED
|
||||
= { 0xff, 0xff, 0xff, 0xff, 0xff, 0xff };
|
||||
|
||||
static inline bool eth_addr_is_broadcast(const uint8_t ea[6])
|
||||
{
|
||||
return (ea[0] & ea[1] & ea[2] & ea[3] & ea[4] & ea[5]) == 0xff;
|
||||
@@ -51,11 +56,38 @@ static inline bool eth_addr_is_local(const uint8_t ea[6])
|
||||
{
|
||||
return ea[0] & 2;
|
||||
}
|
||||
static inline bool eth_addr_is_zero(const uint8_t ea[6])
|
||||
{
|
||||
return !(ea[0] | ea[1] | ea[2] | ea[3] | ea[4] | ea[5]);
|
||||
}
|
||||
static inline bool eth_addr_equals(const uint8_t a[ETH_ADDR_LEN],
|
||||
const uint8_t b[ETH_ADDR_LEN])
|
||||
{
|
||||
return !memcmp(a, b, ETH_ADDR_LEN);
|
||||
}
|
||||
static inline uint64_t eth_addr_to_uint64(const uint8_t ea[ETH_ADDR_LEN])
|
||||
{
|
||||
return (((uint64_t) ea[0] << 40)
|
||||
| ((uint64_t) ea[1] << 32)
|
||||
| ((uint64_t) ea[2] << 24)
|
||||
| ((uint64_t) ea[3] << 16)
|
||||
| ((uint64_t) ea[4] << 8)
|
||||
| ea[5]);
|
||||
}
|
||||
static inline void eth_addr_from_uint64(uint64_t x, uint8_t ea[ETH_ADDR_LEN])
|
||||
{
|
||||
ea[0] = x >> 40;
|
||||
ea[1] = x >> 32;
|
||||
ea[2] = x >> 24;
|
||||
ea[3] = x >> 16;
|
||||
ea[4] = x >> 8;
|
||||
ea[5] = x;
|
||||
}
|
||||
static inline void eth_addr_random(uint8_t ea[ETH_ADDR_LEN])
|
||||
{
|
||||
random_bytes(ea, ETH_ADDR_LEN);
|
||||
}
|
||||
|
||||
#define ETH_ADDR_FMT \
|
||||
"%02"PRIx8":%02"PRIx8":%02"PRIx8":%02"PRIx8":%02"PRIx8":%02"PRIx8
|
||||
#define ETH_ADDR_ARGS(ea) \
|
||||
@@ -67,8 +99,10 @@ static inline bool eth_addr_equals(const uint8_t a[ETH_ADDR_LEN],
|
||||
|
||||
#define ETH_HEADER_LEN 14
|
||||
#define ETH_PAYLOAD_MIN 46
|
||||
#define ETH_PAYLOAD_MAX 1500
|
||||
#define ETH_TOTAL_MIN (ETH_HEADER_LEN + ETH_PAYLOAD_MIN)
|
||||
#define ETH_TOTAL_MAX (ETH_HEADER_LEN + VLAN_HEADER_LEN + 1500)
|
||||
#define ETH_TOTAL_MAX (ETH_HEADER_LEN + ETH_PAYLOAD_MAX)
|
||||
#define ETH_VLAN_TOTAL_MAX (ETH_HEADER_LEN + VLAN_HEADER_LEN + ETH_PAYLOAD_MAX)
|
||||
struct eth_header {
|
||||
uint8_t eth_dst[ETH_ADDR_LEN];
|
||||
uint8_t eth_src[ETH_ADDR_LEN];
|
||||
@@ -132,10 +166,19 @@ BUILD_ASSERT_DECL(VLAN_ETH_HEADER_LEN == sizeof(struct vlan_eth_header));
|
||||
|
||||
#define IP_VER(ip_ihl_ver) ((ip_ihl_ver) >> 4)
|
||||
#define IP_IHL(ip_ihl_ver) ((ip_ihl_ver) & 15)
|
||||
#define IP_IHL_VER(ihl, ver) (((ver) << 4) | (ihl))
|
||||
|
||||
#define IP_TYPE_TCP 6
|
||||
#define IP_TYPE_UDP 17
|
||||
|
||||
#define IP_VERSION 4
|
||||
|
||||
#define IP_DONT_FRAGMENT 0x4000 /* Don't fragment. */
|
||||
#define IP_MORE_FRAGMENTS 0x2000 /* More fragments. */
|
||||
#define IP_FRAG_OFF_MASK 0x1fff /* Fragment offset. */
|
||||
#define IP_IS_FRAGMENT(ip_frag_off) \
|
||||
(ntohs(ip_frag_off) & (IP_MORE_FRAGMENTS | IP_FRAG_OFF_MASK))
|
||||
|
||||
#define IP_HEADER_LEN 20
|
||||
struct ip_header {
|
||||
uint8_t ip_ihl_ver;
|
||||
|
||||
@@ -0,0 +1,44 @@
|
||||
/* 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.
|
||||
*/
|
||||
|
||||
#ifndef RANDOM_H
|
||||
#define RANDOM_H 1
|
||||
|
||||
#include <stddef.h>
|
||||
#include <stdint.h>
|
||||
|
||||
void random_bytes(void *, size_t);
|
||||
uint32_t random_uint32(void);
|
||||
int random_range(int max);
|
||||
|
||||
#endif /* random.h */
|
||||
+12
-1
@@ -36,6 +36,7 @@
|
||||
|
||||
#include "queue.h"
|
||||
#include <stdbool.h>
|
||||
#include <stdint.h>
|
||||
#include <time.h>
|
||||
|
||||
/* A wrapper around vconn that provides queuing and optionally reliability.
|
||||
@@ -52,9 +53,16 @@
|
||||
|
||||
struct vconn;
|
||||
|
||||
struct rconn *rconn_new(const char *name, int txq_limit);
|
||||
struct rconn *rconn_new(const char *name, int txq_limit,
|
||||
int inactivity_probe_interval, int max_backoff);
|
||||
struct rconn *rconn_new_from_vconn(const char *name, int txq_limit,
|
||||
struct vconn *);
|
||||
struct rconn *rconn_create(int txq_limit, int inactivity_probe_interval,
|
||||
int max_backoff);
|
||||
int rconn_connect(struct rconn *, const char *name);
|
||||
void rconn_connect_unreliably(struct rconn *,
|
||||
const char *name, struct vconn *vconn);
|
||||
void rconn_disconnect(struct rconn *);
|
||||
void rconn_destroy(struct rconn *);
|
||||
|
||||
void rconn_run(struct rconn *);
|
||||
@@ -70,5 +78,8 @@ const char *rconn_get_name(const struct rconn *);
|
||||
bool rconn_is_alive(const struct rconn *);
|
||||
bool rconn_is_connected(const struct rconn *);
|
||||
int rconn_disconnected_duration(const struct rconn *);
|
||||
bool rconn_is_connectivity_questionable(struct rconn *);
|
||||
|
||||
uint32_t rconn_get_ip(const struct rconn *);
|
||||
|
||||
#endif /* rconn.h */
|
||||
@@ -35,11 +35,15 @@
|
||||
#define SOCKET_UTIL_H 1
|
||||
|
||||
#include <netinet/in.h>
|
||||
#include <stdbool.h>
|
||||
#include <sys/socket.h>
|
||||
|
||||
int set_nonblocking(int fd);
|
||||
int lookup_ip(const char *host_name, struct in_addr *address);
|
||||
int get_socket_error(int sock);
|
||||
int check_connection_completion(int fd);
|
||||
int drain_rcvbuf(int fd);
|
||||
int make_unix_socket(int style, bool nonblock, bool passcred,
|
||||
const char *bind_path, const char *connect_path);
|
||||
|
||||
#endif /* socket-util.h */
|
||||
@@ -0,0 +1,50 @@
|
||||
/* 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.
|
||||
*/
|
||||
|
||||
#ifndef TIMEVAL_H
|
||||
#define TIMEVAL_H 1
|
||||
|
||||
#include "type-props.h"
|
||||
#include "util.h"
|
||||
|
||||
/* POSIX allows floating-point time_t, but we don't support it. */
|
||||
BUILD_ASSERT_DECL(TYPE_IS_INTEGER(time_t));
|
||||
|
||||
/* We do try to cater to unsigned time_t, but I want to know about it if we
|
||||
* ever encounter such a platform. */
|
||||
BUILD_ASSERT_DECL(TYPE_IS_SIGNED(time_t));
|
||||
|
||||
#define TIME_MAX TYPE_MAXIMUM(time_t)
|
||||
#define TIME_MIN TYPE_MINIMUM(time_t)
|
||||
|
||||
#endif /* timeval.h */
|
||||
@@ -0,0 +1,49 @@
|
||||
/* 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.
|
||||
*/
|
||||
|
||||
#ifndef TYPE_PROPS_H
|
||||
#define TYPE_PROPS_H 1
|
||||
|
||||
#include <limits.h>
|
||||
|
||||
#define TYPE_IS_INTEGER(TYPE) ((TYPE) 1.5 == (TYPE) 1)
|
||||
#define TYPE_IS_SIGNED(TYPE) ((TYPE) 0 > (TYPE) -1)
|
||||
#define TYPE_VALUE_BITS(TYPE) (sizeof(TYPE) * CHAR_BIT - TYPE_IS_SIGNED(TYPE))
|
||||
#define TYPE_MINIMUM(TYPE) (TYPE_IS_SIGNED(TYPE) \
|
||||
? ~(TYPE)0 << TYPE_VALUE_BITS(TYPE) \
|
||||
: 0)
|
||||
#define TYPE_MAXIMUM(TYPE) (TYPE_IS_SIGNED(TYPE) \
|
||||
? ~(~(TYPE)0 << TYPE_VALUE_BITS(TYPE)) \
|
||||
: (TYPE)-1)
|
||||
|
||||
#endif /* type-props.h */
|
||||
@@ -81,6 +81,7 @@ void *xmalloc(size_t);
|
||||
void *xcalloc(size_t, size_t);
|
||||
void *xrealloc(void *, size_t);
|
||||
void *xmemdup(const void *, size_t);
|
||||
char *xmemdup0(const char *, size_t);
|
||||
char *xstrdup(const char *);
|
||||
char *xasprintf(const char *format, ...) PRINTF_FORMAT(1, 2);
|
||||
|
||||
|
||||
@@ -33,7 +33,10 @@
|
||||
#ifndef VCONN_SSL_H
|
||||
#define VCONN_SSL_H 1
|
||||
|
||||
#include <stdbool.h>
|
||||
|
||||
#ifdef HAVE_OPENSSL
|
||||
bool vconn_ssl_is_configured(void);
|
||||
void vconn_ssl_set_private_key_file(const char *file_name);
|
||||
void vconn_ssl_set_certificate_file(const char *file_name);
|
||||
void vconn_ssl_set_ca_cert_file(const char *file_name);
|
||||
@@ -56,6 +59,10 @@ void vconn_ssl_set_ca_cert_file(const char *file_name);
|
||||
vconn_ssl_set_ca_cert_file(optarg); \
|
||||
break;
|
||||
#else /* !HAVE_OPENSSL */
|
||||
static inline bool vconn_ssl_is_configured(void)
|
||||
{
|
||||
return false;
|
||||
}
|
||||
#define VCONN_SSL_LONG_OPTIONS
|
||||
#define VCONN_SSL_OPTION_HANDLERS
|
||||
#endif /* !HAVE_OPENSSL */
|
||||
|
||||
@@ -0,0 +1,50 @@
|
||||
/* 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.
|
||||
*/
|
||||
|
||||
#ifndef VCONN_STREAM_H
|
||||
#define VCONN_STREAM_H 1
|
||||
|
||||
#include <stddef.h>
|
||||
#include <stdint.h>
|
||||
|
||||
struct vconn;
|
||||
struct sockaddr;
|
||||
|
||||
int new_stream_vconn(const char *name, int fd, int connect_status,
|
||||
uint32_t ip, struct vconn **vconnp);
|
||||
int new_pstream_vconn(const char *name, int fd,
|
||||
int (*accept_cb)(int fd, const struct sockaddr *,
|
||||
size_t sa_len, struct vconn **),
|
||||
struct vconn **vconnp);
|
||||
|
||||
#endif /* vconn-stream.h */
|
||||
+15
-1
@@ -35,11 +35,13 @@
|
||||
#define VCONN_H 1
|
||||
|
||||
#include <stdbool.h>
|
||||
#include <stddef.h>
|
||||
#include <stdint.h>
|
||||
|
||||
struct buffer;
|
||||
struct flow;
|
||||
struct pollfd;
|
||||
struct ofp_header;
|
||||
|
||||
/* Client interface. */
|
||||
|
||||
@@ -47,16 +49,19 @@ struct pollfd;
|
||||
struct vconn {
|
||||
struct vconn_class *class;
|
||||
int connect_status;
|
||||
uint32_t ip;
|
||||
};
|
||||
|
||||
void vconn_usage(bool active, bool passive);
|
||||
int vconn_open(const char *name, struct vconn **);
|
||||
void vconn_close(struct vconn *);
|
||||
bool vconn_is_passive(const struct vconn *);
|
||||
uint32_t vconn_get_ip(const struct vconn *);
|
||||
int vconn_connect(struct vconn *);
|
||||
int vconn_accept(struct vconn *, struct vconn **);
|
||||
int vconn_recv(struct vconn *, struct buffer **);
|
||||
int vconn_send(struct vconn *, struct buffer *);
|
||||
int vconn_transact(struct vconn *, struct buffer *, struct buffer **);
|
||||
|
||||
int vconn_open_block(const char *name, struct vconn **);
|
||||
int vconn_send_block(struct vconn *, struct buffer *);
|
||||
@@ -74,12 +79,19 @@ void vconn_accept_wait(struct vconn *);
|
||||
void vconn_recv_wait(struct vconn *);
|
||||
void vconn_send_wait(struct vconn *);
|
||||
|
||||
void *make_openflow(size_t openflow_len, uint8_t type, struct buffer **);
|
||||
void *make_openflow_xid(size_t openflow_len, uint8_t type,
|
||||
uint32_t xid, struct buffer **);
|
||||
void update_openflow_length(struct buffer *);
|
||||
struct buffer *make_add_simple_flow(const struct flow *,
|
||||
uint32_t buffer_id, uint16_t out_port);
|
||||
uint32_t buffer_id, uint16_t out_port,
|
||||
uint16_t max_idle);
|
||||
struct buffer *make_buffered_packet_out(uint32_t buffer_id,
|
||||
uint16_t in_port, uint16_t out_port);
|
||||
struct buffer *make_unbuffered_packet_out(const struct buffer *packet,
|
||||
uint16_t in_port, uint16_t out_port);
|
||||
struct buffer *make_echo_request(void);
|
||||
struct buffer *make_echo_reply(const struct ofp_header *rq);
|
||||
|
||||
/* Provider interface. */
|
||||
|
||||
@@ -161,6 +173,8 @@ struct vconn_class {
|
||||
|
||||
extern struct vconn_class tcp_vconn_class;
|
||||
extern struct vconn_class ptcp_vconn_class;
|
||||
extern struct vconn_class unix_vconn_class;
|
||||
extern struct vconn_class punix_vconn_class;
|
||||
#ifdef HAVE_OPENSSL
|
||||
extern struct vconn_class ssl_vconn_class;
|
||||
extern struct vconn_class pssl_vconn_class;
|
||||
|
||||
+7
-1
@@ -66,6 +66,8 @@ enum vlog_facility vlog_get_facility_val(const char *name);
|
||||
VLOG_MODULE(ctlpath) \
|
||||
VLOG_MODULE(daemon) \
|
||||
VLOG_MODULE(datapath) \
|
||||
VLOG_MODULE(dhcp) \
|
||||
VLOG_MODULE(dhcp_client) \
|
||||
VLOG_MODULE(dpif) \
|
||||
VLOG_MODULE(dpctl) \
|
||||
VLOG_MODULE(fault) \
|
||||
@@ -74,6 +76,7 @@ enum vlog_facility vlog_get_facility_val(const char *name);
|
||||
VLOG_MODULE(mac_learning) \
|
||||
VLOG_MODULE(netdev) \
|
||||
VLOG_MODULE(netlink) \
|
||||
VLOG_MODULE(ofp_discover) \
|
||||
VLOG_MODULE(poll_loop) \
|
||||
VLOG_MODULE(secchan) \
|
||||
VLOG_MODULE(rconn) \
|
||||
@@ -82,7 +85,10 @@ enum vlog_facility vlog_get_facility_val(const char *name);
|
||||
VLOG_MODULE(vconn_netlink) \
|
||||
VLOG_MODULE(vconn_tcp) \
|
||||
VLOG_MODULE(vconn_ssl) \
|
||||
VLOG_MODULE(vconn_stream) \
|
||||
VLOG_MODULE(vconn_unix) \
|
||||
VLOG_MODULE(vconn) \
|
||||
VLOG_MODULE(vlog) \
|
||||
|
||||
/* VLM_ constant for each vlog module. */
|
||||
enum vlog_module {
|
||||
@@ -113,7 +119,7 @@ void vlog(enum vlog_module, enum vlog_level, const char *format, ...)
|
||||
/* Convenience macros. To use these, define THIS_MODULE as a macro that
|
||||
* expands to the module used by the current source file, e.g.
|
||||
* #include "vlog.h"
|
||||
* #define THIS_MODULE VLM_NETLINK
|
||||
* #define THIS_MODULE VLM_netlink
|
||||
* Guaranteed to preserve errno.
|
||||
*/
|
||||
#define VLOG_EMER(...) vlog(THIS_MODULE, VLL_EMER, __VA_ARGS__)
|
||||
|
||||
+11
-5
@@ -1,11 +1,14 @@
|
||||
include ../Make.vars
|
||||
|
||||
noinst_LTLIBRARIES = libopenflow.la
|
||||
noinst_LIBRARIES = libopenflow.a
|
||||
|
||||
libopenflow_la_SOURCES = \
|
||||
libopenflow_a_SOURCES = \
|
||||
buffer.c \
|
||||
command-line.c \
|
||||
csum.c \
|
||||
daemon.c \
|
||||
dhcp-client.c \
|
||||
dhcp.c \
|
||||
dynamic-string.c \
|
||||
fatal-signal.c \
|
||||
fault.c \
|
||||
@@ -18,16 +21,19 @@ libopenflow_la_SOURCES = \
|
||||
ofp-print.c \
|
||||
poll-loop.c \
|
||||
queue.c \
|
||||
random.c \
|
||||
rconn.c \
|
||||
socket-util.c \
|
||||
util.c \
|
||||
vconn-tcp.c \
|
||||
vconn-unix.c \
|
||||
vconn-stream.c \
|
||||
vconn.c \
|
||||
vlog-socket.c \
|
||||
vlog.c
|
||||
|
||||
if HAVE_NETLINK
|
||||
libopenflow_la_SOURCES += \
|
||||
libopenflow_a_SOURCES += \
|
||||
dpif.c \
|
||||
netlink.c \
|
||||
vconn-netlink.c
|
||||
@@ -39,9 +45,9 @@ libopenflow_la_SOURCES += \
|
||||
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 && \
|
||||
|
||||
+39
-7
@@ -31,6 +31,7 @@
|
||||
* derivatives without specific, written prior permission.
|
||||
*/
|
||||
|
||||
#include <config.h>
|
||||
#include "buffer.h"
|
||||
#include <assert.h>
|
||||
#include <stdlib.h>
|
||||
@@ -50,7 +51,7 @@ buffer_use(struct buffer *b, void *base, size_t allocated)
|
||||
b->base = b->data = base;
|
||||
b->allocated = allocated;
|
||||
b->size = 0;
|
||||
b->l2 = b->l3 = b->l4 = NULL;
|
||||
b->l2 = b->l3 = b->l4 = b->l7 = NULL;
|
||||
b->next = NULL;
|
||||
}
|
||||
|
||||
@@ -128,7 +129,7 @@ buffer_tailroom(struct buffer *b)
|
||||
/* Ensures that 'b' has room for at least 'size' bytes at its tail end,
|
||||
* reallocating and copying its data if necessary. */
|
||||
void
|
||||
buffer_reserve_tailroom(struct buffer *b, size_t size)
|
||||
buffer_prealloc_tailroom(struct buffer *b, size_t size)
|
||||
{
|
||||
if (size > buffer_tailroom(b)) {
|
||||
size_t new_allocated = b->allocated + MAX(size, 64);
|
||||
@@ -148,11 +149,14 @@ buffer_reserve_tailroom(struct buffer *b, size_t size)
|
||||
if (b->l4) {
|
||||
b->l4 += base_delta;
|
||||
}
|
||||
if (b->l7) {
|
||||
b->l7 += base_delta;
|
||||
}
|
||||
}
|
||||
}
|
||||
|
||||
void
|
||||
buffer_reserve_headroom(struct buffer *b, size_t size)
|
||||
buffer_prealloc_headroom(struct buffer *b, size_t size)
|
||||
{
|
||||
assert(size <= buffer_headroom(b));
|
||||
}
|
||||
@@ -164,7 +168,7 @@ void *
|
||||
buffer_put_uninit(struct buffer *b, size_t size)
|
||||
{
|
||||
void *p;
|
||||
buffer_reserve_tailroom(b, size);
|
||||
buffer_prealloc_tailroom(b, size);
|
||||
p = buffer_tail(b);
|
||||
b->size += size;
|
||||
return p;
|
||||
@@ -181,15 +185,33 @@ buffer_put(struct buffer *b, const void *p, size_t size)
|
||||
return dst;
|
||||
}
|
||||
|
||||
/* Reserves 'size' bytes of headroom so that they can be later allocated with
|
||||
* buffer_push_uninit() without reallocating the buffer. */
|
||||
void
|
||||
buffer_reserve(struct buffer *b, size_t size)
|
||||
{
|
||||
assert(!b->size);
|
||||
buffer_prealloc_tailroom(b, size);
|
||||
b->data += size;
|
||||
}
|
||||
|
||||
void *
|
||||
buffer_push_uninit(struct buffer *b, size_t size)
|
||||
{
|
||||
buffer_reserve_headroom(b, size);
|
||||
buffer_prealloc_headroom(b, size);
|
||||
b->data -= size;
|
||||
b->size += size;
|
||||
return b->data;
|
||||
}
|
||||
|
||||
void *
|
||||
buffer_push(struct buffer *b, const void *p, size_t size)
|
||||
{
|
||||
void *dst = buffer_push_uninit(b, size);
|
||||
memcpy(dst, p, size);
|
||||
return dst;
|
||||
}
|
||||
|
||||
/* If 'b' contains at least 'offset + size' bytes of data, returns a pointer to
|
||||
* byte 'offset'. Otherwise, returns a null pointers. */
|
||||
void *
|
||||
@@ -231,12 +253,22 @@ buffer_clear(struct buffer *b)
|
||||
}
|
||||
|
||||
/* Removes 'size' bytes from the head end of 'b', which must contain at least
|
||||
* 'size' bytes of data. */
|
||||
void
|
||||
* 'size' bytes of data. Returns the first byte of data removed. */
|
||||
void *
|
||||
buffer_pull(struct buffer *b, size_t size)
|
||||
{
|
||||
void *data = b->data;
|
||||
assert(b->size >= size);
|
||||
b->data += size;
|
||||
b->size -= size;
|
||||
return data;
|
||||
}
|
||||
|
||||
/* If 'b' has at least 'size' bytes of data, removes that many bytes from the
|
||||
* head end of 'b' and returns the first byte removed. Otherwise, returns a
|
||||
* null pointer without modifying 'b'. */
|
||||
void *
|
||||
buffer_try_pull(struct buffer *b, size_t size)
|
||||
{
|
||||
return b->size >= size ? buffer_pull(b, size) : NULL;
|
||||
}
|
||||
@@ -31,6 +31,7 @@
|
||||
* derivatives without specific, written prior permission.
|
||||
*/
|
||||
|
||||
#include <config.h>
|
||||
#include "command-line.h"
|
||||
#include <getopt.h>
|
||||
#include <limits.h>
|
||||
|
||||
+115
@@ -0,0 +1,115 @@
|
||||
/* 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 "csum.h"
|
||||
|
||||
/* Returns the IP checksum of the 'n' bytes in 'data'. */
|
||||
uint16_t
|
||||
csum(const void *data, size_t n)
|
||||
{
|
||||
return csum_finish(csum_continue(0, data, n));
|
||||
}
|
||||
|
||||
/* Adds the 16 bits in 'new' to the partial IP checksum 'partial' and returns
|
||||
* the updated checksum. (To start a new checksum, pass 0 for 'partial'. To
|
||||
* obtain the finished checksum, pass the return value to csum_finish().) */
|
||||
uint32_t
|
||||
csum_add16(uint32_t partial, uint16_t new)
|
||||
{
|
||||
return partial + new;
|
||||
}
|
||||
|
||||
/* Adds the 32 bits in 'new' to the partial IP checksum 'partial' and returns
|
||||
* the updated checksum. (To start a new checksum, pass 0 for 'partial'. To
|
||||
* obtain the finished checksum, pass the return value to csum_finish().) */
|
||||
uint32_t
|
||||
csum_add32(uint32_t partial, uint32_t new)
|
||||
{
|
||||
return partial + (new >> 16) + (new & 0xffff);
|
||||
}
|
||||
|
||||
|
||||
/* Adds the 'n' bytes in 'data' to the partial IP checksum 'partial' and
|
||||
* returns the updated checksum. (To start a new checksum, pass 0 for
|
||||
* 'partial'. To obtain the finished checksum, pass the return value to
|
||||
* csum_finish().) */
|
||||
uint32_t
|
||||
csum_continue(uint32_t partial, const void *data_, size_t n)
|
||||
{
|
||||
const uint16_t *data = data_;
|
||||
|
||||
for (; n > 1; n -= 2) {
|
||||
partial = csum_add16(partial, *data++);
|
||||
}
|
||||
if (n) {
|
||||
partial += *(uint8_t *) data;
|
||||
}
|
||||
return partial;
|
||||
}
|
||||
|
||||
/* Returns the IP checksum corresponding to 'partial', which is a value updated
|
||||
* by some combination of csum_add16(), csum_add32(), and csum_continue(). */
|
||||
uint16_t
|
||||
csum_finish(uint32_t partial)
|
||||
{
|
||||
return ~((partial & 0xffff) + (partial >> 16));
|
||||
}
|
||||
|
||||
/* Returns the new checksum for a packet in which the checksum field previously
|
||||
* contained 'old_csum' and in which a field that contained 'old_u16' was
|
||||
* changed to contain 'new_u16'. */
|
||||
uint16_t
|
||||
recalc_csum16(uint16_t old_csum, uint16_t old_u16, uint16_t new_u16)
|
||||
{
|
||||
/* Ones-complement arithmetic is endian-independent, so this code does not
|
||||
* use htons() or ntohs().
|
||||
*
|
||||
* See RFC 1624 for formula and explanation. */
|
||||
uint16_t hc_complement = ~old_csum;
|
||||
uint16_t m_complement = ~old_u16;
|
||||
uint16_t m_prime = new_u16;
|
||||
uint32_t sum = hc_complement + m_complement + m_prime;
|
||||
uint16_t hc_prime_complement = sum + (sum >> 16);
|
||||
return ~hc_prime_complement;
|
||||
}
|
||||
|
||||
/* Returns the new checksum for a packet in which the checksum field previously
|
||||
* contained 'old_csum' and in which a field that contained 'old_u32' was
|
||||
* changed to contain 'new_u32'. */
|
||||
uint16_t
|
||||
recalc_csum32(uint16_t old_csum, uint32_t old_u32, uint32_t new_u32)
|
||||
{
|
||||
return recalc_csum16(recalc_csum16(old_csum, old_u32, new_u32),
|
||||
old_u32 >> 16, new_u32 >> 16);
|
||||
}
|
||||
+84
-30
@@ -31,8 +31,10 @@
|
||||
* derivatives without specific, written prior permission.
|
||||
*/
|
||||
|
||||
#include <config.h>
|
||||
#include "daemon.h"
|
||||
#include <errno.h>
|
||||
#include <fcntl.h>
|
||||
#include <stdlib.h>
|
||||
#include <string.h>
|
||||
#include <unistd.h>
|
||||
@@ -48,15 +50,27 @@ static bool detach;
|
||||
/* Name of pidfile (null if none). */
|
||||
static char *pidfile;
|
||||
|
||||
/* Returns the file name that would be used for a pidfile if 'name' were
|
||||
* provided to set_pidfile(). The caller must free the returned string. */
|
||||
char *
|
||||
make_pidfile_name(const char *name)
|
||||
{
|
||||
return (!name ? xasprintf("%s/%s.pid", RUNDIR, program_name)
|
||||
: *name == '/' ? xstrdup(name)
|
||||
: xasprintf("%s/%s", RUNDIR, name));
|
||||
}
|
||||
|
||||
/* Sets up a following call to daemonize() to create a pidfile named 'name'.
|
||||
* If 'name' begins with '/', then it is treated as an absolute path.
|
||||
* Otherwise, it is taken relative to RUNDIR, which is $(prefix)/var/run by
|
||||
* default. */
|
||||
* default.
|
||||
*
|
||||
* If 'name' is null, then program_name followed by ".pid" is used. */
|
||||
void
|
||||
set_pidfile(const char *name)
|
||||
{
|
||||
free(pidfile);
|
||||
pidfile = *name == '/' ? xstrdup(name) : xasprintf("%s/%s", RUNDIR, name);
|
||||
pidfile = make_pidfile_name(name);
|
||||
}
|
||||
|
||||
/* Sets up a following call to daemonize() to detach from the foreground
|
||||
@@ -67,25 +81,56 @@ set_detach(void)
|
||||
detach = true;
|
||||
}
|
||||
|
||||
/* If a pidfile has been configured, creates it and stores 'pid' in it. It is
|
||||
* the caller's responsibility to make sure that the pidfile will eventually
|
||||
* be deleted. */
|
||||
/* If a pidfile has been configured, creates it and stores the running process'
|
||||
* pid init. Ensures that the pidfile will be deleted when the process
|
||||
* exits. */
|
||||
static void
|
||||
make_pidfile(pid_t pid)
|
||||
make_pidfile(void)
|
||||
{
|
||||
if (pidfile) {
|
||||
FILE *file;
|
||||
/* Create pidfile via temporary file, so that observers never see an
|
||||
* empty pidfile or an unlocked pidfile. */
|
||||
long int pid = getpid();
|
||||
char *tmpfile;
|
||||
int fd;
|
||||
|
||||
file = fopen(pidfile, "w");
|
||||
if (file) {
|
||||
fprintf(file, "%ld\n", (long int) pid);
|
||||
fclose(file);
|
||||
tmpfile = xasprintf("%s.tmp%ld", pidfile, pid);
|
||||
fatal_signal_add_file_to_unlink(tmpfile);
|
||||
fd = open(tmpfile, O_CREAT | O_WRONLY | O_TRUNC, 0666);
|
||||
if (fd >= 0) {
|
||||
struct flock lck;
|
||||
lck.l_type = F_WRLCK;
|
||||
lck.l_whence = SEEK_SET;
|
||||
lck.l_start = 0;
|
||||
lck.l_len = 0;
|
||||
if (fcntl(fd, F_SETLK, &lck) >= 0) {
|
||||
char *text = xasprintf("%ld\n", pid);
|
||||
if (write(fd, text, strlen(text)) == strlen(text)) {
|
||||
fatal_signal_add_file_to_unlink(pidfile);
|
||||
if (rename(tmpfile, pidfile) < 0) {
|
||||
VLOG_ERR("failed to rename \"%s\" to \"%s\": %s",
|
||||
tmpfile, pidfile, strerror(errno));
|
||||
fatal_signal_remove_file_to_unlink(pidfile);
|
||||
close(fd);
|
||||
} else {
|
||||
/* Keep 'fd' open to retain the lock. */
|
||||
}
|
||||
} else {
|
||||
VLOG_ERR("%s: write failed: %s", tmpfile, strerror(errno));
|
||||
close(fd);
|
||||
}
|
||||
} else {
|
||||
VLOG_ERR("%s: fcntl failed: %s", tmpfile, strerror(errno));
|
||||
close(fd);
|
||||
}
|
||||
} else {
|
||||
VLOG_ERR("failed to create \"%s\": %s", pidfile, strerror(errno));
|
||||
VLOG_ERR("%s: create failed: %s", tmpfile, strerror(errno));
|
||||
}
|
||||
free(pidfile);
|
||||
pidfile = NULL;
|
||||
fatal_signal_remove_file_to_unlink(tmpfile);
|
||||
free(tmpfile);
|
||||
}
|
||||
free(pidfile);
|
||||
pidfile = NULL;
|
||||
}
|
||||
|
||||
/* If configured with set_pidfile() or set_detach(), creates the pid file and
|
||||
@@ -94,28 +139,37 @@ void
|
||||
daemonize(void)
|
||||
{
|
||||
if (detach) {
|
||||
pid_t pid;
|
||||
char c = 0;
|
||||
int fds[2];
|
||||
if (pipe(fds) < 0) {
|
||||
fatal(errno, "pipe failed");
|
||||
}
|
||||
|
||||
/* Fork and exit from the parent. */
|
||||
pid = fork();
|
||||
if (pid < 0) {
|
||||
fatal(errno, "could not fork");
|
||||
} else if (pid) {
|
||||
switch (fork()) {
|
||||
default:
|
||||
/* Parent process: wait for child to create pidfile, then exit. */
|
||||
close(fds[1]);
|
||||
fatal_signal_fork();
|
||||
make_pidfile(pid);
|
||||
read(fds[0], &c, 1);
|
||||
exit(0);
|
||||
}
|
||||
|
||||
if (pidfile) {
|
||||
fatal_signal_add_file_to_unlink(pidfile);
|
||||
case 0:
|
||||
/* Child process. */
|
||||
close(fds[0]);
|
||||
make_pidfile();
|
||||
write(fds[1], &c, 1);
|
||||
close(fds[1]);
|
||||
setsid();
|
||||
chdir("/");
|
||||
break;
|
||||
|
||||
case -1:
|
||||
/* Error. */
|
||||
fatal(errno, "could not fork");
|
||||
break;
|
||||
}
|
||||
setsid();
|
||||
chdir("/");
|
||||
} else {
|
||||
if (pidfile) {
|
||||
fatal_signal_add_file_to_unlink(pidfile);
|
||||
}
|
||||
make_pidfile(getpid());
|
||||
make_pidfile();
|
||||
}
|
||||
}
|
||||
|
||||
+1029
File diff suppressed because it is too large.
Load diff
+825
@@ -0,0 +1,825 @@
|
||||
/* 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 "dhcp.h"
|
||||
#include <arpa/inet.h>
|
||||
#include <assert.h>
|
||||
#include <ctype.h>
|
||||
#include <errno.h>
|
||||
#include <inttypes.h>
|
||||
#include <stdlib.h>
|
||||
#include "buffer.h"
|
||||
#include "dynamic-string.h"
|
||||
|
||||
#define THIS_MODULE VLM_dhcp
|
||||
#include "vlog.h"
|
||||
|
||||
/* Information about a DHCP argument type. */
|
||||
struct arg_type {
|
||||
const char *name; /* Name. */
|
||||
size_t size; /* Number of bytes per argument. */
|
||||
};
|
||||
|
||||
static struct arg_type types[] = {
|
||||
#define DHCP_ARG(NAME, SIZE) [DHCP_ARG_##NAME] = {#NAME, SIZE},
|
||||
DHCP_ARGS
|
||||
#undef DHCP_ARG
|
||||
};
|
||||
|
||||
/* Information about a DHCP option. */
|
||||
struct option_class {
|
||||
const char *name; /* Name. */
|
||||
enum dhcp_arg_type type; /* Argument type. */
|
||||
size_t min_args; /* Minimum number of arguments. */
|
||||
size_t max_args; /* Maximum number of arguments. */
|
||||
};
|
||||
|
||||
static struct option_class classes[DHCP_N_OPTIONS] = {
|
||||
[0 ... 255] = {NULL, DHCP_ARG_UINT8, 0, SIZE_MAX},
|
||||
#define DHCP_OPT(NAME, CODE, TYPE, MIN, MAX) \
|
||||
[CODE] = {#NAME, DHCP_ARG_##TYPE, MIN, MAX},
|
||||
DHCP_OPTS
|
||||
#undef DHCP_OPT
|
||||
};
|
||||
|
||||
static void copy_data(struct dhcp_msg *);
|
||||
|
||||
const char *
|
||||
dhcp_type_name(enum dhcp_msg_type type)
|
||||
{
|
||||
switch (type) {
|
||||
#define DHCP_MSG(NAME, VALUE) case NAME: return #NAME;
|
||||
DHCP_MSGS
|
||||
#undef DHCP_MSG
|
||||
}
|
||||
return "<<unknown DHCP message type>>";
|
||||
}
|
||||
|
||||
/* Initializes 'msg' as a DHCP message. The message should be freed with
|
||||
* dhcp_msg_uninit() when it is no longer needed. */
|
||||
void
|
||||
dhcp_msg_init(struct dhcp_msg *msg)
|
||||
{
|
||||
memset(msg, 0, sizeof *msg);
|
||||
}
|
||||
|
||||
/* Frees the contents of 'msg'. The caller is responsible for freeing 'msg',
|
||||
* if necessary. */
|
||||
void
|
||||
dhcp_msg_uninit(struct dhcp_msg *msg)
|
||||
{
|
||||
if (msg) {
|
||||
free(msg->data);
|
||||
}
|
||||
}
|
||||
|
||||
/* Initializes 'dst' as a copy of 'src'. 'dst' (and 'src') should be freed
|
||||
* with dhcp_msg_uninit() when it is no longer needed. */
|
||||
void
|
||||
dhcp_msg_copy(struct dhcp_msg *dst, const struct dhcp_msg *src)
|
||||
{
|
||||
*dst = *src;
|
||||
dst->data_allocated = src->data_used;
|
||||
dst->data_used = 0;
|
||||
dst->data = xmalloc(dst->data_allocated);
|
||||
copy_data(dst);
|
||||
}
|
||||
|
||||
static void
|
||||
prealloc_data(struct dhcp_msg *msg, size_t n)
|
||||
{
|
||||
size_t needed = msg->data_used + n;
|
||||
if (needed > msg->data_allocated) {
|
||||
uint8_t *old_data = msg->data;
|
||||
msg->data_allocated = MAX(needed * 2, 64);
|
||||
msg->data = xmalloc(msg->data_allocated);
|
||||
if (old_data) {
|
||||
copy_data(msg);
|
||||
free(old_data);
|
||||
}
|
||||
}
|
||||
}
|
||||
|
||||
static void *
|
||||
append_data(struct dhcp_msg *msg, const void *data, size_t n)
|
||||
{
|
||||
uint8_t *p = &msg->data[msg->data_used];
|
||||
memcpy(p, data, n);
|
||||
msg->data_used += n;
|
||||
return p;
|
||||
}
|
||||
|
||||
static void
|
||||
copy_data(struct dhcp_msg *msg)
|
||||
{
|
||||
int code;
|
||||
|
||||
msg->data_used = 0;
|
||||
for (code = 0; code < DHCP_N_OPTIONS; code++) {
|
||||
struct dhcp_option *opt = &msg->options[code];
|
||||
if (opt->data) {
|
||||
assert(msg->data_used + opt->n <= msg->data_allocated);
|
||||
opt->data = append_data(msg, opt->data, opt->n);
|
||||
}
|
||||
}
|
||||
}
|
||||
|
||||
/* Appends the 'n' bytes in 'data' to the DHCP option in 'msg' represented by
|
||||
* 'code' (which must be in the range 0...DHCP_N_OPTIONS). */
|
||||
void
|
||||
dhcp_msg_put(struct dhcp_msg *msg, int code,
|
||||
const void *data, size_t n)
|
||||
{
|
||||
struct dhcp_option *opt;
|
||||
if (code == DHCP_CODE_PAD || code == DHCP_CODE_END) {
|
||||
return;
|
||||
}
|
||||
|
||||
opt = &msg->options[code];
|
||||
prealloc_data(msg, n + opt->n);
|
||||
if (opt->n) {
|
||||
if (&msg->data[msg->data_used - opt->n] != opt->data) {
|
||||
opt->data = append_data(msg, opt->data, opt->n);
|
||||
}
|
||||
append_data(msg, data, n);
|
||||
} else {
|
||||
opt->data = append_data(msg, data, n);
|
||||
}
|
||||
opt->n += n;
|
||||
}
|
||||
|
||||
/* Appends the boolean value 'b', as a octet with value 0 (false) or 1 (true),
|
||||
* to the DHCP option in 'msg' represented by 'code' (which must be in the
|
||||
* range 0...DHCP_N_OPTIONS). */
|
||||
void
|
||||
dhcp_msg_put_bool(struct dhcp_msg *msg, int code, bool b_)
|
||||
{
|
||||
char b = !!b_;
|
||||
dhcp_msg_put(msg, code, &b, 1);
|
||||
}
|
||||
|
||||
/* Appends the number of seconds 'secs', as a 32-bit number in network byte
|
||||
* order, to the DHCP option in 'msg' represented by 'code' (which must be in
|
||||
* the range 0...DHCP_N_OPTIONS). */
|
||||
void
|
||||
dhcp_msg_put_secs(struct dhcp_msg *msg, int code, uint32_t secs_)
|
||||
{
|
||||
uint32_t secs = htonl(secs_);
|
||||
dhcp_msg_put(msg, code, &secs, sizeof secs);
|
||||
}
|
||||
|
||||
/* Appends the IP address 'ip', as a 32-bit number in network byte order, to
|
||||
* the DHCP option in 'msg' represented by 'code' (which must be in the range
|
||||
* 0...DHCP_N_OPTIONS). */
|
||||
void
|
||||
dhcp_msg_put_ip(struct dhcp_msg *msg, int code, uint32_t ip)
|
||||
{
|
||||
dhcp_msg_put(msg, code, &ip, sizeof ip);
|
||||
}
|
||||
|
||||
/* Appends the ASCII string 'string', to the DHCP option in 'msg' represented
|
||||
* by 'code' (which must be in the range 0...DHCP_N_OPTIONS). */
|
||||
void
|
||||
dhcp_msg_put_string(struct dhcp_msg *msg, int code, const char *string)
|
||||
{
|
||||
dhcp_msg_put(msg, code, string, strlen(string));
|
||||
}
|
||||
|
||||
/* Appends octet 'x' to DHCP option in 'msg' represented by 'code' (which must
|
||||
* be in the range 0...DHCP_N_OPTIONS). */
|
||||
void
|
||||
dhcp_msg_put_uint8(struct dhcp_msg *msg, int code, uint8_t x)
|
||||
{
|
||||
dhcp_msg_put(msg, code, &x, sizeof x);
|
||||
}
|
||||
|
||||
/* Appends the 'n' octets in 'data' to DHCP option in 'msg' represented by
|
||||
* 'code' (which must be in the range 0...DHCP_N_OPTIONS). */
|
||||
void dhcp_msg_put_uint8_array(struct dhcp_msg *msg, int code,
|
||||
const uint8_t data[], size_t n)
|
||||
{
|
||||
dhcp_msg_put(msg, code, data, n);
|
||||
}
|
||||
|
||||
/* Appends the 16-bit value in 'x', in network byte order, to DHCP option in
|
||||
* 'msg' represented by 'code' (which must be in the range
|
||||
* 0...DHCP_N_OPTIONS). */
|
||||
void
|
||||
dhcp_msg_put_uint16(struct dhcp_msg *msg, int code, uint16_t x_)
|
||||
{
|
||||
uint16_t x = htons(x_);
|
||||
dhcp_msg_put(msg, code, &x, sizeof x);
|
||||
}
|
||||
|
||||
|
||||
/* Appends the 'n' 16-bit values in 'data', in network byte order, to DHCP
|
||||
* option in 'msg' represented by 'code' (which must be in the range
|
||||
* 0...DHCP_N_OPTIONS). */
|
||||
void
|
||||
dhcp_msg_put_uint16_array(struct dhcp_msg *msg, int code,
|
||||
const uint16_t data[], size_t n)
|
||||
{
|
||||
size_t i;
|
||||
|
||||
for (i = 0; i < n; i++) {
|
||||
dhcp_msg_put_uint16(msg, code, data[i]);
|
||||
}
|
||||
}
|
||||
|
||||
/* Returns a pointer to the 'size' bytes starting at byte offset 'offset' in
|
||||
* the DHCP option in 'msg' represented by 'code' (which must be in the range
|
||||
* 0...DHCP_N_OPTIONS). If the option has fewer than 'offset + size' bytes,
|
||||
* returns a null pointer. */
|
||||
const void *
|
||||
dhcp_msg_get(const struct dhcp_msg *msg, int code,
|
||||
size_t offset, size_t size)
|
||||
{
|
||||
const struct dhcp_option *opt = &msg->options[code];
|
||||
return offset + size <= opt->n ? (const char *) opt->data + offset : NULL;
|
||||
}
|
||||
|
||||
/* Stores in '*out' the boolean value at byte offset 'offset' in the DHCP
|
||||
* option in 'msg' represented by 'code' (which must be in the range
|
||||
* 0...DHCP_N_OPTIONS). Returns true if successful, false if the option has
|
||||
* fewer than 'offset + 1' bytes. */
|
||||
bool
|
||||
dhcp_msg_get_bool(const struct dhcp_msg *msg, int code, size_t offset,
|
||||
bool *out)
|
||||
{
|
||||
const uint8_t *uint8 = dhcp_msg_get(msg, code, offset, sizeof *uint8);
|
||||
if (uint8) {
|
||||
*out = *uint8 != 0;
|
||||
return true;
|
||||
} else {
|
||||
return false;
|
||||
}
|
||||
}
|
||||
|
||||
/* Stores in '*out' the 32-bit count of seconds at offset 'offset' (in
|
||||
* 4-byte increments) in the DHCP option in 'msg' represented by 'code'
|
||||
* (which must be in the range 0...DHCP_N_OPTIONS). The value is converted to
|
||||
* native byte order. Returns true if successful, false if the option has
|
||||
* fewer than '4 * (offset + 1)' bytes. */
|
||||
bool
|
||||
dhcp_msg_get_secs(const struct dhcp_msg *msg, int code, size_t offset,
|
||||
uint32_t *out)
|
||||
{
|
||||
const uint32_t *uint32 = dhcp_msg_get(msg, code, offset * sizeof *uint32,
|
||||
sizeof *uint32);
|
||||
if (uint32) {
|
||||
*out = ntohl(*uint32);
|
||||
return true;
|
||||
} else {
|
||||
return false;
|
||||
}
|
||||
}
|
||||
|
||||
/* Stores in '*out' the IP address at offset 'offset' (in 4-byte increments) in
|
||||
* the DHCP option in 'msg' represented by 'code' (which must be in the range
|
||||
* 0...DHCP_N_OPTIONS). The IP address is stored in network byte order.
|
||||
* Returns true if successful, false if the option has fewer than '4 * (offset
|
||||
* + 1)' bytes. */
|
||||
bool
|
||||
dhcp_msg_get_ip(const struct dhcp_msg *msg, int code,
|
||||
size_t offset, uint32_t *out)
|
||||
{
|
||||
const uint32_t *uint32 = dhcp_msg_get(msg, code, offset * sizeof *uint32,
|
||||
sizeof *uint32);
|
||||
if (uint32) {
|
||||
*out = *uint32;
|
||||
return true;
|
||||
} else {
|
||||
return false;
|
||||
}
|
||||
}
|
||||
|
||||
/* Returns the string in the DHCP option in 'msg' represented by 'code' (which
|
||||
* must be in the range 0...DHCP_N_OPTIONS). The caller is responsible for
|
||||
* freeing the string with free().
|
||||
*
|
||||
* If 'msg' has no option represented by 'code', returns a null pointer. (If
|
||||
* the option was specified but had no content, then an empty string is
|
||||
* returned, not a null pointer.) */
|
||||
char *
|
||||
dhcp_msg_get_string(const struct dhcp_msg *msg, int code)
|
||||
{
|
||||
const struct dhcp_option *opt = &msg->options[code];
|
||||
return opt->data ? xmemdup0(opt->data, opt->n) : NULL;
|
||||
}
|
||||
|
||||
/* Stores in '*out' the octet at byte offset 'offset' in the DHCP option in
|
||||
* 'msg' represented by 'code' (which must be in the range 0...DHCP_N_OPTIONS).
|
||||
* Returns true if successful, false if the option has fewer than 'offset + 1'
|
||||
* bytes. */
|
||||
bool
|
||||
dhcp_msg_get_uint8(const struct dhcp_msg *msg, int code,
|
||||
size_t offset, uint8_t *out)
|
||||
{
|
||||
const uint8_t *uint8 = dhcp_msg_get(msg, code, offset, sizeof *uint8);
|
||||
if (uint8) {
|
||||
*out = *uint8;
|
||||
return true;
|
||||
} else {
|
||||
return false;
|
||||
}
|
||||
}
|
||||
|
||||
/* Stores in '*out' the 16-bit value at offset 'offset' (in 2-byte units) in
|
||||
* the DHCP option in 'msg' represented by 'code' (which must be in the range
|
||||
* 0...DHCP_N_OPTIONS). The value is converted to native byte order. Returns
|
||||
* true if successful, false if the option has fewer than '2 * (offset + 1)'
|
||||
* bytes. */
|
||||
bool
|
||||
dhcp_msg_get_uint16(const struct dhcp_msg *msg, int code,
|
||||
size_t offset, uint16_t *out)
|
||||
{
|
||||
const uint16_t *uint16 = dhcp_msg_get(msg, code, offset * sizeof *uint16,
|
||||
sizeof *uint16);
|
||||
if (uint16) {
|
||||
*out = ntohs(*uint16);
|
||||
return true;
|
||||
} else {
|
||||
return false;
|
||||
}
|
||||
}
|
||||
|
||||
/* 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 *
|
||||
dhcp_option_to_string(const struct dhcp_option *opt, int code, struct ds *ds)
|
||||
{
|
||||
struct option_class *class = &classes[code];
|
||||
const struct arg_type *type = &types[class->type];
|
||||
size_t offset;
|
||||
|
||||
if (class->name) {
|
||||
const char *cp;
|
||||
for (cp = class->name; *cp; cp++) {
|
||||
unsigned char c = *cp;
|
||||
ds_put_char(ds, c == '_' ? '-' : tolower(c));
|
||||
}
|
||||
} else {
|
||||
ds_put_format(ds, "option-%d", code);
|
||||
}
|
||||
ds_put_char(ds, '=');
|
||||
|
||||
if (!opt->data || !opt->n) {
|
||||
ds_put_cstr(ds, opt->data ? "empty" : "null");
|
||||
return ds_cstr(ds);
|
||||
}
|
||||
|
||||
if (class->type == DHCP_ARG_STRING) {
|
||||
ds_put_char(ds, '"');
|
||||
}
|
||||
for (offset = 0; offset + type->size <= opt->n; offset += type->size) {
|
||||
const void *p = (const char *) opt->data + offset;
|
||||
const uint8_t *uint8 = p;
|
||||
const uint32_t *uint32 = p;
|
||||
const uint16_t *uint16 = p;
|
||||
const char *cp = p;
|
||||
unsigned char c;
|
||||
|
||||
if (offset && class->type != DHCP_ARG_STRING) {
|
||||
ds_put_cstr(ds, class->type == DHCP_ARG_UINT8 ? ":" : ", ");
|
||||
}
|
||||
switch (class->type) {
|
||||
case DHCP_ARG_FIXED:
|
||||
NOT_REACHED();
|
||||
case DHCP_ARG_IP:
|
||||
ds_put_format(ds, IP_FMT, IP_ARGS(uint32));
|
||||
break;
|
||||
case DHCP_ARG_UINT8:
|
||||
ds_put_format(ds, "%02"PRIx8, *uint8);
|
||||
break;
|
||||
case DHCP_ARG_UINT16:
|
||||
ds_put_format(ds, "%"PRIu16, ntohs(*uint16));
|
||||
break;
|
||||
case DHCP_ARG_UINT32:
|
||||
ds_put_format(ds, "%"PRIu32, ntohl(*uint32));
|
||||
break;
|
||||
case DHCP_ARG_SECS:
|
||||
put_duration(ds, ntohl(*uint32));
|
||||
break;
|
||||
case DHCP_ARG_STRING:
|
||||
c = *cp;
|
||||
if (isprint(c) && (!isspace(c) || c == ' ') && c != '\\') {
|
||||
ds_put_char(ds, *cp);
|
||||
} else {
|
||||
ds_put_format(ds, "\\%03o", (int) c);
|
||||
}
|
||||
break;
|
||||
case DHCP_ARG_BOOLEAN:
|
||||
if (*uint8 == 0) {
|
||||
ds_put_cstr(ds, "false");
|
||||
} else if (*uint8 == 1) {
|
||||
ds_put_cstr(ds, "true");
|
||||
} else {
|
||||
ds_put_format(ds, "**%"PRIu8"**", *uint8);
|
||||
}
|
||||
break;
|
||||
}
|
||||
}
|
||||
if (class->type == DHCP_ARG_STRING) {
|
||||
ds_put_char(ds, '"');
|
||||
}
|
||||
if (offset != opt->n) {
|
||||
if (offset) {
|
||||
ds_put_cstr(ds, ", ");
|
||||
}
|
||||
ds_put_cstr(ds, "**leftovers:");
|
||||
for (; offset < opt->n; offset++) {
|
||||
const void *p = (const char *) opt->data + offset;
|
||||
const uint8_t *uint8 = p;
|
||||
ds_put_format(ds, " %"PRIu8, *uint8);
|
||||
}
|
||||
ds_put_cstr(ds, "**");
|
||||
}
|
||||
return ds_cstr(ds);
|
||||
}
|
||||
|
||||
/* Returns true if 'a' and 'b' have the same content, false otherwise. */
|
||||
bool
|
||||
dhcp_option_equals(const struct dhcp_option *a, const struct dhcp_option *b)
|
||||
{
|
||||
return ((a->data != NULL) == (b->data != NULL)
|
||||
&& a->n == b->n
|
||||
&& !memcmp(a->data, b->data, a->n));
|
||||
}
|
||||
|
||||
/* 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, bool multiline, struct ds *ds)
|
||||
{
|
||||
char separator = multiline ? '\n' : ' ';
|
||||
int code;
|
||||
|
||||
ds_clear(ds);
|
||||
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_format(ds, "%cflags=", separator);
|
||||
if (msg->flags & DHCP_FLAGS_BROADCAST) {
|
||||
ds_put_cstr(ds, "[BROADCAST]");
|
||||
}
|
||||
if (msg->flags & DHCP_FLAGS_MBZ) {
|
||||
ds_put_format(ds, "[0x%04"PRIx16"]", msg->flags & DHCP_FLAGS_MBZ);
|
||||
}
|
||||
}
|
||||
if (msg->ciaddr) {
|
||||
ds_put_format(ds, "%cciaddr="IP_FMT, separator, IP_ARGS(&msg->ciaddr));
|
||||
}
|
||||
if (msg->yiaddr) {
|
||||
ds_put_format(ds, "%cyiaddr="IP_FMT, separator, IP_ARGS(&msg->yiaddr));
|
||||
}
|
||||
if (msg->siaddr) {
|
||||
ds_put_format(ds, "%csiaddr="IP_FMT, separator, IP_ARGS(&msg->siaddr));
|
||||
}
|
||||
if (msg->giaddr) {
|
||||
ds_put_format(ds, "%cgiaddr="IP_FMT, separator, IP_ARGS(&msg->giaddr));
|
||||
}
|
||||
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);
|
||||
}
|
||||
|
||||
static void
|
||||
parse_options(struct dhcp_msg *msg, const char *name, void *data, size_t size,
|
||||
int option_offset)
|
||||
{
|
||||
struct buffer b;
|
||||
|
||||
b.data = data;
|
||||
b.size = size;
|
||||
for (;;) {
|
||||
uint8_t *code, *len;
|
||||
void *payload;
|
||||
|
||||
code = buffer_try_pull(&b, 1);
|
||||
if (!code || *code == DHCP_CODE_END) {
|
||||
break;
|
||||
} else if (*code == DHCP_CODE_PAD) {
|
||||
continue;
|
||||
}
|
||||
|
||||
len = buffer_try_pull(&b, 1);
|
||||
if (!len) {
|
||||
VLOG_DBG("reached end of %s expecting length byte", name);
|
||||
break;
|
||||
}
|
||||
|
||||
payload = buffer_try_pull(&b, *len);
|
||||
if (!payload) {
|
||||
VLOG_DBG("expected %"PRIu8" bytes of option-%"PRIu8" payload "
|
||||
"with only %zu bytes of %s left",
|
||||
*len, *code, b.size, name);
|
||||
break;
|
||||
}
|
||||
dhcp_msg_put(msg, *code + option_offset, payload, *len);
|
||||
}
|
||||
}
|
||||
|
||||
static void
|
||||
validate_options(struct dhcp_msg *msg)
|
||||
{
|
||||
int code;
|
||||
|
||||
for (code = 0; code < DHCP_N_OPTIONS; code++) {
|
||||
struct dhcp_option *opt = &msg->options[code];
|
||||
struct option_class *class = &classes[code];
|
||||
struct arg_type *type = &types[class->type];
|
||||
if (opt->data) {
|
||||
size_t n_elems = opt->n / type->size;
|
||||
size_t remainder = opt->n % type->size;
|
||||
bool ok = true;
|
||||
if (remainder) {
|
||||
VLOG_DBG("%s option has %zu %zu-byte %s arguments with "
|
||||
"%zu bytes left over",
|
||||
class->name, n_elems, type->size,
|
||||
type->name, remainder);
|
||||
ok = false;
|
||||
}
|
||||
if (n_elems < class->min_args || n_elems > class->max_args) {
|
||||
VLOG_DBG("%s option has %zu %zu-byte %s arguments but "
|
||||
"between %zu and %zu are required",
|
||||
class->name, n_elems, type->size, type->name,
|
||||
class->min_args, class->max_args);
|
||||
ok = false;
|
||||
}
|
||||
if (!ok) {
|
||||
struct ds ds = DS_EMPTY_INITIALIZER;
|
||||
VLOG_DBG("%s option contains: %s",
|
||||
class->name, dhcp_option_to_string(opt, code, &ds));
|
||||
ds_destroy(&ds);
|
||||
|
||||
opt->n = 0;
|
||||
opt->data = NULL;
|
||||
}
|
||||
}
|
||||
}
|
||||
}
|
||||
|
||||
/* Attempts to parse 'b' as a DHCP message. If successful, initializes '*msg'
|
||||
* to the parsed message and returns 0. Otherwise, returns a positive errno
|
||||
* value and '*msg' is indeterminate. */
|
||||
int
|
||||
dhcp_parse(struct dhcp_msg *msg, const struct buffer *b_)
|
||||
{
|
||||
struct buffer b = *b_;
|
||||
struct dhcp_header *dhcp;
|
||||
uint32_t *cookie;
|
||||
uint8_t type;
|
||||
char *vendor_class;
|
||||
|
||||
dhcp = buffer_try_pull(&b, sizeof *dhcp);
|
||||
if (!dhcp) {
|
||||
VLOG_DBG("buffer too small for DHCP header (%zu bytes)", b.size);
|
||||
goto error;
|
||||
}
|
||||
|
||||
if (dhcp->op != DHCP_BOOTREPLY && dhcp->op != DHCP_BOOTREQUEST) {
|
||||
VLOG_DBG("invalid DHCP op (%"PRIu8")", dhcp->op);
|
||||
goto error;
|
||||
}
|
||||
if (dhcp->htype != ARP_HRD_ETHERNET) {
|
||||
VLOG_DBG("invalid DHCP htype (%"PRIu8")", dhcp->htype);
|
||||
goto error;
|
||||
}
|
||||
if (dhcp->hlen != ETH_ADDR_LEN) {
|
||||
VLOG_DBG("invalid DHCP hlen (%"PRIu8")", dhcp->hlen);
|
||||
goto error;
|
||||
}
|
||||
|
||||
dhcp_msg_init(msg);
|
||||
msg->op = dhcp->op;
|
||||
msg->xid = ntohl(dhcp->xid);
|
||||
msg->secs = ntohs(dhcp->secs);
|
||||
msg->flags = ntohs(dhcp->flags);
|
||||
msg->ciaddr = dhcp->ciaddr;
|
||||
msg->yiaddr = dhcp->yiaddr;
|
||||
msg->siaddr = dhcp->siaddr;
|
||||
msg->giaddr = dhcp->giaddr;
|
||||
memcpy(msg->chaddr, dhcp->chaddr, ETH_ADDR_LEN);
|
||||
|
||||
cookie = buffer_try_pull(&b, sizeof cookie);
|
||||
if (cookie) {
|
||||
if (ntohl(*cookie) == DHCP_OPTS_COOKIE) {
|
||||
uint8_t overload;
|
||||
|
||||
parse_options(msg, "options", b.data, b.size, 0);
|
||||
if (dhcp_msg_get_uint8(msg, DHCP_CODE_OPTION_OVERLOAD,
|
||||
0, &overload)) {
|
||||
if (overload & 1) {
|
||||
parse_options(msg, "file", dhcp->file, sizeof dhcp->file,
|
||||
0);
|
||||
}
|
||||
if (overload & 2) {
|
||||
parse_options(msg, "sname",
|
||||
dhcp->sname, sizeof dhcp->sname, 0);
|
||||
}
|
||||
}
|
||||
} else {
|
||||
VLOG_DBG("bad DHCP options cookie: %08"PRIx32, ntohl(*cookie));
|
||||
}
|
||||
} else {
|
||||
VLOG_DBG("DHCP packet has no options");
|
||||
}
|
||||
|
||||
vendor_class = dhcp_msg_get_string(msg, DHCP_CODE_VENDOR_CLASS);
|
||||
if (vendor_class && !strcmp(vendor_class, "OpenFlow")) {
|
||||
parse_options(msg, "vendor-specific",
|
||||
msg->options[DHCP_CODE_VENDOR_SPECIFIC].data,
|
||||
msg->options[DHCP_CODE_VENDOR_SPECIFIC].n,
|
||||
DHCP_VENDOR_OFS);
|
||||
}
|
||||
free(vendor_class);
|
||||
|
||||
validate_options(msg);
|
||||
if (!dhcp_msg_get_uint8(msg, DHCP_CODE_DHCP_MSG_TYPE, 0, &type)) {
|
||||
VLOG_DBG("missing DHCP message type");
|
||||
dhcp_msg_uninit(msg);
|
||||
goto error;
|
||||
}
|
||||
msg->type = type;
|
||||
return 0;
|
||||
|
||||
error:
|
||||
if (VLOG_IS_DBG_ENABLED()) {
|
||||
struct ds ds;
|
||||
|
||||
ds_init(&ds);
|
||||
ds_put_hex_dump(&ds, b_->data, b_->size, 0, true);
|
||||
VLOG_DBG("invalid DHCP message dump:\n%s", ds_cstr(&ds));
|
||||
|
||||
ds_clear(&ds);
|
||||
dhcp_msg_to_string(msg, false, &ds);
|
||||
VLOG_DBG("partially dissected DHCP message: %s", ds_cstr(&ds));
|
||||
|
||||
ds_destroy(&ds);
|
||||
}
|
||||
return EPROTO;
|
||||
}
|
||||
|
||||
static void
|
||||
put_option_chunk(struct buffer *b, uint8_t code, void *data, size_t n)
|
||||
{
|
||||
uint8_t header[2];
|
||||
|
||||
assert(n < 256);
|
||||
header[0] = code;
|
||||
header[1] = n;
|
||||
buffer_put(b, header, sizeof header);
|
||||
buffer_put(b, data, n);
|
||||
}
|
||||
|
||||
static void
|
||||
put_option(struct buffer *b, uint8_t code, void *data, size_t n)
|
||||
{
|
||||
if (data) {
|
||||
if (n) {
|
||||
/* Divide the data into chunks of 255 bytes or less. Make
|
||||
* intermediate chunks multiples of 8 bytes in case the
|
||||
* recipient validates a chunk at a time instead of the
|
||||
* concatenated value. */
|
||||
uint8_t *p = data;
|
||||
while (n) {
|
||||
size_t chunk = n > 255 ? 248 : n;
|
||||
put_option_chunk(b, code, p, chunk);
|
||||
p += chunk;
|
||||
n -= chunk;
|
||||
}
|
||||
} else {
|
||||
/* Option should be present but carry no data. */
|
||||
put_option_chunk(b, code, NULL, 0);
|
||||
}
|
||||
}
|
||||
}
|
||||
|
||||
/* Appends to 'b' the DHCP message represented by 'msg'. */
|
||||
void
|
||||
dhcp_assemble(const struct dhcp_msg *msg, struct buffer *b)
|
||||
{
|
||||
const uint8_t end = DHCP_CODE_END;
|
||||
uint32_t cookie = htonl(DHCP_OPTS_COOKIE);
|
||||
struct buffer vnd_data;
|
||||
struct dhcp_header dhcp;
|
||||
int i;
|
||||
|
||||
memset(&dhcp, 0, sizeof dhcp);
|
||||
dhcp.op = msg->op;
|
||||
dhcp.htype = ARP_HRD_ETHERNET;
|
||||
dhcp.hlen = ETH_ADDR_LEN;
|
||||
dhcp.hops = 0;
|
||||
dhcp.xid = htonl(msg->xid);
|
||||
dhcp.secs = htons(msg->secs);
|
||||
dhcp.flags = htons(msg->flags);
|
||||
dhcp.ciaddr = msg->ciaddr;
|
||||
dhcp.yiaddr = msg->yiaddr;
|
||||
dhcp.siaddr = msg->siaddr;
|
||||
dhcp.giaddr = msg->giaddr;
|
||||
memcpy(dhcp.chaddr, msg->chaddr, ETH_ADDR_LEN);
|
||||
buffer_put(b, &dhcp, sizeof dhcp);
|
||||
buffer_put(b, &cookie, sizeof cookie);
|
||||
|
||||
/* Put DHCP message type first. (The ordering is not required but it
|
||||
* seems polite.) */
|
||||
if (msg->type) {
|
||||
uint8_t type = msg->type;
|
||||
put_option(b, DHCP_CODE_DHCP_MSG_TYPE, &type, 1);
|
||||
}
|
||||
|
||||
/* Put the standard options. */
|
||||
for (i = 0; i < DHCP_VENDOR_OFS; i++) {
|
||||
const struct dhcp_option *option = &msg->options[i];
|
||||
put_option(b, i, option->data, option->n);
|
||||
}
|
||||
|
||||
/* Assemble vendor specific option and put it. */
|
||||
buffer_init(&vnd_data, 0);
|
||||
for (i = DHCP_VENDOR_OFS; i < DHCP_N_OPTIONS; i++) {
|
||||
const struct dhcp_option *option = &msg->options[i];
|
||||
put_option(&vnd_data, i - DHCP_VENDOR_OFS, option->data, option->n);
|
||||
}
|
||||
if (vnd_data.size) {
|
||||
put_option(b, DHCP_CODE_VENDOR_SPECIFIC, vnd_data.data, vnd_data.size);
|
||||
}
|
||||
buffer_uninit(&vnd_data);
|
||||
|
||||
/* Put end-of-options option. */
|
||||
buffer_put(b, &end, sizeof end);
|
||||
}
|
||||
|
||||
+1
-19
@@ -31,6 +31,7 @@
|
||||
* derivatives without specific, written prior permission.
|
||||
*/
|
||||
|
||||
#include <config.h>
|
||||
#include "dpif.h"
|
||||
|
||||
#include <ctype.h>
|
||||
@@ -269,25 +270,6 @@ dpif_del_port(struct dpif *dp, const char *netdev)
|
||||
{
|
||||
return send_mgmt_command(dp, DP_GENL_C_DEL_PORT, netdev);
|
||||
}
|
||||
|
||||
/* Tells dp to send num_packets up through netlink for benchmarking*/
|
||||
int
|
||||
dpif_benchmark_nl(struct dpif *dp, uint32_t num_packets, uint32_t packet_size)
|
||||
{
|
||||
struct buffer request;
|
||||
int retval;
|
||||
|
||||
buffer_init(&request, 0);
|
||||
nl_msg_put_genlmsghdr(&request, dp->sock, 0, openflow_family,
|
||||
NLM_F_REQUEST, DP_GENL_C_BENCHMARK_NL, 1);
|
||||
nl_msg_put_u32(&request, DP_GENL_A_DP_IDX, dp->dp_idx);
|
||||
nl_msg_put_u32(&request, DP_GENL_A_NPACKETS, num_packets);
|
||||
nl_msg_put_u32(&request, DP_GENL_A_PSIZE, packet_size);
|
||||
retval = nl_sock_send(dp->sock, &request, true);
|
||||
buffer_uninit(&request);
|
||||
|
||||
return retval;
|
||||
}
|
||||
|
||||
static const struct nl_policy openflow_multicast_policy[] = {
|
||||
[DP_GENL_A_DP_IDX] = { .type = NL_A_U32 },
|
||||
|
||||
@@ -31,6 +31,7 @@
|
||||
* derivatives without specific, written prior permission.
|
||||
*/
|
||||
|
||||
#include <config.h>
|
||||
#include "dynamic-string.h"
|
||||
#include <assert.h>
|
||||
#include <stdlib.h>
|
||||
@@ -45,6 +46,12 @@ ds_init(struct ds *ds)
|
||||
ds->allocated = 0;
|
||||
}
|
||||
|
||||
void
|
||||
ds_clear(struct ds *ds)
|
||||
{
|
||||
ds->length = 0;
|
||||
}
|
||||
|
||||
void
|
||||
ds_reserve(struct ds *ds, size_t min_length)
|
||||
{
|
||||
|
||||
+2
-1
@@ -30,6 +30,7 @@
|
||||
* advertising or publicity pertaining to the Software or any
|
||||
* derivatives without specific, written prior permission.
|
||||
*/
|
||||
#include <config.h>
|
||||
#include "fatal-signal.h"
|
||||
#include <assert.h>
|
||||
#include <errno.h>
|
||||
@@ -42,7 +43,7 @@
|
||||
#include "util.h"
|
||||
|
||||
/* Signals to catch. */
|
||||
static const int fatal_signals[] = { SIGTERM, SIGINT, SIGHUP };
|
||||
static const int fatal_signals[] = { SIGTERM, SIGINT, SIGHUP, SIGALRM };
|
||||
|
||||
/* Signals to catch as a sigset_t. */
|
||||
static sigset_t fatal_signal_set;
|
||||
|
||||
@@ -31,6 +31,7 @@
|
||||
* derivatives without specific, written prior permission.
|
||||
*/
|
||||
|
||||
#include <config.h>
|
||||
#include "fault.h"
|
||||
#include <dlfcn.h>
|
||||
#include <inttypes.h>
|
||||
|
||||
+60
-15
@@ -30,6 +30,7 @@
|
||||
* advertising or publicity pertaining to the Software or any
|
||||
* derivatives without specific, written prior permission.
|
||||
*/
|
||||
#include <config.h>
|
||||
#include <sys/types.h>
|
||||
#include "flow.h"
|
||||
#include <inttypes.h>
|
||||
@@ -43,6 +44,44 @@
|
||||
#include "vlog.h"
|
||||
#define THIS_MODULE VLM_flow
|
||||
|
||||
struct ip_header *
|
||||
pull_ip(struct buffer *packet)
|
||||
{
|
||||
if (packet->size >= IP_HEADER_LEN) {
|
||||
struct ip_header *ip = packet->data;
|
||||
int ip_len = IP_IHL(ip->ip_ihl_ver) * 4;
|
||||
if (ip_len >= IP_HEADER_LEN && packet->size >= ip_len) {
|
||||
return buffer_pull(packet, ip_len);
|
||||
}
|
||||
}
|
||||
return NULL;
|
||||
}
|
||||
|
||||
struct tcp_header *
|
||||
pull_tcp(struct buffer *packet)
|
||||
{
|
||||
if (packet->size >= TCP_HEADER_LEN) {
|
||||
struct tcp_header *tcp = packet->data;
|
||||
int tcp_len = TCP_OFFSET(tcp->tcp_ctl) * 4;
|
||||
if (tcp_len >= TCP_HEADER_LEN && packet->size >= tcp_len) {
|
||||
return buffer_pull(packet, tcp_len);
|
||||
}
|
||||
}
|
||||
return NULL;
|
||||
}
|
||||
|
||||
struct udp_header *
|
||||
pull_udp(struct buffer *packet)
|
||||
{
|
||||
return buffer_try_pull(packet, UDP_HEADER_LEN);
|
||||
}
|
||||
|
||||
struct eth_header *
|
||||
pull_eth(struct buffer *packet)
|
||||
{
|
||||
return buffer_try_pull(packet, ETH_HEADER_LEN);
|
||||
}
|
||||
|
||||
void
|
||||
flow_extract(struct buffer *packet, uint16_t in_port, struct flow *flow)
|
||||
{
|
||||
@@ -64,10 +103,10 @@ flow_extract(struct buffer *packet, uint16_t in_port, struct flow *flow)
|
||||
packet->l2 = b.data;
|
||||
packet->l3 = NULL;
|
||||
packet->l4 = NULL;
|
||||
packet->l7 = NULL;
|
||||
|
||||
eth = buffer_at(&b, 0, sizeof *eth);
|
||||
eth = pull_eth(&b);
|
||||
if (eth) {
|
||||
buffer_pull(&b, ETH_HEADER_LEN);
|
||||
if (ntohs(eth->eth_type) >= OFP_DL_TYPE_ETH2_CUTOFF) {
|
||||
/* This is an Ethernet II frame */
|
||||
flow->dl_type = eth->eth_type;
|
||||
@@ -104,23 +143,29 @@ flow_extract(struct buffer *packet, uint16_t in_port, struct flow *flow)
|
||||
|
||||
packet->l3 = b.data;
|
||||
if (flow->dl_type == htons(ETH_TYPE_IP)) {
|
||||
const struct ip_header *nh = buffer_at(&b, 0, sizeof *nh);
|
||||
const struct ip_header *nh = pull_ip(&b);
|
||||
if (nh) {
|
||||
int ip_len = IP_IHL(nh->ip_ihl_ver) * 4;
|
||||
if (ip_len < IP_HEADER_LEN) {
|
||||
return;
|
||||
}
|
||||
|
||||
flow->nw_src = nh->ip_src;
|
||||
flow->nw_dst = nh->ip_dst;
|
||||
flow->nw_proto = nh->ip_proto;
|
||||
if (flow->nw_proto == IP_TYPE_TCP
|
||||
|| flow->nw_proto == IP_TYPE_UDP) {
|
||||
const struct udp_header *th;
|
||||
th = packet->l4 = buffer_at(&b, ip_len, sizeof *th);
|
||||
if (th) {
|
||||
flow->tp_src = th->udp_src;
|
||||
flow->tp_dst = th->udp_dst;
|
||||
packet->l4 = b.data;
|
||||
if (flow->nw_proto == IP_TYPE_TCP) {
|
||||
const struct tcp_header *tcp = pull_tcp(&b);
|
||||
if (tcp) {
|
||||
flow->tp_src = tcp->tcp_src;
|
||||
flow->tp_dst = tcp->tcp_dst;
|
||||
packet->l7 = b.data;
|
||||
} else {
|
||||
/* Avoid tricking other code into thinking that this
|
||||
* packet has an L4 header. */
|
||||
flow->nw_proto = 0;
|
||||
}
|
||||
} else if (flow->nw_proto == IP_TYPE_UDP) {
|
||||
const struct udp_header *udp = pull_udp(&b);
|
||||
if (udp) {
|
||||
flow->tp_src = udp->udp_src;
|
||||
flow->tp_dst = udp->udp_dst;
|
||||
packet->l7 = b.data;
|
||||
} else {
|
||||
/* Avoid tricking other code into thinking that this
|
||||
* packet has an L4 header. */
|
||||
|
||||
@@ -30,6 +30,7 @@
|
||||
* advertising or publicity pertaining to the Software or any
|
||||
* derivatives without specific, written prior permission.
|
||||
*/
|
||||
#include <config.h>
|
||||
#include "hash.h"
|
||||
|
||||
uint32_t
|
||||
|
||||
+30
-9
@@ -31,6 +31,7 @@
|
||||
* derivatives without specific, written prior permission.
|
||||
*/
|
||||
|
||||
#include <config.h>
|
||||
#include "learning-switch.h"
|
||||
|
||||
#include <errno.h>
|
||||
@@ -53,7 +54,11 @@
|
||||
#include "vlog.h"
|
||||
|
||||
struct lswitch {
|
||||
bool setup_flows; /* Set up flows? (or controller processes all packets) */
|
||||
/* If nonnegative, the switch sets up flows that expire after the given
|
||||
* number of seconds (or never expire, if the value is OFP_FLOW_PERMANENT).
|
||||
* Otherwise, the switch processes every packet. */
|
||||
int max_idle;
|
||||
|
||||
uint64_t datapath_id;
|
||||
time_t last_features_request;
|
||||
struct mac_learning *ml; /* NULL to act as hub instead of switch. */
|
||||
@@ -63,24 +68,27 @@ static void queue_tx(struct lswitch *, struct rconn *, struct buffer *);
|
||||
static void send_features_request(struct lswitch *, struct rconn *);
|
||||
static void process_packet_in(struct lswitch *, struct rconn *,
|
||||
struct ofp_packet_in *);
|
||||
static void process_echo_request(struct lswitch *, struct rconn *,
|
||||
struct ofp_header *);
|
||||
|
||||
/* Creates and returns a new learning switch.
|
||||
*
|
||||
* If 'learn_macs' is true, the new switch will learn the ports on which MAC
|
||||
* addresses appear. Otherwise, the new switch will flood all packets.
|
||||
*
|
||||
* If 'setup_flows' is true, the new switch will set up flows. Otherwise, the
|
||||
* new switch will process every packet.
|
||||
* If 'max_idle' is nonnegative, the new switch will set up flows that expire
|
||||
* after the given number of seconds (or never expire, if 'max_idle' is
|
||||
* OFP_FLOW_PERMANENT). Otherwise, the new switch will process every packet.
|
||||
*
|
||||
* 'rconn' is used to send out an OpenFlow features request. */
|
||||
struct lswitch *
|
||||
lswitch_create(struct rconn *rconn, bool learn_macs, bool setup_flows)
|
||||
lswitch_create(struct rconn *rconn, bool learn_macs, int max_idle)
|
||||
{
|
||||
struct lswitch *sw = xmalloc(sizeof *sw);
|
||||
memset(sw, 0, sizeof *sw);
|
||||
sw->setup_flows = setup_flows;
|
||||
sw->max_idle = max_idle;
|
||||
sw->datapath_id = 0;
|
||||
sw->last_features_request = 0;
|
||||
sw->last_features_request = time(0) - 1;
|
||||
sw->ml = learn_macs ? mac_learning_create() : NULL;
|
||||
send_features_request(sw, rconn);
|
||||
return sw;
|
||||
@@ -119,7 +127,9 @@ lswitch_process_packet(struct lswitch *sw, struct rconn *rconn,
|
||||
return;
|
||||
}
|
||||
|
||||
if (oh->type == OFPT_FEATURES_REPLY) {
|
||||
if (oh->type == OFPT_ECHO_REQUEST) {
|
||||
process_echo_request(sw, rconn, msg->data);
|
||||
} else if (oh->type == OFPT_FEATURES_REPLY) {
|
||||
struct ofp_switch_features *osf = msg->data;
|
||||
sw->datapath_id = osf->datapath_id;
|
||||
} else if (sw->datapath_id == 0) {
|
||||
@@ -213,11 +223,15 @@ process_packet_in(struct lswitch *sw, struct rconn *rconn,
|
||||
out_port = mac_learning_lookup(sw->ml, flow.dl_dst);
|
||||
}
|
||||
|
||||
if (sw->setup_flows && (!sw->ml || out_port != OFPP_FLOOD)) {
|
||||
if (in_port == out_port) {
|
||||
/* The input port and output port match, so just drop the packet
|
||||
* by returning. */
|
||||
return;
|
||||
} else if (sw->max_idle >= 0 && (!sw->ml || out_port != OFPP_FLOOD)) {
|
||||
/* The output port is known, or we always flood everything, so add a
|
||||
* new flow. */
|
||||
queue_tx(sw, rconn, make_add_simple_flow(&flow, ntohl(opi->buffer_id),
|
||||
out_port));
|
||||
out_port, sw->max_idle));
|
||||
|
||||
/* If the switch didn't buffer the packet, we need to send a copy. */
|
||||
if (ntohl(opi->buffer_id) == UINT32_MAX) {
|
||||
@@ -237,3 +251,10 @@ process_packet_in(struct lswitch *sw, struct rconn *rconn,
|
||||
queue_tx(sw, rconn, b);
|
||||
}
|
||||
}
|
||||
|
||||
static void
|
||||
process_echo_request(struct lswitch *sw, struct rconn *rconn,
|
||||
struct ofp_header *rq)
|
||||
{
|
||||
queue_tx(sw, rconn, make_echo_reply(rq));
|
||||
}
|
||||
@@ -30,6 +30,7 @@
|
||||
* advertising or publicity pertaining to the Software or any
|
||||
* derivatives without specific, written prior permission.
|
||||
*/
|
||||
#include <config.h>
|
||||
#include "list.h"
|
||||
#include <assert.h>
|
||||
|
||||
|
||||
@@ -31,6 +31,7 @@
|
||||
* derivatives without specific, written prior permission.
|
||||
*/
|
||||
|
||||
#include <config.h>
|
||||
#include "mac-learning.h"
|
||||
|
||||
#include <assert.h>
|
||||
|
||||
+240
-90
@@ -31,6 +31,7 @@
|
||||
* derivatives without specific, written prior permission.
|
||||
*/
|
||||
|
||||
#include <config.h>
|
||||
#include "netdev.h"
|
||||
|
||||
#include <assert.h>
|
||||
@@ -64,6 +65,7 @@
|
||||
#include <net/if.h>
|
||||
#include <net/if_arp.h>
|
||||
#include <net/if_packet.h>
|
||||
#include <net/route.h>
|
||||
#include <netinet/in.h>
|
||||
#include <stdlib.h>
|
||||
#include <string.h>
|
||||
@@ -75,6 +77,7 @@
|
||||
#include "openflow.h"
|
||||
#include "packets.h"
|
||||
#include "poll-loop.h"
|
||||
#include "socket-util.h"
|
||||
|
||||
/* This doesn't seem to be defined in the linux/ethtool.h for linux 2.4 */
|
||||
#ifndef SPEED_2500
|
||||
@@ -88,6 +91,7 @@
|
||||
struct netdev {
|
||||
struct list node;
|
||||
char *name;
|
||||
int ifindex;
|
||||
int fd;
|
||||
uint8_t etheraddr[ETH_ADDR_LEN];
|
||||
int speed;
|
||||
@@ -95,11 +99,15 @@ struct netdev {
|
||||
uint32_t features;
|
||||
struct in_addr in4;
|
||||
struct in6_addr in6;
|
||||
int save_flags;
|
||||
int save_flags; /* Initial device flags. */
|
||||
int changed_flags; /* Flags that we changed. */
|
||||
};
|
||||
|
||||
static struct list netdev_list = LIST_INITIALIZER(&netdev_list);
|
||||
|
||||
/* An AF_INET socket (used for ioctl operations). */
|
||||
static int af_inet_sock = -1;
|
||||
|
||||
static void init_netdev(void);
|
||||
static int restore_flags(struct netdev *netdev);
|
||||
static int get_flags(const struct netdev *, int *flagsp);
|
||||
@@ -110,25 +118,17 @@ static int set_flags(struct netdev *, int flags);
|
||||
static bool
|
||||
get_ipv4_address(const char *name, struct in_addr *in4)
|
||||
{
|
||||
int sock;
|
||||
struct ifreq ifr;
|
||||
|
||||
sock = socket(AF_INET, SOCK_DGRAM, 0);
|
||||
if (sock < 0) {
|
||||
VLOG_WARN("socket(AF_INET): %s", strerror(errno));
|
||||
return false;
|
||||
}
|
||||
|
||||
strncpy(ifr.ifr_name, name, sizeof ifr.ifr_name);
|
||||
ifr.ifr_addr.sa_family = AF_INET;
|
||||
if (ioctl(sock, SIOCGIFADDR, &ifr) == 0) {
|
||||
if (ioctl(af_inet_sock, SIOCGIFADDR, &ifr) == 0) {
|
||||
struct sockaddr_in *sin = (struct sockaddr_in *) &ifr.ifr_addr;
|
||||
*in4 = sin->sin_addr;
|
||||
} else {
|
||||
in4->s_addr = INADDR_ANY;
|
||||
}
|
||||
|
||||
close(sock);
|
||||
return true;
|
||||
}
|
||||
|
||||
@@ -236,16 +236,19 @@ do_ethtool(struct netdev *netdev)
|
||||
|
||||
/* Opens the network device named 'name' (e.g. "eth0") and returns zero if
|
||||
* successful, otherwise a positive errno value. On success, sets '*netdev'
|
||||
* to the new network device, otherwise to null. */
|
||||
* to the new network device, otherwise to null.
|
||||
*
|
||||
* 'ethertype' may be a 16-bit Ethernet protocol value in host byte order to
|
||||
* capture frames of that type received on the device. It may also be one of
|
||||
* the 'enum netdev_pseudo_ethertype' values to receive frames in one of those
|
||||
* categories. */
|
||||
int
|
||||
netdev_open(const char *name, struct netdev **netdev_)
|
||||
netdev_open(const char *name, int ethertype, struct netdev **netdev_)
|
||||
{
|
||||
int fd;
|
||||
struct sockaddr sa;
|
||||
struct sockaddr_ll sll;
|
||||
struct ifreq ifr;
|
||||
unsigned int ifindex;
|
||||
socklen_t rcvbuf_len;
|
||||
size_t rcvbuf;
|
||||
uint8_t etheraddr[ETH_ADDR_LEN];
|
||||
struct in_addr in4;
|
||||
struct in6_addr in6;
|
||||
@@ -256,47 +259,16 @@ netdev_open(const char *name, struct netdev **netdev_)
|
||||
*netdev_ = NULL;
|
||||
init_netdev();
|
||||
|
||||
/* Create raw socket.
|
||||
*
|
||||
* We have to use SOCK_PACKET, despite its deprecation, because only
|
||||
* SOCK_PACKET lets us set the hardware source address of outgoing
|
||||
* packets. */
|
||||
fd = socket(PF_PACKET, SOCK_PACKET, htons(ETH_P_ALL));
|
||||
/* Create raw socket. */
|
||||
fd = socket(PF_PACKET, SOCK_RAW,
|
||||
htons(ethertype == NETDEV_ETH_TYPE_NONE ? 0
|
||||
: ethertype == NETDEV_ETH_TYPE_ANY ? ETH_P_ALL
|
||||
: ethertype == NETDEV_ETH_TYPE_802_2 ? ETH_P_802_2
|
||||
: ethertype));
|
||||
if (fd < 0) {
|
||||
return errno;
|
||||
}
|
||||
|
||||
/* Bind to specific ethernet device. */
|
||||
memset(&sa, 0, sizeof sa);
|
||||
sa.sa_family = AF_UNSPEC;
|
||||
strncpy((char *) sa.sa_data, name, sizeof sa.sa_data);
|
||||
if (bind(fd, &sa, sizeof sa) < 0) {
|
||||
VLOG_ERR("bind to %s failed: %s", name, strerror(errno));
|
||||
goto error;
|
||||
}
|
||||
|
||||
/* Between the socket() and bind() calls above, the socket receives all
|
||||
* packets on all system interfaces. We do not want to receive that
|
||||
* data, but there is no way to avoid it. So we must now drain out the
|
||||
* receive queue. There is no way to know how long the receive queue is,
|
||||
* but we know that the total number of bytes queued does not exceed the
|
||||
* receive buffer size, so we pull packets until none are left or we've
|
||||
* read that many bytes. */
|
||||
rcvbuf_len = sizeof rcvbuf;
|
||||
if (getsockopt(fd, SOL_SOCKET, SO_RCVBUF, &rcvbuf, &rcvbuf_len) < 0) {
|
||||
VLOG_ERR("getsockopt(SO_RCVBUF) on %s device failed: %s",
|
||||
name, strerror(errno));
|
||||
goto error;
|
||||
}
|
||||
while (rcvbuf > 0) {
|
||||
char buffer;
|
||||
ssize_t n_bytes = recv(fd, &buffer, 1, MSG_TRUNC | MSG_DONTWAIT);
|
||||
if (n_bytes <= 0) {
|
||||
break;
|
||||
}
|
||||
rcvbuf -= n_bytes;
|
||||
}
|
||||
|
||||
/* Get ethernet device index. */
|
||||
strncpy(ifr.ifr_name, name, sizeof ifr.ifr_name);
|
||||
if (ioctl(fd, SIOCGIFINDEX, &ifr) < 0) {
|
||||
@@ -306,6 +278,26 @@ netdev_open(const char *name, struct netdev **netdev_)
|
||||
}
|
||||
ifindex = ifr.ifr_ifindex;
|
||||
|
||||
/* Bind to specific ethernet device. */
|
||||
memset(&sll, 0, sizeof sll);
|
||||
sll.sll_family = AF_PACKET;
|
||||
sll.sll_ifindex = ifindex;
|
||||
if (bind(fd, (struct sockaddr *) &sll, sizeof sll) < 0) {
|
||||
VLOG_ERR("bind to %s failed: %s", name, strerror(errno));
|
||||
goto error;
|
||||
}
|
||||
|
||||
if (ethertype != NETDEV_ETH_TYPE_NONE) {
|
||||
/* Between the socket() and bind() calls above, the socket receives all
|
||||
* packets of the requested type on all system interfaces. We do not
|
||||
* want to receive that data, but there is no way to avoid it. So we
|
||||
* must now drain out the receive queue. */
|
||||
error = drain_rcvbuf(fd);
|
||||
if (error) {
|
||||
goto error;
|
||||
}
|
||||
}
|
||||
|
||||
/* Get MAC address. */
|
||||
if (ioctl(fd, SIOCGIFHWADDR, &ifr) < 0) {
|
||||
VLOG_ERR("ioctl(SIOCGIFHWADDR) on %s device failed: %s",
|
||||
@@ -335,6 +327,7 @@ netdev_open(const char *name, struct netdev **netdev_)
|
||||
/* Allocate network device. */
|
||||
netdev = xmalloc(sizeof *netdev);
|
||||
netdev->name = xstrdup(name);
|
||||
netdev->ifindex = ifindex;
|
||||
netdev->fd = fd;
|
||||
memcpy(netdev->etheraddr, etheraddr, sizeof etheraddr);
|
||||
netdev->mtu = mtu;
|
||||
@@ -349,6 +342,7 @@ netdev_open(const char *name, struct netdev **netdev_)
|
||||
if (error) {
|
||||
goto preset_error;
|
||||
}
|
||||
netdev->changed_flags = 0;
|
||||
fatal_signal_block();
|
||||
list_push_back(&netdev_list, &netdev->node);
|
||||
fatal_signal_unblock();
|
||||
@@ -450,37 +444,38 @@ netdev_recv_wait(struct netdev *netdev)
|
||||
poll_fd_wait(netdev->fd, POLLIN);
|
||||
}
|
||||
|
||||
/* Discards all packets waiting to be received from 'netdev'. */
|
||||
void
|
||||
netdev_drain(struct netdev *netdev)
|
||||
{
|
||||
drain_rcvbuf(netdev->fd);
|
||||
}
|
||||
|
||||
/* Sends 'buffer' on 'netdev'. Returns 0 if successful, otherwise a positive
|
||||
* errno value. Returns EAGAIN without blocking if the packet cannot be queued
|
||||
* immediately. Returns EMSGSIZE if a partial packet was transmitted or if
|
||||
* the packet is too big to transmit on the device.
|
||||
* the packet is too big or too small to transmit on the device.
|
||||
*
|
||||
* The caller retains ownership of 'buffer' in all cases.
|
||||
*
|
||||
* The kernel maintains a packet transmission queue, so the caller is not
|
||||
* expected to do additional queuing of packets. */
|
||||
int
|
||||
netdev_send(struct netdev *netdev, struct buffer *buffer)
|
||||
netdev_send(struct netdev *netdev, const struct buffer *buffer)
|
||||
{
|
||||
ssize_t n_bytes;
|
||||
const struct eth_header *eh;
|
||||
struct sockaddr_pkt spkt;
|
||||
|
||||
/* Ensure packet is long enough. (Although all incoming packets are at
|
||||
* least ETH_TOTAL_MIN bytes long, we could have trimmed some data off a
|
||||
* minimum-size packet, e.g. by dropping a vlan header.)
|
||||
*
|
||||
* The kernel does not require this, but it ensures that we always access
|
||||
* valid memory in grabbing the sockaddr below. */
|
||||
pad_to_minimum_length(buffer);
|
||||
|
||||
/* Construct packet sockaddr, which SOCK_PACKET requires. */
|
||||
spkt.spkt_family = AF_PACKET;
|
||||
strncpy((char *) spkt.spkt_device, netdev->name, sizeof spkt.spkt_device);
|
||||
/* Pull out the Ethernet header. */
|
||||
if (buffer->size < ETH_HEADER_LEN) {
|
||||
VLOG_WARN("cannot send %zu-byte frame on %s",
|
||||
buffer->size, netdev->name);
|
||||
return EMSGSIZE;
|
||||
}
|
||||
eh = buffer_at_assert(buffer, 0, sizeof *eh);
|
||||
spkt.spkt_protocol = eh->eth_type;
|
||||
|
||||
do {
|
||||
n_bytes = sendto(netdev->fd, buffer->data, buffer->size, 0,
|
||||
(const struct sockaddr *) &spkt, sizeof spkt);
|
||||
n_bytes = sendto(netdev->fd, buffer->data, buffer->size, 0, NULL, 0);
|
||||
} while (n_bytes < 0 && errno == EINTR);
|
||||
|
||||
if (n_bytes < 0) {
|
||||
@@ -557,22 +552,95 @@ netdev_get_features(const struct netdev *netdev)
|
||||
return netdev->features;
|
||||
}
|
||||
|
||||
/* If 'netdev' has an assigned IPv4 address, sets '*in4' to that address and
|
||||
* returns true. Otherwise, returns false. */
|
||||
/* If 'netdev' has an assigned IPv4 address, sets '*in4' to that address (if
|
||||
* 'in4' is non-null) and returns true. Otherwise, returns false. */
|
||||
bool
|
||||
netdev_get_in4(const struct netdev *netdev, struct in_addr *in4)
|
||||
{
|
||||
*in4 = netdev->in4;
|
||||
return in4->s_addr != INADDR_ANY;
|
||||
if (in4) {
|
||||
*in4 = netdev->in4;
|
||||
}
|
||||
return netdev->in4.s_addr != INADDR_ANY;
|
||||
}
|
||||
|
||||
/* If 'netdev' has an assigned IPv6 address, sets '*in6' to that address and
|
||||
* returns true. Otherwise, returns false. */
|
||||
static void
|
||||
make_in4_sockaddr(struct sockaddr *sa, struct in_addr addr)
|
||||
{
|
||||
struct sockaddr_in sin;
|
||||
memset(&sin, 0, sizeof sin);
|
||||
sin.sin_family = AF_INET;
|
||||
sin.sin_addr = addr;
|
||||
sin.sin_port = 0;
|
||||
|
||||
memset(sa, 0, sizeof *sa);
|
||||
memcpy(sa, &sin, sizeof sin);
|
||||
}
|
||||
|
||||
static int
|
||||
do_set_addr(struct netdev *netdev, int sock,
|
||||
int ioctl_nr, const char *ioctl_name, struct in_addr addr)
|
||||
{
|
||||
struct ifreq ifr;
|
||||
int error;
|
||||
|
||||
strncpy(ifr.ifr_name, netdev->name, sizeof ifr.ifr_name);
|
||||
make_in4_sockaddr(&ifr.ifr_addr, addr);
|
||||
error = ioctl(sock, ioctl_nr, &ifr) < 0 ? errno : 0;
|
||||
if (error) {
|
||||
VLOG_WARN("ioctl(%s): %s", ioctl_name, strerror(error));
|
||||
}
|
||||
return error;
|
||||
}
|
||||
|
||||
/* Assigns 'addr' as 'netdev''s IPv4 address and 'mask' as its netmask. If
|
||||
* 'addr' is INADDR_ANY, 'netdev''s IPv4 address is cleared. Returns a
|
||||
* positive errno value. */
|
||||
int
|
||||
netdev_set_in4(struct netdev *netdev, struct in_addr addr, struct in_addr mask)
|
||||
{
|
||||
int error;
|
||||
|
||||
error = do_set_addr(netdev, af_inet_sock,
|
||||
SIOCSIFADDR, "SIOCSIFADDR", addr);
|
||||
if (!error) {
|
||||
netdev->in4 = addr;
|
||||
if (addr.s_addr != INADDR_ANY) {
|
||||
error = do_set_addr(netdev, af_inet_sock,
|
||||
SIOCSIFNETMASK, "SIOCSIFNETMASK", mask);
|
||||
}
|
||||
}
|
||||
return error;
|
||||
}
|
||||
|
||||
/* Adds 'router' as a default gateway for 'netdev''s IP address. */
|
||||
int
|
||||
netdev_add_router(struct netdev *netdev, struct in_addr router)
|
||||
{
|
||||
struct in_addr any = { INADDR_ANY };
|
||||
struct rtentry rt;
|
||||
int error;
|
||||
|
||||
memset(&rt, 0, sizeof rt);
|
||||
make_in4_sockaddr(&rt.rt_dst, any);
|
||||
make_in4_sockaddr(&rt.rt_gateway, router);
|
||||
make_in4_sockaddr(&rt.rt_genmask, any);
|
||||
rt.rt_flags = RTF_UP | RTF_GATEWAY;
|
||||
error = ioctl(af_inet_sock, SIOCADDRT, &rt) < 0 ? errno : 0;
|
||||
if (error) {
|
||||
VLOG_WARN("ioctl(SIOCADDRT): %s", strerror(error));
|
||||
}
|
||||
return error;
|
||||
}
|
||||
|
||||
/* If 'netdev' has an assigned IPv6 address, sets '*in6' to that address (if
|
||||
* 'in6' is non-null) and returns true. Otherwise, returns false. */
|
||||
bool
|
||||
netdev_get_in6(const struct netdev *netdev, struct in6_addr *in6)
|
||||
{
|
||||
*in6 = netdev->in6;
|
||||
return memcmp(in6, &in6addr_any, sizeof *in6) != 0;
|
||||
if (in6) {
|
||||
*in6 = netdev->in6;
|
||||
}
|
||||
return memcmp(&netdev->in6, &in6addr_any, sizeof netdev->in6) != 0;
|
||||
}
|
||||
|
||||
/* Obtains the current flags for 'netdev' and stores them into '*flagsp'.
|
||||
@@ -597,10 +665,26 @@ netdev_get_flags(const struct netdev *netdev, enum netdev_flags *flagsp)
|
||||
return 0;
|
||||
}
|
||||
|
||||
/* Sets the flags for 'netdev' to 'nd_flags'.
|
||||
* Returns 0 if successful, otherwise a positive errno value. */
|
||||
int
|
||||
netdev_set_flags(struct netdev *netdev, enum netdev_flags nd_flags)
|
||||
static int
|
||||
nd_to_iff_flags(enum netdev_flags nd)
|
||||
{
|
||||
int iff = 0;
|
||||
if (nd & NETDEV_UP) {
|
||||
iff |= IFF_UP;
|
||||
}
|
||||
if (nd & NETDEV_PROMISC) {
|
||||
iff |= IFF_PROMISC;
|
||||
}
|
||||
return iff;
|
||||
}
|
||||
|
||||
/* On 'netdev', turns off the flags in 'off' and then turns on the flags in
|
||||
* 'on'. If 'permanent' is true, the changes will persist; otherwise, they
|
||||
* will be reverted when 'netdev' is closed or the program exits. Returns 0 if
|
||||
* successful, otherwise a positive errno value. */
|
||||
static int
|
||||
do_update_flags(struct netdev *netdev, enum netdev_flags off,
|
||||
enum netdev_flags on, bool permanent)
|
||||
{
|
||||
int old_flags, new_flags;
|
||||
int error;
|
||||
@@ -610,18 +694,78 @@ netdev_set_flags(struct netdev *netdev, enum netdev_flags nd_flags)
|
||||
return error;
|
||||
}
|
||||
|
||||
new_flags = old_flags & ~(IFF_UP | IFF_PROMISC);
|
||||
if (nd_flags & NETDEV_UP) {
|
||||
new_flags |= IFF_UP;
|
||||
}
|
||||
if (nd_flags & NETDEV_PROMISC) {
|
||||
new_flags |= IFF_PROMISC;
|
||||
new_flags = (old_flags & ~nd_to_iff_flags(off)) | nd_to_iff_flags(on);
|
||||
if (!permanent) {
|
||||
netdev->changed_flags |= new_flags ^ old_flags;
|
||||
}
|
||||
if (new_flags != old_flags) {
|
||||
error = set_flags(netdev, new_flags);
|
||||
}
|
||||
return error;
|
||||
}
|
||||
|
||||
/* Sets the flags for 'netdev' to 'flags'.
|
||||
* If 'permanent' is true, the changes will persist; otherwise, they
|
||||
* will be reverted when 'netdev' is closed or the program exits.
|
||||
* Returns 0 if successful, otherwise a positive errno value. */
|
||||
int
|
||||
netdev_set_flags(struct netdev *netdev, enum netdev_flags flags,
|
||||
bool permanent)
|
||||
{
|
||||
return do_update_flags(netdev, -1, flags, permanent);
|
||||
}
|
||||
|
||||
/* Turns on the specified 'flags' on 'netdev'.
|
||||
* If 'permanent' is true, the changes will persist; otherwise, they
|
||||
* will be reverted when 'netdev' is closed or the program exits.
|
||||
* Returns 0 if successful, otherwise a positive errno value. */
|
||||
int
|
||||
netdev_turn_flags_on(struct netdev *netdev, enum netdev_flags flags,
|
||||
bool permanent)
|
||||
{
|
||||
return do_update_flags(netdev, 0, flags, permanent);
|
||||
}
|
||||
|
||||
/* Turns off the specified 'flags' on 'netdev'.
|
||||
* If 'permanent' is true, the changes will persist; otherwise, they
|
||||
* will be reverted when 'netdev' is closed or the program exits.
|
||||
* Returns 0 if successful, otherwise a positive errno value. */
|
||||
int
|
||||
netdev_turn_flags_off(struct netdev *netdev, enum netdev_flags flags,
|
||||
bool permanent)
|
||||
{
|
||||
return do_update_flags(netdev, flags, 0, permanent);
|
||||
}
|
||||
|
||||
/* Looks up the ARP table entry for 'ip' on 'netdev'. If one exists and can be
|
||||
* successfully retrieved, it stores the corresponding MAC address in 'mac' and
|
||||
* returns 0. Otherwise, it returns a positive errno value; in particular,
|
||||
* ENXIO indicates that there is not ARP table entry for 'ip' on 'netdev'. */
|
||||
int
|
||||
netdev_arp_lookup(const struct netdev *netdev,
|
||||
uint32_t ip, uint8_t mac[ETH_ADDR_LEN])
|
||||
{
|
||||
struct arpreq r;
|
||||
struct sockaddr_in *pa;
|
||||
int retval;
|
||||
|
||||
memset(&r, 0, sizeof r);
|
||||
pa = (struct sockaddr_in *) &r.arp_pa;
|
||||
pa->sin_family = AF_INET;
|
||||
pa->sin_addr.s_addr = ip;
|
||||
pa->sin_port = 0;
|
||||
r.arp_ha.sa_family = ARPHRD_ETHER;
|
||||
r.arp_flags = 0;
|
||||
strncpy(r.arp_dev, netdev->name, sizeof r.arp_dev);
|
||||
retval = ioctl(af_inet_sock, SIOCGARP, &r) < 0 ? errno : 0;
|
||||
if (!retval) {
|
||||
memcpy(mac, r.arp_ha.sa_data, ETH_ADDR_LEN);
|
||||
} else if (retval != ENXIO) {
|
||||
VLOG_WARN("%s: could not look up ARP entry for "IP_FMT": %s",
|
||||
netdev->name, IP_ARGS(&ip), strerror(retval));
|
||||
}
|
||||
return retval;
|
||||
}
|
||||
|
||||
static void restore_all_flags(void *aux);
|
||||
|
||||
@@ -634,6 +778,10 @@ init_netdev(void)
|
||||
if (!inited) {
|
||||
inited = true;
|
||||
fatal_signal_add_hook(restore_all_flags, NULL);
|
||||
af_inet_sock = socket(AF_INET, SOCK_DGRAM, 0);
|
||||
if (af_inet_sock < 0) {
|
||||
fatal(errno, "socket(AF_INET)");
|
||||
}
|
||||
}
|
||||
}
|
||||
|
||||
@@ -646,6 +794,7 @@ static int
|
||||
restore_flags(struct netdev *netdev)
|
||||
{
|
||||
struct ifreq ifr;
|
||||
int restore_flags;
|
||||
|
||||
/* Get current flags. */
|
||||
strncpy(ifr.ifr_name, netdev->name, sizeof ifr.ifr_name);
|
||||
@@ -654,9 +803,10 @@ restore_flags(struct netdev *netdev)
|
||||
}
|
||||
|
||||
/* Restore flags that we might have changed, if necessary. */
|
||||
if ((ifr.ifr_flags ^ netdev->save_flags) & (IFF_PROMISC | IFF_UP)) {
|
||||
ifr.ifr_flags &= ~(IFF_PROMISC | IFF_UP);
|
||||
ifr.ifr_flags |= netdev->save_flags & (IFF_PROMISC | IFF_UP);
|
||||
restore_flags = netdev->changed_flags & (IFF_PROMISC | IFF_UP);
|
||||
if ((ifr.ifr_flags ^ netdev->save_flags) & restore_flags) {
|
||||
ifr.ifr_flags &= ~restore_flags;
|
||||
ifr.ifr_flags |= netdev->save_flags & restore_flags;
|
||||
if (ioctl(netdev->fd, SIOCSIFFLAGS, &ifr) < 0) {
|
||||
return errno;
|
||||
}
|
||||
|
||||
+3
-2
@@ -31,6 +31,7 @@
|
||||
* derivatives without specific, written prior permission.
|
||||
*/
|
||||
|
||||
#include <config.h>
|
||||
#include "netlink.h"
|
||||
#include <assert.h>
|
||||
#include <errno.h>
|
||||
@@ -470,12 +471,12 @@ nl_msg_nlmsgerr(const struct buffer *msg, int *errorp)
|
||||
}
|
||||
}
|
||||
|
||||
/* Ensures that 'b' has room for at least 'size' bytes plus netlink pading at
|
||||
/* Ensures that 'b' has room for at least 'size' bytes plus netlink padding at
|
||||
* its tail end, reallocating and copying its data if necessary. */
|
||||
void
|
||||
nl_msg_reserve(struct buffer *msg, size_t size)
|
||||
{
|
||||
buffer_reserve_tailroom(msg, NLMSG_ALIGN(size));
|
||||
buffer_prealloc_tailroom(msg, NLMSG_ALIGN(size));
|
||||
}
|
||||
|
||||
/* Puts a nlmsghdr at the beginning of 'msg', which must be initially empty.
|
||||
|
||||
+114
-34
@@ -31,6 +31,7 @@
|
||||
* derivatives without specific, written prior permission.
|
||||
*/
|
||||
|
||||
#include <config.h>
|
||||
#include "ofp-print.h"
|
||||
#include "xtoxll.h"
|
||||
|
||||
@@ -218,16 +219,62 @@ ofp_print_action(struct ds *string, const struct ofp_action *a)
|
||||
{
|
||||
switch (ntohs(a->type)) {
|
||||
case OFPAT_OUTPUT:
|
||||
ds_put_cstr(string, "output(");
|
||||
ofp_print_port_name(string, ntohs(a->arg.output.port));
|
||||
if (a->arg.output.port == htons(OFPP_CONTROLLER)) {
|
||||
ds_put_format(string, ", max %"PRIu16" bytes", ntohs(a->arg.output.max_len));
|
||||
{
|
||||
uint16_t port = ntohs(a->arg.output.port);
|
||||
if (port < OFPP_MAX) {
|
||||
ds_put_format(string, "output:%"PRIu16, port);
|
||||
} else {
|
||||
ofp_print_port_name(string, port);
|
||||
if (port == OFPP_CONTROLLER) {
|
||||
if (a->arg.output.max_len) {
|
||||
ds_put_format(string, ":%"PRIu16,
|
||||
ntohs(a->arg.output.max_len));
|
||||
} else {
|
||||
ds_put_cstr(string, ":all");
|
||||
}
|
||||
}
|
||||
}
|
||||
}
|
||||
ds_put_cstr(string, ")");
|
||||
break;
|
||||
|
||||
case OFPAT_SET_DL_VLAN:
|
||||
ds_put_cstr(string, "mod_vlan:");
|
||||
if (ntohs(a->arg.vlan_id) == OFP_VLAN_NONE) {
|
||||
ds_put_cstr(string, "strip");
|
||||
} else {
|
||||
ds_put_format(string, "%"PRIu16, ntohs(a->arg.vlan_id));
|
||||
}
|
||||
break;
|
||||
|
||||
case OFPAT_SET_DL_SRC:
|
||||
ds_put_format(string, "mod_dl_src:"ETH_ADDR_FMT,
|
||||
ETH_ADDR_ARGS(a->arg.dl_addr));
|
||||
break;
|
||||
|
||||
case OFPAT_SET_DL_DST:
|
||||
ds_put_format(string, "mod_dl_dst:"ETH_ADDR_FMT,
|
||||
ETH_ADDR_ARGS(a->arg.dl_addr));
|
||||
break;
|
||||
|
||||
case OFPAT_SET_NW_SRC:
|
||||
ds_put_format(string, "mod_nw_src:"IP_FMT, IP_ARGS(&a->arg.nw_addr));
|
||||
break;
|
||||
|
||||
case OFPAT_SET_NW_DST:
|
||||
ds_put_format(string, "mod_nw_dst:"IP_FMT, IP_ARGS(&a->arg.nw_addr));
|
||||
break;
|
||||
|
||||
case OFPAT_SET_TP_SRC:
|
||||
ds_put_format(string, "mod_tp_src:%d", ntohs(a->arg.tp));
|
||||
break;
|
||||
|
||||
case OFPAT_SET_TP_DST:
|
||||
ds_put_format(string, "mod_tp_dst:%d", ntohs(a->arg.tp));
|
||||
break;
|
||||
|
||||
default:
|
||||
ds_put_format(string, "(decoder %"PRIu16" not implemented)", ntohs(a->type));
|
||||
ds_put_format(string, "(decoder %"PRIu16" not implemented)",
|
||||
ntohs(a->type));
|
||||
break;
|
||||
}
|
||||
}
|
||||
@@ -237,21 +284,21 @@ static void ofp_print_actions(struct ds *string,
|
||||
size_t n_bytes)
|
||||
{
|
||||
size_t i;
|
||||
int n_actions = n_bytes / sizeof *actions;
|
||||
|
||||
ds_put_cstr(string, " actions[");
|
||||
for (i = 0; i < n_bytes / sizeof *actions; i++) {
|
||||
ds_put_format(string, "action%s=", n_actions == 1 ? "" : "s");
|
||||
for (i = 0; i < n_actions; i++) {
|
||||
if (i) {
|
||||
ds_put_cstr(string, "; ");
|
||||
ds_put_cstr(string, ",");
|
||||
}
|
||||
ofp_print_action(string, &actions[i]);
|
||||
}
|
||||
if (n_bytes % sizeof *actions) {
|
||||
if (i) {
|
||||
ds_put_cstr(string, "; ");
|
||||
ds_put_cstr(string, ",");
|
||||
}
|
||||
ds_put_cstr(string, "; ***trailing garbage***");
|
||||
ds_put_cstr(string, ", ***trailing garbage***");
|
||||
}
|
||||
ds_put_cstr(string, "]");
|
||||
}
|
||||
|
||||
/* Pretty-print the OFPT_PACKET_OUT packet of 'len' bytes at 'oh' to 'string'
|
||||
@@ -369,11 +416,15 @@ ofp_print_switch_config(struct ds *string, const void *oh, size_t len,
|
||||
}
|
||||
|
||||
static void print_wild(struct ds *string, const char *leader, int is_wild,
|
||||
const char *format, ...) __attribute__((format(printf, 4, 5)));
|
||||
int verbosity, const char *format, ...)
|
||||
__attribute__((format(printf, 5, 6)));
|
||||
|
||||
static void print_wild(struct ds *string, const char *leader, int is_wild,
|
||||
const char *format, ...)
|
||||
int verbosity, const char *format, ...)
|
||||
{
|
||||
if (is_wild && verbosity < 2) {
|
||||
return;
|
||||
}
|
||||
ds_put_cstr(string, leader);
|
||||
if (!is_wild) {
|
||||
va_list args;
|
||||
@@ -382,28 +433,36 @@ static void print_wild(struct ds *string, const char *leader, int is_wild,
|
||||
ds_put_format_valist(string, format, args);
|
||||
va_end(args);
|
||||
} else {
|
||||
ds_put_char(string, '?');
|
||||
ds_put_char(string, '*');
|
||||
}
|
||||
}
|
||||
|
||||
/* Pretty-print the ofp_match structure */
|
||||
static void ofp_print_match(struct ds *f, const struct ofp_match *om)
|
||||
static void ofp_print_match(struct ds *f, const struct ofp_match *om,
|
||||
int verbosity)
|
||||
{
|
||||
uint16_t w = ntohs(om->wildcards);
|
||||
|
||||
print_wild(f, " inport", w & OFPFW_IN_PORT, "%d", ntohs(om->in_port));
|
||||
print_wild(f, ":vlan", w & OFPFW_DL_VLAN, "%04x", ntohs(om->dl_vlan));
|
||||
print_wild(f, " mac[", w & OFPFW_DL_SRC,
|
||||
ETH_ADDR_FMT, ETH_ADDR_ARGS(om->dl_src));
|
||||
print_wild(f, "->", w & OFPFW_DL_DST,
|
||||
ETH_ADDR_FMT, ETH_ADDR_ARGS(om->dl_dst));
|
||||
print_wild(f, "] type", w & OFPFW_DL_TYPE, "%04x", ntohs(om->dl_type));
|
||||
print_wild(f, " ip[", w & OFPFW_NW_SRC, IP_FMT, IP_ARGS(&om->nw_src));
|
||||
print_wild(f, "->", w & OFPFW_NW_DST, IP_FMT, IP_ARGS(&om->nw_dst));
|
||||
print_wild(f, "] proto", w & OFPFW_NW_PROTO, "%u", om->nw_proto);
|
||||
print_wild(f, " tport[", w & OFPFW_TP_SRC, "%d", ntohs(om->tp_src));
|
||||
print_wild(f, "->", w & OFPFW_TP_DST, "%d", ntohs(om->tp_dst));
|
||||
ds_put_cstr(f, "]");
|
||||
print_wild(f, "in_port=", w & OFPFW_IN_PORT, verbosity,
|
||||
"%d,", ntohs(om->in_port));
|
||||
print_wild(f, "dl_vlan=", w & OFPFW_DL_VLAN, verbosity,
|
||||
"%04x,", ntohs(om->dl_vlan));
|
||||
print_wild(f, "dl_src=", w & OFPFW_DL_SRC, verbosity,
|
||||
ETH_ADDR_FMT",", ETH_ADDR_ARGS(om->dl_src));
|
||||
print_wild(f, "dl_dst=", w & OFPFW_DL_DST, verbosity,
|
||||
ETH_ADDR_FMT",", ETH_ADDR_ARGS(om->dl_dst));
|
||||
print_wild(f, "dl_type=", w & OFPFW_DL_TYPE, verbosity,
|
||||
"%04x,", ntohs(om->dl_type));
|
||||
print_wild(f, "nw_src=", w & OFPFW_NW_SRC, verbosity,
|
||||
IP_FMT",", IP_ARGS(&om->nw_src));
|
||||
print_wild(f, "nw_dst=", w & OFPFW_NW_DST, verbosity,
|
||||
IP_FMT",", IP_ARGS(&om->nw_dst));
|
||||
print_wild(f, "nw_proto=", w & OFPFW_NW_PROTO, verbosity,
|
||||
"%u,", om->nw_proto);
|
||||
print_wild(f, "tp_src=", w & OFPFW_TP_SRC, verbosity,
|
||||
"%d,", ntohs(om->tp_src));
|
||||
print_wild(f, "tp_dst=", w & OFPFW_TP_DST, verbosity,
|
||||
"%d,", ntohs(om->tp_dst));
|
||||
}
|
||||
|
||||
/* Pretty-print the OFPT_FLOW_MOD packet of 'len' bytes at 'oh' to 'string'
|
||||
@@ -414,7 +473,7 @@ ofp_print_flow_mod(struct ds *string, const void *oh, size_t len,
|
||||
{
|
||||
const struct ofp_flow_mod *ofm = oh;
|
||||
|
||||
ofp_print_match(string, &ofm->match);
|
||||
ofp_print_match(string, &ofm->match, verbosity);
|
||||
ds_put_format(string, " cmd:%d idle:%d pri:%d buf:%#x",
|
||||
ntohs(ofm->command), ntohs(ofm->max_idle),
|
||||
ofm->match.wildcards ? ntohs(ofm->priority) : (uint16_t)-1,
|
||||
@@ -432,7 +491,7 @@ ofp_print_flow_expired(struct ds *string, const void *oh, size_t len,
|
||||
{
|
||||
const struct ofp_flow_expired *ofe = oh;
|
||||
|
||||
ofp_print_match(string, &ofe->match);
|
||||
ofp_print_match(string, &ofe->match, verbosity);
|
||||
ds_put_format(string,
|
||||
" pri%"PRIu16" secs%"PRIu32" pkts%"PRIu64" bytes%"PRIu64"\n",
|
||||
ofe->match.wildcards ? ntohs(ofe->priority) : (uint16_t)-1,
|
||||
@@ -485,7 +544,7 @@ ofp_flow_stats_request(struct ds *string, const void *oh, size_t len,
|
||||
ds_put_format(string, " table_id=%"PRIu8", ", fsr->table_id);
|
||||
}
|
||||
|
||||
ofp_print_match(string, &fsr->match);
|
||||
ofp_print_match(string, &fsr->match, verbosity);
|
||||
}
|
||||
|
||||
static void
|
||||
@@ -535,7 +594,7 @@ ofp_flow_stats_reply(struct ds *string, const void *body_, size_t len,
|
||||
ntohll(fs->packet_count));
|
||||
ds_put_format(string, "n_bytes=%"PRIu64", ", ntohll(fs->byte_count));
|
||||
ds_put_format(string, "max_idle=%"PRIu16",", ntohs(fs->max_idle));
|
||||
ofp_print_match(string, &fs->match);
|
||||
ofp_print_match(string, &fs->match, verbosity);
|
||||
ofp_print_actions(string, fs->actions, length - sizeof *fs);
|
||||
ds_put_char(string, '\n');
|
||||
|
||||
@@ -555,7 +614,7 @@ ofp_aggregate_stats_request(struct ds *string, const void *oh, size_t len,
|
||||
ds_put_format(string, " table_id=%"PRIu8", ", asr->table_id);
|
||||
}
|
||||
|
||||
ofp_print_match(string, &asr->match);
|
||||
ofp_print_match(string, &asr->match, verbosity);
|
||||
}
|
||||
|
||||
static void
|
||||
@@ -722,6 +781,17 @@ ofp_stats_reply(struct ds *string, const void *oh, size_t len, int verbosity)
|
||||
verbosity, REPLY);
|
||||
}
|
||||
|
||||
static void
|
||||
ofp_echo(struct ds *string, const void *oh, size_t len, int verbosity)
|
||||
{
|
||||
const struct ofp_header *hdr = oh;
|
||||
|
||||
ds_put_format(string, " %zu bytes of payload\n", len - sizeof *hdr);
|
||||
if (verbosity > 1) {
|
||||
ds_put_hex_dump(string, hdr, len - sizeof *hdr, 0, true);
|
||||
}
|
||||
}
|
||||
|
||||
struct openflow_packet {
|
||||
const char *name;
|
||||
size_t min_size;
|
||||
@@ -799,6 +869,16 @@ static const struct openflow_packet packets[] = {
|
||||
sizeof (struct ofp_stats_reply),
|
||||
ofp_stats_reply,
|
||||
},
|
||||
[OFPT_ECHO_REQUEST] = {
|
||||
"echo_request",
|
||||
sizeof (struct ofp_header),
|
||||
ofp_echo,
|
||||
},
|
||||
[OFPT_ECHO_REPLY] = {
|
||||
"echo_reply",
|
||||
sizeof (struct ofp_header),
|
||||
ofp_echo,
|
||||
},
|
||||
};
|
||||
|
||||
/* Composes and returns a string representing the OpenFlow packet of 'len'
|
||||
|
||||
@@ -31,6 +31,7 @@
|
||||
* derivatives without specific, written prior permission.
|
||||
*/
|
||||
|
||||
#include <config.h>
|
||||
#include "poll-loop.h"
|
||||
#include <assert.h>
|
||||
#include <errno.h>
|
||||
|
||||
@@ -31,6 +31,7 @@
|
||||
* derivatives without specific, written prior permission.
|
||||
*/
|
||||
|
||||
#include <config.h>
|
||||
#include "queue.h"
|
||||
#include <assert.h>
|
||||
#include "buffer.h"
|
||||
|
||||
@@ -0,0 +1,79 @@
|
||||
/* 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 "random.h"
|
||||
|
||||
#include <errno.h>
|
||||
#include <stdlib.h>
|
||||
#include <sys/time.h>
|
||||
|
||||
#include "util.h"
|
||||
|
||||
void
|
||||
random_init(void)
|
||||
{
|
||||
static bool inited = false;
|
||||
if (!inited) {
|
||||
struct timeval tv;
|
||||
inited = true;
|
||||
if (gettimeofday(&tv, NULL) < 0) {
|
||||
fatal(errno, "gettimeofday");
|
||||
}
|
||||
srand(tv.tv_sec ^ tv.tv_usec);
|
||||
}
|
||||
}
|
||||
|
||||
void
|
||||
random_bytes(void *p_, size_t n)
|
||||
{
|
||||
uint8_t *p = p_;
|
||||
random_init();
|
||||
while (n--) {
|
||||
*p++ = rand();
|
||||
}
|
||||
}
|
||||
|
||||
uint32_t
|
||||
random_uint32(void)
|
||||
{
|
||||
uint32_t x;
|
||||
random_bytes(&x, sizeof x);
|
||||
return x;
|
||||
}
|
||||
|
||||
int
|
||||
random_range(int max)
|
||||
{
|
||||
return random_uint32() % max;
|
||||
}
|
||||
+442
-124
@@ -1,6 +1,6 @@
|
||||
/* 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
|
||||
@@ -13,10 +13,10 @@
|
||||
* 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
|
||||
@@ -25,67 +25,207 @@
|
||||
* 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 "rconn.h"
|
||||
#include <assert.h>
|
||||
#include <errno.h>
|
||||
#include <limits.h>
|
||||
#include <stdlib.h>
|
||||
#include <string.h>
|
||||
#include "buffer.h"
|
||||
#include "poll-loop.h"
|
||||
#include "ofp-print.h"
|
||||
#include "timeval.h"
|
||||
#include "util.h"
|
||||
#include "vconn.h"
|
||||
|
||||
#define THIS_MODULE VLM_rconn
|
||||
#include "vlog.h"
|
||||
|
||||
#define STATES \
|
||||
STATE(VOID, 1 << 0) \
|
||||
STATE(BACKOFF, 1 << 1) \
|
||||
STATE(CONNECTING, 1 << 2) \
|
||||
STATE(ACTIVE, 1 << 3) \
|
||||
STATE(IDLE, 1 << 4)
|
||||
enum state {
|
||||
#define STATE(NAME, VALUE) S_##NAME = VALUE,
|
||||
STATES
|
||||
#undef STATE
|
||||
};
|
||||
|
||||
static const char *
|
||||
state_name(enum state state)
|
||||
{
|
||||
switch (state) {
|
||||
#define STATE(NAME, VALUE) case S_##NAME: return #NAME;
|
||||
STATES
|
||||
#undef STATE
|
||||
}
|
||||
return "***ERROR***";
|
||||
}
|
||||
|
||||
/* A reliable connection to an OpenFlow switch or controller.
|
||||
*
|
||||
* See the large comment in rconn.h for more information. */
|
||||
struct rconn {
|
||||
bool reliable;
|
||||
char *name;
|
||||
enum state state;
|
||||
time_t state_entered;
|
||||
|
||||
struct vconn *vconn;
|
||||
bool connected;
|
||||
char *name;
|
||||
bool reliable;
|
||||
|
||||
struct queue txq;
|
||||
int txq_limit;
|
||||
time_t backoff_deadline;
|
||||
|
||||
int backoff;
|
||||
int max_backoff;
|
||||
time_t backoff_deadline;
|
||||
time_t last_received;
|
||||
time_t last_connected;
|
||||
|
||||
unsigned int packets_sent;
|
||||
|
||||
/* If we can't connect to the peer, it could be for any number of reasons.
|
||||
* Usually, one would assume it is because the peer is not running or
|
||||
* because the network is partitioned. But it could also be because the
|
||||
* network topology has changed, in which case the upper layer will need to
|
||||
* reassess it (in particular, obtain a new IP address via DHCP and find
|
||||
* the new location of the controller). We set this flag when we suspect
|
||||
* that this could be the case. */
|
||||
bool questionable_connectivity;
|
||||
time_t last_questioned;
|
||||
|
||||
/* Throughout this file, "probe" is shorthand for "inactivity probe".
|
||||
* When nothing has been received from the peer for a while, we send out
|
||||
* an echo request as an inactivity probe packet. We should receive back
|
||||
* a response. */
|
||||
int probe_interval; /* Secs of inactivity before sending probe. */
|
||||
};
|
||||
|
||||
static struct rconn *create_rconn(const char *name, int txq_limit,
|
||||
struct vconn *);
|
||||
static unsigned int sat_add(unsigned int x, unsigned int y);
|
||||
static unsigned int sat_mul(unsigned int x, unsigned int y);
|
||||
static unsigned int elapsed_in_this_state(const struct rconn *);
|
||||
static unsigned int timeout(const struct rconn *);
|
||||
static bool timed_out(const struct rconn *);
|
||||
static void state_transition(struct rconn *, enum state);
|
||||
static int try_send(struct rconn *);
|
||||
static int reconnect(struct rconn *);
|
||||
static void disconnect(struct rconn *, int error);
|
||||
static void question_connectivity(struct rconn *);
|
||||
|
||||
/* Creates and returns a new rconn that connects (and re-connects as necessary)
|
||||
* to the vconn named 'name'.
|
||||
*
|
||||
* 'txq_limit' is the maximum length of the send queue, in packets. */
|
||||
/* Creates a new rconn, connects it (reliably) to 'name', and returns it. */
|
||||
struct rconn *
|
||||
rconn_new(const char *name, int txq_limit)
|
||||
rconn_new(const char *name, int txq_limit, int inactivity_probe_interval,
|
||||
int max_backoff)
|
||||
{
|
||||
return create_rconn(name, txq_limit, NULL);
|
||||
struct rconn *rc = rconn_create(txq_limit, inactivity_probe_interval,
|
||||
max_backoff);
|
||||
rconn_connect(rc, name);
|
||||
return rc;
|
||||
}
|
||||
|
||||
/* Creates and returns a new rconn that is initially connected to 'vconn' and
|
||||
* has the given 'name'. The rconn will not re-connect after the connection
|
||||
* drops.
|
||||
*
|
||||
* 'txq_limit' is the maximum length of the send queue, in packets. */
|
||||
/* Creates a new rconn, connects it (unreliably) to 'vconn', and returns it. */
|
||||
struct rconn *
|
||||
rconn_new_from_vconn(const char *name, int txq_limit, struct vconn *vconn)
|
||||
rconn_new_from_vconn(const char *name, int txq_limit, struct vconn *vconn)
|
||||
{
|
||||
struct rconn *rc = rconn_create(txq_limit, 60, 0);
|
||||
rconn_connect_unreliably(rc, name, vconn);
|
||||
return rc;
|
||||
}
|
||||
|
||||
/* Creates and returns a new rconn.
|
||||
*
|
||||
* 'txq_limit' is the maximum length of the send queue, in packets.
|
||||
*
|
||||
* 'probe_interval' is a number of seconds. If the interval passes once
|
||||
* without an OpenFlow message being received from the peer, the rconn sends
|
||||
* out an "echo request" message. If the interval passes again without a
|
||||
* message being received, the rconn disconnects and re-connects to the peer.
|
||||
* Setting 'probe_interval' to 0 disables this behavior.
|
||||
*
|
||||
* 'max_backoff' is the maximum number of seconds between attempts to connect
|
||||
* to the peer. The actual interval starts at 1 second and doubles on each
|
||||
* failure until it reaches 'max_backoff'. If 0 is specified, the default of
|
||||
* 60 seconds is used. */
|
||||
struct rconn *
|
||||
rconn_create(int txq_limit, int probe_interval, int max_backoff)
|
||||
{
|
||||
struct rconn *rc = xcalloc(1, sizeof *rc);
|
||||
|
||||
rc->state = S_VOID;
|
||||
rc->state_entered = time(0);
|
||||
|
||||
rc->vconn = NULL;
|
||||
rc->name = xstrdup("void");
|
||||
rc->reliable = false;
|
||||
|
||||
queue_init(&rc->txq);
|
||||
assert(txq_limit > 0);
|
||||
rc->txq_limit = txq_limit;
|
||||
|
||||
rc->backoff = 0;
|
||||
rc->max_backoff = max_backoff ? max_backoff : 60;
|
||||
rc->backoff_deadline = TIME_MIN;
|
||||
rc->last_received = time(0);
|
||||
rc->last_connected = time(0);
|
||||
|
||||
rc->packets_sent = 0;
|
||||
|
||||
rc->questionable_connectivity = false;
|
||||
rc->last_questioned = time(0);
|
||||
|
||||
rc->probe_interval = probe_interval ? MAX(5, probe_interval) : 0;
|
||||
|
||||
return rc;
|
||||
}
|
||||
|
||||
int
|
||||
rconn_connect(struct rconn *rc, const char *name)
|
||||
{
|
||||
rconn_disconnect(rc);
|
||||
free(rc->name);
|
||||
rc->name = xstrdup(name);
|
||||
rc->reliable = true;
|
||||
return reconnect(rc);
|
||||
}
|
||||
|
||||
void
|
||||
rconn_connect_unreliably(struct rconn *rc,
|
||||
const char *name, struct vconn *vconn)
|
||||
{
|
||||
assert(vconn != NULL);
|
||||
return create_rconn(name, txq_limit, vconn);
|
||||
rconn_disconnect(rc);
|
||||
free(rc->name);
|
||||
rc->name = xstrdup(name);
|
||||
rc->reliable = false;
|
||||
rc->vconn = vconn;
|
||||
rc->last_connected = time(0);
|
||||
state_transition(rc, S_ACTIVE);
|
||||
}
|
||||
|
||||
void
|
||||
rconn_disconnect(struct rconn *rc)
|
||||
{
|
||||
if (rc->vconn) {
|
||||
vconn_close(rc->vconn);
|
||||
rc->vconn = NULL;
|
||||
}
|
||||
free(rc->name);
|
||||
rc->name = xstrdup("void");
|
||||
rc->reliable = false;
|
||||
|
||||
rc->backoff = 0;
|
||||
rc->backoff_deadline = TIME_MIN;
|
||||
|
||||
state_transition(rc, S_VOID);
|
||||
}
|
||||
|
||||
/* Disconnects 'rc' and frees the underlying storage. */
|
||||
@@ -100,63 +240,166 @@ rconn_destroy(struct rconn *rc)
|
||||
}
|
||||
}
|
||||
|
||||
static unsigned int
|
||||
timeout_VOID(const struct rconn *rc)
|
||||
{
|
||||
return UINT_MAX;
|
||||
}
|
||||
|
||||
static void
|
||||
run_VOID(struct rconn *rc)
|
||||
{
|
||||
/* Nothing to do. */
|
||||
}
|
||||
|
||||
static int
|
||||
reconnect(struct rconn *rc)
|
||||
{
|
||||
int retval;
|
||||
|
||||
VLOG_WARN("%s: connecting...", rc->name);
|
||||
retval = vconn_open(rc->name, &rc->vconn);
|
||||
if (!retval) {
|
||||
rc->backoff_deadline = time(0) + rc->backoff;
|
||||
state_transition(rc, S_CONNECTING);
|
||||
} else {
|
||||
VLOG_WARN("%s: connection failed (%s)", rc->name, strerror(retval));
|
||||
disconnect(rc, 0);
|
||||
}
|
||||
return retval;
|
||||
}
|
||||
|
||||
static unsigned int
|
||||
timeout_BACKOFF(const struct rconn *rc)
|
||||
{
|
||||
return rc->backoff;
|
||||
}
|
||||
|
||||
static void
|
||||
run_BACKOFF(struct rconn *rc)
|
||||
{
|
||||
if (timed_out(rc)) {
|
||||
reconnect(rc);
|
||||
}
|
||||
}
|
||||
|
||||
static unsigned int
|
||||
timeout_CONNECTING(const struct rconn *rc)
|
||||
{
|
||||
return MAX(1, rc->backoff);
|
||||
}
|
||||
|
||||
static void
|
||||
run_CONNECTING(struct rconn *rc)
|
||||
{
|
||||
int retval = vconn_connect(rc->vconn);
|
||||
if (!retval) {
|
||||
VLOG_WARN("%s: connected", rc->name);
|
||||
if (vconn_is_passive(rc->vconn)) {
|
||||
error(0, "%s: passive vconn not supported", rc->name);
|
||||
state_transition(rc, S_VOID);
|
||||
} else {
|
||||
state_transition(rc, S_ACTIVE);
|
||||
rc->last_connected = rc->state_entered;
|
||||
}
|
||||
} else if (retval != EAGAIN) {
|
||||
VLOG_WARN("%s: connection failed (%s)", rc->name, strerror(retval));
|
||||
disconnect(rc, retval);
|
||||
} else if (timed_out(rc)) {
|
||||
VLOG_WARN("%s: connection timed out", rc->name);
|
||||
rc->backoff_deadline = TIME_MAX; /* Prevent resetting backoff. */
|
||||
disconnect(rc, 0);
|
||||
}
|
||||
}
|
||||
|
||||
static void
|
||||
do_tx_work(struct rconn *rc)
|
||||
{
|
||||
while (rc->txq.n > 0) {
|
||||
int error = try_send(rc);
|
||||
if (error) {
|
||||
break;
|
||||
}
|
||||
}
|
||||
}
|
||||
|
||||
static unsigned int
|
||||
timeout_ACTIVE(const struct rconn *rc)
|
||||
{
|
||||
if (rc->probe_interval) {
|
||||
unsigned int base = MAX(rc->last_received, rc->state_entered);
|
||||
unsigned int arg = base + rc->probe_interval - rc->state_entered;
|
||||
return arg;
|
||||
}
|
||||
return UINT_MAX;
|
||||
}
|
||||
|
||||
static void
|
||||
run_ACTIVE(struct rconn *rc)
|
||||
{
|
||||
if (timed_out(rc)) {
|
||||
unsigned int base = MAX(rc->last_received, rc->state_entered);
|
||||
queue_push_tail(&rc->txq, make_echo_request());
|
||||
VLOG_DBG("%s: idle %u seconds, sending inactivity probe",
|
||||
rc->name, (unsigned int) (time(0) - base));
|
||||
state_transition(rc, S_IDLE);
|
||||
return;
|
||||
}
|
||||
|
||||
do_tx_work(rc);
|
||||
}
|
||||
|
||||
static unsigned int
|
||||
timeout_IDLE(const struct rconn *rc)
|
||||
{
|
||||
return rc->probe_interval;
|
||||
}
|
||||
|
||||
static void
|
||||
run_IDLE(struct rconn *rc)
|
||||
{
|
||||
if (timed_out(rc)) {
|
||||
question_connectivity(rc);
|
||||
VLOG_ERR("%s: no response to inactivity probe after %u "
|
||||
"seconds, disconnecting",
|
||||
rc->name, elapsed_in_this_state(rc));
|
||||
disconnect(rc, 0);
|
||||
} else {
|
||||
do_tx_work(rc);
|
||||
}
|
||||
}
|
||||
|
||||
/* Performs whatever activities are necessary to maintain 'rc': if 'rc' is
|
||||
* disconnected, attempts to (re)connect, backing off as necessary; if 'rc' is
|
||||
* connected, attempts to send packets in the send queue, if any. */
|
||||
void
|
||||
rconn_run(struct rconn *rc)
|
||||
{
|
||||
if (!rc->vconn) {
|
||||
if (rc->reliable && time(0) >= rc->backoff_deadline) {
|
||||
int retval;
|
||||
|
||||
retval = vconn_open(rc->name, &rc->vconn);
|
||||
if (!retval) {
|
||||
rc->backoff_deadline = time(0) + rc->backoff;
|
||||
rc->connected = false;
|
||||
} else {
|
||||
VLOG_WARN("%s: connection failed (%s)",
|
||||
rc->name, strerror(retval));
|
||||
disconnect(rc, 0);
|
||||
}
|
||||
int old_state;
|
||||
do {
|
||||
old_state = rc->state;
|
||||
switch (rc->state) {
|
||||
#define STATE(NAME, VALUE) case S_##NAME: run_##NAME(rc); break;
|
||||
STATES
|
||||
#undef STATE
|
||||
default:
|
||||
NOT_REACHED();
|
||||
}
|
||||
} else if (!rc->connected) {
|
||||
int error = vconn_connect(rc->vconn);
|
||||
if (!error) {
|
||||
VLOG_WARN("%s: connected", rc->name);
|
||||
if (vconn_is_passive(rc->vconn)) {
|
||||
fatal(0, "%s: passive vconn not supported in switch",
|
||||
rc->name);
|
||||
}
|
||||
rc->connected = true;
|
||||
} else if (error != EAGAIN) {
|
||||
VLOG_WARN("%s: connection failed (%s)", rc->name, strerror(error));
|
||||
disconnect(rc, 0);
|
||||
}
|
||||
} else {
|
||||
while (rc->txq.n > 0) {
|
||||
int error = try_send(rc);
|
||||
if (error == EAGAIN) {
|
||||
break;
|
||||
} else if (error) {
|
||||
disconnect(rc, error);
|
||||
return;
|
||||
}
|
||||
}
|
||||
}
|
||||
} while (rc->state != old_state);
|
||||
}
|
||||
|
||||
/* Causes the next call to poll_block() to wake up when rconn_run() should be
|
||||
* called on 'rc'. */
|
||||
void
|
||||
rconn_run_wait(struct rconn *rc)
|
||||
rconn_run_wait(struct rconn *rc)
|
||||
{
|
||||
if (rc->vconn) {
|
||||
if (rc->txq.n) {
|
||||
vconn_wait(rc->vconn, WAIT_SEND);
|
||||
}
|
||||
} else {
|
||||
poll_timer_wait((rc->backoff_deadline - time(0)) * 1000);
|
||||
unsigned int timeo = timeout(rc);
|
||||
if (timeo != UINT_MAX) {
|
||||
poll_timer_wait(sat_mul(timeo, 1000));
|
||||
}
|
||||
|
||||
if ((rc->state & (S_ACTIVE | S_IDLE)) && rc->txq.n) {
|
||||
vconn_wait(rc->vconn, WAIT_SEND);
|
||||
}
|
||||
}
|
||||
|
||||
@@ -166,14 +409,17 @@ rconn_run_wait(struct rconn *rc)
|
||||
struct buffer *
|
||||
rconn_recv(struct rconn *rc)
|
||||
{
|
||||
if (rc->vconn && rc->connected) {
|
||||
if (rc->state & (S_ACTIVE | S_IDLE)) {
|
||||
struct buffer *buffer;
|
||||
int error = vconn_recv(rc->vconn, &buffer);
|
||||
if (!error) {
|
||||
rc->last_connected = time(0);
|
||||
rc->last_received = time(0);
|
||||
if (rc->state == S_IDLE) {
|
||||
state_transition(rc, S_ACTIVE);
|
||||
}
|
||||
return buffer;
|
||||
} else if (error != EAGAIN) {
|
||||
disconnect(rc, error);
|
||||
disconnect(rc, error);
|
||||
}
|
||||
}
|
||||
return NULL;
|
||||
@@ -182,7 +428,7 @@ rconn_recv(struct rconn *rc)
|
||||
/* Causes the next call to poll_block() to wake up when a packet may be ready
|
||||
* to be received by vconn_recv() on 'rc'. */
|
||||
void
|
||||
rconn_recv_wait(struct rconn *rc)
|
||||
rconn_recv_wait(struct rconn *rc)
|
||||
{
|
||||
if (rc->vconn) {
|
||||
vconn_wait(rc->vconn, WAIT_RECV);
|
||||
@@ -213,7 +459,7 @@ do_send(struct rconn *rc, struct buffer *b, int txq_limit)
|
||||
* full, or ENOTCONN if 'rc' is not currently connected.
|
||||
*
|
||||
* There is no rconn_send_wait() function: an rconn has a send queue that it
|
||||
* takes care of sending if you call rconn_wait(), which will have the side
|
||||
* takes care of sending if you call rconn_run(), which will have the side
|
||||
* effect of waking up poll_block(). */
|
||||
int
|
||||
rconn_send(struct rconn *rc, struct buffer *b)
|
||||
@@ -235,7 +481,7 @@ rconn_force_send(struct rconn *rc, struct buffer *b)
|
||||
/* Returns true if 'rc''s send buffer is full,
|
||||
* false if it has room for at least one more packet. */
|
||||
bool
|
||||
rconn_is_full(const struct rconn *rc)
|
||||
rconn_is_full(const struct rconn *rc)
|
||||
{
|
||||
return rc->txq.n >= rc->txq_limit;
|
||||
}
|
||||
@@ -244,59 +490,65 @@ rconn_is_full(const struct rconn *rc)
|
||||
* vconn. A packet is not counted as sent while it is still queued in the
|
||||
* rconn, only when it has been successfuly passed to the vconn. */
|
||||
unsigned int
|
||||
rconn_packets_sent(const struct rconn *rc)
|
||||
rconn_packets_sent(const struct rconn *rc)
|
||||
{
|
||||
return rc->packets_sent;
|
||||
}
|
||||
|
||||
/* Returns 'rc''s name (the 'name' argument passed to rconn_new()). */
|
||||
const char *
|
||||
rconn_get_name(const struct rconn *rc)
|
||||
rconn_get_name(const struct rconn *rc)
|
||||
{
|
||||
return rc->name;
|
||||
}
|
||||
|
||||
/* Returns true if 'rconn' is connected or in the process of reconnecting,
|
||||
* false if 'rconn' is disconnected and will not be reconnected. */
|
||||
* false if 'rconn' is disconnected and will not reconnect on its own. */
|
||||
bool
|
||||
rconn_is_alive(const struct rconn *rconn)
|
||||
rconn_is_alive(const struct rconn *rconn)
|
||||
{
|
||||
return rconn->reliable || rconn->vconn;
|
||||
return rconn->state != S_VOID;
|
||||
}
|
||||
|
||||
/* Returns true if 'rconn' is connected, false otherwise. */
|
||||
bool
|
||||
rconn_is_connected(const struct rconn *rconn)
|
||||
{
|
||||
return rconn->vconn && !vconn_connect(rconn->vconn);
|
||||
return rconn->state & (S_ACTIVE | S_IDLE);
|
||||
}
|
||||
|
||||
/* Returns 0 if 'rconn' is connected, otherwise the number of seconds that it
|
||||
* has been disconnected. */
|
||||
int
|
||||
rconn_disconnected_duration(const struct rconn *rconn)
|
||||
rconn_disconnected_duration(const struct rconn *rconn)
|
||||
{
|
||||
return rconn_is_connected(rconn) ? 0 : time(0) - rconn->last_connected;
|
||||
}
|
||||
|
||||
static struct rconn *
|
||||
create_rconn(const char *name, int txq_limit, struct vconn *vconn)
|
||||
{
|
||||
struct rconn *rc = xmalloc(sizeof *rc);
|
||||
assert(txq_limit > 0);
|
||||
rc->reliable = vconn == NULL;
|
||||
rc->connected = vconn != NULL;
|
||||
rc->name = xstrdup(name);
|
||||
rc->vconn = vconn;
|
||||
queue_init(&rc->txq);
|
||||
rc->txq_limit = txq_limit;
|
||||
rc->backoff_deadline = 0;
|
||||
rc->backoff = 0;
|
||||
rc->last_connected = time(0);
|
||||
rc->packets_sent = 0;
|
||||
return rc;
|
||||
return rconn_is_connected(rconn) ? 0 : time(0) - rconn->last_received;
|
||||
}
|
||||
|
||||
/* Returns the IP address of the peer, or 0 if the peer is not connected over
|
||||
* an IP-based protocol or if its IP address is not known. */
|
||||
uint32_t
|
||||
rconn_get_ip(const struct rconn *rconn)
|
||||
{
|
||||
return rconn->vconn ? vconn_get_ip(rconn->vconn) : 0;
|
||||
}
|
||||
|
||||
/* If 'rconn' can't connect to the peer, it could be for any number of reasons.
|
||||
* Usually, one would assume it is because the peer is not running or because
|
||||
* the network is partitioned. But it could also be because the network
|
||||
* topology has changed, in which case the upper layer will need to reassess it
|
||||
* (in particular, obtain a new IP address via DHCP and find the new location
|
||||
* of the controller). When this appears that this might be the case, this
|
||||
* function returns true. It also clears the questionability flag and prevents
|
||||
* it from being set again for some time. */
|
||||
bool
|
||||
rconn_is_connectivity_questionable(struct rconn *rconn)
|
||||
{
|
||||
bool questionable = rconn->questionable_connectivity;
|
||||
rconn->questionable_connectivity = false;
|
||||
return questionable;
|
||||
}
|
||||
|
||||
/* Tries to send a packet from 'rc''s send buffer. Returns 0 if successful,
|
||||
* otherwise a positive errno value. */
|
||||
static int
|
||||
@@ -306,6 +558,9 @@ try_send(struct rconn *rc)
|
||||
struct buffer *next = rc->txq.head->next;
|
||||
retval = vconn_send(rc->vconn, rc->txq.head);
|
||||
if (retval) {
|
||||
if (retval != EAGAIN) {
|
||||
disconnect(rc, retval);
|
||||
}
|
||||
return retval;
|
||||
}
|
||||
rc->packets_sent++;
|
||||
@@ -317,32 +572,95 @@ try_send(struct rconn *rc)
|
||||
* nonzero, then it should be EOF to indicate the connection was closed by the
|
||||
* peer in a normal fashion or a positive errno value. */
|
||||
static void
|
||||
disconnect(struct rconn *rc, int error)
|
||||
disconnect(struct rconn *rc, int error)
|
||||
{
|
||||
if (rc->reliable) {
|
||||
time_t now = time(0);
|
||||
|
||||
if (rc->state & (S_CONNECTING | S_ACTIVE | S_IDLE)) {
|
||||
if (error > 0) {
|
||||
VLOG_WARN("%s: connection dropped (%s)",
|
||||
rc->name, strerror(error));
|
||||
} else if (error == EOF) {
|
||||
if (rc->reliable) {
|
||||
VLOG_WARN("%s: connection closed", rc->name);
|
||||
}
|
||||
} else {
|
||||
VLOG_WARN("%s: connection dropped", rc->name);
|
||||
}
|
||||
vconn_close(rc->vconn);
|
||||
rc->vconn = NULL;
|
||||
queue_clear(&rc->txq);
|
||||
}
|
||||
|
||||
if (now >= rc->backoff_deadline) {
|
||||
rc->backoff = 1;
|
||||
} else {
|
||||
rc->backoff = MIN(rc->max_backoff, MAX(1, 2 * rc->backoff));
|
||||
VLOG_WARN("%s: waiting %d seconds before reconnect\n",
|
||||
rc->name, rc->backoff);
|
||||
}
|
||||
rc->backoff_deadline = now + rc->backoff;
|
||||
state_transition(rc, S_BACKOFF);
|
||||
if (now - rc->last_connected > 60) {
|
||||
question_connectivity(rc);
|
||||
}
|
||||
} else {
|
||||
rconn_disconnect(rc);
|
||||
}
|
||||
}
|
||||
|
||||
static unsigned int
|
||||
elapsed_in_this_state(const struct rconn *rc)
|
||||
{
|
||||
return time(0) - rc->state_entered;
|
||||
}
|
||||
|
||||
static unsigned int
|
||||
timeout(const struct rconn *rc)
|
||||
{
|
||||
switch (rc->state) {
|
||||
#define STATE(NAME, VALUE) case S_##NAME: return timeout_##NAME(rc);
|
||||
STATES
|
||||
#undef STATE
|
||||
default:
|
||||
NOT_REACHED();
|
||||
}
|
||||
}
|
||||
|
||||
static bool
|
||||
timed_out(const struct rconn *rc)
|
||||
{
|
||||
return time(0) >= sat_add(rc->state_entered, timeout(rc));
|
||||
}
|
||||
|
||||
static void
|
||||
state_transition(struct rconn *rc, enum state state)
|
||||
{
|
||||
VLOG_DBG("%s: entering %s", rc->name, state_name(state));
|
||||
rc->state = state;
|
||||
rc->state_entered = time(0);
|
||||
}
|
||||
|
||||
static unsigned int
|
||||
sat_add(unsigned int x, unsigned int y)
|
||||
{
|
||||
return x + y >= x ? x + y : UINT_MAX;
|
||||
}
|
||||
|
||||
static unsigned int
|
||||
sat_mul(unsigned int x, unsigned int y)
|
||||
{
|
||||
assert(y);
|
||||
return x <= UINT_MAX / y ? x * y : UINT_MAX;
|
||||
}
|
||||
|
||||
static void
|
||||
question_connectivity(struct rconn *rc)
|
||||
{
|
||||
time_t now = time(0);
|
||||
|
||||
if (rc->vconn) {
|
||||
if (error > 0) {
|
||||
VLOG_WARN("%s: connection dropped (%s)",
|
||||
rc->name, strerror(error));
|
||||
} else if (error == EOF) {
|
||||
if (rc->reliable) {
|
||||
VLOG_WARN("%s: connection closed", rc->name);
|
||||
}
|
||||
} else {
|
||||
VLOG_WARN("%s: connection dropped", rc->name);
|
||||
}
|
||||
vconn_close(rc->vconn);
|
||||
rc->vconn = NULL;
|
||||
queue_clear(&rc->txq);
|
||||
if (now - rc->last_questioned > 60) {
|
||||
rc->questionable_connectivity = true;
|
||||
rc->last_questioned = now;
|
||||
}
|
||||
|
||||
if (now >= rc->backoff_deadline) {
|
||||
rc->backoff = 1;
|
||||
} else {
|
||||
rc->backoff = MIN(60, MAX(1, 2 * rc->backoff));
|
||||
VLOG_WARN("%s: waiting %d seconds before reconnect\n",
|
||||
rc->name, rc->backoff);
|
||||
}
|
||||
rc->backoff_deadline = now + rc->backoff;
|
||||
}
|
||||
@@ -31,14 +31,20 @@
|
||||
* derivatives without specific, written prior permission.
|
||||
*/
|
||||
|
||||
#include <config.h>
|
||||
#include "socket-util.h"
|
||||
#include <arpa/inet.h>
|
||||
#include <errno.h>
|
||||
#include <fcntl.h>
|
||||
#include <netdb.h>
|
||||
#include <poll.h>
|
||||
#include <stddef.h>
|
||||
#include <stdio.h>
|
||||
#include <string.h>
|
||||
#include <sys/un.h>
|
||||
#include <unistd.h>
|
||||
#include "fatal-signal.h"
|
||||
#include "util.h"
|
||||
|
||||
#include "vlog.h"
|
||||
#define THIS_MODULE VLM_socket_util
|
||||
@@ -118,3 +124,129 @@ check_connection_completion(int fd)
|
||||
return EAGAIN;
|
||||
}
|
||||
}
|
||||
|
||||
/* Drain all the data currently in the receive queue of a datagram socket (and
|
||||
* possibly additional data). There is no way to know how many packets are in
|
||||
* the receive queue, but we do know that the total number of bytes queued does
|
||||
* not exceed the receive buffer size, so we pull packets until none are left
|
||||
* or we've read that many bytes. */
|
||||
int
|
||||
drain_rcvbuf(int fd)
|
||||
{
|
||||
socklen_t rcvbuf_len;
|
||||
size_t rcvbuf;
|
||||
|
||||
rcvbuf_len = sizeof rcvbuf;
|
||||
if (getsockopt(fd, SOL_SOCKET, SO_RCVBUF, &rcvbuf, &rcvbuf_len) < 0) {
|
||||
VLOG_ERR("getsockopt(SO_RCVBUF) failed: %s", strerror(errno));
|
||||
return errno;
|
||||
}
|
||||
while (rcvbuf > 0) {
|
||||
/* In Linux, specifying MSG_TRUNC in the flags argument causes the
|
||||
* datagram length to be returned, even if that is longer than the
|
||||
* buffer provided. Thus, we can use a 1-byte buffer to discard the
|
||||
* incoming datagram and still be able to account how many bytes were
|
||||
* removed from the receive buffer.
|
||||
*
|
||||
* On other Unix-like OSes, MSG_TRUNC has no effect in the flags
|
||||
* argument. */
|
||||
#ifdef __linux__
|
||||
#define BUFFER_SIZE 1
|
||||
#else
|
||||
#define BUFFER_SIZE 2048
|
||||
#endif
|
||||
char buffer[BUFFER_SIZE];
|
||||
ssize_t n_bytes = recv(fd, buffer, sizeof buffer,
|
||||
MSG_TRUNC | MSG_DONTWAIT);
|
||||
if (n_bytes <= 0 || n_bytes >= rcvbuf) {
|
||||
break;
|
||||
}
|
||||
rcvbuf -= n_bytes;
|
||||
}
|
||||
return 0;
|
||||
}
|
||||
|
||||
/* Stores in '*un' a sockaddr_un that refers to file 'name'. Stores in
|
||||
* '*un_len' the size of the sockaddr_un. */
|
||||
static void
|
||||
make_sockaddr_un(const char *name, struct sockaddr_un* un, socklen_t *un_len)
|
||||
{
|
||||
un->sun_family = AF_UNIX;
|
||||
strncpy(un->sun_path, name, sizeof un->sun_path);
|
||||
un->sun_path[sizeof un->sun_path - 1] = '\0';
|
||||
*un_len = (offsetof(struct sockaddr_un, sun_path)
|
||||
+ strlen (un->sun_path) + 1);
|
||||
}
|
||||
|
||||
/* Creates a Unix domain socket in the given 'style' (either SOCK_DGRAM or
|
||||
* SOCK_STREAM) that is bound to '*bind_path' (if 'bind_path' is non-null) and
|
||||
* connected to '*connect_path' (if 'connect_path' is non-null). If 'nonblock'
|
||||
* is true, the socket is made non-blocking. If 'passcred' is true, the socket
|
||||
* is configured to receive SCM_CREDENTIALS control messages.
|
||||
*
|
||||
* Returns the socket's fd if successful, otherwise a negative errno value. */
|
||||
int
|
||||
make_unix_socket(int style, bool nonblock, bool passcred UNUSED,
|
||||
const char *bind_path, const char *connect_path)
|
||||
{
|
||||
int error;
|
||||
int fd;
|
||||
|
||||
fd = socket(PF_UNIX, style, 0);
|
||||
if (fd < 0) {
|
||||
return -errno;
|
||||
}
|
||||
|
||||
if (nonblock) {
|
||||
int flags = fcntl(fd, F_GETFL, 0);
|
||||
if (flags == -1) {
|
||||
goto error;
|
||||
}
|
||||
if (fcntl(fd, F_SETFL, flags | O_NONBLOCK) == -1) {
|
||||
goto error;
|
||||
}
|
||||
}
|
||||
|
||||
if (bind_path) {
|
||||
struct sockaddr_un un;
|
||||
socklen_t un_len;
|
||||
make_sockaddr_un(bind_path, &un, &un_len);
|
||||
if (unlink(un.sun_path) && errno != ENOENT) {
|
||||
VLOG_WARN("unlinking \"%s\": %s\n", un.sun_path, strerror(errno));
|
||||
}
|
||||
fatal_signal_add_file_to_unlink(bind_path);
|
||||
if (bind(fd, (struct sockaddr*) &un, un_len)
|
||||
|| fchmod(fd, S_IRWXU)) {
|
||||
goto error;
|
||||
}
|
||||
}
|
||||
|
||||
if (connect_path) {
|
||||
struct sockaddr_un un;
|
||||
socklen_t un_len;
|
||||
make_sockaddr_un(connect_path, &un, &un_len);
|
||||
if (connect(fd, (struct sockaddr*) &un, un_len)
|
||||
&& errno != EINPROGRESS) {
|
||||
goto error;
|
||||
}
|
||||
}
|
||||
|
||||
#ifdef SCM_CREDENTIALS
|
||||
if (passcred) {
|
||||
int enable = 1;
|
||||
if (setsockopt(fd, SOL_SOCKET, SO_PASSCRED, &enable, sizeof(enable))) {
|
||||
goto error;
|
||||
}
|
||||
}
|
||||
#endif
|
||||
|
||||
return fd;
|
||||
|
||||
error:
|
||||
if (bind_path) {
|
||||
fatal_signal_remove_file_to_unlink(bind_path);
|
||||
}
|
||||
error = errno;
|
||||
close(fd);
|
||||
return -error;
|
||||
}
|
||||
+11
-1
@@ -31,6 +31,7 @@
|
||||
* derivatives without specific, written prior permission.
|
||||
*/
|
||||
|
||||
#include <config.h>
|
||||
#include "util.h"
|
||||
#include <stdarg.h>
|
||||
#include <stdio.h>
|
||||
@@ -83,10 +84,19 @@ xmemdup(const void *p_, size_t size)
|
||||
return p;
|
||||
}
|
||||
|
||||
char *
|
||||
xmemdup0(const char *p_, size_t length)
|
||||
{
|
||||
char *p = xmalloc(length + 1);
|
||||
memcpy(p, p_, length);
|
||||
p[length] = '\0';
|
||||
return p;
|
||||
}
|
||||
|
||||
char *
|
||||
xstrdup(const char *s)
|
||||
{
|
||||
return xmemdup(s, strlen(s) + 1);
|
||||
return xmemdup0(s, strlen(s));
|
||||
}
|
||||
|
||||
char *
|
||||
|
||||
+4
-1
@@ -31,6 +31,7 @@
|
||||
* derivatives without specific, written prior permission.
|
||||
*/
|
||||
|
||||
#include <config.h>
|
||||
#include "vconn.h"
|
||||
#include <arpa/inet.h>
|
||||
#include <assert.h>
|
||||
@@ -77,12 +78,14 @@ netlink_open(const char *name, char *suffix, struct vconn **vconnp)
|
||||
|
||||
subscribe = 1;
|
||||
if (sscanf(suffix, "%d:%d", &dp_idx, &subscribe) < 1) {
|
||||
fatal(0, "%s: syntax error", name);
|
||||
error(0, "%s: syntax error", name);
|
||||
return EAFNOSUPPORT;
|
||||
}
|
||||
|
||||
netlink = xmalloc(sizeof *netlink);
|
||||
netlink->vconn.class = &netlink_vconn_class;
|
||||
netlink->vconn.connect_status = 0;
|
||||
netlink->vconn.ip = 0;
|
||||
retval = dpif_open(dp_idx, subscribe, &netlink->dp);
|
||||
if (retval) {
|
||||
free(netlink);
|
||||
|
||||
+30
-9
@@ -31,10 +31,12 @@
|
||||
* derivatives without specific, written prior permission.
|
||||
*/
|
||||
|
||||
#include <config.h>
|
||||
#include "vconn-ssl.h"
|
||||
#include "dhparams.h"
|
||||
#include <assert.h>
|
||||
#include <errno.h>
|
||||
#include <inttypes.h>
|
||||
#include <string.h>
|
||||
#include <netinet/tcp.h>
|
||||
#include <openssl/err.h>
|
||||
@@ -45,6 +47,7 @@
|
||||
#include "socket-util.h"
|
||||
#include "util.h"
|
||||
#include "openflow.h"
|
||||
#include "packets.h"
|
||||
#include "poll-loop.h"
|
||||
#include "ofp-print.h"
|
||||
#include "socket-util.h"
|
||||
@@ -171,7 +174,8 @@ want_to_poll_events(int want)
|
||||
|
||||
static int
|
||||
new_ssl_vconn(const char *name, int fd, enum session_type type,
|
||||
enum ssl_state state, struct vconn **vconnp)
|
||||
enum ssl_state state, const struct sockaddr_in *sin,
|
||||
struct vconn **vconnp)
|
||||
{
|
||||
struct ssl_vconn *sslv;
|
||||
SSL *ssl = NULL;
|
||||
@@ -220,6 +224,7 @@ new_ssl_vconn(const char *name, int fd, enum session_type type,
|
||||
sslv = xmalloc(sizeof *sslv);
|
||||
sslv->vconn.class = &ssl_vconn_class;
|
||||
sslv->vconn.connect_status = EAGAIN;
|
||||
sslv->vconn.ip = sin->sin_addr.s_addr;
|
||||
sslv->state = state;
|
||||
sslv->type = type;
|
||||
sslv->fd = fd;
|
||||
@@ -266,7 +271,8 @@ ssl_open(const char *name, char *suffix, struct vconn **vconnp)
|
||||
host_name = strtok_r(suffix, "::", &save_ptr);
|
||||
port_string = strtok_r(NULL, "::", &save_ptr);
|
||||
if (!host_name) {
|
||||
fatal(0, "%s: bad peer name format", name);
|
||||
error(0, "%s: bad peer name format", name);
|
||||
return EAFNOSUPPORT;
|
||||
}
|
||||
|
||||
memset(&sin, 0, sizeof sin);
|
||||
@@ -294,7 +300,7 @@ ssl_open(const char *name, char *suffix, struct vconn **vconnp)
|
||||
if (retval < 0) {
|
||||
if (errno == EINPROGRESS) {
|
||||
return new_ssl_vconn(name, fd, CLIENT, STATE_TCP_CONNECTING,
|
||||
vconnp);
|
||||
&sin, vconnp);
|
||||
} else {
|
||||
int error = errno;
|
||||
VLOG_ERR("%s: connect: %s", name, strerror(error));
|
||||
@@ -303,7 +309,7 @@ ssl_open(const char *name, char *suffix, struct vconn **vconnp)
|
||||
}
|
||||
} else {
|
||||
return new_ssl_vconn(name, fd, CLIENT, STATE_SSL_CONNECTING,
|
||||
vconnp);
|
||||
&sin, vconnp);
|
||||
}
|
||||
}
|
||||
|
||||
@@ -330,8 +336,9 @@ ssl_connect(struct vconn *vconn)
|
||||
if (retval < 0 && ssl_wants_io(error)) {
|
||||
return EAGAIN;
|
||||
} else {
|
||||
int unused;
|
||||
interpret_ssl_error((sslv->type == CLIENT ? "SSL_connect"
|
||||
: "SSL_accept"), retval, error, NULL);
|
||||
: "SSL_accept"), retval, error, &unused);
|
||||
shutdown(sslv->fd, SHUT_RDWR);
|
||||
return EPROTO;
|
||||
}
|
||||
@@ -453,7 +460,7 @@ again:
|
||||
return 0;
|
||||
}
|
||||
}
|
||||
buffer_reserve_tailroom(rx, want_bytes);
|
||||
buffer_prealloc_tailroom(rx, want_bytes);
|
||||
|
||||
/* Behavior of zero-byte SSL_read is poorly defined. */
|
||||
assert(want_bytes > 0);
|
||||
@@ -732,10 +739,13 @@ static int
|
||||
pssl_accept(struct vconn *vconn, struct vconn **new_vconnp)
|
||||
{
|
||||
struct pssl_vconn *pssl = pssl_vconn_cast(vconn);
|
||||
struct sockaddr_in sin;
|
||||
socklen_t sin_len = sizeof sin;
|
||||
char name[128];
|
||||
int new_fd;
|
||||
int error;
|
||||
|
||||
new_fd = accept(pssl->fd, NULL, NULL);
|
||||
new_fd = accept(pssl->fd, &sin, &sin_len);
|
||||
if (new_fd < 0) {
|
||||
int error = errno;
|
||||
if (error != EAGAIN) {
|
||||
@@ -750,8 +760,12 @@ pssl_accept(struct vconn *vconn, struct vconn **new_vconnp)
|
||||
return error;
|
||||
}
|
||||
|
||||
return new_ssl_vconn("ssl" /* FIXME */, new_fd,
|
||||
SERVER, STATE_SSL_CONNECTING, new_vconnp);
|
||||
sprintf(name, "ssl:"IP_FMT, IP_ARGS(&sin.sin_addr));
|
||||
if (sin.sin_port != htons(OFP_SSL_PORT)) {
|
||||
sprintf(strchr(name, '\0'), ":%"PRIu16, ntohs(sin.sin_port));
|
||||
}
|
||||
return new_ssl_vconn(name, new_fd, SERVER, STATE_SSL_CONNECTING, &sin,
|
||||
new_vconnp);
|
||||
}
|
||||
|
||||
static void
|
||||
@@ -855,6 +869,13 @@ tmp_dh_callback(SSL *ssl, int is_export UNUSED, int keylength)
|
||||
return NULL;
|
||||
}
|
||||
|
||||
/* Returns true if SSL is at least partially configured. */
|
||||
bool
|
||||
vconn_ssl_is_configured(void)
|
||||
{
|
||||
return has_private_key || has_certificate || has_ca_cert;
|
||||
}
|
||||
|
||||
void
|
||||
vconn_ssl_set_private_key_file(const char *file_name)
|
||||
{
|
||||
|
||||
@@ -0,0 +1,354 @@
|
||||
/* 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 "vconn-stream.h"
|
||||
#include <assert.h>
|
||||
#include <errno.h>
|
||||
#include <poll.h>
|
||||
#include <stdlib.h>
|
||||
#include <string.h>
|
||||
#include <sys/types.h>
|
||||
#include <unistd.h>
|
||||
#include "buffer.h"
|
||||
#include "util.h"
|
||||
#include "openflow.h"
|
||||
#include "poll-loop.h"
|
||||
#include "socket-util.h"
|
||||
#include "vconn.h"
|
||||
|
||||
#include "vlog.h"
|
||||
#define THIS_MODULE VLM_vconn_stream
|
||||
|
||||
/* Active stream socket vconn. */
|
||||
|
||||
struct stream_vconn
|
||||
{
|
||||
struct vconn vconn;
|
||||
int fd;
|
||||
struct buffer *rxbuf;
|
||||
struct buffer *txbuf;
|
||||
struct poll_waiter *tx_waiter;
|
||||
};
|
||||
|
||||
static struct vconn_class stream_vconn_class;
|
||||
|
||||
int
|
||||
new_stream_vconn(const char *name, int fd, int connect_status,
|
||||
uint32_t ip, struct vconn **vconnp)
|
||||
{
|
||||
struct stream_vconn *s;
|
||||
|
||||
s = xmalloc(sizeof *s);
|
||||
s->vconn.class = &stream_vconn_class;
|
||||
s->vconn.connect_status = connect_status;
|
||||
s->vconn.ip = ip;
|
||||
s->fd = fd;
|
||||
s->txbuf = NULL;
|
||||
s->tx_waiter = NULL;
|
||||
s->rxbuf = NULL;
|
||||
*vconnp = &s->vconn;
|
||||
return 0;
|
||||
}
|
||||
|
||||
static struct stream_vconn *
|
||||
stream_vconn_cast(struct vconn *vconn)
|
||||
{
|
||||
assert(vconn->class == &stream_vconn_class);
|
||||
return CONTAINER_OF(vconn, struct stream_vconn, vconn);
|
||||
}
|
||||
|
||||
static void
|
||||
stream_close(struct vconn *vconn)
|
||||
{
|
||||
struct stream_vconn *s = stream_vconn_cast(vconn);
|
||||
poll_cancel(s->tx_waiter);
|
||||
close(s->fd);
|
||||
free(s);
|
||||
}
|
||||
|
||||
static int
|
||||
stream_connect(struct vconn *vconn)
|
||||
{
|
||||
struct stream_vconn *s = stream_vconn_cast(vconn);
|
||||
return check_connection_completion(s->fd);
|
||||
}
|
||||
|
||||
static int
|
||||
stream_recv(struct vconn *vconn, struct buffer **bufferp)
|
||||
{
|
||||
struct stream_vconn *s = stream_vconn_cast(vconn);
|
||||
struct buffer *rx;
|
||||
size_t want_bytes;
|
||||
ssize_t retval;
|
||||
|
||||
if (s->rxbuf == NULL) {
|
||||
s->rxbuf = buffer_new(1564);
|
||||
}
|
||||
rx = s->rxbuf;
|
||||
|
||||
again:
|
||||
if (sizeof(struct ofp_header) > rx->size) {
|
||||
want_bytes = sizeof(struct ofp_header) - rx->size;
|
||||
} else {
|
||||
struct ofp_header *oh = rx->data;
|
||||
size_t length = ntohs(oh->length);
|
||||
if (length < sizeof(struct ofp_header)) {
|
||||
VLOG_ERR("received too-short ofp_header (%zu bytes)", length);
|
||||
return EPROTO;
|
||||
}
|
||||
want_bytes = length - rx->size;
|
||||
if (!want_bytes) {
|
||||
*bufferp = rx;
|
||||
s->rxbuf = NULL;
|
||||
return 0;
|
||||
}
|
||||
}
|
||||
buffer_prealloc_tailroom(rx, want_bytes);
|
||||
|
||||
retval = read(s->fd, buffer_tail(rx), want_bytes);
|
||||
if (retval > 0) {
|
||||
rx->size += retval;
|
||||
if (retval == want_bytes) {
|
||||
if (rx->size > sizeof(struct ofp_header)) {
|
||||
*bufferp = rx;
|
||||
s->rxbuf = NULL;
|
||||
return 0;
|
||||
} else {
|
||||
goto again;
|
||||
}
|
||||
}
|
||||
return EAGAIN;
|
||||
} else if (retval == 0) {
|
||||
if (rx->size) {
|
||||
VLOG_ERR("connection dropped mid-packet");
|
||||
return EPROTO;
|
||||
} else {
|
||||
return EOF;
|
||||
}
|
||||
} else {
|
||||
return retval ? errno : EAGAIN;
|
||||
}
|
||||
}
|
||||
|
||||
static void
|
||||
stream_clear_txbuf(struct stream_vconn *s)
|
||||
{
|
||||
buffer_delete(s->txbuf);
|
||||
s->txbuf = NULL;
|
||||
s->tx_waiter = NULL;
|
||||
}
|
||||
|
||||
static void
|
||||
stream_do_tx(int fd UNUSED, short int revents UNUSED, void *vconn_)
|
||||
{
|
||||
struct vconn *vconn = vconn_;
|
||||
struct stream_vconn *s = stream_vconn_cast(vconn);
|
||||
ssize_t n = write(s->fd, s->txbuf->data, s->txbuf->size);
|
||||
if (n < 0) {
|
||||
if (errno != EAGAIN) {
|
||||
VLOG_ERR("send: %s", strerror(errno));
|
||||
stream_clear_txbuf(s);
|
||||
return;
|
||||
}
|
||||
} else if (n > 0) {
|
||||
buffer_pull(s->txbuf, n);
|
||||
if (!s->txbuf->size) {
|
||||
stream_clear_txbuf(s);
|
||||
return;
|
||||
}
|
||||
}
|
||||
s->tx_waiter = poll_fd_callback(s->fd, POLLOUT, stream_do_tx, vconn);
|
||||
}
|
||||
|
||||
static int
|
||||
stream_send(struct vconn *vconn, struct buffer *buffer)
|
||||
{
|
||||
struct stream_vconn *s = stream_vconn_cast(vconn);
|
||||
ssize_t retval;
|
||||
|
||||
if (s->txbuf) {
|
||||
return EAGAIN;
|
||||
}
|
||||
|
||||
retval = write(s->fd, buffer->data, buffer->size);
|
||||
if (retval == buffer->size) {
|
||||
buffer_delete(buffer);
|
||||
return 0;
|
||||
} else if (retval >= 0 || errno == EAGAIN) {
|
||||
s->txbuf = buffer;
|
||||
if (retval > 0) {
|
||||
buffer_pull(buffer, retval);
|
||||
}
|
||||
s->tx_waiter = poll_fd_callback(s->fd, POLLOUT, stream_do_tx, vconn);
|
||||
return 0;
|
||||
} else {
|
||||
return errno;
|
||||
}
|
||||
}
|
||||
|
||||
static void
|
||||
stream_wait(struct vconn *vconn, enum vconn_wait_type wait)
|
||||
{
|
||||
struct stream_vconn *s = stream_vconn_cast(vconn);
|
||||
switch (wait) {
|
||||
case WAIT_CONNECT:
|
||||
poll_fd_wait(s->fd, POLLOUT);
|
||||
break;
|
||||
|
||||
case WAIT_SEND:
|
||||
if (!s->txbuf) {
|
||||
poll_fd_wait(s->fd, POLLOUT);
|
||||
} else {
|
||||
/* Nothing to do: need to drain txbuf first. */
|
||||
}
|
||||
break;
|
||||
|
||||
case WAIT_RECV:
|
||||
poll_fd_wait(s->fd, POLLIN);
|
||||
break;
|
||||
|
||||
default:
|
||||
NOT_REACHED();
|
||||
}
|
||||
}
|
||||
|
||||
static struct vconn_class stream_vconn_class = {
|
||||
.name = "stream",
|
||||
.close = stream_close,
|
||||
.connect = stream_connect,
|
||||
.recv = stream_recv,
|
||||
.send = stream_send,
|
||||
.wait = stream_wait,
|
||||
};
|
||||
|
||||
/* Passive stream socket vconn. */
|
||||
|
||||
struct pstream_vconn
|
||||
{
|
||||
struct vconn vconn;
|
||||
int fd;
|
||||
int (*accept_cb)(int fd, const struct sockaddr *, size_t sa_len,
|
||||
struct vconn **);
|
||||
};
|
||||
|
||||
static struct vconn_class pstream_vconn_class;
|
||||
|
||||
static struct pstream_vconn *
|
||||
pstream_vconn_cast(struct vconn *vconn)
|
||||
{
|
||||
assert(vconn->class == &pstream_vconn_class);
|
||||
return CONTAINER_OF(vconn, struct pstream_vconn, vconn);
|
||||
}
|
||||
|
||||
int
|
||||
new_pstream_vconn(const char *name, int fd,
|
||||
int (*accept_cb)(int fd, const struct sockaddr *,
|
||||
size_t sa_len, struct vconn **),
|
||||
struct vconn **vconnp)
|
||||
{
|
||||
struct pstream_vconn *ps;
|
||||
int retval;
|
||||
|
||||
retval = set_nonblocking(fd);
|
||||
if (retval) {
|
||||
close(fd);
|
||||
return retval;
|
||||
}
|
||||
|
||||
if (listen(fd, 10) < 0) {
|
||||
int error = errno;
|
||||
VLOG_ERR("%s: listen: %s", name, strerror(error));
|
||||
close(fd);
|
||||
return error;
|
||||
}
|
||||
|
||||
ps = xmalloc(sizeof *ps);
|
||||
ps->vconn.class = &pstream_vconn_class;
|
||||
ps->vconn.connect_status = 0;
|
||||
ps->fd = fd;
|
||||
ps->accept_cb = accept_cb;
|
||||
*vconnp = &ps->vconn;
|
||||
return 0;
|
||||
}
|
||||
|
||||
static void
|
||||
pstream_close(struct vconn *vconn)
|
||||
{
|
||||
struct pstream_vconn *ps = pstream_vconn_cast(vconn);
|
||||
close(ps->fd);
|
||||
free(ps);
|
||||
}
|
||||
|
||||
static int
|
||||
pstream_accept(struct vconn *vconn, struct vconn **new_vconnp)
|
||||
{
|
||||
struct pstream_vconn *ps = pstream_vconn_cast(vconn);
|
||||
struct sockaddr_storage ss;
|
||||
socklen_t ss_len = sizeof ss;
|
||||
int new_fd;
|
||||
int retval;
|
||||
|
||||
new_fd = accept(ps->fd, (struct sockaddr *) &ss, &ss_len);
|
||||
if (new_fd < 0) {
|
||||
int retval = errno;
|
||||
if (retval != EAGAIN) {
|
||||
VLOG_DBG("accept: %s", strerror(retval));
|
||||
}
|
||||
return retval;
|
||||
}
|
||||
|
||||
retval = set_nonblocking(new_fd);
|
||||
if (retval) {
|
||||
close(new_fd);
|
||||
return retval;
|
||||
}
|
||||
|
||||
return ps->accept_cb(new_fd, (const struct sockaddr *) &ss, ss_len,
|
||||
new_vconnp);
|
||||
}
|
||||
|
||||
static void
|
||||
pstream_wait(struct vconn *vconn, enum vconn_wait_type wait)
|
||||
{
|
||||
struct pstream_vconn *ps = pstream_vconn_cast(vconn);
|
||||
assert(wait == WAIT_ACCEPT);
|
||||
poll_fd_wait(ps->fd, POLLIN);
|
||||
}
|
||||
|
||||
static struct vconn_class pstream_vconn_class = {
|
||||
.name = "pstream",
|
||||
.close = pstream_close,
|
||||
.accept = pstream_accept,
|
||||
.wait = pstream_wait
|
||||
};
|
||||
+27
-270
@@ -31,43 +31,31 @@
|
||||
* derivatives without specific, written prior permission.
|
||||
*/
|
||||
|
||||
#include <config.h>
|
||||
#include "vconn.h"
|
||||
#include <assert.h>
|
||||
#include <errno.h>
|
||||
#include <netdb.h>
|
||||
#include <poll.h>
|
||||
#include <inttypes.h>
|
||||
#include <sys/types.h>
|
||||
#include <netinet/in.h>
|
||||
#include <netinet/tcp.h>
|
||||
#include <stdlib.h>
|
||||
#include <string.h>
|
||||
#include <unistd.h>
|
||||
#include "buffer.h"
|
||||
#include "packets.h"
|
||||
#include "socket-util.h"
|
||||
#include "util.h"
|
||||
#include "openflow.h"
|
||||
#include "ofp-print.h"
|
||||
#include "poll-loop.h"
|
||||
#include "vconn-stream.h"
|
||||
|
||||
#include "vlog.h"
|
||||
#define THIS_MODULE VLM_vconn_tcp
|
||||
|
||||
/* Active TCP. */
|
||||
|
||||
struct tcp_vconn
|
||||
{
|
||||
struct vconn vconn;
|
||||
int fd;
|
||||
struct buffer *rxbuf;
|
||||
struct buffer *txbuf;
|
||||
struct poll_waiter *tx_waiter;
|
||||
};
|
||||
|
||||
static int
|
||||
new_tcp_vconn(const char *name, int fd, int connect_status,
|
||||
struct vconn **vconnp)
|
||||
const struct sockaddr_in *sin, struct vconn **vconnp)
|
||||
{
|
||||
struct tcp_vconn *tcp;
|
||||
int on = 1;
|
||||
int retval;
|
||||
|
||||
@@ -78,25 +66,10 @@ new_tcp_vconn(const char *name, int fd, int connect_status,
|
||||
return errno;
|
||||
}
|
||||
|
||||
tcp = xmalloc(sizeof *tcp);
|
||||
tcp->vconn.class = &tcp_vconn_class;
|
||||
tcp->vconn.connect_status = connect_status;
|
||||
tcp->fd = fd;
|
||||
tcp->txbuf = NULL;
|
||||
tcp->tx_waiter = NULL;
|
||||
tcp->rxbuf = NULL;
|
||||
*vconnp = &tcp->vconn;
|
||||
return 0;
|
||||
return new_stream_vconn(name, fd, connect_status, sin->sin_addr.s_addr,
|
||||
vconnp);
|
||||
}
|
||||
|
||||
static struct tcp_vconn *
|
||||
tcp_vconn_cast(struct vconn *vconn)
|
||||
{
|
||||
assert(vconn->class == &tcp_vconn_class);
|
||||
return CONTAINER_OF(vconn, struct tcp_vconn, vconn);
|
||||
}
|
||||
|
||||
|
||||
static int
|
||||
tcp_open(const char *name, char *suffix, struct vconn **vconnp)
|
||||
{
|
||||
@@ -114,7 +87,8 @@ tcp_open(const char *name, char *suffix, struct vconn **vconnp)
|
||||
host_name = strtok_r(suffix, "::", &save_ptr);
|
||||
port_string = strtok_r(NULL, "::", &save_ptr);
|
||||
if (!host_name) {
|
||||
fatal(0, "%s: bad peer name format", name);
|
||||
error(0, "%s: bad peer name format", name);
|
||||
return EAFNOSUPPORT;
|
||||
}
|
||||
|
||||
memset(&sin, 0, sizeof sin);
|
||||
@@ -139,7 +113,7 @@ tcp_open(const char *name, char *suffix, struct vconn **vconnp)
|
||||
retval = connect(fd, (struct sockaddr *) &sin, sizeof sin);
|
||||
if (retval < 0) {
|
||||
if (errno == EINPROGRESS) {
|
||||
return new_tcp_vconn(name, fd, EAGAIN, vconnp);
|
||||
return new_tcp_vconn(name, fd, EAGAIN, &sin, vconnp);
|
||||
} else {
|
||||
int error = errno;
|
||||
VLOG_ERR("%s: connect: %s", name, strerror(error));
|
||||
@@ -147,195 +121,24 @@ tcp_open(const char *name, char *suffix, struct vconn **vconnp)
|
||||
return error;
|
||||
}
|
||||
} else {
|
||||
return new_tcp_vconn(name, fd, 0, vconnp);
|
||||
}
|
||||
}
|
||||
|
||||
static void
|
||||
tcp_close(struct vconn *vconn)
|
||||
{
|
||||
struct tcp_vconn *tcp = tcp_vconn_cast(vconn);
|
||||
poll_cancel(tcp->tx_waiter);
|
||||
close(tcp->fd);
|
||||
free(tcp);
|
||||
}
|
||||
|
||||
static int
|
||||
tcp_connect(struct vconn *vconn)
|
||||
{
|
||||
struct tcp_vconn *tcp = tcp_vconn_cast(vconn);
|
||||
return check_connection_completion(tcp->fd);
|
||||
}
|
||||
|
||||
static int
|
||||
tcp_recv(struct vconn *vconn, struct buffer **bufferp)
|
||||
{
|
||||
struct tcp_vconn *tcp = tcp_vconn_cast(vconn);
|
||||
struct buffer *rx;
|
||||
size_t want_bytes;
|
||||
ssize_t retval;
|
||||
|
||||
if (tcp->rxbuf == NULL) {
|
||||
tcp->rxbuf = buffer_new(1564);
|
||||
}
|
||||
rx = tcp->rxbuf;
|
||||
|
||||
again:
|
||||
if (sizeof(struct ofp_header) > rx->size) {
|
||||
want_bytes = sizeof(struct ofp_header) - rx->size;
|
||||
} else {
|
||||
struct ofp_header *oh = rx->data;
|
||||
size_t length = ntohs(oh->length);
|
||||
if (length < sizeof(struct ofp_header)) {
|
||||
VLOG_ERR("received too-short ofp_header (%zu bytes)", length);
|
||||
return EPROTO;
|
||||
}
|
||||
want_bytes = length - rx->size;
|
||||
if (!want_bytes) {
|
||||
*bufferp = rx;
|
||||
tcp->rxbuf = NULL;
|
||||
return 0;
|
||||
}
|
||||
}
|
||||
buffer_reserve_tailroom(rx, want_bytes);
|
||||
|
||||
retval = read(tcp->fd, buffer_tail(rx), want_bytes);
|
||||
if (retval > 0) {
|
||||
rx->size += retval;
|
||||
if (retval == want_bytes) {
|
||||
if (rx->size > sizeof(struct ofp_header)) {
|
||||
*bufferp = rx;
|
||||
tcp->rxbuf = NULL;
|
||||
return 0;
|
||||
} else {
|
||||
goto again;
|
||||
}
|
||||
}
|
||||
return EAGAIN;
|
||||
} else if (retval == 0) {
|
||||
if (rx->size) {
|
||||
VLOG_ERR("connection dropped mid-packet");
|
||||
return EPROTO;
|
||||
} else {
|
||||
return EOF;
|
||||
}
|
||||
} else {
|
||||
return retval ? errno : EAGAIN;
|
||||
}
|
||||
}
|
||||
|
||||
static void
|
||||
tcp_clear_txbuf(struct tcp_vconn *tcp)
|
||||
{
|
||||
buffer_delete(tcp->txbuf);
|
||||
tcp->txbuf = NULL;
|
||||
tcp->tx_waiter = NULL;
|
||||
}
|
||||
|
||||
static void
|
||||
tcp_do_tx(int fd UNUSED, short int revents UNUSED, void *vconn_)
|
||||
{
|
||||
struct vconn *vconn = vconn_;
|
||||
struct tcp_vconn *tcp = tcp_vconn_cast(vconn);
|
||||
ssize_t n = write(tcp->fd, tcp->txbuf->data, tcp->txbuf->size);
|
||||
if (n < 0) {
|
||||
if (errno != EAGAIN) {
|
||||
VLOG_ERR("send: %s", strerror(errno));
|
||||
tcp_clear_txbuf(tcp);
|
||||
return;
|
||||
}
|
||||
} else if (n > 0) {
|
||||
buffer_pull(tcp->txbuf, n);
|
||||
if (!tcp->txbuf->size) {
|
||||
tcp_clear_txbuf(tcp);
|
||||
return;
|
||||
}
|
||||
}
|
||||
tcp->tx_waiter = poll_fd_callback(tcp->fd, POLLOUT, tcp_do_tx, vconn);
|
||||
}
|
||||
|
||||
static int
|
||||
tcp_send(struct vconn *vconn, struct buffer *buffer)
|
||||
{
|
||||
struct tcp_vconn *tcp = tcp_vconn_cast(vconn);
|
||||
ssize_t retval;
|
||||
|
||||
if (tcp->txbuf) {
|
||||
return EAGAIN;
|
||||
}
|
||||
|
||||
retval = write(tcp->fd, buffer->data, buffer->size);
|
||||
if (retval == buffer->size) {
|
||||
buffer_delete(buffer);
|
||||
return 0;
|
||||
} else if (retval >= 0 || errno == EAGAIN) {
|
||||
tcp->txbuf = buffer;
|
||||
if (retval > 0) {
|
||||
buffer_pull(buffer, retval);
|
||||
}
|
||||
tcp->tx_waiter = poll_fd_callback(tcp->fd, POLLOUT, tcp_do_tx, vconn);
|
||||
return 0;
|
||||
} else {
|
||||
return errno;
|
||||
}
|
||||
}
|
||||
|
||||
static void
|
||||
tcp_wait(struct vconn *vconn, enum vconn_wait_type wait)
|
||||
{
|
||||
struct tcp_vconn *tcp = tcp_vconn_cast(vconn);
|
||||
switch (wait) {
|
||||
case WAIT_CONNECT:
|
||||
poll_fd_wait(tcp->fd, POLLOUT);
|
||||
break;
|
||||
|
||||
case WAIT_SEND:
|
||||
if (!tcp->txbuf) {
|
||||
poll_fd_wait(tcp->fd, POLLOUT);
|
||||
} else {
|
||||
/* Nothing to do: need to drain txbuf first. */
|
||||
}
|
||||
break;
|
||||
|
||||
case WAIT_RECV:
|
||||
poll_fd_wait(tcp->fd, POLLIN);
|
||||
break;
|
||||
|
||||
default:
|
||||
NOT_REACHED();
|
||||
return new_tcp_vconn(name, fd, 0, &sin, vconnp);
|
||||
}
|
||||
}
|
||||
|
||||
struct vconn_class tcp_vconn_class = {
|
||||
.name = "tcp",
|
||||
.open = tcp_open,
|
||||
.close = tcp_close,
|
||||
.connect = tcp_connect,
|
||||
.recv = tcp_recv,
|
||||
.send = tcp_send,
|
||||
.wait = tcp_wait,
|
||||
};
|
||||
|
||||
/* Passive TCP. */
|
||||
|
||||
struct ptcp_vconn
|
||||
{
|
||||
struct vconn vconn;
|
||||
int fd;
|
||||
};
|
||||
|
||||
static struct ptcp_vconn *
|
||||
ptcp_vconn_cast(struct vconn *vconn)
|
||||
{
|
||||
assert(vconn->class == &ptcp_vconn_class);
|
||||
return CONTAINER_OF(vconn, struct ptcp_vconn, vconn);
|
||||
}
|
||||
static int ptcp_accept(int fd, const struct sockaddr *sa, size_t sa_len,
|
||||
struct vconn **vconnp);
|
||||
|
||||
static int
|
||||
ptcp_open(const char *name, char *suffix, struct vconn **vconnp)
|
||||
{
|
||||
struct sockaddr_in sin;
|
||||
struct ptcp_vconn *ptcp;
|
||||
int retval;
|
||||
int fd;
|
||||
unsigned int yes = 1;
|
||||
@@ -346,12 +149,11 @@ ptcp_open(const char *name, char *suffix, struct vconn **vconnp)
|
||||
return errno;
|
||||
}
|
||||
|
||||
if ( setsockopt(fd, SOL_SOCKET, SO_REUSEADDR,&yes,sizeof(yes)) < 0) {
|
||||
VLOG_ERR("%s: setsockopt::SO_REUSEADDR: %s", name, strerror(errno));
|
||||
if (setsockopt(fd, SOL_SOCKET, SO_REUSEADDR, &yes, sizeof yes) < 0) {
|
||||
VLOG_ERR("%s: setsockopt(SO_REUSEADDR): %s", name, strerror(errno));
|
||||
return errno;
|
||||
}
|
||||
|
||||
|
||||
memset(&sin, 0, sizeof sin);
|
||||
sin.sin_family = AF_INET;
|
||||
sin.sin_addr.s_addr = htonl(INADDR_ANY);
|
||||
@@ -364,74 +166,29 @@ ptcp_open(const char *name, char *suffix, struct vconn **vconnp)
|
||||
return error;
|
||||
}
|
||||
|
||||
retval = listen(fd, 10);
|
||||
if (retval < 0) {
|
||||
int error = errno;
|
||||
VLOG_ERR("%s: listen: %s", name, strerror(error));
|
||||
close(fd);
|
||||
return error;
|
||||
}
|
||||
|
||||
retval = set_nonblocking(fd);
|
||||
if (retval) {
|
||||
close(fd);
|
||||
return retval;
|
||||
}
|
||||
|
||||
ptcp = xmalloc(sizeof *ptcp);
|
||||
ptcp->vconn.class = &ptcp_vconn_class;
|
||||
ptcp->vconn.connect_status = 0;
|
||||
ptcp->fd = fd;
|
||||
*vconnp = &ptcp->vconn;
|
||||
return 0;
|
||||
}
|
||||
|
||||
static void
|
||||
ptcp_close(struct vconn *vconn)
|
||||
{
|
||||
struct ptcp_vconn *ptcp = ptcp_vconn_cast(vconn);
|
||||
close(ptcp->fd);
|
||||
free(ptcp);
|
||||
return new_pstream_vconn("ptcp", fd, ptcp_accept, vconnp);
|
||||
}
|
||||
|
||||
static int
|
||||
ptcp_accept(struct vconn *vconn, struct vconn **new_vconnp)
|
||||
ptcp_accept(int fd, const struct sockaddr *sa, size_t sa_len,
|
||||
struct vconn **vconnp)
|
||||
{
|
||||
struct ptcp_vconn *ptcp = ptcp_vconn_cast(vconn);
|
||||
int new_fd;
|
||||
int error;
|
||||
const struct sockaddr_in *sin = (const struct sockaddr_in *) sa;
|
||||
char name[128];
|
||||
|
||||
new_fd = accept(ptcp->fd, NULL, NULL);
|
||||
if (new_fd < 0) {
|
||||
int error = errno;
|
||||
if (error != EAGAIN) {
|
||||
VLOG_DBG("accept: %s", strerror(error));
|
||||
if (sa_len == sizeof(struct sockaddr_in) && sin->sin_family == AF_INET) {
|
||||
sprintf(name, "tcp:"IP_FMT, IP_ARGS(&sin->sin_addr));
|
||||
if (sin->sin_port != htons(OFP_TCP_PORT)) {
|
||||
sprintf(strchr(name, '\0'), ":%"PRIu16, ntohs(sin->sin_port));
|
||||
}
|
||||
return error;
|
||||
} else {
|
||||
strcpy(name, "tcp");
|
||||
}
|
||||
|
||||
error = set_nonblocking(new_fd);
|
||||
if (error) {
|
||||
close(new_fd);
|
||||
return error;
|
||||
}
|
||||
|
||||
return new_tcp_vconn("tcp" /* FIXME */, new_fd, 0, new_vconnp);
|
||||
}
|
||||
|
||||
static void
|
||||
ptcp_wait(struct vconn *vconn, enum vconn_wait_type wait)
|
||||
{
|
||||
struct ptcp_vconn *ptcp = ptcp_vconn_cast(vconn);
|
||||
assert(wait == WAIT_ACCEPT);
|
||||
poll_fd_wait(ptcp->fd, POLLIN);
|
||||
return new_tcp_vconn(name, fd, 0, sin, vconnp);
|
||||
}
|
||||
|
||||
struct vconn_class ptcp_vconn_class = {
|
||||
.name = "ptcp",
|
||||
.open = ptcp_open,
|
||||
.close = ptcp_close,
|
||||
.accept = ptcp_accept,
|
||||
.wait = ptcp_wait
|
||||
};
|
||||
|
||||
@@ -0,0 +1,128 @@
|
||||
/* 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 "vconn.h"
|
||||
#include <assert.h>
|
||||
#include <errno.h>
|
||||
#include <inttypes.h>
|
||||
#include <netdb.h>
|
||||
#include <poll.h>
|
||||
#include <sys/types.h>
|
||||
#include <sys/un.h>
|
||||
#include <stdlib.h>
|
||||
#include <string.h>
|
||||
#include <unistd.h>
|
||||
#include "buffer.h"
|
||||
#include "socket-util.h"
|
||||
#include "util.h"
|
||||
#include "openflow.h"
|
||||
#include "ofp-print.h"
|
||||
#include "packets.h"
|
||||
#include "poll-loop.h"
|
||||
#include "vconn-stream.h"
|
||||
|
||||
#include "vlog.h"
|
||||
#define THIS_MODULE VLM_vconn_unix
|
||||
|
||||
/* Active UNIX socket. */
|
||||
|
||||
/* Number of unix sockets created so far, to ensure binding path uniqueness. */
|
||||
static int n_unix_sockets;
|
||||
|
||||
static int
|
||||
unix_open(const char *name, char *suffix, struct vconn **vconnp)
|
||||
{
|
||||
const char *connect_path = suffix;
|
||||
char bind_path[128];
|
||||
int fd;
|
||||
|
||||
sprintf(bind_path, "/tmp/vconn-unix.%ld.%d",
|
||||
(long int) getpid(), n_unix_sockets++);
|
||||
fd = make_unix_socket(SOCK_STREAM, true, false, bind_path, connect_path);
|
||||
if (fd < 0) {
|
||||
VLOG_ERR("%s: connection to %s failed: %s",
|
||||
bind_path, connect_path, strerror(-fd));
|
||||
return -fd;
|
||||
}
|
||||
|
||||
return new_stream_vconn(name, fd, check_connection_completion(fd),
|
||||
0, vconnp);
|
||||
}
|
||||
|
||||
struct vconn_class unix_vconn_class = {
|
||||
.name = "unix",
|
||||
.open = unix_open,
|
||||
};
|
||||
|
||||
/* Passive UNIX socket. */
|
||||
|
||||
static int punix_accept(int fd, const struct sockaddr *sa, size_t sa_len,
|
||||
struct vconn **vconnp);
|
||||
|
||||
static int
|
||||
punix_open(const char *name, char *suffix, struct vconn **vconnp)
|
||||
{
|
||||
int fd;
|
||||
|
||||
fd = make_unix_socket(SOCK_STREAM, true, true, suffix, NULL);
|
||||
if (fd < 0) {
|
||||
VLOG_ERR("%s: binding failed: %s", suffix, strerror(errno));
|
||||
return errno;
|
||||
}
|
||||
|
||||
return new_pstream_vconn("punix", fd, punix_accept, vconnp);
|
||||
}
|
||||
|
||||
static int
|
||||
punix_accept(int fd, const struct sockaddr *sa, size_t sa_len,
|
||||
struct vconn **vconnp)
|
||||
{
|
||||
const struct sockaddr_un *sun = (const struct sockaddr_un *) sa;
|
||||
char name[128];
|
||||
|
||||
if (sa_len >= offsetof(struct sockaddr_un, sun_path)) {
|
||||
snprintf(name, sizeof name, "unix:%.*s",
|
||||
(int) (sa_len - offsetof(struct sockaddr_un, sun_path)),
|
||||
sun->sun_path);
|
||||
} else {
|
||||
strcpy(name, "unix");
|
||||
}
|
||||
return new_stream_vconn(name, fd, 0, 0, vconnp);
|
||||
}
|
||||
|
||||
struct vconn_class punix_vconn_class = {
|
||||
.name = "punix",
|
||||
.open = punix_open,
|
||||
};
|
||||
|
||||
+158
-14
@@ -31,9 +31,11 @@
|
||||
* derivatives without specific, written prior permission.
|
||||
*/
|
||||
|
||||
#include <config.h>
|
||||
#include "vconn.h"
|
||||
#include <assert.h>
|
||||
#include <errno.h>
|
||||
#include <inttypes.h>
|
||||
#include <netinet/in.h>
|
||||
#include <poll.h>
|
||||
#include <stdlib.h>
|
||||
@@ -43,6 +45,7 @@
|
||||
#include "ofp-print.h"
|
||||
#include "openflow.h"
|
||||
#include "poll-loop.h"
|
||||
#include "random.h"
|
||||
#include "util.h"
|
||||
|
||||
#define THIS_MODULE VLM_vconn
|
||||
@@ -58,6 +61,8 @@ static struct vconn_class *vconn_classes[] = {
|
||||
&ssl_vconn_class,
|
||||
&pssl_vconn_class,
|
||||
#endif
|
||||
&unix_vconn_class,
|
||||
&punix_vconn_class,
|
||||
};
|
||||
|
||||
/* Check the validity of the vconn class structures. */
|
||||
@@ -71,11 +76,16 @@ check_vconn_classes(void)
|
||||
struct vconn_class *class = vconn_classes[i];
|
||||
assert(class->name != NULL);
|
||||
assert(class->open != NULL);
|
||||
assert(class->close != NULL);
|
||||
assert(class->accept
|
||||
? !class->recv && !class->send
|
||||
: class->recv && class->send);
|
||||
assert(class->wait != NULL);
|
||||
if (class->close || class->accept || class->recv || class->send
|
||||
|| class->wait) {
|
||||
assert(class->close != NULL);
|
||||
assert(class->accept
|
||||
? !class->recv && !class->send
|
||||
: class->recv && class->send);
|
||||
assert(class->wait != NULL);
|
||||
} else {
|
||||
/* This class delegates to another one. */
|
||||
}
|
||||
}
|
||||
#endif
|
||||
}
|
||||
@@ -102,6 +112,7 @@ vconn_usage(bool active, bool passive)
|
||||
printf(" ssl:HOST[:PORT] "
|
||||
"SSL PORT (default: %d) on remote HOST\n", OFP_SSL_PORT);
|
||||
#endif
|
||||
printf(" unix:FILE Unix domain socket named FILE\n");
|
||||
}
|
||||
|
||||
if (passive) {
|
||||
@@ -114,6 +125,8 @@ vconn_usage(bool active, bool passive)
|
||||
"listen for SSL on PORT (default: %d)\n",
|
||||
OFP_SSL_PORT);
|
||||
#endif
|
||||
printf(" punix:FILE "
|
||||
"listen on Unix domain socket FILE\n");
|
||||
}
|
||||
|
||||
#ifdef HAVE_OPENSSL
|
||||
@@ -141,7 +154,8 @@ vconn_open(const char *name, struct vconn **vconnp)
|
||||
|
||||
prefix_len = strcspn(name, ":");
|
||||
if (prefix_len == strlen(name)) {
|
||||
fatal(0, "`%s' not correct format for peer name", name);
|
||||
error(0, "`%s' not correct format for peer name", name);
|
||||
return EAFNOSUPPORT;
|
||||
}
|
||||
for (i = 0; i < ARRAY_SIZE(vconn_classes); i++) {
|
||||
struct vconn_class *class = vconn_classes[i];
|
||||
@@ -159,8 +173,8 @@ vconn_open(const char *name, struct vconn **vconnp)
|
||||
return retval;
|
||||
}
|
||||
}
|
||||
fatal(0, "unknown peer type `%.*s'", (int) prefix_len, name);
|
||||
abort();
|
||||
error(0, "unknown peer type `%.*s'", (int) prefix_len, name);
|
||||
return EAFNOSUPPORT;
|
||||
}
|
||||
|
||||
int
|
||||
@@ -204,6 +218,14 @@ vconn_is_passive(const struct vconn *vconn)
|
||||
return vconn->class->accept != NULL;
|
||||
}
|
||||
|
||||
/* Returns the IP address of the peer, or 0 if the peer is not connected over
|
||||
* an IP-based protocol or if its IP address is not yet known. */
|
||||
uint32_t
|
||||
vconn_get_ip(const struct vconn *vconn)
|
||||
{
|
||||
return vconn->ip;
|
||||
}
|
||||
|
||||
/* Tries to complete the connection on 'vconn', which must be an active
|
||||
* vconn. If 'vconn''s connection is complete, returns 0 if the connection
|
||||
* was successful or a positive errno value if it failed. If the
|
||||
@@ -254,10 +276,24 @@ vconn_recv(struct vconn *vconn, struct buffer **msgp)
|
||||
int retval = vconn_connect(vconn);
|
||||
if (!retval) {
|
||||
retval = (vconn->class->recv)(vconn, msgp);
|
||||
if (VLOG_IS_DBG_ENABLED() && !retval) {
|
||||
char *s = ofp_to_string((*msgp)->data, (*msgp)->size, 1);
|
||||
VLOG_DBG("received: %s", s);
|
||||
free(s);
|
||||
if (!retval) {
|
||||
struct ofp_header *oh;
|
||||
|
||||
if (VLOG_IS_DBG_ENABLED()) {
|
||||
char *s = ofp_to_string((*msgp)->data, (*msgp)->size, 1);
|
||||
VLOG_DBG("received: %s", s);
|
||||
free(s);
|
||||
}
|
||||
|
||||
oh = buffer_at_assert(*msgp, 0, sizeof *oh);
|
||||
if (oh->version != OFP_VERSION) {
|
||||
VLOG_ERR("received OpenFlow version %02"PRIx8" "
|
||||
"!= expected %02x",
|
||||
oh->version, OFP_VERSION);
|
||||
buffer_delete(*msgp);
|
||||
*msgp = NULL;
|
||||
return EPROTO;
|
||||
}
|
||||
}
|
||||
}
|
||||
if (retval) {
|
||||
@@ -282,6 +318,8 @@ vconn_send(struct vconn *vconn, struct buffer *msg)
|
||||
{
|
||||
int retval = vconn_connect(vconn);
|
||||
if (!retval) {
|
||||
assert(msg->size >= sizeof(struct ofp_header));
|
||||
assert(((struct ofp_header *) msg->data)->length == htons(msg->size));
|
||||
if (!VLOG_IS_DBG_ENABLED()) {
|
||||
retval = (vconn->class->send)(vconn, msg);
|
||||
} else {
|
||||
@@ -322,6 +360,45 @@ vconn_recv_block(struct vconn *vconn, struct buffer **msgp)
|
||||
return retval;
|
||||
}
|
||||
|
||||
/* Sends 'request' to 'vconn' and blocks until it receives a reply with a
|
||||
* matching transaction ID. Returns 0 if successful, in which case the reply
|
||||
* is stored in '*replyp' for the caller to examine and free. Otherwise
|
||||
* returns a positive errno value, or EOF, and sets '*replyp' to null.
|
||||
*
|
||||
* 'request' is always destroyed, regardless of the return value. */
|
||||
int
|
||||
vconn_transact(struct vconn *vconn, struct buffer *request,
|
||||
struct buffer **replyp)
|
||||
{
|
||||
uint32_t send_xid = ((struct ofp_header *) request->data)->xid;
|
||||
int error;
|
||||
|
||||
*replyp = NULL;
|
||||
error = vconn_send_block(vconn, request);
|
||||
if (error) {
|
||||
buffer_delete(request);
|
||||
return error;
|
||||
}
|
||||
for (;;) {
|
||||
uint32_t recv_xid;
|
||||
struct buffer *reply;
|
||||
|
||||
error = vconn_recv_block(vconn, &reply);
|
||||
if (error) {
|
||||
return error;
|
||||
}
|
||||
recv_xid = ((struct ofp_header *) reply->data)->xid;
|
||||
if (send_xid == recv_xid) {
|
||||
*replyp = reply;
|
||||
return 0;
|
||||
}
|
||||
|
||||
VLOG_DBG("received reply with xid %08"PRIx32" != expected %08"PRIx32,
|
||||
recv_xid, send_xid);
|
||||
buffer_delete(reply);
|
||||
}
|
||||
}
|
||||
|
||||
void
|
||||
vconn_wait(struct vconn *vconn, enum vconn_wait_type wait)
|
||||
{
|
||||
@@ -368,9 +445,51 @@ vconn_send_wait(struct vconn *vconn)
|
||||
vconn_wait(vconn, WAIT_SEND);
|
||||
}
|
||||
|
||||
/* Allocates and returns the first byte of a buffer 'openflow_len' bytes long,
|
||||
* containing an OpenFlow header with the given 'type' and a random transaction
|
||||
* id. Stores the new buffer in '*bufferp'. The caller must free the buffer
|
||||
* when it is no longer needed. */
|
||||
void *
|
||||
make_openflow(size_t openflow_len, uint8_t type, struct buffer **bufferp)
|
||||
{
|
||||
return make_openflow_xid(openflow_len, type, random_uint32(), bufferp);
|
||||
}
|
||||
|
||||
/* Allocates and returns the first byte of a buffer 'openflow_len' bytes long,
|
||||
* containing an OpenFlow header with the given 'type' and transaction id
|
||||
* 'xid'. Stores the new buffer in '*bufferp'. The caller must free the
|
||||
* buffer when it is no longer needed. */
|
||||
void *
|
||||
make_openflow_xid(size_t openflow_len, uint8_t type, uint32_t xid,
|
||||
struct buffer **bufferp)
|
||||
{
|
||||
struct buffer *buffer;
|
||||
struct ofp_header *oh;
|
||||
|
||||
assert(openflow_len >= sizeof *oh);
|
||||
assert(openflow_len <= UINT16_MAX);
|
||||
buffer = *bufferp = buffer_new(openflow_len);
|
||||
oh = buffer_put_uninit(buffer, openflow_len);
|
||||
memset(oh, 0, openflow_len);
|
||||
oh->version = OFP_VERSION;
|
||||
oh->type = type;
|
||||
oh->length = htons(openflow_len);
|
||||
oh->xid = xid;
|
||||
return oh;
|
||||
}
|
||||
|
||||
/* Updates the 'length' field of the OpenFlow message in 'buffer' to
|
||||
* 'buffer->size'. */
|
||||
void
|
||||
update_openflow_length(struct buffer *buffer)
|
||||
{
|
||||
struct ofp_header *oh = buffer_at_assert(buffer, 0, sizeof *oh);
|
||||
oh->length = htons(buffer->size);
|
||||
}
|
||||
|
||||
struct buffer *
|
||||
make_add_simple_flow(const struct flow *flow,
|
||||
uint32_t buffer_id, uint16_t out_port)
|
||||
uint32_t buffer_id, uint16_t out_port, uint16_t max_idle)
|
||||
{
|
||||
struct ofp_flow_mod *ofm;
|
||||
size_t size = sizeof *ofm + sizeof ofm->actions[0];
|
||||
@@ -392,7 +511,7 @@ make_add_simple_flow(const struct flow *flow,
|
||||
ofm->match.tp_src = flow->tp_src;
|
||||
ofm->match.tp_dst = flow->tp_dst;
|
||||
ofm->command = htons(OFPFC_ADD);
|
||||
ofm->max_idle = htons(60);
|
||||
ofm->max_idle = htons(max_idle);
|
||||
ofm->buffer_id = htonl(buffer_id);
|
||||
ofm->actions[0].type = htons(OFPAT_OUTPUT);
|
||||
ofm->actions[0].arg.output.max_len = htons(0);
|
||||
@@ -440,3 +559,28 @@ make_buffered_packet_out(uint32_t buffer_id,
|
||||
return out;
|
||||
}
|
||||
|
||||
/* Creates and returns an OFPT_ECHO_REQUEST message with an empty payload. */
|
||||
struct buffer *
|
||||
make_echo_request(void)
|
||||
{
|
||||
struct ofp_header *rq;
|
||||
struct buffer *out = buffer_new(sizeof *rq);
|
||||
rq = buffer_put_uninit(out, sizeof *rq);
|
||||
rq->version = OFP_VERSION;
|
||||
rq->type = OFPT_ECHO_REQUEST;
|
||||
rq->length = htons(sizeof *rq);
|
||||
rq->xid = 0;
|
||||
return out;
|
||||
}
|
||||
|
||||
/* Creates and returns an OFPT_ECHO_REPLY message matching the
|
||||
* OFPT_ECHO_REQUEST message in 'rq'. */
|
||||
struct buffer *
|
||||
make_echo_reply(const struct ofp_header *rq)
|
||||
{
|
||||
size_t size = ntohs(rq->length);
|
||||
struct buffer *out = buffer_new(size);
|
||||
struct ofp_header *reply = buffer_put(out, rq, size);
|
||||
reply->type = OFPT_ECHO_REPLY;
|
||||
return out;
|
||||
}
|
||||
+4
-91
@@ -31,6 +31,7 @@
|
||||
* derivatives without specific, written prior permission.
|
||||
*/
|
||||
|
||||
#include <config.h>
|
||||
#include "vlog-socket.h"
|
||||
#include <errno.h>
|
||||
#include <sys/un.h>
|
||||
@@ -44,15 +45,13 @@
|
||||
#include <unistd.h>
|
||||
#include "fatal-signal.h"
|
||||
#include "poll-loop.h"
|
||||
#include "socket-util.h"
|
||||
#include "util.h"
|
||||
#include "vlog.h"
|
||||
|
||||
#ifndef SCM_CREDENTIALS
|
||||
#include <time.h>
|
||||
#endif
|
||||
|
||||
static int make_unix_socket(bool nonblock, bool passcred,
|
||||
const char *bind_path, const char *connect_path);
|
||||
|
||||
/* Server for Vlog control connection. */
|
||||
struct vlog_server {
|
||||
@@ -89,7 +88,7 @@ vlog_server_listen(const char *path, struct vlog_server **serverp)
|
||||
(long int) getpid(), path ? path : "");
|
||||
}
|
||||
|
||||
server->fd = make_unix_socket(true, true, server->path, NULL);
|
||||
server->fd = make_unix_socket(SOCK_DGRAM, true, true, server->path, NULL);
|
||||
if (server->fd < 0) {
|
||||
int fd = server->fd;
|
||||
free(server->path);
|
||||
@@ -294,7 +293,7 @@ vlog_client_connect(const char *path, struct vlog_client **clientp)
|
||||
: xasprintf("/tmp/vlogs.%s", path));
|
||||
|
||||
client->bind_path = xasprintf("/tmp/vlog.%ld", (long int) getpid());
|
||||
fd = make_unix_socket(false, false,
|
||||
fd = make_unix_socket(SOCK_DGRAM, false, false,
|
||||
client->bind_path, client->connect_path);
|
||||
|
||||
if (fd >= 0) {
|
||||
@@ -434,89 +433,3 @@ vlog_client_target(const struct vlog_client *client)
|
||||
return client->connect_path;
|
||||
}
|
||||
|
||||
/* Helper functions. */
|
||||
|
||||
/* Stores in '*un' a sockaddr_un that refers to file 'name'. Stores in
|
||||
* '*un_len' the size of the sockaddr_un. */
|
||||
static void
|
||||
make_sockaddr_un(const char *name, struct sockaddr_un* un, socklen_t *un_len)
|
||||
{
|
||||
un->sun_family = AF_UNIX;
|
||||
strncpy(un->sun_path, name, sizeof un->sun_path);
|
||||
un->sun_path[sizeof un->sun_path - 1] = '\0';
|
||||
*un_len = (offsetof(struct sockaddr_un, sun_path)
|
||||
+ strlen (un->sun_path) + 1);
|
||||
}
|
||||
|
||||
/* Creates a Unix domain datagram socket that is bound to '*bind_path' (if
|
||||
* 'bind_path' is non-null) and connected to '*connect_path' (if 'connect_path'
|
||||
* is non-null). If 'nonblock' is true, the socket is made non-blocking. If
|
||||
* 'passcred' is true, the socket is configured to receive SCM_CREDENTIALS
|
||||
* control messages.
|
||||
*
|
||||
* Returns the socket's fd if successful, otherwise a negative errno value. */
|
||||
static int
|
||||
make_unix_socket(bool nonblock, bool passcred UNUSED,
|
||||
const char *bind_path, const char *connect_path)
|
||||
{
|
||||
int error;
|
||||
int fd;
|
||||
|
||||
fd = socket(PF_UNIX, SOCK_DGRAM, 0);
|
||||
if (fd < 0) {
|
||||
return -errno;
|
||||
}
|
||||
|
||||
if (nonblock) {
|
||||
int flags = fcntl(fd, F_GETFL, 0);
|
||||
if (flags == -1) {
|
||||
goto error;
|
||||
}
|
||||
if (fcntl(fd, F_SETFL, flags | O_NONBLOCK) == -1) {
|
||||
goto error;
|
||||
}
|
||||
}
|
||||
|
||||
if (bind_path) {
|
||||
struct sockaddr_un un;
|
||||
socklen_t un_len;
|
||||
make_sockaddr_un(bind_path, &un, &un_len);
|
||||
if (unlink(un.sun_path) && errno != ENOENT) {
|
||||
fprintf(stderr, "unlinking \"%s\": %s\n",
|
||||
un.sun_path, strerror(errno));
|
||||
}
|
||||
fatal_signal_add_file_to_unlink(bind_path);
|
||||
if (bind(fd, (struct sockaddr*) &un, un_len)
|
||||
|| fchmod(fd, S_IRWXU)) {
|
||||
goto error;
|
||||
}
|
||||
}
|
||||
|
||||
if (connect_path) {
|
||||
struct sockaddr_un un;
|
||||
socklen_t un_len;
|
||||
make_sockaddr_un(connect_path, &un, &un_len);
|
||||
if (connect(fd, (struct sockaddr*) &un, un_len)) {
|
||||
goto error;
|
||||
}
|
||||
}
|
||||
|
||||
#ifdef SCM_CREDENTIALS
|
||||
if (passcred) {
|
||||
int enable = 1;
|
||||
if (setsockopt(fd, SOL_SOCKET, SO_PASSCRED, &enable, sizeof(enable))) {
|
||||
goto error;
|
||||
}
|
||||
}
|
||||
#endif
|
||||
|
||||
return fd;
|
||||
|
||||
error:
|
||||
if (bind_path) {
|
||||
fatal_signal_remove_file_to_unlink(bind_path);
|
||||
}
|
||||
error = errno;
|
||||
close(fd);
|
||||
return -error;
|
||||
}
|
||||
Loaded 100 of 126 files, more files were not shown because too many files have changed in this diff.
Show more
Reference in new issue
Block a user