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:
Glen Gibb committed 2008-09-22 16:02:39 -07:00
commit 1feb499bf3
126 files changed
+7964 -1829

No files matched your search

-2
View File
@@ -32,8 +32,6 @@
/configure-stamp
/depcomp
/install-sh
/libtool
/ltmain.sh
/missing
/stamp-h1
Module.symvers
+54 -30
View File
@@ -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
View File
@@ -1,7 +1,6 @@
# -*- makefile -*-
AM_CPPFLAGS = $(SSL_CFLAGS)
LIBS = $(SSL_LIBS)
COMMON_FLAGS = -DVERSION=\"$(VERSION)\"
if NDEBUG
+5 -4
View File
@@ -1,10 +1,12 @@
AC_PREREQ(2.59)
AC_INIT(openflow, v0.8.1, info@openflowswitch.org)
AC_CONFIG_HEADERS([config.h])
AM_INIT_AUTOMAKE
AC_PROG_CC
AC_PROG_CPP
AC_PROG_LD
AC_PROG_RANLIB
AC_ARG_VAR([PERL], [path to Perl interpreter])
AC_PATH_PROG([PERL], perl, no)
@@ -14,8 +16,6 @@ fi
AC_USE_SYSTEM_EXTENSIONS
AC_PROG_LIBTOOL
AC_ARG_ENABLE(
[ndebug],
[AC_HELP_STRING([--enable-ndebug],
@@ -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"
+1 -1
View File
@@ -5,7 +5,7 @@ man_MANS = controller.8
DISTCLEANFILES = controller.8
controller_SOURCES = controller.c
controller_LDADD = ../lib/libopenflow.la -ldl
controller_LDADD = ../lib/libopenflow.a $(FAULT_LIBS) $(SSL_LIBS)
EXTRA_DIST = controller.8.in
controller.8: controller.8.in Makefile
+30 -5
View File
@@ -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
View File
@@ -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",
+1 -1
View File
@@ -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
-2
View File
@@ -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
View File
@@ -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
View File
@@ -28,6 +28,7 @@
#include <linux/netfilter_bridge.h>
#include <linux/inetdevice.h>
#include <linux/list.h>
#include <linux/rculist.h>
#include "openflow-netlink.h"
#include "datapath.h"
@@ -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)
+5
View File
@@ -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);
-118
View File
@@ -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;
}
-12
View File
@@ -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
View File
@@ -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);
-10
View File
@@ -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
View File
@@ -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
View File
@@ -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);
+1 -1
View File
@@ -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
-1
View File
@@ -7,7 +7,6 @@
/flow.c
/forward.c
/forward_t.c
/datapath_t.c
/table-hash.c
/table-linear.c
/table-mac.c
+1
View File
@@ -41,6 +41,7 @@ compat24_headers = \
linux-2.4/compat-2.4/include/linux/netdevice.h \
linux-2.4/compat-2.4/include/linux/netlink.h \
linux-2.4/compat-2.4/include/linux/random.h \
linux-2.4/compat-2.4/include/linux/rculist.h \
linux-2.4/compat-2.4/include/linux/rcupdate.h \
linux-2.4/compat-2.4/include/linux/sched.h \
linux-2.4/compat-2.4/include/linux/skbuff.h \
@@ -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>
+19
View File
@@ -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
-1
View File
@@ -5,7 +5,6 @@
/crc32.c
/crc_t.c
/datapath.c
/datapath_t.c
/dp_dev.c
/flow.c
/forward.c
+1
View File
@@ -10,6 +10,7 @@ openflow_headers += \
linux-2.6/compat-2.6/include/linux/mutex.h \
linux-2.6/compat-2.6/include/linux/netlink.h \
linux-2.6/compat-2.6/include/linux/random.h \
linux-2.6/compat-2.6/include/linux/rculist.h \
linux-2.6/compat-2.6/include/linux/skbuff.h \
linux-2.6/compat-2.6/include/linux/tcp.h \
linux-2.6/compat-2.6/include/linux/timer.h \
@@ -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
View File
@@ -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
View File
@@ -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;
+151 -45
View File
@@ -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:/);
}
+2 -2
View File
@@ -1,2 +1,2 @@
utilities/ofp-pki usr/sbin
utilities/vlogconf usr/sbin
_debian/utilities/ofp-pki usr/sbin
_debian/utilities/vlogconf usr/sbin
+1 -1
View File
@@ -1,2 +1,2 @@
utilities/vlogconf.8
utilities/ofp-pki.8
_debian/utilities/ofp-pki.8
+6 -1
View File
@@ -1,5 +1,10 @@
README.Debian for openflow-controller
-------------------------------------
* To reconfigure the controller, edit /etc/default/openflow-controller
* To (re)configure the controller, edit /etc/default/openflow-controller
and run "/etc/init.d/openflow-controller restart".
* To enable OpenFlow switches to automatically discover the location
of the controller, you must install and configure a DHCP server.
The secchan(8) manpage (found in the openflow-switch package) gives
a working example configuration file for the ISC DHCP server.
+1 -1
View File
@@ -1 +1 @@
controller/controller usr/sbin
_debian/controller/controller usr/sbin
+1 -1
View File
@@ -1 +1 @@
controller/controller.8
_debian/controller/controller.8
+1 -1
View File
@@ -1 +1 @@
utilities/ofp-pki-cgi usr/lib/cgi-bin
_debian/utilities/ofp-pki-cgi usr/lib/cgi-bin
-4
View File
@@ -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.
+18 -13
View File
@@ -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.
+53 -29
View File
@@ -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
+5 -3
View File
@@ -1,4 +1,6 @@
switch/switch usr/sbin
secchan/secchan usr/sbin
utilities/dpctl usr/sbin
_debian/switch/switch usr/sbin
_debian/secchan/secchan usr/sbin
_debian/utilities/dpctl usr/sbin
_debian/utilities/ofp-discover usr/sbin
_debian/utilities/ofp-kill usr/sbin
debian/ofp-switch-setup usr/sbin
+4 -2
View File
@@ -1,4 +1,6 @@
debian/ofp-switch-setup.8
secchan/secchan.8
switch/switch.8
_debian/secchan/secchan.8
_debian/switch/switch.8
_debian/utilities/ofp-discover.8
_debian/utilities/ofp-kill.8
utilities/dpctl.8
-14
View File
@@ -1,14 +0,0 @@
#!/bin/sh
# postinst script for openflow-switch
#
# see: dh_installdeb(1)
set -e
. /usr/share/debconf/confmodule
#DEBHELPER#
exit 0
+55 -16
View File
@@ -35,24 +35,59 @@ _Description: Some Network Devices Have IP or IPv6 Addresses
If this is an unintentional mistake, move back and fix the selection,
or de-configure the IP or IPv6 from these network devices.
Template: openflow-switch/band
Template: openflow-switch/mode
Type: select
_Choices: in-band, out-of-band
Default: in-band
_Choices: discovery, in-band, out-of-band
Default: discovery
_Description: Switch-to-controller access method:
The OpenFlow switch must be able to contact the OpenFlow controller over
the network. It can do so in one of two ways:
the network. It can do so in one of three ways:
.
in-band: A single network is used for OpenFlow traffic and other data
traffic; that is, the switch contacts the controller over one of the
network devices selected as OpenFlow switch netdevs in the previous
question. This is the most common case.
discovery: A single network is used for OpenFlow traffic and other
data traffic; that is, the switch contacts the controller over one of
the network devices selected as OpenFlow switch network devices in
the previous question. The switch automatically determines the
location of the controller using a DHCP request with an
OpenFlow-specific vendor option. This is the most common case.
.
in-band: As above, but the location of the controller is manually
configured.
.
out-of-band: OpenFlow traffic uses a network separate from the data traffic
that it controls. If this is the case, the control network must already
be configured on a network device other than one of those selected as
an OpenFlow switch netdev in the previous question.
Template: openflow-switch/discover
Type: note
_Description: Preparing to discover controller.
The setup program will now attempt to discover the OpenFlow controller.
Controller discovery may take up to 30 seconds. Please be patient.
.
See secchan(8) for instructions on how to configure a DHCP server for
controller discovery.
Template: openflow-switch/discovery-failure
Type: error
_Description: Controller discovery failed.
The controller's location could not be determined automatically.
.
Ensure that the OpenFlow DHCP server is properly configured. See
secchan(8) for instructions on how to configure a DHCP server for
controller discovery.
Template: openflow-switch/discovery-success
Type: boolean
Default: true
_Description: Use discovered settings?
Controller discovery obtained the following settings:
.
Controller location: ${controller-vconn}
.
PKI URL: ${pki-uri}
.
Please verify that these settings are correct.
Template: openflow-switch/switch-ip
Type: string
Default: dhcp
@@ -89,12 +124,16 @@ _Description: The controller location is invalid.
connect to the controller over SSL (recommended for security) or
"tcp:HOST[:PORT]" to connect over cleartext TCP.
Template: openflow-switch/pki-host
Template: openflow-switch/pki-uri
Type: string
_Description: OpenFlow PKI server host name:
Specify the host name or IP address of the server that hosts the OpenFlow
public key infrastructure (PKI). This is usually the same host as the
OpenFlow controller.
_Description: OpenFlow PKI server host name or URL:
Specify a URL to the OpenFlow public key infrastructure (PKI). If a
host name or IP address is specified in place of a URL, then
http://<host>/openflow/pki/ will be used,
where <host> is the specified host name or IP address.
.
The OpenFlow PKI is usually on the same machine as the OpenFlow
controller.
.
The setup process will connect to the OpenFlow PKI server over
HTTP, using the system's configured default HTTP proxy (if any).
@@ -105,7 +144,7 @@ _Description: The switch CA certificate could not be retrieved.
Retrieval of ${url} failed, with the following status: "${error}".
.
Ensure that the OpenFlow PKI server is correctly configured and
available on ${pki-host}. If the system is configured to use an HTTP
available at ${pki-uri}. If the system is configured to use an HTTP
proxy, also make sure that the HTTP proxy is available and that the
PKI server can be reached through it.
@@ -147,7 +186,7 @@ _Description: The certificate request could not be sent.
Posting to ${url} failed, with the following status: "${error}".
.
Ensure that the OpenFlow PKI server is correctly configured and
available on ${pki-host}.
available at ${pki-uri}.
Template: openflow-switch/fetch-switch-cert
Type: select
@@ -179,7 +218,7 @@ _Description: Signed switch certificate could not be retrieved.
.
This probably indicates that the switch's certificate request has not
yet been signed. If this is the problem, it may be fixed by signing
the certificate request at ${pki-host}, then trying to fetch the
the certificate request at ${pki-uri}, then trying to fetch the
signed switch certificate again.
Template: openflow-switch/complete
+142 -53
View File
@@ -8,7 +8,7 @@ msgid ""
msgstr ""
"Project-Id-Version: PACKAGE VERSION\n"
"Report-Msgid-Bugs-To: openflow-dev@lists.stanford.edu\n"
"POT-Creation-Date: 2008-06-12 14:45-0700\n"
"POT-Creation-Date: 2008-07-23 14:55-0700\n"
"PO-Revision-Date: YEAR-MO-DA HO:MI+ZONE\n"
"Last-Translator: FULL NAME <EMAIL@ADDRESS>\n"
"Language-Team: LANGUAGE <LL@li.org>\n"
@@ -104,7 +104,7 @@ msgstr ""
#. Type: select
#. Choices
#: ../openflow-switch.templates:4001
msgid "in-band, out-of-band"
msgid "discovery, in-band, out-of-band"
msgstr ""
#. Type: select
@@ -118,17 +118,26 @@ msgstr ""
#: ../openflow-switch.templates:4002
msgid ""
"The OpenFlow switch must be able to contact the OpenFlow controller over the "
"network. It can do so in one of two ways:"
"network. It can do so in one of three ways:"
msgstr ""
#. Type: select
#. Description
#: ../openflow-switch.templates:4002
msgid ""
"in-band: A single network is used for OpenFlow traffic and other data "
"discovery: A single network is used for OpenFlow traffic and other data "
"traffic; that is, the switch contacts the controller over one of the network "
"devices selected as OpenFlow switch netdevs in the previous question. This "
"is the most common case."
"devices selected as OpenFlow switch network devices in the previous "
"question. The switch automatically determines the location of the "
"controller using a DHCP request with an OpenFlow-specific vendor option. "
"This is the most common case."
msgstr ""
#. Type: select
#. Description
#: ../openflow-switch.templates:4002
msgid ""
"in-band: As above, but the location of the controller is manually configured."
msgstr ""
#. Type: select
@@ -141,15 +150,87 @@ msgid ""
"OpenFlow switch netdev in the previous question."
msgstr ""
#. Type: string
#. Type: note
#. Description
#: ../openflow-switch.templates:5001
msgid "Preparing to discover controller."
msgstr ""
#. Type: note
#. Description
#: ../openflow-switch.templates:5001
msgid ""
"The setup program will now attempt to discover the OpenFlow controller. "
"Controller discovery may take up to 30 seconds. Please be patient."
msgstr ""
#. Type: note
#. Description
#: ../openflow-switch.templates:5001
msgid ""
"See secchan(8) for instructions on how to configure a DHCP server for "
"controller discovery."
msgstr ""
#. Type: error
#. Description
#: ../openflow-switch.templates:6001
msgid "Controller discovery failed."
msgstr ""
#. Type: error
#. Description
#: ../openflow-switch.templates:6001
msgid "The controller's location could not be determined automatically."
msgstr ""
#. Type: error
#. Description
#: ../openflow-switch.templates:6001
msgid ""
"Ensure that the OpenFlow DHCP server is properly configured. See secchan(8) "
"for instructions on how to configure a DHCP server for controller discovery."
msgstr ""
#. Type: boolean
#. Description
#: ../openflow-switch.templates:7001
msgid "Use discovered settings?"
msgstr ""
#. Type: boolean
#. Description
#: ../openflow-switch.templates:7001
msgid "Controller discovery obtained the following settings:"
msgstr ""
#. Type: boolean
#. Description
#: ../openflow-switch.templates:7001
msgid "Controller location: ${controller-vconn}"
msgstr ""
#. Type: boolean
#. Description
#: ../openflow-switch.templates:7001
msgid "PKI URL: ${pki-uri}"
msgstr ""
#. Type: boolean
#. Description
#: ../openflow-switch.templates:7001
msgid "Please verify that these settings are correct."
msgstr ""
#. Type: string
#. Description
#: ../openflow-switch.templates:8001
msgid "Switch IP address:"
msgstr ""
#. Type: string
#. Description
#: ../openflow-switch.templates:5001
#: ../openflow-switch.templates:8001
msgid ""
"For in-band communication with the controller, the OpenFlow switch must be "
"able to determine its own IP address. Its IP address may be configured "
@@ -158,13 +239,13 @@ msgstr ""
#. Type: string
#. Description
#: ../openflow-switch.templates:5001
#: ../openflow-switch.templates:8001
msgid "For static configuration, specify the switch's IP address as a string."
msgstr ""
#. Type: string
#. Description
#: ../openflow-switch.templates:5001
#: ../openflow-switch.templates:8001
msgid ""
"For dynamic configuration with DHCP (the most common case), specify \"dhcp"
"\". Configuration with DHCP will only work reliably if the network topology "
@@ -174,13 +255,13 @@ msgstr ""
#. Type: error
#. Description
#: ../openflow-switch.templates:6001
#: ../openflow-switch.templates:9001
msgid "The switch IP address is invalid."
msgstr ""
#. Type: error
#. Description
#: ../openflow-switch.templates:6001
#: ../openflow-switch.templates:9001
msgid ""
"The switch IP address must specified as \"dhcp\" or a valid IP address in "
"dotted-octet form (e.g. \"1.2.3.4\")."
@@ -188,13 +269,13 @@ msgstr ""
#. Type: string
#. Description
#: ../openflow-switch.templates:7001
#: ../openflow-switch.templates:10001
msgid "Controller location:"
msgstr ""
#. Type: string
#. Description
#: ../openflow-switch.templates:7001
#: ../openflow-switch.templates:10001
msgid ""
"Specify how the OpenFlow switch should connect to the OpenFlow controller. "
"The value should be in form \"ssl:HOST[:PORT]\" to connect to the controller "
@@ -204,13 +285,13 @@ msgstr ""
#. Type: error
#. Description
#: ../openflow-switch.templates:8001
#: ../openflow-switch.templates:11001
msgid "The controller location is invalid."
msgstr ""
#. Type: error
#. Description
#: ../openflow-switch.templates:8001
#: ../openflow-switch.templates:11001
msgid ""
"The controller location must be specifed as \"ssl:HOST[:PORT]\" to connect "
"to the controller over SSL (recommended for security) or \"tcp:HOST[:PORT]\" "
@@ -219,22 +300,30 @@ msgstr ""
#. Type: string
#. Description
#: ../openflow-switch.templates:9001
msgid "OpenFlow PKI server host name:"
#: ../openflow-switch.templates:12001
msgid "OpenFlow PKI server host name or URL:"
msgstr ""
#. Type: string
#. Description
#: ../openflow-switch.templates:9001
#: ../openflow-switch.templates:12001
msgid ""
"Specify the host name or IP address of the server that hosts the OpenFlow "
"public key infrastructure (PKI). This is usually the same host as the "
"OpenFlow controller."
"Specify a URL to the OpenFlow public key infrastructure (PKI). If a host "
"name or IP address is specified in place of a URL, then http://<host>/"
"openflow/pki/ will be used, where <host> is the specified host name or IP "
"address."
msgstr ""
#. Type: string
#. Description
#: ../openflow-switch.templates:9001
#: ../openflow-switch.templates:12001
msgid ""
"The OpenFlow PKI is usually on the same machine as the OpenFlow controller."
msgstr ""
#. Type: string
#. Description
#: ../openflow-switch.templates:12001
msgid ""
"The setup process will connect to the OpenFlow PKI server over HTTP, using "
"the system's configured default HTTP proxy (if any)."
@@ -242,22 +331,22 @@ msgstr ""
#. Type: error
#. Description
#: ../openflow-switch.templates:10001
#: ../openflow-switch.templates:13001
msgid "The switch CA certificate could not be retrieved."
msgstr ""
#. Type: error
#. Description
#: ../openflow-switch.templates:10001
#: ../openflow-switch.templates:13001
msgid "Retrieval of ${url} failed, with the following status: \"${error}\"."
msgstr ""
#. Type: error
#. Description
#: ../openflow-switch.templates:10001
#: ../openflow-switch.templates:13001
msgid ""
"Ensure that the OpenFlow PKI server is correctly configured and available on "
"${pki-host}. If the system is configured to use an HTTP proxy, also make "
"Ensure that the OpenFlow PKI server is correctly configured and available at "
"${pki-uri}. If the system is configured to use an HTTP proxy, also make "
"sure that the HTTP proxy is available and that the PKI server can be reached "
"through it."
msgstr ""
@@ -268,20 +357,20 @@ msgstr ""
#. Choices
#. Type: select
#. Choices
#: ../openflow-switch.templates:11001 ../openflow-switch.templates:12001
#: ../openflow-switch.templates:14001
#: ../openflow-switch.templates:14001 ../openflow-switch.templates:15001
#: ../openflow-switch.templates:17001
msgid "yes, no"
msgstr ""
#. Type: select
#. Description
#: ../openflow-switch.templates:11002
#: ../openflow-switch.templates:14002
msgid "Is ${fingerprint} the controller CA's fingerprint?"
msgstr ""
#. Type: select
#. Description
#: ../openflow-switch.templates:11002
#: ../openflow-switch.templates:14002
msgid ""
"If a man-in-the-middle attack is possible in your network environment, check "
"that the controller CA's fingerprint is really ${fingerprint}. Answer \"yes"
@@ -290,7 +379,7 @@ msgstr ""
#. Type: select
#. Description
#: ../openflow-switch.templates:11002
#: ../openflow-switch.templates:14002
msgid ""
"If a man-in-the-middle attack is not a concern, there is no need to verify "
"the fingerprint. Simply answer \"yes\"."
@@ -298,13 +387,13 @@ msgstr ""
#. Type: select
#. Description
#: ../openflow-switch.templates:12002
#: ../openflow-switch.templates:15002
msgid "Send certificate request to switch CA?"
msgstr ""
#. Type: select
#. Description
#: ../openflow-switch.templates:12002
#: ../openflow-switch.templates:15002
msgid ""
"Before it can connect to the controller over SSL, the OpenFlow switch's key "
"must be signed by the switch certificate authority (CA) located on the "
@@ -314,7 +403,7 @@ msgstr ""
#. Type: select
#. Description
#: ../openflow-switch.templates:12002
#: ../openflow-switch.templates:15002
msgid ""
"Answer \"yes\" to send a signing request to the switch CA now. This is "
"ordinarily the correct choice. There is no harm in sending a given signing "
@@ -323,7 +412,7 @@ msgstr ""
#. Type: select
#. Description
#: ../openflow-switch.templates:12002
#: ../openflow-switch.templates:15002
msgid ""
"Answer \"no\" to skip sending a signing request to the switch CA. Unless the "
"request has already been sent to the switch CA, manual sending of the "
@@ -332,33 +421,33 @@ msgstr ""
#. Type: error
#. Description
#: ../openflow-switch.templates:13001
#: ../openflow-switch.templates:16001
msgid "The certificate request could not be sent."
msgstr ""
#. Type: error
#. Description
#: ../openflow-switch.templates:13001
#: ../openflow-switch.templates:16001
msgid "Posting to ${url} failed, with the following status: \"${error}\"."
msgstr ""
#. Type: error
#. Description
#: ../openflow-switch.templates:13001
#: ../openflow-switch.templates:16001
msgid ""
"Ensure that the OpenFlow PKI server is correctly configured and available on "
"${pki-host}."
"Ensure that the OpenFlow PKI server is correctly configured and available at "
"${pki-uri}."
msgstr ""
#. Type: select
#. Description
#: ../openflow-switch.templates:14002
#: ../openflow-switch.templates:17002
msgid "Fetch signed switch certificate from PKI server?"
msgstr ""
#. Type: select
#. Description
#: ../openflow-switch.templates:14002
#: ../openflow-switch.templates:17002
msgid ""
"Before it can connect to the controller over SSL, the OpenFlow switch's key "
"must be signed by the switch certificate authority (CA) located on the "
@@ -368,7 +457,7 @@ msgstr ""
#. Type: select
#. Description
#: ../openflow-switch.templates:14002
#: ../openflow-switch.templates:17002
msgid ""
"At this point, a signing request has been sent to the switch CA (or sending "
"a request has been manually skipped), but the signed certificate has not yet "
@@ -378,7 +467,7 @@ msgstr ""
#. Type: select
#. Description
#: ../openflow-switch.templates:14002
#: ../openflow-switch.templates:17002
msgid ""
"Answer \"yes\" to attempt to retrieve the signed switch certificate from the "
"switch CA. If the switch certificate request has been signed at the PKI "
@@ -387,7 +476,7 @@ msgstr ""
#. Type: select
#. Description
#: ../openflow-switch.templates:14002
#: ../openflow-switch.templates:17002
msgid ""
"Answer \"no\" to postpone switch configuration. The configuration process "
"must be restarted later, when the switch certificate request has been signed."
@@ -395,13 +484,13 @@ msgstr ""
#. Type: error
#. Description
#: ../openflow-switch.templates:15001
#: ../openflow-switch.templates:18001
msgid "Signed switch certificate could not be retrieved."
msgstr ""
#. Type: error
#. Description
#: ../openflow-switch.templates:15001
#: ../openflow-switch.templates:18001
msgid ""
"The signed switch certificate could not be retrieved from the switch CA: "
"retrieval of ${url} failed, with the following status: \"${error}\"."
@@ -409,23 +498,23 @@ msgstr ""
#. Type: error
#. Description
#: ../openflow-switch.templates:15001
#: ../openflow-switch.templates:18001
msgid ""
"This probably indicates that the switch's certificate request has not yet "
"been signed. If this is the problem, it may be fixed by signing the "
"certificate request at ${pki-host}, then trying to fetch the signed switch "
"certificate request at ${pki-uri}, then trying to fetch the signed switch "
"certificate again."
msgstr ""
#. Type: note
#. Description
#: ../openflow-switch.templates:16001
#: ../openflow-switch.templates:19001
msgid "OpenFlow Switch Setup Finished"
msgstr ""
#. Type: note
#. Description
#: ../openflow-switch.templates:16001
#: ../openflow-switch.templates:19001
msgid ""
"Setup of this OpenFlow switch is finished. Complete the setup procedure to "
"enable the switch."
+10 -7
View File
@@ -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
+7
View File
@@ -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
View File
@@ -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 */
+48
View File
@@ -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 */
+1
View File
@@ -34,6 +34,7 @@
#ifndef DAEMON_H
#define DAEMON_H 1
char *make_pidfile_name(const char *name);
void set_pidfile(const char *name);
void set_detach(void);
void daemonize(void);
+66
View File
@@ -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
View File
@@ -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 */
-1
View File
@@ -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 */
+1
View File
@@ -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 *);
+1 -2
View File
@@ -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
View File
@@ -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 */
-2
View File
@@ -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
View File
@@ -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
View File
@@ -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;
+44
View File
@@ -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
View File
@@ -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 */
+4
View File
@@ -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 */
+50
View File
@@ -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 */
+49
View File
@@ -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 */
+1
View File
@@ -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);
+7
View File
@@ -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 */
+50
View File
@@ -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
View File
@@ -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
View File
@@ -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
View File
@@ -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
View File
@@ -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;
}
+1
View File
@@ -31,6 +31,7 @@
* derivatives without specific, written prior permission.
*/
#include <config.h>
#include "command-line.h"
#include <getopt.h>
#include <limits.h>
+115
View File
@@ -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
View File
@@ -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
View File
File diff suppressed because it is too large. Load diff
+825
View File
@@ -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
View File
@@ -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 },
+7
View File
@@ -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
View File
@@ -30,6 +30,7 @@
* advertising or publicity pertaining to the Software or any
* derivatives without specific, written prior permission.
*/
#include <config.h>
#include "fatal-signal.h"
#include <assert.h>
#include <errno.h>
@@ -42,7 +43,7 @@
#include "util.h"
/* Signals to catch. */
static const int fatal_signals[] = { SIGTERM, SIGINT, SIGHUP };
static const int fatal_signals[] = { SIGTERM, SIGINT, SIGHUP, SIGALRM };
/* Signals to catch as a sigset_t. */
static sigset_t fatal_signal_set;
+1
View File
@@ -31,6 +31,7 @@
* derivatives without specific, written prior permission.
*/
#include <config.h>
#include "fault.h"
#include <dlfcn.h>
#include <inttypes.h>
+60 -15
View File
@@ -30,6 +30,7 @@
* advertising or publicity pertaining to the Software or any
* derivatives without specific, written prior permission.
*/
#include <config.h>
#include <sys/types.h>
#include "flow.h"
#include <inttypes.h>
@@ -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. */
+1
View File
@@ -30,6 +30,7 @@
* advertising or publicity pertaining to the Software or any
* derivatives without specific, written prior permission.
*/
#include <config.h>
#include "hash.h"
uint32_t
+30 -9
View File
@@ -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));
}
+1
View File
@@ -30,6 +30,7 @@
* advertising or publicity pertaining to the Software or any
* derivatives without specific, written prior permission.
*/
#include <config.h>
#include "list.h"
#include <assert.h>
+1
View File
@@ -31,6 +31,7 @@
* derivatives without specific, written prior permission.
*/
#include <config.h>
#include "mac-learning.h"
#include <assert.h>
+240 -90
View File
@@ -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
View File
@@ -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
View File
@@ -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'
+1
View File
@@ -31,6 +31,7 @@
* derivatives without specific, written prior permission.
*/
#include <config.h>
#include "poll-loop.h"
#include <assert.h>
#include <errno.h>
+1
View File
@@ -31,6 +31,7 @@
* derivatives without specific, written prior permission.
*/
#include <config.h>
#include "queue.h"
#include <assert.h>
#include "buffer.h"
+79
View File
@@ -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
View File
@@ -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;
}
+132
View File
@@ -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
View File
@@ -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
View File
@@ -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
View File
@@ -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)
{
+354
View File
@@ -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
View File
@@ -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
};
+128
View File
@@ -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
View File
@@ -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
View File
@@ -31,6 +31,7 @@
* derivatives without specific, written prior permission.
*/
#include <config.h>
#include "vlog-socket.h"
#include <errno.h>
#include <sys/un.h>
@@ -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