Andreas Beckmann debian@abeckmann.de
Andrei Andone andrei.andone@softvision.ro
Anton Matsiuk anton.matsiuk@gmail.com
+Anuprem Chalvadi achalvadi@vmware.com
Atzm Watanabe atzm@stratosphere.co.jp
Bastian Blank waldi@debian.org
Ben Basler bbasler@nicira.com
- Python 2.x, for x >= 4.
-To compile the kernel module on Linux, you must also install the
-following. If you cannot build or install the kernel module, you may
-use the userspace-only implementation, at a cost in performance. The
-userspace implementation may also lack some features. Refer to
-INSTALL.userspace for more information.
+On Linux, you may choose to compile the kernel module that comes with
+the Open vSwitch distribution or to use the kernel module built into
+the Linux kernel (version 3.3 or later). See the FAQ question "What
+features are not available in the Open vSwitch kernel datapath that
+ships as part of the upstream Linux kernel?" for more information on
+this trade-off. You may also use the userspace-only implementation,
+at some cost in features and performance (see INSTALL.userspace for
+details). To compile the kernel module on Linux, you must also
+install the following:
- A supported Linux kernel version. Please refer to README for a
list of supported versions.
-l*)
lib=`echo "$1" | sed 's/-l//'`
- if [ $lib != "dnsapi" -a $lib != "ws2_32" -a $lib != "wsock32" ]; then
- lib="lib$lib.lib"
- else
- lib="$lib.lib"
- fi
+ lib="$lib.lib"
clopt="$clopt $lib"
linkopt="$linkopt $lib"
/*
- * Copyright (c) 2007-2013 Nicira, Inc.
+ * Copyright (c) 2007-2014 Nicira, Inc.
*
* This program is free software; you can redistribute it and/or
* modify it under the terms of version 2 of the GNU General Public
* block bottom-halves here.
*/
spin_lock_bh(&stats->lock);
- if (time_after(stats->used, *used))
+ if (!*used || time_after(stats->used, *used))
*used = stats->used;
*tcp_flags |= stats->tcp_flags;
ovs_stats->n_packets += stats->packet_count;
-/* Copyright (c) 2008, 2011, 2012, 2013 The Board of Trustees of The Leland Stanford
+/* Copyright (c) 2008, 2011, 2012, 2013, 2014 The Board of Trustees of The Leland Stanford
* Junior University
*
* We are making the OpenFlow specification and associated documentation
*/
/*
- * Copyright (c) 2008, 2009, 2010, 2011 Nicira, Inc.
+ * Copyright (c) 2008-2014 Nicira, Inc.
*
* Licensed under the Apache License, Version 2.0 (the "License");
* you may not use this file except in compliance with the License.
OFP11_VERSION = 0x02,
OFP12_VERSION = 0x03,
OFP13_VERSION = 0x04
+
+ /* When we add real support for these versions, add them to the enum so
+ * that we get compiler warnings everywhere we might forget to provide
+ * support. Until then, keep them as macros to avoid those warnings. */
+#define OFP14_VERSION 0x05
+#define OFP15_VERSION 0x06
};
/* Vendor (aka experimenter) IDs.
};
#define IPPROTO_IP 0
+#define IPPROTO_IPV6 41
#define IPPROTO_HOPOPTS 0
#define IPPROTO_ICMP 1
#define IPPROTO_TCP 6
#define INET6_ADDRSTRLEN 46
+#define IPV6_TCLASS 67
+
static inline ovs_be32 htonl(uint32_t x)
{
return (OVS_FORCE ovs_be32) x;
lib/shash.h \
lib/simap.c \
lib/simap.h \
- lib/signals.c \
- lib/signals.h \
lib/smap.c \
lib/smap.h \
lib/socket-util.c \
lib/sset.h \
lib/stp.c \
lib/stp.h \
- lib/stream-fd.c \
lib/stream-fd.h \
lib/stream-provider.h \
lib/stream-ssl.h \
lib_libopenvswitch_la_SOURCES += \
lib/daemon-windows.c \
lib/getopt_long.c \
- lib/latch-windows.c
+ lib/latch-windows.c \
+ lib/stream-fd-windows.c
else
lib_libopenvswitch_la_SOURCES += \
lib/daemon.c \
- lib/latch.c \
+ lib/latch-unix.c \
+ lib/signals.c \
+ lib/signals.h \
+ lib/stream-fd-unix.c \
lib/stream-unix.c
endif
dpif_linux_enumerate(struct sset *all_dps)
{
struct nl_dump dump;
- struct ofpbuf msg;
+ uint64_t reply_stub[NL_DUMP_BUFSIZE / 8];
+ struct ofpbuf msg, buf;
int error;
error = dpif_linux_init();
return error;
}
+ ofpbuf_use_stub(&buf, reply_stub, sizeof reply_stub);
dpif_linux_dp_dump_start(&dump);
- while (nl_dump_next(&dump, &msg)) {
+ while (nl_dump_next(&dump, &msg, &buf)) {
struct dpif_linux_dp dp;
if (!dpif_linux_dp_from_ofpbuf(&dp, &msg)) {
sset_add(all_dps, dp.name);
}
}
+ ofpbuf_uninit(&buf);
return nl_dump_done(&dump);
}
static struct vlog_rate_limit rl = VLOG_RATE_LIMIT_INIT(5, 20);
switch (vport->type) {
- case OVS_VPORT_TYPE_NETDEV:
- return "system";
+ case OVS_VPORT_TYPE_NETDEV: {
+ const char *type = netdev_get_type_from_name(vport->name);
+
+ return type ? type : "system";
+ }
case OVS_VPORT_TYPE_INTERNAL:
return "internal";
struct dpif_linux_port_state {
struct nl_dump dump;
+ struct ofpbuf buf;
};
static void
*statep = state = xmalloc(sizeof *state);
dpif_linux_port_dump_start__(dpif, &state->dump);
+ ofpbuf_init(&state->buf, NL_DUMP_BUFSIZE);
return 0;
}
static int
dpif_linux_port_dump_next__(const struct dpif *dpif_, struct nl_dump *dump,
- struct dpif_linux_vport *vport)
+ struct dpif_linux_vport *vport,
+ struct ofpbuf *buffer)
{
struct dpif_linux *dpif = dpif_linux_cast(dpif_);
struct ofpbuf buf;
int error;
- if (!nl_dump_next(dump, &buf)) {
+ if (!nl_dump_next(dump, &buf, buffer)) {
return EOF;
}
struct dpif_linux_vport vport;
int error;
- error = dpif_linux_port_dump_next__(dpif, &state->dump, &vport);
+ error = dpif_linux_port_dump_next__(dpif, &state->dump, &vport,
+ &state->buf);
if (error) {
return error;
}
struct dpif_linux_port_state *state = state_;
int error = nl_dump_done(&state->dump);
+ ofpbuf_uninit(&state->buf);
free(state);
return error;
}
}
struct dpif_linux_flow_state {
- struct nl_dump dump;
struct dpif_linux_flow flow;
struct dpif_flow_stats stats;
- struct ofpbuf *buf;
+ struct ofpbuf buffer; /* Always used to store flows. */
+ struct ofpbuf *tmp; /* Used if kernel does not supply actions. */
};
+struct dpif_linux_flow_iter {
+ struct nl_dump dump;
+ atomic_int status;
+};
+
+static void
+dpif_linux_flow_dump_state_init(void **statep)
+{
+ struct dpif_linux_flow_state *state;
+
+ *statep = state = xmalloc(sizeof *state);
+ ofpbuf_init(&state->buffer, NL_DUMP_BUFSIZE);
+ state->tmp = NULL;
+}
+
+static void
+dpif_linux_flow_dump_state_uninit(void *state_)
+{
+ struct dpif_linux_flow_state *state = state_;
+
+ ofpbuf_uninit(&state->buffer);
+ ofpbuf_delete(state->tmp);
+ free(state);
+}
+
static int
-dpif_linux_flow_dump_start(const struct dpif *dpif_, void **statep)
+dpif_linux_flow_dump_start(const struct dpif *dpif_, void **iterp)
{
const struct dpif_linux *dpif = dpif_linux_cast(dpif_);
- struct dpif_linux_flow_state *state;
+ struct dpif_linux_flow_iter *iter;
struct dpif_linux_flow request;
struct ofpbuf *buf;
- *statep = state = xmalloc(sizeof *state);
+ *iterp = iter = xmalloc(sizeof *iter);
dpif_linux_flow_init(&request);
request.cmd = OVS_FLOW_CMD_GET;
buf = ofpbuf_new(1024);
dpif_linux_flow_to_ofpbuf(&request, buf);
- nl_dump_start(&state->dump, NETLINK_GENERIC, buf);
+ nl_dump_start(&iter->dump, NETLINK_GENERIC, buf);
ofpbuf_delete(buf);
-
- state->buf = NULL;
+ atomic_init(&iter->status, 0);
return 0;
}
static int
-dpif_linux_flow_dump_next(const struct dpif *dpif_, void *state_,
+dpif_linux_flow_dump_next(const struct dpif *dpif_, void *iter_, void *state_,
const struct nlattr **key, size_t *key_len,
const struct nlattr **mask, size_t *mask_len,
const struct nlattr **actions, size_t *actions_len,
const struct dpif_flow_stats **stats)
{
+ struct dpif_linux_flow_iter *iter = iter_;
struct dpif_linux_flow_state *state = state_;
struct ofpbuf buf;
int error;
do {
- ofpbuf_delete(state->buf);
- state->buf = NULL;
+ ofpbuf_delete(state->tmp);
+ state->tmp = NULL;
- if (!nl_dump_next(&state->dump, &buf)) {
+ if (!nl_dump_next(&iter->dump, &buf, &state->buffer)) {
return EOF;
}
error = dpif_linux_flow_from_ofpbuf(&state->flow, &buf);
if (error) {
+ atomic_store(&iter->status, error);
return error;
}
if (actions && !state->flow.actions) {
error = dpif_linux_flow_get__(dpif_, state->flow.key,
state->flow.key_len,
- &state->flow, &state->buf);
+ &state->flow, &state->tmp);
if (error == ENOENT) {
VLOG_DBG("dumped flow disappeared on get");
} else if (error) {
return error;
}
-static int
-dpif_linux_flow_dump_done(const struct dpif *dpif OVS_UNUSED, void *state_)
+static bool
+dpif_linux_flow_dump_next_may_destroy_keys(void *state_)
{
struct dpif_linux_flow_state *state = state_;
- int error = nl_dump_done(&state->dump);
- ofpbuf_delete(state->buf);
- free(state);
- return error;
+
+ return state->buffer.size ? false : true;
+}
+
+static int
+dpif_linux_flow_dump_done(const struct dpif *dpif OVS_UNUSED, void *iter_)
+{
+ struct dpif_linux_flow_iter *iter = iter_;
+ int dump_status;
+ unsigned int nl_status = nl_dump_done(&iter->dump);
+
+ atomic_read(&iter->status, &dump_status);
+ atomic_destroy(&iter->status);
+ free(iter);
+ return dump_status ? dump_status : nl_status;
}
static void
struct dpif_linux_vport vport;
size_t keep_channels_nbits;
struct nl_dump dump;
+ uint64_t reply_stub[NL_DUMP_BUFSIZE / 8];
+ struct ofpbuf buf;
int retval = 0;
size_t i;
dpif->n_events = dpif->event_offset = 0;
+ ofpbuf_use_stub(&buf, reply_stub, sizeof reply_stub);
dpif_linux_port_dump_start__(dpif_, &dump);
- while (!dpif_linux_port_dump_next__(dpif_, &dump, &vport)) {
+ while (!dpif_linux_port_dump_next__(dpif_, &dump, &vport, &buf)) {
uint32_t port_no = odp_to_u32(vport.port_no);
struct nl_sock *sock = (port_no < dpif->uc_array_size
? dpif->channels[port_no].sock
nl_sock_destroy(sock);
}
nl_dump_done(&dump);
+ ofpbuf_uninit(&buf);
/* Discard any saved channels that we didn't reuse. */
for (i = 0; i < keep_channels_nbits; i++) {
dpif_linux_flow_put,
dpif_linux_flow_del,
dpif_linux_flow_flush,
+ dpif_linux_flow_dump_state_init,
dpif_linux_flow_dump_start,
dpif_linux_flow_dump_next,
+ dpif_linux_flow_dump_next_may_destroy_keys,
dpif_linux_flow_dump_done,
+ dpif_linux_flow_dump_state_uninit,
dpif_linux_execute,
dpif_linux_operate,
dpif_linux_recv_set,
}
struct dp_netdev_flow_state {
- uint32_t bucket;
- uint32_t offset;
struct dp_netdev_actions *actions;
struct odputil_keybuf keybuf;
struct odputil_keybuf maskbuf;
struct dpif_flow_stats stats;
};
-static int
-dpif_netdev_flow_dump_start(const struct dpif *dpif OVS_UNUSED, void **statep)
+struct dp_netdev_flow_iter {
+ uint32_t bucket;
+ uint32_t offset;
+ int status;
+ struct ovs_mutex mutex;
+};
+
+static void
+dpif_netdev_flow_dump_state_init(void **statep)
{
struct dp_netdev_flow_state *state;
*statep = state = xmalloc(sizeof *state);
- state->bucket = 0;
- state->offset = 0;
state->actions = NULL;
+}
+
+static void
+dpif_netdev_flow_dump_state_uninit(void *state_)
+{
+ struct dp_netdev_flow_state *state = state_;
+
+ dp_netdev_actions_unref(state->actions);
+ free(state);
+}
+
+static int
+dpif_netdev_flow_dump_start(const struct dpif *dpif OVS_UNUSED, void **iterp)
+{
+ struct dp_netdev_flow_iter *iter;
+
+ *iterp = iter = xmalloc(sizeof *iter);
+ iter->bucket = 0;
+ iter->offset = 0;
+ iter->status = 0;
+ ovs_mutex_init(&iter->mutex);
return 0;
}
static int
-dpif_netdev_flow_dump_next(const struct dpif *dpif, void *state_,
+dpif_netdev_flow_dump_next(const struct dpif *dpif, void *iter_, void *state_,
const struct nlattr **key, size_t *key_len,
const struct nlattr **mask, size_t *mask_len,
const struct nlattr **actions, size_t *actions_len,
const struct dpif_flow_stats **stats)
{
+ struct dp_netdev_flow_iter *iter = iter_;
struct dp_netdev_flow_state *state = state_;
struct dp_netdev *dp = get_dp_netdev(dpif);
struct dp_netdev_flow *netdev_flow;
- struct hmap_node *node;
+ int error;
- fat_rwlock_rdlock(&dp->cls.rwlock);
- node = hmap_at_position(&dp->flow_table, &state->bucket, &state->offset);
- if (node) {
- netdev_flow = CONTAINER_OF(node, struct dp_netdev_flow, node);
- dp_netdev_flow_ref(netdev_flow);
+ ovs_mutex_lock(&iter->mutex);
+ error = iter->status;
+ if (!error) {
+ struct hmap_node *node;
+
+ fat_rwlock_rdlock(&dp->cls.rwlock);
+ node = hmap_at_position(&dp->flow_table, &iter->bucket, &iter->offset);
+ if (node) {
+ netdev_flow = CONTAINER_OF(node, struct dp_netdev_flow, node);
+ dp_netdev_flow_ref(netdev_flow);
+ }
+ fat_rwlock_unlock(&dp->cls.rwlock);
+ if (!node) {
+ iter->status = error = EOF;
+ }
}
- fat_rwlock_unlock(&dp->cls.rwlock);
- if (!node) {
- return EOF;
+ ovs_mutex_unlock(&iter->mutex);
+ if (error) {
+ return error;
}
if (key) {
}
static int
-dpif_netdev_flow_dump_done(const struct dpif *dpif OVS_UNUSED, void *state_)
+dpif_netdev_flow_dump_done(const struct dpif *dpif OVS_UNUSED, void *iter_)
{
- struct dp_netdev_flow_state *state = state_;
+ struct dp_netdev_flow_iter *iter = iter_;
- dp_netdev_actions_unref(state->actions);
- free(state);
+ ovs_mutex_destroy(&iter->mutex);
+ free(iter);
return 0;
}
}
/* Extract flow key. */
- flow_extract(execute->packet, md->skb_priority, md->pkt_mark, &md->tunnel,
- (union flow_in_port *)&md->in_port, &key);
+ flow_extract(execute->packet, md, &key);
ovs_rwlock_rdlock(&dp->port_rwlock);
dp_netdev_execute_actions(dp, &key, execute->packet, md, execute->actions,
if (!error) {
struct pkt_metadata md
= PKT_METADATA_INITIALIZER(port->port_no);
- dp_netdev_port_input(dp, &packet, &md);
+ dp_netdev_port_input(dp, &packet, &md);
received_anything = true;
} else if (error != EAGAIN && error != EOPNOTSUPP) {
static struct vlog_rate_limit rl
if (packet->size < ETH_HEADER_LEN) {
return;
}
- flow_extract(packet, md->skb_priority, md->pkt_mark, &md->tunnel,
- (union flow_in_port *)&md->in_port, &key);
+ flow_extract(packet, md, &key);
netdev_flow = dp_netdev_lookup_flow(dp, &key);
if (netdev_flow) {
struct dp_netdev_actions *actions;
dpif_netdev_flow_put, \
dpif_netdev_flow_del, \
dpif_netdev_flow_flush, \
+ dpif_netdev_flow_dump_state_init, \
dpif_netdev_flow_dump_start, \
dpif_netdev_flow_dump_next, \
+ NULL, \
dpif_netdev_flow_dump_done, \
+ dpif_netdev_flow_dump_state_uninit, \
dpif_netdev_execute, \
NULL, /* operate */ \
dpif_netdev_recv_set, \
* packets. */
int (*flow_flush)(struct dpif *dpif);
- /* Attempts to begin dumping the flows in a dpif. On success, returns 0
- * and initializes '*statep' with any data needed for iteration. On
- * failure, returns a positive errno value. */
- int (*flow_dump_start)(const struct dpif *dpif, void **statep);
+ /* Allocates thread-local state for use with the function 'flow_dump_next'.
+ * On return, initializes '*statep' with any private data needed for
+ * iteration. */
+ void (*flow_dump_state_init)(void **statep);
- /* Attempts to retrieve another flow from 'dpif' for 'state', which was
- * initialized by a successful call to the 'flow_dump_start' function for
- * 'dpif'. On success, updates the output parameters as described below
- * and returns 0. Returns EOF if the end of the flow table has been
- * reached, or a positive errno value on error. This function will not be
- * called again once it returns nonzero within a given iteration (but the
- * 'flow_dump_done' function will be called afterward).
+ /* Attempts to begin dumping the flows in a dpif. On success, returns 0
+ * and initializes '*iterp' with any shared data needed for iteration.
+ * On failure, returns a positive errno value. */
+ int (*flow_dump_start)(const struct dpif *dpif, void **iterp);
+
+ /* Attempts to retrieve another flow from 'dpif' for 'iter', using
+ * 'state' for storage. 'iter' must have been initialized by a successful
+ * call to the 'flow_dump_start' function for 'dpif'. 'state' must have
+ * been initialised with a call to the 'flow_dump_state_init' function for
+ * 'dpif.
+ *
+ * On success, updates the output parameters as described below and returns
+ * 0. Returns EOF if the end of the flow table has been reached, or a
+ * positive errno value on error. Multiple threads may use the same 'dpif'
+ * and 'iter' with this function, but all other parameters must be
+ * different for each thread. If this function returns non-zero,
+ * subsequent calls with the same arguments will also return non-zero.
*
* On success:
*
* All of the returned data is owned by 'dpif', not by the caller, and the
* caller must not modify or free it. 'dpif' must guarantee that it
* remains accessible and unchanging until at least the next call to
- * 'flow_dump_next' or 'flow_dump_done' for 'state'. */
- int (*flow_dump_next)(const struct dpif *dpif, void *state,
+ * 'flow_dump_next' or 'flow_dump_done' for 'iter' and 'state'. */
+ int (*flow_dump_next)(const struct dpif *dpif, void *iter, void *state,
const struct nlattr **key, size_t *key_len,
const struct nlattr **mask, size_t *mask_len,
const struct nlattr **actions, size_t *actions_len,
const struct dpif_flow_stats **stats);
- /* Releases resources from 'dpif' for 'state', which was initialized by a
- * successful call to the 'flow_dump_start' function for 'dpif'. */
- int (*flow_dump_done)(const struct dpif *dpif, void *state);
+ /* Determines whether the next call to 'flow_dump_next' with 'state' will
+ * modify or free the keys that it previously returned. 'state' must have
+ * been initialized by a call to 'flow_dump_state_init' for 'dpif'.
+ *
+ * 'dpif' guarantees that data returned by flow_dump_next() will remain
+ * accessible and unchanging until the next call. This function provides a
+ * way for callers to determine whether that guarantee extends beyond the
+ * next call.
+ *
+ * Returns true if the next call to flow_dump_next() is expected to be
+ * destructive to previously returned keys for 'state', false otherwise. */
+ bool (*flow_dump_next_may_destroy_keys)(void *state);
+
+ /* Releases resources from 'dpif' for 'iter', which was initialized by a
+ * successful call to the 'flow_dump_start' function for 'dpif'. Callers
+ * must ensure that this function is called once within a given iteration,
+ * as the final flow dump operation. */
+ int (*flow_dump_done)(const struct dpif *dpif, void *iter);
+
+ /* Releases 'state' which was initialized by a call to the
+ * 'flow_dump_state_init' function for this 'dpif'. */
+ void (*flow_dump_state_uninit)(void *statep);
/* Performs the 'execute->actions_len' bytes of actions in
* 'execute->actions' on the Ethernet frame in 'execute->packet'
return dpif_flow_del__(dpif, &del);
}
-/* Initializes 'dump' to begin dumping the flows in a dpif.
- *
- * This function provides no status indication. An error status for the entire
- * dump operation is provided when it is completed by calling
- * dpif_flow_dump_done().
- */
+/* Allocates thread-local state for use with the 'flow_dump_next' function for
+ * 'dpif'. On return, initializes '*statep' with any private data needed for
+ * iteration. */
void
+dpif_flow_dump_state_init(const struct dpif *dpif, void **statep)
+{
+ dpif->dpif_class->flow_dump_state_init(statep);
+}
+
+/* Releases 'state' which was initialized by a call to the
+ * 'flow_dump_state_init' function for 'dpif'. */
+void
+dpif_flow_dump_state_uninit(const struct dpif *dpif, void *state)
+{
+ dpif->dpif_class->flow_dump_state_uninit(state);
+}
+
+/* Initializes 'dump' to begin dumping the flows in a dpif. On sucess,
+ * initializes 'dump' with any data needed for iteration and returns 0.
+ * Otherwise, returns a positive errno value describing the problem. */
+int
dpif_flow_dump_start(struct dpif_flow_dump *dump, const struct dpif *dpif)
{
+ int error;
dump->dpif = dpif;
- dump->error = dpif->dpif_class->flow_dump_start(dpif, &dump->state);
- log_operation(dpif, "flow_dump_start", dump->error);
+ error = dpif->dpif_class->flow_dump_start(dpif, &dump->iter);
+ log_operation(dpif, "flow_dump_start", error);
+ return error;
}
-/* Attempts to retrieve another flow from 'dump', which must have been
- * initialized with dpif_flow_dump_start(). On success, updates the output
- * parameters as described below and returns true. Otherwise, returns false.
- * Failure might indicate an actual error or merely the end of the flow table.
- * An error status for the entire dump operation is provided when it is
- * completed by calling dpif_flow_dump_done().
+/* Attempts to retrieve another flow from 'dump', using 'state' for
+ * thread-local storage. 'dump' must have been initialized with a successful
+ * call to dpif_flow_dump_start(), and 'state' must have been initialized with
+ * dpif_flow_state_init().
+ *
+ * On success, updates the output parameters as described below and returns
+ * true. Otherwise, returns false. Failure might indicate an actual error or
+ * merely the end of the flow table. An error status for the entire dump
+ * operation is provided when it is completed by calling dpif_flow_dump_done().
+ * Multiple threads may use the same 'dump' with this function, but all other
+ * parameters must not be shared.
*
* On success, if 'key' and 'key_len' are nonnull then '*key' and '*key_len'
* will be set to Netlink attributes with types OVS_KEY_ATTR_* representing the
* All of the returned data is owned by 'dpif', not by the caller, and the
* caller must not modify or free it. 'dpif' guarantees that it remains
* accessible and unchanging until at least the next call to 'flow_dump_next'
- * or 'flow_dump_done' for 'dump'. */
+ * or 'flow_dump_done' for 'dump' and 'state'. */
bool
-dpif_flow_dump_next(struct dpif_flow_dump *dump,
+dpif_flow_dump_next(struct dpif_flow_dump *dump, void *state,
const struct nlattr **key, size_t *key_len,
const struct nlattr **mask, size_t *mask_len,
const struct nlattr **actions, size_t *actions_len,
const struct dpif_flow_stats **stats)
{
const struct dpif *dpif = dump->dpif;
- int error = dump->error;
+ int error;
- if (!error) {
- error = dpif->dpif_class->flow_dump_next(dpif, dump->state,
- key, key_len,
- mask, mask_len,
- actions, actions_len,
- stats);
- if (error) {
- dpif->dpif_class->flow_dump_done(dpif, dump->state);
- }
- }
+ error = dpif->dpif_class->flow_dump_next(dpif, dump->iter, state,
+ key, key_len, mask, mask_len,
+ actions, actions_len, stats);
if (error) {
if (key) {
*key = NULL;
*stats = NULL;
}
}
- if (!dump->error) {
- if (error == EOF) {
- VLOG_DBG_RL(&dpmsg_rl, "%s: dumped all flows", dpif_name(dpif));
- } else if (should_log_flow_message(error)) {
- log_flow_message(dpif, error, "flow_dump",
- key ? *key : NULL, key ? *key_len : 0,
- mask ? *mask : NULL, mask ? *mask_len : 0,
- stats ? *stats : NULL, actions ? *actions : NULL,
- actions ? *actions_len : 0);
- }
+ if (error == EOF) {
+ VLOG_DBG_RL(&dpmsg_rl, "%s: dumped all flows", dpif_name(dpif));
+ } else if (should_log_flow_message(error)) {
+ log_flow_message(dpif, error, "flow_dump",
+ key ? *key : NULL, key ? *key_len : 0,
+ mask ? *mask : NULL, mask ? *mask_len : 0,
+ stats ? *stats : NULL, actions ? *actions : NULL,
+ actions ? *actions_len : 0);
}
- dump->error = error;
return !error;
}
+/* Determines whether the next call to 'dpif_flow_dump_next' for 'dump' and
+ * 'state' will modify or free the keys that it previously returned. 'state'
+ * must have been initialized by a call to 'dpif_flow_dump_state_init' for
+ * 'dump'.
+ *
+ * 'dpif' guarantees that data returned by flow_dump_next() will remain
+ * accessible and unchanging until the next call. This function provides a way
+ * for callers to determine whether that guarantee extends beyond the next
+ * call.
+ *
+ * Returns true if the next call to flow_dump_next() is expected to be
+ * destructive to previously returned keys for 'state', false otherwise. */
+bool
+dpif_flow_dump_next_may_destroy_keys(struct dpif_flow_dump *dump, void *state)
+{
+ const struct dpif *dpif = dump->dpif;
+ return (dpif->dpif_class->flow_dump_next_may_destroy_keys
+ ? dpif->dpif_class->flow_dump_next_may_destroy_keys(state)
+ : true);
+}
+
/* Completes flow table dump operation 'dump', which must have been initialized
- * with dpif_flow_dump_start(). Returns 0 if the dump operation was
- * error-free, otherwise a positive errno value describing the problem. */
+ * with a successful call to dpif_flow_dump_start(). Returns 0 if the dump
+ * operation was error-free, otherwise a positive errno value describing the
+ * problem. */
int
dpif_flow_dump_done(struct dpif_flow_dump *dump)
{
const struct dpif *dpif = dump->dpif;
- if (!dump->error) {
- dump->error = dpif->dpif_class->flow_dump_done(dpif, dump->state);
- log_operation(dpif, "flow_dump_done", dump->error);
- }
- return dump->error == EOF ? 0 : dump->error;
+ int error = dpif->dpif_class->flow_dump_done(dpif, dump->iter);
+ log_operation(dpif, "flow_dump_done", error);
+ return error == EOF ? 0 : error;
}
struct dpif_execute_helper_aux {
* thread-safe: they may be called from different threads only on
* different dpif objects.
*
- * - Functions that operate on struct dpif_port_dump or struct
- * dpif_flow_dump are conditionally thread-safe with respect to those
- * objects. That is, one may dump ports or flows from any number of
- * threads at once, but each thread must use its own struct dpif_port_dump
- * or dpif_flow_dump.
+ * - dpif_flow_dump_next() is conditionally thread-safe: It may be called
+ * from different threads with the same 'struct dpif_flow_dump', but all
+ * other parameters must be different for each thread.
+ *
+ * - dpif_flow_dump_done() is conditionally thread-safe: All threads that
+ * share the same 'struct dpif_flow_dump' must have finished using it.
+ * This function must then be called exactly once for a particular
+ * dpif_flow_dump to finish the corresponding flow dump operation.
+ *
+ * - Functions that operate on 'struct dpif_port_dump' are conditionally
+ * thread-safe with respect to those objects. That is, one may dump ports
+ * from any number of threads at once, but each thread must use its own
+ * struct dpif_port_dump.
*/
#ifndef DPIF_H
#define DPIF_H 1
struct dpif_flow_dump {
const struct dpif *dpif;
- int error;
- void *state;
+ void *iter;
};
-void dpif_flow_dump_start(struct dpif_flow_dump *, const struct dpif *);
-bool dpif_flow_dump_next(struct dpif_flow_dump *,
+void dpif_flow_dump_state_init(const struct dpif *, void **statep);
+int dpif_flow_dump_start(struct dpif_flow_dump *, const struct dpif *);
+bool dpif_flow_dump_next(struct dpif_flow_dump *, void *state,
const struct nlattr **key, size_t *key_len,
const struct nlattr **mask, size_t *mask_len,
const struct nlattr **actions, size_t *actions_len,
const struct dpif_flow_stats **);
+bool dpif_flow_dump_next_may_destroy_keys(struct dpif_flow_dump *dump,
+ void *state);
int dpif_flow_dump_done(struct dpif_flow_dump *);
+void dpif_flow_dump_state_uninit(const struct dpif *, void *state);
\f
/* Operation batching interface.
*
VLOG_DEFINE_THIS_MODULE(fatal_signal);
/* Signals to catch. */
+#ifndef _WIN32
static const int fatal_signals[] = { SIGTERM, SIGINT, SIGHUP, SIGALRM };
+#else
+static const int fatal_signals[] = { SIGTERM };
+#endif
/* Hooks to call upon catching a signal */
struct hook {
static size_t n_hooks;
static int signal_fds[2];
+static HANDLE wevent;
static volatile sig_atomic_t stored_sig_nr = SIG_ATOMIC_MAX;
static struct ovs_mutex mutex;
static void atexit_handler(void);
static void call_hooks(int sig_nr);
+#ifdef _WIN32
+static BOOL WINAPI ConsoleHandlerRoutine(DWORD dwCtrlType);
+#endif
/* Initializes the fatal signal handling module. Calling this function is
* optional, because calling any other function in the module will also
inited = true;
ovs_mutex_init_recursive(&mutex);
+#ifndef _WIN32
xpipe_nonblocking(signal_fds);
+#else
+ wevent = CreateEvent(NULL, TRUE, FALSE, NULL);
+ if (!wevent) {
+ char *msg_buf = ovs_lasterror_to_string();
+ VLOG_FATAL("Failed to create a event (%s).", msg_buf);
+ }
+
+ /* Register a function to handle Ctrl+C. */
+ SetConsoleCtrlHandler(ConsoleHandlerRoutine, true);
+#endif
for (i = 0; i < ARRAY_SIZE(fatal_signals); i++) {
int sig_nr = fatal_signals[i];
+#ifndef _WIN32
struct sigaction old_sa;
xsigaction(sig_nr, NULL, &old_sa);
&& signal(sig_nr, fatal_signal_handler) == SIG_ERR) {
VLOG_FATAL("signal failed (%s)", ovs_strerror(errno));
}
+#else
+ if (signal(sig_nr, fatal_signal_handler) == SIG_ERR) {
+ VLOG_FATAL("signal failed (%s)", ovs_strerror(errno));
+ }
+#endif
}
atexit(atexit_handler);
}
void
fatal_signal_handler(int sig_nr)
{
+#ifndef _WIN32
ignore(write(signal_fds[1], "", 1));
+#else
+ SetEvent(wevent);
+#endif
stored_sig_nr = sig_nr;
}
ovs_mutex_lock(&mutex);
+#ifndef _WIN32
VLOG_WARN("terminating with signal %d (%s)",
(int)sig_nr, signal_name(sig_nr, namebuf, sizeof namebuf));
+#else
+ VLOG_WARN("terminating with signal %d", (int)sig_nr);
+#endif
call_hooks(sig_nr);
/* Re-raise the signal with the default handling so that the program
fatal_signal_wait(void)
{
fatal_signal_init();
- poll_fd_wait(signal_fds[0], POLLIN);
+ poll_fd_wait_event(signal_fds[0], wevent, POLLIN);
+}
+
+void
+fatal_ignore_sigpipe(void)
+{
+#ifndef _WIN32
+ signal(SIGPIPE, SIG_IGN);
+#endif
}
static void
}
}
}
+
+#ifdef _WIN32
+BOOL WINAPI ConsoleHandlerRoutine(DWORD dwCtrlType)
+{
+ stored_sig_nr = SIGINT;
+ SetEvent(wevent);
+ return true;
+}
+#endif
\f
/* Files to delete on exit. */
static struct sset files = SSET_INITIALIZER(&files);
void fatal_signal_fork(void);
void fatal_signal_run(void);
void fatal_signal_wait(void);
+void fatal_ignore_sigpipe(void);
/* Convenience functions for unlinking files upon termination.
*
#include "ofpbuf.h"
#include "openflow/openflow.h"
#include "packets.h"
+#include "odp-util.h"
#include "random.h"
#include "unaligned.h"
}
-/* Initializes 'flow' members from 'packet', 'skb_priority', 'tnl', and
- * 'in_port'.
+/* Initializes 'flow' members from 'packet' and 'md'
*
* Initializes 'packet' header pointers as follows:
*
* present and has a correct length, and otherwise NULL.
*/
void
-flow_extract(struct ofpbuf *packet, uint32_t skb_priority, uint32_t pkt_mark,
- const struct flow_tnl *tnl, const union flow_in_port *in_port,
+flow_extract(struct ofpbuf *packet, const struct pkt_metadata *md,
struct flow *flow)
{
struct ofpbuf b = *packet;
memset(flow, 0, sizeof *flow);
- if (tnl) {
- ovs_assert(tnl != &flow->tunnel);
- flow->tunnel = *tnl;
- }
- if (in_port) {
- flow->in_port = *in_port;
+ if (md) {
+ flow->tunnel = md->tunnel;
+ if (md->in_port.odp_port != ODPP_NONE) {
+ flow->in_port = md->in_port;
+ };
+ flow->skb_priority = md->skb_priority;
+ flow->pkt_mark = md->pkt_mark;
}
- flow->skb_priority = skb_priority;
- flow->pkt_mark = pkt_mark;
packet->l2 = b.data;
packet->l2_5 = NULL;
struct flow_wildcards;
struct minimask;
struct ofpbuf;
+struct pkt_metadata;
/* This sequence number should be incremented whenever anything involving flows
* or the wildcarding of flows changes. This will cause build assertion
* numbers and other times datapath (dpif) port numbers. This union allows
* access to both. */
union flow_in_port {
- ofp_port_t ofp_port;
odp_port_t odp_port;
+ ofp_port_t ofp_port;
};
/* Maximum number of supported MPLS labels. */
ofp_port_t in_port; /* OpenFlow port or zero. */
};
-void flow_extract(struct ofpbuf *, uint32_t priority, uint32_t mark,
- const struct flow_tnl *, const union flow_in_port *in_port,
+void flow_extract(struct ofpbuf *, const struct pkt_metadata *md,
struct flow *);
void flow_zero_wildcards(struct flow *, const struct flow_wildcards *);
struct ofpbuf pkt;
struct flow flow;
- union flow_in_port in_port_;
error = ofputil_decode_packet_in(&pi, oh);
if (error) {
/* Extract flow data from 'opi' into 'flow'. */
ofpbuf_use_const(&pkt, pi.packet, pi.packet_len);
- in_port_.ofp_port = pi.fmd.in_port;
- flow_extract(&pkt, 0, 0, NULL, &in_port_, &flow);
+ flow_extract(&pkt, NULL, &flow);
+ flow.in_port.ofp_port = pi.fmd.in_port;
flow.tunnel.tun_id = pi.fmd.tun_id;
/* Choose output port. */
case MFF_MPLS_BOS:
value->u8 = mpls_lse_to_bos(flow->mpls_lse[0]);
break;
- break;
case MFF_IPV4_SRC:
value->be32 = flow->nw_src;
case MFF_MPLS_BOS:
match_set_mpls_bos(match, 0, value->u8);
break;
- break;
case MFF_IPV4_SRC:
match_set_nw_src(match, value->be32);
case MFF_MPLS_BOS:
flow_set_mpls_bos(flow, 0, value->u8);
break;
- break;
case MFF_IPV4_SRC:
flow->nw_src = value->be32;
case MFF_MPLS_BOS:
match_set_any_mpls_bos(match, 0);
break;
- break;
case MFF_IPV4_SRC:
case MFF_ARP_SPA:
return error;
}
+struct queue_dump_state {
+ struct nl_dump dump;
+ struct ofpbuf buf;
+};
+
static bool
-start_queue_dump(const struct netdev *netdev, struct nl_dump *dump)
+start_queue_dump(const struct netdev *netdev, struct queue_dump_state *state)
{
struct ofpbuf request;
struct tcmsg *tcmsg;
return false;
}
tcmsg->tcm_parent = 0;
- nl_dump_start(dump, NETLINK_ROUTE, &request);
+ nl_dump_start(&state->dump, NETLINK_ROUTE, &request);
ofpbuf_uninit(&request);
+
+ ofpbuf_init(&state->buf, NL_DUMP_BUFSIZE);
return true;
}
+static int
+finish_queue_dump(struct queue_dump_state *state)
+{
+ ofpbuf_uninit(&state->buf);
+ return nl_dump_done(&state->dump);
+}
+
struct netdev_linux_queue_state {
unsigned int *queues;
size_t cur_queue;
ovs_mutex_lock(&netdev->mutex);
error = tc_query_qdisc(netdev_);
if (!error) {
- struct nl_dump dump;
+ struct queue_dump_state state;
if (!netdev->tc->ops->class_dump_stats) {
error = EOPNOTSUPP;
- } else if (!start_queue_dump(netdev_, &dump)) {
+ } else if (!start_queue_dump(netdev_, &state)) {
error = ENODEV;
} else {
struct ofpbuf msg;
int retval;
- while (nl_dump_next(&dump, &msg)) {
+ while (nl_dump_next(&state.dump, &msg, &state.buf)) {
retval = netdev->tc->ops->class_dump_stats(netdev_, &msg,
cb, aux);
if (retval) {
}
}
- retval = nl_dump_done(&dump);
+ retval = finish_queue_dump(&state);
if (retval) {
error = retval;
}
htb_tc_load(struct netdev *netdev, struct ofpbuf *nlmsg OVS_UNUSED)
{
struct ofpbuf msg;
- struct nl_dump dump;
+ struct queue_dump_state state;
struct htb_class hc;
/* Get qdisc options. */
htb_install__(netdev, hc.max_rate);
/* Get queues. */
- if (!start_queue_dump(netdev, &dump)) {
+ if (!start_queue_dump(netdev, &state)) {
return ENODEV;
}
- while (nl_dump_next(&dump, &msg)) {
+ while (nl_dump_next(&state.dump, &msg, &state.buf)) {
unsigned int queue_id;
if (!htb_parse_tcmsg__(&msg, &queue_id, &hc, NULL)) {
htb_update_queue__(netdev, queue_id, &hc);
}
}
- nl_dump_done(&dump);
+ finish_queue_dump(&state);
return 0;
}
hfsc_tc_load(struct netdev *netdev, struct ofpbuf *nlmsg OVS_UNUSED)
{
struct ofpbuf msg;
- struct nl_dump dump;
+ struct queue_dump_state state;
struct hfsc_class hc;
hc.max_rate = 0;
hfsc_query_class__(netdev, tc_make_handle(1, 0xfffe), 0, &hc, NULL);
hfsc_install__(netdev, hc.max_rate);
- if (!start_queue_dump(netdev, &dump)) {
+ if (!start_queue_dump(netdev, &state)) {
return ENODEV;
}
- while (nl_dump_next(&dump, &msg)) {
+ while (nl_dump_next(&state.dump, &msg, &state.buf)) {
unsigned int queue_id;
if (!hfsc_parse_tcmsg__(&msg, &queue_id, &hc, NULL)) {
}
}
- nl_dump_done(&dump);
+ finish_queue_dump(&state);
return 0;
}
#include "ofpbuf.h"
#include "ovs-thread.h"
#include "poll-loop.h"
+#include "seq.h"
#include "socket-util.h"
#include "util.h"
#include "vlog.h"
void
nl_dump_start(struct nl_dump *dump, int protocol, const struct ofpbuf *request)
{
- ofpbuf_init(&dump->buffer, 4096);
- dump->status = nl_pool_alloc(protocol, &dump->sock);
- if (dump->status) {
+ int status = nl_pool_alloc(protocol, &dump->sock);
+
+ if (status) {
return;
}
nl_msg_nlmsghdr(request)->nlmsg_flags |= NLM_F_DUMP | NLM_F_ACK;
- dump->status = nl_sock_send__(dump->sock, request,
- nl_sock_allocate_seq(dump->sock, 1), true);
+ status = nl_sock_send__(dump->sock, request,
+ nl_sock_allocate_seq(dump->sock, 1), true);
+ atomic_init(&dump->status, status << 1);
dump->nl_seq = nl_msg_nlmsghdr(request)->nlmsg_seq;
+ dump->status_seq = seq_create();
}
-/* Helper function for nl_dump_next(). */
-static int
-nl_dump_recv(struct nl_dump *dump)
-{
- struct nlmsghdr *nlmsghdr;
- int retval;
-
- retval = nl_sock_recv__(dump->sock, &dump->buffer, true);
- if (retval) {
- return retval == EINTR ? EAGAIN : retval;
- }
-
- nlmsghdr = nl_msg_nlmsghdr(&dump->buffer);
- if (dump->nl_seq != nlmsghdr->nlmsg_seq) {
- VLOG_DBG_RL(&rl, "ignoring seq %#"PRIx32" != expected %#"PRIx32,
- nlmsghdr->nlmsg_seq, dump->nl_seq);
- return EAGAIN;
- }
-
- if (nl_msg_nlmsgerr(&dump->buffer, &retval)) {
- VLOG_INFO_RL(&rl, "netlink dump request error (%s)",
- ovs_strerror(retval));
- return retval && retval != EAGAIN ? retval : EPROTO;
- }
-
- return 0;
-}
-
-/* Attempts to retrieve another reply from 'dump', which must have been
- * initialized with nl_dump_start().
+/* Attempts to retrieve another reply from 'dump' into 'buffer'. 'dump' must
+ * have been initialized with nl_dump_start(), and 'buffer' must have been
+ * initialized. 'buffer' should be at least NL_DUMP_BUFSIZE bytes long.
*
* If successful, returns true and points 'reply->data' and 'reply->size' to
- * the message that was retrieved. The caller must not modify 'reply' (because
- * it points into the middle of a larger buffer).
+ * the message that was retrieved. The caller must not modify 'reply' (because
+ * it points within 'buffer', which will be used by future calls to this
+ * function).
*
* On failure, returns false and sets 'reply->data' to NULL and 'reply->size'
* to 0. Failure might indicate an actual error or merely the end of replies.
* An error status for the entire dump operation is provided when it is
* completed by calling nl_dump_done().
+ *
+ * Multiple threads may call this function, passing the same nl_dump, however
+ * each must provide independent buffers. This function may cache multiple
+ * replies in the buffer, and these will be processed before more replies are
+ * fetched. When this function returns false, other threads may continue to
+ * process replies in their buffers, but they will not fetch more replies.
*/
bool
-nl_dump_next(struct nl_dump *dump, struct ofpbuf *reply)
+nl_dump_next(struct nl_dump *dump, struct ofpbuf *reply, struct ofpbuf *buffer)
{
struct nlmsghdr *nlmsghdr;
+ int error = 0;
reply->data = NULL;
reply->size = 0;
- if (dump->status) {
- return false;
- }
- while (!dump->buffer.size) {
- int retval = nl_dump_recv(dump);
+ /* If 'buffer' is empty, fetch another batch of nlmsgs. */
+ while (!buffer->size) {
+ unsigned int status;
+ int retval, seq;
+
+ seq = seq_read(dump->status_seq);
+ atomic_read(&dump->status, &status);
+ if (status) {
+ return false;
+ }
+
+ retval = nl_sock_recv__(dump->sock, buffer, false);
if (retval) {
- ofpbuf_clear(&dump->buffer);
- if (retval != EAGAIN) {
- dump->status = retval;
- return false;
+ ofpbuf_clear(buffer);
+ if (retval == EAGAIN) {
+ nl_sock_wait(dump->sock, POLLIN);
+ seq_wait(dump->status_seq, seq);
+ poll_block();
+ continue;
+ } else {
+ error = retval;
+ goto exit;
}
}
+
+ nlmsghdr = nl_msg_nlmsghdr(buffer);
+ if (dump->nl_seq != nlmsghdr->nlmsg_seq) {
+ VLOG_DBG_RL(&rl, "ignoring seq %#"PRIx32" != expected %#"PRIx32,
+ nlmsghdr->nlmsg_seq, dump->nl_seq);
+ ofpbuf_clear(buffer);
+ continue;
+ }
+
+ if (nl_msg_nlmsgerr(buffer, &retval) && retval) {
+ VLOG_INFO_RL(&rl, "netlink dump request error (%s)",
+ ovs_strerror(retval));
+ error = retval == EAGAIN ? EPROTO : retval;
+ ofpbuf_clear(buffer);
+ goto exit;
+ }
}
- nlmsghdr = nl_msg_next(&dump->buffer, reply);
+ /* Fetch the next nlmsg in the current batch. */
+ nlmsghdr = nl_msg_next(buffer, reply);
if (!nlmsghdr) {
VLOG_WARN_RL(&rl, "netlink dump reply contains message fragment");
- dump->status = EPROTO;
- return false;
+ error = EPROTO;
} else if (nlmsghdr->nlmsg_type == NLMSG_DONE) {
- dump->status = EOF;
- return false;
+ error = EOF;
}
- return true;
+exit:
+ if (error == EOF) {
+ unsigned int old;
+ atomic_or(&dump->status, 1, &old);
+ seq_change(dump->status_seq);
+ } else if (error) {
+ atomic_store(&dump->status, error << 1);
+ seq_change(dump->status_seq);
+ }
+ return !error;
}
/* Completes Netlink dump operation 'dump', which must have been initialized
int
nl_dump_done(struct nl_dump *dump)
{
+ int status;
+
/* Drain any remaining messages that the client didn't read. Otherwise the
* kernel will continue to queue them up and waste buffer space.
*
* XXX We could just destroy and discard the socket in this case. */
- while (!dump->status) {
- struct ofpbuf reply;
- if (!nl_dump_next(dump, &reply)) {
- ovs_assert(dump->status);
+ atomic_read(&dump->status, &status);
+ if (!status) {
+ uint64_t tmp_reply_stub[NL_DUMP_BUFSIZE / 8];
+ struct ofpbuf reply, buf;
+
+ ofpbuf_use_stub(&buf, tmp_reply_stub, sizeof tmp_reply_stub);
+ while (nl_dump_next(dump, &reply, &buf)) {
+ /* Nothing to do. */
}
+ atomic_read(&dump->status, &status);
+ ovs_assert(status);
+ ofpbuf_uninit(&buf);
}
+ atomic_destroy(&dump->status);
nl_pool_release(dump->sock);
- ofpbuf_uninit(&dump->buffer);
- return dump->status == EOF ? 0 : dump->status;
+ seq_destroy(dump->status_seq);
+ return status >> 1;
}
/* Causes poll_block() to wake up when any of the specified 'events' (which is
* Thread-safety
* =============
*
- * Only a single thread may use a given nl_sock or nl_dump at one time.
+ * Most of the netlink functions are not fully thread-safe: Only a single
+ * thread may use a given nl_sock or nl_dump at one time. The exceptions are:
+ *
+ * - nl_sock_recv() is conditionally thread-safe: it may be called from
+ * different threads with the same nl_sock, but each caller must provide
+ * an independent receive buffer.
+ *
+ * - nl_dump_next() is conditionally thread-safe: it may be called from
+ * different threads with the same nl_dump, but each caller must provide
+ * independent buffers.
*/
#include <stdbool.h>
#include <stddef.h>
#include <stdint.h>
#include "ofpbuf.h"
+#include "ovs-atomic.h"
struct nl_sock;
void nl_transact_multiple(int protocol, struct nl_transaction **, size_t n);
/* Table dumping. */
+#define NL_DUMP_BUFSIZE 4096
+
struct nl_dump {
struct nl_sock *sock; /* Socket being dumped. */
uint32_t nl_seq; /* Expected nlmsg_seq for replies. */
- struct ofpbuf buffer; /* Receive buffer currently being iterated. */
- int status; /* 0=OK, EOF=done, or positive errno value. */
+ atomic_uint status; /* Low bit set if we read final message.
+ * Other bits hold an errno (0 for success). */
+ struct seq *status_seq; /* Tracks changes to the above 'status'. */
};
void nl_dump_start(struct nl_dump *, int protocol,
const struct ofpbuf *request);
-bool nl_dump_next(struct nl_dump *, struct ofpbuf *reply);
+bool nl_dump_next(struct nl_dump *, struct ofpbuf *reply, struct ofpbuf *buf);
int nl_dump_done(struct nl_dump *);
/* Miscellaneous */
tun_key_ofs = nl_msg_start_nested(a, OVS_KEY_ATTR_TUNNEL);
- if (tun_key->flags & FLOW_TNL_F_KEY) {
+ /* tun_id != 0 without FLOW_TNL_F_KEY is valid if tun_key is a mask. */
+ if (tun_key->tun_id || tun_key->flags & FLOW_TNL_F_KEY) {
nl_msg_put_be64(a, OVS_TUNNEL_KEY_ATTR_ID, tun_key->tun_id);
}
if (tun_key->ip_src) {
/* Add an ingress port attribute if 'odp_in_port' is not the magical
* value "ODPP_NONE". */
- if (md->in_port != ODPP_NONE) {
- nl_msg_put_odp_port(buf, OVS_KEY_ATTR_IN_PORT, md->in_port);
+ if (md->in_port.odp_port != ODPP_NONE) {
+ nl_msg_put_odp_port(buf, OVS_KEY_ATTR_IN_PORT, md->in_port.odp_port);
}
}
1u << OVS_KEY_ATTR_SKB_MARK | 1u << OVS_KEY_ATTR_TUNNEL |
1u << OVS_KEY_ATTR_IN_PORT;
- memset(md, 0, sizeof *md);
- md->in_port = ODPP_NONE;
+ *md = PKT_METADATA_INITIALIZER(ODPP_NONE);
NL_ATTR_FOR_EACH (nla, left, key, key_len) {
uint16_t type = nl_attr_type(nla);
wanted_attrs &= ~(1u << OVS_KEY_ATTR_TUNNEL);
}
} else if (type == OVS_KEY_ATTR_IN_PORT) {
- md->in_port = nl_attr_get_odp_port(nla);
+ md->in_port.odp_port = nl_attr_get_odp_port(nla);
wanted_attrs &= ~(1u << OVS_KEY_ATTR_IN_PORT);
}
#include "packets.h"
#include "type-props.h"
#include "unaligned.h"
+#include "odp-util.h"
#include "util.h"
static void ofp_print_queue_name(struct ds *string, uint32_t port);
ofp_packet_to_string(const void *data, size_t len)
{
struct ds ds = DS_EMPTY_INITIALIZER;
+ const struct pkt_metadata md = PKT_METADATA_INITIALIZER(ODPP_NONE);
struct ofpbuf buf;
struct flow flow;
ofpbuf_use_const(&buf, data, len);
- flow_extract(&buf, 0, 0, NULL, NULL, &flow);
+ flow_extract(&buf, &md, &flow);
flow_format(&ds, &flow);
if (buf.l7) {
#include "dynamic-string.h"
#include "ofpbuf.h"
#include "ovs-thread.h"
+#include "odp-util.h"
#include "unaligned.h"
const struct in6_addr in6addr_exact = IN6ADDR_EXACT_INIT;
ds_put_cstr(s, "[800]");
}
}
+
+void pkt_metadata_init(struct pkt_metadata *md, const struct flow_tnl *tnl,
+ const uint32_t skb_priority,
+ const uint32_t pkt_mark,
+ const union flow_in_port *in_port)
+{
+
+ tnl ? memcpy(&md->tunnel, tnl, sizeof(md->tunnel))
+ : memset(&md->tunnel, 0, sizeof(md->tunnel));
+
+ md->skb_priority = skb_priority;
+ md->pkt_mark = pkt_mark;
+ md->in_port.odp_port = in_port ? in_port->odp_port : ODPP_NONE;
+}
+
+void pkt_metadata_from_flow(struct pkt_metadata *md, const struct flow *flow)
+{
+ pkt_metadata_init(md, &flow->tunnel, flow->skb_priority,
+ flow->pkt_mark, &flow->in_port);
+}
struct flow_tnl tunnel; /* Encapsulating tunnel parameters. */
uint32_t skb_priority; /* Packet priority for QoS. */
uint32_t pkt_mark; /* Packet mark. */
- odp_port_t in_port; /* Input port. */
+ union flow_in_port in_port; /* Input port. */
};
#define PKT_METADATA_INITIALIZER(PORT) \
- (struct pkt_metadata){ { 0, 0, 0, 0, 0, 0}, 0, 0, (PORT) }
+ (struct pkt_metadata){ { 0, 0, 0, 0, 0, 0}, 0, 0, {(PORT)} }
+
+void pkt_metadata_init(struct pkt_metadata *md, const struct flow_tnl *tnl,
+ const uint32_t skb_priority,
+ const uint32_t pkt_mark,
+ const union flow_in_port *in_port);
+void pkt_metadata_from_flow(struct pkt_metadata *md, const struct flow *flow);
bool dpid_from_string(const char *s, uint64_t *dpidp);
/*
- * Copyright (c) 2008, 2009, 2010, 2011, 2012, 2013 Nicira, Inc.
+ * Copyright (c) 2008, 2009, 2010, 2011, 2012, 2013, 2014 Nicira, Inc.
*
* Licensed under the Apache License, Version 2.0 (the "License");
* you may not use this file except in compliance with the License.
ovs_mutex_lock(&rc->mutex);
if (rc->vconn) {
+ int error;
+
vconn_run(rc->vconn);
+
+ error = vconn_get_status(rc->vconn);
+ if (error) {
+ report_error(rc, error);
+ disconnect(rc, error);
+ }
}
for (i = 0; i < rc->n_monitors; ) {
struct ofpbuf *msg;
{
struct nl_dump dump;
struct rtgenmsg *rtmsg;
- struct ofpbuf request, reply;
+ uint64_t reply_stub[NL_DUMP_BUFSIZE / 8];
+ struct ofpbuf request, reply, buf;
route_map_clear();
route_table_valid = true;
nl_dump_start(&dump, NETLINK_ROUTE, &request);
ofpbuf_uninit(&request);
- while (nl_dump_next(&dump, &reply)) {
+ ofpbuf_use_stub(&buf, reply_stub, sizeof reply_stub);
+ while (nl_dump_next(&dump, &reply, &buf)) {
struct route_table_msg msg;
if (route_table_parse(&reply, &msg)) {
route_table_handle_msg(&msg);
}
}
+ ofpbuf_uninit(&buf);
return nl_dump_done(&dump);
}
{
struct nl_dump dump;
struct rtgenmsg *rtmsg;
- struct ofpbuf request, reply;
+ uint64_t reply_stub[NL_DUMP_BUFSIZE / 8];
+ struct ofpbuf request, reply, buf;
name_table_valid = true;
name_map_clear();
nl_dump_start(&dump, NETLINK_ROUTE, &request);
ofpbuf_uninit(&request);
- while (nl_dump_next(&dump, &reply)) {
+ ofpbuf_use_stub(&buf, reply_stub, sizeof reply_stub);
+ while (nl_dump_next(&dump, &reply, &buf)) {
struct rtnetlink_link_change change;
if (rtnetlink_link_parse(&reply, &change)
hmap_insert(&name_map, &nn->node, hash_int(nn->ifi_index, 0));
}
}
+ ofpbuf_uninit(&buf);
return nl_dump_done(&dump);
}
set_dscp(int fd, uint8_t dscp)
{
int val;
+ bool success;
if (dscp > 63) {
return EINVAL;
}
+ /* Note: this function is used for both of IPv4 and IPv6 sockets */
+ success = false;
val = dscp << 2;
if (setsockopt(fd, IPPROTO_IP, IP_TOS, &val, sizeof val)) {
- return sock_errno();
+ if (sock_errno() != ENOPROTOOPT) {
+ return sock_errno();
+ }
+ } else {
+ success = true;
+ }
+ if (setsockopt(fd, IPPROTO_IPV6, IPV6_TCLASS, &val, sizeof val)) {
+ if (sock_errno() != ENOPROTOOPT) {
+ return sock_errno();
+ }
+ } else {
+ success = true;
+ }
+ if (!success) {
+ return ENOPROTOOPT;
}
return 0;
* connect(), the handshake SYN frames will be sent with a TOS of 0. */
error = set_dscp(fd, dscp);
if (error) {
- VLOG_ERR("%s: socket: %s", target, sock_strerror(error));
+ VLOG_ERR("%s: set_dscp: %s", target, sock_strerror(error));
goto exit;
}
* connect(), the handshake SYN frames will be sent with a TOS of 0. */
error = set_dscp(fd, dscp);
if (error) {
- VLOG_ERR("%s: socket: %s", target, sock_strerror(error));
+ VLOG_ERR("%s: set_dscp: %s", target, sock_strerror(error));
goto error;
}
}
}
+#ifndef _WIN32
void
xpipe(int fds[2])
{
xset_nonblocking(fds[0]);
xset_nonblocking(fds[1]);
}
+#endif
static int
getsockopt_int(int fd, int level, int option, const char *optname, int *valuep)
int fsync_parent_dir(const char *file_name);
int get_mtime(const char *file_name, struct timespec *mtime);
+#ifndef _WIN32
void xpipe(int fds[2]);
void xpipe_nonblocking(int fds[2]);
+#endif
char *describe_fd(int fd);
--- /dev/null
+/*
+ * Copyright (c) 2014 Nicira, Inc.
+ *
+ * Licensed under the Apache License, Version 2.0 (the "License");
+ * you may not use this file except in compliance with the License.
+ * You may obtain a copy of the License at:
+ *
+ * http://www.apache.org/licenses/LICENSE-2.0
+ *
+ * Unless required by applicable law or agreed to in writing, software
+ * distributed under the License is distributed on an "AS IS" BASIS,
+ * WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied.
+ * See the License for the specific language governing permissions and
+ * limitations under the License.
+ */
+
+#include <config.h>
+#include "stream-fd.h"
+#include <errno.h>
+#include <poll.h>
+#include <stdlib.h>
+#include <string.h>
+#include <sys/types.h>
+#include <unistd.h>
+#include <winsock2.h>
+#include "fatal-signal.h"
+#include "poll-loop.h"
+#include "socket-util.h"
+#include "stream.h"
+#include "stream-provider.h"
+#include "util.h"
+#include "vlog.h"
+
+VLOG_DEFINE_THIS_MODULE(stream_fd_windows);
+
+/* Active file descriptor stream. */
+
+struct stream_fd
+{
+ struct stream stream;
+ int fd;
+ HANDLE wevent;
+};
+
+static const struct stream_class stream_fd_class;
+
+static struct vlog_rate_limit rl = VLOG_RATE_LIMIT_INIT(10, 25);
+
+/* Creates a new stream named 'name' that will send and receive data on 'fd'
+ * and stores a pointer to the stream in '*streamp'. Initial connection status
+ * 'connect_status' is interpreted as described for stream_init().
+ *
+ * Returns 0 if successful, otherwise a positive errno value. (The current
+ * implementation never fails.) */
+int
+new_fd_stream(const char *name, int fd, int connect_status,
+ struct stream **streamp)
+{
+ struct stream_fd *s;
+
+ s = xmalloc(sizeof *s);
+ stream_init(&s->stream, &stream_fd_class, connect_status, name);
+ s->fd = fd;
+ s->wevent = CreateEvent(NULL, FALSE, FALSE, NULL);
+ *streamp = &s->stream;
+ return 0;
+}
+
+static struct stream_fd *
+stream_fd_cast(struct stream *stream)
+{
+ stream_assert_class(stream, &stream_fd_class);
+ return CONTAINER_OF(stream, struct stream_fd, stream);
+}
+
+static void
+fd_close(struct stream *stream)
+{
+ struct stream_fd *s = stream_fd_cast(stream);
+ WSAEventSelect(s->fd, NULL, 0);
+ CloseHandle(s->wevent);
+ closesocket(s->fd);
+ free(s);
+}
+
+static int
+fd_connect(struct stream *stream)
+{
+ struct stream_fd *s = stream_fd_cast(stream);
+ return check_connection_completion(s->fd);
+}
+
+static ssize_t
+fd_recv(struct stream *stream, void *buffer, size_t n)
+{
+ struct stream_fd *s = stream_fd_cast(stream);
+ ssize_t retval;
+
+ retval = recv(s->fd, buffer, n, 0);
+ if (retval < 0) {
+ retval = -sock_errno();
+ }
+ if (retval == -WSAEWOULDBLOCK) {
+ return -EAGAIN;
+ }
+ return retval;
+}
+
+static ssize_t
+fd_send(struct stream *stream, const void *buffer, size_t n)
+{
+ struct stream_fd *s = stream_fd_cast(stream);
+ ssize_t retval;
+
+ retval = send(s->fd, buffer, n, 0);
+ if (retval < 0) {
+ retval = -sock_errno();
+ }
+ if (retval == -WSAEWOULDBLOCK) {
+ return -EAGAIN;
+ }
+
+ return retval;
+}
+
+static void
+fd_wait(struct stream *stream, enum stream_wait_type wait)
+{
+ struct stream_fd *s = stream_fd_cast(stream);
+ switch (wait) {
+ case STREAM_CONNECT:
+ case STREAM_SEND:
+ poll_fd_wait_event(s->fd, s->wevent, POLLOUT);
+ break;
+
+ case STREAM_RECV:
+ poll_fd_wait_event(s->fd, s->wevent, POLLIN);
+ break;
+
+ default:
+ NOT_REACHED();
+ }
+}
+
+static const struct stream_class stream_fd_class = {
+ "fd", /* name */
+ false, /* needs_probes */
+ NULL, /* open */
+ fd_close, /* close */
+ fd_connect, /* connect */
+ fd_recv, /* recv */
+ fd_send, /* send */
+ NULL, /* run */
+ NULL, /* run_wait */
+ fd_wait, /* wait */
+};
+\f
+/* Passive file descriptor stream. */
+
+struct fd_pstream
+{
+ struct pstream pstream;
+ int fd;
+ HANDLE wevent;
+ int (*accept_cb)(int fd, const struct sockaddr_storage *, size_t ss_len,
+ struct stream **);
+ int (*set_dscp_cb)(int fd, uint8_t dscp);
+ char *unlink_path;
+};
+
+static const struct pstream_class fd_pstream_class;
+
+static struct fd_pstream *
+fd_pstream_cast(struct pstream *pstream)
+{
+ pstream_assert_class(pstream, &fd_pstream_class);
+ return CONTAINER_OF(pstream, struct fd_pstream, pstream);
+}
+
+/* Creates a new pstream named 'name' that will accept new socket connections
+ * on 'fd' and stores a pointer to the stream in '*pstreamp'.
+ *
+ * When a connection has been accepted, 'accept_cb' will be called with the new
+ * socket fd 'fd' and the remote address of the connection 'sa' and 'sa_len'.
+ * accept_cb must return 0 if the connection is successful, in which case it
+ * must initialize '*streamp' to the new stream, or a positive errno value on
+ * error. In either case accept_cb takes ownership of the 'fd' passed in.
+ *
+ * When '*pstreamp' is closed, then 'unlink_path' (if nonnull) will be passed
+ * to fatal_signal_unlink_file_now() and freed with free().
+ *
+ * Returns 0 if successful, otherwise a positive errno value. (The current
+ * implementation never fails.) */
+int
+new_fd_pstream(const char *name, int fd,
+ int (*accept_cb)(int fd, const struct sockaddr_storage *ss,
+ size_t ss_len, struct stream **streamp),
+ int (*set_dscp_cb)(int fd, uint8_t dscp),
+ char *unlink_path, struct pstream **pstreamp)
+{
+ struct fd_pstream *ps = xmalloc(sizeof *ps);
+ pstream_init(&ps->pstream, &fd_pstream_class, name);
+ ps->fd = fd;
+ ps->wevent = CreateEvent(NULL, FALSE, FALSE, NULL);
+ ps->accept_cb = accept_cb;
+ ps->set_dscp_cb = set_dscp_cb;
+ ps->unlink_path = unlink_path;
+ *pstreamp = &ps->pstream;
+ return 0;
+}
+
+static void
+pfd_close(struct pstream *pstream)
+{
+ struct fd_pstream *ps = fd_pstream_cast(pstream);
+ WSAEventSelect(ps->fd, NULL, 0);
+ CloseHandle(ps->wevent);
+ closesocket(ps->fd);
+ free(ps);
+}
+
+static int
+pfd_accept(struct pstream *pstream, struct stream **new_streamp)
+{
+ struct fd_pstream *ps = fd_pstream_cast(pstream);
+ 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) {
+ retval = sock_errno();
+ if (retval == WSAEWOULDBLOCK) {
+ return EAGAIN;
+ }
+ return retval;
+ }
+
+ retval = set_nonblocking(new_fd);
+ if (retval) {
+ closesocket(new_fd);
+ return retval;
+ }
+
+ return ps->accept_cb(new_fd, &ss, ss_len, new_streamp);
+}
+
+static void
+pfd_wait(struct pstream *pstream)
+{
+ struct fd_pstream *ps = fd_pstream_cast(pstream);
+ poll_fd_wait_event(ps->fd, ps->wevent, POLLIN);
+}
+
+static int
+pfd_set_dscp(struct pstream *pstream, uint8_t dscp)
+{
+ struct fd_pstream *ps = fd_pstream_cast(pstream);
+ if (ps->set_dscp_cb) {
+ return ps->set_dscp_cb(ps->fd, dscp);
+ }
+ return 0;
+}
+
+static const struct pstream_class fd_pstream_class = {
+ "pstream",
+ false,
+ NULL,
+ pfd_close,
+ pfd_accept,
+ pfd_wait,
+ pfd_set_dscp,
+};
* WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied.
* See the License for the specific language governing permissions and
* limitations under the License.
+ *
+ * Note on windows platform, stream fd can only handle sockets, on unix any
+ * fd is acceptable.
*/
#ifndef STREAM_FD_H
static const struct stream_class *stream_classes[] = {
&tcp_stream_class,
-#ifdef AF_UNIX
+#ifndef _WIN32
&unix_stream_class,
#endif
#ifdef HAVE_OPENSSL
static const struct pstream_class *pstream_classes[] = {
&ptcp_pstream_class,
-#ifdef AF_UNIX
+#ifndef _WIN32
&punix_pstream_class,
#endif
#ifdef HAVE_OPENSSL
#endif
if (deadline <= time_msec()) {
+#ifndef _WIN32
fatal_signal_handler(SIGALRM);
+#else
+ VLOG_ERR("wake up from WaitForMultipleObjects after deadline");
+ fatal_signal_handler(SIGTERM);
+#endif
if (retval < 0) {
retval = 0;
}
/*
- * Copyright (c) 2008, 2009, 2010, 2011, 2012, 2013 Nicira, Inc.
+ * Copyright (c) 2008, 2009, 2010, 2011, 2012, 2013, 2014 Nicira, Inc.
*
* Licensed under the Apache License, Version 2.0 (the "License");
* you may not use this file except in compliance with the License.
}
}
+/* Returns 0 if 'vconn' is healthy (connecting or connected), a positive errno
+ * value if the connection died abnormally (connection failed or aborted), or
+ * EOF if the connection was closed in a normal way. */
+int
+vconn_get_status(const struct vconn *vconn)
+{
+ return vconn->error == EAGAIN ? 0 : vconn->error;
+}
+
int
vconn_open_block(const char *name, uint32_t allowed_versions, uint8_t dscp,
struct vconn **vconnp)
/*
- * Copyright (c) 2008, 2009, 2010, 2011, 2012, 2013 Nicira, Inc.
+ * Copyright (c) 2008, 2009, 2010, 2011, 2012, 2013, 2014 Nicira, Inc.
*
* Licensed under the Apache License, Version 2.0 (the "License");
* you may not use this file except in compliance with the License.
void vconn_run(struct vconn *);
void vconn_run_wait(struct vconn *);
+int vconn_get_status(const struct vconn *);
+
int vconn_open_block(const char *name, uint32_t allowed_versions, uint8_t dscp,
struct vconn **);
int vconn_connect_block(struct vconn *);
utilities/ovs-vsctl.8: \
utilities/ovs-vsctl.8.in \
+ lib/common.man \
lib/ssl-bootstrap.man \
lib/ssl-peer-ca-cert.man \
lib/ssl.man \
ovsdb/remote-passive.man \
ovsdb/remote-passive.man
utilities/ovs-vsctl.8.in:
+lib/common.man:
lib/ssl-bootstrap.man:
lib/ssl-peer-ca-cert.man:
lib/ssl.man:
vtep/vtep-ctl.8: \
vtep/vtep-ctl.8.in \
+ lib/common.man \
lib/ssl-bootstrap.man \
lib/ssl-peer-ca-cert.man \
lib/ssl.man \
ovsdb/remote-passive.man \
ovsdb/remote-passive.man
vtep/vtep-ctl.8.in:
+lib/common.man:
lib/ssl-bootstrap.man:
lib/ssl-peer-ca-cert.man:
lib/ssl.man:
enum ofp_packet_in_reason wire_reason);
/* Sends an OFPT_PORT_STATUS message with 'opp' and 'reason' to appropriate
- * controllers managed by 'mgr'. */
+ * controllers managed by 'mgr'. For messages caused by a controller
+ * OFPT_PORT_MOD, specify 'source' as the controller connection that sent the
+ * request; otherwise, specify 'source' as NULL. */
void
-connmgr_send_port_status(struct connmgr *mgr,
+connmgr_send_port_status(struct connmgr *mgr, struct ofconn *source,
const struct ofputil_phy_port *pp, uint8_t reason)
{
/* XXX Should limit the number of queued port status change messages. */
if (ofconn_receives_async_msg(ofconn, OAM_PORT_STATUS, reason)) {
struct ofpbuf *msg;
+ /* Before 1.5, OpenFlow specified that OFPT_PORT_MOD should not
+ * generate OFPT_PORT_STATUS messages. That requirement was a
+ * relic of how OpenFlow originally supported a single controller,
+ * so that one could expect the controller to already know the
+ * changes it had made.
+ *
+ * EXT-338 changes OpenFlow 1.5 OFPT_PORT_MOD to send
+ * OFPT_PORT_STATUS messages to every controller. This is
+ * obviously more useful in the multi-controller case. We could
+ * always implement it that way in OVS, but that would risk
+ * confusing controllers that are intended for single-controller
+ * use only. (Imagine a controller that generates an OFPT_PORT_MOD
+ * in response to any OFPT_PORT_STATUS!)
+ *
+ * So this compromises: for OpenFlow 1.4 and earlier, it generates
+ * OFPT_PORT_STATUS for OFPT_PORT_MOD, but not back to the
+ * originating controller. In a single-controller environment, in
+ * particular, this means that it will never generate
+ * OFPT_PORT_STATUS for OFPT_PORT_MOD at all. */
+ if (ofconn == source
+ && rconn_get_version(ofconn->rconn) < OFP15_VERSION) {
+ continue;
+ }
+
msg = ofputil_encode_port_status(&ps, ofconn_get_protocol(ofconn));
ofconn_send(ofconn, msg, NULL);
}
/*
- * Copyright (c) 2009, 2010, 2011, 2012, 2013 Nicira, Inc.
+ * Copyright (c) 2009, 2010, 2011, 2012, 2013, 2014 Nicira, Inc.
*
* Licensed under the Apache License, Version 2.0 (the "License");
* you may not use this file except in compliance with the License.
const struct ofp_header *request, int error);
/* Sending asynchronous messages. */
-void connmgr_send_port_status(struct connmgr *,
+void connmgr_send_port_status(struct connmgr *, struct ofconn *source,
const struct ofputil_phy_port *, uint8_t reason);
void connmgr_send_flow_removed(struct connmgr *,
const struct ofputil_flow_removed *);
static uint64_t udpif_get_n_flows(struct udpif *);
static void revalidate_udumps(struct revalidator *, struct list *udumps);
static void revalidator_sweep(struct revalidator *);
+static void revalidator_purge(struct revalidator *);
static void upcall_unixctl_show(struct unixctl_conn *conn, int argc,
const char *argv[], void *aux);
static void upcall_unixctl_disable_megaflows(struct unixctl_conn *, int argc,
for (i = 0; i < udpif->n_revalidators; i++) {
struct revalidator *revalidator = &udpif->revalidators[i];
struct udpif_flow_dump *udump, *next_udump;
- struct udpif_key *ukey, *next_ukey;
LIST_FOR_EACH_SAFE (udump, next_udump, list_node,
&revalidator->udumps) {
free(udump);
}
- HMAP_FOR_EACH_SAFE (ukey, next_ukey, hmap_node,
- &revalidator->ukeys) {
- ukey_delete(revalidator, ukey);
- }
+ /* Delete ukeys, and delete all flows from the datapath to prevent
+ * double-counting stats. */
+ revalidator_purge(revalidator);
hmap_destroy(&revalidator->ukeys);
ovs_mutex_destroy(&revalidator->mutex);
}
}
+/* Waits for all ongoing upcall translations to complete. This ensures that
+ * there are no transient references to any removed ofprotos (or other
+ * objects). In particular, this should be called after an ofproto is removed
+ * (e.g. via xlate_remove_ofproto()) but before it is destroyed. */
+void
+udpif_synchronize(struct udpif *udpif)
+{
+ /* This is stronger than necessary. It would be sufficient to ensure
+ * (somehow) that each handler and revalidator thread had passed through
+ * its main loop once. */
+ size_t n_handlers = udpif->n_handlers;
+ size_t n_revalidators = udpif->n_revalidators;
+ udpif_set_threads(udpif, 0, 0);
+ udpif_set_threads(udpif, n_handlers, n_revalidators);
+}
+
/* Notifies 'udpif' that something changed which may render previous
* xlate_actions() results invalid. */
void
bool need_revalidate;
uint64_t reval_seq;
size_t n_flows, i;
+ int error;
+ void *state = NULL;
reval_seq = seq_read(udpif->reval_seq);
need_revalidate = udpif->last_reval_seq != reval_seq;
udpif->avg_n_flows = (udpif->avg_n_flows + n_flows) / 2;
start_time = time_msec();
- dpif_flow_dump_start(&dump, udpif->dpif);
- while (dpif_flow_dump_next(&dump, &key, &key_len, &mask, &mask_len,
- NULL, NULL, &stats)
+ error = dpif_flow_dump_start(&dump, udpif->dpif);
+ if (error) {
+ VLOG_INFO("Failed to start flow dump (%s)", ovs_strerror(error));
+ goto skip;
+ }
+ dpif_flow_dump_state_init(udpif->dpif, &state);
+ while (dpif_flow_dump_next(&dump, state, &key, &key_len,
+ &mask, &mask_len, NULL, NULL, &stats)
&& !latch_is_set(&udpif->exit_latch)) {
struct udpif_flow_dump *udump = xmalloc(sizeof *udump);
struct revalidator *revalidator;
xpthread_cond_signal(&revalidator->wake_cond);
ovs_mutex_unlock(&revalidator->mutex);
}
+ dpif_flow_dump_state_uninit(udpif->dpif, state);
dpif_flow_dump_done(&dump);
/* Let all the revalidators finish and garbage collect. */
duration);
}
+skip:
poll_timer_wait_until(start_time + MIN(MAX_IDLE, 500));
seq_wait(udpif->reval_seq, udpif->last_reval_seq);
latch_wait(&udpif->exit_latch);
type = classify_upcall(upcall);
if (type == MISS_UPCALL) {
uint32_t hash;
+ struct pkt_metadata md;
- flow_extract(packet, flow.skb_priority, flow.pkt_mark,
- &flow.tunnel, &flow.in_port, &miss->flow);
+ pkt_metadata_from_flow(&md, &flow);
+ flow_extract(packet, &md, &miss->flow);
hash = flow_hash(&miss->flow, 0);
existing_miss = flow_miss_find(&misses, ofproto, &miss->flow,
return NULL;
}
+static struct udpif_key *
+ukey_create(const struct nlattr *key, size_t key_len, long long int used)
+{
+ struct udpif_key *ukey = xmalloc(sizeof *ukey);
+
+ ukey->key = (struct nlattr *) &ukey->key_buf;
+ memcpy(&ukey->key_buf, key, key_len);
+ ukey->key_len = key_len;
+
+ ukey->mark = false;
+ ukey->created = used ? used : time_msec();
+ memset(&ukey->stats, 0, sizeof ukey->stats);
+
+ return ukey;
+}
+
static void
ukey_delete(struct revalidator *revalidator, struct udpif_key *ukey)
{
return ok;
}
+struct dump_op {
+ struct udpif_key *ukey;
+ struct udpif_flow_dump *udump;
+ struct dpif_flow_stats stats; /* Stats for 'op'. */
+ struct dpif_op op; /* Flow del operation. */
+};
+
static void
-revalidate_udumps(struct revalidator *revalidator, struct list *udumps)
+dump_op_init(struct dump_op *op, const struct nlattr *key, size_t key_len,
+ struct udpif_key *ukey, struct udpif_flow_dump *udump)
+{
+ op->ukey = ukey;
+ op->udump = udump;
+ op->op.type = DPIF_OP_FLOW_DEL;
+ op->op.u.flow_del.key = key;
+ op->op.u.flow_del.key_len = key_len;
+ op->op.u.flow_del.stats = &op->stats;
+}
+
+static void
+push_dump_ops(struct revalidator *revalidator,
+ struct dump_op *ops, size_t n_ops)
{
struct udpif *udpif = revalidator->udpif;
+ struct dpif_op *opsp[REVALIDATE_MAX_BATCH];
+ size_t i;
- struct {
- struct dpif_flow_stats ukey_stats; /* Stats stored in the ukey. */
- struct dpif_flow_stats stats; /* Stats for 'op'. */
- struct dpif_op op; /* Flow del operation. */
- } ops[REVALIDATE_MAX_BATCH];
+ ovs_assert(n_ops <= REVALIDATE_MAX_BATCH);
+ for (i = 0; i < n_ops; i++) {
+ opsp[i] = &ops[i].op;
+ }
+ dpif_operate(udpif->dpif, opsp, n_ops);
- struct dpif_op *opsp[REVALIDATE_MAX_BATCH];
+ for (i = 0; i < n_ops; i++) {
+ struct dump_op *op = &ops[i];
+ struct dpif_flow_stats *push, *stats, push_buf;
+
+ stats = op->op.u.flow_del.stats;
+ if (op->ukey) {
+ push = &push_buf;
+ push->used = MAX(stats->used, op->ukey->stats.used);
+ push->tcp_flags = stats->tcp_flags | op->ukey->stats.tcp_flags;
+ push->n_packets = stats->n_packets - op->ukey->stats.n_packets;
+ push->n_bytes = stats->n_bytes - op->ukey->stats.n_bytes;
+ } else {
+ push = stats;
+ }
+
+ if (push->n_packets || netflow_exists()) {
+ struct ofproto_dpif *ofproto;
+ struct netflow *netflow;
+ struct flow flow;
+
+ if (!xlate_receive(udpif->backer, NULL, op->op.u.flow_del.key,
+ op->op.u.flow_del.key_len, &flow, &ofproto,
+ NULL, NULL, &netflow, NULL)) {
+ struct xlate_in xin;
+
+ xlate_in_init(&xin, ofproto, &flow, NULL, push->tcp_flags,
+ NULL);
+ xin.resubmit_stats = push->n_packets ? push : NULL;
+ xin.may_learn = push->n_packets > 0;
+ xin.skip_wildcards = true;
+ xlate_actions_for_side_effects(&xin);
+
+ if (netflow) {
+ netflow_expire(netflow, &flow);
+ netflow_flow_clear(netflow, &flow);
+ netflow_unref(netflow);
+ }
+ }
+ }
+ }
+
+ for (i = 0; i < n_ops; i++) {
+ struct udpif_key *ukey;
+
+ /* If there's a udump, this ukey came directly from a datapath flow
+ * dump. Sometimes a datapath can send duplicates in flow dumps, in
+ * which case we wouldn't want to double-free a ukey, so avoid that by
+ * looking up the ukey again.
+ *
+ * If there's no udump then we know what we're doing. */
+ ukey = (ops[i].udump
+ ? ukey_lookup(revalidator, ops[i].udump)
+ : ops[i].ukey);
+ if (ukey) {
+ ukey_delete(revalidator, ukey);
+ }
+ }
+}
+
+static void
+revalidate_udumps(struct revalidator *revalidator, struct list *udumps)
+{
+ struct udpif *udpif = revalidator->udpif;
+
+ struct dump_op ops[REVALIDATE_MAX_BATCH];
struct udpif_flow_dump *udump, *next_udump;
- size_t n_ops, i, n_flows;
+ size_t n_ops, n_flows;
unsigned int flow_limit;
long long int max_idle;
bool must_del;
}
if (must_del || (used && used < now - max_idle)) {
- struct dpif_flow_stats *ukey_stats = &ops[n_ops].ukey_stats;
- struct dpif_op *op = &ops[n_ops].op;
-
- op->type = DPIF_OP_FLOW_DEL;
- op->u.flow_del.key = udump->key;
- op->u.flow_del.key_len = udump->key_len;
- op->u.flow_del.stats = &ops[n_ops].stats;
- n_ops++;
-
- if (ukey) {
- *ukey_stats = ukey->stats;
- ukey_delete(revalidator, ukey);
- } else {
- memset(ukey_stats, 0, sizeof *ukey_stats);
- }
+ struct dump_op *dop = &ops[n_ops++];
+ dump_op_init(dop, udump->key, udump->key_len, ukey, udump);
continue;
}
if (!ukey) {
- ukey = xmalloc(sizeof *ukey);
-
- ukey->key = (struct nlattr *) &ukey->key_buf;
- memcpy(ukey->key, udump->key, udump->key_len);
- ukey->key_len = udump->key_len;
-
- ukey->created = used ? used : now;
- memset(&ukey->stats, 0, sizeof ukey->stats);
-
- ukey->mark = false;
-
+ ukey = ukey_create(udump->key, udump->key_len, used);
hmap_insert(&revalidator->ukeys, &ukey->hmap_node,
udump->key_hash);
}
free(udump);
}
- for (i = 0; i < n_ops; i++) {
- opsp[i] = &ops[i].op;
- }
- dpif_operate(udpif->dpif, opsp, n_ops);
-
- for (i = 0; i < n_ops; i++) {
- struct dpif_flow_stats push, *stats, *ukey_stats;
-
- ukey_stats = &ops[i].ukey_stats;
- stats = ops[i].op.u.flow_del.stats;
- push.used = MAX(stats->used, ukey_stats->used);
- push.tcp_flags = stats->tcp_flags | ukey_stats->tcp_flags;
- push.n_packets = stats->n_packets - ukey_stats->n_packets;
- push.n_bytes = stats->n_bytes - ukey_stats->n_bytes;
-
- if (push.n_packets || netflow_exists()) {
- struct ofproto_dpif *ofproto;
- struct netflow *netflow;
- struct flow flow;
-
- if (!xlate_receive(udpif->backer, NULL, ops[i].op.u.flow_del.key,
- ops[i].op.u.flow_del.key_len, &flow,
- &ofproto, NULL, NULL, &netflow, NULL)) {
- struct xlate_in xin;
-
- xlate_in_init(&xin, ofproto, &flow, NULL, push.tcp_flags,
- NULL);
- xin.resubmit_stats = push.n_packets ? &push : NULL;
- xin.may_learn = push.n_packets > 0;
- xin.skip_wildcards = true;
- xlate_actions_for_side_effects(&xin);
-
- if (netflow) {
- netflow_expire(netflow, &flow);
- netflow_flow_clear(netflow, &flow);
- netflow_unref(netflow);
- }
- }
- }
- }
+ push_dump_ops(revalidator, ops, n_ops);
LIST_FOR_EACH_SAFE (udump, next_udump, list_node, udumps) {
list_remove(&udump->list_node);
}
static void
-revalidator_sweep(struct revalidator *revalidator)
+revalidator_sweep__(struct revalidator *revalidator, bool purge)
{
+ struct dump_op ops[REVALIDATE_MAX_BATCH];
struct udpif_key *ukey, *next;
+ size_t n_ops;
+
+ n_ops = 0;
HMAP_FOR_EACH_SAFE (ukey, next, hmap_node, &revalidator->ukeys) {
- if (ukey->mark) {
+ if (!purge && ukey->mark) {
ukey->mark = false;
} else {
- ukey_delete(revalidator, ukey);
+ struct dump_op *op = &ops[n_ops++];
+
+ /* If we have previously seen a flow in the datapath, but didn't
+ * see it during the most recent dump, delete it. This allows us
+ * to clean up the ukey and keep the statistics consistent. */
+ dump_op_init(op, ukey->key, ukey->key_len, ukey, NULL);
+ if (n_ops == REVALIDATE_MAX_BATCH) {
+ push_dump_ops(revalidator, ops, n_ops);
+ n_ops = 0;
+ }
}
}
+
+ if (n_ops) {
+ push_dump_ops(revalidator, ops, n_ops);
+ }
+}
+
+static void
+revalidator_sweep(struct revalidator *revalidator)
+{
+ revalidator_sweep__(revalidator, false);
+}
+
+static void
+revalidator_purge(struct revalidator *revalidator)
+{
+ revalidator_sweep__(revalidator, true);
}
\f
static void
-/* Copyright (c) 2013 Nicira, Inc.
+/* Copyright (c) 2013, 2014 Nicira, Inc.
*
* Licensed under the Apache License, Version 2.0 (the "License");
* you may not use this file except in compliance with the License.
struct udpif *udpif_create(struct dpif_backer *, struct dpif *);
void udpif_set_threads(struct udpif *, size_t n_handlers,
size_t n_revalidators);
+void udpif_synchronize(struct udpif *);
void udpif_destroy(struct udpif *);
void udpif_revalidate(struct udpif *);
void udpif_get_memory_usage(struct udpif *, struct simap *usage);
struct xport *xport;
struct ofpact_output output;
struct flow flow;
- union flow_in_port in_port_;
ofpact_init(&output.ofpact, OFPACT_OUTPUT, sizeof output);
/* Use OFPP_NONE as the in_port to avoid special packet processing. */
- in_port_.ofp_port = OFPP_NONE;
- flow_extract(packet, 0, 0, NULL, &in_port_, &flow);
+ flow_extract(packet, NULL, &flow);
+ flow.in_port.ofp_port = OFPP_NONE;
ovs_rwlock_rdlock(&xlate_rwlock);
xport = xport_lookup(ofport);
xlate_remove_ofproto(ofproto);
ovs_rwlock_unlock(&xlate_rwlock);
- /* Discard any flow_miss_batches queued up for 'ofproto', avoiding a
- * use-after-free error. */
- udpif_revalidate(ofproto->backer->udpif);
+ /* Ensure that the upcall processing threads have no remaining references
+ * to the ofproto or anything in it. */
+ udpif_synchronize(ofproto->backer->udpif);
hmap_remove(&all_ofproto_dpifs, &ofproto->all_ofproto_dpifs_node);
execute.md.tunnel = flow->tunnel;
execute.md.skb_priority = flow->skb_priority;
execute.md.pkt_mark = flow->pkt_mark;
- execute.md.in_port = ofp_port_to_odp_port(ofproto, in_port);
+ execute.md.in_port.odp_port = ofp_port_to_odp_port(ofproto, in_port);
execute.needs_help = (xout.slow & SLOW_ACTION) != 0;
error = dpif_execute(ofproto->backer->dpif, &execute);
flow_compose(packet, flow);
} else {
union flow_in_port in_port = flow->in_port;
+ struct pkt_metadata md;
/* Use the metadata from the flow and the packet argument
* to reconstruct the flow. */
- flow_extract(packet, flow->skb_priority, flow->pkt_mark, NULL,
- &in_port, flow);
+ pkt_metadata_init(&md, NULL, flow->skb_priority,
+ flow->pkt_mark, &in_port);
+
+ flow_extract(packet, &md, flow);
}
}
struct dpif_port dpif_port;
struct dpif_port_dump port_dump;
struct hmap portno_names;
+ void *state = NULL;
+ int error;
ofproto = ofproto_dpif_lookup(argv[argc - 1]);
if (!ofproto) {
}
ds_init(&ds);
- dpif_flow_dump_start(&flow_dump, ofproto->backer->dpif);
- while (dpif_flow_dump_next(&flow_dump, &key, &key_len, &mask, &mask_len,
- &actions, &actions_len, &stats)) {
+ error = dpif_flow_dump_start(&flow_dump, ofproto->backer->dpif);
+ if (error) {
+ goto exit;
+ }
+ dpif_flow_dump_state_init(ofproto->backer->dpif, &state);
+ while (dpif_flow_dump_next(&flow_dump, state, &key, &key_len,
+ &mask, &mask_len, &actions, &actions_len,
+ &stats)) {
if (!ofproto_dpif_contains_flow(ofproto, key, key_len)) {
continue;
}
format_odp_actions(&ds, actions, actions_len);
ds_put_char(&ds, '\n');
}
+ dpif_flow_dump_state_uninit(ofproto->backer->dpif, state);
+ error = dpif_flow_dump_done(&flow_dump);
- if (dpif_flow_dump_done(&flow_dump)) {
+exit:
+ if (error) {
ds_clear(&ds);
ds_put_format(&ds, "dpif/dump_flows failed: %s", ovs_strerror(errno));
unixctl_command_reply_error(conn, ds_cstr(&ds));
if (error) {
goto error;
}
- connmgr_send_port_status(p->connmgr, pp, OFPPR_ADD);
+ connmgr_send_port_status(p->connmgr, NULL, pp, OFPPR_ADD);
return;
error:
static void
ofport_remove(struct ofport *ofport)
{
- connmgr_send_port_status(ofport->ofproto->connmgr, &ofport->pp,
+ connmgr_send_port_status(ofport->ofproto->connmgr, NULL, &ofport->pp,
OFPPR_DELETE);
ofport_destroy(ofport);
}
port->pp.curr_speed = pp->curr_speed;
port->pp.max_speed = pp->max_speed;
- connmgr_send_port_status(port->ofproto->connmgr, &port->pp, OFPPR_MODIFY);
+ connmgr_send_port_status(port->ofproto->connmgr, NULL,
+ &port->pp, OFPPR_MODIFY);
}
/* Update OpenFlow 'state' in 'port' and notify controller. */
{
if (port->pp.state != state) {
port->pp.state = state;
- connmgr_send_port_status(port->ofproto->connmgr, &port->pp,
- OFPPR_MODIFY);
+ connmgr_send_port_status(port->ofproto->connmgr, NULL,
+ &port->pp, OFPPR_MODIFY);
}
}
guarded_list_pop_all(&ofproto->rule_executes, &executes);
LIST_FOR_EACH_SAFE (e, next, list_node, &executes) {
- union flow_in_port in_port_;
struct flow flow;
- in_port_.ofp_port = e->in_port;
- flow_extract(e->packet, 0, 0, NULL, &in_port_, &flow);
+ flow_extract(e->packet, NULL, &flow);
+ flow.in_port.ofp_port = e->in_port;
ofproto->ofproto_class->rule_execute(e->rule, &flow, e->packet);
rule_execute_destroy(e);
uint64_t ofpacts_stub[1024 / 8];
struct ofpbuf ofpacts;
struct flow flow;
- union flow_in_port in_port_;
enum ofperr error;
COVERAGE_INC(ofproto_packet_out);
}
/* Verify actions against packet, then send packet if successful. */
- in_port_.ofp_port = po.in_port;
- flow_extract(payload, 0, 0, NULL, &in_port_, &flow);
+ flow_extract(payload, NULL, &flow);
+ flow.in_port.ofp_port = po.in_port;
error = ofproto_check_ofpacts(p, po.ofpacts, po.ofpacts_len);
if (!error) {
error = p->ofproto_class->packet_out(p, payload, &flow,
}
static void
-update_port_config(struct ofport *port,
+update_port_config(struct ofconn *ofconn, struct ofport *port,
enum ofputil_port_config config,
enum ofputil_port_config mask)
{
- enum ofputil_port_config old_config = port->pp.config;
- enum ofputil_port_config toggle;
+ enum ofputil_port_config toggle = (config ^ port->pp.config) & mask;
- toggle = (config ^ port->pp.config) & mask;
- if (toggle & OFPUTIL_PC_PORT_DOWN) {
- if (config & OFPUTIL_PC_PORT_DOWN) {
- netdev_turn_flags_off(port->netdev, NETDEV_UP, NULL);
- } else {
- netdev_turn_flags_on(port->netdev, NETDEV_UP, NULL);
- }
+ if (toggle & OFPUTIL_PC_PORT_DOWN
+ && (config & OFPUTIL_PC_PORT_DOWN
+ ? netdev_turn_flags_off(port->netdev, NETDEV_UP, NULL)
+ : netdev_turn_flags_on(port->netdev, NETDEV_UP, NULL))) {
+ /* We tried to bring the port up or down, but it failed, so don't
+ * update the "down" bit. */
toggle &= ~OFPUTIL_PC_PORT_DOWN;
}
- port->pp.config ^= toggle;
- if (port->pp.config != old_config) {
+ if (toggle) {
+ enum ofputil_port_config old_config = port->pp.config;
+ port->pp.config ^= toggle;
port->ofproto->ofproto_class->port_reconfigured(port, old_config);
+ connmgr_send_port_status(port->ofproto->connmgr, ofconn, &port->pp,
+ OFPPR_MODIFY);
}
}
} else if (!eth_addr_equals(port->pp.hw_addr, pm.hw_addr)) {
return OFPERR_OFPPMFC_BAD_HW_ADDR;
} else {
- update_port_config(port, pm.config, pm.mask);
+ update_port_config(ofconn, port, pm.config, pm.mask);
if (pm.advertise) {
netdev_set_advertisements(port->netdev, pm.advertise);
}
struct flow_wildcards *wc)
{
if (tnl_port_should_receive(flow)) {
- memset(&wc->masks.tunnel, 0xff, sizeof wc->masks.tunnel);
+ wc->masks.tunnel.tun_id = OVS_BE64_MAX;
+ wc->masks.tunnel.ip_src = OVS_BE32_MAX;
+ wc->masks.tunnel.ip_dst = OVS_BE32_MAX;
wc->masks.tunnel.flags = (FLOW_TNL_F_DONT_FRAGMENT |
FLOW_TNL_F_CSUM |
FLOW_TNL_F_KEY);
+ wc->masks.tunnel.ip_tos = UINT8_MAX;
+ wc->masks.tunnel.ip_ttl = UINT8_MAX;
+
memset(&wc->masks.pkt_mark, 0xff, sizeof wc->masks.pkt_mark);
if (!tnl_ecn_ok(base_flow, flow)) {
#include "daemon.h"
#include "dirs.h"
#include "dynamic-string.h"
+#include "fatal-signal.h"
#include "json.h"
#include "jsonrpc.h"
#include "lib/table.h"
proctitle_init(argc, argv);
set_program_name(argv[0]);
parse_options(argc, argv);
- signal(SIGPIPE, SIG_IGN);
+ fatal_ignore_sigpipe();
if (optind >= argc) {
ovs_fatal(0, "missing command name; use --help for help");
# Putting '\" p as the first line tells "man" that the manpage
# needs to be preprocessed by "pic".
s = r''''\" p
-.TH "%s" 5 "%s" "Open vSwitch" "Open vSwitch Manual"
+.TH "%s" 5 " DB Schema %s" "Open vSwitch %s" "Open vSwitch Manual"
.\" -*- nroff -*-
.de TQ
. br
.SH NAME
%s \- %s database schema
.PP
-''' % (title, version, textToNroff(schema.name), schema.name)
+''' % (title, schema.version, version, textToNroff(schema.name), schema.name)
tables = ""
introNodes = []
print "\nextern struct ovsdb_idl_class %sidl_class;" % prefix
print "\nvoid %sinit(void);" % prefix
+
+ print "\nconst char * %sget_db_version(void);" % prefix
print "\n#endif /* %(prefix)sIDL_HEADER */" % {'prefix': prefix.upper()}
def printEnum(members):
print " %s_columns_init();" % structName
print "}"
+ print """
+/* Return the schema version. The caller must not free the returned value. */
+const char *
+%sget_db_version(void)
+{
+ return "%s";
+}
+""" % (prefix, schema.version)
+
+
def ovsdb_escape(string):
def escape(match):
#include "dirs.h"
#include "dummy.h"
#include "dynamic-string.h"
+#include "fatal-signal.h"
#include "file.h"
#include "hash.h"
#include "json.h"
proctitle_init(argc, argv);
set_program_name(argv[0]);
service_start(&argc, &argv);
- signal(SIGPIPE, SIG_IGN);
+ fatal_ignore_sigpipe();
process_init();
parse_options(&argc, &argv, &remotes, &unixctl_path, &run_command);
#include "compiler.h"
#include "dirs.h"
#include "dynamic-string.h"
+#include "fatal-signal.h"
#include "file.h"
#include "lockfile.h"
#include "log.h"
{
set_program_name(argv[0]);
parse_options(argc, argv);
- signal(SIGPIPE, SIG_IGN);
+ fatal_ignore_sigpipe();
run_command(argc - optind, argv + optind, get_all_commands());
return 0;
}
def set_dscp(sock, dscp):
if dscp > 63:
raise ValueError("Invalid dscp %d" % dscp)
+
+ # Note: this function is used for both of IPv4 and IPv6 sockets
+ success = False
val = dscp << 2
- sock.setsockopt(socket.IPPROTO_IP, socket.IP_TOS, val)
+ try:
+ sock.setsockopt(socket.IPPROTO_IP, socket.IP_TOS, val)
+ except socket.error, e:
+ if e.errno != errno.ENOPROTOOPT:
+ raise
+ success = True
+ try:
+ sock.setsockopt(socket.IPPROTO_IPV6, socket.IPV6_TCLASS, val)
+ except socket.error, e:
+ if e.errno != errno.ENOPROTOOPT or not success:
+ raise
esac
export MALLOC_CONF
esac
+
+# The name of loopback interface
+case `uname` in
+Linux)
+ LOOPBACK_INTERFACE=lo
+ ;;
+FreeBSD|NetBSD)
+ LOOPBACK_INTERFACE=lo0
+ ;;
+esac
for i in 1 2 3; do
ovs-appctl netdev-dummy/receive p1 'in_port(1),eth(src=40:44:44:44:00:00,dst=50:54:00:00:00:07),eth_type(0x0800),ipv4(src=192.168.0.1,dst=192.168.0.2,proto=6,tos=0,ttl=64,frag=no)'
done
+sleep 1
OVS_WAIT_UNTIL([ovs-appctl -t ovs-ofctl exit])
AT_CHECK([cat ofctl_monitor.log | ofctl_strip], [0], [dnl
for i in 1 2 3; do
ovs-appctl netdev-dummy/receive p1 'in_port(1),eth(src=40:44:44:44:00:01,dst=50:54:00:00:00:07),eth_type(0x8847),mpls(label=10,tc=0,ttl=64,bos=1)'
done
+sleep 1
OVS_WAIT_UNTIL([ovs-appctl -t ovs-ofctl exit])
AT_CHECK([cat ofctl_monitor.log | ofctl_strip], [0], [dnl
for i in 1 2 3; do
ovs-appctl netdev-dummy/receive p1 'in_port(1),eth(src=40:44:44:44:00:02,dst=50:54:00:00:00:07),eth_type(0x8847),mpls(label=10,tc=0,ttl=64,bos=1)'
done
+sleep 1
OVS_WAIT_UNTIL([ovs-appctl -t ovs-ofctl exit])
AT_CHECK([cat ofctl_monitor.log | ofctl_strip], [0], [dnl
for i in 1 2 3; do
ovs-appctl netdev-dummy/receive p1 'in_port(1),eth(src=40:44:44:44:54:50,dst=50:54:00:00:00:07),eth_type(0x0800),ipv4(src=192.168.0.1,dst=192.168.0.2,proto=6,tos=0,ttl=64,frag=no)'
done
+sleep 1
OVS_WAIT_UNTIL([ovs-appctl -t ovs-ofctl exit])
AT_CHECK([cat ofctl_monitor.log | ofctl_strip], [0], [dnl
set Interface p2 options:ifindex=1003 -- \
set Bridge br0 sflow=@sf -- \
--id=@sf create sflow targets=\"$1:$SFLOW_PORT\" \
- header=128 sampling=1 polling=1 agent=lo
+ header=128 sampling=1 polling=1 agent=$LOOPBACK_INTERFACE
dnl open with ARP packets to seed the bridge-learning. The output
dnl ifIndex numbers should be reported predictably after that.
CHECK_NETFLOW_ACTIVE_EXPIRATION([127.0.0.1], [IPv4])
CHECK_NETFLOW_ACTIVE_EXPIRATION([[[::1]]], [IPv6])
+AT_SETUP([ofproto-dpif - flow stats])
+OVS_VSWITCHD_START
+AT_CHECK([ovs-ofctl add-flow br0 "ip,actions=NORMAL"])
+AT_CHECK([ovs-ofctl add-flow br0 "icmp,actions=NORMAL"])
+
+ovs-appctl time/stop
+
+for i in `seq 1 10`; do
+ ovs-appctl netdev-dummy/receive br0 'in_port(0),eth(src=50:54:00:00:00:07,dst=50:54:00:00:00:05),eth_type(0x0800),ipv4(src=192.168.0.2,dst=192.168.0.1,proto=6,tos=0,ttl=64,frag=no)'
+done
+
+ovs-appctl time/warp 1000
+
+AT_CHECK([ovs-ofctl dump-flows br0], [0], [stdout])
+AT_CHECK([STRIP_XIDS stdout | sed -n 's/duration=[[0-9]]*\.[[0-9]]*s/duration=0.0s/p' | sort], [0], [dnl
+ cookie=0x0, duration=0.0s, table=0, n_packets=0, n_bytes=0, idle_age=1, icmp actions=NORMAL
+ cookie=0x0, duration=0.0s, table=0, n_packets=10, n_bytes=600, idle_age=1, ip actions=NORMAL
+])
+OVS_VSWITCHD_STOP
+AT_CLEANUP
+
+AT_SETUP([ofproto-dpif - flow stats, set-n-threads])
+OVS_VSWITCHD_START
+AT_CHECK([ovs-ofctl add-flow br0 "ip,actions=NORMAL"])
+AT_CHECK([ovs-ofctl add-flow br0 "icmp,actions=NORMAL"])
+
+ovs-appctl time/stop
+
+for i in `seq 1 10`; do
+ ovs-appctl netdev-dummy/receive br0 'in_port(0),eth(src=50:54:00:00:00:07,dst=50:54:00:00:00:05),eth_type(0x0800),ipv4(src=192.168.0.2,dst=192.168.0.1,proto=6,tos=0,ttl=64,frag=no)'
+done
+
+ovs-appctl time/warp 100
+AT_CHECK([ovs-vsctl set Open_vSwitch . other-config:n-revalidator-threads=2])
+ovs-appctl time/warp 1000
+
+AT_CHECK([ovs-ofctl dump-flows br0], [0], [stdout])
+AT_CHECK([STRIP_XIDS stdout | sed -n 's/duration=[[0-9]]*\.[[0-9]]*s/duration=0.0s/p' | sort], [0], [dnl
+ cookie=0x0, duration=0.0s, table=0, n_packets=0, n_bytes=0, idle_age=1, icmp actions=NORMAL
+ cookie=0x0, duration=0.0s, table=0, n_packets=10, n_bytes=600, idle_age=1, ip actions=NORMAL
+])
+OVS_VSWITCHD_STOP
+AT_CLEANUP
+
AT_SETUP([idle_age and hard_age increase over time])
OVS_VSWITCHD_START
for i in $(seq 1 10); do
ovs-appctl netdev-dummy/receive br0 'in_port(100),eth(src=50:54:00:00:00:05,dst=50:54:00:00:00:07),eth_type(0x0800),ipv4(src=192.168.0.1,dst=192.168.0.2,proto=1,tos=0,ttl=64,frag=no),icmp(type=8,code=0)'
+ if [[ $i -eq 1 ]]; then
+ sleep 1
+ fi
done
for i in $(seq 1 5); do
ovs-appctl netdev-dummy/receive br1 'in_port(101),eth(src=50:54:00:00:00:07,dst=50:54:00:00:00:05),eth_type(0x0800),ipv4(src=192.168.0.2,dst=192.168.0.1,proto=1,tos=0,ttl=64,frag=no),icmp(type=8,code=0)'
+ if [[ $i -eq 1 ]]; then
+ sleep 1
+ fi
done
AT_CHECK([ovs-appctl time/warp 500], [0],
#include "command-line.h"
#include "compiler.h"
#include "daemon.h"
+#include "fatal-signal.h"
#include "learning-switch.h"
#include "ofp-parse.h"
#include "ofp-version-opt.h"
proctitle_init(argc, argv);
set_program_name(argv[0]);
parse_options(argc, argv);
- signal(SIGPIPE, SIG_IGN);
+ fatal_ignore_sigpipe();
if (argc - optind < 1) {
ovs_fatal(0, "at least one vconn argument required; "
struct ofp10_match extracted_match;
struct match match;
struct flow flow;
- union flow_in_port in_port_;
n++;
retval = ovs_pcap_read(pcap, &packet, NULL);
ovs_fatal(retval, "error reading pcap file");
}
- in_port_.ofp_port = u16_to_ofp(1);
- flow_extract(packet, 0, 0, NULL, &in_port_, &flow);
+ flow_extract(packet, NULL, &flow);
+ flow.in_port.ofp_port = u16_to_ofp(1);
+
match_wc_init(&match, &flow);
ofputil_match_to_ofp10_match(&match, &extracted_match);
#include <stdlib.h>
#include <unistd.h>
#include "command-line.h"
+#include "fatal-signal.h"
#include "ofp-msgs.h"
#include "ofp-util.h"
#include "ofpbuf.h"
set_program_name(argv[0]);
vlog_set_levels(NULL, VLF_ANY_FACILITY, VLL_EMER);
vlog_set_levels(NULL, VLF_CONSOLE, VLL_DBG);
- signal(SIGPIPE, SIG_IGN);
+ fatal_ignore_sigpipe();
time_alarm(10);
#include "dirs.h"
#include "dpif.h"
#include "dynamic-string.h"
+#include "fatal-signal.h"
#include "flow.h"
#include "match.h"
#include "netdev.h"
{
set_program_name(argv[0]);
parse_options(argc, argv);
- signal(SIGPIPE, SIG_IGN);
+ fatal_ignore_sigpipe();
run_command(argc - optind, argv + optind, get_all_commands());
return 0;
}
size_t key_len;
size_t mask_len;
struct ds ds;
- char *name, *error, *filter = NULL;
+ char *name, *filter = NULL;
struct flow flow_filter;
struct flow_wildcards wc_filter;
+ void *state = NULL;
+ int error;
if (argc > 1 && !strncmp(argv[argc - 1], "filter=", 7)) {
filter = xstrdup(argv[--argc] + 7);
}
if (filter) {
- error = parse_ofp_exact_flow(&flow_filter, &wc_filter.masks, filter,
- &names_portno);
- if (error) {
- ovs_fatal(0, "Failed to parse filter (%s)", error);
+ char *err = parse_ofp_exact_flow(&flow_filter, &wc_filter.masks,
+ filter, &names_portno);
+ if (err) {
+ ovs_fatal(0, "Failed to parse filter (%s)", err);
}
}
ds_init(&ds);
- dpif_flow_dump_start(&flow_dump, dpif);
- while (dpif_flow_dump_next(&flow_dump, &key, &key_len,
- &mask, &mask_len,
- &actions, &actions_len, &stats)) {
+ error = dpif_flow_dump_start(&flow_dump, dpif);
+ if (error) {
+ goto exit;
+ }
+ dpif_flow_dump_state_init(dpif, &state);
+ while (dpif_flow_dump_next(&flow_dump, state, &key, &key_len,
+ &mask, &mask_len, &actions, &actions_len,
+ &stats)) {
if (filter) {
struct flow flow;
struct flow_wildcards wc;
format_odp_actions(&ds, actions, actions_len);
printf("%s\n", ds_cstr(&ds));
}
- dpif_flow_dump_done(&flow_dump);
+ dpif_flow_dump_state_uninit(dpif, state);
+ error = dpif_flow_dump_done(&flow_dump);
+exit:
+ if (error) {
+ ovs_fatal(error, "Failed to dump flows from datapath");
+ }
free(filter);
odp_portno_names_destroy(&portno_names);
hmap_destroy(&portno_names);
# of ovs-ctl. It is also useful to document the o/p in ovs-ctl.log.
display=`"${datadir}/scripts/ovs-ctl" "$@" 2>&1`
rc=$?
- echo "${display}" | tee -a "${logdir}/ovs-ctl.log"
+ if test -w "${logdir}/ovs-ctl.log"; then
+ echo "${display}" | tee -a "${logdir}/ovs-ctl.log"
+ else
+ echo "${display}"
+ fi
return ${rc}
;;
*)
#include "compiler.h"
#include "dirs.h"
#include "dynamic-string.h"
+#include "fatal-signal.h"
#include "nx-match.h"
#include "odp-util.h"
#include "ofp-actions.h"
{
set_program_name(argv[0]);
parse_options(argc, argv);
- signal(SIGPIPE, SIG_IGN);
+ fatal_ignore_sigpipe();
run_command(argc - optind, argv + optind, get_all_commands());
return 0;
}
struct ofpbuf *packet;
long long int when;
struct flow flow;
+ const struct pkt_metadata md = PKT_METADATA_INITIALIZER(ODPP_NONE);
error = ovs_pcap_read(file, &packet, &when);
if (error) {
break;
}
- flow_extract(packet, 0, 0, NULL, NULL, &flow);
+ flow_extract(packet, &md, &flow);
if (flow.dl_type == htons(ETH_TYPE_IP)
&& flow.nw_proto == IPPROTO_TCP
&& (is_openflow_port(flow.tp_src, argv + 2) ||
for (;;) {
struct ofpbuf *packet;
struct flow flow;
+ const struct pkt_metadata md = PKT_METADATA_INITIALIZER(ODPP_NONE);
int error;
error = ovs_pcap_read(pcap, &packet, NULL);
ovs_fatal(error, "%s: read failed", argv[1]);
}
- flow_extract(packet, 0, 0, NULL, NULL, &flow);
+ flow_extract(packet, &md, &flow);
flow_print(stdout, &flow);
putchar('\n');
ofpbuf_delete(packet);
.so lib/ssl-bootstrap.man
.so lib/ssl-peer-ca-cert.man
.so lib/vlog.man
+.so lib/common.man
.
.SH COMMANDS
The commands implemented by \fBovs\-vsctl\fR are described in the
#include "compiler.h"
#include "dirs.h"
#include "dynamic-string.h"
+#include "fatal-signal.h"
#include "hash.h"
#include "json.h"
#include "ovsdb-data.h"
char *args;
set_program_name(argv[0]);
- signal(SIGPIPE, SIG_IGN);
+ fatal_ignore_sigpipe();
vlog_set_levels(NULL, VLF_CONSOLE, VLL_WARN);
vlog_set_levels(&VLM_reconnect, VLF_ANY_FACILITY, VLL_WARN);
ovsrec_init();
case 'V':
ovs_print_version(0, 0);
+ printf("DB Schema %s\n", ovsrec_get_db_version());
exit(EXIT_SUCCESS);
case 't':
static struct iface *iface_find(const char *name);
static struct iface *iface_from_ofp_port(const struct bridge *,
ofp_port_t ofp_port);
-static void iface_set_mac(struct iface *, const uint8_t *);
+static void iface_set_mac(const struct bridge *, const struct port *, struct iface *);
static void iface_set_ofport(const struct ovsrec_interface *, ofp_port_t ofport);
static void iface_clear_db_record(const struct ovsrec_interface *if_cfg);
static void iface_configure_qos(struct iface *, const struct ovsrec_qos *);
iface_set_ofport(iface->cfg, iface->ofp_port);
iface_configure_cfm(iface);
iface_configure_qos(iface, port->cfg->qos);
- iface_set_mac(iface, port->cfg->fake_bridge ? br->ea : NULL);
+ iface_set_mac(br, port, iface);
ofproto_port_set_bfd(br->ofproto, iface->ofp_port,
&iface->cfg->bfd);
}
}
static void
-bridge_pick_local_hw_addr(struct bridge *br, uint8_t ea[ETH_ADDR_LEN],
- struct iface **hw_addr_iface)
+find_local_hw_addr(const struct bridge *br, uint8_t ea[ETH_ADDR_LEN],
+ const struct port *fake_br, struct iface **hw_addr_iface)
{
struct hmapx mirror_output_ports;
- const char *hwaddr;
struct port *port;
bool found_addr = false;
int error;
int i;
- *hw_addr_iface = NULL;
-
- /* Did the user request a particular MAC? */
- hwaddr = smap_get(&br->cfg->other_config, "hwaddr");
- if (hwaddr && eth_addr_from_string(hwaddr, ea)) {
- if (eth_addr_is_multicast(ea)) {
- VLOG_ERR("bridge %s: cannot set MAC address to multicast "
- "address "ETH_ADDR_FMT, br->name, ETH_ADDR_ARGS(ea));
- } else if (eth_addr_is_zero(ea)) {
- VLOG_ERR("bridge %s: cannot set MAC address to zero", br->name);
- } else {
- return;
- }
- }
-
/* Mirror output ports don't participate in picking the local hardware
* address. ofproto can't help us find out whether a given port is a
* mirror output because we haven't configured mirrors yet, so we need to
continue;
}
+ /* For fake bridges we only choose from ports with the same tag */
+ if (fake_br && fake_br->cfg && fake_br->cfg->tag) {
+ if (!port->cfg->tag) {
+ continue;
+ }
+ if (*port->cfg->tag != *fake_br->cfg->tag) {
+ continue;
+ }
+ }
+
/* Grab MAC. */
error = netdev_get_etheraddr(iface->netdev, iface_ea);
if (error) {
hmapx_destroy(&mirror_output_ports);
}
+static void
+bridge_pick_local_hw_addr(struct bridge *br, uint8_t ea[ETH_ADDR_LEN],
+ struct iface **hw_addr_iface)
+{
+ const char *hwaddr;
+ *hw_addr_iface = NULL;
+
+ /* Did the user request a particular MAC? */
+ hwaddr = smap_get(&br->cfg->other_config, "hwaddr");
+ if (hwaddr && eth_addr_from_string(hwaddr, ea)) {
+ if (eth_addr_is_multicast(ea)) {
+ VLOG_ERR("bridge %s: cannot set MAC address to multicast "
+ "address "ETH_ADDR_FMT, br->name, ETH_ADDR_ARGS(ea));
+ } else if (eth_addr_is_zero(ea)) {
+ VLOG_ERR("bridge %s: cannot set MAC address to zero", br->name);
+ } else {
+ return;
+ }
+ }
+
+ /* Find a local hw address */
+ find_local_hw_addr(br, ea, NULL, hw_addr_iface);
+}
+
/* Choose and returns the datapath ID for bridge 'br' given that the bridge
* Ethernet address is 'bridge_ea'. If 'bridge_ea' is the Ethernet address of
* an interface on 'br', then that interface must be passed in as
/* Set Ethernet address of 'iface', if one is specified in the configuration
* file. */
static void
-iface_set_mac(struct iface *iface, const uint8_t *mac)
+iface_set_mac(const struct bridge *br, const struct port *port, struct iface *iface)
{
- uint8_t ea[ETH_ADDR_LEN];
+ uint8_t ea[ETH_ADDR_LEN], *mac = NULL;
+ struct iface *hw_addr_iface;
if (strcmp(iface->type, "internal")) {
return;
if (iface->cfg->mac && eth_addr_from_string(iface->cfg->mac, ea)) {
mac = ea;
+ } else if (port->cfg->fake_bridge) {
+ /* Fake bridge and no MAC set in the configuration. Pick a local one. */
+ find_local_hw_addr(br, ea, port, &hw_addr_iface);
+ mac = ea;
}
if (mac) {
#include "dirs.h"
#include "dpif.h"
#include "dummy.h"
+#include "fatal-signal.h"
#include "memory.h"
#include "netdev.h"
#include "openflow/openflow.h"
set_program_name(argv[0]);
service_start(&argc, &argv);
remote = parse_options(argc, argv, &unixctl_path);
- signal(SIGPIPE, SIG_IGN);
+ fatal_ignore_sigpipe();
ovsrec_init();
daemonize_start();
$(OVSDB_DOC) \
--title="vtep" \
$(VTEP_DOT_DIAGRAM_ARG) \
+ --version=$(VERSION) \
$(srcdir)/vtep/vtep.ovsschema \
$(srcdir)/vtep/vtep.xml > $@.tmp
mv $@.tmp $@
.so lib/ssl-bootstrap.man
.so lib/ssl-peer-ca-cert.man
.so lib/vlog.man
+.so lib/common.man
.
.SH COMMANDS
The commands implemented by \fBvtep\-ctl\fR are described in the
#include "compiler.h"
#include "dirs.h"
#include "dynamic-string.h"
+#include "fatal-signal.h"
#include "hash.h"
#include "json.h"
#include "ovsdb-data.h"
char *args;
set_program_name(argv[0]);
- signal(SIGPIPE, SIG_IGN);
+ fatal_ignore_sigpipe();
vlog_set_levels(NULL, VLF_CONSOLE, VLL_WARN);
vlog_set_levels(&VLM_reconnect, VLF_ANY_FACILITY, VLL_WARN);
vteprec_init();
case 'V':
ovs_print_version(0, 0);
+ printf("DB Schema %s\n", vteprec_get_db_version());
exit(EXIT_SUCCESS);
case 't':