summaryrefslogtreecommitdiff
path: root/lib
diff options
context:
space:
mode:
authorBen Pfaff <blp@nicira.com>2009-12-02 11:49:53 -0800
committerBen Pfaff <blp@nicira.com>2009-12-02 11:49:53 -0800
commit58fda1dab104041fc693032475ec4662c1a52849 (patch)
treec6adcb818ad9233155d4d65872e8144dae6fe723 /lib
parentc3bb4bd7f1d9c045a5e5d7062b09d4dac4e48195 (diff)
parent6c88d577e83db12f73df12be8fc575419b011fda (diff)
downloadopenvswitch-58fda1dab104041fc693032475ec4662c1a52849.tar.gz
Merge "master" branch into "db".
Diffstat (limited to 'lib')
-rw-r--r--lib/backtrace.c6
-rw-r--r--lib/classifier.h14
-rw-r--r--lib/dpif-linux.c2
-rw-r--r--lib/dpif-netdev.c30
-rw-r--r--lib/dpif-provider.h4
-rw-r--r--lib/dpif.c24
-rw-r--r--lib/dpif.h1
-rw-r--r--lib/fault.c2
-rw-r--r--lib/flow.c2
-rw-r--r--lib/leak-checker.c2
-rw-r--r--lib/learning-switch.c43
-rw-r--r--lib/learning-switch.h4
-rw-r--r--lib/lockfile.c4
-rw-r--r--lib/mac-learning.c35
-rw-r--r--lib/mac-learning.h3
-rw-r--r--lib/netdev-linux.c131
-rw-r--r--lib/netdev-provider.h82
-rw-r--r--lib/netdev.c227
-rw-r--r--lib/netdev.h7
-rw-r--r--lib/odp-util.c5
-rw-r--r--lib/ofp-print.c15
-rw-r--r--lib/pcap.c2
-rw-r--r--lib/poll-loop.c3
-rw-r--r--lib/socket-util.c37
-rw-r--r--lib/socket-util.h4
-rw-r--r--lib/stream-tcp.c4
-rw-r--r--lib/svec.c16
-rw-r--r--lib/svec.h1
-rw-r--r--lib/timeval.c18
-rw-r--r--lib/unixctl.c2
-rw-r--r--lib/vconn-ssl.c4
-rw-r--r--lib/vconn-tcp.c4
-rw-r--r--lib/vconn.c23
-rw-r--r--lib/vlog-modules.def1
-rw-r--r--lib/vlog-unixctl.man4
-rw-r--r--lib/vlog.man4
36 files changed, 634 insertions, 136 deletions
diff --git a/lib/backtrace.c b/lib/backtrace.c
index 0999a081e..2f4780932 100644
--- a/lib/backtrace.c
+++ b/lib/backtrace.c
@@ -42,7 +42,7 @@ get_max_stack(void)
for (line_number = 1; fgets(line, sizeof line, f); line_number++) {
if (strstr(line, "[stack]")) {
uintptr_t end;
- if (sscanf(line, "%*"SCNxPTR"-%"SCNxPTR, &end) != 1) {
+ if (sscanf(line, "%*x-%"SCNxPTR, &end) != 1) {
VLOG_WARN("%s:%d: parse error", file_name, line_number);
continue;
}
@@ -73,6 +73,10 @@ stack_low(void)
uintptr_t low;
asm("movl %%esp,%0" : "=g" (low));
return low;
+#elif __x86_64__
+ uintptr_t low;
+ asm("movq %%rsp,%0" : "=g" (low));
+ return low;
#else
/* This causes a warning in GCC that cannot be disabled, so use it only on
* non-x86. */
diff --git a/lib/classifier.h b/lib/classifier.h
index 8b095e911..194b04e6b 100644
--- a/lib/classifier.h
+++ b/lib/classifier.h
@@ -25,6 +25,20 @@
* fields after F tend to be wildcarded as well. If this assumption is
* violated, then the classifier will still classify flows correctly, but its
* performance will suffer.
+ *
+ * The classifier uses a collection of CLS_N_FIELDS hash tables for wildcarded
+ * flows. Each of these tables contains the flows that wildcard a given field
+ * and do not wildcard any of the fields that precede F in the ordering. The
+ * key for each hash table is the value of the fields preceding F that are not
+ * wildcarded. All the flows that fall within a table and have the same key
+ * are kept as a linked list ordered from highest to lowest priority.
+ *
+ * The classifier also maintains a separate hash table of exact-match flows.
+ *
+ * To search the classifier we first search the table of exact-match flows,
+ * since exact-match flows always have highest priority. If there is a match,
+ * we're done. Otherwise, we search each of the CLS_N_FIELDS hash tables in
+ * turn, looking for the highest-priority match, and return it (if any).
*/
#include "flow.h"
diff --git a/lib/dpif-linux.c b/lib/dpif-linux.c
index 8216d1870..2bf329f45 100644
--- a/lib/dpif-linux.c
+++ b/lib/dpif-linux.c
@@ -419,7 +419,7 @@ dpif_linux_recv(struct dpif *dpif_, struct ofpbuf **bufp)
return 0;
} else {
VLOG_WARN_RL(&error_rl, "%s: discarding message truncated "
- "from %zu bytes to %d",
+ "from %"PRIu32" bytes to %d",
dpif_name(dpif_), msg->length, retval);
error = ERANGE;
}
diff --git a/lib/dpif-netdev.c b/lib/dpif-netdev.c
index 8bd9648de..816d4025f 100644
--- a/lib/dpif-netdev.c
+++ b/lib/dpif-netdev.c
@@ -373,9 +373,13 @@ do_add_port(struct dp_netdev *dp, const char *devname, uint16_t flags,
if (!internal) {
error = netdev_open(devname, NETDEV_ETH_TYPE_ANY, &netdev);
} else {
- char *tapname = xasprintf("tap:%s", devname);
- error = netdev_open(tapname, NETDEV_ETH_TYPE_ANY, &netdev);
- free(tapname);
+ error = netdev_create(devname, "tap", NULL);
+ if (!error) {
+ error = netdev_open(devname, NETDEV_ETH_TYPE_ANY, &netdev);
+ if (error) {
+ netdev_destroy(devname);
+ }
+ }
}
if (error) {
return error;
@@ -468,6 +472,7 @@ static int
do_del_port(struct dp_netdev *dp, uint16_t port_no)
{
struct dp_netdev_port *port;
+ char *name;
int error;
error = get_port_by_number(dp, port_no, &port);
@@ -480,7 +485,12 @@ do_del_port(struct dp_netdev *dp, uint16_t port_no)
dp->n_ports--;
dp->serial++;
+ name = xstrdup(netdev_get_name(port->netdev));
netdev_close(port->netdev);
+ if (port->internal) {
+ netdev_destroy(name);
+ }
+ free(name);
free(port);
return 0;
@@ -665,7 +675,7 @@ dp_netdev_lookup_flow(const struct dp_netdev *dp, const flow_t *key)
}
static void
-answer_flow_query(const struct dp_netdev_flow *flow,
+answer_flow_query(struct dp_netdev_flow *flow, uint32_t query_flags,
struct odp_flow *odp_flow)
{
if (flow) {
@@ -683,6 +693,11 @@ answer_flow_query(const struct dp_netdev_flow *flow,
n * sizeof *odp_flow->actions);
odp_flow->n_actions = flow->n_actions;
}
+
+ if (query_flags & ODPFF_ZERO_TCP_FLAGS) {
+ flow->tcp_ctl = 0;
+ }
+
} else {
odp_flow->stats.error = ENOENT;
}
@@ -696,7 +711,8 @@ dpif_netdev_flow_get(const struct dpif *dpif, struct odp_flow flows[], int n)
for (i = 0; i < n; i++) {
struct odp_flow *odp_flow = &flows[i];
- answer_flow_query(dp_netdev_lookup_flow(dp, &odp_flow->key), odp_flow);
+ answer_flow_query(dp_netdev_lookup_flow(dp, &odp_flow->key),
+ odp_flow->flags, odp_flow);
}
return 0;
}
@@ -852,7 +868,7 @@ dpif_netdev_flow_del(struct dpif *dpif, struct odp_flow *odp_flow)
flow = dp_netdev_lookup_flow(dp, &odp_flow->key);
if (flow) {
- answer_flow_query(flow, odp_flow);
+ answer_flow_query(flow, 0, odp_flow);
dp_netdev_free_flow(dp, flow);
return 0;
} else {
@@ -872,7 +888,7 @@ dpif_netdev_flow_list(const struct dpif *dpif, struct odp_flow flows[], int n)
if (i >= n) {
break;
}
- answer_flow_query(flow, &flows[i++]);
+ answer_flow_query(flow, 0, &flows[i++]);
}
return hmap_count(&dp->flow_table);
}
diff --git a/lib/dpif-provider.h b/lib/dpif-provider.h
index bd159b2a9..020e017cd 100644
--- a/lib/dpif-provider.h
+++ b/lib/dpif-provider.h
@@ -42,7 +42,7 @@ static inline void dpif_assert_class(const struct dpif *dpif,
}
/* Datapath interface class structure, to be defined by each implementation of
- * a datapath interface
+ * a datapath interface.
*
* These functions return 0 if successful or a positive errno value on failure,
* except where otherwise noted.
@@ -52,7 +52,7 @@ static inline void dpif_assert_class(const struct dpif *dpif,
* EWOULDBLOCK or EINPROGRESS. We may relax this requirement in the future if
* and when we encounter performance problems. */
struct dpif_class {
- /* Prefix for names of dpifs in this class, e.g. "udatapath:".
+ /* Prefix for names of dpifs in this class, e.g. "netdev:".
*
* One dpif class may have the empty string "" as its prefix, in which case
* that dpif class is associated with dpif names that don't match any other
diff --git a/lib/dpif.c b/lib/dpif.c
index 649c2464c..793eaa118 100644
--- a/lib/dpif.c
+++ b/lib/dpif.c
@@ -169,13 +169,35 @@ dpif_open(const char *name, struct dpif **dpifp)
/* Tries to create and open a new datapath with the given 'name'. Will fail if
* a datapath named 'name' already exists. Returns 0 if successful, otherwise
* a positive errno value. On success stores a pointer to the datapath in
- * '*dpifp', otherwise a null pointer.*/
+ * '*dpifp', otherwise a null pointer. */
int
dpif_create(const char *name, struct dpif **dpifp)
{
return do_open(name, true, dpifp);
}
+/* Tries to open a datapath with the given 'name', creating it if it does not
+ * exist. Returns 0 if successful, otherwise a positive errno value. On
+ * success stores a pointer to the datapath in '*dpifp', otherwise a null
+ * pointer. */
+int
+dpif_create_and_open(const char *name, struct dpif **dpifp)
+{
+ int error;
+
+ error = dpif_create(name, dpifp);
+ if (error == EEXIST || error == EBUSY) {
+ error = dpif_open(name, dpifp);
+ if (error) {
+ VLOG_WARN("datapath %s already exists but cannot be opened: %s",
+ name, strerror(error));
+ }
+ } else if (error) {
+ VLOG_WARN("failed to create datapath %s: %s", name, strerror(error));
+ }
+ return error;
+}
+
/* Closes and frees the connection to 'dpif'. Does not destroy the datapath
* itself; call dpif_delete() first, instead, if that is desirable. */
void
diff --git a/lib/dpif.h b/lib/dpif.h
index 216c09969..1d109c2d2 100644
--- a/lib/dpif.h
+++ b/lib/dpif.h
@@ -37,6 +37,7 @@ int dp_enumerate(struct svec *);
int dpif_open(const char *name, struct dpif **);
int dpif_create(const char *name, struct dpif **);
+int dpif_create_and_open(const char *name, struct dpif **);
void dpif_close(struct dpif *);
const char *dpif_name(const struct dpif *);
diff --git a/lib/fault.c b/lib/fault.c
index 3882eff7c..14e229ed6 100644
--- a/lib/fault.c
+++ b/lib/fault.c
@@ -54,7 +54,7 @@ log_backtrace(void)
if (!dladdr(frame[1], &addrinfo) || !addrinfo.dli_sname) {
fprintf(stderr, " 0x%08"PRIxPTR"\n", (uintptr_t) frame[1]);
} else {
- fprintf(stderr, " 0x%08"PRIxPTR" (%s+0x%x)\n",
+ fprintf(stderr, " 0x%08"PRIxPTR" (%s+0x%tx)\n",
(uintptr_t) frame[1], addrinfo.dli_sname,
(char *) frame[1] - (char *) addrinfo.dli_saddr);
}
diff --git a/lib/flow.c b/lib/flow.c
index c1f6240f0..7d4a1bd4d 100644
--- a/lib/flow.c
+++ b/lib/flow.c
@@ -317,7 +317,7 @@ flow_to_string(const flow_t *flow)
void
flow_format(struct ds *ds, const flow_t *flow)
{
- ds_put_format(ds, "port%04x:vlan%d mac"ETH_ADDR_FMT"->"ETH_ADDR_FMT" "
+ ds_put_format(ds, "in_port%04x:vlan%d mac"ETH_ADDR_FMT"->"ETH_ADDR_FMT" "
"type%04x proto%"PRId8" ip"IP_FMT"->"IP_FMT" port%d->%d",
flow->in_port, ntohs(flow->dl_vlan),
ETH_ADDR_ARGS(flow->dl_src), ETH_ADDR_ARGS(flow->dl_dst),
diff --git a/lib/leak-checker.c b/lib/leak-checker.c
index c2c4348f5..8d256bc8c 100644
--- a/lib/leak-checker.c
+++ b/lib/leak-checker.c
@@ -141,7 +141,7 @@ log_callers(const char *format, ...)
putc(':', file);
backtrace_capture(&backtrace);
for (i = 0; i < backtrace.n_frames; i++) {
- fprintf(file, " 0x%x", backtrace.frames[i]);
+ fprintf(file, " 0x%"PRIxPTR, backtrace.frames[i]);
}
putc('\n', file);
}
diff --git a/lib/learning-switch.c b/lib/learning-switch.c
index 99d5ee4c0..78346acbb 100644
--- a/lib/learning-switch.c
+++ b/lib/learning-switch.c
@@ -57,6 +57,8 @@ struct lswitch {
uint32_t capabilities;
time_t last_features_request;
struct mac_learning *ml; /* NULL to act as hub instead of switch. */
+ bool exact_flows; /* Use exact-match flows? */
+ bool action_normal; /* Use OFPP_NORMAL? */
/* Number of outgoing queued packets on the rconn. */
struct rconn_packet_counter *queued;
@@ -105,7 +107,8 @@ static packet_handler_func process_stats_reply;
*
* 'rconn' is used to send out an OpenFlow features request. */
struct lswitch *
-lswitch_create(struct rconn *rconn, bool learn_macs, int max_idle)
+lswitch_create(struct rconn *rconn, bool learn_macs,
+ bool exact_flows, int max_idle, bool action_normal)
{
struct lswitch *sw;
size_t i;
@@ -115,6 +118,8 @@ lswitch_create(struct rconn *rconn, bool learn_macs, int max_idle)
sw->datapath_id = 0;
sw->last_features_request = time_now() - 1;
sw->ml = learn_macs ? mac_learning_create() : NULL;
+ sw->action_normal = action_normal;
+ sw->exact_flows = exact_flows;
sw->queued = rconn_packet_counter_create();
sw->next_query = LLONG_MIN;
sw->last_query = LLONG_MIN;
@@ -430,10 +435,34 @@ process_packet_in(struct lswitch *sw, struct rconn *rconn, void *opi_)
/* Don't send out packets on their input ports. */
goto drop_it;
} else if (sw->max_idle >= 0 && (!sw->ml || out_port != OFPP_FLOOD)) {
+ struct ofpbuf *buffer;
+ struct ofp_flow_mod *ofm;
+ uint32_t wildcards;
+
+ /* Check if we need to wildcard the flows. */
+ if (!sw->exact_flows) {
+ /* We can not wildcard all fields.
+ * We need in_port to detect moves.
+ * We need both SA and DA to do learning. */
+ wildcards = (OFPFW_DL_TYPE | OFPFW_NW_SRC_MASK | OFPFW_NW_DST_MASK
+ | OFPFW_NW_PROTO | OFPFW_TP_SRC | OFPFW_TP_DST);
+ } else {
+ /* Exact match */
+ wildcards = 0;
+ }
+
+ /* Check if we need to use "NORMAL" action. */
+ if (sw->action_normal && out_port != OFPP_FLOOD) {
+ out_port = OFPP_NORMAL;
+ }
+
/* The output port is known, or we always flood everything, so add a
* new flow. */
- queue_tx(sw, rconn, make_add_simple_flow(&flow, ntohl(opi->buffer_id),
- out_port, sw->max_idle));
+ buffer = make_add_simple_flow(&flow, ntohl(opi->buffer_id),
+ out_port, sw->max_idle);
+ ofm = buffer->data;
+ ofm->match.wildcards = htonl(wildcards);
+ queue_tx(sw, rconn, buffer);
/* If the switch didn't buffer the packet, we need to send a copy. */
if (ntohl(opi->buffer_id) == UINT32_MAX) {
@@ -441,9 +470,15 @@ process_packet_in(struct lswitch *sw, struct rconn *rconn, void *opi_)
make_unbuffered_packet_out(&pkt, in_port, out_port));
}
} else {
+ struct ofpbuf *b;
+
+ /* Check if we need to use "NORMAL" action. */
+ if (sw->action_normal && out_port != OFPP_FLOOD) {
+ out_port = OFPP_NORMAL;
+ }
+
/* We don't know that MAC, or we don't set up flows. Send along the
* packet without setting up a flow. */
- struct ofpbuf *b;
if (ntohl(opi->buffer_id) == UINT32_MAX) {
b = make_unbuffered_packet_out(&pkt, in_port, out_port);
} else {
diff --git a/lib/learning-switch.h b/lib/learning-switch.h
index 8837d6470..2de862e6d 100644
--- a/lib/learning-switch.h
+++ b/lib/learning-switch.h
@@ -22,7 +22,9 @@
struct ofpbuf;
struct rconn;
-struct lswitch *lswitch_create(struct rconn *, bool learn_macs, int max_idle);
+struct lswitch *lswitch_create(struct rconn *, bool learn_macs,
+ bool exact_flows, int max_idle,
+ bool action_normal);
void lswitch_run(struct lswitch *, struct rconn *);
void lswitch_wait(struct lswitch *);
void lswitch_destroy(struct lswitch *);
diff --git a/lib/lockfile.c b/lib/lockfile.c
index e5a041eed..9bb7c6b18 100644
--- a/lib/lockfile.c
+++ b/lib/lockfile.c
@@ -61,8 +61,8 @@ lockfile_name(const char *file_name)
{
const char *slash = strrchr(file_name, '/');
return (slash
- ? xasprintf("%.*s/.%s.~lock~", slash - file_name, file_name,
- slash + 1)
+ ? xasprintf("%.*s/.%s.~lock~",
+ (int) (slash - file_name), file_name, slash + 1)
: xasprintf(".%s.~lock~", file_name));
}
diff --git a/lib/mac-learning.c b/lib/mac-learning.c
index c9b7d3e73..3f1db1461 100644
--- a/lib/mac-learning.c
+++ b/lib/mac-learning.c
@@ -21,6 +21,7 @@
#include <inttypes.h>
#include <stdlib.h>
+#include "bitmap.h"
#include "coverage.h"
#include "hash.h"
#include "list.h"
@@ -129,6 +130,7 @@ mac_learning_create(void)
list_push_front(&ml->free, &s->lru_node);
}
ml->secret = random_uint32();
+ ml->non_learning_vlans = NULL;
return ml;
}
@@ -136,9 +138,36 @@ mac_learning_create(void)
void
mac_learning_destroy(struct mac_learning *ml)
{
+ if (ml) {
+ bitmap_free(ml->non_learning_vlans);
+ }
free(ml);
}
+/* Provides a bitmap of VLANs which have learning disabled. It takes
+ * ownership of the bitmap. Returns true if the set has changed from
+ * the previous value. */
+bool
+mac_learning_set_disabled_vlans(struct mac_learning *ml, unsigned long *bitmap)
+{
+ bool ret = (bitmap == NULL
+ ? ml->non_learning_vlans != NULL
+ : (ml->non_learning_vlans == NULL
+ || !bitmap_equal(bitmap, ml->non_learning_vlans, 4096)));
+
+ bitmap_free(ml->non_learning_vlans);
+ ml->non_learning_vlans = bitmap;
+
+ return ret;
+}
+
+static bool
+is_learning_vlan(const struct mac_learning *ml, uint16_t vlan)
+{
+ return !(ml->non_learning_vlans
+ && bitmap_is_set(ml->non_learning_vlans, vlan));
+}
+
/* Attempts to make 'ml' learn from the fact that a frame from 'src_mac' was
* just observed arriving from 'src_port' on the given 'vlan'.
*
@@ -156,6 +185,10 @@ mac_learning_learn(struct mac_learning *ml,
struct mac_entry *e;
struct list *bucket;
+ if (!is_learning_vlan(ml, vlan)) {
+ return 0;
+ }
+
if (eth_addr_is_multicast(src_mac)) {
static struct vlog_rate_limit rl = VLOG_RATE_LIMIT_INIT(30, 30);
VLOG_DBG_RL(&rl, "multicast packet source "ETH_ADDR_FMT,
@@ -216,7 +249,7 @@ mac_learning_lookup_tag(const struct mac_learning *ml,
const uint8_t dst[ETH_ADDR_LEN], uint16_t vlan,
tag_type *tag)
{
- if (eth_addr_is_multicast(dst)) {
+ if (eth_addr_is_multicast(dst) || !is_learning_vlan(ml, vlan)) {
return -1;
} else {
struct mac_entry *e = search_bucket(mac_table_bucket(ml, dst, vlan),
diff --git a/lib/mac-learning.h b/lib/mac-learning.h
index e2ee74ba4..ed843cd45 100644
--- a/lib/mac-learning.h
+++ b/lib/mac-learning.h
@@ -52,10 +52,13 @@ struct mac_learning {
struct list table[MAC_HASH_SIZE]; /* Hash table. */
struct mac_entry entries[MAC_MAX]; /* All entries. */
uint32_t secret; /* Secret for */
+ unsigned long *non_learning_vlans; /* Bitmap of learning disabled VLANs. */
};
struct mac_learning *mac_learning_create(void);
void mac_learning_destroy(struct mac_learning *);
+bool mac_learning_set_disabled_vlans(struct mac_learning *,
+ unsigned long *bitmap);
tag_type mac_learning_learn(struct mac_learning *,
const uint8_t src[ETH_ADDR_LEN], uint16_t vlan,
uint16_t src_port);
diff --git a/lib/netdev-linux.c b/lib/netdev-linux.c
index c33405fd3..47d89efa1 100644
--- a/lib/netdev-linux.c
+++ b/lib/netdev-linux.c
@@ -67,6 +67,14 @@
#define ADVERTISED_Asym_Pause (1 << 14)
#endif
+/* Provider-specific netdev object. Netdev objects are devices that are
+ * created by the netdev library through a netdev_create() call. */
+struct netdev_obj_linux {
+ struct netdev_obj netdev_obj;
+
+ int tap_fd; /* File descriptor for TAP device. */
+};
+
struct netdev_linux {
struct netdev netdev;
@@ -142,6 +150,13 @@ static int set_etheraddr(const char *netdev_name, int hwaddr_family,
static int get_stats_via_netlink(int ifindex, struct netdev_stats *stats);
static int get_stats_via_proc(const char *netdev_name, struct netdev_stats *stats);
+static struct netdev_obj_linux *
+netdev_obj_linux_cast(const struct netdev_obj *netdev_obj)
+{
+ netdev_obj_assert_class(netdev_obj, &netdev_linux_class);
+ return CONTAINER_OF(netdev_obj, struct netdev_obj_linux, netdev_obj);
+}
+
static struct netdev_linux *
netdev_linux_cast(const struct netdev *netdev)
{
@@ -194,9 +209,77 @@ netdev_linux_cache_cb(const struct rtnetlink_change *change,
}
}
+/* Creates the netdev object of 'type' with 'name'. */
+static int
+netdev_linux_create(const char *name, const char *type,
+ const struct shash *args, bool created)
+{
+ struct netdev_obj_linux *netdev_obj;
+ static const char tap_dev[] = "/dev/net/tun";
+ struct ifreq ifr;
+ int error;
+
+ if (!shash_is_empty(args)) {
+ VLOG_WARN("arguments for %s devices should be empty", type);
+ }
+
+ /* Create the name binding in the netdev library for this object. */
+ netdev_obj = xcalloc(1, sizeof *netdev_obj);
+ netdev_obj_init(&netdev_obj->netdev_obj, name, &netdev_linux_class,
+ created);
+ netdev_obj->tap_fd = -1;
+
+ if (strcmp(type, "tap")) {
+ return 0;
+ }
+
+ /* Open tap device. */
+ netdev_obj->tap_fd = open(tap_dev, O_RDWR);
+ if (netdev_obj->tap_fd < 0) {
+ error = errno;
+ VLOG_WARN("opening \"%s\" failed: %s", tap_dev, strerror(error));
+ goto error;
+ }
+
+ /* Create tap device. */
+ ifr.ifr_flags = IFF_TAP | IFF_NO_PI;
+ strncpy(ifr.ifr_name, name, sizeof ifr.ifr_name);
+ if (ioctl(netdev_obj->tap_fd, TUNSETIFF, &ifr) == -1) {
+ VLOG_WARN("%s: creating tap device failed: %s", name,
+ strerror(errno));
+ error = errno;
+ goto error;
+ }
+
+ /* Make non-blocking. */
+ error = set_nonblocking(netdev_obj->tap_fd);
+ if (error) {
+ goto error;
+ }
+
+ return 0;
+
+error:
+ netdev_destroy(name);
+ return error;
+}
+
+/* Destroys the netdev object 'netdev_obj_'. */
+static void
+netdev_linux_destroy(struct netdev_obj *netdev_obj_)
+{
+ struct netdev_obj_linux *netdev_obj = netdev_obj_linux_cast(netdev_obj_);
+
+ if (netdev_obj->tap_fd >= 0) {
+ close(netdev_obj->tap_fd);
+ }
+ free(netdev_obj);
+
+ return;
+}
+
static int
-netdev_linux_open(const char *name, char *suffix, int ethertype,
- struct netdev **netdevp)
+netdev_linux_open(const char *name, int ethertype, struct netdev **netdevp)
{
struct netdev_linux *netdev;
enum netdev_flags flags;
@@ -204,10 +287,10 @@ netdev_linux_open(const char *name, char *suffix, int ethertype,
/* Allocate network device. */
netdev = xzalloc(sizeof *netdev);
- netdev_init(&netdev->netdev, suffix, &netdev_linux_class);
+ netdev_init(&netdev->netdev, name, &netdev_linux_class);
netdev->netdev_fd = -1;
netdev->tap_fd = -1;
- netdev->cache = shash_find_data(&cache_map, suffix);
+ netdev->cache = shash_find_data(&cache_map, name);
if (!netdev->cache) {
if (shash_is_empty(&cache_map)) {
int error = rtnetlink_notifier_register(
@@ -218,14 +301,14 @@ netdev_linux_open(const char *name, char *suffix, int ethertype,
}
}
netdev->cache = xmalloc(sizeof *netdev->cache);
- netdev->cache->shash_node = shash_add(&cache_map, suffix,
+ netdev->cache->shash_node = shash_add(&cache_map, name,
netdev->cache);
netdev->cache->valid = 0;
netdev->cache->ref_cnt = 0;
}
netdev->cache->ref_cnt++;
- if (!strncmp(name, "tap:", 4)) {
+ if (!strcmp(netdev_get_type(&netdev->netdev), "tap")) {
static const char tap_dev[] = "/dev/net/tun";
struct ifreq ifr;
@@ -239,9 +322,9 @@ netdev_linux_open(const char *name, char *suffix, int ethertype,
/* Create tap device. */
ifr.ifr_flags = IFF_TAP | IFF_NO_PI;
- strncpy(ifr.ifr_name, suffix, sizeof ifr.ifr_name);
+ strncpy(ifr.ifr_name, name, sizeof ifr.ifr_name);
if (ioctl(netdev->tap_fd, TUNSETIFF, &ifr) == -1) {
- VLOG_WARN("%s: creating tap device failed: %s", suffix,
+ VLOG_WARN("%s: creating tap device failed: %s", name,
strerror(errno));
error = errno;
goto error;
@@ -296,7 +379,7 @@ netdev_linux_open(const char *name, char *suffix, int ethertype,
if (bind(netdev->netdev_fd,
(struct sockaddr *) &sll, sizeof sll) < 0) {
error = errno;
- VLOG_ERR("bind to %s failed: %s", suffix, strerror(error));
+ VLOG_ERR("bind to %s failed: %s", name, strerror(error));
goto error;
}
@@ -548,6 +631,17 @@ netdev_linux_get_mtu(const struct netdev *netdev_, int *mtup)
return 0;
}
+/* Returns the ifindex of 'netdev', if successful, as a positive number.
+ * On failure, returns a negative errno value. */
+static int
+netdev_linux_get_ifindex(const struct netdev *netdev)
+{
+ int ifindex, error;
+
+ error = get_ifindex(netdev, &ifindex);
+ return error ? -error : ifindex;
+}
+
static int
netdev_linux_get_carrier(const struct netdev *netdev_, bool *carrier)
{
@@ -720,8 +814,7 @@ netdev_linux_get_stats(const struct netdev *netdev_, struct netdev_stats *stats)
/* Stores the features supported by 'netdev' into each of '*current',
* '*advertised', '*supported', and '*peer' that are non-null. Each value is a
* bitmap of "enum ofp_port_features" bits, in host byte order. Returns 0 if
- * successful, otherwise a positive errno value. On failure, all of the
- * passed-in values are set to 0. */
+ * successful, otherwise a positive errno value. */
static int
netdev_linux_get_features(struct netdev *netdev,
uint32_t *current, uint32_t *advertised,
@@ -1367,13 +1460,16 @@ netdev_linux_poll_remove(struct netdev_notifier *notifier_)
}
const struct netdev_class netdev_linux_class = {
- "", /* prefix */
- "linux", /* name */
+ "system", /* type */
netdev_linux_init,
netdev_linux_run,
netdev_linux_wait,
+ netdev_linux_create,
+ netdev_linux_destroy,
+ NULL, /* reconfigure */
+
netdev_linux_open,
netdev_linux_close,
@@ -1389,6 +1485,7 @@ const struct netdev_class netdev_linux_class = {
netdev_linux_set_etheraddr,
netdev_linux_get_etheraddr,
netdev_linux_get_mtu,
+ netdev_linux_get_ifindex,
netdev_linux_get_carrier,
netdev_linux_get_stats,
@@ -1411,13 +1508,16 @@ const struct netdev_class netdev_linux_class = {
};
const struct netdev_class netdev_tap_class = {
- "tap", /* prefix */
- "tap", /* name */
+ "tap", /* type */
netdev_linux_init,
NULL, /* run */
NULL, /* wait */
+ netdev_linux_create,
+ netdev_linux_destroy,
+ NULL, /* reconfigure */
+
netdev_linux_open,
netdev_linux_close,
@@ -1433,6 +1533,7 @@ const struct netdev_class netdev_tap_class = {
netdev_linux_set_etheraddr,
netdev_linux_get_etheraddr,
netdev_linux_get_mtu,
+ netdev_linux_get_ifindex,
netdev_linux_get_carrier,
netdev_linux_get_stats,
diff --git a/lib/netdev-provider.h b/lib/netdev-provider.h
index 3bc7fd444..64d227e8d 100644
--- a/lib/netdev-provider.h
+++ b/lib/netdev-provider.h
@@ -22,6 +22,26 @@
#include <assert.h>
#include "netdev.h"
#include "list.h"
+#include "shash.h"
+
+/* A network device object that was created through the netdev_create()
+ * call.
+ *
+ * This structure should be treated as opaque by network device
+ * implementations. */
+struct netdev_obj {
+ const struct netdev_class *class;
+ int ref_cnt;
+ bool created; /* Was netdev_create() called? */
+};
+
+void netdev_obj_init(struct netdev_obj *, const char *name,
+ const struct netdev_class *, bool created);
+static inline void netdev_obj_assert_class(const struct netdev_obj *netdev_obj,
+ const struct netdev_class *class)
+{
+ assert(netdev_obj->class == class);
+}
/* A network device (e.g. an Ethernet device).
*
@@ -30,6 +50,7 @@
struct netdev {
const struct netdev_class *class;
char *name; /* e.g. "eth0" */
+
enum netdev_flags save_flags; /* Initial device flags. */
enum netdev_flags changed_flags; /* Flags that we changed. */
struct list node; /* Element in global list. */
@@ -42,6 +63,7 @@ static inline void netdev_assert_class(const struct netdev *netdev,
{
assert(netdev->class == class);
}
+const char *netdev_get_type(const struct netdev *netdev);
/* A network device notifier.
*
@@ -62,15 +84,13 @@ void netdev_notifier_init(struct netdev_notifier *, struct netdev *,
* These functions return 0 if successful or a positive errno value on failure,
* except where otherwise noted. */
struct netdev_class {
- /* Prefix for names of netdevs in this class, e.g. "ndunix:".
+ /* Type of netdevs in this class, e.g. "system", "tap", "gre", etc.
*
- * One netdev class may have the empty string "" as its prefix, in which
- * case that netdev class is associated with netdev names that do not
- * contain a colon. */
- const char *prefix;
-
- /* Class name, for use in error messages. */
- const char *name;
+ * One of the providers should supply a "system" type, since this is
+ * the type assumed when a device name was not bound through the
+ * netdev_create() call. The "system" type corresponds to an
+ * existing network device on the system. */
+ const char *type;
/* Called only once, at program startup. Returning an error from this
* function will prevent any network device in this class from being
@@ -88,19 +108,45 @@ struct netdev_class {
* to be called. May be null if nothing is needed here. */
void (*wait)(void);
+ /* Attempts to create a network device object of 'type' with 'name'.
+ * 'type' corresponds to the 'type' field used in the netdev_class
+ * structure.
+ *
+ * The 'created' flag indicates that the user called netdev_create()
+ * and thus will eventually call netdev_destroy(). If the flag is
+ * false, then the object was dynamically created based on a call to
+ * netdev_open() without first calling netdev_create() and will be
+ * automatically destroyed when no more netdevs have 'name' open. A
+ * provider implementation should pass this flag to netdev_obj_init(). */
+ int (*create)(const char *name, const char *type,
+ const struct shash *args, bool created);
+
+ /* Destroys 'netdev_obj'.
+ *
+ * Netdev objects maintain a reference count that is incremented on
+ * netdev_open() and decremented on netdev_close(). If 'netdev_obj'
+ * has a non-zero reference count, then this function will not be
+ * called. */
+ void (*destroy)(struct netdev_obj *netdev_obj);
+
+ /* Reconfigures the device object 'netdev_obj' with 'args'.
+ *
+ * If this netdev class does not support reconfiguring a netdev
+ * object, this may be a null pointer.
+ */
+ int (*reconfigure)(struct netdev_obj *netdev_obj,
+ const struct shash *args);
+
/* Attempts to open a network device. On success, sets '*netdevp' to the
- * new network device. 'name' is the full network device name provided by
+ * new network device. 'name' is the network device name provided by
* the user. This name is useful for error messages but must not be
* modified.
*
- * 'suffix' is a copy of 'name' following the netdev's 'prefix'.
- *
* 'ethertype' may be a 16-bit Ethernet protocol value in host byte order
* to capture frames of that type received on the device. It may also be
* one of the 'enum netdev_pseudo_ethertype' values to receive frames in
* one of those categories. */
- int (*open)(const char *name, char *suffix, int ethertype,
- struct netdev **netdevp);
+ int (*open)(const char *name, int ethertype, struct netdev **netdevp);
/* Closes 'netdev'. */
void (*close)(struct netdev *netdev);
@@ -164,6 +210,16 @@ struct netdev_class {
* bytes for Ethernet devices.*/
int (*get_mtu)(const struct netdev *, int *mtup);
+ /* Returns the ifindex of 'netdev', if successful, as a positive number.
+ * On failure, returns a negative errno value.
+ *
+ * The desired semantics of the ifindex value are a combination of those
+ * specified by POSIX for if_nametoindex() and by SNMP for ifIndex. An
+ * ifindex value should be unique within a host and remain stable at least
+ * until reboot. SNMP says an ifindex "ranges between 1 and the value of
+ * ifNumber" but many systems do not follow this rule anyhow. */
+ int (*get_ifindex)(const struct netdev *);
+
/* Sets 'carrier' to true if carrier is active (link light is on) on
* 'netdev'. */
int (*get_carrier)(const struct netdev *netdev, bool *carrier);
diff --git a/lib/netdev.c b/lib/netdev.c
index 481671f79..fb0f98e6e 100644
--- a/lib/netdev.c
+++ b/lib/netdev.c
@@ -45,6 +45,9 @@ static const struct netdev_class *netdev_classes[] = {
};
static int n_netdev_classes = ARRAY_SIZE(netdev_classes);
+/* All created network devices. */
+static struct shash netdev_obj_shash = SHASH_INITIALIZER(&netdev_obj_shash);
+
/* All open network devices. */
static struct list netdev_list = LIST_INITIALIZER(&netdev_list);
@@ -59,7 +62,8 @@ static int restore_flags(struct netdev *netdev);
* otherwise a positive errno value.
*
* Calling this function is optional. If not called explicitly, it will
- * automatically be called upon the first attempt to open a network device. */
+ * automatically be called upon the first attempt to open or create a
+ * network device. */
int
netdev_initialize(void)
{
@@ -78,7 +82,7 @@ netdev_initialize(void)
netdev_classes[j++] = class;
} else {
VLOG_ERR("failed to initialize %s network device "
- "class: %s", class->name, strerror(retval));
+ "class: %s", class->type, strerror(retval));
if (!status) {
status = retval;
}
@@ -124,6 +128,92 @@ netdev_wait(void)
}
}
+/* Attempts to create a network device object of 'type' with 'name'. 'type'
+ * corresponds to the 'type' field used in the netdev_class * structure.
+ * Arguments for creation are provided in 'args', which may be empty or NULL
+ * if none are needed. */
+int
+netdev_create(const char *name, const char *type, const struct shash *args)
+{
+ struct shash empty_args = SHASH_INITIALIZER(&empty_args);
+ int i;
+
+ netdev_initialize();
+
+ if (!args) {
+ args = &empty_args;
+ }
+
+ if (shash_find(&netdev_obj_shash, name)) {
+ VLOG_WARN("attempted to create a netdev object with bound name: %s",
+ name);
+ return EEXIST;
+ }
+
+ for (i = 0; i < n_netdev_classes; i++) {
+ const struct netdev_class *class = netdev_classes[i];
+ if (!strcmp(type, class->type)) {
+ return class->create(name, type, args, true);
+ }
+ }
+
+ VLOG_WARN("could not create netdev object of unknown type: %s", type);
+
+ return EINVAL;
+}
+
+/* Destroys netdev object 'name'. Netdev objects maintain a reference count
+ * which is incremented on netdev_open() and decremented on netdev_close().
+ * If 'name' has a non-zero reference count, it will not destroy the object
+ * and return EBUSY. */
+int
+netdev_destroy(const char *name)
+{
+ struct shash_node *node;
+ struct netdev_obj *netdev_obj;
+
+ node = shash_find(&netdev_obj_shash, name);
+ if (!node) {
+ return ENODEV;
+ }
+
+ netdev_obj = node->data;
+ if (netdev_obj->ref_cnt != 0) {
+ VLOG_WARN("attempt to destroy open netdev object (%d): %s",
+ netdev_obj->ref_cnt, name);
+ return EBUSY;
+ }
+
+ shash_delete(&netdev_obj_shash, node);
+ netdev_obj->class->destroy(netdev_obj);
+
+ return 0;
+}
+
+/* Reconfigures the device object 'name' with 'args'. 'args' may be empty
+ * or NULL if none are needed. */
+int
+netdev_reconfigure(const char *name, const struct shash *args)
+{
+ struct shash empty_args = SHASH_INITIALIZER(&empty_args);
+ struct netdev_obj *netdev_obj;
+
+ if (!args) {
+ args = &empty_args;
+ }
+
+ netdev_obj = shash_find_data(&netdev_obj_shash, name);
+ if (!netdev_obj) {
+ return ENODEV;
+ }
+
+ if (netdev_obj->class->reconfigure) {
+ return netdev_obj->class->reconfigure(netdev_obj, args);
+ }
+
+ return 0;
+}
+
/* Opens the network device named 'name' (e.g. "eth0") and returns zero if
* successful, otherwise a positive errno value. On success, sets '*netdevp'
* to the new network device, otherwise to null.
@@ -133,37 +223,43 @@ netdev_wait(void)
* the 'enum netdev_pseudo_ethertype' values to receive frames in one of those
* categories. */
int
-netdev_open(const char *name_, int ethertype, struct netdev **netdevp)
+netdev_open(const char *name, int ethertype, struct netdev **netdevp)
{
- char *name = xstrdup(name_);
- char *prefix, *suffix, *colon;
+ struct netdev_obj *netdev_obj;
struct netdev *netdev = NULL;
int error;
int i;
netdev_initialize();
- colon = strchr(name, ':');
- if (colon) {
- *colon = '\0';
- prefix = name;
- suffix = colon + 1;
+
+ netdev_obj = shash_find_data(&netdev_obj_shash, name);
+ if (netdev_obj) {
+ error = netdev_obj->class->open(name, ethertype, &netdev);
} else {
- prefix = "";
- suffix = name;
- }
+ /* Default to "system". */
+ error = EAFNOSUPPORT;
+ for (i = 0; i < n_netdev_classes; i++) {
+ const struct netdev_class *class = netdev_classes[i];
+ if (!strcmp(class->type, "system")) {
+ struct shash empty_args = SHASH_INITIALIZER(&empty_args);
- for (i = 0; i < n_netdev_classes; i++) {
- const struct netdev_class *class = netdev_classes[i];
- if (!strcmp(prefix, class->prefix)) {
- error = class->open(name_, suffix, ethertype, &netdev);
- goto exit;
+ /* Dynamically create the netdev object, but indicate
+ * that it should be destroyed when the the last user
+ * closes its handle. */
+ error = class->create(name, "system", &empty_args, false);
+ if (!error) {
+ error = class->open(name, ethertype, &netdev);
+ netdev_obj = shash_find_data(&netdev_obj_shash, name);
+ }
+ break;
+ }
}
}
- error = EAFNOSUPPORT;
+ if (!error) {
+ netdev_obj->ref_cnt++;
+ }
-exit:
*netdevp = error ? NULL : netdev;
- free(name);
return error;
}
@@ -172,9 +268,24 @@ void
netdev_close(struct netdev *netdev)
{
if (netdev) {
- char *name;
+ struct netdev_obj *netdev_obj;
+ char *name = netdev->name;
int error;
+ netdev_obj = shash_find_data(&netdev_obj_shash, name);
+ assert(netdev_obj);
+ if (netdev_obj->ref_cnt > 0) {
+ netdev_obj->ref_cnt--;
+ } else {
+ VLOG_WARN("netdev %s closed too many times", name);
+ }
+
+ /* If the reference count for the netdev object is zero, and it
+ * was dynamically created by netdev_open(), destroy it. */
+ if (!netdev_obj->ref_cnt && !netdev_obj->created) {
+ netdev_destroy(name);
+ }
+
/* Restore flags that we changed, if any. */
fatal_signal_block();
error = restore_flags(netdev);
@@ -182,11 +293,10 @@ netdev_close(struct netdev *netdev)
fatal_signal_unblock();
if (error) {
VLOG_WARN("failed to restore network device flags on %s: %s",
- netdev->name, strerror(error));
+ name, strerror(error));
}
/* Free. */
- name = netdev->name;
netdev->class->close(netdev);
free(name);
}
@@ -231,7 +341,7 @@ netdev_enumerate(struct svec *svec)
int retval = class->enumerate(svec);
if (retval) {
VLOG_WARN("failed to enumerate %s network devices: %s",
- class->name, strerror(retval));
+ class->type, strerror(retval));
if (!error) {
error = retval;
}
@@ -366,6 +476,21 @@ netdev_get_mtu(const struct netdev *netdev, int *mtup)
return error;
}
+/* Returns the ifindex of 'netdev', if successful, as a positive number. On
+ * failure, returns a negative errno value.
+ *
+ * The desired semantics of the ifindex value are a combination of those
+ * specified by POSIX for if_nametoindex() and by SNMP for ifIndex. An ifindex
+ * value should be unique within a host and remain stable at least until
+ * reboot. SNMP says an ifindex "ranges between 1 and the value of ifNumber"
+ * but many systems do not follow this rule anyhow.
+ */
+int
+netdev_get_ifindex(const struct netdev *netdev)
+{
+ return netdev->class->get_ifindex(netdev);
+}
+
/* Stores the features supported by 'netdev' into each of '*current',
* '*advertised', '*supported', and '*peer' that are non-null. Each value is a
* bitmap of "enum ofp_port_features" bits, in host byte order. Returns 0 if
@@ -377,11 +502,27 @@ netdev_get_features(struct netdev *netdev,
uint32_t *supported, uint32_t *peer)
{
uint32_t dummy[4];
- return netdev->class->get_features(netdev,
- current ? current : &dummy[0],
- advertised ? advertised : &dummy[1],
- supported ? supported : &dummy[2],
- peer ? peer : &dummy[3]);
+ int error;
+
+ if (!current) {
+ current = &dummy[0];
+ }
+ if (!advertised) {
+ advertised = &dummy[1];
+ }
+ if (!supported) {
+ supported = &dummy[2];
+ }
+ if (!peer) {
+ peer = &dummy[3];
+ }
+
+ error = netdev->class->get_features(netdev, current, advertised, supported,
+ peer);
+ if (error) {
+ *current = *advertised = *supported = *peer = 0;
+ }
+ return error;
}
/* Set the features advertised by 'netdev' to 'advertise'. Returns 0 if
@@ -677,6 +818,24 @@ exit:
return netdev;
}
+/* Initializes 'netdev_obj' as a netdev object named 'name' of the
+ * specified 'class'.
+ *
+ * This function adds 'netdev_obj' to a netdev-owned shash, so it is
+ * very important that 'netdev_obj' only be freed after calling
+ * netdev_destroy(). */
+void
+netdev_obj_init(struct netdev_obj *netdev_obj, const char *name,
+ const struct netdev_class *class, bool created)
+{
+ assert(!shash_find(&netdev_obj_shash, name));
+
+ netdev_obj->class = class;
+ netdev_obj->ref_cnt = 0;
+ netdev_obj->created = created;
+ shash_add(&netdev_obj_shash, name, netdev_obj);
+}
+
/* Initializes 'netdev' as a netdev named 'name' of the specified 'class'.
*
* This function adds 'netdev' to a netdev-owned linked list, so it is very
@@ -692,6 +851,14 @@ netdev_init(struct netdev *netdev, const char *name,
list_push_back(&netdev_list, &netdev->node);
}
+/* Returns the class type of 'netdev'.
+ *
+ * The caller must not free the returned value. */
+const char *netdev_get_type(const struct netdev *netdev)
+{
+ return netdev->class->type;
+}
+
/* Initializes 'notifier' as a netdev notifier for 'netdev', for which
* notification will consist of calling 'cb', with auxiliary data 'aux'. */
void
diff --git a/lib/netdev.h b/lib/netdev.h
index 4a29cf374..b8c7dfb49 100644
--- a/lib/netdev.h
+++ b/lib/netdev.h
@@ -30,6 +30,7 @@
struct ofpbuf;
struct in_addr;
struct in6_addr;
+struct shash;
struct svec;
enum netdev_flags {
@@ -81,6 +82,11 @@ int netdev_initialize(void);
void netdev_run(void);
void netdev_wait(void);
+int netdev_create(const char *name, const char *type,
+ const struct shash *args);
+int netdev_destroy(const char *name);
+int netdev_reconfigure(const char *name, const struct shash *args);
+
int netdev_open(const char *name, int ethertype, struct netdev **);
void netdev_close(struct netdev *);
@@ -90,6 +96,7 @@ int netdev_enumerate(struct svec *);
const char *netdev_get_name(const struct netdev *);
int netdev_get_mtu(const struct netdev *, int *mtup);
+int netdev_get_ifindex(const struct netdev *);
int netdev_recv(struct netdev *, struct ofpbuf *);
void netdev_recv_wait(struct netdev *);
diff --git a/lib/odp-util.c b/lib/odp-util.c
index 21bc9a516..a80016209 100644
--- a/lib/odp-util.c
+++ b/lib/odp-util.c
@@ -111,8 +111,9 @@ format_odp_actions(struct ds *ds, const union odp_action *actions,
void
format_odp_flow_stats(struct ds *ds, const struct odp_flow_stats *s)
{
- ds_put_format(ds, "packets:%"PRIu64", bytes:%"PRIu64", used:",
- s->n_packets, s->n_bytes);
+ ds_put_format(ds, "packets:%llu, bytes:%llu, used:",
+ (unsigned long long int) s->n_packets,
+ (unsigned long long int) s->n_bytes);
if (s->used_sec) {
long long int used = s->used_sec * 1000 + s->used_nsec / 1000000;
ds_put_format(ds, "%.3fs", (time_msec() - used) / 1000.0);
diff --git a/lib/ofp-print.c b/lib/ofp-print.c
index 63b59ddb0..9c1afe448 100644
--- a/lib/ofp-print.c
+++ b/lib/ofp-print.c
@@ -797,7 +797,11 @@ static const struct error_type error_types[] = {
ERROR_CODE(OFPET_BAD_REQUEST, OFPBRC_BAD_VERSION),
ERROR_CODE(OFPET_BAD_REQUEST, OFPBRC_BAD_TYPE),
ERROR_CODE(OFPET_BAD_REQUEST, OFPBRC_BAD_STAT),
- ERROR_CODE(OFPET_BAD_REQUEST, OFPBRC_BAD_VERSION),
+ ERROR_CODE(OFPET_BAD_REQUEST, OFPBRC_BAD_VENDOR),
+ ERROR_CODE(OFPET_BAD_REQUEST, OFPBRC_BAD_SUBTYPE),
+ ERROR_CODE(OFPET_BAD_REQUEST, OFPBRC_BAD_LENGTH),
+ ERROR_CODE(OFPET_BAD_REQUEST, OFPBRC_BUFFER_EMPTY),
+ ERROR_CODE(OFPET_BAD_REQUEST, OFPBRC_BAD_COOKIE),
ERROR_TYPE(OFPET_BAD_ACTION),
ERROR_CODE(OFPET_BAD_ACTION, OFPBAC_BAD_TYPE),
@@ -805,9 +809,16 @@ static const struct error_type error_types[] = {
ERROR_CODE(OFPET_BAD_ACTION, OFPBAC_BAD_VENDOR),
ERROR_CODE(OFPET_BAD_ACTION, OFPBAC_BAD_VENDOR_TYPE),
ERROR_CODE(OFPET_BAD_ACTION, OFPBAC_BAD_OUT_PORT),
+ ERROR_CODE(OFPET_BAD_ACTION, OFPBAC_BAD_ARGUMENT),
+ ERROR_CODE(OFPET_BAD_ACTION, OFPBAC_TOO_MANY),
ERROR_TYPE(OFPET_FLOW_MOD_FAILED),
- ERROR_CODE(OFPET_FLOW_MOD_FAILED, OFPFMFC_ALL_TABLES_FULL)
+ ERROR_CODE(OFPET_FLOW_MOD_FAILED, OFPFMFC_ALL_TABLES_FULL),
+ ERROR_CODE(OFPET_FLOW_MOD_FAILED, OFPFMFC_BAD_COMMAND),
+
+ ERROR_TYPE(OFPET_PORT_MOD_FAILED),
+ ERROR_CODE(OFPET_PORT_MOD_FAILED, OFPPMFC_BAD_PORT),
+ ERROR_CODE(OFPET_PORT_MOD_FAILED, OFPPMFC_BAD_HW_ADDR)
};
#define N_ERROR_TYPES ARRAY_SIZE(error_types)
diff --git a/lib/pcap.c b/lib/pcap.c
index 4330c575d..967bb5c34 100644
--- a/lib/pcap.c
+++ b/lib/pcap.c
@@ -128,7 +128,7 @@ pcap_read(FILE *file, struct ofpbuf **bufp)
((len & 0x0000ff00) << 8) |
((len & 0x000000ff) << 24));
if (swapped_len > 0xffff) {
- VLOG_WARN("bad packet length %"PRIu32" or %"PRIu32" "
+ VLOG_WARN("bad packet length %zu or %"PRIu32" "
"reading pcap file",
len, swapped_len);
return EPROTO;
diff --git a/lib/poll-loop.c b/lib/poll-loop.c
index 26a17e83f..32bbc1349 100644
--- a/lib/poll-loop.c
+++ b/lib/poll-loop.c
@@ -18,6 +18,7 @@
#include "poll-loop.h"
#include <assert.h>
#include <errno.h>
+#include <inttypes.h>
#include <poll.h>
#include <stdlib.h>
#include <string.h>
@@ -122,7 +123,7 @@ log_wakeup(const struct backtrace *backtrace, const char *format, ...)
ds_put_char(&ds, ':');
for (i = 0; i < backtrace->n_frames; i++) {
- ds_put_format(&ds, " 0x%x", backtrace->frames[i]);
+ ds_put_format(&ds, " 0x%"PRIxPTR, backtrace->frames[i]);
}
}
VLOG_DBG("%s", ds_cstr(&ds));
diff --git a/lib/socket-util.c b/lib/socket-util.c
index e400bb543..e6a6c70ef 100644
--- a/lib/socket-util.c
+++ b/lib/socket-util.c
@@ -291,10 +291,12 @@ guess_netmask(uint32_t ip)
: htonl(0)); /* ??? */
}
-/* Opens a non-blocking TCP socket and connects to 'target', which should be a
- * string in the format "<host>[:<port>]". <host> is required. If
- * 'default_port' is nonzero then <port> is optional and defaults to
- * 'default_port'.
+/* Opens a non-blocking IPv4 socket of the specified 'style' and connects to
+ * 'target', which should be a string in the format "<host>[:<port>]". <host>
+ * is required. If 'default_port' is nonzero then <port> is optional and
+ * defaults to 'default_port'.
+ *
+ * 'style' should be SOCK_STREAM (for TCP) or SOCK_DGRAM (for UDP).
*
* On success, returns 0 (indicating connection complete) or EAGAIN (indicating
* connection in progress), in which case the new file descriptor is stored
@@ -304,8 +306,8 @@ guess_netmask(uint32_t ip)
* If 'sinp' is non-null, then on success the target address is stored into
* '*sinp'. */
int
-tcp_open_active(const char *target_, uint16_t default_port,
- struct sockaddr_in *sinp, int *fdp)
+inet_open_active(int style, const char *target_, uint16_t default_port,
+ struct sockaddr_in *sinp, int *fdp)
{
char *target = xstrdup(target_);
char *save_ptr = NULL;
@@ -343,7 +345,7 @@ tcp_open_active(const char *target_, uint16_t default_port,
}
/* Create non-blocking socket. */
- fd = socket(AF_INET, SOCK_STREAM, 0);
+ fd = socket(AF_INET, style, 0);
if (fd < 0) {
VLOG_ERR("%s: socket: %s", target_, strerror(errno));
error = errno;
@@ -380,18 +382,20 @@ exit:
return error;
}
-/* Opens a non-blocking TCP socket, binds to 'target', and listens for incoming
- * connections. 'target' should be a string in the format "[<port>][:<ip>]".
- * <port> may be omitted if 'default_port' is nonzero, in which case it
- * defaults to 'default_port'. If <ip> is omitted it defaults to the wildcard
- * IP address.
+/* Opens a non-blocking IPv4 socket of the specified 'style', binds to
+ * 'target', and listens for incoming connections. 'target' should be a string
+ * in the format "[<port>][:<ip>]". <port> may be omitted if 'default_port' is
+ * nonzero, in which case it defaults to 'default_port'. If <ip> is omitted it
+ * defaults to the wildcard IP address.
+ *
+ * 'style' should be SOCK_STREAM (for TCP) or SOCK_DGRAM (for UDP).
*
- * The socket will have SO_REUSEADDR turned on.
+ * For TCP, the socket will have SO_REUSEADDR turned on.
*
* On success, returns a non-negative file descriptor. On failure, returns a
* negative errno value. */
int
-tcp_open_passive(const char *target_, uint16_t default_port)
+inet_open_passive(int style, const char *target_, uint16_t default_port)
{
char *target = xstrdup(target_);
char *string_ptr = target;
@@ -427,7 +431,7 @@ tcp_open_passive(const char *target_, uint16_t default_port)
}
/* Create non-blocking socket, set SO_REUSEADDR. */
- fd = socket(AF_INET, SOCK_STREAM, 0);
+ fd = socket(AF_INET, style, 0);
if (fd < 0) {
error = errno;
VLOG_ERR("%s: socket: %s", target_, strerror(error));
@@ -437,7 +441,8 @@ tcp_open_passive(const char *target_, uint16_t default_port)
if (error) {
goto exit_close;
}
- if (setsockopt(fd, SOL_SOCKET, SO_REUSEADDR, &yes, sizeof yes) < 0) {
+ if (style == SOCK_STREAM
+ && setsockopt(fd, SOL_SOCKET, SO_REUSEADDR, &yes, sizeof yes) < 0) {
error = errno;
VLOG_ERR("%s: setsockopt(SO_REUSEADDR): %s", target_, strerror(error));
goto exit_close;
diff --git a/lib/socket-util.h b/lib/socket-util.h
index febe5e735..4259115d4 100644
--- a/lib/socket-util.h
+++ b/lib/socket-util.h
@@ -34,9 +34,9 @@ int get_unix_name_len(socklen_t sun_len);
uint32_t guess_netmask(uint32_t ip);
int get_null_fd(void);
-int tcp_open_active(const char *target, uint16_t default_port,
+int inet_open_active(int style, const char *target, uint16_t default_port,
struct sockaddr_in *sinp, int *fdp);
-int tcp_open_passive(const char *target, uint16_t default_port);
+int inet_open_passive(int style, const char *target, uint16_t default_port);
int read_fully(int fd, void *, size_t, size_t *bytes_read);
int write_fully(int fd, const void *, size_t, size_t *bytes_written);
diff --git a/lib/stream-tcp.c b/lib/stream-tcp.c
index ecd96865f..947be9f19 100644
--- a/lib/stream-tcp.c
+++ b/lib/stream-tcp.c
@@ -74,7 +74,7 @@ tcp_open(const char *name, char *suffix, struct stream **streamp)
struct sockaddr_in sin;
int fd, error;
- error = tcp_open_active(suffix, 0, &sin, &fd);
+ error = inet_open_active(SOCK_STREAM, suffix, 0, &sin, &fd);
if (fd >= 0) {
return new_tcp_stream(name, fd, error, &sin, streamp);
} else {
@@ -103,7 +103,7 @@ ptcp_open(const char *name UNUSED, char *suffix, struct pstream **pstreamp)
{
int fd;
- fd = tcp_open_passive(suffix, 0);
+ fd = inet_open_passive(SOCK_STREAM, suffix, 0);
if (fd < 0) {
return -fd;
} else {
diff --git a/lib/svec.c b/lib/svec.c
index 81a36b57a..bc3df23d5 100644
--- a/lib/svec.c
+++ b/lib/svec.c
@@ -372,6 +372,22 @@ svec_join(const struct svec *svec,
return ds_cstr(&ds);
}
+/* Breaks 's' into tokens at any character in 'delimiters', and appends each
+ * token to 'svec'. Empty tokens are not added. */
+void
+svec_split(struct svec *svec, const char *s_, const char *delimiters)
+{
+ char *s = xstrdup(s_);
+ char *save_ptr = NULL;
+ char *token;
+
+ for (token = strtok_r(s, delimiters, &save_ptr); token != NULL;
+ token = strtok_r(NULL, delimiters, &save_ptr)) {
+ svec_add(svec, token);
+ }
+ free(s);
+}
+
const char *
svec_back(const struct svec *svec)
{
diff --git a/lib/svec.h b/lib/svec.h
index ff5619732..2a93139ad 100644
--- a/lib/svec.h
+++ b/lib/svec.h
@@ -53,6 +53,7 @@ void svec_swap(struct svec *a, struct svec *b);
void svec_print(const struct svec *svec, const char *title);
void svec_parse_words(struct svec *svec, const char *words);
bool svec_equal(const struct svec *, const struct svec *);
+void svec_split(struct svec *, const char *s, const char *delimiters);
char *svec_join(const struct svec *,
const char *delimiter, const char *terminator);
const char *svec_back(const struct svec *);
diff --git a/lib/timeval.c b/lib/timeval.c
index 84abdfae4..5e4238758 100644
--- a/lib/timeval.c
+++ b/lib/timeval.c
@@ -43,8 +43,8 @@ static struct timeval now;
/* Time at which to die with SIGALRM (if not TIME_MIN). */
static time_t deadline = TIME_MIN;
-static void setup_timer(void);
-static void setup_signal(int flags);
+static void set_up_timer(void);
+static void set_up_signal(int flags);
static void sigalrm_handler(int);
static void refresh_if_ticked(void);
static time_t time_add(time_t, time_t);
@@ -67,12 +67,12 @@ time_init(void)
gettimeofday(&now, NULL);
tick = false;
- setup_signal(SA_RESTART);
- setup_timer();
+ set_up_signal(SA_RESTART);
+ set_up_timer();
}
static void
-setup_signal(int flags)
+set_up_signal(int flags)
{
struct sigaction sa;
@@ -100,7 +100,7 @@ setup_signal(int flags)
void
time_disable_restart(void)
{
- setup_signal(0);
+ set_up_signal(0);
}
/* Add SA_RESTART to the flags for SIGALRM, so that any system call that
@@ -109,11 +109,11 @@ time_disable_restart(void)
void
time_enable_restart(void)
{
- setup_signal(SA_RESTART);
+ set_up_signal(SA_RESTART);
}
static void
-setup_timer(void)
+set_up_timer(void)
{
struct itimerval itimer;
@@ -133,7 +133,7 @@ setup_timer(void)
void
time_postfork(void)
{
- setup_timer();
+ set_up_timer();
}
/* Forces a refresh of the current time from the kernel. It is not usually
diff --git a/lib/unixctl.c b/lib/unixctl.c
index 8565e5882..637843941 100644
--- a/lib/unixctl.c
+++ b/lib/unixctl.c
@@ -170,7 +170,7 @@ unixctl_command_reply(struct unixctl_conn *conn,
* A program that (optionally) daemonizes itself should call this function
* *after* daemonization, so that the socket name contains the pid of the
* daemon instead of the pid of the program that exited. (Otherwise,
- * "ovs-appctl --target <program>.pid" will fail.)
+ * "ovs-appctl --target=<program>" will fail.)
*
* Returns 0 if successful, otherwise a positive errno value. If successful,
* sets '*serverp' to the new unixctl_server, otherwise to NULL. */
diff --git a/lib/vconn-ssl.c b/lib/vconn-ssl.c
index 2452bcea5..58c54f877 100644
--- a/lib/vconn-ssl.c
+++ b/lib/vconn-ssl.c
@@ -288,7 +288,7 @@ ssl_open(const char *name, char *suffix, struct vconn **vconnp)
return error;
}
- error = tcp_open_active(suffix, OFP_SSL_PORT, &sin, &fd);
+ error = inet_open_active(SOCK_STREAM, suffix, OFP_SSL_PORT, &sin, &fd);
if (fd >= 0) {
int state = error ? STATE_TCP_CONNECTING : STATE_SSL_CONNECTING;
return new_ssl_vconn(name, fd, CLIENT, state, &sin, vconnp);
@@ -776,7 +776,7 @@ pssl_open(const char *name, char *suffix, struct pvconn **pvconnp)
return retval;
}
- fd = tcp_open_passive(suffix, OFP_SSL_PORT);
+ fd = inet_open_passive(SOCK_STREAM, suffix, OFP_SSL_PORT);
if (fd < 0) {
return -fd;
}
diff --git a/lib/vconn-tcp.c b/lib/vconn-tcp.c
index b4e523435..aac716623 100644
--- a/lib/vconn-tcp.c
+++ b/lib/vconn-tcp.c
@@ -75,7 +75,7 @@ tcp_open(const char *name, char *suffix, struct vconn **vconnp)
struct sockaddr_in sin;
int fd, error;
- error = tcp_open_active(suffix, OFP_TCP_PORT, &sin, &fd);
+ error = inet_open_active(SOCK_STREAM, suffix, OFP_TCP_PORT, &sin, &fd);
if (fd >= 0) {
return new_tcp_vconn(name, fd, error, &sin, vconnp);
} else {
@@ -104,7 +104,7 @@ ptcp_open(const char *name UNUSED, char *suffix, struct pvconn **pvconnp)
{
int fd;
- fd = tcp_open_passive(suffix, OFP_TCP_PORT);
+ fd = inet_open_passive(SOCK_STREAM, suffix, OFP_TCP_PORT);
if (fd < 0) {
return -fd;
} else {
diff --git a/lib/vconn.c b/lib/vconn.c
index 3cd294874..b11650fbc 100644
--- a/lib/vconn.c
+++ b/lib/vconn.c
@@ -1042,7 +1042,7 @@ check_ofp_message(const struct ofp_header *msg, uint8_t type, size_t size)
if (got_size != size) {
char *type_name = ofp_message_type_to_string(type);
VLOG_WARN_RL(&bad_ofmsg_rl,
- "received %s message of length %"PRIu16" (expected %zu)",
+ "received %s message of length %zu (expected %zu)",
type_name, got_size, size);
free(type_name);
return ofp_mkerr(OFPET_BAD_REQUEST, OFPBRC_BAD_LENGTH);
@@ -1077,7 +1077,7 @@ check_ofp_message_array(const struct ofp_header *msg, uint8_t type,
got_size = ntohs(msg->length);
if (got_size < min_size) {
char *type_name = ofp_message_type_to_string(type);
- VLOG_WARN_RL(&bad_ofmsg_rl, "received %s message of length %"PRIu16" "
+ VLOG_WARN_RL(&bad_ofmsg_rl, "received %s message of length %zu "
"(expected at least %zu)",
type_name, got_size, min_size);
free(type_name);
@@ -1086,7 +1086,7 @@ check_ofp_message_array(const struct ofp_header *msg, uint8_t type,
if ((got_size - min_size) % array_elt_size) {
char *type_name = ofp_message_type_to_string(type);
VLOG_WARN_RL(&bad_ofmsg_rl,
- "received %s message of bad length %"PRIu16": the "
+ "received %s message of bad length %zu: the "
"excess over %zu (%zu) is not evenly divisible by %zu "
"(remainder is %zu)",
type_name, got_size, min_size, got_size - min_size,
@@ -1119,13 +1119,13 @@ check_ofp_packet_out(const struct ofp_header *oh, struct ofpbuf *data,
actions_len = ntohs(opo->actions_len);
if (actions_len > extra) {
- VLOG_WARN_RL(&bad_ofmsg_rl, "packet-out claims %zu bytes of actions "
+ VLOG_WARN_RL(&bad_ofmsg_rl, "packet-out claims %u bytes of actions "
"but message has room for only %zu bytes",
actions_len, extra);
return ofp_mkerr(OFPET_BAD_REQUEST, OFPBRC_BAD_LENGTH);
}
if (actions_len % sizeof(union ofp_action)) {
- VLOG_WARN_RL(&bad_ofmsg_rl, "packet-out claims %zu bytes of actions, "
+ VLOG_WARN_RL(&bad_ofmsg_rl, "packet-out claims %u bytes of actions, "
"which is not a multiple of %zu",
actions_len, sizeof(union ofp_action));
return ofp_mkerr(OFPET_BAD_REQUEST, OFPBRC_BAD_LENGTH);
@@ -1282,7 +1282,8 @@ check_action(const union ofp_action *a, unsigned int len, int max_ports)
break;
default:
- VLOG_WARN_RL(&bad_ofmsg_rl, "unknown action type %"PRIu16, a->type);
+ VLOG_WARN_RL(&bad_ofmsg_rl, "unknown action type %"PRIu16,
+ ntohs(a->type));
return ofp_mkerr(OFPET_BAD_ACTION, OFPBAC_BAD_TYPE);
}
@@ -1312,7 +1313,7 @@ validate_actions(const union ofp_action *actions, size_t n_actions,
if (n_slots > slots_left) {
VLOG_DBG_RL(&bad_ofmsg_rl,
- "action requires %u slots but only %td remain",
+ "action requires %u slots but only %u remain",
n_slots, slots_left);
return ofp_mkerr(OFPET_BAD_ACTION, OFPBAC_BAD_LEN);
}
@@ -1360,7 +1361,7 @@ normalize_match(struct ofp_match *m)
if (wc & OFPFW_DL_TYPE) {
m->dl_type = 0;
- /* Can't sensibly m on network or transport headers if the
+ /* Can't sensibly match on network or transport headers if the
* data link type is unknown. */
wc |= OFPFW_NW | OFPFW_TP;
m->nw_src = m->nw_dst = m->nw_proto = 0;
@@ -1369,7 +1370,7 @@ normalize_match(struct ofp_match *m)
if (wc & OFPFW_NW_PROTO) {
m->nw_proto = 0;
- /* Can't sensibly m on transport headers if the network
+ /* Can't sensibly match on transport headers if the network
* protocol is unknown. */
wc |= OFPFW_TP;
m->tp_src = m->tp_dst = 0;
@@ -1384,7 +1385,7 @@ normalize_match(struct ofp_match *m)
}
} else {
/* Transport layer fields will always be extracted as zeros, so we
- * can do an exact-m on those values. */
+ * can do an exact-match on those values. */
wc &= ~OFPFW_TP;
m->tp_src = m->tp_dst = 0;
}
@@ -1396,7 +1397,7 @@ normalize_match(struct ofp_match *m)
}
} else {
/* Network and transport layer fields will always be extracted as
- * zeros, so we can do an exact-m on those values. */
+ * zeros, so we can do an exact-match on those values. */
wc &= ~(OFPFW_NW | OFPFW_TP);
m->nw_proto = m->nw_src = m->nw_dst = 0;
m->tp_src = m->tp_dst = 0;
diff --git a/lib/vlog-modules.def b/lib/vlog-modules.def
index 2f056c855..9a9ea065c 100644
--- a/lib/vlog-modules.def
+++ b/lib/vlog-modules.def
@@ -21,6 +21,7 @@ VLOG_MODULE(bridge)
VLOG_MODULE(chain)
VLOG_MODULE(cfg)
VLOG_MODULE(cfg_mod)
+VLOG_MODULE(collectors)
VLOG_MODULE(controller)
VLOG_MODULE(coverage)
VLOG_MODULE(ctlpath)
diff --git a/lib/vlog-unixctl.man b/lib/vlog-unixctl.man
index 5c79875fc..86eece3b7 100644
--- a/lib/vlog-unixctl.man
+++ b/lib/vlog-unixctl.man
@@ -7,7 +7,7 @@ Sets the logging level for \fImodule\fR in \fIfacility\fR to
.RS
.IP \(bu
\fImodule\fR may be any valid module name (as displayed by the
-\fB--list\fR action on \fBovs-appctl\fR(8)), or the special name
+\fB--list\fR action on \fBovs\-appctl\fR(8)), or the special name
\fBANY\fR to set the logging levels for all modules.
.
.IP \(bu
@@ -26,7 +26,7 @@ logged. If it is omitted, \fIlevel\fR defaults to \fBdbg\fR.
.RE
.IP "\fBvlog/set PATTERN:\fIfacility\fB:\fIpattern\fR"
Sets the log pattern for \fIfacility\fR to \fIpattern\fR. Refer to
-\fBovs-appctl\fR(8) for a description of the valid syntax for \fIpattern\fR.
+\fBovs\-appctl\fR(8) for a description of the valid syntax for \fIpattern\fR.
.
.IP "\fBvlog/list\fR"
Lists the supported logging modules and their current levels.
diff --git a/lib/vlog.man b/lib/vlog.man
index 0bd8a26e8..882793164 100644
--- a/lib/vlog.man
+++ b/lib/vlog.man
@@ -7,7 +7,7 @@ Sets the logging level for \fImodule\fR in \fIfacility\fR to
.RS
.IP \(bu
\fImodule\fR may be any valid module name (as displayed by the
-\fB--list\fR action on \fBovs-appctl\fR(8)), or the special name
+\fB--list\fR action on \fBovs\-appctl\fR(8)), or the special name
\fBANY\fR to set the logging levels for all modules.
.IP \(bu
@@ -35,7 +35,7 @@ Sets the maximum logging verbosity level, equivalent to
.TP
\fB-vPATTERN:\fIfacility\fB:\fIpattern\fR, \fB--verbose=PATTERN:\fIfacility\fB:\fIpattern\fR
Sets the log pattern for \fIfacility\fR to \fIpattern\fR. Refer to
-\fBovs-appctl\fR(8) for a description of the valid syntax for \fIpattern\fR.
+\fBovs\-appctl\fR(8) for a description of the valid syntax for \fIpattern\fR.
.TP
\fB--log-file\fR[\fB=\fIfile\fR]